summaryrefslogtreecommitdiffstats
path: root/sc/source
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source')
-rw-r--r--sc/source/core/data/SolverSettings.cxx508
-rw-r--r--sc/source/core/data/attarray.cxx2716
-rw-r--r--sc/source/core/data/attrib.cxx885
-rw-r--r--sc/source/core/data/autonamecache.cxx90
-rw-r--r--sc/source/core/data/bcaslot.cxx1287
-rw-r--r--sc/source/core/data/bigrange.cxx25
-rw-r--r--sc/source/core/data/broadcast.cxx159
-rw-r--r--sc/source/core/data/celltextattr.cxx21
-rw-r--r--sc/source/core/data/cellvalue.cxx688
-rw-r--r--sc/source/core/data/cellvalues.cxx364
-rw-r--r--sc/source/core/data/clipcontext.cxx436
-rw-r--r--sc/source/core/data/clipparam.cxx189
-rw-r--r--sc/source/core/data/colcontainer.cxx56
-rw-r--r--sc/source/core/data/colorscale.cxx1523
-rw-r--r--sc/source/core/data/column.cxx3343
-rw-r--r--sc/source/core/data/column2.cxx3829
-rw-r--r--sc/source/core/data/column3.cxx3726
-rw-r--r--sc/source/core/data/column4.cxx2266
-rw-r--r--sc/source/core/data/columniterator.cxx209
-rw-r--r--sc/source/core/data/columnset.cxx67
-rw-r--r--sc/source/core/data/columnspanset.cxx373
-rw-r--r--sc/source/core/data/compressedarray.cxx418
-rw-r--r--sc/source/core/data/conditio.cxx2365
-rw-r--r--sc/source/core/data/dbdocutl.cxx201
-rw-r--r--sc/source/core/data/dociter.cxx1782
-rw-r--r--sc/source/core/data/docparam.cxx25
-rw-r--r--sc/source/core/data/docpool.cxx617
-rw-r--r--sc/source/core/data/documen2.cxx1515
-rw-r--r--sc/source/core/data/documen3.cxx2152
-rw-r--r--sc/source/core/data/documen4.cxx1360
-rw-r--r--sc/source/core/data/documen5.cxx656
-rw-r--r--sc/source/core/data/documen6.cxx210
-rw-r--r--sc/source/core/data/documen7.cxx615
-rw-r--r--sc/source/core/data/documen8.cxx1328
-rw-r--r--sc/source/core/data/documen9.cxx670
-rw-r--r--sc/source/core/data/document.cxx6967
-rw-r--r--sc/source/core/data/document10.cxx1119
-rw-r--r--sc/source/core/data/documentimport.cxx856
-rw-r--r--sc/source/core/data/documentstreamaccess.cxx147
-rw-r--r--sc/source/core/data/dpcache.cxx1522
-rw-r--r--sc/source/core/data/dpdimsave.cxx792
-rw-r--r--sc/source/core/data/dpfilteredcache.cxx433
-rw-r--r--sc/source/core/data/dpglobal.cxx20
-rw-r--r--sc/source/core/data/dpgroup.cxx1031
-rw-r--r--sc/source/core/data/dpitemdata.cxx371
-rw-r--r--sc/source/core/data/dpnumgroupinfo.cxx35
-rw-r--r--sc/source/core/data/dpobject.cxx3980
-rw-r--r--sc/source/core/data/dpoutput.cxx1961
-rw-r--r--sc/source/core/data/dpoutputgeometry.cxx255
-rw-r--r--sc/source/core/data/dpresfilter.cxx268
-rw-r--r--sc/source/core/data/dpsave.cxx1390
-rw-r--r--sc/source/core/data/dpsdbtab.cxx169
-rw-r--r--sc/source/core/data/dpshttab.cxx323
-rw-r--r--sc/source/core/data/dptabdat.cxx292
-rw-r--r--sc/source/core/data/dptabres.cxx4117
-rw-r--r--sc/source/core/data/dptabsrc.cxx2609
-rw-r--r--sc/source/core/data/dputil.cxx420
-rw-r--r--sc/source/core/data/drawpage.cxx51
-rw-r--r--sc/source/core/data/drwlayer.cxx2946
-rw-r--r--sc/source/core/data/edittextiterator.cxx97
-rw-r--r--sc/source/core/data/fillinfo.cxx1087
-rw-r--r--sc/source/core/data/formulacell.cxx5648
-rw-r--r--sc/source/core/data/formulaiter.cxx77
-rw-r--r--sc/source/core/data/funcdesc.cxx1250
-rw-r--r--sc/source/core/data/global.cxx1173
-rw-r--r--sc/source/core/data/global2.cxx592
-rw-r--r--sc/source/core/data/globalx.cxx142
-rw-r--r--sc/source/core/data/grouptokenconverter.cxx316
-rw-r--r--sc/source/core/data/listenercontext.cxx96
-rw-r--r--sc/source/core/data/markarr.cxx462
-rw-r--r--sc/source/core/data/markdata.cxx950
-rw-r--r--sc/source/core/data/markmulti.cxx485
-rw-r--r--sc/source/core/data/mtvcellfunc.cxx31
-rw-r--r--sc/source/core/data/mtvelements.cxx198
-rw-r--r--sc/source/core/data/olinetab.cxx873
-rw-r--r--sc/source/core/data/pagepar.cxx56
-rw-r--r--sc/source/core/data/patattr.cxx1513
-rw-r--r--sc/source/core/data/pivot2.cxx162
-rw-r--r--sc/source/core/data/poolcach.cxx112
-rw-r--r--sc/source/core/data/poolhelp.cxx118
-rw-r--r--sc/source/core/data/postit.cxx1028
-rw-r--r--sc/source/core/data/queryevaluator.cxx902
-rw-r--r--sc/source/core/data/queryiter.cxx1486
-rw-r--r--sc/source/core/data/refupdatecontext.cxx140
-rw-r--r--sc/source/core/data/rowheightcontext.cxx38
-rw-r--r--sc/source/core/data/segmenttree.cxx711
-rw-r--r--sc/source/core/data/sheetevents.cxx123
-rw-r--r--sc/source/core/data/simpleformulacalc.cxx156
-rw-r--r--sc/source/core/data/sortparam.cxx310
-rw-r--r--sc/source/core/data/stlpool.cxx483
-rw-r--r--sc/source/core/data/stlsheet.cxx343
-rw-r--r--sc/source/core/data/subtotalparam.cxx199
-rw-r--r--sc/source/core/data/tabbgcolor.cxx36
-rw-r--r--sc/source/core/data/table1.cxx2759
-rw-r--r--sc/source/core/data/table2.cxx4354
-rw-r--r--sc/source/core/data/table3.cxx3148
-rw-r--r--sc/source/core/data/table4.cxx3055
-rw-r--r--sc/source/core/data/table5.cxx1372
-rw-r--r--sc/source/core/data/table6.cxx1154
-rw-r--r--sc/source/core/data/table7.cxx664
-rw-r--r--sc/source/core/data/tabprotection.cxx724
-rw-r--r--sc/source/core/data/types.cxx32
-rw-r--r--sc/source/core/data/userdat.cxx51
-rw-r--r--sc/source/core/data/validat.cxx1185
-rw-r--r--sc/source/core/inc/addinhelpid.hxx45
-rw-r--r--sc/source/core/inc/addinlis.hxx84
-rw-r--r--sc/source/core/inc/adiasync.hxx77
-rw-r--r--sc/source/core/inc/bcaslot.hxx398
-rw-r--r--sc/source/core/inc/cellkeytranslator.hxx82
-rw-r--r--sc/source/core/inc/ddelink.hxx84
-rw-r--r--sc/source/core/inc/doubleref.hxx178
-rw-r--r--sc/source/core/inc/formulagroupcl.hxx29
-rw-r--r--sc/source/core/inc/grouptokenconverter.hxx38
-rw-r--r--sc/source/core/inc/interpre.hxx1167
-rw-r--r--sc/source/core/inc/jumpmatrix.hxx122
-rw-r--r--sc/source/core/inc/parclass.hxx144
-rw-r--r--sc/source/core/inc/poolhelp.hxx64
-rw-r--r--sc/source/core/inc/refupdat.hxx71
-rw-r--r--sc/source/core/inc/scrdata.hxx37
-rw-r--r--sc/source/core/inc/sharedstringpoolpurge.hxx47
-rw-r--r--sc/source/core/inc/webservicelink.hxx49
-rw-r--r--sc/source/core/opencl/cl-test.odsbin0 -> 18569 bytes
-rw-r--r--sc/source/core/opencl/formulagroupcl.cxx3047
-rw-r--r--sc/source/core/opencl/op_addin.cxx127
-rw-r--r--sc/source/core/opencl/op_addin.hxx34
-rw-r--r--sc/source/core/opencl/op_array.cxx72
-rw-r--r--sc/source/core/opencl/op_array.hxx41
-rw-r--r--sc/source/core/opencl/op_financial.cxx1845
-rw-r--r--sc/source/core/opencl/op_financial.hxx559
-rw-r--r--sc/source/core/opencl/op_financial_helpers.hxx1412
-rw-r--r--sc/source/core/opencl/op_logical.cxx93
-rw-r--r--sc/source/core/opencl/op_logical.hxx68
-rw-r--r--sc/source/core/opencl/op_math.cxx1657
-rw-r--r--sc/source/core/opencl/op_math.hxx673
-rw-r--r--sc/source/core/opencl/op_math_helpers.hxx208
-rw-r--r--sc/source/core/opencl/op_spreadsheet.cxx286
-rw-r--r--sc/source/core/opencl/op_spreadsheet.hxx28
-rw-r--r--sc/source/core/opencl/op_statistical.cxx2161
-rw-r--r--sc/source/core/opencl/op_statistical.hxx632
-rw-r--r--sc/source/core/opencl/op_statistical_helpers.hxx1383
-rw-r--r--sc/source/core/opencl/opbase.cxx884
-rw-r--r--sc/source/core/opencl/opbase.hxx525
-rw-r--r--sc/source/core/opencl/utils.cxx88
-rw-r--r--sc/source/core/opencl/utils.hxx48
-rw-r--r--sc/source/core/tool/addincfg.cxx53
-rw-r--r--sc/source/core/tool/addincol.cxx1731
-rw-r--r--sc/source/core/tool/addinhelpid.cxx210
-rw-r--r--sc/source/core/tool/addinlis.cxx135
-rw-r--r--sc/source/core/tool/address.cxx2531
-rw-r--r--sc/source/core/tool/adiasync.cxx137
-rw-r--r--sc/source/core/tool/appoptio.cxx668
-rw-r--r--sc/source/core/tool/arraysumSSE2.cxx120
-rw-r--r--sc/source/core/tool/autoform.cxx934
-rw-r--r--sc/source/core/tool/calcconfig.cxx241
-rw-r--r--sc/source/core/tool/callform.cxx412
-rw-r--r--sc/source/core/tool/cellform.cxx217
-rw-r--r--sc/source/core/tool/cellkeytranslator.cxx225
-rw-r--r--sc/source/core/tool/cellkeywords.inl199
-rw-r--r--sc/source/core/tool/chartarr.cxx374
-rw-r--r--sc/source/core/tool/charthelper.cxx432
-rw-r--r--sc/source/core/tool/chartlis.cxx630
-rw-r--r--sc/source/core/tool/chartlock.cxx180
-rw-r--r--sc/source/core/tool/chartpos.cxx524
-rw-r--r--sc/source/core/tool/chgtrack.cxx4682
-rw-r--r--sc/source/core/tool/chgviset.cxx150
-rw-r--r--sc/source/core/tool/compare.cxx334
-rw-r--r--sc/source/core/tool/compiler.cxx6651
-rw-r--r--sc/source/core/tool/consoli.cxx544
-rw-r--r--sc/source/core/tool/dbdata.cxx1656
-rw-r--r--sc/source/core/tool/ddelink.cxx266
-rw-r--r--sc/source/core/tool/defaultsoptions.cxx152
-rw-r--r--sc/source/core/tool/detdata.cxx87
-rw-r--r--sc/source/core/tool/detfunc.cxx1653
-rw-r--r--sc/source/core/tool/docoptio.cxx371
-rw-r--r--sc/source/core/tool/doubleref.cxx474
-rw-r--r--sc/source/core/tool/editdataarray.cxx75
-rw-r--r--sc/source/core/tool/editutil.cxx941
-rw-r--r--sc/source/core/tool/filtopt.cxx74
-rw-r--r--sc/source/core/tool/formulagroup.cxx244
-rw-r--r--sc/source/core/tool/formulalogger.cxx352
-rw-r--r--sc/source/core/tool/formulaopt.cxx653
-rw-r--r--sc/source/core/tool/formulaparserpool.cxx143
-rw-r--r--sc/source/core/tool/formularesult.cxx641
-rw-r--r--sc/source/core/tool/grouparealistener.cxx352
-rw-r--r--sc/source/core/tool/hints.cxx114
-rw-r--r--sc/source/core/tool/inputopt.cxx157
-rw-r--r--sc/source/core/tool/interpr1.cxx10332
-rw-r--r--sc/source/core/tool/interpr2.cxx3727
-rw-r--r--sc/source/core/tool/interpr3.cxx5585
-rw-r--r--sc/source/core/tool/interpr4.cxx4839
-rw-r--r--sc/source/core/tool/interpr5.cxx3303
-rw-r--r--sc/source/core/tool/interpr6.cxx1010
-rw-r--r--sc/source/core/tool/interpr7.cxx557
-rw-r--r--sc/source/core/tool/interpr8.cxx2008
-rw-r--r--sc/source/core/tool/interpretercontext.cxx219
-rw-r--r--sc/source/core/tool/jumpmatrix.cxx273
-rw-r--r--sc/source/core/tool/listenerquery.cxx90
-rw-r--r--sc/source/core/tool/lookupcache.cxx127
-rw-r--r--sc/source/core/tool/math.cxx72
-rw-r--r--sc/source/core/tool/matrixoperators.cxx41
-rw-r--r--sc/source/core/tool/navicfg.cxx60
-rw-r--r--sc/source/core/tool/numformat.cxx71
-rw-r--r--sc/source/core/tool/odffmap.cxx142
-rw-r--r--sc/source/core/tool/optutil.cxx67
-rw-r--r--sc/source/core/tool/orcusxml.cxx31
-rw-r--r--sc/source/core/tool/parclass.cxx710
-rw-r--r--sc/source/core/tool/printopt.cxx131
-rw-r--r--sc/source/core/tool/prnsave.cxx127
-rw-r--r--sc/source/core/tool/progress.cxx178
-rw-r--r--sc/source/core/tool/queryentry.cxx207
-rw-r--r--sc/source/core/tool/queryparam.cxx464
-rw-r--r--sc/source/core/tool/rangecache.cxx198
-rw-r--r--sc/source/core/tool/rangelst.cxx1531
-rw-r--r--sc/source/core/tool/rangenam.cxx900
-rw-r--r--sc/source/core/tool/rangeseq.cxx451
-rw-r--r--sc/source/core/tool/rangeutl.cxx1067
-rw-r--r--sc/source/core/tool/rechead.cxx148
-rw-r--r--sc/source/core/tool/recursionhelper.cxx304
-rw-r--r--sc/source/core/tool/refdata.cxx599
-rw-r--r--sc/source/core/tool/reffind.cxx334
-rw-r--r--sc/source/core/tool/refhint.cxx80
-rw-r--r--sc/source/core/tool/refreshtimer.cxx140
-rw-r--r--sc/source/core/tool/reftokenhelper.cxx486
-rw-r--r--sc/source/core/tool/refupdat.cxx592
-rw-r--r--sc/source/core/tool/scmatrix.cxx3645
-rw-r--r--sc/source/core/tool/scopetools.cxx122
-rw-r--r--sc/source/core/tool/sharedformula.cxx442
-rw-r--r--sc/source/core/tool/sharedstringpoolpurge.cxx58
-rw-r--r--sc/source/core/tool/stringutil.cxx469
-rw-r--r--sc/source/core/tool/stylehelper.cxx173
-rw-r--r--sc/source/core/tool/subtotal.cxx204
-rw-r--r--sc/source/core/tool/token.cxx5413
-rw-r--r--sc/source/core/tool/tokenstringcontext.cxx136
-rw-r--r--sc/source/core/tool/typedstrdata.cxx128
-rw-r--r--sc/source/core/tool/unitconv.cxx118
-rw-r--r--sc/source/core/tool/userlist.cxx248
-rw-r--r--sc/source/core/tool/viewopti.cxx593
-rw-r--r--sc/source/core/tool/webservicelink.cxx99
-rw-r--r--sc/source/core/tool/zforauto.cxx88
-rw-r--r--sc/source/filter/dif/difexp.cxx259
-rw-r--r--sc/source/filter/dif/difimp.cxx675
-rw-r--r--sc/source/filter/excel/colrowst.cxx359
-rw-r--r--sc/source/filter/excel/excdoc.cxx928
-rw-r--r--sc/source/filter/excel/excel.cxx498
-rw-r--r--sc/source/filter/excel/excform.cxx1911
-rw-r--r--sc/source/filter/excel/excform8.cxx1672
-rw-r--r--sc/source/filter/excel/excimp8.cxx821
-rw-r--r--sc/source/filter/excel/excrecds.cxx1219
-rw-r--r--sc/source/filter/excel/exctools.cxx245
-rw-r--r--sc/source/filter/excel/expop2.cxx150
-rw-r--r--sc/source/filter/excel/export/ExportTools.cxx45
-rw-r--r--sc/source/filter/excel/export/SparklineExt.cxx215
-rw-r--r--sc/source/filter/excel/fontbuff.cxx130
-rw-r--r--sc/source/filter/excel/frmbase.cxx209
-rw-r--r--sc/source/filter/excel/impop.cxx1419
-rw-r--r--sc/source/filter/excel/namebuff.cxx188
-rw-r--r--sc/source/filter/excel/ooxml-export-TODO.txt164
-rw-r--r--sc/source/filter/excel/read.cxx1312
-rw-r--r--sc/source/filter/excel/tokstack.cxx752
-rw-r--r--sc/source/filter/excel/xechart.cxx3475
-rw-r--r--sc/source/filter/excel/xecontent.cxx2226
-rw-r--r--sc/source/filter/excel/xedbdata.cxx262
-rw-r--r--sc/source/filter/excel/xeescher.cxx2066
-rw-r--r--sc/source/filter/excel/xeextlst.cxx653
-rw-r--r--sc/source/filter/excel/xeformula.cxx2722
-rw-r--r--sc/source/filter/excel/xehelper.cxx1110
-rw-r--r--sc/source/filter/excel/xelink.cxx2661
-rw-r--r--sc/source/filter/excel/xename.cxx876
-rw-r--r--sc/source/filter/excel/xepage.cxx513
-rw-r--r--sc/source/filter/excel/xepivot.cxx1702
-rw-r--r--sc/source/filter/excel/xepivotxml.cxx1210
-rw-r--r--sc/source/filter/excel/xerecord.cxx264
-rw-r--r--sc/source/filter/excel/xeroot.cxx363
-rw-r--r--sc/source/filter/excel/xestream.cxx1289
-rw-r--r--sc/source/filter/excel/xestring.cxx565
-rw-r--r--sc/source/filter/excel/xestyle.cxx3344
-rw-r--r--sc/source/filter/excel/xetable.cxx2841
-rw-r--r--sc/source/filter/excel/xeview.cxx537
-rw-r--r--sc/source/filter/excel/xichart.cxx4423
-rw-r--r--sc/source/filter/excel/xicontent.cxx1451
-rw-r--r--sc/source/filter/excel/xiescher.cxx4514
-rw-r--r--sc/source/filter/excel/xiformula.cxx107
-rw-r--r--sc/source/filter/excel/xihelper.cxx902
-rw-r--r--sc/source/filter/excel/xilink.cxx964
-rw-r--r--sc/source/filter/excel/xiname.cxx329
-rw-r--r--sc/source/filter/excel/xipage.cxx401
-rw-r--r--sc/source/filter/excel/xipivot.cxx1736
-rw-r--r--sc/source/filter/excel/xiroot.cxx299
-rw-r--r--sc/source/filter/excel/xistream.cxx1084
-rw-r--r--sc/source/filter/excel/xistring.cxx213
-rw-r--r--sc/source/filter/excel/xistyle.cxx2085
-rw-r--r--sc/source/filter/excel/xiview.cxx300
-rw-r--r--sc/source/filter/excel/xladdress.cxx161
-rw-r--r--sc/source/filter/excel/xlchart.cxx1284
-rw-r--r--sc/source/filter/excel/xlescher.cxx335
-rw-r--r--sc/source/filter/excel/xlformula.cxx1022
-rw-r--r--sc/source/filter/excel/xlpage.cxx262
-rw-r--r--sc/source/filter/excel/xlpivot.cxx1043
-rw-r--r--sc/source/filter/excel/xlroot.cxx443
-rw-r--r--sc/source/filter/excel/xlstyle.cxx1749
-rw-r--r--sc/source/filter/excel/xltoolbar.cxx439
-rw-r--r--sc/source/filter/excel/xltoolbar.hxx106
-rw-r--r--sc/source/filter/excel/xltools.cxx744
-rw-r--r--sc/source/filter/excel/xltracer.cxx130
-rw-r--r--sc/source/filter/excel/xlview.cxx103
-rw-r--r--sc/source/filter/ftools/fapihelper.cxx369
-rw-r--r--sc/source/filter/ftools/fprogressbar.cxx235
-rw-r--r--sc/source/filter/ftools/ftools.cxx365
-rw-r--r--sc/source/filter/ftools/sharedformulagroups.cxx40
-rw-r--r--sc/source/filter/html/htmlexp.cxx1405
-rw-r--r--sc/source/filter/html/htmlexp2.cxx223
-rw-r--r--sc/source/filter/html/htmlimp.cxx240
-rw-r--r--sc/source/filter/html/htmlpars.cxx3146
-rw-r--r--sc/source/filter/importfilterdata.cxx19
-rw-r--r--sc/source/filter/inc/SparklineFragment.hxx74
-rw-r--r--sc/source/filter/inc/XclExpChangeTrack.hxx632
-rw-r--r--sc/source/filter/inc/XclImpChangeTrack.hxx144
-rw-r--r--sc/source/filter/inc/addressconverter.hxx505
-rw-r--r--sc/source/filter/inc/autofilterbuffer.hxx287
-rw-r--r--sc/source/filter/inc/autofiltercontext.hxx114
-rw-r--r--sc/source/filter/inc/biffhelper.hxx621
-rw-r--r--sc/source/filter/inc/chartsheetfragment.hxx51
-rw-r--r--sc/source/filter/inc/colrowst.hxx86
-rw-r--r--sc/source/filter/inc/commentsbuffer.hxx91
-rw-r--r--sc/source/filter/inc/commentsfragment.hxx53
-rw-r--r--sc/source/filter/inc/condformatbuffer.hxx334
-rw-r--r--sc/source/filter/inc/condformatcontext.hxx89
-rw-r--r--sc/source/filter/inc/connectionsbuffer.hxx160
-rw-r--r--sc/source/filter/inc/connectionsfragment.hxx60
-rw-r--r--sc/source/filter/inc/decl.h25
-rw-r--r--sc/source/filter/inc/defnamesbuffer.hxx182
-rw-r--r--sc/source/filter/inc/dif.hxx152
-rw-r--r--sc/source/filter/inc/drawingbase.hxx133
-rw-r--r--sc/source/filter/inc/drawingfragment.hxx208
-rw-r--r--sc/source/filter/inc/eeimport.hxx63
-rw-r--r--sc/source/filter/inc/eeparser.hxx133
-rw-r--r--sc/source/filter/inc/excdefs.hxx93
-rw-r--r--sc/source/filter/inc/excdoc.hxx99
-rw-r--r--sc/source/filter/inc/excelchartconverter.hxx47
-rw-r--r--sc/source/filter/inc/excelfilter.hxx61
-rw-r--r--sc/source/filter/inc/excelhandlers.hxx80
-rw-r--r--sc/source/filter/inc/excelvbaproject.hxx48
-rw-r--r--sc/source/filter/inc/excform.hxx141
-rw-r--r--sc/source/filter/inc/excimp8.hxx115
-rw-r--r--sc/source/filter/inc/excrecds.hxx460
-rw-r--r--sc/source/filter/inc/excscen.hxx70
-rw-r--r--sc/source/filter/inc/exp_op.hxx62
-rw-r--r--sc/source/filter/inc/expbase.hxx63
-rw-r--r--sc/source/filter/inc/export/ExportTools.hxx23
-rw-r--r--sc/source/filter/inc/export/SparklineExt.hxx55
-rw-r--r--sc/source/filter/inc/externallinkbuffer.hxx350
-rw-r--r--sc/source/filter/inc/externallinkfragment.hxx99
-rw-r--r--sc/source/filter/inc/extlstcontext.hxx148
-rw-r--r--sc/source/filter/inc/fapihelper.hxx294
-rw-r--r--sc/source/filter/inc/flttypes.hxx39
-rw-r--r--sc/source/filter/inc/formel.hxx188
-rw-r--r--sc/source/filter/inc/formulabase.hxx771
-rw-r--r--sc/source/filter/inc/formulabuffer.hxx120
-rw-r--r--sc/source/filter/inc/formulaparser.hxx131
-rw-r--r--sc/source/filter/inc/fprogressbar.hxx223
-rw-r--r--sc/source/filter/inc/ftools.hxx295
-rw-r--r--sc/source/filter/inc/htmlexp.hxx185
-rw-r--r--sc/source/filter/inc/htmlimp.hxx39
-rw-r--r--sc/source/filter/inc/htmlpars.hxx631
-rw-r--r--sc/source/filter/inc/imp_op.hxx201
-rw-r--r--sc/source/filter/inc/lotattr.hxx134
-rw-r--r--sc/source/filter/inc/lotfntbf.hxx59
-rw-r--r--sc/source/filter/inc/lotform.hxx114
-rw-r--r--sc/source/filter/inc/lotimpop.hxx136
-rw-r--r--sc/source/filter/inc/lotrange.hxx114
-rw-r--r--sc/source/filter/inc/namebuff.hxx191
-rw-r--r--sc/source/filter/inc/numberformatsbuffer.hxx116
-rw-r--r--sc/source/filter/inc/ooxformulaparser.hxx89
-rw-r--r--sc/source/filter/inc/op.h58
-rw-r--r--sc/source/filter/inc/optab.h48
-rw-r--r--sc/source/filter/inc/orcusfiltersimpl.hxx44
-rw-r--r--sc/source/filter/inc/orcusinterface.hxx773
-rw-r--r--sc/source/filter/inc/otlnbuff.hxx50
-rw-r--r--sc/source/filter/inc/pagesettings.hxx186
-rw-r--r--sc/source/filter/inc/patterncache.hxx46
-rw-r--r--sc/source/filter/inc/pivotcachebuffer.hxx458
-rw-r--r--sc/source/filter/inc/pivotcachefragment.hxx91
-rw-r--r--sc/source/filter/inc/pivottablebuffer.hxx407
-rw-r--r--sc/source/filter/inc/pivottablefragment.hxx83
-rw-r--r--sc/source/filter/inc/qpro.hxx57
-rw-r--r--sc/source/filter/inc/qproform.hxx71
-rw-r--r--sc/source/filter/inc/qprostyle.hxx56
-rw-r--r--sc/source/filter/inc/querytablebuffer.hxx83
-rw-r--r--sc/source/filter/inc/querytablefragment.hxx47
-rw-r--r--sc/source/filter/inc/revisionfragment.hxx76
-rw-r--r--sc/source/filter/inc/richstring.hxx270
-rw-r--r--sc/source/filter/inc/richstringcontext.hxx57
-rw-r--r--sc/source/filter/inc/root.hxx74
-rw-r--r--sc/source/filter/inc/rtfexp.hxx48
-rw-r--r--sc/source/filter/inc/rtfimp.hxx30
-rw-r--r--sc/source/filter/inc/rtfparse.hxx78
-rw-r--r--sc/source/filter/inc/scenariobuffer.hxx126
-rw-r--r--sc/source/filter/inc/scenariocontext.hxx63
-rw-r--r--sc/source/filter/inc/scmem.h29
-rw-r--r--sc/source/filter/inc/sharedformulagroups.hxx51
-rw-r--r--sc/source/filter/inc/sharedstringsbuffer.hxx48
-rw-r--r--sc/source/filter/inc/sharedstringsfragment.hxx43
-rw-r--r--sc/source/filter/inc/sheetdatabuffer.hxx225
-rw-r--r--sc/source/filter/inc/sheetdatacontext.hxx124
-rw-r--r--sc/source/filter/inc/stylesbuffer.hxx901
-rw-r--r--sc/source/filter/inc/stylesfragment.hxx131
-rw-r--r--sc/source/filter/inc/tablebuffer.hxx124
-rw-r--r--sc/source/filter/inc/tablecolumnsbuffer.hxx100
-rw-r--r--sc/source/filter/inc/tablecolumnscontext.hxx63
-rw-r--r--sc/source/filter/inc/tablefragment.hxx47
-rw-r--r--sc/source/filter/inc/themebuffer.hxx50
-rw-r--r--sc/source/filter/inc/tokstack.hxx427
-rw-r--r--sc/source/filter/inc/tool.h133
-rw-r--r--sc/source/filter/inc/unitconverter.hxx96
-rw-r--r--sc/source/filter/inc/viewsettings.hxx189
-rw-r--r--sc/source/filter/inc/workbookfragment.hxx64
-rw-r--r--sc/source/filter/inc/workbookhelper.hxx325
-rw-r--r--sc/source/filter/inc/workbooksettings.hxx119
-rw-r--r--sc/source/filter/inc/worksheetbuffer.hxx108
-rw-r--r--sc/source/filter/inc/worksheetfragment.hxx182
-rw-r--r--sc/source/filter/inc/worksheethelper.hxx304
-rw-r--r--sc/source/filter/inc/worksheetsettings.hxx115
-rw-r--r--sc/source/filter/inc/xcl97esc.hxx176
-rw-r--r--sc/source/filter/inc/xcl97rec.hxx601
-rw-r--r--sc/source/filter/inc/xechart.hxx1192
-rw-r--r--sc/source/filter/inc/xecontent.hxx416
-rw-r--r--sc/source/filter/inc/xedbdata.hxx66
-rw-r--r--sc/source/filter/inc/xeescher.hxx466
-rw-r--r--sc/source/filter/inc/xeextlst.hxx208
-rw-r--r--sc/source/filter/inc/xeformula.hxx91
-rw-r--r--sc/source/filter/inc/xehelper.hxx438
-rw-r--r--sc/source/filter/inc/xelink.hxx222
-rw-r--r--sc/source/filter/inc/xename.hxx73
-rw-r--r--sc/source/filter/inc/xepage.hxx124
-rw-r--r--sc/source/filter/inc/xepivot.hxx436
-rw-r--r--sc/source/filter/inc/xepivotxml.hxx90
-rw-r--r--sc/source/filter/inc/xerecord.hxx419
-rw-r--r--sc/source/filter/inc/xeroot.hxx191
-rw-r--r--sc/source/filter/inc/xestream.hxx359
-rw-r--r--sc/source/filter/inc/xestring.hxx264
-rw-r--r--sc/source/filter/inc/xestyle.hxx786
-rw-r--r--sc/source/filter/inc/xetable.hxx1028
-rw-r--r--sc/source/filter/inc/xeview.hxx164
-rw-r--r--sc/source/filter/inc/xichart.hxx1433
-rw-r--r--sc/source/filter/inc/xicontent.hxx330
-rw-r--r--sc/source/filter/inc/xiescher.hxx1218
-rw-r--r--sc/source/filter/inc/xiformula.hxx54
-rw-r--r--sc/source/filter/inc/xihelper.hxx350
-rw-r--r--sc/source/filter/inc/xilink.hxx228
-rw-r--r--sc/source/filter/inc/xiname.hxx107
-rw-r--r--sc/source/filter/inc/xipage.hxx70
-rw-r--r--sc/source/filter/inc/xipivot.hxx427
-rw-r--r--sc/source/filter/inc/xiroot.hxx219
-rw-r--r--sc/source/filter/inc/xistream.hxx552
-rw-r--r--sc/source/filter/inc/xistring.hxx104
-rw-r--r--sc/source/filter/inc/xistyle.hxx670
-rw-r--r--sc/source/filter/inc/xiview.hxx83
-rw-r--r--sc/source/filter/inc/xladdress.hxx169
-rw-r--r--sc/source/filter/inc/xlchart.hxx1439
-rw-r--r--sc/source/filter/inc/xlconst.hxx259
-rw-r--r--sc/source/filter/inc/xlcontent.hxx187
-rw-r--r--sc/source/filter/inc/xlescher.hxx434
-rw-r--r--sc/source/filter/inc/xlformula.hxx551
-rw-r--r--sc/source/filter/inc/xllink.hxx99
-rw-r--r--sc/source/filter/inc/xlname.hxx63
-rw-r--r--sc/source/filter/inc/xlpage.hxx166
-rw-r--r--sc/source/filter/inc/xlpivot.hxx774
-rw-r--r--sc/source/filter/inc/xlroot.hxx270
-rw-r--r--sc/source/filter/inc/xlstream.hxx38
-rw-r--r--sc/source/filter/inc/xlstring.hxx87
-rw-r--r--sc/source/filter/inc/xlstyle.hxx598
-rw-r--r--sc/source/filter/inc/xltable.hxx167
-rw-r--r--sc/source/filter/inc/xltools.hxx256
-rw-r--r--sc/source/filter/inc/xltracer.hxx86
-rw-r--r--sc/source/filter/inc/xlview.hxx162
-rw-r--r--sc/source/filter/lotus/filter.cxx212
-rw-r--r--sc/source/filter/lotus/lotattr.cxx250
-rw-r--r--sc/source/filter/lotus/lotfilter.hxx63
-rw-r--r--sc/source/filter/lotus/lotform.cxx2106
-rw-r--r--sc/source/filter/lotus/lotimpop.cxx489
-rw-r--r--sc/source/filter/lotus/lotread.cxx337
-rw-r--r--sc/source/filter/lotus/lotus.cxx100
-rw-r--r--sc/source/filter/lotus/memory.cxx55
-rw-r--r--sc/source/filter/lotus/op.cxx684
-rw-r--r--sc/source/filter/lotus/optab.cxx235
-rw-r--r--sc/source/filter/lotus/tool.cxx524
-rw-r--r--sc/source/filter/oox/SparklineFragment.cxx288
-rw-r--r--sc/source/filter/oox/addressconverter.cxx486
-rw-r--r--sc/source/filter/oox/autofilterbuffer.cxx973
-rw-r--r--sc/source/filter/oox/autofiltercontext.cxx219
-rw-r--r--sc/source/filter/oox/biffhelper.cxx99
-rw-r--r--sc/source/filter/oox/chartsheetfragment.cxx174
-rw-r--r--sc/source/filter/oox/commentsbuffer.cxx310
-rw-r--r--sc/source/filter/oox/commentsfragment.cxx144
-rw-r--r--sc/source/filter/oox/condformatbuffer.cxx1582
-rw-r--r--sc/source/filter/oox/condformatcontext.cxx274
-rw-r--r--sc/source/filter/oox/connectionsbuffer.cxx315
-rw-r--r--sc/source/filter/oox/connectionsfragment.cxx161
-rw-r--r--sc/source/filter/oox/defnamesbuffer.cxx453
-rw-r--r--sc/source/filter/oox/drawingbase.cxx295
-rw-r--r--sc/source/filter/oox/drawingfragment.cxx843
-rw-r--r--sc/source/filter/oox/excelchartconverter.cxx125
-rw-r--r--sc/source/filter/oox/excelfilter.cxx215
-rw-r--r--sc/source/filter/oox/excelhandlers.cxx44
-rw-r--r--sc/source/filter/oox/excelvbaproject.cxx128
-rw-r--r--sc/source/filter/oox/externallinkbuffer.cxx695
-rw-r--r--sc/source/filter/oox/externallinkfragment.cxx338
-rw-r--r--sc/source/filter/oox/extlstcontext.cxx484
-rw-r--r--sc/source/filter/oox/formulabase.cxx1707
-rw-r--r--sc/source/filter/oox/formulabuffer.cxx519
-rw-r--r--sc/source/filter/oox/formulaparser.cxx1858
-rw-r--r--sc/source/filter/oox/numberformatsbuffer.cxx2098
-rw-r--r--sc/source/filter/oox/ooxformulaparser.cxx174
-rw-r--r--sc/source/filter/oox/pagesettings.cxx1065
-rw-r--r--sc/source/filter/oox/patterncache.cxx52
-rw-r--r--sc/source/filter/oox/pivotcachebuffer.cxx1233
-rw-r--r--sc/source/filter/oox/pivotcachefragment.cxx325
-rw-r--r--sc/source/filter/oox/pivottablebuffer.cxx1493
-rw-r--r--sc/source/filter/oox/pivottablefragment.cxx280
-rw-r--r--sc/source/filter/oox/querytablebuffer.cxx287
-rw-r--r--sc/source/filter/oox/querytablefragment.cxx72
-rw-r--r--sc/source/filter/oox/revisionfragment.cxx450
-rw-r--r--sc/source/filter/oox/richstring.cxx499
-rw-r--r--sc/source/filter/oox/richstringcontext.cxx93
-rw-r--r--sc/source/filter/oox/scenariobuffer.cxx216
-rw-r--r--sc/source/filter/oox/scenariocontext.cxx112
-rw-r--r--sc/source/filter/oox/sharedstringsbuffer.cxx49
-rw-r--r--sc/source/filter/oox/sharedstringsfragment.cxx88
-rw-r--r--sc/source/filter/oox/sheetdatabuffer.cxx814
-rw-r--r--sc/source/filter/oox/sheetdatacontext.cxx587
-rw-r--r--sc/source/filter/oox/stylesbuffer.cxx3115
-rw-r--r--sc/source/filter/oox/stylesfragment.cxx317
-rw-r--r--sc/source/filter/oox/tablebuffer.cxx215
-rw-r--r--sc/source/filter/oox/tablecolumnsbuffer.cxx137
-rw-r--r--sc/source/filter/oox/tablecolumnscontext.cxx92
-rw-r--r--sc/source/filter/oox/tablefragment.cxx97
-rw-r--r--sc/source/filter/oox/themebuffer.cxx54
-rw-r--r--sc/source/filter/oox/unitconverter.cxx229
-rw-r--r--sc/source/filter/oox/viewsettings.cxx651
-rw-r--r--sc/source/filter/oox/workbookfragment.cxx669
-rw-r--r--sc/source/filter/oox/workbookhelper.cxx1063
-rw-r--r--sc/source/filter/oox/workbooksettings.cxx299
-rw-r--r--sc/source/filter/oox/worksheetbuffer.cxx252
-rw-r--r--sc/source/filter/oox/worksheetfragment.cxx909
-rw-r--r--sc/source/filter/oox/worksheethelper.cxx1665
-rw-r--r--sc/source/filter/oox/worksheetsettings.cxx294
-rw-r--r--sc/source/filter/orcus/filterdetect.cxx111
-rw-r--r--sc/source/filter/orcus/interface.cxx2481
-rw-r--r--sc/source/filter/orcus/orcusfiltersimpl.cxx129
-rw-r--r--sc/source/filter/orcus/xmlcontext.cxx280
-rw-r--r--sc/source/filter/qpro/README4
-rw-r--r--sc/source/filter/qpro/qpro.cxx320
-rw-r--r--sc/source/filter/qpro/qproform.cxx752
-rw-r--r--sc/source/filter/qpro/qprostyle.cxx139
-rw-r--r--sc/source/filter/rtf/eeimpars.cxx663
-rw-r--r--sc/source/filter/rtf/expbase.cxx75
-rw-r--r--sc/source/filter/rtf/rtfexp.cxx310
-rw-r--r--sc/source/filter/rtf/rtfimp.cxx47
-rw-r--r--sc/source/filter/rtf/rtfparse.cxx403
-rw-r--r--sc/source/filter/xcl97/XclExpChangeTrack.cxx1729
-rw-r--r--sc/source/filter/xcl97/XclImpChangeTrack.cxx512
-rw-r--r--sc/source/filter/xcl97/xcl97esc.cxx572
-rw-r--r--sc/source/filter/xcl97/xcl97rec.cxx2028
-rw-r--r--sc/source/filter/xml/SparklineGroupsExport.cxx238
-rw-r--r--sc/source/filter/xml/SparklineGroupsExport.hxx53
-rw-r--r--sc/source/filter/xml/SparklineGroupsImportContext.cxx405
-rw-r--r--sc/source/filter/xml/SparklineGroupsImportContext.hxx82
-rw-r--r--sc/source/filter/xml/XMLCalculationSettingsContext.cxx199
-rw-r--r--sc/source/filter/xml/XMLCalculationSettingsContext.hxx76
-rw-r--r--sc/source/filter/xml/XMLCellRangeSourceContext.cxx96
-rw-r--r--sc/source/filter/xml/XMLCellRangeSourceContext.hxx51
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx687
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx84
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx804
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx221
-rw-r--r--sc/source/filter/xml/XMLCodeNameProvider.cxx178
-rw-r--r--sc/source/filter/xml/XMLCodeNameProvider.hxx53
-rw-r--r--sc/source/filter/xml/XMLColumnRowGroupExport.cxx148
-rw-r--r--sc/source/filter/xml/XMLColumnRowGroupExport.hxx62
-rw-r--r--sc/source/filter/xml/XMLConsolidationContext.cxx123
-rw-r--r--sc/source/filter/xml/XMLConsolidationContext.hxx49
-rw-r--r--sc/source/filter/xml/XMLConverter.cxx630
-rw-r--r--sc/source/filter/xml/XMLConverter.hxx148
-rw-r--r--sc/source/filter/xml/XMLDDELinksContext.cxx362
-rw-r--r--sc/source/filter/xml/XMLDDELinksContext.hxx158
-rw-r--r--sc/source/filter/xml/XMLDetectiveContext.cxx190
-rw-r--r--sc/source/filter/xml/XMLDetectiveContext.hxx127
-rw-r--r--sc/source/filter/xml/XMLEmptyContext.cxx41
-rw-r--r--sc/source/filter/xml/XMLEmptyContext.hxx35
-rw-r--r--sc/source/filter/xml/XMLExportDDELinks.cxx159
-rw-r--r--sc/source/filter/xml/XMLExportDDELinks.hxx41
-rw-r--r--sc/source/filter/xml/XMLExportDataPilot.cxx905
-rw-r--r--sc/source/filter/xml/XMLExportDataPilot.hxx74
-rw-r--r--sc/source/filter/xml/XMLExportDatabaseRanges.cxx752
-rw-r--r--sc/source/filter/xml/XMLExportDatabaseRanges.hxx43
-rw-r--r--sc/source/filter/xml/XMLExportIterator.cxx731
-rw-r--r--sc/source/filter/xml/XMLExportIterator.hxx372
-rw-r--r--sc/source/filter/xml/XMLExportSharedData.cxx136
-rw-r--r--sc/source/filter/xml/XMLExportSharedData.hxx83
-rw-r--r--sc/source/filter/xml/XMLStylesExportHelper.cxx1066
-rw-r--r--sc/source/filter/xml/XMLStylesExportHelper.hxx254
-rw-r--r--sc/source/filter/xml/XMLStylesImportHelper.cxx401
-rw-r--r--sc/source/filter/xml/XMLStylesImportHelper.hxx148
-rw-r--r--sc/source/filter/xml/XMLTableHeaderFooterContext.cxx237
-rw-r--r--sc/source/filter/xml/XMLTableHeaderFooterContext.hxx78
-rw-r--r--sc/source/filter/xml/XMLTableMasterPageExport.cxx205
-rw-r--r--sc/source/filter/xml/XMLTableMasterPageExport.hxx53
-rw-r--r--sc/source/filter/xml/XMLTableShapeImportHelper.cxx231
-rw-r--r--sc/source/filter/xml/XMLTableShapeImportHelper.hxx52
-rw-r--r--sc/source/filter/xml/XMLTableShapeResizer.cxx144
-rw-r--r--sc/source/filter/xml/XMLTableShapeResizer.hxx56
-rw-r--r--sc/source/filter/xml/XMLTableShapesContext.cxx53
-rw-r--r--sc/source/filter/xml/XMLTableShapesContext.hxx38
-rw-r--r--sc/source/filter/xml/XMLTableSourceContext.cxx107
-rw-r--r--sc/source/filter/xml/XMLTableSourceContext.hxx45
-rw-r--r--sc/source/filter/xml/XMLTrackedChangesContext.cxx1397
-rw-r--r--sc/source/filter/xml/XMLTrackedChangesContext.hxx46
-rw-r--r--sc/source/filter/xml/cachedattraccess.cxx55
-rw-r--r--sc/source/filter/xml/cachedattraccess.hxx44
-rw-r--r--sc/source/filter/xml/celltextparacontext.cxx454
-rw-r--r--sc/source/filter/xml/celltextparacontext.hxx191
-rw-r--r--sc/source/filter/xml/datastreamimport.cxx84
-rw-r--r--sc/source/filter/xml/datastreamimport.hxx36
-rw-r--r--sc/source/filter/xml/editattributemap.cxx91
-rw-r--r--sc/source/filter/xml/editattributemap.hxx44
-rw-r--r--sc/source/filter/xml/importcontext.cxx28
-rw-r--r--sc/source/filter/xml/importcontext.hxx29
-rw-r--r--sc/source/filter/xml/pivotsource.cxx120
-rw-r--r--sc/source/filter/xml/pivotsource.hxx78
-rw-r--r--sc/source/filter/xml/sheetdata.cxx245
-rw-r--r--sc/source/filter/xml/xmlannoi.cxx171
-rw-r--r--sc/source/filter/xml/xmlannoi.hxx104
-rw-r--r--sc/source/filter/xml/xmlbodyi.cxx272
-rw-r--r--sc/source/filter/xml/xmlbodyi.hxx56
-rw-r--r--sc/source/filter/xml/xmlcelli.cxx1532
-rw-r--r--sc/source/filter/xml/xmlcelli.hxx153
-rw-r--r--sc/source/filter/xml/xmlcoli.cxx250
-rw-r--r--sc/source/filter/xml/xmlcoli.hxx66
-rw-r--r--sc/source/filter/xml/xmlcondformat.cxx1007
-rw-r--r--sc/source/filter/xml/xmlcondformat.hxx154
-rw-r--r--sc/source/filter/xml/xmlconti.cxx65
-rw-r--r--sc/source/filter/xml/xmlconti.hxx41
-rw-r--r--sc/source/filter/xml/xmlcvali.cxx555
-rw-r--r--sc/source/filter/xml/xmlcvali.hxx35
-rw-r--r--sc/source/filter/xml/xmldpimp.cxx1562
-rw-r--r--sc/source/filter/xml/xmldpimp.hxx473
-rw-r--r--sc/source/filter/xml/xmldrani.cxx812
-rw-r--r--sc/source/filter/xml/xmldrani.hxx256
-rw-r--r--sc/source/filter/xml/xmlexprt.cxx5488
-rw-r--r--sc/source/filter/xml/xmlexprt.hxx279
-rw-r--r--sc/source/filter/xml/xmlexternaltabi.cxx378
-rw-r--r--sc/source/filter/xml/xmlexternaltabi.hxx132
-rw-r--r--sc/source/filter/xml/xmlfilti.cxx784
-rw-r--r--sc/source/filter/xml/xmlfilti.hxx265
-rw-r--r--sc/source/filter/xml/xmlfonte.cxx156
-rw-r--r--sc/source/filter/xml/xmlimprt.cxx1768
-rw-r--r--sc/source/filter/xml/xmlimprt.hxx357
-rw-r--r--sc/source/filter/xml/xmllabri.cxx98
-rw-r--r--sc/source/filter/xml/xmllabri.hxx57
-rw-r--r--sc/source/filter/xml/xmlmappingi.cxx140
-rw-r--r--sc/source/filter/xml/xmlmappingi.hxx45
-rw-r--r--sc/source/filter/xml/xmlnexpi.cxx165
-rw-r--r--sc/source/filter/xml/xmlnexpi.hxx106
-rw-r--r--sc/source/filter/xml/xmlrowi.cxx360
-rw-r--r--sc/source/filter/xml/xmlrowi.hxx72
-rw-r--r--sc/source/filter/xml/xmlsceni.cxx125
-rw-r--r--sc/source/filter/xml/xmlsceni.hxx52
-rw-r--r--sc/source/filter/xml/xmlsorti.cxx244
-rw-r--r--sc/source/filter/xml/xmlsorti.hxx81
-rw-r--r--sc/source/filter/xml/xmlstyle.cxx1930
-rw-r--r--sc/source/filter/xml/xmlstyle.hxx340
-rw-r--r--sc/source/filter/xml/xmlstyli.cxx1068
-rw-r--r--sc/source/filter/xml/xmlstyli.hxx235
-rw-r--r--sc/source/filter/xml/xmlsubti.cxx291
-rw-r--r--sc/source/filter/xml/xmlsubti.hxx108
-rw-r--r--sc/source/filter/xml/xmltabi.cxx469
-rw-r--r--sc/source/filter/xml/xmltabi.hxx70
-rw-r--r--sc/source/filter/xml/xmltransformationi.cxx637
-rw-r--r--sc/source/filter/xml/xmltransformationi.hxx169
-rw-r--r--sc/source/filter/xml/xmlwrap.cxx996
-rw-r--r--sc/source/ui/Accessibility/AccessibilityHints.cxx62
-rw-r--r--sc/source/ui/Accessibility/AccessibleCell.cxx615
-rw-r--r--sc/source/ui/Accessibility/AccessibleCellBase.cxx587
-rw-r--r--sc/source/ui/Accessibility/AccessibleContextBase.cxx493
-rw-r--r--sc/source/ui/Accessibility/AccessibleCsvControl.cxx1405
-rw-r--r--sc/source/ui/Accessibility/AccessibleDocument.cxx2222
-rw-r--r--sc/source/ui/Accessibility/AccessibleDocumentBase.cxx36
-rw-r--r--sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx1569
-rw-r--r--sc/source/ui/Accessibility/AccessibleEditObject.cxx600
-rw-r--r--sc/source/ui/Accessibility/AccessiblePageHeader.cxx372
-rw-r--r--sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx273
-rw-r--r--sc/source/ui/Accessibility/AccessiblePreviewCell.cxx272
-rw-r--r--sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx403
-rw-r--r--sc/source/ui/Accessibility/AccessiblePreviewTable.cxx640
-rw-r--r--sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx1669
-rw-r--r--sc/source/ui/Accessibility/AccessibleTableBase.cxx466
-rw-r--r--sc/source/ui/Accessibility/AccessibleText.cxx1386
-rw-r--r--sc/source/ui/Accessibility/DrawModelBroadcaster.cxx107
-rw-r--r--sc/source/ui/StatisticsDialogs/AnalysisOfVarianceDialog.cxx559
-rw-r--r--sc/source/ui/StatisticsDialogs/ChiSquareTestDialog.cxx91
-rw-r--r--sc/source/ui/StatisticsDialogs/CorrelationDialog.cxx39
-rw-r--r--sc/source/ui/StatisticsDialogs/CovarianceDialog.cxx44
-rw-r--r--sc/source/ui/StatisticsDialogs/DescriptiveStatisticsDialog.cxx141
-rw-r--r--sc/source/ui/StatisticsDialogs/ExponentialSmoothingDialog.cxx120
-rw-r--r--sc/source/ui/StatisticsDialogs/FTestDialog.cxx171
-rw-r--r--sc/source/ui/StatisticsDialogs/FourierAnalysisDialog.cxx231
-rw-r--r--sc/source/ui/StatisticsDialogs/MatrixComparisonGenerator.cxx113
-rw-r--r--sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx116
-rw-r--r--sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx500
-rw-r--r--sc/source/ui/StatisticsDialogs/RegressionDialog.cxx695
-rw-r--r--sc/source/ui/StatisticsDialogs/SamplingDialog.cxx563
-rw-r--r--sc/source/ui/StatisticsDialogs/StatisticsInputOutputDialog.cxx305
-rw-r--r--sc/source/ui/StatisticsDialogs/StatisticsTwoVariableDialog.cxx347
-rw-r--r--sc/source/ui/StatisticsDialogs/TTestDialog.cxx183
-rw-r--r--sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx386
-rw-r--r--sc/source/ui/StatisticsDialogs/ZTestDialog.cxx167
-rw-r--r--sc/source/ui/app/client.cxx240
-rw-r--r--sc/source/ui/app/drwtrans.cxx734
-rw-r--r--sc/source/ui/app/inputhdl.cxx4733
-rw-r--r--sc/source/ui/app/inputwin.cxx2762
-rw-r--r--sc/source/ui/app/lnktrans.cxx76
-rw-r--r--sc/source/ui/app/msgpool.cxx94
-rw-r--r--sc/source/ui/app/rfindlst.cxx91
-rw-r--r--sc/source/ui/app/scdll.cxx258
-rw-r--r--sc/source/ui/app/scmod.cxx2354
-rw-r--r--sc/source/ui/app/seltrans.cxx429
-rw-r--r--sc/source/ui/app/transobj.cxx903
-rw-r--r--sc/source/ui/app/typemap.cxx141
-rw-r--r--sc/source/ui/app/uiitems.cxx439
-rw-r--r--sc/source/ui/attrdlg/attrdlg.cxx96
-rw-r--r--sc/source/ui/attrdlg/scabstdlg.cxx55
-rw-r--r--sc/source/ui/attrdlg/scdlgfact.cxx1397
-rw-r--r--sc/source/ui/attrdlg/scdlgfact.hxx814
-rw-r--r--sc/source/ui/attrdlg/scuiexp.cxx33
-rw-r--r--sc/source/ui/attrdlg/tabpages.cxx220
-rw-r--r--sc/source/ui/cctrl/cbnumberformat.cxx89
-rw-r--r--sc/source/ui/cctrl/cbuttonw.cxx139
-rw-r--r--sc/source/ui/cctrl/checklistmenu.cxx1832
-rw-r--r--sc/source/ui/cctrl/dpcontrol.cxx300
-rw-r--r--sc/source/ui/cctrl/editfield.cxx63
-rw-r--r--sc/source/ui/cctrl/tbzoomsliderctrl.cxx458
-rw-r--r--sc/source/ui/condformat/colorformat.cxx302
-rw-r--r--sc/source/ui/condformat/condformatdlg.cxx709
-rw-r--r--sc/source/ui/condformat/condformatdlgentry.cxx1538
-rw-r--r--sc/source/ui/condformat/condformatdlgitem.cxx66
-rw-r--r--sc/source/ui/condformat/condformateasydlg.cxx230
-rw-r--r--sc/source/ui/condformat/condformathelper.cxx225
-rw-r--r--sc/source/ui/condformat/condformatmgr.cxx178
-rw-r--r--sc/source/ui/dataprovider/csvdataprovider.cxx173
-rw-r--r--sc/source/ui/dataprovider/dataprovider.cxx314
-rw-r--r--sc/source/ui/dataprovider/datatransformation.cxx1276
-rw-r--r--sc/source/ui/dataprovider/htmldataprovider.cxx280
-rw-r--r--sc/source/ui/dataprovider/htmldataprovider.hxx38
-rw-r--r--sc/source/ui/dataprovider/sqldataprovider.cxx170
-rw-r--r--sc/source/ui/dataprovider/sqldataprovider.hxx38
-rw-r--r--sc/source/ui/dataprovider/xmldataprovider.cxx126
-rw-r--r--sc/source/ui/dataprovider/xmldataprovider.hxx37
-rw-r--r--sc/source/ui/dbgui/PivotLayoutDialog.cxx722
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeList.cxx132
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx122
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeListData.cxx287
-rw-r--r--sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx85
-rw-r--r--sc/source/ui/dbgui/asciiopt.cxx313
-rw-r--r--sc/source/ui/dbgui/consdlg.cxx534
-rw-r--r--sc/source/ui/dbgui/csvcontrol.cxx284
-rw-r--r--sc/source/ui/dbgui/csvgrid.cxx1416
-rw-r--r--sc/source/ui/dbgui/csvruler.cxx665
-rw-r--r--sc/source/ui/dbgui/csvsplits.cxx102
-rw-r--r--sc/source/ui/dbgui/csvtablebox.cxx369
-rw-r--r--sc/source/ui/dbgui/dapidata.cxx169
-rw-r--r--sc/source/ui/dbgui/dapitype.cxx153
-rw-r--r--sc/source/ui/dbgui/dbnamdlg.cxx634
-rw-r--r--sc/source/ui/dbgui/dpgroupdlg.cxx353
-rw-r--r--sc/source/ui/dbgui/filtdlg.cxx1571
-rw-r--r--sc/source/ui/dbgui/foptmgr.cxx260
-rw-r--r--sc/source/ui/dbgui/imoptdlg.cxx140
-rw-r--r--sc/source/ui/dbgui/pfiltdlg.cxx507
-rw-r--r--sc/source/ui/dbgui/pvfundlg.cxx964
-rw-r--r--sc/source/ui/dbgui/scendlg.cxx163
-rw-r--r--sc/source/ui/dbgui/scuiasciiopt.cxx975
-rw-r--r--sc/source/ui/dbgui/scuiimoptdlg.cxx340
-rw-r--r--sc/source/ui/dbgui/sfiltdlg.cxx435
-rw-r--r--sc/source/ui/dbgui/sortdlg.cxx79
-rw-r--r--sc/source/ui/dbgui/sortkeydlg.cxx81
-rw-r--r--sc/source/ui/dbgui/subtdlg.cxx46
-rw-r--r--sc/source/ui/dbgui/textimportoptions.cxx120
-rw-r--r--sc/source/ui/dbgui/tpsort.cxx873
-rw-r--r--sc/source/ui/dbgui/tpsubt.cxx621
-rw-r--r--sc/source/ui/dbgui/validate.cxx931
-rw-r--r--sc/source/ui/dialogs/SparklineDataRangeDialog.cxx202
-rw-r--r--sc/source/ui/dialogs/SparklineDialog.cxx552
-rw-r--r--sc/source/ui/dialogs/searchresults.cxx287
-rw-r--r--sc/source/ui/docshell/arealink.cxx498
-rw-r--r--sc/source/ui/docshell/autostyl.cxx191
-rw-r--r--sc/source/ui/docshell/datastream.cxx549
-rw-r--r--sc/source/ui/docshell/dbdocfun.cxx1790
-rw-r--r--sc/source/ui/docshell/dbdocimp.cxx628
-rw-r--r--sc/source/ui/docshell/docfunc.cxx5923
-rw-r--r--sc/source/ui/docshell/docfuncutil.cxx116
-rw-r--r--sc/source/ui/docshell/docsh.cxx3458
-rw-r--r--sc/source/ui/docshell/docsh2.cxx184
-rw-r--r--sc/source/ui/docshell/docsh3.cxx1334
-rw-r--r--sc/source/ui/docshell/docsh4.cxx2788
-rw-r--r--sc/source/ui/docshell/docsh5.cxx1047
-rw-r--r--sc/source/ui/docshell/docsh6.cxx517
-rw-r--r--sc/source/ui/docshell/docsh8.cxx1082
-rw-r--r--sc/source/ui/docshell/docshimp.hxx38
-rw-r--r--sc/source/ui/docshell/documentlinkmgr.cxx292
-rw-r--r--sc/source/ui/docshell/editable.cxx162
-rw-r--r--sc/source/ui/docshell/externalrefmgr.cxx3323
-rw-r--r--sc/source/ui/docshell/impex.cxx2894
-rw-r--r--sc/source/ui/docshell/macromgr.cxx201
-rw-r--r--sc/source/ui/docshell/olinefun.cxx797
-rw-r--r--sc/source/ui/docshell/pagedata.cxx100
-rw-r--r--sc/source/ui/docshell/pntlock.cxx43
-rw-r--r--sc/source/ui/docshell/servobj.cxx258
-rw-r--r--sc/source/ui/docshell/sizedev.cxx63
-rw-r--r--sc/source/ui/docshell/tablink.cxx561
-rw-r--r--sc/source/ui/docshell/tpstat.cxx70
-rw-r--r--sc/source/ui/drawfunc/chartsh.cxx155
-rw-r--r--sc/source/ui/drawfunc/drawsh.cxx596
-rw-r--r--sc/source/ui/drawfunc/drawsh2.cxx564
-rw-r--r--sc/source/ui/drawfunc/drawsh4.cxx65
-rw-r--r--sc/source/ui/drawfunc/drawsh5.cxx724
-rw-r--r--sc/source/ui/drawfunc/drformsh.cxx55
-rw-r--r--sc/source/ui/drawfunc/drtxtob.cxx1256
-rw-r--r--sc/source/ui/drawfunc/drtxtob1.cxx125
-rw-r--r--sc/source/ui/drawfunc/drtxtob2.cxx229
-rw-r--r--sc/source/ui/drawfunc/fuconarc.cxx154
-rw-r--r--sc/source/ui/drawfunc/fuconcustomshape.cxx199
-rw-r--r--sc/source/ui/drawfunc/fuconpol.cxx288
-rw-r--r--sc/source/ui/drawfunc/fuconrec.cxx290
-rw-r--r--sc/source/ui/drawfunc/fuconstr.cxx252
-rw-r--r--sc/source/ui/drawfunc/fuconuno.cxx122
-rw-r--r--sc/source/ui/drawfunc/fudraw.cxx766
-rw-r--r--sc/source/ui/drawfunc/fuins1.cxx449
-rw-r--r--sc/source/ui/drawfunc/fuins2.cxx703
-rw-r--r--sc/source/ui/drawfunc/fupoor.cxx279
-rw-r--r--sc/source/ui/drawfunc/fusel.cxx558
-rw-r--r--sc/source/ui/drawfunc/fusel2.cxx148
-rw-r--r--sc/source/ui/drawfunc/futext.cxx688
-rw-r--r--sc/source/ui/drawfunc/futext2.cxx46
-rw-r--r--sc/source/ui/drawfunc/futext3.cxx186
-rw-r--r--sc/source/ui/drawfunc/graphsh.cxx380
-rw-r--r--sc/source/ui/drawfunc/mediash.cxx65
-rw-r--r--sc/source/ui/drawfunc/oleobjsh.cxx51
-rw-r--r--sc/source/ui/formdlg/dwfunctr.cxx531
-rw-r--r--sc/source/ui/formdlg/formdata.cxx33
-rw-r--r--sc/source/ui/formdlg/formula.cxx691
-rw-r--r--sc/source/ui/inc/AccessibilityHints.hxx57
-rw-r--r--sc/source/ui/inc/AccessibleCell.hxx164
-rw-r--r--sc/source/ui/inc/AccessibleCellBase.hxx136
-rw-r--r--sc/source/ui/inc/AccessibleContextBase.hxx256
-rw-r--r--sc/source/ui/inc/AccessibleCsvControl.hxx483
-rw-r--r--sc/source/ui/inc/AccessibleDocument.hxx261
-rw-r--r--sc/source/ui/inc/AccessibleDocumentBase.hxx34
-rw-r--r--sc/source/ui/inc/AccessibleDocumentPagePreview.hxx129
-rw-r--r--sc/source/ui/inc/AccessibleEditObject.hxx228
-rw-r--r--sc/source/ui/inc/AccessiblePageHeader.hxx88
-rw-r--r--sc/source/ui/inc/AccessiblePageHeaderArea.hxx112
-rw-r--r--sc/source/ui/inc/AccessiblePreviewCell.hxx96
-rw-r--r--sc/source/ui/inc/AccessiblePreviewHeaderCell.hxx128
-rw-r--r--sc/source/ui/inc/AccessiblePreviewTable.hxx132
-rw-r--r--sc/source/ui/inc/AccessibleSpreadsheet.hxx272
-rw-r--r--sc/source/ui/inc/AccessibleTableBase.hxx234
-rw-r--r--sc/source/ui/inc/AccessibleText.hxx290
-rw-r--r--sc/source/ui/inc/AnalysisOfVarianceDialog.hxx61
-rw-r--r--sc/source/ui/inc/ChartRangeSelectionListener.hxx64
-rw-r--r--sc/source/ui/inc/ChiSquareTestDialog.hxx31
-rw-r--r--sc/source/ui/inc/ChildWindowWrapper.hxx81
-rw-r--r--sc/source/ui/inc/CorrelationDialog.hxx29
-rw-r--r--sc/source/ui/inc/CovarianceDialog.hxx30
-rw-r--r--sc/source/ui/inc/DescriptiveStatisticsDialog.hxx31
-rw-r--r--sc/source/ui/inc/DrawModelBroadcaster.hxx55
-rw-r--r--sc/source/ui/inc/ExponentialSmoothingDialog.hxx37
-rw-r--r--sc/source/ui/inc/FTestDialog.hxx31
-rw-r--r--sc/source/ui/inc/FilterListBox.hxx81
-rw-r--r--sc/source/ui/inc/FourierAnalysisDialog.hxx56
-rw-r--r--sc/source/ui/inc/IAnyRefDialog.hxx49
-rw-r--r--sc/source/ui/inc/MatrixComparisonGenerator.hxx36
-rw-r--r--sc/source/ui/inc/MovingAverageDialog.hxx38
-rw-r--r--sc/source/ui/inc/PivotLayoutDialog.hxx138
-rw-r--r--sc/source/ui/inc/PivotLayoutTreeList.hxx41
-rw-r--r--sc/source/ui/inc/PivotLayoutTreeListBase.hxx70
-rw-r--r--sc/source/ui/inc/PivotLayoutTreeListData.hxx42
-rw-r--r--sc/source/ui/inc/PivotLayoutTreeListLabel.hxx34
-rw-r--r--sc/source/ui/inc/RandomNumberGeneratorDialog.hxx85
-rw-r--r--sc/source/ui/inc/RegressionDialog.hxx81
-rw-r--r--sc/source/ui/inc/SamplingDialog.hxx91
-rw-r--r--sc/source/ui/inc/SparklineDataRangeDialog.hxx67
-rw-r--r--sc/source/ui/inc/SparklineDialog.hxx113
-rw-r--r--sc/source/ui/inc/SparklineRenderer.hxx576
-rw-r--r--sc/source/ui/inc/SparklineShell.hxx41
-rw-r--r--sc/source/ui/inc/StatisticsInputOutputDialog.hxx90
-rw-r--r--sc/source/ui/inc/StatisticsTwoVariableDialog.hxx91
-rw-r--r--sc/source/ui/inc/TTestDialog.hxx31
-rw-r--r--sc/source/ui/inc/TableFillingAndNavigationTools.hxx159
-rw-r--r--sc/source/ui/inc/ThemeColorChanger.hxx32
-rw-r--r--sc/source/ui/inc/ZTestDialog.hxx31
-rw-r--r--sc/source/ui/inc/acredlin.hxx163
-rw-r--r--sc/source/ui/inc/anyrefdg.hxx165
-rw-r--r--sc/source/ui/inc/areasave.hxx70
-rw-r--r--sc/source/ui/inc/areasdlg.hxx88
-rw-r--r--sc/source/ui/inc/asciiopt.hxx114
-rw-r--r--sc/source/ui/inc/attrdlg.hxx40
-rw-r--r--sc/source/ui/inc/auditsh.hxx49
-rw-r--r--sc/source/ui/inc/autofmt.hxx91
-rw-r--r--sc/source/ui/inc/autostyl.hxx82
-rw-r--r--sc/source/ui/inc/cbnumberformat.hxx42
-rw-r--r--sc/source/ui/inc/cbutton.hxx56
-rw-r--r--sc/source/ui/inc/cellmergeoption.hxx34
-rw-r--r--sc/source/ui/inc/cellsh.hxx109
-rw-r--r--sc/source/ui/inc/chartsh.hxx48
-rw-r--r--sc/source/ui/inc/checklistmenu.hxx396
-rw-r--r--sc/source/ui/inc/client.hxx44
-rw-r--r--sc/source/ui/inc/cliputil.hxx31
-rw-r--r--sc/source/ui/inc/colorformat.hxx63
-rw-r--r--sc/source/ui/inc/colrowba.hxx90
-rw-r--r--sc/source/ui/inc/condformatdlg.hxx129
-rw-r--r--sc/source/ui/inc/condformatdlgentry.hxx331
-rw-r--r--sc/source/ui/inc/condformatdlgitem.hxx62
-rw-r--r--sc/source/ui/inc/condformateasydlg.hxx56
-rw-r--r--sc/source/ui/inc/condformathelper.hxx36
-rw-r--r--sc/source/ui/inc/condformatmgr.hxx66
-rw-r--r--sc/source/ui/inc/condformatuno.hxx366
-rw-r--r--sc/source/ui/inc/conflictsdlg.hxx148
-rw-r--r--sc/source/ui/inc/consdlg.hxx98
-rw-r--r--sc/source/ui/inc/content.hxx154
-rw-r--r--sc/source/ui/inc/corodlg.hxx45
-rw-r--r--sc/source/ui/inc/crdlg.hxx38
-rw-r--r--sc/source/ui/inc/crnrdlg.hxx94
-rw-r--r--sc/source/ui/inc/csvcontrol.hxx373
-rw-r--r--sc/source/ui/inc/csvgrid.hxx315
-rw-r--r--sc/source/ui/inc/csvruler.hxx181
-rw-r--r--sc/source/ui/inc/csvsplits.hxx81
-rw-r--r--sc/source/ui/inc/csvtablebox.hxx132
-rw-r--r--sc/source/ui/inc/dapidata.hxx44
-rw-r--r--sc/source/ui/inc/dapitype.hxx69
-rw-r--r--sc/source/ui/inc/datafdlg.hxx70
-rw-r--r--sc/source/ui/inc/dataprovider.hxx149
-rw-r--r--sc/source/ui/inc/dataproviderdlg.hxx100
-rw-r--r--sc/source/ui/inc/datastream.hxx125
-rw-r--r--sc/source/ui/inc/datastreamdlg.hxx62
-rw-r--r--sc/source/ui/inc/datatableview.hxx111
-rw-r--r--sc/source/ui/inc/datatransformation.hxx229
-rw-r--r--sc/source/ui/inc/dbdocfun.hxx101
-rw-r--r--sc/source/ui/inc/dbfunc.hxx123
-rw-r--r--sc/source/ui/inc/dbnamdlg.hxx100
-rw-r--r--sc/source/ui/inc/delcldlg.hxx41
-rw-r--r--sc/source/ui/inc/delcodlg.hxx53
-rw-r--r--sc/source/ui/inc/docfunc.hxx270
-rw-r--r--sc/source/ui/inc/docfuncutil.hxx42
-rw-r--r--sc/source/ui/inc/docsh.hxx524
-rw-r--r--sc/source/ui/inc/dpcontrol.hxx79
-rw-r--r--sc/source/ui/inc/dpgroupdlg.hxx138
-rw-r--r--sc/source/ui/inc/drawsh.hxx96
-rw-r--r--sc/source/ui/inc/drawutil.hxx38
-rw-r--r--sc/source/ui/inc/drawview.hxx179
-rw-r--r--sc/source/ui/inc/drformsh.hxx43
-rw-r--r--sc/source/ui/inc/drtxtob.hxx80
-rw-r--r--sc/source/ui/inc/drwtrans.hxx96
-rw-r--r--sc/source/ui/inc/dwfunctr.hxx81
-rw-r--r--sc/source/ui/inc/editable.hxx89
-rw-r--r--sc/source/ui/inc/editfield.hxx43
-rw-r--r--sc/source/ui/inc/editsh.hxx85
-rw-r--r--sc/source/ui/inc/filldlg.hxx101
-rw-r--r--sc/source/ui/inc/filtdlg.hxx244
-rw-r--r--sc/source/ui/inc/foptmgr.hxx83
-rw-r--r--sc/source/ui/inc/formatsh.hxx74
-rw-r--r--sc/source/ui/inc/formdata.hxx48
-rw-r--r--sc/source/ui/inc/formula.hxx104
-rw-r--r--sc/source/ui/inc/fuconarc.hxx43
-rw-r--r--sc/source/ui/inc/fuconcustomshape.hxx49
-rw-r--r--sc/source/ui/inc/fuconpol.hxx44
-rw-r--r--sc/source/ui/inc/fuconrec.hxx44
-rw-r--r--sc/source/ui/inc/fuconstr.hxx43
-rw-r--r--sc/source/ui/inc/fuconuno.hxx50
-rw-r--r--sc/source/ui/inc/fudraw.hxx54
-rw-r--r--sc/source/ui/inc/fuinsert.hxx59
-rw-r--r--sc/source/ui/inc/fupoor.hxx107
-rw-r--r--sc/source/ui/inc/fusel.hxx46
-rw-r--r--sc/source/ui/inc/futext.hxx56
-rw-r--r--sc/source/ui/inc/gototabdlg.hxx43
-rw-r--r--sc/source/ui/inc/graphsh.hxx70
-rw-r--r--sc/source/ui/inc/gridmerg.hxx52
-rw-r--r--sc/source/ui/inc/gridwin.hxx528
-rw-r--r--sc/source/ui/inc/groupdlg.hxx36
-rw-r--r--sc/source/ui/inc/hdrcont.hxx133
-rw-r--r--sc/source/ui/inc/hfedtdlg.hxx152
-rw-r--r--sc/source/ui/inc/highred.hxx73
-rw-r--r--sc/source/ui/inc/hiranges.hxx34
-rw-r--r--sc/source/ui/inc/imoptdlg.hxx61
-rw-r--r--sc/source/ui/inc/impex.hxx244
-rw-r--r--sc/source/ui/inc/inputhdl.hxx332
-rw-r--r--sc/source/ui/inc/inputwin.hxx370
-rw-r--r--sc/source/ui/inc/inscldlg.hxx41
-rw-r--r--sc/source/ui/inc/inscodlg.hxx105
-rw-r--r--sc/source/ui/inc/instbdlg.hxx93
-rw-r--r--sc/source/ui/inc/invmerge.hxx44
-rw-r--r--sc/source/ui/inc/lbseldlg.hxx38
-rw-r--r--sc/source/ui/inc/linkarea.hxx72
-rw-r--r--sc/source/ui/inc/lnktrans.hxx42
-rw-r--r--sc/source/ui/inc/mediash.hxx45
-rw-r--r--sc/source/ui/inc/mergecellsdialog.hxx35
-rw-r--r--sc/source/ui/inc/msgpool.hxx57
-rw-r--r--sc/source/ui/inc/mtrindlg.hxx49
-rw-r--r--sc/source/ui/inc/mvtabdlg.hxx84
-rw-r--r--sc/source/ui/inc/namecrea.hxx38
-rw-r--r--sc/source/ui/inc/namedefdlg.hxx88
-rw-r--r--sc/source/ui/inc/namedlg.hxx121
-rw-r--r--sc/source/ui/inc/namemgrtable.hxx91
-rw-r--r--sc/source/ui/inc/namepast.hxx53
-rw-r--r--sc/source/ui/inc/navcitem.hxx40
-rw-r--r--sc/source/ui/inc/navipi.hxx193
-rw-r--r--sc/source/ui/inc/navsett.hxx49
-rw-r--r--sc/source/ui/inc/notemark.hxx69
-rw-r--r--sc/source/ui/inc/oleobjsh.hxx43
-rw-r--r--sc/source/ui/inc/olinefun.hxx52
-rw-r--r--sc/source/ui/inc/olinewin.hxx225
-rw-r--r--sc/source/ui/inc/opredlin.hxx43
-rw-r--r--sc/source/ui/inc/optsolver.hxx209
-rw-r--r--sc/source/ui/inc/output.hxx395
-rw-r--r--sc/source/ui/inc/overlayobject.hxx65
-rw-r--r--sc/source/ui/inc/pagedata.hxx81
-rw-r--r--sc/source/ui/inc/pfiltdlg.hxx93
-rw-r--r--sc/source/ui/inc/pfuncache.hxx111
-rw-r--r--sc/source/ui/inc/pgbrksh.hxx43
-rw-r--r--sc/source/ui/inc/pivotsh.hxx52
-rw-r--r--sc/source/ui/inc/pntlock.hxx56
-rw-r--r--sc/source/ui/inc/preview.hxx163
-rw-r--r--sc/source/ui/inc/prevloc.hxx152
-rw-r--r--sc/source/ui/inc/prevwsh.hxx121
-rw-r--r--sc/source/ui/inc/printfun.hxx434
-rw-r--r--sc/source/ui/inc/protectiondlg.hxx71
-rw-r--r--sc/source/ui/inc/pvfundlg.hxx214
-rw-r--r--sc/source/ui/inc/redcom.hxx57
-rw-r--r--sc/source/ui/inc/reffact.hxx224
-rw-r--r--sc/source/ui/inc/refundo.hxx55
-rw-r--r--sc/source/ui/inc/retypepassdlg.hxx128
-rw-r--r--sc/source/ui/inc/rfindlst.hxx64
-rw-r--r--sc/source/ui/inc/scendlg.hxx58
-rw-r--r--sc/source/ui/inc/scui_def.hxx69
-rw-r--r--sc/source/ui/inc/scuiasciiopt.hxx137
-rw-r--r--sc/source/ui/inc/scuiautofmt.hxx78
-rw-r--r--sc/source/ui/inc/scuiimoptdlg.hxx74
-rw-r--r--sc/source/ui/inc/scuitphfedit.hxx158
-rw-r--r--sc/source/ui/inc/searchresults.hxx59
-rw-r--r--sc/source/ui/inc/select.hxx108
-rw-r--r--sc/source/ui/inc/selectionstate.hxx55
-rw-r--r--sc/source/ui/inc/seltrans.hxx72
-rw-r--r--sc/source/ui/inc/servobj.hxx64
-rw-r--r--sc/source/ui/inc/sharedocdlg.hxx52
-rw-r--r--sc/source/ui/inc/shtabdlg.hxx47
-rw-r--r--sc/source/ui/inc/simpref.hxx78
-rw-r--r--sc/source/ui/inc/sizedev.hxx46
-rw-r--r--sc/source/ui/inc/solveroptions.hxx126
-rw-r--r--sc/source/ui/inc/solverutil.hxx39
-rw-r--r--sc/source/ui/inc/solvrdlg.hxx90
-rw-r--r--sc/source/ui/inc/sortdlg.hxx44
-rw-r--r--sc/source/ui/inc/sortkeydlg.hxx52
-rw-r--r--sc/source/ui/inc/spelldialog.hxx90
-rw-r--r--sc/source/ui/inc/spelleng.hxx151
-rw-r--r--sc/source/ui/inc/spellparam.hxx71
-rw-r--r--sc/source/ui/inc/strindlg.hxx43
-rw-r--r--sc/source/ui/inc/styledlg.hxx57
-rw-r--r--sc/source/ui/inc/subtdlg.hxx35
-rw-r--r--sc/source/ui/inc/tabbgcolordlg.hxx68
-rw-r--r--sc/source/ui/inc/tabcont.hxx78
-rw-r--r--sc/source/ui/inc/tabopdlg.hxx93
-rw-r--r--sc/source/ui/inc/tabpages.hxx68
-rw-r--r--sc/source/ui/inc/tabsplit.hxx44
-rw-r--r--sc/source/ui/inc/tabview.hxx633
-rw-r--r--sc/source/ui/inc/tabvwsh.hxx442
-rw-r--r--sc/source/ui/inc/target.hxx39
-rw-r--r--sc/source/ui/inc/tbzoomsliderctrl.hxx86
-rw-r--r--sc/source/ui/inc/textdlgs.hxx48
-rw-r--r--sc/source/ui/inc/textimportoptions.hxx54
-rw-r--r--sc/source/ui/inc/tpcalc.hxx76
-rw-r--r--sc/source/ui/inc/tpcompatibility.hxx32
-rw-r--r--sc/source/ui/inc/tpdefaults.hxx48
-rw-r--r--sc/source/ui/inc/tpformula.hxx87
-rw-r--r--sc/source/ui/inc/tphf.hxx70
-rw-r--r--sc/source/ui/inc/tphfedit.hxx86
-rw-r--r--sc/source/ui/inc/tpprint.hxx42
-rw-r--r--sc/source/ui/inc/tpsort.hxx156
-rw-r--r--sc/source/ui/inc/tpstat.hxx43
-rw-r--r--sc/source/ui/inc/tpsubt.hxx146
-rw-r--r--sc/source/ui/inc/tptable.hxx80
-rw-r--r--sc/source/ui/inc/tpusrlst.hxx91
-rw-r--r--sc/source/ui/inc/tpview.hxx153
-rw-r--r--sc/source/ui/inc/transobj.hxx112
-rw-r--r--sc/source/ui/inc/uiitems.hxx277
-rw-r--r--sc/source/ui/inc/uiobject.hxx47
-rw-r--r--sc/source/ui/inc/undo/UndoDeleteSparkline.hxx43
-rw-r--r--sc/source/ui/inc/undo/UndoDeleteSparklineGroup.hxx45
-rw-r--r--sc/source/ui/inc/undo/UndoEditSparkline.hxx47
-rw-r--r--sc/source/ui/inc/undo/UndoEditSparklineGroup.hxx44
-rw-r--r--sc/source/ui/inc/undo/UndoGroupSparklines.hxx57
-rw-r--r--sc/source/ui/inc/undo/UndoInsertSparkline.hxx44
-rw-r--r--sc/source/ui/inc/undo/UndoThemeChange.hxx36
-rw-r--r--sc/source/ui/inc/undo/UndoUngroupSparklines.hxx55
-rw-r--r--sc/source/ui/inc/undobase.hxx183
-rw-r--r--sc/source/ui/inc/undoblk.hxx983
-rw-r--r--sc/source/ui/inc/undocell.hxx379
-rw-r--r--sc/source/ui/inc/undoconvert.hxx33
-rw-r--r--sc/source/ui/inc/undodat.hxx446
-rw-r--r--sc/source/ui/inc/undodraw.hxx52
-rw-r--r--sc/source/ui/inc/undomanager.hxx47
-rw-r--r--sc/source/ui/inc/undoolk.hxx32
-rw-r--r--sc/source/ui/inc/undosort.hxx34
-rw-r--r--sc/source/ui/inc/undostyl.hxx102
-rw-r--r--sc/source/ui/inc/undotab.hxx471
-rw-r--r--sc/source/ui/inc/undoutil.hxx50
-rw-r--r--sc/source/ui/inc/validate.hxx277
-rw-r--r--sc/source/ui/inc/viewdata.hxx737
-rw-r--r--sc/source/ui/inc/viewfunc.hxx385
-rw-r--r--sc/source/ui/inc/viewutil.hxx88
-rw-r--r--sc/source/ui/inc/warnbox.hxx39
-rw-r--r--sc/source/ui/inc/xmlsourcedlg.hxx106
-rw-r--r--sc/source/ui/miscdlgs/acredlin.cxx1792
-rw-r--r--sc/source/ui/miscdlgs/anyrefdg.cxx780
-rw-r--r--sc/source/ui/miscdlgs/autofmt.cxx531
-rw-r--r--sc/source/ui/miscdlgs/conflictsdlg.cxx643
-rw-r--r--sc/source/ui/miscdlgs/crdlg.cxx44
-rw-r--r--sc/source/ui/miscdlgs/crnrdlg.cxx800
-rw-r--r--sc/source/ui/miscdlgs/datafdlg.cxx355
-rw-r--r--sc/source/ui/miscdlgs/dataproviderdlg.cxx1122
-rw-r--r--sc/source/ui/miscdlgs/datastreamdlg.cxx175
-rw-r--r--sc/source/ui/miscdlgs/datatableview.cxx320
-rw-r--r--sc/source/ui/miscdlgs/delcldlg.cxx101
-rw-r--r--sc/source/ui/miscdlgs/delcodlg.cxx124
-rw-r--r--sc/source/ui/miscdlgs/filldlg.cxx291
-rw-r--r--sc/source/ui/miscdlgs/gototabdlg.cxx81
-rw-r--r--sc/source/ui/miscdlgs/groupdlg.cxx52
-rw-r--r--sc/source/ui/miscdlgs/highred.cxx221
-rw-r--r--sc/source/ui/miscdlgs/inscldlg.cxx109
-rw-r--r--sc/source/ui/miscdlgs/inscodlg.cxx504
-rw-r--r--sc/source/ui/miscdlgs/instbdlg.cxx355
-rw-r--r--sc/source/ui/miscdlgs/lbseldlg.cxx54
-rw-r--r--sc/source/ui/miscdlgs/linkarea.cxx344
-rw-r--r--sc/source/ui/miscdlgs/mergecellsdialog.cxx36
-rw-r--r--sc/source/ui/miscdlgs/mtrindlg.cxx103
-rw-r--r--sc/source/ui/miscdlgs/mvtabdlg.cxx330
-rw-r--r--sc/source/ui/miscdlgs/namecrea.cxx55
-rw-r--r--sc/source/ui/miscdlgs/optsolver.cxx1124
-rw-r--r--sc/source/ui/miscdlgs/protectiondlg.cxx161
-rw-r--r--sc/source/ui/miscdlgs/redcom.cxx167
-rw-r--r--sc/source/ui/miscdlgs/retypepassdlg.cxx366
-rw-r--r--sc/source/ui/miscdlgs/scuiautofmt.cxx385
-rw-r--r--sc/source/ui/miscdlgs/sharedocdlg.cxx212
-rw-r--r--sc/source/ui/miscdlgs/shtabdlg.cxx66
-rw-r--r--sc/source/ui/miscdlgs/simpref.cxx190
-rw-r--r--sc/source/ui/miscdlgs/solveroptions.cxx416
-rw-r--r--sc/source/ui/miscdlgs/solverutil.cxx175
-rw-r--r--sc/source/ui/miscdlgs/solvrdlg.cxx282
-rw-r--r--sc/source/ui/miscdlgs/strindlg.cxx42
-rw-r--r--sc/source/ui/miscdlgs/tabbgcolordlg.cxx141
-rw-r--r--sc/source/ui/miscdlgs/tabopdlg.cxx345
-rw-r--r--sc/source/ui/miscdlgs/textdlgs.cxx97
-rw-r--r--sc/source/ui/miscdlgs/warnbox.cxx52
-rw-r--r--sc/source/ui/namedlg/namedefdlg.cxx330
-rw-r--r--sc/source/ui/namedlg/namedlg.cxx512
-rw-r--r--sc/source/ui/namedlg/namemgrtable.cxx187
-rw-r--r--sc/source/ui/namedlg/namepast.cxx99
-rw-r--r--sc/source/ui/navipi/content.cxx1548
-rw-r--r--sc/source/ui/navipi/navcitem.cxx99
-rw-r--r--sc/source/ui/navipi/navipi.cxx965
-rw-r--r--sc/source/ui/navipi/scenwnd.cxx243
-rw-r--r--sc/source/ui/optdlg/calcoptionsdlg.cxx135
-rw-r--r--sc/source/ui/optdlg/calcoptionsdlg.hxx43
-rw-r--r--sc/source/ui/optdlg/opredlin.cxx119
-rw-r--r--sc/source/ui/optdlg/tpcalc.cxx310
-rw-r--r--sc/source/ui/optdlg/tpcompatibility.cxx103
-rw-r--r--sc/source/ui/optdlg/tpdefaults.cxx170
-rw-r--r--sc/source/ui/optdlg/tpformula.cxx448
-rw-r--r--sc/source/ui/optdlg/tpprint.cxx133
-rw-r--r--sc/source/ui/optdlg/tpusrlst.cxx756
-rw-r--r--sc/source/ui/optdlg/tpview.cxx840
-rw-r--r--sc/source/ui/pagedlg/areasdlg.cxx787
-rw-r--r--sc/source/ui/pagedlg/hfedtdlg.cxx263
-rw-r--r--sc/source/ui/pagedlg/scuitphfedit.cxx856
-rw-r--r--sc/source/ui/pagedlg/tphf.cxx262
-rw-r--r--sc/source/ui/pagedlg/tphfedit.cxx259
-rw-r--r--sc/source/ui/pagedlg/tptable.cxx522
-rw-r--r--sc/source/ui/sidebar/AlignmentPropertyPanel.cxx340
-rw-r--r--sc/source/ui/sidebar/AlignmentPropertyPanel.hxx112
-rw-r--r--sc/source/ui/sidebar/CellAppearancePropertyPanel.cxx506
-rw-r--r--sc/source/ui/sidebar/CellAppearancePropertyPanel.hxx147
-rw-r--r--sc/source/ui/sidebar/CellBorderStyleControl.cxx282
-rw-r--r--sc/source/ui/sidebar/CellBorderStyleControl.hxx52
-rw-r--r--sc/source/ui/sidebar/CellLineStyleControl.cxx244
-rw-r--r--sc/source/ui/sidebar/CellLineStyleControl.hxx54
-rw-r--r--sc/source/ui/sidebar/CellLineStyleValueSet.cxx187
-rw-r--r--sc/source/ui/sidebar/CellLineStyleValueSet.hxx48
-rw-r--r--sc/source/ui/sidebar/NumberFormatControl.cxx77
-rw-r--r--sc/source/ui/sidebar/NumberFormatPropertyPanel.cxx305
-rw-r--r--sc/source/ui/sidebar/NumberFormatPropertyPanel.hxx94
-rw-r--r--sc/source/ui/sidebar/ScPanelFactory.cxx143
-rw-r--r--sc/source/ui/sidebar/ScPanelFactory.hxx55
-rw-r--r--sc/source/ui/sparklines/SparklineAttributes.cxx327
-rw-r--r--sc/source/ui/sparklines/SparklineData.cxx30
-rw-r--r--sc/source/ui/sparklines/SparklineGroup.cxx35
-rw-r--r--sc/source/ui/sparklines/SparklineList.cxx103
-rw-r--r--sc/source/ui/styleui/styledlg.cxx241
-rw-r--r--sc/source/ui/styleui/template.curbin0 -> 326 bytes
-rw-r--r--sc/source/ui/theme/ThemeColorChanger.cxx359
-rw-r--r--sc/source/ui/uitest/uiobject.cxx420
-rw-r--r--sc/source/ui/undo/UndoDeleteSparkline.cxx76
-rw-r--r--sc/source/ui/undo/UndoDeleteSparklineGroup.cxx83
-rw-r--r--sc/source/ui/undo/UndoEditSparkline.cxx64
-rw-r--r--sc/source/ui/undo/UndoEditSparklineGroup.cxx66
-rw-r--r--sc/source/ui/undo/UndoGroupSparklines.cxx91
-rw-r--r--sc/source/ui/undo/UndoInsertSparkline.cxx79
-rw-r--r--sc/source/ui/undo/UndoThemeChange.cxx73
-rw-r--r--sc/source/ui/undo/UndoUngroupSparklines.cxx89
-rw-r--r--sc/source/ui/undo/areasave.cxx193
-rw-r--r--sc/source/ui/undo/refundo.cxx176
-rw-r--r--sc/source/ui/undo/target.cxx24
-rw-r--r--sc/source/ui/undo/undobase.cxx714
-rw-r--r--sc/source/ui/undo/undoblk.cxx2440
-rw-r--r--sc/source/ui/undo/undoblk2.cxx186
-rw-r--r--sc/source/ui/undo/undoblk3.cxx1743
-rw-r--r--sc/source/ui/undo/undocell.cxx1053
-rw-r--r--sc/source/ui/undo/undocell2.cxx71
-rw-r--r--sc/source/ui/undo/undoconvert.cxx52
-rw-r--r--sc/source/ui/undo/undodat.cxx1926
-rw-r--r--sc/source/ui/undo/undodraw.cxx107
-rw-r--r--sc/source/ui/undo/undoolk.cxx75
-rw-r--r--sc/source/ui/undo/undorangename.cxx134
-rw-r--r--sc/source/ui/undo/undosort.cxx88
-rw-r--r--sc/source/ui/undo/undostyl.cxx285
-rw-r--r--sc/source/ui/undo/undotab.cxx1570
-rw-r--r--sc/source/ui/undo/undoutil.cxx114
-rw-r--r--sc/source/ui/unoobj/ChartRangeSelectionListener.cxx71
-rw-r--r--sc/source/ui/unoobj/ChartTools.cxx174
-rw-r--r--sc/source/ui/unoobj/PivotTableDataProvider.cxx906
-rw-r--r--sc/source/ui/unoobj/PivotTableDataSequence.cxx278
-rw-r--r--sc/source/ui/unoobj/PivotTableDataSource.cxx49
-rw-r--r--sc/source/ui/unoobj/TablePivotChart.cxx104
-rw-r--r--sc/source/ui/unoobj/TablePivotCharts.cxx279
-rw-r--r--sc/source/ui/unoobj/addruno.cxx298
-rw-r--r--sc/source/ui/unoobj/afmtuno.cxx718
-rw-r--r--sc/source/ui/unoobj/appluno.cxx619
-rw-r--r--sc/source/ui/unoobj/celllistsource.cxx433
-rw-r--r--sc/source/ui/unoobj/celllistsource.hxx155
-rw-r--r--sc/source/ui/unoobj/cellsuno.cxx9266
-rw-r--r--sc/source/ui/unoobj/cellvaluebinding.cxx576
-rw-r--r--sc/source/ui/unoobj/cellvaluebinding.hxx146
-rw-r--r--sc/source/ui/unoobj/chart2uno.cxx3517
-rw-r--r--sc/source/ui/unoobj/chartuno.cxx739
-rw-r--r--sc/source/ui/unoobj/condformatuno.cxx1899
-rw-r--r--sc/source/ui/unoobj/confuno.cxx661
-rw-r--r--sc/source/ui/unoobj/convuno.cxx46
-rw-r--r--sc/source/ui/unoobj/cursuno.cxx453
-rw-r--r--sc/source/ui/unoobj/dapiuno.cxx3344
-rw-r--r--sc/source/ui/unoobj/datauno.cxx2384
-rw-r--r--sc/source/ui/unoobj/defltuno.cxx334
-rw-r--r--sc/source/ui/unoobj/dispuno.cxx366
-rw-r--r--sc/source/ui/unoobj/docuno.cxx4984
-rw-r--r--sc/source/ui/unoobj/drdefuno.cxx80
-rw-r--r--sc/source/ui/unoobj/editsrc.cxx272
-rw-r--r--sc/source/ui/unoobj/eventuno.cxx174
-rw-r--r--sc/source/ui/unoobj/exceldetect.cxx198
-rw-r--r--sc/source/ui/unoobj/exceldetect.hxx34
-rw-r--r--sc/source/ui/unoobj/fielduno.cxx1289
-rw-r--r--sc/source/ui/unoobj/filtuno.cxx371
-rw-r--r--sc/source/ui/unoobj/fmtuno.cxx913
-rw-r--r--sc/source/ui/unoobj/forbiuno.cxx81
-rw-r--r--sc/source/ui/unoobj/funcuno.cxx653
-rw-r--r--sc/source/ui/unoobj/linkuno.cxx1688
-rw-r--r--sc/source/ui/unoobj/listenercalls.cxx69
-rw-r--r--sc/source/ui/unoobj/miscuno.cxx285
-rw-r--r--sc/source/ui/unoobj/nameuno.cxx1147
-rw-r--r--sc/source/ui/unoobj/notesuno.cxx231
-rw-r--r--sc/source/ui/unoobj/optuno.cxx221
-rw-r--r--sc/source/ui/unoobj/pageuno.cxx60
-rw-r--r--sc/source/ui/unoobj/scdetect.cxx353
-rw-r--r--sc/source/ui/unoobj/scdetect.hxx49
-rw-r--r--sc/source/ui/unoobj/servuno.cxx625
-rw-r--r--sc/source/ui/unoobj/shapeuno.cxx1469
-rw-r--r--sc/source/ui/unoobj/srchuno.cxx195
-rw-r--r--sc/source/ui/unoobj/styleuno.cxx2069
-rw-r--r--sc/source/ui/unoobj/targuno.cxx293
-rw-r--r--sc/source/ui/unoobj/textuno.cxx869
-rw-r--r--sc/source/ui/unoobj/tokenuno.cxx520
-rw-r--r--sc/source/ui/unoobj/unodoc.cxx45
-rw-r--r--sc/source/ui/unoobj/unoreflist.cxx55
-rw-r--r--sc/source/ui/unoobj/viewuno.cxx2220
-rw-r--r--sc/source/ui/unoobj/warnpassword.cxx62
-rw-r--r--sc/source/ui/vba/excelvbahelper.cxx617
-rw-r--r--sc/source/ui/vba/excelvbahelper.hxx109
-rw-r--r--sc/source/ui/vba/vbaapplication.cxx1579
-rw-r--r--sc/source/ui/vba/vbaapplication.hxx169
-rw-r--r--sc/source/ui/vba/vbaassistant.cxx116
-rw-r--r--sc/source/ui/vba/vbaassistant.hxx57
-rw-r--r--sc/source/ui/vba/vbaaxes.cxx211
-rw-r--r--sc/source/ui/vba/vbaaxes.hxx49
-rw-r--r--sc/source/ui/vba/vbaaxis.cxx664
-rw-r--r--sc/source/ui/vba/vbaaxis.hxx94
-rw-r--r--sc/source/ui/vba/vbaaxistitle.cxx44
-rw-r--r--sc/source/ui/vba/vbaaxistitle.hxx36
-rw-r--r--sc/source/ui/vba/vbaborders.cxx592
-rw-r--r--sc/source/ui/vba/vbaborders.hxx67
-rw-r--r--sc/source/ui/vba/vbacharacters.cxx135
-rw-r--r--sc/source/ui/vba/vbacharacters.hxx61
-rw-r--r--sc/source/ui/vba/vbachart.cxx1057
-rw-r--r--sc/source/ui/vba/vbachart.hxx103
-rw-r--r--sc/source/ui/vba/vbachartobject.cxx148
-rw-r--r--sc/source/ui/vba/vbachartobject.hxx61
-rw-r--r--sc/source/ui/vba/vbachartobjects.cxx207
-rw-r--r--sc/source/ui/vba/vbachartobjects.hxx60
-rw-r--r--sc/source/ui/vba/vbacharttitle.cxx44
-rw-r--r--sc/source/ui/vba/vbacharttitle.hxx35
-rw-r--r--sc/source/ui/vba/vbacomment.cxx234
-rw-r--r--sc/source/ui/vba/vbacomment.hxx72
-rw-r--r--sc/source/ui/vba/vbacomments.cxx113
-rw-r--r--sc/source/ui/vba/vbacomments.hxx49
-rw-r--r--sc/source/ui/vba/vbacondition.cxx143
-rw-r--r--sc/source/ui/vba/vbacondition.hxx48
-rw-r--r--sc/source/ui/vba/vbadialog.cxx85
-rw-r--r--sc/source/ui/vba/vbadialog.hxx40
-rw-r--r--sc/source/ui/vba/vbadialogs.cxx51
-rw-r--r--sc/source/ui/vba/vbadialogs.hxx43
-rw-r--r--sc/source/ui/vba/vbaeventshelper.cxx898
-rw-r--r--sc/source/ui/vba/vbaeventshelper.hxx84
-rw-r--r--sc/source/ui/vba/vbafiledialog.cxx174
-rw-r--r--sc/source/ui/vba/vbafiledialog.hxx58
-rw-r--r--sc/source/ui/vba/vbafiledialogitems.cxx123
-rw-r--r--sc/source/ui/vba/vbafiledialogitems.hxx43
-rw-r--r--sc/source/ui/vba/vbafont.cxx329
-rw-r--r--sc/source/ui/vba/vbafont.hxx76
-rw-r--r--sc/source/ui/vba/vbaformat.cxx815
-rw-r--r--sc/source/ui/vba/vbaformat.hxx154
-rw-r--r--sc/source/ui/vba/vbaformatcondition.cxx158
-rw-r--r--sc/source/ui/vba/vbaformatcondition.hxx67
-rw-r--r--sc/source/ui/vba/vbaformatconditions.cxx290
-rw-r--r--sc/source/ui/vba/vbaformatconditions.hxx68
-rw-r--r--sc/source/ui/vba/vbaglobals.cxx265
-rw-r--r--sc/source/ui/vba/vbaglobals.hxx82
-rw-r--r--sc/source/ui/vba/vbahyperlink.cxx236
-rw-r--r--sc/source/ui/vba/vbahyperlink.hxx86
-rw-r--r--sc/source/ui/vba/vbahyperlinks.cxx277
-rw-r--r--sc/source/ui/vba/vbahyperlinks.hxx136
-rw-r--r--sc/source/ui/vba/vbainterior.cxx421
-rw-r--r--sc/source/ui/vba/vbainterior.hxx83
-rw-r--r--sc/source/ui/vba/vbalineshape.cxx34
-rw-r--r--sc/source/ui/vba/vbalineshape.hxx34
-rw-r--r--sc/source/ui/vba/vbamenu.cxx68
-rw-r--r--sc/source/ui/vba/vbamenu.hxx37
-rw-r--r--sc/source/ui/vba/vbamenubar.cxx49
-rw-r--r--sc/source/ui/vba/vbamenubar.hxx33
-rw-r--r--sc/source/ui/vba/vbamenubars.cxx119
-rw-r--r--sc/source/ui/vba/vbamenubars.hxx40
-rw-r--r--sc/source/ui/vba/vbamenuitem.cxx66
-rw-r--r--sc/source/ui/vba/vbamenuitem.hxx38
-rw-r--r--sc/source/ui/vba/vbamenuitems.cxx139
-rw-r--r--sc/source/ui/vba/vbamenuitems.hxx42
-rw-r--r--sc/source/ui/vba/vbamenus.cxx125
-rw-r--r--sc/source/ui/vba/vbamenus.hxx42
-rw-r--r--sc/source/ui/vba/vbaname.cxx219
-rw-r--r--sc/source/ui/vba/vbaname.hxx69
-rw-r--r--sc/source/ui/vba/vbanames.cxx261
-rw-r--r--sc/source/ui/vba/vbanames.hxx69
-rw-r--r--sc/source/ui/vba/vbaoleobject.cxx150
-rw-r--r--sc/source/ui/vba/vbaoleobject.hxx57
-rw-r--r--sc/source/ui/vba/vbaoleobjects.cxx185
-rw-r--r--sc/source/ui/vba/vbaoleobjects.hxx47
-rw-r--r--sc/source/ui/vba/vbaoutline.cxx58
-rw-r--r--sc/source/ui/vba/vbaoutline.hxx44
-rw-r--r--sc/source/ui/vba/vbaovalshape.cxx34
-rw-r--r--sc/source/ui/vba/vbaovalshape.hxx34
-rw-r--r--sc/source/ui/vba/vbapagebreak.cxx143
-rw-r--r--sc/source/ui/vba/vbapagebreak.hxx87
-rw-r--r--sc/source/ui/vba/vbapagebreaks.cxx323
-rw-r--r--sc/source/ui/vba/vbapagebreaks.hxx83
-rw-r--r--sc/source/ui/vba/vbapagesetup.cxx635
-rw-r--r--sc/source/ui/vba/vbapagesetup.hxx90
-rw-r--r--sc/source/ui/vba/vbapalette.cxx115
-rw-r--r--sc/source/ui/vba/vbapalette.hxx44
-rw-r--r--sc/source/ui/vba/vbapane.cxx197
-rw-r--r--sc/source/ui/vba/vbapane.hxx55
-rw-r--r--sc/source/ui/vba/vbapivotcache.cxx52
-rw-r--r--sc/source/ui/vba/vbapivotcache.hxx40
-rw-r--r--sc/source/ui/vba/vbapivottable.cxx55
-rw-r--r--sc/source/ui/vba/vbapivottable.hxx40
-rw-r--r--sc/source/ui/vba/vbapivottables.cxx89
-rw-r--r--sc/source/ui/vba/vbapivottables.hxx49
-rw-r--r--sc/source/ui/vba/vbarange.cxx5778
-rw-r--r--sc/source/ui/vba/vbarange.hxx335
-rw-r--r--sc/source/ui/vba/vbasheetobject.cxx550
-rw-r--r--sc/source/ui/vba/vbasheetobject.hxx209
-rw-r--r--sc/source/ui/vba/vbasheetobjects.cxx558
-rw-r--r--sc/source/ui/vba/vbasheetobjects.hxx102
-rw-r--r--sc/source/ui/vba/vbastyle.cxx183
-rw-r--r--sc/source/ui/vba/vbastyle.hxx62
-rw-r--r--sc/source/ui/vba/vbastyles.cxx200
-rw-r--r--sc/source/ui/vba/vbastyles.hxx50
-rw-r--r--sc/source/ui/vba/vbatextboxshape.cxx60
-rw-r--r--sc/source/ui/vba/vbatextboxshape.hxx39
-rw-r--r--sc/source/ui/vba/vbatextframe.cxx67
-rw-r--r--sc/source/ui/vba/vbatextframe.hxx40
-rw-r--r--sc/source/ui/vba/vbatitle.hxx144
-rw-r--r--sc/source/ui/vba/vbavalidation.cxx382
-rw-r--r--sc/source/ui/vba/vbavalidation.hxx65
-rw-r--r--sc/source/ui/vba/vbawindow.cxx869
-rw-r--r--sc/source/ui/vba/vbawindow.hxx126
-rw-r--r--sc/source/ui/vba/vbawindows.cxx272
-rw-r--r--sc/source/ui/vba/vbawindows.hxx48
-rw-r--r--sc/source/ui/vba/vbaworkbook.cxx436
-rw-r--r--sc/source/ui/vba/vbaworkbook.hxx76
-rw-r--r--sc/source/ui/vba/vbaworkbooks.cxx291
-rw-r--r--sc/source/ui/vba/vbaworkbooks.hxx56
-rw-r--r--sc/source/ui/vba/vbaworksheet.cxx1074
-rw-r--r--sc/source/ui/vba/vbaworksheet.hxx170
-rw-r--r--sc/source/ui/vba/vbaworksheets.cxx536
-rw-r--r--sc/source/ui/vba/vbaworksheets.hxx68
-rw-r--r--sc/source/ui/vba/vbawsfunction.cxx298
-rw-r--r--sc/source/ui/vba/vbawsfunction.hxx45
-rw-r--r--sc/source/ui/view/SparklineShell.cxx54
-rw-r--r--sc/source/ui/view/auditsh.cxx123
-rw-r--r--sc/source/ui/view/cellmergeoption.cxx49
-rw-r--r--sc/source/ui/view/cellsh.cxx1325
-rw-r--r--sc/source/ui/view/cellsh1.cxx3600
-rw-r--r--sc/source/ui/view/cellsh2.cxx1256
-rw-r--r--sc/source/ui/view/cellsh3.cxx1096
-rw-r--r--sc/source/ui/view/cellsh4.cxx522
-rw-r--r--sc/source/ui/view/cliputil.cxx169
-rw-r--r--sc/source/ui/view/colrowba.cxx383
-rw-r--r--sc/source/ui/view/dbfunc.cxx466
-rw-r--r--sc/source/ui/view/dbfunc2.cxx41
-rw-r--r--sc/source/ui/view/dbfunc3.cxx2312
-rw-r--r--sc/source/ui/view/dbfunc4.cxx73
-rw-r--r--sc/source/ui/view/drawutil.cxx89
-rw-r--r--sc/source/ui/view/drawvie3.cxx259
-rw-r--r--sc/source/ui/view/drawvie4.cxx572
-rw-r--r--sc/source/ui/view/drawview.cxx1251
-rw-r--r--sc/source/ui/view/editsh.cxx1427
-rw-r--r--sc/source/ui/view/formatsh.cxx2108
-rw-r--r--sc/source/ui/view/gridmerg.cxx225
-rw-r--r--sc/source/ui/view/gridwin.cxx7218
-rw-r--r--sc/source/ui/view/gridwin2.cxx1312
-rw-r--r--sc/source/ui/view/gridwin3.cxx399
-rw-r--r--sc/source/ui/view/gridwin4.cxx2695
-rw-r--r--sc/source/ui/view/gridwin5.cxx420
-rw-r--r--sc/source/ui/view/gridwin_dbgutil.cxx171
-rw-r--r--sc/source/ui/view/hdrcont.cxx1124
-rw-r--r--sc/source/ui/view/hintwin.cxx182
-rw-r--r--sc/source/ui/view/imapwrap.cxx48
-rw-r--r--sc/source/ui/view/imapwrap.hxx40
-rw-r--r--sc/source/ui/view/invmerge.cxx162
-rw-r--r--sc/source/ui/view/notemark.cxx203
-rw-r--r--sc/source/ui/view/olinewin.cxx1040
-rw-r--r--sc/source/ui/view/output.cxx2773
-rw-r--r--sc/source/ui/view/output2.cxx5248
-rw-r--r--sc/source/ui/view/output3.cxx267
-rw-r--r--sc/source/ui/view/overlayobject.cxx89
-rw-r--r--sc/source/ui/view/pfuncache.cxx191
-rw-r--r--sc/source/ui/view/pgbrksh.cxx53
-rw-r--r--sc/source/ui/view/pivotsh.cxx167
-rw-r--r--sc/source/ui/view/preview.cxx1575
-rw-r--r--sc/source/ui/view/prevloc.cxx718
-rw-r--r--sc/source/ui/view/prevwsh.cxx1181
-rw-r--r--sc/source/ui/view/prevwsh2.cxx68
-rw-r--r--sc/source/ui/view/printfun.cxx3204
-rw-r--r--sc/source/ui/view/reffact.cxx299
-rw-r--r--sc/source/ui/view/scextopt.cxx218
-rw-r--r--sc/source/ui/view/select.cxx986
-rw-r--r--sc/source/ui/view/selectionstate.cxx54
-rw-r--r--sc/source/ui/view/spellcheckcontext.cxx387
-rw-r--r--sc/source/ui/view/spelldialog.cxx281
-rw-r--r--sc/source/ui/view/spelleng.cxx448
-rw-r--r--sc/source/ui/view/tabcont.cxx666
-rw-r--r--sc/source/ui/view/tabsplit.cxx127
-rw-r--r--sc/source/ui/view/tabview.cxx3162
-rw-r--r--sc/source/ui/view/tabview2.cxx1709
-rw-r--r--sc/source/ui/view/tabview3.cxx3171
-rw-r--r--sc/source/ui/view/tabview4.cxx538
-rw-r--r--sc/source/ui/view/tabview5.cxx704
-rw-r--r--sc/source/ui/view/tabvwsh.cxx121
-rw-r--r--sc/source/ui/view/tabvwsh2.cxx472
-rw-r--r--sc/source/ui/view/tabvwsh3.cxx1398
-rw-r--r--sc/source/ui/view/tabvwsh4.cxx1946
-rw-r--r--sc/source/ui/view/tabvwsh5.cxx390
-rw-r--r--sc/source/ui/view/tabvwsh8.cxx76
-rw-r--r--sc/source/ui/view/tabvwsh9.cxx203
-rw-r--r--sc/source/ui/view/tabvwsha.cxx1817
-rw-r--r--sc/source/ui/view/tabvwshb.cxx919
-rw-r--r--sc/source/ui/view/tabvwshc.cxx775
-rw-r--r--sc/source/ui/view/tabvwshd.cxx67
-rw-r--r--sc/source/ui/view/tabvwshe.cxx322
-rw-r--r--sc/source/ui/view/tabvwshf.cxx1094
-rw-r--r--sc/source/ui/view/tabvwshg.cxx120
-rw-r--r--sc/source/ui/view/tabvwshh.cxx261
-rw-r--r--sc/source/ui/view/viewdata.cxx4365
-rw-r--r--sc/source/ui/view/viewfun2.cxx3487
-rw-r--r--sc/source/ui/view/viewfun3.cxx2028
-rw-r--r--sc/source/ui/view/viewfun4.cxx791
-rw-r--r--sc/source/ui/view/viewfun5.cxx818
-rw-r--r--sc/source/ui/view/viewfun6.cxx559
-rw-r--r--sc/source/ui/view/viewfun7.cxx462
-rw-r--r--sc/source/ui/view/viewfunc.cxx3183
-rw-r--r--sc/source/ui/view/viewutil.cxx426
-rw-r--r--sc/source/ui/view/waitoff.cxx51
-rw-r--r--sc/source/ui/xmlsource/xmlsourcedlg.cxx625
1504 files changed, 745209 insertions, 0 deletions
diff --git a/sc/source/core/data/SolverSettings.cxx b/sc/source/core/data/SolverSettings.cxx
new file mode 100644
index 0000000000..60eb747f55
--- /dev/null
+++ b/sc/source/core/data/SolverSettings.cxx
@@ -0,0 +1,508 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <global.hxx>
+#include <table.hxx>
+#include <docsh.hxx>
+#include <solverutil.hxx>
+#include <unotools/charclass.hxx>
+#include <SolverSettings.hxx>
+
+namespace sc
+{
+SolverSettings::SolverSettings(ScTable& rTable)
+ : m_rTable(rTable)
+ , m_rDoc(m_rTable.GetDoc())
+ , m_pDocShell(m_rDoc.GetDocumentShell())
+{
+ // Get the named range manager for this tab
+ std::map<OUString, ScRangeName*> rRangeMap;
+ m_rDoc.GetRangeNameMap(rRangeMap);
+ m_pRangeName = rRangeMap.find(m_rTable.GetName())->second;
+
+ Initialize();
+}
+
+void SolverSettings::Initialize()
+{
+ // Assign default values for the solver parameters
+ ResetToDefaults();
+
+ // Read the parameter values in the sheet
+ ReadParamValue(SP_OBJ_CELL, m_sObjCell);
+ ReadParamValue(SP_OBJ_VAL, m_sObjVal);
+ ReadParamValue(SP_VAR_CELLS, m_sVariableCells);
+
+ // Read the objective type
+ OUString sObjType;
+ if (ReadParamValue(SP_OBJ_TYPE, sObjType))
+ {
+ switch (sObjType.toInt32())
+ {
+ case 1:
+ m_eObjType = ObjectiveType::OT_MAXIMIZE;
+ break;
+ case 2:
+ m_eObjType = ObjectiveType::OT_MINIMIZE;
+ break;
+ case 3:
+ m_eObjType = ObjectiveType::OT_VALUE;
+ break;
+ default:
+ m_eObjType = ObjectiveType::OT_MAXIMIZE;
+ }
+ }
+
+ // Read all constraints in the tab
+ ReadConstraints();
+
+ // Read the solver engine being used
+ ReadEngine();
+
+ // Read engine options
+ ReadParamValue(SP_INTEGER, m_sInteger);
+ ReadParamValue(SP_NON_NEGATIVE, m_sNonNegative);
+ ReadParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
+ ReadParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
+ ReadParamValue(SP_TIMEOUT, m_sTimeout);
+ ReadParamValue(SP_ALGORITHM, m_sAlgorithm);
+}
+
+// Returns the current value of the parameter in the object as a string
+OUString SolverSettings::GetParameter(SolverParameter eParam)
+{
+ switch (eParam)
+ {
+ case SP_OBJ_CELL:
+ return m_sObjCell;
+ break;
+ case SP_OBJ_TYPE:
+ return OUString::number(m_eObjType);
+ break;
+ case SP_OBJ_VAL:
+ return m_sObjVal;
+ break;
+ case SP_VAR_CELLS:
+ return m_sVariableCells;
+ break;
+ case SP_CONSTR_COUNT:
+ return OUString::number(m_aConstraints.size());
+ break;
+ case SP_LO_ENGINE:
+ return m_sLOEngineName;
+ break;
+ case SP_MS_ENGINE:
+ return m_sMSEngineId;
+ break;
+ case SP_INTEGER:
+ return m_sInteger;
+ break;
+ case SP_NON_NEGATIVE:
+ return m_sNonNegative;
+ break;
+ case SP_EPSILON_LEVEL:
+ return m_sEpsilonLevel;
+ break;
+ case SP_LIMIT_BBDEPTH:
+ return m_sLimitBBDepth;
+ break;
+ case SP_TIMEOUT:
+ return m_sTimeout;
+ break;
+ case SP_ALGORITHM:
+ return m_sAlgorithm;
+ break;
+ default:
+ return "";
+ }
+}
+
+// Sets the value of a single solver parameter in the object
+void SolverSettings::SetParameter(SolverParameter eParam, OUString sValue)
+{
+ switch (eParam)
+ {
+ case SP_OBJ_CELL:
+ m_sObjCell = sValue;
+ break;
+ case SP_OBJ_TYPE:
+ {
+ sal_Int32 nObjType = sValue.toInt32();
+ switch (nObjType)
+ {
+ case OT_MAXIMIZE:
+ m_eObjType = ObjectiveType::OT_MAXIMIZE;
+ break;
+ case OT_MINIMIZE:
+ m_eObjType = ObjectiveType::OT_MINIMIZE;
+ break;
+ case OT_VALUE:
+ m_eObjType = ObjectiveType::OT_VALUE;
+ break;
+ default:
+ m_eObjType = ObjectiveType::OT_MAXIMIZE;
+ break;
+ }
+ break;
+ }
+ case SP_OBJ_VAL:
+ m_sObjVal = sValue;
+ break;
+ case SP_VAR_CELLS:
+ m_sVariableCells = sValue;
+ break;
+ case SP_LO_ENGINE:
+ m_sLOEngineName = sValue;
+ break;
+ case SP_INTEGER:
+ {
+ if (sValue == "0" || sValue == "1")
+ m_sInteger = sValue;
+ }
+ break;
+ case SP_NON_NEGATIVE:
+ {
+ if (sValue == "1" || sValue == "2")
+ m_sNonNegative = sValue;
+ }
+ break;
+ case SP_EPSILON_LEVEL:
+ m_sEpsilonLevel = sValue;
+ break;
+ case SP_LIMIT_BBDEPTH:
+ m_sLimitBBDepth = sValue;
+ break;
+ case SP_TIMEOUT:
+ m_sTimeout = sValue;
+ break;
+ case SP_ALGORITHM:
+ {
+ if (sValue == "1" || sValue == "2" || sValue == "3")
+ m_sAlgorithm = sValue;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void SolverSettings::SetObjectiveType(ObjectiveType eType) { m_eObjType = eType; }
+
+// Loads all constraints in the tab
+void SolverSettings::ReadConstraints()
+{
+ // Condition indices start at 1 for MS compatibility
+ // The number of "lhs", "rel" and "rhs" entries will always be the same
+ tools::Long nConstraint = 1;
+ m_aConstraints.clear();
+ OUString sValue;
+
+ while (ReadConstraintPart(CP_LEFT_HAND_SIDE, nConstraint, sValue))
+ {
+ // Left hand side
+ ModelConstraint aNewCondition;
+ aNewCondition.aLeftStr = sValue;
+
+ // Right hand side
+ if (ReadConstraintPart(CP_RIGHT_HAND_SIDE, nConstraint, sValue))
+ aNewCondition.aRightStr = sValue;
+
+ // Relation (operator)
+ if (ReadConstraintPart(CP_OPERATOR, nConstraint, sValue))
+ aNewCondition.nOperator = static_cast<sc::ConstraintOperator>(sValue.toInt32());
+
+ m_aConstraints.push_back(aNewCondition);
+ nConstraint++;
+ }
+}
+
+// Writes all constraints to the file
+void SolverSettings::WriteConstraints()
+{
+ // Condition indices start at 1 for MS compatibility
+ tools::Long nConstraint = 1;
+
+ for (auto& aConstraint : m_aConstraints)
+ {
+ // Left hand side
+ WriteConstraintPart(CP_LEFT_HAND_SIDE, nConstraint, aConstraint.aLeftStr);
+ // Relation (operator)
+ WriteConstraintPart(CP_OPERATOR, nConstraint, OUString::number(aConstraint.nOperator));
+ // Right hand side
+ WriteConstraintPart(CP_RIGHT_HAND_SIDE, nConstraint, aConstraint.aRightStr);
+ nConstraint++;
+ }
+}
+
+// Write a single constraint part to the file
+void SolverSettings::WriteConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString sValue)
+{
+ // Empty named ranges cannot be written to the file (this corrupts MS files)
+ if (sValue.isEmpty())
+ return;
+
+ OUString sRange = m_aConstraintParts[ePart] + OUString::number(nIndex);
+ ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sValue);
+ m_pRangeName->insert(pNewEntry);
+}
+
+// Reads a single constraint part from its associated named range; returns false if the named
+// range does not exist in the file
+bool SolverSettings::ReadConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString& rValue)
+{
+ OUString sRange = m_aConstraintParts[ePart] + OUString::number(nIndex);
+ ScRangeData* pRangeData
+ = m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
+ if (pRangeData)
+ {
+ rValue = pRangeData->GetSymbol();
+ return true;
+ }
+ return false;
+}
+
+/* Reads the engine name parameter as informed in the file in the format used in LO.
+ * If only a MS engine is informed, then it is converted to a LO-equivalent engine
+ */
+void SolverSettings::ReadEngine()
+{
+ if (!ReadParamValue(SP_LO_ENGINE, m_sLOEngineName, true))
+ {
+ // If no engine is defined, use CoinMP solver as default
+ m_sLOEngineName = "com.sun.star.comp.Calc.CoinMPSolver";
+ }
+
+ if (SolverNamesToExcelEngines.count(m_sLOEngineName))
+ {
+ // Find equivalent MS engine code
+ m_sMSEngineId = SolverNamesToExcelEngines.find(m_sLOEngineName)->second;
+ }
+}
+
+// Write solver LO and MS-equivalent engine names
+void SolverSettings::WriteEngine()
+{
+ WriteParamValue(SP_LO_ENGINE, m_sLOEngineName, true);
+ // Find equivalent MS engine code
+ if (SolverNamesToExcelEngines.count(m_sLOEngineName))
+ {
+ m_sMSEngineId = SolverNamesToExcelEngines.find(m_sLOEngineName)->second;
+ WriteParamValue(SP_MS_ENGINE, m_sMSEngineId);
+ }
+}
+
+// Assigns a new constraints vector
+void SolverSettings::SetConstraints(std::vector<ModelConstraint> aConstraints)
+{
+ m_aConstraints = std::move(aConstraints);
+}
+
+// Saves all solver settings into the file
+void SolverSettings::SaveSolverSettings()
+{
+ // Before saving, remove all existing named ranges related to the solver
+ DeleteAllNamedRanges();
+
+ WriteParamValue(SP_OBJ_CELL, m_sObjCell);
+ WriteParamValue(SP_OBJ_TYPE, OUString::number(m_eObjType));
+ WriteParamValue(SP_OBJ_VAL, m_sObjVal);
+ WriteParamValue(SP_VAR_CELLS, m_sVariableCells);
+
+ WriteConstraints();
+ WriteEngine();
+
+ sal_Int32 nConstrCount = m_aConstraints.size();
+ WriteParamValue(SP_CONSTR_COUNT, OUString::number(nConstrCount));
+
+ WriteParamValue(SP_INTEGER, m_sInteger);
+ WriteParamValue(SP_NON_NEGATIVE, m_sNonNegative);
+ WriteParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
+ WriteParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
+ WriteParamValue(SP_TIMEOUT, m_sTimeout);
+ WriteParamValue(SP_ALGORITHM, m_sAlgorithm);
+
+ if (m_pDocShell)
+ m_pDocShell->SetDocumentModified();
+}
+
+/* Reads the current value of the parameter in the named range into rValue
+ * If the value does not exist, the rValue is left unchanged
+ * This is private because it is only used during initialization
+ * Returns true if the value exits; returns false otherwise
+ */
+bool SolverSettings::ReadParamValue(SolverParameter eParam, OUString& rValue, bool bRemoveQuotes)
+{
+ const auto iter = m_mNamedRanges.find(eParam);
+ assert(iter != m_mNamedRanges.end());
+ OUString sRange = iter->second;
+ ScRangeData* pRangeData
+ = m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
+ if (pRangeData)
+ {
+ rValue = pRangeData->GetSymbol();
+ if (bRemoveQuotes)
+ ScGlobal::EraseQuotes(rValue, '"');
+ return true;
+ }
+ return false;
+}
+
+/* Writes a parameter value to the file as a named range.
+ * Argument bQuoted indicates whether the value should be enclosed with quotes or not (used
+ * for string expressions that must be enclosed with quotes)
+ */
+void SolverSettings::WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted)
+{
+ // Empty parameters cannot be written to the file (this corrupts MS files)
+ // There's no problem if the parameter is missing both for LO and MS
+ if (sValue.isEmpty())
+ return;
+
+ if (bQuoted)
+ ScGlobal::AddQuotes(sValue, '"');
+
+ const auto iter = m_mNamedRanges.find(eParam);
+ assert(iter != m_mNamedRanges.end());
+ OUString sRange = iter->second;
+ ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sValue);
+ m_pRangeName->insert(pNewEntry);
+}
+
+void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions)
+{
+ sal_Int32 nOptionsSize = aOptions.getLength();
+ auto pParamValues = aOptions.getArray();
+
+ for (auto i = 0; i < nOptionsSize; i++)
+ {
+ css::beans::PropertyValue aProp = aOptions[i];
+ OUString sLOParamName = aProp.Name;
+ // Only try to get the parameter value if it is an expected parameter name
+ if (SolverParamNames.count(sLOParamName))
+ {
+ TParamInfo aParamInfo;
+ aParamInfo = SolverParamNames.find(sLOParamName)->second;
+ SolverParameter eParamId = std::get<SolverParameter>(aParamInfo[0]);
+ OUString sParamType = std::get<OUString>(aParamInfo[2]);
+ OUString sParamValue = GetParameter(eParamId);
+ if (sParamType == "int")
+ {
+ css::uno::Any nValue(sParamValue.toInt32());
+ pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, nValue,
+ css::beans::PropertyState_DIRECT_VALUE);
+ }
+ if (sParamType == "bool")
+ {
+ // The parameter NonNegative is a special case for MS compatibility
+ // It uses "1" for "true" and "2" for "false"
+ bool bTmpValue;
+ if (sLOParamName == "NonNegative")
+ bTmpValue = sParamValue == "1" ? true : false;
+ else
+ bTmpValue = sParamValue.toBoolean();
+
+ css::uno::Any bValue(bTmpValue);
+ pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, bValue,
+ css::beans::PropertyState_DIRECT_VALUE);
+ }
+ }
+ }
+}
+
+// Updates the object members related to solver engine options using aOptions info
+void SolverSettings::SetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions)
+{
+ sal_Int32 nOptionsSize = aOptions.getLength();
+
+ for (auto i = 0; i < nOptionsSize; i++)
+ {
+ css::beans::PropertyValue aProp = aOptions[i];
+ OUString sLOParamName = aProp.Name;
+ // Only try to set the parameter value if it is an expected parameter name
+ if (SolverParamNames.count(sLOParamName))
+ {
+ TParamInfo aParamInfo;
+ aParamInfo = SolverParamNames.find(sLOParamName)->second;
+ SolverParameter eParamId = std::get<SolverParameter>(aParamInfo[0]);
+ OUString sParamType = std::get<OUString>(aParamInfo[2]);
+ if (sParamType == "int")
+ {
+ sal_Int32 nValue = 0;
+ aProp.Value >>= nValue;
+ SetParameter(eParamId, OUString::number(nValue));
+ }
+ if (sParamType == "bool")
+ {
+ bool bValue = false;
+ aProp.Value >>= bValue;
+ if (sLOParamName == "NonNegative")
+ {
+ // The parameter NonNegative is a special case for MS compatibility
+ // It uses "1" for "true" and "2" for "false"
+ if (bValue)
+ SetParameter(eParamId, OUString::number(1));
+ else
+ SetParameter(eParamId, OUString::number(2));
+ }
+ else
+ {
+ SetParameter(eParamId, OUString::number(sal_Int32(bValue)));
+ }
+ }
+ }
+ }
+}
+
+// Deletes all named ranges in the current tab that are related to the solver (i.e. start with "solver_")
+void SolverSettings::DeleteAllNamedRanges()
+{
+ std::vector<ScRangeData*> aItemsToErase;
+
+ // Indices in m_pRangeName start at 1
+ for (size_t i = 1; i <= m_pRangeName->size(); ++i)
+ {
+ ScRangeData* pData = m_pRangeName->findByIndex(i);
+ if (pData && pData->GetName().startsWith("solver_"))
+ aItemsToErase.push_back(pData);
+ }
+
+ for (auto pItem : aItemsToErase)
+ m_pRangeName->erase(*pItem);
+}
+
+/* Sets all solver parameters to their default values and clear all constraints.
+ * This method only resets the object properties, but does not save changes to the
+ * document. To save changes, call SaveSolverSettings().
+ */
+void SolverSettings::ResetToDefaults()
+{
+ m_sObjCell = "";
+ m_eObjType = ObjectiveType::OT_MAXIMIZE;
+ m_sObjVal = "";
+ m_sVariableCells = "";
+ m_sMSEngineId = "1";
+
+ // The default solver engine is the first implementation available
+ css::uno::Sequence<OUString> aEngineNames;
+ css::uno::Sequence<OUString> aDescriptions;
+ ScSolverUtil::GetImplementations(aEngineNames, aDescriptions);
+ m_sLOEngineName = aEngineNames[0];
+
+ // Default engine options
+ m_aEngineOptions = ScSolverUtil::GetDefaults(m_sLOEngineName);
+
+ // Default solver engine options
+ SetEngineOptions(m_aEngineOptions);
+
+ // Clear all constraints
+ m_aConstraints.clear();
+}
+
+} // namespace sc
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx
new file mode 100644
index 0000000000..4c31fb86e3
--- /dev/null
+++ b/sc/source/core/data/attarray.cxx
@@ -0,0 +1,2716 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <attarray.hxx>
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/justifyitem.hxx>
+#include <osl/diagnose.h>
+#include <poolcach.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <global.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <patattr.hxx>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+#include <markarr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <segmenttree.hxx>
+#include <editdataarray.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+#include <mtvelements.hxx>
+#include <memory>
+
+using ::editeng::SvxBorderLine;
+
+ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, ScAttrArray* pDefaultColAttrArray ) :
+ nCol( nNewCol ),
+ nTab( nNewTab ),
+ rDocument( rDoc )
+{
+ if ( nCol == -1 || !pDefaultColAttrArray || pDefaultColAttrArray->mvData.empty() )
+ return;
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd( nCol, 0, nTab );
+ mvData.resize( pDefaultColAttrArray->mvData.size() );
+ for ( size_t nIdx = 0; nIdx < pDefaultColAttrArray->mvData.size(); ++nIdx )
+ {
+ mvData[nIdx].nEndRow = pDefaultColAttrArray->mvData[nIdx].nEndRow;
+ ScPatternAttr aNewPattern( *(pDefaultColAttrArray->mvData[nIdx].pPattern) );
+ mvData[nIdx].pPattern = &rDocument.GetPool()->DirectPutItemInPool( aNewPattern );
+ bool bNumFormatChanged = false;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ mvData[nIdx].pPattern->GetItemSet(), rDocument.GetDefPattern()->GetItemSet() ) )
+ {
+ aAdrStart.SetRow( nIdx ? mvData[nIdx-1].nEndRow+1 : 0 );
+ aAdrEnd.SetRow( mvData[nIdx].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+}
+
+ScAttrArray::~ScAttrArray()
+{
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ for (auto const & rEntry : mvData)
+ pDocPool->DirectRemoveItemFromPool(*rEntry.pPattern);
+}
+
+#if DEBUG_SC_TESTATTRARRAY
+void ScAttrArray::TestData() const
+{
+
+ sal_uInt16 nErr = 0;
+ SCSIZE nPos;
+ for (nPos=0; nPos<nCount; nPos++)
+ {
+ if (nPos > 0)
+ if (mvData[nPos].pPattern == mvData[nPos-1].pPattern || mvData[nPos].nRow <= mvData[nPos-1].nRow)
+ ++nErr;
+ if (mvData[nPos].pPattern->Which() != ATTR_PATTERN)
+ ++nErr;
+ }
+ if ( nPos && mvData[nPos-1].nRow != rDocument.MaxRow() )
+ ++nErr;
+
+ SAL_WARN_IF( nErr, "sc", nErr << " errors in attribute array, column " << nCol );
+}
+#endif
+
+void ScAttrArray::SetDefaultIfNotInit( SCSIZE nNeeded )
+{
+ if ( !mvData.empty() )
+ return;
+
+ SCSIZE nNewLimit = std::max<SCSIZE>( SC_ATTRARRAY_DELTA, nNeeded );
+ mvData.reserve( nNewLimit );
+ mvData.emplace_back();
+ mvData[0].nEndRow = rDocument.MaxRow();
+ mvData[0].pPattern = rDocument.GetDefPattern(); // no put
+}
+
+void ScAttrArray::Reset( const ScPatternAttr* pPattern )
+{
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ for (SCSIZE i=0; i<mvData.size(); i++)
+ {
+ // ensure that attributing changes text width of cell
+ const ScPatternAttr* pOldPattern = mvData[i].pPattern;
+ if ( nCol != -1 )
+ {
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ pPattern->GetItemSet(), pOldPattern->GetItemSet() ) )
+ {
+ aAdrStart.SetRow( i ? mvData[i-1].nEndRow+1 : 0 );
+ aAdrEnd .SetRow( mvData[i].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+ pDocPool->DirectRemoveItemFromPool(*pOldPattern);
+ }
+ mvData.resize(0);
+
+ rDocument.SetStreamValid(nTab, false);
+
+ mvData.resize(1);
+ const ScPatternAttr* pNewPattern = &pDocPool->DirectPutItemInPool(*pPattern);
+ mvData[0].nEndRow = rDocument.MaxRow();
+ mvData[0].pPattern = pNewPattern;
+}
+
+bool ScAttrArray::Concat(SCSIZE nPos)
+{
+ bool bRet = false;
+ if (nPos < mvData.size())
+ {
+ if (nPos > 0)
+ {
+ if (SfxPoolItem::areSame(mvData[nPos - 1].pPattern, mvData[nPos].pPattern))
+ {
+ mvData[nPos - 1].nEndRow = mvData[nPos].nEndRow;
+ rDocument.GetPool()->DirectRemoveItemFromPool(*mvData[nPos].pPattern);
+ mvData.erase(mvData.begin() + nPos);
+ nPos--;
+ bRet = true;
+ }
+ }
+ if (nPos + 1 < mvData.size())
+ {
+ if (SfxPoolItem::areSame(mvData[nPos + 1].pPattern, mvData[nPos].pPattern))
+ {
+ mvData[nPos].nEndRow = mvData[nPos + 1].nEndRow;
+ rDocument.GetPool()->DirectRemoveItemFromPool(*mvData[nPos].pPattern);
+ mvData.erase(mvData.begin() + nPos + 1);
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+/*
+ * nCount is the number of runs of different attribute combinations;
+ * no attribute in a column => nCount==1, one attribute somewhere => nCount == 3
+ * (ie. one run with no attribute + one attribute + another run with no attribute)
+ * so a range of identical attributes is only one entry in ScAttrArray.
+ *
+ * Iterative implementation of Binary Search
+ * The same implementation was used inside ScMarkArray::Search().
+ *
+ * @param oIndexHint, hint for the start of the search, useful when searching twice for successive values
+ */
+
+bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex, std::optional<SCROW> oIndexHint ) const
+{
+/* auto it = std::lower_bound(mvData.begin(), mvData.end(), nRow,
+ [] (const ScAttrEntry &r1, SCROW nRow)
+ { return r1.nEndRow < nRow; } );
+ if (it != mvData.end())
+ nIndex = it - mvData.begin();
+ return it != mvData.end(); */
+
+ if (mvData.size() == 1)
+ {
+ nIndex = 0;
+ return true;
+ }
+
+ tools::Long nHi = static_cast<tools::Long>(mvData.size()) - 1;
+ tools::Long i = 0;
+ assert((!oIndexHint || *oIndexHint <= nHi) && "bad index hint");
+ tools::Long nLo = oIndexHint ? *oIndexHint : 0;
+
+ while ( nLo <= nHi )
+ {
+ i = (nLo + nHi) / 2;
+
+ if (mvData[i].nEndRow < nRow)
+ {
+ // If [nRow] greater, ignore left half
+ nLo = i + 1;
+ }
+ else if ((i > 0) && (mvData[i - 1].nEndRow >= nRow))
+ {
+ // If [nRow] is smaller, ignore right half
+ nHi = i - 1;
+ }
+ else
+ {
+ // found
+ nIndex=static_cast<SCSIZE>(i);
+ return true;
+ }
+ }
+
+ nIndex=0;
+ return false;
+}
+
+const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const
+{
+ if ( mvData.empty() )
+ {
+ if ( !rDocument.ValidRow(nRow) )
+ return nullptr;
+ return rDocument.GetDefPattern();
+ }
+ SCSIZE i;
+ if (Search( nRow, i ))
+ return mvData[i].pPattern;
+ else
+ return nullptr;
+}
+
+const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow,
+ SCROW& rEndRow, SCROW nRow ) const
+{
+ if ( mvData.empty() )
+ {
+ if ( !rDocument.ValidRow( nRow ) )
+ return nullptr;
+ rStartRow = 0;
+ rEndRow = rDocument.MaxRow();
+ return rDocument.GetDefPattern();
+ }
+ SCSIZE nIndex;
+ if ( Search( nRow, nIndex ) )
+ {
+ if ( nIndex > 0 )
+ rStartRow = mvData[nIndex-1].nEndRow + 1;
+ else
+ rStartRow = 0;
+ rEndRow = mvData[nIndex].nEndRow;
+ return mvData[nIndex].pPattern;
+ }
+ return nullptr;
+}
+
+void ScAttrArray::AddCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
+{
+ if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
+ return;
+
+ if(nEndRow < nStartRow)
+ return;
+
+ SCROW nTempStartRow = nStartRow;
+ SCROW nTempEndRow = nEndRow;
+
+ do
+ {
+ const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
+
+ // changed to create pNewPattern only if needed, else use already
+ // existing pPattern. This shows by example how to avoid that special
+ // handling of ATTR_PATTERN/ScPatternAttr in SC and massive
+ // incarnations/destructions of that Item (which contains an ItemSet)
+ std::unique_ptr<ScPatternAttr> pNewPattern;
+ if(pPattern)
+ {
+ SCROW nPatternStartRow;
+ SCROW nPatternEndRow;
+ GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
+
+ nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
+ if (const ScCondFormatItem* pItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ))
+ {
+ ScCondFormatIndexes const & rCondFormatData = pItem->GetCondFormatData();
+ if (rCondFormatData.find(nIndex) == rCondFormatData.end())
+ {
+ ScCondFormatIndexes aNewCondFormatData;
+ aNewCondFormatData.reserve(rCondFormatData.size()+1);
+ aNewCondFormatData = rCondFormatData;
+ aNewCondFormatData.insert(nIndex);
+ ScCondFormatItem aItem( std::move(aNewCondFormatData) );
+ pNewPattern.reset( new ScPatternAttr(*pPattern) );
+ pNewPattern->GetItemSet().Put( aItem );
+ }
+ }
+ else
+ {
+ ScCondFormatItem aItem(nIndex);
+ pNewPattern.reset( new ScPatternAttr(*pPattern) );
+ pNewPattern->GetItemSet().Put( aItem );
+ }
+ }
+ else
+ {
+ pNewPattern.reset( new ScPatternAttr( rDocument.GetPool() ) );
+ ScCondFormatItem aItem(nIndex);
+ pNewPattern->GetItemSet().Put( aItem );
+ nTempEndRow = nEndRow;
+ }
+
+ if (pNewPattern)
+ SetPatternArea( nTempStartRow, nTempEndRow, std::move(pNewPattern), true );
+ else
+ SetPatternArea( nTempStartRow, nTempEndRow, pPattern, true );
+
+ nTempStartRow = nTempEndRow + 1;
+ }
+ while(nTempEndRow < nEndRow);
+
+}
+
+void ScAttrArray::RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
+{
+ if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
+ return;
+
+ if(nEndRow < nStartRow)
+ return;
+
+ SCROW nTempStartRow = nStartRow;
+ SCROW nTempEndRow = nEndRow;
+
+ do
+ {
+ const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
+
+ if(pPattern)
+ {
+ SCROW nPatternStartRow;
+ SCROW nPatternEndRow;
+ GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
+
+ nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
+ if (const ScCondFormatItem* pItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ))
+ {
+ auto pPatternAttr = std::make_unique<ScPatternAttr>( *pPattern );
+ if (nIndex == 0)
+ {
+ ScCondFormatItem aItem;
+ pPatternAttr->GetItemSet().Put( aItem );
+ SetPatternArea( nTempStartRow, nTempEndRow, std::move(pPatternAttr), true );
+ }
+ else
+ {
+ ScCondFormatIndexes const & rCondFormatData = pItem->GetCondFormatData();
+ auto itr = rCondFormatData.find(nIndex);
+ if(itr != rCondFormatData.end())
+ {
+ ScCondFormatIndexes aNewCondFormatData(rCondFormatData);
+ aNewCondFormatData.erase_at(std::distance(rCondFormatData.begin(), itr));
+ ScCondFormatItem aItem( std::move(aNewCondFormatData) );
+ pPatternAttr->GetItemSet().Put( aItem );
+ SetPatternArea( nTempStartRow, nTempEndRow, std::move(pPatternAttr), true );
+ }
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ nTempStartRow = nTempEndRow + 1;
+ }
+ while(nTempEndRow < nEndRow);
+
+}
+
+void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow,
+ const ScPatternAttr* pPattern, ScEditDataArray* pDataArray )
+{
+ assert( nCol != -1 );
+ // cache mdds position, this doesn't modify the mdds container, just EditTextObject's
+ sc::ColumnBlockPosition blockPos;
+ rDocument.InitColumnBlockPosition( blockPos, nTab, nCol );
+ nEndRow = rDocument.GetLastDataRow(nTab, nCol, nCol, nEndRow);
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(rDocument, aPos, blockPos);
+ if (aCell.getType() != CELLTYPE_EDIT || !aCell.getEditText())
+ continue;
+
+ std::unique_ptr<EditTextObject> pOldData;
+ if (pDataArray)
+ pOldData = aCell.getEditText()->Clone();
+
+ // Direct modification of cell content - something to watch out for if
+ // we decide to share edit text instances in the future.
+ ScEditUtil::RemoveCharAttribs(const_cast<EditTextObject&>(*aCell.getEditText()), *pPattern);
+
+ if (pDataArray)
+ {
+ std::unique_ptr<EditTextObject> pNewData = aCell.getEditText()->Clone();
+ pDataArray->AddItem(nTab, nCol, nRow, std::move(pOldData), std::move(pNewData));
+ }
+ }
+}
+
+bool ScAttrArray::Reserve( SCSIZE nReserve )
+{
+ if ( mvData.empty() && nReserve )
+ {
+ try {
+ mvData.reserve(nReserve);
+ mvData.emplace_back();
+ mvData[0].nEndRow = rDocument.MaxRow();
+ mvData[0].pPattern = rDocument.GetDefPattern(); // no put
+ return true;
+ } catch (std::bad_alloc const &) {
+ return false;
+ }
+ }
+ else if ( mvData.capacity() < nReserve )
+ {
+ try {
+ mvData.reserve(nReserve);
+ return true;
+ } catch (std::bad_alloc const &) {
+ return false;
+ }
+ }
+ else
+ return false;
+}
+
+const ScPatternAttr* ScAttrArray::SetPatternAreaImpl(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr* pPattern,
+ bool bPutToPool, ScEditDataArray* pDataArray, bool bPassingOwnership )
+{
+ if (rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow))
+ {
+ if (bPutToPool)
+ {
+ if (bPassingOwnership)
+ pPattern = &rDocument.GetPool()->DirectPutItemInPool(std::unique_ptr<ScPatternAttr>(const_cast<ScPatternAttr*>(pPattern)));
+ else
+ pPattern = &rDocument.GetPool()->DirectPutItemInPool(*pPattern);
+ }
+ if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
+ Reset(pPattern);
+ else
+ {
+ SCSIZE nNeeded = mvData.size() + 2;
+ SetDefaultIfNotInit( nNeeded );
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ SCSIZE ni = 0; // number of entries in beginning
+ SCSIZE nx = 0; // track position
+ SCROW ns = 0; // start row of track position
+ if ( nStartRow > 0 )
+ {
+ // skip beginning
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ ni = nIndex;
+
+ if ( ni > 0 )
+ {
+ nx = ni;
+ ns = mvData[ni-1].nEndRow+1;
+ }
+ }
+
+ // ensure that attributing changes text width of cell
+ // otherwise, conditional formats need to be reset or deleted
+ bool bIsLoading = !rDocument.GetDocumentShell() || rDocument.GetDocumentShell()->IsLoading();
+ while ( ns <= nEndRow )
+ {
+ if ( nCol != -1 && !bIsLoading )
+ {
+ const SfxItemSet& rNewSet = pPattern->GetItemSet();
+ const SfxItemSet& rOldSet = mvData[nx].pPattern->GetItemSet();
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rNewSet, rOldSet ) )
+ {
+ aAdrStart.SetRow( std::max(nStartRow,ns) );
+ aAdrEnd .SetRow( std::min(nEndRow,mvData[nx].nEndRow) );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+ ns = mvData[nx].nEndRow + 1;
+ nx++;
+ }
+
+ // continue modifying data array
+
+ SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert)
+ bool bCombined = false;
+ bool bSplit = false;
+ if ( nStartRow > 0 )
+ {
+ nInsert = rDocument.MaxRow() + 1;
+ if ( !SfxPoolItem::areSame(mvData[ni].pPattern, pPattern ) )
+ {
+ if ( ni == 0 || (mvData[ni-1].nEndRow < nStartRow - 1) )
+ { // may be a split or a simple insert or just a shrink,
+ // row adjustment is done further down
+ if ( mvData[ni].nEndRow > nEndRow )
+ bSplit = true;
+ ni++;
+ nInsert = ni;
+ }
+ else if (mvData[ni - 1].nEndRow == nStartRow - 1)
+ nInsert = ni;
+ }
+ if ( ni > 0 && SfxPoolItem::areSame(mvData[ni-1].pPattern, pPattern) )
+ { // combine
+ mvData[ni-1].nEndRow = nEndRow;
+ nInsert = rDocument.MaxRow() + 1;
+ bCombined = true;
+ }
+ }
+ else
+ nInsert = 0;
+
+ SCSIZE nj = ni; // stop position of range to replace
+ while ( nj < mvData.size() && mvData[nj].nEndRow <= nEndRow )
+ nj++;
+ if ( !bSplit )
+ {
+ if ( nj < mvData.size() && SfxPoolItem::areSame(mvData[nj].pPattern, pPattern ) )
+ { // combine
+ if ( ni > 0 )
+ {
+ if ( SfxPoolItem::areSame(mvData[ni-1].pPattern, pPattern ) )
+ { // adjacent entries
+ mvData[ni-1].nEndRow = mvData[nj].nEndRow;
+ nj++;
+ }
+ else if ( ni == nInsert )
+ mvData[ni-1].nEndRow = nStartRow - 1; // shrink
+ }
+ nInsert = rDocument.MaxRow() + 1;
+ bCombined = true;
+ }
+ else if ( ni > 0 && ni == nInsert )
+ mvData[ni-1].nEndRow = nStartRow - 1; // shrink
+ }
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ if ( bSplit )
+ { // duplicate split entry in pool
+ pDocPool->DirectPutItemInPool( *mvData[ni-1].pPattern );
+ }
+ if ( ni < nj )
+ { // remove middle entries
+ for ( SCSIZE nk=ni; nk<nj; nk++)
+ { // remove entries from pool
+ pDocPool->DirectRemoveItemFromPool( *mvData[nk].pPattern );
+ }
+ if ( !bCombined )
+ { // replace one entry
+ mvData[ni].nEndRow = nEndRow;
+ mvData[ni].pPattern = pPattern;
+ ni++;
+ nInsert = rDocument.MaxRow() + 1;
+ }
+ if ( ni < nj )
+ { // remove entries
+ mvData.erase( mvData.begin() + ni, mvData.begin() + nj);
+ }
+ }
+
+ if ( nInsert < sal::static_int_cast<SCSIZE>(rDocument.MaxRow() + 1) )
+ { // insert or append new entry
+ if ( nInsert <= mvData.size() )
+ {
+ if ( !bSplit )
+ mvData.emplace(mvData.begin() + nInsert);
+ else
+ {
+ mvData.insert(mvData.begin() + nInsert, 2, ScAttrEntry());
+ mvData[nInsert+1] = mvData[nInsert-1];
+ }
+ }
+ if ( nInsert )
+ mvData[nInsert-1].nEndRow = nStartRow - 1;
+ mvData[nInsert].nEndRow = nEndRow;
+ mvData[nInsert].pPattern = pPattern;
+
+ // Remove character attributes from these cells if the pattern
+ // is applied during normal session.
+ if (pDataArray && nCol != -1)
+ RemoveCellCharAttribs(nStartRow, nEndRow, pPattern, pDataArray);
+ }
+
+ rDocument.SetStreamValid(nTab, false);
+ }
+ }
+
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+ return pPattern;
+}
+
+void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle )
+{
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SetDefaultIfNotInit();
+ SCSIZE nPos;
+ SCROW nStart=0;
+ if (!Search( nStartRow, nPos ))
+ {
+ OSL_FAIL("Search Failure");
+ return;
+ }
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ do
+ {
+ const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
+ pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(&rStyle));
+ SCROW nY1 = nStart;
+ SCROW nY2 = mvData[nPos].nEndRow;
+ nStart = mvData[nPos].nEndRow + 1;
+
+ if ( *pNewPattern == *pOldPattern )
+ {
+ // keep the original pattern (might be default)
+ // pNewPattern is deleted below
+ nPos++;
+ }
+ else if ( nY1 < nStartRow || nY2 > nEndRow )
+ {
+ if (nY1 < nStartRow) nY1=nStartRow;
+ if (nY2 > nEndRow) nY2=nEndRow;
+ SetPatternArea( nY1, nY2, std::move(pNewPattern), true );
+ Search( nStart, nPos );
+ }
+ else
+ {
+ if ( nCol != -1 )
+ {
+ // ensure attributing changes text width of cell; otherwise
+ // there aren't (yet) template format changes
+ const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rNewSet, rOldSet ) )
+ {
+ aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
+ aAdrEnd .SetRow( mvData[nPos].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+
+ rDocument.GetPool()->DirectRemoveItemFromPool(*mvData[nPos].pPattern);
+ mvData[nPos].pPattern = &rDocument.GetPool()->DirectPutItemInPool(*pNewPattern);
+ if (Concat(nPos))
+ Search(nStart, nPos);
+ else
+ nPos++;
+ }
+ }
+ while ((nStart <= nEndRow) && (nPos < mvData.size()));
+
+ rDocument.SetStreamValid(nTab, false);
+
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+}
+
+ // const cast, otherwise it will be too inefficient/complicated
+static void SetLineColor(SvxBorderLine const * dest, Color c)
+{
+ if (dest)
+ {
+ const_cast<SvxBorderLine*>(dest)->SetColor(c);
+ }
+}
+
+static void SetLine(const SvxBorderLine* dest, const SvxBorderLine* src)
+{
+ if (dest)
+ {
+ SvxBorderLine* pCast = const_cast<SvxBorderLine*>(dest);
+ pCast->SetBorderLineStyle( src->GetBorderLineStyle() );
+ pCast->SetWidth( src->GetWidth() );
+ }
+}
+
+void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
+ const SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SCSIZE nPos;
+ SCROW nStart=0;
+ SetDefaultIfNotInit();
+ if (!Search( nStartRow, nPos ))
+ {
+ OSL_FAIL("Search failure");
+ return;
+ }
+
+ do
+ {
+ const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+ const SvxBoxItem* pBoxItem = rOldSet.GetItemIfSet( ATTR_BORDER );
+ const SvxLineItem* pTLBRItem = rOldSet.GetItemIfSet( ATTR_BORDER_TLBR );
+ const SvxLineItem* pBLTRItem = rOldSet.GetItemIfSet( ATTR_BORDER_BLTR );
+
+ if ( pBoxItem || pTLBRItem || pBLTRItem )
+ {
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
+ SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+ SCROW nY1 = nStart;
+ SCROW nY2 = mvData[nPos].nEndRow;
+
+ std::unique_ptr<SvxBoxItem> pNewBoxItem( pBoxItem ? pBoxItem->Clone() : nullptr);
+ std::unique_ptr<SvxLineItem> pNewTLBRItem( pTLBRItem ? pTLBRItem->Clone() : nullptr);
+ std::unique_ptr<SvxLineItem> pNewBLTRItem(pBLTRItem ? pBLTRItem->Clone() : nullptr);
+
+ // fetch line and update attributes with parameters
+
+ if ( !pLine )
+ {
+ if( pNewBoxItem )
+ {
+ if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::TOP );
+ if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+ if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::LEFT );
+ if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ if( pNewTLBRItem && pNewTLBRItem->GetLine() )
+ pNewTLBRItem->SetLine( nullptr );
+ if( pNewBLTRItem && pNewBLTRItem->GetLine() )
+ pNewBLTRItem->SetLine( nullptr );
+ }
+ else
+ {
+ if ( bColorOnly )
+ {
+ Color aColor( pLine->GetColor() );
+ if( pNewBoxItem )
+ {
+ SetLineColor( pNewBoxItem->GetTop(), aColor );
+ SetLineColor( pNewBoxItem->GetBottom(), aColor );
+ SetLineColor( pNewBoxItem->GetLeft(), aColor );
+ SetLineColor( pNewBoxItem->GetRight(), aColor );
+ }
+ if( pNewTLBRItem )
+ SetLineColor( pNewTLBRItem->GetLine(), aColor );
+ if( pNewBLTRItem )
+ SetLineColor( pNewBLTRItem->GetLine(), aColor );
+ }
+ else
+ {
+ if( pNewBoxItem )
+ {
+ SetLine( pNewBoxItem->GetTop(), pLine );
+ SetLine( pNewBoxItem->GetBottom(), pLine );
+ SetLine( pNewBoxItem->GetLeft(), pLine );
+ SetLine( pNewBoxItem->GetRight(), pLine );
+ }
+ if( pNewTLBRItem )
+ SetLine( pNewTLBRItem->GetLine(), pLine );
+ if( pNewBLTRItem )
+ SetLine( pNewBLTRItem->GetLine(), pLine );
+ }
+ }
+ if( pNewBoxItem ) rNewSet.Put( std::move(pNewBoxItem) );
+ if( pNewTLBRItem ) rNewSet.Put( std::move(pNewTLBRItem) );
+ if( pNewBLTRItem ) rNewSet.Put( std::move(pNewBLTRItem) );
+
+ nStart = mvData[nPos].nEndRow + 1;
+
+ if ( nY1 < nStartRow || nY2 > nEndRow )
+ {
+ if (nY1 < nStartRow) nY1=nStartRow;
+ if (nY2 > nEndRow) nY2=nEndRow;
+ SetPatternArea( nY1, nY2, std::move(pNewPattern), true );
+ Search( nStart, nPos );
+ }
+ else
+ {
+ // remove from pool ?
+ rDocument.GetPool()->DirectRemoveItemFromPool(*mvData[nPos].pPattern);
+ mvData[nPos].pPattern =
+ &rDocument.GetPool()->DirectPutItemInPool(std::move(pNewPattern));
+
+ if (Concat(nPos))
+ Search(nStart, nPos);
+ else
+ nPos++;
+ }
+ }
+ else
+ {
+ nStart = mvData[nPos].nEndRow + 1;
+ nPos++;
+ }
+ }
+ while ((nStart <= nEndRow) && (nPos < mvData.size()));
+}
+
+void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, ScItemPoolCache* pCache, ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SCSIZE nPos;
+ SCROW nStart=0;
+ SetDefaultIfNotInit();
+ if (!Search( nStartRow, nPos ))
+ {
+ OSL_FAIL("Search Failure");
+ return;
+ }
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ do
+ {
+ const ScPatternAttr& rOldPattern = *mvData[nPos].pPattern;
+ const ScPatternAttr& rNewPattern = pCache->ApplyTo( rOldPattern );
+ if (!SfxPoolItem::areSame(rNewPattern, rOldPattern))
+ {
+ SCROW nY1 = nStart;
+ SCROW nY2 = mvData[nPos].nEndRow;
+ nStart = mvData[nPos].nEndRow + 1;
+
+ if(pIsChanged)
+ *pIsChanged = true;
+
+ if ( nY1 < nStartRow || nY2 > nEndRow )
+ {
+ if (nY1 < nStartRow) nY1=nStartRow;
+ if (nY2 > nEndRow) nY2=nEndRow;
+ SetPatternArea( nY1, nY2, &rNewPattern, false, pDataArray );
+ Search( nStart, nPos );
+ }
+ else
+ {
+ if ( nCol != -1 )
+ {
+ // ensure attributing changes text-width of cell
+
+ const SfxItemSet& rNewSet = rNewPattern.GetItemSet();
+ const SfxItemSet& rOldSet = rOldPattern.GetItemSet();
+
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rNewSet, rOldSet ) )
+ {
+ aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
+ aAdrEnd .SetRow( mvData[nPos].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+
+ rDocument.GetPool()->DirectRemoveItemFromPool(*mvData[nPos].pPattern);
+ mvData[nPos].pPattern = &rNewPattern;
+ if (Concat(nPos))
+ Search(nStart, nPos);
+ else
+ ++nPos;
+ }
+ }
+ else
+ {
+ nStart = mvData[nPos].nEndRow + 1;
+ ++nPos;
+ }
+ }
+ while (nStart <= nEndRow);
+
+ rDocument.SetStreamValid(nTab, false);
+
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+}
+
+void ScAttrArray::SetAttrEntries(std::vector<ScAttrEntry> && vNewData)
+{
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ for (auto const & rEntry : mvData)
+ pDocPool->DirectRemoveItemFromPool(*rEntry.pPattern);
+
+ mvData = std::move(vNewData);
+
+#ifdef DBG_UTIL
+ SCROW lastEndRow = -1;
+ for(const auto& entry : mvData)
+ { // Verify that the data is not corrupted.
+ assert(entry.nEndRow > lastEndRow);
+ lastEndRow = entry.nEndRow;
+ }
+#endif
+}
+
+static void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource )
+{
+ const SfxPoolItem* pNewItem;
+ const SfxPoolItem* pOldItem;
+ for (sal_uInt16 nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++)
+ {
+ // pMergeSet has no parent
+ SfxItemState eOldState = rMergeSet.GetItemState( nId, false, &pOldItem );
+
+ if ( eOldState == SfxItemState::DEFAULT )
+ {
+ SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
+ if ( eNewState == SfxItemState::SET )
+ {
+ if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) )
+ rMergeSet.InvalidateItem( nId );
+ }
+ }
+ else if ( eOldState == SfxItemState::SET ) // Item set
+ {
+ SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
+ if ( eNewState == SfxItemState::SET )
+ {
+ if ( !SfxPoolItem::areSame(pNewItem, pOldItem) ) // Both pulled
+ rMergeSet.InvalidateItem( nId );
+ }
+ else // Default
+ {
+ if ( *pOldItem != rSource.GetPool()->GetDefaultItem(nId) )
+ rMergeSet.InvalidateItem( nId );
+ }
+ }
+ // Dontcare remains Dontcare
+ }
+}
+
+void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow,
+ ScMergePatternState& rState, bool bDeep ) const
+{
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SCSIZE nPos = 0;
+ SCROW nStart=0;
+ if ( !mvData.empty() && !Search( nStartRow, nPos ) )
+ {
+ OSL_FAIL("Search failure");
+ return;
+ }
+
+ do
+ {
+ // similar patterns must not be repeated
+ const ScPatternAttr* pPattern = nullptr;
+ if ( !mvData.empty() )
+ pPattern = mvData[nPos].pPattern;
+ else
+ pPattern = rDocument.GetDefPattern();
+ if ( !SfxPoolItem::areSame(pPattern, rState.pOld1) && !SfxPoolItem::areSame(pPattern, rState.pOld2) )
+ {
+ const SfxItemSet& rThisSet = pPattern->GetItemSet();
+ if (rState.pItemSet)
+ {
+ rState.mbValidPatternId = false;
+ if (bDeep)
+ lcl_MergeDeep( *rState.pItemSet, rThisSet );
+ else
+ rState.pItemSet->MergeValues( rThisSet );
+ }
+ else
+ {
+ // first pattern - copied from parent
+ rState.pItemSet.emplace( *rThisSet.GetPool(), rThisSet.GetRanges() );
+ rState.pItemSet->Set( rThisSet, bDeep );
+ rState.mnPatternId = pPattern->GetPAKey();
+ }
+
+ rState.pOld2 = rState.pOld1;
+ rState.pOld1 = pPattern;
+ }
+
+ if ( !mvData.empty() )
+ nStart = mvData[nPos].nEndRow + 1;
+ else
+ nStart = rDocument.MaxRow() + 1;
+ ++nPos;
+ }
+ while (nStart <= nEndRow);
+}
+
+// assemble border
+
+static bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
+ sal_uInt8& rModified, const SvxBorderLine*& rpNew )
+{
+ if (rModified == SC_LINE_DONTCARE)
+ return false; // don't go again
+
+ if (rModified == SC_LINE_EMPTY)
+ {
+ rModified = SC_LINE_SET;
+ rpNew = pNewLine;
+ return true; // initial value
+ }
+
+ if (pOldLine == pNewLine)
+ {
+ rpNew = pOldLine;
+ return false;
+ }
+
+ if (pOldLine && pNewLine)
+ if (*pOldLine == *pNewLine)
+ {
+ rpNew = pOldLine;
+ return false;
+ }
+
+ rModified = SC_LINE_DONTCARE;
+ rpNew = nullptr;
+ return true; // another line -> don't care
+}
+
+static void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
+ ScLineFlags& rFlags, const ScPatternAttr* pPattern,
+ bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
+{
+ // right/bottom border set when connected together
+ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
+ if ( rMerge.GetColMerge() == nDistRight + 1 )
+ nDistRight = 0;
+ if ( rMerge.GetRowMerge() == nDistBottom + 1 )
+ nDistBottom = 0;
+
+ const SvxBoxItem* pCellFrame = &pPattern->GetItemSet().Get( ATTR_BORDER );
+ const SvxBorderLine* pLeftAttr = pCellFrame->GetLeft();
+ const SvxBorderLine* pRightAttr = pCellFrame->GetRight();
+ const SvxBorderLine* pTopAttr = pCellFrame->GetTop();
+ const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom();
+ const SvxBorderLine* pNew;
+
+ if (bTop)
+ {
+ if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::TOP );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
+ }
+
+ if (nDistBottom == 0)
+ {
+ if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::BOTTOM );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
+ }
+
+ if (bLeft)
+ {
+ if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::LEFT );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
+ }
+
+ if (nDistRight == 0)
+ {
+ if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::RIGHT );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
+ }
+}
+
+void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
+ ScLineFlags& rFlags,
+ SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight ) const
+{
+ const ScPatternAttr* pPattern;
+
+ if (nStartRow == nEndRow)
+ {
+ pPattern = GetPattern( nStartRow );
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true, 0 );
+ }
+ else if ( !mvData.empty() ) // non-default pattern
+ {
+ pPattern = GetPattern( nStartRow );
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true,
+ nEndRow-nStartRow );
+
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nStartRow+1, nStartIndex );
+ Search( nEndRow-1, nEndIndex );
+ for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
+ {
+ pPattern = mvData[i].pPattern;
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false,
+ nEndRow - std::min( mvData[i].nEndRow, static_cast<SCROW>(nEndRow-1) ) );
+ // nDistBottom here always > 0
+ }
+
+ pPattern = GetPattern( nEndRow );
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, 0 );
+ }
+ else
+ {
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, rDocument.GetDefPattern(), bLeft, nDistRight, true, 0 );
+ }
+}
+
+// apply border
+
+// ApplyFrame - on an entry into the array
+
+bool ScAttrArray::ApplyFrame( const SvxBoxItem& rBoxItem,
+ const SvxBoxInfoItem* pBoxInfoItem,
+ SCROW nStartRow, SCROW nEndRow,
+ bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
+{
+ OSL_ENSURE( pBoxInfoItem, "Missing line attributes!" );
+
+ const ScPatternAttr* pPattern = GetPattern( nStartRow );
+ const SvxBoxItem* pOldFrame = &pPattern->GetItemSet().Get( ATTR_BORDER );
+
+ // right/bottom border set when connected together
+ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
+ if ( rMerge.GetColMerge() == nDistRight + 1 )
+ nDistRight = 0;
+ if ( rMerge.GetRowMerge() == nDistBottom + 1 )
+ nDistBottom = 0;
+
+ SvxBoxItem aNewFrame( *pOldFrame );
+ bool bRTL=rDocument.IsLayoutRTL(nTab);
+ // fdo#37464 check if the sheet are RTL then replace right <=> left
+ if (bRTL)
+ {
+ if( bLeft && nDistRight==0)
+ {
+ if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
+ aNewFrame.SetLine( rBoxItem.GetLeft(), SvxBoxItemLine::RIGHT );
+ if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
+ aNewFrame.SetLine( rBoxItem.GetRight(), SvxBoxItemLine::LEFT );
+ }
+ else
+ {
+ if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( (nDistRight==0) ? rBoxItem.GetLeft() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::RIGHT );
+ if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( bLeft ? rBoxItem.GetRight() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::LEFT );
+ }
+ }
+ else
+ {
+ if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( bLeft ? rBoxItem.GetLeft() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::LEFT );
+ if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( (nDistRight==0) ? rBoxItem.GetRight() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::RIGHT );
+ }
+ if ( bTop ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ aNewFrame.SetLine( bTop ? rBoxItem.GetTop() : pBoxInfoItem->GetHori(),
+ SvxBoxItemLine::TOP );
+ if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ aNewFrame.SetLine( (nDistBottom==0) ? rBoxItem.GetBottom() : pBoxInfoItem->GetHori(),
+ SvxBoxItemLine::BOTTOM );
+
+ if (aNewFrame == *pOldFrame)
+ {
+ // nothing to do
+ return false;
+ }
+ else
+ {
+ ScItemPoolCache aCache( rDocument.GetPool(), &aNewFrame );
+ ApplyCacheArea( nStartRow, nEndRow, &aCache );
+
+ return true;
+ }
+}
+
+void ScAttrArray::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
+ SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight)
+{
+ SetDefaultIfNotInit();
+ if (nStartRow == nEndRow)
+ ApplyFrame(rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0);
+ else
+ {
+ ApplyFrame(rLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight,
+ true, nEndRow-nStartRow);
+
+ if ( nEndRow > nStartRow+1 ) // inner part available?
+ {
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nStartRow+1, nStartIndex );
+ Search( nEndRow-1, nEndIndex );
+ SCROW nTmpStart = nStartRow+1;
+ SCROW nTmpEnd;
+ for (SCSIZE i=nStartIndex; i<=nEndIndex;)
+ {
+ nTmpEnd = std::min( static_cast<SCROW>(nEndRow-1), mvData[i].nEndRow );
+ bool bChanged = ApplyFrame(rLineOuter, pLineInner, nTmpStart, nTmpEnd,
+ bLeft, nDistRight, false, nEndRow - nTmpEnd);
+ nTmpStart = nTmpEnd+1;
+ if (bChanged)
+ {
+ Search(nTmpStart, i);
+ Search(nEndRow-1, nEndIndex);
+ }
+ else
+ i++;
+ }
+ }
+
+ ApplyFrame(rLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, false, 0);
+ }
+}
+
+bool ScAttrArray::HasAttrib_Impl(const ScPatternAttr* pPattern, HasAttrFlags nMask, SCROW nRow1, SCROW nRow2, SCSIZE i) const
+{
+ bool bFound = false;
+ if ( nMask & HasAttrFlags::Merged )
+ {
+ const ScMergeAttr* pMerge = &pPattern->GetItem( ATTR_MERGE );
+ if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
+ bFound = true;
+ }
+ if ( nMask & ( HasAttrFlags::Overlapped | HasAttrFlags::NotOverlapped | HasAttrFlags::AutoFilter ) )
+ {
+ const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem( ATTR_MERGE_FLAG );
+ if ( (nMask & HasAttrFlags::Overlapped) && pMergeFlag->IsOverlapped() )
+ bFound = true;
+ if ( (nMask & HasAttrFlags::NotOverlapped) && !pMergeFlag->IsOverlapped() )
+ bFound = true;
+ if ( (nMask & HasAttrFlags::AutoFilter) && pMergeFlag->HasAutoFilter() )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Lines )
+ {
+ const SvxBoxItem* pBox = &pPattern->GetItem( ATTR_BORDER );
+ if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Shadow )
+ {
+ const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
+ if ( pShadow->GetLocation() != SvxShadowLocation::NONE )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Conditional )
+ {
+ if ( !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty())
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Protected )
+ {
+ const ScProtectionAttr* pProtect = &pPattern->GetItem( ATTR_PROTECTION );
+ bool bFoundTemp = false;
+ if ( pProtect->GetProtection() || pProtect->GetHideCell() )
+ bFoundTemp = true;
+
+ bool bContainsCondFormat = !mvData.empty() &&
+ !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty();
+ if ( bContainsCondFormat && nCol != -1 ) // rDocument.GetCondResult() is valid only for real columns.
+ {
+ SCROW nRowStartCond = std::max<SCROW>( nRow1, i ? mvData[i-1].nEndRow + 1: 0 );
+ SCROW nRowEndCond = std::min<SCROW>( nRow2, mvData[i].nEndRow );
+ bool bFoundCond = false;
+ for(SCROW nRowCond = nRowStartCond; nRowCond <= nRowEndCond && !bFoundCond; ++nRowCond)
+ {
+ const SfxItemSet* pSet = rDocument.GetCondResult( nCol, nRowCond, nTab );
+
+ const ScProtectionAttr* pCondProtect;
+ if( pSet && (pCondProtect = pSet->GetItemIfSet( ATTR_PROTECTION )) )
+ {
+ if( pCondProtect->GetProtection() || pCondProtect->GetHideCell() )
+ bFoundCond = true;
+ else
+ break;
+ }
+ else
+ {
+ // well it is not true that we found one
+ // but existing one + cell where conditional
+ // formatting does not remove it
+ // => we should use the existing protection setting
+ bFoundCond = bFoundTemp;
+ }
+ }
+ bFoundTemp = bFoundCond;
+ }
+
+ if(bFoundTemp)
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Rotate )
+ {
+ const ScRotateValueItem* pRotate = &pPattern->GetItem( ATTR_ROTATE_VALUE );
+ // 90 or 270 degrees is former SvxOrientationItem - only look for other values
+ // (see ScPatternAttr::GetCellOrientation)
+ Degree100 nAngle = pRotate->GetValue();
+ if ( nAngle && nAngle != 9000_deg100 && nAngle != 27000_deg100 )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::NeedHeight )
+ {
+ if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
+ bFound = true;
+ else if (pPattern->GetItem( ATTR_LINEBREAK ).GetValue())
+ bFound = true;
+ else if (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block)
+ bFound = true;
+
+ else if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
+ bFound = true;
+ else if (pPattern->GetItem( ATTR_ROTATE_VALUE ).GetValue())
+ bFound = true;
+ }
+ if ( nMask & ( HasAttrFlags::ShadowRight | HasAttrFlags::ShadowDown ) )
+ {
+ const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
+ SvxShadowLocation eLoc = pShadow->GetLocation();
+ if ( nMask & HasAttrFlags::ShadowRight )
+ if ( eLoc == SvxShadowLocation::TopRight || eLoc == SvxShadowLocation::BottomRight )
+ bFound = true;
+ if ( nMask & HasAttrFlags::ShadowDown )
+ if ( eLoc == SvxShadowLocation::BottomLeft || eLoc == SvxShadowLocation::BottomRight )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::RightOrCenter )
+ {
+ // called only if the sheet is LTR, so physical=logical alignment can be assumed
+ SvxCellHorJustify eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+ if ( eHorJust == SvxCellHorJustify::Right || eHorJust == SvxCellHorJustify::Center )
+ bFound = true;
+ }
+
+ return bFound;
+}
+
+// Test if field contains specific attribute
+bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) const
+{
+ if (mvData.empty())
+ {
+ return HasAttrib_Impl(rDocument.GetDefPattern(), nMask, 0, rDocument.MaxRow(), 0);
+ }
+
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nRow1, nStartIndex );
+ if (nRow1 != nRow2)
+ Search( nRow2, nEndIndex, /*hint*/nStartIndex );
+ else
+ nEndIndex = nStartIndex;
+ bool bFound = false;
+
+ for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++)
+ {
+ const ScPatternAttr* pPattern = mvData[i].pPattern;
+ bFound = HasAttrib_Impl(pPattern, nMask, nRow1, nRow2, i);
+ }
+
+ return bFound;
+}
+
+bool ScAttrArray::HasAttrib( SCROW nRow, HasAttrFlags nMask, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ if (mvData.empty())
+ {
+ if( nStartRow )
+ *nStartRow = 0;
+ if( nEndRow )
+ *nEndRow = rDocument.MaxRow();
+ return HasAttrib_Impl(rDocument.GetDefPattern(), nMask, 0, rDocument.MaxRow(), 0);
+ }
+
+ SCSIZE nIndex;
+ Search( nRow, nIndex );
+ if( nStartRow )
+ *nStartRow = nIndex > 0 ? mvData[nIndex-1].nEndRow+1 : 0;
+ if( nEndRow )
+ *nEndRow = mvData[nIndex].nEndRow;
+ const ScPatternAttr* pPattern = mvData[nIndex].pPattern;
+ return HasAttrib_Impl(pPattern, nMask, nRow, nRow, nIndex);
+}
+
+bool ScAttrArray::IsMerged( SCROW nRow ) const
+{
+ if ( !mvData.empty() )
+ {
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ const ScMergeAttr& rItem = mvData[nIndex].pPattern->GetItem(ATTR_MERGE);
+
+ return rItem.IsMerged();
+ }
+
+ return rDocument.GetDefPattern()->GetItem(ATTR_MERGE).IsMerged();
+}
+
+/**
+ * Area around any given summaries expand and adapt any MergeFlag (bRefresh)
+ */
+bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
+ SCCOL& rPaintCol, SCROW& rPaintRow,
+ bool bRefresh )
+{
+ assert( nCol != -1 );
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pPattern;
+ const ScMergeAttr* pItem;
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nStartRow, nStartIndex );
+ Search( nEndRow, nEndIndex );
+ bool bFound = false;
+
+ for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
+ {
+ pPattern = mvData[i].pPattern;
+ pItem = &pPattern->GetItem( ATTR_MERGE );
+ SCCOL nCountX = pItem->GetColMerge();
+ SCROW nCountY = pItem->GetRowMerge();
+ if (nCountX>1 || nCountY>1)
+ {
+ SCROW nThisRow = (i>0) ? mvData[i-1].nEndRow+1 : 0;
+ SCCOL nMergeEndCol = nThisCol + nCountX - 1;
+ SCROW nMergeEndRow = nThisRow + nCountY - 1;
+ if (nMergeEndCol > rPaintCol && nMergeEndCol <= rDocument.MaxCol())
+ rPaintCol = nMergeEndCol;
+ if (nMergeEndRow > rPaintRow && nMergeEndRow <= rDocument.MaxRow())
+ rPaintRow = nMergeEndRow;
+ bFound = true;
+
+ if (bRefresh)
+ {
+ if ( nMergeEndCol > nThisCol )
+ rDocument.ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, mvData[i].nEndRow,
+ nTab, ScMF::Hor );
+ if ( nMergeEndRow > nThisRow )
+ rDocument.ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow,
+ nTab, ScMF::Ver );
+ if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow )
+ rDocument.ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow,
+ nTab, ScMF::Hor | ScMF::Ver );
+
+ Search( nThisRow, i ); // Data changed
+ Search( nStartRow, nStartIndex );
+ Search( nEndRow, nEndIndex );
+ }
+ }
+ }
+
+ return bFound;
+}
+
+void ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
+{
+ assert( nCol != -1 );
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pPattern;
+ const ScMergeAttr* pItem;
+ SCSIZE nIndex;
+
+ Search( nStartRow, nIndex );
+ SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisStart < nStartRow)
+ nThisStart = nStartRow;
+
+ while ( nThisStart <= nEndRow )
+ {
+ SCROW nThisEnd = mvData[nIndex].nEndRow;
+ if (nThisEnd > nEndRow)
+ nThisEnd = nEndRow;
+
+ pPattern = mvData[nIndex].pPattern;
+ pItem = &pPattern->GetItem( ATTR_MERGE );
+ SCCOL nCountX = pItem->GetColMerge();
+ SCROW nCountY = pItem->GetRowMerge();
+ if (nCountX>1 || nCountY>1)
+ {
+ const ScMergeAttr* pAttr = &rDocument.GetPool()->GetDefaultItem( ATTR_MERGE );
+ const ScMergeFlagAttr* pFlagAttr = &rDocument.GetPool()->GetDefaultItem( ATTR_MERGE_FLAG );
+
+ OSL_ENSURE( nCountY==1 || nThisStart==nThisEnd, "What's up?" );
+
+ SCCOL nThisCol = nCol;
+ SCCOL nMergeEndCol = nThisCol + nCountX - 1;
+ SCROW nMergeEndRow = nThisEnd + nCountY - 1;
+
+ // ApplyAttr for areas
+ for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++)
+ rDocument.ApplyAttr( nThisCol, nThisRow, nTab, *pAttr );
+
+ ScPatternAttr aNewPattern( rDocument.GetPool() );
+ SfxItemSet* pSet = &aNewPattern.GetItemSet();
+ pSet->Put( *pFlagAttr );
+ rDocument.ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow,
+ nTab, aNewPattern );
+
+ Search( nThisEnd, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ if ( nIndex < mvData.size() )
+ nThisStart = mvData[nIndex-1].nEndRow+1;
+ else
+ nThisStart = rDocument.MaxRow()+1; // End
+ }
+}
+
+void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow,
+ const ScPatternAttr* pWantedPattern, bool bDefault )
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pOldPattern;
+ const ScMergeFlagAttr* pItem;
+
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+ bool bFirstUse = true;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ while ( nThisRow <= nEndRow )
+ {
+ pOldPattern = mvData[nIndex].pPattern;
+ if (!SfxPoolItem::areSame(pOldPattern, pWantedPattern)) // FIXME: else-branch?
+ {
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ pItem = &pOldPattern->GetItem( ATTR_MERGE_FLAG );
+
+ if (pItem->IsOverlapped() || pItem->HasAutoFilter())
+ {
+ // default-constructing a ScPatternAttr for DeleteArea doesn't work
+ // because it would have no cell style information.
+ // Instead, the document's GetDefPattern is copied. Since it is passed as
+ // pWantedPattern, no special treatment of default is needed here anymore.
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( *pWantedPattern ));
+ pNewPattern->GetItemSet().Put( *pItem );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ }
+ else
+ {
+ if ( !bDefault )
+ {
+ if (bFirstUse)
+ bFirstUse = false;
+ else
+ // it's in the pool
+ rDocument.GetPool()->DirectPutItemInPool( *pWantedPattern );
+ }
+ SetPatternArea( nThisRow, nAttrRow, pWantedPattern );
+ }
+
+ Search( nThisRow, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+}
+
+bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pOldPattern;
+
+ ScMF nOldValue;
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+ bool bChanged = false;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ pOldPattern = mvData[nIndex].pPattern;
+ nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
+ if ( (nOldValue | nFlags) != nOldValue )
+ {
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ Search( nThisRow, nIndex ); // data changed
+ bChanged = true;
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+
+ return bChanged;
+}
+
+bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pOldPattern;
+
+ ScMF nOldValue;
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+ bool bChanged = false;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ pOldPattern = mvData[nIndex].pPattern;
+ nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
+ if ( (nOldValue & ~nFlags) != nOldValue )
+ {
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ Search( nThisRow, nIndex ); // data changed
+ bChanged = true;
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+
+ return bChanged;
+}
+
+void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
+{
+ SetDefaultIfNotInit();
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
+ if ( pOldPattern->HasItemsSet( pWhich ) )
+ {
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->ClearItems( pWhich );
+
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ Search( nThisRow, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+}
+
+void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, bool bIncrement )
+{
+ SetDefaultIfNotInit();
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisStart < nStartRow) nThisStart = nStartRow;
+
+ while ( nThisStart <= nEndRow )
+ {
+ const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+ const SvxHorJustifyItem* pItem;
+
+ bool bNeedJust = !( pItem = rOldSet.GetItemIfSet( ATTR_HOR_JUSTIFY, false ) )
+ || (pItem->GetValue() != SvxCellHorJustify::Left &&
+ pItem->GetValue() != SvxCellHorJustify::Right );
+ sal_uInt16 nOldValue = rOldSet.Get( ATTR_INDENT ).GetValue();
+ sal_uInt16 nNewValue = nOldValue;
+ // To keep Increment indent from running outside the cell1659
+ tools::Long nColWidth = static_cast<tools::Long>(
+ rDocument.GetColWidth(nCol == -1 ? rDocument.MaxCol() : nCol,nTab));
+ if ( bIncrement )
+ {
+ if ( nNewValue < nColWidth-SC_INDENT_STEP )
+ {
+ nNewValue += SC_INDENT_STEP;
+ if ( nNewValue > nColWidth-SC_INDENT_STEP )
+ nNewValue = nColWidth-SC_INDENT_STEP;
+ }
+ }
+ else
+ {
+ if ( nNewValue > 0 )
+ {
+ if ( nNewValue > SC_INDENT_STEP )
+ nNewValue -= SC_INDENT_STEP;
+ else
+ nNewValue = 0;
+ }
+ }
+
+ if ( bNeedJust || nNewValue != nOldValue )
+ {
+ SCROW nThisEnd = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nThisEnd, nEndRow );
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->GetItemSet().Put( ScIndentItem( nNewValue ) );
+ if ( bNeedJust )
+ pNewPattern->GetItemSet().Put(
+ SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
+ SetPatternArea( nThisStart, nAttrRow, std::move(pNewPattern), true );
+
+ nThisStart = nThisEnd + 1;
+ Search( nThisStart, nIndex ); // data changed
+ }
+ else
+ {
+ nThisStart = mvData[nIndex].nEndRow + 1;
+ ++nIndex;
+ }
+ }
+}
+
+SCROW ScAttrArray::GetNextUnprotected( SCROW nRow, bool bUp ) const
+{
+ tools::Long nRet = nRow;
+ if (rDocument.ValidRow(nRow))
+ {
+ if ( mvData.empty() )
+ {
+ if ( bUp )
+ return -1;
+ else
+ return rDocument.MaxRow()+1;
+ }
+
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ while (mvData[nIndex].pPattern->
+ GetItem(ATTR_PROTECTION).GetProtection())
+ {
+ if (bUp)
+ {
+ if (nIndex==0)
+ return -1; // not found
+ --nIndex;
+ nRet = mvData[nIndex].nEndRow;
+ }
+ else
+ {
+ nRet = mvData[nIndex].nEndRow+1;
+ ++nIndex;
+ if (nIndex >= mvData.size())
+ return rDocument.MaxRow()+1; // not found
+ }
+ }
+ }
+ return nRet;
+}
+
+void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
+{
+ SetDefaultIfNotInit();
+ SCROW nStart = 0;
+ SCSIZE nPos = 0;
+ while (nPos < mvData.size())
+ {
+ SCROW nEnd = mvData[nPos].nEndRow;
+ if (mvData[nPos].pPattern->GetStyleSheet() == pStyleSheet)
+ {
+ rUsedRows.setTrue(nStart, nEnd);
+
+ if (bReset)
+ {
+ ScPatternAttr aNewPattern(*mvData[nPos].pPattern);
+ rDocument.GetPool()->DirectRemoveItemFromPool(*mvData[nPos].pPattern);
+ aNewPattern.SetStyleSheet( static_cast<ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->
+ Find( ScResId(STR_STYLENAME_STANDARD),
+ SfxStyleFamily::Para,
+ SfxStyleSearchBits::Auto | SfxStyleSearchBits::ScStandard ) ) );
+ mvData[nPos].pPattern = &rDocument.GetPool()->DirectPutItemInPool(aNewPattern);
+
+ if (Concat(nPos))
+ {
+ Search(nStart, nPos);
+ --nPos; // because ++ at end
+ }
+ }
+ }
+ nStart = nEnd + 1;
+ ++nPos;
+ }
+}
+
+bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
+{
+ if ( mvData.empty() )
+ {
+ const ScStyleSheet* pStyle = rDocument.GetDefPattern()->GetStyleSheet();
+ if ( pStyle )
+ {
+ pStyle->SetUsage( ScStyleSheet::Usage::USED );
+ if ( pStyle == &rStyle )
+ return true;
+ }
+ return false;
+ }
+
+ bool bIsUsed = false;
+ SCSIZE nPos = 0;
+
+ while ( nPos < mvData.size() )
+ {
+ const ScStyleSheet* pStyle = mvData[nPos].pPattern->GetStyleSheet();
+ if ( pStyle )
+ {
+ pStyle->SetUsage( ScStyleSheet::Usage::USED );
+ if ( pStyle == &rStyle )
+ {
+ bIsUsed = true;
+ }
+ }
+ nPos++;
+ }
+
+ return bIsUsed;
+}
+
+bool ScAttrArray::IsEmpty() const
+{
+ if ( mvData.empty() )
+ return true;
+
+ if (mvData.size() == 1)
+ {
+ return SfxPoolItem::areSame(mvData[0].pPattern, rDocument.GetDefPattern());
+ }
+ else
+ return false;
+}
+
+bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
+{
+ if ( mvData.empty() )
+ return false;
+
+ bool bFound = false;
+ SCSIZE nStart = 0;
+
+ // Skip first entry if more than 1 row.
+ // Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr.
+
+ SCSIZE nVisStart = 1;
+ while ( nVisStart < mvData.size() && mvData[nVisStart].pPattern->IsVisibleEqual(*mvData[nVisStart-1].pPattern) )
+ ++nVisStart;
+ if ( nVisStart >= mvData.size() || mvData[nVisStart-1].nEndRow > 0 ) // more than 1 row?
+ nStart = nVisStart;
+
+ while ( nStart < mvData.size() && !bFound )
+ {
+ if ( mvData[nStart].pPattern->IsVisible() )
+ {
+ rFirstRow = nStart ? ( mvData[nStart-1].nEndRow + 1 ) : 0;
+ bFound = true;
+ }
+ else
+ ++nStart;
+ }
+
+ return bFound;
+}
+
+// size (rows) of a range of attributes after cell content where the search is stopped
+// (more than a default page size, 2*42 because it's as good as any number)
+
+const SCROW SC_VISATTR_STOP = 84;
+
+bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData, bool bSkipEmpty ) const
+{
+ if ( mvData.empty() )
+ {
+ rLastRow = nLastData;
+ return false;
+ }
+
+ // #i30830# changed behavior:
+ // ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows
+ // below the last content cell
+
+ if ( nLastData == rDocument.MaxRow() )
+ {
+ rLastRow = rDocument.MaxRow(); // can't look for attributes below rDocument.MaxRow()
+ return true;
+ }
+
+ // Quick check: last data row in or immediately preceding a run that is the
+ // last attribution down to the end, e.g. default style or column style.
+ SCSIZE nPos = mvData.size() - 1;
+ SCROW nStartRow = (nPos ? mvData[nPos-1].nEndRow + 1 : 0);
+ if (nStartRow <= nLastData + 1)
+ {
+ // Ignore here a few rows if data happens to end within
+ // SC_VISATTR_STOP rows before rDocument.MaxRow().
+ rLastRow = nLastData;
+ return false;
+ }
+ // tdf#93315: If "Suppress output of empty pages" in Calc Options is not checked, show empty
+ // (containing only empty data cells) page in the document
+ bool bFound = false;
+ if (bSkipEmpty)
+ {
+ Search( nLastData, nPos );
+ while ( nPos < mvData.size() )
+ {
+ // find range of visually equal formats
+ SCSIZE nEndPos = nPos;
+ while ( nEndPos < mvData.size()-1 &&
+ mvData[nEndPos].pPattern->IsVisibleEqual(*mvData[nEndPos+1].pPattern))
+ ++nEndPos;
+ SCROW nAttrStartRow = ( nPos > 0 ) ? ( mvData[nPos-1].nEndRow + 1) : 0;
+ if ( nAttrStartRow <= nLastData )
+ nAttrStartRow = nLastData + 1;
+ SCROW nAttrSize = mvData[nEndPos].nEndRow + 1 - nAttrStartRow;
+ if ( nAttrSize >= SC_VISATTR_STOP )
+ break; // while, ignore this range and below
+ else if ( mvData[nEndPos].pPattern->IsVisible() )
+ {
+ rLastRow = mvData[nEndPos].nEndRow;
+ bFound = true;
+ }
+ nPos = nEndPos + 1;
+ }
+ }
+ else
+ {
+ if ((nPos > 0 && mvData[nPos-1].pPattern->IsVisible())
+ || (nPos > 1 && !mvData[nPos-1].pPattern->IsVisibleEqual(*mvData[nPos-2].pPattern)))
+ {
+ rLastRow = mvData[nPos-1].nEndRow;
+ return true;
+ }
+ rLastRow = nLastData;
+ }
+ return bFound;
+}
+
+bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( mvData.empty() )
+ return rDocument.GetDefPattern()->IsVisible();
+
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ SCROW nThisStart = nStartRow;
+ bool bFound = false;
+ while ( nIndex < mvData.size() && nThisStart <= nEndRow && !bFound )
+ {
+ if ( mvData[nIndex].pPattern->IsVisible() )
+ bFound = true;
+
+ nThisStart = mvData[nIndex].nEndRow + 1;
+ ++nIndex;
+ }
+
+ return bFound;
+}
+
+bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
+ SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( mvData.empty() && rOther.mvData.empty() )
+ {
+ const ScPatternAttr* pDefPattern1 = rDocument.GetDefPattern();
+ const ScPatternAttr* pDefPattern2 = rOther.rDocument.GetDefPattern();
+ return ( SfxPoolItem::areSame(pDefPattern1, pDefPattern2) || pDefPattern1->IsVisibleEqual( *pDefPattern2 ) );
+ }
+
+ {
+ const ScAttrArray* pNonDefault = nullptr;
+ const ScPatternAttr* pDefPattern = nullptr;
+ bool bDefNonDefCase = false;
+ if ( mvData.empty() && !rOther.mvData.empty() )
+ {
+ pNonDefault = &rOther;
+ pDefPattern = rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+ else if ( !mvData.empty() && rOther.mvData.empty() )
+ {
+ pNonDefault = this;
+ pDefPattern = rOther.rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+
+ if ( bDefNonDefCase )
+ {
+ bool bEqual = true;
+ SCSIZE nPos = 0;
+ if ( nStartRow > 0 )
+ pNonDefault->Search( nStartRow, nPos );
+
+ while ( nPos < pNonDefault->Count() && bEqual )
+ {
+ const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].pPattern;
+ bEqual = ( SfxPoolItem::areSame(pNonDefPattern, pDefPattern) ||
+ pNonDefPattern->IsVisibleEqual( *pDefPattern ) );
+
+ if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
+ ++nPos;
+ }
+ return bEqual;
+ }
+ }
+
+ bool bEqual = true;
+ SCSIZE nThisPos = 0;
+ SCSIZE nOtherPos = 0;
+ if ( nStartRow > 0 )
+ {
+ Search( nStartRow, nThisPos );
+ rOther.Search( nStartRow, nOtherPos );
+ }
+
+ while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
+ {
+ SCROW nThisRow = mvData[nThisPos].nEndRow;
+ SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
+ const ScPatternAttr* pThisPattern = mvData[nThisPos].pPattern;
+ const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].pPattern;
+ bEqual = ( SfxPoolItem::areSame(pThisPattern, pOtherPattern) ||
+ pThisPattern->IsVisibleEqual(*pOtherPattern) );
+
+ if ( nThisRow >= nOtherRow )
+ {
+ if ( nOtherRow >= nEndRow ) break;
+ ++nOtherPos;
+ }
+ if ( nThisRow <= nOtherRow )
+ {
+ if ( nThisRow >= nEndRow ) break;
+ ++nThisPos;
+ }
+ }
+
+ return bEqual;
+}
+
+bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
+{
+ // summarised with IsVisibleEqual
+ if ( mvData.empty() && rOther.mvData.empty() )
+ {
+ const ScPatternAttr* pDefPattern1 = rDocument.GetDefPattern();
+ const ScPatternAttr* pDefPattern2 = rOther.rDocument.GetDefPattern();
+ return SfxPoolItem::areSame(pDefPattern1, pDefPattern2);
+ }
+
+ {
+ const ScAttrArray* pNonDefault = nullptr;
+ const ScPatternAttr* pDefPattern = nullptr;
+ bool bDefNonDefCase = false;
+ if ( mvData.empty() && !rOther.mvData.empty() )
+ {
+ pNonDefault = &rOther;
+ pDefPattern = rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+ else if ( !mvData.empty() && rOther.mvData.empty() )
+ {
+ pNonDefault = this;
+ pDefPattern = rOther.rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+
+ if ( bDefNonDefCase )
+ {
+ bool bEqual = true;
+ SCSIZE nPos = 0;
+ if ( nStartRow > 0 )
+ pNonDefault->Search( nStartRow, nPos );
+
+ while ( nPos < pNonDefault->Count() && bEqual )
+ {
+ const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].pPattern;
+ bEqual = SfxPoolItem::areSame( pNonDefPattern, pDefPattern );
+
+ if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
+ ++nPos;
+ }
+ return bEqual;
+ }
+ }
+
+ bool bEqual = true;
+ SCSIZE nThisPos = 0;
+ SCSIZE nOtherPos = 0;
+ if ( nStartRow > 0 )
+ {
+ Search( nStartRow, nThisPos );
+ rOther.Search( nStartRow, nOtherPos );
+ }
+
+ while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
+ {
+ SCROW nThisRow = mvData[nThisPos].nEndRow;
+ SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
+ const ScPatternAttr* pThisPattern = mvData[nThisPos].pPattern;
+ const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].pPattern;
+ bEqual = SfxPoolItem::areSame( pThisPattern, pOtherPattern );
+
+ if ( nThisRow >= nOtherRow )
+ {
+ if ( nOtherRow >= nEndRow ) break;
+ ++nOtherPos;
+ }
+ if ( nThisRow <= nOtherRow )
+ {
+ if ( nThisRow >= nEndRow ) break;
+ ++nThisPos;
+ }
+ }
+
+ return bEqual;
+}
+
+bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
+{
+ // Horizontal aggregate are not allowed to be moved out; if whole summary,
+ // here is not recognized
+
+ bool bTest = true;
+ if (!IsEmpty())
+ {
+ SCSIZE nIndex = 0;
+ if ( nStartRow > 0 )
+ Search( nStartRow, nIndex );
+
+ for ( ; nIndex < mvData.size(); nIndex++ )
+ {
+ if ( mvData[nIndex].pPattern->
+ GetItem(ATTR_MERGE_FLAG).IsHorOverlapped() )
+ {
+ bTest = false; // may not be pushed out
+ break;
+ }
+ if ( mvData[nIndex].nEndRow >= nEndRow ) // end of range
+ break;
+ }
+ }
+ return bTest;
+}
+
+bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
+{
+ // if 1st row pushed out is vertically overlapped, summary would be broken
+
+ // rDocument.MaxRow() + 1 - nSize = 1st row pushed out
+
+ if ( mvData.empty() )
+ return !rDocument.GetDefPattern()->
+ GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
+
+ SCSIZE nFirstLost = mvData.size()-1;
+ while ( nFirstLost && mvData[nFirstLost-1].nEndRow >= sal::static_int_cast<SCROW>(rDocument.MaxRow() + 1 - nSize) )
+ --nFirstLost;
+
+ return !mvData[nFirstLost].pPattern->
+ GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
+}
+
+void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
+{
+ SetDefaultIfNotInit();
+
+ SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // expand predecessor
+ SCSIZE nIndex;
+ Search( nSearch, nIndex );
+
+ // set ScMergeAttr may not be extended (so behind delete again)
+
+ bool bDoMerge = mvData[nIndex].pPattern->GetItem(ATTR_MERGE).IsMerged();
+
+ assert( !bDoMerge || nCol != -1 );
+
+ SCSIZE nRemove = 0;
+ SCSIZE i;
+ for (i = nIndex; i < mvData.size()-1; i++)
+ {
+ SCROW nNew = mvData[i].nEndRow + nSize;
+ if ( nNew >= rDocument.MaxRow() ) // at end?
+ {
+ nNew = rDocument.MaxRow();
+ if (!nRemove)
+ nRemove = i+1; // remove the following?
+ }
+ mvData[i].nEndRow = nNew;
+ }
+
+ // Remove entries at end ?
+
+ if (nRemove && nRemove < mvData.size())
+ DeleteRange( nRemove, mvData.size()-1 );
+
+ if (bDoMerge) // extensively repair (again) ScMergeAttr
+ {
+ // ApplyAttr for areas
+
+ const SfxPoolItem& rDef = rDocument.GetPool()->GetDefaultItem( ATTR_MERGE );
+ for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
+ rDocument.ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
+
+ // reply inserts in this area not summarized
+ }
+
+ // Don't duplicate the merge flags in the inserted row.
+ // #i108488# ScMF::Scenario has to be allowed.
+ RemoveFlags( nStartRow, nStartRow+nSize-1, ScMF::Hor | ScMF::Ver | ScMF::Auto | ScMF::Button );
+}
+
+void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
+{
+ SetDefaultIfNotInit();
+ bool bFirst=true;
+ SCSIZE nStartIndex = 0;
+ SCSIZE nEndIndex = 0;
+ SCSIZE i;
+
+ for ( i = 0; i < mvData.size()-1; i++)
+ if (mvData[i].nEndRow >= nStartRow && mvData[i].nEndRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
+ {
+ if (bFirst)
+ {
+ nStartIndex = i;
+ bFirst = false;
+ }
+ nEndIndex = i;
+ }
+ if (!bFirst)
+ {
+ SCROW nStart;
+ if (nStartIndex==0)
+ nStart = 0;
+ else
+ nStart = mvData[nStartIndex-1].nEndRow + 1;
+
+ if (nStart < nStartRow)
+ {
+ mvData[nStartIndex].nEndRow = nStartRow - 1;
+ ++nStartIndex;
+ }
+ if (nEndIndex >= nStartIndex)
+ {
+ DeleteRange( nStartIndex, nEndIndex );
+ if (nStartIndex > 0)
+ if ( SfxPoolItem::areSame( mvData[nStartIndex-1].pPattern, mvData[nStartIndex].pPattern ) )
+ DeleteRange( nStartIndex-1, nStartIndex-1 );
+ }
+ }
+ for (i = 0; i < mvData.size()-1; i++)
+ if (mvData[i].nEndRow >= nStartRow)
+ mvData[i].nEndRow -= nSize;
+
+ // Below does not follow the pattern to detect pressure ranges;
+ // instead, only remove merge flags.
+ RemoveFlags( rDocument.MaxRow()-nSize+1, rDocument.MaxRow(), ScMF::Hor | ScMF::Ver | ScMF::Auto );
+}
+
+void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
+{
+ SetDefaultIfNotInit();
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ for (SCSIZE i = nStartIndex; i <= nEndIndex; i++)
+ pDocPool->DirectRemoveItemFromPool(*mvData[i].pPattern);
+
+ mvData.erase(mvData.begin() + nStartIndex, mvData.begin() + nEndIndex + 1);
+}
+
+void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
+{
+ SetDefaultIfNotInit();
+ if ( nCol != -1 )
+ RemoveAreaMerge( nStartRow, nEndRow ); // remove from combined flags
+
+ if ( !HasAttrib( nStartRow, nEndRow, HasAttrFlags::Overlapped | HasAttrFlags::AutoFilter) )
+ SetPatternArea( nStartRow, nEndRow, rDocument.GetDefPattern() );
+ else
+ SetPatternAreaSafe( nStartRow, nEndRow, rDocument.GetDefPattern(), true ); // leave merge flags
+}
+
+void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pDefPattern = rDocument.GetDefPattern();
+
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
+
+ if ( pOldPattern->GetItemSet().Count() ) // hard attributes ?
+ {
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ SfxItemSet& rSet = pNewPattern->GetItemSet();
+ for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
+ if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
+ rSet.ClearItem(nId);
+
+ if ( *pNewPattern == *pDefPattern )
+ SetPatternArea( nThisRow, nAttrRow, pDefPattern );
+ else
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+
+ Search( nThisRow, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+}
+
+/**
+ * Move within a document
+ */
+void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
+{
+ SetDefaultIfNotInit();
+ SCROW nStart = nStartRow;
+ for (SCSIZE i = 0; i < mvData.size(); i++)
+ {
+ if ((mvData[i].nEndRow >= nStartRow) && (i == 0 || mvData[i-1].nEndRow < nEndRow))
+ {
+ // copy (bPutToPool=TRUE)
+ rAttrArray.SetPatternArea( nStart, std::min( mvData[i].nEndRow, nEndRow ),
+ mvData[i].pPattern, true );
+ }
+ nStart = std::max( nStart, mvData[i].nEndRow + 1 );
+ }
+ DeleteArea(nStartRow, nEndRow);
+}
+
+/**
+ * Copy between documents (Clipboard)
+ */
+void ScAttrArray::CopyArea(
+ SCROW nStartRow, SCROW nEndRow, tools::Long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags) const
+{
+ nStartRow -= nDy; // Source
+ nEndRow -= nDy;
+
+ SCROW nDestStart = std::max(static_cast<tools::Long>(static_cast<tools::Long>(nStartRow) + nDy), tools::Long(0));
+ SCROW nDestEnd = std::min(static_cast<tools::Long>(static_cast<tools::Long>(nEndRow) + nDy), tools::Long(rDocument.MaxRow()));
+
+ ScDocumentPool* pSourceDocPool = rDocument.GetPool();
+ ScDocumentPool* pDestDocPool = rAttrArray.rDocument.GetPool();
+ bool bSamePool = (pSourceDocPool==pDestDocPool);
+
+ if ( mvData.empty() )
+ {
+ const ScPatternAttr* pNewPattern = &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
+ rAttrArray.SetPatternArea(nDestStart, nDestEnd, pNewPattern);
+ return;
+ }
+
+ for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
+ {
+ if (mvData[i].nEndRow >= nStartRow)
+ {
+ const ScPatternAttr* pOldPattern = mvData[i].pPattern;
+ const ScPatternAttr* pNewPattern;
+
+ if (IsDefaultItem( pOldPattern ))
+ {
+ // default: nothing changed
+
+ pNewPattern = &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
+ }
+ else if ( nStripFlags != ScMF::NONE )
+ {
+ ScPatternAttr aTmpPattern( *pOldPattern );
+ ScMF nNewFlags = ScMF::NONE;
+ if ( nStripFlags != ScMF::All )
+ nNewFlags = aTmpPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & ~nStripFlags;
+
+ if ( nNewFlags != ScMF::NONE )
+ aTmpPattern.GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
+ else
+ aTmpPattern.GetItemSet().ClearItem( ATTR_MERGE_FLAG );
+
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->DirectPutItemInPool(aTmpPattern);
+ else
+ pNewPattern = aTmpPattern.PutInPool( &rAttrArray.rDocument, &rDocument );
+ }
+ else
+ {
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->DirectPutItemInPool(*pOldPattern);
+ else
+ pNewPattern = pOldPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
+ }
+
+ rAttrArray.SetPatternArea(nDestStart,
+ std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), pNewPattern);
+ }
+
+ // when pasting from clipboard and skipping filtered rows, the adjusted
+ // end position can be negative
+ nDestStart = std::max(static_cast<tools::Long>(nDestStart), static_cast<tools::Long>(mvData[i].nEndRow + nDy + 1));
+ }
+}
+
+/**
+ * Leave flags
+ * summarized with CopyArea
+ */
+void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, tools::Long nDy, ScAttrArray& rAttrArray )
+{
+ nStartRow -= nDy; // Source
+ nEndRow -= nDy;
+
+ SCROW nDestStart = std::max(static_cast<tools::Long>(static_cast<tools::Long>(nStartRow) + nDy), tools::Long(0));
+ SCROW nDestEnd = std::min(static_cast<tools::Long>(static_cast<tools::Long>(nEndRow) + nDy), tools::Long(rDocument.MaxRow()));
+
+ if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HasAttrFlags::Overlapped ) )
+ {
+ CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
+ return;
+ }
+
+ ScDocumentPool* pSourceDocPool = rDocument.GetPool();
+ ScDocumentPool* pDestDocPool = rAttrArray.rDocument.GetPool();
+ bool bSamePool = (pSourceDocPool==pDestDocPool);
+
+ if ( mvData.empty() )
+ {
+ const ScPatternAttr* pNewPattern;
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->DirectPutItemInPool(*rDocument.GetDefPattern());
+ else
+ pNewPattern = rDocument.GetDefPattern()->PutInPool( &rAttrArray.rDocument, &rDocument );
+
+ rAttrArray.SetPatternAreaSafe(nDestStart, nDestEnd, pNewPattern, false);
+ return;
+ }
+
+
+ for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
+ {
+ if (mvData[i].nEndRow >= nStartRow)
+ {
+ const ScPatternAttr* pOldPattern = mvData[i].pPattern;
+ const ScPatternAttr* pNewPattern;
+
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->DirectPutItemInPool(*pOldPattern);
+ else
+ pNewPattern = pOldPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
+
+ rAttrArray.SetPatternAreaSafe(nDestStart,
+ std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), pNewPattern, false);
+ }
+
+ // when pasting from clipboard and skipping filtered rows, the adjusted
+ // end position can be negative
+ nDestStart = std::max(static_cast<tools::Long>(nDestStart), static_cast<tools::Long>(mvData[i].nEndRow + nDy + 1));
+ }
+}
+
+SCROW ScAttrArray::SearchStyle(
+ SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp,
+ const ScMarkArray* pMarkArray) const
+{
+ bool bFound = false;
+
+ if (pMarkArray)
+ {
+ nRow = pMarkArray->GetNextMarked( nRow, bUp );
+ if (!rDocument.ValidRow(nRow))
+ return nRow;
+ }
+
+ if ( mvData.empty() )
+ {
+ if (rDocument.GetDefPattern()->GetStyleSheet() == pSearchStyle)
+ return nRow;
+
+ nRow = bUp ? -1 : rDocument.MaxRow() + 1;
+ return nRow;
+ }
+
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ const ScPatternAttr* pPattern = mvData[nIndex].pPattern;
+
+ while (nIndex < mvData.size() && !bFound)
+ {
+ if (pPattern->GetStyleSheet() == pSearchStyle)
+ {
+ if (pMarkArray)
+ {
+ nRow = pMarkArray->GetNextMarked( nRow, bUp );
+ SCROW nStart = nIndex ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nRow >= nStart && nRow <= mvData[nIndex].nEndRow)
+ bFound = true;
+ }
+ else
+ bFound = true;
+ }
+
+ if (!bFound)
+ {
+ if (bUp)
+ {
+ if (nIndex==0)
+ {
+ nIndex = mvData.size();
+ nRow = -1;
+ }
+ else
+ {
+ --nIndex;
+ nRow = mvData[nIndex].nEndRow;
+ pPattern = mvData[nIndex].pPattern;
+ }
+ }
+ else
+ {
+ nRow = mvData[nIndex].nEndRow+1;
+ ++nIndex;
+ if (nIndex<mvData.size())
+ pPattern = mvData[nIndex].pPattern;
+ }
+ }
+ }
+
+ OSL_ENSURE( bFound || !rDocument.ValidRow(nRow), "Internal failure in ScAttrArray::SearchStyle" );
+
+ return nRow;
+}
+
+bool ScAttrArray::SearchStyleRange(
+ SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
+ const ScMarkArray* pMarkArray) const
+{
+ SCROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
+ if (rDocument.ValidRow(nStartRow))
+ {
+ if ( mvData.empty() )
+ {
+ rRow = nStartRow;
+ if (bUp)
+ {
+ rEndRow = 0;
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
+ if (nMarkEnd>rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+ else
+ {
+ rEndRow = rDocument.MaxRow();
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
+ if (nMarkEnd<rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+
+ return true;
+ }
+
+ SCSIZE nIndex;
+ Search(nStartRow,nIndex);
+
+ rRow = nStartRow;
+ if (bUp)
+ {
+ if (nIndex>0)
+ rEndRow = mvData[nIndex-1].nEndRow + 1;
+ else
+ rEndRow = 0;
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
+ if (nMarkEnd>rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+ else
+ {
+ rEndRow = mvData[nIndex].nEndRow;
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
+ if (nMarkEnd<rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+
+ return true;
+ }
+ else
+ return false;
+}
+
+SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( mvData.empty() )
+ return 1;
+
+ SCSIZE nIndex1, nIndex2;
+
+ if( !Search( nStartRow, nIndex1 ) )
+ return 0;
+
+ if( !Search( nEndRow, nIndex2 ) )
+ nIndex2 = mvData.size() - 1;
+
+ return nIndex2 - nIndex1 + 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/attrib.cxx b/sc/source/core/data/attrib.cxx
new file mode 100644
index 0000000000..1a587d2b65
--- /dev/null
+++ b/sc/source/core/data/attrib.cxx
@@ -0,0 +1,885 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/util/CellProtection.hpp>
+
+#include <scitems.hxx>
+
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/itemtype.hxx>
+#include <svl/itempool.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <attrib.hxx>
+#include <global.hxx>
+#include <editutil.hxx>
+#include <mid.h>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <textuno.hxx>
+
+using namespace com::sun::star;
+
+
+SfxPoolItem* ScProtectionAttr::CreateDefault() { return new ScProtectionAttr; }
+
+/**
+ * General Help Function
+ */
+bool ScHasPriority( const ::editeng::SvxBorderLine* pThis, const ::editeng::SvxBorderLine* pOther )
+{
+
+ if (!pThis)
+ return false;
+ if (!pOther)
+ 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; // FIXME: What is this?
+ }
+ }
+}
+
+/** Item - Implementations */
+
+/**
+ * Merge
+ */
+ScMergeAttr::ScMergeAttr():
+ SfxPoolItem(ATTR_MERGE),
+ nColMerge(0),
+ nRowMerge(0)
+{}
+
+ScMergeAttr::ScMergeAttr( SCCOL nCol, SCROW nRow):
+ SfxPoolItem(ATTR_MERGE),
+ nColMerge(nCol),
+ nRowMerge(nRow)
+{}
+
+ScMergeAttr::ScMergeAttr(const ScMergeAttr& rItem):
+ SfxPoolItem(ATTR_MERGE)
+{
+ nColMerge = rItem.nColMerge;
+ nRowMerge = rItem.nRowMerge;
+}
+
+ScMergeAttr::~ScMergeAttr()
+{
+}
+
+bool ScMergeAttr::operator==( const SfxPoolItem& rItem ) const
+{
+ return SfxPoolItem::operator==(rItem)
+ && (nColMerge == static_cast<const ScMergeAttr&>(rItem).nColMerge)
+ && (nRowMerge == static_cast<const ScMergeAttr&>(rItem).nRowMerge);
+}
+
+ScMergeAttr* ScMergeAttr::Clone( SfxItemPool * ) const
+{
+ return new ScMergeAttr(*this);
+}
+
+void ScMergeAttr::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScMergeAttr"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("col-merge"), BAD_CAST(OString::number(GetColMerge()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("row-merge"), BAD_CAST(OString::number(GetRowMerge()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("merged"), BAD_CAST(OString::boolean(IsMerged()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * MergeFlag
+ */
+ScMergeFlagAttr::ScMergeFlagAttr():
+ SfxInt16Item(ATTR_MERGE_FLAG, 0)
+{
+}
+
+ScMergeFlagAttr::ScMergeFlagAttr(ScMF nFlags):
+ SfxInt16Item(ATTR_MERGE_FLAG, static_cast<sal_Int16>(nFlags))
+{
+}
+
+ScMergeFlagAttr::~ScMergeFlagAttr()
+{
+}
+
+ScMergeFlagAttr* ScMergeFlagAttr::Clone(SfxItemPool *) const
+{
+ return new ScMergeFlagAttr(*this);
+}
+
+bool ScMergeFlagAttr::HasPivotButton() const
+{
+ return bool(GetValue() & ScMF::Button);
+}
+
+bool ScMergeFlagAttr::HasPivotPopupButton() const
+{
+ return bool(GetValue() & ScMF::ButtonPopup);
+}
+
+bool ScMergeFlagAttr::HasPivotToggle() const
+{
+ auto nFlags = GetValue();
+ return (nFlags & ScMF::DpCollapse) || (nFlags & ScMF::DpExpand);
+}
+
+bool ScMergeFlagAttr::HasPivotMultiFieldPopupButton() const
+{
+ return bool(GetValue() & ScMF::ButtonPopup2);
+}
+
+void ScMergeFlagAttr::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScMergeFlagAttr"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("overlapped"), BAD_CAST(OString::boolean(IsOverlapped()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("hor_overlapped"), BAD_CAST(OString::boolean(IsHorOverlapped()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("ver_overlapped"), BAD_CAST(OString::boolean(IsVerOverlapped()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("autofilter"), BAD_CAST(OString::boolean(HasAutoFilter()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("scenario"), BAD_CAST(OString::boolean(IsScenario()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pivot-button"), BAD_CAST(OString::boolean(HasPivotButton()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pivot-popup-button"), BAD_CAST(OString::boolean(HasPivotPopupButton()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * Protection
+ */
+ScProtectionAttr::ScProtectionAttr():
+ SfxPoolItem(ATTR_PROTECTION),
+ bProtection(true),
+ bHideFormula(false),
+ bHideCell(false),
+ bHidePrint(false)
+{
+}
+
+ScProtectionAttr::ScProtectionAttr( bool bProtect, bool bHFormula,
+ bool bHCell, bool bHPrint):
+ SfxPoolItem(ATTR_PROTECTION),
+ bProtection(bProtect),
+ bHideFormula(bHFormula),
+ bHideCell(bHCell),
+ bHidePrint(bHPrint)
+{
+}
+
+ScProtectionAttr::ScProtectionAttr(const ScProtectionAttr& rItem):
+ SfxPoolItem(ATTR_PROTECTION)
+{
+ bProtection = rItem.bProtection;
+ bHideFormula = rItem.bHideFormula;
+ bHideCell = rItem.bHideCell;
+ bHidePrint = rItem.bHidePrint;
+}
+
+ScProtectionAttr::~ScProtectionAttr()
+{
+}
+
+bool ScProtectionAttr::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ util::CellProtection aProtection;
+ aProtection.IsLocked = bProtection;
+ aProtection.IsFormulaHidden = bHideFormula;
+ aProtection.IsHidden = bHideCell;
+ aProtection.IsPrintHidden = bHidePrint;
+ rVal <<= aProtection;
+ break;
+ }
+ case MID_1 :
+ rVal <<= bProtection; break;
+ case MID_2 :
+ rVal <<= bHideFormula; break;
+ case MID_3 :
+ rVal <<= bHideCell; break;
+ case MID_4 :
+ rVal <<= bHidePrint; break;
+ default:
+ OSL_FAIL("Wrong MemberID!");
+ return false;
+ }
+
+ return true;
+}
+
+bool ScProtectionAttr::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = false;
+ bool bVal = false;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ util::CellProtection aProtection;
+ if ( rVal >>= aProtection )
+ {
+ bProtection = aProtection.IsLocked;
+ bHideFormula = aProtection.IsFormulaHidden;
+ bHideCell = aProtection.IsHidden;
+ bHidePrint = aProtection.IsPrintHidden;
+ bRet = true;
+ }
+ else
+ {
+ OSL_FAIL("exception - wrong argument");
+ }
+ break;
+ }
+ case MID_1 :
+ bRet = (rVal >>= bVal); if (bRet) bProtection=bVal; break;
+ case MID_2 :
+ bRet = (rVal >>= bVal); if (bRet) bHideFormula=bVal; break;
+ case MID_3 :
+ bRet = (rVal >>= bVal); if (bRet) bHideCell=bVal; break;
+ case MID_4 :
+ bRet = (rVal >>= bVal); if (bRet) bHidePrint=bVal; break;
+ default:
+ OSL_FAIL("Wrong MemberID!");
+ }
+
+ return bRet;
+}
+
+OUString ScProtectionAttr::GetValueText() const
+{
+ const OUString aStrYes ( ScResId(STR_YES) );
+ const OUString aStrNo ( ScResId(STR_NO) );
+
+ const OUString aValue = "("
+ + (bProtection ? aStrYes : aStrNo)
+ + ","
+ + (bHideFormula ? aStrYes : aStrNo)
+ + ","
+ + (bHideCell ? aStrYes : aStrNo)
+ + ","
+ + (bHidePrint ? aStrYes : aStrNo)
+ + ")";
+
+ return aValue;
+}
+
+bool ScProtectionAttr::GetPresentation
+ (
+ SfxItemPresentation ePres,
+ MapUnit /* eCoreMetric */,
+ MapUnit /* ePresMetric */,
+ OUString& rText,
+ const IntlWrapper& /* rIntl */
+ ) const
+{
+ const OUString aStrYes ( ScResId(STR_YES) );
+ const OUString aStrNo ( ScResId(STR_NO) );
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = GetValueText();
+ break;
+
+ case SfxItemPresentation::Complete:
+ rText = ScResId(STR_PROTECTION)
+ + ": "
+ + (bProtection ? aStrYes : aStrNo)
+ + ", "
+ + ScResId(STR_FORMULAS)
+ + ": "
+ + (!bHideFormula ? aStrYes : aStrNo)
+ + ", "
+ + ScResId(STR_HIDE)
+ + ": "
+ + (bHideCell ? aStrYes : aStrNo)
+ + ", "
+ + ScResId(STR_PRINT)
+ + ": "
+ + (!bHidePrint ? aStrYes : aStrNo);
+ break;
+
+ default: break;
+ }
+
+ return true;
+}
+
+bool ScProtectionAttr::operator==( const SfxPoolItem& rItem ) const
+{
+ return SfxPoolItem::operator==(rItem)
+ && (bProtection == static_cast<const ScProtectionAttr&>(rItem).bProtection)
+ && (bHideFormula == static_cast<const ScProtectionAttr&>(rItem).bHideFormula)
+ && (bHideCell == static_cast<const ScProtectionAttr&>(rItem).bHideCell)
+ && (bHidePrint == static_cast<const ScProtectionAttr&>(rItem).bHidePrint);
+}
+
+ScProtectionAttr* ScProtectionAttr::Clone( SfxItemPool * ) const
+{
+ return new ScProtectionAttr(*this);
+}
+
+void ScProtectionAttr::SetProtection( bool bProtect)
+{
+ bProtection = bProtect;
+}
+
+void ScProtectionAttr::SetHideFormula( bool bHFormula)
+{
+ bHideFormula = bHFormula;
+}
+
+void ScProtectionAttr::SetHideCell( bool bHCell)
+{
+ bHideCell = bHCell;
+}
+
+void ScProtectionAttr::SetHidePrint( bool bHPrint)
+{
+ bHidePrint = bHPrint;
+}
+
+void ScProtectionAttr::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScProtectionAttr"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("protection"), BAD_CAST(OString::boolean(GetProtection()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("hide-formula"), BAD_CAST(OString::boolean(GetHideFormula()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("hide-cell"), BAD_CAST(OString::boolean(GetHideCell()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("hide-print"), BAD_CAST(OString::boolean(GetHidePrint()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * ScPageHFItem - Dates from the Head and Foot lines
+ */
+ScPageHFItem::ScPageHFItem( sal_uInt16 nWhichP )
+ : SfxPoolItem ( nWhichP )
+{
+}
+
+ScPageHFItem::ScPageHFItem( const ScPageHFItem& rItem )
+ : SfxPoolItem ( rItem )
+{
+ if ( rItem.pLeftArea )
+ pLeftArea = rItem.pLeftArea->Clone();
+ if ( rItem.pCenterArea )
+ pCenterArea = rItem.pCenterArea->Clone();
+ if ( rItem.pRightArea )
+ pRightArea = rItem.pRightArea->Clone();
+}
+
+ScPageHFItem::~ScPageHFItem()
+{
+}
+
+bool ScPageHFItem::QueryValue( uno::Any& rVal, sal_uInt8 /* nMemberId */ ) const
+{
+ rtl::Reference<ScHeaderFooterContentObj> xContent =
+ new ScHeaderFooterContentObj();
+ xContent->Init(pLeftArea.get(), pCenterArea.get(), pRightArea.get());
+
+ uno::Reference<sheet::XHeaderFooterContent> xCont(xContent);
+
+ rVal <<= xCont;
+ return true;
+}
+
+bool ScPageHFItem::PutValue( const uno::Any& rVal, sal_uInt8 /* nMemberId */ )
+{
+ bool bRet = false;
+ uno::Reference<sheet::XHeaderFooterContent> xContent;
+ if ( rVal >>= xContent )
+ {
+ if ( xContent.is() )
+ {
+ rtl::Reference<ScHeaderFooterContentObj> pImp =
+ ScHeaderFooterContentObj::getImplementation( xContent );
+ if (pImp.is())
+ {
+ const EditTextObject* pImpLeft = pImp->GetLeftEditObject();
+ pLeftArea.reset();
+ if (pImpLeft)
+ pLeftArea = pImpLeft->Clone();
+
+ const EditTextObject* pImpCenter = pImp->GetCenterEditObject();
+ pCenterArea.reset();
+ if (pImpCenter)
+ pCenterArea = pImpCenter->Clone();
+
+ const EditTextObject* pImpRight = pImp->GetRightEditObject();
+ pRightArea.reset();
+ if (pImpRight)
+ pRightArea = pImpRight->Clone();
+
+ if ( !pLeftArea || !pCenterArea || !pRightArea )
+ {
+ // no Text with Null are left
+ ScEditEngineDefaulter aEngine( EditEngine::CreatePool().get(), true );
+ if (!pLeftArea)
+ pLeftArea = aEngine.CreateTextObject();
+ if (!pCenterArea)
+ pCenterArea = aEngine.CreateTextObject();
+ if (!pRightArea)
+ pRightArea = aEngine.CreateTextObject();
+ }
+
+ bRet = true;
+ }
+ }
+ }
+
+ if (!bRet)
+ {
+ OSL_FAIL("exception - wrong argument");
+ }
+
+ return true;
+}
+
+bool ScPageHFItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScPageHFItem& r = static_cast<const ScPageHFItem&>(rItem);
+
+ return ScGlobal::EETextObjEqual(pLeftArea.get(), r.pLeftArea.get())
+ && ScGlobal::EETextObjEqual(pCenterArea.get(), r.pCenterArea.get())
+ && ScGlobal::EETextObjEqual(pRightArea.get(), r.pRightArea.get());
+}
+
+ScPageHFItem* ScPageHFItem::Clone( SfxItemPool* ) const
+{
+ return new ScPageHFItem( *this );
+}
+
+void ScPageHFItem::SetLeftArea( const EditTextObject& rNew )
+{
+ pLeftArea = rNew.Clone();
+}
+
+void ScPageHFItem::SetCenterArea( const EditTextObject& rNew )
+{
+ pCenterArea = rNew.Clone();
+}
+
+void ScPageHFItem::SetRightArea( const EditTextObject& rNew )
+{
+ pRightArea = rNew.Clone();
+}
+void ScPageHFItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScPageHFItem"));
+ GetLeftArea()->dumpAsXml(pWriter);
+ GetCenterArea()->dumpAsXml(pWriter);
+ GetRightArea()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * ScViewObjectModeItem - Display Mode of View Objects
+ */
+ScViewObjectModeItem::ScViewObjectModeItem( sal_uInt16 nWhichP )
+ : SfxEnumItem( nWhichP, VOBJ_MODE_SHOW )
+{
+}
+
+ScViewObjectModeItem::ScViewObjectModeItem( sal_uInt16 nWhichP, ScVObjMode eMode )
+ : SfxEnumItem( nWhichP, eMode )
+{
+}
+
+ScViewObjectModeItem::~ScViewObjectModeItem()
+{
+}
+
+bool ScViewObjectModeItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /* eCoreUnit */,
+ MapUnit /* ePresUnit */,
+ OUString& rText,
+ const IntlWrapper& /* rIntl */
+) const
+{
+ OUString aDel(": ");
+ rText.clear();
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Complete:
+ switch( Which() )
+ {
+ case SID_SCATTR_PAGE_CHARTS:
+ rText = ScResId(STR_VOBJ_CHART) + aDel;
+ break;
+
+ case SID_SCATTR_PAGE_OBJECTS:
+ rText = ScResId(STR_VOBJ_OBJECT) + aDel;
+ break;
+
+ case SID_SCATTR_PAGE_DRAWINGS:
+ rText = ScResId(STR_VOBJ_DRAWINGS) + aDel;
+ break;
+
+ default: break;
+ }
+ [[fallthrough]];
+ case SfxItemPresentation::Nameless:
+ if (GetValue() == VOBJ_MODE_SHOW)
+ rText += ScResId(STR_VOBJ_MODE_SHOW);
+ else
+ rText += ScResId(STR_VOBJ_MODE_HIDE);
+ return true;
+
+ default: break;
+ // added to avoid warnings
+ }
+
+ return false;
+}
+
+sal_uInt16 ScViewObjectModeItem::GetValueCount() const
+{
+ return 2;
+}
+
+ScViewObjectModeItem* ScViewObjectModeItem::Clone( SfxItemPool* ) const
+{
+ return new ScViewObjectModeItem( *this );
+}
+
+ScPageScaleToItem::ScPageScaleToItem() :
+ SfxPoolItem( ATTR_PAGE_SCALETO ),
+ mnWidth( 0 ),
+ mnHeight( 0 )
+{
+}
+
+ScPageScaleToItem::ScPageScaleToItem( sal_uInt16 nWidth, sal_uInt16 nHeight ) :
+ SfxPoolItem( ATTR_PAGE_SCALETO ),
+ mnWidth( nWidth ),
+ mnHeight( nHeight )
+{
+}
+
+ScPageScaleToItem::~ScPageScaleToItem()
+{
+}
+
+ScPageScaleToItem* ScPageScaleToItem::Clone( SfxItemPool* ) const
+{
+ return new ScPageScaleToItem( *this );
+}
+
+bool ScPageScaleToItem::operator==( const SfxPoolItem& rCmp ) const
+{
+ assert(SfxPoolItem::operator==(rCmp));
+ const ScPageScaleToItem& rPageCmp = static_cast< const ScPageScaleToItem& >( rCmp );
+ return (mnWidth == rPageCmp.mnWidth) && (mnHeight == rPageCmp.mnHeight);
+}
+
+namespace {
+void lclAppendScalePageCount( OUString& rText, sal_uInt16 nPages )
+{
+ rText += ": ";
+ if( nPages )
+ {
+ OUString aPages(ScResId(STR_SCATTR_PAGE_SCALE_PAGES, nPages));
+ rText += aPages.replaceFirst( "%1", OUString::number( nPages ) );
+ }
+ else
+ rText += ScResId( STR_SCATTR_PAGE_SCALE_AUTO );
+}
+} // namespace
+
+bool ScPageScaleToItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit, MapUnit, OUString& rText, const IntlWrapper& ) const
+{
+ rText.clear();
+ if( !IsValid())
+ return false;
+
+ OUString aName( ScResId( STR_SCATTR_PAGE_SCALETO ) );
+ OUString aValue( ScResId( STR_SCATTR_PAGE_SCALE_WIDTH ) );
+ lclAppendScalePageCount( aValue, mnWidth );
+ aValue += ", " + ScResId( STR_SCATTR_PAGE_SCALE_HEIGHT );
+ lclAppendScalePageCount( aValue, mnHeight );
+
+ switch( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = aValue;
+ return true;
+
+ case SfxItemPresentation::Complete:
+ rText = aName + " (" + aValue + ")";
+ return true;
+
+ default:
+ OSL_FAIL( "ScPageScaleToItem::GetPresentation - unknown presentation mode" );
+ }
+ return false;
+}
+
+bool ScPageScaleToItem::QueryValue( uno::Any& rAny, sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+ switch( nMemberId )
+ {
+ case SC_MID_PAGE_SCALETO_WIDTH: rAny <<= mnWidth; break;
+ case SC_MID_PAGE_SCALETO_HEIGHT: rAny <<= mnHeight; break;
+ default:
+ OSL_FAIL( "ScPageScaleToItem::QueryValue - unknown member ID" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool ScPageScaleToItem::PutValue( const uno::Any& rAny, sal_uInt8 nMemberId )
+{
+ bool bRet = false;
+ switch( nMemberId )
+ {
+ case SC_MID_PAGE_SCALETO_WIDTH: bRet = rAny >>= mnWidth; break;
+ case SC_MID_PAGE_SCALETO_HEIGHT: bRet = rAny >>= mnHeight; break;
+ default:
+ OSL_FAIL( "ScPageScaleToItem::PutValue - unknown member ID" );
+ }
+ return bRet;
+}
+void ScPageScaleToItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScPageScaleToItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("width"), BAD_CAST(OString::number(GetWidth()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("height"), BAD_CAST(OString::number(GetHeight()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+ScCondFormatItem::ScCondFormatItem():
+ SfxPoolItem( ATTR_CONDITIONAL )
+{
+}
+
+ScCondFormatItem::ScCondFormatItem( sal_uInt32 nIndex ):
+ SfxPoolItem( ATTR_CONDITIONAL )
+{
+ maIndex.insert(nIndex);
+}
+
+ScCondFormatItem::ScCondFormatItem( const ScCondFormatIndexes& rIndex ):
+ SfxPoolItem( ATTR_CONDITIONAL ),
+ maIndex( rIndex )
+{
+}
+
+ScCondFormatItem::ScCondFormatItem( ScCondFormatIndexes&& aIndex ) noexcept:
+ SfxPoolItem( ATTR_CONDITIONAL ),
+ maIndex( std::move(aIndex) )
+{
+}
+
+ScCondFormatItem::~ScCondFormatItem()
+{
+}
+
+bool ScCondFormatItem::operator==( const SfxPoolItem& rCmp ) const
+{
+ if (!SfxPoolItem::operator==(rCmp))
+ return false;
+ auto const & other = static_cast<const ScCondFormatItem&>(rCmp);
+ if (maIndex.empty() && other.maIndex.empty())
+ return true;
+ // memcmp is faster than operator== on std::vector
+ return maIndex.size() == other.maIndex.size()
+ && memcmp(&maIndex.front(), &other.maIndex.front(), maIndex.size() * sizeof(sal_uInt32)) == 0;
+}
+
+ScCondFormatItem* ScCondFormatItem::Clone(SfxItemPool*) const
+{
+ return new ScCondFormatItem(maIndex);
+}
+
+void ScCondFormatItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ScCondFormatItem"));
+ for (const auto& nItem : maIndex)
+ {
+ std::string aStrVal = std::to_string(nItem);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(aStrVal.c_str()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+ScRotateValueItem::ScRotateValueItem(Degree100 nAngle)
+ : SdrAngleItem(ATTR_ROTATE_VALUE, nAngle)
+{
+}
+
+ScRotateValueItem* ScRotateValueItem::Clone(SfxItemPool*) const
+{
+ return new ScRotateValueItem(GetValue());
+}
+
+bool ScRotateValueItem::GetPresentation(SfxItemPresentation ePresentation,
+ MapUnit eCoreMetric, MapUnit ePresMetric,
+ OUString& rText,
+ const IntlWrapper& rWrapper) const
+{
+ bool bRet = SdrAngleItem::GetPresentation(SfxItemPresentation::Nameless, eCoreMetric, ePresMetric, rText, rWrapper);
+ if (bRet && ePresentation == SfxItemPresentation::Complete)
+ rText = ScResId(STR_TEXTORIENTANGLE) + " " + rText;
+ return bRet;
+}
+
+ScShrinkToFitCell::ScShrinkToFitCell(bool bShrink)
+ : SfxBoolItem(ATTR_SHRINKTOFIT, bShrink)
+{
+}
+
+ScShrinkToFitCell* ScShrinkToFitCell::Clone(SfxItemPool*) const
+{
+ return new ScShrinkToFitCell(GetValue());
+}
+
+bool ScShrinkToFitCell::GetPresentation(SfxItemPresentation,
+ MapUnit, MapUnit,
+ OUString& rText,
+ const IntlWrapper&) const
+{
+ TranslateId pId = GetValue() ? STR_SHRINKTOFITCELL_ON : STR_SHRINKTOFITCELL_OFF;
+ rText = ScResId(pId);
+ return true;
+}
+
+ScVerticalStackCell::ScVerticalStackCell(bool bStack)
+ : SfxBoolItem(ATTR_STACKED, bStack)
+{
+}
+
+ScVerticalStackCell* ScVerticalStackCell::Clone(SfxItemPool*) const
+{
+ return new ScVerticalStackCell(GetValue());
+}
+
+bool ScVerticalStackCell::GetPresentation(SfxItemPresentation,
+ MapUnit, MapUnit,
+ OUString& rText,
+ const IntlWrapper&) const
+{
+ TranslateId pId = GetValue() ? STR_VERTICALSTACKCELL_ON : STR_VERTICALSTACKCELL_OFF;
+ rText = ScResId(pId);
+ return true;
+}
+
+ScLineBreakCell::ScLineBreakCell(bool bStack)
+ : SfxBoolItem(ATTR_LINEBREAK, bStack)
+{
+}
+
+ScLineBreakCell* ScLineBreakCell::Clone(SfxItemPool*) const
+{
+ return new ScLineBreakCell(GetValue());
+}
+
+bool ScLineBreakCell::GetPresentation(SfxItemPresentation,
+ MapUnit, MapUnit,
+ OUString& rText,
+ const IntlWrapper&) const
+{
+ TranslateId pId = GetValue() ? STR_LINEBREAKCELL_ON : STR_LINEBREAKCELL_OFF;
+ rText = ScResId(pId);
+ return true;
+}
+
+ScHyphenateCell::ScHyphenateCell(bool bHyphenate)
+ : SfxBoolItem(ATTR_HYPHENATE, bHyphenate)
+{
+}
+
+ScHyphenateCell* ScHyphenateCell::Clone(SfxItemPool*) const
+{
+ return new ScHyphenateCell(GetValue());
+}
+
+bool ScHyphenateCell::GetPresentation(SfxItemPresentation,
+ MapUnit, MapUnit,
+ OUString& rText,
+ const IntlWrapper&) const
+{
+ TranslateId pId = GetValue() ? STR_HYPHENATECELL_ON : STR_HYPHENATECELL_OFF;
+ rText = ScResId(pId);
+ return true;
+}
+
+ScIndentItem::ScIndentItem(sal_uInt16 nIndent)
+ : SfxUInt16Item(ATTR_INDENT, nIndent)
+{
+}
+
+ScIndentItem* ScIndentItem::Clone(SfxItemPool*) const
+{
+ return new ScIndentItem(GetValue());
+}
+
+bool ScIndentItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit eCoreUnit, MapUnit,
+ OUString& rText,
+ const IntlWrapper& rIntl) const
+{
+ auto nValue = GetValue();
+
+ switch (ePres)
+ {
+ case SfxItemPresentation::Complete:
+ rText = ScResId(STR_INDENTCELL);
+ [[fallthrough]];
+ case SfxItemPresentation::Nameless:
+ rText += GetMetricText( nValue, eCoreUnit, MapUnit::MapPoint, &rIntl ) +
+ " " + EditResId(GetMetricId(MapUnit::MapPoint));
+ return true;
+ default: ; //prevent warning
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/autonamecache.cxx b/sc/source/core/data/autonamecache.cxx
new file mode 100644
index 0000000000..f74219baf7
--- /dev/null
+++ b/sc/source/core/data/autonamecache.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 <unotools/transliterationwrapper.hxx>
+
+#include <autonamecache.hxx>
+#include <dociter.hxx>
+#include <formulacell.hxx>
+#include <editutil.hxx>
+
+ScAutoNameCache::ScAutoNameCache( ScDocument& rD ) :
+ rDoc( rD ),
+ nCurrentTab( 0 ) // doesn't matter - aNames is empty
+{
+}
+
+ScAutoNameCache::~ScAutoNameCache()
+{
+}
+
+const ScAutoNameAddresses& ScAutoNameCache::GetNameOccurrences( const OUString& rName, SCTAB nTab )
+{
+ if ( nTab != nCurrentTab )
+ {
+ // the lists are valid only for one sheet, so they are cleared when another sheet is used
+ aNames.clear();
+ nCurrentTab = nTab;
+ }
+
+ ScAutoNameHashMap::const_iterator aFound = aNames.find( rName );
+ if ( aFound != aNames.end() )
+ return aFound->second; // already initialized
+
+ ScAutoNameAddresses& rAddresses = aNames[rName];
+
+ ScCellIterator aIter( rDoc, ScRange( 0, 0, nCurrentTab, rDoc.MaxCol(), rDoc.MaxRow(), nCurrentTab ) );
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ // don't check code length here, always use the stored result
+ // (AutoCalc is disabled during CompileXML)
+ if (aIter.hasString())
+ {
+ OUString aStr;
+ switch (aIter.getType())
+ {
+ case CELLTYPE_STRING:
+ aStr = aIter.getString();
+ break;
+ case CELLTYPE_FORMULA:
+ aStr = aIter.getFormulaCell()->GetString().getString();
+ break;
+ case CELLTYPE_EDIT:
+ {
+ const EditTextObject* p = aIter.getEditText();
+ if (p)
+ aStr = ScEditUtil::GetMultilineString(*p); // string with line separators between paragraphs
+ }
+ break;
+ case CELLTYPE_NONE:
+ case CELLTYPE_VALUE:
+ ; // nothing, prevent compiler warning
+ break;
+ }
+ if ( ScGlobal::GetTransliteration().isEqual( aStr, rName ) )
+ {
+ rAddresses.push_back(aIter.GetPos());
+ }
+ }
+ }
+
+ return rAddresses;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx
new file mode 100644
index 0000000000..c8f182e11f
--- /dev/null
+++ b/sc/source/core/data/bcaslot.cxx
@@ -0,0 +1,1287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svl/listener.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <brdcst.hxx>
+#include <bcaslot.hxx>
+#include <scerrors.hxx>
+#include <refupdat.hxx>
+#include <bulkdatahint.hxx>
+#include <columnspanset.hxx>
+#include <formulacell.hxx>
+#include <grouparealistener.hxx>
+#include <broadcast.hxx>
+
+ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) :
+ pUpdateChainNext(nullptr),
+ aRange(rRange),
+ nRefCount(0),
+ mbInUpdateChain(false),
+ mbGroupListening(false) {}
+
+ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
+ ScBroadcastAreaSlotMachine* pBASMa ) :
+ aTmpSeekBroadcastArea( ScRange()),
+ pDoc( pDocument ),
+ pBASM( pBASMa ),
+ mbInBroadcastIteration( false),
+ mbHasErasedArea(false)
+{
+}
+
+ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
+{
+ for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
+ aIter != aBroadcastAreaTbl.end(); /* none */)
+ {
+ // Prevent hash from accessing dangling pointer in case area is
+ // deleted.
+ ScBroadcastArea* pArea = (*aIter).mpArea;
+ // Erase all so no hash will be accessed upon destruction of the
+ // unordered_map.
+ aIter = aBroadcastAreaTbl.erase(aIter);
+ if (!pArea->DecRef())
+ delete pArea;
+ }
+}
+
+ScDocument::HardRecalcState ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
+{
+ ScDocument::HardRecalcState eState = pDoc->GetHardRecalcState();
+ if (eState == ScDocument::HardRecalcState::OFF)
+ {
+ if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
+ { // this is more hypothetical now, check existed for old SV_PTRARR_SORT
+ ScDocShell* pShell = pDoc->GetDocumentShell();
+ OSL_ENSURE( pShell, "Missing DocShell :-/" );
+
+ if ( pShell )
+ pShell->SetError(SCWARN_CORE_HARD_RECALC);
+
+ pDoc->SetAutoCalc( false );
+ eState = ScDocument::HardRecalcState::ETERNAL;
+ pDoc->SetHardRecalcState( eState );
+ }
+ }
+ return eState;
+}
+
+bool ScBroadcastAreaSlot::StartListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
+{
+ bool bNewArea = false;
+ OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
+ assert(!pDoc->IsDelayedFormulaGrouping()); // otherwise the group size might be incorrect
+ if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
+ return false;
+ if ( !rpArea )
+ {
+ // Even if most times the area doesn't exist yet and immediately trying
+ // to new and insert it would save an attempt to find it, on massive
+ // operations like identical large [HV]LOOKUP() areas the new/delete
+ // would add quite some penalty for all but the first formula cell.
+ ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
+ if (aIter != aBroadcastAreaTbl.end())
+ rpArea = (*aIter).mpArea;
+ else
+ {
+ rpArea = new ScBroadcastArea( rRange);
+ rpArea->SetGroupListening(bGroupListening);
+ if (aBroadcastAreaTbl.insert( rpArea).second)
+ {
+ rpArea->IncRef();
+ bNewArea = true;
+ }
+ else
+ {
+ OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
+ delete rpArea;
+ rpArea = nullptr;
+ }
+ }
+ if (rpArea)
+ pListener->StartListening( rpArea->GetBroadcaster());
+ }
+ else
+ {
+ if (aBroadcastAreaTbl.insert( rpArea).second)
+ rpArea->IncRef();
+ }
+ return bNewArea;
+}
+
+void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
+{
+ OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
+ if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
+ return;
+ if (aBroadcastAreaTbl.insert( pArea).second)
+ pArea->IncRef();
+}
+
+// If rpArea != NULL then no listeners are stopped, only the area is removed
+// and the reference count decremented.
+void ScBroadcastAreaSlot::EndListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
+{
+ OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
+ if ( !rpArea )
+ {
+ ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
+ if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
+ return;
+ rpArea = (*aIter).mpArea;
+ pListener->EndListening( rpArea->GetBroadcaster() );
+ if ( !rpArea->GetBroadcaster().HasListeners() )
+ { // if nobody is listening we can dispose it
+ if (rpArea->GetRef() == 1)
+ rpArea = nullptr; // will be deleted by erase
+ EraseArea( aIter);
+ }
+ }
+ else
+ {
+ if (rpArea && !rpArea->GetBroadcaster().HasListeners())
+ {
+ ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
+ if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
+ return;
+ OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
+ if (rpArea->GetRef() == 1)
+ rpArea = nullptr; // will be deleted by erase
+ EraseArea( aIter);
+ }
+ }
+}
+
+ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
+ const ScRange& rRange, bool bGroupListening )
+{
+ aTmpSeekBroadcastArea.UpdateRange( rRange);
+ aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
+ return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
+}
+
+namespace {
+
+void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, SfxHintId nHint )
+{
+ ScHint aHint(nHint, ScAddress());
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ aHint.SetAddressTab(nTab);
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ aHint.SetAddressCol(nCol);
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ aHint.SetAddressRow(nRow);
+ rBC.Broadcast(aHint);
+ }
+ }
+ }
+}
+
+}
+
+bool ScBroadcastAreaSlot::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
+{
+ if (aBroadcastAreaTbl.empty())
+ return false;
+
+ bool bInBroadcast = mbInBroadcastIteration;
+ mbInBroadcastIteration = true;
+ bool bIsBroadcasted = false;
+
+ mbHasErasedArea = false;
+
+ for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
+ aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
+ {
+ if (mbHasErasedArea && isMarkedErased( aIter))
+ continue;
+
+ ScBroadcastArea* pArea = (*aIter).mpArea;
+ const ScRange& rAreaRange = pArea->GetRange();
+
+ // Take the intersection of the area range and the broadcast range.
+ ScRange aIntersection = rAreaRange.Intersection(rRange);
+ if (!aIntersection.IsValid())
+ continue;
+
+ if (pArea->IsGroupListening())
+ {
+ if (pBASM->IsInBulkBroadcast())
+ {
+ pBASM->InsertBulkGroupArea(pArea, aIntersection);
+ }
+ else
+ {
+ broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
+ bIsBroadcasted = true;
+ }
+ }
+ else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
+ {
+ broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
+ bIsBroadcasted = true;
+ }
+ }
+
+ mbInBroadcastIteration = bInBroadcast;
+
+ // A Notify() during broadcast may call EndListeningArea() and thus dispose
+ // an area if it was the last listener, which would invalidate an iterator
+ // pointing to it, hence the real erase is done afterwards.
+ FinallyEraseAreas();
+
+ return bIsBroadcasted;
+}
+
+bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
+{
+ if (aBroadcastAreaTbl.empty())
+ return false;
+
+ bool bInBroadcast = mbInBroadcastIteration;
+ mbInBroadcastIteration = true;
+ bool bIsBroadcasted = false;
+
+ mbHasErasedArea = false;
+
+ const ScRange& rRange = rHint.GetRange();
+ for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
+ aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
+ {
+ if (mbHasErasedArea && isMarkedErased( aIter))
+ continue;
+
+ ScBroadcastArea* pArea = (*aIter).mpArea;
+ const ScRange& rAreaRange = pArea->GetRange();
+ if (rAreaRange.Intersects( rRange))
+ {
+ if (pArea->IsGroupListening())
+ {
+ if (pBASM->IsInBulkBroadcast())
+ {
+ pBASM->InsertBulkGroupArea(pArea, rRange);
+ }
+ else
+ {
+ pArea->GetBroadcaster().Broadcast( rHint);
+ bIsBroadcasted = true;
+ }
+ }
+ else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
+ {
+ pArea->GetBroadcaster().Broadcast( rHint);
+ bIsBroadcasted = true;
+ }
+ }
+ }
+
+ mbInBroadcastIteration = bInBroadcast;
+
+ // A Notify() during broadcast may call EndListeningArea() and thus dispose
+ // an area if it was the last listener, which would invalidate an iterator
+ // pointing to it, hence the real erase is done afterwards.
+ FinallyEraseAreas();
+
+ return bIsBroadcasted;
+}
+
+void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
+{
+ if (aBroadcastAreaTbl.empty())
+ return;
+ for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
+ aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
+ {
+ const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
+ if (rRange.Contains( rAreaRange))
+ {
+ ScBroadcastArea* pArea = (*aIter).mpArea;
+ aIter = aBroadcastAreaTbl.erase(aIter); // erase before modifying
+ if (!pArea->DecRef())
+ {
+ if (pBASM->IsInBulkBroadcast())
+ pBASM->RemoveBulkArea( pArea);
+ delete pArea;
+ }
+ }
+ else
+ ++aIter;
+ }
+}
+
+void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ if (aBroadcastAreaTbl.empty())
+ return;
+
+ SCCOL nCol1, nCol2, theCol1, theCol2;
+ SCROW nRow1, nRow2, theRow1, theRow2;
+ SCTAB nTab1, nTab2, theTab1, theTab2;
+ rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
+ aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
+ {
+ ScBroadcastArea* pArea = (*aIter).mpArea;
+ if ( pArea->IsInUpdateChain() )
+ {
+ aIter = aBroadcastAreaTbl.erase(aIter);
+ pArea->DecRef();
+ }
+ else
+ {
+ pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
+ theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
+ {
+ aIter = aBroadcastAreaTbl.erase(aIter);
+ pArea->DecRef();
+ if (pBASM->IsInBulkBroadcast())
+ pBASM->RemoveBulkArea( pArea);
+ pArea->SetInUpdateChain( true );
+ ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
+ if ( pUC )
+ pUC->SetUpdateChainNext( pArea );
+ else // no tail => no head
+ pBASM->SetUpdateChain( pArea );
+ pBASM->SetEOUpdateChain( pArea );
+ }
+ else
+ ++aIter;
+ }
+ }
+}
+
+void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
+{
+ ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
+ if (aIter == aBroadcastAreaTbl.end())
+ return;
+ if ((*aIter).mpArea != pArea)
+ OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
+ else
+ {
+ aBroadcastAreaTbl.erase( aIter);
+ pArea->DecRef();
+ }
+}
+
+void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
+{
+ ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
+ aBroadcastAreaTbl.insert( pArea);
+ if (aPair.second)
+ pArea->IncRef();
+ else
+ {
+ // Identical area already exists, add listeners.
+ ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
+ if (pArea != pTarget)
+ {
+ SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
+ SvtBroadcaster::ListenersType& rListeners = pArea->GetBroadcaster().GetAllListeners();
+ for (auto& pListener : rListeners)
+ {
+ SvtListener& rListener = *pListener;
+ rListener.StartListening(rTarget);
+ }
+ }
+ }
+}
+
+void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
+{
+ if (mbInBroadcastIteration)
+ {
+ (*rIter).mbErasure = true; // mark for erasure
+ mbHasErasedArea = true; // at least one area is marked for erasure.
+ pBASM->PushAreaToBeErased( this, rIter);
+ }
+ else
+ {
+ ScBroadcastArea* pArea = (*rIter).mpArea;
+ aBroadcastAreaTbl.erase( rIter);
+ if (!pArea->DecRef())
+ {
+ if (pBASM->IsInBulkBroadcast())
+ pBASM->RemoveBulkGroupArea(pArea);
+ delete pArea;
+ }
+ }
+}
+
+void ScBroadcastAreaSlot::GetAllListeners(
+ const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
+ sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
+{
+ for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
+ aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
+ {
+ if (isMarkedErased( aIter))
+ continue;
+
+ ScBroadcastArea* pArea = (*aIter).mpArea;
+ const ScRange& rAreaRange = pArea->GetRange();
+ switch (eGroup)
+ {
+ case sc::ListenerGroupType::Group:
+ if (!pArea->IsGroupListening())
+ continue;
+ break;
+ case sc::ListenerGroupType::Both:
+ default:
+ ;
+ }
+
+ switch (eType)
+ {
+ case sc::AreaOverlapType::Inside:
+ if (!rRange.Contains(rAreaRange))
+ // The range needs to be fully inside specified range.
+ continue;
+ break;
+ case sc::AreaOverlapType::InsideOrOverlap:
+ if (!rRange.Intersects(rAreaRange))
+ // The range needs to be partially overlapping or fully inside.
+ continue;
+ break;
+ case sc::AreaOverlapType::OneRowInside:
+ if (rAreaRange.aStart.Row() != rAreaRange.aEnd.Row() || !rRange.Contains(rAreaRange))
+ // The range needs to be one single row and fully inside
+ // specified range.
+ continue;
+ break;
+ case sc::AreaOverlapType::OneColumnInside:
+ if (rAreaRange.aStart.Col() != rAreaRange.aEnd.Col() || !rRange.Contains(rAreaRange))
+ // The range needs to be one single column and fully inside
+ // specified range.
+ continue;
+ break;
+ }
+
+ SvtBroadcaster::ListenersType& rLst = pArea->GetBroadcaster().GetAllListeners();
+ for (const auto& pListener : rLst)
+ {
+ sc::AreaListener aEntry;
+ aEntry.maArea = rAreaRange;
+ aEntry.mbGroupListening = pArea->IsGroupListening();
+ aEntry.mpListener = pListener;
+ rListeners.push_back(aEntry);
+ }
+ }
+}
+
+void ScBroadcastAreaSlot::CollectBroadcasterState(sc::BroadcasterState& rState) const
+{
+ for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
+ {
+ const ScRange& rRange = rEntry.mpArea->GetRange();
+ auto aRes = rState.aAreaListenerStore.try_emplace(rRange);
+ auto& rLisStore = aRes.first->second;
+
+ for (const SvtListener* pLis : rEntry.mpArea->GetBroadcaster().GetAllListeners())
+ {
+ if (auto pFC = dynamic_cast<const ScFormulaCell*>(pLis); pFC)
+ {
+ rLisStore.emplace_back(pFC);
+ continue;
+ }
+
+ if (auto pFGL = dynamic_cast<const sc::FormulaGroupAreaListener*>(pLis); pFGL)
+ {
+ rLisStore.emplace_back(pFGL);
+ continue;
+ }
+
+ rLisStore.emplace_back(pLis);
+ }
+ }
+}
+
+void ScBroadcastAreaSlot::FinallyEraseAreas()
+{
+ pBASM->FinallyEraseAreas( this);
+}
+
+// --- ScBroadcastAreaSlotMachine -------------------------------------
+
+ScBroadcastAreaSlotMachine::TableSlots::TableSlots(SCSIZE nBcaSlots)
+ : mnBcaSlots(nBcaSlots)
+{
+ ppSlots.reset( new ScBroadcastAreaSlot* [ nBcaSlots ] );
+ memset( ppSlots.get(), 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
+}
+
+ScBroadcastAreaSlotMachine::TableSlots::TableSlots(TableSlots&& rOther) noexcept
+ : mnBcaSlots(rOther.mnBcaSlots)
+ , ppSlots( std::move(rOther.ppSlots) )
+{
+}
+
+ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
+{
+ if (ppSlots)
+ for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
+ delete *pp;
+}
+
+ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
+ ScDocument* pDocument ) :
+ pDoc( pDocument ),
+ pUpdateChain( nullptr ),
+ pEOUpdateChain( nullptr ),
+ nInBulkBroadcast( 0 )
+{
+ // initSlotDistribution ---------
+ // Logarithmic or any other distribution.
+ // Upper and leftmost sheet part usually is more populated and referenced and gets fine
+ // grained resolution, larger data in larger hunks.
+ // Just like with cells, slots are organized in columns. Slot 0 is for first nSliceRow x nSliceCol
+ // cells, slot 1 is for next nSliceRow x nSliceCel cells below, etc. After a while the size of row
+ // slice doubles (making more cells share the same slot), this distribution data is stored
+ // in ScSlotData including ranges of cells. This is repeated for another column of nSliceCol cells,
+ // again with the column slice doubling after some time.
+ // Functions ComputeSlotOffset(), ComputeArePoints() and ComputeNextSlot() do the necessary
+ // calculations.
+ SCSIZE nSlots = 0;
+ // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
+ sal_Int32 nCol1 = 0;
+ sal_Int32 nCol2 = 1024;
+ SCSIZE nSliceCol = 16;
+ while (nCol2 <= pDoc->GetMaxColCount())
+ {
+ SCROW nRow1 = 0;
+ SCROW nRow2 = 32*1024;
+ SCSIZE nSliceRow = 128;
+ SCSIZE nSlotsCol = 0;
+ SCSIZE nSlotsStartCol = nSlots;
+ // Must be sorted by row1,row2!
+ while (nRow2 <= pDoc->GetMaxRowCount())
+ {
+ maSlotDistribution.emplace_back(nRow1, nRow2, nSliceRow, nSlotsCol, nCol1, nCol2, nSliceCol, nSlotsStartCol);
+ nSlotsCol += (nRow2 - nRow1) / nSliceRow;
+ nRow1 = nRow2;
+ nRow2 *= 2;
+ nSliceRow *= 2;
+ }
+ // Store the number of slots in a column in mnBcaSlotsCol, so that finding a slot
+ // to the right can be computed quickly in ComputeNextSlot().
+ if(nCol1 == 0)
+ mnBcaSlotsCol = nSlotsCol;
+ assert(nSlotsCol == mnBcaSlotsCol);
+ nSlots += (nCol2 - nCol1) / nSliceCol * nSlotsCol;
+ nCol1 = nCol2;
+ nCol2 *= 2;
+ nSliceCol *= 2;
+ }
+ mnBcaSlots = nSlots;
+#ifdef DBG_UTIL
+ DoChecks();
+#endif
+}
+
+ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
+{
+ aTableSlotsMap.clear();
+ pBCAlways.reset();
+ // Areas to-be-erased still present is a serious error in handling, but at
+ // this stage there's nothing we can do anymore.
+ SAL_WARN_IF( !maAreasToBeErased.empty(), "sc.core", "ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
+}
+
+inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
+ const ScAddress& rAddress ) const
+{
+ SCROW nRow = rAddress.Row();
+ SCCOL nCol = rAddress.Col();
+ if ( !pDoc->ValidRow(nRow) || !pDoc->ValidCol(nCol) )
+ {
+ OSL_FAIL( "Row/Col invalid, using first slot!" );
+ return 0;
+ }
+ for (const ScSlotData& rSD : maSlotDistribution)
+ {
+ if (nRow < rSD.nStopRow && nCol < rSD.nStopCol)
+ {
+ assert(nRow >= rSD.nStartRow);
+ assert(nCol >= rSD.nStartCol);
+ SCSIZE slot = rSD.nCumulatedRow
+ + static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSliceRow
+ + rSD.nCumulatedCol
+ + static_cast<SCSIZE>(nCol - rSD.nStartCol) / rSD.nSliceCol * mnBcaSlotsCol;
+ assert(slot < mnBcaSlots);
+ return slot;
+ }
+ }
+ OSL_FAIL( "No slot found, using last!" );
+ return mnBcaSlots - 1;
+}
+
+void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
+ SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
+{
+ rStart = ComputeSlotOffset( rRange.aStart );
+ rEnd = ComputeSlotOffset( rRange.aEnd );
+ // count of row slots per column minus one
+ rRowBreak = ComputeSlotOffset(
+ ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
+}
+
+static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
+ SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol )
+{
+ if ( nOff < nBreak )
+ {
+ ++nOff;
+ ++pp;
+ }
+ else
+ {
+ nStart += nBcaSlotsCol;
+ nOff = nStart;
+ pp = ppSlots + nOff;
+ nBreak = nOff + nRowBreak;
+ }
+}
+
+#ifdef DBG_UTIL
+static void compare(SCSIZE value1, SCSIZE value2, int line)
+{
+ if(value1!=value2)
+ SAL_WARN("sc", "V1:" << value1 << " V2:" << value2 << " (" << line << ")");
+ assert(value1 == value2);
+}
+
+// Basic checks that the calculations work correctly.
+void ScBroadcastAreaSlotMachine::DoChecks()
+{
+ // Copy&paste from the ctor.
+ constexpr SCSIZE nSliceRow = 128;
+ constexpr SCSIZE nSliceCol = 16;
+ // First and second column are in the same slice and so get the same slot.
+ compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )), ComputeSlotOffset( ScAddress( 1, 0, 0 )), __LINE__);
+ // Each nSliceRow rows are offset by one slot (at the start of the logarithmic distribution).
+ compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )),
+ ComputeSlotOffset( ScAddress( 0, nSliceRow, 0 )) - 1, __LINE__ );
+ compare( ComputeSlotOffset( ScAddress( nSliceCol - 1, 0, 0 )),
+ ComputeSlotOffset( ScAddress( nSliceCol, 0, 0 )) - mnBcaSlotsCol, __LINE__ );
+ // Check that last cell is the last slot.
+ compare( ComputeSlotOffset( ScAddress( pDoc->GetMaxColCount() - 1, pDoc->GetMaxRowCount() - 1, 0 )),
+ mnBcaSlots - 1, __LINE__ );
+ // Check that adjacent rows in the same column but in different distribution areas differ by one slot.
+ for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
+ {
+ const ScSlotData& s1 = maSlotDistribution[ i ];
+ const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
+ if( s1.nStartCol == s2.nStartCol )
+ {
+ assert( s1.nStopRow == s2.nStartRow );
+ compare( ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow - 1, 0 )),
+ ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow, 0 )) - 1, __LINE__ );
+ }
+ }
+ // Check that adjacent columns in the same row but in different distribution areas differ by mnBcaSlotsCol.
+ for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
+ {
+ const ScSlotData& s1 = maSlotDistribution[ i ];
+ for( size_t j = i + 1; j < maSlotDistribution.size(); ++j )
+ {
+ const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
+ if( s1.nStartRow == s2.nStartRow && s1.nStopCol == s2.nStartCol )
+ {
+ assert( s1.nStopRow == s2.nStartRow );
+ compare( ComputeSlotOffset( ScAddress( s1.nStopCol - 1, s1.nStartRow, 0 )),
+ ComputeSlotOffset( ScAddress( s1.nStopCol, s1.nStartRow, 0 )) - mnBcaSlotsCol, __LINE__ );
+ }
+ }
+ }
+ // Iterate all slots.
+ ScRange range( ScAddress( 0, 0, 0 ), ScAddress( pDoc->MaxCol(), pDoc->MaxRow(), 0 ));
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
+ assert( nStart == 0 );
+ assert( nEnd == mnBcaSlots - 1 );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ std::unique_ptr<ScBroadcastAreaSlot*[]> slots( new ScBroadcastAreaSlot*[ mnBcaSlots ] ); // dummy, not accessed
+ ScBroadcastAreaSlot** ppSlots = slots.get();
+ ScBroadcastAreaSlot** pp = ppSlots;
+ while ( nOff <= nEnd )
+ {
+ SCSIZE previous = nOff;
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ compare( nOff, previous + 1, __LINE__ );
+ }
+ // Iterate slots in the last row (each will differ by mnBcaSlotsCol).
+ range = ScRange( ScAddress( 0, pDoc->MaxRow(), 0 ),
+ ScAddress( pDoc->MaxCol(), pDoc->MaxRow() - 1, 0 ));
+ ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
+ assert( nStart == mnBcaSlotsCol - 1 );
+ assert( nEnd == mnBcaSlots - 1 );
+ nOff = nStart;
+ nBreak = nOff + nRowBreak;
+ ppSlots = slots.get();
+ pp = ppSlots;
+ while ( nOff <= nEnd )
+ {
+ SCSIZE previous = nOff;
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ compare( nOff, previous + mnBcaSlotsCol, __LINE__ );
+ }
+}
+#endif
+
+void ScBroadcastAreaSlotMachine::StartListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
+{
+ if ( rRange == BCA_LISTEN_ALWAYS )
+ {
+ if ( !pBCAlways )
+ pBCAlways.reset( new SvtBroadcaster );
+ pListener->StartListening( *pBCAlways );
+ }
+ else
+ {
+ // A new area needs to be inserted to the corresponding slots, for 3D
+ // ranges for all sheets, do not slice into per sheet areas or the
+ // !bDone will break too early (i.e. after the first sheet) if
+ // subsequent listeners are to be added.
+ ScBroadcastArea* pArea = nullptr;
+ bool bDone = false;
+ for (SCTAB nTab = rRange.aStart.Tab();
+ !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
+ if (iTab == aTableSlotsMap.end())
+ iTab = aTableSlotsMap.emplace( std::piecewise_construct,
+ std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ while ( !bDone && nOff <= nEnd )
+ {
+ if ( !*pp )
+ *pp = new ScBroadcastAreaSlot( pDoc, this );
+ if (!pArea)
+ {
+ // If the call to StartListeningArea didn't create the
+ // ScBroadcastArea, listeners were added to an already
+ // existing identical area that doesn't need to be inserted
+ // to slots again.
+ if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
+ bDone = true;
+ }
+ else
+ (*pp)->InsertListeningArea( pArea);
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+ }
+}
+
+void ScBroadcastAreaSlotMachine::EndListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
+{
+ if ( rRange == BCA_LISTEN_ALWAYS )
+ {
+ if ( pBCAlways )
+ {
+ pListener->EndListening( *pBCAlways);
+ if (!pBCAlways->HasListeners())
+ {
+ pBCAlways.reset();
+ }
+ }
+ }
+ else
+ {
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
+ {
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ ScBroadcastArea* pArea = nullptr;
+ if (nOff == 0 && nEnd == mnBcaSlots-1)
+ {
+ // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
+ // happen for insertion and deletion of sheets.
+ ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
+ do
+ {
+ if ( *pp )
+ (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
+ } while (++pp < pStop);
+ }
+ else
+ {
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+ }
+ }
+}
+
+bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
+{
+ bool bBroadcasted = false;
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
+ {
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+ return bBroadcasted;
+}
+
+bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
+{
+ const ScAddress& rAddress = rHint.GetStartAddress();
+ if ( rAddress == BCA_BRDCST_ALWAYS )
+ {
+ if ( pBCAlways )
+ {
+ pBCAlways->Broadcast( rHint );
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ {
+ TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
+ if (iTab == aTableSlotsMap.end())
+ return false;
+ // Process all slots for the given row range.
+ ScRange broadcastRange( rAddress,
+ ScAddress( rAddress.Col(), rAddress.Row() + rHint.GetRowCount() - 1, rAddress.Tab()));
+ bool bBroadcasted = false;
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( broadcastRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ bBroadcasted |= (*pp)->AreaBroadcast( rHint );
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ return bBroadcasted;
+ }
+}
+
+void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
+ const ScRange& rRange )
+{
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
+ {
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ if (nOff == 0 && nEnd == mnBcaSlots-1)
+ {
+ // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
+ // happen for insertion and deletion of sheets.
+ ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
+ do
+ {
+ if ( *pp )
+ (*pp)->DelBroadcastAreasInRange( rRange );
+ } while (++pp < pStop);
+ }
+ else
+ {
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ (*pp)->DelBroadcastAreasInRange( rRange );
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+ }
+}
+
+// for all affected: remove, chain, update range, insert, and maybe delete
+void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
+ UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ // remove affected and put in chain
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
+ {
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ if (nOff == 0 && nEnd == mnBcaSlots-1)
+ {
+ // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
+ // happen for insertion and deletion of sheets.
+ ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
+ do
+ {
+ if ( *pp )
+ (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
+ } while (++pp < pStop);
+ }
+ else
+ {
+ while ( nOff <= nEnd )
+ {
+ if ( *pp )
+ (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+ }
+
+ // Updating an area's range will modify the hash key, remove areas from all
+ // affected slots. Will be reinserted later with the updated range.
+ ScBroadcastArea* pChain = pUpdateChain;
+ while (pChain)
+ {
+ ScBroadcastArea* pArea = pChain;
+ pChain = pArea->GetUpdateChainNext();
+ ScRange aRange( pArea->GetRange());
+ // remove from slots
+ for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
+ {
+ TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
+ if (iTab == aTableSlotsMap.end())
+ {
+ OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
+ continue; // for
+ }
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ while ( nOff <= nEnd && pArea->GetRef() )
+ {
+ if (*pp)
+ (*pp)->UpdateRemoveArea( pArea);
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+
+ }
+
+ // shift sheets
+ if (nDz)
+ {
+ if (nDz < 0)
+ {
+ TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
+ // Remove sheets, if any, iDel or/and iTab may as well point to end().
+ while (iDel != iTab)
+ {
+ iDel = aTableSlotsMap.erase(iDel);
+ }
+ // shift remaining down
+ while (iTab != aTableSlotsMap.end())
+ {
+ SCTAB nTab = (*iTab).first + nDz;
+ aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
+ iTab = aTableSlotsMap.erase(iTab);
+ }
+ }
+ else
+ {
+ TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ if (iStop != aTableSlotsMap.end())
+ {
+ bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
+ if (!bStopIsBegin)
+ --iStop;
+ TableSlotsMap::iterator iTab( aTableSlotsMap.end());
+ --iTab;
+ while (iTab != iStop)
+ {
+ SCTAB nTab = (*iTab).first + nDz;
+ aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
+ aTableSlotsMap.erase( iTab--);
+ }
+ // Shift the very first, iTab==iStop in this case.
+ if (bStopIsBegin)
+ {
+ SCTAB nTab = (*iTab).first + nDz;
+ aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
+ aTableSlotsMap.erase( iStop);
+ }
+ }
+ }
+ }
+
+ // work off chain
+ SCCOL nCol1, nCol2, theCol1, theCol2;
+ SCROW nRow1, nRow2, theRow1, theRow2;
+ SCTAB nTab1, nTab2, theTab1, theTab2;
+ rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ while ( pUpdateChain )
+ {
+ ScBroadcastArea* pArea = pUpdateChain;
+ ScRange aRange( pArea->GetRange());
+ pUpdateChain = pArea->GetUpdateChainNext();
+
+ // update range
+ aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
+ theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
+ {
+ aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
+ pArea->UpdateRange( aRange );
+ // For DDE and ScLookupCache
+ pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
+ }
+
+ // insert to slots
+ for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
+ {
+ TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
+ if (iTab == aTableSlotsMap.end())
+ iTab = aTableSlotsMap.emplace( std::piecewise_construct,
+ std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ while ( nOff <= nEnd )
+ {
+ if (!*pp)
+ *pp = new ScBroadcastAreaSlot( pDoc, this );
+ (*pp)->UpdateInsert( pArea );
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+
+ // unchain
+ pArea->SetUpdateChainNext( nullptr );
+ pArea->SetInUpdateChain( false );
+
+ // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
+ // already executed in UpdateRemove().
+ if (!pArea->GetRef())
+ delete pArea;
+ }
+ pEOUpdateChain = nullptr;
+}
+
+void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
+{
+ ++nInBulkBroadcast;
+}
+
+void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast( SfxHintId nHintId )
+{
+ if (nInBulkBroadcast <= 0)
+ return;
+
+ if (--nInBulkBroadcast == 0)
+ {
+ ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
+ bool bBroadcasted = BulkBroadcastGroupAreas( nHintId );
+ // Trigger the "final" tracking.
+ if (pDoc->IsTrackFormulasPending())
+ pDoc->FinalTrackFormulas( nHintId );
+ else if (bBroadcasted)
+ pDoc->TrackFormulas( nHintId );
+ }
+}
+
+bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
+{
+ return aBulkBroadcastAreas.insert( pArea ).second;
+}
+
+void ScBroadcastAreaSlotMachine::InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange )
+{
+ BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
+ if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
+ {
+ // Insert a new one.
+ it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, sc::ColumnSpanSet()));
+ }
+
+ sc::ColumnSpanSet& rSet = it->second;
+ rSet.set(*pDoc, rRange, true);
+}
+
+bool ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas( SfxHintId nHintId )
+{
+ if (m_BulkGroupAreas.empty())
+ return false;
+
+ sc::BulkDataHint aHint( *pDoc, nHintId);
+
+ bool bBroadcasted = false;
+ for (const auto& [pArea, rSpans] : m_BulkGroupAreas)
+ {
+ assert(pArea);
+ SvtBroadcaster& rBC = pArea->GetBroadcaster();
+ if (!rBC.HasListeners())
+ {
+ /* FIXME: find the cause where the last listener is removed and
+ * this area is still listed here. */
+ SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
+ }
+ else
+ {
+ aHint.setSpans(&rSpans);
+ rBC.Broadcast(aHint);
+ bBroadcasted = true;
+ }
+ }
+
+ m_BulkGroupAreas.clear();
+
+ return bBroadcasted;
+}
+
+size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
+{
+ return aBulkBroadcastAreas.erase( pArea );
+}
+
+void ScBroadcastAreaSlotMachine::RemoveBulkGroupArea( ScBroadcastArea* pArea )
+{
+ m_BulkGroupAreas.erase(pArea);
+}
+
+void ScBroadcastAreaSlotMachine::PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
+ ScBroadcastAreas::iterator& rIter )
+{
+ maAreasToBeErased.emplace_back( pSlot, rIter);
+}
+
+void ScBroadcastAreaSlotMachine::FinallyEraseAreas( ScBroadcastAreaSlot* pSlot )
+{
+ SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
+ "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
+ if (pSlot->IsInBroadcastIteration())
+ return;
+
+ // maAreasToBeErased is a simple vector so erasing an element may
+ // invalidate iterators and would be inefficient anyway. Instead, copy
+ // elements to be preserved (usually none!) to temporary vector and swap.
+ AreasToBeErased aCopy;
+ for (auto& rArea : maAreasToBeErased)
+ {
+ if (rArea.first == pSlot)
+ pSlot->EraseArea( rArea.second);
+ else
+ aCopy.push_back( rArea);
+ }
+ maAreasToBeErased.swap( aCopy);
+}
+
+std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
+ const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
+{
+ std::vector<sc::AreaListener> aRet;
+
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+ iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
+ {
+ ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
+ SCSIZE nStart, nEnd, nRowBreak;
+ ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+ SCSIZE nOff = nStart;
+ SCSIZE nBreak = nOff + nRowBreak;
+ ScBroadcastAreaSlot** pp = ppSlots + nOff;
+ while ( nOff <= nEnd )
+ {
+ ScBroadcastAreaSlot* p = *pp;
+ if (p)
+ p->GetAllListeners(rRange, aRet, eType, eGroup);
+ ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
+ }
+ }
+
+ return aRet;
+}
+
+void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState& rState) const
+{
+ for (const auto& [rTab, rTabSlots] : aTableSlotsMap)
+ {
+ (void)rTab;
+
+ ScBroadcastAreaSlot** pp = rTabSlots.getSlots();
+ for (SCSIZE i = 0; i < mnBcaSlots; ++i)
+ {
+ const ScBroadcastAreaSlot* pSlot = pp[i];
+ if (pSlot)
+ pSlot->CollectBroadcasterState(rState);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/bigrange.cxx b/sc/source/core/data/bigrange.cxx
new file mode 100644
index 0000000000..1927657439
--- /dev/null
+++ b/sc/source/core/data/bigrange.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/.
+ */
+
+#include <bigrange.hxx>
+#include <document.hxx>
+
+bool ScBigAddress::IsValid( const ScDocument& rDoc ) const
+{ // min/max interval bounds define whole col/row/tab
+ return
+ ((0 <= nCol && nCol <= rDoc.MaxCol())
+ || nCol == ScBigRange::nRangeMin || nCol == ScBigRange::nRangeMax) &&
+ ((0 <= nRow && nRow <= rDoc.MaxRow())
+ || nRow == ScBigRange::nRangeMin || nRow == ScBigRange::nRangeMax) &&
+ ((0 <= nTab && nTab < rDoc.GetTableCount())
+ || nTab == ScBigRange::nRangeMin || nTab == ScBigRange::nRangeMax)
+ ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/broadcast.cxx b/sc/source/core/data/broadcast.cxx
new file mode 100644
index 0000000000..d76591692f
--- /dev/null
+++ b/sc/source/core/data/broadcast.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/.
+ */
+
+#include <broadcast.hxx>
+#include <address.hxx>
+#include <formulacell.hxx>
+
+namespace sc
+{
+BroadcasterState::CellListener::CellListener(const ScFormulaCell* p)
+ : pData(p)
+{
+}
+
+BroadcasterState::CellListener::CellListener(const SvtListener* p)
+ : pData(p)
+{
+}
+
+BroadcasterState::AreaListener::AreaListener(const ScFormulaCell* p)
+ : pData(p)
+{
+}
+
+BroadcasterState::AreaListener::AreaListener(const sc::FormulaGroupAreaListener* p)
+ : pData(p)
+{
+}
+
+BroadcasterState::AreaListener::AreaListener(const SvtListener* p)
+ : pData(p)
+{
+}
+
+bool BroadcasterState::hasFormulaCellListener(const ScAddress& rBroadcasterPos,
+ const ScAddress& rFormulaPos) const
+{
+ auto it = aCellListenerStore.find(rBroadcasterPos);
+ if (it == aCellListenerStore.end())
+ return false;
+
+ for (const auto& rLis : it->second)
+ {
+ if (rLis.pData.index() == 0)
+ {
+ auto pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ if (pFC->aPos == rFormulaPos)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BroadcasterState::hasFormulaCellListener(const ScRange& rBroadcasterRange,
+ const ScAddress& rFormulaPos) const
+{
+ auto it = aAreaListenerStore.find(rBroadcasterRange);
+ if (it == aAreaListenerStore.end())
+ return false;
+
+ for (const auto& rLis : it->second)
+ {
+ if (rLis.pData.index() == 0)
+ {
+ auto pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ if (pFC->aPos == rFormulaPos)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void BroadcasterState::dump(std::ostream& rStrm, const ScDocument* pDoc) const
+{
+ constexpr ScRefFlags nPosFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+
+ rStrm << "---" << std::endl;
+
+ for (const auto & [ rPos, rListeners ] : aCellListenerStore)
+ {
+ rStrm << "- type: cell-broadcaster\n";
+ rStrm << " position: " << rPos.Format(nPosFlags, pDoc) << std::endl;
+
+ if (!rListeners.empty())
+ rStrm << " listeners:\n";
+
+ for (const auto& rLis : rListeners)
+ {
+ switch (rLis.pData.index())
+ {
+ case 0:
+ {
+ auto* pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ rStrm << " - type: formula-cell\n";
+ rStrm << " position: " << pFC->aPos.Format(nPosFlags, pDoc) << std::endl;
+ break;
+ }
+ case 1:
+ {
+ rStrm << " - type: unknown" << std::endl;
+ break;
+ }
+ }
+ }
+ }
+
+ for (const auto & [ rRange, rListeners ] : aAreaListenerStore)
+ {
+ rStrm << "- type: area-broadcaster\n";
+ rStrm << " range: " << rRange.Format(*pDoc, nPosFlags) << std::endl;
+
+ if (!rListeners.empty())
+ rStrm << " listeners:\n";
+
+ for (const auto& rLis : rListeners)
+ {
+ switch (rLis.pData.index())
+ {
+ case 0:
+ {
+ auto* pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ rStrm << " - type: formula-cell\n";
+ rStrm << " position: " << pFC->aPos.Format(nPosFlags, pDoc) << std::endl;
+ break;
+ }
+ case 1:
+ {
+ auto* pFGL = std::get<const sc::FormulaGroupAreaListener*>(rLis.pData);
+
+ auto pTopCell = pFGL->getTopCell();
+ if (auto xFG = pTopCell->GetCellGroup(); xFG)
+ {
+ ScRange aGR(pTopCell->aPos);
+ aGR.aEnd.IncRow(xFG->mnLength - 1);
+ rStrm << " - type: formula-group\n";
+ rStrm << " range: " << aGR.Format(*pDoc, nPosFlags) << std::endl;
+ }
+ break;
+ }
+ case 2:
+ {
+ rStrm << " - type: unknown" << std::endl;
+ break;
+ }
+ }
+ }
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/celltextattr.cxx b/sc/source/core/data/celltextattr.cxx
new file mode 100644
index 0000000000..09bfb0829a
--- /dev/null
+++ b/sc/source/core/data/celltextattr.cxx
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <celltextattr.hxx>
+#include <globalnames.hxx>
+
+namespace sc {
+
+CellTextAttr::CellTextAttr() :
+ mnTextWidth(TEXTWIDTH_DIRTY),
+ mnScriptType(SvtScriptType::UNKNOWN) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/cellvalue.cxx b/sc/source/core/data/cellvalue.cxx
new file mode 100644
index 0000000000..4330ea9729
--- /dev/null
+++ b/sc/source/core/data/cellvalue.cxx
@@ -0,0 +1,688 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <column.hxx>
+#include <formulacell.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <stringutil.hxx>
+#include <editutil.hxx>
+#include <tokenarray.hxx>
+#include <formula/token.hxx>
+#include <formula/errorcodes.hxx>
+#include <svl/sharedstring.hxx>
+
+namespace {
+
+CellType adjustCellType( CellType eOrig )
+{
+ switch (eOrig)
+ {
+ case CELLTYPE_EDIT:
+ return CELLTYPE_STRING;
+ default:
+ ;
+ }
+ return eOrig;
+}
+
+template<typename T>
+OUString getString( const T& rVal )
+{
+ if (rVal.getType() == CELLTYPE_STRING)
+ return rVal.getSharedString()->getString();
+
+ if (rVal.getType() == CELLTYPE_EDIT)
+ {
+ OUStringBuffer aRet;
+ sal_Int32 n = rVal.getEditText()->GetParagraphCount();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ if (i > 0)
+ aRet.append('\n');
+ aRet.append(rVal.getEditText()->GetText(i));
+ }
+ return aRet.makeStringAndClear();
+ }
+
+ return OUString();
+}
+
+bool equalsFormulaCells( const ScFormulaCell* p1, const ScFormulaCell* p2 )
+{
+ const ScTokenArray* pCode1 = p1->GetCode();
+ const ScTokenArray* pCode2 = p2->GetCode();
+
+ if (pCode1->GetLen() != pCode2->GetLen())
+ return false;
+
+ if (pCode1->GetCodeError() != pCode2->GetCodeError())
+ return false;
+
+ sal_uInt16 n = pCode1->GetLen();
+ formula::FormulaToken** ppToken1 = pCode1->GetArray();
+ formula::FormulaToken** ppToken2 = pCode2->GetArray();
+ for (sal_uInt16 i = 0; i < n; ++i)
+ {
+ if (!ppToken1[i]->TextEqual(*(ppToken2[i])))
+ return false;
+ }
+
+ return true;
+}
+
+template<typename T>
+bool equalsWithoutFormatImpl( const T& left, const T& right )
+{
+ CellType eType1 = adjustCellType(left.getType());
+ CellType eType2 = adjustCellType(right.getType());
+ if (eType1 != eType2)
+ return false;
+
+ switch (eType1)
+ {
+ case CELLTYPE_NONE:
+ return true;
+ case CELLTYPE_VALUE:
+ return left.getDouble() == right.getDouble();
+ case CELLTYPE_STRING:
+ {
+ OUString aStr1 = getString(left);
+ OUString aStr2 = getString(right);
+ return aStr1 == aStr2;
+ }
+ case CELLTYPE_FORMULA:
+ return equalsFormulaCells(left.getFormula(), right.getFormula());
+ default:
+ ;
+ }
+ return false;
+}
+
+bool equalsWithoutFormatImpl( const ScCellValue& left, const ScCellValue& right )
+{
+ CellType eType1 = adjustCellType(left.getType());
+ CellType eType2 = adjustCellType(right.getType());
+ if (eType1 != eType2)
+ return false;
+
+ switch (eType1)
+ {
+ case CELLTYPE_NONE:
+ return true;
+ case CELLTYPE_VALUE:
+ return left.getDouble() == right.getDouble();
+ case CELLTYPE_STRING:
+ {
+ OUString aStr1 = getString(left);
+ OUString aStr2 = getString(right);
+ return aStr1 == aStr2;
+ }
+ case CELLTYPE_FORMULA:
+ return equalsFormulaCells(left.getFormula(), right.getFormula());
+ default:
+ ;
+ }
+ return false;
+}
+
+void commitToColumn( const ScCellValue& rCell, ScColumn& rColumn, SCROW nRow )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING:
+ rColumn.SetRawString(nRow, *rCell.getSharedString());
+ break;
+ case CELLTYPE_EDIT:
+ rColumn.SetEditText(nRow, ScEditUtil::Clone(*rCell.getEditText(), rColumn.GetDoc()));
+ break;
+ case CELLTYPE_VALUE:
+ rColumn.SetValue(nRow, rCell.getDouble());
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScAddress aDestPos(rColumn.GetCol(), nRow, rColumn.GetTab());
+ rColumn.SetFormulaCell(nRow, new ScFormulaCell(*rCell.getFormula(), rColumn.GetDoc(), aDestPos));
+ }
+ break;
+ default:
+ rColumn.DeleteContent(nRow);
+ }
+}
+
+bool hasStringImpl( CellType eType, ScFormulaCell* pFormula )
+{
+ switch (eType)
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ return true;
+ case CELLTYPE_FORMULA:
+ return !pFormula->IsValue();
+ default:
+ return false;
+ }
+}
+
+bool hasNumericImpl( CellType eType, ScFormulaCell* pFormula )
+{
+ switch (eType)
+ {
+ case CELLTYPE_VALUE:
+ return true;
+ case CELLTYPE_FORMULA:
+ return pFormula->IsValue();
+ default:
+ return false;
+ }
+}
+
+template <typename T>
+OUString getStringImpl( const T& rCell, const ScDocument* pDoc )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ return OUString::number(rCell.getDouble());
+ case CELLTYPE_STRING:
+ return rCell.getSharedString()->getString();
+ case CELLTYPE_EDIT:
+ if (rCell.getEditText())
+ return ScEditUtil::GetString(*rCell.getEditText(), pDoc);
+ break;
+ case CELLTYPE_FORMULA:
+ return rCell.getFormula()->GetString().getString();
+ default:
+ ;
+ }
+ return OUString();
+}
+
+template<typename CellT>
+OUString getRawStringImpl( const CellT& rCell, const ScDocument& rDoc )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ return OUString::number(rCell.getDouble());
+ case CELLTYPE_STRING:
+ return rCell.getSharedString()->getString();
+ case CELLTYPE_EDIT:
+ if (rCell.getEditText())
+ return ScEditUtil::GetString(*rCell.getEditText(), &rDoc);
+ break;
+ case CELLTYPE_FORMULA:
+ return rCell.getFormula()->GetRawString().getString();
+ default:
+ ;
+ }
+ return OUString();
+}
+
+}
+
+ScCellValue::ScCellValue() {}
+
+ScCellValue::ScCellValue( const ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING:
+ maData = *rCell.getSharedString();
+ break;
+ case CELLTYPE_EDIT:
+ maData = rCell.getEditText()->Clone().release();
+ break;
+ case CELLTYPE_FORMULA:
+ maData = rCell.getFormula()->Clone();
+ break;
+ case CELLTYPE_VALUE:
+ maData = rCell.getDouble();
+ break;
+ default: ;
+ }
+}
+
+ScCellValue::ScCellValue( double fValue ) : maData(fValue) {}
+
+ScCellValue::ScCellValue( const svl::SharedString& rString ) : maData(rString) {}
+
+ScCellValue::ScCellValue( std::unique_ptr<EditTextObject> xEdit ) : maData(xEdit.release()) {}
+
+ScCellValue::ScCellValue( const ScCellValue& r )
+{
+ switch (r.getType())
+ {
+ case CELLTYPE_STRING:
+ maData = *r.getSharedString();
+ break;
+ case CELLTYPE_EDIT:
+ maData = r.getEditText()->Clone().release();
+ break;
+ case CELLTYPE_FORMULA:
+ maData = r.getFormula()->Clone();
+ break;
+ case CELLTYPE_VALUE:
+ maData = r.getDouble();
+ break;
+ default: ;
+ }
+}
+
+void ScCellValue::reset_to_empty()
+{
+ suppress_fun_call_w_exception(maData = std::monostate()); // reset to empty;
+}
+
+ScCellValue::ScCellValue(ScCellValue&& r) noexcept
+ : maData(std::move(r.maData))
+{
+ r.reset_to_empty();
+}
+
+ScCellValue::~ScCellValue()
+{
+ clear();
+}
+
+CellType ScCellValue::getType() const
+{
+ switch (maData.index())
+ {
+ case 0: return CELLTYPE_NONE;
+ case 1: return CELLTYPE_VALUE;
+ case 2: return CELLTYPE_STRING;
+ case 3: return CELLTYPE_EDIT;
+ case 4: return CELLTYPE_FORMULA;
+ default:
+ assert(false);
+ return CELLTYPE_NONE;
+ }
+}
+
+void ScCellValue::clear() noexcept
+{
+ switch (getType())
+ {
+ case CELLTYPE_EDIT:
+ suppress_fun_call_w_exception(delete getEditText());
+ break;
+ case CELLTYPE_FORMULA:
+ suppress_fun_call_w_exception(delete getFormula());
+ break;
+ default:
+ ;
+ }
+
+ // Reset to empty value.
+ reset_to_empty();
+}
+
+void ScCellValue::set( double fValue )
+{
+ clear();
+ maData = fValue;
+}
+
+void ScCellValue::set( const svl::SharedString& rStr )
+{
+ clear();
+ maData = rStr;
+}
+
+void ScCellValue::set( const EditTextObject& rEditText )
+{
+ clear();
+ maData = rEditText.Clone().release();
+}
+
+void ScCellValue::set( std::unique_ptr<EditTextObject> xEditText )
+{
+ clear();
+ maData = xEditText.release();
+}
+
+void ScCellValue::set( ScFormulaCell* pFormula )
+{
+ clear();
+ maData = pFormula;
+}
+
+void ScCellValue::assign( const ScDocument& rDoc, const ScAddress& rPos )
+{
+ clear();
+
+ ScRefCellValue aRefVal(const_cast<ScDocument&>(rDoc), rPos);
+
+ switch (aRefVal.getType())
+ {
+ case CELLTYPE_STRING:
+ maData = *aRefVal.getSharedString();
+ break;
+ case CELLTYPE_EDIT:
+ maData = aRefVal.getEditText() ? aRefVal.getEditText()->Clone().release() : static_cast<EditTextObject*>(nullptr);
+ break;
+ case CELLTYPE_VALUE:
+ maData = aRefVal.getDouble();
+ break;
+ case CELLTYPE_FORMULA:
+ maData = aRefVal.getFormula()->Clone();
+ break;
+ default: ; // leave empty
+ }
+}
+
+void ScCellValue::assign(const ScCellValue& rOther, ScDocument& rDestDoc, ScCloneFlags nCloneFlags)
+{
+ clear();
+
+ switch (rOther.getType())
+ {
+ case CELLTYPE_STRING:
+ maData = rOther.maData;
+ break;
+ case CELLTYPE_EDIT:
+ {
+ // Switch to the pool of the destination document.
+ ScFieldEditEngine& rEngine = rDestDoc.GetEditEngine();
+ if (rOther.getEditText()->HasOnlineSpellErrors())
+ {
+ EEControlBits nControl = rEngine.GetControlWord();
+ const EEControlBits nSpellControl = EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS;
+ bool bNewControl = ((nControl & nSpellControl) != nSpellControl);
+ if (bNewControl)
+ rEngine.SetControlWord(nControl | nSpellControl);
+ rEngine.SetTextCurrentDefaults(*rOther.getEditText());
+ maData = rEngine.CreateTextObject().release();
+ if (bNewControl)
+ rEngine.SetControlWord(nControl);
+ }
+ else
+ {
+ rEngine.SetTextCurrentDefaults(*rOther.getEditText());
+ maData = rEngine.CreateTextObject().release();
+ }
+ }
+ break;
+ case CELLTYPE_VALUE:
+ maData = rOther.maData;
+ break;
+ case CELLTYPE_FORMULA:
+ // Switch to the destination document.
+ maData = new ScFormulaCell(*rOther.getFormula(), rDestDoc, rOther.getFormula()->aPos, nCloneFlags);
+ break;
+ default: ; // leave empty
+ }
+}
+
+void ScCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const
+{
+ switch (getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(rPos, getSharedString()->getString(), &aParam);
+ }
+ break;
+ case CELLTYPE_EDIT:
+ rDoc.SetEditText(rPos, getEditText()->Clone());
+ break;
+ case CELLTYPE_VALUE:
+ rDoc.SetValue(rPos, getDouble());
+ break;
+ case CELLTYPE_FORMULA:
+ rDoc.SetFormulaCell(rPos, getFormula()->Clone());
+ break;
+ default:
+ rDoc.SetEmptyCell(rPos);
+ }
+}
+
+void ScCellValue::commit( ScColumn& rColumn, SCROW nRow ) const
+{
+ commitToColumn(*this, rColumn, nRow);
+}
+
+void ScCellValue::release( ScDocument& rDoc, const ScAddress& rPos )
+{
+ switch (getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ // Currently, string cannot be placed without copying.
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(rPos, getSharedString()->getString(), &aParam);
+ }
+ break;
+ case CELLTYPE_EDIT:
+ // Cell takes the ownership of the text object.
+ rDoc.SetEditText(rPos, std::unique_ptr<EditTextObject>(getEditText()));
+ break;
+ case CELLTYPE_VALUE:
+ rDoc.SetValue(rPos, getDouble());
+ break;
+ case CELLTYPE_FORMULA:
+ // This formula cell instance is directly placed in the document without copying.
+ rDoc.SetFormulaCell(rPos, getFormula());
+ break;
+ default:
+ rDoc.SetEmptyCell(rPos);
+ }
+
+ reset_to_empty(); // reset to empty
+}
+
+void ScCellValue::release( ScColumn& rColumn, SCROW nRow, sc::StartListeningType eListenType )
+{
+ switch (getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ // Currently, string cannot be placed without copying.
+ rColumn.SetRawString(nRow, *getSharedString());
+ }
+ break;
+ case CELLTYPE_EDIT:
+ // Cell takes the ownership of the text object.
+ rColumn.SetEditText(nRow, std::unique_ptr<EditTextObject>(getEditText()));
+ break;
+ case CELLTYPE_VALUE:
+ rColumn.SetValue(nRow, getDouble());
+ break;
+ case CELLTYPE_FORMULA:
+ // This formula cell instance is directly placed in the document without copying.
+ rColumn.SetFormulaCell(nRow, getFormula(), eListenType);
+ break;
+ default:
+ rColumn.DeleteContent(nRow);
+ }
+
+ reset_to_empty(); // reset to empty
+}
+
+OUString ScCellValue::getString( const ScDocument& rDoc ) const
+{
+ return getStringImpl(*this, &rDoc);
+}
+
+bool ScCellValue::isEmpty() const
+{
+ return getType() == CELLTYPE_NONE;
+}
+
+bool ScCellValue::equalsWithoutFormat( const ScCellValue& r ) const
+{
+ return equalsWithoutFormatImpl(*this, r);
+}
+
+ScCellValue& ScCellValue::operator= ( const ScCellValue& r )
+{
+ ScCellValue aTmp(r);
+ swap(aTmp);
+ return *this;
+}
+
+ScCellValue& ScCellValue::operator=(ScCellValue&& rCell) noexcept
+{
+ clear();
+ maData = std::move(rCell.maData);
+ rCell.reset_to_empty(); // reset to empty;
+ return *this;
+}
+
+ScCellValue& ScCellValue::operator= ( const ScRefCellValue& r )
+{
+ ScCellValue aTmp(r);
+ swap(aTmp);
+ return *this;
+}
+
+void ScCellValue::swap( ScCellValue& r )
+{
+ std::swap(maData, r.maData);
+}
+
+ScRefCellValue::ScRefCellValue() : meType(CELLTYPE_NONE), mfValue(0.0) {}
+ScRefCellValue::ScRefCellValue( double fValue ) : meType(CELLTYPE_VALUE), mfValue(fValue) {}
+ScRefCellValue::ScRefCellValue( const svl::SharedString* pString ) : meType(CELLTYPE_STRING), mpString(pString) {}
+ScRefCellValue::ScRefCellValue( const EditTextObject* pEditText ) : meType(CELLTYPE_EDIT), mpEditText(pEditText) {}
+ScRefCellValue::ScRefCellValue( ScFormulaCell* pFormula ) : meType(CELLTYPE_FORMULA), mpFormula(pFormula) {}
+
+ScRefCellValue::ScRefCellValue( ScDocument& rDoc, const ScAddress& rPos )
+{
+ assign( rDoc, rPos);
+}
+
+ScRefCellValue::ScRefCellValue( ScDocument& rDoc, const ScAddress& rPos, sc::ColumnBlockPosition& rBlockPos )
+{
+ assign( rDoc, rPos, rBlockPos );
+}
+
+void ScRefCellValue::clear()
+{
+ // Reset to empty value.
+ meType = CELLTYPE_NONE;
+ mfValue = 0.0;
+}
+
+void ScRefCellValue::assign( ScDocument& rDoc, const ScAddress& rPos )
+{
+ *this = rDoc.GetRefCellValue(rPos);
+}
+
+void ScRefCellValue::assign( ScDocument& rDoc, const ScAddress& rPos, sc::ColumnBlockPosition& rBlockPos )
+{
+ *this = rDoc.GetRefCellValue(rPos, rBlockPos);
+}
+
+void ScRefCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const
+{
+ switch (meType)
+ {
+ case CELLTYPE_STRING:
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(rPos, mpString->getString(), &aParam);
+ }
+ break;
+ case CELLTYPE_EDIT:
+ rDoc.SetEditText(rPos, ScEditUtil::Clone(*mpEditText, rDoc));
+ break;
+ case CELLTYPE_VALUE:
+ rDoc.SetValue(rPos, mfValue);
+ break;
+ case CELLTYPE_FORMULA:
+ rDoc.SetFormulaCell(rPos, new ScFormulaCell(*mpFormula, rDoc, rPos));
+ break;
+ default:
+ rDoc.SetEmptyCell(rPos);
+ }
+}
+
+bool ScRefCellValue::hasString() const
+{
+ return hasStringImpl(meType, mpFormula);
+}
+
+bool ScRefCellValue::hasNumeric() const
+{
+ return hasNumericImpl(meType, mpFormula);
+}
+
+bool ScRefCellValue::hasError() const
+{
+ return meType == CELLTYPE_FORMULA && mpFormula->GetErrCode() != FormulaError::NONE;
+}
+
+double ScRefCellValue::getValue()
+{
+ switch (meType)
+ {
+ case CELLTYPE_VALUE:
+ return mfValue;
+ case CELLTYPE_FORMULA:
+ return mpFormula->GetValue();
+ default:
+ ;
+ }
+ return 0.0;
+}
+
+double ScRefCellValue::getRawValue() const
+{
+ switch (meType)
+ {
+ case CELLTYPE_VALUE:
+ return mfValue;
+ case CELLTYPE_FORMULA:
+ return mpFormula->GetRawValue();
+ default:
+ ;
+ }
+ return 0.0;
+}
+
+OUString ScRefCellValue::getString( const ScDocument* pDoc ) const
+{
+ return getStringImpl(*this, pDoc);
+}
+
+OUString ScRefCellValue::getRawString( const ScDocument& rDoc ) const
+{
+ return getRawStringImpl(*this, rDoc);
+}
+
+bool ScRefCellValue::isEmpty() const
+{
+ return meType == CELLTYPE_NONE;
+}
+
+bool ScRefCellValue::hasEmptyValue()
+{
+ if (isEmpty())
+ return true;
+
+ if (meType == CELLTYPE_FORMULA)
+ return mpFormula->IsEmpty();
+
+ return false;
+}
+
+bool ScRefCellValue::equalsWithoutFormat( const ScRefCellValue& r ) const
+{
+ return equalsWithoutFormatImpl(*this, r);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/cellvalues.cxx b/sc/source/core/data/cellvalues.cxx
new file mode 100644
index 0000000000..d23d7a9ecc
--- /dev/null
+++ b/sc/source/core/data/cellvalues.cxx
@@ -0,0 +1,364 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <cellvalues.hxx>
+#include <column.hxx>
+#include <formulacell.hxx>
+#include <editeng/editobj.hxx>
+
+#include <cassert>
+
+namespace sc {
+
+namespace {
+
+struct BlockPos
+{
+ size_t mnStart;
+ size_t mnEnd;
+};
+
+}
+
+CellValueSpan::CellValueSpan( SCROW nRow1, SCROW nRow2 ) :
+ mnRow1(nRow1), mnRow2(nRow2) {}
+
+struct CellValuesImpl
+{
+ CellStoreType maCells;
+ CellTextAttrStoreType maCellTextAttrs;
+ CellStoreType::iterator miCellPos;
+ CellTextAttrStoreType::iterator miAttrPos;
+
+ CellValuesImpl() = default;
+
+ CellValuesImpl(const CellValuesImpl&) = delete;
+ const CellValuesImpl& operator=(const CellValuesImpl&) = delete;
+};
+
+CellValues::CellValues() :
+ mpImpl(new CellValuesImpl) {}
+
+CellValues::~CellValues()
+{
+}
+
+void CellValues::transferFrom( ScColumn& rCol, SCROW nRow, size_t nLen )
+{
+ mpImpl->maCells.resize(nLen);
+ mpImpl->maCellTextAttrs.resize(nLen);
+ rCol.maCells.transfer(nRow, nRow+nLen-1, mpImpl->maCells, 0);
+ rCol.maCellTextAttrs.transfer(nRow, nRow+nLen-1, mpImpl->maCellTextAttrs, 0);
+}
+
+
+void CellValues::copyTo( ScColumn& rCol, SCROW nRow ) const
+{
+ copyCellsTo(rCol, nRow);
+ copyCellTextAttrsTo(rCol, nRow);
+}
+
+void CellValues::swapNonEmpty( ScColumn& rCol )
+{
+ std::vector<BlockPos> aBlocksToSwap;
+
+ // Go through static value blocks and record their positions and sizes.
+ for (const auto& rCell : mpImpl->maCells)
+ {
+ if (rCell.type == sc::element_type_empty)
+ continue;
+
+ BlockPos aPos;
+ aPos.mnStart = rCell.position;
+ aPos.mnEnd = aPos.mnStart + rCell.size - 1;
+ aBlocksToSwap.push_back(aPos);
+ }
+
+ // Do the swapping. The undo storage will store the replaced formula cells after this.
+ for (const auto& rBlock : aBlocksToSwap)
+ {
+ rCol.maCells.swap(rBlock.mnStart, rBlock.mnEnd, mpImpl->maCells, rBlock.mnStart);
+ rCol.maCellTextAttrs.swap(rBlock.mnStart, rBlock.mnEnd, mpImpl->maCellTextAttrs, rBlock.mnStart);
+ }
+}
+
+void CellValues::assign( const std::vector<double>& rVals )
+{
+ mpImpl->maCells.resize(rVals.size());
+ mpImpl->maCells.set(0, rVals.begin(), rVals.end());
+
+ // Set default text attributes.
+ std::vector<CellTextAttr> aDefaults(rVals.size(), CellTextAttr());
+ mpImpl->maCellTextAttrs.resize(rVals.size());
+ mpImpl->maCellTextAttrs.set(0, aDefaults.begin(), aDefaults.end());
+}
+
+void CellValues::assign( const std::vector<ScFormulaCell*>& rVals )
+{
+ std::vector<ScFormulaCell*> aCopyVals(rVals.size());
+ size_t nIdx = 0;
+ for (const auto* pCell : rVals)
+ {
+ aCopyVals[nIdx] = pCell->Clone();
+ ++nIdx;
+ }
+
+ mpImpl->maCells.resize(aCopyVals.size());
+ mpImpl->maCells.set(0, aCopyVals.begin(), aCopyVals.end());
+
+ // Set default text attributes.
+ std::vector<CellTextAttr> aDefaults(rVals.size(), CellTextAttr());
+ mpImpl->maCellTextAttrs.resize(rVals.size());
+ mpImpl->maCellTextAttrs.set(0, aDefaults.begin(), aDefaults.end());
+}
+
+size_t CellValues::size() const
+{
+ assert(mpImpl->maCells.size() == mpImpl->maCellTextAttrs.size());
+ return mpImpl->maCells.size();
+}
+
+void CellValues::reset( size_t nSize )
+{
+ mpImpl->maCells.clear();
+ mpImpl->maCells.resize(nSize);
+ mpImpl->maCellTextAttrs.clear();
+ mpImpl->maCellTextAttrs.resize(nSize);
+
+ mpImpl->miCellPos = mpImpl->maCells.begin();
+ mpImpl->miAttrPos = mpImpl->maCellTextAttrs.begin();
+}
+
+void CellValues::setValue( size_t nRow, double fVal )
+{
+ mpImpl->miCellPos = mpImpl->maCells.set(mpImpl->miCellPos, nRow, fVal);
+ mpImpl->miAttrPos = mpImpl->maCellTextAttrs.set(mpImpl->miAttrPos, nRow, sc::CellTextAttr());
+}
+
+void CellValues::setValue( size_t nRow, const svl::SharedString& rStr )
+{
+ mpImpl->miCellPos = mpImpl->maCells.set(mpImpl->miCellPos, nRow, rStr);
+ mpImpl->miAttrPos = mpImpl->maCellTextAttrs.set(mpImpl->miAttrPos, nRow, sc::CellTextAttr());
+}
+
+void CellValues::setValue( size_t nRow, std::unique_ptr<EditTextObject> pEditText )
+{
+ mpImpl->miCellPos = mpImpl->maCells.set(mpImpl->miCellPos, nRow, pEditText.release());
+ mpImpl->miAttrPos = mpImpl->maCellTextAttrs.set(mpImpl->miAttrPos, nRow, sc::CellTextAttr());
+}
+
+void CellValues::swap( CellValues& r )
+{
+ std::swap(mpImpl, r.mpImpl);
+}
+
+std::vector<CellValueSpan> CellValues::getNonEmptySpans() const
+{
+ std::vector<CellValueSpan> aRet;
+ for (const auto& rCell : mpImpl->maCells)
+ {
+ if (rCell.type != element_type_empty)
+ {
+ // Record this span.
+ size_t nRow1 = rCell.position;
+ size_t nRow2 = nRow1 + rCell.size - 1;
+ aRet.emplace_back(nRow1, nRow2);
+ }
+ }
+ return aRet;
+}
+
+void CellValues::copyCellsTo( ScColumn& rCol, SCROW nRow ) const
+{
+ CellStoreType& rDest = rCol.maCells;
+ const CellStoreType& rSrc = mpImpl->maCells;
+
+ // Caller must ensure the destination is long enough.
+ assert(rSrc.size() + static_cast<size_t>(nRow) <= rDest.size());
+
+ SCROW nCurRow = nRow;
+ CellStoreType::iterator itPos = rDest.begin();
+
+ for (const auto& rBlk : rSrc)
+ {
+ switch (rBlk.type)
+ {
+ case element_type_numeric:
+ {
+ numeric_block::const_iterator it = numeric_block::begin(*rBlk.data);
+ numeric_block::const_iterator itEnd = numeric_block::end(*rBlk.data);
+ itPos = rDest.set(itPos, nCurRow, it, itEnd);
+ }
+ break;
+ case element_type_string:
+ {
+ string_block::const_iterator it = string_block::begin(*rBlk.data);
+ string_block::const_iterator itEnd = string_block::end(*rBlk.data);
+ itPos = rDest.set(itPos, nCurRow, it, itEnd);
+ }
+ break;
+ case element_type_edittext:
+ {
+ edittext_block::const_iterator it = edittext_block::begin(*rBlk.data);
+ edittext_block::const_iterator itEnd = edittext_block::end(*rBlk.data);
+ std::vector<EditTextObject*> aVals;
+ aVals.reserve(rBlk.size);
+ for (; it != itEnd; ++it)
+ {
+ const EditTextObject* p = *it;
+ aVals.push_back(p->Clone().release());
+ }
+ itPos = rDest.set(itPos, nCurRow, aVals.begin(), aVals.end());
+ }
+ break;
+ case element_type_formula:
+ {
+ formula_block::const_iterator it = formula_block::begin(*rBlk.data);
+ formula_block::const_iterator itEnd = formula_block::end(*rBlk.data);
+ std::vector<ScFormulaCell*> aVals;
+ aVals.reserve(rBlk.size);
+ for (; it != itEnd; ++it)
+ {
+ const ScFormulaCell* p = *it;
+ aVals.push_back(p->Clone());
+ }
+ itPos = rDest.set(itPos, nCurRow, aVals.begin(), aVals.end());
+ }
+ break;
+ default:
+ itPos = rDest.set_empty(itPos, nCurRow, nCurRow+rBlk.size-1);
+ }
+
+ nCurRow += rBlk.size;
+ }
+}
+
+void CellValues::copyCellTextAttrsTo( ScColumn& rCol, SCROW nRow ) const
+{
+ CellTextAttrStoreType& rDest = rCol.maCellTextAttrs;
+ const CellTextAttrStoreType& rSrc = mpImpl->maCellTextAttrs;
+
+ // Caller must ensure the destination is long enough.
+ assert(rSrc.size() + static_cast<size_t>(nRow) <= rDest.size());
+
+ SCROW nCurRow = nRow;
+ CellTextAttrStoreType::iterator itPos = rDest.begin();
+
+ for (const auto& rBlk : rSrc)
+ {
+ switch (rBlk.type)
+ {
+ case element_type_celltextattr:
+ {
+ celltextattr_block::const_iterator it = celltextattr_block::begin(*rBlk.data);
+ celltextattr_block::const_iterator itEnd = celltextattr_block::end(*rBlk.data);
+ itPos = rDest.set(itPos, nCurRow, it, itEnd);
+ }
+ break;
+ default:
+ itPos = rDest.set_empty(itPos, nCurRow, nCurRow+rBlk.size-1);
+ }
+
+ nCurRow += rBlk.size;
+ }
+}
+
+typedef std::vector<std::unique_ptr<CellValues>> TableType;
+typedef std::vector<std::unique_ptr<TableType>> TablesType;
+
+struct TableValues::Impl
+{
+ ScRange maRange;
+ TablesType m_Tables;
+
+ explicit Impl( const ScRange& rRange ) : maRange(rRange)
+ {
+ size_t nTabs = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+ size_t nCols = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+
+ for (size_t nTab = 0; nTab < nTabs; ++nTab)
+ {
+ m_Tables.push_back(std::make_unique<TableType>());
+ std::unique_ptr<TableType>& rTab2 = m_Tables.back();
+ for (size_t nCol = 0; nCol < nCols; ++nCol)
+ rTab2->push_back(std::make_unique<CellValues>());
+ }
+ }
+
+ CellValues* getCellValues( SCTAB nTab, SCCOL nCol )
+ {
+ if (nTab < maRange.aStart.Tab() || maRange.aEnd.Tab() < nTab)
+ // sheet index out of bound.
+ return nullptr;
+ if (nCol < maRange.aStart.Col() || maRange.aEnd.Col() < nCol)
+ // column index out of bound.
+ return nullptr;
+ size_t nTabOffset = nTab - maRange.aStart.Tab();
+ if (nTabOffset >= m_Tables.size())
+ return nullptr;
+ std::unique_ptr<TableType>& rTab2 = m_Tables[nTab-maRange.aStart.Tab()];
+ size_t nColOffset = nCol - maRange.aStart.Col();
+ if (nColOffset >= rTab2->size())
+ return nullptr;
+ return &rTab2.get()[0][nColOffset].get()[0];
+ }
+};
+
+TableValues::TableValues() :
+ mpImpl(new Impl(ScRange(ScAddress::INITIALIZE_INVALID))) {}
+
+TableValues::TableValues( const ScRange& rRange ) :
+ mpImpl(new Impl(rRange)) {}
+
+TableValues::~TableValues()
+{
+}
+
+const ScRange& TableValues::getRange() const
+{
+ return mpImpl->maRange;
+}
+
+void TableValues::swap( SCTAB nTab, SCCOL nCol, CellValues& rColValue )
+{
+ CellValues* pCol = mpImpl->getCellValues(nTab, nCol);
+ if (!pCol)
+ return;
+
+ pCol->swap(rColValue);
+}
+
+void TableValues::swapNonEmpty( SCTAB nTab, SCCOL nCol, ScColumn& rCol )
+{
+ CellValues* pCol = mpImpl->getCellValues(nTab, nCol);
+ if (!pCol)
+ return;
+
+ pCol->swapNonEmpty(rCol);
+}
+
+std::vector<CellValueSpan> TableValues::getNonEmptySpans( SCTAB nTab, SCCOL nCol ) const
+{
+ std::vector<CellValueSpan> aRet;
+ CellValues* pCol = mpImpl->getCellValues(nTab, nCol);
+ if (pCol)
+ aRet = pCol->getNonEmptySpans();
+
+ return aRet;
+}
+
+void TableValues::swap( TableValues& rOther )
+{
+ std::swap(mpImpl, rOther.mpImpl);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/clipcontext.cxx b/sc/source/core/data/clipcontext.cxx
new file mode 100644
index 0000000000..ce6974d423
--- /dev/null
+++ b/sc/source/core/data/clipcontext.cxx
@@ -0,0 +1,436 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <clipcontext.hxx>
+#include <document.hxx>
+#include <mtvelements.hxx>
+#include <column.hxx>
+#include <scitems.hxx>
+#include <tokenarray.hxx>
+#include <editutil.hxx>
+#include <clipparam.hxx>
+
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <formula/errorcodes.hxx>
+#include <refdata.hxx>
+#include <listenercontext.hxx>
+
+namespace sc {
+
+ClipContextBase::ClipContextBase(ScDocument& rDoc) :
+ mpSet(new ColumnBlockPositionSet(rDoc)) {}
+
+ClipContextBase::~ClipContextBase() {}
+
+ColumnBlockPosition* ClipContextBase::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+ return mpSet->getBlockPosition(nTab, nCol);
+}
+
+CopyFromClipContext::CopyFromClipContext(ScDocument& rDoc,
+ ScDocument* pRefUndoDoc, ScDocument* pClipDoc, InsertDeleteFlags nInsertFlag,
+ bool bAsLink, bool bSkipEmptyCells) :
+ ClipContextBase(rDoc),
+ mnDestCol1(-1), mnDestCol2(-1),
+ mnDestRow1(-1), mnDestRow2(-1),
+ mnTabStart(-1), mnTabEnd(-1),
+ mrDestDoc(rDoc),
+ mpRefUndoDoc(pRefUndoDoc), mpClipDoc(pClipDoc),
+ mnInsertFlag(nInsertFlag), mnDeleteFlag(InsertDeleteFlags::NONE),
+ mpCondFormatList(nullptr),
+ mbAsLink(bAsLink), mbSkipEmptyCells(bSkipEmptyCells),
+ mbTableProtected(false)
+{
+}
+
+CopyFromClipContext::~CopyFromClipContext()
+{
+}
+
+void CopyFromClipContext::setTabRange(SCTAB nStart, SCTAB nEnd)
+{
+ mnTabStart = nStart;
+ mnTabEnd = nEnd;
+}
+
+SCTAB CopyFromClipContext::getTabStart() const
+{
+ return mnTabStart;
+}
+
+SCTAB CopyFromClipContext::getTabEnd() const
+{
+ return mnTabEnd;
+}
+
+void CopyFromClipContext::setDestRange( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ mnDestCol1 = nCol1;
+ mnDestRow1 = nRow1;
+ mnDestCol2 = nCol2;
+ mnDestRow2 = nRow2;
+}
+
+CopyFromClipContext::Range CopyFromClipContext::getDestRange() const
+{
+ Range aRet;
+ aRet.mnCol1 = mnDestCol1;
+ aRet.mnCol2 = mnDestCol2;
+ aRet.mnRow1 = mnDestRow1;
+ aRet.mnRow2 = mnDestRow2;
+ return aRet;
+}
+
+ScDocument* CopyFromClipContext::getUndoDoc()
+{
+ return mpRefUndoDoc;
+}
+
+ScDocument* CopyFromClipContext::getClipDoc()
+{
+ return mpClipDoc;
+}
+
+InsertDeleteFlags CopyFromClipContext::getInsertFlag() const
+{
+ return mnInsertFlag;
+}
+
+void CopyFromClipContext::setDeleteFlag( InsertDeleteFlags nFlag )
+{
+ mnDeleteFlag = nFlag;
+}
+
+InsertDeleteFlags CopyFromClipContext::getDeleteFlag() const
+{
+ return mnDeleteFlag;
+}
+
+void CopyFromClipContext::setListeningFormulaSpans(
+ SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ maListeningFormulaSpans.set(mrDestDoc, nTab, nCol, nRow1, nRow2, true);
+}
+
+namespace {
+
+class StartListeningAction : public sc::ColumnSpanSet::Action
+{
+ ScDocument& mrDestDoc;
+ sc::StartListeningContext& mrStartCxt;
+ sc::EndListeningContext& mrEndCxt;
+
+public:
+ StartListeningAction( ScDocument& rDestDoc, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
+ mrDestDoc(rDestDoc), mrStartCxt(rStartCxt), mrEndCxt(rEndCxt)
+ {
+ }
+
+ virtual void execute( const ScAddress& rPos, SCROW nLength, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ SCROW nRow1 = rPos.Row();
+ SCROW nRow2 = nRow1 + nLength - 1;
+
+ mrDestDoc.StartListeningFromClip(
+ mrStartCxt, mrEndCxt, rPos.Tab(), rPos.Col(), nRow1, rPos.Col(), nRow2);
+ }
+};
+
+}
+
+void CopyFromClipContext::startListeningFormulas()
+{
+ auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(mrDestDoc);
+ sc::StartListeningContext aStartCxt(mrDestDoc, pSet);
+ sc::EndListeningContext aEndCxt(mrDestDoc, pSet, nullptr);
+
+ StartListeningAction aAction(mrDestDoc, aStartCxt, aEndCxt);
+ maListeningFormulaSpans.executeAction(mrDestDoc, aAction);
+}
+
+void CopyFromClipContext::setSingleCellColumnSize( size_t nSize )
+{
+ maSingleCells.resize(nSize);
+ maSingleCellAttrs.resize(nSize);
+ maSinglePatterns.resize(nSize, nullptr);
+ maSingleNotes.resize(nSize, nullptr);
+ maSingleSparkline.resize(nSize);
+}
+
+ScCellValue& CopyFromClipContext::getSingleCell( size_t nColOffset )
+{
+ assert(nColOffset < maSingleCells.size());
+ return maSingleCells[nColOffset];
+}
+
+sc::CellTextAttr& CopyFromClipContext::getSingleCellAttr( size_t nColOffset )
+{
+ assert(nColOffset < maSingleCellAttrs.size());
+ return maSingleCellAttrs[nColOffset];
+}
+
+void CopyFromClipContext::setSingleCell( const ScAddress& rSrcPos, const ScColumn& rSrcCol )
+{
+ SCCOL nColOffset = rSrcPos.Col() - mpClipDoc->GetClipParam().getWholeRange().aStart.Col();
+ ScCellValue& rSrcCell = getSingleCell(nColOffset);
+
+ const sc::CellTextAttr* pAttr = rSrcCol.GetCellTextAttr(rSrcPos.Row());
+
+ if (pAttr)
+ {
+ sc::CellTextAttr& rAttr = getSingleCellAttr(nColOffset);
+ rAttr = *pAttr;
+ }
+
+ if (mbAsLink)
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(rSrcPos);
+ aRef.SetFlag3D(true);
+
+ ScTokenArray aArr(*mpClipDoc);
+ aArr.AddSingleReference(aRef);
+ rSrcCell.set(new ScFormulaCell(*mpClipDoc, rSrcPos, aArr));
+ return;
+ }
+
+ rSrcCell.assign(*mpClipDoc, rSrcPos);
+
+ // Check the paste flag to see whether we want to paste this cell. If the
+ // flag says we don't want to paste this cell, we'll return with true.
+ InsertDeleteFlags nFlags = getInsertFlag();
+ bool bNumeric = (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
+ bool bDateTime = (nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE;
+ bool bString = (nFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE;
+ bool bBoolean = (nFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE;
+ bool bFormula = (nFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE;
+
+ switch (rSrcCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ bool bPaste = isDateCell(rSrcCol, rSrcPos.Row()) ? bDateTime : bNumeric;
+ if (!bPaste)
+ // Don't paste this.
+ rSrcCell.clear();
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ if (!bString)
+ // Skip pasting.
+ rSrcCell.clear();
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ if (bBoolean)
+ {
+ // Check if this formula cell is a boolean cell, and if so, go ahead and paste it.
+ const ScTokenArray* pCode = rSrcCell.getFormula()->GetCode();
+ if (pCode && pCode->GetLen() == 1)
+ {
+ const formula::FormulaToken* p = pCode->FirstToken();
+ if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
+ // This is a boolean formula. Good.
+ break;
+ }
+ }
+
+ if (bFormula)
+ // Good.
+ break;
+
+ FormulaError nErr = rSrcCell.getFormula()->GetErrCode();
+ if (nErr != FormulaError::NONE)
+ {
+ // error codes are cloned with values
+ if (!bNumeric)
+ // Error code is treated as numeric value. Don't paste it.
+ rSrcCell.clear();
+ else
+ {
+ // Turn this into a formula cell with just the error code.
+ ScFormulaCell* pErrCell = new ScFormulaCell(*mpClipDoc, rSrcPos);
+ pErrCell->SetErrCode(nErr);
+ rSrcCell.set(pErrCell);
+ }
+ }
+ else if (rSrcCell.getFormula()->IsEmptyDisplayedAsString())
+ {
+ // Empty stays empty and doesn't become 0.
+ rSrcCell.clear();
+ }
+ else if (rSrcCell.getFormula()->IsValue())
+ {
+ bool bPaste = isDateCell(rSrcCol, rSrcPos.Row()) ? bDateTime : bNumeric;
+ if (!bPaste)
+ {
+ // Don't paste this.
+ rSrcCell.clear();
+ break;
+ }
+
+ // Turn this into a numeric cell.
+ rSrcCell.set(rSrcCell.getFormula()->GetValue());
+ }
+ else if (bString)
+ {
+ svl::SharedString aStr = rSrcCell.getFormula()->GetString();
+ if (aStr.isEmpty())
+ {
+ // do not clone empty string
+ rSrcCell.clear();
+ break;
+ }
+
+ // Turn this into a string or edit cell.
+ if (rSrcCell.getFormula()->IsMultilineResult())
+ {
+ std::unique_ptr<EditTextObject> pObj(mrDestDoc.CreateSharedStringTextObject(
+ rSrcCell.getFormula()->GetString()));
+ rSrcCell.set(*pObj);
+ }
+ else
+ rSrcCell.set(rSrcCell.getFormula()->GetString());
+ }
+ else
+ // We don't want to paste this.
+ rSrcCell.clear();
+ }
+ break;
+ case CELLTYPE_NONE:
+ default:
+ // There is nothing to paste.
+ rSrcCell.clear();
+ }
+}
+
+const ScPatternAttr* CopyFromClipContext::getSingleCellPattern( size_t nColOffset ) const
+{
+ assert(nColOffset < maSinglePatterns.size());
+ return maSinglePatterns[nColOffset];
+}
+
+void CopyFromClipContext::setSingleCellPattern( size_t nColOffset, const ScPatternAttr* pAttr )
+{
+ assert(nColOffset < maSinglePatterns.size());
+ maSinglePatterns[nColOffset] = pAttr;
+}
+
+const ScPostIt* CopyFromClipContext::getSingleCellNote( size_t nColOffset ) const
+{
+ assert(nColOffset < maSingleNotes.size());
+ return maSingleNotes[nColOffset];
+}
+
+void CopyFromClipContext::setSingleCellNote( size_t nColOffset, const ScPostIt* pNote )
+{
+ assert(nColOffset < maSingleNotes.size());
+ maSingleNotes[nColOffset] = pNote;
+}
+
+std::shared_ptr<sc::Sparkline> const& CopyFromClipContext::getSingleSparkline(size_t nColOffset) const
+{
+ assert(nColOffset < maSingleSparkline.size());
+ return maSingleSparkline[nColOffset];
+}
+
+void CopyFromClipContext::setSingleSparkline(size_t nColOffset, std::shared_ptr<sc::Sparkline> const& pSparkline)
+{
+ assert(nColOffset < maSingleSparkline.size());
+ maSingleSparkline[nColOffset] = pSparkline;
+}
+
+void CopyFromClipContext::setCondFormatList( ScConditionalFormatList* pCondFormatList )
+{
+ mpCondFormatList = pCondFormatList;
+}
+
+ScConditionalFormatList* CopyFromClipContext::getCondFormatList()
+{
+ return mpCondFormatList;
+}
+
+void CopyFromClipContext::setTableProtected( bool b )
+{
+ mbTableProtected = b;
+}
+
+bool CopyFromClipContext::isTableProtected() const
+{
+ return mbTableProtected;
+}
+
+bool CopyFromClipContext::isAsLink() const
+{
+ return mbAsLink;
+}
+
+bool CopyFromClipContext::isSkipEmptyCells() const
+{
+ return mbSkipEmptyCells;
+}
+
+bool CopyFromClipContext::isCloneNotes() const
+{
+ return bool(mnInsertFlag & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES));
+}
+
+bool CopyFromClipContext::isCloneSparklines() const
+{
+ return bool(mnInsertFlag & InsertDeleteFlags::SPARKLINES);
+}
+
+bool CopyFromClipContext::isDateCell( const ScColumn& rCol, SCROW nRow ) const
+{
+ sal_uLong nNumIndex = rCol.GetAttr(nRow, ATTR_VALUE_FORMAT).GetValue();
+ SvNumFormatType nType = mpClipDoc->GetFormatTable()->GetType(nNumIndex);
+ return (nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) || (nType == SvNumFormatType::DATETIME);
+}
+
+CopyToClipContext::CopyToClipContext(
+ ScDocument& rDoc, bool bKeepScenarioFlags) :
+ ClipContextBase(rDoc), mbKeepScenarioFlags(bKeepScenarioFlags) {}
+
+CopyToClipContext::~CopyToClipContext() {}
+
+bool CopyToClipContext::isKeepScenarioFlags() const
+{
+ return mbKeepScenarioFlags;
+}
+
+CopyToDocContext::CopyToDocContext(ScDocument& rDoc) :
+ ClipContextBase(rDoc), mbStartListening(true) {}
+
+CopyToDocContext::~CopyToDocContext() {}
+
+void CopyToDocContext::setStartListening( bool b )
+{
+ mbStartListening = b;
+}
+
+bool CopyToDocContext::isStartListening() const
+{
+ return mbStartListening;
+}
+
+MixDocContext::MixDocContext(ScDocument& rDoc) : ClipContextBase(rDoc) {}
+MixDocContext::~MixDocContext() {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/clipparam.cxx b/sc/source/core/data/clipparam.cxx
new file mode 100644
index 0000000000..c2255adee3
--- /dev/null
+++ b/sc/source/core/data/clipparam.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <clipparam.hxx>
+
+
+ScClipParam::ScClipParam() :
+ meDirection(Unspecified),
+ mbCutMode(false),
+ mnSourceDocID(0)
+{
+}
+
+ScClipParam::ScClipParam(const ScRange& rRange, bool bCutMode) :
+ meDirection(Unspecified),
+ mbCutMode(bCutMode),
+ mnSourceDocID(0)
+{
+ maRanges.push_back(rRange);
+}
+
+bool ScClipParam::isMultiRange() const
+{
+ return maRanges.size() > 1;
+}
+
+SCCOL ScClipParam::getPasteColSize()
+{
+ if (maRanges.empty())
+ return 0;
+
+ switch (meDirection)
+ {
+ case ScClipParam::Column:
+ {
+ SCCOL nColSize = 0;
+ for ( size_t i = 0, nListSize = maRanges.size(); i < nListSize; ++i )
+ {
+ const ScRange& rRange = maRanges[ i ];
+ nColSize += rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ }
+ return nColSize;
+ }
+ case ScClipParam::Row:
+ {
+ // We assume that all ranges have identical column size.
+ const ScRange& rRange = maRanges.front();
+ return rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ }
+ case ScClipParam::Unspecified:
+ default:
+ ;
+ }
+ return 0;
+}
+
+SCROW ScClipParam::getPasteRowSize(const ScDocument& rSrcDoc, bool bIncludeFiltered)
+{
+ if (maRanges.empty())
+ return 0;
+
+ switch (meDirection)
+ {
+ case ScClipParam::Column:
+ {
+ // We assume that all ranges have identical row size.
+ const ScRange& rRange = maRanges.front();
+ return bIncludeFiltered
+ ? rRange.aEnd.Row() - rRange.aStart.Row() + 1
+ : rSrcDoc.CountNonFilteredRows(rRange.aStart.Row(), rRange.aEnd.Row(),
+ rRange.aStart.Tab());
+ }
+ case ScClipParam::Row:
+ {
+ SCROW nRowSize = 0;
+ for ( size_t i = 0, nListSize = maRanges.size(); i < nListSize; ++i )
+ {
+ const ScRange& rRange = maRanges[ i ];
+ nRowSize += bIncludeFiltered ? rRange.aEnd.Row() - rRange.aStart.Row() + 1
+ : rSrcDoc.CountNonFilteredRows(rRange.aStart.Row(),
+ rRange.aEnd.Row(),
+ rRange.aStart.Tab());
+ }
+ return nRowSize;
+ }
+ case ScClipParam::Unspecified:
+ default:
+ ;
+ }
+ return 0;
+}
+
+ScRange ScClipParam::getWholeRange() const
+{
+ return maRanges.Combine();
+}
+
+void ScClipParam::transpose(const ScDocument& rSrcDoc, bool bIncludeFiltered,
+ bool bIsMultiRangeRowFilteredTranspose)
+{
+ mbTransposed = true;
+
+ switch (meDirection)
+ {
+ case Column:
+ meDirection = ScClipParam::Row;
+ break;
+ case Row:
+ meDirection = ScClipParam::Column;
+ break;
+ case Unspecified:
+ default:
+ ;
+ }
+
+ ScRangeList aNewRanges;
+ if (!maRanges.empty())
+ {
+ const ScRange & rRange1 = maRanges.front();
+ SCCOL nColOrigin = rRange1.aStart.Col();
+ SCROW nRowOrigin = rRange1.aStart.Row();
+ SCROW nRowCount = 0;
+ for ( size_t i = 0, n = maRanges.size(); i < n; ++i )
+ {
+ const ScRange & rRange = maRanges[ i ];
+ SCCOL nColDelta = rRange.aStart.Col() - nColOrigin;
+ SCROW nRowDelta = rRange.aStart.Row() - nRowOrigin;
+ SCROW nNonFilteredRows = rSrcDoc.CountNonFilteredRows(
+ rRange.aStart.Row(), rRange.aEnd.Row(), rRange.aStart.Tab());
+ if (!bIsMultiRangeRowFilteredTranspose)
+ {
+ SCCOL nCol1 = 0;
+ SCCOL nCol2 = bIncludeFiltered
+ ? static_cast<SCCOL>(rRange.aEnd.Row() - rRange.aStart.Row())
+ : nNonFilteredRows - 1;
+ SCROW nRow1 = 0;
+ SCROW nRow2 = static_cast<SCROW>(rRange.aEnd.Col() - rRange.aStart.Col());
+ nCol1 += static_cast<SCCOL>(nRowDelta);
+ nCol2 += static_cast<SCCOL>(nRowDelta);
+ nRow1 += static_cast<SCROW>(nColDelta);
+ nRow2 += static_cast<SCROW>(nColDelta);
+ aNewRanges.push_back(ScRange(nColOrigin + nCol1, nRowOrigin + nRow1,
+ rRange.aStart.Tab(), nColOrigin + nCol2,
+ nRowOrigin + nRow2, rRange.aStart.Tab()));
+ }
+ else
+ nRowCount += nNonFilteredRows;
+ }
+
+ // Transpose of filtered multi range row selection is a special case since filtering
+ // and selection are in the same dimension (i.e. row), see ScDocument::TransposeClip()
+ if (bIsMultiRangeRowFilteredTranspose)
+ {
+ assert(!bIncludeFiltered && "bIsMultiRangeRowFilteredTranspose can only be true if bIncludeFiltered is false");
+ SCCOL nColDelta = rRange1.aStart.Col() - nColOrigin;
+ SCROW nRowDelta = rRange1.aStart.Row() - nRowOrigin;
+ SCCOL nCol1 = 0;
+ SCCOL nCol2 = nRowCount - 1;
+ SCROW nRow1 = 0;
+ SCROW nRow2 = static_cast<SCROW>(rRange1.aEnd.Col() - rRange1.aStart.Col());
+ nCol1 += static_cast<SCCOL>(nRowDelta);
+ nCol2 += static_cast<SCCOL>(nRowDelta);
+ nRow1 += static_cast<SCROW>(nColDelta);
+ nRow2 += static_cast<SCROW>(nColDelta);
+ aNewRanges.push_back(ScRange(nColOrigin + nCol1, nRowOrigin + nRow1,
+ rRange1.aStart.Tab(), nColOrigin + nCol2,
+ nRowOrigin + nRow2, rRange1.aStart.Tab()));
+ }
+ }
+ maRanges = aNewRanges;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/colcontainer.cxx b/sc/source/core/data/colcontainer.cxx
new file mode 100644
index 0000000000..f6ef8ff7da
--- /dev/null
+++ b/sc/source/core/data/colcontainer.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 <colcontainer.hxx>
+#include <column.hxx>
+
+ScColContainer::ScColContainer( ScSheetLimits const & rSheetLimits, const size_t nSize )
+{
+ aCols.resize( nSize );
+ for ( size_t nCol = 0; nCol < nSize; ++nCol )
+ aCols[nCol].reset( new ScColumn(rSheetLimits) );
+}
+
+ScColContainer::~ScColContainer() COVERITY_NOEXCEPT_FALSE
+{
+ Clear();
+}
+
+void ScColContainer::Clear()
+{
+ SCCOL nSize = size();
+ for ( SCCOL nIdx = 0; nIdx < nSize; ++nIdx )
+ {
+ aCols[nIdx]->PrepareBroadcastersForDestruction();
+ aCols[nIdx].reset();
+ }
+ aCols.clear();
+}
+
+void ScColContainer::resize( ScSheetLimits const & rSheetLimits, const size_t aNewColSize )
+{
+ size_t aOldColSize = aCols.size();
+ if (aNewColSize > aCols.capacity())
+ aCols.reserve( aNewColSize );
+ for ( size_t nCol = aOldColSize; nCol < aNewColSize; ++nCol )
+ aCols.emplace_back(new ScColumn(rSheetLimits));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/colorscale.cxx b/sc/source/core/data/colorscale.cxx
new file mode 100644
index 0000000000..eff6d050b1
--- /dev/null
+++ b/sc/source/core/data/colorscale.cxx
@@ -0,0 +1,1523 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <colorscale.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <fillinfo.hxx>
+#include <bitmaps.hlst>
+#include <tokenarray.hxx>
+#include <refupdatecontext.hxx>
+#include <refdata.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <scitems.hxx>
+
+#include <formula/token.hxx>
+#include <vcl/bitmapex.hxx>
+
+#include <algorithm>
+#include <cassert>
+
+ScFormulaListener::ScFormulaListener(ScFormulaCell* pCell):
+ mbDirty(false),
+ mrDoc(pCell->GetDocument())
+{
+ startListening( pCell->GetCode(), pCell->aPos );
+}
+
+ScFormulaListener::ScFormulaListener(ScDocument& rDoc):
+ mbDirty(false),
+ mrDoc(rDoc)
+{
+}
+
+ScFormulaListener::ScFormulaListener(ScDocument& rDoc, const ScRangeList& rRange):
+ mbDirty(false),
+ mrDoc(rDoc)
+{
+ startListening(rRange);
+}
+
+void ScFormulaListener::startListening(const ScTokenArray* pArr, const ScRange& rRange)
+{
+ if (!pArr || mrDoc.IsClipOrUndo())
+ return;
+
+ for ( auto t: pArr->References() )
+ {
+ switch (t->GetType())
+ {
+ case formula::svSingleRef:
+ {
+ ScAddress aCell = t->GetSingleRef()->toAbs(mrDoc, rRange.aStart);
+ ScAddress aCell2 = t->GetSingleRef()->toAbs(mrDoc, rRange.aEnd);
+ ScRange aRange(aCell, aCell2);
+ if (aRange.IsValid())
+ mrDoc.StartListeningArea(aRange, false, this);
+ }
+ break;
+ case formula::svDoubleRef:
+ {
+ const ScSingleRefData& rRef1 = *t->GetSingleRef();
+ const ScSingleRefData& rRef2 = *t->GetSingleRef2();
+ ScAddress aCell1 = rRef1.toAbs(mrDoc, rRange.aStart);
+ ScAddress aCell2 = rRef2.toAbs(mrDoc, rRange.aStart);
+ ScAddress aCell3 = rRef1.toAbs(mrDoc, rRange.aEnd);
+ ScAddress aCell4 = rRef2.toAbs(mrDoc, rRange.aEnd);
+ ScRange aRange1(aCell1, aCell3);
+ ScRange aRange2(aCell2, aCell4);
+ aRange1.ExtendTo(aRange2);
+ if (aRange1.IsValid())
+ {
+ if (t->GetOpCode() == ocColRowNameAuto)
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ aRange1.aEnd.SetRow(mrDoc.MaxRow());
+ }
+ else
+ { // RowName
+ aRange1.aEnd.SetCol(mrDoc.MaxCol());
+ }
+ }
+ mrDoc.StartListeningArea(aRange1, false, this);
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+}
+
+void ScFormulaListener::startListening(const ScRangeList& rRange)
+{
+ if (mrDoc.IsClipOrUndo())
+ return;
+
+ size_t nLength = rRange.size();
+ for (size_t i = 0; i < nLength; ++i)
+ {
+ const ScRange& aRange = rRange[i];
+ mrDoc.StartListeningArea(aRange, false, this);
+ }
+}
+
+void ScFormulaListener::addTokenArray(const ScTokenArray* pArray, const ScRange& rRange)
+{
+ startListening(pArray, rRange);
+}
+
+void ScFormulaListener::setCallback(const std::function<void()>& aCallback)
+{
+ maCallbackFunction = aCallback;
+}
+
+void ScFormulaListener::stopListening()
+{
+ if (mrDoc.IsClipOrUndo())
+ return;
+
+ EndListeningAll();
+}
+
+ScFormulaListener::~ScFormulaListener()
+{
+ stopListening();
+}
+
+void ScFormulaListener::Notify(const SfxHint& rHint)
+{
+ mbDirty = true;
+
+ if (rHint.GetId() == SfxHintId::Dying)
+ return;
+
+ if (maCallbackFunction)
+ maCallbackFunction();
+}
+
+bool ScFormulaListener::NeedsRepaint() const
+{
+ bool bRet = mbDirty;
+ mbDirty = false;
+ return bRet;
+}
+
+ScColorScaleEntry::ScColorScaleEntry():
+ mnVal(0),
+ mpFormat(nullptr),
+ meType(COLORSCALE_VALUE)
+{
+}
+
+ScColorScaleEntry::ScColorScaleEntry(double nVal, const Color& rCol, ScColorScaleEntryType eType):
+ mnVal(nVal),
+ mpFormat(nullptr),
+ maColor(rCol),
+ meType(eType)
+{
+}
+
+ScColorScaleEntry::ScColorScaleEntry(const ScColorScaleEntry& rEntry):
+ mnVal(rEntry.mnVal),
+ mpFormat(rEntry.mpFormat),
+ maColor(rEntry.maColor),
+ meType(rEntry.meType)
+{
+ setListener();
+ if(rEntry.mpCell)
+ {
+ mpCell.reset(new ScFormulaCell(*rEntry.mpCell, rEntry.mpCell->GetDocument(), rEntry.mpCell->aPos, ScCloneFlags::NoMakeAbsExternal));
+ mpCell->StartListeningTo(mpCell->GetDocument());
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ }
+}
+
+ScColorScaleEntry::ScColorScaleEntry(ScDocument* pDoc, const ScColorScaleEntry& rEntry):
+ mnVal(rEntry.mnVal),
+ mpFormat(rEntry.mpFormat),
+ maColor(rEntry.maColor),
+ meType(rEntry.meType)
+{
+ setListener();
+ if(rEntry.mpCell)
+ {
+ mpCell.reset(new ScFormulaCell(*rEntry.mpCell, *pDoc, rEntry.mpCell->aPos, ScCloneFlags::NoMakeAbsExternal));
+ mpCell->StartListeningTo( *pDoc );
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ if (mpFormat)
+ mpListener->setCallback([&]() { mpFormat->DoRepaint();});
+ }
+}
+
+ScColorScaleEntry::~ScColorScaleEntry() COVERITY_NOEXCEPT_FALSE
+{
+ if(mpCell)
+ mpCell->EndListeningTo(mpCell->GetDocument());
+}
+
+void ScColorScaleEntry::SetFormula( const OUString& rFormula, ScDocument& rDoc, const ScAddress& rAddr, formula::FormulaGrammar::Grammar eGrammar )
+{
+ mpCell.reset(new ScFormulaCell( rDoc, rAddr, rFormula, eGrammar ));
+ mpCell->StartListeningTo( rDoc );
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ if (mpFormat)
+ mpListener->setCallback([&]() { mpFormat->DoRepaint();});
+}
+
+const ScTokenArray* ScColorScaleEntry::GetFormula() const
+{
+ if(mpCell)
+ {
+ return mpCell->GetCode();
+ }
+
+ return nullptr;
+}
+
+OUString ScColorScaleEntry::GetFormula( formula::FormulaGrammar::Grammar eGrammar ) const
+{
+ if(mpCell)
+ {
+ return mpCell->GetFormula(eGrammar);
+ }
+
+ return OUString();
+}
+
+double ScColorScaleEntry::GetValue() const
+{
+ if(mpCell)
+ {
+ mpCell->Interpret();
+ if(mpCell->IsValue())
+ return mpCell->GetValue();
+
+ return std::numeric_limits<double>::max();
+ }
+
+ return mnVal;
+}
+
+void ScColorScaleEntry::SetValue(double nValue)
+{
+ mnVal = nValue;
+ mpCell.reset();
+ setListener();
+}
+
+void ScColorScaleEntry::UpdateReference( const sc::RefUpdateContext& rCxt )
+{
+ if (!mpCell)
+ {
+ setListener();
+ return;
+ }
+
+ mpCell->UpdateReference(rCxt);
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ SetRepaintCallback(mpFormat);
+}
+
+void ScColorScaleEntry::UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt )
+{
+ if (!mpCell)
+ {
+ setListener();
+ return;
+ }
+
+ mpCell->UpdateInsertTab(rCxt);
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ SetRepaintCallback(mpFormat);
+}
+
+void ScColorScaleEntry::UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt )
+{
+ if (!mpCell)
+ {
+ setListener();
+ return;
+ }
+
+ mpCell->UpdateDeleteTab(rCxt);
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ SetRepaintCallback(mpFormat);
+}
+
+void ScColorScaleEntry::UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt )
+{
+ if (!mpCell)
+ {
+ setListener();
+ return;
+ }
+
+ SCTAB nTabNo = rCxt.getNewTab(mpCell->aPos.Tab());
+ mpCell->UpdateMoveTab(rCxt, nTabNo);
+ mpListener.reset(new ScFormulaListener(mpCell.get()));
+ SetRepaintCallback(mpFormat);
+}
+
+void ScColorScaleEntry::SetColor(const Color& rColor)
+{
+ maColor = rColor;
+}
+
+void ScColorScaleEntry::SetRepaintCallback(ScConditionalFormat* pFormat)
+{
+ mpFormat = pFormat;
+ setListener();
+ if (mpFormat && mpListener)
+ mpListener->setCallback([&]() { mpFormat->DoRepaint();});
+}
+
+void ScColorScaleEntry::SetType( ScColorScaleEntryType eType )
+{
+ meType = eType;
+ if(eType != COLORSCALE_FORMULA)
+ {
+ mpCell.reset();
+ mpListener.reset();
+ }
+
+ setListener();
+}
+
+void ScColorScaleEntry::setListener()
+{
+ if (!mpFormat)
+ return;
+
+ if (meType == COLORSCALE_PERCENT || meType == COLORSCALE_PERCENTILE
+ || meType == COLORSCALE_MIN || meType == COLORSCALE_MAX
+ || meType == COLORSCALE_AUTO)
+ {
+ mpListener.reset(new ScFormulaListener(*mpFormat->GetDocument(), mpFormat->GetRange()));
+ mpListener->setCallback([&]() { mpFormat->DoRepaint();});
+ }
+}
+
+void ScColorScaleEntry::SetRepaintCallback(const std::function<void()>& func)
+{
+ mpListener->setCallback(func);
+}
+
+ScColorFormat::ScColorFormat(ScDocument* pDoc)
+ : ScFormatEntry(pDoc)
+ , mpParent(nullptr)
+{
+}
+
+ScColorFormat::~ScColorFormat()
+{
+}
+
+void ScColorFormat::SetParent( ScConditionalFormat* pParent )
+{
+ mpParent = pParent;
+}
+
+ScColorScaleFormat::ScColorScaleFormat(ScDocument* pDoc):
+ ScColorFormat(pDoc)
+{
+}
+
+ScColorScaleFormat::ScColorScaleFormat(ScDocument* pDoc, const ScColorScaleFormat& rFormat):
+ ScColorFormat(pDoc)
+{
+ for(const auto& rxEntry : rFormat)
+ {
+ maColorScales.emplace_back(new ScColorScaleEntry(pDoc, *rxEntry));
+ }
+
+ auto aCache = rFormat.GetCache();
+ SetCache(aCache);
+}
+
+ScColorFormat* ScColorScaleFormat::Clone(ScDocument* pDoc) const
+{
+ return new ScColorScaleFormat(pDoc, *this);
+}
+
+ScColorScaleFormat::~ScColorScaleFormat()
+{
+}
+
+void ScColorScaleFormat::SetParent(ScConditionalFormat* pFormat)
+{
+ for (auto itr = begin(), itrEnd = end(); itr != itrEnd; ++itr)
+ {
+ (*itr)->SetRepaintCallback(pFormat);
+ }
+ ScColorFormat::SetParent(pFormat);
+}
+
+void ScColorScaleFormat::AddEntry( ScColorScaleEntry* pEntry )
+{
+ maColorScales.push_back(std::unique_ptr<ScColorScaleEntry, o3tl::default_delete<ScColorScaleEntry>>(pEntry));
+ maColorScales.back()->SetRepaintCallback(mpParent);
+}
+
+bool ScColorScaleFormat::IsEqual(const ScFormatEntry& rOther, bool /*bIgnoreSrcPos*/) const
+{
+ if (GetType() != rOther.GetType())
+ return false;
+
+ const ScColorScaleFormat& r = static_cast<const ScColorScaleFormat&>(rOther);
+
+ for (size_t i = 0; i < maColorScales.size(); ++i)
+ {
+ if (!maColorScales[i]->GetColor().IsRGBEqual(r.maColorScales[i]->GetColor().GetRGBColor())
+ || maColorScales[i]->GetType() != r.maColorScales[i]->GetType()
+ || maColorScales[i]->GetValue() != r.maColorScales[i]->GetValue())
+ return false;
+ }
+
+ return true;
+}
+
+double ScColorScaleFormat::GetMinValue() const
+{
+ ScColorScaleEntries::const_iterator itr = maColorScales.begin();
+
+ if((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
+ return (*itr)->GetValue();
+ else
+ {
+ return getMinValue();
+ }
+}
+
+double ScColorScaleFormat::GetMaxValue() const
+{
+ ScColorScaleEntries::const_reverse_iterator itr = maColorScales.rbegin();
+
+ if((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
+ return (*itr)->GetValue();
+ else
+ {
+ return getMaxValue();
+ }
+}
+
+void ScColorScaleFormat::calcMinMax(double& rMin, double& rMax) const
+{
+ rMin = GetMinValue();
+ rMax = GetMaxValue();
+}
+
+const ScRangeList& ScColorFormat::GetRange() const
+{
+ return mpParent->GetRange();
+}
+
+std::vector<double> ScColorFormat::GetCache() const
+{
+ std::vector<double> empty;
+ return mpCache ? mpCache->maValues : empty;
+}
+
+void ScColorFormat::SetCache(const std::vector<double>& aValues)
+{
+ mpCache.reset(new ScColorFormatCache);
+ mpCache->maValues = aValues;
+}
+
+std::vector<double>& ScColorFormat::getValues() const
+{
+ if(!mpCache)
+ {
+ mpCache.reset(new ScColorFormatCache);
+ std::vector<double>& rValues = mpCache->maValues;
+
+ size_t n = GetRange().size();
+ const ScRangeList& aRanges = GetRange();
+ for(size_t i = 0; i < n; ++i)
+ {
+ const ScRange & rRange = aRanges[i];
+ SCTAB nTab = rRange.aStart.Tab();
+
+ SCCOL nColStart = rRange.aStart.Col();
+ SCROW nRowStart = rRange.aStart.Row();
+ SCCOL nColEnd = rRange.aEnd.Col();
+ SCROW nRowEnd = rRange.aEnd.Row();
+
+ if(nRowEnd == mpDoc->MaxRow())
+ {
+ bool bShrunk = false;
+ mpDoc->ShrinkToUsedDataArea(bShrunk, nTab, nColStart, nRowStart,
+ nColEnd, nRowEnd, false);
+ }
+ for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ {
+ for(SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
+ {
+ ScAddress aAddr(nCol, nRow, nTab);
+ ScRefCellValue rCell(*mpDoc, aAddr);
+ if(rCell.hasNumeric())
+ {
+ double aVal = rCell.getValue();
+ rValues.push_back(aVal);
+ }
+ }
+ }
+ }
+
+ std::sort(rValues.begin(), rValues.end());
+ }
+
+ return mpCache->maValues;
+}
+
+double ScColorFormat::getMinValue() const
+{
+ std::vector<double>& rValues = getValues();
+ if(rValues.empty())
+ return 0;
+ return rValues[0];
+}
+
+double ScColorFormat::getMaxValue() const
+{
+ std::vector<double>& rValues = getValues();
+ if(rValues.empty())
+ return 0;
+ return rValues[rValues.size()-1];
+}
+
+void ScColorFormat::startRendering()
+{
+ mpCache.reset();
+}
+
+void ScColorFormat::endRendering()
+{
+ mpCache.reset();
+}
+
+void ScColorFormat::updateValues()
+{
+ getMinValue();
+ getMaxValue();
+}
+
+namespace {
+
+sal_uInt8 GetColorValue( double nVal, double nVal1, sal_uInt8 nColVal1, double nVal2, sal_uInt8 nColVal2 )
+{
+ if (nVal <= nVal1)
+ return nColVal1;
+
+ if (nVal >= nVal2)
+ return nColVal2;
+
+ sal_uInt8 nColVal = static_cast<int>((nVal - nVal1)/(nVal2-nVal1)*(nColVal2-nColVal1))+nColVal1;
+ return nColVal;
+}
+
+Color CalcColor( double nVal, double nVal1, const Color& rCol1, double nVal2, const Color& rCol2)
+{
+ sal_uInt8 nColRed = GetColorValue(nVal, nVal1, rCol1.GetRed(), nVal2, rCol2.GetRed());
+ sal_uInt8 nColBlue = GetColorValue(nVal, nVal1, rCol1.GetBlue(), nVal2, rCol2.GetBlue());
+ sal_uInt8 nColGreen = GetColorValue(nVal, nVal1, rCol1.GetGreen(), nVal2, rCol2.GetGreen());
+
+ return Color(nColRed, nColGreen, nColBlue);
+}
+
+/**
+ * @param rVector sorted vector of the array
+ * @param fPercentile percentile
+ */
+double GetPercentile( const std::vector<double>& rArray, double fPercentile )
+{
+ assert(!rArray.empty());
+ SAL_WARN_IF(fPercentile < 0, "sc", "negative percentile");
+ if (fPercentile < 0)
+ return rArray.front();
+ assert(fPercentile <= 1);
+ size_t nSize = rArray.size();
+ double fFloor = ::rtl::math::approxFloor(fPercentile * (nSize-1));
+ size_t nIndex = static_cast<size_t>(fFloor);
+ double fDiff = fPercentile * (nSize-1) - fFloor;
+ std::vector<double>::const_iterator iter = rArray.begin() + nIndex;
+ if (fDiff == 0.0)
+ return *iter;
+ else
+ {
+ double fVal = *iter;
+ iter = rArray.begin() + nIndex+1;
+ return fVal + fDiff * (*iter - fVal);
+ }
+}
+
+}
+
+double ScColorScaleFormat::CalcValue(double nMin, double nMax, const ScColorScaleEntries::const_iterator& itr) const
+{
+ switch((*itr)->GetType())
+ {
+ case COLORSCALE_PERCENT:
+ return nMin + (nMax-nMin)*((*itr)->GetValue()/100);
+ case COLORSCALE_MIN:
+ return nMin;
+ case COLORSCALE_MAX:
+ return nMax;
+ case COLORSCALE_PERCENTILE:
+ {
+ std::vector<double>& rValues = getValues();
+ if(rValues.size() == 1)
+ return rValues[0];
+ else
+ {
+ double fPercentile = (*itr)->GetValue()/100.0;
+ return GetPercentile(rValues, fPercentile);
+ }
+ }
+
+ default:
+ break;
+ }
+
+ return (*itr)->GetValue();
+}
+
+std::optional<Color> ScColorScaleFormat::GetColor( const ScAddress& rAddr ) const
+{
+ ScRefCellValue rCell(*mpDoc, rAddr);
+ if(!rCell.hasNumeric())
+ return std::optional<Color>();
+
+ // now we have for sure a value
+ double nVal = rCell.getValue();
+
+ if (maColorScales.size() < 2)
+ return std::optional<Color>();
+
+ double nMin = std::numeric_limits<double>::max();
+ double nMax = std::numeric_limits<double>::min();
+ calcMinMax(nMin, nMax);
+
+ // this check is for safety
+ if(nMin > nMax)
+ return std::optional<Color>();
+
+ ScColorScaleEntries::const_iterator itr = begin();
+ double nValMin = CalcValue(nMin, nMax, itr);
+ Color rColMin = (*itr)->GetColor();
+ ++itr;
+ double nValMax = CalcValue(nMin, nMax, itr);
+ Color rColMax = (*itr)->GetColor();
+
+ // tdf#155321 for the last percentile value, use always the end of the color scale,
+ // i.e. not the first possible color in the case of repeating values
+ bool bEqual = COLORSCALE_PERCENTILE == (*itr)->GetType() && nVal == nMax && nVal == nValMax;
+
+ ++itr;
+ while(itr != end() && (nVal > nValMax || bEqual))
+ {
+ rColMin = rColMax;
+ nValMin = !bEqual ? nValMax : nValMax - 1;
+ rColMax = (*itr)->GetColor();
+ nValMax = CalcValue(nMin, nMax, itr);
+ ++itr;
+ }
+
+ Color aColor = CalcColor(nVal, nValMin, rColMin, nValMax, rColMax);
+
+ return aColor;
+}
+
+void ScColorScaleFormat::UpdateReference( sc::RefUpdateContext& rCxt )
+{
+ for(ScColorScaleEntries::iterator itr = begin(); itr != end(); ++itr)
+ (*itr)->UpdateReference(rCxt);
+}
+
+void ScColorScaleFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ for (ScColorScaleEntries::iterator it = begin(); it != end(); ++it)
+ (*it)->UpdateInsertTab(rCxt);
+}
+
+void ScColorScaleFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ for (ScColorScaleEntries::iterator it = begin(); it != end(); ++it)
+ (*it)->UpdateDeleteTab(rCxt);
+}
+
+void ScColorScaleFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
+{
+ for (ScColorScaleEntries::iterator it = begin(); it != end(); ++it)
+ (*it)->UpdateMoveTab(rCxt);
+}
+
+ScFormatEntry::Type ScColorScaleFormat::GetType() const
+{
+ return Type::Colorscale;
+}
+
+ScColorScaleEntries::iterator ScColorScaleFormat::begin()
+{
+ return maColorScales.begin();
+}
+
+ScColorScaleEntries::const_iterator ScColorScaleFormat::begin() const
+{
+ return maColorScales.begin();
+}
+
+ScColorScaleEntries::iterator ScColorScaleFormat::end()
+{
+ return maColorScales.end();
+}
+
+ScColorScaleEntries::const_iterator ScColorScaleFormat::end() const
+{
+ return maColorScales.end();
+}
+
+ScColorScaleEntry* ScColorScaleFormat::GetEntry(size_t nPos)
+{
+ if (maColorScales.size() <= nPos)
+ return nullptr;
+
+ return maColorScales[nPos].get();
+}
+
+const ScColorScaleEntry* ScColorScaleFormat::GetEntry(size_t nPos) const
+{
+ if (maColorScales.size() <= nPos)
+ return nullptr;
+
+ return maColorScales[nPos].get();
+}
+
+size_t ScColorScaleFormat::size() const
+{
+ return maColorScales.size();
+}
+
+void ScColorScaleFormat::EnsureSize()
+{
+ if (maColorScales.size() < 2)
+ {
+ // TODO: create 2 valid entries
+ }
+}
+
+ScDataBarFormat::ScDataBarFormat(ScDocument* pDoc):
+ ScColorFormat(pDoc),
+ mpFormatData(new ScDataBarFormatData())
+{
+}
+
+ScDataBarFormat::ScDataBarFormat(ScDocument* pDoc, const ScDataBarFormat& rFormat):
+ ScColorFormat(pDoc),
+ mpFormatData(new ScDataBarFormatData(*rFormat.mpFormatData))
+{
+}
+
+void ScDataBarFormat::SetDataBarData( ScDataBarFormatData* pData )
+{
+ mpFormatData.reset(pData);
+ if (mpParent)
+ {
+ mpFormatData->mpUpperLimit->SetRepaintCallback(mpParent);
+ mpFormatData->mpLowerLimit->SetRepaintCallback(mpParent);
+ }
+}
+
+ScDataBarFormatData* ScDataBarFormat::GetDataBarData()
+{
+ return mpFormatData.get();
+}
+
+const ScDataBarFormatData* ScDataBarFormat::GetDataBarData() const
+{
+ return mpFormatData.get();
+}
+
+ScColorFormat* ScDataBarFormat::Clone(ScDocument* pDoc) const
+{
+ return new ScDataBarFormat(pDoc, *this);
+}
+
+void ScDataBarFormat::SetParent(ScConditionalFormat* pFormat)
+{
+ if (mpFormatData)
+ {
+ mpFormatData->mpUpperLimit->SetRepaintCallback(pFormat);
+ mpFormatData->mpLowerLimit->SetRepaintCallback(pFormat);
+ }
+ ScColorFormat::SetParent(pFormat);
+}
+
+ScFormatEntry::Type ScDataBarFormat::GetType() const
+{
+ return Type::Databar;
+}
+
+bool ScDataBarFormat::IsEqual(const ScFormatEntry& rOther, bool /*bIgnoreSrcPos*/) const
+{
+ if (GetType() != rOther.GetType())
+ return false;
+
+ const ScDataBarFormat& r = static_cast<const ScDataBarFormat&>(rOther);
+
+ bool bEq = (mpFormatData->maAxisColor.IsRGBEqual(r.mpFormatData->maAxisColor)
+ && mpFormatData->maPositiveColor.IsRGBEqual(r.mpFormatData->maPositiveColor)
+ && mpFormatData->mxNegativeColor == r.mpFormatData->mxNegativeColor
+ && mpFormatData->meAxisPosition == r.mpFormatData->meAxisPosition
+ && mpFormatData->mbGradient == r.mpFormatData->mbGradient
+ && mpFormatData->mbOnlyBar == r.mpFormatData->mbOnlyBar);
+
+ if (mpFormatData->mpUpperLimit->GetType() == r.mpFormatData->mpUpperLimit->GetType()
+ && bEq)
+ {
+ bEq = (mpFormatData->mpUpperLimit->GetColor().IsRGBEqual(
+ r.mpFormatData->mpUpperLimit->GetColor())
+ && mpFormatData->mpUpperLimit->GetValue()
+ == r.mpFormatData->mpUpperLimit->GetValue());
+ }
+
+ if (mpFormatData->mpLowerLimit->GetType() == r.mpFormatData->mpLowerLimit->GetType()
+ && bEq)
+ {
+ bEq = (mpFormatData->mpLowerLimit->GetColor().IsRGBEqual(
+ r.mpFormatData->mpLowerLimit->GetColor())
+ && mpFormatData->mpLowerLimit->GetValue()
+ == r.mpFormatData->mpLowerLimit->GetValue());
+ }
+
+ return bEq;
+}
+
+void ScDataBarFormat::UpdateReference( sc::RefUpdateContext& rCxt )
+{
+ mpFormatData->mpUpperLimit->UpdateReference(rCxt);
+ mpFormatData->mpLowerLimit->UpdateReference(rCxt);
+}
+
+void ScDataBarFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ mpFormatData->mpUpperLimit->UpdateInsertTab(rCxt);
+ mpFormatData->mpLowerLimit->UpdateInsertTab(rCxt);
+}
+
+void ScDataBarFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ mpFormatData->mpUpperLimit->UpdateDeleteTab(rCxt);
+ mpFormatData->mpLowerLimit->UpdateDeleteTab(rCxt);
+}
+
+void ScDataBarFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
+{
+ mpFormatData->mpUpperLimit->UpdateMoveTab(rCxt);
+ mpFormatData->mpLowerLimit->UpdateMoveTab(rCxt);
+}
+
+double ScDataBarFormat::getMin(double nMin, double nMax) const
+{
+ switch(mpFormatData->mpLowerLimit->GetType())
+ {
+ case COLORSCALE_MIN:
+ return nMin;
+
+ case COLORSCALE_AUTO:
+ return std::min<double>(0, nMin);
+
+ case COLORSCALE_PERCENT:
+ return nMin + (nMax-nMin)/100*mpFormatData->mpLowerLimit->GetValue();
+
+ case COLORSCALE_PERCENTILE:
+ {
+ double fPercentile = mpFormatData->mpLowerLimit->GetValue()/100.0;
+ std::vector<double>& rValues = getValues();
+ return GetPercentile(rValues, fPercentile);
+ }
+
+ default:
+ break;
+ }
+
+ return mpFormatData->mpLowerLimit->GetValue();
+}
+
+double ScDataBarFormat::getMax(double nMin, double nMax) const
+{
+ switch(mpFormatData->mpUpperLimit->GetType())
+ {
+ case COLORSCALE_MAX:
+ return nMax;
+ case COLORSCALE_AUTO:
+ return std::max<double>(0, nMax);
+ case COLORSCALE_PERCENT:
+ return nMin + (nMax-nMin)/100*mpFormatData->mpUpperLimit->GetValue();
+ case COLORSCALE_PERCENTILE:
+ {
+ double fPercentile = mpFormatData->mpUpperLimit->GetValue()/100.0;
+ std::vector<double>& rValues = getValues();
+ return GetPercentile(rValues, fPercentile);
+ }
+
+ default:
+ break;
+ }
+
+ return mpFormatData->mpUpperLimit->GetValue();
+}
+
+std::unique_ptr<ScDataBarInfo> ScDataBarFormat::GetDataBarInfo(const ScAddress& rAddr) const
+{
+ ScRefCellValue rCell(*mpDoc, rAddr);
+ if(!rCell.hasNumeric())
+ return nullptr;
+
+ // now we have for sure a value
+
+ double nValMin = getMinValue();
+ double nValMax = getMaxValue();
+ double nMin = getMin(nValMin, nValMax);
+ double nMax = getMax(nValMin, nValMax);
+ double nMinLength = mpFormatData->mnMinLength;
+ double nMaxLength = mpFormatData->mnMaxLength;
+
+ double nValue = rCell.getValue();
+
+ std::unique_ptr<ScDataBarInfo> pInfo(new ScDataBarInfo);
+ if(mpFormatData->meAxisPosition == databar::NONE)
+ {
+ if(nValue <= nMin)
+ {
+ pInfo->mnLength = nMinLength;
+ }
+ else if(nValue >= nMax)
+ {
+ pInfo->mnLength = nMaxLength;
+ }
+ else
+ {
+ double nDiff = nMax - nMin;
+ pInfo->mnLength = nMinLength + (nValue - nMin)/nDiff * (nMaxLength-nMinLength);
+ }
+ pInfo->mnZero = 0;
+ }
+ else if (mpFormatData->meAxisPosition == databar::AUTOMATIC)
+ {
+ // if auto is used we may need to adjust it
+ // for the length calculation
+ if (mpFormatData->mpLowerLimit->GetType() == COLORSCALE_AUTO && nMin > 0)
+ nMin = 0;
+ if (mpFormatData->mpUpperLimit->GetType() == COLORSCALE_MAX && nMax < 0)
+ nMax = 0;
+
+ //calculate the zero position first
+ if(nMin < 0)
+ {
+ if(nMax < 0)
+ pInfo->mnZero = 100;
+ else
+ {
+ pInfo->mnZero = -100*nMin/(nMax-nMin);
+ }
+ }
+ else
+ pInfo->mnZero = 0;
+
+ double nMinNonNegative = std::max(0.0, nMin);
+ double nMaxNonPositive = std::min(0.0, nMax);
+ //calculate the length
+ if(nValue < 0 && nMin < 0)
+ {
+ if (nValue < nMin)
+ pInfo->mnLength = -100;
+ else
+ pInfo->mnLength = -100 * (nValue-nMaxNonPositive)/(nMin-nMaxNonPositive);
+ }
+ else
+ {
+ if ( nValue > nMax )
+ pInfo->mnLength = 100;
+ else if (nValue <= nMin)
+ pInfo->mnLength = 0;
+ else
+ pInfo->mnLength = 100 * (nValue-nMinNonNegative)/(nMax-nMinNonNegative);
+ }
+ }
+ else if( mpFormatData->meAxisPosition == databar::MIDDLE)
+ {
+ pInfo->mnZero = 50;
+ double nAbsMax = std::max(std::abs(nMin), std::abs(nMax));
+ if (nValue < 0 && nMin < 0)
+ {
+ if (nValue < nMin)
+ pInfo->mnLength = nMaxLength * (nMin/nAbsMax);
+ else
+ pInfo->mnLength = nMaxLength * (nValue/nAbsMax);
+ }
+ else
+ {
+ if (nValue > nMax)
+ pInfo->mnLength = nMaxLength * (nMax/nAbsMax);
+ else
+ pInfo->mnLength = nMaxLength * (std::max(nValue, nMin)/nAbsMax);
+ }
+ }
+ else
+ assert(false);
+
+ // set color
+ if(mpFormatData->mbNeg && nValue < 0)
+ {
+ if(mpFormatData->mxNegativeColor)
+ {
+ pInfo->maColor = *mpFormatData->mxNegativeColor;
+ }
+ else
+ {
+ // default negative color is red
+ pInfo->maColor = COL_LIGHTRED;
+ }
+
+ }
+ else
+ pInfo->maColor = mpFormatData->maPositiveColor;
+
+ pInfo->mbGradient = mpFormatData->mbGradient;
+ pInfo->mbShowValue = !mpFormatData->mbOnlyBar;
+ pInfo->maAxisColor = mpFormatData->maAxisColor;
+
+ return pInfo;
+}
+
+void ScDataBarFormat::EnsureSize()
+{
+ if (!mpFormatData->mpLowerLimit)
+ {
+ // TODO: implement
+ }
+ if (!mpFormatData->mpUpperLimit)
+ {
+ // TODO: implement
+ }
+}
+
+ScIconSetFormatData::ScIconSetFormatData(ScIconSetFormatData const& rOther)
+ : eIconSetType(rOther.eIconSetType)
+ , mbShowValue(rOther.mbShowValue)
+ , mbReverse(rOther.mbReverse)
+ , mbCustom(rOther.mbCustom)
+ , maCustomVector(rOther.maCustomVector)
+{
+ m_Entries.reserve(rOther.m_Entries.size());
+ for (auto const& it : rOther.m_Entries)
+ {
+ m_Entries.emplace_back(new ScColorScaleEntry(*it));
+ }
+}
+
+ScIconSetFormat::ScIconSetFormat(ScDocument* pDoc):
+ ScColorFormat(pDoc),
+ mpFormatData(new ScIconSetFormatData)
+{
+}
+
+ScIconSetFormat::ScIconSetFormat(ScDocument* pDoc, const ScIconSetFormat& rFormat):
+ ScColorFormat(pDoc),
+ mpFormatData(new ScIconSetFormatData(*rFormat.mpFormatData))
+{
+}
+
+ScColorFormat* ScIconSetFormat::Clone( ScDocument* pDoc ) const
+{
+ return new ScIconSetFormat(pDoc, *this);
+}
+
+void ScIconSetFormat::SetParent(ScConditionalFormat* pFormat)
+{
+ for(iterator itr = begin(); itr != end(); ++itr)
+ {
+ (*itr)->SetRepaintCallback(pFormat);
+ }
+ ScColorFormat::SetParent(pFormat);
+}
+
+void ScIconSetFormat::SetIconSetData( ScIconSetFormatData* pFormatData )
+{
+ mpFormatData.reset( pFormatData );
+ SetParent(mpParent);
+}
+
+ScIconSetFormatData* ScIconSetFormat::GetIconSetData()
+{
+ return mpFormatData.get();
+}
+
+const ScIconSetFormatData* ScIconSetFormat::GetIconSetData() const
+{
+ return mpFormatData.get();
+}
+
+std::unique_ptr<ScIconSetInfo> ScIconSetFormat::GetIconSetInfo(const ScAddress& rAddr) const
+{
+ ScRefCellValue rCell(*mpDoc, rAddr);
+ if(!rCell.hasNumeric())
+ return nullptr;
+
+ // now we have for sure a value
+ double nVal = rCell.getValue();
+
+ if (mpFormatData->m_Entries.size() < 2)
+ return nullptr;
+
+ double nMin = GetMinValue();
+ double nMax = GetMaxValue();
+
+ sal_Int32 nIndex = 0;
+ const_iterator itr = begin();
+ ++itr;
+ double nValMax = CalcValue(nMin, nMax, itr);
+
+ ++itr;
+ while(itr != end() && nVal >= nValMax)
+ {
+ ++nIndex;
+ nValMax = CalcValue(nMin, nMax, itr);
+ ++itr;
+ }
+
+ if(nVal >= nValMax)
+ ++nIndex;
+
+ std::unique_ptr<ScIconSetInfo> pInfo(new ScIconSetInfo);
+
+ const SfxPoolItem& rPoolItem = mpDoc->GetPattern(rAddr)->GetItem(ATTR_FONT_HEIGHT);
+ tools::Long aFontHeight = static_cast<const SvxFontHeightItem&>(rPoolItem).GetHeight();
+ pInfo->mnHeight = aFontHeight;
+
+ if(mpFormatData->mbReverse)
+ {
+ sal_Int32 nMaxIndex = mpFormatData->m_Entries.size() - 1;
+ nIndex = nMaxIndex - nIndex;
+ }
+
+ if (mpFormatData->mbCustom && sal_Int32(mpFormatData->maCustomVector.size()) > nIndex)
+ {
+ ScIconSetType eCustomType = mpFormatData->maCustomVector[nIndex].first;
+ sal_Int32 nCustomIndex = mpFormatData->maCustomVector[nIndex].second;
+ if (nCustomIndex == -1)
+ {
+ return nullptr;
+ }
+
+ pInfo->eIconSetType = eCustomType;
+ pInfo->nIconIndex = nCustomIndex;
+ }
+ else
+ {
+ pInfo->nIconIndex = nIndex;
+ pInfo->eIconSetType = mpFormatData->eIconSetType;
+ }
+
+ pInfo->mbShowValue = mpFormatData->mbShowValue;
+ return pInfo;
+}
+
+ScFormatEntry::Type ScIconSetFormat::GetType() const
+{
+ return Type::Iconset;
+}
+
+void ScIconSetFormat::UpdateReference( sc::RefUpdateContext& rCxt )
+{
+ for(iterator itr = begin(); itr != end(); ++itr)
+ {
+ (*itr)->UpdateReference(rCxt);
+ }
+}
+
+void ScIconSetFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ for(iterator itr = begin(); itr != end(); ++itr)
+ {
+ (*itr)->UpdateInsertTab(rCxt);
+ }
+}
+
+void ScIconSetFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ for(iterator itr = begin(); itr != end(); ++itr)
+ {
+ (*itr)->UpdateDeleteTab(rCxt);
+ }
+}
+
+void ScIconSetFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
+{
+ for(iterator itr = begin(); itr != end(); ++itr)
+ {
+ (*itr)->UpdateMoveTab(rCxt);
+ }
+}
+
+ScIconSetFormat::iterator ScIconSetFormat::begin()
+{
+ return mpFormatData->m_Entries.begin();
+}
+
+ScIconSetFormat::const_iterator ScIconSetFormat::begin() const
+{
+ return mpFormatData->m_Entries.begin();
+}
+
+ScIconSetFormat::iterator ScIconSetFormat::end()
+{
+ return mpFormatData->m_Entries.end();
+}
+
+ScIconSetFormat::const_iterator ScIconSetFormat::end() const
+{
+ return mpFormatData->m_Entries.end();
+}
+
+double ScIconSetFormat::GetMinValue() const
+{
+ const_iterator itr = begin();
+
+ if ((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
+ return (*itr)->GetValue();
+ else
+ {
+ return getMinValue();
+ }
+}
+
+double ScIconSetFormat::GetMaxValue() const
+{
+ auto const itr = mpFormatData->m_Entries.rbegin();
+
+ if ((*itr)->GetType() == COLORSCALE_VALUE || (*itr)->GetType() == COLORSCALE_FORMULA)
+ return (*itr)->GetValue();
+ else
+ {
+ return getMaxValue();
+ }
+}
+
+double ScIconSetFormat::CalcValue(double nMin, double nMax, const ScIconSetFormat::const_iterator& itr) const
+{
+ switch ((*itr)->GetType())
+ {
+ case COLORSCALE_PERCENT:
+ return nMin + (nMax-nMin)*((*itr)->GetValue()/100);
+ case COLORSCALE_MIN:
+ return nMin;
+ case COLORSCALE_MAX:
+ return nMax;
+ case COLORSCALE_PERCENTILE:
+ {
+ std::vector<double>& rValues = getValues();
+ if(rValues.size() == 1)
+ return rValues[0];
+ else
+ {
+ double fPercentile = (*itr)->GetValue()/100.0;
+ return GetPercentile(rValues, fPercentile);
+ }
+ }
+
+ default:
+ break;
+ }
+
+ return (*itr)->GetValue();
+}
+
+const ScIconSetMap ScIconSetFormat::g_IconSetMap[] = {
+ { "3Arrows", IconSet_3Arrows, 3 },
+ { "3ArrowsGray", IconSet_3ArrowsGray, 3 },
+ { "3Flags", IconSet_3Flags, 3 },
+ { "3TrafficLights1", IconSet_3TrafficLights1, 3 },
+ { "3TrafficLights2", IconSet_3TrafficLights2, 3 },
+ { "3Signs", IconSet_3Signs, 3 },
+ { "3Symbols", IconSet_3Symbols, 3 },
+ { "3Symbols2", IconSet_3Symbols2, 3 },
+ { "3Smilies", IconSet_3Smilies, 3 },
+ { "3ColorSmilies", IconSet_3ColorSmilies, 3 },
+ { "3Stars", IconSet_3Stars, 3 },
+ { "3Triangles", IconSet_3Triangles, 3 },
+ { "4Arrows", IconSet_4Arrows, 4 },
+ { "4ArrowsGray", IconSet_4ArrowsGray, 4 },
+ { "4RedToBlack", IconSet_4RedToBlack, 4 },
+ { "4Rating", IconSet_4Rating, 4 },
+ { "4TrafficLights", IconSet_4TrafficLights, 4 },
+ { "5Arrows", IconSet_5Arrows, 5 },
+ { "5ArrowsGray", IconSet_5ArrowsGray, 5 },
+ { "5Rating", IconSet_5Ratings, 5 },
+ { "5Quarters", IconSet_5Quarters, 5 },
+ { "5Boxes", IconSet_5Boxes, 5 },
+ { nullptr, IconSet_3Arrows, 0 }
+};
+
+size_t ScIconSetFormat::size() const
+{
+ return mpFormatData->m_Entries.size();
+}
+
+
+namespace {
+
+constexpr OUString a3TrafficLights1[] = {
+ BMP_ICON_SET_CIRCLES1_RED, BMP_ICON_SET_CIRCLES1_YELLOW, BMP_ICON_SET_CIRCLES1_GREEN
+};
+
+constexpr OUString a3TrafficLights2[] = {
+ BMP_ICON_SET_TRAFFICLIGHTS_RED, BMP_ICON_SET_TRAFFICLIGHTS_YELLOW, BMP_ICON_SET_TRAFFICLIGHTS_GREEN
+};
+
+constexpr OUString a3Arrows[] = {
+ BMP_ICON_SET_COLORARROWS_DOWN, BMP_ICON_SET_COLORARROWS_SAME, BMP_ICON_SET_COLORARROWS_UP
+};
+
+constexpr OUString a3ArrowsGray[] = {
+ BMP_ICON_SET_GRAYARROWS_DOWN, BMP_ICON_SET_GRAYARROWS_SAME, BMP_ICON_SET_GRAYARROWS_UP
+};
+
+constexpr OUString a3Flags[] = {
+ BMP_ICON_SET_FLAGS_RED, BMP_ICON_SET_FLAGS_YELLOW, BMP_ICON_SET_FLAGS_GREEN
+};
+
+constexpr OUString a3Smilies[] = {
+ BMP_ICON_SET_NEGATIVE_YELLOW_SMILIE, BMP_ICON_SET_NEUTRAL_YELLOW_SMILIE, BMP_ICON_SET_POSITIVE_YELLOW_SMILIE
+};
+
+constexpr OUString a3ColorSmilies[] = {
+ BMP_ICON_SET_NEGATIVE_RED_SMILIE, BMP_ICON_SET_NEUTRAL_YELLOW_SMILIE, BMP_ICON_SET_POSITIVE_GREEN_SMILIE
+};
+
+constexpr OUString a3Stars[] = {
+ BMP_ICON_SET_STARS_EMPTY, BMP_ICON_SET_STARS_HALF, BMP_ICON_SET_STARS_FULL
+};
+
+constexpr OUString a3Triangles[] = {
+ BMP_ICON_SET_TRIANGLES_DOWN, BMP_ICON_SET_TRIANGLES_SAME, BMP_ICON_SET_TRIANGLES_UP
+};
+
+constexpr OUString a4Arrows[] = {
+ BMP_ICON_SET_COLORARROWS_DOWN, BMP_ICON_SET_COLORARROWS_SLIGHTLY_DOWN, BMP_ICON_SET_COLORARROWS_SLIGHTLY_UP, BMP_ICON_SET_COLORARROWS_UP
+};
+
+constexpr OUString a4ArrowsGray[] = {
+ BMP_ICON_SET_GRAYARROWS_DOWN, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_DOWN, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_UP, BMP_ICON_SET_GRAYARROWS_UP
+};
+
+constexpr OUString a5Arrows[] = {
+ BMP_ICON_SET_COLORARROWS_DOWN, BMP_ICON_SET_COLORARROWS_SLIGHTLY_DOWN,
+ BMP_ICON_SET_COLORARROWS_SAME, BMP_ICON_SET_COLORARROWS_SLIGHTLY_UP, BMP_ICON_SET_COLORARROWS_UP
+};
+
+constexpr OUString a5ArrowsGray[] = {
+ BMP_ICON_SET_GRAYARROWS_DOWN, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_DOWN,
+ BMP_ICON_SET_GRAYARROWS_SAME, BMP_ICON_SET_GRAYARROWS_SLIGHTLY_UP, BMP_ICON_SET_GRAYARROWS_UP
+};
+
+constexpr OUString a4TrafficLights[] = {
+ BMP_ICON_SET_CIRCLES1_GRAY, BMP_ICON_SET_CIRCLES1_RED,
+ BMP_ICON_SET_CIRCLES1_YELLOW, BMP_ICON_SET_CIRCLES1_GREEN
+};
+
+constexpr OUString a5Quarters[] = {
+ BMP_ICON_SET_PIES_EMPTY, BMP_ICON_SET_PIES_ONE_QUARTER, BMP_ICON_SET_PIES_HALF,
+ BMP_ICON_SET_PIES_THREE_QUARTER, BMP_ICON_SET_PIES_FULL,
+};
+
+constexpr OUString a5Boxes[] = {
+ BMP_ICON_SET_SQUARES_EMPTY, BMP_ICON_SET_SQUARES_ONE_QUARTER,
+ BMP_ICON_SET_SQUARES_HALF, BMP_ICON_SET_SQUARES_THREE_QUARTER,
+ BMP_ICON_SET_SQUARES_FULL
+};
+
+constexpr OUString a3Symbols1[] = {
+ BMP_ICON_SET_SYMBOLS1_CROSS, BMP_ICON_SET_SYMBOLS1_EXCLAMATION_MARK, BMP_ICON_SET_SYMBOLS1_CHECK
+};
+
+constexpr OUString a3Signs[] = {
+ BMP_ICON_SET_SHAPES_DIAMOND, BMP_ICON_SET_SHAPES_TRIANGLE, BMP_ICON_SET_SHAPES_CIRCLE
+};
+
+constexpr OUString a4RedToBlack[] = {
+ BMP_ICON_SET_CIRCLES2_DARK_GRAY, BMP_ICON_SET_CIRCLES2_LIGHT_GRAY,
+ BMP_ICON_SET_CIRCLES2_LIGHT_RED, BMP_ICON_SET_CIRCLES2_DARK_RED
+};
+
+constexpr OUString a4Ratings[] = {
+ BMP_ICON_SET_BARS_ONE_QUARTER, BMP_ICON_SET_BARS_HALF,
+ BMP_ICON_SET_BARS_THREE_QUARTER, BMP_ICON_SET_BARS_FULL
+};
+
+constexpr OUString a5Ratings[] = {
+ BMP_ICON_SET_BARS_EMPTY, BMP_ICON_SET_BARS_ONE_QUARTER, BMP_ICON_SET_BARS_HALF,
+ BMP_ICON_SET_BARS_THREE_QUARTER, BMP_ICON_SET_BARS_FULL
+};
+
+struct ScIconSetBitmapMap {
+ ScIconSetType eType;
+ const OUString* pBitmaps;
+};
+
+const ScIconSetBitmapMap aBitmapMap[] = {
+ { IconSet_3Arrows, a3Arrows },
+ { IconSet_3ArrowsGray, a3ArrowsGray },
+ { IconSet_3Flags, a3Flags },
+ { IconSet_3Signs, a3Signs },
+ { IconSet_3Symbols, a3Symbols1 },
+ { IconSet_3Symbols2, a3Symbols1 },
+ { IconSet_3TrafficLights1, a3TrafficLights1 },
+ { IconSet_3TrafficLights2, a3TrafficLights2 },
+ { IconSet_3Smilies, a3Smilies },
+ { IconSet_3ColorSmilies, a3ColorSmilies },
+ { IconSet_3Triangles, a3Triangles },
+ { IconSet_3Stars, a3Stars },
+ { IconSet_4Arrows, a4Arrows },
+ { IconSet_4ArrowsGray, a4ArrowsGray },
+ { IconSet_4Rating, a4Ratings },
+ { IconSet_4RedToBlack, a4RedToBlack },
+ { IconSet_4TrafficLights, a4TrafficLights },
+ { IconSet_5Arrows, a5Arrows },
+ { IconSet_5ArrowsGray, a5ArrowsGray },
+ { IconSet_5Quarters, a5Quarters },
+ { IconSet_5Ratings, a5Ratings },
+ { IconSet_5Boxes, a5Boxes }
+};
+
+const ScIconSetMap* findIconSetType(ScIconSetType eType)
+{
+ const ScIconSetMap* pMap = ScIconSetFormat::g_IconSetMap;
+ for (; pMap->pName; ++pMap)
+ {
+ if (pMap->eType == eType)
+ return pMap;
+ }
+
+ return nullptr;
+}
+
+}
+
+const char* ScIconSetFormat::getIconSetName( ScIconSetType eType )
+{
+ const ScIconSetMap* pMap = findIconSetType(eType);
+ if (pMap)
+ return pMap->pName;
+
+ return "";
+}
+
+sal_Int32 ScIconSetFormat::getIconSetElements( ScIconSetType eType )
+{
+ const ScIconSetMap* pMap = findIconSetType(eType);
+ if (pMap)
+ return pMap->nElements;
+
+ return 0;
+}
+
+OUString ScIconSetFormat::getIconName(ScIconSetType const eType, sal_Int32 const nIndex)
+{
+ OUString sBitmap;
+
+ for(const ScIconSetBitmapMap & i : aBitmapMap)
+ {
+ if(i.eType == eType)
+ {
+ sBitmap = *(i.pBitmaps + nIndex);
+ break;
+ }
+ }
+
+ assert(!sBitmap.isEmpty());
+
+ return sBitmap;
+}
+
+BitmapEx& ScIconSetFormat::getBitmap(sc::IconSetBitmapMap & rIconSetBitmapMap,
+ ScIconSetType const eType, sal_Int32 const nIndex)
+{
+ OUString sBitmap(ScIconSetFormat::getIconName(eType, nIndex));
+
+ std::map<OUString, BitmapEx>::iterator itr = rIconSetBitmapMap.find(sBitmap);
+ if (itr != rIconSetBitmapMap.end())
+ return itr->second;
+
+ BitmapEx aBitmap(sBitmap);
+ std::pair<OUString, BitmapEx> aPair(sBitmap, aBitmap);
+ std::pair<std::map<OUString, BitmapEx>::iterator, bool> itrNew = rIconSetBitmapMap.insert(aPair);
+ assert(itrNew.second);
+
+ return itrNew.first->second;
+}
+
+void ScIconSetFormat::EnsureSize()
+{
+ ScIconSetType eType = mpFormatData->eIconSetType;
+ for (const ScIconSetMap & i : g_IconSetMap)
+ {
+ if (i.eType == eType)
+ {
+ // size_t nElements = aIconSetMap[i].nElements;
+ // TODO: implement
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
new file mode 100644
index 0000000000..5bae9b7b9d
--- /dev/null
+++ b/sc/source/core/data/column.cxx
@@ -0,0 +1,3343 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <column.hxx>
+#include <scitems.hxx>
+#include <formulacell.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <docpool.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <compiler.hxx>
+#include <brdcst.hxx>
+#include <markdata.hxx>
+#include <postit.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <clipcontext.hxx>
+#include <types.hxx>
+#include <editutil.hxx>
+#include <mtvcellfunc.hxx>
+#include <columnspanset.hxx>
+#include <scopetools.hxx>
+#include <sharedformula.hxx>
+#include <refupdatecontext.hxx>
+#include <listenercontext.hxx>
+#include <formulagroup.hxx>
+#include <drwlayer.hxx>
+#include <mtvelements.hxx>
+#include <bcaslot.hxx>
+
+#include <svl/numformat.hxx>
+#include <poolcach.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <editeng/fieldupdater.hxx>
+#include <formula/errorcodes.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <map>
+#include <cstdio>
+#include <memory>
+
+using ::editeng::SvxBorderLine;
+using namespace formula;
+
+namespace {
+
+bool IsAmbiguousScriptNonZero( SvtScriptType nScript )
+{
+ //TODO: move to a header file
+ return ( nScript != SvtScriptType::LATIN &&
+ nScript != SvtScriptType::ASIAN &&
+ nScript != SvtScriptType::COMPLEX &&
+ nScript != SvtScriptType::NONE );
+}
+
+}
+
+ScNeededSizeOptions::ScNeededSizeOptions() :
+ pPattern(nullptr), bFormula(false), bSkipMerged(true), bGetFont(true), bTotalSize(false)
+{
+}
+
+ScColumn::ScColumn(ScSheetLimits const & rSheetLimits) :
+ maCellTextAttrs(rSheetLimits.GetMaxRowCount()),
+ maCellNotes(rSheetLimits.GetMaxRowCount()),
+ maBroadcasters(rSheetLimits.GetMaxRowCount()),
+ maCells(sc::CellStoreEvent(this)),
+ maSparklines(rSheetLimits.GetMaxRowCount()),
+ mnBlkCountFormula(0),
+ nCol( 0 ),
+ nTab( 0 ),
+ mbEmptyBroadcastersPending( false )
+{
+ maCells.resize(rSheetLimits.GetMaxRowCount());
+}
+
+ScColumn::~ScColumn() COVERITY_NOEXCEPT_FALSE
+{
+ FreeAll();
+}
+
+void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, bool bEmptyAttrArray)
+{
+ nCol = nNewCol;
+ nTab = nNewTab;
+ if ( bEmptyAttrArray )
+ InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, nullptr ));
+ else
+ InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, &rDoc.maTabs[nTab]->aDefaultColData.AttrArray()));
+}
+
+sc::MatrixEdge ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sc::MatrixEdge nMask,
+ bool bNoMatrixAtAll ) const
+{
+ using namespace sc;
+
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return MatrixEdge::Nothing;
+
+ ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
+
+ if (nRow1 == nRow2)
+ {
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
+ if (aPos.first->type != sc::element_type_formula)
+ return MatrixEdge::Nothing;
+
+ const ScFormulaCell* pCell = sc::formula_block::at(*aPos.first->data, aPos.second);
+ if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
+ return MatrixEdge::Nothing;
+
+ return pCell->GetMatrixEdge(GetDoc(), aOrigin);
+ }
+
+ bool bOpen = false;
+ MatrixEdge nEdges = MatrixEdge::Nothing;
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ SCROW nRow = nRow1;
+ for (;it != maCells.end() && nRow <= nRow2; ++it, nOffset = 0)
+ {
+ if (it->type != sc::element_type_formula)
+ {
+ // Skip this block.
+ nRow += it->size - nOffset;
+ continue;
+ }
+
+ size_t nRowsToRead = nRow2 - nRow + 1;
+ size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
+ sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, nOffset);
+ for (size_t i = nOffset; i < nEnd; ++itCell, ++i)
+ {
+ // Loop inside the formula block.
+ const ScFormulaCell* pCell = *itCell;
+ if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
+ continue;
+
+ nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
+ if (nEdges == MatrixEdge::Nothing)
+ continue;
+
+ // A 1x1 matrix array formula is OK even for no matrix at all.
+ if (bNoMatrixAtAll
+ && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right)))
+ return MatrixEdge::Inside; // per convention Inside
+
+ if (nEdges & MatrixEdge::Top)
+ bOpen = true; // top edge opens, keep on looking
+ else if (!bOpen)
+ return nEdges | MatrixEdge::Open; // there's something that wasn't opened
+ else if (nEdges & MatrixEdge::Inside)
+ return nEdges; // inside
+ if (((nMask & MatrixEdge::Right) && (nEdges & MatrixEdge::Left) && !(nEdges & MatrixEdge::Right)) ||
+ ((nMask & MatrixEdge::Left) && (nEdges & MatrixEdge::Right) && !(nEdges & MatrixEdge::Left)))
+ return nEdges; // only left/right edge
+
+ if (nEdges & MatrixEdge::Bottom)
+ bOpen = false; // bottom edge closes
+ }
+
+ nRow += nEnd - nOffset;
+ }
+ if (bOpen)
+ nEdges |= MatrixEdge::Open; // not closed, matrix continues
+
+ return nEdges;
+}
+
+bool ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark, const ScRangeList& rRangeList) const
+{
+ using namespace sc;
+
+ if (!rMark.IsMultiMarked())
+ return false;
+
+ ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
+ ScAddress aCurOrigin = aOrigin;
+
+ bool bOpen = false;
+ ScRangeList aRanges = rRangeList; // cached rMark.GetMarkedRanges(), for performance reasons (tdf#148147)
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = aRanges[i];
+ if (nTab < r.aStart.Tab() || r.aEnd.Tab() < nTab)
+ continue;
+
+ if (nCol < r.aStart.Col() || r.aEnd.Col() < nCol)
+ continue;
+
+ SCROW nTop = r.aStart.Row(), nBottom = r.aEnd.Row();
+ SCROW nRow = nTop;
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+
+ for (;it != maCells.end() && nRow <= nBottom; ++it, nOffset = 0)
+ {
+ if (it->type != sc::element_type_formula)
+ {
+ // Skip this block.
+ nRow += it->size - nOffset;
+ continue;
+ }
+
+ // This is a formula cell block.
+ size_t nRowsToRead = nBottom - nRow + 1;
+ size_t nEnd = std::min(it->size, nRowsToRead);
+ sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, nOffset);
+ for (size_t j = nOffset; j < nEnd; ++itCell, ++j)
+ {
+ // Loop inside the formula block.
+ const ScFormulaCell* pCell = *itCell;
+ if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
+ // cell is not a part of a matrix.
+ continue;
+
+ MatrixEdge nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
+ if (nEdges == MatrixEdge::Nothing)
+ continue;
+
+ bool bFound = false;
+
+ if (nEdges & MatrixEdge::Top)
+ bOpen = true; // top edge opens, keep on looking
+ else if (!bOpen)
+ return true; // there's something that wasn't opened
+ else if (nEdges & MatrixEdge::Inside)
+ bFound = true; // inside, all selected?
+
+ if (((nEdges & MatrixEdge::Left) | MatrixEdge::Right) ^ ((nEdges & MatrixEdge::Right) | MatrixEdge::Left))
+ // either left or right, but not both.
+ bFound = true; // only left/right edge, all selected?
+
+ if (nEdges & MatrixEdge::Bottom)
+ bOpen = false; // bottom edge closes
+
+ if (bFound)
+ {
+ // Check if the matrix is inside the selection in its entirety.
+ //
+ // TODO: It's more efficient to skip the matrix range if
+ // it's within selection, to avoid checking it again and
+ // again.
+
+ if (aCurOrigin != aOrigin)
+ { // new matrix to check?
+ aCurOrigin = aOrigin;
+ const ScFormulaCell* pFCell;
+ if (pCell->GetMatrixFlag() == ScMatrixMode::Reference)
+ pFCell = GetDoc().GetFormulaCell(aOrigin);
+ else
+ pFCell = pCell;
+
+ SCCOL nC;
+ SCROW nR;
+ pFCell->GetMatColsRows(nC, nR);
+ ScRange aRange(aOrigin, ScAddress(aOrigin.Col()+nC-1, aOrigin.Row()+nR-1, aOrigin.Tab()));
+ if (rMark.IsAllMarked(aRange))
+ bFound = false;
+ }
+ else
+ bFound = false; // done already
+ }
+
+ if (bFound)
+ return true;
+ }
+
+ nRow += nEnd;
+ }
+ }
+
+ return bOpen;
+}
+
+bool ScColumn::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const
+{
+ bool bFound = false;
+
+ SCROW nTop;
+ SCROW nBottom;
+
+ if (rMark.IsMultiMarked())
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ) && !bFound)
+ {
+ if (pAttrArray->HasAttrib( nTop, nBottom, nMask ))
+ bFound = true;
+ }
+ }
+
+ return bFound;
+}
+
+void ScColumn::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const
+{
+ SCROW nTop;
+ SCROW nBottom;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ const ScMultiSel& rMultiSel = rMark.GetMultiSelData();
+ if ( rMultiSel.HasMarks( nCol ) )
+ {
+ ScMultiSelIter aMultiIter( rMultiSel, nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->MergePatternArea( nTop, nBottom, rState, bDeep );
+ }
+ }
+}
+
+const ScPatternAttr* ScColumnData::GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const
+{
+ ::std::map< const ScPatternAttr*, size_t > aAttrMap;
+ const ScPatternAttr* pMaxPattern = nullptr;
+ size_t nMaxCount = 0;
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), nStartRow, nEndRow, GetDoc().GetDefPattern() );
+ const ScPatternAttr* pPattern;
+ SCROW nAttrRow1 = 0, nAttrRow2 = 0;
+
+ while( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != nullptr )
+ {
+ size_t& rnCount = aAttrMap[ pPattern ];
+ rnCount += (nAttrRow2 - nAttrRow1 + 1);
+ if( rnCount > nMaxCount )
+ {
+ pMaxPattern = pPattern;
+ nMaxCount = rnCount;
+ }
+ }
+
+ return pMaxPattern;
+}
+
+sal_uInt32 ScColumnData::GetNumberFormat( SCROW nStartRow, SCROW nEndRow ) const
+{
+ SCROW nPatStartRow, nPatEndRow;
+ const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
+ sal_uInt32 nFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
+ while (nEndRow > nPatEndRow)
+ {
+ nStartRow = nPatEndRow + 1;
+ pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
+ sal_uInt32 nTmpFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
+ if (nFormat != nTmpFormat)
+ return 0;
+ }
+ return nFormat;
+}
+
+void ScColumnData::ApplySelectionCache(ScItemPoolCache* pCache, SCROW nStartRow, SCROW nEndRow,
+ ScEditDataArray* pDataArray, bool* pIsChanged)
+{
+ pAttrArray->ApplyCacheArea(nStartRow, nEndRow, pCache, pDataArray, pIsChanged);
+}
+
+void ScColumnData::ChangeSelectionIndent(bool bIncrement, SCROW nStartRow, SCROW nEndRow)
+{
+ pAttrArray->ChangeIndent(nStartRow, nEndRow, bIncrement);
+}
+
+void ScColumnData::ClearSelectionItems(const sal_uInt16* pWhich, SCROW nStartRow, SCROW nEndRow)
+{
+ if (!pAttrArray)
+ return;
+
+ pAttrArray->ClearItems(nStartRow, nEndRow, pWhich);
+}
+
+void ScColumn::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
+{
+ SCROW nTop;
+ SCROW nBottom;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ DeleteArea(nTop, nBottom, nDelFlag, bBroadcast);
+ }
+}
+
+void ScColumn::ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr )
+{
+ const SfxItemSet* pSet = &rPatAttr.GetItemSet();
+ ScItemPoolCache aCache( GetDoc().GetPool(), pSet );
+
+ const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
+
+ // true = keep old content
+
+ const ScPatternAttr& rNewPattern = aCache.ApplyTo( *pPattern );
+
+ if (!SfxPoolItem::areSame(rNewPattern, *pPattern))
+ pAttrArray->SetPattern( nRow, &rNewPattern );
+}
+
+void ScColumnData::ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr,
+ ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+ const SfxItemSet* pSet = &rPatAttr.GetItemSet();
+ ScItemPoolCache aCache( GetDoc().GetPool(), pSet );
+ pAttrArray->ApplyCacheArea( nStartRow, nEndRow, &aCache, pDataArray, pIsChanged );
+}
+
+void ScColumn::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
+ const ScPatternAttr& rPattern, SvNumFormatType nNewType )
+{
+ const SfxItemSet* pSet = &rPattern.GetItemSet();
+ ScItemPoolCache aCache( GetDoc().GetPool(), pSet );
+ SvNumberFormatter* pFormatter = GetDoc().GetFormatTable();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for ( SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; nRow++ )
+ {
+ SCROW nRow1, nRow2;
+ const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(
+ nRow1, nRow2, nRow );
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
+ SvNumFormatType nOldType = pFormatter->GetType( nFormat );
+ if ( nOldType == nNewType || SvNumberFormatter::IsCompatible( nOldType, nNewType ) )
+ nRow = nRow2;
+ else
+ {
+ SCROW nNewRow1 = std::max( nRow1, nRow );
+ SCROW nNewRow2 = std::min( nRow2, nEndRow );
+ pAttrArray->ApplyCacheArea( nNewRow1, nNewRow2, &aCache );
+ nRow = nNewRow2;
+ }
+ }
+}
+
+void ScColumn::ApplyStyle( SCROW nRow, const ScStyleSheet* rStyle )
+{
+ const ScPatternAttr* pPattern = pAttrArray->GetPattern(nRow);
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pPattern));
+ pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(rStyle));
+ pAttrArray->SetPattern(nRow, std::move(pNewPattern), true);
+}
+
+void ScColumnData::ApplySelectionStyle(const ScStyleSheet& rStyle, SCROW nTop, SCROW nBottom)
+{
+ pAttrArray->ApplyStyleArea(nTop, nBottom, rStyle);
+}
+
+void ScColumn::ApplySelectionLineStyle( const ScMarkData& rMark,
+ const SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ SCROW nTop;
+ SCROW nBottom;
+
+ if (rMark.IsMultiMarked())
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->ApplyLineStyleArea(nTop, nBottom, pLine, bColorOnly );
+ }
+}
+
+const ScStyleSheet* ScColumn::GetSelectionStyle( const ScMarkData& rMark, bool& rFound ) const
+{
+ rFound = false;
+ if (!rMark.IsMultiMarked())
+ {
+ OSL_FAIL("No selection in ScColumn::GetSelectionStyle");
+ return nullptr;
+ }
+
+ bool bEqual = true;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ ScDocument& rDocument = GetDoc();
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ SCROW nTop;
+ SCROW nBottom;
+ while (bEqual && aMultiIter.Next( nTop, nBottom ))
+ {
+ ScAttrIterator aAttrIter( pAttrArray.get(), nTop, nBottom, rDocument.GetDefPattern() );
+ SCROW nRow;
+ SCROW nDummy;
+ while (bEqual)
+ {
+ const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
+ if (!pPattern)
+ break;
+ pNewStyle = pPattern->GetStyleSheet();
+ rFound = true;
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // difference
+ pStyle = pNewStyle;
+ }
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+const ScStyleSheet* ScColumn::GetAreaStyle( bool& rFound, SCROW nRow1, SCROW nRow2 ) const
+{
+ rFound = false;
+
+ bool bEqual = true;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), nRow1, nRow2, GetDoc().GetDefPattern() );
+ SCROW nRow;
+ SCROW nDummy;
+ while (bEqual)
+ {
+ const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
+ if (!pPattern)
+ break;
+ pNewStyle = pPattern->GetStyleSheet();
+ rFound = true;
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // difference
+ pStyle = pNewStyle;
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr )
+{
+ // in order to only create a new SetItem, we don't need SfxItemPoolCache.
+ //TODO: Warning: ScItemPoolCache seems to create too many Refs for the new SetItem ??
+
+ ScDocumentPool* pDocPool = GetDoc().GetPool();
+
+ const ScPatternAttr* pOldPattern = pAttrArray->GetPattern( nRow );
+ ScPatternAttr aTemp(*pOldPattern);
+ aTemp.GetItemSet().Put(rAttr);
+ const ScPatternAttr* pNewPattern = &pDocPool->DirectPutItemInPool( aTemp );
+
+ if (!SfxPoolItem::areSame( pNewPattern, pOldPattern ))
+ pAttrArray->SetPattern( nRow, pNewPattern );
+ else
+ pDocPool->DirectRemoveItemFromPool( *pNewPattern ); // free up resources
+}
+
+ScRefCellValue ScColumn::GetCellValue( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ if (aPos.first == maCells.end())
+ return ScRefCellValue();
+
+ return GetCellValue(aPos.first, aPos.second);
+}
+
+ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockPosition& rBlockPos, SCROW nRow )
+{
+ std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
+ if (aPos.first == maCells.end())
+ return ScRefCellValue();
+
+ rBlockPos.miCellPos = aPos.first; // Store this for next call.
+ return GetCellValue(aPos.first, aPos.second);
+}
+
+ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
+ if (aPos.first == maCells.end())
+ return ScRefCellValue();
+
+ rBlockPos.miCellPos = aPos.first; // Store this for next call.
+ return GetCellValue(aPos.first, aPos.second);
+}
+
+ScRefCellValue ScColumn::GetCellValue( const sc::CellStoreType::const_iterator& itPos, size_t nOffset )
+{
+ switch (itPos->type)
+ {
+ case sc::element_type_numeric:
+ // Numeric cell
+ return ScRefCellValue(sc::numeric_block::at(*itPos->data, nOffset));
+ break;
+ case sc::element_type_string:
+ // String cell
+ return ScRefCellValue(&sc::string_block::at(*itPos->data, nOffset));
+ break;
+ case sc::element_type_edittext:
+ // Edit cell
+ return ScRefCellValue(sc::edittext_block::at(*itPos->data, nOffset));
+ break;
+ case sc::element_type_formula:
+ // Formula cell
+ return ScRefCellValue(sc::formula_block::at(*itPos->data, nOffset));
+ break;
+ default:
+ return ScRefCellValue(); // empty cell
+ }
+}
+
+const sc::CellTextAttr* ScColumn::GetCellTextAttr( SCROW nRow ) const
+{
+ sc::ColumnBlockConstPosition aBlockPos;
+ aBlockPos.miCellTextAttrPos = maCellTextAttrs.begin();
+ return GetCellTextAttr(aBlockPos, nRow);
+}
+
+const sc::CellTextAttr* ScColumn::GetCellTextAttr( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
+{
+ sc::CellTextAttrStoreType::const_position_type aPos = maCellTextAttrs.position(rBlockPos.miCellTextAttrPos, nRow);
+ if (aPos.first == maCellTextAttrs.end())
+ return nullptr;
+
+ rBlockPos.miCellTextAttrPos = aPos.first;
+
+ if (aPos.first->type != sc::element_type_celltextattr)
+ return nullptr;
+
+ return &sc::celltextattr_block::at(*aPos.first->data, aPos.second);
+}
+
+bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
+{
+ if (IsEmptyData() && IsEmptyAttr())
+ return true;
+
+ // Return false if we have any non-empty cells between nStartRow and nEndRow inclusive.
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type != sc::element_type_empty)
+ return false;
+
+ // Get the length of the remaining empty segment.
+ size_t nLen = it->size - aPos.second;
+ SCROW nNextNonEmptyRow = nStartRow + nLen;
+ if (nNextNonEmptyRow <= nEndRow)
+ return false;
+
+ // AttrArray only looks for merged cells
+
+ return pAttrArray == nullptr || pAttrArray->TestInsertCol(nStartRow, nEndRow);
+}
+
+bool ScColumn::TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const
+{
+ // AttrArray only looks for merged cells
+ {
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type == sc::element_type_empty && maCells.block_size() == 1)
+ // The entire cell array is empty.
+ return pAttrArray->TestInsertRow(nSize);
+ }
+
+ // See if there would be any non-empty cell that gets pushed out.
+
+ // Find the position of the last non-empty cell below nStartRow.
+ size_t nLastNonEmptyRow = GetDoc().MaxRow();
+ sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
+ if (it->type == sc::element_type_empty)
+ nLastNonEmptyRow -= it->size;
+
+ if (nLastNonEmptyRow < o3tl::make_unsigned(nStartRow))
+ // No cells would get pushed out.
+ return pAttrArray->TestInsertRow(nSize);
+
+ if (nLastNonEmptyRow + nSize > o3tl::make_unsigned(GetDoc().MaxRow()))
+ // At least one cell would get pushed out. Not good.
+ return false;
+
+ return pAttrArray->TestInsertRow(nSize);
+}
+
+void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize )
+{
+ pAttrArray->InsertRow( nStartRow, nSize );
+
+ maCellNotes.insert_empty(nStartRow, nSize);
+ maCellNotes.resize(GetDoc().GetMaxRowCount());
+
+ maSparklines.insert_empty(nStartRow, nSize);
+ maSparklines.resize(GetDoc().GetSheetLimits().GetMaxRowCount());
+
+ maBroadcasters.insert_empty(nStartRow, nSize);
+ maBroadcasters.resize(GetDoc().GetMaxRowCount());
+
+ maCellTextAttrs.insert_empty(nStartRow, nSize);
+ maCellTextAttrs.resize(GetDoc().GetMaxRowCount());
+
+ maCells.insert_empty(nStartRow, nSize);
+ maCells.resize(GetDoc().GetMaxRowCount());
+
+ CellStorageModified();
+
+ // We *probably* don't need to broadcast here since the parent call seems
+ // to take care of it.
+}
+
+namespace {
+
+class CopyToClipHandler
+{
+ const ScDocument& mrSrcDoc;
+ const ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ sc::ColumnBlockPosition maDestPos;
+ sc::ColumnBlockPosition* mpDestPos;
+
+ void setDefaultAttrsToDest(size_t nRow, size_t nSize)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
+ }
+
+public:
+ CopyToClipHandler(const ScDocument& rSrcDoc, const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos) :
+ mrSrcDoc(rSrcDoc), mrSrcCol(rSrcCol), mrDestCol(rDestCol), mpDestPos(pDestPos)
+ {
+ if (mpDestPos)
+ maDestPos = *mpDestPos;
+ else
+ mrDestCol.InitBlockPosition(maDestPos);
+ }
+
+ ~CopyToClipHandler()
+ {
+ if (mpDestPos)
+ *mpDestPos = maDestPos;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nTopRow = aNode.position + nOffset;
+
+ bool bSet = true;
+
+ switch (aNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::string_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
+
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::edittext_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ std::vector<EditTextObject*> aCloned;
+ aCloned.reserve(nDataSize);
+ for (; it != itEnd; ++it)
+ aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(
+ maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ std::vector<ScFormulaCell*> aCloned;
+ aCloned.reserve(nDataSize);
+ ScAddress aDestPos(mrDestCol.GetCol(), nTopRow, mrDestCol.GetTab());
+ for (; it != itEnd; ++it, aDestPos.IncRow())
+ {
+ const ScFormulaCell& rOld = **it;
+ if (rOld.GetDirty() && mrSrcCol.GetDoc().GetAutoCalc())
+ const_cast<ScFormulaCell&>(rOld).Interpret();
+
+ aCloned.push_back(new ScFormulaCell(rOld, mrDestCol.GetDoc(), aDestPos));
+ }
+
+ // Group the cloned formula cells.
+ if (!aCloned.empty())
+ sc::SharedFormulaUtil::groupFormulaCells(aCloned.begin(), aCloned.end());
+
+ sc::CellStoreType& rDestCells = mrDestCol.GetCellStore();
+ maDestPos.miCellPos = rDestCells.set(
+ maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
+
+ // Merge adjacent formula cell groups (if applicable).
+ sc::CellStoreType::position_type aPos =
+ rDestCells.position(maDestPos.miCellPos, nTopRow);
+ maDestPos.miCellPos = aPos.first;
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ size_t nLastRow = nTopRow + nDataSize;
+ if (nLastRow < o3tl::make_unsigned(mrSrcDoc.MaxRow()))
+ {
+ aPos = rDestCells.position(maDestPos.miCellPos, nLastRow+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+ }
+ break;
+ default:
+ bSet = false;
+ }
+
+ if (bSet)
+ setDefaultAttrsToDest(nTopRow, nDataSize);
+
+ mrSrcCol.DuplicateNotes(nTopRow, nDataSize, mrDestCol, maDestPos, false);
+ mrSrcCol.DuplicateSparklines(nTopRow, nDataSize, mrDestCol, maDestPos);
+ }
+};
+
+class CopyTextAttrToClipHandler
+{
+ sc::CellTextAttrStoreType& mrDestAttrs;
+ sc::CellTextAttrStoreType::iterator miPos;
+
+public:
+ explicit CopyTextAttrToClipHandler( sc::CellTextAttrStoreType& rAttrs ) :
+ mrDestAttrs(rAttrs), miPos(mrDestAttrs.begin()) {}
+
+ void operator() ( const sc::CellTextAttrStoreType::value_type& aNode, size_t nOffset, size_t nDataSize )
+ {
+ if (aNode.type != sc::element_type_celltextattr)
+ return;
+
+ sc::celltextattr_block::const_iterator it = sc::celltextattr_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::celltextattr_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ size_t nPos = aNode.position + nOffset;
+ miPos = mrDestAttrs.set(miPos, nPos, it, itEnd);
+ }
+};
+
+
+}
+
+void ScColumn::CopyToClip(
+ sc::CopyToClipContext& rCxt, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const
+{
+ pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
+ rCxt.isKeepScenarioFlags() ? (ScMF::All & ~ScMF::Scenario) : ScMF::All );
+
+ {
+ CopyToClipHandler aFunc(GetDoc(), *this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol));
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+ }
+
+ {
+ CopyTextAttrToClipHandler aFunc(rColumn.maCellTextAttrs);
+ sc::ParseBlock(maCellTextAttrs.begin(), maCellTextAttrs, aFunc, nRow1, nRow2);
+ }
+
+ rColumn.CellStorageModified();
+}
+
+void ScColumn::CopyStaticToDocument(
+ SCROW nRow1, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScColumn& rDestCol )
+{
+ if (nRow1 > nRow2)
+ return;
+
+ sc::ColumnBlockPosition aDestPos;
+ CopyCellTextAttrsToDocument(nRow1, nRow2, rDestCol);
+ CopyCellNotesToDocument(nRow1, nRow2, rDestCol);
+
+ // First, clear the destination column for the specified row range.
+ rDestCol.maCells.set_empty(nRow1, nRow2);
+
+ aDestPos.miCellPos = rDestCol.maCells.begin();
+
+ ScDocument& rDocument = GetDoc();
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ size_t nDataSize = 0;
+ size_t nCurRow = nRow1;
+
+ for (; it != maCells.end() && nCurRow <= o3tl::make_unsigned(nRow2); ++it, nOffset = 0, nCurRow += nDataSize)
+ {
+ bool bLastBlock = false;
+ nDataSize = it->size - nOffset;
+ if (nCurRow + nDataSize - 1 > o3tl::make_unsigned(nRow2))
+ {
+ // Truncate the block to copy to clipboard.
+ nDataSize = nRow2 - nCurRow + 1;
+ bLastBlock = true;
+ }
+
+ switch (it->type)
+ {
+ case sc::element_type_numeric:
+ {
+ sc::numeric_block::const_iterator itData = sc::numeric_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::numeric_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ sc::string_block::const_iterator itData = sc::string_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::string_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::const_iterator itData = sc::edittext_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::edittext_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+
+ // Convert to simple strings.
+ std::vector<svl::SharedString> aConverted;
+ aConverted.reserve(nDataSize);
+ for (; itData != itDataEnd; ++itData)
+ {
+ const EditTextObject& rObj = **itData;
+ svl::SharedString aSS = rDocument.GetSharedStringPool().intern(ScEditUtil::GetString(rObj, &rDocument));
+ aConverted.push_back(aSS);
+ }
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, aConverted.begin(), aConverted.end());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator itData = sc::formula_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::formula_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+
+ // Interpret and convert to raw values.
+ for (SCROW i = 0; itData != itDataEnd; ++itData, ++i)
+ {
+ SCROW nRow = nCurRow + i;
+
+ ScFormulaCell& rFC = **itData;
+ if (rFC.GetDirty() && rDocument.GetAutoCalc())
+ rFC.Interpret();
+
+ if (rFC.GetErrCode() != FormulaError::NONE)
+ // Skip cells with error.
+ continue;
+
+ if (rFC.IsValue())
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, rFC.GetValue());
+ else
+ {
+ svl::SharedString aSS = rFC.GetString();
+ if (aSS.isValid())
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, aSS);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (bLastBlock)
+ break;
+ }
+
+ // Don't forget to copy the number formats over. Charts may reference them.
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ sal_uInt32 nNumFmt = GetNumberFormat(rDocument.GetNonThreadedContext(), nRow);
+ SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
+ if (itNum != rMap.end())
+ nNumFmt = itNum->second;
+
+ rDestCol.SetNumberFormat(nRow, nNumFmt);
+ }
+
+ rDestCol.CellStorageModified();
+}
+
+void ScColumn::CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol )
+{
+ ScDocument& rDocument = GetDoc();
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nSrcRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ bool bSet = true;
+ switch (it->type)
+ {
+ case sc::element_type_numeric:
+ rDestCol.maCells.set(nDestRow, sc::numeric_block::at(*it->data, aPos.second));
+ break;
+ case sc::element_type_string:
+ rDestCol.maCells.set(nDestRow, sc::string_block::at(*it->data, aPos.second));
+ break;
+ case sc::element_type_edittext:
+ {
+ EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second);
+ if (&rDocument == &rDestCol.GetDoc())
+ rDestCol.maCells.set(nDestRow, p->Clone().release());
+ else
+ rDestCol.maCells.set(nDestRow, ScEditUtil::Clone(*p, rDestCol.GetDoc()).release());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ if (p->GetDirty() && rDocument.GetAutoCalc())
+ p->Interpret();
+
+ ScAddress aDestPos = p->aPos;
+ aDestPos.SetRow(nDestRow);
+ ScFormulaCell* pNew = new ScFormulaCell(*p, rDestCol.GetDoc(), aDestPos);
+ rDestCol.SetFormulaCell(nDestRow, pNew);
+ }
+ break;
+ case sc::element_type_empty:
+ default:
+ // empty
+ rDestCol.maCells.set_empty(nDestRow, nDestRow);
+ bSet = false;
+ }
+
+ if (bSet)
+ {
+ rDestCol.maCellTextAttrs.set(nDestRow, maCellTextAttrs.get<sc::CellTextAttr>(nSrcRow));
+ ScPostIt* pNote = maCellNotes.get<ScPostIt*>(nSrcRow);
+ if (pNote)
+ {
+ pNote = pNote->Clone(ScAddress(nCol, nSrcRow, nTab),
+ rDestCol.GetDoc(),
+ ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab),
+ false).release();
+ rDestCol.maCellNotes.set(nDestRow, pNote);
+ pNote->UpdateCaptionPos(ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab));
+ }
+ else
+ rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
+ }
+ else
+ {
+ rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
+ rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
+ }
+
+ rDestCol.CellStorageModified();
+}
+
+namespace {
+
+bool canCopyValue(const ScDocument& rDoc, const ScAddress& rPos, InsertDeleteFlags nFlags)
+{
+ sal_uInt32 nNumIndex = rDoc.GetAttr(rPos, ATTR_VALUE_FORMAT)->GetValue();
+ SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nNumIndex);
+ if ((nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) || (nType == SvNumFormatType::DATETIME))
+ return ((nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE);
+
+ return (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
+}
+
+class CopyAsLinkHandler
+{
+ const ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ sc::ColumnBlockPosition maDestPos;
+ sc::ColumnBlockPosition* mpDestPos;
+ InsertDeleteFlags mnCopyFlags;
+
+ sc::StartListeningType meListenType;
+
+ void setDefaultAttrToDest(size_t nRow)
+ {
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+ }
+
+ void setDefaultAttrsToDest(size_t nRow, size_t nSize)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
+ }
+
+ ScFormulaCell* createRefCell(size_t nRow)
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab())); // Absolute reference.
+ aRef.SetFlag3D(true);
+
+ ScTokenArray aArr(mrDestCol.GetDoc());
+ aArr.AddSingleReference(aRef);
+ return new ScFormulaCell(mrDestCol.GetDoc(), ScAddress(mrDestCol.GetCol(), nRow, mrDestCol.GetTab()), aArr);
+ }
+
+ void createRefBlock(const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nTopRow = aNode.position + nOffset;
+
+ for (size_t i = 0; i < nDataSize; ++i)
+ {
+ SCROW nRow = nTopRow + i;
+ mrDestCol.SetFormulaCell(maDestPos, nRow, createRefCell(nRow), meListenType);
+ }
+
+ setDefaultAttrsToDest(nTopRow, nDataSize);
+ }
+
+public:
+ CopyAsLinkHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos, InsertDeleteFlags nCopyFlags) :
+ mrSrcCol(rSrcCol),
+ mrDestCol(rDestCol),
+ mpDestPos(pDestPos),
+ mnCopyFlags(nCopyFlags),
+ meListenType(sc::SingleCellListening)
+ {
+ if (mpDestPos)
+ maDestPos = *mpDestPos;
+ }
+
+ ~CopyAsLinkHandler()
+ {
+ if (mpDestPos)
+ {
+ // Similar to CopyByCloneHandler, don't copy a singular iterator.
+ {
+ sc::ColumnBlockPosition aTempBlock;
+ mrDestCol.InitBlockPosition(aTempBlock);
+ maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
+ }
+
+ *mpDestPos = maDestPos;
+ }
+ }
+
+ void setStartListening( bool b )
+ {
+ meListenType = b ? sc::SingleCellListening : sc::NoListening;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nRow = aNode.position + nOffset;
+
+ if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
+ {
+ bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
+ }
+
+ switch (aNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
+ return;
+
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
+ for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
+ {
+ if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
+ continue;
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, createRefCell(nRow));
+ setDefaultAttrToDest(nRow);
+ }
+ }
+ break;
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::STRING))
+ return;
+
+ createRefBlock(aNode, nOffset, nDataSize);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::FORMULA))
+ return;
+
+ createRefBlock(aNode, nOffset, nDataSize);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+class CopyByCloneHandler
+{
+ const ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ sc::ColumnBlockPosition maDestPos;
+ sc::ColumnBlockPosition* mpDestPos;
+ svl::SharedStringPool* mpSharedStringPool;
+ InsertDeleteFlags mnCopyFlags;
+
+ sc::StartListeningType meListenType;
+ ScCloneFlags mnFormulaCellCloneFlags;
+
+ void setDefaultAttrToDest(size_t nRow)
+ {
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+ }
+
+ void setDefaultAttrsToDest(size_t nRow, size_t nSize)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
+ }
+
+ void cloneFormulaCell(size_t nRow, ScFormulaCell& rSrcCell)
+ {
+ ScAddress aDestPos(mrDestCol.GetCol(), nRow, mrDestCol.GetTab());
+
+ bool bCloneValue = (mnCopyFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
+ bool bCloneDateTime = (mnCopyFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE;
+ bool bCloneString = (mnCopyFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE;
+ bool bCloneSpecialBoolean = (mnCopyFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE;
+ bool bCloneFormula = (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE;
+
+ bool bForceFormula = false;
+
+ if (bCloneSpecialBoolean)
+ {
+ // See if the formula consists of =TRUE() or =FALSE().
+ const ScTokenArray* pCode = rSrcCell.GetCode();
+ if (pCode && pCode->GetLen() == 1)
+ {
+ const formula::FormulaToken* p = pCode->FirstToken();
+ if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
+ // This is a boolean formula.
+ bForceFormula = true;
+ }
+ }
+
+ if (bForceFormula || bCloneFormula)
+ {
+ // Clone as formula cell.
+ ScFormulaCell* pCell = new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos, mnFormulaCellCloneFlags);
+ pCell->SetDirtyVar();
+ mrDestCol.SetFormulaCell(maDestPos, nRow, pCell, meListenType, rSrcCell.NeedsNumberFormat());
+ setDefaultAttrToDest(nRow);
+ return;
+ }
+
+ if (mrDestCol.GetDoc().IsUndo())
+ return;
+
+ if (bCloneValue)
+ {
+ FormulaError nErr = rSrcCell.GetErrCode();
+ if (nErr != FormulaError::NONE)
+ {
+ // error codes are cloned with values
+ ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos);
+ pErrCell->SetErrCode(nErr);
+ mrDestCol.SetFormulaCell(maDestPos, nRow, pErrCell, meListenType);
+ setDefaultAttrToDest(nRow);
+ return;
+ }
+ }
+
+ if (bCloneValue || bCloneDateTime)
+ {
+ if (rSrcCell.IsValue())
+ {
+ if (canCopyValue(mrSrcCol.GetDoc(), ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab()), mnCopyFlags))
+ {
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(
+ maDestPos.miCellPos, nRow, rSrcCell.GetValue());
+ setDefaultAttrToDest(nRow);
+ }
+
+ return;
+ }
+ }
+
+ if (!bCloneString)
+ return;
+
+ svl::SharedString aStr = rSrcCell.GetString();
+ if (aStr.isEmpty())
+ // Don't create empty string cells.
+ return;
+
+ if (rSrcCell.IsMultilineResult())
+ {
+ // Clone as an edit text object.
+ EditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine();
+ rEngine.SetText(aStr.getString());
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rEngine.CreateTextObject().release());
+ }
+ else
+ {
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aStr);
+ }
+
+ setDefaultAttrToDest(nRow);
+ }
+
+public:
+ CopyByCloneHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos,
+ InsertDeleteFlags nCopyFlags, svl::SharedStringPool* pSharedStringPool, bool bGlobalNamesToLocal) :
+ mrSrcCol(rSrcCol),
+ mrDestCol(rDestCol),
+ mpDestPos(pDestPos),
+ mpSharedStringPool(pSharedStringPool),
+ mnCopyFlags(nCopyFlags),
+ meListenType(sc::SingleCellListening),
+ mnFormulaCellCloneFlags(bGlobalNamesToLocal ? ScCloneFlags::NamesToLocal : ScCloneFlags::Default)
+ {
+ if (mpDestPos)
+ maDestPos = *mpDestPos;
+ }
+
+ ~CopyByCloneHandler()
+ {
+ if (!mpDestPos)
+ return;
+
+ // If broadcasters were setup in the same column,
+ // maDestPos.miBroadcasterPos doesn't match
+ // mrDestCol.maBroadcasters because it is never passed anywhere.
+ // Assign a corresponding iterator before copying all over.
+ // Otherwise this may result in wrongly copying a singular
+ // iterator.
+
+ {
+ /* XXX Using a temporary ColumnBlockPosition just for
+ * initializing from ScColumn::maBroadcasters.begin() is ugly,
+ * on the other hand we don't want to expose
+ * ScColumn::maBroadcasters to the outer world and have a
+ * getter. */
+ sc::ColumnBlockPosition aTempBlock;
+ mrDestCol.InitBlockPosition(aTempBlock);
+ maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
+ }
+
+ *mpDestPos = maDestPos;
+ }
+
+ void setStartListening( bool b )
+ {
+ meListenType = b ? sc::SingleCellListening : sc::NoListening;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nRow = aNode.position + nOffset;
+
+ if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
+ {
+ bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
+ }
+
+ switch (aNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
+ return;
+
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
+ for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
+ {
+ if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
+ continue;
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, *it);
+ setDefaultAttrToDest(nRow);
+ }
+ }
+ break;
+ case sc::element_type_string:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::STRING))
+ return;
+
+ sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::string_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; ++it, ++nRow)
+ {
+ const svl::SharedString& rStr = *it;
+ if (rStr.isEmpty())
+ {
+ // String cell with empty value is used to special-case cell value removal.
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set_empty(
+ maDestPos.miCellPos, nRow, nRow);
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set_empty(
+ maDestPos.miCellTextAttrPos, nRow, nRow);
+ }
+ else
+ {
+ if (mpSharedStringPool)
+ {
+ // Re-intern the string if source is a different document.
+ svl::SharedString aInterned = mpSharedStringPool->intern( rStr.getString());
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aInterned);
+ }
+ else
+ {
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rStr);
+ }
+ setDefaultAttrToDest(nRow);
+ }
+ }
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::STRING))
+ return;
+
+ sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::edittext_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ std::vector<EditTextObject*> aCloned;
+ aCloned.reserve(nDataSize);
+ for (; it != itEnd; ++it)
+ aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(
+ maDestPos.miCellPos, nRow, aCloned.begin(), aCloned.end());
+
+ setDefaultAttrsToDest(nRow, nDataSize);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ sc::DelayStartListeningFormulaCells startDelay(mrDestCol); // disabled
+ if(nDataSize > 1024 && (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE)
+ {
+ // If the column to be replaced contains a long formula group (tdf#102364), there can
+ // be so many listeners in a single vector that the quadratic cost of repeatedly removing
+ // the first element becomes very high. Optimize this by removing them in one go.
+ sc::EndListeningContext context(mrDestCol.GetDoc());
+ mrDestCol.EndListeningFormulaCells( context, nRow, nRow + nDataSize - 1, nullptr, nullptr );
+ // There can be a similar problem with starting to listen to cells repeatedly (tdf#133302).
+ // Delay it.
+ startDelay.set();
+ }
+
+ for (; it != itEnd; ++it, ++nRow)
+ cloneFormulaCell(nRow, **it);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+}
+
+void ScColumn::CopyToColumn(
+ sc::CopyToDocContext& rCxt,
+ SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked, ScColumn& rColumn,
+ const ScMarkData* pMarkData, bool bAsLink, bool bGlobalNamesToLocal) const
+{
+ if (bMarked)
+ {
+ SCROW nStart, nEnd;
+ if (pMarkData && pMarkData->IsMultiMarked())
+ {
+ ScMultiSelIter aIter( pMarkData->GetMultiSelData(), nCol );
+
+ while ( aIter.Next( nStart, nEnd ) && nStart <= nRow2 )
+ {
+ if ( nEnd >= nRow1 )
+ CopyToColumn(rCxt, std::max(nRow1,nStart), std::min(nRow2,nEnd),
+ nFlags, false, rColumn, pMarkData, bAsLink );
+ }
+ }
+ else
+ {
+ OSL_FAIL("CopyToColumn: bMarked, but no mark");
+ }
+ return;
+ }
+
+ if ( (nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE )
+ {
+ if ( (nFlags & InsertDeleteFlags::STYLES) != InsertDeleteFlags::STYLES )
+ { // keep the StyleSheets in the target document
+ // e.g. DIF and RTF Clipboard-Import
+ for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
+ {
+ const ScStyleSheet* pStyle =
+ rColumn.pAttrArray->GetPattern( nRow )->GetStyleSheet();
+ const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( *pPattern ));
+ pNewPattern->SetStyleSheet( const_cast<ScStyleSheet*>(pStyle) );
+ rColumn.pAttrArray->SetPattern( nRow, std::move(pNewPattern), true );
+ }
+ }
+ else
+ pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray);
+ }
+
+ if ((nFlags & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE)
+ return;
+
+ if (bAsLink)
+ {
+ CopyAsLinkHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags);
+ aFunc.setStartListening(rCxt.isStartListening());
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+ }
+ else
+ {
+ // Compare the ScDocumentPool* to determine if we are copying
+ // within the same document. If not, re-intern shared strings.
+ svl::SharedStringPool* pSharedStringPool =
+ (GetDoc().GetPool() != rColumn.GetDoc().GetPool()) ?
+ &rColumn.GetDoc().GetSharedStringPool() : nullptr;
+ CopyByCloneHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags,
+ pSharedStringPool, bGlobalNamesToLocal);
+ aFunc.setStartListening(rCxt.isStartListening());
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+ }
+
+ rColumn.CellStorageModified();
+}
+
+void ScColumn::UndoToColumn(
+ sc::CopyToDocContext& rCxt, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked,
+ ScColumn& rColumn ) const
+{
+ if (nRow1 > 0)
+ CopyToColumn(rCxt, 0, nRow1-1, InsertDeleteFlags::FORMULA, false, rColumn);
+
+ CopyToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, rColumn); //TODO: bMarked ????
+
+ if (nRow2 < GetDoc().MaxRow())
+ CopyToColumn(rCxt, nRow2+1, GetDoc().MaxRow(), InsertDeleteFlags::FORMULA, false, rColumn);
+}
+
+void ScColumn::CopyUpdated( const ScColumn* pPosCol, ScColumn& rDestCol ) const
+{
+ // Copy cells from this column to the destination column only for those
+ // rows that are present in the position column (pPosCol).
+
+ // First, mark all the non-empty cell ranges from the position column.
+ sc::SingleColumnSpanSet aRangeSet(GetDoc().GetSheetLimits());
+ if(pPosCol)
+ aRangeSet.scan(*pPosCol);
+
+ // Now, copy cells from this column to the destination column for those
+ // marked row ranges.
+ sc::SingleColumnSpanSet::SpansType aRanges;
+ aRangeSet.getSpans(aRanges);
+
+ CopyToClipHandler aFunc(GetDoc(), *this, rDestCol, nullptr);
+ sc::CellStoreType::const_iterator itPos = maCells.begin();
+ for (const auto& rRange : aRanges)
+ itPos = sc::ParseBlock(itPos, maCells, aFunc, rRange.mnRow1, rRange.mnRow2);
+
+ rDestCol.CellStorageModified();
+}
+
+void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol )
+{
+ // This is the scenario table, the data is copied into it
+ ScDocument& rDocument = GetDoc();
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), rDocument.GetDefPattern() );
+ SCROW nStart = -1, nEnd = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ {
+ DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
+ sc::CopyToDocContext aCxt(rDocument);
+ rSrcCol.
+ CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, *this);
+
+ // UpdateUsed not needed, already done in TestCopyScenario (obsolete comment ?)
+
+ sc::RefUpdateContext aRefCxt(rDocument);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = ScRange(nCol, nStart, nTab, nCol, nEnd, nTab);
+ aRefCxt.mnTabDelta = nTab - rSrcCol.nTab;
+ UpdateReferenceOnCopy(aRefCxt);
+ UpdateCompile();
+ }
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+}
+
+void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const
+{
+ // This is the scenario table, the data is copied to the other
+ ScDocument& rDocument = GetDoc();
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), rDocument.GetDefPattern() );
+ SCROW nStart = -1, nEnd = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ {
+ rDestCol.DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
+ sc::CopyToDocContext aCxt(rDestCol.GetDoc());
+ CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, rDestCol);
+
+ sc::RefUpdateContext aRefCxt(rDocument);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = ScRange(rDestCol.nCol, nStart, rDestCol.nTab, rDestCol.nCol, nEnd, rDestCol.nTab);
+ aRefCxt.mnTabDelta = rDestCol.nTab - nTab;
+ rDestCol.UpdateReferenceOnCopy(aRefCxt);
+ rDestCol.UpdateCompile();
+ }
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+}
+
+bool ScColumn::TestCopyScenarioTo( const ScColumn& rDestCol ) const
+{
+ bool bOk = true;
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), GetDoc().GetDefPattern() );
+ SCROW nStart = 0, nEnd = 0;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern && bOk)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ if ( rDestCol.pAttrArray->HasAttrib( nStart, nEnd, HasAttrFlags::Protected ) )
+ bOk = false;
+
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+ return bOk;
+}
+
+void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const
+{
+ ScRange aRange( nCol, 0, nTab );
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), GetDoc().GetDefPattern() );
+ SCROW nStart = -1, nEnd = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ {
+ aRange.aStart.SetRow( nStart );
+ aRange.aEnd.SetRow( nEnd );
+ rDestMark.SetMultiMarkArea( aRange );
+ }
+
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+}
+
+namespace {
+
+void resetColumnPosition(sc::CellStoreType& rCells, SCCOL nCol)
+{
+ for (auto& rCellItem : rCells)
+ {
+ if (rCellItem.type != sc::element_type_formula)
+ continue;
+
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*rCellItem.data);
+ sc::formula_block::iterator itCellEnd = sc::formula_block::end(*rCellItem.data);
+ for (; itCell != itCellEnd; ++itCell)
+ {
+ ScFormulaCell& rCell = **itCell;
+ rCell.aPos.SetCol(nCol);
+ }
+ }
+}
+
+class NoteCaptionUpdater
+{
+ const ScDocument& m_rDocument;
+ const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
+ bool m_bUpdateCaptionPos; // false if we want to skip updating the caption pos, only useful in kit mode
+ bool m_bAddressChanged; // false if the cell anchor address is unchanged
+public:
+ NoteCaptionUpdater(const ScDocument& rDocument, const ScAddress& rPos, bool bUpdateCaptionPos, bool bAddressChanged)
+ : m_rDocument(rDocument)
+ , m_aAddress(rPos)
+ , m_bUpdateCaptionPos(bUpdateCaptionPos)
+ , m_bAddressChanged(bAddressChanged)
+ {
+ }
+
+ void operator() ( size_t nRow, ScPostIt* p )
+ {
+ // Create a 'complete' address object
+ ScAddress aAddr(m_aAddress);
+ aAddr.SetRow(nRow);
+
+ if (m_bUpdateCaptionPos)
+ p->UpdateCaptionPos(aAddr);
+
+ // Notify our LOK clients
+ if (m_bAddressChanged)
+ ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Modify, m_rDocument, aAddr, p);
+ }
+};
+
+}
+
+void ScColumn::UpdateNoteCaptions( SCROW nRow1, SCROW nRow2, bool bAddressChanged )
+{
+ ScAddress aAddr(nCol, 0, nTab);
+ NoteCaptionUpdater aFunc(GetDoc(), aAddr, true, bAddressChanged);
+ sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::CommentNotifyAddressChange( SCROW nRow1, SCROW nRow2 )
+{
+ ScAddress aAddr(nCol, 0, nTab);
+ NoteCaptionUpdater aFunc(GetDoc(), aAddr, false, true);
+ sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::UpdateDrawObjects(std::vector<std::vector<SdrObject*>>& pObjects, SCROW nRowStart, SCROW nRowEnd)
+{
+ assert(static_cast<int>(pObjects.size()) >= nRowEnd - nRowStart + 1);
+
+ int nObj = 0;
+ for (SCROW nCurrentRow = nRowStart; nCurrentRow <= nRowEnd; nCurrentRow++, nObj++)
+ {
+ if (pObjects[nObj].empty())
+ continue; // No draw objects in this row
+
+ UpdateDrawObjectsForRow(pObjects[nObj], nCol, nCurrentRow);
+ }
+}
+
+void ScColumn::UpdateDrawObjectsForRow( std::vector<SdrObject*>& pObjects, SCCOL nTargetCol, SCROW nTargetRow )
+{
+ for (auto &pObject : pObjects)
+ {
+ ScAddress aNewAddress(nTargetCol, nTargetRow, nTab);
+
+ // Update draw object according to new anchor
+ ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
+ if (pDrawLayer)
+ pDrawLayer->MoveObject(pObject, aNewAddress);
+ }
+}
+
+bool ScColumn::IsDrawObjectsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
+{
+ ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
+ if (!pDrawLayer)
+ return true;
+
+ ScRange aRange(nCol, nStartRow, nTab, nCol, nEndRow, nTab);
+ return !pDrawLayer->HasObjectsAnchoredInRange(aRange);
+}
+
+void ScColumn::SwapCol(ScColumn& rCol)
+{
+ maBroadcasters.swap(rCol.maBroadcasters);
+ maCells.swap(rCol.maCells);
+ maCellTextAttrs.swap(rCol.maCellTextAttrs);
+ maCellNotes.swap(rCol.maCellNotes);
+ maSparklines.swap(rCol.maSparklines);
+
+ // Swap all CellStoreEvent mdds event_func related.
+ maCells.event_handler().swap(rCol.maCells.event_handler());
+ std::swap( mnBlkCountFormula, rCol.mnBlkCountFormula);
+
+ // notes update caption
+ UpdateNoteCaptions(0, GetDoc().MaxRow());
+ rCol.UpdateNoteCaptions(0, GetDoc().MaxRow());
+
+ std::swap(pAttrArray, rCol.pAttrArray);
+
+ // AttrArray needs to have the right column number
+ pAttrArray->SetCol(nCol);
+ rCol.pAttrArray->SetCol(rCol.nCol);
+
+ // Reset column positions in formula cells.
+ resetColumnPosition(maCells, nCol);
+ resetColumnPosition(rCol.maCells, rCol.nCol);
+
+ CellStorageModified();
+ rCol.CellStorageModified();
+}
+
+void ScColumn::MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol)
+{
+ pAttrArray->MoveTo(nStartRow, nEndRow, *rCol.pAttrArray);
+
+ // Mark the non-empty cells within the specified range, for later broadcasting.
+ sc::SingleColumnSpanSet aNonEmpties(GetDoc().GetSheetLimits());
+ aNonEmpties.scan(*this, nStartRow, nEndRow);
+ sc::SingleColumnSpanSet::SpansType aRanges;
+ aNonEmpties.getSpans(aRanges);
+
+ // Split the formula grouping at the top and bottom boundaries.
+ sc::CellStoreType::position_type aPos = maCells.position(nStartRow);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ if (GetDoc().ValidRow(nEndRow+1))
+ {
+ aPos = maCells.position(aPos.first, nEndRow+1);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ }
+
+ // Do the same with the destination column.
+ aPos = rCol.maCells.position(nStartRow);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ if (GetDoc().ValidRow(nEndRow+1))
+ {
+ aPos = rCol.maCells.position(aPos.first, nEndRow+1);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ }
+
+ // Move the broadcasters to the destination column.
+ maBroadcasters.transfer(nStartRow, nEndRow, rCol.maBroadcasters, nStartRow);
+ maCells.transfer(nStartRow, nEndRow, rCol.maCells, nStartRow);
+ maCellTextAttrs.transfer(nStartRow, nEndRow, rCol.maCellTextAttrs, nStartRow);
+
+ // move the notes to the destination column
+ maCellNotes.transfer(nStartRow, nEndRow, rCol.maCellNotes, nStartRow);
+ UpdateNoteCaptions(0, GetDoc().MaxRow());
+
+ // Re-group transferred formula cells.
+ aPos = rCol.maCells.position(nStartRow);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (GetDoc().ValidRow(nEndRow+1))
+ {
+ aPos = rCol.maCells.position(aPos.first, nEndRow+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+
+ CellStorageModified();
+ rCol.CellStorageModified();
+
+ // Broadcast on moved ranges. Area-broadcast only.
+ ScDocument& rDocument = GetDoc();
+ ScHint aHint(SfxHintId::ScDataChanged, ScAddress(nCol, 0, nTab));
+ for (const auto& rRange : aRanges)
+ {
+ for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow)
+ {
+ aHint.SetAddressRow(nRow);
+ rDocument.AreaBroadcast(aHint);
+ }
+ }
+}
+
+namespace {
+
+class SharedTopFormulaCellPicker
+{
+public:
+ SharedTopFormulaCellPicker() = default;
+ SharedTopFormulaCellPicker(SharedTopFormulaCellPicker const &) = default;
+ SharedTopFormulaCellPicker(SharedTopFormulaCellPicker &&) = default;
+ SharedTopFormulaCellPicker & operator =(SharedTopFormulaCellPicker const &) = default;
+ SharedTopFormulaCellPicker & operator =(SharedTopFormulaCellPicker &&) = default;
+
+ virtual ~SharedTopFormulaCellPicker() {}
+
+ void operator() ( sc::CellStoreType::value_type& node )
+ {
+ if (node.type != sc::element_type_formula)
+ return;
+
+ size_t nTopRow = node.position;
+
+ sc::formula_block::iterator itBeg = sc::formula_block::begin(*node.data);
+ sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data);
+
+ // Only pick shared formula cells that are the top cells of their
+ // respective shared ranges.
+ for (sc::formula_block::iterator it = itBeg; it != itEnd; ++it)
+ {
+ ScFormulaCell* pCell = *it;
+ size_t nRow = nTopRow + std::distance(itBeg, it);
+ if (!pCell->IsShared())
+ {
+ processNonShared(pCell, nRow);
+ continue;
+ }
+
+ if (pCell->IsSharedTop())
+ {
+ ScFormulaCell** pp = &(*it);
+ processSharedTop(pp, nRow, pCell->GetSharedLength());
+
+ // Move to the last cell in the group, to get incremented to
+ // the next cell in the next iteration.
+ size_t nOffsetToLast = pCell->GetSharedLength() - 1;
+ std::advance(it, nOffsetToLast);
+ }
+ }
+ }
+
+ virtual void processNonShared( ScFormulaCell* /*pCell*/, size_t /*nRow*/ ) {}
+ virtual void processSharedTop( ScFormulaCell** /*ppCells*/, size_t /*nRow*/, size_t /*nLength*/ ) {}
+};
+
+class UpdateRefOnCopy
+{
+ const sc::RefUpdateContext& mrCxt;
+ ScDocument* mpUndoDoc;
+ bool mbUpdated;
+
+public:
+ UpdateRefOnCopy(const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc) :
+ mrCxt(rCxt), mpUndoDoc(pUndoDoc), mbUpdated(false) {}
+
+ bool isUpdated() const { return mbUpdated; }
+
+ void operator() (sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ if (node.type != sc::element_type_formula)
+ return;
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::formula_block::iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; ++it)
+ {
+ ScFormulaCell& rCell = **it;
+ mbUpdated |= rCell.UpdateReference(mrCxt, mpUndoDoc);
+ }
+ }
+};
+
+class UpdateRefOnNonCopy
+{
+ SCCOL mnCol;
+ SCROW mnTab;
+ const sc::RefUpdateContext* mpCxt;
+ ScDocument* mpUndoDoc;
+ bool mbUpdated;
+ bool mbClipboardSource;
+
+ void recompileTokenArray( ScFormulaCell& rTopCell )
+ {
+ // We need to re-compile the token array when a range name is
+ // modified, to correctly reflect the new references in the
+ // name.
+ ScCompiler aComp(mpCxt->mrDoc, rTopCell.aPos, *rTopCell.GetCode(), mpCxt->mrDoc.GetGrammar(),
+ true, rTopCell.GetMatrixFlag() != ScMatrixMode::NONE);
+ aComp.CompileTokenArray();
+ }
+
+ void updateRefOnShift( sc::FormulaGroupEntry& rGroup )
+ {
+ if (!rGroup.mbShared)
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ mbUpdated |= rGroup.mpCell->UpdateReferenceOnShift(*mpCxt, mpUndoDoc, &aUndoPos);
+ return;
+ }
+
+ // Update references of a formula group.
+ ScFormulaCell** pp = rGroup.mpCells;
+ ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+ ScFormulaCell* pTop = *pp;
+ ScTokenArray* pCode = pTop->GetCode();
+ ScTokenArray aOldCode(pCode->CloneValue());
+ ScAddress aOldPos = pTop->aPos;
+
+ // Run this before the position gets updated.
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(*mpCxt, aOldPos);
+
+ bool bGroupShifted = false;
+ if (pTop->UpdatePosOnShift(*mpCxt))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ // Update the positions of all formula cells.
+ for (++pp; pp != ppEnd; ++pp) // skip the top cell.
+ {
+ ScFormulaCell* pFC = *pp;
+ if (!pFC->aPos.Move(mpCxt->mnColDelta, mpCxt->mnRowDelta, mpCxt->mnTabDelta,
+ aErrorPos, mpCxt->mrDoc))
+ {
+ assert(!"can't move formula cell");
+ }
+ }
+
+ if (pCode->IsRecalcModeOnRefMove())
+ aRes.mbValueChanged = true;
+
+ // FormulaGroupAreaListener (contrary to ScBroadcastArea) is not
+ // updated but needs to be re-setup, else at least its mpColumn
+ // would indicate the old column to collect cells from. tdf#129396
+ /* TODO: investigate if that could be short-cut to avoid all the
+ * EndListeningTo() / StartListeningTo() overhead and is really
+ * only necessary when shifting the column, not also when shifting
+ * rows. */
+ bGroupShifted = true;
+ }
+ else if (aRes.mbReferenceModified && pCode->IsRecalcModeOnRefMove())
+ {
+ // The cell itself hasn't shifted. But it may have ROW or COLUMN
+ // referencing another cell that has.
+ aRes.mbValueChanged = true;
+ }
+
+ if (aRes.mbNameModified)
+ recompileTokenArray(*pTop);
+
+ if (aRes.mbReferenceModified || aRes.mbNameModified || bGroupShifted)
+ {
+ sc::EndListeningContext aEndCxt(mpCxt->mrDoc, &aOldCode);
+ aEndCxt.setPositionDelta(
+ ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
+
+ for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->EndListeningTo(aEndCxt);
+ p->SetNeedsListening(true);
+ }
+
+ mbUpdated = true;
+
+ fillUndoDoc(aOldPos, rGroup.mnLength, aOldCode);
+ }
+
+ if (aRes.mbValueChanged)
+ {
+ for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->SetNeedsDirty(true);
+ }
+ }
+ }
+
+ void updateRefOnMove( sc::FormulaGroupEntry& rGroup )
+ {
+ if (!rGroup.mbShared)
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ mbUpdated |= rGroup.mpCell->UpdateReferenceOnMove(*mpCxt, mpUndoDoc, &aUndoPos);
+ return;
+ }
+
+ // Update references of a formula group.
+ ScFormulaCell** pp = rGroup.mpCells;
+ ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+ ScFormulaCell* pTop = *pp;
+ ScTokenArray* pCode = pTop->GetCode();
+ ScTokenArray aOldCode(pCode->CloneValue());
+
+ ScAddress aPos = pTop->aPos;
+ ScAddress aOldPos = aPos;
+
+ bool bCellMoved;
+ if (mpCxt->maRange.Contains(aPos))
+ {
+ bCellMoved = true;
+
+ // The cell is being moved or copied to a new position. The
+ // position has already been updated prior to this call.
+ // Determine its original position before the move which will be
+ // used to adjust relative references later.
+
+ aOldPos.Set(
+ aPos.Col() - mpCxt->mnColDelta,
+ aPos.Row() - mpCxt->mnRowDelta,
+ aPos.Tab() - mpCxt->mnTabDelta);
+ }
+ else
+ {
+ bCellMoved = false;
+ }
+
+ bool bRecalcOnMove = pCode->IsRecalcModeOnRefMove();
+ if (bRecalcOnMove)
+ bRecalcOnMove = aPos != aOldPos;
+
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(*mpCxt, aOldPos, aPos);
+
+ if (!(aRes.mbReferenceModified || aRes.mbNameModified || bRecalcOnMove))
+ return;
+
+ sc::AutoCalcSwitch aACSwitch(mpCxt->mrDoc, false);
+
+ if (aRes.mbNameModified)
+ recompileTokenArray(*pTop);
+
+ // Perform end-listening, start-listening, and dirtying on all
+ // formula cells in the group.
+
+ // Make sure that the start and end listening contexts share the
+ // same block position set, else an invalid iterator may ensue.
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(mpCxt->mrDoc);
+
+ sc::StartListeningContext aStartCxt(mpCxt->mrDoc, pPosSet);
+ sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pPosSet, &aOldCode);
+
+ aEndCxt.setPositionDelta(
+ ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
+
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->EndListeningTo(aEndCxt);
+ p->StartListeningTo(aStartCxt);
+ p->SetDirty();
+ }
+
+ mbUpdated = true;
+
+ // Move from clipboard is Cut&Paste, then do not copy the original
+ // positions' formula cells to the Undo document.
+ if (!mbClipboardSource || !bCellMoved)
+ fillUndoDoc(aOldPos, rGroup.mnLength, aOldCode);
+ }
+
+ void fillUndoDoc( const ScAddress& rOldPos, SCROW nLength, const ScTokenArray& rOldCode )
+ {
+ if (!mpUndoDoc || nLength <= 0)
+ return;
+
+ // Insert the old formula group into the undo document.
+ ScAddress aUndoPos = rOldPos;
+ ScFormulaCell* pFC = new ScFormulaCell(*mpUndoDoc, aUndoPos, rOldCode.Clone());
+
+ if (nLength == 1)
+ {
+ mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
+ return;
+ }
+
+ std::vector<ScFormulaCell*> aCells;
+ aCells.reserve(nLength);
+ ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(nLength, false);
+ aCells.push_back(pFC);
+ aUndoPos.IncRow();
+ for (SCROW i = 1; i < nLength; ++i, aUndoPos.IncRow())
+ {
+ pFC = new ScFormulaCell(*mpUndoDoc, aUndoPos, xGroup);
+ aCells.push_back(pFC);
+ }
+
+ if (!mpUndoDoc->SetFormulaCells(rOldPos, aCells))
+ // Insertion failed. Delete all formula cells.
+ std::for_each(aCells.begin(), aCells.end(), std::default_delete<ScFormulaCell>());
+ }
+
+public:
+ UpdateRefOnNonCopy(
+ SCCOL nCol, SCTAB nTab, const sc::RefUpdateContext* pCxt,
+ ScDocument* pUndoDoc) :
+ mnCol(nCol), mnTab(nTab), mpCxt(pCxt),
+ mpUndoDoc(pUndoDoc), mbUpdated(false),
+ mbClipboardSource(pCxt->mrDoc.IsClipboardSource()){}
+
+ void operator() ( sc::FormulaGroupEntry& rGroup )
+ {
+ switch (mpCxt->meMode)
+ {
+ case URM_INSDEL:
+ updateRefOnShift(rGroup);
+ return;
+ case URM_MOVE:
+ updateRefOnMove(rGroup);
+ return;
+ default:
+ ;
+ }
+
+ if (rGroup.mbShared)
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ ScFormulaCell** pp = rGroup.mpCells;
+ ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+ for (; pp != ppEnd; ++pp, aUndoPos.IncRow())
+ {
+ ScFormulaCell* p = *pp;
+ mbUpdated |= p->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
+ }
+ }
+ else
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ mbUpdated |= rGroup.mpCell->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
+ }
+ }
+
+ bool isUpdated() const { return mbUpdated; }
+};
+
+class UpdateRefGroupBoundChecker : public SharedTopFormulaCellPicker
+{
+ const sc::RefUpdateContext& mrCxt;
+ std::vector<SCROW>& mrBounds;
+
+public:
+ UpdateRefGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
+ mrCxt(rCxt), mrBounds(rBounds) {}
+
+ virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ ) override
+ {
+ // Check its tokens and record its reference boundaries.
+ ScFormulaCell& rCell = **ppCells;
+ const ScTokenArray& rCode = *rCell.GetCode();
+ rCode.CheckRelativeReferenceBounds(
+ mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
+ }
+};
+
+class UpdateRefExpandGroupBoundChecker : public SharedTopFormulaCellPicker
+{
+ const sc::RefUpdateContext& mrCxt;
+ std::vector<SCROW>& mrBounds;
+
+public:
+ UpdateRefExpandGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
+ mrCxt(rCxt), mrBounds(rBounds) {}
+
+ virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ ) override
+ {
+ // Check its tokens and record its reference boundaries.
+ ScFormulaCell& rCell = **ppCells;
+ const ScTokenArray& rCode = *rCell.GetCode();
+ rCode.CheckExpandReferenceBounds(
+ mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
+ }
+};
+
+class FormulaGroupPicker : public SharedTopFormulaCellPicker
+{
+ std::vector<sc::FormulaGroupEntry>& mrGroups;
+
+public:
+ explicit FormulaGroupPicker( std::vector<sc::FormulaGroupEntry>& rGroups ) : mrGroups(rGroups) {}
+
+ virtual void processNonShared( ScFormulaCell* pCell, size_t nRow ) override
+ {
+ mrGroups.emplace_back(pCell, nRow);
+ }
+
+ virtual void processSharedTop( ScFormulaCell** ppCells, size_t nRow, size_t nLength ) override
+ {
+ mrGroups.emplace_back(ppCells, nRow, nLength);
+ }
+};
+
+}
+
+bool ScColumn::UpdateReferenceOnCopy( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc )
+{
+ // When copying, the range equals the destination range where cells
+ // are pasted, and the dx, dy, dz refer to the distance from the
+ // source range.
+
+ UpdateRefOnCopy aHandler(rCxt, pUndoDoc);
+ sc::ColumnBlockPosition* blockPos = rCxt.getBlockPosition(nTab, nCol);
+ sc::CellStoreType::position_type aPos = blockPos
+ ? maCells.position(blockPos->miCellPos, rCxt.maRange.aStart.Row())
+ : maCells.position(rCxt.maRange.aStart.Row());
+ sc::ProcessBlock(aPos.first, maCells, aHandler, rCxt.maRange.aStart.Row(), rCxt.maRange.aEnd.Row());
+
+ // The formula groups at the top and bottom boundaries are expected to
+ // have been split prior to this call. Here, we only do the joining.
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (rCxt.maRange.aEnd.Row() < GetDoc().MaxRow())
+ {
+ aPos = maCells.position(aPos.first, rCxt.maRange.aEnd.Row()+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+
+ return aHandler.isUpdated();
+}
+
+bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc )
+{
+ if (IsEmptyData() || GetDoc().IsClipOrUndo())
+ // Cells in this column are all empty, or clip or undo doc. No update needed.
+ return false;
+
+ if (rCxt.meMode == URM_COPY)
+ return UpdateReferenceOnCopy(rCxt, pUndoDoc);
+
+ std::vector<SCROW> aBounds;
+
+ bool bThisColShifted = (rCxt.maRange.aStart.Tab() <= nTab && nTab <= rCxt.maRange.aEnd.Tab() &&
+ rCxt.maRange.aStart.Col() <= nCol && nCol <= rCxt.maRange.aEnd.Col());
+ if (bThisColShifted)
+ {
+ // Cells in this column is being shifted. Split formula grouping at
+ // the top and bottom boundaries before they get shifted.
+ // Also, for deleted rows split at the top of the deleted area to adapt
+ // the affected group length.
+ SCROW nSplitPos;
+ if (rCxt.mnRowDelta < 0)
+ {
+ nSplitPos = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
+ if (GetDoc().ValidRow(nSplitPos))
+ aBounds.push_back(nSplitPos);
+ }
+ nSplitPos = rCxt.maRange.aStart.Row();
+ if (GetDoc().ValidRow(nSplitPos))
+ {
+ aBounds.push_back(nSplitPos);
+ nSplitPos = rCxt.maRange.aEnd.Row() + 1;
+ if (GetDoc().ValidRow(nSplitPos))
+ aBounds.push_back(nSplitPos);
+ }
+ }
+
+ // Check the row positions at which the group must be split per relative
+ // references.
+ UpdateRefGroupBoundChecker aBoundChecker(rCxt, aBounds);
+ std::for_each(maCells.begin(), maCells.end(), aBoundChecker);
+
+ // If expand reference edges is on, splitting groups may happen anywhere
+ // where a reference points to an adjacent row of the insertion.
+ if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
+ {
+ UpdateRefExpandGroupBoundChecker aExpandChecker(rCxt, aBounds);
+ std::for_each(maCells.begin(), maCells.end(), aExpandChecker);
+ }
+
+ // Do the actual splitting.
+ const bool bSplit = sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
+
+ // Collect all formula groups.
+ std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
+
+ // Process all collected formula groups.
+ UpdateRefOnNonCopy aHandler(nCol, nTab, &rCxt, pUndoDoc);
+ aHandler = std::for_each(aGroups.begin(), aGroups.end(), aHandler);
+ if (bSplit || aHandler.isUpdated())
+ rCxt.maRegroupCols.set(nTab, nCol);
+
+ return aHandler.isUpdated();
+}
+
+std::vector<sc::FormulaGroupEntry> ScColumn::GetFormulaGroupEntries()
+{
+ std::vector<sc::FormulaGroupEntry> aGroups;
+ std::for_each(maCells.begin(), maCells.end(), FormulaGroupPicker(aGroups));
+ return aGroups;
+}
+
+namespace {
+
+class UpdateTransHandler
+{
+ ScColumn& mrColumn;
+ sc::CellStoreType::iterator miPos;
+ ScRange maSource;
+ ScAddress maDest;
+ ScDocument* mpUndoDoc;
+public:
+ UpdateTransHandler(ScColumn& rColumn, const ScRange& rSource, const ScAddress& rDest, ScDocument* pUndoDoc) :
+ mrColumn(rColumn),
+ miPos(rColumn.GetCellStore().begin()),
+ maSource(rSource), maDest(rDest), mpUndoDoc(pUndoDoc) {}
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
+ miPos = aPos.first;
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
+ pCell->UpdateTranspose(maSource, maDest, mpUndoDoc);
+ ScColumn::JoinNewFormulaCell(aPos, *pCell);
+ }
+};
+
+class UpdateGrowHandler
+{
+ ScColumn& mrColumn;
+ sc::CellStoreType::iterator miPos;
+ ScRange maArea;
+ SCCOL mnGrowX;
+ SCROW mnGrowY;
+public:
+ UpdateGrowHandler(ScColumn& rColumn, const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY) :
+ mrColumn(rColumn),
+ miPos(rColumn.GetCellStore().begin()),
+ maArea(rArea), mnGrowX(nGrowX), mnGrowY(nGrowY) {}
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
+ miPos = aPos.first;
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
+ pCell->UpdateGrow(maArea, mnGrowX, mnGrowY);
+ ScColumn::JoinNewFormulaCell(aPos, *pCell);
+ }
+};
+
+class InsertTabUpdater
+{
+ sc::RefUpdateInsertTabContext& mrCxt;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ bool mbModified;
+
+public:
+ InsertTabUpdater(sc::RefUpdateInsertTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
+ mrCxt(rCxt),
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mbModified(false) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateInsertTab(mrCxt);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class DeleteTabUpdater
+{
+ sc::RefUpdateDeleteTabContext& mrCxt;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ bool mbModified;
+public:
+ DeleteTabUpdater(sc::RefUpdateDeleteTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
+ mrCxt(rCxt),
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mbModified(false) {}
+
+ void operator() (size_t, ScFormulaCell* pCell)
+ {
+ pCell->UpdateDeleteTab(mrCxt);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class InsertAbsTabUpdater
+{
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ SCTAB mnNewPos;
+ bool mbModified;
+public:
+ InsertAbsTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab, SCTAB nNewPos) :
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mnNewPos(nNewPos),
+ mbModified(false) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateInsertTabAbs(mnNewPos);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class MoveTabUpdater
+{
+ sc::RefUpdateMoveTabContext& mrCxt;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ bool mbModified;
+public:
+ MoveTabUpdater(sc::RefUpdateMoveTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
+ mrCxt(rCxt),
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mbModified(false) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateMoveTab(mrCxt, mnTab);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class UpdateCompileHandler
+{
+ bool mbForceIfNameInUse:1;
+public:
+ explicit UpdateCompileHandler(bool bForceIfNameInUse) :
+ mbForceIfNameInUse(bForceIfNameInUse) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateCompile(mbForceIfNameInUse);
+ }
+};
+
+class TabNoSetter
+{
+ SCTAB mnTab;
+public:
+ explicit TabNoSetter(SCTAB nTab) : mnTab(nTab) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->aPos.SetTab(mnTab);
+ }
+};
+
+class UsedRangeNameFinder
+{
+ sc::UpdatedRangeNames& mrIndexes;
+public:
+ explicit UsedRangeNameFinder(sc::UpdatedRangeNames& rIndexes) : mrIndexes(rIndexes) {}
+
+ void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
+ {
+ pCell->FindRangeNamesInUse(mrIndexes);
+ }
+};
+
+class CheckVectorizationHandler
+{
+public:
+ CheckVectorizationHandler()
+ {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ ScTokenArray* pCode = p->GetCode();
+ if (pCode && pCode->IsFormulaVectorDisabled())
+ {
+ pCode->ResetVectorState();
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ FormulaToken* pFT = aIter.First();
+ while (pFT)
+ {
+ pCode->CheckToken(*pFT);
+ pFT = aIter.Next();
+ }
+ }
+ }
+};
+
+struct SetDirtyVarHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ p->SetDirtyVar();
+ }
+};
+
+class SetDirtyHandler
+{
+ ScDocument& mrDoc;
+ const sc::SetFormulaDirtyContext& mrCxt;
+public:
+ SetDirtyHandler( ScDocument& rDoc, const sc::SetFormulaDirtyContext& rCxt ) :
+ mrDoc(rDoc), mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ if (mrCxt.mbClearTabDeletedFlag)
+ {
+ if (!p->IsShared() || p->IsSharedTop())
+ {
+ ScTokenArray* pCode = p->GetCode();
+ pCode->ClearTabDeleted(
+ p->aPos, mrCxt.mnTabDeletedStart, mrCxt.mnTabDeletedEnd);
+ }
+ }
+
+ p->SetDirtyVar();
+ if (!mrDoc.IsInFormulaTree(p))
+ mrDoc.PutInFormulaTree(p);
+ }
+};
+
+class SetDirtyOnRangeHandler
+{
+ sc::SingleColumnSpanSet maValueRanges;
+ ScColumn& mrColumn;
+public:
+ explicit SetDirtyOnRangeHandler(ScColumn& rColumn)
+ : maValueRanges(rColumn.GetDoc().GetSheetLimits()),
+ mrColumn(rColumn) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ p->SetDirty();
+ }
+
+ void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize)
+ {
+ if (type == sc::element_type_empty)
+ // Ignore empty blocks.
+ return;
+
+ // Non-formula cells.
+ SCROW nRow1 = nTopRow;
+ SCROW nRow2 = nTopRow + nDataSize - 1;
+ maValueRanges.set(nRow1, nRow2, true);
+ }
+
+ void broadcast()
+ {
+ std::vector<SCROW> aRows;
+ maValueRanges.getRows(aRows);
+ mrColumn.BroadcastCells(aRows, SfxHintId::ScDataChanged);
+ }
+
+ void fillBroadcastSpans( sc::ColumnSpanSet& rBroadcastSpans ) const
+ {
+ SCCOL nCol = mrColumn.GetCol();
+ SCTAB nTab = mrColumn.GetTab();
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ maValueRanges.getSpans(aSpans);
+
+ for (const auto& rSpan : aSpans)
+ rBroadcastSpans.set(mrColumn.GetDoc(), nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, true);
+ }
+};
+
+class SetTableOpDirtyOnRangeHandler
+{
+ sc::SingleColumnSpanSet maValueRanges;
+ ScColumn& mrColumn;
+public:
+ explicit SetTableOpDirtyOnRangeHandler(ScColumn& rColumn)
+ : maValueRanges(rColumn.GetDoc().GetSheetLimits()),
+ mrColumn(rColumn) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ p->SetTableOpDirty();
+ }
+
+ void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize)
+ {
+ if (type == sc::element_type_empty)
+ // Ignore empty blocks.
+ return;
+
+ // Non-formula cells.
+ SCROW nRow1 = nTopRow;
+ SCROW nRow2 = nTopRow + nDataSize - 1;
+ maValueRanges.set(nRow1, nRow2, true);
+ }
+
+ void broadcast()
+ {
+ std::vector<SCROW> aRows;
+ maValueRanges.getRows(aRows);
+ mrColumn.BroadcastCells(aRows, SfxHintId::ScTableOpDirty);
+ }
+};
+
+struct SetDirtyAfterLoadHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+#if 1
+ // Simply set dirty and append to FormulaTree, without broadcasting,
+ // which is a magnitude faster. This is used to calculate the entire
+ // document, e.g. when loading alien file formats.
+ pCell->SetDirtyAfterLoad();
+#else
+/* This was used with the binary file format that stored results, where only
+ * newly compiled and volatile functions and their dependents had to be
+ * recalculated, which was faster then. Since that was moved to 'binfilter' to
+ * convert to an XML file this isn't needed anymore, and not used for other
+ * file formats. Kept for reference in case mechanism needs to be reactivated
+ * for some file formats, we'd have to introduce a controlling parameter to
+ * this method here then.
+*/
+
+ // If the cell was already dirty because of CalcAfterLoad,
+ // FormulaTracking has to take place.
+ if (pCell->GetDirty())
+ pCell->SetDirty();
+#endif
+ }
+};
+
+struct SetDirtyIfPostponedHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ if (pCell->IsPostponedDirty() || (pCell->HasRelNameReference() != ScFormulaCell::RelNameRef::NONE))
+ pCell->SetDirty();
+ }
+};
+
+struct CalcAllHandler
+{
+#define DEBUG_SC_CHECK_FORMULATREE_CALCULATION 0
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+#if DEBUG_SC_CHECK_FORMULATREE_CALCULATION
+ // after F9 ctrl-F9: check the calculation for each FormulaTree
+ double nOldVal, nNewVal;
+ nOldVal = pCell->GetValue();
+#endif
+ pCell->Interpret();
+#if DEBUG_SC_CHECK_FORMULATREE_CALCULATION
+ if (pCell->GetCode()->IsRecalcModeNormal())
+ nNewVal = pCell->GetValue();
+ else
+ nNewVal = nOldVal; // random(), jetzt() etc.
+
+ assert(nOldVal == nNewVal);
+#endif
+ }
+#undef DEBUG_SC_CHECK_FORMULATREE_CALCULATION
+};
+
+class CompileAllHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+public:
+ explicit CompileAllHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ // for unconditional compilation
+ // bCompile=true and pCode->nError=0
+ pCell->GetCode()->SetCodeError(FormulaError::NONE);
+ pCell->SetCompile(true);
+ pCell->CompileTokenArray(mrCxt);
+ }
+};
+
+class CompileXMLHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+ ScProgress& mrProgress;
+ const ScColumn& mrCol;
+public:
+ CompileXMLHandler( sc::CompileFormulaContext& rCxt, ScProgress& rProgress, const ScColumn& rCol) :
+ mrCxt(rCxt),
+ mrProgress(rProgress),
+ mrCol(rCol) {}
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ sal_uInt32 nFormat = mrCol.GetNumberFormat(mrCol.GetDoc().GetNonThreadedContext(), nRow);
+ if( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
+ // Non-default number format is set.
+ pCell->SetNeedNumberFormat(false);
+ else if (pCell->NeedsNumberFormat())
+ pCell->SetDirtyVar();
+
+ if (pCell->GetMatrixFlag() != ScMatrixMode::NONE)
+ pCell->SetDirtyVar();
+
+ pCell->CompileXML(mrCxt, mrProgress);
+ }
+};
+
+class CompileErrorCellsHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+ ScColumn& mrColumn;
+ sc::CellStoreType::iterator miPos;
+ FormulaError mnErrCode;
+ bool mbCompiled;
+public:
+ CompileErrorCellsHandler( sc::CompileFormulaContext& rCxt, ScColumn& rColumn, FormulaError nErrCode ) :
+ mrCxt(rCxt),
+ mrColumn(rColumn),
+ miPos(mrColumn.GetCellStore().begin()),
+ mnErrCode(nErrCode),
+ mbCompiled(false)
+ {
+ }
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ FormulaError nCurError = pCell->GetRawError();
+ if (nCurError == FormulaError::NONE)
+ // It's not an error cell. Skip it.
+ return;
+
+ if (mnErrCode != FormulaError::NONE && nCurError != mnErrCode)
+ // Error code is specified, and it doesn't match. Skip it.
+ return;
+
+ sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
+ miPos = aPos.first;
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
+ pCell->GetCode()->SetCodeError(FormulaError::NONE);
+ OUString aFormula = pCell->GetFormula(mrCxt);
+ pCell->Compile(mrCxt, aFormula);
+ ScColumn::JoinNewFormulaCell(aPos, *pCell);
+
+ mbCompiled = true;
+ }
+
+ bool isCompiled() const { return mbCompiled; }
+};
+
+class CalcAfterLoadHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+ bool mbStartListening;
+
+public:
+ CalcAfterLoadHandler( sc::CompileFormulaContext& rCxt, bool bStartListening ) :
+ mrCxt(rCxt), mbStartListening(bStartListening) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->CalcAfterLoad(mrCxt, mbStartListening);
+ }
+};
+
+struct ResetChangedHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->SetChanged(false);
+ }
+};
+
+/**
+ * Ambiguous script type counts as edit cell.
+ */
+class FindEditCellsHandler
+{
+ ScColumn& mrColumn;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ sc::CellStoreType::iterator miCellPos;
+
+public:
+ explicit FindEditCellsHandler(ScColumn& rCol) :
+ mrColumn(rCol),
+ miAttrPos(rCol.GetCellAttrStore().begin()),
+ miCellPos(rCol.GetCellStore().begin()) {}
+
+ bool operator() (size_t, const EditTextObject*)
+ {
+ // This is definitely an edit text cell.
+ return true;
+ }
+
+ bool operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ // With a formula cell, it's considered an edit text cell when either
+ // the result is multi-line or it has more than one script types.
+ SvtScriptType nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow, miCellPos);
+ if (IsAmbiguousScriptNonZero(nScriptType))
+ return true;
+
+ return const_cast<ScFormulaCell*>(p)->IsMultilineResult();
+ }
+
+ /**
+ * Callback for a block of other types.
+ */
+ std::pair<size_t,bool> operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ typedef std::pair<size_t,bool> RetType;
+
+ if (node.type == sc::element_type_empty)
+ // Ignore empty blocks.
+ return RetType(0, false);
+
+ // Check the script type of a non-empty element and see if it has
+ // multiple script types.
+ for (size_t i = 0; i < nDataSize; ++i)
+ {
+ SCROW nRow = node.position + i + nOffset;
+ SvtScriptType nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow, miCellPos);
+ if (IsAmbiguousScriptNonZero(nScriptType))
+ // Return the offset from the first row.
+ return RetType(i+nOffset, true);
+ }
+
+ // No edit text cell found.
+ return RetType(0, false);
+ }
+};
+
+}
+
+void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
+ ScDocument* pUndoDoc )
+{
+ UpdateTransHandler aFunc(*this, rSource, rDest, pUndoDoc);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ UpdateGrowHandler aFunc(*this, rArea, nGrowX, nGrowY);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ if (nTab >= rCxt.mnInsertPos)
+ {
+ nTab += rCxt.mnSheets;
+ pAttrArray->SetTab(nTab);
+ }
+
+ UpdateInsertTabOnlyCells(rCxt);
+}
+
+void ScColumn::UpdateInsertTabOnlyCells( sc::RefUpdateInsertTabContext& rCxt )
+{
+ InsertTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ if (nTab > rCxt.mnDeletePos)
+ {
+ nTab -= rCxt.mnSheets;
+ pAttrArray->SetTab(nTab);
+ }
+
+ DeleteTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateInsertTabAbs(SCTAB nNewPos)
+{
+ InsertAbsTabUpdater aFunc(maCellTextAttrs, nTab, nNewPos);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
+{
+ nTab = nTabNo;
+ pAttrArray->SetTab( nTabNo );
+
+ MoveTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateCompile( bool bForceIfNameInUse )
+{
+ UpdateCompileHandler aFunc(bForceIfNameInUse);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::SetTabNo(SCTAB nNewTab)
+{
+ nTab = nNewTab;
+ pAttrArray->SetTab( nNewTab );
+
+ TabNoSetter aFunc(nTab);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, sc::UpdatedRangeNames& rIndexes) const
+{
+ UsedRangeNameFinder aFunc(rIndexes);
+ sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::SetDirtyVar()
+{
+ SetDirtyVarHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+bool ScColumn::IsFormulaDirty( SCROW nRow ) const
+{
+ if (!GetDoc().ValidRow(nRow))
+ return false;
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ // This is not a formula cell block.
+ return false;
+
+ const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ return p->GetDirty();
+}
+
+void ScColumn::CheckVectorizationState()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ CheckVectorizationHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
+{
+ // is only done documentwide, no FormulaTracking
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ SetDirtyHandler aFunc(GetDoc(), rCxt);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::SetDirtyFromClip( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ // Set all formula cells in the range dirty, and pick up all non-formula
+ // cells for later broadcasting. We don't broadcast here.
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
+ aHdl.fillBroadcastSpans(rBroadcastSpans);
+}
+
+namespace {
+
+class BroadcastBroadcastersHandler
+{
+ ScHint maHint;
+ bool mbBroadcasted;
+
+public:
+ explicit BroadcastBroadcastersHandler( SfxHintId nHint, SCTAB nTab, SCCOL nCol )
+ : maHint(nHint, ScAddress(nCol, 0, nTab))
+ , mbBroadcasted(false)
+ {
+ }
+
+ void operator() ( size_t nRow, SvtBroadcaster* pBroadcaster )
+ {
+ maHint.SetAddressRow(nRow);
+ pBroadcaster->Broadcast(maHint);
+ mbBroadcasted = true;
+ }
+
+ bool wasBroadcasted() { return mbBroadcasted; }
+};
+
+}
+
+bool ScColumn::BroadcastBroadcasters( SCROW nRow1, SCROW nRow2, SfxHintId nHint )
+{
+ BroadcastBroadcastersHandler aBroadcasterHdl(nHint, nTab, nCol);
+ sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aBroadcasterHdl);
+ return aBroadcasterHdl.wasBroadcasted();
+}
+
+void ScColumn::SetDirty( SCROW nRow1, SCROW nRow2, BroadcastMode eMode )
+{
+ // broadcasts everything within the range, with FormulaTracking
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+
+ switch (eMode)
+ {
+ case BROADCAST_NONE:
+ {
+ // Handler only used with formula cells.
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl);
+ }
+ break;
+ case BROADCAST_DATA_POSITIONS:
+ {
+ // Handler used with both, formula and non-formula cells.
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
+ aHdl.broadcast();
+ }
+ break;
+ case BROADCAST_BROADCASTERS:
+ {
+ // Handler only used with formula cells.
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl);
+ // Broadcast all broadcasters in range.
+ if (BroadcastBroadcasters( nRow1, nRow2, SfxHintId::ScDataChanged))
+ {
+ // SetDirtyOnRangeHandler implicitly tracks notified
+ // formulas via ScDocument::Broadcast(), which
+ // BroadcastBroadcastersHandler doesn't, so explicitly
+ // track them here.
+ GetDoc().TrackFormulas();
+ }
+ }
+ break;
+ }
+}
+
+void ScColumn::SetTableOpDirty( const ScRange& rRange )
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+ SetTableOpDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
+ aHdl.broadcast();
+}
+
+void ScColumn::SetDirtyAfterLoad()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ SetDirtyAfterLoadHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+namespace {
+
+class RecalcOnRefMoveCollector
+{
+ std::vector<SCROW> maDirtyRows;
+public:
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ if (pCell->GetDirty() && pCell->GetCode()->IsRecalcModeOnRefMove())
+ maDirtyRows.push_back(nRow);
+ }
+
+ const std::vector<SCROW>& getDirtyRows() const
+ {
+ return maDirtyRows;
+ }
+};
+
+}
+
+void ScColumn::SetDirtyIfPostponed()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ SetDirtyIfPostponedHandler aFunc;
+ ScBulkBroadcast aBulkBroadcast( GetDoc().GetBASM(), SfxHintId::ScDataChanged);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::BroadcastRecalcOnRefMove()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ RecalcOnRefMoveCollector aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+ BroadcastCells(aFunc.getDirtyRows(), SfxHintId::ScDataChanged);
+}
+
+void ScColumn::CalcAll()
+{
+ CalcAllHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::CompileAll( sc::CompileFormulaContext& rCxt )
+{
+ CompileAllHandler aFunc(rCxt);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
+{
+ CompileXMLHandler aFunc(rCxt, rProgress, *this);
+ sc::ProcessFormula(maCells, aFunc);
+ RegroupFormulaCells();
+}
+
+bool ScColumn::CompileErrorCells( sc::CompileFormulaContext& rCxt, FormulaError nErrCode )
+{
+ CompileErrorCellsHandler aHdl(rCxt, *this, nErrCode);
+ sc::ProcessFormula(maCells, aHdl);
+ return aHdl.isCompiled();
+}
+
+void ScColumn::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
+{
+ CalcAfterLoadHandler aFunc(rCxt, bStartListening);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow )
+{
+ ResetChangedHandler aFunc;
+ sc::ProcessFormula(maCells.begin(), maCells, nStartRow, nEndRow, aFunc);
+}
+
+bool ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst)
+{
+ // used in GetOptimalHeight - ambiguous script type counts as edit cell
+
+ FindEditCellsHandler aFunc(*this);
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
+ sc::FindFormulaEditText(maCells, nStartRow, nEndRow, aFunc);
+
+ if (aPos.first == maCells.end())
+ return false;
+
+ rFirst = aPos.first->position + aPos.second;
+ return true;
+}
+
+SCROW ScColumn::SearchStyle(
+ SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp, bool bInSelection,
+ const ScMarkData& rMark) const
+{
+ if (bInSelection)
+ {
+ if (rMark.IsMultiMarked())
+ {
+ ScMarkArray aArray(rMark.GetMarkArray(nCol));
+ return pAttrArray->SearchStyle(nRow, pSearchStyle, bUp, &aArray);
+ }
+ else
+ return -1;
+ }
+ else
+ return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp );
+}
+
+bool ScColumn::SearchStyleRange(
+ SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
+ bool bInSelection, const ScMarkData& rMark) const
+{
+ if (bInSelection)
+ {
+ if (rMark.IsMultiMarked())
+ {
+ ScMarkArray aArray(rMark.GetMarkArray(nCol));
+ return pAttrArray->SearchStyleRange(
+ rRow, rEndRow, pSearchStyle, bUp, &aArray);
+ }
+ else
+ return false;
+ }
+ else
+ return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
new file mode 100644
index 0000000000..0592c97e58
--- /dev/null
+++ b/sc/source/core/data/column2.cxx
@@ -0,0 +1,3829 @@
+/* -*- 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 <column.hxx>
+#include <docsh.hxx>
+#include <scitems.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <cellform.hxx>
+#include <editutil.hxx>
+#include <subtotal.hxx>
+#include <markdata.hxx>
+#include <fillinfo.hxx>
+#include <segmenttree.hxx>
+#include <docparam.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <formulagroup.hxx>
+#include <listenercontext.hxx>
+#include <mtvcellfunc.hxx>
+#include <progress.hxx>
+#include <scmatrix.hxx>
+#include <rowheightcontext.hxx>
+#include <tokenstringcontext.hxx>
+#include <sortparam.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/broadcast.hxx>
+#include <utility>
+#include <vcl/outdev.hxx>
+#include <formula/errorcodes.hxx>
+#include <formula/vectortoken.hxx>
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <numeric>
+
+// factor from font size to optimal cell height (text width)
+#define SC_ROT_BREAK_FACTOR 6
+
+static bool IsAmbiguousScript( SvtScriptType nScript )
+{
+ //TODO: move to a header file
+ return ( nScript != SvtScriptType::LATIN &&
+ nScript != SvtScriptType::ASIAN &&
+ nScript != SvtScriptType::COMPLEX );
+}
+
+// Data operations
+
+tools::Long ScColumn::GetNeededSize(
+ SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bWidth, const ScNeededSizeOptions& rOptions,
+ const ScPatternAttr** ppPatternChange, bool bInPrintTwips ) const
+{
+ // If bInPrintTwips is set, the size calculated should be in print twips,
+ // else it should be in pixels.
+
+ // Switch unit to MapTwip instead ? (temporarily and then revert before exit).
+ if (bInPrintTwips)
+ assert(pDev->GetMapMode().GetMapUnit() == MapUnit::MapTwip);
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end() || it->type == sc::element_type_empty)
+ // Empty cell, or invalid row.
+ return 0;
+
+ tools::Long nValue = 0;
+ ScRefCellValue aCell = GetCellValue(it, aPos.second);
+ double nPPT = bWidth ? nPPTX : nPPTY;
+
+ auto conditionalScaleFunc = [bInPrintTwips](tools::Long nMeasure, double fScale) {
+ return bInPrintTwips ? nMeasure : static_cast<tools::Long>(nMeasure * fScale);
+ };
+
+ const ScPatternAttr* pPattern = rOptions.pPattern;
+ if (!pPattern)
+ pPattern = pAttrArray->GetPattern( nRow );
+
+ // merged?
+ // Do not merge in conditional formatting
+
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+
+ if ( bWidth )
+ {
+ if ( pFlag->IsHorOverlapped() )
+ return 0;
+ if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 )
+ return 0;
+ }
+ else
+ {
+ if ( pFlag->IsVerOverlapped() )
+ return 0;
+ if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 )
+ return 0;
+ }
+
+ // conditional formatting
+ ScDocument& rDocument = GetDoc();
+ const SfxItemSet* pCondSet = rDocument.GetCondResult( nCol, nRow, nTab );
+
+ //The pPattern may change in GetCondResult
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ pPattern = pAttrArray->GetPattern( nRow );
+ if (ppPatternChange)
+ *ppPatternChange = pPattern;
+ }
+ // line break?
+
+ const SvxHorJustifyItem* pCondItem;
+ SvxCellHorJustify eHorJust;
+ if (pCondSet && (pCondItem = pCondSet->GetItemIfSet(ATTR_HOR_JUSTIFY)) )
+ eHorJust = pCondItem->GetValue();
+ else
+ eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+ bool bBreak;
+ const ScLineBreakCell* pLineBreakCell;
+ if ( eHorJust == SvxCellHorJustify::Block )
+ bBreak = true;
+ else if ( pCondSet && (pLineBreakCell = pCondSet->GetItemIfSet(ATTR_LINEBREAK)) )
+ bBreak = pLineBreakCell->GetValue();
+ else
+ bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue();
+
+ SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
+
+ // get "cell is value" flag
+ // Must be synchronized with ScOutputData::LayoutStrings()
+ bool bCellIsValue = (aCell.getType() == CELLTYPE_VALUE);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ bCellIsValue = pFCell->IsRunning();
+ if (!bCellIsValue)
+ {
+ bCellIsValue = pFCell->IsValue();
+ if (bCellIsValue)
+ {
+ // the pattern may change in IsValue()
+ pPattern = pAttrArray->GetPattern( nRow );
+ if (ppPatternChange)
+ *ppPatternChange = pPattern;
+ }
+ }
+ }
+
+ // #i111387#, tdf#121040: disable automatic line breaks for all number formats
+ if (bBreak && bCellIsValue && (pFormatter->GetType(nFormat) == SvNumFormatType::NUMBER))
+ {
+ // If a formula cell needs to be interpreted during aCell.hasNumeric()
+ // to determine the type, the pattern may get invalidated because the
+ // result may set a number format. In which case there's also the
+ // General format not set anymore...
+ bool bMayInvalidatePattern = (aCell.getType() == CELLTYPE_FORMULA);
+ const ScPatternAttr* pOldPattern = pPattern;
+ bool bNumeric = aCell.hasNumeric();
+ if (bMayInvalidatePattern)
+ {
+ pPattern = pAttrArray->GetPattern( nRow );
+ if (ppPatternChange)
+ *ppPatternChange = pPattern; // XXX caller may have to check for change!
+ }
+ if (bNumeric)
+ {
+ if (!bMayInvalidatePattern || SfxPoolItem::areSame(pPattern, pOldPattern))
+ bBreak = false;
+ else
+ {
+ nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
+ if (pFormatter->GetType(nFormat) == SvNumFormatType::NUMBER)
+ bBreak = false;
+ }
+ }
+ }
+
+ // get other attributes from pattern and conditional formatting
+
+ SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
+ bool bAsianVertical = ( eOrient == SvxCellOrientation::Stacked &&
+ pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet ).GetValue() );
+ if ( bAsianVertical )
+ bBreak = false;
+
+ if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset)
+ return 0;
+
+ Degree100 nRotate(0);
+ SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
+ if ( eOrient == SvxCellOrientation::Standard )
+ {
+ const ScRotateValueItem* pRotateValueItem;
+ if (pCondSet &&
+ (pRotateValueItem = pCondSet->GetItemIfSet(ATTR_ROTATE_VALUE)) )
+ nRotate = pRotateValueItem->GetValue();
+ else
+ nRotate = pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue();
+ if ( nRotate )
+ {
+ const SvxRotateModeItem* pRotateModeItem;
+ if (pCondSet &&
+ (pRotateModeItem = pCondSet->GetItemIfSet(ATTR_ROTATE_MODE)) )
+ eRotMode = pRotateModeItem->GetValue();
+ else
+ eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE).GetValue();
+
+ if ( nRotate == 18000_deg100 )
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+ }
+
+ if ( eHorJust == SvxCellHorJustify::Repeat )
+ {
+ // ignore orientation/rotation if "repeat" is active
+ eOrient = SvxCellOrientation::Standard;
+ nRotate = 0_deg100;
+ bAsianVertical = false;
+ }
+
+ const SvxMarginItem* pMargin;
+ if (pCondSet &&
+ (pMargin = pCondSet->GetItemIfSet(ATTR_MARGIN)) )
+ ;
+ else
+ pMargin = &pPattern->GetItem(ATTR_MARGIN);
+ sal_uInt16 nIndent = 0;
+ if ( eHorJust == SvxCellHorJustify::Left )
+ {
+ const ScIndentItem* pIndentItem;
+ if (pCondSet &&
+ (pIndentItem = pCondSet->GetItemIfSet(ATTR_INDENT)) )
+ nIndent = pIndentItem->GetValue();
+ else
+ nIndent = pPattern->GetItem(ATTR_INDENT).GetValue();
+ }
+
+ SvtScriptType nScript = rDocument.GetScriptType(nCol, nRow, nTab);
+ if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
+
+ // also call SetFont for edit cells, because bGetFont may be set only once
+ // bGetFont is set also if script type changes
+ if (rOptions.bGetFont)
+ {
+ Fraction aFontZoom = ( eOrient == SvxCellOrientation::Standard ) ? rZoomX : rZoomY;
+ vcl::Font aFont;
+ aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern
+ // font color doesn't matter here
+ pPattern->fillFontOnly(aFont, pDev, &aFontZoom, pCondSet, nScript);
+ pDev->SetFont(aFont);
+ }
+
+ bool bAddMargin = true;
+ CellType eCellType = aCell.getType();
+
+ bool bEditEngine = (eCellType == CELLTYPE_EDIT ||
+ eOrient == SvxCellOrientation::Stacked ||
+ IsAmbiguousScript(nScript) ||
+ ((eCellType == CELLTYPE_FORMULA) && aCell.getFormula()->IsMultilineResult()));
+
+ if (!bEditEngine) // direct output
+ {
+ const Color* pColor;
+ OUString aValStr = ScCellFormat::GetString(
+ aCell, nFormat, &pColor, *pFormatter, rDocument, true, rOptions.bFormula);
+
+ if (!aValStr.isEmpty())
+ {
+ // SetFont is moved up
+
+ Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() );
+ if ( eOrient != SvxCellOrientation::Standard )
+ {
+ tools::Long nTemp = aSize.Width();
+ aSize.setWidth( aSize.Height() );
+ aSize.setHeight( nTemp );
+ }
+ else if ( nRotate )
+ {
+ //TODO: take different X/Y scaling into consideration
+
+ double nRealOrient = toRadians(nRotate);
+ double nCosAbs = fabs( cos( nRealOrient ) );
+ double nSinAbs = fabs( sin( nRealOrient ) );
+ tools::Long nHeight = static_cast<tools::Long>( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
+ tools::Long nWidth;
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nWidth = static_cast<tools::Long>( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
+ else if ( rOptions.bTotalSize )
+ {
+ nWidth = conditionalScaleFunc(rDocument.GetColWidth( nCol,nTab ), nPPT);
+ bAddMargin = false;
+ // only to the right:
+ //TODO: differ on direction up/down (only Text/whole height)
+ if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
+ nWidth += static_cast<tools::Long>( rDocument.GetRowHeight( nRow,nTab ) *
+ (bInPrintTwips ? 1.0 : nPPT) * nCosAbs / nSinAbs );
+ }
+ else
+ nWidth = static_cast<tools::Long>( aSize.Height() / nSinAbs ); //TODO: limit?
+
+ if ( bBreak && !rOptions.bTotalSize )
+ {
+ // limit size for line break
+ tools::Long nCmp = pDev->GetFont().GetFontSize().Height() * SC_ROT_BREAK_FACTOR;
+ if ( nHeight > nCmp )
+ nHeight = nCmp;
+ }
+
+ aSize = Size( nWidth, nHeight );
+ }
+ nValue = bWidth ? aSize.Width() : aSize.Height();
+
+ if ( bAddMargin )
+ {
+ if (bWidth)
+ {
+ nValue += conditionalScaleFunc(pMargin->GetLeftMargin(), nPPT) +
+ conditionalScaleFunc(pMargin->GetRightMargin(), nPPT);
+ if ( nIndent )
+ nValue += conditionalScaleFunc(nIndent, nPPT);
+ }
+ else
+ nValue += conditionalScaleFunc(pMargin->GetTopMargin(), nPPT) +
+ conditionalScaleFunc(pMargin->GetBottomMargin(), nPPT);
+ }
+
+ // linebreak done ?
+
+ if ( bBreak && !bWidth )
+ {
+ // test with EditEngine the safety at 90%
+ // (due to rounding errors and because EditEngine formats partially differently)
+
+ tools::Long nDocSize = conditionalScaleFunc((rDocument.GetColWidth( nCol,nTab ) -
+ pMargin->GetLeftMargin() - pMargin->GetRightMargin() -
+ nIndent), nPPTX);
+ nDocSize = (nDocSize * 9) / 10; // for safety
+ if ( aSize.Width() > nDocSize )
+ bEditEngine = true;
+ }
+ }
+ }
+
+ if (bEditEngine)
+ {
+ // the font is not reset each time with !bEditEngine
+ vcl::Font aOldFont = pDev->GetFont();
+
+ MapMode aHMMMode( MapUnit::Map100thMM, Point(), rZoomX, rZoomY );
+
+ // save in document ?
+ std::unique_ptr<ScFieldEditEngine> pEngine = rDocument.CreateFieldEditEngine();
+
+ const bool bPrevUpdateLayout = pEngine->SetUpdateLayout( false );
+ bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER );
+ EEControlBits nCtrl = pEngine->GetControlWord();
+ if ( bTextWysiwyg )
+ nCtrl |= EEControlBits::FORMAT100;
+ else
+ nCtrl &= ~EEControlBits::FORMAT100;
+ pEngine->SetControlWord( nCtrl );
+ MapMode aOld = pDev->GetMapMode();
+ pDev->SetMapMode( aHMMMode );
+ pEngine->SetRefDevice( pDev );
+ rDocument.ApplyAsianEditSettings( *pEngine );
+ SfxItemSet aSet( pEngine->GetEmptyItemSet() );
+ if ( ScStyleSheet* pPreviewStyle = rDocument.GetPreviewCellStyle( nCol, nRow, nTab ) )
+ {
+ ScPatternAttr aPreviewPattern( *pPattern );
+ aPreviewPattern.SetStyleSheet(pPreviewStyle);
+ aPreviewPattern.FillEditItemSet( &aSet, pCondSet );
+ }
+ else
+ {
+ SfxItemSet* pFontSet = rDocument.GetPreviewFont( nCol, nRow, nTab );
+ pPattern->FillEditItemSet( &aSet, pFontSet ? pFontSet : pCondSet );
+ }
+// no longer needed, are set with the text (is faster)
+// pEngine->SetDefaults( pSet );
+
+ if ( aSet.Get(EE_PARA_HYPHENATE).GetValue() ) {
+
+ css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
+ pEngine->SetHyphenator( xXHyphenator );
+ }
+
+ Size aPaper( 1000000, 1000000 );
+ if ( eOrient==SvxCellOrientation::Stacked && !bAsianVertical )
+ aPaper.setWidth( 1 );
+ else if (bBreak)
+ {
+ double fWidthFactor = bInPrintTwips ? 1.0 : nPPTX;
+ if ( bTextWysiwyg )
+ {
+ // if text is formatted for printer, don't use PixelToLogic,
+ // to ensure the exact same paper width (and same line breaks) as in
+ // ScEditUtil::GetEditArea, used for output.
+
+ fWidthFactor = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+
+ // use original width for hidden columns:
+ tools::Long nDocWidth = static_cast<tools::Long>( rDocument.GetOriginalWidth(nCol,nTab) * fWidthFactor );
+ SCCOL nColMerge = pMerge->GetColMerge();
+ if (nColMerge > 1)
+ for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++)
+ nDocWidth += static_cast<tools::Long>( rDocument.GetColWidth(nCol+nColAdd,nTab) * fWidthFactor );
+ nDocWidth -= static_cast<tools::Long>( pMargin->GetLeftMargin() * fWidthFactor )
+ + static_cast<tools::Long>( pMargin->GetRightMargin() * fWidthFactor )
+ + 1; // output size is width-1 pixel (due to gridline)
+ if ( nIndent )
+ nDocWidth -= static_cast<tools::Long>( nIndent * fWidthFactor );
+
+ // space for AutoFilter button: 20 * nZoom/100
+ constexpr tools::Long nFilterButtonWidthPix = 20; // Autofilter pixel width at 100% zoom.
+ if ( pFlag->HasAutoFilter() && !bTextWysiwyg )
+ nDocWidth -= bInPrintTwips ? o3tl::convert(nFilterButtonWidthPix, o3tl::Length::px,
+ o3tl::Length::twip)
+ : tools::Long(rZoomX * nFilterButtonWidthPix);
+
+ aPaper.setWidth( nDocWidth );
+
+ if ( !bTextWysiwyg )
+ {
+ aPaper = bInPrintTwips ?
+ o3tl::convert(aPaper, o3tl::Length::twip, o3tl::Length::mm100) :
+ pDev->PixelToLogic(aPaper, aHMMMode);
+ }
+ }
+ pEngine->SetPaperSize(aPaper);
+
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ pEngine->SetTextNewDefaults(*aCell.getEditText(), std::move(aSet));
+ }
+ else
+ {
+ const Color* pColor;
+ OUString aString = ScCellFormat::GetString(
+ aCell, nFormat, &pColor, *pFormatter, rDocument, true,
+ rOptions.bFormula);
+
+ if (!aString.isEmpty())
+ pEngine->SetTextNewDefaults(aString, std::move(aSet));
+ else
+ pEngine->SetDefaults(std::move(aSet));
+ }
+
+ bool bEngineVertical = pEngine->IsEffectivelyVertical();
+ pEngine->SetVertical( bAsianVertical );
+ pEngine->SetUpdateLayout( bPrevUpdateLayout );
+
+ bool bEdWidth = bWidth;
+ if ( eOrient != SvxCellOrientation::Standard && eOrient != SvxCellOrientation::Stacked )
+ bEdWidth = !bEdWidth;
+ if ( nRotate )
+ {
+ //TODO: take different X/Y scaling into consideration
+
+ Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() );
+ double nRealOrient = toRadians(nRotate);
+ double nCosAbs = fabs( cos( nRealOrient ) );
+ double nSinAbs = fabs( sin( nRealOrient ) );
+ tools::Long nHeight = static_cast<tools::Long>( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
+ tools::Long nWidth;
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nWidth = static_cast<tools::Long>( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
+ else if ( rOptions.bTotalSize )
+ {
+ nWidth = conditionalScaleFunc(rDocument.GetColWidth( nCol,nTab ), nPPT);
+ bAddMargin = false;
+ if ( pPattern->GetRotateDir( pCondSet ) == ScRotateDir::Right )
+ nWidth += static_cast<tools::Long>( rDocument.GetRowHeight( nRow,nTab ) *
+ (bInPrintTwips ? 1.0 : nPPT) * nCosAbs / nSinAbs );
+ }
+ else
+ nWidth = static_cast<tools::Long>( aSize.Height() / nSinAbs ); //TODO: limit?
+ aSize = Size( nWidth, nHeight );
+
+ Size aTextSize = bInPrintTwips ?
+ o3tl::toTwips(aSize, o3tl::Length::mm100) :
+ pDev->LogicToPixel(aSize, aHMMMode);
+
+ if ( bEdWidth )
+ nValue = aTextSize.Width();
+ else
+ {
+ nValue = aTextSize.Height();
+
+ if ( bBreak && !rOptions.bTotalSize )
+ {
+ // limit size for line break
+ tools::Long nCmp = aOldFont.GetFontSize().Height() * SC_ROT_BREAK_FACTOR;
+ if ( nValue > nCmp )
+ nValue = nCmp;
+ }
+ }
+ }
+ else if ( bEdWidth )
+ {
+ if (bBreak)
+ nValue = 0;
+ else
+ {
+ sal_uInt32 aTextSize(pEngine->CalcTextWidth());
+ nValue = bInPrintTwips ?
+ o3tl::toTwips(aTextSize, o3tl::Length::mm100) :
+ pDev->LogicToPixel(Size(aTextSize, 0), aHMMMode).Width();
+ }
+ }
+ else // height
+ {
+ sal_uInt32 aTextSize(pEngine->GetTextHeight());
+ nValue = bInPrintTwips ?
+ o3tl::toTwips(aTextSize, o3tl::Length::mm100) :
+ pDev->LogicToPixel(Size(0, aTextSize), aHMMMode).Height();
+
+ // With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set
+ if ( !bTextWysiwyg && ( rZoomY.GetNumerator() != 1 || rZoomY.GetDenominator() != 1 ) &&
+ ( pEngine->GetParagraphCount() > 1 || ( bBreak && pEngine->GetLineCount(0) > 1 ) ) )
+ {
+ pEngine->SetControlWord( nCtrl | EEControlBits::FORMAT100 );
+ pEngine->QuickFormatDoc( true );
+ aTextSize = pEngine->GetTextHeight();
+ tools::Long nSecondValue = bInPrintTwips ?
+ o3tl::toTwips(aTextSize, o3tl::Length::mm100) :
+ pDev->LogicToPixel(Size(0, aTextSize), aHMMMode).Height();
+ if ( nSecondValue > nValue )
+ nValue = nSecondValue;
+ }
+ }
+
+ if ( nValue && bAddMargin )
+ {
+ if (bWidth)
+ {
+ nValue += conditionalScaleFunc(pMargin->GetLeftMargin(), nPPT) +
+ conditionalScaleFunc(pMargin->GetRightMargin(), nPPT);
+ if (nIndent)
+ nValue += conditionalScaleFunc(nIndent, nPPT);
+ }
+ else
+ {
+ nValue += conditionalScaleFunc(pMargin->GetTopMargin(), nPPT) +
+ conditionalScaleFunc(pMargin->GetBottomMargin(), nPPT);
+
+ if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ // add 1pt extra (default margin value) for line breaks with SetVertical
+ constexpr tools::Long nDefaultMarginInPoints = 1;
+ nValue += conditionalScaleFunc(
+ o3tl::convert(nDefaultMarginInPoints, o3tl::Length::pt, o3tl::Length::twip),
+ nPPT);
+ }
+ }
+ }
+
+ // EditEngine is cached and re-used, so the old vertical flag must be restored
+ pEngine->SetVertical( bEngineVertical );
+
+ rDocument.DisposeFieldEditEngine(pEngine);
+
+ pDev->SetMapMode( aOld );
+ pDev->SetFont( aOldFont );
+ }
+
+ if (bWidth)
+ {
+ // place for Autofilter Button
+ // 20 * nZoom/100
+ // Conditional formatting is not interesting here
+ constexpr tools::Long nFilterButtonWidthPix = 20; // Autofilter pixel width at 100% zoom.
+ ScMF nFlags = pPattern->GetItem(ATTR_MERGE_FLAG).GetValue();
+ if (nFlags & ScMF::Auto)
+ nValue += bInPrintTwips ? o3tl::convert(nFilterButtonWidthPix, o3tl::Length::px,
+ o3tl::Length::twip)
+ : tools::Long(rZoomX * nFilterButtonWidthPix);
+ }
+
+ return nValue;
+}
+
+namespace {
+
+class MaxStrLenFinder
+{
+ ScDocument& mrDoc;
+ sal_uInt32 mnFormat;
+ OUString maMaxLenStr;
+ sal_Int32 mnMaxLen;
+
+ // tdf#59820 - search for the longest substring in a multiline string
+ void checkLineBreak(const OUString& aStrVal)
+ {
+ sal_Int32 nFromIndex = 0;
+ sal_Int32 nToIndex = aStrVal.indexOf('\n', nFromIndex);
+ // if there is no line break, just take the length of the entire string
+ if (nToIndex == -1)
+ {
+ mnMaxLen = aStrVal.getLength();
+ maMaxLenStr = aStrVal;
+ }
+ else
+ {
+ sal_Int32 nMaxLen = 0;
+ // search for the longest substring in the multiline string
+ while (nToIndex != -1)
+ {
+ if (nMaxLen < nToIndex - nFromIndex)
+ {
+ nMaxLen = nToIndex - nFromIndex;
+ }
+ nFromIndex = nToIndex + 1;
+ nToIndex = aStrVal.indexOf('\n', nFromIndex);
+ }
+ // take into consideration the last part of multiline string
+ nToIndex = aStrVal.getLength() - nFromIndex;
+ if (nMaxLen < nToIndex)
+ {
+ nMaxLen = nToIndex;
+ }
+ // assign new maximum including its substring
+ if (mnMaxLen < nMaxLen)
+ {
+ mnMaxLen = nMaxLen;
+ maMaxLenStr = aStrVal.subView(nFromIndex);
+ }
+ }
+ }
+
+ void checkLength(const ScRefCellValue& rCell)
+ {
+ const Color* pColor;
+ OUString aValStr = ScCellFormat::GetString(
+ rCell, mnFormat, &pColor, *mrDoc.GetFormatTable(), mrDoc);
+
+ if (aValStr.getLength() <= mnMaxLen)
+ return;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_NONE:
+ case CELLTYPE_VALUE:
+ mnMaxLen = aValStr.getLength();
+ maMaxLenStr = aValStr;
+ break;
+ case CELLTYPE_EDIT:
+ case CELLTYPE_STRING:
+ case CELLTYPE_FORMULA:
+ default:
+ checkLineBreak(aValStr);
+ }
+ }
+
+public:
+ MaxStrLenFinder(ScDocument& rDoc, sal_uInt32 nFormat) :
+ mrDoc(rDoc), mnFormat(nFormat), mnMaxLen(0) {}
+
+ void operator() (size_t /*nRow*/, double f)
+ {
+ ScRefCellValue aCell(f);
+ checkLength(aCell);
+ }
+
+ void operator() (size_t /*nRow*/, const svl::SharedString& rSS)
+ {
+ if (rSS.getLength() > mnMaxLen)
+ {
+ checkLineBreak(rSS.getString());
+ }
+ }
+
+ void operator() (size_t /*nRow*/, const EditTextObject* p)
+ {
+ ScRefCellValue aCell(p);
+ checkLength(aCell);
+ }
+
+ void operator() (size_t /*nRow*/, const ScFormulaCell* p)
+ {
+ ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
+ checkLength(aCell);
+ }
+
+ const OUString& getMaxLenStr() const { return maMaxLenStr; }
+};
+
+}
+
+sal_uInt16 ScColumn::GetOptimalColWidth(
+ OutputDevice* pDev, double nPPTX, double nPPTY, const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bFormula, sal_uInt16 nOldWidth, const ScMarkData* pMarkData, const ScColWidthParam* pParam) const
+{
+ if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty)
+ // All cells are empty.
+ return nOldWidth;
+
+ sc::SingleColumnSpanSet::SpansType aMarkedSpans;
+ if (pMarkData && (pMarkData->IsMarked() || pMarkData->IsMultiMarked()))
+ {
+ sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
+ aSpanSet.scan(*pMarkData, nTab, nCol);
+ aSpanSet.getSpans(aMarkedSpans);
+ }
+ else
+ // "Select" the entire column if no selection exists.
+ aMarkedSpans.emplace_back(0, GetDoc().MaxRow());
+
+ sal_uInt16 nWidth = static_cast<sal_uInt16>(nOldWidth*nPPTX);
+ bool bFound = false;
+ ScDocument& rDocument = GetDoc();
+
+ if ( pParam && pParam->mbSimpleText )
+ { // all the same except for number format
+ SCROW nRow = 0;
+ const ScPatternAttr* pPattern = GetPattern( nRow );
+ vcl::Font aFont;
+ aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern
+ // font color doesn't matter here
+ pPattern->fillFontOnly(aFont, pDev, &rZoomX);
+ pDev->SetFont(aFont);
+ const SvxMarginItem* pMargin = &pPattern->GetItem(ATTR_MARGIN);
+ tools::Long nMargin = static_cast<tools::Long>( pMargin->GetLeftMargin() * nPPTX ) +
+ static_cast<tools::Long>( pMargin->GetRightMargin() * nPPTX );
+
+ // Try to find the row that has the longest string, and measure the width of that string.
+ SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
+ while ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && nRow <= 2)
+ {
+ // This is often used with CSV import or other data having a header
+ // row; if there is no specific format set try next row for actual
+ // data format.
+ // Or again in case there was a leading sep=";" row or two header
+ // rows..
+ const ScPatternAttr* pNextPattern = GetPattern( ++nRow );
+ if (!SfxPoolItem::areSame(pNextPattern, pPattern))
+ nFormat = pNextPattern->GetNumberFormat( pFormatter );
+ }
+ OUString aLongStr;
+ const Color* pColor;
+ if (pParam->mnMaxTextRow >= 0)
+ {
+ ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
+ aLongStr = ScCellFormat::GetString(
+ aCell, nFormat, &pColor, *pFormatter, rDocument);
+ }
+ else
+ {
+ // Go though all non-empty cells within selection.
+ MaxStrLenFinder aFunc(rDocument, nFormat);
+ sc::CellStoreType::const_iterator itPos = maCells.begin();
+ for (const auto& rMarkedSpan : aMarkedSpans)
+ itPos = sc::ParseAllNonEmpty(itPos, maCells, rMarkedSpan.mnRow1, rMarkedSpan.mnRow2, aFunc);
+
+ aLongStr = aFunc.getMaxLenStr();
+ }
+
+ if (!aLongStr.isEmpty())
+ {
+ nWidth = pDev->GetTextWidth(aLongStr) + static_cast<sal_uInt16>(nMargin);
+ bFound = true;
+ }
+ }
+ else
+ {
+ ScNeededSizeOptions aOptions;
+ aOptions.bFormula = bFormula;
+ const ScPatternAttr* pOldPattern = nullptr;
+
+ // Go though all non-empty cells within selection.
+ sc::CellStoreType::const_iterator itPos = maCells.begin();
+ // coverity[auto_causes_copy] This trivial copy is intentional
+ for (auto [ nRow, nRow2 ] : aMarkedSpans)
+ {
+ while (nRow <= nRow2)
+ {
+ size_t nOffset;
+ std::tie(itPos, nOffset) = maCells.position(itPos, nRow);
+ if (itPos->type == sc::element_type_empty)
+ {
+ // Skip empty cells.
+ nRow += itPos->size - nOffset;
+ continue;
+ }
+
+ for (; nOffset < itPos->size && nRow <= nRow2; ++nOffset, ++nRow)
+ {
+ SvtScriptType nScript = rDocument.GetScriptType(nCol, nRow, nTab);
+ if (nScript == SvtScriptType::NONE)
+ nScript = ScGlobal::GetDefaultScriptType();
+
+ const ScPatternAttr* pPattern = GetPattern(nRow);
+ aOptions.pPattern = pPattern;
+ aOptions.bGetFont = (!SfxPoolItem::areSame(pPattern, pOldPattern) || nScript != SvtScriptType::NONE);
+ pOldPattern = pPattern;
+ sal_uInt16 nThis = static_cast<sal_uInt16>(GetNeededSize(
+ nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, true, aOptions, &pOldPattern));
+ if (nThis && (nThis > nWidth || !bFound))
+ {
+ nWidth = nThis;
+ bFound = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (bFound)
+ {
+ nWidth += 2;
+ sal_uInt16 nTwips = static_cast<sal_uInt16>(
+ std::min(nWidth / nPPTX, std::numeric_limits<sal_uInt16>::max() / 2.0));
+ return nTwips;
+ }
+ else
+ return nOldWidth;
+}
+
+static sal_uInt16 lcl_GetAttribHeight(const ScPatternAttr& rPattern, sal_uInt16 nFontHeightId,
+ sal_uInt16 nMinHeight)
+{
+ const SvxFontHeightItem& rFontHeight =
+ static_cast<const SvxFontHeightItem&>(rPattern.GetItem(nFontHeightId));
+
+ sal_uInt16 nHeight = rFontHeight.GetHeight();
+ nHeight *= 1.18;
+
+ if ( rPattern.GetItem(ATTR_FONT_EMPHASISMARK).GetEmphasisMark() != FontEmphasisMark::NONE )
+ {
+ // add height for emphasis marks
+ //TODO: font metrics should be used instead
+ nHeight += nHeight / 4;
+ }
+
+ const SvxMarginItem& rMargin = rPattern.GetItem(ATTR_MARGIN);
+
+ nHeight += rMargin.GetTopMargin() + rMargin.GetBottomMargin();
+
+ if (nHeight > STD_ROWHEIGHT_DIFF)
+ nHeight -= STD_ROWHEIGHT_DIFF;
+
+ if (nHeight < nMinHeight)
+ nHeight = nMinHeight;
+
+ return nHeight;
+}
+
+// pHeight in Twips
+// optimize nMinHeight, nMinStart : with nRow >= nMinStart is at least nMinHeight
+// (is only evaluated with bStdAllowed)
+
+void ScColumn::GetOptimalHeight(
+ sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, sal_uInt16 nMinHeight, SCROW nMinStart )
+{
+ ScDocument& rDocument = GetDoc();
+ RowHeightsArray& rHeights = rCxt.getHeightArray();
+ ScAttrIterator aIter( pAttrArray.get(), nStartRow, nEndRow, rDocument.GetDefPattern() );
+
+ SCROW nStart = -1;
+ SCROW nEnd = -1;
+ SCROW nEditPos = 0;
+ SCROW nNextEnd = 0;
+
+ // with conditional formatting, always consider the individual cells
+
+ const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd);
+ const sal_uInt16 nOptimalMinRowHeight = GetDoc().GetSheetOptimalMinRowHeight(nTab);
+ while ( pPattern )
+ {
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() )
+ {
+ // do nothing - vertically with merged and overlapping,
+ // horizontally only with overlapped (invisible) -
+ // only one horizontal merged is always considered
+ }
+ else
+ {
+ bool bStdAllowed = (pPattern->GetCellOrientation() == SvxCellOrientation::Standard);
+ bool bStdOnly = false;
+ if (bStdAllowed)
+ {
+ bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
+ (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() ==
+ SvxCellHorJustify::Block);
+ bStdOnly = !bBreak;
+
+ // conditional formatting: loop all cells
+ if (bStdOnly &&
+ !pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
+ {
+ bStdOnly = false;
+ }
+
+ // rotated text: loop all cells
+ if ( bStdOnly && pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue() )
+ bStdOnly = false;
+ }
+
+ if (bStdOnly)
+ {
+ bool bHasEditCells = HasEditCells(nStart,nEnd,nEditPos);
+ // Call to HasEditCells() may change pattern due to
+ // calculation, => sync always.
+ // We don't know which row changed first, but as pPattern
+ // covered nStart to nEnd we can pick nStart. Worst case we
+ // have to repeat that for every row in range if every row
+ // changed.
+ pPattern = aIter.Resync( nStart, nStart, nEnd);
+ if (bHasEditCells && nEnd < nEditPos)
+ bHasEditCells = false; // run into that again
+ if (bHasEditCells) // includes mixed script types
+ {
+ if (nEditPos == nStart)
+ {
+ bStdOnly = false;
+ if (nEnd > nEditPos)
+ nNextEnd = nEnd;
+ nEnd = nEditPos; // calculate single
+ bStdAllowed = false; // will be computed in any case per cell
+ }
+ else
+ {
+ nNextEnd = nEnd;
+ nEnd = nEditPos - 1; // standard - part
+ }
+ }
+ }
+
+ sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
+ aSpanSet.scan(*this, nStart, nEnd);
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ aSpanSet.getSpans(aSpans);
+
+ if (bStdAllowed)
+ {
+ sal_uInt16 nLatHeight = 0;
+ sal_uInt16 nCjkHeight = 0;
+ sal_uInt16 nCtlHeight = 0;
+ sal_uInt16 nDefHeight;
+ SvtScriptType nDefScript = ScGlobal::GetDefaultScriptType();
+ if ( nDefScript == SvtScriptType::ASIAN )
+ nDefHeight = nCjkHeight = lcl_GetAttribHeight(*pPattern, ATTR_CJK_FONT_HEIGHT,
+ nOptimalMinRowHeight);
+ else if ( nDefScript == SvtScriptType::COMPLEX )
+ nDefHeight = nCtlHeight = lcl_GetAttribHeight(*pPattern, ATTR_CTL_FONT_HEIGHT,
+ nOptimalMinRowHeight);
+ else
+ nDefHeight = nLatHeight = lcl_GetAttribHeight(*pPattern, ATTR_FONT_HEIGHT,
+ nOptimalMinRowHeight);
+
+ // if everything below is already larger, the loop doesn't have to
+ // be run again
+ SCROW nStdEnd = nEnd;
+ if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
+ nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
+
+ if (nStart <= nStdEnd)
+ {
+ SCROW nRow = nStart;
+ for (;;)
+ {
+ size_t nIndex;
+ SCROW nRangeEnd;
+ sal_uInt16 nRangeHeight = rHeights.GetValue(nRow, nIndex, nRangeEnd);
+ if (nRangeHeight < nDefHeight)
+ rHeights.SetValue(nRow, std::min(nRangeEnd, nStdEnd), nDefHeight);
+ nRow = nRangeEnd + 1;
+ if (nRow > nStdEnd)
+ break;
+ }
+ }
+
+ if ( bStdOnly )
+ {
+ // if cells are not handled individually below,
+ // check for cells with different script type
+ sc::CellTextAttrStoreType::iterator itAttr = maCellTextAttrs.begin();
+ sc::CellStoreType::iterator itCells = maCells.begin();
+ for (const auto& rSpan : aSpans)
+ {
+ for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
+ {
+ SvtScriptType nScript = GetRangeScriptType(itAttr, nRow, nRow, itCells);
+ if (nScript == nDefScript)
+ continue;
+
+ if ( nScript == SvtScriptType::ASIAN )
+ {
+ if ( nCjkHeight == 0 )
+ nCjkHeight = lcl_GetAttribHeight(*pPattern,
+ ATTR_CJK_FONT_HEIGHT,
+ nOptimalMinRowHeight);
+ if (nCjkHeight > rHeights.GetValue(nRow))
+ rHeights.SetValue(nRow, nRow, nCjkHeight);
+ }
+ else if ( nScript == SvtScriptType::COMPLEX )
+ {
+ if ( nCtlHeight == 0 )
+ nCtlHeight = lcl_GetAttribHeight(*pPattern,
+ ATTR_CTL_FONT_HEIGHT,
+ nOptimalMinRowHeight);
+ if (nCtlHeight > rHeights.GetValue(nRow))
+ rHeights.SetValue(nRow, nRow, nCtlHeight);
+ }
+ else
+ {
+ if ( nLatHeight == 0 )
+ nLatHeight = lcl_GetAttribHeight(*pPattern, ATTR_FONT_HEIGHT,
+ nOptimalMinRowHeight);
+ if (nLatHeight > rHeights.GetValue(nRow))
+ rHeights.SetValue(nRow, nRow, nLatHeight);
+ }
+ }
+ }
+ }
+ }
+
+ if (!bStdOnly) // search covered cells
+ {
+ ScNeededSizeOptions aOptions;
+
+ for (const auto& rSpan : aSpans)
+ {
+ for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
+ {
+ // only calculate the cell height when it's used later (#37928#)
+
+ if (rCxt.isForceAutoSize() || !(rDocument.GetRowFlags(nRow, nTab) & CRFlags::ManualSize) )
+ {
+ aOptions.pPattern = pPattern;
+ const ScPatternAttr* pOldPattern = pPattern;
+ sal_uInt16 nHeight = static_cast<sal_uInt16>(
+ std::min(
+ GetNeededSize( nRow, rCxt.getOutputDevice(), rCxt.getPPTX(), rCxt.getPPTY(),
+ rCxt.getZoomX(), rCxt.getZoomY(), false, aOptions,
+ &pPattern) / rCxt.getPPTY(),
+ double(std::numeric_limits<sal_uInt16>::max())));
+ if (nHeight > rHeights.GetValue(nRow))
+ rHeights.SetValue(nRow, nRow, nHeight);
+ // Pattern changed due to calculation? => sync.
+ if (!SfxPoolItem::areSame(pPattern, pOldPattern))
+ {
+ pPattern = aIter.Resync( nRow, nStart, nEnd);
+ nNextEnd = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (nNextEnd > 0)
+ {
+ nStart = nEnd + 1;
+ nEnd = nNextEnd;
+ nNextEnd = 0;
+ }
+ else
+ pPattern = aIter.Next(nStart,nEnd);
+ }
+}
+
+bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const
+{
+ ScDocument& rDocument = GetDoc();
+ sc::CellStoreType::const_iterator it = maCells.position(nRow).first;
+ mdds::mtv::element_t eType = it->type;
+ if (!bInSel && it != maCells.end() && eType != sc::element_type_empty)
+ {
+ if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
+ !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
+ rDocument.IsTabProtected(nTab)) )
+ return true;
+ }
+ if (bInSel)
+ {
+ SCROW lastDataPos = GetLastDataPos();
+ for (;;)
+ {
+ nRow = rData.GetNextMarked(nCol, nRow, false);
+ if (!rDocument.ValidRow(nRow) || nRow > lastDataPos )
+ {
+ nRow = GetDoc().MaxRow()+1;
+ return false;
+ }
+ else
+ {
+ it = maCells.position(it, nRow).first;
+ eType = it->type;
+ if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
+ !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
+ rDocument.IsTabProtected(nTab)) )
+ return true;
+ else
+ nRow++;
+ }
+ }
+ }
+ else
+ {
+ while (GetNextDataPos(nRow))
+ {
+ it = maCells.position(it, nRow).first;
+ eType = it->type;
+ if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
+ !(HasAttrib( nRow, nRow, HasAttrFlags::Protected) &&
+ rDocument.IsTabProtected(nTab)) )
+ return true;
+ else
+ nRow++;
+ }
+ nRow = GetDoc().MaxRow()+1;
+ return false;
+ }
+}
+
+namespace {
+
+class StrEntries
+{
+ sc::CellStoreType& mrCells;
+
+protected:
+ struct StrEntry
+ {
+ SCROW mnRow;
+ OUString maStr;
+
+ StrEntry(SCROW nRow, OUString aStr) : mnRow(nRow), maStr(std::move(aStr)) {}
+ };
+
+ std::vector<StrEntry> maStrEntries;
+ ScDocument* mpDoc;
+
+ StrEntries(sc::CellStoreType& rCells, ScDocument* pDoc) : mrCells(rCells), mpDoc(pDoc) {}
+
+public:
+ void commitStrings()
+ {
+ svl::SharedStringPool& rPool = mpDoc->GetSharedStringPool();
+ sc::CellStoreType::iterator it = mrCells.begin();
+ for (const auto& rStrEntry : maStrEntries)
+ it = mrCells.set(it, rStrEntry.mnRow, rPool.intern(rStrEntry.maStr));
+ }
+};
+
+class RemoveEditAttribsHandler : public StrEntries
+{
+ std::unique_ptr<ScFieldEditEngine> mpEngine;
+
+public:
+ RemoveEditAttribsHandler(sc::CellStoreType& rCells, ScDocument* pDoc) : StrEntries(rCells, pDoc) {}
+
+ void operator() (size_t nRow, EditTextObject*& pObj)
+ {
+ // For the test on hard formatting (ScEditAttrTester), are the defaults in the
+ // EditEngine of no importance. When the tester would later recognise the same
+ // attributes in default and hard formatting and has to remove them, the correct
+ // defaults must be set in the EditEngine for each cell.
+
+ // test for attributes
+ if (!mpEngine)
+ {
+ mpEngine.reset(new ScFieldEditEngine(mpDoc, mpDoc->GetEditPool()));
+ // EEControlBits::ONLINESPELLING if there are errors already
+ mpEngine->SetControlWord(mpEngine->GetControlWord() | EEControlBits::ONLINESPELLING);
+ mpDoc->ApplyAsianEditSettings(*mpEngine);
+ }
+ mpEngine->SetTextCurrentDefaults(*pObj);
+ sal_Int32 nParCount = mpEngine->GetParagraphCount();
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ {
+ mpEngine->RemoveCharAttribs(nPar);
+ const SfxItemSet& rOld = mpEngine->GetParaAttribs(nPar);
+ if ( rOld.Count() )
+ {
+ SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty
+ mpEngine->SetParaAttribs( nPar, aNew );
+ }
+ }
+ // change URL field to text (not possible otherwise, thus pType=0)
+ mpEngine->RemoveFields();
+
+ bool bSpellErrors = mpEngine->HasOnlineSpellErrors();
+ bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs
+ // ScEditAttrTester is not needed anymore, arrays are gone
+
+ if (bNeedObject) // remains edit cell
+ {
+ EEControlBits nCtrl = mpEngine->GetControlWord();
+ EEControlBits nWantBig = bSpellErrors ? EEControlBits::ALLOWBIGOBJS : EEControlBits::NONE;
+ if ( ( nCtrl & EEControlBits::ALLOWBIGOBJS ) != nWantBig )
+ mpEngine->SetControlWord( (nCtrl & ~EEControlBits::ALLOWBIGOBJS) | nWantBig );
+
+ // Overwrite the existing object.
+ delete pObj;
+ pObj = mpEngine->CreateTextObject().release();
+ }
+ else // create String
+ {
+ // Store the string replacement for later commits.
+ OUString aText = ScEditUtil::GetSpaceDelimitedString(*mpEngine);
+ maStrEntries.emplace_back(nRow, aText);
+ }
+ }
+};
+
+class TestTabRefAbsHandler
+{
+ SCTAB mnTab;
+ bool mbTestResult;
+public:
+ explicit TestTabRefAbsHandler(SCTAB nTab) : mnTab(nTab), mbTestResult(false) {}
+
+ void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
+ {
+ if (const_cast<ScFormulaCell*>(pCell)->TestTabRefAbs(mnTab))
+ mbTestResult = true;
+ }
+
+ bool getTestResult() const { return mbTestResult; }
+};
+
+}
+
+void ScColumn::RemoveEditAttribs( sc::ColumnBlockPosition& rBlockPos, SCROW nStartRow, SCROW nEndRow )
+{
+ RemoveEditAttribsHandler aFunc(maCells, &GetDoc());
+
+ rBlockPos.miCellPos = sc::ProcessEditText(
+ rBlockPos.miCellPos, maCells, nStartRow, nEndRow, aFunc);
+
+ aFunc.commitStrings();
+}
+
+bool ScColumn::TestTabRefAbs(SCTAB nTable) const
+{
+ TestTabRefAbsHandler aFunc(nTable);
+ sc::ParseFormula(maCells, aFunc);
+ return aFunc.getTestResult();
+}
+
+bool ScColumn::IsEmptyData() const
+{
+ return maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty;
+}
+
+namespace {
+
+class CellCounter
+{
+ size_t mnCount;
+public:
+ CellCounter() : mnCount(0) {}
+
+ void operator() (
+ const sc::CellStoreType::value_type& node, size_t /*nOffset*/, size_t nDataSize)
+ {
+ if (node.type == sc::element_type_empty)
+ return;
+
+ mnCount += nDataSize;
+ }
+
+ size_t getCount() const { return mnCount; }
+};
+
+}
+
+SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const
+{
+ CellCounter aFunc;
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
+ return aFunc.getCount();
+}
+
+bool ScColumn::HasVisibleDataAt(SCROW nRow) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ // Likely invalid row number.
+ return false;
+
+ return it->type != sc::element_type_empty;
+}
+
+bool ScColumn::IsEmptyData(SCROW nStartRow, SCROW nEndRow) const
+{
+ // simple case
+ if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty)
+ return true;
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ // Invalid row number.
+ return false;
+
+ if (it->type != sc::element_type_empty)
+ // Non-empty cell at the start position.
+ return false;
+
+ // start position of next block which is not empty.
+ SCROW nNextRow = nStartRow + it->size - aPos.second;
+ return nEndRow < nNextRow;
+}
+
+bool ScColumn::IsNotesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
+{
+ std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
+ sc::CellNoteStoreType::const_iterator it = aPos.first;
+ if (it == maCellNotes.end())
+ // Invalid row number.
+ return false;
+
+ if (it->type != sc::element_type_empty)
+ // Non-empty cell at the start position.
+ return false;
+
+ // start position of next block which is not empty.
+ SCROW nNextRow = nStartRow + it->size - aPos.second;
+ return nEndRow < nNextRow;
+}
+
+SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const
+{
+ // Given a range of rows, find a top or bottom empty segment.
+ switch (eDir)
+ {
+ case DIR_TOP:
+ {
+ // Determine the length of empty head segment.
+ size_t nLength = nEndRow - nStartRow + 1;
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type != sc::element_type_empty)
+ // First row is already not empty.
+ return 0;
+
+ // length of this empty block minus the offset.
+ size_t nThisLen = it->size - aPos.second;
+ return std::min(nThisLen, nLength);
+ }
+ break;
+ case DIR_BOTTOM:
+ {
+ // Determine the length of empty tail segment.
+ size_t nLength = nEndRow - nStartRow + 1;
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nEndRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type != sc::element_type_empty)
+ // end row is already not empty.
+ return 0;
+
+ // length of this empty block from the tip to the end row position.
+ size_t nThisLen = aPos.second + 1;
+ return std::min(nThisLen, nLength);
+ }
+ break;
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+SCROW ScColumn::GetFirstDataPos() const
+{
+ if (IsEmptyData())
+ return 0;
+
+ sc::CellStoreType::const_iterator it = maCells.begin();
+ if (it->type != sc::element_type_empty)
+ return 0;
+
+ return it->size;
+}
+
+SCROW ScColumn::GetLastDataPos() const
+{
+ if (IsEmptyData())
+ return 0;
+
+ sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
+ if (it->type != sc::element_type_empty)
+ return GetDoc().MaxRow();
+
+ return GetDoc().MaxRow() - static_cast<SCROW>(it->size);
+}
+
+SCROW ScColumn::GetLastDataPos( SCROW nLastRow, ScDataAreaExtras* pDataAreaExtras ) const
+{
+ nLastRow = std::min( nLastRow, GetDoc().MaxRow());
+
+ if (pDataAreaExtras && pDataAreaExtras->mnEndRow < nLastRow)
+ {
+ // Check in order of likeliness.
+ if ( (pDataAreaExtras->mbCellFormats && HasVisibleAttrIn(nLastRow, nLastRow)) ||
+ (pDataAreaExtras->mbCellNotes && !IsNotesEmptyBlock(nLastRow, nLastRow)) ||
+ (pDataAreaExtras->mbCellDrawObjects && !IsDrawObjectsEmptyBlock(nLastRow, nLastRow)))
+ pDataAreaExtras->mnEndRow = nLastRow;
+ }
+
+ sc::CellStoreType::const_position_type aPos = maCells.position(nLastRow);
+
+ if (aPos.first->type != sc::element_type_empty)
+ return nLastRow;
+
+ if (aPos.first == maCells.begin())
+ // This is the first block, and is empty.
+ return 0;
+
+ return static_cast<SCROW>(aPos.first->position - 1);
+}
+
+bool ScColumn::GetPrevDataPos(SCROW& rRow) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ return false;
+
+ if (it->type == sc::element_type_empty)
+ {
+ if (it == maCells.begin())
+ // No more previous non-empty cell.
+ return false;
+
+ rRow -= aPos.second + 1; // Last row position of the previous block.
+ return true;
+ }
+
+ // This block is not empty.
+ if (aPos.second)
+ {
+ // There are preceding cells in this block. Simply move back one cell.
+ --rRow;
+ return true;
+ }
+
+ // This is the first cell in a non-empty block. Move back to the previous block.
+ if (it == maCells.begin())
+ // No more preceding block.
+ return false;
+
+ --rRow; // Move to the last cell of the previous block.
+ --it;
+ if (it->type == sc::element_type_empty)
+ {
+ // This block is empty.
+ if (it == maCells.begin())
+ // No more preceding blocks.
+ return false;
+
+ // Skip the whole empty block segment.
+ rRow -= it->size;
+ }
+
+ return true;
+}
+
+bool ScColumn::GetNextDataPos(SCROW& rRow) const // greater than rRow
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ return false;
+
+ if (it->type == sc::element_type_empty)
+ {
+ // This block is empty. Skip ahead to the next block (if exists).
+ rRow += it->size - aPos.second;
+ ++it;
+ if (it == maCells.end())
+ // No more next block.
+ return false;
+
+ // Next block exists, and is non-empty.
+ return true;
+ }
+
+ if (aPos.second < it->size - 1)
+ {
+ // There are still cells following the current position.
+ ++rRow;
+ return true;
+ }
+
+ // This is the last cell in the block. Move ahead to the next block.
+ rRow += it->size - aPos.second; // First cell in the next block.
+ ++it;
+ if (it == maCells.end())
+ // No more next block.
+ return false;
+
+ if (it->type == sc::element_type_empty)
+ {
+ // Next block is empty. Move to the next block.
+ rRow += it->size;
+ ++it;
+ if (it == maCells.end())
+ return false;
+ }
+
+ return true;
+}
+
+bool ScColumn::TrimEmptyBlocks(SCROW& rRowStart, SCROW& rRowEnd) const
+{
+ assert(rRowStart <= rRowEnd);
+ SCROW nRowStartNew = rRowStart, nRowEndNew = rRowEnd;
+
+ // Trim down rRowStart first
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRowStart);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ return false;
+
+ if (it->type == sc::element_type_empty)
+ {
+ // This block is empty. Skip ahead to the next block (if exists).
+ nRowStartNew += it->size - aPos.second;
+ if (nRowStartNew > rRowEnd)
+ return false;
+ ++it;
+ if (it == maCells.end())
+ // No more next block.
+ return false;
+ }
+
+ // Trim up rRowEnd next
+ aPos = maCells.position(rRowEnd);
+ it = aPos.first;
+ if (it == maCells.end())
+ {
+ rRowStart = nRowStartNew;
+ return true; // Because trimming of rRowStart is ok
+ }
+
+ if (it->type == sc::element_type_empty)
+ {
+ // rRowEnd cannot be in the first block which is empty !
+ assert(it != maCells.begin());
+ // This block is empty. Skip to the previous block (it exists).
+ nRowEndNew -= aPos.second + 1; // Last row position of the previous block.
+ assert(nRowStartNew <= nRowEndNew);
+ }
+
+ rRowStart = nRowStartNew;
+ rRowEnd = nRowEndNew;
+ return true;
+}
+
+SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const
+{
+ if(bForward)
+ {
+ nRow++;
+ SCROW nEndRow = 0;
+ bool bHidden = GetDoc().RowHidden(nRow, nTab, nullptr, &nEndRow);
+ if(bHidden)
+ return std::min<SCROW>(GetDoc().MaxRow(), nEndRow + 1);
+ else
+ return nRow;
+ }
+ else
+ {
+ nRow--;
+ SCROW nStartRow = GetDoc().MaxRow();
+ bool bHidden = GetDoc().RowHidden(nRow, nTab, &nStartRow);
+ if(bHidden)
+ return std::max<SCROW>(0, nStartRow - 1);
+ else
+ return nRow;
+ }
+}
+
+SCROW ScColumn::FindNextVisibleRowWithContent(
+ sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const
+{
+ ScDocument& rDocument = GetDoc();
+ if (bForward)
+ {
+ do
+ {
+ nRow++;
+ SCROW nEndRow = 0;
+ bool bHidden = rDocument.RowHidden(nRow, nTab, nullptr, &nEndRow);
+ if (bHidden)
+ {
+ nRow = nEndRow + 1;
+ if(nRow >= GetDoc().MaxRow())
+ return GetDoc().MaxRow();
+ }
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
+ itPos = aPos.first;
+ if (itPos == maCells.end())
+ // Invalid row.
+ return GetDoc().MaxRow();
+
+ if (itPos->type != sc::element_type_empty)
+ return nRow;
+
+ // Move to the last cell of the current empty block.
+ nRow += itPos->size - aPos.second - 1;
+ }
+ while (nRow < GetDoc().MaxRow());
+
+ return GetDoc().MaxRow();
+ }
+
+ do
+ {
+ nRow--;
+ SCROW nStartRow = GetDoc().MaxRow();
+ bool bHidden = rDocument.RowHidden(nRow, nTab, &nStartRow);
+ if (bHidden)
+ {
+ nRow = nStartRow - 1;
+ if(nRow <= 0)
+ return 0;
+ }
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
+ itPos = aPos.first;
+ if (itPos == maCells.end())
+ // Invalid row.
+ return 0;
+
+ if (itPos->type != sc::element_type_empty)
+ return nRow;
+
+ // Move to the first cell of the current empty block.
+ nRow -= aPos.second;
+ }
+ while (nRow > 0);
+
+ return 0;
+}
+
+void ScColumn::CellStorageModified()
+{
+ // Remove cached values. Given how often this function is called and how (not that) often
+ // the cached values are used, it should be more efficient to just discard everything
+ // instead of trying to figure out each time exactly what to discard.
+ GetDoc().DiscardFormulaGroupContext();
+
+ // TODO: Update column's "last updated" timestamp here.
+
+ assert(sal::static_int_cast<SCROW>(maCells.size()) == GetDoc().GetMaxRowCount()
+ && "Size of the cell array is incorrect." );
+
+ assert(sal::static_int_cast<SCROW>(maCellTextAttrs.size()) == GetDoc().GetMaxRowCount()
+ && "Size of the cell text attribute array is incorrect.");
+
+ assert(sal::static_int_cast<SCROW>(maBroadcasters.size()) == GetDoc().GetMaxRowCount()
+ && "Size of the broadcaster array is incorrect.");
+
+#if DEBUG_COLUMN_STORAGE
+ // Make sure that these two containers are synchronized wrt empty segments.
+ auto lIsEmptyType = [](const auto& rElement) { return rElement.type == sc::element_type_empty; };
+ // Move to the first empty blocks.
+ auto itCell = std::find_if(maCells.begin(), maCells.end(), lIsEmptyType);
+ auto itAttr = std::find_if(maCellTextAttrs.begin(), maCellTextAttrs.end(), lIsEmptyType);
+
+ while (itCell != maCells.end())
+ {
+ if (itCell->position != itAttr->position || itCell->size != itAttr->size)
+ {
+ cout << "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl;
+ cout << "-- cell array" << endl;
+ maCells.dump_blocks(cout);
+ cout << "-- attribute array" << endl;
+ maCellTextAttrs.dump_blocks(cout);
+ cout.flush();
+ abort();
+ }
+
+ // Move to the next empty blocks.
+ ++itCell;
+ itCell = std::find_if(itCell, maCells.end(), lIsEmptyType);
+
+ ++itAttr;
+ itAttr = std::find_if(itAttr, maCellTextAttrs.end(), lIsEmptyType);
+ }
+#endif
+}
+
+#if DUMP_COLUMN_STORAGE
+
+namespace {
+
+#define DUMP_FORMULA_RESULTS 0
+
+struct ColumnStorageDumper
+{
+ const ScDocument& mrDoc;
+
+ ColumnStorageDumper( const ScDocument& rDoc ) : mrDoc(rDoc) {}
+
+ void operator() (const sc::CellStoreType::value_type& rNode) const
+ {
+ switch (rNode.type)
+ {
+ case sc::element_type_numeric:
+ cout << " * numeric block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
+ break;
+ case sc::element_type_string:
+ cout << " * string block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
+ break;
+ case sc::element_type_edittext:
+ cout << " * edit-text block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
+ break;
+ case sc::element_type_formula:
+ dumpFormulaBlock(rNode);
+ break;
+ case sc::element_type_empty:
+ cout << " * empty block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
+ break;
+ default:
+ cout << " * unknown block" << endl;
+ }
+ }
+
+ void dumpFormulaBlock(const sc::CellStoreType::value_type& rNode) const
+ {
+ cout << " * formula block (pos=" << rNode.position << ", length=" << rNode.size << ")" << endl;
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
+ sc::formula_block::const_iterator itEnd = sc::formula_block::end(*rNode.data);
+
+ for (; it != itEnd; ++it)
+ {
+ const ScFormulaCell* pCell = *it;
+ if (!pCell->IsShared())
+ {
+ cout << " * row " << pCell->aPos.Row() << " not shared" << endl;
+ printFormula(pCell);
+ printResult(pCell);
+ continue;
+ }
+
+ if (pCell->GetSharedTopRow() != pCell->aPos.Row())
+ {
+ cout << " * row " << pCell->aPos.Row() << " shared with top row "
+ << pCell->GetSharedTopRow() << " with length " << pCell->GetSharedLength()
+ << endl;
+ continue;
+ }
+
+ SCROW nLen = pCell->GetSharedLength();
+ cout << " * group: start=" << pCell->aPos.Row() << ", length=" << nLen << endl;
+ printFormula(pCell);
+ printResult(pCell);
+
+ if (nLen > 1)
+ {
+ for (SCROW i = 0; i < nLen-1; ++i, ++it)
+ {
+ pCell = *it;
+ printResult(pCell);
+ }
+ }
+ }
+ }
+
+ void printFormula(const ScFormulaCell* pCell) const
+ {
+ sc::TokenStringContext aCxt(mrDoc, mrDoc.GetGrammar());
+ OUString aFormula = pCell->GetCode()->CreateString(aCxt, pCell->aPos);
+ cout << " * formula: " << aFormula << endl;
+ }
+
+#if DUMP_FORMULA_RESULTS
+ void printResult(const ScFormulaCell* pCell) const
+ {
+ sc::FormulaResultValue aRes = pCell->GetResult();
+ cout << " * result: ";
+ switch (aRes.meType)
+ {
+ case sc::FormulaResultValue::Value:
+ cout << aRes.mfValue << " (type: value)";
+ break;
+ case sc::FormulaResultValue::String:
+ cout << "'" << aRes.maString.getString() << "' (type: string)";
+ break;
+ case sc::FormulaResultValue::Error:
+ cout << "error (" << static_cast<int>(aRes.mnError) << ")";
+ break;
+ case sc::FormulaResultValue::Invalid:
+ cout << "invalid";
+ break;
+ }
+
+ cout << endl;
+ }
+#else
+ void printResult(const ScFormulaCell*) const
+ {
+ (void) this; /* loplugin:staticmethods */
+ }
+#endif
+};
+
+}
+
+void ScColumn::DumpColumnStorage() const
+{
+ cout << "-- table: " << nTab << "; column: " << nCol << endl;
+ std::for_each(maCells.begin(), maCells.end(), ColumnStorageDumper(GetDoc()));
+ cout << "--" << endl;
+}
+#endif
+
+void ScColumn::CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const
+{
+ rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2); // Empty the destination range first.
+
+ sc::CellTextAttrStoreType::const_iterator itBlk = maCellTextAttrs.begin(), itBlkEnd = maCellTextAttrs.end();
+
+ // Locate the top row position.
+ size_t nBlockStart = 0, nRowPos = static_cast<size_t>(nRow1);
+ itBlk = std::find_if(itBlk, itBlkEnd, [&nRowPos, &nBlockStart](const auto& rAttr) {
+ return nBlockStart <= nRowPos && nRowPos < nBlockStart + rAttr.size; });
+
+ if (itBlk == itBlkEnd)
+ // Specified range not found. Bail out.
+ return;
+
+ size_t nBlockEnd;
+ size_t nOffsetInBlock = nRowPos - nBlockStart;
+
+ nRowPos = static_cast<size_t>(nRow2); // End row position.
+
+ // Keep copying until we hit the end row position.
+ sc::celltextattr_block::const_iterator itData, itDataEnd;
+ for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
+ {
+ nBlockEnd = nBlockStart + itBlk->size;
+ if (!itBlk->data)
+ {
+ // Empty block.
+ if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
+ // This block contains the end row.
+ rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nRowPos);
+ else
+ rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nBlockEnd-1);
+
+ continue;
+ }
+
+ // Non-empty block.
+ itData = sc::celltextattr_block::begin(*itBlk->data);
+ itDataEnd = sc::celltextattr_block::end(*itBlk->data);
+ std::advance(itData, nOffsetInBlock);
+
+ if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
+ {
+ // This block contains the end row. Only copy partially.
+ size_t nOffset = nRowPos - nBlockStart + 1;
+ itDataEnd = sc::celltextattr_block::begin(*itBlk->data);
+ std::advance(itDataEnd, nOffset);
+
+ rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
+ break;
+ }
+
+ rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
+ }
+}
+
+namespace {
+
+class CopyCellNotesHandler
+{
+ ScColumn& mrDestCol;
+ sc::CellNoteStoreType& mrDestNotes;
+ sc::CellNoteStoreType::iterator miPos;
+ SCTAB mnSrcTab;
+ SCCOL mnSrcCol;
+ SCTAB mnDestTab;
+ SCCOL mnDestCol;
+ SCROW mnDestOffset; /// Add this to the source row position to get the destination row.
+ bool mbCloneCaption;
+
+public:
+ CopyCellNotesHandler( const ScColumn& rSrcCol, ScColumn& rDestCol, SCROW nDestOffset, bool bCloneCaption ) :
+ mrDestCol(rDestCol),
+ mrDestNotes(rDestCol.GetCellNoteStore()),
+ miPos(mrDestNotes.begin()),
+ mnSrcTab(rSrcCol.GetTab()),
+ mnSrcCol(rSrcCol.GetCol()),
+ mnDestTab(rDestCol.GetTab()),
+ mnDestCol(rDestCol.GetCol()),
+ mnDestOffset(nDestOffset),
+ mbCloneCaption(bCloneCaption) {}
+
+ void operator() ( size_t nRow, const ScPostIt* p )
+ {
+ SCROW nDestRow = nRow + mnDestOffset;
+ ScAddress aSrcPos(mnSrcCol, nRow, mnSrcTab);
+ ScAddress aDestPos(mnDestCol, nDestRow, mnDestTab);
+ ScPostIt* pNew = p->Clone(aSrcPos, mrDestCol.GetDoc(), aDestPos, mbCloneCaption).release();
+ miPos = mrDestNotes.set(miPos, nDestRow, pNew);
+ // Notify our LOK clients also
+ ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, mrDestCol.GetDoc(), aDestPos, pNew);
+ }
+};
+
+}
+
+void ScColumn::CopyCellNotesToDocument(
+ SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, bool bCloneCaption, SCROW nRowOffsetDest ) const
+{
+ if (IsNotesEmptyBlock(nRow1, nRow2))
+ // The column has no cell notes to copy between specified rows.
+ return;
+
+ ScDrawLayer *pDrawLayer = rDestCol.GetDoc().GetDrawLayer();
+ bool bWasLocked = bool();
+ if (pDrawLayer)
+ {
+ // Avoid O(n^2) by temporary locking SdrModel which disables broadcasting.
+ // Each cell note adds undo listener, and all of them would be woken up in ScPostIt::CreateCaption.
+ bWasLocked = pDrawLayer->isLocked();
+ pDrawLayer->setLock(true);
+ }
+ CopyCellNotesHandler aFunc(*this, rDestCol, nRowOffsetDest, bCloneCaption);
+ sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
+ if (pDrawLayer)
+ pDrawLayer->setLock(bWasLocked);
+}
+
+void ScColumn::DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol, sc::ColumnBlockPosition& maDestBlockPos,
+ bool bCloneCaption, SCROW nRowOffsetDest ) const
+{
+ CopyCellNotesToDocument(nStartRow, nStartRow + nDataSize -1, rDestCol, bCloneCaption, nRowOffsetDest);
+ maDestBlockPos.miCellNotePos = rDestCol.maCellNotes.begin();
+}
+
+SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow)
+{
+ return maBroadcasters.get<SvtBroadcaster*>(nRow);
+}
+
+const SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow) const
+{
+ return maBroadcasters.get<SvtBroadcaster*>(nRow);
+}
+
+void ScColumn::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 )
+{
+ rBlockPos.miBroadcasterPos =
+ maBroadcasters.set_empty(rBlockPos.miBroadcasterPos, nRow1, nRow2);
+}
+
+void ScColumn::PrepareBroadcastersForDestruction()
+{
+ for (auto& rBroadcaster : maBroadcasters)
+ {
+ if (rBroadcaster.type == sc::element_type_broadcaster)
+ {
+ sc::broadcaster_block::iterator it = sc::broadcaster_block::begin(*rBroadcaster.data);
+ sc::broadcaster_block::iterator itEnd = sc::broadcaster_block::end(*rBroadcaster.data);
+ for (; it != itEnd; ++it)
+ (*it)->PrepareForDestruction();
+ }
+ }
+}
+
+namespace
+{
+struct BroadcasterNoListenersPredicate
+{
+ bool operator()( size_t, const SvtBroadcaster* broadcaster )
+ {
+ return !broadcaster->HasListeners();
+ }
+};
+
+}
+
+void ScColumn::DeleteEmptyBroadcasters()
+{
+ if(!mbEmptyBroadcastersPending)
+ return;
+ // Clean up after ScDocument::EnableDelayDeletingBroadcasters().
+ BroadcasterNoListenersPredicate predicate;
+ sc::SetElementsToEmpty1<sc::broadcaster_block>( maBroadcasters, predicate );
+ mbEmptyBroadcastersPending = false;
+}
+
+// Sparklines
+
+namespace
+{
+
+class DeletingSparklinesHandler
+{
+ ScDocument& m_rDocument;
+ SCTAB m_nTab;
+
+public:
+ DeletingSparklinesHandler(ScDocument& rDocument, SCTAB nTab)
+ : m_rDocument(rDocument)
+ , m_nTab(nTab)
+ {}
+
+ void operator() (size_t /*nRow*/, const sc::SparklineCell* pCell)
+ {
+ auto* pList = m_rDocument.GetSparklineList(m_nTab);
+ pList->removeSparkline(pCell->getSparkline());
+ }
+};
+
+} // end anonymous ns
+
+sc::SparklineCell* ScColumn::GetSparklineCell(SCROW nRow)
+{
+ return maSparklines.get<sc::SparklineCell*>(nRow);
+}
+
+void ScColumn::CreateSparklineCell(SCROW nRow, std::shared_ptr<sc::Sparkline> const& pSparkline)
+{
+ auto* pList = GetDoc().GetSparklineList(GetTab());
+ pList->addSparkline(pSparkline);
+ maSparklines.set(nRow, new sc::SparklineCell(pSparkline));
+}
+
+void ScColumn::DeleteSparklineCells(sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2)
+{
+ DeletingSparklinesHandler aFunction(GetDoc(), nTab);
+ sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow1, nRow2, aFunction);
+
+ rBlockPos.miSparklinePos = maSparklines.set_empty(rBlockPos.miSparklinePos, nRow1, nRow2);
+}
+
+bool ScColumn::DeleteSparkline(SCROW nRow)
+{
+ if (!GetDoc().ValidRow(nRow))
+ return false;
+
+ DeletingSparklinesHandler aFunction(GetDoc(), nTab);
+ sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow, nRow, aFunction);
+
+ maSparklines.set_empty(nRow, nRow);
+ return true;
+}
+
+bool ScColumn::IsSparklinesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
+{
+ std::pair<sc::SparklineStoreType::const_iterator,size_t> aPos = maSparklines.position(nStartRow);
+ sc::SparklineStoreType::const_iterator it = aPos.first;
+ if (it == maSparklines.end())
+ return false;
+
+ if (it->type != sc::element_type_empty)
+ return false;
+
+ // start position of next block which is not empty.
+ SCROW nNextRow = nStartRow + it->size - aPos.second;
+ return nEndRow < nNextRow;
+}
+
+namespace
+{
+
+class CopySparklinesHandler
+{
+ ScColumn& mrDestColumn;
+ sc::SparklineStoreType& mrDestSparkline;
+ sc::SparklineStoreType::iterator miDestPosition;
+ SCROW mnDestOffset;
+
+public:
+ CopySparklinesHandler(ScColumn& rDestColumn, SCROW nDestOffset)
+ : mrDestColumn(rDestColumn)
+ , mrDestSparkline(mrDestColumn.GetSparklineStore())
+ , miDestPosition(mrDestSparkline.begin())
+ , mnDestOffset(nDestOffset)
+ {}
+
+ void operator() (size_t nRow, const sc::SparklineCell* pCell)
+ {
+ SCROW nDestRow = nRow + mnDestOffset;
+
+ auto const& pSparkline = pCell->getSparkline();
+ auto const& pGroup = pCell->getSparklineGroup();
+
+ auto& rDestDoc = mrDestColumn.GetDoc();
+ auto pDestinationGroup = rDestDoc.SearchSparklineGroup(pGroup->getID());
+ if (!pDestinationGroup)
+ pDestinationGroup = std::make_shared<sc::SparklineGroup>(*pGroup); // Copy the group
+ auto pNewSparkline = std::make_shared<sc::Sparkline>(mrDestColumn.GetCol(), nDestRow, pDestinationGroup);
+ pNewSparkline->setInputRange(pSparkline->getInputRange());
+
+ auto* pList = rDestDoc.GetSparklineList(mrDestColumn.GetTab());
+ pList->addSparkline(pNewSparkline);
+
+ miDestPosition = mrDestSparkline.set(miDestPosition, nDestRow, new sc::SparklineCell(pNewSparkline));
+ }
+};
+
+}
+
+void ScColumn::CopyCellSparklinesToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, SCROW nRowOffsetDest) const
+{
+ if (IsSparklinesEmptyBlock(nRow1, nRow2))
+ // The column has no cell sparklines to copy between specified rows.
+ return;
+
+ CopySparklinesHandler aFunctor(rDestCol, nRowOffsetDest);
+ sc::ParseSparkline(maSparklines.begin(), maSparklines, nRow1, nRow2, aFunctor);
+}
+
+void ScColumn::DuplicateSparklines(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol,
+ sc::ColumnBlockPosition& rDestBlockPos, SCROW nRowOffsetDest) const
+{
+ CopyCellSparklinesToDocument(nStartRow, nStartRow + nDataSize - 1, rDestCol, nRowOffsetDest);
+ rDestBlockPos.miSparklinePos = rDestCol.maSparklines.begin();
+}
+
+bool ScColumn::HasSparklines() const
+{
+ if (maSparklines.block_size() == 1 && maSparklines.begin()->type == sc::element_type_empty)
+ return false; // all elements are empty
+ return true; // otherwise some must be sparklines
+}
+
+SCROW ScColumn::GetSparklinesMaxRow() const
+{
+ SCROW maxRow = 0;
+ for (const auto& rSparkline : maSparklines)
+ {
+ if (rSparkline.type == sc::element_type_sparkline)
+ maxRow = rSparkline.position + rSparkline.size - 1;
+ }
+ return maxRow;
+}
+
+SCROW ScColumn::GetSparklinesMinRow() const
+{
+ SCROW minRow = 0;
+ sc::SparklineStoreType::const_iterator it = std::find_if(maSparklines.begin(), maSparklines.end(),
+ [](const auto& rSparkline)
+ {
+ return rSparkline.type == sc::element_type_sparkline;
+ });
+ if (it != maSparklines.end())
+ minRow = it->position;
+ return minRow;
+}
+
+// Notes
+
+ScPostIt* ScColumn::GetCellNote(SCROW nRow)
+{
+ return maCellNotes.get<ScPostIt*>(nRow);
+}
+
+const ScPostIt* ScColumn::GetCellNote(SCROW nRow) const
+{
+ return maCellNotes.get<ScPostIt*>(nRow);
+}
+
+const ScPostIt* ScColumn::GetCellNote( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
+{
+ sc::CellNoteStoreType::const_position_type aPos = maCellNotes.position(rBlockPos.miCellNotePos, nRow);
+ rBlockPos.miCellNotePos = aPos.first;
+
+ if (aPos.first->type != sc::element_type_cellnote)
+ return nullptr;
+
+ return sc::cellnote_block::at(*aPos.first->data, aPos.second);
+}
+
+ScPostIt* ScColumn::GetCellNote( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow )
+{
+ return const_cast<ScPostIt*>(const_cast<const ScColumn*>(this)->GetCellNote( rBlockPos, nRow ));
+}
+
+void ScColumn::SetCellNote(SCROW nRow, std::unique_ptr<ScPostIt> pNote)
+{
+ //pNote->UpdateCaptionPos(ScAddress(nCol, nRow, nTab)); // TODO notes useful ? slow import with many notes
+ maCellNotes.set(nRow, pNote.release());
+}
+
+namespace {
+ class CellNoteHandler
+ {
+ const ScDocument& m_rDocument;
+ const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
+ const bool m_bForgetCaptionOwnership;
+
+ public:
+ CellNoteHandler(const ScDocument& rDocument, const ScAddress& rPos, bool bForgetCaptionOwnership) :
+ m_rDocument(rDocument),
+ m_aAddress(rPos),
+ m_bForgetCaptionOwnership(bForgetCaptionOwnership) {}
+
+ void operator() ( size_t nRow, ScPostIt* p )
+ {
+ if (m_bForgetCaptionOwnership)
+ p->ForgetCaption();
+
+ // Create a 'complete' address object
+ ScAddress aAddr(m_aAddress);
+ aAddr.SetRow(nRow);
+ // Notify our LOK clients
+ ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Remove, m_rDocument, aAddr, p);
+ }
+ };
+} // anonymous namespace
+
+void ScColumn::CellNotesDeleting(SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership)
+{
+ ScAddress aAddr(nCol, 0, nTab);
+ CellNoteHandler aFunc(GetDoc(), aAddr, bForgetCaptionOwnership);
+ sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::DeleteCellNotes( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, bool bForgetCaptionOwnership )
+{
+ CellNotesDeleting(nRow1, nRow2, bForgetCaptionOwnership);
+
+ rBlockPos.miCellNotePos =
+ maCellNotes.set_empty(rBlockPos.miCellNotePos, nRow1, nRow2);
+}
+
+bool ScColumn::HasCellNotes() const
+{
+ if (maCellNotes.block_size() == 1 && maCellNotes.begin()->type == sc::element_type_empty)
+ return false; // all elements are empty
+ return true; // otherwise some must be notes
+}
+
+SCROW ScColumn::GetCellNotesMaxRow() const
+{
+ // hypothesis : the column has cell notes (should be checked before)
+ SCROW maxRow = 0;
+ for (const auto& rCellNote : maCellNotes)
+ {
+ if (rCellNote.type == sc::element_type_cellnote)
+ maxRow = rCellNote.position + rCellNote.size -1;
+ }
+ return maxRow;
+}
+SCROW ScColumn::GetCellNotesMinRow() const
+{
+ // hypothesis : the column has cell notes (should be checked before)
+ SCROW minRow = 0;
+ sc::CellNoteStoreType::const_iterator it = std::find_if(maCellNotes.begin(), maCellNotes.end(),
+ [](const auto& rCellNote) { return rCellNote.type == sc::element_type_cellnote; });
+ if (it != maCellNotes.end())
+ minRow = it->position;
+ return minRow;
+}
+
+sal_uInt16 ScColumn::GetTextWidth(SCROW nRow) const
+{
+ return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnTextWidth;
+}
+
+void ScColumn::SetTextWidth(SCROW nRow, sal_uInt16 nWidth)
+{
+ sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
+ if (aPos.first->type != sc::element_type_celltextattr)
+ return;
+
+ // Set new value only when the slot is not empty.
+ sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnTextWidth = nWidth;
+ CellStorageModified();
+}
+
+SvtScriptType ScColumn::GetScriptType( SCROW nRow ) const
+{
+ if (!GetDoc().ValidRow(nRow) || maCellTextAttrs.is_empty(nRow))
+ return SvtScriptType::NONE;
+
+ return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnScriptType;
+}
+
+SvtScriptType ScColumn::GetRangeScriptType(
+ sc::CellTextAttrStoreType::iterator& itPos, SCROW nRow1, SCROW nRow2, const sc::CellStoreType::iterator& itrCells_ )
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return SvtScriptType::NONE;
+
+ SCROW nRow = nRow1;
+ std::pair<sc::CellTextAttrStoreType::iterator,size_t> aRet =
+ maCellTextAttrs.position(itPos, nRow1);
+
+ itPos = aRet.first; // Track the position of cell text attribute array.
+ sc::CellStoreType::iterator itrCells = itrCells_;
+
+ SvtScriptType nScriptType = SvtScriptType::NONE;
+ bool bUpdated = false;
+ if (itPos->type == sc::element_type_celltextattr)
+ {
+ sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
+ sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
+ std::advance(it, aRet.second);
+ for (; it != itEnd; ++it, ++nRow)
+ {
+ if (nRow > nRow2)
+ return nScriptType;
+
+ sc::CellTextAttr& rVal = *it;
+ if (UpdateScriptType(rVal, nRow, itrCells))
+ bUpdated = true;
+ nScriptType |= rVal.mnScriptType;
+ }
+ }
+ else
+ {
+ // Skip this whole block.
+ nRow += itPos->size - aRet.second;
+ }
+
+ while (nRow <= nRow2)
+ {
+ ++itPos;
+ if (itPos == maCellTextAttrs.end())
+ return nScriptType;
+
+ if (itPos->type != sc::element_type_celltextattr)
+ {
+ // Skip this whole block.
+ nRow += itPos->size;
+ continue;
+ }
+
+ sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
+ sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
+ for (; it != itEnd; ++it, ++nRow)
+ {
+ if (nRow > nRow2)
+ return nScriptType;
+
+ sc::CellTextAttr& rVal = *it;
+ if (UpdateScriptType(rVal, nRow, itrCells))
+ bUpdated = true;
+
+ nScriptType |= rVal.mnScriptType;
+ }
+ }
+
+ if (bUpdated)
+ CellStorageModified();
+
+ return nScriptType;
+}
+
+void ScColumn::SetScriptType( SCROW nRow, SvtScriptType nType )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
+ if (aPos.first->type != sc::element_type_celltextattr)
+ // Set new value only when the slot is already set.
+ return;
+
+ sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnScriptType = nType;
+ CellStorageModified();
+}
+
+formula::FormulaTokenRef ScColumn::ResolveStaticReference( SCROW nRow )
+{
+ std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it == maCells.end())
+ // Invalid row. Return a null token.
+ return formula::FormulaTokenRef();
+
+ switch (it->type)
+ {
+ case sc::element_type_numeric:
+ {
+ double fVal = sc::numeric_block::at(*it->data, aPos.second);
+ return formula::FormulaTokenRef(new formula::FormulaDoubleToken(fVal));
+ }
+ case sc::element_type_formula:
+ {
+ ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ if (p->IsValue())
+ return formula::FormulaTokenRef(new formula::FormulaDoubleToken(p->GetValue()));
+
+ return formula::FormulaTokenRef(new formula::FormulaStringToken(p->GetString()));
+ }
+ case sc::element_type_string:
+ {
+ const svl::SharedString& rSS = sc::string_block::at(*it->data, aPos.second);
+ return formula::FormulaTokenRef(new formula::FormulaStringToken(rSS));
+ }
+ case sc::element_type_edittext:
+ {
+ const EditTextObject* pText = sc::edittext_block::at(*it->data, aPos.second);
+ OUString aStr = ScEditUtil::GetString(*pText, &GetDoc());
+ svl::SharedString aSS( GetDoc().GetSharedStringPool().intern(aStr));
+ return formula::FormulaTokenRef(new formula::FormulaStringToken(std::move(aSS)));
+ }
+ case sc::element_type_empty:
+ default:
+ // Return a value of 0.0 in all the other cases.
+ return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
+ }
+}
+
+namespace {
+
+class ToMatrixHandler
+{
+ ScMatrix& mrMat;
+ SCCOL mnMatCol;
+ SCROW mnTopRow;
+ ScDocument* mpDoc;
+ svl::SharedStringPool& mrStrPool;
+public:
+ ToMatrixHandler(ScMatrix& rMat, SCCOL nMatCol, SCROW nTopRow, ScDocument* pDoc) :
+ mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
+ mpDoc(pDoc), mrStrPool(pDoc->GetSharedStringPool()) {}
+
+ void operator() (size_t nRow, double fVal)
+ {
+ mrMat.PutDouble(fVal, mnMatCol, nRow - mnTopRow);
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ // Formula cell may need to re-calculate.
+ ScFormulaCell& rCell = const_cast<ScFormulaCell&>(*p);
+ if (rCell.IsValue())
+ mrMat.PutDouble(rCell.GetValue(), mnMatCol, nRow - mnTopRow);
+ else
+ mrMat.PutString(rCell.GetString(), mnMatCol, nRow - mnTopRow);
+ }
+
+ void operator() (size_t nRow, const svl::SharedString& rSS)
+ {
+ mrMat.PutString(rSS, mnMatCol, nRow - mnTopRow);
+ }
+
+ void operator() (size_t nRow, const EditTextObject* pStr)
+ {
+ mrMat.PutString(mrStrPool.intern(ScEditUtil::GetString(*pStr, mpDoc)), mnMatCol, nRow - mnTopRow);
+ }
+};
+
+}
+
+bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 )
+{
+ if (nRow1 > nRow2)
+ return false;
+
+ ToMatrixHandler aFunc(rMat, nMatCol, nRow1, &GetDoc());
+ sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+ return true;
+}
+
+namespace {
+
+struct CellBucket
+{
+ SCSIZE mnEmpValStart;
+ SCSIZE mnNumValStart;
+ SCSIZE mnStrValStart;
+ SCSIZE mnEmpValCount;
+ std::vector<double> maNumVals;
+ std::vector<svl::SharedString> maStrVals;
+
+ CellBucket() : mnEmpValStart(0), mnNumValStart(0), mnStrValStart(0), mnEmpValCount(0) {}
+
+ void flush(ScMatrix& rMat, SCSIZE nCol)
+ {
+ if (mnEmpValCount)
+ {
+ rMat.PutEmptyResultVector(mnEmpValCount, nCol, mnEmpValStart);
+ reset();
+ }
+ else if (!maNumVals.empty())
+ {
+ const double* p = maNumVals.data();
+ rMat.PutDouble(p, maNumVals.size(), nCol, mnNumValStart);
+ reset();
+ }
+ else if (!maStrVals.empty())
+ {
+ const svl::SharedString* p = maStrVals.data();
+ rMat.PutString(p, maStrVals.size(), nCol, mnStrValStart);
+ reset();
+ }
+ }
+
+ void reset()
+ {
+ mnEmpValStart = mnNumValStart = mnStrValStart = 0;
+ mnEmpValCount = 0;
+ maNumVals.clear();
+ maStrVals.clear();
+ }
+};
+
+class FillMatrixHandler
+{
+ ScMatrix& mrMat;
+ size_t mnMatCol;
+ size_t mnTopRow;
+
+ ScDocument* mpDoc;
+ svl::SharedStringPool& mrPool;
+ svl::SharedStringPool* mpPool; // if matrix is not in the same document
+
+public:
+ FillMatrixHandler(ScMatrix& rMat, size_t nMatCol, size_t nTopRow, ScDocument* pDoc, svl::SharedStringPool* pPool) :
+ mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
+ mpDoc(pDoc), mrPool(pDoc->GetSharedStringPool()), mpPool(pPool) {}
+
+ void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ size_t nMatRow = node.position + nOffset - mnTopRow;
+
+ switch (node.type)
+ {
+ case sc::element_type_numeric:
+ {
+ const double* p = &sc::numeric_block::at(*node.data, nOffset);
+ mrMat.PutDouble(p, nDataSize, mnMatCol, nMatRow);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ if (!mpPool)
+ {
+ const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
+ mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
+ }
+ else
+ {
+ std::vector<svl::SharedString> aStrings;
+ aStrings.reserve(nDataSize);
+ const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
+ for (size_t i = 0; i < nDataSize; ++i)
+ {
+ aStrings.push_back(mpPool->intern(p[i].getString()));
+ }
+ mrMat.PutString(aStrings.data(), aStrings.size(), mnMatCol, nMatRow);
+ }
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ std::vector<svl::SharedString> aSSs;
+ aSSs.reserve(nDataSize);
+ sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::edittext_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (; it != itEnd; ++it)
+ {
+ OUString aStr = ScEditUtil::GetString(**it, mpDoc);
+ if (!mpPool)
+ aSSs.push_back(mrPool.intern(aStr));
+ else
+ aSSs.push_back(mpPool->intern(aStr));
+ }
+
+ const svl::SharedString* p = aSSs.data();
+ mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ CellBucket aBucket;
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ size_t nPrevRow = 0, nThisRow = node.position + nOffset;
+ for (; it != itEnd; ++it, nPrevRow = nThisRow, ++nThisRow)
+ {
+ ScFormulaCell& rCell = **it;
+
+ if (rCell.IsEmpty())
+ {
+ if (aBucket.mnEmpValCount && nThisRow == nPrevRow + 1)
+ {
+ // Secondary empty results.
+ ++aBucket.mnEmpValCount;
+ }
+ else
+ {
+ // First empty result.
+ aBucket.flush(mrMat, mnMatCol);
+ aBucket.mnEmpValStart = nThisRow - mnTopRow;
+ ++aBucket.mnEmpValCount;
+ }
+ continue;
+ }
+
+ FormulaError nErr;
+ double fVal;
+ if (rCell.GetErrorOrValue(nErr, fVal))
+ {
+ if (nErr != FormulaError::NONE)
+ fVal = CreateDoubleError(nErr);
+
+ if (!aBucket.maNumVals.empty() && nThisRow == nPrevRow + 1)
+ {
+ // Secondary numbers.
+ aBucket.maNumVals.push_back(fVal);
+ }
+ else
+ {
+ // First number.
+ aBucket.flush(mrMat, mnMatCol);
+ aBucket.mnNumValStart = nThisRow - mnTopRow;
+ aBucket.maNumVals.push_back(fVal);
+ }
+ continue;
+ }
+
+ svl::SharedString aStr = rCell.GetString();
+ if (mpPool)
+ aStr = mpPool->intern(aStr.getString());
+ if (!aBucket.maStrVals.empty() && nThisRow == nPrevRow + 1)
+ {
+ // Secondary strings.
+ aBucket.maStrVals.push_back(aStr);
+ }
+ else
+ {
+ // First string.
+ aBucket.flush(mrMat, mnMatCol);
+ aBucket.mnStrValStart = nThisRow - mnTopRow;
+ aBucket.maStrVals.push_back(aStr);
+ }
+ }
+
+ aBucket.flush(mrMat, mnMatCol);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+}
+
+void ScColumn::FillMatrix( ScMatrix& rMat, size_t nMatCol, SCROW nRow1, SCROW nRow2, svl::SharedStringPool* pPool ) const
+{
+ FillMatrixHandler aFunc(rMat, nMatCol, nRow1, &GetDoc(), pPool);
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+}
+
+namespace {
+
+template<typename Blk>
+void getBlockIterators(
+ const sc::CellStoreType::iterator& it, size_t& rLenRemain,
+ typename Blk::iterator& rData, typename Blk::iterator& rDataEnd )
+{
+ rData = Blk::begin(*it->data);
+ if (rLenRemain >= it->size)
+ {
+ // Block is shorter than the remaining requested length.
+ rDataEnd = Blk::end(*it->data);
+ rLenRemain -= it->size;
+ }
+ else
+ {
+ rDataEnd = rData;
+ std::advance(rDataEnd, rLenRemain);
+ rLenRemain = 0;
+ }
+}
+
+bool appendToBlock(
+ ScDocument* pDoc, sc::FormulaGroupContext& rCxt, sc::FormulaGroupContext::ColArray& rColArray,
+ size_t nPos, size_t nArrayLen, const sc::CellStoreType::iterator& _it, const sc::CellStoreType::iterator& itEnd )
+{
+ svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
+ size_t nLenRemain = nArrayLen - nPos;
+
+ for (sc::CellStoreType::iterator it = _it; it != itEnd; ++it)
+ {
+ switch (it->type)
+ {
+ case sc::element_type_string:
+ {
+ sc::string_block::iterator itData, itDataEnd;
+ getBlockIterators<sc::string_block>(it, nLenRemain, itData, itDataEnd);
+ rCxt.ensureStrArray(rColArray, nArrayLen);
+
+ for (; itData != itDataEnd; ++itData, ++nPos)
+ (*rColArray.mpStrArray)[nPos] = itData->getData();
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::iterator itData, itDataEnd;
+ getBlockIterators<sc::edittext_block>(it, nLenRemain, itData, itDataEnd);
+ rCxt.ensureStrArray(rColArray, nArrayLen);
+
+ for (; itData != itDataEnd; ++itData, ++nPos)
+ {
+ OUString aStr = ScEditUtil::GetString(**itData, pDoc);
+ (*rColArray.mpStrArray)[nPos] = rPool.intern(aStr).getData();
+ }
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::iterator itData, itDataEnd;
+ getBlockIterators<sc::formula_block>(it, nLenRemain, itData, itDataEnd);
+
+ /* tdf#91416 setting progress in triggers a resize of the window
+ and so ScTabView::DoResize and an InterpretVisible and
+ InterpretDirtyCells which resets the mpFormulaGroupCxt that
+ the current rCxt points to, which is bad, so disable progress
+ during GetResult
+ */
+ ScProgress *pProgress = ScProgress::GetInterpretProgress();
+ bool bTempDisableProgress = pProgress && pProgress->Enabled();
+ if (bTempDisableProgress)
+ pProgress->Disable();
+
+ for (; itData != itDataEnd; ++itData, ++nPos)
+ {
+ ScFormulaCell& rFC = **itData;
+
+ sc::FormulaResultValue aRes = rFC.GetResult();
+
+ if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
+ {
+ if (aRes.mnError == FormulaError::CircularReference)
+ {
+ // This cell needs to be recalculated on next visit.
+ rFC.SetErrCode(FormulaError::NONE);
+ rFC.SetDirtyVar();
+ }
+ return false;
+ }
+
+ if (aRes.meType == sc::FormulaResultValue::String)
+ {
+ rCxt.ensureStrArray(rColArray, nArrayLen);
+ (*rColArray.mpStrArray)[nPos] = aRes.maString.getData();
+ }
+ else
+ {
+ rCxt.ensureNumArray(rColArray, nArrayLen);
+ (*rColArray.mpNumArray)[nPos] = aRes.mfValue;
+ }
+ }
+
+ if (bTempDisableProgress)
+ pProgress->Enable();
+ }
+ break;
+ case sc::element_type_empty:
+ {
+ if (nLenRemain > it->size)
+ {
+ nPos += it->size;
+ nLenRemain -= it->size;
+ }
+ else
+ {
+ nPos = nArrayLen;
+ nLenRemain = 0;
+ }
+ }
+ break;
+ case sc::element_type_numeric:
+ {
+ sc::numeric_block::iterator itData, itDataEnd;
+ getBlockIterators<sc::numeric_block>(it, nLenRemain, itData, itDataEnd);
+ rCxt.ensureNumArray(rColArray, nArrayLen);
+
+ for (; itData != itDataEnd; ++itData, ++nPos)
+ (*rColArray.mpNumArray)[nPos] = *itData;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ if (!nLenRemain)
+ return true;
+ }
+
+ return false;
+}
+
+void copyFirstStringBlock(
+ ScDocument& rDoc, sc::FormulaGroupContext::StrArrayType& rArray, size_t nLen, const sc::CellStoreType::iterator& itBlk )
+{
+ sc::FormulaGroupContext::StrArrayType::iterator itArray = rArray.begin();
+
+ switch (itBlk->type)
+ {
+ case sc::element_type_string:
+ {
+ sc::string_block::iterator it = sc::string_block::begin(*itBlk->data);
+ sc::string_block::iterator itEnd = it;
+ std::advance(itEnd, nLen);
+ for (; it != itEnd; ++it, ++itArray)
+ *itArray = it->getData();
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::iterator it = sc::edittext_block::begin(*itBlk->data);
+ sc::edittext_block::iterator itEnd = it;
+ std::advance(itEnd, nLen);
+
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ for (; it != itEnd; ++it, ++itArray)
+ {
+ EditTextObject* pText = *it;
+ OUString aStr = ScEditUtil::GetString(*pText, &rDoc);
+ *itArray = rPool.intern(aStr).getData();
+ }
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+sc::FormulaGroupContext::ColArray*
+copyFirstFormulaBlock(
+ sc::FormulaGroupContext& rCxt, const sc::CellStoreType::iterator& itBlk, size_t nArrayLen,
+ SCTAB nTab, SCCOL nCol )
+{
+ size_t nLen = std::min(itBlk->size, nArrayLen);
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*itBlk->data);
+ sc::formula_block::iterator itEnd;
+
+ sc::FormulaGroupContext::NumArrayType* pNumArray = nullptr;
+ sc::FormulaGroupContext::StrArrayType* pStrArray = nullptr;
+
+ itEnd = it;
+ std::advance(itEnd, nLen);
+ size_t nPos = 0;
+ for (; it != itEnd; ++it, ++nPos)
+ {
+ ScFormulaCell& rFC = **it;
+ sc::FormulaResultValue aRes = rFC.GetResult();
+ if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError != FormulaError::NONE)
+ {
+ if (aRes.mnError == FormulaError::CircularReference)
+ {
+ // This cell needs to be recalculated on next visit.
+ rFC.SetErrCode(FormulaError::NONE);
+ rFC.SetDirtyVar();
+ }
+ return nullptr;
+ }
+
+ if (aRes.meType == sc::FormulaResultValue::Value)
+ {
+ if (!pNumArray)
+ {
+ rCxt.m_NumArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::NumArrayType>(nArrayLen,
+ std::numeric_limits<double>::quiet_NaN()));
+ pNumArray = rCxt.m_NumArrays.back().get();
+ }
+
+ (*pNumArray)[nPos] = aRes.mfValue;
+ }
+ else
+ {
+ if (!pStrArray)
+ {
+ rCxt.m_StrArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::StrArrayType>(nArrayLen, nullptr));
+ pStrArray = rCxt.m_StrArrays.back().get();
+ }
+
+ (*pStrArray)[nPos] = aRes.maString.getData();
+ }
+ }
+
+ if (!pNumArray && !pStrArray)
+ // At least one of these arrays should be allocated.
+ return nullptr;
+
+ return rCxt.setCachedColArray(nTab, nCol, pNumArray, pStrArray);
+}
+
+struct NonNullStringFinder
+{
+ bool operator() (const rtl_uString* p) const { return p != nullptr; }
+};
+
+bool hasNonEmpty( const sc::FormulaGroupContext::StrArrayType& rArray, SCROW nRow1, SCROW nRow2 )
+{
+ // The caller has to make sure the array is at least nRow2+1 long.
+ sc::FormulaGroupContext::StrArrayType::const_iterator it = rArray.begin();
+ std::advance(it, nRow1);
+ sc::FormulaGroupContext::StrArrayType::const_iterator itEnd = it;
+ std::advance(itEnd, nRow2-nRow1+1);
+ return std::any_of(it, itEnd, NonNullStringFinder());
+}
+
+struct ProtectFormulaGroupContext
+{
+ ProtectFormulaGroupContext( ScDocument* d )
+ : doc( d ) { doc->BlockFormulaGroupContextDiscard( true ); }
+ ~ProtectFormulaGroupContext()
+ { doc->BlockFormulaGroupContextDiscard( false ); }
+ ScDocument* doc;
+};
+
+}
+
+formula::VectorRefArray ScColumn::FetchVectorRefArray( SCROW nRow1, SCROW nRow2 )
+{
+ if (nRow1 > nRow2)
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+
+ // See if the requested range is already cached.
+ ScDocument& rDocument = GetDoc();
+ sc::FormulaGroupContext& rCxt = *(rDocument.GetFormulaGroupContext());
+ sc::FormulaGroupContext::ColArray* pColArray = rCxt.getCachedColArray(nTab, nCol, nRow2+1);
+ if (pColArray)
+ {
+ const double* pNum = nullptr;
+ if (pColArray->mpNumArray)
+ pNum = &(*pColArray->mpNumArray)[nRow1];
+
+ rtl_uString** pStr = nullptr;
+ if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
+ pStr = &(*pColArray->mpStrArray)[nRow1];
+
+ return formula::VectorRefArray(pNum, pStr);
+ }
+
+ // ScColumn::CellStorageModified() simply discards the entire cache (FormulaGroupContext)
+ // on any modification. However getting cell values may cause this to be called
+ // if interpreting a cell results in a change to it (not just its result though).
+ // So temporarily block the discarding.
+ ProtectFormulaGroupContext protectContext(&GetDoc());
+
+ // We need to fetch all cell values from row 0 to nRow2 for caching purposes.
+ sc::CellStoreType::iterator itBlk = maCells.begin();
+ switch (itBlk->type)
+ {
+ case sc::element_type_numeric:
+ {
+ if (o3tl::make_unsigned(nRow2) < itBlk->size)
+ {
+ // Requested range falls within the first block. No need to cache.
+ const double* p = &sc::numeric_block::at(*itBlk->data, nRow1);
+ return formula::VectorRefArray(p);
+ }
+
+ // Allocate a new array and copy the values to it.
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*itBlk->data);
+ sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*itBlk->data);
+ rCxt.m_NumArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::NumArrayType>(it, itEnd));
+ sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
+ // allocate to the requested length.
+ rArray.resize(nRow2+1, std::numeric_limits<double>::quiet_NaN());
+
+ pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr);
+ if (!pColArray)
+ // Failed to insert a new cached column array.
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+
+ // Fill the remaining array with values from the following blocks.
+ size_t nPos = itBlk->size;
+ ++itBlk;
+ if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
+ {
+ rCxt.discardCachedColArray(nTab, nCol);
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+ }
+
+ rtl_uString** pStr = nullptr;
+ if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
+ pStr = &(*pColArray->mpStrArray)[nRow1];
+
+ return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
+ }
+ break;
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ {
+ rCxt.m_StrArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::StrArrayType>(nRow2+1, nullptr));
+ sc::FormulaGroupContext::StrArrayType& rArray = *rCxt.m_StrArrays.back();
+ pColArray = rCxt.setCachedColArray(nTab, nCol, nullptr, &rArray);
+ if (!pColArray)
+ // Failed to insert a new cached column array.
+ return formula::VectorRefArray();
+
+ if (o3tl::make_unsigned(nRow2) < itBlk->size)
+ {
+ // Requested range falls within the first block.
+ copyFirstStringBlock(rDocument, rArray, nRow2+1, itBlk);
+ return formula::VectorRefArray(&rArray[nRow1]);
+ }
+
+ copyFirstStringBlock(rDocument, rArray, itBlk->size, itBlk);
+
+ // Fill the remaining array with values from the following blocks.
+ size_t nPos = itBlk->size;
+ ++itBlk;
+ if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
+ {
+ rCxt.discardCachedColArray(nTab, nCol);
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+ }
+
+ assert(pColArray->mpStrArray);
+
+ rtl_uString** pStr = nullptr;
+ if (hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
+ pStr = &(*pColArray->mpStrArray)[nRow1];
+
+ if (pColArray->mpNumArray)
+ return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], pStr);
+ else
+ return formula::VectorRefArray(pStr);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ if (o3tl::make_unsigned(nRow2) < itBlk->size)
+ {
+ // Requested length is within a single block, and the data is
+ // not cached.
+ pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
+ if (!pColArray)
+ // Failed to insert a new cached column array.
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+
+ const double* pNum = nullptr;
+ rtl_uString** pStr = nullptr;
+ if (pColArray->mpNumArray)
+ pNum = &(*pColArray->mpNumArray)[nRow1];
+ if (pColArray->mpStrArray)
+ pStr = &(*pColArray->mpStrArray)[nRow1];
+
+ return formula::VectorRefArray(pNum, pStr);
+ }
+
+ pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
+ if (!pColArray)
+ {
+ // Failed to insert a new cached column array.
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+ }
+
+ size_t nPos = itBlk->size;
+ ++itBlk;
+ if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
+ {
+ rCxt.discardCachedColArray(nTab, nCol);
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+ }
+
+ const double* pNum = nullptr;
+ rtl_uString** pStr = nullptr;
+ if (pColArray->mpNumArray)
+ pNum = &(*pColArray->mpNumArray)[nRow1];
+ if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
+ pStr = &(*pColArray->mpStrArray)[nRow1];
+
+ return formula::VectorRefArray(pNum, pStr);
+ }
+ break;
+ case sc::element_type_empty:
+ {
+ // Fill the whole length with NaN's.
+ rCxt.m_NumArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::NumArrayType>(nRow2+1,
+ std::numeric_limits<double>::quiet_NaN()));
+ sc::FormulaGroupContext::NumArrayType& rArray = *rCxt.m_NumArrays.back();
+ pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, nullptr);
+ if (!pColArray)
+ // Failed to insert a new cached column array.
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+
+ if (o3tl::make_unsigned(nRow2) < itBlk->size)
+ return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
+
+ // Fill the remaining array with values from the following blocks.
+ size_t nPos = itBlk->size;
+ ++itBlk;
+ if (!appendToBlock(&rDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
+ {
+ rCxt.discardCachedColArray(nTab, nCol);
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+ }
+
+ if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
+ return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]);
+ else
+ return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
+ }
+ break;
+ default:
+ ;
+ }
+
+ return formula::VectorRefArray(formula::VectorRefArray::Invalid);
+}
+
+#ifdef DBG_UTIL
+static void assertNoInterpretNeededHelper( const sc::CellStoreType::value_type& node,
+ size_t nOffset, size_t nDataSize )
+{
+ switch (node.type)
+ {
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (; it != itEnd; ++it)
+ {
+ const ScFormulaCell* pCell = *it;
+ assert( !pCell->NeedsInterpret());
+ }
+ break;
+ }
+ }
+}
+void ScColumn::AssertNoInterpretNeeded( SCROW nRow1, SCROW nRow2 )
+{
+ assert(nRow2 >= nRow1);
+ sc::ParseBlock( maCells.begin(), maCells, assertNoInterpretNeededHelper, 0, nRow2 );
+}
+#endif
+
+void ScColumn::SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ {
+ // This is not a formula block.
+ assert( false );
+ return;
+ }
+
+ size_t nBlockLen = it->size - aPos.second;
+ if (nBlockLen < nLen)
+ {
+ // Result array is longer than the length of formula cells. Not good.
+ assert( false );
+ return;
+ }
+
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, aPos.second);
+
+ const double* pResEnd = pResults + nLen;
+ for (; pResults != pResEnd; ++pResults, ++itCell)
+ {
+ ScFormulaCell& rCell = **itCell;
+ FormulaError nErr = GetDoubleErrorValue(*pResults);
+ if (nErr != FormulaError::NONE)
+ rCell.SetResultError(nErr);
+ else
+ rCell.SetResultDouble(*pResults);
+ rCell.ResetDirty();
+ rCell.SetChanged(true);
+ }
+}
+
+void ScColumn::CalculateInThread( ScInterpreterContext& rContext, SCROW nRow, size_t nLen, size_t nOffset,
+ unsigned nThisThread, unsigned nThreadsTotal)
+{
+ assert(GetDoc().IsThreadedGroupCalcInProgress());
+
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ {
+ // This is not a formula block.
+ assert( false );
+ return;
+ }
+
+ size_t nBlockLen = it->size - aPos.second;
+ if (nBlockLen < nLen)
+ {
+ // Length is longer than the length of formula cells. Not good.
+ assert( false );
+ return;
+ }
+
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, aPos.second);
+
+ for (size_t i = 0; i < nLen; ++i, ++itCell)
+ {
+ if (nThreadsTotal > 0 && ((i + nOffset) % nThreadsTotal) != nThisThread)
+ continue;
+
+ ScFormulaCell& rCell = **itCell;
+ if (!rCell.NeedsInterpret())
+ continue;
+ // Here we don't call IncInterpretLevel() and DecInterpretLevel() as this call site is
+ // always in a threaded calculation.
+ rCell.InterpretTail(rContext, ScFormulaCell::SCITP_NORMAL);
+ }
+}
+
+void ScColumn::HandleStuffAfterParallelCalculation( SCROW nRow, size_t nLen, ScInterpreter* pInterpreter )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ {
+ // This is not a formula block.
+ assert( false );
+ return;
+ }
+
+ size_t nBlockLen = it->size - aPos.second;
+ if (nBlockLen < nLen)
+ {
+ // Length is longer than the length of formula cells. Not good.
+ assert( false );
+ return;
+ }
+
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, aPos.second);
+
+ for (size_t i = 0; i < nLen; ++i, ++itCell)
+ {
+ ScFormulaCell& rCell = **itCell;
+ rCell.HandleStuffAfterParallelCalculation(pInterpreter);
+ }
+}
+
+void ScColumn::SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat )
+{
+ ApplyAttr(nRow, SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat));
+}
+
+ScFormulaCell * const * ScColumn::GetFormulaCellBlockAddress( SCROW nRow, size_t& rBlockSize ) const
+{
+ if (!GetDoc().ValidRow(nRow))
+ {
+ rBlockSize = 0;
+ return nullptr;
+ }
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ {
+ rBlockSize = 0;
+ return nullptr;
+ }
+
+ if (it->type != sc::element_type_formula)
+ {
+ // Not a formula cell.
+ rBlockSize = 0;
+ return nullptr;
+ }
+
+ rBlockSize = it->size;
+ return &sc::formula_block::at(*it->data, aPos.second);
+}
+
+const ScFormulaCell* ScColumn::FetchFormulaCell( SCROW nRow ) const
+{
+ size_t nBlockSize = 0;
+ ScFormulaCell const * const * pp = GetFormulaCellBlockAddress( nRow, nBlockSize );
+ return pp ? *pp : nullptr;
+}
+
+void ScColumn::FindDataAreaPos(SCROW& rRow, bool bDown) const
+{
+ // If the cell is empty, find the next non-empty cell position. If the
+ // cell is not empty, find the last non-empty cell position in the current
+ // contiguous cell block.
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ // Invalid row.
+ return;
+
+ if (it->type == sc::element_type_empty)
+ {
+ // Current cell is empty. Find the next non-empty cell.
+ rRow = FindNextVisibleRowWithContent(it, rRow, bDown);
+ return;
+ }
+
+ // Current cell is not empty.
+ SCROW nNextRow = FindNextVisibleRow(rRow, bDown);
+ aPos = maCells.position(it, nNextRow);
+ it = aPos.first;
+ if (it->type == sc::element_type_empty)
+ {
+ // Next visible cell is empty. Find the next non-empty cell.
+ rRow = FindNextVisibleRowWithContent(it, nNextRow, bDown);
+ return;
+ }
+
+ // Next visible cell is non-empty. Find the edge that's still visible.
+ SCROW nLastRow = nNextRow;
+ do
+ {
+ nNextRow = FindNextVisibleRow(nLastRow, bDown);
+ if (nNextRow == nLastRow)
+ break;
+
+ aPos = maCells.position(it, nNextRow);
+ it = aPos.first;
+ if (it->type != sc::element_type_empty)
+ nLastRow = nNextRow;
+ }
+ while (it->type != sc::element_type_empty);
+
+ rRow = nLastRow;
+}
+
+bool ScColumn::HasDataAt(SCROW nRow, ScDataAreaExtras* pDataAreaExtras ) const
+{
+ if (pDataAreaExtras)
+ GetDataExtrasAt( nRow, *pDataAreaExtras);
+
+ return maCells.get_type(nRow) != sc::element_type_empty;
+}
+
+bool ScColumn::HasDataAt( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow,
+ ScDataAreaExtras* pDataAreaExtras ) const
+{
+ if (pDataAreaExtras)
+ GetDataExtrasAt( nRow, *pDataAreaExtras);
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
+ if (aPos.first == maCells.end())
+ return false;
+ rBlockPos.miCellPos = aPos.first; // Store this for next call.
+ return aPos.first->type != sc::element_type_empty;
+}
+
+bool ScColumn::HasDataAt( sc::ColumnBlockPosition& rBlockPos, SCROW nRow,
+ ScDataAreaExtras* pDataAreaExtras )
+{
+ if (pDataAreaExtras)
+ GetDataExtrasAt( nRow, *pDataAreaExtras);
+
+ std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
+ if (aPos.first == maCells.end())
+ return false;
+ rBlockPos.miCellPos = aPos.first; // Store this for next call.
+ return aPos.first->type != sc::element_type_empty;
+}
+
+void ScColumn::GetDataExtrasAt( SCROW nRow, ScDataAreaExtras& rDataAreaExtras ) const
+{
+ if (rDataAreaExtras.mnStartRow <= nRow && nRow <= rDataAreaExtras.mnEndRow)
+ return;
+
+ // Check in order of likeliness.
+ if ( (rDataAreaExtras.mbCellFormats && HasVisibleAttrIn(nRow, nRow)) ||
+ (rDataAreaExtras.mbCellNotes && !IsNotesEmptyBlock(nRow, nRow)) ||
+ (rDataAreaExtras.mbCellDrawObjects && !IsDrawObjectsEmptyBlock(nRow, nRow)))
+ {
+ if (rDataAreaExtras.mnStartRow > nRow)
+ rDataAreaExtras.mnStartRow = nRow;
+ if (rDataAreaExtras.mnEndRow < nRow)
+ rDataAreaExtras.mnEndRow = nRow;
+ }
+}
+
+namespace {
+
+class FindUsedRowsHandler
+{
+ typedef mdds::flat_segment_tree<SCROW,bool> UsedRowsType;
+ UsedRowsType& mrUsed;
+ UsedRowsType::const_iterator miUsed;
+public:
+ explicit FindUsedRowsHandler(UsedRowsType& rUsed) : mrUsed(rUsed), miUsed(rUsed.begin()) {}
+
+ void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ if (node.type == sc::element_type_empty)
+ return;
+
+ SCROW nRow1 = node.position + nOffset;
+ SCROW nRow2 = nRow1 + nDataSize - 1;
+ miUsed = mrUsed.insert(miUsed, nRow1, nRow2+1, true).first;
+ }
+};
+
+}
+
+void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW,bool>& rUsed ) const
+{
+ FindUsedRowsHandler aFunc(rUsed);
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
+}
+
+namespace {
+
+void startListening(
+ sc::BroadcasterStoreType& rStore, sc::BroadcasterStoreType::iterator& itBlockPos, size_t nElemPos,
+ SCROW nRow, SvtListener& rLst)
+{
+ switch (itBlockPos->type)
+ {
+ case sc::element_type_broadcaster:
+ {
+ // Broadcaster already exists here.
+ SvtBroadcaster* pBC = sc::broadcaster_block::at(*itBlockPos->data, nElemPos);
+ rLst.StartListening(*pBC);
+ }
+ break;
+ case mdds::mtv::element_type_empty:
+ {
+ // No broadcaster exists at this position yet.
+ SvtBroadcaster* pBC = new SvtBroadcaster;
+ rLst.StartListening(*pBC);
+ itBlockPos = rStore.set(itBlockPos, nRow, pBC); // Store the block position for next iteration.
+ }
+ break;
+ default:
+ assert(false && "wrong block type encountered in the broadcaster storage.");
+ }
+}
+
+}
+
+void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
+{
+ std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(nRow);
+ startListening(maBroadcasters, aPos.first, aPos.second, nRow, rLst);
+}
+
+void ScColumn::EndListening( SvtListener& rLst, SCROW nRow )
+{
+ SvtBroadcaster* pBC = GetBroadcaster(nRow);
+ if (!pBC)
+ return;
+
+ rLst.EndListening(*pBC);
+ if (!pBC->HasListeners())
+ { // There is no more listeners for this cell. Remove the broadcaster.
+ if(GetDoc().IsDelayedDeletingBroadcasters())
+ mbEmptyBroadcastersPending = true;
+ else
+ maBroadcasters.set_empty(nRow, nRow);
+ }
+}
+
+void ScColumn::StartListening( sc::StartListeningContext& rCxt, const ScAddress& rAddress, SvtListener& rLst )
+{
+ if (!GetDoc().ValidRow(rAddress.Row()))
+ return;
+
+ sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
+ if (!p)
+ return;
+
+ sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
+ std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
+ it = aPos.first; // store the block position for next iteration.
+ startListening(maBroadcasters, it, aPos.second, rAddress.Row(), rLst);
+}
+
+void ScColumn::EndListening( sc::EndListeningContext& rCxt, const ScAddress& rAddress, SvtListener& rListener )
+{
+ sc::ColumnBlockPosition* p = rCxt.getBlockPosition(rAddress.Tab(), rAddress.Col());
+ if (!p)
+ return;
+
+ sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
+ std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, rAddress.Row());
+ it = aPos.first; // store the block position for next iteration.
+ if (it->type != sc::element_type_broadcaster)
+ return;
+
+ SvtBroadcaster* pBC = sc::broadcaster_block::at(*it->data, aPos.second);
+ assert(pBC);
+
+ rListener.EndListening(*pBC);
+ if (!pBC->HasListeners())
+ // There is no more listeners for this cell. Add it to the purge list for later purging.
+ rCxt.addEmptyBroadcasterPosition(rAddress.Tab(), rAddress.Col(), rAddress.Row());
+}
+
+namespace {
+
+class CompileDBFormulaHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+
+public:
+ explicit CompileDBFormulaHandler( sc::CompileFormulaContext& rCxt ) :
+ mrCxt(rCxt) {}
+
+ void operator() (size_t, ScFormulaCell* p)
+ {
+ p->CompileDBFormula(mrCxt);
+ }
+};
+
+struct CompileColRowNameFormulaHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+public:
+ explicit CompileColRowNameFormulaHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
+
+ void operator() (size_t, ScFormulaCell* p)
+ {
+ p->CompileColRowNameFormula(mrCxt);
+ }
+};
+
+}
+
+void ScColumn::CompileDBFormula( sc::CompileFormulaContext& rCxt )
+{
+ CompileDBFormulaHandler aFunc(rCxt);
+ sc::ProcessFormula(maCells, aFunc);
+ RegroupFormulaCells();
+}
+
+void ScColumn::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
+{
+ CompileColRowNameFormulaHandler aFunc(rCxt);
+ sc::ProcessFormula(maCells, aFunc);
+ RegroupFormulaCells();
+}
+
+namespace {
+
+class UpdateSubTotalHandler
+{
+ ScFunctionData& mrData;
+
+ void update(double fVal, bool bVal)
+ {
+ if (mrData.getError())
+ return;
+
+ switch (mrData.getFunc())
+ {
+ case SUBTOTAL_FUNC_CNT2: // everything
+ mrData.update( fVal);
+ break;
+ default: // only numeric values
+ if (bVal)
+ mrData.update( fVal);
+ }
+ }
+
+public:
+ explicit UpdateSubTotalHandler(ScFunctionData& rData) : mrData(rData) {}
+
+ void operator() (size_t /*nRow*/, double fVal)
+ {
+ update(fVal, true);
+ }
+
+ void operator() (size_t /*nRow*/, const svl::SharedString&)
+ {
+ update(0.0, false);
+ }
+
+ void operator() (size_t /*nRow*/, const EditTextObject*)
+ {
+ update(0.0, false);
+ }
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ double fVal = 0.0;
+ bool bVal = false;
+ if (mrData.getFunc() != SUBTOTAL_FUNC_CNT2) // it doesn't interest us
+ {
+
+ if (pCell->GetErrCode() != FormulaError::NONE)
+ {
+ if (mrData.getFunc() != SUBTOTAL_FUNC_CNT) // simply remove from count
+ mrData.setError();
+ }
+ else if (pCell->IsValue())
+ {
+ fVal = pCell->GetValue();
+ bVal = true;
+ }
+ // otherwise text
+ }
+
+ update(fVal, bVal);
+ }
+};
+
+}
+
+// multiple selections:
+void ScColumn::UpdateSelectionFunction(
+ const ScRangeList& rRanges, ScFunctionData& rData, const ScFlatBoolRowSegments& rHiddenRows )
+{
+ sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
+ aSpanSet.scan(rRanges, nTab, nCol); // mark all selected rows.
+
+ if (aSpanSet.empty())
+ return; // nothing to do, bail out
+
+ // Exclude all hidden rows.
+ ScFlatBoolRowSegments::RangeData aRange;
+ SCROW nRow = 0;
+ while (nRow <= GetDoc().MaxRow())
+ {
+ if (!rHiddenRows.getRangeData(nRow, aRange))
+ break;
+
+ if (aRange.mbValue)
+ // Hidden range detected.
+ aSpanSet.set(nRow, aRange.mnRow2, false);
+
+ nRow = aRange.mnRow2 + 1;
+ }
+
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ aSpanSet.getSpans(aSpans);
+
+ switch (rData.getFunc())
+ {
+ case SUBTOTAL_FUNC_SELECTION_COUNT:
+ {
+ // Simply count selected rows regardless of cell contents.
+ for (const auto& rSpan : aSpans)
+ rData.update( rSpan.mnRow2 - rSpan.mnRow1 + 1);
+ }
+ break;
+ case SUBTOTAL_FUNC_CNT2:
+ {
+ // We need to parse all non-empty cells.
+ sc::CellStoreType::const_iterator itCellPos = maCells.begin();
+ UpdateSubTotalHandler aFunc(rData);
+ for (const auto& rSpan : aSpans)
+ {
+ itCellPos = sc::ParseAllNonEmpty(
+ itCellPos, maCells, rSpan.mnRow1, rSpan.mnRow2, aFunc);
+ }
+ }
+ break;
+ default:
+ {
+ // We need to parse only numeric values.
+ sc::CellStoreType::const_iterator itCellPos = maCells.begin();
+ UpdateSubTotalHandler aFunc(rData);
+ for (const auto& rSpan : aSpans)
+ {
+ itCellPos = sc::ParseFormulaNumeric(
+ itCellPos, maCells, rSpan.mnRow1, rSpan.mnRow2, aFunc);
+ }
+ }
+ }
+}
+
+namespace {
+
+class WeightedCounter
+{
+ sal_uLong mnCount;
+public:
+ WeightedCounter() : mnCount(0) {}
+
+ void operator() (const sc::CellStoreType::value_type& node)
+ {
+ mnCount += getWeight(node);
+ }
+
+ static sal_uLong getWeight(const sc::CellStoreType::value_type& node)
+ {
+ switch (node.type)
+ {
+ case sc::element_type_numeric:
+ case sc::element_type_string:
+ return node.size;
+ case sc::element_type_formula:
+ {
+ // Each formula cell is worth its code length plus 5.
+ return std::accumulate(sc::formula_block::begin(*node.data), sc::formula_block::end(*node.data), size_t(0),
+ [](const size_t& rCount, const ScFormulaCell* p) { return rCount + 5 + p->GetCode()->GetCodeLen(); });
+ }
+ case sc::element_type_edittext:
+ // each edit-text cell is worth 50.
+ return node.size * 50;
+ default:
+ return 0;
+ }
+ }
+
+ sal_uLong getCount() const { return mnCount; }
+};
+
+class WeightedCounterWithRows
+{
+ const SCROW mnStartRow;
+ const SCROW mnEndRow;
+ sal_uLong mnCount;
+
+public:
+ WeightedCounterWithRows(SCROW nStartRow, SCROW nEndRow)
+ : mnStartRow(nStartRow)
+ , mnEndRow(nEndRow)
+ , mnCount(0)
+ {
+ }
+
+ void operator() (const sc::CellStoreType::value_type& node)
+ {
+ const SCROW nRow1 = node.position;
+ const SCROW nRow2 = nRow1 + 1;
+
+ if ((nRow2 >= mnStartRow) && (nRow1 <= mnEndRow))
+ {
+ mnCount += WeightedCounter::getWeight(node);
+ }
+ }
+
+ sal_uLong getCount() const { return mnCount; }
+};
+
+}
+
+sal_uInt64 ScColumn::GetWeightedCount() const
+{
+ const WeightedCounter aFunc = std::for_each(maCells.begin(), maCells.end(),
+ WeightedCounter());
+ return aFunc.getCount();
+}
+
+sal_uInt64 ScColumn::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
+{
+ const WeightedCounterWithRows aFunc = std::for_each(maCells.begin(), maCells.end(),
+ WeightedCounterWithRows(nStartRow, nEndRow));
+ return aFunc.getCount();
+}
+
+namespace {
+
+class CodeCounter
+{
+ sal_uInt64 mnCount;
+public:
+ CodeCounter() : mnCount(0) {}
+
+ void operator() (size_t, const ScFormulaCell* p)
+ {
+ mnCount += p->GetCode()->GetCodeLen();
+ }
+
+ sal_uInt64 getCount() const { return mnCount; }
+};
+
+}
+
+sal_uInt64 ScColumn::GetCodeCount() const
+{
+ CodeCounter aFunc;
+ sc::ParseFormula(maCells, aFunc);
+ return aFunc.getCount();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
new file mode 100644
index 0000000000..a0b0c639a0
--- /dev/null
+++ b/sc/source/core/data/column3.cxx
@@ -0,0 +1,3726 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <column.hxx>
+
+#include <scitems.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <cellform.hxx>
+#include <typedstrdata.hxx>
+#include <formula/errorcodes.hxx>
+#include <formula/token.hxx>
+#include <brdcst.hxx>
+#include <docoptio.hxx>
+#include <subtotal.hxx>
+#include <markdata.hxx>
+#include <stringutil.hxx>
+#include <docpool.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <clipcontext.hxx>
+#include <columnspanset.hxx>
+#include <mtvcellfunc.hxx>
+#include <scopetools.hxx>
+#include <editutil.hxx>
+#include <sharedformula.hxx>
+#include <listenercontext.hxx>
+#include <filterentries.hxx>
+#include <conditio.hxx>
+#include <colorscale.hxx>
+#include <table.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+
+#include <com/sun/star/i18n/LocaleDataItem2.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <memory>
+
+#include <o3tl/deleter.hxx>
+
+#include <rtl/tencinfo.h>
+
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <osl/diagnose.h>
+
+#include <cstdio>
+#include <refdata.hxx>
+
+using ::com::sun::star::i18n::LocaleDataItem2;
+
+using namespace formula;
+
+void ScColumn::Broadcast( SCROW nRow )
+{
+ ScHint aHint(SfxHintId::ScDataChanged, ScAddress(nCol, nRow, nTab));
+ GetDoc().Broadcast(aHint);
+}
+
+void ScColumn::BroadcastCells( const std::vector<SCROW>& rRows, SfxHintId nHint )
+{
+ if (rRows.empty())
+ return;
+
+ // Broadcast the changes.
+ ScDocument& rDocument = GetDoc();
+ ScHint aHint(nHint, ScAddress(nCol, 0, nTab));
+ for (const auto& rRow : rRows)
+ {
+ aHint.SetAddressRow(rRow);
+ rDocument.Broadcast(aHint);
+ }
+}
+
+void ScColumn::BroadcastRows( SCROW nStartRow, SCROW nEndRow, SfxHintId nHint )
+{
+ if( nStartRow > GetLastDataPos())
+ return;
+ sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
+ aSpanSet.scan(*this, nStartRow, nEndRow);
+ std::vector<SCROW> aRows;
+ aSpanSet.getRows(aRows);
+ BroadcastCells(aRows, nHint);
+}
+
+namespace {
+
+class CellInterpreterBase
+{
+protected:
+ void Interpret(ScFormulaCell* p)
+ {
+ // Interpret() takes a range in a formula group, so group those together.
+ if( !groupCells.empty() && p->GetCellGroup() == groupCells.back()->GetCellGroup()
+ && p->aPos.Row() == groupCells.back()->aPos.Row() + 1 )
+ {
+ assert( p->aPos.Tab() == groupCells.back()->aPos.Tab()
+ && p->aPos.Col() == groupCells.back()->aPos.Col());
+ groupCells.push_back(p); // Extend range.
+ return;
+ }
+ flushPending();
+ if( !p->GetCellGroup())
+ {
+ p->Interpret();
+ return;
+ }
+ groupCells.push_back(p);
+
+ }
+ ~CellInterpreterBase()
+ {
+ suppress_fun_call_w_exception(flushPending());
+ }
+private:
+ void flushPending()
+ {
+ if(groupCells.empty())
+ return;
+ SCROW firstRow = groupCells.front()->GetCellGroup()->mpTopCell->aPos.Row();
+ if(!groupCells.front()->Interpret(
+ groupCells.front()->aPos.Row() - firstRow, groupCells.back()->aPos.Row() - firstRow))
+ {
+ // Interpret() will try to group-interpret the given cell range if possible, but if that
+ // is not possible, it will interpret just the given cell. So if group-interpreting
+ // wasn't possible, interpret them one by one.
+ for(ScFormulaCell* cell : groupCells)
+ cell->Interpret();
+ }
+ groupCells.clear();
+ }
+ std::vector<ScFormulaCell*> groupCells;
+};
+
+class DirtyCellInterpreter : public CellInterpreterBase
+{
+public:
+ void operator() (size_t, ScFormulaCell* p)
+ {
+ if(p->GetDirty())
+ Interpret(p);
+ }
+};
+
+class NeedsInterpretCellInterpreter : public CellInterpreterBase
+{
+public:
+ void operator() (size_t, ScFormulaCell* p)
+ {
+ if(p->NeedsInterpret())
+ {
+ Interpret(p);
+ // In some cases such as circular dependencies Interpret()
+ // will not reset the dirty flag, check that in order to tell
+ // the caller that the cell range may trigger Interpret() again.
+ if(p->NeedsInterpret())
+ allInterpreted = false;
+ }
+ }
+ bool allInterpreted = true;
+};
+
+}
+
+void ScColumn::InterpretDirtyCells( SCROW nRow1, SCROW nRow2 )
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return;
+
+ DirtyCellInterpreter aFunc;
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+}
+
+bool ScColumn::InterpretCellsIfNeeded( SCROW nRow1, SCROW nRow2 )
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return false;
+
+ NeedsInterpretCellInterpreter aFunc;
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+ return aFunc.allInterpreted;
+}
+
+void ScColumn::DeleteContent( SCROW nRow, bool bBroadcast )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it == maCells.end())
+ return;
+
+ if (it->type == sc::element_type_formula)
+ {
+ ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ p->EndListeningTo(GetDoc());
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *p);
+ }
+ maCells.set_empty(nRow, nRow);
+
+ if (bBroadcast)
+ {
+ Broadcast(nRow);
+ CellStorageModified();
+ }
+}
+
+void ScColumn::Delete( SCROW nRow )
+{
+ DeleteContent(nRow, false);
+ maCellTextAttrs.set_empty(nRow, nRow);
+ maCellNotes.set_empty(nRow, nRow);
+ maSparklines.set_empty(nRow, nRow);
+
+ Broadcast(nRow);
+ CellStorageModified();
+}
+
+void ScColumn::FreeAll()
+{
+ maCells.event_handler().stop();
+
+ auto maxRowCount = GetDoc().GetMaxRowCount();
+ // Keep a logical empty range of 0-rDoc.MaxRow() at all times.
+ maCells.clear();
+ maCells.resize(maxRowCount);
+ maCellTextAttrs.clear();
+ maCellTextAttrs.resize(maxRowCount);
+ maCellNotes.clear();
+ maCellNotes.resize(maxRowCount);
+ maSparklines.clear();
+ maSparklines.resize(maxRowCount);
+ CellStorageModified();
+}
+
+void ScColumn::FreeNotes()
+{
+ maCellNotes.clear();
+ maCellNotes.resize(GetDoc().GetMaxRowCount());
+}
+
+namespace {
+
+class ShiftFormulaPosHandler
+{
+public:
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ pCell->aPos.SetRow(nRow);
+ }
+};
+
+}
+
+void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize, std::vector<ScAddress>* pGroupPos )
+{
+ pAttrArray->DeleteRow( nStartRow, nSize );
+
+ SCROW nEndRow = nStartRow + nSize - 1;
+
+ maBroadcasters.erase(nStartRow, nEndRow);
+ maBroadcasters.resize(GetDoc().GetMaxRowCount());
+
+ CellNotesDeleting(nStartRow, nEndRow, false);
+ maCellNotes.erase(nStartRow, nEndRow);
+ maCellNotes.resize(GetDoc().GetMaxRowCount());
+
+ // See if we have any cells that would get deleted or shifted by deletion.
+ sc::CellStoreType::position_type aPos = maCells.position(nStartRow);
+ sc::CellStoreType::iterator itCell = aPos.first;
+ if (itCell->type == sc::element_type_empty)
+ {
+ // This is an empty block. If this is the last block, then there is no cells to delete or shift.
+ sc::CellStoreType::iterator itTest = itCell;
+ ++itTest;
+ if (itTest == maCells.end())
+ {
+ // No cells are affected by this deletion. Bail out.
+ CellStorageModified(); // broadcast array has been modified.
+ return;
+ }
+ }
+
+ // Check if there are any cells below the end row that will get shifted.
+ bool bShiftCells = false;
+ if (nEndRow < GetDoc().MaxRow()) //only makes sense to do this if there *is* a row after the end row
+ {
+ aPos = maCells.position(itCell, nEndRow+1);
+ itCell = aPos.first;
+ if (itCell->type == sc::element_type_empty)
+ {
+ // This block is empty. See if there is any block that follows.
+ sc::CellStoreType::iterator itTest = itCell;
+ ++itTest;
+ if (itTest != maCells.end())
+ // Non-empty block follows -> cells that will get shifted.
+ bShiftCells = true;
+ }
+ else
+ bShiftCells = true;
+ }
+
+ sc::SingleColumnSpanSet aNonEmptySpans(GetDoc().GetSheetLimits());
+ if (bShiftCells)
+ {
+ // Mark all non-empty cell positions below the end row.
+ sc::ColumnBlockConstPosition aBlockPos;
+ aBlockPos.miCellPos = itCell;
+ aNonEmptySpans.scan(aBlockPos, *this, nEndRow+1, GetDoc().MaxRow());
+ }
+
+ sc::AutoCalcSwitch aACSwitch(GetDoc(), false);
+
+ // Remove the cells.
+ maCells.erase(nStartRow, nEndRow);
+ maCells.resize(GetDoc().GetMaxRowCount());
+
+ // Get the position again after the container change.
+ aPos = maCells.position(nStartRow);
+
+ // Shift the formula cell positions below the start row.
+ ShiftFormulaPosHandler aShiftFormulaFunc;
+ sc::ProcessFormula(aPos.first, maCells, nStartRow, GetDoc().MaxRow(), aShiftFormulaFunc);
+
+ bool bJoined = sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (bJoined && pGroupPos)
+ pGroupPos->push_back(ScAddress(nCol, nStartRow, nTab));
+
+ // Shift the text attribute array too (before the broadcast).
+ maCellTextAttrs.erase(nStartRow, nEndRow);
+ maCellTextAttrs.resize(GetDoc().GetMaxRowCount());
+
+ CellStorageModified();
+}
+
+sc::CellStoreType::iterator ScColumn::GetPositionToInsert( SCROW nRow, std::vector<SCROW>& rNewSharedRows,
+ bool bInsertFormula )
+{
+ return GetPositionToInsert(maCells.begin(), nRow, rNewSharedRows, bInsertFormula);
+}
+
+void ScColumn::JoinNewFormulaCell(
+ const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell )
+{
+ // Check the previous row position for possible grouping.
+ if (aPos.first->type == sc::element_type_formula && aPos.second > 0)
+ {
+ ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
+ sc::CellStoreType::position_type aPosPrev = aPos;
+ --aPosPrev.second;
+ sc::SharedFormulaUtil::joinFormulaCells(aPosPrev, rPrev, rCell);
+ }
+
+ // Check the next row position for possible grouping.
+ if (aPos.first->type == sc::element_type_formula && aPos.second+1 < aPos.first->size)
+ {
+ ScFormulaCell& rNext = *sc::formula_block::at(*aPos.first->data, aPos.second+1);
+ sc::SharedFormulaUtil::joinFormulaCells(aPos, rCell, rNext);
+ }
+}
+
+void ScColumn::DetachFormulaCell(
+ const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, std::vector<SCROW>& rNewSharedRows )
+{
+ if (!GetDoc().IsClipOrUndo())
+ {
+#if USE_FORMULA_GROUP_LISTENER
+ if (rCell.IsShared() && rCell.GetSharedLength() > 1)
+ {
+ // Record new spans (shared or remaining single) that will result
+ // from unsharing to reestablish listeners.
+ // Same cases as in unshareFormulaCell().
+ // XXX NOTE: this is not part of unshareFormulaCell() because that
+ // is called in other contexts as well, for which passing and
+ // determining the rows vector would be superfluous. If that was
+ // needed, move it there.
+ const SCROW nSharedTopRow = rCell.GetSharedTopRow();
+ const SCROW nSharedLength = rCell.GetSharedLength();
+ if (rCell.aPos.Row() == nSharedTopRow)
+ {
+ // Top cell.
+ // Next row will be new shared top or single cell.
+ rNewSharedRows.push_back( nSharedTopRow + 1);
+ rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1);
+ }
+ else if (rCell.aPos.Row() == nSharedTopRow + nSharedLength - 1)
+ {
+ // Bottom cell.
+ // Current shared top row will be new shared top again or
+ // single cell.
+ rNewSharedRows.push_back( nSharedTopRow);
+ rNewSharedRows.push_back( rCell.aPos.Row() - 1);
+ }
+ else
+ {
+ // Some mid cell.
+ // Current shared top row will be new shared top again or
+ // single cell, plus a new shared top below or single cell.
+ rNewSharedRows.push_back( nSharedTopRow);
+ rNewSharedRows.push_back( rCell.aPos.Row() - 1);
+ rNewSharedRows.push_back( rCell.aPos.Row() + 1);
+ rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1);
+ }
+ }
+#endif
+
+ // Have the dying formula cell stop listening.
+ // If in a shared formula group this ends the group listening.
+ rCell.EndListeningTo(GetDoc());
+ }
+
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, rCell);
+}
+
+void ScColumn::StartListeningUnshared( const std::vector<SCROW>& rNewSharedRows )
+{
+ assert(rNewSharedRows.empty() || rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4);
+ ScDocument& rDoc = GetDoc();
+ if (rNewSharedRows.empty() || rDoc.IsDelayedFormulaGrouping())
+ return;
+
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(rDoc);
+ sc::StartListeningContext aStartCxt(rDoc, pPosSet);
+ sc::EndListeningContext aEndCxt(rDoc, pPosSet);
+ if (rNewSharedRows.size() >= 2)
+ {
+ if(!rDoc.CanDelayStartListeningFormulaCells( this, rNewSharedRows[0], rNewSharedRows[1]))
+ StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[0], rNewSharedRows[1]);
+ }
+ if (rNewSharedRows.size() >= 4)
+ {
+ if(!rDoc.CanDelayStartListeningFormulaCells( this, rNewSharedRows[2], rNewSharedRows[3]))
+ StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[2], rNewSharedRows[3]);
+ }
+}
+
+namespace {
+
+class AttachFormulaCellsHandler
+{
+ sc::StartListeningContext& mrCxt;
+
+public:
+ explicit AttachFormulaCellsHandler(sc::StartListeningContext& rCxt)
+ : mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->StartListeningTo(mrCxt);
+ }
+};
+
+class DetachFormulaCellsHandler
+{
+ ScDocument& mrDoc;
+ sc::EndListeningContext* mpCxt;
+
+public:
+ DetachFormulaCellsHandler( ScDocument& rDoc, sc::EndListeningContext* pCxt ) :
+ mrDoc(rDoc), mpCxt(pCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ if (mpCxt)
+ pCell->EndListeningTo(*mpCxt);
+ else
+ pCell->EndListeningTo(mrDoc);
+ }
+};
+
+}
+
+void ScColumn::DetachFormulaCells(
+ const sc::CellStoreType::position_type& aPos, size_t nLength, std::vector<SCROW>* pNewSharedRows )
+{
+ const size_t nRow = aPos.first->position + aPos.second;
+ const size_t nNextTopRow = nRow + nLength; // start row of next formula group.
+
+ bool bLowerSplitOff = false;
+ if (pNewSharedRows && !GetDoc().IsClipOrUndo())
+ {
+ const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos);
+ if (pFC)
+ {
+ const SCROW nTopRow = pFC->GetSharedTopRow();
+ const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1;
+ // nTopRow <= nRow <= nBotRow, because otherwise pFC would not exist.
+ if (nTopRow < static_cast<SCROW>(nRow))
+ {
+ // Upper part will be split off.
+ pNewSharedRows->push_back(nTopRow);
+ pNewSharedRows->push_back(nRow - 1);
+ }
+ if (static_cast<SCROW>(nNextTopRow) <= nBotRow)
+ {
+ // Lower part will be split off.
+ pNewSharedRows->push_back(nNextTopRow);
+ pNewSharedRows->push_back(nBotRow);
+ bLowerSplitOff = true;
+ }
+ }
+ }
+
+ // Split formula grouping at the top and bottom boundaries.
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+
+ if (nLength > 0 && GetDoc().ValidRow(nNextTopRow))
+ {
+ if (pNewSharedRows && !bLowerSplitOff && !GetDoc().IsClipOrUndo())
+ {
+ sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow-1);
+ const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos2);
+ if (pFC)
+ {
+ const SCROW nTopRow = pFC->GetSharedTopRow();
+ const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1;
+ // nRow < nTopRow < nNextTopRow <= nBotRow
+ if (static_cast<SCROW>(nNextTopRow) <= nBotRow)
+ {
+ // Lower part will be split off.
+ pNewSharedRows->push_back(nNextTopRow);
+ pNewSharedRows->push_back(nBotRow);
+ }
+ }
+ }
+
+ sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos2, nullptr);
+ }
+
+ if (GetDoc().IsClipOrUndo())
+ return;
+
+ DetachFormulaCellsHandler aFunc(GetDoc(), nullptr);
+ sc::ProcessFormula(aPos.first, maCells, nRow, nNextTopRow-1, aFunc);
+}
+
+void ScColumn::AttachFormulaCells( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow1);
+ sc::CellStoreType::iterator it = aPos.first;
+
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (GetDoc().ValidRow(nRow2+1))
+ {
+ aPos = maCells.position(it, nRow2+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+
+ if (GetDoc().IsClipOrUndo())
+ return;
+
+ // Need to process (start listening) entire shared formula groups, not just
+ // a slice thereof.
+ bool bEnlargedDown = false;
+ aPos = maCells.position(nRow1);
+ it = aPos.first;
+ if (it->type == sc::element_type_formula)
+ {
+ ScFormulaCell& rCell = *sc::formula_block::at(*it->data, aPos.second);
+ if (rCell.IsShared())
+ {
+ nRow1 = std::min( nRow1, rCell.GetSharedTopRow());
+ if (nRow2 < rCell.GetSharedTopRow() + rCell.GetSharedLength())
+ {
+ nRow2 = rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1;
+ bEnlargedDown = true;
+ // Same end row is also enlarged, i.e. doesn't need to be
+ // checked for another group.
+ }
+ }
+ }
+ if (!bEnlargedDown)
+ {
+ aPos = maCells.position(it, nRow2);
+ it = aPos.first;
+ if (it->type == sc::element_type_formula)
+ {
+ ScFormulaCell& rCell = *sc::formula_block::at(*it->data, aPos.second);
+ if (rCell.IsShared())
+ {
+ nRow2 = std::max( nRow2, rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1);
+ }
+ }
+ }
+
+ AttachFormulaCellsHandler aFunc(rCxt);
+ sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2 )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow1);
+ sc::CellStoreType::iterator it = aPos.first;
+
+ // Split formula grouping at the top and bottom boundaries.
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt);
+ if (GetDoc().ValidRow(nRow2+1))
+ {
+ aPos = maCells.position(it, nRow2+1);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt);
+ }
+
+ if (GetDoc().IsClipOrUndo())
+ return;
+
+ DetachFormulaCellsHandler aFunc(GetDoc(), &rCxt);
+ sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc);
+}
+
+static void lcl_AddFormulaGroupBoundaries(const sc::CellStoreType::position_type& rPos,
+ std::vector<SCROW>& rNewSharedRows )
+{
+ sc::CellStoreType::iterator itRet = rPos.first;
+ if (itRet->type != sc::element_type_formula)
+ return;
+
+ ScFormulaCell& rFC = *sc::formula_block::at(*itRet->data, rPos.second);
+ if ( rFC.IsShared() )
+ {
+ const SCROW nSharedTopRow = rFC.GetSharedTopRow();
+ const SCROW nSharedLength = rFC.GetSharedLength();
+ rNewSharedRows.push_back( nSharedTopRow);
+ rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1);
+ }
+ else
+ {
+ const SCROW nRow = rFC.aPos.Row();
+ rNewSharedRows.push_back( nRow);
+ rNewSharedRows.push_back( nRow);
+ }
+}
+
+sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow,
+ std::vector<SCROW>& rNewSharedRows, bool bInsertFormula )
+{
+ // See if we are overwriting an existing formula cell.
+ sc::CellStoreType::position_type aPos = maCells.position(it, nRow);
+ sc::CellStoreType::iterator itRet = aPos.first;
+
+ if (itRet->type == sc::element_type_formula)
+ {
+ ScFormulaCell& rCell = *sc::formula_block::at(*itRet->data, aPos.second);
+ DetachFormulaCell(aPos, rCell, rNewSharedRows);
+ }
+ else if (bInsertFormula && !GetDoc().IsClipOrUndo())
+ {
+ if (nRow > 0)
+ {
+ sc::CellStoreType::position_type aPosBefore = maCells.position(maCells.begin(), nRow-1);
+ lcl_AddFormulaGroupBoundaries(aPosBefore, rNewSharedRows);
+ }
+ if (nRow < GetDoc().MaxRow())
+ {
+ sc::CellStoreType::position_type aPosAfter = maCells.position(maCells.begin(), nRow+1);
+ lcl_AddFormulaGroupBoundaries(aPosAfter, rNewSharedRows);
+ }
+ }
+
+ return itRet;
+}
+
+void ScColumn::AttachNewFormulaCell(
+ const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell,
+ const std::vector<SCROW>& rNewSharedRows,
+ bool bJoin, sc::StartListeningType eListenType )
+{
+ AttachNewFormulaCell(maCells.position(itPos, nRow), rCell, rNewSharedRows, bJoin, eListenType);
+}
+
+void ScColumn::AttachNewFormulaCell(
+ const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell,
+ const std::vector<SCROW>& rNewSharedRows,
+ bool bJoin, sc::StartListeningType eListenType )
+{
+ if (bJoin)
+ // See if this new formula cell can join an existing shared formula group.
+ JoinNewFormulaCell(aPos, rCell);
+
+ // When we insert from the Clipboard we still have wrong (old) References!
+ // First they are rewired in CopyBlockFromClip via UpdateReference and the
+ // we call StartListeningFromClip and BroadcastFromClip.
+ // If we insert into the Clipboard/andoDoc, we do not use a Broadcast.
+ // After Import we call CalcAfterLoad and in there Listening.
+ ScDocument& rDocument = GetDoc();
+ if (rDocument.IsClipOrUndo() || rDocument.IsInsertingFromOtherDoc())
+ return;
+
+ switch (eListenType)
+ {
+ case sc::ConvertToGroupListening:
+ {
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(rDocument);
+ sc::StartListeningContext aStartCxt(rDocument, pPosSet);
+ sc::EndListeningContext aEndCxt(rDocument, pPosSet);
+ SCROW nStartRow, nEndRow;
+ nStartRow = nEndRow = aPos.first->position + aPos.second;
+ for (const SCROW nR : rNewSharedRows)
+ {
+ if (nStartRow > nR)
+ nStartRow = nR;
+ if (nEndRow < nR)
+ nEndRow = nR;
+ }
+ StartListeningFormulaCells(aStartCxt, aEndCxt, nStartRow, nEndRow);
+ }
+ break;
+ case sc::SingleCellListening:
+ rCell.StartListeningTo(rDocument);
+ StartListeningUnshared( rNewSharedRows);
+ break;
+ case sc::NoListening:
+ default:
+ if (!rNewSharedRows.empty())
+ {
+ assert(rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4);
+ // Calling SetNeedsListeningGroup() with a top row sets it to
+ // all affected formula cells of that group.
+ const ScFormulaCell* pFC = GetFormulaCell( rNewSharedRows[0]);
+ assert(pFC); // that *is* supposed to be a top row
+ if (pFC && !pFC->NeedsListening())
+ SetNeedsListeningGroup( rNewSharedRows[0]);
+ if (rNewSharedRows.size() > 2)
+ {
+ pFC = GetFormulaCell( rNewSharedRows[2]);
+ assert(pFC); // that *is* supposed to be a top row
+ if (pFC && !pFC->NeedsListening())
+ SetNeedsListeningGroup( rNewSharedRows[2]);
+ }
+ }
+ break;
+ }
+
+ if (!rDocument.IsCalcingAfterLoad())
+ rCell.SetDirty();
+}
+
+void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength,
+ std::vector<SCROW>& rNewSharedRows )
+{
+ // Make sure the whole length consists of formula cells.
+ if (aPos.first->type != sc::element_type_formula)
+ return;
+
+ if (aPos.first->size < aPos.second + nLength)
+ // Block is shorter than specified length.
+ return;
+
+ // Join the top and bottom cells only.
+ ScFormulaCell* pCell1 = sc::formula_block::at(*aPos.first->data, aPos.second);
+ JoinNewFormulaCell(aPos, *pCell1);
+
+ sc::CellStoreType::position_type aPosLast = aPos;
+ aPosLast.second += nLength - 1;
+ ScFormulaCell* pCell2 = sc::formula_block::at(*aPosLast.first->data, aPosLast.second);
+ JoinNewFormulaCell(aPosLast, *pCell2);
+
+ ScDocument& rDocument = GetDoc();
+ if (rDocument.IsClipOrUndo() || rDocument.IsInsertingFromOtherDoc())
+ return;
+
+ const bool bShared = pCell1->IsShared() || pCell2->IsShared();
+ if (bShared)
+ {
+ const SCROW nTopRow = (pCell1->IsShared() ? pCell1->GetSharedTopRow() : pCell1->aPos.Row());
+ const SCROW nBotRow = (pCell2->IsShared() ?
+ pCell2->GetSharedTopRow() + pCell2->GetSharedLength() - 1 : pCell2->aPos.Row());
+ if (rNewSharedRows.empty())
+ {
+ rNewSharedRows.push_back( nTopRow);
+ rNewSharedRows.push_back( nBotRow);
+ }
+ else if (rNewSharedRows.size() == 2)
+ {
+ // Combine into one span.
+ if (rNewSharedRows[0] > nTopRow)
+ rNewSharedRows[0] = nTopRow;
+ if (rNewSharedRows[1] < nBotRow)
+ rNewSharedRows[1] = nBotRow;
+ }
+ else if (rNewSharedRows.size() == 4)
+ {
+ // Merge into one span.
+ // The original two spans are ordered from top to bottom.
+ std::vector<SCROW> aRows { std::min( rNewSharedRows[0], nTopRow), std::max( rNewSharedRows[3], nBotRow) };
+ rNewSharedRows.swap( aRows);
+ }
+ else
+ {
+ assert(!"rNewSharedRows?");
+ }
+ }
+ StartListeningUnshared( rNewSharedRows);
+
+ sc::StartListeningContext aCxt(rDocument);
+ ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
+ ScFormulaCell** ppEnd = pp + nLength;
+ for (; pp != ppEnd; ++pp)
+ {
+ if (!bShared)
+ (*pp)->StartListeningTo(aCxt);
+ if (!rDocument.IsCalcingAfterLoad())
+ (*pp)->SetDirty();
+ }
+}
+
+void ScColumn::BroadcastNewCell( SCROW nRow )
+{
+ // When we insert from the Clipboard we still have wrong (old) References!
+ // First they are rewired in CopyBlockFromClip via UpdateReference and the
+ // we call StartListeningFromClip and BroadcastFromClip.
+ // If we insert into the Clipboard/andoDoc, we do not use a Broadcast.
+ // After Import we call CalcAfterLoad and in there Listening.
+ if (GetDoc().IsClipOrUndo() || GetDoc().IsInsertingFromOtherDoc() || GetDoc().IsCalcingAfterLoad())
+ return;
+
+ Broadcast(nRow);
+}
+
+bool ScColumn::UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow, sc::CellStoreType::iterator& itr )
+{
+ if (rAttr.mnScriptType != SvtScriptType::UNKNOWN)
+ // Already updated. Nothing to do.
+ return false;
+
+ // Script type not yet determined. Determine the real script
+ // type, and store it.
+ const ScPatternAttr* pPattern = GetPattern(nRow);
+ if (!pPattern)
+ return false;
+
+ sc::CellStoreType::position_type pos = maCells.position(itr, nRow);
+ itr = pos.first;
+ size_t nOffset = pos.second;
+ ScRefCellValue aCell = GetCellValue( itr, nOffset );
+ ScAddress aPos(nCol, nRow, nTab);
+
+ ScDocument& rDocument = GetDoc();
+ const SfxItemSet* pCondSet = nullptr;
+ ScConditionalFormatList* pCFList = rDocument.GetCondFormList(nTab);
+ if (pCFList)
+ {
+ const ScCondFormatItem& rItem =
+ pPattern->GetItem(ATTR_CONDITIONAL);
+ const ScCondFormatIndexes& rData = rItem.GetCondFormatData();
+ pCondSet = rDocument.GetCondResult(aCell, aPos, *pCFList, rData);
+ }
+
+ SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
+
+ // fetch the pattern again, it might have changed under us
+ pPattern = GetPattern(nRow);
+ const Color* pColor;
+ sal_uInt32 nFormat = pPattern->GetNumberFormat(pFormatter, pCondSet);
+ OUString aStr = ScCellFormat::GetString(aCell, nFormat, &pColor, *pFormatter, rDocument);
+
+ // Store the real script type to the array.
+ rAttr.mnScriptType = rDocument.GetStringScriptType(aStr);
+ return true;
+}
+
+namespace {
+
+class DeleteAreaHandler
+{
+ ScDocument& mrDoc;
+ std::vector<ScFormulaCell*> maFormulaCells;
+ sc::SingleColumnSpanSet maDeleteRanges;
+
+ bool mbNumeric:1;
+ bool mbString:1;
+ bool mbFormula:1;
+ bool mbDateTime:1;
+ ScColumn& mrCol;
+
+public:
+ DeleteAreaHandler(ScDocument& rDoc, InsertDeleteFlags nDelFlag, ScColumn& rCol) :
+ mrDoc(rDoc),
+ maDeleteRanges(rDoc.GetSheetLimits()),
+ mbNumeric(nDelFlag & InsertDeleteFlags::VALUE),
+ mbString(nDelFlag & InsertDeleteFlags::STRING),
+ mbFormula(nDelFlag & InsertDeleteFlags::FORMULA),
+ mbDateTime(nDelFlag & InsertDeleteFlags::DATETIME),
+ mrCol(rCol) {}
+
+ void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ switch (node.type)
+ {
+ case sc::element_type_numeric:
+ // Numeric type target datetime and number, thus we have a dedicated function
+ if (!mbNumeric && !mbDateTime)
+ return;
+
+ // If numeric and datetime selected, delete full range
+ if (mbNumeric && mbDateTime)
+ break;
+
+ deleteNumeric(node, nOffset, nDataSize);
+ return;
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ if (!mbString)
+ return;
+ break;
+ case sc::element_type_formula:
+ {
+ if (!mbFormula)
+ return;
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*node.data) + nOffset;
+ sc::formula_block::iterator itEnd = it + nDataSize;
+ maFormulaCells.insert(maFormulaCells.end(), it, itEnd);
+ }
+ break;
+ case sc::element_type_empty:
+ default:
+ return;
+ }
+
+ // Tag these cells for deletion.
+ SCROW nRow1 = node.position + nOffset;
+ SCROW nRow2 = nRow1 + nDataSize - 1;
+ maDeleteRanges.set(nRow1, nRow2, true);
+ }
+
+ void deleteNumeric(const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ size_t nStart = node.position + nOffset;
+ size_t nElements = 1;
+ bool bLastTypeDateTime = isDateTime(nStart); // true = datetime, false = numeric
+ size_t nCount = nStart + nDataSize;
+
+ for (size_t i = nStart + 1; i < nCount; i++)
+ {
+ bool bIsDateTime = isDateTime(i);
+
+ // same type as previous
+ if (bIsDateTime == bLastTypeDateTime)
+ {
+ nElements++;
+ }
+ // type switching
+ else
+ {
+ deleteNumberOrDateTime(nStart, nStart + nElements - 1, bLastTypeDateTime);
+ nStart += nElements;
+ nElements = 1;
+ }
+
+ bLastTypeDateTime = bIsDateTime;
+ }
+
+ // delete last cells
+ deleteNumberOrDateTime(nStart, nStart + nElements - 1, bLastTypeDateTime);
+ }
+
+ void deleteNumberOrDateTime(SCROW nRow1, SCROW nRow2, bool dateTime)
+ {
+ if (!dateTime && !mbNumeric) // numeric flag must be selected
+ return;
+ if (dateTime && !mbDateTime) // datetime flag must be selected
+ return;
+ maDeleteRanges.set(nRow1, nRow2, true);
+ }
+
+ bool isDateTime(size_t position)
+ {
+ SvNumFormatType nType = mrDoc.GetFormatTable()->GetType(
+ mrCol.GetAttr(position, ATTR_VALUE_FORMAT).GetValue());
+
+ return (nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) ||
+ (nType == SvNumFormatType::DATETIME);
+ }
+
+ /**
+ * Query the formula ranges that may have stopped listening, accounting for
+ * the formula groups.
+ */
+ std::vector<std::pair<SCROW, SCROW>> getFormulaRanges()
+ {
+ std::vector<std::pair<SCROW, SCROW>> aRet;
+
+ for (const ScFormulaCell* pFC : maFormulaCells)
+ {
+ SCROW nTopRow = pFC->aPos.Row();
+ SCROW nBottomRow = pFC->aPos.Row();
+
+ auto xGroup = pFC->GetCellGroup();
+ if (xGroup)
+ {
+ pFC = xGroup->mpTopCell;
+ nTopRow = pFC->aPos.Row();
+ nBottomRow = nTopRow + xGroup->mnLength - 1;
+ }
+
+ aRet.emplace_back(nTopRow, nBottomRow);
+ }
+
+ return aRet;
+ }
+
+ void endFormulas()
+ {
+ mrDoc.EndListeningFormulaCells(maFormulaCells);
+ }
+
+ sc::SingleColumnSpanSet& getSpans()
+ {
+ return maDeleteRanges;
+ }
+};
+
+class EmptyCells
+{
+ ScColumn& mrColumn;
+ sc::ColumnBlockPosition& mrPos;
+
+ static void splitFormulaGrouping(const sc::CellStoreType::position_type& rPos)
+ {
+ if (rPos.first->type == sc::element_type_formula)
+ {
+ ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, rPos.second);
+ sc::SharedFormulaUtil::unshareFormulaCell(rPos, rCell);
+ }
+ }
+
+public:
+ EmptyCells( sc::ColumnBlockPosition& rPos, ScColumn& rColumn ) :
+ mrColumn(rColumn), mrPos(rPos) {}
+
+ void operator() (const sc::RowSpan& rSpan)
+ {
+ sc::CellStoreType& rCells = mrColumn.GetCellStore();
+
+ // First, split formula grouping at the top and bottom boundaries
+ // before emptying the cells.
+ sc::CellStoreType::position_type aPos = rCells.position(mrPos.miCellPos, rSpan.mnRow1);
+ splitFormulaGrouping(aPos);
+ aPos = rCells.position(aPos.first, rSpan.mnRow2);
+ splitFormulaGrouping(aPos);
+
+ mrPos.miCellPos = rCells.set_empty(mrPos.miCellPos, rSpan.mnRow1, rSpan.mnRow2);
+ mrPos.miCellTextAttrPos = mrColumn.GetCellAttrStore().set_empty(mrPos.miCellTextAttrPos, rSpan.mnRow1, rSpan.mnRow2);
+ }
+};
+
+}
+
+ScColumn::DeleteCellsResult::DeleteCellsResult( const ScDocument& rDoc ) :
+ aDeletedRows( rDoc.GetSheetLimits() )
+{
+}
+
+std::unique_ptr<ScColumn::DeleteCellsResult> ScColumn::DeleteCells(
+ sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag )
+{
+ std::unique_ptr<DeleteCellsResult> xResult = std::make_unique<DeleteCellsResult>(GetDoc());
+
+ // Determine which cells to delete based on the deletion flags.
+ DeleteAreaHandler aFunc(GetDoc(), nDelFlag, *this);
+ sc::CellStoreType::iterator itPos = maCells.position(rBlockPos.miCellPos, nRow1).first;
+ sc::ProcessBlock(itPos, maCells, aFunc, nRow1, nRow2);
+ xResult->aFormulaRanges = aFunc.getFormulaRanges();
+ aFunc.endFormulas(); // Have the formula cells stop listening.
+
+ // Get the deletion spans.
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ aFunc.getSpans().getSpans(aSpans);
+
+ // Delete the cells for real.
+ // tdf#139820: Deleting in reverse order is more efficient.
+ std::for_each(aSpans.rbegin(), aSpans.rend(), EmptyCells(rBlockPos, *this));
+ CellStorageModified();
+
+ aFunc.getSpans().swap(xResult->aDeletedRows);
+
+ return xResult;
+}
+
+void ScColumn::DeleteArea(
+ SCROW nStartRow, SCROW nEndRow, InsertDeleteFlags nDelFlag, bool bBroadcast,
+ sc::ColumnSpanSet* pBroadcastSpans )
+{
+ InsertDeleteFlags nContMask = InsertDeleteFlags::CONTENTS;
+ // InsertDeleteFlags::NOCAPTIONS needs to be passed too, if InsertDeleteFlags::NOTE is set
+ if( nDelFlag & InsertDeleteFlags::NOTE )
+ nContMask |= InsertDeleteFlags::NOCAPTIONS;
+ InsertDeleteFlags nContFlag = nDelFlag & nContMask;
+
+ sc::ColumnBlockPosition aBlockPos;
+ InitBlockPosition(aBlockPos);
+ std::unique_ptr<DeleteCellsResult> xResult;
+
+ if (!IsEmptyData() && nContFlag != InsertDeleteFlags::NONE)
+ {
+ xResult = DeleteCells(aBlockPos, nStartRow, nEndRow, nDelFlag);
+ if (pBroadcastSpans)
+ {
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ xResult->aDeletedRows.getSpans(aSpans);
+ for (const auto& rSpan : aSpans)
+ pBroadcastSpans->set(GetDoc(), nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, true);
+ }
+ }
+
+ if (nDelFlag & InsertDeleteFlags::NOTE)
+ {
+ bool bForgetCaptionOwnership = ((nDelFlag & InsertDeleteFlags::FORGETCAPTIONS) != InsertDeleteFlags::NONE);
+ DeleteCellNotes(aBlockPos, nStartRow, nEndRow, bForgetCaptionOwnership);
+ }
+
+ if (nDelFlag & InsertDeleteFlags::SPARKLINES)
+ {
+ DeleteSparklineCells(aBlockPos, nStartRow, nEndRow);
+ }
+
+ if ( nDelFlag & InsertDeleteFlags::EDITATTR )
+ {
+ RemoveEditAttribs(aBlockPos, nStartRow, nEndRow);
+ }
+
+ // Delete attributes just now
+ if ((nDelFlag & InsertDeleteFlags::ATTRIB) == InsertDeleteFlags::ATTRIB)
+ pAttrArray->DeleteArea( nStartRow, nEndRow );
+ else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
+ pAttrArray->DeleteHardAttr( nStartRow, nEndRow );
+
+ if (xResult && bBroadcast)
+ {
+ // Broadcast on only cells that were deleted; no point broadcasting on
+ // cells that were already empty before the deletion.
+ std::vector<SCROW> aRows;
+ xResult->aDeletedRows.getRows(aRows);
+ BroadcastCells(aRows, SfxHintId::ScDataChanged);
+ }
+}
+
+void ScColumn::InitBlockPosition( sc::ColumnBlockPosition& rBlockPos )
+{
+ rBlockPos.miBroadcasterPos = maBroadcasters.begin();
+ rBlockPos.miCellNotePos = maCellNotes.begin();
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin();
+ rBlockPos.miCellPos = maCells.begin();
+ rBlockPos.miSparklinePos = maSparklines.begin();
+}
+
+void ScColumn::InitBlockPosition( sc::ColumnBlockConstPosition& rBlockPos ) const
+{
+ rBlockPos.miCellNotePos = maCellNotes.begin();
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.begin();
+ rBlockPos.miCellPos = maCells.begin();
+}
+
+namespace {
+
+class CopyAttrArrayByRange
+{
+ ScAttrArray& mrDestAttrArray;
+ ScAttrArray& mrSrcAttrArray;
+ tools::Long mnRowOffset;
+public:
+ CopyAttrArrayByRange(ScAttrArray& rDestAttrArray, ScAttrArray& rSrcAttrArray, tools::Long nRowOffset) :
+ mrDestAttrArray(rDestAttrArray), mrSrcAttrArray(rSrcAttrArray), mnRowOffset(nRowOffset) {}
+
+ void operator() (const sc::RowSpan& rSpan)
+ {
+ mrDestAttrArray.CopyAreaSafe(
+ rSpan.mnRow1+mnRowOffset, rSpan.mnRow2+mnRowOffset, mnRowOffset, mrSrcAttrArray);
+ }
+};
+
+class CopyCellsFromClipHandler
+{
+ sc::CopyFromClipContext& mrCxt;
+ ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ SCTAB mnTab;
+ SCCOL mnCol;
+ SCTAB mnSrcTab;
+ SCCOL mnSrcCol;
+ tools::Long mnRowOffset;
+ sc::ColumnBlockPosition maDestBlockPos;
+ sc::ColumnBlockPosition* mpDestBlockPos; // to save it for next iteration.
+ svl::SharedStringPool* mpSharedStringPool;
+
+ void insertRefCell(SCROW nSrcRow, SCROW nDestRow)
+ {
+ ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab);
+ ScAddress aDestPos(mnCol, nDestRow, mnTab);
+ ScSingleRefData aRef;
+ aRef.InitAddress(aSrcPos);
+ aRef.SetFlag3D(true);
+
+ ScTokenArray aArr(*mrCxt.getDestDoc());
+ aArr.AddSingleReference(aRef);
+
+ mrDestCol.SetFormulaCell(
+ maDestBlockPos, nDestRow, new ScFormulaCell(mrDestCol.GetDoc(), aDestPos, aArr));
+ }
+
+ void duplicateNotes(SCROW nStartRow, size_t nDataSize, bool bCloneCaption )
+ {
+ mrSrcCol.DuplicateNotes(nStartRow, nDataSize, mrDestCol, maDestBlockPos, bCloneCaption, mnRowOffset);
+ }
+
+ void duplicateSparklines(SCROW nStartRow, size_t nDataSize)
+ {
+ mrSrcCol.DuplicateSparklines(nStartRow, nDataSize, mrDestCol, maDestBlockPos, mnRowOffset);
+ }
+
+public:
+ CopyCellsFromClipHandler(sc::CopyFromClipContext& rCxt, ScColumn& rSrcCol, ScColumn& rDestCol, SCTAB nDestTab, SCCOL nDestCol, tools::Long nRowOffset, svl::SharedStringPool* pSharedStringPool) :
+ mrCxt(rCxt),
+ mrSrcCol(rSrcCol),
+ mrDestCol(rDestCol),
+ mnTab(nDestTab),
+ mnCol(nDestCol),
+ mnSrcTab(rSrcCol.GetTab()),
+ mnSrcCol(rSrcCol.GetCol()),
+ mnRowOffset(nRowOffset),
+ mpDestBlockPos(mrCxt.getBlockPosition(nDestTab, nDestCol)),
+ mpSharedStringPool(pSharedStringPool)
+ {
+ if (mpDestBlockPos)
+ {
+ {
+ // Re-initialize the broadcaster position hint, which may have
+ // become invalid by the time it gets here...
+ sc::ColumnBlockPosition aTempPos;
+ mrDestCol.InitBlockPosition(aTempPos);
+ mpDestBlockPos->miBroadcasterPos = aTempPos.miBroadcasterPos;
+ }
+ maDestBlockPos = *mpDestBlockPos;
+ }
+ else
+ mrDestCol.InitBlockPosition(maDestBlockPos);
+ }
+
+ ~CopyCellsFromClipHandler()
+ {
+ if (mpDestBlockPos)
+ // Don't forget to save this to the context!
+ *mpDestBlockPos = maDestBlockPos;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ SCROW nSrcRow1 = node.position + nOffset;
+ bool bCopyCellNotes = mrCxt.isCloneNotes();
+ bool bCopySparklines = mrCxt.isCloneSparklines();
+
+ InsertDeleteFlags nFlags = mrCxt.getInsertFlag();
+
+ if (node.type == sc::element_type_empty)
+ {
+ if (bCopyCellNotes && !mrCxt.isSkipEmptyCells())
+ {
+ bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ duplicateNotes(nSrcRow1, nDataSize, bCloneCaption );
+ }
+ if (bCopySparklines) // If there is a sparkline is it empty?
+ {
+ duplicateSparklines(nSrcRow1, nDataSize);
+ }
+ return;
+ }
+
+ bool bNumeric = (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
+ bool bDateTime = (nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE;
+ bool bString = (nFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE;
+ bool bBoolean = (nFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE;
+ bool bFormula = (nFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE;
+
+ bool bAsLink = mrCxt.isAsLink();
+
+ switch (node.type)
+ {
+ case sc::element_type_numeric:
+ {
+ // We need to copy numeric cells individually because of date type check.
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
+ {
+ bool bCopy = mrCxt.isDateCell(mrSrcCol, nSrcRow) ? bDateTime : bNumeric;
+ if (!bCopy)
+ continue;
+
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else
+ mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, *it);
+ }
+ }
+ break;
+ case sc::element_type_string:
+ {
+ if (!bString)
+ break;
+
+ sc::string_block::const_iterator it = sc::string_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::string_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
+ {
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else if (mpSharedStringPool)
+ {
+ // Re-intern the string if source is a different document.
+ svl::SharedString aInterned = mpSharedStringPool->intern( (*it).getString());
+ mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aInterned);
+ }
+ else
+ mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, *it);
+ }
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ if (!bString)
+ break;
+
+ sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::edittext_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
+ {
+
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else
+ mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, **it);
+ }
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (SCROW nSrcRow = nSrcRow1; it != itEnd; ++it, ++nSrcRow)
+ {
+ ScFormulaCell& rSrcCell = **it;
+ bool bForceFormula = false;
+ if (bBoolean)
+ {
+ // See if the formula consists of =TRUE() or =FALSE().
+ const ScTokenArray* pCode = rSrcCell.GetCode();
+ if (pCode && pCode->GetLen() == 1)
+ {
+ const formula::FormulaToken* p = pCode->FirstToken();
+ if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
+ // This is a boolean formula.
+ bForceFormula = true;
+ }
+ }
+
+ ScAddress aDestPos(mnCol, nSrcRow + mnRowOffset, mnTab);
+ if (bFormula || bForceFormula)
+ {
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else
+ {
+ mrDestCol.SetFormulaCell(
+ maDestBlockPos, nSrcRow + mnRowOffset,
+ new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos),
+ sc::SingleCellListening,
+ rSrcCell.NeedsNumberFormat());
+ }
+ }
+ else if (bNumeric || bDateTime || bString)
+ {
+ // Always just copy the original row to the Undo Document;
+ // do not create Value/string cells from formulas
+
+ FormulaError nErr = rSrcCell.GetErrCode();
+ if (nErr != FormulaError::NONE)
+ {
+ // error codes are cloned with values
+ if (bNumeric)
+ {
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else
+ {
+ ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos);
+ pErrCell->SetErrCode(nErr);
+ mrDestCol.SetFormulaCell(
+ maDestBlockPos, nSrcRow + mnRowOffset, pErrCell);
+ }
+ }
+ }
+ else if (rSrcCell.IsEmptyDisplayedAsString())
+ {
+ // Empty stays empty and doesn't become 0.
+ continue;
+ }
+ else if (rSrcCell.IsValue())
+ {
+ bool bCopy = mrCxt.isDateCell(mrSrcCol, nSrcRow) ? bDateTime : bNumeric;
+ if (!bCopy)
+ continue;
+
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else
+ mrDestCol.SetValue(maDestBlockPos, nSrcRow + mnRowOffset, rSrcCell.GetValue());
+ }
+ else if (bString)
+ {
+ svl::SharedString aStr = rSrcCell.GetString();
+ if (aStr.isEmpty())
+ // do not clone empty string
+ continue;
+
+ if (bAsLink)
+ insertRefCell(nSrcRow, nSrcRow + mnRowOffset);
+ else if (rSrcCell.IsMultilineResult())
+ {
+ // Clone as an edit text object.
+ ScFieldEditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aStr.getString());
+ mrDestCol.SetEditText(maDestBlockPos, nSrcRow + mnRowOffset, rEngine.CreateTextObject());
+ }
+ else if (mpSharedStringPool)
+ {
+ // Re-intern the string if source is a different document.
+ svl::SharedString aInterned = mpSharedStringPool->intern( aStr.getString());
+ mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aInterned);
+ }
+ else
+ {
+ mrDestCol.SetRawString(maDestBlockPos, nSrcRow + mnRowOffset, aStr);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ if (bCopyCellNotes)
+ {
+ bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ duplicateNotes(nSrcRow1, nDataSize, bCloneCaption );
+ }
+ if (bCopySparklines)
+ {
+ duplicateSparklines(nSrcRow1, nDataSize);
+ }
+ }
+};
+
+class CopyTextAttrsFromClipHandler
+{
+ sc::CellTextAttrStoreType& mrAttrs;
+ size_t mnDelta;
+ sc::ColumnBlockPosition maDestBlockPos;
+ sc::ColumnBlockPosition* mpDestBlockPos; // to save it for next iteration.
+
+public:
+ CopyTextAttrsFromClipHandler( sc::CopyFromClipContext& rCxt, sc::CellTextAttrStoreType& rAttrs,
+ ScColumn& rDestCol, SCTAB nDestTab, SCCOL nDestCol, size_t nDelta ) :
+ mrAttrs(rAttrs),
+ mnDelta(nDelta),
+ mpDestBlockPos(rCxt.getBlockPosition(nDestTab, nDestCol))
+ {
+ if (mpDestBlockPos)
+ maDestBlockPos.miCellTextAttrPos = mpDestBlockPos->miCellTextAttrPos;
+ else
+ rDestCol.InitBlockPosition(maDestBlockPos);
+ }
+
+ ~CopyTextAttrsFromClipHandler()
+ {
+ if (mpDestBlockPos)
+ // Don't forget to save this to the context!
+ mpDestBlockPos->miCellTextAttrPos = maDestBlockPos.miCellTextAttrPos;
+ }
+
+ void operator() ( const sc::CellTextAttrStoreType::value_type& aNode, size_t nOffset, size_t nDataSize )
+ {
+ if (aNode.type != sc::element_type_celltextattr)
+ return;
+
+ sc::celltextattr_block::const_iterator it = sc::celltextattr_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::celltextattr_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ size_t nPos = aNode.position + nOffset + mnDelta;
+ maDestBlockPos.miCellTextAttrPos = mrAttrs.set(maDestBlockPos.miCellTextAttrPos, nPos, it, itEnd);
+ }
+};
+
+}
+
+// rColumn = source
+// nRow1, nRow2 = target position
+
+void ScColumn::CopyFromClip(
+ sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, tools::Long nDy, ScColumn& rColumn )
+{
+ sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
+ if (!pBlockPos)
+ return;
+
+ if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE)
+ {
+ if (rCxt.isSkipEmptyCells())
+ {
+ // copy only attributes for non-empty cells between nRow1-nDy and nRow2-nDy.
+ sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
+ aSpanSet.scan(rColumn, nRow1-nDy, nRow2-nDy);
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ aSpanSet.getSpans(aSpans);
+ std::for_each(
+ aSpans.begin(), aSpans.end(), CopyAttrArrayByRange(*rColumn.pAttrArray, *pAttrArray, nDy));
+ }
+ else
+ rColumn.pAttrArray->CopyAreaSafe( nRow1, nRow2, nDy, *pAttrArray );
+ }
+ if ((rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE)
+ return;
+
+ ScDocument& rDocument = GetDoc();
+ if (rCxt.isAsLink() && rCxt.getInsertFlag() == InsertDeleteFlags::ALL)
+ {
+ // We also reference empty cells for "ALL"
+ // InsertDeleteFlags::ALL must always contain more flags when compared to "Insert contents" as
+ // contents can be selected one by one!
+
+ ScAddress aDestPos( nCol, 0, nTab ); // Adapt Row
+
+ // Create reference (Source Position)
+ ScSingleRefData aRef;
+ aRef.InitFlags(); // -> All absolute
+ aRef.SetAbsCol(rColumn.nCol);
+ aRef.SetAbsTab(rColumn.nTab);
+ aRef.SetFlag3D(true);
+
+ for (SCROW nDestRow = nRow1; nDestRow <= nRow2; nDestRow++)
+ {
+ aRef.SetAbsRow(nDestRow - nDy); // Source row
+ aDestPos.SetRow( nDestRow );
+
+ ScTokenArray aArr(GetDoc());
+ aArr.AddSingleReference( aRef );
+ SetFormulaCell(*pBlockPos, nDestRow, new ScFormulaCell(rDocument, aDestPos, aArr));
+ }
+
+ // Don't forget to copy the cell text attributes.
+ CopyTextAttrsFromClipHandler aFunc(rCxt, maCellTextAttrs, *this, nTab, nCol, nDy);
+ sc::ParseBlock(rColumn.maCellTextAttrs.begin(), rColumn.maCellTextAttrs, aFunc, nRow1-nDy, nRow2-nDy);
+
+ return;
+ }
+
+ // Compare the ScDocumentPool* to determine if we are copying within the
+ // same document. If not, re-intern shared strings.
+ svl::SharedStringPool* pSharedStringPool = (rColumn.GetDoc().GetPool() != rDocument.GetPool()) ?
+ &rDocument.GetSharedStringPool() : nullptr;
+
+ // nRow1 to nRow2 is for destination (this) column. Subtract nDy to get the source range.
+ // Copy all cells in the source column (rColumn) from nRow1-nDy to nRow2-nDy to this column.
+ {
+ CopyCellsFromClipHandler aFunc(rCxt, rColumn, *this, nTab, nCol, nDy, pSharedStringPool);
+ sc::ParseBlock(rColumn.maCells.begin(), rColumn.maCells, aFunc, nRow1-nDy, nRow2-nDy);
+ }
+
+ {
+ // Don't forget to copy the cell text attributes.
+ CopyTextAttrsFromClipHandler aFunc(rCxt, maCellTextAttrs, *this, nTab, nCol, nDy);
+ sc::ParseBlock(rColumn.maCellTextAttrs.begin(), rColumn.maCellTextAttrs, aFunc, nRow1-nDy, nRow2-nDy);
+ }
+}
+
+void ScColumn::MixMarked(
+ sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction,
+ bool bSkipEmpty, const ScColumn& rSrcCol )
+{
+ SCROW nRow1, nRow2;
+
+ if (rMark.IsMultiMarked())
+ {
+ ScMultiSelIter aIter( rMark.GetMultiSelData(), nCol );
+ while (aIter.Next( nRow1, nRow2 ))
+ MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, rSrcCol);
+ }
+}
+
+namespace {
+
+// Result in rVal1
+bool lcl_DoFunction( double& rVal1, double nVal2, ScPasteFunc nFunction )
+{
+ bool bOk = false;
+ switch (nFunction)
+ {
+ case ScPasteFunc::ADD:
+ bOk = SubTotal::SafePlus( rVal1, nVal2 );
+ break;
+ case ScPasteFunc::SUB:
+ nVal2 = -nVal2; // FIXME: Can we do this always without error?
+ bOk = SubTotal::SafePlus( rVal1, nVal2 );
+ break;
+ case ScPasteFunc::MUL:
+ bOk = SubTotal::SafeMult( rVal1, nVal2 );
+ break;
+ case ScPasteFunc::DIV:
+ bOk = SubTotal::SafeDiv( rVal1, nVal2 );
+ break;
+ default: break;
+ }
+ return bOk;
+}
+
+void lcl_AddCode( ScTokenArray& rArr, const ScFormulaCell* pCell )
+{
+ rArr.AddOpCode(ocOpen);
+
+ const ScTokenArray* pCode = pCell->GetCode();
+ if (pCode)
+ {
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ const formula::FormulaToken* pToken = aIter.First();
+ while (pToken)
+ {
+ rArr.AddToken( *pToken );
+ pToken = aIter.Next();
+ }
+ }
+
+ rArr.AddOpCode(ocClose);
+}
+
+class MixDataHandler
+{
+ ScColumn& mrDestColumn;
+ sc::ColumnBlockPosition& mrBlockPos;
+
+ sc::CellStoreType maNewCells;
+ sc::CellStoreType::iterator miNewCellsPos;
+
+ size_t mnRowOffset;
+ ScPasteFunc mnFunction;
+
+ bool mbSkipEmpty;
+
+ void doFunction( size_t nDestRow, double fVal1, double fVal2 )
+ {
+ bool bOk = lcl_DoFunction(fVal1, fVal2, mnFunction);
+
+ if (bOk)
+ miNewCellsPos = maNewCells.set(miNewCellsPos, nDestRow-mnRowOffset, fVal1);
+ else
+ {
+ ScAddress aPos(mrDestColumn.GetCol(), nDestRow, mrDestColumn.GetTab());
+
+ ScFormulaCell* pFC = new ScFormulaCell(mrDestColumn.GetDoc(), aPos);
+ pFC->SetErrCode(FormulaError::NoValue);
+
+ miNewCellsPos = maNewCells.set(miNewCellsPos, nDestRow-mnRowOffset, pFC);
+ }
+ }
+
+public:
+ MixDataHandler(
+ sc::ColumnBlockPosition& rBlockPos,
+ ScColumn& rDestColumn,
+ SCROW nRow1, SCROW nRow2,
+ ScPasteFunc nFunction, bool bSkipEmpty) :
+ mrDestColumn(rDestColumn),
+ mrBlockPos(rBlockPos),
+ maNewCells(nRow2 - nRow1 + 1),
+ miNewCellsPos(maNewCells.begin()),
+ mnRowOffset(nRow1),
+ mnFunction(nFunction),
+ mbSkipEmpty(bSkipEmpty)
+ {
+ }
+
+ void operator() (size_t nRow, double f)
+ {
+ sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nRow);
+ mrBlockPos.miCellPos = aPos.first;
+ switch (aPos.first->type)
+ {
+ case sc::element_type_empty:
+ case sc::element_type_numeric:
+ {
+ double fSrcVal = 0.0;
+ if (aPos.first->type == sc::element_type_numeric)
+ fSrcVal = sc::numeric_block::at(*aPos.first->data, aPos.second);
+
+ // Both src and dest are of numeric type.
+ doFunction(nRow, f, fSrcVal);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ // Combination of value and at least one formula -> Create formula
+ ScTokenArray aArr(mrDestColumn.GetDoc());
+
+ // First row
+ aArr.AddDouble(f);
+
+ // Operator
+ OpCode eOp = ocAdd;
+ switch (mnFunction)
+ {
+ case ScPasteFunc::ADD: eOp = ocAdd; break;
+ case ScPasteFunc::SUB: eOp = ocSub; break;
+ case ScPasteFunc::MUL: eOp = ocMul; break;
+ case ScPasteFunc::DIV: eOp = ocDiv; break;
+ default: break;
+ }
+ aArr.AddOpCode(eOp); // Function
+
+ // Second row
+ ScFormulaCell* pDest = sc::formula_block::at(*aPos.first->data, aPos.second);
+ lcl_AddCode(aArr, pDest);
+
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nRow-mnRowOffset,
+ new ScFormulaCell(
+ mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr));
+ }
+ break;
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ {
+ // Destination cell is not a number. Just take the source cell.
+ miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, f);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ void operator() (size_t nRow, const svl::SharedString& rStr)
+ {
+ miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, rStr);
+ }
+
+ void operator() (size_t nRow, const EditTextObject* p)
+ {
+ miNewCellsPos = maNewCells.set(miNewCellsPos, nRow-mnRowOffset, p->Clone().release());
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nRow);
+ mrBlockPos.miCellPos = aPos.first;
+ switch (aPos.first->type)
+ {
+ case sc::element_type_numeric:
+ {
+ // Source is formula, and dest is value.
+ ScTokenArray aArr(mrDestColumn.GetDoc());
+
+ // First row
+ lcl_AddCode(aArr, p);
+
+ // Operator
+ OpCode eOp = ocAdd;
+ switch (mnFunction)
+ {
+ case ScPasteFunc::ADD: eOp = ocAdd; break;
+ case ScPasteFunc::SUB: eOp = ocSub; break;
+ case ScPasteFunc::MUL: eOp = ocMul; break;
+ case ScPasteFunc::DIV: eOp = ocDiv; break;
+ default: break;
+ }
+ aArr.AddOpCode(eOp); // Function
+
+ // Second row
+ aArr.AddDouble(sc::numeric_block::at(*aPos.first->data, aPos.second));
+
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nRow-mnRowOffset,
+ new ScFormulaCell(
+ mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr));
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ // Both are formulas.
+ ScTokenArray aArr(mrDestColumn.GetDoc());
+
+ // First row
+ lcl_AddCode(aArr, p);
+
+ // Operator
+ OpCode eOp = ocAdd;
+ switch (mnFunction)
+ {
+ case ScPasteFunc::ADD: eOp = ocAdd; break;
+ case ScPasteFunc::SUB: eOp = ocSub; break;
+ case ScPasteFunc::MUL: eOp = ocMul; break;
+ case ScPasteFunc::DIV: eOp = ocDiv; break;
+ default: break;
+ }
+ aArr.AddOpCode(eOp); // Function
+
+ // Second row
+ ScFormulaCell* pDest = sc::formula_block::at(*aPos.first->data, aPos.second);
+ lcl_AddCode(aArr, pDest);
+
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nRow-mnRowOffset,
+ new ScFormulaCell(
+ mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab()), aArr));
+ }
+ break;
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ case sc::element_type_empty:
+ {
+ // Destination cell is not a number. Just take the source cell.
+ ScAddress aDestPos(mrDestColumn.GetCol(), nRow, mrDestColumn.GetTab());
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nRow-mnRowOffset, new ScFormulaCell(*p, mrDestColumn.GetDoc(), aDestPos));
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ /**
+ * Empty cell series in the source (clip) document.
+ */
+ void operator() (mdds::mtv::element_t, size_t nTopRow, size_t nDataSize)
+ {
+ if (mbSkipEmpty)
+ return;
+
+ // Source cells are empty. Treat them as if they have a value of 0.0.
+ for (size_t i = 0; i < nDataSize; ++i)
+ {
+ size_t nDestRow = nTopRow + i;
+ sc::CellStoreType::position_type aPos = mrDestColumn.GetCellStore().position(mrBlockPos.miCellPos, nDestRow);
+ mrBlockPos.miCellPos = aPos.first;
+ switch (aPos.first->type)
+ {
+ case sc::element_type_numeric:
+ {
+ double fVal2 = sc::numeric_block::at(*aPos.first->data, aPos.second);
+ doFunction(nDestRow, 0.0, fVal2);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ const svl::SharedString& aVal = sc::string_block::at(*aPos.first->data, aPos.second);
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nDestRow-mnRowOffset, aVal);
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ EditTextObject* pObj = sc::edittext_block::at(*aPos.first->data, aPos.second);
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nDestRow-mnRowOffset, pObj->Clone().release());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ ScTokenArray aArr(mrDestColumn.GetDoc());
+
+ // First row
+ ScFormulaCell* pSrc = sc::formula_block::at(*aPos.first->data, aPos.second);
+ lcl_AddCode( aArr, pSrc);
+
+ // Operator
+ OpCode eOp = ocAdd;
+ switch (mnFunction)
+ {
+ case ScPasteFunc::ADD: eOp = ocAdd; break;
+ case ScPasteFunc::SUB: eOp = ocSub; break;
+ case ScPasteFunc::MUL: eOp = ocMul; break;
+ case ScPasteFunc::DIV: eOp = ocDiv; break;
+ default: break;
+ }
+
+ aArr.AddOpCode(eOp); // Function
+ aArr.AddDouble(0.0);
+
+ miNewCellsPos = maNewCells.set(
+ miNewCellsPos, nDestRow-mnRowOffset,
+ new ScFormulaCell(
+ mrDestColumn.GetDoc(), ScAddress(mrDestColumn.GetCol(), nDestRow, mrDestColumn.GetTab()), aArr));
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ /**
+ * Set the new cells to the destination (this) column.
+ */
+ void commit()
+ {
+ sc::CellStoreType& rDestCells = mrDestColumn.GetCellStore();
+
+ // Stop all formula cells in the destination range first.
+ sc::CellStoreType::position_type aPos = rDestCells.position(mrBlockPos.miCellPos, mnRowOffset);
+ mrDestColumn.DetachFormulaCells(aPos, maNewCells.size(), nullptr);
+
+ // Move the new cells to the destination range.
+ sc::CellStoreType::iterator& itDestPos = mrBlockPos.miCellPos;
+ sc::CellTextAttrStoreType::iterator& itDestAttrPos = mrBlockPos.miCellTextAttrPos;
+
+ for (const auto& rNewCell : maNewCells)
+ {
+ bool bHasContent = true;
+ size_t nDestRow = mnRowOffset + rNewCell.position;
+
+ switch (rNewCell.type)
+ {
+ case sc::element_type_numeric:
+ {
+ sc::numeric_block::iterator itData = sc::numeric_block::begin(*rNewCell.data);
+ sc::numeric_block::iterator itDataEnd = sc::numeric_block::end(*rNewCell.data);
+ itDestPos = mrDestColumn.GetCellStore().set(itDestPos, nDestRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ sc::string_block::iterator itData = sc::string_block::begin(*rNewCell.data);
+ sc::string_block::iterator itDataEnd = sc::string_block::end(*rNewCell.data);
+ itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::iterator itData = sc::edittext_block::begin(*rNewCell.data);
+ sc::edittext_block::iterator itDataEnd = sc::edittext_block::end(*rNewCell.data);
+ itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::iterator itData = sc::formula_block::begin(*rNewCell.data);
+ sc::formula_block::iterator itDataEnd = sc::formula_block::end(*rNewCell.data);
+
+ // Group new formula cells before inserting them.
+ sc::SharedFormulaUtil::groupFormulaCells(itData, itDataEnd);
+
+ // Insert the formula cells to the column.
+ itDestPos = rDestCells.set(itDestPos, nDestRow, itData, itDataEnd);
+
+ // Merge with the previous formula group (if any).
+ aPos = rDestCells.position(itDestPos, nDestRow);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+
+ // Merge with the next formula group (if any).
+ size_t nNextRow = nDestRow + rNewCell.size;
+ if (mrDestColumn.GetDoc().ValidRow(nNextRow))
+ {
+ aPos = rDestCells.position(aPos.first, nNextRow);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+
+ // Start listening on cells to get them updated by changes of referenced cells
+ std::vector<SCROW> aNewSharedRows;
+ aPos = rDestCells.position(itDestPos, nDestRow);
+ size_t nFormulaCells = std::distance(itData, itDataEnd);
+ mrDestColumn.AttachNewFormulaCells(aPos, nFormulaCells, aNewSharedRows);
+ }
+ break;
+ case sc::element_type_empty:
+ {
+ itDestPos = rDestCells.set_empty(itDestPos, nDestRow, nDestRow+rNewCell.size-1);
+ bHasContent = false;
+ }
+ break;
+ default:
+ ;
+ }
+
+ sc::CellTextAttrStoreType& rDestAttrs = mrDestColumn.GetCellAttrStore();
+ if (bHasContent)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(rNewCell.size, sc::CellTextAttr());
+ itDestAttrPos = rDestAttrs.set(itDestAttrPos, nDestRow, aAttrs.begin(), aAttrs.end());
+ }
+ else
+ itDestAttrPos = rDestAttrs.set_empty(itDestAttrPos, nDestRow, nDestRow+rNewCell.size-1);
+ }
+
+ maNewCells.release();
+ }
+};
+
+}
+
+void ScColumn::MixData(
+ sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, ScPasteFunc nFunction,
+ bool bSkipEmpty, const ScColumn& rSrcCol )
+{
+ // destination (this column) block position.
+
+ sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
+ if (!p)
+ return;
+
+ MixDataHandler aFunc(*p, *this, nRow1, nRow2, nFunction, bSkipEmpty);
+ sc::ParseAll(rSrcCol.maCells.begin(), rSrcCol.maCells, nRow1, nRow2, aFunc, aFunc);
+
+ aFunc.commit();
+ CellStorageModified();
+}
+
+std::unique_ptr<ScAttrIterator> ScColumnData::CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) const
+{
+ return std::make_unique<ScAttrIterator>( pAttrArray.get(), nStartRow, nEndRow, GetDoc().GetDefPattern() );
+}
+
+namespace {
+
+class StartListenersHandler
+{
+ sc::StartListeningContext* mpCxt;
+ bool mbAllListeners;
+
+public:
+ StartListenersHandler( sc::StartListeningContext& rCxt, bool bAllListeners ) :
+ mpCxt(&rCxt), mbAllListeners(bAllListeners) {}
+
+ void operator() ( sc::CellStoreType::value_type& aBlk )
+ {
+ if (aBlk.type != sc::element_type_formula)
+ return;
+
+ ScFormulaCell** pp = &sc::formula_block::at(*aBlk.data, 0);
+ ScFormulaCell** ppEnd = pp + aBlk.size;
+
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell& rFC = **pp;
+ if (!mbAllListeners && !rFC.NeedsListening())
+ continue;
+
+ if (rFC.IsSharedTop())
+ {
+ sc::SharedFormulaUtil::startListeningAsGroup(*mpCxt, pp);
+ pp += rFC.GetSharedLength() - 1; // Move to the last cell in the group.
+ }
+ else
+ rFC.StartListeningTo(*mpCxt);
+ }
+ }
+};
+
+}
+
+void ScColumn::StartListeners( sc::StartListeningContext& rCxt, bool bAll )
+{
+ std::for_each(maCells.begin(), maCells.end(), StartListenersHandler(rCxt, bAll));
+}
+
+namespace {
+
+void applyTextNumFormat( ScColumn& rCol, SCROW nRow, SvNumberFormatter* pFormatter )
+{
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat(SvNumFormatType::TEXT);
+ ScPatternAttr aNewAttrs(rCol.GetDoc().GetPool());
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
+ rCol.ApplyPattern(nRow, aNewAttrs);
+}
+
+}
+
+bool ScColumn::ParseString(
+ ScCellValue& rCell, SCROW nRow, SCTAB nTabP, const OUString& rString,
+ formula::FormulaGrammar::AddressConvention eConv,
+ const ScSetStringParam* pParam )
+{
+ if (rString.isEmpty())
+ return false;
+
+ bool bNumFmtSet = false;
+
+ ScSetStringParam aParam;
+
+ if (pParam)
+ aParam = *pParam;
+
+ sal_uInt32 nIndex = 0;
+ sal_uInt32 nOldIndex = 0;
+ SvNumFormatType eNumFormatType = SvNumFormatType::ALL;
+ if (!aParam.mpNumFormatter)
+ aParam.mpNumFormatter = GetDoc().GetFormatTable();
+
+ sal_Unicode cFirstChar = 0; // Text
+ nIndex = nOldIndex = GetNumberFormat( GetDoc().GetNonThreadedContext(), nRow );
+ if ( rString.getLength() > 1 )
+ {
+ eNumFormatType = aParam.mpNumFormatter->GetType(nIndex);
+ if ( eNumFormatType != SvNumFormatType::TEXT )
+ cFirstChar = rString[0];
+ }
+
+ svl::SharedStringPool& rPool = GetDoc().GetSharedStringPool();
+
+ if ( cFirstChar == '=' )
+ {
+ if ( rString.getLength() == 1 ) // = Text
+ {
+ rCell.set(rPool.intern(rString));
+ }
+ else if (aParam.meSetTextNumFormat == ScSetStringParam::Always)
+ {
+ // Set the cell format type to Text.
+ applyTextNumFormat(*this, nRow, aParam.mpNumFormatter);
+ rCell.set(rPool.intern(rString));
+ }
+ else // = Formula
+ {
+ ScFormulaCell* pFormulaCell = new ScFormulaCell(
+ GetDoc(), ScAddress(nCol, nRow, nTabP), rString,
+ formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_DEFAULT, eConv),
+ ScMatrixMode::NONE);
+ if (aParam.mbCheckLinkFormula)
+ GetDoc().CheckLinkFormulaNeedingCheck( *pFormulaCell->GetCode());
+ rCell.set( pFormulaCell);
+ }
+ }
+ else if ( cFirstChar == '\'') // 'Text
+ {
+ if (aParam.mbHandleApostrophe)
+ {
+ // Cell format is not 'Text', and the first char is an apostrophe.
+ // Strip it and set text content.
+ // NOTE: this corresponds with sc/source/ui/view/tabvwsha.cxx
+ // ScTabViewShell::UpdateInputHandler() prepending an apostrophe if
+ // necessary.
+ rCell.set(rPool.intern(rString.copy(1)));
+ }
+ else
+ {
+ // This is normal text. Take it as-is.
+ rCell.set(rPool.intern(rString));
+ }
+ }
+ else
+ {
+ double nVal;
+
+ do
+ {
+ if (aParam.mbDetectNumberFormat)
+ {
+ // Editing a date prefers the format's locale's edit date
+ // format's date acceptance patterns and YMD order.
+ /* TODO: this could be determined already far above when
+ * starting to edit a date "cell" and passed down. A problem
+ * could also be if a new date was typed over or written by a
+ * macro assuming the current locale if that conflicts somehow.
+ * You can't have everything. See tdf#116579 and tdf#125109. */
+ if (eNumFormatType == SvNumFormatType::ALL)
+ eNumFormatType = aParam.mpNumFormatter->GetType(nIndex);
+ bool bForceFormatDate = (eNumFormatType == SvNumFormatType::DATE
+ || eNumFormatType == SvNumFormatType::DATETIME);
+ const SvNumberformat* pOldFormat = nullptr;
+ NfEvalDateFormat eEvalDateFormat = NF_EVALDATEFORMAT_INTL_FORMAT;
+ if (bForceFormatDate)
+ {
+ ScRefCellValue aCell = GetCellValue(nRow);
+ if (aCell.getType() == CELLTYPE_VALUE)
+ {
+ // Only for an actual date (serial number), not an
+ // arbitrary string or formula or empty cell.
+ // Also ensure the edit date format's pattern is used,
+ // not the display format's.
+ pOldFormat = aParam.mpNumFormatter->GetEntry( nOldIndex);
+ if (!pOldFormat)
+ bForceFormatDate = false;
+ else
+ {
+ nIndex = aParam.mpNumFormatter->GetEditFormat(
+ aCell.getValue(), nOldIndex, eNumFormatType, pOldFormat);
+ eEvalDateFormat = aParam.mpNumFormatter->GetEvalDateFormat();
+ aParam.mpNumFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL);
+ }
+ }
+ else
+ {
+ bForceFormatDate = false;
+ }
+ }
+
+ const bool bIsNumberFormat = aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal);
+
+ if (bForceFormatDate)
+ aParam.mpNumFormatter->SetEvalDateFormat( eEvalDateFormat);
+
+ if (!bIsNumberFormat)
+ break;
+
+ // If we have bForceFormatDate, the pOldFormat was/is of
+ // nOldIndex date(+time) type already, if detected type is
+ // compatible keep the original format.
+ if (bForceFormatDate && SvNumberFormatter::IsCompatible(
+ eNumFormatType, aParam.mpNumFormatter->GetType( nIndex)))
+ {
+ nIndex = nOldIndex;
+ }
+ else
+ {
+ // convert back to the original language if a built-in format was detected
+ if (!pOldFormat)
+ pOldFormat = aParam.mpNumFormatter->GetEntry( nOldIndex );
+ if (pOldFormat)
+ nIndex = aParam.mpNumFormatter->GetFormatForLanguageIfBuiltIn(
+ nIndex, pOldFormat->GetLanguage());
+ }
+
+ rCell.set(nVal);
+ if ( nIndex != nOldIndex)
+ {
+ // #i22345# New behavior: Apply the detected number format only if
+ // the old one was the default number, date, time or boolean format.
+ // Exception: If the new format is boolean, always apply it.
+
+ bool bOverwrite = false;
+ if ( pOldFormat )
+ {
+ SvNumFormatType nOldType = pOldFormat->GetMaskedType();
+ if ( nOldType == SvNumFormatType::NUMBER || nOldType == SvNumFormatType::DATE ||
+ nOldType == SvNumFormatType::TIME || nOldType == SvNumFormatType::LOGICAL )
+ {
+ if ( nOldIndex == aParam.mpNumFormatter->GetStandardFormat(
+ nOldType, pOldFormat->GetLanguage() ) )
+ {
+ bOverwrite = true; // default of these types can be overwritten
+ }
+ }
+ }
+ if ( !bOverwrite && aParam.mpNumFormatter->GetType( nIndex ) == SvNumFormatType::LOGICAL )
+ {
+ bOverwrite = true; // overwrite anything if boolean was detected
+ }
+
+ if ( bOverwrite )
+ {
+ ApplyAttr( nRow, SfxUInt32Item( ATTR_VALUE_FORMAT,
+ nIndex) );
+ bNumFmtSet = true;
+ }
+ }
+ }
+ else if (aParam.meSetTextNumFormat == ScSetStringParam::Never ||
+ aParam.meSetTextNumFormat == ScSetStringParam::SpecialNumberOnly)
+ {
+ // Only check if the string is a regular number.
+ const LocaleDataWrapper* pLocale = aParam.mpNumFormatter->GetLocaleData();
+ if (!pLocale)
+ break;
+
+ const LocaleDataItem2& aLocaleItem = pLocale->getLocaleItem();
+ const OUString& rDecSep = aLocaleItem.decimalSeparator;
+ const OUString& rGroupSep = aLocaleItem.thousandSeparator;
+ const OUString& rDecSepAlt = aLocaleItem.decimalSeparatorAlternative;
+ if (rDecSep.getLength() != 1 || rGroupSep.getLength() != 1 || rDecSepAlt.getLength() > 1)
+ break;
+
+ sal_Unicode dsep = rDecSep[0];
+ sal_Unicode gsep = rGroupSep[0];
+ sal_Unicode dsepa = rDecSepAlt.toChar();
+
+ if (!ScStringUtil::parseSimpleNumber(rString, dsep, gsep, dsepa, nVal, aParam.mbDetectScientificNumberFormat))
+ break;
+
+ rCell.set(nVal);
+ }
+ }
+ while (false);
+
+ if (rCell.getType() == CELLTYPE_NONE)
+ {
+ // If we reach here with ScSetStringParam::SpecialNumberOnly it
+ // means a simple number was not detected above, so test for
+ // special numbers. In any case ScSetStringParam::Always does not
+ // mean always, but only always for content that could be any
+ // numeric.
+ if ((aParam.meSetTextNumFormat == ScSetStringParam::Always ||
+ aParam.meSetTextNumFormat == ScSetStringParam::SpecialNumberOnly) &&
+ aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal))
+ {
+ // Set the cell format type to Text.
+ applyTextNumFormat(*this, nRow, aParam.mpNumFormatter);
+ }
+
+ rCell.set(rPool.intern(rString));
+ }
+ }
+
+ return bNumFmtSet;
+}
+
+/**
+ * Returns true if the cell format was set as well
+ */
+bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const OUString& rString,
+ formula::FormulaGrammar::AddressConvention eConv,
+ const ScSetStringParam* pParam )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return false;
+
+ ScCellValue aNewCell;
+ bool bNumFmtSet = ParseString(aNewCell, nRow, nTabP, rString, eConv, pParam);
+ if (pParam)
+ aNewCell.release(*this, nRow, pParam->meStartListening);
+ else
+ aNewCell.release(*this, nRow);
+
+ // Do not set Formats and Formulas here anymore!
+ // These are queried during output
+
+ return bNumFmtSet;
+}
+
+void ScColumn::SetEditText( SCROW nRow, std::unique_ptr<EditTextObject> pEditText )
+{
+ pEditText->NormalizeString(GetDoc().GetSharedStringPool());
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, false);
+ maCells.set(it, nRow, pEditText.release());
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ BroadcastNewCell(nRow);
+}
+
+void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, std::unique_ptr<EditTextObject> pEditText )
+{
+ pEditText->NormalizeString(GetDoc().GetSharedStringPool());
+ std::vector<SCROW> aNewSharedRows;
+ rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, false);
+ rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pEditText.release());
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
+ rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ BroadcastNewCell(nRow);
+}
+
+void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const EditTextObject& rEditText )
+{
+ if (GetDoc().GetEditPool() == rEditText.GetPool())
+ {
+ SetEditText(rBlockPos, nRow, rEditText.Clone());
+ return;
+ }
+
+ // rats, yet another "spool"
+ // Sadly there is no other way to change the Pool than to
+ // "spool" the Object through a corresponding Engine
+ EditEngine& rEngine = GetDoc().GetEditEngine();
+ rEngine.SetText(rEditText);
+ SetEditText(rBlockPos, nRow, rEngine.CreateTextObject());
+}
+
+void ScColumn::SetEditText( SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
+{
+ if (pEditPool && GetDoc().GetEditPool() == pEditPool)
+ {
+ SetEditText(nRow, rEditText.Clone());
+ return;
+ }
+
+ // rats, yet another "spool"
+ // Sadly there is no other way to change the Pool than to
+ // "spool" the Object through a corresponding Engine
+ EditEngine& rEngine = GetDoc().GetEditEngine();
+ rEngine.SetText(rEditText);
+ SetEditText(nRow, rEngine.CreateTextObject());
+}
+
+void ScColumn::SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram )
+{
+ ScAddress aPos(nCol, nRow, nTab);
+
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true);
+ ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), aPos, rArray, eGram);
+ sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow);
+ if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ pCell->SetNeedNumberFormat(true);
+ it = maCells.set(it, nRow, pCell);
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows);
+}
+
+void ScColumn::SetFormula( SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram )
+{
+ ScAddress aPos(nCol, nRow, nTab);
+
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true);
+ ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), aPos, rFormula, eGram);
+ sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow);
+ if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ pCell->SetNeedNumberFormat(true);
+ it = maCells.set(it, nRow, pCell);
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows);
+}
+
+ScFormulaCell* ScColumn::SetFormulaCell(
+ SCROW nRow, ScFormulaCell* pCell, sc::StartListeningType eListenType,
+ bool bInheritNumFormatIfNeeded )
+{
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true);
+ sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow);
+ if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && bInheritNumFormatIfNeeded )
+ pCell->SetNeedNumberFormat(true);
+ it = maCells.set(it, nRow, pCell);
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows, true, eListenType);
+
+ return pCell;
+}
+
+void ScColumn::SetFormulaCell(
+ sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScFormulaCell* pCell,
+ sc::StartListeningType eListenType,
+ bool bInheritNumFormatIfNeeded )
+{
+ std::vector<SCROW> aNewSharedRows;
+ rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, true);
+ sal_uInt32 nCellFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow);
+ if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && bInheritNumFormatIfNeeded )
+ pCell->SetNeedNumberFormat(true);
+ rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pCell);
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
+ rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ AttachNewFormulaCell(rBlockPos.miCellPos, nRow, *pCell, aNewSharedRows, true, eListenType);
+}
+
+bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return false;
+
+ SCROW nEndRow = nRow + rCells.size() - 1;
+ if (!GetDoc().ValidRow(nEndRow))
+ return false;
+
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+
+ // Detach all formula cells that will be overwritten.
+ std::vector<SCROW> aNewSharedRows;
+ DetachFormulaCells(aPos, rCells.size(), &aNewSharedRows);
+
+ if (!GetDoc().IsClipOrUndo())
+ {
+ for (size_t i = 0, n = rCells.size(); i < n; ++i)
+ {
+ SCROW nThisRow = nRow + i;
+ sal_uInt32 nFmt = GetNumberFormat(GetDoc().GetNonThreadedContext(), nThisRow);
+ if ((nFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ rCells[i]->SetNeedNumberFormat(true);
+ }
+ }
+
+ std::vector<sc::CellTextAttr> aDefaults(rCells.size(), sc::CellTextAttr());
+ maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end());
+
+ maCells.set(aPos.first, nRow, rCells.begin(), rCells.end());
+
+ CellStorageModified();
+
+ // Reget position_type as the type may have changed to formula, block and
+ // block size changed, ...
+ aPos = maCells.position(nRow);
+ AttachNewFormulaCells(aPos, rCells.size(), aNewSharedRows);
+
+ return true;
+}
+
+svl::SharedString ScColumn::GetSharedString( SCROW nRow ) const
+{
+ sc::CellStoreType::const_position_type aPos = maCells.position(nRow);
+ switch (aPos.first->type)
+ {
+ case sc::element_type_string:
+ return sc::string_block::at(*aPos.first->data, aPos.second);
+ case sc::element_type_edittext:
+ {
+ const EditTextObject* pObj = sc::edittext_block::at(*aPos.first->data, aPos.second);
+ std::vector<svl::SharedString> aSSs = pObj->GetSharedStrings();
+ if (aSSs.size() != 1)
+ // We don't handle multiline content for now.
+ return svl::SharedString();
+
+ return aSSs[0];
+ }
+ break;
+ default:
+ ;
+ }
+ return svl::SharedString();
+}
+
+namespace {
+
+class FilterEntriesHandler
+{
+ ScColumn& mrColumn;
+ ScFilterEntries& mrFilterEntries;
+ bool mbFiltering;
+ bool mbFilteredRow;
+
+ void processCell(const ScColumn& rColumn, SCROW nRow, ScRefCellValue& rCell, bool bIsEmptyCell=false)
+ {
+ SvNumberFormatter* pFormatter = mrColumn.GetDoc().GetFormatTable();
+ sal_uLong nFormat = mrColumn.GetNumberFormat(mrColumn.GetDoc().GetNonThreadedContext(), nRow);
+ OUString aStr = ScCellFormat::GetInputString(rCell, nFormat, *pFormatter, mrColumn.GetDoc(), mbFiltering);
+
+ // Colors
+ ScAddress aPos(rColumn.GetCol(), nRow, rColumn.GetTab());
+ if (ScTable* pTable = rColumn.GetDoc().FetchTable(rColumn.GetTab()))
+ {
+ mrFilterEntries.addTextColor(pTable->GetCellTextColor(aPos));
+ mrFilterEntries.addBackgroundColor(pTable->GetCellBackgroundColor(aPos));
+ }
+
+ if (bIsEmptyCell)
+ {
+ if (!mrFilterEntries.mbHasEmpties)
+ {
+ mrFilterEntries.push_back(ScTypedStrData(OUString()));
+ mrFilterEntries.mbHasEmpties = true;
+ }
+ return;
+ }
+
+ if (rCell.hasString())
+ {
+ mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), 0.0, 0.0, ScTypedStrData::Standard, false, mbFilteredRow));
+ return;
+ }
+
+ double fVal = 0.0;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ fVal = rCell.getDouble();
+ break;
+
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFC = rCell.getFormula();
+ FormulaError nErr = pFC->GetErrCode();
+ if (nErr != FormulaError::NONE)
+ {
+ // Error cell is evaluated as string (for now).
+ OUString aErr = ScGlobal::GetErrorString(nErr);
+ if (!aErr.isEmpty())
+ {
+ mrFilterEntries.push_back(ScTypedStrData(std::move(aErr)));
+ return;
+ }
+ }
+ else
+ fVal = pFC->GetValue();
+ }
+ break;
+ default:
+ ;
+ }
+
+ SvNumFormatType nType = pFormatter->GetType(nFormat);
+ bool bDate = false;
+ if ((nType & SvNumFormatType::DATE) && !(nType & SvNumFormatType::TIME))
+ {
+ // special case for date values. Disregard the time
+ // element if the number format is of date type.
+ fVal = rtl::math::approxFloor(fVal);
+ mrFilterEntries.mbHasDates = true;
+ bDate = true;
+ // Convert string representation to ISO 8601 date to eliminate
+ // locale dependent behaviour later when filtering for dates.
+ sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_YYYYMMDD);
+ pFormatter->GetInputLineString( fVal, nIndex, aStr);
+ }
+ else if (nType == SvNumFormatType::DATETIME)
+ {
+ // special case for datetime values.
+ // Convert string representation to ISO 8601 (with blank instead of T) datetime
+ // to eliminate locale dependent behaviour later when filtering for datetimes.
+ sal_uInt32 nIndex = pFormatter->GetFormatIndex(NF_DATETIME_ISO_YYYYMMDD_HHMMSS);
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ }
+ // store the formatted/rounded value for filtering
+ if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0 && !bDate)
+ mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), fVal, rColumn.GetDoc().RoundValueAsShown(fVal, nFormat), ScTypedStrData::Value, bDate, mbFilteredRow));
+ else
+ mrFilterEntries.push_back(ScTypedStrData(std::move(aStr), fVal, fVal, ScTypedStrData::Value, bDate, mbFilteredRow));
+ }
+
+public:
+ FilterEntriesHandler(ScColumn& rColumn, ScFilterEntries& rFilterEntries, bool bFiltering, bool bFilteredRow) :
+ mrColumn(rColumn), mrFilterEntries(rFilterEntries), mbFiltering(bFiltering), mbFilteredRow(bFilteredRow) {}
+
+ void operator() (size_t nRow, double fVal)
+ {
+ ScRefCellValue aCell(fVal);
+ processCell(mrColumn, nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const svl::SharedString& rStr)
+ {
+ ScRefCellValue aCell(&rStr);
+ processCell(mrColumn, nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const EditTextObject* p)
+ {
+ ScRefCellValue aCell(p);
+ processCell(mrColumn, nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
+ processCell(mrColumn, nRow, aCell);
+ }
+
+ void operator() (const int nElemType, size_t nRow, size_t /* nDataSize */)
+ {
+ ScRefCellValue aCell = mrColumn.GetCellValue(nRow);
+ processCell(mrColumn, nRow, aCell, nElemType == sc::element_type_empty);
+ }
+};
+
+}
+
+void ScColumn::GetFilterEntries(
+ sc::ColumnBlockConstPosition& rBlockPos, SCROW nStartRow, SCROW nEndRow,
+ ScFilterEntries& rFilterEntries, bool bFiltering, bool bFilteredRow )
+{
+ FilterEntriesHandler aFunc(*this, rFilterEntries, bFiltering, bFilteredRow);
+ rBlockPos.miCellPos =
+ sc::ParseAll(rBlockPos.miCellPos, maCells, nStartRow, nEndRow, aFunc, aFunc);
+}
+
+void ScColumn::GetBackColorFilterEntries(SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries)
+{
+ Color aBackColor;
+ bool bCondBackColor = false;
+ ScAddress aCell(GetCol(), 0, GetTab());
+ ScConditionalFormat* pCondFormat = nullptr;
+
+ const SfxItemSet* pCondSet = nullptr;
+ const SvxBrushItem* pBrush = nullptr;
+ const ScPatternAttr* pPattern = nullptr;
+ const ScColorScaleFormat* pColFormat = nullptr;
+
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
+ return;
+
+ while (nRow1 <= nRow2)
+ {
+ aCell.SetRow(nRow1);
+ pPattern = GetDoc().GetPattern(aCell.Col(), aCell.Row(), aCell.Tab());
+ if (pPattern)
+ {
+ if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
+ {
+ pCondSet = GetDoc().GetCondResult(GetCol(), nRow1, GetTab());
+ pBrush = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
+ aBackColor = pBrush->GetColor();
+ bCondBackColor = true;
+ }
+ }
+
+ pCondFormat = GetDoc().GetCondFormat(aCell.Col(), aCell.Row(), aCell.Tab());
+ if (pCondFormat)
+ {
+ for (size_t nFormat = 0; nFormat < pCondFormat->size(); nFormat++)
+ {
+ auto aEntry = pCondFormat->GetEntry(nFormat);
+ if (aEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ {
+ pColFormat = static_cast<const ScColorScaleFormat*>(aEntry);
+ std::optional<Color> oColor = pColFormat->GetColor(aCell);
+ if (oColor)
+ {
+ aBackColor = *oColor;
+ bCondBackColor = true;
+ }
+ }
+ }
+ }
+
+ if (!bCondBackColor)
+ {
+ pBrush = GetDoc().GetAttr(aCell, ATTR_BACKGROUND);
+ aBackColor = pBrush->GetColor();
+ }
+
+ rFilterEntries.addBackgroundColor(aBackColor);
+ nRow1++;
+ }
+}
+
+namespace {
+
+/**
+ * Iterate over only string and edit-text cells.
+ */
+class StrCellIterator
+{
+ typedef std::pair<sc::CellStoreType::const_iterator,size_t> PosType;
+ PosType maPos;
+ sc::CellStoreType::const_iterator miBeg;
+ sc::CellStoreType::const_iterator miEnd;
+ const ScDocument* mpDoc;
+public:
+ StrCellIterator(const sc::CellStoreType& rCells, SCROW nStart, const ScDocument* pDoc) :
+ miBeg(rCells.begin()), miEnd(rCells.end()), mpDoc(pDoc)
+ {
+ if (pDoc->ValidRow(nStart))
+ maPos = rCells.position(nStart);
+ else
+ // Make this iterator invalid.
+ maPos.first = miEnd;
+ }
+
+ bool valid() const { return (maPos.first != miEnd); }
+
+ bool has() const
+ {
+ return (maPos.first->type == sc::element_type_string || maPos.first->type == sc::element_type_edittext);
+ }
+
+ bool prev()
+ {
+ if (!has())
+ {
+ // Not in a string block. Move back until we hit a string block.
+ while (!has())
+ {
+ if (maPos.first == miBeg)
+ return false;
+
+ --maPos.first; // move to the preceding block.
+ maPos.second = maPos.first->size - 1; // last cell in the block.
+ }
+ return true;
+ }
+
+ // We are in a string block.
+ if (maPos.second > 0)
+ {
+ // Move back one cell in the same block.
+ --maPos.second;
+ }
+ else
+ {
+ // Move back to the preceding string block.
+ while (true)
+ {
+ if (maPos.first == miBeg)
+ return false;
+
+ // Move to the last cell of the previous block.
+ --maPos.first;
+ maPos.second = maPos.first->size - 1;
+ if (has())
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool next()
+ {
+ if (!has())
+ {
+ // Not in a string block. Move forward until we hit a string block.
+ while (!has())
+ {
+ ++maPos.first;
+ if (maPos.first == miEnd)
+ return false;
+
+ maPos.second = 0; // First cell in this block.
+ }
+ return true;
+ }
+
+ // We are in a string block.
+ ++maPos.second;
+ if (maPos.second >= maPos.first->size)
+ {
+ // Move to the next string block.
+ while (true)
+ {
+ ++maPos.first;
+ if (maPos.first == miEnd)
+ return false;
+
+ maPos.second = 0;
+ if (has())
+ break;
+ }
+ }
+ return true;
+ }
+
+ OUString get() const
+ {
+ switch (maPos.first->type)
+ {
+ case sc::element_type_string:
+ return sc::string_block::at(*maPos.first->data, maPos.second).getString();
+ case sc::element_type_edittext:
+ {
+ const EditTextObject* p = sc::edittext_block::at(*maPos.first->data, maPos.second);
+ return ScEditUtil::GetString(*p, mpDoc);
+ }
+ default:
+ ;
+ }
+ return OUString();
+ }
+};
+
+}
+
+// GetDataEntries - Strings from continuous Section around nRow
+bool ScColumn::GetDataEntries(
+ SCROW nStartRow, std::set<ScTypedStrData>& rStrings) const
+{
+ // Start at the specified row position, and collect all string values
+ // going upward and downward directions in parallel. The start position
+ // cell must be skipped.
+
+ StrCellIterator aItrUp(maCells, nStartRow, &GetDoc());
+ StrCellIterator aItrDown(maCells, nStartRow+1, &GetDoc());
+
+ bool bMoveUp = aItrUp.valid();
+ if (!bMoveUp)
+ // Current cell is invalid.
+ return false;
+
+ // Skip the start position cell.
+ bMoveUp = aItrUp.prev(); // Find the previous string cell position.
+
+ bool bMoveDown = aItrDown.valid();
+ if (bMoveDown && !aItrDown.has())
+ bMoveDown = aItrDown.next(); // Find the next string cell position.
+
+ bool bFound = false;
+ while (bMoveUp)
+ {
+ // Get the current string and move up.
+ OUString aStr = aItrUp.get();
+ if (!aStr.isEmpty())
+ {
+ if (rStrings.insert(ScTypedStrData(std::move(aStr))).second)
+ bFound = true;
+ }
+
+ bMoveUp = aItrUp.prev();
+ }
+
+ while (bMoveDown)
+ {
+ // Get the current string and move down.
+ OUString aStr = aItrDown.get();
+ if (!aStr.isEmpty())
+ {
+ if (rStrings.insert(ScTypedStrData(std::move(aStr))).second)
+ bFound = true;
+ }
+
+ bMoveDown = aItrDown.next();
+ }
+
+ return bFound;
+}
+
+namespace {
+
+class FormulaToValueHandler
+{
+ struct Entry
+ {
+ SCROW mnRow;
+ ScCellValue maValue;
+
+ Entry(SCROW nRow, double f) : mnRow(nRow), maValue(f) {}
+ Entry(SCROW nRow, const svl::SharedString& rStr) : mnRow(nRow), maValue(rStr) {}
+ };
+
+ typedef std::vector<Entry> EntriesType;
+ EntriesType maEntries;
+
+public:
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ ScFormulaCell* p2 = const_cast<ScFormulaCell*>(p);
+ if (p2->IsValue())
+ maEntries.emplace_back(nRow, p2->GetValue());
+ else
+ maEntries.emplace_back(nRow, p2->GetString());
+ }
+
+ void commitCells(ScColumn& rColumn)
+ {
+ sc::ColumnBlockPosition aBlockPos;
+ rColumn.InitBlockPosition(aBlockPos);
+
+ for (const Entry& r : maEntries)
+ {
+ switch (r.maValue.getType())
+ {
+ case CELLTYPE_VALUE:
+ rColumn.SetValue(aBlockPos, r.mnRow, r.maValue.getDouble(), false);
+ break;
+ case CELLTYPE_STRING:
+ rColumn.SetRawString(aBlockPos, r.mnRow, *r.maValue.getSharedString(), false);
+ break;
+ default:
+ ;
+ }
+ }
+ }
+};
+
+}
+
+void ScColumn::RemoveProtected( SCROW nStartRow, SCROW nEndRow )
+{
+ FormulaToValueHandler aFunc;
+ sc::CellStoreType::const_iterator itPos = maCells.begin();
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), nStartRow, nEndRow, GetDoc().GetDefPattern() );
+ SCROW nTop = -1;
+ SCROW nBottom = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nTop, nBottom );
+ while (pPattern)
+ {
+ const ScProtectionAttr* pAttr = &pPattern->GetItem(ATTR_PROTECTION);
+ if ( pAttr->GetHideCell() )
+ DeleteArea( nTop, nBottom, InsertDeleteFlags::CONTENTS );
+ else if ( pAttr->GetHideFormula() )
+ {
+ // Replace all formula cells between nTop and nBottom with raw value cells.
+ itPos = sc::ParseFormula(itPos, maCells, nTop, nBottom, aFunc);
+ }
+
+ pPattern = aAttrIter.Next( nTop, nBottom );
+ }
+
+ aFunc.commitCells(*this);
+}
+
+void ScColumn::SetError( SCROW nRow, const FormulaError nError)
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), ScAddress(nCol, nRow, nTab));
+ pCell->SetErrCode(nError);
+
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, true);
+ it = maCells.set(it, nRow, pCell);
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows);
+}
+
+void ScColumn::SetRawString( SCROW nRow, const OUString& rStr )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ svl::SharedString aSS = GetDoc().GetSharedStringPool().intern(rStr);
+ if (!aSS.getData())
+ return;
+
+ SetRawString(nRow, aSS);
+}
+
+void ScColumn::SetRawString( SCROW nRow, const svl::SharedString& rStr )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, false);
+ maCells.set(it, nRow, rStr);
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ BroadcastNewCell(nRow);
+}
+
+void ScColumn::SetRawString(
+ sc::ColumnBlockPosition& rBlockPos, SCROW nRow, const svl::SharedString& rStr, bool bBroadcast )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ std::vector<SCROW> aNewSharedRows;
+ rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, false);
+ rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, rStr);
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
+ rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ if (bBroadcast)
+ BroadcastNewCell(nRow);
+}
+
+void ScColumn::SetValue( SCROW nRow, double fVal )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ std::vector<SCROW> aNewSharedRows;
+ sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows, false);
+ maCells.set(it, nRow, fVal);
+ maCellTextAttrs.set(nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ BroadcastNewCell(nRow);
+}
+
+void ScColumn::SetValue(
+ sc::ColumnBlockPosition& rBlockPos, SCROW nRow, double fVal, bool bBroadcast )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ std::vector<SCROW> aNewSharedRows;
+ rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows, false);
+ rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, fVal);
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
+ rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ if (bBroadcast)
+ BroadcastNewCell(nRow);
+}
+
+OUString ScColumn::GetString( const ScRefCellValue& aCell, SCROW nRow, const ScInterpreterContext* pContext ) const
+{
+ // ugly hack for ordering problem with GetNumberFormat and missing inherited formats
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ aCell.getFormula()->MaybeInterpret();
+
+ sal_uInt32 nFormat = GetNumberFormat( pContext ? *pContext : GetDoc().GetNonThreadedContext(), nRow);
+ const Color* pColor = nullptr;
+ return ScCellFormat::GetString(aCell, nFormat, &pColor,
+ pContext ? *(pContext->GetFormatTable()) : *(GetDoc().GetFormatTable()), GetDoc());
+}
+
+double* ScColumn::GetValueCell( SCROW nRow )
+{
+ std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it == maCells.end())
+ return nullptr;
+
+ if (it->type != sc::element_type_numeric)
+ return nullptr;
+
+ return &sc::numeric_block::at(*it->data, aPos.second);
+}
+
+OUString ScColumn::GetInputString( const ScRefCellValue& aCell, SCROW nRow, bool bForceSystemLocale ) const
+{
+ sal_uLong nFormat = GetNumberFormat(GetDoc().GetNonThreadedContext(), nRow);
+ return ScCellFormat::GetInputString(aCell, nFormat, *(GetDoc().GetFormatTable()), GetDoc(), nullptr, false, bForceSystemLocale);
+}
+
+double ScColumn::GetValue( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ switch (it->type)
+ {
+ case sc::element_type_numeric:
+ return sc::numeric_block::at(*it->data, aPos.second);
+ case sc::element_type_formula:
+ {
+ const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ ScFormulaCell* p2 = const_cast<ScFormulaCell*>(p);
+ return p2->IsValue() ? p2->GetValue() : 0.0;
+ }
+ default:
+ ;
+ }
+
+ return 0.0;
+}
+
+const EditTextObject* ScColumn::GetEditText( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ return nullptr;
+
+ if (it->type != sc::element_type_edittext)
+ return nullptr;
+
+ return sc::edittext_block::at(*it->data, aPos.second);
+}
+
+void ScColumn::RemoveEditTextCharAttribs( SCROW nRow, const ScPatternAttr& rAttr )
+{
+ std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it == maCells.end())
+ return;
+
+ if (it->type != sc::element_type_edittext)
+ return;
+
+ EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second);
+ ScEditUtil::RemoveCharAttribs(*p, rAttr);
+}
+
+OUString ScColumn::GetFormula( SCROW nRow ) const
+{
+ const ScFormulaCell* p = FetchFormulaCell(nRow);
+ if (p)
+ return p->GetFormula();
+ return OUString();
+}
+
+const ScFormulaCell* ScColumn::GetFormulaCell( SCROW nRow ) const
+{
+ return FetchFormulaCell(nRow);
+}
+
+ScFormulaCell* ScColumn::GetFormulaCell( SCROW nRow )
+{
+ return const_cast<ScFormulaCell*>(FetchFormulaCell(nRow));
+}
+
+CellType ScColumn::GetCellType( SCROW nRow ) const
+{
+ switch (maCells.get_type(nRow))
+ {
+ case sc::element_type_numeric:
+ return CELLTYPE_VALUE;
+ case sc::element_type_string:
+ return CELLTYPE_STRING;
+ case sc::element_type_edittext:
+ return CELLTYPE_EDIT;
+ case sc::element_type_formula:
+ return CELLTYPE_FORMULA;
+ default:
+ ;
+ }
+ return CELLTYPE_NONE;
+}
+
+namespace {
+
+/**
+ * Count the number of all non-empty cells.
+ */
+class CellCounter
+{
+ size_t mnCount;
+public:
+ CellCounter() : mnCount(0) {}
+
+ void operator() (const sc::CellStoreType::value_type& node)
+ {
+ if (node.type == sc::element_type_empty)
+ return;
+
+ mnCount += node.size;
+ }
+
+ size_t getCount() const { return mnCount; }
+};
+
+}
+
+SCSIZE ScColumn::GetCellCount() const
+{
+ CellCounter aFunc;
+ aFunc = std::for_each(maCells.begin(), maCells.end(), aFunc);
+ return aFunc.getCount();
+}
+
+FormulaError ScColumn::GetErrCode( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it == maCells.end())
+ return FormulaError::NONE;
+
+ if (it->type != sc::element_type_formula)
+ return FormulaError::NONE;
+
+ const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ return const_cast<ScFormulaCell*>(p)->GetErrCode();
+}
+
+bool ScColumn::HasStringData( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ switch (aPos.first->type)
+ {
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ return true;
+ case sc::element_type_formula:
+ {
+ const ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second);
+ return !const_cast<ScFormulaCell*>(p)->IsValue();
+ }
+ default:
+ ;
+ }
+
+ return false;
+}
+
+bool ScColumn::HasValueData( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ switch (aPos.first->type)
+ {
+ case sc::element_type_numeric:
+ return true;
+ case sc::element_type_formula:
+ {
+ const ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second);
+ return const_cast<ScFormulaCell*>(p)->IsValue();
+ }
+ default:
+ ;
+ }
+
+ return false;
+}
+
+/**
+ * Return true if there is a string or editcell in the range
+ */
+bool ScColumn::HasStringCells( SCROW nStartRow, SCROW nEndRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ SCROW nRow = nStartRow;
+ for (; it != maCells.end() && nRow <= nEndRow; ++it)
+ {
+ if (it->type == sc::element_type_string || it->type == sc::element_type_edittext)
+ return true;
+
+ nRow += it->size - nOffset;
+ nOffset = 0;
+ }
+
+ return false;
+}
+
+namespace {
+
+class MaxStringLenHandler
+{
+ sal_Int32 mnMaxLen;
+ const ScColumn& mrColumn;
+ SvNumberFormatter* mpFormatter;
+ rtl_TextEncoding meCharSet;
+ bool mbOctetEncoding;
+
+ void processCell(size_t nRow, const ScRefCellValue& rCell)
+ {
+ const Color* pColor;
+ sal_uInt32 nFormat = mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT).GetValue();
+ OUString aString = ScCellFormat::GetString(rCell, nFormat, &pColor, *mpFormatter, mrColumn.GetDoc());
+ sal_Int32 nLen = 0;
+ if (mbOctetEncoding)
+ {
+ OString aOString;
+ if (!aString.convertToString(&aOString, meCharSet,
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
+ {
+ // TODO: anything? this is used by the dBase export filter
+ // that throws an error anyway, but in case of another
+ // context we might want to indicate a conversion error
+ // early.
+ }
+ nLen = aOString.getLength();
+ }
+ else
+ nLen = aString.getLength() * sizeof(sal_Unicode);
+
+ if (mnMaxLen < nLen)
+ mnMaxLen = nLen;
+ }
+
+public:
+ MaxStringLenHandler(const ScColumn& rColumn, rtl_TextEncoding eCharSet) :
+ mnMaxLen(0),
+ mrColumn(rColumn),
+ mpFormatter(rColumn.GetDoc().GetFormatTable()),
+ meCharSet(eCharSet),
+ mbOctetEncoding(rtl_isOctetTextEncoding(eCharSet))
+ {
+ }
+
+ void operator() (size_t nRow, double fVal)
+ {
+ ScRefCellValue aCell(fVal);
+ processCell(nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const svl::SharedString& rStr)
+ {
+ ScRefCellValue aCell(&rStr);
+ processCell(nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const EditTextObject* p)
+ {
+ ScRefCellValue aCell(p);
+ processCell(nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
+ processCell(nRow, aCell);
+ }
+
+ sal_Int32 getMaxLen() const { return mnMaxLen; }
+};
+
+}
+
+sal_Int32 ScColumn::GetMaxStringLen( SCROW nRowStart, SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
+{
+ MaxStringLenHandler aFunc(*this, eCharSet);
+ sc::ParseAllNonEmpty(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc);
+ return aFunc.getMaxLen();
+}
+
+namespace {
+
+class MaxNumStringLenHandler
+{
+ const ScColumn& mrColumn;
+ SvNumberFormatter* mpFormatter;
+ sal_Int32 mnMaxLen;
+ sal_uInt16 mnPrecision;
+ sal_uInt16 mnMaxGeneralPrecision;
+ bool mbHaveSigned;
+
+ void processCell(size_t nRow, ScRefCellValue& rCell)
+ {
+ sal_uInt16 nCellPrecision = mnMaxGeneralPrecision;
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ {
+ if (!rCell.getFormula()->IsValue())
+ return;
+
+ // Limit unformatted formula cell precision to precision
+ // encountered so far, if any, otherwise we'd end up with 15 just
+ // because of =1/3 ... If no precision yet then arbitrarily limit
+ // to a maximum of 4 unless a maximum general precision is set.
+ if (mnPrecision)
+ nCellPrecision = mnPrecision;
+ else
+ nCellPrecision = (mnMaxGeneralPrecision >= 15) ? 4 : mnMaxGeneralPrecision;
+ }
+
+ double fVal = rCell.getValue();
+ if (!mbHaveSigned && fVal < 0.0)
+ mbHaveSigned = true;
+
+ OUString aString;
+ OUString aSep;
+ sal_uInt16 nPrec;
+ sal_uInt32 nFormat =
+ mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT).GetValue();
+ if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
+ {
+ aSep = mpFormatter->GetFormatDecimalSep(nFormat);
+ aString = ScCellFormat::GetInputString(rCell, nFormat, *mpFormatter, mrColumn.GetDoc());
+ const SvNumberformat* pEntry = mpFormatter->GetEntry(nFormat);
+ if (pEntry)
+ {
+ bool bThousand, bNegRed;
+ sal_uInt16 nLeading;
+ pEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrec, nLeading);
+ }
+ else
+ nPrec = mpFormatter->GetFormatPrecision(nFormat);
+ }
+ else
+ {
+ if (mnPrecision >= mnMaxGeneralPrecision)
+ return; // early bail out for nothing changes here
+
+ if (!fVal)
+ {
+ // 0 doesn't change precision, but set a maximum length if none yet.
+ if (!mnMaxLen)
+ mnMaxLen = 1;
+ return;
+ }
+
+ // Simple number string with at most 15 decimals and trailing
+ // decimal zeros eliminated.
+ aSep = ".";
+ aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true);
+ nPrec = SvNumberFormatter::UNLIMITED_PRECISION;
+ }
+
+ sal_Int32 nLen = aString.getLength();
+ if (nLen <= 0)
+ // Ignore empty string.
+ return;
+
+ if (nPrec == SvNumberFormatter::UNLIMITED_PRECISION && mnPrecision < mnMaxGeneralPrecision)
+ {
+ if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
+ {
+ // For some reason we couldn't obtain a precision from the
+ // format, retry with simple number string.
+ aSep = ".";
+ aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true);
+ nLen = aString.getLength();
+ }
+ sal_Int32 nSep = aString.indexOf( aSep);
+ if (nSep != -1)
+ nPrec = aString.getLength() - nSep - 1;
+
+ }
+
+ if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision)
+ mnPrecision = nPrec;
+
+ if (mnPrecision)
+ { // less than mnPrecision in string => widen it
+ // more => shorten it
+ sal_Int32 nTmp = aString.indexOf(aSep);
+ if ( nTmp == -1 )
+ nLen += mnPrecision + aSep.getLength();
+ else
+ {
+ nTmp = aString.getLength() - (nTmp + aSep.getLength());
+ if (nTmp != mnPrecision)
+ nLen += mnPrecision - nTmp;
+ // nPrecision > nTmp : nLen + Diff
+ // nPrecision < nTmp : nLen - Diff
+ }
+ }
+
+ // Enlarge for sign if necessary. Bear in mind that
+ // GetMaxNumberStringLen() is for determining dBase decimal field width
+ // and precision where the overall field width must include the sign.
+ // Fitting -1 into "#.##" (width 4, 2 decimals) does not work.
+ if (mbHaveSigned && fVal >= 0.0)
+ ++nLen;
+
+ if (mnMaxLen < nLen)
+ mnMaxLen = nLen;
+ }
+
+public:
+ MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nMaxGeneralPrecision) :
+ mrColumn(rColumn), mpFormatter(rColumn.GetDoc().GetFormatTable()),
+ mnMaxLen(0), mnPrecision(0), mnMaxGeneralPrecision(nMaxGeneralPrecision),
+ mbHaveSigned(false)
+ {
+ // Limit the decimals passed to doubleToUString().
+ // Also, the dBaseIII maximum precision is 15.
+ if (mnMaxGeneralPrecision > 15)
+ mnMaxGeneralPrecision = 15;
+ }
+
+ void operator() (size_t nRow, double fVal)
+ {
+ ScRefCellValue aCell(fVal);
+ processCell(nRow, aCell);
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
+ processCell(nRow, aCell);
+ }
+
+ sal_Int32 getMaxLen() const { return mnMaxLen; }
+
+ sal_uInt16 getPrecision() const { return mnPrecision; }
+};
+
+}
+
+sal_Int32 ScColumn::GetMaxNumberStringLen(
+ sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const
+{
+ sal_uInt16 nMaxGeneralPrecision = GetDoc().GetDocOptions().GetStdPrecision();
+ MaxNumStringLenHandler aFunc(*this, nMaxGeneralPrecision);
+ sc::ParseFormulaNumeric(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc);
+ nPrecision = aFunc.getPrecision();
+ return aFunc.getMaxLen();
+}
+
+namespace {
+
+class GroupFormulaCells
+{
+ std::vector<ScAddress>* mpGroupPos;
+
+public:
+ explicit GroupFormulaCells(std::vector<ScAddress>* pGroupPos)
+ : mpGroupPos(pGroupPos) {}
+
+ void operator() (sc::CellStoreType::value_type& node)
+ {
+ if (node.type != sc::element_type_formula)
+ // We are only interested in formula cells.
+ return;
+
+ size_t nRow = node.position; // start row position.
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*node.data);
+ sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data);
+
+ // This block should never be empty.
+
+ ScFormulaCell* pPrev = *it;
+ ScFormulaCellGroupRef xPrevGrp = pPrev->GetCellGroup();
+ if (xPrevGrp)
+ {
+ // Move to the cell after the last cell of the current group.
+ std::advance(it, xPrevGrp->mnLength);
+ nRow += xPrevGrp->mnLength;
+ }
+ else
+ {
+ ++it;
+ ++nRow;
+ }
+
+ ScFormulaCell* pCur = nullptr;
+ ScFormulaCellGroupRef xCurGrp;
+ for (; it != itEnd; pPrev = pCur, xPrevGrp = xCurGrp)
+ {
+ pCur = *it;
+ xCurGrp = pCur->GetCellGroup();
+
+ ScFormulaCell::CompareState eCompState = pPrev->CompareByTokenArray(*pCur);
+ if (eCompState == ScFormulaCell::NotEqual)
+ {
+ // different formula tokens.
+ if (xCurGrp)
+ {
+ // Move to the cell after the last cell of the current group.
+ if (xCurGrp->mnLength > std::distance(it, itEnd))
+ throw css::lang::IllegalArgumentException();
+ std::advance(it, xCurGrp->mnLength);
+ nRow += xCurGrp->mnLength;
+ }
+ else
+ {
+ ++it;
+ ++nRow;
+ }
+
+ continue;
+ }
+
+ // Formula tokens equal those of the previous formula cell or cell group.
+ if (xPrevGrp)
+ {
+ // Previous cell is a group.
+ if (xCurGrp)
+ {
+ // The current cell is a group. Merge these two groups.
+ xPrevGrp->mnLength += xCurGrp->mnLength;
+ pCur->SetCellGroup(xPrevGrp);
+ sc::formula_block::iterator itGrpEnd = it;
+ if (xCurGrp->mnLength > std::distance(itGrpEnd, itEnd))
+ throw css::lang::IllegalArgumentException();
+ std::advance(itGrpEnd, xCurGrp->mnLength);
+ for (++it; it != itGrpEnd; ++it)
+ {
+ ScFormulaCell* pCell = *it;
+ pCell->SetCellGroup(xPrevGrp);
+ }
+ nRow += xCurGrp->mnLength;
+ }
+ else
+ {
+ // Add this cell to the previous group.
+ pCur->SetCellGroup(xPrevGrp);
+ ++xPrevGrp->mnLength;
+ ++nRow;
+ ++it;
+ }
+
+ }
+ else if (xCurGrp)
+ {
+ // Previous cell is a regular cell and current cell is a group.
+ nRow += xCurGrp->mnLength;
+ if (xCurGrp->mnLength > std::distance(it, itEnd))
+ throw css::lang::IllegalArgumentException();
+ std::advance(it, xCurGrp->mnLength);
+ pPrev->SetCellGroup(xCurGrp);
+ xCurGrp->mpTopCell = pPrev;
+ ++xCurGrp->mnLength;
+ xPrevGrp = xCurGrp;
+ }
+ else
+ {
+ // Both previous and current cells are regular cells.
+ assert(pPrev->aPos.Row() == static_cast<SCROW>(nRow - 1));
+ // silence set-but-unused warning for non-dbg build
+ (void) nRow;
+ xPrevGrp = pPrev->CreateCellGroup(2, eCompState == ScFormulaCell::EqualInvariant);
+ pCur->SetCellGroup(xPrevGrp);
+ ++nRow;
+ ++it;
+ }
+
+ if (mpGroupPos)
+ mpGroupPos->push_back(pCur->aPos);
+
+ pCur = pPrev;
+ xCurGrp = xPrevGrp;
+ }
+ }
+};
+
+}
+
+void ScColumn::RegroupFormulaCells( std::vector<ScAddress>* pGroupPos )
+{
+ // re-build formula groups.
+ std::for_each(maCells.begin(), maCells.end(), GroupFormulaCells(pGroupPos));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
new file mode 100644
index 0000000000..324e54be2a
--- /dev/null
+++ b/sc/source/core/data/column4.cxx
@@ -0,0 +1,2266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <column.hxx>
+#include <clipparam.hxx>
+#include <cellvalue.hxx>
+#include <attarray.hxx>
+#include <document.hxx>
+#include <cellvalues.hxx>
+#include <columnspanset.hxx>
+#include <columniterator.hxx>
+#include <mtvcellfunc.hxx>
+#include <clipcontext.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <conditio.hxx>
+#include <formulagroup.hxx>
+#include <tokenarray.hxx>
+#include <scitems.hxx>
+#include <cellform.hxx>
+#include <sharedformula.hxx>
+#include <drwlayer.hxx>
+#include <compiler.hxx>
+#include <recursionhelper.hxx>
+#include <docsh.hxx>
+#include <editutil.hxx>
+#include <broadcast.hxx>
+
+#include <SparklineGroup.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+
+#include <numeric>
+#include <vector>
+#include <cassert>
+
+sc::MultiDataCellState::StateType ScColumn::HasDataCellsInRange(
+ SCROW nRow1, SCROW nRow2, SCROW* pRow1 ) const
+{
+ sc::CellStoreType::const_position_type aPos = maCells.position(nRow1);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ SCROW nRow = nRow1;
+ bool bHasOne = false; // whether or not we have found a non-empty block of size one.
+
+ for (; it != maCells.end() && nRow <= nRow2; ++it)
+ {
+ if (it->type != sc::element_type_empty)
+ {
+ // non-empty block found.
+ assert(it->size > 0); // mtv should never contain a block of zero length.
+ size_t nSize = it->size - nOffset;
+
+ SCROW nLastRow = nRow + nSize - 1;
+ if (nLastRow > nRow2)
+ // shrink the size to avoid exceeding the specified last row position.
+ nSize -= nLastRow - nRow2;
+
+ if (nSize == 1)
+ {
+ // this block is of size one.
+ if (bHasOne)
+ return sc::MultiDataCellState::HasMultipleCells;
+
+ bHasOne = true;
+ if (pRow1)
+ *pRow1 = nRow;
+ }
+ else
+ {
+ // size of this block is greater than one.
+ if (pRow1)
+ *pRow1 = nRow;
+ return sc::MultiDataCellState::HasMultipleCells;
+ }
+ }
+
+ nRow += it->size - nOffset;
+ nOffset = 0;
+ }
+
+ return bHasOne ? sc::MultiDataCellState::HasOneCell : sc::MultiDataCellState::Empty;
+}
+
+void ScColumn::DeleteBeforeCopyFromClip(
+ sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ ScDocument& rDocument = GetDoc();
+ sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
+ if (!rDocument.ValidRow(aRange.mnRow1) || !rDocument.ValidRow(aRange.mnRow2))
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
+ if (!pBlockPos)
+ return;
+
+ InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag();
+
+ if (!rCxt.isSkipEmptyCells())
+ {
+ // Delete the whole destination range.
+
+ if (nDelFlag & InsertDeleteFlags::CONTENTS)
+ {
+ auto xResult = DeleteCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2, nDelFlag);
+ rBroadcastSpans.set(GetDoc(), nTab, nCol, xResult->aDeletedRows, true);
+
+ for (const auto& rRange : xResult->aFormulaRanges)
+ rCxt.setListeningFormulaSpans(
+ nTab, nCol, rRange.first, nCol, rRange.second);
+ }
+
+ if (nDelFlag & InsertDeleteFlags::NOTE)
+ DeleteCellNotes(*pBlockPos, aRange.mnRow1, aRange.mnRow2, false);
+
+ if (nDelFlag & InsertDeleteFlags::SPARKLINES)
+ DeleteSparklineCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
+
+ if (nDelFlag & InsertDeleteFlags::EDITATTR)
+ RemoveEditAttribs(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
+
+ if (nDelFlag & InsertDeleteFlags::ATTRIB)
+ {
+ pAttrArray->DeleteArea(aRange.mnRow1, aRange.mnRow2);
+
+ if (rCxt.isTableProtected())
+ {
+ ScPatternAttr aPattern(rDocument.GetPool());
+ aPattern.GetItemSet().Put(ScProtectionAttr(false));
+ ApplyPatternArea(aRange.mnRow1, aRange.mnRow2, aPattern);
+ }
+
+ ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
+ if (pCondList)
+ pCondList->DeleteArea(nCol, aRange.mnRow1, nCol, aRange.mnRow2);
+ }
+ else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
+ pAttrArray->DeleteHardAttr(aRange.mnRow1, aRange.mnRow2);
+
+ return;
+ }
+
+ ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
+ SCROW nClipRow1 = aClipRange.aStart.Row();
+ SCROW nClipRow2 = aClipRange.aEnd.Row();
+ SCROW nClipRowLen = nClipRow2 - nClipRow1 + 1;
+
+ // Check for non-empty cell ranges in the clip column.
+ sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
+ aSpanSet.scan(rClipCol, nClipRow1, nClipRow2);
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ aSpanSet.getSpans(aSpans);
+
+ if (aSpans.empty())
+ // All cells in the range in the clip are empty. Nothing to delete.
+ return;
+
+ // Translate the clip column spans into the destination column, and repeat as needed.
+ std::vector<sc::RowSpan> aDestSpans;
+ SCROW nDestOffset = aRange.mnRow1 - nClipRow1;
+ bool bContinue = true;
+ while (bContinue)
+ {
+ for (const sc::RowSpan& r : aSpans)
+ {
+ SCROW nDestRow1 = r.mnRow1 + nDestOffset;
+ SCROW nDestRow2 = r.mnRow2 + nDestOffset;
+
+ if (nDestRow1 > aRange.mnRow2)
+ {
+ // We're done.
+ bContinue = false;
+ break;
+ }
+
+ if (nDestRow2 > aRange.mnRow2)
+ {
+ // Truncate this range, and set it as the last span.
+ nDestRow2 = aRange.mnRow2;
+ bContinue = false;
+ }
+
+ aDestSpans.emplace_back(nDestRow1, nDestRow2);
+
+ if (!bContinue)
+ break;
+ }
+
+ nDestOffset += nClipRowLen;
+ }
+
+ for (const auto& rDestSpan : aDestSpans)
+ {
+ SCROW nRow1 = rDestSpan.mnRow1;
+ SCROW nRow2 = rDestSpan.mnRow2;
+
+ if (nDelFlag & InsertDeleteFlags::CONTENTS)
+ {
+ auto xResult = DeleteCells(*pBlockPos, nRow1, nRow2, nDelFlag);
+ rBroadcastSpans.set(GetDoc(), nTab, nCol, xResult->aDeletedRows, true);
+
+ for (const auto& rRange : xResult->aFormulaRanges)
+ rCxt.setListeningFormulaSpans(
+ nTab, nCol, rRange.first, nCol, rRange.second);
+ }
+
+ if (nDelFlag & InsertDeleteFlags::NOTE)
+ DeleteCellNotes(*pBlockPos, nRow1, nRow2, false);
+
+ if (nDelFlag & InsertDeleteFlags::SPARKLINES)
+ DeleteSparklineCells(*pBlockPos, nRow1, nRow2);
+
+ if (nDelFlag & InsertDeleteFlags::EDITATTR)
+ RemoveEditAttribs(*pBlockPos, nRow1, nRow2);
+
+ // Delete attributes just now
+ if (nDelFlag & InsertDeleteFlags::ATTRIB)
+ {
+ pAttrArray->DeleteArea(nRow1, nRow2);
+
+ if (rCxt.isTableProtected())
+ {
+ ScPatternAttr aPattern(rDocument.GetPool());
+ aPattern.GetItemSet().Put(ScProtectionAttr(false));
+ ApplyPatternArea(nRow1, nRow2, aPattern);
+ }
+
+ ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
+ if (pCondList)
+ pCondList->DeleteArea(nCol, nRow1, nCol, nRow2);
+ }
+ else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
+ pAttrArray->DeleteHardAttr(nRow1, nRow2);
+ }
+}
+
+void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset )
+{
+ assert(nRow1 <= nRow2);
+
+ size_t nDestSize = nRow2 - nRow1 + 1;
+ sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
+ if (!pBlockPos)
+ return;
+
+ ScDocument& rDocument = GetDoc();
+ bool bSameDocPool = (rCxt.getClipDoc()->GetPool() == rDocument.GetPool());
+
+ ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset);
+ sc::CellTextAttr& rSrcAttr = rCxt.getSingleCellAttr(nColOffset);
+
+ InsertDeleteFlags nFlags = rCxt.getInsertFlag();
+
+ if ((nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE)
+ {
+ if (!rCxt.isSkipEmptyCells() || rSrcCell.getType() != CELLTYPE_NONE)
+ {
+ const ScPatternAttr* pAttr = (bSameDocPool ? rCxt.getSingleCellPattern(nColOffset) :
+ rCxt.getSingleCellPattern(nColOffset)->PutInPool( &rDocument, rCxt.getClipDoc()));
+
+ pAttrArray->SetPatternArea(nRow1, nRow2, pAttr, true);
+ }
+ }
+
+ if ((nFlags & InsertDeleteFlags::CONTENTS) != InsertDeleteFlags::NONE)
+ {
+ std::vector<sc::CellTextAttr> aTextAttrs(nDestSize, rSrcAttr);
+
+ switch (rSrcCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ std::vector<double> aVals(nDestSize, rSrcCell.getDouble());
+ pBlockPos->miCellPos =
+ maCells.set(pBlockPos->miCellPos, nRow1, aVals.begin(), aVals.end());
+ pBlockPos->miCellTextAttrPos =
+ maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
+ CellStorageModified();
+ }
+ break;
+ case CELLTYPE_STRING:
+ {
+ // Compare the ScDocumentPool* to determine if we are copying within the
+ // same document. If not, re-intern shared strings.
+ svl::SharedStringPool* pSharedStringPool = (bSameDocPool ? nullptr : &rDocument.GetSharedStringPool());
+ svl::SharedString aStr = (pSharedStringPool ?
+ pSharedStringPool->intern( rSrcCell.getSharedString()->getString()) :
+ *rSrcCell.getSharedString());
+
+ std::vector<svl::SharedString> aStrs(nDestSize, aStr);
+ pBlockPos->miCellPos =
+ maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
+ pBlockPos->miCellTextAttrPos =
+ maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
+ CellStorageModified();
+ }
+ break;
+ case CELLTYPE_EDIT:
+ {
+ std::vector<EditTextObject*> aStrs;
+ aStrs.reserve(nDestSize);
+ for (size_t i = 0; i < nDestSize; ++i)
+ aStrs.push_back(rSrcCell.getEditText()->Clone().release());
+
+ pBlockPos->miCellPos =
+ maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
+ pBlockPos->miCellTextAttrPos =
+ maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
+ CellStorageModified();
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ std::vector<sc::RowSpan> aRanges;
+ aRanges.reserve(1);
+ aRanges.emplace_back(nRow1, nRow2);
+ CloneFormulaCell(*pBlockPos, *rSrcCell.getFormula(), rSrcAttr, aRanges);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ ScAddress aDestPosition(nCol, nRow1, nTab);
+
+ duplicateSparkline(rCxt, pBlockPos, nColOffset, nDestSize, aDestPosition);
+
+ // Notes
+ const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset);
+ if (!(pNote && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE))
+ return;
+
+ // Duplicate the cell note over the whole pasted range.
+
+ ScDocument* pClipDoc = rCxt.getClipDoc();
+ const ScAddress aSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart;
+ std::vector<ScPostIt*> aNotes;
+ aNotes.reserve(nDestSize);
+ for (size_t i = 0; i < nDestSize; ++i)
+ {
+ bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ aNotes.push_back(pNote->Clone(aSrcPos, rDocument, aDestPosition, bCloneCaption).release());
+ aDestPosition.IncRow();
+ }
+
+ pBlockPos->miCellNotePos =
+ maCellNotes.set(
+ pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end());
+
+ // Notify our LOK clients.
+ aDestPosition.SetRow(nRow1);
+ for (size_t i = 0; i < nDestSize; ++i)
+ {
+ ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, rDocument, aDestPosition, aNotes[i]);
+ aDestPosition.IncRow();
+ }
+}
+
+void ScColumn::duplicateSparkline(sc::CopyFromClipContext& rContext, sc::ColumnBlockPosition* pBlockPos,
+ size_t nColOffset, size_t nDestSize, ScAddress aDestPosition)
+{
+ if ((rContext.getInsertFlag() & InsertDeleteFlags::SPARKLINES) == InsertDeleteFlags::NONE)
+ return;
+
+ auto pSparkline = rContext.getSingleSparkline(nColOffset);
+ if (pSparkline)
+ {
+ auto const& pSparklineGroup = pSparkline->getSparklineGroup();
+
+ auto pDuplicatedGroup = GetDoc().SearchSparklineGroup(pSparklineGroup->getID());
+ if (!pDuplicatedGroup)
+ pDuplicatedGroup = std::make_shared<sc::SparklineGroup>(*pSparklineGroup);
+
+ std::vector<sc::SparklineCell*> aSparklines(nDestSize, nullptr);
+ ScAddress aCurrentPosition = aDestPosition;
+ for (size_t i = 0; i < nDestSize; ++i)
+ {
+ auto pNewSparkline = std::make_shared<sc::Sparkline>(aCurrentPosition.Col(), aCurrentPosition.Row(), pDuplicatedGroup);
+ pNewSparkline->setInputRange(pSparkline->getInputRange());
+ aSparklines[i] = new sc::SparklineCell(pNewSparkline);
+ aCurrentPosition.IncRow();
+ }
+
+ pBlockPos->miSparklinePos = maSparklines.set(pBlockPos->miSparklinePos, aDestPosition.Row(), aSparklines.begin(), aSparklines.end());
+ }
+}
+
+void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ SCROW nLastRow = nRow + rVals.size() - 1;
+ if (nLastRow > GetDoc().MaxRow())
+ // Out of bound. Do nothing.
+ return;
+
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ std::vector<SCROW> aNewSharedRows;
+ DetachFormulaCells(aPos, rVals.size(), &aNewSharedRows);
+
+ maCells.set(nRow, rVals.begin(), rVals.end());
+ std::vector<sc::CellTextAttr> aDefaults(rVals.size());
+ maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end());
+
+ CellStorageModified();
+
+ StartListeningUnshared( aNewSharedRows);
+
+ std::vector<SCROW> aRows;
+ aRows.reserve(rVals.size());
+ for (SCROW i = nRow; i <= nLastRow; ++i)
+ aRows.push_back(i);
+
+ BroadcastCells(aRows, SfxHintId::ScDataChanged);
+}
+
+void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ SCROW nLastRow = nRow + nLen - 1;
+ if (nLastRow > GetDoc().MaxRow())
+ // Out of bound. Do nothing.
+ return;
+
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ DetachFormulaCells(aPos, nLen, nullptr);
+
+ rDest.transferFrom(*this, nRow, nLen);
+
+ CellStorageModified();
+
+ std::vector<SCROW> aRows;
+ aRows.reserve(nLen);
+ for (SCROW i = nRow; i <= nLastRow; ++i)
+ aRows.push_back(i);
+
+ BroadcastCells(aRows, SfxHintId::ScDataChanged);
+}
+
+void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ SCROW nLastRow = nRow + rSrc.size() - 1;
+ if (nLastRow > GetDoc().MaxRow())
+ // Out of bound. Do nothing
+ return;
+
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ DetachFormulaCells(aPos, rSrc.size(), nullptr);
+
+ rSrc.copyTo(*this, nRow);
+
+ CellStorageModified();
+
+ std::vector<SCROW> aRows;
+ aRows.reserve(rSrc.size());
+ for (SCROW i = nRow; i <= nLastRow; ++i)
+ aRows.push_back(i);
+
+ BroadcastCells(aRows, SfxHintId::ScDataChanged);
+}
+
+namespace {
+
+class ConvertFormulaToValueHandler
+{
+ sc::CellValues maResValues;
+ ScDocument& mrDoc;
+ bool mbModified;
+
+public:
+ ConvertFormulaToValueHandler(ScDocument& rDoc) :
+ mrDoc(rDoc),
+ mbModified(false)
+ {
+ maResValues.reset(mrDoc.GetSheetLimits().GetMaxRowCount());
+ }
+
+ void operator() ( size_t nRow, const ScFormulaCell* pCell )
+ {
+ sc::FormulaResultValue aRes = pCell->GetResult();
+ switch (aRes.meType)
+ {
+ case sc::FormulaResultValue::Value:
+ maResValues.setValue(nRow, aRes.mfValue);
+ break;
+ case sc::FormulaResultValue::String:
+ if (aRes.mbMultiLine)
+ {
+ std::unique_ptr<EditTextObject> pObj(mrDoc.CreateSharedStringTextObject(aRes.maString));
+ maResValues.setValue(nRow, std::move(pObj));
+ }
+ else
+ {
+ maResValues.setValue(nRow, aRes.maString);
+ }
+ break;
+ case sc::FormulaResultValue::Error:
+ case sc::FormulaResultValue::Invalid:
+ default:
+ maResValues.setValue(nRow, svl::SharedString::getEmptyString());
+ }
+
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+
+ sc::CellValues& getResValues() { return maResValues; }
+};
+
+}
+
+void ScColumn::ConvertFormulaToValue(
+ sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo )
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return;
+
+ std::vector<SCROW> aBounds { nRow1 };
+ if (nRow2 < GetDoc().MaxRow()-1)
+ aBounds.push_back(nRow2+1);
+
+ // Split formula cell groups at top and bottom boundaries (if applicable).
+ sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
+
+ // Parse all formulas within the range and store their results into temporary storage.
+ ConvertFormulaToValueHandler aFunc(GetDoc());
+ sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+ if (!aFunc.isModified())
+ // No formula cells encountered.
+ return;
+
+ DetachFormulaCells(rCxt, nRow1, nRow2);
+
+ // Undo storage to hold static values which will get swapped to the cell storage later.
+ sc::CellValues aUndoCells;
+ aFunc.getResValues().swap(aUndoCells);
+ aUndoCells.swapNonEmpty(*this);
+ if (pUndo)
+ pUndo->swap(nTab, nCol, aUndoCells);
+}
+
+namespace {
+
+class StartListeningHandler
+{
+ sc::StartListeningContext& mrCxt;
+
+public:
+ explicit StartListeningHandler( sc::StartListeningContext& rCxt ) :
+ mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->StartListeningTo(mrCxt);
+ }
+};
+
+class EndListeningHandler
+{
+ sc::EndListeningContext& mrCxt;
+
+public:
+ explicit EndListeningHandler( sc::EndListeningContext& rCxt ) :
+ mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->EndListeningTo(mrCxt);
+ }
+};
+
+}
+
+void ScColumn::SwapNonEmpty(
+ sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
+{
+ const ScRange& rRange = rValues.getRange();
+ std::vector<SCROW> aBounds { rRange.aStart.Row() };
+ if (rRange.aEnd.Row() < GetDoc().MaxRow()-1)
+ aBounds.push_back(rRange.aEnd.Row()+1);
+
+ // Split formula cell groups at top and bottom boundaries (if applicable).
+ sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
+ std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
+
+ // Detach formula cells within the spans (if any).
+ EndListeningHandler aEndLisFunc(rEndCxt);
+ sc::CellStoreType::iterator itPos = maCells.begin();
+ for (const auto& rSpan : aSpans)
+ {
+ SCROW nRow1 = rSpan.mnRow1;
+ SCROW nRow2 = rSpan.mnRow2;
+ itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
+ }
+
+ rValues.swapNonEmpty(nTab, nCol, *this);
+ RegroupFormulaCells();
+
+ // Attach formula cells within the spans (if any).
+ StartListeningHandler aStartLisFunc(rStartCxt);
+ itPos = maCells.begin();
+ for (const auto& rSpan : aSpans)
+ {
+ SCROW nRow1 = rSpan.mnRow1;
+ SCROW nRow2 = rSpan.mnRow2;
+ itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc);
+ }
+
+ CellStorageModified();
+}
+
+void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag )
+{
+ for (const auto& rSpan : rRanges)
+ DeleteArea(rSpan.mnRow1, rSpan.mnRow2, nDelFlag, false/*bBroadcast*/);
+}
+
+void ScColumn::CloneFormulaCell(
+ sc::ColumnBlockPosition& rBlockPos,
+ const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
+ const std::vector<sc::RowSpan>& rRanges )
+{
+ SCCOL nMatrixCols = 0;
+ SCROW nMatrixRows = 0;
+ ScMatrixMode nMatrixFlag = rSrc.GetMatrixFlag();
+ if (nMatrixFlag == ScMatrixMode::Formula)
+ {
+ rSrc.GetMatColsRows( nMatrixCols, nMatrixRows);
+ SAL_WARN_IF( nMatrixCols != 1 || nMatrixRows != 1, "sc.core",
+ "ScColumn::CloneFormulaCell - cloning array/matrix with not exactly one column or row as single cell");
+ }
+
+ ScDocument& rDocument = GetDoc();
+ std::vector<ScFormulaCell*> aFormulas;
+ for (const auto& rSpan : rRanges)
+ {
+ SCROW nRow1 = rSpan.mnRow1, nRow2 = rSpan.mnRow2;
+ size_t nLen = nRow2 - nRow1 + 1;
+ assert(nLen > 0);
+ aFormulas.clear();
+ aFormulas.reserve(nLen);
+
+ ScAddress aPos(nCol, nRow1, nTab);
+
+ if (nLen == 1 || !rSrc.GetCode()->IsShareable())
+ {
+ // Single, ungrouped formula cell, or create copies for
+ // non-shareable token arrays.
+ for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
+ {
+ ScFormulaCell* pCell = new ScFormulaCell(rSrc, rDocument, aPos);
+ aFormulas.push_back(pCell);
+ }
+ }
+ else
+ {
+ // Create a group of formula cells.
+ ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup);
+ xGroup->setCode(*rSrc.GetCode());
+ xGroup->compileCode(rDocument, aPos, rDocument.GetGrammar());
+ for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
+ {
+ ScFormulaCell* pCell = new ScFormulaCell(rDocument, aPos, xGroup, rDocument.GetGrammar(), nMatrixFlag);
+ if (nMatrixFlag == ScMatrixMode::Formula)
+ pCell->SetMatColsRows( nMatrixCols, nMatrixRows);
+ if (i == 0)
+ {
+ xGroup->mpTopCell = pCell;
+ xGroup->mnLength = nLen;
+ }
+ aFormulas.push_back(pCell);
+ }
+ }
+
+ rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow1, aFormulas.begin(), aFormulas.end());
+
+ // Join the top and bottom of the pasted formula cells as needed.
+ sc::CellStoreType::position_type aPosObj = maCells.position(rBlockPos.miCellPos, nRow1);
+
+ assert(aPosObj.first->type == sc::element_type_formula);
+ ScFormulaCell* pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
+ JoinNewFormulaCell(aPosObj, *pCell);
+
+ aPosObj = maCells.position(aPosObj.first, nRow2);
+ assert(aPosObj.first->type == sc::element_type_formula);
+ pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
+ JoinNewFormulaCell(aPosObj, *pCell);
+
+ std::vector<sc::CellTextAttr> aTextAttrs(nLen, rAttr);
+ rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
+ rBlockPos.miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
+ }
+
+ CellStorageModified();
+}
+
+void ScColumn::CloneFormulaCell(
+ const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
+ const std::vector<sc::RowSpan>& rRanges )
+{
+ sc::ColumnBlockPosition aBlockPos;
+ InitBlockPosition(aBlockPos);
+ CloneFormulaCell(aBlockPos, rSrc, rAttr, rRanges);
+}
+
+std::unique_ptr<ScPostIt> ScColumn::ReleaseNote( SCROW nRow )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return nullptr;
+
+ ScPostIt* p = nullptr;
+ maCellNotes.release(nRow, p);
+ return std::unique_ptr<ScPostIt>(p);
+}
+
+size_t ScColumn::GetNoteCount() const
+{
+ return std::accumulate(maCellNotes.begin(), maCellNotes.end(), size_t(0),
+ [](const size_t& rCount, const auto& rCellNote) {
+ if (rCellNote.type != sc::element_type_cellnote)
+ return rCount;
+ return rCount + rCellNote.size;
+ });
+}
+
+namespace {
+
+class NoteCaptionCreator
+{
+ ScAddress maPos;
+public:
+ NoteCaptionCreator( SCTAB nTab, SCCOL nCol ) : maPos(nCol,0,nTab) {}
+
+ void operator() ( size_t nRow, const ScPostIt* p )
+ {
+ maPos.SetRow(nRow);
+ p->GetOrCreateCaption(maPos);
+ }
+};
+
+class NoteCaptionCleaner
+{
+ bool mbPreserveData;
+public:
+ explicit NoteCaptionCleaner( bool bPreserveData ) : mbPreserveData(bPreserveData) {}
+
+ void operator() ( size_t /*nRow*/, ScPostIt* p )
+ {
+ p->ForgetCaption(mbPreserveData);
+ }
+};
+
+}
+
+void ScColumn::CreateAllNoteCaptions()
+{
+ NoteCaptionCreator aFunc(nTab, nCol);
+ sc::ProcessNote(maCellNotes, aFunc);
+}
+
+void ScColumn::ForgetNoteCaptions( SCROW nRow1, SCROW nRow2, bool bPreserveData )
+{
+ if (maCellNotes.empty())
+ return;
+
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
+ return;
+
+ NoteCaptionCleaner aFunc(bPreserveData);
+ sc::CellNoteStoreType::iterator it = maCellNotes.begin();
+ sc::ProcessNote(it, maCellNotes, nRow1, nRow2, aFunc);
+}
+
+SCROW ScColumn::GetNotePosition( size_t nIndex ) const
+{
+ // Return the row position of the nth note in the column.
+
+ size_t nCount = 0; // Number of notes encountered so far.
+ for (const auto& rCellNote : maCellNotes)
+ {
+ if (rCellNote.type != sc::element_type_cellnote)
+ // Skip the empty blocks.
+ continue;
+
+ if (nIndex < nCount + rCellNote.size)
+ {
+ // Index falls within this block.
+ size_t nOffset = nIndex - nCount;
+ return rCellNote.position + nOffset;
+ }
+
+ nCount += rCellNote.size;
+ }
+
+ return -1;
+}
+
+namespace {
+
+class NoteEntryCollector
+{
+ std::vector<sc::NoteEntry>& mrNotes;
+ SCTAB mnTab;
+ SCCOL mnCol;
+ SCROW mnStartRow;
+ SCROW mnEndRow;
+public:
+ NoteEntryCollector( std::vector<sc::NoteEntry>& rNotes, SCTAB nTab, SCCOL nCol,
+ SCROW nStartRow, SCROW nEndRow) :
+ mrNotes(rNotes), mnTab(nTab), mnCol(nCol),
+ mnStartRow(nStartRow), mnEndRow(nEndRow) {}
+
+ void operator() (const sc::CellNoteStoreType::value_type& node) const
+ {
+ if (node.type != sc::element_type_cellnote)
+ return;
+
+ size_t nTopRow = node.position;
+ sc::cellnote_block::const_iterator it = sc::cellnote_block::begin(*node.data);
+ sc::cellnote_block::const_iterator itEnd = sc::cellnote_block::end(*node.data);
+ size_t nOffset = 0;
+ if(nTopRow < o3tl::make_unsigned(mnStartRow))
+ {
+ std::advance(it, mnStartRow - nTopRow);
+ nOffset = mnStartRow - nTopRow;
+ }
+
+ for (; it != itEnd && nTopRow + nOffset <= o3tl::make_unsigned(mnEndRow);
+ ++it, ++nOffset)
+ {
+ ScAddress aPos(mnCol, nTopRow + nOffset, mnTab);
+ mrNotes.emplace_back(aPos, *it);
+ }
+ }
+};
+
+}
+
+void ScColumn::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
+{
+ if (HasCellNotes())
+ std::for_each(maCellNotes.begin(), maCellNotes.end(),
+ NoteEntryCollector(rNotes, nTab, nCol, 0, GetDoc().MaxRow()));
+}
+
+void ScColumn::GetNotesInRange(SCROW nStartRow, SCROW nEndRow,
+ std::vector<sc::NoteEntry>& rNotes ) const
+{
+ std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
+ sc::CellNoteStoreType::const_iterator it = aPos.first;
+ if (it == maCellNotes.end())
+ // Invalid row number.
+ return;
+
+ std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
+ maCellNotes.position(nEndRow);
+ sc::CellNoteStoreType::const_iterator itEnd = aEndPos.first;
+
+ std::for_each(it, ++itEnd, NoteEntryCollector(rNotes, nTab, nCol, nStartRow, nEndRow));
+}
+
+bool ScColumn::HasCellNote(SCROW nStartRow, SCROW nEndRow) const
+{
+ std::pair<sc::CellNoteStoreType::const_iterator,size_t> aStartPos =
+ maCellNotes.position(nStartRow);
+ if (aStartPos.first == maCellNotes.end())
+ // Invalid row number.
+ return false;
+
+ std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
+ maCellNotes.position(nEndRow);
+
+ for (sc::CellNoteStoreType::const_iterator it = aStartPos.first; it != aEndPos.first; ++it)
+ {
+ if (it->type != sc::element_type_cellnote)
+ continue;
+ size_t nTopRow = it->position;
+ sc::cellnote_block::const_iterator blockIt = sc::cellnote_block::begin(*(it->data));
+ sc::cellnote_block::const_iterator blockItEnd = sc::cellnote_block::end(*(it->data));
+ size_t nOffset = 0;
+ if(nTopRow < o3tl::make_unsigned(nStartRow))
+ {
+ std::advance(blockIt, nStartRow - nTopRow);
+ nOffset = nStartRow - nTopRow;
+ }
+
+ if (blockIt != blockItEnd && nTopRow + nOffset <= o3tl::make_unsigned(nEndRow))
+ return true;
+ }
+
+ return false;
+}
+
+void ScColumn::GetUnprotectedCells( SCROW nStartRow, SCROW nEndRow, ScRangeList& rRangeList ) const
+{
+ SCROW nTmpStartRow = nStartRow, nTmpEndRow = nEndRow;
+ const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow);
+ bool bProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection();
+ if (!bProtection)
+ {
+ // Limit the span to the range in question.
+ if (nTmpStartRow < nStartRow)
+ nTmpStartRow = nStartRow;
+ if (nTmpEndRow > nEndRow)
+ nTmpEndRow = nEndRow;
+ rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
+ }
+ while (nEndRow > nTmpEndRow)
+ {
+ nStartRow = nTmpEndRow + 1;
+ pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow);
+ bool bTmpProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection();
+ if (!bTmpProtection)
+ {
+ // Limit the span to the range in question.
+ // Only end row needs to be checked as we enter here only for spans
+ // below the original nStartRow.
+ if (nTmpEndRow > nEndRow)
+ nTmpEndRow = nEndRow;
+ rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
+ }
+ }
+}
+
+namespace {
+
+class RecompileByOpcodeHandler
+{
+ ScDocument* mpDoc;
+ const formula::unordered_opcode_set& mrOps;
+ sc::EndListeningContext& mrEndListenCxt;
+ sc::CompileFormulaContext& mrCompileFormulaCxt;
+
+public:
+ RecompileByOpcodeHandler(
+ ScDocument* pDoc, const formula::unordered_opcode_set& rOps,
+ sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
+ mpDoc(pDoc),
+ mrOps(rOps),
+ mrEndListenCxt(rEndListenCxt),
+ mrCompileFormulaCxt(rCompileCxt) {}
+
+ void operator() ( sc::FormulaGroupEntry& rEntry )
+ {
+ // Perform end listening, remove from formula tree, and set them up
+ // for re-compilation.
+
+ ScFormulaCell* pTop = nullptr;
+
+ if (rEntry.mbShared)
+ {
+ // Only inspect the code from the top cell.
+ pTop = *rEntry.mpCells;
+ }
+ else
+ pTop = rEntry.mpCell;
+
+ ScTokenArray* pCode = pTop->GetCode();
+ bool bRecompile = pCode->HasOpCodes(mrOps);
+
+ if (!bRecompile)
+ return;
+
+ // Get the formula string.
+ OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt);
+ sal_Int32 n = aFormula.getLength();
+ if (pTop->GetMatrixFlag() != ScMatrixMode::NONE && n > 0)
+ {
+ if (aFormula[0] == '{' && aFormula[n-1] == '}')
+ aFormula = aFormula.copy(1, n-2);
+ }
+
+ if (rEntry.mbShared)
+ {
+ ScFormulaCell** pp = rEntry.mpCells;
+ ScFormulaCell** ppEnd = pp + rEntry.mnLength;
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->EndListeningTo(mrEndListenCxt);
+ mpDoc->RemoveFromFormulaTree(p);
+ }
+ }
+ else
+ {
+ rEntry.mpCell->EndListeningTo(mrEndListenCxt);
+ mpDoc->RemoveFromFormulaTree(rEntry.mpCell);
+ }
+
+ pCode->Clear();
+ pTop->SetHybridFormula(aFormula, mpDoc->GetGrammar());
+ }
+};
+
+class CompileHybridFormulaHandler
+{
+ ScDocument& mrDoc;
+ sc::StartListeningContext& mrStartListenCxt;
+ sc::CompileFormulaContext& mrCompileFormulaCxt;
+
+public:
+ CompileHybridFormulaHandler(ScDocument& rDoc, sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
+ mrDoc(rDoc),
+ mrStartListenCxt(rStartListenCxt),
+ mrCompileFormulaCxt(rCompileCxt) {}
+
+ void operator() ( sc::FormulaGroupEntry& rEntry )
+ {
+ if (rEntry.mbShared)
+ {
+ ScFormulaCell* pTop = *rEntry.mpCells;
+ OUString aFormula = pTop->GetHybridFormula();
+
+ if (!aFormula.isEmpty())
+ {
+ // Create a new token array from the hybrid formula string, and
+ // set it to the group.
+ ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos);
+ std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
+ ScFormulaCellGroupRef xGroup = pTop->GetCellGroup();
+ assert(xGroup);
+ xGroup->setCode(std::move(*pNewCode));
+ xGroup->compileCode(mrDoc, pTop->aPos, mrDoc.GetGrammar());
+
+ // Propagate the new token array to all formula cells in the group.
+ ScFormulaCell** pp = rEntry.mpCells;
+ ScFormulaCell** ppEnd = pp + rEntry.mnLength;
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->SyncSharedCode();
+ p->StartListeningTo(mrStartListenCxt);
+ p->SetDirty();
+ }
+ }
+ }
+ else
+ {
+ ScFormulaCell* pCell = rEntry.mpCell;
+ OUString aFormula = pCell->GetHybridFormula();
+
+ if (!aFormula.isEmpty())
+ {
+ // Create token array from formula string.
+ ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos);
+ std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
+
+ // Generate RPN tokens.
+ ScCompiler aComp2(mrDoc, pCell->aPos, *pNewCode, formula::FormulaGrammar::GRAM_UNSPECIFIED,
+ true, pCell->GetMatrixFlag() != ScMatrixMode::NONE);
+ aComp2.CompileTokenArray();
+
+ pCell->SetCode(std::move(pNewCode));
+ pCell->StartListeningTo(mrStartListenCxt);
+ pCell->SetDirty();
+ }
+ }
+ }
+};
+
+}
+
+void ScColumn::PreprocessRangeNameUpdate(
+ sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
+{
+ // Collect all formula groups.
+ std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
+
+ formula::unordered_opcode_set aOps;
+ aOps.insert(ocBad);
+ aOps.insert(ocColRowName);
+ aOps.insert(ocName);
+ RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt);
+ std::for_each(aGroups.begin(), aGroups.end(), aFunc);
+}
+
+void ScColumn::PreprocessDBDataUpdate(
+ sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
+{
+ // Collect all formula groups.
+ std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
+
+ formula::unordered_opcode_set aOps;
+ aOps.insert(ocBad);
+ aOps.insert(ocColRowName);
+ aOps.insert(ocDBArea);
+ aOps.insert(ocTableRef);
+ RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt);
+ std::for_each(aGroups.begin(), aGroups.end(), aFunc);
+}
+
+void ScColumn::CompileHybridFormula(
+ sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
+{
+ // Collect all formula groups.
+ std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
+
+ CompileHybridFormulaHandler aFunc(GetDoc(), rStartListenCxt, rCompileCxt);
+ std::for_each(aGroups.begin(), aGroups.end(), aFunc);
+}
+
+namespace {
+
+class ScriptTypeUpdater
+{
+ ScColumn& mrCol;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miPosAttr;
+ ScConditionalFormatList* mpCFList;
+ SvNumberFormatter* mpFormatter;
+ ScAddress maPos;
+ bool mbUpdated;
+
+private:
+ void updateScriptType( size_t nRow, ScRefCellValue& rCell )
+ {
+ sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow);
+ miPosAttr = aAttrPos.first;
+
+ if (aAttrPos.first->type != sc::element_type_celltextattr)
+ return;
+
+ sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second);
+ if (rAttr.mnScriptType != SvtScriptType::UNKNOWN)
+ // Script type already determined. Skip it.
+ return;
+
+ const ScPatternAttr* pPat = mrCol.GetPattern(nRow);
+ if (!pPat)
+ // In theory this should never return NULL. But let's be safe.
+ return;
+
+ const SfxItemSet* pCondSet = nullptr;
+ if (mpCFList)
+ {
+ maPos.SetRow(nRow);
+ const ScCondFormatItem& rItem = pPat->GetItem(ATTR_CONDITIONAL);
+ const ScCondFormatIndexes& rData = rItem.GetCondFormatData();
+ pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData);
+ }
+
+ const Color* pColor;
+ sal_uInt32 nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet);
+ OUString aStr = ScCellFormat::GetString(rCell, nFormat, &pColor, *mpFormatter, mrCol.GetDoc());
+
+ rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr);
+ mbUpdated = true;
+ }
+
+public:
+ explicit ScriptTypeUpdater( ScColumn& rCol ) :
+ mrCol(rCol),
+ mrTextAttrs(rCol.GetCellAttrStore()),
+ miPosAttr(mrTextAttrs.begin()),
+ mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())),
+ mpFormatter(rCol.GetDoc().GetFormatTable()),
+ maPos(rCol.GetCol(), 0, rCol.GetTab()),
+ mbUpdated(false)
+ {}
+
+ void operator() ( size_t nRow, double fVal )
+ {
+ ScRefCellValue aCell(fVal);
+ updateScriptType(nRow, aCell);
+ }
+
+ void operator() ( size_t nRow, const svl::SharedString& rStr )
+ {
+ ScRefCellValue aCell(&rStr);
+ updateScriptType(nRow, aCell);
+ }
+
+ void operator() ( size_t nRow, const EditTextObject* pText )
+ {
+ ScRefCellValue aCell(pText);
+ updateScriptType(nRow, aCell);
+ }
+
+ void operator() ( size_t nRow, const ScFormulaCell* pCell )
+ {
+ ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell));
+ updateScriptType(nRow, aCell);
+ }
+
+ bool isUpdated() const { return mbUpdated; }
+};
+
+}
+
+void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return;
+
+ ScriptTypeUpdater aFunc(*this);
+ sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+ if (aFunc.isUpdated())
+ CellStorageModified();
+}
+
+void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern )
+{
+ maCells.swap(nRow1, nRow2, rOther.maCells, nRow1);
+ maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1);
+ maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1);
+ maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1);
+
+ // Update draw object anchors
+ ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
+ if (pDrawLayer)
+ {
+ std::map<SCROW, std::vector<SdrObject*>> aThisColRowDrawObjects
+ = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), GetCol(), nRow1, nRow2);
+ std::map<SCROW, std::vector<SdrObject*>> aOtherColRowDrawObjects
+ = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), rOther.GetCol(), nRow1, nRow2);
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ std::vector<SdrObject*>& rThisCellDrawObjects = aThisColRowDrawObjects[nRow];
+ if (!rThisCellDrawObjects.empty())
+ UpdateDrawObjectsForRow(rThisCellDrawObjects, rOther.GetCol(), nRow);
+ std::vector<SdrObject*>& rOtherCellDrawObjects = aOtherColRowDrawObjects[nRow];
+ if (!rOtherCellDrawObjects.empty())
+ rOther.UpdateDrawObjectsForRow(rOtherCellDrawObjects, GetCol(), nRow);
+ }
+ }
+
+ if (bPattern)
+ {
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ const ScPatternAttr* pPat1 = GetPattern(nRow);
+ const ScPatternAttr* pPat2 = rOther.GetPattern(nRow);
+ if (!SfxPoolItem::areSame(pPat1, pPat2))
+ {
+ if (pPat1->GetRefCount() == 1)
+ pPat1 = &rOther.GetDoc().GetPool()->DirectPutItemInPool(*pPat1);
+ SetPattern(nRow, *pPat2);
+ rOther.SetPattern(nRow, *pPat1);
+ }
+ }
+ }
+
+ CellStorageModified();
+ rOther.CellStorageModified();
+}
+
+namespace {
+
+class FormulaColPosSetter
+{
+ SCCOL mnCol;
+ bool mbUpdateRefs;
+public:
+ FormulaColPosSetter( SCCOL nCol, bool bUpdateRefs ) : mnCol(nCol), mbUpdateRefs(bUpdateRefs) {}
+
+ void operator() ( size_t nRow, ScFormulaCell* pCell )
+ {
+ if (!pCell->IsShared() || pCell->IsSharedTop())
+ {
+ // Ensure that the references still point to the same locations
+ // after the position change.
+ ScAddress aOldPos = pCell->aPos;
+ pCell->aPos.SetCol(mnCol);
+ pCell->aPos.SetRow(nRow);
+ if (mbUpdateRefs)
+ pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos);
+ else
+ pCell->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, pCell->aPos);
+ }
+ else
+ {
+ pCell->aPos.SetCol(mnCol);
+ pCell->aPos.SetRow(nRow);
+ }
+ }
+};
+
+}
+
+void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2, bool bUpdateRefs )
+{
+ FormulaColPosSetter aFunc(nCol, bUpdateRefs);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+}
+
+namespace {
+
+class RelativeRefBoundChecker
+{
+ std::vector<SCROW> maBounds;
+ ScRange maBoundRange;
+
+public:
+ explicit RelativeRefBoundChecker( const ScRange& rBoundRange ) :
+ maBoundRange(rBoundRange) {}
+
+ void operator() ( size_t /*nRow*/, ScFormulaCell* pCell )
+ {
+ if (!pCell->IsSharedTop())
+ return;
+
+ pCell->GetCode()->CheckRelativeReferenceBounds(
+ pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds);
+ }
+
+ void swapBounds( std::vector<SCROW>& rBounds )
+ {
+ rBounds.swap(maBounds);
+ }
+};
+
+}
+
+void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange )
+{
+ if (rBoundRange.aStart.Row() >= GetDoc().MaxRow())
+ // Nothing to split.
+ return;
+
+ std::vector<SCROW> aBounds;
+
+ // Cut at row boundaries first.
+ aBounds.push_back(rBoundRange.aStart.Row());
+ if (rBoundRange.aEnd.Row() < GetDoc().MaxRow())
+ aBounds.push_back(rBoundRange.aEnd.Row()+1);
+ sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
+
+ RelativeRefBoundChecker aFunc(rBoundRange);
+ sc::ProcessFormula(
+ maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc);
+ aFunc.swapBounds(aBounds);
+ sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
+}
+
+namespace {
+
+class ListenerCollector
+{
+ std::vector<SvtListener*>& mrListeners;
+public:
+ explicit ListenerCollector( std::vector<SvtListener*>& rListener ) :
+ mrListeners(rListener) {}
+
+ void operator() ( size_t /*nRow*/, SvtBroadcaster* p )
+ {
+ SvtBroadcaster::ListenersType& rLis = p->GetAllListeners();
+ mrListeners.insert(mrListeners.end(), rLis.begin(), rLis.end());
+ }
+};
+
+class FormulaCellCollector
+{
+ std::vector<ScFormulaCell*>& mrCells;
+public:
+ explicit FormulaCellCollector( std::vector<ScFormulaCell*>& rCells ) : mrCells(rCells) {}
+
+ void operator() ( size_t /*nRow*/, ScFormulaCell* p )
+ {
+ mrCells.push_back(p);
+ }
+};
+
+}
+
+void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 )
+{
+ if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
+ return;
+
+ ListenerCollector aFunc(rListeners);
+ sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::CollectFormulaCells( std::vector<ScFormulaCell*>& rCells, SCROW nRow1, SCROW nRow2 )
+{
+ if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
+ return;
+
+ FormulaCellCollector aFunc(rCells);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+}
+
+bool ScColumn::HasFormulaCell() const
+{
+ return mnBlkCountFormula != 0;
+}
+
+namespace {
+
+struct FindAnyFormula
+{
+ bool operator() ( size_t /*nRow*/, const ScFormulaCell* /*pCell*/ ) const
+ {
+ return true;
+ }
+};
+
+}
+
+bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
+{
+ if (!mnBlkCountFormula)
+ return false;
+
+ if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
+ return false;
+
+ if (nRow1 == 0 && nRow2 == GetDoc().MaxRow())
+ return HasFormulaCell();
+
+ FindAnyFormula aFunc;
+ std::pair<sc::CellStoreType::const_iterator, size_t> aRet =
+ sc::FindFormula(maCells, nRow1, nRow2, aFunc);
+
+ return aRet.first != maCells.end();
+}
+
+namespace {
+
+void endListening( sc::EndListeningContext& rCxt, ScFormulaCell** pp, ScFormulaCell** ppEnd )
+{
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell& rFC = **pp;
+ rFC.EndListeningTo(rCxt);
+ }
+}
+
+class StartListeningFormulaCellsHandler
+{
+ sc::StartListeningContext& mrStartCxt;
+ sc::EndListeningContext& mrEndCxt;
+ SCROW mnStartRow;
+
+public:
+ StartListeningFormulaCellsHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
+ mrStartCxt(rStartCxt), mrEndCxt(rEndCxt), mnStartRow(-1) {}
+
+ void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
+ {
+ if (node.type != sc::element_type_formula)
+ // We are only interested in formulas.
+ return;
+
+ mnStartRow = node.position + nOffset;
+
+ ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
+ ScFormulaCell** ppEnd = ppBeg + nDataSize;
+
+ ScFormulaCell** pp = ppBeg;
+
+ // If the first formula cell belongs to a group and it's not the top
+ // cell, move up to the top cell of the group, and have all the extra
+ // formula cells stop listening.
+
+ ScFormulaCell* pFC = *pp;
+ if (pFC->IsShared() && !pFC->IsSharedTop())
+ {
+ SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
+ if (nBackTrackSize > 0)
+ {
+ assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset);
+ for (SCROW i = 0; i < nBackTrackSize; ++i)
+ --pp;
+ endListening(mrEndCxt, pp, ppBeg);
+ mnStartRow -= nBackTrackSize;
+ }
+ }
+
+ for (; pp != ppEnd; ++pp)
+ {
+ pFC = *pp;
+
+ if (!pFC->IsSharedTop())
+ {
+ assert(!pFC->IsShared());
+ pFC->StartListeningTo(mrStartCxt);
+ continue;
+ }
+
+ // If This is the last group in the range, see if the group
+ // extends beyond the range, in which case have the excess
+ // formula cells stop listening.
+ size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
+ if (nEndGroupPos > nDataSize)
+ {
+ size_t nExcessSize = nEndGroupPos - nDataSize;
+ ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
+ ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
+ endListening(mrEndCxt, ppGrp, ppGrpEnd);
+
+ // Register formula cells as a group.
+ sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
+ pp = ppEnd - 1; // Move to the one before the end position.
+ }
+ else
+ {
+ // Register formula cells as a group.
+ sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
+ pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
+ }
+ }
+ }
+
+};
+
+class EndListeningFormulaCellsHandler
+{
+ sc::EndListeningContext& mrEndCxt;
+ SCROW mnStartRow;
+ SCROW mnEndRow;
+
+public:
+ explicit EndListeningFormulaCellsHandler( sc::EndListeningContext& rEndCxt ) :
+ mrEndCxt(rEndCxt), mnStartRow(-1), mnEndRow(-1) {}
+
+ void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
+ {
+ if (node.type != sc::element_type_formula)
+ // We are only interested in formulas.
+ return;
+
+ mnStartRow = node.position + nOffset;
+
+ ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
+ ScFormulaCell** ppEnd = ppBeg + nDataSize;
+
+ ScFormulaCell** pp = ppBeg;
+
+ // If the first formula cell belongs to a group and it's not the top
+ // cell, move up to the top cell of the group.
+
+ ScFormulaCell* pFC = *pp;
+ if (pFC->IsShared() && !pFC->IsSharedTop())
+ {
+ SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
+ if (nBackTrackSize > 0)
+ {
+ assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset);
+ for (SCROW i = 0; i < nBackTrackSize; ++i)
+ --pp;
+ mnStartRow -= nBackTrackSize;
+ }
+ }
+
+ for (; pp != ppEnd; ++pp)
+ {
+ pFC = *pp;
+
+ if (!pFC->IsSharedTop())
+ {
+ assert(!pFC->IsShared());
+ pFC->EndListeningTo(mrEndCxt);
+ continue;
+ }
+
+ size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
+ mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group.
+
+ ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
+ endListening(mrEndCxt, pp, ppGrpEnd);
+
+ if (nEndGroupPos > nDataSize)
+ {
+ // The group goes beyond the specified end row. Move to the
+ // one before the end position to finish the loop.
+ pp = ppEnd - 1;
+ }
+ else
+ {
+ // Move to the last one in the group.
+ pp += pFC->GetSharedLength() - 1;
+ }
+ }
+ }
+
+ SCROW getStartRow() const
+ {
+ return mnStartRow;
+ }
+
+ SCROW getEndRow() const
+ {
+ return mnEndRow;
+ }
+};
+
+}
+
+void ScColumn::StartListeningFormulaCells(
+ sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+ SCROW nRow1, SCROW nRow2 )
+{
+ if (!HasFormulaCell())
+ return;
+
+ StartListeningFormulaCellsHandler aFunc(rStartCxt, rEndCxt);
+ sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+}
+
+void ScColumn::EndListeningFormulaCells(
+ sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2,
+ SCROW* pStartRow, SCROW* pEndRow )
+{
+ if (!HasFormulaCell())
+ return;
+
+ EndListeningFormulaCellsHandler aFunc(rCxt);
+ sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+
+ if (pStartRow)
+ *pStartRow = aFunc.getStartRow();
+
+ if (pEndRow)
+ *pEndRow = aFunc.getEndRow();
+}
+
+void ScColumn::EndListeningIntersectedGroup(
+ sc::EndListeningContext& rCxt, SCROW nRow, std::vector<ScAddress>* pGroupPos )
+{
+ if (!GetDoc().ValidRow(nRow))
+ return;
+
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ // Only interested in a formula block.
+ return;
+
+ ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
+ ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
+ if (!xGroup)
+ // Not a formula group.
+ return;
+
+ // End listening.
+ pFC->EndListeningTo(rCxt);
+
+ if (pGroupPos)
+ {
+ if (!pFC->IsSharedTop())
+ // Record the position of the top cell of the group.
+ pGroupPos->push_back(xGroup->mpTopCell->aPos);
+
+ SCROW nGrpLastRow = pFC->GetSharedTopRow() + pFC->GetSharedLength() - 1;
+ if (nRow < nGrpLastRow)
+ // Record the last position of the group.
+ pGroupPos->push_back(ScAddress(nCol, nGrpLastRow, nTab));
+ }
+}
+
+void ScColumn::EndListeningIntersectedGroups(
+ sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, std::vector<ScAddress>* pGroupPos )
+{
+ // Only end the intersected group.
+ sc::CellStoreType::position_type aPos = maCells.position(nRow1);
+ sc::CellStoreType::iterator it = aPos.first;
+ if (it->type == sc::element_type_formula)
+ {
+ ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
+ ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
+ if (xGroup)
+ {
+ if (!pFC->IsSharedTop())
+ // End listening.
+ pFC->EndListeningTo(rCxt);
+
+ if (pGroupPos)
+ // Record the position of the top cell of the group.
+ pGroupPos->push_back(xGroup->mpTopCell->aPos);
+ }
+ }
+
+ aPos = maCells.position(it, nRow2);
+ it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ return;
+
+ ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
+ ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
+ if (!xGroup)
+ return;
+
+ if (!pFC->IsSharedTop())
+ // End listening.
+ pFC->EndListeningTo(rCxt);
+
+ if (pGroupPos)
+ {
+ // Record the position of the bottom cell of the group.
+ ScAddress aPosLast = xGroup->mpTopCell->aPos;
+ aPosLast.IncRow(xGroup->mnLength-1);
+ pGroupPos->push_back(aPosLast);
+ }
+}
+
+void ScColumn::EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ if (aPos.first->type != sc::element_type_formula)
+ // not a formula cell.
+ return;
+
+ ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
+
+ ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
+ if (!xGroup)
+ {
+ // not a formula group.
+ (*pp)->EndListeningTo(rCxt);
+ return;
+ }
+
+ // Move back to the top cell.
+ SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
+ assert(nTopDelta >= 0);
+ if (nTopDelta > 0)
+ pp -= nTopDelta;
+
+ // Set the needs listening flag to all cells in the group.
+ assert(*pp == xGroup->mpTopCell);
+ ScFormulaCell** ppEnd = pp + xGroup->mnLength;
+ for (; pp != ppEnd; ++pp)
+ (*pp)->EndListeningTo(rCxt);
+}
+
+void ScColumn::SetNeedsListeningGroup( SCROW nRow )
+{
+ sc::CellStoreType::position_type aPos = maCells.position(nRow);
+ if (aPos.first->type != sc::element_type_formula)
+ // not a formula cell.
+ return;
+
+ ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
+
+ ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
+ if (!xGroup)
+ {
+ // not a formula group.
+ (*pp)->SetNeedsListening(true);
+ return;
+ }
+
+ // Move back to the top cell.
+ SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
+ assert(nTopDelta >= 0);
+ if (nTopDelta > 0)
+ pp -= nTopDelta;
+
+ // Set the needs listening flag to all cells in the group.
+ assert(*pp == xGroup->mpTopCell);
+ ScFormulaCell** ppEnd = pp + xGroup->mnLength;
+ for (; pp != ppEnd; ++pp)
+ (*pp)->SetNeedsListening(true);
+}
+
+std::optional<sc::ColumnIterator> ScColumn::GetColumnIterator( SCROW nRow1, SCROW nRow2 ) const
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return {};
+
+ return sc::ColumnIterator(maCells, nRow1, nRow2);
+}
+
+static bool lcl_InterpretSpan(sc::formula_block::const_iterator& rSpanIter, SCROW nStartOffset, SCROW nEndOffset,
+ const ScFormulaCellGroupRef& mxParentGroup, bool& bAllowThreading, ScDocument& rDoc)
+{
+ bAllowThreading = true;
+ ScFormulaCell* pCellStart = nullptr;
+ SCROW nSpanStart = -1;
+ SCROW nSpanEnd = -1;
+ sc::formula_block::const_iterator itSpanStart;
+ bool bAnyDirty = false;
+ for (SCROW nFGOffset = nStartOffset; nFGOffset <= nEndOffset; ++rSpanIter, ++nFGOffset)
+ {
+ bool bThisDirty = (*rSpanIter)->NeedsInterpret();
+ if (!pCellStart && bThisDirty)
+ {
+ pCellStart = *rSpanIter;
+ itSpanStart = rSpanIter;
+ nSpanStart = nFGOffset;
+ bAnyDirty = true;
+ }
+
+ if (pCellStart && (!bThisDirty || nFGOffset == nEndOffset))
+ {
+ nSpanEnd = bThisDirty ? nFGOffset : nFGOffset - 1;
+ assert(nSpanStart >= nStartOffset && nSpanStart <= nSpanEnd && nSpanEnd <= nEndOffset);
+
+ // Found a completely dirty sub span [nSpanStart, nSpanEnd] inside the required span [nStartOffset, nEndOffset]
+ bool bGroupInterpreted = pCellStart->Interpret(nSpanStart, nSpanEnd);
+
+ if (bGroupInterpreted)
+ for (SCROW nIdx = nSpanStart; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
+ assert(!(*itSpanStart)->NeedsInterpret());
+
+ ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper();
+ // child cell's Interpret could result in calling dependency calc
+ // and that could detect a cycle involving mxGroup
+ // and do early exit in that case.
+ // OR
+ // this call resulted from a dependency calculation for a multi-formula-group-threading and
+ // if intergroup dependency is found, return early.
+ if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
+ {
+ bAllowThreading = false;
+ return bAnyDirty;
+ }
+
+ if (!bGroupInterpreted)
+ {
+ // Evaluate from second cell in non-grouped style (no point in trying group-interpret again).
+ ++itSpanStart;
+ for (SCROW nIdx = nSpanStart+1; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
+ {
+ (*itSpanStart)->Interpret(); // We know for sure that this cell is dirty so directly call Interpret().
+ if ((*itSpanStart)->NeedsInterpret())
+ {
+ SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itSpanStart)->aPos
+ << " failed running Interpret(), not allowing threading");
+ bAllowThreading = false;
+ return bAnyDirty;
+ }
+
+ // Allow early exit like above.
+ if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
+ {
+ // Set this cell as dirty as this may be interpreted in InterpretTail()
+ pCellStart->SetDirtyVar();
+ bAllowThreading = false;
+ return bAnyDirty;
+ }
+ }
+ }
+
+ pCellStart = nullptr; // For next sub span start detection.
+ }
+ }
+
+ return bAnyDirty;
+}
+
+static void lcl_EvalDirty(sc::CellStoreType& rCells, SCROW nRow1, SCROW nRow2, ScDocument& rDoc,
+ const ScFormulaCellGroupRef& mxGroup, bool bThreadingDepEval, bool bSkipRunning,
+ bool& bIsDirty, bool& bAllowThreading, ScAddress* pDirtiedAddress)
+{
+ ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper();
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nRow1);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ SCROW nRow = nRow1;
+
+ bIsDirty = false;
+
+ for (;it != rCells.end() && nRow <= nRow2; ++it, nOffset = 0)
+ {
+ switch( it->type )
+ {
+ case sc::element_type_edittext:
+ // These require EditEngine (in ScEditUtils::GetString()), which is probably
+ // too complex for use in threads.
+ if (bThreadingDepEval)
+ {
+ bAllowThreading = false;
+ return;
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ size_t nRowsToRead = nRow2 - nRow + 1;
+ const size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
+ sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, nOffset);
+
+ // Loop inside the formula block.
+ size_t nCellIdx = nOffset;
+ while (nCellIdx < nEnd)
+ {
+ const ScFormulaCellGroupRef& mxGroupChild = (*itCell)->GetCellGroup();
+ ScFormulaCell* pChildTopCell = mxGroupChild ? mxGroupChild->mpTopCell : *itCell;
+
+ // Check if itCell is already in path.
+ // If yes use a cycle guard to mark all elements of the cycle
+ // and return false
+ if (bThreadingDepEval && pChildTopCell->GetSeenInPath())
+ {
+ ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pChildTopCell);
+ bAllowThreading = false;
+ return;
+ }
+
+ if (bSkipRunning && (*itCell)->IsRunning())
+ {
+ ++itCell;
+ nCellIdx += 1;
+ nRow += 1;
+ nRowsToRead -= 1;
+ continue;
+ }
+
+ if (mxGroupChild)
+ {
+ // It is a Formula-group, evaluate the necessary parts of it (spans).
+ const SCROW nFGStartOffset = (*itCell)->aPos.Row() - pChildTopCell->aPos.Row();
+ const SCROW nFGEndOffset = std::min(nFGStartOffset + static_cast<SCROW>(nRowsToRead) - 1, mxGroupChild->mnLength - 1);
+ assert(nFGEndOffset >= nFGStartOffset);
+ const SCROW nSpanLen = nFGEndOffset - nFGStartOffset + 1;
+ // The (main) span required to be evaluated is [nFGStartOffset, nFGEndOffset], but this span may contain
+ // non-dirty cells, so split this into sets of completely-dirty spans and try evaluate each of them in grouped-style.
+
+ bool bAnyDirtyInSpan = lcl_InterpretSpan(itCell, nFGStartOffset, nFGEndOffset, mxGroup, bAllowThreading, rDoc);
+ if (!bAllowThreading)
+ return;
+ // itCell will now point to cell just after the end of span [nFGStartOffset, nFGEndOffset].
+ bIsDirty = bIsDirty || bAnyDirtyInSpan;
+
+ // update the counters by nSpanLen.
+ // itCell already got updated.
+ nCellIdx += nSpanLen;
+ nRow += nSpanLen;
+ nRowsToRead -= nSpanLen;
+ }
+ else
+ {
+ // No formula-group here.
+ bool bDirtyFlag = false;
+ if( (*itCell)->NeedsInterpret())
+ {
+ bDirtyFlag = true;
+ (*itCell)->Interpret();
+ if ((*itCell)->NeedsInterpret())
+ {
+ SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itCell)->aPos
+ << " failed running Interpret(), not allowing threading");
+ bAllowThreading = false;
+ return;
+ }
+ }
+ bIsDirty = bIsDirty || bDirtyFlag;
+
+ // child cell's Interpret could result in calling dependency calc
+ // and that could detect a cycle involving mxGroup
+ // and do early exit in that case.
+ // OR
+ // we are trying multi-formula-group-threading, but found intergroup dependency.
+ if (bThreadingDepEval && mxGroup &&
+ (mxGroup->mbPartOfCycle || !rRecursionHelper.AreGroupsIndependent()))
+ {
+ // Set itCell as dirty as itCell may be interpreted in InterpretTail()
+ (*itCell)->SetDirtyVar();
+ if (pDirtiedAddress)
+ pDirtiedAddress->SetRow(nRow);
+ bAllowThreading = false;
+ return;
+ }
+
+ // update the counters by 1.
+ nCellIdx += 1;
+ nRow += 1;
+ nRowsToRead -= 1;
+ ++itCell;
+ }
+ }
+ break;
+ }
+ default:
+ // Skip this block.
+ nRow += it->size - nOffset;
+ continue;
+ }
+ }
+
+ if (bThreadingDepEval)
+ bAllowThreading = true;
+
+}
+
+// Returns true if at least one FC is dirty.
+bool ScColumn::EnsureFormulaCellResults( SCROW nRow1, SCROW nRow2, bool bSkipRunning )
+{
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return false;
+
+ if (!HasFormulaCell(nRow1, nRow2))
+ return false;
+
+ bool bAnyDirty = false, bTmp = false;
+ lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), nullptr, false, bSkipRunning, bAnyDirty, bTmp, nullptr);
+ return bAnyDirty;
+}
+
+bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2, const ScFormulaCellGroupRef& mxGroup, ScAddress* pDirtiedAddress )
+{
+ if (nRow1 > nRow2)
+ return false;
+
+ bool bAllowThreading = true, bTmp = false;
+ lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), mxGroup, true, false, bTmp, bAllowThreading, pDirtiedAddress);
+
+ return bAllowThreading;
+}
+
+namespace {
+
+class StoreToCacheFunc
+{
+ SvStream& mrStrm;
+public:
+
+ StoreToCacheFunc(SvStream& rStrm):
+ mrStrm(rStrm)
+ {
+ }
+
+ void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
+ {
+ SCROW nStartRow = node.position + nOffset;
+ mrStrm.WriteUInt64(nStartRow);
+ mrStrm.WriteUInt64(nDataSize);
+ switch (node.type)
+ {
+ case sc::element_type_empty:
+ {
+ mrStrm.WriteUChar(0);
+ }
+ break;
+ case sc::element_type_numeric:
+ {
+ mrStrm.WriteUChar(1);
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; ++it)
+ {
+ mrStrm.WriteDouble(*it);
+ }
+ }
+ break;
+ case sc::element_type_string:
+ {
+ mrStrm.WriteUChar(2);
+ sc::string_block::const_iterator it = sc::string_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::string_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; ++it)
+ {
+ OString aStr = OUStringToOString(it->getString(), RTL_TEXTENCODING_UTF8);
+ sal_Int32 nStrLength = aStr.getLength();
+ mrStrm.WriteInt32(nStrLength);
+ mrStrm.WriteOString(aStr);
+ }
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ mrStrm.WriteUChar(3);
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; /* incrementing through std::advance*/)
+ {
+ const ScFormulaCell* pCell = *it;
+ OUString aFormula = pCell->GetFormula(formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+ const auto& xCellGroup = pCell->GetCellGroup();
+ sal_uInt64 nGroupLength = 0;
+ if (xCellGroup)
+ {
+ nGroupLength = xCellGroup->mnLength;
+ }
+ else
+ {
+ nGroupLength = 1;
+ }
+ mrStrm.WriteUInt64(nGroupLength);
+ mrStrm.WriteInt32(aFormula.getLength());
+ mrStrm.WriteOString(OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8));
+
+ // incrementing the iterator
+ std::advance(it, nGroupLength);
+ }
+ }
+ break;
+ }
+ }
+};
+
+}
+
+void ScColumn::StoreToCache(SvStream& rStrm) const
+{
+ rStrm.WriteUInt64(nCol);
+ SCROW nLastRow = GetLastDataPos();
+ rStrm.WriteUInt64(nLastRow + 1); // the rows are zero based
+
+ StoreToCacheFunc aFunc(rStrm);
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, SCROW(0), nLastRow);
+}
+
+void ScColumn::RestoreFromCache(SvStream& rStrm)
+{
+ sal_uInt64 nStoredCol = 0;
+ rStrm.ReadUInt64(nStoredCol);
+ if (nStoredCol != static_cast<sal_uInt64>(nCol))
+ throw std::exception();
+
+ sal_uInt64 nLastRow = 0;
+ rStrm.ReadUInt64(nLastRow);
+ sal_uInt64 nReadRow = 0;
+ ScDocument& rDocument = GetDoc();
+ while (nReadRow < nLastRow)
+ {
+ sal_uInt64 nStartRow = 0;
+ sal_uInt64 nDataSize = 0;
+ rStrm.ReadUInt64(nStartRow);
+ rStrm.ReadUInt64(nDataSize);
+ sal_uInt8 nType = 0;
+ rStrm.ReadUChar(nType);
+ switch (nType)
+ {
+ case 0:
+ // nothing to do
+ maCells.set_empty(nStartRow, nDataSize);
+ break;
+ case 1:
+ {
+ // nDataSize double values
+ std::vector<double> aValues(nDataSize);
+ for (auto& rValue : aValues)
+ {
+ rStrm.ReadDouble(rValue);
+ }
+ maCells.set(nStartRow, aValues.begin(), aValues.end());
+ }
+ break;
+ case 2:
+ {
+ std::vector<svl::SharedString> aStrings(nDataSize);
+ svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
+ for (auto& rString : aStrings)
+ {
+ sal_Int32 nStrLength = 0;
+ rStrm.ReadInt32(nStrLength);
+ std::unique_ptr<char[]> pStr(new char[nStrLength]);
+ rStrm.ReadBytes(pStr.get(), nStrLength);
+ std::string_view aOStr(pStr.get(), nStrLength);
+ OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8);
+ rString = rPool.intern(aStr);
+ }
+ maCells.set(nStartRow, aStrings.begin(), aStrings.end());
+
+ }
+ break;
+ case 3:
+ {
+ std::vector<ScFormulaCell*> aFormulaCells(nDataSize);
+
+ ScAddress aAddr(nCol, nStartRow, nTab);
+ const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
+ for (SCROW nRow = 0; nRow < static_cast<SCROW>(nDataSize);)
+ {
+ sal_uInt64 nFormulaGroupSize = 0;
+ rStrm.ReadUInt64(nFormulaGroupSize);
+ sal_Int32 nStrLength = 0;
+ rStrm.ReadInt32(nStrLength);
+ std::unique_ptr<char[]> pStr(new char[nStrLength]);
+ rStrm.ReadBytes(pStr.get(), nStrLength);
+ std::string_view aOStr(pStr.get(), nStrLength);
+ OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8);
+ for (sal_uInt64 i = 0; i < nFormulaGroupSize; ++i)
+ {
+ aFormulaCells[nRow + i] = new ScFormulaCell(rDocument, aAddr, aStr, eGrammar);
+ aAddr.IncRow();
+ }
+
+ nRow += nFormulaGroupSize;
+ }
+
+ maCells.set(nStartRow, aFormulaCells.begin(), aFormulaCells.end());
+ }
+ break;
+ }
+
+ nReadRow += nDataSize;
+ }
+}
+
+void ScColumn::CheckIntegrity() const
+{
+ const ScColumn* pColTest = maCells.event_handler().getColumn();
+
+ if (pColTest != this)
+ {
+ std::ostringstream os;
+ os << "cell store's event handler references wrong column instance (this=" << this
+ << "; stored=" << pColTest << ")";
+ throw std::runtime_error(os.str());
+ }
+
+ size_t nCount = std::count_if(maCells.cbegin(), maCells.cend(),
+ [](const auto& blk) { return blk.type == sc::element_type_formula; }
+ );
+
+ if (mnBlkCountFormula != nCount)
+ {
+ std::ostringstream os;
+ os << "incorrect cached formula block count (expected=" << nCount << "; actual="
+ << mnBlkCountFormula << ")";
+ throw std::runtime_error(os.str());
+ }
+}
+
+void ScColumn::CollectBroadcasterState(sc::BroadcasterState& rState) const
+{
+ for (const auto& block : maBroadcasters)
+ {
+ if (block.type != sc::element_type_broadcaster)
+ continue;
+
+ auto itBeg = sc::broadcaster_block::begin(*block.data);
+ auto itEnd = sc::broadcaster_block::end(*block.data);
+
+ for (auto it = itBeg; it != itEnd; ++it)
+ {
+ ScAddress aBCPos(nCol, block.position + std::distance(itBeg, it), nTab);
+
+ auto aRes = rState.aCellListenerStore.try_emplace(aBCPos);
+ auto& rLisStore = aRes.first->second;
+
+ const SvtBroadcaster& rBC = **it;
+ for (const SvtListener* pLis : rBC.GetAllListeners())
+ {
+ const auto* pFC = dynamic_cast<const ScFormulaCell*>(pLis);
+ if (pFC)
+ rLisStore.emplace_back(pFC);
+ else
+ rLisStore.emplace_back(pLis);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/columniterator.cxx b/sc/source/core/data/columniterator.cxx
new file mode 100644
index 0000000000..cec8f7a202
--- /dev/null
+++ b/sc/source/core/data/columniterator.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/.
+ */
+
+#include <columniterator.hxx>
+#include <column.hxx>
+#include <document.hxx>
+#include <table.hxx>
+
+#include <osl/diagnose.h>
+
+ScColumnTextWidthIterator::ScColumnTextWidthIterator(const ScDocument& rDoc, ScColumn& rCol, SCROW nStartRow, SCROW nEndRow) :
+ mnEnd(static_cast<size_t>(nEndRow)),
+ mnCurPos(0)
+{
+ miBlockCur = rCol.maCellTextAttrs.begin();
+ miBlockEnd = rCol.maCellTextAttrs.end();
+ init(rDoc, nStartRow, nEndRow);
+}
+
+ScColumnTextWidthIterator::ScColumnTextWidthIterator(const ScDocument& rDoc, const ScAddress& rStartPos, SCROW nEndRow) :
+ mnEnd(static_cast<size_t>(nEndRow)),
+ mnCurPos(0)
+{
+ auto & rCellTextAttrs = rDoc.maTabs[rStartPos.Tab()]->aCol[rStartPos.Col()].maCellTextAttrs;
+ miBlockCur = rCellTextAttrs.begin();
+ miBlockEnd = rCellTextAttrs.end();
+ init(rDoc, rStartPos.Row(), nEndRow);
+}
+
+void ScColumnTextWidthIterator::next()
+{
+ ++miDataCur;
+ ++mnCurPos;
+
+ if (miDataCur != miDataEnd)
+ {
+ // Still in the same block. We're good.
+ checkEndRow();
+ return;
+ }
+
+ // Move to the next block.
+ for (++miBlockCur; miBlockCur != miBlockEnd; ++miBlockCur)
+ {
+ if (miBlockCur->type != sc::element_type_celltextattr)
+ {
+ // We don't iterator over this block.
+ mnCurPos += miBlockCur->size;
+ continue;
+ }
+
+ getDataIterators(0);
+ checkEndRow();
+ return;
+ }
+
+ // Reached the end.
+ assert(miBlockCur == miBlockEnd);
+}
+
+bool ScColumnTextWidthIterator::hasCell() const
+{
+ return miBlockCur != miBlockEnd;
+}
+
+SCROW ScColumnTextWidthIterator::getPos() const
+{
+ assert(miBlockCur != miBlockEnd && miDataCur != miDataEnd);
+ return static_cast<SCROW>(mnCurPos);
+}
+
+sal_uInt16 ScColumnTextWidthIterator::getValue() const
+{
+ assert(miBlockCur != miBlockEnd && miDataCur != miDataEnd);
+ return miDataCur->mnTextWidth;
+}
+
+void ScColumnTextWidthIterator::setValue(sal_uInt16 nVal)
+{
+ assert(miBlockCur != miBlockEnd && miDataCur != miDataEnd);
+ miDataCur->mnTextWidth = nVal;
+}
+
+void ScColumnTextWidthIterator::init(const ScDocument& rDoc, SCROW nStartRow, SCROW nEndRow)
+{
+ if (!rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow))
+ miBlockCur = miBlockEnd;
+
+ size_t nStart = static_cast<size_t>(nStartRow);
+
+ // Locate the start row position.
+ size_t nBlockStart = 0, nBlockEnd = 0;
+ for (; miBlockCur != miBlockEnd; ++miBlockCur, nBlockStart = nBlockEnd)
+ {
+ nBlockEnd = nBlockStart + miBlockCur->size; // non-inclusive end point.
+ if (nBlockStart <= nStart && nStart < nBlockEnd)
+ {
+ // Initial block is found!
+ break;
+ }
+ }
+
+ if (miBlockCur == miBlockEnd)
+ // Initial block not found for whatever reason... Bail out.
+ return;
+
+ // Locate the initial row position within this block.
+ if (miBlockCur->type == sc::element_type_celltextattr)
+ {
+ // This block stores text widths for non-empty cells.
+ size_t nOffsetInBlock = nStart - nBlockStart;
+ mnCurPos = nStart;
+ getDataIterators(nOffsetInBlock);
+ checkEndRow();
+ return;
+ }
+
+ // Current block is not of ushort type. Skip to the next block.
+ nBlockStart = nBlockEnd;
+ ++miBlockCur;
+
+ // Look for the first ushort block.
+ for (; miBlockCur != miBlockEnd; ++miBlockCur, nBlockStart = nBlockEnd)
+ {
+ nBlockEnd = nBlockStart + miBlockCur->size; // non-inclusive end point.
+ if (miBlockCur->type != sc::element_type_celltextattr)
+ continue;
+
+ // Found!
+ mnCurPos = nBlockStart;
+ getDataIterators(0);
+ checkEndRow();
+ return;
+ }
+
+ // Not found.
+ assert(miBlockCur == miBlockEnd);
+}
+
+void ScColumnTextWidthIterator::getDataIterators(size_t nOffsetInBlock)
+{
+ OSL_ENSURE(miBlockCur != miBlockEnd, "block is at end position");
+#if 0
+ // Does not compile
+ OSL_ENSURE(miBlockCur->type == sc::celltextattr_block,
+ "wrong block type - unsigned short block expected.");
+#endif
+ miDataCur = sc::celltextattr_block::begin(*miBlockCur->data);
+ miDataEnd = sc::celltextattr_block::end(*miBlockCur->data);
+
+ std::advance(miDataCur, nOffsetInBlock);
+}
+
+void ScColumnTextWidthIterator::checkEndRow()
+{
+ if (mnCurPos <= mnEnd)
+ // We're still good.
+ return;
+
+ // We're below the end position. End the iteration.
+ miBlockCur = miBlockEnd;
+}
+
+namespace sc {
+
+ColumnIterator::ColumnIterator( const CellStoreType& rCells, SCROW nRow1, SCROW nRow2 ) :
+ maPos(rCells.position(nRow1)),
+ maPosEnd(rCells.position(maPos.first, nRow2)),
+ mbComplete(false)
+{
+}
+
+void ColumnIterator::next()
+{
+ if ( maPos == maPosEnd)
+ mbComplete = true;
+ else
+ maPos = CellStoreType::next_position(maPos);
+}
+
+SCROW ColumnIterator::getRow() const
+{
+ return CellStoreType::logical_position(maPos);
+}
+
+bool ColumnIterator::hasCell() const
+{
+ return !mbComplete;
+}
+
+mdds::mtv::element_t ColumnIterator::getType() const
+{
+ return maPos.first->type;
+}
+
+ScRefCellValue ColumnIterator::getCell() const
+{
+ return toRefCell(maPos.first, maPos.second);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/columnset.cxx b/sc/source/core/data/columnset.cxx
new file mode 100644
index 0000000000..5f91dd8c60
--- /dev/null
+++ b/sc/source/core/data/columnset.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <columnset.hxx>
+#include <algorithm>
+
+namespace sc {
+
+void ColumnSet::set(SCTAB nTab, SCCOL nCol)
+{
+ TabsType::iterator itTab = maTabs.find(nTab);
+ if (itTab == maTabs.end())
+ {
+ std::pair<TabsType::iterator,bool> r =
+ maTabs.emplace(nTab, ColsType());
+
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itTab = r.first;
+ }
+
+ ColsType& rCols = itTab->second;
+ rCols.insert(nCol);
+}
+
+void ColumnSet::getColumns(SCTAB nTab, std::vector<SCCOL>& rCols) const
+{
+ std::vector<SCCOL> aCols;
+ TabsType::const_iterator itTab = maTabs.find(nTab);
+ if (itTab == maTabs.end())
+ {
+ rCols.swap(aCols); // empty it.
+ return;
+ }
+
+ const ColsType& rTabCols = itTab->second;
+ aCols.assign(rTabCols.begin(), rTabCols.end());
+
+ // Sort and remove duplicates.
+ std::sort(aCols.begin(), aCols.end());
+ std::vector<SCCOL>::iterator itCol = std::unique(aCols.begin(), aCols.end());
+ aCols.erase(itCol, aCols.end());
+
+ rCols.swap(aCols);
+}
+
+bool ColumnSet::hasTab(SCTAB nTab) const
+{
+ return maTabs.find(nTab) != maTabs.end();
+}
+
+bool ColumnSet::empty() const
+{
+ return maTabs.empty();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/columnspanset.cxx b/sc/source/core/data/columnspanset.cxx
new file mode 100644
index 0000000000..bec9c8a7e2
--- /dev/null
+++ b/sc/source/core/data/columnspanset.cxx
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <columnspanset.hxx>
+#include <column.hxx>
+#include <table.hxx>
+#include <document.hxx>
+#include <mtvfunctions.hxx>
+#include <markdata.hxx>
+#include <rangelst.hxx>
+#include <fstalgorithm.hxx>
+
+#include <algorithm>
+#include <memory>
+
+#include <o3tl/safeint.hxx>
+
+namespace sc {
+
+namespace {
+
+class ColumnNonEmptyRangesScanner
+{
+ ColumnSpanSet::ColumnSpansType& mrRanges;
+ bool mbVal;
+public:
+ ColumnNonEmptyRangesScanner(ColumnSpanSet::ColumnSpansType& rRanges, bool bVal) :
+ mrRanges(rRanges), mbVal(bVal) {}
+
+ void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ if (node.type == sc::element_type_empty)
+ return;
+
+ size_t nRow = node.position + nOffset;
+ size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1
+ mrRanges.insert_back(nRow, nEndRow, mbVal);
+ }
+};
+
+}
+
+RowSpan::RowSpan(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {}
+
+ColRowSpan::ColRowSpan(SCCOLROW nStart, SCCOLROW nEnd) : mnStart(nStart), mnEnd(nEnd) {}
+
+ColumnSpanSet::ColumnType::ColumnType(SCROW nStart, SCROW nEnd, bool bInit) :
+ maSpans(nStart, nEnd+1, bInit), miPos(maSpans.begin()) {}
+
+ColumnSpanSet::ColumnType::ColumnType(const ColumnType& rOther) :
+ maSpans(rOther.maSpans), miPos(maSpans.begin()) {} // NB: copying maSpans invalidates miPos - reset it
+
+ColumnSpanSet::Action::~Action() {}
+void ColumnSpanSet::Action::startColumn(SCTAB /*nTab*/, SCCOL /*nCol*/) {}
+
+ColumnSpanSet::ColumnAction::~ColumnAction() {}
+
+ColumnSpanSet::ColumnSpanSet() {}
+
+ColumnSpanSet::~ColumnSpanSet()
+{
+}
+
+ColumnSpanSet::ColumnType& ColumnSpanSet::getColumn(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
+{
+ if (o3tl::make_unsigned(nTab) >= maTables.size())
+ maTables.resize(nTab+1);
+
+ TableType& rTab = maTables[nTab];
+ if (o3tl::make_unsigned(nCol) >= rTab.size())
+ rTab.resize(nCol+1);
+
+ if (!rTab[nCol])
+ rTab[nCol].emplace(0, rDoc.MaxRow(), /*bInit*/false);
+
+ return *rTab[nCol];
+}
+
+void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
+{
+ if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow))
+ return;
+
+ ColumnType& rCol = getColumn(rDoc, nTab, nCol);
+ rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow, nRow+1, bVal).first;
+}
+
+void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, bool bVal)
+{
+ if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow1) || !rDoc.ValidRow(nRow2))
+ return;
+
+ ColumnType& rCol = getColumn(rDoc, nTab, nCol);
+ rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow1, nRow2+1, bVal).first;
+}
+
+void ColumnSpanSet::set(const ScDocument& rDoc, const ScRange& rRange, bool bVal)
+{
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ ColumnType& rCol = getColumn(rDoc, nTab, nCol);
+ rCol.miPos = rCol.maSpans.insert(rCol.miPos, rRange.aStart.Row(), rRange.aEnd.Row()+1, bVal).first;
+ }
+ }
+}
+
+void ColumnSpanSet::set( const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SingleColumnSpanSet& rSingleSet, bool bVal )
+{
+ SingleColumnSpanSet::SpansType aSpans;
+ rSingleSet.getSpans(aSpans);
+ for (const auto& rSpan : aSpans)
+ set(rDoc, nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, bVal);
+}
+
+void ColumnSpanSet::scan(
+ const ScDocument& rDoc, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal)
+{
+ if (!rDoc.ValidColRow(nCol1, nRow1) || !rDoc.ValidColRow(nCol2, nRow2))
+ return;
+
+ if (nCol1 > nCol2 || nRow1 > nRow2)
+ return;
+
+ const ScTable* pTab = rDoc.FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ nCol2 = pTab->ClampToAllocatedColumns(nCol2);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ ColumnType& rCol = getColumn(rDoc, nTab, nCol);
+
+ const CellStoreType& rSrcCells = pTab->aCol[nCol].maCells;
+
+ if( nRow1 > pTab->aCol[nCol].GetLastDataPos())
+ continue;
+
+ ColumnNonEmptyRangesScanner aScanner(rCol.maSpans, bVal);
+ ParseBlock(rSrcCells.begin(), rSrcCells, aScanner, nRow1, nRow2);
+ rCol.miPos = rCol.maSpans.begin();
+ }
+}
+
+void ColumnSpanSet::executeAction(ScDocument& rDoc, Action& ac) const
+{
+ for (size_t nTab = 0; nTab < maTables.size(); ++nTab)
+ {
+ if (maTables[nTab].empty())
+ continue;
+
+ ScTable* pTab = rDoc.FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ const TableType& rTab = maTables[nTab];
+ for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol)
+ {
+ if (!rTab[nCol])
+ continue;
+ if (nCol >= pTab->GetAllocatedColumnsCount())
+ break;
+
+ ac.startColumn(nTab, nCol);
+ const ColumnType& rCol = *rTab[nCol];
+ ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end();
+ SCROW nRow1, nRow2;
+ nRow1 = it->first;
+ bool bVal = it->second;
+ for (++it; it != itEnd; ++it)
+ {
+ nRow2 = it->first-1;
+ ac.execute(ScAddress(nCol, nRow1, nTab), nRow2-nRow1+1, bVal);
+
+ nRow1 = nRow2+1; // for the next iteration.
+ bVal = it->second;
+ }
+ }
+ }
+}
+
+void ColumnSpanSet::executeColumnAction(ScDocument& rDoc, ColumnAction& ac) const
+{
+ for (size_t nTab = 0; nTab < maTables.size(); ++nTab)
+ {
+ if (maTables[nTab].empty())
+ continue;
+
+ ScTable* pTab = rDoc.FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ const TableType& rTab = maTables[nTab];
+ for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol)
+ {
+ if (!rTab[nCol])
+ continue;
+ if (nCol >= pTab->GetAllocatedColumnsCount())
+ break;
+
+ ScColumn& rColumn = pTab->aCol[nCol];
+ ac.startColumn(&rColumn);
+ const ColumnType& rCol = *rTab[nCol];
+ ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end();
+ SCROW nRow1, nRow2;
+ nRow1 = it->first;
+ bool bVal = it->second;
+ for (++it; it != itEnd; ++it)
+ {
+ nRow2 = it->first-1;
+ ac.execute(nRow1, nRow2, bVal);
+
+ nRow1 = nRow2+1; // for the next iteration.
+ bVal = it->second;
+ }
+ }
+ }
+}
+
+namespace {
+
+class NonEmptyRangesScanner
+{
+ SingleColumnSpanSet::ColumnSpansType& mrRanges;
+public:
+ explicit NonEmptyRangesScanner(SingleColumnSpanSet::ColumnSpansType& rRanges) : mrRanges(rRanges) {}
+
+ void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ if (node.type == sc::element_type_empty)
+ return;
+
+ size_t nRow = node.position + nOffset;
+ size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1
+ mrRanges.insert_back(nRow, nEndRow, true);
+ }
+};
+
+}
+
+SingleColumnSpanSet::SingleColumnSpanSet(ScSheetLimits const & rSheetLimits)
+ : mrSheetLimits(rSheetLimits),
+ maSpans(0, rSheetLimits.GetMaxRowCount(), false) {}
+
+void SingleColumnSpanSet::scan(const ScColumn& rColumn)
+{
+ const CellStoreType& rCells = rColumn.maCells;
+ SCROW nCurRow = 0;
+ for (const auto& rCell : rCells)
+ {
+ SCROW nEndRow = nCurRow + rCell.size; // Last row of current block plus 1.
+ if (rCell.type != sc::element_type_empty)
+ maSpans.insert_back(nCurRow, nEndRow, true);
+
+ nCurRow = nEndRow;
+ }
+}
+
+void SingleColumnSpanSet::scan(const ScColumn& rColumn, SCROW nStart, SCROW nEnd)
+{
+ if( nStart > rColumn.GetLastDataPos())
+ return;
+ const CellStoreType& rCells = rColumn.maCells;
+ NonEmptyRangesScanner aScanner(maSpans);
+ sc::ParseBlock(rCells.begin(), rCells, aScanner, nStart, nEnd);
+}
+
+void SingleColumnSpanSet::scan(
+ ColumnBlockConstPosition& rBlockPos, const ScColumn& rColumn, SCROW nStart, SCROW nEnd)
+{
+ if( nStart > rColumn.GetLastDataPos())
+ return;
+ const CellStoreType& rCells = rColumn.maCells;
+ NonEmptyRangesScanner aScanner(maSpans);
+ rBlockPos.miCellPos = sc::ParseBlock(rBlockPos.miCellPos, rCells, aScanner, nStart, nEnd);
+}
+
+void SingleColumnSpanSet::scan(const ScMarkData& rMark, SCTAB nTab, SCCOL nCol)
+{
+ if (!rMark.GetTableSelect(nTab))
+ // This table is not selected. Nothing to scan.
+ return;
+
+ ScRangeList aRanges = rMark.GetMarkedRangesForTab(nTab);
+ scan(aRanges, nTab, nCol);
+}
+
+void SingleColumnSpanSet::scan(const ScRangeList& rRanges, SCTAB nTab, SCCOL nCol)
+{
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rRanges[i];
+ if (nTab < rRange.aStart.Tab() || rRange.aEnd.Tab() < nTab)
+ continue;
+
+ if (nCol < rRange.aStart.Col() || rRange.aEnd.Col() < nCol)
+ // This column is not in this range. Skip it.
+ continue;
+
+ maSpans.insert_back(rRange.aStart.Row(), rRange.aEnd.Row()+1, true);
+ }
+}
+
+void SingleColumnSpanSet::set(SCROW nRow1, SCROW nRow2, bool bVal)
+{
+ maSpans.insert_back(nRow1, nRow2+1, bVal);
+}
+
+void SingleColumnSpanSet::getRows(std::vector<SCROW> &rRows) const
+{
+ std::vector<SCROW> aRows;
+
+ SpansType aRanges;
+ getSpans(aRanges);
+ for (const auto& rRange : aRanges)
+ {
+ for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow)
+ aRows.push_back(nRow);
+ }
+
+ rRows.swap(aRows);
+}
+
+void SingleColumnSpanSet::getSpans(SpansType& rSpans) const
+{
+ SpansType aSpans = toSpanArray<SCROW,RowSpan>(maSpans);
+ rSpans.swap(aSpans);
+}
+
+void SingleColumnSpanSet::swap( SingleColumnSpanSet& r )
+{
+ maSpans.swap(r.maSpans);
+}
+
+bool SingleColumnSpanSet::empty() const
+{
+ // Empty if there's only the 0..rDoc.MaxRow() span with false.
+ ColumnSpansType::const_iterator it = maSpans.begin();
+ return (it->first == 0) && !(it->second) && (++it != maSpans.end()) && (it->first == mrSheetLimits.GetMaxRowCount());
+}
+
+
+void RangeColumnSpanSet::executeColumnAction(ScDocument& rDoc, sc::ColumnSpanSet::ColumnAction& ac) const
+{
+ for (SCTAB nTab = range.aStart.Tab(); nTab <= range.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = rDoc.FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ SCCOL nEndCol = pTab->ClampToAllocatedColumns(range.aEnd.Col());
+ for (SCCOL nCol = range.aStart.Col(); nCol <= nEndCol; ++nCol)
+ {
+ if (!rDoc.ValidCol(nCol))
+ break;
+
+ ScColumn& rColumn = pTab->aCol[nCol];
+ ac.startColumn(&rColumn);
+ ac.execute( range.aStart.Row(), range.aEnd.Row(), true );
+ }
+ }
+}
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/compressedarray.cxx b/sc/source/core/data/compressedarray.cxx
new file mode 100644
index 0000000000..793b43b43a
--- /dev/null
+++ b/sc/source/core/data/compressedarray.cxx
@@ -0,0 +1,418 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <compressedarray.hxx>
+#include <global.hxx>
+
+template< typename A, typename D >
+ScCompressedArray<A,D>::ScCompressedArray( A nMaxAccessP, const D& rValue )
+ : nCount(1)
+ , nLimit(1)
+ , pData( new DataEntry[1])
+ , nMaxAccess( nMaxAccessP)
+{
+ pData[0].aValue = rValue;
+ pData[0].nEnd = nMaxAccess;
+}
+
+template< typename A, typename D >
+size_t ScCompressedArray<A,D>::Search( A nAccess ) const
+{
+ if (nAccess == 0)
+ return 0;
+
+ tools::Long nLo = 0;
+ tools::Long nHi = static_cast<tools::Long>(nCount) - 1;
+ tools::Long nStart = 0;
+ tools::Long i = 0;
+ bool bFound = (nCount == 1);
+ while (!bFound && nLo <= nHi)
+ {
+ i = (nLo + nHi) / 2;
+ if (i > 0)
+ nStart = static_cast<tools::Long>(pData[i - 1].nEnd);
+ else
+ nStart = -1;
+ tools::Long nEnd = static_cast<tools::Long>(pData[i].nEnd);
+ if (nEnd < static_cast<tools::Long>(nAccess))
+ nLo = ++i;
+ else
+ if (nStart >= static_cast<tools::Long>(nAccess))
+ nHi = --i;
+ else
+ bFound = true;
+ }
+ return (bFound ? static_cast<size_t>(i) : (nAccess < 0 ? 0 : nCount-1));
+}
+
+template< typename A, typename D >
+void ScCompressedArray<A,D>::SetValue( A nStart, A nEnd, const D& rValue )
+{
+ if (!(0 <= nStart && nStart <= nMaxAccess && 0 <= nEnd && nEnd <= nMaxAccess
+ && nStart <= nEnd))
+ return;
+
+ if ((nStart == 0) && (nEnd == nMaxAccess))
+ Reset( rValue);
+ else
+ {
+ // Create a temporary copy in case we got a reference passed that
+ // points to a part of the array to be reallocated.
+ D aNewVal( rValue);
+ size_t nNeeded = nCount + 2;
+ if (nLimit < nNeeded)
+ {
+ nLimit *= 1.5;
+ if (nLimit < nNeeded)
+ nLimit = nNeeded;
+ std::unique_ptr<DataEntry[]> pNewData(new DataEntry[nLimit]);
+ memcpy( pNewData.get(), pData.get(), nCount*sizeof(DataEntry));
+ pData = std::move(pNewData);
+ }
+
+ size_t ni; // number of leading entries
+ size_t nInsert; // insert position (nMaxAccess+1 := no insert)
+ bool bCombined = false;
+ bool bSplit = false;
+ if (nStart > 0)
+ {
+ // skip leading
+ ni = this->Search( nStart);
+
+ nInsert = nMaxAccess+1;
+ if (!(pData[ni].aValue == aNewVal))
+ {
+ if (ni == 0 || (pData[ni-1].nEnd < nStart - 1))
+ { // may be a split or a simple insert or just a shrink,
+ // row adjustment is done further down
+ if (pData[ni].nEnd > nEnd)
+ bSplit = true;
+ ni++;
+ nInsert = ni;
+ }
+ else if (ni > 0 && pData[ni-1].nEnd == nStart - 1)
+ nInsert = ni;
+ }
+ if (ni > 0 && pData[ni-1].aValue == aNewVal)
+ { // combine
+ pData[ni-1].nEnd = nEnd;
+ nInsert = nMaxAccess+1;
+ bCombined = true;
+ }
+ }
+ else
+ {
+ nInsert = 0;
+ ni = 0;
+ }
+
+ size_t nj = ni; // stop position of range to replace
+ while (nj < nCount && pData[nj].nEnd <= nEnd)
+ nj++;
+ if (!bSplit)
+ {
+ if (nj < nCount && pData[nj].aValue == aNewVal)
+ { // combine
+ if (ni > 0)
+ {
+ if (pData[ni-1].aValue == aNewVal)
+ { // adjacent entries
+ pData[ni-1].nEnd = pData[nj].nEnd;
+ nj++;
+ }
+ else if (ni == nInsert)
+ pData[ni-1].nEnd = nStart - 1; // shrink
+ }
+ nInsert = nMaxAccess+1;
+ bCombined = true;
+ }
+ else if (ni > 0 && ni == nInsert)
+ pData[ni-1].nEnd = nStart - 1; // shrink
+ }
+ if (ni < nj)
+ { // remove middle entries
+ if (!bCombined)
+ { // replace one entry
+ pData[ni].nEnd = nEnd;
+ pData[ni].aValue = aNewVal;
+ ni++;
+ nInsert = nMaxAccess+1;
+ }
+ if (ni < nj)
+ { // remove entries
+ memmove( pData.get() + ni, pData.get() + nj,
+ (nCount - nj) * sizeof(DataEntry));
+ nCount -= nj - ni;
+ }
+ }
+
+ if (nInsert < static_cast<size_t>(nMaxAccess+1))
+ { // insert or append new entry
+ if (nInsert <= nCount)
+ {
+ if (!bSplit)
+ memmove( pData.get() + nInsert + 1, pData.get() + nInsert,
+ (nCount - nInsert) * sizeof(DataEntry));
+ else
+ {
+ memmove( pData.get() + nInsert + 2, pData.get() + nInsert,
+ (nCount - nInsert) * sizeof(DataEntry));
+ pData[nInsert+1] = pData[nInsert-1];
+ nCount++;
+ }
+ }
+ if (nInsert)
+ pData[nInsert-1].nEnd = nStart - 1;
+ pData[nInsert].nEnd = nEnd;
+ pData[nInsert].aValue = aNewVal;
+ nCount++;
+ }
+ }
+}
+
+template< typename A, typename D >
+void ScCompressedArray<A,D>::CopyFrom( const ScCompressedArray<A,D>& rArray, A nDestStart,
+ A nDestEnd, A nSrcStart )
+{
+ assert( this != &rArray && "cannot copy self->self" );
+ size_t nIndex = 0;
+ A nRegionEnd;
+ for (A j=nDestStart; j<=nDestEnd; ++j)
+ {
+ const D& rValue = (j==nDestStart ?
+ rArray.GetValue( j - nDestStart + nSrcStart, nIndex, nRegionEnd) :
+ rArray.GetNextValue( nIndex, nRegionEnd));
+ nRegionEnd = nRegionEnd - nSrcStart + nDestStart;
+ if (nRegionEnd > nDestEnd)
+ nRegionEnd = nDestEnd;
+ this->SetValue( j, nRegionEnd, rValue);
+ j = nRegionEnd;
+ }
+}
+
+template< typename A, typename D >
+const D& ScCompressedArray<A,D>::Insert( A nStart, size_t nAccessCount )
+{
+ size_t nIndex = this->Search( nStart);
+ // No real insertion is needed, simply extend the one entry and adapt all
+ // following. In case nStart points to the start row of an entry, extend
+ // the previous entry (inserting before nStart).
+ if (nIndex > 0 && pData[nIndex-1].nEnd+1 == nStart)
+ --nIndex;
+ const D& rValue = pData[nIndex].aValue; // the value "copied"
+ do
+ {
+ pData[nIndex].nEnd += nAccessCount;
+ if (pData[nIndex].nEnd >= nMaxAccess)
+ {
+ pData[nIndex].nEnd = nMaxAccess;
+ nCount = nIndex + 1; // discard trailing entries
+ }
+ } while (++nIndex < nCount);
+ return rValue;
+}
+
+template< typename A, typename D >
+void ScCompressedArray<A,D>::InsertPreservingSize( A nStart, size_t nAccessCount, const D& rFillValue )
+{
+ const A nPrevLastPos = GetLastPos();
+
+ Insert(nStart, nAccessCount);
+ for (A i = nStart; i < A(nStart + nAccessCount); ++i)
+ SetValue(i, rFillValue);
+
+ const A nNewLastPos = GetLastPos();
+ Remove(nPrevLastPos, nNewLastPos - nPrevLastPos);
+}
+
+template< typename A, typename D >
+void ScCompressedArray<A,D>::Remove( A nStart, size_t nAccessCount )
+{
+ A nEnd = nStart + nAccessCount - 1;
+ size_t nIndex = this->Search( nStart);
+ // equalize/combine/remove all entries in between
+ if (nEnd > pData[nIndex].nEnd)
+ this->SetValue( nStart, nEnd, pData[nIndex].aValue);
+ // remove an exactly matching entry by shifting up all following by one
+ if ((nStart == 0 || (nIndex > 0 && nStart == pData[nIndex-1].nEnd+1)) &&
+ pData[nIndex].nEnd == nEnd && nIndex < nCount-1)
+ {
+ // In case removing an entry results in two adjacent entries with
+ // identical data, combine them into one. This is also necessary to
+ // make the algorithm used in SetValue() work correctly, it relies on
+ // the fact that consecutive values actually differ.
+ size_t nRemove;
+ if (nIndex > 0 && pData[nIndex-1].aValue == pData[nIndex+1].aValue)
+ {
+ nRemove = 2;
+ --nIndex;
+ }
+ else
+ nRemove = 1;
+ memmove( pData.get() + nIndex, pData.get() + nIndex + nRemove, (nCount - (nIndex +
+ nRemove)) * sizeof(DataEntry));
+ nCount -= nRemove;
+ }
+ // adjust end rows, nIndex still being valid
+ do
+ {
+ pData[nIndex].nEnd -= nAccessCount;
+ } while (++nIndex < nCount);
+ pData[nCount-1].nEnd = nMaxAccess;
+}
+
+template< typename A, typename D >
+void ScCompressedArray<A,D>::RemovePreservingSize( A nStart, size_t nAccessCount, const D& rFillValue )
+{
+ const A nPrevLastPos = GetLastPos();
+
+ Remove(nStart, nAccessCount);
+
+ const A nNewLastPos = GetLastPos();
+ InsertPreservingSize(nNewLastPos, nNewLastPos - nPrevLastPos, rFillValue);
+}
+
+template< typename A, typename D >
+void ScCompressedArray<A,D>::Iterator::operator++()
+{
+ ++mnRegion;
+ if (mnRegion > mrArray.pData[mnIndex].nEnd)
+ ++mnIndex;
+}
+
+template< typename A, typename D >
+typename ScCompressedArray<A,D>::Iterator ScCompressedArray<A,D>::Iterator::operator+(size_t nAccessCount) const
+{
+ A nRegion = mnRegion + nAccessCount;
+ auto nIndex = mnIndex;
+ while (nRegion > mrArray.pData[nIndex].nEnd)
+ ++nIndex;
+ return Iterator(mrArray, nIndex, nRegion);
+}
+
+// === ScBitMaskCompressedArray ==============================================
+
+template< typename A, typename D >
+void ScBitMaskCompressedArray<A,D>::AndValue( A nStart, A nEnd,
+ const D& rValueToAnd )
+{
+ if (nStart > nEnd)
+ return;
+
+ size_t nIndex = this->Search( nStart);
+ do
+ {
+ if ((this->pData[nIndex].aValue & rValueToAnd) != this->pData[nIndex].aValue)
+ {
+ A nS = ::std::max<A>( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart);
+ A nE = ::std::min( this->pData[nIndex].nEnd, nEnd);
+ this->SetValue( nS, nE, this->pData[nIndex].aValue & rValueToAnd);
+ if (nE >= nEnd)
+ break; // while
+ nIndex = this->Search( nE + 1);
+ }
+ else if (this->pData[nIndex].nEnd >= nEnd)
+ break; // while
+ else
+ ++nIndex;
+ } while (nIndex < this->nCount);
+}
+
+template< typename A, typename D >
+void ScBitMaskCompressedArray<A,D>::OrValue( A nStart, A nEnd,
+ const D& rValueToOr )
+{
+ if (nStart > nEnd)
+ return;
+
+ size_t nIndex = this->Search( nStart);
+ do
+ {
+ if ((this->pData[nIndex].aValue | rValueToOr) != this->pData[nIndex].aValue)
+ {
+ A nS = ::std::max<A>( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart);
+ A nE = ::std::min( this->pData[nIndex].nEnd, nEnd);
+ this->SetValue( nS, nE, this->pData[nIndex].aValue | rValueToOr);
+ if (nE >= nEnd)
+ break; // while
+ nIndex = this->Search( nE + 1);
+ }
+ else if (this->pData[nIndex].nEnd >= nEnd)
+ break; // while
+ else
+ ++nIndex;
+ } while (nIndex < this->nCount);
+}
+
+template< typename A, typename D >
+void ScBitMaskCompressedArray<A,D>::CopyFromAnded(
+ const ScBitMaskCompressedArray<A,D>& rArray, A nStart, A nEnd,
+ const D& rValueToAnd )
+{
+ size_t nIndex = 0;
+ A nRegionEnd;
+ for (A j=nStart; j<=nEnd; ++j)
+ {
+ const D& rValue = (j==nStart ?
+ rArray.GetValue( j, nIndex, nRegionEnd) :
+ rArray.GetNextValue( nIndex, nRegionEnd));
+ if (nRegionEnd > nEnd)
+ nRegionEnd = nEnd;
+ this->SetValue( j, nRegionEnd, rValue & rValueToAnd);
+ j = nRegionEnd;
+ }
+}
+
+template< typename A, typename D >
+A ScBitMaskCompressedArray<A,D>::GetLastAnyBitAccess( const D& rBitMask ) const
+{
+ A nEnd = ::std::numeric_limits<A>::max();
+ size_t nIndex = this->nCount-1;
+ while (true)
+ {
+ if (this->pData[nIndex].aValue & rBitMask)
+ {
+ nEnd = this->pData[nIndex].nEnd;
+ break; // while
+ }
+ else
+ {
+ if (nIndex > 0)
+ {
+ --nIndex;
+ if (this->pData[nIndex].nEnd < 0)
+ break; // while
+ }
+ else
+ break; // while
+ }
+ }
+ return nEnd;
+}
+
+// === Force instantiation of specializations ================================
+
+template class ScCompressedArray< SCROW, CRFlags>; // flags, base class
+template class ScBitMaskCompressedArray< SCROW, CRFlags>; // flags
+template class ScCompressedArray< SCCOL, sal_uInt16>;
+template class ScCompressedArray< SCCOL, CRFlags>;
+template class ScCompressedArray< SCROW, sal_uInt16>;
+template class ScBitMaskCompressedArray< SCCOL, CRFlags>;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx
new file mode 100644
index 0000000000..13859539f3
--- /dev/null
+++ b/sc/source/core/data/conditio.cxx
@@ -0,0 +1,2365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <svl/numformat.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+#include <com/sun/star/sheet/ConditionOperator2.hpp>
+
+#include <attrib.hxx>
+#include <conditio.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <compiler.hxx>
+#include <rangelst.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <colorscale.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+#include <tokenarray.hxx>
+#include <fillinfo.hxx>
+#include <refupdatecontext.hxx>
+#include <formula/errorcodes.hxx>
+#include <svl/sharedstring.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <memory>
+#include <numeric>
+#include <utility>
+
+using namespace formula;
+
+ScFormatEntry::ScFormatEntry(ScDocument* pDoc):
+ mpDoc(pDoc)
+{
+}
+
+bool ScFormatEntry::operator==( const ScFormatEntry& r ) const
+{
+ return IsEqual(r, false);
+}
+
+// virtual
+bool ScFormatEntry::IsEqual( const ScFormatEntry& /*r*/, bool /*bIgnoreSrcPos*/ ) const
+{
+ // By default, return false; this makes sense for all cases except ScConditionEntry
+ // As soon as databar and color scale are tested we need to think about the range
+ return false;
+}
+
+void ScFormatEntry::startRendering()
+{
+}
+
+void ScFormatEntry::endRendering()
+{
+}
+
+void ScFormatEntry::updateValues()
+{
+}
+
+static bool lcl_HasRelRef( ScDocument* pDoc, const ScTokenArray* pFormula, sal_uInt16 nRecursion = 0 )
+{
+ if (pFormula)
+ {
+ FormulaTokenArrayPlainIterator aIter( *pFormula );
+ FormulaToken* t;
+ for( t = aIter.Next(); t; t = aIter.Next() )
+ {
+ switch( t->GetType() )
+ {
+ case svDoubleRef:
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
+ return true;
+ [[fallthrough]];
+ }
+
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
+ return true;
+ }
+ break;
+
+ case svIndex:
+ {
+ if( t->GetOpCode() == ocName ) // DB areas always absolute
+ if( ScRangeData* pRangeData = pDoc->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()) )
+ if( (nRecursion < 42) && lcl_HasRelRef( pDoc, pRangeData->GetCode(), nRecursion + 1 ) )
+ return true;
+ }
+ break;
+
+ // #i34474# function result dependent on cell position
+ case svByte:
+ {
+ switch( t->GetOpCode() )
+ {
+ case ocRow: // ROW() returns own row index
+ case ocColumn: // COLUMN() returns own column index
+ case ocSheet: // SHEET() returns own sheet index
+ case ocCell: // CELL() may return own cell address
+ return true;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ return false;
+}
+
+namespace {
+
+void start_listen_to(ScFormulaListener& rListener, const ScTokenArray* pTokens, const ScRangeList& rRangeList)
+{
+ size_t n = rRangeList.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ rListener.addTokenArray(pTokens, rRange);
+ }
+}
+
+}
+
+void ScConditionEntry::StartListening()
+{
+ if (!pCondFormat)
+ return;
+
+ mpRepaintTask = std::make_unique<RepaintInIdle>(pCondFormat);
+ const ScRangeList& rRanges = pCondFormat->GetRange();
+ mpListener->stopListening();
+ start_listen_to(*mpListener, pFormula1.get(), rRanges);
+ start_listen_to(*mpListener, pFormula2.get(), rRanges);
+
+ mpListener->setCallback([&]() { mpRepaintTask->Start();});
+}
+
+void ScConditionEntry::SetParent(ScConditionalFormat* pParent)
+{
+ pCondFormat = pParent;
+ StartListening();
+}
+
+ScConditionEntry::ScConditionEntry( const ScConditionEntry& r ) :
+ ScFormatEntry(r.mpDoc),
+ eOp(r.eOp),
+ nOptions(r.nOptions),
+ nVal1(r.nVal1),
+ nVal2(r.nVal2),
+ aStrVal1(r.aStrVal1),
+ aStrVal2(r.aStrVal2),
+ aStrNmsp1(r.aStrNmsp1),
+ aStrNmsp2(r.aStrNmsp2),
+ eTempGrammar1(r.eTempGrammar1),
+ eTempGrammar2(r.eTempGrammar2),
+ bIsStr1(r.bIsStr1),
+ bIsStr2(r.bIsStr2),
+ aSrcPos(r.aSrcPos),
+ aSrcString(r.aSrcString),
+ bRelRef1(r.bRelRef1),
+ bRelRef2(r.bRelRef2),
+ bFirstRun(true),
+ mpListener(new ScFormulaListener(*r.mpDoc)),
+ eConditionType( r.eConditionType ),
+ pCondFormat(r.pCondFormat),
+ mpRepaintTask()
+{
+ // ScTokenArray copy ctor creates a flat copy
+ if (r.pFormula1)
+ pFormula1.reset( new ScTokenArray( *r.pFormula1 ) );
+ if (r.pFormula2)
+ pFormula2.reset( new ScTokenArray( *r.pFormula2 ) );
+
+ StartListening();
+ // Formula cells are created at IsValid
+}
+
+ScConditionEntry::ScConditionEntry( ScDocument& rDocument, const ScConditionEntry& r ) :
+ ScFormatEntry(&rDocument),
+ eOp(r.eOp),
+ nOptions(r.nOptions),
+ nVal1(r.nVal1),
+ nVal2(r.nVal2),
+ aStrVal1(r.aStrVal1),
+ aStrVal2(r.aStrVal2),
+ aStrNmsp1(r.aStrNmsp1),
+ aStrNmsp2(r.aStrNmsp2),
+ eTempGrammar1(r.eTempGrammar1),
+ eTempGrammar2(r.eTempGrammar2),
+ bIsStr1(r.bIsStr1),
+ bIsStr2(r.bIsStr2),
+ aSrcPos(r.aSrcPos),
+ aSrcString(r.aSrcString),
+ bRelRef1(r.bRelRef1),
+ bRelRef2(r.bRelRef2),
+ bFirstRun(true),
+ mpListener(new ScFormulaListener(rDocument)),
+ eConditionType( r.eConditionType),
+ pCondFormat(r.pCondFormat),
+ mpRepaintTask()
+{
+ // Real copy of the formulas (for Ref Undo)
+ if (r.pFormula1)
+ pFormula1 = r.pFormula1->Clone();
+ if (r.pFormula2)
+ pFormula2 = r.pFormula2->Clone();
+
+ // Formula cells are created at IsValid
+ // TODO: But not in the Clipboard! So interpret beforehand!
+}
+
+ScConditionEntry::ScConditionEntry( ScConditionMode eOper,
+ const OUString& rExpr1, const OUString& rExpr2, ScDocument& rDocument, const ScAddress& rPos,
+ const OUString& rExprNmsp1, const OUString& rExprNmsp2,
+ FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2,
+ Type eType ) :
+ ScFormatEntry(&rDocument),
+ eOp(eOper),
+ nOptions(0),
+ nVal1(0.0),
+ nVal2(0.0),
+ aStrNmsp1(rExprNmsp1),
+ aStrNmsp2(rExprNmsp2),
+ eTempGrammar1(eGrammar1),
+ eTempGrammar2(eGrammar2),
+ bIsStr1(false),
+ bIsStr2(false),
+ aSrcPos(rPos),
+ bRelRef1(false),
+ bRelRef2(false),
+ bFirstRun(true),
+ mpListener(new ScFormulaListener(rDocument)),
+ eConditionType(eType),
+ pCondFormat(nullptr),
+ mpRepaintTask()
+{
+ Compile( rExpr1, rExpr2, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2, false );
+
+ // Formula cells are created at IsValid
+}
+
+ScConditionEntry::ScConditionEntry( ScConditionMode eOper,
+ const ScTokenArray* pArr1, const ScTokenArray* pArr2,
+ ScDocument& rDocument, const ScAddress& rPos ) :
+ ScFormatEntry(&rDocument),
+ eOp(eOper),
+ nOptions(0),
+ nVal1(0.0),
+ nVal2(0.0),
+ eTempGrammar1(FormulaGrammar::GRAM_DEFAULT),
+ eTempGrammar2(FormulaGrammar::GRAM_DEFAULT),
+ bIsStr1(false),
+ bIsStr2(false),
+ aSrcPos(rPos),
+ bRelRef1(false),
+ bRelRef2(false),
+ bFirstRun(true),
+ mpListener(new ScFormulaListener(rDocument)),
+ eConditionType(ScFormatEntry::Type::Condition),
+ pCondFormat(nullptr),
+ mpRepaintTask()
+{
+ if ( pArr1 )
+ {
+ pFormula1.reset( new ScTokenArray( *pArr1 ) );
+ SimplifyCompiledFormula( pFormula1, nVal1, bIsStr1, aStrVal1 );
+ bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
+ }
+ if ( pArr2 )
+ {
+ pFormula2.reset( new ScTokenArray( *pArr2 ) );
+ SimplifyCompiledFormula( pFormula2, nVal2, bIsStr2, aStrVal2 );
+ bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
+ }
+
+ StartListening();
+
+ // Formula cells are created at IsValid
+}
+
+ScConditionEntry::~ScConditionEntry()
+{
+}
+
+void ScConditionEntry::SimplifyCompiledFormula( std::unique_ptr<ScTokenArray>& rFormula,
+ double& rVal,
+ bool& rIsStr,
+ OUString& rStrVal )
+{
+ if ( rFormula->GetLen() != 1 )
+ return;
+
+ // Single (constant number)?
+ FormulaToken* pToken = rFormula->FirstToken();
+ if ( pToken->GetOpCode() != ocPush )
+ return;
+
+ if ( pToken->GetType() == svDouble )
+ {
+ rVal = pToken->GetDouble();
+ rFormula.reset(); // Do not remember as formula
+ }
+ else if ( pToken->GetType() == svString )
+ {
+ rIsStr = true;
+ rStrVal = pToken->GetString().getString();
+ rFormula.reset(); // Do not remember as formula
+ }
+}
+
+void ScConditionEntry::SetOperation(ScConditionMode eMode)
+{
+ eOp = eMode;
+}
+
+void ScConditionEntry::Compile( const OUString& rExpr1, const OUString& rExpr2,
+ const OUString& rExprNmsp1, const OUString& rExprNmsp2,
+ FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2, bool bTextToReal )
+{
+ if ( !rExpr1.isEmpty() || !rExpr2.isEmpty() )
+ {
+ ScCompiler aComp( *mpDoc, aSrcPos );
+
+ if ( !rExpr1.isEmpty() )
+ {
+ pFormula1.reset();
+ aComp.SetGrammar( eGrammar1 );
+ if ( mpDoc->IsImportingXML() && !bTextToReal )
+ {
+ // temporary formula string as string tokens
+ pFormula1.reset( new ScTokenArray(*mpDoc) );
+ pFormula1->AssignXMLString( rExpr1, rExprNmsp1 );
+ // bRelRef1 is set when the formula is compiled again (CompileXML)
+ }
+ else
+ {
+ pFormula1 = aComp.CompileString( rExpr1, rExprNmsp1 );
+ SimplifyCompiledFormula( pFormula1, nVal1, bIsStr1, aStrVal1 );
+ bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
+ }
+ }
+
+ if ( !rExpr2.isEmpty() )
+ {
+ pFormula2.reset();
+ aComp.SetGrammar( eGrammar2 );
+ if ( mpDoc->IsImportingXML() && !bTextToReal )
+ {
+ // temporary formula string as string tokens
+ pFormula2.reset( new ScTokenArray(*mpDoc) );
+ pFormula2->AssignXMLString( rExpr2, rExprNmsp2 );
+ // bRelRef2 is set when the formula is compiled again (CompileXML)
+ }
+ else
+ {
+ pFormula2 = aComp.CompileString( rExpr2, rExprNmsp2 );
+ SimplifyCompiledFormula( pFormula2, nVal2, bIsStr2, aStrVal2 );
+ bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
+ }
+ }
+ }
+
+ StartListening();
+}
+
+/**
+ * Create formula cells
+ */
+void ScConditionEntry::MakeCells( const ScAddress& rPos )
+{
+ if ( mpDoc->IsClipOrUndo() ) // Never calculate in the Clipboard!
+ return;
+
+ if ( pFormula1 && !pFCell1 && !bRelRef1 )
+ {
+ // pFCell1 will hold a flat-copied ScTokenArray sharing ref-counted
+ // code tokens with pFormula1
+ pFCell1.reset( new ScFormulaCell(*mpDoc, rPos, *pFormula1) );
+ pFCell1->SetFreeFlying(true);
+ pFCell1->StartListeningTo( *mpDoc );
+ }
+
+ if ( pFormula2 && !pFCell2 && !bRelRef2 )
+ {
+ // pFCell2 will hold a flat-copied ScTokenArray sharing ref-counted
+ // code tokens with pFormula2
+ pFCell2.reset( new ScFormulaCell(*mpDoc, rPos, *pFormula2) );
+ pFCell2->SetFreeFlying(true);
+ pFCell2->StartListeningTo( *mpDoc );
+ }
+}
+
+void ScConditionEntry::SetIgnoreBlank(bool bSet)
+{
+ // The bit SC_COND_NOBLANKS is set if blanks are not ignored
+ // (only of valid)
+ if (bSet)
+ nOptions &= ~SC_COND_NOBLANKS;
+ else
+ nOptions |= SC_COND_NOBLANKS;
+}
+
+/**
+ * Delete formula cells, so we re-compile at the next IsValid
+ */
+void ScConditionEntry::CompileAll()
+{
+ pFCell1.reset();
+ pFCell2.reset();
+}
+
+void ScConditionEntry::CompileXML()
+{
+ // First parse the formula source position if it was stored as text
+ if ( !aSrcString.isEmpty() )
+ {
+ ScAddress aNew;
+ /* XML is always in OOo:A1 format, although R1C1 would be more amenable
+ * to compression */
+ if ( aNew.Parse( aSrcString, *mpDoc ) & ScRefFlags::VALID )
+ aSrcPos = aNew;
+ // if the position is invalid, there isn't much we can do at this time
+ aSrcString.clear();
+ }
+
+ // Convert the text tokens that were created during XML import into real tokens.
+ Compile( GetExpression(aSrcPos, 0, 0, eTempGrammar1),
+ GetExpression(aSrcPos, 1, 0, eTempGrammar2),
+ aStrNmsp1, aStrNmsp2, eTempGrammar1, eTempGrammar2, true );
+
+ // Importing ocDde/ocWebservice?
+ if (pFormula1)
+ mpDoc->CheckLinkFormulaNeedingCheck(*pFormula1);
+ if (pFormula2)
+ mpDoc->CheckLinkFormulaNeedingCheck(*pFormula2);
+}
+
+void ScConditionEntry::SetSrcString( const OUString& rNew )
+{
+ // aSrcString is only evaluated in CompileXML
+ SAL_WARN_IF( !mpDoc->IsImportingXML(), "sc", "SetSrcString is only valid for XML import" );
+
+ aSrcString = rNew;
+}
+
+void ScConditionEntry::SetFormula1( const ScTokenArray& rArray )
+{
+ pFormula1.reset();
+ if( rArray.GetLen() > 0 )
+ {
+ pFormula1.reset( new ScTokenArray( rArray ) );
+ SimplifyCompiledFormula(pFormula1, nVal1, bIsStr1, aStrVal1);
+ bRelRef1 = lcl_HasRelRef( mpDoc, pFormula1.get() );
+ }
+
+ StartListening();
+}
+
+void ScConditionEntry::SetFormula2( const ScTokenArray& rArray )
+{
+ pFormula2.reset();
+ if( rArray.GetLen() > 0 )
+ {
+ pFormula2.reset( new ScTokenArray( rArray ) );
+ SimplifyCompiledFormula(pFormula2, nVal2, bIsStr2, aStrVal2);
+ bRelRef2 = lcl_HasRelRef( mpDoc, pFormula2.get() );
+ }
+
+ StartListening();
+}
+
+void ScConditionEntry::UpdateReference( sc::RefUpdateContext& rCxt )
+{
+ if(pCondFormat)
+ aSrcPos = pCondFormat->GetRange().Combine().aStart;
+ ScAddress aOldSrcPos = aSrcPos;
+ bool bChangedPos = false;
+ if (rCxt.meMode == URM_INSDEL && rCxt.maRange.Contains(aSrcPos))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aSrcPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, *mpDoc))
+ {
+ assert(!"can't move ScConditionEntry");
+ }
+ bChangedPos = aSrcPos != aOldSrcPos;
+ }
+
+ if (pFormula1)
+ {
+ sc::RefUpdateResult aRes;
+ switch (rCxt.meMode)
+ {
+ case URM_INSDEL:
+ aRes = pFormula1->AdjustReferenceOnShift(rCxt, aOldSrcPos);
+ break;
+ case URM_MOVE:
+ aRes = pFormula1->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
+ break;
+ default:
+ ;
+ }
+
+ if (aRes.mbReferenceModified || bChangedPos)
+ pFCell1.reset(); // is created again in IsValid
+ }
+
+ if (pFormula2)
+ {
+ sc::RefUpdateResult aRes;
+ switch (rCxt.meMode)
+ {
+ case URM_INSDEL:
+ aRes = pFormula2->AdjustReferenceOnShift(rCxt, aOldSrcPos);
+ break;
+ case URM_MOVE:
+ aRes = pFormula2->AdjustReferenceOnMove(rCxt, aOldSrcPos, aSrcPos);
+ break;
+ default:
+ ;
+ }
+
+ if (aRes.mbReferenceModified || bChangedPos)
+ pFCell2.reset(); // is created again in IsValid
+ }
+
+ StartListening();
+}
+
+void ScConditionEntry::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ if (pFormula1)
+ {
+ pFormula1->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
+ pFCell1.reset();
+ }
+
+ if (pFormula2)
+ {
+ pFormula2->AdjustReferenceOnInsertedTab(rCxt, aSrcPos);
+ pFCell2.reset();
+ }
+
+ ScRangeUpdater::UpdateInsertTab(aSrcPos, rCxt);
+}
+
+void ScConditionEntry::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ if (pFormula1)
+ {
+ pFormula1->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
+ pFCell1.reset();
+ }
+
+ if (pFormula2)
+ {
+ pFormula2->AdjustReferenceOnDeletedTab(rCxt, aSrcPos);
+ pFCell2.reset();
+ }
+
+ ScRangeUpdater::UpdateDeleteTab(aSrcPos, rCxt);
+ StartListening();
+}
+
+void ScConditionEntry::UpdateMoveTab(sc::RefUpdateMoveTabContext& rCxt)
+{
+ sc::RefUpdateResult aResFinal;
+ aResFinal.mnTab = aSrcPos.Tab();
+ if (pFormula1)
+ {
+ sc::RefUpdateResult aRes = pFormula1->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
+ if (aRes.mbValueChanged)
+ aResFinal.mnTab = aRes.mnTab;
+ pFCell1.reset();
+ }
+
+ if (pFormula2)
+ {
+ sc::RefUpdateResult aRes = pFormula2->AdjustReferenceOnMovedTab(rCxt, aSrcPos);
+ if (aRes.mbValueChanged)
+ aResFinal.mnTab = aRes.mnTab;
+ pFCell2.reset();
+ }
+
+ if (aResFinal.mnTab != aSrcPos.Tab())
+ aSrcPos.SetTab(aResFinal.mnTab);
+
+ StartListening();
+}
+
+static bool lcl_IsEqual( const std::unique_ptr<ScTokenArray>& pArr1, const std::unique_ptr<ScTokenArray>& pArr2 )
+{
+ // We only compare the non-RPN array
+ if ( pArr1 && pArr2 )
+ return pArr1->EqualTokens( pArr2.get() );
+ else
+ return !pArr1 && !pArr2; // Both 0? -> the same
+}
+
+// virtual
+bool ScConditionEntry::IsEqual( const ScFormatEntry& rOther, bool bIgnoreSrcPos ) const
+{
+ if (GetType() != rOther.GetType())
+ return false;
+
+ const ScConditionEntry& r = static_cast<const ScConditionEntry&>(rOther);
+
+ bool bEq = (eOp == r.eOp && nOptions == r.nOptions &&
+ lcl_IsEqual( pFormula1, r.pFormula1 ) &&
+ lcl_IsEqual( pFormula2, r.pFormula2 ));
+
+ if (!bIgnoreSrcPos)
+ {
+ // for formulas, the reference positions must be compared, too
+ // (including aSrcString, for inserting the entries during XML import)
+ if ( bEq && ( pFormula1 || pFormula2 ) && ( aSrcPos != r.aSrcPos || aSrcString != r.aSrcString ) )
+ bEq = false;
+ }
+
+ // If not formulas, compare values
+ if ( bEq && !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) )
+ bEq = false;
+ if ( bEq && !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) )
+ bEq = false;
+
+ return bEq;
+}
+
+void ScConditionEntry::Interpret( const ScAddress& rPos )
+{
+ // Create formula cells
+ // Note: New Broadcaster (Note cells) may be inserted into the document!
+ if ( ( pFormula1 && !pFCell1 ) || ( pFormula2 && !pFCell2 ) )
+ MakeCells( rPos );
+
+ // Evaluate formulas
+ bool bDirty = false; // 1 and 2 separate?
+
+ std::optional<ScFormulaCell> oTemp;
+ ScFormulaCell* pEff1 = pFCell1.get();
+ if ( bRelRef1 )
+ {
+ if (pFormula1)
+ oTemp.emplace(*mpDoc, rPos, *pFormula1);
+ else
+ oTemp.emplace(*mpDoc, rPos);
+ pEff1 = &*oTemp;
+ pEff1->SetFreeFlying(true);
+ }
+ if ( pEff1 )
+ {
+ if (!pEff1->IsRunning()) // Don't create 522
+ {
+ //TODO: Query Changed instead of Dirty!
+ if (pEff1->GetDirty() && !bRelRef1 && mpDoc->GetAutoCalc())
+ bDirty = true;
+ if (pEff1->IsValue())
+ {
+ bIsStr1 = false;
+ nVal1 = pEff1->GetValue();
+ aStrVal1.clear();
+ }
+ else
+ {
+ bIsStr1 = true;
+ aStrVal1 = pEff1->GetString().getString();
+ nVal1 = 0.0;
+ }
+ }
+ }
+ oTemp.reset();
+
+ ScFormulaCell* pEff2 = pFCell2.get(); //@ 1!=2
+ if ( bRelRef2 )
+ {
+ if (pFormula2)
+ oTemp.emplace(*mpDoc, rPos, *pFormula2);
+ else
+ oTemp.emplace(*mpDoc, rPos);
+ pEff2 = &*oTemp;
+ pEff2->SetFreeFlying(true);
+ }
+ if ( pEff2 )
+ {
+ if (!pEff2->IsRunning()) // Don't create 522
+ {
+ if (pEff2->GetDirty() && !bRelRef2 && mpDoc->GetAutoCalc())
+ bDirty = true;
+ if (pEff2->IsValue())
+ {
+ bIsStr2 = false;
+ nVal2 = pEff2->GetValue();
+ aStrVal2.clear();
+ }
+ else
+ {
+ bIsStr2 = true;
+ aStrVal2 = pEff2->GetString().getString();
+ nVal2 = 0.0;
+ }
+ }
+ }
+ oTemp.reset();
+
+ // If IsRunning, the last values remain
+ if (bDirty && !bFirstRun)
+ {
+ // Repaint everything for dependent formats
+ DataChanged();
+ }
+
+ bFirstRun = false;
+}
+
+static bool lcl_GetCellContent( ScRefCellValue& rCell, bool bIsStr1, double& rArg, OUString& rArgStr,
+ const ScDocument* pDoc )
+{
+
+ if (rCell.isEmpty())
+ return !bIsStr1;
+
+ bool bVal = true;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ rArg = rCell.getDouble();
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ bVal = rCell.getFormula()->IsValue();
+ if (bVal)
+ rArg = rCell.getFormula()->GetValue();
+ else
+ rArgStr = rCell.getFormula()->GetString().getString();
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ bVal = false;
+ if (rCell.getType() == CELLTYPE_STRING)
+ rArgStr = rCell.getSharedString()->getString();
+ else if (rCell.getEditText())
+ rArgStr = ScEditUtil::GetString(*rCell.getEditText(), pDoc);
+ break;
+ default:
+ ;
+ }
+
+ return bVal;
+}
+
+void ScConditionEntry::FillCache() const
+{
+ if(mpCache)
+ return;
+
+ const ScRangeList& rRanges = pCondFormat->GetRange();
+ mpCache.reset(new ScConditionEntryCache);
+ size_t nListCount = rRanges.size();
+ for( size_t i = 0; i < nListCount; i++ )
+ {
+ const ScRange & rRange = rRanges[i];
+ SCROW nRow = rRange.aEnd.Row();
+ SCCOL nCol = rRange.aEnd.Col();
+ SCCOL nColStart = rRange.aStart.Col();
+ SCROW nRowStart = rRange.aStart.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ // temporary fix to workaround slow duplicate entry
+ // conditions, prevent to use a whole row
+ if(nRow == mpDoc->MaxRow())
+ {
+ bool bShrunk = false;
+ mpDoc->ShrinkToUsedDataArea(bShrunk, nTab, nColStart, nRowStart,
+ nCol, nRow, false);
+ }
+
+ for( SCROW r = nRowStart; r <= nRow; r++ )
+ for( SCCOL c = nColStart; c <= nCol; c++ )
+ {
+ ScRefCellValue aCell(*mpDoc, ScAddress(c, r, nTab));
+ if (aCell.isEmpty())
+ continue;
+
+ double nVal = 0.0;
+ OUString aStr;
+ if (!lcl_GetCellContent(aCell, false, nVal, aStr, mpDoc))
+ {
+ std::pair<ScConditionEntryCache::StringCacheType::iterator, bool> aResult =
+ mpCache->maStrings.emplace(aStr, 1);
+
+ if(!aResult.second)
+ aResult.first->second++;
+ }
+ else
+ {
+ std::pair<ScConditionEntryCache::ValueCacheType::iterator, bool> aResult =
+ mpCache->maValues.emplace(nVal, 1);
+
+ if(!aResult.second)
+ aResult.first->second++;
+
+ ++(mpCache->nValueItems);
+ }
+ }
+ }
+}
+
+bool ScConditionEntry::IsDuplicate( double nArg, const OUString& rStr ) const
+{
+ FillCache();
+
+ if(rStr.isEmpty())
+ {
+ ScConditionEntryCache::ValueCacheType::iterator itr = mpCache->maValues.find(nArg);
+ if(itr == mpCache->maValues.end())
+ return false;
+ else
+ {
+ return itr->second > 1;
+ }
+ }
+ else
+ {
+ ScConditionEntryCache::StringCacheType::iterator itr = mpCache->maStrings.find(rStr);
+ if(itr == mpCache->maStrings.end())
+ return false;
+ else
+ {
+ return itr->second > 1;
+ }
+ }
+}
+
+bool ScConditionEntry::IsTopNElement( double nArg ) const
+{
+ FillCache();
+
+ if(mpCache->nValueItems <= nVal1)
+ return true;
+
+ size_t nCells = 0;
+ for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
+ itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
+ {
+ if(nCells >= nVal1)
+ return false;
+ if(itr->first <= nArg)
+ return true;
+ nCells += itr->second;
+ }
+
+ return true;
+}
+
+bool ScConditionEntry::IsBottomNElement( double nArg ) const
+{
+ FillCache();
+
+ if(mpCache->nValueItems <= nVal1)
+ return true;
+
+ size_t nCells = 0;
+ for(const auto& [rVal, rCount] : mpCache->maValues)
+ {
+ if(nCells >= nVal1)
+ return false;
+ if(rVal >= nArg)
+ return true;
+ nCells += rCount;
+ }
+
+ return true;
+}
+
+bool ScConditionEntry::IsTopNPercent( double nArg ) const
+{
+ FillCache();
+
+ size_t nCells = 0;
+ size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
+ for(ScConditionEntryCache::ValueCacheType::const_reverse_iterator itr = mpCache->maValues.rbegin(),
+ itrEnd = mpCache->maValues.rend(); itr != itrEnd; ++itr)
+ {
+ if(nCells >= nLimitCells)
+ return false;
+ if(itr->first <= nArg)
+ return true;
+ nCells += itr->second;
+ }
+
+ return true;
+}
+
+bool ScConditionEntry::IsBottomNPercent( double nArg ) const
+{
+ FillCache();
+
+ size_t nCells = 0;
+ size_t nLimitCells = static_cast<size_t>(mpCache->nValueItems*nVal1/100);
+ for(const auto& [rVal, rCount] : mpCache->maValues)
+ {
+ if(nCells >= nLimitCells)
+ return false;
+ if(rVal >= nArg)
+ return true;
+ nCells += rCount;
+ }
+
+ return true;
+}
+
+bool ScConditionEntry::IsBelowAverage( double nArg, bool bEqual ) const
+{
+ FillCache();
+
+ double nSum = std::accumulate(mpCache->maValues.begin(), mpCache->maValues.end(), double(0),
+ [](const double& rSum, const ScConditionEntryCache::ValueCacheType::value_type& rEntry) {
+ return rSum + rEntry.first * rEntry.second; });
+
+ if(bEqual)
+ return (nArg <= nSum/mpCache->nValueItems);
+ else
+ return (nArg < nSum/mpCache->nValueItems);
+}
+
+bool ScConditionEntry::IsAboveAverage( double nArg, bool bEqual ) const
+{
+ FillCache();
+
+ double nSum = std::accumulate(mpCache->maValues.begin(), mpCache->maValues.end(), double(0),
+ [](const double& rSum, const ScConditionEntryCache::ValueCacheType::value_type& rEntry) {
+ return rSum + rEntry.first * rEntry.second; });
+
+ if(bEqual)
+ return (nArg >= nSum/mpCache->nValueItems);
+ else
+ return (nArg > nSum/mpCache->nValueItems);
+}
+
+bool ScConditionEntry::IsError( const ScAddress& rPos ) const
+{
+ ScRefCellValue rCell(*mpDoc, rPos);
+
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ {
+ if (rCell.getFormula()->GetErrCode() != FormulaError::NONE)
+ return true;
+ }
+
+ return false;
+}
+
+bool ScConditionEntry::IsValid( double nArg, const ScAddress& rPos ) const
+{
+ // Interpret must already have been called
+ if ( bIsStr1 )
+ {
+ switch( eOp )
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ break;
+ case ScConditionMode::NotEqual:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
+ if ( bIsStr2 )
+ return false;
+
+ double nComp1 = nVal1; // Copy, so that it can be changed
+ double nComp2 = nVal2;
+
+ if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
+ if ( nComp1 > nComp2 )
+ // Right order for value range
+ std::swap( nComp1, nComp2 );
+
+ // All corner cases need to be tested with ::rtl::math::approxEqual!
+ bool bValid = false;
+ switch (eOp)
+ {
+ case ScConditionMode::NONE:
+ break; // Always sal_False
+ case ScConditionMode::Equal:
+ bValid = ::rtl::math::approxEqual( nArg, nComp1 );
+ break;
+ case ScConditionMode::NotEqual:
+ bValid = !::rtl::math::approxEqual( nArg, nComp1 );
+ break;
+ case ScConditionMode::Greater:
+ bValid = ( nArg > nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
+ break;
+ case ScConditionMode::EqGreater:
+ bValid = ( nArg >= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
+ break;
+ case ScConditionMode::Less:
+ bValid = ( nArg < nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 );
+ break;
+ case ScConditionMode::EqLess:
+ bValid = ( nArg <= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 );
+ break;
+ case ScConditionMode::Between:
+ bValid = ( nArg >= nComp1 && nArg <= nComp2 ) ||
+ ::rtl::math::approxEqual( nArg, nComp1 ) || ::rtl::math::approxEqual( nArg, nComp2 );
+ break;
+ case ScConditionMode::NotBetween:
+ bValid = ( nArg < nComp1 || nArg > nComp2 ) &&
+ !::rtl::math::approxEqual( nArg, nComp1 ) && !::rtl::math::approxEqual( nArg, nComp2 );
+ break;
+ case ScConditionMode::Duplicate:
+ case ScConditionMode::NotDuplicate:
+ if( pCondFormat )
+ {
+ bValid = IsDuplicate( nArg, OUString() );
+ if( eOp == ScConditionMode::NotDuplicate )
+ bValid = !bValid;
+ }
+ break;
+ case ScConditionMode::Direct:
+ bValid = nComp1 != 0.0;
+ break;
+ case ScConditionMode::Top10:
+ bValid = IsTopNElement( nArg );
+ break;
+ case ScConditionMode::Bottom10:
+ bValid = IsBottomNElement( nArg );
+ break;
+ case ScConditionMode::TopPercent:
+ bValid = IsTopNPercent( nArg );
+ break;
+ case ScConditionMode::BottomPercent:
+ bValid = IsBottomNPercent( nArg );
+ break;
+ case ScConditionMode::AboveAverage:
+ case ScConditionMode::AboveEqualAverage:
+ bValid = IsAboveAverage( nArg, eOp == ScConditionMode::AboveEqualAverage );
+ break;
+ case ScConditionMode::BelowAverage:
+ case ScConditionMode::BelowEqualAverage:
+ bValid = IsBelowAverage( nArg, eOp == ScConditionMode::BelowEqualAverage );
+ break;
+ case ScConditionMode::Error:
+ case ScConditionMode::NoError:
+ bValid = IsError( rPos );
+ if( eOp == ScConditionMode::NoError )
+ bValid = !bValid;
+ break;
+ case ScConditionMode::BeginsWith:
+ if(aStrVal1.isEmpty())
+ {
+ OUString aStr = OUString::number(nVal1);
+ OUString aStr2 = OUString::number(nArg);
+ bValid = aStr2.startsWith(aStr);
+ }
+ else
+ {
+ OUString aStr2 = OUString::number(nArg);
+ bValid = aStr2.startsWith(aStrVal1);
+ }
+ break;
+ case ScConditionMode::EndsWith:
+ if(aStrVal1.isEmpty())
+ {
+ OUString aStr = OUString::number(nVal1);
+ OUString aStr2 = OUString::number(nArg);
+ bValid = aStr2.endsWith(aStr);
+ }
+ else
+ {
+ OUString aStr2 = OUString::number(nArg);
+ bValid = aStr2.endsWith(aStrVal1);
+ }
+ break;
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ if(aStrVal1.isEmpty())
+ {
+ OUString aStr = OUString::number(nVal1);
+ OUString aStr2 = OUString::number(nArg);
+ bValid = aStr2.indexOf(aStr) != -1;
+ }
+ else
+ {
+ OUString aStr2 = OUString::number(nArg);
+ bValid = aStr2.indexOf(aStrVal1) != -1;
+ }
+
+ if( eOp == ScConditionMode::NotContainsText )
+ bValid = !bValid;
+ break;
+ default:
+ SAL_WARN("sc", "unknown operation at ScConditionEntry");
+ break;
+ }
+ return bValid;
+}
+
+bool ScConditionEntry::IsValidStr( const OUString& rArg, const ScAddress& rPos ) const
+{
+ bool bValid = false;
+ // Interpret must already have been called
+ if ( eOp == ScConditionMode::Direct ) // Formula is independent from the content
+ return nVal1 != 0.0;
+
+ if ( eOp == ScConditionMode::Duplicate || eOp == ScConditionMode::NotDuplicate )
+ {
+ if( pCondFormat && !rArg.isEmpty() )
+ {
+ bValid = IsDuplicate( 0.0, rArg );
+ if( eOp == ScConditionMode::NotDuplicate )
+ bValid = !bValid;
+ return bValid;
+ }
+ }
+
+ if (eOp == ScConditionMode::Error)
+ return IsError(rPos);
+ if (eOp == ScConditionMode::NoError)
+ return !IsError(rPos);
+
+ // If number contains condition, always false, except for "not equal".
+ if (!bIsStr1)
+ return ( eOp == ScConditionMode::NotEqual );
+ if ( eOp == ScConditionMode::Between || eOp == ScConditionMode::NotBetween )
+ if ( !bIsStr2 )
+ return false;
+
+ OUString aUpVal1( aStrVal1 ); //TODO: As a member? (Also set in Interpret)
+ OUString aUpVal2( aStrVal2 );
+
+ switch ( eOp )
+ {
+ case ScConditionMode::Equal:
+ bValid = ScGlobal::GetTransliteration().isEqual(aUpVal1, rArg);
+ break;
+ case ScConditionMode::NotEqual:
+ bValid = !ScGlobal::GetTransliteration().isEqual(aUpVal1, rArg);
+ break;
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::BottomPercent:
+ case ScConditionMode::Top10:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::AboveAverage:
+ case ScConditionMode::BelowAverage:
+ return false;
+ case ScConditionMode::BeginsWith:
+ bValid = ScGlobal::GetTransliteration().isMatch(aUpVal1, rArg);
+ break;
+ case ScConditionMode::EndsWith:
+ {
+ sal_Int32 nStart = rArg.getLength();
+ const sal_Int32 nLen = aUpVal1.getLength();
+ if (nLen > nStart)
+ bValid = false;
+ else
+ {
+ nStart = nStart - nLen;
+ sal_Int32 nMatch1(0), nMatch2(0);
+ bValid = ScGlobal::GetTransliteration().equals(rArg, nStart, nLen, nMatch1,
+ aUpVal1, 0, nLen, nMatch2);
+ }
+ }
+ break;
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ {
+ const OUString aArgStr(ScGlobal::getCharClass().lowercase(rArg));
+ const OUString aValStr(ScGlobal::getCharClass().lowercase(aUpVal1));
+ bValid = aArgStr.indexOf(aValStr) != -1;
+
+ if(eOp == ScConditionMode::NotContainsText)
+ bValid = !bValid;
+ }
+ break;
+ default:
+ {
+ sal_Int32 nCompare = ScGlobal::GetCollator().compareString(
+ rArg, aUpVal1 );
+ switch ( eOp )
+ {
+ case ScConditionMode::Greater:
+ bValid = ( nCompare > 0 );
+ break;
+ case ScConditionMode::EqGreater:
+ bValid = ( nCompare >= 0 );
+ break;
+ case ScConditionMode::Less:
+ bValid = ( nCompare < 0 );
+ break;
+ case ScConditionMode::EqLess:
+ bValid = ( nCompare <= 0 );
+ break;
+ case ScConditionMode::Between:
+ case ScConditionMode::NotBetween:
+ {
+ const sal_Int32 nCompare2 = ScGlobal::GetCollator().compareString(rArg, aUpVal2);
+ // Test for NOTBETWEEN:
+ bValid = (nCompare > 0 && nCompare2 > 0) || (nCompare < 0 && nCompare2 < 0);
+ if ( eOp == ScConditionMode::Between )
+ bValid = !bValid;
+ break;
+ }
+ default:
+ SAL_WARN("sc", "unknown operation in ScConditionEntry");
+ bValid = false;
+ break;
+ }
+ }
+ }
+ return bValid;
+}
+
+bool ScConditionEntry::IsCellValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
+{
+ const_cast<ScConditionEntry*>(this)->Interpret(rPos); // Evaluate formula
+
+ if ( eOp == ScConditionMode::Direct )
+ return nVal1 != 0.0;
+
+ double nArg = 0.0;
+ OUString aArgStr;
+ bool bVal = lcl_GetCellContent( rCell, bIsStr1, nArg, aArgStr, mpDoc );
+ if (bVal)
+ return IsValid( nArg, rPos );
+ else
+ return IsValidStr( aArgStr, rPos );
+}
+
+OUString ScConditionEntry::GetExpression( const ScAddress& rCursor, sal_uInt16 nIndex,
+ sal_uInt32 nNumFmt,
+ const FormulaGrammar::Grammar eGrammar ) const
+{
+ assert( nIndex <= 1);
+ OUString aRet;
+
+ if ( FormulaGrammar::isEnglish( eGrammar) && nNumFmt == 0 )
+ nNumFmt = mpDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US );
+
+ if ( nIndex==0 )
+ {
+ if ( pFormula1 )
+ {
+ ScCompiler aComp(*mpDoc, rCursor, *pFormula1, eGrammar);
+ OUStringBuffer aBuffer;
+ aComp.CreateStringFromTokenArray( aBuffer );
+ aRet = aBuffer.makeStringAndClear();
+ }
+ else if (bIsStr1)
+ {
+ aRet = "\"" + aStrVal1 + "\"";
+ }
+ else
+ mpDoc->GetFormatTable()->GetInputLineString(nVal1, nNumFmt, aRet);
+ }
+ else if ( nIndex==1 )
+ {
+ if ( pFormula2 )
+ {
+ ScCompiler aComp(*mpDoc, rCursor, *pFormula2, eGrammar);
+ OUStringBuffer aBuffer;
+ aComp.CreateStringFromTokenArray( aBuffer );
+ aRet = aBuffer.makeStringAndClear();
+ }
+ else if (bIsStr2)
+ {
+ aRet = "\"" + aStrVal2 + "\"";
+ }
+ else
+ mpDoc->GetFormatTable()->GetInputLineString(nVal2, nNumFmt, aRet);
+ }
+
+ return aRet;
+}
+
+std::unique_ptr<ScTokenArray> ScConditionEntry::CreateFlatCopiedTokenArray( sal_uInt16 nIndex ) const
+{
+ assert(nIndex <= 1);
+ std::unique_ptr<ScTokenArray> pRet;
+
+ if ( nIndex==0 )
+ {
+ if ( pFormula1 )
+ pRet.reset(new ScTokenArray( *pFormula1 ));
+ else
+ {
+ pRet.reset(new ScTokenArray(*mpDoc));
+ if (bIsStr1)
+ {
+ svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
+ pRet->AddString(rSPool.intern(aStrVal1));
+ }
+ else
+ pRet->AddDouble( nVal1 );
+ }
+ }
+ else if ( nIndex==1 )
+ {
+ if ( pFormula2 )
+ pRet.reset(new ScTokenArray( *pFormula2 ));
+ else
+ {
+ pRet.reset(new ScTokenArray(*mpDoc));
+ if (bIsStr2)
+ {
+ svl::SharedStringPool& rSPool = mpDoc->GetSharedStringPool();
+ pRet->AddString(rSPool.intern(aStrVal2));
+ }
+ else
+ pRet->AddDouble( nVal2 );
+ }
+ }
+
+ return pRet;
+}
+
+/**
+ * Return a position that's adjusted to allow textual representation
+ * of expressions if possible
+ */
+ScAddress ScConditionEntry::GetValidSrcPos() const
+{
+ SCTAB nMinTab = aSrcPos.Tab();
+ SCTAB nMaxTab = nMinTab;
+
+ for (sal_uInt16 nPass = 0; nPass < 2; nPass++)
+ {
+ ScTokenArray* pFormula = nPass ? pFormula2.get() : pFormula1.get();
+ if (pFormula)
+ {
+ for ( auto t: pFormula->References() )
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ ScAddress aAbs = rRef1.toAbs(*mpDoc, aSrcPos);
+ if (!rRef1.IsTabDeleted())
+ {
+ if (aAbs.Tab() < nMinTab)
+ nMinTab = aAbs.Tab();
+ if (aAbs.Tab() > nMaxTab)
+ nMaxTab = aAbs.Tab();
+ }
+ if ( t->GetType() == svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ aAbs = rRef2.toAbs(*mpDoc, aSrcPos);
+ if (!rRef2.IsTabDeleted())
+ {
+ if (aAbs.Tab() < nMinTab)
+ nMinTab = aAbs.Tab();
+ if (aAbs.Tab() > nMaxTab)
+ nMaxTab = aAbs.Tab();
+ }
+ }
+ }
+ }
+ }
+
+ ScAddress aValidPos = aSrcPos;
+ SCTAB nTabCount = mpDoc->GetTableCount();
+ if ( nMaxTab >= nTabCount && nMinTab > 0 )
+ aValidPos.SetTab( aSrcPos.Tab() - nMinTab ); // so the lowest tab ref will be on 0
+
+ if ( aValidPos.Tab() >= nTabCount )
+ aValidPos.SetTab( nTabCount - 1 ); // ensure a valid position even if some references will be invalid
+
+ return aValidPos;
+}
+
+void ScConditionEntry::DataChanged() const
+{
+ //FIXME: Nothing so far
+}
+
+bool ScConditionEntry::MarkUsedExternalReferences() const
+{
+ bool bAllMarked = false;
+ for (sal_uInt16 nPass = 0; !bAllMarked && nPass < 2; nPass++)
+ {
+ ScTokenArray* pFormula = nPass ? pFormula2.get() : pFormula1.get();
+ if (pFormula)
+ bAllMarked = mpDoc->MarkUsedExternalReferences(*pFormula, aSrcPos);
+ }
+ return bAllMarked;
+}
+
+ScFormatEntry* ScConditionEntry::Clone(ScDocument* pDoc) const
+{
+ return new ScConditionEntry(*pDoc, *this);
+}
+
+ScConditionMode ScConditionEntry::GetModeFromApi(css::sheet::ConditionOperator nOperation)
+{
+ ScConditionMode eMode = ScConditionMode::NONE;
+ switch (static_cast<sal_Int32>(nOperation))
+ {
+ case css::sheet::ConditionOperator2::EQUAL:
+ eMode = ScConditionMode::Equal;
+ break;
+ case css::sheet::ConditionOperator2::LESS:
+ eMode = ScConditionMode::Less;
+ break;
+ case css::sheet::ConditionOperator2::GREATER:
+ eMode = ScConditionMode::Greater;
+ break;
+ case css::sheet::ConditionOperator2::LESS_EQUAL:
+ eMode = ScConditionMode::EqLess;
+ break;
+ case css::sheet::ConditionOperator2::GREATER_EQUAL:
+ eMode = ScConditionMode::EqGreater;
+ break;
+ case css::sheet::ConditionOperator2::NOT_EQUAL:
+ eMode = ScConditionMode::NotEqual;
+ break;
+ case css::sheet::ConditionOperator2::BETWEEN:
+ eMode = ScConditionMode::Between;
+ break;
+ case css::sheet::ConditionOperator2::NOT_BETWEEN:
+ eMode = ScConditionMode::NotBetween;
+ break;
+ case css::sheet::ConditionOperator2::FORMULA:
+ eMode = ScConditionMode::Direct;
+ break;
+ case css::sheet::ConditionOperator2::DUPLICATE:
+ eMode = ScConditionMode::Duplicate;
+ break;
+ case css::sheet::ConditionOperator2::NOT_DUPLICATE:
+ eMode = ScConditionMode::NotDuplicate;
+ break;
+ default:
+ break;
+ }
+ return eMode;
+}
+
+void ScConditionEntry::startRendering()
+{
+ mpCache.reset();
+}
+
+void ScConditionEntry::endRendering()
+{
+ mpCache.reset();
+}
+
+bool ScConditionEntry::NeedsRepaint() const
+{
+ return mpListener->NeedsRepaint();
+}
+
+ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper,
+ const OUString& rExpr1, const OUString& rExpr2,
+ ScDocument& rDocument, const ScAddress& rPos,
+ OUString aStyle,
+ const OUString& rExprNmsp1, const OUString& rExprNmsp2,
+ FormulaGrammar::Grammar eGrammar1,
+ FormulaGrammar::Grammar eGrammar2,
+ ScFormatEntry::Type eType ) :
+ ScConditionEntry( eOper, rExpr1, rExpr2, rDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2, eType ),
+ aStyleName(std::move( aStyle )),
+ eCondFormatType( eType )
+{
+}
+
+ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper,
+ const ScTokenArray* pArr1, const ScTokenArray* pArr2,
+ ScDocument& rDocument, const ScAddress& rPos,
+ OUString aStyle ) :
+ ScConditionEntry( eOper, pArr1, pArr2, rDocument, rPos ),
+ aStyleName(std::move( aStyle ))
+{
+}
+
+ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry& r ) :
+ ScConditionEntry( r ),
+ aStyleName( r.aStyleName ),
+ eCondFormatType( r.eCondFormatType)
+{
+}
+
+ScCondFormatEntry::ScCondFormatEntry( ScDocument& rDocument, const ScCondFormatEntry& r ) :
+ ScConditionEntry( rDocument, r ),
+ aStyleName( r.aStyleName ),
+ eCondFormatType( r.eCondFormatType)
+{
+}
+
+// virtual
+bool ScCondFormatEntry::IsEqual( const ScFormatEntry& r, bool bIgnoreSrcPos ) const
+{
+ return ScConditionEntry::IsEqual(r, bIgnoreSrcPos) &&
+ (aStyleName == static_cast<const ScCondFormatEntry&>(r).aStyleName);
+}
+
+ScCondFormatEntry::~ScCondFormatEntry()
+{
+}
+
+void ScCondFormatEntry::DataChanged() const
+{
+ if ( pCondFormat )
+ pCondFormat->DoRepaint();
+}
+
+ScFormatEntry* ScCondFormatEntry::Clone( ScDocument* pDoc ) const
+{
+ return new ScCondFormatEntry( *pDoc, *this );
+}
+
+void ScConditionEntry::CalcAll()
+{
+ if (pFCell1 || pFCell2)
+ {
+ if (pFCell1)
+ pFCell1->SetDirty();
+ if (pFCell2)
+ pFCell2->SetDirty();
+ pCondFormat->DoRepaint();
+ }
+}
+
+ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc )
+ : ScFormatEntry( pDoc )
+ , meType(condformat::TODAY)
+{
+}
+
+ScCondDateFormatEntry::ScCondDateFormatEntry( ScDocument* pDoc, const ScCondDateFormatEntry& rFormat ):
+ ScFormatEntry( pDoc ),
+ meType( rFormat.meType ),
+ maStyleName( rFormat.maStyleName )
+{
+}
+
+bool ScCondDateFormatEntry::IsValid( const ScAddress& rPos ) const
+{
+ ScRefCellValue rCell(*mpDoc, rPos);
+
+ if (!rCell.hasNumeric())
+ // non-numerical cell.
+ return false;
+
+ if( !mpCache )
+ mpCache.reset( new Date( Date::SYSTEM ) );
+
+ const Date& rActDate = *mpCache;
+ SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
+ sal_Int32 nCurrentDate = rActDate - pFormatter->GetNullDate();
+
+ double nVal = rCell.getValue();
+ sal_Int32 nCellDate = static_cast<sal_Int32>(::rtl::math::approxFloor(nVal));
+ Date aCellDate = pFormatter->GetNullDate();
+ aCellDate.AddDays(nCellDate);
+
+ switch(meType)
+ {
+ case condformat::TODAY:
+ if( nCurrentDate == nCellDate )
+ return true;
+ break;
+ case condformat::TOMORROW:
+ if( nCurrentDate == nCellDate -1 )
+ return true;
+ break;
+ case condformat::YESTERDAY:
+ if( nCurrentDate == nCellDate + 1)
+ return true;
+ break;
+ case condformat::LAST7DAYS:
+ if( nCurrentDate >= nCellDate && nCurrentDate - 7 < nCellDate )
+ return true;
+ break;
+ case condformat::LASTWEEK:
+ {
+ const DayOfWeek eDay = rActDate.GetDayOfWeek();
+ if( eDay != SUNDAY )
+ {
+ Date aBegin(rActDate - (8 + static_cast<sal_Int32>(eDay)));
+ Date aEnd(rActDate - (2 + static_cast<sal_Int32>(eDay)));
+ return aCellDate.IsBetween( aBegin, aEnd );
+ }
+ else
+ {
+ Date aBegin(rActDate - 8);
+ Date aEnd(rActDate - 1);
+ return aCellDate.IsBetween( aBegin, aEnd );
+ }
+ }
+ break;
+ case condformat::THISWEEK:
+ {
+ const DayOfWeek eDay = rActDate.GetDayOfWeek();
+ if( eDay != SUNDAY )
+ {
+ Date aBegin(rActDate - (1 + static_cast<sal_Int32>(eDay)));
+ Date aEnd(rActDate + (5 - static_cast<sal_Int32>(eDay)));
+ return aCellDate.IsBetween( aBegin, aEnd );
+ }
+ else
+ {
+ Date aEnd( rActDate + 6);
+ return aCellDate.IsBetween( rActDate, aEnd );
+ }
+ }
+ break;
+ case condformat::NEXTWEEK:
+ {
+ const DayOfWeek eDay = rActDate.GetDayOfWeek();
+ if( eDay != SUNDAY )
+ {
+ return aCellDate.IsBetween( rActDate + (6 - static_cast<sal_Int32>(eDay)),
+ rActDate + (12 - static_cast<sal_Int32>(eDay)) );
+ }
+ else
+ {
+ return aCellDate.IsBetween( rActDate + 7, rActDate + 13 );
+ }
+ }
+ break;
+ case condformat::LASTMONTH:
+ if( rActDate.GetMonth() == 1 )
+ {
+ if( aCellDate.GetMonth() == 12 && rActDate.GetYear() == aCellDate.GetNextYear() )
+ return true;
+ }
+ else if( rActDate.GetYear() == aCellDate.GetYear() )
+ {
+ if( rActDate.GetMonth() == aCellDate.GetMonth() + 1)
+ return true;
+ }
+ break;
+ case condformat::THISMONTH:
+ if( rActDate.GetYear() == aCellDate.GetYear() )
+ {
+ if( rActDate.GetMonth() == aCellDate.GetMonth() )
+ return true;
+ }
+ break;
+ case condformat::NEXTMONTH:
+ if( rActDate.GetMonth() == 12 )
+ {
+ if( aCellDate.GetMonth() == 1 && rActDate.GetYear() == aCellDate.GetYear() - 1 )
+ return true;
+ }
+ else if( rActDate.GetYear() == aCellDate.GetYear() )
+ {
+ if( rActDate.GetMonth() == aCellDate.GetMonth() - 1)
+ return true;
+ }
+ break;
+ case condformat::LASTYEAR:
+ if( rActDate.GetYear() == aCellDate.GetNextYear() )
+ return true;
+ break;
+ case condformat::THISYEAR:
+ if( rActDate.GetYear() == aCellDate.GetYear() )
+ return true;
+ break;
+ case condformat::NEXTYEAR:
+ if( rActDate.GetYear() == aCellDate.GetYear() - 1 )
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+void ScCondDateFormatEntry::SetDateType( condformat::ScCondFormatDateType eType )
+{
+ meType = eType;
+}
+
+void ScCondDateFormatEntry::SetStyleName( const OUString& rStyleName )
+{
+ maStyleName = rStyleName;
+}
+
+ScFormatEntry* ScCondDateFormatEntry::Clone( ScDocument* pDoc ) const
+{
+ return new ScCondDateFormatEntry( pDoc, *this );
+}
+
+void ScCondDateFormatEntry::startRendering()
+{
+ mpCache.reset();
+}
+
+void ScCondDateFormatEntry::endRendering()
+{
+ mpCache.reset();
+}
+
+ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey, ScDocument* pDocument) :
+ pDoc( pDocument ),
+ nKey( nNewKey )
+{
+}
+
+std::unique_ptr<ScConditionalFormat> ScConditionalFormat::Clone(ScDocument* pNewDoc) const
+{
+ // Real copy of the formula (for Ref Undo/between documents)
+ if (!pNewDoc)
+ pNewDoc = pDoc;
+
+ std::unique_ptr<ScConditionalFormat> pNew(new ScConditionalFormat(nKey, pNewDoc));
+ pNew->SetRange( maRanges ); // prerequisite for listeners
+
+ for (const auto& rxEntry : maEntries)
+ {
+ ScFormatEntry* pNewEntry = rxEntry->Clone(pNewDoc);
+ pNew->maEntries.push_back( std::unique_ptr<ScFormatEntry>(pNewEntry) );
+ pNewEntry->SetParent(pNew.get());
+ }
+
+ return pNew;
+}
+
+bool ScConditionalFormat::EqualEntries( const ScConditionalFormat& r, bool bIgnoreSrcPos ) const
+{
+ if( size() != r.size())
+ return false;
+
+ //TODO: Test for same entries in reverse order?
+ if (! std::equal(maEntries.begin(), maEntries.end(), r.maEntries.begin(),
+ [&bIgnoreSrcPos](const std::unique_ptr<ScFormatEntry>& p1, const std::unique_ptr<ScFormatEntry>& p2) -> bool
+ {
+ return p1->IsEqual(*p2, bIgnoreSrcPos);
+ }))
+ return false;
+
+ // right now don't check for same range
+ // we only use this method to merge same conditional formats from
+ // old ODF data structure
+ return true;
+}
+
+void ScConditionalFormat::SetRange( const ScRangeList& rRanges )
+{
+ maRanges = rRanges;
+ SAL_WARN_IF(maRanges.empty(), "sc", "the conditional format range is empty! will result in a crash later!");
+}
+
+void ScConditionalFormat::AddEntry( ScFormatEntry* pNew )
+{
+ maEntries.push_back( std::unique_ptr<ScFormatEntry>(pNew));
+ pNew->SetParent(this);
+}
+
+void ScConditionalFormat::RemoveEntry(size_t n)
+{
+ if (n < maEntries.size())
+ {
+ maEntries.erase(maEntries.begin() + n);
+ DoRepaint();
+ }
+}
+
+bool ScConditionalFormat::IsEmpty() const
+{
+ return maEntries.empty();
+}
+
+size_t ScConditionalFormat::size() const
+{
+ return maEntries.size();
+}
+
+ScDocument* ScConditionalFormat::GetDocument()
+{
+ return pDoc;
+}
+
+ScConditionalFormat::~ScConditionalFormat()
+{
+}
+
+const ScFormatEntry* ScConditionalFormat::GetEntry( sal_uInt16 nPos ) const
+{
+ if ( nPos < size() )
+ return maEntries[nPos].get();
+ else
+ return nullptr;
+}
+
+OUString ScConditionalFormat::GetCellStyle( ScRefCellValue& rCell, const ScAddress& rPos ) const
+{
+ for (const auto& rxEntry : maEntries)
+ {
+ if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry& rEntry = static_cast<const ScCondFormatEntry&>(*rxEntry);
+ if (rEntry.IsCellValid(rCell, rPos))
+ return rEntry.GetStyle();
+ }
+ else if(rxEntry->GetType() == ScFormatEntry::Type::Date)
+ {
+ const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(*rxEntry);
+ if (rEntry.IsValid( rPos ))
+ return rEntry.GetStyleName();
+ }
+ }
+
+ return OUString();
+}
+
+ScCondFormatData ScConditionalFormat::GetData( ScRefCellValue& rCell, const ScAddress& rPos ) const
+{
+ ScCondFormatData aData;
+ for(const auto& rxEntry : maEntries)
+ {
+ if( (rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition) &&
+ aData.aStyleName.isEmpty())
+ {
+ const ScCondFormatEntry& rEntry = static_cast<const ScCondFormatEntry&>(*rxEntry);
+ if (rEntry.IsCellValid(rCell, rPos))
+ aData.aStyleName = rEntry.GetStyle();
+ }
+ else if(rxEntry->GetType() == ScFormatEntry::Type::Colorscale && !aData.mxColorScale)
+ {
+ const ScColorScaleFormat& rEntry = static_cast<const ScColorScaleFormat&>(*rxEntry);
+ aData.mxColorScale = rEntry.GetColor(rPos);
+ }
+ else if(rxEntry->GetType() == ScFormatEntry::Type::Databar && !aData.pDataBar)
+ {
+ const ScDataBarFormat& rEntry = static_cast<const ScDataBarFormat&>(*rxEntry);
+ aData.pDataBar = rEntry.GetDataBarInfo(rPos);
+ }
+ else if(rxEntry->GetType() == ScFormatEntry::Type::Iconset && !aData.pIconSet)
+ {
+ const ScIconSetFormat& rEntry = static_cast<const ScIconSetFormat&>(*rxEntry);
+ aData.pIconSet = rEntry.GetIconSetInfo(rPos);
+ }
+ else if(rxEntry->GetType() == ScFormatEntry::Type::Date && aData.aStyleName.isEmpty())
+ {
+ const ScCondDateFormatEntry& rEntry = static_cast<const ScCondDateFormatEntry&>(*rxEntry);
+ if ( rEntry.IsValid( rPos ) )
+ aData.aStyleName = rEntry.GetStyleName();
+ }
+ }
+ return aData;
+}
+
+void ScConditionalFormat::DoRepaint()
+{
+ // all conditional format cells
+ pDoc->RepaintRange( maRanges );
+}
+
+void ScConditionalFormat::CompileAll()
+{
+ for(auto& rxEntry : maEntries)
+ if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ static_cast<ScCondFormatEntry&>(*rxEntry).CompileAll();
+}
+
+void ScConditionalFormat::CompileXML()
+{
+ for(auto& rxEntry : maEntries)
+ if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ static_cast<ScCondFormatEntry&>(*rxEntry).CompileXML();
+}
+
+void ScConditionalFormat::UpdateReference( sc::RefUpdateContext& rCxt, bool bCopyAsMove )
+{
+ if (rCxt.meMode == URM_COPY && bCopyAsMove)
+ {
+ // ScConditionEntry::UpdateReference() obtains its aSrcPos from
+ // maRanges and does not update it on URM_COPY, but it's needed later
+ // for the moved position, so update maRanges beforehand.
+ maRanges.UpdateReference(URM_MOVE, pDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
+ for (auto& rxEntry : maEntries)
+ rxEntry->UpdateReference(rCxt);
+ }
+ else
+ {
+ for (auto& rxEntry : maEntries)
+ rxEntry->UpdateReference(rCxt);
+ maRanges.UpdateReference(rCxt.meMode, pDoc, rCxt.maRange, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
+ }
+}
+
+void ScConditionalFormat::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
+{
+ maRanges.InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
+}
+
+void ScConditionalFormat::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
+{
+ maRanges.InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
+}
+
+void ScConditionalFormat::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ for (size_t i = 0, n = maRanges.size(); i < n; ++i)
+ {
+ // We assume that the start and end sheet indices are equal.
+ ScRange & rRange = maRanges[i];
+ SCTAB nTab = rRange.aStart.Tab();
+
+ if (nTab < rCxt.mnInsertPos)
+ // Unaffected.
+ continue;
+
+ rRange.aStart.IncTab(rCxt.mnSheets);
+ rRange.aEnd.IncTab(rCxt.mnSheets);
+ }
+
+ for (auto& rxEntry : maEntries)
+ rxEntry->UpdateInsertTab(rCxt);
+}
+
+void ScConditionalFormat::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ for (size_t i = 0, n = maRanges.size(); i < n; ++i)
+ {
+ // We assume that the start and end sheet indices are equal.
+ ScRange & rRange = maRanges[i];
+ SCTAB nTab = rRange.aStart.Tab();
+
+ if (nTab < rCxt.mnDeletePos)
+ // Left of the deleted sheet(s). Unaffected.
+ continue;
+
+ if (nTab <= rCxt.mnDeletePos+rCxt.mnSheets-1)
+ {
+ // On the deleted sheet(s).
+ rRange.aStart.SetTab(-1);
+ rRange.aEnd.SetTab(-1);
+ continue;
+ }
+
+ // Right of the deleted sheet(s). Adjust the sheet indices.
+ rRange.aStart.IncTab(-1*rCxt.mnSheets);
+ rRange.aEnd.IncTab(-1*rCxt.mnSheets);
+ }
+
+ for (auto& rxEntry : maEntries)
+ rxEntry->UpdateDeleteTab(rCxt);
+}
+
+void ScConditionalFormat::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
+{
+ size_t n = maRanges.size();
+ SCTAB nMinTab = std::min<SCTAB>(rCxt.mnOldPos, rCxt.mnNewPos);
+ SCTAB nMaxTab = std::max<SCTAB>(rCxt.mnOldPos, rCxt.mnNewPos);
+ for(size_t i = 0; i < n; ++i)
+ {
+ ScRange & rRange = maRanges[i];
+ SCTAB nTab = rRange.aStart.Tab();
+ if(nTab < nMinTab || nTab > nMaxTab)
+ {
+ continue;
+ }
+
+ if (nTab == rCxt.mnOldPos)
+ {
+ rRange.aStart.SetTab(rCxt.mnNewPos);
+ rRange.aEnd.SetTab(rCxt.mnNewPos);
+ continue;
+ }
+
+ if (rCxt.mnNewPos < rCxt.mnOldPos)
+ {
+ rRange.aStart.IncTab();
+ rRange.aEnd.IncTab();
+ }
+ else
+ {
+ rRange.aStart.IncTab(-1);
+ rRange.aEnd.IncTab(-1);
+ }
+ }
+
+ for (auto& rxEntry : maEntries)
+ rxEntry->UpdateMoveTab(rCxt);
+}
+
+void ScConditionalFormat::DeleteArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ if (maRanges.empty())
+ return;
+
+ SCTAB nTab = maRanges[0].aStart.Tab();
+ maRanges.DeleteArea( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+}
+
+void ScConditionalFormat::RenameCellStyle(std::u16string_view rOld, const OUString& rNew)
+{
+ for(const auto& rxEntry : maEntries)
+ if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ ScCondFormatEntry& rFormat = static_cast<ScCondFormatEntry&>(*rxEntry);
+ if(rFormat.GetStyle() == rOld)
+ rFormat.UpdateStyleName( rNew );
+ }
+}
+
+bool ScConditionalFormat::MarkUsedExternalReferences() const
+{
+ bool bAllMarked = false;
+ for(const auto& rxEntry : maEntries)
+ if(rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*rxEntry);
+ bAllMarked = rFormat.MarkUsedExternalReferences();
+ if (bAllMarked)
+ break;
+ }
+
+ return bAllMarked;
+}
+
+void ScConditionalFormat::startRendering()
+{
+ for(auto& rxEntry : maEntries)
+ {
+ rxEntry->startRendering();
+ }
+}
+
+void ScConditionalFormat::endRendering()
+{
+ for(auto& rxEntry : maEntries)
+ {
+ rxEntry->endRendering();
+ }
+}
+
+void ScConditionalFormat::updateValues()
+{
+ for(auto& rxEntry : maEntries)
+ {
+ rxEntry->updateValues();
+ }
+}
+
+void ScConditionalFormat::CalcAll()
+{
+ for(const auto& rxEntry : maEntries)
+ {
+ if (rxEntry->GetType() == ScFormatEntry::Type::Condition ||
+ rxEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ ScCondFormatEntry& rFormat = static_cast<ScCondFormatEntry&>(*rxEntry);
+ rFormat.CalcAll();
+ }
+ }
+}
+
+ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList& rList)
+{
+ for(const auto& rxFormat : rList)
+ InsertNew( rxFormat->Clone() );
+}
+
+ScConditionalFormatList::ScConditionalFormatList(ScDocument& rDoc, const ScConditionalFormatList& rList)
+{
+ for(const auto& rxFormat : rList)
+ InsertNew( rxFormat->Clone(&rDoc) );
+}
+
+void ScConditionalFormatList::InsertNew( std::unique_ptr<ScConditionalFormat> pNew )
+{
+ m_ConditionalFormats.insert(std::move(pNew));
+}
+
+ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey )
+{
+ auto itr = m_ConditionalFormats.find(nKey);
+ if (itr != m_ConditionalFormats.end())
+ return itr->get();
+
+ SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
+ return nullptr;
+}
+
+const ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey ) const
+{
+ auto itr = m_ConditionalFormats.find(nKey);
+ if (itr != m_ConditionalFormats.end())
+ return itr->get();
+
+ SAL_WARN("sc", "ScConditionalFormatList: Entry not found");
+ return nullptr;
+}
+
+void ScConditionalFormatList::CompileAll()
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->CompileAll();
+ }
+}
+
+void ScConditionalFormatList::CompileXML()
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->CompileXML();
+ }
+}
+
+void ScConditionalFormatList::UpdateReference( sc::RefUpdateContext& rCxt )
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->UpdateReference(rCxt);
+ }
+
+ if (rCxt.meMode == URM_INSDEL)
+ {
+ // need to check which must be deleted
+ CheckAllEntries();
+ }
+}
+
+void ScConditionalFormatList::InsertRow(SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize)
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->InsertRow(nTab, nColStart, nColEnd, nRowPos, nSize);
+ }
+}
+
+void ScConditionalFormatList::InsertCol(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize)
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->InsertCol(nTab, nRowStart, nRowEnd, nColPos, nSize);
+ }
+}
+
+void ScConditionalFormatList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->UpdateInsertTab(rCxt);
+ }
+}
+
+void ScConditionalFormatList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->UpdateDeleteTab(rCxt);
+ }
+}
+
+void ScConditionalFormatList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->UpdateMoveTab(rCxt);
+ }
+}
+
+void ScConditionalFormatList::RenameCellStyle( std::u16string_view rOld, const OUString& rNew )
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->RenameCellStyle(rOld, rNew);
+ }
+}
+
+bool ScConditionalFormatList::CheckAllEntries(const Link<ScConditionalFormat*,void>& rLink)
+{
+ bool bValid = true;
+
+ // need to check which must be deleted
+ iterator itr = m_ConditionalFormats.begin();
+ while(itr != m_ConditionalFormats.end())
+ {
+ if ((*itr)->GetRange().empty())
+ {
+ bValid = false;
+ if (rLink.IsSet())
+ rLink.Call(itr->get());
+ itr = m_ConditionalFormats.erase(itr);
+ }
+ else
+ ++itr;
+ }
+
+ return bValid;
+}
+
+void ScConditionalFormatList::DeleteArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ for (auto& rxFormat : m_ConditionalFormats)
+ rxFormat->DeleteArea( nCol1, nRow1, nCol2, nRow2 );
+
+ CheckAllEntries();
+}
+
+ScConditionalFormatList::iterator ScConditionalFormatList::begin()
+{
+ return m_ConditionalFormats.begin();
+}
+
+ScConditionalFormatList::const_iterator ScConditionalFormatList::begin() const
+{
+ return m_ConditionalFormats.begin();
+}
+
+ScConditionalFormatList::iterator ScConditionalFormatList::end()
+{
+ return m_ConditionalFormats.end();
+}
+
+ScConditionalFormatList::const_iterator ScConditionalFormatList::end() const
+{
+ return m_ConditionalFormats.end();
+}
+
+ScRangeList ScConditionalFormatList::GetCombinedRange() const
+{
+ ScRangeList aRange;
+ for (auto& itr: m_ConditionalFormats)
+ {
+ const ScRangeList& rRange = itr->GetRange();
+ for (size_t i = 0, n = rRange.size(); i < n; ++i)
+ {
+ aRange.Join(rRange[i]);
+ }
+ }
+ return aRange;
+}
+
+void ScConditionalFormatList::RemoveFromDocument(ScDocument& rDoc) const
+{
+ ScRangeList aRange = GetCombinedRange();
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.MarkFromRangeList(aRange, true);
+ sal_uInt16 const pItems[2] = { sal_uInt16(ATTR_CONDITIONAL),0};
+ rDoc.ClearSelectionItems(pItems, aMark);
+}
+
+void ScConditionalFormatList::AddToDocument(ScDocument& rDoc) const
+{
+ for (auto& itr: m_ConditionalFormats)
+ {
+ const ScRangeList& rRange = itr->GetRange();
+ if (rRange.empty())
+ continue;
+
+ SCTAB nTab = rRange.front().aStart.Tab();
+ rDoc.AddCondFormatData(rRange, nTab, itr->GetKey());
+ }
+}
+
+size_t ScConditionalFormatList::size() const
+{
+ return m_ConditionalFormats.size();
+}
+
+bool ScConditionalFormatList::empty() const
+{
+ return m_ConditionalFormats.empty();
+}
+
+void ScConditionalFormatList::erase( sal_uLong nIndex )
+{
+ auto itr = m_ConditionalFormats.find(nIndex);
+ if (itr != end())
+ m_ConditionalFormats.erase(itr);
+}
+
+void ScConditionalFormatList::startRendering()
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->startRendering();
+ }
+}
+
+void ScConditionalFormatList::endRendering()
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->endRendering();
+ }
+}
+
+void ScConditionalFormatList::updateValues()
+{
+ for (auto const& it : m_ConditionalFormats)
+ {
+ it->updateValues();
+ }
+}
+
+void ScConditionalFormatList::clear()
+{
+ m_ConditionalFormats.clear();
+}
+
+sal_uInt32 ScConditionalFormatList::getMaxKey() const
+{
+ if (m_ConditionalFormats.empty())
+ return 0;
+ return (*m_ConditionalFormats.rbegin())->GetKey();
+}
+
+void ScConditionalFormatList::CalcAll()
+{
+ for (const auto& aEntry : m_ConditionalFormats)
+ {
+ aEntry->CalcAll();
+ }
+
+}
+
+ScCondFormatData::ScCondFormatData() {}
+
+ScCondFormatData::ScCondFormatData(ScCondFormatData&&) = default;
+
+ScCondFormatData::~ScCondFormatData() {}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dbdocutl.cxx b/sc/source/core/data/dbdocutl.cxx
new file mode 100644
index 0000000000..db45170978
--- /dev/null
+++ b/sc/source/core/data/dbdocutl.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 <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+
+#include <dbdocutl.hxx>
+#include <document.hxx>
+#include <formula/errorcodes.hxx>
+#include <stringutil.hxx>
+
+using namespace ::com::sun::star;
+
+ScDatabaseDocUtil::StrData::StrData() :
+ mbSimpleText(true), mnStrLength(0)
+{
+}
+
+void ScDatabaseDocUtil::PutData(ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const uno::Reference<sdbc::XRow>& xRow, sal_Int32 nRowPos,
+ tools::Long nType, bool bCurrency, StrData* pStrData)
+{
+ OUString aString;
+ double nVal = 0.0;
+ bool bValue = false;
+ bool bEmptyFlag = false;
+ bool bError = false;
+ sal_uLong nFormatIndex = 0;
+
+ // wasNull calls only if null value was found?
+
+ try
+ {
+ switch ( nType )
+ {
+ case sdbc::DataType::BIT:
+ case sdbc::DataType::BOOLEAN:
+ //TODO: use language from doc (here, date/time and currency)?
+ nFormatIndex = rDoc.GetFormatTable()->GetStandardFormat(
+ SvNumFormatType::LOGICAL, ScGlobal::eLnge );
+ nVal = (xRow->getBoolean(nRowPos) ? 1 : 0);
+ bEmptyFlag = ( nVal == 0.0 ) && xRow->wasNull();
+ bValue = true;
+ break;
+
+ case sdbc::DataType::TINYINT:
+ case sdbc::DataType::SMALLINT:
+ case sdbc::DataType::INTEGER:
+ case sdbc::DataType::BIGINT:
+ case sdbc::DataType::FLOAT:
+ case sdbc::DataType::REAL:
+ case sdbc::DataType::DOUBLE:
+ case sdbc::DataType::NUMERIC:
+ case sdbc::DataType::DECIMAL:
+ //TODO: do the conversion here?
+ nVal = xRow->getDouble(nRowPos);
+ bEmptyFlag = ( nVal == 0.0 ) && xRow->wasNull();
+ bValue = true;
+ break;
+
+ case sdbc::DataType::CHAR:
+ case sdbc::DataType::VARCHAR:
+ case sdbc::DataType::LONGVARCHAR:
+ aString = xRow->getString(nRowPos);
+ bEmptyFlag = ( aString.isEmpty() ) && xRow->wasNull();
+ break;
+
+ case sdbc::DataType::DATE:
+ {
+ util::Date aDate = xRow->getDate(nRowPos);
+ bEmptyFlag = xRow->wasNull();
+ if (bEmptyFlag)
+ nVal = 0.0;
+ else
+ {
+ SvNumberFormatter* pFormTable = rDoc.GetFormatTable();
+ nFormatIndex = pFormTable->GetStandardFormat(
+ SvNumFormatType::DATE, ScGlobal::eLnge );
+ nVal = Date( aDate ) - pFormTable->GetNullDate();
+ }
+ bValue = true;
+ }
+ break;
+
+ case sdbc::DataType::TIME:
+ {
+ SvNumberFormatter* pFormTable = rDoc.GetFormatTable();
+ nFormatIndex = pFormTable->GetStandardFormat(
+ SvNumFormatType::TIME, ScGlobal::eLnge );
+
+ util::Time aTime = xRow->getTime(nRowPos);
+ nVal = aTime.Hours / static_cast<double>(::tools::Time::hourPerDay) +
+ aTime.Minutes / static_cast<double>(::tools::Time::minutePerDay) +
+ aTime.Seconds / static_cast<double>(::tools::Time::secondPerDay) +
+ aTime.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
+ bEmptyFlag = xRow->wasNull();
+ bValue = true;
+ }
+ break;
+
+ case sdbc::DataType::TIMESTAMP:
+ {
+ SvNumberFormatter* pFormTable = rDoc.GetFormatTable();
+ nFormatIndex = pFormTable->GetStandardFormat(
+ SvNumFormatType::DATETIME, ScGlobal::eLnge );
+
+ util::DateTime aStamp = xRow->getTimestamp(nRowPos);
+ if (aStamp.Year != 0)
+ {
+ nVal = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) -
+ pFormTable->GetNullDate() ) +
+ aStamp.Hours / static_cast<double>(::tools::Time::hourPerDay) +
+ aStamp.Minutes / static_cast<double>(::tools::Time::minutePerDay) +
+ aStamp.Seconds / static_cast<double>(::tools::Time::secondPerDay) +
+ aStamp.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
+ bEmptyFlag = xRow->wasNull();
+ bValue = true;
+ }
+ }
+ break;
+
+ case sdbc::DataType::SQLNULL:
+ bEmptyFlag = true;
+ break;
+
+ case sdbc::DataType::BINARY:
+ case sdbc::DataType::VARBINARY:
+ case sdbc::DataType::LONGVARBINARY:
+ default:
+ bError = true; // unknown type
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ bError = true;
+ }
+
+ if ( bValue && bCurrency )
+ nFormatIndex = rDoc.GetFormatTable()->GetStandardFormat(
+ SvNumFormatType::CURRENCY, ScGlobal::eLnge );
+
+ ScAddress aPos(nCol, nRow, nTab);
+ if (bEmptyFlag)
+ rDoc.SetEmptyCell(aPos);
+ else if (bError)
+ {
+ rDoc.SetError( nCol, nRow, nTab, FormulaError::NotAvailable );
+ }
+ else if (bValue)
+ {
+ rDoc.SetValue(aPos, nVal);
+ if (nFormatIndex)
+ rDoc.SetNumberFormat(aPos, nFormatIndex);
+ }
+ else
+ {
+ if (!aString.isEmpty())
+ {
+ if (ScStringUtil::isMultiline(aString))
+ {
+ rDoc.SetEditText(aPos, aString);
+ if (pStrData)
+ pStrData->mbSimpleText = false;
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(aPos, aString, &aParam);
+ if (pStrData)
+ pStrData->mbSimpleText = true;
+ }
+
+ if (pStrData)
+ pStrData->mnStrLength = aString.getLength();
+ }
+ else
+ rDoc.SetEmptyCell(aPos);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
new file mode 100644
index 0000000000..b1fca78c86
--- /dev/null
+++ b/sc/source/core/data/dociter.cxx
@@ -0,0 +1,1782 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/numformat.hxx>
+#include <svl/zforlist.hxx>
+
+#include <global.hxx>
+#include <dociter.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <formulacell.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <docoptio.hxx>
+#include <cellform.hxx>
+#include <segmenttree.hxx>
+#include <progress.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <cellvalue.hxx>
+#include <scmatrix.hxx>
+#include <rowheightcontext.hxx>
+#include <queryevaluator.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <tools/fract.hxx>
+#include <editeng/editobj.hxx>
+#include <svl/sharedstring.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+using ::rtl::math::approxEqual;
+using ::std::vector;
+using ::std::set;
+
+// iterators have very high frequency use -> custom debug.
+// #define debugiter(...) fprintf(stderr, __VA_ARGS__)
+#define debugiter(...)
+
+static void ScAttrArray_IterGetNumberFormat( sal_uInt32& nFormat, const ScAttrArray*& rpArr,
+ SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow,
+ const ScDocument& rDoc, const ScInterpreterContext* pContext = nullptr )
+{
+ if ( rpArr == pNewArr && nAttrEndRow >= nRow )
+ return;
+
+ SCROW nRowStart = 0;
+ SCROW nRowEnd = rDoc.MaxRow();
+ const ScPatternAttr* pPattern = pNewArr->GetPatternRange( nRowStart, nRowEnd, nRow );
+ if( !pPattern )
+ {
+ pPattern = rDoc.GetDefPattern();
+ nRowEnd = rDoc.MaxRow();
+ }
+
+ nFormat = pPattern->GetNumberFormat( pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable() );
+ rpArr = pNewArr;
+ nAttrEndRow = nRowEnd;
+}
+
+ScValueIterator::ScValueIterator(ScInterpreterContext& rContext, const ScRange& rRange,
+ SubtotalFlags nSubTotalFlags, bool bTextZero )
+ : mrDoc(*rContext.mpDoc)
+ , mrContext(rContext)
+ , pAttrArray(nullptr)
+ , nNumFormat(0) // Initialized in GetNumberFormat
+ , nNumFmtIndex(0)
+ , maStartPos(rRange.aStart)
+ , maEndPos(rRange.aEnd)
+ , mnCol(0)
+ , mnTab(0)
+ , nAttrEndRow(0)
+ , mnSubTotalFlags(nSubTotalFlags)
+ , nNumFmtType(SvNumFormatType::UNDEFINED)
+ , bNumValid(false)
+ , bCalcAsShown((*rContext.mpDoc).GetDocOptions().IsCalcAsShown())
+ , bTextAsZero(bTextZero)
+ , mpCells(nullptr)
+{
+ SCTAB nDocMaxTab = mrDoc.GetTableCount() - 1;
+
+ if (!mrDoc.ValidCol(maStartPos.Col())) maStartPos.SetCol(mrDoc.MaxCol());
+ if (!mrDoc.ValidCol(maEndPos.Col())) maEndPos.SetCol(mrDoc.MaxCol());
+ if (!mrDoc.ValidRow(maStartPos.Row())) maStartPos.SetRow(mrDoc.MaxRow());
+ if (!mrDoc.ValidRow(maEndPos.Row())) maEndPos.SetRow(mrDoc.MaxRow());
+ if (!ValidTab(maStartPos.Tab()) || maStartPos.Tab() > nDocMaxTab) maStartPos.SetTab(nDocMaxTab);
+ if (!ValidTab(maEndPos.Tab()) || maEndPos.Tab() > nDocMaxTab) maEndPos.SetTab(nDocMaxTab);
+}
+
+SCROW ScValueIterator::GetRow() const
+{
+ // Position of the head of the current block + offset within the block
+ // equals the logical element position.
+ return maCurPos.first->position + maCurPos.second;
+}
+
+void ScValueIterator::IncBlock()
+{
+ ++maCurPos.first;
+ maCurPos.second = 0;
+}
+
+void ScValueIterator::IncPos()
+{
+ if (maCurPos.second + 1 < maCurPos.first->size)
+ // Move within the same block.
+ ++maCurPos.second;
+ else
+ // Move to the next block.
+ IncBlock();
+}
+
+bool ScValueIterator::GetThis(double& rValue, FormulaError& rErr)
+{
+ while (true)
+ {
+ bool bNextColumn = !mpCells || maCurPos.first == mpCells->end();
+ if (!bNextColumn)
+ {
+ if (GetRow() > maEndPos.Row())
+ bNextColumn = true;
+ }
+
+ ScColumn* pCol;
+ if (!bNextColumn)
+ pCol = &(mrDoc.maTabs[mnTab])->aCol[mnCol];
+ else
+ {
+ // Find the next available column.
+ do
+ {
+ ++mnCol;
+ while (mnCol > maEndPos.Col() || mnCol >= mrDoc.maTabs[mnTab]->GetAllocatedColumnsCount())
+ {
+ mnCol = maStartPos.Col();
+ ++mnTab;
+ if (mnTab > maEndPos.Tab())
+ {
+ rErr = FormulaError::NONE;
+ return false;
+ }
+ }
+ pCol = &(mrDoc.maTabs[mnTab])->aCol[mnCol];
+ }
+ while (pCol->IsEmptyData());
+
+ mpCells = &pCol->maCells;
+ maCurPos = mpCells->position(maStartPos.Row());
+ }
+
+ SCROW nCurRow = GetRow();
+ SCROW nLastRow;
+ // Skip all filtered or hidden rows, depending on mnSubTotalFlags
+ if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) &&
+ mrDoc.RowFiltered( nCurRow, mnTab, nullptr, &nLastRow ) ) ||
+ ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) &&
+ mrDoc.RowHidden( nCurRow, mnTab, nullptr, &nLastRow ) ) )
+ {
+ maCurPos = mpCells->position(maCurPos.first, nLastRow+1);
+ continue;
+ }
+
+ switch (maCurPos.first->type)
+ {
+ case sc::element_type_numeric:
+ {
+ bNumValid = false;
+ rValue = sc::numeric_block::at(*maCurPos.first->data, maCurPos.second);
+ rErr = FormulaError::NONE;
+ if (bCalcAsShown)
+ {
+ ScAttrArray_IterGetNumberFormat(nNumFormat, pAttrArray,
+ nAttrEndRow, pCol->pAttrArray.get(), nCurRow, mrDoc, &mrContext);
+ rValue = mrDoc.RoundValueAsShown(rValue, nNumFormat, &mrContext);
+ }
+ return true; // Found it!
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ ScFormulaCell& rCell = *sc::formula_block::at(*maCurPos.first->data, maCurPos.second);
+ if ( ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) && rCell.IsSubTotal() )
+ {
+ // Skip subtotal formula cells.
+ IncPos();
+ break;
+ }
+
+ if (rCell.GetErrorOrValue(rErr, rValue))
+ {
+ if ( rErr != FormulaError::NONE && ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ {
+ IncPos();
+ break;
+ }
+ bNumValid = false;
+ return true; // Found it!
+ }
+ else if (bTextAsZero)
+ {
+ rValue = 0.0;
+ bNumValid = false;
+ return true;
+ }
+ IncPos();
+ }
+ break;
+ case sc::element_type_string :
+ case sc::element_type_edittext :
+ {
+ if (bTextAsZero)
+ {
+ rErr = FormulaError::NONE;
+ rValue = 0.0;
+ nNumFmtType = SvNumFormatType::NUMBER;
+ nNumFmtIndex = 0;
+ bNumValid = true;
+ return true;
+ }
+ IncBlock();
+ }
+ break;
+ case sc::element_type_empty:
+ default:
+ // Skip the whole block.
+ IncBlock();
+ }
+ }
+}
+
+void ScValueIterator::GetCurNumFmtInfo( SvNumFormatType& nType, sal_uInt32& nIndex )
+{
+ if (!bNumValid && mnTab < mrDoc.GetTableCount())
+ {
+ SCROW nCurRow = GetRow();
+ const ScColumn* pCol = &(mrDoc.maTabs[mnTab])->aCol[mnCol];
+ nNumFmtIndex = pCol->GetNumberFormat(mrContext, nCurRow);
+ nNumFmtType = mrContext.GetNumberFormatType( nNumFmtIndex );
+ bNumValid = true;
+ }
+
+ nType = nNumFmtType;
+ nIndex = nNumFmtIndex;
+}
+
+bool ScValueIterator::GetFirst(double& rValue, FormulaError& rErr)
+{
+ mnCol = maStartPos.Col();
+ mnTab = maStartPos.Tab();
+
+ const ScTable* pTab = mrDoc.FetchTable(mnTab);
+ if (!pTab)
+ return false;
+
+ nNumFormat = 0; // Initialized in GetNumberFormat
+ pAttrArray = nullptr;
+ nAttrEndRow = 0;
+
+ auto nCol = maStartPos.Col();
+ if (nCol < pTab->GetAllocatedColumnsCount())
+ {
+ mpCells = &pTab->aCol[nCol].maCells;
+ maCurPos = mpCells->position(maStartPos.Row());
+ }
+ else
+ mpCells = nullptr;
+ return GetThis(rValue, rErr);
+}
+
+bool ScValueIterator::GetNext(double& rValue, FormulaError& rErr)
+{
+ IncPos();
+ return GetThis(rValue, rErr);
+}
+
+ScDBQueryDataIterator::DataAccess::DataAccess()
+{
+}
+
+ScDBQueryDataIterator::DataAccess::~DataAccess()
+{
+}
+
+const sc::CellStoreType* ScDBQueryDataIterator::GetColumnCellStore(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
+{
+ ScTable* pTab = rDoc.FetchTable(nTab);
+ if (!pTab)
+ return nullptr;
+ if (nCol >= pTab->GetAllocatedColumnsCount())
+ return nullptr;
+ return &pTab->aCol[nCol].maCells;
+}
+
+const ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
+{
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ ScColumn* pCol = &rDoc.maTabs[nTab]->aCol[nCol];
+ return pCol->pAttrArray.get();
+}
+
+bool ScDBQueryDataIterator::IsQueryValid(
+ ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, const ScRefCellValue* pCell)
+{
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], rParam);
+ return queryEvaluator.ValidQuery(nRow, pCell);
+}
+
+ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(ScDBQueryParamInternal* pParam, ScDocument& rDoc, const ScInterpreterContext& rContext)
+ : mpCells(nullptr)
+ , mpParam(pParam)
+ , mrDoc(rDoc)
+ , mrContext(rContext)
+ , pAttrArray(nullptr)
+ , nNumFormat(0) // Initialized in GetNumberFormat
+ , nNumFmtIndex(0)
+ , nCol(mpParam->mnField)
+ , nRow(mpParam->nRow1)
+ , nAttrEndRow(0)
+ , nTab(mpParam->nTab)
+ , nNumFmtType(SvNumFormatType::ALL)
+ , bCalcAsShown(rDoc.GetDocOptions().IsCalcAsShown())
+{
+ SCSIZE i;
+ SCSIZE nCount = mpParam->GetEntryCount();
+ for (i=0; (i<nCount) && (mpParam->GetEntry(i).bDoQuery); i++)
+ {
+ ScQueryEntry& rEntry = mpParam->GetEntry(i);
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ rItems.resize(1);
+ ScQueryEntry::Item& rItem = rItems.front();
+ sal_uInt32 nIndex = 0;
+ bool bNumber = mrDoc.GetFormatTable()->IsNumberFormat(
+ rItem.maString.getString(), nIndex, rItem.mfVal);
+ rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ }
+}
+
+ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal()
+{
+}
+
+bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue)
+{
+ // Start with the current row position, and find the first row position
+ // that satisfies the query.
+
+ // If the query starts in the same column as the result vector we can
+ // prefetch the cell which saves us one fetch in the success case.
+ SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField;
+ ScRefCellValue aCell;
+
+ while (true)
+ {
+ if (maCurPos.first == mpCells->end() || nRow > mpParam->nRow2)
+ {
+ // Bottom of the range reached. Bail out.
+ rValue.mnError = FormulaError::NONE;
+ return false;
+ }
+
+ if (maCurPos.first->type == sc::element_type_empty)
+ {
+ // Skip the whole empty block.
+ incBlock();
+ continue;
+ }
+
+ ScRefCellValue* pCell = nullptr;
+ if (nCol == static_cast<SCCOL>(nFirstQueryField))
+ {
+ aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
+ pCell = &aCell;
+ }
+
+ if (ScDBQueryDataIterator::IsQueryValid(mrDoc, *mpParam, nTab, nRow, pCell))
+ {
+ if (!pCell)
+ aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ rValue.mfValue = aCell.getDouble();
+ rValue.mbIsNumber = true;
+ if ( bCalcAsShown )
+ {
+ const ScAttrArray* pNewAttrArray =
+ ScDBQueryDataIterator::GetAttrArrayByCol(mrDoc, nTab, nCol);
+ ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray,
+ nAttrEndRow, pNewAttrArray, nRow, mrDoc );
+ rValue.mfValue = mrDoc.RoundValueAsShown( rValue.mfValue, nNumFormat );
+ }
+ nNumFmtType = SvNumFormatType::NUMBER;
+ nNumFmtIndex = 0;
+ rValue.mnError = FormulaError::NONE;
+ return true; // Found it!
+ }
+
+ case CELLTYPE_FORMULA:
+ {
+ if (aCell.getFormula()->IsValue())
+ {
+ rValue.mfValue = aCell.getFormula()->GetValue();
+ rValue.mbIsNumber = true;
+ mrDoc.GetNumberFormatInfo(
+ mrContext, nNumFmtType, nNumFmtIndex, ScAddress(nCol, nRow, nTab));
+ rValue.mnError = aCell.getFormula()->GetErrCode();
+ return true; // Found it!
+ }
+ else if(mpParam->mbSkipString)
+ incPos();
+ else
+ {
+ rValue.maString = aCell.getFormula()->GetString().getString();
+ rValue.mfValue = 0.0;
+ rValue.mnError = aCell.getFormula()->GetErrCode();
+ rValue.mbIsNumber = false;
+ return true;
+ }
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ if (mpParam->mbSkipString)
+ incPos();
+ else
+ {
+ rValue.maString = aCell.getString(&mrDoc);
+ rValue.mfValue = 0.0;
+ rValue.mnError = FormulaError::NONE;
+ rValue.mbIsNumber = false;
+ return true;
+ }
+ break;
+ default:
+ incPos();
+ }
+ }
+ else
+ incPos();
+ }
+// statement unreachable
+}
+
+bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue)
+{
+ if (mpParam->bHasHeader)
+ ++nRow;
+
+ mpCells = ScDBQueryDataIterator::GetColumnCellStore(mrDoc, nTab, nCol);
+ if (!mpCells)
+ return false;
+
+ maCurPos = mpCells->position(nRow);
+ return getCurrent(rValue);
+}
+
+bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue)
+{
+ if (!mpCells || maCurPos.first == mpCells->end())
+ return false;
+
+ incPos();
+ return getCurrent(rValue);
+}
+
+void ScDBQueryDataIterator::DataAccessInternal::incBlock()
+{
+ ++maCurPos.first;
+ maCurPos.second = 0;
+
+ nRow = maCurPos.first->position;
+}
+
+void ScDBQueryDataIterator::DataAccessInternal::incPos()
+{
+ if (maCurPos.second + 1 < maCurPos.first->size)
+ {
+ // Move within the same block.
+ ++maCurPos.second;
+ ++nRow;
+ }
+ else
+ // Move to the next block.
+ incBlock();
+}
+
+ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(ScDBQueryParamMatrix* pParam)
+ : mpParam(pParam)
+ , mnCurRow(0)
+{
+ SCSIZE nC, nR;
+ mpParam->mpMatrix->GetDimensions(nC, nR);
+ mnRows = static_cast<SCROW>(nR);
+}
+
+ScDBQueryDataIterator::DataAccessMatrix::~DataAccessMatrix()
+{
+}
+
+bool ScDBQueryDataIterator::DataAccessMatrix::getCurrent(Value& rValue)
+{
+ // Starting from row == mnCurRow, get the first row that satisfies all the
+ // query parameters.
+ for ( ;mnCurRow < mnRows; ++mnCurRow)
+ {
+ const ScMatrix& rMat = *mpParam->mpMatrix;
+ if (rMat.IsEmpty(mpParam->mnField, mnCurRow))
+ // Don't take empty values into account.
+ continue;
+
+ bool bIsStrVal = rMat.IsStringOrEmpty(mpParam->mnField, mnCurRow);
+ if (bIsStrVal && mpParam->mbSkipString)
+ continue;
+
+ if (isValidQuery(mnCurRow, rMat))
+ {
+ rValue.maString = rMat.GetString(mpParam->mnField, mnCurRow).getString();
+ rValue.mfValue = rMat.GetDouble(mpParam->mnField, mnCurRow);
+ rValue.mbIsNumber = !bIsStrVal;
+ rValue.mnError = FormulaError::NONE;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value& rValue)
+{
+ mnCurRow = mpParam->bHasHeader ? 1 : 0;
+ return getCurrent(rValue);
+}
+
+bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value& rValue)
+{
+ ++mnCurRow;
+ return getCurrent(rValue);
+}
+
+namespace {
+
+bool isQueryByValue(const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
+{
+ if (rItem.meType == ScQueryEntry::ByString)
+ return false;
+
+ if (!rMat.IsValueOrEmpty(nCol, nRow))
+ return false;
+
+ return true;
+}
+
+bool isQueryByString(const ScQueryEntry& rEntry, const ScQueryEntry::Item& rItem, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow)
+{
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ case SC_NOT_EQUAL:
+ case SC_CONTAINS:
+ case SC_DOES_NOT_CONTAIN:
+ case SC_BEGINS_WITH:
+ case SC_ENDS_WITH:
+ case SC_DOES_NOT_BEGIN_WITH:
+ case SC_DOES_NOT_END_WITH:
+ return true;
+ default:
+ ;
+ }
+
+ return rItem.meType == ScQueryEntry::ByString && rMat.IsStringOrEmpty(nCol, nRow);
+}
+
+}
+
+bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow, const ScMatrix& rMat) const
+{
+ SCSIZE nEntryCount = mpParam->GetEntryCount();
+ vector<bool> aResults;
+ aResults.reserve(nEntryCount);
+
+ const CollatorWrapper& rCollator = ScGlobal::GetCollator(mpParam->bCaseSens);
+
+ for (SCSIZE i = 0; i < nEntryCount; ++i)
+ {
+ const ScQueryEntry& rEntry = mpParam->GetEntry(i);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (!rEntry.bDoQuery)
+ continue;
+
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ case SC_LESS:
+ case SC_GREATER:
+ case SC_LESS_EQUAL:
+ case SC_GREATER_EQUAL:
+ case SC_NOT_EQUAL:
+ break;
+ default:
+ // Only the above operators are supported.
+ SAL_WARN("sc.core", "Unsupported operator " << rEntry.eOp
+ << " in ScDBQueryDataIterator::DataAccessMatrix::isValidQuery()");
+ continue;
+ }
+
+ bool bValid = false;
+
+ SCSIZE nField = static_cast<SCSIZE>(rEntry.nField);
+ if (isQueryByValue(rItem, rMat, nField, nRow))
+ {
+ // By value
+ double fMatVal = rMat.GetDouble(nField, nRow);
+ bool bEqual = approxEqual(fMatVal, rItem.mfVal);
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ bValid = bEqual;
+ break;
+ case SC_LESS:
+ bValid = (fMatVal < rItem.mfVal) && !bEqual;
+ break;
+ case SC_GREATER:
+ bValid = (fMatVal > rItem.mfVal) && !bEqual;
+ break;
+ case SC_LESS_EQUAL:
+ bValid = (fMatVal < rItem.mfVal) || bEqual;
+ break;
+ case SC_GREATER_EQUAL:
+ bValid = (fMatVal > rItem.mfVal) || bEqual;
+ break;
+ case SC_NOT_EQUAL:
+ bValid = !bEqual;
+ break;
+ default:
+ ;
+ }
+ }
+ else if (isQueryByString(rEntry, rItem, rMat, nField, nRow))
+ {
+ // By string
+ do
+ {
+ // Equality check first.
+ svl::SharedString aMatStr = rMat.GetString(nField, nRow);
+ svl::SharedString aQueryStr = rEntry.GetQueryItem().maString;
+ bool bDone = false;
+ rtl_uString* p1 = mpParam->bCaseSens ? aMatStr.getData() : aMatStr.getDataIgnoreCase();
+ rtl_uString* p2 = mpParam->bCaseSens ? aQueryStr.getData() : aQueryStr.getDataIgnoreCase();
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ bValid = (p1 == p2);
+ bDone = true;
+ break;
+ case SC_NOT_EQUAL:
+ bValid = (p1 != p2);
+ bDone = true;
+ break;
+ default:
+ ;
+ }
+
+ if (bDone)
+ break;
+
+ // Unequality check using collator.
+ sal_Int32 nCompare = rCollator.compareString(aMatStr.getString(), aQueryStr.getString());
+ switch (rEntry.eOp)
+ {
+ case SC_LESS :
+ bValid = (nCompare < 0);
+ break;
+ case SC_GREATER :
+ bValid = (nCompare > 0);
+ break;
+ case SC_LESS_EQUAL :
+ bValid = (nCompare <= 0);
+ break;
+ case SC_GREATER_EQUAL :
+ bValid = (nCompare >= 0);
+ break;
+ default:
+ ;
+ }
+ }
+ while (false);
+ }
+
+ if (aResults.empty())
+ // First query entry.
+ aResults.push_back(bValid);
+ else if (rEntry.eConnect == SC_AND)
+ {
+ // For AND op, tuck the result into the last result value.
+ size_t n = aResults.size();
+ aResults[n-1] = aResults[n-1] && bValid;
+ }
+ else
+ // For OR op, store its own result.
+ aResults.push_back(bValid);
+ }
+
+ // Row is valid as long as there is at least one result being true.
+ return std::find(aResults.begin(), aResults.end(), true) != aResults.end();
+}
+
+ScDBQueryDataIterator::Value::Value()
+ : mfValue(std::numeric_limits<double>::quiet_NaN())
+ , mnError(FormulaError::NONE), mbIsNumber(true)
+{
+}
+
+ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument& rDocument, const ScInterpreterContext& rContext, std::unique_ptr<ScDBQueryParamBase> pParam) :
+ mpParam (std::move(pParam))
+{
+ switch (mpParam->GetType())
+ {
+ case ScDBQueryParamBase::INTERNAL:
+ {
+ ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(mpParam.get());
+ mpData.reset(new DataAccessInternal(p, rDocument, rContext));
+ }
+ break;
+ case ScDBQueryParamBase::MATRIX:
+ {
+ ScDBQueryParamMatrix* p = static_cast<ScDBQueryParamMatrix*>(mpParam.get());
+ mpData.reset(new DataAccessMatrix(p));
+ }
+ }
+}
+
+bool ScDBQueryDataIterator::GetFirst(Value& rValue)
+{
+ return mpData->getFirst(rValue);
+}
+
+bool ScDBQueryDataIterator::GetNext(Value& rValue)
+{
+ return mpData->getNext(rValue);
+}
+
+ScFormulaGroupIterator::ScFormulaGroupIterator( ScDocument& rDoc ) :
+ mrDoc(rDoc),
+ mnTab(0),
+ mnCol(0),
+ mnIndex(0)
+{
+ ScTable *pTab = mrDoc.FetchTable(mnTab);
+ ScColumn *pCol = pTab ? pTab->FetchColumn(mnCol) : nullptr;
+ if (pCol)
+ {
+ mbNullCol = false;
+ maEntries = pCol->GetFormulaGroupEntries();
+ }
+ else
+ mbNullCol = true;
+}
+
+sc::FormulaGroupEntry* ScFormulaGroupIterator::first()
+{
+ return next();
+}
+
+sc::FormulaGroupEntry* ScFormulaGroupIterator::next()
+{
+ if (mnIndex >= maEntries.size() || mbNullCol)
+ {
+ while (mnIndex >= maEntries.size() || mbNullCol)
+ {
+ mnIndex = 0;
+ mnCol++;
+ if (mnCol > mrDoc.MaxCol())
+ {
+ mnCol = 0;
+ mnTab++;
+ if (mnTab >= mrDoc.GetTableCount())
+ return nullptr;
+ }
+ ScTable *pTab = mrDoc.FetchTable(mnTab);
+ ScColumn *pCol = (pTab && pTab->IsColValid(mnCol)) ? pTab->FetchColumn(mnCol) : nullptr;
+ if (pCol)
+ {
+ mbNullCol = false;
+ maEntries = pCol->GetFormulaGroupEntries();
+ }
+ else
+ mbNullCol = true;
+ }
+ }
+
+ return &maEntries[mnIndex++];
+}
+
+ScCellIterator::ScCellIterator( ScDocument& rDoc, const ScRange& rRange, SubtotalFlags nSubTotalFlags ) :
+ mrDoc(rDoc),
+ maStartPos(rRange.aStart),
+ maEndPos(rRange.aEnd),
+ mnSubTotalFlags(nSubTotalFlags)
+{
+ init();
+}
+
+void ScCellIterator::incBlock()
+{
+ ++maCurColPos.first;
+ maCurColPos.second = 0;
+
+ maCurPos.SetRow(maCurColPos.first->position);
+}
+
+void ScCellIterator::incPos()
+{
+ if (maCurColPos.second + 1 < maCurColPos.first->size)
+ {
+ // Move within the same block.
+ ++maCurColPos.second;
+ maCurPos.IncRow();
+ }
+ else
+ // Move to the next block.
+ incBlock();
+}
+
+void ScCellIterator::setPos(size_t nPos)
+{
+ maCurColPos = getColumn()->maCells.position(maCurColPos.first, nPos);
+ maCurPos.SetRow(nPos);
+}
+
+const ScColumn* ScCellIterator::getColumn() const
+{
+ return &mrDoc.maTabs[maCurPos.Tab()]->aCol[maCurPos.Col()];
+}
+
+void ScCellIterator::init()
+{
+ SCTAB nDocMaxTab = mrDoc.GetTableCount() - 1;
+
+ PutInOrder(maStartPos, maEndPos);
+
+ if (!mrDoc.ValidCol(maStartPos.Col())) maStartPos.SetCol(mrDoc.MaxCol());
+ if (!mrDoc.ValidCol(maEndPos.Col())) maEndPos.SetCol(mrDoc.MaxCol());
+ if (!mrDoc.ValidRow(maStartPos.Row())) maStartPos.SetRow(mrDoc.MaxRow());
+ if (!mrDoc.ValidRow(maEndPos.Row())) maEndPos.SetRow(mrDoc.MaxRow());
+ if (!ValidTab(maStartPos.Tab(), nDocMaxTab)) maStartPos.SetTab(nDocMaxTab);
+ if (!ValidTab(maEndPos.Tab(), nDocMaxTab)) maEndPos.SetTab(nDocMaxTab);
+
+ while (maEndPos.Tab() > 0 && !mrDoc.maTabs[maEndPos.Tab()])
+ maEndPos.IncTab(-1); // Only the tables in use
+
+ if (maStartPos.Tab() > maEndPos.Tab())
+ maStartPos.SetTab(maEndPos.Tab());
+
+ if (!mrDoc.maTabs[maStartPos.Tab()])
+ {
+ assert(!"Table not found");
+ maStartPos = ScAddress(mrDoc.MaxCol()+1, mrDoc.MaxRow()+1, MAXTAB+1); // -> Abort on GetFirst.
+ }
+ else
+ {
+ maStartPos.SetCol(mrDoc.maTabs[maStartPos.Tab()]->ClampToAllocatedColumns(maStartPos.Col()));
+ }
+
+ maCurPos = maStartPos;
+}
+
+bool ScCellIterator::getCurrent()
+{
+ const ScColumn* pCol = getColumn();
+
+ while (true)
+ {
+ bool bNextColumn = maCurColPos.first == pCol->maCells.end();
+ if (!bNextColumn)
+ {
+ if (maCurPos.Row() > maEndPos.Row())
+ bNextColumn = true;
+ }
+
+ if (bNextColumn)
+ {
+ // Move to the next column.
+ maCurPos.SetRow(maStartPos.Row());
+ do
+ {
+ maCurPos.IncCol();
+ while (maCurPos.Col() >= mrDoc.GetAllocatedColumnsCount(maCurPos.Tab())
+ || maCurPos.Col() > maEndPos.Col())
+ {
+ maCurPos.SetCol(maStartPos.Col());
+ maCurPos.IncTab();
+ if (maCurPos.Tab() > maEndPos.Tab())
+ {
+ maCurCell.clear();
+ return false;
+ }
+ }
+ pCol = getColumn();
+ }
+ while (pCol->IsEmptyData());
+
+ maCurColPos = pCol->maCells.position(maCurPos.Row());
+ }
+
+ if (maCurColPos.first->type == sc::element_type_empty)
+ {
+ incBlock();
+ continue;
+ }
+
+ SCROW nLastRow;
+ // Skip all filtered or hidden rows, depending on mSubTotalFlags
+ if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) &&
+ pCol->GetDoc().RowFiltered(maCurPos.Row(), maCurPos.Tab(), nullptr, &nLastRow) ) ||
+ ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) &&
+ pCol->GetDoc().RowHidden(maCurPos.Row(), maCurPos.Tab(), nullptr, &nLastRow) ) )
+ {
+ setPos(nLastRow+1);
+ continue;
+ }
+
+ if (maCurColPos.first->type == sc::element_type_formula)
+ {
+ if ( mnSubTotalFlags != SubtotalFlags::NONE )
+ {
+ ScFormulaCell* pCell = sc::formula_block::at(*maCurColPos.first->data, maCurColPos.second);
+ // Skip formula cells with Subtotal formulae or errors, depending on mnSubTotalFlags
+ if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) && pCell->IsSubTotal() ) ||
+ ( ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) && pCell->GetErrCode() != FormulaError::NONE ) )
+ {
+ incPos();
+ continue;
+ }
+ }
+ }
+
+ maCurCell = sc::toRefCell(maCurColPos.first, maCurColPos.second);
+ return true;
+ }
+ return false;
+}
+
+OUString ScCellIterator::getString() const
+{
+ return maCurCell.getString(&mrDoc);
+}
+
+ScCellValue ScCellIterator::getCellValue() const
+{
+ switch (maCurCell.getType())
+ {
+ case CELLTYPE_STRING:
+ return ScCellValue(maCurCell.getSharedString());
+ break;
+ case CELLTYPE_EDIT:
+ return ScCellValue(maCurCell.getEditText()->Clone());
+ break;
+ case CELLTYPE_VALUE:
+ return ScCellValue(maCurCell.getDouble());
+ break;
+ case CELLTYPE_FORMULA:
+ return ScCellValue(maCurCell.getFormula()->Clone());
+ break;
+ default:
+ return ScCellValue();
+ }
+}
+
+bool ScCellIterator::hasString() const
+{
+ return maCurCell.hasString();
+}
+
+bool ScCellIterator::isEmpty() const
+{
+ return maCurCell.isEmpty();
+}
+
+bool ScCellIterator::equalsWithoutFormat( const ScAddress& rPos ) const
+{
+ ScRefCellValue aOther(mrDoc, rPos);
+ return maCurCell.equalsWithoutFormat(aOther);
+}
+
+bool ScCellIterator::first()
+{
+ if (!ValidTab(maCurPos.Tab()))
+ return false;
+
+ maCurPos = maStartPos;
+ const ScColumn* pCol = getColumn();
+
+ maCurColPos = pCol->maCells.position(maCurPos.Row());
+ return getCurrent();
+}
+
+bool ScCellIterator::next()
+{
+ incPos();
+ return getCurrent();
+}
+
+ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument& rDocument, SCTAB nTable,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
+ rDoc( rDocument ),
+ mnTab( nTable ),
+ nStartCol( nCol1 ),
+ nEndCol( nCol2 ),
+ nStartRow( nRow1 ),
+ nEndRow( nRow2 ),
+ mnCol( nCol1 ),
+ mnRow( nRow1 ),
+ mbMore( false )
+{
+ assert(mnTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+
+ const ScTable* pTab = rDoc.FetchTable(mnTab);
+ if (!pTab)
+ return;
+
+ nEndCol = pTab->ClampToAllocatedColumns(nEndCol);
+ if (nEndCol < nStartCol) // E.g., somewhere completely outside allocated area
+ nEndCol = nStartCol - 1; // Empty
+
+ maColPositions.reserve( nEndCol-nStartCol+1 );
+
+ SetTab( mnTab );
+}
+
+ScHorizontalCellIterator::~ScHorizontalCellIterator()
+{
+}
+
+void ScHorizontalCellIterator::SetTab( SCTAB nTabP )
+{
+ mbMore = false;
+ mnTab = nTabP;
+ mnRow = nStartRow;
+ mnCol = nStartCol;
+ maColPositions.resize(0);
+
+ // Set the start position in each column.
+ for (SCCOL i = nStartCol; i <= nEndCol; ++i)
+ {
+ ScColumn* pCol = &rDoc.maTabs[mnTab]->aCol[i];
+ ColParam aParam;
+ aParam.maPos = pCol->maCells.position(nStartRow).first;
+ aParam.maEnd = pCol->maCells.end();
+ aParam.mnCol = i;
+
+ // find first non-empty element.
+ while (aParam.maPos != aParam.maEnd) {
+ if (aParam.maPos->type == sc::element_type_empty)
+ ++aParam.maPos;
+ else
+ {
+ maColPositions.push_back( aParam );
+ break;
+ }
+ }
+ }
+
+ if (maColPositions.empty())
+ return;
+
+ maColPos = maColPositions.begin();
+ mbMore = true;
+ SkipInvalid();
+}
+
+ScRefCellValue* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow )
+{
+ if (!mbMore)
+ {
+ debugiter("no more !\n");
+ return nullptr;
+ }
+
+ // Return the current non-empty cell, and move the cursor to the next one.
+ ColParam& r = *maColPos;
+
+ rCol = mnCol = r.mnCol;
+ rRow = mnRow;
+ debugiter("return col %d row %d\n", (int)rCol, (int)rRow);
+
+ size_t nOffset = static_cast<size_t>(mnRow) - r.maPos->position;
+ maCurCell = sc::toRefCell(r.maPos, nOffset);
+ Advance();
+ debugiter("advance to: col %d row %d\n", (int)maColPos->mnCol, (int)mnRow);
+
+ return &maCurCell;
+}
+
+bool ScHorizontalCellIterator::GetPos( SCCOL& rCol, SCROW& rRow )
+{
+ rCol = mnCol;
+ rRow = mnRow;
+ return mbMore;
+}
+
+// Skip any invalid / empty cells across the current row,
+// we only advance the cursor if the current entry is invalid.
+// if we return true we have a valid cursor (or hit the end)
+bool ScHorizontalCellIterator::SkipInvalidInRow()
+{
+ assert (mbMore);
+ assert (maColPos != maColPositions.end());
+
+ // Find the next non-empty cell in the current row.
+ while( maColPos != maColPositions.end() )
+ {
+ ColParam& r = *maColPos;
+ assert (r.maPos != r.maEnd);
+
+ size_t nRow = static_cast<size_t>(mnRow);
+
+ if (nRow >= r.maPos->position)
+ {
+ if (nRow < r.maPos->position + r.maPos->size)
+ {
+ mnCol = maColPos->mnCol;
+ debugiter("found valid cell at column %d, row %d\n",
+ (int)mnCol, (int)mnRow);
+ assert(r.maPos->type != sc::element_type_empty);
+ return true;
+ }
+ else
+ {
+ bool bMoreBlocksInColumn = false;
+ // This block is behind the current row position. Advance the block.
+ for (++r.maPos; r.maPos != r.maEnd; ++r.maPos)
+ {
+ if (nRow < r.maPos->position + r.maPos->size &&
+ r.maPos->type != sc::element_type_empty)
+ {
+ bMoreBlocksInColumn = true;
+ break;
+ }
+ }
+ if (!bMoreBlocksInColumn)
+ {
+ debugiter("remove column %d at row %d\n",
+ (int)maColPos->mnCol, (int)nRow);
+ maColPos = maColPositions.erase(maColPos);
+ if (maColPositions.empty())
+ {
+ debugiter("no more columns\n");
+ mbMore = false;
+ }
+ }
+ else
+ {
+ debugiter("advanced column %d to block starting row %d, retrying\n",
+ (int)maColPos->mnCol, r.maPos->position);
+ }
+ }
+ }
+ else
+ {
+ debugiter("skip empty cells at column %d, row %d\n",
+ (int)maColPos->mnCol, (int)nRow);
+ ++maColPos;
+ }
+ }
+
+ // No more columns with anything interesting in them ?
+ if (maColPositions.empty())
+ {
+ debugiter("no more live columns left - done\n");
+ mbMore = false;
+ return true;
+ }
+
+ return false;
+}
+
+/// Find the next row that has some real content in one of its columns.
+SCROW ScHorizontalCellIterator::FindNextNonEmptyRow()
+{
+ size_t nNextRow = rDoc.MaxRow()+1;
+
+ for (const ColParam& r : maColPositions)
+ {
+ assert(o3tl::make_unsigned(mnRow) <= r.maPos->position);
+ nNextRow = std::min (nNextRow, static_cast<size_t>(r.maPos->position));
+ }
+
+ SCROW nRow = std::max(static_cast<SCROW>(nNextRow), mnRow);
+ debugiter("Next non empty row is %d\n", (int) nRow);
+ return nRow;
+}
+
+void ScHorizontalCellIterator::Advance()
+{
+ assert (mbMore);
+ assert (maColPos != maColPositions.end());
+
+ ++maColPos;
+
+ SkipInvalid();
+}
+
+void ScHorizontalCellIterator::SkipInvalid()
+{
+ if (maColPos == maColPositions.end() ||
+ !SkipInvalidInRow())
+ {
+ mnRow++;
+
+ if (mnRow > nEndRow)
+ {
+ mbMore = false;
+ return;
+ }
+
+ maColPos = maColPositions.begin();
+ debugiter("moving to next row\n");
+ if (SkipInvalidInRow())
+ {
+ debugiter("moved to valid cell in next row (or end)\n");
+ return;
+ }
+
+ mnRow = FindNextNonEmptyRow();
+ maColPos = maColPositions.begin();
+ bool bCorrect = SkipInvalidInRow();
+ assert (bCorrect); (void) bCorrect;
+ }
+
+ if (mnRow > nEndRow)
+ mbMore = false;
+}
+
+ScHorizontalValueIterator::ScHorizontalValueIterator( ScDocument& rDocument,
+ const ScRange& rRange ) :
+ rDoc( rDocument ),
+ nEndTab( rRange.aEnd.Tab() ),
+ bCalcAsShown( rDocument.GetDocOptions().IsCalcAsShown() )
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ PutInOrder( nStartCol, nEndCol);
+ PutInOrder( nStartRow, nEndRow);
+ PutInOrder( nStartTab, nEndTab );
+
+ if (!rDoc.ValidCol(nStartCol)) nStartCol = rDoc.MaxCol();
+ if (!rDoc.ValidCol(nEndCol)) nEndCol = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nStartRow)) nStartRow = rDoc.MaxRow();
+ if (!rDoc.ValidRow(nEndRow)) nEndRow = rDoc.MaxRow();
+ if (!ValidTab(nStartTab)) nStartTab = MAXTAB;
+ if (!ValidTab(nEndTab)) nEndTab = MAXTAB;
+
+ nCurCol = nStartCol;
+ nCurRow = nStartRow;
+ nCurTab = nStartTab;
+
+ nNumFormat = 0; // Will be initialized in GetNumberFormat()
+ pAttrArray = nullptr;
+ nAttrEndRow = 0;
+
+ pCellIter.reset( new ScHorizontalCellIterator( rDoc, nStartTab, nStartCol,
+ nStartRow, nEndCol, nEndRow ) );
+}
+
+ScHorizontalValueIterator::~ScHorizontalValueIterator()
+{
+}
+
+bool ScHorizontalValueIterator::GetNext( double& rValue, FormulaError& rErr )
+{
+ bool bFound = false;
+ while ( !bFound )
+ {
+ ScRefCellValue* pCell = pCellIter->GetNext( nCurCol, nCurRow );
+ while ( !pCell )
+ {
+ if ( nCurTab < nEndTab )
+ {
+ pCellIter->SetTab( ++nCurTab);
+ pCell = pCellIter->GetNext( nCurCol, nCurRow );
+ }
+ else
+ return false;
+ }
+ switch (pCell->getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ rValue = pCell->getDouble();
+ rErr = FormulaError::NONE;
+ if ( bCalcAsShown )
+ {
+ ScColumn* pCol = &rDoc.maTabs[nCurTab]->aCol[nCurCol];
+ ScAttrArray_IterGetNumberFormat( nNumFormat, pAttrArray,
+ nAttrEndRow, pCol->pAttrArray.get(), nCurRow, rDoc );
+ rValue = rDoc.RoundValueAsShown( rValue, nNumFormat );
+ }
+ bFound = true;
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ rErr = pCell->getFormula()->GetErrCode();
+ if (rErr != FormulaError::NONE || pCell->getFormula()->IsValue())
+ {
+ rValue = pCell->getFormula()->GetValue();
+ bFound = true;
+ }
+ }
+ break;
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ break;
+ default: ; // nothing
+ }
+ }
+ return bFound;
+}
+
+ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument& rDocument, SCTAB nTable,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) :
+ rDoc( rDocument ),
+ nTab( nTable ),
+ nStartCol( nCol1 ),
+ nStartRow( nRow1 ),
+ nEndCol( nCol2 ),
+ nEndRow( nRow2 )
+{
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ assert(rDoc.maTabs[nTab]);
+
+ nRow = nStartRow;
+ nCol = nStartCol;
+
+ pIndices.reset( new SCSIZE[nEndCol-nStartCol+1] );
+ pNextEnd.reset( new SCROW[nEndCol-nStartCol+1] );
+ pHorizEnd.reset( new SCCOL[nEndCol-nStartCol+1] );
+ ppPatterns.reset( new const ScPatternAttr*[nEndCol-nStartCol+1] );
+
+ InitForNextRow(true);
+}
+
+ScHorizontalAttrIterator::~ScHorizontalAttrIterator()
+{
+}
+
+void ScHorizontalAttrIterator::InitForNextRow(bool bInitialization)
+{
+ nMinNextEnd = rDoc.MaxRow();
+ SCCOL nThisHead = 0;
+
+ for (SCCOL i=nStartCol; i<=nEndCol; i++)
+ {
+ SCCOL nPos = i - nStartCol;
+ if ( bInitialization || pNextEnd[nPos] < nRow )
+ {
+ const ScAttrArray& pArray = rDoc.maTabs[nTab]->ColumnData(i).AttrArray();
+
+ SCSIZE nIndex;
+ if (bInitialization)
+ {
+ if ( pArray.Count() )
+ pArray.Search( nStartRow, nIndex );
+ else
+ nIndex = 0;
+ pIndices[nPos] = nIndex;
+ pHorizEnd[nPos] = rDoc.MaxCol()+1; // only for assert()
+ }
+ else
+ nIndex = ++pIndices[nPos];
+
+ if ( !nIndex && !pArray.Count() )
+ {
+ pNextEnd[nPos] = rDoc.MaxRow();
+ assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
+ ppPatterns[nPos] = rDoc.GetDefPattern();
+ }
+ else if ( nIndex < pArray.Count() )
+ {
+ const ScPatternAttr* pPattern = pArray.mvData[nIndex].pPattern;
+ SCROW nThisEnd = pArray.mvData[nIndex].nEndRow;
+ pNextEnd[nPos] = nThisEnd;
+ assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
+ ppPatterns[nPos] = pPattern;
+ }
+ else
+ {
+ assert(!"AttrArray does not range to MAXROW");
+ pNextEnd[nPos] = rDoc.MaxRow();
+ ppPatterns[nPos] = nullptr;
+ }
+ }
+
+ if ( nMinNextEnd > pNextEnd[nPos] )
+ nMinNextEnd = pNextEnd[nPos];
+
+ // store positions of ScHorizontalAttrIterator elements (minimizing expensive ScPatternAttr comparisons)
+ if (i > nStartCol && !SfxPoolItem::areSame(ppPatterns[nThisHead], ppPatterns[nPos]))
+ {
+ pHorizEnd[nThisHead] = i - 1;
+ nThisHead = nPos; // start position of the next horizontal group
+ }
+ }
+
+ pHorizEnd[nThisHead] = nEndCol; // set the end position of the last horizontal group, too
+}
+
+const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow )
+{
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ for (;;)
+ {
+ if ( nCol <= nEndCol )
+ {
+ const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol];
+ rRow = nRow;
+ rCol1 = nCol;
+ assert( pHorizEnd[nCol-nStartCol] < rDoc.MaxCol()+1 && "missing stored data" );
+ nCol = pHorizEnd[nCol-nStartCol];
+ rCol2 = nCol;
+ ++nCol; // Count up for next call
+ return pPat; // Found it!
+ }
+
+ // Next row
+ ++nRow;
+ if ( nRow > nEndRow ) // Already at the end?
+ return nullptr; // Found nothing
+ nCol = nStartCol; // Start at the left again
+
+ if ( nRow > nMinNextEnd )
+ InitForNextRow(false);
+ }
+}
+
+static bool IsGreater( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ return ( nRow1 > nRow2 ) || ( nRow1 == nRow2 && nCol1 > nCol2 );
+}
+
+ScUsedAreaIterator::ScUsedAreaIterator( ScDocument& rDocument, SCTAB nTable,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+ : aCellIter( rDocument, nTable, nCol1, nRow1, nCol2, nRow2 )
+ , aAttrIter( rDocument, nTable, nCol1, nRow1, nCol2, nRow2 )
+ , nNextCol( nCol1 )
+ , nNextRow( nRow1 )
+ , nCellCol( 0 )
+ , nCellRow( 0 )
+ , nAttrCol1( 0 )
+ , nAttrCol2( 0 )
+ , nAttrRow( 0 )
+ , nFoundStartCol( 0 )
+ , nFoundEndCol( 0 )
+ , nFoundRow( 0 )
+ , pFoundPattern( nullptr )
+{
+ pCell = aCellIter.GetNext( nCellCol, nCellRow );
+ pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow );
+}
+
+ScUsedAreaIterator::~ScUsedAreaIterator()
+{
+}
+
+bool ScUsedAreaIterator::GetNext()
+{
+ // Forward iterators
+ if ( pCell && IsGreater( nNextCol, nNextRow, nCellCol, nCellRow ) )
+ pCell = aCellIter.GetNext( nCellCol, nCellRow );
+
+ while (pCell && pCell->isEmpty())
+ pCell = aCellIter.GetNext( nCellCol, nCellRow );
+
+ if ( pPattern && IsGreater( nNextCol, nNextRow, nAttrCol2, nAttrRow ) )
+ pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow );
+
+ if ( pPattern && nAttrRow == nNextRow && nAttrCol1 < nNextCol )
+ nAttrCol1 = nNextCol;
+
+ // Find next area
+ bool bFound = true;
+ bool bUseCell = false;
+
+ if ( pCell && pPattern )
+ {
+ if ( IsGreater( nCellCol, nCellRow, nAttrCol1, nAttrRow ) ) // Only attributes at the beginning?
+ {
+ maFoundCell.clear();
+ pFoundPattern = pPattern;
+ nFoundRow = nAttrRow;
+ nFoundStartCol = nAttrCol1;
+ if ( nCellRow == nAttrRow && nCellCol <= nAttrCol2 ) // Area also contains cell?
+ nFoundEndCol = nCellCol - 1; // Only until right before the cell
+ else
+ nFoundEndCol = nAttrCol2; // Everything
+ }
+ else
+ {
+ bUseCell = true;
+ if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attributes on the cell?
+ pFoundPattern = pPattern;
+ else
+ pFoundPattern = nullptr;
+ }
+ }
+ else if ( pCell ) // Just a cell -> take over right away
+ {
+ pFoundPattern = nullptr;
+ bUseCell = true; // Cell position
+ }
+ else if ( pPattern ) // Just attributes -> take over right away
+ {
+ maFoundCell.clear();
+ pFoundPattern = pPattern;
+ nFoundRow = nAttrRow;
+ nFoundStartCol = nAttrCol1;
+ nFoundEndCol = nAttrCol2;
+ }
+ else // Nothing
+ bFound = false;
+
+ if ( bUseCell ) // Cell position
+ {
+ if (pCell)
+ maFoundCell = *pCell;
+ else
+ maFoundCell.clear();
+
+ nFoundRow = nCellRow;
+ nFoundStartCol = nFoundEndCol = nCellCol;
+ }
+
+ if (bFound)
+ {
+ nNextRow = nFoundRow;
+ nNextCol = nFoundEndCol + 1;
+ }
+
+ return bFound;
+}
+
+ScDocAttrIterator::ScDocAttrIterator(ScDocument& rDocument, SCTAB nTable,
+ SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2) :
+ rDoc( rDocument ),
+ nTab( nTable ),
+ nEndCol( nCol2 ),
+ nStartRow( nRow1 ),
+ nEndRow( nRow2 ),
+ nCol( nCol1 )
+{
+ if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
+ pColIter = rDoc.maTabs[nTab]->ColumnData(nCol).CreateAttrIterator( nStartRow, nEndRow );
+}
+
+ScDocAttrIterator::~ScDocAttrIterator()
+{
+}
+
+const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 )
+{
+ while ( pColIter )
+ {
+ const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 );
+ if ( pPattern )
+ {
+ rCol = nCol;
+ return pPattern;
+ }
+
+ ++nCol;
+ if ( nCol <= nEndCol )
+ pColIter = rDoc.maTabs[nTab]->ColumnData(nCol).CreateAttrIterator( nStartRow, nEndRow );
+ else
+ pColIter.reset();
+ }
+ return nullptr; // Nothing anymore
+}
+
+ScDocRowHeightUpdater::TabRanges::TabRanges(SCTAB nTab, SCROW nMaxRow) :
+ mnTab(nTab), maRanges(nMaxRow)
+{
+}
+
+ScDocRowHeightUpdater::ScDocRowHeightUpdater(ScDocument& rDoc, OutputDevice* pOutDev, double fPPTX, double fPPTY, const vector<TabRanges>* pTabRangesArray) :
+ mrDoc(rDoc), mpOutDev(pOutDev), mfPPTX(fPPTX), mfPPTY(fPPTY), mpTabRangesArray(pTabRangesArray)
+{
+}
+
+void ScDocRowHeightUpdater::update(const bool bOnlyUsedRows)
+{
+ if (!mpTabRangesArray || mpTabRangesArray->empty())
+ {
+ // No ranges defined. Update all rows in all tables.
+ updateAll(bOnlyUsedRows);
+ return;
+ }
+
+ sal_uInt64 nCellCount = 0;
+ for (const auto& rTabRanges : *mpTabRangesArray)
+ {
+ const SCTAB nTab = rTabRanges.mnTab;
+ if (!ValidTab(nTab) || nTab >= mrDoc.GetTableCount() || !mrDoc.maTabs[nTab])
+ continue;
+
+ ScFlatBoolRowSegments::RangeData aData;
+ ScFlatBoolRowSegments::RangeIterator aRangeItr(rTabRanges.maRanges);
+ for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
+ {
+ if (!aData.mbValue)
+ continue;
+
+ nCellCount += mrDoc.maTabs[nTab]->GetWeightedCount(aData.mnRow1, aData.mnRow2);
+ }
+ }
+
+ ScProgress aProgress(mrDoc.GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true);
+
+ Fraction aZoom(1, 1);
+ sal_uInt64 nProgressStart = 0;
+ for (const auto& rTabRanges : *mpTabRangesArray)
+ {
+ const SCTAB nTab = rTabRanges.mnTab;
+ if (!ValidTab(nTab) || nTab >= mrDoc.GetTableCount() || !mrDoc.maTabs[nTab])
+ continue;
+
+ sc::RowHeightContext aCxt(mrDoc.MaxRow(), mfPPTX, mfPPTY, aZoom, aZoom, mpOutDev);
+ ScFlatBoolRowSegments::RangeData aData;
+ ScFlatBoolRowSegments::RangeIterator aRangeItr(rTabRanges.maRanges);
+ for (bool bFound = aRangeItr.getFirst(aData); bFound; bFound = aRangeItr.getNext(aData))
+ {
+ if (!aData.mbValue)
+ continue;
+
+ mrDoc.maTabs[nTab]->SetOptimalHeight(
+ aCxt, aData.mnRow1, aData.mnRow2, true, &aProgress, nProgressStart);
+
+ nProgressStart += mrDoc.maTabs[nTab]->GetWeightedCount(aData.mnRow1, aData.mnRow2);
+ }
+ }
+}
+
+void ScDocRowHeightUpdater::updateAll(const bool bOnlyUsedRows)
+{
+ sal_uInt64 nCellCount = 0;
+ for (SCTAB nTab = 0; nTab < mrDoc.GetTableCount(); ++nTab)
+ {
+ if (!ValidTab(nTab) || !mrDoc.maTabs[nTab])
+ continue;
+
+ nCellCount += mrDoc.maTabs[nTab]->GetWeightedCount();
+ }
+
+ ScProgress aProgress(mrDoc.GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true);
+
+ Fraction aZoom(1, 1);
+ sc::RowHeightContext aCxt(mrDoc.MaxRow(), mfPPTX, mfPPTY, aZoom, aZoom, mpOutDev);
+ sal_uInt64 nProgressStart = 0;
+ for (SCTAB nTab = 0; nTab < mrDoc.GetTableCount(); ++nTab)
+ {
+ if (!ValidTab(nTab) || !mrDoc.maTabs[nTab])
+ continue;
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = mrDoc.MaxRow();
+ if (!bOnlyUsedRows || mrDoc.GetPrintArea(nTab, nEndCol, nEndRow))
+ mrDoc.maTabs[nTab]->SetOptimalHeight(aCxt, 0, nEndRow, true, &aProgress, nProgressStart);
+ nProgressStart += mrDoc.maTabs[nTab]->GetWeightedCount();
+ }
+}
+
+ScAttrRectIterator::ScAttrRectIterator(ScDocument& rDocument, SCTAB nTable,
+ SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2) :
+ rDoc( rDocument ),
+ nTab( nTable ),
+ nEndCol( nCol2 ),
+ nStartRow( nRow1 ),
+ nEndRow( nRow2 ),
+ nIterStartCol( nCol1 ),
+ nIterEndCol( nCol1 )
+{
+ if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
+ {
+ pColIter = rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nStartRow, nEndRow );
+ while ( nIterEndCol < nEndCol &&
+ rDoc.maTabs[nTab]->ColumnData(nIterEndCol).IsAllAttrEqual(
+ rDoc.maTabs[nTab]->ColumnData(nIterEndCol+1), nStartRow, nEndRow ) )
+ ++nIterEndCol;
+ }
+}
+
+ScAttrRectIterator::~ScAttrRectIterator()
+{
+}
+
+void ScAttrRectIterator::DataChanged()
+{
+ if (pColIter)
+ {
+ SCROW nNextRow = pColIter->GetNextRow();
+ pColIter = rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nNextRow, nEndRow );
+ }
+}
+
+const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2,
+ SCROW& rRow1, SCROW& rRow2 )
+{
+ while ( pColIter )
+ {
+ const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 );
+ if ( pPattern )
+ {
+ rCol1 = nIterStartCol;
+ rCol2 = nIterEndCol;
+ return pPattern;
+ }
+
+ nIterStartCol = nIterEndCol+1;
+ if ( nIterStartCol <= nEndCol )
+ {
+ nIterEndCol = nIterStartCol;
+ pColIter = rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nStartRow, nEndRow );
+ while ( nIterEndCol < nEndCol &&
+ rDoc.maTabs[nTab]->ColumnData(nIterEndCol).IsAllAttrEqual(
+ rDoc.maTabs[nTab]->ColumnData(nIterEndCol+1), nStartRow, nEndRow ) )
+ ++nIterEndCol;
+ }
+ else
+ pColIter.reset();
+ }
+ return nullptr; // Nothing anymore
+}
+
+ScRowBreakIterator::ScRowBreakIterator(set<SCROW>& rBreaks) :
+ mrBreaks(rBreaks),
+ maItr(rBreaks.begin()), maEnd(rBreaks.end())
+{
+}
+
+SCROW ScRowBreakIterator::first()
+{
+ maItr = mrBreaks.begin();
+ return maItr == maEnd ? NOT_FOUND : *maItr;
+}
+
+SCROW ScRowBreakIterator::next()
+{
+ ++maItr;
+ return maItr == maEnd ? NOT_FOUND : *maItr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/docparam.cxx b/sc/source/core/data/docparam.cxx
new file mode 100644
index 0000000000..3e2dea1b6d
--- /dev/null
+++ b/sc/source/core/data/docparam.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 <docparam.hxx>
+
+ScColWidthParam::ScColWidthParam() :
+ mnMaxTextRow(-1), mnMaxTextLen(0), mbSimpleText(true) {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/docpool.cxx b/sc/source/core/data/docpool.cxx
new file mode 100644
index 0000000000..05dd889582
--- /dev/null
+++ b/sc/source/core/data/docpool.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 <sal/config.h>
+
+#include <scitems.hxx>
+
+#include <comphelper/string.hxx>
+#include <i18nutil/unicode.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/stritem.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/itemtype.hxx>
+#include <editeng/editrids.hrc>
+#include <editeng/eerdll.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/pbinitem.hxx>
+#include <editeng/postitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <docpool.hxx>
+#include <global.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+
+// ATTR_FONT_TWOLINES (not used) was changed to ATTR_USERDEF (not saved in binary format) in 641c
+
+namespace {
+
+SvxFontItem* getDefaultFontItem(LanguageType eLang, DefaultFontType nFontType, sal_uInt16 nItemId)
+{
+ vcl::Font aDefFont = OutputDevice::GetDefaultFont( nFontType, eLang, GetDefaultFontFlags::OnlyOne );
+ SvxFontItem* pNewItem = new SvxFontItem( aDefFont.GetFamilyType(), aDefFont.GetFamilyName(), aDefFont.GetStyleName(),
+ aDefFont.GetPitch(), aDefFont.GetCharSet(), nItemId );
+
+ return pNewItem;
+}
+
+}
+
+SfxItemInfo const aItemInfos[] =
+{
+ // _nSID, _bNeedsPoolRegistration, _bShareable
+ { SID_ATTR_CHAR_FONT, true, true }, // ATTR_FONT
+ { SID_ATTR_CHAR_FONTHEIGHT, false, true }, // ATTR_FONT_HEIGHT
+ { SID_ATTR_CHAR_WEIGHT, false, true }, // ATTR_FONT_WEIGHT
+ { SID_ATTR_CHAR_POSTURE, false, true }, // ATTR_FONT_POSTURE
+ { SID_ATTR_CHAR_UNDERLINE, false, true }, // ATTR_FONT_UNDERLINE
+ { SID_ATTR_CHAR_OVERLINE, false, true }, // ATTR_FONT_OVERLINE
+ { SID_ATTR_CHAR_STRIKEOUT, false, true }, // ATTR_FONT_CROSSEDOUT
+ { SID_ATTR_CHAR_CONTOUR, false, true }, // ATTR_FONT_CONTOUR
+ { SID_ATTR_CHAR_SHADOWED, false, true }, // ATTR_FONT_SHADOWED
+ { SID_ATTR_CHAR_COLOR, true, true }, // ATTR_FONT_COLOR
+ { SID_ATTR_CHAR_LANGUAGE, false, true }, // ATTR_FONT_LANGUAGE
+ { SID_ATTR_CHAR_CJK_FONT, true, true }, // ATTR_CJK_FONT from 614
+ { SID_ATTR_CHAR_CJK_FONTHEIGHT, false, true }, // ATTR_CJK_FONT_HEIGHT from 614
+ { SID_ATTR_CHAR_CJK_WEIGHT, false, true }, // ATTR_CJK_FONT_WEIGHT from 614
+ { SID_ATTR_CHAR_CJK_POSTURE, false, true }, // ATTR_CJK_FONT_POSTURE from 614
+ { SID_ATTR_CHAR_CJK_LANGUAGE, false, true }, // ATTR_CJK_FONT_LANGUAGE from 614
+ { SID_ATTR_CHAR_CTL_FONT, true, true }, // ATTR_CTL_FONT from 614
+ { SID_ATTR_CHAR_CTL_FONTHEIGHT, false, true }, // ATTR_CTL_FONT_HEIGHT from 614
+ { SID_ATTR_CHAR_CTL_WEIGHT, false, true }, // ATTR_CTL_FONT_WEIGHT from 614
+ { SID_ATTR_CHAR_CTL_POSTURE, false, true }, // ATTR_CTL_FONT_POSTURE from 614
+ { SID_ATTR_CHAR_CTL_LANGUAGE, false, true }, // ATTR_CTL_FONT_LANGUAGE from 614
+ { SID_ATTR_CHAR_EMPHASISMARK, false, true }, // ATTR_FONT_EMPHASISMARK from 614
+ { 0, true, true }, // ATTR_USERDEF from 614 / 641c
+ { SID_ATTR_CHAR_WORDLINEMODE, false, true }, // ATTR_FONT_WORDLINE from 632b
+ { SID_ATTR_CHAR_RELIEF, false, true }, // ATTR_FONT_RELIEF from 632b
+ { SID_ATTR_ALIGN_HYPHENATION, false, true }, // ATTR_HYPHENATE from 632b
+ { 0, false, true }, // ATTR_SCRIPTSPACE from 614d
+ { 0, false, true }, // ATTR_HANGPUNCTUATION from 614d
+ { SID_ATTR_PARA_FORBIDDEN_RULES, false, true }, // ATTR_FORBIDDEN_RULES from 614d
+ { SID_ATTR_ALIGN_HOR_JUSTIFY, false, true }, // ATTR_HOR_JUSTIFY
+ { SID_ATTR_ALIGN_HOR_JUSTIFY_METHOD, false, true }, // ATTR_HOR_JUSTIFY_METHOD
+ { SID_ATTR_ALIGN_INDENT, false, true }, // ATTR_INDENT from 350
+ { SID_ATTR_ALIGN_VER_JUSTIFY, false, true }, // ATTR_VER_JUSTIFY
+ { SID_ATTR_ALIGN_VER_JUSTIFY_METHOD, false, true }, // ATTR_VER_JUSTIFY_METHOD
+ { SID_ATTR_ALIGN_STACKED, false, true }, // ATTR_STACKED from 680/dr14 (replaces ATTR_ORIENTATION)
+ { SID_ATTR_ALIGN_DEGREES, true, true }, // ATTR_ROTATE_VALUE from 367
+ { SID_ATTR_ALIGN_LOCKPOS, false, true }, // ATTR_ROTATE_MODE from 367
+ { SID_ATTR_ALIGN_ASIANVERTICAL, false, true }, // ATTR_VERTICAL_ASIAN from 642
+ { SID_ATTR_FRAMEDIRECTION, false, true }, // ATTR_WRITINGDIR from 643
+ { SID_ATTR_ALIGN_LINEBREAK, false, true }, // ATTR_LINEBREAK
+ { SID_ATTR_ALIGN_SHRINKTOFIT, false, true }, // ATTR_SHRINKTOFIT from 680/dr14
+ { SID_ATTR_BORDER_DIAG_TLBR, false, true }, // ATTR_BORDER_TLBR from 680/dr14
+ { SID_ATTR_BORDER_DIAG_BLTR, false, true }, // ATTR_BORDER_BLTR from 680/dr14
+ { SID_ATTR_ALIGN_MARGIN, false, true }, // ATTR_MARGIN
+ { 0, false, true }, // ATTR_MERGE
+ { 0, false, true }, // ATTR_MERGE_FLAG
+ { SID_ATTR_NUMBERFORMAT_VALUE, false, true }, // ATTR_VALUE_FORMAT
+ { 0, false, true }, // ATTR_LANGUAGE_FORMAT from 329, is combined with SID_ATTR_NUMBERFORMAT_VALUE in the dialog
+ { SID_ATTR_BRUSH, true, true }, // ATTR_BACKGROUND
+ { SID_SCATTR_PROTECTION, false, true }, // ATTR_PROTECTION
+ { SID_ATTR_BORDER_OUTER, false, true }, // ATTR_BORDER
+ { SID_ATTR_BORDER_INNER, false, true }, // ATTR_BORDER_INNER
+ { SID_ATTR_BORDER_SHADOW, false, true }, // ATTR_SHADOW
+ { 0, false, true }, // ATTR_VALIDDATA
+ { 0, false, true }, // ATTR_CONDITIONAL
+ { 0, false, true }, // ATTR_HYPERLINK
+ { 0, true, true }, // ATTR_PATTERN
+ { SID_ATTR_LRSPACE, false, true }, // ATTR_LRSPACE
+ { SID_ATTR_ULSPACE, false, true }, // ATTR_ULSPACE
+ { SID_ATTR_PAGE, false, true }, // ATTR_PAGE
+ { SID_ATTR_PAGE_PAPERBIN, false, true }, // ATTR_PAGE_PAPERBIN
+ { SID_ATTR_PAGE_SIZE, false, true }, // ATTR_PAGE_SIZE
+ { SID_ATTR_PAGE_EXT1, false, true }, // ATTR_PAGE_HORCENTER
+ { SID_ATTR_PAGE_EXT2, false, true }, // ATTR_PAGE_VERCENTER
+ { SID_ATTR_PAGE_ON, false, true }, // ATTR_PAGE_ON
+ { SID_ATTR_PAGE_DYNAMIC, false, true }, // ATTR_PAGE_DYNAMIC
+ { SID_ATTR_PAGE_SHARED, false, true }, // ATTR_PAGE_SHARED
+ { SID_ATTR_PAGE_SHARED_FIRST, false, true }, // ATTR_PAGE_SHARED_FIRST
+ { 0, false, true }, // ATTR_PAGE_NOTES aka. SID_SCATTR_PAGE_NOTES
+ { 0, false, true }, // ATTR_PAGE_GRID aka. SID_SCATTR_PAGE_GRID
+ { 0, false, true }, // ATTR_PAGE_HEADERS aka. SID_SCATTR_PAGE_HEADERS
+ { 0, false, true }, // ATTR_PAGE_CHARTS aka. SID_SCATTR_PAGE_CHARTS
+ { 0, false, true }, // ATTR_PAGE_OBJECTS aka. SID_SCATTR_PAGE_OBJECTS
+ { 0, false, true }, // ATTR_PAGE_DRAWINGS aka. SID_SCATTR_PAGE_DRAWINGS
+ { 0, false, true }, // ATTR_PAGE_TOPDOWN aka. SID_SCATTR_PAGE_TOPDOWN
+ { 0, false, true }, // ATTR_PAGE_SCALE aka SID_SCATTR_PAGE_SCALE
+ { 0, false, true }, // ATTR_PAGE_SCALETOPAGES aka SID_SCATTR_PAGE_SCALETOPAGES
+ { 0, false, true }, // ATTR_PAGE_FIRSTPAGENO aka SID_SCATTR_PAGE_FIRSTPAGENO
+ { 0, true, true }, // ATTR_PAGE_HEADERLEFT aka SID_SCATTR_PAGE_HEADERLEFT
+ { 0, true, true }, // ATTR_PAGE_FOOTERLEFT aka SID_SCATTR_PAGE_FOOTERLEFT
+ { 0, true, true }, // ATTR_PAGE_HEADERRIGHT aka SID_SCATTR_PAGE_HEADERRIGHT
+ { 0, true, true }, // ATTR_PAGE_FOOTERRIGHT aka. SID_SCATTR_PAGE_FOOTERRIGHT
+ { 0, true, true }, // ATTR_PAGE_HEADERFIRST aka. SID_SCATTR_PAGE_HEADERFIRST
+ { 0, true, true }, // ATTR_PAGE_FOOTERFIRST aka. SID_SCATTR_PAGE_FOOTERFIRST`
+ { SID_ATTR_PAGE_HEADERSET, false, true }, // ATTR_PAGE_HEADERSET
+ { SID_ATTR_PAGE_FOOTERSET, false, true }, // ATTR_PAGE_FOOTERSET
+ { 0, false, true }, // ATTR_PAGE_FORMULAS aka. SID_SCATTR_PAGE_FORMULAS
+ { 0, false, true }, // ATTR_PAGE_NULLVALS aka. SID_SCATTR_PAGE_NULLVALS
+ { 0, false, true }, // ATTR_PAGE_SCALETO aka. SID_SCATTR_PAGE_SCALETO
+ { 0, false, true } // ATTR_HIDDEN
+};
+static_assert(
+ SAL_N_ELEMENTS(aItemInfos) == ATTR_ENDINDEX - ATTR_STARTINDEX + 1, "these must match");
+
+ScDocumentPool::ScDocumentPool()
+
+ : SfxItemPool ( "ScDocumentPool",
+ ATTR_STARTINDEX, ATTR_ENDINDEX,
+ aItemInfos, nullptr ),
+ mvPoolDefaults(ATTR_ENDINDEX-ATTR_STARTINDEX+1),
+ mnCurrentMaxKey(0)
+{
+
+ LanguageType nDefLang, nCjkLang, nCtlLang;
+ bool bAutoSpell;
+ ScModule::GetSpellSettings( nDefLang, nCjkLang, nCtlLang, bAutoSpell );
+
+ // latin font from GetDefaultFonts is not used, DEFAULTFONT_LATIN_SPREADSHEET instead
+ SvxFontItem* pStdFont = getDefaultFontItem(nDefLang, DefaultFontType::LATIN_SPREADSHEET, ATTR_FONT);
+ SvxFontItem* pCjkFont = getDefaultFontItem(nCjkLang, DefaultFontType::CJK_SPREADSHEET, ATTR_CJK_FONT);
+ SvxFontItem* pCtlFont = getDefaultFontItem(nCtlLang, DefaultFontType::CTL_SPREADSHEET, ATTR_CTL_FONT);
+
+ SvxBoxInfoItem* pGlobalBorderInnerAttr = new SvxBoxInfoItem( ATTR_BORDER_INNER );
+ SfxItemSet aSetItemItemSet( *this,
+ svl::Items<ATTR_BACKGROUND, ATTR_BACKGROUND,
+ ATTR_BORDER, ATTR_SHADOW,
+ ATTR_LRSPACE, ATTR_ULSPACE,
+ ATTR_PAGE_SIZE, ATTR_PAGE_SIZE,
+ ATTR_PAGE_ON, ATTR_PAGE_SHARED_FIRST> );
+
+ pGlobalBorderInnerAttr->SetLine(nullptr, SvxBoxInfoItemLine::HORI);
+ pGlobalBorderInnerAttr->SetLine(nullptr, SvxBoxInfoItemLine::VERT);
+ pGlobalBorderInnerAttr->SetTable(true);
+ pGlobalBorderInnerAttr->SetDist(true);
+ pGlobalBorderInnerAttr->SetMinDist(false);
+
+ mvPoolDefaults[ ATTR_FONT - ATTR_STARTINDEX ] = pStdFont;
+ mvPoolDefaults[ ATTR_FONT_HEIGHT - ATTR_STARTINDEX ] = new SvxFontHeightItem( 200, 100, ATTR_FONT_HEIGHT ); // 10 pt;
+ mvPoolDefaults[ ATTR_FONT_WEIGHT - ATTR_STARTINDEX ] = new SvxWeightItem( WEIGHT_NORMAL, ATTR_FONT_WEIGHT );
+ mvPoolDefaults[ ATTR_FONT_POSTURE - ATTR_STARTINDEX ] = new SvxPostureItem( ITALIC_NONE, ATTR_FONT_POSTURE );
+ mvPoolDefaults[ ATTR_FONT_UNDERLINE - ATTR_STARTINDEX ] = new SvxUnderlineItem( LINESTYLE_NONE, ATTR_FONT_UNDERLINE );
+ mvPoolDefaults[ ATTR_FONT_OVERLINE - ATTR_STARTINDEX ] = new SvxOverlineItem( LINESTYLE_NONE, ATTR_FONT_OVERLINE );
+ mvPoolDefaults[ ATTR_FONT_CROSSEDOUT - ATTR_STARTINDEX ] = new SvxCrossedOutItem( STRIKEOUT_NONE, ATTR_FONT_CROSSEDOUT );
+ mvPoolDefaults[ ATTR_FONT_CONTOUR - ATTR_STARTINDEX ] = new SvxContourItem( false, ATTR_FONT_CONTOUR );
+ mvPoolDefaults[ ATTR_FONT_SHADOWED - ATTR_STARTINDEX ] = new SvxShadowedItem( false, ATTR_FONT_SHADOWED );
+ mvPoolDefaults[ ATTR_FONT_COLOR - ATTR_STARTINDEX ] = new SvxColorItem( COL_AUTO, ATTR_FONT_COLOR );
+ mvPoolDefaults[ ATTR_FONT_LANGUAGE - ATTR_STARTINDEX ] = new SvxLanguageItem( LANGUAGE_DONTKNOW, ATTR_FONT_LANGUAGE );
+ mvPoolDefaults[ ATTR_CJK_FONT - ATTR_STARTINDEX ] = pCjkFont;
+ mvPoolDefaults[ ATTR_CJK_FONT_HEIGHT - ATTR_STARTINDEX ] = new SvxFontHeightItem( 200, 100, ATTR_CJK_FONT_HEIGHT );
+ mvPoolDefaults[ ATTR_CJK_FONT_WEIGHT - ATTR_STARTINDEX ] = new SvxWeightItem( WEIGHT_NORMAL, ATTR_CJK_FONT_WEIGHT );
+ mvPoolDefaults[ ATTR_CJK_FONT_POSTURE- ATTR_STARTINDEX ] = new SvxPostureItem( ITALIC_NONE, ATTR_CJK_FONT_POSTURE );
+ mvPoolDefaults[ ATTR_CJK_FONT_LANGUAGE-ATTR_STARTINDEX ] = new SvxLanguageItem( LANGUAGE_DONTKNOW, ATTR_CJK_FONT_LANGUAGE );
+ mvPoolDefaults[ ATTR_CTL_FONT - ATTR_STARTINDEX ] = pCtlFont;
+ mvPoolDefaults[ ATTR_CTL_FONT_HEIGHT - ATTR_STARTINDEX ] = new SvxFontHeightItem( 200, 100, ATTR_CTL_FONT_HEIGHT );
+ mvPoolDefaults[ ATTR_CTL_FONT_WEIGHT - ATTR_STARTINDEX ] = new SvxWeightItem( WEIGHT_NORMAL, ATTR_CTL_FONT_WEIGHT );
+ mvPoolDefaults[ ATTR_CTL_FONT_POSTURE- ATTR_STARTINDEX ] = new SvxPostureItem( ITALIC_NONE, ATTR_CTL_FONT_POSTURE );
+ mvPoolDefaults[ ATTR_CTL_FONT_LANGUAGE-ATTR_STARTINDEX ] = new SvxLanguageItem( LANGUAGE_DONTKNOW, ATTR_CTL_FONT_LANGUAGE );
+ mvPoolDefaults[ ATTR_FONT_EMPHASISMARK-ATTR_STARTINDEX ] = new SvxEmphasisMarkItem( FontEmphasisMark::NONE, ATTR_FONT_EMPHASISMARK );
+ mvPoolDefaults[ ATTR_USERDEF - ATTR_STARTINDEX ] = new SvXMLAttrContainerItem( ATTR_USERDEF );
+ mvPoolDefaults[ ATTR_FONT_WORDLINE - ATTR_STARTINDEX ] = new SvxWordLineModeItem(false, ATTR_FONT_WORDLINE );
+ mvPoolDefaults[ ATTR_FONT_RELIEF - ATTR_STARTINDEX ] = new SvxCharReliefItem( FontRelief::NONE, ATTR_FONT_RELIEF );
+ mvPoolDefaults[ ATTR_HYPHENATE - ATTR_STARTINDEX ] = new ScHyphenateCell();
+ mvPoolDefaults[ ATTR_SCRIPTSPACE - ATTR_STARTINDEX ] = new SvxScriptSpaceItem( false, ATTR_SCRIPTSPACE);
+ mvPoolDefaults[ ATTR_HANGPUNCTUATION - ATTR_STARTINDEX ] = new SvxHangingPunctuationItem( false, ATTR_HANGPUNCTUATION);
+ mvPoolDefaults[ ATTR_FORBIDDEN_RULES - ATTR_STARTINDEX ] = new SvxForbiddenRuleItem( false, ATTR_FORBIDDEN_RULES);
+ mvPoolDefaults[ ATTR_HOR_JUSTIFY - ATTR_STARTINDEX ] = new SvxHorJustifyItem( SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY);
+ mvPoolDefaults[ ATTR_HOR_JUSTIFY_METHOD - ATTR_STARTINDEX ] = new SvxJustifyMethodItem( SvxCellJustifyMethod::Auto, ATTR_HOR_JUSTIFY_METHOD);
+ mvPoolDefaults[ ATTR_INDENT - ATTR_STARTINDEX ] = new ScIndentItem( 0 );
+ mvPoolDefaults[ ATTR_VER_JUSTIFY - ATTR_STARTINDEX ] = new SvxVerJustifyItem( SvxCellVerJustify::Standard, ATTR_VER_JUSTIFY);
+ mvPoolDefaults[ ATTR_VER_JUSTIFY_METHOD - ATTR_STARTINDEX ] = new SvxJustifyMethodItem( SvxCellJustifyMethod::Auto, ATTR_VER_JUSTIFY_METHOD);
+ mvPoolDefaults[ ATTR_STACKED - ATTR_STARTINDEX ] = new ScVerticalStackCell(false);
+ mvPoolDefaults[ ATTR_ROTATE_VALUE - ATTR_STARTINDEX ] = new ScRotateValueItem( 0_deg100 );
+ mvPoolDefaults[ ATTR_ROTATE_MODE - ATTR_STARTINDEX ] = new SvxRotateModeItem( SVX_ROTATE_MODE_BOTTOM, ATTR_ROTATE_MODE );
+ mvPoolDefaults[ ATTR_VERTICAL_ASIAN - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_VERTICAL_ASIAN );
+ // The default for the ATTR_WRITINGDIR cell attribute must by SvxFrameDirection::Environment,
+ // so that value is returned when asking for a default cell's attributes.
+ // The value from the page style is set as DefaultHorizontalTextDirection for the EditEngine.
+ mvPoolDefaults[ ATTR_WRITINGDIR - ATTR_STARTINDEX ] = new SvxFrameDirectionItem( SvxFrameDirection::Environment, ATTR_WRITINGDIR );
+ mvPoolDefaults[ ATTR_LINEBREAK - ATTR_STARTINDEX ] = new ScLineBreakCell();
+ mvPoolDefaults[ ATTR_SHRINKTOFIT - ATTR_STARTINDEX ] = new ScShrinkToFitCell();
+ mvPoolDefaults[ ATTR_BORDER_TLBR - ATTR_STARTINDEX ] = new SvxLineItem( ATTR_BORDER_TLBR );
+ mvPoolDefaults[ ATTR_BORDER_BLTR - ATTR_STARTINDEX ] = new SvxLineItem( ATTR_BORDER_BLTR );
+ mvPoolDefaults[ ATTR_MARGIN - ATTR_STARTINDEX ] = new SvxMarginItem( ATTR_MARGIN );
+ mvPoolDefaults[ ATTR_MERGE - ATTR_STARTINDEX ] = new ScMergeAttr;
+ mvPoolDefaults[ ATTR_MERGE_FLAG - ATTR_STARTINDEX ] = new ScMergeFlagAttr;
+ mvPoolDefaults[ ATTR_VALUE_FORMAT - ATTR_STARTINDEX ] = new SfxUInt32Item( ATTR_VALUE_FORMAT, 0 );
+ mvPoolDefaults[ ATTR_LANGUAGE_FORMAT - ATTR_STARTINDEX ] = new SvxLanguageItem( ScGlobal::eLnge, ATTR_LANGUAGE_FORMAT );
+ mvPoolDefaults[ ATTR_BACKGROUND - ATTR_STARTINDEX ] = new SvxBrushItem( COL_TRANSPARENT, ATTR_BACKGROUND );
+ mvPoolDefaults[ ATTR_PROTECTION - ATTR_STARTINDEX ] = new ScProtectionAttr;
+ mvPoolDefaults[ ATTR_BORDER - ATTR_STARTINDEX ] = new SvxBoxItem( ATTR_BORDER );
+ mvPoolDefaults[ ATTR_BORDER_INNER - ATTR_STARTINDEX ] = pGlobalBorderInnerAttr;
+ mvPoolDefaults[ ATTR_SHADOW - ATTR_STARTINDEX ] = new SvxShadowItem( ATTR_SHADOW );
+ mvPoolDefaults[ ATTR_VALIDDATA - ATTR_STARTINDEX ] = new SfxUInt32Item( ATTR_VALIDDATA, 0 );
+ mvPoolDefaults[ ATTR_CONDITIONAL - ATTR_STARTINDEX ] = new ScCondFormatItem;
+ mvPoolDefaults[ ATTR_HYPERLINK - ATTR_STARTINDEX ] = new SfxStringItem( ATTR_HYPERLINK, OUString() ) ;
+
+ // GetRscString only works after ScGlobal::Init (indicated by the EmptyBrushItem)
+ // TODO: Write additional method ScGlobal::IsInit() or somesuch
+ // or detect whether this is the Secondary Pool for a MessagePool
+ if ( ScGlobal::GetEmptyBrushItem() )
+
+ mvPoolDefaults[ ATTR_PATTERN - ATTR_STARTINDEX ] =
+ new ScPatternAttr( SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( *this ),
+ ScResId(STR_STYLENAME_STANDARD) );
+ else
+ mvPoolDefaults[ ATTR_PATTERN - ATTR_STARTINDEX ] =
+ new ScPatternAttr( SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( *this ),
+ STRING_STANDARD ); // FIXME: without name?
+
+ mvPoolDefaults[ ATTR_LRSPACE - ATTR_STARTINDEX ] = new SvxLRSpaceItem( ATTR_LRSPACE );
+ mvPoolDefaults[ ATTR_ULSPACE - ATTR_STARTINDEX ] = new SvxULSpaceItem( ATTR_ULSPACE );
+ mvPoolDefaults[ ATTR_PAGE - ATTR_STARTINDEX ] = new SvxPageItem( ATTR_PAGE );
+ mvPoolDefaults[ ATTR_PAGE_PAPERBIN - ATTR_STARTINDEX ] = new SvxPaperBinItem( ATTR_PAGE_PAPERBIN );
+ mvPoolDefaults[ ATTR_PAGE_SIZE - ATTR_STARTINDEX ] = new SvxSizeItem( ATTR_PAGE_SIZE );
+ mvPoolDefaults[ ATTR_PAGE_HORCENTER - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_HORCENTER );
+ mvPoolDefaults[ ATTR_PAGE_VERCENTER - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_VERCENTER );
+ mvPoolDefaults[ ATTR_PAGE_ON - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_ON, true );
+ mvPoolDefaults[ ATTR_PAGE_DYNAMIC - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_DYNAMIC, true );
+ mvPoolDefaults[ ATTR_PAGE_SHARED - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_SHARED, true );
+ mvPoolDefaults[ ATTR_PAGE_SHARED_FIRST- ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_SHARED_FIRST, true );
+ mvPoolDefaults[ ATTR_PAGE_NOTES - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_NOTES, false );
+ mvPoolDefaults[ ATTR_PAGE_GRID - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_GRID, false );
+ mvPoolDefaults[ ATTR_PAGE_HEADERS - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_HEADERS, false );
+ mvPoolDefaults[ ATTR_PAGE_CHARTS - ATTR_STARTINDEX ] = new ScViewObjectModeItem( ATTR_PAGE_CHARTS );
+ mvPoolDefaults[ ATTR_PAGE_OBJECTS - ATTR_STARTINDEX ] = new ScViewObjectModeItem( ATTR_PAGE_OBJECTS );
+ mvPoolDefaults[ ATTR_PAGE_DRAWINGS - ATTR_STARTINDEX ] = new ScViewObjectModeItem( ATTR_PAGE_DRAWINGS );
+ mvPoolDefaults[ ATTR_PAGE_TOPDOWN - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_TOPDOWN, true );
+ mvPoolDefaults[ ATTR_PAGE_SCALE - ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_PAGE_SCALE, 100 );
+ mvPoolDefaults[ ATTR_PAGE_SCALETOPAGES-ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, 1 );
+ mvPoolDefaults[ ATTR_PAGE_FIRSTPAGENO- ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, 1 );
+ mvPoolDefaults[ ATTR_PAGE_HEADERLEFT - ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_HEADERLEFT );
+ mvPoolDefaults[ ATTR_PAGE_FOOTERLEFT - ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_FOOTERLEFT );
+ mvPoolDefaults[ ATTR_PAGE_HEADERRIGHT- ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_HEADERRIGHT );
+ mvPoolDefaults[ ATTR_PAGE_FOOTERRIGHT- ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_FOOTERRIGHT );
+ mvPoolDefaults[ ATTR_PAGE_HEADERFIRST- ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_HEADERFIRST );
+ mvPoolDefaults[ ATTR_PAGE_FOOTERFIRST- ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_FOOTERFIRST );
+ mvPoolDefaults[ ATTR_PAGE_HEADERSET - ATTR_STARTINDEX ] = new SvxSetItem( ATTR_PAGE_HEADERSET, aSetItemItemSet );
+ mvPoolDefaults[ ATTR_PAGE_FOOTERSET - ATTR_STARTINDEX ] = new SvxSetItem( ATTR_PAGE_FOOTERSET, aSetItemItemSet );
+ mvPoolDefaults[ ATTR_PAGE_FORMULAS - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_FORMULAS, false );
+ mvPoolDefaults[ ATTR_PAGE_NULLVALS - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_NULLVALS, true );
+ mvPoolDefaults[ ATTR_PAGE_SCALETO - ATTR_STARTINDEX ] = new ScPageScaleToItem( 1, 1 );
+ mvPoolDefaults[ ATTR_HIDDEN - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_HIDDEN, false );
+
+ SetDefaults( &mvPoolDefaults );
+}
+
+ScDocumentPool::~ScDocumentPool()
+{
+ Delete();
+ SetSecondaryPool(nullptr);
+
+ for ( sal_uInt16 i=0; i < ATTR_ENDINDEX-ATTR_STARTINDEX+1; i++ )
+ {
+ ClearRefCount( *mvPoolDefaults[i] );
+ delete mvPoolDefaults[i];
+ }
+}
+
+void ScDocumentPool::newItem_Callback(const SfxPoolItem& rItem) const
+{
+ if (ATTR_PATTERN == rItem.Which() && 1 == rItem.GetRefCount())
+ {
+ const_cast<ScDocumentPool*>(this)->mnCurrentMaxKey++;
+ const_cast<ScPatternAttr&>(static_cast<const ScPatternAttr&>(rItem)).SetPAKey(mnCurrentMaxKey);
+ }
+}
+
+bool ScDocumentPool::newItem_UseDirect(const SfxPoolItem& rItem) const
+{
+ // I have evaluated that this is currently needed for ATTR_PATTERN/ScPatternAttr to work,
+ // so this needs to stay at ptr-compare
+ return (ATTR_PATTERN == rItem.Which() && areSfxPoolItemPtrsEqual(&rItem, mvPoolDefaults[ATTR_PATTERN - ATTR_STARTINDEX]));
+}
+
+void ScDocumentPool::StyleDeleted( const ScStyleSheet* pStyle )
+{
+ for (const SfxPoolItem* pItem : GetItemSurrogates( ATTR_PATTERN ))
+ {
+ ScPatternAttr* pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
+ if ( pPattern && pPattern->GetStyleSheet() == pStyle )
+ pPattern->StyleToName();
+ }
+}
+
+void ScDocumentPool::CellStyleCreated( std::u16string_view rName, const ScDocument& rDoc )
+{
+ // If a style was created, don't keep any pattern with its name string in the pool,
+ // because it would compare equal to a pattern with a pointer to the new style.
+ // Calling StyleSheetChanged isn't enough because the pool may still contain items
+ // for undo or clipboard content.
+
+ for (const SfxPoolItem* pItem : GetItemSurrogates( ATTR_PATTERN ))
+ {
+ auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
+ if ( pPattern && pPattern->GetStyleSheet() == nullptr )
+ {
+ const OUString* pStyleName = pPattern->GetStyleName();
+ if ( pStyleName && *pStyleName == rName )
+ pPattern->UpdateStyleSheet(rDoc); // find and store style pointer
+ }
+ }
+}
+
+rtl::Reference<SfxItemPool> ScDocumentPool::Clone() const
+{
+ return new SfxItemPool (*this, true);
+}
+
+static bool lcl_HFPresentation
+(
+ const SfxPoolItem& rItem,
+ MapUnit eCoreMetric,
+ MapUnit ePresentationMetric,
+ OUString& rText,
+ const IntlWrapper& rIntl
+)
+{
+ const SfxItemSet& rSet = static_cast<const SfxSetItem&>(rItem).GetItemSet();
+
+ if ( const SfxBoolItem* pItem = rSet.GetItemIfSet(ATTR_PAGE_ON,false) )
+ {
+ if( !pItem->GetValue() )
+ return false;
+ }
+
+ SfxItemIter aIter( rSet );
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ sal_uInt16 nWhich = pItem->Which();
+
+ OUString aText;
+
+ switch( nWhich )
+ {
+ case ATTR_PAGE_ON:
+ case ATTR_PAGE_DYNAMIC:
+ case ATTR_PAGE_SHARED:
+ case ATTR_PAGE_SHARED_FIRST:
+ break;
+
+ case ATTR_LRSPACE:
+ {
+ const SvxLRSpaceItem& rLRItem = static_cast<const SvxLRSpaceItem&>(*pItem);
+ sal_uInt16 nPropLeftMargin = rLRItem.GetPropLeft();
+ sal_uInt16 nPropRightMargin = rLRItem.GetPropRight();
+ sal_uInt16 nLeftMargin, nRightMargin;
+ tools::Long nTmp;
+ nTmp = rLRItem.GetLeft();
+ nLeftMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ nTmp = rLRItem.GetRight();
+ nRightMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+
+ aText = EditResId(RID_SVXITEMS_LRSPACE_LEFT);
+ if ( 100 != nPropLeftMargin )
+ {
+ aText += unicode::formatPercent(nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ aText += GetMetricText( static_cast<tools::Long>(nLeftMargin),
+ eCoreMetric, ePresentationMetric, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresentationMetric));
+ }
+ aText += cpDelim +
+ // We don't have a nPropFirstLineOffset
+ EditResId(RID_SVXITEMS_LRSPACE_RIGHT);
+ if ( 100 != nPropRightMargin )
+ {
+ aText += unicode::formatPercent(nPropLeftMargin,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ aText += GetMetricText( static_cast<tools::Long>(nRightMargin),
+ eCoreMetric, ePresentationMetric, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresentationMetric));
+ }
+ }
+ break;
+
+ default:
+ pItem->GetPresentation( SfxItemPresentation::Complete, eCoreMetric, ePresentationMetric, aText, rIntl );
+
+ }
+
+ if ( aText.getLength() )
+ {
+ rText += aText + " + ";
+ }
+ }
+
+ rText = comphelper::string::stripEnd(rText, ' ');
+ rText = comphelper::string::stripEnd(rText, '+');
+ rText = comphelper::string::stripEnd(rText, ' ');
+ return true;
+}
+
+bool ScDocumentPool::GetPresentation(
+ const SfxPoolItem& rItem,
+ MapUnit ePresentationMetric,
+ OUString& rText,
+ const IntlWrapper& rIntl ) const
+{
+ sal_uInt16 nW = rItem.Which();
+ OUString aStrYes ( ScResId(STR_YES) );
+ OUString aStrNo ( ScResId(STR_NO) );
+ OUString aStrSep(": ");
+
+ bool ePresentationRet = true;
+ switch( nW )
+ {
+ case ATTR_PAGE_TOPDOWN:
+ rText = ScResId(STR_SCATTR_PAGE_PRINTDIR) + aStrSep;
+ rText += static_cast<const SfxBoolItem&>(rItem).GetValue() ?
+ ScResId(STR_SCATTR_PAGE_TOPDOWN) :
+ ScResId(STR_SCATTR_PAGE_LEFTRIGHT) ;
+ break;
+
+ case ATTR_PAGE_HEADERS:
+ rText = ScResId(STR_SCATTR_PAGE_HEADERS) + aStrSep;
+ rText += static_cast<const SfxBoolItem&>(rItem).GetValue() ? aStrYes : aStrNo ;
+ break;
+
+ case ATTR_PAGE_NULLVALS:
+ rText = ScResId(STR_SCATTR_PAGE_NULLVALS) + aStrSep;
+ rText += static_cast<const SfxBoolItem&>(rItem).GetValue() ? aStrYes : aStrNo ;
+ break;
+
+ case ATTR_PAGE_FORMULAS:
+ rText = ScResId(STR_SCATTR_PAGE_FORMULAS) + aStrSep;
+ rText += static_cast<const SfxBoolItem&>(rItem).GetValue() ? aStrYes : aStrNo ;
+ break;
+
+ case ATTR_PAGE_NOTES:
+ rText = ScResId(STR_SCATTR_PAGE_NOTES) + aStrSep;
+ rText += static_cast<const SfxBoolItem&>(rItem).GetValue() ? aStrYes : aStrNo ;
+ break;
+
+ case ATTR_PAGE_GRID:
+ rText = ScResId(STR_SCATTR_PAGE_GRID) + aStrSep;
+ rText += static_cast<const SfxBoolItem&>(rItem).GetValue() ? aStrYes : aStrNo ;
+ break;
+
+ case ATTR_PAGE_SCALETOPAGES:
+ {
+ sal_uInt16 nPagNo = static_cast<const SfxUInt16Item&>(rItem).GetValue();
+
+ if( nPagNo )
+ {
+ rText = ScResId( STR_SCATTR_PAGE_SCALETOPAGES ) + aStrSep;
+ OUString aPages(ScResId(STR_SCATTR_PAGE_SCALE_PAGES, nPagNo));
+ aPages = aPages.replaceFirst( "%1", OUString::number( nPagNo ) );
+ rText += aPages;
+ }
+ else
+ {
+ ePresentationRet = false;
+ }
+ }
+ break;
+
+ case ATTR_PAGE_FIRSTPAGENO:
+ {
+ sal_uInt16 nPagNo = static_cast<const SfxUInt16Item&>(rItem).GetValue();
+
+ if( nPagNo )
+ {
+ rText = ScResId(STR_SCATTR_PAGE_FIRSTPAGENO) + aStrSep;
+ rText += OUString::number( nPagNo );
+ }
+ else
+ {
+ ePresentationRet = false;
+ }
+ }
+ break;
+
+ case ATTR_PAGE_SCALE:
+ {
+ sal_uInt16 nPercent = static_cast<const SfxUInt16Item &>(rItem).GetValue();
+
+ if( nPercent )
+ {
+ rText = ScResId(STR_SCATTR_PAGE_SCALE) + aStrSep;
+ rText += unicode::formatPercent(nPercent,
+ Application::GetSettings().GetUILanguageTag());
+ }
+ else
+ {
+ ePresentationRet = false;
+ }
+ }
+ break;
+
+ case ATTR_PAGE_HEADERSET:
+ {
+ OUString aBuffer;
+
+ if( lcl_HFPresentation( rItem, GetMetric( nW ), ePresentationMetric, aBuffer, rIntl ) )
+ {
+ rText = ScResId(STR_HEADER) + " ( " + aBuffer + " ) ";
+ }
+ }
+ break;
+
+ case ATTR_PAGE_FOOTERSET:
+ {
+ OUString aBuffer;
+
+ if( lcl_HFPresentation( rItem, GetMetric( nW ), ePresentationMetric, aBuffer, rIntl ) )
+ {
+ rText = ScResId(STR_FOOTER) + " ( " + aBuffer + " ) ";
+ }
+ }
+ break;
+
+ default:
+ ePresentationRet = rItem.GetPresentation( SfxItemPresentation::Complete, GetMetric( nW ), ePresentationMetric, rText, rIntl );
+ break;
+ }
+
+ return ePresentationRet;
+}
+
+MapUnit ScDocumentPool::GetMetric( sal_uInt16 nWhich ) const
+{
+ // Own attributes in Twips, everything else in 1/100 mm
+ if ( nWhich >= ATTR_STARTINDEX && nWhich <= ATTR_ENDINDEX )
+ return MapUnit::MapTwip;
+ else
+ return MapUnit::Map100thMM;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
new file mode 100644
index 0000000000..3b9b24cfec
--- /dev/null
+++ b/sc/source/core/data/documen2.cxx
@@ -0,0 +1,1515 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scextopt.hxx>
+#include <autonamecache.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <svx/xtable.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <svl/asiancfg.hxx>
+#include <vcl/virdev.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <tools/urlobj.hxx>
+#include <rtl/crc.h>
+#include <basic/basmgr.hxx>
+#include <comphelper/threadpool.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <unotools/configmgr.hxx>
+
+#include <scmod.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <patattr.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <chartlock.hxx>
+#include <rechead.hxx>
+#include <global.hxx>
+#include <bcaslot.hxx>
+#include <adiasync.hxx>
+#include <addinlis.hxx>
+#include <chartlis.hxx>
+#include <markdata.hxx>
+#include <validat.hxx>
+#include <detdata.hxx>
+#include <defaultsoptions.hxx>
+#include <ddelink.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <editutil.hxx>
+#include <hints.hxx>
+#include <dpobject.hxx>
+#include <scrdata.hxx>
+#include <poolhelp.hxx>
+#include <unoreflist.hxx>
+#include <listenercalls.hxx>
+#include <recursionhelper.hxx>
+#include <lookupcache.hxx>
+#include <rangecache.hxx>
+#include <externalrefmgr.hxx>
+#include <viewdata.hxx>
+#include <viewutil.hxx>
+#include <tabprotection.hxx>
+#include <formulaparserpool.hxx>
+#include <clipparam.hxx>
+#include <macromgr.hxx>
+#include <formulacell.hxx>
+#include <clipcontext.hxx>
+#include <refupdatecontext.hxx>
+#include <refreshtimerprotector.hxx>
+#include <scopetools.hxx>
+#include <documentlinkmgr.hxx>
+#include <interpre.hxx>
+#include <tokenstringcontext.hxx>
+#include <docsh.hxx>
+#include <clipoptions.hxx>
+#include <listenercontext.hxx>
+#include <datamapper.hxx>
+#include <drwlayer.hxx>
+#include <sharedstringpoolpurge.hxx>
+#include <dociter.hxx>
+#include <config_features.h>
+
+using namespace com::sun::star;
+
+const sal_uInt16 ScDocument::nSrcVer = SC_CURRENT_VERSION;
+
+ScSheetLimits ScSheetLimits::CreateDefault()
+{
+#if HAVE_FEATURE_JUMBO_SHEETS
+ bool jumboSheets = false;
+ if( SC_MOD())
+ jumboSheets = SC_MOD()->GetDefaultsOptions().GetInitJumboSheets();
+ else
+ assert( getenv("LO_TESTNAME") != nullptr ); // in unittests
+ if (jumboSheets)
+ return ScSheetLimits(MAXCOL_JUMBO, MAXROW_JUMBO);
+ else
+#endif
+ return ScSheetLimits(MAXCOL, MAXROW);
+}
+
+ScDocument::ScDocument( ScDocumentMode eMode, ScDocShell* pDocShell ) :
+ mpCellStringPool(std::make_shared<svl::SharedStringPool>(ScGlobal::getCharClass())),
+ mpDocLinkMgr(new sc::DocumentLinkManager(pDocShell)),
+ mbFormulaGroupCxtBlockDiscard(false),
+ maCalcConfig( ScInterpreter::GetGlobalConfig()),
+ mpUndoManager( nullptr ),
+ mpShell( pDocShell ),
+ mpPrinter( nullptr ),
+ mpVirtualDevice_100th_mm( nullptr ),
+ pFormatExchangeList( nullptr ),
+ mxSheetLimits(new ScSheetLimits(ScSheetLimits::CreateDefault())),
+ pFormulaTree( nullptr ),
+ pEOFormulaTree( nullptr ),
+ pFormulaTrack( nullptr ),
+ pEOFormulaTrack( nullptr ),
+ pPreviewCellStyle( nullptr ),
+ maPreviewSelection(*mxSheetLimits),
+ nUnoObjectId( 0 ),
+ nRangeOverflowType( 0 ),
+ aCurTextWidthCalcPos(MaxCol(),0,0),
+ aTrackIdle("sc ScDocument Track Idle"),
+ nFormulaCodeInTree(0),
+ nXMLImportedFormulaCount( 0 ),
+ nInterpretLevel(0),
+ nMacroInterpretLevel(0),
+ nInterpreterTableOpLevel(0),
+ maInterpreterContext( *this, nullptr ),
+ mxScSortedRangeCache(new ScSortedRangeCacheMap),
+ nFormulaTrackCount(0),
+ eHardRecalcState(HardRecalcState::OFF),
+ nVisibleTab( 0 ),
+ nPosLeft( 0 ),
+ nPosTop( 0 ),
+ eLinkMode(LM_UNKNOWN),
+ bAutoCalc( eMode == SCDOCMODE_DOCUMENT || eMode == SCDOCMODE_FUNCTIONACCESS ),
+ bAutoCalcShellDisabled( false ),
+ bForcedFormulaPending( false ),
+ bCalculatingFormulaTree( false ),
+ bIsClip( eMode == SCDOCMODE_CLIP ),
+ bIsUndo( eMode == SCDOCMODE_UNDO ),
+ bIsFunctionAccess( eMode == SCDOCMODE_FUNCTIONACCESS ),
+ bIsVisible( false ),
+ bIsEmbedded( false ),
+ bInsertingFromOtherDoc( false ),
+ bLoadingMedium( false ),
+ bImportingXML( false ),
+ bCalcingAfterLoad( false ),
+ bNoListening( false ),
+ mbIdleEnabled(true),
+ bInLinkUpdate( false ),
+ bChartListenerCollectionNeedsUpdate( false ),
+ bHasForcedFormulas( false ),
+ bInDtorClear( false ),
+ bExpandRefs( false ),
+ bDetectiveDirty( false ),
+ bDelayedDeletingBroadcasters( false ),
+ bLinkFormulaNeedingCheck( false ),
+ nAsianCompression(CharCompressType::Invalid),
+ nAsianKerning(SC_ASIANKERNING_INVALID),
+ bPastingDrawFromOtherDoc( false ),
+ nInDdeLinkUpdate( 0 ),
+ bInUnoBroadcast( false ),
+ bInUnoListenerCall( false ),
+ nAdjustHeightLock(0),
+ eGrammar( formula::FormulaGrammar::GRAM_NATIVE ),
+ bStyleSheetUsageInvalid( true ),
+ mbUndoEnabled( true ),
+ mbExecuteLinkEnabled( true ),
+ mbChangeReadOnlyEnabled( false ),
+ mbStreamValidLocked( false ),
+ mbUserInteractionEnabled(true),
+ mnNamedRangesLockCount(0),
+ mbEmbedFonts(false),
+ mbEmbedUsedFontsOnly(false),
+ mbEmbedFontScriptLatin(true),
+ mbEmbedFontScriptAsian(true),
+ mbEmbedFontScriptComplex(true),
+ mnImagePreferredDPI(0),
+ mbTrackFormulasPending(false),
+ mbFinalTrackFormulas(false),
+ mbDocShellRecalc(false),
+ mbLayoutStrings(false),
+ mnMutationGuardFlags(0)
+{
+ maPreviewSelection = { *mxSheetLimits };
+ aCurTextWidthCalcPos = { MaxCol(), 0, 0 };
+
+ SetStorageGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT);
+
+ eSrcSet = osl_getThreadTextEncoding();
+
+ /* TODO: for SCDOCMODE_FUNCTIONACCESS it might not even be necessary to
+ * have all of these available. */
+ if ( eMode == SCDOCMODE_DOCUMENT || eMode == SCDOCMODE_FUNCTIONACCESS )
+ {
+ mxPoolHelper = new ScPoolHelper( *this );
+ if (!utl::ConfigManager::IsFuzzing()) //just too slow
+ pBASM.reset( new ScBroadcastAreaSlotMachine( this ) );
+ pChartListenerCollection.reset( new ScChartListenerCollection( *this ) );
+ pRefreshTimerControl.reset( new ScRefreshTimerControl );
+ }
+ else
+ {
+ pChartListenerCollection = nullptr;
+ }
+ pDBCollection.reset( new ScDBCollection(*this) );
+ pSelectionAttr = nullptr;
+ apTemporaryChartLock.reset( new ScTemporaryChartLock(this) );
+ xColNameRanges = new ScRangePairList;
+ xRowNameRanges = new ScRangePairList;
+ ImplCreateOptions();
+ // languages for a visible document are set by docshell later (from options)
+ SetLanguage( ScGlobal::eLnge, ScGlobal::eLnge, ScGlobal::eLnge );
+
+ aTrackIdle.SetInvokeHandler( LINK( this, ScDocument, TrackTimeHdl ) );
+}
+
+sfx2::LinkManager* ScDocument::GetLinkManager()
+{
+ return GetDocLinkManager().getLinkManager();
+}
+
+const sfx2::LinkManager* ScDocument::GetLinkManager() const
+{
+ return GetDocLinkManager().getExistingLinkManager();
+}
+
+sc::DocumentLinkManager& ScDocument::GetDocLinkManager()
+{
+ return *mpDocLinkMgr;
+}
+
+const sc::DocumentLinkManager& ScDocument::GetDocLinkManager() const
+{
+ return const_cast<ScDocument*>(this)->GetDocLinkManager();
+}
+
+void ScDocument::SetStorageGrammar( formula::FormulaGrammar::Grammar eGram )
+{
+ OSL_PRECOND(
+ eGram == formula::FormulaGrammar::GRAM_ODFF ||
+ eGram == formula::FormulaGrammar::GRAM_PODF,
+ "ScDocument::SetStorageGrammar: wrong storage grammar");
+
+ eStorageGrammar = eGram;
+}
+
+void ScDocument::SetDocVisible( bool bSet )
+{
+ // called from view ctor - only for a visible document,
+ // each new sheet's RTL flag is initialized from the locale
+ bIsVisible = bSet;
+}
+
+sal_uInt32 ScDocument::GetDocumentID() const
+{
+ const ScDocument* pThis = this;
+ sal_uInt32 nCrc = rtl_crc32( 0, &pThis, sizeof(ScDocument*) );
+ // the this pointer only might not be sufficient
+ nCrc = rtl_crc32( nCrc, &mpShell, sizeof(SfxObjectShell*) );
+ return nCrc;
+}
+
+void ScDocument::StartChangeTracking()
+{
+ if (!pChangeTrack)
+ {
+ pChangeTrack.reset( new ScChangeTrack( *this ) );
+ if (mpShell)
+ mpShell->SetModified();
+ }
+}
+
+void ScDocument::EndChangeTracking()
+{
+ if (pChangeTrack && mpShell)
+ mpShell->SetModified();
+ pChangeTrack.reset();
+}
+
+void ScDocument::SetChangeTrack( std::unique_ptr<ScChangeTrack> pTrack )
+{
+ OSL_ENSURE( &pTrack->GetDocument() == this, "SetChangeTrack: different documents" );
+ if ( !pTrack || pTrack == pChangeTrack || &pTrack->GetDocument() != this )
+ return ;
+ EndChangeTracking();
+ pChangeTrack = std::move(pTrack);
+}
+
+IMPL_LINK_NOARG(ScDocument, TrackTimeHdl, Timer *, void)
+{
+ if ( ScDdeLink::IsInUpdate() ) // do not nest
+ {
+ aTrackIdle.Start(); // try again later
+ }
+ else if (mpShell) // execute
+ {
+ TrackFormulas();
+ mpShell->Broadcast( SfxHint( SfxHintId::ScDataChanged ) );
+
+ if (!mpShell->IsModified())
+ {
+ mpShell->SetModified();
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_SAVEDOC );
+ pBindings->Invalidate( SID_DOC_MODIFIED );
+ }
+ }
+ }
+}
+
+void ScDocument::SetExpandRefs( bool bVal )
+{
+ bExpandRefs = bVal;
+}
+
+void ScDocument::StartTrackTimer()
+{
+ if (!aTrackIdle.IsActive()) // do not postpone for forever
+ aTrackIdle.Start();
+}
+
+void ScDocument::ClosingClipboardSource()
+{
+ if (!bIsClip)
+ return;
+
+ ForgetNoteCaptions( ScRangeList( ScRange( 0,0,0, MaxCol(), MaxRow(), GetTableCount()-1)), true);
+}
+
+ScDocument::~ScDocument()
+{
+ OSL_PRECOND( !bInLinkUpdate, "bInLinkUpdate in dtor" );
+
+ // Join any pending(recalc) threads in global threadpool
+ comphelper::ThreadPool::getSharedOptimalPool().joinThreadsIfIdle();
+
+ bInDtorClear = true;
+
+ // first of all disable all refresh timers by deleting the control
+ if ( pRefreshTimerControl )
+ { // To be sure there isn't anything running do it with a protector,
+ // this ensures also that nothing needs the control anymore.
+ ScRefreshTimerProtector aProt( GetRefreshTimerControlAddress() );
+ pRefreshTimerControl.reset();
+ }
+
+ mxFormulaParserPool.reset();
+ // Destroy the external ref mgr instance here because it has a timer
+ // which needs to be stopped before the app closes.
+ pExternalRefMgr.reset();
+
+ ScAddInAsync::RemoveDocument( this );
+ ScAddInListener::RemoveDocument( this );
+ pChartListenerCollection.reset(); // before pBASM because of potential Listener!
+
+ ClearLookupCaches(); // before pBASM because of listeners
+
+ // destroy BroadcastAreas first to avoid un-needed Single-EndListenings of Formula-Cells
+ pBASM.reset(); // BroadcastAreaSlotMachine
+
+ pUnoBroadcaster.reset(); // broadcasts SfxHintId::Dying again
+
+ pUnoRefUndoList.reset();
+ pUnoListenerCalls.reset();
+
+ Clear( true ); // true = from destructor (needed for SdrModel::ClearModel)
+
+ pValidationList.reset();
+ pRangeName.reset();
+ pDBCollection.reset();
+ pSelectionAttr.reset();
+ apTemporaryChartLock.reset();
+ mpDrawLayer.reset();
+ mpPrinter.disposeAndClear();
+ ImplDeleteOptions();
+ pConsolidateDlgData.reset();
+ pClipData.reset();
+ pDetOpList.reset(); // also deletes entries
+ pChangeTrack.reset();
+ mpEditEngine.reset();
+ mpNoteEngine.reset();
+ pChangeViewSettings.reset(); // and delete
+ mpVirtualDevice_100th_mm.disposeAndClear();
+
+ pDPCollection.reset();
+ mpAnonymousDBData.reset();
+
+ // delete the EditEngine before destroying the mxPoolHelper
+ pCacheFieldEditEngine.reset();
+
+ if ( mxPoolHelper.is() && !bIsClip && !bIsUndo)
+ mxPoolHelper->SourceDocumentGone();
+ mxPoolHelper.clear();
+
+ pScriptTypeData.reset();
+ maNonThreaded.xRecursionHelper.reset();
+ assert(!maThreadSpecific.xRecursionHelper);
+
+ pPreviewFont.reset();
+ SAL_WARN_IF( pAutoNameCache, "sc.core", "AutoNameCache still set in dtor" );
+
+ mpFormulaGroupCxt.reset();
+ // Purge unused items if the string pool will be still used (e.g. by undo history).
+ if(mpCellStringPool.use_count() > 1)
+ {
+ // Calling purge() may be somewhat expensive with large documents, so
+ // try to delay and compress it for temporary documents.
+ if(IsClipOrUndo())
+ ScGlobal::GetSharedStringPoolPurge().delayedPurge(mpCellStringPool);
+ else
+ mpCellStringPool->purge();
+ }
+ mpCellStringPool.reset();
+
+ assert( pDelayedFormulaGrouping == nullptr );
+ assert( pDelayedStartListeningFormulaCells.empty());
+}
+
+void ScDocument::InitClipPtrs( ScDocument* pSourceDoc )
+{
+ OSL_ENSURE(bIsClip, "InitClipPtrs and not bIsClip");
+
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+
+ pValidationList.reset();
+
+ Clear();
+
+ SharePooledResources(pSourceDoc);
+
+ // conditional Formats / validations
+ // TODO: Copy Templates?
+ const ScValidationDataList* pSourceValid = pSourceDoc->pValidationList.get();
+ if ( pSourceValid )
+ pValidationList.reset(new ScValidationDataList(*this, *pSourceValid));
+
+ // store Links in Stream
+ pClipData.reset();
+ if (pSourceDoc->GetDocLinkManager().hasDdeLinks())
+ {
+ pClipData.reset( new SvMemoryStream );
+ pSourceDoc->SaveDdeLinks(*pClipData);
+ }
+
+ // Options pointers exist (ImplCreateOptions) for any document.
+ // Must be copied for correct results in OLE objects (#i42666#).
+ SetDocOptions( pSourceDoc->GetDocOptions() );
+ SetViewOptions( pSourceDoc->GetViewOptions() );
+}
+
+SvNumberFormatter* ScDocument::GetFormatTable() const
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ return mxPoolHelper->GetFormTable();
+}
+
+SfxItemPool* ScDocument::GetEditPool() const
+{
+ return mxPoolHelper->GetEditPool();
+}
+
+SfxItemPool* ScDocument::GetEnginePool() const
+{
+ return mxPoolHelper->GetEnginePool();
+}
+
+ScFieldEditEngine& ScDocument::GetEditEngine()
+{
+ if ( !mpEditEngine )
+ {
+ mpEditEngine.reset( new ScFieldEditEngine(this, GetEnginePool(), GetEditPool()) );
+ mpEditEngine->SetUpdateLayout( false );
+ mpEditEngine->EnableUndo( false );
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ ApplyAsianEditSettings( *mpEditEngine );
+ }
+ return *mpEditEngine;
+}
+
+ScNoteEditEngine& ScDocument::GetNoteEngine()
+{
+ if ( !mpNoteEngine )
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpNoteEngine.reset( new ScNoteEditEngine( GetEnginePool(), GetEditPool() ) );
+ mpNoteEngine->SetUpdateLayout( false );
+ mpNoteEngine->EnableUndo( false );
+ mpNoteEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ ApplyAsianEditSettings( *mpNoteEngine );
+ const SfxItemSet& rItemSet = GetDefPattern()->GetItemSet();
+ SfxItemSet aEEItemSet( mpNoteEngine->GetEmptyItemSet() );
+ ScPatternAttr::FillToEditItemSet( aEEItemSet, rItemSet );
+ mpNoteEngine->SetDefaults( std::move(aEEItemSet) ); // edit engine takes ownership
+ }
+ return *mpNoteEngine;
+}
+
+std::unique_ptr<EditTextObject> ScDocument::CreateSharedStringTextObject( const svl::SharedString& rSS )
+{
+ /* TODO: Add shared string support to the edit engine to make this process
+ * simpler. */
+ ScFieldEditEngine& rEngine = GetEditEngine();
+ rEngine.SetTextCurrentDefaults( rSS.getString());
+ std::unique_ptr<EditTextObject> pObj( rEngine.CreateTextObject());
+ pObj->NormalizeString( GetSharedStringPool());
+ return pObj;
+}
+
+void ScDocument::ResetClip( ScDocument* pSourceDoc, const ScMarkData* pMarks )
+{
+ if (bIsClip)
+ {
+ InitClipPtrs(pSourceDoc);
+
+ for (SCTAB i = 0; i < pSourceDoc->GetTableCount(); i++)
+ if (pSourceDoc->maTabs[i])
+ if (!pMarks || pMarks->GetTableSelect(i))
+ {
+ OUString aString = pSourceDoc->maTabs[i]->GetName();
+ if (i < GetTableCount())
+ {
+ maTabs[i].reset( new ScTable(*this, i, aString) );
+
+ }
+ else
+ {
+ if (i > GetTableCount())
+ {
+ maTabs.resize(i);
+ }
+ maTabs.emplace_back(new ScTable(*this, i, aString));
+ }
+ maTabs[i]->SetLayoutRTL( pSourceDoc->maTabs[i]->IsLayoutRTL() );
+ }
+ }
+ else
+ {
+ OSL_FAIL("ResetClip");
+ }
+}
+
+void ScDocument::ResetClip( ScDocument* pSourceDoc, SCTAB nTab )
+{
+ if (bIsClip)
+ {
+ InitClipPtrs(pSourceDoc);
+ if (nTab >= GetTableCount())
+ {
+ maTabs.resize(nTab+1);
+ }
+ maTabs[nTab].reset( new ScTable(*this, nTab, "baeh") );
+ if (nTab < pSourceDoc->GetTableCount() && pSourceDoc->maTabs[nTab])
+ maTabs[nTab]->SetLayoutRTL( pSourceDoc->maTabs[nTab]->IsLayoutRTL() );
+ }
+ else
+ {
+ OSL_FAIL("ResetClip");
+ }
+}
+
+void ScDocument::EnsureTable( SCTAB nTab )
+{
+ bool bExtras = !bIsUndo; // Column-Widths, Row-Heights, Flags
+ if (nTab >= GetTableCount())
+ maTabs.resize(nTab+1);
+
+ if (!maTabs[nTab])
+ maTabs[nTab].reset( new ScTable(*this, nTab, "temp", bExtras, bExtras) );
+}
+
+ScRefCellValue ScDocument::GetRefCellValue( const ScAddress& rPos )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetRefCellValue(rPos.Col(), rPos.Row());
+ return ScRefCellValue(); // empty
+}
+
+ScRefCellValue ScDocument::GetRefCellValue( const ScAddress& rPos, sc::ColumnBlockPosition& rBlockPos )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetRefCellValue(rPos.Col(), rPos.Row(), rBlockPos);
+ return ScRefCellValue(); // empty
+}
+
+svl::SharedStringPool& ScDocument::GetSharedStringPool()
+{
+ return *mpCellStringPool;
+}
+
+const svl::SharedStringPool& ScDocument::GetSharedStringPool() const
+{
+ return *mpCellStringPool;
+}
+
+bool ScDocument::GetPrintArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow,
+ bool bNotes) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ bool bAny = pTable->GetPrintArea( rEndCol, rEndRow, bNotes, /*bCalcHiddens*/false);
+ if (mpDrawLayer)
+ {
+ ScRange aDrawRange(0,0,nTab, MaxCol(),MaxRow(),nTab);
+ if (DrawGetPrintArea( aDrawRange, true, true ))
+ {
+ if (aDrawRange.aEnd.Col()>rEndCol) rEndCol=aDrawRange.aEnd.Col();
+ if (aDrawRange.aEnd.Row()>rEndRow) rEndRow=aDrawRange.aEnd.Row();
+ bAny = true;
+ }
+ }
+ return bAny;
+ }
+
+ rEndCol = 0;
+ rEndRow = 0;
+ return false;
+}
+
+bool ScDocument::GetPrintAreaHor( SCTAB nTab, SCROW nStartRow, SCROW nEndRow,
+ SCCOL& rEndCol ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ bool bAny = pTable->GetPrintAreaHor( nStartRow, nEndRow, rEndCol );
+ if (mpDrawLayer)
+ {
+ ScRange aDrawRange(0,nStartRow,nTab, MaxCol(),nEndRow,nTab);
+ if (DrawGetPrintArea( aDrawRange, true, false ))
+ {
+ if (aDrawRange.aEnd.Col()>rEndCol) rEndCol=aDrawRange.aEnd.Col();
+ bAny = true;
+ }
+ }
+ return bAny;
+ }
+
+ rEndCol = 0;
+ return false;
+}
+
+bool ScDocument::GetPrintAreaVer( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol,
+ SCROW& rEndRow, bool bNotes ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ bool bAny = pTable->GetPrintAreaVer( nStartCol, nEndCol, rEndRow, bNotes );
+ if (mpDrawLayer)
+ {
+ ScRange aDrawRange(nStartCol,0,nTab, nEndCol,MaxRow(),nTab);
+ if (DrawGetPrintArea( aDrawRange, false, true ))
+ {
+ if (aDrawRange.aEnd.Row()>rEndRow) rEndRow=aDrawRange.aEnd.Row();
+ bAny = true;
+ }
+ }
+ return bAny;
+ }
+
+ rEndRow = 0;
+ return false;
+}
+
+bool ScDocument::GetDataStart( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ bool bAny = pTable->GetDataStart( rStartCol, rStartRow );
+ if (mpDrawLayer)
+ {
+ ScRange aDrawRange(0,0,nTab, MaxCol(),MaxRow(),nTab);
+ if (DrawGetPrintArea( aDrawRange, true, true ))
+ {
+ if (aDrawRange.aStart.Col()<rStartCol) rStartCol=aDrawRange.aStart.Col();
+ if (aDrawRange.aStart.Row()<rStartRow) rStartRow=aDrawRange.aStart.Row();
+ bAny = true;
+ }
+ }
+ return bAny;
+ }
+
+ rStartCol = 0;
+ rStartRow = 0;
+ return false;
+}
+
+void ScDocument::GetTiledRenderingArea(SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow) const
+{
+ bool bHasPrintArea = GetCellArea(nTab, rEndCol, rEndRow);
+
+ // we need some reasonable minimal document size
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ {
+ if (!bHasPrintArea)
+ {
+ rEndCol = 20;
+ rEndRow = 50;
+ }
+ else
+ {
+ rEndCol += 20;
+ rEndRow += 50;
+ }
+ }
+ else if (!bHasPrintArea)
+ {
+ rEndCol = pViewData->GetMaxTiledCol();
+ rEndRow = pViewData->GetMaxTiledRow();
+ }
+ else
+ {
+ rEndCol = std::max(rEndCol, pViewData->GetMaxTiledCol());
+ rEndRow = std::max(rEndRow, pViewData->GetMaxTiledRow());
+ }
+}
+
+bool ScDocument::MoveTab( SCTAB nOldPos, SCTAB nNewPos, ScProgress* pProgress )
+{
+ if (nOldPos == nNewPos)
+ return false;
+
+ SCTAB nTabCount = GetTableCount();
+ if(nTabCount < 2)
+ return false;
+
+ bool bValid = false;
+ if (ValidTab(nOldPos) && nOldPos < nTabCount )
+ {
+ if (maTabs[nOldPos])
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ SetNoListening( true );
+ if (nNewPos == SC_TAB_APPEND || nNewPos >= nTabCount)
+ nNewPos = nTabCount-1;
+
+ // Update Reference
+ // TODO: combine with UpdateReference!
+
+ sc::RefUpdateMoveTabContext aCxt( *this, nOldPos, nNewPos);
+
+ SCTAB nDz = nNewPos - nOldPos;
+ ScRange aSourceRange( 0,0,nOldPos, MaxCol(),MaxRow(),nOldPos );
+ if (pRangeName)
+ pRangeName->UpdateMoveTab(aCxt);
+
+ pDBCollection->UpdateMoveTab( nOldPos, nNewPos );
+ xColNameRanges->UpdateReference( URM_REORDER, this, aSourceRange, 0,0,nDz );
+ xRowNameRanges->UpdateReference( URM_REORDER, this, aSourceRange, 0,0,nDz );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_REORDER, aSourceRange, 0,0,nDz );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_REORDER, aSourceRange, 0,0,nDz );
+ UpdateChartRef( URM_REORDER,
+ 0,0,nOldPos, MaxCol(),MaxRow(),nOldPos, 0,0,nDz );
+ UpdateRefAreaLinks( URM_REORDER, aSourceRange, 0,0,nDz );
+ if ( pValidationList )
+ pValidationList->UpdateMoveTab(aCxt);
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_REORDER,
+ aSourceRange, 0,0,nDz ) );
+
+ ScTableUniquePtr pSaveTab = std::move(maTabs[nOldPos]);
+ maTabs.erase(maTabs.begin()+nOldPos);
+ maTabs.insert(maTabs.begin()+nNewPos, std::move(pSaveTab));
+ for (SCTAB i = 0; i < nTabCount; i++)
+ if (maTabs[i])
+ maTabs[i]->UpdateMoveTab(aCxt, i, pProgress);
+ for (auto& rxTab : maTabs)
+ if (rxTab)
+ rxTab->UpdateCompile();
+ SetNoListening( false );
+ StartAllListeners();
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+
+ if (mpDrawLayer)
+ mpDrawLayer->ScMovePage( static_cast<sal_uInt16>(nOldPos), static_cast<sal_uInt16>(nNewPos) );
+
+ bValid = true;
+ }
+ }
+ return bValid;
+}
+
+bool ScDocument::CopyTab( SCTAB nOldPos, SCTAB nNewPos, const ScMarkData* pOnlyMarked )
+{
+ if (SC_TAB_APPEND == nNewPos || nNewPos >= GetTableCount())
+ nNewPos = GetTableCount();
+ OUString aName;
+ GetName(nOldPos, aName);
+
+ // check first if Prefix is valid; if not, then only avoid duplicates
+ bool bPrefix = ValidTabName( aName );
+ OSL_ENSURE(bPrefix, "invalid table name");
+ SCTAB nDummy;
+
+ CreateValidTabName(aName);
+
+ bool bValid;
+ if (bPrefix)
+ bValid = ValidNewTabName(aName);
+ else
+ bValid = !GetTable( aName, nDummy );
+
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ sc::RefUpdateInsertTabContext aCxt( *this, nNewPos, 1);
+
+ if (bValid)
+ {
+ if (nNewPos >= GetTableCount())
+ {
+ nNewPos = GetTableCount();
+ maTabs.emplace_back(new ScTable(*this, nNewPos, aName));
+ }
+ else
+ {
+ if (ValidTab(nNewPos) && nNewPos < GetTableCount())
+ {
+ SetNoListening( true );
+
+ ScRange aRange( 0,0,nNewPos, MaxCol(),MaxRow(),MAXTAB );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
+ if (pRangeName)
+ pRangeName->UpdateInsertTab(aCxt);
+
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nNewPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 );
+ UpdateChartRef( URM_INSDEL, 0,0,nNewPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );
+
+ for (TableContainer::iterator it = maTabs.begin(); it != maTabs.end(); ++it)
+ if (*it && it != (maTabs.begin() + nOldPos))
+ (*it)->UpdateInsertTab(aCxt);
+ if (nNewPos <= nOldPos)
+ nOldPos++;
+ maTabs.emplace(maTabs.begin() + nNewPos, new ScTable(*this, nNewPos, aName));
+ bValid = true;
+ for (TableContainer::iterator it = maTabs.begin(); it != maTabs.end(); ++it)
+ if (*it && it != maTabs.begin()+nOldPos && it != maTabs.begin() + nNewPos)
+ (*it)->UpdateCompile();
+ SetNoListening( false );
+ sc::StartListeningContext aSLCxt(*this);
+ for (TableContainer::iterator it = maTabs.begin(); it != maTabs.end(); ++it)
+ if (*it && it != maTabs.begin()+nOldPos && it != maTabs.begin()+nNewPos)
+ (*it)->StartListeners(aSLCxt, true);
+
+ if (pValidationList)
+ pValidationList->UpdateInsertTab(aCxt);
+ }
+ else
+ bValid = false;
+ }
+ }
+
+ if (bValid)
+ {
+ SetNoListening( true ); // not yet at CopyToTable/Insert
+
+ const bool bGlobalNamesToLocal = true;
+ const SCTAB nRealOldPos = (nNewPos < nOldPos) ? nOldPos - 1 : nOldPos;
+ const ScRangeName* pNames = GetRangeName( nOldPos);
+ if (pNames)
+ pNames->CopyUsedNames( nOldPos, nRealOldPos, nNewPos, *this, *this, bGlobalNamesToLocal);
+ GetRangeName()->CopyUsedNames( -1, nRealOldPos, nNewPos, *this, *this, bGlobalNamesToLocal);
+
+ sc::CopyToDocContext aCopyDocCxt(*this);
+ pDBCollection->CopyToTable(nOldPos, nNewPos);
+ maTabs[nOldPos]->CopyToTable(aCopyDocCxt, 0, 0, MaxCol(), MaxRow(), InsertDeleteFlags::ALL,
+ (pOnlyMarked != nullptr), maTabs[nNewPos].get(), pOnlyMarked,
+ false /*bAsLink*/, true /*bColRowFlags*/, bGlobalNamesToLocal, false /*bCopyCaptions*/ );
+ maTabs[nNewPos]->SetTabBgColor(maTabs[nOldPos]->GetTabBgColor());
+
+ SCTAB nDz = nNewPos - nOldPos;
+ sc::RefUpdateContext aRefCxt(*this);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = ScRange(0, 0, nNewPos, MaxCol(), MaxRow(), nNewPos);
+ aRefCxt.mnTabDelta = nDz;
+ maTabs[nNewPos]->UpdateReference(aRefCxt);
+
+ maTabs[nNewPos]->UpdateInsertTabAbs(nNewPos); // move all paragraphs up by one!!
+ maTabs[nOldPos]->UpdateInsertTab(aCxt);
+
+ maTabs[nOldPos]->UpdateCompile();
+ maTabs[nNewPos]->UpdateCompile( true ); // maybe already compiled in Clone, but used names need recompilation
+ SetNoListening( false );
+ sc::StartListeningContext aSLCxt(*this);
+ maTabs[nOldPos]->StartListeners(aSLCxt, true);
+ maTabs[nNewPos]->StartListeners(aSLCxt, true);
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+
+ if (mpDrawLayer) // Skip cloning Note caption object
+ // page is already created in ScTable ctor
+ mpDrawLayer->ScCopyPage( static_cast<sal_uInt16>(nOldPos), static_cast<sal_uInt16>(nNewPos) );
+
+ if (pDPCollection)
+ pDPCollection->CopyToTab(nOldPos, nNewPos);
+
+ maTabs[nNewPos]->SetPageStyle( maTabs[nOldPos]->GetPageStyle() );
+ maTabs[nNewPos]->SetPendingRowHeights( maTabs[nOldPos]->IsPendingRowHeights() );
+
+ // Copy the custom print range if exists.
+ maTabs[nNewPos]->CopyPrintRange(*maTabs[nOldPos]);
+
+ // Copy the RTL settings
+ maTabs[nNewPos]->SetLayoutRTL(maTabs[nOldPos]->IsLayoutRTL());
+ maTabs[nNewPos]->SetLoadingRTL(maTabs[nOldPos]->IsLoadingRTL());
+
+ // Finally copy the note captions, which need
+ // 1. the updated source ScColumn::nTab members if nNewPos <= nOldPos
+ // 2. row heights and column widths of the destination
+ // 3. RTL settings of the destination
+ maTabs[nOldPos]->CopyCaptionsToTable( 0, 0, MaxCol(), MaxRow(), maTabs[nNewPos].get(), true /*bCloneCaption*/);
+ }
+
+ return bValid;
+}
+
+sal_uLong ScDocument::TransferTab( ScDocument& rSrcDoc, SCTAB nSrcPos,
+ SCTAB nDestPos, bool bInsertNew,
+ bool bResultsOnly )
+{
+ sal_uLong nRetVal = 1; // 0 => error 1 = ok
+ // 3 => NameBox
+ // 4 => both
+
+ if (rSrcDoc.mpShell->GetMedium())
+ {
+ rSrcDoc.maFileURL = rSrcDoc.mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ // for unsaved files use the title name and adjust during save of file
+ if (rSrcDoc.maFileURL.isEmpty())
+ rSrcDoc.maFileURL = rSrcDoc.mpShell->GetName();
+ }
+ else
+ {
+ rSrcDoc.maFileURL = rSrcDoc.mpShell->GetName();
+ }
+
+ bool bValid = true;
+ if (bInsertNew) // re-insert
+ {
+ OUString aName;
+ rSrcDoc.GetName(nSrcPos, aName);
+ CreateValidTabName(aName);
+ bValid = InsertTab(nDestPos, aName);
+
+ // Copy the RTL settings
+ maTabs[nDestPos]->SetLayoutRTL(rSrcDoc.maTabs[nSrcPos]->IsLayoutRTL());
+ maTabs[nDestPos]->SetLoadingRTL(rSrcDoc.maTabs[nSrcPos]->IsLoadingRTL());
+ }
+ else // replace existing tables
+ {
+ if (ScTable* pTable = FetchTable(nDestPos))
+ {
+ pTable->DeleteArea(0, 0, MaxCol(), MaxRow(), InsertDeleteFlags::ALL);
+ }
+ else
+ bValid = false;
+ }
+
+ if (bValid)
+ {
+ bool bOldAutoCalcSrc = false;
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid repeated calculations
+ SetNoListening( true );
+ if ( bResultsOnly )
+ {
+ bOldAutoCalcSrc = rSrcDoc.GetAutoCalc();
+ rSrcDoc.SetAutoCalc( true ); // in case something needs calculation
+ }
+
+ {
+ NumFmtMergeHandler aNumFmtMergeHdl(*this, rSrcDoc);
+
+ sc::CopyToDocContext aCxt(*this);
+ nDestPos = std::min(nDestPos, static_cast<SCTAB>(GetTableCount() - 1));
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( pBASM.get(), SfxHintId::ScDataChanged);
+ if (!bResultsOnly)
+ {
+ const bool bGlobalNamesToLocal = false;
+ const ScRangeName* pNames = rSrcDoc.GetRangeName( nSrcPos);
+ if (pNames)
+ pNames->CopyUsedNames( nSrcPos, nSrcPos, nDestPos, rSrcDoc, *this, bGlobalNamesToLocal);
+ rSrcDoc.GetRangeName()->CopyUsedNames( -1, nSrcPos, nDestPos, rSrcDoc, *this, bGlobalNamesToLocal);
+ }
+ rSrcDoc.maTabs[nSrcPos]->CopyToTable(aCxt, 0, 0, MaxCol(), MaxRow(),
+ ( bResultsOnly ? InsertDeleteFlags::ALL & ~InsertDeleteFlags::FORMULA : InsertDeleteFlags::ALL),
+ false, maTabs[nDestPos].get(), /*pMarkData*/nullptr, /*bAsLink*/false, /*bColRowFlags*/true,
+ /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+ }
+ }
+ maTabs[nDestPos]->SetTabNo(nDestPos);
+ maTabs[nDestPos]->SetTabBgColor(rSrcDoc.maTabs[nSrcPos]->GetTabBgColor());
+
+ // tdf#66613 - copy existing print ranges and col/row repetitions
+ if (auto aRepeatColRange = rSrcDoc.maTabs[nSrcPos]->GetRepeatColRange())
+ {
+ aRepeatColRange->aStart.SetTab(nDestPos);
+ aRepeatColRange->aEnd.SetTab(nDestPos);
+ maTabs[nDestPos]->SetRepeatColRange(aRepeatColRange);
+ }
+
+ if (auto aRepeatRowRange = rSrcDoc.maTabs[nSrcPos]->GetRepeatRowRange())
+ {
+ aRepeatRowRange->aStart.SetTab(nDestPos);
+ aRepeatRowRange->aEnd.SetTab(nDestPos);
+ maTabs[nDestPos]->SetRepeatRowRange(aRepeatRowRange);
+ }
+
+ if (rSrcDoc.IsPrintEntireSheet(nSrcPos))
+ maTabs[nDestPos]->SetPrintEntireSheet();
+ else
+ {
+ // tdf#157897 - clear print ranges before adding additional ones
+ maTabs[nDestPos]->ClearPrintRanges();
+ const auto nPrintRangeCount = rSrcDoc.maTabs[nSrcPos]->GetPrintRangeCount();
+ for (auto nPos = 0; nPos < nPrintRangeCount; nPos++)
+ {
+ // Adjust the tab for the print range at the new position
+ ScRange aSrcPrintRange(*rSrcDoc.maTabs[nSrcPos]->GetPrintRange(nPos));
+ aSrcPrintRange.aStart.SetTab(nDestPos);
+ aSrcPrintRange.aEnd.SetTab(nDestPos);
+ maTabs[nDestPos]->AddPrintRange(aSrcPrintRange);
+ }
+ }
+
+ if ( !bResultsOnly )
+ {
+ sc::RefUpdateContext aRefCxt(*this);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = ScRange(0, 0, nDestPos, MaxCol(), MaxRow(), nDestPos);
+ aRefCxt.mnTabDelta = nDestPos - nSrcPos;
+ maTabs[nDestPos]->UpdateReference(aRefCxt);
+
+ // Readjust self-contained absolute references to this sheet
+ maTabs[nDestPos]->TestTabRefAbs(nSrcPos);
+ sc::CompileFormulaContext aFormulaCxt(*this);
+ maTabs[nDestPos]->CompileAll(aFormulaCxt);
+ }
+
+ SetNoListening( false );
+ if ( !bResultsOnly )
+ {
+ sc::StartListeningContext aSLCxt(*this);
+ maTabs[nDestPos]->StartListeners(aSLCxt, true);
+ }
+ SetDirty( ScRange( 0, 0, nDestPos, MaxCol(), MaxRow(), nDestPos), false);
+
+ if ( bResultsOnly )
+ rSrcDoc.SetAutoCalc( bOldAutoCalcSrc );
+ SetAutoCalc( bOldAutoCalc );
+
+ // copy Drawing
+
+ if (bInsertNew)
+ TransferDrawPage( rSrcDoc, nSrcPos, nDestPos );
+
+ maTabs[nDestPos]->SetPendingRowHeights( rSrcDoc.maTabs[nSrcPos]->IsPendingRowHeights() );
+ }
+ if (!bValid)
+ nRetVal = 0;
+ bool bVbaEnabled = IsInVBAMode();
+
+ if ( bVbaEnabled )
+ {
+ ScDocShell* pSrcShell = rSrcDoc.GetDocumentShell();
+ if ( pSrcShell )
+ {
+ OUString aLibName("Standard");
+#if HAVE_FEATURE_SCRIPTING
+ const BasicManager *pBasicManager = pSrcShell->GetBasicManager();
+ if (pBasicManager && !pBasicManager->GetName().isEmpty())
+ {
+ aLibName = pSrcShell->GetBasicManager()->GetName();
+ }
+#endif
+ OUString sSource;
+ uno::Reference< script::XLibraryContainer > xLibContainer = pSrcShell->GetBasicContainer();
+ uno::Reference< container::XNameContainer > xLib;
+ if( xLibContainer.is() )
+ {
+ uno::Any aLibAny = xLibContainer->getByName(aLibName);
+ aLibAny >>= xLib;
+ }
+
+ if( xLib.is() )
+ {
+ OUString sSrcCodeName;
+ rSrcDoc.GetCodeName( nSrcPos, sSrcCodeName );
+ OUString sRTLSource;
+ if (xLib->hasByName( sSrcCodeName ))
+ xLib->getByName( sSrcCodeName ) >>= sRTLSource;
+ sSource = sRTLSource;
+ }
+ VBA_InsertModule( *this, nDestPos, sSource );
+ }
+ }
+
+ return nRetVal;
+}
+
+void ScDocument::SetError( SCCOL nCol, SCROW nRow, SCTAB nTab, const FormulaError nError)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetError(nCol, nRow, nError);
+}
+
+void ScDocument::SetFormula(
+ const ScAddress& rPos, const ScTokenArray& rArray )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ pTable->SetFormula(rPos.Col(), rPos.Row(), rArray, formula::FormulaGrammar::GRAM_DEFAULT);
+}
+
+void ScDocument::SetFormula(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ pTable->SetFormula(rPos.Col(), rPos.Row(), rFormula, eGram);
+}
+
+ScFormulaCell* ScDocument::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->SetFormulaCell(rPos.Col(), rPos.Row(), pCell);
+
+ delete pCell;
+ return nullptr;
+}
+
+bool ScDocument::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells )
+{
+ if (rCells.empty())
+ return false;
+
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->SetFormulaCells(rPos.Col(), rPos.Row(), rCells);
+ return false;
+}
+
+void ScDocument::SetConsolidateDlgData( std::unique_ptr<ScConsolidateParam> pData )
+{
+ pConsolidateDlgData = std::move(pData);
+}
+
+void ScDocument::SetEasyConditionalFormatDialogData(std::unique_ptr<ScConditionMode> pMode)
+{
+ pConditionalFormatDialogMode = std::move(pMode);
+}
+
+void ScDocument::SetChangeViewSettings(const ScChangeViewSettings& rNew)
+{
+ if (pChangeViewSettings==nullptr)
+ pChangeViewSettings.reset( new ScChangeViewSettings );
+
+ *pChangeViewSettings=rNew;
+}
+
+std::unique_ptr<ScFieldEditEngine> ScDocument::CreateFieldEditEngine()
+{
+ std::unique_ptr<ScFieldEditEngine> pNewEditEngine;
+ if (!pCacheFieldEditEngine)
+ {
+ pNewEditEngine.reset( new ScFieldEditEngine(
+ this, GetEnginePool(), GetEditPool(), false) );
+ }
+ else
+ {
+ if ( !bImportingXML )
+ {
+ // #i66209# previous use might not have restored update mode,
+ // ensure same state as for a new EditEngine (UpdateMode = true)
+ pCacheFieldEditEngine->SetUpdateLayout(true);
+ }
+
+ pNewEditEngine = std::move(pCacheFieldEditEngine);
+ }
+ return pNewEditEngine;
+}
+
+void ScDocument::DisposeFieldEditEngine(std::unique_ptr<ScFieldEditEngine>& rpEditEngine)
+{
+ if (!pCacheFieldEditEngine && rpEditEngine)
+ {
+ pCacheFieldEditEngine = std::move( rpEditEngine );
+ pCacheFieldEditEngine->Clear();
+ }
+ else
+ rpEditEngine.reset();
+}
+
+ScLookupCache & ScDocument::GetLookupCache( const ScRange & rRange, ScInterpreterContext* pContext )
+{
+ ScLookupCache* pCache = nullptr;
+ if (!pContext->mxScLookupCache)
+ pContext->mxScLookupCache.reset(new ScLookupCacheMap);
+ ScLookupCacheMap* pCacheMap = pContext->mxScLookupCache.get();
+ // insert with temporary value to avoid doing two lookups
+ auto [findIt, bInserted] = pCacheMap->aCacheMap.emplace(rRange, nullptr);
+ if (bInserted)
+ {
+ findIt->second = std::make_unique<ScLookupCache>(this, rRange, *pCacheMap);
+ pCache = findIt->second.get();
+ // The StartListeningArea() call is not thread-safe, as all threads
+ // would access the same SvtBroadcaster.
+ std::unique_lock guard( mScLookupMutex );
+ StartListeningArea(rRange, false, pCache);
+ }
+ else
+ pCache = (*findIt).second.get();
+
+ return *pCache;
+}
+
+ScSortedRangeCache& ScDocument::GetSortedRangeCache( const ScRange & rRange, const ScQueryParam& param,
+ ScInterpreterContext* pContext )
+{
+ assert(mxScSortedRangeCache);
+ ScSortedRangeCache::HashKey key = ScSortedRangeCache::makeHashKey(rRange, param);
+ // This should be created just once for one range, and repeated calls should reuse it, even
+ // between threads (it doesn't make sense to use ScInterpreterContext and have all threads
+ // build their own copy of the same data). So first try read-only access, which should
+ // in most cases be enough.
+ {
+ std::shared_lock guard(mScLookupMutex);
+ auto findIt = mxScSortedRangeCache->aCacheMap.find(key);
+ if( findIt != mxScSortedRangeCache->aCacheMap.end())
+ return *findIt->second;
+ }
+ // Avoid recursive calls because of some cells in the range being dirty and triggering
+ // interpreting, which may call into this again. Threaded calculation makes sure
+ // no cells are dirty. If some cells in the range cannot be interpreted and remain
+ // dirty e.g. because of circular dependencies, create only an invalid empty cache to prevent
+ // a possible recursive deadlock.
+ bool invalid = false;
+ if(!IsThreadedGroupCalcInProgress())
+ if(!InterpretCellsIfNeeded(rRange))
+ invalid = true;
+ std::unique_lock guard(mScLookupMutex);
+ auto [findIt, bInserted] = mxScSortedRangeCache->aCacheMap.emplace(key, nullptr);
+ if (bInserted)
+ {
+ findIt->second = std::make_unique<ScSortedRangeCache>(this, rRange, param, pContext, invalid);
+ StartListeningArea(rRange, false, findIt->second.get());
+ }
+ return *findIt->second;
+}
+
+void ScDocument::RemoveLookupCache( ScLookupCache & rCache )
+{
+ // Data changes leading to this should never happen during calculation (they are either
+ // a result of user input or recalc). If it turns out this can be the case, locking is needed
+ // here and also in ScLookupCache::Notify().
+ assert(!IsThreadedGroupCalcInProgress());
+ auto & cacheMap = rCache.getCacheMap();
+ auto it(cacheMap.aCacheMap.find(rCache.getRange()));
+ if (it != cacheMap.aCacheMap.end())
+ {
+ std::unique_ptr<ScLookupCache> xCache = std::move(it->second);
+ cacheMap.aCacheMap.erase(it);
+ assert(!IsThreadedGroupCalcInProgress()); // EndListeningArea() is not thread-safe
+ EndListeningArea(xCache->getRange(), false, &rCache);
+ return;
+ }
+ OSL_FAIL( "ScDocument::RemoveLookupCache: range not found in hash map");
+}
+
+void ScDocument::RemoveSortedRangeCache( ScSortedRangeCache & rCache )
+{
+ // Data changes leading to this should never happen during calculation (they are either
+ // a result of user input or recalc). If it turns out this can be the case, locking is needed
+ // here and also in ScSortedRangeCache::Notify().
+ assert(!IsThreadedGroupCalcInProgress());
+ auto it(mxScSortedRangeCache->aCacheMap.find(rCache.getHashKey()));
+ if (it != mxScSortedRangeCache->aCacheMap.end())
+ {
+ std::unique_ptr<ScSortedRangeCache> xCache = std::move(it->second);
+ mxScSortedRangeCache->aCacheMap.erase(it);
+ assert(!IsThreadedGroupCalcInProgress()); // EndListeningArea() is not thread-safe
+ EndListeningArea(xCache->getRange(), false, &rCache);
+ return;
+ }
+ OSL_FAIL( "ScDocument::RemoveSortedRangeCache: range not found in hash map");
+}
+
+void ScDocument::ClearLookupCaches()
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ GetNonThreadedContext().mxScLookupCache.reset();
+ mxScSortedRangeCache->aCacheMap.clear();
+ // Clear lookup cache in all interpreter-contexts in the (threaded/non-threaded) pools.
+ ScInterpreterContextPool::ClearLookupCaches(this);
+}
+
+bool ScDocument::IsCellInChangeTrack(const ScAddress &cell,Color *pColCellBorder)
+{
+ ScChangeTrack* pTrack = GetChangeTrack();
+ ScChangeViewSettings* pSettings = GetChangeViewSettings();
+ if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
+ return false; // missing or turned-off
+ ScActionColorChanger aColorChanger(*pTrack);
+ // Clipping happens from outside
+ //! TODO: without Clipping; only paint affected cells ??!??!?
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ if ( pAction->IsVisible() )
+ {
+ ScChangeActionType eType = pAction->GetType();
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == cell.Tab())
+ {
+ ScRange aRange = rBig.MakeRange( *this );
+ if ( eType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+ if (ScViewUtil::IsActionShown( *pAction, *pSettings, *this ) )
+ {
+ if (aRange.Contains(cell))
+ {
+ if (pColCellBorder != nullptr)
+ {
+ aColorChanger.Update( *pAction );
+ Color aColor( aColorChanger.GetColor() );
+ *pColCellBorder = aColor;
+ }
+ return true;
+ }
+ }
+ }
+ if ( eType == SC_CAT_MOVE &&
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().aStart.Tab() == cell.Col() )
+ {
+ ScRange aRange = static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( *this );
+ if (ScViewUtil::IsActionShown( *pAction, *pSettings, *this ) )
+ {
+ if (aRange.Contains(cell))
+ {
+ if (pColCellBorder != nullptr)
+ {
+ aColorChanger.Update( *pAction );
+ Color aColor( aColorChanger.GetColor() );
+ *pColCellBorder = aColor;
+ }
+ return true;
+ }
+ }
+ }
+ }
+ pAction = pAction->GetNext();
+ }
+ return false;
+}
+
+void ScDocument::GetCellChangeTrackNote( const ScAddress &aCellPos, OUString &aTrackText,bool &bLeftEdge)
+{
+ aTrackText.clear();
+ // Change-Tracking
+ ScChangeTrack* pTrack = GetChangeTrack();
+ ScChangeViewSettings* pSettings = GetChangeViewSettings();
+ if ( !(pTrack && pTrack->GetFirst() && pSettings && pSettings->ShowChanges()))
+ return;
+
+ const ScChangeAction* pFound = nullptr;
+ const ScChangeAction* pFoundContent = nullptr;
+ const ScChangeAction* pFoundMove = nullptr;
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ if ( pAction->IsVisible() &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, *this ) )
+ {
+ ScChangeActionType eType = pAction->GetType();
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == aCellPos.Tab())
+ {
+ ScRange aRange = rBig.MakeRange( *this );
+ if ( eType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+ if ( aRange.Contains( aCellPos ) )
+ {
+ pFound = pAction; // the last wins
+ switch ( eType )
+ {
+ case SC_CAT_CONTENT :
+ pFoundContent = pAction;
+ break;
+ case SC_CAT_MOVE :
+ pFoundMove = pAction;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if ( eType == SC_CAT_MOVE )
+ {
+ ScRange aRange =
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( *this );
+ if ( aRange.Contains( aCellPos ) )
+ {
+ pFound = pAction;
+ }
+ }
+ }
+ pAction = pAction->GetNext();
+ }
+ if ( !pFound )
+ return;
+
+ if ( pFoundContent && pFound->GetType() != SC_CAT_CONTENT )
+ pFound = pFoundContent; // Content wins
+ if ( pFoundMove && pFound->GetType() != SC_CAT_MOVE &&
+ pFoundMove->GetActionNumber() >
+ pFound->GetActionNumber() )
+ pFound = pFoundMove; // Move wins
+ // for deleted columns: arrow on left side of row
+ if ( pFound->GetType() == SC_CAT_DELETE_COLS )
+ bLeftEdge = true;
+ DateTime aDT = pFound->GetDateTime();
+ aTrackText = pFound->GetUser();
+ aTrackText += ", ";
+ aTrackText += ScGlobal::getLocaleData().getDate(aDT);
+ aTrackText += " ";
+ aTrackText += ScGlobal::getLocaleData().getTime(aDT);
+ aTrackText += ":\n";
+ OUString aComStr = pFound->GetComment();
+ if(!aComStr.isEmpty())
+ {
+ aTrackText += aComStr;
+ aTrackText += "\n( ";
+ }
+ aTrackText = pFound->GetDescription( *this );
+ if (!aComStr.isEmpty())
+ {
+ aTrackText += ")";
+ }
+}
+
+void ScDocument::SetPreviewFont( std::unique_ptr<SfxItemSet> pFont )
+{
+ pPreviewFont = std::move(pFont);
+}
+
+void ScDocument::SetPreviewSelection( const ScMarkData& rSel )
+{
+ maPreviewSelection = rSel;
+}
+
+SfxItemSet* ScDocument::GetPreviewFont( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ SfxItemSet* pRet = nullptr;
+ if ( pPreviewFont )
+ {
+ ScMarkData aSel = GetPreviewSelection();
+ if ( aSel.IsCellMarked( nCol, nRow ) && aSel.GetFirstSelected() == nTab )
+ pRet = pPreviewFont.get();
+ }
+ return pRet;
+}
+
+ScStyleSheet* ScDocument::GetPreviewCellStyle( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScStyleSheet* pRet = nullptr;
+ ScMarkData aSel = GetPreviewSelection();
+ if ( pPreviewCellStyle && aSel.IsCellMarked( nCol, nRow ) && aSel.GetFirstSelected() == nTab )
+ pRet = pPreviewCellStyle;
+ return pRet;
+}
+
+sc::IconSetBitmapMap& ScDocument::GetIconSetBitmapMap()
+{
+ if (!m_pIconSetBitmapMap)
+ {
+ m_pIconSetBitmapMap.reset(new sc::IconSetBitmapMap);
+ }
+ return *m_pIconSetBitmapMap;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
new file mode 100644
index 0000000000..b42a8d36b5
--- /dev/null
+++ b/sc/source/core/data/documen3.cxx
@@ -0,0 +1,2152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <scitems.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/thread.hxx>
+#include <osl/diagnose.h>
+#include <tools/duration.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <table.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <poolhelp.hxx>
+#include <rangelst.hxx>
+#include <chartlock.hxx>
+#include <refupdat.hxx>
+#include <docoptio.hxx>
+#include <scmod.hxx>
+#include <clipoptions.hxx>
+#include <viewopti.hxx>
+#include <scextopt.hxx>
+#include <tablink.hxx>
+#include <externalrefmgr.hxx>
+#include <markdata.hxx>
+#include <validat.hxx>
+#include <dociter.hxx>
+#include <detdata.hxx>
+#include <inputopt.hxx>
+#include <chartlis.hxx>
+#include <sc.hrc>
+#include <hints.hxx>
+#include <dpobject.hxx>
+#include <drwlayer.hxx>
+#include <unoreflist.hxx>
+#include <listenercalls.hxx>
+#include <tabprotection.hxx>
+#include <formulaparserpool.hxx>
+#include <clipparam.hxx>
+#include <sheetevents.hxx>
+#include <queryentry.hxx>
+#include <formulacell.hxx>
+#include <refupdatecontext.hxx>
+#include <scopetools.hxx>
+#include <filterentries.hxx>
+#include <queryparam.hxx>
+
+#include <globalnames.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <config_fuzzers.h>
+#include <memory>
+
+using namespace com::sun::star;
+
+namespace {
+
+void sortAndRemoveDuplicates(std::vector<ScTypedStrData>& rStrings, bool bCaseSens)
+{
+ if (bCaseSens)
+ {
+ std::stable_sort(rStrings.begin(), rStrings.end(), ScTypedStrData::LessCaseSensitive());
+ std::vector<ScTypedStrData>::iterator it =
+ std::unique(rStrings.begin(), rStrings.end(), ScTypedStrData::EqualCaseSensitive());
+ rStrings.erase(it, rStrings.end());
+ }
+ else
+ {
+ std::stable_sort(rStrings.begin(), rStrings.end(), ScTypedStrData::LessCaseInsensitive());
+ std::vector<ScTypedStrData>::iterator it =
+ std::unique(rStrings.begin(), rStrings.end(), ScTypedStrData::EqualCaseInsensitive());
+ rStrings.erase(it, rStrings.end());
+ }
+ if (std::any_of(rStrings.begin(), rStrings.end(),
+ [](ScTypedStrData& rString) { return rString.IsHiddenByFilter(); })) {
+ std::stable_sort(rStrings.begin(), rStrings.end(), ScTypedStrData::LessHiddenRows());
+ }
+}
+
+}
+
+void ScDocument::GetAllTabRangeNames(ScRangeName::TabNameCopyMap& rNames) const
+{
+ ScRangeName::TabNameCopyMap aNames;
+ for (SCTAB i = 0; i < GetTableCount(); ++i)
+ {
+ if (!maTabs[i])
+ // no more tables to iterate through.
+ break;
+
+ const ScRangeName* p = maTabs[i]->mpRangeName.get();
+ if (!p || p->empty())
+ // ignore empty ones.
+ continue;
+
+ aNames.emplace(i, p);
+ }
+ rNames.swap(aNames);
+}
+
+void ScDocument::SetAllRangeNames(const std::map<OUString, ScRangeName>& rRangeMap)
+{
+ for (const auto& [rName, rRangeName] : rRangeMap)
+ {
+ if (rName == STR_GLOBAL_RANGE_NAME)
+ {
+ pRangeName.reset();
+ if (!rRangeName.empty())
+ pRangeName.reset( new ScRangeName( rRangeName ) );
+ }
+ else
+ {
+ SCTAB nTab;
+ bool bFound = GetTable(rName, nTab);
+ assert(bFound); (void)bFound; // fouled up?
+ if (rRangeName.empty())
+ SetRangeName( nTab, nullptr );
+ else
+ SetRangeName( nTab, std::unique_ptr<ScRangeName>(new ScRangeName( rRangeName )) );
+ }
+ }
+}
+
+void ScDocument::GetRangeNameMap(std::map<OUString, ScRangeName*>& aRangeNameMap)
+{
+ for (SCTAB i = 0; i < GetTableCount(); ++i)
+ {
+ if (!maTabs[i])
+ continue;
+ ScRangeName* p = maTabs[i]->GetRangeName();
+ if (!p )
+ {
+ p = new ScRangeName();
+ SetRangeName(i, std::unique_ptr<ScRangeName>(p));
+ }
+ OUString aTableName = maTabs[i]->GetName();
+ aRangeNameMap.insert(std::pair<OUString, ScRangeName*>(aTableName,p));
+ }
+ if (!pRangeName)
+ {
+ pRangeName.reset(new ScRangeName());
+ }
+ aRangeNameMap.insert(std::pair<OUString, ScRangeName*>(STR_GLOBAL_RANGE_NAME, pRangeName.get()));
+}
+
+ScRangeName* ScDocument::GetRangeName(SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRangeName();
+ return nullptr;
+}
+
+ScRangeName* ScDocument::GetRangeName() const
+{
+ if (!pRangeName)
+ pRangeName.reset(new ScRangeName);
+ return pRangeName.get();
+}
+
+void ScDocument::SetRangeName(SCTAB nTab, std::unique_ptr<ScRangeName> pNew)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRangeName(std::move(pNew));
+}
+
+void ScDocument::SetRangeName( std::unique_ptr<ScRangeName> pNewRangeName )
+{
+ pRangeName = std::move(pNewRangeName);
+}
+
+bool ScDocument::IsAddressInRangeName( RangeNameScope eScope, const ScAddress& rAddress )
+{
+ ScRangeName* pRangeNames;
+ ScRange aNameRange;
+
+ if (eScope == RangeNameScope::GLOBAL)
+ pRangeNames= GetRangeName();
+ else
+ pRangeNames= GetRangeName(rAddress.Tab());
+
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (rEntry.second->IsValidReference(aNameRange))
+ {
+ if (aNameRange.Contains(rAddress))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScDocument::InsertNewRangeName( const OUString& rName, const ScAddress& rPos, const OUString& rExpr )
+{
+ ScRangeName* pGlobalNames = GetRangeName();
+ if (!pGlobalNames)
+ return false;
+
+ ScRangeData* pName = new ScRangeData(*this, rName, rExpr, rPos, ScRangeData::Type::Name, GetGrammar());
+ return pGlobalNames->insert(pName);
+}
+
+bool ScDocument::InsertNewRangeName( SCTAB nTab, const OUString& rName, const ScAddress& rPos, const OUString& rExpr )
+{
+ ScRangeName* pLocalNames = GetRangeName(nTab);
+ if (!pLocalNames)
+ return false;
+
+ ScRangeData* pName = new ScRangeData(*this, rName, rExpr, rPos, ScRangeData::Type::Name, GetGrammar());
+ return pLocalNames->insert(pName);
+}
+
+const ScRangeData* ScDocument::GetRangeAtBlock( const ScRange& rBlock, OUString& rName, bool* pSheetLocal ) const
+{
+ const ScRangeData* pData = nullptr;
+ if (rBlock.aStart.Tab() == rBlock.aEnd.Tab())
+ {
+ const ScRangeName* pLocalNames = GetRangeName(rBlock.aStart.Tab());
+ if (pLocalNames)
+ {
+ pData = pLocalNames->findByRange( rBlock );
+ if (pData)
+ {
+ rName = pData->GetName();
+ if (pSheetLocal)
+ *pSheetLocal = true;
+ return pData;
+ }
+ }
+ }
+ if ( pRangeName )
+ {
+ pData = pRangeName->findByRange( rBlock );
+ if (pData)
+ {
+ rName = pData->GetName();
+ if (pSheetLocal)
+ *pSheetLocal = false;
+ }
+ }
+ return pData;
+}
+
+ScRangeData* ScDocument::FindRangeNameBySheetAndIndex( SCTAB nTab, sal_uInt16 nIndex ) const
+{
+ const ScRangeName* pRN = (nTab < 0 ? GetRangeName() : GetRangeName(nTab));
+ return (pRN ? pRN->findByIndex( nIndex) : nullptr);
+}
+
+void ScDocument::SetDBCollection( std::unique_ptr<ScDBCollection> pNewDBCollection, bool bRemoveAutoFilter )
+{
+ if (pDBCollection && bRemoveAutoFilter)
+ {
+ // remove auto filter attribute if new db data don't contain auto filter flag
+ // start position is also compared, so bRemoveAutoFilter must not be set from ref-undo!
+
+ ScDBCollection::NamedDBs& rNamedDBs = pDBCollection->getNamedDBs();
+ for (const auto& rxNamedDB : rNamedDBs)
+ {
+ const ScDBData& rOldData = *rxNamedDB;
+ if (!rOldData.HasAutoFilter())
+ continue;
+
+ ScRange aOldRange;
+ rOldData.GetArea(aOldRange);
+
+ bool bFound = false;
+ if (pNewDBCollection)
+ {
+ ScDBData* pNewData = pNewDBCollection->getNamedDBs().findByUpperName(rOldData.GetUpperName());
+ if (pNewData)
+ {
+ if (pNewData->HasAutoFilter())
+ {
+ ScRange aNewRange;
+ pNewData->GetArea(aNewRange);
+ if (aOldRange.aStart == aNewRange.aStart)
+ bFound = true;
+ }
+ }
+ }
+
+ if (!bFound)
+ {
+ aOldRange.aEnd.SetRow(aOldRange.aStart.Row());
+ RemoveFlagsTab( aOldRange.aStart.Col(), aOldRange.aStart.Row(),
+ aOldRange.aEnd.Col(), aOldRange.aEnd.Row(),
+ aOldRange.aStart.Tab(), ScMF::Auto );
+ RepaintRange( aOldRange );
+ }
+ }
+ }
+
+ pDBCollection = std::move(pNewDBCollection);
+}
+
+const ScDBData* ScDocument::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ePortion);
+ else
+ return nullptr;
+}
+
+ScDBData* ScDocument::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion)
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ePortion);
+ else
+ return nullptr;
+}
+
+const ScDBData* ScDocument::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2);
+ else
+ return nullptr;
+}
+
+ScDBData* ScDocument::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2);
+ else
+ return nullptr;
+}
+
+void ScDocument::RefreshDirtyTableColumnNames()
+{
+ if (pDBCollection)
+ pDBCollection->RefreshDirtyTableColumnNames();
+}
+
+bool ScDocument::HasPivotTable() const
+{
+ return pDPCollection && pDPCollection->GetCount();
+}
+
+ScDPCollection* ScDocument::GetDPCollection()
+{
+ if (!pDPCollection)
+ pDPCollection.reset( new ScDPCollection(*this) );
+ return pDPCollection.get();
+}
+
+const ScDPCollection* ScDocument::GetDPCollection() const
+{
+ return pDPCollection.get();
+}
+
+ScDPObject* ScDocument::GetDPAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab) const
+{
+ if (!pDPCollection)
+ return nullptr;
+
+ sal_uInt16 nCount = pDPCollection->GetCount();
+ ScAddress aPos( nCol, nRow, nTab );
+ for (sal_uInt16 i=0; i<nCount; i++)
+ if ( (*pDPCollection)[i].GetOutRange().Contains( aPos ) )
+ return &(*pDPCollection)[i];
+
+ return nullptr;
+}
+
+ScDPObject* ScDocument::GetDPAtBlock( const ScRange & rBlock ) const
+{
+ if (!pDPCollection)
+ return nullptr;
+
+ /* Walk the collection in reverse order to get something of an
+ * approximation of MS Excels 'most recent' effect. */
+ sal_uInt16 i = pDPCollection->GetCount();
+ while ( i-- > 0 )
+ if ( (*pDPCollection)[i].GetOutRange().Contains( rBlock ) )
+ return &(*pDPCollection)[i];
+
+ return nullptr;
+}
+
+void ScDocument::StopTemporaryChartLock()
+{
+ if (apTemporaryChartLock)
+ apTemporaryChartLock->StopLocking();
+}
+
+void ScDocument::SetChartListenerCollection(
+ std::unique_ptr<ScChartListenerCollection> pNewChartListenerCollection,
+ bool bSetChartRangeLists )
+{
+ std::unique_ptr<ScChartListenerCollection> pOld = std::move(pChartListenerCollection);
+ pChartListenerCollection = std::move(pNewChartListenerCollection);
+ if ( pChartListenerCollection )
+ {
+ if ( pOld )
+ pChartListenerCollection->SetDiffDirty( *pOld, bSetChartRangeLists );
+ pChartListenerCollection->StartAllListeners();
+ }
+}
+
+void ScDocument::SetScenario( SCTAB nTab, bool bFlag )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetScenario(bFlag);
+}
+
+bool ScDocument::IsScenario( SCTAB nTab ) const
+{
+ const ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->IsScenario();
+}
+
+void ScDocument::SetScenarioData( SCTAB nTab, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags )
+{
+ if (ScTable* pTable = FetchTable(nTab); pTable && pTable->IsScenario())
+ {
+ pTable->SetScenarioComment( rComment );
+ pTable->SetScenarioColor( rColor );
+ pTable->SetScenarioFlags( nFlags );
+ }
+}
+
+Color ScDocument::GetTabBgColor( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetTabBgColor();
+ return COL_AUTO;
+}
+
+void ScDocument::SetTabBgColor( SCTAB nTab, const Color& rColor )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetTabBgColor(rColor);
+}
+
+bool ScDocument::IsDefaultTabBgColor( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetTabBgColor() == COL_AUTO;
+ return true;
+}
+
+void ScDocument::GetScenarioData( SCTAB nTab, OUString& rComment,
+ Color& rColor, ScScenarioFlags& rFlags ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab); pTable && pTable->IsScenario())
+ {
+ pTable->GetScenarioComment( rComment );
+ rColor = pTable->GetScenarioColor();
+ rFlags = pTable->GetScenarioFlags();
+ }
+}
+
+void ScDocument::GetScenarioFlags( SCTAB nTab, ScScenarioFlags& rFlags ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab); pTable && pTable->IsScenario())
+ rFlags = pTable->GetScenarioFlags();
+}
+
+bool ScDocument::IsLinked( SCTAB nTab ) const
+{
+ const ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->IsLinked();
+}
+
+formula::FormulaGrammar::AddressConvention ScDocument::GetAddressConvention() const
+{
+ return formula::FormulaGrammar::extractRefConvention(eGrammar);
+}
+
+void ScDocument::SetGrammar( formula::FormulaGrammar::Grammar eGram )
+{
+ eGrammar = eGram;
+}
+
+ScLinkMode ScDocument::GetLinkMode( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLinkMode();
+ return ScLinkMode::NONE;
+}
+
+OUString ScDocument::GetLinkDoc( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLinkDoc();
+ return OUString();
+}
+
+OUString ScDocument::GetLinkFlt( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLinkFlt();
+ return OUString();
+}
+
+OUString ScDocument::GetLinkOpt( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLinkOpt();
+ return OUString();
+}
+
+OUString ScDocument::GetLinkTab( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLinkTab();
+ return OUString();
+}
+
+sal_uLong ScDocument::GetLinkRefreshDelay( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLinkRefreshDelay();
+ return 0;
+}
+
+void ScDocument::SetLink( SCTAB nTab, ScLinkMode nMode, const OUString& rDoc,
+ const OUString& rFilter, const OUString& rOptions,
+ const OUString& rTabName, sal_uLong nRefreshDelay )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetLink(nMode, rDoc, rFilter, rOptions, rTabName, nRefreshDelay);
+}
+
+bool ScDocument::HasLink( std::u16string_view rDoc,
+ std::u16string_view rFilter, std::u16string_view rOptions ) const
+{
+ SCTAB nCount = GetTableCount();
+ for (SCTAB i=0; i<nCount; i++)
+ if (maTabs[i]->IsLinked()
+ && maTabs[i]->GetLinkDoc() == rDoc
+ && maTabs[i]->GetLinkFlt() == rFilter
+ && maTabs[i]->GetLinkOpt() == rOptions)
+ return true;
+
+ return false;
+}
+
+bool ScDocument::LinkExternalTab( SCTAB& rTab, const OUString& aDocTab,
+ const OUString& aFileName, const OUString& aTabName )
+{
+ if ( IsClipboard() )
+ {
+ OSL_FAIL( "LinkExternalTab in Clipboard" );
+ return false;
+ }
+ rTab = 0;
+#if ENABLE_FUZZERS
+ (void)aDocTab;
+ (void)aFileName;
+ (void)aTabName;
+ return false;
+#else
+ OUString aFilterName; // Is filled by the Loader
+ OUString aOptions; // Filter options
+ sal_uInt32 nLinkCnt = pExtDocOptions ? pExtDocOptions->GetDocSettings().mnLinkCnt : 0;
+ ScDocumentLoader aLoader( aFileName, aFilterName, aOptions, nLinkCnt + 1 );
+ if ( aLoader.IsError() )
+ return false;
+ ScDocument* pSrcDoc = aLoader.GetDocument();
+
+ // Copy table
+ SCTAB nSrcTab;
+ if ( pSrcDoc->GetTable( aTabName, nSrcTab ) )
+ {
+ if ( !InsertTab( SC_TAB_APPEND, aDocTab, true ) )
+ {
+ OSL_FAIL("can't insert external document table");
+ return false;
+ }
+ rTab = GetTableCount() - 1;
+ // Don't insert anew, just the results
+ TransferTab( *pSrcDoc, nSrcTab, rTab, false, true );
+ }
+ else
+ return false;
+
+ sal_uLong nRefreshDelay = 0;
+
+ bool bWasThere = HasLink( aFileName, aFilterName, aOptions );
+ SetLink( rTab, ScLinkMode::VALUE, aFileName, aFilterName, aOptions, aTabName, nRefreshDelay );
+ if ( !bWasThere ) // Add link only once per source document
+ {
+ ScTableLink* pLink = new ScTableLink( mpShell, aFileName, aFilterName, aOptions, nRefreshDelay );
+ pLink->SetInCreate( true );
+ OUString aFilName = aFilterName;
+ GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilName );
+ pLink->Update();
+ pLink->SetInCreate( false );
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS );
+ }
+ return true;
+#endif
+}
+
+ScExternalRefManager* ScDocument::GetExternalRefManager() const
+{
+ ScDocument* pThis = const_cast<ScDocument*>(this);
+ if (!pExternalRefMgr)
+ pThis->pExternalRefMgr.reset( new ScExternalRefManager(*pThis));
+
+ return pExternalRefMgr.get();
+}
+
+bool ScDocument::IsInExternalReferenceMarking() const
+{
+ return pExternalRefMgr && pExternalRefMgr->isInReferenceMarking();
+}
+
+void ScDocument::MarkUsedExternalReferences()
+{
+ if (!pExternalRefMgr)
+ return;
+ if (!pExternalRefMgr->hasExternalData())
+ return;
+ // Charts.
+ pExternalRefMgr->markUsedByLinkListeners();
+ // Formula cells.
+ pExternalRefMgr->markUsedExternalRefCells();
+
+ /* NOTE: Conditional formats and validation objects are marked when
+ * collecting them during export. */
+}
+
+ScFormulaParserPool& ScDocument::GetFormulaParserPool() const
+{
+ if (!mxFormulaParserPool)
+ mxFormulaParserPool.reset( new ScFormulaParserPool( *this ) );
+ return *mxFormulaParserPool;
+}
+
+const ScSheetEvents* ScDocument::GetSheetEvents( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetSheetEvents();
+ return nullptr;
+}
+
+void ScDocument::SetSheetEvents( SCTAB nTab, std::unique_ptr<ScSheetEvents> pNew )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetSheetEvents( std::move(pNew) );
+}
+
+bool ScDocument::HasSheetEventScript( SCTAB nTab, ScSheetEventId nEvent, bool bWithVbaEvents ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ // check if any event handler script has been configured
+ const ScSheetEvents* pEvents = pTable->GetSheetEvents();
+ if ( pEvents && pEvents->GetScript( nEvent ) )
+ return true;
+ // check if VBA event handlers exist
+ if (bWithVbaEvents && mxVbaEvents.is()) try
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(nTab) };
+ if (mxVbaEvents->hasVbaEventHandler( ScSheetEvents::GetVbaSheetEventId( nEvent ), aArgs ) ||
+ mxVbaEvents->hasVbaEventHandler( ScSheetEvents::GetVbaDocumentEventId( nEvent ), uno::Sequence< uno::Any >() ))
+ return true;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ return false;
+}
+
+bool ScDocument::HasAnySheetEventScript( ScSheetEventId nEvent, bool bWithVbaEvents ) const
+{
+ SCTAB nSize = GetTableCount();
+ for (SCTAB nTab = 0; nTab < nSize; nTab++)
+ if (HasSheetEventScript( nTab, nEvent, bWithVbaEvents ))
+ return true;
+ return false;
+}
+
+bool ScDocument::HasAnyCalcNotification() const
+{
+ SCTAB nSize = GetTableCount();
+ for (SCTAB nTab = 0; nTab < nSize; nTab++)
+ if (maTabs[nTab] && maTabs[nTab]->GetCalcNotification())
+ return true;
+ return false;
+}
+
+bool ScDocument::HasCalcNotification( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetCalcNotification();
+ return false;
+}
+
+void ScDocument::SetCalcNotification( SCTAB nTab )
+{
+ // set only if not set before
+ if (ScTable* pTable = FetchTable(nTab) ; pTable && !pTable->GetCalcNotification())
+ pTable->SetCalcNotification(true);
+}
+
+void ScDocument::ResetCalcNotifications()
+{
+ SCTAB nSize = GetTableCount();
+ for (SCTAB nTab = 0; nTab < nSize; nTab++)
+ if (maTabs[nTab] && maTabs[nTab]->GetCalcNotification())
+ maTabs[nTab]->SetCalcNotification(false);
+}
+
+ScOutlineTable* ScDocument::GetOutlineTable( SCTAB nTab, bool bCreate )
+{
+ ScOutlineTable* pVal = nullptr;
+
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ pVal = pTable->GetOutlineTable();
+ if (!pVal && bCreate)
+ {
+ pTable->StartOutlineTable();
+ pVal = pTable->GetOutlineTable();
+ }
+ }
+
+ return pVal;
+}
+
+bool ScDocument::SetOutlineTable( SCTAB nTab, const ScOutlineTable* pNewOutline )
+{
+ ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->SetOutlineTable(pNewOutline);
+}
+
+void ScDocument::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+bool ScDocument::TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& rParam )
+{
+ ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->TestRemoveSubTotals(rParam);
+}
+
+void ScDocument::RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->RemoveSubTotals( rParam );
+}
+
+bool ScDocument::DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
+{
+ ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->DoSubTotals(rParam);
+}
+
+bool ScDocument::HasSubTotalCells( const ScRange& rRange )
+{
+ ScCellIterator aIter(*this, rRange);
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ if (aIter.getFormulaCell()->IsSubTotal())
+ return true;
+ }
+ return false; // none found
+}
+
+/**
+ * From this document this method copies the cells of positions at which
+ * there are also cells in pPosDoc to pDestDoc
+ */
+void ScDocument::CopyUpdated( ScDocument* pPosDoc, ScDocument* pDestDoc )
+{
+ SCTAB nCount = GetTableCount();
+ for (SCTAB nTab=0; nTab<nCount; nTab++)
+ if (maTabs[nTab] && pPosDoc->maTabs[nTab] && pDestDoc->maTabs[nTab])
+ maTabs[nTab]->CopyUpdated( pPosDoc->maTabs[nTab].get(), pDestDoc->maTabs[nTab].get() );
+}
+
+void ScDocument::CopyScenario( SCTAB nSrcTab, SCTAB nDestTab, bool bNewScenario )
+{
+ if (!HasTable(nSrcTab) || !HasTable(nDestTab))
+ return;
+
+ // Set flags correctly for active scenarios
+ // and write current values back to recently active scenarios
+ ScRangeList aRanges = *maTabs[nSrcTab]->GetScenarioRanges();
+
+ // nDestTab is the target table
+ for ( SCTAB nTab = nDestTab+1;
+ nTab < GetTableCount() && maTabs[nTab] && maTabs[nTab]->IsScenario();
+ nTab++ )
+ {
+ if ( maTabs[nTab]->IsActiveScenario() ) // Even if it's the same scenario
+ {
+ bool bTouched = false;
+ for ( size_t nR=0, nRangeCount = aRanges.size(); nR < nRangeCount && !bTouched; nR++ )
+ {
+ const ScRange& rRange = aRanges[ nR ];
+ if ( maTabs[nTab]->HasScenarioRange( rRange ) )
+ bTouched = true;
+ }
+ if (bTouched)
+ {
+ maTabs[nTab]->SetActiveScenario(false);
+ if ( maTabs[nTab]->GetScenarioFlags() & ScScenarioFlags::TwoWay )
+ maTabs[nTab]->CopyScenarioFrom( maTabs[nDestTab].get() );
+ }
+ }
+ }
+
+ maTabs[nSrcTab]->SetActiveScenario(true); // This is where it's from ...
+ if (!bNewScenario) // Copy data from the selected scenario
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ maTabs[nSrcTab]->CopyScenarioTo( maTabs[nDestTab].get() );
+
+ sc::SetFormulaDirtyContext aCxt;
+ SetAllFormulasDirty(aCxt);
+ }
+}
+
+void ScDocument::MarkScenario( SCTAB nSrcTab, SCTAB nDestTab, ScMarkData& rDestMark,
+ bool bResetMark, ScScenarioFlags nNeededBits ) const
+{
+ if (bResetMark)
+ rDestMark.ResetMark();
+
+ if (const ScTable* pTable = FetchTable(nSrcTab))
+ pTable->MarkScenarioIn(rDestMark, nNeededBits);
+
+ rDestMark.SetAreaTab( nDestTab);
+}
+
+bool ScDocument::HasScenarioRange( SCTAB nTab, const ScRange& rRange ) const
+{
+ const ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->HasScenarioRange(rRange);
+}
+
+const ScRangeList* ScDocument::GetScenarioRanges( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetScenarioRanges();
+
+ return nullptr;
+}
+
+bool ScDocument::IsActiveScenario( SCTAB nTab ) const
+{
+ const ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->IsActiveScenario();
+}
+
+void ScDocument::SetActiveScenario( SCTAB nTab, bool bActive )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetActiveScenario( bActive );
+}
+
+bool ScDocument::TestCopyScenario( SCTAB nSrcTab, SCTAB nDestTab ) const
+{
+ if (HasTable(nSrcTab) && HasTable(nDestTab))
+ return maTabs[nSrcTab]->TestCopyScenarioTo(maTabs[nDestTab].get());
+
+ OSL_FAIL("wrong table at TestCopyScenario");
+ return false;
+}
+
+void ScDocument::AddUnoObject( SfxListener& rObject )
+{
+ if (!pUnoBroadcaster)
+ pUnoBroadcaster.reset( new SfxBroadcaster );
+
+ rObject.StartListening( *pUnoBroadcaster );
+}
+
+void ScDocument::RemoveUnoObject( SfxListener& rObject )
+{
+ if (pUnoBroadcaster)
+ {
+ rObject.EndListening( *pUnoBroadcaster );
+
+ if ( bInUnoBroadcast )
+ {
+ // Broadcasts from ScDocument::BroadcastUno are the only way that
+ // uno object methods are called without holding a reference.
+ //
+ // If RemoveUnoObject is called from an object dtor in the finalizer thread
+ // while the main thread is calling BroadcastUno, the dtor thread must wait
+ // (or the object's Notify might try to access a deleted object).
+ // The SolarMutex can't be locked here because if a component is called from
+ // a VCL event, the main thread has the SolarMutex locked all the time.
+ //
+ // This check is done after calling EndListening, so a later BroadcastUno call
+ // won't touch this object.
+
+ vcl::SolarMutexTryAndBuyGuard g;
+ if (g.isAcquired())
+ {
+ // BroadcastUno is always called with the SolarMutex locked, so if it
+ // can be acquired, this is within the same thread (should not happen)
+ OSL_FAIL( "RemoveUnoObject called from BroadcastUno" );
+ }
+ else
+ {
+ // Let the thread that called BroadcastUno continue
+ while ( bInUnoBroadcast )
+ {
+ osl::Thread::yield();
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("No Uno broadcaster");
+ }
+}
+
+void ScDocument::BroadcastUno( const SfxHint &rHint )
+{
+ if (!pUnoBroadcaster)
+ return;
+
+ bInUnoBroadcast = true;
+ pUnoBroadcaster->Broadcast( rHint );
+ bInUnoBroadcast = false;
+
+ // During Broadcast notification, Uno objects can add to pUnoListenerCalls.
+ // The listener calls must be processed after completing the broadcast,
+ // because they can add or remove objects from pUnoBroadcaster.
+
+ if ( pUnoListenerCalls &&
+ rHint.GetId() == SfxHintId::DataChanged &&
+ !bInUnoListenerCall )
+ {
+ // Listener calls may lead to BroadcastUno calls again. The listener calls
+ // are not nested, instead the calls are collected in the list, and the
+ // outermost call executes them all.
+
+ ScChartLockGuard aChartLockGuard(this);
+ bInUnoListenerCall = true;
+ pUnoListenerCalls->ExecuteAndClear();
+ bInUnoListenerCall = false;
+ }
+}
+
+void ScDocument::AddUnoListenerCall( const uno::Reference<util::XModifyListener>& rListener,
+ const lang::EventObject& rEvent )
+{
+ OSL_ENSURE( bInUnoBroadcast, "AddUnoListenerCall is supposed to be called from BroadcastUno only" );
+
+ if ( !pUnoListenerCalls )
+ pUnoListenerCalls.reset( new ScUnoListenerCalls );
+ pUnoListenerCalls->Add( rListener, rEvent );
+}
+
+void ScDocument::BeginUnoRefUndo()
+{
+ OSL_ENSURE( !pUnoRefUndoList, "BeginUnoRefUndo twice" );
+ pUnoRefUndoList.reset( new ScUnoRefList );
+}
+
+std::unique_ptr<ScUnoRefList> ScDocument::EndUnoRefUndo()
+{
+ return std::move(pUnoRefUndoList);
+ // Must be deleted by caller!
+}
+
+void ScDocument::AddUnoRefChange( sal_Int64 nId, const ScRangeList& rOldRanges )
+{
+ if ( pUnoRefUndoList )
+ pUnoRefUndoList->Add( nId, rOldRanges );
+}
+
+void ScDocument::UpdateReference(
+ sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, bool bIncludeDraw, bool bUpdateNoteCaptionPos )
+{
+ if (!ValidRange(rCxt.maRange) && !(rCxt.meMode == URM_INSDEL &&
+ ((rCxt.mnColDelta < 0 && // convention from ScDocument::DeleteCol()
+ rCxt.maRange.aStart.Col() == GetMaxColCount() && rCxt.maRange.aEnd.Col() == GetMaxColCount()) ||
+ (rCxt.mnRowDelta < 0 && // convention from ScDocument::DeleteRow()
+ rCxt.maRange.aStart.Row() == GetMaxRowCount() && rCxt.maRange.aEnd.Row() == GetMaxRowCount()))))
+ return;
+
+ std::unique_ptr<sc::ExpandRefsSwitch> pExpandRefsSwitch;
+ if (rCxt.isInserted())
+ pExpandRefsSwitch.reset(new sc::ExpandRefsSwitch(*this, SC_MOD()->GetInputOptions().GetExpandRefs()));
+
+ size_t nFirstTab, nLastTab;
+ if (rCxt.meMode == URM_COPY)
+ {
+ nFirstTab = rCxt.maRange.aStart.Tab();
+ nLastTab = rCxt.maRange.aEnd.Tab();
+ }
+ else
+ {
+ // TODO: Have these methods use the context object directly.
+ ScRange aRange = rCxt.maRange;
+ UpdateRefMode eUpdateRefMode = rCxt.meMode;
+ SCCOL nDx = rCxt.mnColDelta;
+ SCROW nDy = rCxt.mnRowDelta;
+ SCTAB nDz = rCxt.mnTabDelta;
+ SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
+ SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
+ SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
+
+ xColNameRanges->UpdateReference( eUpdateRefMode, this, aRange, nDx, nDy, nDz );
+ xRowNameRanges->UpdateReference( eUpdateRefMode, this, aRange, nDx, nDy, nDz );
+ pDBCollection->UpdateReference( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz );
+ if (pRangeName)
+ pRangeName->UpdateReference(rCxt);
+ if ( pDPCollection )
+ pDPCollection->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz );
+ UpdateChartRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz );
+ UpdateRefAreaLinks( eUpdateRefMode, aRange, nDx, nDy, nDz );
+ if ( pValidationList )
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateReference(rCxt);
+ }
+ if ( pDetOpList )
+ pDetOpList->UpdateReference( this, eUpdateRefMode, aRange, nDx, nDy, nDz );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint(
+ eUpdateRefMode, aRange, nDx, nDy, nDz ) );
+
+ nFirstTab = 0;
+ nLastTab = maTabs.size()-1;
+ }
+
+ for (size_t i = nFirstTab, n = maTabs.size() ; i <= nLastTab && i < n; ++i)
+ {
+ if (!maTabs[i])
+ continue;
+
+ maTabs[i]->UpdateReference(rCxt, pUndoDoc, bIncludeDraw, bUpdateNoteCaptionPos);
+ }
+
+ if ( bIsEmbedded )
+ {
+ SCCOL theCol1;
+ SCROW theRow1;
+ SCTAB theTab1;
+ SCCOL theCol2;
+ SCROW theRow2;
+ SCTAB theTab2;
+ theCol1 = aEmbedRange.aStart.Col();
+ theRow1 = aEmbedRange.aStart.Row();
+ theTab1 = aEmbedRange.aStart.Tab();
+ theCol2 = aEmbedRange.aEnd.Col();
+ theRow2 = aEmbedRange.aEnd.Row();
+ theTab2 = aEmbedRange.aEnd.Tab();
+
+ // TODO: Have ScRefUpdate::Update() use the context object directly.
+ UpdateRefMode eUpdateRefMode = rCxt.meMode;
+ SCCOL nDx = rCxt.mnColDelta;
+ SCROW nDy = rCxt.mnRowDelta;
+ SCTAB nDz = rCxt.mnTabDelta;
+ SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
+ SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
+ SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
+
+ if ( ScRefUpdate::Update( this, eUpdateRefMode, nCol1,nRow1,nTab1, nCol2,nRow2,nTab2,
+ nDx,nDy,nDz, theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) )
+ {
+ aEmbedRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
+ }
+ }
+
+ // After moving, no clipboard move ref-updates are possible
+ if (rCxt.meMode != URM_COPY && IsClipboardSource())
+ {
+ ScDocument* pClipDoc = ScModule::GetClipDoc();
+ if (pClipDoc)
+ pClipDoc->GetClipParam().mbCutMode = false;
+ }
+}
+
+void ScDocument::UpdateTranspose( const ScAddress& rDestPos, ScDocument* pClipDoc,
+ const ScMarkData& rMark, ScDocument* pUndoDoc )
+{
+ OSL_ENSURE(pClipDoc->bIsClip, "UpdateTranspose: No Clip");
+
+ ScRange aSource;
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ if (!rClipParam.maRanges.empty())
+ aSource = rClipParam.maRanges.front();
+ ScAddress aDest = rDestPos;
+
+ SCTAB nClipTab = 0;
+ for (SCTAB nDestTab = 0; nDestTab < GetTableCount() && maTabs[nDestTab]; nDestTab++)
+ if (rMark.GetTableSelect(nDestTab))
+ {
+ while (!pClipDoc->maTabs[nClipTab]) nClipTab = (nClipTab+1) % (MAXTAB+1);
+ aSource.aStart.SetTab( nClipTab );
+ aSource.aEnd.SetTab( nClipTab );
+ aDest.SetTab( nDestTab );
+
+ // Like UpdateReference
+ if (pRangeName)
+ pRangeName->UpdateTranspose( aSource, aDest ); // Before the cells!
+ for (SCTAB i = 0; i < GetTableCount(); i++)
+ if (maTabs[i])
+ maTabs[i]->UpdateTranspose( aSource, aDest, pUndoDoc );
+
+ nClipTab = (nClipTab+1) % (MAXTAB+1);
+ }
+}
+
+void ScDocument::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ //TODO: pDBCollection
+ //TODO: pPivotCollection
+ //TODO: UpdateChartRef
+
+ if (pRangeName)
+ pRangeName->UpdateGrow( rArea, nGrowX, nGrowY );
+
+ for (SCTAB i = 0; i < GetTableCount() && maTabs[i]; i++)
+ maTabs[i]->UpdateGrow( rArea, nGrowX, nGrowY );
+}
+
+void ScDocument::Fill(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScProgress* pProgress, const ScMarkData& rMark,
+ sal_uInt64 nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd,
+ double nStepValue, double nMaxValue)
+{
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ const ScRange& aRange = rMark.GetMarkArea();
+ SCTAB nMax = maTabs.size();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ {
+ maTabs[rTab]->Fill(nCol1, nRow1, nCol2, nRow2,
+ nFillCount, eFillDir, eFillCmd, eFillDateCmd,
+ nStepValue, tools::Duration(), nMaxValue, pProgress);
+ RefreshAutoFilter(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rTab);
+ }
+ }
+}
+
+OUString ScDocument::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY )
+{
+ SCTAB nTab = rSource.aStart.Tab();
+ if (nTab < GetTableCount() && maTabs[nTab])
+ return maTabs[nTab]->GetAutoFillPreview( rSource, nEndX, nEndY );
+
+ return OUString();
+}
+
+void ScDocument::AutoFormat( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ sal_uInt16 nFormatNo, const ScMarkData& rMark )
+{
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ SCTAB nMax = maTabs.size();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo );
+ }
+}
+
+void ScDocument::GetAutoFormatData(SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScAutoFormatData& rData)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ pTable->GetAutoFormatData(nStartCol, nStartRow, nEndCol, nEndRow, rData);
+ }
+}
+
+void ScDocument::GetSearchAndReplaceStart( const SvxSearchItem& rSearchItem,
+ SCCOL& rCol, SCROW& rRow )
+{
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ bool bReplace = ( nCommand == SvxSearchCmd::REPLACE ||
+ nCommand == SvxSearchCmd::REPLACE_ALL );
+ if ( rSearchItem.GetBackward() )
+ {
+ if ( rSearchItem.GetRowDirection() )
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = MaxCol();
+ rRow = MaxRow()+1;
+ }
+ else if ( bReplace )
+ {
+ rCol = MaxCol();
+ rRow = MaxRow();
+ }
+ else
+ {
+ rCol = MaxCol()+1;
+ rRow = MaxRow();
+ }
+ }
+ else
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = MaxCol()+1;
+ rRow = MaxRow();
+ }
+ else if ( bReplace )
+ {
+ rCol = MaxCol();
+ rRow = MaxRow();
+ }
+ else
+ {
+ rCol = MaxCol();
+ rRow = MaxRow()+1;
+ }
+ }
+ }
+ else
+ {
+ if ( rSearchItem.GetRowDirection() )
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = 0;
+ rRow = SCROW(-1);
+ }
+ else if ( bReplace )
+ {
+ rCol = 0;
+ rRow = 0;
+ }
+ else
+ {
+ rCol = SCCOL(-1);
+ rRow = 0;
+ }
+ }
+ else
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = SCCOL(-1);
+ rRow = 0;
+ }
+ else if ( bReplace )
+ {
+ rCol = 0;
+ rRow = 0;
+ }
+ else
+ {
+ rCol = 0;
+ rRow = SCROW(-1);
+ }
+ }
+ }
+}
+
+// static
+bool ScDocument::IsEmptyCellSearch( const SvxSearchItem& rSearchItem )
+{
+ return !rSearchItem.GetPattern() && (rSearchItem.GetCellType() != SvxSearchCellType::NOTE)
+ && (rSearchItem.GetSearchOptions().searchString.isEmpty()
+ || (rSearchItem.GetRegExp() && rSearchItem.GetSearchOptions().searchString == "^$"));
+}
+
+bool ScDocument::SearchAndReplace(
+ const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, SCTAB& rTab,
+ const ScMarkData& rMark, ScRangeList& rMatchedRanges,
+ OUString& rUndoStr, ScDocument* pUndoDoc, bool& bMatchedRangesWereClamped)
+{
+ // FIXME: Manage separated marks per table!
+ bool bFound = false;
+ if (rTab >= GetTableCount())
+ OSL_FAIL("table out of range");
+ if (ValidTab(rTab))
+ {
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ if ( nCommand == SvxSearchCmd::FIND_ALL ||
+ nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ SCTAB nMax = maTabs.size();
+ for (const auto& rMarkedTab : rMark)
+ {
+ if (rMarkedTab >= nMax)
+ break;
+ if (maTabs[rMarkedTab])
+ {
+ nCol = 0;
+ nRow = 0;
+ bFound |= maTabs[rMarkedTab]->SearchAndReplace(
+ rSearchItem, nCol, nRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc, bMatchedRangesWereClamped);
+ }
+ }
+
+ // Mark is set completely inside already
+ }
+ else
+ {
+ nCol = rCol;
+ nRow = rRow;
+ if (rSearchItem.GetBackward())
+ {
+ for (nTab = rTab; (nTab >= 0) && !bFound; nTab--)
+ if (maTabs[nTab])
+ {
+ if (rMark.GetTableSelect(nTab))
+ {
+ bFound = maTabs[nTab]->SearchAndReplace(
+ rSearchItem, nCol, nRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc, bMatchedRangesWereClamped);
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ rTab = nTab;
+ }
+ else
+ {
+ ScDocument::GetSearchAndReplaceStart(
+ rSearchItem, nCol, nRow );
+
+ // notify LibreOfficeKit about changed page
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = OString::number(nTab);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (nTab = rTab; (nTab < GetTableCount()) && !bFound; nTab++)
+ if (maTabs[nTab])
+ {
+ if (rMark.GetTableSelect(nTab))
+ {
+ bFound = maTabs[nTab]->SearchAndReplace(
+ rSearchItem, nCol, nRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc, bMatchedRangesWereClamped);
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ rTab = nTab;
+ }
+ else
+ {
+ ScDocument::GetSearchAndReplaceStart(
+ rSearchItem, nCol, nRow );
+
+ // notify LibreOfficeKit about changed page
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = OString::number(nTab);
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return bFound;
+}
+
+/**
+ * Adapt Outline
+ */
+bool ScDocument::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bShow )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->UpdateOutlineCol(nStartCol, nEndCol, bShow);
+
+ OSL_FAIL("missing tab");
+ return false;
+}
+
+bool ScDocument::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bShow )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->UpdateOutlineRow(nStartRow, nEndRow, bShow);
+
+ OSL_FAIL("missing tab");
+ return false;
+}
+
+void ScDocument::Sort(
+ SCTAB nTab, const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
+ ScProgress* pProgress, sc::ReorderParam* pUndo )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ bool bOldEnableIdle = IsIdleEnabled();
+ EnableIdle(false);
+ pTable->Sort(rSortParam, bKeepQuery, bUpdateRefs, pProgress, pUndo);
+ EnableIdle(bOldEnableIdle);
+ }
+}
+
+void ScDocument::Reorder( const sc::ReorderParam& rParam )
+{
+ ScTable* pTable = FetchTable(rParam.maSortRange.aStart.Tab());
+ if (!pTable)
+ return;
+
+ bool bOldEnableIdle = IsIdleEnabled();
+ EnableIdle(false);
+ pTable->Reorder(rParam);
+ EnableIdle(bOldEnableIdle);
+}
+
+void ScDocument::PrepareQuery( SCTAB nTab, ScQueryParam& rQueryParam )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->PrepareQuery(rQueryParam);
+ else
+ {
+ OSL_FAIL("missing tab");
+ }
+}
+
+SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->Query(rQueryParam, bKeepSub);
+
+ OSL_FAIL("missing tab");
+ return 0;
+}
+
+OUString ScDocument::GetUpperCellString(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetUpperCellString( nCol, nRow );
+ else
+ return OUString();
+}
+
+bool ScDocument::CreateQueryParam( const ScRange& rRange, ScQueryParam& rQueryParam )
+{
+ if (ScTable* pTable = FetchTable(rRange.aStart.Tab()))
+ return pTable->CreateQueryParam(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rQueryParam);
+
+ OSL_FAIL("missing tab");
+ return false;
+}
+
+bool ScDocument::HasAutoFilter( SCCOL nCurCol, SCROW nCurRow, SCTAB nCurTab )
+{
+ const ScDBData* pDBData = GetDBAtCursor( nCurCol, nCurRow, nCurTab, ScDBDataPortion::AREA );
+ bool bHasAutoFilter = (pDBData != nullptr);
+
+ if ( pDBData )
+ {
+ if ( pDBData->HasHeader() )
+ {
+ SCCOL nCol;
+ SCROW nRow;
+ ScMF nFlag;
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam( aParam );
+ nRow = aParam.nRow1;
+
+ for ( nCol=aParam.nCol1; nCol<=aParam.nCol2 && bHasAutoFilter; nCol++ )
+ {
+ nFlag = GetAttr( nCol, nRow, nCurTab, ATTR_MERGE_FLAG )->GetValue();
+
+ if ( !(nFlag & ScMF::Auto) )
+ bHasAutoFilter = false;
+ }
+ }
+ else
+ bHasAutoFilter = false;
+ }
+
+ return bHasAutoFilter;
+}
+
+bool ScDocument::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCTAB nTab )
+{
+ ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->HasColHeader(nStartCol, nStartRow, nEndCol, nEndRow);
+}
+
+bool ScDocument::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCTAB nTab )
+{
+ ScTable* pTable = FetchTable(nTab);
+ return pTable && pTable->HasRowHeader(nStartCol, nStartRow, nEndCol, nEndRow);
+}
+
+void ScDocument::GetFilterSelCount( SCCOL nCol, SCROW nRow, SCTAB nTab, SCSIZE& nSelected, SCSIZE& nTotal )
+{
+ nSelected = 0;
+ nTotal = 0;
+
+ if (HasTable(nTab))
+ {
+ ScDBData* pDBData = GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
+ if( pDBData && pDBData->HasAutoFilter() )
+ pDBData->GetFilterSelCount( nSelected, nTotal );
+ }
+}
+
+/**
+ * Entries for AutoFilter listbox
+ */
+void ScDocument::GetFilterEntries(
+ SCCOL nCol, SCROW nRow, SCTAB nTab, ScFilterEntries& rFilterEntries )
+{
+ if (!HasTable(nTab) || !pDBCollection)
+ return;
+
+ ScDBData* pDBData = pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA); //!??
+ if (!pDBData)
+ return;
+
+ pDBData->ExtendDataArea(*this);
+ SCTAB nAreaTab;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
+
+ if (pDBData->HasHeader())
+ ++nStartRow;
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam( aParam );
+
+ // Return all filter entries, if a filter condition is connected with a boolean OR
+ bool bFilter = true;
+ SCSIZE nEntryCount = aParam.GetEntryCount();
+ for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i )
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if ( rEntry.eConnect != SC_AND )
+ {
+ bFilter = false;
+ break;
+ }
+ }
+
+ if ( bFilter )
+ {
+ maTabs[nTab]->GetFilteredFilterEntries( nCol, nStartRow, nEndRow, aParam, rFilterEntries, bFilter );
+ }
+ else
+ {
+ maTabs[nTab]->GetFilterEntries( nCol, nStartRow, nEndRow, rFilterEntries );
+ }
+
+ sortAndRemoveDuplicates( rFilterEntries.maStrData, aParam.bCaseSens);
+}
+
+/**
+ * Entries for Filter dialog
+ */
+void ScDocument::GetFilterEntriesArea(
+ SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bCaseSens,
+ ScFilterEntries& rFilterEntries )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ pTable->GetFilterEntries(nCol, nStartRow, nEndRow, rFilterEntries, true);
+ sortAndRemoveDuplicates(rFilterEntries.maStrData, bCaseSens);
+ }
+}
+
+/**
+ * Entries for selection list listbox (no numbers/formulas)
+ */
+void ScDocument::GetDataEntries(
+ SCCOL nCol, SCROW nRow, SCTAB nTab,
+ std::vector<ScTypedStrData>& rStrings, bool bValidation )
+{
+ if( bValidation )
+ {
+ /* Try to generate the list from list validation. This part is skipped,
+ if bValidation==false, because in that case this function is called to get
+ cell values for auto completion on input. */
+ sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = GetValidationEntry( nValidation );
+ if( pData && pData->FillSelectionList( rStrings, ScAddress( nCol, nRow, nTab ) ) )
+ {
+ if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
+ sortAndRemoveDuplicates(rStrings, true/*bCaseSens*/);
+
+ return;
+ }
+ }
+ }
+
+ if (!HasTable(nTab))
+ return;
+
+ std::set<ScTypedStrData> aStrings;
+ if (maTabs[nTab]->GetDataEntries(nCol, nRow, aStrings))
+ {
+ rStrings.insert(rStrings.end(), aStrings.begin(), aStrings.end());
+ sortAndRemoveDuplicates(rStrings, true/*bCaseSens*/);
+ }
+}
+
+/**
+ * Entries for Formula auto input
+ */
+void ScDocument::GetFormulaEntries( ScTypedCaseStrSet& rStrings )
+{
+
+ // Range name
+ if ( pRangeName )
+ {
+ for (const auto& rEntry : *pRangeName)
+ rStrings.insert(ScTypedStrData(rEntry.second->GetName(), 0.0, 0.0, ScTypedStrData::Name));
+ }
+
+ // Database collection
+ if ( pDBCollection )
+ {
+ const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ rStrings.insert(ScTypedStrData(rxDB->GetName(), 0.0, 0.0, ScTypedStrData::DbName));
+ }
+
+ // Content of name ranges
+ ScRangePairList* pLists[2];
+ pLists[0] = GetColNameRanges();
+ pLists[1] = GetRowNameRanges();
+ for (ScRangePairList* pList : pLists)
+ {
+ if (!pList)
+ continue;
+
+ for ( size_t i = 0, nPairs = pList->size(); i < nPairs; ++i )
+ {
+ const ScRangePair & rPair = (*pList)[i];
+ const ScRange & rRange = rPair.GetRange(0);
+ ScCellIterator aIter( *this, rRange );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (!aIter.hasString())
+ continue;
+
+ OUString aStr = aIter.getString();
+ rStrings.insert(ScTypedStrData(aStr, 0.0, 0.0, ScTypedStrData::Header));
+ }
+ }
+ }
+}
+
+void ScDocument::GetEmbedded( ScRange& rRange ) const
+{
+ rRange = aEmbedRange;
+}
+
+tools::Rectangle ScDocument::GetEmbeddedRect() const // 1/100 mm
+{
+ tools::Rectangle aRect;
+ ScTable* pTable = nullptr;
+ if (aEmbedRange.aStart.Tab() < GetTableCount())
+ pTable = maTabs[aEmbedRange.aStart.Tab()].get();
+ else
+ OSL_FAIL("table out of range");
+ if (!pTable)
+ {
+ OSL_FAIL("GetEmbeddedRect without a table");
+ }
+ else
+ {
+ SCCOL i;
+
+ for (i=0; i<aEmbedRange.aStart.Col(); i++)
+ aRect.AdjustLeft(pTable->GetColWidth(i) );
+ aRect.AdjustTop(pTable->GetRowHeight( 0, aEmbedRange.aStart.Row() - 1) );
+ aRect.SetRight( aRect.Left() );
+ for (i=aEmbedRange.aStart.Col(); i<=aEmbedRange.aEnd.Col(); i++)
+ aRect.AdjustRight(pTable->GetColWidth(i) );
+ aRect.SetBottom( aRect.Top() );
+ aRect.AdjustBottom(pTable->GetRowHeight( aEmbedRange.aStart.Row(), aEmbedRange.aEnd.Row()) );
+
+ aRect = o3tl::convert(aRect, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ return aRect;
+}
+
+void ScDocument::SetEmbedded( const ScRange& rRange )
+{
+ bIsEmbedded = true;
+ aEmbedRange = rRange;
+}
+
+void ScDocument::ResetEmbedded()
+{
+ bIsEmbedded = false;
+ aEmbedRange = ScRange();
+}
+
+/** Similar to ScViewData::AddPixelsWhile(), but add height twips and only
+ while result is less than nStopTwips.
+ @return true if advanced at least one row.
+ */
+static bool lcl_AddTwipsWhile( tools::Long & rTwips, tools::Long nStopTwips, SCROW & rPosY, SCROW nEndRow, const ScTable * pTable, bool bHiddenAsZero )
+{
+ SCROW nRow = rPosY;
+ bool bAdded = false;
+ bool bStop = false;
+ while (rTwips < nStopTwips && nRow <= nEndRow && !bStop)
+ {
+ SCROW nHeightEndRow;
+ sal_uInt16 nHeight = pTable->GetRowHeight( nRow, nullptr, &nHeightEndRow, bHiddenAsZero );
+ if (nHeightEndRow > nEndRow)
+ nHeightEndRow = nEndRow;
+ if (!nHeight)
+ nRow = nHeightEndRow + 1;
+ else
+ {
+ SCROW nRows = nHeightEndRow - nRow + 1;
+ sal_Int64 nAdd = static_cast<sal_Int64>(nHeight) * nRows;
+ if (nAdd + rTwips >= nStopTwips)
+ {
+ sal_Int64 nDiff = nAdd + rTwips - nStopTwips;
+ nRows -= static_cast<SCROW>(nDiff / nHeight);
+ nAdd = static_cast<sal_Int64>(nHeight) * nRows;
+ // We're looking for a value that satisfies loop condition.
+ if (nAdd + rTwips >= nStopTwips)
+ {
+ --nRows;
+ nAdd -= nHeight;
+ }
+ bStop = true;
+ }
+ rTwips += static_cast<tools::Long>(nAdd);
+ nRow += nRows;
+ }
+ }
+ if (nRow > rPosY)
+ {
+ --nRow;
+ bAdded = true;
+ }
+ rPosY = nRow;
+ return bAdded;
+}
+
+ScRange ScDocument::GetRange( SCTAB nTab, const tools::Rectangle& rMMRect, bool bHiddenAsZero ) const
+{
+ ScTable* pTable = nullptr;
+ if (nTab < GetTableCount())
+ pTable = maTabs[nTab].get();
+ else
+ OSL_FAIL("table out of range");
+ if (!pTable)
+ {
+ OSL_FAIL("GetRange without a table");
+ return ScRange();
+ }
+
+ tools::Rectangle aPosRect = o3tl::convert(rMMRect, o3tl::Length::mm100, o3tl::Length::twip);
+ if ( IsNegativePage( nTab ) )
+ ScDrawLayer::MirrorRectRTL( aPosRect ); // Always with positive (LTR) values
+
+ tools::Long nSize;
+ tools::Long nTwips;
+ tools::Long nAdd;
+ bool bEnd;
+
+ nSize = 0;
+ nTwips = aPosRect.Left();
+
+ SCCOL nX1 = 0;
+ bEnd = false;
+ while (!bEnd)
+ {
+ nAdd = pTable->GetColWidth(nX1, bHiddenAsZero);
+ if (nSize+nAdd <= nTwips+1 && nX1<MaxCol())
+ {
+ nSize += nAdd;
+ ++nX1;
+ }
+ else
+ bEnd = true;
+ }
+
+
+ SCCOL nX2 = nX1;
+ if (!aPosRect.IsEmpty())
+ {
+ bEnd = false;
+ nTwips = aPosRect.Right();
+ while (!bEnd)
+ {
+ nAdd = pTable->GetColWidth(nX2, bHiddenAsZero);
+ if (nSize+nAdd < nTwips && nX2<MaxCol())
+ {
+ nSize += nAdd;
+ ++nX2;
+ }
+ else
+ bEnd = true;
+ }
+ }
+
+ nSize = 0;
+ nTwips = aPosRect.Top();
+
+ SCROW nY1 = 0;
+ // Was if(nSize+nAdd<=nTwips+1) inside loop => if(nSize+nAdd<nTwips+2)
+ if (lcl_AddTwipsWhile( nSize, nTwips+2, nY1, MaxRow(), pTable, bHiddenAsZero) && nY1 < MaxRow())
+ ++nY1; // original loop ended on last matched +1 unless that was rDoc.MaxRow()
+
+ SCROW nY2 = nY1;
+ if (!aPosRect.IsEmpty())
+ {
+ nTwips = aPosRect.Bottom();
+ // Was if(nSize+nAdd<nTwips) inside loop => if(nSize+nAdd<nTwips)
+ if (lcl_AddTwipsWhile( nSize, nTwips, nY2, MaxRow(), pTable, bHiddenAsZero) && nY2 < MaxRow())
+ ++nY2; // original loop ended on last matched +1 unless that was rDoc.MaxRow()
+ }
+
+ return ScRange( nX1,nY1,nTab, nX2,nY2,nTab );
+}
+
+void ScDocument::SetEmbedded( SCTAB nTab, const tools::Rectangle& rRect ) // From VisArea (1/100 mm)
+{
+ bIsEmbedded = true;
+ aEmbedRange = GetRange( nTab, rRect );
+}
+
+ScDocProtection* ScDocument::GetDocProtection() const
+{
+ return pDocProtection.get();
+}
+
+void ScDocument::SetDocProtection(const ScDocProtection* pProtect)
+{
+ if (pProtect)
+ pDocProtection.reset(new ScDocProtection(*pProtect));
+ else
+ pDocProtection.reset();
+}
+
+bool ScDocument::IsDocProtected() const
+{
+ return pDocProtection && pDocProtection->isProtected();
+}
+
+bool ScDocument::IsDocEditable() const
+{
+ // Import into read-only document is possible
+ return !IsDocProtected() && ( bImportingXML || mbChangeReadOnlyEnabled || !mpShell || !mpShell->IsReadOnly() );
+}
+
+bool ScDocument::IsTabProtected( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsProtected();
+
+ OSL_FAIL("Wrong table number");
+ return false;
+}
+
+const ScTableProtection* ScDocument::GetTabProtection(SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetProtection();
+
+ return nullptr;
+}
+
+void ScDocument::SetTabProtection(SCTAB nTab, const ScTableProtection* pProtect)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetProtection(pProtect);
+}
+
+void ScDocument::CopyTabProtection(SCTAB nTabSrc, SCTAB nTabDest)
+{
+ if (!HasTable(nTabSrc) || !HasTable(nTabDest))
+ return;
+
+ maTabs[nTabDest]->SetProtection( maTabs[nTabSrc]->GetProtection() );
+}
+
+const ScDocOptions& ScDocument::GetDocOptions() const
+{
+ assert(pDocOptions && "No DocOptions! :-(");
+ return *pDocOptions;
+}
+
+void ScDocument::SetDocOptions( const ScDocOptions& rOpt )
+{
+ assert(pDocOptions && "No DocOptions! :-(");
+
+ *pDocOptions = rOpt;
+ if (mxPoolHelper)
+ mxPoolHelper->SetFormTableOpt(rOpt);
+}
+
+const ScViewOptions& ScDocument::GetViewOptions() const
+{
+ assert(pViewOptions && "No ViewOptions! :-(");
+ return *pViewOptions;
+}
+
+void ScDocument::SetViewOptions( const ScViewOptions& rOpt )
+{
+ assert(pViewOptions && "No ViewOptions! :-(");
+ *pViewOptions = rOpt;
+}
+
+void ScDocument::GetLanguage( LanguageType& rLatin, LanguageType& rCjk, LanguageType& rCtl ) const
+{
+ rLatin = eLanguage;
+ rCjk = eCjkLanguage;
+ rCtl = eCtlLanguage;
+}
+
+void ScDocument::SetLanguage( LanguageType eLatin, LanguageType eCjk, LanguageType eCtl )
+{
+ eLanguage = eLatin;
+ eCjkLanguage = eCjk;
+ eCtlLanguage = eCtl;
+ if ( mxPoolHelper.is() )
+ {
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+ pPool->SetPoolDefaultItem( SvxLanguageItem( eLanguage, ATTR_FONT_LANGUAGE ) );
+ pPool->SetPoolDefaultItem( SvxLanguageItem( eCjkLanguage, ATTR_CJK_FONT_LANGUAGE ) );
+ pPool->SetPoolDefaultItem( SvxLanguageItem( eCtlLanguage, ATTR_CTL_FONT_LANGUAGE ) );
+ }
+
+ UpdateDrawLanguages(); // Set edit engine defaults in drawing layer pool
+}
+
+tools::Rectangle ScDocument::GetMMRect( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (!HasTable(nTab))
+ {
+ OSL_FAIL("GetMMRect: wrong table");
+ return tools::Rectangle(0,0,0,0);
+ }
+
+ SCCOL i;
+ tools::Rectangle aRect;
+
+ for (i=0; i<nStartCol; i++)
+ aRect.AdjustLeft(GetColWidth(i,nTab, bHiddenAsZero ) );
+ aRect.AdjustTop(GetRowHeight( 0, nStartRow-1, nTab, bHiddenAsZero ) );
+
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+
+ for (i=nStartCol; i<=nEndCol; i++)
+ aRect.AdjustRight(GetColWidth(i,nTab, bHiddenAsZero) );
+ aRect.AdjustBottom(GetRowHeight( nStartRow, nEndRow, nTab, bHiddenAsZero ) );
+
+ aRect = o3tl::convert(aRect, o3tl::Length::twip, o3tl::Length::mm100);
+
+ if ( IsNegativePage( nTab ) )
+ ScDrawLayer::MirrorRectRTL( aRect );
+
+ return aRect;
+}
+
+void ScDocument::SetExtDocOptions( std::unique_ptr<ScExtDocOptions> pNewOptions )
+{
+ pExtDocOptions = std::move(pNewOptions);
+}
+
+void ScDocument::SetClipOptions(std::unique_ptr<ScClipOptions> pClipOptions)
+{
+ mpClipOptions = std::move(pClipOptions);
+}
+
+void ScDocument::DoMergeContents( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ OUStringBuffer aTotal;
+ OUString aCellStr;
+ SCCOL nCol;
+ SCROW nRow;
+ ScCellValue aCell;
+ for (nRow=nStartRow; nRow<=nEndRow; nRow++)
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ aCellStr = GetString(nCol, nRow, nTab);
+ if (!aCellStr.isEmpty())
+ {
+ if (!aTotal.isEmpty())
+ aTotal.append(' ');
+ aTotal.append(aCellStr);
+ ScAddress aPos(nCol, nRow, nTab);
+ if ((GetCellType(aPos) == CELLTYPE_EDIT) && aCell.isEmpty())
+ aCell = ScRefCellValue(*this, aPos);
+ }
+ if (nCol != nStartCol || nRow != nStartRow)
+ SetString(nCol,nRow,nTab,"");
+ }
+
+ if (aCell.isEmpty() || !GetString(nStartCol, nStartRow, nTab).isEmpty())
+ SetString(nStartCol, nStartRow, nTab, aTotal.makeStringAndClear());
+ else
+ aCell.release(*this, ScAddress(nStartCol, nStartRow, nTab));
+}
+
+void ScDocument::DoEmptyBlock( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ for (nRow=nStartRow; nRow<=nEndRow; nRow++)
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ { // empty block except first cell
+ if (nCol != nStartCol || nRow != nStartRow)
+ SetString(nCol,nRow,nTab,"");
+ }
+}
+
+void ScDocument::DoMerge( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, bool bDeleteCaptions )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->SetMergedCells(nStartCol, nStartRow, nEndCol, nEndRow);
+
+ // Remove all covered notes (removed captions are collected by drawing undo if active)
+ InsertDeleteFlags nDelFlag = InsertDeleteFlags::NOTE | (bDeleteCaptions ? InsertDeleteFlags::NONE : InsertDeleteFlags::NOCAPTIONS);
+ if( nStartCol < nEndCol )
+ DeleteAreaTab( nStartCol + 1, nStartRow, nEndCol, nStartRow, nTab, nDelFlag );
+ if( nStartRow < nEndRow )
+ DeleteAreaTab( nStartCol, nStartRow + 1, nEndCol, nEndRow, nTab, nDelFlag );
+}
+
+void ScDocument::RemoveMerge( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ const ScMergeAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE );
+
+ if ( pAttr->GetColMerge() <= 1 && pAttr->GetRowMerge() <= 1 )
+ return;
+
+ SCCOL nEndCol = nCol + pAttr->GetColMerge() - 1;
+ SCROW nEndRow = nRow + pAttr->GetRowMerge() - 1;
+
+ RemoveFlagsTab( nCol, nRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
+
+ const ScMergeAttr* pDefAttr = &mxPoolHelper->GetDocPool()->GetDefaultItem( ATTR_MERGE );
+ ApplyAttr( nCol, nRow, nTab, *pDefAttr );
+}
+
+void ScDocument::ExtendPrintArea( OutputDevice* pDev, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow ) const
+{
+ if (HasTable(nTab))
+ maTabs[nTab]->ExtendPrintArea(pDev, nStartCol, nStartRow, rEndCol, nEndRow);
+}
+
+SCSIZE ScDocument::GetPatternCount( SCTAB nTab, SCCOL nCol ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPatternCount( nCol );
+ else
+ return 0;
+}
+
+SCSIZE ScDocument::GetPatternCount( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPatternCount(nCol, nRow1, nRow2);
+ return 0;
+}
+
+void ScDocument::ReservePatternCount( SCTAB nTab, SCCOL nCol, SCSIZE nReserve )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ReservePatternCount(nCol, nReserve);
+}
+
+void ScDocument::GetSortParam( ScSortParam& rParam, SCTAB nTab )
+{
+ rParam = mSheetSortParams[ nTab ];
+}
+
+void ScDocument::SetSortParam( const ScSortParam& rParam, SCTAB nTab )
+{
+ mSheetSortParams[ nTab ] = rParam;
+}
+
+SCCOL ScDocument::ClampToAllocatedColumns(SCTAB nTab, SCCOL nCol) const
+{
+ return maTabs[nTab]->ClampToAllocatedColumns(nCol);
+}
+
+SCCOL ScDocument::GetAllocatedColumnsCount(SCTAB nTab) const
+{
+ return maTabs[nTab]->GetAllocatedColumnsCount();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen4.cxx b/sc/source/core/data/documen4.cxx
new file mode 100644
index 0000000000..e9a4fea132
--- /dev/null
+++ b/sc/source/core/data/documen4.cxx
@@ -0,0 +1,1360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <formula/token.hxx>
+#include <sal/log.hxx>
+#include <unotools/configmgr.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <table.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <subtotal.hxx>
+#include <docoptio.hxx>
+#include <markdata.hxx>
+#include <validat.hxx>
+#include <scitems.hxx>
+#include <stlpool.hxx>
+#include <poolhelp.hxx>
+#include <detdata.hxx>
+#include <patattr.hxx>
+#include <chgtrack.hxx>
+#include <progress.hxx>
+#include <paramisc.hxx>
+#include <compiler.hxx>
+#include <externalrefmgr.hxx>
+#include <attrib.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <tokenstringcontext.hxx>
+#include <memory>
+
+using namespace formula;
+
+/** (Goal Seek) Find a value of x that is a root of f(x)
+
+ This function is used internally for the goal seek operation. It uses the
+ Regula Falsi (aka false position) algorithm to find a root of f(x). The
+ start value and the target value are to be given by the user in the
+ goal seek dialog. The f(x) in this case is defined as the formula in the
+ formula cell minus target value. This function may also perform additional
+ search in the horizontal directions when the f(x) is discrete in order to
+ ensure a non-zero slope necessary for deriving a subsequent x that is
+ reasonably close to the root of interest.
+
+ @change 24.10.2004 by Kohei Yoshida (kohei@openoffice.org)
+
+ @see #i28955#
+
+ @change 6 Aug 2013, fdo37341
+*/
+bool ScDocument::Solver(SCCOL nFCol, SCROW nFRow, SCTAB nFTab,
+ SCCOL nVCol, SCROW nVRow, SCTAB nVTab,
+ const OUString& sValStr, double& nX)
+{
+ bool bRet = false;
+ nX = 0.0;
+ if ( ValidColRow( nFCol, nFRow ) && ValidTab( nFTab ) &&
+ ValidColRow( nVCol, nVRow ) && ValidTab( nVTab ) &&
+ nFTab < GetTableCount() && maTabs[nFTab] &&
+ nVTab < GetTableCount() && maTabs[nVTab] )
+ {
+ CellType eFType = GetCellType(nFCol, nFRow, nFTab);
+ CellType eVType = GetCellType(nVCol, nVRow, nVTab);
+ // #i108005# convert target value to number using default format,
+ // as previously done in ScInterpreter::GetDouble
+ ScFormulaCell* pFormula = nullptr;
+ double fTargetVal = 0.0;
+ sal_uInt32 nFIndex = 0;
+ if ( eFType == CELLTYPE_FORMULA && eVType == CELLTYPE_VALUE &&
+ GetFormatTable()->IsNumberFormat( sValStr, nFIndex, fTargetVal ) )
+ {
+ ScAddress aFormulaAdr( nFCol, nFRow, nFTab );
+ pFormula = GetFormulaCell( aFormulaAdr );
+ }
+ if (pFormula)
+ {
+ bool bDoneIteration = false;
+ ScAddress aValueAdr( nVCol, nVRow, nVTab );
+ double* pVCell = GetValueCell( aValueAdr );
+
+ ScRange aVRange( aValueAdr, aValueAdr ); // for SetDirty
+ // Original value to be restored later if necessary
+ double fSaveVal = *pVCell;
+
+ const sal_uInt16 nMaxIter = 100;
+ const double fEps = 1E-10;
+ const double fDelta = 1E-6;
+
+ double fBestX, fXPrev;
+ double fBestF, fFPrev;
+ fBestX = fXPrev = fSaveVal;
+
+ pFormula->Interpret();
+ bool bError = ( pFormula->GetErrCode() != FormulaError::NONE );
+ // bError always corresponds with fF
+
+ fFPrev = pFormula->GetValue() - fTargetVal;
+
+ fBestF = fabs( fFPrev );
+ if ( fBestF < fDelta )
+ bDoneIteration = true;
+
+ double fX = fXPrev + fEps;
+ double fF = fFPrev;
+ double fSlope;
+
+ sal_uInt16 nIter = 0;
+
+ bool bHorMoveError = false;
+ // Conform Regula Falsi Method
+ while ( !bDoneIteration && ( nIter++ < nMaxIter ) )
+ {
+ *pVCell = fX;
+ SetDirty( aVRange, false );
+ pFormula->Interpret();
+ bError = ( pFormula->GetErrCode() != FormulaError::NONE );
+ fF = pFormula->GetValue() - fTargetVal;
+
+ if ( fF == fFPrev && !bError )
+ {
+ // HORIZONTAL SEARCH: Keep moving x in both directions until the f(x)
+ // becomes different from the previous f(x). This routine is needed
+ // when a given function is discrete, in which case the resulting slope
+ // may become zero which ultimately causes the goal seek operation
+ // to fail. #i28955#
+
+ sal_uInt16 nHorIter = 0;
+ const double fHorStepAngle = 5.0;
+ const double fHorMaxAngle = 80.0;
+ int const nHorMaxIter = static_cast<int>( fHorMaxAngle / fHorStepAngle );
+ bool bDoneHorMove = false;
+
+ while ( !bDoneHorMove && !bHorMoveError && nHorIter++ < nHorMaxIter )
+ {
+ double fHorAngle = fHorStepAngle * static_cast<double>( nHorIter );
+ double fHorTangent = std::tan(basegfx::deg2rad(fHorAngle));
+
+ sal_uInt16 nIdx = 0;
+ while( nIdx++ < 2 && !bDoneHorMove )
+ {
+ double fHorX;
+ if ( nIdx == 1 )
+ fHorX = fX + fabs( fF ) * fHorTangent;
+ else
+ fHorX = fX - fabs( fF ) * fHorTangent;
+
+ *pVCell = fHorX;
+ SetDirty( aVRange, false );
+ pFormula->Interpret();
+ bHorMoveError = ( pFormula->GetErrCode() != FormulaError::NONE );
+ if ( bHorMoveError )
+ break;
+
+ fF = pFormula->GetValue() - fTargetVal;
+ if ( fF != fFPrev )
+ {
+ fX = fHorX;
+ bDoneHorMove = true;
+ }
+ }
+ }
+ if ( !bDoneHorMove )
+ bHorMoveError = true;
+ }
+
+ if ( bError )
+ {
+ // move closer to last valid value (fXPrev), keep fXPrev & fFPrev
+ double fDiff = ( fXPrev - fX ) / 2;
+ if ( fabs( fDiff ) < fEps )
+ fDiff = ( fDiff < 0.0 ? - fEps : fEps );
+ fX += fDiff;
+ }
+ else if ( bHorMoveError )
+ break;
+ else if ( fabs(fF) < fDelta )
+ {
+ // converged to root
+ fBestX = fX;
+ bDoneIteration = true;
+ }
+ else
+ {
+ if ( fabs(fF) + fDelta < fBestF )
+ {
+ fBestX = fX;
+ fBestF = fabs( fF );
+ }
+
+ if ( ( fXPrev - fX ) != 0 )
+ {
+ fSlope = ( fFPrev - fF ) / ( fXPrev - fX );
+ if ( fabs( fSlope ) < fEps )
+ fSlope = fSlope < 0.0 ? -fEps : fEps;
+ }
+ else
+ fSlope = fEps;
+
+ fXPrev = fX;
+ fFPrev = fF;
+ fX = fX - ( fF / fSlope );
+ }
+ }
+
+ // Try a nice rounded input value if possible.
+ const double fNiceDelta = ( bDoneIteration && fabs( fBestX ) >= 1e-3 ? 1e-3 : fDelta );
+ nX = ::rtl::math::approxFloor( ( fBestX / fNiceDelta ) + 0.5 ) * fNiceDelta;
+
+ if ( bDoneIteration )
+ {
+ *pVCell = nX;
+ SetDirty( aVRange, false );
+ pFormula->Interpret();
+ if ( fabs( pFormula->GetValue() - fTargetVal ) > fabs( fF ) )
+ nX = fBestX;
+ bRet = true;
+ }
+ else if ( bError || bHorMoveError )
+ {
+ nX = fBestX;
+ }
+ *pVCell = fSaveVal;
+ SetDirty( aVRange, false );
+ pFormula->Interpret();
+ if ( !bDoneIteration )
+ {
+ SetError( nVCol, nVRow, nVTab, FormulaError::NotAvailable );
+ }
+ }
+ else
+ {
+ SetError( nVCol, nVRow, nVTab, FormulaError::NotAvailable );
+ }
+ }
+ return bRet;
+}
+
+void ScDocument::InsertMatrixFormula(SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark,
+ const OUString& rFormula,
+ const ScTokenArray* pArr,
+ const formula::FormulaGrammar::Grammar eGram )
+{
+ PutInOrder(nCol1, nCol2);
+ PutInOrder(nRow1, nRow2);
+ nCol2 = std::min<SCCOL>(nCol2, MaxCol());
+ nRow2 = std::min<SCROW>(nRow2, MaxRow());
+ if (!rMark.GetSelectCount())
+ {
+ SAL_WARN("sc", "ScDocument::InsertMatrixFormula: No table marked");
+ return;
+ }
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ // just too slow
+ if (nCol2 - nCol1 > 64)
+ return;
+ if (nRow2 - nRow1 > 64)
+ return;
+ }
+ assert( ValidColRow( nCol1, nRow1) && ValidColRow( nCol2, nRow2));
+
+ SCTAB nTab1 = *rMark.begin();
+
+ ScFormulaCell* pCell;
+ ScAddress aPos( nCol1, nRow1, nTab1 );
+ if (pArr)
+ pCell = new ScFormulaCell(*this, aPos, *pArr, eGram, ScMatrixMode::Formula);
+ else
+ pCell = new ScFormulaCell(*this, aPos, rFormula, eGram, ScMatrixMode::Formula);
+ pCell->SetMatColsRows( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1 );
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (!maTabs[rTab])
+ continue;
+
+ if (rTab == nTab1)
+ {
+ pCell = maTabs[rTab]->SetFormulaCell(nCol1, nRow1, pCell);
+ if (!pCell) //NULL if nCol1/nRow1 is invalid, which it can't be here
+ break;
+ }
+ else
+ maTabs[rTab]->SetFormulaCell(
+ nCol1, nRow1,
+ new ScFormulaCell(
+ *pCell, *this, ScAddress(nCol1, nRow1, rTab), ScCloneFlags::StartListening));
+ }
+
+ ScSingleRefData aRefData;
+ aRefData.InitFlags();
+ aRefData.SetRelCol(0);
+ aRefData.SetRelRow(0);
+ aRefData.SetRelTab(0); // 2D matrix, always same sheet
+
+ ScTokenArray aArr(*this); // consists only of one single reference token.
+ formula::FormulaToken* t = aArr.AddMatrixSingleReference(aRefData);
+
+ for (const SCTAB& nTab : rMark)
+ {
+ if (nTab >= nMax)
+ break;
+
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ for (SCCOL nCol : GetWritableColumnsRange(nTab, nCol1, nCol2))
+ {
+ aRefData.SetRelCol(nCol1 - nCol);
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ if (nCol == nCol1 && nRow == nRow1)
+ // Skip the base position.
+ continue;
+
+ // Reference in each cell must point to the origin cell relative to the current cell.
+ aRefData.SetRelRow(nRow1 - nRow);
+ *t->GetSingleRef() = aRefData;
+ // Token array must be cloned so that each formula cell receives its own copy.
+ ScTokenArray aTokArr(aArr.CloneValue());
+ aPos = ScAddress(nCol, nRow, nTab);
+ pCell = new ScFormulaCell(*this, aPos, aTokArr, eGram, ScMatrixMode::Reference);
+ pTab->SetFormulaCell(nCol, nRow, pCell);
+ }
+ }
+ }
+}
+
+void ScDocument::InsertTableOp(const ScTabOpParam& rParam, // multiple (repeated?) operation
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark)
+{
+ PutInOrder(nCol1, nCol2);
+ PutInOrder(nRow1, nRow2);
+ assert( ValidColRow( nCol1, nRow1) && ValidColRow( nCol2, nRow2));
+ SCTAB i, nTab1;
+ SCCOL j;
+ SCROW k;
+ i = 0;
+ bool bStop = false;
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ {
+ i = rTab;
+ bStop = true;
+ break;
+ }
+ }
+ nTab1 = i;
+ if (!bStop)
+ {
+ OSL_FAIL("ScDocument::InsertTableOp: No table marked");
+ return;
+ }
+
+ ScRefAddress aRef;
+ OUStringBuffer aForString("="
+ + ScCompiler::GetNativeSymbol(ocTableOp)
+ + ScCompiler::GetNativeSymbol( ocOpen));
+
+ const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
+ if (rParam.meMode == ScTabOpParam::Column) // column only
+ {
+ aRef.Set( rParam.aRefFormulaCell.GetAddress(), true, false, false );
+ aForString.append(aRef.GetRefString(*this, nTab1)
+ + sSep
+ + rParam.aRefColCell.GetRefString(*this, nTab1)
+ + sSep);
+ aRef.Set( nCol1, nRow1, nTab1, false, true, true );
+ aForString.append(aRef.GetRefString(*this, nTab1));
+ nCol1++;
+ nCol2 = std::min( nCol2, static_cast<SCCOL>(rParam.aRefFormulaEnd.Col() -
+ rParam.aRefFormulaCell.Col() + nCol1 + 1));
+ }
+ else if (rParam.meMode == ScTabOpParam::Row) // row only
+ {
+ aRef.Set( rParam.aRefFormulaCell.GetAddress(), false, true, false );
+ aForString.append(aRef.GetRefString(*this, nTab1)
+ + sSep
+ + rParam.aRefRowCell.GetRefString(*this, nTab1)
+ + sSep);
+ aRef.Set( nCol1, nRow1, nTab1, true, false, true );
+ aForString.append(aRef.GetRefString(*this, nTab1));
+ nRow1++;
+ nRow2 = std::min( nRow2, static_cast<SCROW>(rParam.aRefFormulaEnd.Row() -
+ rParam.aRefFormulaCell.Row() + nRow1 + 1));
+ }
+ else // both
+ {
+ aForString.append(rParam.aRefFormulaCell.GetRefString(*this, nTab1)
+ + sSep
+ + rParam.aRefColCell.GetRefString(*this, nTab1)
+ + sSep);
+ aRef.Set( nCol1, nRow1 + 1, nTab1, false, true, true );
+ aForString.append(aRef.GetRefString(*this, nTab1)
+ + sSep
+ + rParam.aRefRowCell.GetRefString(*this, nTab1)
+ + sSep);
+ aRef.Set( nCol1 + 1, nRow1, nTab1, true, false, true );
+ aForString.append(aRef.GetRefString(*this, nTab1));
+ nCol1++; nRow1++;
+ }
+ aForString.append(ScCompiler::GetNativeSymbol( ocClose ));
+
+ ScFormulaCell aRefCell( *this, ScAddress( nCol1, nRow1, nTab1 ), aForString.makeStringAndClear(),
+ formula::FormulaGrammar::GRAM_NATIVE, ScMatrixMode::NONE );
+ for( j = nCol1; j <= nCol2; j++ )
+ for( k = nRow1; k <= nRow2; k++ )
+ for (i = 0; i < GetTableCount(); i++)
+ {
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if( maTabs[rTab] )
+ maTabs[rTab]->SetFormulaCell(
+ j, k, new ScFormulaCell(aRefCell, *this, ScAddress(j, k, rTab), ScCloneFlags::StartListening));
+ }
+ }
+}
+
+namespace {
+
+bool setCacheTableReferenced(const ScDocument& rDoc, formula::FormulaToken& rToken, ScExternalRefManager& rRefMgr, const ScAddress& rPos)
+{
+ switch (rToken.GetType())
+ {
+ case svExternalSingleRef:
+ return rRefMgr.setCacheTableReferenced(
+ rToken.GetIndex(), rToken.GetString().getString(), 1);
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = *rToken.GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(rDoc, rPos);
+ size_t nSheets = aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1;
+ return rRefMgr.setCacheTableReferenced(
+ rToken.GetIndex(), rToken.GetString().getString(), nSheets);
+ }
+ case svExternalName:
+ /* TODO: external names aren't supported yet, but would
+ * have to be marked as well, if so. Mechanism would be
+ * different. */
+ OSL_FAIL("ScDocument::MarkUsedExternalReferences: implement the svExternalName case!");
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+}
+
+bool ScDocument::MarkUsedExternalReferences( const ScTokenArray& rArr, const ScAddress& rPos )
+{
+ if (!rArr.GetLen())
+ return false;
+
+ ScExternalRefManager* pRefMgr = nullptr;
+ formula::FormulaTokenArrayPlainIterator aIter( rArr );
+ bool bAllMarked = false;
+ while (!bAllMarked)
+ {
+ formula::FormulaToken* t = aIter.GetNextReferenceOrName();
+ if (!t)
+ break;
+ if (t->IsExternalRef())
+ {
+ if (!pRefMgr)
+ pRefMgr = GetExternalRefManager();
+
+ bAllMarked = setCacheTableReferenced(*this, *t, *pRefMgr, rPos);
+ }
+ else if (t->GetType() == svIndex)
+ {
+ // this is a named range. Check if the range contains an external
+ // reference.
+ ScRangeData* pRangeData = GetRangeName()->findByIndex(t->GetIndex());
+ if (!pRangeData)
+ continue;
+
+ ScTokenArray* pArray = pRangeData->GetCode();
+ formula::FormulaTokenArrayPlainIterator aArrayIter(*pArray);
+ for (t = aArrayIter.First(); t; t = aArrayIter.Next())
+ {
+ if (!t->IsExternalRef())
+ continue;
+
+ if (!pRefMgr)
+ pRefMgr = GetExternalRefManager();
+
+ bAllMarked = setCacheTableReferenced(*this, *t, *pRefMgr, rPos);
+ }
+ }
+ }
+ return bAllMarked;
+}
+
+bool ScDocument::GetNextSpellingCell(SCCOL& nCol, SCROW& nRow, SCTAB nTab,
+ bool bInSel, const ScMarkData& rMark) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNextSpellingCell( nCol, nRow, bInSel, rMark );
+ return false;
+}
+
+bool ScDocument::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, SCTAB nTab,
+ const ScMarkData& rMark )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNextMarkedCell( rCol, rRow, rMark );
+ return false;
+}
+
+void ScDocument::ReplaceStyle(const SvxSearchItem& rSearchItem,
+ SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const ScMarkData& rMark)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ReplaceStyle(rSearchItem, nCol, nRow, rMark, true/*bIsUndoP*/);
+}
+
+void ScDocument::CompileDBFormula()
+{
+ sc::CompileFormulaContext aCxt(*this);
+ for (auto& rxTab : maTabs)
+ {
+ if (rxTab)
+ rxTab->CompileDBFormula(aCxt);
+ }
+}
+
+void ScDocument::CompileColRowNameFormula()
+{
+ sc::CompileFormulaContext aCxt(*this);
+ for (auto& rxTab : maTabs)
+ {
+ if (rxTab)
+ rxTab->CompileColRowNameFormula(aCxt);
+ }
+}
+
+void ScDocument::InvalidateTableArea()
+{
+ for (auto& rxTab : maTabs)
+ {
+ if (!rxTab)
+ break;
+ rxTab->InvalidateTableArea();
+ if ( rxTab->IsScenario() )
+ rxTab->InvalidateScenarioRanges();
+ }
+}
+
+sal_Int32 ScDocument::GetMaxStringLen( SCTAB nTab, SCCOL nCol,
+ SCROW nRowStart, SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetMaxStringLen(nCol, nRowStart, nRowEnd, eCharSet);
+ return 0;
+}
+
+sal_Int32 ScDocument::GetMaxNumberStringLen( sal_uInt16& nPrecision, SCTAB nTab,
+ SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetMaxNumberStringLen(nPrecision, nCol, nRowStart, nRowEnd);
+ return 0;
+}
+
+bool ScDocument::GetSelectionFunction( ScSubTotalFunc eFunc,
+ const ScAddress& rCursor, const ScMarkData& rMark,
+ double& rResult )
+{
+ ScFunctionData aData(eFunc);
+
+ ScMarkData aMark(rMark);
+ aMark.MarkToMulti();
+ if (!aMark.IsMultiMarked() && !aMark.IsCellMarked(rCursor.Col(), rCursor.Row()))
+ aMark.SetMarkArea(rCursor);
+
+ SCTAB nMax = GetTableCount();
+ ScMarkData::const_iterator itr = aMark.begin(), itrEnd = aMark.end();
+
+ for (; itr != itrEnd && *itr < nMax && !aData.getError(); ++itr)
+ if (maTabs[*itr])
+ maTabs[*itr]->UpdateSelectionFunction(aData, aMark);
+
+ rResult = aData.getResult();
+ if (aData.getError())
+ rResult = 0.0;
+
+ return !aData.getError();
+}
+
+double ScDocument::RoundValueAsShown( double fVal, sal_uInt32 nFormat, const ScInterpreterContext* pContext ) const
+{
+ const SvNumberFormatter* pFormatter = pContext ? pContext->GetFormatTable() : GetFormatTable();
+ const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
+ if (!pFormat)
+ return fVal;
+ SvNumFormatType nType = pFormat->GetMaskedType();
+ if (nType != SvNumFormatType::DATE && nType != SvNumFormatType::TIME && nType != SvNumFormatType::DATETIME )
+ {
+ // MSVC doesn't recognize all paths init nPrecision and wails about
+ // "potentially uninitialized local variable 'nPrecision' used"
+ // so init to some random sensible value preserving all decimals.
+ short nPrecision = 20;
+ bool bStdPrecision = ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0);
+ if (!bStdPrecision)
+ {
+ sal_uInt16 nIdx = pFormat->GetSubformatIndex( fVal );
+ nPrecision = static_cast<short>(pFormat->GetFormatPrecision( nIdx ));
+ switch ( nType )
+ {
+ case SvNumFormatType::PERCENT: // 0.41% == 0.0041
+ nPrecision += 2;
+ break;
+ case SvNumFormatType::SCIENTIFIC: // 1.23e-3 == 0.00123
+ {
+ short nExp = 0;
+ if ( fVal > 0.0 )
+ nExp = static_cast<short>(floor( log10( fVal ) ));
+ else if ( fVal < 0.0 )
+ nExp = static_cast<short>(floor( log10( -fVal ) ));
+ nPrecision -= nExp;
+ short nInteger = static_cast<short>(pFormat->GetFormatIntegerDigits( nIdx ));
+ if ( nInteger > 1 ) // Engineering notation
+ {
+ short nIncrement = nExp % nInteger;
+ if ( nIncrement != 0 )
+ {
+ nPrecision += nIncrement;
+ if (nExp < 0 )
+ nPrecision += nInteger;
+ }
+ }
+ break;
+ }
+ case SvNumFormatType::FRACTION: // get value of fraction representation
+ {
+ return pFormat->GetRoundFractionValue( fVal );
+ }
+ case SvNumFormatType::NUMBER:
+ case SvNumFormatType::CURRENCY:
+ { // tdf#106253 Thousands divisors for format "0,"
+ const sal_uInt16 nTD = pFormat->GetThousandDivisorPrecision( nIdx );
+ if (nTD == SvNumberFormatter::UNLIMITED_PRECISION)
+ // Format contains General keyword, handled below.
+ bStdPrecision = true;
+ else
+ nPrecision -= nTD;
+ break;
+ }
+ default: break;
+ }
+ }
+ if (bStdPrecision)
+ {
+ nPrecision = static_cast<short>(GetDocOptions().GetStdPrecision());
+ // #i115512# no rounding for automatic decimals
+ if (nPrecision == static_cast<short>(SvNumberFormatter::UNLIMITED_PRECISION))
+ return fVal;
+ }
+ double fRound = ::rtl::math::round( fVal, nPrecision );
+ if ( ::rtl::math::approxEqual( fVal, fRound ) )
+ return fVal; // rounding might introduce some error
+ else
+ return fRound;
+ }
+ else
+ return fVal;
+}
+
+// conditional formats and validation ranges
+
+sal_uLong ScDocument::AddCondFormat( std::unique_ptr<ScConditionalFormat> pNew, SCTAB nTab )
+{
+ if(!pNew)
+ return 0;
+
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->AddCondFormat(std::move(pNew));
+
+ return 0;
+}
+
+sal_uLong ScDocument::AddValidationEntry( const ScValidationData& rNew )
+{
+ if (rNew.IsEmpty())
+ return 0; // empty is always 0
+
+ if (!pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList.reset(new ScValidationDataList);
+ }
+
+ sal_uLong nMax = 0;
+ for( const auto& rxData : *pValidationList )
+ {
+ const ScValidationData* pData = rxData.get();
+ sal_uLong nKey = pData->GetKey();
+ if ( pData->EqualEntries( rNew ) )
+ return nKey;
+ if ( nKey > nMax )
+ nMax = nKey;
+ }
+
+ // might be called from ScPatternAttr::PutInPool; thus clone (real copy)
+
+ sal_uLong nNewKey = nMax + 1;
+ std::unique_ptr<ScValidationData> pInsert(rNew.Clone(this));
+ pInsert->SetKey( nNewKey );
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->InsertNew( std::move(pInsert) );
+ return nNewKey;
+}
+
+const SfxPoolItem* ScDocument::GetEffItem(
+ SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
+{
+ const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
+ if ( pPattern )
+ {
+ const SfxItemSet& rSet = pPattern->GetItemSet();
+ if ( rSet.GetItemState( ATTR_CONDITIONAL ) == SfxItemState::SET )
+ {
+ const ScCondFormatIndexes& rIndex = pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+ ScConditionalFormatList* pCondFormList = GetCondFormList( nTab );
+ if (!rIndex.empty() && pCondFormList)
+ {
+ for(const auto& rItem : rIndex)
+ {
+ const ScConditionalFormat* pForm = pCondFormList->GetFormat( rItem );
+ if ( pForm )
+ {
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(const_cast<ScDocument&>(*this), aPos);
+ const OUString& aStyle = pForm->GetCellStyle(aCell, aPos);
+ if (!aStyle.isEmpty())
+ {
+ SfxStyleSheetBase* pStyleSheet = mxPoolHelper->GetStylePool()->Find(
+ aStyle, SfxStyleFamily::Para );
+ const SfxPoolItem* pItem = nullptr;
+ if ( pStyleSheet && pStyleSheet->GetItemSet().GetItemState(
+ nWhich, true, &pItem ) == SfxItemState::SET )
+ return pItem;
+ }
+ }
+ }
+ }
+ }
+ return &rSet.Get( nWhich );
+ }
+ OSL_FAIL("no pattern");
+ return nullptr;
+}
+
+const SfxItemSet* ScDocument::GetCondResult( SCCOL nCol, SCROW nRow, SCTAB nTab, ScRefCellValue* pCell ) const
+{
+ ScConditionalFormatList* pFormatList = GetCondFormList(nTab);
+ if (!pFormatList)
+ return nullptr;
+
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell;
+ if( pCell == nullptr )
+ {
+ aCell.assign(const_cast<ScDocument&>(*this), aPos);
+ pCell = &aCell;
+ }
+ const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
+ const ScCondFormatIndexes& rIndex =
+ pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+
+ return GetCondResult(*pCell, aPos, *pFormatList, rIndex);
+}
+
+const SfxItemSet* ScDocument::GetCondResult(
+ ScRefCellValue& rCell, const ScAddress& rPos, const ScConditionalFormatList& rList,
+ const ScCondFormatIndexes& rIndex ) const
+{
+ for (const auto& rItem : rIndex)
+ {
+ const ScConditionalFormat* pForm = rList.GetFormat(rItem);
+ if (!pForm)
+ continue;
+
+ const OUString& aStyle = pForm->GetCellStyle(rCell, rPos);
+ if (!aStyle.isEmpty())
+ {
+ SfxStyleSheetBase* pStyleSheet =
+ mxPoolHelper->GetStylePool()->Find(aStyle, SfxStyleFamily::Para);
+
+ if (pStyleSheet)
+ return &pStyleSheet->GetItemSet();
+
+ // if style is not there, treat like no condition
+ }
+ }
+
+ return nullptr;
+}
+
+ScConditionalFormat* ScDocument::GetCondFormat(
+ SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ sal_uInt32 nIndex = 0;
+ const ScCondFormatIndexes& rCondFormats = GetAttr(nCol, nRow, nTab, ATTR_CONDITIONAL)->GetCondFormatData();
+
+ if(!rCondFormats.empty())
+ nIndex = rCondFormats[0];
+
+ if (nIndex)
+ {
+ ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
+ if (pCondFormList)
+ return pCondFormList->GetFormat( nIndex );
+ else
+ {
+ OSL_FAIL("pCondFormList is 0");
+ }
+ }
+
+ return nullptr;
+}
+
+ScConditionalFormatList* ScDocument::GetCondFormList(SCTAB nTab) const
+{
+ if (HasTable(nTab))
+ return maTabs[nTab]->GetCondFormList();
+ return nullptr;
+}
+
+void ScDocument::SetCondFormList( ScConditionalFormatList* pList, SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetCondFormList(pList);
+}
+
+const ScValidationData* ScDocument::GetValidationEntry( sal_uInt32 nIndex ) const
+{
+ if ( pValidationList )
+ return pValidationList->GetData( nIndex );
+ else
+ return nullptr;
+}
+
+void ScDocument::DeleteConditionalFormat(sal_uLong nOldIndex, SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->DeleteConditionalFormat(nOldIndex);
+}
+
+bool ScDocument::HasDetectiveOperations() const
+{
+ return pDetOpList && pDetOpList->Count();
+}
+
+void ScDocument::AddDetectiveOperation( const ScDetOpData& rData )
+{
+ if (!pDetOpList)
+ pDetOpList.reset(new ScDetOpList);
+
+ pDetOpList->Append( rData );
+}
+
+void ScDocument::ClearDetectiveOperations()
+{
+ pDetOpList.reset(); // deletes also the entries
+}
+
+void ScDocument::SetDetOpList(std::unique_ptr<ScDetOpList> pNew)
+{
+ pDetOpList = std::move(pNew);
+}
+
+// Comparison of Documents
+
+// Pfriemel-Factors
+#define SC_DOCCOMP_MAXDIFF 256
+#define SC_DOCCOMP_MINGOOD 128
+#define SC_DOCCOMP_COLUMNS 10
+#define SC_DOCCOMP_ROWS 100
+
+sal_uInt16 ScDocument::RowDifferences( SCROW nThisRow, SCTAB nThisTab,
+ ScDocument& rOtherDoc, SCROW nOtherRow, SCTAB nOtherTab,
+ SCCOL nMaxCol, const SCCOLROW* pOtherCols )
+{
+ sal_uLong nDif = 0;
+ sal_uLong nUsed = 0;
+ for (SCCOL nThisCol=0; nThisCol<=nMaxCol; nThisCol++)
+ {
+ SCCOL nOtherCol;
+ if ( pOtherCols )
+ nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
+ else
+ nOtherCol = nThisCol;
+
+ if (ValidCol(nOtherCol)) // only compare columns that are common to both docs
+ {
+ ScRefCellValue aThisCell(*this, ScAddress(nThisCol, nThisRow, nThisTab));
+ ScRefCellValue aOtherCell(rOtherDoc, ScAddress(nOtherCol, nOtherRow, nOtherTab));
+ if (!aThisCell.equalsWithoutFormat(aOtherCell))
+ {
+ if (!aThisCell.isEmpty() && !aOtherCell.isEmpty())
+ nDif += 3;
+ else
+ nDif += 4; // content <-> empty counts more
+ }
+
+ if (!aThisCell.isEmpty() || !aOtherCell.isEmpty())
+ ++nUsed;
+ }
+ }
+
+ if (nUsed > 0)
+ return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256 (SC_DOCCOMP_MAXDIFF)
+
+ OSL_ENSURE(!nDif,"Diff without Used");
+ return 0;
+}
+
+sal_uInt16 ScDocument::ColDifferences( SCCOL nThisCol, SCTAB nThisTab,
+ ScDocument& rOtherDoc, SCCOL nOtherCol, SCTAB nOtherTab,
+ SCROW nMaxRow, const SCCOLROW* pOtherRows )
+{
+
+ //TODO: optimize e.g. with iterator?
+
+ sal_uInt64 nDif = 0;
+ sal_uInt64 nUsed = 0;
+ for (SCROW nThisRow=0; nThisRow<=nMaxRow; nThisRow++)
+ {
+ SCROW nOtherRow;
+ if ( pOtherRows )
+ nOtherRow = pOtherRows[nThisRow];
+ else
+ nOtherRow = nThisRow;
+
+ if (ValidRow(nOtherRow)) // only compare rows that are common to both docs
+ {
+ ScRefCellValue aThisCell(*this, ScAddress(nThisCol, nThisRow, nThisTab));
+ ScRefCellValue aOtherCell(rOtherDoc, ScAddress(nOtherCol, nOtherRow, nOtherTab));
+ if (!aThisCell.equalsWithoutFormat(aOtherCell))
+ {
+ if (!aThisCell.isEmpty() && !aOtherCell.isEmpty())
+ nDif += 3;
+ else
+ nDif += 4; // content <-> empty counts more
+ }
+
+ if (!aThisCell.isEmpty() || !aOtherCell.isEmpty())
+ ++nUsed;
+ }
+ }
+
+ if (nUsed > 0)
+ return static_cast<sal_uInt16>((nDif*64)/nUsed); // max.256
+
+ OSL_ENSURE(!nDif,"Diff without Used");
+ return 0;
+}
+
+void ScDocument::FindOrder( SCCOLROW* pOtherRows, SCCOLROW nThisEndRow, SCCOLROW nOtherEndRow,
+ bool bColumns, ScDocument& rOtherDoc, SCTAB nThisTab, SCTAB nOtherTab,
+ SCCOLROW nEndCol, const SCCOLROW* pTranslate, ScProgress* pProgress, sal_uInt64 nProAdd )
+{
+ // bColumns=true: rows are columns and vice versa
+
+ SCCOLROW nMaxCont; // continue by how much
+ SCCOLROW nMinGood; // what is a hit (incl.)
+ if ( bColumns )
+ {
+ nMaxCont = SC_DOCCOMP_COLUMNS; // 10 columns
+ nMinGood = SC_DOCCOMP_MINGOOD;
+
+ //TODO: additional pass with nMinGood = 0 ????
+
+ }
+ else
+ {
+ nMaxCont = SC_DOCCOMP_ROWS; // 100 rows
+ nMinGood = SC_DOCCOMP_MINGOOD;
+ }
+ bool bUseTotal = bColumns && !pTranslate; // only for the 1st pass
+
+ SCCOLROW nOtherRow = 0;
+ sal_uInt16 nComp;
+ SCCOLROW nThisRow;
+ bool bTotal = false; // hold for several nThisRow
+ SCCOLROW nUnknown = 0;
+ for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
+ {
+ SCCOLROW nTempOther = nOtherRow;
+ bool bFound = false;
+ sal_uInt16 nBest = SC_DOCCOMP_MAXDIFF;
+ SCCOLROW nMax = std::min( nOtherEndRow, static_cast<SCCOLROW>(( nTempOther + nMaxCont + nUnknown )) );
+ for (SCCOLROW i=nTempOther; i<=nMax && nBest>0; i++) // stop at 0
+ {
+ if (bColumns)
+ nComp = ColDifferences( static_cast<SCCOL>(nThisRow), nThisTab, rOtherDoc, static_cast<SCCOL>(i), nOtherTab, nEndCol, pTranslate );
+ else
+ nComp = RowDifferences( nThisRow, nThisTab, rOtherDoc, i, nOtherTab, static_cast<SCCOL>(nEndCol), pTranslate );
+ if ( nComp < nBest && ( nComp <= nMinGood || bTotal ) )
+ {
+ nTempOther = i;
+ nBest = nComp;
+ bFound = true;
+ }
+ if ( nComp < SC_DOCCOMP_MAXDIFF || bFound )
+ bTotal = false;
+ else if ( i == nTempOther && bUseTotal )
+ bTotal = true; // only at the very top
+ }
+ if ( bFound )
+ {
+ pOtherRows[nThisRow] = nTempOther;
+ nOtherRow = nTempOther + 1;
+ nUnknown = 0;
+ }
+ else
+ {
+ pOtherRows[nThisRow] = SCROW_MAX;
+ ++nUnknown;
+ }
+
+ if (pProgress)
+ pProgress->SetStateOnPercent(nProAdd+static_cast<sal_uLong>(nThisRow));
+ }
+
+ // fill in blocks that don't match
+
+ SCROW nFillStart = 0;
+ SCROW nFillPos = 0;
+ bool bInFill = false;
+ for (nThisRow = 0; nThisRow <= nThisEndRow+1; nThisRow++)
+ {
+ SCROW nThisOther = ( nThisRow <= nThisEndRow ) ? pOtherRows[nThisRow] : (nOtherEndRow+1);
+ if ( ValidRow(nThisOther) )
+ {
+ if ( bInFill )
+ {
+ if ( nThisOther > nFillStart ) // is there something to distribute?
+ {
+ SCROW nDiff1 = nThisOther - nFillStart;
+ SCROW nDiff2 = nThisRow - nFillPos;
+ SCROW nMinDiff = std::min(nDiff1, nDiff2);
+ for (SCROW i=0; i<nMinDiff; i++)
+ pOtherRows[nFillPos+i] = nFillStart+i;
+ }
+
+ bInFill = false;
+ }
+ nFillStart = nThisOther + 1;
+ nFillPos = nThisRow + 1;
+ }
+ else
+ bInFill = true;
+ }
+}
+
+void ScDocument::CompareDocument( ScDocument& rOtherDoc )
+{
+ if (!pChangeTrack)
+ return;
+
+ SCTAB nThisCount = GetTableCount();
+ SCTAB nOtherCount = rOtherDoc.GetTableCount();
+ std::unique_ptr<SCTAB[]> pOtherTabs(new SCTAB[nThisCount]);
+ SCTAB nThisTab;
+
+ // compare tables with identical names
+ OUString aThisName;
+ OUString aOtherName;
+ for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
+ {
+ SCTAB nOtherTab = SCTAB_MAX;
+ if (!IsScenario(nThisTab)) // skip scenarios
+ {
+ GetName( nThisTab, aThisName );
+ for (SCTAB nTemp=0; nTemp<nOtherCount && nOtherTab>MAXTAB; nTemp++)
+ if (!rOtherDoc.IsScenario(nTemp))
+ {
+ rOtherDoc.GetName( nTemp, aOtherName );
+ if ( aThisName == aOtherName )
+ nOtherTab = nTemp;
+ }
+ }
+ pOtherTabs[nThisTab] = nOtherTab;
+ }
+ // fill in, so that un-named tables don't get lost
+ SCTAB nFillStart = 0;
+ SCTAB nFillPos = 0;
+ bool bInFill = false;
+ for (nThisTab = 0; nThisTab <= nThisCount; nThisTab++)
+ {
+ SCTAB nThisOther = ( nThisTab < nThisCount ) ? pOtherTabs[nThisTab] : nOtherCount;
+ if ( ValidTab(nThisOther) )
+ {
+ if ( bInFill )
+ {
+ if ( nThisOther > nFillStart ) // is there something to distribute?
+ {
+ SCTAB nDiff1 = nThisOther - nFillStart;
+ SCTAB nDiff2 = nThisTab - nFillPos;
+ SCTAB nMinDiff = std::min(nDiff1, nDiff2);
+ for (SCTAB i=0; i<nMinDiff; i++)
+ if ( !IsScenario(nFillPos+i) && !rOtherDoc.IsScenario(nFillStart+i) )
+ pOtherTabs[nFillPos+i] = nFillStart+i;
+ }
+
+ bInFill = false;
+ }
+ nFillStart = nThisOther + 1;
+ nFillPos = nThisTab + 1;
+ }
+ else
+ bInFill = true;
+ }
+
+ // compare tables in the original order
+
+ for (nThisTab=0; nThisTab<nThisCount; nThisTab++)
+ {
+ SCTAB nOtherTab = pOtherTabs[nThisTab];
+ if ( ValidTab(nOtherTab) )
+ {
+ SCCOL nThisEndCol = 0;
+ SCROW nThisEndRow = 0;
+ SCCOL nOtherEndCol = 0;
+ SCROW nOtherEndRow = 0;
+ GetCellArea( nThisTab, nThisEndCol, nThisEndRow );
+ rOtherDoc.GetCellArea( nOtherTab, nOtherEndCol, nOtherEndRow );
+ SCCOL nEndCol = std::max(nThisEndCol, nOtherEndCol);
+ SCROW nEndRow = std::max(nThisEndRow, nOtherEndRow);
+ SCCOL nThisCol;
+ SCROW nThisRow;
+ sal_uLong n1,n2; // for AppendDeleteRange
+
+ //TODO: one Progress over all tables ???
+
+ OUString aTabName;
+ GetName( nThisTab, aTabName );
+ OUString aTemplate = ScResId(STR_PROGRESS_COMPARING);
+ sal_Int32 nIndex = 0;
+ OUString aProText = o3tl::getToken(aTemplate, 0, '#', nIndex ) +
+ aTabName +
+ o3tl::getToken(aTemplate, 0, '#', nIndex );
+ ScProgress aProgress( GetDocumentShell(), aProText, 3*nThisEndRow, true ); // 2x FindOrder, 1x here
+ tools::Long nProgressStart = 2*nThisEndRow; // start for here
+
+ std::unique_ptr<SCCOLROW[]> pTempRows(new SCCOLROW[nThisEndRow+1]);
+ std::unique_ptr<SCCOLROW[]> pOtherRows(new SCCOLROW[nThisEndRow+1]);
+ std::unique_ptr<SCCOLROW[]> pOtherCols(new SCCOLROW[nThisEndCol+1]);
+
+ // find inserted/deleted columns/rows:
+ // Two attempts:
+ // 1) compare original rows (pTempRows)
+ // 2) compare original columns (pOtherCols)
+ // with this column order compare rows (pOtherRows)
+
+ //TODO: compare columns twice with different nMinGood ???
+
+ // 1
+ FindOrder( pTempRows.get(), nThisEndRow, nOtherEndRow, false,
+ rOtherDoc, nThisTab, nOtherTab, nEndCol, nullptr, &aProgress, 0 );
+ // 2
+ FindOrder( pOtherCols.get(), nThisEndCol, nOtherEndCol, true,
+ rOtherDoc, nThisTab, nOtherTab, nEndRow, nullptr, nullptr, 0 );
+ FindOrder( pOtherRows.get(), nThisEndRow, nOtherEndRow, false,
+ rOtherDoc, nThisTab, nOtherTab, nThisEndCol,
+ pOtherCols.get(), &aProgress, nThisEndRow );
+
+ sal_uLong nMatch1 = 0; // pTempRows, no columns
+ for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
+ if (ValidRow(pTempRows[nThisRow]))
+ nMatch1 += SC_DOCCOMP_MAXDIFF -
+ RowDifferences( nThisRow, nThisTab, rOtherDoc, pTempRows[nThisRow],
+ nOtherTab, nEndCol, nullptr );
+
+ sal_uLong nMatch2 = 0; // pOtherRows, pOtherCols
+ for (nThisRow = 0; nThisRow<=nThisEndRow; nThisRow++)
+ if (ValidRow(pOtherRows[nThisRow]))
+ nMatch2 += SC_DOCCOMP_MAXDIFF -
+ RowDifferences( nThisRow, nThisTab, rOtherDoc, pOtherRows[nThisRow],
+ nOtherTab, nThisEndCol, pOtherCols.get() );
+
+ if ( nMatch1 >= nMatch2 ) // without columns ?
+ {
+ // reset columns
+ for (nThisCol = 0; nThisCol<=nThisEndCol; nThisCol++)
+ pOtherCols[nThisCol] = nThisCol;
+
+ // swap row-arrays (they get both deleted anyway)
+ pTempRows.swap(pOtherRows);
+ }
+ else
+ {
+ // remains for pOtherCols, pOtherRows
+ }
+
+ // Generate Change-Actions
+ // 1) columns from the right
+ // 2) rows from below
+ // 3) single cells in normal order
+
+ // Actions for inserted/deleted columns
+
+ SCCOL nLastOtherCol = static_cast<SCCOL>(nOtherEndCol + 1);
+ // nThisEndCol ... 0
+ for ( nThisCol = nThisEndCol+1; nThisCol > 0; )
+ {
+ --nThisCol;
+ SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
+ if ( ValidCol(nOtherCol) && nOtherCol+1 < nLastOtherCol )
+ {
+ // gap -> deleted
+ ScRange aDelRange( nOtherCol+1, 0, nOtherTab,
+ nLastOtherCol-1, MaxRow(), nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+ if ( nOtherCol > MaxCol() ) // inserted
+ {
+ // combine
+ if ( nThisCol == nThisEndCol || ValidCol(static_cast<SCCOL>(pOtherCols[nThisCol+1])) )
+ {
+ SCCOL nFirstNew = nThisCol;
+ while ( nFirstNew > 0 && pOtherCols[nFirstNew-1] > MaxCol() )
+ --nFirstNew;
+ SCCOL nDiff = nThisCol - nFirstNew;
+ ScRange aRange( nLastOtherCol, 0, nOtherTab,
+ nLastOtherCol+nDiff, MaxRow(), nOtherTab );
+ pChangeTrack->AppendInsert( aRange );
+ }
+ }
+ else
+ nLastOtherCol = nOtherCol;
+ }
+ if ( nLastOtherCol > 0 ) // deleted at the very top
+ {
+ ScRange aDelRange( 0, 0, nOtherTab,
+ nLastOtherCol-1, MaxRow(), nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+
+ // Actions for inserted/deleted rows
+
+ SCROW nLastOtherRow = nOtherEndRow + 1;
+ // nThisEndRow ... 0
+ for ( nThisRow = nThisEndRow+1; nThisRow > 0; )
+ {
+ --nThisRow;
+ SCROW nOtherRow = pOtherRows[nThisRow];
+ if ( ValidRow(nOtherRow) && nOtherRow+1 < nLastOtherRow )
+ {
+ // gap -> deleted
+ ScRange aDelRange( 0, nOtherRow+1, nOtherTab,
+ MaxCol(), nLastOtherRow-1, nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+ if ( nOtherRow > MaxRow() ) // inserted
+ {
+ // combine
+ if ( nThisRow == nThisEndRow || ValidRow(pOtherRows[nThisRow+1]) )
+ {
+ SCROW nFirstNew = nThisRow;
+ while ( nFirstNew > 0 && pOtherRows[nFirstNew-1] > MaxRow() )
+ --nFirstNew;
+ SCROW nDiff = nThisRow - nFirstNew;
+ ScRange aRange( 0, nLastOtherRow, nOtherTab,
+ MaxCol(), nLastOtherRow+nDiff, nOtherTab );
+ pChangeTrack->AppendInsert( aRange );
+ }
+ }
+ else
+ nLastOtherRow = nOtherRow;
+ }
+ if ( nLastOtherRow > 0 ) // deleted at the very top
+ {
+ ScRange aDelRange( 0, 0, nOtherTab,
+ MaxCol(), nLastOtherRow-1, nOtherTab );
+ pChangeTrack->AppendDeleteRange( aDelRange, &rOtherDoc, n1, n2 );
+ }
+
+ // walk rows to find single cells
+
+ for (nThisRow = 0; nThisRow <= nThisEndRow; nThisRow++)
+ {
+ SCROW nOtherRow = pOtherRows[nThisRow];
+ for (nThisCol = 0; nThisCol <= nThisEndCol; nThisCol++)
+ {
+ SCCOL nOtherCol = static_cast<SCCOL>(pOtherCols[nThisCol]);
+ ScAddress aThisPos( nThisCol, nThisRow, nThisTab );
+ ScCellValue aThisCell;
+ aThisCell.assign(*this, aThisPos);
+ ScCellValue aOtherCell; // start empty
+ if ( ValidCol(nOtherCol) && ValidRow(nOtherRow) )
+ {
+ ScAddress aOtherPos( nOtherCol, nOtherRow, nOtherTab );
+ aOtherCell.assign(rOtherDoc, aOtherPos);
+ }
+
+ if (!aThisCell.equalsWithoutFormat(aOtherCell))
+ {
+ ScRange aRange( aThisPos );
+ ScChangeActionContent* pAction = new ScChangeActionContent( aRange );
+ pAction->SetOldValue(aOtherCell, &rOtherDoc, this);
+ pAction->SetNewValue(aThisCell, this);
+ pChangeTrack->Append( pAction );
+ }
+ }
+ aProgress.SetStateOnPercent(nProgressStart+nThisRow);
+ }
+ }
+ }
+}
+
+sal_Unicode ScDocument::GetSheetSeparator() const
+{
+ const ScCompiler::Convention* pConv = ScCompiler::GetRefConvention(
+ FormulaGrammar::extractRefConvention( GetGrammar()));
+ assert(pConv);
+ return pConv ? pConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR) : '.';
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen5.cxx b/sc/source/core/data/documen5.cxx
new file mode 100644
index 0000000000..0a9a4179f0
--- /dev/null
+++ b/sc/source/core/data/documen5.cxx
@@ -0,0 +1,656 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svtools/embedhlp.hxx>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <table.hxx>
+#include <drwlayer.hxx>
+#include <chartlis.hxx>
+#include <chartlock.hxx>
+#include <refupdat.hxx>
+
+#include <miscuno.hxx>
+#include <chart2uno.hxx>
+#include <charthelper.hxx>
+
+using namespace ::com::sun::star;
+
+static void lcl_GetChartParameters( const uno::Reference< chart2::XChartDocument >& xChartDoc,
+ OUString& rRanges, chart::ChartDataRowSource& rDataRowSource,
+ bool& rHasCategories, bool& rFirstCellAsLabel )
+{
+ rHasCategories = rFirstCellAsLabel = false; // default if not in sequence
+
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY );
+
+ uno::Reference< chart2::data::XDataSource > xDataSource = xReceiver->getUsedData();
+ uno::Reference< chart2::data::XDataProvider > xProvider = xChartDoc->getDataProvider();
+
+ if ( !xProvider.is() )
+ return;
+
+ const uno::Sequence< beans::PropertyValue > aArgs( xProvider->detectArguments( xDataSource ) );
+
+ for (const beans::PropertyValue& rProp : aArgs)
+ {
+ OUString aPropName(rProp.Name);
+
+ if ( aPropName == "CellRangeRepresentation" )
+ rProp.Value >>= rRanges;
+ else if ( aPropName == "DataRowSource" )
+ rDataRowSource = static_cast<chart::ChartDataRowSource>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ));
+ else if ( aPropName == "HasCategories" )
+ rHasCategories = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if ( aPropName == "FirstCellAsLabel" )
+ rFirstCellAsLabel = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ }
+}
+
+static void lcl_SetChartParameters( const uno::Reference< chart2::data::XDataReceiver >& xReceiver,
+ const OUString& rRanges, chart::ChartDataRowSource eDataRowSource,
+ bool bHasCategories, bool bFirstCellAsLabel )
+{
+ if ( !xReceiver.is() )
+ return;
+
+ uno::Sequence< beans::PropertyValue > aArgs{
+ beans::PropertyValue(
+ "CellRangeRepresentation", -1,
+ uno::Any( rRanges ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "HasCategories", -1,
+ uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "FirstCellAsLabel", -1,
+ uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "DataRowSource", -1,
+ uno::Any( eDataRowSource ), beans::PropertyState_DIRECT_VALUE )
+ };
+ xReceiver->setArguments( aArgs );
+}
+
+bool ScDocument::HasChartAtPoint( SCTAB nTab, const Point& rPos, OUString& rName )
+{
+ if (mpDrawLayer && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ pObject->GetCurrentBoundRect().Contains(rPos) )
+ {
+ // also Chart-Objects that are not in the Collection
+
+ if (IsChart(pObject))
+ {
+ rName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+ return true;
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+
+ rName.clear();
+ return false; // nothing found
+}
+
+void ScDocument::UpdateChartArea( const OUString& rChartName,
+ const ScRange& rNewArea, bool bColHeaders, bool bRowHeaders,
+ bool bAdd )
+{
+ ScRangeListRef aRLR( new ScRangeList(rNewArea) );
+ UpdateChartArea( rChartName, aRLR, bColHeaders, bRowHeaders, bAdd );
+}
+
+uno::Reference< chart2::XChartDocument > ScDocument::GetChartByName( std::u16string_view rChartName )
+{
+ uno::Reference< chart2::XChartDocument > xReturn;
+
+ if (mpDrawLayer)
+ {
+ sal_uInt16 nCount = mpDrawLayer->GetPageCount();
+ SCTAB nSize = static_cast<SCTAB>(maTabs.size());
+ for (sal_uInt16 nTab=0; nTab<nCount && nTab < nSize; nTab++)
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(nTab);
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == rChartName )
+ {
+ xReturn.set( ScChartHelper::GetChartFromSdrObject( pObject ) );
+ return xReturn;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ return xReturn;
+}
+
+void ScDocument::GetChartRanges( std::u16string_view rChartName, ::std::vector< ScRangeList >& rRangesVector, const ScDocument& rSheetNameDoc )
+{
+ rRangesVector.clear();
+ uno::Reference< chart2::XChartDocument > xChartDoc( GetChartByName( rChartName ) );
+ if ( xChartDoc.is() )
+ {
+ std::vector< OUString > aRangeStrings;
+ ScChartHelper::GetChartRanges( xChartDoc, aRangeStrings );
+ for(const OUString & aRangeString : aRangeStrings)
+ {
+ ScRangeList aRanges;
+ aRanges.Parse( aRangeString, rSheetNameDoc, rSheetNameDoc.GetAddressConvention() );
+ rRangesVector.push_back(aRanges);
+ }
+ }
+}
+
+void ScDocument::SetChartRanges( std::u16string_view rChartName, const ::std::vector< ScRangeList >& rRangesVector )
+{
+ uno::Reference< chart2::XChartDocument > xChartDoc( GetChartByName( rChartName ) );
+ if ( !xChartDoc.is() )
+ return;
+
+ sal_Int32 nCount = static_cast<sal_Int32>( rRangesVector.size() );
+ uno::Sequence< OUString > aRangeStrings(nCount);
+ auto aRangeStringsRange = asNonConstRange(aRangeStrings);
+ for( sal_Int32 nN=0; nN<nCount; nN++ )
+ {
+ ScRangeList aScRangeList( rRangesVector[nN] );
+ OUString sRangeStr;
+ aScRangeList.Format( sRangeStr, ScRefFlags::RANGE_ABS_3D, *this, GetAddressConvention() );
+ aRangeStringsRange[nN]=sRangeStr;
+ }
+ ScChartHelper::SetChartRanges( xChartDoc, aRangeStrings );
+}
+
+void ScDocument::GetOldChartParameters( std::u16string_view rName,
+ ScRangeList& rRanges, bool& rColHeaders, bool& rRowHeaders )
+{
+ // used for undo of changing chart source area
+
+ if (!mpDrawLayer)
+ return;
+
+ sal_uInt16 nCount = mpDrawLayer->GetPageCount();
+ for (sal_uInt16 nTab=0; nTab<nCount && nTab < static_cast<SCTAB>(maTabs.size()); nTab++)
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(nTab);
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == rName )
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc( ScChartHelper::GetChartFromSdrObject( pObject ) );
+ if ( xChartDoc.is() )
+ {
+ chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS;
+ bool bHasCategories = false;
+ bool bFirstCellAsLabel = false;
+ OUString aRangesStr;
+ lcl_GetChartParameters( xChartDoc, aRangesStr, eDataRowSource, bHasCategories, bFirstCellAsLabel );
+
+ rRanges.Parse( aRangesStr, *this, GetAddressConvention());
+ if ( eDataRowSource == chart::ChartDataRowSource_COLUMNS )
+ {
+ rRowHeaders = bHasCategories;
+ rColHeaders = bFirstCellAsLabel;
+ }
+ else
+ {
+ rColHeaders = bHasCategories;
+ rRowHeaders = bFirstCellAsLabel;
+ }
+ }
+ return;
+ }
+ pObject = aIter.Next();
+ }
+ }
+}
+
+void ScDocument::UpdateChartArea( const OUString& rChartName,
+ const ScRangeListRef& rNewList, bool bColHeaders, bool bRowHeaders,
+ bool bAdd )
+{
+ if (!mpDrawLayer)
+ return;
+
+ for (SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]; nTab++)
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == rChartName )
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc( ScChartHelper::GetChartFromSdrObject( pObject ) );
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY );
+ if ( xChartDoc.is() && xReceiver.is() )
+ {
+ ScRangeListRef aNewRanges;
+ chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS;
+ bool bHasCategories = false;
+ bool bFirstCellAsLabel = false;
+ OUString aRangesStr;
+ lcl_GetChartParameters( xChartDoc, aRangesStr, eDataRowSource, bHasCategories, bFirstCellAsLabel );
+
+ bool bInternalData = xChartDoc->hasInternalDataProvider();
+
+ if ( bAdd && !bInternalData )
+ {
+ // append to old ranges, keep other settings
+
+ aNewRanges = new ScRangeList;
+ aNewRanges->Parse( aRangesStr, *this, GetAddressConvention());
+ aNewRanges->insert( aNewRanges->begin(), rNewList->begin(), rNewList->end() );
+ }
+ else
+ {
+ // directly use new ranges (only eDataRowSource is used from old settings)
+
+ if ( eDataRowSource == chart::ChartDataRowSource_COLUMNS )
+ {
+ bHasCategories = bRowHeaders;
+ bFirstCellAsLabel = bColHeaders;
+ }
+ else
+ {
+ bHasCategories = bColHeaders;
+ bFirstCellAsLabel = bRowHeaders;
+ }
+ aNewRanges = rNewList;
+ }
+
+ if ( bInternalData && mpShell )
+ {
+ // Calc -> DataProvider
+ uno::Reference< chart2::data::XDataProvider > xDataProvider = new ScChart2DataProvider( this );
+ xReceiver->attachDataProvider( xDataProvider );
+ uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier(
+ static_cast<cppu::OWeakObject*>(mpShell->GetModel()), uno::UNO_QUERY );
+ xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier );
+ }
+
+ OUString sRangeStr;
+ aNewRanges->Format( sRangeStr, ScRefFlags::RANGE_ABS_3D, *this, GetAddressConvention() );
+
+ lcl_SetChartParameters( xReceiver, sRangeStr, eDataRowSource, bHasCategories, bFirstCellAsLabel );
+
+ pChartListenerCollection->ChangeListening( rChartName, aNewRanges );
+
+ return; // do not search anymore
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+}
+
+void ScDocument::UpdateChart( const OUString& rChartName )
+{
+ if (!mpDrawLayer || bInDtorClear)
+ return;
+ uno::Reference< chart2::XChartDocument > xChartDoc( GetChartByName( rChartName ) );
+ if (xChartDoc && (!mpShell || mpShell->IsEnableSetModified()))
+ {
+ try
+ {
+ uno::Reference< util::XModifiable > xModif( xChartDoc, uno::UNO_QUERY_THROW );
+ if (apTemporaryChartLock)
+ apTemporaryChartLock->AlsoLockThisChart( uno::Reference< frame::XModel >( xModif, uno::UNO_QUERY ) );
+ xModif->setModified( true );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ // After the update, chart keeps track of its own data source ranges,
+ // the listener doesn't need to listen anymore, except the chart has
+ // an internal data provider.
+ if ( !( xChartDoc.is() && xChartDoc->hasInternalDataProvider() ) && pChartListenerCollection )
+ {
+ pChartListenerCollection->ChangeListening( rChartName, new ScRangeList );
+ }
+}
+
+void ScDocument::RestoreChartListener( const OUString& rName )
+{
+ if (!pChartListenerCollection)
+ return;
+
+ // Read the data ranges from the chart object, and start listening to those ranges again
+ // (called when a chart is saved, because then it might be swapped out and stop listening itself).
+
+ uno::Reference< embed::XEmbeddedObject > xObject = FindOleObjectByName( rName );
+ if ( !xObject.is() )
+ return;
+
+ uno::Reference< util::XCloseable > xComponent = xObject->getComponent();
+ uno::Reference< chart2::XChartDocument > xChartDoc( xComponent, uno::UNO_QUERY );
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xComponent, uno::UNO_QUERY );
+ if ( !xChartDoc.is() || !xReceiver.is() || xChartDoc->hasInternalDataProvider() )
+ return;
+
+ const uno::Sequence<OUString> aRepresentations( xReceiver->getUsedRangeRepresentations() );
+ ScRangeListRef aRanges = new ScRangeList;
+ for ( const auto& rRepresentation : aRepresentations )
+ {
+ ScRange aRange;
+ ScAddress::Details aDetails(GetAddressConvention(), 0, 0);
+ if ( aRange.ParseAny( rRepresentation, *this, aDetails ) & ScRefFlags::VALID )
+ aRanges->push_back( aRange );
+ }
+
+ pChartListenerCollection->ChangeListening( rName, aRanges );
+}
+
+void ScDocument::UpdateChartRef( UpdateRefMode eUpdateRefMode,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ if (!mpDrawLayer)
+ return;
+
+ ScChartListenerCollection::ListenersType& rListeners = pChartListenerCollection->getListeners();
+ for (auto const& it : rListeners)
+ {
+ ScChartListener *const pChartListener = it.second.get();
+ ScRangeListRef aRLR( pChartListener->GetRangeList() );
+ ScRangeListRef aNewRLR( new ScRangeList );
+ bool bChanged = false;
+ bool bDataChanged = false;
+ for ( size_t i = 0, nListSize = aRLR->size(); i < nListSize; ++i )
+ {
+ ScRange& rRange = (*aRLR)[i];
+ SCCOL theCol1 = rRange.aStart.Col();
+ SCROW theRow1 = rRange.aStart.Row();
+ SCTAB theTab1 = rRange.aStart.Tab();
+ SCCOL theCol2 = rRange.aEnd.Col();
+ SCROW theRow2 = rRange.aEnd.Row();
+ SCTAB theTab2 = rRange.aEnd.Tab();
+ ScRefUpdateRes eRes = ScRefUpdate::Update(
+ this, eUpdateRefMode,
+ nCol1,nRow1,nTab1, nCol2,nRow2,nTab2,
+ nDx,nDy,nDz,
+ theCol1,theRow1,theTab1,
+ theCol2,theRow2,theTab2 );
+ if ( eRes != UR_NOTHING )
+ {
+ bChanged = true;
+ aNewRLR->push_back( ScRange(
+ theCol1, theRow1, theTab1,
+ theCol2, theRow2, theTab2 ));
+ if ( eUpdateRefMode == URM_INSDEL
+ && !bDataChanged
+ && (eRes == UR_INVALID ||
+ ((rRange.aEnd.Col() - rRange.aStart.Col()
+ != theCol2 - theCol1)
+ || (rRange.aEnd.Row() - rRange.aStart.Row()
+ != theRow2 - theRow1)
+ || (rRange.aEnd.Tab() - rRange.aStart.Tab()
+ != theTab2 - theTab1))) )
+ {
+ bDataChanged = true;
+ }
+ }
+ else
+ aNewRLR->push_back( rRange );
+ }
+ if ( bChanged )
+ {
+ // Force the chart to be loaded now, so it registers itself for UNO events.
+ // UNO broadcasts are done after UpdateChartRef, so the chart will get this
+ // reference change.
+
+ uno::Reference<embed::XEmbeddedObject> xIPObj =
+ FindOleObjectByName(pChartListener->GetName());
+
+ (void)svt::EmbeddedObjectRef::TryRunningState( xIPObj );
+
+ // After the change, chart keeps track of its own data source ranges,
+ // the listener doesn't need to listen anymore, except the chart has
+ // an internal data provider.
+ bool bInternalDataProvider = false;
+ if ( xIPObj.is() )
+ {
+ try
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc( xIPObj->getComponent(), uno::UNO_QUERY_THROW );
+ bInternalDataProvider = xChartDoc->hasInternalDataProvider();
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ if ( bInternalDataProvider )
+ {
+ pChartListener->ChangeListening( aNewRLR, bDataChanged );
+ }
+ else
+ {
+ pChartListener->ChangeListening( new ScRangeList, bDataChanged );
+ }
+ }
+ }
+}
+
+void ScDocument::SetChartRangeList( std::u16string_view rChartName,
+ const ScRangeListRef& rNewRangeListRef )
+{
+ // called from ChartListener
+
+ if (!mpDrawLayer)
+ return;
+
+ for (SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]; nTab++)
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == rChartName )
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc( ScChartHelper::GetChartFromSdrObject( pObject ) );
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY );
+ if ( xChartDoc.is() && xReceiver.is() )
+ {
+ chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS;
+ bool bHasCategories = false;
+ bool bFirstCellAsLabel = false;
+ OUString aRangesStr;
+ lcl_GetChartParameters( xChartDoc, aRangesStr, eDataRowSource, bHasCategories, bFirstCellAsLabel );
+
+ OUString sRangeStr;
+ rNewRangeListRef->Format( sRangeStr, ScRefFlags::RANGE_ABS_3D, *this, GetAddressConvention() );
+
+ lcl_SetChartParameters( xReceiver, sRangeStr, eDataRowSource, bHasCategories, bFirstCellAsLabel );
+
+ // don't modify pChartListenerCollection here, called from there
+ return;
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+}
+
+bool ScDocument::HasData( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab) ; pTable && nCol < pTable->GetAllocatedColumnsCount())
+ return pTable->HasData(nCol, nRow);
+ return false;
+}
+
+uno::Reference< embed::XEmbeddedObject >
+ ScDocument::FindOleObjectByName( std::u16string_view rName )
+{
+ if (!mpDrawLayer)
+ return uno::Reference< embed::XEmbeddedObject >();
+
+ // take the pages here from Draw-Layer, as they might not match with the tables
+ // (e.g. delete Redo of table; Draw-Redo happens before DeleteTab)
+
+ sal_uInt16 nCount = mpDrawLayer->GetPageCount();
+ for (sal_uInt16 nTab=0; nTab<nCount; nTab++)
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(nTab);
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ SdrOle2Obj * pOleObject ( dynamic_cast< SdrOle2Obj * >( pObject ));
+ if( pOleObject &&
+ pOleObject->GetPersistName() == rName )
+ {
+ return pOleObject->GetObjRef();
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+
+ return uno::Reference< embed::XEmbeddedObject >();
+}
+
+void ScDocument::UpdateChartListenerCollection()
+{
+ assert(pChartListenerCollection);
+
+ bChartListenerCollectionNeedsUpdate = false;
+ if (!mpDrawLayer)
+ return;
+
+ for (SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++)
+ {
+ if (!maTabs[nTab])
+ continue;
+
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ if (!pPage)
+ continue;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ ScChartListenerCollection::StringSetType& rNonOleObjects =
+ pChartListenerCollection->getNonOleObjectNames();
+
+ for (SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next())
+ {
+ if ( pObject->GetObjIdentifier() != SdrObjKind::OLE2 )
+ continue;
+
+ OUString aObjName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+ ScChartListener* pListener = pChartListenerCollection->findByName(aObjName);
+
+ if (pListener)
+ pListener->SetUsed(true);
+ else if (rNonOleObjects.count(aObjName) > 0)
+ {
+ // non-chart OLE object -> don't touch
+ }
+ else
+ {
+ uno::Reference< embed::XEmbeddedObject > xIPObj = static_cast<SdrOle2Obj*>(pObject)->GetObjRef();
+ OSL_ENSURE( xIPObj.is(), "No embedded object is given!");
+ uno::Reference< css::chart2::data::XDataReceiver > xReceiver;
+ if( xIPObj.is())
+ xReceiver.set( xIPObj->getComponent(), uno::UNO_QUERY );
+
+ // if the object is a chart2::XDataReceiver, we must attach as XDataProvider
+ if( xReceiver.is() &&
+ !PastingDrawFromOtherDoc())
+ {
+ // NOTE: this currently does not work as we are
+ // unable to set the data. So a chart from the
+ // same document is treated like a chart with
+ // own data for the time being.
+
+ // data provider
+ // number formats supplier
+
+ // data ?
+ // how to set?? Defined in XML-file, which is already loaded!!!
+ // => we have to do this stuff here, BEFORE the chart is actually loaded
+ }
+
+ // put into list of other ole objects, so the object doesn't have to
+ // be swapped in the next time UpdateChartListenerCollection is called
+ //TODO: remove names when objects are no longer there?
+ // (object names aren't used again before reloading the document)
+
+ rNonOleObjects.insert(aObjName);
+ }
+ }
+ }
+ // delete all that are not set SetUsed
+ pChartListenerCollection->FreeUnused();
+}
+
+void ScDocument::AddOLEObjectToCollection(const OUString& rName)
+{
+ assert(pChartListenerCollection);
+ ScChartListenerCollection::StringSetType& rNonOleObjects =
+ pChartListenerCollection->getNonOleObjectNames();
+
+ rNonOleObjects.insert(rName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen6.cxx b/sc/source/core/data/documen6.cxx
new file mode 100644
index 0000000000..931cb9002b
--- /dev/null
+++ b/sc/source/core/data/documen6.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 <scitems.hxx>
+
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <document.hxx>
+#include <cellform.hxx>
+#include <patattr.hxx>
+#include <scrdata.hxx>
+#include <poolhelp.hxx>
+#include <attrib.hxx>
+#include <columnspanset.hxx>
+#include <table.hxx>
+
+using namespace com::sun::star;
+
+// this file is compiled with exceptions enabled
+// put functions here that need exceptions!
+
+const uno::Reference< i18n::XBreakIterator >& ScDocument::GetBreakIterator()
+{
+ if ( !pScriptTypeData )
+ pScriptTypeData.reset( new ScScriptTypeData );
+ if ( !pScriptTypeData->xBreakIter.is() )
+ {
+ pScriptTypeData->xBreakIter = i18n::BreakIterator::create( comphelper::getProcessComponentContext() );
+ }
+ return pScriptTypeData->xBreakIter;
+}
+
+bool ScDocument::HasStringWeakCharacters( const OUString& rString )
+{
+ if (!rString.isEmpty())
+ {
+ uno::Reference<i18n::XBreakIterator> xBreakIter = GetBreakIterator();
+ if ( xBreakIter.is() )
+ {
+ sal_Int32 nLen = rString.getLength();
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ sal_Int16 nType = xBreakIter->getScriptType( rString, nPos );
+ if ( nType == i18n::ScriptType::WEAK )
+ return true; // found
+
+ nPos = xBreakIter->endOfScript( rString, nPos, nType );
+ }
+ while ( nPos >= 0 && nPos < nLen );
+ }
+ }
+
+ return false; // none found
+}
+
+SvtScriptType ScDocument::GetStringScriptType( const OUString& rString )
+{
+ SvtScriptType nRet = SvtScriptType::NONE;
+ if (!rString.isEmpty())
+ {
+ uno::Reference<i18n::XBreakIterator> xBreakIter = GetBreakIterator();
+ if ( xBreakIter.is() )
+ {
+ sal_Int32 nLen = rString.getLength();
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ sal_Int16 nType = xBreakIter->getScriptType( rString, nPos );
+ switch ( nType )
+ {
+ case i18n::ScriptType::LATIN:
+ nRet |= SvtScriptType::LATIN;
+ break;
+ case i18n::ScriptType::ASIAN:
+ nRet |= SvtScriptType::ASIAN;
+ break;
+ case i18n::ScriptType::COMPLEX:
+ nRet |= SvtScriptType::COMPLEX;
+ break;
+ // WEAK is ignored
+ }
+ nPos = xBreakIter->endOfScript( rString, nPos, nType );
+ }
+ while ( nPos >= 0 && nPos < nLen );
+ }
+ }
+ return nRet;
+}
+
+SvtScriptType ScDocument::GetCellScriptType( const ScAddress& rPos, sal_uInt32 nNumberFormat,
+ const ScRefCellValue* pCell )
+{
+ SvtScriptType nStored = GetScriptType(rPos);
+ if ( nStored != SvtScriptType::UNKNOWN ) // stored value valid?
+ return nStored; // use stored value
+
+ const Color* pColor;
+ OUString aStr;
+ if( pCell )
+ aStr = ScCellFormat::GetString(*pCell, nNumberFormat, &pColor, *mxPoolHelper->GetFormTable(), *this);
+ else
+ aStr = ScCellFormat::GetString(*this, rPos, nNumberFormat, &pColor, *mxPoolHelper->GetFormTable());
+
+ SvtScriptType nRet = GetStringScriptType( aStr );
+
+ SetScriptType(rPos, nRet); // store for later calls
+
+ return nRet;
+}
+
+SvtScriptType ScDocument::GetScriptType( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScRefCellValue* pCell )
+{
+ // if script type is set, don't have to get number formats
+
+ ScAddress aPos(nCol, nRow, nTab);
+ SvtScriptType nStored = GetScriptType(aPos);
+ if ( nStored != SvtScriptType::UNKNOWN ) // stored value valid?
+ return nStored; // use stored value
+
+ // include number formats from conditional formatting
+
+ const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
+ if (!pPattern) return SvtScriptType::NONE;
+ const SfxItemSet* pCondSet = nullptr;
+ if ( !pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty() )
+ pCondSet = GetCondResult( nCol, nRow, nTab );
+
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( mxPoolHelper->GetFormTable(), pCondSet );
+
+ return GetCellScriptType(aPos, nFormat, pCell);
+}
+
+namespace {
+
+class ScriptTypeAggregator : public sc::ColumnSpanSet::Action
+{
+ ScDocument& mrDoc;
+ sc::ColumnBlockPosition maBlockPos;
+ SvtScriptType mnScriptType;
+
+public:
+ explicit ScriptTypeAggregator(ScDocument& rDoc) : mrDoc(rDoc), mnScriptType(SvtScriptType::NONE) {}
+
+ virtual void startColumn(SCTAB nTab, SCCOL nCol) override
+ {
+ mrDoc.InitColumnBlockPosition(maBlockPos, nTab, nCol);
+ }
+
+ virtual void execute(const ScAddress& rPos, SCROW nLength, bool bVal) override
+ {
+ if (!bVal)
+ return;
+
+ mnScriptType |= mrDoc.GetRangeScriptType(maBlockPos, rPos, nLength);
+ };
+
+ SvtScriptType getScriptType() const { return mnScriptType; }
+};
+
+}
+
+SvtScriptType ScDocument::GetRangeScriptType(
+ sc::ColumnBlockPosition& rBlockPos, const ScAddress& rPos, SCROW nLength )
+{
+ if (!HasTable(rPos.Tab()))
+ return SvtScriptType::NONE;
+
+ return maTabs[rPos.Tab()]->GetRangeScriptType(rBlockPos, rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
+}
+
+SvtScriptType ScDocument::GetRangeScriptType( const ScRangeList& rRanges )
+{
+ sc::ColumnSpanSet aSet;
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange& rRange = rRanges[i];
+ SCTAB nTab = rRange.aStart.Tab();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCROW nRow2 = rRange.aEnd.Row();
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ aSet.set(*this, nTab, nCol, nRow1, nRow2, true);
+ }
+
+ ScriptTypeAggregator aAction(*this);
+ aSet.executeAction(*this, aAction);
+ return aAction.getScriptType();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx
new file mode 100644
index 0000000000..f69585167c
--- /dev/null
+++ b/sc/source/core/data/documen7.cxx
@@ -0,0 +1,615 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <document.hxx>
+#include <brdcst.hxx>
+#include <bcaslot.hxx>
+#include <formulacell.hxx>
+#include <table.hxx>
+#include <progress.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <sheetevents.hxx>
+#include <tokenarray.hxx>
+#include <listenercontext.hxx>
+
+void ScDocument::StartListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
+{
+ if (!pBASM)
+ return;
+
+ // Ensure sane ranges for the slots, specifically don't attempt to listen
+ // to more sheets than the document has. The slot machine handles it but
+ // with memory waste. Binary import filters can set out-of-bounds ranges
+ // in formula expressions' references, so all middle layers would have to
+ // check it, rather have this central point here.
+ ScRange aLimitedRange( ScAddress::UNINITIALIZED );
+ bool bEntirelyOut;
+ if (!LimitRangeToAvailableSheets( rRange, aLimitedRange, bEntirelyOut))
+ {
+ pBASM->StartListeningArea(rRange, bGroupListening, pListener);
+ return;
+ }
+
+ // If both sheets are out-of-bounds in the same direction then just bail out.
+ if (bEntirelyOut)
+ return;
+
+ pBASM->StartListeningArea( aLimitedRange, bGroupListening, pListener);
+}
+
+void ScDocument::EndListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
+{
+ if (!pBASM)
+ return;
+
+ // End listening has to limit the range exactly the same as in
+ // StartListeningArea(), otherwise the range would not be found.
+ ScRange aLimitedRange( ScAddress::UNINITIALIZED );
+ bool bEntirelyOut;
+ if (!LimitRangeToAvailableSheets( rRange, aLimitedRange, bEntirelyOut))
+ {
+ pBASM->EndListeningArea(rRange, bGroupListening, pListener);
+ return;
+ }
+
+ // If both sheets are out-of-bounds in the same direction then just bail out.
+ if (bEntirelyOut)
+ return;
+
+ pBASM->EndListeningArea( aLimitedRange, bGroupListening, pListener);
+}
+
+bool ScDocument::LimitRangeToAvailableSheets( const ScRange& rRange, ScRange& o_rRange,
+ bool& o_bEntirelyOutOfBounds ) const
+{
+ const SCTAB nMaxTab = GetTableCount() - 1;
+ if (ValidTab( rRange.aStart.Tab(), nMaxTab) && ValidTab( rRange.aEnd.Tab(), nMaxTab))
+ return false;
+
+ // Originally BCA_LISTEN_ALWAYS uses an implicit tab 0 and should had been
+ // valid already, but in case that would change...
+ if (rRange == BCA_LISTEN_ALWAYS)
+ return false;
+
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ SAL_WARN("sc.core","ScDocument::LimitRangeToAvailableSheets - bad sheet range: " << nTab1 << ".." << nTab2 <<
+ ", sheets: 0.." << nMaxTab);
+
+ // Both sheets are out-of-bounds in the same direction.
+ if ((nTab1 < 0 && nTab2 < 0) || (nMaxTab < nTab1 && nMaxTab < nTab2))
+ {
+ o_bEntirelyOutOfBounds = true;
+ return true;
+ }
+
+ // Limit the sheet range to bounds.
+ o_bEntirelyOutOfBounds = false;
+ nTab1 = std::clamp<SCTAB>( nTab1, 0, nMaxTab);
+ nTab2 = std::clamp<SCTAB>( nTab2, 0, nMaxTab);
+ o_rRange = rRange;
+ o_rRange.aStart.SetTab(nTab1);
+ o_rRange.aEnd.SetTab(nTab2);
+ return true;
+}
+
+void ScDocument::Broadcast( const ScHint& rHint )
+{
+ if ( !pBASM )
+ return ; // Clipboard or Undo
+ if ( eHardRecalcState == HardRecalcState::OFF )
+ {
+ ScBulkBroadcast aBulkBroadcast( pBASM.get(), rHint.GetId()); // scoped bulk broadcast
+ bool bIsBroadcasted = BroadcastHintInternal(rHint);
+ if ( pBASM->AreaBroadcast( rHint ) || bIsBroadcasted )
+ TrackFormulas( rHint.GetId() );
+ }
+
+ if ( rHint.GetStartAddress() != BCA_BRDCST_ALWAYS )
+ {
+ SCTAB nTab = rHint.GetStartAddress().Tab();
+ if (nTab < GetTableCount() && maTabs[nTab])
+ maTabs[nTab]->SetStreamValid(false);
+ }
+}
+
+bool ScDocument::BroadcastHintInternal( const ScHint& rHint )
+{
+ bool bIsBroadcasted = false;
+ const ScAddress address(rHint.GetStartAddress());
+ SvtBroadcaster* pLastBC = nullptr;
+ // Process all broadcasters for the given row range.
+ for( SCROW nRow = 0; nRow < rHint.GetRowCount(); ++nRow )
+ {
+ ScAddress a(address);
+ a.SetRow(address.Row() + nRow);
+ SvtBroadcaster* pBC = GetBroadcaster(a);
+ if ( pBC && pBC != pLastBC )
+ {
+ pBC->Broadcast( rHint );
+ bIsBroadcasted = true;
+ pLastBC = pBC;
+ }
+ }
+ return bIsBroadcasted;
+}
+
+void ScDocument::BroadcastCells( const ScRange& rRange, SfxHintId nHint, bool bBroadcastSingleBroadcasters )
+{
+ PrepareFormulaCalc();
+
+ if (!pBASM)
+ return; // Clipboard or Undo
+
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCROW nRow2 = rRange.aEnd.Row();
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCCOL nCol2 = rRange.aEnd.Col();
+
+ if (eHardRecalcState == HardRecalcState::OFF)
+ {
+ ScBulkBroadcast aBulkBroadcast( pBASM.get(), nHint); // scoped bulk broadcast
+ bool bIsBroadcasted = false;
+
+ if (bBroadcastSingleBroadcasters)
+ {
+ for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ bIsBroadcasted |= pTab->BroadcastBroadcasters( nCol1, nRow1, nCol2, nRow2, nHint);
+ }
+ }
+
+ if (pBASM->AreaBroadcast(rRange, nHint) || bIsBroadcasted)
+ TrackFormulas(nHint);
+ }
+
+ for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (pTab)
+ pTab->SetStreamValid(false);
+ }
+
+ BroadcastUno(SfxHint(SfxHintId::ScDataChanged));
+}
+
+void ScDocument::AreaBroadcast( const ScHint& rHint )
+{
+ if ( !pBASM )
+ return ; // Clipboard or Undo
+ if (eHardRecalcState == HardRecalcState::OFF)
+ {
+ ScBulkBroadcast aBulkBroadcast( pBASM.get(), rHint.GetId()); // scoped bulk broadcast
+ if ( pBASM->AreaBroadcast( rHint ) )
+ TrackFormulas( rHint.GetId() );
+ }
+}
+
+void ScDocument::DelBroadcastAreasInRange( const ScRange& rRange )
+{
+ if ( pBASM )
+ pBASM->DelBroadcastAreasInRange( rRange );
+}
+
+void ScDocument::StartListeningCell( const ScAddress& rAddress,
+ SvtListener* pListener )
+{
+ OSL_ENSURE(pListener, "StartListeningCell: pListener Null");
+ SCTAB nTab = rAddress.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->StartListening(rAddress, pListener);
+}
+
+void ScDocument::EndListeningCell( const ScAddress& rAddress,
+ SvtListener* pListener )
+{
+ OSL_ENSURE(pListener, "EndListeningCell: pListener Null");
+ SCTAB nTab = rAddress.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->EndListening( rAddress, pListener );
+}
+
+void ScDocument::StartListeningCell(
+ sc::StartListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ pTable->StartListening(rCxt, rPos, rListener);
+}
+
+void ScDocument::EndListeningCell(
+ sc::EndListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ pTable->EndListening(rCxt, rPos, rListener);
+}
+
+void ScDocument::EndListeningFormulaCells( std::vector<ScFormulaCell*>& rCells )
+{
+ if (rCells.empty())
+ return;
+
+ sc::EndListeningContext aCxt(*this);
+ for (auto& pCell : rCells)
+ pCell->EndListeningTo(aCxt);
+
+ aCxt.purgeEmptyBroadcasters();
+}
+
+void ScDocument::PutInFormulaTree( ScFormulaCell* pCell )
+{
+ OSL_ENSURE( pCell, "PutInFormulaTree: pCell Null" );
+ RemoveFromFormulaTree( pCell );
+ // append
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ if ( pEOFormulaTree )
+ pEOFormulaTree->SetNext( pCell );
+ else
+ pFormulaTree = pCell; // No end, no beginning...
+ pCell->SetPrevious( pEOFormulaTree );
+ pCell->SetNext( nullptr );
+ pEOFormulaTree = pCell;
+ nFormulaCodeInTree += pCell->GetCode()->GetCodeLen();
+}
+
+void ScDocument::RemoveFromFormulaTree( ScFormulaCell* pCell )
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ OSL_ENSURE( pCell, "RemoveFromFormulaTree: pCell Null" );
+ ScFormulaCell* pPrev = pCell->GetPrevious();
+ assert(pPrev != pCell); // pointing to itself?!?
+ // if the cell is first or somewhere in chain
+ if ( pPrev || pFormulaTree == pCell )
+ {
+ ScFormulaCell* pNext = pCell->GetNext();
+ assert(pNext != pCell); // pointing to itself?!?
+ if ( pPrev )
+ {
+ assert(pFormulaTree != pCell); // if this cell is also head something's wrong
+ pPrev->SetNext( pNext ); // predecessor exists, set successor
+ }
+ else
+ {
+ pFormulaTree = pNext; // this cell was first cell
+ }
+ if ( pNext )
+ {
+ assert(pEOFormulaTree != pCell); // if this cell is also tail something's wrong
+ pNext->SetPrevious( pPrev ); // successor exists, set predecessor
+ }
+ else
+ {
+ pEOFormulaTree = pPrev; // this cell was last cell
+ }
+ pCell->SetPrevious( nullptr );
+ pCell->SetNext( nullptr );
+ sal_uInt16 nRPN = pCell->GetCode()->GetCodeLen();
+ if ( nFormulaCodeInTree >= nRPN )
+ nFormulaCodeInTree -= nRPN;
+ else
+ {
+ OSL_FAIL( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" );
+ nFormulaCodeInTree = 0;
+ }
+ }
+ else if ( !pFormulaTree && nFormulaCodeInTree )
+ {
+ OSL_FAIL( "!pFormulaTree && nFormulaCodeInTree != 0" );
+ nFormulaCodeInTree = 0;
+ }
+}
+
+void ScDocument::CalcFormulaTree( bool bOnlyForced, bool bProgressBar, bool bSetAllDirty )
+{
+ OSL_ENSURE( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" );
+ // never ever recurse into this, might end up lost in infinity
+ if ( IsCalculatingFormulaTree() )
+ return ;
+
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpFormulaGroupCxt.reset();
+ bCalculatingFormulaTree = true;
+
+ SetForcedFormulaPending( false );
+ bool bOldIdleEnabled = IsIdleEnabled();
+ EnableIdle(false);
+ bool bOldAutoCalc = GetAutoCalc();
+ //ATTENTION: _not_ SetAutoCalc( true ) because this might call CalcFormulaTree( true )
+ //ATTENTION: if it was disabled before and bHasForcedFormulas is set
+ bAutoCalc = true;
+ if (eHardRecalcState == HardRecalcState::ETERNAL)
+ CalcAll();
+ else
+ {
+ ::std::vector<ScFormulaCell*> vAlwaysDirty;
+ ScFormulaCell* pCell = pFormulaTree;
+ while ( pCell )
+ {
+ if ( pCell->GetDirty() )
+ ; // nothing to do
+ else if ( pCell->GetCode()->IsRecalcModeAlways() )
+ {
+ // pCell and dependents are to be set dirty again, collect
+ // them first and broadcast afterwards to not break the
+ // FormulaTree chain here.
+ vAlwaysDirty.push_back( pCell);
+ }
+ else if ( bSetAllDirty )
+ {
+ // Force calculating all in tree, without broadcasting.
+ pCell->SetDirtyVar();
+ }
+ pCell = pCell->GetNext();
+ }
+ for (const auto& rpCell : vAlwaysDirty)
+ {
+ pCell = rpCell;
+ if (!pCell->GetDirty())
+ pCell->SetDirty();
+ }
+
+ bool bProgress = !bOnlyForced && nFormulaCodeInTree && bProgressBar;
+ if ( bProgress )
+ ScProgress::CreateInterpretProgress( this );
+
+ pCell = pFormulaTree;
+ ScFormulaCell* pLastNoGood = nullptr;
+ while ( pCell )
+ {
+ // Interpret resets bDirty and calls Remove, also the referenced!
+ // the Cell remains when ScRecalcMode::ALWAYS.
+ if ( bOnlyForced )
+ {
+ if ( pCell->GetCode()->IsRecalcModeForced() )
+ pCell->Interpret();
+ }
+ else
+ {
+ pCell->Interpret();
+ }
+ if ( pCell->GetPrevious() || pCell == pFormulaTree )
+ { // (IsInFormulaTree(pCell)) no Remove was called => next
+ pLastNoGood = pCell;
+ pCell = pCell->GetNext();
+ }
+ else
+ {
+ if ( pFormulaTree )
+ {
+ if ( pFormulaTree->GetDirty() && !bOnlyForced )
+ {
+ pCell = pFormulaTree;
+ pLastNoGood = nullptr;
+ }
+ else
+ {
+ // IsInFormulaTree(pLastNoGood)
+ if ( pLastNoGood && (pLastNoGood->GetPrevious() ||
+ pLastNoGood == pFormulaTree) )
+ pCell = pLastNoGood->GetNext();
+ else
+ {
+ pCell = pFormulaTree;
+ while ( pCell && !pCell->GetDirty() )
+ pCell = pCell->GetNext();
+ if ( pCell )
+ pLastNoGood = pCell->GetPrevious();
+ }
+ }
+ }
+ else
+ pCell = nullptr;
+ }
+ }
+ if ( bProgress )
+ ScProgress::DeleteInterpretProgress();
+ }
+ bAutoCalc = bOldAutoCalc;
+ EnableIdle(bOldIdleEnabled);
+ bCalculatingFormulaTree = false;
+
+ mpFormulaGroupCxt.reset();
+}
+
+void ScDocument::ClearFormulaTree()
+{
+ ScFormulaCell* pCell;
+ ScFormulaCell* pTree = pFormulaTree;
+ while ( pTree )
+ {
+ pCell = pTree;
+ pTree = pCell->GetNext();
+ if ( !pCell->GetCode()->IsRecalcModeAlways() )
+ RemoveFromFormulaTree( pCell );
+ }
+}
+
+void ScDocument::AppendToFormulaTrack( ScFormulaCell* pCell )
+{
+ OSL_ENSURE( pCell, "AppendToFormulaTrack: pCell Null" );
+ // The cell can not be in both lists at the same time
+ RemoveFromFormulaTrack( pCell );
+ RemoveFromFormulaTree( pCell );
+ if ( pEOFormulaTrack )
+ pEOFormulaTrack->SetNextTrack( pCell );
+ else
+ pFormulaTrack = pCell; // No end, no beginning...
+ pCell->SetPreviousTrack( pEOFormulaTrack );
+ pCell->SetNextTrack( nullptr );
+ pEOFormulaTrack = pCell;
+ ++nFormulaTrackCount;
+}
+
+void ScDocument::RemoveFromFormulaTrack( ScFormulaCell* pCell )
+{
+ OSL_ENSURE( pCell, "RemoveFromFormulaTrack: pCell Null" );
+ ScFormulaCell* pPrev = pCell->GetPreviousTrack();
+ assert(pPrev != pCell); // pointing to itself?!?
+ // if the cell is first or somewhere in chain
+ if ( !(pPrev || pFormulaTrack == pCell) )
+ return;
+
+ ScFormulaCell* pNext = pCell->GetNextTrack();
+ assert(pNext != pCell); // pointing to itself?!?
+ if ( pPrev )
+ {
+ assert(pFormulaTrack != pCell); // if this cell is also head something's wrong
+ pPrev->SetNextTrack( pNext ); // predecessor exists, set successor
+ }
+ else
+ {
+ pFormulaTrack = pNext; // this cell was first cell
+ }
+ if ( pNext )
+ {
+ assert(pEOFormulaTrack != pCell); // if this cell is also tail something's wrong
+ pNext->SetPreviousTrack( pPrev ); // successor exists, set predecessor
+ }
+ else
+ {
+ pEOFormulaTrack = pPrev; // this cell was last cell
+ }
+ pCell->SetPreviousTrack( nullptr );
+ pCell->SetNextTrack( nullptr );
+ --nFormulaTrackCount;
+}
+
+void ScDocument::FinalTrackFormulas( SfxHintId nHintId )
+{
+ mbTrackFormulasPending = false;
+ mbFinalTrackFormulas = true;
+ {
+ ScBulkBroadcast aBulk( GetBASM(), nHintId);
+ // Collect all pending formula cells in bulk.
+ TrackFormulas( nHintId );
+ }
+ // A final round not in bulk to track all remaining formula cells and their
+ // dependents that were collected during ScBulkBroadcast dtor.
+ TrackFormulas( nHintId );
+ mbFinalTrackFormulas = false;
+}
+
+/*
+ The first is broadcasted,
+ the ones that are created through this are appended to the Track by Notify.
+ The next is broadcasted again, and so on.
+ View initiates Interpret.
+ */
+void ScDocument::TrackFormulas( SfxHintId nHintId )
+{
+ if (!pBASM)
+ return;
+
+ if (pBASM->IsInBulkBroadcast() && !IsFinalTrackFormulas() &&
+ (nHintId == SfxHintId::ScDataChanged || nHintId == SfxHintId::ScHiddenRowsChanged))
+ {
+ SetTrackFormulasPending();
+ return;
+ }
+
+ if ( pFormulaTrack )
+ {
+ // outside the loop, check if any sheet has a "calculate" event script
+ bool bCalcEvent = HasAnySheetEventScript( ScSheetEventId::CALCULATE, true );
+ for( ScFormulaCell* pTrack = pFormulaTrack; pTrack != nullptr; pTrack = pTrack->GetNextTrack())
+ {
+ SCROW rowCount = 1;
+ ScAddress address = pTrack->aPos;
+ // Compress to include all adjacent cells in the same column.
+ for(ScFormulaCell* pNext = pTrack->GetNextTrack(); pNext != nullptr; pNext = pNext->GetNextTrack())
+ {
+ if(pNext->aPos != ScAddress(address.Col(), address.Row() + rowCount, address.Tab()))
+ break;
+ ++rowCount;
+ pTrack = pNext;
+ }
+ ScHint aHint( nHintId, address, rowCount );
+ BroadcastHintInternal( aHint );
+ pBASM->AreaBroadcast( aHint );
+ // for "calculate" event, keep track of which sheets are affected by tracked formulas
+ if ( bCalcEvent )
+ SetCalcNotification( address.Tab() );
+ }
+ bool bHaveForced = false;
+ for( ScFormulaCell* pTrack = pFormulaTrack; pTrack != nullptr;)
+ {
+ ScFormulaCell* pNext = pTrack->GetNextTrack();
+ RemoveFromFormulaTrack( pTrack );
+ PutInFormulaTree( pTrack );
+ if ( pTrack->GetCode()->IsRecalcModeForced() )
+ bHaveForced = true;
+ pTrack = pNext;
+ }
+ if ( bHaveForced )
+ {
+ SetForcedFormulas( true );
+ if ( bAutoCalc && !IsAutoCalcShellDisabled() && !IsInInterpreter()
+ && !IsCalculatingFormulaTree() )
+ CalcFormulaTree( true );
+ else
+ SetForcedFormulaPending( true );
+ }
+ }
+ OSL_ENSURE( nFormulaTrackCount==0, "TrackFormulas: nFormulaTrackCount!=0" );
+}
+
+void ScDocument::StartAllListeners()
+{
+ sc::StartListeningContext aCxt(*this);
+ for ( auto const & i: maTabs )
+ if ( i )
+ i->StartListeners(aCxt, true);
+}
+
+void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz
+ )
+{
+ bool bExpandRefsOld = IsExpandRefs();
+ if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) )
+ SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
+ if ( pBASM )
+ pBASM->UpdateBroadcastAreas( eUpdateRefMode, rRange, nDx, nDy, nDz );
+ SetExpandRefs( bExpandRefsOld );
+}
+
+void ScDocument::SetAutoCalc( bool bNewAutoCalc )
+{
+ bool bOld = bAutoCalc;
+ bAutoCalc = bNewAutoCalc;
+ if ( !bOld && bNewAutoCalc && bHasForcedFormulas )
+ {
+ if ( IsAutoCalcShellDisabled() )
+ SetForcedFormulaPending( true );
+ else if ( !IsInInterpreter() )
+ CalcFormulaTree( true );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx
new file mode 100644
index 0000000000..50748236ee
--- /dev/null
+++ b/sc/source/core/data/documen8.cxx
@@ -0,0 +1,1328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/langitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svl/flagitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/ctloptions.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/TaskStopwatch.hxx>
+
+#include <inputopt.hxx>
+#include <global.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <poolhelp.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <docoptio.hxx>
+#include <viewopti.hxx>
+#include <scextopt.hxx>
+#include <rechead.hxx>
+#include <ddelink.hxx>
+#include <scmatrix.hxx>
+#include <arealink.hxx>
+#include <patattr.hxx>
+#include <editutil.hxx>
+#include <progress.hxx>
+#include <document.hxx>
+#include <chartlis.hxx>
+#include <chartlock.hxx>
+#include <refupdat.hxx>
+#include <markdata.hxx>
+#include <scmod.hxx>
+#include <externalrefmgr.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <sc.hrc>
+#include <charthelper.hxx>
+#include <macromgr.hxx>
+#include <docuno.hxx>
+#include <scresid.hxx>
+#include <columniterator.hxx>
+#include <globalnames.hxx>
+#include <stringutil.hxx>
+#include <documentlinkmgr.hxx>
+#include <tokenarray.hxx>
+#include <recursionhelper.hxx>
+
+#include <memory>
+#include <utility>
+
+using namespace com::sun::star;
+
+namespace {
+
+sal_uInt16 getScaleValue(SfxStyleSheetBase& rStyle, sal_uInt16 nWhich)
+{
+ return static_cast<const SfxUInt16Item&>(rStyle.GetItemSet().Get(nWhich)).GetValue();
+}
+
+}
+
+void ScDocument::ImplCreateOptions()
+{
+ pDocOptions.reset( new ScDocOptions() );
+ pViewOptions.reset( new ScViewOptions() );
+}
+
+void ScDocument::ImplDeleteOptions()
+{
+ pDocOptions.reset();
+ pViewOptions.reset();
+ pExtDocOptions.reset();
+}
+
+SfxPrinter* ScDocument::GetPrinter(bool bCreateIfNotExist)
+{
+ if (!mpPrinter && bCreateIfNotExist && mxPoolHelper)
+ {
+ auto pSet =
+ std::make_unique<SfxItemSetFixed
+ <SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
+ SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
+ SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET,
+ SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS>>(*mxPoolHelper->GetDocPool());
+
+ SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
+ if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
+ nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
+ if (officecfg::Office::Common::Print::Warning::PaperSize::get())
+ nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
+ pSet->Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
+ pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );
+
+ mpPrinter = VclPtr<SfxPrinter>::Create( std::move(pSet) );
+ mpPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
+ UpdateDrawPrinter();
+ mpPrinter->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ }
+
+ return mpPrinter;
+}
+
+void ScDocument::SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter )
+{
+ if ( pNewPrinter == mpPrinter.get() )
+ {
+ // #i6706# SetPrinter is called with the same printer again if
+ // the JobSetup has changed. In that case just call UpdateDrawPrinter
+ // (SetRefDevice for drawing layer) because of changed text sizes.
+ UpdateDrawPrinter();
+ }
+ else
+ {
+ ScopedVclPtr<SfxPrinter> xKeepAlive( mpPrinter );
+ mpPrinter = pNewPrinter;
+ UpdateDrawPrinter();
+ mpPrinter->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ }
+ InvalidateTextWidth(nullptr, nullptr, false); // in both cases
+}
+
+void ScDocument::SetPrintOptions()
+{
+ if ( !mpPrinter ) GetPrinter(); // this sets mpPrinter
+ OSL_ENSURE( mpPrinter, "Error in printer creation :-/" );
+
+ if ( !mpPrinter )
+ return;
+
+ SfxItemSet aOptSet( mpPrinter->GetOptions() );
+
+ SfxPrinterChangeFlags nFlags = SfxPrinterChangeFlags::NONE;
+ if (officecfg::Office::Common::Print::Warning::PaperOrientation::get())
+ nFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
+ if (officecfg::Office::Common::Print::Warning::PaperSize::get())
+ nFlags |= SfxPrinterChangeFlags::CHG_SIZE;
+ aOptSet.Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, static_cast<int>(nFlags) ) );
+ aOptSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, officecfg::Office::Common::Print::Warning::NotFound::get() ) );
+
+ mpPrinter->SetOptions( aOptSet );
+}
+
+VirtualDevice* ScDocument::GetVirtualDevice_100th_mm()
+{
+ if (!mpVirtualDevice_100th_mm)
+ {
+#ifdef IOS
+ mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::GRAYSCALE);
+#else
+ mpVirtualDevice_100th_mm = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA);
+#endif
+ mpVirtualDevice_100th_mm->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1);
+ MapMode aMapMode( mpVirtualDevice_100th_mm->GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::Map100thMM );
+ mpVirtualDevice_100th_mm->SetMapMode( aMapMode );
+ }
+ return mpVirtualDevice_100th_mm;
+}
+
+OutputDevice* ScDocument::GetRefDevice(bool bForceVirtDev)
+{
+ // Create printer like ref device, see Writer...
+ OutputDevice* pRefDevice = nullptr;
+ if ( !bForceVirtDev && SC_MOD()->GetInputOptions().GetTextWysiwyg() )
+ {
+ pRefDevice = GetPrinter();
+ SAL_WARN_IF(!pRefDevice, "sc", "unable to get a printer, fallback to virdev");
+ }
+ if (!pRefDevice)
+ pRefDevice = GetVirtualDevice_100th_mm();
+ return pRefDevice;
+}
+
+void ScDocument::ModifyStyleSheet( SfxStyleSheetBase& rStyleSheet,
+ const SfxItemSet& rChanges )
+{
+ SfxItemSet& rSet = rStyleSheet.GetItemSet();
+
+ switch ( rStyleSheet.GetFamily() )
+ {
+ case SfxStyleFamily::Page:
+ {
+ const sal_uInt16 nOldScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
+ const sal_uInt16 nOldScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
+ rSet.Put( rChanges );
+ const sal_uInt16 nNewScale = getScaleValue(rStyleSheet, ATTR_PAGE_SCALE);
+ const sal_uInt16 nNewScaleToPages = getScaleValue(rStyleSheet, ATTR_PAGE_SCALETOPAGES);
+
+ if ( (nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages) )
+ InvalidateTextWidth( rStyleSheet.GetName() );
+
+ if( SvtCTLOptions::IsCTLFontEnabled() )
+ {
+ if( rChanges.GetItemState(ATTR_WRITINGDIR ) == SfxItemState::SET )
+ ScChartHelper::DoUpdateAllCharts( *this );
+ }
+ }
+ break;
+
+ case SfxStyleFamily::Para:
+ {
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rSet, rChanges ) )
+ InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );
+
+ for (SCTAB nTab=0; nTab<=MAXTAB; ++nTab)
+ if (maTabs[nTab])
+ maTabs[nTab]->SetStreamValid( false );
+
+ sal_uLong nOldFormat =
+ rSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ sal_uLong nNewFormat =
+ rChanges.Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType eNewLang, eOldLang;
+ eNewLang = eOldLang = LANGUAGE_DONTKNOW;
+ if ( nNewFormat != nOldFormat )
+ {
+ SvNumberFormatter* pFormatter = GetFormatTable();
+ eOldLang = pFormatter->GetEntry( nOldFormat )->GetLanguage();
+ eNewLang = pFormatter->GetEntry( nNewFormat )->GetLanguage();
+ }
+
+ // Explanation to Items in rChanges:
+ // Set Item - take over change
+ // Dontcare - Set Default
+ // Default - No change
+ // ("no change" is not possible with PutExtended, thus the loop)
+ for (sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++)
+ {
+ const SfxPoolItem* pItem;
+ SfxItemState eState = rChanges.GetItemState( nWhich, false, &pItem );
+ if ( eState == SfxItemState::SET )
+ rSet.Put( *pItem );
+ else if ( eState == SfxItemState::DONTCARE )
+ rSet.ClearItem( nWhich );
+ // when Default nothing
+ }
+
+ if ( eNewLang != eOldLang )
+ rSet.Put(
+ SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScDocument::CopyStdStylesFrom( const ScDocument& rSrcDoc )
+{
+ // number format exchange list has to be handled here, too
+ NumFmtMergeHandler aNumFmtMergeHdl(*this, rSrcDoc);
+ mxPoolHelper->GetStylePool()->CopyStdStylesFrom( rSrcDoc.mxPoolHelper->GetStylePool() );
+}
+
+void ScDocument::InvalidateTextWidth( std::u16string_view rStyleName )
+{
+ const SCTAB nCount = GetTableCount();
+ for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
+ if ( maTabs[i]->GetPageStyle() == rStyleName )
+ InvalidateTextWidth( i );
+}
+
+void ScDocument::InvalidateTextWidth( SCTAB nTab )
+{
+ ScAddress aAdrFrom( 0, 0, nTab );
+ ScAddress aAdrTo ( MaxCol(), MaxRow(), nTab );
+ InvalidateTextWidth( &aAdrFrom, &aAdrTo, false );
+}
+
+bool ScDocument::IsPageStyleInUse( std::u16string_view rStrPageStyle, SCTAB* pInTab )
+{
+ bool bInUse = false;
+ const SCTAB nCount = GetTableCount();
+ SCTAB i;
+
+ for ( i = 0; !bInUse && i < nCount && maTabs[i]; i++ )
+ bInUse = ( maTabs[i]->GetPageStyle() == rStrPageStyle );
+
+ if ( pInTab )
+ *pInTab = i-1;
+
+ return bInUse;
+}
+
+bool ScDocument::RemovePageStyleInUse( std::u16string_view rStyle )
+{
+ bool bWasInUse = false;
+ const SCTAB nCount = GetTableCount();
+
+ for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
+ if ( maTabs[i]->GetPageStyle() == rStyle )
+ {
+ bWasInUse = true;
+ maTabs[i]->SetPageStyle( ScResId(STR_STYLENAME_STANDARD) );
+ }
+
+ return bWasInUse;
+}
+
+bool ScDocument::RenamePageStyleInUse( std::u16string_view rOld, const OUString& rNew )
+{
+ bool bWasInUse = false;
+ const SCTAB nCount = GetTableCount();
+
+ for ( SCTAB i=0; i<nCount && maTabs[i]; i++ )
+ if ( maTabs[i]->GetPageStyle() == rOld )
+ {
+ bWasInUse = true;
+ maTabs[i]->SetPageStyle( rNew );
+ }
+
+ return bWasInUse;
+}
+
+EEHorizontalTextDirection ScDocument::GetEditTextDirection(SCTAB nTab) const
+{
+ EEHorizontalTextDirection eRet = EEHorizontalTextDirection::Default;
+
+ OUString aStyleName = GetPageStyle( nTab );
+ SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( aStyleName, SfxStyleFamily::Page );
+ if ( pStyle )
+ {
+ SfxItemSet& rStyleSet = pStyle->GetItemSet();
+ SvxFrameDirection eDirection =
+ rStyleSet.Get( ATTR_WRITINGDIR ).GetValue();
+
+ if ( eDirection == SvxFrameDirection::Horizontal_LR_TB )
+ eRet = EEHorizontalTextDirection::L2R;
+ else if ( eDirection == SvxFrameDirection::Horizontal_RL_TB )
+ eRet = EEHorizontalTextDirection::R2L;
+ // else (invalid for EditEngine): keep "default"
+ }
+
+ return eRet;
+}
+
+ScMacroManager* ScDocument::GetMacroManager()
+{
+ if (!mpMacroMgr)
+ mpMacroMgr.reset(new ScMacroManager(*this));
+ return mpMacroMgr.get();
+}
+
+void ScDocument::FillMatrix(
+ ScMatrix& rMat, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool* pPool ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ if (nCol1 > nCol2 || nRow1 > nRow2)
+ return;
+
+ SCSIZE nC, nR;
+ rMat.GetDimensions(nC, nR);
+ if (static_cast<SCROW>(nR) != nRow2 - nRow1 + 1 || static_cast<SCCOL>(nC) != nCol2 - nCol1 + 1)
+ return;
+
+ pTab->FillMatrix(rMat, nCol1, nRow1, nCol2, nRow2, pPool);
+}
+
+void ScDocument::SetFormulaResults( const ScAddress& rTopPos, const double* pResults, size_t nLen )
+{
+ ScTable* pTab = FetchTable(rTopPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->SetFormulaResults(rTopPos.Col(), rTopPos.Row(), pResults, nLen);
+}
+
+void ScDocument::CalculateInColumnInThread( ScInterpreterContext& rContext, const ScRange& rCalcRange, unsigned nThisThread, unsigned nThreadsTotal)
+{
+ ScTable* pTab = FetchTable(rCalcRange.aStart.Tab());
+ if (!pTab)
+ return;
+
+ assert(IsThreadedGroupCalcInProgress());
+
+ maThreadSpecific.pContext = &rContext;
+ pTab->CalculateInColumnInThread(rContext, rCalcRange.aStart.Col(), rCalcRange.aEnd.Col(), rCalcRange.aStart.Row(), rCalcRange.aEnd.Row(), nThisThread, nThreadsTotal);
+
+ assert(IsThreadedGroupCalcInProgress());
+ maThreadSpecific.pContext = nullptr;
+ // If any of the thread_local data would cause problems if they stay around for too long
+ // (and e.g. outlive the ScDocument), clean them up here, they cannot be cleaned up
+ // later from the main thread.
+ if(maThreadSpecific.xRecursionHelper)
+ maThreadSpecific.xRecursionHelper->Clear();
+}
+
+void ScDocument::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab, ScInterpreter* pInterpreter )
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ for( const DelayedSetNumberFormat& data : GetNonThreadedContext().maDelayedSetNumberFormat)
+ SetNumberFormat( ScAddress( data.mCol, data.mRow, nTab ), data.mnNumberFormat );
+ GetNonThreadedContext().maDelayedSetNumberFormat.clear();
+
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->HandleStuffAfterParallelCalculation(nColStart, nColEnd, nRow, nLen, pInterpreter);
+}
+
+void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo,
+ bool bNumFormatChanged )
+{
+ bool bBroadcast = (bNumFormatChanged && GetDocOptions().IsCalcAsShown() && !IsImportingXML() && !IsClipboard());
+ if ( pAdrFrom && !pAdrTo )
+ {
+ const SCTAB nTab = pAdrFrom->Tab();
+
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->InvalidateTextWidth( pAdrFrom, nullptr, bNumFormatChanged, bBroadcast );
+ }
+ else
+ {
+ const SCTAB nTabStart = pAdrFrom ? pAdrFrom->Tab() : 0;
+ const SCTAB nTabEnd = pAdrTo ? pAdrTo->Tab() : MAXTAB;
+
+ for ( SCTAB nTab=nTabStart; nTab<=nTabEnd && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
+ if ( maTabs[nTab] )
+ maTabs[nTab]->InvalidateTextWidth( pAdrFrom, pAdrTo, bNumFormatChanged, bBroadcast );
+ }
+}
+
+#define CALCMAX 1000 // Calculations
+
+namespace {
+
+class IdleCalcTextWidthScope : public TaskStopwatch
+{
+ ScDocument& mrDoc;
+ ScAddress& mrCalcPos;
+ MapMode maOldMapMode;
+ ScStyleSheetPool* mpStylePool;
+ bool mbNeedMore;
+ bool mbProgress;
+
+public:
+ IdleCalcTextWidthScope(ScDocument& rDoc, ScAddress& rCalcPos) :
+ mrDoc(rDoc),
+ mrCalcPos(rCalcPos),
+ mpStylePool(rDoc.GetStyleSheetPool()),
+ mbNeedMore(false),
+ mbProgress(false)
+ {
+ mrDoc.EnableIdle(false);
+ }
+
+ ~IdleCalcTextWidthScope() COVERITY_NOEXCEPT_FALSE
+ {
+ SfxPrinter* pDev = mrDoc.GetPrinter();
+ if (pDev)
+ pDev->SetMapMode(maOldMapMode);
+
+ if (mbProgress)
+ ScProgress::DeleteInterpretProgress();
+
+ mrDoc.EnableIdle(true);
+ }
+
+ SCTAB Tab() const { return mrCalcPos.Tab(); }
+ SCCOL Col() const { return mrCalcPos.Col(); }
+ SCROW Row() const { return mrCalcPos.Row(); }
+
+ void setTab(SCTAB nTab) { mrCalcPos.SetTab(nTab); }
+ void setCol(SCCOL nCol) { mrCalcPos.SetCol(nCol); }
+ void setRow(SCROW nRow) { mrCalcPos.SetRow(nRow); }
+
+ void incTab() { mrCalcPos.IncTab(); }
+ void incCol(SCCOL nInc) { mrCalcPos.IncCol(nInc); }
+
+ void setOldMapMode(const MapMode& rOldMapMode) { maOldMapMode = rOldMapMode; }
+
+ void setNeedMore(bool b) { mbNeedMore = b; }
+ bool getNeedMore() const { return mbNeedMore; }
+
+ void createProgressBar()
+ {
+ ScProgress::CreateInterpretProgress(&mrDoc, false);
+ mbProgress = true;
+ }
+
+ bool hasProgressBar() const { return mbProgress; }
+
+ ScStyleSheetPool* getStylePool() { return mpStylePool; }
+};
+
+}
+
+bool ScDocument::IdleCalcTextWidth() // true = try next again
+{
+ // #i75610# if a printer hasn't been set or created yet, don't create one for this
+ if (!mbIdleEnabled || IsInLinkUpdate() || GetPrinter(false) == nullptr)
+ return false;
+
+ IdleCalcTextWidthScope aScope(*this, aCurTextWidthCalcPos);
+
+ if (!ValidRow(aScope.Row()))
+ {
+ aScope.setRow(0);
+ aScope.incCol(-1);
+ }
+
+ if (aScope.Col() < 0)
+ {
+ aScope.setCol(MaxCol());
+ aScope.incTab();
+ }
+
+ if (!HasTable(aScope.Tab()))
+ aScope.setTab(0);
+
+ ScTable* pTab = maTabs[aScope.Tab()].get();
+ ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(pTab->aPageStyle, SfxStyleFamily::Page));
+ OSL_ENSURE( pStyle, "Missing StyleSheet :-/" );
+
+ if (!pStyle || getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
+ {
+ // Move to the next sheet as the current one has scale-to-pages set,
+ // and bail out.
+ aScope.incTab();
+ return false;
+ }
+
+ sal_uInt16 nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
+ Fraction aZoomFract(nZoom, 100);
+
+ aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
+ // Start at specified cell position (nCol, nRow, nTab).
+ ScColumn* pCol = &pTab->aCol[aScope.Col()];
+ std::optional<ScColumnTextWidthIterator> pColIter(std::in_place, *this, *pCol, aScope.Row(), MaxRow());
+
+ OutputDevice* pDev = nullptr;
+ sal_uInt16 nRestart = 0;
+ sal_uInt16 nCount = 0;
+ while ( (nZoom > 0) && (nCount < CALCMAX) && (nRestart < 2) )
+ {
+ if (pColIter->hasCell())
+ {
+ // More cell in this column.
+ SCROW nRow = pColIter->getPos();
+ aScope.setRow(nRow);
+
+ if (pColIter->getValue() == TEXTWIDTH_DIRTY)
+ {
+ // Calculate text width for this cell.
+ double nPPTX = 0.0;
+ double nPPTY = 0.0;
+ if (!pDev)
+ {
+ pDev = GetPrinter();
+ aScope.setOldMapMode(pDev->GetMapMode());
+ pDev->SetMapMode(MapMode(MapUnit::MapPixel)); // Important for GetNeededSize
+
+ Point aPix1000 = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ nPPTX = aPix1000.X() / 1000.0;
+ nPPTY = aPix1000.Y() / 1000.0;
+ }
+
+ if (!aScope.hasProgressBar() && pCol->IsFormulaDirty(nRow))
+ aScope.createProgressBar();
+
+ sal_uInt16 nNewWidth = static_cast<sal_uInt16>(GetNeededSize(
+ aScope.Col(), aScope.Row(), aScope.Tab(),
+ pDev, nPPTX, nPPTY, aZoomFract,aZoomFract, true, true)); // bTotalSize
+
+ pColIter->setValue(nNewWidth);
+ aScope.setNeedMore(true);
+ }
+ pColIter->next();
+ }
+ else
+ {
+ // No more cell in this column. Move to the left column and start at row 0.
+
+ bool bNewTab = false;
+
+ aScope.setRow(0);
+ aScope.incCol(-1);
+
+ if (aScope.Col() < 0)
+ {
+ // No more column to the left. Move to the right-most column of the next sheet.
+ aScope.setCol(MaxCol());
+ aScope.incTab();
+ bNewTab = true;
+ }
+
+ if (!HasTable(aScope.Tab()))
+ {
+ // Sheet doesn't exist at specified sheet position. Restart at sheet 0.
+ aScope.setTab(0);
+ nRestart++;
+ bNewTab = true;
+ }
+
+ if ( nRestart < 2 )
+ {
+ if ( bNewTab )
+ {
+ pTab = maTabs[aScope.Tab()].get();
+ aScope.setCol(pTab->ClampToAllocatedColumns(aScope.Col()));
+ pStyle = static_cast<ScStyleSheet*>(aScope.getStylePool()->Find(
+ pTab->aPageStyle, SfxStyleFamily::Page));
+
+ if ( pStyle )
+ {
+ // Check if the scale-to-pages setting is set. If
+ // set, we exit the loop. If not, get the page
+ // scale factor of the new sheet.
+ if (getScaleValue(*pStyle, ATTR_PAGE_SCALETOPAGES) == 0)
+ {
+ nZoom = getScaleValue(*pStyle, ATTR_PAGE_SCALE);
+ aZoomFract = Fraction(nZoom, 100);
+ }
+ else
+ nZoom = 0;
+ }
+ else
+ {
+ OSL_FAIL( "Missing StyleSheet :-/" );
+ }
+ }
+
+ if ( nZoom > 0 )
+ {
+ pCol = &pTab->aCol[aScope.Col()];
+ pColIter.emplace(*this, *pCol, aScope.Row(), MaxRow());
+ }
+ else
+ {
+ aScope.incTab(); // Move to the next sheet as the current one has scale-to-pages set.
+ return false;
+ }
+ }
+ }
+
+ ++nCount;
+
+ if (!aScope.continueIter())
+ break;
+ }
+
+ return aScope.getNeedMore();
+}
+
+void ScDocument::RepaintRange( const ScRange& rRange )
+{
+ if ( bIsVisible && mpShell )
+ {
+ ScModelObj* pModel = mpShell->GetModel();
+ if ( pModel )
+ pModel->RepaintRange( rRange ); // locked repaints are checked there
+ }
+}
+
+void ScDocument::RepaintRange( const ScRangeList& rRange )
+{
+ if ( bIsVisible && mpShell )
+ {
+ ScModelObj* pModel = mpShell->GetModel();
+ if ( pModel )
+ pModel->RepaintRange( rRange ); // locked repaints are checked there
+ }
+}
+
+void ScDocument::SaveDdeLinks(SvStream& rStream) const
+{
+ // when 4.0-Export, remove all with mode != DEFAULT
+ bool bExport40 = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_40 );
+
+ const ::sfx2::SvBaseLinks& rLinks = GetLinkManager()->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+
+ // Count them first
+
+ sal_uInt16 nDdeCount = 0;
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
+ if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
+ ++nDdeCount;
+ }
+
+ // Header
+
+ ScMultipleWriteHeader aHdr( rStream );
+ rStream.WriteUInt16( nDdeCount );
+
+ // Save links
+
+ for (i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
+ {
+ if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT )
+ pLink->Store( rStream, aHdr );
+ }
+ }
+}
+
+void ScDocument::LoadDdeLinks(SvStream& rStream)
+{
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
+ if (!pMgr)
+ return;
+
+ ScMultipleReadHeader aHdr( rStream );
+
+ sal_uInt16 nCount(0);
+ rStream.ReadUInt16( nCount );
+
+ const rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
+ const size_t nMinStringSize = eCharSet == RTL_TEXTENCODING_UNICODE ? sizeof(sal_uInt32) : sizeof(sal_uInt16);
+ const size_t nMinRecordSize = 1 + nMinStringSize*3;
+ const size_t nMaxRecords = rStream.remainingSize() / nMinRecordSize;
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+
+ for (sal_uInt16 i=0; i<nCount; ++i)
+ {
+ ScDdeLink* pLink = new ScDdeLink( *this, rStream, aHdr );
+ pMgr->InsertDDELink(pLink, pLink->GetAppl(), pLink->GetTopic(), pLink->GetItem());
+ }
+}
+
+void ScDocument::SetInLinkUpdate(bool bSet)
+{
+ // called from TableLink and AreaLink
+
+ OSL_ENSURE( bInLinkUpdate != bSet, "SetInLinkUpdate twice" );
+ bInLinkUpdate = bSet;
+}
+
+bool ScDocument::IsInLinkUpdate() const
+{
+ return bInLinkUpdate || IsInDdeLinkUpdate();
+}
+
+void ScDocument::UpdateExternalRefLinks(weld::Window* pWin)
+{
+ if (!pExternalRefMgr)
+ return;
+
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
+ if (!pMgr)
+ return;
+
+ const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+
+ bool bAny = false;
+
+ // Collect all the external ref links first.
+ std::vector<ScExternalRefLink*> aRefLinks;
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ ScExternalRefLink* pRefLink = dynamic_cast<ScExternalRefLink*>(pBase);
+ if (pRefLink)
+ aRefLinks.push_back(pRefLink);
+ }
+
+ weld::WaitObject aWaitSwitch(pWin);
+
+ pExternalRefMgr->enableDocTimer(false);
+ ScProgress aProgress(GetDocumentShell(), ScResId(SCSTR_UPDATE_EXTDOCS), aRefLinks.size(), true);
+ for (size_t i = 0, n = aRefLinks.size(); i < n; ++i)
+ {
+ aProgress.SetState(i+1);
+
+ ScExternalRefLink* pRefLink = aRefLinks[i];
+ if (pRefLink->Update())
+ {
+ bAny = true;
+ continue;
+ }
+
+ // Update failed. Notify the user.
+
+ OUString aFile;
+ sfx2::LinkManager::GetDisplayNames(pRefLink, nullptr, &aFile);
+ // Decode encoded URL for display friendliness.
+ INetURLObject aUrl(aFile,INetURLObject::EncodeMechanism::WasEncoded);
+ aFile = aUrl.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
+
+ OUString sMessage = ScResId(SCSTR_EXTDOC_NOT_LOADED) +
+ "\n\n" +
+ aFile;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ sMessage));
+ xBox->run();
+ }
+
+ pExternalRefMgr->enableDocTimer(true);
+
+ if (!bAny)
+ return;
+
+ TrackFormulas();
+ mpShell->Broadcast( SfxHint(SfxHintId::ScDataChanged) );
+
+ // #i101960# set document modified, as in TrackTimeHdl for DDE links
+ if (!mpShell->IsModified())
+ {
+ mpShell->SetModified();
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_SAVEDOC );
+ pBindings->Invalidate( SID_DOC_MODIFIED );
+ }
+ }
+}
+
+void ScDocument::CopyDdeLinks( ScDocument& rDestDoc ) const
+{
+ if (bIsClip) // Create from Stream
+ {
+ if (pClipData)
+ {
+ pClipData->Seek(0);
+ rDestDoc.LoadDdeLinks(*pClipData);
+ }
+
+ return;
+ }
+
+ const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
+ if (!pMgr)
+ return;
+
+ sfx2::LinkManager* pDestMgr = rDestDoc.GetDocLinkManager().getLinkManager(rDestDoc.bAutoCalc);
+ if (!pDestMgr)
+ return;
+
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ const sfx2::SvBaseLink* pBase = rLink.get();
+ if (const ScDdeLink* p = dynamic_cast<const ScDdeLink*>(pBase))
+ {
+ ScDdeLink* pNew = new ScDdeLink(rDestDoc, *p);
+ pDestMgr->InsertDDELink(
+ pNew, pNew->GetAppl(), pNew->GetTopic(), pNew->GetItem());
+ }
+ }
+}
+
+namespace {
+
+/** Tries to find the specified DDE link.
+ @param pnDdePos (out-param) if not 0, the index of the DDE link is returned here
+ (does not include other links from link manager).
+ @return The DDE link, if it exists, otherwise 0. */
+ScDdeLink* lclGetDdeLink(
+ const sfx2::LinkManager* pLinkManager,
+ std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem, sal_uInt8 nMode,
+ size_t* pnDdePos = nullptr )
+{
+ if( pLinkManager )
+ {
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ if( pnDdePos ) *pnDdePos = 0;
+ for( const auto& nLinks : rLinks )
+ {
+ if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( nLinks.get() ) )
+ {
+ if( (pDdeLink->GetAppl() == rAppl) &&
+ (pDdeLink->GetTopic() == rTopic) &&
+ (pDdeLink->GetItem() == rItem) &&
+ ((nMode == SC_DDE_IGNOREMODE) || (nMode == pDdeLink->GetMode())) )
+ return pDdeLink;
+ if( pnDdePos ) ++*pnDdePos;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/** Returns a pointer to the specified DDE link.
+ @param nDdePos Index of the DDE link (does not include other links from link manager).
+ @return The DDE link, if it exists, otherwise 0. */
+ScDdeLink* lclGetDdeLink( const sfx2::LinkManager* pLinkManager, size_t nDdePos )
+{
+ if( pLinkManager )
+ {
+ size_t nDdeIndex = 0; // counts only the DDE links
+ for( const auto& pLink : pLinkManager->GetLinks() )
+ {
+ if( ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>( pLink.get() ) )
+ {
+ if( nDdeIndex == nDdePos )
+ return pDdeLink;
+ ++nDdeIndex;
+ }
+ }
+ }
+ return nullptr;
+}
+
+} // namespace
+
+bool ScDocument::FindDdeLink( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem,
+ sal_uInt8 nMode, size_t& rnDdePos )
+{
+ return lclGetDdeLink( GetLinkManager(), rAppl, rTopic, rItem, nMode, &rnDdePos ) != nullptr;
+}
+
+bool ScDocument::GetDdeLinkData( size_t nDdePos, OUString& rAppl, OUString& rTopic, OUString& rItem ) const
+{
+ if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
+ {
+ rAppl = pDdeLink->GetAppl();
+ rTopic = pDdeLink->GetTopic();
+ rItem = pDdeLink->GetItem();
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::GetDdeLinkMode( size_t nDdePos, sal_uInt8& rnMode ) const
+{
+ if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
+ {
+ rnMode = pDdeLink->GetMode();
+ return true;
+ }
+ return false;
+}
+
+const ScMatrix* ScDocument::GetDdeLinkResultMatrix( size_t nDdePos ) const
+{
+ const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos );
+ return pDdeLink ? pDdeLink->GetResult() : nullptr;
+}
+
+bool ScDocument::CreateDdeLink( const OUString& rAppl, const OUString& rTopic, const OUString& rItem, sal_uInt8 nMode, const ScMatrixRef& pResults )
+{
+ /* Create a DDE link without updating it (i.e. for Excel import), to prevent
+ unwanted connections. First try to find existing link. Set result array
+ on existing and new links. */
+ //TODO: store DDE links additionally at document (for efficiency)?
+ OSL_ENSURE( nMode != SC_DDE_IGNOREMODE, "ScDocument::CreateDdeLink - SC_DDE_IGNOREMODE not allowed here" );
+
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
+ if (!pMgr)
+ return false;
+
+ if (nMode != SC_DDE_IGNOREMODE)
+ {
+ ScDdeLink* pDdeLink = lclGetDdeLink(pMgr, rAppl, rTopic, rItem, nMode);
+ if( !pDdeLink )
+ {
+ // create a new DDE link, but without TryUpdate
+ pDdeLink = new ScDdeLink( *this, rAppl, rTopic, rItem, nMode );
+ pMgr->InsertDDELink(pDdeLink, rAppl, rTopic, rItem);
+ }
+
+ // insert link results
+ if( pResults )
+ pDdeLink->SetResult( pResults );
+
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::SetDdeLinkResultMatrix( size_t nDdePos, const ScMatrixRef& pResults )
+{
+ if( ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) )
+ {
+ pDdeLink->SetResult( pResults );
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::HasAreaLinks() const
+{
+ const sfx2::LinkManager* pMgr = GetDocLinkManager().getExistingLinkManager();
+ if (!pMgr)
+ return false;
+
+ const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ if (nullptr != dynamic_cast<const ScAreaLink* >(rLinks[i].get()))
+ return true;
+
+ return false;
+}
+
+void ScDocument::UpdateAreaLinks()
+{
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
+ if (!pMgr)
+ return;
+
+ const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ ::sfx2::SvBaseLink* pBase = rLink.get();
+ if (dynamic_cast<const ScAreaLink*>( pBase) != nullptr)
+ pBase->Update();
+ }
+}
+
+void ScDocument::DeleteAreaLinksOnTab( SCTAB nTab )
+{
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
+ if (!pMgr)
+ return;
+
+ const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ sfx2::SvBaseLinks::size_type nPos = 0;
+ while ( nPos < rLinks.size() )
+ {
+ const ::sfx2::SvBaseLink* pBase = rLinks[nPos].get();
+ const ScAreaLink* pLink = dynamic_cast<const ScAreaLink*>(pBase);
+ if (pLink && pLink->GetDestArea().aStart.Tab() == nTab)
+ pMgr->Remove(nPos);
+ else
+ ++nPos;
+ }
+}
+
+void ScDocument::UpdateRefAreaLinks( UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(false);
+ if (!pMgr)
+ return;
+
+ bool bAnyUpdate = false;
+
+ const ::sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase))
+ {
+ ScRange aOutRange = pLink->GetDestArea();
+
+ SCCOL nCol1 = aOutRange.aStart.Col();
+ SCROW nRow1 = aOutRange.aStart.Row();
+ SCTAB nTab1 = aOutRange.aStart.Tab();
+ SCCOL nCol2 = aOutRange.aEnd.Col();
+ SCROW nRow2 = aOutRange.aEnd.Row();
+ SCTAB nTab2 = aOutRange.aEnd.Tab();
+
+ ScRefUpdateRes eRes =
+ ScRefUpdate::Update( this, eUpdateRefMode,
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( eRes != UR_NOTHING )
+ {
+ pLink->SetDestArea( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
+ bAnyUpdate = true;
+ }
+ }
+ }
+
+ if ( !bAnyUpdate )
+ return;
+
+ // #i52120# Look for duplicates (after updating all positions).
+ // If several links start at the same cell, the one with the lower index is removed
+ // (file format specifies only one link definition for a cell).
+
+ sal_uInt16 nFirstIndex = 0;
+ while ( nFirstIndex < nCount )
+ {
+ bool bFound = false;
+ ::sfx2::SvBaseLink* pFirst = rLinks[nFirstIndex].get();
+ if (ScAreaLink* pFirstLink = dynamic_cast<ScAreaLink*>(pFirst))
+ {
+ ScAddress aFirstPos = pFirstLink->GetDestArea().aStart;
+ for ( sal_uInt16 nSecondIndex = nFirstIndex + 1; nSecondIndex < nCount && !bFound; ++nSecondIndex )
+ {
+ ::sfx2::SvBaseLink* pSecond = rLinks[nSecondIndex].get();
+ ScAreaLink* pSecondLink = dynamic_cast<ScAreaLink*>(pSecond);
+ if (pSecondLink && pSecondLink->GetDestArea().aStart == aFirstPos)
+ {
+ // remove the first link, exit the inner loop, don't increment nFirstIndex
+ pMgr->Remove(pFirst);
+ nCount = rLinks.size();
+ bFound = true;
+ }
+ }
+ }
+ if (!bFound)
+ ++nFirstIndex;
+ }
+}
+
+void ScDocument::CheckLinkFormulaNeedingCheck( const ScTokenArray& rCode )
+{
+ if (HasLinkFormulaNeedingCheck())
+ return;
+
+ // Prefer RPN over tokenized formula if available.
+ if (rCode.GetCodeLen())
+ {
+ if (rCode.HasOpCodeRPN(ocDde) || rCode.HasOpCodeRPN(ocWebservice))
+ SetLinkFormulaNeedingCheck(true);
+ }
+ else if (rCode.GetLen())
+ {
+ if (rCode.HasOpCode(ocDde) || rCode.HasOpCode(ocWebservice))
+ SetLinkFormulaNeedingCheck(true);
+ }
+ else
+ {
+ // Possible with named expression without expression like Excel
+ // internal print ranges, obscure user define names, ... formula error
+ // cells without formula ...
+ SAL_WARN("sc.core","ScDocument::CheckLinkFormulaNeedingCheck - called with empty ScTokenArray");
+ }
+}
+
+// TimerDelays etc.
+void ScDocument::KeyInput()
+{
+ if ( pChartListenerCollection->hasListeners() )
+ pChartListenerCollection->StartTimer();
+ if (apTemporaryChartLock)
+ apTemporaryChartLock->StartOrContinueLocking();
+}
+
+SfxBindings* ScDocument::GetViewBindings()
+{
+ // used to invalidate slots after changes to this document
+
+ if ( !mpShell )
+ return nullptr; // no ObjShell -> no view
+
+ // first check current view
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if ( pViewFrame && pViewFrame->GetObjectShell() != mpShell ) // wrong document?
+ pViewFrame = nullptr;
+
+ // otherwise use first view for this doc
+ if ( !pViewFrame )
+ pViewFrame = SfxViewFrame::GetFirst( mpShell );
+
+ if (pViewFrame)
+ return &pViewFrame->GetBindings();
+ else
+ return nullptr;
+}
+
+void ScDocument::TransliterateText( const ScMarkData& rMultiMark, TransliterationFlags nType )
+{
+ OSL_ENSURE( rMultiMark.IsMultiMarked(), "TransliterateText: no selection" );
+
+ utl::TransliterationWrapper aTransliterationWrapper( comphelper::getProcessComponentContext(), nType );
+ bool bConsiderLanguage = aTransliterationWrapper.needLanguageForTheMode();
+ LanguageType nLanguage = LANGUAGE_SYSTEM;
+
+ std::unique_ptr<ScEditEngineDefaulter> pEngine; // not using mpEditEngine member because of defaults
+
+ SCTAB nCount = GetTableCount();
+ for (const SCTAB& nTab : rMultiMark)
+ {
+ if (nTab >= nCount)
+ break;
+
+ if ( maTabs[nTab] )
+ {
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+
+ bool bFound = rMultiMark.IsCellMarked( nCol, nRow );
+ if (!bFound)
+ bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
+
+ while (bFound)
+ {
+ ScRefCellValue aCell(*this, ScAddress(nCol, nRow, nTab));
+
+ // fdo#32786 TITLE_CASE/SENTENCE_CASE need the extra handling in EditEngine (loop over words/sentences).
+ // Still use TransliterationWrapper directly for text cells with other transliteration types,
+ // for performance reasons.
+ if (aCell.getType() == CELLTYPE_EDIT ||
+ (aCell.getType() == CELLTYPE_STRING &&
+ ( nType == TransliterationFlags::SENTENCE_CASE || nType == TransliterationFlags::TITLE_CASE)))
+ {
+ if (!pEngine)
+ pEngine.reset(new ScFieldEditEngine(this, GetEnginePool(), GetEditPool()));
+
+ // defaults from cell attributes must be set so right language is used
+ const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab );
+ SfxItemSet aDefaults( pEngine->GetEmptyItemSet() );
+ if ( ScStyleSheet* pPreviewStyle = GetPreviewCellStyle( nCol, nRow, nTab ) )
+ {
+ ScPatternAttr aPreviewPattern( *pPattern );
+ aPreviewPattern.SetStyleSheet(pPreviewStyle);
+ aPreviewPattern.FillEditItemSet( &aDefaults );
+ }
+ else
+ {
+ SfxItemSet* pFontSet = GetPreviewFont( nCol, nRow, nTab );
+ pPattern->FillEditItemSet( &aDefaults, pFontSet );
+ }
+ pEngine->SetDefaults( std::move(aDefaults) );
+ if (aCell.getType() == CELLTYPE_STRING)
+ pEngine->SetTextCurrentDefaults(aCell.getSharedString()->getString());
+ else if (aCell.getEditText())
+ pEngine->SetTextCurrentDefaults(*aCell.getEditText());
+
+ pEngine->ClearModifyFlag();
+
+ sal_Int32 nLastPar = pEngine->GetParagraphCount();
+ if (nLastPar)
+ --nLastPar;
+ sal_Int32 nTxtLen = pEngine->GetTextLen(nLastPar);
+ ESelection aSelAll( 0, 0, nLastPar, nTxtLen );
+
+ pEngine->TransliterateText( aSelAll, nType );
+
+ if ( pEngine->IsModified() )
+ {
+ ScEditAttrTester aTester( pEngine.get() );
+ if ( aTester.NeedsObject() )
+ {
+ // remove defaults (paragraph attributes) before creating text object
+ pEngine->SetDefaults( std::make_unique<SfxItemSet>( pEngine->GetEmptyItemSet() ) );
+
+ // The cell will take ownership of the text object instance.
+ SetEditText(ScAddress(nCol,nRow,nTab), pEngine->CreateTextObject());
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ SetString(ScAddress(nCol,nRow,nTab), pEngine->GetText(), &aParam);
+ }
+ }
+ }
+
+ else if (aCell.getType() == CELLTYPE_STRING)
+ {
+ OUString aOldStr = aCell.getSharedString()->getString();
+ sal_Int32 nOldLen = aOldStr.getLength();
+
+ if ( bConsiderLanguage )
+ {
+ SvtScriptType nScript = GetStringScriptType( aOldStr ); //TODO: cell script type?
+ sal_uInt16 nWhich = ( nScript == SvtScriptType::ASIAN ) ? ATTR_CJK_FONT_LANGUAGE :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE :
+ ATTR_FONT_LANGUAGE );
+ nLanguage = static_cast<const SvxLanguageItem*>(GetAttr( nCol, nRow, nTab, nWhich ))->GetValue();
+ }
+
+ uno::Sequence<sal_Int32> aOffsets;
+ OUString aNewStr = aTransliterationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets );
+
+ if ( aNewStr != aOldStr )
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ SetString(ScAddress(nCol,nRow,nTab), aNewStr, &aParam);
+ }
+ }
+ bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark );
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documen9.cxx b/sc/source/core/data/documen9.cxx
new file mode 100644
index 0000000000..a9f04943c0
--- /dev/null
+++ b/sc/source/core/data/documen9.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 <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/autokernitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <svl/asiancfg.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/xtable.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/printer.hxx>
+
+#include <document.hxx>
+#include <docoptio.hxx>
+#include <docsh.hxx>
+#include <table.hxx>
+#include <drwlayer.hxx>
+#include <markdata.hxx>
+#include <patattr.hxx>
+#include <rechead.hxx>
+#include <poolhelp.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <editutil.hxx>
+#include <charthelper.hxx>
+#include <conditio.hxx>
+#include <documentlinkmgr.hxx>
+
+using namespace ::com::sun::star;
+
+SfxBroadcaster* ScDocument::GetDrawBroadcaster()
+{
+ return mpDrawLayer.get();
+}
+
+void ScDocument::BeginDrawUndo()
+{
+ if (mpDrawLayer)
+ mpDrawLayer->BeginCalcUndo(false);
+}
+
+void ScDocument::TransferDrawPage(const ScDocument& rSrcDoc, SCTAB nSrcPos, SCTAB nDestPos)
+{
+ if (mpDrawLayer && rSrcDoc.mpDrawLayer)
+ {
+ SdrPage* pOldPage = rSrcDoc.mpDrawLayer->GetPage(static_cast<sal_uInt16>(nSrcPos));
+ SdrPage* pNewPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nDestPos));
+
+ if (pOldPage && pNewPage)
+ {
+ SdrObjListIter aIter( pOldPage, SdrIterMode::Flat );
+ SdrObject* pOldObject = aIter.Next();
+ while (pOldObject)
+ {
+ // Copy style sheet
+ auto pStyleSheet = pOldObject->GetStyleSheet();
+ if (pStyleSheet)
+ GetStyleSheetPool()->CopyStyleFrom(rSrcDoc.GetStyleSheetPool(),
+ pStyleSheet->GetName(), pStyleSheet->GetFamily(), true);
+
+ // Clone to target SdrModel
+ rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*mpDrawLayer));
+ pNewObject->NbcMove(Size(0,0));
+ pNewPage->InsertObject( pNewObject.get() );
+
+ if (mpDrawLayer->IsRecording())
+ mpDrawLayer->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) );
+
+ pOldObject = aIter.Next();
+ }
+ }
+ }
+
+ // make sure the data references of charts are adapted
+ // (this must be after InsertObject!)
+ ScChartHelper::AdjustRangesOfChartsOnDestinationPage( rSrcDoc, *this, nSrcPos, nDestPos );
+ ScChartHelper::UpdateChartsOnDestinationPage(*this, nDestPos);
+}
+
+void ScDocument::InitDrawLayer( ScDocShell* pDocShell )
+{
+ if (pDocShell && !mpShell)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpShell = pDocShell;
+ }
+
+ if (mpDrawLayer)
+ return;
+
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ OUString aName;
+ if ( mpShell && !mpShell->IsLoading() ) // don't call GetTitle while loading
+ aName = mpShell->GetTitle();
+ mpDrawLayer.reset(new ScDrawLayer( this, aName ));
+
+ sfx2::LinkManager* pMgr = GetDocLinkManager().getLinkManager(bAutoCalc);
+ if (pMgr)
+ mpDrawLayer->SetLinkManager(pMgr);
+
+ // set DrawingLayer's SfxItemPool at Calc's SfxItemPool as
+ // secondary pool to support DrawingLayer FillStyle ranges (and similar)
+ // in SfxItemSets using the Calc SfxItemPool. This is e.g. needed when
+ // the PageStyle using SvxBrushItem is visualized and will be potentially
+ // used more intense in the future
+ if (mxPoolHelper.is() && !IsClipOrUndo()) //Using IsClipOrUndo as a proxy for SharePooledResources called
+ {
+ ScDocumentPool* pLocalPool = mxPoolHelper->GetDocPool();
+
+ if (pLocalPool)
+ {
+ OSL_ENSURE(!pLocalPool->GetSecondaryPool(), "OOps, already a secondary pool set where the DrawingLayer ItemPool is to be placed (!)");
+ pLocalPool->SetSecondaryPool(&mpDrawLayer->GetItemPool());
+ }
+ mpDrawLayer->CreateDefaultStyles();
+ }
+
+ // Drawing pages are accessed by table number, so they must also be present
+ // for preceding table numbers, even if the tables aren't allocated
+ // (important for clipboard documents).
+
+ SCTAB nDrawPages = 0;
+ SCTAB nTab;
+ for (nTab = 0; nTab < GetTableCount(); nTab++)
+ if (maTabs[nTab])
+ nDrawPages = nTab + 1; // needed number of pages
+
+ for (nTab = 0; nTab < nDrawPages && nTab < GetTableCount(); nTab++)
+ {
+ mpDrawLayer->ScAddPage( nTab ); // always add page, with or without the table
+ if (maTabs[nTab])
+ {
+ OUString aTabName = maTabs[nTab]->GetName();
+ mpDrawLayer->ScRenamePage( nTab, aTabName );
+
+ maTabs[nTab]->SetDrawPageSize(false,false); // set the right size immediately
+ }
+ }
+
+ mpDrawLayer->SetDefaultTabulator( GetDocOptions().GetTabDistance() );
+
+ UpdateDrawPrinter();
+
+ // set draw defaults directly
+ SfxItemPool& rDrawPool = mpDrawLayer->GetItemPool();
+ rDrawPool.SetPoolDefaultItem( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) );
+
+ UpdateDrawLanguages();
+ if (bImportingXML)
+ mpDrawLayer->EnableAdjust(false);
+
+ mpDrawLayer->SetForbiddenCharsTable( xForbiddenCharacters );
+ mpDrawLayer->SetCharCompressType( GetAsianCompression() );
+ mpDrawLayer->SetKernAsianPunctuation( GetAsianKerning() );
+}
+
+void ScDocument::UpdateDrawLanguages()
+{
+ if (mpDrawLayer)
+ {
+ SfxItemPool& rDrawPool = mpDrawLayer->GetItemPool();
+ rDrawPool.SetPoolDefaultItem( SvxLanguageItem( eLanguage, EE_CHAR_LANGUAGE ) );
+ rDrawPool.SetPoolDefaultItem( SvxLanguageItem( eCjkLanguage, EE_CHAR_LANGUAGE_CJK ) );
+ rDrawPool.SetPoolDefaultItem( SvxLanguageItem( eCtlLanguage, EE_CHAR_LANGUAGE_CTL ) );
+ }
+}
+
+void ScDocument::UpdateDrawPrinter()
+{
+ if (mpDrawLayer)
+ {
+ // use the printer even if IsValid is false
+ // Application::GetDefaultDevice causes trouble with changing MapModes
+ mpDrawLayer->SetRefDevice(GetRefDevice());
+ }
+}
+
+void ScDocument::SetDrawPageSize(SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetDrawPageSize();
+}
+
+bool ScDocument::IsChart( const SdrObject* pObject )
+{
+ // IsChart() implementation moved to svx drawinglayer
+ if(pObject && SdrObjKind::OLE2 == pObject->GetObjIdentifier())
+ {
+ return static_cast<const SdrOle2Obj*>(pObject)->IsChart();
+ }
+
+ return false;
+}
+
+IMPL_LINK( ScDocument, GetUserDefinedColor, sal_uInt16, nColorIndex, Color* )
+{
+ rtl::Reference<XColorList> xColorList;
+ if (mpDrawLayer)
+ xColorList = mpDrawLayer->GetColorList();
+ else
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ if (!pColorList.is())
+ pColorList = XColorList::CreateStdColorList();
+ xColorList = pColorList;
+ }
+ return const_cast<Color*>(&(xColorList->GetColor(nColorIndex)->GetColor()));
+}
+
+bool ScDocument::DrawGetPrintArea( ScRange& rRange, bool bSetHor, bool bSetVer ) const
+{
+ return mpDrawLayer->GetPrintArea( rRange, bSetHor, bSetVer );
+}
+
+void ScDocument::DeleteObjectsInArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark, bool bAnchored)
+{
+ if (!mpDrawLayer)
+ return;
+
+ SCTAB nTabCount = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+ if (maTabs[rTab])
+ mpDrawLayer->DeleteObjectsInArea( rTab, nCol1, nRow1, nCol2, nRow2, bAnchored);
+ }
+}
+
+void ScDocument::DeleteObjectsInSelection( const ScMarkData& rMark )
+{
+ if (!mpDrawLayer)
+ return;
+
+ mpDrawLayer->DeleteObjectsInSelection( rMark );
+}
+
+bool ScDocument::HasOLEObjectsInArea( const ScRange& rRange, const ScMarkData* pTabMark )
+{
+ // pTabMark is used only for selected tables. If pTabMark is 0, all tables of rRange are used.
+
+ if (!mpDrawLayer)
+ return false;
+
+ SCTAB nStartTab = 0;
+ SCTAB nEndTab = GetTableCount();
+ if ( !pTabMark )
+ {
+ nStartTab = rRange.aStart.Tab();
+ nEndTab = rRange.aEnd.Tab();
+ }
+
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab; nTab++)
+ {
+ if ( !pTabMark || pTabMark->GetTableSelect(nTab) )
+ {
+ tools::Rectangle aMMRect = GetMMRect( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
+
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ aMMRect.Contains( pObject->GetCurrentBoundRect() ) )
+ return true;
+
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ScDocument::StartAnimations( SCTAB nTab )
+{
+ if (!mpDrawLayer)
+ return;
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ return;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pObject))
+ {
+ if ( pGrafObj->IsAnimated() )
+ {
+ pGrafObj->StartAnimation();
+ }
+ }
+ pObject = aIter.Next();
+ }
+}
+
+bool ScDocument::HasBackgroundDraw( SCTAB nTab, const tools::Rectangle& rMMRect ) const
+{
+ // Are there objects in the background layer who are (partly) affected by rMMRect
+ // (for Drawing optimization, no deletion in front of the background
+ if (!mpDrawLayer)
+ return false;
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ return false;
+
+ bool bFound = false;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ if ( pObject->GetLayer() == SC_LAYER_BACK && pObject->GetCurrentBoundRect().Overlaps( rMMRect ) )
+ bFound = true;
+ pObject = aIter.Next();
+ }
+
+ return bFound;
+}
+
+bool ScDocument::HasAnyDraw( SCTAB nTab, const tools::Rectangle& rMMRect ) const
+{
+ // Are there any objects at all who are (partly) affected by rMMRect?
+ // (To detect blank pages when printing)
+ if (!mpDrawLayer)
+ return false;
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ return false;
+
+ bool bFound = false;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ if ( pObject->GetCurrentBoundRect().Overlaps( rMMRect ) )
+ bFound = true;
+ pObject = aIter.Next();
+ }
+
+ return bFound;
+}
+
+void ScDocument::EnsureGraphicNames()
+{
+ if (mpDrawLayer)
+ mpDrawLayer->EnsureGraphicNames();
+}
+
+SdrObject* ScDocument::GetObjectAtPoint( SCTAB nTab, const Point& rPos )
+{
+ // for Drag&Drop on draw object
+ SdrObject* pFound = nullptr;
+ if (mpDrawLayer && nTab < GetTableCount() && maTabs[nTab])
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetCurrentBoundRect().Contains(rPos) )
+ {
+ // Intern is of no interest
+ // Only object form background layer, when no object form another layer is found
+ SdrLayerID nLayer = pObject->GetLayer();
+ if ( (nLayer != SC_LAYER_INTERN) && (nLayer != SC_LAYER_HIDDEN) )
+ {
+ if ( nLayer != SC_LAYER_BACK ||
+ !pFound || pFound->GetLayer() == SC_LAYER_BACK )
+ {
+ pFound = pObject;
+ }
+ }
+ }
+ // Continue search -> take last (on top) found object
+ pObject = aIter.Next();
+ }
+ }
+ }
+ return pFound;
+}
+
+bool ScDocument::IsPrintEmpty( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ SCTAB nTab, bool bLeftIsEmpty,
+ ScRange* pLastRange, tools::Rectangle* pLastMM ) const
+{
+ if (!IsBlockEmpty( nStartCol, nStartRow, nEndCol, nEndRow, nTab ))
+ return false;
+
+ if (HasAttrib(ScRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab), HasAttrFlags::Lines))
+ // We want to print sheets with borders even if there is no cell content.
+ return false;
+
+ tools::Rectangle aMMRect;
+ if ( pLastRange && pLastMM && nTab == pLastRange->aStart.Tab() &&
+ nStartRow == pLastRange->aStart.Row() && nEndRow == pLastRange->aEnd.Row() )
+ {
+ // keep vertical part of aMMRect, only update horizontal position
+ aMMRect = *pLastMM;
+
+ tools::Long nLeft = 0;
+ SCCOL i;
+ for (i=0; i<nStartCol; i++)
+ nLeft += GetColWidth(i,nTab);
+ tools::Long nRight = nLeft;
+ for (i=nStartCol; i<=nEndCol; i++)
+ nRight += GetColWidth(i,nTab);
+
+ aMMRect.SetLeft(o3tl::convert(nLeft, o3tl::Length::twip, o3tl::Length::mm100));
+ aMMRect.SetRight(o3tl::convert(nRight, o3tl::Length::twip, o3tl::Length::mm100));
+ }
+ else
+ aMMRect = GetMMRect( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
+
+ if ( pLastRange && pLastMM )
+ {
+ *pLastRange = ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
+ *pLastMM = aMMRect;
+ }
+
+ if ( HasAnyDraw( nTab, aMMRect ))
+ return false;
+
+ if ( nStartCol > 0 && !bLeftIsEmpty )
+ {
+ // similar to in ScPrintFunc::AdjustPrintArea
+ // ExtendPrintArea starting only from the start column of the print area
+
+ SCCOL nExtendCol = nStartCol - 1;
+ SCROW nTmpRow = nEndRow;
+
+ // ExtendMerge() is non-const, but called without refresh. GetPrinter()
+ // might create and assign a printer.
+ ScDocument* pThis = const_cast<ScDocument*>(this);
+
+ pThis->ExtendMerge( 0,nStartRow, nExtendCol,nTmpRow, nTab ); // no Refresh, incl. Attrs
+
+ OutputDevice* pDev = pThis->GetPrinter();
+ pDev->SetMapMode(MapMode(MapUnit::MapPixel)); // Important for GetNeededSize
+ ExtendPrintArea( pDev, nTab, 0, nStartRow, nExtendCol, nEndRow );
+ if ( nExtendCol >= nStartCol )
+ return false;
+ }
+
+ return true;
+}
+
+void ScDocument::Clear( bool bFromDestructor )
+{
+ for (auto& rxTab : maTabs)
+ if (rxTab)
+ rxTab->GetCondFormList()->clear();
+
+ maTabs.clear();
+ pSelectionAttr.reset();
+
+ if (mpDrawLayer)
+ {
+ mpDrawLayer->ClearModel( bFromDestructor );
+ }
+}
+
+bool ScDocument::HasDetectiveObjects(SCTAB nTab) const
+{
+ // looks for detective objects, annotations don't count
+ // (used to adjust scale so detective objects hit their cells better)
+
+ bool bFound = false;
+
+ if (mpDrawLayer)
+ {
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ // anything on the internal layer except captions (annotations)
+ if ( (pObject->GetLayer() == SC_LAYER_INTERN) && !ScDrawLayer::IsNoteCaption( pObject ) )
+ bFound = true;
+
+ pObject = aIter.Next();
+ }
+ }
+ }
+
+ return bFound;
+}
+
+void ScDocument::UpdateFontCharSet()
+{
+ // In old versions (until 4.0 without SP), when switching between systems,
+ // the Font attribute was not adjusted.
+ // This has to be redone for Documents until SP2:
+ // Everything that is not SYMBOL is set to system CharSet.
+ // CharSet should be correct for new documents (version SC_FONTCHARSET)
+
+ bool bUpdateOld = ( nSrcVer < SC_FONTCHARSET );
+
+ rtl_TextEncoding eSysSet = osl_getThreadTextEncoding();
+ if ( !(eSrcSet != eSysSet || bUpdateOld) )
+ return;
+
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_FONT))
+ {
+ auto pFontItem = const_cast<SvxFontItem*>(dynamic_cast<const SvxFontItem*>(pItem));
+ if ( pFontItem && ( pFontItem->GetCharSet() == eSrcSet ||
+ ( bUpdateOld && pFontItem->GetCharSet() != RTL_TEXTENCODING_SYMBOL ) ) )
+ pFontItem->SetCharSet(eSysSet);
+ }
+
+ if ( mpDrawLayer )
+ {
+ SfxItemPool& rDrawPool = mpDrawLayer->GetItemPool();
+ for (const SfxPoolItem* pItem : rDrawPool.GetItemSurrogates(EE_CHAR_FONTINFO))
+ {
+ SvxFontItem* pFontItem = const_cast<SvxFontItem*>(dynamic_cast<const SvxFontItem*>(pItem));
+ if ( pFontItem && ( pFontItem->GetCharSet() == eSrcSet ||
+ ( bUpdateOld && pFontItem->GetCharSet() != RTL_TEXTENCODING_SYMBOL ) ) )
+ pFontItem->SetCharSet( eSysSet );
+ }
+ }
+}
+
+void ScDocument::SetLoadingMedium( bool bVal )
+{
+ bLoadingMedium = bVal;
+ for (auto& rxTab : maTabs)
+ {
+ if (!rxTab)
+ return;
+
+ rxTab->SetLoadingMedium(bVal);
+ }
+}
+
+void ScDocument::SetImportingXML( bool bVal )
+{
+ bImportingXML = bVal;
+ if (mpDrawLayer)
+ mpDrawLayer->EnableAdjust(!bImportingXML);
+
+ if ( !bVal )
+ {
+ // #i57869# after loading, do the real RTL mirroring for the sheets that have the LoadingRTL flag set
+
+ for (SCTAB nTab = 0; nTab < GetTableCount() && maTabs[nTab]; nTab++)
+ if ( maTabs[nTab]->IsLoadingRTL() )
+ {
+ // SetLayoutRTL => SetDrawPageSize => ScDrawLayer::SetPageSize, includes RTL-mirroring;
+ // bImportingXML must be cleared first
+ maTabs[nTab]->SetLoadingRTL( false );
+ SetLayoutRTL( nTab, true, ScObjectHandling::MoveRTLMode );
+ }
+ }
+
+ SetLoadingMedium(bVal);
+}
+
+const std::shared_ptr<SvxForbiddenCharactersTable>& ScDocument::GetForbiddenCharacters() const
+{
+ return xForbiddenCharacters;
+}
+
+void ScDocument::SetForbiddenCharacters(const std::shared_ptr<SvxForbiddenCharactersTable>& rNew)
+{
+ xForbiddenCharacters = rNew;
+ if ( mpEditEngine )
+ EditEngine::SetForbiddenCharsTable( xForbiddenCharacters );
+ if ( mpDrawLayer )
+ mpDrawLayer->SetForbiddenCharsTable( xForbiddenCharacters );
+}
+
+bool ScDocument::IsValidAsianCompression() const
+{
+ return nAsianCompression != CharCompressType::Invalid;
+}
+
+CharCompressType ScDocument::GetAsianCompression() const
+{
+ if ( nAsianCompression == CharCompressType::Invalid )
+ return CharCompressType::NONE;
+ else
+ return nAsianCompression;
+}
+
+void ScDocument::SetAsianCompression(CharCompressType nNew)
+{
+ nAsianCompression = nNew;
+ if ( mpEditEngine )
+ mpEditEngine->SetAsianCompressionMode( nAsianCompression );
+ if ( mpDrawLayer )
+ mpDrawLayer->SetCharCompressType( nAsianCompression );
+}
+
+bool ScDocument::IsValidAsianKerning() const
+{
+ return ( nAsianKerning != SC_ASIANKERNING_INVALID );
+}
+
+bool ScDocument::GetAsianKerning() const
+{
+ if ( nAsianKerning == SC_ASIANKERNING_INVALID )
+ return false;
+ else
+ return static_cast<bool>(nAsianKerning);
+}
+
+void ScDocument::SetAsianKerning(bool bNew)
+{
+ nAsianKerning = static_cast<sal_uInt8>(bNew);
+ if ( mpEditEngine )
+ mpEditEngine->SetKernAsianPunctuation( static_cast<bool>( nAsianKerning ) );
+ if ( mpDrawLayer )
+ mpDrawLayer->SetKernAsianPunctuation( static_cast<bool>( nAsianKerning ) );
+}
+
+void ScDocument::ApplyAsianEditSettings( ScEditEngineDefaulter& rEngine )
+{
+ EditEngine::SetForbiddenCharsTable( xForbiddenCharacters );
+ rEngine.SetAsianCompressionMode( GetAsianCompression() );
+ rEngine.SetKernAsianPunctuation( GetAsianKerning() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
new file mode 100644
index 0000000000..ff6d77b432
--- /dev/null
+++ b/sc/source/core/data/document.cxx
@@ -0,0 +1,6967 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <editeng/boxitem.hxx>
+#include <editeng/editobj.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <svx/svditer.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <svl/numformat.hxx>
+#include <poolcach.hxx>
+#include <svl/zforlist.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/sheet/TablePageBreakData.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <docuno.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <attrib.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <rangenam.hxx>
+#include <poolhelp.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <dbdata.hxx>
+#include <chartlis.hxx>
+#include <rangelst.hxx>
+#include <markdata.hxx>
+#include <drwlayer.hxx>
+#include <validat.hxx>
+#include <prnsave.hxx>
+#include <chgtrack.hxx>
+#include <hints.hxx>
+#include <detdata.hxx>
+#include <dpobject.hxx>
+#include <scmod.hxx>
+#include <dociter.hxx>
+#include <progress.hxx>
+#include <autonamecache.hxx>
+#include <bcaslot.hxx>
+#include <postit.hxx>
+#include <clipparam.hxx>
+#include <defaultsoptions.hxx>
+#include <editutil.hxx>
+#include <stringutil.hxx>
+#include <formulaiter.hxx>
+#include <formulacell.hxx>
+#include <clipcontext.hxx>
+#include <listenercontext.hxx>
+#include <scopetools.hxx>
+#include <refupdatecontext.hxx>
+#include <formulagroup.hxx>
+#include <tokenstringcontext.hxx>
+#include <compressedarray.hxx>
+#include <recursionhelper.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+#include <undomanager.hxx>
+
+#include <formula/vectortoken.hxx>
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <mtvelements.hxx>
+#include <sfx2/lokhelper.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::sheet::TablePageBreakData;
+using ::std::set;
+
+namespace {
+
+std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark)
+{
+ SCTAB nTabStart = MAXTAB;
+ SCTAB nTabEnd = 0;
+ SCTAB nMax = static_cast<SCTAB>(rTables.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (!rTables[rTab])
+ continue;
+
+ if (rTab < nTabStart)
+ nTabStart = rTab;
+ nTabEnd = rTab;
+ }
+
+ return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd);
+}
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+struct ScDefaultAttr
+{
+ const ScPatternAttr* pAttr;
+ SCROW nFirst;
+ SCSIZE nCount;
+ explicit ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {}
+};
+
+struct ScLessDefaultAttr
+{
+ bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const
+ {
+ return rValue1.pAttr < rValue2.pAttr;
+ }
+};
+
+}
+
+typedef std::set<ScDefaultAttr, ScLessDefaultAttr> ScDefaultAttrSet;
+
+void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck )
+{
+ if (!ValidTab(nTab) || HasTable(nTab))
+ return;
+
+ // Get Custom prefix
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ OUString aString = rOpt.GetInitTabPrefix() + OUString::number(nTab+1);
+ if ( _bNeedsNameCheck )
+ CreateValidTabName( aString ); // no doubles
+ if (nTab < GetTableCount())
+ {
+ maTabs[nTab].reset( new ScTable(*this, nTab, aString) );
+ }
+ else
+ {
+ while (nTab > GetTableCount())
+ maTabs.push_back(nullptr);
+ maTabs.emplace_back( new ScTable(*this, nTab, aString) );
+ }
+ maTabs[nTab]->SetLoadingMedium(bLoadingMedium);
+}
+
+bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ rHashCode = pTable->GetHashCode();
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ rName = pTable->GetName();
+ return true;
+ }
+ rName.clear();
+ return false;
+}
+
+OUString ScDocument::GetCopyTabName( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size()))
+ return maTabNames[nTab];
+ return OUString();
+}
+
+bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ pTable->SetCodeName(rName);
+ return true;
+ }
+ SAL_WARN("sc", "can't set code name " << rName );
+ return false;
+}
+
+bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ rName = pTable->GetCodeName();
+ return true;
+ }
+ rName.clear();
+ return false;
+}
+
+bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const
+{
+ static OUString aCacheName, aCacheUpperName;
+
+ assert(!IsThreadedGroupCalcInProgress());
+ if (aCacheName != rName)
+ {
+ aCacheName = rName;
+ // surprisingly slow ...
+ aCacheUpperName = ScGlobal::getCharClass().uppercase(rName);
+ }
+ const OUString aUpperName = aCacheUpperName;
+
+ for (SCTAB i = 0; i < GetTableCount(); i++)
+ if (maTabs[i])
+ {
+ if (aUpperName == maTabs[i]->GetUpperName())
+ {
+ rTab = i;
+ return true;
+ }
+ }
+ rTab = 0;
+ return false;
+}
+
+std::vector<OUString> ScDocument::GetAllTableNames() const
+{
+ std::vector<OUString> aNames;
+ aNames.reserve(maTabs.size());
+ for (const auto& a : maTabs)
+ {
+ // Positions need to be preserved for ScCompiler and address convention
+ // context, so still push an empty string for NULL tabs.
+ OUString aName;
+ if (a)
+ {
+ const ScTable& rTab = *a;
+ aName = rTab.GetName();
+ }
+ aNames.push_back(aName);
+ }
+
+ return aNames;
+}
+
+ScDBData* ScDocument::GetAnonymousDBData(SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetAnonymousDBData();
+ return nullptr;
+}
+
+SCTAB ScDocument::GetTableCount() const
+{
+ return static_cast<SCTAB>(maTabs.size());
+}
+
+void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetAnonymousDBData(std::move(pDBData));
+}
+
+void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData )
+{
+ mpAnonymousDBData = std::move(pDBData);
+}
+
+ScDBData* ScDocument::GetAnonymousDBData()
+{
+ return mpAnonymousDBData.get();
+}
+
+bool ScDocument::ValidTabName( const OUString& rName )
+{
+ if (rName.isEmpty())
+ return false;
+ sal_Int32 nLen = rName.getLength();
+
+#if 1
+ // Restrict sheet names to what Excel accepts.
+ /* TODO: We may want to remove this restriction for full ODFF compliance.
+ * Merely loading and calculating ODF documents using these characters in
+ * sheet names is not affected by this, but all sheet name editing and
+ * copying functionality is, maybe falling back to "Sheet4" or similar. */
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ const sal_Unicode c = rName[i];
+ switch (c)
+ {
+ case ':':
+ case '\\':
+ case '/':
+ case '?':
+ case '*':
+ case '[':
+ case ']':
+ // these characters are not allowed to match XL's convention.
+ return false;
+ case '\'':
+ if (i == 0 || i == nLen - 1)
+ // single quote is not allowed at the first or last
+ // character position.
+ return false;
+ break;
+ }
+ }
+#endif
+
+ return true;
+}
+
+bool ScDocument::ValidNewTabName( const OUString& rName ) const
+{
+ bool bValid = ValidTabName(rName);
+ if (!bValid)
+ return false;
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ for (const auto& a : maTabs)
+ {
+ if (!a)
+ continue;
+ const OUString& rOldName = a->GetUpperName();
+ bValid = rOldName != aUpperName;
+ if (!bValid)
+ break;
+ }
+ return bValid;
+}
+
+void ScDocument::CreateValidTabName(OUString& rName) const
+{
+ if ( !ValidTabName(rName) )
+ {
+ // Find new one
+
+ // Get Custom prefix
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ const OUString& aStrTable = rOpt.GetInitTabPrefix();
+
+ bool bOk = false;
+
+ // First test if the prefix is valid, if so only avoid doubles
+ bool bPrefix = ValidTabName( aStrTable );
+ OSL_ENSURE(bPrefix, "Invalid Table Name");
+ SCTAB nDummy;
+
+ for (SCTAB i = GetTableCount() + 1; !bOk ; i++)
+ {
+ rName = aStrTable + OUString::number(static_cast<sal_Int32>(i));
+ if (bPrefix)
+ bOk = ValidNewTabName( rName );
+ else
+ bOk = !GetTable( rName, nDummy );
+ }
+ }
+ else
+ {
+ // testing the supplied Name
+
+ if ( !ValidNewTabName(rName) )
+ {
+ SCTAB i = 1;
+ OUString aName;
+ do
+ {
+ i++;
+ aName = rName + "_" + OUString::number(static_cast<sal_Int32>(i));
+ }
+ while (!ValidNewTabName(aName) && (i < MAXTAB+1));
+ rName = aName;
+ }
+ }
+}
+
+void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const
+{
+ aNames.clear();//ensure that the vector is empty
+
+ // Get Custom prefix
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ const OUString& aStrTable = rOpt.GetInitTabPrefix();
+
+ OUStringBuffer rName;
+
+ // First test if the prefix is valid, if so only avoid doubles
+ bool bPrefix = ValidTabName( aStrTable );
+ OSL_ENSURE(bPrefix, "Invalid Table Name");
+ SCTAB nDummy;
+ SCTAB i = GetTableCount() + 1;
+
+ for (SCTAB j = 0; j < nCount; ++j)
+ {
+ bool bOk = false;
+ while(!bOk)
+ {
+ rName = aStrTable;
+ rName.append(static_cast<sal_Int32>(i));
+ if (bPrefix)
+ bOk = ValidNewTabName( rName.toString() );
+ else
+ bOk = !GetTable( rName.toString(), nDummy );
+ i++;
+ }
+ aNames.push_back(rName.makeStringAndClear());
+ }
+}
+
+void ScDocument::AppendTabOnLoad(const OUString& rName)
+{
+ SCTAB nTabCount = GetTableCount();
+ if (!ValidTab(nTabCount))
+ // max table count reached. No more tables.
+ return;
+
+ OUString aName = rName;
+ CreateValidTabName(aName);
+ maTabs.emplace_back( new ScTable(*this, nTabCount, aName) );
+}
+
+void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName)
+{
+ if (!ValidTab(nTab) || GetTableCount() <= nTab)
+ return;
+
+ if (!ValidTabName(rName))
+ return;
+
+ maTabs[nTab]->SetName(rName);
+}
+
+void ScDocument::InvalidateStreamOnSave()
+{
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetStreamValid(false);
+ }
+}
+
+bool ScDocument::InsertTab(
+ SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab )
+{
+ SCTAB nTabCount = GetTableCount();
+ bool bValid = ValidTab(nTabCount);
+ if ( !bExternalDocument ) // else test rName == "'Doc'!Tab" first
+ bValid = (bValid && ValidNewTabName(rName));
+ if (bValid)
+ {
+ if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
+ {
+ nPos = maTabs.size();
+ maTabs.emplace_back( new ScTable(*this, nTabCount, rName) );
+ if ( bExternalDocument )
+ maTabs[nTabCount]->SetVisible( false );
+ }
+ else
+ {
+ if (ValidTab(nPos) && (nPos < nTabCount))
+ {
+ sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1);
+
+ ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
+ if (pRangeName)
+ pRangeName->UpdateInsertTab(aCxt);
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 );
+ UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateInsertTab(aCxt);
+ }
+ maTabs.emplace(maTabs.begin() + nPos, new ScTable(*this, nPos, rName));
+
+ // UpdateBroadcastAreas must be called between UpdateInsertTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+
+ StartAllListeners();
+
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateInsertTab(aCxt);
+ }
+
+ bValid = true;
+ }
+ else
+ bValid = false;
+ }
+ }
+
+ if (bValid)
+ {
+ sc::SetFormulaDirtyContext aCxt;
+ aCxt.mbClearTabDeletedFlag = bUndoDeleteTab;
+ aCxt.mnTabDeletedStart = nPos;
+ aCxt.mnTabDeletedEnd = nPos;
+ SetAllFormulasDirty(aCxt);
+
+ if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
+ {
+ ScModelObj* pModel = GetDocumentShell()->GetModel();
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+ }
+
+ return bValid;
+}
+
+bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames,
+ bool bNamesValid )
+{
+ SCTAB nNewSheets = static_cast<SCTAB>(rNames.size());
+ SCTAB nTabCount = GetTableCount();
+ bool bValid = bNamesValid || ValidTab(nTabCount+nNewSheets);
+
+ if (bValid)
+ {
+ if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
+ {
+ for ( SCTAB i = 0; i < nNewSheets; ++i )
+ {
+ maTabs.emplace_back( new ScTable(*this, nTabCount + i, rNames.at(i)) );
+ }
+ }
+ else
+ {
+ if (ValidTab(nPos) && (nPos < nTabCount))
+ {
+ sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets);
+ ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
+ if (pRangeName)
+ pRangeName->UpdateInsertTab(aCxt);
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,nNewSheets );
+ UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) );
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateInsertTab(aCxt);
+ }
+ for (SCTAB i = 0; i < nNewSheets; ++i)
+ {
+ maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(*this, nPos + i, rNames.at(i)) );
+ }
+
+ // UpdateBroadcastAreas must be called between UpdateInsertTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+
+ StartAllListeners();
+
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateInsertTab(aCxt);
+ }
+
+ bValid = true;
+ }
+ else
+ bValid = false;
+ }
+ }
+
+ if (bValid)
+ {
+ sc::SetFormulaDirtyContext aCxt;
+ SetAllFormulasDirty(aCxt);
+ }
+
+ return bValid;
+}
+
+bool ScDocument::DeleteTab( SCTAB nTab )
+{
+ bool bValid = false;
+ if (HasTable(nTab))
+ {
+ SCTAB nTabCount = GetTableCount();
+ if (nTabCount > 1)
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab );
+ DelBroadcastAreasInRange( aRange );
+
+ // #i8180# remove database ranges etc. that are on the deleted tab
+ // (restored in undo with ScRefUndoData)
+
+ xColNameRanges->DeleteOnTab( nTab );
+ xRowNameRanges->DeleteOnTab( nTab );
+ pDBCollection->DeleteOnTab( nTab );
+ if (pDPCollection)
+ pDPCollection->DeleteOnTab( nTab );
+ if (pDetOpList)
+ pDetOpList->DeleteOnTab( nTab );
+ DeleteAreaLinksOnTab( nTab );
+
+ // normal reference update
+
+ aRange.aEnd.SetTab(GetTableCount() - 1);
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
+ if (pRangeName)
+ pRangeName->UpdateDeleteTab(aCxt);
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 );
+ UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 );
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateDeleteTab(aCxt);
+ }
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) );
+
+ for (auto & pTab : maTabs)
+ if (pTab)
+ pTab->UpdateDeleteTab(aCxt);
+
+ // tdf#149502 make sure ScTable destructor called after the erase is finished, when
+ // maTabs[x].nTab==x is true again, as it should be always true.
+ // In the end of maTabs.erase, maTabs indexes change, but nTab updated before erase.
+ // ~ScTable expect that maTabs[x].nTab==x so it shouldn't be called during erase.
+ ScTableUniquePtr pErasedTab = std::move(maTabs[nTab]);
+ maTabs.erase(maTabs.begin() + nTab);
+ delete pErasedTab.release();
+
+ // UpdateBroadcastAreas must be called between UpdateDeleteTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+ // Excel-Filter deletes some Tables while loading, Listeners will
+ // only be triggered after the loading is done.
+ if ( !bInsertingFromOtherDoc )
+ {
+ StartAllListeners();
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = GetDocumentShell()->GetModel();
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+
+ bValid = true;
+ }
+ }
+ return bValid;
+}
+
+bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets )
+{
+ bool bValid = false;
+ if (HasTable(nTab) && (nTab + nSheets) <= GetTableCount())
+ {
+ SCTAB nTabCount = GetTableCount();
+ if (nTabCount > nSheets)
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
+ {
+ ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab + aTab );
+ DelBroadcastAreasInRange( aRange );
+
+ // #i8180# remove database ranges etc. that are on the deleted tab
+ // (restored in undo with ScRefUndoData)
+
+ xColNameRanges->DeleteOnTab( nTab + aTab );
+ xRowNameRanges->DeleteOnTab( nTab + aTab );
+ pDBCollection->DeleteOnTab( nTab + aTab );
+ if (pDPCollection)
+ pDPCollection->DeleteOnTab( nTab + aTab );
+ if (pDetOpList)
+ pDetOpList->DeleteOnTab( nTab + aTab );
+ DeleteAreaLinksOnTab( nTab + aTab );
+ }
+
+ if (pRangeName)
+ pRangeName->UpdateDeleteTab(aCxt);
+
+ // normal reference update
+
+ ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTabCount - 1 );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1*nSheets );
+ UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets );
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateDeleteTab(aCxt);
+ }
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) );
+
+ for (auto & pTab : maTabs)
+ if (pTab)
+ pTab->UpdateDeleteTab(aCxt);
+
+ maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets);
+ // UpdateBroadcastAreas must be called between UpdateDeleteTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+ // Excel-Filter deletes some Tables while loading, Listeners will
+ // only be triggered after the loading is done.
+ if ( !bInsertingFromOtherDoc )
+ {
+ StartAllListeners();
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = GetDocumentShell()->GetModel();
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+
+ bValid = true;
+ }
+ }
+ return bValid;
+}
+
+bool ScDocument::RenameTab( SCTAB nTab, const OUString& rName, bool bExternalDocument )
+{
+ bool bValid = false;
+ SCTAB i;
+ if (HasTable(nTab))
+ {
+ if ( bExternalDocument )
+ bValid = true; // composed name
+ else
+ bValid = ValidTabName(rName);
+ for (i = 0; i < GetTableCount() && bValid; i++)
+ {
+ if (maTabs[i] && (i != nTab))
+ {
+ OUString aOldName = maTabs[i]->GetName();
+ bValid = !ScGlobal::GetTransliteration().isEqual( rName, aOldName );
+ }
+ }
+ if (bValid)
+ {
+ // #i75258# update charts before renaming, so they can get their live data objects.
+ // Once the charts are live, the sheet can be renamed without problems.
+ if ( pChartListenerCollection )
+ pChartListenerCollection->UpdateChartsContainingTab( nTab );
+ maTabs[nTab]->SetName(rName);
+
+ // If formulas refer to the renamed sheet, the TokenArray remains valid,
+ // but the XML stream must be re-generated.
+ for (const auto& pTable : maTabs)
+ {
+ if (pTable)
+ {
+ pTable->SetStreamValid( false );
+ // tdf#156815 Reset solver settings so next time they're loaded they come with
+ // the updated sheet name
+ pTable->ResetSolverSettings();
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
+ {
+ ScModelObj* pModel = GetDocumentShell()->GetModel();
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+ }
+ }
+
+ collectUIInformation({{"NewName", rName}}, "Rename_Sheet");
+
+ return bValid;
+}
+
+void ScDocument::SetVisible( SCTAB nTab, bool bVisible )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetVisible(bVisible);
+}
+
+bool ScDocument::IsVisible( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsVisible();
+ return false;
+}
+
+bool ScDocument::IsStreamValid( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsStreamValid();
+ return false;
+}
+
+void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetStreamValid( bSet, bIgnoreLock );
+}
+
+void ScDocument::LockStreamValid( bool bLock )
+{
+ mbStreamValidLocked = bLock;
+}
+
+bool ScDocument::IsPendingRowHeights( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsPendingRowHeights();
+ return false;
+}
+
+void ScDocument::SetPendingRowHeights( SCTAB nTab, bool bSet )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetPendingRowHeights(bSet);
+}
+
+sal_uInt16 ScDocument::GetSheetOptimalMinRowHeight(SCTAB nTab) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return ScGlobal::nStdRowHeight;
+
+ return pTab->GetOptimalMinRowHeight();
+}
+
+void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling)
+{
+ ScTable* pTable = FetchTable(nTab);
+ if (!pTable)
+ return;
+
+ if ( bImportingXML )
+ {
+ // #i57869# only set the LoadingRTL flag, the real setting (including mirroring)
+ // is applied in SetImportingXML(false). This is so the shapes can be loaded in
+ // normal LTR mode.
+
+ pTable->SetLoadingRTL( bRTL );
+ return;
+ }
+
+ pTable->SetLayoutRTL( bRTL ); // only sets the flag
+ pTable->SetDrawPageSize(true, true, eObjectHandling);
+
+ // objects are already repositioned via SetDrawPageSize, only writing mode is missing
+ if (!mpDrawLayer)
+ return;
+
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ return;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB );
+ pObject = aIter.Next();
+ }
+}
+
+bool ScDocument::IsLayoutRTL( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsLayoutRTL();
+
+ return false;
+}
+
+bool ScDocument::IsNegativePage( SCTAB nTab ) const
+{
+ // Negative page area is always used for RTL layout.
+ // The separate method is used to find all RTL handling of drawing objects.
+ return IsLayoutRTL( nTab );
+}
+
+/* ----------------------------------------------------------------------------
+ used search area:
+
+ GetCellArea - Only Data
+ GetTableArea - Data / Attributes
+ GetPrintArea - intended for character objects,
+ sweeps attributes all the way to bottom / right
+---------------------------------------------------------------------------- */
+
+bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
+{
+ if (HasTable(nTab))
+ return maTabs[nTab]->GetCellArea(rEndCol, rEndRow);
+
+ rEndCol = 0;
+ rEndRow = 0;
+ return false;
+}
+
+bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow, bool bCalcHiddens) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetTableArea(rEndCol, rEndRow, bCalcHiddens);
+
+ rEndCol = 0;
+ rEndRow = 0;
+ return false;
+}
+
+bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const
+{
+ if (!HasTable(nTab))
+ return false;
+
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ maTabs[nTab]->GetFirstDataPos(nCol1, nRow1);
+ maTabs[nTab]->GetLastDataPos(nCol2, nRow2);
+
+ if (nCol1 > nCol2 || nRow1 > nRow2)
+ // invalid range.
+ return false;
+
+ // Make sure the area only shrinks, and doesn't grow.
+ if (rStartCol < nCol1)
+ rStartCol = nCol1;
+ if (nCol2 < rEndCol)
+ rEndCol = nCol2;
+ if (rStartRow < nRow1)
+ rStartRow = nRow1;
+ if (nRow2 < rEndRow)
+ rEndRow = nRow2;
+
+ if (rStartCol > rEndCol || rStartRow > rEndRow)
+ // invalid range.
+ return false;
+
+ return true; // success!
+}
+
+bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol,
+ SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
+ bool bStickyTopRow, bool bStickyLeftCol, ScDataAreaExtras* pDataAreaExtras ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ return pTable->ShrinkToUsedDataArea(
+ o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow, bColumnsOnly, bStickyTopRow,
+ bStickyLeftCol, pDataAreaExtras);
+ }
+ o_bShrunk = false;
+ return false;
+}
+
+SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLastDataRow(nCol1, nCol2, nLastRow);
+ return -1;
+}
+
+// connected area
+
+void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
+}
+
+void ScDocument::GetBackColorArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->GetBackColorArea( rStartCol, rStartRow, rEndCol, rEndRow );
+}
+
+bool ScDocument::GetDataAreaSubrange(ScRange& rRange) const
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ if (nTab != rRange.aEnd.Tab())
+ return true;
+
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetDataAreaSubrange(rRange);
+
+ return true;
+}
+
+void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow);
+}
+
+void ScDocument::LimitChartIfAll( ScRangeListRef& rRangeList )
+{
+ ScRangeListRef aNew = new ScRangeList;
+ if (rRangeList.is())
+ {
+ for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ )
+ {
+ ScRange aRange( (*rRangeList)[i] );
+ if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MaxCol() ) ||
+ ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MaxRow() ) )
+ {
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ SCTAB nTab = aRange.aStart.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow);
+ aRange.aStart.SetCol( nStartCol );
+ aRange.aStart.SetRow( nStartRow );
+ aRange.aEnd.SetCol( nEndCol );
+ aRange.aEnd.SetRow( nEndRow );
+ }
+ aNew->push_back(aRange);
+ }
+ }
+ else
+ {
+ OSL_FAIL("LimitChartIfAll: Ref==0");
+ }
+ rRangeList = aNew;
+}
+
+static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
+{
+ // without ScMarkData, leave start/end unchanged
+ if ( !pTabMark )
+ return;
+
+ for (SCTAB nTab=0; nTab< aMaxTab; ++nTab)
+ if (pTabMark->GetTableSelect(nTab))
+ {
+ // find first range of consecutive selected sheets
+ rTabRangeStart = pTabMark->GetFirstSelected();
+ while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
+ ++nTab;
+ rTabRangeEnd = nTab;
+ return;
+ }
+}
+
+static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
+{
+ if ( pTabMark )
+ {
+ // find next range of consecutive selected sheets after rTabRangeEnd
+ for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab)
+ if (pTabMark->GetTableSelect(nTab))
+ {
+ rTabRangeStart = nTab;
+ while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
+ ++nTab;
+ rTabRangeEnd = nTab;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScDocument::CanInsertRow( const ScRange& rRange ) const
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+
+ bool bTest = true;
+ for (SCTAB i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
+ if (maTabs[i])
+ bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize );
+
+ return bTest;
+}
+
+namespace {
+
+struct SetDirtyIfPostponedHandler
+{
+ void operator() (const ScTableUniquePtr & p)
+ {
+ if (p)
+ p->SetDirtyIfPostponed();
+ }
+};
+
+struct BroadcastRecalcOnRefMoveHandler
+{
+ void operator() (const ScTableUniquePtr & p)
+ {
+ if (p)
+ p->BroadcastRecalcOnRefMove();
+ }
+};
+
+struct BroadcastRecalcOnRefMoveGuard
+{
+ explicit BroadcastRecalcOnRefMoveGuard( ScDocument* pDoc ) :
+ aSwitch( *pDoc, false),
+ aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged)
+ {
+ }
+
+private:
+ sc::AutoCalcSwitch aSwitch; // first for ctor/dtor order, destroy second
+ ScBulkBroadcast aBulk; // second for ctor/dtor order, destroy first
+};
+
+}
+
+bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
+ SCCOL nEndCol, SCTAB nEndTab,
+ SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc,
+ const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = GetTableCount() - 1;
+ }
+
+ bool bTest = true;
+ bool bRet = false;
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+ bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
+ EnableDelayDeletingBroadcasters( true );
+ for ( i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize);
+ if (bTest)
+ {
+ // UpdateBroadcastAreas have to be called before UpdateReference, so that entries
+ // aren't shifted that would be rebuild at UpdateReference
+
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+ ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
+ sc::EndListeningContext aEndListenCxt(*this);
+
+ std::vector<ScAddress> aGroupPos;
+ do
+ {
+ aShiftedRange.aStart.SetTab(nTabRangeStart);
+ aShiftedRange.aEnd.SetTab(nTabRangeEnd);
+
+ // Collect all formula groups that will get split by the shifting,
+ // and end all their listening. Record the position of the top
+ // cell of the topmost group, and the position of the bottom cell
+ // of the bottommost group.
+ EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos);
+
+ UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0);
+ }
+ while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+
+ sc::RefUpdateContext aCxt(*this);
+ aCxt.meMode = URM_INSDEL;
+ aCxt.maRange = aShiftedRange;
+ aCxt.mnRowDelta = nSize;
+ do
+ {
+ aCxt.maRange.aStart.SetTab(nTabRangeStart);
+ aCxt.maRange.aEnd.SetTab(nTabRangeEnd);
+ UpdateReference(aCxt, pRefUndoDoc, false); // without drawing objects
+ }
+ while (lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+
+ // UpdateReference should have set "needs listening" flags to those
+ // whose references have been modified. We also need to set this flag
+ // to those that were in the groups that got split by shifting.
+ SetNeedsListeningGroups(aGroupPos);
+
+ for (i=nStartTab; i<=nEndTab && i < GetTableCount(); i++)
+ {
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ {
+ maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize );
+ maTabs[i]->CommentNotifyAddressChange(nStartCol, nStartRow, nEndCol, MaxRow());
+ }
+ }
+
+ // UpdateRef for drawing layer must be after inserting,
+ // when the new row heights are known.
+ for (i=nStartTab; i<=nEndTab && i < GetTableCount(); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->UpdateDrawRef( URM_INSDEL,
+ nStartCol, nStartRow, nStartTab, nEndCol, MaxRow(), nEndTab,
+ 0, static_cast<SCROW>(nSize), 0 );
+
+ if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
+ { // A new Listening is needed when references to deleted ranges are restored,
+ // previous Listeners were removed in FormulaCell UpdateReference.
+ StartAllListeners();
+ }
+ else
+ { // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+
+ // At least all cells using range names pointing relative to the
+ // moved range must be recalculated, and all cells marked postponed
+ // dirty.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyIfPostponed();
+ }
+
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+ bRet = true;
+ }
+ EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
+ SetAutoCalc( bOldAutoCalc );
+ if ( bRet && pChartListenerCollection )
+ pChartListenerCollection->UpdateDirtyCharts();
+ return bRet;
+}
+
+bool ScDocument::InsertRow( const ScRange& rRange )
+{
+ return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Tab(),
+ rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
+}
+
+void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
+ SCCOL nEndCol, SCTAB nEndTab,
+ SCROW nStartRow, SCSIZE nSize,
+ ScDocument* pRefUndoDoc, bool* pUndoOutline,
+ const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = GetTableCount() - 1;
+ }
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
+
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+ do
+ {
+ if ( ValidRow(nStartRow+nSize) )
+ {
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) );
+ UpdateBroadcastAreas( URM_INSDEL, ScRange(
+ ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ),
+ ScAddress( nEndCol, MaxRow(), nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 );
+ }
+ else
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( nEndCol, MaxRow(), nTabRangeEnd ) ) );
+ }
+ while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+
+ sc::RefUpdateContext aCxt(*this);
+ const bool bLastRowIncluded = (static_cast<SCROW>(nStartRow + nSize) == GetMaxRowCount() && ValidRow(nStartRow));
+ if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
+ {
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+ aCxt.meMode = URM_INSDEL;
+ aCxt.mnRowDelta = -static_cast<SCROW>(nSize);
+ if (bLastRowIncluded)
+ {
+ // Last row is included, shift a virtually non-existent row in.
+ aCxt.maRange = ScRange( nStartCol, GetMaxRowCount(), nTabRangeStart, nEndCol, GetMaxRowCount(), nTabRangeEnd);
+ }
+ else
+ {
+ aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
+ }
+ do
+ {
+ UpdateReference(aCxt, pRefUndoDoc, true, false);
+ }
+ while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+ }
+
+ if (pUndoOutline)
+ *pUndoOutline = false;
+
+ // Keep track of the positions of all formula groups that have been joined
+ // during row deletion.
+ std::vector<ScAddress> aGroupPos;
+
+ for ( i = nStartTab; i <= nEndTab && i < GetTableCount(); i++)
+ {
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ {
+ maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos);
+ maTabs[i]->CommentNotifyAddressChange(nStartCol, nStartRow, nEndCol, MaxRow());
+ }
+ }
+
+ // Newly joined groups have some of their members still listening. We
+ // need to make sure none of them are listening.
+ EndListeningGroups(aGroupPos);
+
+ // Mark all joined groups for group listening.
+ SetNeedsListeningGroups(aGroupPos);
+
+ if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
+ {
+ // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+
+ // At least all cells using range names pointing relative to the moved
+ // range must be recalculated, and all cells marked postponed dirty.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyIfPostponed();
+ }
+
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+
+ if (pChartListenerCollection)
+ pChartListenerCollection->UpdateDirtyCharts();
+}
+
+void ScDocument::DeleteRow( const ScRange& rRange )
+{
+ DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Tab(),
+ rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
+}
+
+bool ScDocument::CanInsertCol( const ScRange& rRange ) const
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1);
+
+ bool bTest = true;
+ for (SCTAB i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
+ if (maTabs[i])
+ bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
+
+ return bTest;
+}
+
+bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
+ SCROW nEndRow, SCTAB nEndTab,
+ SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
+ const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = GetTableCount() - 1;
+ }
+
+ bool bTest = true;
+ bool bRet = false;
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+ bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
+ EnableDelayDeletingBroadcasters( true );
+ for ( i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
+ if (bTest)
+ {
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+ do
+ {
+ UpdateBroadcastAreas( URM_INSDEL, ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 );
+ }
+ while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+
+ sc::RefUpdateContext aCxt(*this);
+ aCxt.meMode = URM_INSDEL;
+ aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MaxCol(), nEndRow, nTabRangeEnd);
+ aCxt.mnColDelta = nSize;
+ do
+ {
+ UpdateReference(aCxt, pRefUndoDoc, true, false);
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+
+ for (i = nStartTab; i <= nEndTab && i < GetTableCount(); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize);
+
+ if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
+ { // A new Listening is needed when references to deleted ranges are restored,
+ // previous Listeners were removed in FormulaCell UpdateReference.
+ StartAllListeners();
+ }
+ else
+ {
+ // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+ // At least all cells using range names pointing relative to the
+ // moved range must be recalculated, and all cells marked postponed
+ // dirty.
+ {
+ ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
+ std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler());
+ }
+ // Cells containing functions such as CELL, COLUMN or ROW may have
+ // changed their values on relocation. Broadcast them.
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+ bRet = true;
+ }
+ EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
+ SetAutoCalc( bOldAutoCalc );
+ if ( bRet && pChartListenerCollection )
+ pChartListenerCollection->UpdateDirtyCharts();
+ return bRet;
+}
+
+bool ScDocument::InsertCol( const ScRange& rRange )
+{
+ return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
+}
+
+void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab,
+ SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
+ bool* pUndoOutline, const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = GetTableCount() - 1;
+ }
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
+ ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
+
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+ do
+ {
+ if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
+ {
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) );
+ UpdateBroadcastAreas( URM_INSDEL, ScRange(
+ ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ),
+ ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 );
+ }
+ else
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( MaxCol(), nEndRow, nTabRangeEnd ) ) );
+ }
+ while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+
+ sc::RefUpdateContext aCxt(*this);
+ const bool bLastColIncluded = (static_cast<SCCOL>(nStartCol + nSize) == GetMaxColCount() && ValidCol(nStartCol));
+ if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
+ {
+ lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
+ aCxt.meMode = URM_INSDEL;
+ aCxt.mnColDelta = -static_cast<SCCOL>(nSize);
+ if (bLastColIncluded)
+ {
+ // Last column is included, shift a virtually non-existent column in.
+ aCxt.maRange = ScRange( GetMaxColCount(), nStartRow, nTabRangeStart, GetMaxColCount(), nEndRow, nTabRangeEnd);
+ }
+ else
+ {
+ aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart,
+ MaxCol(), nEndRow, nTabRangeEnd);
+ }
+ do
+ {
+ UpdateReference(aCxt, pRefUndoDoc, true, false);
+ }
+ while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
+ }
+
+ if (pUndoOutline)
+ *pUndoOutline = false;
+
+ for (i = nStartTab; i <= nEndTab && i < GetTableCount(); ++i)
+ {
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline);
+ }
+
+ if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
+ {
+ // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+
+ // At least all cells using range names pointing relative to the moved
+ // range must be recalculated, and all cells marked postponed dirty.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyIfPostponed();
+ }
+
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+
+ if (pChartListenerCollection)
+ pChartListenerCollection->UpdateDirtyCharts();
+}
+
+void ScDocument::DeleteCol( const ScRange& rRange )
+{
+ DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
+}
+
+// for Area-Links: Insert/delete cells, when the range is changed.
+// (without Paint)
+
+static void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew,
+ ScRange& rColRange, bool& rInsCol, bool& rDelCol,
+ ScRange& rRowRange, bool& rInsRow, bool& rDelRow )
+{
+ OSL_ENSURE( rOld.aStart == rNew.aStart, "FitBlock: Beginning is different" );
+
+ rInsCol = rDelCol = rInsRow = rDelRow = false;
+
+ SCCOL nStartX = rOld.aStart.Col();
+ SCROW nStartY = rOld.aStart.Row();
+ SCCOL nOldEndX = rOld.aEnd.Col();
+ SCROW nOldEndY = rOld.aEnd.Row();
+ SCCOL nNewEndX = rNew.aEnd.Col();
+ SCROW nNewEndY = rNew.aEnd.Row();
+ SCTAB nTab = rOld.aStart.Tab();
+
+ // if more rows, columns are inserted/deleted at the old height.
+ bool bGrowY = ( nNewEndY > nOldEndY );
+ SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY;
+ SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX;
+
+ // Columns
+
+ if ( nNewEndX > nOldEndX ) // Insert columns
+ {
+ rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab );
+ rInsCol = true;
+ }
+ else if ( nNewEndX < nOldEndX ) // Delete columns
+ {
+ rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab );
+ rDelCol = true;
+ }
+
+ // Rows
+
+ if ( nNewEndY > nOldEndY ) // Insert rows
+ {
+ rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab );
+ rInsRow = true;
+ }
+ else if ( nNewEndY < nOldEndY ) // Delete rows
+ {
+ rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab );
+ rDelRow = true;
+ }
+}
+
+bool ScDocument::HasPartOfMerged( const ScRange& rRange )
+{
+ bool bPart = false;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ SCCOL nStartX = rRange.aStart.Col();
+ SCROW nStartY = rRange.aStart.Row();
+ SCCOL nEndX = rRange.aEnd.Col();
+ SCROW nEndY = rRange.aEnd.Row();
+
+ if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
+ ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab );
+
+ bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() ||
+ nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() );
+ }
+ return bPart;
+}
+
+formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScAddress& rPos )
+{
+ SCTAB nTab = rPos.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->ResolveStaticReference(rPos.Col(), rPos.Row());
+ return formula::FormulaTokenRef();
+}
+
+formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScRange& rRange )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ if (nTab != rRange.aEnd.Tab() || !HasTable(nTab))
+ return formula::FormulaTokenRef();
+
+ return maTabs[nTab]->ResolveStaticReference(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+}
+
+formula::VectorRefArray ScDocument::FetchVectorRefArray( const ScAddress& rPos, SCROW nLength )
+{
+ SCTAB nTab = rPos.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
+ return formula::VectorRefArray();
+}
+
+#ifdef DBG_UTIL
+void ScDocument::AssertNoInterpretNeeded( const ScAddress& rPos, SCROW nLength )
+{
+ SCTAB nTab = rPos.Tab();
+ assert(HasTable(nTab));
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
+}
+#endif
+
+void ScDocument::UnlockAdjustHeight()
+{
+ assert(nAdjustHeightLock > 0);
+ if(nAdjustHeightLock > 0)
+ --nAdjustHeightLock;
+}
+
+bool ScDocument::HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength, const ScFormulaCellGroupRef& mxGroup, ScAddress* pDirtiedAddress)
+{
+ SCTAB nTab = rPos.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ bool bRet = pTable->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup, pDirtiedAddress);
+ if (!bRet && pDirtiedAddress && pDirtiedAddress->Row() != -1)
+ {
+ pDirtiedAddress->SetCol(rPos.Col());
+ pDirtiedAddress->SetTab(nTab);
+ }
+ return bRet;
+ }
+ return false;
+}
+
+bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew )
+{
+ if ( rOld == rNew )
+ return true;
+
+ bool bOk = true;
+ bool bInsCol,bDelCol,bInsRow,bDelRow;
+ ScRange aColRange,aRowRange;
+ lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
+
+ if ( bInsCol && !CanInsertCol( aColRange ) ) // Cells at the edge ?
+ bOk = false;
+ if ( bInsRow && !CanInsertRow( aRowRange ) ) // Cells at the edge ?
+ bOk = false;
+
+ if ( bInsCol || bDelCol )
+ {
+ aColRange.aEnd.SetCol(MaxCol());
+ if ( HasPartOfMerged(aColRange) )
+ bOk = false;
+ }
+ if ( bInsRow || bDelRow )
+ {
+ aRowRange.aEnd.SetRow(MaxRow());
+ if ( HasPartOfMerged(aRowRange) )
+ bOk = false;
+ }
+
+ return bOk;
+}
+
+void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear )
+{
+ if (bClear)
+ DeleteAreaTab( rOld, InsertDeleteFlags::ALL );
+
+ bool bInsCol,bDelCol,bInsRow,bDelRow;
+ ScRange aColRange,aRowRange;
+ lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
+
+ if ( bInsCol )
+ InsertCol( aColRange ); // First insert columns
+ if ( bInsRow )
+ InsertRow( aRowRange );
+
+ if ( bDelRow )
+ DeleteRow( aRowRange ); // First delete rows
+ if ( bDelCol )
+ DeleteCol( aColRange );
+
+ // Expand references to inserted rows
+
+ if ( bInsCol || bInsRow )
+ {
+ ScRange aGrowSource = rOld;
+ aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() ));
+ aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() ));
+ SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0;
+ SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0;
+ UpdateGrow( aGrowSource, nGrowX, nGrowY );
+ }
+}
+
+void ScDocument::DeleteArea(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
+ InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
+{
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+
+ std::vector<ScAddress> aGroupPos;
+ // Destroy and reconstruct listeners only if content is affected.
+ bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
+ if (bDelContent)
+ {
+ // Record the positions of top and/or bottom formula groups that intersect
+ // the area borders.
+ sc::EndListeningContext aCxt(*this);
+ ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
+ for (SCTAB i = 0; i < GetTableCount(); i++)
+ {
+ if (rMark.GetTableSelect(i))
+ {
+ aRange.aStart.SetTab(i);
+ aRange.aEnd.SetTab(i);
+
+ EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos);
+ }
+ }
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ for (SCTAB i = 0; i < GetTableCount(); i++)
+ if (maTabs[i])
+ if ( rMark.GetTableSelect(i) || bIsUndo )
+ maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
+
+ if (!bDelContent)
+ return;
+
+ // Re-start listeners on those top bottom groups that have been split.
+ SetNeedsListeningGroups(aGroupPos);
+ StartNeededListeners();
+
+ // If formula groups were split their listeners were destroyed and may
+ // need to be notified now that they're restored, ScTable::DeleteArea()
+ // couldn't do that.
+ if (aGroupPos.empty())
+ return;
+
+ ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
+ for (SCTAB i = 0; i < GetTableCount(); i++)
+ {
+ if (rMark.GetTableSelect(i))
+ {
+ aRange.aStart.SetTab(i);
+ aRange.aEnd.SetTab(i);
+ SetDirty( aRange, true);
+ }
+ }
+}
+
+void ScDocument::DeleteAreaTab(SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ SCTAB nTab, InsertDeleteFlags nDelFlag)
+{
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+ pTable->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag);
+ SetAutoCalc( bOldAutoCalc );
+ }
+}
+
+void ScDocument::DeleteAreaTab( const ScRange& rRange, InsertDeleteFlags nDelFlag )
+{
+ for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ )
+ DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(),
+ nTab, nDelFlag );
+}
+
+void ScDocument::InitUndoSelected(const ScDocument& rSrcDoc, const ScMarkData& rTabSelection,
+ bool bColInfo, bool bRowInfo )
+{
+ if (bIsUndo)
+ {
+ Clear();
+
+ SharePooledResources(&rSrcDoc);
+
+ for (SCTAB nTab = 0; nTab <= rTabSelection.GetLastSelected(); nTab++)
+ if ( rTabSelection.GetTableSelect( nTab ) )
+ {
+ ScTableUniquePtr pTable(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
+ if (nTab < GetTableCount())
+ maTabs[nTab] = std::move(pTable);
+ else
+ maTabs.push_back(std::move(pTable));
+ }
+ else
+ {
+ if (nTab < GetTableCount())
+ maTabs[nTab]=nullptr;
+ else
+ maTabs.push_back(nullptr);
+ }
+ }
+ else
+ {
+ OSL_FAIL("InitUndo");
+ }
+}
+
+void ScDocument::InitUndo( const ScDocument& rSrcDoc, SCTAB nTab1, SCTAB nTab2,
+ bool bColInfo, bool bRowInfo )
+{
+ if (!bIsUndo)
+ {
+ OSL_FAIL("InitUndo");
+ return;
+ }
+
+ Clear();
+
+ // Undo document shares its pooled resources with the source document.
+ SharePooledResources(&rSrcDoc);
+
+ if (rSrcDoc.mpShell->GetMedium())
+ maFileURL = rSrcDoc.mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+
+ if (nTab2 >= GetTableCount())
+ maTabs.resize(nTab2 + 1);
+ for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
+ {
+ maTabs[nTab].reset(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
+ }
+}
+
+void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, bool bColInfo, bool bRowInfo )
+{
+ if (!bIsUndo)
+ {
+ OSL_FAIL("AddUndoTab");
+ return;
+ }
+
+ if (nTab2 >= GetTableCount())
+ {
+ maTabs.resize(nTab2+1);
+ }
+
+ for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
+ if (!maTabs[nTab])
+ {
+ maTabs[nTab].reset( new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo) );
+ }
+}
+
+void ScDocument::SetCutMode( bool bVal )
+{
+ if (bIsClip)
+ GetClipParam().mbCutMode = bVal;
+ else
+ {
+ OSL_FAIL("SetCutMode without bIsClip");
+ }
+}
+
+bool ScDocument::IsCutMode()
+{
+ if (bIsClip)
+ return GetClipParam().mbCutMode;
+ else
+ {
+ OSL_FAIL("IsCutMode without bIsClip");
+ return false;
+ }
+}
+
+void ScDocument::CopyToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
+ const ScMarkData* pMarks, bool bColRowFlags )
+{
+ if (ValidTab(nTab1) && ValidTab(nTab2))
+ {
+ ScRange aThisRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ CopyToDocument(aThisRange, nFlags, bOnlyMarked, rDestDoc, pMarks, bColRowFlags);
+ }
+}
+
+void ScDocument::UndoToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
+{
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ PutInOrder( nTab1, nTab2 );
+ if (!(ValidTab(nTab1) && ValidTab(nTab2)))
+ return;
+
+ sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
+
+ if (nTab1 > 0)
+ CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
+
+ sc::CopyToDocContext aCxt(rDestDoc);
+ assert(nTab2 < GetTableCount() && nTab2 < rDestDoc.GetTableCount());
+ for (SCTAB i = nTab1; i <= nTab2; i++)
+ {
+ if (maTabs[i] && rDestDoc.maTabs[i])
+ maTabs[i]->UndoToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags,
+ bOnlyMarked, rDestDoc.maTabs[i].get());
+ }
+
+ if (nTab2 < MAXTAB)
+ CopyToDocument(0, 0, nTab2+1, MaxCol(), MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA, false, rDestDoc);
+}
+
+void ScDocument::CopyToDocument(const ScRange& rRange,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
+ const ScMarkData* pMarks, bool bColRowFlags)
+{
+ ScRange aNewRange = rRange;
+ aNewRange.PutInOrder();
+
+ if (rDestDoc.aDocName.isEmpty())
+ rDestDoc.aDocName = aDocName;
+
+ sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
+ ScBulkBroadcast aBulkBroadcast(rDestDoc.GetBASM(), SfxHintId::ScDataChanged);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ sc::CopyToDocContext aCxt(rDestDoc);
+ aCxt.setStartListening(false);
+
+ SCTAB nMinSizeBothTabs = std::min(GetTableCount(), rDestDoc.GetTableCount());
+ for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < nMinSizeBothTabs; i++)
+ {
+ ScTable* pTab = FetchTable(i);
+ ScTable* pDestTab = rDestDoc.FetchTable(i);
+ if (!pTab || !pDestTab)
+ continue;
+
+ pTab->CopyToTable(
+ aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
+ nFlags, bOnlyMarked, pDestTab, pMarks, false, bColRowFlags,
+ /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true);
+ }
+
+ rDestDoc.StartAllListeners(aNewRange);
+}
+
+void ScDocument::UndoToDocument(const ScRange& rRange,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
+{
+ sc::AutoCalcSwitch aAutoCalcSwitch(*this, false);
+
+ ScRange aNewRange = rRange;
+ aNewRange.PutInOrder();
+ SCTAB nTab1 = aNewRange.aStart.Tab();
+ SCTAB nTab2 = aNewRange.aEnd.Tab();
+
+ sc::CopyToDocContext aCxt(rDestDoc);
+ if (nTab1 > 0)
+ CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
+
+ SCTAB nMinSizeBothTabs = std::min(GetTableCount(), rDestDoc.GetTableCount());
+ for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
+ {
+ if (maTabs[i] && rDestDoc.maTabs[i])
+ maTabs[i]->UndoToTable(aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(),
+ aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
+ nFlags, bOnlyMarked, rDestDoc.maTabs[i].get());
+ }
+
+ if (nTab2 < GetTableCount())
+ CopyToDocument(0, 0 , nTab2+1, MaxCol(), MaxRow(), GetTableCount(), InsertDeleteFlags::FORMULA, false, rDestDoc);
+}
+
+void ScDocument::CopyToClip(const ScClipParam& rClipParam,
+ ScDocument* pClipDoc, const ScMarkData* pMarks,
+ bool bKeepScenarioFlags, bool bIncludeObjects )
+{
+ OSL_ENSURE( pMarks, "CopyToClip: ScMarkData fails" );
+
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc)
+ {
+ SAL_WARN("sc", "CopyToClip: no ClipDoc");
+ pClipDoc = ScModule::GetClipDoc();
+ }
+
+ if (mpShell->GetMedium())
+ {
+ pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ // for unsaved files use the title name and adjust during save of file
+ if (pClipDoc->maFileURL.isEmpty())
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+ else
+ {
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+
+ //init maTabNames
+ for (const auto& rxTab : maTabs)
+ {
+ if( rxTab )
+ {
+ OUString aTabName = rxTab->GetName();
+ pClipDoc->maTabNames.push_back(aTabName);
+ }
+ else
+ pClipDoc->maTabNames.emplace_back();
+ }
+
+ pClipDoc->aDocName = aDocName;
+ pClipDoc->SetClipParam(rClipParam);
+ ScRange aClipRange = rClipParam.getWholeRange();
+ SCTAB nEndTab = GetTableCount();
+
+ pClipDoc->ResetClip(this, pMarks);
+
+ sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags);
+ CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks);
+
+ for (SCTAB i = 0; i < nEndTab; ++i)
+ {
+ if (!maTabs[i] || i >= pClipDoc->GetTableCount() || !pClipDoc->maTabs[i])
+ continue;
+
+ if ( pMarks && !pMarks->GetTableSelect(i) )
+ continue;
+
+ maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get());
+
+ if (mpDrawLayer && bIncludeObjects)
+ {
+ // also copy drawing objects
+ tools::Rectangle aObjRect = GetMMRect(
+ aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
+ mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect);
+ }
+ }
+
+ // Make sure to mark overlapped cells.
+ pClipDoc->ExtendMerge(aClipRange, true);
+}
+
+void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument& rDestDoc)
+{
+ ScTable* pSrcTab = rSrcRange.aStart.Tab() < GetTableCount() ? maTabs[rSrcRange.aStart.Tab()].get() : nullptr;
+ ScTable* pDestTab = nDestTab < rDestDoc.GetTableCount() ? rDestDoc.maTabs[nDestTab].get() : nullptr;
+
+ if (!pSrcTab || !pDestTab)
+ return;
+
+ rDestDoc.GetFormatTable()->MergeFormatter(*GetFormatTable());
+ SvNumberFormatterMergeMap aMap = rDestDoc.GetFormatTable()->ConvertMergeTableToMap();
+
+ pSrcTab->CopyStaticToDocument(
+ rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(),
+ aMap, pDestTab);
+}
+
+void ScDocument::CopyCellToDocument( const ScAddress& rSrcPos, const ScAddress& rDestPos, ScDocument& rDestDoc )
+{
+ if (!HasTable(rSrcPos.Tab()) || !rDestDoc.HasTable(rDestPos.Tab()))
+ return;
+
+ ScTable& rSrcTab = *maTabs[rSrcPos.Tab()];
+ ScTable& rDestTab = *rDestDoc.maTabs[rDestPos.Tab()];
+
+ rSrcTab.CopyCellToDocument(rSrcPos.Col(), rSrcPos.Row(), rDestPos.Col(), rDestPos.Row(), rDestTab);
+}
+
+void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ SCTAB nTab, ScDocument* pClipDoc)
+{
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc)
+ {
+ SAL_WARN("sc", "CopyTabToClip: no ClipDoc");
+ pClipDoc = ScModule::GetClipDoc();
+ }
+
+ if (mpShell->GetMedium())
+ {
+ pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ // for unsaved files use the title name and adjust during save of file
+ if (pClipDoc->maFileURL.isEmpty())
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+ else
+ {
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+
+ //init maTabNames
+ for (const auto& rxTab : maTabs)
+ {
+ if( rxTab )
+ {
+ OUString aTabName = rxTab->GetName();
+ pClipDoc->maTabNames.push_back(aTabName);
+ }
+ else
+ pClipDoc->maTabNames.emplace_back();
+ }
+
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ pClipDoc->aDocName = aDocName;
+ rClipParam.maRanges.RemoveAll();
+ rClipParam.maRanges.push_back(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
+ pClipDoc->ResetClip( this, nTab );
+
+ sc::CopyToClipContext aCxt(*pClipDoc, false);
+ if (nTab < GetTableCount() && nTab < pClipDoc->GetTableCount())
+ if (maTabs[nTab] && pClipDoc->maTabs[nTab])
+ maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab].get());
+
+ pClipDoc->GetClipParam().mbCutMode = false;
+}
+
+void ScDocument::TransposeClip(ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink,
+ bool bIncludeFiltered)
+{
+ OSL_ENSURE( bIsClip && pTransClip && pTransClip->bIsClip,
+ "TransposeClip with wrong Document" );
+
+ // initialize
+ // -> pTransClip has to be deleted before the original document!
+
+ pTransClip->ResetClip(this, nullptr); // all
+
+ // Take over range
+
+ if (pRangeName)
+ {
+ pTransClip->GetRangeName()->clear();
+ for (const auto& rEntry : *pRangeName)
+ {
+ sal_uInt16 nIndex = rEntry.second->GetIndex();
+ ScRangeData* pData = new ScRangeData(*rEntry.second);
+ if (pTransClip->pRangeName->insert(pData))
+ pData->SetIndex(nIndex);
+ }
+ }
+
+ ScRange aCombinedClipRange = GetClipParam().getWholeRange();
+
+ if (!ValidRow(aCombinedClipRange.aEnd.Row() - aCombinedClipRange.aStart.Row()))
+ {
+ SAL_WARN("sc", "TransposeClip: Too big");
+ return;
+ }
+
+ // Transpose of filtered multi range row selection is a special case since filtering
+ // and selection are in the same dimension (i.e. row).
+ // The filtered row status and the selection ranges are not available at the same time,
+ // handle this case specially, do not use GetClipParam().getWholeRange(),
+ // instead loop through the ranges, calculate the row offset and handle filtered rows and
+ // create in ScClipParam::transpose() a unified range.
+ const bool bIsMultiRangeRowFilteredTranspose
+ = !bIncludeFiltered && GetClipParam().isMultiRange()
+ && HasFilteredRows(aCombinedClipRange.aStart.Row(), aCombinedClipRange.aEnd.Row(),
+ aCombinedClipRange.aStart.Tab())
+ && GetClipParam().meDirection == ScClipParam::Row;
+
+ ScRangeList aClipRanges;
+ if (bIsMultiRangeRowFilteredTranspose)
+ aClipRanges = GetClipParam().maRanges;
+ else
+ aClipRanges = ScRangeList(aCombinedClipRange);
+
+ // The data
+ ScRange aClipRange;
+ SCROW nRowCount = 0; // next consecutive row
+ for (size_t j = 0, n = aClipRanges.size(); j < n; ++j)
+ {
+ aClipRange = aClipRanges[j];
+
+ SCROW nRowOffset = 0;
+ if (bIsMultiRangeRowFilteredTranspose)
+ {
+ // adjust for the rows that are filtered
+ nRowOffset = nRowCount;
+
+ // calculate filtered rows of current clip range
+ SCROW nRowCountNonFiltered = CountNonFilteredRows(
+ aClipRange.aStart.Row(), aClipRange.aEnd.Row(), aClipRange.aStart.Tab());
+ assert(!bIncludeFiltered && "bIsMultiRangeRowFilteredTranspose can only be true if bIncludeFiltered is false");
+ nRowCount += nRowCountNonFiltered; // for next iteration
+ }
+
+ for (SCTAB i = 0; i < GetTableCount(); i++)
+ {
+ if (maTabs[i])
+ {
+ OSL_ENSURE(pTransClip->maTabs[i], "TransposeClip: Table not there");
+ maTabs[i]->TransposeClip(
+ aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(),
+ aClipRange.aEnd.Row(), aCombinedClipRange.aStart.Row(), nRowOffset,
+ pTransClip->maTabs[i].get(), nFlags, bAsLink, bIncludeFiltered);
+
+ if ( mpDrawLayer && ( nFlags & InsertDeleteFlags::OBJECTS ) )
+ {
+ // Drawing objects are copied to the new area without transposing.
+ // CopyFromClip is used to adjust the objects to the transposed block's
+ // cell range area.
+ // (mpDrawLayer in the original clipboard document is set only if there
+ // are drawing objects to copy)
+
+ // ToDo: Loop over blocks of non-filtered rows in case of filtered rows exist.
+ pTransClip->InitDrawLayer();
+ ScAddress aTransposedEnd(
+ static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row() + aClipRange.aStart.Col()),
+ static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col() + aClipRange.aStart.Row()), i);
+ ScRange aDestRange(aClipRange.aStart, aTransposedEnd);
+ ScAddress aDestStart = aClipRange.aStart;
+ pTransClip->mpDrawLayer->CopyFromClip(mpDrawLayer.get(), i, aClipRange, aDestStart, aDestRange, true);
+ }
+ }
+ }
+ }
+
+ pTransClip->SetClipParam(GetClipParam());
+ pTransClip->GetClipParam().transpose(*this, bIncludeFiltered,
+ bIsMultiRangeRowFilteredTranspose);
+
+ // This happens only when inserting...
+
+ GetClipParam().mbCutMode = false;
+}
+
+namespace {
+
+void copyUsedNamesToClip(ScRangeName* pClipRangeName, ScRangeName* pRangeName,
+ const sc::UpdatedRangeNames::NameIndicesType& rUsedNames)
+{
+ pClipRangeName->clear();
+ for (const auto& rEntry : *pRangeName) //TODO: also DB and Pivot regions!!!
+ {
+ sal_uInt16 nIndex = rEntry.second->GetIndex();
+ bool bInUse = (rUsedNames.count(nIndex) > 0);
+ if (!bInUse)
+ continue;
+
+ ScRangeData* pData = new ScRangeData(*rEntry.second);
+ if (pClipRangeName->insert(pData))
+ pData->SetIndex(nIndex);
+ }
+}
+
+}
+
+void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks)
+{
+ if (!pRangeName || pRangeName->empty())
+ return;
+
+ sc::UpdatedRangeNames aUsedNames; // indexes of named ranges that are used in the copied cells
+ SCTAB nMinSizeBothTabs = std::min(GetTableCount(), pClipDoc->GetTableCount());
+ for (SCTAB i = 0; i < nMinSizeBothTabs; ++i)
+ if (maTabs[i] && pClipDoc->maTabs[i])
+ if ( !pMarks || pMarks->GetTableSelect(i) )
+ maTabs[i]->FindRangeNamesInUse(
+ rClipRange.aStart.Col(), rClipRange.aStart.Row(),
+ rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames);
+
+ /* TODO: handle also sheet-local names */
+ sc::UpdatedRangeNames::NameIndicesType aUsedGlobalNames( aUsedNames.getUpdatedNames(-1));
+ copyUsedNamesToClip(pClipDoc->GetRangeName(), pRangeName.get(), aUsedGlobalNames);
+}
+
+ScDocument::NumFmtMergeHandler::NumFmtMergeHandler(ScDocument& rDoc, const ScDocument& rSrcDoc)
+ : mrDoc(rDoc)
+{
+ mrDoc.MergeNumberFormatter(rSrcDoc);
+}
+
+ScDocument::NumFmtMergeHandler::~NumFmtMergeHandler()
+{
+ ScMutationGuard aGuard(mrDoc, ScMutationGuardFlags::CORE);
+ mrDoc.pFormatExchangeList = nullptr;
+}
+
+void ScDocument::PrepareFormulaCalc()
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpFormulaGroupCxt.reset();
+}
+
+SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return nullptr;
+
+ return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
+}
+
+const SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) const
+{
+ const ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return nullptr;
+
+ return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
+}
+
+void ScDocument::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength )
+{
+ ScTable* pTab = FetchTable(rTopPos.Tab());
+ if (!pTab || nLength <= 0)
+ return;
+
+ pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
+}
+
+#if DUMP_COLUMN_STORAGE
+void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->DumpColumnStorage(nCol);
+}
+#endif
+
+bool ScDocument::HasTable(SCTAB nTab) const
+{
+ return ValidTab(nTab)
+ && nTab < GetTableCount()
+ && maTabs[nTab];
+}
+
+ScTable* ScDocument::FetchTable( SCTAB nTab )
+{
+ if (!HasTable(nTab))
+ return nullptr;
+
+ return maTabs[nTab].get();
+}
+
+const ScTable* ScDocument::FetchTable( SCTAB nTab ) const
+{
+ if (!HasTable(nTab))
+ return nullptr;
+
+ return maTabs[nTab].get();
+}
+
+ScColumnsRange ScDocument::GetWritableColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetWritableColumnsRange(nColBegin, nColEnd);
+
+ SAL_WARN("sc", "GetWritableColumnsRange() called for non-existent table");
+ return ScColumnsRange(-1, -1);
+}
+
+ScColumnsRange ScDocument::GetAllocatedColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetAllocatedColumnsRange(nColBegin, nColEnd);
+ return ScColumnsRange(-1, -1);
+}
+
+ScColumnsRange ScDocument::GetColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetColumnsRange(nColBegin, nColEnd);
+ return ScColumnsRange(-1, -1);
+}
+
+void ScDocument::MergeNumberFormatter(const ScDocument& rSrcDoc)
+{
+ SvNumberFormatter* pThisFormatter = mxPoolHelper->GetFormTable();
+ SvNumberFormatter* pOtherFormatter = rSrcDoc.mxPoolHelper->GetFormTable();
+ if (pOtherFormatter && pOtherFormatter != pThisFormatter)
+ {
+ SvNumberFormatterIndexTable* pExchangeList =
+ pThisFormatter->MergeFormatter(*pOtherFormatter);
+ if (!pExchangeList->empty())
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pFormatExchangeList = pExchangeList;
+ }
+ }
+}
+
+ScClipParam& ScDocument::GetClipParam()
+{
+ if (!mpClipParam)
+ mpClipParam.reset(new ScClipParam);
+
+ return *mpClipParam;
+}
+
+void ScDocument::SetClipParam(const ScClipParam& rParam)
+{
+ mpClipParam.reset(new ScClipParam(rParam));
+}
+
+bool ScDocument::IsClipboardSource() const
+{
+ if (bIsClip || mpShell == nullptr || mpShell->IsLoading())
+ return false;
+
+ ScDocument* pClipDoc = ScModule::GetClipDoc();
+ return pClipDoc && pClipDoc->bIsClip && pClipDoc->mxPoolHelper.is() && mxPoolHelper.is() &&
+ mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool();
+}
+
+void ScDocument::StartListeningFromClip(
+ sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+ SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->StartListeningFormulaCells(rStartCxt, rEndCxt, nCol1, nRow1, nCol2, nRow2);
+}
+
+void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark, InsertDeleteFlags nInsFlag )
+{
+ if (!(nInsFlag & InsertDeleteFlags::CONTENTS))
+ return;
+
+ auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
+
+ sc::StartListeningContext aStartCxt(*this, pSet);
+ sc::EndListeningContext aEndCxt(*this, pSet, nullptr);
+
+ for (SCTAB nTab : rMark)
+ StartListeningFromClip(aStartCxt, aEndCxt, nTab, nCol1, nRow1, nCol2, nRow2);
+}
+
+void ScDocument::SetDirtyFromClip(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
+ InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ if (nInsFlag & InsertDeleteFlags::CONTENTS)
+ {
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
+ }
+ }
+}
+
+bool ScDocument::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCTAB nTab, SCCOL nCol )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->InitColumnBlockPosition(rBlockPos, nCol);
+ return false;
+}
+
+void ScDocument::CopyBlockFromClip(
+ sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark, SCCOL nDx, SCROW nDy )
+{
+ TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
+ SCTAB nTabEnd = rCxt.getTabEnd();
+ SCTAB nClipTab = 0;
+ for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < GetTableCount(); i++)
+ {
+ if (maTabs[i] && rMark.GetTableSelect(i) )
+ {
+ while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
+
+ maTabs[i]->CopyFromClip(
+ rCxt, nCol1, nRow1, nCol2, nRow2, nDx, nDy, rClipTabs[nClipTab].get());
+
+ if (rCxt.getClipDoc()->mpDrawLayer && (rCxt.getInsertFlag() & InsertDeleteFlags::OBJECTS))
+ {
+ // also copy drawing objects
+
+ // drawing layer must be created before calling CopyFromClip
+ // (ScDocShell::MakeDrawLayer also does InitItems etc.)
+ OSL_ENSURE( mpDrawLayer, "CopyBlockFromClip: No drawing layer" );
+ if ( mpDrawLayer )
+ {
+ // For GetMMRect, the row heights in the target document must already be valid
+ // (copied in an extra step before pasting, or updated after pasting cells, but
+ // before pasting objects).
+ ScRange aSourceRange(nCol1 - nDx, nRow1 - nDy, nClipTab, nCol2 - nDx, nRow2 - nDy, nClipTab);
+ ScRange aDestRange(nCol1, nRow1, i, nCol2, nRow2, i);
+ mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRange,
+ ScAddress( nCol1, nRow1, i ), aDestRange);
+ }
+ }
+
+ nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
+ }
+ }
+ if (!(rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS))
+ return;
+
+ nClipTab = 0;
+ for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < GetTableCount(); i++)
+ {
+ if (maTabs[i] && rMark.GetTableSelect(i) )
+ {
+ while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
+ SCTAB nDz = i - nClipTab;
+
+ // ranges of consecutive selected tables (in clipboard and dest. doc)
+ // must be handled in one UpdateReference call
+ SCTAB nFollow = 0;
+ while ( i + nFollow < nTabEnd
+ && rMark.GetTableSelect( i + nFollow + 1 )
+ && nClipTab + nFollow < MAXTAB
+ && rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] )
+ ++nFollow;
+
+ sc::RefUpdateContext aRefCxt(*this, rCxt.getClipDoc());
+ aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow);
+ aRefCxt.mnColDelta = nDx;
+ aRefCxt.mnRowDelta = nDy;
+ aRefCxt.mnTabDelta = nDz;
+ aRefCxt.setBlockPositionReference(rCxt.getBlockPositionSet()); // share mdds position caching
+ if (rCxt.getClipDoc()->GetClipParam().mbCutMode)
+ {
+ // Update references only if cut originates from the same
+ // document we are pasting into.
+ if (rCxt.getClipDoc()->GetPool() == GetPool())
+ {
+ bool bOldInserting = IsInsertingFromOtherDoc();
+ SetInsertingFromOtherDoc( true);
+ aRefCxt.meMode = URM_MOVE;
+ UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
+
+ // For URM_MOVE group listeners may have been removed,
+ // re-establish them.
+ if (!aRefCxt.maRegroupCols.empty())
+ {
+ /* TODO: holding the ColumnSet in a shared_ptr at
+ * RefUpdateContext would eliminate the need of
+ * copying it here. */
+ auto pColSet = std::make_shared<sc::ColumnSet>( aRefCxt.maRegroupCols);
+ StartNeededListeners( pColSet);
+ }
+
+ SetInsertingFromOtherDoc( bOldInserting);
+ }
+ }
+ else
+ {
+ aRefCxt.meMode = URM_COPY;
+ UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
+ }
+
+ nClipTab = (nClipTab+nFollow+1) % static_cast<SCTAB>(rClipTabs.size());
+ i = sal::static_int_cast<SCTAB>( i + nFollow );
+ }
+ }
+}
+
+SCROW ScDocument::CopyNonFilteredFromClip(sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
+ SCCOL nDx, SCROW& rClipStartRow, SCROW nClipEndRow)
+{
+ // call CopyBlockFromClip for ranges of consecutive non-filtered rows
+ // nCol1/nRow1 etc. is in target doc
+
+ // filtered state is taken from first used table in clipboard (as in GetClipArea)
+ SCTAB nFlagTab = 0;
+ TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
+ while ( nFlagTab < static_cast<SCTAB>(rClipTabs.size()) && !rClipTabs[nFlagTab] )
+ ++nFlagTab;
+
+ SCROW nSourceRow = rClipStartRow;
+ SCROW nSourceEnd = nClipEndRow;
+ SCROW nDestRow = nRow1;
+ SCROW nFilteredRows = 0;
+
+ while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 )
+ {
+ // skip filtered rows
+ SCROW nSourceRowOriginal = nSourceRow;
+ nSourceRow = rCxt.getClipDoc()->FirstNonFilteredRow(nSourceRow, nSourceEnd, nFlagTab);
+ nFilteredRows += nSourceRow - nSourceRowOriginal;
+
+ if ( nSourceRow <= nSourceEnd )
+ {
+ // look for more non-filtered rows following
+ SCROW nLastRow = nSourceRow;
+ (void)rCxt.getClipDoc()->RowFiltered(nSourceRow, nFlagTab, nullptr, &nLastRow);
+ SCROW nFollow = nLastRow - nSourceRow;
+
+ if (nFollow > nSourceEnd - nSourceRow)
+ nFollow = nSourceEnd - nSourceRow;
+ if (nFollow > nRow2 - nDestRow)
+ nFollow = nRow2 - nDestRow;
+
+ SCROW nNewDy = nDestRow - nSourceRow;
+ CopyBlockFromClip(
+ rCxt, nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy);
+
+ nSourceRow += nFollow + 1;
+ nDestRow += nFollow + 1;
+ }
+ }
+ rClipStartRow = nSourceRow;
+ return nFilteredRows;
+}
+
+namespace {
+
+class BroadcastAction : public sc::ColumnSpanSet::ColumnAction
+{
+ ScDocument& mrDoc;
+ ScColumn* mpCol;
+
+public:
+ explicit BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(nullptr) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mpCol = pCol;
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ assert(mpCol);
+ ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab());
+ aRange.aEnd.SetRow(nRow2);
+ mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ };
+};
+
+}
+
+void ScDocument::CopyFromClip(
+ const ScRange& rDestRange, const ScMarkData& rMark, InsertDeleteFlags nInsFlag,
+ ScDocument* pRefUndoDoc, ScDocument* pClipDoc, bool bResetCut,
+ bool bAsLink, bool bIncludeFiltered, bool bSkipEmptyCells,
+ const ScRangeList * pDestRanges )
+{
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc)
+ {
+ OSL_FAIL("CopyFromClip: no ClipDoc");
+ pClipDoc = ScModule::GetClipDoc();
+ }
+
+ if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
+ return;
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // temporarily turn off auto calc.
+
+ NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
+
+ SCCOL nAllCol1 = rDestRange.aStart.Col();
+ SCROW nAllRow1 = rDestRange.aStart.Row();
+ SCCOL nAllCol2 = rDestRange.aEnd.Col();
+ SCROW nAllRow2 = rDestRange.aEnd.Row();
+
+ SCCOL nXw = 0;
+ SCROW nYw = 0;
+ ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
+ for (SCTAB nTab = 0; nTab < pClipDoc->GetTableCount(); nTab++) // find largest merge overlap
+ if (pClipDoc->maTabs[nTab]) // all sheets of the clipboard content
+ {
+ SCCOL nThisEndX = aClipRange.aEnd.Col();
+ SCROW nThisEndY = aClipRange.aEnd.Row();
+ pClipDoc->ExtendMerge( aClipRange.aStart.Col(),
+ aClipRange.aStart.Row(),
+ nThisEndX, nThisEndY, nTab );
+ // only extra value from ExtendMerge
+ nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() );
+ nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() );
+ if ( nThisEndX > nXw )
+ nXw = nThisEndX;
+ if ( nThisEndY > nYw )
+ nYw = nThisEndY;
+ }
+
+ SCCOL nDestAddX;
+ SCROW nDestAddY;
+ pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered );
+ nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX );
+ nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY ); // ClipArea, plus ExtendMerge value
+
+ /* Decide which contents to delete before copying. Delete all
+ contents if nInsFlag contains any real content flag.
+ #i102056# Notes are pasted from clipboard in a second pass,
+ together with the special flag InsertDeleteFlags::ADDNOTES that states to not
+ overwrite/delete existing cells but to insert the notes into
+ these cells. In this case, just delete old notes from the
+ destination area. */
+ InsertDeleteFlags nDelFlag = InsertDeleteFlags::NONE;
+ if ( (nInsFlag & (InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ADDNOTES)) == (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES) )
+ nDelFlag |= InsertDeleteFlags::NOTE;
+ // tdf#141440 - do not delete notes when pasting contents (see InsertDeleteFlags::CONTENTS)
+ else if ( nInsFlag & (InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE) )
+ nDelFlag |= InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE;
+
+ if (nInsFlag & InsertDeleteFlags::ATTRIB)
+ nDelFlag |= InsertDeleteFlags::ATTRIB;
+
+ sc::CopyFromClipContext aCxt(*this, pRefUndoDoc, pClipDoc, nInsFlag, bAsLink, bSkipEmptyCells);
+ std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
+ aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
+ aCxt.setDeleteFlag(nDelFlag);
+
+ ScRangeList aLocalRangeList;
+ if (!pDestRanges)
+ {
+ aLocalRangeList.push_back( rDestRange);
+ pDestRanges = &aLocalRangeList;
+ }
+
+ bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
+
+ sc::ColumnSpanSet aBroadcastSpans;
+
+ SCCOL nClipStartCol = aClipRange.aStart.Col();
+ SCROW nClipStartRow = aClipRange.aStart.Row();
+ SCROW nClipEndRow = aClipRange.aEnd.Row();
+ for ( size_t nRange = 0; nRange < pDestRanges->size(); ++nRange )
+ {
+ const ScRange & rRange = (*pDestRanges)[nRange];
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ SCROW nRow2 = rRange.aEnd.Row();
+
+ aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
+ DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans); // <- this removes existing formula listeners
+
+ if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2))
+ continue;
+
+ SCCOL nC1 = nCol1;
+ SCROW nR1 = nRow1;
+ SCCOL nC2 = nC1 + nXw;
+ if (nC2 > nCol2)
+ nC2 = nCol2;
+ SCROW nR2 = nR1 + nYw;
+ if (nR2 > nRow2)
+ nR2 = nRow2;
+
+ const SCCOLROW nThreshold = 8192;
+ bool bPreallocatePattern = ((nInsFlag & InsertDeleteFlags::ATTRIB) && (nRow2 - nRow1 > nThreshold));
+ std::vector< SCTAB > vTables;
+
+ if (bPreallocatePattern)
+ {
+ for (SCTAB i = aCxt.getTabStart(); i <= aCxt.getTabEnd(); ++i)
+ if (maTabs[i] && rMark.GetTableSelect( i ) )
+ vTables.push_back( i );
+ }
+
+ do
+ {
+ // Pasting is done column-wise, when pasting to a filtered
+ // area this results in partitioning and we have to
+ // remember and reset the start row for each column until
+ // it can be advanced for the next chunk of unfiltered
+ // rows.
+ SCROW nSaveClipStartRow = nClipStartRow;
+ do
+ {
+ nClipStartRow = nSaveClipStartRow;
+ SCCOL nDx = nC1 - nClipStartCol;
+ SCROW nDy = nR1 - nClipStartRow;
+ if ( bIncludeFiltered )
+ {
+ CopyBlockFromClip(
+ aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nDy);
+ nClipStartRow += nR2 - nR1 + 1;
+ }
+ else
+ {
+ CopyNonFilteredFromClip(aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nClipStartRow,
+ nClipEndRow);
+ }
+ nC1 = nC2 + 1;
+ nC2 = std::min(static_cast<SCCOL>(nC1 + nXw), nCol2);
+ } while (nC1 <= nCol2);
+ if (nClipStartRow > nClipEndRow)
+ nClipStartRow = aClipRange.aStart.Row();
+ nC1 = nCol1;
+ nC2 = nC1 + nXw;
+ if (nC2 > nCol2)
+ nC2 = nCol2;
+
+ // Preallocate pattern memory once if further chunks are to be pasted.
+ if (bPreallocatePattern && (nR2+1) <= nRow2)
+ {
+ SCROW nR3 = nR2 + 1;
+ for (SCTAB nTab : vTables)
+ {
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ // Pattern count of the first chunk pasted.
+ SCSIZE nChunk = GetPatternCount( nTab, nCol, nR1, nR2);
+ // If it is only one pattern per chunk and chunks are
+ // pasted consecutively then it will get its range
+ // enlarged for each chunk and no further allocation
+ // happens. For non-consecutive chunks we're out of
+ // luck in this case.
+ if (nChunk > 1)
+ {
+ SCSIZE nNeeded = nChunk * (nRow2 - nR3 + 1) / (nYw + 1);
+ SCSIZE nRemain = GetPatternCount( nTab, nCol, nR3, nRow2);
+ if (nNeeded > nRemain)
+ {
+ SCSIZE nCurr = GetPatternCount( nTab, nCol);
+ ReservePatternCount( nTab, nCol, nCurr + nNeeded);
+ }
+ }
+ }
+ }
+ bPreallocatePattern = false;
+ }
+
+ nR1 = nR2 + 1;
+ nR2 = std::min(static_cast<SCROW>(nR1 + nYw), nRow2);
+ } while (nR1 <= nRow2);
+ }
+
+ bInsertingFromOtherDoc = false;
+
+ if (nInsFlag & InsertDeleteFlags::CONTENTS)
+ {
+ for (SCTAB nTab : rMark)
+ aCxt.setListeningFormulaSpans(nTab, nAllCol1, nAllRow1, nAllCol2, nAllRow2);
+ }
+
+ // Create Listener after everything has been inserted
+ aCxt.startListeningFormulas();
+
+ {
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+
+ // Set all formula cells dirty, and collect non-empty non-formula cell
+ // positions so that we can broadcast on them below.
+ SetDirtyFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans);
+
+ BroadcastAction aAction(*this);
+ aBroadcastSpans.executeColumnAction(*this, aAction);
+ }
+
+ if (bResetCut)
+ pClipDoc->GetClipParam().mbCutMode = false;
+}
+
+void ScDocument::CopyMultiRangeFromClip(const ScAddress& rDestPos, const ScMarkData& rMark,
+ InsertDeleteFlags nInsFlag, ScDocument* pClipDoc,
+ bool bResetCut, bool bAsLink, bool bIncludeFiltered,
+ bool bSkipAttrForEmpty)
+{
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
+ // There is nothing in the clip doc to copy.
+ return;
+
+ // Right now, we don't allow pasting into filtered rows, so we don't even handle it here.
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // turn of auto calc temporarily.
+ NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
+
+ const ScRange& aDestRange = rMark.GetMarkArea();
+
+ bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
+
+ SCCOL nCol1 = rDestPos.Col();
+ SCROW nRow1 = rDestPos.Row();
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+
+ sc::ColumnSpanSet aBroadcastSpans;
+
+ if (!bSkipAttrForEmpty)
+ {
+ // Do the deletion first.
+ SCCOL nColSize = rClipParam.getPasteColSize();
+ SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, bIncludeFiltered);
+
+ DeleteArea(nCol1, nRow1, nCol1+nColSize-1, nRow1+nRowSize-1, rMark, InsertDeleteFlags::CONTENTS, false, &aBroadcastSpans);
+ }
+
+ sc::CopyFromClipContext aCxt(*this, nullptr, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty);
+ std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
+ aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
+
+ for (size_t i = 0, n = rClipParam.maRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rClipParam.maRanges[i];
+
+ SCROW nRowCount = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ SCCOL nDx = static_cast<SCCOL>(nCol1 - rRange.aStart.Col());
+ SCROW nDy = static_cast<SCROW>(nRow1 - rRange.aStart.Row());
+ SCCOL nCol2 = nCol1 + rRange.aEnd.Col() - rRange.aStart.Col();
+ SCROW nEndRow = nRow1 + nRowCount - 1;
+ SCROW nFilteredRows = 0;
+
+ if (bIncludeFiltered)
+ {
+ CopyBlockFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx, nDy);
+ }
+ else
+ {
+ SCROW nClipStartRow = rRange.aStart.Row();
+ SCROW nClipEndRow = rRange.aEnd.Row();
+ nFilteredRows += CopyNonFilteredFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx,
+ nClipStartRow, nClipEndRow);
+ nRowCount -= nFilteredRows;
+ }
+
+ switch (rClipParam.meDirection)
+ {
+ case ScClipParam::Row:
+ // Begin row for the next range being pasted.
+ nRow1 += nRowCount;
+ break;
+ case ScClipParam::Column:
+ nCol1 += rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ break;
+ default:
+ ;
+ }
+ }
+
+ bInsertingFromOtherDoc = false;
+
+ // Create Listener after everything has been inserted
+ StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag );
+
+ {
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+
+ // Set formula cells dirty and collect non-formula cells.
+ SetDirtyFromClip(
+ aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aEnd.Col(), aDestRange.aEnd.Row(),
+ rMark, nInsFlag, aBroadcastSpans);
+
+ BroadcastAction aAction(*this);
+ aBroadcastSpans.executeColumnAction(*this, aAction);
+ }
+
+ if (bResetCut)
+ pClipDoc->GetClipParam().mbCutMode = false;
+}
+
+void ScDocument::SetClipArea( const ScRange& rArea, bool bCut )
+{
+ if (bIsClip)
+ {
+ ScClipParam& rClipParam = GetClipParam();
+ rClipParam.maRanges.RemoveAll();
+ rClipParam.maRanges.push_back(rArea);
+ rClipParam.mbCutMode = bCut;
+ }
+ else
+ {
+ OSL_FAIL("SetClipArea: No Clip");
+ }
+}
+
+void ScDocument::GetClipArea(SCCOL& nClipX, SCROW& nClipY, bool bIncludeFiltered)
+{
+ if (!bIsClip)
+ {
+ OSL_FAIL("GetClipArea: No Clip");
+ return;
+ }
+
+ ScRangeList& rClipRanges = GetClipParam().maRanges;
+ if (rClipRanges.empty())
+ // No clip range. Bail out.
+ return;
+
+ ScRange const & rRange = rClipRanges.front();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for ( size_t i = 1, n = rClipRanges.size(); i < n; ++i )
+ {
+ ScRange const & rRange2 = rClipRanges[ i ];
+ if (rRange2.aStart.Col() < nStartCol)
+ nStartCol = rRange2.aStart.Col();
+ if (rRange2.aStart.Row() < nStartRow)
+ nStartRow = rRange2.aStart.Row();
+ if (rRange2.aEnd.Col() > nEndCol)
+ nEndCol = rRange2.aEnd.Col();
+ if (rRange2.aEnd.Row() > nEndRow)
+ nEndRow = rRange2.aEnd.Row();
+ }
+
+ nClipX = nEndCol - nStartCol;
+
+ if ( bIncludeFiltered )
+ nClipY = nEndRow - nStartRow;
+ else
+ {
+ // count non-filtered rows
+ // count on first used table in clipboard
+ SCTAB nCountTab = 0;
+ while (nCountTab < GetTableCount() && !maTabs[nCountTab])
+ ++nCountTab;
+
+ SCROW nResult = CountNonFilteredRows(nStartRow, nEndRow, nCountTab);
+
+ if ( nResult > 0 )
+ nClipY = nResult - 1;
+ else
+ nClipY = 0; // always return at least 1 row
+ }
+}
+
+void ScDocument::GetClipStart(SCCOL& nClipX, SCROW& nClipY)
+{
+ if (bIsClip)
+ {
+ ScRangeList& rClipRanges = GetClipParam().maRanges;
+ if ( !rClipRanges.empty() )
+ {
+ nClipX = rClipRanges.front().aStart.Col();
+ nClipY = rClipRanges.front().aStart.Row();
+ }
+ }
+ else
+ {
+ OSL_FAIL("GetClipStart: No Clip");
+ }
+}
+
+bool ScDocument::HasClipFilteredRows()
+{
+ // count on first used table in clipboard
+ SCTAB nCountTab = 0;
+ while (nCountTab < GetTableCount() && !maTabs[nCountTab])
+ ++nCountTab;
+
+ ScRangeList& rClipRanges = GetClipParam().maRanges;
+ if ( rClipRanges.empty() )
+ return false;
+
+ if (maTabs.size() > 0)
+ {
+ for (size_t i = 0, n = rClipRanges.size(); i < n; ++i)
+ {
+ ScRange& rRange = rClipRanges[i];
+ bool bAnswer
+ = maTabs[nCountTab]->HasFilteredRows(rRange.aStart.Row(), rRange.aEnd.Row());
+ if (bAnswer)
+ return true;
+ }
+ }
+ return false;
+}
+
+void ScDocument::MixDocument( const ScRange& rRange, ScPasteFunc nFunction, bool bSkipEmpty,
+ ScDocument& rSrcDoc )
+{
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ sc::MixDocContext aCxt(*this);
+ SCTAB nMinSizeBothTabs = std::min(GetTableCount(), rSrcDoc.GetTableCount());
+ for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
+ {
+ ScTable* pTab = FetchTable(i);
+ const ScTable* pSrcTab = rSrcDoc.FetchTable(i);
+ if (!pTab || !pSrcTab)
+ continue;
+
+ pTab->MixData(
+ aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
+ nFunction, bSkipEmpty, pSrcTab);
+ }
+}
+
+void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark,
+ InsertDeleteFlags nFlags, ScPasteFunc nFunction,
+ bool bSkipEmpty, bool bAsLink )
+{
+ InsertDeleteFlags nDelFlags = nFlags;
+ if (nDelFlags & InsertDeleteFlags::CONTENTS)
+ nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing!
+
+ SCTAB nSrcTab = rSrcArea.aStart.Tab();
+
+ if (ScTable* pSourceTable = FetchTable(nSrcTab))
+ {
+ SCCOL nStartCol = rSrcArea.aStart.Col();
+ SCROW nStartRow = rSrcArea.aStart.Row();
+ SCCOL nEndCol = rSrcArea.aEnd.Col();
+ SCROW nEndRow = rSrcArea.aEnd.Row();
+ ScDocumentUniquePtr pMixDoc;
+ bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
+
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+
+ sc::CopyToDocContext aCxt(*this);
+ sc::MixDocContext aMixDocCxt(*this);
+
+ SCTAB nCount = GetTableCount();
+ for (const SCTAB& i : rMark)
+ {
+ if (i >= nCount)
+ break;
+ if (i != nSrcTab && maTabs[i])
+ {
+ if (bDoMix)
+ {
+ if (!pMixDoc)
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndo( *this, i, i );
+ }
+ else
+ pMixDoc->AddUndoTab( i, i );
+
+ // context used for copying content to the temporary mix document.
+ sc::CopyToDocContext aMixCxt(*pMixDoc);
+ maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ InsertDeleteFlags::CONTENTS, false, pMixDoc->maTabs[i].get(),
+ /*pMarkData*/nullptr, /*bAsLink*/false, /*bColRowFlags*/true,
+ /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+ }
+ maTabs[i]->DeleteArea( nStartCol,nStartRow, nEndCol,nEndRow, nDelFlags);
+ pSourceTable->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ nFlags, false, maTabs[i].get(), nullptr, bAsLink,
+ /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+
+ if (bDoMix)
+ maTabs[i]->MixData(aMixDocCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ nFunction, bSkipEmpty, pMixDoc->maTabs[i].get() );
+ }
+ }
+
+ SetAutoCalc( bOldAutoCalc );
+ }
+ else
+ {
+ OSL_FAIL("wrong table");
+ }
+}
+
+void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark,
+ InsertDeleteFlags nFlags, ScPasteFunc nFunction,
+ bool bSkipEmpty, bool bAsLink )
+{
+ InsertDeleteFlags nDelFlags = nFlags;
+ if (nDelFlags & InsertDeleteFlags::CONTENTS)
+ nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing!
+
+ if (ScTable* pSourceTable = FetchTable(nSrcTab))
+ {
+ ScDocumentUniquePtr pMixDoc;
+ bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
+
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+
+ const ScRange& aArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aArea.aStart.Col();
+ SCROW nStartRow = aArea.aStart.Row();
+ SCCOL nEndCol = aArea.aEnd.Col();
+ SCROW nEndRow = aArea.aEnd.Row();
+
+ sc::CopyToDocContext aCxt(*this);
+ sc::MixDocContext aMixDocCxt(*this);
+ SCTAB nCount = GetTableCount();
+ for (const SCTAB& i : rMark)
+ {
+ if (i >= nCount)
+ break;
+ if ( i != nSrcTab && maTabs[i] )
+ {
+ if (bDoMix)
+ {
+ if (!pMixDoc)
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndo( *this, i, i );
+ }
+ else
+ pMixDoc->AddUndoTab( i, i );
+
+ sc::CopyToDocContext aMixCxt(*pMixDoc);
+ maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ InsertDeleteFlags::CONTENTS, true, pMixDoc->maTabs[i].get(), &rMark,
+ /*bAsLink*/false, /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false,
+ /*bCopyCaptions*/true );
+ }
+
+ maTabs[i]->DeleteSelection( nDelFlags, rMark );
+ pSourceTable->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ nFlags, true, maTabs[i].get(), &rMark, bAsLink,
+ /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+
+ if (bDoMix)
+ maTabs[i]->MixMarked(aMixDocCxt, rMark, nFunction, bSkipEmpty, pMixDoc->maTabs[i].get());
+ }
+ }
+
+ SetAutoCalc( bOldAutoCalc );
+ }
+ else
+ {
+ OSL_FAIL("wrong table");
+ }
+}
+
+bool ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString,
+ const ScSetStringParam* pParam )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(nCol, nRow);
+ if (pCurCellFormula && pCurCellFormula->IsShared())
+ {
+ // In case setting this string affects an existing formula group, end
+ // its listening to purge then empty cell broadcasters. Affected
+ // remaining split group listeners will be set up again via
+ // ScColumn::DetachFormulaCell() and
+ // ScColumn::StartListeningUnshared().
+
+ sc::EndListeningContext aCxt(*this);
+ ScAddress aPos(nCol, nRow, nTab);
+ EndListeningIntersectedGroup(aCxt, aPos, nullptr);
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ return pTab->SetString(nCol, nRow, nTab, rString, pParam);
+}
+
+bool ScDocument::SetString(
+ const ScAddress& rPos, const OUString& rString, const ScSetStringParam* pParam )
+{
+ return SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rString, pParam);
+}
+
+bool ScDocument::SetEditText( const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->SetEditText(rPos.Col(), rPos.Row(), std::move(pEditText));
+ return false;
+}
+
+void ScDocument::SetEditText( const ScAddress& rPos, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ pTable->SetEditText(rPos.Col(), rPos.Row(), rEditText, pEditPool);
+}
+
+void ScDocument::SetEditText( const ScAddress& rPos, const OUString& rStr )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ {
+ ScFieldEditEngine& rEngine = GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ pTable->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
+ }
+}
+
+SCROW ScDocument::GetFirstEditTextRow( const ScRange& rRange ) const
+{
+ if (const ScTable* pTable = FetchTable(rRange.aStart.Tab()))
+ return pTable->GetFirstEditTextRow(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+ return -1;
+}
+
+void ScDocument::SetTextCell( const ScAddress& rPos, const OUString& rStr )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ {
+ if (ScStringUtil::isMultiline(rStr))
+ {
+ ScFieldEditEngine& rEngine = GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ pTable->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ pTable->SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rStr, &aParam);
+ }
+ }
+}
+
+void ScDocument::SetEmptyCell( const ScAddress& rPos )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ pTable->SetEmptyCell(rPos.Col(), rPos.Row());
+}
+
+void ScDocument::SetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rVal )
+{
+ SetValue(ScAddress(nCol, nRow, nTab), rVal);
+}
+
+void ScDocument::SetValue( const ScAddress& rPos, double fVal )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(rPos.Col(), rPos.Row());
+ if (pCurCellFormula && pCurCellFormula->IsShared())
+ {
+ // In case setting this value affects an existing formula group, end
+ // its listening to purge then empty cell broadcasters. Affected
+ // remaining split group listeners will be set up again via
+ // ScColumn::DetachFormulaCell() and
+ // ScColumn::StartListeningUnshared().
+
+ sc::EndListeningContext aCxt(*this);
+ EndListeningIntersectedGroup(aCxt, rPos, nullptr);
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ pTab->SetValue(rPos.Col(), rPos.Row(), fVal);
+}
+
+OUString ScDocument::GetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext* pContext ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetString(nCol, nRow, pContext);
+ return OUString();
+}
+
+OUString ScDocument::GetString( const ScAddress& rPos, const ScInterpreterContext* pContext ) const
+{
+ if (const ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetString(rPos.Col(), rPos.Row(), pContext);
+ return OUString();
+}
+
+double* ScDocument::GetValueCell( const ScAddress& rPos )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetValueCell(rPos.Col(), rPos.Row());
+ return nullptr;
+}
+
+svl::SharedString ScDocument::GetSharedString( const ScAddress& rPos ) const
+{
+ if (const ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetSharedString(rPos.Col(), rPos.Row());
+ return svl::SharedString();
+}
+
+std::shared_ptr<sc::FormulaGroupContext>& ScDocument::GetFormulaGroupContext()
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ if (!mpFormulaGroupCxt)
+ mpFormulaGroupCxt = std::make_shared<sc::FormulaGroupContext>();
+
+ return mpFormulaGroupCxt;
+}
+
+void ScDocument::DiscardFormulaGroupContext()
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ if( !mbFormulaGroupCxtBlockDiscard )
+ mpFormulaGroupCxt.reset();
+}
+
+OUString ScDocument::GetInputString(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bForceSystemLocale ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetInputString(nCol, nRow, bForceSystemLocale);
+ else
+ return OUString();
+}
+
+FormulaError ScDocument::GetStringForFormula( const ScAddress& rPos, OUString& rString )
+{
+ // Used in formulas (add-in parameters etc), so it must use the same semantics as
+ // ScInterpreter::GetCellString: always format values as numbers.
+ // The return value is the error code.
+
+ ScRefCellValue aCell(*this, rPos);
+ if (aCell.isEmpty())
+ {
+ rString.clear();
+ return FormulaError::NONE;
+ }
+
+ FormulaError nErr = FormulaError::NONE;
+ OUString aStr;
+ SvNumberFormatter* pFormatter = GetFormatTable();
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ aStr = aCell.getString(this);
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ nErr = pFCell->GetErrCode();
+ if (pFCell->IsValue())
+ {
+ double fVal = pFCell->GetValue();
+ sal_uInt32 nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ }
+ else
+ aStr = pFCell->GetString().getString();
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ double fVal = aCell.getDouble();
+ sal_uInt32 nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ }
+ break;
+ default:
+ ;
+ }
+
+ rString = aStr;
+ return nErr;
+}
+
+const EditTextObject* ScDocument::GetEditText( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetEditText(rPos.Col(), rPos.Row());
+ return nullptr;
+}
+
+void ScDocument::RemoveEditTextCharAttribs( const ScAddress& rPos, const ScPatternAttr& rAttr )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->RemoveEditTextCharAttribs(rPos.Col(), rPos.Row(), rAttr);
+}
+
+double ScDocument::GetValue( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetValue(rPos.Col(), rPos.Row());
+ return 0.0;
+}
+
+double ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ ScAddress aAdr(nCol, nRow, nTab);
+ return GetValue(aAdr);
+}
+
+sal_uInt32 ScDocument::GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNumberFormat(nCol, nRow);
+ return 0;
+}
+
+sal_uInt32 ScDocument::GetNumberFormat( const ScRange& rRange ) const
+{
+ SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+
+ if (!HasTable(nTab1) || !HasTable(nTab2))
+ return 0;
+
+ sal_uInt32 nFormat = 0;
+ bool bFirstItem = true;
+ for (SCTAB nTab = nTab1; nTab <= nTab2 && nTab < GetTableCount() ; ++nTab)
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ sal_uInt32 nThisFormat = maTabs[nTab]->GetNumberFormat(nCol, nRow1, nRow2);
+ if (bFirstItem)
+ {
+ nFormat = nThisFormat;
+ bFirstItem = false;
+ }
+ else if (nThisFormat != nFormat)
+ return 0;
+ }
+
+ return nFormat;
+}
+
+sal_uInt32 ScDocument::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNumberFormat( rContext, rPos );
+ return 0;
+}
+
+void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat )
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ SCTAB nTab = rPos.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetNumberFormat(rPos.Col(), rPos.Row(), nNumberFormat);
+}
+
+void ScDocument::GetNumberFormatInfo( const ScInterpreterContext& rContext, SvNumFormatType& nType, sal_uInt32& nIndex,
+ const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (nTab < GetTableCount() && maTabs[nTab])
+ {
+ nIndex = maTabs[nTab]->GetNumberFormat( rContext, rPos );
+ nType = rContext.GetNumberFormatType( nIndex );
+ }
+ else
+ {
+ nType = SvNumFormatType::UNDEFINED;
+ nIndex = 0;
+ }
+}
+
+OUString ScDocument::GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetFormula(nCol, nRow);
+
+ return OUString();
+}
+
+const ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos ) const
+{
+ if (const ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetFormulaCell(rPos.Col(), rPos.Row());
+ return nullptr;
+}
+
+ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos )
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetFormulaCell(rPos.Col(), rPos.Row());
+ return nullptr;
+}
+
+CellType ScDocument::GetCellType( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetCellType(rPos);
+ return CELLTYPE_NONE;
+}
+
+CellType ScDocument::GetCellType( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetCellType( nCol, nRow );
+ return CELLTYPE_NONE;
+}
+
+bool ScDocument::HasStringData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab) ; pTable && nCol < pTable->GetAllocatedColumnsCount())
+ return pTable->HasStringData(nCol, nRow);
+ return false;
+}
+
+bool ScDocument::HasValueData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab) ; pTable && nCol < pTable->GetAllocatedColumnsCount())
+ return pTable->HasValueData( nCol, nRow );
+ return false;
+}
+
+bool ScDocument::HasValueData( const ScAddress& rPos ) const
+{
+ return HasValueData(rPos.Col(), rPos.Row(), rPos.Tab());
+}
+
+bool ScDocument::HasStringCells( const ScRange& rRange ) const
+{
+ // true, if String- or Edit cells in range
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < GetTableCount(); nTab++)
+ {
+ if ( maTabs[nTab] && maTabs[nTab]->HasStringCells( nStartCol, nStartRow, nEndCol, nEndRow ) )
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = GetValidationEntry( nValidation );
+ if( pData && pData->HasSelectionList() )
+ return true;
+ }
+ return HasStringCells( ScRange( nCol, 0, nTab, nCol, MaxRow(), nTab ) );
+}
+
+bool ScDocument::HasValidationData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = GetValidationEntry( nValidation );
+ if( pData && pData->GetDataMode() != ScValidationMode::SC_VALID_ANY )
+ return true;
+ }
+ return false;
+}
+
+void ScDocument::CheckVectorizationState()
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple calculations
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CheckVectorizationState();
+ }
+
+ SetAutoCalc(bOldAutoCalc);
+}
+
+void ScDocument::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple calculations
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetAllFormulasDirty(rCxt);
+ }
+ }
+
+ // Although Charts are also set to dirty in Tracking without AutoCalc
+ // if all formulas are dirty, the charts can no longer be caught
+ // (#45205#) - that is why all Charts have to be explicitly handled again
+ if (pChartListenerCollection)
+ pChartListenerCollection->SetDirty();
+
+ SetAutoCalc( bOldAutoCalc );
+}
+
+void ScDocument::SetDirty( const ScRange& rRange, bool bIncludeEmptyCells )
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple calculations
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i = rRange.aStart.Tab(); i <= nTab2 && i < GetTableCount(); i++)
+ if (maTabs[i]) maTabs[i]->SetDirty( rRange,
+ (bIncludeEmptyCells ? ScColumn::BROADCAST_BROADCASTERS : ScColumn::BROADCAST_DATA_POSITIONS));
+
+ /* TODO: this now also notifies conditional formatting and does a UNO
+ * broadcast, which wasn't done here before. Is that an actually
+ * desired side effect, or should we come up with a method that
+ * doesn't? */
+ if (bIncludeEmptyCells)
+ BroadcastCells( rRange, SfxHintId::ScDataChanged, false);
+ }
+ SetAutoCalc( bOldAutoCalc );
+}
+
+void ScDocument::SetTableOpDirty( const ScRange& rRange )
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple recalculation
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i = rRange.aStart.Tab(); i <= nTab2 && i < GetTableCount(); i++)
+ if (maTabs[i]) maTabs[i]->SetTableOpDirty( rRange );
+ SetAutoCalc( bOldAutoCalc );
+}
+
+void ScDocument::InterpretDirtyCells( const ScRangeList& rRanges )
+{
+ if (!GetAutoCalc())
+ return;
+
+ PrepareFormulaCalc();
+
+ for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
+ {
+ const ScRange& rRange = rRanges[nPos];
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->InterpretDirtyCells(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+ }
+ }
+
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpFormulaGroupCxt.reset();
+}
+
+bool ScDocument::InterpretCellsIfNeeded( const ScRangeList& rRanges )
+{
+ bool allInterpreted = true;
+ for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
+ {
+ const ScRange& rRange = rRanges[nPos];
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ break;
+
+ if( !pTab->InterpretCellsIfNeeded(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()))
+ {
+ allInterpreted = false;
+ }
+ }
+ }
+ return allInterpreted;
+}
+
+void ScDocument::AddTableOpFormulaCell( ScFormulaCell* pCell )
+{
+ if (m_TableOpList.empty())
+ return;
+
+ ScInterpreterTableOpParams *const p = m_TableOpList.back();
+ if ( p->bCollectNotifications )
+ {
+ if ( p->bRefresh )
+ { // refresh pointers only
+ p->aNotifiedFormulaCells.push_back( pCell );
+ }
+ else
+ { // init both, address and pointer
+ p->aNotifiedFormulaCells.push_back( pCell );
+ p->aNotifiedFormulaPos.push_back( pCell->aPos );
+ }
+ }
+}
+
+void ScDocument::CalcAll()
+{
+ PrepareFormulaCalc();
+ ClearLookupCaches(); // Ensure we don't deliver zombie data.
+ sc::AutoCalcSwitch aSwitch(*this, true);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyVar();
+ }
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CalcAll();
+ }
+ ClearFormulaTree();
+
+ // In eternal hard recalc state caches were not added as listeners,
+ // invalidate them so the next non-CalcAll() normal lookup will not be
+ // presented with outdated data.
+ if (GetHardRecalcState() == HardRecalcState::ETERNAL)
+ ClearLookupCaches();
+}
+
+void ScDocument::CompileAll()
+{
+ sc::CompileFormulaContext aCxt(*this);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CompileAll(aCxt);
+ }
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+}
+
+void ScDocument::CompileXML()
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false );
+ ScProgress aProgress( GetDocumentShell(), ScResId(
+ STR_PROGRESS_CALCULATING ), GetXMLImportedFormulaCount(), true );
+
+ sc::CompileFormulaContext aCxt(*this);
+
+ // set AutoNameCache to speed up automatic name lookup
+ OSL_ENSURE( !pAutoNameCache, "AutoNameCache already set" );
+ pAutoNameCache.reset( new ScAutoNameCache( *this ) );
+
+ if (pRangeName)
+ pRangeName->CompileUnresolvedXML(aCxt);
+
+ std::for_each(maTabs.begin(), maTabs.end(),
+ [&](ScTableUniquePtr & pTab)
+ {
+ if (pTab)
+ pTab->CompileXML(aCxt, aProgress);
+ }
+ );
+ StartAllListeners();
+
+ pAutoNameCache.reset(); // valid only during CompileXML, where cell contents don't change
+
+ if ( pValidationList )
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->CompileXML();
+ }
+
+ // Track all formula cells that were appended to the FormulaTrack during
+ // import or CompileXML().
+ TrackFormulas();
+
+ SetAutoCalc( bOldAutoCalc );
+}
+
+bool ScDocument::CompileErrorCells(FormulaError nErrCode)
+{
+ bool bCompiled = false;
+ sc::CompileFormulaContext aCxt(*this);
+ for (const auto& pTab : maTabs)
+ {
+ if (pTab && pTab->CompileErrorCells(aCxt, nErrCode))
+ bCompiled = true;
+ }
+
+ return bCompiled;
+}
+
+void ScDocument::CalcAfterLoad( bool bStartListening )
+{
+ if (bIsClip) // Excel data is loaded from the Clipboard to a Clip-Doc
+ return; // the calculation is then only performed when inserting into the real document
+
+ bCalcingAfterLoad = true;
+ sc::CompileFormulaContext aCxt(*this);
+ {
+ for (const auto& pTable : maTabs)
+ {
+ if (pTable)
+ pTable->CalcAfterLoad(aCxt, bStartListening);
+ }
+ for (const auto& pTable : maTabs)
+ {
+ if (pTable)
+ pTable->SetDirtyAfterLoad();
+ }
+ }
+ bCalcingAfterLoad = false;
+
+ SetDetectiveDirty(false); // No real changes yet
+
+ // #i112436# If formula cells are already dirty, they don't broadcast further changes.
+ // So the source ranges of charts must be interpreted even if they are not visible,
+ // similar to ScMyShapeResizer::CreateChartListener for loading own files (i104899).
+ if (pChartListenerCollection)
+ {
+ const ScChartListenerCollection::ListenersType& rListeners = pChartListenerCollection->getListeners();
+ for (auto const& it : rListeners)
+ {
+ const ScChartListener *const p = it.second.get();
+ InterpretDirtyCells(*p->GetRangeList());
+ }
+ }
+}
+
+FormulaError ScDocument::GetErrCode( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetErrCode( rPos );
+ return FormulaError::NONE;
+}
+
+void ScDocument::ResetChanged( const ScRange& rRange )
+{
+ SCTAB nTabSize = GetTableCount();
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB nTab = nTab1; nTab1 <= nTab2 && nTab < nTabSize; ++nTab)
+ if (maTabs[nTab])
+ maTabs[nTab]->ResetChanged(rRange);
+}
+
+// Column widths / Row heights --------------------------------------
+
+void ScDocument::SetColWidth( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetColWidth(nCol, nNewWidth);
+}
+
+void ScDocument::SetColWidthOnly( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetColWidthOnly(nCol, nNewWidth);
+}
+
+void ScDocument::SetRowHeight( SCROW nRow, SCTAB nTab, sal_uInt16 nNewHeight )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowHeight(nRow, nNewHeight);
+}
+
+void ScDocument::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowHeightRange(nStartRow, nEndRow, nNewHeight, 1.0, true);
+}
+
+void ScDocument::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowHeightOnly( nStartRow, nEndRow, nNewHeight );
+}
+
+void ScDocument::SetManualHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bManual )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetManualHeight( nStartRow, nEndRow, bManual );
+}
+
+sal_uInt16 ScDocument::GetColWidth( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetColWidth( nCol, bHiddenAsZero );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetColWidth( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetColWidth(nStartCol, nEndCol);
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetOriginalWidth( SCCOL nCol, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetOriginalWidth( nCol );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetCommonWidth( SCCOL nEndCol, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetCommonWidth( nEndCol );
+ OSL_FAIL("Wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetOriginalHeight( SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetOriginalHeight( nRow );
+ OSL_FAIL("Wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRowHeight( nRow, nullptr, nullptr, bHiddenAsZero );
+ OSL_FAIL("Wrong sheet number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, SCROW* pStartRow, SCROW* pEndRow, bool bHiddenAsZero ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRowHeight( nRow, pStartRow, pEndRow, bHiddenAsZero );
+ OSL_FAIL("Wrong sheet number");
+ return 0;
+}
+
+tools::Long ScDocument::GetRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (nStartRow == nEndRow)
+ return GetRowHeight( nStartRow, nTab, bHiddenAsZero ); // faster for a single row
+
+ // check bounds because this method replaces former for(i=start;i<=end;++i) loops
+ if (nStartRow > nEndRow)
+ return 0;
+
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRowHeight( nStartRow, nEndRow, bHiddenAsZero );
+
+ OSL_FAIL("wrong sheet number");
+ return 0;
+}
+
+SCROW ScDocument::GetRowForHeight( SCTAB nTab, tools::Long nHeight ) const
+{
+ return maTabs[nTab]->GetRowForHeight(nHeight);
+}
+
+tools::Long ScDocument::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow,
+ SCTAB nTab, double fScale ) const
+{
+ // faster for a single row
+ if (nStartRow == nEndRow)
+ return static_cast<tools::Long>(GetRowHeight( nStartRow, nTab) * fScale);
+
+ // check bounds because this method replaces former for(i=start;i<=end;++i) loops
+ if (nStartRow > nEndRow)
+ return 0;
+
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetScaledRowHeight( nStartRow, nEndRow, fScale);
+
+ OSL_FAIL("wrong sheet number");
+ return 0;
+}
+
+SCROW ScDocument::GetHiddenRowCount( SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetHiddenRowCount( nRow );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetColOffset( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetColOffset( nCol, bHiddenAsZero );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetRowOffset( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRowOffset( nRow, bHiddenAsZero );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bFormula, const ScMarkData* pMarkData,
+ const ScColWidthParam* pParam )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetOptimalColWidth(nCol, pDev, nPPTX, nPPTY, rZoomX,
+ rZoomY, bFormula, pMarkData, pParam);
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetNeededSize( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bWidth, bool bTotalSize, bool bInPrintTwips )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNeededSize(nCol, nRow, pDev, nPPTX, nPPTY,
+ rZoomX, rZoomY, bWidth, bTotalSize,
+ bInPrintTwips);
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+bool ScDocument::SetOptimalHeight( sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bApi )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->SetOptimalHeight(rCxt, nStartRow, nEndRow, bApi);
+ return false;
+}
+
+void ScDocument::UpdateAllRowHeights( sc::RowHeightContext& rCxt, const ScMarkData* pTabMark )
+{
+ // one progress across all (selected) sheets
+
+ sal_uInt64 nCellCount = 0;
+ for (SCTAB nTab = 0; nTab < GetTableCount(); nTab++)
+ if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
+ nCellCount += maTabs[nTab]->GetWeightedCount();
+
+ ScProgress aProgress( GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true );
+
+ sal_uInt64 nProgressStart = 0;
+ for (SCTAB nTab = 0; nTab < GetTableCount(); nTab++)
+ if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
+ {
+ maTabs[nTab]->SetOptimalHeightOnly(rCxt, 0, MaxRow(), &aProgress, nProgressStart);
+ maTabs[nTab]->SetDrawPageSize();
+ nProgressStart += maTabs[nTab]->GetWeightedCount();
+ }
+}
+
+// Column/Row - Flags ----------------------------------------------
+
+void ScDocument::ShowCol(SCCOL nCol, SCTAB nTab, bool bShow)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ShowCol(nCol, bShow);
+}
+
+void ScDocument::ShowRow(SCROW nRow, SCTAB nTab, bool bShow)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ShowRow(nRow, bShow);
+}
+
+void ScDocument::ShowRows(SCROW nRow1, SCROW nRow2, SCTAB nTab, bool bShow)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ShowRows( nRow1, nRow2, bShow );
+}
+
+void ScDocument::SetRowFlags( SCROW nRow, SCTAB nTab, CRFlags nNewFlags )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowFlags( nRow, nNewFlags );
+}
+
+void ScDocument::SetRowFlags( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, CRFlags nNewFlags )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowFlags( nStartRow, nEndRow, nNewFlags );
+}
+
+CRFlags ScDocument::GetColFlags( SCCOL nCol, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetColFlags( nCol );
+ OSL_FAIL("wrong table number");
+ return CRFlags::NONE;
+}
+
+CRFlags ScDocument::GetRowFlags( SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRowFlags( nRow );
+ OSL_FAIL("wrong table number");
+ return CRFlags::NONE;
+}
+
+void ScDocument::GetAllRowBreaks(set<SCROW>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->GetAllRowBreaks(rBreaks, bPage, bManual);
+}
+
+void ScDocument::GetAllColBreaks(set<SCCOL>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->GetAllColBreaks(rBreaks, bPage, bManual);
+}
+
+ScBreakType ScDocument::HasRowBreak(SCROW nRow, SCTAB nTab) const
+{
+ ScBreakType nType = ScBreakType::NONE;
+ if (const ScTable* pTable = FetchTable(nTab); pTable && ValidRow(nRow))
+ {
+ if (pTable->HasRowPageBreak(nRow))
+ nType |= ScBreakType::Page;
+
+ if (pTable->HasRowManualBreak(nRow))
+ nType |= ScBreakType::Manual;
+ }
+ return nType;
+}
+
+ScBreakType ScDocument::HasColBreak(SCCOL nCol, SCTAB nTab) const
+{
+ ScBreakType nType = ScBreakType::NONE;
+
+ if (const ScTable* pTable = FetchTable(nTab); pTable && ValidCol(nCol))
+ {
+ if (pTable->HasColPageBreak(nCol))
+ nType |= ScBreakType::Page;
+
+ if (pTable->HasColManualBreak(nCol))
+ nType |= ScBreakType::Manual;
+ }
+ return nType;
+}
+
+void ScDocument::SetRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (ScTable* pTable = FetchTable(nTab); pTable && ValidRow(nRow))
+ pTable->SetRowBreak(nRow, bPage, bManual);
+}
+
+void ScDocument::SetColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (ScTable* pTable = FetchTable(nTab); pTable && ValidCol(nCol))
+ pTable->SetColBreak(nCol, bPage, bManual);
+}
+
+void ScDocument::RemoveRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (ScTable* pTable = FetchTable(nTab); pTable && ValidRow(nRow))
+ pTable->RemoveRowBreak(nRow, bPage, bManual);
+}
+
+void ScDocument::RemoveColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (ScTable* pTable = FetchTable(nTab); pTable && ValidCol(nCol))
+ pTable->RemoveColBreak(nCol, bPage, bManual);
+}
+
+Sequence<TablePageBreakData> ScDocument::GetRowBreakData(SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRowBreakData();
+
+ return Sequence<TablePageBreakData>();
+}
+
+bool ScDocument::RowHidden(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->RowHidden(nRow, pFirstRow, pLastRow);
+ return false;
+}
+
+bool ScDocument::HasHiddenRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->HasHiddenRows(nStartRow, nEndRow);
+ return false;
+}
+
+bool ScDocument::ColHidden(SCCOL nCol, SCTAB nTab, SCCOL* pFirstCol, SCCOL* pLastCol) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->ColHidden(nCol, pFirstCol, pLastCol);
+
+ if (pFirstCol)
+ *pFirstCol = nCol;
+ if (pLastCol)
+ *pLastCol = nCol;
+ return false;
+}
+
+void ScDocument::SetRowHidden(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHidden)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowHidden(nStartRow, nEndRow, bHidden);
+}
+
+void ScDocument::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bHidden)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetColHidden(nStartCol, nEndCol, bHidden);
+}
+
+SCROW ScDocument::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->FirstVisibleRow(nStartRow, nEndRow);
+ return 0;
+}
+
+SCROW ScDocument::LastVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->LastVisibleRow(nStartRow, nEndRow);
+ return ::std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScDocument::CountVisibleRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->CountVisibleRows(nStartRow, nEndRow);
+ return 0;
+}
+
+bool ScDocument::RowFiltered(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->RowFiltered(nRow, pFirstRow, pLastRow);
+ return false;
+}
+
+bool ScDocument::HasFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->HasFilteredRows(nStartRow, nEndRow);
+ return false;
+}
+
+bool ScDocument::ColFiltered(SCCOL nCol, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->ColFiltered(nCol);
+ return false;
+}
+
+void ScDocument::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bFiltered)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRowFiltered(nStartRow, nEndRow, bFiltered);
+}
+
+SCROW ScDocument::FirstNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->FirstNonFilteredRow(nStartRow, nEndRow);
+ return std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScDocument::LastNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->LastNonFilteredRow(nStartRow, nEndRow);
+ return std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScDocument::CountNonFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->CountNonFilteredRows(nStartRow, nEndRow);
+ return 0;
+}
+
+bool ScDocument::IsManualRowHeight(SCROW nRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsManualRowHeight(nRow);
+ return false;
+}
+
+void ScDocument::SyncColRowFlags()
+{
+ for (const auto& pTable : maTabs)
+ {
+ if (pTable)
+ pTable->SyncColRowFlags();
+ }
+}
+
+SCROW ScDocument::GetLastFlaggedRow( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLastFlaggedRow();
+ return 0;
+}
+
+SCCOL ScDocument::GetLastChangedColFlagsWidth( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLastChangedColFlagsWidth();
+ return 0;
+}
+
+SCROW ScDocument::GetLastChangedRowFlagsWidth( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetLastChangedRowFlagsWidth();
+ return 0;
+}
+
+SCCOL ScDocument::GetNextDifferentChangedColFlagsWidth( SCTAB nTab, SCCOL nStart) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ CRFlags nStartFlags = pTable->GetColFlags(nStart);
+ sal_uInt16 nStartWidth = pTable->GetOriginalWidth(nStart);
+ for (SCCOL nCol : pTable->GetColumnsRange( nStart + 1, MaxCol()))
+ {
+ if (((nStartFlags & CRFlags::ManualBreak) != (pTable->GetColFlags(nCol) & CRFlags::ManualBreak)) ||
+ (nStartWidth != pTable->GetOriginalWidth(nCol)) ||
+ ((nStartFlags & CRFlags::Hidden) != (pTable->GetColFlags(nCol) & CRFlags::Hidden)) )
+ {
+ return nCol;
+ }
+ }
+ return MaxCol()+1;
+ }
+ return 0;
+}
+
+SCROW ScDocument::GetNextDifferentChangedRowFlagsWidth( SCTAB nTab, SCROW nStart) const
+{
+ const ScTable* pTable = FetchTable(nTab);
+ if (!pTable)
+ return 0;
+
+ const ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlagsArray = pTable->GetRowFlagsArray();
+ if (!pRowFlagsArray)
+ return 0;
+
+ if (!pTable->mpRowHeights || !pTable->mpHiddenRows)
+ return 0;
+
+ size_t nIndex; // ignored
+ SCROW nFlagsEndRow;
+ SCROW nHiddenEndRow;
+ SCROW nHeightEndRow;
+ CRFlags nFlags;
+ bool bHidden;
+ sal_uInt16 nHeight;
+ CRFlags nStartFlags = nFlags = pRowFlagsArray->GetValue( nStart, nIndex, nFlagsEndRow);
+ bool bStartHidden = bHidden = pTable->RowHidden( nStart, nullptr, &nHiddenEndRow);
+ sal_uInt16 nStartHeight = nHeight = pTable->GetRowHeight( nStart, nullptr, &nHeightEndRow, false);
+ SCROW nRow;
+ while ((nRow = std::min( nHiddenEndRow, std::min( nFlagsEndRow, nHeightEndRow)) + 1) <= MaxRow())
+ {
+ if (nFlagsEndRow < nRow)
+ nFlags = pRowFlagsArray->GetValue( nRow, nIndex, nFlagsEndRow);
+ if (nHiddenEndRow < nRow)
+ bHidden = pTable->RowHidden( nRow, nullptr, &nHiddenEndRow);
+ if (nHeightEndRow < nRow)
+ nHeight = pTable->GetRowHeight( nRow, nullptr, &nHeightEndRow, false);
+
+ if (((nStartFlags & CRFlags::ManualBreak) != (nFlags & CRFlags::ManualBreak)) ||
+ ((nStartFlags & CRFlags::ManualSize) != (nFlags & CRFlags::ManualSize)) ||
+ (bStartHidden != bHidden) ||
+ (nStartHeight != nHeight))
+ return nRow;
+ }
+
+ return MaxRow()+1;
+}
+
+void ScDocument::GetColDefault( SCTAB nTab, SCCOL nCol, SCROW nLastRow, SCROW& nDefault)
+{
+ nDefault = 0;
+ ScDocAttrIterator aDocAttrItr(*this, nTab, nCol, 0, nCol, nLastRow);
+ SCCOL nColumn;
+ SCROW nStartRow;
+ SCROW nEndRow;
+ const ScPatternAttr* pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
+ if (nEndRow >= nLastRow)
+ return;
+
+ ScDefaultAttrSet aSet;
+ ScDefaultAttrSet::iterator aItr = aSet.end();
+ while (pAttr)
+ {
+ ScDefaultAttr aAttr(pAttr);
+ aItr = aSet.find(aAttr);
+ if (aItr == aSet.end())
+ {
+ aAttr.nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+ aAttr.nFirst = nStartRow;
+ aSet.insert(aAttr);
+ }
+ else
+ {
+ aAttr.nCount = aItr->nCount + static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+ aAttr.nFirst = aItr->nFirst;
+ aSet.erase(aItr);
+ aSet.insert(aAttr);
+ }
+ pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
+ }
+ ScDefaultAttrSet::iterator aDefaultItr = aSet.begin();
+ aItr = aDefaultItr;
+ ++aItr;
+ while (aItr != aSet.end())
+ {
+ // for entries with equal count, use the one with the lowest start row,
+ // don't use the random order of pointer comparisons
+ if ( aItr->nCount > aDefaultItr->nCount ||
+ ( aItr->nCount == aDefaultItr->nCount && aItr->nFirst < aDefaultItr->nFirst ) )
+ aDefaultItr = aItr;
+ ++aItr;
+ }
+ nDefault = aDefaultItr->nFirst;
+}
+
+void ScDocument::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->StripHidden( rX1, rY1, rX2, rY2 );
+}
+
+void ScDocument::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ExtendHidden( rX1, rY1, rX2, rY2 );
+}
+
+// Attribute ----------------------------------------------------------
+
+const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ const SfxPoolItem* pTemp = pTable->GetAttr( nCol, nRow, nWhich );
+ if (pTemp)
+ return pTemp;
+ else
+ {
+ OSL_FAIL( "Attribute Null" );
+ }
+ }
+ return &mxPoolHelper->GetDocPool()->GetDefaultItem( nWhich );
+}
+
+const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich, SCROW& nStartRow, SCROW& nEndRow ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ const SfxPoolItem* pTemp = pTable->GetAttr( nCol, nRow, nWhich, nStartRow, nEndRow );
+ if (pTemp)
+ return pTemp;
+ else
+ {
+ OSL_FAIL( "Attribute Null" );
+ }
+ }
+ return &mxPoolHelper->GetDocPool()->GetDefaultItem( nWhich );
+}
+
+const SfxPoolItem* ScDocument::GetAttr( const ScAddress& rPos, sal_uInt16 nWhich ) const
+{
+ return GetAttr(rPos.Col(), rPos.Row(), rPos.Tab(), nWhich);
+}
+
+const ScPatternAttr* ScDocument::GetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPattern( nCol, nRow );
+ return nullptr;
+}
+
+const ScPatternAttr* ScDocument::GetPattern( const ScAddress& rPos ) const
+{
+ if (const ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->GetPattern(rPos.Col(), rPos.Row());
+
+ return nullptr;
+}
+
+const ScPatternAttr* ScDocument::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetMostUsedPattern(nCol, nStartRow, nEndRow);
+ return nullptr;
+}
+
+void ScDocument::ApplyAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem& rAttr )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ApplyAttr( nCol, nRow, rAttr );
+}
+
+void ScDocument::ApplyPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ApplyPattern(nCol, nRow, rAttr);
+}
+
+void ScDocument::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark,
+ const ScPatternAttr& rAttr,
+ ScEditDataArray* pDataArray,
+ bool* const pIsChanged )
+{
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr, pDataArray, pIsChanged );
+ }
+}
+
+void ScDocument::ApplyPatternAreaTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScPatternAttr& rAttr )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ApplyPatternArea(nStartCol, nStartRow, nEndCol, nEndRow, rAttr);
+}
+
+void ScDocument::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
+ const ScMarkData& rMark, const ScPatternAttr& rPattern, SvNumFormatType nNewType )
+{
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType );
+ }
+}
+
+void ScDocument::AddCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->AddCondFormatData(rRange, nIndex);
+}
+
+void ScDocument::RemoveCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->RemoveCondFormatData(rRange, nIndex);
+}
+
+void ScDocument::ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScStyleSheet& rStyle)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ApplyStyle(nCol, nRow, &rStyle);
+}
+
+void ScDocument::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark,
+ const ScStyleSheet& rStyle)
+{
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle );
+ }
+}
+
+void ScDocument::ApplyStyleAreaTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScStyleSheet& rStyle)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle );
+}
+
+void ScDocument::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
+{
+ // ApplySelectionStyle needs multi mark
+ if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ ApplyStyleArea( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rStyle );
+ }
+ else
+ {
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if ( maTabs[rTab] )
+ maTabs[rTab]->ApplySelectionStyle( rStyle, rMark );
+ }
+ }
+}
+
+void ScDocument::ApplySelectionLineStyle( const ScMarkData& rMark,
+ const SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplySelectionLineStyle( rMark, pLine, bColorOnly );
+ }
+}
+
+const ScStyleSheet* ScDocument::GetStyle( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetStyle(nCol, nRow);
+ return nullptr;
+}
+
+const ScStyleSheet* ScDocument::GetSelectionStyle( const ScMarkData& rMark ) const
+{
+ bool bEqual = true;
+ bool bFound;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ {
+ pNewStyle = maTabs[rTab]->GetSelectionStyle( rMark, bFound );
+ if (bFound)
+ {
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // different
+ pStyle = pNewStyle;
+ }
+ }
+ }
+ }
+ if ( rMark.IsMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ for (SCTAB i = aRange.aStart.Tab(); i <= aRange.aEnd.Tab() && bEqual && i < GetTableCount(); i++)
+ if (maTabs[i] && rMark.GetTableSelect(i))
+ {
+ pNewStyle = maTabs[i]->GetAreaStyle( bFound,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ if (bFound)
+ {
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // different
+ pStyle = pNewStyle;
+ }
+ }
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+void ScDocument::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved,
+ OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY )
+{
+ for (const auto& rTab : maTabs)
+ {
+ if (rTab)
+ {
+ rTab->StyleSheetChanged(pStyleSheet, bRemoved, pDev, nPPTX, nPPTY, rZoomX, rZoomY);
+ }
+ }
+}
+
+bool ScDocument::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
+{
+ if ( bStyleSheetUsageInvalid || rStyle.GetUsage() == ScStyleSheet::Usage::UNKNOWN )
+ {
+ SfxStyleSheetIterator aIter( mxPoolHelper->GetStylePool(),
+ SfxStyleFamily::Para );
+ for ( const SfxStyleSheetBase* pStyle = aIter.First(); pStyle;
+ pStyle = aIter.Next() )
+ {
+ if (pStyle->isScStyleSheet())
+ {
+ const ScStyleSheet* pScStyle = static_cast<const ScStyleSheet*>( pStyle );
+ pScStyle->SetUsage( ScStyleSheet::Usage::NOTUSED );
+ }
+ }
+
+ bool bIsUsed = false;
+
+ for (const auto& pTable : maTabs)
+ {
+ if (pTable && pTable->IsStyleSheetUsed(rStyle))
+ bIsUsed = true;
+ }
+
+ bStyleSheetUsageInvalid = false;
+
+ return bIsUsed;
+ }
+
+ return rStyle.GetUsage() == ScStyleSheet::Usage::USED;
+}
+
+bool ScDocument::ApplyFlagsTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->ApplyFlags(nStartCol, nStartRow, nEndCol, nEndRow, nFlags);
+
+ OSL_FAIL("ApplyFlags: wrong table");
+ return false;
+}
+
+bool ScDocument::RemoveFlagsTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->RemoveFlags(nStartCol, nStartRow, nEndCol, nEndRow, nFlags);
+
+ OSL_FAIL("RemoveFlags: wrong table");
+ return false;
+}
+
+const ScPatternAttr* ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, std::unique_ptr<ScPatternAttr> pAttr )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->SetPattern(nCol, nRow, std::move(pAttr));
+ return nullptr;
+}
+
+const ScPatternAttr* ScDocument::SetPattern( const ScAddress& rPos, std::unique_ptr<ScPatternAttr> pAttr )
+{
+ return SetPattern(rPos.Col(), rPos.Row(), rPos.Tab(), std::move(pAttr));
+}
+
+void ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetPattern(nCol, nRow, rAttr);
+}
+
+void ScDocument::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr )
+{
+ SCTAB nTab = rPos.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetPattern(rPos, rAttr);
+}
+
+std::unique_ptr<ScPatternAttr> ScDocument::CreateSelectionPattern( const ScMarkData& rMark, bool bDeep )
+{
+ ScMergePatternState aState;
+
+ if ( rMark.IsMultiMarked() ) // multi selection
+ {
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->MergeSelectionPattern( aState, rMark, bDeep );
+ }
+ }
+ if ( rMark.IsMarked() ) // single selection
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->MergePatternArea( aState,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), bDeep );
+ }
+ }
+
+ OSL_ENSURE( aState.pItemSet, "SelectionPattern Null" );
+ if (aState.pItemSet)
+ {
+ std::unique_ptr<ScPatternAttr> pPattern(new ScPatternAttr( std::move(*aState.pItemSet) ));
+ if (aState.mbValidPatternId)
+ pPattern->SetPAKey(aState.mnPatternId);
+
+ return pPattern;
+ }
+ else
+ return std::unique_ptr<ScPatternAttr>(new ScPatternAttr( GetPool() )); // empty
+}
+
+const ScPatternAttr* ScDocument::GetSelectionPattern( const ScMarkData& rMark )
+{
+ pSelectionAttr = CreateSelectionPattern( rMark );
+ return pSelectionAttr.get();
+}
+
+void ScDocument::GetSelectionFrame( const ScMarkData& rMark,
+ SvxBoxItem& rLineOuter,
+ SvxBoxInfoItem& rLineInner )
+{
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::TOP);
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::BOTTOM);
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::LEFT);
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::RIGHT);
+ rLineOuter.SetAllDistances(0);
+
+ rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::HORI);
+ rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::VERT);
+ rLineInner.SetTable(true);
+ rLineInner.SetDist(true);
+ rLineInner.SetMinDist(false);
+
+ ScLineFlags aFlags;
+
+ if( rMark.IsMultiMarked() )
+ {
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false );
+ size_t nRangeCount = aRangeList.size();
+ bool bMultipleRows = false, bMultipleCols = false;
+ for( size_t nRangeIdx = 0; nRangeIdx < nRangeCount; ++nRangeIdx )
+ {
+ const ScRange & rRange = aRangeList[ nRangeIdx ];
+ bMultipleRows = ( bMultipleRows || ( rRange.aStart.Row() != rRange.aEnd.Row() ) );
+ bMultipleCols = ( bMultipleCols || ( rRange.aStart.Col() != rRange.aEnd.Col() ) );
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ }
+ rLineInner.EnableHor( bMultipleRows );
+ rLineInner.EnableVer( bMultipleCols );
+ }
+ else if( rMark.IsMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ rLineInner.EnableHor( aRange.aStart.Row() != aRange.aEnd.Row() );
+ rLineInner.EnableVer( aRange.aStart.Col() != aRange.aEnd.Col() );
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ }
+ }
+
+ // Evaluate don't care Status
+
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, ( aFlags.nLeft != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, ( aFlags.nRight != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::TOP, ( aFlags.nTop != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, ( aFlags.nBottom != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::HORI, ( aFlags.nHori != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::VERT, ( aFlags.nVert != SC_LINE_DONTCARE ) );
+}
+
+static HasAttrFlags OptimizeHasAttrib( HasAttrFlags nMask, const ScDocumentPool* pPool )
+{
+ if ( nMask & HasAttrFlags::Rotate )
+ {
+ // Is attribute used in document?
+ // (as in fillinfo)
+
+ bool bAnyItem = false;
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_ROTATE_VALUE))
+ {
+ // 90 or 270 degrees is former SvxOrientationItem - only look for other values
+ // (see ScPatternAttr::GetCellOrientation)
+ Degree100 nAngle = static_cast<const ScRotateValueItem*>(pItem)->GetValue();
+ if ( nAngle && nAngle != 9000_deg100 && nAngle != 27000_deg100 )
+ {
+ bAnyItem = true;
+ break;
+ }
+ }
+ if (!bAnyItem)
+ nMask &= ~HasAttrFlags::Rotate;
+ }
+ return nMask;
+}
+
+bool ScDocument::HasAttrib( SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, HasAttrFlags nMask ) const
+{
+ nMask = OptimizeHasAttrib( nMask, mxPoolHelper->GetDocPool());
+
+ if (nMask == HasAttrFlags::NONE)
+ return false;
+
+ for (SCTAB i = nTab1; i <= nTab2 && i < GetTableCount(); i++)
+ if (maTabs[i])
+ {
+ if ( nMask & HasAttrFlags::RightOrCenter )
+ {
+ // On a RTL sheet, don't start to look for the default left value
+ // (which is then logically right), instead always assume true.
+ // That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets.
+
+ if ( IsLayoutRTL(i) )
+ return true;
+ }
+
+ if( maTabs[i]->HasAttrib( nCol1, nRow1, nCol2, nRow2, nMask ))
+ return true;
+ }
+
+ return false;
+}
+
+bool ScDocument::HasAttrib( SCCOL nCol, SCROW nRow, SCTAB nTab, HasAttrFlags nMask, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ nMask = OptimizeHasAttrib( nMask, mxPoolHelper->GetDocPool());
+
+ if (nMask == HasAttrFlags::NONE || nTab >= GetTableCount())
+ {
+ if( nStartRow )
+ *nStartRow = 0;
+ if( nEndRow )
+ *nEndRow = MaxRow();
+ return false;
+ }
+
+ if ( nMask & HasAttrFlags::RightOrCenter )
+ {
+ // On a RTL sheet, don't start to look for the default left value
+ // (which is then logically right), instead always assume true.
+ // That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets.
+
+ if ( IsLayoutRTL(nTab) )
+ {
+ if( nStartRow )
+ *nStartRow = 0;
+ if( nEndRow )
+ *nEndRow = MaxRow();
+ return true;
+ }
+ }
+
+ return maTabs[nTab]->HasAttrib( nCol, nRow, nMask, nStartRow, nEndRow );
+}
+
+bool ScDocument::HasAttrib( const ScRange& rRange, HasAttrFlags nMask ) const
+{
+ return HasAttrib( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ nMask );
+}
+
+void ScDocument::FindMaxRotCol( SCTAB nTab, RowInfo* pRowInfo, SCSIZE nArrCount,
+ SCCOL nX1, SCCOL nX2 ) const
+{
+ if (HasTable(nTab))
+ {
+ maTabs[nTab]->FindMaxRotCol(pRowInfo, nArrCount, nX1, nX2);
+ return;
+ }
+ OSL_FAIL("FindMaxRotCol: wrong table");
+}
+
+void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const SvxBorderLine** ppLeft, const SvxBorderLine** ppTop,
+ const SvxBorderLine** ppRight, const SvxBorderLine** ppBottom ) const
+{
+ //TODO: consider page limits for printing !!!!!
+
+ const SvxBoxItem* pThisAttr = GetEffItem( nCol, nRow, nTab, ATTR_BORDER );
+ OSL_ENSURE(pThisAttr,"where is the attribute?");
+
+ const SvxBorderLine* pLeftLine = pThisAttr->GetLeft();
+ const SvxBorderLine* pTopLine = pThisAttr->GetTop();
+ const SvxBorderLine* pRightLine = pThisAttr->GetRight();
+ const SvxBorderLine* pBottomLine = pThisAttr->GetBottom();
+
+ if ( nCol > 0 )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol-1, nRow, nTab, ATTR_BORDER )->GetRight();
+ if ( ScHasPriority( pOther, pLeftLine ) )
+ pLeftLine = pOther;
+ }
+ if ( nRow > 0 )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol, nRow-1, nTab, ATTR_BORDER )->GetBottom();
+ if ( ScHasPriority( pOther, pTopLine ) )
+ pTopLine = pOther;
+ }
+ if ( nCol < MaxCol() )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol+1, nRow, nTab, ATTR_BORDER )->GetLeft();
+ if ( ScHasPriority( pOther, pRightLine ) )
+ pRightLine = pOther;
+ }
+ if ( nRow < MaxRow() )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol, nRow+1, nTab, ATTR_BORDER )->GetTop();
+ if ( ScHasPriority( pOther, pBottomLine ) )
+ pBottomLine = pOther;
+ }
+
+ if (ppLeft)
+ *ppLeft = pLeftLine;
+ if (ppTop)
+ *ppTop = pTopLine;
+ if (ppRight)
+ *ppRight = pRightLine;
+ if (ppBottom)
+ *ppBottom = pBottomLine;
+}
+
+bool ScDocument::IsBlockEmpty(SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab) const
+{
+ if (HasTable(nTab))
+ return maTabs[nTab]->IsBlockEmpty(nStartCol, nStartRow, nEndCol, nEndRow);
+ OSL_FAIL("wrong table number");
+ return false;
+}
+
+void ScDocument::LockTable(SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->LockTable();
+ else
+ {
+ OSL_FAIL("wrong table number");
+ }
+}
+
+void ScDocument::UnlockTable(SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->UnlockTable();
+ else
+ {
+ OSL_FAIL("wrong table number");
+ }
+}
+
+bool ScDocument::IsBlockEditable( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ bool* pOnlyNotBecauseOfMatrix /* = NULL */,
+ bool bNoMatrixAtAll ) const
+{
+ // import into read-only document is possible
+ if (!bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly())
+ {
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+ }
+
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsBlockEditable(nStartCol, nStartRow, nEndCol, nEndRow,
+ pOnlyNotBecauseOfMatrix, bNoMatrixAtAll);
+
+ OSL_FAIL("wrong table number");
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+}
+
+bool ScDocument::IsSelectionEditable( const ScMarkData& rMark,
+ bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const
+{
+ // import into read-only document is possible
+ if ( !bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly() )
+ {
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+ }
+
+ const ScRange& aRange = rMark.GetMarkArea();
+
+ bool bOk = true;
+ bool bMatrix = ( pOnlyNotBecauseOfMatrix != nullptr );
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if ( maTabs[rTab] )
+ {
+ if (rMark.IsMarked())
+ {
+ if ( !maTabs[rTab]->IsBlockEditable( aRange.aStart.Col(),
+ aRange.aStart.Row(), aRange.aEnd.Col(),
+ aRange.aEnd.Row(), pOnlyNotBecauseOfMatrix ) )
+ {
+ bOk = false;
+ if ( pOnlyNotBecauseOfMatrix )
+ bMatrix = *pOnlyNotBecauseOfMatrix;
+ }
+ }
+ if (rMark.IsMultiMarked())
+ {
+ if ( !maTabs[rTab]->IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix ) )
+ {
+ bOk = false;
+ if ( pOnlyNotBecauseOfMatrix )
+ bMatrix = *pOnlyNotBecauseOfMatrix;
+ }
+ }
+ }
+
+ if (!bOk && !bMatrix)
+ break;
+ }
+
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = ( !bOk && bMatrix );
+
+ return bOk;
+}
+
+bool ScDocument::HasSelectedBlockMatrixFragment( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark ) const
+{
+ bool bOk = true;
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab] && maTabs[rTab]->HasBlockMatrixFragment( nStartCol, nStartRow, nEndCol, nEndRow ))
+ bOk = false;
+
+ if (!bOk)
+ break;
+ }
+
+ return !bOk;
+}
+
+bool ScDocument::GetMatrixFormulaRange( const ScAddress& rCellPos, ScRange& rMatrix )
+{
+ // if rCell is part of a matrix formula, return its complete range
+
+ ScFormulaCell* pFCell = GetFormulaCell(rCellPos);
+ if (!pFCell)
+ // not a formula cell. Bail out.
+ return false;
+
+ ScAddress aOrigin = rCellPos;
+ if (!pFCell->GetMatrixOrigin(*this, aOrigin))
+ // Failed to get the address of the matrix origin.
+ return false;
+
+ if (aOrigin != rCellPos)
+ {
+ pFCell = GetFormulaCell(aOrigin);
+ if (!pFCell)
+ // The matrix origin cell is not a formula cell !? Something is up...
+ return false;
+ }
+
+ SCCOL nSizeX;
+ SCROW nSizeY;
+ pFCell->GetMatColsRows(nSizeX, nSizeY);
+ if (nSizeX <= 0 || nSizeY <= 0)
+ {
+ // GetMatrixEdge computes also dimensions of the matrix
+ // if not already done (may occur if document is loaded
+ // from old file format).
+ // Needs an "invalid" initialized address.
+ aOrigin.SetInvalid();
+ pFCell->GetMatrixEdge(*this, aOrigin);
+ pFCell->GetMatColsRows(nSizeX, nSizeY);
+ }
+
+ if (nSizeX <= 0 || nSizeY <= 0)
+ // Matrix size is still invalid. Give up.
+ return false;
+
+ ScAddress aEnd( aOrigin.Col() + nSizeX - 1,
+ aOrigin.Row() + nSizeY - 1,
+ aOrigin.Tab() );
+
+ rMatrix.aStart = aOrigin;
+ rMatrix.aEnd = aEnd;
+
+ return true;
+}
+
+void ScDocument::ExtendOverlapped( SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) const
+{
+ if ( ValidColRow(rStartCol,rStartRow) && ValidColRow(nEndCol,nEndRow) && ValidTab(nTab) )
+ {
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ SCCOL nCol;
+ SCCOL nOldCol = rStartCol;
+ SCROW nOldRow = rStartRow;
+ for (nCol=nOldCol; nCol<=nEndCol; nCol++)
+ while (GetAttr(nCol,rStartRow,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped())
+ --rStartRow;
+
+ //TODO: pass on ?
+
+ const ScAttrArray& pAttrArray = pTable->ColumnData(nOldCol).AttrArray();
+ SCSIZE nIndex;
+ if ( pAttrArray.Count() )
+ pAttrArray.Search( nOldRow, nIndex );
+ else
+ nIndex = 0;
+ SCROW nAttrPos = nOldRow;
+ while (nAttrPos<=nEndRow)
+ {
+ OSL_ENSURE( nIndex < pAttrArray.Count(), "Wrong index in AttrArray" );
+
+ bool bHorOverlapped;
+ if ( pAttrArray.Count() )
+ bHorOverlapped = pAttrArray.mvData[nIndex].pPattern->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped();
+ else
+ bHorOverlapped = GetDefPattern()->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped();
+ if ( bHorOverlapped )
+ {
+ SCROW nEndRowSeg = (pAttrArray.Count()) ? pAttrArray.mvData[nIndex].nEndRow : MaxRow();
+ SCROW nLoopEndRow = std::min( nEndRow, nEndRowSeg );
+ for (SCROW nAttrRow = nAttrPos; nAttrRow <= nLoopEndRow; nAttrRow++)
+ {
+ SCCOL nTempCol = nOldCol;
+ do
+ --nTempCol;
+ while (GetAttr(nTempCol,nAttrRow,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped());
+ if (nTempCol < rStartCol)
+ rStartCol = nTempCol;
+ }
+ }
+ if ( pAttrArray.Count() )
+ {
+ nAttrPos = pAttrArray.mvData[nIndex].nEndRow + 1;
+ ++nIndex;
+ }
+ else
+ nAttrPos = MaxRow() + 1;
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("ExtendOverlapped: invalid range");
+ }
+}
+
+void ScDocument::ExtendMergeSel( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow,
+ const ScMarkData& rMark, bool bRefresh )
+{
+ // use all selected sheets from rMark
+
+ SCCOL nOldEndCol = rEndCol;
+ SCROW nOldEndRow = rEndRow;
+
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if ( maTabs[rTab] )
+ {
+ SCCOL nThisEndCol = nOldEndCol;
+ SCROW nThisEndRow = nOldEndRow;
+ ExtendMerge( nStartCol, nStartRow, nThisEndCol, nThisEndRow, rTab, bRefresh );
+ if ( nThisEndCol > rEndCol )
+ rEndCol = nThisEndCol;
+ if ( nThisEndRow > rEndRow )
+ rEndRow = nThisEndRow;
+ }
+ }
+}
+
+bool ScDocument::ExtendMerge( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow,
+ SCTAB nTab, bool bRefresh )
+{
+ bool bFound = false;
+ if ( ValidColRow(nStartCol,nStartRow) && ValidColRow(rEndCol,rEndRow) && ValidTab(nTab) )
+ {
+ if (ScTable* pTable = FetchTable(nTab))
+ bFound = pTable->ExtendMerge( nStartCol, nStartRow, rEndCol, rEndRow, bRefresh );
+
+ if (bRefresh)
+ RefreshAutoFilter( nStartCol, nStartRow, rEndCol, rEndRow, nTab );
+ }
+ else
+ {
+ OSL_FAIL("ExtendMerge: invalid range");
+ }
+
+ return bFound;
+}
+
+bool ScDocument::ExtendMerge( ScRange& rRange, bool bRefresh )
+{
+ bool bFound = false;
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+
+ PutInOrder( nStartTab, nEndTab );
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < GetTableCount(); nTab++ )
+ {
+ SCCOL nExtendCol = rRange.aEnd.Col();
+ SCROW nExtendRow = rRange.aEnd.Row();
+ if (ExtendMerge( rRange.aStart.Col(), rRange.aStart.Row(),
+ nExtendCol, nExtendRow,
+ nTab, bRefresh ) )
+ {
+ bFound = true;
+ if (nExtendCol > nEndCol) nEndCol = nExtendCol;
+ if (nExtendRow > nEndRow) nEndRow = nExtendRow;
+ }
+ }
+
+ rRange.aEnd.SetCol(nEndCol);
+ rRange.aEnd.SetRow(nEndRow);
+
+ return bFound;
+}
+
+void ScDocument::ExtendTotalMerge( ScRange& rRange ) const
+{
+ // Extend range to merged cells without including any new non-overlapped cells
+ ScRange aExt = rRange;
+ // ExtendMerge() is non-const, but called without refresh.
+ if (!const_cast<ScDocument*>(this)->ExtendMerge( aExt ))
+ return;
+
+ if ( aExt.aEnd.Row() > rRange.aEnd.Row() )
+ {
+ ScRange aTest = aExt;
+ aTest.aStart.SetRow( rRange.aEnd.Row() + 1 );
+ if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) )
+ aExt.aEnd.SetRow(rRange.aEnd.Row());
+ }
+ if ( aExt.aEnd.Col() > rRange.aEnd.Col() )
+ {
+ ScRange aTest = aExt;
+ aTest.aStart.SetCol( rRange.aEnd.Col() + 1 );
+ if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) )
+ aExt.aEnd.SetCol(rRange.aEnd.Col());
+ }
+
+ rRange = aExt;
+}
+
+void ScDocument::ExtendOverlapped( ScRange& rRange ) const
+{
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+
+ PutInOrder( nStartTab, nEndTab );
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < GetTableCount(); nTab++ )
+ {
+ SCCOL nExtendCol = rRange.aStart.Col();
+ SCROW nExtendRow = rRange.aStart.Row();
+ ExtendOverlapped( nExtendCol, nExtendRow,
+ rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
+ if (nExtendCol < nStartCol)
+ {
+ nStartCol = nExtendCol;
+ }
+ if (nExtendRow < nStartRow)
+ {
+ nStartRow = nExtendRow;
+ }
+ }
+
+ rRange.aStart.SetCol(nStartCol);
+ rRange.aStart.SetRow(nStartRow);
+}
+
+bool ScDocument::RefreshAutoFilter( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ SCTAB nDBTab;
+ SCCOL nDBStartCol;
+ SCROW nDBStartRow;
+ SCCOL nDBEndCol;
+ SCROW nDBEndRow;
+
+ // Delete Autofilter
+
+ bool bChange = RemoveFlagsTab( nStartCol,nStartRow, nEndCol,nEndRow, nTab, ScMF::Auto );
+
+ // Set Autofilter
+
+ const ScDBData* pData = nullptr;
+ ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ if (rxDB->HasAutoFilter())
+ {
+ rxDB->GetArea(nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow);
+ if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow &&
+ nDBStartCol<=nEndCol && nDBEndCol>=nStartCol )
+ {
+ if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow,
+ nDBTab, ScMF::Auto ))
+ bChange = true;
+ }
+ }
+ }
+ if (ScTable* pTable = FetchTable(nTab))
+ pData = pTable->GetAnonymousDBData();
+ else
+ pData = nullptr;
+ if (pData && pData->HasAutoFilter())
+ {
+ pData->GetArea( nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow );
+ if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow &&
+ nDBStartCol<=nEndCol && nDBEndCol>=nStartCol )
+ {
+ if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow,
+ nDBTab, ScMF::Auto ))
+ bChange = true;
+ }
+ }
+ return bChange;
+}
+
+void ScDocument::SkipOverlapped( SCCOL& rCol, SCROW& rRow, SCTAB nTab ) const
+{
+ while (IsHorOverlapped(rCol, rRow, nTab))
+ --rCol;
+ while (IsVerOverlapped(rCol, rRow, nTab))
+ --rRow;
+}
+
+bool ScDocument::IsHorOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG );
+ if (pAttr)
+ return pAttr->IsHorOverlapped();
+ else
+ {
+ OSL_FAIL("Overlapped: Attr==0");
+ return false;
+ }
+}
+
+bool ScDocument::IsVerOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ SCROW dummy;
+ const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG,
+ nStartRow ? *nStartRow : dummy, nEndRow ? *nEndRow : dummy );
+ if (pAttr)
+ return pAttr->IsVerOverlapped();
+ else
+ {
+ OSL_FAIL("Overlapped: Attr==0");
+ return false;
+ }
+}
+
+void ScDocument::ApplySelectionFrame( const ScMarkData& rMark,
+ const SvxBoxItem& rLineOuter,
+ const SvxBoxInfoItem* pLineInner )
+{
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false );
+ size_t nRangeCount = aRangeList.size();
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ {
+ for ( size_t j=0; j < nRangeCount; j++ )
+ {
+ const ScRange & rRange = aRangeList[ j ];
+ maTabs[rTab]->ApplyBlockFrame( rLineOuter, pLineInner,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ }
+ }
+ if (!rLineOuter.IsRemoveAdjacentCellBorder())
+ return;
+
+ SvxBoxItem aTmp0(rLineOuter);
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::TOP );
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ SvxBoxItem aLeft( aTmp0 );
+ SvxBoxItem aRight( aTmp0 );
+ SvxBoxItem aTop( aTmp0 );
+ SvxBoxItem aBottom( aTmp0 );
+
+ SvxBoxInfoItem aTmp1( *pLineInner );
+ aTmp1.SetTable( false );
+ aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ aTmp1.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
+ aTmp1.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ SvxBoxInfoItem aLeftInfo( aTmp1 );
+ SvxBoxInfoItem aRightInfo( aTmp1 );
+ SvxBoxInfoItem aTopInfo( aTmp1 );
+ SvxBoxInfoItem aBottomInfo( aTmp1 );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::TOP ) && !rLineOuter.GetTop())
+ aTopInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) && !rLineOuter.GetBottom())
+ aBottomInfo.SetValid( SvxBoxInfoItemValidFlags::TOP );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::LEFT ) && !rLineOuter.GetLeft())
+ aLeftInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) && !rLineOuter.GetRight())
+ aRightInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT );
+
+ const ScRangeList& rRangeListTopEnvelope = rMark.GetTopEnvelope();
+ const ScRangeList& rRangeListBottomEnvelope = rMark.GetBottomEnvelope();
+ const ScRangeList& rRangeListLeftEnvelope = rMark.GetLeftEnvelope();
+ const ScRangeList& rRangeListRightEnvelope = rMark.GetRightEnvelope();
+
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if ( maTabs[rTab] )
+ {
+ size_t nEnvelopeRangeCount = rRangeListTopEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListTopEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aTop, &aTopInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ nEnvelopeRangeCount = rRangeListBottomEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListBottomEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aBottom, &aBottomInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ nEnvelopeRangeCount = rRangeListLeftEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListLeftEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aLeft, &aLeftInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ nEnvelopeRangeCount = rRangeListRightEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListRightEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aRight, &aRightInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ }
+ }
+}
+
+void ScDocument::ApplyFrameAreaTab(const ScRange& rRange,
+ const SvxBoxItem& rLineOuter,
+ const SvxBoxInfoItem& rLineInner)
+{
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCTAB nEndTab = rRange.aStart.Tab();
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < GetTableCount(); nTab++)
+ if (maTabs[nTab])
+ maTabs[nTab]->ApplyBlockFrame(rLineOuter, &rLineInner,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row());
+}
+
+void ScDocument::ApplySelectionPattern( const ScPatternAttr& rAttr, const ScMarkData& rMark, ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+ const SfxItemSet* pSet = &rAttr.GetItemSet();
+ bool bSet = false;
+ sal_uInt16 i;
+ for (i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END && !bSet; i++)
+ if (pSet->GetItemState(i) == SfxItemState::SET)
+ bSet = true;
+
+ if (!bSet)
+ return;
+
+ // ApplySelectionCache needs multi mark
+ if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ ApplyPatternArea( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rAttr, pDataArray, pIsChanged );
+ }
+ else
+ {
+ ScItemPoolCache aCache( mxPoolHelper->GetDocPool(), pSet );
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplySelectionCache( &aCache, rMark, pDataArray, pIsChanged );
+ }
+ }
+}
+
+void ScDocument::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark )
+{
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ChangeSelectionIndent( bIncrement, rMark );
+ }
+}
+
+void ScDocument::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark )
+{
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ClearSelectionItems( pWhich, rMark );
+ }
+}
+
+void ScDocument::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
+{
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+
+ std::vector<ScAddress> aGroupPos;
+ // Destroy and reconstruct listeners only if content is affected.
+ bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
+ if (bDelContent)
+ {
+ // Record the positions of top and/or bottom formula groups that
+ // intersect the area borders.
+ sc::EndListeningContext aCxt(*this);
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ EndListeningIntersectedGroups( aCxt, rRange, &aGroupPos);
+ }
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ SCTAB nMax = GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->DeleteSelection(nDelFlag, rMark, bBroadcast);
+ }
+
+ if (!bDelContent)
+ return;
+
+ // Re-start listeners on those top bottom groups that have been split.
+ SetNeedsListeningGroups(aGroupPos);
+ StartNeededListeners();
+
+ // If formula groups were split their listeners were destroyed and may
+ // need to be notified now that they're restored,
+ // ScTable::DeleteSelection() couldn't do that.
+ if (aGroupPos.empty())
+ return;
+
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ SetDirty( aRangeList[i], true);
+ }
+ //Notify listeners on top and bottom of the group that has been split
+ for (size_t i = 0; i < aGroupPos.size(); ++i) {
+ ScFormulaCell *pFormulaCell = GetFormulaCell(aGroupPos[i]);
+ if (pFormulaCell)
+ pFormulaCell->SetDirty(true);
+ }
+}
+
+void ScDocument::DeleteSelectionTab(
+ SCTAB nTab, InsertDeleteFlags nDelFlag, const ScMarkData& rMark )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+
+ std::vector<ScAddress> aGroupPos;
+ // Destroy and reconstruct listeners only if content is affected.
+ bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
+ if (bDelContent)
+ {
+ // Record the positions of top and/or bottom formula groups that
+ // intersect the area borders.
+ sc::EndListeningContext aCxt(*this);
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab())
+ {
+ ScRange aRange( rRange);
+ aRange.aStart.SetTab( nTab);
+ aRange.aEnd.SetTab( nTab);
+ EndListeningIntersectedGroups( aCxt, aRange, &aGroupPos);
+ }
+ }
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ pTable->DeleteSelection(nDelFlag, rMark);
+
+ if (bDelContent)
+ {
+ // Re-start listeners on those top bottom groups that have been split.
+ SetNeedsListeningGroups(aGroupPos);
+ StartNeededListeners();
+
+ // If formula groups were split their listeners were destroyed and may
+ // need to be notified now that they're restored,
+ // ScTable::DeleteSelection() couldn't do that.
+ if (!aGroupPos.empty())
+ {
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab())
+ {
+ ScRange aRange( rRange);
+ aRange.aStart.SetTab( nTab);
+ aRange.aEnd.SetTab( nTab);
+ SetDirty( aRange, true);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("wrong table");
+ }
+}
+
+ScPatternAttr* ScDocument::GetDefPattern() const
+{
+ return const_cast<ScPatternAttr*>(&mxPoolHelper->GetDocPool()->GetDefaultItem(ATTR_PATTERN));
+}
+
+ScDocumentPool* ScDocument::GetPool()
+{
+ return mxPoolHelper ? mxPoolHelper->GetDocPool() : nullptr;
+}
+
+ScStyleSheetPool* ScDocument::GetStyleSheetPool() const
+{
+ return mxPoolHelper ? mxPoolHelper->GetStylePool() : nullptr;
+}
+
+bool ScDocument::IsEmptyData(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->IsEmptyData(nStartCol, nStartRow, nEndCol, nEndRow);
+ return true;
+}
+
+SCSIZE ScDocument::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, ScDirection eDir )
+{
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ PutInOrder(nStartTab, nEndTab);
+ if (ScTable* pTable = FetchTable(nStartTab))
+ return pTable->GetEmptyLinesInBlock(nStartCol, nStartRow, nEndCol, nEndRow, eDir);
+ return 0;
+}
+
+void ScDocument::FindAreaPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, ScMoveDirection eDirection ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->FindAreaPos(rCol, rRow, eDirection);
+}
+
+void ScDocument::GetNextPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, SCCOL nMovX, SCROW nMovY,
+ bool bMarked, bool bUnprotected, const ScMarkData& rMark, SCCOL nTabStartCol ) const
+{
+ OSL_ENSURE( !nMovX || !nMovY, "GetNextPos: only X or Y" );
+
+ ScMarkData aCopyMark = rMark;
+ aCopyMark.SetMarking(false);
+ aCopyMark.MarkToMulti();
+
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->GetNextPos(rCol, rRow, nMovX, nMovY, bMarked, bUnprotected, aCopyMark, nTabStartCol);
+}
+
+// Data operations
+
+void ScDocument::UpdStlShtPtrsFrmNms()
+{
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN))
+ {
+ auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
+ if (pPattern)
+ pPattern->UpdateStyleSheet(*this);
+ }
+ const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).UpdateStyleSheet(*this);
+}
+
+void ScDocument::StylesToNames()
+{
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN))
+ {
+ auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
+ if (pPattern)
+ pPattern->StyleToName();
+ }
+ const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).StyleToName();
+}
+
+sal_uInt64 ScDocument::GetCellCount() const
+{
+ sal_uInt64 nCellCount = 0;
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ nCellCount += a->GetCellCount();
+ }
+
+ return nCellCount;
+}
+
+sal_uInt64 ScDocument::GetFormulaGroupCount() const
+{
+ sal_uInt64 nFormulaGroupCount = 0;
+
+ ScFormulaGroupIterator aIter( *const_cast<ScDocument*>(this) );
+ for ( sc::FormulaGroupEntry* ptr = aIter.first(); ptr; ptr = aIter.next())
+ {
+ nFormulaGroupCount++;
+ }
+
+ return nFormulaGroupCount;
+}
+
+sal_uInt64 ScDocument::GetCodeCount() const
+{
+ sal_uInt64 nCodeCount = 0;
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ nCodeCount += a->GetCodeCount();
+ }
+
+ return nCodeCount;
+}
+
+void ScDocument::PageStyleModified( SCTAB nTab, const OUString& rNewName )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->PageStyleModified( rNewName );
+}
+
+void ScDocument::SetPageStyle( SCTAB nTab, const OUString& rName )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetPageStyle( rName );
+}
+
+OUString ScDocument::GetPageStyle( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPageStyle();
+ return OUString();
+}
+
+void ScDocument::SetPageSize( SCTAB nTab, const Size& rSize )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetPageSize( rSize );
+}
+
+Size ScDocument::GetPageSize( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPageSize();
+
+ OSL_FAIL("invalid tab");
+ return Size();
+}
+
+void ScDocument::SetRepeatArea( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRepeatArea( nStartCol, nEndCol, nStartRow, nEndRow );
+}
+
+void ScDocument::InvalidatePageBreaks(SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->InvalidatePageBreaks();
+}
+
+void ScDocument::UpdatePageBreaks( SCTAB nTab, const ScRange* pUserArea )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->UpdatePageBreaks( pUserArea );
+}
+
+void ScDocument::RemoveManualBreaks( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->RemoveManualBreaks();
+}
+
+bool ScDocument::HasManualBreaks( SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->HasManualBreaks();
+
+ OSL_FAIL("invalid tab");
+ return false;
+}
+
+void ScDocument::GetDocStat( ScDocStat& rDocStat )
+{
+ rDocStat.nTableCount = GetTableCount();
+ rDocStat.aDocName = aDocName;
+ rDocStat.nFormulaCount = GetFormulaGroupCount();
+ rDocStat.nCellCount = GetCellCount();
+}
+
+bool ScDocument::HasPrintRange()
+{
+ bool bResult = false;
+
+ for (const auto& a : maTabs)
+ {
+ if (!a)
+ continue;
+ bResult = a->IsPrintEntireSheet() || (a->GetPrintRangeCount() > 0);
+ if (bResult)
+ break;
+ }
+
+ return bResult;
+}
+
+bool ScDocument::IsPrintEntireSheet( SCTAB nTab ) const
+{
+ const ScTable* pTable = FetchTable(nTab);
+ return (pTable && pTable->IsPrintEntireSheet());
+}
+
+sal_uInt16 ScDocument::GetPrintRangeCount( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPrintRangeCount();
+ return 0;
+}
+
+const ScRange* ScDocument::GetPrintRange( SCTAB nTab, sal_uInt16 nPos )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetPrintRange(nPos);
+
+ return nullptr;
+}
+
+std::optional<ScRange> ScDocument::GetRepeatColRange( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRepeatColRange();
+
+ return std::nullopt;
+}
+
+std::optional<ScRange> ScDocument::GetRepeatRowRange( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetRepeatRowRange();
+ return std::nullopt;
+}
+
+void ScDocument::ClearPrintRanges( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ClearPrintRanges();
+}
+
+void ScDocument::ClearPrintNamedRanges( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ClearPrintNamedRanges();
+}
+
+void ScDocument::AddPrintRange( SCTAB nTab, const ScRange& rNew )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->AddPrintRange(rNew);
+}
+
+void ScDocument::SetPrintEntireSheet( SCTAB nTab )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetPrintEntireSheet();
+}
+
+void ScDocument::SetRepeatColRange( SCTAB nTab, std::optional<ScRange> oNew )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRepeatColRange(std::move(oNew));
+}
+
+void ScDocument::SetRepeatRowRange( SCTAB nTab, std::optional<ScRange> oNew )
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetRepeatRowRange(std::move(oNew));
+}
+
+std::unique_ptr<ScPrintRangeSaver> ScDocument::CreatePrintRangeSaver() const
+{
+ const SCTAB nCount = GetTableCount();
+ std::unique_ptr<ScPrintRangeSaver> pNew(new ScPrintRangeSaver( nCount ));
+ for (SCTAB i=0; i<nCount; i++)
+ if (maTabs[i])
+ maTabs[i]->FillPrintSaver( pNew->GetTabData(i) );
+ return pNew;
+}
+
+void ScDocument::RestorePrintRanges( const ScPrintRangeSaver& rSaver )
+{
+ const SCTAB nCount = rSaver.GetTabCount();
+ const SCTAB maxIndex = std::min(nCount, GetTableCount());
+ for (SCTAB i=0; i<maxIndex; i++)
+ if (maTabs[i])
+ maTabs[i]->RestorePrintRanges( rSaver.GetTabData(i) );
+}
+
+bool ScDocument::NeedPageResetAfterTab( SCTAB nTab ) const
+{
+ // The page number count restarts at a sheet, if another template is set at
+ // the preceding one (only compare names) and if a pagenumber is specified (not 0)
+
+ if (nTab + 1 < GetTableCount() && maTabs[nTab] && maTabs[nTab+1])
+ {
+ const OUString & rNew = maTabs[nTab+1]->GetPageStyle();
+ if ( rNew != maTabs[nTab]->GetPageStyle() )
+ {
+ SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( rNew, SfxStyleFamily::Page );
+ if ( pStyle )
+ {
+ const SfxItemSet& rSet = pStyle->GetItemSet();
+ sal_uInt16 nFirst = rSet.Get(ATTR_PAGE_FIRSTPAGENO).GetValue();
+ if ( nFirst != 0 )
+ return true; // Specify page number in new template
+ }
+ }
+ }
+
+ return false; // otherwise not
+}
+
+ScUndoManager* ScDocument::GetUndoManager()
+{
+ if (!mpUndoManager)
+ {
+ // to support enhanced text edit for draw objects, use an SdrUndoManager
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+
+ ScUndoManager* pUndoManager = new ScUndoManager;
+ pUndoManager->SetDocShell(GetDocumentShell());
+ mpUndoManager = pUndoManager;
+ }
+
+ return mpUndoManager;
+}
+
+ScRowBreakIterator* ScDocument::GetRowBreakIterator(SCTAB nTab) const
+{
+ if (HasTable(nTab))
+ return new ScRowBreakIterator(maTabs[nTab]->maRowPageBreaks);
+ return nullptr;
+}
+
+void ScDocument::AddSubTotalCell(ScFormulaCell* pCell)
+{
+ maSubTotalCells.insert(pCell);
+}
+
+void ScDocument::RemoveSubTotalCell(ScFormulaCell* pCell)
+{
+ maSubTotalCells.erase(pCell);
+}
+
+namespace {
+
+bool lcl_hasDirtyRange(const ScDocument& rDoc, ScFormulaCell* pCell, const ScRange& rDirtyRange)
+{
+ ScDetectiveRefIter aRefIter(rDoc, pCell);
+ ScRange aRange;
+ while (aRefIter.GetNextRef(aRange))
+ {
+ if (aRange.Intersects(rDirtyRange))
+ return true;
+ }
+ return false;
+}
+
+}
+
+void ScDocument::SetSubTotalCellsDirty(const ScRange& rDirtyRange)
+{
+ // to update the list by skipping cells that no longer contain subtotal function.
+ set<ScFormulaCell*> aNewSet;
+
+ bool bOldRecalc = GetAutoCalc();
+ SetAutoCalc(false);
+ for (ScFormulaCell* pCell : maSubTotalCells)
+ {
+ if (pCell->IsSubTotal())
+ {
+ aNewSet.insert(pCell);
+ if (lcl_hasDirtyRange(*this, pCell, rDirtyRange))
+ pCell->SetDirty();
+ }
+ }
+
+ SetAutoCalc(bOldRecalc);
+ maSubTotalCells.swap(aNewSet); // update the list.
+}
+
+sal_uInt16 ScDocument::GetTextWidth( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetTextWidth(rPos.Col(), rPos.Row());
+ return 0;
+}
+
+SvtScriptType ScDocument::GetScriptType( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetScriptType(rPos.Col(), rPos.Row());
+ return SvtScriptType::NONE;
+}
+
+void ScDocument::SetScriptType( const ScAddress& rPos, SvtScriptType nType )
+{
+ SCTAB nTab = rPos.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->SetScriptType(rPos.Col(), rPos.Row(), nType);
+}
+
+void ScDocument::EnableUndo( bool bVal )
+{
+ // The undo manager increases lock count every time undo is disabled.
+ // Because of this, we shouldn't disable undo unless it's currently
+ // enabled, or else re-enabling it may not actually re-enable undo unless
+ // the lock count becomes zero.
+
+ if (bVal != GetUndoManager()->IsUndoEnabled())
+ {
+ GetUndoManager()->EnableUndo(bVal);
+ if( mpDrawLayer ) mpDrawLayer->EnableUndo(bVal);
+ }
+
+ mbUndoEnabled = bVal;
+}
+
+void ScDocument::EnableUserInteraction( bool bVal )
+{
+ mbUserInteractionEnabled = bVal;
+}
+
+bool ScDocument::IsInVBAMode() const
+{
+ if (!mpShell)
+ return false;
+
+ try
+ {
+ uno::Reference<script::vba::XVBACompatibility> xVBA(
+ mpShell->GetBasicContainer(), uno::UNO_QUERY);
+
+ return xVBA.is() && xVBA->getVBACompatibilityMode();
+ }
+ catch (const lang::NotInitializedException&) {}
+
+ return false;
+}
+
+// Sparklines
+std::shared_ptr<sc::Sparkline> ScDocument::GetSparkline(ScAddress const& rPosition)
+{
+ SCTAB nTab = rPosition.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetSparkline(rPosition.Col(), rPosition.Row());
+ return std::shared_ptr<sc::Sparkline>();
+}
+
+bool ScDocument::HasSparkline(ScAddress const & rPosition)
+{
+ return bool(GetSparkline(rPosition));
+}
+
+sc::Sparkline* ScDocument::CreateSparkline(ScAddress const& rPosition, std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup)
+{
+ SCTAB nTab = rPosition.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->CreateSparkline(rPosition.Col(), rPosition.Row(), pSparklineGroup);
+ return nullptr;
+}
+
+bool ScDocument::DeleteSparkline(ScAddress const & rPosition)
+{
+ SCTAB nTab = rPosition.Tab();
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->DeleteSparkline(rPosition.Col(), rPosition.Row());
+ return false;
+}
+
+sc::SparklineList* ScDocument::GetSparklineList(SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return &pTable->GetSparklineList();
+ return nullptr;
+}
+
+bool ScDocument::HasOneSparklineGroup(ScRange const& rRange)
+{
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup;
+ return GetSparklineGroupInRange(rRange, pSparklineGroup);
+}
+
+bool ScDocument::GetSparklineGroupInRange(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup>& rGroup)
+{
+ std::shared_ptr<sc::SparklineGroup> pFoundGroup;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ for (SCCOL nX = rRange.aStart.Col(); nX <= rRange.aEnd.Col(); nX++)
+ {
+ for (SCROW nY = rRange.aStart.Row(); nY <= rRange.aEnd.Row(); nY++)
+ {
+ auto pSparkline = GetSparkline(ScAddress(nX, nY, nTab));
+ if (!pSparkline)
+ {
+ return false;
+ }
+ else if (!pFoundGroup)
+ {
+ pFoundGroup = pSparkline->getSparklineGroup();
+ }
+ else if (pFoundGroup != pSparkline->getSparklineGroup())
+ {
+ return false;
+ }
+ }
+ }
+
+ rGroup = pFoundGroup;
+ return true;
+}
+
+std::shared_ptr<sc::SparklineGroup> ScDocument::SearchSparklineGroup(tools::Guid const& rGuid)
+{
+ for (auto const& rTable : maTabs)
+ {
+ if (!rTable)
+ continue;
+
+ auto& rSparklineList = rTable->GetSparklineList();
+
+ for (auto const& pSparklineGroup : rSparklineList.getSparklineGroups())
+ {
+ if (pSparklineGroup->getID() == rGuid)
+ return pSparklineGroup;
+ }
+ }
+
+ return std::shared_ptr<sc::SparklineGroup>();
+}
+
+// Notes
+
+ScPostIt* ScDocument::GetNote(const ScAddress& rPos)
+{
+ return GetNote(rPos.Col(), rPos.Row(), rPos.Tab());
+}
+
+ScPostIt* ScDocument::GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNote(nCol, nRow);
+ return nullptr;
+}
+
+void ScDocument::SetNote(const ScAddress& rPos, std::unique_ptr<ScPostIt> pNote)
+{
+ return SetNote(rPos.Col(), rPos.Row(), rPos.Tab(), std::move(pNote));
+}
+
+void ScDocument::SetNote(SCCOL nCol, SCROW nRow, SCTAB nTab, std::unique_ptr<ScPostIt> pNote)
+{
+ if (ScTable* pTable = FetchTable(nTab))
+ {
+ pTable->SetNote(nCol, nRow, std::move(pNote));
+
+ if (ScDocShell* pDocSh = GetDocumentShell())
+ {
+ HelperNotifyChanges::NotifyIfChangesListeners(
+ *pDocSh, ScRange(nCol, nRow, nTab), "note");
+ }
+ }
+}
+
+bool ScDocument::HasNote(const ScAddress& rPos) const
+{
+ return HasNote(rPos.Col(), rPos.Row(), rPos.Tab());
+}
+
+bool ScDocument::HasNote(SCCOL nCol, SCROW nRow, SCTAB nTab) const
+{
+ if (!ValidColRow(nCol, nRow))
+ return false;
+
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ if (nCol >= pTab->GetAllocatedColumnsCount())
+ return false;
+
+ const ScPostIt* pNote = pTab->aCol[nCol].GetCellNote(nRow);
+ return pNote != nullptr;
+}
+
+bool ScDocument::HasNote(SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ nStartCol = pTable->ClampToAllocatedColumns(nStartCol);
+ nEndCol = pTable->ClampToAllocatedColumns(nEndCol);
+ for (SCCOL nCol = nStartCol; nCol < nEndCol; ++nCol)
+ if (pTable->aCol[nCol].HasCellNote(nStartRow, nEndRow))
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::HasColNotes(SCCOL nCol, SCTAB nTab) const
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ if (nCol >= pTable->GetAllocatedColumnsCount())
+ return false;
+
+ return pTable->aCol[nCol].HasCellNotes();
+ }
+
+ return false;
+}
+
+bool ScDocument::HasTabNotes(SCTAB nTab) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ {
+ for (SCCOL nCol=0, nColSize = pTable->aCol.size(); nCol < nColSize; ++nCol)
+ if ( HasColNotes(nCol, nTab) )
+ return true;
+ }
+ return false;
+}
+
+bool ScDocument::HasNotes() const
+{
+ for (SCTAB i = 0; i <= MAXTAB; ++i)
+ {
+ if (HasTabNotes(i))
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<ScPostIt> ScDocument::ReleaseNote(const ScAddress& rPos)
+{
+ if (ScTable* pTable = FetchTable(rPos.Tab()))
+ return pTable->ReleaseNote(rPos.Col(), rPos.Row());
+ return nullptr;
+}
+
+ScPostIt* ScDocument::GetOrCreateNote(const ScAddress& rPos)
+{
+ if (HasNote(rPos))
+ return GetNote(rPos);
+ else
+ return CreateNote(rPos);
+}
+
+ScPostIt* ScDocument::CreateNote(const ScAddress& rPos)
+{
+ ScPostIt* pPostIt = new ScPostIt(*this, rPos);
+ SetNote(rPos, std::unique_ptr<ScPostIt>(pPostIt));
+ return pPostIt;
+}
+
+size_t ScDocument::GetNoteCount( SCTAB nTab, SCCOL nCol ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNoteCount(nCol);
+ return 0;
+}
+
+void ScDocument::CreateAllNoteCaptions()
+{
+ for (const auto& pTable : maTabs)
+ {
+ if (pTable)
+ pTable->CreateAllNoteCaptions();
+ }
+}
+
+void ScDocument::ForgetNoteCaptions( const ScRangeList& rRanges, bool bPreserveData )
+{
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rRanges[i];
+ const ScAddress& s = rRange.aStart;
+ const ScAddress& e = rRange.aEnd;
+ for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab)
+ {
+ if (ScTable* pTable = FetchTable(nTab))
+ pTable->ForgetNoteCaptions(s.Col(), s.Row(), e.Col(), e.Row(), bPreserveData);
+ }
+ }
+}
+
+CommentCaptionState ScDocument::GetAllNoteCaptionsState( const ScRangeList& rRanges )
+{
+ CommentCaptionState aTmpState = CommentCaptionState::ALLHIDDEN;
+ CommentCaptionState aState = CommentCaptionState::ALLHIDDEN;
+ bool bFirstControl = true;
+ std::vector<sc::NoteEntry> aNotes;
+
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rRanges[i];
+
+ for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
+ {
+ aState = maTabs[nTab]->GetAllNoteCaptionsState( rRange, aNotes );
+
+ if (aState == CommentCaptionState::MIXED)
+ return aState;
+
+ if (bFirstControl) // it is possible that a range is ALLSHOWN, another range is ALLHIDDEN,
+ { // we have to detect that situation as mixed.
+ aTmpState = aState;
+ bFirstControl = false;
+ }
+ else if(aTmpState != aState)
+ {
+ aState = CommentCaptionState::MIXED;
+ return aState;
+ }
+ }
+ }
+ return aState;
+}
+
+ScAddress ScDocument::GetNotePosition( size_t nIndex ) const
+{
+ for (size_t nTab = 0; nTab < maTabs.size(); ++nTab)
+ {
+ for (SCCOL nCol : GetAllocatedColumnsRange(nTab, 0, MaxCol()))
+ {
+ size_t nColNoteCount = GetNoteCount(nTab, nCol);
+ if (!nColNoteCount)
+ continue;
+
+ if (nIndex >= nColNoteCount)
+ {
+ nIndex -= nColNoteCount;
+ continue;
+ }
+
+ SCROW nRow = GetNotePosition(nTab, nCol, nIndex);
+ if (nRow >= 0)
+ return ScAddress(nCol, nRow, nTab);
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+ }
+ }
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+}
+
+ScAddress ScDocument::GetNotePosition( size_t nIndex, SCTAB nTab ) const
+{
+ for (SCCOL nCol : GetAllocatedColumnsRange(nTab, 0, MaxCol()))
+ {
+ size_t nColNoteCount = GetNoteCount(nTab, nCol);
+ if (!nColNoteCount)
+ continue;
+
+ if (nIndex >= nColNoteCount)
+ {
+ nIndex -= nColNoteCount;
+ continue;
+ }
+
+ SCROW nRow = GetNotePosition(nTab, nCol, nIndex);
+ if (nRow >= 0)
+ return ScAddress(nCol, nRow, nTab);
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+ }
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+}
+
+SCROW ScDocument::GetNotePosition( SCTAB nTab, SCCOL nCol, size_t nIndex ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ return pTable->GetNotePosition(nCol, nIndex);
+ return -1;
+}
+
+void ScDocument::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
+{
+ for (const auto & pTable : maTabs)
+ {
+ if (pTable)
+ pTable->GetAllNoteEntries(rNotes);
+ }
+}
+
+void ScDocument::GetAllNoteEntries( SCTAB nTab, std::vector<sc::NoteEntry>& rNotes ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->GetAllNoteEntries(rNotes);
+}
+
+void ScDocument::GetNotesInRange( const ScRangeList& rRangeList, std::vector<sc::NoteEntry>& rNotes ) const
+{
+ for( size_t i = 0; i < rRangeList.size(); ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
+ {
+ maTabs[nTab]->GetNotesInRange( rRange, rNotes );
+ }
+ }
+}
+
+void ScDocument::GetUnprotectedCells( ScRangeList& rRangeList, SCTAB nTab ) const
+{
+ if (const ScTable* pTable = FetchTable(nTab))
+ pTable->GetUnprotectedCells(rRangeList);
+}
+
+bool ScDocument::ContainsNotesInRange( const ScRangeList& rRangeList ) const
+{
+ for( size_t i = 0; i < rRangeList.size(); ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
+ {
+ bool bContainsNote = maTabs[nTab]->ContainsNotesInRange( rRange );
+ if(bContainsNote)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScDocument::SetAutoNameCache( std::unique_ptr<ScAutoNameCache> pCache )
+{
+ pAutoNameCache = std::move(pCache);
+}
+
+thread_local ScDocumentThreadSpecific ScDocument::maThreadSpecific;
+
+ScRecursionHelper& ScDocument::GetRecursionHelper()
+{
+ if (!IsThreadedGroupCalcInProgress())
+ {
+ if (!maNonThreaded.xRecursionHelper)
+ maNonThreaded.xRecursionHelper = std::make_unique<ScRecursionHelper>();
+ return *maNonThreaded.xRecursionHelper;
+ }
+ else
+ {
+ if (!maThreadSpecific.xRecursionHelper)
+ maThreadSpecific.xRecursionHelper = std::make_unique<ScRecursionHelper>();
+ return *maThreadSpecific.xRecursionHelper;
+ }
+}
+
+void ScDocument::SetupContextFromNonThreadedContext(ScInterpreterContext& /*threadedContext*/, int /*threadNumber*/)
+{
+ (void)this;
+ // lookup cache is now only in pooled ScInterpreterContext's
+}
+
+void ScDocument::MergeContextBackIntoNonThreadedContext(ScInterpreterContext& threadedContext, int /*threadNumber*/)
+{
+ // Move data from a context used by a calculation thread to the main thread's context.
+ // Called from the main thread after the calculation thread has already finished.
+ assert(!IsThreadedGroupCalcInProgress());
+ maInterpreterContext.maDelayedSetNumberFormat.insert(
+ maInterpreterContext.maDelayedSetNumberFormat.end(),
+ std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.begin()),
+ std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.end()));
+ // lookup cache is now only in pooled ScInterpreterContext's
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
new file mode 100644
index 0000000000..acf0f27672
--- /dev/null
+++ b/sc/source/core/data/document10.cxx
@@ -0,0 +1,1119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <document.hxx>
+#include <clipcontext.hxx>
+#include <clipparam.hxx>
+#include <table.hxx>
+#include <tokenarray.hxx>
+#include <listenercontext.hxx>
+#include <tokenstringcontext.hxx>
+#include <poolhelp.hxx>
+#include <cellvalues.hxx>
+#include <docpool.hxx>
+#include <columniterator.hxx>
+
+#include <refupdatecontext.hxx>
+#include <sal/log.hxx>
+#include <editeng/colritem.hxx>
+#include <scitems.hxx>
+#include <datamapper.hxx>
+#include <docsh.hxx>
+#include <bcaslot.hxx>
+#include <broadcast.hxx>
+
+// Add totally brand-new methods to this source file.
+
+bool ScDocument::IsMerged( const ScAddress& rPos ) const
+{
+ const ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return false;
+
+ return pTab->IsMerged(rPos.Col(), rPos.Row());
+}
+
+sc::MultiDataCellState ScDocument::HasMultipleDataCells( const ScRange& rRange ) const
+{
+ if (rRange.aStart.Tab() != rRange.aEnd.Tab())
+ // Currently we only support a single-sheet range.
+ return sc::MultiDataCellState();
+
+ const ScTable* pTab = FetchTable(rRange.aStart.Tab());
+ if (!pTab)
+ return sc::MultiDataCellState(sc::MultiDataCellState::Empty);
+
+ const ScAddress& s = rRange.aStart;
+ const ScAddress& e = rRange.aEnd;
+ return pTab->HasMultipleDataCells(s.Col(), s.Row(), e.Col(), e.Row());
+}
+
+void ScDocument::DeleteBeforeCopyFromClip(
+ sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ SCTAB nClipTab = 0;
+ const TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
+ SCTAB nClipTabCount = rClipTabs.size();
+
+ for (SCTAB nTab = rCxt.getTabStart(); nTab <= rCxt.getTabEnd(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ if (!rMark.GetTableSelect(nTab))
+ continue;
+
+ while (!rClipTabs[nClipTab])
+ nClipTab = (nClipTab+1) % nClipTabCount;
+
+ pTab->DeleteBeforeCopyFromClip(rCxt, *rClipTabs[nClipTab], rBroadcastSpans);
+
+ nClipTab = (nClipTab+1) % nClipTabCount;
+ }
+}
+
+bool ScDocument::CopyOneCellFromClip(
+ sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ ScDocument* pClipDoc = rCxt.getClipDoc();
+ if (pClipDoc->GetClipParam().mbCutMode)
+ // We don't handle cut and paste or moving of cells here.
+ return false;
+
+ ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
+ if (aClipRange.aStart.Row() != aClipRange.aEnd.Row())
+ // The source is not really a single row. Bail out.
+ return false;
+
+ SCCOL nSrcColSize = aClipRange.aEnd.Col() - aClipRange.aStart.Col() + 1;
+ SCCOL nDestColSize = nCol2 - nCol1 + 1;
+ if (nDestColSize < nSrcColSize)
+ return false;
+
+ if (pClipDoc->maTabs.size() > 1)
+ // Copying from multiple source sheets is not handled here.
+ return false;
+
+ ScAddress aSrcPos = aClipRange.aStart;
+
+ for (SCCOL nCol = aClipRange.aStart.Col(); nCol <= aClipRange.aEnd.Col(); ++nCol)
+ {
+ ScAddress aTestPos = aSrcPos;
+ aTestPos.SetCol(nCol);
+ if (pClipDoc->IsMerged(aTestPos))
+ // We don't handle merged source cell for this.
+ return false;
+ }
+
+ ScTable* pSrcTab = pClipDoc->FetchTable(aSrcPos.Tab());
+ if (!pSrcTab)
+ return false;
+
+ rCxt.setSingleCellColumnSize(nSrcColSize);
+
+ for (SCCOL nColOffset = 0; nColOffset < nSrcColSize; ++nColOffset, aSrcPos.IncCol())
+ {
+ const ScPatternAttr* pAttr = pClipDoc->GetPattern(aSrcPos);
+ rCxt.setSingleCellPattern(nColOffset, pAttr);
+
+ if ((rCxt.getInsertFlag() & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE)
+ rCxt.setSingleCellNote(nColOffset, pClipDoc->GetNote(aSrcPos));
+
+ if ((rCxt.getInsertFlag() & InsertDeleteFlags::SPARKLINES) != InsertDeleteFlags::NONE)
+ rCxt.setSingleSparkline(nColOffset, pClipDoc->GetSparkline(aSrcPos));
+
+ ScColumn* pSrcCol = pSrcTab->FetchColumn(aSrcPos.Col());
+ assert(pSrcCol);
+ // Determine the script type of the copied single cell.
+ pSrcCol->UpdateScriptTypes(aSrcPos.Row(), aSrcPos.Row());
+ rCxt.setSingleCell(aSrcPos, *pSrcCol);
+ }
+
+ // All good. Proceed with the pasting.
+
+ SCTAB nTabEnd = rCxt.getTabEnd();
+ for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < GetTableCount(); ++i)
+ {
+ maTabs[i]->CopyOneCellFromClip(rCxt, nCol1, nRow1, nCol2, nRow2, aClipRange.aStart.Row(), pSrcTab);
+ }
+
+ sc::RefUpdateContext aRefCxt(*this);
+ aRefCxt.maRange = ScRange(nCol1, nRow1, rCxt.getTabStart(), nCol2, nRow2, nTabEnd);
+ aRefCxt.mnColDelta = nCol1 - aSrcPos.Col();
+ aRefCxt.mnRowDelta = nRow1 - aSrcPos.Row();
+ aRefCxt.mnTabDelta = rCxt.getTabStart() - aSrcPos.Tab();
+ // Only Copy&Paste, for Cut&Paste we already bailed out early.
+ aRefCxt.meMode = URM_COPY;
+ UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
+
+ return true;
+}
+
+void ScDocument::SetValues( const ScAddress& rPos, const std::vector<double>& rVals )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->SetValues(rPos.Col(), rPos.Row(), rVals);
+}
+
+void ScDocument::TransferCellValuesTo( const ScAddress& rTopPos, size_t nLen, sc::CellValues& rDest )
+{
+ ScTable* pTab = FetchTable(rTopPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->TransferCellValuesTo(rTopPos.Col(), rTopPos.Row(), nLen, rDest);
+}
+
+void ScDocument::CopyCellValuesFrom( const ScAddress& rTopPos, const sc::CellValues& rSrc )
+{
+ ScTable* pTab = FetchTable(rTopPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->CopyCellValuesFrom(rTopPos.Col(), rTopPos.Row(), rSrc);
+}
+
+std::set<Color> ScDocument::GetDocColors()
+{
+ std::set<Color> aDocColors;
+ ScDocumentPool *pPool = GetPool();
+ const sal_uInt16 pAttribs[] = {ATTR_BACKGROUND, ATTR_FONT_COLOR};
+ for (sal_uInt16 nAttrib : pAttribs)
+ {
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(nAttrib))
+ {
+ const SvxColorItem *pColorItem = static_cast<const SvxColorItem*>(pItem);
+ Color aColor( pColorItem->GetValue() );
+ if (COL_AUTO != aColor)
+ aDocColors.insert(aColor);
+ }
+ }
+ return aDocColors;
+}
+
+void ScDocument::SetCalcConfig( const ScCalcConfig& rConfig )
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ maCalcConfig = rConfig;
+}
+
+void ScDocument::ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo )
+{
+ sc::EndListeningContext aCxt(*this);
+
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ pTab->ConvertFormulaToValue(
+ aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
+ pUndo);
+ }
+
+ aCxt.purgeEmptyBroadcasters();
+}
+
+void ScDocument::SwapNonEmpty( sc::TableValues& rValues )
+{
+ const ScRange& rRange = rValues.getRange();
+ if (!rRange.IsValid())
+ return;
+
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
+ sc::StartListeningContext aStartCxt(*this, pPosSet);
+ sc::EndListeningContext aEndCxt(*this, pPosSet);
+
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ pTab->SwapNonEmpty(rValues, aStartCxt, aEndCxt);
+ }
+
+ aEndCxt.purgeEmptyBroadcasters();
+}
+
+void ScDocument::PreprocessAllRangeNamesUpdate( const std::map<OUString, ScRangeName>& rRangeMap )
+{
+ // Update all existing names with new names.
+ // The prerequisites are that the name dialog preserves ScRangeData index
+ // for changes and does not reuse free index slots for new names.
+ // ScDocument::SetAllRangeNames() hereafter then will replace the
+ // ScRangeName containers of ScRangeData instances with empty
+ // ScRangeData::maNewName.
+ std::map<OUString, ScRangeName*> aRangeNameMap;
+ GetRangeNameMap( aRangeNameMap);
+ for (const auto& itTab : aRangeNameMap)
+ {
+ ScRangeName* pOldRangeNames = itTab.second;
+ if (!pOldRangeNames)
+ continue;
+
+ const auto& itNewTab( rRangeMap.find( itTab.first));
+ if (itNewTab == rRangeMap.end())
+ continue;
+
+ const ScRangeName& rNewRangeNames = itNewTab->second;
+
+ for (const auto& rEntry : *pOldRangeNames)
+ {
+ ScRangeData* pOldData = rEntry.second.get();
+ if (!pOldData)
+ continue;
+
+ const ScRangeData* pNewData = rNewRangeNames.findByIndex( pOldData->GetIndex());
+ if (pNewData)
+ pOldData->SetNewName( pNewData->GetName());
+ }
+ }
+
+ sc::EndListeningContext aEndListenCxt(*this);
+ sc::CompileFormulaContext aCompileCxt(*this);
+
+ for (const auto& rxTab : maTabs)
+ {
+ ScTable* p = rxTab.get();
+ p->PreprocessRangeNameUpdate(aEndListenCxt, aCompileCxt);
+ }
+}
+
+void ScDocument::PreprocessRangeNameUpdate()
+{
+ sc::EndListeningContext aEndListenCxt(*this);
+ sc::CompileFormulaContext aCompileCxt(*this);
+
+ for (const auto& rxTab : maTabs)
+ {
+ ScTable* p = rxTab.get();
+ p->PreprocessRangeNameUpdate(aEndListenCxt, aCompileCxt);
+ }
+}
+
+void ScDocument::PreprocessDBDataUpdate()
+{
+ sc::EndListeningContext aEndListenCxt(*this);
+ sc::CompileFormulaContext aCompileCxt(*this);
+
+ for (const auto& rxTab : maTabs)
+ {
+ ScTable* p = rxTab.get();
+ p->PreprocessDBDataUpdate(aEndListenCxt, aCompileCxt);
+ }
+}
+
+void ScDocument::CompileHybridFormula()
+{
+ sc::StartListeningContext aStartListenCxt(*this);
+ sc::CompileFormulaContext aCompileCxt(*this);
+ for (const auto& rxTab : maTabs)
+ {
+ ScTable* p = rxTab.get();
+ p->CompileHybridFormula(aStartListenCxt, aCompileCxt);
+ }
+}
+
+void ScDocument::SharePooledResources( const ScDocument* pSrcDoc )
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mxPoolHelper = pSrcDoc->mxPoolHelper;
+ mpCellStringPool = pSrcDoc->mpCellStringPool;
+}
+
+void ScDocument::UpdateScriptTypes( const ScAddress& rPos, SCCOL nColSize, SCROW nRowSize )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->UpdateScriptTypes(rPos.Col(), rPos.Row(), rPos.Col()+nColSize-1, rPos.Row()+nRowSize-1);
+}
+
+bool ScDocument::HasUniformRowHeight( SCTAB nTab, SCROW nRow1, SCROW nRow2 ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ return pTab->HasUniformRowHeight(nRow1, nRow2);
+}
+
+void ScDocument::UnshareFormulaCells( SCTAB nTab, SCCOL nCol, std::vector<SCROW>& rRows )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->UnshareFormulaCells(nCol, rRows);
+}
+
+void ScDocument::RegroupFormulaCells( SCTAB nTab, SCCOL nCol )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->RegroupFormulaCells(nCol);
+}
+
+void ScDocument::RegroupFormulaCells( const ScRange& rRange )
+{
+ for( SCTAB tab = rRange.aStart.Tab(); tab <= rRange.aEnd.Tab(); ++tab )
+ for( SCCOL col = rRange.aStart.Col(); col <= rRange.aEnd.Col(); ++col )
+ RegroupFormulaCells( tab, col );
+}
+
+void ScDocument::DelayFormulaGrouping( bool delay )
+{
+ if( delay )
+ {
+ if( !pDelayedFormulaGrouping )
+ pDelayedFormulaGrouping.reset( new ScRange( ScAddress::INITIALIZE_INVALID ));
+ }
+ else
+ {
+ if( pDelayedFormulaGrouping && pDelayedFormulaGrouping->IsValid())
+ RegroupFormulaCells( *pDelayedFormulaGrouping );
+ pDelayedFormulaGrouping.reset();
+ }
+}
+
+void ScDocument::AddDelayedFormulaGroupingCell( const ScFormulaCell* cell )
+{
+ if( !pDelayedFormulaGrouping->Contains( cell->aPos ))
+ pDelayedFormulaGrouping->ExtendTo( cell->aPos );
+}
+
+void ScDocument::EnableDelayStartListeningFormulaCells( ScColumn* column, bool delay )
+{
+ if( delay )
+ {
+ if( pDelayedStartListeningFormulaCells.find( column ) == pDelayedStartListeningFormulaCells.end())
+ pDelayedStartListeningFormulaCells[ column ] = std::pair<SCROW, SCROW>( -1, -1 );
+ }
+ else
+ {
+ auto it = pDelayedStartListeningFormulaCells.find( column );
+ if( it != pDelayedStartListeningFormulaCells.end())
+ {
+ if( it->second.first != -1 )
+ {
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
+ sc::StartListeningContext aStartCxt(*this, pPosSet);
+ sc::EndListeningContext aEndCxt(*this, pPosSet);
+ column->StartListeningFormulaCells(aStartCxt, aEndCxt, it->second.first, it->second.second);
+ }
+ pDelayedStartListeningFormulaCells.erase( it );
+ }
+ }
+}
+
+bool ScDocument::IsEnabledDelayStartListeningFormulaCells( ScColumn* column ) const
+{
+ return pDelayedStartListeningFormulaCells.find( column ) != pDelayedStartListeningFormulaCells.end();
+}
+
+bool ScDocument::CanDelayStartListeningFormulaCells( ScColumn* column, SCROW row1, SCROW row2 )
+{
+ auto it = pDelayedStartListeningFormulaCells.find( column );
+ if( it == pDelayedStartListeningFormulaCells.end())
+ return false; // not enabled
+ if( it->second.first == -1 && it->second.second == -1 ) // uninitialized
+ pDelayedStartListeningFormulaCells[ column ] = std::make_pair( row1, row2 );
+ else
+ {
+ if( row1 > it->second.second + 1 || row2 < it->second.first - 1 )
+ { // two non-adjacent ranges, just bail out
+ return false;
+ }
+ it->second.first = std::min( it->second.first, row1 );
+ it->second.second = std::max( it->second.second, row2 );
+ }
+ return true;
+}
+
+void ScDocument::EnableDelayDeletingBroadcasters( bool set )
+{
+ if( bDelayedDeletingBroadcasters == set )
+ return;
+ bDelayedDeletingBroadcasters = set;
+ if( !bDelayedDeletingBroadcasters )
+ {
+ for (auto& rxTab : maTabs)
+ if (rxTab)
+ rxTab->DeleteEmptyBroadcasters();
+ }
+}
+
+bool ScDocument::HasFormulaCell( const ScRange& rRange ) const
+{
+ if (!rRange.IsValid())
+ return false;
+
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ if (pTab->HasFormulaCell(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()))
+ return true;
+ }
+
+ return false;
+}
+
+void ScDocument::EndListeningIntersectedGroup(
+ sc::EndListeningContext& rCxt, const ScAddress& rPos, std::vector<ScAddress>* pGroupPos )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->EndListeningIntersectedGroup(rCxt, rPos.Col(), rPos.Row(), pGroupPos);
+}
+
+void ScDocument::EndListeningIntersectedGroups(
+ sc::EndListeningContext& rCxt, const ScRange& rRange, std::vector<ScAddress>* pGroupPos )
+{
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ pTab->EndListeningIntersectedGroups(
+ rCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
+ pGroupPos);
+ }
+}
+
+void ScDocument::EndListeningGroups( const std::vector<ScAddress>& rPosArray )
+{
+ sc::EndListeningContext aCxt(*this);
+ for (const ScAddress& rPos : rPosArray)
+ {
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->EndListeningGroup(aCxt, rPos.Col(), rPos.Row());
+ }
+
+ aCxt.purgeEmptyBroadcasters();
+}
+
+void ScDocument::SetNeedsListeningGroups( const std::vector<ScAddress>& rPosArray )
+{
+ for (const ScAddress& rPos : rPosArray)
+ {
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ pTab->SetNeedsListeningGroup(rPos.Col(), rPos.Row());
+ }
+}
+
+namespace {
+
+class StartNeededListenersHandler
+{
+ std::shared_ptr<sc::StartListeningContext> mpCxt;
+public:
+ explicit StartNeededListenersHandler( ScDocument& rDoc ) : mpCxt(std::make_shared<sc::StartListeningContext>(rDoc)) {}
+ explicit StartNeededListenersHandler( ScDocument& rDoc, const std::shared_ptr<const sc::ColumnSet>& rpColSet ) :
+ mpCxt(std::make_shared<sc::StartListeningContext>(rDoc))
+ {
+ mpCxt->setColumnSet( rpColSet);
+ }
+
+ void operator() (const ScTableUniquePtr & p)
+ {
+ if (p)
+ p->StartListeners(*mpCxt, false);
+ }
+};
+
+}
+
+void ScDocument::StartNeededListeners()
+{
+ std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this));
+}
+
+void ScDocument::StartNeededListeners( const std::shared_ptr<const sc::ColumnSet>& rpColSet )
+{
+ std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this, rpColSet));
+}
+
+void ScDocument::StartAllListeners( const ScRange& rRange )
+{
+ if (IsClipOrUndo() || GetNoListening())
+ return;
+
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
+ sc::StartListeningContext aStartCxt(*this, pPosSet);
+ sc::EndListeningContext aEndCxt(*this, pPosSet);
+
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ pTab->StartListeningFormulaCells(
+ aStartCxt, aEndCxt,
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+ }
+}
+
+void ScDocument::finalizeOutlineImport()
+{
+ for (const auto& rxTab : maTabs)
+ {
+ ScTable* p = rxTab.get();
+ p->finalizeOutlineImport();
+ }
+}
+
+bool ScDocument::FindRangeNamesReferencingSheet( sc::UpdatedRangeNames& rIndexes,
+ SCTAB nTokenTab, const sal_uInt16 nTokenIndex,
+ SCTAB nGlobalRefTab, SCTAB nLocalRefTab, SCTAB nOldTokenTab, SCTAB nOldTokenTabReplacement,
+ bool bSameDoc, int nRecursion) const
+{
+ if (nTokenTab < -1)
+ {
+ SAL_WARN("sc.core", "ScDocument::FindRangeNamesReferencingSheet - nTokenTab < -1 : " <<
+ nTokenTab << ", nTokenIndex " << nTokenIndex << " Fix the creator!");
+#if OSL_DEBUG_LEVEL > 0
+ const ScRangeData* pData = FindRangeNameBySheetAndIndex( nTokenTab, nTokenIndex);
+ SAL_WARN_IF( pData, "sc.core", "ScDocument::FindRangeNamesReferencingSheet - named expression is: " << pData->GetName());
+#endif
+ nTokenTab = -1;
+ }
+ SCTAB nRefTab = nGlobalRefTab;
+ if (nTokenTab == nOldTokenTab)
+ {
+ nTokenTab = nOldTokenTabReplacement;
+ nRefTab = nLocalRefTab;
+ }
+ else if (nTokenTab == nOldTokenTabReplacement)
+ {
+ nRefTab = nLocalRefTab;
+ }
+
+ if (rIndexes.isNameUpdated( nTokenTab, nTokenIndex))
+ return true;
+
+ ScRangeData* pData = FindRangeNameBySheetAndIndex( nTokenTab, nTokenIndex);
+ if (!pData)
+ return false;
+
+ ScTokenArray* pCode = pData->GetCode();
+ if (!pCode)
+ return false;
+
+ bool bRef = !bSameDoc; // include every name used when copying to other doc
+ if (nRecursion < 126) // whatever... 42*3
+ {
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ bRef |= FindRangeNamesReferencingSheet( rIndexes, p->GetSheet(), p->GetIndex(),
+ nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, bSameDoc, nRecursion+1);
+ }
+ }
+ }
+
+ if (!bRef)
+ {
+ SCTAB nPosTab = pData->GetPos().Tab();
+ if (nPosTab == nOldTokenTab)
+ nPosTab = nOldTokenTabReplacement;
+ bRef = pCode->ReferencesSheet( nRefTab, nPosTab);
+ }
+ if (bRef)
+ rIndexes.setUpdatedName( nTokenTab, nTokenIndex);
+
+ return bRef;
+}
+
+namespace {
+
+enum MightReferenceSheet
+{
+ UNKNOWN,
+ NONE,
+ CODE,
+ NAME
+};
+
+MightReferenceSheet mightRangeNameReferenceSheet( ScRangeData* pData, SCTAB nRefTab)
+{
+ ScTokenArray* pCode = pData->GetCode();
+ if (!pCode)
+ return MightReferenceSheet::NONE;
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetOpCode() == ocName)
+ return MightReferenceSheet::NAME;
+ }
+
+ return pCode->ReferencesSheet( nRefTab, pData->GetPos().Tab()) ?
+ MightReferenceSheet::CODE : MightReferenceSheet::NONE;
+}
+
+ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument& rOldDoc,
+ const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal,
+ SCTAB nOldSheet, const SCTAB nNewSheet, bool bSameDoc)
+{
+ ScAddress aRangePos( pOldRangeData->GetPos());
+ if (nNewSheet >= 0)
+ aRangePos.SetTab( nNewSheet);
+ ScRangeData* pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc, &aRangePos);
+ pRangeData->SetIndex(0); // needed for insert to assign a new index
+ ScTokenArray* pRangeNameToken = pRangeData->GetCode();
+ if (bSameDoc && nNewSheet >= 0)
+ {
+ if (bGlobalNamesToLocal && nOldSheet < 0)
+ {
+ nOldSheet = rOldPos.Tab();
+ if (rNewPos.Tab() <= nOldSheet)
+ // Sheet was inserted before and references already updated.
+ ++nOldSheet;
+ }
+ pRangeNameToken->AdjustSheetLocalNameReferences( nOldSheet, nNewSheet);
+ }
+ if (!bSameDoc)
+ {
+ pRangeNameToken->ReadjustAbsolute3DReferences(rOldDoc, rNewDoc, pRangeData->GetPos(), true);
+ pRangeNameToken->AdjustAbsoluteRefs(rOldDoc, rOldPos, rNewPos, true);
+ }
+
+ bool bInserted;
+ if (nNewSheet < 0)
+ bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
+ else
+ bInserted = rNewDoc.GetRangeName(nNewSheet)->insert(pRangeData);
+
+ return bInserted ? pRangeData : nullptr;
+}
+
+struct SheetIndex
+{
+ SCTAB mnSheet;
+ sal_uInt16 mnIndex;
+
+ SheetIndex( SCTAB nSheet, sal_uInt16 nIndex ) : mnSheet(nSheet < -1 ? -1 : nSheet), mnIndex(nIndex) {}
+ bool operator<( const SheetIndex& r ) const
+ {
+ // Ascending order sheet, index
+ if (mnSheet < r.mnSheet)
+ return true;
+ if (mnSheet == r.mnSheet)
+ return mnIndex < r.mnIndex;
+ return false;
+ }
+};
+typedef std::map< SheetIndex, SheetIndex > SheetIndexMap;
+
+ScRangeData* copyRangeNames( SheetIndexMap& rSheetIndexMap, std::vector<ScRangeData*>& rRangeDataVec,
+ const sc::UpdatedRangeNames& rReferencingNames, SCTAB nTab,
+ const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument& rOldDoc,
+ const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal,
+ const SCTAB nOldSheet, const SCTAB nNewSheet, bool bSameDoc)
+{
+ ScRangeData* pRangeData = nullptr;
+ const ScRangeName* pOldRangeName = (nTab < 0 ? rOldDoc.GetRangeName() : rOldDoc.GetRangeName(nTab));
+ if (pOldRangeName)
+ {
+ const ScRangeName* pNewRangeName = (nNewSheet < 0 ? rNewDoc.GetRangeName() : rNewDoc.GetRangeName(nNewSheet));
+ sc::UpdatedRangeNames::NameIndicesType aSet( rReferencingNames.getUpdatedNames(nTab));
+ for (auto const & rIndex : aSet)
+ {
+ const ScRangeData* pCopyData = pOldRangeName->findByIndex(rIndex);
+ if (pCopyData)
+ {
+ // Match the original pOldRangeData to adapt the current
+ // token's values later. For that no check for an already
+ // copied name is needed as we only enter here if there was
+ // none.
+ if (pCopyData == pOldRangeData)
+ {
+ pRangeData = copyRangeName( pCopyData, rNewDoc, rOldDoc, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc);
+ if (pRangeData)
+ {
+ rRangeDataVec.push_back(pRangeData);
+ rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
+ SheetIndex( nNewSheet, pRangeData->GetIndex())));
+ }
+ }
+ else
+ {
+ // First check if the name is already available as copy.
+ const ScRangeData* pFoundData = pNewRangeName->findByUpperName( pCopyData->GetUpperName());
+ if (pFoundData)
+ {
+ // Just add the resulting sheet/index mapping.
+ rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
+ SheetIndex( nNewSheet, pFoundData->GetIndex())));
+ }
+ else
+ {
+ ScRangeData* pTmpData = copyRangeName( pCopyData, rNewDoc, rOldDoc, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc);
+ if (pTmpData)
+ {
+ rRangeDataVec.push_back(pTmpData);
+ rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
+ SheetIndex( nNewSheet, pTmpData->GetIndex())));
+ }
+ }
+ }
+ }
+ }
+ }
+ return pRangeData;
+}
+
+} // namespace
+
+bool ScDocument::CopyAdjustRangeName( SCTAB& rSheet, sal_uInt16& rIndex, ScRangeData*& rpRangeData,
+ ScDocument& rNewDoc, const ScAddress& rNewPos, const ScAddress& rOldPos, const bool bGlobalNamesToLocal,
+ const bool bUsedByFormula ) const
+{
+ ScDocument* pThis = const_cast<ScDocument*>(this);
+ const bool bSameDoc = (rNewDoc.GetPool() == pThis->GetPool());
+ if (bSameDoc && ((rSheet < 0 && !bGlobalNamesToLocal) || (rSheet >= 0
+ && (rSheet != rOldPos.Tab() || (IsClipboard() && pThis->IsCutMode())))))
+ // Same doc and global name, if not copied to local name, or
+ // sheet-local name on other sheet stays the same. Sheet-local on
+ // same sheet also in a clipboard cut&paste / move operation.
+ return false;
+
+ // Ensure we don't fiddle with the references until exit.
+ const SCTAB nOldSheet = rSheet;
+ const sal_uInt16 nOldIndex = rIndex;
+
+ SAL_WARN_IF( !bSameDoc && nOldSheet >= 0 && nOldSheet != rOldPos.Tab(),
+ "sc.core", "adjustCopyRangeName - sheet-local name was on other sheet in other document");
+ /* TODO: can we do something about that? e.g. loop over sheets? */
+
+ OUString aRangeName;
+ ScRangeData* pOldRangeData = nullptr;
+
+ // XXX bGlobalNamesToLocal is also a synonym for copied sheet.
+ bool bInsertingBefore = (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() <= rOldPos.Tab());
+
+ // The Tab where an old local name is to be found or that a global name
+ // references. May differ below from nOldSheet if a sheet was inserted
+ // before the old position. Global names and local names other than on the
+ // old sheet or new sheet are already updated, local names on the old sheet
+ // or inserted sheet will be updated later. Confusing stuff. Watch out.
+ SCTAB nOldTab = (nOldSheet < 0 ? rOldPos.Tab() : nOldSheet);
+ if (bInsertingBefore)
+ // Sheet was already inserted before old position.
+ ++nOldTab;
+
+ // Search the name of the RangeName.
+ if (nOldSheet >= 0)
+ {
+ const ScRangeName* pNames = GetRangeName(nOldTab);
+ pOldRangeData = pNames ? pNames->findByIndex(nOldIndex) : nullptr;
+ if (!pOldRangeData)
+ return false; // might be an error in the formula array
+ aRangeName = pOldRangeData->GetUpperName();
+ }
+ else
+ {
+ pOldRangeData = GetRangeName()->findByIndex(nOldIndex);
+ if (!pOldRangeData)
+ return false; // might be an error in the formula array
+ aRangeName = pOldRangeData->GetUpperName();
+ }
+
+ // Find corresponding range name in new document.
+ // First search for local range name then global range names.
+ SCTAB nNewSheet = rNewPos.Tab();
+ ScRangeName* pNewNames = rNewDoc.GetRangeName(nNewSheet);
+ // Search local range names.
+ if (pNewNames)
+ {
+ rpRangeData = pNewNames->findByUpperName(aRangeName);
+ }
+ // Search global range names.
+ if (!rpRangeData && !bGlobalNamesToLocal)
+ {
+ nNewSheet = -1;
+ pNewNames = rNewDoc.GetRangeName();
+ if (pNewNames)
+ rpRangeData = pNewNames->findByUpperName(aRangeName);
+ }
+ // If no range name was found copy it.
+ if (!rpRangeData)
+ {
+ // Do not copy global name if it doesn't reference sheet or is not used
+ // by a formula copied to another document.
+ bool bEarlyBailOut = (nOldSheet < 0 && (bSameDoc || !bUsedByFormula));
+ MightReferenceSheet eMightReference = mightRangeNameReferenceSheet( pOldRangeData, nOldTab);
+ if (bEarlyBailOut && eMightReference == MightReferenceSheet::NONE)
+ return false;
+
+ if (eMightReference == MightReferenceSheet::NAME)
+ {
+ // Name these to clarify what is passed where.
+ const SCTAB nGlobalRefTab = nOldTab;
+ const SCTAB nLocalRefTab = (bInsertingBefore ? nOldTab-1 : nOldTab);
+ const SCTAB nOldTokenTab = (nOldSheet < 0 ? (bInsertingBefore ? nOldTab-1 : nOldTab) : nOldSheet);
+ const SCTAB nOldTokenTabReplacement = nOldTab;
+ sc::UpdatedRangeNames aReferencingNames;
+ FindRangeNamesReferencingSheet( aReferencingNames, nOldSheet, nOldIndex,
+ nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, bSameDoc, 0);
+ if (bEarlyBailOut && aReferencingNames.isEmpty(-1) && aReferencingNames.isEmpty(nOldTokenTabReplacement))
+ return false;
+
+ SheetIndexMap aSheetIndexMap;
+ std::vector<ScRangeData*> aRangeDataVec;
+ if (!aReferencingNames.isEmpty(nOldTokenTabReplacement))
+ {
+ const SCTAB nTmpOldSheet = (nOldSheet < 0 ? nOldTab : nOldSheet);
+ nNewSheet = rNewPos.Tab();
+ rpRangeData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, nOldTab,
+ pOldRangeData, rNewDoc, *this, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nTmpOldSheet, nNewSheet, bSameDoc);
+ }
+ if ((bGlobalNamesToLocal || !bSameDoc) && !aReferencingNames.isEmpty(-1))
+ {
+ const SCTAB nTmpOldSheet = -1;
+ const SCTAB nTmpNewSheet = (bGlobalNamesToLocal ? rNewPos.Tab() : -1);
+ ScRangeData* pTmpData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, -1,
+ pOldRangeData, rNewDoc, *this, rNewPos, rOldPos,
+ bGlobalNamesToLocal, nTmpOldSheet, nTmpNewSheet, bSameDoc);
+ if (!rpRangeData)
+ {
+ rpRangeData = pTmpData;
+ nNewSheet = nTmpNewSheet;
+ }
+ }
+
+ // Adjust copied nested names to new sheet/index.
+ for (auto & iRD : aRangeDataVec)
+ {
+ ScTokenArray* pCode = iRD->GetCode();
+ if (pCode)
+ {
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ for (formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ auto it = aSheetIndexMap.find( SheetIndex( p->GetSheet(), p->GetIndex()));
+ if (it != aSheetIndexMap.end())
+ {
+ p->SetSheet( it->second.mnSheet);
+ p->SetIndex( it->second.mnIndex);
+ }
+ else if (!bSameDoc)
+ {
+ SAL_WARN("sc.core","adjustCopyRangeName - mapping to new name in other doc missing");
+ p->SetIndex(0); // #NAME? error instead of arbitrary name.
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ nNewSheet = ((nOldSheet < 0 && !bGlobalNamesToLocal) ? -1 : rNewPos.Tab());
+ rpRangeData = copyRangeName( pOldRangeData, rNewDoc, *this, rNewPos, rOldPos, bGlobalNamesToLocal,
+ nOldSheet, nNewSheet, bSameDoc);
+ }
+
+ if (rpRangeData && !rNewDoc.IsClipOrUndo())
+ {
+ ScDocShell* pDocSh = rNewDoc.GetDocumentShell();
+ if (pDocSh)
+ pDocSh->SetAreasChangedNeedBroadcast();
+ }
+ }
+
+ rSheet = nNewSheet;
+ rIndex = rpRangeData ? rpRangeData->GetIndex() : 0; // 0 means not inserted
+ return true;
+}
+
+bool ScDocument::IsEditActionAllowed(
+ sc::ColRowEditAction eAction, SCTAB nTab, SCCOLROW nStart, SCCOLROW nEnd ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ return pTab->IsEditActionAllowed(eAction, nStart, nEnd);
+}
+
+bool ScDocument::IsEditActionAllowed(
+ sc::ColRowEditAction eAction, const ScMarkData& rMark, SCCOLROW nStart, SCCOLROW nEnd ) const
+{
+ return std::all_of(rMark.begin(), rMark.end(),
+ [this, &eAction, &nStart, &nEnd](const SCTAB& rTab) { return IsEditActionAllowed(eAction, rTab, nStart, nEnd); });
+}
+
+std::optional<sc::ColumnIterator> ScDocument::GetColumnIterator( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return {};
+
+ return pTab->GetColumnIterator(nCol, nRow1, nRow2);
+}
+
+void ScDocument::CreateColumnIfNotExists( SCTAB nTab, SCCOL nCol )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->CreateColumnIfNotExists(nCol);
+}
+
+bool ScDocument::EnsureFormulaCellResults( const ScRange& rRange, bool bSkipRunning )
+{
+ bool bAnyDirty = false;
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ bool bRet = pTab->EnsureFormulaCellResults(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), bSkipRunning);
+ bAnyDirty = bAnyDirty || bRet;
+ }
+
+ return bAnyDirty;
+}
+
+sc::ExternalDataMapper& ScDocument::GetExternalDataMapper()
+{
+ if (!mpDataMapper)
+ mpDataMapper.reset(new sc::ExternalDataMapper(*this));
+
+ return *mpDataMapper;
+}
+
+void ScDocument::StoreTabToCache(SCTAB nTab, SvStream& rStrm) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->StoreToCache(rStrm);
+}
+
+void ScDocument::RestoreTabFromCache(SCTAB nTab, SvStream& rStrm)
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->RestoreFromCache(rStrm);
+}
+
+OString ScDocument::dumpSheetGeomData(SCTAB nTab, bool bColumns, SheetGeomType eGeomType)
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return ""_ostr;
+
+ return pTab->dumpSheetGeomData(bColumns, eGeomType);
+}
+
+SCCOL ScDocument::GetLOKFreezeCol(SCTAB nTab) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return -1;
+
+ return pTab->GetLOKFreezeCol();
+}
+SCROW ScDocument::GetLOKFreezeRow(SCTAB nTab) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return -1;
+
+ return pTab->GetLOKFreezeRow();
+}
+
+bool ScDocument::SetLOKFreezeCol(SCCOL nFreezeCol, SCTAB nTab)
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ return pTab->SetLOKFreezeCol(nFreezeCol);
+}
+
+bool ScDocument::SetLOKFreezeRow(SCROW nFreezeRow, SCTAB nTab)
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ return pTab->SetLOKFreezeRow(nFreezeRow);
+}
+
+std::set<SCCOL> ScDocument::QueryColumnsWithFormulaCells( SCTAB nTab ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return std::set<SCCOL>{};
+
+ return pTab->QueryColumnsWithFormulaCells();
+}
+
+void ScDocument::CheckIntegrity( SCTAB nTab ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->CheckIntegrity();
+}
+
+sc::BroadcasterState ScDocument::GetBroadcasterState() const
+{
+ sc::BroadcasterState aState;
+
+ for (const auto& xTab : maTabs)
+ xTab->CollectBroadcasterState(aState);
+
+ if (pBASM)
+ pBASM->CollectBroadcasterState(aState);
+
+ return aState;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx
new file mode 100644
index 0000000000..a6c25a1bd5
--- /dev/null
+++ b/sc/source/core/data/documentimport.cxx
@@ -0,0 +1,856 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <documentimport.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <formulacell.hxx>
+#include <docoptio.hxx>
+#include <mtvelements.hxx>
+#include <tokenarray.hxx>
+#include <stringutil.hxx>
+#include <compiler.hxx>
+#include <paramisc.hxx>
+#include <listenercontext.hxx>
+#include <attarray.hxx>
+#include <sharedformula.hxx>
+#include <bcaslot.hxx>
+#include <scopetools.hxx>
+#include <numformat.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <svl/languageoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <unordered_map>
+
+namespace {
+
+struct ColAttr
+{
+ bool mbLatinNumFmtOnly;
+
+ ColAttr() : mbLatinNumFmtOnly(false) {}
+};
+
+struct TabAttr
+{
+ std::vector<ColAttr> maCols;
+};
+
+}
+
+struct ScDocumentImportImpl
+{
+ ScDocument& mrDoc;
+ sc::StartListeningContext maListenCxt;
+ std::vector<sc::TableColumnBlockPositionSet> maBlockPosSet;
+ SvtScriptType mnDefaultScriptNumeric;
+ bool mbFuzzing;
+ std::vector<TabAttr> maTabAttrs;
+ std::unordered_map<sal_uInt32, bool> maIsLatinScriptMap;
+
+ explicit ScDocumentImportImpl(ScDocument& rDoc) :
+ mrDoc(rDoc),
+ maListenCxt(rDoc),
+ mnDefaultScriptNumeric(SvtScriptType::UNKNOWN),
+ mbFuzzing(utl::ConfigManager::IsFuzzing())
+ {}
+
+ bool isValid( size_t nTab, size_t nCol )
+ {
+ return (nTab <= o3tl::make_unsigned(MAXTAB) && nCol <= o3tl::make_unsigned(mrDoc.MaxCol()));
+ }
+
+ ColAttr* getColAttr( size_t nTab, size_t nCol )
+ {
+ if (!isValid(nTab, nCol))
+ return nullptr;
+
+ if (nTab >= maTabAttrs.size())
+ maTabAttrs.resize(nTab+1);
+
+ TabAttr& rTab = maTabAttrs[nTab];
+ if (nCol >= rTab.maCols.size())
+ rTab.maCols.resize(nCol+1);
+
+ return &rTab.maCols[nCol];
+ }
+
+ sc::ColumnBlockPosition* getBlockPosition( SCTAB nTab, SCCOL nCol )
+ {
+ if (!isValid(nTab, nCol))
+ return nullptr;
+
+ if (o3tl::make_unsigned(nTab) >= maBlockPosSet.size())
+ {
+ for (SCTAB i = maBlockPosSet.size(); i <= nTab; ++i)
+ maBlockPosSet.emplace_back(mrDoc, i);
+ }
+
+ sc::TableColumnBlockPositionSet& rTab = maBlockPosSet[nTab];
+ return rTab.getBlockPosition(nCol);
+ }
+
+ void invalidateBlockPositionSet(SCTAB nTab)
+ {
+ if (o3tl::make_unsigned(nTab) >= maBlockPosSet.size())
+ return;
+
+ sc::TableColumnBlockPositionSet& rTab = maBlockPosSet[nTab];
+ rTab.invalidate();
+ }
+
+ void initForSheets()
+ {
+ size_t n = mrDoc.GetTableCount();
+ for (size_t i = maBlockPosSet.size(); i < n; ++i)
+ maBlockPosSet.emplace_back(mrDoc, i);
+
+ if (maTabAttrs.size() < n)
+ maTabAttrs.resize(n);
+ }
+};
+
+ScDocumentImport::Attrs::Attrs() : mbLatinNumFmtOnly(false) {}
+
+ScDocumentImport::Attrs::~Attrs() {}
+
+ScDocumentImport::ScDocumentImport(ScDocument& rDoc) : mpImpl(new ScDocumentImportImpl(rDoc)) {}
+
+ScDocumentImport::~ScDocumentImport()
+{
+}
+
+ScDocument& ScDocumentImport::getDoc()
+{
+ return mpImpl->mrDoc;
+}
+
+const ScDocument& ScDocumentImport::getDoc() const
+{
+ return mpImpl->mrDoc;
+}
+
+void ScDocumentImport::initForSheets()
+{
+ mpImpl->initForSheets();
+}
+
+void ScDocumentImport::setDefaultNumericScript(SvtScriptType nScript)
+{
+ mpImpl->mnDefaultScriptNumeric = nScript;
+}
+
+void ScDocumentImport::setCellStyleToSheet(SCTAB nTab, const ScStyleSheet& rStyle)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->ApplyStyleArea(0, 0, getDoc().MaxCol(), getDoc().MaxRow(), rStyle);
+}
+
+SCTAB ScDocumentImport::getSheetIndex(const OUString& rName) const
+{
+ SCTAB nTab = -1;
+ if (!mpImpl->mrDoc.GetTable(rName, nTab))
+ return -1;
+
+ return nTab;
+}
+
+SCTAB ScDocumentImport::getSheetCount() const
+{
+ return mpImpl->mrDoc.maTabs.size();
+}
+
+bool ScDocumentImport::appendSheet(const OUString& rName)
+{
+ SCTAB nTabCount = mpImpl->mrDoc.maTabs.size();
+ if (!ValidTab(nTabCount))
+ return false;
+
+ mpImpl->mrDoc.maTabs.emplace_back(new ScTable(mpImpl->mrDoc, nTabCount, rName));
+ return true;
+}
+
+void ScDocumentImport::setSheetName(SCTAB nTab, const OUString& rName)
+{
+ mpImpl->mrDoc.SetTabNameOnLoad(nTab, rName);
+}
+
+void ScDocumentImport::setOriginDate(sal_uInt16 nYear, sal_uInt16 nMonth, sal_uInt16 nDay)
+{
+ if (!mpImpl->mrDoc.pDocOptions)
+ mpImpl->mrDoc.pDocOptions.reset( new ScDocOptions );
+
+ mpImpl->mrDoc.pDocOptions->SetDate(nDay, nMonth, nYear);
+}
+
+void ScDocumentImport::invalidateBlockPositionSet(SCTAB nTab)
+{
+ mpImpl->invalidateBlockPositionSet(nTab);
+}
+
+void ScDocumentImport::setAutoInput(const ScAddress& rPos, const OUString& rStr, const ScSetStringParam* pStringParam)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ // If ScSetStringParam was given, ScColumn::ParseString() shall take care
+ // of checking. Ensure caller said so.
+ assert(!pStringParam || pStringParam->mbCheckLinkFormula);
+
+ ScCellValue aCell;
+ pTab->aCol[rPos.Col()].ParseString(
+ aCell, rPos.Row(), rPos.Tab(), rStr, mpImpl->mrDoc.GetAddressConvention(), pStringParam);
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING:
+ // string is copied.
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), *aCell.getSharedString());
+ break;
+ case CELLTYPE_EDIT:
+ // Cell takes the ownership of the text object.
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.releaseEditText());
+ break;
+ case CELLTYPE_VALUE:
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.getDouble());
+ break;
+ case CELLTYPE_FORMULA:
+ if (!pStringParam)
+ mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *aCell.getFormula()->GetCode());
+ // This formula cell instance is directly placed in the document without copying.
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aCell.releaseFormula());
+ break;
+ default:
+ pBlockPos->miCellPos = rCells.set_empty(pBlockPos->miCellPos, rPos.Row(), rPos.Row());
+ }
+}
+
+void ScDocumentImport::setNumericCell(const ScAddress& rPos, double fVal)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), fVal);
+}
+
+void ScDocumentImport::setStringCell(const ScAddress& rPos, const OUString& rStr)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ svl::SharedString aSS = mpImpl->mrDoc.GetSharedStringPool().intern(rStr);
+ if (!aSS.getData())
+ return;
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aSS);
+}
+
+void ScDocumentImport::setEditCell(const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ pEditText->NormalizeString(mpImpl->mrDoc.GetSharedStringPool());
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), pEditText.release());
+}
+
+void ScDocumentImport::setFormulaCell(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
+ const double* pResult )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ std::unique_ptr<ScFormulaCell> pFC =
+ std::make_unique<ScFormulaCell>(mpImpl->mrDoc, rPos, rFormula, eGrammar);
+
+ mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode());
+
+ if (pResult)
+ {
+ // Set cached result to this formula cell.
+ pFC->SetResultDouble(*pResult);
+ }
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos =
+ rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release());
+}
+
+void ScDocumentImport::setFormulaCell(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
+ const OUString& rResult )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ std::unique_ptr<ScFormulaCell> pFC =
+ std::make_unique<ScFormulaCell>(mpImpl->mrDoc, rPos, rFormula, eGrammar);
+
+ mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode());
+
+ // Set cached result to this formula cell.
+ pFC->SetHybridString(mpImpl->mrDoc.GetSharedStringPool().intern(rResult));
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos =
+ rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release());
+}
+
+void ScDocumentImport::setFormulaCell(const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ std::unique_ptr<ScFormulaCell> pFC =
+ std::make_unique<ScFormulaCell>(mpImpl->mrDoc, rPos, std::move(pArray));
+
+ mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pFC->GetCode());
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos =
+ rCells.set(pBlockPos->miCellPos, rPos.Row(), pFC.release());
+}
+
+void ScDocumentImport::setFormulaCell(const ScAddress& rPos, ScFormulaCell* pCell)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ if (pCell)
+ mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+
+ sc::CellStoreType::position_type aPos = rCells.position(rPos.Row());
+ if (aPos.first != rCells.end() && aPos.first->type == sc::element_type_formula)
+ {
+ ScFormulaCell* p = sc::formula_block::at(*aPos.first->data, aPos.second);
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *p);
+ }
+
+ pBlockPos->miCellPos =
+ rCells.set(pBlockPos->miCellPos, rPos.Row(), pCell);
+}
+
+void ScDocumentImport::setMatrixCells(
+ const ScRange& rRange, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram)
+{
+ const ScAddress& rBasePos = rRange.aStart;
+
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rBasePos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rBasePos.Tab(), rBasePos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ if (utl::ConfigManager::IsFuzzing()) //just too slow
+ return;
+
+ sc::CellStoreType& rCells = pTab->aCol[rBasePos.Col()].maCells;
+
+ // Set the master cell.
+ ScFormulaCell* pCell = new ScFormulaCell(mpImpl->mrDoc, rBasePos, rArray, eGram, ScMatrixMode::Formula);
+
+ mpImpl->mrDoc.CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+
+ pBlockPos->miCellPos =
+ rCells.set(pBlockPos->miCellPos, rBasePos.Row(), pCell);
+
+ // Matrix formulas currently need re-calculation on import.
+ pCell->SetMatColsRows(
+ rRange.aEnd.Col()-rRange.aStart.Col()+1, rRange.aEnd.Row()-rRange.aStart.Row()+1);
+
+ // Set the reference cells.
+ ScSingleRefData aRefData;
+ aRefData.InitFlags();
+ aRefData.SetColRel(true);
+ aRefData.SetRowRel(true);
+ aRefData.SetTabRel(true);
+ aRefData.SetAddress(mpImpl->mrDoc.GetSheetLimits(), rBasePos, rBasePos);
+
+ ScTokenArray aArr(mpImpl->mrDoc); // consists only of one single reference token.
+ formula::FormulaToken* t = aArr.AddMatrixSingleReference(aRefData);
+
+ ScAddress aPos = rBasePos;
+ for (SCROW nRow = rRange.aStart.Row()+1; nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ // Token array must be cloned so that each formula cell receives its own copy.
+ aPos.SetRow(nRow);
+ // Reference in each cell must point to the origin cell relative to the current cell.
+ aRefData.SetAddress(mpImpl->mrDoc.GetSheetLimits(), rBasePos, aPos);
+ *t->GetSingleRef() = aRefData;
+ ScTokenArray aTokArr(aArr.CloneValue());
+ pCell = new ScFormulaCell(mpImpl->mrDoc, aPos, aTokArr, eGram, ScMatrixMode::Reference);
+ pBlockPos->miCellPos =
+ rCells.set(pBlockPos->miCellPos, aPos.Row(), pCell);
+ }
+
+ for (SCCOL nCol = rRange.aStart.Col()+1; nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ pBlockPos = mpImpl->getBlockPosition(rBasePos.Tab(), nCol);
+ if (!pBlockPos)
+ return;
+
+ sc::CellStoreType& rColCells = pTab->aCol[nCol].maCells;
+
+ aPos.SetCol(nCol);
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ aPos.SetRow(nRow);
+ aRefData.SetAddress(mpImpl->mrDoc.GetSheetLimits(), rBasePos, aPos);
+ *t->GetSingleRef() = aRefData;
+ ScTokenArray aTokArr(aArr.CloneValue());
+ pCell = new ScFormulaCell(mpImpl->mrDoc, aPos, aTokArr, eGram, ScMatrixMode::Reference);
+ pBlockPos->miCellPos =
+ rColCells.set(pBlockPos->miCellPos, aPos.Row(), pCell);
+ }
+ }
+}
+
+void ScDocumentImport::setTableOpCells(const ScRange& rRange, const ScTabOpParam& rParam)
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ SCROW nRow2 = rRange.aEnd.Row();
+
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ ScDocument& rDoc = mpImpl->mrDoc;
+ ScRefAddress aRef;
+ OUStringBuffer aFormulaBuf("="
+ + ScCompiler::GetNativeSymbol(ocTableOp)
+ + ScCompiler::GetNativeSymbol(ocOpen));
+
+ OUString aSep = ScCompiler::GetNativeSymbol(ocSep);
+ if (rParam.meMode == ScTabOpParam::Column) // column only
+ {
+ aRef.Set(rParam.aRefFormulaCell.GetAddress(), true, false, false);
+ aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)
+ + aSep
+ + rParam.aRefColCell.GetRefString(rDoc, nTab)
+ + aSep);
+ aRef.Set(nCol1, nRow1, nTab, false, true, true);
+ aFormulaBuf.append(aRef.GetRefString(rDoc, nTab));
+ nCol1++;
+ nCol2 = std::min( nCol2, static_cast<SCCOL>(rParam.aRefFormulaEnd.Col() -
+ rParam.aRefFormulaCell.Col() + nCol1 + 1));
+ }
+ else if (rParam.meMode == ScTabOpParam::Row) // row only
+ {
+ aRef.Set(rParam.aRefFormulaCell.GetAddress(), false, true, false);
+ aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)
+ + aSep
+ + rParam.aRefRowCell.GetRefString(rDoc, nTab)
+ + aSep);
+ aRef.Set(nCol1, nRow1, nTab, true, false, true);
+ aFormulaBuf.append(aRef.GetRefString(rDoc, nTab));
+ ++nRow1;
+ nRow2 = std::min(
+ nRow2, rParam.aRefFormulaEnd.Row() - rParam.aRefFormulaCell.Row() + nRow1 + 1);
+ }
+ else // both
+ {
+ aFormulaBuf.append(rParam.aRefFormulaCell.GetRefString(rDoc, nTab)
+ + aSep
+ + rParam.aRefColCell.GetRefString(rDoc, nTab)
+ + aSep);
+ aRef.Set(nCol1, nRow1 + 1, nTab, false, true, true);
+ aFormulaBuf.append(aRef.GetRefString(rDoc, nTab)
+ + aSep
+ + rParam.aRefRowCell.GetRefString(rDoc, nTab)
+ + aSep);
+ aRef.Set(nCol1 + 1, nRow1, nTab, true, false, true);
+ aFormulaBuf.append(aRef.GetRefString(rDoc, nTab));
+ ++nCol1;
+ ++nRow1;
+ }
+
+ aFormulaBuf.append(ScCompiler::GetNativeSymbol(ocClose));
+
+ ScFormulaCell aRefCell(
+ rDoc, ScAddress(nCol1, nRow1, nTab), aFormulaBuf.makeStringAndClear(),
+ formula::FormulaGrammar::GRAM_NATIVE, ScMatrixMode::NONE);
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(nTab, nCol);
+
+ if (!pBlockPos)
+ // Something went horribly wrong.
+ return;
+
+ sc::CellStoreType& rColCells = pTab->aCol[nCol].maCells;
+
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ ScAddress aPos(nCol, nRow, nTab);
+ ScFormulaCell* pCell = new ScFormulaCell(aRefCell, rDoc, aPos);
+ pBlockPos->miCellPos =
+ rColCells.set(pBlockPos->miCellPos, nRow, pCell);
+ }
+ }
+}
+
+void ScDocumentImport::fillDownCells(const ScAddress& rPos, SCROW nFillSize)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ sc::ColumnBlockPosition* pBlockPos = mpImpl->getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ sc::CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ ScRefCellValue aRefCell = pTab->aCol[rPos.Col()].GetCellValue(*pBlockPos, rPos.Row());
+
+ switch (aRefCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ std::vector<double> aCopied(nFillSize, aRefCell.getDouble());
+ pBlockPos->miCellPos = rCells.set(
+ pBlockPos->miCellPos, rPos.Row()+1, aCopied.begin(), aCopied.end());
+ break;
+ }
+ case CELLTYPE_STRING:
+ {
+ std::vector<svl::SharedString> aCopied(nFillSize, *aRefCell.getSharedString());
+ pBlockPos->miCellPos = rCells.set(
+ pBlockPos->miCellPos, rPos.Row()+1, aCopied.begin(), aCopied.end());
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void ScDocumentImport::setAttrEntries( SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, Attrs&& rAttrs )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol )
+ {
+ ColAttr* pColAttr = mpImpl->getColAttr(nTab, nCol);
+ if (pColAttr)
+ pColAttr->mbLatinNumFmtOnly = rAttrs.mbLatinNumFmtOnly;
+ }
+
+ pTab->SetAttrEntries( nColStart, nColEnd, std::move( rAttrs.mvData ));
+}
+
+void ScDocumentImport::setRowsVisible(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, bool bVisible)
+{
+ if (!bVisible)
+ {
+ getDoc().ShowRows(nRowStart, nRowEnd, nTab, false);
+ getDoc().SetDrawPageSize(nTab);
+ getDoc().UpdatePageBreaks( nTab );
+ }
+ else
+ {
+ assert(false);
+ }
+}
+
+void ScDocumentImport::setMergedCells(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->SetMergedCells(nCol1, nRow1, nCol2, nRow2);
+}
+
+namespace {
+
+class CellStoreInitializer
+{
+ // The pimpl pattern here is intentional.
+ //
+ // The problem with having the attributes in CellStoreInitializer
+ // directly is that, as a functor, it might be copied around. In
+ // that case miPos in _copied_ object points to maAttrs in the
+ // original object, not in the copy. So later, deep in mdds, we end
+ // up comparing iterators from different sequences.
+ //
+ // This could be solved by defining copy constructor and operator=,
+ // but given the limited usage of the class, I think it is simpler
+ // to let copies share the state.
+ struct Impl
+ {
+ sc::CellTextAttrStoreType maAttrs;
+ sc::CellTextAttrStoreType::iterator miPos;
+ SvtScriptType mnScriptNumeric;
+
+ explicit Impl(const ScSheetLimits& rSheetLimits, const SvtScriptType nScriptNumeric)
+ : maAttrs(rSheetLimits.GetMaxRowCount()), miPos(maAttrs.begin()), mnScriptNumeric(nScriptNumeric)
+ {}
+ };
+
+ ScDocumentImportImpl& mrDocImpl;
+ SCTAB mnTab;
+ SCCOL mnCol;
+
+public:
+ CellStoreInitializer( ScDocumentImportImpl& rDocImpl, SCTAB nTab, SCCOL nCol ) :
+ mrDocImpl(rDocImpl),
+ mnTab(nTab),
+ mnCol(nCol),
+ mpImpl(std::make_shared<Impl>(rDocImpl.mrDoc.GetSheetLimits(), mrDocImpl.mnDefaultScriptNumeric))
+ {}
+
+ std::shared_ptr<Impl> mpImpl;
+
+ void operator() (const sc::CellStoreType::value_type& node)
+ {
+ if (node.type == sc::element_type_empty)
+ return;
+
+ // Fill with default values for non-empty cell segments.
+ sc::CellTextAttr aDefault;
+ switch (node.type)
+ {
+ case sc::element_type_numeric:
+ {
+ aDefault.mnScriptType = mpImpl->mnScriptNumeric;
+ const ColAttr* p = mrDocImpl.getColAttr(mnTab, mnCol);
+ if (p && p->mbLatinNumFmtOnly)
+ aDefault.mnScriptType = SvtScriptType::LATIN;
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ const ColAttr* p = mrDocImpl.getColAttr(mnTab, mnCol);
+ if (p && p->mbLatinNumFmtOnly)
+ {
+ // We can assume latin script type if the block only
+ // contains formula cells with numeric results.
+ ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0);
+ ScFormulaCell** ppEnd = pp + node.size;
+ bool bNumResOnly = true;
+ for (; pp != ppEnd; ++pp)
+ {
+ const ScFormulaCell& rCell = **pp;
+ if (!rCell.IsValueNoError())
+ {
+ bNumResOnly = false;
+ break;
+ }
+ }
+
+ if (bNumResOnly)
+ aDefault.mnScriptType = SvtScriptType::LATIN;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+
+ std::vector<sc::CellTextAttr> aDefaults(node.size, aDefault);
+ mpImpl->miPos = mpImpl->maAttrs.set(mpImpl->miPos, node.position, aDefaults.begin(), aDefaults.end());
+
+ if (node.type != sc::element_type_formula)
+ return;
+
+ if (mrDocImpl.mbFuzzing) // skip listening when fuzzing
+ return;
+
+ // Have all formula cells start listening to the document.
+ ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0);
+ ScFormulaCell** ppEnd = pp + node.size;
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell& rFC = **pp;
+ if (rFC.IsSharedTop())
+ {
+ // Register formula cells as a group.
+ sc::SharedFormulaUtil::startListeningAsGroup(mrDocImpl.maListenCxt, pp);
+ pp += rFC.GetSharedLength() - 1; // Move to the last one in the group.
+ }
+ else
+ rFC.StartListeningTo(mrDocImpl.maListenCxt);
+ }
+ }
+
+ void swap(sc::CellTextAttrStoreType& rAttrs)
+ {
+ mpImpl->maAttrs.swap(rAttrs);
+ }
+};
+
+}
+
+void ScDocumentImport::finalize()
+{
+ // Populate the text width and script type arrays in all columns. Also
+ // activate all formula cells.
+ for (auto& rxTab : mpImpl->mrDoc.maTabs)
+ {
+ if (!rxTab)
+ continue;
+
+ ScTable& rTab = *rxTab;
+ SCCOL nNumCols = rTab.aCol.size();
+ for (SCCOL nColIdx = 0; nColIdx < nNumCols; ++nColIdx)
+ initColumn(rTab.aCol[nColIdx]);
+ }
+
+ mpImpl->mrDoc.finalizeOutlineImport();
+}
+
+void ScDocumentImport::initColumn(ScColumn& rCol)
+{
+ rCol.RegroupFormulaCells();
+
+ CellStoreInitializer aFunc(*mpImpl, rCol.nTab, rCol.nCol);
+ std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc);
+ aFunc.swap(rCol.maCellTextAttrs);
+
+ rCol.CellStorageModified();
+}
+
+namespace {
+
+class CellStoreAfterImportBroadcaster
+{
+public:
+
+ CellStoreAfterImportBroadcaster() {}
+
+ void operator() (const sc::CellStoreType::value_type& node)
+ {
+ if (node.type == sc::element_type_formula)
+ {
+ // Broadcast all formula cells marked for recalc.
+ ScFormulaCell** pp = &sc::formula_block::at(*node.data, 0);
+ ScFormulaCell** ppEnd = pp + node.size;
+ for (; pp != ppEnd; ++pp)
+ {
+ if ((*pp)->GetCode()->IsRecalcModeMustAfterImport())
+ (*pp)->SetDirty();
+ }
+ }
+ }
+};
+
+}
+
+void ScDocumentImport::broadcastRecalcAfterImport()
+{
+ sc::AutoCalcSwitch aACSwitch( mpImpl->mrDoc, false);
+ ScBulkBroadcast aBulkBroadcast( mpImpl->mrDoc.GetBASM(), SfxHintId::ScDataChanged);
+
+ for (auto& rxTab : mpImpl->mrDoc.maTabs)
+ {
+ if (!rxTab)
+ continue;
+
+ ScTable& rTab = *rxTab;
+ SCCOL nNumCols = rTab.aCol.size();
+ for (SCCOL nColIdx = 0; nColIdx < nNumCols; ++nColIdx)
+ broadcastRecalcAfterImportColumn(rTab.aCol[nColIdx]);
+ }
+}
+
+void ScDocumentImport::broadcastRecalcAfterImportColumn(ScColumn& rCol)
+{
+ CellStoreAfterImportBroadcaster aFunc;
+ std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc);
+}
+
+
+bool ScDocumentImport::isLatinScript(const ScPatternAttr& rPatAttr)
+{
+ SvNumberFormatter* pFormatter = mpImpl->mrDoc.GetFormatTable();
+ sal_uInt32 nKey = rPatAttr.GetNumberFormat(pFormatter);
+ return isLatinScript(nKey);
+}
+
+bool ScDocumentImport::isLatinScript(sal_uInt32 nFormat)
+{
+ auto it = mpImpl->maIsLatinScriptMap.find(nFormat);
+ if (it != mpImpl->maIsLatinScriptMap.end())
+ return it->second;
+ bool b = sc::NumFmtUtil::isLatinScript(nFormat, mpImpl->mrDoc);
+ mpImpl->maIsLatinScriptMap.emplace(nFormat, b);
+ return b;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/documentstreamaccess.cxx b/sc/source/core/data/documentstreamaccess.cxx
new file mode 100644
index 0000000000..6b35810580
--- /dev/null
+++ b/sc/source/core/data/documentstreamaccess.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/.
+ */
+
+#include <documentstreamaccess.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <mtvelements.hxx>
+
+#include <svl/sharedstringpool.hxx>
+
+namespace sc {
+
+struct DocumentStreamAccessImpl
+{
+ ScDocument& mrDoc;
+ ColumnBlockPositionSet maBlockPosSet;
+
+ explicit DocumentStreamAccessImpl( ScDocument& rDoc ) :
+ mrDoc(rDoc),
+ maBlockPosSet(rDoc)
+ {}
+};
+
+DocumentStreamAccess::DocumentStreamAccess( ScDocument& rDoc ) :
+ mpImpl(new DocumentStreamAccessImpl(rDoc)) {}
+
+DocumentStreamAccess::~DocumentStreamAccess()
+{
+}
+
+void DocumentStreamAccess::setNumericCell( const ScAddress& rPos, double fVal )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ ColumnBlockPosition* pBlockPos =
+ mpImpl->maBlockPosSet.getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ // Set the numeric value.
+ CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), fVal);
+
+ // Be sure to set the corresponding text attribute to the default value.
+ CellTextAttrStoreType& rAttrs = pTab->aCol[rPos.Col()].maCellTextAttrs;
+ pBlockPos->miCellTextAttrPos = rAttrs.set(pBlockPos->miCellTextAttrPos, rPos.Row(), CellTextAttr());
+}
+
+void DocumentStreamAccess::setStringCell( const ScAddress& rPos, const OUString& rStr )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ ColumnBlockPosition* pBlockPos =
+ mpImpl->maBlockPosSet.getBlockPosition(rPos.Tab(), rPos.Col());
+
+ if (!pBlockPos)
+ return;
+
+ svl::SharedString aSS = mpImpl->mrDoc.GetSharedStringPool().intern(rStr);
+ if (!aSS.getData())
+ return;
+
+ // Set the string.
+ CellStoreType& rCells = pTab->aCol[rPos.Col()].maCells;
+ pBlockPos->miCellPos = rCells.set(pBlockPos->miCellPos, rPos.Row(), aSS);
+
+ // Be sure to set the corresponding text attribute to the default value.
+ CellTextAttrStoreType& rAttrs = pTab->aCol[rPos.Col()].maCellTextAttrs;
+ pBlockPos->miCellTextAttrPos = rAttrs.set(pBlockPos->miCellTextAttrPos, rPos.Row(), CellTextAttr());
+}
+
+void DocumentStreamAccess::reset()
+{
+ mpImpl->maBlockPosSet.clear();
+}
+
+void DocumentStreamAccess::shiftRangeUp( const ScRange& rRange )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rRange.aStart.Tab());
+ if (!pTab)
+ return;
+
+ SCROW nTopRow = rRange.aStart.Row();
+ SCROW nLastRow = rRange.aEnd.Row();
+
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ ColumnBlockPosition* pBlockPos =
+ mpImpl->maBlockPosSet.getBlockPosition(rRange.aStart.Tab(), nCol);
+
+ if (!pBlockPos)
+ return;
+
+ CellStoreType& rCells = pTab->aCol[nCol].maCells;
+ rCells.erase(nTopRow, nTopRow); // Erase the top, and shift the rest up.
+ pBlockPos->miCellPos = rCells.insert_empty(nLastRow, 1);
+
+ // Do the same for the text attribute storage.
+ CellTextAttrStoreType& rAttrs = pTab->aCol[nCol].maCellTextAttrs;
+ rAttrs.erase(nTopRow, nTopRow);
+ pBlockPos->miCellTextAttrPos = rAttrs.insert_empty(nLastRow, 1);
+ }
+}
+
+void DocumentStreamAccess::shiftRangeDown( const ScRange& rRange )
+{
+ ScTable* pTab = mpImpl->mrDoc.FetchTable(rRange.aStart.Tab());
+ if (!pTab)
+ return;
+
+ SCROW nTopRow = rRange.aStart.Row();
+ SCROW nLastRow = rRange.aEnd.Row();
+
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ ColumnBlockPosition* pBlockPos =
+ mpImpl->maBlockPosSet.getBlockPosition(rRange.aStart.Tab(), nCol);
+
+ if (!pBlockPos)
+ return;
+
+ CellStoreType& rCells = pTab->aCol[nCol].maCells;
+ rCells.erase(nLastRow, nLastRow); // Erase the bottom.
+ pBlockPos->miCellPos = rCells.insert_empty(nTopRow, 1); // insert at the top and shift everything down.
+
+ // Do the same for the text attribute storage.
+ CellTextAttrStoreType& rAttrs = pTab->aCol[nCol].maCellTextAttrs;
+ rAttrs.erase(nLastRow, nLastRow);
+ pBlockPos->miCellTextAttrPos = rAttrs.insert_empty(nTopRow, 1);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpcache.cxx b/sc/source/core/data/dpcache.cxx
new file mode 100644
index 0000000000..52109c673b
--- /dev/null
+++ b/sc/source/core/data/dpcache.cxx
@@ -0,0 +1,1522 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpcache.hxx>
+
+#include <document.hxx>
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+#include <dpobject.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docoptio.hxx>
+#include <dpitemdata.hxx>
+#include <dputil.hxx>
+#include <dpnumgroupinfo.hxx>
+#include <columniterator.hxx>
+#include <cellvalue.hxx>
+
+#include <comphelper/parallelsort.hxx>
+#include <rtl/math.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/textsearch.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#if DUMP_PIVOT_TABLE
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#endif
+
+// TODO : Threaded pivot cache operation is disabled until we can figure out
+// ways to make the edit engine and number formatter codes thread-safe in a
+// proper fashion.
+#define ENABLE_THREADED_PIVOT_CACHE 0
+
+#if ENABLE_THREADED_PIVOT_CACHE
+#include <thread>
+#include <future>
+#include <queue>
+#endif
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Exception;
+
+ScDPCache::GroupItems::GroupItems() : mnGroupType(0) {}
+
+ScDPCache::GroupItems::GroupItems(const ScDPNumGroupInfo& rInfo, sal_Int32 nGroupType) :
+ maInfo(rInfo), mnGroupType(nGroupType) {}
+
+ScDPCache::Field::Field() : mnNumFormat(0) {}
+
+ScDPCache::ScDPCache(ScDocument& rDoc) :
+ mrDoc( rDoc ),
+ mnColumnCount ( 0 ),
+ maEmptyRows(0, rDoc.GetMaxRowCount(), true),
+ mnDataSize(-1),
+ mnRowCount(0),
+ mbDisposing(false)
+{
+}
+
+namespace {
+
+struct ClearObjectSource
+{
+ void operator() (ScDPObject* p) const
+ {
+ p->ClearTableData();
+ }
+};
+
+}
+
+ScDPCache::~ScDPCache()
+{
+ // Make sure no live ScDPObject instances hold reference to this cache any
+ // more.
+ mbDisposing = true;
+ std::for_each(maRefObjects.begin(), maRefObjects.end(), ClearObjectSource());
+}
+
+namespace {
+
+/**
+ * While the macro interpret level is incremented, the formula cells are
+ * (semi-)guaranteed to be interpreted.
+ */
+class MacroInterpretIncrementer
+{
+public:
+ explicit MacroInterpretIncrementer(ScDocument& rDoc) :
+ mrDoc(rDoc)
+ {
+ mrDoc.IncMacroInterpretLevel();
+ }
+ ~MacroInterpretIncrementer()
+ {
+ mrDoc.DecMacroInterpretLevel();
+ }
+private:
+ ScDocument& mrDoc;
+};
+
+rtl_uString* internString( ScDPCache::StringSetType& rPool, const OUString& rStr )
+{
+ return rPool.insert(rStr).first->pData;
+}
+
+OUString createLabelString( const ScDocument& rDoc, SCCOL nCol, const ScRefCellValue& rCell )
+{
+ OUString aDocStr = rCell.getRawString(rDoc);
+
+ if (aDocStr.isEmpty())
+ {
+ // Replace an empty label string with column name.
+
+ ScAddress aColAddr(nCol, 0, 0);
+ aDocStr = ScResId(STR_COLUMN) + " " + aColAddr.Format(ScRefFlags::COL_VALID);
+ }
+ return aDocStr;
+}
+
+void initFromCell(
+ ScDPCache::StringSetType& rStrPool, const ScDocument& rDoc, const ScAddress& rPos,
+ const ScRefCellValue& rCell, ScDPItemData& rData, sal_uInt32& rNumFormat)
+{
+ OUString aDocStr = rCell.getRawString(rDoc);
+ rNumFormat = 0;
+
+ if (rCell.hasError())
+ {
+ rData.SetErrorStringInterned(internString(rStrPool, rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab())));
+ }
+ else if (rCell.hasNumeric())
+ {
+ double fVal = rCell.getRawValue();
+ rNumFormat = rDoc.GetNumberFormat(rPos);
+ rData.SetValue(fVal);
+ }
+ else if (!rCell.isEmpty())
+ {
+ rData.SetStringInterned(internString(rStrPool, aDocStr));
+ }
+ else
+ rData.SetEmpty();
+}
+
+struct Bucket
+{
+ ScDPItemData maValue;
+ SCROW mnOrderIndex;
+ SCROW mnDataIndex;
+ Bucket() :
+ mnOrderIndex(0), mnDataIndex(0) {}
+ Bucket(const ScDPItemData& rValue, SCROW nData) :
+ maValue(rValue), mnOrderIndex(0), mnDataIndex(nData) {}
+};
+
+#if DEBUG_PIVOT_TABLE
+#include <iostream>
+using std::cout;
+using std::endl;
+
+struct PrintBucket
+{
+ void operator() (const Bucket& v) const
+ {
+ cout << "value: " << v.maValue.GetValue() << " order index: " << v.mnOrderIndex << " data index: " << v.mnDataIndex << endl;
+ }
+};
+
+#endif
+
+struct LessByValue
+{
+ bool operator() (const Bucket& left, const Bucket& right) const
+ {
+ return left.maValue < right.maValue;
+ }
+};
+
+struct LessByOrderIndex
+{
+ bool operator() (const Bucket& left, const Bucket& right) const
+ {
+ return left.mnOrderIndex < right.mnOrderIndex;
+ }
+};
+
+struct LessByDataIndex
+{
+ bool operator() (const Bucket& left, const Bucket& right) const
+ {
+ return left.mnDataIndex < right.mnDataIndex;
+ }
+};
+
+struct EqualByOrderIndex
+{
+ bool operator() (const Bucket& left, const Bucket& right) const
+ {
+ return left.mnOrderIndex == right.mnOrderIndex;
+ }
+};
+
+class PushBackValue
+{
+ ScDPCache::ScDPItemDataVec& mrItems;
+public:
+ explicit PushBackValue(ScDPCache::ScDPItemDataVec& _items) : mrItems(_items) {}
+ void operator() (const Bucket& v)
+ {
+ mrItems.push_back(v.maValue);
+ }
+};
+
+class PushBackOrderIndex
+{
+ ScDPCache::IndexArrayType& mrData;
+public:
+ explicit PushBackOrderIndex(ScDPCache::IndexArrayType& _items) : mrData(_items) {}
+ void operator() (const Bucket& v)
+ {
+ mrData.push_back(v.mnOrderIndex);
+ }
+};
+
+void processBuckets(std::vector<Bucket>& aBuckets, ScDPCache::Field& rField)
+{
+ if (aBuckets.empty())
+ return;
+
+ // Sort by the value.
+ comphelper::parallelSort(aBuckets.begin(), aBuckets.end(), LessByValue());
+
+ {
+ // Set order index such that unique values have identical index value.
+ SCROW nCurIndex = 0;
+ std::vector<Bucket>::iterator it = aBuckets.begin(), itEnd = aBuckets.end();
+ ScDPItemData aPrev = it->maValue;
+ it->mnOrderIndex = nCurIndex;
+ for (++it; it != itEnd; ++it)
+ {
+ if (!aPrev.IsCaseInsEqual(it->maValue))
+ ++nCurIndex;
+
+ it->mnOrderIndex = nCurIndex;
+ aPrev = it->maValue;
+ }
+ }
+
+ // Re-sort the bucket this time by the data index.
+ comphelper::parallelSort(aBuckets.begin(), aBuckets.end(), LessByDataIndex());
+
+ // Copy the order index series into the field object.
+ rField.maData.reserve(aBuckets.size());
+ std::for_each(aBuckets.begin(), aBuckets.end(), PushBackOrderIndex(rField.maData));
+
+ // Sort by the value again.
+ comphelper::parallelSort(aBuckets.begin(), aBuckets.end(), LessByOrderIndex());
+
+ // Unique by value.
+ std::vector<Bucket>::iterator itUniqueEnd =
+ std::unique(aBuckets.begin(), aBuckets.end(), EqualByOrderIndex());
+
+ // Copy the unique values into items.
+ std::vector<Bucket>::iterator itBeg = aBuckets.begin();
+ size_t nLen = distance(itBeg, itUniqueEnd);
+ rField.maItems.reserve(nLen);
+ std::for_each(itBeg, itUniqueEnd, PushBackValue(rField.maItems));
+}
+
+struct InitColumnData
+{
+ ScDPCache::EmptyRowsType maEmptyRows;
+ OUString maLabel;
+
+ ScDPCache::StringSetType* mpStrPool;
+ ScDPCache::Field* mpField;
+
+ SCCOL mnCol;
+
+ InitColumnData(ScSheetLimits const & rSheetLimits) :
+ maEmptyRows(0, rSheetLimits.GetMaxRowCount(), true),
+ mpStrPool(nullptr),
+ mpField(nullptr),
+ mnCol(-1) {}
+
+ void init( SCCOL nCol, ScDPCache::StringSetType* pStrPool, ScDPCache::Field* pField )
+ {
+ mpStrPool = pStrPool;
+ mpField = pField;
+ mnCol = nCol;
+ }
+};
+
+struct InitDocData
+{
+ ScDocument& mrDoc;
+ SCTAB mnDocTab;
+ SCROW mnStartRow;
+ SCROW mnEndRow;
+ bool mbTailEmptyRows;
+
+ InitDocData(ScDocument& rDoc) :
+ mrDoc(rDoc),
+ mnDocTab(-1),
+ mnStartRow(-1),
+ mnEndRow(-1),
+ mbTailEmptyRows(false) {}
+};
+
+typedef std::unordered_set<OUString> LabelSet;
+
+void normalizeAddLabel(const OUString& rLabel, std::vector<OUString>& rLabels, LabelSet& rExistingNames)
+{
+ const OUString aLabelLower = ScGlobal::getCharClass().lowercase(rLabel);
+ sal_Int32 nSuffix = 1;
+ OUString aNewLabel = rLabel;
+ OUString aNewLabelLower = aLabelLower;
+ while (true)
+ {
+ if (!rExistingNames.count(aNewLabelLower))
+ {
+ // this is a unique label.
+ rLabels.push_back(aNewLabel);
+ rExistingNames.insert(aNewLabelLower);
+ break;
+ }
+
+ // This name already exists.
+ aNewLabel = rLabel + OUString::number(++nSuffix);
+ aNewLabelLower = aLabelLower + OUString::number(nSuffix);
+ }
+}
+
+std::vector<OUString> normalizeLabels(const std::vector<InitColumnData>& rColData)
+{
+ std::vector<OUString> aLabels;
+ aLabels.reserve(rColData.size() + 1);
+
+ LabelSet aExistingNames;
+ normalizeAddLabel(ScResId(STR_PIVOT_DATA), aLabels, aExistingNames);
+
+ for (const InitColumnData& rCol : rColData)
+ normalizeAddLabel(rCol.maLabel, aLabels, aExistingNames);
+
+ return aLabels;
+}
+
+std::vector<OUString> normalizeLabels(const ScDPCache::DBConnector& rDB, const sal_Int32 nLabelCount)
+{
+ std::vector<OUString> aLabels;
+ aLabels.reserve(nLabelCount + 1);
+
+ LabelSet aExistingNames;
+ normalizeAddLabel(ScResId(STR_PIVOT_DATA), aLabels, aExistingNames);
+
+ for (sal_Int32 nCol = 0; nCol < nLabelCount; ++nCol)
+ {
+ OUString aColTitle = rDB.getColumnLabel(nCol);
+ normalizeAddLabel(aColTitle, aLabels, aExistingNames);
+ }
+
+ return aLabels;
+}
+
+void initColumnFromDoc( InitDocData& rDocData, InitColumnData &rColData )
+{
+ ScDPCache::Field& rField = *rColData.mpField;
+ ScDocument& rDoc = rDocData.mrDoc;
+ SCTAB nDocTab = rDocData.mnDocTab;
+ SCCOL nCol = rColData.mnCol;
+ SCROW nStartRow = rDocData.mnStartRow;
+ SCROW nEndRow = rDocData.mnEndRow;
+ bool bTailEmptyRows = rDocData.mbTailEmptyRows;
+
+ std::optional<sc::ColumnIterator> pIter =
+ rDoc.GetColumnIterator(nDocTab, nCol, nStartRow, nEndRow);
+ assert(pIter);
+ assert(pIter->hasCell());
+
+ ScDPItemData aData;
+
+ rColData.maLabel = createLabelString(rDoc, nCol, pIter->getCell());
+ pIter->next();
+
+ std::vector<Bucket> aBuckets;
+ aBuckets.reserve(nEndRow-nStartRow); // skip the topmost label cell.
+
+ // Push back all original values.
+ for (SCROW i = 0, n = nEndRow-nStartRow; i < n; ++i, pIter->next())
+ {
+ assert(pIter->hasCell());
+
+ sal_uInt32 nNumFormat = 0;
+ ScAddress aPos(nCol, pIter->getRow(), nDocTab);
+ initFromCell(*rColData.mpStrPool, rDoc, aPos, pIter->getCell(), aData, nNumFormat);
+
+ aBuckets.emplace_back(aData, i);
+
+ if (!aData.IsEmpty())
+ {
+ rColData.maEmptyRows.insert_back(i, i+1, false);
+ if (nNumFormat)
+ // Only take non-default number format.
+ rField.mnNumFormat = nNumFormat;
+ }
+ }
+
+ processBuckets(aBuckets, rField);
+
+ if (bTailEmptyRows)
+ {
+ // If the last item is not empty, append one. Note that the items
+ // are sorted, and empty item should come last when sorted.
+ if (rField.maItems.empty() || !rField.maItems.back().IsEmpty())
+ {
+ aData.SetEmpty();
+ rField.maItems.push_back(aData);
+ }
+ }
+}
+
+#if ENABLE_THREADED_PIVOT_CACHE
+
+class ThreadQueue
+{
+ using FutureType = std::future<void>;
+ std::queue<FutureType> maQueue;
+ std::mutex maMutex;
+ std::condition_variable maCond;
+
+ size_t mnMaxQueue;
+
+public:
+ ThreadQueue( size_t nMaxQueue ) : mnMaxQueue(nMaxQueue) {}
+
+ void push( std::function<void()> aFunc )
+ {
+ std::unique_lock<std::mutex> lock(maMutex);
+
+ while (maQueue.size() >= mnMaxQueue)
+ maCond.wait(lock);
+
+ FutureType f = std::async(std::launch::async, aFunc);
+ maQueue.push(std::move(f));
+ lock.unlock();
+
+ maCond.notify_one();
+ }
+
+ void waitForOne()
+ {
+ std::unique_lock<std::mutex> lock(maMutex);
+
+ while (maQueue.empty())
+ maCond.wait(lock);
+
+ FutureType ret = std::move(maQueue.front());
+ maQueue.pop();
+ lock.unlock();
+
+ ret.get(); // This may throw if an exception was thrown on the async thread.
+
+ maCond.notify_one();
+ }
+};
+
+class ThreadScopedGuard
+{
+ std::thread maThread;
+public:
+ ThreadScopedGuard(std::thread thread) : maThread(std::move(thread)) {}
+ ThreadScopedGuard(ThreadScopedGuard&& other) : maThread(std::move(other.maThread)) {}
+
+ ThreadScopedGuard(const ThreadScopedGuard&) = delete;
+ ThreadScopedGuard& operator= (const ThreadScopedGuard&) = delete;
+
+ ~ThreadScopedGuard()
+ {
+ maThread.join();
+ }
+};
+
+#endif
+
+}
+
+void ScDPCache::InitFromDoc(ScDocument& rDoc, const ScRange& rRange)
+{
+ Clear();
+
+ InitDocData aDocData(rDoc);
+
+ // Make sure the formula cells within the data range are interpreted
+ // during this call, for this method may be called from the interpretation
+ // of GETPIVOTDATA, which disables nested formula interpretation without
+ // increasing the macro level.
+ MacroInterpretIncrementer aMacroInc(rDoc);
+
+ aDocData.mnStartRow = rRange.aStart.Row(); // start of data
+ aDocData.mnEndRow = rRange.aEnd.Row();
+
+ // Sanity check
+ if (!GetDoc().ValidRow(aDocData.mnStartRow) || !GetDoc().ValidRow(aDocData.mnEndRow) || aDocData.mnEndRow <= aDocData.mnStartRow)
+ return;
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ aDocData.mnDocTab = rRange.aStart.Tab();
+
+ mnColumnCount = nEndCol - nStartCol + 1;
+
+ // this row count must include the trailing empty rows.
+ mnRowCount = aDocData.mnEndRow - aDocData.mnStartRow; // skip the topmost label row.
+
+ // Skip trailing empty rows if exists.
+ SCCOL nCol1 = nStartCol, nCol2 = nEndCol;
+ SCROW nRow1 = aDocData.mnStartRow, nRow2 = aDocData.mnEndRow;
+ rDoc.ShrinkToDataArea(aDocData.mnDocTab, nCol1, nRow1, nCol2, nRow2);
+ aDocData.mbTailEmptyRows = aDocData.mnEndRow > nRow2; // Trailing empty rows exist.
+ aDocData.mnEndRow = nRow2;
+
+ if (aDocData.mnEndRow <= aDocData.mnStartRow)
+ {
+ // Check this again since the end row position has changed. It's
+ // possible that the new end row becomes lower than the start row
+ // after the shrinkage.
+ Clear();
+ return;
+ }
+
+ maStringPools.resize(mnColumnCount);
+ std::vector<InitColumnData> aColData(mnColumnCount, InitColumnData(rDoc.GetSheetLimits()));
+ maFields.reserve(mnColumnCount);
+ for (SCCOL i = 0; i < mnColumnCount; ++i)
+ maFields.push_back(std::make_unique<Field>());
+
+ maLabelNames.reserve(mnColumnCount+1);
+
+ // Ensure that none of the formula cells in the data range are dirty.
+ rDoc.EnsureFormulaCellResults(rRange);
+
+#if ENABLE_THREADED_PIVOT_CACHE
+ ThreadQueue aQueue(std::thread::hardware_concurrency());
+
+ auto aFuncLaunchFieldThreads = [&]()
+ {
+ for (sal_uInt16 nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ size_t nDim = nCol - nStartCol;
+ InitColumnData& rColData = aColData[nDim];
+ rColData.init(nCol, &maStringPools[nDim], maFields[nDim].get());
+
+ auto func = [&aDocData,&rColData]()
+ {
+ initColumnFromDoc(aDocData, rColData);
+ };
+
+ aQueue.push(std::move(func));
+ }
+ };
+
+ {
+ // Launch a separate thread that in turn spawns async threads to populate the fields.
+ std::thread t(aFuncLaunchFieldThreads);
+ ThreadScopedGuard sg(std::move(t));
+
+ // Wait for all the async threads to complete on the main thread.
+ for (SCCOL i = 0; i < mnColumnCount; ++i)
+ aQueue.waitForOne();
+ }
+
+#else
+ for (sal_uInt16 nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ size_t nDim = nCol - nStartCol;
+ InitColumnData& rColData = aColData[nDim];
+ rColData.init(nCol, &maStringPools[nDim], maFields[nDim].get());
+
+ initColumnFromDoc(aDocData, rColData);
+ }
+#endif
+
+ maLabelNames = normalizeLabels(aColData);
+
+ // Merge all non-empty rows data.
+ for (const InitColumnData& rCol : aColData)
+ {
+ EmptyRowsType::const_segment_iterator it = rCol.maEmptyRows.begin_segment();
+ EmptyRowsType::const_segment_iterator ite = rCol.maEmptyRows.end_segment();
+ EmptyRowsType::const_iterator pos = maEmptyRows.begin();
+
+ for (; it != ite; ++it)
+ {
+ if (!it->value)
+ // Non-empty segment found. Record it.
+ pos = maEmptyRows.insert(pos, it->start, it->end, false).first;
+ }
+ }
+
+ PostInit();
+}
+
+bool ScDPCache::InitFromDataBase(DBConnector& rDB)
+{
+ Clear();
+
+ try
+ {
+ mnColumnCount = rDB.getColumnCount();
+ maStringPools.resize(mnColumnCount);
+ maFields.clear();
+ maFields.reserve(mnColumnCount);
+ for (SCCOL i = 0; i < mnColumnCount; ++i)
+ maFields.push_back(std::make_unique<Field>());
+
+ // Get column titles and types.
+ maLabelNames = normalizeLabels(rDB, mnColumnCount);
+
+ std::vector<Bucket> aBuckets;
+ ScDPItemData aData;
+ for (sal_Int32 nCol = 0; nCol < mnColumnCount; ++nCol)
+ {
+ if (!rDB.first())
+ continue;
+
+ aBuckets.clear();
+ Field& rField = *maFields[nCol];
+ SCROW nRow = 0;
+ do
+ {
+ SvNumFormatType nFormatType = SvNumFormatType::UNDEFINED;
+ aData.SetEmpty();
+ rDB.getValue(nCol, aData, nFormatType);
+ aBuckets.emplace_back(aData, nRow);
+ if (!aData.IsEmpty())
+ {
+ maEmptyRows.insert_back(nRow, nRow+1, false);
+ SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
+ rField.mnNumFormat = pFormatter ? pFormatter->GetStandardFormat(nFormatType) : 0;
+ }
+
+ ++nRow;
+ }
+ while (rDB.next());
+
+ processBuckets(aBuckets, rField);
+ }
+
+ rDB.finish();
+
+ if (!maFields.empty())
+ mnRowCount = maFields[0]->maData.size();
+
+ PostInit();
+ return true;
+ }
+ catch (const Exception&)
+ {
+ return false;
+ }
+}
+
+bool ScDPCache::ValidQuery( SCROW nRow, const ScQueryParam &rParam) const
+{
+ if (!rParam.GetEntryCount())
+ return true;
+
+ if (!rParam.GetEntry(0).bDoQuery)
+ return true;
+
+ bool bMatchWholeCell = mrDoc.GetDocOptions().IsMatchWholeCell();
+
+ SCSIZE nEntryCount = rParam.GetEntryCount();
+ std::vector<bool> aPassed(nEntryCount, false);
+
+ tools::Long nPos = -1;
+ CollatorWrapper& rCollator = ScGlobal::GetCollator(rParam.bCaseSens);
+ ::utl::TransliterationWrapper& rTransliteration = ScGlobal::GetTransliteration(rParam.bCaseSens);
+
+ for (size_t i = 0; i < nEntryCount && rParam.GetEntry(i).bDoQuery; ++i)
+ {
+ const ScQueryEntry& rEntry = rParam.GetEntry(i);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ // we can only handle one single direct query
+ // #i115431# nField in QueryParam is the sheet column, not the field within the source range
+ SCCOL nQueryCol = static_cast<SCCOL>(rEntry.nField);
+ if ( nQueryCol < rParam.nCol1 )
+ nQueryCol = rParam.nCol1;
+ if ( nQueryCol > rParam.nCol2 )
+ nQueryCol = rParam.nCol2;
+ SCCOL nSourceField = nQueryCol - rParam.nCol1;
+ SCROW nId = GetItemDataId( nSourceField, nRow, false );
+ const ScDPItemData* pCellData = GetItemDataById( nSourceField, nId );
+
+ bool bOk = false;
+
+ if (rEntry.GetQueryItem().meType == ScQueryEntry::ByEmpty)
+ {
+ if (rEntry.IsQueryByEmpty())
+ bOk = pCellData->IsEmpty();
+ else
+ {
+ assert(rEntry.IsQueryByNonEmpty());
+ bOk = !pCellData->IsEmpty();
+ }
+ }
+ else if (rEntry.GetQueryItem().meType != ScQueryEntry::ByString && pCellData->IsValue())
+ { // by Value
+ double nCellVal = pCellData->GetValue();
+
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL :
+ bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
+ break;
+ case SC_LESS :
+ bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
+ break;
+ case SC_GREATER :
+ bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
+ break;
+ case SC_LESS_EQUAL :
+ bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
+ break;
+ case SC_GREATER_EQUAL :
+ bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
+ break;
+ case SC_NOT_EQUAL :
+ bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
+ break;
+ default:
+ bOk= false;
+ break;
+ }
+ }
+ else if ((rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
+ || (rEntry.GetQueryItem().meType == ScQueryEntry::ByString
+ && pCellData->HasStringData() )
+ )
+ { // by String
+ OUString aCellStr = pCellData->GetString();
+
+ bool bRealWildOrRegExp = (rParam.eSearchType != utl::SearchParam::SearchType::Normal &&
+ ((rEntry.eOp == SC_EQUAL) || (rEntry.eOp == SC_NOT_EQUAL)));
+ if (bRealWildOrRegExp)
+ {
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = aCellStr.getLength();
+
+ bool bMatch = rEntry.GetSearchTextPtr( rParam.eSearchType, rParam.bCaseSens, bMatchWholeCell )
+ ->SearchForward( aCellStr, &nStart, &nEnd );
+ // from 614 on, nEnd is behind the found text
+ if (bMatch && bMatchWholeCell
+ && (nStart != 0 || nEnd != aCellStr.getLength()))
+ bMatch = false; // RegExp must match entire cell string
+
+ bOk = ((rEntry.eOp == SC_NOT_EQUAL) ? !bMatch : bMatch);
+ }
+ else
+ {
+ if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
+ {
+ if (bMatchWholeCell)
+ {
+ // TODO: Use shared string for fast equality check.
+ OUString aStr = rEntry.GetQueryItem().maString.getString();
+ bOk = rTransliteration.isEqual(aCellStr, aStr);
+ bool bHasStar = false;
+ sal_Int32 nIndex;
+ if (( nIndex = aStr.indexOf('*') ) != -1)
+ bHasStar = true;
+ if (bHasStar && (nIndex>0))
+ {
+ for (sal_Int32 j=0;(j<nIndex) && (j< aCellStr.getLength()) ; j++)
+ {
+ if (aCellStr[j] == aStr[j])
+ {
+ bOk=true;
+ }
+ else
+ {
+ bOk=false;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ OUString aQueryStr = rEntry.GetQueryItem().maString.getString();
+ css::uno::Sequence< sal_Int32 > xOff;
+ const LanguageType nLang = ScGlobal::oSysLocale->GetLanguageTag().getLanguageType();
+ OUString aCell = rTransliteration.transliterate(
+ aCellStr, nLang, 0, aCellStr.getLength(), &xOff);
+ OUString aQuer = rTransliteration.transliterate(
+ aQueryStr, nLang, 0, aQueryStr.getLength(), &xOff);
+ bOk = (aCell.indexOf( aQuer ) != -1);
+ }
+ if (rEntry.eOp == SC_NOT_EQUAL)
+ bOk = !bOk;
+ }
+ else
+ { // use collator here because data was probably sorted
+ sal_Int32 nCompare = rCollator.compareString(
+ aCellStr, rEntry.GetQueryItem().maString.getString());
+ switch (rEntry.eOp)
+ {
+ case SC_LESS :
+ bOk = (nCompare < 0);
+ break;
+ case SC_GREATER :
+ bOk = (nCompare > 0);
+ break;
+ case SC_LESS_EQUAL :
+ bOk = (nCompare <= 0);
+ break;
+ case SC_GREATER_EQUAL :
+ bOk = (nCompare >= 0);
+ break;
+ case SC_NOT_EQUAL:
+ OSL_FAIL("SC_NOT_EQUAL");
+ break;
+ case SC_TOPVAL:
+ case SC_BOTVAL:
+ case SC_TOPPERC:
+ case SC_BOTPERC:
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (nPos == -1)
+ {
+ nPos++;
+ aPassed[nPos] = bOk;
+ }
+ else
+ {
+ if (rEntry.eConnect == SC_AND)
+ {
+ aPassed[nPos] = aPassed[nPos] && bOk;
+ }
+ else
+ {
+ nPos++;
+ aPassed[nPos] = bOk;
+ }
+ }
+ }
+
+ for (tools::Long j=1; j <= nPos; j++)
+ aPassed[0] = aPassed[0] || aPassed[j];
+
+ bool bRet = aPassed[0];
+ return bRet;
+}
+
+ScDocument& ScDPCache::GetDoc() const
+{
+ return mrDoc;
+}
+
+tools::Long ScDPCache::GetColumnCount() const
+{
+ return mnColumnCount;
+}
+
+bool ScDPCache::IsRowEmpty(SCROW nRow) const
+{
+ bool bEmpty = true;
+ maEmptyRows.search_tree(nRow, bEmpty);
+ return bEmpty;
+}
+
+const ScDPCache::GroupItems* ScDPCache::GetGroupItems(tools::Long nDim) const
+{
+ if (nDim < 0)
+ return nullptr;
+
+ tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
+ if (nDim < nSourceCount)
+ return maFields[nDim]->mpGroup.get();
+
+ nDim -= nSourceCount;
+ if (nDim < static_cast<tools::Long>(maGroupFields.size()))
+ return maGroupFields[nDim].get();
+
+ return nullptr;
+}
+
+OUString ScDPCache::GetDimensionName(std::vector<OUString>::size_type nDim) const
+{
+ OSL_ENSURE(nDim < maLabelNames.size()-1 , "ScDPTableDataCache::GetDimensionName");
+ OSL_ENSURE(maLabelNames.size() == static_cast <sal_uInt16> (mnColumnCount+1), "ScDPTableDataCache::GetDimensionName");
+
+ if ( nDim+1 < maLabelNames.size() )
+ {
+ return maLabelNames[nDim+1];
+ }
+ else
+ return OUString();
+}
+
+void ScDPCache::PostInit()
+{
+ OSL_ENSURE(!maFields.empty(), "Cache not initialized!");
+
+ maEmptyRows.build_tree();
+ auto it = maEmptyRows.rbegin();
+ OSL_ENSURE(it != maEmptyRows.rend(), "corrupt flat_segment_tree instance!");
+ mnDataSize = maFields[0]->maData.size();
+ ++it; // Skip the first position.
+ OSL_ENSURE(it != maEmptyRows.rend(), "buggy version of flat_segment_tree is used.");
+ if (it->second)
+ {
+ SCROW nLastNonEmpty = it->first - 1;
+ if (nLastNonEmpty+1 < mnDataSize)
+ mnDataSize = nLastNonEmpty+1;
+ }
+}
+
+void ScDPCache::Clear()
+{
+ mnColumnCount = 0;
+ mnRowCount = 0;
+ maFields.clear();
+ maLabelNames.clear();
+ maGroupFields.clear();
+ maEmptyRows.clear();
+ maStringPools.clear();
+}
+
+SCROW ScDPCache::GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
+{
+ OSL_ENSURE(nDim < mnColumnCount, "ScDPTableDataCache::GetItemDataId ");
+
+ const Field& rField = *maFields[nDim];
+ if (o3tl::make_unsigned(nRow) >= rField.maData.size())
+ {
+ // nRow is in the trailing empty rows area.
+ if (bRepeatIfEmpty)
+ nRow = rField.maData.size()-1; // Move to the last non-empty row.
+ else
+ // Return the last item, which should always be empty if the
+ // initialization has skipped trailing empty rows.
+ return rField.maItems.size()-1;
+
+ }
+ else if (bRepeatIfEmpty)
+ {
+ while (nRow > 0 && rField.maItems[rField.maData[nRow]].IsEmpty())
+ --nRow;
+ }
+
+ return rField.maData[nRow];
+}
+
+const ScDPItemData* ScDPCache::GetItemDataById(tools::Long nDim, SCROW nId) const
+{
+ if (nDim < 0 || nId < 0)
+ return nullptr;
+
+ size_t nSourceCount = maFields.size();
+ size_t nDimPos = static_cast<size_t>(nDim);
+ size_t nItemId = static_cast<size_t>(nId);
+ if (nDimPos < nSourceCount)
+ {
+ // source field.
+ const Field& rField = *maFields[nDimPos];
+ if (nItemId < rField.maItems.size())
+ return &rField.maItems[nItemId];
+
+ if (!rField.mpGroup)
+ return nullptr;
+
+ nItemId -= rField.maItems.size();
+ const ScDPItemDataVec& rGI = rField.mpGroup->maItems;
+ if (nItemId >= rGI.size())
+ return nullptr;
+
+ return &rGI[nItemId];
+ }
+
+ // Try group fields.
+ nDimPos -= nSourceCount;
+ if (nDimPos >= maGroupFields.size())
+ return nullptr;
+
+ const ScDPItemDataVec& rGI = maGroupFields[nDimPos]->maItems;
+ if (nItemId >= rGI.size())
+ return nullptr;
+
+ return &rGI[nItemId];
+}
+
+size_t ScDPCache::GetFieldCount() const
+{
+ return maFields.size();
+}
+
+size_t ScDPCache::GetGroupFieldCount() const
+{
+ return maGroupFields.size();
+}
+
+SCROW ScDPCache::GetRowCount() const
+{
+ return mnRowCount;
+}
+
+SCROW ScDPCache::GetDataSize() const
+{
+ OSL_ENSURE(mnDataSize <= GetRowCount(), "Data size should never be larger than the row count.");
+ return mnDataSize >= 0 ? mnDataSize : 0;
+}
+
+const ScDPCache::IndexArrayType* ScDPCache::GetFieldIndexArray( size_t nDim ) const
+{
+ if (nDim >= maFields.size())
+ return nullptr;
+
+ return &maFields[nDim]->maData;
+}
+
+const ScDPCache::ScDPItemDataVec& ScDPCache::GetDimMemberValues(SCCOL nDim) const
+{
+ OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," nDim < mnColumnCount ");
+ return maFields.at(nDim)->maItems;
+}
+
+sal_uInt32 ScDPCache::GetNumberFormat( tools::Long nDim ) const
+{
+ if ( nDim >= mnColumnCount )
+ return 0;
+
+ // TODO: Find a way to determine the dominant number format in presence of
+ // multiple number formats in the same field.
+ return maFields[nDim]->mnNumFormat;
+}
+
+bool ScDPCache::IsDateDimension( tools::Long nDim ) const
+{
+ if (nDim >= mnColumnCount)
+ return false;
+
+ SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
+ if (!pFormatter)
+ return false;
+
+ SvNumFormatType eType = pFormatter->GetType(maFields[nDim]->mnNumFormat);
+ return (eType == SvNumFormatType::DATE) || (eType == SvNumFormatType::DATETIME);
+}
+
+tools::Long ScDPCache::GetDimMemberCount(tools::Long nDim) const
+{
+ OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," ScDPTableDataCache::GetDimMemberCount : out of bound ");
+ return maFields[nDim]->maItems.size();
+}
+
+SCCOL ScDPCache::GetDimensionIndex(std::u16string_view sName) const
+{
+ for (size_t i = 1; i < maLabelNames.size(); ++i)
+ {
+ if (maLabelNames[i] == sName)
+ return static_cast<SCCOL>(i-1);
+ }
+ return -1;
+}
+
+rtl_uString* ScDPCache::InternString( size_t nDim, const OUString& rStr )
+{
+ assert(nDim < maStringPools.size());
+ return internString(maStringPools[nDim], rStr);
+}
+
+void ScDPCache::AddReference(ScDPObject* pObj) const
+{
+ maRefObjects.insert(pObj);
+}
+
+void ScDPCache::RemoveReference(ScDPObject* pObj) const
+{
+ if (mbDisposing)
+ // Object being deleted.
+ return;
+
+ maRefObjects.erase(pObj);
+ if (maRefObjects.empty())
+ mrDoc.GetDPCollection()->RemoveCache(this);
+}
+
+const ScDPCache::ScDPObjectSet& ScDPCache::GetAllReferences() const
+{
+ return maRefObjects;
+}
+
+SCROW ScDPCache::GetIdByItemData(tools::Long nDim, const ScDPItemData& rItem) const
+{
+ if (nDim < 0)
+ return -1;
+
+ if (nDim < mnColumnCount)
+ {
+ // source field.
+ const ScDPItemDataVec& rItems = maFields[nDim]->maItems;
+ for (size_t i = 0, n = rItems.size(); i < n; ++i)
+ {
+ if (rItems[i] == rItem)
+ return i;
+ }
+
+ if (!maFields[nDim]->mpGroup)
+ return -1;
+
+ // grouped source field.
+ const ScDPItemDataVec& rGI = maFields[nDim]->mpGroup->maItems;
+ for (size_t i = 0, n = rGI.size(); i < n; ++i)
+ {
+ if (rGI[i] == rItem)
+ return rItems.size() + i;
+ }
+ return -1;
+ }
+
+ // group field.
+ nDim -= mnColumnCount;
+ if (o3tl::make_unsigned(nDim) < maGroupFields.size())
+ {
+ const ScDPItemDataVec& rGI = maGroupFields[nDim]->maItems;
+ for (size_t i = 0, n = rGI.size(); i < n; ++i)
+ {
+ if (rGI[i] == rItem)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+// static
+sal_uInt32 ScDPCache::GetLocaleIndependentFormat( SvNumberFormatter& rFormatter, sal_uInt32 nNumFormat )
+{
+ // For a date or date+time format use ISO format so it works across locales
+ // and can be matched against string based item queries. For time use 24h
+ // format. All others use General format, no currency, percent, ...
+ // Use en-US locale for all.
+ switch (rFormatter.GetType( nNumFormat))
+ {
+ case SvNumFormatType::DATE:
+ return rFormatter.GetFormatIndex( NF_DATE_ISO_YYYYMMDD, LANGUAGE_ENGLISH_US);
+ case SvNumFormatType::TIME:
+ return rFormatter.GetFormatIndex( NF_TIME_HHMMSS, LANGUAGE_ENGLISH_US);
+ case SvNumFormatType::DATETIME:
+ return rFormatter.GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS, LANGUAGE_ENGLISH_US);
+ default:
+ return rFormatter.GetFormatIndex( NF_NUMBER_STANDARD, LANGUAGE_ENGLISH_US);
+ }
+}
+
+// static
+OUString ScDPCache::GetLocaleIndependentFormattedNumberString( double fValue )
+{
+ return rtl::math::doubleToUString( fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
+}
+
+// static
+OUString ScDPCache::GetLocaleIndependentFormattedString( double fValue,
+ SvNumberFormatter& rFormatter, sal_uInt32 nNumFormat )
+{
+ nNumFormat = GetLocaleIndependentFormat( rFormatter, nNumFormat);
+ if ((nNumFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ return GetLocaleIndependentFormattedNumberString( fValue);
+
+ OUString aStr;
+ const Color* pColor = nullptr;
+ rFormatter.GetOutputString( fValue, nNumFormat, aStr, &pColor);
+ return aStr;
+}
+
+OUString ScDPCache::GetFormattedString(tools::Long nDim, const ScDPItemData& rItem, bool bLocaleIndependent) const
+{
+ if (nDim < 0)
+ return rItem.GetString();
+
+ ScDPItemData::Type eType = rItem.GetType();
+ if (eType == ScDPItemData::Value)
+ {
+ // Format value using the stored number format.
+ SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
+ if (pFormatter)
+ {
+ sal_uInt32 nNumFormat = GetNumberFormat(nDim);
+ if (bLocaleIndependent)
+ return GetLocaleIndependentFormattedString( rItem.GetValue(), *pFormatter, nNumFormat);
+
+ OUString aStr;
+ const Color* pColor = nullptr;
+ pFormatter->GetOutputString(rItem.GetValue(), nNumFormat, aStr, &pColor);
+ return aStr;
+ }
+
+ // Last resort...
+ return GetLocaleIndependentFormattedNumberString( rItem.GetValue());
+ }
+
+ if (eType == ScDPItemData::GroupValue)
+ {
+ ScDPItemData::GroupValueAttr aAttr = rItem.GetGroupValue();
+ double fStart = 0.0, fEnd = 0.0;
+ const GroupItems* p = GetGroupItems(nDim);
+ if (p)
+ {
+ fStart = p->maInfo.mfStart;
+ fEnd = p->maInfo.mfEnd;
+ }
+ return ScDPUtil::getDateGroupName(
+ aAttr.mnGroupType, aAttr.mnValue, mrDoc.GetFormatTable(), fStart, fEnd);
+ }
+
+ if (eType == ScDPItemData::RangeStart)
+ {
+ double fVal = rItem.GetValue();
+ const GroupItems* p = GetGroupItems(nDim);
+ if (!p)
+ return rItem.GetString();
+
+ sal_Unicode cDecSep = ScGlobal::getLocaleData().getNumDecimalSep()[0];
+ return ScDPUtil::getNumGroupName(fVal, p->maInfo, cDecSep, mrDoc.GetFormatTable());
+ }
+
+ return rItem.GetString();
+}
+
+SvNumberFormatter* ScDPCache::GetNumberFormatter() const
+{
+ return mrDoc.GetFormatTable();
+}
+
+tools::Long ScDPCache::AppendGroupField()
+{
+ maGroupFields.push_back(std::make_unique<GroupItems>());
+ return static_cast<tools::Long>(maFields.size() + maGroupFields.size() - 1);
+}
+
+void ScDPCache::ResetGroupItems(tools::Long nDim, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nGroupType)
+{
+ if (nDim < 0)
+ return;
+
+ tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
+ if (nDim < nSourceCount)
+ {
+ maFields.at(nDim)->mpGroup.reset(new GroupItems(rNumInfo, nGroupType));
+ return;
+ }
+
+ nDim -= nSourceCount;
+ if (nDim < static_cast<tools::Long>(maGroupFields.size()))
+ {
+ GroupItems& rGI = *maGroupFields[nDim];
+ rGI.maItems.clear();
+ rGI.maInfo = rNumInfo;
+ rGI.mnGroupType = nGroupType;
+ }
+}
+
+SCROW ScDPCache::SetGroupItem(tools::Long nDim, const ScDPItemData& rData)
+{
+ if (nDim < 0)
+ return -1;
+
+ tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
+ if (nDim < nSourceCount)
+ {
+ GroupItems& rGI = *maFields.at(nDim)->mpGroup;
+ rGI.maItems.push_back(rData);
+ SCROW nId = maFields[nDim]->maItems.size() + rGI.maItems.size() - 1;
+ return nId;
+ }
+
+ nDim -= nSourceCount;
+ if (nDim < static_cast<tools::Long>(maGroupFields.size()))
+ {
+ ScDPItemDataVec& rItems = maGroupFields.at(nDim)->maItems;
+ rItems.push_back(rData);
+ return rItems.size()-1;
+ }
+
+ return -1;
+}
+
+void ScDPCache::GetGroupDimMemberIds(tools::Long nDim, std::vector<SCROW>& rIds) const
+{
+ if (nDim < 0)
+ return;
+
+ tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
+ if (nDim < nSourceCount)
+ {
+ if (!maFields.at(nDim)->mpGroup)
+ return;
+
+ size_t nOffset = maFields[nDim]->maItems.size();
+ const ScDPItemDataVec& rGI = maFields[nDim]->mpGroup->maItems;
+ for (size_t i = 0, n = rGI.size(); i < n; ++i)
+ rIds.push_back(static_cast<SCROW>(i + nOffset));
+
+ return;
+ }
+
+ nDim -= nSourceCount;
+ if (nDim < static_cast<tools::Long>(maGroupFields.size()))
+ {
+ const ScDPItemDataVec& rGI = maGroupFields.at(nDim)->maItems;
+ for (size_t i = 0, n = rGI.size(); i < n; ++i)
+ rIds.push_back(static_cast<SCROW>(i));
+ }
+}
+
+namespace {
+
+struct ClearGroupItems
+{
+ void operator() (const std::unique_ptr<ScDPCache::Field>& r) const
+ {
+ r->mpGroup.reset();
+ }
+};
+
+}
+
+void ScDPCache::ClearGroupFields()
+{
+ maGroupFields.clear();
+}
+
+void ScDPCache::ClearAllFields()
+{
+ ClearGroupFields();
+ std::for_each(maFields.begin(), maFields.end(), ClearGroupItems());
+}
+
+const ScDPNumGroupInfo* ScDPCache::GetNumGroupInfo(tools::Long nDim) const
+{
+ if (nDim < 0)
+ return nullptr;
+
+ tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
+ if (nDim < nSourceCount)
+ {
+ if (!maFields.at(nDim)->mpGroup)
+ return nullptr;
+
+ return &maFields[nDim]->mpGroup->maInfo;
+ }
+
+ nDim -= nSourceCount;
+ if (nDim < static_cast<tools::Long>(maGroupFields.size()))
+ return &maGroupFields.at(nDim)->maInfo;
+
+ return nullptr;
+}
+
+sal_Int32 ScDPCache::GetGroupType(tools::Long nDim) const
+{
+ if (nDim < 0)
+ return 0;
+
+ tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
+ if (nDim < nSourceCount)
+ {
+ if (!maFields.at(nDim)->mpGroup)
+ return 0;
+
+ return maFields[nDim]->mpGroup->mnGroupType;
+ }
+
+ nDim -= nSourceCount;
+ if (nDim < static_cast<tools::Long>(maGroupFields.size()))
+ return maGroupFields.at(nDim)->mnGroupType;
+
+ return 0;
+}
+
+#if DUMP_PIVOT_TABLE
+
+namespace {
+
+void dumpItems(const ScDPCache& rCache, tools::Long nDim, const ScDPCache::ScDPItemDataVec& rItems, size_t nOffset)
+{
+ for (size_t i = 0; i < rItems.size(); ++i)
+ cout << " " << (i+nOffset) << ": " << rCache.GetFormattedString(nDim, rItems[i], false) << endl;
+}
+
+void dumpSourceData(const ScDPCache& rCache, tools::Long nDim, const ScDPCache::ScDPItemDataVec& rItems, const ScDPCache::IndexArrayType& rArray)
+{
+ for (const auto& rIndex : rArray)
+ cout << " '" << rCache.GetFormattedString(nDim, rItems[rIndex], false) << "'" << endl;
+}
+
+const char* getGroupTypeName(sal_Int32 nType)
+{
+ static const char* pNames[] = {
+ "", "years", "quarters", "months", "days", "hours", "minutes", "seconds"
+ };
+
+ switch (nType)
+ {
+ case sheet::DataPilotFieldGroupBy::YEARS: return pNames[1];
+ case sheet::DataPilotFieldGroupBy::QUARTERS: return pNames[2];
+ case sheet::DataPilotFieldGroupBy::MONTHS: return pNames[3];
+ case sheet::DataPilotFieldGroupBy::DAYS: return pNames[4];
+ case sheet::DataPilotFieldGroupBy::HOURS: return pNames[5];
+ case sheet::DataPilotFieldGroupBy::MINUTES: return pNames[6];
+ case sheet::DataPilotFieldGroupBy::SECONDS: return pNames[7];
+ default:
+ ;
+ }
+
+ return pNames[0];
+}
+
+}
+
+void ScDPCache::Dump() const
+{
+ // Change these flags to fit your debugging needs.
+ bool bDumpItems = false;
+ bool bDumpSourceData = false;
+
+ cout << "--- pivot cache dump" << endl;
+ {
+ size_t i = 0;
+ for (const auto& rxField : maFields)
+ {
+ const Field& fld = *rxField;
+ cout << "* source dimension: " << GetDimensionName(i) << " (ID = " << i << ")" << endl;
+ cout << " item count: " << fld.maItems.size() << endl;
+ if (bDumpItems)
+ dumpItems(*this, i, fld.maItems, 0);
+ if (fld.mpGroup)
+ {
+ cout << " group item count: " << fld.mpGroup->maItems.size() << endl;
+ cout << " group type: " << getGroupTypeName(fld.mpGroup->mnGroupType) << endl;
+ if (bDumpItems)
+ dumpItems(*this, i, fld.mpGroup->maItems, fld.maItems.size());
+ }
+
+ if (bDumpSourceData)
+ {
+ cout << " source data (re-constructed):" << endl;
+ dumpSourceData(*this, i, fld.maItems, fld.maData);
+ }
+
+ ++i;
+ }
+ }
+
+ {
+ size_t i = maFields.size();
+ for (const auto& rxGroupField : maGroupFields)
+ {
+ const GroupItems& gi = *rxGroupField;
+ cout << "* group dimension: (unnamed) (ID = " << i << ")" << endl;
+ cout << " item count: " << gi.maItems.size() << endl;
+ cout << " group type: " << getGroupTypeName(gi.mnGroupType) << endl;
+ if (bDumpItems)
+ dumpItems(*this, i, gi.maItems, 0);
+ ++i;
+ }
+ }
+
+ {
+ struct { SCROW start; SCROW end; bool empty; } aRange;
+ cout << "* empty rows: " << endl;
+ mdds::flat_segment_tree<SCROW, bool>::const_iterator it = maEmptyRows.begin(), itEnd = maEmptyRows.end();
+ if (it != itEnd)
+ {
+ aRange.start = it->first;
+ aRange.empty = it->second;
+
+ for (++it; it != itEnd; ++it)
+ {
+ aRange.end = it->first-1;
+ cout << " rows " << aRange.start << "-" << aRange.end << ": " << (aRange.empty ? "empty" : "not-empty") << endl;
+ aRange.start = it->first;
+ aRange.empty = it->second;
+ }
+ }
+ }
+
+ cout << "---" << endl;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpdimsave.cxx b/sc/source/core/data/dpdimsave.cxx
new file mode 100644
index 0000000000..63811dc1f7
--- /dev/null
+++ b/sc/source/core/data/dpdimsave.cxx
@@ -0,0 +1,792 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpcache.hxx>
+#include <dpdimsave.hxx>
+#include <dpgroup.hxx>
+#include <dpobject.hxx>
+#include <dputil.hxx>
+#include <document.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+
+#include <svl/numformat.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <algorithm>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <utility>
+
+using namespace com::sun::star;
+
+ScDPSaveGroupItem::ScDPSaveGroupItem( OUString aName ) :
+ aGroupName(std::move(aName)) {}
+
+ScDPSaveGroupItem::~ScDPSaveGroupItem() {}
+
+void ScDPSaveGroupItem::AddElement( const OUString& rName )
+{
+ aElements.push_back(rName);
+}
+
+void ScDPSaveGroupItem::AddElementsFromGroup( const ScDPSaveGroupItem& rGroup )
+{
+ // add all elements of the other group (used for nested grouping)
+ aElements.insert( aElements.end(), rGroup.aElements.begin(), rGroup.aElements.end() );
+}
+
+bool ScDPSaveGroupItem::RemoveElement( const OUString& rName )
+{
+ auto it = std::find(aElements.begin(), aElements.end(), rName); //TODO: ignore case
+ if (it != aElements.end())
+ {
+ aElements.erase(it);
+ return true;
+ }
+ return false; // not found
+}
+
+bool ScDPSaveGroupItem::IsEmpty() const
+{
+ return aElements.empty();
+}
+
+size_t ScDPSaveGroupItem::GetElementCount() const
+{
+ return aElements.size();
+}
+
+const OUString* ScDPSaveGroupItem::GetElementByIndex(size_t nIndex) const
+{
+ return (nIndex < aElements.size()) ? &aElements[ nIndex ] : nullptr;
+}
+
+void ScDPSaveGroupItem::Rename( const OUString& rNewName )
+{
+ aGroupName = rNewName;
+}
+
+void ScDPSaveGroupItem::RemoveElementsFromGroups( ScDPSaveGroupDimension& rDimension ) const
+{
+ // remove this group's elements from their groups in rDimension
+ // (rDimension must be a different dimension from the one which contains this)
+
+ for ( const auto& rElement : aElements )
+ rDimension.RemoveFromGroups( rElement );
+}
+
+void ScDPSaveGroupItem::ConvertElementsToItems(SvNumberFormatter* pFormatter) const
+{
+ maItems.reserve(aElements.size());
+ for (const auto& rElement : aElements)
+ {
+ sal_uInt32 nFormat = 0;
+ double fValue;
+ ScDPItemData aData;
+ if (pFormatter->IsNumberFormat(rElement, nFormat, fValue))
+ aData.SetValue(fValue);
+ else
+ aData.SetString(rElement);
+
+ maItems.push_back(aData);
+ }
+}
+
+bool ScDPSaveGroupItem::HasInGroup(const ScDPItemData& rItem) const
+{
+ return std::find(maItems.begin(), maItems.end(), rItem) != maItems.end();
+}
+
+void ScDPSaveGroupItem::AddToData(ScDPGroupDimension& rDataDim) const
+{
+ ScDPGroupItem aGroup(aGroupName);
+ for (const auto& rItem : maItems)
+ aGroup.AddElement(rItem);
+
+ rDataDim.AddItem(aGroup);
+}
+
+ScDPSaveGroupDimension::ScDPSaveGroupDimension( OUString aSource, OUString aName ) :
+ aSourceDim(std::move( aSource )),
+ aGroupDimName(std::move( aName )),
+ nDatePart( 0 )
+{
+}
+
+ScDPSaveGroupDimension::ScDPSaveGroupDimension( OUString aSource, OUString aName, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nPart ) :
+ aSourceDim(std::move( aSource )),
+ aGroupDimName(std::move( aName )),
+ aDateInfo( rDateInfo ),
+ nDatePart( nPart )
+{
+}
+
+void ScDPSaveGroupDimension::SetDateInfo( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
+{
+ aDateInfo = rInfo;
+ nDatePart = nPart;
+}
+
+void ScDPSaveGroupDimension::AddGroupItem( const ScDPSaveGroupItem& rItem )
+{
+ aGroups.push_back( rItem );
+}
+
+OUString ScDPSaveGroupDimension::CreateGroupName(std::u16string_view rPrefix)
+{
+ // create a name for a new group, using "Group1", "Group2" etc. (translated prefix in rPrefix)
+
+ //TODO: look in all dimensions, to avoid clashes with automatic groups (=name of base element)?
+ //TODO: (only dimensions for the same base)
+
+ sal_Int32 nAdd = 1; // first try is "Group1"
+ const sal_Int32 nMaxAdd = nAdd + aGroups.size(); // limit the loop
+ while ( nAdd <= nMaxAdd )
+ {
+ OUString aGroupName = rPrefix + OUString::number( nAdd );
+
+ // look for existing groups
+ bool bExists = std::any_of(aGroups.begin(), aGroups.end(),
+ [&aGroupName](const ScDPSaveGroupItem& rGroup) {
+ return rGroup.GetGroupName() == aGroupName; //TODO: ignore case
+ });
+
+ if ( !bExists )
+ return aGroupName; // found a new name
+
+ ++nAdd; // continue with higher number
+ }
+
+ OSL_FAIL("CreateGroupName: no valid name found");
+ return OUString();
+}
+
+const ScDPSaveGroupItem* ScDPSaveGroupDimension::GetNamedGroup( const OUString& rGroupName ) const
+{
+ return const_cast< ScDPSaveGroupDimension* >( this )->GetNamedGroupAcc( rGroupName );
+}
+
+ScDPSaveGroupItem* ScDPSaveGroupDimension::GetNamedGroupAcc( const OUString& rGroupName )
+{
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&rGroupName](const ScDPSaveGroupItem& rGroup) {
+ return rGroup.GetGroupName() == rGroupName; //TODO: ignore case
+ });
+ if (aIter != aGroups.end())
+ return &*aIter;
+
+ return nullptr; // none found
+}
+
+tools::Long ScDPSaveGroupDimension::GetGroupCount() const
+{
+ return aGroups.size();
+}
+
+const ScDPSaveGroupItem& ScDPSaveGroupDimension::GetGroupByIndex( tools::Long nIndex ) const
+{
+ return aGroups[nIndex];
+}
+
+
+void ScDPSaveGroupDimension::RemoveFromGroups( const OUString& rItemName )
+{
+ // if the item is in any group, remove it from the group,
+ // also remove the group if it is empty afterwards
+
+ for ( ScDPSaveGroupItemVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
+ if ( aIter->RemoveElement( rItemName ) )
+ {
+ if ( aIter->IsEmpty() ) // removed last item from the group?
+ aGroups.erase( aIter ); // then remove the group
+
+ return; // don't have to look further
+ }
+}
+
+void ScDPSaveGroupDimension::RemoveGroup(const OUString& rGroupName)
+{
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&rGroupName](const ScDPSaveGroupItem& rGroup) {
+ return rGroup.GetGroupName() == rGroupName; //TODO: ignore case
+ });
+ if (aIter != aGroups.end())
+ aGroups.erase( aIter );
+}
+
+bool ScDPSaveGroupDimension::IsEmpty() const
+{
+ return aGroups.empty();
+}
+
+bool ScDPSaveGroupDimension::HasOnlyHidden(const ScDPUniqueStringSet& rVisible)
+{
+ // check if there are only groups that don't appear in the list of visible names
+
+ return std::none_of(aGroups.begin(), aGroups.end(),
+ [&rVisible](const ScDPSaveGroupItem& rGroup) { return rVisible.count(rGroup.GetGroupName()) > 0; });
+}
+
+void ScDPSaveGroupDimension::Rename( const OUString& rNewName )
+{
+ aGroupDimName = rNewName;
+}
+
+bool ScDPSaveGroupDimension::IsInGroup(const ScDPItemData& rItem) const
+{
+ return std::any_of(aGroups.begin(), aGroups.end(),
+ [&rItem](const ScDPSaveGroupItem& rGroup) { return rGroup.HasInGroup(rItem); });
+}
+
+namespace {
+
+bool isInteger(double fValue)
+{
+ return rtl::math::approxEqual(fValue, rtl::math::approxFloor(fValue));
+}
+
+void fillDateGroupDimension(
+ ScDPCache& rCache, ScDPNumGroupInfo& rDateInfo, tools::Long nSourceDim, tools::Long nGroupDim,
+ sal_Int32 nDatePart, const SvNumberFormatter* pFormatter)
+{
+ // Auto min/max is only used for "Years" part, but the loop is always
+ // needed.
+ double fSourceMin = 0.0;
+ double fSourceMax = 0.0;
+ bool bFirst = true;
+
+ const ScDPCache::ScDPItemDataVec& rItems = rCache.GetDimMemberValues(nSourceDim);
+ for (const ScDPItemData& rItem : rItems)
+ {
+ if (rItem.GetType() != ScDPItemData::Value)
+ continue;
+
+ double fVal = rItem.GetValue();
+ if (bFirst)
+ {
+ fSourceMin = fSourceMax = fVal;
+ bFirst = false;
+ }
+ else
+ {
+ if (fVal < fSourceMin)
+ fSourceMin = fVal;
+ if ( fVal > fSourceMax )
+ fSourceMax = fVal;
+ }
+ }
+
+ // For the start/end values, use the same date rounding as in
+ // ScDPNumGroupDimension::GetNumEntries (but not for the list of
+ // available years).
+ if (rDateInfo.mbAutoStart)
+ rDateInfo.mfStart = rtl::math::approxFloor(fSourceMin);
+ if (rDateInfo.mbAutoEnd)
+ rDateInfo.mfEnd = rtl::math::approxFloor(fSourceMax) + 1;
+
+ //TODO: if not automatic, limit fSourceMin/fSourceMax for list of year values?
+
+ tools::Long nStart = 0, nEnd = 0; // end is inclusive
+
+ switch (nDatePart)
+ {
+ case sheet::DataPilotFieldGroupBy::YEARS:
+ nStart = ScDPUtil::getDatePartValue(
+ fSourceMin, nullptr, sheet::DataPilotFieldGroupBy::YEARS, pFormatter);
+ nEnd = ScDPUtil::getDatePartValue(fSourceMax, nullptr, sheet::DataPilotFieldGroupBy::YEARS, pFormatter);
+ break;
+ case sheet::DataPilotFieldGroupBy::QUARTERS: nStart = 1; nEnd = 4; break;
+ case sheet::DataPilotFieldGroupBy::MONTHS: nStart = 1; nEnd = 12; break;
+ case sheet::DataPilotFieldGroupBy::DAYS: nStart = 1; nEnd = 366; break;
+ case sheet::DataPilotFieldGroupBy::HOURS: nStart = 0; nEnd = 23; break;
+ case sheet::DataPilotFieldGroupBy::MINUTES: nStart = 0; nEnd = 59; break;
+ case sheet::DataPilotFieldGroupBy::SECONDS: nStart = 0; nEnd = 59; break;
+ default:
+ OSL_FAIL("invalid date part");
+ }
+
+ // Now, populate the group items in the cache.
+ rCache.ResetGroupItems(nGroupDim, rDateInfo, nDatePart);
+
+ for (tools::Long nValue = nStart; nValue <= nEnd; ++nValue)
+ rCache.SetGroupItem(nGroupDim, ScDPItemData(nDatePart, nValue));
+
+ // add first/last entry (min/max)
+ rCache.SetGroupItem(nGroupDim, ScDPItemData(nDatePart, ScDPItemData::DateFirst));
+ rCache.SetGroupItem(nGroupDim, ScDPItemData(nDatePart, ScDPItemData::DateLast));
+}
+
+}
+
+void ScDPSaveGroupDimension::AddToData( ScDPGroupTableData& rData ) const
+{
+ tools::Long nSourceIndex = rData.GetDimensionIndex( aSourceDim );
+ if ( nSourceIndex < 0 )
+ return;
+
+ ScDPGroupDimension aDim( nSourceIndex, aGroupDimName );
+ if ( nDatePart )
+ {
+ // date grouping
+
+ aDim.SetDateDimension();
+ }
+ else
+ {
+ // normal (manual) grouping
+
+ for (const auto& rGroup : aGroups)
+ rGroup.AddToData(aDim);
+ }
+
+ rData.AddGroupDimension( aDim );
+}
+
+void ScDPSaveGroupDimension::AddToCache(ScDPCache& rCache) const
+{
+ tools::Long nSourceDim = rCache.GetDimensionIndex(aSourceDim);
+ if (nSourceDim < 0)
+ return;
+
+ tools::Long nDim = rCache.AppendGroupField();
+ SvNumberFormatter* pFormatter = rCache.GetDoc().GetFormatTable();
+
+ if (nDatePart)
+ {
+ fillDateGroupDimension(rCache, aDateInfo, nSourceDim, nDim, nDatePart, pFormatter);
+ return;
+ }
+
+ rCache.ResetGroupItems(nDim, aDateInfo, 0);
+ for (const ScDPSaveGroupItem& rGI : aGroups)
+ {
+ rGI.ConvertElementsToItems(pFormatter);
+ rCache.SetGroupItem(nDim, ScDPItemData(rGI.GetGroupName()));
+ }
+
+ const ScDPCache::ScDPItemDataVec& rItems = rCache.GetDimMemberValues(nSourceDim);
+ for (const ScDPItemData& rItem : rItems)
+ {
+ if (!IsInGroup(rItem))
+ // Not in any group. Add as its own group.
+ rCache.SetGroupItem(nDim, rItem);
+ }
+}
+
+ScDPSaveNumGroupDimension::ScDPSaveNumGroupDimension( OUString aName, const ScDPNumGroupInfo& rInfo ) :
+ aDimensionName(std::move( aName )),
+ aGroupInfo( rInfo ),
+ nDatePart( 0 )
+{
+}
+
+ScDPSaveNumGroupDimension::ScDPSaveNumGroupDimension( OUString aName, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nPart ) :
+ aDimensionName(std::move( aName )),
+ aDateInfo( rDateInfo ),
+ nDatePart( nPart )
+{
+}
+
+void ScDPSaveNumGroupDimension::AddToData( ScDPGroupTableData& rData ) const
+{
+ tools::Long nSource = rData.GetDimensionIndex( aDimensionName );
+ if ( nSource >= 0 )
+ {
+ ScDPNumGroupDimension aDim( aGroupInfo ); // aGroupInfo: value grouping
+ if ( nDatePart )
+ aDim.SetDateDimension();
+
+ rData.SetNumGroupDimension( nSource, aDim );
+ }
+}
+
+void ScDPSaveNumGroupDimension::AddToCache(ScDPCache& rCache) const
+{
+ tools::Long nDim = rCache.GetDimensionIndex(aDimensionName);
+ if (nDim < 0)
+ return;
+
+ if (aDateInfo.mbEnable)
+ {
+ // Date grouping
+ SvNumberFormatter* pFormatter = rCache.GetDoc().GetFormatTable();
+ fillDateGroupDimension(rCache, aDateInfo, nDim, nDim, nDatePart, pFormatter);
+ }
+ else if (aGroupInfo.mbEnable)
+ {
+ // Number-range grouping
+
+ // Look through the source entries for non-integer numbers, minimum
+ // and maximum.
+
+ // non-integer GroupInfo values count, too
+ aGroupInfo.mbIntegerOnly =
+ (aGroupInfo.mbAutoStart || isInteger(aGroupInfo.mfStart)) &&
+ (aGroupInfo.mbAutoEnd || isInteger(aGroupInfo.mfEnd)) &&
+ isInteger(aGroupInfo.mfStep);
+
+ double fSourceMin = 0.0;
+ double fSourceMax = 0.0;
+ bool bFirst = true;
+
+ const ScDPCache::ScDPItemDataVec& rItems = rCache.GetDimMemberValues(nDim);
+ for (const ScDPItemData& rItem : rItems)
+ {
+ if (rItem.GetType() != ScDPItemData::Value)
+ continue;
+
+ double fValue = rItem.GetValue();
+ if (bFirst)
+ {
+ fSourceMin = fSourceMax = fValue;
+ bFirst = false;
+ continue;
+ }
+
+ if (fValue < fSourceMin)
+ fSourceMin = fValue;
+ if (fValue > fSourceMax)
+ fSourceMax = fValue;
+
+ if (aGroupInfo.mbIntegerOnly && !isInteger(fValue))
+ {
+ // If any non-integer numbers are involved, the group labels
+ // are shown including their upper limit.
+ aGroupInfo.mbIntegerOnly = false;
+ }
+ }
+
+ if (aGroupInfo.mbDateValues)
+ {
+ // special handling for dates: always integer, round down limits
+ aGroupInfo.mbIntegerOnly = true;
+ fSourceMin = rtl::math::approxFloor(fSourceMin);
+ fSourceMax = rtl::math::approxFloor(fSourceMax) + 1;
+ }
+
+ if (aGroupInfo.mbAutoStart)
+ aGroupInfo.mfStart = fSourceMin;
+ if (aGroupInfo.mbAutoEnd)
+ aGroupInfo.mfEnd = fSourceMax;
+
+ //TODO: limit number of entries?
+
+ tools::Long nLoopCount = 0;
+ double fLoop = aGroupInfo.mfStart;
+
+ rCache.ResetGroupItems(nDim, aGroupInfo, 0);
+
+ // Use "less than" instead of "less or equal" for the loop - don't
+ // create a group that consists only of the end value. Instead, the
+ // end value is then included in the last group (last group is bigger
+ // than the others). The first group has to be created nonetheless.
+ // GetNumGroupForValue has corresponding logic.
+
+ bool bFirstGroup = true;
+ while (bFirstGroup || (fLoop < aGroupInfo.mfEnd && !rtl::math::approxEqual(fLoop, aGroupInfo.mfEnd)))
+ {
+ ScDPItemData aItem;
+ aItem.SetRangeStart(fLoop);
+ rCache.SetGroupItem(nDim, aItem);
+ ++nLoopCount;
+ fLoop = aGroupInfo.mfStart + nLoopCount * aGroupInfo.mfStep;
+ bFirstGroup = false;
+
+ // ScDPItemData values are compared with approxEqual
+ }
+
+ ScDPItemData aItem;
+ aItem.SetRangeFirst();
+ rCache.SetGroupItem(nDim, aItem);
+
+ aItem.SetRangeLast();
+ rCache.SetGroupItem(nDim, aItem);
+ }
+}
+
+void ScDPSaveNumGroupDimension::SetGroupInfo( const ScDPNumGroupInfo& rNew )
+{
+ aGroupInfo = rNew;
+}
+
+void ScDPSaveNumGroupDimension::SetDateInfo( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
+{
+ aDateInfo = rInfo;
+ nDatePart = nPart;
+}
+
+namespace {
+
+struct ScDPSaveGroupDimNameFunc
+{
+ OUString maDimName;
+ explicit ScDPSaveGroupDimNameFunc( OUString aDimName ) : maDimName(std::move( aDimName )) {}
+ bool operator()( const ScDPSaveGroupDimension& rGroupDim ) const { return rGroupDim.GetGroupDimName() == maDimName; }
+};
+
+struct ScDPSaveGroupSourceNameFunc
+{
+ OUString maSrcDimName;
+ explicit ScDPSaveGroupSourceNameFunc( OUString aSrcDimName ) : maSrcDimName(std::move( aSrcDimName )) {}
+ bool operator()( const ScDPSaveGroupDimension& rGroupDim ) const { return rGroupDim.GetSourceDimName() == maSrcDimName; }
+};
+
+} // namespace
+
+ScDPDimensionSaveData::ScDPDimensionSaveData()
+{
+}
+
+bool ScDPDimensionSaveData::operator==( const ScDPDimensionSaveData& ) const
+{
+ return false;
+}
+
+void ScDPDimensionSaveData::AddGroupDimension( const ScDPSaveGroupDimension& rGroupDim )
+{
+ OSL_ENSURE( ::std::none_of( maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDim.GetGroupDimName() ) ),
+ "ScDPDimensionSaveData::AddGroupDimension - group dimension exists already" );
+ // ReplaceGroupDimension() adds new or replaces existing
+ ReplaceGroupDimension( rGroupDim );
+}
+
+void ScDPDimensionSaveData::ReplaceGroupDimension( const ScDPSaveGroupDimension& rGroupDim )
+{
+ ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
+ maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDim.GetGroupDimName() ) );
+ if( aIt == maGroupDims.end() )
+ maGroupDims.push_back( rGroupDim );
+ else
+ *aIt = rGroupDim;
+}
+
+void ScDPDimensionSaveData::RemoveGroupDimension( const OUString& rGroupDimName )
+{
+ ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
+ maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) );
+ if( aIt != maGroupDims.end() )
+ maGroupDims.erase( aIt );
+}
+
+void ScDPDimensionSaveData::AddNumGroupDimension( const ScDPSaveNumGroupDimension& rGroupDim )
+{
+ OSL_ENSURE( maNumGroupDims.count( rGroupDim.GetDimensionName() ) == 0,
+ "ScDPDimensionSaveData::AddNumGroupDimension - numeric group dimension exists already" );
+ // ReplaceNumGroupDimension() adds new or replaces existing
+ ReplaceNumGroupDimension( rGroupDim );
+}
+
+void ScDPDimensionSaveData::ReplaceNumGroupDimension( const ScDPSaveNumGroupDimension& rGroupDim )
+{
+ ScDPSaveNumGroupDimMap::iterator aIt = maNumGroupDims.find( rGroupDim.GetDimensionName() );
+ if( aIt == maNumGroupDims.end() )
+ maNumGroupDims.emplace( rGroupDim.GetDimensionName(), rGroupDim );
+ else
+ aIt->second = rGroupDim;
+}
+
+void ScDPDimensionSaveData::RemoveNumGroupDimension( const OUString& rGroupDimName )
+{
+ maNumGroupDims.erase( rGroupDimName );
+}
+
+void ScDPDimensionSaveData::WriteToData( ScDPGroupTableData& rData ) const
+{
+ // rData is assumed to be empty
+ // AddToData also handles date grouping
+
+ for( const auto& rGroupDim : maGroupDims )
+ rGroupDim.AddToData( rData );
+
+ for( const auto& rEntry : maNumGroupDims )
+ rEntry.second.AddToData( rData );
+}
+
+void ScDPDimensionSaveData::WriteToCache(ScDPCache& rCache) const
+{
+ for (const auto& rEntry : maGroupDims)
+ rEntry.AddToCache(rCache);
+ for (const auto& rEntry : maNumGroupDims)
+ rEntry.second.AddToCache(rCache);
+}
+
+const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetGroupDimForBase( const OUString& rBaseDimName ) const
+{
+ return const_cast< ScDPDimensionSaveData* >( this )->GetGroupDimAccForBase( rBaseDimName );
+}
+
+const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNamedGroupDim( const OUString& rGroupDimName ) const
+{
+ return const_cast< ScDPDimensionSaveData* >( this )->GetNamedGroupDimAcc( rGroupDimName );
+}
+
+const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetFirstNamedGroupDim( const OUString& rBaseDimName ) const
+{
+ return const_cast< ScDPDimensionSaveData* >( this )->GetFirstNamedGroupDimAcc( rBaseDimName );
+}
+
+const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNextNamedGroupDim( const OUString& rGroupDimName ) const
+{
+ return const_cast< ScDPDimensionSaveData* >( this )->GetNextNamedGroupDimAcc( rGroupDimName );
+}
+
+const ScDPSaveNumGroupDimension* ScDPDimensionSaveData::GetNumGroupDim( const OUString& rGroupDimName ) const
+{
+ return const_cast< ScDPDimensionSaveData* >( this )->GetNumGroupDimAcc( rGroupDimName );
+}
+
+ScDPSaveGroupDimension* ScDPDimensionSaveData::GetGroupDimAccForBase( const OUString& rBaseDimName )
+{
+ ScDPSaveGroupDimension* pGroupDim = GetFirstNamedGroupDimAcc( rBaseDimName );
+ return pGroupDim ? pGroupDim : GetNextNamedGroupDimAcc( rBaseDimName );
+}
+
+ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNamedGroupDimAcc( const OUString& rGroupDimName )
+{
+ ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
+ maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) );
+ return (aIt == maGroupDims.end()) ? nullptr : &*aIt;
+}
+
+ScDPSaveGroupDimension* ScDPDimensionSaveData::GetFirstNamedGroupDimAcc( const OUString& rBaseDimName )
+{
+ ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
+ maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupSourceNameFunc( rBaseDimName ) );
+ return (aIt == maGroupDims.end()) ? nullptr : &*aIt;
+}
+
+ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNextNamedGroupDimAcc( const OUString& rGroupDimName )
+{
+ // find the group dimension with the passed name
+ ScDPSaveGroupDimVec::iterator aIt = ::std::find_if(
+ maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) );
+ // find next group dimension based on the same source dimension name
+ if( aIt != maGroupDims.end() )
+ aIt = ::std::find_if( aIt + 1, maGroupDims.end(), ScDPSaveGroupSourceNameFunc( aIt->GetSourceDimName() ) );
+ return (aIt == maGroupDims.end()) ? nullptr : &*aIt;
+}
+
+ScDPSaveNumGroupDimension* ScDPDimensionSaveData::GetNumGroupDimAcc( const OUString& rGroupDimName )
+{
+ ScDPSaveNumGroupDimMap::iterator aIt = maNumGroupDims.find( rGroupDimName );
+ return (aIt == maNumGroupDims.end()) ? nullptr : &aIt->second;
+}
+
+bool ScDPDimensionSaveData::HasGroupDimensions() const
+{
+ return !maGroupDims.empty() || !maNumGroupDims.empty();
+}
+
+sal_Int32 ScDPDimensionSaveData::CollectDateParts( const OUString& rBaseDimName ) const
+{
+ sal_Int32 nParts = 0;
+ // start with part of numeric group
+ if( const ScDPSaveNumGroupDimension* pNumDim = GetNumGroupDim( rBaseDimName ) )
+ nParts |= pNumDim->GetDatePart();
+ // collect parts from all matching group dimensions
+ for( const ScDPSaveGroupDimension* pGroupDim = GetFirstNamedGroupDim( rBaseDimName ); pGroupDim; pGroupDim = GetNextNamedGroupDim( pGroupDim->GetGroupDimName() ) )
+ nParts |= pGroupDim->GetDatePart();
+
+ return nParts;
+}
+
+OUString ScDPDimensionSaveData::CreateGroupDimName(
+ const OUString& rSourceName, const ScDPObject& rObject, bool bAllowSource,
+ const std::vector<OUString>* pDeletedNames )
+{
+ // create a name for the new dimension by appending a number to the original
+ // dimension's name
+
+ bool bUseSource = bAllowSource; // if set, try the unchanged original name first
+
+ sal_Int32 nAdd = 2; // first try is "Name2"
+ const sal_Int32 nMaxAdd = 1000; // limit the loop
+ while ( nAdd <= nMaxAdd )
+ {
+ OUString aDimName( rSourceName );
+ if ( !bUseSource )
+ aDimName += OUString::number(nAdd);
+
+ // look for existing group dimensions
+ bool bExists = std::any_of(maGroupDims.begin(), maGroupDims.end(),
+ [&aDimName](const ScDPSaveGroupDimension& rDim) {
+ return rDim.GetGroupDimName() == aDimName; //TODO: ignore case
+ });
+
+ // look for base dimensions that happen to have that name
+ if ( !bExists && rObject.IsDimNameInUse( aDimName ) )
+ {
+ if ( pDeletedNames &&
+ std::find( pDeletedNames->begin(), pDeletedNames->end(), aDimName ) != pDeletedNames->end() )
+ {
+ // allow the name anyway if the name is in pDeletedNames
+ }
+ else
+ bExists = true;
+ }
+
+ if ( !bExists )
+ return aDimName; // found a new name
+
+ if ( bUseSource )
+ bUseSource = false;
+ else
+ ++nAdd; // continue with higher number
+ }
+ OSL_FAIL("CreateGroupDimName: no valid name found");
+ return OUString();
+}
+
+namespace
+{
+ const TranslateId aDatePartIds[] =
+ {
+ STR_DPFIELD_GROUP_BY_SECONDS,
+ STR_DPFIELD_GROUP_BY_MINUTES,
+ STR_DPFIELD_GROUP_BY_HOURS,
+ STR_DPFIELD_GROUP_BY_DAYS,
+ STR_DPFIELD_GROUP_BY_MONTHS,
+ STR_DPFIELD_GROUP_BY_QUARTERS,
+ STR_DPFIELD_GROUP_BY_YEARS
+ };
+}
+
+OUString ScDPDimensionSaveData::CreateDateGroupDimName(
+ sal_Int32 nDatePart, const ScDPObject& rObject, bool bAllowSource,
+ const std::vector<OUString>* pDeletedNames )
+{
+ using namespace css::sheet::DataPilotFieldGroupBy;
+ OUString aPartName;
+ switch( nDatePart )
+ {
+ case SECONDS: aPartName = ScResId(aDatePartIds[0]); break;
+ case MINUTES: aPartName = ScResId(aDatePartIds[1]); break;
+ case HOURS: aPartName = ScResId(aDatePartIds[2]); break;
+ case DAYS: aPartName = ScResId(aDatePartIds[3]); break;
+ case MONTHS: aPartName = ScResId(aDatePartIds[4]); break;
+ case QUARTERS: aPartName = ScResId(aDatePartIds[5]); break;
+ case YEARS: aPartName = ScResId(aDatePartIds[6]); break;
+ }
+ OSL_ENSURE(!aPartName.isEmpty(), "ScDPDimensionSaveData::CreateDateGroupDimName - invalid date part");
+ return CreateGroupDimName( aPartName, rObject, bAllowSource, pDeletedNames );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpfilteredcache.cxx b/sc/source/core/data/dpfilteredcache.cxx
new file mode 100644
index 0000000000..b47fc43aeb
--- /dev/null
+++ b/sc/source/core/data/dpfilteredcache.cxx
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpcache.hxx>
+#include <dpfilteredcache.hxx>
+#include <address.hxx>
+#include <queryparam.hxx>
+#include <dpitemdata.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <algorithm>
+
+using ::std::vector;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+
+ScDPFilteredCache::SingleFilter::SingleFilter(const ScDPItemData& rItem) :
+ maItem(rItem) {}
+
+bool ScDPFilteredCache::SingleFilter::match(const ScDPItemData& rCellData) const
+{
+ return maItem == rCellData;
+}
+
+std::vector<ScDPItemData> ScDPFilteredCache::SingleFilter::getMatchValues() const
+{
+ return { maItem };
+}
+
+ScDPFilteredCache::GroupFilter::GroupFilter()
+{
+}
+
+bool ScDPFilteredCache::GroupFilter::match(const ScDPItemData& rCellData) const
+{
+ return std::find(maItems.begin(), maItems.end(), rCellData) != maItems.end();
+}
+
+std::vector<ScDPItemData> ScDPFilteredCache::GroupFilter::getMatchValues() const
+{
+ return maItems;
+}
+
+void ScDPFilteredCache::GroupFilter::addMatchItem(const ScDPItemData& rItem)
+{
+ maItems.push_back(rItem);
+}
+
+size_t ScDPFilteredCache::GroupFilter::getMatchItemCount() const
+{
+ return maItems.size();
+}
+
+ScDPFilteredCache::Criterion::Criterion() :
+ mnFieldIndex(-1)
+{
+}
+
+ScDPFilteredCache::ScDPFilteredCache(const ScDPCache& rCache) :
+ maShowByFilter(0, MAXROW+1, false), maShowByPage(0, MAXROW+1, true), mrCache(rCache)
+{
+}
+
+ScDPFilteredCache::~ScDPFilteredCache()
+{
+}
+
+sal_Int32 ScDPFilteredCache::getRowSize() const
+{
+ return mrCache.GetRowCount();
+}
+
+sal_Int32 ScDPFilteredCache::getColSize() const
+{
+ return mrCache.GetColumnCount();
+}
+
+void ScDPFilteredCache::fillTable(
+ const ScQueryParam& rQuery, bool bIgnoreEmptyRows, bool bRepeatIfEmpty)
+{
+ SCROW nRowCount = getRowSize();
+ SCROW nDataSize = mrCache.GetDataSize();
+ SCCOL nColCount = getColSize();
+ if (nRowCount <= 0 || nColCount <= 0)
+ return;
+
+ maShowByFilter.clear();
+ maShowByPage.clear();
+ maShowByPage.build_tree();
+
+ // Process the non-empty data rows.
+ for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
+ {
+ if (!getCache().ValidQuery(nRow, rQuery))
+ continue;
+
+ if (bIgnoreEmptyRows && getCache().IsRowEmpty(nRow))
+ continue;
+
+ maShowByFilter.insert_back(nRow, nRow+1, true);
+ }
+
+ // Process the trailing empty rows.
+ if (!bIgnoreEmptyRows)
+ maShowByFilter.insert_back(nDataSize, nRowCount, true);
+
+ maShowByFilter.build_tree();
+
+ // Initialize field entries container.
+ maFieldEntries.clear();
+ maFieldEntries.reserve(nColCount);
+
+ // Build unique field entries.
+ for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
+ {
+ maFieldEntries.emplace_back( );
+ SCROW nMemCount = getCache().GetDimMemberCount( nCol );
+ if (!nMemCount)
+ continue;
+
+ std::vector<SCROW> aAdded(nMemCount, -1);
+ bool bShow = false;
+ SCROW nEndSegment = -1;
+ for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
+ {
+ if (nRow > nEndSegment)
+ {
+ if (!maShowByFilter.search_tree(nRow, bShow, nullptr, &nEndSegment).second)
+ {
+ OSL_FAIL("Tree search failed!");
+ continue;
+ }
+ --nEndSegment; // End position is not inclusive. Move back one.
+ }
+
+ if (!bShow)
+ {
+ nRow = nEndSegment;
+ continue;
+ }
+
+ SCROW nIndex = getCache().GetItemDataId(nCol, nRow, bRepeatIfEmpty);
+ aAdded[nIndex] = nIndex;
+
+ // tdf#96588 - large numbers of trailing identical empty
+ // rows generate the same nIndex & nOrder.
+ if (nRow == nDataSize)
+ break;
+ }
+ for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
+ {
+ if (aAdded[nRow] != -1)
+ maFieldEntries.back().push_back(aAdded[nRow]);
+ }
+ }
+}
+
+void ScDPFilteredCache::fillTable()
+{
+ SCROW nRowCount = getRowSize();
+ SCCOL nColCount = getColSize();
+ if (nRowCount <= 0 || nColCount <= 0)
+ return;
+
+ maShowByPage.clear();
+ maShowByPage.build_tree();
+
+ maShowByFilter.clear();
+ maShowByFilter.insert_front(0, nRowCount, true);
+ maShowByFilter.build_tree();
+
+ // Initialize field entries container.
+ maFieldEntries.clear();
+ maFieldEntries.reserve(nColCount);
+
+ // Data rows
+ for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
+ {
+ maFieldEntries.emplace_back( );
+ SCROW nMemCount = getCache().GetDimMemberCount( nCol );
+ if (!nMemCount)
+ continue;
+
+ std::vector<SCROW> aAdded(nMemCount, -1);
+
+ for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
+ {
+ SCROW nIndex = getCache().GetItemDataId(nCol, nRow, false);
+ aAdded[nIndex] = nIndex;
+ }
+ for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
+ {
+ if (aAdded[nRow] != -1)
+ maFieldEntries.back().push_back(aAdded[nRow]);
+ }
+ }
+}
+
+bool ScDPFilteredCache::isRowActive(sal_Int32 nRow, sal_Int32* pLastRow) const
+{
+ bool bFilter = false, bPage = true;
+ SCROW nLastRowFilter = MAXROW, nLastRowPage = MAXROW;
+ maShowByFilter.search_tree(nRow, bFilter, nullptr, &nLastRowFilter);
+ maShowByPage.search_tree(nRow, bPage, nullptr, &nLastRowPage);
+ if (pLastRow)
+ {
+ // Return the last row of current segment.
+ *pLastRow = std::min(nLastRowFilter, nLastRowPage);
+ *pLastRow -= 1; // End position is not inclusive. Move back one.
+ }
+
+ return bFilter && bPage;
+}
+
+void ScDPFilteredCache::filterByPageDimension(const vector<Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
+{
+ SCROW nRowSize = getRowSize();
+ SCROW nDataSize = mrCache.GetDataSize();
+
+ maShowByPage.clear();
+
+ for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
+ {
+ bool bShow = isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims);
+ maShowByPage.insert_back(nRow, nRow+1, bShow);
+ }
+
+ // tdf#96588 - rapidly extend for blank rows with identical data
+ if (nDataSize < nRowSize)
+ {
+ bool bBlankShow = isRowQualified(nDataSize, rCriteria, rRepeatIfEmptyDims);
+ maShowByPage.insert_back(nDataSize, nRowSize, bBlankShow);
+ }
+
+ maShowByPage.build_tree();
+}
+
+const ScDPItemData* ScDPFilteredCache::getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
+{
+ SCROW nId= mrCache.GetItemDataId(nCol, nRow, bRepeatIfEmpty);
+ return mrCache.GetItemDataById( nCol, nId );
+}
+
+void ScDPFilteredCache::getValue( ScDPValue& rVal, SCCOL nCol, SCROW nRow) const
+{
+ const ScDPItemData* pData = getCell( nCol, nRow, false/*bRepeatIfEmpty*/ );
+
+ if (pData)
+ {
+ rVal.mfValue = pData->IsValue() ? pData->GetValue() : 0.0;
+ rVal.meType = pData->GetCellType();
+ }
+ else
+ rVal.Set(0.0, ScDPValue::Empty);
+}
+
+OUString ScDPFilteredCache::getFieldName(SCCOL nIndex) const
+{
+ return mrCache.GetDimensionName(nIndex);
+}
+
+const ::std::vector<SCROW>& ScDPFilteredCache::getFieldEntries( sal_Int32 nColumn ) const
+{
+ if (nColumn < 0 || o3tl::make_unsigned(nColumn) >= maFieldEntries.size())
+ {
+ // index out of bound. Hopefully this code will never be reached.
+ static const ::std::vector<SCROW> emptyEntries{};
+ return emptyEntries;
+ }
+ return maFieldEntries[nColumn];
+}
+
+void ScDPFilteredCache::filterTable(const vector<Criterion>& rCriteria, Sequence< Sequence<Any> >& rTabData,
+ const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
+{
+ sal_Int32 nRowSize = getRowSize();
+ SCCOL nColSize = getColSize();
+
+ if (!nRowSize)
+ // no data to filter.
+ return;
+
+ // Row first, then column.
+ vector< Sequence<Any> > tableData;
+ tableData.reserve(nRowSize+1);
+
+ // Header first.
+ Sequence<Any> headerRow(nColSize);
+ auto pRow = headerRow.getArray();
+ for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
+ {
+ OUString str = getFieldName( nCol);
+ Any any;
+ any <<= str;
+ pRow[nCol] = any;
+ }
+ tableData.push_back(headerRow);
+
+ for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
+ {
+ sal_Int32 nLastRow;
+ if (!isRowActive(nRow, &nLastRow))
+ {
+ // This row is filtered out.
+ nRow = nLastRow;
+ continue;
+ }
+
+ if (!isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims))
+ continue;
+
+ // Insert this row into table.
+
+ Sequence<Any> row(nColSize);
+ pRow = row.getArray();
+ for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
+ {
+ Any any;
+ bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(nCol) > 0;
+ const ScDPItemData* pData= getCell(nCol, nRow, bRepeatIfEmpty);
+ if ( pData->IsValue() )
+ any <<= pData->GetValue();
+ else
+ {
+ OUString string (pData->GetString() );
+ any <<= string;
+ }
+ pRow[nCol] = any;
+ }
+ tableData.push_back(row);
+ }
+
+ // convert vector to Sequence
+ sal_Int32 nTabSize = static_cast<sal_Int32>(tableData.size());
+ rTabData.realloc(nTabSize);
+ auto pTabData = rTabData.getArray();
+ for (sal_Int32 i = 0; i < nTabSize; ++i)
+ pTabData[i] = tableData[i];
+}
+
+void ScDPFilteredCache::clear()
+{
+ maFieldEntries.clear();
+ maShowByFilter.clear();
+ maShowByPage.clear();
+}
+
+bool ScDPFilteredCache::empty() const
+{
+ return maFieldEntries.empty();
+}
+
+bool ScDPFilteredCache::isRowQualified(sal_Int32 nRow, const vector<Criterion>& rCriteria,
+ const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims) const
+{
+ sal_Int32 nColSize = getColSize();
+ for (const auto& rCriterion : rCriteria)
+ {
+ if (rCriterion.mnFieldIndex >= nColSize)
+ // specified field is outside the source data columns. Don't
+ // use this criterion.
+ continue;
+
+ // Check if the 'repeat if empty' flag is set for this field.
+ bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(rCriterion.mnFieldIndex) > 0;
+ const ScDPItemData* pCellData = getCell(static_cast<SCCOL>(rCriterion.mnFieldIndex), nRow, bRepeatIfEmpty);
+ if (!rCriterion.mpFilter->match(*pCellData))
+ return false;
+ }
+ return true;
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPFilteredCache::dumpRowFlag( const RowFlagType& rFlag )
+{
+ RowFlagType::const_iterator it = rFlag.begin(), itEnd = rFlag.end();
+ bool bShow = it->second;
+ SCROW nRow1 = it->first;
+ for (++it; it != itEnd; ++it)
+ {
+ SCROW nRow2 = it->first;
+ cout << " * range " << nRow1 << "-" << nRow2 << ": " << (bShow ? "on" : "off") << endl;
+ bShow = it->second;
+ nRow1 = nRow2;
+ }
+}
+
+void ScDPFilteredCache::dump() const
+{
+ cout << "--- pivot filtered cache dump" << endl;
+
+ cout << endl;
+ cout << "* show by filter" << endl;
+ dumpRowFlag(maShowByFilter);
+
+ cout << endl;
+ cout << "* show by page dimensions" << endl;
+ dumpRowFlag(maShowByPage);
+
+ cout << endl;
+ cout << "* field entries" << endl;
+ size_t nFieldCount = maFieldEntries.size();
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ const vector<SCROW>& rField = maFieldEntries[i];
+ cout << " * field " << i << endl;
+ for (size_t j = 0, n = rField.size(); j < n; ++j)
+ cout << " ID: " << rField[j] << endl;
+ }
+ cout << "---" << endl;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpglobal.cxx b/sc/source/core/data/dpglobal.cxx
new file mode 100644
index 0000000000..ac72054e73
--- /dev/null
+++ b/sc/source/core/data/dpglobal.cxx
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <dpglobal.hxx>
+
+ScDPValue::ScDPValue() : mfValue(0.0), meType(String) {}
+
+void ScDPValue::Set( double fV, Type eT )
+{
+ mfValue = fV;
+ meType = eT;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpgroup.cxx b/sc/source/core/data/dpgroup.cxx
new file mode 100644
index 0000000000..336388a36d
--- /dev/null
+++ b/sc/source/core/data/dpgroup.cxx
@@ -0,0 +1,1031 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpgroup.hxx>
+
+#include <dpcache.hxx>
+#include <document.hxx>
+#include <dpfilteredcache.hxx>
+#include <dputil.hxx>
+
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+
+using ::std::vector;
+using ::std::shared_ptr;
+
+const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
+
+namespace {
+
+class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase
+{
+public:
+ ScDPGroupNumFilter(std::vector<ScDPItemData>&& rValues, const ScDPNumGroupInfo& rInfo);
+
+ virtual bool match(const ScDPItemData &rCellData) const override;
+ virtual std::vector<ScDPItemData> getMatchValues() const override;
+private:
+ std::vector<ScDPItemData> maValues;
+ ScDPNumGroupInfo maNumInfo;
+};
+
+}
+
+ScDPGroupNumFilter::ScDPGroupNumFilter( std::vector<ScDPItemData>&& rValues, const ScDPNumGroupInfo& rInfo) :
+ maValues(std::move(rValues)), maNumInfo(rInfo) {}
+
+bool ScDPGroupNumFilter::match(const ScDPItemData& rCellData) const
+{
+ if (rCellData.GetType() != ScDPItemData::Value)
+ return false;
+
+ for (const auto& rValue : maValues)
+ {
+ double fVal = rValue.GetValue();
+ if (std::isinf(fVal))
+ {
+ if (std::signbit(fVal))
+ {
+ // Less than the min value.
+ if (rCellData.GetValue() < maNumInfo.mfStart)
+ return true;
+ }
+
+ // Greater than the max value.
+ if (maNumInfo.mfEnd < rCellData.GetValue())
+ return true;
+
+ continue;
+ }
+
+ double low = fVal;
+ double high = low + maNumInfo.mfStep;
+ if (maNumInfo.mbIntegerOnly)
+ high += 1.0;
+
+ if (low <= rCellData.GetValue() && rCellData.GetValue() < high)
+ return true;
+ }
+
+ return false;
+}
+
+std::vector<ScDPItemData> ScDPGroupNumFilter::getMatchValues() const
+{
+ return std::vector<ScDPItemData>();
+}
+
+namespace {
+
+class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase
+{
+public:
+ ScDPGroupDateFilter(
+ std::vector<ScDPItemData>&& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo);
+
+ virtual bool match(const ScDPItemData & rCellData) const override;
+ virtual std::vector<ScDPItemData> getMatchValues() const override;
+
+private:
+ std::vector<ScDPItemData> maValues;
+ Date maNullDate;
+ ScDPNumGroupInfo maNumInfo;
+};
+
+}
+
+ScDPGroupDateFilter::ScDPGroupDateFilter(
+ std::vector<ScDPItemData>&& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo) :
+ maValues(std::move(rValues)),
+ maNullDate(rNullDate),
+ maNumInfo(rNumInfo)
+{
+}
+
+bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
+{
+ using namespace ::com::sun::star::sheet;
+ using ::rtl::math::approxFloor;
+ using ::rtl::math::approxEqual;
+
+ if ( !rCellData.IsValue() )
+ return false;
+
+ for (const ScDPItemData& rValue : maValues)
+ {
+ if (rValue.GetType() != ScDPItemData::GroupValue)
+ continue;
+
+ sal_Int32 nGroupType = rValue.GetGroupValue().mnGroupType;
+ sal_Int32 nValue = rValue.GetGroupValue().mnValue;
+
+ // Start and end dates are inclusive. (An end date without a time value
+ // is included, while an end date with a time value is not.)
+
+ if (rCellData.GetValue() < maNumInfo.mfStart && !approxEqual(rCellData.GetValue(), maNumInfo.mfStart))
+ {
+ if (nValue == ScDPItemData::DateFirst)
+ return true;
+ continue;
+ }
+
+ if (rCellData.GetValue() > maNumInfo.mfEnd && !approxEqual(rCellData.GetValue(), maNumInfo.mfEnd))
+ {
+ if (nValue == ScDPItemData::DateLast)
+ return true;
+ continue;
+ }
+
+ if (nGroupType == DataPilotFieldGroupBy::HOURS || nGroupType == DataPilotFieldGroupBy::MINUTES ||
+ nGroupType == DataPilotFieldGroupBy::SECONDS)
+ {
+ // handle time
+ // (do as in the cell functions, ScInterpreter::ScGetHour() etc.)
+
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( rCellData.GetValue(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
+
+ switch (nGroupType)
+ {
+ case DataPilotFieldGroupBy::HOURS:
+ {
+ if (nHour == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::MINUTES:
+ {
+ if (nMinute == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::SECONDS:
+ {
+ if (nSecond == nValue)
+ return true;
+ }
+ break;
+ default:
+ OSL_FAIL("invalid time part");
+ }
+
+ continue;
+ }
+
+ Date date = maNullDate + static_cast<sal_Int32>(approxFloor(rCellData.GetValue()));
+ switch (nGroupType)
+ {
+ case DataPilotFieldGroupBy::YEARS:
+ {
+ sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
+ if (year == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::QUARTERS:
+ {
+ sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
+ if (qtr == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::MONTHS:
+ {
+ sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
+ if (month == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::DAYS:
+ {
+ Date yearStart(1, 1, date.GetYear());
+ sal_Int32 days = (date - yearStart) + 1; // Jan 01 has value 1
+ if (days >= 60 && !date.IsLeapYear())
+ {
+ // This is not a leap year. Adjust the value accordingly.
+ ++days;
+ }
+ if (days == nValue)
+ return true;
+ }
+ break;
+ default:
+ OSL_FAIL("invalid date part");
+ }
+ }
+
+ return false;
+}
+
+std::vector<ScDPItemData> ScDPGroupDateFilter::getMatchValues() const
+{
+ return std::vector<ScDPItemData>();
+}
+
+namespace {
+
+bool isDateInGroup(const ScDPItemData& rGroupItem, const ScDPItemData& rChildItem)
+{
+ if (rGroupItem.GetType() != ScDPItemData::GroupValue || rChildItem.GetType() != ScDPItemData::GroupValue)
+ return false;
+
+ sal_Int32 nGroupPart = rGroupItem.GetGroupValue().mnGroupType;
+ sal_Int32 nGroupValue = rGroupItem.GetGroupValue().mnValue;
+ sal_Int32 nChildPart = rChildItem.GetGroupValue().mnGroupType;
+ sal_Int32 nChildValue = rChildItem.GetGroupValue().mnValue;
+
+ if (nGroupValue == ScDPItemData::DateFirst || nGroupValue == ScDPItemData::DateLast ||
+ nChildValue == ScDPItemData::DateFirst || nChildValue == ScDPItemData::DateLast)
+ {
+ // first/last entry matches only itself
+ return nGroupValue == nChildValue;
+ }
+
+ switch (nChildPart) // inner part
+ {
+ case css::sheet::DataPilotFieldGroupBy::MONTHS:
+ // a month is only contained in its quarter
+ if (nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
+ // months and quarters are both 1-based
+ return (nGroupValue - 1 == (nChildValue - 1) / 3);
+ break;
+ case css::sheet::DataPilotFieldGroupBy::DAYS:
+ // a day is only contained in its quarter or month
+ if (nGroupPart == css::sheet::DataPilotFieldGroupBy::MONTHS ||
+ nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
+ {
+ Date aDate(1, 1, SC_DP_LEAPYEAR);
+ aDate.AddDays(nChildValue - 1); // days are 1-based
+ sal_Int32 nCompare = aDate.GetMonth();
+ if (nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
+ nCompare = ( ( nCompare - 1 ) / 3 ) + 1; // get quarter from date
+
+ return nGroupValue == nCompare;
+ }
+ break;
+ default:
+ ;
+ }
+
+ return true;
+}
+
+}
+
+ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) :
+ aGroupName( rName )
+{
+}
+
+void ScDPGroupItem::AddElement( const ScDPItemData& rName )
+{
+ aElements.push_back( rName );
+}
+
+bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
+{
+ return std::any_of(aElements.begin(), aElements.end(),
+ [&rData](const ScDPItemData& rElement) { return rElement.IsCaseInsEqual(rData); });
+}
+
+bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const
+{
+ return std::any_of(aElements.begin(), aElements.end(),
+ [&rOther](const ScDPItemData& rElement) { return rOther.HasElement(rElement); });
+}
+
+void ScDPGroupItem::FillGroupFilter( ScDPFilteredCache::GroupFilter& rFilter ) const
+{
+ for (const auto& rElement : aElements)
+ rFilter.addMatchItem(rElement);
+}
+
+ScDPGroupDimension::ScDPGroupDimension( tools::Long nSource, OUString aNewName ) :
+ nSourceDim( nSource ),
+ nGroupDim( -1 ),
+ aGroupName(std::move( aNewName )),
+ mbDateDimension(false)
+{
+}
+
+ScDPGroupDimension::~ScDPGroupDimension()
+{
+ maMemberEntries.clear();
+}
+
+ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension& rOther ) :
+ nSourceDim( rOther.nSourceDim ),
+ nGroupDim( rOther.nGroupDim ),
+ aGroupName( rOther.aGroupName ),
+ aItems( rOther.aItems ),
+ mbDateDimension(rOther.mbDateDimension)
+{
+}
+
+ScDPGroupDimension& ScDPGroupDimension::operator=( const ScDPGroupDimension& rOther )
+{
+ nSourceDim = rOther.nSourceDim;
+ nGroupDim = rOther.nGroupDim;
+ aGroupName = rOther.aGroupName;
+ aItems = rOther.aItems;
+ mbDateDimension = rOther.mbDateDimension;
+ return *this;
+}
+
+void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem )
+{
+ aItems.push_back( rItem );
+}
+
+void ScDPGroupDimension::SetGroupDim( tools::Long nDim )
+{
+ nGroupDim = nDim;
+}
+
+const std::vector<SCROW>& ScDPGroupDimension::GetColumnEntries(
+ const ScDPFilteredCache& rCacheTable) const
+{
+ if (!maMemberEntries.empty())
+ return maMemberEntries;
+
+ rCacheTable.getCache().GetGroupDimMemberIds(nGroupDim, maMemberEntries);
+ return maMemberEntries;
+}
+
+const ScDPGroupItem* ScDPGroupDimension::GetGroupForData( const ScDPItemData& rData ) const
+{
+ auto aIter = std::find_if(aItems.begin(), aItems.end(),
+ [&rData](const ScDPGroupItem& rItem) { return rItem.HasElement(rData); });
+ if (aIter != aItems.end())
+ return &*aIter;
+
+ return nullptr;
+}
+
+const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const
+{
+ auto aIter = std::find_if(aItems.begin(), aItems.end(),
+ [&rName](const ScDPGroupItem& rItem) { return rItem.GetName().IsCaseInsEqual(rName); });
+ if (aIter != aItems.end())
+ return &*aIter;
+
+ return nullptr;
+}
+
+const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const
+{
+ if (nIndex >= aItems.size())
+ return nullptr;
+
+ return &aItems[nIndex];
+}
+
+void ScDPGroupDimension::DisposeData()
+{
+ maMemberEntries.clear();
+}
+
+void ScDPGroupDimension::SetDateDimension()
+{
+ mbDateDimension = true;
+}
+
+ScDPNumGroupDimension::ScDPNumGroupDimension() : mbDateDimension(false) {}
+
+ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) :
+ aGroupInfo(rInfo), mbDateDimension(false) {}
+
+ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension& rOther ) :
+ aGroupInfo(rOther.aGroupInfo), mbDateDimension(rOther.mbDateDimension) {}
+
+ScDPNumGroupDimension& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension& rOther )
+{
+ aGroupInfo = rOther.aGroupInfo;
+ mbDateDimension = rOther.mbDateDimension;
+ return *this;
+}
+
+void ScDPNumGroupDimension::DisposeData()
+{
+ aGroupInfo = ScDPNumGroupInfo();
+ maMemberEntries.clear();
+}
+
+ScDPNumGroupDimension::~ScDPNumGroupDimension()
+{
+}
+
+void ScDPNumGroupDimension::SetDateDimension()
+{
+ aGroupInfo.mbEnable = true; //TODO: or query both?
+ mbDateDimension = true;
+}
+
+const std::vector<SCROW>& ScDPNumGroupDimension::GetNumEntries(
+ SCCOL nSourceDim, const ScDPCache* pCache) const
+{
+ if (!maMemberEntries.empty())
+ return maMemberEntries;
+
+ pCache->GetGroupDimMemberIds(nSourceDim, maMemberEntries);
+ return maMemberEntries;
+}
+
+ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
+ ScDPTableData(pDocument),
+ pSourceData( pSource ),
+ pDoc( pDocument )
+{
+ OSL_ENSURE( pSource, "ScDPGroupTableData: pSource can't be NULL" );
+
+ CreateCacheTable();
+ nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout
+ pNumGroups.reset( new ScDPNumGroupDimension[nSourceCount] );
+}
+
+ScDPGroupTableData::~ScDPGroupTableData()
+{
+}
+
+void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension& rGroup )
+{
+ ScDPGroupDimension aNewGroup( rGroup );
+ aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
+ aGroups.push_back( aNewGroup );
+}
+
+void ScDPGroupTableData::SetNumGroupDimension( sal_Int32 nIndex, const ScDPNumGroupDimension& rGroup )
+{
+ if ( nIndex < nSourceCount )
+ {
+ pNumGroups[nIndex] = rGroup;
+
+ // automatic minimum / maximum is handled in GetNumEntries
+ }
+}
+
+sal_Int32 ScDPGroupTableData::GetDimensionIndex( std::u16string_view rName )
+{
+ for (tools::Long i = 0; i < nSourceCount; ++i) // nSourceCount excludes data layout
+ if (pSourceData->getDimensionName(i) == rName) //TODO: ignore case?
+ return i;
+ return -1; // none
+}
+
+sal_Int32 ScDPGroupTableData::GetColumnCount()
+{
+ return nSourceCount + aGroups.size();
+}
+
+bool ScDPGroupTableData::IsNumGroupDimension( tools::Long nDimension ) const
+{
+ return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().mbEnable );
+}
+
+void ScDPGroupTableData::GetNumGroupInfo(tools::Long nDimension, ScDPNumGroupInfo& rInfo)
+{
+ if ( nDimension < nSourceCount )
+ rInfo = pNumGroups[nDimension].GetInfo();
+}
+sal_Int32 ScDPGroupTableData::GetMembersCount( sal_Int32 nDim )
+{
+ const std::vector< SCROW >& members = GetColumnEntries( nDim );
+ return members.size();
+}
+const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( sal_Int32 nColumn )
+{
+ if ( nColumn >= nSourceCount )
+ {
+ if ( getIsDataLayoutDimension( nColumn) ) // data layout dimension?
+ nColumn = nSourceCount; // index of data layout in source data
+ else
+ {
+ const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
+ return rGroupDim.GetColumnEntries( GetCacheTable() );
+ }
+ }
+
+ if ( IsNumGroupDimension( nColumn ) )
+ {
+ // dimension number is unchanged for numerical groups
+ return pNumGroups[nColumn].GetNumEntries(
+ static_cast<SCCOL>(nColumn), &GetCacheTable().getCache());
+ }
+
+ return pSourceData->GetColumnEntries( nColumn );
+}
+
+const ScDPItemData* ScDPGroupTableData::GetMemberById( sal_Int32 nDim, sal_Int32 nId )
+{
+ return pSourceData->GetMemberById( nDim, nId );
+}
+
+OUString ScDPGroupTableData::getDimensionName(sal_Int32 nColumn)
+{
+ if ( nColumn >= nSourceCount )
+ {
+ if ( nColumn == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
+ nColumn = nSourceCount; // index of data layout in source data
+ else
+ return aGroups[nColumn - nSourceCount].GetName();
+ }
+
+ return pSourceData->getDimensionName( nColumn );
+}
+
+bool ScDPGroupTableData::getIsDataLayoutDimension(sal_Int32 nColumn)
+{
+ // position of data layout dimension is moved from source data
+ return ( nColumn == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ); // data layout dimension?
+}
+
+bool ScDPGroupTableData::IsDateDimension(sal_Int32 nDim)
+{
+ if ( nDim >= nSourceCount )
+ {
+ if ( nDim == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
+ nDim = nSourceCount; // index of data layout in source data
+ else
+ nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
+ }
+
+ return pSourceData->IsDateDimension( nDim );
+}
+
+sal_uInt32 ScDPGroupTableData::GetNumberFormat(sal_Int32 nDim)
+{
+ if ( nDim >= nSourceCount )
+ {
+ if ( nDim == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
+ nDim = nSourceCount; // index of data layout in source data
+ else
+ nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
+ }
+
+ return pSourceData->GetNumberFormat( nDim );
+}
+
+void ScDPGroupTableData::DisposeData()
+{
+ for ( auto& rGroup : aGroups )
+ rGroup.DisposeData();
+
+ for ( tools::Long i=0; i<nSourceCount; i++ )
+ pNumGroups[i].DisposeData();
+
+ pSourceData->DisposeData();
+}
+
+void ScDPGroupTableData::SetEmptyFlags( bool bIgnoreEmptyRows, bool bRepeatIfEmpty )
+{
+ pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+bool ScDPGroupTableData::IsRepeatIfEmpty()
+{
+ return pSourceData->IsRepeatIfEmpty();
+}
+
+void ScDPGroupTableData::CreateCacheTable()
+{
+ pSourceData->CreateCacheTable();
+}
+
+namespace {
+
+class FindCaseInsensitive
+{
+ ScDPItemData maValue;
+public:
+ explicit FindCaseInsensitive(const ScDPItemData& rVal) : maValue(rVal) {}
+
+ bool operator() (const ScDPItemData& rItem) const
+ {
+ return maValue.IsCaseInsEqual(rItem);
+ }
+};
+
+}
+
+void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPFilteredCache::Criterion>& rCriteria)
+{
+ // Build dimension ID to object map for group dimensions.
+ typedef std::unordered_map<tools::Long, const ScDPGroupDimension*> GroupFieldMapType;
+ GroupFieldMapType aGroupFieldIds;
+
+ for (const auto& rGroup : aGroups)
+ {
+ aGroupFieldIds.emplace(rGroup.GetGroupDim(), &rGroup);
+ }
+
+ vector<ScDPFilteredCache::Criterion> aNewCriteria;
+ aNewCriteria.reserve(rCriteria.size() + aGroups.size());
+
+ // Go through all the filtered field names and process them appropriately.
+
+ const ScDPCache& rCache = GetCacheTable().getCache();
+ GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
+ for (const auto& rCriterion : rCriteria)
+ {
+ std::vector<ScDPItemData> aMatchValues = rCriterion.mpFilter->getMatchValues();
+
+ GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(rCriterion.mnFieldIndex);
+ if (itrGrp == itrGrpEnd)
+ {
+ if (IsNumGroupDimension(rCriterion.mnFieldIndex))
+ {
+ // internal number group field
+ const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(rCriterion.mnFieldIndex);
+ if (!pNumInfo)
+ // Number group dimension without num info? Something is wrong...
+ continue;
+
+ ScDPFilteredCache::Criterion aCri;
+ aCri.mnFieldIndex = rCriterion.mnFieldIndex;
+ const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[rCriterion.mnFieldIndex];
+
+ if (rNumGrpDim.IsDateDimension())
+ {
+ // grouped by dates.
+ aCri.mpFilter =
+ std::make_shared<ScDPGroupDateFilter>(
+ std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
+ }
+ else
+ {
+ // This dimension is grouped by numeric ranges.
+ aCri.mpFilter =
+ std::make_shared<ScDPGroupNumFilter>(std::move(aMatchValues), *pNumInfo);
+ }
+
+ aNewCriteria.push_back(aCri);
+ }
+ else
+ {
+ // This is a regular source field.
+ aNewCriteria.push_back(rCriterion);
+ }
+ }
+ else
+ {
+ // This is an ordinary group field or external number group field.
+
+ const ScDPGroupDimension* pGrpDim = itrGrp->second;
+ tools::Long nSrcDim = pGrpDim->GetSourceDim();
+ tools::Long nGrpDim = pGrpDim->GetGroupDim();
+ const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nGrpDim);
+
+ if (pGrpDim->IsDateDimension() && pNumInfo)
+ {
+ // external number group
+ ScDPFilteredCache::Criterion aCri;
+ aCri.mnFieldIndex = nSrcDim; // use the source dimension, not the group dimension.
+ aCri.mpFilter =
+ std::make_shared<ScDPGroupDateFilter>(
+ std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
+
+ aNewCriteria.push_back(aCri);
+ }
+ else
+ {
+ // normal group
+
+ ScDPFilteredCache::Criterion aCri;
+ aCri.mnFieldIndex = nSrcDim;
+ aCri.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
+ ScDPFilteredCache::GroupFilter* pGrpFilter =
+ static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
+
+ size_t nGroupItemCount = pGrpDim->GetItemCount();
+ for (size_t i = 0; i < nGroupItemCount; ++i)
+ {
+ const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
+ if (!pGrpItem)
+ continue;
+
+ // Make sure this group name equals one of the match values.
+ if (std::none_of(aMatchValues.begin(), aMatchValues.end(), FindCaseInsensitive(pGrpItem->GetName())))
+ continue;
+
+ pGrpItem->FillGroupFilter(*pGrpFilter);
+ }
+
+ aNewCriteria.push_back(aCri);
+ }
+ }
+ }
+ rCriteria.swap(aNewCriteria);
+}
+
+void ScDPGroupTableData::FilterCacheTable(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims)
+{
+ ModifyFilterCriteria(rCriteria);
+ pSourceData->FilterCacheTable(std::move(rCriteria), std::move(rCatDims));
+}
+
+void ScDPGroupTableData::GetDrillDownData(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&&rCatDims, Sequence< Sequence<Any> >& rData)
+{
+ ModifyFilterCriteria(rCriteria);
+ pSourceData->GetDrillDownData(std::move(rCriteria), std::move(rCatDims), rData);
+}
+
+void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
+{
+ // #i111435# Inside FillRowDataFromCacheTable/GetItemData, virtual methods
+ // getIsDataLayoutDimension and GetSourceDim are used, so it has to be called
+ // with original rInfo, containing dimension indexes of the grouped data.
+
+ const ScDPFilteredCache& rCacheTable = pSourceData->GetCacheTable();
+ sal_Int32 nRowSize = rCacheTable.getRowSize();
+ for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
+ {
+ sal_Int32 nLastRow;
+ if (!rCacheTable.isRowActive(nRow, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ CalcRowData aData;
+ FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
+
+ if ( !rInfo.aColLevelDims.empty() )
+ FillGroupValues(aData.aColData, rInfo.aColLevelDims);
+ if ( !rInfo.aRowLevelDims.empty() )
+ FillGroupValues(aData.aRowData, rInfo.aRowLevelDims);
+ if ( !rInfo.aPageDims.empty() )
+ FillGroupValues(aData.aPageData, rInfo.aPageDims);
+
+ ProcessRowData(rInfo, aData, bAutoShow);
+ }
+}
+
+const ScDPFilteredCache& ScDPGroupTableData::GetCacheTable() const
+{
+ return pSourceData->GetCacheTable();
+}
+
+void ScDPGroupTableData::ReloadCacheTable()
+{
+ pSourceData->ReloadCacheTable();
+}
+
+void ScDPGroupTableData::FillGroupValues(vector<SCROW>& rItems, const vector<sal_Int32>& rDims)
+{
+ sal_Int32 nGroupedColumns = aGroups.size();
+
+ const ScDPCache& rCache = GetCacheTable().getCache();
+ size_t i = 0;
+ for (tools::Long nColumn : rDims)
+ {
+ bool bDateDim = false;
+
+ sal_Int32 nSourceDim = nColumn;
+ if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns )
+ {
+ const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
+ nSourceDim= rGroupDim.GetSourceDim();
+ bDateDim = rGroupDim.IsDateDimension();
+ if (!bDateDim) // date is handled below
+ {
+ const ScDPItemData& rItem = *GetMemberById(nSourceDim, rItems[i]);
+ const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData(rItem);
+ if (pGroupItem)
+ {
+ rItems[i] =
+ rCache.GetIdByItemData(nColumn, pGroupItem->GetName());
+ }
+ else
+ rItems[i] = rCache.GetIdByItemData(nColumn, rItem);
+ }
+ }
+ else if ( IsNumGroupDimension( nColumn ) )
+ {
+ bDateDim = pNumGroups[nColumn].IsDateDimension();
+ if (!bDateDim) // date is handled below
+ {
+ const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
+ if (pData->GetType() == ScDPItemData::Value)
+ {
+ ScDPNumGroupInfo aNumInfo;
+ GetNumGroupInfo(nColumn, aNumInfo);
+ double fGroupValue = ScDPUtil::getNumGroupStartValue(pData->GetValue(), aNumInfo);
+ ScDPItemData aItemData;
+ aItemData.SetRangeStart(fGroupValue);
+ rItems[i] = rCache.GetIdByItemData(nSourceDim, aItemData);
+ }
+ // else (textual) keep original value
+ }
+ }
+
+ const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nColumn);
+
+ if (bDateDim && pNumInfo)
+ {
+ // This is a date group dimension.
+ sal_Int32 nDatePart = rCache.GetGroupType(nColumn);
+ const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
+ if (pData->GetType() == ScDPItemData::Value)
+ {
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ sal_Int32 nPartValue = ScDPUtil::getDatePartValue(
+ pData->GetValue(), pNumInfo, nDatePart, pFormatter);
+
+ ScDPItemData aItem(nDatePart, nPartValue);
+ rItems[i] = rCache.GetIdByItemData(nColumn, aItem);
+ }
+ }
+
+ ++i;
+ }
+}
+
+bool ScDPGroupTableData::IsBaseForGroup(sal_Int32 nDim) const
+{
+ return std::any_of(aGroups.begin(), aGroups.end(),
+ [&nDim](const ScDPGroupDimension& rDim) { return rDim.GetSourceDim() == nDim; });
+}
+
+sal_Int32 ScDPGroupTableData::GetGroupBase(sal_Int32 nGroupDim) const
+{
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&nGroupDim](const ScDPGroupDimension& rDim) { return rDim.GetGroupDim() == nGroupDim; });
+ if (aIter != aGroups.end())
+ return aIter->GetSourceDim();
+
+ return -1; // none
+}
+
+bool ScDPGroupTableData::IsNumOrDateGroup(sal_Int32 nDimension) const
+{
+ // Virtual method from ScDPTableData, used in result data to force text labels.
+
+ if ( nDimension < nSourceCount )
+ {
+ return pNumGroups[nDimension].GetInfo().mbEnable ||
+ pNumGroups[nDimension].IsDateDimension();
+ }
+
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&nDimension](const ScDPGroupDimension& rDim) { return rDim.GetGroupDim() == nDimension; });
+ if (aIter != aGroups.end())
+ return aIter->IsDateDimension();
+
+ return false;
+}
+
+bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, sal_Int32 nGroupIndex,
+ const ScDPItemData& rBaseData, sal_Int32 nBaseIndex ) const
+{
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&nGroupIndex, &nBaseIndex](const ScDPGroupDimension& rDim) {
+ return rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex; });
+ if (aIter != aGroups.end())
+ {
+ const ScDPGroupDimension& rDim = *aIter;
+ if (rDim.IsDateDimension())
+ {
+ return isDateInGroup(rGroupData, rBaseData);
+ }
+ else
+ {
+ // If the item is in a group, only that group is valid.
+ // If the item is not in any group, its own name is valid.
+
+ const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData );
+ return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) :
+ rGroupData.IsCaseInsEqual( rBaseData );
+ }
+ }
+
+ OSL_FAIL("IsInGroup: no group dimension found");
+ return true;
+}
+
+bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, sal_Int32 nFirstIndex,
+ const ScDPItemData& rSecondData, sal_Int32 nSecondIndex ) const
+{
+ const ScDPGroupDimension* pFirstDim = nullptr;
+ const ScDPGroupDimension* pSecondDim = nullptr;
+ for ( const auto& rDim : aGroups )
+ {
+ const ScDPGroupDimension* pDim = &rDim;
+ if ( pDim->GetGroupDim() == nFirstIndex )
+ pFirstDim = pDim;
+ else if ( pDim->GetGroupDim() == nSecondIndex )
+ pSecondDim = pDim;
+ }
+ if ( pFirstDim && pSecondDim )
+ {
+ bool bFirstDate = pFirstDim->IsDateDimension();
+ bool bSecondDate = pSecondDim->IsDateDimension();
+ if (bFirstDate || bSecondDate)
+ {
+ // If one is a date group dimension, the other one must be, too.
+ if (!bFirstDate || !bSecondDate)
+ {
+ OSL_FAIL( "mix of date and non-date groups" );
+ return true;
+ }
+
+ return isDateInGroup(rFirstData, rSecondData);
+ }
+
+ const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData );
+ const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData );
+ if ( pFirstItem && pSecondItem )
+ {
+ // two existing groups -> sal_True if they have a common element
+ return pFirstItem->HasCommonElement( *pSecondItem );
+ }
+ else if ( pFirstItem )
+ {
+ // "automatic" group contains only its own name
+ return pFirstItem->HasElement( rSecondData );
+ }
+ else if ( pSecondItem )
+ {
+ // "automatic" group contains only its own name
+ return pSecondItem->HasElement( rFirstData );
+ }
+ else
+ {
+ // no groups -> sal_True if equal
+ return rFirstData.IsCaseInsEqual( rSecondData );
+ }
+ }
+
+ OSL_FAIL("HasCommonElement: no group dimension found");
+ return true;
+}
+
+sal_Int32 ScDPGroupTableData::GetSourceDim( sal_Int32 nDim )
+{
+ if ( getIsDataLayoutDimension( nDim ) )
+ return nSourceCount;
+ if ( nDim >= nSourceCount && nDim < nSourceCount +static_cast<tools::Long>(aGroups.size()) )
+ {
+ const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
+ return rGroupDim.GetSourceDim();
+ }
+ return nDim;
+}
+
+sal_Int32 ScDPGroupTableData::Compare(sal_Int32 nDim, sal_Int32 nDataId1, sal_Int32 nDataId2)
+{
+ if ( getIsDataLayoutDimension(nDim) )
+ return 0;
+ const ScDPItemData* rItem1 = GetMemberById(nDim, nDataId1);
+ const ScDPItemData* rItem2 = GetMemberById(nDim, nDataId2);
+ if (rItem1 == nullptr || rItem2 == nullptr)
+ return 0;
+ return ScDPItemData::Compare( *rItem1,*rItem2);
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPGroupTableData::Dump() const
+{
+ cout << "--- ScDPGroupTableData" << endl;
+ for (tools::Long i = 0; i < nSourceCount; ++i)
+ {
+ cout << "* dimension: " << i << endl;
+ const ScDPNumGroupDimension& rGrp = pNumGroups[i];
+ rGrp.GetInfo().Dump();
+ }
+ cout << "---" << endl;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpitemdata.cxx b/sc/source/core/data/dpitemdata.cxx
new file mode 100644
index 0000000000..e4efe75ee9
--- /dev/null
+++ b/sc/source/core/data/dpitemdata.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 <dpitemdata.hxx>
+#include <global.hxx>
+
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <rtl/math.hxx>
+
+const sal_Int32 ScDPItemData::DateFirst = -1;
+const sal_Int32 ScDPItemData::DateLast = 10000;
+
+sal_Int32 ScDPItemData::Compare(const ScDPItemData& rA, const ScDPItemData& rB)
+{
+ if (rA.meType != rB.meType)
+ {
+ // group value, value and string in this order. Ensure that the empty
+ // type comes last.
+ return rA.meType < rB.meType ? -1 : 1;
+ }
+
+ switch (rA.meType)
+ {
+ case GroupValue:
+ {
+ if (rA.maGroupValue.mnGroupType == rB.maGroupValue.mnGroupType)
+ {
+ if (rA.maGroupValue.mnValue == rB.maGroupValue.mnValue)
+ return 0;
+
+ return rA.maGroupValue.mnValue < rB.maGroupValue.mnValue ? -1 : 1;
+ }
+
+ return rA.maGroupValue.mnGroupType < rB.maGroupValue.mnGroupType ? -1 : 1;
+ }
+ case Value:
+ case RangeStart:
+ {
+ if (rA.mfValue == rB.mfValue)
+ return 0;
+
+ return rA.mfValue < rB.mfValue ? -1 : 1;
+ }
+ case String:
+ case Error:
+ if (rA.mpString == rB.mpString)
+ // strings may be interned.
+ return 0;
+
+ return ScGlobal::GetCollator().compareString(rA.GetString(), rB.GetString());
+ default:
+ ;
+ }
+ return 0;
+}
+
+ScDPItemData::ScDPItemData() :
+ mfValue(0.0), meType(Empty), mbStringInterned(false) {}
+
+ScDPItemData::ScDPItemData(const ScDPItemData& r) :
+ meType(r.meType), mbStringInterned(r.mbStringInterned)
+{
+ switch (r.meType)
+ {
+ case String:
+ case Error:
+ mpString = r.mpString;
+ if (!mbStringInterned)
+ rtl_uString_acquire(mpString);
+ break;
+ case Value:
+ case RangeStart:
+ mfValue = r.mfValue;
+ break;
+ case GroupValue:
+ maGroupValue.mnGroupType = r.maGroupValue.mnGroupType;
+ maGroupValue.mnValue = r.maGroupValue.mnValue;
+ break;
+ case Empty:
+ default:
+ mfValue = 0.0;
+ }
+}
+
+void ScDPItemData::DisposeString()
+{
+ if (!mbStringInterned)
+ {
+ if (meType == String || meType == Error)
+ rtl_uString_release(mpString);
+ }
+
+ mbStringInterned = false;
+}
+
+ScDPItemData::ScDPItemData(const OUString& rStr) :
+ mpString(rStr.pData), meType(String), mbStringInterned(false)
+{
+ rtl_uString_acquire(mpString);
+}
+
+ScDPItemData::ScDPItemData(sal_Int32 nGroupType, sal_Int32 nValue) :
+ meType(GroupValue), mbStringInterned(false)
+{
+ maGroupValue.mnGroupType = nGroupType;
+ maGroupValue.mnValue = nValue;
+}
+
+ScDPItemData::~ScDPItemData()
+{
+ DisposeString();
+}
+
+void ScDPItemData::SetEmpty()
+{
+ DisposeString();
+ meType = Empty;
+}
+
+void ScDPItemData::SetString(const OUString& rS)
+{
+ DisposeString();
+ mpString = rS.pData;
+ rtl_uString_acquire(mpString);
+ meType = String;
+}
+
+void ScDPItemData::SetStringInterned( rtl_uString* pS )
+{
+ DisposeString();
+ mpString = pS;
+ meType = String;
+ mbStringInterned = true;
+}
+
+void ScDPItemData::SetValue(double fVal)
+{
+ DisposeString();
+ mfValue = fVal;
+ meType = Value;
+}
+
+void ScDPItemData::SetRangeStart(double fVal)
+{
+ DisposeString();
+ mfValue = fVal;
+ meType = RangeStart;
+}
+
+void ScDPItemData::SetRangeFirst()
+{
+ DisposeString();
+ mfValue = -std::numeric_limits<double>::infinity();
+ meType = RangeStart;
+}
+
+void ScDPItemData::SetRangeLast()
+{
+ DisposeString();
+ mfValue = std::numeric_limits<double>::infinity();
+ meType = RangeStart;
+}
+
+void ScDPItemData::SetErrorStringInterned( rtl_uString* pS )
+{
+ SetStringInterned(pS);
+ meType = Error;
+}
+
+bool ScDPItemData::IsCaseInsEqual(const ScDPItemData& r) const
+{
+ if (meType != r.meType)
+ return false;
+
+ switch (meType)
+ {
+ case Value:
+ case RangeStart:
+ return rtl::math::approxEqual(mfValue, r.mfValue);
+ case GroupValue:
+ return maGroupValue.mnGroupType == r.maGroupValue.mnGroupType &&
+ maGroupValue.mnValue == r.maGroupValue.mnValue;
+ default:
+ ;
+ }
+
+ if (mpString == r.mpString)
+ // Fast equality check for interned strings.
+ return true;
+
+ return ScGlobal::GetTransliteration().isEqual(GetString(), r.GetString());
+}
+
+bool ScDPItemData::operator== (const ScDPItemData& r) const
+{
+ if (meType != r.meType)
+ return false;
+
+ switch (meType)
+ {
+ case Value:
+ case RangeStart:
+ return rtl::math::approxEqual(mfValue, r.mfValue);
+ case GroupValue:
+ return maGroupValue.mnGroupType == r.maGroupValue.mnGroupType &&
+ maGroupValue.mnValue == r.maGroupValue.mnValue;
+ default:
+ ;
+ }
+
+ // need exact equality until we have a safe case insensitive string hash
+ return GetString() == r.GetString();
+}
+
+bool ScDPItemData::operator< (const ScDPItemData& r) const
+{
+ return Compare(*this, r) == -1;
+}
+
+ScDPItemData& ScDPItemData::operator= (const ScDPItemData& r)
+{
+ DisposeString();
+ meType = r.meType;
+ switch (r.meType)
+ {
+ case String:
+ case Error:
+ mbStringInterned = r.mbStringInterned;
+ mpString = r.mpString;
+ if (!mbStringInterned)
+ rtl_uString_acquire(mpString);
+ break;
+ case Value:
+ case RangeStart:
+ mfValue = r.mfValue;
+ break;
+ case GroupValue:
+ maGroupValue.mnGroupType = r.maGroupValue.mnGroupType;
+ maGroupValue.mnValue = r.maGroupValue.mnValue;
+ break;
+ case Empty:
+ default:
+ mfValue = 0.0;
+ }
+ return *this;
+}
+
+ScDPValue::Type ScDPItemData::GetCellType() const
+{
+ switch (meType)
+ {
+ case Error:
+ return ScDPValue::Error;
+ case Empty:
+ return ScDPValue::Empty;
+ case Value:
+ return ScDPValue::Value;
+ default:
+ ;
+ }
+
+ return ScDPValue::String;
+}
+
+#if DEBUG_PIVOT_TABLE
+
+void ScDPItemData::Dump(const char* msg) const
+{
+ printf("--- (%s)\n", msg);
+ switch (meType)
+ {
+ case Empty:
+ printf("empty\n");
+ break;
+ case Error:
+ printf("error: %s\n",
+ OUStringToOString(OUString(mpString), RTL_TEXTENCODING_UTF8).getStr());
+ break;
+ case GroupValue:
+ printf("group value: group type = %d value = %d\n",
+ maGroupValue.mnGroupType, maGroupValue.mnValue);
+ break;
+ case String:
+ printf("string: %s\n",
+ OUStringToOString(OUString(mpString), RTL_TEXTENCODING_UTF8).getStr());
+ break;
+ case Value:
+ printf("value: %g\n", mfValue);
+ break;
+ case RangeStart:
+ printf("range start: %g\n", mfValue);
+ break;
+ default:
+ printf("unknown type\n");
+ }
+ printf("---\n");
+}
+#endif
+
+bool ScDPItemData::IsEmpty() const
+{
+ return meType == Empty;
+}
+
+bool ScDPItemData::IsValue() const
+{
+ return meType == Value;
+}
+
+OUString ScDPItemData::GetString() const
+{
+ switch (meType)
+ {
+ case String:
+ case Error:
+ return OUString(mpString);
+ case Value:
+ case RangeStart:
+ return OUString::number(mfValue);
+ case GroupValue:
+ return OUString::number(maGroupValue.mnValue);
+ case Empty:
+ default:
+ ;
+ }
+
+ return OUString();
+}
+
+double ScDPItemData::GetValue() const
+{
+ if (meType == Value || meType == RangeStart)
+ return mfValue;
+
+ return 0.0;
+}
+
+ScDPItemData::GroupValueAttr ScDPItemData::GetGroupValue() const
+{
+ if (meType == GroupValue)
+ return maGroupValue;
+
+ GroupValueAttr aGV;
+ aGV.mnGroupType = -1;
+ aGV.mnValue = -1;
+ return aGV;
+}
+
+bool ScDPItemData::HasStringData() const
+{
+ return meType == String || meType == Error;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpnumgroupinfo.cxx b/sc/source/core/data/dpnumgroupinfo.cxx
new file mode 100644
index 0000000000..f57822e163
--- /dev/null
+++ b/sc/source/core/data/dpnumgroupinfo.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/.
+ */
+
+#include <dpnumgroupinfo.hxx>
+
+ScDPNumGroupInfo::ScDPNumGroupInfo() :
+ mbEnable(false),
+ mbDateValues(false),
+ mbAutoStart(false),
+ mbAutoEnd(false),
+ mbIntegerOnly(true),
+ mfStart(0.0), mfEnd(0.0), mfStep(0.0) {}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPNumGroupInfo::Dump() const
+{
+ cout << "--- ScDPNumGroupInfo" << endl;
+ cout << " enabled: " << mbEnable << endl;
+ cout << " auto start: " << mbAutoStart << endl;
+ cout << " auto end: " << mbAutoEnd << endl;
+ cout << " start: " << mfStart << endl;
+ cout << " end: " << mfEnd << endl;
+ cout << " step: " << mfStep << endl;
+ cout << "---" << endl;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx
new file mode 100644
index 0000000000..37a7bf73d3
--- /dev/null
+++ b/sc/source/core/data/dpobject.cxx
@@ -0,0 +1,3980 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <docsh.hxx>
+#include <dpcache.hxx>
+#include <dpobject.hxx>
+#include <dptabsrc.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpoutput.hxx>
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <dpgroup.hxx>
+#include <document.hxx>
+#include <pivot.hxx>
+#include <dapiuno.hxx>
+#include <miscuno.hxx>
+#include <refupdat.hxx>
+#include <attrib.hxx>
+#include <scitems.hxx>
+#include <unonames.hxx>
+#include <dpglobal.hxx>
+#include <globstr.hrc>
+#include <queryentry.hxx>
+#include <dputil.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/XCompletedExecution.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
+#include <com/sun/star/sheet/DimensionFlags.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
+
+#include <unotools/charclass.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/macros.h>
+#include <svl/numformat.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <svl/zforlist.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <vector>
+#include <memory>
+#include <algorithm>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::std::shared_ptr;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::lang::XComponent;
+using ::com::sun::star::sheet::DataPilotTableHeaderData;
+using ::com::sun::star::sheet::DataPilotTablePositionData;
+using ::com::sun::star::sheet::XDimensionsSupplier;
+using ::com::sun::star::beans::XPropertySet;
+
+constexpr OUStringLiteral SC_SERVICE_ROWSET = u"com.sun.star.sdb.RowSet";
+
+constexpr OUStringLiteral SC_DBPROP_DATASOURCENAME = u"DataSourceName";
+constexpr OUStringLiteral SC_DBPROP_COMMAND = u"Command";
+constexpr OUStringLiteral SC_DBPROP_COMMANDTYPE = u"CommandType";
+
+constexpr OUString SCDPSOURCE_SERVICE = u"com.sun.star.sheet.DataPilotSource"_ustr;
+
+namespace {
+
+/**
+ * Database connection implementation for UNO database API. Note that in
+ * the UNO database API, column index is 1-based, whereas the interface
+ * requires that column index be 0-based.
+ */
+class DBConnector : public ScDPCache::DBConnector
+{
+ ScDPCache& mrCache;
+
+ uno::Reference<sdbc::XRowSet> mxRowSet;
+ uno::Reference<sdbc::XRow> mxRow;
+ uno::Reference<sdbc::XResultSetMetaData> mxMetaData;
+ Date maNullDate;
+
+public:
+ DBConnector(ScDPCache& rCache, uno::Reference<sdbc::XRowSet> xRowSet, const Date& rNullDate);
+
+ bool isValid() const;
+
+ virtual void getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType& rNumType) const override;
+ virtual OUString getColumnLabel(tools::Long nCol) const override;
+ virtual tools::Long getColumnCount() const override;
+ virtual bool first() override;
+ virtual bool next() override;
+ virtual void finish() override;
+};
+
+DBConnector::DBConnector(ScDPCache& rCache, uno::Reference<sdbc::XRowSet> xRowSet, const Date& rNullDate) :
+ mrCache(rCache), mxRowSet(std::move(xRowSet)), maNullDate(rNullDate)
+{
+ Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp(mxRowSet, UNO_QUERY);
+ if (xMetaSupp.is())
+ mxMetaData = xMetaSupp->getMetaData();
+
+ mxRow.set(mxRowSet, UNO_QUERY);
+}
+
+bool DBConnector::isValid() const
+{
+ return mxRowSet.is() && mxRow.is() && mxMetaData.is();
+}
+
+bool DBConnector::first()
+{
+ return mxRowSet->first();
+}
+
+bool DBConnector::next()
+{
+ return mxRowSet->next();
+}
+
+void DBConnector::finish()
+{
+ mxRowSet->beforeFirst();
+}
+
+tools::Long DBConnector::getColumnCount() const
+{
+ return mxMetaData->getColumnCount();
+}
+
+OUString DBConnector::getColumnLabel(tools::Long nCol) const
+{
+ return mxMetaData->getColumnLabel(nCol+1);
+}
+
+void DBConnector::getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType& rNumType) const
+{
+ rNumType = SvNumFormatType::NUMBER;
+ sal_Int32 nType = mxMetaData->getColumnType(nCol+1);
+
+ try
+ {
+ double fValue = 0.0;
+ switch (nType)
+ {
+ case sdbc::DataType::BIT:
+ case sdbc::DataType::BOOLEAN:
+ {
+ rNumType = SvNumFormatType::LOGICAL;
+ fValue = mxRow->getBoolean(nCol+1) ? 1 : 0;
+ rData.SetValue(fValue);
+ break;
+ }
+ case sdbc::DataType::TINYINT:
+ case sdbc::DataType::SMALLINT:
+ case sdbc::DataType::INTEGER:
+ case sdbc::DataType::BIGINT:
+ case sdbc::DataType::FLOAT:
+ case sdbc::DataType::REAL:
+ case sdbc::DataType::DOUBLE:
+ case sdbc::DataType::NUMERIC:
+ case sdbc::DataType::DECIMAL:
+ {
+ //TODO: do the conversion here?
+ fValue = mxRow->getDouble(nCol+1);
+ rData.SetValue(fValue);
+ break;
+ }
+ case sdbc::DataType::DATE:
+ {
+ rNumType = SvNumFormatType::DATE;
+
+ util::Date aDate = mxRow->getDate(nCol+1);
+ fValue = Date(aDate.Day, aDate.Month, aDate.Year) - maNullDate;
+ rData.SetValue(fValue);
+ break;
+ }
+ case sdbc::DataType::TIME:
+ {
+ rNumType = SvNumFormatType::TIME;
+
+ util::Time aTime = mxRow->getTime(nCol+1);
+ fValue = aTime.Hours / static_cast<double>(::tools::Time::hourPerDay) +
+ aTime.Minutes / static_cast<double>(::tools::Time::minutePerDay) +
+ aTime.Seconds / static_cast<double>(::tools::Time::secondPerDay) +
+ aTime.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
+ rData.SetValue(fValue);
+ break;
+ }
+ case sdbc::DataType::TIMESTAMP:
+ {
+ rNumType = SvNumFormatType::DATETIME;
+
+ util::DateTime aStamp = mxRow->getTimestamp(nCol+1);
+ fValue = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) - maNullDate ) +
+ aStamp.Hours / static_cast<double>(::tools::Time::hourPerDay) +
+ aStamp.Minutes / static_cast<double>(::tools::Time::minutePerDay) +
+ aStamp.Seconds / static_cast<double>(::tools::Time::secondPerDay) +
+ aStamp.NanoSeconds / static_cast<double>(::tools::Time::nanoSecPerDay);
+ rData.SetValue(fValue);
+ break;
+ }
+ case sdbc::DataType::CHAR:
+ case sdbc::DataType::VARCHAR:
+ case sdbc::DataType::LONGVARCHAR:
+ case sdbc::DataType::SQLNULL:
+ case sdbc::DataType::BINARY:
+ case sdbc::DataType::VARBINARY:
+ case sdbc::DataType::LONGVARBINARY:
+ default:
+ // nCol is 0-based, and the left-most column always has nCol == 0.
+ rData.SetStringInterned(
+ mrCache.InternString(nCol, mxRow->getString(nCol+1)));
+ }
+ }
+ catch (uno::Exception&)
+ {
+ rData.SetEmpty();
+ }
+}
+
+}
+
+static sheet::DataPilotFieldOrientation lcl_GetDataGetOrientation( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ sheet::DataPilotFieldOrientation nRet = sheet::DataPilotFieldOrientation_HIDDEN;
+ if ( xSource.is() )
+ {
+ uno::Reference<container::XNameAccess> xDimNameAccess = xSource->getDimensions();
+ const uno::Sequence<OUString> aDimNames = xDimNameAccess->getElementNames();
+ for (const OUString& rDimName : aDimNames)
+ {
+ uno::Reference<beans::XPropertySet> xDimProp(xDimNameAccess->getByName(rDimName),
+ uno::UNO_QUERY);
+ if ( xDimProp.is() )
+ {
+ const bool bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT );
+ //TODO: error checking -- is "IsDataLayoutDimension" property required??
+ if (bFound)
+ {
+ nRet = ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ break;
+ }
+ }
+ }
+ }
+ return nRet;
+}
+
+ScDPServiceDesc::ScDPServiceDesc(
+ OUString aServ, OUString aSrc, OUString aNam,
+ OUString aUser, OUString aPass ) :
+ aServiceName(std::move( aServ )),
+ aParSource(std::move( aSrc )),
+ aParName(std::move( aNam )),
+ aParUser(std::move( aUser )),
+ aParPass(std::move( aPass )) {}
+
+bool ScDPServiceDesc::operator== ( const ScDPServiceDesc& rOther ) const
+{
+ return aServiceName == rOther.aServiceName &&
+ aParSource == rOther.aParSource &&
+ aParName == rOther.aParName &&
+ aParUser == rOther.aParUser &&
+ aParPass == rOther.aParPass;
+}
+
+ScDPObject::ScDPObject( ScDocument* pD ) :
+ pDoc( pD ),
+ nHeaderRows( 0 ),
+ mbHeaderLayout(false),
+ bAllowMove(false),
+ bSettingsChanged(false),
+ mbEnableGetPivotData(true)
+{
+}
+
+ScDPObject::ScDPObject(const ScDPObject& r) :
+ pDoc( r.pDoc ),
+ aTableName( r.aTableName ),
+ aTableTag( r.aTableTag ),
+ aOutRange( r.aOutRange ),
+ maInteropGrabBag(r.maInteropGrabBag),
+ nHeaderRows( r.nHeaderRows ),
+ mbHeaderLayout( r.mbHeaderLayout ),
+ bAllowMove(false),
+ bSettingsChanged(false),
+ mbEnableGetPivotData(r.mbEnableGetPivotData)
+{
+ if (r.pSaveData)
+ pSaveData.reset( new ScDPSaveData(*r.pSaveData) );
+ if (r.pSheetDesc)
+ pSheetDesc.reset( new ScSheetSourceDesc(*r.pSheetDesc) );
+ if (r.pImpDesc)
+ pImpDesc.reset( new ScImportSourceDesc(*r.pImpDesc) );
+ if (r.pServDesc)
+ pServDesc.reset( new ScDPServiceDesc(*r.pServDesc) );
+ // xSource (and pOutput) is not copied
+}
+
+ScDPObject::~ScDPObject()
+{
+ Clear();
+}
+
+ScDPObject& ScDPObject::operator= (const ScDPObject& r)
+{
+ if (this != &r)
+ {
+ Clear();
+
+ pDoc = r.pDoc;
+ aTableName = r.aTableName;
+ aTableTag = r.aTableTag;
+ aOutRange = r.aOutRange;
+ maInteropGrabBag = r.maInteropGrabBag;
+ nHeaderRows = r.nHeaderRows;
+ mbHeaderLayout = r.mbHeaderLayout;
+ bAllowMove = false;
+ bSettingsChanged = false;
+ mbEnableGetPivotData = r.mbEnableGetPivotData;
+
+ if (r.pSaveData)
+ pSaveData.reset( new ScDPSaveData(*r.pSaveData) );
+ if (r.pSheetDesc)
+ pSheetDesc.reset( new ScSheetSourceDesc(*r.pSheetDesc) );
+ if (r.pImpDesc)
+ pImpDesc.reset( new ScImportSourceDesc(*r.pImpDesc) );
+ if (r.pServDesc)
+ pServDesc.reset( new ScDPServiceDesc(*r.pServDesc) );
+ }
+ return *this;
+}
+
+void ScDPObject::EnableGetPivotData(bool b)
+{
+ mbEnableGetPivotData = b;
+}
+
+void ScDPObject::SetAllowMove(bool bSet)
+{
+ bAllowMove = bSet;
+}
+
+void ScDPObject::SetSaveData(const ScDPSaveData& rData)
+{
+ if ( pSaveData.get() != &rData ) // API implementation modifies the original SaveData object
+ {
+ pSaveData.reset( new ScDPSaveData( rData ) );
+ }
+
+ InvalidateData(); // re-init source from SaveData
+}
+
+void ScDPObject::SetHeaderLayout (bool bUseGrid)
+{
+ mbHeaderLayout = bUseGrid;
+}
+
+void ScDPObject::SetOutRange(const ScRange& rRange)
+{
+ aOutRange = rRange;
+
+ if ( pOutput )
+ pOutput->SetPosition( rRange.aStart );
+}
+
+const ScRange& ScDPObject::GetOutRange() const
+{
+ return aOutRange;
+}
+
+void ScDPObject::SetSheetDesc(const ScSheetSourceDesc& rDesc)
+{
+ if ( pSheetDesc && rDesc == *pSheetDesc )
+ return; // nothing to do
+
+ pImpDesc.reset();
+ pServDesc.reset();
+
+ pSheetDesc.reset( new ScSheetSourceDesc(rDesc) );
+
+ // make valid QueryParam
+
+ const ScRange& rSrcRange = pSheetDesc->GetSourceRange();
+ ScQueryParam aParam = pSheetDesc->GetQueryParam();
+ aParam.nCol1 = rSrcRange.aStart.Col();
+ aParam.nRow1 = rSrcRange.aStart.Row();
+ aParam.nCol2 = rSrcRange.aEnd.Col();
+ aParam.nRow2 = rSrcRange.aEnd.Row();
+ aParam.bHasHeader = true;
+ pSheetDesc->SetQueryParam(aParam);
+
+ ClearTableData(); // new source must be created
+}
+
+void ScDPObject::SetImportDesc(const ScImportSourceDesc& rDesc)
+{
+ if ( pImpDesc && rDesc == *pImpDesc )
+ return; // nothing to do
+
+ pSheetDesc.reset();
+ pServDesc.reset();
+
+ pImpDesc.reset( new ScImportSourceDesc(rDesc) );
+
+ ClearTableData(); // new source must be created
+}
+
+void ScDPObject::SetServiceData(const ScDPServiceDesc& rDesc)
+{
+ if ( pServDesc && rDesc == *pServDesc )
+ return; // nothing to do
+
+ pSheetDesc.reset();
+ pImpDesc.reset();
+
+ pServDesc.reset( new ScDPServiceDesc(rDesc) );
+
+ ClearTableData(); // new source must be created
+}
+
+void ScDPObject::WriteSourceDataTo( ScDPObject& rDest ) const
+{
+ if ( pSheetDesc )
+ rDest.SetSheetDesc( *pSheetDesc );
+ else if ( pImpDesc )
+ rDest.SetImportDesc( *pImpDesc );
+ else if ( pServDesc )
+ rDest.SetServiceData( *pServDesc );
+
+ // name/tag are not source data, but needed along with source data
+
+ rDest.aTableName = aTableName;
+ rDest.aTableTag = aTableTag;
+}
+
+void ScDPObject::WriteTempDataTo( ScDPObject& rDest ) const
+{
+ rDest.nHeaderRows = nHeaderRows;
+}
+
+bool ScDPObject::IsSheetData() const
+{
+ return ( pSheetDesc != nullptr );
+}
+
+void ScDPObject::SetName(const OUString& rNew)
+{
+ aTableName = rNew;
+}
+
+void ScDPObject::SetTag(const OUString& rNew)
+{
+ aTableTag = rNew;
+}
+
+bool ScDPObject::IsDataDescriptionCell(const ScAddress& rPos)
+{
+ if (!pSaveData)
+ return false;
+
+ tools::Long nDataDimCount = pSaveData->GetDataDimensionCount();
+ if (nDataDimCount != 1)
+ // There has to be exactly one data dimension for the description to
+ // appear at top-left corner.
+ return false;
+
+ CreateOutput();
+ ScRange aTabRange = pOutput->GetOutputRange(sheet::DataPilotOutputRangeType::TABLE);
+ return (rPos == aTabRange.aStart);
+}
+
+uno::Reference<sheet::XDimensionsSupplier> const & ScDPObject::GetSource()
+{
+ CreateObjects();
+ return xSource;
+}
+
+void ScDPObject::CreateOutput()
+{
+ CreateObjects();
+ if (pOutput)
+ return;
+
+ bool bFilterButton = IsSheetData() && pSaveData && pSaveData->GetFilterButton();
+ pOutput.reset( new ScDPOutput( pDoc, xSource, aOutRange.aStart, bFilterButton, pSaveData ? pSaveData->GetExpandCollapse() : false ) );
+ pOutput->SetHeaderLayout ( mbHeaderLayout );
+
+ sal_Int32 nOldRows = nHeaderRows;
+ nHeaderRows = pOutput->GetHeaderRows();
+
+ if ( !(bAllowMove && nHeaderRows != nOldRows) )
+ return;
+
+ sal_Int32 nDiff = nOldRows - nHeaderRows;
+ if ( nOldRows == 0 )
+ --nDiff;
+ if ( nHeaderRows == 0 )
+ ++nDiff;
+
+ sal_Int32 nNewRow = aOutRange.aStart.Row() + nDiff;
+ if ( nNewRow < 0 )
+ nNewRow = 0;
+
+ ScAddress aStart( aOutRange.aStart );
+ aStart.SetRow(nNewRow);
+ pOutput->SetPosition( aStart );
+
+ //TODO: modify aOutRange?
+
+ bAllowMove = false; // use only once
+}
+
+namespace {
+
+class DisableGetPivotData
+{
+ ScDPObject& mrDPObj;
+ bool mbOldState;
+public:
+ DisableGetPivotData(ScDPObject& rObj, bool bOld) : mrDPObj(rObj), mbOldState(bOld)
+ {
+ mrDPObj.EnableGetPivotData(false);
+ }
+
+ ~DisableGetPivotData()
+ {
+ mrDPObj.EnableGetPivotData(mbOldState);
+ }
+};
+
+class FindIntersectingTable
+{
+ ScRange maRange;
+public:
+ explicit FindIntersectingTable(const ScRange& rRange) : maRange(rRange) {}
+
+ bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
+ {
+ return maRange.Intersects(rObj->GetOutRange());
+ }
+};
+
+class FindIntersectingTableByColumns
+{
+ SCCOL mnCol1;
+ SCCOL mnCol2;
+ SCROW mnRow;
+ SCTAB mnTab;
+public:
+ FindIntersectingTableByColumns(SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCTAB nTab) :
+ mnCol1(nCol1), mnCol2(nCol2), mnRow(nRow), mnTab(nTab) {}
+
+ bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
+ {
+ const ScRange& rRange = rObj->GetOutRange();
+ if (mnTab != rRange.aStart.Tab())
+ // Not on this sheet.
+ return false;
+
+ if (rRange.aEnd.Row() < mnRow)
+ // This table is above the row. It's safe.
+ return false;
+
+ if (mnCol1 <= rRange.aStart.Col() && rRange.aEnd.Col() <= mnCol2)
+ // This table is fully enclosed in this column range.
+ return false;
+
+ if (rRange.aEnd.Col() < mnCol1 || mnCol2 < rRange.aStart.Col())
+ // This table is entirely outside this column range.
+ return false;
+
+ // This table must be intersected by this column range.
+ return true;
+ }
+};
+
+class FindIntersectingTableByRows
+{
+ SCCOL mnCol;
+ SCROW mnRow1;
+ SCROW mnRow2;
+ SCTAB mnTab;
+public:
+ FindIntersectingTableByRows(SCCOL nCol, SCROW nRow1, SCROW nRow2, SCTAB nTab) :
+ mnCol(nCol), mnRow1(nRow1), mnRow2(nRow2), mnTab(nTab) {}
+
+ bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
+ {
+ const ScRange& rRange = rObj->GetOutRange();
+ if (mnTab != rRange.aStart.Tab())
+ // Not on this sheet.
+ return false;
+
+ if (rRange.aEnd.Col() < mnCol)
+ // This table is to the left of the column. It's safe.
+ return false;
+
+ if (mnRow1 <= rRange.aStart.Row() && rRange.aEnd.Row() <= mnRow2)
+ // This table is fully enclosed in this row range.
+ return false;
+
+ if (rRange.aEnd.Row() < mnRow1 || mnRow2 < rRange.aStart.Row())
+ // This table is entirely outside this row range.
+ return false;
+
+ // This table must be intersected by this row range.
+ return true;
+ }
+};
+
+class AccumulateOutputRanges
+{
+ ScRangeList maRanges;
+ SCTAB mnTab;
+public:
+ explicit AccumulateOutputRanges(SCTAB nTab) : mnTab(nTab) {}
+ AccumulateOutputRanges(const AccumulateOutputRanges& r) : maRanges(r.maRanges), mnTab(r.mnTab) {}
+
+ void operator() (const std::unique_ptr<ScDPObject>& rObj)
+ {
+ const ScRange& rRange = rObj->GetOutRange();
+ if (mnTab != rRange.aStart.Tab())
+ // Not on this sheet.
+ return;
+
+ maRanges.Join(rRange);
+ }
+
+ const ScRangeList& getRanges() const { return maRanges; }
+};
+
+}
+
+ScDPTableData* ScDPObject::GetTableData()
+{
+ if (!mpTableData)
+ {
+ shared_ptr<ScDPTableData> pData;
+ const ScDPDimensionSaveData* pDimData = pSaveData ? pSaveData->GetExistingDimensionData() : nullptr;
+
+ if ( pImpDesc )
+ {
+ // database data
+ const ScDPCache* pCache = pImpDesc->CreateCache(pDimData);
+ if (pCache)
+ {
+ pCache->AddReference(this);
+ pData = std::make_shared<ScDatabaseDPData>(pDoc, *pCache);
+ }
+ }
+ else
+ {
+ // cell data
+ if (!pSheetDesc)
+ {
+ OSL_FAIL("no source descriptor");
+ pSheetDesc.reset( new ScSheetSourceDesc(pDoc) ); // dummy defaults
+ }
+
+ {
+ // Temporarily disable GETPIVOTDATA to avoid having
+ // GETPIVOTDATA called onto itself from within the source
+ // range.
+ DisableGetPivotData aSwitch(*this, mbEnableGetPivotData);
+ const ScDPCache* pCache = pSheetDesc->CreateCache(pDimData);
+ if (pCache)
+ {
+ pCache->AddReference(this);
+ pData = std::make_shared<ScSheetDPData>(pDoc, *pSheetDesc, *pCache);
+ }
+ }
+ }
+
+ // grouping (for cell or database data)
+ if (pData && pDimData)
+ {
+ auto pGroupData = std::make_shared<ScDPGroupTableData>(pData, pDoc);
+ pDimData->WriteToData(*pGroupData);
+ pData = pGroupData;
+ }
+
+ mpTableData = pData; // after SetCacheId
+ }
+
+ return mpTableData.get();
+}
+
+void ScDPObject::CreateObjects()
+{
+ if (!xSource.is())
+ {
+ pOutput.reset(); // not valid when xSource is changed
+
+ if ( pServDesc )
+ {
+ xSource = CreateSource( *pServDesc );
+ }
+
+ if ( !xSource.is() ) // database or sheet data, or error in CreateSource
+ {
+ OSL_ENSURE( !pServDesc, "DPSource could not be created" );
+ ScDPTableData* pData = GetTableData();
+ if (pData)
+ {
+ if (pSaveData)
+ // Make sure to transfer these flags to the table data
+ // since they may have changed.
+ pData->SetEmptyFlags(pSaveData->GetIgnoreEmptyRows(), pSaveData->GetRepeatIfEmpty());
+
+ pData->ReloadCacheTable();
+ xSource = new ScDPSource( pData );
+ }
+ }
+
+ if (pSaveData)
+ pSaveData->WriteToSource( xSource );
+ }
+ else if (bSettingsChanged)
+ {
+ pOutput.reset(); // not valid when xSource is changed
+
+ uno::Reference<util::XRefreshable> xRef( xSource, uno::UNO_QUERY );
+ if (xRef.is())
+ {
+ try
+ {
+ xRef->refresh();
+ }
+ catch(uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "exception in refresh");
+ }
+ }
+
+ if (pSaveData)
+ pSaveData->WriteToSource( xSource );
+ }
+ bSettingsChanged = false;
+}
+
+void ScDPObject::InvalidateData()
+{
+ bSettingsChanged = true;
+}
+
+void ScDPObject::Clear()
+{
+ pOutput.reset();
+ pSaveData.reset();
+ pSheetDesc.reset();
+ pImpDesc.reset();
+ pServDesc.reset();
+ ClearTableData();
+ maInteropGrabBag.clear();
+}
+
+void ScDPObject::ClearTableData()
+{
+ ClearSource();
+
+ if (mpTableData)
+ mpTableData->GetCacheTable().getCache().RemoveReference(this);
+ mpTableData.reset();
+}
+
+void ScDPObject::ReloadGroupTableData()
+{
+ ClearSource();
+
+ if (!mpTableData)
+ // Table data not built yet. No need to reload the group data.
+ return;
+
+ if (!pSaveData)
+ // How could it not have the save data... but whatever.
+ return;
+
+ const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
+ if (!pDimData || !pDimData->HasGroupDimensions())
+ {
+ // No group dimensions exist. Check if it currently has group
+ // dimensions, and if so, remove all of them.
+ ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get());
+ if (pData)
+ {
+ // Replace the existing group table data with the source data.
+ mpTableData = pData->GetSourceTableData();
+ }
+ return;
+ }
+
+ ScDPGroupTableData* pData = dynamic_cast<ScDPGroupTableData*>(mpTableData.get());
+ if (pData)
+ {
+ // This is already a group table data. Salvage the source data and
+ // re-create a new group data.
+ const shared_ptr<ScDPTableData>& pSource = pData->GetSourceTableData();
+ auto pGroupData = std::make_shared<ScDPGroupTableData>(pSource, pDoc);
+ pDimData->WriteToData(*pGroupData);
+ mpTableData = pGroupData;
+ }
+ else
+ {
+ // This is a source data. Create a group data based on it.
+ auto pGroupData = std::make_shared<ScDPGroupTableData>(mpTableData, pDoc);
+ pDimData->WriteToData(*pGroupData);
+ mpTableData = pGroupData;
+ }
+
+ bSettingsChanged = true;
+}
+
+void ScDPObject::ClearSource()
+{
+ Reference< XComponent > xObjectComp( xSource, UNO_QUERY );
+ if (xObjectComp.is())
+ {
+ try
+ {
+ xObjectComp->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sc.core");
+ }
+ }
+ xSource = nullptr;
+}
+
+ScRange ScDPObject::GetNewOutputRange( bool& rOverflow )
+{
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ rOverflow = pOutput->HasError(); // range overflow or exception from source
+ if ( rOverflow )
+ return ScRange( aOutRange.aStart );
+ else
+ {
+ // don't store the result in aOutRange, because nothing has been output yet
+ return pOutput->GetOutputRange();
+ }
+}
+
+void ScDPObject::Output( const ScAddress& rPos )
+{
+ // clear old output area
+ pDoc->DeleteAreaTab( aOutRange.aStart.Col(), aOutRange.aStart.Row(),
+ aOutRange.aEnd.Col(), aOutRange.aEnd.Row(),
+ aOutRange.aStart.Tab(), InsertDeleteFlags::ALL );
+ pDoc->RemoveFlagsTab( aOutRange.aStart.Col(), aOutRange.aStart.Row(),
+ aOutRange.aEnd.Col(), aOutRange.aEnd.Row(),
+ aOutRange.aStart.Tab(), ScMF::Auto );
+
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ pOutput->SetPosition( rPos );
+
+ pOutput->Output();
+
+ // aOutRange is always the range that was last output to the document
+ aOutRange = pOutput->GetOutputRange();
+ const ScAddress& s = aOutRange.aStart;
+ const ScAddress& e = aOutRange.aEnd;
+ pDoc->ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
+}
+
+ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType )
+{
+ CreateOutput();
+
+ if (pOutput->HasError())
+ return ScRange(aOutRange.aStart);
+
+ return pOutput->GetOutputRange(nType);
+}
+
+ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType ) const
+{
+ if (!pOutput || pOutput->HasError())
+ return ScRange(ScAddress::INITIALIZE_INVALID);
+
+ return pOutput->GetOutputRange(nType);
+}
+
+static bool lcl_HasButton( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ return pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->HasPivotButton();
+}
+
+void ScDPObject::RefreshAfterLoad()
+{
+ // apply drop-down attribute, initialize nHeaderRows, without accessing the source
+ // (button attribute must be present)
+
+ // simple test: block of button cells at the top, followed by an empty cell
+
+ SCCOL nFirstCol = aOutRange.aStart.Col();
+ SCROW nFirstRow = aOutRange.aStart.Row();
+ SCTAB nTab = aOutRange.aStart.Tab();
+
+ SCROW nInitial = 0;
+ SCROW nOutRows = aOutRange.aEnd.Row() + 1 - aOutRange.aStart.Row();
+ while ( nInitial + 1 < nOutRows && lcl_HasButton( pDoc, nFirstCol, nFirstRow + nInitial, nTab ) )
+ ++nInitial;
+
+ if ( nInitial + 1 < nOutRows &&
+ pDoc->IsBlockEmpty( nFirstCol, nFirstRow + nInitial, nFirstCol, nFirstRow + nInitial, nTab ) &&
+ aOutRange.aEnd.Col() > nFirstCol )
+ {
+ nHeaderRows = nInitial;
+ }
+ else
+ nHeaderRows = 0; // nothing found, no drop-down lists
+}
+
+void ScDPObject::BuildAllDimensionMembers()
+{
+ if (!pSaveData)
+ return;
+
+ // #i111857# don't always create empty mpTableData for external service.
+ if (pServDesc)
+ return;
+
+ ScDPTableData* pTableData = GetTableData();
+ if(pTableData)
+ pSaveData->BuildAllDimensionMembers(pTableData);
+}
+
+bool ScDPObject::SyncAllDimensionMembers()
+{
+ if (!pSaveData)
+ return false;
+
+ // #i111857# don't always create empty mpTableData for external service.
+ // Ideally, xSource should be used instead of mpTableData.
+ if (pServDesc)
+ return false;
+
+ ScDPTableData* pData = GetTableData();
+ if (!pData)
+ // No table data exists. This can happen when refreshing from an
+ // external source which doesn't exist.
+ return false;
+
+ // Refresh the cache wrapper since the cache may have changed.
+ pData->SetEmptyFlags(pSaveData->GetIgnoreEmptyRows(), pSaveData->GetRepeatIfEmpty());
+ pData->ReloadCacheTable();
+ pSaveData->SyncAllDimensionMembers(pData);
+ return true;
+}
+
+bool ScDPObject::GetMemberNames( sal_Int32 nDim, Sequence<OUString>& rNames )
+{
+ vector<ScDPLabelData::Member> aMembers;
+ if (!GetMembers(nDim, GetUsedHierarchy(nDim), aMembers))
+ return false;
+
+ size_t n = aMembers.size();
+ rNames.realloc(n);
+ auto pNames = rNames.getArray();
+ for (size_t i = 0; i < n; ++i)
+ pNames[i] = aMembers[i].maName;
+
+ return true;
+}
+
+bool ScDPObject::GetMembers( sal_Int32 nDim, sal_Int32 nHier, vector<ScDPLabelData::Member>& rMembers )
+{
+ Reference< sheet::XMembersAccess > xMembersNA;
+ if (!GetMembersNA( nDim, nHier, xMembersNA ))
+ return false;
+
+ Reference<container::XIndexAccess> xMembersIA( new ScNameToIndexAccess(xMembersNA) );
+ sal_Int32 nCount = xMembersIA->getCount();
+ vector<ScDPLabelData::Member> aMembers;
+ aMembers.reserve(nCount);
+
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ Reference<container::XNamed> xMember;
+ try
+ {
+ xMember = Reference<container::XNamed>(xMembersIA->getByIndex(i), UNO_QUERY);
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ TOOLS_WARN_EXCEPTION("sc", "ScNameToIndexAccess getByIndex failed");
+ }
+
+ ScDPLabelData::Member aMem;
+
+ if (xMember.is())
+ aMem.maName = xMember->getName();
+
+ Reference<beans::XPropertySet> xMemProp(xMember, UNO_QUERY);
+ if (xMemProp.is())
+ {
+ aMem.mbVisible = ScUnoHelpFunctions::GetBoolProperty(xMemProp, SC_UNO_DP_ISVISIBLE);
+ aMem.mbShowDetails = ScUnoHelpFunctions::GetBoolProperty(xMemProp, SC_UNO_DP_SHOWDETAILS);
+
+ aMem.maLayoutName = ScUnoHelpFunctions::GetStringProperty(
+ xMemProp, SC_UNO_DP_LAYOUTNAME, OUString());
+ }
+
+ aMembers.push_back(aMem);
+ }
+ rMembers.swap(aMembers);
+ return true;
+}
+
+void ScDPObject::UpdateReference( UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ // Output area
+
+ SCCOL nCol1 = aOutRange.aStart.Col();
+ SCROW nRow1 = aOutRange.aStart.Row();
+ SCTAB nTab1 = aOutRange.aStart.Tab();
+ SCCOL nCol2 = aOutRange.aEnd.Col();
+ SCROW nRow2 = aOutRange.aEnd.Row();
+ SCTAB nTab2 = aOutRange.aEnd.Tab();
+
+ ScRefUpdateRes eRes =
+ ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( eRes != UR_NOTHING )
+ SetOutRange( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
+
+ // sheet source data
+
+ if ( !pSheetDesc )
+ return;
+
+ const OUString& rRangeName = pSheetDesc->GetRangeName();
+ if (!rRangeName.isEmpty())
+ // Source range is a named range. No need to update.
+ return;
+
+ const ScRange& rSrcRange = pSheetDesc->GetSourceRange();
+ nCol1 = rSrcRange.aStart.Col();
+ nRow1 = rSrcRange.aStart.Row();
+ nTab1 = rSrcRange.aStart.Tab();
+ nCol2 = rSrcRange.aEnd.Col();
+ nRow2 = rSrcRange.aEnd.Row();
+ nTab2 = rSrcRange.aEnd.Tab();
+
+ eRes = ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( eRes == UR_NOTHING )
+ return;
+
+ SCCOL nDiffX = nCol1 - pSheetDesc->GetSourceRange().aStart.Col();
+ SCROW nDiffY = nRow1 - pSheetDesc->GetSourceRange().aStart.Row();
+
+ ScQueryParam aParam = pSheetDesc->GetQueryParam();
+ aParam.nCol1 = sal::static_int_cast<SCCOL>( aParam.nCol1 + nDiffX );
+ aParam.nCol2 = sal::static_int_cast<SCCOL>( aParam.nCol2 + nDiffX );
+ aParam.nRow1 += nDiffY; //TODO: used?
+ aParam.nRow2 += nDiffY; //TODO: used?
+ SCSIZE nEC = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nEC; i++)
+ if (aParam.GetEntry(i).bDoQuery)
+ aParam.GetEntry(i).nField += nDiffX;
+
+ pSheetDesc->SetQueryParam(aParam);
+ pSheetDesc->SetSourceRange(ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
+}
+
+bool ScDPObject::RefsEqual( const ScDPObject& r ) const
+{
+ if ( aOutRange != r.aOutRange )
+ return false;
+
+ if ( pSheetDesc && r.pSheetDesc )
+ {
+ if ( pSheetDesc->GetSourceRange() != r.pSheetDesc->GetSourceRange() )
+ return false;
+ }
+ else if ( pSheetDesc || r.pSheetDesc )
+ {
+ OSL_FAIL("RefsEqual: SheetDesc set at only one object");
+ return false;
+ }
+
+ return true;
+}
+
+void ScDPObject::WriteRefsTo( ScDPObject& r ) const
+{
+ r.SetOutRange( aOutRange );
+ if ( pSheetDesc )
+ r.SetSheetDesc( *pSheetDesc );
+}
+
+void ScDPObject::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
+{
+ CreateOutput();
+ pOutput->GetPositionData(rPos, rPosData);
+}
+
+bool ScDPObject::GetDataFieldPositionData(
+ const ScAddress& rPos, Sequence<sheet::DataPilotFieldFilter>& rFilters)
+{
+ CreateOutput();
+
+ vector<sheet::DataPilotFieldFilter> aFilters;
+ if (!pOutput->GetDataResultPositionData(aFilters, rPos))
+ return false;
+
+ sal_Int32 n = static_cast<sal_Int32>(aFilters.size());
+ rFilters.realloc(n);
+ auto pFilters = rFilters.getArray();
+ for (sal_Int32 i = 0; i < n; ++i)
+ pFilters[i] = aFilters[i];
+
+ return true;
+}
+
+void ScDPObject::GetDrillDownData(const ScAddress& rPos, Sequence< Sequence<Any> >& rTableData)
+{
+ CreateOutput();
+
+ Reference<sheet::XDrillDownDataSupplier> xDrillDownData(xSource, UNO_QUERY);
+ if (!xDrillDownData.is())
+ return;
+
+ Sequence<sheet::DataPilotFieldFilter> filters;
+ if (!GetDataFieldPositionData(rPos, filters))
+ return;
+
+ rTableData = xDrillDownData->getDrillDownData(filters);
+}
+
+bool ScDPObject::IsDimNameInUse(std::u16string_view rName) const
+{
+ if (!xSource.is())
+ return false;
+
+ Reference<container::XNameAccess> xDims = xSource->getDimensions();
+ const Sequence<OUString> aDimNames = xDims->getElementNames();
+ for (const OUString& rDimName : aDimNames)
+ {
+ if (rDimName.equalsIgnoreAsciiCase(rName))
+ return true;
+
+ Reference<beans::XPropertySet> xPropSet(xDims->getByName(rDimName), UNO_QUERY);
+ if (!xPropSet.is())
+ continue;
+
+ OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
+ xPropSet, SC_UNO_DP_LAYOUTNAME, OUString());
+ if (aLayoutName.equalsIgnoreAsciiCase(rName))
+ return true;
+ }
+ return false;
+}
+
+OUString ScDPObject::GetDimName( tools::Long nDim, bool& rIsDataLayout, sal_Int32* pFlags )
+{
+ rIsDataLayout = false;
+ OUString aRet;
+
+ if ( xSource.is() )
+ {
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nDimCount = xDims->getCount();
+ if ( nDim < nDimCount )
+ {
+ uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
+ uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
+ if ( xDimName.is() && xDimProp.is() )
+ {
+ bool bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT );
+ //TODO: error checking -- is "IsDataLayoutDimension" property required??
+
+ OUString aName;
+ try
+ {
+ aName = xDimName->getName();
+ }
+ catch(uno::Exception&)
+ {
+ }
+ if ( bData )
+ rIsDataLayout = true;
+ else
+ aRet = aName;
+
+ if (pFlags)
+ *pFlags = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+ SC_UNO_DP_FLAGS );
+ }
+ }
+ }
+ else if (ScDPTableData* pData = GetTableData())
+ {
+ aRet = pData->getDimensionName(nDim);
+ rIsDataLayout = pData->getIsDataLayoutDimension(nDim);
+ }
+
+ return aRet;
+}
+
+bool ScDPObject::IsDuplicated( tools::Long nDim )
+{
+ bool bDuplicated = false;
+ if ( xSource.is() )
+ {
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nDimCount = xDims->getCount();
+ if ( nDim < nDimCount )
+ {
+ uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ if ( xDimProp.is() )
+ {
+ try
+ {
+ uno::Any aOrigAny = xDimProp->getPropertyValue( SC_UNO_DP_ORIGINAL );
+ uno::Reference<uno::XInterface> xIntOrig;
+ if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() )
+ bDuplicated = true;
+ }
+ catch(uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ return bDuplicated;
+}
+
+tools::Long ScDPObject::GetDimCount()
+{
+ tools::Long nRet = 0;
+ if ( xSource.is() )
+ {
+ try
+ {
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ if ( xDimsName.is() )
+ nRet = xDimsName->getElementNames().getLength();
+ }
+ catch(uno::Exception&)
+ {
+ }
+ }
+ return nRet;
+}
+
+void ScDPObject::GetHeaderPositionData(const ScAddress& rPos, DataPilotTableHeaderData& rData)
+{
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ // Reset member values to invalid state.
+ rData.Dimension = rData.Hierarchy = rData.Level = -1;
+ rData.Flags = 0;
+
+ DataPilotTablePositionData aPosData;
+ pOutput->GetPositionData(rPos, aPosData);
+ const sal_Int32 nPosType = aPosData.PositionType;
+ if (nPosType == css::sheet::DataPilotTablePositionType::COLUMN_HEADER || nPosType == css::sheet::DataPilotTablePositionType::ROW_HEADER)
+ aPosData.PositionData >>= rData;
+}
+
+namespace {
+
+class FindByName
+{
+ OUString maName; // must be all uppercase.
+public:
+ explicit FindByName(OUString aName) : maName(std::move(aName)) {}
+ bool operator() (const ScDPSaveDimension* pDim) const
+ {
+ // Layout name takes precedence.
+ const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
+ if (pLayoutName && ScGlobal::getCharClass().uppercase(*pLayoutName) == maName)
+ return true;
+
+ ScGeneralFunction eGenFunc = pDim->GetFunction();
+ ScSubTotalFunc eFunc = ScDPUtil::toSubTotalFunc(eGenFunc);
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
+ OUString aFuncName = ScDPUtil::getDisplayedMeasureName(aSrcName, eFunc);
+ if (maName == ScGlobal::getCharClass().uppercase(aFuncName))
+ return true;
+
+ return maName == ScGlobal::getCharClass().uppercase(aSrcName);
+ }
+};
+
+class LessByDimOrder
+{
+ const ScDPSaveData::DimOrderType& mrDimOrder;
+
+public:
+ explicit LessByDimOrder(const ScDPSaveData::DimOrderType& rDimOrder) : mrDimOrder(rDimOrder) {}
+
+ bool operator() (const sheet::DataPilotFieldFilter& r1, const sheet::DataPilotFieldFilter& r2) const
+ {
+ size_t nRank1 = mrDimOrder.size();
+ size_t nRank2 = mrDimOrder.size();
+ ScDPSaveData::DimOrderType::const_iterator it1 = mrDimOrder.find(
+ ScGlobal::getCharClass().uppercase(r1.FieldName));
+ if (it1 != mrDimOrder.end())
+ nRank1 = it1->second;
+
+ ScDPSaveData::DimOrderType::const_iterator it2 = mrDimOrder.find(
+ ScGlobal::getCharClass().uppercase(r2.FieldName));
+ if (it2 != mrDimOrder.end())
+ nRank2 = it2->second;
+
+ return nRank1 < nRank2;
+ }
+};
+
+}
+
+double ScDPObject::GetPivotData(const OUString& rDataFieldName, std::vector<sheet::DataPilotFieldFilter>& rFilters)
+{
+ if (!mbEnableGetPivotData)
+ return std::numeric_limits<double>::quiet_NaN();
+
+ CreateObjects();
+
+ std::vector<const ScDPSaveDimension*> aDataDims;
+ pSaveData->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_DATA, aDataDims);
+ if (aDataDims.empty())
+ return std::numeric_limits<double>::quiet_NaN();
+
+ std::vector<const ScDPSaveDimension*>::iterator it = std::find_if(
+ aDataDims.begin(), aDataDims.end(),
+ FindByName(ScGlobal::getCharClass().uppercase(rDataFieldName)));
+
+ if (it == aDataDims.end())
+ return std::numeric_limits<double>::quiet_NaN();
+
+ size_t nDataIndex = std::distance(aDataDims.begin(), it);
+
+ uno::Reference<sheet::XDataPilotResults> xDPResults(xSource, uno::UNO_QUERY);
+ if (!xDPResults.is())
+ return std::numeric_limits<double>::quiet_NaN();
+
+ // Dimensions must be sorted in order of appearance, and row dimensions
+ // must come before column dimensions.
+ std::sort(rFilters.begin(), rFilters.end(), LessByDimOrder(pSaveData->GetDimensionSortOrder()));
+
+ size_t n = rFilters.size();
+ uno::Sequence<sheet::DataPilotFieldFilter> aFilters(n);
+ auto aFiltersRange = asNonConstRange(aFilters);
+ for (size_t i = 0; i < n; ++i)
+ aFiltersRange[i] = rFilters[i];
+
+ uno::Sequence<double> aRes = xDPResults->getFilteredResults(aFilters);
+ if (nDataIndex >= o3tl::make_unsigned(aRes.getLength()))
+ return std::numeric_limits<double>::quiet_NaN();
+
+ return aRes[nDataIndex];
+}
+
+bool ScDPObject::IsFilterButton( const ScAddress& rPos )
+{
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ return pOutput->IsFilterButton( rPos );
+}
+
+tools::Long ScDPObject::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
+{
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ return pOutput->GetHeaderDim( rPos, rOrient );
+}
+
+bool ScDPObject::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop, tools::Long nDragDim,
+ tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
+{
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ return pOutput->GetHeaderDrag( rPos, bMouseLeft, bMouseTop, nDragDim, rPosRect, rOrient, rDimPos );
+}
+
+void ScDPObject::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
+{
+ CreateOutput(); // create xSource and pOutput if not already done
+
+ pOutput->GetMemberResultNames(rNames, nDimension); // used only with table data -> level not needed
+}
+
+OUString ScDPObject::GetFormattedString(std::u16string_view rDimName, const double fValue)
+{
+ ScDPTableData* pTableData = GetTableData();
+ if(!pTableData)
+ return OUString();
+
+ tools::Long nDim;
+ for (nDim = 0; nDim < pTableData->GetColumnCount(); ++nDim)
+ {
+ if(rDimName == pTableData->getDimensionName(nDim))
+ break;
+ }
+ ScDPItemData aItemData;
+ aItemData.SetValue(fValue);
+ return GetTableData()->GetFormattedString(nDim, aItemData, false);
+}
+
+
+namespace {
+
+bool dequote( std::u16string_view rSource, sal_Int32 nStartPos, sal_Int32& rEndPos, OUString& rResult )
+{
+ // nStartPos has to point to opening quote
+
+ const sal_Unicode cQuote = '\'';
+
+ if (rSource[nStartPos] == cQuote)
+ {
+ OUStringBuffer aBuffer;
+ sal_Int32 nPos = nStartPos + 1;
+ const sal_Int32 nLen = rSource.size();
+
+ while ( nPos < nLen )
+ {
+ const sal_Unicode cNext = rSource[nPos];
+ if ( cNext == cQuote )
+ {
+ if (nPos+1 < nLen && rSource[nPos+1] == cQuote)
+ {
+ // double quote is used for an embedded quote
+ aBuffer.append( cNext ); // append one quote
+ ++nPos; // skip the next one
+ }
+ else
+ {
+ // end of quoted string
+ rResult = aBuffer.makeStringAndClear();
+ rEndPos = nPos + 1; // behind closing quote
+ return true;
+ }
+ }
+ else
+ aBuffer.append( cNext );
+
+ ++nPos;
+ }
+ // no closing quote before the end of the string -> error (bRet still false)
+ }
+
+ return false;
+}
+
+struct ScGetPivotDataFunctionEntry
+{
+ const char* pName;
+ sal_Int16 eFunc;
+};
+
+bool parseFunction( std::u16string_view rList, sal_Int32 nStartPos, sal_Int32& rEndPos, sal_Int16& rFunc )
+{
+ static const ScGetPivotDataFunctionEntry aFunctions[] =
+ {
+ // our names
+ { "Sum", sheet::GeneralFunction2::SUM },
+ { "Count", sheet::GeneralFunction2::COUNT },
+ { "Average", sheet::GeneralFunction2::AVERAGE },
+ { "Max", sheet::GeneralFunction2::MAX },
+ { "Min", sheet::GeneralFunction2::MIN },
+ { "Product", sheet::GeneralFunction2::PRODUCT },
+ { "CountNums", sheet::GeneralFunction2::COUNTNUMS },
+ { "StDev", sheet::GeneralFunction2::STDEV },
+ { "StDevp", sheet::GeneralFunction2::STDEVP },
+ { "Var", sheet::GeneralFunction2::VAR },
+ { "VarP", sheet::GeneralFunction2::VARP },
+ // compatibility names
+ { "Count Nums", sheet::GeneralFunction2::COUNTNUMS },
+ { "StdDev", sheet::GeneralFunction2::STDEV },
+ { "StdDevp", sheet::GeneralFunction2::STDEVP }
+ };
+
+ const sal_Int32 nListLen = rList.size();
+ while (nStartPos < nListLen && rList[nStartPos] == ' ')
+ ++nStartPos;
+
+ bool bParsed = false;
+ bool bFound = false;
+ OUString aFuncStr;
+ sal_Int32 nFuncEnd = 0;
+ if (nStartPos < nListLen && rList[nStartPos] == '\'')
+ bParsed = dequote( rList, nStartPos, nFuncEnd, aFuncStr );
+ else
+ {
+ nFuncEnd = rList.find(']', nStartPos);
+ if (nFuncEnd >= 0)
+ {
+ aFuncStr = rList.substr(nStartPos, nFuncEnd - nStartPos);
+ bParsed = true;
+ }
+ }
+
+ if ( bParsed )
+ {
+ aFuncStr = comphelper::string::strip(aFuncStr, ' ');
+
+ const sal_Int32 nFuncCount = SAL_N_ELEMENTS(aFunctions);
+ for ( sal_Int32 nFunc=0; nFunc<nFuncCount && !bFound; nFunc++ )
+ {
+ if (aFuncStr.equalsIgnoreAsciiCaseAscii(aFunctions[nFunc].pName))
+ {
+ rFunc = aFunctions[nFunc].eFunc;
+ bFound = true;
+
+ while (nFuncEnd < nListLen && rList[nFuncEnd] == ' ')
+ ++nFuncEnd;
+ rEndPos = nFuncEnd;
+ }
+ }
+ }
+
+ return bFound;
+}
+
+bool extractAtStart( std::u16string_view rList, sal_Int32& rMatched, bool bAllowBracket, sal_Int16* pFunc,
+ OUString& rDequoted )
+{
+ size_t nMatchList = 0;
+ sal_Unicode cFirst = rList[0];
+ bool bParsed = false;
+ if ( cFirst == '\'' || cFirst == '[' )
+ {
+ // quoted string or string in brackets must match completely
+
+ OUString aDequoted;
+ sal_Int32 nQuoteEnd = 0;
+
+ if ( cFirst == '\'' )
+ bParsed = dequote( rList, 0, nQuoteEnd, aDequoted );
+ else if ( cFirst == '[' )
+ {
+ // skip spaces after the opening bracket
+
+ sal_Int32 nStartPos = 1;
+ const sal_Int32 nListLen = rList.size();
+ while (nStartPos < nListLen && rList[nStartPos] == ' ')
+ ++nStartPos;
+
+ if (nStartPos < nListLen && rList[nStartPos] == '\'') // quoted within the brackets?
+ {
+ if ( dequote( rList, nStartPos, nQuoteEnd, aDequoted ) )
+ {
+ // after the quoted string, there must be the closing bracket, optionally preceded by spaces,
+ // and/or a function name
+ while (nQuoteEnd < nListLen && rList[nQuoteEnd] == ' ')
+ ++nQuoteEnd;
+
+ // semicolon separates function name
+ if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ';' && pFunc)
+ {
+ sal_Int32 nFuncEnd = 0;
+ if ( parseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) )
+ nQuoteEnd = nFuncEnd;
+ }
+ if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ']')
+ {
+ ++nQuoteEnd; // include the closing bracket for the matched length
+ bParsed = true;
+ }
+ }
+ }
+ else
+ {
+ // implicit quoting to the closing bracket
+
+ sal_Int32 nClosePos = rList.find(']', nStartPos);
+ if (nClosePos >= 0)
+ {
+ sal_Int32 nNameEnd = nClosePos;
+ sal_Int32 nSemiPos = rList.find(';', nStartPos);
+ if (nSemiPos >= 0 && nSemiPos < nClosePos && pFunc)
+ {
+ sal_Int32 nFuncEnd = 0;
+ if (parseFunction(rList, nSemiPos+1, nFuncEnd, *pFunc))
+ nNameEnd = nSemiPos;
+ }
+
+ aDequoted = rList.substr(nStartPos, nNameEnd - nStartPos);
+ // spaces before the closing bracket or semicolon
+ aDequoted = comphelper::string::stripEnd(aDequoted, ' ');
+ nQuoteEnd = nClosePos + 1;
+ bParsed = true;
+ }
+ }
+ }
+
+ if ( bParsed )
+ {
+ nMatchList = nQuoteEnd; // match count in the list string, including quotes
+ rDequoted = aDequoted;
+ }
+ }
+
+ if (bParsed)
+ {
+ // look for following space or end of string
+
+ bool bValid = false;
+ if ( nMatchList >= rList.size() )
+ bValid = true;
+ else
+ {
+ sal_Unicode cNext = rList[nMatchList];
+ if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
+ bValid = true;
+ }
+
+ if ( bValid )
+ {
+ rMatched = nMatchList;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isAtStart(
+ const OUString& rList, const OUString& rSearch, sal_Int32& rMatched,
+ bool bAllowBracket, sal_Int16* pFunc )
+{
+ sal_Int32 nMatchList = 0;
+ sal_Int32 nMatchSearch = 0;
+ sal_Unicode cFirst = rList[0];
+ if ( cFirst == '\'' || cFirst == '[' )
+ {
+ OUString aDequoted;
+ bool bParsed = extractAtStart( rList, rMatched, bAllowBracket, pFunc, aDequoted);
+ if ( bParsed && ScGlobal::GetTransliteration().isEqual( aDequoted, rSearch ) )
+ {
+ nMatchList = rMatched; // match count in the list string, including quotes
+ nMatchSearch = rSearch.getLength();
+ }
+ }
+ else
+ {
+ // otherwise look for search string at the start of rList
+ ScGlobal::GetTransliteration().equals(
+ rList, 0, rList.getLength(), nMatchList, rSearch, 0, rSearch.getLength(), nMatchSearch);
+ }
+
+ if (nMatchSearch == rSearch.getLength())
+ {
+ // search string is at start of rList - look for following space or end of string
+
+ bool bValid = false;
+ if ( sal::static_int_cast<sal_Int32>(nMatchList) >= rList.getLength() )
+ bValid = true;
+ else
+ {
+ sal_Unicode cNext = rList[nMatchList];
+ if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
+ bValid = true;
+ }
+
+ if ( bValid )
+ {
+ rMatched = nMatchList;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+bool ScDPObject::ParseFilters(
+ OUString& rDataFieldName,
+ std::vector<sheet::DataPilotFieldFilter>& rFilters,
+ std::vector<sal_Int16>& rFilterFuncs, std::u16string_view rFilterList )
+{
+ // parse the string rFilterList into parameters for GetPivotData
+
+ CreateObjects(); // create xSource if not already done
+
+ std::vector<OUString> aDataNames; // data fields (source name)
+ std::vector<OUString> aGivenNames; // data fields (compound name)
+ std::vector<OUString> aFieldNames; // column/row/data fields
+ std::vector< uno::Sequence<OUString> > aFieldValueNames;
+ std::vector< uno::Sequence<OUString> > aFieldValues;
+
+ // get all the field and item names
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ sal_Int32 nDimCount = xIntDims->getCount();
+ for ( sal_Int32 nDim = 0; nDim<nDimCount; nDim++ )
+ {
+ uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xDim( xIntDim, uno::UNO_QUERY );
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
+ bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT );
+ sheet::DataPilotFieldOrientation nOrient = ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( !bDataLayout )
+ {
+ if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ OUString aSourceName;
+ OUString aGivenName;
+ ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xIntDim );
+ aDataNames.push_back( aSourceName );
+ aGivenNames.push_back( aGivenName );
+ }
+ else if ( nOrient != sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ // get level names, as in ScDPOutput
+
+ uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+ sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+ SC_UNO_DP_USEDHIERARCHY );
+ if ( nHierarchy >= xHiers->getCount() )
+ nHierarchy = 0;
+
+ uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
+ uno::UNO_QUERY);
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
+ sal_Int32 nLevCount = xLevels->getCount();
+ for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
+ {
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
+ uno::Reference<sheet::XMembersSupplier> xLevSupp( xLevel, uno::UNO_QUERY );
+ if ( xLevNam.is() && xLevSupp.is() )
+ {
+ uno::Reference<sheet::XMembersAccess> xMembers = xLevSupp->getMembers();
+
+ OUString aFieldName( xLevNam->getName() );
+ // getElementNames() and getLocaleIndependentElementNames()
+ // must be consecutive calls to obtain strings in matching order.
+ uno::Sequence<OUString> aMemberValueNames( xMembers->getElementNames() );
+ uno::Sequence<OUString> aMemberValues( xMembers->getLocaleIndependentElementNames() );
+
+ aFieldNames.push_back( aFieldName );
+ aFieldValueNames.push_back( aMemberValueNames );
+ aFieldValues.push_back( aMemberValues );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // compare and build filters
+
+ SCSIZE nDataFields = aDataNames.size();
+ SCSIZE nFieldCount = aFieldNames.size();
+ OSL_ENSURE( aGivenNames.size() == nDataFields && aFieldValueNames.size() == nFieldCount &&
+ aFieldValues.size() == nFieldCount, "wrong count" );
+
+ bool bError = false;
+ bool bHasData = false;
+ OUString aRemaining(comphelper::string::strip(rFilterList, ' '));
+ while (!aRemaining.isEmpty() && !bError)
+ {
+ bool bUsed = false;
+
+ // look for data field name
+
+ for ( SCSIZE nDataPos=0; nDataPos<nDataFields && !bUsed; nDataPos++ )
+ {
+ OUString aFound;
+ sal_Int32 nMatched = 0;
+ if (isAtStart(aRemaining, aDataNames[nDataPos], nMatched, false, nullptr))
+ aFound = aDataNames[nDataPos];
+ else if (isAtStart(aRemaining, aGivenNames[nDataPos], nMatched, false, nullptr))
+ aFound = aGivenNames[nDataPos];
+
+ if (!aFound.isEmpty())
+ {
+ rDataFieldName = aFound;
+ aRemaining = aRemaining.copy(nMatched);
+ bHasData = true;
+ bUsed = true;
+ }
+ }
+
+ // look for field name
+
+ OUString aSpecField;
+ bool bHasFieldName = false;
+ if ( !bUsed )
+ {
+ sal_Int32 nMatched = 0;
+ for ( SCSIZE nField=0; nField<nFieldCount && !bHasFieldName; nField++ )
+ {
+ if (isAtStart(aRemaining, aFieldNames[nField], nMatched, true, nullptr))
+ {
+ aSpecField = aFieldNames[nField];
+ aRemaining = aRemaining.copy(nMatched);
+ aRemaining = comphelper::string::stripStart(aRemaining, ' ');
+
+ // field name has to be followed by item name in brackets
+ if (aRemaining.startsWith("["))
+ {
+ bHasFieldName = true;
+ // bUsed remains false - still need the item
+ }
+ else
+ {
+ bUsed = true;
+ bError = true;
+ }
+ }
+ }
+ }
+
+ // look for field item
+
+ if ( !bUsed )
+ {
+ bool bItemFound = false;
+ sal_Int32 nMatched = 0;
+ OUString aFoundName;
+ OUString aFoundValueName;
+ OUString aFoundValue;
+ sal_Int16 eFunc = sheet::GeneralFunction2::NONE;
+ sal_Int16 eFoundFunc = sheet::GeneralFunction2::NONE;
+
+ OUString aQueryValueName;
+ const bool bHasQuery = extractAtStart( aRemaining, nMatched, false, &eFunc, aQueryValueName);
+
+ OUString aQueryValue = aQueryValueName;
+ if (mpTableData)
+ {
+ SvNumberFormatter* pFormatter = mpTableData->GetCacheTable().getCache().GetNumberFormatter();
+ if (pFormatter)
+ {
+ // Parse possible number from aQueryValueName and format
+ // locale independent as aQueryValue.
+ sal_uInt32 nNumFormat = 0;
+ double fValue;
+ if (pFormatter->IsNumberFormat( aQueryValueName, nNumFormat, fValue))
+ aQueryValue = ScDPCache::GetLocaleIndependentFormattedString( fValue, *pFormatter, nNumFormat);
+ }
+ }
+
+ for ( SCSIZE nField=0; nField<nFieldCount; nField++ )
+ {
+ // If a field name is given, look in that field only, otherwise in all fields.
+ // aSpecField is initialized from aFieldNames array, so exact comparison can be used.
+ if ( !bHasFieldName || aFieldNames[nField] == aSpecField )
+ {
+ const uno::Sequence<OUString>& rItemNames = aFieldValueNames[nField];
+ const uno::Sequence<OUString>& rItemValues = aFieldValues[nField];
+ sal_Int32 nItemCount = rItemNames.getLength();
+ assert(nItemCount == rItemValues.getLength());
+ const OUString* pItemNamesArr = rItemNames.getConstArray();
+ const OUString* pItemValuesArr = rItemValues.getConstArray();
+ for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ )
+ {
+ bool bThisItemFound;
+ if (bHasQuery)
+ {
+ // First check given value name against both.
+ bThisItemFound = ScGlobal::GetTransliteration().isEqual(
+ aQueryValueName, pItemNamesArr[nItem]);
+ if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
+ bThisItemFound = ScGlobal::GetTransliteration().isEqual(
+ aQueryValueName, pItemValuesArr[nItem]);
+ if (!bThisItemFound && aQueryValueName != aQueryValue)
+ {
+ // Second check locale independent value
+ // against both.
+ /* TODO: or check only value string against
+ * value string, not against the value name? */
+ bThisItemFound = ScGlobal::GetTransliteration().isEqual(
+ aQueryValue, pItemNamesArr[nItem]);
+ if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
+ bThisItemFound = ScGlobal::GetTransliteration().isEqual(
+ aQueryValue, pItemValuesArr[nItem]);
+ }
+ }
+ else
+ {
+ bThisItemFound = isAtStart( aRemaining, pItemNamesArr[nItem], nMatched, false, &eFunc );
+ if (!bThisItemFound && pItemValuesArr[nItem] != pItemNamesArr[nItem])
+ bThisItemFound = isAtStart( aRemaining, pItemValuesArr[nItem], nMatched, false, &eFunc );
+ /* TODO: this checks only the given value name,
+ * check also locale independent value. But we'd
+ * have to do that in each iteration of the loop
+ * inside isAtStart() since a query could not be
+ * extracted and a match could be on the passed
+ * item value name string or item value string
+ * starting at aRemaining. */
+ }
+ if (bThisItemFound)
+ {
+ if ( bItemFound )
+ bError = true; // duplicate (also across fields)
+ else
+ {
+ aFoundName = aFieldNames[nField];
+ aFoundValueName = pItemNamesArr[nItem];
+ aFoundValue = pItemValuesArr[nItem];
+ eFoundFunc = eFunc;
+ bItemFound = true;
+ bUsed = true;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bItemFound && !bError )
+ {
+ sheet::DataPilotFieldFilter aField;
+ aField.FieldName = aFoundName;
+ aField.MatchValueName = aFoundValueName;
+ aField.MatchValue = aFoundValue;
+ rFilters.push_back(aField);
+ rFilterFuncs.push_back(eFoundFunc);
+ aRemaining = aRemaining.copy(nMatched);
+ }
+ }
+
+ if ( !bUsed )
+ bError = true;
+
+ // remove any number of spaces between entries
+ aRemaining = comphelper::string::stripStart(aRemaining, ' ');
+ }
+
+ if ( !bError && !bHasData && aDataNames.size() == 1 )
+ {
+ // if there's only one data field, its name need not be specified
+ rDataFieldName = aDataNames[0];
+ bHasData = true;
+ }
+
+ return bHasData && !bError;
+}
+
+void ScDPObject::ToggleDetails(const DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj)
+{
+ CreateObjects(); // create xSource if not already done
+
+ // find dimension name
+
+ uno::Reference<container::XNamed> xDim;
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nIntCount = xIntDims->getCount();
+ if ( rElemDesc.Dimension < nIntCount )
+ {
+ xDim.set(xIntDims->getByIndex(rElemDesc.Dimension), uno::UNO_QUERY);
+ }
+ OSL_ENSURE( xDim.is(), "dimension not found" );
+ if ( !xDim.is() ) return;
+ OUString aDimName = xDim->getName();
+
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT );
+ if (bDataLayout)
+ {
+ // the elements of the data layout dimension can't be found by their names
+ // -> don't change anything
+ return;
+ }
+
+ // query old state
+
+ tools::Long nHierCount = 0;
+ uno::Reference<container::XIndexAccess> xHiers;
+ uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
+ xHiers = new ScNameToIndexAccess( xHiersName );
+ nHierCount = xHiers->getCount();
+ }
+ uno::Reference<uno::XInterface> xHier;
+ if ( rElemDesc.Hierarchy < nHierCount )
+ xHier.set(xHiers->getByIndex(rElemDesc.Hierarchy), uno::UNO_QUERY);
+ OSL_ENSURE( xHier.is(), "hierarchy not found" );
+ if ( !xHier.is() ) return;
+
+ tools::Long nLevCount = 0;
+ uno::Reference<container::XIndexAccess> xLevels;
+ uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
+ if ( xLevSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
+ xLevels = new ScNameToIndexAccess( xLevsName );
+ nLevCount = xLevels->getCount();
+ }
+ uno::Reference<uno::XInterface> xLevel;
+ if ( rElemDesc.Level < nLevCount )
+ xLevel.set(xLevels->getByIndex(rElemDesc.Level), uno::UNO_QUERY);
+ OSL_ENSURE( xLevel.is(), "level not found" );
+ if ( !xLevel.is() ) return;
+
+ uno::Reference<sheet::XMembersAccess> xMembers;
+ uno::Reference<sheet::XMembersSupplier> xMbrSupp( xLevel, uno::UNO_QUERY );
+ if ( xMbrSupp.is() )
+ xMembers = xMbrSupp->getMembers();
+
+ bool bFound = false;
+ bool bShowDetails = true;
+
+ if ( xMembers.is() )
+ {
+ if ( xMembers->hasByName(rElemDesc.MemberName) )
+ {
+ uno::Reference<beans::XPropertySet> xMbrProp(xMembers->getByName(rElemDesc.MemberName),
+ uno::UNO_QUERY);
+ if ( xMbrProp.is() )
+ {
+ bShowDetails = ScUnoHelpFunctions::GetBoolProperty( xMbrProp,
+ SC_UNO_DP_SHOWDETAILS );
+ //TODO: don't set bFound if property is unknown?
+ bFound = true;
+ }
+ }
+ }
+
+ OSL_ENSURE( bFound, "member not found" );
+
+ //TODO: use Hierarchy and Level in SaveData !!!!
+
+ // modify pDestObj if set, this object otherwise
+ ScDPSaveData* pModifyData = pDestObj ? ( pDestObj->pSaveData.get() ) : pSaveData.get();
+ OSL_ENSURE( pModifyData, "no data?" );
+ if ( pModifyData )
+ {
+ const OUString aName = rElemDesc.MemberName;
+ pModifyData->GetDimensionByName(aDimName)->
+ GetMemberByName(aName)->SetShowDetails( !bShowDetails ); // toggle
+
+ if ( pDestObj )
+ pDestObj->InvalidateData(); // re-init source from SaveData
+ else
+ InvalidateData(); // re-init source from SaveData
+ }
+}
+
+static PivotFunc lcl_FirstSubTotal( const uno::Reference<beans::XPropertySet>& xDimProp ) // PIVOT_FUNC mask
+{
+ uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY );
+ if ( xDimProp.is() && xDimSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+ tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+ SC_UNO_DP_USEDHIERARCHY );
+ if ( nHierarchy >= xHiers->getCount() )
+ nHierarchy = 0;
+
+ uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
+ uno::UNO_QUERY);
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
+ if ( xLevProp.is() )
+ {
+ uno::Any aSubAny;
+ try
+ {
+ aSubAny = xLevProp->getPropertyValue( SC_UNO_DP_SUBTOTAL2 );
+ }
+ catch(uno::Exception&)
+ {
+ }
+ uno::Sequence<sal_Int16> aSeq;
+ if ( aSubAny >>= aSeq )
+ {
+ PivotFunc nMask = PivotFunc::NONE;
+ for (const sal_Int16 nElem : std::as_const(aSeq))
+ nMask |= ScDataPilotConversion::FunctionBit(nElem);
+ return nMask;
+ }
+ }
+ }
+ }
+
+ OSL_FAIL("FirstSubTotal: NULL");
+ return PivotFunc::NONE;
+}
+
+namespace {
+
+class FindByColumn
+{
+ SCCOL mnCol;
+ PivotFunc mnMask;
+public:
+ FindByColumn(SCCOL nCol, PivotFunc nMask) : mnCol(nCol), mnMask(nMask) {}
+ bool operator() (const ScPivotField& r) const
+ {
+ return r.nCol == mnCol && r.nFuncMask == mnMask;
+ }
+};
+
+}
+
+static void lcl_FillOldFields( ScPivotFieldVector& rFields,
+ const uno::Reference<sheet::XDimensionsSupplier>& xSource,
+ sheet::DataPilotFieldOrientation nOrient, bool bAddData )
+{
+ ScPivotFieldVector aFields;
+
+ bool bDataFound = false;
+
+ //TODO: merge multiple occurrences (data field with different functions)
+ //TODO: force data field in one dimension
+
+ vector<tools::Long> aPos;
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim = 0; nDim < nDimCount; ++nDim)
+ {
+ // dimension properties
+ uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
+
+ // dimension orientation, hidden by default.
+ sheet::DataPilotFieldOrientation nDimOrient = ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+
+ if ( xDimProp.is() && nDimOrient == nOrient )
+ {
+ // Let's take this dimension.
+
+ // function mask.
+ PivotFunc nMask = PivotFunc::NONE;
+ if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
+ xDimProp, SC_UNO_DP_FUNCTION2,
+ sheet::GeneralFunction2::NONE );
+ if ( eFunc == sheet::GeneralFunction2::AUTO )
+ {
+ //TODO: test for numeric data
+ eFunc = sheet::GeneralFunction2::SUM;
+ }
+ nMask = ScDataPilotConversion::FunctionBit(eFunc);
+ }
+ else
+ nMask = lcl_FirstSubTotal( xDimProp ); // from first hierarchy
+
+ // is this data layout dimension?
+ bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, SC_UNO_DP_ISDATALAYOUT);
+
+ // is this dimension cloned?
+ tools::Long nDupSource = -1;
+ try
+ {
+ uno::Any aOrigAny = xDimProp->getPropertyValue(SC_UNO_DP_ORIGINAL_POS);
+ sal_Int32 nTmp = 0;
+ if (aOrigAny >>= nTmp)
+ nDupSource = nTmp;
+ }
+ catch(uno::Exception&)
+ {
+ }
+
+ sal_uInt8 nDupCount = 0;
+ if (nDupSource >= 0)
+ {
+ // this dimension is cloned.
+
+ SCCOL nCompCol; // ID of the original dimension.
+ if ( bDataLayout )
+ nCompCol = PIVOT_DATA_FIELD;
+ else
+ nCompCol = static_cast<SCCOL>(nDupSource); //TODO: seek source column from name
+
+ ScPivotFieldVector::iterator it = std::find_if(aFields.begin(), aFields.end(), FindByColumn(nCompCol, nMask));
+ if (it != aFields.end())
+ nDupCount = it->mnDupCount + 1;
+ }
+
+ aFields.emplace_back();
+ ScPivotField& rField = aFields.back();
+ if (bDataLayout)
+ {
+ rField.nCol = PIVOT_DATA_FIELD;
+ bDataFound = true;
+ }
+ else
+ {
+ rField.mnOriginalDim = nDupSource;
+ rField.nCol = static_cast<SCCOL>(nDim); //TODO: seek source column from name
+ }
+
+ rField.nFuncMask = nMask;
+ rField.mnDupCount = nDupCount;
+ tools::Long nPos = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp, SC_UNO_DP_POSITION);
+ aPos.push_back(nPos);
+
+ try
+ {
+ if (nOrient == sheet::DataPilotFieldOrientation_DATA)
+ xDimProp->getPropertyValue(SC_UNO_DP_REFVALUE)
+ >>= rField.maFieldRef;
+ }
+ catch (uno::Exception&)
+ {
+ }
+ }
+ }
+
+ // sort by getPosition() value
+
+ size_t nOutCount = aFields.size();
+ if (nOutCount >= 1)
+ {
+ for (size_t i = 0; i < nOutCount - 1; ++i)
+ {
+ for (size_t j = 0; j + i < nOutCount - 1; ++j)
+ {
+ if ( aPos[j+1] < aPos[j] )
+ {
+ std::swap( aPos[j], aPos[j+1] );
+ std::swap( aFields[j], aFields[j+1] );
+ }
+ }
+ }
+ }
+
+ if (bAddData && !bDataFound)
+ aFields.emplace_back(PIVOT_DATA_FIELD);
+
+ rFields.swap(aFields);
+}
+
+void ScDPObject::FillOldParam(ScPivotParam& rParam) const
+{
+ const_cast<ScDPObject*>(this)->CreateObjects(); // xSource is needed for field numbers
+
+ if (!xSource.is())
+ return;
+
+ rParam.nCol = aOutRange.aStart.Col();
+ rParam.nRow = aOutRange.aStart.Row();
+ rParam.nTab = aOutRange.aStart.Tab();
+ // ppLabelArr / nLabels is not changed
+
+ bool bAddData = ( lcl_GetDataGetOrientation( xSource ) == sheet::DataPilotFieldOrientation_HIDDEN );
+ lcl_FillOldFields(
+ rParam.maPageFields, xSource, sheet::DataPilotFieldOrientation_PAGE, false);
+ lcl_FillOldFields(
+ rParam.maColFields, xSource, sheet::DataPilotFieldOrientation_COLUMN, bAddData);
+ lcl_FillOldFields(
+ rParam.maRowFields, xSource, sheet::DataPilotFieldOrientation_ROW, false);
+ lcl_FillOldFields(
+ rParam.maDataFields, xSource, sheet::DataPilotFieldOrientation_DATA, false);
+
+ uno::Reference<beans::XPropertySet> xProp( xSource, uno::UNO_QUERY );
+ if (!xProp.is())
+ return;
+
+ try
+ {
+ rParam.bMakeTotalCol = ScUnoHelpFunctions::GetBoolProperty( xProp,
+ SC_UNO_DP_COLGRAND, true );
+ rParam.bMakeTotalRow = ScUnoHelpFunctions::GetBoolProperty( xProp,
+ SC_UNO_DP_ROWGRAND, true );
+
+ // following properties may be missing for external sources
+ rParam.bIgnoreEmptyRows = ScUnoHelpFunctions::GetBoolProperty( xProp,
+ SC_UNO_DP_IGNOREEMPTY );
+ rParam.bDetectCategories = ScUnoHelpFunctions::GetBoolProperty( xProp,
+ SC_UNO_DP_REPEATEMPTY );
+ }
+ catch(uno::Exception&)
+ {
+ // no error
+ }
+}
+
+static void lcl_FillLabelData( ScDPLabelData& rData, const uno::Reference< beans::XPropertySet >& xDimProp )
+{
+ uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY );
+ if (!xDimProp.is() || !xDimSupp.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+ tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp, SC_UNO_DP_USEDHIERARCHY);
+ if ( nHierarchy >= xHiers->getCount() )
+ nHierarchy = 0;
+ rData.mnUsedHier = nHierarchy;
+
+ uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
+ uno::UNO_QUERY);
+ if (!xHierSupp.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xLevels =
+ new ScNameToIndexAccess( xHierSupp->getLevels() );
+
+ uno::Reference<beans::XPropertySet> xLevProp(xLevels->getByIndex(0), uno::UNO_QUERY);
+ if (!xLevProp.is())
+ return;
+
+ rData.mbShowAll = ScUnoHelpFunctions::GetBoolProperty(
+ xLevProp, SC_UNO_DP_SHOWEMPTY);
+
+ rData.mbRepeatItemLabels = ScUnoHelpFunctions::GetBoolProperty(
+ xLevProp, SC_UNO_DP_REPEATITEMLABELS);
+
+ try
+ {
+ xLevProp->getPropertyValue( SC_UNO_DP_SORTING )
+ >>= rData.maSortInfo;
+ xLevProp->getPropertyValue( SC_UNO_DP_LAYOUT )
+ >>= rData.maLayoutInfo;
+ xLevProp->getPropertyValue( SC_UNO_DP_AUTOSHOW )
+ >>= rData.maShowInfo;
+ }
+ catch(uno::Exception&)
+ {
+ }
+}
+
+void ScDPObject::FillLabelDataForDimension(
+ const uno::Reference<container::XIndexAccess>& xDims, sal_Int32 nDim, ScDPLabelData& rLabelData)
+{
+ uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
+ uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
+
+ if (!xDimName.is() || !xDimProp.is())
+ return;
+
+ bool bData = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, SC_UNO_DP_ISDATALAYOUT);
+ //TODO: error checking -- is "IsDataLayoutDimension" property required??
+
+ sal_Int32 nOrigPos = -1;
+ OUString aFieldName;
+ try
+ {
+ aFieldName = xDimName->getName();
+ uno::Any aOrigAny = xDimProp->getPropertyValue(SC_UNO_DP_ORIGINAL_POS);
+ aOrigAny >>= nOrigPos;
+ }
+ catch(uno::Exception&)
+ {
+ }
+
+ OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
+ xDimProp, SC_UNO_DP_LAYOUTNAME, OUString());
+
+ OUString aSubtotalName = ScUnoHelpFunctions::GetStringProperty(
+ xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, OUString());
+
+ // Name from the UNO dimension object may have trailing '*'s in which
+ // case it's a duplicate dimension. Convert that to a duplicate index.
+
+ sal_uInt8 nDupCount = ScDPUtil::getDuplicateIndex(aFieldName);
+ aFieldName = ScDPUtil::getSourceDimensionName(aFieldName);
+
+ rLabelData.maName = aFieldName;
+ rLabelData.mnCol = static_cast<SCCOL>(nDim);
+ rLabelData.mnDupCount = nDupCount;
+ rLabelData.mbDataLayout = bData;
+ rLabelData.mbIsValue = true; //TODO: check
+
+ if (bData)
+ return;
+
+ rLabelData.mnOriginalDim = static_cast<tools::Long>(nOrigPos);
+ rLabelData.maLayoutName = aLayoutName;
+ rLabelData.maSubtotalName = aSubtotalName;
+ if (nOrigPos >= 0)
+ // This is a duplicated dimension. Use the original dimension index.
+ nDim = nOrigPos;
+ GetHierarchies(nDim, rLabelData.maHiers);
+ GetMembers(nDim, GetUsedHierarchy(nDim), rLabelData.maMembers);
+ lcl_FillLabelData(rLabelData, xDimProp);
+ rLabelData.mnFlags = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp, SC_UNO_DP_FLAGS );
+}
+
+void ScDPObject::FillLabelData(sal_Int32 nDim, ScDPLabelData& rLabels)
+{
+ CreateObjects();
+ if (!xSource.is())
+ return;
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ sal_Int32 nDimCount = xDims->getCount();
+ if (nDimCount <= 0 || nDim >= nDimCount)
+ return;
+
+ FillLabelDataForDimension(xDims, nDim, rLabels);
+}
+
+void ScDPObject::FillLabelData(ScPivotParam& rParam)
+{
+ rParam.maLabelArray.clear();
+
+ CreateObjects();
+ if (!xSource.is())
+ return;
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ sal_Int32 nDimCount = xDims->getCount();
+ if (nDimCount <= 0)
+ return;
+
+ for (sal_Int32 nDim = 0; nDim < nDimCount; ++nDim)
+ {
+ ScDPLabelData* pNewLabel = new ScDPLabelData;
+ FillLabelDataForDimension(xDims, nDim, *pNewLabel);
+ rParam.maLabelArray.push_back(std::unique_ptr<ScDPLabelData>(pNewLabel));
+ }
+}
+
+void ScDPObject::GetFieldIdsNames(sheet::DataPilotFieldOrientation nOrient, std::vector<tools::Long>& rIndices,
+ std::vector<OUString>& rNames)
+{
+ CreateObjects();
+ if (!xSource.is())
+ return;
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim = 0; nDim < nDimCount; ++nDim)
+ {
+ uno::Reference<uno::XInterface> xIntDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xDimName(xIntDim, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp(xIntDim, uno::UNO_QUERY);
+
+ sheet::DataPilotFieldOrientation nDimOrient = ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+
+ if ( xDimProp.is() && nDimOrient == nOrient)
+ {
+ rIndices.push_back(nDim);
+ rNames.push_back(xDimName->getName());
+ }
+ }
+}
+
+bool ScDPObject::GetHierarchiesNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xHiers )
+{
+ bool bRet = false;
+ uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
+ uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
+ if( xIntDims.is() )
+ {
+ uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
+ if (xHierSup.is())
+ {
+ xHiers.set( xHierSup->getHierarchies() );
+ bRet = xHiers.is();
+ }
+ }
+ return bRet;
+}
+
+void ScDPObject::GetHierarchies( sal_Int32 nDim, uno::Sequence< OUString >& rHiers )
+{
+ uno::Reference< container::XNameAccess > xHiersNA;
+ if( GetHierarchiesNA( nDim, xHiersNA ) )
+ {
+ rHiers = xHiersNA->getElementNames();
+ }
+}
+
+sal_Int32 ScDPObject::GetUsedHierarchy( sal_Int32 nDim )
+{
+ sal_Int32 nHier = 0;
+ uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
+ uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
+ uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
+ if (xDim.is())
+ nHier = ScUnoHelpFunctions::GetLongProperty( xDim, SC_UNO_DP_USEDHIERARCHY );
+ return nHier;
+}
+
+bool ScDPObject::GetMembersNA( sal_Int32 nDim, uno::Reference< sheet::XMembersAccess >& xMembers )
+{
+ return GetMembersNA( nDim, GetUsedHierarchy( nDim ), xMembers );
+}
+
+bool ScDPObject::GetMembersNA( sal_Int32 nDim, sal_Int32 nHier, uno::Reference< sheet::XMembersAccess >& xMembers )
+{
+ bool bRet = false;
+ uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
+ uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
+ uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
+ if (xDim.is())
+ {
+ uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xDim, uno::UNO_QUERY);
+ if (xHierSup.is())
+ {
+ uno::Reference<container::XIndexAccess> xHiers(new ScNameToIndexAccess(xHierSup->getHierarchies()));
+ uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHiers->getByIndex(nHier), uno::UNO_QUERY );
+ if ( xLevSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xLevels(new ScNameToIndexAccess( xLevSupp->getLevels()));
+ if (xLevels.is())
+ {
+ sal_Int32 nLevCount = xLevels->getCount();
+ if (nLevCount > 0)
+ {
+ uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevels->getByIndex(0), uno::UNO_QUERY );
+ if ( xMembSupp.is() )
+ {
+ xMembers.set(xMembSupp->getMembers());
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+// convert old pivot tables into new datapilot tables
+
+namespace {
+
+OUString lcl_GetDimName( const uno::Reference<sheet::XDimensionsSupplier>& xSource, tools::Long nDim )
+{
+ OUString aName;
+ if ( xSource.is() )
+ {
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nDimCount = xDims->getCount();
+ if ( nDim < nDimCount )
+ {
+ uno::Reference<container::XNamed> xDimName(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ if (xDimName.is())
+ {
+ try
+ {
+ aName = xDimName->getName();
+ }
+ catch(uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ return aName;
+}
+
+bool hasFieldColumn(const vector<ScPivotField>* pRefFields, SCCOL nCol)
+{
+ if (!pRefFields)
+ return false;
+
+ return std::any_of(pRefFields->begin(), pRefFields->end(),
+ [&nCol](const ScPivotField& rField) {
+ // This array of fields contains the specified column.
+ return rField.nCol == nCol; });
+}
+
+class FindByOriginalDim
+{
+ tools::Long mnDim;
+public:
+ explicit FindByOriginalDim(tools::Long nDim) : mnDim(nDim) {}
+ bool operator() (const ScPivotField& r) const
+ {
+ return mnDim == r.getOriginalDim();
+ }
+};
+
+}
+
+void ScDPObject::ConvertOrientation(
+ ScDPSaveData& rSaveData, const ScPivotFieldVector& rFields, sheet::DataPilotFieldOrientation nOrient,
+ const Reference<XDimensionsSupplier>& xSource,
+ const ScDPLabelDataVector& rLabels,
+ const ScPivotFieldVector* pRefColFields,
+ const ScPivotFieldVector* pRefRowFields,
+ const ScPivotFieldVector* pRefPageFields )
+{
+ ScPivotFieldVector::const_iterator itr, itrBeg = rFields.begin(), itrEnd = rFields.end();
+ for (itr = itrBeg; itr != itrEnd; ++itr)
+ {
+ const ScPivotField& rField = *itr;
+
+ tools::Long nCol = rField.getOriginalDim();
+ PivotFunc nFuncs = rField.nFuncMask;
+ const sheet::DataPilotFieldReference& rFieldRef = rField.maFieldRef;
+
+ ScDPSaveDimension* pDim = nullptr;
+ if ( nCol == PIVOT_DATA_FIELD )
+ pDim = rSaveData.GetDataLayoutDimension();
+ else
+ {
+ OUString aDocStr = lcl_GetDimName( xSource, nCol ); // cols must start at 0
+ if (!aDocStr.isEmpty())
+ pDim = rSaveData.GetDimensionByName(aDocStr);
+ else
+ pDim = nullptr;
+ }
+
+ if (!pDim)
+ continue;
+
+ if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) // set summary function
+ {
+ // generate an individual entry for each function
+ bool bFirst = true;
+
+ // if a dimension is used for column/row/page and data,
+ // use duplicated dimensions for all data occurrences
+ if (hasFieldColumn(pRefColFields, nCol))
+ bFirst = false;
+
+ if (bFirst && hasFieldColumn(pRefRowFields, nCol))
+ bFirst = false;
+
+ if (bFirst && hasFieldColumn(pRefPageFields, nCol))
+ bFirst = false;
+
+ if (bFirst)
+ {
+ // if set via api, a data column may occur several times
+ // (if the function hasn't been changed yet) -> also look for duplicate data column
+ bFirst = std::none_of(itrBeg, itr, FindByOriginalDim(nCol));
+ }
+
+ ScGeneralFunction eFunc = ScDataPilotConversion::FirstFunc(rField.nFuncMask);
+ if (!bFirst)
+ pDim = rSaveData.DuplicateDimension(pDim->GetName());
+ pDim->SetOrientation(nOrient);
+ pDim->SetFunction(eFunc);
+
+ if( rFieldRef.ReferenceType == sheet::DataPilotFieldReferenceType::NONE )
+ pDim->SetReferenceValue(nullptr);
+ else
+ pDim->SetReferenceValue(&rFieldRef);
+ }
+ else // set SubTotals
+ {
+ pDim->SetOrientation( nOrient );
+
+ std::vector<ScGeneralFunction> nSubTotalFuncs;
+ nSubTotalFuncs.reserve(16);
+ sal_uInt16 nMask = 1;
+ for (sal_uInt16 nBit=0; nBit<16; nBit++)
+ {
+ if ( nFuncs & static_cast<PivotFunc>(nMask) )
+ nSubTotalFuncs.push_back( ScDataPilotConversion::FirstFunc( static_cast<PivotFunc>(nMask) ) );
+ nMask *= 2;
+ }
+ pDim->SetSubTotals( std::move(nSubTotalFuncs) );
+
+ // ShowEmpty was implicit in old tables,
+ // must be set for data layout dimension (not accessible in dialog)
+ if ( nCol == PIVOT_DATA_FIELD )
+ pDim->SetShowEmpty( true );
+ }
+
+ size_t nDimIndex = rField.nCol;
+ pDim->RemoveLayoutName();
+ pDim->RemoveSubtotalName();
+ if (nDimIndex < rLabels.size())
+ {
+ const ScDPLabelData& rLabel = *rLabels[nDimIndex];
+ if (!rLabel.maLayoutName.isEmpty())
+ pDim->SetLayoutName(rLabel.maLayoutName);
+ if (!rLabel.maSubtotalName.isEmpty())
+ pDim->SetSubtotalName(rLabel.maSubtotalName);
+ }
+ }
+}
+
+bool ScDPObject::IsOrientationAllowed( sheet::DataPilotFieldOrientation nOrient, sal_Int32 nDimFlags )
+{
+ bool bAllowed = true;
+ switch (nOrient)
+ {
+ case sheet::DataPilotFieldOrientation_PAGE:
+ bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_PAGE_ORIENTATION ) == 0;
+ break;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_COLUMN_ORIENTATION ) == 0;
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_ROW_ORIENTATION ) == 0;
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_DATA_ORIENTATION ) == 0;
+ break;
+ default:
+ {
+ // allowed to remove from previous orientation
+ }
+ }
+ return bAllowed;
+}
+
+bool ScDPObject::HasRegisteredSources()
+{
+ bool bFound = false;
+
+ uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
+ uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
+ if ( xEnAc.is() )
+ {
+ uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
+ SCDPSOURCE_SERVICE );
+ if ( xEnum.is() && xEnum->hasMoreElements() )
+ bFound = true;
+ }
+
+ return bFound;
+}
+
+std::vector<OUString> ScDPObject::GetRegisteredSources()
+{
+ std::vector<OUString> aVec;
+
+ // use implementation names...
+
+ uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
+ uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
+ if ( xEnAc.is() )
+ {
+ uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
+ SCDPSOURCE_SERVICE );
+ if ( xEnum.is() )
+ {
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Any aAddInAny = xEnum->nextElement();
+// if ( aAddInAny.getReflection()->getTypeClass() == TypeClass_INTERFACE )
+ {
+ uno::Reference<uno::XInterface> xIntFac;
+ aAddInAny >>= xIntFac;
+ if ( xIntFac.is() )
+ {
+ uno::Reference<lang::XServiceInfo> xInfo( xIntFac, uno::UNO_QUERY );
+ if ( xInfo.is() )
+ {
+ OUString sName = xInfo->getImplementationName();
+ aVec.push_back( sName );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return aVec;
+}
+
+uno::Reference<sheet::XDimensionsSupplier> ScDPObject::CreateSource( const ScDPServiceDesc& rDesc )
+{
+ OUString aImplName = rDesc.aServiceName;
+ uno::Reference<sheet::XDimensionsSupplier> xRet;
+
+ uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
+ uno::Reference<container::XContentEnumerationAccess> xEnAc(xManager, uno::UNO_QUERY);
+ if (!xEnAc.is())
+ return xRet;
+
+ uno::Reference<container::XEnumeration> xEnum =
+ xEnAc->createContentEnumeration(SCDPSOURCE_SERVICE);
+ if (!xEnum.is())
+ return xRet;
+
+ while (xEnum->hasMoreElements() && !xRet.is())
+ {
+ uno::Any aAddInAny = xEnum->nextElement();
+ uno::Reference<uno::XInterface> xIntFac;
+ aAddInAny >>= xIntFac;
+ if (!xIntFac.is())
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xInfo(xIntFac, uno::UNO_QUERY);
+ if (!xInfo.is() || xInfo->getImplementationName() != aImplName)
+ continue;
+
+ try
+ {
+ // #i113160# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
+ // passing the context to the component (see ScUnoAddInCollection::Initialize)
+
+ uno::Reference<uno::XInterface> xInterface;
+ uno::Reference<uno::XComponentContext> xCtx(
+ comphelper::getComponentContext(xManager));
+ uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
+ if (xCFac.is())
+ xInterface = xCFac->createInstanceWithContext(xCtx);
+
+ if (!xInterface.is())
+ {
+ uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
+ if ( xFac.is() )
+ xInterface = xFac->createInstance();
+ }
+
+ uno::Reference<lang::XInitialization> xInit( xInterface, uno::UNO_QUERY );
+ if (xInit.is())
+ {
+ // initialize
+ uno::Sequence<uno::Any> aSeq(4);
+ uno::Any* pArray = aSeq.getArray();
+ pArray[0] <<= rDesc.aParSource;
+ pArray[1] <<= rDesc.aParName;
+ pArray[2] <<= rDesc.aParUser;
+ pArray[3] <<= rDesc.aParPass;
+ xInit->initialize( aSeq );
+ }
+ xRet.set( xInterface, uno::UNO_QUERY );
+ }
+ catch(uno::Exception&)
+ {
+ }
+ }
+
+ return xRet;
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPObject::Dump() const
+{
+ if (pSaveData)
+ pSaveData->Dump();
+
+ if (mpTableData)
+ mpTableData->Dump();
+}
+
+void ScDPObject::DumpCache() const
+{
+ if (!mpTableData)
+ return;
+
+ const ScDPCache &rCache = mpTableData->GetCacheTable().getCache();
+
+ rCache.Dump();
+}
+#endif
+
+ScDPCollection::SheetCaches::SheetCaches(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+namespace {
+
+struct FindInvalidRange
+{
+ bool operator() (const ScRange& r) const
+ {
+ return !r.IsValid();
+ }
+};
+
+void setGroupItemsToCache( ScDPCache& rCache, const o3tl::sorted_vector<ScDPObject*>& rRefs )
+{
+ // Go through all referencing pivot tables, and re-fill the group dimension info.
+ for (const ScDPObject* pObj : rRefs)
+ {
+ const ScDPSaveData* pSave = pObj->GetSaveData();
+ if (!pSave)
+ continue;
+
+ const ScDPDimensionSaveData* pGroupDims = pSave->GetExistingDimensionData();
+ if (!pGroupDims)
+ continue;
+
+ pGroupDims->WriteToCache(rCache);
+ }
+}
+
+}
+
+bool ScDPCollection::SheetCaches::hasCache(const ScRange& rRange) const
+{
+ RangeIndexType::const_iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
+ if (it == maRanges.end())
+ return false;
+
+ // Already cached.
+ size_t nIndex = std::distance(maRanges.begin(), it);
+ CachesType::const_iterator const itCache = m_Caches.find(nIndex);
+ return itCache != m_Caches.end();
+}
+
+const ScDPCache* ScDPCollection::SheetCaches::getCache(const ScRange& rRange, const ScDPDimensionSaveData* pDimData)
+{
+ RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
+ if (it != maRanges.end())
+ {
+ // Already cached.
+ size_t nIndex = std::distance(maRanges.begin(), it);
+ CachesType::iterator const itCache = m_Caches.find(nIndex);
+ if (itCache == m_Caches.end())
+ {
+ OSL_FAIL("Cache pool and index pool out-of-sync !!!");
+ return nullptr;
+ }
+
+ if (pDimData)
+ {
+ (itCache->second)->ClearGroupFields();
+ pDimData->WriteToCache(*itCache->second);
+ }
+
+ return itCache->second.get();
+ }
+
+ // Not cached. Create a new cache.
+ ::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
+ pCache->InitFromDoc(mrDoc, rRange);
+ if (pDimData)
+ pDimData->WriteToCache(*pCache);
+
+ // Get the smallest available range index.
+ it = std::find_if(maRanges.begin(), maRanges.end(), FindInvalidRange());
+
+ size_t nIndex = maRanges.size();
+ if (it == maRanges.end())
+ {
+ // All range indices are valid. Append a new index.
+ maRanges.push_back(rRange);
+ }
+ else
+ {
+ // Slot with invalid range. Re-use this slot.
+ *it = rRange;
+ nIndex = std::distance(maRanges.begin(), it);
+ }
+
+ const ScDPCache* p = pCache.get();
+ m_Caches.insert(std::make_pair(nIndex, std::move(pCache)));
+ return p;
+}
+
+ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange)
+{
+ RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
+ if (it == maRanges.end())
+ // Not cached.
+ return nullptr;
+
+ // Already cached.
+ size_t nIndex = std::distance(maRanges.begin(), it);
+ CachesType::iterator const itCache = m_Caches.find(nIndex);
+ if (itCache == m_Caches.end())
+ {
+ OSL_FAIL("Cache pool and index pool out-of-sync !!!");
+ return nullptr;
+ }
+
+ return itCache->second.get();
+}
+
+const ScDPCache* ScDPCollection::SheetCaches::getExistingCache(const ScRange& rRange) const
+{
+ RangeIndexType::const_iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
+ if (it == maRanges.end())
+ // Not cached.
+ return nullptr;
+
+ // Already cached.
+ size_t nIndex = std::distance(maRanges.begin(), it);
+ CachesType::const_iterator const itCache = m_Caches.find(nIndex);
+ if (itCache == m_Caches.end())
+ {
+ OSL_FAIL("Cache pool and index pool out-of-sync !!!");
+ return nullptr;
+ }
+
+ return itCache->second.get();
+}
+
+size_t ScDPCollection::SheetCaches::size() const
+{
+ return m_Caches.size();
+}
+
+void ScDPCollection::SheetCaches::updateReference(
+ UpdateRefMode eMode, const ScRange& r, SCCOL nDx, SCROW nDy, SCTAB nDz)
+{
+ if (maRanges.empty())
+ // No caches.
+ return;
+
+ for (ScRange& rKeyRange : maRanges)
+ {
+ SCCOL nCol1 = rKeyRange.aStart.Col();
+ SCROW nRow1 = rKeyRange.aStart.Row();
+ SCTAB nTab1 = rKeyRange.aStart.Tab();
+ SCCOL nCol2 = rKeyRange.aEnd.Col();
+ SCROW nRow2 = rKeyRange.aEnd.Row();
+ SCTAB nTab2 = rKeyRange.aEnd.Tab();
+
+ ScRefUpdateRes eRes = ScRefUpdate::Update(
+ &mrDoc, eMode,
+ r.aStart.Col(), r.aStart.Row(), r.aStart.Tab(),
+ r.aEnd.Col(), r.aEnd.Row(), r.aEnd.Tab(), nDx, nDy, nDz,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+
+ if (eRes != UR_NOTHING)
+ {
+ // range updated.
+ ScRange aNew(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ rKeyRange = aNew;
+ }
+ }
+}
+
+void ScDPCollection::SheetCaches::updateCache(const ScRange& rRange, o3tl::sorted_vector<ScDPObject*>& rRefs)
+{
+ RangeIndexType::iterator it = std::find(maRanges.begin(), maRanges.end(), rRange);
+ if (it == maRanges.end())
+ {
+ // Not cached. Nothing to do.
+ rRefs.clear();
+ return;
+ }
+
+ size_t nIndex = std::distance(maRanges.begin(), it);
+ CachesType::iterator const itCache = m_Caches.find(nIndex);
+ if (itCache == m_Caches.end())
+ {
+ OSL_FAIL("Cache pool and index pool out-of-sync !!!");
+ rRefs.clear();
+ return;
+ }
+
+ ScDPCache& rCache = *itCache->second;
+
+ // Update the cache with new cell values. This will clear all group dimension info.
+ rCache.InitFromDoc(mrDoc, rRange);
+
+ o3tl::sorted_vector<ScDPObject*> aRefs(rCache.GetAllReferences());
+ rRefs.swap(aRefs);
+
+ // Make sure to re-populate the group dimension info.
+ setGroupItemsToCache(rCache, rRefs);
+}
+
+bool ScDPCollection::SheetCaches::remove(const ScDPCache* p)
+{
+ CachesType::iterator it = std::find_if(m_Caches.begin(), m_Caches.end(),
+ [&p](const CachesType::value_type& rEntry) { return rEntry.second.get() == p; });
+ if (it != m_Caches.end())
+ {
+ size_t idx = it->first;
+ m_Caches.erase(it);
+ maRanges[idx].SetInvalid();
+ return true;
+ }
+ return false;
+}
+
+const std::vector<ScRange>& ScDPCollection::SheetCaches::getAllRanges() const
+{
+ return maRanges;
+}
+
+ScDPCollection::NameCaches::NameCaches(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+bool ScDPCollection::NameCaches::hasCache(const OUString& rName) const
+{
+ return m_Caches.count(rName) != 0;
+}
+
+const ScDPCache* ScDPCollection::NameCaches::getCache(
+ const OUString& rName, const ScRange& rRange, const ScDPDimensionSaveData* pDimData)
+{
+ CachesType::const_iterator const itr = m_Caches.find(rName);
+ if (itr != m_Caches.end())
+ // already cached.
+ return itr->second.get();
+
+ ::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
+ pCache->InitFromDoc(mrDoc, rRange);
+ if (pDimData)
+ pDimData->WriteToCache(*pCache);
+
+ const ScDPCache *const p = pCache.get();
+ m_Caches.insert(std::make_pair(rName, std::move(pCache)));
+ return p;
+}
+
+ScDPCache* ScDPCollection::NameCaches::getExistingCache(const OUString& rName)
+{
+ CachesType::iterator const itr = m_Caches.find(rName);
+ return itr != m_Caches.end() ? itr->second.get() : nullptr;
+}
+
+size_t ScDPCollection::NameCaches::size() const
+{
+ return m_Caches.size();
+}
+
+void ScDPCollection::NameCaches::updateCache(
+ const OUString& rName, const ScRange& rRange, o3tl::sorted_vector<ScDPObject*>& rRefs)
+{
+ CachesType::iterator const itr = m_Caches.find(rName);
+ if (itr == m_Caches.end())
+ {
+ rRefs.clear();
+ return;
+ }
+
+ ScDPCache& rCache = *itr->second;
+ // Update the cache with new cell values. This will clear all group dimension info.
+ rCache.InitFromDoc(mrDoc, rRange);
+
+ o3tl::sorted_vector<ScDPObject*> aRefs(rCache.GetAllReferences());
+ rRefs.swap(aRefs);
+
+ // Make sure to re-populate the group dimension info.
+ setGroupItemsToCache(rCache, rRefs);
+}
+
+bool ScDPCollection::NameCaches::remove(const ScDPCache* p)
+{
+ CachesType::iterator it = std::find_if(m_Caches.begin(), m_Caches.end(),
+ [&p](const CachesType::value_type& rEntry) { return rEntry.second.get() == p; });
+ if (it != m_Caches.end())
+ {
+ m_Caches.erase(it);
+ return true;
+ }
+ return false;
+}
+
+ScDPCollection::DBType::DBType(sal_Int32 nSdbType, OUString aDBName, OUString aCommand) :
+ mnSdbType(nSdbType), maDBName(std::move(aDBName)), maCommand(std::move(aCommand)) {}
+
+bool ScDPCollection::DBType::less::operator() (const DBType& left, const DBType& right) const
+{
+ return left < right;
+}
+
+ScDPCollection::DBCaches::DBCaches(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+bool ScDPCollection::DBCaches::hasCache(sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand) const
+{
+ DBType aType(nSdbType, rDBName, rCommand);
+ CachesType::const_iterator const itr = m_Caches.find(aType);
+ return itr != m_Caches.end();
+}
+
+const ScDPCache* ScDPCollection::DBCaches::getCache(
+ sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand,
+ const ScDPDimensionSaveData* pDimData)
+{
+ DBType aType(nSdbType, rDBName, rCommand);
+ CachesType::const_iterator const itr = m_Caches.find(aType);
+ if (itr != m_Caches.end())
+ // already cached.
+ return itr->second.get();
+
+ uno::Reference<sdbc::XRowSet> xRowSet = createRowSet(nSdbType, rDBName, rCommand);
+ if (!xRowSet.is())
+ return nullptr;
+
+ ::std::unique_ptr<ScDPCache> pCache(new ScDPCache(mrDoc));
+ SvNumberFormatter aFormat( comphelper::getProcessComponentContext(), ScGlobal::eLnge);
+ DBConnector aDB(*pCache, xRowSet, aFormat.GetNullDate());
+ if (!aDB.isValid())
+ return nullptr;
+
+ if (!pCache->InitFromDataBase(aDB))
+ {
+ // initialization failed.
+ comphelper::disposeComponent(xRowSet);
+ return nullptr;
+ }
+
+ if (pDimData)
+ pDimData->WriteToCache(*pCache);
+
+ ::comphelper::disposeComponent(xRowSet);
+ const ScDPCache* p = pCache.get();
+ m_Caches.insert(std::make_pair(aType, std::move(pCache)));
+ return p;
+}
+
+ScDPCache* ScDPCollection::DBCaches::getExistingCache(
+ sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand)
+{
+ DBType aType(nSdbType, rDBName, rCommand);
+ CachesType::iterator const itr = m_Caches.find(aType);
+ return itr != m_Caches.end() ? itr->second.get() : nullptr;
+}
+
+uno::Reference<sdbc::XRowSet> ScDPCollection::DBCaches::createRowSet(
+ sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand)
+{
+ uno::Reference<sdbc::XRowSet> xRowSet;
+ try
+ {
+ xRowSet.set(comphelper::getProcessServiceFactory()->createInstance(
+ SC_SERVICE_ROWSET),
+ UNO_QUERY);
+
+ uno::Reference<beans::XPropertySet> xRowProp(xRowSet, UNO_QUERY);
+ OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
+ if (!xRowProp.is())
+ {
+ xRowSet.clear();
+ return xRowSet;
+ }
+
+ // set source parameters
+
+ xRowProp->setPropertyValue( SC_DBPROP_DATASOURCENAME, Any(rDBName) );
+ xRowProp->setPropertyValue( SC_DBPROP_COMMAND, Any(rCommand) );
+ xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, Any(nSdbType) );
+
+ uno::Reference<sdb::XCompletedExecution> xExecute( xRowSet, uno::UNO_QUERY );
+ if ( xExecute.is() )
+ {
+ uno::Reference<task::XInteractionHandler> xHandler(
+ task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr),
+ uno::UNO_QUERY_THROW);
+ xExecute->executeWithCompletion( xHandler );
+ }
+ else
+ xRowSet->execute();
+
+ return xRowSet;
+ }
+ catch ( const sdbc::SQLException& rError )
+ {
+ //! store error message
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ rError.Message));
+ xInfoBox->run();
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
+ }
+
+ xRowSet.clear();
+ return xRowSet;
+}
+
+void ScDPCollection::DBCaches::updateCache(
+ sal_Int32 nSdbType, const OUString& rDBName, const OUString& rCommand,
+ o3tl::sorted_vector<ScDPObject*>& rRefs)
+{
+ DBType aType(nSdbType, rDBName, rCommand);
+ CachesType::iterator const it = m_Caches.find(aType);
+ if (it == m_Caches.end())
+ {
+ // not cached.
+ rRefs.clear();
+ return;
+ }
+
+ ScDPCache& rCache = *it->second;
+
+ uno::Reference<sdbc::XRowSet> xRowSet = createRowSet(nSdbType, rDBName, rCommand);
+ if (!xRowSet.is())
+ {
+ rRefs.clear();
+ return;
+ }
+
+ SvNumberFormatter aFormat( comphelper::getProcessComponentContext(), ScGlobal::eLnge);
+ DBConnector aDB(rCache, xRowSet, aFormat.GetNullDate());
+ if (!aDB.isValid())
+ return;
+
+ if (!rCache.InitFromDataBase(aDB))
+ {
+ // initialization failed.
+ rRefs.clear();
+ comphelper::disposeComponent(xRowSet);
+ return;
+ }
+
+ comphelper::disposeComponent(xRowSet);
+ o3tl::sorted_vector<ScDPObject*> aRefs(rCache.GetAllReferences());
+ aRefs.swap(rRefs);
+
+ // Make sure to re-populate the group dimension info.
+ setGroupItemsToCache(rCache, rRefs);
+}
+
+bool ScDPCollection::DBCaches::remove(const ScDPCache* p)
+{
+ CachesType::iterator it = std::find_if(m_Caches.begin(), m_Caches.end(),
+ [&p](const CachesType::value_type& rEntry) { return rEntry.second.get() == p; });
+ if (it != m_Caches.end())
+ {
+ m_Caches.erase(it);
+ return true;
+ }
+ return false;
+}
+
+ScDPCollection::ScDPCollection(ScDocument& rDocument) :
+ mrDoc(rDocument),
+ maSheetCaches(rDocument),
+ maNameCaches(rDocument),
+ maDBCaches(rDocument)
+{
+}
+
+ScDPCollection::ScDPCollection(const ScDPCollection& r) :
+ mrDoc(r.mrDoc),
+ maSheetCaches(r.mrDoc),
+ maNameCaches(r.mrDoc),
+ maDBCaches(r.mrDoc)
+{
+}
+
+ScDPCollection::~ScDPCollection()
+{
+ maTables.clear();
+}
+
+namespace {
+
+/**
+ * Unary predicate to match DP objects by the table ID.
+ */
+class MatchByTable
+{
+ SCTAB mnTab;
+public:
+ explicit MatchByTable(SCTAB nTab) : mnTab(nTab) {}
+
+ bool operator() (const std::unique_ptr<ScDPObject>& rObj) const
+ {
+ return rObj->GetOutRange().aStart.Tab() == mnTab;
+ }
+};
+
+}
+
+TranslateId ScDPCollection::ReloadCache(const ScDPObject* pDPObj, o3tl::sorted_vector<ScDPObject*>& rRefs)
+{
+ if (!pDPObj)
+ return STR_ERR_DATAPILOTSOURCE;
+
+ if (pDPObj->IsSheetData())
+ {
+ // data source is internal sheet.
+ const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
+ if (!pDesc)
+ return STR_ERR_DATAPILOTSOURCE;
+
+ TranslateId pErrId = pDesc->CheckSourceRange();
+ if (pErrId)
+ return pErrId;
+
+ if (pDesc->HasRangeName())
+ {
+ // cache by named range
+ ScDPCollection::NameCaches& rCaches = GetNameCaches();
+ if (rCaches.hasCache(pDesc->GetRangeName()))
+ rCaches.updateCache(pDesc->GetRangeName(), pDesc->GetSourceRange(), rRefs);
+ else
+ {
+ // Not cached yet. Collect all tables that use this named
+ // range as data source.
+ GetAllTables(pDesc->GetRangeName(), rRefs);
+ }
+ }
+ else
+ {
+ // cache by cell range
+ ScDPCollection::SheetCaches& rCaches = GetSheetCaches();
+ if (rCaches.hasCache(pDesc->GetSourceRange()))
+ rCaches.updateCache(pDesc->GetSourceRange(), rRefs);
+ else
+ {
+ // Not cached yet. Collect all tables that use this range as
+ // data source.
+ GetAllTables(pDesc->GetSourceRange(), rRefs);
+ }
+ }
+ }
+ else if (pDPObj->IsImportData())
+ {
+ // data source is external database.
+ const ScImportSourceDesc* pDesc = pDPObj->GetImportSourceDesc();
+ if (!pDesc)
+ return STR_ERR_DATAPILOTSOURCE;
+
+ ScDPCollection::DBCaches& rCaches = GetDBCaches();
+ if (rCaches.hasCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject))
+ rCaches.updateCache(
+ pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
+ else
+ {
+ // Not cached yet. Collect all tables that use this range as
+ // data source.
+ GetAllTables(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
+ }
+ }
+ return {};
+}
+
+bool ScDPCollection::ReloadGroupsInCache(const ScDPObject* pDPObj, o3tl::sorted_vector<ScDPObject*>& rRefs)
+{
+ if (!pDPObj)
+ return false;
+
+ const ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if (!pSaveData)
+ return false;
+
+ // Note: Unlike reloading cache, when modifying the group dimensions the
+ // cache may not have all its references when this method is called.
+ // Therefore, we need to always call GetAllTables to get its correct
+ // references even when the cache exists. This may become a non-issue
+ // if/when we implement loading and saving of pivot caches.
+
+ ScDPCache* pCache = nullptr;
+
+ if (pDPObj->IsSheetData())
+ {
+ // data source is internal sheet.
+ const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
+ if (!pDesc)
+ return false;
+
+ if (pDesc->HasRangeName())
+ {
+ // cache by named range
+ ScDPCollection::NameCaches& rCaches = GetNameCaches();
+ if (rCaches.hasCache(pDesc->GetRangeName()))
+ pCache = rCaches.getExistingCache(pDesc->GetRangeName());
+ else
+ {
+ // Not cached yet. Cache the source dimensions. Groups will
+ // be added below.
+ pCache = const_cast<ScDPCache*>(
+ rCaches.getCache(pDesc->GetRangeName(), pDesc->GetSourceRange(), nullptr));
+ }
+ GetAllTables(pDesc->GetRangeName(), rRefs);
+ }
+ else
+ {
+ // cache by cell range
+ ScDPCollection::SheetCaches& rCaches = GetSheetCaches();
+ if (rCaches.hasCache(pDesc->GetSourceRange()))
+ pCache = rCaches.getExistingCache(pDesc->GetSourceRange());
+ else
+ {
+ // Not cached yet. Cache the source dimensions. Groups will
+ // be added below.
+ pCache = const_cast<ScDPCache*>(
+ rCaches.getCache(pDesc->GetSourceRange(), nullptr));
+ }
+ GetAllTables(pDesc->GetSourceRange(), rRefs);
+ }
+ }
+ else if (pDPObj->IsImportData())
+ {
+ // data source is external database.
+ const ScImportSourceDesc* pDesc = pDPObj->GetImportSourceDesc();
+ if (!pDesc)
+ return false;
+
+ ScDPCollection::DBCaches& rCaches = GetDBCaches();
+ if (rCaches.hasCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject))
+ pCache = rCaches.getExistingCache(
+ pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject);
+ else
+ {
+ // Not cached yet. Cache the source dimensions. Groups will
+ // be added below.
+ pCache = const_cast<ScDPCache*>(
+ rCaches.getCache(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, nullptr));
+ }
+ GetAllTables(pDesc->GetCommandType(), pDesc->aDBName, pDesc->aObject, rRefs);
+ }
+
+ if (!pCache)
+ return false;
+
+ // Clear the existing group/field data from the cache, and rebuild it from the
+ // dimension data.
+ pCache->ClearAllFields();
+ const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
+ if (pDimData)
+ pDimData->WriteToCache(*pCache);
+ return true;
+}
+
+bool ScDPCollection::GetReferenceGroups(const ScDPObject& rDPObj, const ScDPDimensionSaveData** pGroups) const
+{
+ for (const std::unique_ptr<ScDPObject>& aTable : maTables)
+ {
+ const ScDPObject& rRefObj = *aTable;
+
+ if (&rRefObj == &rDPObj)
+ continue;
+
+ if (rDPObj.IsSheetData()){
+ if(!rRefObj.IsSheetData())
+ continue;
+
+ const ScSheetSourceDesc* pDesc = rDPObj.GetSheetDesc();
+ const ScSheetSourceDesc* pRefDesc = rRefObj.GetSheetDesc();
+ if (pDesc == nullptr || pRefDesc == nullptr)
+ continue;
+
+ if (pDesc->HasRangeName())
+ {
+ if (!pRefDesc->HasRangeName())
+ continue;
+
+ if (pDesc->GetRangeName() == pRefDesc->GetRangeName())
+ {
+ *pGroups = rRefObj.GetSaveData()->GetExistingDimensionData();
+ return true;
+ }
+ }
+ else
+ {
+ if (pRefDesc->HasRangeName())
+ continue;
+
+ if (pDesc->GetSourceRange() == pRefDesc->GetSourceRange())
+ {
+ *pGroups = rRefObj.GetSaveData()->GetExistingDimensionData();
+ return true;
+ }
+ }
+ }
+ else if (rDPObj.IsImportData())
+ {
+ if (!rRefObj.IsImportData ())
+ continue;
+
+ const ScImportSourceDesc* pDesc = rDPObj.GetImportSourceDesc();
+ const ScImportSourceDesc* pRefDesc = rRefObj.GetImportSourceDesc();
+ if (pDesc == nullptr || pRefDesc == nullptr)
+ continue;
+
+ if (pDesc->aDBName == pRefDesc->aDBName &&
+ pDesc->aObject == pRefDesc->aObject &&
+ pDesc->GetCommandType() == pRefDesc->GetCommandType())
+ {
+ *pGroups = rRefObj.GetSaveData()->GetExistingDimensionData();
+ return true;
+ }
+
+ }
+ }
+ return false;
+}
+
+
+void ScDPCollection::DeleteOnTab( SCTAB nTab )
+{
+ std::erase_if(maTables, MatchByTable(nTab));
+}
+
+void ScDPCollection::UpdateReference( UpdateRefMode eUpdateRefMode,
+ const ScRange& r, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ for (auto& rxTable : maTables)
+ rxTable->UpdateReference(eUpdateRefMode, r, nDx, nDy, nDz);
+
+ // Update the source ranges of the caches.
+ maSheetCaches.updateReference(eUpdateRefMode, r, nDx, nDy, nDz);
+}
+
+void ScDPCollection::CopyToTab( SCTAB nOld, SCTAB nNew )
+{
+ TablesType aAdded;
+ for (const auto& rxTable : maTables)
+ {
+ const ScDPObject& rObj = *rxTable;
+ ScRange aOutRange = rObj.GetOutRange();
+ if (aOutRange.aStart.Tab() != nOld)
+ continue;
+
+ ScAddress& s = aOutRange.aStart;
+ ScAddress& e = aOutRange.aEnd;
+ s.SetTab(nNew);
+ e.SetTab(nNew);
+ ScDPObject* pNew = new ScDPObject(rObj);
+ pNew->SetOutRange(aOutRange);
+ mrDoc.ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
+ aAdded.push_back(std::unique_ptr<ScDPObject>(pNew));
+ }
+
+ std::move(aAdded.begin(), aAdded.end(), std::back_inserter(maTables));
+}
+
+bool ScDPCollection::RefsEqual( const ScDPCollection& r ) const
+{
+ return std::equal(maTables.begin(), maTables.end(), r.maTables.begin(), r.maTables.end(),
+ [](const TablesType::value_type& a, const TablesType::value_type& b) { return a->RefsEqual(*b); });
+}
+
+void ScDPCollection::WriteRefsTo( ScDPCollection& r ) const
+{
+ if ( maTables.size() == r.maTables.size() )
+ {
+ //TODO: assert equal names?
+ TablesType::iterator itr2 = r.maTables.begin();
+ for (const auto& rxTable : maTables)
+ {
+ rxTable->WriteRefsTo(**itr2);
+ ++itr2;
+ }
+ }
+ else
+ {
+ // #i8180# If data pilot tables were deleted with their sheet,
+ // this collection contains extra entries that must be restored.
+ // Matching objects are found by their names.
+ size_t nSrcSize = maTables.size();
+ size_t nDestSize = r.maTables.size();
+ OSL_ENSURE( nSrcSize >= nDestSize, "WriteRefsTo: missing entries in document" );
+ for (size_t nSrcPos = 0; nSrcPos < nSrcSize; ++nSrcPos)
+ {
+ const ScDPObject& rSrcObj = *maTables[nSrcPos];
+ const OUString& aName = rSrcObj.GetName();
+ bool bFound = false;
+ for (size_t nDestPos = 0; nDestPos < nDestSize && !bFound; ++nDestPos)
+ {
+ ScDPObject& rDestObj = *r.maTables[nDestPos];
+ if (rDestObj.GetName() == aName)
+ {
+ rSrcObj.WriteRefsTo(rDestObj); // found object, copy refs
+ bFound = true;
+ }
+ }
+
+ if (!bFound)
+ {
+ // none found, re-insert deleted object (see ScUndoDataPilot::Undo)
+ r.InsertNewTable(std::make_unique<ScDPObject>(rSrcObj));
+ }
+ }
+ OSL_ENSURE( maTables.size() == r.maTables.size(), "WriteRefsTo: couldn't restore all entries" );
+ }
+}
+
+size_t ScDPCollection::GetCount() const
+{
+ return maTables.size();
+}
+
+ScDPObject& ScDPCollection::operator [](size_t nIndex)
+{
+ return *maTables[nIndex];
+}
+
+const ScDPObject& ScDPCollection::operator [](size_t nIndex) const
+{
+ return *maTables[nIndex];
+}
+
+ScDPObject* ScDPCollection::GetByName(std::u16string_view rName) const
+{
+ for (std::unique_ptr<ScDPObject> const & pObject : maTables)
+ {
+ if (pObject->GetName() == rName)
+ return pObject.get();
+ }
+
+ return nullptr;
+}
+
+OUString ScDPCollection::CreateNewName() const
+{
+ size_t n = maTables.size();
+ for (size_t nAdd = 0; nAdd <= n; ++nAdd) // nCount+1 tries
+ {
+ OUString aNewName = "DataPilot" + OUString::number(1 + nAdd);
+ if (std::none_of(maTables.begin(), maTables.end(),
+ [&aNewName](const TablesType::value_type& rxObj) { return rxObj->GetName() == aNewName; }))
+ return aNewName; // found unused Name
+ }
+ return OUString(); // should not happen
+}
+
+void ScDPCollection::FreeTable(const ScDPObject* pDPObject)
+{
+ const ScRange& rOutRange = pDPObject->GetOutRange();
+ const ScAddress& s = rOutRange.aStart;
+ const ScAddress& e = rOutRange.aEnd;
+ mrDoc.RemoveFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
+
+ auto funcRemoveCondition = [pDPObject] (std::unique_ptr<ScDPObject> const & pCurrent)
+ {
+ return pCurrent.get() == pDPObject;
+ };
+
+ std::erase_if(maTables, funcRemoveCondition);
+}
+
+ScDPObject* ScDPCollection::InsertNewTable(std::unique_ptr<ScDPObject> pDPObj)
+{
+ const ScRange& rOutRange = pDPObj->GetOutRange();
+ const ScAddress& s = rOutRange.aStart;
+ const ScAddress& e = rOutRange.aEnd;
+ mrDoc.ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), ScMF::DpTable);
+
+ maTables.push_back(std::move(pDPObj));
+ return maTables.back().get();
+}
+
+bool ScDPCollection::HasTable(const ScDPObject* pDPObj) const
+{
+ for (const std::unique_ptr<ScDPObject>& aTable : maTables)
+ {
+ if (aTable.get() == pDPObj)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+ScDPCollection::SheetCaches& ScDPCollection::GetSheetCaches()
+{
+ return maSheetCaches;
+}
+
+const ScDPCollection::SheetCaches& ScDPCollection::GetSheetCaches() const
+{
+ return maSheetCaches;
+}
+
+ScDPCollection::NameCaches& ScDPCollection::GetNameCaches()
+{
+ return maNameCaches;
+}
+
+const ScDPCollection::NameCaches& ScDPCollection::GetNameCaches() const
+{
+ return maNameCaches;
+}
+
+ScDPCollection::DBCaches& ScDPCollection::GetDBCaches()
+{
+ return maDBCaches;
+}
+
+const ScDPCollection::DBCaches& ScDPCollection::GetDBCaches() const
+{
+ return maDBCaches;
+}
+
+ScRangeList ScDPCollection::GetAllTableRanges( SCTAB nTab ) const
+{
+ return std::for_each(maTables.begin(), maTables.end(), AccumulateOutputRanges(nTab)).getRanges();
+}
+
+bool ScDPCollection::IntersectsTableByColumns( SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCTAB nTab ) const
+{
+ return std::any_of(maTables.begin(), maTables.end(), FindIntersectingTableByColumns(nCol1, nCol2, nRow, nTab));
+}
+
+bool ScDPCollection::IntersectsTableByRows( SCCOL nCol, SCROW nRow1, SCROW nRow2, SCTAB nTab ) const
+{
+ return std::any_of(maTables.begin(), maTables.end(), FindIntersectingTableByRows(nCol, nRow1, nRow2, nTab));
+}
+
+bool ScDPCollection::HasTable( const ScRange& rRange ) const
+{
+ return std::any_of(maTables.begin(), maTables.end(), FindIntersectingTable(rRange));
+}
+
+#if DEBUG_PIVOT_TABLE
+
+namespace {
+
+struct DumpTable
+{
+ void operator() (const std::unique_ptr<ScDPObject>& rObj) const
+ {
+ cout << "-- '" << rObj->GetName() << "'" << endl;
+ ScDPSaveData* pSaveData = rObj->GetSaveData();
+ if (!pSaveData)
+ return;
+
+ pSaveData->Dump();
+
+ cout << endl; // blank line
+ }
+};
+
+}
+
+void ScDPCollection::DumpTables() const
+{
+ std::for_each(maTables.begin(), maTables.end(), DumpTable());
+}
+
+#endif
+
+void ScDPCollection::RemoveCache(const ScDPCache* pCache)
+{
+ if (maSheetCaches.remove(pCache))
+ // sheet cache removed.
+ return;
+
+ if (maNameCaches.remove(pCache))
+ // named range cache removed.
+ return;
+
+ if (maDBCaches.remove(pCache))
+ // database cache removed.
+ return;
+}
+
+void ScDPCollection::GetAllTables(const ScRange& rSrcRange, o3tl::sorted_vector<ScDPObject*>& rRefs) const
+{
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ for (const auto& rxTable : maTables)
+ {
+ const ScDPObject& rObj = *rxTable;
+ if (!rObj.IsSheetData())
+ // Source is not a sheet range.
+ continue;
+
+ const ScSheetSourceDesc* pDesc = rObj.GetSheetDesc();
+ if (!pDesc)
+ continue;
+
+ if (pDesc->HasRangeName())
+ // This table has a range name as its source.
+ continue;
+
+ if (pDesc->GetSourceRange() != rSrcRange)
+ // Different source range.
+ continue;
+
+ aRefs.insert(const_cast<ScDPObject*>(&rObj));
+ }
+
+ rRefs.swap(aRefs);
+}
+
+void ScDPCollection::GetAllTables(std::u16string_view rSrcName, o3tl::sorted_vector<ScDPObject*>& rRefs) const
+{
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ for (const auto& rxTable : maTables)
+ {
+ const ScDPObject& rObj = *rxTable;
+ if (!rObj.IsSheetData())
+ // Source is not a sheet range.
+ continue;
+
+ const ScSheetSourceDesc* pDesc = rObj.GetSheetDesc();
+ if (!pDesc)
+ continue;
+
+ if (!pDesc->HasRangeName())
+ // This table probably has a sheet range as its source.
+ continue;
+
+ if (pDesc->GetRangeName() != rSrcName)
+ // Different source name.
+ continue;
+
+ aRefs.insert(const_cast<ScDPObject*>(&rObj));
+ }
+
+ rRefs.swap(aRefs);
+}
+
+void ScDPCollection::GetAllTables(
+ sal_Int32 nSdbType, std::u16string_view rDBName, std::u16string_view rCommand,
+ o3tl::sorted_vector<ScDPObject*>& rRefs) const
+{
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ for (const auto& rxTable : maTables)
+ {
+ const ScDPObject& rObj = *rxTable;
+ if (!rObj.IsImportData())
+ // Source data is not a database.
+ continue;
+
+ const ScImportSourceDesc* pDesc = rObj.GetImportSourceDesc();
+ if (!pDesc)
+ continue;
+
+ if (pDesc->aDBName != rDBName || pDesc->aObject != rCommand || pDesc->GetCommandType() != nSdbType)
+ // Different database source.
+ continue;
+
+ aRefs.insert(const_cast<ScDPObject*>(&rObj));
+ }
+
+ rRefs.swap(aRefs);
+}
+
+bool operator<(const ScDPCollection::DBType& left, const ScDPCollection::DBType& right)
+{
+ if (left.mnSdbType != right.mnSdbType)
+ return left.mnSdbType < right.mnSdbType;
+
+ if (left.maDBName != right.maDBName)
+ return left.maDBName < right.maDBName;
+
+ return left.maCommand < right.maCommand;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpoutput.cxx b/sc/source/core/data/dpoutput.cxx
new file mode 100644
index 0000000000..c74f43d10b
--- /dev/null
+++ b/sc/source/core/data/dpoutput.cxx
@@ -0,0 +1,1961 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemset.hxx>
+
+#include <dpoutput.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <formula/errorcodes.hxx>
+#include <miscuno.hxx>
+#include <globstr.hrc>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <scresid.hxx>
+#include <unonames.hxx>
+#include <strings.hrc>
+#include <stringutil.hxx>
+#include <dputil.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
+#include <com/sun/star/sheet/DataPilotTableResultData.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/DataResultFlags.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <com/sun/star/sheet/MemberResult.hpp>
+#include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
+#include <com/sun/star/sheet/XDataPilotResults.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
+#include <com/sun/star/sheet/XLevelsSupplier.hpp>
+#include <com/sun/star/sheet/XMembersAccess.hpp>
+#include <com/sun/star/sheet/XMembersSupplier.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+
+#include <limits>
+#include <string_view>
+#include <utility>
+#include <vector>
+#include <iostream>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::sheet::DataPilotTablePositionData;
+using ::com::sun::star::sheet::DataPilotTableResultData;
+
+#define SC_DP_FRAME_INNER_BOLD 20
+#define SC_DP_FRAME_OUTER_BOLD 40
+
+#define SC_DP_FRAME_COLOR Color(0,0,0) //( 0x20, 0x40, 0x68 )
+
+struct ScDPOutLevelData
+{
+ tools::Long mnDim;
+ tools::Long mnHier;
+ tools::Long mnLevel;
+ tools::Long mnDimPos;
+ sal_uInt32 mnSrcNumFmt; /// Prevailing number format used in the source data.
+ uno::Sequence<sheet::MemberResult> maResult;
+ OUString maName; /// Name is the internal field name.
+ OUString maCaption; /// Caption is the name visible in the output table.
+ bool mbHasHiddenMember:1;
+ bool mbDataLayout:1;
+ bool mbPageDim:1;
+
+ ScDPOutLevelData(tools::Long nDim, tools::Long nHier, tools::Long nLevel, tools::Long nDimPos, sal_uInt32 nSrcNumFmt, const uno::Sequence<sheet::MemberResult> &aResult,
+ OUString aName, OUString aCaption, bool bHasHiddenMember, bool bDataLayout, bool bPageDim) :
+ mnDim(nDim), mnHier(nHier), mnLevel(nLevel), mnDimPos(nDimPos), mnSrcNumFmt(nSrcNumFmt), maResult(aResult),
+ maName(std::move(aName)), maCaption(std::move(aCaption)), mbHasHiddenMember(bHasHiddenMember), mbDataLayout(bDataLayout),
+ mbPageDim(bPageDim)
+ {
+ }
+
+ // bug (73840) in uno::Sequence - copy and then assign doesn't work!
+};
+
+
+
+namespace {
+ struct ScDPOutLevelDataComparator
+ {
+ bool operator()(const ScDPOutLevelData & rA, const ScDPOutLevelData & rB)
+ {
+ return rA.mnDimPos<rB.mnDimPos || ( rA.mnDimPos==rB.mnDimPos && rA.mnHier<rB.mnHier ) ||
+ ( rA.mnDimPos==rB.mnDimPos && rA.mnHier==rB.mnHier && rA.mnLevel<rB.mnLevel );
+ }
+ };
+
+
+class ScDPOutputImpl
+{
+ ScDocument* mpDoc;
+ sal_uInt16 mnTab;
+ ::std::vector< bool > mbNeedLineCols;
+ ::std::vector< SCCOL > mnCols;
+
+ ::std::vector< bool > mbNeedLineRows;
+ ::std::vector< SCROW > mnRows;
+
+ SCCOL mnTabStartCol;
+ SCROW mnTabStartRow;
+
+ SCCOL mnDataStartCol;
+ SCROW mnDataStartRow;
+ SCCOL mnTabEndCol;
+ SCROW mnTabEndRow;
+
+public:
+ ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
+ SCCOL nTabStartCol,
+ SCROW nTabStartRow,
+ SCCOL nDataStartCol,
+ SCROW nDataStartRow,
+ SCCOL nTabEndCol,
+ SCROW nTabEndRow );
+ bool AddRow( SCROW nRow );
+ bool AddCol( SCCOL nCol );
+
+ void OutputDataArea();
+ void OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori = false );
+
+};
+
+void ScDPOutputImpl::OutputDataArea()
+{
+ AddRow( mnDataStartRow );
+ AddCol( mnDataStartCol );
+
+ mnCols.push_back( mnTabEndCol+1); //set last row bottom
+ mnRows.push_back( mnTabEndRow+1); //set last col bottom
+
+ bool bAllRows = ( ( mnTabEndRow - mnDataStartRow + 2 ) == static_cast<SCROW>(mnRows.size()) );
+
+ std::sort( mnCols.begin(), mnCols.end());
+ std::sort( mnRows.begin(), mnRows.end());
+
+ for( SCCOL nCol = 0; nCol < static_cast<SCCOL>(mnCols.size())-1; nCol ++ )
+ {
+ if ( !bAllRows )
+ {
+ if ( nCol < static_cast<SCCOL>(mnCols.size())-2)
+ {
+ for ( SCROW i = nCol%2; i < static_cast<SCROW>(mnRows.size())-2; i +=2 )
+ OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
+ if ( mnRows.size()>=2 )
+ OutputBlockFrame( mnCols[nCol], mnRows[mnRows.size()-2], mnCols[nCol+1]-1, mnRows[mnRows.size()-1]-1 );
+ }
+ else
+ {
+ for ( SCROW i = 0 ; i < static_cast<SCROW>(mnRows.size())-1; i++ )
+ OutputBlockFrame( mnCols[nCol], mnRows[i], mnCols[nCol+1]-1, mnRows[i+1]-1 );
+ }
+ }
+ else
+ OutputBlockFrame( mnCols[nCol], mnRows.front(), mnCols[nCol+1]-1, mnRows.back()-1, bAllRows );
+ }
+ //out put rows area outer framer
+ if ( mnTabStartCol != mnDataStartCol )
+ {
+ if ( mnTabStartRow != mnDataStartRow )
+ OutputBlockFrame( mnTabStartCol, mnTabStartRow, mnDataStartCol-1, mnDataStartRow-1 );
+ OutputBlockFrame( mnTabStartCol, mnDataStartRow, mnDataStartCol-1, mnTabEndRow );
+ }
+ //out put cols area outer framer
+ OutputBlockFrame( mnDataStartCol, mnTabStartRow, mnTabEndCol, mnDataStartRow-1 );
+}
+
+ScDPOutputImpl::ScDPOutputImpl( ScDocument* pDoc, sal_uInt16 nTab,
+ SCCOL nTabStartCol,
+ SCROW nTabStartRow,
+ SCCOL nDataStartCol,
+ SCROW nDataStartRow,
+ SCCOL nTabEndCol,
+ SCROW nTabEndRow ):
+ mpDoc( pDoc ),
+ mnTab( nTab ),
+ mnTabStartCol( nTabStartCol ),
+ mnTabStartRow( nTabStartRow ),
+ mnDataStartCol ( nDataStartCol ),
+ mnDataStartRow ( nDataStartRow ),
+ mnTabEndCol( nTabEndCol ),
+ mnTabEndRow( nTabEndRow )
+{
+ mbNeedLineCols.resize( nTabEndCol-nDataStartCol+1, false );
+ mbNeedLineRows.resize( nTabEndRow-nDataStartRow+1, false );
+
+}
+
+bool ScDPOutputImpl::AddRow( SCROW nRow )
+{
+ if ( !mbNeedLineRows[ nRow - mnDataStartRow ] )
+ {
+ mbNeedLineRows[ nRow - mnDataStartRow ] = true;
+ mnRows.push_back( nRow );
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScDPOutputImpl::AddCol( SCCOL nCol )
+{
+
+ if ( !mbNeedLineCols[ nCol - mnDataStartCol ] )
+ {
+ mbNeedLineCols[ nCol - mnDataStartCol ] = true;
+ mnCols.push_back( nCol );
+ return true;
+ }
+ else
+ return false;
+}
+
+void ScDPOutputImpl::OutputBlockFrame ( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bHori )
+{
+ Color color = SC_DP_FRAME_COLOR;
+ ::editeng::SvxBorderLine aLine( &color, SC_DP_FRAME_INNER_BOLD );
+ ::editeng::SvxBorderLine aOutLine( &color, SC_DP_FRAME_OUTER_BOLD );
+
+ SvxBoxItem aBox( ATTR_BORDER );
+
+ if ( nStartCol == mnTabStartCol )
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::LEFT);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
+
+ if ( nStartRow == mnTabStartRow )
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::TOP);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
+
+ if ( nEndCol == mnTabEndCol ) //bottom row
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::RIGHT);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
+
+ if ( nEndRow == mnTabEndRow ) //bottom
+ aBox.SetLine(&aOutLine, SvxBoxItemLine::BOTTOM);
+ else
+ aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
+
+ SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false );
+ if ( bHori )
+ {
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI);
+ aBoxInfo.SetLine( &aLine, SvxBoxInfoItemLine::HORI );
+ }
+ else
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false );
+
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
+
+ mpDoc->ApplyFrameAreaTab(ScRange(nStartCol, nStartRow, mnTab, nEndCol, nEndRow , mnTab), aBox, aBoxInfo);
+
+}
+
+void lcl_SetStyleById(ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ TranslateId pStrId)
+{
+ if ( nCol1 > nCol2 || nRow1 > nRow2 )
+ {
+ OSL_FAIL("SetStyleById: invalid range");
+ return;
+ }
+
+ OUString aStyleName = ScResId(pStrId);
+ ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool();
+ ScStyleSheet* pStyle = static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
+ if (!pStyle)
+ {
+ // create new style (was in ScPivot::SetStyle)
+
+ pStyle = static_cast<ScStyleSheet*>( &pStlPool->Make( aStyleName, SfxStyleFamily::Para,
+ SfxStyleSearchBits::UserDefined ) );
+ pStyle->SetParent( ScResId(STR_STYLENAME_STANDARD) );
+ SfxItemSet& rSet = pStyle->GetItemSet();
+ if (pStrId == STR_PIVOT_STYLENAME_RESULT || pStrId == STR_PIVOT_STYLENAME_TITLE){
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CJK_FONT_WEIGHT ) );
+ rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_CTL_FONT_WEIGHT ) );
+ }
+ if (pStrId == STR_PIVOT_STYLENAME_CATEGORY || pStrId == STR_PIVOT_STYLENAME_TITLE)
+ rSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
+ }
+
+ pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle );
+}
+
+void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt16 nWidth )
+{
+ ::editeng::SvxBorderLine aLine(nullptr, nWidth, SvxBorderLineStyle::SOLID);
+ SvxBoxItem aBox( ATTR_BORDER );
+ aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
+ aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
+ aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
+ aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
+ SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER );
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI,false);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT,false);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE,false);
+
+ pDoc->ApplyFrameAreaTab(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), aBox, aBoxInfo);
+}
+
+void lcl_FillNumberFormats( std::unique_ptr<sal_uInt32[]>& rFormats, sal_Int32& rCount,
+ const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes,
+ const uno::Reference<container::XIndexAccess>& xDims )
+{
+ if ( rFormats )
+ return; // already set
+
+ // xLevRes is from the data layout dimension
+ //TODO: use result sequence from ScDPOutLevelData!
+
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+
+ tools::Long nSize = aResult.getLength();
+ if (!nSize)
+ return;
+
+ // get names/formats for all data dimensions
+ //TODO: merge this with the loop to collect ScDPOutLevelData?
+
+ std::vector <OUString> aDataNames;
+ std::vector <sal_uInt32> aDataFormats;
+ sal_Int32 nDimCount = xDims->getCount();
+ sal_Int32 nDim = 0;
+ for ( ; nDim < nDimCount ; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() && xDimName.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ aDataNames.push_back(xDimName->getName());
+ tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ SC_UNONAME_NUMFMT );
+ aDataFormats.push_back(nFormat);
+ }
+ }
+ }
+
+ if (aDataFormats.empty())
+ return;
+
+ const sheet::MemberResult* pArray = aResult.getConstArray();
+
+ OUString aName;
+ sal_uInt32* pNumFmt = new sal_uInt32[nSize];
+ if (aDataFormats.size() == 1)
+ {
+ // only one data dimension -> use its numberformat everywhere
+ tools::Long nFormat = aDataFormats[0];
+ for (tools::Long nPos=0; nPos<nSize; nPos++)
+ pNumFmt[nPos] = nFormat;
+ }
+ else
+ {
+ for (tools::Long nPos=0; nPos<nSize; nPos++)
+ {
+ // if CONTINUE bit is set, keep previous name
+ //TODO: keep number format instead!
+ if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) )
+ aName = pArray[nPos].Name;
+
+ sal_uInt32 nFormat = 0;
+ for (size_t i=0; i<aDataFormats.size(); i++)
+ if (aName == aDataNames[i]) //TODO: search more efficiently?
+ {
+ nFormat = aDataFormats[i];
+ break;
+ }
+ pNumFmt[nPos] = nFormat;
+ }
+ }
+
+ rFormats.reset( pNumFmt );
+ rCount = nSize;
+}
+
+sal_uInt32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims )
+{
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<beans::XPropertySet> xDimProp(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ if ( xDimProp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ tools::Long nFormat = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ SC_UNONAME_NUMFMT );
+
+ return nFormat; // use format from first found data dimension
+ }
+ }
+ }
+
+ return 0; // none found
+}
+
+bool lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq )
+{
+ // used to skip levels that have no members
+
+ return std::none_of(rSeq.begin(), rSeq.end(),
+ [](const sheet::MemberResult& rMem) {
+ return rMem.Flags & sheet::MemberResultFlags::HASMEMBER; });
+}
+
+/**
+ * Get visible page dimension members as results, except that, if all
+ * members are visible, then this function returns empty result.
+ */
+uno::Sequence<sheet::MemberResult> getVisiblePageMembersAsResults( const uno::Reference<uno::XInterface>& xLevel )
+{
+ if (!xLevel.is())
+ return uno::Sequence<sheet::MemberResult>();
+
+ uno::Reference<sheet::XMembersSupplier> xMSupplier(xLevel, UNO_QUERY);
+ if (!xMSupplier.is())
+ return uno::Sequence<sheet::MemberResult>();
+
+ uno::Reference<sheet::XMembersAccess> xNA = xMSupplier->getMembers();
+ if (!xNA.is())
+ return uno::Sequence<sheet::MemberResult>();
+
+ std::vector<sheet::MemberResult> aRes;
+ const uno::Sequence<OUString> aNames = xNA->getElementNames();
+ for (const OUString& rName : aNames)
+ {
+ xNA->getByName(rName);
+
+ uno::Reference<beans::XPropertySet> xMemPS(xNA->getByName(rName), UNO_QUERY);
+ if (!xMemPS.is())
+ continue;
+
+ OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xMemPS, SC_UNO_DP_LAYOUTNAME, OUString());
+ if (aCaption.isEmpty())
+ aCaption = rName;
+
+ bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xMemPS, SC_UNO_DP_ISVISIBLE);
+
+ if (bVisible)
+ {
+ /* TODO: any numeric value to obtain? */
+ aRes.emplace_back(rName, aCaption, 0, std::numeric_limits<double>::quiet_NaN());
+ }
+ }
+
+ if (o3tl::make_unsigned(aNames.getLength()) == aRes.size())
+ // All members are visible. Return empty result.
+ return uno::Sequence<sheet::MemberResult>();
+
+ return comphelper::containerToSequence(aRes);
+}
+
+}
+
+ScDPOutput::ScDPOutput( ScDocument* pD, uno::Reference<sheet::XDimensionsSupplier> xSrc,
+ const ScAddress& rPos, bool bFilter, bool bExpandCollapse ) :
+ pDoc( pD ),
+ xSource(std::move( xSrc )),
+ aStartPos( rPos ),
+ nColFmtCount( 0 ),
+ nRowFmtCount( 0 ),
+ nSingleNumFmt( 0 ),
+ nRowDims( 0 ),
+ nColCount(0),
+ nRowCount(0),
+ nHeaderSize(0),
+ bDoFilter(bFilter),
+ bResultsError(false),
+ bSizesValid(false),
+ bSizeOverflow(false),
+ mbHeaderLayout(false),
+ mbHasCompactRowField(false),
+ mbExpandCollapse(bExpandCollapse)
+{
+ nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0;
+ nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0;
+
+ uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY );
+ if ( xSource.is() && xResult.is() )
+ {
+ // get dimension results:
+
+ uno::Reference<container::XIndexAccess> xDims =
+ new ScNameToIndexAccess( xSource->getDimensions() );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() && xDimSupp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ tools::Long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+ SC_UNO_DP_POSITION );
+ bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, SC_UNO_DP_ISDATALAYOUT);
+ bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(
+ xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
+ sal_Int32 nNumFmt = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp, SC_UNO_DP_NUMBERFO);
+
+ if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ uno::Reference<container::XIndexAccess> xHiers =
+ new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+ tools::Long nHierarchy = ScUnoHelpFunctions::GetLongProperty(
+ xDimProp,
+ SC_UNO_DP_USEDHIERARCHY );
+ if ( nHierarchy >= xHiers->getCount() )
+ nHierarchy = 0;
+
+ uno::Reference<sheet::XLevelsSupplier> xHierSupp(xHiers->getByIndex(nHierarchy),
+ uno::UNO_QUERY);
+ if ( xHierSupp.is() )
+ {
+ uno::Reference<container::XIndexAccess> xLevels =
+ new ScNameToIndexAccess( xHierSupp->getLevels() );
+ tools::Long nLevCount = xLevels->getCount();
+ for (tools::Long nLev=0; nLev<nLevCount; nLev++)
+ {
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev),
+ uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
+ uno::Reference<sheet::XDataPilotMemberResults> xLevRes(
+ xLevel, uno::UNO_QUERY );
+ if ( xLevNam.is() && xLevRes.is() )
+ {
+ OUString aName = xLevNam->getName();
+ Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
+ // Caption equals the field name by default.
+ // #i108948# use ScUnoHelpFunctions::GetStringProperty, because
+ // LayoutName is new and may not be present in external implementation
+ OUString aCaption = ScUnoHelpFunctions::GetStringProperty( xPropSet,
+ SC_UNO_DP_LAYOUTNAME, aName );
+
+ switch ( eDimOrient )
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ {
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+ if (!lcl_MemberEmpty(aResult))
+ {
+ ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
+ aCaption, bHasHiddenMember, bIsDataLayout, false);
+ pColFields.push_back(tmp);
+ }
+ }
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ {
+ uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults();
+ ++nRowDims;
+ // We want only to remove the DATA column if it is empty
+ // and not any other empty columns (to still show the
+ // header columns)
+ bool bSkip = lcl_MemberEmpty(aResult) && bIsDataLayout;
+ if (!bSkip)
+ {
+ bool bFieldCompact = false;
+ try
+ {
+ sheet::DataPilotFieldLayoutInfo aLayoutInfo;
+ xPropSet->getPropertyValue( SC_UNO_DP_LAYOUT ) >>= aLayoutInfo;
+ bFieldCompact = (aLayoutInfo.LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
+ }
+ catch (uno::Exception&)
+ {
+ }
+ ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
+ aCaption, bHasHiddenMember, bIsDataLayout, false);
+ pRowFields.push_back(tmp);
+ aRowCompactFlags.push_back(bFieldCompact);
+ mbHasCompactRowField |= bFieldCompact;
+ }
+
+ }
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ {
+ uno::Sequence<sheet::MemberResult> aResult = getVisiblePageMembersAsResults(xLevel);
+ // no check on results for page fields
+ ScDPOutLevelData tmp(nDim, nHierarchy, nLev, nDimPos, nNumFmt, aResult, aName,
+ aCaption, bHasHiddenMember, false, true);
+ pPageFields.push_back(tmp);
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // get number formats from data dimensions
+ if ( bIsDataLayout )
+ {
+ OSL_ENSURE( nLevCount == 1, "data layout: multiple levels?" );
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN )
+ lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims );
+ else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW )
+ lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims );
+ }
+ }
+ }
+ }
+ }
+ else if ( bIsDataLayout )
+ {
+ // data layout dimension is hidden (allowed if there is only one data dimension)
+ // -> use the number format from the first data dimension for all results
+
+ nSingleNumFmt = lcl_GetFirstNumberFormat( xDims );
+ }
+ }
+ }
+ std::sort(pColFields.begin(), pColFields.end(), ScDPOutLevelDataComparator());
+ std::sort(pRowFields.begin(), pRowFields.end(), ScDPOutLevelDataComparator());
+ std::sort(pPageFields.begin(), pPageFields.end(), ScDPOutLevelDataComparator());
+
+ // get data results:
+
+ try
+ {
+ aData = xResult->getResults();
+ }
+ catch (const uno::RuntimeException&)
+ {
+ bResultsError = true;
+ }
+ }
+
+ // get "DataDescription" property (may be missing in external sources)
+
+ uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
+ if ( !xSrcProp.is() )
+ return;
+
+ try
+ {
+ uno::Any aAny = xSrcProp->getPropertyValue( SC_UNO_DP_DATADESC );
+ OUString aUStr;
+ aAny >>= aUStr;
+ aDataDescription = aUStr;
+ }
+ catch(const uno::Exception&)
+ {
+ }
+}
+
+ScDPOutput::~ScDPOutput()
+{
+}
+
+void ScDPOutput::SetPosition( const ScAddress& rPos )
+{
+ aStartPos = rPos;
+ bSizesValid = bSizeOverflow = false;
+}
+
+void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData )
+{
+ tools::Long nFlags = rData.Flags;
+ if ( nFlags & sheet::DataResultFlags::ERROR )
+ {
+ pDoc->SetError( nCol, nRow, nTab, FormulaError::NoValue );
+ }
+ else if ( nFlags & sheet::DataResultFlags::HASDATA )
+ {
+ pDoc->SetValue( nCol, nRow, nTab, rData.Value );
+
+ // use number formats from source
+
+ OSL_ENSURE( bSizesValid, "DataCell: !bSizesValid" );
+ sal_uInt32 nFormat = 0;
+ bool bApplyFormat = false;
+ if ( pColNumFmt )
+ {
+ if ( nCol >= nDataStartCol )
+ {
+ tools::Long nIndex = nCol - nDataStartCol;
+ if ( nIndex < nColFmtCount )
+ {
+ nFormat = pColNumFmt[nIndex];
+ bApplyFormat = true;
+ }
+ }
+ }
+ else if ( pRowNumFmt )
+ {
+ if ( nRow >= nDataStartRow )
+ {
+ tools::Long nIndex = nRow - nDataStartRow;
+ if ( nIndex < nRowFmtCount )
+ {
+ nFormat = pRowNumFmt[nIndex];
+ bApplyFormat = true;
+ }
+ }
+ }
+ else if ( nSingleNumFmt != 0 )
+ {
+ nFormat = nSingleNumFmt; // single format is used everywhere
+ bApplyFormat = true;
+ }
+
+ if (bApplyFormat)
+ pDoc->ApplyAttr(nCol, nRow, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat));
+ }
+ // SubTotal formatting is controlled by headers
+}
+
+void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const sheet::MemberResult& rData, bool bColHeader, tools::Long nLevel )
+{
+ tools::Long nFlags = rData.Flags;
+
+ if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
+ {
+ bool bNumeric = (nFlags & sheet::MemberResultFlags::NUMERIC) != 0;
+ if (bNumeric && std::isfinite( rData.Value))
+ {
+ pDoc->SetValue( nCol, nRow, nTab, rData.Value);
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ if (bNumeric)
+ aParam.setNumericInput();
+ else
+ aParam.setTextInput();
+
+ pDoc->SetString(nCol, nRow, nTab, rData.Caption, &aParam);
+ }
+ }
+
+ if ( !(nFlags & sheet::MemberResultFlags::SUBTOTAL) )
+ return;
+
+ ScDPOutputImpl outputimp( pDoc, nTab,
+ nTabStartCol, nTabStartRow,
+ nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
+ //TODO: limit frames to horizontal or vertical?
+ if (bColHeader)
+ {
+ outputimp.OutputBlockFrame( nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1 );
+
+ lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+static_cast<SCROW>(nLevel), nCol,nDataStartRow-1,
+ STR_PIVOT_STYLENAME_TITLE );
+ lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow,
+ STR_PIVOT_STYLENAME_RESULT );
+ }
+ else
+ {
+ outputimp.OutputBlockFrame( nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow );
+ lcl_SetStyleById( pDoc,nTab, nMemberStartCol+static_cast<SCCOL>(nLevel),nRow, nDataStartCol-1,nRow,
+ STR_PIVOT_STYLENAME_TITLE );
+ lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow,
+ STR_PIVOT_STYLENAME_RESULT );
+ }
+}
+
+void ScDPOutput::MultiFieldCell(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bRowField)
+{
+ pDoc->SetString(nCol, nRow, nTab, ScResId(bRowField ? STR_PIVOT_ROW_LABELS : STR_PIVOT_COL_LABELS));
+
+ ScMF nMergeFlag = ScMF::Button;
+ for (auto& rData : pRowFields)
+ {
+ if (rData.mbHasHiddenMember)
+ {
+ nMergeFlag |= ScMF::HiddenMember;
+ break;
+ }
+ }
+
+ nMergeFlag |= ScMF::ButtonPopup2;
+
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
+ lcl_SetStyleById( pDoc, nTab, nCol, nRow, nCol, nRow, STR_PIVOT_STYLENAME_FIELDNAME );
+}
+
+void ScDPOutput::FieldCell(
+ SCCOL nCol, SCROW nRow, SCTAB nTab, const ScDPOutLevelData& rData, bool bInTable)
+{
+ // Avoid unwanted automatic format detection.
+ ScSetStringParam aParam;
+ aParam.mbDetectNumberFormat = false;
+ aParam.meSetTextNumFormat = ScSetStringParam::Always;
+ aParam.mbHandleApostrophe = false;
+ pDoc->SetString(nCol, nRow, nTab, rData.maCaption, &aParam);
+
+ if (bInTable)
+ lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 );
+
+ // For field button drawing
+ ScMF nMergeFlag = ScMF::NONE;
+ if (rData.mbHasHiddenMember)
+ nMergeFlag |= ScMF::HiddenMember;
+
+ if (rData.mbPageDim)
+ {
+ nMergeFlag |= ScMF::ButtonPopup;
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
+ pDoc->ApplyFlagsTab(nCol+1, nRow, nCol+1, nRow, nTab, nMergeFlag);
+ }
+ else
+ {
+ nMergeFlag |= ScMF::Button;
+ if (!rData.mbDataLayout)
+ nMergeFlag |= ScMF::ButtonPopup;
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, nMergeFlag);
+ }
+
+ lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLENAME_FIELDNAME );
+}
+
+static void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ pDoc->SetString( nCol, nRow, nTab, ScResId(STR_CELL_FILTER) );
+ pDoc->ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, ScMF::Button);
+}
+
+SCCOL ScDPOutput::GetColumnsForRowFields() const
+{
+ if (!mbHasCompactRowField)
+ return static_cast<SCCOL>(pRowFields.size());
+
+ SCCOL nNum = 0;
+ for (const auto bCompact: aRowCompactFlags)
+ if (!bCompact)
+ ++nNum;
+
+ if (aRowCompactFlags.back())
+ ++nNum;
+
+ return nNum;
+}
+
+void ScDPOutput::CalcSizes()
+{
+ if (bSizesValid)
+ return;
+
+ // get column size of data from first row
+ //TODO: allow different sizes (and clear following areas) ???
+
+ nRowCount = aData.getLength();
+ const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
+ nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0;
+
+ nHeaderSize = 1;
+ if (GetHeaderLayout() && pColFields.empty())
+ // Insert an extra header row only when there is no column field.
+ nHeaderSize = 2;
+
+ // calculate output positions and sizes
+
+ tools::Long nPageSize = 0; // use page fields!
+ if ( bDoFilter || !pPageFields.empty() )
+ {
+ nPageSize += pPageFields.size() + 1; // plus one empty row
+ if ( bDoFilter )
+ ++nPageSize; // filter button above the page fields
+ }
+
+ if ( aStartPos.Col() + static_cast<tools::Long>(pRowFields.size()) + nColCount - 1 > pDoc->MaxCol() ||
+ aStartPos.Row() + nPageSize + nHeaderSize + static_cast<tools::Long>(pColFields.size()) + nRowCount > pDoc->MaxRow())
+ {
+ bSizeOverflow = true;
+ }
+
+ nTabStartCol = aStartPos.Col();
+ nTabStartRow = aStartPos.Row() + static_cast<SCROW>(nPageSize); // below page fields
+ nMemberStartCol = nTabStartCol;
+ nMemberStartRow = nTabStartRow + static_cast<SCROW>(nHeaderSize);
+ nDataStartCol = nMemberStartCol + GetColumnsForRowFields();
+ nDataStartRow = nMemberStartRow + static_cast<SCROW>(pColFields.size());
+ if ( nColCount > 0 )
+ nTabEndCol = nDataStartCol + static_cast<SCCOL>(nColCount) - 1;
+ else
+ nTabEndCol = nDataStartCol; // single column will remain empty
+ // if page fields are involved, include the page selection cells
+ if ( !pPageFields.empty() && nTabEndCol < nTabStartCol + 1 )
+ nTabEndCol = nTabStartCol + 1;
+ if ( nRowCount > 0 )
+ nTabEndRow = nDataStartRow + static_cast<SCROW>(nRowCount) - 1;
+ else
+ nTabEndRow = nDataStartRow; // single row will remain empty
+ bSizesValid = true;
+}
+
+sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos)
+{
+ using namespace ::com::sun::star::sheet;
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return DataPilotTablePositionType::NOT_IN_TABLE;
+
+ CalcSizes();
+
+ // Make sure the cursor is within the table.
+ if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow)
+ return DataPilotTablePositionType::NOT_IN_TABLE;
+
+ // test for result data area.
+ if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow)
+ return DataPilotTablePositionType::RESULT;
+
+ bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow);
+ bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol);
+
+ if (bInColHeader && bInRowHeader)
+ // probably in that ugly little box at the upper-left corner of the table.
+ return DataPilotTablePositionType::OTHER;
+
+ if (bInColHeader)
+ {
+ if (nRow == nTabStartRow)
+ // first row in the column header area is always used for column
+ // field buttons.
+ return DataPilotTablePositionType::OTHER;
+
+ return DataPilotTablePositionType::COLUMN_HEADER;
+ }
+
+ if (bInRowHeader)
+ return DataPilotTablePositionType::ROW_HEADER;
+
+ return DataPilotTablePositionType::OTHER;
+}
+
+void ScDPOutput::Output()
+{
+ SCTAB nTab = aStartPos.Tab();
+ const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray();
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+ if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits?
+ return; // nothing
+
+ // clear whole (new) output area
+ // when modifying table, clear old area !
+ //TODO: include InsertDeleteFlags::OBJECTS ???
+ pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, InsertDeleteFlags::ALL );
+
+ if ( bDoFilter )
+ lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab );
+
+ // output page fields:
+
+ for (size_t nField=0; nField<pPageFields.size(); ++nField)
+ {
+ SCCOL nHdrCol = aStartPos.Col();
+ SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 );
+ // draw without frame for consistency with filter button:
+ FieldCell(nHdrCol, nHdrRow, nTab, pPageFields[nField], false);
+ SCCOL nFldCol = nHdrCol + 1;
+
+ OUString aPageValue = ScResId(SCSTR_ALL);
+ const uno::Sequence<sheet::MemberResult>& rRes = pPageFields[nField].maResult;
+ sal_Int32 n = rRes.getLength();
+ if (n == 1)
+ {
+ if (rRes[0].Caption.isEmpty())
+ aPageValue = ScResId(STR_EMPTYDATA);
+ else
+ aPageValue = rRes[0].Caption;
+ }
+ else if (n > 1)
+ aPageValue = ScResId(SCSTR_MULTIPLE);
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ pDoc->SetString(nFldCol, nHdrRow, nTab, aPageValue, &aParam);
+
+ lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 );
+ }
+
+ // data description
+ // (may get overwritten by first row field)
+
+ if (aDataDescription.isEmpty())
+ {
+ //TODO: use default string ("result") ?
+ }
+ pDoc->SetString(nTabStartCol, nTabStartRow, nTab, aDataDescription);
+
+ // set STR_PIVOT_STYLENAME_INNER for whole data area (subtotals are overwritten)
+
+ if ( nDataStartRow > nTabStartRow )
+ lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1,
+ STR_PIVOT_STYLENAME_TOP );
+ lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow,
+ STR_PIVOT_STYLENAME_INNER );
+
+ // output column headers:
+ ScDPOutputImpl outputimp( pDoc, nTab,
+ nTabStartCol, nTabStartRow,
+ nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow );
+ size_t nNumColFields = pColFields.size();
+ for (size_t nField=0; nField<nNumColFields; nField++)
+ {
+ SCCOL nHdrCol = nDataStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
+ if (!mbHasCompactRowField || nNumColFields == 1)
+ FieldCell(nHdrCol, nTabStartRow, nTab, pColFields[nField], true);
+ else if (!nField)
+ MultiFieldCell(nHdrCol, nTabStartRow, nTab, false /* bRowField */);
+
+ SCROW nRowPos = nMemberStartRow + static_cast<SCROW>(nField); //TODO: check for overflow
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ tools::Long nThisColCount = rSequence.getLength();
+ OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
+ for (tools::Long nCol=0; nCol<nThisColCount; nCol++)
+ {
+ SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
+ HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], true, nField );
+ if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+ !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ {
+ tools::Long nEnd = nCol;
+ while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
+ ++nEnd;
+ SCCOL nEndColPos = nDataStartCol + static_cast<SCCOL>(nEnd); //TODO: check for overflow
+ if ( nField+1 < pColFields.size())
+ {
+ if ( nField == pColFields.size() - 2 )
+ {
+ outputimp.AddCol( nColPos );
+ if ( nColPos + 1 == nEndColPos )
+ outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos+1, true );
+ }
+ else
+ outputimp.OutputBlockFrame( nColPos,nRowPos, nEndColPos,nRowPos );
+
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else
+ lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else if ( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL )
+ outputimp.AddCol( nColPos );
+
+ // Apply the same number format as in data source.
+ pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pColFields[nField].mnSrcNumFmt));
+ }
+ if ( nField== 0 && pColFields.size() == 1 )
+ outputimp.OutputBlockFrame( nDataStartCol,nTabStartRow, nTabEndCol,nRowPos-1 );
+ }
+
+ // output row headers:
+ std::vector<bool> vbSetBorder;
+ vbSetBorder.resize( nTabEndRow - nDataStartRow + 1, false );
+ size_t nFieldColOffset = 0;
+ size_t nFieldIndentLevel = 0; // To calculate indent level for fields packed in a column.
+ size_t nNumRowFields = pRowFields.size();
+ for (size_t nField=0; nField<nNumRowFields; nField++)
+ {
+ const bool bCompactField = aRowCompactFlags[nField];
+ SCCOL nHdrCol = nTabStartCol + static_cast<SCCOL>(nField); //TODO: check for overflow
+ SCROW nHdrRow = nDataStartRow - 1;
+ if (!mbHasCompactRowField || nNumRowFields == 1)
+ FieldCell(nHdrCol, nHdrRow, nTab, pRowFields[nField], true);
+ else if (!nField)
+ MultiFieldCell(nHdrCol, nHdrRow, nTab, true /* bRowField */);
+
+ SCCOL nColPos = nMemberStartCol + static_cast<SCCOL>(nFieldColOffset); //TODO: check for overflow
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ sal_Int32 nThisRowCount = rSequence.getLength();
+ OSL_ENSURE( nThisRowCount == nRowCount, "count mismatch" ); //TODO: ???
+ for (sal_Int32 nRow=0; nRow<nThisRowCount; nRow++)
+ {
+ const sheet::MemberResult& rData = pArray[nRow];
+ const bool bHasMember = (rData.Flags & sheet::MemberResultFlags::HASMEMBER);
+ const bool bSubtotal = (rData.Flags & sheet::MemberResultFlags::SUBTOTAL);
+ SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
+ HeaderCell( nColPos, nRowPos, nTab, rData, false, nFieldColOffset );
+ if (bHasMember && !bSubtotal)
+ {
+ if ( nField+1 < pRowFields.size() )
+ {
+ tools::Long nEnd = nRow;
+ while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) )
+ ++nEnd;
+ SCROW nEndRowPos = nDataStartRow + static_cast<SCROW>(nEnd); //TODO: check for overflow
+ outputimp.AddRow( nRowPos );
+ if ( !vbSetBorder[ nRow ] )
+ {
+ outputimp.OutputBlockFrame( nColPos, nRowPos, nTabEndCol, nEndRowPos );
+ vbSetBorder[ nRow ] = true;
+ }
+ outputimp.OutputBlockFrame( nColPos, nRowPos, nColPos, nEndRowPos );
+
+ if ( nField == pRowFields.size() - 2 )
+ outputimp.OutputBlockFrame( nColPos+1, nRowPos, nColPos+1, nEndRowPos );
+
+ lcl_SetStyleById( pDoc, nTab, nColPos, nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+ else
+ {
+ lcl_SetStyleById( pDoc, nTab, nColPos, nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLENAME_CATEGORY );
+ }
+
+ // Set flags for collapse/expand buttons and indent field header text
+ {
+ bool bLast = nRowDims == (nField + 1);
+ size_t nMinIndentLevel = mbExpandCollapse ? 1 : 0;
+ tools::Long nIndent = o3tl::convert(13 * (bLast ? nFieldIndentLevel : nMinIndentLevel + nFieldIndentLevel), o3tl::Length::px, o3tl::Length::twip);
+ bool bHasContinue = (!bLast && nRow + 1 < nThisRowCount
+ && (pArray[nRow + 1].Flags & sheet::MemberResultFlags::CONTINUE));
+ if (nIndent)
+ pDoc->ApplyAttr(nColPos, nRowPos, nTab, ScIndentItem(nIndent));
+ if (mbExpandCollapse && !bLast)
+ {
+ pDoc->ApplyFlagsTab(nColPos, nRowPos, nColPos, nRowPos, nTab,
+ bHasContinue ? ScMF::DpCollapse : ScMF::DpExpand);
+ }
+ }
+ }
+ else if ( bSubtotal )
+ outputimp.AddRow( nRowPos );
+
+ // Apply the same number format as in data source.
+ pDoc->ApplyAttr(nColPos, nRowPos, nTab, SfxUInt32Item(ATTR_VALUE_FORMAT, pRowFields[nField].mnSrcNumFmt));
+ }
+
+ if (!bCompactField)
+ {
+ // Next field should be placed in next column only if current field has a non-compact layout.
+ ++nFieldColOffset;
+ nFieldIndentLevel = 0; // Reset indent level.
+ }
+ else
+ {
+ ++nFieldIndentLevel;
+ }
+ }
+
+ if (nColCount == 1 && nRowCount > 0 && pColFields.empty())
+ {
+ // the table contains exactly one data field and no column fields.
+ // Display data description at top right corner.
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ pDoc->SetString(nDataStartCol, nDataStartRow-1, nTab, aDataDescription, &aParam);
+ }
+
+ // output data results:
+
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ SCROW nRowPos = nDataStartRow + static_cast<SCROW>(nRow); //TODO: check for overflow
+ const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray();
+ sal_Int32 nThisColCount = pRowAry[nRow].getLength();
+ OSL_ENSURE( nThisColCount == nColCount, "count mismatch" ); //TODO: ???
+ for (sal_Int32 nCol=0; nCol<nThisColCount; nCol++)
+ {
+ SCCOL nColPos = nDataStartCol + static_cast<SCCOL>(nCol); //TODO: check for overflow
+ DataCell( nColPos, nRowPos, nTab, pColAry[nCol] );
+ }
+ }
+
+ outputimp.OutputDataArea();
+}
+
+ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType )
+{
+ using namespace ::com::sun::star::sheet;
+
+ CalcSizes();
+
+ SCTAB nTab = aStartPos.Tab();
+ switch (nRegionType)
+ {
+ case DataPilotOutputRangeType::RESULT:
+ return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
+ case DataPilotOutputRangeType::TABLE:
+ return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab);
+ default:
+ OSL_ENSURE(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type");
+ break;
+ }
+ return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab);
+}
+
+bool ScDPOutput::HasError()
+{
+ CalcSizes();
+
+ return bSizeOverflow || bResultsError;
+}
+
+sal_Int32 ScDPOutput::GetHeaderRows() const
+{
+ return pPageFields.size() + ( bDoFilter ? 1 : 0 );
+}
+
+namespace
+{
+ void insertNames(ScDPUniqueStringSet& rNames, const uno::Sequence<sheet::MemberResult>& rMemberResults)
+ {
+ for (const sheet::MemberResult& rMemberResult : rMemberResults)
+ {
+ if (rMemberResult.Flags & sheet::MemberResultFlags::HASMEMBER)
+ rNames.insert(rMemberResult.Name);
+ }
+ }
+}
+
+void ScDPOutput::GetMemberResultNames(ScDPUniqueStringSet& rNames, tools::Long nDimension)
+{
+ // Return the list of all member names in a dimension's MemberResults.
+ // Only the dimension has to be compared because this is only used with table data,
+ // where each dimension occurs only once.
+
+ auto lFindDimension = [nDimension](const ScDPOutLevelData& rField) { return rField.mnDim == nDimension; };
+
+ // look in column fields
+ auto colit = std::find_if(pColFields.begin(), pColFields.end(), lFindDimension);
+ if (colit != pColFields.end())
+ {
+ // collect the member names
+ insertNames(rNames, colit->maResult);
+ return;
+ }
+
+ // look in row fields
+ auto rowit = std::find_if(pRowFields.begin(), pRowFields.end(), lFindDimension);
+ if (rowit != pRowFields.end())
+ {
+ // collect the member names
+ insertNames(rNames, rowit->maResult);
+ }
+}
+
+void ScDPOutput::SetHeaderLayout(bool bUseGrid)
+{
+ mbHeaderLayout = bUseGrid;
+ bSizesValid = false;
+}
+
+namespace {
+
+void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex,
+ std::vector<OUString>& rDataNames, std::vector<OUString>& rGivenNames,
+ sheet::DataPilotFieldOrientation& rDataOrient,
+ const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ rDataLayoutIndex = -1; // invalid
+ rGrandTotalCols = 0;
+ rGrandTotalRows = 0;
+ rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN;
+
+ uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY );
+ bool bColGrand = ScUnoHelpFunctions::GetBoolProperty(
+ xSrcProp, SC_UNO_DP_COLGRAND);
+ if ( bColGrand )
+ rGrandTotalCols = 1; // default if data layout not in columns
+
+ bool bRowGrand = ScUnoHelpFunctions::GetBoolProperty(
+ xSrcProp, SC_UNO_DP_ROWGRAND);
+ if ( bRowGrand )
+ rGrandTotalRows = 1; // default if data layout not in rows
+
+ if ( !xSource.is() )
+ return;
+
+ // find index and orientation of "data layout" dimension, count data dimensions
+
+ sal_Int32 nDataCount = 0;
+
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() );
+ tools::Long nDimCount = xDims->getCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ if ( xDimProp.is() )
+ {
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(
+ xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN );
+ if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT ) )
+ {
+ rDataLayoutIndex = nDim;
+ rDataOrient = eDimOrient;
+ }
+ if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA )
+ {
+ OUString aSourceName;
+ OUString aGivenName;
+ ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim );
+ try
+ {
+ uno::Any aValue = xDimProp->getPropertyValue( SC_UNO_DP_LAYOUTNAME );
+
+ if( aValue.hasValue() )
+ {
+ OUString strLayoutName;
+
+ if( ( aValue >>= strLayoutName ) && !strLayoutName.isEmpty() )
+ aGivenName = strLayoutName;
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ }
+ rDataNames.push_back( aSourceName );
+ rGivenNames.push_back( aGivenName );
+
+ ++nDataCount;
+ }
+ }
+ }
+
+ if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand )
+ rGrandTotalCols = nDataCount;
+ else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand )
+ rGrandTotalRows = nDataCount;
+}
+
+}
+
+void ScDPOutput::GetRowFieldRange(SCCOL nCol, sal_Int32& nRowFieldStart, sal_Int32& nRowFieldEnd) const
+{
+ if (!mbHasCompactRowField)
+ {
+ nRowFieldStart = nCol;
+ nRowFieldEnd = nCol + 1;
+ return;
+ }
+
+ if (nCol >= static_cast<SCCOL>(aRowCompactFlags.size()))
+ {
+ nRowFieldStart = nRowFieldEnd = 0;
+ return;
+ }
+
+ nRowFieldStart = -1;
+ nRowFieldEnd = -1;
+ SCCOL nCurCol = 0;
+ sal_Int32 nField = 0;
+
+ for (const auto bCompact: aRowCompactFlags)
+ {
+ if (nCurCol == nCol && nRowFieldStart == -1)
+ nRowFieldStart = nField;
+
+ if (!bCompact)
+ ++nCurCol;
+
+ ++nField;
+
+ if (nCurCol == (nCol + 1) && nRowFieldStart != -1 && nRowFieldEnd == -1)
+ {
+ nRowFieldEnd = nField;
+ break;
+ }
+ }
+
+ if (nRowFieldStart != -1 && nRowFieldEnd == -1 && nCurCol == nCol)
+ nRowFieldEnd = static_cast<sal_Int32>(aRowCompactFlags.size());
+
+ if (nRowFieldStart == -1 || nRowFieldEnd == -1)
+ {
+ SAL_WARN("sc.core", "ScDPOutput::GetRowFieldRange : unable to find field range for nCol = " << nCol);
+ nRowFieldStart = nRowFieldEnd = 0;
+ }
+}
+
+sal_Int32 ScDPOutput::GetRowFieldCompact(SCCOL nColQuery, SCROW nRowQuery) const
+{
+ if (!mbHasCompactRowField)
+ return nColQuery - nTabStartCol;
+
+ SCCOL nCol = nColQuery - nTabStartCol;
+ sal_Int32 nStartField = 0;
+ sal_Int32 nEndField = 0;
+ GetRowFieldRange(nCol, nStartField, nEndField);
+
+ for (sal_Int32 nField = nEndField - 1; nField >= nStartField; --nField)
+ {
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+ sal_Int32 nThisRowCount = rSequence.getLength();
+ SCROW nRow = nRowQuery - nDataStartRow;
+ if (nRow >= 0 && nRow < nThisRowCount)
+ {
+ const sheet::MemberResult& rData = pArray[nRow];
+ if ((rData.Flags & sheet::MemberResultFlags::HASMEMBER)
+ && !(rData.Flags & sheet::MemberResultFlags::SUBTOTAL))
+ {
+ return nField;
+ }
+ }
+ }
+
+ return -1;
+}
+
+void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
+{
+ using namespace ::com::sun::star::sheet;
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return; // wrong sheet
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+
+ rPosData.PositionType = GetPositionType(rPos);
+ switch (rPosData.PositionType)
+ {
+ case DataPilotTablePositionType::RESULT:
+ {
+ vector<DataPilotFieldFilter> aFilters;
+ GetDataResultPositionData(aFilters, rPos);
+
+ DataPilotTableResultData aResData;
+ aResData.FieldFilters = comphelper::containerToSequence(aFilters);
+ aResData.DataFieldIndex = 0;
+ Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
+ if (xPropSet.is())
+ {
+ sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
+ SC_UNO_DP_DATAFIELDCOUNT );
+ if (nDataFieldCount > 0)
+ aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount;
+ }
+
+ // Copy appropriate DataResult object from the cached sheet::DataResult table.
+ if (aData.getLength() > nRow - nDataStartRow &&
+ aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol)
+ aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol];
+
+ rPosData.PositionData <<= aResData;
+ return;
+ }
+ case DataPilotTablePositionType::COLUMN_HEADER:
+ {
+ tools::Long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons
+ if (nField < 0)
+ break;
+
+ if (pColFields.size() < o3tl::make_unsigned(nField) + 1 )
+ break;
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].maResult;
+ if (!rSequence.hasElements())
+ break;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ tools::Long nItem = nCol - nDataStartCol;
+ // get origin of "continue" fields
+ while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ if (nItem < 0)
+ break;
+
+ DataPilotTableHeaderData aHeaderData;
+ aHeaderData.MemberName = pArray[nItem].Name;
+ aHeaderData.Flags = pArray[nItem].Flags;
+ aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].mnDim);
+ aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].mnHier);
+ aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].mnLevel);
+
+ rPosData.PositionData <<= aHeaderData;
+ return;
+ }
+ case DataPilotTablePositionType::ROW_HEADER:
+ {
+ tools::Long nField = GetRowFieldCompact(nCol, nRow);
+ if (nField < 0)
+ break;
+
+ if (pRowFields.size() < o3tl::make_unsigned(nField) + 1 )
+ break;
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].maResult;
+ if (!rSequence.hasElements())
+ break;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ tools::Long nItem = nRow - nDataStartRow;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ if (nItem < 0)
+ break;
+
+ DataPilotTableHeaderData aHeaderData;
+ aHeaderData.MemberName = pArray[nItem].Name;
+ aHeaderData.Flags = pArray[nItem].Flags;
+ aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].mnDim);
+ aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].mnHier);
+ aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].mnLevel);
+
+ rPosData.PositionData <<= aHeaderData;
+ return;
+ }
+ }
+}
+
+bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos)
+{
+ // Check to make sure there is at least one data field.
+ Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY);
+ if (!xPropSet.is())
+ return false;
+
+ sal_Int32 nDataFieldCount = ScUnoHelpFunctions::GetLongProperty( xPropSet,
+ SC_UNO_DP_DATAFIELDCOUNT );
+ if (nDataFieldCount == 0)
+ // No data field is present in this datapilot table.
+ return false;
+
+ // #i111421# use lcl_GetTableVars for correct size of totals and data layout position
+ sal_Int32 nGrandTotalCols;
+ sal_Int32 nGrandTotalRows;
+ sal_Int32 nDataLayoutIndex;
+ std::vector<OUString> aDataNames;
+ std::vector<OUString> aGivenNames;
+ sheet::DataPilotFieldOrientation eDataOrient;
+ lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource );
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return false; // wrong sheet
+
+ CalcSizes();
+
+ // test for data area.
+ if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow)
+ {
+ // Cell is outside the data field area.
+ return false;
+ }
+
+ bool bFilterByCol = (nCol <= static_cast<SCCOL>(nTabEndCol - nGrandTotalCols));
+ bool bFilterByRow = (nRow <= static_cast<SCROW>(nTabEndRow - nGrandTotalRows));
+
+ // column fields
+ for (size_t nColField = 0; nColField < pColFields.size() && bFilterByCol; ++nColField)
+ {
+ if (pColFields[nColField].mnDim == nDataLayoutIndex)
+ // There is no sense including the data layout field for filtering.
+ continue;
+
+ sheet::DataPilotFieldFilter filter;
+ filter.FieldName = pColFields[nColField].maName;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ OSL_ENSURE(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
+
+ tools::Long nItem = nCol - nDataStartCol;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ filter.MatchValueName = pArray[nItem].Name;
+ rFilters.push_back(filter);
+ }
+
+ // row fields
+ for (size_t nRowField = 0; nRowField < pRowFields.size() && bFilterByRow; ++nRowField)
+ {
+ if (pRowFields[nRowField].mnDim == nDataLayoutIndex)
+ // There is no sense including the data layout field for filtering.
+ continue;
+
+ sheet::DataPilotFieldFilter filter;
+ filter.FieldName = pRowFields[nRowField].maName;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].maResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+
+ OSL_ENSURE(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption");
+
+ tools::Long nItem = nRow - nDataStartRow;
+ // get origin of "continue" fields
+ while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) )
+ --nItem;
+
+ filter.MatchValueName = pArray[nItem].Name;
+ rFilters.push_back(filter);
+ }
+
+ return true;
+}
+
+namespace {
+
+OUString lcl_GetDataFieldName( std::u16string_view rSourceName, sal_Int16 eFunc )
+{
+ TranslateId pStrId;
+ switch ( eFunc )
+ {
+ case sheet::GeneralFunction2::SUM: pStrId = STR_FUN_TEXT_SUM; break;
+ case sheet::GeneralFunction2::COUNT:
+ case sheet::GeneralFunction2::COUNTNUMS: pStrId = STR_FUN_TEXT_COUNT; break;
+ case sheet::GeneralFunction2::AVERAGE: pStrId = STR_FUN_TEXT_AVG; break;
+ case sheet::GeneralFunction2::MEDIAN: pStrId = STR_FUN_TEXT_MEDIAN; break;
+ case sheet::GeneralFunction2::MAX: pStrId = STR_FUN_TEXT_MAX; break;
+ case sheet::GeneralFunction2::MIN: pStrId = STR_FUN_TEXT_MIN; break;
+ case sheet::GeneralFunction2::PRODUCT: pStrId = STR_FUN_TEXT_PRODUCT; break;
+ case sheet::GeneralFunction2::STDEV:
+ case sheet::GeneralFunction2::STDEVP: pStrId = STR_FUN_TEXT_STDDEV; break;
+ case sheet::GeneralFunction2::VAR:
+ case sheet::GeneralFunction2::VARP: pStrId = STR_FUN_TEXT_VAR; break;
+ case sheet::GeneralFunction2::NONE:
+ case sheet::GeneralFunction2::AUTO: break;
+ default:
+ {
+ assert(false);
+ }
+ }
+ if (!pStrId)
+ return OUString();
+
+ return ScResId(pStrId) + " - " + rSourceName;
+}
+
+}
+
+void ScDPOutput::GetDataDimensionNames(
+ OUString& rSourceName, OUString& rGivenName, const uno::Reference<uno::XInterface>& xDim )
+{
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY );
+ if ( !(xDimProp.is() && xDimName.is()) )
+ return;
+
+ // Asterisks are added in ScDPSaveData::WriteToSource to create unique names.
+ //TODO: preserve original name there?
+ rSourceName = ScDPUtil::getSourceDimensionName(xDimName->getName());
+
+ // Generate "given name" the same way as in dptabres.
+ //TODO: Should use a stored name when available
+
+ sal_Int16 eFunc = ScUnoHelpFunctions::GetShortProperty(
+ xDimProp, SC_UNO_DP_FUNCTION2,
+ sheet::GeneralFunction2::NONE );
+ rGivenName = lcl_GetDataFieldName( rSourceName, eFunc );
+}
+
+bool ScDPOutput::IsFilterButton( const ScAddress& rPos )
+{
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() || !bDoFilter )
+ return false; // wrong sheet or no button at all
+
+ // filter button is at top left
+ return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() );
+}
+
+tools::Long ScDPOutput::GetHeaderDim( const ScAddress& rPos, sheet::DataPilotFieldOrientation& rOrient )
+{
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return -1; // wrong sheet
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+
+ // test for column header
+
+ if ( nRow == nTabStartRow && nCol >= nDataStartCol && o3tl::make_unsigned(nCol) < nDataStartCol + pColFields.size())
+ {
+ rOrient = sheet::DataPilotFieldOrientation_COLUMN;
+ tools::Long nField = nCol - nDataStartCol;
+ return pColFields[nField].mnDim;
+ }
+
+ // test for row header
+
+ if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ tools::Long nField = nCol - nTabStartCol;
+ return pRowFields[nField].mnDim;
+ }
+
+ // test for page field
+
+ SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
+ if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_PAGE;
+ tools::Long nField = nRow - nPageStartRow;
+ return pPageFields[nField].mnDim;
+ }
+
+ //TODO: single data field (?)
+
+ rOrient = sheet::DataPilotFieldOrientation_HIDDEN;
+ return -1; // invalid
+}
+
+bool ScDPOutput::GetHeaderDrag( const ScAddress& rPos, bool bMouseLeft, bool bMouseTop,
+ tools::Long nDragDim,
+ tools::Rectangle& rPosRect, sheet::DataPilotFieldOrientation& rOrient, tools::Long& rDimPos )
+{
+ // Rectangle instead of ScRange for rPosRect to allow for negative values
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ if ( nTab != aStartPos.Tab() )
+ return false; // wrong sheet
+
+ // calculate output positions and sizes
+
+ CalcSizes();
+
+ // test for column header
+
+ if ( nCol >= nDataStartCol && nCol <= nTabEndCol &&
+ nRow + 1 >= nMemberStartRow && o3tl::make_unsigned(nRow) < nMemberStartRow + pColFields.size())
+ {
+ tools::Long nField = nRow - nMemberStartRow;
+ if (nField < 0)
+ {
+ nField = 0;
+ bMouseTop = true;
+ }
+ //TODO: find start of dimension
+
+ rPosRect = tools::Rectangle( nDataStartCol, nMemberStartRow + nField,
+ nTabEndCol, nMemberStartRow + nField -1 );
+
+ bool bFound = false; // is this within the same orientation?
+ bool bBeforeDrag = false;
+ bool bAfterDrag = false;
+ for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pColFields.size() && !bFound; nPos++)
+ {
+ if (pColFields[nPos].mnDim == nDragDim)
+ {
+ bFound = true;
+ if ( nField < nPos )
+ bBeforeDrag = true;
+ else if ( nField > nPos )
+ bAfterDrag = true;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ rPosRect.AdjustBottom( 1 );
+ if (bAfterDrag)
+ rPosRect.AdjustTop( 1 );
+ }
+ }
+ else
+ {
+ if ( !bMouseTop )
+ {
+ rPosRect.AdjustTop( 1 );
+ rPosRect.AdjustBottom( 1 );
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_COLUMN;
+ rDimPos = nField; //!...
+ return true;
+ }
+
+ // test for row header
+
+ // special case if no row fields
+ bool bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
+ pRowFields.empty() && nCol == nTabStartCol && bMouseLeft );
+
+ if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow &&
+ nCol + 1 >= nTabStartCol && o3tl::make_unsigned(nCol) < nTabStartCol + pRowFields.size() ) )
+ {
+ tools::Long nField = nCol - nTabStartCol;
+ //TODO: find start of dimension
+
+ rPosRect = tools::Rectangle( nTabStartCol + nField, nDataStartRow - 1,
+ nTabStartCol + nField - 1, nTabEndRow );
+
+ bool bFound = false; // is this within the same orientation?
+ bool bBeforeDrag = false;
+ bool bAfterDrag = false;
+ for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pRowFields.size() && !bFound; nPos++)
+ {
+ if (pRowFields[nPos].mnDim == nDragDim)
+ {
+ bFound = true;
+ if ( nField < nPos )
+ bBeforeDrag = true;
+ else if ( nField > nPos )
+ bAfterDrag = true;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ rPosRect.AdjustRight( 1 );
+ if (bAfterDrag)
+ rPosRect.AdjustLeft( 1 );
+ }
+ }
+ else
+ {
+ if ( !bMouseLeft )
+ {
+ rPosRect.AdjustLeft( 1 );
+ rPosRect.AdjustRight( 1 );
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ rDimPos = nField; //!...
+ return true;
+ }
+
+ // test for page fields
+
+ SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 );
+ if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol &&
+ nRow + 1 >= nPageStartRow && o3tl::make_unsigned(nRow) < nPageStartRow + pPageFields.size() )
+ {
+ tools::Long nField = nRow - nPageStartRow;
+ if (nField < 0)
+ {
+ nField = 0;
+ bMouseTop = true;
+ }
+ //TODO: find start of dimension
+
+ rPosRect = tools::Rectangle( aStartPos.Col(), nPageStartRow + nField,
+ nTabEndCol, nPageStartRow + nField - 1 );
+
+ bool bFound = false; // is this within the same orientation?
+ bool bBeforeDrag = false;
+ bool bAfterDrag = false;
+ for (tools::Long nPos=0; o3tl::make_unsigned(nPos)<pPageFields.size() && !bFound; nPos++)
+ {
+ if (pPageFields[nPos].mnDim == nDragDim)
+ {
+ bFound = true;
+ if ( nField < nPos )
+ bBeforeDrag = true;
+ else if ( nField > nPos )
+ bAfterDrag = true;
+ }
+ }
+
+ if ( bFound )
+ {
+ if (!bBeforeDrag)
+ {
+ rPosRect.AdjustBottom( 1 );
+ if (bAfterDrag)
+ rPosRect.AdjustTop( 1 );
+ }
+ }
+ else
+ {
+ if ( !bMouseTop )
+ {
+ rPosRect.AdjustTop( 1 );
+ rPosRect.AdjustBottom( 1 );
+ ++nField;
+ }
+ }
+
+ rOrient = sheet::DataPilotFieldOrientation_PAGE;
+ rDimPos = nField; //!...
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpoutputgeometry.cxx b/sc/source/core/data/dpoutputgeometry.cxx
new file mode 100644
index 0000000000..0c5307258c
--- /dev/null
+++ b/sc/source/core/data/dpoutputgeometry.cxx
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpoutputgeometry.hxx>
+#include <address.hxx>
+
+#include <vector>
+
+using ::std::vector;
+
+ScDPOutputGeometry::ScDPOutputGeometry(const ScRange& rOutRange, bool bShowFilter) :
+ maOutRange(rOutRange),
+ mnRowFields(0),
+ mnColumnFields(0),
+ mnPageFields(0),
+ mnDataFields(0),
+ meDataLayoutType(None),
+ mbShowFilter(bShowFilter),
+ mbHeaderLayout (false),
+ mbCompactMode (false)
+{
+}
+
+void ScDPOutputGeometry::setRowFieldCount(sal_uInt32 nCount)
+{
+ mnRowFields = nCount;
+}
+
+void ScDPOutputGeometry::setColumnFieldCount(sal_uInt32 nCount)
+{
+ mnColumnFields = nCount;
+}
+
+void ScDPOutputGeometry::setPageFieldCount(sal_uInt32 nCount)
+{
+ mnPageFields = nCount;
+}
+
+void ScDPOutputGeometry::setDataFieldCount(sal_uInt32 nCount)
+{
+ mnDataFields = nCount;
+}
+
+void ScDPOutputGeometry::setDataLayoutType(FieldType eType)
+{
+ meDataLayoutType = eType;
+}
+
+void ScDPOutputGeometry::setHeaderLayout(bool bHeaderLayout)
+{
+ mbHeaderLayout = bHeaderLayout;
+}
+
+void ScDPOutputGeometry::setCompactMode(bool bCompactMode)
+{
+ mbCompactMode = bCompactMode;
+}
+
+void ScDPOutputGeometry::getColumnFieldPositions(vector<ScAddress>& rAddrs) const
+{
+ sal_uInt32 nColumnFields, nRowFields;
+ adjustFieldsForDataLayout(nColumnFields, nRowFields);
+
+ vector<ScAddress> aAddrs;
+ if (!nColumnFields)
+ {
+ rAddrs.swap(aAddrs);
+ return;
+ }
+
+ SCROW nCurRow = maOutRange.aStart.Row();
+
+ if (mnPageFields)
+ {
+ SCROW nRowStart = maOutRange.aStart.Row() + int(mbShowFilter);
+ SCROW nRowEnd = nRowStart + static_cast<SCCOL>(mnPageFields-1);
+ nCurRow = nRowEnd + 2;
+ }
+ else if (mbShowFilter)
+ nCurRow += 2;
+
+ SCROW nRow = nCurRow;
+ SCTAB nTab = maOutRange.aStart.Tab();
+ SCCOL nColStart = static_cast<SCCOL>(maOutRange.aStart.Col() + nRowFields);
+ if(mbCompactMode)
+ nColStart = static_cast<SCCOL>(maOutRange.aStart.Col() + 1); // We have only one row in compact mode
+ SCCOL nColEnd = nColStart + static_cast<SCCOL>(nColumnFields-1);
+
+ for (SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ aAddrs.emplace_back(nCol, nRow, nTab);
+ rAddrs.swap(aAddrs);
+}
+
+void ScDPOutputGeometry::getRowFieldPositions(vector<ScAddress>& rAddrs) const
+{
+ sal_uInt32 nColumnFields, nRowFields;
+ adjustFieldsForDataLayout(nColumnFields, nRowFields);
+
+ vector<ScAddress> aAddrs;
+ if (!nRowFields)
+ {
+ rAddrs.swap(aAddrs);
+ return;
+ }
+
+ SCROW nRow = getRowFieldHeaderRow();
+ SCTAB nTab = maOutRange.aStart.Tab();
+ SCCOL nColStart = maOutRange.aStart.Col();
+ SCCOL nColEnd = nColStart + static_cast<SCCOL>(nRowFields-1);
+
+ if(mbCompactMode)
+ nColEnd = nColStart; // We have only one row in compact mode
+
+ for (SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ aAddrs.emplace_back(nCol, nRow, nTab);
+ rAddrs.swap(aAddrs);
+}
+
+void ScDPOutputGeometry::getPageFieldPositions(vector<ScAddress>& rAddrs) const
+{
+ vector<ScAddress> aAddrs;
+ if (!mnPageFields)
+ {
+ rAddrs.swap(aAddrs);
+ return;
+ }
+
+ SCTAB nTab = maOutRange.aStart.Tab();
+ SCCOL nCol = maOutRange.aStart.Col();
+
+ SCROW nRowStart = maOutRange.aStart.Row() + int(mbShowFilter);
+ SCROW nRowEnd = nRowStart + static_cast<SCCOL>(mnPageFields-1);
+
+ for (SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
+ aAddrs.emplace_back(nCol, nRow, nTab);
+ rAddrs.swap(aAddrs);
+}
+
+SCROW ScDPOutputGeometry::getRowFieldHeaderRow() const
+{
+ SCROW nCurRow = maOutRange.aStart.Row();
+ sal_uInt32 nColumnFields, nRowFields;
+ adjustFieldsForDataLayout(nColumnFields, nRowFields);
+
+ if (mnPageFields)
+ {
+ SCROW nRowStart = maOutRange.aStart.Row() + int(mbShowFilter);
+ SCROW nRowEnd = nRowStart + static_cast<SCCOL>(mnPageFields-1);
+ nCurRow = nRowEnd + 2;
+ }
+ else if (mbShowFilter)
+ nCurRow += 2;
+
+ if (nColumnFields)
+ nCurRow += static_cast<SCROW>(nColumnFields);
+ else if (nRowFields && mbHeaderLayout)
+ ++nCurRow;
+
+ return nCurRow;
+}
+
+void ScDPOutputGeometry::adjustFieldsForDataLayout(sal_uInt32& rColumnFields, sal_uInt32& rRowFields) const
+{
+ rRowFields = mnRowFields;
+ rColumnFields = mnColumnFields;
+
+ if (mnDataFields >= 2)
+ return;
+
+ // Data layout field can be either row or column field, never page field.
+ switch (meDataLayoutType)
+ {
+ case Column:
+ if (rColumnFields > 0)
+ rColumnFields -= 1;
+ break;
+ case Row:
+ if (rRowFields > 0)
+ rRowFields -= 1;
+ break;
+ default:
+ ;
+ }
+}
+
+std::pair<ScDPOutputGeometry::FieldType, size_t>
+ScDPOutputGeometry::getFieldButtonType(const ScAddress& rPos) const
+{
+ SCROW nCurRow = maOutRange.aStart.Row();
+ sal_uInt32 nColumnFields, nRowFields;
+ adjustFieldsForDataLayout(nColumnFields, nRowFields);
+
+ if (mnPageFields)
+ {
+ SCCOL nCol = maOutRange.aStart.Col();
+ SCROW nRowStart = maOutRange.aStart.Row() + int(mbShowFilter);
+ SCROW nRowEnd = nRowStart + static_cast<SCCOL>(mnPageFields-1);
+ if (rPos.Col() == nCol && nRowStart <= rPos.Row() && rPos.Row() <= nRowEnd)
+ {
+ size_t nPos = static_cast<size_t>(rPos.Row() - nRowStart);
+ return std::pair<FieldType, size_t>(Page, nPos);
+ }
+
+ nCurRow = nRowEnd + 2;
+ }
+ else if (mbShowFilter)
+ nCurRow += 2;
+
+ if (nColumnFields)
+ {
+ SCROW nRow = nCurRow;
+ SCCOL nColStart = static_cast<SCCOL>(maOutRange.aStart.Col() + nRowFields);
+ SCCOL nColEnd = nColStart + static_cast<SCCOL>(nColumnFields-1);
+ if (rPos.Row() == nRow && nColStart <= rPos.Col() && rPos.Col() <= nColEnd)
+ {
+ size_t nPos = static_cast<size_t>(rPos.Col() - nColStart);
+ return std::pair<FieldType, size_t>(Column, nPos);
+ }
+
+ nCurRow += static_cast<SCROW>(nColumnFields);
+ }
+ else if (mbHeaderLayout)
+ ++nCurRow;
+
+ if (nRowFields)
+ {
+ SCCOL nColStart = maOutRange.aStart.Col();
+ SCCOL nColEnd = nColStart + static_cast<SCCOL>(nRowFields-1);
+ if (rPos.Row() == nCurRow && nColStart <= rPos.Col() && rPos.Col() <= nColEnd)
+ {
+ size_t nPos = static_cast<size_t>(rPos.Col() - nColStart);
+ return std::pair<FieldType, size_t>(Row, nPos);
+ }
+ }
+
+ return std::pair<FieldType, size_t>(None, 0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpresfilter.cxx b/sc/source/core/data/dpresfilter.cxx
new file mode 100644
index 0000000000..5028080571
--- /dev/null
+++ b/sc/source/core/data/dpresfilter.cxx
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <dpresfilter.hxx>
+#include <global.hxx>
+
+#include <unotools/charclass.hxx>
+#include <sal/log.hxx>
+#include <o3tl/hash_combine.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <limits>
+#include <utility>
+
+using namespace com::sun::star;
+
+ScDPResultFilter::ScDPResultFilter(OUString aDimName, bool bDataLayout) :
+ maDimName(std::move(aDimName)), mbHasValue(false), mbDataLayout(bDataLayout) {}
+
+ScDPResultFilterContext::ScDPResultFilterContext() :
+ mnCol(0), mnRow(0) {}
+
+size_t ScDPResultTree::NamePairHash::operator() (const NamePairType& rPair) const
+{
+ std::size_t seed = 0;
+ o3tl::hash_combine(seed, rPair.first.hashCode());
+ o3tl::hash_combine(seed, rPair.second.hashCode());
+ return seed;
+}
+
+#if DEBUG_PIVOT_TABLE
+void ScDPResultTree::DimensionNode::dump(int nLevel) const
+{
+ string aIndent(nLevel*2, ' ');
+ MembersType::const_iterator it = maChildMembersValueNames.begin(), itEnd = maChildMembersValueNames.end();
+ for (; it != itEnd; ++it)
+ {
+ cout << aIndent << "member: ";
+ const ScDPItemData& rVal = it->first;
+ if (rVal.IsValue())
+ cout << rVal.GetValue();
+ else
+ cout << rVal.GetString();
+ cout << endl;
+
+ it->second->dump(nLevel+1);
+ }
+}
+#endif
+
+ScDPResultTree::MemberNode::MemberNode() {}
+
+ScDPResultTree::MemberNode::~MemberNode() {}
+
+#if DEBUG_PIVOT_TABLE
+void ScDPResultTree::MemberNode::dump(int nLevel) const
+{
+ string aIndent(nLevel*2, ' ');
+ for (const auto& rValue : maValues)
+ cout << aIndent << "value: " << rValue << endl;
+
+ for (const auto& [rName, rxDim] : maChildDimensions)
+ {
+ cout << aIndent << "dimension: " << rName << endl;
+ rxDim->dump(nLevel+1);
+ }
+}
+#endif
+
+ScDPResultTree::ScDPResultTree() : mpRoot(new MemberNode) {}
+ScDPResultTree::~ScDPResultTree()
+{
+}
+
+void ScDPResultTree::add(
+ const std::vector<ScDPResultFilter>& rFilters, double fVal)
+{
+ // TODO: I'll work on the col / row to value node mapping later.
+
+ const OUString* pDimName = nullptr;
+ const OUString* pMemName = nullptr;
+ MemberNode* pMemNode = mpRoot.get();
+
+ for (const ScDPResultFilter& filter : rFilters)
+ {
+ if (filter.mbDataLayout)
+ continue;
+
+ if (maPrimaryDimName.isEmpty())
+ maPrimaryDimName = filter.maDimName;
+
+ // See if this dimension exists.
+ auto& rDims = pMemNode->maChildDimensions;
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(filter.maDimName);
+ auto itDim = rDims.find(aUpperName);
+ if (itDim == rDims.end())
+ {
+ // New dimension. Insert it.
+ auto r = rDims.emplace(aUpperName, DimensionNode());
+ assert(r.second);
+ itDim = r.first;
+ }
+
+ pDimName = &itDim->first;
+
+ // Now, see if this dimension member exists.
+ DimensionNode& rDim = itDim->second;
+ MembersType& rMembersValueNames = rDim.maChildMembersValueNames;
+ aUpperName = ScGlobal::getCharClass().uppercase(filter.maValueName);
+ MembersType::iterator itMem = rMembersValueNames.find(aUpperName);
+ if (itMem == rMembersValueNames.end())
+ {
+ // New member. Insert it.
+ auto pNode = std::make_shared<MemberNode>();
+ std::pair<MembersType::iterator, bool> r =
+ rMembersValueNames.emplace(aUpperName, pNode);
+
+ if (!r.second)
+ // Insertion failed!
+ return;
+
+ itMem = r.first;
+
+ // If the locale independent value string isn't any different it
+ // makes no sense to add it to the separate mapping.
+ if (!filter.maValue.isEmpty() && filter.maValue != filter.maValueName)
+ {
+ MembersType& rMembersValues = rDim.maChildMembersValues;
+ aUpperName = ScGlobal::getCharClass().uppercase(filter.maValue);
+ MembersType::iterator itMemVal = rMembersValues.find(aUpperName);
+ if (itMemVal == rMembersValues.end())
+ {
+ // New member. Insert it.
+ std::pair<MembersType::iterator, bool> it =
+ rMembersValues.emplace(aUpperName, pNode);
+ // If insertion failed do not bail out anymore.
+ SAL_WARN_IF( !it.second, "sc.core", "ScDPResultTree::add - rMembersValues.insert failed");
+ }
+ }
+ }
+
+ pMemName = &itMem->first;
+ pMemNode = itMem->second.get();
+ }
+
+ if (pDimName && pMemName)
+ {
+ NamePairType aNames(
+ ScGlobal::getCharClass().uppercase(*pDimName),
+ ScGlobal::getCharClass().uppercase(*pMemName));
+
+ LeafValuesType::iterator it = maLeafValues.find(aNames);
+ if (it == maLeafValues.end())
+ {
+ // This name pair doesn't exist. Associate a new value for it.
+ maLeafValues.emplace(aNames, fVal);
+ }
+ else
+ {
+ // This name pair already exists. Set the value to NaN.
+ it->second = std::numeric_limits<double>::quiet_NaN();
+ }
+ }
+
+ pMemNode->maValues.push_back(fVal);
+}
+
+void ScDPResultTree::swap(ScDPResultTree& rOther)
+{
+ std::swap(maPrimaryDimName, rOther.maPrimaryDimName);
+ std::swap(mpRoot, rOther.mpRoot);
+ maLeafValues.swap(rOther.maLeafValues);
+}
+
+bool ScDPResultTree::empty() const
+{
+ return mpRoot->maChildDimensions.empty();
+}
+
+void ScDPResultTree::clear()
+{
+ maPrimaryDimName.clear();
+ mpRoot.reset( new MemberNode );
+}
+
+const ScDPResultTree::ValuesType* ScDPResultTree::getResults(
+ const uno::Sequence<sheet::DataPilotFieldFilter>& rFilters) const
+{
+ const MemberNode* pMember = mpRoot.get();
+ for (const sheet::DataPilotFieldFilter& rFilter : rFilters)
+ {
+ auto itDim = pMember->maChildDimensions.find(
+ ScGlobal::getCharClass().uppercase(rFilter.FieldName));
+
+ if (itDim == pMember->maChildDimensions.end())
+ // Specified dimension not found.
+ return nullptr;
+
+ const DimensionNode& rDim = itDim->second;
+ MembersType::const_iterator itMem( rDim.maChildMembersValueNames.find(
+ ScGlobal::getCharClass().uppercase( rFilter.MatchValueName)));
+
+ if (itMem == rDim.maChildMembersValueNames.end())
+ {
+ // Specified member name not found, try locale independent value.
+ itMem = rDim.maChildMembersValues.find( ScGlobal::getCharClass().uppercase( rFilter.MatchValue));
+
+ if (itMem == rDim.maChildMembersValues.end())
+ // Specified member not found.
+ return nullptr;
+ }
+
+ pMember = itMem->second.get();
+ }
+
+ if (pMember->maValues.empty())
+ {
+ // Descend into dimension member children while there is no result and
+ // exactly one dimension field with exactly one member item, for which
+ // no further constraint (filter) has to match.
+ const MemberNode* pFieldMember = pMember;
+ while (pFieldMember->maChildDimensions.size() == 1)
+ {
+ auto itDim( pFieldMember->maChildDimensions.begin());
+ const DimensionNode& rDim = itDim->second;
+ if (rDim.maChildMembersValueNames.size() != 1)
+ break; // while
+ pFieldMember = rDim.maChildMembersValueNames.begin()->second.get();
+ if (!pFieldMember->maValues.empty())
+ return &pFieldMember->maValues;
+ }
+ }
+
+ return &pMember->maValues;
+}
+
+double ScDPResultTree::getLeafResult(const css::sheet::DataPilotFieldFilter& rFilter) const
+{
+ NamePairType aPair(
+ ScGlobal::getCharClass().uppercase(rFilter.FieldName),
+ ScGlobal::getCharClass().uppercase(rFilter.MatchValueName));
+
+ LeafValuesType::const_iterator it = maLeafValues.find(aPair);
+ if (it != maLeafValues.end())
+ // Found!
+ return it->second;
+
+ // Not found. Return an NaN.
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+#if DEBUG_PIVOT_TABLE
+void ScDPResultTree::dump() const
+{
+ cout << "primary dimension name: " << maPrimaryDimName << endl;
+ mpRoot->dump(0);
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpsave.cxx b/sc/source/core/data/dpsave.cxx
new file mode 100644
index 0000000000..246714e843
--- /dev/null
+++ b/sc/source/core/data/dpsave.cxx
@@ -0,0 +1,1390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <dputil.hxx>
+#include <generalfunction.hxx>
+#include <dptabdat.hxx>
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/stl_types.hxx>
+#include <unotools/charclass.hxx>
+
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
+#include <com/sun/star/sheet/XLevelsSupplier.hpp>
+#include <com/sun/star/sheet/XMembersSupplier.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <unordered_map>
+#include <algorithm>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace com::sun::star::sheet;
+using ::std::unique_ptr;
+
+#define SC_DPSAVEMODE_DONTKNOW 2
+
+static void lcl_SetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp,
+ const OUString& rName, bool bValue )
+{
+ //TODO: move to ScUnoHelpFunctions?
+
+ xProp->setPropertyValue( rName, uno::Any( bValue ) );
+}
+
+ScDPSaveMember::ScDPSaveMember(OUString _aName) :
+ aName(std::move( _aName )),
+ nVisibleMode( SC_DPSAVEMODE_DONTKNOW ),
+ nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW )
+{
+}
+
+ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) :
+ aName( r.aName ),
+ mpLayoutName( r.mpLayoutName ),
+ nVisibleMode( r.nVisibleMode ),
+ nShowDetailsMode( r.nShowDetailsMode )
+{
+}
+
+ScDPSaveMember::~ScDPSaveMember()
+{
+}
+
+bool ScDPSaveMember::operator== ( const ScDPSaveMember& r ) const
+{
+ return aName == r.aName &&
+ nVisibleMode == r.nVisibleMode &&
+ nShowDetailsMode == r.nShowDetailsMode;
+}
+
+bool ScDPSaveMember::HasIsVisible() const
+{
+ return nVisibleMode != SC_DPSAVEMODE_DONTKNOW;
+}
+
+void ScDPSaveMember::SetIsVisible(bool bSet)
+{
+ nVisibleMode = sal_uInt16(bSet);
+}
+
+bool ScDPSaveMember::HasShowDetails() const
+{
+ return nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW;
+}
+
+void ScDPSaveMember::SetShowDetails(bool bSet)
+{
+ nShowDetailsMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveMember::SetName( const OUString& rNew )
+{
+ // Used only if the source member was renamed (groups).
+ // For UI renaming of members, a layout name must be used.
+
+ aName = rNew;
+}
+
+void ScDPSaveMember::SetLayoutName( const OUString& rName )
+{
+ mpLayoutName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveMember::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+void ScDPSaveMember::RemoveLayoutName()
+{
+ mpLayoutName.reset();
+}
+
+void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition )
+{
+ uno::Reference<beans::XPropertySet> xMembProp( xMember, uno::UNO_QUERY );
+ OSL_ENSURE( xMembProp.is(), "no properties at member" );
+ if ( !xMembProp.is() )
+ return;
+
+ // exceptions are caught at ScDPSaveData::WriteToSource
+
+ if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xMembProp,
+ SC_UNO_DP_ISVISIBLE, static_cast<bool>(nVisibleMode) );
+
+ if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xMembProp,
+ SC_UNO_DP_SHOWDETAILS, static_cast<bool>(nShowDetailsMode) );
+
+ if (mpLayoutName)
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
+
+ if ( nPosition >= 0 )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, SC_UNO_DP_POSITION, nPosition);
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPSaveMember::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*4, ' ');
+ cout << aIndent << "* member name: '" << aName << "'" << endl;
+
+ cout << aIndent << " + layout name: ";
+ if (mpLayoutName)
+ cout << "'" << *mpLayoutName << "'";
+ else
+ cout << "(none)";
+ cout << endl;
+
+ cout << aIndent << " + visibility: ";
+ if (nVisibleMode == SC_DPSAVEMODE_DONTKNOW)
+ cout << "(unknown)";
+ else
+ cout << (nVisibleMode ? "visible" : "hidden");
+ cout << endl;
+}
+
+#endif
+
+ScDPSaveDimension::ScDPSaveDimension(OUString _aName, bool bDataLayout) :
+ aName(std::move( _aName )),
+ bIsDataLayout( bDataLayout ),
+ bDupFlag( false ),
+ nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ),
+ nFunction( ScGeneralFunction::AUTO ),
+ nUsedHierarchy( -1 ),
+ nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ bRepeatItemLabels( false ),
+ bSubTotalDefault( true )
+{
+}
+
+ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
+ aName( r.aName ),
+ mpLayoutName( r.mpLayoutName ),
+ mpSubtotalName( r.mpSubtotalName ),
+ bIsDataLayout( r.bIsDataLayout ),
+ bDupFlag( r.bDupFlag ),
+ nOrientation( r.nOrientation ),
+ nFunction( r.nFunction ),
+ nUsedHierarchy( r.nUsedHierarchy ),
+ nShowEmptyMode( r.nShowEmptyMode ),
+ bRepeatItemLabels( r.bRepeatItemLabels ),
+ bSubTotalDefault( r.bSubTotalDefault ),
+ maSubTotalFuncs( r.maSubTotalFuncs )
+{
+ for (const ScDPSaveMember* pMem : r.maMemberList)
+ {
+ const OUString& rName = pMem->GetName();
+ std::unique_ptr<ScDPSaveMember> pNew(new ScDPSaveMember( *pMem ));
+ maMemberList.push_back( pNew.get() );
+ maMemberHash[rName] = std::move(pNew);
+ }
+ if (r.pReferenceValue)
+ pReferenceValue.reset( new sheet::DataPilotFieldReference( *(r.pReferenceValue) ) );
+ if (r.pSortInfo)
+ pSortInfo.reset( new sheet::DataPilotFieldSortInfo( *(r.pSortInfo) ) );
+ if (r.pAutoShowInfo)
+ pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo( *(r.pAutoShowInfo) ) );
+ if (r.pLayoutInfo)
+ pLayoutInfo.reset(new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) ));
+}
+
+ScDPSaveDimension::~ScDPSaveDimension()
+{
+ maMemberHash.clear();
+ pReferenceValue.reset();
+ pSortInfo.reset();
+ pAutoShowInfo.reset();
+ pLayoutInfo.reset();
+}
+
+bool ScDPSaveDimension::operator== ( const ScDPSaveDimension& r ) const
+{
+ if ( aName != r.aName ||
+ bIsDataLayout != r.bIsDataLayout ||
+ bDupFlag != r.bDupFlag ||
+ nOrientation != r.nOrientation ||
+ nFunction != r.nFunction ||
+ nUsedHierarchy != r.nUsedHierarchy ||
+ nShowEmptyMode != r.nShowEmptyMode ||
+ bRepeatItemLabels!= r.bRepeatItemLabels||
+ bSubTotalDefault != r.bSubTotalDefault ||
+ maSubTotalFuncs != r.maSubTotalFuncs )
+ return false;
+
+ if (maMemberHash.size() != r.maMemberHash.size() )
+ return false;
+
+ if (!std::equal(maMemberList.begin(), maMemberList.end(), r.maMemberList.begin(), r.maMemberList.end(),
+ [](const ScDPSaveMember* a, const ScDPSaveMember* b) { return *a == *b; }))
+ return false;
+
+ if( pReferenceValue && r.pReferenceValue )
+ {
+ if ( *pReferenceValue != *r.pReferenceValue )
+ {
+ return false;
+ }
+ }
+ else if ( pReferenceValue || r.pReferenceValue )
+ {
+ return false;
+ }
+ if( this->pSortInfo && r.pSortInfo )
+ {
+ if ( *this->pSortInfo != *r.pSortInfo )
+ {
+ return false;
+ }
+ }
+ else if ( this->pSortInfo || r.pSortInfo )
+ {
+ return false;
+ }
+ if( this->pAutoShowInfo && r.pAutoShowInfo )
+ {
+ if ( *this->pAutoShowInfo != *r.pAutoShowInfo )
+ {
+ return false;
+ }
+ }
+ else if ( this->pAutoShowInfo || r.pAutoShowInfo )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ScDPSaveDimension::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
+{
+ const OUString & rName = pMember->GetName();
+ auto aExisting = maMemberHash.find( rName );
+ auto tmp = pMember.get();
+ if ( aExisting == maMemberHash.end() )
+ {
+ maMemberHash[rName] = std::move(pMember);
+ }
+ else
+ {
+ std::erase(maMemberList, aExisting->second.get());
+ aExisting->second = std::move(pMember);
+ }
+ maMemberList.push_back( tmp );
+}
+
+void ScDPSaveDimension::SetName( const OUString& rNew )
+{
+ // Used only if the source dim was renamed (groups).
+ // For UI renaming of dimensions, the layout name must be used.
+
+ aName = rNew;
+}
+
+void ScDPSaveDimension::SetOrientation(css::sheet::DataPilotFieldOrientation nNew)
+{
+ nOrientation = nNew;
+}
+
+void ScDPSaveDimension::SetSubTotals(std::vector<ScGeneralFunction> && rFuncs)
+{
+ maSubTotalFuncs = std::move(rFuncs);
+ bSubTotalDefault = false;
+}
+
+bool ScDPSaveDimension::HasShowEmpty() const
+{
+ return nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW;
+}
+
+void ScDPSaveDimension::SetShowEmpty(bool bSet)
+{
+ nShowEmptyMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveDimension::SetRepeatItemLabels(bool bSet)
+{
+ bRepeatItemLabels = bSet;
+}
+
+void ScDPSaveDimension::SetFunction(ScGeneralFunction nNew)
+{
+ nFunction = nNew;
+}
+
+void ScDPSaveDimension::SetUsedHierarchy(tools::Long nNew)
+{
+ nUsedHierarchy = nNew;
+}
+
+void ScDPSaveDimension::SetSubtotalName(const OUString& rName)
+{
+ mpSubtotalName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveDimension::GetSubtotalName() const
+{
+ return mpSubtotalName;
+}
+
+void ScDPSaveDimension::RemoveSubtotalName()
+{
+ mpSubtotalName.reset();
+}
+
+bool ScDPSaveDimension::IsMemberNameInUse(const OUString& rName) const
+{
+ return std::any_of(maMemberList.begin(), maMemberList.end(), [&rName](const ScDPSaveMember* pMem) {
+ if (rName.equalsIgnoreAsciiCase(pMem->GetName()))
+ return true;
+
+ const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
+ return pLayoutName && rName.equalsIgnoreAsciiCase(*pLayoutName);
+ });
+}
+
+void ScDPSaveDimension::SetLayoutName(const OUString& rName)
+{
+ mpLayoutName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveDimension::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+void ScDPSaveDimension::RemoveLayoutName()
+{
+ mpLayoutName.reset();
+}
+
+void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew)
+{
+ if (pNew)
+ pReferenceValue.reset( new sheet::DataPilotFieldReference(*pNew) );
+ else
+ pReferenceValue.reset();
+}
+
+void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo* pNew)
+{
+ if (pNew)
+ pSortInfo.reset( new sheet::DataPilotFieldSortInfo(*pNew) );
+ else
+ pSortInfo.reset();
+}
+
+void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo* pNew)
+{
+ if (pNew)
+ pAutoShowInfo.reset( new sheet::DataPilotFieldAutoShowInfo(*pNew) );
+ else
+ pAutoShowInfo.reset();
+}
+
+void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo* pNew)
+{
+ if (pNew)
+ pLayoutInfo.reset( new sheet::DataPilotFieldLayoutInfo(*pNew) );
+ else
+ pLayoutInfo.reset();
+}
+
+void ScDPSaveDimension::SetCurrentPage( const OUString* pPage )
+{
+ // We use member's visibility attribute to filter by page dimension.
+
+ // pPage == nullptr -> all members visible.
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ bool bVisible = !pPage || pMem->GetName() == *pPage;
+ pMem->SetIsVisible(bVisible);
+ }
+}
+
+OUString ScDPSaveDimension::GetCurrentPage() const
+{
+ MemberList::const_iterator it = std::find_if(maMemberList.begin(), maMemberList.end(),
+ [](const ScDPSaveMember* pMem) { return pMem->GetIsVisible(); });
+ if (it != maMemberList.end())
+ return (*it)->GetName();
+
+ return OUString();
+}
+
+ScDPSaveMember* ScDPSaveDimension::GetExistingMemberByName(const OUString& rName)
+{
+ auto res = maMemberHash.find (rName);
+ if (res != maMemberHash.end())
+ return res->second.get();
+ return nullptr;
+}
+
+ScDPSaveMember* ScDPSaveDimension::GetMemberByName(const OUString& rName)
+{
+ auto res = maMemberHash.find (rName);
+ if (res != maMemberHash.end())
+ return res->second.get();
+
+ ScDPSaveMember* pNew = new ScDPSaveMember( rName );
+ maMemberHash[rName] = std::unique_ptr<ScDPSaveMember>(pNew);
+ maMemberList.push_back( pNew );
+ return pNew;
+}
+
+void ScDPSaveDimension::SetMemberPosition( const OUString& rName, sal_Int32 nNewPos )
+{
+ ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash
+
+ std::erase(maMemberList, pMember);
+
+ maMemberList.insert( maMemberList.begin() + nNewPos, pMember );
+}
+
+void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xDim )
+{
+ uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+ OSL_ENSURE( xDimProp.is(), "no properties at dimension" );
+ if ( xDimProp.is() )
+ {
+ // exceptions are caught at ScDPSaveData::WriteToSource
+
+ sheet::DataPilotFieldOrientation eOrient = nOrientation;
+ xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(eOrient) );
+
+ sal_Int16 eFunc = static_cast<sal_Int16>(nFunction);
+ xDimProp->setPropertyValue( SC_UNO_DP_FUNCTION2, uno::Any(eFunc) );
+
+ if ( nUsedHierarchy >= 0 )
+ {
+ xDimProp->setPropertyValue( SC_UNO_DP_USEDHIERARCHY, uno::Any(static_cast<sal_Int32>(nUsedHierarchy)) );
+ }
+
+ if ( pReferenceValue )
+ {
+ ;
+ xDimProp->setPropertyValue( SC_UNO_DP_REFVALUE, uno::Any(*pReferenceValue) );
+ }
+
+ if (mpLayoutName)
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_LAYOUTNAME, *mpLayoutName);
+
+ const std::optional<OUString> & pSubTotalName = GetSubtotalName();
+ if (pSubTotalName)
+ // Custom subtotal name, with '?' being replaced by the visible field name later.
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_FIELD_SUBTOTALNAME, *pSubTotalName);
+ }
+
+ // Level loop outside of maMemberList loop
+ // because SubTotals have to be set independently of known members
+
+ tools::Long nCount = maMemberHash.size();
+
+ tools::Long nHierCount = 0;
+ uno::Reference<container::XIndexAccess> xHiers;
+ uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
+ if ( xHierSupp.is() )
+ {
+ xHiers = new ScNameToIndexAccess(xHierSupp->getHierarchies());
+ nHierCount = xHiers->getCount();
+ }
+
+ bool bHasHiddenMember = false;
+
+ for (tools::Long nHier=0; nHier<nHierCount; nHier++)
+ {
+ tools::Long nLevCount = 0;
+ uno::Reference<container::XIndexAccess> xLevels;
+ uno::Reference<sheet::XLevelsSupplier> xLevSupp(xHiers->getByIndex(nHier), uno::UNO_QUERY);
+ if ( xLevSupp.is() )
+ {
+ uno::Reference<container::XNameAccess> xLevelsName = xLevSupp->getLevels();
+ xLevels = new ScNameToIndexAccess( xLevelsName );
+ nLevCount = xLevels->getCount();
+ }
+
+ for (tools::Long nLev=0; nLev<nLevCount; nLev++)
+ {
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLev), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
+ OSL_ENSURE( xLevProp.is(), "no properties at level" );
+ if ( xLevProp.is() )
+ {
+ if ( !bSubTotalDefault )
+ {
+ uno::Sequence<sal_Int16> aSeq(maSubTotalFuncs.size());
+ for(size_t i = 0; i < maSubTotalFuncs.size(); ++i)
+ aSeq.getArray()[i] = static_cast<sal_Int16>(maSubTotalFuncs[i]);
+ xLevProp->setPropertyValue( SC_UNO_DP_SUBTOTAL2, uno::Any(aSeq) );
+ }
+ if ( nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xLevProp,
+ SC_UNO_DP_SHOWEMPTY, static_cast<bool>(nShowEmptyMode) );
+
+ lcl_SetBoolProperty( xLevProp,
+ SC_UNO_DP_REPEATITEMLABELS, bRepeatItemLabels );
+
+ if ( pSortInfo )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_SORTING, *pSortInfo);
+
+ if ( pAutoShowInfo )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_AUTOSHOW, *pAutoShowInfo);
+
+ if ( pLayoutInfo )
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xLevProp, SC_UNO_DP_LAYOUT, *pLayoutInfo);
+
+ // exceptions are caught at ScDPSaveData::WriteToSource
+ }
+
+ if ( nCount > 0 )
+ {
+ uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevel, uno::UNO_QUERY );
+ if ( xMembSupp.is() )
+ {
+ uno::Reference<sheet::XMembersAccess> xMembers = xMembSupp->getMembers();
+ if ( xMembers.is() )
+ {
+ sal_Int32 nPosition = -1; // set position only in manual mode
+ if ( !pSortInfo || pSortInfo->Mode == sheet::DataPilotFieldSortMode::MANUAL )
+ nPosition = 0;
+
+ for (ScDPSaveMember* pMember : maMemberList)
+ {
+ if (!pMember->GetIsVisible())
+ bHasHiddenMember = true;
+ OUString aMemberName = pMember->GetName();
+ if ( xMembers->hasByName( aMemberName ) )
+ {
+ uno::Reference<uno::XInterface> xMemberInt(
+ xMembers->getByName(aMemberName), uno::UNO_QUERY);
+ pMember->WriteToSource( xMemberInt, nPosition );
+
+ if ( nPosition >= 0 )
+ ++nPosition; // increase if initialized
+ }
+ // missing member is no error
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (xDimProp.is())
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER, bHasHiddenMember);
+}
+
+void ScDPSaveDimension::UpdateMemberVisibility(const std::unordered_map<OUString, bool>& rData)
+{
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ const OUString& rMemName = pMem->GetName();
+ auto itr = rData.find(rMemName);
+ if (itr != rData.end())
+ pMem->SetIsVisible(itr->second);
+ }
+}
+
+bool ScDPSaveDimension::HasInvisibleMember() const
+{
+ return std::any_of(maMemberList.begin(), maMemberList.end(),
+ [](const ScDPSaveMember* pMem) { return !pMem->GetIsVisible(); });
+}
+
+void ScDPSaveDimension::RemoveObsoleteMembers(const MemberSetType& rMembers)
+{
+ MemberList aNew;
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ if (rMembers.count(pMem->GetName()))
+ {
+ // This member still exists.
+ aNew.push_back(pMem);
+ }
+ else
+ {
+ maMemberHash.erase(pMem->GetName());
+ }
+ }
+
+ maMemberList.swap(aNew);
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPSaveDimension::Dump(int nIndent) const
+{
+ static const char* pOrientNames[] = { "hidden", "column", "row", "page", "data" };
+ std::string aIndent(nIndent*4, ' ');
+
+ cout << aIndent << "* dimension name: '" << aName << "'" << endl;
+
+ cout << aIndent << " + orientation: ";
+ if (nOrientation <= DataPilotFieldOrientation_DATA)
+ cout << pOrientNames[static_cast<int>(nOrientation)];
+ else
+ cout << "(invalid)";
+ cout << endl;
+
+ cout << aIndent << " + layout name: ";
+ if (mpLayoutName)
+ cout << "'" << *mpLayoutName << "'";
+ else
+ cout << "(none)";
+ cout << endl;
+
+ cout << aIndent << " + subtotal name: ";
+ if (mpSubtotalName)
+ cout << "'" << *mpSubtotalName << "'";
+ else
+ cout << "(none)";
+ cout << endl;
+
+ cout << aIndent << " + is data layout: " << (bIsDataLayout ? "yes" : "no") << endl;
+ cout << aIndent << " + is duplicate: " << (bDupFlag ? "yes" : "no") << endl;
+
+ for (ScDPSaveMember* pMem : maMemberList)
+ {
+ pMem->Dump(nIndent+1);
+ }
+
+ cout << endl; // blank line
+}
+
+#endif
+
+ScDPSaveData::ScDPSaveData() :
+ nColumnGrandMode( SC_DPSAVEMODE_DONTKNOW ),
+ nRowGrandMode( SC_DPSAVEMODE_DONTKNOW ),
+ nIgnoreEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ bFilterButton( true ),
+ bDrillDown( true ),
+ bExpandCollapse( false ),
+ mbDimensionMembersBuilt(false)
+{
+}
+
+ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
+ nColumnGrandMode( r.nColumnGrandMode ),
+ nRowGrandMode( r.nRowGrandMode ),
+ nIgnoreEmptyMode( r.nIgnoreEmptyMode ),
+ nRepeatEmptyMode( r.nRepeatEmptyMode ),
+ bFilterButton( r.bFilterButton ),
+ bDrillDown( r.bDrillDown ),
+ bExpandCollapse( r.bExpandCollapse ),
+ mbDimensionMembersBuilt(r.mbDimensionMembersBuilt),
+ mpGrandTotalName(r.mpGrandTotalName)
+{
+ if ( r.pDimensionData )
+ pDimensionData.reset( new ScDPDimensionSaveData( *r.pDimensionData ) );
+
+ for (auto const& it : r.m_DimList)
+ {
+ m_DimList.push_back(std::make_unique<ScDPSaveDimension>(*it));
+ }
+}
+
+ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r )
+{
+ if ( &r != this )
+ {
+ this->~ScDPSaveData();
+ new( this ) ScDPSaveData ( r );
+ }
+ return *this;
+}
+
+bool ScDPSaveData::operator== ( const ScDPSaveData& r ) const
+{
+ if ( nColumnGrandMode != r.nColumnGrandMode ||
+ nRowGrandMode != r.nRowGrandMode ||
+ nIgnoreEmptyMode != r.nIgnoreEmptyMode ||
+ nRepeatEmptyMode != r.nRepeatEmptyMode ||
+ bFilterButton != r.bFilterButton ||
+ bDrillDown != r.bDrillDown ||
+ mbDimensionMembersBuilt != r.mbDimensionMembersBuilt)
+ return false;
+
+ if ( pDimensionData || r.pDimensionData )
+ if ( !pDimensionData || !r.pDimensionData || !( *pDimensionData == *r.pDimensionData ) )
+ return false;
+
+ if (!(::comphelper::ContainerUniquePtrEquals(m_DimList, r.m_DimList)))
+ return false;
+
+ if (mpGrandTotalName)
+ {
+ if (!r.mpGrandTotalName)
+ return false;
+ if (*mpGrandTotalName != *r.mpGrandTotalName)
+ return false;
+ }
+ else if (r.mpGrandTotalName)
+ return false;
+
+ return true;
+}
+
+ScDPSaveData::~ScDPSaveData()
+{
+}
+
+void ScDPSaveData::SetGrandTotalName(const OUString& rName)
+{
+ mpGrandTotalName = rName;
+}
+
+const std::optional<OUString> & ScDPSaveData::GetGrandTotalName() const
+{
+ return mpGrandTotalName;
+}
+
+namespace {
+
+class DimOrderInserter
+{
+ ScDPSaveData::DimOrderType& mrNames;
+public:
+ explicit DimOrderInserter(ScDPSaveData::DimOrderType& rNames) : mrNames(rNames) {}
+
+ void operator() (const ScDPSaveDimension* pDim)
+ {
+ size_t nRank = mrNames.size();
+ mrNames.emplace(ScGlobal::getCharClass().uppercase(pDim->GetName()), nRank);
+ }
+};
+
+}
+
+const ScDPSaveData::DimOrderType& ScDPSaveData::GetDimensionSortOrder() const
+{
+ if (!mpDimOrder)
+ {
+ mpDimOrder.reset(new DimOrderType);
+ std::vector<const ScDPSaveDimension*> aRowDims, aColDims;
+ GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
+ GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
+
+ std::for_each(aRowDims.begin(), aRowDims.end(), DimOrderInserter(*mpDimOrder));
+ std::for_each(aColDims.begin(), aColDims.end(), DimOrderInserter(*mpDimOrder));
+ }
+ return *mpDimOrder;
+}
+
+void ScDPSaveData::GetAllDimensionsByOrientation(
+ sheet::DataPilotFieldOrientation eOrientation, std::vector<const ScDPSaveDimension*>& rDims) const
+{
+ std::vector<const ScDPSaveDimension*> aDims;
+ for (auto const& it : m_DimList)
+ {
+ const ScDPSaveDimension& rDim = *it;
+ if (rDim.GetOrientation() != eOrientation)
+ continue;
+
+ aDims.push_back(&rDim);
+ }
+
+ rDims.swap(aDims);
+}
+
+void ScDPSaveData::AddDimension(ScDPSaveDimension* pDim)
+{
+ if (!pDim)
+ return;
+
+ CheckDuplicateName(*pDim);
+ m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pDim));
+
+ DimensionsChanged();
+}
+
+ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const OUString& rName)
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetName() == rName && !iter->IsDataLayout() )
+ return &(*iter);
+ }
+
+ return AppendNewDimension(rName, false);
+}
+
+ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(std::u16string_view rName) const
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetName() == rName && !iter->IsDataLayout() )
+ return &(*iter);
+ }
+ return nullptr; // don't create new
+}
+
+ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const OUString& rName)
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetName() == rName && !iter->IsDataLayout() )
+ return DuplicateDimension(rName);
+ }
+
+ return AppendNewDimension(rName, false);
+}
+
+ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
+{
+ ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
+ if (pDim)
+ return pDim;
+
+ return AppendNewDimension(OUString(), true);
+}
+
+ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
+{
+ for (auto const& iter : m_DimList)
+ {
+ if ( iter->IsDataLayout() )
+ return &(*iter);
+ }
+ return nullptr;
+}
+
+ScDPSaveDimension* ScDPSaveData::DuplicateDimension(std::u16string_view rName)
+{
+ // always insert new
+
+ ScDPSaveDimension* pOld = GetExistingDimensionByName(rName);
+ if (!pOld)
+ return nullptr;
+
+ ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld );
+ AddDimension(pNew);
+ return pNew;
+}
+
+void ScDPSaveData::RemoveDimensionByName(const OUString& rName)
+{
+ auto iter = std::find_if(m_DimList.begin(), m_DimList.end(),
+ [&rName](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
+ return rxDim->GetName() == rName && !rxDim->IsDataLayout(); });
+ if (iter != m_DimList.end())
+ {
+ m_DimList.erase(iter);
+ RemoveDuplicateNameCount(rName);
+ DimensionsChanged();
+ }
+}
+
+ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim )
+{
+ ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim );
+ AddDimension(pNew);
+ return *pNew;
+}
+
+ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(DataPilotFieldOrientation nOrientation)
+{
+ // return the innermost dimension for the given orientation,
+ // excluding data layout dimension
+
+ auto iter = std::find_if(m_DimList.rbegin(), m_DimList.rend(),
+ [&nOrientation](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
+ return rxDim->GetOrientation() == nOrientation && !rxDim->IsDataLayout(); });
+ if (iter != m_DimList.rend())
+ return iter->get();
+
+ return nullptr;
+}
+
+ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetOrientation() == eOrientation && !iter->IsDataLayout())
+ return &(*iter);
+ }
+ return nullptr;
+}
+
+tools::Long ScDPSaveData::GetDataDimensionCount() const
+{
+ tools::Long nDataCount = 0;
+
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetOrientation() == sheet::DataPilotFieldOrientation_DATA)
+ ++nDataCount;
+ }
+
+ return nDataCount;
+}
+
+void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, tools::Long nNew )
+{
+ // position (nNew) is counted within dimensions of the same orientation
+
+ DataPilotFieldOrientation nOrient = pDim->GetOrientation();
+
+ auto it = std::find_if(m_DimList.begin(), m_DimList.end(),
+ [&pDim](const std::unique_ptr<ScDPSaveDimension>& rxDim) { return pDim == rxDim.get(); });
+ if (it != m_DimList.end())
+ {
+ // Tell vector<unique_ptr> to give up ownership of this element. Don't
+ // delete this instance as it is re-inserted into the container later.
+ // coverity[leaked_storage] - re-inserted into the container later
+ it->release();
+ m_DimList.erase(it);
+ }
+
+ auto iterInsert = std::find_if(m_DimList.begin(), m_DimList.end(),
+ [&nOrient, &nNew](const std::unique_ptr<ScDPSaveDimension>& rxDim) {
+ if (rxDim->GetOrientation() == nOrient )
+ --nNew;
+ return nNew <= 0;
+ });
+
+ m_DimList.insert(iterInsert, std::unique_ptr<ScDPSaveDimension>(pDim));
+ DimensionsChanged();
+}
+
+void ScDPSaveData::SetColumnGrand(bool bSet)
+{
+ nColumnGrandMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetRowGrand(bool bSet)
+{
+ nRowGrandMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetIgnoreEmptyRows(bool bSet)
+{
+ nIgnoreEmptyMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetRepeatIfEmpty(bool bSet)
+{
+ nRepeatEmptyMode = sal_uInt16(bSet);
+}
+
+void ScDPSaveData::SetFilterButton(bool bSet)
+{
+ bFilterButton = bSet;
+}
+
+void ScDPSaveData::SetDrillDown(bool bSet)
+{
+ bDrillDown = bSet;
+}
+
+void ScDPSaveData::SetExpandCollapse(bool bSet)
+{
+ bExpandCollapse = bSet;
+}
+
+static void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nIntCount = xIntDims->getCount();
+ for (tools::Long nIntDim=0; nIntDim<nIntCount; nIntDim++)
+ {
+ uno::Reference<beans::XPropertySet> xDimProp(xIntDims->getByIndex(nIntDim), uno::UNO_QUERY);
+ if (xDimProp.is())
+ {
+ xDimProp->setPropertyValue( SC_UNO_DP_ORIENTATION, uno::Any(sheet::DataPilotFieldOrientation_HIDDEN) );
+ }
+ }
+}
+
+void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
+{
+ if (!xSource.is())
+ return;
+
+ // source options must be first!
+
+ uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY );
+ SAL_WARN_IF( !xSourceProp.is(), "sc.core", "no properties at source" );
+ if ( xSourceProp.is() )
+ {
+ // source options are not available for external sources
+ //TODO: use XPropertySetInfo to test for availability?
+
+ try
+ {
+ if ( nIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_IGNOREEMPTY, static_cast<bool>(nIgnoreEmptyMode) );
+ if ( nRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_REPEATEMPTY, static_cast<bool>(nRepeatEmptyMode) );
+ }
+ catch(uno::Exception&)
+ {
+ // no error
+ }
+
+ const std::optional<OUString> & pGrandTotalName = GetGrandTotalName();
+ if (pGrandTotalName)
+ ScUnoHelpFunctions::SetOptionalPropertyValue(xSourceProp, SC_UNO_DP_GRANDTOTAL_NAME, *pGrandTotalName);
+ }
+
+ // exceptions in the other calls are errors
+ try
+ {
+ // reset all orientations
+ //TODO: "forgetSettings" or similar at source ?????
+ //TODO: reset all duplicated dimensions, or reuse them below !!!
+ SAL_INFO("sc.core", "ScDPSaveData::WriteToSource");
+
+ lcl_ResetOrient( xSource );
+
+ uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+ uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+ tools::Long nIntCount = xIntDims->getCount();
+
+ for (const auto& rxDim : m_DimList)
+ {
+ OUString aName = rxDim->GetName();
+ OUString aCoreName = ScDPUtil::getSourceDimensionName(aName);
+
+ SAL_INFO("sc.core", aName);
+
+ bool bData = rxDim->IsDataLayout();
+
+ //TODO: getByName for ScDPSource, including DataLayoutDimension !!!!!!!!
+
+ bool bFound = false;
+ for (tools::Long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
+ {
+ uno::Reference<uno::XInterface> xIntDim(xIntDims->getByIndex(nIntDim),
+ uno::UNO_QUERY);
+ if ( bData )
+ {
+ uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
+ if ( xDimProp.is() )
+ {
+ bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
+ SC_UNO_DP_ISDATALAYOUT );
+ //TODO: error checking -- is "IsDataLayoutDimension" property required??
+ }
+ }
+ else
+ {
+ uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
+ if (xDimName.is() && xDimName->getName() == aCoreName)
+ bFound = true;
+ }
+
+ if (bFound)
+ {
+ if (rxDim->GetDupFlag())
+ {
+ uno::Reference<util::XCloneable> xCloneable(xIntDim, uno::UNO_QUERY);
+ SAL_WARN_IF(!xCloneable.is(), "sc.core", "cannot clone dimension");
+ if (xCloneable.is())
+ {
+ uno::Reference<util::XCloneable> xNew = xCloneable->createClone();
+ uno::Reference<container::XNamed> xNewName(xNew, uno::UNO_QUERY);
+ if (xNewName.is())
+ {
+ xNewName->setName(aName);
+ rxDim->WriteToSource(xNew);
+ }
+ }
+ }
+ else
+ rxDim->WriteToSource( xIntDim );
+ }
+ }
+ SAL_WARN_IF(!bFound, "sc.core", "WriteToSource: Dimension not found: " + aName + ".");
+ }
+
+ if ( xSourceProp.is() )
+ {
+ if ( nColumnGrandMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_COLGRAND, static_cast<bool>(nColumnGrandMode) );
+ if ( nRowGrandMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ SC_UNO_DP_ROWGRAND, static_cast<bool>(nRowGrandMode) );
+ }
+ }
+ catch(uno::Exception const &)
+ {
+ TOOLS_WARN_EXCEPTION("sc.core", "WriteToSource");
+ }
+}
+
+bool ScDPSaveData::IsEmpty() const
+{
+ for (auto const& iter : m_DimList)
+ {
+ if (iter->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !iter->IsDataLayout())
+ return false;
+ }
+ return true; // no entries that are not hidden
+}
+
+void ScDPSaveData::RemoveAllGroupDimensions( const OUString& rSrcDimName, std::vector<OUString>* pDeletedNames )
+{
+ if (!pDimensionData)
+ // No group dimensions exist. Nothing to do.
+ return;
+
+ // Remove numeric group dimension (exists once at most). No need to delete
+ // anything in save data (grouping was done inplace in an existing base
+ // dimension).
+ pDimensionData->RemoveNumGroupDimension(rSrcDimName);
+
+ // Remove named group dimension(s). Dimensions have to be removed from
+ // dimension save data and from save data too.
+ const ScDPSaveGroupDimension* pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
+ while ( pExistingGroup )
+ {
+ OUString aGroupDimName = pExistingGroup->GetGroupDimName();
+ pDimensionData->RemoveGroupDimension(aGroupDimName); // pExistingGroup is deleted
+
+ // also remove SaveData settings for the dimension that no longer exists
+ RemoveDimensionByName(aGroupDimName);
+
+ if (pDeletedNames)
+ pDeletedNames->push_back(aGroupDimName);
+
+ // see if there are more group dimensions
+ pExistingGroup = pDimensionData->GetGroupDimForBase(rSrcDimName);
+
+ if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
+ {
+ // still get the same group dimension?
+ OSL_FAIL("couldn't remove group dimension");
+ pExistingGroup = nullptr; // avoid endless loop
+ }
+ }
+}
+
+ScDPDimensionSaveData* ScDPSaveData::GetDimensionData()
+{
+ if (!pDimensionData)
+ pDimensionData.reset( new ScDPDimensionSaveData );
+ return pDimensionData.get();
+}
+
+void ScDPSaveData::SetDimensionData( const ScDPDimensionSaveData* pNew )
+{
+ if ( pNew )
+ pDimensionData.reset( new ScDPDimensionSaveData( *pNew ) );
+ else
+ pDimensionData.reset();
+}
+
+void ScDPSaveData::BuildAllDimensionMembers(ScDPTableData* pData)
+{
+ if (mbDimensionMembersBuilt)
+ return;
+
+ // First, build a dimension name-to-index map.
+ typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
+ NameIndexMap aMap;
+ tools::Long nColCount = pData->GetColumnCount();
+ for (tools::Long i = 0; i < nColCount; ++i)
+ aMap.emplace(pData->getDimensionName(i), i);
+
+ NameIndexMap::const_iterator itrEnd = aMap.end();
+
+ for (auto const& iter : m_DimList)
+ {
+ const OUString& rDimName = iter->GetName();
+ if (rDimName.isEmpty())
+ // empty dimension name. It must be data layout.
+ continue;
+
+ NameIndexMap::const_iterator itr = aMap.find(rDimName);
+ if (itr == itrEnd)
+ // dimension name not in the data. This should never happen!
+ continue;
+
+ tools::Long nDimIndex = itr->second;
+ const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
+ size_t nMemberCount = rMembers.size();
+ for (size_t j = 0; j < nMemberCount; ++j)
+ {
+ const ScDPItemData* pMemberData = pData->GetMemberById( nDimIndex, rMembers[j] );
+ OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
+ if (iter->GetExistingMemberByName(aMemName))
+ // this member instance already exists. nothing to do.
+ continue;
+
+ unique_ptr<ScDPSaveMember> pNewMember(new ScDPSaveMember(aMemName));
+ pNewMember->SetIsVisible(true);
+ iter->AddMember(std::move(pNewMember));
+ }
+ }
+
+ mbDimensionMembersBuilt = true;
+}
+
+void ScDPSaveData::SyncAllDimensionMembers(ScDPTableData* pData)
+{
+ typedef std::unordered_map<OUString, tools::Long> NameIndexMap;
+
+ // First, build a dimension name-to-index map.
+ NameIndexMap aMap;
+ tools::Long nColCount = pData->GetColumnCount();
+ for (tools::Long i = 0; i < nColCount; ++i)
+ aMap.emplace(pData->getDimensionName(i), i);
+
+ NameIndexMap::const_iterator itMapEnd = aMap.end();
+
+ for (auto const& it : m_DimList)
+ {
+ const OUString& rDimName = it->GetName();
+ if (rDimName.isEmpty())
+ // empty dimension name. It must be data layout.
+ continue;
+
+ NameIndexMap::const_iterator itMap = aMap.find(rDimName);
+ if (itMap == itMapEnd)
+ // dimension name not in the data. This should never happen!
+ continue;
+
+ ScDPSaveDimension::MemberSetType aMemNames;
+ tools::Long nDimIndex = itMap->second;
+ const std::vector<SCROW>& rMembers = pData->GetColumnEntries(nDimIndex);
+ size_t nMemberCount = rMembers.size();
+ for (size_t j = 0; j < nMemberCount; ++j)
+ {
+ const ScDPItemData* pMemberData = pData->GetMemberById(nDimIndex, rMembers[j]);
+ // ScDPCache::GetItemDataById() (via
+ // ScDPTableData::GetMemberById(),
+ // ScDPGroupTableData::GetMemberById() through
+ // GetCacheTable().getCache()) may return nullptr.
+ if (pMemberData)
+ {
+ OUString aMemName = pData->GetFormattedString(nDimIndex, *pMemberData, false);
+ aMemNames.insert(aMemName);
+ }
+ else
+ {
+ SAL_WARN("sc.core", "No pMemberData for nDimIndex " << nDimIndex << ", rMembers[j] " << rMembers[j]
+ << ", j " << j);
+ }
+ }
+
+ it->RemoveObsoleteMembers(aMemNames);
+ }
+}
+
+bool ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName) const
+{
+ ScDPSaveDimension* pDim = GetExistingDimensionByName(rDimName);
+ if (!pDim)
+ return false;
+
+ return pDim->HasInvisibleMember();
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPSaveData::Dump() const
+{
+ for (auto const& itDim : m_DimList)
+ {
+ const ScDPSaveDimension& rDim = *itDim;
+ rDim.Dump();
+ }
+}
+
+#endif
+
+void ScDPSaveData::CheckDuplicateName(ScDPSaveDimension& rDim)
+{
+ const OUString aName = ScDPUtil::getSourceDimensionName(rDim.GetName());
+ DupNameCountType::iterator it = maDupNameCounts.find(aName);
+ if (it != maDupNameCounts.end())
+ {
+ rDim.SetName(ScDPUtil::createDuplicateDimensionName(aName, ++it->second));
+ rDim.SetDupFlag(true);
+ }
+ else
+ // New name.
+ maDupNameCounts.emplace(aName, 0);
+}
+
+void ScDPSaveData::RemoveDuplicateNameCount(const OUString& rName)
+{
+ OUString aCoreName = rName;
+ if (ScDPUtil::isDuplicateDimension(rName))
+ aCoreName = ScDPUtil::getSourceDimensionName(rName);
+
+ DupNameCountType::iterator it = maDupNameCounts.find(aCoreName);
+ if (it == maDupNameCounts.end())
+ return;
+
+ if (!it->second)
+ {
+ maDupNameCounts.erase(it);
+ return;
+ }
+
+ --it->second;
+}
+
+ScDPSaveDimension* ScDPSaveData::AppendNewDimension(const OUString& rName, bool bDataLayout)
+{
+ if (ScDPUtil::isDuplicateDimension(rName))
+ // This call is for original dimensions only.
+ return nullptr;
+
+ ScDPSaveDimension* pNew = new ScDPSaveDimension(rName, bDataLayout);
+ m_DimList.push_back(std::unique_ptr<ScDPSaveDimension>(pNew));
+ if (!maDupNameCounts.count(rName))
+ maDupNameCounts.emplace(rName, 0);
+
+ DimensionsChanged();
+ return pNew;
+}
+
+void ScDPSaveData::DimensionsChanged()
+{
+ mpDimOrder.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpsdbtab.cxx b/sc/source/core/data/dpsdbtab.cxx
new file mode 100644
index 0000000000..b56217c27f
--- /dev/null
+++ b/sc/source/core/data/dpsdbtab.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 <dpsdbtab.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <dpfilteredcache.hxx>
+#include <document.hxx>
+#include <dpobject.hxx>
+
+#include <com/sun/star/sdb/CommandType.hpp>
+
+using namespace com::sun::star;
+
+using ::std::vector;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+
+sal_Int32 ScImportSourceDesc::GetCommandType() const
+{
+ sal_Int32 nSdbType = -1;
+
+ switch ( nType )
+ {
+ case sheet::DataImportMode_SQL: nSdbType = sdb::CommandType::COMMAND; break;
+ case sheet::DataImportMode_TABLE: nSdbType = sdb::CommandType::TABLE; break;
+ case sheet::DataImportMode_QUERY: nSdbType = sdb::CommandType::QUERY; break;
+ default:
+ ;
+ }
+ return nSdbType;
+}
+
+const ScDPCache* ScImportSourceDesc::CreateCache(const ScDPDimensionSaveData* pDimData) const
+{
+ if (!mpDoc)
+ return nullptr;
+
+ sal_Int32 nSdbType = GetCommandType();
+ if (nSdbType < 0)
+ return nullptr;
+
+ ScDPCollection::DBCaches& rCaches = mpDoc->GetDPCollection()->GetDBCaches();
+ return rCaches.getCache(nSdbType, aDBName, aObject, pDimData);
+}
+
+ScDatabaseDPData::ScDatabaseDPData(
+ const ScDocument* pDoc, const ScDPCache& rCache) :
+ ScDPTableData(pDoc),
+ aCacheTable(rCache)
+{
+}
+
+ScDatabaseDPData::~ScDatabaseDPData()
+{
+}
+
+void ScDatabaseDPData::DisposeData()
+{
+ //TODO: use OpenDatabase here?
+ aCacheTable.clear();
+}
+
+sal_Int32 ScDatabaseDPData::GetColumnCount()
+{
+ CreateCacheTable();
+ return GetCacheTable().getColSize();
+}
+
+OUString ScDatabaseDPData::getDimensionName(sal_Int32 nColumn)
+{
+ if (getIsDataLayoutDimension(nColumn))
+ {
+ //TODO: different internal and display names?
+ //return "Data";
+ return ScResId(STR_PIVOT_DATA);
+ }
+
+ CreateCacheTable();
+ return aCacheTable.getFieldName(static_cast<SCCOL>(nColumn));
+}
+
+bool ScDatabaseDPData::getIsDataLayoutDimension(sal_Int32 nColumn)
+{
+ return ( nColumn == GetCacheTable().getColSize());
+}
+
+bool ScDatabaseDPData::IsDateDimension(sal_Int32 /* nDim */)
+{
+ //TODO: later...
+ return false;
+}
+
+void ScDatabaseDPData::SetEmptyFlags( bool /* bIgnoreEmptyRows */, bool /* bRepeatIfEmpty */ )
+{
+ // not used for database data
+ //TODO: disable flags
+}
+
+void ScDatabaseDPData::CreateCacheTable()
+{
+ if (!aCacheTable.empty())
+ // cache table already created.
+ return;
+
+ aCacheTable.fillTable();
+}
+
+void ScDatabaseDPData::FilterCacheTable(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims)
+{
+ CreateCacheTable();
+ aCacheTable.filterByPageDimension(
+ rCriteria, (IsRepeatIfEmpty() ? std::move(rCatDims) : std::unordered_set<sal_Int32>()));
+}
+
+void ScDatabaseDPData::GetDrillDownData(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims, Sequence< Sequence<Any> >& rData)
+{
+ CreateCacheTable();
+ sal_Int32 nRowSize = aCacheTable.getRowSize();
+ if (!nRowSize)
+ return;
+
+ aCacheTable.filterTable(
+ rCriteria, rData, IsRepeatIfEmpty() ? std::move(rCatDims) : std::unordered_set<sal_Int32>());
+}
+
+void ScDatabaseDPData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
+{
+ CreateCacheTable();
+ CalcResultsFromCacheTable( aCacheTable, rInfo, bAutoShow);
+}
+
+const ScDPFilteredCache& ScDatabaseDPData::GetCacheTable() const
+{
+ return aCacheTable;
+}
+
+void ScDatabaseDPData::ReloadCacheTable()
+{
+ aCacheTable.clear();
+ CreateCacheTable();
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDatabaseDPData::Dump() const
+{
+ // TODO : Implement this.
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dpshttab.cxx b/sc/source/core/data/dpshttab.cxx
new file mode 100644
index 0000000000..a5fd1bd01d
--- /dev/null
+++ b/sc/source/core/data/dpshttab.cxx
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <unotools/charclass.hxx>
+
+#include <dpcache.hxx>
+#include <dpshttab.hxx>
+#include <document.hxx>
+#include <dpfilteredcache.hxx>
+#include <dpobject.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <rangenam.hxx>
+#include <queryentry.hxx>
+
+#include <osl/diagnose.h>
+
+#include <vector>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+using ::std::vector;
+
+ScSheetDPData::ScSheetDPData(const ScDocument* pD, const ScSheetSourceDesc& rDesc, const ScDPCache& rCache) :
+ ScDPTableData(pD),
+ aQuery ( rDesc.GetQueryParam() ),
+ bIgnoreEmptyRows( false ),
+ bRepeatIfEmpty(false),
+ aCacheTable(rCache)
+{
+ SCSIZE nEntryCount( aQuery.GetEntryCount());
+ for (SCSIZE j = 0; j < nEntryCount; ++j)
+ {
+ ScQueryEntry& rEntry = aQuery.GetEntry(j);
+ if (rEntry.bDoQuery)
+ {
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ sal_uInt32 nIndex = 0;
+ bool bNumber = pD->GetFormatTable()->IsNumberFormat(
+ rItem.maString.getString(), nIndex, rItem.mfVal);
+ rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ }
+ }
+ }
+}
+
+ScSheetDPData::~ScSheetDPData()
+{
+}
+
+void ScSheetDPData::DisposeData()
+{
+ aCacheTable.clear();
+}
+
+sal_Int32 ScSheetDPData::GetColumnCount()
+{
+ CreateCacheTable();
+ return aCacheTable.getColSize();
+}
+
+OUString ScSheetDPData::getDimensionName(sal_Int32 nColumn)
+{
+ CreateCacheTable();
+ if (getIsDataLayoutDimension(nColumn))
+ {
+ //TODO: different internal and display names?
+ //return "Data";
+ return ScResId(STR_PIVOT_DATA);
+ }
+ else if (nColumn >= aCacheTable.getColSize())
+ {
+ OSL_FAIL("getDimensionName: invalid dimension");
+ return OUString();
+ }
+ else
+ {
+ return aCacheTable.getFieldName(static_cast<SCCOL>(nColumn));
+ }
+}
+
+bool ScSheetDPData::IsDateDimension(sal_Int32 nDim)
+{
+ CreateCacheTable();
+ tools::Long nColCount = aCacheTable.getColSize();
+ if (getIsDataLayoutDimension(nDim))
+ {
+ return false;
+ }
+ else if (nDim >= nColCount)
+ {
+ OSL_FAIL("IsDateDimension: invalid dimension");
+ return false;
+ }
+ else
+ {
+ return GetCacheTable().getCache().IsDateDimension( nDim);
+ }
+}
+
+sal_uInt32 ScSheetDPData::GetNumberFormat(sal_Int32 nDim)
+{
+ CreateCacheTable();
+ if (getIsDataLayoutDimension(nDim))
+ {
+ return 0;
+ }
+ else if (nDim >= GetCacheTable().getColSize())
+ {
+ OSL_FAIL("GetNumberFormat: invalid dimension");
+ return 0;
+ }
+ else
+ {
+ return GetCacheTable().getCache().GetNumberFormat( nDim );
+ }
+}
+sal_uInt32 ScDPTableData::GetNumberFormatByIdx( NfIndexTableOffset eIdx )
+{
+ if( !mpDoc )
+ return 0;
+
+ if ( SvNumberFormatter* pFormatter = mpDoc->GetFormatTable() )
+ return pFormatter->GetFormatIndex( eIdx, LANGUAGE_SYSTEM );
+
+ return 0;
+}
+
+bool ScSheetDPData::getIsDataLayoutDimension(sal_Int32 nColumn)
+{
+ CreateCacheTable();
+ return (nColumn ==static_cast<tools::Long>( aCacheTable.getColSize()));
+}
+
+void ScSheetDPData::SetEmptyFlags( bool bIgnoreEmptyRowsP, bool bRepeatIfEmptyP )
+{
+ bIgnoreEmptyRows = bIgnoreEmptyRowsP;
+ bRepeatIfEmpty = bRepeatIfEmptyP;
+}
+
+bool ScSheetDPData::IsRepeatIfEmpty()
+{
+ return bRepeatIfEmpty;
+}
+
+void ScSheetDPData::CreateCacheTable()
+{
+ // Scan and store the data from the source range.
+ if (!aCacheTable.empty())
+ // already cached.
+ return;
+
+ aCacheTable.fillTable(aQuery, bIgnoreEmptyRows, bRepeatIfEmpty);
+}
+
+void ScSheetDPData::FilterCacheTable(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims)
+{
+ CreateCacheTable();
+ aCacheTable.filterByPageDimension(
+ rCriteria, (IsRepeatIfEmpty() ? rCatDims : std::unordered_set<sal_Int32>()));
+}
+
+void ScSheetDPData::GetDrillDownData(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims, Sequence< Sequence<Any> >& rData)
+{
+ CreateCacheTable();
+ sal_Int32 nRowSize = aCacheTable.getRowSize();
+ if (!nRowSize)
+ return;
+
+ aCacheTable.filterTable(
+ rCriteria, rData, IsRepeatIfEmpty() ? rCatDims : std::unordered_set<sal_Int32>());
+}
+
+void ScSheetDPData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
+{
+ CreateCacheTable();
+ CalcResultsFromCacheTable(aCacheTable, rInfo, bAutoShow);
+}
+
+const ScDPFilteredCache& ScSheetDPData::GetCacheTable() const
+{
+ return aCacheTable;
+}
+
+void ScSheetDPData::ReloadCacheTable()
+{
+ aCacheTable.clear();
+ CreateCacheTable();
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScSheetDPData::Dump() const
+{
+ // TODO : Implement this.
+}
+
+#endif
+
+ScSheetSourceDesc::ScSheetSourceDesc(ScDocument* pDoc) :
+ mpDoc(pDoc) {}
+
+void ScSheetSourceDesc::SetSourceRange(const ScRange& rRange)
+{
+ maSourceRange = rRange;
+ maRangeName.clear(); // overwrite existing range name if any.
+}
+
+const ScRange& ScSheetSourceDesc::GetSourceRange() const
+{
+ if (!maRangeName.isEmpty())
+ {
+ // Obtain the source range from the range name first.
+ maSourceRange = ScRange();
+ ScRangeName* pRangeName = mpDoc->GetRangeName();
+ do
+ {
+ if (!pRangeName)
+ break;
+
+ OUString aUpper = ScGlobal::getCharClass().uppercase(maRangeName);
+ const ScRangeData* pData = pRangeName->findByUpperName(aUpper);
+ if (!pData)
+ break;
+
+ // range name found. Fow now, we only use the first token and
+ // ignore the rest.
+ ScRange aRange;
+ if (!pData->IsReference(aRange))
+ break;
+
+ maSourceRange = aRange;
+ }
+ while (false);
+ }
+ return maSourceRange;
+}
+
+void ScSheetSourceDesc::SetRangeName(const OUString& rName)
+{
+ maRangeName = rName;
+}
+
+bool ScSheetSourceDesc::HasRangeName() const
+{
+ return !maRangeName.isEmpty();
+}
+
+void ScSheetSourceDesc::SetQueryParam(const ScQueryParam& rParam)
+{
+ maQueryParam = rParam;
+}
+
+bool ScSheetSourceDesc::operator== (const ScSheetSourceDesc& rOther) const
+{
+ return maSourceRange == rOther.maSourceRange &&
+ maRangeName == rOther.maRangeName &&
+ maQueryParam == rOther.maQueryParam;
+}
+
+const ScDPCache* ScSheetSourceDesc::CreateCache(const ScDPDimensionSaveData* pDimData) const
+{
+ if (!mpDoc)
+ return nullptr;
+
+ TranslateId pErrId = CheckSourceRange();
+ if (pErrId)
+ {
+ OSL_FAIL( "Error Create Cache" );
+ return nullptr;
+ }
+
+ // All cache instances are managed centrally by ScDPCollection.
+ ScDPCollection* pDPs = mpDoc->GetDPCollection();
+ if (HasRangeName())
+ {
+ // Name-based data source.
+ ScDPCollection::NameCaches& rCaches = pDPs->GetNameCaches();
+ return rCaches.getCache(GetRangeName(), GetSourceRange(), pDimData);
+ }
+
+ ScDPCollection::SheetCaches& rCaches = pDPs->GetSheetCaches();
+ return rCaches.getCache(GetSourceRange(), pDimData);
+}
+
+TranslateId ScSheetSourceDesc::CheckSourceRange() const
+{
+ if (!mpDoc)
+ return STR_ERR_DATAPILOTSOURCE;
+
+ // Make sure the range is valid and sane.
+ const ScRange& rSrcRange = GetSourceRange();
+ if (!rSrcRange.IsValid())
+ return STR_ERR_DATAPILOTSOURCE;
+
+ if (rSrcRange.aStart.Col() > rSrcRange.aEnd.Col() || rSrcRange.aStart.Row() > rSrcRange.aEnd.Row())
+ return STR_ERR_DATAPILOTSOURCE;
+
+ return {};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dptabdat.cxx b/sc/source/core/data/dptabdat.cxx
new file mode 100644
index 0000000000..7b3c807124
--- /dev/null
+++ b/sc/source/core/data/dptabdat.cxx
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dptabdat.hxx>
+#include <dpcache.hxx>
+#include <dpfilteredcache.hxx>
+#include <dptabres.hxx>
+
+#include <osl/diagnose.h>
+#include <tools/date.hxx>
+
+
+using namespace ::com::sun::star;
+using ::std::vector;
+
+ScDPTableData::CalcInfo::CalcInfo() :
+ pInitState( nullptr ),
+ pColRoot( nullptr ),
+ pRowRoot( nullptr )
+{
+}
+
+ScDPTableData::ScDPTableData(const ScDocument* pDoc) :
+ mpDoc(pDoc)
+{
+ nLastDateVal = nLastHier = nLastLevel = nLastRet = -1; // invalid
+
+ //TODO: reset before new calculation (in case the base date is changed)
+}
+
+ScDPTableData::~ScDPTableData()
+{
+}
+
+OUString ScDPTableData::GetFormattedString(sal_Int32 nDim, const ScDPItemData& rItem, bool bLocaleIndependent) const
+{
+ const ScDPCache& rCache = GetCacheTable().getCache();
+ return rCache.GetFormattedString(nDim, rItem, bLocaleIndependent);
+}
+
+tools::Long ScDPTableData::GetDatePart( tools::Long nDateVal, tools::Long nHierarchy, tools::Long nLevel )
+{
+ if ( nDateVal == nLastDateVal && nHierarchy == nLastHier && nLevel == nLastLevel )
+ return nLastRet;
+
+ Date aDate( 30,12,1899 ); //TODO: get from source data (and cache here)
+ aDate.AddDays( nDateVal);
+
+ tools::Long nRet = 0;
+ switch (nHierarchy)
+ {
+ case SC_DAPI_HIERARCHY_QUARTER:
+ switch (nLevel)
+ {
+ case 0: nRet = aDate.GetYear(); break;
+ case 1: nRet = (aDate.GetMonth()-1) / 3 + 1; break;
+ case 2: nRet = aDate.GetMonth(); break;
+ case 3: nRet = aDate.GetDay(); break;
+ default:
+ OSL_FAIL("GetDatePart: wrong level");
+ }
+ break;
+ case SC_DAPI_HIERARCHY_WEEK:
+ switch (nLevel)
+ {
+ //TODO: use settings for different definitions
+ case 0: nRet = aDate.GetYear(); break; //!...
+ case 1: nRet = aDate.GetWeekOfYear(); break;
+ case 2: nRet = static_cast<tools::Long>(aDate.GetDayOfWeek()); break;
+ default:
+ OSL_FAIL("GetDatePart: wrong level");
+ }
+ break;
+ default:
+ OSL_FAIL("GetDatePart: wrong hierarchy");
+ }
+
+ nLastDateVal = nDateVal;
+ nLastHier = nHierarchy;
+ nLastLevel = nLevel;
+ nLastRet = nRet;
+
+ return nRet;
+}
+
+bool ScDPTableData::IsRepeatIfEmpty()
+{
+ return false;
+}
+
+sal_uInt32 ScDPTableData::GetNumberFormat(sal_Int32)
+{
+ return 0; // default format
+}
+
+bool ScDPTableData::IsBaseForGroup(sal_Int32) const
+{
+ return false; // always false
+}
+
+sal_Int32 ScDPTableData::GetGroupBase(sal_Int32) const
+{
+ return -1; // always none
+}
+
+bool ScDPTableData::IsNumOrDateGroup(sal_Int32) const
+{
+ return false; // always false
+}
+
+bool ScDPTableData::IsInGroup( const ScDPItemData&, sal_Int32,
+ const ScDPItemData&, sal_Int32 ) const
+{
+ OSL_FAIL("IsInGroup shouldn't be called for non-group data");
+ return false;
+}
+
+bool ScDPTableData::HasCommonElement( const ScDPItemData&, sal_Int32,
+ const ScDPItemData&, sal_Int32 ) const
+{
+ OSL_FAIL("HasCommonElement shouldn't be called for non-group data");
+ return false;
+}
+void ScDPTableData::FillRowDataFromCacheTable(sal_Int32 nRow, const ScDPFilteredCache& rCacheTable,
+ const CalcInfo& rInfo, CalcRowData& rData)
+{
+ // column dimensions
+ GetItemData(rCacheTable, nRow, rInfo.aColLevelDims, rData.aColData);
+
+ // row dimensions
+ GetItemData(rCacheTable, nRow, rInfo.aRowLevelDims, rData.aRowData);
+
+ // page dimensions
+ GetItemData(rCacheTable, nRow, rInfo.aPageDims, rData.aPageData);
+
+ tools::Long nCacheColumnCount = rCacheTable.getCache().GetColumnCount();
+ sal_Int32 n = rInfo.aDataSrcCols.size();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ tools::Long nDim = rInfo.aDataSrcCols[i];
+ rData.aValues.emplace_back( );
+ // #i111435# GetItemData needs dimension indexes including groups,
+ // so the index must be checked here (groups aren't useful as data fields).
+ if ( nDim < nCacheColumnCount )
+ {
+ ScDPValue& rVal = rData.aValues.back();
+ rCacheTable.getValue( rVal, static_cast<SCCOL>(nDim), static_cast<SCROW>(nRow));
+ }
+ }
+}
+
+void ScDPTableData::ProcessRowData(CalcInfo& rInfo, const CalcRowData& rData, bool bAutoShow)
+{
+ if (!bAutoShow)
+ {
+ LateInitParams aColParams(rInfo.aColDims, rInfo.aColLevels, false);
+ LateInitParams aRowParams(rInfo.aRowDims, rInfo.aRowLevels, true);
+ // root always init child
+ aColParams.SetInitChild(true);
+ aColParams.SetInitAllChildren( false);
+ aRowParams.SetInitChild(true);
+ aRowParams.SetInitAllChildren( false);
+
+ rInfo.pColRoot->LateInitFrom(aColParams, rData.aColData, 0, *rInfo.pInitState);
+ rInfo.pRowRoot->LateInitFrom(aRowParams, rData.aRowData, 0, *rInfo.pInitState);
+ }
+
+ if ( ( !rInfo.pColRoot->GetChildDimension() || rInfo.pColRoot->GetChildDimension()->IsValidEntry(rData.aColData) ) &&
+ ( !rInfo.pRowRoot->GetChildDimension() || rInfo.pRowRoot->GetChildDimension()->IsValidEntry(rData.aRowData) ) )
+ {
+ //TODO: single process method with ColMembers, RowMembers and data !!!
+ if (rInfo.pColRoot->GetChildDimension())
+ {
+ vector<SCROW> aEmptyData;
+ rInfo.pColRoot->GetChildDimension()->ProcessData(rData.aColData, nullptr, aEmptyData, rData.aValues);
+ }
+
+ rInfo.pRowRoot->ProcessData(rData.aRowData, rInfo.pColRoot->GetChildDimension(),
+ rData.aColData, rData.aValues);
+ }
+}
+
+void ScDPTableData::CalcResultsFromCacheTable(const ScDPFilteredCache& rCacheTable, CalcInfo& rInfo, bool bAutoShow)
+{
+ sal_Int32 nRowSize = rCacheTable.getRowSize();
+ for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
+ {
+ sal_Int32 nLastRow;
+ if (!rCacheTable.isRowActive(nRow, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ CalcRowData aData;
+ FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
+ ProcessRowData(rInfo, aData, bAutoShow);
+ }
+}
+
+void ScDPTableData::GetItemData(const ScDPFilteredCache& rCacheTable, sal_Int32 nRow,
+ const vector<sal_Int32>& rDims, vector<SCROW>& rItemData)
+{
+ sal_Int32 nDimSize = rDims.size();
+ rItemData.reserve(rItemData.size() + nDimSize);
+ for (sal_Int32 i = 0; i < nDimSize; ++i)
+ {
+ sal_Int32 nDim = rDims[i];
+
+ if (getIsDataLayoutDimension(nDim))
+ {
+ rItemData.push_back( -1 );
+ continue;
+ }
+
+ nDim = GetSourceDim( nDim );
+ if ( nDim >= rCacheTable.getCache().GetColumnCount() )
+ continue;
+
+ SCROW nId= rCacheTable.getCache().GetItemDataId( static_cast<SCCOL>(nDim), static_cast<SCROW>(nRow), IsRepeatIfEmpty());
+ rItemData.push_back( nId );
+ }
+}
+
+sal_Int32 ScDPTableData::GetMembersCount( sal_Int32 nDim )
+{
+ if ( nDim > MAXCOL )
+ return 0;
+ return GetCacheTable().getFieldEntries( nDim ).size();
+}
+
+const ScDPItemData* ScDPTableData::GetMemberByIndex( sal_Int32 nDim, sal_Int32 nIndex )
+{
+ if ( nIndex >= GetMembersCount( nDim ) )
+ return nullptr;
+
+ const ::std::vector<SCROW>& nMembers = GetCacheTable().getFieldEntries( nDim );
+
+ return GetCacheTable().getCache().GetItemDataById( static_cast<SCCOL>(nDim), static_cast<SCROW>(nMembers[nIndex]) );
+}
+
+const ScDPItemData* ScDPTableData::GetMemberById( sal_Int32 nDim, sal_Int32 nId)
+{
+ return GetCacheTable().getCache().GetItemDataById(nDim, static_cast<SCROW>(nId));
+}
+
+const std::vector< SCROW >& ScDPTableData::GetColumnEntries( sal_Int32 nColumn )
+{
+ return GetCacheTable().getFieldEntries( nColumn );
+}
+
+sal_Int32 ScDPTableData::GetSourceDim( sal_Int32 nDim )
+{
+ return nDim;
+}
+
+sal_Int32 ScDPTableData::Compare( sal_Int32 nDim, sal_Int32 nDataId1, sal_Int32 nDataId2)
+{
+ if ( getIsDataLayoutDimension(nDim) )
+ return 0;
+
+ if ( nDataId1 > nDataId2 )
+ return 1;
+ else if ( nDataId1 == nDataId2 )
+ return 0;
+ else
+ return -1;
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPTableData::Dump() const
+{
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dptabres.cxx b/sc/source/core/data/dptabres.cxx
new file mode 100644
index 0000000000..1a4020e56e
--- /dev/null
+++ b/sc/source/core/data/dptabres.cxx
@@ -0,0 +1,4117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dptabres.hxx>
+
+#include <dptabdat.hxx>
+#include <dptabsrc.hxx>
+#include <global.hxx>
+#include <subtotal.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <dpitemdata.hxx>
+#include <generalfunction.hxx>
+
+#include <document.hxx>
+#include <dpresfilter.hxx>
+#include <dputil.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+#include <math.h>
+#include <float.h>
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <unordered_map>
+
+#include <com/sun/star/sheet/DataResultFlags.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::std::pair;
+using ::com::sun::star::uno::Sequence;
+
+namespace {
+
+const TranslateId aFuncStrIds[] = // matching enum ScSubTotalFunc
+{
+ {}, // SUBTOTAL_FUNC_NONE
+ STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE
+ STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT
+ STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2
+ STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX
+ STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN
+ STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD
+ STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD
+ STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP
+ STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM
+ STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR
+ STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VARP
+ STR_FUN_TEXT_MEDIAN, // SUBTOTAL_FUNC_MED
+ {} // SUBTOTAL_FUNC_SELECTION_COUNT - not used for pivot table
+};
+
+bool lcl_SearchMember( const std::vector<std::unique_ptr<ScDPResultMember>>& list, SCROW nOrder, SCROW& rIndex)
+{
+ bool bFound = false;
+ SCROW nLo = 0;
+ SCROW nHi = list.size() - 1;
+ SCROW nIndex;
+ while (nLo <= nHi)
+ {
+ nIndex = (nLo + nHi) / 2;
+ if ( list[nIndex]->GetOrder() < nOrder )
+ nLo = nIndex + 1;
+ else
+ {
+ nHi = nIndex - 1;
+ if ( list[nIndex]->GetOrder() == nOrder )
+ {
+ bFound = true;
+ nLo = nIndex;
+ }
+ }
+ }
+ rIndex = nLo;
+ return bFound;
+}
+
+class FilterStack
+{
+ std::vector<ScDPResultFilter>& mrFilters;
+public:
+ explicit FilterStack(std::vector<ScDPResultFilter>& rFilters) : mrFilters(rFilters) {}
+
+ void pushDimName(const OUString& rName, bool bDataLayout)
+ {
+ mrFilters.emplace_back(rName, bDataLayout);
+ }
+
+ void pushDimValue(const OUString& rValueName, const OUString& rValue)
+ {
+ ScDPResultFilter& rFilter = mrFilters.back();
+ rFilter.maValueName = rValueName;
+ rFilter.maValue = rValue;
+ rFilter.mbHasValue = true;
+ }
+
+ ~FilterStack()
+ {
+ ScDPResultFilter& rFilter = mrFilters.back();
+ if (rFilter.mbHasValue)
+ rFilter.mbHasValue = false;
+ else
+ mrFilters.pop_back();
+ }
+};
+
+// function objects for sorting of the column and row members:
+
+class ScDPRowMembersOrder
+{
+ ScDPResultDimension& rDimension;
+ tools::Long nMeasure;
+ bool bAscending;
+
+public:
+ ScDPRowMembersOrder( ScDPResultDimension& rDim, tools::Long nM, bool bAsc ) :
+ rDimension(rDim),
+ nMeasure(nM),
+ bAscending(bAsc)
+ {}
+
+ bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
+};
+
+class ScDPColMembersOrder
+{
+ ScDPDataDimension& rDimension;
+ tools::Long nMeasure;
+ bool bAscending;
+
+public:
+ ScDPColMembersOrder( ScDPDataDimension& rDim, tools::Long nM, bool bAsc ) :
+ rDimension(rDim),
+ nMeasure(nM),
+ bAscending(bAsc)
+ {}
+
+ bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
+};
+
+}
+
+static bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, tools::Long nMeasure, bool bAscending )
+{
+ // members can be NULL if used for rows
+
+ ScDPSubTotalState aEmptyState;
+ const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
+ const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
+
+ bool bError1 = pAgg1 && pAgg1->HasError();
+ bool bError2 = pAgg2 && pAgg2->HasError();
+ if ( bError1 )
+ return false; // errors are always sorted at the end
+ else if ( bError2 )
+ return true; // errors are always sorted at the end
+ else
+ {
+ double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
+ double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
+
+ // compare values
+ // don't have to check approxEqual, as this is the only sort criterion
+
+ return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
+ }
+}
+
+static bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, tools::Long nMeasure )
+{
+ // members can be NULL if used for rows
+
+ ScDPSubTotalState aEmptyState;
+ const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
+ const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
+
+ bool bError1 = pAgg1 && pAgg1->HasError();
+ bool bError2 = pAgg2 && pAgg2->HasError();
+ if ( bError1 )
+ {
+ if ( bError2 )
+ return true; // equal
+ else
+ return false;
+ }
+ else if ( bError2 )
+ return false;
+ else
+ {
+ double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
+ double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
+
+ // compare values
+ // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
+
+ return rtl::math::approxEqual( fVal1, fVal2 );
+ }
+}
+
+bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
+{
+ const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
+ const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
+
+// make the hide item to the largest order.
+ if ( !pMember1->IsVisible() || !pMember2->IsVisible() )
+ return pMember1->IsVisible();
+ const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ;
+ const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot();
+ // GetDataRoot can be NULL if there was no data.
+ // IsVisible == false can happen after AutoShow.
+ return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
+}
+
+bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
+{
+ const ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
+ const ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
+ bool bHide1 = pDataMember1 && !pDataMember1->IsVisible();
+ bool bHide2 = pDataMember2 && !pDataMember2->IsVisible();
+ if ( bHide1 || bHide2 )
+ return !bHide1;
+ return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
+}
+
+ScDPInitState::Member::Member(tools::Long nSrcIndex, SCROW nNameIndex) :
+ mnSrcIndex(nSrcIndex), mnNameIndex(nNameIndex) {}
+
+void ScDPInitState::AddMember( tools::Long nSourceIndex, SCROW nMember )
+{
+ maMembers.emplace_back(nSourceIndex, nMember);
+}
+
+void ScDPInitState::RemoveMember()
+{
+ OSL_ENSURE(!maMembers.empty(), "ScDPInitState::RemoveMember: Attempt to remove member while empty.");
+ if (!maMembers.empty())
+ maMembers.pop_back();
+}
+
+namespace {
+
+#if DUMP_PIVOT_TABLE
+void dumpRow(
+ const OUString& rType, const OUString& rName, const ScDPAggData* pAggData,
+ ScDocument* pDoc, ScAddress& rPos )
+{
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+ pDoc->SetString( nCol++, nRow, nTab, rType );
+ pDoc->SetString( nCol++, nRow, nTab, rName );
+ while ( pAggData )
+ {
+ pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
+ pAggData = pAggData->GetExistingChild();
+ }
+ rPos.SetRow( nRow + 1 );
+}
+
+void indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
+{
+ SCCOL nCol = rPos.Col();
+ SCTAB nTab = rPos.Tab();
+
+ OUString aString;
+ for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
+ {
+ aString = pDoc->GetString(nCol, nRow, nTab);
+ if (!aString.isEmpty())
+ {
+ aString = " " + aString;
+ pDoc->SetString( nCol, nRow, nTab, aString );
+ }
+ }
+}
+#endif
+
+}
+
+ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) :
+ pColResRoot(pColRoot), pRowResRoot(pRowRoot)
+{
+ // These arrays should never be empty as the terminating value must be present at all times.
+ maColVisible.push_back(-1);
+ maColSorted.push_back(-1);
+ maRowVisible.push_back(-1);
+ maRowSorted.push_back(-1);
+}
+
+void ScDPRunningTotalState::AddColIndex( sal_Int32 nVisible, tools::Long nSorted )
+{
+ maColVisible.back() = nVisible;
+ maColVisible.push_back(-1);
+
+ maColSorted.back() = nSorted;
+ maColSorted.push_back(-1);
+}
+
+void ScDPRunningTotalState::AddRowIndex( sal_Int32 nVisible, tools::Long nSorted )
+{
+ maRowVisible.back() = nVisible;
+ maRowVisible.push_back(-1);
+
+ maRowSorted.back() = nSorted;
+ maRowSorted.push_back(-1);
+}
+
+void ScDPRunningTotalState::RemoveColIndex()
+{
+ OSL_ENSURE(!maColVisible.empty() && !maColSorted.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
+ if (maColVisible.size() >= 2)
+ {
+ maColVisible.pop_back();
+ maColVisible.back() = -1;
+ }
+
+ if (maColSorted.size() >= 2)
+ {
+ maColSorted.pop_back();
+ maColSorted.back() = -1;
+ }
+}
+
+void ScDPRunningTotalState::RemoveRowIndex()
+{
+ OSL_ENSURE(!maRowVisible.empty() && !maRowSorted.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
+ if (maRowVisible.size() >= 2)
+ {
+ maRowVisible.pop_back();
+ maRowVisible.back() = -1;
+ }
+
+ if (maRowSorted.size() >= 2)
+ {
+ maRowSorted.pop_back();
+ maRowSorted.back() = -1;
+ }
+}
+
+ScDPRelativePos::ScDPRelativePos( tools::Long nBase, tools::Long nDir ) :
+ nBasePos( nBase ),
+ nDirection( nDir )
+{
+}
+
+void ScDPAggData::Update( const ScDPValue& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
+{
+ if (nCount<0) // error?
+ return; // nothing more...
+
+ if (rNext.meType == ScDPValue::Empty)
+ return;
+
+ if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
+ rSubState.eColForce != rSubState.eRowForce )
+ return;
+ if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
+ if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
+
+ if ( eFunc == SUBTOTAL_FUNC_NONE )
+ return;
+
+ if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors
+ {
+ if (rNext.meType == ScDPValue::Error)
+ {
+ nCount = -1; // -1 for error (not for CNT2)
+ return;
+ }
+ if (rNext.meType == ScDPValue::String)
+ return; // ignore
+ }
+
+ ++nCount; // for all functions
+
+ switch (eFunc)
+ {
+ case SUBTOTAL_FUNC_SUM:
+ case SUBTOTAL_FUNC_AVE:
+ if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
+ nCount = -1; // -1 for error
+ break;
+ case SUBTOTAL_FUNC_PROD:
+ if ( nCount == 1 ) // copy first value (fVal is initialized to 0)
+ fVal = rNext.mfValue;
+ else if ( !SubTotal::SafeMult( fVal, rNext.mfValue ) )
+ nCount = -1; // -1 for error
+ break;
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2:
+ // nothing more than incrementing nCount
+ break;
+ case SUBTOTAL_FUNC_MAX:
+ if ( nCount == 1 || rNext.mfValue > fVal )
+ fVal = rNext.mfValue;
+ break;
+ case SUBTOTAL_FUNC_MIN:
+ if ( nCount == 1 || rNext.mfValue < fVal )
+ fVal = rNext.mfValue;
+ break;
+ case SUBTOTAL_FUNC_STD:
+ case SUBTOTAL_FUNC_STDP:
+ case SUBTOTAL_FUNC_VAR:
+ case SUBTOTAL_FUNC_VARP:
+ maWelford.update( rNext.mfValue);
+ break;
+ case SUBTOTAL_FUNC_MED:
+ {
+ auto aIter = std::upper_bound(mSortedValues.begin(), mSortedValues.end(), rNext.mfValue);
+ if (aIter == mSortedValues.end())
+ mSortedValues.push_back(rNext.mfValue);
+ else
+ mSortedValues.insert(aIter, rNext.mfValue);
+ }
+ break;
+ default:
+ OSL_FAIL("invalid function");
+ }
+}
+
+void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
+{
+ // calculate the original result
+ // (without reference value, used as the basis for reference value calculation)
+
+ // called several times at the cross-section of several subtotals - don't calculate twice then
+ if ( IsCalculated() )
+ return;
+
+ if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
+ if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
+
+ if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension
+ {
+ nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc.
+ return;
+ }
+
+ // check the error conditions for the selected function
+
+ bool bError = false;
+ switch (eFunc)
+ {
+ case SUBTOTAL_FUNC_SUM:
+ case SUBTOTAL_FUNC_PROD:
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2:
+ bError = ( nCount < 0 ); // only real errors
+ break;
+
+ case SUBTOTAL_FUNC_AVE:
+ case SUBTOTAL_FUNC_MED:
+ case SUBTOTAL_FUNC_MAX:
+ case SUBTOTAL_FUNC_MIN:
+ bError = ( nCount <= 0 ); // no data is an error
+ break;
+
+ case SUBTOTAL_FUNC_STDP:
+ case SUBTOTAL_FUNC_VARP:
+ bError = ( nCount <= 0 ); // no data is an error
+ assert(bError || nCount == static_cast<sal_Int64>(maWelford.getCount()));
+ break;
+
+ case SUBTOTAL_FUNC_STD:
+ case SUBTOTAL_FUNC_VAR:
+ bError = ( nCount < 2 ); // need at least 2 values
+ assert(bError || nCount == static_cast<sal_Int64>(maWelford.getCount()));
+ break;
+
+ default:
+ OSL_FAIL("invalid function");
+ }
+
+ // calculate the selected function
+
+ double fResult = 0.0;
+ if ( !bError )
+ {
+ switch (eFunc)
+ {
+ case SUBTOTAL_FUNC_MAX:
+ case SUBTOTAL_FUNC_MIN:
+ case SUBTOTAL_FUNC_SUM:
+ case SUBTOTAL_FUNC_PROD:
+ // different error conditions are handled above
+ fResult = fVal;
+ break;
+
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2:
+ fResult = nCount;
+ break;
+
+ case SUBTOTAL_FUNC_AVE:
+ if ( nCount > 0 )
+ fResult = fVal / static_cast<double>(nCount);
+ break;
+
+ case SUBTOTAL_FUNC_STD:
+ if ( nCount >= 2 )
+ {
+ fResult = maWelford.getVarianceSample();
+ if (fResult < 0.0)
+ bError = true;
+ else
+ fResult = sqrt( fResult);
+ }
+ break;
+ case SUBTOTAL_FUNC_VAR:
+ if ( nCount >= 2 )
+ fResult = maWelford.getVarianceSample();
+ break;
+ case SUBTOTAL_FUNC_STDP:
+ if ( nCount > 0 )
+ {
+ fResult = maWelford.getVariancePopulation();
+ if (fResult < 0.0)
+ bError = true;
+ else
+ fResult = sqrt( fResult);
+ }
+ break;
+ case SUBTOTAL_FUNC_VARP:
+ if ( nCount > 0 )
+ fResult = maWelford.getVariancePopulation();
+ break;
+ case SUBTOTAL_FUNC_MED:
+ {
+ size_t nSize = mSortedValues.size();
+ if (nSize > 0)
+ {
+ assert(nSize == static_cast<size_t>(nCount));
+ if ((nSize % 2) == 1)
+ fResult = mSortedValues[nSize / 2];
+ else
+ fResult = (mSortedValues[nSize / 2 - 1] + mSortedValues[nSize / 2]) / 2.0;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL("invalid function");
+ }
+ }
+
+ bool bEmpty = ( nCount == 0 ); // no data
+
+ // store the result
+ // Empty is checked first, so empty results are shown empty even for "average" etc.
+ // If these results should be treated as errors in reference value calculations,
+ // a separate state value (EMPTY_ERROR) is needed.
+ // Now, for compatibility, empty "average" results are counted as 0.
+
+ if ( bEmpty )
+ nCount = SC_DPAGG_RESULT_EMPTY;
+ else if ( bError )
+ nCount = SC_DPAGG_RESULT_ERROR;
+ else
+ nCount = SC_DPAGG_RESULT_VALID;
+
+ if ( bEmpty || bError )
+ fResult = 0.0; // default, in case the state is later modified
+
+ fVal = fResult; // used directly from now on
+ fAux = 0.0; // used for running total or original result of reference value
+}
+
+bool ScDPAggData::IsCalculated() const
+{
+ return ( nCount <= SC_DPAGG_RESULT_EMPTY );
+}
+
+double ScDPAggData::GetResult() const
+{
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ return fVal; // use calculated value
+}
+
+bool ScDPAggData::HasError() const
+{
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ return ( nCount == SC_DPAGG_RESULT_ERROR );
+}
+
+bool ScDPAggData::HasData() const
+{
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error
+}
+
+void ScDPAggData::SetResult( double fNew )
+{
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ fVal = fNew; // don't reset error flag
+}
+
+void ScDPAggData::SetError()
+{
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ nCount = SC_DPAGG_RESULT_ERROR;
+}
+
+void ScDPAggData::SetEmpty( bool bSet )
+{
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ if ( bSet )
+ nCount = SC_DPAGG_RESULT_EMPTY;
+ else
+ nCount = SC_DPAGG_RESULT_VALID;
+}
+
+double ScDPAggData::GetAuxiliary() const
+{
+ // after Calculate, fAux is used as auxiliary value for running totals and reference values
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ return fAux;
+}
+
+void ScDPAggData::SetAuxiliary( double fNew )
+{
+ // after Calculate, fAux is used as auxiliary value for running totals and reference values
+ assert( IsCalculated() && "ScDPAggData not calculated" );
+
+ fAux = fNew;
+}
+
+ScDPAggData* ScDPAggData::GetChild()
+{
+ if (!pChild)
+ pChild.reset( new ScDPAggData );
+ return pChild.get();
+}
+
+void ScDPAggData::Reset()
+{
+ maWelford = WelfordRunner();
+ fVal = 0.0;
+ fAux = 0.0;
+ nCount = SC_DPAGG_EMPTY;
+ pChild.reset();
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPAggData::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*2, ' ');
+ std::cout << aIndent << "* ";
+ if (IsCalculated())
+ std::cout << GetResult();
+ else
+ std::cout << "not calculated";
+
+ std::cout << " [val=" << fVal << "; aux=" << fAux << "; count=" << nCount << "]" << std::endl;
+}
+#endif
+
+ScDPRowTotals::ScDPRowTotals() :
+ bIsInColRoot( false )
+{
+}
+
+ScDPRowTotals::~ScDPRowTotals()
+{
+}
+
+static ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, tools::Long nMeasure )
+{
+ OSL_ENSURE( nMeasure >= 0, "GetColTotal: no measure" );
+
+ ScDPAggData* pAgg = pFirst;
+ tools::Long nSkip = nMeasure;
+
+ // subtotal settings are ignored - column/row totals exist once per measure
+
+ for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
+ pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created
+
+ if ( !pAgg->IsCalculated() )
+ {
+ // for first use, simulate an empty calculation
+ ScDPSubTotalState aEmptyState;
+ pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
+ }
+
+ return pAgg;
+}
+
+ScDPAggData* ScDPRowTotals::GetRowTotal( tools::Long nMeasure )
+{
+ return lcl_GetChildTotal( &aRowTotal, nMeasure );
+}
+
+ScDPAggData* ScDPRowTotals::GetGrandTotal( tools::Long nMeasure )
+{
+ return lcl_GetChildTotal( &aGrandTotal, nMeasure );
+}
+
+static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, tools::Long nFuncNo )
+{
+ ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE;
+ if ( pLevel )
+ {
+ //TODO: direct access via ScDPLevel
+
+ uno::Sequence<sal_Int16> aSeq = pLevel->getSubTotals();
+ tools::Long nSequence = aSeq.getLength();
+ if ( nSequence && aSeq[0] != sheet::GeneralFunction2::AUTO )
+ {
+ // For manual subtotals, "automatic" is added as first function.
+ // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
+ // returned as the first function then.
+
+ --nFuncNo; // keep NONE for first (check below), move the other entries
+ }
+
+ if ( nFuncNo >= 0 && nFuncNo < nSequence )
+ {
+ ScGeneralFunction eUser = static_cast<ScGeneralFunction>(aSeq.getConstArray()[nFuncNo]);
+ if (eUser != ScGeneralFunction::AUTO)
+ eRet = ScDPUtil::toSubTotalFunc(eUser);
+ }
+ }
+ return eRet;
+}
+
+ScDPResultData::ScDPResultData( ScDPSource& rSrc ) :
+ mrSource(rSrc),
+ bLateInit( false ),
+ bDataAtCol( false ),
+ bDataAtRow( false )
+{
+}
+
+ScDPResultData::~ScDPResultData()
+{
+}
+
+void ScDPResultData::SetMeasureData(
+ std::vector<ScSubTotalFunc>& rFunctions, std::vector<sheet::DataPilotFieldReference>& rRefs,
+ std::vector<sheet::DataPilotFieldOrientation>& rRefOrient, std::vector<OUString>& rNames )
+{
+ // We need to have at least one measure data at all times.
+
+ maMeasureFuncs.swap(rFunctions);
+ if (maMeasureFuncs.empty())
+ maMeasureFuncs.push_back(SUBTOTAL_FUNC_NONE);
+
+ maMeasureRefs.swap(rRefs);
+ if (maMeasureRefs.empty())
+ maMeasureRefs.emplace_back(); // default ctor is ok.
+
+ maMeasureRefOrients.swap(rRefOrient);
+ if (maMeasureRefOrients.empty())
+ maMeasureRefOrients.push_back(sheet::DataPilotFieldOrientation_HIDDEN);
+
+ maMeasureNames.swap(rNames);
+ if (maMeasureNames.empty())
+ maMeasureNames.push_back(ScResId(STR_EMPTYDATA));
+}
+
+void ScDPResultData::SetDataLayoutOrientation( sheet::DataPilotFieldOrientation nOrient )
+{
+ bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
+ bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
+}
+
+void ScDPResultData::SetLateInit( bool bSet )
+{
+ bLateInit = bSet;
+}
+
+tools::Long ScDPResultData::GetColStartMeasure() const
+{
+ if (maMeasureFuncs.size() == 1)
+ return 0;
+
+ return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
+}
+
+tools::Long ScDPResultData::GetRowStartMeasure() const
+{
+ if (maMeasureFuncs.size() == 1)
+ return 0;
+
+ return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
+}
+
+ScSubTotalFunc ScDPResultData::GetMeasureFunction(tools::Long nMeasure) const
+{
+ OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureFuncs.size(), "bumm");
+ return maMeasureFuncs[nMeasure];
+}
+
+const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(tools::Long nMeasure) const
+{
+ OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureRefs.size(), "bumm");
+ return maMeasureRefs[nMeasure];
+}
+
+sheet::DataPilotFieldOrientation ScDPResultData::GetMeasureRefOrient(tools::Long nMeasure) const
+{
+ OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureRefOrients.size(), "bumm");
+ return maMeasureRefOrients[nMeasure];
+}
+
+OUString ScDPResultData::GetMeasureString(tools::Long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
+{
+ // with bForce==true, return function instead of "result" for single measure
+ // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
+ rbTotalResult = false;
+ if ( nMeasure < 0 || (maMeasureFuncs.size() == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE) )
+ {
+ // for user-specified subtotal function with all measures,
+ // display only function name
+ assert(unsigned(eForceFunc) < SAL_N_ELEMENTS(aFuncStrIds));
+ if ( eForceFunc != SUBTOTAL_FUNC_NONE )
+ return ScResId(aFuncStrIds[eForceFunc]);
+
+ rbTotalResult = true;
+ return ScResId(STR_TABLE_ERGEBNIS);
+ }
+ else
+ {
+ OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureFuncs.size(), "bumm");
+ const ScDPDimension* pDataDim = mrSource.GetDataDimension(nMeasure);
+ if (pDataDim)
+ {
+ const std::optional<OUString> & pLayoutName = pDataDim->GetLayoutName();
+ if (pLayoutName)
+ return *pLayoutName;
+ }
+
+ ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
+ GetMeasureFunction(nMeasure) : eForceFunc;
+
+ return ScDPUtil::getDisplayedMeasureName(maMeasureNames[nMeasure], eFunc);
+ }
+}
+
+OUString ScDPResultData::GetMeasureDimensionName(tools::Long nMeasure) const
+{
+ if ( nMeasure < 0 )
+ {
+ OSL_FAIL("GetMeasureDimensionName: negative");
+ return "***";
+ }
+
+ return mrSource.GetDataDimName(nMeasure);
+}
+
+bool ScDPResultData::IsBaseForGroup( tools::Long nDim ) const
+{
+ return mrSource.GetData()->IsBaseForGroup(nDim);
+}
+
+tools::Long ScDPResultData::GetGroupBase( tools::Long nGroupDim ) const
+{
+ return mrSource.GetData()->GetGroupBase(nGroupDim);
+}
+
+bool ScDPResultData::IsNumOrDateGroup( tools::Long nDim ) const
+{
+ return mrSource.GetData()->IsNumOrDateGroup(nDim);
+}
+
+bool ScDPResultData::IsInGroup( SCROW nGroupDataId, tools::Long nGroupIndex,
+ const ScDPItemData& rBaseData, tools::Long nBaseIndex ) const
+{
+ const ScDPItemData* pGroupData = mrSource.GetItemDataById(nGroupIndex , nGroupDataId);
+ if ( pGroupData )
+ return mrSource.GetData()->IsInGroup(*pGroupData, nGroupIndex, rBaseData, nBaseIndex);
+ else
+ return false;
+}
+
+bool ScDPResultData::HasCommonElement( SCROW nFirstDataId, tools::Long nFirstIndex,
+ const ScDPItemData& rSecondData, tools::Long nSecondIndex ) const
+{
+ const ScDPItemData* pFirstData = mrSource.GetItemDataById(nFirstIndex , nFirstDataId);
+ if ( pFirstData )
+ return mrSource.GetData()->HasCommonElement(*pFirstData, nFirstIndex, rSecondData, nSecondIndex);
+ else
+ return false;
+}
+
+ResultMembers& ScDPResultData::GetDimResultMembers(tools::Long nDim, const ScDPDimension* pDim, ScDPLevel* pLevel) const
+{
+ if (nDim < static_cast<tools::Long>(maDimMembers.size()) && maDimMembers[nDim])
+ return *maDimMembers[nDim];
+
+ if (nDim >= static_cast<tools::Long>(maDimMembers.size()))
+ maDimMembers.resize(nDim+1);
+
+ std::unique_ptr<ResultMembers> pResultMembers(new ResultMembers());
+ // global order is used to initialize aMembers, so it doesn't have to be looked at later
+ const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder();
+
+ ScDPMembers* pMembers = pLevel->GetMembersObject();
+ tools::Long nMembCount = pMembers->getCount();
+ for (tools::Long i = 0; i < nMembCount; ++i)
+ {
+ tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
+ ScDPMember* pMember = pMembers->getByIndex(nSorted);
+ if (!pResultMembers->FindMember(pMember->GetItemDataId()))
+ {
+ ScDPParentDimData aNew(i, pDim, pLevel, pMember);
+ pResultMembers->InsertMember(aNew);
+ }
+ }
+
+ maDimMembers[nDim] = std::move(pResultMembers);
+ return *maDimMembers[nDim];
+}
+
+ScDPResultMember::ScDPResultMember(
+ const ScDPResultData* pData, const ScDPParentDimData& rParentDimData ) :
+ pResultData( pData ),
+ aParentDimData( rParentDimData ),
+ bHasElements( false ),
+ bForceSubTotal( false ),
+ bHasHiddenDetails( false ),
+ bInitialized( false ),
+ bAutoHidden( false ),
+ nMemberStep( 1 )
+{
+ // pParentLevel/pMemberDesc is 0 for root members
+}
+
+ScDPResultMember::ScDPResultMember(
+ const ScDPResultData* pData, bool bForceSub ) :
+ pResultData( pData ),
+ bHasElements( false ),
+ bForceSubTotal( bForceSub ),
+ bHasHiddenDetails( false ),
+ bInitialized( false ),
+ bAutoHidden( false ),
+ nMemberStep( 1 )
+{
+}
+ScDPResultMember::~ScDPResultMember()
+{
+}
+
+OUString ScDPResultMember::GetName() const
+{
+ const ScDPMember* pMemberDesc = GetDPMember();
+ if (pMemberDesc)
+ return pMemberDesc->GetNameStr( false );
+ else
+ return ScResId(STR_PIVOT_TOTAL); // root member
+}
+
+OUString ScDPResultMember::GetDisplayName( bool bLocaleIndependent ) const
+{
+ const ScDPMember* pDPMember = GetDPMember();
+ if (!pDPMember)
+ return OUString();
+
+ ScDPItemData aItem(pDPMember->FillItemData());
+ if (aParentDimData.mpParentDim)
+ {
+ tools::Long nDim = aParentDimData.mpParentDim->GetDimension();
+ return pResultData->GetSource().GetData()->GetFormattedString(nDim, aItem, bLocaleIndependent);
+ }
+
+ return aItem.GetString();
+}
+
+ScDPItemData ScDPResultMember::FillItemData() const
+{
+ const ScDPMember* pMemberDesc = GetDPMember();
+ if (pMemberDesc)
+ return pMemberDesc->FillItemData();
+ return ScDPItemData(ScResId(STR_PIVOT_TOTAL)); // root member
+}
+
+bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const
+{
+ //TODO: store ScDPMember pointer instead of ScDPMember ???
+ const ScDPMember* pMemberDesc = GetDPMember();
+ if (pMemberDesc)
+ return pMemberDesc->IsNamedItem(nIndex);
+ return false;
+}
+
+bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const
+{
+ if ( !IsValid() )
+ return false;
+
+ const ScDPResultDimension* pChildDim = GetChildDimension();
+ if (pChildDim)
+ {
+ if (aMembers.size() < 2)
+ return false;
+
+ vector<SCROW>::const_iterator itr = aMembers.begin();
+ vector<SCROW> aChildMembers(++itr, aMembers.end());
+ return pChildDim->IsValidEntry(aChildMembers);
+ }
+ else
+ return true;
+}
+
+void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
+ size_t nPos, ScDPInitState& rInitState ,
+ bool bInitChild )
+{
+ // with LateInit, initialize only those members that have data
+ if ( pResultData->IsLateInit() )
+ return;
+
+ bInitialized = true;
+
+ if (nPos >= ppDim.size())
+ return;
+
+ // skip child dimension if details are not shown
+ if ( GetDPMember() && !GetDPMember()->getShowDetails() )
+ {
+ // Show DataLayout dimension
+ nMemberStep = 1;
+ while ( nPos < ppDim.size() )
+ {
+ if ( ppDim[nPos]->getIsDataLayoutDimension() )
+ {
+ if ( !pChildDimension )
+ pChildDimension.reset( new ScDPResultDimension( pResultData ) );
+ pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , false );
+ return;
+ }
+ else
+ { //find next dim
+ nPos ++;
+ nMemberStep ++;
+ }
+ }
+ bHasHiddenDetails = true; // only if there is a next dimension
+ return;
+ }
+
+ if ( bInitChild )
+ {
+ pChildDimension.reset( new ScDPResultDimension( pResultData ) );
+ pChildDimension->InitFrom(ppDim, ppLev, nPos, rInitState);
+ }
+}
+
+void ScDPResultMember::LateInitFrom(
+ LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
+{
+ // without LateInit, everything has already been initialized
+ if ( !pResultData->IsLateInit() )
+ return;
+
+ bInitialized = true;
+
+ if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/)
+ // No next dimension. Bail out.
+ return;
+
+ // skip child dimension if details are not shown
+ if ( GetDPMember() && !GetDPMember()->getShowDetails() )
+ {
+ // Show DataLayout dimension
+ nMemberStep = 1;
+ while ( !rParams.IsEnd( nPos ) )
+ {
+ if ( rParams.GetDim( nPos )->getIsDataLayoutDimension() )
+ {
+ if ( !pChildDimension )
+ pChildDimension.reset( new ScDPResultDimension( pResultData ) );
+
+ // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
+ // not for following members of parent dimensions
+ bool bWasInitChild = rParams.GetInitChild();
+ rParams.SetInitChild( false );
+ pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
+ rParams.SetInitChild( bWasInitChild );
+ return;
+ }
+ else
+ { //find next dim
+ nPos ++;
+ nMemberStep ++;
+ }
+ }
+ bHasHiddenDetails = true; // only if there is a next dimension
+ return;
+ }
+
+ // LateInitFrom is called several times...
+ if ( rParams.GetInitChild() )
+ {
+ if ( !pChildDimension )
+ pChildDimension.reset( new ScDPResultDimension( pResultData ) );
+ pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
+ }
+}
+
+bool ScDPResultMember::IsSubTotalInTitle(tools::Long nMeasure) const
+{
+ bool bRet = false;
+ if ( pChildDimension && /*pParentLevel*/GetParentLevel() &&
+ /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
+ {
+ tools::Long nUserSubStart;
+ tools::Long nSubTotals = GetSubTotalCount( &nUserSubStart );
+ nSubTotals -= nUserSubStart; // visible count
+ if ( nSubTotals )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted
+
+ // only a single subtotal row will be shown in the outline title row
+ if ( nSubTotals == 1 )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+tools::Long ScDPResultMember::GetSize(tools::Long nMeasure) const
+{
+ if ( !IsVisible() )
+ return 0;
+ const ScDPLevel* pParentLevel = GetParentLevel();
+ tools::Long nExtraSpace = 0;
+ if ( pParentLevel && pParentLevel->IsAddEmpty() )
+ ++nExtraSpace;
+
+ if ( pChildDimension )
+ {
+ // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
+ if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
+ ++nExtraSpace;
+
+ tools::Long nSize = pChildDimension->GetSize(nMeasure);
+ tools::Long nUserSubStart;
+ tools::Long nUserSubCount = GetSubTotalCount( &nUserSubStart );
+ nUserSubCount -= nUserSubStart; // for output size, use visible count
+ if ( nUserSubCount )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nSize += pResultData->GetMeasureCount() * nUserSubCount;
+ else
+ nSize += nUserSubCount;
+ }
+ return nSize + nExtraSpace;
+ }
+ else
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ return pResultData->GetMeasureCount() + nExtraSpace;
+ else
+ return 1 + nExtraSpace;
+ }
+}
+
+bool ScDPResultMember::IsVisible() const
+{
+ if (!bInitialized)
+ return false;
+
+ if (!IsValid())
+ return false;
+
+ if (bHasElements)
+ return true;
+
+ // not initialized -> shouldn't be there at all
+ // (allocated only to preserve ordering)
+ const ScDPLevel* pParentLevel = GetParentLevel();
+
+ return (pParentLevel && pParentLevel->getShowEmpty());
+}
+
+bool ScDPResultMember::IsValid() const
+{
+ // non-Valid members are left out of calculation
+
+ // was member set no invisible at the DataPilotSource?
+ const ScDPMember* pMemberDesc = GetDPMember();
+ if ( pMemberDesc && !pMemberDesc->isVisible() )
+ return false;
+
+ if ( bAutoHidden )
+ return false;
+
+ return true;
+}
+
+tools::Long ScDPResultMember::GetSubTotalCount( tools::Long* pUserSubStart ) const
+{
+ if ( pUserSubStart )
+ *pUserSubStart = 0; // default
+
+ const ScDPLevel* pParentLevel = GetParentLevel();
+
+ if ( bForceSubTotal ) // set if needed for root members
+ return 1; // grand total is always "automatic"
+ else if ( pParentLevel )
+ {
+ //TODO: direct access via ScDPLevel
+
+ uno::Sequence<sal_Int16> aSeq = pParentLevel->getSubTotals();
+ tools::Long nSequence = aSeq.getLength();
+ if ( nSequence && aSeq[0] != sheet::GeneralFunction2::AUTO )
+ {
+ // For manual subtotals, always add "automatic" as first function
+ // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
+
+ ++nSequence;
+ if ( pUserSubStart )
+ *pUserSubStart = 1; // visible subtotals start at 1
+ }
+ return nSequence;
+ }
+ else
+ return 0;
+}
+
+void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim,
+ const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues )
+{
+ SetHasElements();
+
+ if (pChildDimension)
+ pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
+
+ if ( !pDataRoot )
+ {
+ pDataRoot.reset( new ScDPDataMember( pResultData, nullptr ) );
+ if ( pDataDim )
+ pDataRoot->InitFrom( pDataDim ); // recursive
+ }
+
+ ScDPSubTotalState aSubState; // initial state
+
+ tools::Long nUserSubCount = GetSubTotalCount();
+
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !pChildDimension )
+ nUserSubCount = 1;
+
+ const ScDPLevel* pParentLevel = GetParentLevel();
+
+ for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
+ {
+ // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
+ if ( pChildDimension && nUserSubCount > 1 )
+ {
+ aSubState.nRowSubTotalFunc = nUserPos;
+ aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
+ }
+
+ pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
+ }
+}
+
+/**
+ * Parse subtotal string and replace all occurrences of '?' with the caption
+ * string. Do ensure that escaped characters are not translated.
+ */
+static OUString lcl_parseSubtotalName(std::u16string_view rSubStr, std::u16string_view rCaption)
+{
+ OUStringBuffer aNewStr;
+ sal_Int32 n = rSubStr.size();
+ bool bEscaped = false;
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ sal_Unicode c = rSubStr[i];
+ if (!bEscaped && c == '\\')
+ {
+ bEscaped = true;
+ continue;
+ }
+
+ if (!bEscaped && c == '?')
+ aNewStr.append(rCaption);
+ else
+ aNewStr.append(c);
+ bEscaped = false;
+ }
+ return aNewStr.makeStringAndClear();
+}
+
+void ScDPResultMember::FillMemberResults(
+ uno::Sequence<sheet::MemberResult>* pSequences, tools::Long& rPos, tools::Long nMeasure, bool bRoot,
+ const OUString* pMemberName, const OUString* pMemberCaption )
+{
+ // IsVisible() test is in ScDPResultDimension::FillMemberResults
+ // (not on data layout dimension)
+
+ if (!pSequences->hasElements())
+ // empty sequence. Bail out.
+ return;
+
+ tools::Long nSize = GetSize(nMeasure);
+ sheet::MemberResult* pArray = pSequences->getArray();
+ OSL_ENSURE( rPos+nSize <= pSequences->getLength(), "bumm" );
+
+ bool bIsNumeric = false;
+ double fValue = std::numeric_limits<double>::quiet_NaN();
+ OUString aName;
+ if ( pMemberName ) // if pMemberName != NULL, use instead of real member name
+ {
+ aName = *pMemberName;
+ }
+ else
+ {
+ ScDPItemData aItemData(FillItemData());
+ if (aParentDimData.mpParentDim)
+ {
+ tools::Long nDim = aParentDimData.mpParentDim->GetDimension();
+ aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData, false);
+ }
+ else
+ {
+ tools::Long nDim = -1;
+ const ScDPMember* pMem = GetDPMember();
+ if (pMem)
+ nDim = pMem->GetDim();
+ aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData, false);
+ }
+
+ ScDPItemData::Type eType = aItemData.GetType();
+ bIsNumeric = eType == ScDPItemData::Value || eType == ScDPItemData::GroupValue;
+ // IsValue() is not identical to bIsNumeric, i.e.
+ // ScDPItemData::GroupValue is excluded and not stored in the double,
+ // so even if the item is numeric the Value may be NaN.
+ if (aItemData.IsValue())
+ fValue = aItemData.GetValue();
+ }
+
+ const ScDPDimension* pParentDim = GetParentDim();
+ if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) )
+ {
+ // Numeric group dimensions use numeric entries for proper sorting,
+ // but the group titles must be output as text.
+ bIsNumeric = false;
+ }
+
+ OUString aCaption = aName;
+ const ScDPMember* pMemberDesc = GetDPMember();
+ if (pMemberDesc)
+ {
+ const std::optional<OUString> & pLayoutName = pMemberDesc->GetLayoutName();
+ if (pLayoutName)
+ {
+ aCaption = *pLayoutName;
+ bIsNumeric = false; // layout name is always non-numeric.
+ }
+ }
+
+ if ( pMemberCaption ) // use pMemberCaption if != NULL
+ aCaption = *pMemberCaption;
+ if (aCaption.isEmpty())
+ aCaption = ScResId(STR_EMPTYDATA);
+
+ if (bIsNumeric)
+ pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
+ else
+ pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
+
+ const ScDPLevel* pParentLevel = GetParentLevel();
+ if ( nSize && !bRoot ) // root is overwritten by first dimension
+ {
+ pArray[rPos].Name = aName;
+ pArray[rPos].Caption = aCaption;
+ pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER;
+ pArray[rPos].Value = fValue;
+
+ // set "continue" flag (removed for subtotals later)
+ for (tools::Long i=1; i<nSize; i++)
+ {
+ pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE;
+ // tdf#113002 - add numeric flag to recurring data fields
+ if (bIsNumeric)
+ pArray[rPos + i].Flags |= sheet::MemberResultFlags::NUMERIC;
+ }
+
+ if ( pParentLevel && pParentLevel->getRepeatItemLabels() )
+ {
+ tools::Long nSizeNonEmpty = nSize;
+ if ( pParentLevel->IsAddEmpty() )
+ --nSizeNonEmpty;
+ for (tools::Long i=1; i<nSizeNonEmpty; i++)
+ {
+ pArray[rPos+i].Name = aName;
+ pArray[rPos+i].Caption = aCaption;
+ pArray[rPos+i].Flags |= sheet::MemberResultFlags::HASMEMBER;
+ pArray[rPos+i].Value = fValue;
+ }
+ }
+ }
+
+ tools::Long nExtraSpace = 0;
+ if ( pParentLevel && pParentLevel->IsAddEmpty() )
+ ++nExtraSpace;
+
+ bool bTitleLine = false;
+ if ( pParentLevel && pParentLevel->IsOutlineLayout() )
+ bTitleLine = true;
+
+ // if the subtotals are shown at the top (title row) in outline layout,
+ // no extra row for the subtotals is needed
+ bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
+
+ bool bHasChild = ( pChildDimension != nullptr );
+ if (bHasChild)
+ {
+ if ( bTitleLine ) // in tabular layout the title is on a separate row
+ ++rPos; // -> fill child dimension one row below
+
+ if (bRoot) // same sequence for root member
+ pChildDimension->FillMemberResults( pSequences, rPos, nMeasure );
+ else
+ pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure );
+
+ if ( bTitleLine ) // title row is included in GetSize, so the following
+ --rPos; // positions are calculated with the normal values
+ }
+
+ rPos += nSize;
+
+ tools::Long nUserSubStart;
+ tools::Long nUserSubCount = GetSubTotalCount(&nUserSubStart);
+ if ( !nUserSubCount || !pChildDimension || bSubTotalInTitle )
+ return;
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+
+ rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal
+ rPos -= nExtraSpace; // GetSize includes the empty line
+
+ for (tools::Long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
+ {
+ for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nMemberMeasure = nSubCount;
+
+ ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE;
+ if (bHasChild)
+ eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
+
+ bool bTotalResult = false;
+ OUString aSubStr = aCaption + " " + pResultData->GetMeasureString(nMemberMeasure, false, eForce, bTotalResult);
+
+ if (bTotalResult)
+ {
+ if (pMemberDesc)
+ {
+ // single data field layout.
+ const std::optional<OUString> & pSubtotalName = pParentDim->GetSubtotalName();
+ if (pSubtotalName)
+ aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
+ pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
+ }
+ else
+ {
+ // root member - subtotal (grand total?) for multi-data field layout.
+ const std::optional<OUString> & pGrandTotalName = pResultData->GetSource().GetGrandTotalName();
+ if (pGrandTotalName)
+ aSubStr = *pGrandTotalName;
+ pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
+ }
+ }
+
+ fValue = std::numeric_limits<double>::quiet_NaN(); /* TODO: any numeric value to obtain? */
+ pArray[rPos].Name = aName;
+ pArray[rPos].Caption = aSubStr;
+ pArray[rPos].Flags = ( pArray[rPos].Flags |
+ ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) &
+ ~sheet::MemberResultFlags::CONTINUE;
+ pArray[rPos].Value = fValue;
+
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ {
+ // data layout dimension is (direct/indirect) child of this.
+ // data layout dimension must have name for all entries.
+
+ uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences;
+ if (!bRoot)
+ ++pLayoutSeq;
+ ScDPResultDimension* pLayoutDim = pChildDimension.get();
+ while ( pLayoutDim && !pLayoutDim->IsDataLayout() )
+ {
+ pLayoutDim = pLayoutDim->GetFirstChildDimension();
+ ++pLayoutSeq;
+ }
+ if ( pLayoutDim )
+ {
+ sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray();
+ pLayoutArray[rPos].Name = pResultData->GetMeasureDimensionName(nMemberMeasure);
+ }
+ }
+
+ rPos += 1;
+ }
+ }
+
+ rPos += nExtraSpace; // add again (subtracted above)
+}
+
+void ScDPResultMember::FillDataResults(
+ const ScDPResultMember* pRefMember,
+ ScDPResultFilterContext& rFilterCxt, uno::Sequence<uno::Sequence<sheet::DataResult> >& rSequence,
+ tools::Long nMeasure) const
+{
+ std::unique_ptr<FilterStack> pFilterStack;
+ const ScDPMember* pDPMember = GetDPMember();
+ if (pDPMember)
+ {
+ // Root result has no corresponding DP member. Only take the non-root results.
+ pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
+ pFilterStack->pushDimValue( GetDisplayName( false), GetDisplayName( true));
+ }
+
+ // IsVisible() test is in ScDPResultDimension::FillDataResults
+ // (not on data layout dimension)
+ const ScDPLevel* pParentLevel = GetParentLevel();
+ sal_Int32 nStartRow = rFilterCxt.mnRow;
+
+ tools::Long nExtraSpace = 0;
+ if ( pParentLevel && pParentLevel->IsAddEmpty() )
+ ++nExtraSpace;
+
+ bool bTitleLine = false;
+ if ( pParentLevel && pParentLevel->IsOutlineLayout() )
+ bTitleLine = true;
+
+ bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
+
+ bool bHasChild = ( pChildDimension != nullptr );
+ if (bHasChild)
+ {
+ if ( bTitleLine ) // in tabular layout the title is on a separate row
+ ++rFilterCxt.mnRow; // -> fill child dimension one row below
+
+ sal_Int32 nOldRow = rFilterCxt.mnRow;
+ pChildDimension->FillDataResults(pRefMember, rFilterCxt, rSequence, nMeasure);
+ rFilterCxt.mnRow = nOldRow; // Revert to the original row before the call.
+
+ rFilterCxt.mnRow += GetSize( nMeasure );
+
+ if ( bTitleLine ) // title row is included in GetSize, so the following
+ --rFilterCxt.mnRow; // positions are calculated with the normal values
+ }
+
+ tools::Long nUserSubStart;
+ tools::Long nUserSubCount = GetSubTotalCount(&nUserSubStart);
+ if ( !nUserSubCount && bHasChild )
+ return;
+
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !bHasChild )
+ {
+ nUserSubCount = 1;
+ nUserSubStart = 0;
+ }
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+ if (bHasChild)
+ {
+ rFilterCxt.mnRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
+ rFilterCxt.mnRow -= nExtraSpace; // GetSize includes the empty line
+ }
+
+ tools::Long nMoveSubTotal = 0;
+ if ( bSubTotalInTitle )
+ {
+ nMoveSubTotal = rFilterCxt.mnRow - nStartRow; // force to first (title) row
+ rFilterCxt.mnRow = nStartRow;
+ }
+
+ if ( pDataRoot )
+ {
+ ScDPSubTotalState aSubState; // initial state
+
+ for (tools::Long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
+ {
+ if ( bHasChild && nUserSubCount > 1 )
+ {
+ aSubState.nRowSubTotalFunc = nUserPos;
+ aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos );
+ }
+
+ for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nMemberMeasure = nSubCount;
+ else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
+ nMemberMeasure = SC_DPMEASURE_ALL;
+
+ OSL_ENSURE( rFilterCxt.mnRow < rSequence.getLength(), "bumm" );
+ rFilterCxt.mnCol = 0;
+ if (pRefMember->IsVisible())
+ {
+ uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rFilterCxt.mnRow];
+ pDataRoot->FillDataRow(pRefMember, rFilterCxt, rSubSeq, nMemberMeasure, bHasChild, aSubState);
+ }
+ rFilterCxt.mnRow += 1;
+ }
+ }
+ }
+ else
+ rFilterCxt.mnRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true
+
+ // add extra space again if subtracted from GetSize above,
+ // add to own size if no children
+ rFilterCxt.mnRow += nExtraSpace;
+ rFilterCxt.mnRow += nMoveSubTotal;
+}
+
+void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, tools::Long nMeasure ) const
+{
+ // IsVisible() test is in ScDPResultDimension::FillDataResults
+ // (not on data layout dimension)
+
+ bool bHasChild = ( pChildDimension != nullptr );
+
+ tools::Long nUserSubCount = GetSubTotalCount();
+
+ // process subtotals even if not shown
+
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if (!nUserSubCount || !bHasChild)
+ nUserSubCount = 1;
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+
+ if (pDataRoot)
+ {
+ ScDPSubTotalState aSubState; // initial state
+
+ for (tools::Long nUserPos = 0; nUserPos < nUserSubCount; ++nUserPos) // including hidden "automatic"
+ {
+ if (bHasChild && nUserSubCount > 1)
+ {
+ aSubState.nRowSubTotalFunc = nUserPos;
+ aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
+ }
+
+ for (tools::Long nSubCount = 0; nSubCount < nSubSize; ++nSubCount)
+ {
+ if (nMeasure == SC_DPMEASURE_ALL)
+ nMemberMeasure = nSubCount;
+ else if (pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL)
+ nMemberMeasure = SC_DPMEASURE_ALL;
+
+ pDataRoot->UpdateDataRow(pRefMember, nMemberMeasure, bHasChild, aSubState);
+ }
+ }
+ }
+
+ if (bHasChild) // child dimension must be processed last, so the column total is known
+ {
+ pChildDimension->UpdateDataResults( pRefMember, nMeasure );
+ }
+}
+
+void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember )
+{
+ bool bHasChild = ( pChildDimension != nullptr );
+ if (bHasChild)
+ pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension
+
+ if ( IsRoot() && pDataRoot )
+ {
+ // use the row root member to sort columns
+ // sub total count is always 1
+
+ pDataRoot->SortMembers( pRefMember );
+ }
+}
+
+void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember )
+{
+ bool bHasChild = ( pChildDimension != nullptr );
+ if (bHasChild)
+ pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension
+
+ if ( IsRoot()&& pDataRoot )
+ {
+ // use the row root member to sort columns
+ // sub total count is always 1
+
+ pDataRoot->DoAutoShow( pRefMember );
+ }
+}
+
+void ScDPResultMember::ResetResults()
+{
+ if (pDataRoot)
+ pDataRoot->ResetResults();
+
+ if (pChildDimension)
+ pChildDimension->ResetResults();
+}
+
+void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, tools::Long nMeasure,
+ ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
+{
+ // IsVisible() test is in ScDPResultDimension::FillDataResults
+ // (not on data layout dimension)
+
+ rTotals.SetInColRoot( IsRoot() );
+
+ bool bHasChild = ( pChildDimension != nullptr );
+
+ tools::Long nUserSubCount = GetSubTotalCount();
+ //if ( nUserSubCount || !bHasChild )
+ {
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !bHasChild )
+ nUserSubCount = 1;
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+
+ if ( pDataRoot )
+ {
+ ScDPSubTotalState aSubState; // initial state
+
+ for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
+ {
+ if ( bHasChild && nUserSubCount > 1 )
+ {
+ aSubState.nRowSubTotalFunc = nUserPos;
+ aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
+ }
+
+ for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nMemberMeasure = nSubCount;
+ else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
+ nMemberMeasure = SC_DPMEASURE_ALL;
+
+ if (pRefMember->IsVisible())
+ pDataRoot->UpdateRunningTotals(
+ pRefMember, nMemberMeasure, bHasChild, aSubState, rRunning, rTotals, *this);
+ }
+ }
+ }
+ }
+
+ if (bHasChild) // child dimension must be processed last, so the column total is known
+ {
+ pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals );
+ }
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
+{
+ dumpRow("ScDPResultMember", GetName(), nullptr, pDoc, rPos);
+ SCROW nStartRow = rPos.Row();
+
+ if (pDataRoot)
+ pDataRoot->DumpState( pRefMember, pDoc, rPos );
+
+ if (pChildDimension)
+ pChildDimension->DumpState( pRefMember, pDoc, rPos );
+
+ indent(pDoc, nStartRow, rPos);
+}
+
+void ScDPResultMember::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*2, ' ');
+ std::cout << aIndent << "-- result member '" << GetName() << "'" << std::endl;
+
+ std::cout << aIndent << " column totals" << std::endl;
+ for (const ScDPAggData* p = &aColTotal; p; p = p->GetExistingChild())
+ p->Dump(nIndent+1);
+
+ if (pChildDimension)
+ pChildDimension->Dump(nIndent+1);
+
+ if (pDataRoot)
+ {
+ std::cout << aIndent << " data root" << std::endl;
+ pDataRoot->Dump(nIndent+1);
+ }
+}
+#endif
+
+ScDPAggData* ScDPResultMember::GetColTotal( tools::Long nMeasure ) const
+{
+ return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
+}
+
+void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const
+{
+ if (pChildDimension)
+ pChildDimension->FillVisibilityData(rData);
+}
+
+ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) :
+ pResultData( pData ),
+ pResultMember( pRes )
+{
+ // pResultMember is 0 for root members
+}
+
+ScDPDataMember::~ScDPDataMember()
+{
+}
+
+OUString ScDPDataMember::GetName() const
+{
+ if (pResultMember)
+ return pResultMember->GetName();
+ else
+ return OUString();
+}
+
+bool ScDPDataMember::IsVisible() const
+{
+ if (pResultMember)
+ return pResultMember->IsVisible();
+ else
+ return false;
+}
+
+bool ScDPDataMember::IsNamedItem( SCROW nRow ) const
+{
+ if (pResultMember)
+ return pResultMember->IsNamedItem(nRow);
+ else
+ return false;
+}
+
+bool ScDPDataMember::HasHiddenDetails() const
+{
+ if (pResultMember)
+ return pResultMember->HasHiddenDetails();
+ else
+ return false;
+}
+
+void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim )
+{
+ if ( !pChildDimension )
+ pChildDimension.reset( new ScDPDataDimension(pResultData) );
+ pChildDimension->InitFrom(pDim);
+}
+
+const tools::Long SC_SUBTOTALPOS_AUTO = -1; // default
+const tools::Long SC_SUBTOTALPOS_SKIP = -2; // don't use
+
+static tools::Long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState )
+{
+ if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 &&
+ rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc )
+ {
+ // #i68338# don't return the same index for different combinations (leading to repeated updates),
+ // return a "don't use" value instead
+
+ return SC_SUBTOTALPOS_SKIP;
+ }
+
+ tools::Long nRet = SC_SUBTOTALPOS_AUTO;
+ if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
+ if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
+ return nRet;
+}
+
+void ScDPDataMember::UpdateValues( const vector<ScDPValue>& aValues, const ScDPSubTotalState& rSubState )
+{
+ //TODO: find out how many and which subtotals are used
+
+ ScDPAggData* pAgg = &aAggregate;
+
+ tools::Long nSubPos = lcl_GetSubTotalPos(rSubState);
+ if (nSubPos == SC_SUBTOTALPOS_SKIP)
+ return;
+ if (nSubPos > 0)
+ {
+ tools::Long nSkip = nSubPos * pResultData->GetMeasureCount();
+ for (tools::Long i=0; i<nSkip; i++)
+ pAgg = pAgg->GetChild(); // created if not there
+ }
+
+ size_t nCount = aValues.size();
+ for (size_t nPos = 0; nPos < nCount; ++nPos)
+ {
+ pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState);
+ pAgg = pAgg->GetChild();
+ }
+}
+
+void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValue>& aValues,
+ const ScDPSubTotalState& rSubState )
+{
+ if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() )
+ {
+ // if this DataMember doesn't have a child dimension because the ResultMember's
+ // child dimension wasn't there yet during this DataMembers's creation,
+ // create the child dimension now
+ InitFrom( pResultMember->GetChildDimension() );
+ }
+
+ tools::Long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0;
+
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !pChildDimension )
+ nUserSubCount = 1;
+
+ ScDPSubTotalState aLocalSubState = rSubState; // keep row state, modify column
+ for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
+ {
+ if ( pChildDimension && nUserSubCount > 1 )
+ {
+ const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
+ aLocalSubState.nColSubTotalFunc = nUserPos;
+ aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
+ }
+
+ UpdateValues( aValues, aLocalSubState );
+ }
+
+ if (pChildDimension)
+ pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state
+}
+
+bool ScDPDataMember::HasData( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
+{
+ if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
+ rSubState.eColForce != rSubState.eRowForce )
+ return false;
+
+ // HasData can be different between measures!
+
+ const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
+ if (!pAgg)
+ return false; //TODO: error?
+
+ return pAgg->HasData();
+}
+
+bool ScDPDataMember::HasError( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
+{
+ const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
+ if (!pAgg)
+ return true;
+
+ return pAgg->HasError();
+}
+
+double ScDPDataMember::GetAggregate( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
+{
+ const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
+ if (!pAgg)
+ return DBL_MAX; //TODO: error?
+
+ return pAgg->GetResult();
+}
+
+ScDPAggData* ScDPDataMember::GetAggData( tools::Long nMeasure, const ScDPSubTotalState& rSubState )
+{
+ OSL_ENSURE( nMeasure >= 0, "GetAggData: no measure" );
+
+ ScDPAggData* pAgg = &aAggregate;
+ tools::Long nSkip = nMeasure;
+ tools::Long nSubPos = lcl_GetSubTotalPos(rSubState);
+ if (nSubPos == SC_SUBTOTALPOS_SKIP)
+ return nullptr;
+ if (nSubPos > 0)
+ nSkip += nSubPos * pResultData->GetMeasureCount();
+
+ for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
+ pAgg = pAgg->GetChild(); //TODO: need to create children here?
+
+ return pAgg;
+}
+
+const ScDPAggData* ScDPDataMember::GetConstAggData( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
+{
+ OSL_ENSURE( nMeasure >= 0, "GetConstAggData: no measure" );
+
+ const ScDPAggData* pAgg = &aAggregate;
+ tools::Long nSkip = nMeasure;
+ tools::Long nSubPos = lcl_GetSubTotalPos(rSubState);
+ if (nSubPos == SC_SUBTOTALPOS_SKIP)
+ return nullptr;
+ if (nSubPos > 0)
+ nSkip += nSubPos * pResultData->GetMeasureCount();
+
+ for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
+ {
+ pAgg = pAgg->GetExistingChild();
+ if (!pAgg)
+ return nullptr;
+ }
+
+ return pAgg;
+}
+
+void ScDPDataMember::FillDataRow(
+ const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
+ uno::Sequence<sheet::DataResult>& rSequence, tools::Long nMeasure, bool bIsSubTotalRow,
+ const ScDPSubTotalState& rSubState) const
+{
+ std::unique_ptr<FilterStack> pFilterStack;
+ if (pResultMember)
+ {
+ // Topmost data member (pResultMember=NULL) doesn't need to be handled
+ // since its immediate parent result member is linked to the same
+ // dimension member.
+ pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
+ pFilterStack->pushDimValue( pResultMember->GetDisplayName( false), pResultMember->GetDisplayName( true));
+ }
+
+ OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
+
+ tools::Long nStartCol = rFilterCxt.mnCol;
+
+ const ScDPDataDimension* pDataChild = GetChildDimension();
+ const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
+
+ const ScDPLevel* pRefParentLevel = pRefMember->GetParentLevel();
+
+ tools::Long nExtraSpace = 0;
+ if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() )
+ ++nExtraSpace;
+
+ bool bTitleLine = false;
+ if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() )
+ bTitleLine = true;
+
+ bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure );
+
+ // leave space for children even if the DataMember hasn't been initialized
+ // (pDataChild is null then, this happens when no values for it are in this row)
+ bool bHasChild = ( pRefChild != nullptr );
+
+ if ( bHasChild )
+ {
+ if ( bTitleLine ) // in tabular layout the title is on a separate column
+ ++rFilterCxt.mnCol; // -> fill child dimension one column below
+
+ if ( pDataChild )
+ {
+ tools::Long nOldCol = rFilterCxt.mnCol;
+ pDataChild->FillDataRow(pRefChild, rFilterCxt, rSequence, nMeasure, bIsSubTotalRow, rSubState);
+ rFilterCxt.mnCol = nOldCol; // Revert to the old column value before the call.
+ }
+ rFilterCxt.mnCol += static_cast<sal_uInt16>(pRefMember->GetSize( nMeasure ));
+
+ if ( bTitleLine ) // title column is included in GetSize, so the following
+ --rFilterCxt.mnCol; // positions are calculated with the normal values
+ }
+
+ tools::Long nUserSubStart;
+ tools::Long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart);
+ if ( !nUserSubCount && bHasChild )
+ return;
+
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !bHasChild )
+ {
+ nUserSubCount = 1;
+ nUserSubStart = 0;
+ }
+
+ ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+ if (bHasChild)
+ {
+ rFilterCxt.mnCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal
+ rFilterCxt.mnCol -= nExtraSpace; // GetSize includes the empty line
+ }
+
+ tools::Long nMoveSubTotal = 0;
+ if ( bSubTotalInTitle )
+ {
+ nMoveSubTotal = rFilterCxt.mnCol - nStartCol; // force to first (title) column
+ rFilterCxt.mnCol = nStartCol;
+ }
+
+ for (tools::Long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
+ {
+ if ( pChildDimension && nUserSubCount > 1 )
+ {
+ const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
+ aLocalSubState.nColSubTotalFunc = nUserPos;
+ aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
+ }
+
+ for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nMemberMeasure = nSubCount;
+
+ OSL_ENSURE( rFilterCxt.mnCol < rSequence.getLength(), "bumm" );
+ sheet::DataResult& rRes = rSequence.getArray()[rFilterCxt.mnCol];
+
+ if ( HasData( nMemberMeasure, aLocalSubState ) )
+ {
+ if ( HasError( nMemberMeasure, aLocalSubState ) )
+ {
+ rRes.Value = 0;
+ rRes.Flags |= sheet::DataResultFlags::ERROR;
+ }
+ else
+ {
+ rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState );
+ rRes.Flags |= sheet::DataResultFlags::HASDATA;
+ }
+ }
+
+ if ( bHasChild || bIsSubTotalRow )
+ rRes.Flags |= sheet::DataResultFlags::SUBTOTAL;
+
+ rFilterCxt.maFilterSet.add(rFilterCxt.maFilters, rRes.Value);
+ rFilterCxt.mnCol += 1;
+ }
+ }
+
+ // add extra space again if subtracted from GetSize above,
+ // add to own size if no children
+ rFilterCxt.mnCol += nExtraSpace;
+ rFilterCxt.mnCol += nMoveSubTotal;
+}
+
+void ScDPDataMember::UpdateDataRow(
+ const ScDPResultMember* pRefMember, tools::Long nMeasure, bool bIsSubTotalRow,
+ const ScDPSubTotalState& rSubState )
+{
+ OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
+
+ // Calculate must be called even if not visible (for use as reference value)
+ const ScDPDataDimension* pDataChild = GetChildDimension();
+ const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
+
+ // leave space for children even if the DataMember hasn't been initialized
+ // (pDataChild is null then, this happens when no values for it are in this row)
+ bool bHasChild = ( pRefChild != nullptr );
+
+ // process subtotals even if not shown
+ tools::Long nUserSubCount = pRefMember->GetSubTotalCount();
+
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !bHasChild )
+ nUserSubCount = 1;
+
+ ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+
+ for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
+ {
+ if ( pChildDimension && nUserSubCount > 1 )
+ {
+ const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
+ aLocalSubState.nColSubTotalFunc = nUserPos;
+ aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
+ }
+
+ for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nMemberMeasure = nSubCount;
+
+ // update data...
+ ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
+ if (pAggData)
+ {
+ //TODO: aLocalSubState?
+ ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure );
+ sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
+ sal_Int32 eRefType = aReferenceValue.ReferenceType;
+
+ // calculate the result first - for all members, regardless of reference value
+ pAggData->Calculate( eFunc, aLocalSubState );
+
+ if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
+ {
+ // copy the result into auxiliary value, so differences can be
+ // calculated in any order
+ pAggData->SetAuxiliary( pAggData->GetResult() );
+ }
+ // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
+ }
+ }
+ }
+
+ if ( bHasChild ) // child dimension must be processed last, so the row total is known
+ {
+ if ( pDataChild )
+ pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState );
+ }
+}
+
+void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember )
+{
+ OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
+
+ if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataDimension ???
+ {
+ ScDPDataDimension* pDataChild = GetChildDimension();
+ ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
+ if ( pRefChild && pDataChild )
+ pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension
+ }
+}
+
+void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember )
+{
+ OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
+
+ if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataDimension ???
+ {
+ ScDPDataDimension* pDataChild = GetChildDimension();
+ ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
+ if ( pRefChild && pDataChild )
+ pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension
+ }
+}
+
+void ScDPDataMember::ResetResults()
+{
+ aAggregate.Reset();
+
+ ScDPDataDimension* pDataChild = GetChildDimension();
+ if ( pDataChild )
+ pDataChild->ResetResults();
+}
+
+void ScDPDataMember::UpdateRunningTotals(
+ const ScDPResultMember* pRefMember, tools::Long nMeasure, bool bIsSubTotalRow,
+ const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
+ ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent )
+{
+ OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
+
+ const ScDPDataDimension* pDataChild = GetChildDimension();
+ const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
+
+ bool bIsRoot = ( pResultMember == nullptr || pResultMember->GetParentLevel() == nullptr );
+
+ // leave space for children even if the DataMember hasn't been initialized
+ // (pDataChild is null then, this happens when no values for it are in this row)
+ bool bHasChild = ( pRefChild != nullptr );
+
+ tools::Long nUserSubCount = pRefMember->GetSubTotalCount();
+ {
+ // Calculate at least automatic if no subtotals are selected,
+ // show only own values if there's no child dimension (innermost).
+ if ( !nUserSubCount || !bHasChild )
+ nUserSubCount = 1;
+
+ ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
+
+ for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
+ {
+ if ( pChildDimension && nUserSubCount > 1 )
+ {
+ const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
+ aLocalSubState.nColSubTotalFunc = nUserPos;
+ aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
+ }
+
+ for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
+ {
+ if ( nMeasure == SC_DPMEASURE_ALL )
+ nMemberMeasure = nSubCount;
+
+ // update data...
+ ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
+ if (pAggData)
+ {
+ //TODO: aLocalSubState?
+ sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
+ sal_Int32 eRefType = aReferenceValue.ReferenceType;
+
+ if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
+ {
+ bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL );
+ bool bRelative =
+ ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal );
+ tools::Long nRelativeDir = bRelative ?
+ ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0;
+
+ const ScDPRunningTotalState::IndexArray& rColVisible = rRunning.GetColVisible();
+ const ScDPRunningTotalState::IndexArray& rColSorted = rRunning.GetColSorted();
+ const ScDPRunningTotalState::IndexArray& rRowVisible = rRunning.GetRowVisible();
+ const ScDPRunningTotalState::IndexArray& rRowSorted = rRunning.GetRowSorted();
+
+ OUString aRefFieldName = aReferenceValue.ReferenceField;
+
+ //TODO: aLocalSubState?
+ sheet::DataPilotFieldOrientation nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure );
+ bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN );
+ bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW );
+
+ ScDPResultDimension* pSelectDim = nullptr;
+ sal_Int32 nRowPos = 0;
+ sal_Int32 nColPos = 0;
+
+ // find the reference field in column or row dimensions
+
+ if ( bRefDimInRow ) // look in row dimensions
+ {
+ pSelectDim = rRunning.GetRowResRoot()->GetChildDimension();
+ while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
+ {
+ tools::Long nIndex = rRowSorted[nRowPos];
+ if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
+ pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
+ else
+ pSelectDim = nullptr;
+ ++nRowPos;
+ }
+ // child dimension of innermost member?
+ if ( pSelectDim && rRowSorted[nRowPos] < 0 )
+ pSelectDim = nullptr;
+ }
+
+ if ( bRefDimInCol ) // look in column dimensions
+ {
+ pSelectDim = rRunning.GetColResRoot()->GetChildDimension();
+ while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
+ {
+ tools::Long nIndex = rColSorted[nColPos];
+ if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
+ pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
+ else
+ pSelectDim = nullptr;
+ ++nColPos;
+ }
+ // child dimension of innermost member?
+ if ( pSelectDim && rColSorted[nColPos] < 0 )
+ pSelectDim = nullptr;
+ }
+
+ bool bNoDetailsInRef = false;
+ if ( pSelectDim && bRunningTotal )
+ {
+ // Running totals:
+ // If details are hidden for this member in the reference dimension,
+ // don't show or sum up the value. Otherwise, for following members,
+ // the running totals of details and subtotals wouldn't match.
+
+ tools::Long nMyIndex = bRefDimInCol ? rColSorted[nColPos] : rRowSorted[nRowPos];
+ if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() )
+ {
+ const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex);
+ if ( pMyRefMember && pMyRefMember->HasHiddenDetails() )
+ {
+ pSelectDim = nullptr; // don't calculate
+ bNoDetailsInRef = true; // show error, not empty
+ }
+ }
+ }
+
+ if ( bRelative )
+ {
+ // Difference/Percentage from previous/next:
+ // If details are hidden for this member in the innermost column/row
+ // dimension (the orientation of the reference dimension), show an
+ // error value.
+ // - If the no-details dimension is the reference dimension, its
+ // members will be skipped when finding the previous/next member,
+ // so there must be no results for its members.
+ // - If the no-details dimension is outside of the reference dimension,
+ // no calculation in the reference dimension is possible.
+ // - Otherwise, the error isn't strictly necessary, but shown for
+ // consistency.
+
+ bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() :
+ ( !bRefDimInRow || rRowParent.HasHiddenDetails() );
+ if ( bInnerNoDetails )
+ {
+ pSelectDim = nullptr;
+ bNoDetailsInRef = true; // show error, not empty
+ }
+ }
+
+ if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified
+ bNoDetailsInRef = true; // pSelectDim is then already NULL
+
+ // get the member for the reference item and do the calculation
+
+ if ( bRunningTotal )
+ {
+ // running total in (dimension) -> find first existing member
+
+ if ( pSelectDim )
+ {
+ ScDPDataMember* pSelectMember;
+ if ( bRefDimInCol )
+ pSelectMember = ScDPResultDimension::GetColReferenceMember( nullptr, nullptr,
+ nColPos, rRunning );
+ else
+ {
+ const sal_Int32* pRowSorted = rRowSorted.data();
+ const sal_Int32* pColSorted = rColSorted.data();
+ pRowSorted += nRowPos + 1; // including the reference dimension
+ pSelectMember = pSelectDim->GetRowReferenceMember(
+ nullptr, nullptr, pRowSorted, pColSorted);
+ }
+
+ if ( pSelectMember )
+ {
+ // The running total is kept as the auxiliary value in
+ // the first available member for the reference dimension.
+ // Members are visited in final order, so each one's result
+ // can be used and then modified.
+
+ ScDPAggData* pSelectData = pSelectMember->
+ GetAggData( nMemberMeasure, aLocalSubState );
+ if ( pSelectData )
+ {
+ double fTotal = pSelectData->GetAuxiliary();
+ fTotal += pAggData->GetResult();
+ pSelectData->SetAuxiliary( fTotal );
+ pAggData->SetResult( fTotal );
+ pAggData->SetEmpty(false); // always display
+ }
+ }
+ else
+ pAggData->SetError();
+ }
+ else if (bNoDetailsInRef)
+ pAggData->SetError();
+ else
+ pAggData->SetEmpty(true); // empty (dim set to 0 above)
+ }
+ else
+ {
+ // difference/percentage -> find specified member
+
+ if ( pSelectDim )
+ {
+ OUString aRefItemName = aReferenceValue.ReferenceItemName;
+ ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later
+
+ const OUString* pRefName = nullptr;
+ const ScDPRelativePos* pRefPos = nullptr;
+ if ( bRelative )
+ pRefPos = &aRefItemPos;
+ else
+ pRefName = &aRefItemName;
+
+ ScDPDataMember* pSelectMember;
+ if ( bRefDimInCol )
+ {
+ aRefItemPos.nBasePos = rColVisible[nColPos]; // without sort order applied
+ pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName,
+ nColPos, rRunning );
+ }
+ else
+ {
+ aRefItemPos.nBasePos = rRowVisible[nRowPos]; // without sort order applied
+ const sal_Int32* pRowSorted = rRowSorted.data();
+ const sal_Int32* pColSorted = rColSorted.data();
+ pRowSorted += nRowPos + 1; // including the reference dimension
+ pSelectMember = pSelectDim->GetRowReferenceMember(
+ pRefPos, pRefName, pRowSorted, pColSorted);
+ }
+
+ // difference or perc.difference is empty for the reference item itself
+ if ( pSelectMember == this &&
+ eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE )
+ {
+ pAggData->SetEmpty(true);
+ }
+ else if ( pSelectMember )
+ {
+ const ScDPAggData* pOtherAggData = pSelectMember->
+ GetConstAggData( nMemberMeasure, aLocalSubState );
+ OSL_ENSURE( pOtherAggData, "no agg data" );
+ if ( pOtherAggData )
+ {
+ // Reference member may be visited before or after this one,
+ // so the auxiliary value is used for the original result.
+
+ double fOtherResult = pOtherAggData->GetAuxiliary();
+ double fThisResult = pAggData->GetResult();
+ bool bError = false;
+ switch ( eRefType )
+ {
+ case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE:
+ fThisResult = fThisResult - fOtherResult;
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ if ( fOtherResult == 0.0 )
+ bError = true;
+ else
+ fThisResult = fThisResult / fOtherResult;
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ if ( fOtherResult == 0.0 )
+ bError = true;
+ else
+ fThisResult = ( fThisResult - fOtherResult ) / fOtherResult;
+ break;
+ default:
+ OSL_FAIL("invalid calculation type");
+ }
+ if ( bError )
+ {
+ pAggData->SetError();
+ }
+ else
+ {
+ pAggData->SetResult(fThisResult);
+ pAggData->SetEmpty(false); // always display
+ }
+ //TODO: errors in data?
+ }
+ }
+ else if (bRelative && !bNoDetailsInRef)
+ pAggData->SetEmpty(true); // empty
+ else
+ pAggData->SetError(); // error
+ }
+ else if (bNoDetailsInRef)
+ pAggData->SetError(); // error
+ else
+ pAggData->SetEmpty(true); // empty
+ }
+ }
+ else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::INDEX )
+ {
+
+ // set total values when they are encountered (always before their use)
+
+ ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure );
+ ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure );
+ ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure );
+
+ double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult();
+
+ if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData )
+ pGrandTotalData->SetAuxiliary( fTotalValue );
+
+ if ( bIsRoot && pRowTotalData )
+ pRowTotalData->SetAuxiliary( fTotalValue );
+
+ if ( rTotals.IsInColRoot() && pColTotalData )
+ pColTotalData->SetAuxiliary( fTotalValue );
+
+ // find relation to total values
+
+ switch ( eRefType )
+ {
+ case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
+ {
+ double nTotal;
+ if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE )
+ nTotal = pRowTotalData ? pRowTotalData->GetAuxiliary() : 0.0;
+ else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE )
+ nTotal = pColTotalData ? pColTotalData->GetAuxiliary() : 0.0;
+ else
+ nTotal = pGrandTotalData ? pGrandTotalData->GetAuxiliary() : 0.0;
+
+ if ( nTotal == 0.0 )
+ pAggData->SetError();
+ else
+ pAggData->SetResult( pAggData->GetResult() / nTotal );
+ }
+ break;
+ case sheet::DataPilotFieldReferenceType::INDEX:
+ {
+ double nColTotal = pColTotalData ? pColTotalData->GetAuxiliary() : 0.0;
+ double nRowTotal = pRowTotalData ? pRowTotalData->GetAuxiliary() : 0.0;
+ double nGrandTotal = pGrandTotalData ? pGrandTotalData->GetAuxiliary() : 0.0;
+ if ( nRowTotal == 0.0 || nColTotal == 0.0 )
+ pAggData->SetError();
+ else
+ pAggData->SetResult(
+ ( pAggData->GetResult() * nGrandTotal ) /
+ ( nRowTotal * nColTotal ) );
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( bHasChild ) // child dimension must be processed last, so the row total is known
+ {
+ if ( pDataChild )
+ pDataChild->UpdateRunningTotals( pRefChild, nMeasure,
+ bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
+ }
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
+{
+ dumpRow("ScDPDataMember", GetName(), &aAggregate, pDoc, rPos);
+ SCROW nStartRow = rPos.Row();
+
+ const ScDPDataDimension* pDataChild = GetChildDimension();
+ const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
+ if ( pDataChild && pRefChild )
+ pDataChild->DumpState( pRefChild, pDoc, rPos );
+
+ indent(pDoc, nStartRow, rPos);
+}
+
+void ScDPDataMember::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*2, ' ');
+ std::cout << aIndent << "-- data member '"
+ << (pResultMember ? pResultMember->GetName() : OUString()) << "'" << std::endl;
+ for (const ScDPAggData* pAgg = &aAggregate; pAgg; pAgg = pAgg->GetExistingChild())
+ pAgg->Dump(nIndent+1);
+
+ if (pChildDimension)
+ pChildDimension->Dump(nIndent+1);
+}
+#endif
+
+// Helper class to select the members to include in
+// ScDPResultDimension::InitFrom or LateInitFrom if groups are used
+
+namespace {
+
+class ScDPGroupCompare
+{
+private:
+ const ScDPResultData* pResultData;
+ const ScDPInitState& rInitState;
+ tools::Long nDimSource;
+ bool bIncludeAll;
+ bool bIsBase;
+ tools::Long nGroupBase;
+public:
+ ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, tools::Long nDimension );
+
+ bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); }
+ bool TestIncluded( const ScDPMember& rMember );
+};
+
+}
+
+ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, tools::Long nDimension ) :
+ pResultData( pData ),
+ rInitState( rState ),
+ nDimSource( nDimension )
+{
+ bIsBase = pResultData->IsBaseForGroup( nDimSource );
+ nGroupBase = pResultData->GetGroupBase( nDimSource ); //TODO: get together in one call?
+
+ // if bIncludeAll is set, TestIncluded doesn't need to be called
+ bIncludeAll = !( bIsBase || nGroupBase >= 0 );
+}
+
+bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember )
+{
+ bool bInclude = true;
+ if ( bIsBase )
+ {
+ // need to check all previous groups
+ //TODO: get array of groups (or indexes) before loop?
+ ScDPItemData aMemberData(rMember.FillItemData());
+
+ const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
+ bInclude = std::all_of(rMemStates.begin(), rMemStates.end(),
+ [this, &aMemberData](const ScDPInitState::Member& rMem) {
+ return (pResultData->GetGroupBase(rMem.mnSrcIndex) != nDimSource)
+ || pResultData->IsInGroup(rMem.mnNameIndex, rMem.mnSrcIndex, aMemberData, nDimSource);
+ });
+ }
+ else if ( nGroupBase >= 0 )
+ {
+ // base isn't used in preceding fields
+ // -> look for other groups using the same base
+
+ //TODO: get array of groups (or indexes) before loop?
+ ScDPItemData aMemberData(rMember.FillItemData());
+ const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
+ bInclude = std::all_of(rMemStates.begin(), rMemStates.end(),
+ [this, &aMemberData](const ScDPInitState::Member& rMem) {
+ // coverity[copy_paste_error : FALSE] - same base (hierarchy between
+ // the two groups is irrelevant)
+ return (pResultData->GetGroupBase(rMem.mnSrcIndex) != nGroupBase)
+ || pResultData->HasCommonElement(rMem.mnNameIndex, rMem.mnSrcIndex, aMemberData, nDimSource);
+ });
+ }
+
+ return bInclude;
+}
+
+ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) :
+ pResultData( pData ),
+ nSortMeasure( 0 ),
+ bIsDataLayout( false ),
+ bSortByData( false ),
+ bSortAscending( false ),
+ bAutoShow( false ),
+ bAutoTopItems( false ),
+ bInitialized( false ),
+ nAutoMeasure( 0 ),
+ nAutoCount( 0 )
+{
+}
+
+ScDPResultDimension::~ScDPResultDimension()
+{
+}
+
+ScDPResultMember *ScDPResultDimension::FindMember( SCROW iData ) const
+{
+ if( bIsDataLayout )
+ {
+ SAL_WARN_IF(maMemberArray.empty(), "sc.core", "MemberArray is empty");
+ return !maMemberArray.empty() ? maMemberArray[0].get() : nullptr;
+ }
+
+ MemberHash::const_iterator aRes = maMemberHash.find( iData );
+ if( aRes != maMemberHash.end()) {
+ if ( aRes->second->IsNamedItem( iData ) )
+ return aRes->second;
+ OSL_FAIL("problem! hash result is not the same as IsNamedItem");
+ }
+
+ unsigned int i;
+ unsigned int nCount = maMemberArray.size();
+ for( i = 0; i < nCount ; i++ )
+ {
+ ScDPResultMember* pResultMember = maMemberArray[i].get();
+ if ( pResultMember->IsNamedItem( iData ) )
+ return pResultMember;
+ }
+ return nullptr;
+}
+
+void ScDPResultDimension::InitFrom(
+ const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
+ size_t nPos, ScDPInitState& rInitState, bool bInitChild )
+{
+ if (nPos >= ppDim.size() || nPos >= ppLev.size())
+ {
+ bInitialized = true;
+ return;
+ }
+
+ ScDPDimension* pThisDim = ppDim[nPos];
+ ScDPLevel* pThisLevel = ppLev[nPos];
+
+ if (!pThisDim || !pThisLevel)
+ {
+ bInitialized = true;
+ return;
+ }
+
+ bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member
+ aDimensionName = pThisDim->getName(); // member
+
+ // Check the autoshow setting. If it's enabled, store the settings.
+ const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
+ if ( rAutoInfo.IsEnabled )
+ {
+ bAutoShow = true;
+ bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
+ nAutoMeasure = pThisLevel->GetAutoMeasure();
+ nAutoCount = rAutoInfo.ItemCount;
+ }
+
+ // Check the sort info, and store the settings if appropriate.
+ const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
+ if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
+ {
+ bSortByData = true;
+ bSortAscending = rSortInfo.IsAscending;
+ nSortMeasure = pThisLevel->GetSortMeasure();
+ }
+
+ // global order is used to initialize aMembers, so it doesn't have to be looked at later
+ const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
+
+ tools::Long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
+ ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
+
+ // Now, go through all members and initialize them.
+ ScDPMembers* pMembers = pThisLevel->GetMembersObject();
+ tools::Long nMembCount = pMembers->getCount();
+ for ( tools::Long i=0; i<nMembCount; i++ )
+ {
+ tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
+
+ ScDPMember* pMember = pMembers->getByIndex(nSorted);
+ if ( aCompare.IsIncluded( *pMember ) )
+ {
+ ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember);
+ ScDPResultMember* pNew = AddMember( aData );
+
+ rInitState.AddMember(nDimSource, pNew->GetDataId());
+ pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild );
+ rInitState.RemoveMember();
+ }
+ }
+ bInitialized = true;
+}
+
+void ScDPResultDimension::LateInitFrom(
+ LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
+{
+ if ( rParams.IsEnd( nPos ) )
+ return;
+ if (nPos >= pItemData.size())
+ {
+ SAL_WARN("sc.core", "pos " << nPos << ", but vector size is " << pItemData.size());
+ return;
+ }
+ SCROW rThisData = pItemData[nPos];
+ ScDPDimension* pThisDim = rParams.GetDim( nPos );
+ ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
+
+ if (!pThisDim || !pThisLevel)
+ return;
+
+ tools::Long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
+
+ bool bShowEmpty = pThisLevel->getShowEmpty();
+
+ if ( !bInitialized )
+ { // init some values
+ // create all members at the first call (preserve order)
+ bIsDataLayout = pThisDim->getIsDataLayoutDimension();
+ aDimensionName = pThisDim->getName();
+
+ const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
+ if ( rAutoInfo.IsEnabled )
+ {
+ bAutoShow = true;
+ bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
+ nAutoMeasure = pThisLevel->GetAutoMeasure();
+ nAutoCount = rAutoInfo.ItemCount;
+ }
+
+ const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
+ if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
+ {
+ bSortByData = true;
+ bSortAscending = rSortInfo.IsAscending;
+ nSortMeasure = pThisLevel->GetSortMeasure();
+ }
+ }
+
+ bool bLateInitAllMembers= bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty;
+
+ if ( !bLateInitAllMembers )
+ {
+ ResultMembers& rMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
+ bLateInitAllMembers = rMembers.IsHasHideDetailsMembers();
+
+ SAL_INFO("sc.core", aDimensionName << (rMembers.IsHasHideDetailsMembers() ? " HasHideDetailsMembers" : ""));
+
+ rMembers.SetHasHideDetailsMembers( false );
+ }
+
+ bool bNewAllMembers = (!rParams.IsRow()) || nPos == 0 || bLateInitAllMembers;
+
+ if (bNewAllMembers )
+ {
+ // global order is used to initialize aMembers, so it doesn't have to be looked at later
+ if ( !bInitialized )
+ { //init all members
+ const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
+
+ ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
+ ScDPMembers* pMembers = pThisLevel->GetMembersObject();
+ tools::Long nMembCount = pMembers->getCount();
+ for ( tools::Long i=0; i<nMembCount; i++ )
+ {
+ tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
+
+ ScDPMember* pMember = pMembers->getByIndex(nSorted);
+ if ( aCompare.IsIncluded( *pMember ) )
+ { // add all members
+ ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember );
+ AddMember( aData );
+ }
+ }
+ bInitialized = true; // don't call again, even if no members were included
+ }
+ // initialize only specific member (or all if "show empty" flag is set)
+ if ( bLateInitAllMembers )
+ {
+ tools::Long nCount = maMemberArray.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ ScDPResultMember* pResultMember = maMemberArray[i].get();
+
+ // check show empty
+ bool bAllChildren = false;
+ if( bShowEmpty )
+ {
+ bAllChildren = !pResultMember->IsNamedItem( rThisData );
+ }
+ rParams.SetInitAllChildren( bAllChildren );
+ rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
+ pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
+ rInitState.RemoveMember();
+ }
+ }
+ else
+ {
+ ScDPResultMember* pResultMember = FindMember( rThisData );
+ if( nullptr != pResultMember )
+ {
+ rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
+ pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
+ rInitState.RemoveMember();
+ }
+ }
+ }
+ else
+ InitWithMembers( rParams, pItemData, nPos, rInitState );
+}
+
+tools::Long ScDPResultDimension::GetSize(tools::Long nMeasure) const
+{
+ tools::Long nMemberCount = maMemberArray.size();
+ if (!nMemberCount)
+ return 0;
+
+ tools::Long nTotal = 0;
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ // repeat first member...
+ nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size
+ }
+ else
+ {
+ // add all members
+ for (tools::Long nMem=0; nMem<nMemberCount; nMem++)
+ nTotal += maMemberArray[nMem]->GetSize(nMeasure);
+ }
+ return nTotal;
+}
+
+bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const
+{
+ if (aMembers.empty())
+ return false;
+
+ const ScDPResultMember* pMember = FindMember( aMembers[0] );
+ if ( nullptr != pMember )
+ return pMember->IsValidEntry( aMembers );
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("sc.core", "IsValidEntry: Member not found, DimNam = " << GetName());
+#endif
+ return false;
+}
+
+void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers,
+ const ScDPResultDimension* pDataDim,
+ const vector< SCROW >& aDataMembers,
+ const vector<ScDPValue>& aValues ) const
+{
+ if (aMembers.empty())
+ return;
+
+ ScDPResultMember* pMember = FindMember( aMembers[0] );
+ if ( nullptr != pMember )
+ {
+ vector<SCROW> aChildMembers;
+ if (aMembers.size() > 1)
+ {
+ vector<SCROW>::const_iterator itr = aMembers.begin();
+ aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end());
+ }
+ pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
+ return;
+ }
+
+ OSL_FAIL("ProcessData: Member not found");
+}
+
+void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
+ tools::Long nStart, tools::Long nMeasure )
+{
+ tools::Long nPos = nStart;
+ tools::Long nCount = maMemberArray.size();
+
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ tools::Long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
+
+ ScDPResultMember* pMember = maMemberArray[nSorted].get();
+ // in data layout dimension, use first member with different measures/names
+ if ( bIsDataLayout )
+ {
+ bool bTotalResult = false;
+ OUString aMbrName = pResultData->GetMeasureDimensionName( nSorted );
+ OUString aMbrCapt = pResultData->GetMeasureString( nSorted, false, SUBTOTAL_FUNC_NONE, bTotalResult );
+ maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, false, &aMbrName, &aMbrCapt );
+ }
+ else if ( pMember->IsVisible() )
+ {
+ pMember->FillMemberResults( pSequences, nPos, nMeasure, false, nullptr, nullptr );
+ }
+ // nPos is modified
+ }
+}
+
+void ScDPResultDimension::FillDataResults(
+ const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
+ uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, tools::Long nMeasure) const
+{
+ FilterStack aFilterStack(rFilterCxt.maFilters);
+ aFilterStack.pushDimName(GetName(), bIsDataLayout);
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nCount = maMemberArray.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ tools::Long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
+
+ const ScDPResultMember* pMember;
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ pMember = maMemberArray[0].get();
+ nMemberMeasure = nSorted;
+ }
+ else
+ pMember = maMemberArray[nSorted].get();
+
+ if ( pMember->IsVisible() )
+ pMember->FillDataResults(pRefMember, rFilterCxt, rSequence, nMemberMeasure);
+ }
+}
+
+void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, tools::Long nMeasure ) const
+{
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nCount = maMemberArray.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ const ScDPResultMember* pMember;
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ pMember = maMemberArray[0].get();
+ nMemberMeasure = i;
+ }
+ else
+ pMember = maMemberArray[i].get();
+
+ if ( pMember->IsVisible() )
+ pMember->UpdateDataResults( pRefMember, nMemberMeasure );
+ }
+}
+
+void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember )
+{
+ tools::Long nCount = maMemberArray.size();
+
+ if ( bSortByData )
+ {
+ // sort members
+
+ OSL_ENSURE( aMemberOrder.empty(), "sort twice?" );
+ aMemberOrder.resize( nCount );
+ for (tools::Long nPos=0; nPos<nCount; nPos++)
+ aMemberOrder[nPos] = nPos;
+
+ ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending );
+ ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp );
+ }
+
+ // handle children
+
+ // for data layout, call only once - sorting measure is always taken from settings
+ tools::Long nLoopCount = bIsDataLayout ? std::min<tools::Long>(1, nCount) : nCount;
+ for (tools::Long i=0; i<nLoopCount; i++)
+ {
+ ScDPResultMember* pMember = maMemberArray[i].get();
+ if ( pMember->IsVisible() )
+ pMember->SortMembers( pRefMember );
+ }
+}
+
+void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember )
+{
+ tools::Long nCount = maMemberArray.size();
+
+ // handle children first, before changing the visible state
+
+ // for data layout, call only once - sorting measure is always taken from settings
+ tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
+ for (tools::Long i=0; i<nLoopCount; i++)
+ {
+ ScDPResultMember* pMember = maMemberArray[i].get();
+ if ( pMember->IsVisible() )
+ pMember->DoAutoShow( pRefMember );
+ }
+
+ if ( !(bAutoShow && nAutoCount > 0 && nAutoCount < nCount) )
+ return;
+
+ // establish temporary order, hide remaining members
+
+ ScMemberSortOrder aAutoOrder;
+ aAutoOrder.resize( nCount );
+ tools::Long nPos;
+ for (nPos=0; nPos<nCount; nPos++)
+ aAutoOrder[nPos] = nPos;
+
+ ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems );
+ ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
+
+ // look for equal values to the last included one
+
+ tools::Long nIncluded = nAutoCount;
+ const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]].get();
+ const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : nullptr;
+ bool bContinue = true;
+ while ( bContinue )
+ {
+ bContinue = false;
+ if ( nIncluded < nCount )
+ {
+ const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]].get();
+ const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : nullptr;
+
+ if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) )
+ {
+ ++nIncluded; // include more members if values are equal
+ bContinue = true;
+ }
+ }
+ }
+
+ // hide the remaining members
+
+ for (nPos = nIncluded; nPos < nCount; nPos++)
+ {
+ ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]].get();
+ pMember->SetAutoHidden();
+ }
+}
+
+void ScDPResultDimension::ResetResults()
+{
+ tools::Long nCount = maMemberArray.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ // sort order doesn't matter
+ ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i].get();
+ pMember->ResetResults();
+ }
+}
+
+tools::Long ScDPResultDimension::GetSortedIndex( tools::Long nUnsorted ) const
+{
+ return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
+}
+
+void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, tools::Long nMeasure,
+ ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
+{
+ const ScDPResultMember* pMember;
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nCount = maMemberArray.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ tools::Long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
+
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ pMember = maMemberArray[0].get();
+ nMemberMeasure = nSorted;
+ }
+ else
+ pMember = maMemberArray[nSorted].get();
+
+ if ( pMember->IsVisible() )
+ {
+ if ( bIsDataLayout )
+ rRunning.AddRowIndex( 0, 0 );
+ else
+ rRunning.AddRowIndex( i, nSorted );
+ pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals );
+ rRunning.RemoveRowIndex();
+ }
+ }
+}
+
+ScDPDataMember* ScDPResultDimension::GetRowReferenceMember(
+ const ScDPRelativePos* pRelativePos, const OUString* pName,
+ const sal_Int32* pRowIndexes, const sal_Int32* pColIndexes ) const
+{
+ // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
+
+ OSL_ENSURE( pRelativePos == nullptr || pName == nullptr, "can't use position and name" );
+
+ ScDPDataMember* pColMember = nullptr;
+
+ bool bFirstExisting = ( pRelativePos == nullptr && pName == nullptr );
+ tools::Long nMemberCount = maMemberArray.size();
+ tools::Long nMemberIndex = 0; // unsorted
+ tools::Long nDirection = 1; // forward if no relative position is used
+ if ( pRelativePos )
+ {
+ nDirection = pRelativePos->nDirection;
+ nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
+
+ OSL_ENSURE( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" );
+ }
+ else if ( pName )
+ {
+ // search for named member
+
+ const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)].get();
+
+ //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
+ while ( pRowMember && pRowMember->GetName() != *pName )
+ {
+ ++nMemberIndex;
+ if ( nMemberIndex < nMemberCount )
+ pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)].get();
+ else
+ pRowMember = nullptr;
+ }
+ }
+
+ bool bContinue = true;
+ while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount )
+ {
+ const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)].get();
+
+ // get child members by given indexes
+
+ const sal_Int32* pNextRowIndex = pRowIndexes;
+ while ( *pNextRowIndex >= 0 && pRowMember )
+ {
+ const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
+ if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
+ pRowMember = pRowChild->GetMember( *pNextRowIndex );
+ else
+ pRowMember = nullptr;
+ ++pNextRowIndex;
+ }
+
+ if ( pRowMember && pRelativePos )
+ {
+ // Skip the member if it has hidden details
+ // (because when looking for the details, it is skipped, too).
+ // Also skip if the member is invisible because it has no data,
+ // for consistent ordering.
+ if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() )
+ pRowMember = nullptr;
+ }
+
+ if ( pRowMember )
+ {
+ pColMember = pRowMember->GetDataRoot();
+
+ const sal_Int32* pNextColIndex = pColIndexes;
+ while ( *pNextColIndex >= 0 && pColMember )
+ {
+ ScDPDataDimension* pColChild = pColMember->GetChildDimension();
+ if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
+ pColMember = pColChild->GetMember( *pNextColIndex );
+ else
+ pColMember = nullptr;
+ ++pNextColIndex;
+ }
+ }
+
+ // continue searching only if looking for first existing or relative position
+ bContinue = ( pColMember == nullptr && ( bFirstExisting || pRelativePos ) );
+ nMemberIndex += nDirection;
+ }
+
+ return pColMember;
+}
+
+ScDPDataMember* ScDPResultDimension::GetColReferenceMember(
+ const ScDPRelativePos* pRelativePos, const OUString* pName,
+ sal_Int32 nRefDimPos, const ScDPRunningTotalState& rRunning )
+{
+ OSL_ENSURE( pRelativePos == nullptr || pName == nullptr, "can't use position and name" );
+
+ const sal_Int32* pColIndexes = rRunning.GetColSorted().data();
+ const sal_Int32* pRowIndexes = rRunning.GetRowSorted().data();
+
+ // get own row member using all indexes
+
+ const ScDPResultMember* pRowMember = rRunning.GetRowResRoot();
+ ScDPDataMember* pColMember = nullptr;
+
+ const sal_Int32* pNextRowIndex = pRowIndexes;
+ while ( *pNextRowIndex >= 0 && pRowMember )
+ {
+ const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
+ if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
+ pRowMember = pRowChild->GetMember( *pNextRowIndex );
+ else
+ pRowMember = nullptr;
+ ++pNextRowIndex;
+ }
+
+ // get column (data) members before the reference field
+ //TODO: pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
+
+ if ( pRowMember )
+ {
+ pColMember = pRowMember->GetDataRoot();
+
+ const sal_Int32* pNextColIndex = pColIndexes;
+ sal_Int32 nColSkipped = 0;
+ while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos )
+ {
+ ScDPDataDimension* pColChild = pColMember->GetChildDimension();
+ if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
+ pColMember = pColChild->GetMember( *pNextColIndex );
+ else
+ pColMember = nullptr;
+ ++pNextColIndex;
+ ++nColSkipped;
+ }
+ }
+
+ // get column member for the reference field
+
+ if ( pColMember )
+ {
+ ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension();
+ if ( pReferenceDim )
+ {
+ tools::Long nReferenceCount = pReferenceDim->GetMemberCount();
+
+ bool bFirstExisting = ( pRelativePos == nullptr && pName == nullptr );
+ tools::Long nMemberIndex = 0; // unsorted
+ tools::Long nDirection = 1; // forward if no relative position is used
+ pColMember = nullptr; // don't use parent dimension's member if none found
+ if ( pRelativePos )
+ {
+ nDirection = pRelativePos->nDirection;
+ nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below
+ }
+ else if ( pName )
+ {
+ // search for named member
+
+ pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
+
+ //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
+ while ( pColMember && pColMember->GetName() != *pName )
+ {
+ ++nMemberIndex;
+ if ( nMemberIndex < nReferenceCount )
+ pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
+ else
+ pColMember = nullptr;
+ }
+ }
+
+ bool bContinue = true;
+ while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount )
+ {
+ pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
+
+ // get column members below the reference field
+
+ const sal_Int32* pNextColIndex = pColIndexes + nRefDimPos + 1;
+ while ( *pNextColIndex >= 0 && pColMember )
+ {
+ ScDPDataDimension* pColChild = pColMember->GetChildDimension();
+ if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
+ pColMember = pColChild->GetMember( *pNextColIndex );
+ else
+ pColMember = nullptr;
+ ++pNextColIndex;
+ }
+
+ if ( pColMember && pRelativePos )
+ {
+ // Skip the member if it has hidden details
+ // (because when looking for the details, it is skipped, too).
+ // Also skip if the member is invisible because it has no data,
+ // for consistent ordering.
+ if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() )
+ pColMember = nullptr;
+ }
+
+ // continue searching only if looking for first existing or relative position
+ bContinue = ( pColMember == nullptr && ( bFirstExisting || pRelativePos ) );
+ nMemberIndex += nDirection;
+ }
+ }
+ else
+ pColMember = nullptr;
+ }
+
+ return pColMember;
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
+{
+ OUString aDimName = bIsDataLayout ? OUString("(data layout)") : GetName();
+ dumpRow("ScDPResultDimension", aDimName, nullptr, pDoc, rPos);
+
+ SCROW nStartRow = rPos.Row();
+
+ tools::Long nCount = bIsDataLayout ? 1 : maMemberArray.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ const ScDPResultMember* pMember = maMemberArray[i].get();
+ pMember->DumpState( pRefMember, pDoc, rPos );
+ }
+
+ indent(pDoc, nStartRow, rPos);
+}
+
+void ScDPResultDimension::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*2, ' ');
+ std::cout << aIndent << "-- dimension '" << GetName() << "'" << std::endl;
+ for (const auto& rxMember : maMemberArray)
+ {
+ const ScDPResultMember* p = rxMember.get();
+ p->Dump(nIndent+1);
+ }
+}
+#endif
+
+tools::Long ScDPResultDimension::GetMemberCount() const
+{
+ return maMemberArray.size();
+}
+
+const ScDPResultMember* ScDPResultDimension::GetMember(tools::Long n) const
+{
+ return maMemberArray[n].get();
+}
+ScDPResultMember* ScDPResultDimension::GetMember(tools::Long n)
+{
+ return maMemberArray[n].get();
+}
+
+ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const
+{
+ if ( !maMemberArray.empty() )
+ return maMemberArray[0]->GetChildDimension();
+ else
+ return nullptr;
+}
+
+void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const
+{
+ if (IsDataLayout())
+ return;
+
+ for (const auto& rxMember : maMemberArray)
+ {
+ ScDPResultMember* pMember = rxMember.get();
+ if (pMember->IsValid())
+ {
+ ScDPItemData aItem(pMember->FillItemData());
+ rData.addVisibleMember(GetName(), aItem);
+ pMember->FillVisibilityData(rData);
+ }
+ }
+}
+
+ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) :
+ pResultData( pData ),
+ pResultDimension( nullptr ),
+ bIsDataLayout( false )
+{
+}
+
+ScDPDataDimension::~ScDPDataDimension()
+{
+}
+
+void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim )
+{
+ if (!pDim)
+ return;
+
+ pResultDimension = pDim;
+ bIsDataLayout = pDim->IsDataLayout();
+
+ // Go through all result members under the given result dimension, and
+ // create a new data member instance for each result member.
+ tools::Long nCount = pDim->GetMemberCount();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ const ScDPResultMember* pResMem = pDim->GetMember(i);
+
+ ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem );
+ maMembers.emplace_back( pNew);
+
+ if ( !pResultData->IsLateInit() )
+ {
+ // with LateInit, pResMem hasn't necessarily been initialized yet,
+ // so InitFrom for the new result member is called from its ProcessData method
+
+ const ScDPResultDimension* pChildDim = pResMem->GetChildDimension();
+ if ( pChildDim )
+ pNew->InitFrom( pChildDim );
+ }
+ }
+}
+
+void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues,
+ const ScDPSubTotalState& rSubState )
+{
+ // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
+
+ tools::Long nCount = maMembers.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ ScDPDataMember* pMember = maMembers[static_cast<sal_uInt16>(i)].get();
+
+ // always first member for data layout dim
+ if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) )
+ {
+ vector<SCROW> aChildDataMembers;
+ if (aDataMembers.size() > 1)
+ {
+ vector<SCROW>::const_iterator itr = aDataMembers.begin();
+ aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end());
+ }
+ pMember->ProcessData( aChildDataMembers, aValues, rSubState );
+ return;
+ }
+ }
+
+ OSL_FAIL("ProcessData: Member not found");
+}
+
+void ScDPDataDimension::FillDataRow(
+ const ScDPResultDimension* pRefDim, ScDPResultFilterContext& rFilterCxt,
+ uno::Sequence<sheet::DataResult>& rSequence, tools::Long nMeasure, bool bIsSubTotalRow,
+ const ScDPSubTotalState& rSubState) const
+{
+ OUString aDimName;
+ bool bDataLayout = false;
+ if (pResultDimension)
+ {
+ aDimName = pResultDimension->GetName();
+ bDataLayout = pResultDimension->IsDataLayout();
+ }
+
+ FilterStack aFilterStack(rFilterCxt.maFilters);
+ aFilterStack.pushDimName(aDimName, bDataLayout);
+
+ OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
+ OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
+
+ const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nCount = maMembers.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ tools::Long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
+
+ tools::Long nMemberPos = nSorted;
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ nMemberPos = 0;
+ nMemberMeasure = nSorted;
+ }
+
+ const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
+ if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember::FillDataRow ???
+ {
+ const ScDPDataMember* pDataMember = maMembers[static_cast<sal_uInt16>(nMemberPos)].get();
+ pDataMember->FillDataRow(pRefMember, rFilterCxt, rSequence, nMemberMeasure, bIsSubTotalRow, rSubState);
+ }
+ }
+}
+
+void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim,
+ tools::Long nMeasure, bool bIsSubTotalRow,
+ const ScDPSubTotalState& rSubState ) const
+{
+ OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
+ OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nCount = maMembers.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ tools::Long nMemberPos = i;
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ nMemberPos = 0;
+ nMemberMeasure = i;
+ }
+
+ // Calculate must be called even if the member is not visible (for use as reference value)
+ const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
+ ScDPDataMember* pDataMember = maMembers[static_cast<sal_uInt16>(nMemberPos)].get();
+ pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState );
+ }
+}
+
+void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim )
+{
+ tools::Long nCount = maMembers.size();
+
+ if ( pRefDim->IsSortByData() )
+ {
+ // sort members
+
+ ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
+ OSL_ENSURE( rMemberOrder.empty(), "sort twice?" );
+ rMemberOrder.resize( nCount );
+ for (tools::Long nPos=0; nPos<nCount; nPos++)
+ rMemberOrder[nPos] = nPos;
+
+ ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() );
+ ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp );
+ }
+
+ // handle children
+
+ OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
+ OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
+
+ // for data layout, call only once - sorting measure is always taken from settings
+ tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
+ for (tools::Long i=0; i<nLoopCount; i++)
+ {
+ ScDPResultMember* pRefMember = pRefDim->GetMember(i);
+ if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember ???
+ {
+ ScDPDataMember* pDataMember = maMembers[static_cast<sal_uInt16>(i)].get();
+ pDataMember->SortMembers( pRefMember );
+ }
+ }
+}
+
+void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim )
+{
+ tools::Long nCount = maMembers.size();
+
+ // handle children first, before changing the visible state
+
+ OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
+ OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
+
+ // for data layout, call only once - sorting measure is always taken from settings
+ tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
+ for (tools::Long i=0; i<nLoopCount; i++)
+ {
+ ScDPResultMember* pRefMember = pRefDim->GetMember(i);
+ if ( pRefMember->IsVisible() ) //TODO: here or in ScDPDataMember ???
+ {
+ ScDPDataMember* pDataMember = maMembers[i].get();
+ pDataMember->DoAutoShow( pRefMember );
+ }
+ }
+
+ if ( !(pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount) )
+ return;
+
+ // establish temporary order, hide remaining members
+
+ ScMemberSortOrder aAutoOrder;
+ aAutoOrder.resize( nCount );
+ tools::Long nPos;
+ for (nPos=0; nPos<nCount; nPos++)
+ aAutoOrder[nPos] = nPos;
+
+ ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() );
+ ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
+
+ // look for equal values to the last included one
+
+ tools::Long nIncluded = pRefDim->GetAutoCount();
+ ScDPDataMember* pDataMember1 = maMembers[aAutoOrder[nIncluded - 1]].get();
+ if ( !pDataMember1->IsVisible() )
+ pDataMember1 = nullptr;
+ bool bContinue = true;
+ while ( bContinue )
+ {
+ bContinue = false;
+ if ( nIncluded < nCount )
+ {
+ ScDPDataMember* pDataMember2 = maMembers[aAutoOrder[nIncluded]].get();
+ if ( !pDataMember2->IsVisible() )
+ pDataMember2 = nullptr;
+
+ if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) )
+ {
+ ++nIncluded; // include more members if values are equal
+ bContinue = true;
+ }
+ }
+ }
+
+ // hide the remaining members
+
+ for (nPos = nIncluded; nPos < nCount; nPos++)
+ {
+ ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]);
+ pMember->SetAutoHidden();
+ }
+}
+
+void ScDPDataDimension::ResetResults()
+{
+ tools::Long nCount = maMembers.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ // sort order doesn't matter
+
+ tools::Long nMemberPos = bIsDataLayout ? 0 : i;
+ ScDPDataMember* pDataMember = maMembers[nMemberPos].get();
+ pDataMember->ResetResults();
+ }
+}
+
+tools::Long ScDPDataDimension::GetSortedIndex( tools::Long nUnsorted ) const
+{
+ if (!pResultDimension)
+ return nUnsorted;
+
+ const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
+ return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
+}
+
+void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim,
+ tools::Long nMeasure, bool bIsSubTotalRow,
+ const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
+ ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const
+{
+ OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
+ OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
+
+ tools::Long nMemberMeasure = nMeasure;
+ tools::Long nCount = maMembers.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
+ tools::Long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
+
+ tools::Long nMemberPos = nSorted;
+ if (bIsDataLayout)
+ {
+ OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
+ "DataLayout dimension twice?");
+ nMemberPos = 0;
+ nMemberMeasure = nSorted;
+ }
+
+ const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
+ if ( pRefMember->IsVisible() )
+ {
+ if ( bIsDataLayout )
+ rRunning.AddColIndex( 0, 0 );
+ else
+ rRunning.AddColIndex( i, nSorted );
+
+ ScDPDataMember* pDataMember = maMembers[nMemberPos].get();
+ pDataMember->UpdateRunningTotals(
+ pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent);
+
+ rRunning.RemoveColIndex();
+ }
+ }
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const
+{
+ OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString("(unknown)");
+ dumpRow("ScDPDataDimension", aDimName, nullptr, pDoc, rPos);
+
+ SCROW nStartRow = rPos.Row();
+
+ tools::Long nCount = bIsDataLayout ? 1 : maMembers.size();
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ const ScDPResultMember* pRefMember = pRefDim->GetMember(i);
+ const ScDPDataMember* pDataMember = maMembers[i].get();
+ pDataMember->DumpState( pRefMember, pDoc, rPos );
+ }
+
+ indent(pDoc, nStartRow, rPos);
+}
+
+void ScDPDataDimension::Dump(int nIndent) const
+{
+ std::string aIndent(nIndent*2, ' ');
+ std::cout << aIndent << "-- data dimension '"
+ << (pResultDimension ? pResultDimension->GetName() : OUString()) << "'" << std::endl;
+ for (auto& rxMember : maMembers)
+ rxMember->Dump(nIndent+1);
+}
+#endif
+
+tools::Long ScDPDataDimension::GetMemberCount() const
+{
+ return maMembers.size();
+}
+
+const ScDPDataMember* ScDPDataDimension::GetMember(tools::Long n) const
+{
+ return maMembers[n].get();
+}
+
+ScDPDataMember* ScDPDataDimension::GetMember(tools::Long n)
+{
+ return maMembers[n].get();
+}
+
+ScDPResultVisibilityData::ScDPResultVisibilityData(
+ ScDPSource* pSource) :
+ mpSource(pSource)
+{
+}
+
+ScDPResultVisibilityData::~ScDPResultVisibilityData()
+{
+}
+
+void ScDPResultVisibilityData::addVisibleMember(const OUString& rDimName, const ScDPItemData& rMemberItem)
+{
+ DimMemberType::iterator itr = maDimensions.find(rDimName);
+ if (itr == maDimensions.end())
+ {
+ pair<DimMemberType::iterator, bool> r = maDimensions.emplace(
+ rDimName, VisibleMemberType());
+
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ VisibleMemberType& rMem = itr->second;
+ rMem.insert(rMemberItem);
+}
+
+void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPFilteredCache::Criterion>& rFilters) const
+{
+ typedef std::unordered_map<OUString, tools::Long> FieldNameMapType;
+ FieldNameMapType aFieldNames;
+ ScDPTableData* pData = mpSource->GetData();
+ sal_Int32 nColumnCount = pData->GetColumnCount();
+ for (sal_Int32 i = 0; i < nColumnCount; ++i)
+ {
+ aFieldNames.emplace(pData->getDimensionName(i), i);
+ }
+
+ const ScDPDimensions* pDims = mpSource->GetDimensionsObject();
+ for (const auto& [rDimName, rMem] : maDimensions)
+ {
+ ScDPFilteredCache::Criterion aCri;
+ FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName);
+ if (itrField == aFieldNames.end())
+ // This should never happen!
+ continue;
+
+ tools::Long nDimIndex = itrField->second;
+ aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex);
+ aCri.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
+
+ ScDPFilteredCache::GroupFilter* pGrpFilter =
+ static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
+
+ for (const ScDPItemData& rMemItem : rMem)
+ {
+ pGrpFilter->addMatchItem(rMemItem);
+ }
+
+ ScDPDimension* pDim = pDims->getByIndex(nDimIndex);
+ ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
+ GetLevelsObject()->getByIndex(0)->GetMembersObject();
+ if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(pMembers->getCount()))
+ rFilters.push_back(aCri);
+ }
+}
+
+size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const
+{
+ if (r.IsValue())
+ return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue()));
+ else
+ return r.GetString().hashCode();
+}
+SCROW ScDPResultMember::GetDataId( ) const
+{
+ const ScDPMember* pMemberDesc = GetDPMember();
+ if (pMemberDesc)
+ return pMemberDesc->GetItemDataId();
+ return -1;
+}
+
+ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData )
+{
+ ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData );
+ SCROW nDataIndex = pMember->GetDataId();
+ maMemberArray.emplace_back( pMember );
+
+ maMemberHash.emplace( nDataIndex, pMember );
+ return pMember;
+}
+
+ScDPResultMember* ScDPResultDimension::InsertMember(const ScDPParentDimData *pMemberData)
+{
+ SCROW nInsert = 0;
+ if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) )
+ {
+ ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData );
+ maMemberArray.emplace( maMemberArray.begin()+nInsert, pNew );
+
+ SCROW nDataIndex = pMemberData->mpMemberDesc->GetItemDataId();
+ maMemberHash.emplace( nDataIndex, pNew );
+ return pNew;
+ }
+ return maMemberArray[ nInsert ].get();
+}
+
+void ScDPResultDimension::InitWithMembers(
+ LateInitParams& rParams, const std::vector<SCROW>& pItemData, size_t nPos,
+ ScDPInitState& rInitState)
+{
+ if ( rParams.IsEnd( nPos ) )
+ return;
+ ScDPDimension* pThisDim = rParams.GetDim( nPos );
+ ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
+ SCROW nDataID = pItemData[nPos];
+
+ if (!(pThisDim && pThisLevel))
+ return;
+
+ tools::Long nDimSource = pThisDim->GetDimension(); //TODO: check GetSourceDim?
+
+ // create all members at the first call (preserve order)
+ ResultMembers& rMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
+ ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
+ // initialize only specific member (or all if "show empty" flag is set)
+ ScDPResultMember* pResultMember = nullptr;
+ if ( bInitialized )
+ pResultMember = FindMember( nDataID );
+ else
+ bInitialized = true;
+
+ if ( pResultMember == nullptr )
+ { //only insert found item
+ const ScDPParentDimData* pMemberData = rMembers.FindMember( nDataID );
+ if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) )
+ pResultMember = InsertMember( pMemberData );
+ }
+ if ( pResultMember )
+ {
+ rInitState.AddMember( nDimSource, pResultMember->GetDataId() );
+ pResultMember->LateInitFrom(rParams, pItemData, nPos+1, rInitState);
+ rInitState.RemoveMember();
+ }
+}
+
+ScDPParentDimData::ScDPParentDimData() :
+ mnOrder(-1), mpParentDim(nullptr), mpParentLevel(nullptr), mpMemberDesc(nullptr) {}
+
+ScDPParentDimData::ScDPParentDimData(
+ SCROW nIndex, const ScDPDimension* pDim, const ScDPLevel* pLev, const ScDPMember* pMember) :
+ mnOrder(nIndex), mpParentDim(pDim), mpParentLevel(pLev), mpMemberDesc(pMember) {}
+
+const ScDPParentDimData* ResultMembers::FindMember( SCROW nIndex ) const
+{
+ auto aRes = maMemberHash.find( nIndex );
+ if( aRes != maMemberHash.end()) {
+ if ( aRes->second.mpMemberDesc && aRes->second.mpMemberDesc->GetItemDataId()==nIndex )
+ return &aRes->second;
+ }
+ return nullptr;
+}
+void ResultMembers::InsertMember( const ScDPParentDimData& rNew )
+{
+ if ( !rNew.mpMemberDesc->getShowDetails() )
+ mbHasHideDetailsMember = true;
+ maMemberHash.emplace( rNew.mpMemberDesc->GetItemDataId(), rNew );
+}
+
+ResultMembers::ResultMembers():
+ mbHasHideDetailsMember( false )
+{
+}
+ResultMembers::~ResultMembers()
+{
+}
+
+LateInitParams::LateInitParams(
+ const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, bool bRow ) :
+ mppDim( ppDim ),
+ mppLev( ppLev ),
+ mbRow( bRow ),
+ mbInitChild( true ),
+ mbAllChildren( false )
+{
+}
+
+bool LateInitParams::IsEnd( size_t nPos ) const
+{
+ return nPos >= mppDim.size();
+}
+
+void ScDPResultDimension::CheckShowEmpty( bool bShow )
+{
+ tools::Long nCount = maMemberArray.size();
+
+ for (tools::Long i=0; i<nCount; i++)
+ {
+ ScDPResultMember* pMember = maMemberArray.at(i).get();
+ pMember->CheckShowEmpty(bShow);
+ }
+
+}
+
+void ScDPResultMember::CheckShowEmpty( bool bShow )
+{
+ if (bHasElements)
+ {
+ ScDPResultDimension* pChildDim = GetChildDimension();
+ if (pChildDim)
+ pChildDim->CheckShowEmpty();
+ }
+ else if (IsValid() && bInitialized)
+ {
+ bShow = bShow || (GetParentLevel() && GetParentLevel()->getShowEmpty());
+ if (bShow)
+ {
+ SetHasElements();
+ ScDPResultDimension* pChildDim = GetChildDimension();
+ if (pChildDim)
+ pChildDim->CheckShowEmpty(true);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dptabsrc.cxx b/sc/source/core/data/dptabsrc.cxx
new file mode 100644
index 0000000000..f589e1b538
--- /dev/null
+++ b/sc/source/core/data/dptabsrc.cxx
@@ -0,0 +1,2609 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dptabsrc.hxx>
+
+#include <algorithm>
+#include <vector>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+
+#include <dpcache.hxx>
+#include <dptabres.hxx>
+#include <dptabdat.hxx>
+#include <global.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <dpitemdata.hxx>
+#include <dputil.hxx>
+#include <dpresfilter.hxx>
+#include <calcmacros.hxx>
+#include <generalfunction.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <com/sun/star/sheet/TableFilterField.hpp>
+
+#include <unotools/calendarwrapper.hxx>
+#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+
+#define SC_MINCOUNT_LIMIT 1000000
+
+SC_SIMPLE_SERVICE_INFO( ScDPSource, "ScDPSource", "com.sun.star.sheet.DataPilotSource" )
+SC_SIMPLE_SERVICE_INFO( ScDPDimensions, "ScDPDimensions", "com.sun.star.sheet.DataPilotSourceDimensions" )
+SC_SIMPLE_SERVICE_INFO( ScDPDimension, "ScDPDimension", "com.sun.star.sheet.DataPilotSourceDimension" )
+
+// Typos are on purpose here, quote from Eike Rathke (see https://gerrit.libreoffice.org/c/core/+/101116):
+// "The typo is exactly why the SC_SIMPLE_SERVICE_INFO_COMPAT() lists both service names,
+// the old with the typo and the new corrected one. Correcting the typo in the old name
+// will make all extensions fail that use it. This is not to be changed."
+SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchies, "ScDPHierarchies",
+ "com.sun.star.sheet.DataPilotSourceHierarchies", "com.sun.star.sheet.DataPilotSourceHierarcies" )
+SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchy, "ScDPHierarchy",
+ "com.sun.star.sheet.DataPilotSourceHierarchy", "com.sun.star.sheet.DataPilotSourceHierarcy" )
+
+SC_SIMPLE_SERVICE_INFO( ScDPLevels, "ScDPLevels", "com.sun.star.sheet.DataPilotSourceLevels" )
+SC_SIMPLE_SERVICE_INFO( ScDPLevel, "ScDPLevel", "com.sun.star.sheet.DataPilotSourceLevel" )
+SC_SIMPLE_SERVICE_INFO( ScDPMembers, "ScDPMembers", "com.sun.star.sheet.DataPilotSourceMembers" )
+SC_SIMPLE_SERVICE_INFO( ScDPMember, "ScDPMember", "com.sun.star.sheet.DataPilotSourceMember" )
+
+// property maps for PropertySetInfo
+// DataDescription / NumberFormat are internal
+
+//TODO: move to a header?
+static bool lcl_GetBoolFromAny( const uno::Any& aAny )
+{
+ auto b = o3tl::tryAccess<bool>(aAny);
+ return b.has_value() && *b;
+}
+
+ScDPSource::ScDPSource( ScDPTableData* pD ) :
+ pData( pD ),
+ bColumnGrand( true ), // default is true
+ bRowGrand( true ),
+ bIgnoreEmptyRows( false ),
+ bRepeatIfEmpty( false ),
+ nDupCount( 0 ),
+ bResultOverflow( false ),
+ bPageFiltered( false )
+{
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+ScDPSource::~ScDPSource()
+{
+ // free lists
+
+ pColResults.reset();
+ pRowResults.reset();
+
+ pColResRoot.reset();
+ pRowResRoot.reset();
+ pResData.reset();
+}
+
+const std::optional<OUString> & ScDPSource::GetGrandTotalName() const
+{
+ return mpGrandTotalName;
+}
+
+sheet::DataPilotFieldOrientation ScDPSource::GetOrientation(sal_Int32 nColumn)
+{
+ if (std::find(maColDims.begin(), maColDims.end(), nColumn) != maColDims.end())
+ return sheet::DataPilotFieldOrientation_COLUMN;
+
+ if (std::find(maRowDims.begin(), maRowDims.end(), nColumn) != maRowDims.end())
+ return sheet::DataPilotFieldOrientation_ROW;
+
+ if (std::find(maDataDims.begin(), maDataDims.end(), nColumn) != maDataDims.end())
+ return sheet::DataPilotFieldOrientation_DATA;
+
+ if (std::find(maPageDims.begin(), maPageDims.end(), nColumn) != maPageDims.end())
+ return sheet::DataPilotFieldOrientation_PAGE;
+
+ return sheet::DataPilotFieldOrientation_HIDDEN;
+}
+
+sal_Int32 ScDPSource::GetDataDimensionCount() const
+{
+ return maDataDims.size();
+}
+
+ScDPDimension* ScDPSource::GetDataDimension(sal_Int32 nIndex)
+{
+ if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maDataDims.size())
+ return nullptr;
+
+ sal_Int32 nDimIndex = maDataDims[nIndex];
+ return GetDimensionsObject()->getByIndex(nDimIndex);
+}
+
+OUString ScDPSource::GetDataDimName(sal_Int32 nIndex)
+{
+ OUString aRet;
+ ScDPDimension* pDim = GetDataDimension(nIndex);
+ if (pDim)
+ aRet = pDim->getName();
+ return aRet;
+}
+
+sal_Int32 ScDPSource::GetPosition(sal_Int32 nColumn)
+{
+ std::vector<sal_Int32>::const_iterator it, itBeg = maColDims.begin(), itEnd = maColDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ itBeg = maRowDims.begin();
+ itEnd = maRowDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ itBeg = maDataDims.begin();
+ itEnd = maDataDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ itBeg = maPageDims.begin();
+ itEnd = maPageDims.end();
+ it = std::find(itBeg, itEnd, nColumn);
+ if (it != itEnd)
+ return std::distance(itBeg, it);
+
+ return 0;
+}
+
+namespace {
+
+bool testSubTotal( bool& rAllowed, sal_Int32 nColumn, const std::vector<sal_Int32>& rDims, ScDPSource* pSource )
+{
+ rAllowed = true;
+ std::vector<sal_Int32>::const_iterator it = rDims.begin(), itEnd = rDims.end();
+ for (; it != itEnd; ++it)
+ {
+ if (*it != nColumn)
+ continue;
+
+ if ( pSource->IsDataLayoutDimension(nColumn) )
+ {
+ // no subtotals for data layout dim, no matter where
+ rAllowed = false;
+ return true;
+ }
+
+ // no subtotals if no other dim but data layout follows
+ ++it;
+ if (it != itEnd && pSource->IsDataLayoutDimension(*it))
+ ++it;
+ if (it == itEnd)
+ rAllowed = false;
+
+ return true; // found
+ }
+
+ return false;
+}
+
+void removeDim( sal_Int32 nRemove, std::vector<sal_Int32>& rDims )
+{
+ std::vector<sal_Int32>::iterator it = std::find(rDims.begin(), rDims.end(), nRemove);
+ if (it != rDims.end())
+ rDims.erase(it);
+}
+
+}
+
+bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn)
+{
+ //TODO: cache this at ScDPResultData
+ bool bAllowed = true;
+ if ( testSubTotal(bAllowed, nColumn, maColDims, this) )
+ return bAllowed;
+ if ( testSubTotal(bAllowed, nColumn, maRowDims, this) )
+ return bAllowed;
+ return bAllowed;
+}
+
+void ScDPSource::SetOrientation(sal_Int32 nColumn, sheet::DataPilotFieldOrientation nNew)
+{
+ //TODO: change to no-op if new orientation is equal to old?
+
+ // remove from old list
+ removeDim(nColumn, maColDims);
+ removeDim(nColumn, maRowDims);
+ removeDim(nColumn, maDataDims);
+ removeDim(nColumn, maPageDims);
+
+ // add to new list
+ switch (nNew)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ maColDims.push_back(nColumn);
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ maRowDims.push_back(nColumn);
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ maDataDims.push_back(nColumn);
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ maPageDims.push_back(nColumn);
+ break;
+ // DataPilot Migration - Cache&&Performance
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ break;
+ default:
+ OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" );
+ break;
+ }
+}
+
+bool ScDPSource::IsDataLayoutDimension(sal_Int32 nDim)
+{
+ return nDim == pData->GetColumnCount();
+}
+
+sheet::DataPilotFieldOrientation ScDPSource::GetDataLayoutOrientation()
+{
+ return GetOrientation(pData->GetColumnCount());
+}
+
+bool ScDPSource::IsDateDimension(sal_Int32 nDim)
+{
+ return pData->IsDateDimension(nDim);
+}
+
+ScDPDimensions* ScDPSource::GetDimensionsObject()
+{
+ if (!pDimensions.is())
+ {
+ pDimensions = new ScDPDimensions(this);
+ }
+ return pDimensions.get();
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions()
+{
+ return GetDimensionsObject();
+}
+
+void ScDPSource::SetDupCount( tools::Long nNew )
+{
+ nDupCount = nNew;
+}
+
+ScDPDimension* ScDPSource::AddDuplicated(std::u16string_view rNewName)
+{
+ OSL_ENSURE( pDimensions.is(), "AddDuplicated without dimensions?" );
+
+ // re-use
+
+ tools::Long nOldDimCount = pDimensions->getCount();
+ for (tools::Long i=0; i<nOldDimCount; i++)
+ {
+ ScDPDimension* pDim = pDimensions->getByIndex(i);
+ if (pDim && pDim->getName() == rNewName)
+ {
+ //TODO: test if pDim is a duplicate of source
+ return pDim;
+ }
+ }
+
+ SetDupCount( nDupCount + 1 );
+ pDimensions->CountChanged(); // uses nDupCount
+
+ return pDimensions->getByIndex( pDimensions->getCount() - 1 );
+}
+
+sal_Int32 ScDPSource::GetSourceDim(sal_Int32 nDim)
+{
+ // original source dimension or data layout dimension?
+ if ( nDim <= pData->GetColumnCount() )
+ return nDim;
+
+ if ( nDim < pDimensions->getCount() )
+ {
+ ScDPDimension* pDimObj = pDimensions->getByIndex( nDim );
+ if ( pDimObj )
+ {
+ tools::Long nSource = pDimObj->GetSourceDim();
+ if ( nSource >= 0 )
+ return nSource;
+ }
+ }
+
+ OSL_FAIL("GetSourceDim: wrong dim");
+ return nDim;
+}
+
+uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults()
+{
+ CreateRes_Impl(); // create pColResRoot and pRowResRoot
+
+ if ( bResultOverflow ) // set in CreateRes_Impl
+ {
+ // no results available
+ throw uno::RuntimeException();
+ }
+
+ sal_Int32 nColCount = pColResRoot->GetSize(pResData->GetColStartMeasure());
+ sal_Int32 nRowCount = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
+
+ // allocate full sequence
+ //TODO: leave out empty rows???
+
+ uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount );
+ uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<sheet::DataResult> aColSeq( nColCount );
+ // use default values of DataResult
+ pRowAry[nRow] = aColSeq;
+ }
+
+ ScDPResultFilterContext aFilterCxt;
+ pRowResRoot->FillDataResults(
+ pColResRoot.get(), aFilterCxt, aSeq, pResData->GetRowStartMeasure());
+
+ maResFilterSet.swap(aFilterCxt.maFilterSet); // Keep this data for GETPIVOTDATA.
+
+ return aSeq;
+}
+
+uno::Sequence<double> ScDPSource::getFilteredResults(
+ const uno::Sequence<sheet::DataPilotFieldFilter>& aFilters )
+{
+ if (maResFilterSet.empty())
+ getResults(); // Build result tree first.
+
+ // Get result values from the tree.
+ const ScDPResultTree::ValuesType* pVals = maResFilterSet.getResults(aFilters);
+ if (pVals && !pVals->empty())
+ {
+ return comphelper::containerToSequence(*pVals);
+ }
+
+ if (aFilters.getLength() == 1)
+ {
+ // Try to get result from the leaf nodes.
+ double fVal = maResFilterSet.getLeafResult(aFilters[0]);
+ if (!std::isnan(fVal))
+ {
+ return { fVal };
+ }
+ }
+
+ return uno::Sequence<double>();
+}
+
+void SAL_CALL ScDPSource::refresh()
+{
+ disposeData();
+}
+
+void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters)
+{
+ sal_Int32 nColumnCount = GetData()->GetColumnCount();
+
+ vector<ScDPFilteredCache::Criterion> aFilterCriteria;
+ for (const sheet::DataPilotFieldFilter& rFilter : aFilters)
+ {
+ const OUString& aFieldName = rFilter.FieldName;
+ for (sal_Int32 nCol = 0; nCol < nColumnCount; ++nCol)
+ {
+ if (aFieldName == pData->getDimensionName(nCol))
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol );
+ ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
+ GetLevelsObject()->getByIndex(0)->GetMembersObject();
+ sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValueName );
+ if ( nIndex >= 0 )
+ {
+ ScDPItemData aItem(pMembers->getByIndex(nIndex)->FillItemData());
+ aFilterCriteria.emplace_back( );
+ aFilterCriteria.back().mnFieldIndex = nCol;
+ aFilterCriteria.back().mpFilter =
+ std::make_shared<ScDPFilteredCache::SingleFilter>(aItem);
+ }
+ }
+ }
+ }
+
+ // Take into account the visibilities of field members.
+ ScDPResultVisibilityData aResVisData(this);
+ pRowResRoot->FillVisibilityData(aResVisData);
+ pColResRoot->FillVisibilityData(aResVisData);
+ aResVisData.fillFieldFilters(aFilterCriteria);
+
+ Sequence< Sequence<Any> > aTabData;
+ std::unordered_set<sal_Int32> aCatDims;
+ GetCategoryDimensionIndices(aCatDims);
+ pData->GetDrillDownData(std::move(aFilterCriteria), std::move(aCatDims), aTabData);
+ return aTabData;
+}
+
+OUString ScDPSource::getDataDescription()
+{
+ CreateRes_Impl(); // create pResData
+
+ OUString aRet;
+ if ( pResData->GetMeasureCount() == 1 )
+ {
+ bool bTotalResult = false;
+ aRet = pResData->GetMeasureString(0, true, SUBTOTAL_FUNC_NONE, bTotalResult);
+ }
+
+ // empty for more than one measure
+
+ return aRet;
+}
+
+void ScDPSource::setIgnoreEmptyRows(bool bSet)
+{
+ bIgnoreEmptyRows = bSet;
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+void ScDPSource::setRepeatIfEmpty(bool bSet)
+{
+ bRepeatIfEmpty = bSet;
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+void ScDPSource::disposeData()
+{
+ maResFilterSet.clear();
+
+ if ( pResData )
+ {
+ // reset all data...
+
+ pColResRoot.reset();
+ pRowResRoot.reset();
+ pResData.reset();
+ pColResults.reset();
+ pRowResults.reset();
+ aColLevelList.clear();
+ aRowLevelList.clear();
+ }
+
+ pDimensions.clear(); // settings have to be applied (from SaveData) again!
+ SetDupCount( 0 );
+
+ maColDims.clear();
+ maRowDims.clear();
+ maDataDims.clear();
+ maPageDims.clear();
+
+ pData->DisposeData(); // cached entries etc.
+ bPageFiltered = false;
+ bResultOverflow = false;
+}
+
+static tools::Long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, tools::Long nLevels )
+{
+ // Calculate the product of the member count for those consecutive levels that
+ // have the "show all" flag, one following level, and the data layout dimension.
+
+ tools::Long nTotal = 1;
+ tools::Long nDataCount = 1;
+ bool bWasShowAll = true;
+ tools::Long nPos = nLevels;
+ while ( nPos > 0 )
+ {
+ --nPos;
+
+ if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] )
+ {
+ OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
+ return 0;
+ }
+
+ bool bDo = false;
+ if ( ppDim[nPos]->getIsDataLayoutDimension() )
+ {
+ // data layout dim doesn't interfere with "show all" flags
+ nDataCount = ppLevel[nPos]->GetMembersObject()->getCount();
+ if ( nDataCount == 0 )
+ nDataCount = 1;
+ }
+ else if ( bWasShowAll ) // "show all" set for all following levels?
+ {
+ bDo = true;
+ if ( !ppLevel[nPos]->getShowEmpty() )
+ {
+ // this level is counted, following ones are not
+ bWasShowAll = false;
+ }
+ }
+ if ( bDo )
+ {
+ tools::Long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers();
+ if ( nThisCount == 0 )
+ {
+ nTotal = 1; // empty level -> start counting from here
+ //TODO: start with visible elements in this level?
+ }
+ else
+ {
+ if ( nTotal >= LONG_MAX / nThisCount )
+ return LONG_MAX; // overflow
+ nTotal *= nThisCount;
+ }
+ }
+ }
+
+ // always include data layout dim, even after restarting
+ if ( nTotal >= LONG_MAX / nDataCount )
+ return LONG_MAX; // overflow
+ nTotal *= nDataCount;
+
+ return nTotal;
+}
+
+void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow)
+{
+ const std::vector<sal_Int32>& rDims = bIsRow ? maRowDims : maColDims;
+ for (const auto& rDimIndex : rDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+ ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
+ sal_Int32 nCount = pLevels->getCount();
+
+ //TODO: Test
+ if (pDim->getIsDataLayoutDimension() && maDataDims.size() < 2)
+ nCount = 0;
+ //TODO: Test
+
+ for (sal_Int32 j = 0; j < nCount; ++j)
+ {
+ ScDPLevel* pLevel = pLevels->getByIndex(j);
+ pLevel->EvaluateSortOrder();
+
+ // no layout flags for column fields, only for row fields
+ pLevel->SetEnableLayout( bIsRow );
+
+ if ( pLevel->GetAutoShow().IsEnabled )
+ rHasAutoShow = true;
+
+ if (bIsRow)
+ {
+ rInfo.aRowLevelDims.push_back(rDimIndex);
+ rInfo.aRowDims.push_back(pDim);
+ rInfo.aRowLevels.push_back(pLevel);
+ }
+ else
+ {
+ rInfo.aColLevelDims.push_back(rDimIndex);
+ rInfo.aColDims.push_back(pDim);
+ rInfo.aColLevels.push_back(pLevel);
+ }
+
+ pLevel->GetMembersObject(); // initialize for groups
+ }
+ }
+}
+
+namespace {
+
+class CategoryDimInserter
+{
+ ScDPSource& mrSource;
+ std::unordered_set<sal_Int32>& mrCatDims;
+public:
+ CategoryDimInserter(ScDPSource& rSource, std::unordered_set<sal_Int32>& rCatDims) :
+ mrSource(rSource),
+ mrCatDims(rCatDims) {}
+
+ void operator() (tools::Long nDim)
+ {
+ if (!mrSource.IsDataLayoutDimension(nDim))
+ mrCatDims.insert(nDim);
+ }
+};
+
+}
+
+void ScDPSource::GetCategoryDimensionIndices(std::unordered_set<sal_Int32>& rCatDims)
+{
+ std::unordered_set<sal_Int32> aCatDims;
+
+ CategoryDimInserter aInserter(*this, aCatDims);
+ std::for_each(maColDims.begin(), maColDims.end(), aInserter);
+ std::for_each(maRowDims.begin(), maRowDims.end(), aInserter);
+ std::for_each(maPageDims.begin(), maPageDims.end(), aInserter);
+
+ rCatDims.swap(aCatDims);
+}
+
+void ScDPSource::FilterCacheByPageDimensions()
+{
+ // #i117661# Repeated calls to ScDPFilteredCache::filterByPageDimension
+ // are invalid because rows are only hidden, never shown again. If
+ // FilterCacheByPageDimensions is called again, the cache table must
+ // be re-initialized. Currently, CreateRes_Impl always uses a fresh cache
+ // because ScDBDocFunc::DataPilotUpdate calls InvalidateData.
+
+ if (bPageFiltered)
+ {
+ SAL_WARN( "sc.core","tried to apply page field filters several times");
+
+ pData->DisposeData();
+ pData->CreateCacheTable(); // re-initialize the cache table
+ bPageFiltered = false;
+ }
+
+ // filter table by page dimensions.
+ vector<ScDPFilteredCache::Criterion> aCriteria;
+ for (const auto& rDimIndex : maPageDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ tools::Long nField = pDim->GetDimension();
+
+ ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
+ GetLevelsObject()->getByIndex(0)->GetMembersObject();
+
+ tools::Long nMemCount = pMems->getCount();
+ ScDPFilteredCache::Criterion aFilter;
+ aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
+ aFilter.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
+ ScDPFilteredCache::GroupFilter* pGrpFilter =
+ static_cast<ScDPFilteredCache::GroupFilter*>(aFilter.mpFilter.get());
+ for (tools::Long j = 0; j < nMemCount; ++j)
+ {
+ ScDPMember* pMem = pMems->getByIndex(j);
+ if (pMem->isVisible())
+ {
+ ScDPItemData aData(pMem->FillItemData());
+ pGrpFilter->addMatchItem(aData);
+ }
+ }
+ if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(nMemCount))
+ // there is at least one invisible item. Add this filter criterion to the mix.
+ aCriteria.push_back(aFilter);
+
+ if (!pDim->HasSelectedPage())
+ continue;
+
+ const ScDPItemData& rData = pDim->GetSelectedData();
+ aCriteria.emplace_back();
+ ScDPFilteredCache::Criterion& r = aCriteria.back();
+ r.mnFieldIndex = static_cast<sal_Int32>(nField);
+ r.mpFilter = std::make_shared<ScDPFilteredCache::SingleFilter>(rData);
+ }
+ if (!aCriteria.empty())
+ {
+ std::unordered_set<sal_Int32> aCatDims;
+ GetCategoryDimensionIndices(aCatDims);
+ pData->FilterCacheTable(std::move(aCriteria), std::move(aCatDims));
+ bPageFiltered = true;
+ }
+}
+
+void ScDPSource::CreateRes_Impl()
+{
+ if (pResData)
+ return;
+
+ sheet::DataPilotFieldOrientation nDataOrient = GetDataLayoutOrientation();
+ if (maDataDims.size() > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
+ nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
+ {
+ // if more than one data dimension, data layout orientation must be set
+ SetOrientation( pData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW );
+ nDataOrient = sheet::DataPilotFieldOrientation_ROW;
+ }
+
+ // TODO: Aggregate pDataNames, pDataRefValues, nDataRefOrient, and
+ // eDataFunctions into a structure and use vector instead of static
+ // or pointer arrays.
+ vector<OUString> aDataNames;
+ vector<sheet::DataPilotFieldReference> aDataRefValues;
+ vector<ScSubTotalFunc> aDataFunctions;
+ vector<sheet::DataPilotFieldOrientation> aDataRefOrient;
+
+ ScDPTableData::CalcInfo aInfo;
+
+ // LateInit (initialize only those rows/children that are used) can be used unless
+ // any data dimension needs reference values from column/row dimensions
+ bool bLateInit = true;
+
+ // Go through all data dimensions (i.e. fields) and build their meta data
+ // so that they can be passed on to ScDPResultData instance later.
+ // TODO: aggregate all of data dimension info into a structure.
+ for (const tools::Long nDimIndex : maDataDims)
+ {
+ // Get function for each data field.
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
+ ScGeneralFunction eUser = pDim->getFunction();
+ if (eUser == ScGeneralFunction::AUTO)
+ {
+ //TODO: test for numeric data
+ eUser = ScGeneralFunction::SUM;
+ }
+
+ // Map UNO's enum to internal enum ScSubTotalFunc.
+ aDataFunctions.push_back(ScDPUtil::toSubTotalFunc(eUser));
+
+ // Get reference field/item information.
+ aDataRefValues.push_back(pDim->GetReferenceValue());
+ sheet::DataPilotFieldOrientation nDataRefOrient = sheet::DataPilotFieldOrientation_HIDDEN; // default if not used
+ sal_Int32 eRefType = aDataRefValues.back().ReferenceType;
+ if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
+ eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
+ eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
+ {
+ sal_Int32 nColumn = comphelper::findValue(
+ GetDimensionsObject()->getElementNames(), aDataRefValues.back().ReferenceField);
+ if ( nColumn >= 0 )
+ {
+ nDataRefOrient = GetOrientation(nColumn);
+ // need fully initialized results to find reference values
+ // (both in column or row dimensions), so updated values or
+ // differences to 0 can be displayed even for empty results.
+ bLateInit = false;
+ }
+ }
+
+ aDataRefOrient.push_back(nDataRefOrient);
+
+ aDataNames.push_back(pDim->getName());
+
+ //TODO: modify user visible strings as in ScDPResultData::GetMeasureString instead!
+
+ aDataNames.back() = ScDPUtil::getSourceDimensionName(aDataNames.back());
+
+ //TODO: if the name is overridden by user, a flag must be set
+ //TODO: so the user defined name replaces the function string and field name.
+
+ //TODO: the complete name (function and field) must be stored at the dimension
+
+ tools::Long nSource = pDim->GetSourceDim();
+ if (nSource >= 0)
+ aInfo.aDataSrcCols.push_back(nSource);
+ else
+ aInfo.aDataSrcCols.push_back(nDimIndex);
+ }
+
+ pResData.reset( new ScDPResultData(*this) );
+ pResData->SetMeasureData(aDataFunctions, aDataRefValues, aDataRefOrient, aDataNames);
+ pResData->SetDataLayoutOrientation(nDataOrient);
+ pResData->SetLateInit( bLateInit );
+
+ bool bHasAutoShow = false;
+
+ ScDPInitState aInitState;
+
+ // Page field selections restrict the members shown in related fields
+ // (both in column and row fields). aInitState is filled with the page
+ // field selections, they are kept across the data iterator loop.
+
+ for (const auto& rDimIndex : maPageDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ if ( pDim->HasSelectedPage() )
+ aInitState.AddMember(rDimIndex, GetCache()->GetIdByItemData(rDimIndex, pDim->GetSelectedData()));
+ }
+
+ // Show grand total columns only when the option is set *and* there is at
+ // least one column field. Same for the grand total rows.
+ sheet::DataPilotFieldOrientation nDataLayoutOrient = GetDataLayoutOrientation();
+ tools::Long nColDimCount2 = maColDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_COLUMN ? 1 : 0);
+ tools::Long nRowDimCount2 = maRowDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_ROW ? 1 : 0);
+ bool bShowColGrand = bColumnGrand && nColDimCount2 > 0;
+ bool bShowRowGrand = bRowGrand && nRowDimCount2 > 0;
+ pColResRoot.reset( new ScDPResultMember(pResData.get(), bShowColGrand) );
+ pRowResRoot.reset( new ScDPResultMember(pResData.get(), bShowRowGrand) );
+
+ FillCalcInfo(false, aInfo, bHasAutoShow);
+ tools::Long nColLevelCount = aInfo.aColLevels.size();
+
+ pColResRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState );
+ pColResRoot->SetHasElements();
+
+ FillCalcInfo(true, aInfo, bHasAutoShow);
+ tools::Long nRowLevelCount = aInfo.aRowLevels.size();
+
+ if ( nRowLevelCount > 0 )
+ {
+ // disable layout flags for the innermost row field (level)
+ aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( false );
+ }
+
+ pRowResRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState );
+ pRowResRoot->SetHasElements();
+
+ // initialize members object also for all page dimensions (needed for numeric groups)
+ for (const auto& rDimIndex : maPageDims)
+ {
+ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
+ tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+
+ ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
+ tools::Long nCount = pLevels->getCount();
+ for (tools::Long j=0; j<nCount; j++)
+ pLevels->getByIndex(j)->GetMembersObject(); // initialize for groups
+ }
+
+ // pre-check: calculate minimum number of result columns / rows from
+ // levels that have the "show all" flag set
+
+ tools::Long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount );
+ tools::Long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount );
+
+ if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
+ {
+ // resulting table is too big -> abort before calculating
+ // (this relies on late init, so no members are allocated in InitFrom above)
+
+ bResultOverflow = true;
+ return;
+ }
+
+ FilterCacheByPageDimensions();
+
+ aInfo.aPageDims = maPageDims;
+ aInfo.pInitState = &aInitState;
+ aInfo.pColRoot = pColResRoot.get();
+ aInfo.pRowRoot = pRowResRoot.get();
+ pData->CalcResults(aInfo, false);
+
+ pColResRoot->CheckShowEmpty();
+ pRowResRoot->CheckShowEmpty();
+
+ // With all data processed, calculate the final results:
+
+ // UpdateDataResults calculates all original results from the collected values,
+ // and stores them as reference values if needed.
+ pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
+
+ if ( bHasAutoShow ) // do the double calculation only if AutoShow is used
+ {
+ // Find the desired members and set bAutoHidden flag for the others
+ pRowResRoot->DoAutoShow( pColResRoot.get() );
+
+ // Reset all results to empty, so they can be built again with data for the
+ // desired members only.
+ pColResRoot->ResetResults();
+ pRowResRoot->ResetResults();
+ pData->CalcResults(aInfo, true);
+
+ // Call UpdateDataResults again, with the new (limited) values.
+ pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
+ }
+
+ // SortMembers does the sorting by a result dimension, using the original results,
+ // but not running totals etc.
+ pRowResRoot->SortMembers( pColResRoot.get() );
+
+ // UpdateRunningTotals calculates running totals along column/row dimensions,
+ // differences from other members (named or relative), and column/row percentages
+ // or index values.
+ // Running totals and relative differences need to be done using the sorted values.
+ // Column/row percentages and index values must be done after sorting, because the
+ // results may no longer be in the right order (row total for percentage of row is
+ // always 1).
+ ScDPRunningTotalState aRunning( pColResRoot.get(), pRowResRoot.get() );
+ ScDPRowTotals aTotals;
+ pRowResRoot->UpdateRunningTotals( pColResRoot.get(), pResData->GetRowStartMeasure(), aRunning, aTotals );
+
+#if DUMP_PIVOT_TABLE
+ DumpResults();
+#endif
+}
+
+void ScDPSource::FillLevelList( sheet::DataPilotFieldOrientation nOrientation, std::vector<ScDPLevel*> &rList )
+{
+ rList.clear();
+
+ std::vector<sal_Int32>* pDimIndex = nullptr;
+ switch (nOrientation)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ pDimIndex = &maColDims;
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ pDimIndex = &maRowDims;
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ pDimIndex = &maDataDims;
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ pDimIndex = &maPageDims;
+ break;
+ default:
+ OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
+ break;
+ }
+ if (!pDimIndex)
+ {
+ OSL_FAIL("invalid orientation");
+ return;
+ }
+
+ ScDPDimensions* pDims = GetDimensionsObject();
+ for (const auto& rIndex : *pDimIndex)
+ {
+ ScDPDimension* pDim = pDims->getByIndex(rIndex);
+ OSL_ENSURE( pDim->getOrientation() == nOrientation, "orientations are wrong" );
+
+ ScDPHierarchies* pHiers = pDim->GetHierarchiesObject();
+ sal_Int32 nHierarchy = ScDPDimension::getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+ ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy);
+ ScDPLevels* pLevels = pHier->GetLevelsObject();
+ sal_Int32 nLevCount = pLevels->getCount();
+ for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
+ {
+ ScDPLevel* pLevel = pLevels->getByIndex(nLev);
+ rList.push_back(pLevel);
+ }
+ }
+}
+
+void ScDPSource::FillMemberResults()
+{
+ if ( pColResults || pRowResults )
+ return;
+
+ CreateRes_Impl();
+
+ if ( bResultOverflow ) // set in CreateRes_Impl
+ {
+ // no results available -> abort (leave empty)
+ // exception is thrown in ScDPSource::getResults
+ return;
+ }
+
+ FillLevelList( sheet::DataPilotFieldOrientation_COLUMN, aColLevelList );
+ sal_Int32 nColLevelCount = aColLevelList.size();
+ if (nColLevelCount)
+ {
+ tools::Long nColDimSize = pColResRoot->GetSize(pResData->GetColStartMeasure());
+ pColResults.reset(new uno::Sequence<sheet::MemberResult>[nColLevelCount]);
+ for (tools::Long i=0; i<nColLevelCount; i++)
+ pColResults[i].realloc(nColDimSize);
+
+ tools::Long nPos = 0;
+ pColResRoot->FillMemberResults( pColResults.get(), nPos, pResData->GetColStartMeasure(),
+ true, nullptr, nullptr );
+ }
+
+ FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList );
+ tools::Long nRowLevelCount = aRowLevelList.size();
+ if (nRowLevelCount)
+ {
+ tools::Long nRowDimSize = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
+ pRowResults.reset( new uno::Sequence<sheet::MemberResult>[nRowLevelCount] );
+ for (tools::Long i=0; i<nRowLevelCount; i++)
+ pRowResults[i].realloc(nRowDimSize);
+
+ tools::Long nPos = 0;
+ pRowResRoot->FillMemberResults( pRowResults.get(), nPos, pResData->GetRowStartMeasure(),
+ true, nullptr, nullptr );
+ }
+}
+
+const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( const ScDPLevel* pLevel )
+{
+ FillMemberResults();
+
+ sal_Int32 i = 0;
+ sal_Int32 nColCount = aColLevelList.size();
+ for (i=0; i<nColCount; i++)
+ {
+ ScDPLevel* pColLevel = aColLevelList[i];
+ if ( pColLevel == pLevel )
+ return &pColResults[i];
+ }
+ sal_Int32 nRowCount = aRowLevelList.size();
+ for (i=0; i<nRowCount; i++)
+ {
+ ScDPLevel* pRowLevel = aRowLevelList[i];
+ if ( pRowLevel == pLevel )
+ return &pRowResults[i];
+ }
+ return nullptr;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
+{
+ using beans::PropertyAttribute::READONLY;
+
+ static const SfxItemPropertyMapEntry aDPSourceMap_Impl[] =
+ {
+ { SC_UNO_DP_COLGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_DATADESC, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_IGNOREEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
+ { SC_UNO_DP_REPEATEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 }, // for sheet data only
+ { SC_UNO_DP_ROWGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_ROWFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
+ { SC_UNO_DP_COLUMNFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
+ { SC_UNO_DP_DATAFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
+ { SC_UNO_DP_GRANDTOTAL_NAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPSourceMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPSource::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if (aPropertyName == SC_UNO_DP_COLGRAND)
+ bColumnGrand = lcl_GetBoolFromAny(aValue);
+ else if (aPropertyName == SC_UNO_DP_ROWGRAND)
+ bRowGrand = lcl_GetBoolFromAny(aValue);
+ else if (aPropertyName == SC_UNO_DP_IGNOREEMPTY)
+ setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_DP_REPEATEMPTY)
+ setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
+ {
+ OUString aName;
+ if (aValue >>= aName)
+ mpGrandTotalName = aName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+}
+
+uno::Any SAL_CALL ScDPSource::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_COLGRAND )
+ aRet <<= bColumnGrand;
+ else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
+ aRet <<= bRowGrand;
+ else if ( aPropertyName == SC_UNO_DP_IGNOREEMPTY )
+ aRet <<= bIgnoreEmptyRows;
+ else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
+ aRet <<= bRepeatIfEmpty;
+ else if ( aPropertyName == SC_UNO_DP_DATADESC ) // read-only
+ aRet <<= getDataDescription();
+ else if ( aPropertyName == SC_UNO_DP_ROWFIELDCOUNT ) // read-only
+ aRet <<= static_cast<sal_Int32>(maRowDims.size());
+ else if ( aPropertyName == SC_UNO_DP_COLUMNFIELDCOUNT ) // read-only
+ aRet <<= static_cast<sal_Int32>(maColDims.size());
+ else if ( aPropertyName == SC_UNO_DP_DATAFIELDCOUNT ) // read-only
+ aRet <<= static_cast<sal_Int32>(maDataDims.size());
+ else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
+ {
+ if (mpGrandTotalName)
+ aRet <<= *mpGrandTotalName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+ return aRet;
+}
+
+#if DUMP_PIVOT_TABLE
+void ScDPSource::DumpResults() const
+{
+ std::cout << "+++++ column root" << std::endl;
+ pColResRoot->Dump(1);
+ std::cout << "+++++ row root" << std::endl;
+ pRowResRoot->Dump(1);
+}
+#endif
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource )
+
+ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) :
+ pSource( pSrc )
+{
+ //TODO: hold pSource
+
+ // include data layout dimension and duplicated dimensions
+ nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
+}
+
+ScDPDimensions::~ScDPDimensions()
+{
+ //TODO: release pSource
+}
+
+void ScDPDimensions::CountChanged()
+{
+ // include data layout dimension and duplicated dimensions
+ sal_Int32 nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
+ if ( ppDims )
+ {
+ sal_Int32 i;
+ sal_Int32 nCopy = std::min( nNewCount, nDimCount );
+ rtl::Reference<ScDPDimension>* ppNew = new rtl::Reference<ScDPDimension>[nNewCount];
+
+ for (i=0; i<nCopy; i++) // copy existing dims
+ ppNew[i] = ppDims[i];
+ for (i=nCopy; i<nNewCount; i++) // clear additional pointers
+ ppNew[i] = nullptr;
+
+ ppDims.reset( ppNew );
+ }
+ nDimCount = nNewCount;
+}
+
+// very simple XNameAccess implementation using getCount/getByIndex
+
+uno::Any SAL_CALL ScDPDimensions::getByName( const OUString& aName )
+{
+ sal_Int32 nCount = getCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(i);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+// return uno::Any();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPDimensions::getElementNames()
+{
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(i)->getName();
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDPDimensions::hasByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ return true;
+ return false;
+}
+
+uno::Type SAL_CALL ScDPDimensions::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPDimensions::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+tools::Long ScDPDimensions::getCount() const
+{
+ // in tabular data, every column of source data is a dimension
+
+ return nDimCount;
+}
+
+ScDPDimension* ScDPDimensions::getByIndex(tools::Long nIndex) const
+{
+ if ( nIndex >= 0 && nIndex < nDimCount )
+ {
+ if ( !ppDims )
+ {
+ const_cast<ScDPDimensions*>(this)->ppDims.reset(new rtl::Reference<ScDPDimension>[nDimCount] );
+ for (tools::Long i=0; i<nDimCount; i++)
+ ppDims[i] = nullptr;
+ }
+ if ( !ppDims[nIndex].is() )
+ {
+ ppDims[nIndex] = new ScDPDimension( pSource, nIndex );
+ }
+
+ return ppDims[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+ScDPDimension::ScDPDimension( ScDPSource* pSrc, tools::Long nD ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nFunction( ScGeneralFunction::SUM ), // sum is default
+ nSourceDim( -1 ),
+ bHasSelectedPage( false ),
+ mbHasHiddenMember(false)
+{
+ //TODO: hold pSource
+}
+
+ScDPDimension::~ScDPDimension()
+{
+ //TODO: release pSource
+}
+
+ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
+{
+ if (!mxHierarchies.is())
+ {
+ mxHierarchies = new ScDPHierarchies( pSource, nDim );
+ }
+ return mxHierarchies.get();
+}
+
+const std::optional<OUString> & ScDPDimension::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+const std::optional<OUString> & ScDPDimension::GetSubtotalName() const
+{
+ return mpSubtotalName;
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
+{
+ return GetHierarchiesObject();
+}
+
+OUString SAL_CALL ScDPDimension::getName()
+{
+ if (!aName.isEmpty())
+ return aName;
+ else
+ return pSource->GetData()->getDimensionName( nDim );
+}
+
+void SAL_CALL ScDPDimension::setName( const OUString& rNewName )
+{
+ // used after cloning
+ aName = rNewName;
+}
+
+sheet::DataPilotFieldOrientation ScDPDimension::getOrientation() const
+{
+ return pSource->GetOrientation( nDim );
+}
+
+bool ScDPDimension::getIsDataLayoutDimension() const
+{
+ return pSource->GetData()->getIsDataLayoutDimension( nDim );
+}
+
+void ScDPDimension::setFunction(ScGeneralFunction nNew)
+{
+ nFunction = nNew;
+}
+
+ScDPDimension* ScDPDimension::CreateCloneObject()
+{
+ OSL_ENSURE( nSourceDim < 0, "recursive duplicate - not implemented" );
+
+ //TODO: set new name here, or temporary name ???
+ OUString aNewName = aName;
+
+ ScDPDimension* pNew = pSource->AddDuplicated( aNewName );
+
+ pNew->aName = aNewName; //TODO: here or in source?
+ pNew->nSourceDim = nDim; //TODO: recursive?
+
+ return pNew;
+}
+
+uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone()
+{
+ return CreateCloneObject();
+}
+
+const ScDPItemData& ScDPDimension::GetSelectedData()
+{
+ if ( !pSelectedData )
+ {
+ // find the named member to initialize pSelectedData from it, with name and value
+
+ tools::Long nLevel = 0;
+
+ tools::Long nHierarchy = getUsedHierarchy();
+ if ( nHierarchy >= ScDPHierarchies::getCount() )
+ nHierarchy = 0;
+ ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
+ tools::Long nLevCount = pLevels->getCount();
+ if ( nLevel < nLevCount )
+ {
+ ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject();
+
+ //TODO: merge with ScDPMembers::getByName
+ tools::Long nCount = pMembers->getCount();
+ for (tools::Long i=0; i<nCount && !pSelectedData; i++)
+ {
+ ScDPMember* pMember = pMembers->getByIndex(i);
+ if (aSelectedPage == pMember->GetNameStr(false))
+ {
+ pSelectedData.reset( new ScDPItemData(pMember->FillItemData()) );
+ }
+ }
+ }
+
+ if ( !pSelectedData )
+ pSelectedData.reset( new ScDPItemData(aSelectedPage) ); // default - name only
+ }
+
+ return *pSelectedData;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aDPDimensionMap_Impl[] =
+ {
+ { SC_UNO_DP_FILTER, 0, cppu::UnoType<uno::Sequence<sheet::TableFilterField>>::get(), 0, 0 },
+ { SC_UNO_DP_FLAGS, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_FUNCTION, 0, cppu::UnoType<sheet::GeneralFunction>::get(), 0, 0 },
+ { SC_UNO_DP_FUNCTION2, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNO_DP_ISDATALAYOUT, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_NUMBERFO, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_ORIENTATION, 0, cppu::UnoType<sheet::DataPilotFieldOrientation>::get(), 0, 0 },
+ { SC_UNO_DP_ORIGINAL, 0, cppu::UnoType<container::XNamed>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_DP_ORIGINAL_POS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_REFVALUE, 0, cppu::UnoType<sheet::DataPilotFieldReference>::get(), 0, 0 },
+ { SC_UNO_DP_USEDHIERARCHY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNO_DP_FIELD_SUBTOTALNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNO_DP_HAS_HIDDEN_MEMBER, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPDimensionMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPDimension::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
+ {
+ // #i52547# don't use the incomplete date hierarchy implementation - ignore the call
+ }
+ else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
+ {
+ sheet::DataPilotFieldOrientation eEnum;
+ if (aValue >>= eEnum)
+ pSource->SetOrientation( nDim, eEnum );
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION )
+ {
+ sheet::GeneralFunction eEnum;
+ if (aValue >>= eEnum)
+ setFunction( static_cast<ScGeneralFunction>(eEnum) );
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
+ {
+ sal_Int16 eEnum;
+ if (aValue >>= eEnum)
+ setFunction( static_cast<ScGeneralFunction>(eEnum) );
+ }
+ else if ( aPropertyName == SC_UNO_DP_REFVALUE )
+ aValue >>= aReferenceValue;
+ else if ( aPropertyName == SC_UNO_DP_FILTER )
+ {
+ bool bDone = false;
+ uno::Sequence<sheet::TableFilterField> aSeq;
+ if (aValue >>= aSeq)
+ {
+ sal_Int32 nLength = aSeq.getLength();
+ if ( nLength == 0 )
+ {
+ aSelectedPage.clear();
+ bHasSelectedPage = false;
+ bDone = true;
+ }
+ else if ( nLength == 1 )
+ {
+ const sheet::TableFilterField& rField = aSeq[0];
+ if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
+ {
+ aSelectedPage = rField.StringValue;
+ bHasSelectedPage = true;
+ bDone = true;
+ }
+ }
+ }
+ if ( !bDone )
+ {
+ OSL_FAIL("Filter property is not a single string");
+ throw lang::IllegalArgumentException();
+ }
+ pSelectedData.reset(); // invalid after changing aSelectedPage
+ }
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ {
+ OUString aTmpName;
+ if (aValue >>= aTmpName)
+ mpLayoutName = aTmpName;
+ }
+ else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
+ {
+ OUString aTmpName;
+ if (aValue >>= aTmpName)
+ mpSubtotalName = aTmpName;
+ }
+ else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
+ {
+ bool b = false;
+ aValue >>= b;
+ mbHasHiddenMember = b;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+}
+
+uno::Any SAL_CALL ScDPDimension::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_POSITION )
+ aRet <<= pSource->GetPosition( nDim );
+ else if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
+ aRet <<= static_cast<sal_Int32>(getUsedHierarchy());
+ else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
+ {
+ sheet::DataPilotFieldOrientation eVal = getOrientation();
+ aRet <<= eVal;
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION )
+ {
+ ScGeneralFunction nVal = getFunction();
+ if (nVal == ScGeneralFunction::MEDIAN)
+ nVal = ScGeneralFunction::NONE;
+ const int nValAsInt = static_cast<int>(nVal);
+ assert(nValAsInt >= int(css::sheet::GeneralFunction_NONE) &&
+ nValAsInt <= int(css::sheet::GeneralFunction_VARP));
+ aRet <<= static_cast<sheet::GeneralFunction>(nValAsInt);
+ }
+ else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
+ {
+ ScGeneralFunction eVal = getFunction();
+ aRet <<= static_cast<sal_Int16>(eVal);
+ }
+ else if ( aPropertyName == SC_UNO_DP_REFVALUE )
+ aRet <<= aReferenceValue;
+ else if ( aPropertyName == SC_UNO_DP_ISDATALAYOUT ) // read-only properties
+ aRet <<= getIsDataLayoutDimension();
+ else if ( aPropertyName == SC_UNO_DP_NUMBERFO )
+ {
+ sal_Int32 nFormat = 0;
+ ScGeneralFunction eFunc = getFunction();
+ // #i63745# don't use source format for "count"
+ if ( eFunc != ScGeneralFunction::COUNT && eFunc != ScGeneralFunction::COUNTNUMS )
+ nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim );
+
+ switch ( aReferenceValue.ReferenceType )
+ {
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
+ case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
+ nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_PERCENT_DEC2 );
+ break;
+ case sheet::DataPilotFieldReferenceType::INDEX:
+ nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_NUMBER_SYSTEM );
+ break;
+ default:
+ break;
+ }
+
+ aRet <<= nFormat;
+ }
+ else if ( aPropertyName == SC_UNO_DP_ORIGINAL )
+ {
+ uno::Reference<container::XNamed> xOriginal;
+ if (nSourceDim >= 0)
+ xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim);
+ aRet <<= xOriginal;
+ }
+ else if (aPropertyName == SC_UNO_DP_ORIGINAL_POS)
+ {
+ aRet <<= nSourceDim;
+ }
+ else if ( aPropertyName == SC_UNO_DP_FILTER )
+ {
+ if ( bHasSelectedPage )
+ {
+ // single filter field: first field equal to selected string
+ sheet::TableFilterField aField( sheet::FilterConnection_AND, 0,
+ sheet::FilterOperator_EQUAL, false, 0.0, aSelectedPage );
+ aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 );
+ }
+ else
+ aRet <<= uno::Sequence<sheet::TableFilterField>(0);
+ }
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ aRet <<= mpLayoutName ? *mpLayoutName : OUString();
+ else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
+ aRet <<= mpSubtotalName ? *mpSubtotalName : OUString();
+ else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
+ aRet <<= mbHasHiddenMember;
+ else if (aPropertyName == SC_UNO_DP_FLAGS)
+ {
+ aRet <<= sal_Int32(0); // tabular data: all orientations are possible
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ //TODO: THROW( UnknownPropertyException() );
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension )
+
+ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, tools::Long nD ) :
+ pSource( pSrc ),
+ nDim( nD )
+{
+ //TODO: hold pSource
+}
+
+ScDPHierarchies::~ScDPHierarchies()
+{
+ //TODO: release pSource
+}
+
+// very simple XNameAccess implementation using getCount/getByIndex
+
+uno::Any SAL_CALL ScDPHierarchies::getByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(i);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPHierarchies::getElementNames()
+{
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(i)->getName();
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDPHierarchies::hasByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ return true;
+ return false;
+}
+
+uno::Type SAL_CALL ScDPHierarchies::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPHierarchies::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+sal_Int32 ScDPHierarchies::getCount()
+{
+ return nHierCount;
+}
+
+ScDPHierarchy* ScDPHierarchies::getByIndex(tools::Long nIndex) const
+{
+ // pass hierarchy index to new object in case the implementation
+ // will be extended to more than one hierarchy
+
+ if ( nIndex >= 0 && nIndex < nHierCount )
+ {
+ if ( !ppHiers )
+ {
+ const_cast<ScDPHierarchies*>(this)->ppHiers.reset( new rtl::Reference<ScDPHierarchy>[nHierCount] );
+ for (sal_Int32 i=0; i<nHierCount; i++)
+ ppHiers[i] = nullptr;
+ }
+ if ( !ppHiers[nIndex].is() )
+ {
+ ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
+ }
+
+ return ppHiers[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH )
+{
+ //TODO: hold pSource
+}
+
+ScDPHierarchy::~ScDPHierarchy()
+{
+ //TODO: release pSource
+}
+
+ScDPLevels* ScDPHierarchy::GetLevelsObject()
+{
+ if (!mxLevels.is())
+ {
+ mxLevels = new ScDPLevels( pSource, nDim, nHier );
+ }
+ return mxLevels.get();
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels()
+{
+ return GetLevelsObject();
+}
+
+OUString SAL_CALL ScDPHierarchy::getName()
+{
+ OUString aRet; //TODO: globstr-ID !!!!
+ switch (nHier)
+ {
+ case SC_DAPI_HIERARCHY_FLAT:
+ aRet = "flat";
+ break; //TODO: name ???????
+ case SC_DAPI_HIERARCHY_QUARTER:
+ aRet = "Quarter";
+ break; //TODO: name ???????
+ case SC_DAPI_HIERARCHY_WEEK:
+ aRet = "Week";
+ break; //TODO: name ???????
+ default:
+ OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
+ break;
+ }
+ return aRet;
+}
+
+void SAL_CALL ScDPHierarchy::setName( const OUString& /* rNewName */ )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+ScDPLevels::ScDPLevels( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH )
+{
+ //TODO: hold pSource
+
+ // text columns have only one level
+
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDateDimension( nSrcDim ) )
+ {
+ switch ( nHier )
+ {
+ case SC_DAPI_HIERARCHY_FLAT: nLevCount = SC_DAPI_FLAT_LEVELS; break;
+ case SC_DAPI_HIERARCHY_QUARTER: nLevCount = SC_DAPI_QUARTER_LEVELS; break;
+ case SC_DAPI_HIERARCHY_WEEK: nLevCount = SC_DAPI_WEEK_LEVELS; break;
+ default:
+ OSL_FAIL("wrong hierarchy");
+ nLevCount = 0;
+ }
+ }
+ else
+ nLevCount = 1;
+}
+
+ScDPLevels::~ScDPLevels()
+{
+ //TODO: release pSource
+}
+
+// very simple XNameAccess implementation using getCount/getByIndex
+
+uno::Any SAL_CALL ScDPLevels::getByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(i);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPLevels::getElementNames()
+{
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(i)->getName();
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDPLevels::hasByName( const OUString& aName )
+{
+ tools::Long nCount = getCount();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( getByIndex(i)->getName() == aName )
+ return true;
+ return false;
+}
+
+uno::Type SAL_CALL ScDPLevels::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPLevels::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+sal_Int32 ScDPLevels::getCount() const
+{
+ return nLevCount;
+}
+
+ScDPLevel* ScDPLevels::getByIndex(sal_Int32 nIndex) const
+{
+ if ( nIndex >= 0 && nIndex < nLevCount )
+ {
+ if ( !ppLevs )
+ {
+ const_cast<ScDPLevels*>(this)->ppLevs.reset(new rtl::Reference<ScDPLevel>[nLevCount] );
+ for (tools::Long i=0; i<nLevCount; i++)
+ ppLevs[i] = nullptr;
+ }
+ if ( !ppLevs[nIndex].is() )
+ {
+ ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex );
+ }
+
+ return ppLevs[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+namespace {
+
+class ScDPGlobalMembersOrder
+{
+ ScDPLevel& rLevel;
+ bool bAscending;
+
+public:
+ ScDPGlobalMembersOrder( ScDPLevel& rLev, bool bAsc ) :
+ rLevel(rLev),
+ bAscending(bAsc)
+ {}
+
+ bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
+};
+
+}
+
+bool ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
+{
+ sal_Int32 nCompare = 0;
+ // seems that some ::std::sort() implementations pass the same index twice
+ if( nIndex1 != nIndex2 )
+ {
+ ScDPMembers* pMembers = rLevel.GetMembersObject();
+ ScDPMember* pMember1 = pMembers->getByIndex(nIndex1);
+ ScDPMember* pMember2 = pMembers->getByIndex(nIndex2);
+ nCompare = pMember1->Compare( *pMember2 );
+ }
+ return bAscending ? (nCompare < 0) : (nCompare > 0);
+}
+
+ScDPLevel::ScDPLevel( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH ),
+ nLev( nL ),
+ aSortInfo( OUString(), true, sheet::DataPilotFieldSortMode::NAME ), // default: sort by name
+ nSortMeasure( 0 ),
+ nAutoMeasure( 0 ),
+ bShowEmpty( false ),
+ bEnableLayout( false ),
+ bRepeatItemLabels( false )
+{
+ //TODO: hold pSource
+ // aSubTotals is empty
+}
+
+ScDPLevel::~ScDPLevel()
+{
+ //TODO: release pSource
+}
+
+void ScDPLevel::EvaluateSortOrder()
+{
+ switch (aSortInfo.Mode)
+ {
+ case sheet::DataPilotFieldSortMode::DATA:
+ {
+ // find index of measure (index among data dimensions)
+
+ tools::Long nMeasureCount = pSource->GetDataDimensionCount();
+ for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
+ {
+ if (pSource->GetDataDimName(nMeasure) == aSortInfo.Field)
+ {
+ nSortMeasure = nMeasure;
+ break;
+ }
+ }
+
+ //TODO: error if not found?
+ }
+ break;
+ case sheet::DataPilotFieldSortMode::MANUAL:
+ case sheet::DataPilotFieldSortMode::NAME:
+ {
+ ScDPMembers* pLocalMembers = GetMembersObject();
+ tools::Long nCount = pLocalMembers->getCount();
+
+ aGlobalOrder.resize( nCount );
+ for (tools::Long nPos=0; nPos<nCount; nPos++)
+ aGlobalOrder[nPos] = nPos;
+
+ // allow manual or name (manual is always ascending)
+ bool bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending );
+ ScDPGlobalMembersOrder aComp( *this, bAscending );
+ ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp );
+ }
+ break;
+ }
+
+ if ( !aAutoShowInfo.IsEnabled )
+ return;
+
+ // find index of measure (index among data dimensions)
+
+ tools::Long nMeasureCount = pSource->GetDataDimensionCount();
+ for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
+ {
+ if (pSource->GetDataDimName(nMeasure) == aAutoShowInfo.DataField)
+ {
+ nAutoMeasure = nMeasure;
+ break;
+ }
+ }
+
+ //TODO: error if not found?
+}
+
+void ScDPLevel::SetEnableLayout(bool bSet)
+{
+ bEnableLayout = bSet;
+}
+
+ScDPMembers* ScDPLevel::GetMembersObject()
+{
+ if (!mxMembers.is())
+ {
+ mxMembers = new ScDPMembers( pSource, nDim, nHier, nLev );
+ }
+ return mxMembers.get();
+}
+
+uno::Reference<sheet::XMembersAccess> SAL_CALL ScDPLevel::getMembers()
+{
+ return GetMembersObject();
+}
+
+uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults()
+{
+ const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this );
+ if (pRes)
+ return *pRes;
+
+ return {}; //TODO: Error?
+}
+
+OUString SAL_CALL ScDPLevel::getName()
+{
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDateDimension( nSrcDim ) )
+ {
+ OUString aRet; //TODO: globstr-ID !!!!
+
+ if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
+ {
+ switch ( nLev )
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ aRet = "Year";
+ break;
+ case SC_DAPI_LEVEL_QUARTER:
+ aRet = "Quarter";
+ break;
+ case SC_DAPI_LEVEL_MONTH:
+ aRet = "Month";
+ break;
+ case SC_DAPI_LEVEL_DAY:
+ aRet = "Day";
+ break;
+ default:
+ OSL_FAIL( "ScDPLevel::getName: unexpected level" );
+ break;
+ }
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
+ {
+ switch ( nLev )
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ aRet = "Year";
+ break;
+ case SC_DAPI_LEVEL_WEEK:
+ aRet = "Week";
+ break;
+ case SC_DAPI_LEVEL_WEEKDAY:
+ aRet = "Weekday";
+ break;
+ default:
+ OSL_FAIL( "ScDPLevel::getName: unexpected level" );
+ break;
+ }
+ }
+ if (!aRet.isEmpty())
+ return aRet;
+ }
+
+ ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
+ if (!pDim)
+ return OUString();
+
+ return pDim->getName();
+}
+
+void SAL_CALL ScDPLevel::setName( const OUString& /* rNewName */ )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+uno::Sequence<sal_Int16> ScDPLevel::getSubTotals() const
+{
+ //TODO: separate functions for settings and evaluation?
+
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( !pSource->SubTotalAllowed( nSrcDim ) )
+ return {};
+
+ return aSubTotals;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aDPLevelMap_Impl[] =
+ {
+ //TODO: change type of AutoShow/Layout/Sorting to API struct when available
+ { SC_UNO_DP_AUTOSHOW, 0, cppu::UnoType<sheet::DataPilotFieldAutoShowInfo>::get(), 0, 0 },
+ { SC_UNO_DP_LAYOUT, 0, cppu::UnoType<sheet::DataPilotFieldLayoutInfo>::get(), 0, 0 },
+ { SC_UNO_DP_SHOWEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_SORTING, 0, cppu::UnoType<sheet::DataPilotFieldSortInfo>::get(), 0, 0 },
+ { SC_UNO_DP_SUBTOTAL, 0, cppu::UnoType<uno::Sequence<sheet::GeneralFunction>>::get(), 0, 0 },
+ { SC_UNO_DP_SUBTOTAL2, 0, cppu::UnoType<uno::Sequence<sal_Int16>>::get(), 0, 0 },
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPLevelMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPLevel::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
+ bShowEmpty = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
+ bRepeatItemLabels = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
+ {
+ uno::Sequence<sheet::GeneralFunction> aSeq;
+ aValue >>= aSeq;
+ aSubTotals.realloc(aSeq.getLength());
+ std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.getArray(),
+ [](const sheet::GeneralFunction& rFunc) -> sal_Int16 {
+ return static_cast<sal_Int16>(rFunc); });
+ }
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
+ aValue >>= aSubTotals;
+ else if ( aPropertyName == SC_UNO_DP_SORTING )
+ aValue >>= aSortInfo;
+ else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
+ aValue >>= aAutoShowInfo;
+ else if ( aPropertyName == SC_UNO_DP_LAYOUT )
+ aValue >>= aLayoutInfo;
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+}
+
+uno::Any SAL_CALL ScDPLevel::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
+ aRet <<= bShowEmpty;
+ else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
+ aRet <<= bRepeatItemLabels;
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
+ {
+ const uno::Sequence<sal_Int16> aSeq = getSubTotals();
+ uno::Sequence<sheet::GeneralFunction> aNewSeq(aSeq.getLength());
+ std::transform(aSeq.begin(), aSeq.end(), aNewSeq.getArray(),
+ [](const sal_Int16 nFunc) -> sheet::GeneralFunction {
+ if (nFunc == sheet::GeneralFunction2::MEDIAN)
+ return sheet::GeneralFunction_NONE;
+ return static_cast<sheet::GeneralFunction>(nFunc);
+ });
+
+ aRet <<= aNewSeq;
+ }
+ else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
+ {
+ uno::Sequence<sal_Int16> aSeq = getSubTotals(); //TODO: avoid extra copy?
+ aRet <<= aSeq;
+ }
+ else if ( aPropertyName == SC_UNO_DP_SORTING )
+ aRet <<= aSortInfo;
+ else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
+ aRet <<= aAutoShowInfo;
+ else if ( aPropertyName == SC_UNO_DP_LAYOUT )
+ aRet <<= aLayoutInfo;
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ {
+ // read only property
+ tools::Long nSrcDim = pSource->GetSourceDim(nDim);
+ ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
+ if (!pDim)
+ return aRet;
+
+ const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
+ if (!pLayoutName)
+ return aRet;
+
+ aRet <<= *pLayoutName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel )
+
+ScDPMembers::ScDPMembers( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH ),
+ nLev( nL )
+{
+ //TODO: hold pSource
+
+ tools::Long nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDataLayoutDimension(nSrcDim) )
+ nMbrCount = pSource->GetDataDimensionCount();
+ else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
+ {
+ nMbrCount = 0;
+ if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
+ {
+ switch (nLev)
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ {
+ const ScDPItemData* pLastNumData = nullptr;
+ for ( SCROW n = 0; n < static_cast<SCROW>(pSource->GetData()->GetColumnEntries(nDim).size()); n-- )
+ {
+ const ScDPItemData* pData = GetSrcItemDataByIndex( n );
+ if ( pData && pData->HasStringData() )
+ break;
+ else
+ pLastNumData = pData;
+ }
+
+ if ( pLastNumData )
+ {
+ const ScDPItemData* pFirstData = GetSrcItemDataByIndex( 0 );
+ double fFirstVal = pFirstData->GetValue();
+ double fLastVal = pLastNumData->GetValue();
+
+ tools::Long nFirstYear = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
+ nHier, nLev );
+ tools::Long nLastYear = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( fLastVal )),
+ nHier, nLev );
+
+ nMbrCount = nLastYear + 1 - nFirstYear;
+ }
+ else
+ nMbrCount = 0; // no values
+ }
+ break;
+ case SC_DAPI_LEVEL_QUARTER: nMbrCount = 4; break;
+ case SC_DAPI_LEVEL_MONTH: nMbrCount = 12; break;
+ case SC_DAPI_LEVEL_DAY: nMbrCount = 31; break;
+ default:
+ OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
+ break;
+ }
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
+ {
+ switch (nLev)
+ {
+ case SC_DAPI_LEVEL_YEAR: nMbrCount = 1; break; //TODO: get years from source
+ case SC_DAPI_LEVEL_WEEK: nMbrCount = 53; break;
+ case SC_DAPI_LEVEL_WEEKDAY: nMbrCount = 7; break;
+ default:
+ OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
+ break;
+ }
+ }
+ }
+ else
+ nMbrCount = pSource->GetData()->GetMembersCount( nSrcDim );
+}
+
+ScDPMembers::~ScDPMembers()
+{
+}
+
+// XNameAccess implementation using getCount/getByIndex
+
+sal_Int32 ScDPMembers::GetIndexFromName( const OUString& rName ) const
+{
+ if ( aHashMap.empty() )
+ {
+ // store the index for each name
+
+ sal_Int32 nCount = getCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ aHashMap[ getByIndex(i)->getName() ] = i;
+ }
+
+ ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName );
+ if ( aIter != aHashMap.end() )
+ return aIter->second; // found index
+ else
+ return -1; // not found
+}
+
+uno::Any SAL_CALL ScDPMembers::getByName( const OUString& aName )
+{
+ sal_Int32 nIndex = GetIndexFromName( aName );
+ if ( nIndex >= 0 )
+ {
+ uno::Reference<container::XNamed> xNamed = getByIndex(nIndex);
+ uno::Any aRet;
+ aRet <<= xNamed;
+ return aRet;
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScDPMembers::getElementNames()
+{
+ return getElementNames( false );
+}
+
+sal_Bool SAL_CALL ScDPMembers::hasByName( const OUString& aName )
+{
+ return ( GetIndexFromName( aName ) >= 0 );
+}
+
+uno::Type SAL_CALL ScDPMembers::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDPMembers::hasElements()
+{
+ return ( getCount() > 0 );
+}
+
+// end of XNameAccess implementation
+
+// XMembersAccess implementation
+
+uno::Sequence<OUString> SAL_CALL ScDPMembers::getLocaleIndependentElementNames()
+{
+ return getElementNames( true );
+}
+
+// end of XMembersAccess implementation
+
+uno::Sequence<OUString> ScDPMembers::getElementNames( bool bLocaleIndependent ) const
+{
+ // Return list of names in sorted order,
+ // so it's displayed in that order in the field options dialog.
+ // Sorting is done at the level object (parent of this).
+
+ ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)->
+ GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev);
+ pLevel->EvaluateSortOrder();
+ const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder();
+ bool bSort = !rGlobalOrder.empty();
+
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->GetNameStr( bLocaleIndependent);
+ return aSeq;
+}
+
+sal_Int32 ScDPMembers::getMinMembers() const
+{
+ // used in lcl_CountMinMembers
+
+ sal_Int32 nVisCount = 0;
+ if (!maMembers.empty())
+ {
+ nVisCount = std::count_if(maMembers.begin(), maMembers.end(), [](const rtl::Reference<ScDPMember>& pMbr) {
+ // count only visible with details (default is true for both)
+ return !pMbr || (pMbr->isVisible() && pMbr->getShowDetails()); });
+ }
+ else
+ nVisCount = nMbrCount; // default for all
+
+ return nVisCount;
+}
+
+ScDPMember* ScDPMembers::getByIndex(sal_Int32 nIndex) const
+{
+ // result of GetColumnEntries must not change between ScDPMembers ctor
+ // and all calls to getByIndex
+
+ if ( nIndex >= 0 && nIndex < nMbrCount )
+ {
+ if (maMembers.empty())
+ maMembers.resize(nMbrCount);
+
+ if (!maMembers[nIndex])
+ {
+ rtl::Reference<ScDPMember> pNew;
+ sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
+ if ( pSource->IsDataLayoutDimension(nSrcDim) )
+ {
+ // empty name (never shown, not used for lookup)
+ pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, 0));
+ }
+ else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
+ {
+ sal_Int32 nGroupBy = 0;
+ sal_Int32 nVal = 0;
+ OUString aName;
+
+ if ( nLev == SC_DAPI_LEVEL_YEAR ) // YEAR is in both hierarchies
+ {
+ //TODO: cache year range here!
+
+ double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
+ tools::Long nFirstYear = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
+ nHier, nLev );
+
+ nVal = nFirstYear + nIndex;
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY )
+ {
+ nVal = nIndex; // DayOfWeek is 0-based
+ aName = ScGlobal::GetCalendar().getDisplayName(
+ css::i18n::CalendarDisplayIndex::DAY,
+ sal::static_int_cast<sal_Int16>(nVal), 0 );
+ }
+ else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH )
+ {
+ nVal = nIndex; // Month is 0-based
+ aName = ScGlobal::GetCalendar().getDisplayName(
+ css::i18n::CalendarDisplayIndex::MONTH,
+ sal::static_int_cast<sal_Int16>(nVal), 0 );
+ }
+ else
+ nVal = nIndex + 1; // Quarter, Day, Week are 1-based
+
+ switch (nLev)
+ {
+ case SC_DAPI_LEVEL_YEAR:
+ nGroupBy = sheet::DataPilotFieldGroupBy::YEARS;
+ break;
+ case SC_DAPI_LEVEL_QUARTER:
+ case SC_DAPI_LEVEL_WEEK:
+ nGroupBy = sheet::DataPilotFieldGroupBy::QUARTERS;
+ break;
+ case SC_DAPI_LEVEL_MONTH:
+ case SC_DAPI_LEVEL_WEEKDAY:
+ nGroupBy = sheet::DataPilotFieldGroupBy::MONTHS;
+ break;
+ case SC_DAPI_LEVEL_DAY:
+ nGroupBy = sheet::DataPilotFieldGroupBy::DAYS;
+ break;
+ default:
+ ;
+ }
+ if (aName.isEmpty())
+ aName = OUString::number(nVal);
+
+ ScDPItemData aData(nGroupBy, nVal);
+ SCROW nId = pSource->GetCache()->GetIdByItemData(nDim, aData);
+ pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, nId));
+ }
+ else
+ {
+ const std::vector<SCROW>& memberIndexs = pSource->GetData()->GetColumnEntries(nSrcDim);
+ pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, memberIndexs[nIndex]));
+ }
+ maMembers[nIndex] = pNew;
+ }
+
+ return maMembers[nIndex].get();
+ }
+
+ return nullptr; //TODO: exception?
+}
+
+ScDPMember::ScDPMember(
+ ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL, SCROW nIndex) :
+ pSource( pSrc ),
+ nDim( nD ),
+ nHier( nH ),
+ nLev( nL ),
+ mnDataId( nIndex ),
+ nPosition( -1 ),
+ bVisible( true ),
+ bShowDet( true )
+{
+ //TODO: hold pSource
+}
+
+ScDPMember::~ScDPMember()
+{
+ //TODO: release pSource
+}
+
+bool ScDPMember::IsNamedItem(SCROW nIndex) const
+{
+ sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
+ if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
+ {
+ const ScDPItemData* pData = pSource->GetCache()->GetItemDataById(nDim, nIndex);
+ if (pData->IsValue())
+ {
+ tools::Long nComp = pSource->GetData()->GetDatePart(
+ static_cast<tools::Long>(::rtl::math::approxFloor( pData->GetValue() )),
+ nHier, nLev );
+ // fValue is converted from integer, so simple comparison works
+ const ScDPItemData* pData2 = GetItemData();
+ return pData2 && nComp == pData2->GetValue();
+ }
+ }
+
+ return nIndex == mnDataId;
+}
+
+sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const
+{
+ if ( nPosition >= 0 )
+ {
+ if ( rOther.nPosition >= 0 )
+ {
+ OSL_ENSURE( nPosition != rOther.nPosition, "same position for two members" );
+ return ( nPosition < rOther.nPosition ) ? -1 : 1;
+ }
+ else
+ {
+ // only this has a position - members with specified positions come before those without
+ return -1;
+ }
+ }
+ else if ( rOther.nPosition >= 0 )
+ {
+ // only rOther has a position
+ return 1;
+ }
+
+ // no positions set - compare names
+ return pSource->GetData()->Compare( pSource->GetSourceDim(nDim),mnDataId,rOther.GetItemDataId());
+}
+
+ScDPItemData ScDPMember::FillItemData() const
+{
+ //TODO: handle date hierarchy...
+
+ const ScDPItemData* pData = GetItemData();
+ return (pData ? *pData : ScDPItemData());
+}
+
+const std::optional<OUString> & ScDPMember::GetLayoutName() const
+{
+ return mpLayoutName;
+}
+
+OUString ScDPMember::GetNameStr( bool bLocaleIndependent ) const
+{
+ const ScDPItemData* pData = GetItemData();
+ if (pData)
+ return pSource->GetData()->GetFormattedString(nDim, *pData, bLocaleIndependent);
+ return OUString();
+}
+
+OUString SAL_CALL ScDPMember::getName()
+{
+ return GetNameStr( false );
+}
+
+void SAL_CALL ScDPMember::setName( const OUString& /* rNewName */ )
+{
+ OSL_FAIL("not implemented"); //TODO: exception?
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo()
+{
+ static const SfxItemPropertyMapEntry aDPMemberMap_Impl[] =
+ {
+ { SC_UNO_DP_ISVISIBLE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_POSITION, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_DP_SHOWDETAILS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( aDPMemberMap_Impl );
+ return aRef;
+}
+
+void SAL_CALL ScDPMember::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
+ bVisible = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
+ bShowDet = lcl_GetBoolFromAny(aValue);
+ else if ( aPropertyName == SC_UNO_DP_POSITION )
+ aValue >>= nPosition;
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ {
+ OUString aName;
+ if (aValue >>= aName)
+ mpLayoutName = aName;
+ }
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+}
+
+uno::Any SAL_CALL ScDPMember::getPropertyValue( const OUString& aPropertyName )
+{
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
+ aRet <<= bVisible;
+ else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
+ aRet <<= bShowDet;
+ else if ( aPropertyName == SC_UNO_DP_POSITION )
+ aRet <<= nPosition;
+ else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
+ aRet <<= mpLayoutName ? *mpLayoutName : OUString();
+ else
+ {
+ OSL_FAIL("unknown property");
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )
+
+const ScDPCache* ScDPSource::GetCache()
+{
+ OSL_ENSURE( GetData() , "empty ScDPTableData pointer");
+ return ( GetData()!=nullptr ) ? &GetData()->GetCacheTable().getCache() : nullptr ;
+}
+
+const ScDPItemData* ScDPMember::GetItemData() const
+{
+ const ScDPItemData* pData = pSource->GetItemDataById(nDim, mnDataId);
+ SAL_WARN_IF( !pData, "sc.core", "ScDPMember::GetItemData: what data? nDim " << nDim << ", mnDataId " << mnDataId);
+ return pData;
+}
+
+const ScDPItemData* ScDPSource::GetItemDataById(sal_Int32 nDim, sal_Int32 nId)
+{
+ return GetData()->GetMemberById(nDim, nId);
+}
+
+const ScDPItemData* ScDPMembers::GetSrcItemDataByIndex(SCROW nIndex)
+{
+ const std::vector< SCROW >& memberIds = pSource->GetData()->GetColumnEntries( nDim );
+ if ( nIndex < 0 || o3tl::make_unsigned(nIndex) >= memberIds.size() )
+ return nullptr;
+ SCROW nId = memberIds[ nIndex ];
+ return pSource->GetItemDataById( nDim, nId );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dputil.cxx b/sc/source/core/data/dputil.cxx
new file mode 100644
index 0000000000..651d550935
--- /dev/null
+++ b/sc/source/core/data/dputil.cxx
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <dputil.hxx>
+#include <dpitemdata.hxx>
+#include <dpnumgroupinfo.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <generalfunction.hxx>
+
+#include <comphelper/string.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/calendarwrapper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+
+const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
+
+OUString getTwoDigitString(sal_Int32 nValue)
+{
+ OUString aRet = OUString::number( nValue );
+ if ( aRet.getLength() < 2 )
+ aRet = "0" + aRet;
+ return aRet;
+}
+
+void appendDateStr(OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter)
+{
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, ScGlobal::eLnge );
+ OUString aString;
+ pFormatter->GetInputLineString(fValue, nFormat, aString);
+ rBuffer.append(aString);
+}
+
+OUString getSpecialDateName(double fValue, bool bFirst, SvNumberFormatter* pFormatter)
+{
+ OUStringBuffer aBuffer;
+ aBuffer.append( bFirst ? '<' : '>' );
+ appendDateStr(aBuffer, fValue, pFormatter);
+ return aBuffer.makeStringAndClear();
+}
+
+}
+
+bool ScDPUtil::isDuplicateDimension(std::u16string_view rName)
+{
+ return o3tl::ends_with(rName, u"*");
+}
+
+OUString ScDPUtil::getSourceDimensionName(std::u16string_view rName)
+{
+ return OUString(comphelper::string::stripEnd(rName, '*'));
+}
+
+sal_uInt8 ScDPUtil::getDuplicateIndex(const OUString& rName)
+{
+ // Count all trailing '*'s.
+
+ sal_Int32 n = rName.getLength();
+ if (!n)
+ return 0;
+
+ sal_uInt8 nDupCount = 0;
+ const sal_Unicode* p = rName.getStr();
+ const sal_Unicode* pStart = p;
+ p += n-1; // Set it to the last char.
+ for (; p != pStart; --p, ++nDupCount)
+ {
+ if (*p != '*')
+ break;
+ }
+
+ return nDupCount;
+}
+
+OUString ScDPUtil::createDuplicateDimensionName(const OUString& rOriginal, size_t nDupCount)
+{
+ if (!nDupCount)
+ return rOriginal;
+
+ OUStringBuffer aBuf(rOriginal);
+ for (size_t i = 0; i < nDupCount; ++i)
+ aBuf.append('*');
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString ScDPUtil::getDateGroupName(
+ sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter,
+ double fStart, double fEnd)
+{
+ if (nValue == ScDPItemData::DateFirst)
+ return getSpecialDateName(fStart, true, pFormatter);
+ if (nValue == ScDPItemData::DateLast)
+ return getSpecialDateName(fEnd, false, pFormatter);
+
+ switch ( nDatePart )
+ {
+ case sheet::DataPilotFieldGroupBy::YEARS:
+ return OUString::number(nValue);
+ case sheet::DataPilotFieldGroupBy::QUARTERS:
+ return ScGlobal::getLocaleData().getQuarterAbbreviation(sal_Int16(nValue-1)); // nValue is 1-based
+ case css::sheet::DataPilotFieldGroupBy::MONTHS:
+ return ScGlobal::GetCalendar().getDisplayName(
+ i18n::CalendarDisplayIndex::MONTH, sal_Int16(nValue-1), 0); // 0-based, get short name
+ case sheet::DataPilotFieldGroupBy::DAYS:
+ {
+ Date aDate(1, 1, SC_DP_LEAPYEAR);
+ aDate.AddDays(nValue - 1); // nValue is 1-based
+ tools::Long nDays = aDate - pFormatter->GetNullDate();
+
+ const sal_uInt32 nFormat = pFormatter->GetFormatIndex(NF_DATE_SYS_DDMMM, ScGlobal::eLnge);
+ const Color* pColor;
+ OUString aStr;
+ pFormatter->GetOutputString(nDays, nFormat, aStr, &pColor);
+ return aStr;
+ }
+ case sheet::DataPilotFieldGroupBy::HOURS:
+ {
+ //TODO: allow am/pm format?
+ return getTwoDigitString(nValue);
+ }
+ break;
+ case sheet::DataPilotFieldGroupBy::MINUTES:
+ case sheet::DataPilotFieldGroupBy::SECONDS:
+ {
+ return ScGlobal::getLocaleData().getTimeSep() + getTwoDigitString(nValue);
+ }
+ break;
+ default:
+ OSL_FAIL("invalid date part");
+ }
+
+ return "FIXME: unhandled value";
+}
+
+double ScDPUtil::getNumGroupStartValue(double fValue, const ScDPNumGroupInfo& rInfo)
+{
+ if (fValue < rInfo.mfStart && !rtl::math::approxEqual(fValue, rInfo.mfStart))
+ return -std::numeric_limits<double>::infinity();
+
+ if (fValue > rInfo.mfEnd && !rtl::math::approxEqual(fValue, rInfo.mfEnd))
+ return std::numeric_limits<double>::infinity();
+
+ double fDiff = fValue - rInfo.mfStart;
+ double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
+ double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
+
+ if (rtl::math::approxEqual(fGroupStart, rInfo.mfEnd) &&
+ !rtl::math::approxEqual(fGroupStart, rInfo.mfStart))
+ {
+ if (!rInfo.mbDateValues)
+ {
+ // A group that would consist only of the end value is not
+ // created, instead the value is included in the last group
+ // before. So the previous group is used if the calculated group
+ // start value is the selected end value.
+
+ fDiv -= 1.0;
+ return rInfo.mfStart + fDiv * rInfo.mfStep;
+ }
+
+ // For date values, the end value is instead treated as above the
+ // limit if it would be a group of its own.
+
+ return rInfo.mfEnd + rInfo.mfStep;
+ }
+
+ return fGroupStart;
+}
+
+namespace {
+
+void lcl_AppendDateStr( OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter )
+{
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, ScGlobal::eLnge );
+ OUString aString;
+ pFormatter->GetInputLineString( fValue, nFormat, aString );
+ rBuffer.append( aString );
+}
+
+OUString lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator,
+ bool bDateValues, SvNumberFormatter* pFormatter )
+{
+ OSL_ENSURE( cDecSeparator != 0, "cDecSeparator not initialized" );
+
+ OUStringBuffer aBuffer;
+ aBuffer.append( bFirst ? '<' : '>' );
+ if ( bDateValues )
+ lcl_AppendDateStr( aBuffer, fValue, pFormatter );
+ else
+ rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, cDecSeparator, true );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString lcl_GetNumGroupName(
+ double fStartValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep,
+ SvNumberFormatter* pFormatter)
+{
+ OSL_ENSURE( cDecSep != 0, "cDecSeparator not initialized" );
+
+ double fStep = rInfo.mfStep;
+ double fEndValue = fStartValue + fStep;
+ if (rInfo.mbIntegerOnly && (rInfo.mbDateValues || !rtl::math::approxEqual(fEndValue, rInfo.mfEnd)))
+ {
+ // The second number of the group label is
+ // (first number + size - 1) if there are only integer numbers,
+ // (first number + size) if any non-integer numbers are involved.
+ // Exception: The last group (containing the end value) is always
+ // shown as including the end value (but not for dates).
+
+ fEndValue -= 1.0;
+ }
+
+ if ( fEndValue > rInfo.mfEnd && !rInfo.mbAutoEnd )
+ {
+ // limit the last group to the end value
+
+ fEndValue = rInfo.mfEnd;
+ }
+
+ OUStringBuffer aBuffer;
+ if ( rInfo.mbDateValues )
+ {
+ lcl_AppendDateStr( aBuffer, fStartValue, pFormatter );
+ aBuffer.append( " - " ); // with spaces
+ lcl_AppendDateStr( aBuffer, fEndValue, pFormatter );
+ }
+ else
+ {
+ rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, cDecSep, true );
+ aBuffer.append( '-' );
+ rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, cDecSep, true );
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+}
+
+OUString ScDPUtil::getNumGroupName(
+ double fValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep, SvNumberFormatter* pFormatter)
+{
+ if ( fValue < rInfo.mfStart && !rtl::math::approxEqual( fValue, rInfo.mfStart ) )
+ return lcl_GetSpecialNumGroupName( rInfo.mfStart, true, cDecSep, rInfo.mbDateValues, pFormatter );
+
+ if ( fValue > rInfo.mfEnd && !rtl::math::approxEqual( fValue, rInfo.mfEnd ) )
+ return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
+
+ double fDiff = fValue - rInfo.mfStart;
+ double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
+ double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
+
+ if ( rtl::math::approxEqual( fGroupStart, rInfo.mfEnd ) &&
+ !rtl::math::approxEqual( fGroupStart, rInfo.mfStart ) )
+ {
+ if (rInfo.mbDateValues)
+ {
+ // For date values, the end value is instead treated as above the limit
+ // if it would be a group of its own.
+ return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
+ }
+ }
+
+ return lcl_GetNumGroupName(fGroupStart, rInfo, cDecSep, pFormatter);
+}
+
+sal_Int32 ScDPUtil::getDatePartValue(
+ double fValue, const ScDPNumGroupInfo* pInfo, sal_Int32 nDatePart,
+ const SvNumberFormatter* pFormatter)
+{
+ // Start and end are inclusive
+ // (End date without a time value is included, with a time value it's not)
+
+ if (pInfo)
+ {
+ if (fValue < pInfo->mfStart && !rtl::math::approxEqual(fValue, pInfo->mfStart))
+ return ScDPItemData::DateFirst;
+ if (fValue > pInfo->mfEnd && !rtl::math::approxEqual(fValue, pInfo->mfEnd))
+ return ScDPItemData::DateLast;
+ }
+
+ sal_Int32 nResult = 0;
+
+ if (nDatePart == sheet::DataPilotFieldGroupBy::HOURS ||
+ nDatePart == sheet::DataPilotFieldGroupBy::MINUTES ||
+ nDatePart == sheet::DataPilotFieldGroupBy::SECONDS)
+ {
+ // handle time
+ // (do as in the cell functions, ScInterpreter::ScGetHour() etc.)
+
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( fValue, nHour, nMinute, nSecond, fFractionOfSecond, 0);
+
+ switch (nDatePart)
+ {
+ case sheet::DataPilotFieldGroupBy::HOURS:
+ nResult = nHour;
+ break;
+ case sheet::DataPilotFieldGroupBy::MINUTES:
+ nResult = nMinute;
+ break;
+ case sheet::DataPilotFieldGroupBy::SECONDS:
+ nResult = nSecond;
+ break;
+ }
+ }
+ else
+ {
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays(::rtl::math::approxFloor(fValue));
+
+ switch ( nDatePart )
+ {
+ case css::sheet::DataPilotFieldGroupBy::YEARS:
+ nResult = aDate.GetYear();
+ break;
+ case css::sheet::DataPilotFieldGroupBy::QUARTERS:
+ nResult = 1 + (aDate.GetMonth() - 1) / 3; // 1..4
+ break;
+ case css::sheet::DataPilotFieldGroupBy::MONTHS:
+ nResult = aDate.GetMonth(); // 1..12
+ break;
+ case css::sheet::DataPilotFieldGroupBy::DAYS:
+ {
+ Date aYearStart(1, 1, aDate.GetYear());
+ nResult = (aDate - aYearStart) + 1; // Jan 01 has value 1
+ if (nResult >= 60 && !aDate.IsLeapYear())
+ {
+ // days are counted from 1 to 366 - if not from a leap year, adjust
+ ++nResult;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL("invalid date part");
+ }
+ }
+
+ return nResult;
+}
+
+namespace {
+
+const TranslateId aFuncStrIds[] = {
+ {}, // SUBTOTAL_FUNC_NONE
+ STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE
+ STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT
+ STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2
+ STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX
+ STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN
+ STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD
+ STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD
+ STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP
+ STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM
+ STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR
+ STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VARP
+ STR_FUN_TEXT_MEDIAN, // SUBTOTAL_FUNC_MED
+ {} // SUBTOTAL_FUNC_SELECTION_COUNT - not used for pivot table
+};
+
+}
+
+OUString ScDPUtil::getDisplayedMeasureName(const OUString& rName, ScSubTotalFunc eFunc)
+{
+ assert(unsigned(eFunc) < SAL_N_ELEMENTS(aFuncStrIds));
+ TranslateId pId = aFuncStrIds[eFunc];
+ if (!pId)
+ return rName;
+ return ScResId(pId) + // function name
+ " - " +
+ rName; // field name
+}
+
+ScSubTotalFunc ScDPUtil::toSubTotalFunc(ScGeneralFunction eGenFunc)
+{
+ ScSubTotalFunc eSubTotal = SUBTOTAL_FUNC_NONE;
+ switch (eGenFunc)
+ {
+ case ScGeneralFunction::NONE: eSubTotal = SUBTOTAL_FUNC_NONE; break;
+ case ScGeneralFunction::SUM: eSubTotal = SUBTOTAL_FUNC_SUM; break;
+ case ScGeneralFunction::COUNT: eSubTotal = SUBTOTAL_FUNC_CNT2; break;
+ case ScGeneralFunction::AVERAGE: eSubTotal = SUBTOTAL_FUNC_AVE; break;
+ case ScGeneralFunction::MEDIAN: eSubTotal = SUBTOTAL_FUNC_MED; break;
+ case ScGeneralFunction::MAX: eSubTotal = SUBTOTAL_FUNC_MAX; break;
+ case ScGeneralFunction::MIN: eSubTotal = SUBTOTAL_FUNC_MIN; break;
+ case ScGeneralFunction::PRODUCT: eSubTotal = SUBTOTAL_FUNC_PROD; break;
+ case ScGeneralFunction::COUNTNUMS: eSubTotal = SUBTOTAL_FUNC_CNT; break;
+ case ScGeneralFunction::STDEV: eSubTotal = SUBTOTAL_FUNC_STD; break;
+ case ScGeneralFunction::STDEVP: eSubTotal = SUBTOTAL_FUNC_STDP; break;
+ case ScGeneralFunction::VAR: eSubTotal = SUBTOTAL_FUNC_VAR; break;
+ case ScGeneralFunction::VARP: eSubTotal = SUBTOTAL_FUNC_VARP; break;
+ case ScGeneralFunction::AUTO: eSubTotal = SUBTOTAL_FUNC_NONE; break;
+ default:
+ assert(false);
+ }
+ return eSubTotal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/drawpage.cxx b/sc/source/core/data/drawpage.cxx
new file mode 100644
index 0000000000..9baa9be3ca
--- /dev/null
+++ b/sc/source/core/data/drawpage.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawpage.hxx>
+#include <drwlayer.hxx>
+#include <pageuno.hxx>
+
+ScDrawPage::ScDrawPage(ScDrawLayer& rNewModel, bool bMasterPage)
+: FmFormPage(rNewModel, bMasterPage)
+{
+ SetSize( Size( SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ // largest size supported by sal_Int32 SdrPage::mnWidth/Height
+}
+
+ScDrawPage::~ScDrawPage()
+{
+}
+
+rtl::Reference<SdrPage> ScDrawPage::CloneSdrPage(SdrModel& rTargetModel) const
+{
+ ScDrawLayer& rScDrawLayer(static_cast< ScDrawLayer& >(rTargetModel));
+ rtl::Reference<ScDrawPage> pClonedScDrawPage(
+ new ScDrawPage(
+ rScDrawLayer,
+ IsMasterPage()));
+ pClonedScDrawPage->FmFormPage::lateInit(*this);
+ return pClonedScDrawPage;
+}
+
+css::uno::Reference< css::uno::XInterface > ScDrawPage::createUnoPage()
+{
+ return cppu::getXWeak( new ScPageObj( this ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx
new file mode 100644
index 0000000000..3f98fc770a
--- /dev/null
+++ b/sc/source/core/data/drwlayer.cxx
@@ -0,0 +1,2946 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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.hxx>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/embed/XClassifiedObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sot/exchange.hxx>
+#include <svx/objfac3d.hxx>
+#include <svx/xtable.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/svxids.hrc>
+#include <svx/sxcecitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <editeng/unolingu.hxx>
+#include <svx/drawitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/itempool.hxx>
+#include <utility>
+#include <vcl/canvastools.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <tools/globname.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#include <drwlayer.hxx>
+#include <drawpage.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <userdat.hxx>
+#include <markdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <postit.hxx>
+#include <attrib.hxx>
+#include <charthelper.hxx>
+#include <table.hxx>
+#include <stlpool.hxx>
+#include <docpool.hxx>
+#include <detfunc.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <clipparam.hxx>
+
+#include <memory>
+#include <algorithm>
+#include <cstdlib>
+
+namespace com::sun::star::embed { class XEmbeddedObject; }
+
+#define DET_ARROW_OFFSET 1000
+
+using namespace ::com::sun::star;
+
+static E3dObjFactory* pF3d = nullptr;
+static sal_uInt16 nInst = 0;
+
+SfxObjectShell* ScDrawLayer::pGlobalDrawPersist = nullptr;
+
+bool bDrawIsInUndo = false; //TODO: Member
+
+ScUndoObjData::ScUndoObjData( SdrObject* pObjP, const ScAddress& rOS, const ScAddress& rOE,
+ const ScAddress& rNS, const ScAddress& rNE ) :
+ SdrUndoObj( *pObjP ),
+ aOldStt( rOS ),
+ aOldEnd( rOE ),
+ aNewStt( rNS ),
+ aNewEnd( rNE )
+{
+}
+
+ScUndoObjData::~ScUndoObjData()
+{
+}
+
+void ScUndoObjData::Undo()
+{
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( mxObj.get() );
+ OSL_ENSURE(pData,"ScUndoObjData: Data missing");
+ if (pData)
+ {
+ pData->maStart = aOldStt;
+ pData->maEnd = aOldEnd;
+ }
+
+ // Undo also an untransformed anchor
+ pData = ScDrawLayer::GetNonRotatedObjData( mxObj.get() );
+ if (pData)
+ {
+ pData->maStart = aOldStt;
+ pData->maEnd = aOldEnd;
+ }
+}
+
+void ScUndoObjData::Redo()
+{
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( mxObj.get() );
+ OSL_ENSURE(pData,"ScUndoObjData: Data missing");
+ if (pData)
+ {
+ pData->maStart = aNewStt;
+ pData->maEnd = aNewEnd;
+ }
+
+ // Redo also an untransformed anchor
+ pData = ScDrawLayer::GetNonRotatedObjData( mxObj.get() );
+ if (pData)
+ {
+ pData->maStart = aNewStt;
+ pData->maEnd = aNewEnd;
+ }
+}
+
+ScUndoAnchorData::ScUndoAnchorData( SdrObject* pObjP, ScDocument* pDoc, SCTAB nTab ) :
+ SdrUndoObj( *pObjP ),
+ mpDoc( pDoc ),
+ mnTab( nTab )
+{
+ mbWasCellAnchored = ScDrawLayer::IsCellAnchored( *pObjP );
+ mbWasResizeWithCell = ScDrawLayer::IsResizeWithCell( *pObjP );
+}
+
+ScUndoAnchorData::~ScUndoAnchorData()
+{
+}
+
+void ScUndoAnchorData::Undo()
+{
+ // Trigger Object Change
+ if (mxObj->IsInserted() && mxObj->getSdrPageFromSdrObject())
+ {
+ SdrHint aHint(SdrHintKind::ObjectChange, *mxObj);
+ mxObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ if (mbWasCellAnchored)
+ ScDrawLayer::SetCellAnchoredFromPosition(*mxObj, *mpDoc, mnTab, mbWasResizeWithCell);
+ else
+ ScDrawLayer::SetPageAnchored( *mxObj );
+}
+
+void ScUndoAnchorData::Redo()
+{
+ if (mbWasCellAnchored)
+ ScDrawLayer::SetPageAnchored( *mxObj );
+ else
+ ScDrawLayer::SetCellAnchoredFromPosition(*mxObj, *mpDoc, mnTab, mbWasResizeWithCell);
+
+ // Trigger Object Change
+ if (mxObj->IsInserted() && mxObj->getSdrPageFromSdrObject())
+ {
+ SdrHint aHint(SdrHintKind::ObjectChange, *mxObj);
+ mxObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+}
+
+ScTabDeletedHint::ScTabDeletedHint( SCTAB nTabNo ) :
+ nTab( nTabNo )
+{
+}
+
+ScTabDeletedHint::~ScTabDeletedHint()
+{
+}
+
+ScTabSizeChangedHint::ScTabSizeChangedHint( SCTAB nTabNo ) :
+ nTab( nTabNo )
+{
+}
+
+ScTabSizeChangedHint::~ScTabSizeChangedHint()
+{
+}
+
+#define MAXMM 10000000
+
+
+static void lcl_ReverseTwipsToMM( tools::Rectangle& rRect )
+{
+ rRect = o3tl::convert(rRect, o3tl::Length::mm100, o3tl::Length::twip);
+}
+
+static ScRange lcl_getClipRangeFromClipDoc(ScDocument* pClipDoc, SCTAB nClipTab)
+{
+ if (!pClipDoc)
+ return ScRange();
+
+ SCCOL nClipStartX;
+ SCROW nClipStartY;
+ SCCOL nClipEndX;
+ SCROW nClipEndY;
+ pClipDoc->GetClipStart(nClipStartX, nClipStartY);
+ pClipDoc->GetClipArea(nClipEndX, nClipEndY, true);
+ nClipEndX = nClipEndX + nClipStartX;
+ nClipEndY += nClipStartY; // GetClipArea returns the difference
+
+ return ScRange(nClipStartX, nClipStartY, nClipTab, nClipEndX, nClipEndY, nClipTab);
+}
+
+ScDrawLayer::ScDrawLayer( ScDocument* pDocument, OUString _aName ) :
+ FmFormModel(
+ nullptr,
+ pGlobalDrawPersist ? pGlobalDrawPersist : (pDocument ? pDocument->GetDocumentShell() : nullptr)),
+ aName(std::move( _aName )),
+ pDoc( pDocument ),
+ bRecording( false ),
+ bAdjustEnabled( true ),
+ bHyphenatorSet( false )
+{
+ SetVOCInvalidationIsReliable(true);
+ m_bThemedControls = false;
+
+ pGlobalDrawPersist = nullptr; // Only use once
+
+ ScDocShell* pObjSh = pDocument ? pDocument->GetDocumentShell() : nullptr;
+ XColorListRef pXCol = XColorList::GetStdColorList();
+ if ( pObjSh )
+ {
+ SetObjectShell( pObjSh );
+
+ // set color table
+ const SvxColorListItem* pColItem = pObjSh->GetItem( SID_COLOR_TABLE );
+ if ( pColItem )
+ pXCol = pColItem->GetColorList();
+ }
+ SetPropertyList( static_cast<XPropertyList *> (pXCol.get()) );
+
+ SetSwapGraphics();
+
+ SetScaleUnit(MapUnit::Map100thMM);
+ SfxItemPool& rPool = GetItemPool();
+ rPool.SetDefaultMetric(MapUnit::Map100thMM);
+ SvxFrameDirectionItem aModeItem( SvxFrameDirection::Environment, EE_PARA_WRITINGDIR );
+ rPool.SetPoolDefaultItem( aModeItem );
+
+ // #i33700#
+ // Set shadow distance defaults as PoolDefaultItems. Details see bug.
+ rPool.SetPoolDefaultItem(makeSdrShadowXDistItem(300));
+ rPool.SetPoolDefaultItem(makeSdrShadowYDistItem(300));
+
+ // default for script spacing depends on locale, see SdDrawDocument ctor in sd
+ LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+ if (MsLangId::isKorean(eOfficeLanguage) || eOfficeLanguage == LANGUAGE_JAPANESE)
+ {
+ // secondary is edit engine pool
+ rPool.GetSecondaryPool()->SetPoolDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) );
+ }
+
+ rPool.FreezeIdRanges(); // the pool is also used directly
+
+ SetStyleSheetPool(pDocument ? pDocument->GetStyleSheetPool() : new ScStyleSheetPool(rPool, pDocument));
+
+ SdrLayerAdmin& rAdmin = GetLayerAdmin();
+ rAdmin.NewLayer("vorne", SC_LAYER_FRONT.get());
+ rAdmin.NewLayer("hinten", SC_LAYER_BACK.get());
+ rAdmin.NewLayer("intern", SC_LAYER_INTERN.get());
+ // tdf#140252 use same name as in ctor of SdrLayerAdmin
+ rAdmin.NewLayer(rAdmin.GetControlLayerName(), SC_LAYER_CONTROLS.get());
+ rAdmin.NewLayer("hidden", SC_LAYER_HIDDEN.get());
+
+ // Set link for URL-Fields
+ ScModule* pScMod = SC_MOD();
+ Outliner& rOutliner = GetDrawOutliner();
+ rOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) );
+ rOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool()));
+
+ Outliner& rHitOutliner = GetHitTestOutliner();
+ rHitOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) );
+ rHitOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool()));
+
+ // set FontHeight pool defaults without changing static SdrEngineDefaults
+ SfxItemPool* pOutlinerPool = rOutliner.GetEditTextObjectPool();
+ if ( pOutlinerPool )
+ {
+ m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt
+ m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK )); // 12Pt
+ m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL )); // 12Pt
+ }
+ SfxItemPool* pHitOutlinerPool = rHitOutliner.GetEditTextObjectPool();
+ if ( pHitOutlinerPool )
+ {
+ pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt
+ pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK )); // 12Pt
+ pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL )); // 12Pt
+ }
+
+ // initial undo mode as in Calc document
+ if( pDoc )
+ EnableUndo( pDoc->IsUndoEnabled() );
+
+ // URL-Buttons have no handler anymore, all is done by themselves
+
+ if( !nInst++ )
+ {
+ pF3d = new E3dObjFactory;
+ }
+}
+
+ScDrawLayer::~ScDrawLayer()
+{
+ Broadcast(SdrHint(SdrHintKind::ModelCleared));
+
+ ClearModel(true);
+
+ pUndoGroup.reset();
+ if( !--nInst )
+ {
+ delete pF3d;
+ pF3d = nullptr;
+ }
+}
+
+void ScDrawLayer::CreateDefaultStyles()
+{
+ // Default
+ auto pSheet = &GetStyleSheetPool()->Make(ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Frame, SfxStyleSearchBits::ScStandard);
+ SetDefaultStyleSheet(static_cast<SfxStyleSheet*>(pSheet));
+
+ // Note
+ pSheet = &GetStyleSheetPool()->Make(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame, SfxStyleSearchBits::ScStandard);
+
+ // caption tail arrow
+ ::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);
+
+ auto pSet = &pSheet->GetItemSet();
+ pSet->Put(XLineStartItem(OUString(), ::basegfx::B2DPolyPolygon(aTriangle)).checkForUniqueItem(this));
+ pSet->Put(XLineStartWidthItem(200));
+ pSet->Put(XLineStartCenterItem(false));
+ pSet->Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ pSet->Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ pSet->Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor()));
+ pSet->Put(SdrCaptionEscDirItem(SdrCaptionEscDir::BestFit));
+
+ // shadow
+ pSet->Put(makeSdrShadowItem(true));
+ pSet->Put(makeSdrShadowXDistItem(100));
+ pSet->Put(makeSdrShadowYDistItem(100));
+
+ // text attributes
+ pSet->Put(makeSdrTextLeftDistItem(100));
+ pSet->Put(makeSdrTextRightDistItem(100));
+ pSet->Put(makeSdrTextUpperDistItem(100));
+ pSet->Put(makeSdrTextLowerDistItem(100));
+ pSet->Put(makeSdrTextAutoGrowWidthItem(false));
+ pSet->Put(makeSdrTextAutoGrowHeightItem(true));
+
+ // text formatting
+ SfxItemSet aEditSet(GetItemPool());
+ ScPatternAttr::FillToEditItemSet(aEditSet, pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN).GetItemSet());
+
+ pSet->Put(aEditSet.Get(EE_CHAR_FONTINFO));
+ pSet->Put(aEditSet.Get(EE_CHAR_FONTINFO_CJK));
+ pSet->Put(aEditSet.Get(EE_CHAR_FONTINFO_CTL));
+
+ pSet->Put(aEditSet.Get(EE_CHAR_FONTHEIGHT));
+ pSet->Put(aEditSet.Get(EE_CHAR_FONTHEIGHT_CJK));
+ pSet->Put(aEditSet.Get(EE_CHAR_FONTHEIGHT_CTL));
+}
+
+void ScDrawLayer::UseHyphenator()
+{
+ if (!bHyphenatorSet)
+ {
+ css::uno::Reference< css::linguistic2::XHyphenator >
+ xHyphenator = LinguMgr::GetHyphenator();
+
+ GetDrawOutliner().SetHyphenator( xHyphenator );
+ GetHitTestOutliner().SetHyphenator( xHyphenator );
+
+ bHyphenatorSet = true;
+ }
+}
+
+rtl::Reference<SdrPage> ScDrawLayer::AllocPage(bool bMasterPage)
+{
+ return new ScDrawPage(*this, bMasterPage);
+}
+
+bool ScDrawLayer::HasObjects() const
+{
+ bool bFound = false;
+
+ sal_uInt16 nCount = GetPageCount();
+ for (sal_uInt16 i=0; i<nCount && !bFound; i++)
+ if (GetPage(i)->GetObjCount())
+ bFound = true;
+
+ return bFound;
+}
+
+SdrModel* ScDrawLayer::AllocModel() const
+{
+ // Allocated model (for clipboard etc) must not have a pointer
+ // to the original model's document, pass NULL as document:
+ auto pNewModel = std::make_unique<ScDrawLayer>(nullptr, aName);
+ auto pNewPool = static_cast<ScStyleSheetPool*>(pNewModel->GetStyleSheetPool());
+ pNewPool->CopyUsedGraphicStylesFrom(GetStyleSheetPool());
+
+ return pNewModel.release();
+}
+
+bool ScDrawLayer::ScAddPage( SCTAB nTab )
+{
+ if (bDrawIsInUndo)
+ return false; // not inserted
+
+ rtl::Reference<ScDrawPage> pPage = static_cast<ScDrawPage*>(AllocPage( false ).get());
+ InsertPage(pPage.get(), static_cast<sal_uInt16>(nTab));
+ if (bRecording)
+ AddCalcUndo(std::make_unique<SdrUndoNewPage>(*pPage));
+
+ ResetTab(nTab, pDoc->GetTableCount()-1);
+ return true; // inserted
+}
+
+void ScDrawLayer::ScRemovePage( SCTAB nTab )
+{
+ if (bDrawIsInUndo)
+ return;
+
+ Broadcast( ScTabDeletedHint( nTab ) );
+ if (bRecording)
+ {
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ AddCalcUndo(std::make_unique<SdrUndoDelPage>(*pPage)); // Undo-Action becomes the page owner
+ RemovePage( static_cast<sal_uInt16>(nTab) ); // just deliver, not deleting
+ }
+ else
+ DeletePage( static_cast<sal_uInt16>(nTab) ); // just get rid of it
+
+ ResetTab(nTab, pDoc->GetTableCount()-1);
+}
+
+void ScDrawLayer::ScRenamePage( SCTAB nTab, const OUString& rNewName )
+{
+ ScDrawPage* pPage = static_cast<ScDrawPage*>( GetPage(static_cast<sal_uInt16>(nTab)) );
+ if (pPage)
+ pPage->SetName(rNewName);
+}
+
+void ScDrawLayer::ScMovePage( sal_uInt16 nOldPos, sal_uInt16 nNewPos )
+{
+ MovePage( nOldPos, nNewPos );
+ sal_uInt16 nMinPos = std::min(nOldPos, nNewPos);
+ ResetTab(nMinPos, pDoc->GetTableCount()-1);
+}
+
+void ScDrawLayer::ScCopyPage( sal_uInt16 nOldPos, sal_uInt16 nNewPos )
+{
+ if (bDrawIsInUndo)
+ return;
+
+ SdrPage* pOldPage = GetPage(nOldPos);
+ SdrPage* pNewPage = GetPage(nNewPos);
+
+ // Copying
+
+ if (pOldPage && pNewPage)
+ {
+ SCTAB nOldTab = static_cast<SCTAB>(nOldPos);
+ SCTAB nNewTab = static_cast<SCTAB>(nNewPos);
+
+ SdrObjListIter aIter( pOldPage, SdrIterMode::Flat );
+ SdrObject* pOldObject = aIter.Next();
+ while (pOldObject)
+ {
+ ScDrawObjData* pOldData = GetObjData(pOldObject);
+ if (pOldData)
+ {
+ pOldData->maStart.SetTab(nOldTab);
+ pOldData->maEnd.SetTab(nOldTab);
+ }
+
+ // Clone to target SdrModel
+ rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this));
+ pNewObject->NbcMove(Size(0,0));
+ pNewPage->InsertObject( pNewObject.get() );
+ ScDrawObjData* pNewData = GetObjData(pNewObject.get());
+ if (pNewData)
+ {
+ pNewData->maStart.SetTab(nNewTab);
+ pNewData->maEnd.SetTab(nNewTab);
+ }
+
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) );
+
+ pOldObject = aIter.Next();
+ }
+ }
+
+ ResetTab(static_cast<SCTAB>(nNewPos), pDoc->GetTableCount()-1);
+}
+
+void ScDrawLayer::ResetTab( SCTAB nStart, SCTAB nEnd )
+{
+ SCTAB nPageSize = static_cast<SCTAB>(GetPageCount());
+ if (nPageSize < 0)
+ // No drawing pages exist.
+ return;
+
+ if (nEnd >= nPageSize)
+ // Avoid iterating beyond the last existing page.
+ nEnd = nPageSize - 1;
+
+ for (SCTAB i = nStart; i <= nEnd; ++i)
+ {
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(i));
+ if (!pPage)
+ continue;
+
+ SdrObjListIter aIter(pPage, SdrIterMode::Flat);
+ for (SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next())
+ {
+ ScDrawObjData* pData = GetObjData(pObj);
+ if (!pData)
+ continue;
+
+ pData->maStart.SetTab(i);
+ pData->maEnd.SetTab(i);
+ }
+ }
+}
+
+static bool IsInBlock( const ScAddress& rPos, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2 )
+{
+ return rPos.Col() >= nCol1 && rPos.Col() <= nCol2 &&
+ rPos.Row() >= nRow1 && rPos.Row() <= nRow2;
+}
+
+void ScDrawLayer::MoveCells( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2,
+ SCCOL nDx,SCROW nDy, bool bUpdateNoteCaptionPos )
+{
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page not found");
+ if (!pPage)
+ return;
+
+ bool bNegativePage = pDoc && pDoc->IsNegativePage( nTab );
+
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ {
+ ScDrawObjData* pData = GetObjDataTab( pObj.get(), nTab );
+ if( pData )
+ {
+ const ScAddress aOldStt = pData->maStart;
+ const ScAddress aOldEnd = pData->maEnd;
+ bool bChange = false;
+ if ( aOldStt.IsValid() && IsInBlock( aOldStt, nCol1,nRow1, nCol2,nRow2 ) )
+ {
+ pData->maStart.IncCol( nDx );
+ pData->maStart.IncRow( nDy );
+ bChange = true;
+ }
+ if ( aOldEnd.IsValid() && IsInBlock( aOldEnd, nCol1,nRow1, nCol2,nRow2 ) )
+ {
+ pData->maEnd.IncCol( nDx );
+ pData->maEnd.IncRow( nDy );
+ bChange = true;
+ }
+ if (bChange)
+ {
+ if ( dynamic_cast<const SdrRectObj*>( pObj.get()) != nullptr && pData->maStart.IsValid() && pData->maEnd.IsValid() )
+ pData->maStart.PutInOrder( pData->maEnd );
+
+ // Update also an untransformed anchor that's what we stored ( and still do ) to xml
+ ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData( pObj.get() );
+ if ( pNoRotatedAnchor )
+ {
+ const ScAddress aOldSttNoRotatedAnchor = pNoRotatedAnchor->maStart;
+ const ScAddress aOldEndNoRotatedAnchor = pNoRotatedAnchor->maEnd;
+ if ( aOldSttNoRotatedAnchor.IsValid() && IsInBlock( aOldSttNoRotatedAnchor, nCol1,nRow1, nCol2,nRow2 ) )
+ {
+ pNoRotatedAnchor->maStart.IncCol(nDx);
+ pNoRotatedAnchor->maStart.IncRow(nDy);
+ }
+ if ( aOldEndNoRotatedAnchor.IsValid() && IsInBlock( aOldEndNoRotatedAnchor, nCol1,nRow1, nCol2,nRow2 ) )
+ {
+ pNoRotatedAnchor->maEnd.IncCol(nDx);
+ pNoRotatedAnchor->maEnd.IncRow(nDy);
+ }
+ }
+
+ AddCalcUndo( std::make_unique<ScUndoObjData>( pObj.get(), aOldStt, aOldEnd, pData->maStart, pData->maEnd ) );
+ RecalcPos( pObj.get(), *pData, bNegativePage, bUpdateNoteCaptionPos );
+ }
+ }
+ }
+}
+
+void ScDrawLayer::SetPageSize(sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos,
+ const ScObjectHandling eObjectHandling)
+{
+ SdrPage* pPage = GetPage(nPageNo);
+ if (!pPage)
+ return;
+
+ if ( rSize != pPage->GetSize() )
+ {
+ pPage->SetSize( rSize );
+ Broadcast( ScTabSizeChangedHint( static_cast<SCTAB>(nPageNo) ) ); // SetWorkArea() on the views
+ }
+
+ // Do not call RecalcPos while loading, because row height is not finished, when SetPageSize
+ // is called first time. Instead the objects are initialized from ScXMLImport::endDocument() and
+ // RecalcPos is called from there.
+ if (!pDoc || pDoc->IsImportingXML())
+ return;
+
+ // Implement Detective lines (adjust to new heights / widths)
+ // even if size is still the same
+ // (individual rows/columns can have been changed))
+
+ bool bNegativePage = pDoc && pDoc->IsNegativePage( static_cast<SCTAB>(nPageNo) );
+
+ // Disable mass broadcasts from drawing objects' position changes.
+ bool bWasLocked = isLocked();
+ setLock(true);
+
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ {
+ ScDrawObjData* pData = GetObjDataTab( pObj.get(), static_cast<SCTAB>(nPageNo) );
+ if( pData ) // cell anchored
+ {
+ if (pData->meType == ScDrawObjData::DrawingObject
+ || pData->meType == ScDrawObjData::ValidationCircle)
+ {
+ switch (eObjectHandling)
+ {
+ case ScObjectHandling::RecalcPosMode:
+ RecalcPos(pObj.get(), *pData, bNegativePage, bUpdateNoteCaptionPos);
+ break;
+ case ScObjectHandling::MoveRTLMode:
+ MoveRTL(pObj.get());
+ break;
+ case ScObjectHandling::MirrorRTLMode:
+ MirrorRTL(pObj.get());
+ break;
+ }
+ }
+ else // DetectiveArrow and CellNote
+ RecalcPos(pObj.get(), *pData, bNegativePage, bUpdateNoteCaptionPos);
+ }
+ else // page anchored
+ {
+ switch (eObjectHandling)
+ {
+ case ScObjectHandling::MoveRTLMode:
+ MoveRTL(pObj.get());
+ break;
+ case ScObjectHandling::MirrorRTLMode:
+ MirrorRTL(pObj.get());
+ break;
+ case ScObjectHandling::RecalcPosMode: // does not occur for page anchored shapes
+ break;
+ }
+ }
+ }
+
+ setLock(bWasLocked);
+}
+
+namespace
+{
+ //Can't have a zero width dimension
+ tools::Rectangle lcl_makeSafeRectangle(const tools::Rectangle &rNew)
+ {
+ tools::Rectangle aRect = rNew;
+ if (aRect.Bottom() == aRect.Top())
+ aRect.SetBottom( aRect.Top()+1 );
+ if (aRect.Right() == aRect.Left())
+ aRect.SetRight( aRect.Left()+1 );
+ return aRect;
+ }
+
+ Point lcl_calcAvailableDiff(const ScDocument &rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const Point &aWantedDiff)
+ {
+ Point aAvailableDiff(aWantedDiff);
+ tools::Long nHeight = o3tl::convert(rDoc.GetRowHeight( nRow, nTab ), o3tl::Length::twip, o3tl::Length::mm100);
+ tools::Long nWidth = o3tl::convert(rDoc.GetColWidth( nCol, nTab ), o3tl::Length::twip, o3tl::Length::mm100);
+ if (aAvailableDiff.Y() > nHeight)
+ aAvailableDiff.setY( nHeight );
+ if (aAvailableDiff.X() > nWidth)
+ aAvailableDiff.setX( nWidth );
+ return aAvailableDiff;
+ }
+
+ tools::Rectangle lcl_UpdateCalcPoly(basegfx::B2DPolygon &rCalcPoly, int nWhichPoint, const Point &rPos)
+ {
+ rCalcPoly.setB2DPoint(nWhichPoint, basegfx::B2DPoint(rPos.X(), rPos.Y()));
+ basegfx::B2DRange aRange(basegfx::utils::getRange(rCalcPoly));
+ return tools::Rectangle(static_cast<tools::Long>(aRange.getMinX()), static_cast<tools::Long>(aRange.getMinY()),
+ static_cast<tools::Long>(aRange.getMaxX()), static_cast<tools::Long>(aRange.getMaxY()));
+ }
+
+bool lcl_AreRectanglesApproxEqual(const tools::Rectangle& rRectA, const tools::Rectangle& rRectB)
+{
+ // Twips <-> Hmm conversions introduce +-1 differences although there are no real changes in the object.
+ // Therefore test with == is not appropriate in some cases.
+ if (std::abs(rRectA.Left() - rRectB.Left()) > 1)
+ return false;
+ if (std::abs(rRectA.Top() - rRectB.Top()) > 1)
+ return false;
+ if (std::abs(rRectA.Right() - rRectB.Right()) > 1)
+ return false;
+ if (std::abs(rRectA.Bottom() - rRectB.Bottom()) > 1)
+ return false;
+ return true;
+}
+
+bool lcl_NeedsMirrorYCorrection(const SdrObject* pObj)
+{
+ return pObj->GetObjIdentifier() == SdrObjKind::CustomShape
+ && static_cast<const SdrObjCustomShape*>(pObj)->IsMirroredY();
+}
+
+void lcl_SetLogicRectFromAnchor(SdrObject* pObj, const ScDrawObjData& rAnchor, const ScDocument* pDoc)
+{
+ // This is only used during initialization. At that time, shape handling is always LTR. No need
+ // to consider negative page.
+ if (!pObj || !pDoc || !rAnchor.maEnd.IsValid() || !rAnchor.maStart.IsValid())
+ return;
+
+ // In case of a vertical mirrored custom shape, LibreOffice uses internally an additional 180deg
+ // in aGeo.nRotationAngle and in turn has a different logic rectangle position. We remove flip,
+ // set the logic rectangle, and apply flip again. You cannot simple use a 180deg-rotated
+ // rectangle, because custom shape mirroring is internally applied after the other
+ // transformations.
+ const bool bNeedsMirrorYCorrection = lcl_NeedsMirrorYCorrection(pObj); // remember state
+ if (bNeedsMirrorYCorrection)
+ {
+ const tools::Rectangle aRect(pObj->GetSnapRect());
+ const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1);
+ const Point aRight(aLeft.X() + 1000, aLeft.Y());
+ pObj->NbcMirror(aLeft, aRight);
+ }
+
+ // Build full sized logic rectangle from start and end given in anchor.
+ const tools::Rectangle aStartCellRect(
+ pDoc->GetMMRect(rAnchor.maStart.Col(), rAnchor.maStart.Row(), rAnchor.maStart.Col(),
+ rAnchor.maStart.Row(), rAnchor.maStart.Tab(), false /*bHiddenAsZero*/));
+ Point aStartPoint(aStartCellRect.Left(), aStartCellRect.Top());
+ aStartPoint.AdjustX(rAnchor.maStartOffset.getX());
+ aStartPoint.AdjustY(rAnchor.maStartOffset.getY());
+
+ const tools::Rectangle aEndCellRect(
+ pDoc->GetMMRect(rAnchor.maEnd.Col(), rAnchor.maEnd.Row(), rAnchor.maEnd.Col(),
+ rAnchor.maEnd.Row(), rAnchor.maEnd.Tab(), false /*bHiddenAsZero*/));
+
+ Point aEndPoint(aEndCellRect.Left(), aEndCellRect.Top());
+ aEndPoint.AdjustX(rAnchor.maEndOffset.getX());
+ aEndPoint.AdjustY(rAnchor.maEndOffset.getY());
+
+ // Set this as new, full sized logical rectangle
+ tools::Rectangle aNewRectangle(aStartPoint, aEndPoint);
+ aNewRectangle.Normalize();
+ if (!lcl_AreRectanglesApproxEqual(pObj->GetLogicRect(), aNewRectangle))
+ pObj->NbcSetLogicRect(lcl_makeSafeRectangle(aNewRectangle));
+
+ // The shape has the correct logical rectangle now. Reapply the above removed mirroring.
+ if (bNeedsMirrorYCorrection)
+ {
+ const tools::Rectangle aRect(pObj->GetSnapRect());
+ const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1);
+ const Point aRight(aLeft.X() + 1000, aLeft.Y());
+ pObj->NbcMirror(aLeft, aRight);
+ }
+}
+
+} // namespace
+
+void ScDrawLayer::ResizeLastRectFromAnchor(const SdrObject* pObj, ScDrawObjData& rData,
+ bool bNegativePage, bool bCanResize)
+{
+ tools::Rectangle aRect = pObj->GetSnapRect();
+ SCCOL nCol1 = rData.maStart.Col();
+ SCROW nRow1 = rData.maStart.Row();
+ SCTAB nTab1 = rData.maStart.Tab();
+ SCCOL nCol2 = rData.maEnd.Col();
+ SCROW nRow2 = rData.maEnd.Row();
+ SCTAB nTab2 = rData.maEnd.Tab();
+ Point aPos(pDoc->GetColOffset(nCol1, nTab1, /*bHiddenAsZero*/true),
+ pDoc->GetRowOffset(nRow1, nTab1, /*bHiddenAsZero*/true));
+ aPos.setX(convertTwipToMm100(aPos.X()));
+ aPos.setY(convertTwipToMm100(aPos.Y()));
+ aPos += lcl_calcAvailableDiff(*pDoc, nCol1, nRow1, nTab1, rData.maStartOffset);
+
+ // this sets the needed changed position (translation)
+ aRect.SetPos(aPos);
+
+ if (bCanResize)
+ {
+ // all this stuff is additional stuff to evtl. not only translate the
+ // range (Rectangle), but also check for and evtl. do corrections for it's size
+ const tools::Rectangle aLastCellRect(rData.getLastCellRect());
+
+ // If the row was hidden before, or we don't have a valid cell rect, calculate the
+ // new rect based on the end point.
+ // Also when the end point is set, we need to consider it.
+ if (rData.mbWasInHiddenRow || aLastCellRect.IsEmpty() || nRow1 != nRow2 || nCol1 != nCol2)
+ {
+ Point aEnd(pDoc->GetColOffset(nCol2, nTab2, /*bHiddenAsZero*/true),
+ pDoc->GetRowOffset(nRow2, nTab2, /*bHiddenAsZero*/true));
+ aEnd.setX(convertTwipToMm100(aEnd.X()));
+ aEnd.setY(convertTwipToMm100(aEnd.Y()));
+ aEnd += lcl_calcAvailableDiff(*pDoc, nCol2, nRow2, nTab2, rData.maEndOffset);
+
+ aRect = tools::Rectangle(aPos, aEnd);
+ }
+ else if (!aLastCellRect.IsEmpty())
+ {
+ // We calculate based on the last cell rect to be able to scale the image
+ // as much as the cell was scaled.
+ // Still, we keep the image in its current cell (to keep start anchor == end anchor)
+ const tools::Rectangle aCurrentCellRect(GetCellRect(*GetDocument(), rData.maStart, true));
+ tools::Long nCurrentWidth(aCurrentCellRect.GetWidth());
+ tools::Long nCurrentHeight(aCurrentCellRect.GetHeight());
+ const tools::Long nLastWidth(aLastCellRect.GetWidth());
+ const tools::Long nLastHeight(aLastCellRect.GetHeight());
+
+ // tdf#116931 Avoid and correct nifty numerical problems with the integer
+ // based and converted values (GetCellRect uses multiplies with HMM_PER_TWIPS)
+ if(nCurrentWidth + 1 == nLastWidth || nCurrentWidth == nLastWidth + 1)
+ {
+ nCurrentWidth = nLastWidth;
+ }
+
+ if(nCurrentHeight + 1 == nLastHeight || nCurrentHeight == nLastHeight + 1)
+ {
+ nCurrentHeight = nLastHeight;
+ }
+
+ // get initial ScalingFactors
+ double fWidthFactor(nCurrentWidth == nLastWidth || 0 == nLastWidth
+ ? 1.0
+ : static_cast<double>(nCurrentWidth) / static_cast<double>(nLastWidth));
+ double fHeightFactor(nCurrentHeight == nLastHeight || 0 == nLastHeight
+ ? 1.0
+ : static_cast<double>(nCurrentHeight) / static_cast<double>(nLastHeight));
+
+ // check if we grow or shrink - and at all
+ const bool bIsGrowing(nCurrentWidth > nLastWidth || nCurrentHeight > nLastHeight);
+ const bool bIsShrinking(nCurrentWidth < nLastWidth || nCurrentHeight < nLastHeight);
+ const bool bIsSizeChanged(bIsGrowing || bIsShrinking);
+
+ // handle AspectRatio, only needed if size does change
+ if(bIsSizeChanged && pObj->shouldKeepAspectRatio())
+ {
+ tools::Rectangle aRectIncludingOffset = aRect;
+ aRectIncludingOffset.setWidth(aRect.GetWidth() + rData.maStartOffset.X());
+ aRectIncludingOffset.setHeight(aRect.GetHeight() + rData.maStartOffset.Y());
+ tools::Long nWidth = aRectIncludingOffset.GetWidth();
+ assert(nWidth && "div-by-zero");
+ double fMaxWidthFactor = static_cast<double>(nCurrentWidth)
+ / static_cast<double>(nWidth);
+ tools::Long nHeight = aRectIncludingOffset.GetHeight();
+ assert(nHeight && "div-by-zero");
+ double fMaxHeightFactor = static_cast<double>(nCurrentHeight)
+ / static_cast<double>(nHeight);
+ double fMaxFactor = std::min(fMaxHeightFactor, fMaxWidthFactor);
+
+ if(bIsGrowing) // cell is growing larger
+ {
+ // To actually grow the image, we need to take the max
+ fWidthFactor = std::max(fWidthFactor, fHeightFactor);
+ }
+ else if(bIsShrinking) // cell is growing smaller, take the min
+ {
+ fWidthFactor = std::min(fWidthFactor, fHeightFactor);
+ }
+
+ // We don't want the image to become larger than the current cell
+ fWidthFactor = fHeightFactor = std::min(fWidthFactor, fMaxFactor);
+ }
+
+ if(bIsSizeChanged)
+ {
+ // tdf#116931 re-organized scaling (if needed)
+ // Check if we need to scale at all. Always scale on growing.
+ bool bNeedToScale(bIsGrowing);
+
+ if(!bNeedToScale && bIsShrinking)
+ {
+ // Check if original still fits into space. Do *not* forget to
+ // compare with evtl. numerically corrected aCurrentCellRect
+ const bool bFitsInX(aRect.Right() <= aCurrentCellRect.Left() + nCurrentWidth);
+ const bool bFitsInY(aRect.Bottom() <= aCurrentCellRect.Top() + nCurrentHeight);
+
+ // If the image still fits in the smaller cell, don't resize it at all
+ bNeedToScale = (!bFitsInX || !bFitsInY);
+ }
+
+ if(bNeedToScale)
+ {
+ // tdf#116931 use transformations now. Translation is already applied
+ // (see aRect.SetPos above), so only scale needs to be applied - relative
+ // to *new* CellRect (which is aCurrentCellRect).
+ // Prepare scale relative to top-left of aCurrentCellRect
+ basegfx::B2DHomMatrix aChange;
+
+ aChange.translate(-aCurrentCellRect.Left(), -aCurrentCellRect.Top());
+ aChange.scale(fWidthFactor, fHeightFactor);
+ aChange.translate(aCurrentCellRect.Left(), aCurrentCellRect.Top());
+
+ // create B2DRange and transform by prepared scale
+ basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aRect);
+
+ aNewRange.transform(aChange);
+
+ // apply to aRect
+ aRect = tools::Rectangle(
+ basegfx::fround(aNewRange.getMinX()), basegfx::fround(aNewRange.getMinY()),
+ basegfx::fround(aNewRange.getMaxX()), basegfx::fround(aNewRange.getMaxY()));
+ }
+ }
+ }
+ }
+
+ if (bNegativePage)
+ MirrorRectRTL(aRect);
+
+ rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect), pObj->IsVisible());
+}
+
+void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* pObj, ScDrawObjData& rData)
+{
+ // This is called from ScXMLImport::endDocument()
+ if (!pDoc || !pObj)
+ return;
+ if (!rData.getShapeRect().IsEmpty())
+ return; // already initialized, should not happen
+ if (rData.meType == ScDrawObjData::CellNote || rData.meType == ScDrawObjData::ValidationCircle
+ || rData.meType == ScDrawObjData::DetectiveArrow)
+ return; // handled in RecalcPos
+
+ // Prevent multiple broadcasts during the series of changes.
+ bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked();
+ pObj->getSdrModelFromSdrObject().setLock(true);
+
+ // rNoRotatedAnchor refers in its start and end addresses and its start and end offsets to
+ // the logic rectangle of the object. The values are so, as if no hidden columns and rows
+ // exists and if it is a LTR sheet. These values are directly used for XML in ODF file.
+ ScDrawObjData& rNoRotatedAnchor = *GetNonRotatedObjData(pObj, true /*bCreate*/);
+
+ // From XML import, rData contains temporarily the anchor information as they are given in
+ // XML. Copy it to rNoRotatedAnchor, where it belongs. rData will later contain the anchor
+ // of the transformed object as visible on screen.
+ rNoRotatedAnchor.maStart = rData.maStart;
+ rNoRotatedAnchor.maEnd = rData.maEnd;
+ rNoRotatedAnchor.maStartOffset = rData.maStartOffset;
+ rNoRotatedAnchor.maEndOffset = rData.maEndOffset;
+
+ SCCOL nCol1 = rNoRotatedAnchor.maStart.Col();
+ SCROW nRow1 = rNoRotatedAnchor.maStart.Row();
+ SCTAB nTab1 = rNoRotatedAnchor.maStart.Tab(); // Used as parameter several times
+
+ // Object has coordinates relative to left/top of containing cell in XML. Change object to
+ // absolute coordinates as internally used.
+ const tools::Rectangle aRect(
+ pDoc->GetMMRect(nCol1, nRow1, nCol1, nRow1, nTab1, false /*bHiddenAsZero*/));
+ const Size aShift(aRect.Left(), aRect.Top());
+ pObj->NbcMove(aShift);
+
+ const ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj);
+ if (aAnchorType == SCA_CELL_RESIZE)
+ {
+ if (pObj->GetObjIdentifier() == SdrObjKind::Line)
+ {
+ // Horizontal lines might have wrong start and end anchor because of erroneously applied
+ // 180deg rotation (tdf#137446). Other lines have wrong end anchor. Coordinates in
+ // object are correct. Use them for recreating the anchor.
+ const basegfx::B2DPolygon aPoly(
+ static_cast<SdrPathObj*>(pObj)->GetPathPoly().getB2DPolygon(0));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
+ const Point aPointLT(FRound(std::min(aB2DPoint0.getX(), aB2DPoint1.getX())),
+ FRound(std::min(aB2DPoint0.getY(), aB2DPoint1.getY())));
+ const Point aPointRB(FRound(std::max(aB2DPoint0.getX(), aB2DPoint1.getX())),
+ FRound(std::max(aB2DPoint0.getY(), aB2DPoint1.getY())));
+ const tools::Rectangle aObjRect(aPointLT, aPointRB);
+ GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, nTab1,
+ false /*bHiddenAsZero*/);
+ }
+ else if (pObj->GetObjIdentifier() == SdrObjKind::Measure)
+ {
+ // Measure lines might have got wrong start and end anchor from XML import. Recreate
+ // anchor from start and end point.
+ SdrMeasureObj* pMeasureObj = static_cast<SdrMeasureObj*>(pObj);
+ // tdf#137576. The logic rectangle has likely no current values here, but only the
+ // 1cm x 1cm default size. The call of TakeUnrotatedSnapRect is currently (LO 7.2)
+ // the only way to force a recalc of the logic rectangle.
+ tools::Rectangle aObjRect;
+ pMeasureObj->TakeUnrotatedSnapRect(aObjRect);
+ GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, rData.maStart.Tab(),
+ false /*bHiddenAsZero*/);
+ }
+ else if (pObj->IsResizeProtect())
+ {
+ // tdf#154005: This is a workaround for documents created with LO 6 and older.
+ rNoRotatedAnchor.mbResizeWithCell = false;
+ rData.mbResizeWithCell = false;
+ UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1,
+ true /*bUseLogicRect*/);
+ }
+ else
+ {
+ // In case there are hidden rows or cols, versions 7.0 and earlier have written width and
+ // height in file so that hidden row or col are count as zero. XML import bases the
+ // logical rectangle of the object on it. Shapes have at least wrong size, when row or col
+ // are shown. We try to regenerate the logic rectangle as far as possible from the anchor.
+ // ODF specifies anyway, that the size has to be ignored, if end cell attributes exist.
+ lcl_SetLogicRectFromAnchor(pObj, rNoRotatedAnchor, pDoc);
+ }
+ }
+ else // aAnchorType == SCA_CELL
+ {
+ // XML has no end cell address in this case. We generate it from position.
+ UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1,
+ true /*bUseLogicRect*/);
+ }
+
+ // Make sure maShapeRect of rNoRotatedAnchor is not empty. Method ScDrawView::Notify()
+ // needs it to detect a change in object geometry. For example a 180deg rotation effects only
+ // logic rect.
+ rNoRotatedAnchor.setShapeRect(GetDocument(), pObj->GetLogicRect(), true);
+
+ // Start and end addresses and offsets in rData refer to the actual snap rectangle of the
+ // shape. We initialize them here based on the "full" sized object. Adaptation to reduced size
+ // (by hidden row/col) is done later in RecalcPos.
+ GetCellAnchorFromPosition(pObj->GetSnapRect(), rData, *pDoc, nTab1, false /*bHiddenAsZero*/);
+
+ // As of ODF 1.3 strict there is no attribute to store whether an object is hidden. So a "visible"
+ // object might actually be hidden by being in hidden row or column. We detect it here.
+ // Note, that visibility by hidden row or column refers to the snap rectangle.
+ if (pObj->IsVisible()
+ && (pDoc->RowHidden(rData.maStart.Row(), rData.maStart.Tab())
+ || pDoc->ColHidden(rData.maStart.Col(), rData.maStart.Tab())))
+ pObj->SetVisible(false);
+
+ // Set visibility. ToDo: Really used?
+ rNoRotatedAnchor.setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible());
+
+ // And set maShapeRect in rData. It stores not only the current rectangles, but currently,
+ // existence of maShapeRect is the flag for initialization is done.
+ rData.setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible());
+
+ pObj->getSdrModelFromSdrObject().setLock(bWasLocked);
+}
+
+void ScDrawLayer::RecalcPos( SdrObject* pObj, ScDrawObjData& rData, bool bNegativePage, bool bUpdateNoteCaptionPos )
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::RecalcPos - missing document" );
+ if( !pDoc )
+ return;
+
+ if (rData.meType == ScDrawObjData::CellNote)
+ {
+ OSL_ENSURE( rData.maStart.IsValid(), "ScDrawLayer::RecalcPos - invalid position for cell note" );
+ /* #i109372# On insert/remove rows/columns/cells: Updating the caption
+ position must not be done, if the cell containing the note has not
+ been moved yet in the document. The calling code now passes an
+ additional boolean stating if the cells are already moved. */
+ /* tdf #152081 Do not change hidden objects. That would produce zero height
+ or width and loss of caption.*/
+ if (bUpdateNoteCaptionPos && pObj->IsVisible())
+ {
+ /* When inside an undo action, there may be pending note captions
+ where cell note is already deleted (thus document cannot find
+ the note object anymore). The caption will be deleted later
+ with drawing undo. */
+ if( ScPostIt* pNote = pDoc->GetNote( rData.maStart ) )
+ pNote->UpdateCaptionPos( rData.maStart );
+ }
+ return;
+ }
+
+ bool bValid1 = rData.maStart.IsValid();
+ SCCOL nCol1 = rData.maStart.Col();
+ SCROW nRow1 = rData.maStart.Row();
+ SCTAB nTab1 = rData.maStart.Tab();
+ bool bValid2 = rData.maEnd.IsValid();
+ SCCOL nCol2 = rData.maEnd.Col();
+ SCROW nRow2 = rData.maEnd.Row();
+ SCTAB nTab2 = rData.maEnd.Tab();
+
+ if (rData.meType == ScDrawObjData::ValidationCircle)
+ {
+ // Validation circle for detective.
+ rData.setShapeRect(GetDocument(), pObj->GetLogicRect());
+
+ // rData.maStart should contain the address of the be validated cell.
+ tools::Rectangle aRect = GetCellRect(*GetDocument(), rData.maStart, true);
+ aRect.AdjustLeft( -250 );
+ aRect.AdjustRight(250 );
+ aRect.AdjustTop( -70 );
+ aRect.AdjustBottom(70 );
+ if ( bNegativePage )
+ MirrorRectRTL( aRect );
+
+ if ( pObj->GetLogicRect() != aRect )
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect));
+ // maStart has the meaning of "to be validated cell" in a validation circle. For usual
+ // drawing objects it has the meaning "left/top of logic/snap rect". Because the rectangle
+ // is expanded above, SetLogicRect() will set maStart to one cell left and one cell above
+ // of the to be validated cell. We need to backup the old value and restore it.
+ ScAddress aBackup(rData.maStart);
+ pObj->SetLogicRect(rData.getShapeRect());
+ rData.maStart = aBackup;
+ }
+ }
+ else if (rData.meType == ScDrawObjData::DetectiveArrow)
+ {
+ rData.setShapeRect(GetDocument(), pObj->GetLogicRect());
+ basegfx::B2DPolygon aCalcPoly;
+ Point aOrigStartPos(pObj->GetPoint(0));
+ Point aOrigEndPos(pObj->GetPoint(1));
+ aCalcPoly.append(basegfx::B2DPoint(aOrigStartPos.X(), aOrigStartPos.Y()));
+ aCalcPoly.append(basegfx::B2DPoint(aOrigEndPos.X(), aOrigEndPos.Y()));
+ //TODO: do not create multiple Undos for one object (last one can be omitted then)
+
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if( bValid1 )
+ {
+ Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) );
+ if (!pDoc->ColHidden(nCol1, nTab1, nullptr, &nLastCol))
+ aPos.AdjustX(pDoc->GetColWidth( nCol1, nTab1 ) / 4 );
+ if (!pDoc->RowHidden(nRow1, nTab1, nullptr, &nLastRow))
+ aPos.AdjustY(pDoc->GetRowHeight( nRow1, nTab1 ) / 2 );
+ aPos.setX(convertTwipToMm100(aPos.X()));
+ aPos.setY(convertTwipToMm100(aPos.Y()));
+ Point aStartPos = aPos;
+ if ( bNegativePage )
+ aStartPos.setX( -aStartPos.X() ); // don't modify aPos - used below
+ if ( pObj->GetPoint( 0 ) != aStartPos )
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+
+ rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos));
+ pObj->SetPoint( aStartPos, 0 );
+ }
+
+ if( !bValid2 )
+ {
+ Point aEndPos( aPos.X() + DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET );
+ if (aEndPos.Y() < 0)
+ aEndPos.AdjustY(2 * DET_ARROW_OFFSET);
+ if ( bNegativePage )
+ aEndPos.setX( -aEndPos.X() );
+ if ( pObj->GetPoint( 1 ) != aEndPos )
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+
+ rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 1, aEndPos));
+ pObj->SetPoint( aEndPos, 1 );
+ }
+ }
+ }
+ if( bValid2 )
+ {
+ Point aPos( pDoc->GetColOffset( nCol2, nTab2 ), pDoc->GetRowOffset( nRow2, nTab2 ) );
+ if (!pDoc->ColHidden(nCol2, nTab2, nullptr, &nLastCol))
+ aPos.AdjustX(pDoc->GetColWidth( nCol2, nTab2 ) / 4 );
+ if (!pDoc->RowHidden(nRow2, nTab2, nullptr, &nLastRow))
+ aPos.AdjustY(pDoc->GetRowHeight( nRow2, nTab2 ) / 2 );
+ aPos.setX(convertTwipToMm100(aPos.X()));
+ aPos.setY(convertTwipToMm100(aPos.Y()));
+ Point aEndPos = aPos;
+ if ( bNegativePage )
+ aEndPos.setX( -aEndPos.X() ); // don't modify aPos - used below
+ if ( pObj->GetPoint( 1 ) != aEndPos )
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+
+ rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 1, aEndPos));
+ pObj->SetPoint( aEndPos, 1 );
+ }
+
+ if( !bValid1 )
+ {
+ Point aStartPos( aPos.X() - DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET );
+ if (aStartPos.X() < 0)
+ aStartPos.AdjustX(2 * DET_ARROW_OFFSET);
+ if (aStartPos.Y() < 0)
+ aStartPos.AdjustY(2 * DET_ARROW_OFFSET);
+ if ( bNegativePage )
+ aStartPos.setX( -aStartPos.X() );
+ if ( pObj->GetPoint( 0 ) != aStartPos )
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+
+ rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos));
+ pObj->SetPoint( aStartPos, 0 );
+ }
+ }
+ }
+ } // end ScDrawObjData::DetectiveArrow
+ else // start ScDrawObjData::DrawingObject
+ {
+ // Do not change hidden objects. That would produce zero height or width and loss of offsets.
+ if (!pObj->IsVisible())
+ return;
+
+ // Prevent multiple broadcasts during the series of changes.
+ bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked();
+ pObj->getSdrModelFromSdrObject().setLock(true);
+
+ bool bCanResize = bValid2 && !pObj->IsResizeProtect() && rData.mbResizeWithCell;
+
+ // update anchor with snap rect
+ ResizeLastRectFromAnchor( pObj, rData, bNegativePage, bCanResize );
+
+ ScDrawObjData& rNoRotatedAnchor = *GetNonRotatedObjData( pObj, true /*bCreate*/);
+
+ if( bCanResize )
+ {
+ tools::Rectangle aNew = rData.getShapeRect();
+ tools::Rectangle aOld(pObj->GetSnapRect());
+ if (!lcl_AreRectanglesApproxEqual(aNew, aOld))
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+
+ // ToDo: Implement NbcSetSnapRect of SdrMeasureObj. Then this can be removed.
+ tools::Long nOldWidth = aOld.GetWidth();
+ tools::Long nOldHeight = aOld.GetHeight();
+ if (pObj->IsPolyObj() && nOldWidth && nOldHeight)
+ {
+ // Polyline objects need special treatment.
+ Size aSizeMove(aNew.Left()-aOld.Left(), aNew.Top()-aOld.Top());
+ pObj->NbcMove(aSizeMove);
+
+ double fXFrac = static_cast<double>(aNew.GetWidth()) / static_cast<double>(nOldWidth);
+ double fYFrac = static_cast<double>(aNew.GetHeight()) / static_cast<double>(nOldHeight);
+ pObj->NbcResize(aNew.TopLeft(), Fraction(fXFrac), Fraction(fYFrac));
+ }
+
+ rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(rData.getShapeRect()), pObj->IsVisible());
+ if (pObj->GetObjIdentifier() == SdrObjKind::CustomShape)
+ pObj->AdjustToMaxRect(rData.getShapeRect());
+ else
+ pObj->SetSnapRect(rData.getShapeRect());
+
+ // The shape rectangle in the 'unrotated' anchor needs to be updated to the changed
+ // object geometry. It is used in adjustAnchoredPosition() in ScDrawView::Notify().
+ rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible());
+ }
+ }
+ else
+ {
+ const Point aPos(rData.getShapeRect().TopLeft());
+ if ( pObj->GetRelativePos() != aPos )
+ {
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ pObj->SetRelativePos( aPos );
+ rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible());
+ }
+ }
+ /*
+ * If we were not allowed resize the object, then the end cell anchor
+ * is possibly incorrect now, and if the object has no end-cell (e.g.
+ * missing in original .xml) we are also forced to generate one
+ */
+ bool bEndAnchorIsBad = !bValid2 || pObj->IsResizeProtect();
+ if (bEndAnchorIsBad)
+ {
+ // update 'rotated' anchor
+ ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rData, *pDoc, nTab1, false);
+ // update 'unrotated' anchor
+ ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1 );
+ }
+
+ // End prevent multiple broadcasts during the series of changes.
+ pObj->getSdrModelFromSdrObject().setLock(bWasLocked);
+ if (!bWasLocked)
+ pObj->BroadcastObjectChange();
+ } // end ScDrawObjData::DrawingObject
+}
+
+bool ScDrawLayer::GetPrintArea( ScRange& rRange, bool bSetHor, bool bSetVer ) const
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::GetPrintArea without document" );
+ if ( !pDoc )
+ return false;
+
+ SCTAB nTab = rRange.aStart.Tab();
+ OSL_ENSURE( rRange.aEnd.Tab() == nTab, "GetPrintArea: Tab differ" );
+
+ bool bNegativePage = pDoc->IsNegativePage( nTab );
+
+ bool bAny = false;
+ tools::Long nEndX = 0;
+ tools::Long nEndY = 0;
+ tools::Long nStartX = LONG_MAX;
+ tools::Long nStartY = LONG_MAX;
+
+ // Calculate borders
+
+ if (!bSetHor)
+ {
+ nStartX = 0;
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCCOL i;
+ for (i=0; i<nStartCol; i++)
+ nStartX +=pDoc->GetColWidth(i,nTab);
+ nEndX = nStartX;
+ SCCOL nEndCol = rRange.aEnd.Col();
+ for (i=nStartCol; i<=nEndCol; i++)
+ nEndX += pDoc->GetColWidth(i,nTab);
+ nStartX = convertTwipToMm100(nStartX);
+ nEndX = convertTwipToMm100(nEndX);
+ }
+ if (!bSetVer)
+ {
+ nStartY = pDoc->GetRowHeight( 0, rRange.aStart.Row()-1, nTab);
+ nEndY = nStartY + pDoc->GetRowHeight( rRange.aStart.Row(),
+ rRange.aEnd.Row(), nTab);
+ nStartY = convertTwipToMm100(nStartY);
+ nEndY = convertTwipToMm100(nEndY);
+ }
+
+ if ( bNegativePage )
+ {
+ nStartX = -nStartX; // positions are negative, swap start/end so the same comparisons work
+ nEndX = -nEndX;
+ ::std::swap( nStartX, nEndX );
+ }
+
+ const SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page not found");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ //TODO: test Flags (hidden?)
+
+ tools::Rectangle aObjRect = pObject->GetCurrentBoundRect();
+ bool bFit = true;
+ if ( !bSetHor && ( aObjRect.Right() < nStartX || aObjRect.Left() > nEndX ) )
+ bFit = false;
+ if ( !bSetVer && ( aObjRect.Bottom() < nStartY || aObjRect.Top() > nEndY ) )
+ bFit = false;
+ // #i104716# don't include hidden note objects
+ if ( bFit && pObject->GetLayer() != SC_LAYER_HIDDEN )
+ {
+ if (bSetHor)
+ {
+ if (aObjRect.Left() < nStartX) nStartX = aObjRect.Left();
+ if (aObjRect.Right() > nEndX) nEndX = aObjRect.Right();
+ }
+ if (bSetVer)
+ {
+ if (aObjRect.Top() < nStartY) nStartY = aObjRect.Top();
+ if (aObjRect.Bottom() > nEndY) nEndY = aObjRect.Bottom();
+ }
+ bAny = true;
+ }
+
+ pObject = aIter.Next();
+ }
+ }
+
+ if ( bNegativePage )
+ {
+ nStartX = -nStartX; // reverse transformation, so the same cell address calculation works
+ nEndX = -nEndX;
+ ::std::swap( nStartX, nEndX );
+ }
+
+ if (bAny)
+ {
+ OSL_ENSURE( nStartX<=nEndX && nStartY<=nEndY, "Start/End wrong in ScDrawLayer::GetPrintArea" );
+
+ if (bSetHor)
+ {
+ nStartX = o3tl::toTwips(nStartX, o3tl::Length::mm100);
+ nEndX = o3tl::toTwips(nEndX, o3tl::Length::mm100);
+ tools::Long nWidth;
+
+ nWidth = 0;
+ rRange.aStart.SetCol( 0 );
+ if (nWidth <= nStartX)
+ {
+ for (SCCOL nCol : pDoc->GetColumnsRange(nTab, 0, pDoc->MaxCol()))
+ {
+ nWidth += pDoc->GetColWidth(nCol,nTab);
+ if (nWidth > nStartX)
+ {
+ rRange.aStart.SetCol( nCol );
+ break;
+ }
+ }
+ }
+
+ nWidth = 0;
+ rRange.aEnd.SetCol( 0 );
+ if (nWidth <= nEndX)
+ {
+ for (SCCOL nCol : pDoc->GetColumnsRange(nTab, 0, pDoc->MaxCol())) //TODO: start at Start
+ {
+ nWidth += pDoc->GetColWidth(nCol,nTab);
+ if (nWidth > nEndX)
+ {
+ rRange.aEnd.SetCol( nCol );
+ break;
+ }
+ }
+ }
+ }
+
+ if (bSetVer)
+ {
+ nStartY = o3tl::toTwips(nStartY, o3tl::Length::mm100);
+ nEndY = o3tl::toTwips(nEndY, o3tl::Length::mm100);
+ SCROW nRow = pDoc->GetRowForHeight( nTab, nStartY);
+ rRange.aStart.SetRow( nRow>0 ? (nRow-1) : 0);
+ nRow = pDoc->GetRowForHeight( nTab, nEndY);
+ rRange.aEnd.SetRow( nRow == pDoc->MaxRow() ? pDoc->MaxRow() :
+ (nRow>0 ? (nRow-1) : 0));
+ }
+ }
+ else
+ {
+ if (bSetHor)
+ {
+ rRange.aStart.SetCol(0);
+ rRange.aEnd.SetCol(0);
+ }
+ if (bSetVer)
+ {
+ rRange.aStart.SetRow(0);
+ rRange.aEnd.SetRow(0);
+ }
+ }
+ return bAny;
+}
+
+void ScDrawLayer::AddCalcUndo( std::unique_ptr<SdrUndoAction> pUndo )
+{
+ if (bRecording)
+ {
+ if (!pUndoGroup)
+ pUndoGroup.reset(new SdrUndoGroup(*this));
+
+ pUndoGroup->AddAction( std::move(pUndo) );
+ }
+}
+
+void ScDrawLayer::BeginCalcUndo(bool bDisableTextEditUsesCommonUndoManager)
+{
+ SetDisableTextEditUsesCommonUndoManager(bDisableTextEditUsesCommonUndoManager);
+ pUndoGroup.reset();
+ bRecording = true;
+}
+
+std::unique_ptr<SdrUndoGroup> ScDrawLayer::GetCalcUndo()
+{
+ std::unique_ptr<SdrUndoGroup> pRet = std::move(pUndoGroup);
+ bRecording = false;
+ SetDisableTextEditUsesCommonUndoManager(false);
+ return pRet;
+}
+
+void ScDrawLayer::MoveArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2,
+ SCCOL nDx,SCROW nDy, bool bInsDel, bool bUpdateNoteCaptionPos )
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::MoveArea without document" );
+ if ( !pDoc )
+ return;
+
+ if (!bAdjustEnabled)
+ return;
+
+ bool bNegativePage = pDoc->IsNegativePage( nTab );
+
+ tools::Rectangle aRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab );
+ lcl_ReverseTwipsToMM( aRect );
+ //TODO: use twips directly?
+
+ Point aMove;
+
+ if (nDx > 0)
+ for (SCCOL s=0; s<nDx; s++)
+ aMove.AdjustX(pDoc->GetColWidth(s+nCol1,nTab) );
+ else
+ for (SCCOL s=-1; s>=nDx; s--)
+ aMove.AdjustX( -(pDoc->GetColWidth(s+nCol1,nTab)) );
+ if (nDy > 0)
+ aMove.AdjustY(pDoc->GetRowHeight( nRow1, nRow1+nDy-1, nTab) );
+ else
+ aMove.AdjustY( -sal_Int16(pDoc->GetRowHeight( nRow1+nDy, nRow1-1, nTab)) );
+
+ if ( bNegativePage )
+ aMove.setX( -aMove.X() );
+
+ Point aTopLeft = aRect.TopLeft(); // Beginning when zoomed out
+ if (bInsDel)
+ {
+ if ( aMove.X() != 0 && nDx < 0 ) // nDx counts cells, sign is independent of RTL
+ aTopLeft.AdjustX(aMove.X() );
+ if ( aMove.Y() < 0 )
+ aTopLeft.AdjustY(aMove.Y() );
+ }
+
+ // Detectiv arrows: Adjust cell position
+
+ MoveCells( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy, bUpdateNoteCaptionPos );
+}
+
+bool ScDrawLayer::HasObjectsInRows( SCTAB nTab, SCROW nStartRow, SCROW nEndRow )
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::HasObjectsInRows without document" );
+ if ( !pDoc )
+ return false;
+
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page not found");
+ if (!pPage)
+ return false;
+
+ // for an empty page, there's no need to calculate the row heights
+ if (!pPage->GetObjCount())
+ return false;
+
+ tools::Rectangle aTestRect;
+
+ aTestRect.AdjustTop(pDoc->GetRowHeight( 0, nStartRow-1, nTab) );
+
+ if (nEndRow==pDoc->MaxRow())
+ aTestRect.SetBottom( MAXMM );
+ else
+ {
+ aTestRect.SetBottom( aTestRect.Top() );
+ aTestRect.AdjustBottom(pDoc->GetRowHeight( nStartRow, nEndRow, nTab) );
+ aTestRect.SetBottom(convertTwipToMm100(aTestRect.Bottom()));
+ }
+
+ aTestRect.SetTop(convertTwipToMm100(aTestRect.Top()));
+
+ aTestRect.SetLeft( 0 );
+ aTestRect.SetRight( MAXMM );
+
+ bool bNegativePage = pDoc->IsNegativePage( nTab );
+ if ( bNegativePage )
+ MirrorRectRTL( aTestRect );
+
+ bool bFound = false;
+
+ tools::Rectangle aObjRect;
+ SdrObjListIter aIter( pPage );
+ SdrObject* pObject = aIter.Next();
+ while ( pObject && !bFound )
+ {
+ aObjRect = pObject->GetSnapRect(); //TODO: GetLogicRect ?
+ if (aTestRect.Contains(aObjRect.TopLeft()) || aTestRect.Contains(aObjRect.BottomLeft()))
+ bFound = true;
+
+ pObject = aIter.Next();
+ }
+
+ return bFound;
+}
+
+void ScDrawLayer::DeleteObjectsInArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1,
+ SCCOL nCol2,SCROW nRow2, bool bAnchored )
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::DeleteObjectsInArea without document" );
+ if ( !pDoc )
+ return;
+
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ return;
+
+ pPage->RecalcObjOrdNums();
+
+ const size_t nObjCount = pPage->GetObjCount();
+ if (!nObjCount)
+ return;
+
+ tools::Rectangle aDelRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab );
+ tools::Rectangle aDelCircle = aDelRect;
+ aDelCircle.AdjustLeft(-250);
+ aDelCircle.AdjustRight(250);
+ aDelCircle.AdjustTop(-70);
+ aDelCircle.AdjustBottom(70);
+
+ std::vector<SdrObject*> ppObj;
+ ppObj.reserve(nObjCount);
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ // do not delete note caption, they are always handled by the cell note
+ // TODO: detective objects are still deleted, is this desired?
+ if (!IsNoteCaption( pObject ))
+ {
+ tools::Rectangle aObjRect;
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObject);
+ if (pObjData && pObjData->meType == ScDrawObjData::ValidationCircle)
+ {
+ aObjRect = pObject->GetLogicRect();
+ if(aDelCircle.Contains(aObjRect))
+ ppObj.push_back(pObject);
+ }
+ else
+ {
+ aObjRect = pObject->GetCurrentBoundRect();
+ if (aDelRect.Contains(aObjRect))
+ {
+ if (bAnchored)
+ {
+ ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject);
+ if (aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE)
+ ppObj.push_back(pObject);
+ }
+ else
+ ppObj.push_back(pObject);
+ }
+ }
+ }
+
+ pObject = aIter.Next();
+ }
+
+ if (bRecording)
+ for (auto p : ppObj)
+ AddCalcUndo(std::make_unique<SdrUndoDelObj>(*p));
+
+ for (auto p : ppObj)
+ pPage->RemoveObject(p->GetOrdNum());
+}
+
+void ScDrawLayer::DeleteObjectsInSelection( const ScMarkData& rMark )
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::DeleteObjectsInSelection without document" );
+ if ( !pDoc )
+ return;
+
+ if ( !rMark.IsMultiMarked() )
+ return;
+
+ const ScRange& aMarkRange = rMark.GetMultiMarkArea();
+
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (const SCTAB nTab : rMark)
+ {
+ if (nTab >= nTabCount)
+ break;
+
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ if (pPage)
+ {
+ pPage->RecalcObjOrdNums();
+ const size_t nObjCount = pPage->GetObjCount();
+ if (nObjCount)
+ {
+ // Rectangle around the whole selection
+ tools::Rectangle aMarkBound = pDoc->GetMMRect(
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab );
+
+ std::vector<SdrObject*> ppObj;
+ ppObj.reserve(nObjCount);
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ // do not delete note caption, they are always handled by the cell note
+ // TODO: detective objects are still deleted, is this desired?
+ if (!IsNoteCaption( pObject ))
+ {
+ tools::Rectangle aObjRect = pObject->GetCurrentBoundRect();
+ ScRange aRange = pDoc->GetRange(nTab, aObjRect);
+ bool bObjectInMarkArea =
+ aMarkBound.Contains(aObjRect) && rMark.IsAllMarked(aRange);
+ const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObject);
+ ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject);
+ bool bObjectAnchoredToMarkedCell
+ = ((aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE)
+ && pObjData && pObjData->maStart.IsValid()
+ && rMark.IsCellMarked(pObjData->maStart.Col(),
+ pObjData->maStart.Row()));
+ if (bObjectInMarkArea || bObjectAnchoredToMarkedCell)
+ {
+ ppObj.push_back(pObject);
+ }
+ }
+
+ pObject = aIter.Next();
+ }
+
+ // Delete objects (backwards)
+
+ if (bRecording)
+ for (auto p : ppObj)
+ AddCalcUndo(std::make_unique<SdrUndoDelObj>(*p));
+
+ for (auto p : ppObj)
+ pPage->RemoveObject(p->GetOrdNum());
+ }
+ }
+ else
+ {
+ OSL_FAIL("pPage?");
+ }
+ }
+}
+
+void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rectangle& rRange )
+{
+ // copy everything in the specified range into the same page (sheet) in the clipboard doc
+
+ SdrPage* pSrcPage = GetPage(static_cast<sal_uInt16>(nTab));
+ if (!pSrcPage)
+ return;
+
+ ScDrawLayer* pDestModel = nullptr;
+ SdrPage* pDestPage = nullptr;
+
+ ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nTab);
+
+ SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat );
+ SdrObject* pOldObject = aIter.Next();
+ while (pOldObject)
+ {
+ // Catch objects where the object itself is inside the rectangle to be copied.
+ bool bObjectInArea = rRange.Contains(pOldObject->GetCurrentBoundRect());
+ // Catch objects whose anchor is inside the rectangle to be copied.
+ const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject);
+ if (pObjData)
+ bObjectInArea = bObjectInArea || aClipRange.Contains(pObjData->maStart);
+
+ // do not copy internal objects (detective) and note captions
+ if (bObjectInArea && pOldObject->GetLayer() != SC_LAYER_INTERN
+ && !IsNoteCaption(pOldObject))
+ {
+ if ( !pDestModel )
+ {
+ pDestModel = pClipDoc->GetDrawLayer(); // does the document already have a drawing layer?
+ if ( !pDestModel )
+ {
+ // allocate drawing layer in clipboard document only if there are objects to copy
+
+ pClipDoc->InitDrawLayer(); //TODO: create contiguous pages
+ pDestModel = pClipDoc->GetDrawLayer();
+ }
+ if (pDestModel)
+ pDestPage = pDestModel->GetPage( static_cast<sal_uInt16>(nTab) );
+ }
+
+ OSL_ENSURE( pDestPage, "no page" );
+ if (pDestPage)
+ {
+ // Clone to target SdrModel
+ rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*pDestModel));
+ uno::Reference< chart2::XChartDocument > xOldChart( ScChartHelper::GetChartFromSdrObject( pOldObject ) );
+ if(!xOldChart.is())//#i110034# do not move charts as they lose all their data references otherwise
+ {
+ if (pObjData)
+ {
+ // The object is anchored to cell. The position is determined by the start
+ // address. Copying into the clipboard does not change the anchor.
+ // ToDo: Adapt Offset relative to anchor cell size for cell anchored.
+ // ToDo: Adapt Offset and size for cell-anchored with resize objects.
+ // ToDo: Exclude object from resize if disallowed at object.
+ }
+ else
+ {
+ // The object is anchored to page. We make its position so, that the
+ // cell behind the object will have the same address in clipboard document as
+ // in source document. So we will be able to reconstruct the original cell
+ // address from position when pasting the object.
+ tools::Rectangle aObjRect = pOldObject->GetSnapRect();
+ ScRange aPseudoAnchor
+ = pDoc->GetRange(nTab, aObjRect, true /*bHiddenAsZero*/);
+ tools::Rectangle aSourceCellRect
+ = GetCellRect(*pDoc, aPseudoAnchor.aStart, false /*bMergedCell*/);
+ tools::Rectangle aDestCellRect
+ = GetCellRect(*pClipDoc, aPseudoAnchor.aStart, false);
+ Point aMove = aDestCellRect.TopLeft() - aSourceCellRect.TopLeft();
+ pNewObject->NbcMove(Size(aMove.getX(), aMove.getY()));
+ }
+ }
+
+ pDestPage->InsertObject( pNewObject.get() );
+
+ // no undo needed in clipboard document
+ // charts are not updated
+ }
+ }
+
+ pOldObject = aIter.Next();
+ }
+}
+
+static bool lcl_IsAllInRange( const ::std::vector< ScRangeList >& rRangesVector, const ScRange& rClipRange )
+{
+ // check if every range of rRangesVector is completely in rClipRange
+
+ for( const ScRangeList& rRanges : rRangesVector )
+ {
+ for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ )
+ {
+ const ScRange & rRange = rRanges[ i ];
+ if ( !rClipRange.Contains( rRange ) )
+ {
+ return false; // at least one range is not valid
+ }
+ }
+ }
+
+ return true; // everything is fine
+}
+
+static bool lcl_MoveRanges( ::std::vector< ScRangeList >& rRangesVector, const ScRange& rSourceRange,
+ const ScAddress& rDestPos, const ScDocument& rDoc )
+{
+ bool bChanged = false;
+
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ for( ScRangeList& rRanges : rRangesVector )
+ {
+ for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ )
+ {
+ ScRange & rRange = rRanges[ i ];
+ if ( rSourceRange.Contains( rRange ) )
+ {
+ SCCOL nDiffX = rDestPos.Col() - rSourceRange.aStart.Col();
+ SCROW nDiffY = rDestPos.Row() - rSourceRange.aStart.Row();
+ SCTAB nDiffZ = rDestPos.Tab() - rSourceRange.aStart.Tab();
+ if (!rRange.Move( nDiffX, nDiffY, nDiffZ, aErrorRange, rDoc ))
+ {
+ assert(!"can't move range");
+ }
+ bChanged = true;
+ }
+ }
+ }
+
+ return bChanged;
+}
+
+void ScDrawLayer::CopyFromClip(ScDrawLayer* pClipModel, SCTAB nSourceTab,
+ const ScRange& rSourceRange, const ScAddress& rDestPos,
+ const ScRange& rDestRange, bool bTransposing)
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::CopyFromClip without document" );
+ if ( !pDoc )
+ return;
+
+ if (!pClipModel)
+ return;
+
+ if (bDrawIsInUndo) //TODO: can this happen?
+ {
+ OSL_FAIL("CopyFromClip, bDrawIsInUndo");
+ return;
+ }
+
+ SCTAB nDestTab = rDestPos.Tab();
+
+ SdrPage* pSrcPage = pClipModel->GetPage(static_cast<sal_uInt16>(nSourceTab));
+ SdrPage* pDestPage = GetPage(static_cast<sal_uInt16>(nDestTab));
+ OSL_ENSURE( pSrcPage && pDestPage, "draw page missing" );
+ if ( !pSrcPage || !pDestPage )
+ return;
+
+ ScDocument* pClipDoc = pClipModel->GetDocument();
+ if (!pClipDoc)
+ return; // Can this happen? And if yes, what to do?
+
+ SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat );
+ SdrObject* pOldObject = aIter.Next();
+ if (!pOldObject)
+ return; // no objects at all. Nothing to do.
+
+ // a clipboard document and its source share the same document item pool,
+ // so the pointers can be compared to see if this is copy&paste within
+ // the same document
+ bool bSameDoc = pDoc->GetPool() == pClipDoc->GetPool();
+ bool bDestClip = pDoc->IsClipboard(); // Happens while transposing. ToDo: Other cases?
+
+ //#i110034# charts need correct sheet names for xml range conversion during load
+ //so the target sheet name is temporarily renamed (if we have any SdrObjects)
+ OUString aDestTabName;
+ bool bRestoreDestTabName = false;
+ if (!bSameDoc && !bDestClip)
+ {
+ OUString aSourceTabName;
+ if (pClipDoc->GetName(nSourceTab, aSourceTabName) && pDoc->GetName(nDestTab, aDestTabName)
+ && aSourceTabName != aDestTabName && pDoc->ValidNewTabName(aSourceTabName))
+ {
+ bRestoreDestTabName = pDoc->RenameTab( nDestTab, aSourceTabName );
+ }
+ }
+
+ SCTAB nClipTab = bRestoreDestTabName ? nDestTab : nSourceTab;
+ ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nClipTab);
+
+ // We are going to make all rectangle calculations on LTR, so determine whether doc is RTL.
+ bool bSourceRTL = pClipDoc->IsLayoutRTL(nSourceTab);
+ bool bDestRTL = pDoc->IsLayoutRTL(nDestTab);
+
+ while (pOldObject)
+ {
+ // ToDO: Can this happen? Such objects should not be in the clipboard document.
+ // do not copy internal objects (detective) and note captions
+ if ((pOldObject->GetLayer() == SC_LAYER_INTERN) || IsNoteCaption(pOldObject))
+ {
+ pOldObject = aIter.Next();
+ continue;
+ }
+
+ // 'aIter' considers all objects on pSrcPage. But ScDocument::CopyBlockFromClip, which is used
+ // for filtered data, acts not on the total range but only on parts of it. So we need to look,
+ // whether an object is really contained in the current rSourceRange.
+ // For cell anchored objects we use the start address of the anchor, for page anchored objects
+ // we use the cell range behind the bounding box of the shape.
+ ScAddress aSrcObjStart;
+ const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject);
+ if (pObjData) // Object is anchored to cell.
+ {
+ aSrcObjStart = (*pObjData).maStart;
+ }
+ else // Object is anchored to page.
+ {
+ aSrcObjStart = pClipDoc->GetRange(nClipTab, pOldObject->GetCurrentBoundRect()).aStart;
+ }
+ if (!rSourceRange.Contains(aSrcObjStart))
+ {
+ pOldObject = aIter.Next();
+ continue;
+ }
+ // If object is anchored to a filtered cell, we will not copy it, because filtered rows are
+ // eliminated in paste. Copying would produce hidden objects which can only be accessed per
+ // macro.
+ if (pObjData && pClipDoc->RowFiltered((*pObjData).maStart.Row(), nSourceTab))
+ {
+ pOldObject = aIter.Next();
+ continue;
+ }
+
+ // Copy style sheet
+ auto pStyleSheet = pOldObject->GetStyleSheet();
+ if (pStyleSheet && !bSameDoc)
+ pDoc->GetStyleSheetPool()->CopyStyleFrom(pClipModel->GetStyleSheetPool(),
+ pStyleSheet->GetName(),
+ pStyleSheet->GetFamily(), true);
+
+ rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this));
+ tools::Rectangle aObjRect = pOldObject->GetSnapRect();
+ if (bSourceRTL)
+ {
+ MirrorRTL(pNewObject.get()); // We make the calculations in LTR.
+ MirrorRectRTL(aObjRect);
+ }
+
+ bool bCanResize = IsResizeWithCell(*pOldObject) && !pOldObject->IsResizeProtect();
+ // We do not resize charts or other OLE objects and do not resize when transposing.
+ bCanResize &= pOldObject->GetObjIdentifier() != SdrObjKind::OLE2;
+ bCanResize &= !bTransposing && !pClipDoc->GetClipParam().isTransposed();
+ if (bCanResize)
+ {
+ // Filtered rows are eliminated on paste. Filtered cols do not exist as of May 2023.
+ // Collapsed or hidden rows/cols are shown on paste.
+ // Idea: First calculate top left cell and bottom right cell of pasted object. Then
+ // calculate the object corners inside these cell and from that build new snap rectangle.
+ // We assume that pObjData is valid and pObjData and aObjRect correspond to each other
+ // in the source document.
+
+ // Start cell of object in source and destination. The case of a filtered start cell is
+ // already excluded above. aSrcObjStart = (*pObjData).maStart is already done above.
+ // If filtered rows exist in the total range, the range was divided into blocks which
+ // do not contain filtered rows. So the rows between start of aSourceRange and object
+ // start row do not contain filtered rows.
+ SCROW nStartRowDiff = aSrcObjStart.Row() - rSourceRange.aStart.Row();
+ SCCOL nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col();
+ ScAddress aDestObjStart = rDestRange.aStart;
+ aDestObjStart.IncCol(nStartColDiff);
+ aDestObjStart.IncRow(nStartRowDiff);
+
+ // End cell of object in source and destination. We look at the amount of rows/cols to be
+ // added to get object end cell from object start cell.
+ ScAddress aSrcObjEnd = (*pObjData).maEnd;
+ SCCOL nColsToAdd = aSrcObjEnd.Col() - aSrcObjStart.Col();
+ SCROW nRowsToAdd
+ = pClipDoc->CountNonFilteredRows(aSrcObjStart.Row(), aSrcObjEnd.Row(), nSourceTab)
+ - 1;
+ ScAddress aDestObjEnd = aDestObjStart;
+ aDestObjEnd.IncCol(nColsToAdd);
+ aDestObjEnd.IncRow(nRowsToAdd);
+
+ // Position of object inside start and end cell in source. We describe the distance from
+ // cell corner to object corner as ratio of offset to cell width/height.
+ // We cannot use GetCellRect method, because that uses bHiddenAsZero=true.
+ Point aSrcObjTopLeftOffset = (*pObjData).maStartOffset;
+ tools::Rectangle aSrcStartRect
+ = pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(),
+ aSrcObjStart.Row(), nSourceTab, false /*bHiddenAsZero*/);
+ if (bSourceRTL)
+ MirrorRectRTL(aSrcStartRect);
+ double fStartXRatio
+ = aSrcStartRect.getOpenWidth() == 0
+ ? 1.0
+ : double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth());
+ double fStartYRatio
+ = aSrcStartRect.getOpenHeight() == 0
+ ? 1.0
+ : double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight());
+
+ Point aSrcObjBottomRightOffset = (*pObjData).maEndOffset;
+ tools::Rectangle aSrcEndRect
+ = pClipDoc->GetMMRect(aSrcObjEnd.Col(), aSrcObjEnd.Row(), aSrcObjEnd.Col(),
+ aSrcObjEnd.Row(), nSourceTab, false /*bHiddenAsZero*/);
+ if (bSourceRTL)
+ MirrorRectRTL(aSrcEndRect);
+ double fEndXRatio
+ = aSrcEndRect.getOpenWidth() == 0
+ ? 1.0
+ : double(aSrcObjBottomRightOffset.X()) / double(aSrcEndRect.getOpenWidth());
+ double fEndYRatio
+ = aSrcEndRect.getOpenHeight() == 0
+ ? 1.0
+ : double(aSrcObjBottomRightOffset.Y()) / double(aSrcEndRect.getOpenHeight());
+ // The end cell given in pObjData might be filtered. In that case the object is cut at
+ // the lower cell edge. The offset is as large as the cell.
+ if (pClipDoc->RowFiltered(aSrcObjEnd.Row(), nSourceTab))
+ fEndYRatio = 1.0;
+
+ // Position of object inside start and end cell in destination
+ tools::Rectangle aDestStartRect
+ = GetCellRect(*pDoc, aDestObjStart, false /*bMergedCell*/);
+ if (bDestRTL)
+ MirrorRectRTL(aDestStartRect);
+ Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(),
+ fStartYRatio * aDestStartRect.getOpenHeight());
+ Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset;
+
+ tools::Rectangle aDestEndRect = GetCellRect(*pDoc, aDestObjEnd, false /*bMergedCell*/);
+ if (bDestRTL)
+ MirrorRectRTL(aDestEndRect);
+ Point aDestObjBottomRightOffset(fEndXRatio * aDestEndRect.getOpenWidth(),
+ fEndYRatio * aDestEndRect.getOpenHeight());
+ Point aDestObjBottomRight = aDestEndRect.TopLeft() + aDestObjBottomRightOffset;
+
+ // Fit new object into destination rectangle
+ tools::Rectangle aNewObjRect(aDestObjTopLeft, aDestObjBottomRight);
+ aNewObjRect = lcl_makeSafeRectangle(aNewObjRect);
+ if (pNewObject->GetObjIdentifier() == SdrObjKind::CustomShape)
+ pNewObject->AdjustToMaxRect(aNewObjRect);
+ else
+ pNewObject->SetSnapRect(aNewObjRect);
+ }
+ else
+ {
+ // We determine the MM-distance of the new object from its start cell in destination from
+ // the ratio of offset to cell width/height. Thus the object still starts in this cell
+ // even if the destination cell has different size. Otherwise we might lose objects when
+ // transposing.
+
+ // Start Cell address in source and destination
+ SCCOLROW nStartRowDiff = pClipDoc->CountNonFilteredRows(rSourceRange.aStart.Row(),
+ aSrcObjStart.Row(), nSourceTab)
+ - 1;
+ SCCOLROW nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col();
+ if (bTransposing)
+ std::swap(nStartRowDiff, nStartColDiff);
+ ScAddress aDestObjStart = rDestRange.aStart;
+ aDestObjStart.IncCol(nStartColDiff);
+ aDestObjStart.IncRow(nStartRowDiff);
+
+ // Position of object inside start cell in source.
+ tools::Rectangle aSrcStartRect
+ = pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(),
+ aSrcObjStart.Row(), nSourceTab, false /*bHiddenAsZero*/);
+ if (bSourceRTL)
+ MirrorRectRTL(aSrcStartRect);
+ Point aSrcObjTopLeftOffset = pObjData ? (*pObjData).maStartOffset
+ : aObjRect.TopLeft() - aSrcStartRect.TopLeft();
+
+ double fStartXRatio
+ = aSrcStartRect.getOpenWidth() == 0
+ ? 1.0
+ : double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth());
+ double fStartYRatio
+ = aSrcStartRect.getOpenHeight() == 0
+ ? 1.0
+ : double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight());
+
+ // Position of object inside start cell in destination
+ tools::Rectangle aDestStartRect
+ = GetCellRect(*pDoc, aDestObjStart, false /*bMergedCell*/);
+ if (bDestRTL)
+ MirrorRectRTL(aDestStartRect);
+ Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(),
+ fStartYRatio * aDestStartRect.getOpenHeight());
+ Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset;
+
+ // Move new object to new position
+ Point aMoveBy = aDestObjTopLeft - aObjRect.TopLeft();
+ pNewObject->NbcMove(Size(aMoveBy.getX(), aMoveBy.getY()));
+ }
+
+ if (bDestRTL)
+ MirrorRTL(pNewObject.get());
+
+ // Changing object position or size does not automatically change its anchor.
+ if (IsCellAnchored(*pOldObject))
+ SetCellAnchoredFromPosition(*pNewObject, *pDoc, nDestTab,
+ IsResizeWithCell(*pOldObject));
+
+ // InsertObject includes broadcasts
+ // MakeNameUnique makes the pasted objects accessible via Navigator.
+ if (bDestClip)
+ pDestPage->InsertObject(pNewObject.get());
+ else
+ {
+ if (bRecording)
+ pDoc->EnableUndo(false);
+ pDestPage->InsertObjectThenMakeNameUnique(pNewObject.get());
+ if (bRecording)
+ pDoc->EnableUndo(true);
+ }
+
+ if (bRecording)
+ AddCalcUndo(std::make_unique<SdrUndoInsertObj>(*pNewObject));
+
+ //#i110034# handle chart data references (after InsertObject)
+ if (pNewObject->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ uno::Reference<embed::XEmbeddedObject> xIPObj
+ = static_cast<SdrOle2Obj*>(pNewObject.get())->GetObjRef();
+ uno::Reference<embed::XClassifiedObject> xClassified = xIPObj;
+ SvGlobalName aObjectClassName;
+ if (xClassified.is())
+ {
+ try
+ {
+ aObjectClassName = SvGlobalName(xClassified->getClassID());
+ }
+ catch (uno::Exception&)
+ {
+ // TODO: handle error?
+ }
+ }
+
+ if (xIPObj.is() && SotExchange::IsChart(aObjectClassName))
+ {
+ uno::Reference<chart2::XChartDocument> xNewChart(
+ ScChartHelper::GetChartFromSdrObject(pNewObject.get()));
+ if (xNewChart.is() && !xNewChart->hasInternalDataProvider())
+ {
+ OUString aChartName
+ = static_cast<SdrOle2Obj*>(pNewObject.get())->GetPersistName();
+ ::std::vector<ScRangeList> aRangesVector;
+ pDoc->GetChartRanges(aChartName, aRangesVector, *pDoc);
+ if (!aRangesVector.empty())
+ {
+ bool bInSourceRange = false;
+ bInSourceRange = lcl_IsAllInRange(aRangesVector, aClipRange);
+
+ // always lose references when pasting into a clipboard document (transpose)
+ if ((bInSourceRange || bSameDoc) && !bDestClip)
+ {
+ if (bInSourceRange)
+ {
+ if (rDestPos != aClipRange.aStart)
+ {
+ // update the data ranges to the new (copied) position
+ if (lcl_MoveRanges(aRangesVector, aClipRange, rDestPos, *pDoc))
+ pDoc->SetChartRanges(aChartName, aRangesVector);
+ }
+ }
+ else
+ {
+ // leave the ranges unchanged
+ }
+ }
+ else
+ {
+ // pasting into a new document without the complete source data
+ // -> break connection to source data and switch to own data
+ uno::Reference<chart::XChartDocument> xOldChartDoc(
+ ScChartHelper::GetChartFromSdrObject(pOldObject), uno::UNO_QUERY);
+ uno::Reference<chart::XChartDocument> xNewChartDoc(xNewChart,
+ uno::UNO_QUERY);
+ if (xOldChartDoc.is() && xNewChartDoc.is())
+ xNewChartDoc->attachData(xOldChartDoc->getData());
+
+ // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc)
+ }
+ }
+ }
+ }
+ }
+ pOldObject = aIter.Next();
+ }
+
+ if( bRestoreDestTabName )
+ pDoc->RenameTab( nDestTab, aDestTabName );
+}
+
+void ScDrawLayer::MirrorRTL( SdrObject* pObj )
+{
+ OSL_ENSURE( pDoc, "ScDrawLayer::MirrorRTL - missing document" );
+ if( !pDoc )
+ return;
+
+ SdrObjKind nIdent = pObj->GetObjIdentifier();
+
+ // don't mirror OLE or graphics, otherwise ask the object
+ // if it can be mirrored
+ bool bCanMirror = ( nIdent != SdrObjKind::Graphic && nIdent != SdrObjKind::OLE2 );
+ if (bCanMirror)
+ {
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo( aInfo );
+ bCanMirror = aInfo.bMirror90Allowed;
+ }
+
+ if (bCanMirror)
+ {
+ ScDrawObjData* pData = GetObjData(pObj);
+ if (pData) // cell anchored
+ {
+ // Remember values from positive side.
+ const tools::Rectangle aOldSnapRect = pObj->GetSnapRect();
+ const tools::Rectangle aOldLogicRect = pObj->GetLogicRect();
+ // Generate noRotate anchor if missing.
+ ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj);
+ if (!pNoRotatedAnchor)
+ {
+ ScDrawObjData aNoRotateAnchor;
+ const tools::Rectangle aLogicRect(pObj->GetLogicRect());
+ GetCellAnchorFromPosition(aLogicRect, aNoRotateAnchor,
+ *pDoc, pData->maStart.Tab());
+ aNoRotateAnchor.mbResizeWithCell = pData->mbResizeWithCell;
+ SetNonRotatedAnchor(*pObj, aNoRotateAnchor);
+ pNoRotatedAnchor = GetNonRotatedObjData(pObj);
+ assert(pNoRotatedAnchor);
+ }
+ // Mirror object at vertical axis
+ Point aRef1( 0, 0 );
+ Point aRef2( 0, 1 );
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ pObj->Mirror( aRef1, aRef2 );
+
+ // Adapt offsets in pNoRotatedAnchor so, that object will be moved to current position in
+ // save and reload.
+ const tools::Long nInverseShift = aOldSnapRect.Left() + aOldSnapRect.Right();
+ const Point aLogicLT = pObj->GetLogicRect().TopLeft();
+ const Point aMirroredLogicLT = aLogicLT + Point(nInverseShift, 0);
+ const Point aOffsetDiff = aMirroredLogicLT - aOldLogicRect.TopLeft();
+ // new Offsets
+ pNoRotatedAnchor->maStartOffset += aOffsetDiff;
+ pNoRotatedAnchor->maEndOffset += aOffsetDiff;
+ }
+ else // page anchored
+ {
+ Point aRef1( 0, 0 );
+ Point aRef2( 0, 1 );
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ pObj->Mirror( aRef1, aRef2 );
+ }
+ }
+ else
+ {
+ // Move instead of mirroring:
+ // New start position is negative of old end position
+ // -> move by sum of start and end position
+ tools::Rectangle aObjRect = pObj->GetSnapRect();
+ Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 );
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) );
+ pObj->Move( aMoveSize );
+ }
+
+ // for cell anchored objects adapt rectangles in anchors
+ ScDrawObjData* pData = GetObjData(pObj);
+ if (pData)
+ {
+ pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible());
+ ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true /*bCreate*/);
+ pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible());
+ }
+}
+
+void ScDrawLayer::MoveRTL(SdrObject* pObj)
+{
+ tools::Rectangle aObjRect = pObj->GetSnapRect();
+ Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 );
+ if (bRecording)
+ AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) );
+ pObj->Move( aMoveSize );
+
+ // for cell anchored objects adapt rectangles in anchors
+ ScDrawObjData* pData = GetObjData(pObj);
+ if (pData)
+ {
+ pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible());
+ ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true /*bCreate*/);
+ pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible());
+ }
+}
+
+void ScDrawLayer::MirrorRectRTL( tools::Rectangle& rRect )
+{
+ // mirror and swap left/right
+ tools::Long nTemp = rRect.Left();
+ rRect.SetLeft( -rRect.Right() );
+ rRect.SetRight( -nTemp );
+}
+
+tools::Rectangle ScDrawLayer::GetCellRect( const ScDocument& rDoc, const ScAddress& rPos, bool bMergedCell )
+{
+ tools::Rectangle aCellRect;
+ OSL_ENSURE( rDoc.ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ), "ScDrawLayer::GetCellRect - invalid cell address" );
+ if( rDoc.ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ) )
+ {
+ // find top left position of passed cell address
+ Point aTopLeft;
+ for( SCCOL nCol = 0; nCol < rPos.Col(); ++nCol )
+ aTopLeft.AdjustX(rDoc.GetColWidth( nCol, rPos.Tab() ) );
+ if( rPos.Row() > 0 )
+ aTopLeft.AdjustY(rDoc.GetRowHeight( 0, rPos.Row() - 1, rPos.Tab() ) );
+
+ // find bottom-right position of passed cell address
+ ScAddress aEndPos = rPos;
+ if( bMergedCell )
+ {
+ const ScMergeAttr* pMerge = rDoc.GetAttr( rPos, ATTR_MERGE );
+ if( pMerge->GetColMerge() > 1 )
+ aEndPos.IncCol( pMerge->GetColMerge() - 1 );
+ if( pMerge->GetRowMerge() > 1 )
+ aEndPos.IncRow( pMerge->GetRowMerge() - 1 );
+ }
+ Point aBotRight = aTopLeft;
+ for( SCCOL nCol = rPos.Col(); nCol <= aEndPos.Col(); ++nCol )
+ aBotRight.AdjustX(rDoc.GetColWidth( nCol, rPos.Tab() ) );
+ aBotRight.AdjustY(rDoc.GetRowHeight( rPos.Row(), aEndPos.Row(), rPos.Tab() ) );
+
+ // twips -> 1/100 mm
+ aTopLeft = o3tl::convert(aTopLeft, o3tl::Length::twip, o3tl::Length::mm100);
+ aBotRight = o3tl::convert(aBotRight, o3tl::Length::twip, o3tl::Length::mm100);
+
+ aCellRect = tools::Rectangle( aTopLeft, aBotRight );
+ if( rDoc.IsNegativePage( rPos.Tab() ) )
+ MirrorRectRTL( aCellRect );
+ }
+ return aCellRect;
+}
+
+OUString ScDrawLayer::GetVisibleName( const SdrObject* pObj )
+{
+ OUString aName = pObj->GetName();
+ if ( pObj->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ // For OLE, the user defined name (GetName) is used
+ // if it's not empty (accepting possibly duplicate names),
+ // otherwise the persist name is used so every object appears
+ // in the Navigator at all.
+
+ if ( aName.isEmpty() )
+ aName = static_cast<const SdrOle2Obj*>(pObj)->GetPersistName();
+ }
+ return aName;
+}
+
+static bool IsNamedObject( const SdrObject* pObj, std::u16string_view rName )
+{
+ // sal_True if rName is the object's Name or PersistName
+ // (used to find a named object)
+
+ return ( pObj->GetName() == rName ||
+ ( pObj->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ static_cast<const SdrOle2Obj*>(pObj)->GetPersistName() == rName ) );
+}
+
+SdrObject* ScDrawLayer::GetNamedObject( std::u16string_view rName, SdrObjKind nId, SCTAB& rFoundTab ) const
+{
+ sal_uInt16 nTabCount = GetPageCount();
+ for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++)
+ {
+ const SdrPage* pPage = GetPage(nTab);
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( nId == SdrObjKind::NONE || pObject->GetObjIdentifier() == nId )
+ if ( IsNamedObject( pObject, rName ) )
+ {
+ rFoundTab = static_cast<SCTAB>(nTab);
+ return pObject;
+ }
+
+ pObject = aIter.Next();
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+OUString ScDrawLayer::GetNewGraphicName( tools::Long* pnCounter ) const
+{
+ OUString aBase = ScResId(STR_GRAPHICNAME) + " ";
+
+ bool bThere = true;
+ OUString aGraphicName;
+ SCTAB nDummy;
+ tools::Long nId = pnCounter ? *pnCounter : 0;
+ while (bThere)
+ {
+ ++nId;
+ aGraphicName = aBase + OUString::number( nId );
+ bThere = ( GetNamedObject( aGraphicName, SdrObjKind::NONE, nDummy ) != nullptr );
+ }
+
+ if ( pnCounter )
+ *pnCounter = nId;
+
+ return aGraphicName;
+}
+
+void ScDrawLayer::EnsureGraphicNames()
+{
+ // make sure all graphic objects have names (after Excel import etc.)
+
+ sal_uInt16 nTabCount = GetPageCount();
+ for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++)
+ {
+ SdrPage* pPage = GetPage(nTab);
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+
+ /* The index passed to GetNewGraphicName() will be set to
+ the used index in each call. This prevents the repeated search
+ for all names from 1 to current index. */
+ tools::Long nCounter = 0;
+
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::Graphic && pObject->GetName().isEmpty())
+ pObject->SetName( GetNewGraphicName( &nCounter ) );
+
+ pObject = aIter.Next();
+ }
+ }
+ }
+}
+
+namespace
+{
+ SdrObjUserData* GetFirstUserDataOfType(const SdrObject *pObj, sal_uInt16 nId)
+ {
+ sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0;
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ SdrObjUserData* pData = pObj->GetUserData( i );
+ if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == nId )
+ return pData;
+ }
+ return nullptr;
+ }
+
+ void DeleteFirstUserDataOfType(SdrObject *pObj, sal_uInt16 nId)
+ {
+ sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0;
+ for( sal_uInt16 i = nCount; i > 0; i-- )
+ {
+ SdrObjUserData* pData = pObj->GetUserData( i-1 );
+ if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == nId )
+ pObj->DeleteUserData(i-1);
+ }
+ }
+}
+
+void ScDrawLayer::SetNonRotatedAnchor(SdrObject& rObj, const ScDrawObjData& rAnchor)
+{
+ ScDrawObjData* pAnchor = GetNonRotatedObjData( &rObj, true );
+ pAnchor->maStart = rAnchor.maStart;
+ pAnchor->maEnd = rAnchor.maEnd;
+ pAnchor->maStartOffset = rAnchor.maStartOffset;
+ pAnchor->maEndOffset = rAnchor.maEndOffset;
+ pAnchor->mbResizeWithCell = rAnchor.mbResizeWithCell;
+}
+
+void ScDrawLayer::SetCellAnchored( SdrObject &rObj, const ScDrawObjData &rAnchor )
+{
+ ScDrawObjData* pAnchor = GetObjData( &rObj, true );
+ pAnchor->maStart = rAnchor.maStart;
+ pAnchor->maEnd = rAnchor.maEnd;
+ pAnchor->maStartOffset = rAnchor.maStartOffset;
+ pAnchor->maEndOffset = rAnchor.maEndOffset;
+ pAnchor->mbResizeWithCell = rAnchor.mbResizeWithCell;
+}
+
+void ScDrawLayer::SetCellAnchoredFromPosition( SdrObject &rObj, const ScDocument &rDoc, SCTAB nTab,
+ bool bResizeWithCell )
+{
+ if (!rObj.IsVisible())
+ return;
+ ScDrawObjData aAnchor;
+ // set anchor in terms of the visual ( SnapRect )
+ // object ( e.g. for when object is rotated )
+ const tools::Rectangle aObjRect(rObj.GetSnapRect());
+ GetCellAnchorFromPosition(
+ aObjRect,
+ aAnchor,
+ rDoc,
+ nTab);
+
+ aAnchor.mbResizeWithCell = bResizeWithCell;
+ SetCellAnchored( rObj, aAnchor );
+
+ // absolutely necessary to set flag, ScDrawLayer::RecalcPos expects it.
+ if ( ScDrawObjData* pAnchor = GetObjData( &rObj ) )
+ {
+ pAnchor->setShapeRect(&rDoc, rObj.GetSnapRect());
+ }
+
+ // - keep also an anchor in terms of the Logic ( untransformed ) object
+ // because that's what we stored ( and still do ) to xml
+
+ // Vertical flipped custom shapes need special treatment, see comment in
+ // lcl_SetLogicRectFromAnchor
+ tools::Rectangle aObjRect2;
+ if (lcl_NeedsMirrorYCorrection(&rObj))
+ {
+ const tools::Rectangle aRect(rObj.GetSnapRect());
+ const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1);
+ const Point aRight(aLeft.X() + 1000, aLeft.Y());
+ rObj.NbcMirror(aLeft, aRight);
+ aObjRect2 = rObj.GetLogicRect();
+ rObj.NbcMirror(aLeft, aRight);
+ }
+ else if (rObj.GetObjIdentifier() == SdrObjKind::Measure)
+ {
+ // tdf#137576. A SdrMeasureObj might have a wrong logic rect here. TakeUnrotatedSnapRect
+ // calculates the current unrotated snap rectangle, sets logic rectangle and returns it.
+ static_cast<SdrMeasureObj&>(rObj).TakeUnrotatedSnapRect(aObjRect2);
+ }
+ else
+ aObjRect2 = rObj.GetLogicRect();
+
+ // Values in XML are so as if it is a LTR sheet. The object is shifted to negative page on loading
+ // so that the snap rectangle appears mirrored. For transformed objects the shifted logic rectangle
+ // is not the mirrored LTR rectangle. We calculate the mirrored LTR rectangle here.
+ if (rDoc.IsNegativePage(nTab))
+ {
+ const tools::Rectangle aSnapRect(rObj.GetSnapRect());
+ aObjRect2.Move(Size(-aSnapRect.Left() - aSnapRect.Right(), 0));
+ MirrorRectRTL(aObjRect2);
+ }
+
+ ScDrawObjData aNoRotatedAnchor;
+ GetCellAnchorFromPosition(
+ aObjRect2,
+ aNoRotatedAnchor,
+ rDoc,
+ nTab);
+
+ aNoRotatedAnchor.mbResizeWithCell = bResizeWithCell;
+ SetNonRotatedAnchor( rObj, aNoRotatedAnchor);
+ // And update maShapeRect. It is used in adjustAnchoredPosition() in ScDrawView::Notify().
+ if (ScDrawObjData* pAnchor = GetNonRotatedObjData(&rObj))
+ {
+ pAnchor->setShapeRect(&rDoc, rObj.GetLogicRect());
+ }
+}
+
+void ScDrawLayer::GetCellAnchorFromPosition(
+ const tools::Rectangle &rObjRect,
+ ScDrawObjData &rAnchor,
+ const ScDocument &rDoc,
+ SCTAB nTab,
+ bool bHiddenAsZero)
+{
+ ScRange aRange = rDoc.GetRange( nTab, rObjRect, bHiddenAsZero );
+
+ tools::Rectangle aCellRect;
+
+ rAnchor.maStart = aRange.aStart;
+ aCellRect = rDoc.GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Col(), aRange.aStart.Row(), aRange.aStart.Tab(), bHiddenAsZero );
+ rAnchor.maStartOffset.setY( rObjRect.Top()-aCellRect.Top() );
+ if (!rDoc.IsNegativePage(nTab))
+ rAnchor.maStartOffset.setX( rObjRect.Left()-aCellRect.Left() );
+ else
+ rAnchor.maStartOffset.setX( aCellRect.Right()-rObjRect.Right() );
+
+ rAnchor.maEnd = aRange.aEnd;
+ aCellRect = rDoc.GetMMRect( aRange.aEnd.Col(), aRange.aEnd.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab(), bHiddenAsZero );
+ if (!rObjRect.IsEmpty())
+ rAnchor.maEndOffset.setY( rObjRect.Bottom()-aCellRect.Top() );
+ if (!rDoc.IsNegativePage(nTab))
+ {
+ if (!rObjRect.IsEmpty())
+ rAnchor.maEndOffset.setX( rObjRect.Right()-aCellRect.Left() );
+ }
+ else
+ rAnchor.maEndOffset.setX( aCellRect.Right()-rObjRect.Left() );
+}
+
+void ScDrawLayer::UpdateCellAnchorFromPositionEnd( const SdrObject &rObj, ScDrawObjData &rAnchor, const ScDocument &rDoc, SCTAB nTab, bool bUseLogicRect )
+{
+ tools::Rectangle aObjRect(bUseLogicRect ? rObj.GetLogicRect() : rObj.GetSnapRect());
+ ScRange aRange = rDoc.GetRange( nTab, aObjRect );
+
+ ScDrawObjData* pAnchor = &rAnchor;
+ pAnchor->maEnd = aRange.aEnd;
+
+ tools::Rectangle aCellRect = rDoc.GetMMRect( aRange.aEnd.Col(), aRange.aEnd.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab() );
+ pAnchor->maEndOffset.setY( aObjRect.Bottom()-aCellRect.Top() );
+ if (!rDoc.IsNegativePage(nTab))
+ pAnchor->maEndOffset.setX( aObjRect.Right()-aCellRect.Left() );
+ else
+ pAnchor->maEndOffset.setX( aCellRect.Right()-aObjRect.Left() );
+}
+
+bool ScDrawLayer::IsCellAnchored( const SdrObject& rObj )
+{
+ // Cell anchored object always has a user data, to store the anchor cell
+ // info. If it doesn't then it's page-anchored.
+ return GetFirstUserDataOfType(&rObj, SC_UD_OBJDATA) != nullptr;
+}
+
+bool ScDrawLayer::IsResizeWithCell( const SdrObject& rObj )
+{
+ // Cell anchored object always has a user data, to store the anchor cell
+ // info. If it doesn't then it's page-anchored.
+ ScDrawObjData* pDrawObjData = GetObjData(const_cast<SdrObject*>(&rObj));
+ if (!pDrawObjData)
+ return false;
+
+ return pDrawObjData->mbResizeWithCell;
+}
+
+void ScDrawLayer::SetPageAnchored( SdrObject &rObj )
+{
+ DeleteFirstUserDataOfType(&rObj, SC_UD_OBJDATA);
+ DeleteFirstUserDataOfType(&rObj, SC_UD_OBJDATA);
+}
+
+ScAnchorType ScDrawLayer::GetAnchorType( const SdrObject &rObj )
+{
+ //If this object has a cell anchor associated with it
+ //then it's cell-anchored, otherwise it's page-anchored
+ const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(const_cast<SdrObject*>(&rObj));
+
+ // When there is no cell anchor, it is page anchored.
+ if (!pObjData)
+ return SCA_PAGE;
+
+ // It's cell-anchored, check if the object resizes with the cell
+ if (pObjData->mbResizeWithCell)
+ return SCA_CELL_RESIZE;
+
+ return SCA_CELL;
+}
+
+std::vector<SdrObject*>
+ScDrawLayer::GetObjectsAnchoredToRows(SCTAB nTab, SCROW nStartRow, SCROW nEndRow)
+{
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ if (!pPage || pPage->GetObjCount() < 1)
+ return std::vector<SdrObject*>();
+
+ std::vector<SdrObject*> aObjects;
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ ScRange aRange( 0, nStartRow, nTab, pDoc->MaxCol(), nEndRow, nTab);
+ while (pObject)
+ {
+ ScDrawObjData* pObjData = GetObjData(pObject);
+ if (pObjData && aRange.Contains(pObjData->maStart))
+ aObjects.push_back(pObject);
+ pObject = aIter.Next();
+ }
+ return aObjects;
+}
+
+std::map<SCROW, std::vector<SdrObject*>>
+ScDrawLayer::GetObjectsAnchoredToRange(SCTAB nTab, SCCOL nCol, SCROW nStartRow, SCROW nEndRow)
+{
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ if (!pPage || pPage->GetObjCount() < 1)
+ return std::map<SCROW, std::vector<SdrObject*>>();
+
+ std::map<SCROW, std::vector<SdrObject*>> aRowObjects;
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ ScRange aRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab);
+ while (pObject)
+ {
+ if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently
+ {
+ ScDrawObjData* pObjData = GetObjData(pObject);
+ if (pObjData && aRange.Contains(pObjData->maStart))
+ aRowObjects[pObjData->maStart.Row()].push_back(pObject);
+ }
+ pObject = aIter.Next();
+ }
+ return aRowObjects;
+}
+
+bool ScDrawLayer::HasObjectsAnchoredInRange(const ScRange& rRange)
+{
+ // This only works for one table at a time
+ assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
+
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(rRange.aStart.Tab()));
+ if (!pPage || pPage->GetObjCount() < 1)
+ return false;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently
+ {
+ ScDrawObjData* pObjData = GetObjData(pObject);
+ if (pObjData && rRange.Contains(pObjData->maStart)) // Object is in given range
+ return true;
+ }
+ pObject = aIter.Next();
+ }
+ return false;
+}
+
+std::vector<SdrObject*> ScDrawLayer::GetObjectsAnchoredToCols(SCTAB nTab, SCCOL nStartCol,
+ SCCOL nEndCol)
+{
+ SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
+ if (!pPage || pPage->GetObjCount() < 1)
+ return std::vector<SdrObject*>();
+
+ std::vector<SdrObject*> aObjects;
+ SdrObjListIter aIter(pPage, SdrIterMode::Flat);
+ SdrObject* pObject = aIter.Next();
+ ScRange aRange(nStartCol, 0, nTab, nEndCol, pDoc->MaxRow(), nTab);
+ while (pObject)
+ {
+ ScDrawObjData* pObjData = GetObjData(pObject);
+ if (pObjData && aRange.Contains(pObjData->maStart))
+ aObjects.push_back(pObject);
+ pObject = aIter.Next();
+ }
+ return aObjects;
+}
+
+void ScDrawLayer::MoveObject(SdrObject* pObject, const ScAddress& rNewPosition)
+{
+ // Get anchor data
+ ScDrawObjData* pObjData = GetObjData(pObject, false);
+ if (!pObjData)
+ return;
+ const ScAddress aOldStart = pObjData->maStart;
+ const ScAddress aOldEnd = pObjData->maEnd;
+
+ // Set start address
+ pObjData->maStart = rNewPosition;
+
+ // Set end address
+ const SCCOL nObjectColSpan = aOldEnd.Col() - aOldStart.Col();
+ const SCROW nObjectRowSpan = aOldEnd.Row() - aOldStart.Row();
+ ScAddress aNewEnd = rNewPosition;
+ aNewEnd.IncRow(nObjectRowSpan);
+ aNewEnd.IncCol(nObjectColSpan);
+ pObjData->maEnd = aNewEnd;
+
+ // Update draw object according to new anchor
+ RecalcPos(pObject, *pObjData, false, false);
+}
+
+ScDrawObjData* ScDrawLayer::GetNonRotatedObjData( SdrObject* pObj, bool bCreate )
+{
+ sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0;
+ sal_uInt16 nFound = 0;
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ SdrObjUserData* pData = pObj->GetUserData( i );
+ if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == SC_UD_OBJDATA && ++nFound == 2 )
+ return static_cast<ScDrawObjData*>(pData);
+ }
+ if( pObj && bCreate )
+ {
+ ScDrawObjData* pData = new ScDrawObjData;
+ pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
+ return pData;
+ }
+ return nullptr;
+}
+
+ScDrawObjData* ScDrawLayer::GetObjData( SdrObject* pObj, bool bCreate )
+{
+ if (SdrObjUserData *pData = GetFirstUserDataOfType(pObj, SC_UD_OBJDATA))
+ return static_cast<ScDrawObjData*>(pData);
+
+ if( pObj && bCreate )
+ {
+ ScDrawObjData* pData = new ScDrawObjData;
+ pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
+ return pData;
+ }
+ return nullptr;
+}
+
+ScDrawObjData* ScDrawLayer::GetObjDataTab( SdrObject* pObj, SCTAB nTab )
+{
+ ScDrawObjData* pData = GetObjData( pObj );
+ if ( pData )
+ {
+ if ( pData->maStart.IsValid() )
+ pData->maStart.SetTab( nTab );
+ if ( pData->maEnd.IsValid() )
+ pData->maEnd.SetTab( nTab );
+ }
+ return pData;
+}
+
+bool ScDrawLayer::IsNoteCaption( SdrObject* pObj )
+{
+ ScDrawObjData* pData = pObj ? GetObjData( pObj ) : nullptr;
+ return pData && pData->meType == ScDrawObjData::CellNote;
+}
+
+ScDrawObjData* ScDrawLayer::GetNoteCaptionData( SdrObject* pObj, SCTAB nTab )
+{
+ ScDrawObjData* pData = pObj ? GetObjDataTab( pObj, nTab ) : nullptr;
+ return (pData && pData->meType == ScDrawObjData::CellNote) ? pData : nullptr;
+}
+
+ScMacroInfo* ScDrawLayer::GetMacroInfo( SdrObject* pObj, bool bCreate )
+{
+ if (SdrObjUserData *pData = GetFirstUserDataOfType(pObj, SC_UD_MACRODATA))
+ return static_cast<ScMacroInfo*>(pData);
+
+ if ( bCreate )
+ {
+ ScMacroInfo* pData = new ScMacroInfo;
+ pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
+ return pData;
+ }
+ return nullptr;
+}
+
+void ScDrawLayer::SetGlobalDrawPersist(SfxObjectShell* pPersist)
+{
+ OSL_ENSURE(!pGlobalDrawPersist,"Multiple SetGlobalDrawPersist");
+ pGlobalDrawPersist = pPersist;
+}
+
+void ScDrawLayer::SetChanged( bool bFlg /* = true */ )
+{
+ if ( bFlg && pDoc )
+ pDoc->SetChartListenerCollectionNeedsUpdate( true );
+ FmFormModel::SetChanged( bFlg );
+}
+
+css::uno::Reference< css::frame::XModel > ScDrawLayer::createUnoModel()
+{
+ css::uno::Reference< css::frame::XModel > xRet;
+ if( pDoc && pDoc->GetDocumentShell() )
+ xRet = pDoc->GetDocumentShell()->GetModel();
+
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/edittextiterator.cxx b/sc/source/core/data/edittextiterator.cxx
new file mode 100644
index 0000000000..bdf7c99341
--- /dev/null
+++ b/sc/source/core/data/edittextiterator.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/.
+ */
+
+#include <edittextiterator.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <column.hxx>
+
+namespace sc {
+
+EditTextIterator::EditTextIterator( const ScDocument& rDoc, SCTAB nTab ) :
+ mrTable(*rDoc.maTabs.at(nTab)),
+ mnCol(0),
+ mpCells(nullptr),
+ miEnd(maPos.first)
+{
+ init();
+}
+
+void EditTextIterator::init()
+{
+ mnCol = 0;
+ if (mnCol >= mrTable.aCol.size())
+ mnCol = -1;
+
+ if (mnCol != -1)
+ {
+ mpCells = &mrTable.aCol[mnCol].maCells;
+ maPos = mpCells->position(0);
+ miEnd = mpCells->end();
+ }
+}
+
+const EditTextObject* EditTextIterator::seek()
+{
+ while (maPos.first->type != sc::element_type_edittext)
+ {
+ incBlock();
+ if (maPos.first == miEnd)
+ {
+ // Move to the next column.
+ ++mnCol;
+ if (mnCol >= mrTable.aCol.size())
+ // No more columns.
+ return nullptr;
+
+ mpCells = &mrTable.aCol[mnCol].maCells;
+ maPos = mpCells->position(0);
+ miEnd = mpCells->end();
+ }
+ }
+
+ // We are on the right block type.
+ return sc::edittext_block::at(*maPos.first->data, maPos.second);
+}
+
+void EditTextIterator::incBlock()
+{
+ ++maPos.first;
+ maPos.second = 0;
+}
+
+const EditTextObject* EditTextIterator::first()
+{
+ init();
+ if (mnCol == -1)
+ return nullptr;
+ return seek();
+}
+
+const EditTextObject* EditTextIterator::next()
+{
+ if (mnCol == -1)
+ return nullptr;
+
+ if (maPos.first == miEnd)
+ return nullptr;
+
+ // increment position by one
+ if (maPos.second + 1 < maPos.first->size)
+ // Increment within the block.
+ ++maPos.second;
+ else
+ incBlock();
+
+ return seek();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/fillinfo.cxx b/sc/source/core/data/fillinfo.cxx
new file mode 100644
index 0000000000..4a573e1c21
--- /dev/null
+++ b/sc/source/core/data/fillinfo.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 <scitems.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svx/framelink.hxx>
+#include <osl/diagnose.h>
+
+#include <fillinfo.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <attrib.hxx>
+#include <attarray.hxx>
+#include <markarr.hxx>
+#include <markdata.hxx>
+#include <patattr.hxx>
+#include <poolhelp.hxx>
+#include <docpool.hxx>
+#include <conditio.hxx>
+#include <stlpool.hxx>
+#include <cellvalue.hxx>
+#include <mtvcellfunc.hxx>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+#include <memory>
+
+// Similar as in output.cxx
+
+static void lcl_GetMergeRange( SCCOL nX, SCROW nY, SCSIZE nArrY,
+ const ScDocument* pDoc, RowInfo* pRowInfo,
+ SCCOL nX1, SCROW nY1, SCTAB nTab,
+ SCCOL& rStartX, SCROW& rStartY, SCCOL& rEndX, SCROW& rEndY )
+{
+ ScCellInfo* pInfo = &pRowInfo[nArrY].cellInfo(nX);
+
+ rStartX = nX;
+ rStartY = nY;
+ bool bHOver = pInfo->bHOverlapped;
+ bool bVOver = pInfo->bVOverlapped;
+ SCCOL nLastCol;
+ SCROW nLastRow;
+
+ while (bHOver) // nY constant
+ {
+ --rStartX;
+ if (rStartX >= nX1 && !pDoc->ColHidden(rStartX, nTab, nullptr, &nLastCol))
+ {
+ bHOver = pRowInfo[nArrY].cellInfo(rStartX).bHOverlapped;
+ bVOver = pRowInfo[nArrY].cellInfo(rStartX).bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap = pDoc->GetAttr( rStartX, rStartY, nTab, ATTR_MERGE_FLAG )->GetValue();
+ bHOver = bool(nOverlap & ScMF::Hor);
+ bVOver = bool(nOverlap & ScMF::Ver);
+ }
+ }
+
+ while (bVOver)
+ {
+ --rStartY;
+
+ if (nArrY>0)
+ --nArrY; // local copy !
+
+ if (rStartX >= nX1 && rStartY >= nY1 &&
+ !pDoc->ColHidden(rStartX, nTab, nullptr, &nLastCol) &&
+ !pDoc->RowHidden(rStartY, nTab, nullptr, &nLastRow) &&
+ pRowInfo[nArrY].nRowNo == rStartY)
+ {
+ bVOver = pRowInfo[nArrY].cellInfo(rStartX).bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap = pDoc->GetAttr(
+ rStartX, rStartY, nTab, ATTR_MERGE_FLAG )->GetValue();
+ bVOver = bool(nOverlap & ScMF::Ver);
+ }
+ }
+
+ const ScMergeAttr* pMerge;
+ if (rStartX >= nX1 && rStartY >= nY1 &&
+ !pDoc->ColHidden(rStartX, nTab, nullptr, &nLastCol) &&
+ !pDoc->RowHidden(rStartY, nTab, nullptr, &nLastRow) &&
+ pRowInfo[nArrY].nRowNo == rStartY)
+ {
+ pMerge = &pRowInfo[nArrY].cellInfo(rStartX).pPatternAttr->
+ GetItem(ATTR_MERGE);
+ }
+ else
+ pMerge = pDoc->GetAttr(rStartX,rStartY,nTab,ATTR_MERGE);
+
+ rEndX = rStartX + pMerge->GetColMerge() - 1;
+ rEndY = rStartY + pMerge->GetRowMerge() - 1;
+}
+
+namespace {
+
+class RowInfoFiller
+{
+ ScDocument& mrDoc;
+ SCTAB mnTab;
+ RowInfo* mpRowInfo;
+ SCCOL mnCol;
+ SCSIZE mnArrY;
+ SCCOL mnStartCol;
+ SCROW mnHiddenEndRow;
+ bool mbHiddenRow;
+
+ bool isHidden(size_t nRow)
+ {
+ SCROW nThisRow = static_cast<SCROW>(nRow);
+ if (nThisRow > mnHiddenEndRow)
+ mbHiddenRow = mrDoc.RowHidden(nThisRow, mnTab, nullptr, &mnHiddenEndRow);
+ return mbHiddenRow;
+ }
+
+ void alignArray(size_t nRow)
+ {
+ while (mpRowInfo[mnArrY].nRowNo < static_cast<SCROW>(nRow))
+ ++mnArrY;
+ }
+
+ void setInfo(size_t nRow, const ScRefCellValue& rCell)
+ {
+ alignArray(nRow);
+
+ RowInfo& rThisRowInfo = mpRowInfo[mnArrY];
+ if(mnCol >= mnStartCol-1)
+ rThisRowInfo.cellInfo(mnCol).maCell = rCell;
+ rThisRowInfo.basicCellInfo(mnCol).bEmptyCellText = false;
+ ++mnArrY;
+ }
+
+public:
+ RowInfoFiller(ScDocument& rDoc, SCTAB nTab, RowInfo* pRowInfo, SCCOL nCol, SCSIZE nArrY, SCCOL nStartCol) :
+ mrDoc(rDoc), mnTab(nTab), mpRowInfo(pRowInfo), mnCol(nCol), mnArrY(nArrY), mnStartCol(nStartCol),
+ mnHiddenEndRow(-1), mbHiddenRow(false) {}
+
+ void operator() (size_t nRow, double fVal)
+ {
+ if (!isHidden(nRow))
+ setInfo(nRow, ScRefCellValue(fVal));
+ }
+
+ void operator() (size_t nRow, const svl::SharedString& rStr)
+ {
+ if (!isHidden(nRow))
+ setInfo(nRow, ScRefCellValue(&rStr));
+ }
+
+ void operator() (size_t nRow, const EditTextObject* p)
+ {
+ if (!isHidden(nRow))
+ setInfo(nRow, ScRefCellValue(p));
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ if (!isHidden(nRow))
+ setInfo(nRow, ScRefCellValue(const_cast<ScFormulaCell*>(p)));
+ }
+};
+
+bool isRotateItemUsed(const ScDocumentPool *pPool)
+{
+ return pPool->GetItemSurrogates(ATTR_ROTATE_VALUE).size() > 0;
+}
+
+void initRowInfo(const ScDocument* pDoc, RowInfo* pRowInfo, const SCSIZE nMaxRow,
+ double fRowScale, SCROW nRow1, SCTAB nTab, SCROW& rYExtra, SCSIZE& rArrRow, SCROW& rRow2)
+{
+ sal_uInt16 nDocHeight = pDoc->GetSheetOptimalMinRowHeight(nTab);
+ SCROW nDocHeightEndRow = -1;
+ for (SCROW nSignedY=nRow1-1; nSignedY<=rYExtra; nSignedY++)
+ {
+ SCROW nY;
+ if (nSignedY >= 0)
+ nY = nSignedY;
+ else
+ nY = pDoc->MaxRow()+1; // invalid
+
+ if (nY > nDocHeightEndRow)
+ {
+ if (pDoc->ValidRow(nY))
+ nDocHeight = pDoc->GetRowHeight( nY, nTab, nullptr, &nDocHeightEndRow );
+ else
+ nDocHeight = pDoc->GetSheetOptimalMinRowHeight(nTab);
+ }
+
+ if ( rArrRow==0 || nDocHeight || nY > pDoc->MaxRow() )
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[rArrRow];
+ // pThisRowInfo->pCellInfo is set below using allocCellInfo()
+
+ sal_uInt16 nHeight = static_cast<sal_uInt16>(
+ std::clamp(
+ nDocHeight * fRowScale, 1.0, double(std::numeric_limits<sal_uInt16>::max())));
+
+ pThisRowInfo->nRowNo = nY; //TODO: case < 0 ?
+ pThisRowInfo->nHeight = nHeight;
+ pThisRowInfo->bEmptyBack = true;
+ pThisRowInfo->bChanged = true;
+ pThisRowInfo->bAutoFilter = false;
+ pThisRowInfo->bPivotButton = false;
+ pThisRowInfo->bPivotToggle = false;
+ pThisRowInfo->nRotMaxCol = SC_ROTMAX_NONE;
+
+ ++rArrRow;
+ if (rArrRow >= nMaxRow)
+ {
+ OSL_FAIL("FillInfo: Range too big" );
+ rYExtra = nSignedY; // End
+ rRow2 = rYExtra - 1; // Adjust range
+ }
+ }
+ else
+ if (nSignedY == rYExtra) // hidden additional line?
+ ++rYExtra;
+ }
+}
+
+void initCellInfo(RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nStartCol, SCCOL nRotMax,
+ const SvxShadowItem* pDefShadow)
+{
+ for (SCSIZE nArrRow = 0; nArrRow < nArrCount; ++nArrRow)
+ {
+ RowInfo& rThisRowInfo = pRowInfo[nArrRow];
+ // A lot of memory (and performance allocating and initializing it) can
+ // be saved if we do not allocate CellInfo for columns before nStartCol.
+ // But code in ScOutputData::SetCellRotation(), ScOutputData::DrawRotatedFrame()
+ // and ScOutputData::SetCellRotations() accesses those. That depends on
+ // nRotMaxCol being set to something else than none, and the value is already
+ // initialized here. So allocate all those cells starting from column 0 only if needed.
+ SCCOL nMinCol = rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE ? 0 : nStartCol;
+ rThisRowInfo.allocCellInfo( nMinCol, nRotMax + 1 );
+
+ for (SCCOL nCol = nMinCol-1; nCol <= nRotMax+1; ++nCol) // Preassign cell info
+ {
+ ScCellInfo& rInfo = rThisRowInfo.cellInfo(nCol);
+ rInfo.pShadowAttr = pDefShadow;
+ }
+ }
+}
+
+void initColWidths(RowInfo* pRowInfo, const ScDocument* pDoc, double fColScale, SCTAB nTab, SCCOL nCol2, SCCOL nRotMax)
+{
+ for (SCCOL nCol=nCol2+2; nCol<=nRotMax+1; nCol++) // Add remaining widths
+ {
+ if ( pDoc->ValidCol(nCol) )
+ {
+ if (!pDoc->ColHidden(nCol, nTab))
+ {
+ sal_uInt16 nThisWidth = static_cast<sal_uInt16>(pDoc->GetColWidth( nCol, nTab ) * fColScale);
+ if (!nThisWidth)
+ nThisWidth = 1;
+
+ pRowInfo[0].basicCellInfo(nCol).nWidth = nThisWidth;
+ }
+ }
+ }
+}
+
+bool handleConditionalFormat(ScConditionalFormatList& rCondFormList, const ScCondFormatIndexes& rCondFormats,
+ ScCellInfo* pInfo, ScTableInfo* pTableInfo, ScStyleSheetPool* pStlPool,
+ const ScAddress& rAddr, bool& bHidden, bool& bHideFormula, bool bTabProtect)
+{
+ bool bAnyCondition = false;
+ for(const auto& rCondFormat : rCondFormats)
+ {
+ ScConditionalFormat* pCondForm = rCondFormList.GetFormat(rCondFormat);
+ if(!pCondForm)
+ continue;
+
+ ScCondFormatData aData = pCondForm->GetData(
+ pInfo->maCell, rAddr);
+ if (!aData.aStyleName.isEmpty())
+ {
+ SfxStyleSheetBase* pStyleSheet =
+ pStlPool->Find( aData.aStyleName, SfxStyleFamily::Para );
+ if ( pStyleSheet )
+ {
+ //TODO: cache Style-Sets !!!
+ pInfo->pConditionSet = &pStyleSheet->GetItemSet();
+ bAnyCondition = true;
+
+ // TODO: moggi: looks like there is a bug around bHidden and bHideFormula
+ // They are normally for the whole pattern and not for a single cell
+ // we need to check already here for protected cells
+ const ScProtectionAttr* pProtAttr;
+ if ( bTabProtect && (pProtAttr = pInfo->pConditionSet->GetItemIfSet( ATTR_PROTECTION )) )
+ {
+ bHidden = pProtAttr->GetHideCell();
+ bHideFormula = pProtAttr->GetHideFormula();
+
+ }
+ }
+ // if style is not there, treat like no condition
+ }
+
+ if(aData.mxColorScale && !pInfo->mxColorScale)
+ {
+ pInfo->mxColorScale = aData.mxColorScale;
+ }
+
+ if(aData.pDataBar && !pInfo->pDataBar)
+ {
+ pInfo->pDataBar = aData.pDataBar.get();
+ pTableInfo->addDataBarInfo(std::move(aData.pDataBar));
+ }
+
+ if(aData.pIconSet && !pInfo->pIconSet)
+ {
+ pInfo->pIconSet = aData.pIconSet.get();
+ pTableInfo->addIconSetInfo(std::move(aData.pIconSet));
+ }
+
+ if (pInfo->mxColorScale && pInfo->pIconSet && pInfo->pDataBar)
+ break;
+ }
+
+ return bAnyCondition;
+}
+
+}
+
+void ScDocument::FillInfo(
+ ScTableInfo& rTabInfo, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ SCTAB nTab, double fColScale, double fRowScale, bool bPageMode, bool bFormulaMode,
+ const ScMarkData* pMarkData )
+{
+ OSL_ENSURE( maTabs[nTab], "Table does not exist" );
+
+ bool bLayoutRTL = IsLayoutRTL( nTab );
+
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+ ScStyleSheetPool* pStlPool = mxPoolHelper->GetStylePool();
+
+ RowInfo* pRowInfo = rTabInfo.mpRowInfo.get();
+
+ const SvxBrushItem* pDefBackground =
+ &pPool->GetDefaultItem( ATTR_BACKGROUND );
+ const ScMergeAttr* pDefMerge =
+ &pPool->GetDefaultItem( ATTR_MERGE );
+ const SvxShadowItem* pDefShadow =
+ &pPool->GetDefaultItem( ATTR_SHADOW );
+
+ SCSIZE nArrRow;
+ SCSIZE nArrCount;
+ bool bAnyMerged = false;
+ bool bAnyShadow = false;
+ bool bAnyCondition = false;
+ bool bAnyPreview = false;
+
+ bool bTabProtect = IsTabProtected(nTab);
+
+ // first only the entries for the entire column
+
+ nArrRow=0;
+ SCROW nYExtra = nRow2+1;
+ initRowInfo(this, pRowInfo, rTabInfo.mnArrCapacity, fRowScale, nRow1,
+ nTab, nYExtra, nArrRow, nRow2);
+ nArrCount = nArrRow; // incl. Dummys
+
+ // Rotated text...
+
+ // Is Attribute really used in document?
+ bool bAnyItem = isRotateItemUsed(pPool);
+
+ SCCOL nRotMax = nCol2;
+ if ( bAnyItem && HasAttrib( 0, nRow1, nTab, MaxCol(), nRow2+1, nTab,
+ HasAttrFlags::Rotate | HasAttrFlags::Conditional ) )
+ {
+ //TODO: check Conditionals also for HasAttrFlags::Rotate ????
+
+ OSL_ENSURE( nArrCount>2, "nArrCount too small" );
+ FindMaxRotCol( nTab, &pRowInfo[1], nArrCount-1, nCol1, nCol2 );
+ // FindMaxRotCol sets nRotMaxCol
+
+ for (nArrRow=0; nArrRow<nArrCount; nArrRow++)
+ if (pRowInfo[nArrRow].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nArrRow].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nArrRow].nRotMaxCol;
+ }
+
+ // Allocate cell information only after the test rotation
+ // to nRotMax due to nRotateDir Flag
+ initCellInfo(pRowInfo, nArrCount, nCol1, nRotMax, pDefShadow);
+
+ initColWidths(pRowInfo, this, fColScale, nTab, nCol2, nRotMax);
+
+ ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
+ if (pCondFormList)
+ pCondFormList->startRendering();
+
+ SCCOL nLastHiddenCheckedCol = -2;
+ bool bColHidden = false;
+ for (SCCOL nCol=-1; nCol<=nCol2+1; nCol++) // collect basic info also for all previous cols, and left & right + 1
+ {
+ if (ValidCol(nCol))
+ {
+ // #i58049#, #i57939# Hidden columns must be skipped here, or their attributes
+ // will disturb the output
+
+ if (nCol > nLastHiddenCheckedCol)
+ bColHidden = ColHidden(nCol, nTab, nullptr, &nLastHiddenCheckedCol);
+ // TODO: Optimize this loop.
+ if (!bColHidden)
+ {
+ sal_uInt16 nColWidth = GetColWidth( nCol, nTab, false ); // false=no need to check for hidden, checked above
+ sal_uInt16 nThisWidth = static_cast<sal_uInt16>(std::clamp(nColWidth * fColScale, 1.0, double(std::numeric_limits<sal_uInt16>::max())));
+
+ pRowInfo[0].basicCellInfo(nCol).nWidth = nThisWidth; //TODO: this should be enough
+
+ const ScAttrArray* pThisAttrArr; // Attribute
+ if (nCol < maTabs[nTab]->GetAllocatedColumnsCount())
+ {
+ ScColumn* pThisCol = &maTabs[nTab]->aCol[nCol]; // Column data
+
+ nArrRow = 1;
+ // Iterate between rows nY1 and nY2 and pick up non-empty
+ // cells that are not hidden.
+ RowInfoFiller aFunc(*this, nTab, pRowInfo, nCol, nArrRow, nCol1);
+ sc::ParseAllNonEmpty(pThisCol->maCells.begin(), pThisCol->maCells, nRow1, nRow2,
+ aFunc);
+
+ pThisAttrArr = pThisCol->pAttrArray.get();
+ }
+ else
+ pThisAttrArr = &maTabs[nTab]->aDefaultColData.AttrArray();
+
+ if (nCol+1 >= nCol1) // Attribute/Blockmark from nX1-1
+ {
+ nArrRow = 0;
+
+ SCROW nCurRow=nRow1; // single rows
+ if (nCurRow>0)
+ --nCurRow; // 1 more on top
+ else
+ nArrRow = 1;
+
+ SCROW nThisRow;
+ SCSIZE nIndex;
+ if ( pThisAttrArr->Count() )
+ (void) pThisAttrArr->Search( nCurRow, nIndex );
+ else
+ nIndex = 0;
+
+ do
+ {
+ const ScPatternAttr* pPattern = nullptr;
+ if ( pThisAttrArr->Count() )
+ {
+ nThisRow = pThisAttrArr->mvData[nIndex].nEndRow; // End of range
+ pPattern = pThisAttrArr->mvData[nIndex].pPattern;
+ }
+ else
+ {
+ nThisRow = MaxRow();
+ pPattern = GetDefPattern();
+ }
+
+ const SvxBrushItem* pBackground = &pPattern->GetItem(ATTR_BACKGROUND);
+ const SvxBoxItem* pLinesAttr = &pPattern->GetItem(ATTR_BORDER);
+
+ const SvxLineItem* pTLBRLine = &pPattern->GetItem( ATTR_BORDER_TLBR );
+ const SvxLineItem* pBLTRLine = &pPattern->GetItem( ATTR_BORDER_BLTR );
+
+ const SvxShadowItem* pShadowAttr = &pPattern->GetItem(ATTR_SHADOW);
+ if (!SfxPoolItem::areSame(pShadowAttr, pDefShadow))
+ bAnyShadow = true;
+
+ const ScMergeAttr* pMergeAttr = &pPattern->GetItem(ATTR_MERGE);
+ bool bMerged = !SfxPoolItem::areSame( pMergeAttr, pDefMerge );
+ ScMF nOverlap = pPattern->GetItemSet().
+ Get(ATTR_MERGE_FLAG).GetValue();
+ bool bHOverlapped(nOverlap & ScMF::Hor);
+ bool bVOverlapped(nOverlap & ScMF::Ver);
+ bool bAutoFilter(nOverlap & ScMF::Auto);
+ bool bPivotButton(nOverlap & ScMF::Button);
+ bool bScenario(nOverlap & ScMF::Scenario);
+ bool bPivotPopupButton(nOverlap & ScMF::ButtonPopup);
+ bool bFilterActive(nOverlap & ScMF::HiddenMember);
+ bool bPivotCollapseButton(nOverlap & ScMF::DpCollapse);
+ bool bPivotExpandButton(nOverlap & ScMF::DpExpand);
+ bool bPivotPopupButtonMulti(nOverlap & ScMF::ButtonPopup2);
+ if (bMerged||bHOverlapped||bVOverlapped)
+ bAnyMerged = true; // internal
+
+ bool bHidden, bHideFormula;
+ if (bTabProtect)
+ {
+ const ScProtectionAttr& rProtAttr = pPattern->GetItem(ATTR_PROTECTION);
+ bHidden = rProtAttr.GetHideCell();
+ bHideFormula = rProtAttr.GetHideFormula();
+ }
+ else
+ bHidden = bHideFormula = false;
+
+ const ScCondFormatIndexes& rCondFormats = pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+ bool bContainsCondFormat = !rCondFormats.empty();
+
+ do
+ {
+ SCROW nLastHiddenRow = -1;
+ bool bRowHidden = RowHidden(nCurRow, nTab, nullptr, &nLastHiddenRow);
+ if ( nArrRow==0 || !bRowHidden )
+ {
+ if ( GetPreviewCellStyle( nCol, nCurRow, nTab ) != nullptr )
+ bAnyPreview = true;
+ RowInfo* pThisRowInfo = &pRowInfo[nArrRow];
+ if (!SfxPoolItem::areSame(pBackground, pDefBackground)) // Column background == Default ?
+ pThisRowInfo->bEmptyBack = false;
+ if (bContainsCondFormat)
+ pThisRowInfo->bEmptyBack = false;
+ if (bAutoFilter)
+ pThisRowInfo->bAutoFilter = true;
+ if (bPivotButton || bPivotPopupButton || bPivotPopupButtonMulti)
+ pThisRowInfo->bPivotButton = true;
+ if (bPivotCollapseButton || bPivotExpandButton)
+ pThisRowInfo->bPivotToggle = true;
+
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
+ ScBasicCellInfo* pBasicInfo = &pThisRowInfo->basicCellInfo(nCol);
+ pInfo->pBackground = pBackground;
+ pInfo->pPatternAttr = pPattern;
+ pInfo->bMerged = bMerged;
+ pInfo->bHOverlapped = bHOverlapped;
+ pInfo->bVOverlapped = bVOverlapped;
+ pInfo->bAutoFilter = bAutoFilter;
+ pInfo->bPivotButton = bPivotButton;
+ pInfo->bPivotPopupButton = bPivotPopupButton;
+ pInfo->bPivotCollapseButton = bPivotCollapseButton;
+ pInfo->bPivotExpandButton = bPivotExpandButton;
+ pInfo->bPivotPopupButtonMulti = bPivotPopupButtonMulti;
+ pInfo->bFilterActive = bFilterActive;
+ pInfo->pLinesAttr = pLinesAttr;
+ pInfo->mpTLBRLine = pTLBRLine;
+ pInfo->mpBLTRLine = pBLTRLine;
+ pInfo->pShadowAttr = pShadowAttr;
+ // nWidth is no longer set individually
+
+ if (bScenario)
+ {
+ pInfo->pBackground = ScGlobal::GetButtonBrushItem();
+ pThisRowInfo->bEmptyBack = false;
+ }
+
+ if (bContainsCondFormat && pCondFormList)
+ {
+ bAnyCondition |= handleConditionalFormat(*pCondFormList, rCondFormats,
+ pInfo, &rTabInfo, pStlPool, ScAddress(nCol, nCurRow, nTab),
+ bHidden, bHideFormula, bTabProtect);
+ }
+
+ if (bHidden || (bFormulaMode && bHideFormula && pInfo->maCell.getType() == CELLTYPE_FORMULA))
+ pBasicInfo->bEmptyCellText = true;
+
+ ++nArrRow;
+ }
+ else if (nLastHiddenRow >= 0)
+ {
+ nCurRow = nLastHiddenRow;
+ if (nCurRow > nThisRow)
+ nCurRow = nThisRow;
+ }
+ ++nCurRow;
+ }
+ while (nCurRow <= nThisRow && nCurRow <= nYExtra);
+ ++nIndex;
+ }
+ while ( nIndex < pThisAttrArr->Count() && nThisRow < nYExtra );
+
+ if (pMarkData && pMarkData->IsMultiMarked())
+ {
+ // Block marks
+ ScMarkArray aThisMarkArr(pMarkData->GetMarkArray( nCol ));
+ nArrRow = 1;
+ nCurRow = nRow1; // single rows
+
+ if ( aThisMarkArr.Search( nRow1, nIndex ) )
+ {
+ do
+ {
+ nThisRow=aThisMarkArr.mvData[nIndex].nRow; // End of range
+
+ do
+ {
+ if ( !RowHidden( nCurRow,nTab ) )
+ {
+ ++nArrRow;
+ }
+ ++nCurRow;
+ }
+ while (nCurRow <= nThisRow && nCurRow <= nRow2);
+ ++nIndex;
+ }
+ while ( nIndex < aThisMarkArr.mvData.size() && nThisRow < nRow2 );
+ }
+ }
+ }
+ else // columns in front
+ {
+ for (nArrRow=1; nArrRow+1<nArrCount; nArrRow++)
+ pRowInfo[nArrRow].basicCellInfo(nCol).nWidth = nThisWidth; //TODO: or check only 0 ??
+ }
+ }
+ }
+ else
+ pRowInfo[0].basicCellInfo(nCol).nWidth = STD_COL_WIDTH;
+ // STD_COL_WIDTH farthest to the left and right is needed for DrawExtraShadow
+ }
+
+ if (pCondFormList)
+ pCondFormList->endRendering();
+
+ // evaluate conditional formatting
+ std::vector< std::unique_ptr<ScPatternAttr> > aAltPatterns;
+ // favour preview over condition
+ if (bAnyCondition || bAnyPreview)
+ {
+ for (nArrRow=0; nArrRow<nArrCount; nArrRow++)
+ {
+ for (SCCOL nCol=nCol1-1; nCol<=nCol2+1; nCol++) // 1 more left and right
+ {
+ ScCellInfo* pInfo = &pRowInfo[nArrRow].cellInfo(nCol);
+ ScPatternAttr* pModifiedPatt = nullptr;
+
+ if ( ValidCol(nCol) && pRowInfo[nArrRow].nRowNo <= MaxRow() )
+ {
+ if ( ScStyleSheet* pPreviewStyle = GetPreviewCellStyle( nCol, pRowInfo[nArrRow].nRowNo, nTab ) )
+ {
+ aAltPatterns.push_back( std::make_unique<ScPatternAttr>( *pInfo->pPatternAttr ) );
+ pModifiedPatt = aAltPatterns.back().get();
+ pModifiedPatt->SetStyleSheet( pPreviewStyle );
+ }
+ }
+ // favour preview over condition
+ const SfxItemSet* pCondSet = pModifiedPatt ? &pModifiedPatt->GetItemSet() : pInfo->pConditionSet;
+
+ if (pCondSet)
+ {
+ // Background
+ if ( const SvxBrushItem* pItem = pCondSet->GetItemIfSet( ATTR_BACKGROUND ) )
+ {
+ pInfo->pBackground = pItem;
+ pRowInfo[nArrRow].bEmptyBack = false;
+ }
+
+ // Border
+ if ( const SvxBoxItem* pItem = pCondSet->GetItemIfSet( ATTR_BORDER ) )
+ pInfo->pLinesAttr = pItem;
+
+ if ( const SvxLineItem* pItem = pCondSet->GetItemIfSet( ATTR_BORDER_TLBR ) )
+ pInfo->mpTLBRLine = pItem;
+ if ( const SvxLineItem* pItem = pCondSet->GetItemIfSet( ATTR_BORDER_BLTR ) )
+ pInfo->mpBLTRLine = pItem;
+
+ // Shadow
+ if ( const SvxShadowItem* pItem = pCondSet->GetItemIfSet( ATTR_SHADOW ) )
+ {
+ pInfo->pShadowAttr = pItem;
+ bAnyShadow = true;
+ }
+ }
+ if( bAnyCondition && pInfo->mxColorScale)
+ {
+ pRowInfo[nArrRow].bEmptyBack = false;
+ pInfo->pBackground = &pPool->DirectPutItemInPool(SvxBrushItem(*pInfo->mxColorScale, ATTR_BACKGROUND));
+ }
+ }
+ }
+ }
+
+ // End conditional formatting
+
+ // Adjust data from merged cells
+
+ if (bAnyMerged)
+ {
+ for (nArrRow=0; nArrRow<nArrCount; nArrRow++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrRow];
+ SCROW nSignedY = nArrRow ? pThisRowInfo->nRowNo : nRow1-1;
+
+ for (SCCOL nCol=nCol1-1; nCol<=nCol2+1; nCol++) // 1 more left and right
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
+
+ if (pInfo->bMerged || pInfo->bHOverlapped || pInfo->bVOverlapped)
+ {
+ SCCOL nStartX;
+ SCROW nStartY;
+ SCCOL nEndX;
+ SCROW nEndY;
+ lcl_GetMergeRange( nCol,nSignedY, nArrRow, this,pRowInfo, nCol1,nRow1,nTab,
+ nStartX,nStartY, nEndX,nEndY );
+ const ScPatternAttr* pStartPattern = GetPattern( nStartX,nStartY,nTab );
+ const SfxItemSet* pStartCond = GetCondResult( nStartX,nStartY,nTab );
+
+ // Copy Background (or in output.cxx)
+
+ const SvxBrushItem* pBrushItem = nullptr;
+ if ( !pStartCond ||
+ !(pBrushItem = pStartCond->GetItemIfSet(ATTR_BACKGROUND)) )
+ pBrushItem = &pStartPattern->GetItem(ATTR_BACKGROUND);
+ pInfo->pBackground = pBrushItem;
+ pRowInfo[nArrRow].bEmptyBack = false;
+
+ // Shadow
+
+ const SvxShadowItem* pShadowItem = nullptr;
+ if ( !pStartCond ||
+ !(pShadowItem = pStartCond->GetItemIfSet(ATTR_SHADOW)) )
+ pShadowItem = &pStartPattern->GetItem(ATTR_SHADOW);
+ pInfo->pShadowAttr = pShadowItem;
+ if (!SfxPoolItem::areSame(pInfo->pShadowAttr, pDefShadow))
+ bAnyShadow = true;
+
+ const ScCondFormatIndexes& rCondFormatIndex
+ = pStartPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+
+ if (pCondFormList && !pStartCond && !rCondFormatIndex.empty())
+ {
+ for (const auto& rItem : rCondFormatIndex)
+ {
+ const ScConditionalFormat* pCondForm = pCondFormList->GetFormat(rItem);
+ if (pCondForm)
+ {
+ ScCondFormatData aData = pCondForm->GetData(
+ pInfo->maCell, ScAddress(nStartX, nStartY, nTab));
+
+ // Color scale
+ if (aData.mxColorScale && !pInfo->mxColorScale)
+ pInfo->mxColorScale = aData.mxColorScale;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (bAnyShadow) // distribute Shadow
+ {
+ for (nArrRow=0; nArrRow<nArrCount; nArrRow++)
+ {
+ bool bTop = ( nArrRow == 0 );
+ bool bBottom = ( nArrRow+1 == nArrCount );
+
+ for (SCCOL nCol=nCol1-1; nCol<=nCol2+1; nCol++) // 1 more left and right
+ {
+ bool bLeft = ( nCol == nCol1-1 );
+ bool bRight = ( nCol == nCol2+1 );
+
+ ScCellInfo* pInfo = &pRowInfo[nArrRow].cellInfo(nCol);
+ const SvxShadowItem* pThisAttr = pInfo->pShadowAttr;
+ SvxShadowLocation eLoc = pThisAttr ? pThisAttr->GetLocation() : SvxShadowLocation::NONE;
+ if (eLoc != SvxShadowLocation::NONE)
+ {
+ // or test on != eLoc
+
+ SCCOL nDxPos = 1;
+ SCCOL nDxNeg = -1;
+
+ while ( nCol+nDxPos < nCol2+1 && pRowInfo[0].basicCellInfo(nCol+nDxPos).nWidth == 0 )
+ ++nDxPos;
+ while ( nCol+nDxNeg > nCol1-1 && pRowInfo[0].basicCellInfo(nCol+nDxNeg).nWidth == 0 )
+ --nDxNeg;
+
+ bool bLeftDiff = !bLeft &&
+ pRowInfo[nArrRow].cellInfo(nCol+nDxNeg).pShadowAttr->GetLocation() == SvxShadowLocation::NONE;
+ bool bRightDiff = !bRight &&
+ pRowInfo[nArrRow].cellInfo(nCol+nDxPos).pShadowAttr->GetLocation() == SvxShadowLocation::NONE;
+ bool bTopDiff = !bTop &&
+ pRowInfo[nArrRow-1].cellInfo(nCol).pShadowAttr->GetLocation() == SvxShadowLocation::NONE;
+ bool bBottomDiff = !bBottom &&
+ pRowInfo[nArrRow+1].cellInfo(nCol).pShadowAttr->GetLocation() == SvxShadowLocation::NONE;
+
+ if ( bLayoutRTL )
+ {
+ switch (eLoc)
+ {
+ case SvxShadowLocation::BottomRight: eLoc = SvxShadowLocation::BottomLeft; break;
+ case SvxShadowLocation::BottomLeft: eLoc = SvxShadowLocation::BottomRight; break;
+ case SvxShadowLocation::TopRight: eLoc = SvxShadowLocation::TopLeft; break;
+ case SvxShadowLocation::TopLeft: eLoc = SvxShadowLocation::TopRight; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ switch (eLoc)
+ {
+ case SvxShadowLocation::BottomRight:
+ if (bBottomDiff)
+ {
+ pRowInfo[nArrRow+1].cellInfo(nCol).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow+1].cellInfo(nCol).eHShadowPart =
+ bLeftDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ;
+ }
+ if (bRightDiff)
+ {
+ pRowInfo[nArrRow].cellInfo(nCol+1).pVShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow].cellInfo(nCol+1).eVShadowPart =
+ bTopDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT;
+ }
+ if (bBottomDiff && bRightDiff)
+ {
+ pRowInfo[nArrRow+1].cellInfo(nCol+1).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow+1].cellInfo(nCol+1).eHShadowPart = SC_SHADOW_CORNER;
+ }
+ break;
+
+ case SvxShadowLocation::BottomLeft:
+ if (bBottomDiff)
+ {
+ pRowInfo[nArrRow+1].cellInfo(nCol).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow+1].cellInfo(nCol).eHShadowPart =
+ bRightDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ;
+ }
+ if (bLeftDiff)
+ {
+ pRowInfo[nArrRow].cellInfo(nCol-1).pVShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow].cellInfo(nCol-1).eVShadowPart =
+ bTopDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT;
+ }
+ if (bBottomDiff && bLeftDiff)
+ {
+ pRowInfo[nArrRow+1].cellInfo(nCol-1).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow+1].cellInfo(nCol-1).eHShadowPart = SC_SHADOW_CORNER;
+ }
+ break;
+
+ case SvxShadowLocation::TopRight:
+ if (bTopDiff)
+ {
+ pRowInfo[nArrRow-1].cellInfo(nCol).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow-1].cellInfo(nCol).eHShadowPart =
+ bLeftDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ;
+ }
+ if (bRightDiff)
+ {
+ pRowInfo[nArrRow].cellInfo(nCol+1).pVShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow].cellInfo(nCol+1).eVShadowPart =
+ bBottomDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT;
+ }
+ if (bTopDiff && bRightDiff)
+ {
+ pRowInfo[nArrRow-1].cellInfo(nCol+1).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow-1].cellInfo(nCol+1).eHShadowPart = SC_SHADOW_CORNER;
+ }
+ break;
+
+ case SvxShadowLocation::TopLeft:
+ if (bTopDiff)
+ {
+ pRowInfo[nArrRow-1].cellInfo(nCol).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow-1].cellInfo(nCol).eHShadowPart =
+ bRightDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ;
+ }
+ if (bLeftDiff)
+ {
+ pRowInfo[nArrRow].cellInfo(nCol-1).pVShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow].cellInfo(nCol-1).eVShadowPart =
+ bBottomDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT;
+ }
+ if (bTopDiff && bLeftDiff)
+ {
+ pRowInfo[nArrRow-1].cellInfo(nCol-1).pHShadowOrigin = pThisAttr;
+ pRowInfo[nArrRow-1].cellInfo(nCol-1).eHShadowPart = SC_SHADOW_CORNER;
+ }
+ break;
+
+ default:
+ OSL_FAIL("wrong Shadow-Enum");
+ }
+ }
+ }
+ }
+ }
+
+ rTabInfo.mnArrCount = sal::static_int_cast<sal_uInt16>(nArrCount);
+ rTabInfo.mbPageMode = bPageMode;
+
+ // *** create the frame border array ***
+
+ // RowInfo structs are filled in the range [ 0 , nArrCount-1 ],
+ // each RowInfo contains ScCellInfo structs in the range [ nCol1-1 , nCol2+1 ]
+ // and ScBasicCellInfo structs in the range [ -1, nCol2+1 ]
+
+ size_t nColCount = nCol2 - nCol1 + 1 + 2;
+ size_t nRowCount = nArrCount;
+
+ svx::frame::Array& rArray = rTabInfo.maArray;
+ rArray.Initialize( nColCount, nRowCount );
+
+ for( size_t nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ sal_uInt16 nCellInfoY = static_cast< sal_uInt16 >( nRow );
+ RowInfo& rThisRowInfo = pRowInfo[ nCellInfoY ];
+
+ for( SCCOL nCol = nCol1 - 1; nCol <= nCol2 + 1; ++nCol ) // 1 more left and right
+ {
+ const ScCellInfo& rInfo = rThisRowInfo.cellInfo( nCol );
+ const SvxBoxItem* pBox = rInfo.pLinesAttr;
+ const SvxLineItem* pTLBR = rInfo.mpTLBRLine;
+ const SvxLineItem* pBLTR = rInfo.mpBLTRLine;
+
+ size_t colToIndex = -(nCol1 - 1);
+ // These are rArray indexes (0-based), not really rows/columns.
+ size_t nX = nCol + colToIndex;
+ size_t nFirstCol = nX;
+ size_t nFirstRow = nRow;
+
+ // *** merged cells *** -------------------------------------------
+
+ if( !rArray.IsMerged( nX, nRow ) && (rInfo.bMerged || rInfo.bHOverlapped || rInfo.bVOverlapped) )
+ {
+ // *** insert merged range in svx::frame::Array ***
+
+ /* #i69369# top-left cell of a merged range may be located in
+ a hidden column or row. Use lcl_GetMergeRange() to find the
+ complete merged range, then calculate dimensions and
+ document position of the visible range. */
+
+ // note: document rows must be looked up in RowInfo structs
+
+ // current column and row in document coordinates
+ SCCOL nCurrDocCol = nCol;
+ SCROW nCurrDocRow = static_cast< SCROW >( (nCellInfoY > 0) ? rThisRowInfo.nRowNo : (nRow1 - 1) );
+
+ // find entire merged range in document, returns signed document coordinates
+ SCCOL nFirstRealDocColS, nLastRealDocColS;
+ SCROW nFirstRealDocRowS, nLastRealDocRowS;
+ lcl_GetMergeRange( nCurrDocCol, nCurrDocRow,
+ nCellInfoY, this, pRowInfo, nCol1,nRow1,nTab,
+ nFirstRealDocColS, nFirstRealDocRowS, nLastRealDocColS, nLastRealDocRowS );
+
+ // *complete* merged range in document coordinates
+ SCCOL nFirstRealDocCol = nFirstRealDocColS;
+ SCROW nFirstRealDocRow = nFirstRealDocRowS;
+ SCCOL nLastRealDocCol = nLastRealDocColS;
+ SCROW nLastRealDocRow = nLastRealDocRowS;
+
+ // first visible column (nCol1-1 is first processed document column)
+ SCCOL nFirstDocCol = (nCol1 > 0) ? ::std::max< SCCOL >( nFirstRealDocCol, nCol1 - 1 ) : nFirstRealDocCol;
+ nFirstCol = nFirstDocCol + colToIndex;
+
+ // last visible column (nCol2+1 is last processed document column)
+ SCCOL nLastDocCol = (nCol2 < MaxCol()) ? ::std::min< SCCOL >( nLastRealDocCol, nCol2 + 1 ) : nLastRealDocCol;
+ size_t nLastCol = nLastDocCol + colToIndex;
+
+ // first visible row
+ sal_uInt16 nFirstCellInfoY = nCellInfoY;
+ while( ((nFirstCellInfoY > 1) && (pRowInfo[ nFirstCellInfoY - 1 ].nRowNo >= nFirstRealDocRow)) ||
+ ((nFirstCellInfoY == 1) && (static_cast< SCROW >( nRow1 - 1 ) >= nFirstRealDocRow)) )
+ --nFirstCellInfoY;
+ SCROW nFirstDocRow = (nFirstCellInfoY > 0) ? pRowInfo[ nFirstCellInfoY ].nRowNo : static_cast< SCROW >( nRow1 - 1 );
+ nFirstRow = static_cast< size_t >( nFirstCellInfoY );
+
+ // last visible row
+ sal_uInt16 nLastCellInfoY = nCellInfoY;
+ while( (sal::static_int_cast<SCSIZE>(nLastCellInfoY + 1) < nArrCount) &&
+ (pRowInfo[ nLastCellInfoY + 1 ].nRowNo <= nLastRealDocRow) )
+ ++nLastCellInfoY;
+ SCROW nLastDocRow = (nLastCellInfoY > 0) ? pRowInfo[ nLastCellInfoY ].nRowNo : static_cast< SCROW >( nRow1 - 1 );
+ size_t nLastRow = static_cast< size_t >( nLastCellInfoY );
+
+ // insert merged range
+ rArray.SetMergedRange( nFirstCol, nFirstRow, nLastCol, nLastRow );
+
+ // *** find additional size not included in svx::frame::Array ***
+
+ // additional space before first column
+ if( nFirstCol == 0 )
+ {
+ tools::Long nSize = 0;
+ for( SCCOL nDocCol = nFirstRealDocCol; nDocCol < nFirstDocCol; ++nDocCol )
+ nSize += std::max( tools::Long(GetColWidth( nDocCol, nTab ) * fColScale), tools::Long(1) );
+ rArray.SetAddMergedLeftSize( nX, nRow, nSize );
+ }
+ // additional space after last column
+ if( nLastCol + 1 == nColCount )
+ {
+ tools::Long nSize = 0;
+ for( SCCOL nDocCol = nLastDocCol + 1; nDocCol <= nLastRealDocCol; ++nDocCol )
+ nSize += std::max( tools::Long(GetColWidth( nDocCol, nTab ) * fColScale), tools::Long(1) );
+ rArray.SetAddMergedRightSize( nX, nRow, nSize );
+ }
+ // additional space above first row
+ if( nFirstRow == 0 )
+ {
+ tools::Long nSize = 0;
+ for( SCROW nDocRow = nFirstRealDocRow; nDocRow < nFirstDocRow; ++nDocRow )
+ nSize += std::max( tools::Long(GetRowHeight( nDocRow, nTab ) * fRowScale), tools::Long(1) );
+ rArray.SetAddMergedTopSize( nX, nRow, nSize );
+ }
+ // additional space beyond last row
+ if( nLastRow + 1 == nRowCount )
+ {
+ tools::Long nSize = 0;
+ for( SCROW nDocRow = nLastDocRow + 1; nDocRow <= nLastRealDocRow; ++nDocRow )
+ nSize += std::max( tools::Long(GetRowHeight( nDocRow, nTab ) * fRowScale), tools::Long(1) );
+ rArray.SetAddMergedBottomSize( nX, nRow, nSize );
+ }
+
+ // *** use line attributes from real origin cell ***
+
+ if( (nFirstRealDocCol != nCurrDocCol) || (nFirstRealDocRow != nCurrDocRow) )
+ {
+ if( const ScPatternAttr* pPattern = GetPattern( nFirstRealDocCol, nFirstRealDocRow, nTab ) )
+ {
+ const SfxItemSet* pCond = GetCondResult( nFirstRealDocCol, nFirstRealDocRow, nTab );
+ pBox = &pPattern->GetItem( ATTR_BORDER, pCond );
+ pTLBR = &pPattern->GetItem( ATTR_BORDER_TLBR, pCond );
+ pBLTR = &pPattern->GetItem( ATTR_BORDER_BLTR, pCond );
+ }
+ else
+ {
+ pBox = nullptr;
+ pTLBR = pBLTR = nullptr;
+ }
+ }
+ }
+
+ // *** borders *** ------------------------------------------------
+
+ if( pBox )
+ {
+ rArray.SetCellStyleLeft( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetLeft(), fColScale ) );
+ rArray.SetCellStyleRight( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetRight(), fColScale ) );
+ rArray.SetCellStyleTop( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetTop(), fRowScale ) );
+ rArray.SetCellStyleBottom( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetBottom(), fRowScale ) );
+ }
+
+ if( pTLBR )
+ rArray.SetCellStyleTLBR( nFirstCol, nFirstRow, svx::frame::Style( pTLBR->GetLine(), fRowScale ) );
+ if( pBLTR )
+ rArray.SetCellStyleBLTR( nFirstCol, nFirstRow, svx::frame::Style( pBLTR->GetLine(), fRowScale ) );
+ }
+ }
+
+ /* Mirror the entire frame array. */
+ if( bLayoutRTL )
+ rArray.MirrorSelfX();
+}
+
+ScTableInfo::ScTableInfo(const SCSIZE capacity)
+ : mpRowInfo(new RowInfo[capacity])
+ , mnArrCount(0)
+ , mnArrCapacity(capacity)
+ , mbPageMode(false)
+{
+ memset(static_cast<void*>(mpRowInfo.get()), 0, mnArrCapacity * sizeof(RowInfo));
+}
+
+ScTableInfo::~ScTableInfo()
+{
+ for( SCSIZE nIdx = 0; nIdx < mnArrCapacity; ++nIdx )
+ mpRowInfo[ nIdx ].freeCellInfo();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
new file mode 100644
index 0000000000..f278492b09
--- /dev/null
+++ b/sc/source/core/data/formulacell.cxx
@@ -0,0 +1,5648 @@
+/* -*- 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_feature_opencl.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <cassert>
+#include <cstdlib>
+
+#include <formulacell.hxx>
+#include <grouptokenconverter.hxx>
+
+#include <compiler.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <interpre.hxx>
+#include <macromgr.hxx>
+#include <refupdat.hxx>
+#include <recursionhelper.hxx>
+#include <docoptio.hxx>
+#include <rangenam.hxx>
+#include <rangelst.hxx>
+#include <dbdata.hxx>
+#include <progress.hxx>
+#include <scmatrix.hxx>
+#include <rechead.hxx>
+#include <scitems.hxx>
+#include <validat.hxx>
+#include <editutil.hxx>
+#include <chgtrack.hxx>
+#include <tokenarray.hxx>
+
+#include <comphelper/threadpool.hxx>
+#include <editeng/editobj.hxx>
+#include <formula/errorcodes.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <formulagroup.hxx>
+#include <listenercontext.hxx>
+#include <types.hxx>
+#include <scopetools.hxx>
+#include <refupdatecontext.hxx>
+#include <tokenstringcontext.hxx>
+#include <refhint.hxx>
+#include <listenerquery.hxx>
+#include <listenerqueryids.hxx>
+#include <grouparealistener.hxx>
+#include <formulalogger.hxx>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+
+#if HAVE_FEATURE_OPENCL
+#include <opencl/openclwrapper.hxx>
+#endif
+
+#include <memory>
+#include <map>
+
+using namespace formula;
+
+#define DEBUG_CALCULATION 0
+#if DEBUG_CALCULATION
+static bool bDebugCalculationActive = false; // Set to true for global active init,
+static ScAddress aDebugCalculationTriggerAddress(1,2,0); // or on cell Sheet1.B3, whatever you like
+
+struct DebugCalculationEntry
+{
+ ScAddress maPos;
+ OUString maResult;
+ const ScDocument& mrDoc;
+ sal_uInt32 mnGroup;
+ sal_uInt16 mnRecursion;
+
+ DebugCalculationEntry( const ScAddress& rPos, ScDocument& rDoc, sal_uInt32 nGroup ) :
+ maPos(rPos),
+ mrDoc(rDoc),
+ mnGroup(nGroup),
+ mnRecursion(rDoc.GetRecursionHelper().GetRecursionCount())
+ {
+ }
+};
+
+/** Debug/dump formula cell calculation chain.
+ Either, somewhere set aDC.mbActive=true, or
+ aDC.maTrigger=ScAddress(col,row,tab) of interest from where to start.
+ This does not work for deep recursion > MAXRECURSION, the results are
+ somewhat... funny... ;)
+ */
+static struct DebugCalculation
+{
+ std::vector< DebugCalculationEntry > mvPos;
+ std::vector< DebugCalculationEntry > mvResults;
+ ScAddress maTrigger;
+ sal_uInt32 mnGroup;
+ bool mbActive;
+ bool mbSwitchOff;
+ bool mbPrint;
+ bool mbPrintResults;
+
+ DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false),
+ mbPrint(true), mbPrintResults(false) {}
+
+ /** Print chain in encountered dependency order. */
+ void print() const
+ {
+ for (auto const& it : mvPos)
+ {
+ OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc) +
+ " [" + OUString::number( it.mnRecursion) + "," + OUString::number( it.mnGroup) + "]");
+ fprintf( stderr, "%s -> ", aStr.toUtf8().getStr());
+ }
+ fprintf( stderr, "%s", "END\n");
+ }
+
+ /** Print chain results. */
+ void printResults() const
+ {
+ for (auto const& it : mvResults)
+ {
+ OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc));
+ aStr += " (" + it.maResult + ")";
+ fprintf( stderr, "%s, ", aStr.toUtf8().getStr());
+ }
+ fprintf( stderr, "%s", "END\n");
+ }
+
+ void storeResult( const svl::SharedString& rStr )
+ {
+ if (mbActive && !mvPos.empty())
+ mvPos.back().maResult = "\"" + rStr.getString() + "\"";
+ }
+
+ void storeResult( const double& fVal )
+ {
+ if (mbActive && !mvPos.empty())
+ mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.', true);
+ }
+
+ void storeResultError( FormulaError nErr )
+ {
+ if (mbActive && !mvPos.empty())
+ mvPos.back().maResult = "Err:" + OUString::number( int( nErr ));
+ }
+
+ void enterGroup()
+ {
+ ++mnGroup;
+ }
+
+ void leaveGroup()
+ {
+ --mnGroup;
+ }
+
+} aDC;
+
+struct DebugCalculationStacker
+{
+ DebugCalculationStacker( const ScAddress& rPos, ScDocument& rDoc )
+ {
+ if (!aDC.mbActive && rPos == aDC.maTrigger)
+ aDC.mbActive = aDC.mbSwitchOff = true;
+ if (aDC.mbActive)
+ {
+ aDC.mvPos.push_back( DebugCalculationEntry( rPos, rDoc, aDC.mnGroup));
+ aDC.mbPrint = true;
+ }
+ }
+
+ ~DebugCalculationStacker()
+ {
+ if (aDC.mbActive)
+ {
+ if (!aDC.mvPos.empty())
+ {
+ if (aDC.mbPrint)
+ {
+ aDC.print();
+ aDC.mbPrint = false;
+ }
+ if (aDC.mbPrintResults)
+ {
+ // Store results until final result is available, reversing order.
+ aDC.mvResults.push_back( aDC.mvPos.back());
+ }
+ aDC.mvPos.pop_back();
+ if (aDC.mbPrintResults && aDC.mvPos.empty())
+ {
+ aDC.printResults();
+ std::vector< DebugCalculationEntry >().swap( aDC.mvResults);
+ }
+ if (aDC.mbSwitchOff && aDC.mvPos.empty())
+ aDC.mbActive = false;
+ }
+ }
+ }
+};
+#endif
+
+namespace {
+
+// More or less arbitrary, of course all recursions must fit into available
+// stack space (which is what on all systems we don't know yet?). Choosing a
+// lower value may be better than trying a much higher value that also isn't
+// sufficient but temporarily leads to high memory consumption. On the other
+// hand, if the value fits all recursions, execution is quicker as no resumes
+// are necessary. Could be made a configurable option.
+// Allow for a year's calendar (366).
+const sal_uInt16 MAXRECURSION = 400;
+
+typedef SCCOLROW(*DimensionSelector)(const ScDocument&, const ScAddress&, const ScSingleRefData&);
+
+SCCOLROW lcl_GetCol(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
+{
+ return rData.toAbs(rDoc, rPos).Col();
+}
+
+SCCOLROW lcl_GetRow(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
+{
+ return rData.toAbs(rDoc, rPos).Row();
+}
+
+SCCOLROW lcl_GetTab(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
+{
+ return rData.toAbs(rDoc, rPos).Tab();
+}
+
+/** Check if both references span the same range in selected dimension.
+ */
+bool
+lcl_checkRangeDimension(
+ const ScDocument& rDoc,
+ const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
+ const DimensionSelector aWhich)
+{
+ return aWhich(rDoc, rPos, rRef1.Ref1) == aWhich(rDoc, rPos, rRef2.Ref1) &&
+ aWhich(rDoc, rPos, rRef1.Ref2) == aWhich(rDoc, rPos, rRef2.Ref2);
+}
+
+bool
+lcl_checkRangeDimensions(
+ const ScDocument& rDoc,
+ const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
+ bool& bCol, bool& bRow, bool& bTab)
+{
+ const bool bSameCols(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetCol));
+ const bool bSameRows(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetRow));
+ const bool bSameTabs(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetTab));
+
+ // Test if exactly two dimensions are equal
+ if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
+ {
+ bCol = !bSameCols;
+ bRow = !bSameRows;
+ bTab = !bSameTabs;
+ return true;
+ }
+ return false;
+}
+
+/** Check if references in given reference list can possibly
+ form a range. To do that, two of their dimensions must be the same.
+ */
+bool
+lcl_checkRangeDimensions(
+ const ScDocument& rDoc, const ScAddress& rPos,
+ const std::vector<formula::FormulaToken*>::const_iterator& rBegin,
+ const std::vector<formula::FormulaToken*>::const_iterator& rEnd,
+ bool& bCol, bool& bRow, bool& bTab)
+{
+ std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin);
+ ++aCur;
+ const SingleDoubleRefProvider aRef(**rBegin);
+ bool bOk(false);
+ {
+ const SingleDoubleRefProvider aRefCur(**aCur);
+ bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bCol, bRow, bTab);
+ }
+ while (bOk && aCur != rEnd)
+ {
+ const SingleDoubleRefProvider aRefCur(**aCur);
+ bool bColTmp(false);
+ bool bRowTmp(false);
+ bool bTabTmp(false);
+ bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
+ bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
+ ++aCur;
+ }
+
+ return bOk && aCur == rEnd;
+}
+
+class LessByReference
+{
+ const ScDocument& mrDoc;
+ ScAddress maPos;
+ DimensionSelector maFunc;
+public:
+ LessByReference(const ScDocument& rDoc, const ScAddress& rPos, const DimensionSelector& rFunc) :
+ mrDoc(rDoc), maPos(rPos), maFunc(rFunc) {}
+
+ bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
+ {
+ const SingleDoubleRefProvider aRef1(*pRef1);
+ const SingleDoubleRefProvider aRef2(*pRef2);
+ return maFunc(mrDoc, maPos, aRef1.Ref1) < maFunc(mrDoc, maPos, aRef2.Ref1);
+ }
+};
+
+/**
+ * Returns true if range denoted by token p2 starts immediately after range
+ * denoted by token p1. Dimension, in which the comparison takes place, is
+ * given by maFunc.
+ */
+class AdjacentByReference
+{
+ const ScDocument& mrDoc;
+ ScAddress maPos;
+ DimensionSelector maFunc;
+public:
+ AdjacentByReference(const ScDocument& rDoc, const ScAddress& rPos, DimensionSelector aFunc) :
+ mrDoc(rDoc), maPos(rPos), maFunc(aFunc) {}
+
+ bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
+ {
+ const SingleDoubleRefProvider aRef1(*p1);
+ const SingleDoubleRefProvider aRef2(*p2);
+ return maFunc(mrDoc, maPos, aRef2.Ref1) - maFunc(mrDoc, maPos, aRef1.Ref2) == 1;
+ }
+};
+
+bool
+lcl_checkIfAdjacent(
+ const ScDocument& rDoc,
+ const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
+{
+ auto aBegin(rReferences.cbegin());
+ auto aEnd(rReferences.cend());
+ auto aBegin1(aBegin);
+ ++aBegin1;
+ --aEnd;
+ return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rDoc, rPos, aWhich));
+}
+
+void
+lcl_fillRangeFromRefList(
+ const ScDocument& rDoc,
+ const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange)
+{
+ const ScSingleRefData aStart(
+ SingleDoubleRefProvider(*rReferences.front()).Ref1);
+ rRange.aStart = aStart.toAbs(rDoc, aPos);
+ const ScSingleRefData aEnd(
+ SingleDoubleRefProvider(*rReferences.back()).Ref2);
+ rRange.aEnd = aEnd.toAbs(rDoc, aPos);
+}
+
+bool
+lcl_refListFormsOneRange(
+ const ScDocument& rDoc,
+ const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
+ ScRange& rRange)
+{
+ if (rReferences.size() == 1)
+ {
+ lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
+ return true;
+ }
+
+ bool bCell(false);
+ bool bRow(false);
+ bool bTab(false);
+ if (lcl_checkRangeDimensions(rDoc, rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
+ {
+ DimensionSelector aWhich;
+ if (bCell)
+ {
+ aWhich = lcl_GetCol;
+ }
+ else if (bRow)
+ {
+ aWhich = lcl_GetRow;
+ }
+ else if (bTab)
+ {
+ aWhich = lcl_GetTab;
+ }
+ else
+ {
+ OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
+ aWhich = lcl_GetRow; // initialize to avoid warning
+ }
+
+ // Sort the references by start of range
+ std::sort(rReferences.begin(), rReferences.end(), LessByReference(rDoc, rPos, aWhich));
+ if (lcl_checkIfAdjacent(rDoc, rPos, rReferences, aWhich))
+ {
+ lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool lcl_isReference(const FormulaToken& rToken)
+{
+ return
+ rToken.GetType() == svSingleRef ||
+ rToken.GetType() == svDoubleRef;
+}
+
+void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc,
+ const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
+{
+ ScRangeData* pRangeData = nullptr;
+ SCTAB nSheet = pToken->GetSheet();
+ sal_uInt16 nIndex = pToken->GetIndex();
+ if (!rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true))
+ return; // nothing to do
+
+ if (!pRangeData)
+ {
+ // If this happened we have a real problem.
+ pToken->SetIndex(0);
+ assert(!"inserting the range name should not fail");
+ return;
+ }
+
+ pToken->SetIndex(nIndex);
+ pToken->SetSheet(nSheet);
+}
+
+void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc)
+{
+ ScDBCollection* pOldDBCollection = rOldDoc.GetDBCollection();
+ if (!pOldDBCollection)
+ return;//strange error case, don't do anything
+ ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
+ ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
+ if (!pDBData)
+ return; //invalid index
+ OUString aDBName = pDBData->GetUpperName();
+
+ //search in new document
+ ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
+ if (!pNewDBCollection)
+ {
+ rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(rNewDoc)));
+ pNewDBCollection = rNewDoc.GetDBCollection();
+ }
+ ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
+ ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
+ if (!pNewDBData)
+ {
+ pNewDBData = new ScDBData(*pDBData);
+ bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData));
+ assert(ins); (void)ins;
+ }
+ pToken->SetIndex(pNewDBData->GetIndex());
+}
+
+}
+
+bool AreaListenerKey::operator < ( const AreaListenerKey& r ) const
+{
+ if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
+ return maRange.aStart.Tab() < r.maRange.aStart.Tab();
+ if (maRange.aStart.Col() != r.maRange.aStart.Col())
+ return maRange.aStart.Col() < r.maRange.aStart.Col();
+ if (maRange.aStart.Row() != r.maRange.aStart.Row())
+ return maRange.aStart.Row() < r.maRange.aStart.Row();
+ if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
+ return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
+ if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
+ return maRange.aEnd.Col() < r.maRange.aEnd.Col();
+ if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
+ return maRange.aEnd.Row() < r.maRange.aEnd.Row();
+ if (mbStartFixed != r.mbStartFixed)
+ return r.mbStartFixed;
+ if (mbEndFixed != r.mbEndFixed)
+ return r.mbEndFixed;
+
+ return false;
+}
+
+ScFormulaCellGroup::ScFormulaCellGroup() :
+ mnRefCount(0),
+ mpTopCell(nullptr),
+ mnLength(0),
+ mnWeight(0),
+ mnFormatType(SvNumFormatType::NUMBER),
+ mbInvariant(false),
+ mbSubTotal(false),
+ mbPartOfCycle(false),
+ meCalcState(sc::GroupCalcEnabled)
+{
+}
+
+ScFormulaCellGroup::~ScFormulaCellGroup()
+{
+}
+
+void ScFormulaCellGroup::setCode( const ScTokenArray& rCode )
+{
+ mpCode = rCode.CloneValue();
+ mbInvariant = mpCode->IsInvariant();
+ mpCode->GenHash();
+}
+
+void ScFormulaCellGroup::compileCode(
+ ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
+{
+ if (!mpCode)
+ return;
+
+ if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
+ {
+ bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
+ ScCompiler aComp(rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
+ mbSubTotal = aComp.CompileTokenArray();
+ mnFormatType = aComp.GetNumFormatType();
+ }
+ else
+ {
+ mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
+ }
+}
+
+sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener(
+ ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
+{
+ AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);
+
+ AreaListenersType::iterator it = m_AreaListeners.lower_bound(aKey);
+ if (it == m_AreaListeners.end() || m_AreaListeners.key_comp()(aKey, it->first))
+ {
+ // Insert a new one.
+ it = m_AreaListeners.emplace_hint(
+ it, std::piecewise_construct,
+ std::forward_as_tuple(aKey),
+ std::forward_as_tuple(
+ rRange, (*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed));
+ }
+
+ return &it->second;
+}
+
+void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc )
+{
+ for (auto& rEntry : m_AreaListeners)
+ {
+ sc::FormulaGroupAreaListener& rListener = rEntry.second;
+ ScRange aListenRange = rListener.getListeningRange();
+ // This "always listen" special range is never grouped.
+ bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
+ rDoc.EndListeningArea(aListenRange, bGroupListening, &rListener);
+ }
+
+ m_AreaListeners.clear();
+}
+
+ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos ) :
+ bDirty(false),
+ bTableOpDirty(false),
+ bChanged(false),
+ bRunning(false),
+ bCompile(false),
+ bSubTotal(false),
+ bIsIterCell(false),
+ bInChangeTrack(false),
+ bNeedListening(false),
+ mbNeedsNumberFormat(false),
+ mbAllowNumberFormatChange(false),
+ mbPostponedDirty(false),
+ mbIsExtRef(false),
+ mbSeenInPath(false),
+ mbFreeFlying(false),
+ cMatrixFlag(ScMatrixMode::NONE),
+ nSeenInIteration(0),
+ nFormatType(SvNumFormatType::NUMBER),
+ eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
+ pCode(new ScTokenArray(rDoc)),
+ rDocument(rDoc),
+ pPrevious(nullptr),
+ pNext(nullptr),
+ pPreviousTrack(nullptr),
+ pNextTrack(nullptr),
+ aPos(rPos)
+{
+}
+
+ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos,
+ const OUString& rFormula,
+ const FormulaGrammar::Grammar eGrammar,
+ ScMatrixMode cMatInd ) :
+ bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
+ bTableOpDirty( false ),
+ bChanged( false ),
+ bRunning( false ),
+ bCompile( false ),
+ bSubTotal( false ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bNeedListening( false ),
+ mbNeedsNumberFormat( false ),
+ mbAllowNumberFormatChange(false),
+ mbPostponedDirty(false),
+ mbIsExtRef(false),
+ mbSeenInPath(false),
+ mbFreeFlying(false),
+ cMatrixFlag ( cMatInd ),
+ nSeenInIteration(0),
+ nFormatType ( SvNumFormatType::NUMBER ),
+ eTempGrammar( eGrammar),
+ pCode( nullptr ),
+ rDocument( rDoc ),
+ pPrevious(nullptr),
+ pNext(nullptr),
+ pPreviousTrack(nullptr),
+ pNextTrack(nullptr),
+ aPos(rPos)
+{
+ Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
+ if (!pCode)
+ // We need to have a non-NULL token array instance at all times.
+ pCode = new ScTokenArray(rDoc);
+}
+
+ScFormulaCell::ScFormulaCell(
+ ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray,
+ const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
+ bDirty( true ),
+ bTableOpDirty( false ),
+ bChanged( false ),
+ bRunning( false ),
+ bCompile( false ),
+ bSubTotal( false ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bNeedListening( false ),
+ mbNeedsNumberFormat( false ),
+ mbAllowNumberFormatChange(false),
+ mbPostponedDirty(false),
+ mbIsExtRef(false),
+ mbSeenInPath(false),
+ mbFreeFlying(false),
+ cMatrixFlag ( cMatInd ),
+ nSeenInIteration(0),
+ nFormatType ( SvNumFormatType::NUMBER ),
+ eTempGrammar( eGrammar),
+ pCode(pArray.release()),
+ rDocument( rDoc ),
+ pPrevious(nullptr),
+ pNext(nullptr),
+ pPreviousTrack(nullptr),
+ pNextTrack(nullptr),
+ aPos(rPos)
+{
+ assert(pCode); // Never pass a NULL pointer here.
+
+ pCode->Finalize(); // Reduce memory usage if needed.
+
+ // Generate RPN token array.
+ if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
+ {
+ ScCompiler aComp(rDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE);
+ bSubTotal = aComp.CompileTokenArray();
+ nFormatType = aComp.GetNumFormatType();
+ }
+ else
+ {
+ if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
+ bSubTotal = true;
+ }
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+
+ pCode->GenHash();
+}
+
+ScFormulaCell::ScFormulaCell(
+ ScDocument& rDoc, const ScAddress& rPos, const ScTokenArray& rArray,
+ const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
+ bDirty( true ),
+ bTableOpDirty( false ),
+ bChanged( false ),
+ bRunning( false ),
+ bCompile( false ),
+ bSubTotal( false ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bNeedListening( false ),
+ mbNeedsNumberFormat( false ),
+ mbAllowNumberFormatChange(false),
+ mbPostponedDirty(false),
+ mbIsExtRef(false),
+ mbSeenInPath(false),
+ mbFreeFlying(false),
+ cMatrixFlag ( cMatInd ),
+ nSeenInIteration(0),
+ nFormatType ( SvNumFormatType::NUMBER ),
+ eTempGrammar( eGrammar),
+ pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array
+ rDocument( rDoc ),
+ pPrevious(nullptr),
+ pNext(nullptr),
+ pPreviousTrack(nullptr),
+ pNextTrack(nullptr),
+ aPos(rPos)
+{
+ // RPN array generation
+ if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
+ {
+ ScCompiler aComp( rDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE );
+ bSubTotal = aComp.CompileTokenArray();
+ nFormatType = aComp.GetNumFormatType();
+ }
+ else
+ {
+ if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
+ bSubTotal = true;
+ }
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+
+ pCode->GenHash();
+}
+
+ScFormulaCell::ScFormulaCell(
+ ScDocument& rDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
+ const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) :
+ mxGroup(xGroup),
+ bDirty(true),
+ bTableOpDirty( false ),
+ bChanged( false ),
+ bRunning( false ),
+ bCompile( false ),
+ bSubTotal(xGroup->mbSubTotal),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bNeedListening( false ),
+ mbNeedsNumberFormat( false ),
+ mbAllowNumberFormatChange(false),
+ mbPostponedDirty(false),
+ mbIsExtRef(false),
+ mbSeenInPath(false),
+ mbFreeFlying(false),
+ cMatrixFlag ( cInd ),
+ nSeenInIteration(0),
+ nFormatType(xGroup->mnFormatType),
+ eTempGrammar( eGrammar),
+ pCode(xGroup->mpCode ? &*xGroup->mpCode : new ScTokenArray(rDoc)),
+ rDocument( rDoc ),
+ pPrevious(nullptr),
+ pNext(nullptr),
+ pPreviousTrack(nullptr),
+ pNextTrack(nullptr),
+ aPos(rPos)
+{
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+}
+
+ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) :
+ bDirty( rCell.bDirty ),
+ bTableOpDirty( false ),
+ bChanged( rCell.bChanged ),
+ bRunning( false ),
+ bCompile( rCell.bCompile ),
+ bSubTotal( rCell.bSubTotal ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bNeedListening( false ),
+ mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ),
+ mbAllowNumberFormatChange(false),
+ mbPostponedDirty(false),
+ mbIsExtRef(false),
+ mbSeenInPath(false),
+ mbFreeFlying(false),
+ cMatrixFlag ( rCell.cMatrixFlag ),
+ nSeenInIteration(0),
+ nFormatType( rCell.nFormatType ),
+ aResult( rCell.aResult ),
+ eTempGrammar( rCell.eTempGrammar),
+ rDocument( rDoc ),
+ pPrevious(nullptr),
+ pNext(nullptr),
+ pPreviousTrack(nullptr),
+ pNextTrack(nullptr),
+ aPos(rPos)
+{
+ pCode = rCell.pCode->Clone().release();
+
+ // set back any errors and recompile
+ // not in the Clipboard - it must keep the received error flag
+ // Special Length=0: as bad cells are generated, then they are also retained
+ if ( pCode->GetCodeError() != FormulaError::NONE && !rDocument.IsClipboard() && pCode->GetLen() )
+ {
+ pCode->SetCodeError( FormulaError::NONE );
+ bCompile = true;
+ }
+ // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
+ bool bCompileLater = false;
+ bool bClipMode = rCell.rDocument.IsClipboard();
+
+ //update ScNameTokens
+ if (!rDocument.IsClipOrUndo() || rDoc.IsUndo())
+ {
+ if (!rDocument.IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
+ {
+ bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default);
+ formula::FormulaToken* pToken = nullptr;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ while((pToken = aIter.GetNextName())!= nullptr)
+ {
+ OpCode eOpCode = pToken->GetOpCode();
+ if (eOpCode == ocName)
+ adjustRangeName(pToken, rDoc, rCell.rDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
+ else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
+ adjustDBRange(pToken, rDoc, rCell.rDocument);
+ }
+ }
+
+ bool bCopyBetweenDocs = rDocument.GetPool() != rCell.rDocument.GetPool();
+ if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
+ {
+ pCode->ReadjustAbsolute3DReferences(rCell.rDocument, rDoc, rCell.aPos);
+ }
+
+ pCode->AdjustAbsoluteRefs( rCell.rDocument, rCell.aPos, aPos, bCopyBetweenDocs );
+ }
+
+ if (!rDocument.IsClipOrUndo())
+ {
+ if (&rDocument.GetSharedStringPool() != &rCell.rDocument.GetSharedStringPool())
+ pCode->ReinternStrings( rDocument.GetSharedStringPool());
+ pCode->AdjustReferenceOnCopy( aPos);
+ }
+
+ if( !bCompile )
+ { // Name references with references and ColRowNames
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ for (;;)
+ {
+ formula::FormulaToken* t = aIter.GetNextReferenceOrName();
+ if (!t || bCompile)
+ break;
+ if ( t->IsExternalRef() )
+ {
+ // External name, cell, and area references.
+ bCompile = true;
+ }
+ else if ( t->GetType() == svIndex )
+ {
+ const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
+ if( pRangeData )
+ {
+ if( pRangeData->HasReferences() )
+ bCompile = true;
+ }
+ else
+ bCompile = true; // invalid reference!
+ }
+ else if ( t->GetOpCode() == ocColRowName )
+ {
+ bCompile = true; // new lookup needed
+ bCompileLater = bClipMode;
+ }
+ }
+ }
+ if( bCompile )
+ {
+ if ( !bCompileLater && bClipMode )
+ {
+ // Merging ranges needs the actual positions after UpdateReference.
+ // ColRowNames and TableRefs need new lookup after positions are
+ // adjusted.
+ bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
+ pCode->HasOpCode( ocTableRef);
+ }
+ if ( !bCompileLater )
+ {
+ // bNoListening, not at all if in Clipboard/Undo,
+ // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
+ CompileTokenArray( true );
+ }
+ }
+
+ if( nCloneFlags & ScCloneFlags::StartListening )
+ StartListeningTo( rDoc );
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+}
+
+ScFormulaCell::~ScFormulaCell()
+{
+ rDocument.RemoveFromFormulaTrack( this );
+ rDocument.RemoveFromFormulaTree( this );
+ rDocument.RemoveSubTotalCell(this);
+ if (pCode->HasOpCode(ocMacro))
+ rDocument.GetMacroManager()->RemoveDependentCell(this);
+
+ if (rDocument.HasExternalRefManager())
+ rDocument.GetExternalRefManager()->removeRefCell(this);
+
+ if (!mxGroup || !mxGroup->mpCode)
+ // Formula token is not shared.
+ delete pCode;
+
+ if (mxGroup && mxGroup->mpTopCell == this)
+ mxGroup->mpTopCell = nullptr;
+}
+
+ScFormulaCell* ScFormulaCell::Clone() const
+{
+ return new ScFormulaCell(*this, rDocument, aPos);
+}
+
+ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const
+{
+ return new ScFormulaCell(*this, rDocument, rPos, ScCloneFlags::Default);
+}
+
+size_t ScFormulaCell::GetHash() const
+{
+ return pCode->GetHash();
+}
+
+OUString ScFormulaCell::GetFormula( const FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext ) const
+{
+ if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
+ {
+ return ScGlobal::GetErrorString(pCode->GetCodeError());
+ }
+ OUStringBuffer buffer;
+ if( cMatrixFlag == ScMatrixMode::Reference )
+ {
+ // Reference to another cell that contains a matrix formula.
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* p = aIter.GetNextReferenceRPN();
+ if( p )
+ {
+ /* FIXME: original GetFormula() code obtained
+ * pCell only if (!IsInChangeTrack()),
+ * GetEnglishFormula() omitted that test.
+ * Can we live without in all cases? */
+ ScFormulaCell* pCell = nullptr;
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(rDocument, aPos);
+ if (rDocument.ValidAddress(aAbs))
+ pCell = rDocument.GetFormulaCell(aAbs);
+
+ if (pCell)
+ {
+ return pCell->GetFormula( eGrammar, pContext );
+ }
+ else
+ {
+ ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
+ aComp.CreateStringFromTokenArray( buffer );
+ }
+ }
+ else
+ {
+ OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
+ }
+ }
+ else
+ {
+ ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
+ aComp.CreateStringFromTokenArray( buffer );
+ }
+
+ buffer.insert( 0, '=');
+ if( cMatrixFlag != ScMatrixMode::NONE )
+ {
+ buffer.insert( 0, '{');
+ buffer.append( '}');
+ }
+ return buffer.makeStringAndClear();
+}
+
+OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, const ScInterpreterContext* pContext ) const
+{
+ OUStringBuffer aBuf;
+ if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
+ {
+ ScTokenArray aCode(rCxt.getDoc());
+ aCode.AddToken( FormulaErrorToken( pCode->GetCodeError()));
+ ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
+ aComp.CreateStringFromTokenArray(aBuf);
+ return aBuf.makeStringAndClear();
+ }
+ else if( cMatrixFlag == ScMatrixMode::Reference )
+ {
+ // Reference to another cell that contains a matrix formula.
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* p = aIter.GetNextReferenceRPN();
+ if( p )
+ {
+ /* FIXME: original GetFormula() code obtained
+ * pCell only if (!IsInChangeTrack()),
+ * GetEnglishFormula() omitted that test.
+ * Can we live without in all cases? */
+ ScFormulaCell* pCell = nullptr;
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(rDocument, aPos);
+ if (rDocument.ValidAddress(aAbs))
+ pCell = rDocument.GetFormulaCell(aAbs);
+
+ if (pCell)
+ {
+ return pCell->GetFormula(rCxt);
+ }
+ else
+ {
+ ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
+ aComp.CreateStringFromTokenArray(aBuf);
+ }
+ }
+ else
+ {
+ OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
+ }
+ }
+ else
+ {
+ ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
+ aComp.CreateStringFromTokenArray(aBuf);
+ }
+
+ aBuf.insert( 0, '=');
+ if( cMatrixFlag != ScMatrixMode::NONE )
+ {
+ aBuf.insert( 0, '{');
+ aBuf.append( '}');
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
+{
+ MaybeInterpret();
+
+ if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell)
+ {
+ const ScMatrix* pMat = aResult.GetToken()->GetMatrix();
+ if (pMat)
+ {
+ pMat->GetDimensions( rCols, rRows );
+ if (pCode->IsHyperLink())
+ {
+ // Row 2 element is the URL that is not to be displayed and the
+ // result dimension not to be extended.
+ assert(rRows == 2);
+ rRows = 1;
+ }
+ return;
+ }
+ }
+ rCols = 0;
+ rRows = 0;
+}
+
+void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; }
+void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; }
+
+void ScFormulaCell::SetNeedsDirty( bool bVar )
+{
+ mbPostponedDirty = bVar;
+}
+
+void ScFormulaCell::SetNeedNumberFormat( bool bVal )
+{
+ mbNeedsNumberFormat = mbAllowNumberFormatChange = bVal;
+}
+
+void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
+ const FormulaGrammar::Grammar eGrammar )
+{
+ if ( rDocument.IsClipOrUndo() )
+ return;
+ bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ rDocument.RemoveFromFormulaTree( this );
+ // pCode may not deleted for queries, but must be empty
+ if ( pCode )
+ pCode->Clear();
+ ScTokenArray* pCodeOld = pCode;
+ ScCompiler aComp( rDocument, aPos, eGrammar);
+ pCode = aComp.CompileString( rFormula ).release();
+ assert(!mxGroup);
+ delete pCodeOld;
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
+ { // not recursive CompileTokenArray/Compile/CompileTokenArray
+ if ( rFormula[0] == '=' )
+ pCode->AddBad( rFormula.copy(1) );
+ else
+ pCode->AddBad( rFormula );
+ }
+ bCompile = true;
+ CompileTokenArray( bNoListening );
+ }
+ else
+ bChanged = true;
+
+ if ( bWasInFormulaTree )
+ rDocument.PutInFormulaTree( this );
+}
+
+void ScFormulaCell::Compile(
+ sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
+{
+ if ( rDocument.IsClipOrUndo() )
+ return;
+ bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ rDocument.RemoveFromFormulaTree( this );
+ // pCode may not deleted for queries, but must be empty
+ if ( pCode )
+ pCode->Clear();
+ ScTokenArray* pCodeOld = pCode;
+ ScCompiler aComp(rCxt, aPos);
+ pCode = aComp.CompileString( rFormula ).release();
+ assert(!mxGroup);
+ delete pCodeOld;
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
+ { // not recursive CompileTokenArray/Compile/CompileTokenArray
+ if ( rFormula[0] == '=' )
+ pCode->AddBad( rFormula.copy(1) );
+ else
+ pCode->AddBad( rFormula );
+ }
+ bCompile = true;
+ CompileTokenArray(rCxt, bNoListening);
+ }
+ else
+ bChanged = true;
+
+ if ( bWasInFormulaTree )
+ rDocument.PutInFormulaTree( this );
+}
+
+void ScFormulaCell::CompileTokenArray( bool bNoListening )
+{
+ // Not already compiled?
+ if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
+ }
+ else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
+ {
+ // RPN length may get changed
+ bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ rDocument.RemoveFromFormulaTree( this );
+
+ // Loading from within filter? No listening yet!
+ if( rDocument.IsInsertingFromOtherDoc() )
+ bNoListening = true;
+
+ if( !bNoListening && pCode->GetCodeLen() )
+ EndListeningTo( rDocument );
+ ScCompiler aComp(rDocument, aPos, *pCode, rDocument.GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
+ bSubTotal = aComp.CompileTokenArray();
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ nFormatType = aComp.GetNumFormatType();
+ bChanged = true;
+ aResult.SetToken( nullptr);
+ bCompile = false;
+ if ( !bNoListening )
+ StartListeningTo( rDocument );
+ }
+ if ( bWasInFormulaTree )
+ rDocument.PutInFormulaTree( this );
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+ }
+}
+
+void ScFormulaCell::CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNoListening )
+{
+ // Not already compiled?
+ if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ rCxt.setGrammar(eTempGrammar);
+ Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
+ }
+ else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
+ {
+ // RPN length may get changed
+ bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ rDocument.RemoveFromFormulaTree( this );
+
+ // Loading from within filter? No listening yet!
+ if( rDocument.IsInsertingFromOtherDoc() )
+ bNoListening = true;
+
+ if( !bNoListening && pCode->GetCodeLen() )
+ EndListeningTo( rDocument );
+ ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
+ bSubTotal = aComp.CompileTokenArray();
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ nFormatType = aComp.GetNumFormatType();
+ bChanged = true;
+ aResult.SetToken( nullptr);
+ bCompile = false;
+ if ( !bNoListening )
+ StartListeningTo( rDocument );
+ }
+ if ( bWasInFormulaTree )
+ rDocument.PutInFormulaTree( this );
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+ }
+}
+
+void ScFormulaCell::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
+{
+ if ( cMatrixFlag == ScMatrixMode::Reference )
+ { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
+ // just establish listeners
+ StartListeningTo( rDocument );
+ return ;
+ }
+
+ // Error constant formula cell stays as is.
+ if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
+ return;
+
+ // Compilation changes RPN count, remove and reinsert to FormulaTree if it
+ // was in to update its count.
+ bool bWasInFormulaTree = rDocument.IsInFormulaTree( this);
+ if (bWasInFormulaTree)
+ rDocument.RemoveFromFormulaTree( this);
+ rCxt.setGrammar(eTempGrammar);
+ ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
+ OUString aFormula, aFormulaNmsp;
+ aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
+ rDocument.DecXMLImportedFormulaCount( aFormula.getLength() );
+ rProgress.SetStateCountDownOnPercent( rDocument.GetXMLImportedFormulaCount() );
+ // pCode may not deleted for queries, but must be empty
+ pCode->Clear();
+
+ bool bDoCompile = true;
+
+ if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
+ {
+ ScAddress aPreviousCell( aPos );
+ aPreviousCell.IncRow( -1 );
+ ScFormulaCell *pPreviousCell = rDocument.GetFormulaCell( aPreviousCell );
+ if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
+ {
+ // Build formula string using the tokens from the previous cell,
+ // but use the current cell position.
+ ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
+ OUStringBuffer aShouldBeBuf;
+ aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
+
+ // The initial '=' is optional in ODFF.
+ const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
+ if (aFormula.getLength() == aShouldBeBuf.getLength() + nLeadingEqual &&
+ aFormula.match( aShouldBeBuf, nLeadingEqual))
+ {
+ // Put them in the same formula group.
+ ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
+ if (!xGroup) // Last cell is not grouped yet. Start a new group.
+ xGroup = pPreviousCell->CreateCellGroup(1, false);
+ ++xGroup->mnLength;
+ SetCellGroup( xGroup );
+
+ // Do setup here based on previous cell.
+
+ nFormatType = pPreviousCell->nFormatType;
+ bSubTotal = pPreviousCell->bSubTotal;
+ bChanged = true;
+ bCompile = false;
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+
+ bDoCompile = false;
+ pCode = pPreviousCell->pCode;
+ if (pPreviousCell->mbIsExtRef)
+ rDocument.GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
+ }
+ }
+ }
+
+ if (bDoCompile)
+ {
+ ScTokenArray* pCodeOld = pCode;
+ pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release();
+ assert(!mxGroup);
+ delete pCodeOld;
+
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ if ( !pCode->GetLen() )
+ {
+ if ( !aFormula.isEmpty() && aFormula[0] == '=' )
+ pCode->AddBad( aFormula.copy( 1 ) );
+ else
+ pCode->AddBad( aFormula );
+ }
+ bSubTotal = aComp.CompileTokenArray();
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ nFormatType = aComp.GetNumFormatType();
+ bChanged = true;
+ bCompile = false;
+ }
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+ }
+ else
+ bChanged = true;
+ }
+
+ // After loading, it must be known if ocDde/ocWebservice is in any formula
+ // (for external links warning, CompileXML is called at the end of loading XML file)
+ rDocument.CheckLinkFormulaNeedingCheck(*pCode);
+
+ //volatile cells must be added here for import
+ if( !pCode->IsRecalcModeNormal() || pCode->IsRecalcModeForced())
+ {
+ // During load, only those cells that are marked explicitly dirty get
+ // recalculated. So we need to set it dirty here.
+ SetDirtyVar();
+ rDocument.AppendToFormulaTrack(this);
+ // Do not call TrackFormulas() here, not all listeners may have been
+ // established, postponed until ScDocument::CompileXML() finishes.
+ }
+ else if (bWasInFormulaTree)
+ rDocument.PutInFormulaTree(this);
+}
+
+void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
+{
+ bool bNewCompiled = false;
+ // If a Calc 1.0-doc is read, we have a result, but no token array
+ if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ rCxt.setGrammar(eTempGrammar);
+ Compile(rCxt, aResult.GetHybridFormula(), true);
+ aResult.SetToken( nullptr);
+ bDirty = true;
+ bNewCompiled = true;
+ }
+ // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
+ if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
+ {
+ ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
+ bSubTotal = aComp.CompileTokenArray();
+ nFormatType = aComp.GetNumFormatType();
+ bDirty = true;
+ bCompile = false;
+ bNewCompiled = true;
+
+ if (bSubTotal)
+ rDocument.AddSubTotalCell(this);
+ }
+
+ // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
+ // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
+ // We iron this out here for all systems, such that we also have an Err503 here.
+ if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
+ {
+ OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
+ aResult.SetResultError( FormulaError::IllegalFPOperation );
+ bDirty = true;
+ }
+
+ // DoubleRefs for binary operators were always a Matrix before version v5.0.
+ // Now this is only the case when in an array formula, otherwise it's an implicit intersection
+ if ( ScDocument::GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
+ GetMatrixFlag() == ScMatrixMode::NONE && pCode->HasMatrixDoubleRefOps() )
+ {
+ cMatrixFlag = ScMatrixMode::Formula;
+ SetMatColsRows( 1, 1);
+ }
+
+ // Do the cells need to be calculated? After Load cells can contain an error code, and then start
+ // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
+ if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
+ {
+ if (bStartListening)
+ StartListeningTo(rDocument);
+
+ if( !pCode->IsRecalcModeNormal() )
+ bDirty = true;
+ }
+ if ( pCode->IsRecalcModeAlways() )
+ { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
+ // for each F9
+ bDirty = true;
+ }
+ // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
+}
+
+bool ScFormulaCell::MarkUsedExternalReferences()
+{
+ return pCode && rDocument.MarkUsedExternalReferences(*pCode, aPos);
+}
+
+namespace {
+class RecursionCounter
+{
+ ScRecursionHelper& rRec;
+ bool bStackedInIteration;
+#if defined DBG_UTIL && !defined NDEBUG
+ const ScFormulaCell* cell;
+#endif
+public:
+ RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
+ : rRec(r)
+#if defined DBG_UTIL && !defined NDEBUG
+ , cell(p)
+#endif
+ {
+ bStackedInIteration = rRec.IsDoingIteration();
+ if (bStackedInIteration)
+ rRec.GetRecursionInIterationStack().push( p);
+ rRec.IncRecursionCount();
+ }
+ ~RecursionCounter()
+ {
+ rRec.DecRecursionCount();
+ if (bStackedInIteration)
+ {
+#if defined DBG_UTIL && !defined NDEBUG
+ assert(rRec.GetRecursionInIterationStack().top() == cell);
+#endif
+ rRec.GetRecursionInIterationStack().pop();
+ }
+ }
+};
+
+// Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group".
+// Remove the group again at the end, since there are some places throughout the code
+// that do not handle well groups with just 1 cell. Remove the groups only when the recursion level
+// reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing
+// a group immediately would remove the info), for this reason affected cells are stored in the recursion
+// helper.
+struct TemporaryCellGroupMaker
+{
+ TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable )
+ : mCell( cell )
+ , mEnabled( enable )
+ {
+ if( mEnabled && mCell->GetCellGroup() == nullptr )
+ {
+ mCell->CreateCellGroup( 1, false );
+ mCell->GetDocument().GetRecursionHelper().AddTemporaryGroupCell( mCell );
+ }
+ }
+ ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE
+ {
+ if( mEnabled )
+ mCell->GetDocument().GetRecursionHelper().CleanTemporaryGroupCells();
+ }
+ ScFormulaCell* mCell;
+ const bool mEnabled;
+};
+
+} // namespace
+
+bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset)
+{
+ ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
+ bool bGroupInterpreted = false;
+
+ // The result would possibly depend on a cell without a valid value, bail out
+ // the entire dependency computation.
+ if (rRecursionHelper.IsAbortingDependencyComputation())
+ return false;
+
+ if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent())
+ return bGroupInterpreted;
+
+ static ForceCalculationType forceType = ScCalcConfig::getForceCalculationType();
+ TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore );
+
+ ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
+
+ if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
+ rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
+ {
+ // This call arose from a dependency calculation and we just found a cycle.
+ // This will mark all elements in the cycle as parts-of-cycle.
+ ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
+ // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet.
+ // If there is a genuine circular reference, it will be marked so when all groups
+ // in the cycle get out of dependency evaluation mode.
+ // But returning without calculation a new value means other cells depending
+ // on this one would use a possibly invalid value, so ensure the dependency
+ // computation is aborted without resetting the dirty flag of any cell.
+ rRecursionHelper.AbortDependencyComputation();
+ return bGroupInterpreted;
+ }
+
+#if DEBUG_CALCULATION
+ static bool bDebugCalculationInit = true;
+ if (bDebugCalculationInit)
+ {
+ aDC.maTrigger = aDebugCalculationTriggerAddress;
+ aDC.mbPrintResults = true;
+ bDebugCalculationInit = false;
+ }
+ DebugCalculationStacker aDebugEntry(aPos, rDocument);
+#endif
+
+ if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
+ return bGroupInterpreted; // no double/triple processing
+
+ //FIXME:
+ // If the call originates from a Reschedule in DdeLink update, leave dirty
+ // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
+ if ( rDocument.IsInDdeLinkUpdate() )
+ return bGroupInterpreted;
+
+ if (bRunning)
+ {
+ if (!rDocument.GetDocOptions().IsIter())
+ {
+ aResult.SetResultError( FormulaError::CircularReference );
+ return bGroupInterpreted;
+ }
+
+ if (aResult.GetResultError() == FormulaError::CircularReference)
+ aResult.SetResultError( FormulaError::NONE );
+
+ // Start or add to iteration list.
+ if (!rRecursionHelper.IsDoingIteration() ||
+ !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
+ rRecursionHelper.SetInIterationReturn( true);
+
+ return bGroupInterpreted;
+ }
+ // no multiple interprets for GetErrCode, IsValue, GetValue and
+ // different entry point recursions. Would also lead to premature
+ // convergence in iterations.
+ if (rRecursionHelper.GetIteration() && nSeenInIteration ==
+ rRecursionHelper.GetIteration())
+ return bGroupInterpreted;
+
+ bool bOldRunning = bRunning;
+ if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
+ {
+ bRunning = true;
+ rRecursionHelper.SetInRecursionReturn( true);
+ }
+ else
+ {
+ rDocument.IncInterpretLevel();
+
+#if DEBUG_CALCULATION
+ aDC.enterGroup();
+#endif
+ bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
+ bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset);
+ bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;
+
+#if DEBUG_CALCULATION
+ aDC.leaveGroup();
+#endif
+ if (!bGroupInterpreted)
+ {
+ // This call resulted from a dependency calculation for a multigroup-threading attempt,
+ // but found dependency among the groups.
+ if (!rRecursionHelper.AreGroupsIndependent())
+ {
+ rDocument.DecInterpretLevel();
+ return bGroupInterpreted;
+ }
+ // Dependency calc inside InterpretFormulaGroup() failed due to
+ // detection of a cycle and there are parent FG's in the cycle.
+ // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
+ if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
+ {
+ rDocument.DecInterpretLevel();
+ return bGroupInterpreted;
+ }
+
+ ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
+ ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
+ InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
+ }
+
+ rDocument.DecInterpretLevel();
+ }
+
+ // While leaving a recursion or iteration stack, insert its cells to the
+ // recursion list in reverse order.
+ if (rRecursionHelper.IsInReturn())
+ {
+ bool bFreeFlyingInserted = false;
+ if (rRecursionHelper.GetRecursionCount() > 0 || !rRecursionHelper.IsDoingRecursion())
+ {
+ rRecursionHelper.Insert( this, bOldRunning, aResult);
+ bFreeFlyingInserted = mbFreeFlying;
+ }
+ bool bIterationFromRecursion = false;
+ bool bResumeIteration = false;
+ do
+ {
+ if ((rRecursionHelper.IsInIterationReturn() &&
+ rRecursionHelper.GetRecursionCount() == 0 &&
+ !rRecursionHelper.IsDoingIteration()) ||
+ bIterationFromRecursion || bResumeIteration)
+ {
+ bool & rDone = rRecursionHelper.GetConvergingReference();
+ rDone = false;
+ if (!bIterationFromRecursion && bResumeIteration)
+ {
+ bResumeIteration = false;
+ // Resuming iteration expands the range.
+ ScFormulaRecursionList::const_iterator aOldStart(
+ rRecursionHelper.GetLastIterationStart());
+ rRecursionHelper.ResumeIteration();
+ // Mark new cells being in iteration.
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart()); aIter !=
+ aOldStart; ++aIter)
+ {
+ ScFormulaCell* pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = true;
+ }
+ // Mark older cells dirty again, in case they converted
+ // without accounting for all remaining cells in the circle
+ // that weren't touched so far, e.g. conditional. Restore
+ // backupped result.
+ sal_uInt16 nIteration = rRecursionHelper.GetIteration();
+ for (ScFormulaRecursionList::const_iterator aIter(
+ aOldStart); aIter !=
+ rRecursionHelper.GetIterationEnd(); ++aIter)
+ {
+ ScFormulaCell* pIterCell = (*aIter).pCell;
+ if (pIterCell->nSeenInIteration == nIteration)
+ {
+ if (!pIterCell->bDirty || aIter == aOldStart)
+ {
+ pIterCell->aResult = (*aIter).aPreviousResult;
+ }
+ --pIterCell->nSeenInIteration;
+ }
+ pIterCell->bDirty = true;
+ }
+ }
+ else
+ {
+ bResumeIteration = false;
+ // Close circle once. If 'this' is self-referencing only
+ // (e.g. counter or self-adder) then it is already
+ // implicitly closed.
+ /* TODO: does this even make sense anymore? The last cell
+ * added above with rRecursionHelper.Insert() should always
+ * be 'this', shouldn't it? */
+ if (rRecursionHelper.GetList().size() > 1)
+ {
+ ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell;
+ if (pLastCell != this)
+ {
+ rDocument.IncInterpretLevel();
+ ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
+ pLastCell->InterpretTail(
+ *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
+ rDocument.DecInterpretLevel();
+ }
+ }
+ // Start at 1, init things.
+ rRecursionHelper.StartIteration();
+ // Mark all cells being in iteration. Reset results to
+ // original values, formula cells have been interpreted
+ // already, discard that step.
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart()); aIter !=
+ rRecursionHelper.GetIterationEnd(); ++aIter)
+ {
+ ScFormulaCell* pIterCell = (*aIter).pCell;
+ pIterCell->aResult = (*aIter).aPreviousResult;
+ pIterCell->bIsIterCell = true;
+ }
+ }
+ bIterationFromRecursion = false;
+ sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount();
+ for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
+ rRecursionHelper.IncIteration())
+ {
+ rDone = false;
+ bool bFirst = true;
+ for ( ScFormulaRecursionList::iterator aIter(
+ rRecursionHelper.GetIterationStart()); aIter !=
+ rRecursionHelper.GetIterationEnd() &&
+ !rRecursionHelper.IsInReturn(); ++aIter)
+ {
+ ScFormulaCell* pIterCell = (*aIter).pCell;
+ if (pIterCell->IsDirtyOrInTableOpDirty() &&
+ rRecursionHelper.GetIteration() !=
+ pIterCell->GetSeenInIteration())
+ {
+ (*aIter).aPreviousResult = pIterCell->aResult;
+ rDocument.IncInterpretLevel();
+ ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
+ pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
+ rDocument.DecInterpretLevel();
+ }
+ if (bFirst)
+ {
+ rDone = !pIterCell->IsDirtyOrInTableOpDirty();
+ bFirst = false;
+ }
+ else if (rDone)
+ {
+ rDone = !pIterCell->IsDirtyOrInTableOpDirty();
+ }
+ }
+ if (rRecursionHelper.IsInReturn())
+ {
+ bResumeIteration = true;
+ break; // for
+ // Don't increment iteration.
+ }
+ }
+ if (!bResumeIteration)
+ {
+ if (rDone)
+ {
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart());
+ aIter != rRecursionHelper.GetIterationEnd();
+ ++aIter)
+ {
+ ScFormulaCell* pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = false;
+ pIterCell->nSeenInIteration = 0;
+ pIterCell->bRunning = (*aIter).bOldRunning;
+ }
+ }
+ else
+ {
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart());
+ aIter != rRecursionHelper.GetIterationEnd();
+ ++aIter)
+ {
+ ScFormulaCell* pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = false;
+ pIterCell->nSeenInIteration = 0;
+ pIterCell->bRunning = (*aIter).bOldRunning;
+ pIterCell->ResetDirty();
+ // The difference to Excel is that Excel does not
+ // produce an error for non-convergence thus a
+ // delta of 0.001 still works to execute the
+ // maximum number of iterations and display the
+ // results no matter if the result anywhere reached
+ // near delta, but also never indicates whether the
+ // result actually makes sense in case of
+ // non-counter context. Calc does check the delta
+ // in every case. If we wanted to support what
+ // Excel does then add another option "indicate
+ // non-convergence error" (default on) and execute
+ // the following block only if set.
+#if 1
+ // If one cell didn't converge, all cells of this
+ // circular dependency don't, no matter whether
+ // single cells did.
+ pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
+ pIterCell->bChanged = true;
+#endif
+ }
+ }
+ // End this iteration and remove entries.
+ rRecursionHelper.EndIteration();
+ bResumeIteration = rRecursionHelper.IsDoingIteration();
+ }
+ }
+ if (rRecursionHelper.IsInRecursionReturn() &&
+ rRecursionHelper.GetRecursionCount() == 0 &&
+ !rRecursionHelper.IsDoingRecursion())
+ {
+ bIterationFromRecursion = false;
+ // Iterate over cells known so far, start with the last cell
+ // encountered, inserting new cells if another recursion limit
+ // is reached. Repeat until solved.
+ rRecursionHelper.SetDoingRecursion( true);
+ do
+ {
+ rRecursionHelper.SetInRecursionReturn( false);
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart());
+ !rRecursionHelper.IsInReturn() && aIter !=
+ rRecursionHelper.GetIterationEnd(); ++aIter)
+ {
+ ScFormulaCell* pCell = (*aIter).pCell;
+ if (pCell->IsDirtyOrInTableOpDirty())
+ {
+ rDocument.IncInterpretLevel();
+ ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable());
+ pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
+ rDocument.DecInterpretLevel();
+ if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
+ pCell->bRunning = (*aIter).bOldRunning;
+ }
+ }
+ } while (rRecursionHelper.IsInRecursionReturn());
+ rRecursionHelper.SetDoingRecursion( false);
+ if (rRecursionHelper.IsInIterationReturn())
+ {
+ if (!bResumeIteration)
+ bIterationFromRecursion = true;
+ }
+ else if (bResumeIteration ||
+ rRecursionHelper.IsDoingIteration())
+ rRecursionHelper.GetList().erase(
+ rRecursionHelper.GetIterationStart(),
+ rRecursionHelper.GetLastIterationStart());
+ else
+ rRecursionHelper.Clear();
+ }
+ } while (bIterationFromRecursion || bResumeIteration);
+
+ if (bFreeFlyingInserted)
+ {
+ // Remove this from recursion list, it may get deleted.
+ // It additionally also should mean that the recursion/iteration
+ // ends here as it must had been triggered by this free-flying
+ // out-of-sheets cell
+ const bool bOnlyThis = (rRecursionHelper.GetList().size() == 1);
+ rRecursionHelper.GetList().remove_if([this](const ScFormulaRecursionEntry& r){return r.pCell == this;});
+ if (bOnlyThis)
+ {
+ assert(rRecursionHelper.GetList().empty());
+ if (rRecursionHelper.GetList().empty())
+ rRecursionHelper.EndIteration();
+ }
+ }
+ }
+
+#if DEBUG_CALCULATION
+ FormulaError nErr = aResult.GetResultError();
+ if (nErr != FormulaError::NONE)
+ aDC.storeResultError( nErr);
+ else if (aResult.IsValue())
+ aDC.storeResult( aResult.GetDouble());
+ else
+ aDC.storeResult( aResult.GetString());
+#endif
+
+ return bGroupInterpreted;
+}
+
+void ScFormulaCell::InterpretTail( ScInterpreterContext& rContext, ScInterpretTailParameter eTailParam )
+{
+ RecursionCounter aRecursionCounter( rDocument.GetRecursionHelper(), this);
+ // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
+ if(bIsIterCell)
+ nSeenInIteration = rDocument.GetRecursionHelper().GetIteration();
+ if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
+ {
+ // #i11719# no RPN and no error and no token code but result string present
+ // => interpretation of this cell during name-compilation and unknown names
+ // => can't exchange underlying code array in CompileTokenArray() /
+ // Compile() because interpreter's token iterator would crash or pCode
+ // would be deleted twice if this cell was interpreted during
+ // compilation.
+ // This should only be a temporary condition and, since we set an
+ // error, if ran into it again we'd bump into the dirty-clearing
+ // condition further down.
+ if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ pCode->SetCodeError( FormulaError::NoCode );
+ // This is worth an assertion; if encountered in daily work
+ // documents we might need another solution. Or just confirm correctness.
+ return;
+ }
+ CompileTokenArray();
+ }
+
+ if( pCode->GetCodeLen() )
+ {
+ std::unique_ptr<ScInterpreter> pScopedInterpreter;
+ ScInterpreter* pInterpreter;
+ if (rContext.pInterpreter)
+ {
+ pInterpreter = rContext.pInterpreter;
+ pInterpreter->Init(this, aPos, *pCode);
+ }
+ else
+ {
+ pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rContext, aPos, *pCode ));
+ pInterpreter = pScopedInterpreter.get();
+ }
+
+ FormulaError nOldErrCode = aResult.GetResultError();
+ if ( nSeenInIteration == 0 )
+ { // Only the first time
+ // With bChanged=false, if a newly compiled cell has a result of
+ // 0.0, no change is detected and the cell will not be repainted.
+ // bChanged = false;
+ aResult.SetResultError( FormulaError::NONE );
+ }
+
+ switch ( aResult.GetResultError() )
+ {
+ case FormulaError::CircularReference : // will be determined again if so
+ aResult.SetResultError( FormulaError::NONE );
+ break;
+ default: break;
+ }
+
+ bool bOldRunning = bRunning;
+ bRunning = true;
+ pInterpreter->Interpret();
+ if (rDocument.GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
+ {
+ if (nSeenInIteration > 0)
+ --nSeenInIteration; // retry when iteration is resumed
+
+ if ( aResult.GetType() == formula::svUnknown )
+ aResult.SetToken( pInterpreter->GetResultToken().get() );
+
+ return;
+ }
+ bRunning = bOldRunning;
+
+ // The result may be invalid or depend on another invalid result, just abort
+ // without updating the cell value. Since the dirty flag will not be reset,
+ // the proper value will be computed later.
+ if(rDocument.GetRecursionHelper().IsAbortingDependencyComputation())
+ return;
+
+ // #i102616# For single-sheet saving consider only content changes, not format type,
+ // because format type isn't set on loading (might be changed later)
+ bool bContentChanged = false;
+
+ // Do not create a HyperLink() cell if the formula results in an error.
+ if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
+ pCode->SetHyperLink(false);
+
+ if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
+ {
+ bChanged = true;
+
+ if (pInterpreter->GetError() == FormulaError::RetryCircular)
+ {
+ // Array formula matrix calculation corner case. Keep dirty
+ // state, do not remove from formula tree or anything else, but
+ // store FormulaError::CircularReference in case this cell does not get
+ // recalculated.
+ aResult.SetResultError( FormulaError::CircularReference);
+ return;
+ }
+
+ ResetDirty();
+ }
+
+ if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
+ {
+ bool bIsValue = aResult.IsValue(); // the previous type
+ // Did it converge?
+ if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
+ pInterpreter->GetNumResult() - aResult.GetDouble()) <=
+ rDocument.GetDocOptions().GetIterEps()) ||
+ (!bIsValue && pInterpreter->GetResultType() == svString &&
+ pInterpreter->GetStringResult() == aResult.GetString()))
+ {
+ // A convergence in the first iteration doesn't necessarily
+ // mean that it's done, it may be as not all related cells
+ // of a circle changed their values yet. If the set really
+ // converges it will do so also during the next iteration. This
+ // fixes situations like of #i44115#. If this wasn't wanted an
+ // initial "uncalculated" value would be needed for all cells
+ // of a circular dependency => graph needed before calculation.
+ if (nSeenInIteration > 1 ||
+ rDocument.GetDocOptions().GetIterCount() == 1)
+ {
+ ResetDirty();
+ }
+ }
+ }
+
+ // New error code?
+ if( pInterpreter->GetError() != nOldErrCode )
+ {
+ bChanged = true;
+ // bContentChanged only has to be set if the file content would be changed
+ if ( aResult.GetCellResultType() != svUnknown )
+ bContentChanged = true;
+ }
+
+ ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
+
+ // For IF() and other jumps or changed formatted source data the result
+ // format may change for different runs, e.g. =IF(B1,B1) with first
+ // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
+ // displayed as TRUE. Do not force a general format though if
+ // mbNeedsNumberFormat is set (because there was a general format..).
+ // Note that nFormatType may be out of sync here if a format was
+ // applied or cleared after the last run, but obtaining the current
+ // format always just to check would be expensive. There may be
+ // cases where the format should be changed but is not. If that turns
+ // out to be a real problem then obtain the current format type after
+ // the initial check when needed.
+ bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
+ !SvNumberFormatter::IsCompatible( nFormatType, pInterpreter->GetRetFormatType()));
+
+ // We have some requirements additionally to IsCompatible().
+ // * Do not apply a NumberFormat::LOGICAL if the result value is not
+ // 1.0 or 0.0
+ // * Do not override an already set numeric number format if the result
+ // is of type NumberFormat::LOGICAL, it could be user applied.
+ // On the other hand, for an empty jump path instead of FALSE an
+ // unexpected for example 0% could be displayed. YMMV.
+ // * Never override a non-standard number format that indicates user
+ // applied.
+ // * NumberFormat::TEXT does not force a change.
+ if (bForceNumberFormat)
+ {
+ sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
+ if (nRetType == SvNumFormatType::LOGICAL)
+ {
+ double fVal = aNewResult.GetDouble();
+ if (fVal != 1.0 && fVal != 0.0)
+ bForceNumberFormat = false;
+ else
+ {
+ nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
+ nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
+ switch (nFormatType)
+ {
+ case SvNumFormatType::PERCENT:
+ case SvNumFormatType::CURRENCY:
+ case SvNumFormatType::SCIENTIFIC:
+ case SvNumFormatType::FRACTION:
+ bForceNumberFormat = false;
+ break;
+ case SvNumFormatType::NUMBER:
+ if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
+ bForceNumberFormat = false;
+ break;
+ default: break;
+ }
+ }
+ }
+ else if (nRetType == SvNumFormatType::TEXT)
+ {
+ bForceNumberFormat = false;
+ }
+ if (bForceNumberFormat)
+ {
+ if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
+ nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
+ }
+ if (nOldFormatIndex !=
+ ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
+ bForceNumberFormat = false;
+ }
+ }
+
+ if( mbNeedsNumberFormat || bForceNumberFormat )
+ {
+ bool bSetFormat = true;
+ const SvNumFormatType nOldFormatType = nFormatType;
+ nFormatType = pInterpreter->GetRetFormatType();
+ sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
+
+ if (nFormatType == SvNumFormatType::TEXT)
+ {
+ // Don't set text format as hard format.
+ bSetFormat = false;
+ }
+ else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
+ {
+ // In a matrix range do not set an (inherited) logical format
+ // as hard format if the value does not represent a strict TRUE
+ // or FALSE value. But do set for a top left error value so
+ // following matrix cells can inherit for non-error values.
+ // This solves a problem with IF() expressions in array context
+ // where incidentally the top left element results in logical
+ // type but some others don't. It still doesn't solve the
+ // reverse case though, where top left is not logical type but
+ // some other elements should be. We'd need to transport type
+ // or format information on arrays.
+ StackVar eNewCellResultType = aNewResult.GetCellResultType();
+ if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
+ {
+ if (eNewCellResultType != svDouble)
+ {
+ bSetFormat = false;
+ nFormatType = nOldFormatType; // that? or number?
+ }
+ else
+ {
+ double fVal = aNewResult.GetDouble();
+ if (fVal != 1.0 && fVal != 0.0)
+ {
+ bSetFormat = false;
+ nFormatType = SvNumFormatType::NUMBER;
+ }
+ }
+ }
+ }
+
+ if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)))
+ nFormatIndex = ScGlobal::GetStandardFormat(*rContext.GetFormatTable(),
+ nFormatIndex, nFormatType);
+
+ // Do not replace a General format (which was the reason why
+ // mbNeedsNumberFormat was set) with a General format.
+ // 1. setting a format has quite some overhead in the
+ // ScPatternAttr/ScAttrArray handling, even if identical.
+ // 2. the General formats may be of different locales.
+ // XXX if mbNeedsNumberFormat was set even if the current format
+ // was not General then we'd have to obtain the current format here
+ // and check at least the types.
+ const bool bSetNumberFormat = bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0));
+ if (bSetNumberFormat && !rDocument.IsInLayoutStrings())
+ {
+ // set number format explicitly
+ if (!rDocument.IsThreadedGroupCalcInProgress())
+ rDocument.SetNumberFormat( aPos, nFormatIndex );
+ else
+ {
+ // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work
+ // to the main thread. Since thread calculations operate on formula groups,
+ // it's enough to store just the row.
+ DelayedSetNumberFormat data = { aPos.Col(), aPos.Row(), nFormatIndex };
+ rContext.maDelayedSetNumberFormat.push_back( data );
+ }
+ bChanged = true;
+ }
+
+ // Currently (2019-05-10) nothing else can cope with a duration
+ // format type, change to time as it was before.
+ if (nFormatType == SvNumFormatType::DURATION)
+ nFormatType = SvNumFormatType::TIME;
+
+ mbNeedsNumberFormat = false;
+ }
+
+ // In case of changes just obtain the result, no temporary and
+ // comparison needed anymore.
+ if (bChanged)
+ {
+ // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
+ // Also handle special cases of initial results after loading.
+ if ( !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
+ {
+ StackVar eOld = aResult.GetCellResultType();
+ StackVar eNew = aNewResult.GetCellResultType();
+ if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
+ {
+ // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
+ // -> no change
+ }
+ else
+ {
+ if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
+ eOld = svString; // ScHybridCellToken has a valid GetString method
+
+ // #i106045# use approxEqual to compare with stored value
+ bContentChanged = (eOld != eNew ||
+ (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
+ (eNew == svString && aResult.GetString() != aNewResult.GetString()));
+ }
+ }
+
+ aResult.SetToken( pInterpreter->GetResultToken().get() );
+ }
+ else
+ {
+ StackVar eOld = aResult.GetCellResultType();
+ StackVar eNew = aNewResult.GetCellResultType();
+ bChanged = (eOld != eNew ||
+ (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
+ (eNew == svString && aResult.GetString() != aNewResult.GetString()));
+
+ // #i102616# handle special cases of initial results after loading
+ // (only if the sheet is still marked unchanged)
+ if ( bChanged && !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
+ {
+ if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
+ ((eOld == svHybridCell) &&
+ eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
+ (eOld == svDouble && eNew == svDouble &&
+ rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
+ {
+ // no change, see above
+ }
+ else
+ bContentChanged = true;
+ }
+
+ aResult.Assign( aNewResult);
+ }
+
+ // Precision as shown?
+ if ( aResult.IsValue() && pInterpreter->GetError() == FormulaError::NONE
+ && rDocument.GetDocOptions().IsCalcAsShown()
+ && nFormatType != SvNumFormatType::DATE
+ && nFormatType != SvNumFormatType::TIME
+ && nFormatType != SvNumFormatType::DATETIME )
+ {
+ sal_uInt32 nFormat = rDocument.GetNumberFormat( rContext, aPos );
+ aResult.SetDouble( rDocument.RoundValueAsShown(
+ aResult.GetDouble(), nFormat, &rContext));
+ }
+ if (eTailParam == SCITP_NORMAL)
+ {
+ ResetDirty();
+ }
+ if( aResult.GetMatrix() )
+ {
+ // If the formula wasn't entered as a matrix formula, live on with
+ // the upper left corner and let reference counting delete the matrix.
+ if( cMatrixFlag != ScMatrixMode::Formula && !pCode->IsHyperLink() )
+ aResult.SetToken( aResult.GetCellResultToken().get());
+ }
+ if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
+ {
+ // Coded double error may occur via filter import.
+ FormulaError nErr = GetDoubleErrorValue( aResult.GetDouble());
+ aResult.SetResultError( nErr);
+ bChanged = bContentChanged = true;
+ }
+
+ if (bContentChanged && rDocument.IsStreamValid(aPos.Tab()))
+ {
+ // pass bIgnoreLock=true, because even if called from pending row height update,
+ // a changed result must still reset the stream flag
+ rDocument.SetStreamValid(aPos.Tab(), false, true);
+ }
+ if ( !rDocument.IsThreadedGroupCalcInProgress() && !pCode->IsRecalcModeAlways() )
+ rDocument.RemoveFromFormulaTree( this );
+
+ // FORCED cells also immediately tested for validity (start macro possibly)
+
+ if ( pCode->IsRecalcModeForced() )
+ {
+ sal_uLong nValidation = rDocument.GetAttr(
+ aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
+ if ( nValidation )
+ {
+ const ScValidationData* pData = rDocument.GetValidationEntry( nValidation );
+ ScRefCellValue aTmpCell(this);
+ if ( pData && !pData->IsDataValid(aTmpCell, aPos))
+ pData->DoCalcError( this );
+ }
+ }
+
+ // Reschedule slows the whole thing down considerably, thus only execute on percent change
+ if (!rDocument.IsThreadedGroupCalcInProgress())
+ {
+ ScProgress *pProgress = ScProgress::GetInterpretProgress();
+ if (pProgress && pProgress->Enabled())
+ {
+ pProgress->SetStateCountDownOnPercent(
+ rDocument.GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
+ }
+
+ switch (pInterpreter->GetVolatileType())
+ {
+ case ScInterpreter::VOLATILE:
+ // Volatile via built-in volatile functions. No actions needed.
+ break;
+ case ScInterpreter::VOLATILE_MACRO:
+ // The formula contains a volatile macro.
+ pCode->SetExclusiveRecalcModeAlways();
+ rDocument.PutInFormulaTree(this);
+ StartListeningTo(rDocument);
+ break;
+ case ScInterpreter::NOT_VOLATILE:
+ if (pCode->IsRecalcModeAlways())
+ {
+ // The formula was previously volatile, but no more.
+ EndListeningTo(rDocument);
+ pCode->SetExclusiveRecalcModeNormal();
+ }
+ else
+ {
+ // non-volatile formula. End listening to the area in case
+ // it's listening due to macro module change.
+ rDocument.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
+ }
+ rDocument.RemoveFromFormulaTree(this);
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ else
+ {
+ // Cells with compiler errors should not be marked dirty forever
+ OSL_ENSURE( pCode->GetCodeError() != FormulaError::NONE, "no RPN code and no errors ?!?!" );
+ ResetDirty();
+ }
+
+ pCode->ClearRecalcModeMustAfterImport();
+}
+
+void ScFormulaCell::HandleStuffAfterParallelCalculation(ScInterpreter* pInterpreter)
+{
+ if( !pCode->GetCodeLen() )
+ return;
+
+ if ( !pCode->IsRecalcModeAlways() )
+ rDocument.RemoveFromFormulaTree( this );
+
+ std::unique_ptr<ScInterpreter> pScopedInterpreter;
+ if (pInterpreter)
+ pInterpreter->Init(this, aPos, *pCode);
+ else
+ {
+ pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode ));
+ pInterpreter = pScopedInterpreter.get();
+ }
+
+ switch (pInterpreter->GetVolatileType())
+ {
+ case ScInterpreter::VOLATILE_MACRO:
+ // The formula contains a volatile macro.
+ pCode->SetExclusiveRecalcModeAlways();
+ rDocument.PutInFormulaTree(this);
+ StartListeningTo(rDocument);
+ break;
+ case ScInterpreter::NOT_VOLATILE:
+ if (pCode->IsRecalcModeAlways())
+ {
+ // The formula was previously volatile, but no more.
+ EndListeningTo(rDocument);
+ pCode->SetExclusiveRecalcModeNormal();
+ }
+ else
+ {
+ // non-volatile formula. End listening to the area in case
+ // it's listening due to macro module change.
+ rDocument.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
+ }
+ rDocument.RemoveFromFormulaTree(this);
+ break;
+ default:
+ ;
+ }
+}
+
+void ScFormulaCell::SetCompile( bool bVal )
+{
+ bCompile = bVal;
+}
+
+void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
+{
+ ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
+ if (pMat)
+ pMat->SetMatColsRows( nCols, nRows );
+ else if (nCols || nRows)
+ {
+ aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
+ // Setting the new token actually forces an empty result at this top
+ // left cell, so have that recalculated.
+ SetDirty();
+ }
+}
+
+void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
+{
+ const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
+ if (pMat)
+ pMat->GetMatColsRows( nCols, nRows);
+ else
+ {
+ nCols = 0;
+ nRows = 0;
+ }
+}
+
+void ScFormulaCell::SetInChangeTrack( bool bVal )
+{
+ bInChangeTrack = bVal;
+}
+
+void ScFormulaCell::Notify( const SfxHint& rHint )
+{
+ if (rDocument.IsInDtorClear())
+ return;
+
+ const SfxHintId nHint = rHint.GetId();
+ if (nHint == SfxHintId::ScReference)
+ {
+ const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
+
+ switch (rRefHint.getType())
+ {
+ case sc::RefHint::ColumnReordered:
+ {
+ const sc::RefColReorderHint& rRefColReorder =
+ static_cast<const sc::RefColReorderHint&>(rRefHint);
+ if (!IsShared() || IsSharedTop())
+ pCode->MoveReferenceColReorder(
+ aPos, rRefColReorder.getTab(),
+ rRefColReorder.getStartRow(),
+ rRefColReorder.getEndRow(),
+ rRefColReorder.getColMap());
+ }
+ break;
+ case sc::RefHint::RowReordered:
+ {
+ const sc::RefRowReorderHint& rRefRowReorder =
+ static_cast<const sc::RefRowReorderHint&>(rRefHint);
+ if (!IsShared() || IsSharedTop())
+ pCode->MoveReferenceRowReorder(
+ aPos, rRefRowReorder.getTab(),
+ rRefRowReorder.getStartColumn(),
+ rRefRowReorder.getEndColumn(),
+ rRefRowReorder.getRowMap());
+ }
+ break;
+ case sc::RefHint::StartListening:
+ {
+ StartListeningTo(rDocument);
+ }
+ break;
+ case sc::RefHint::StopListening:
+ {
+ EndListeningTo(rDocument);
+ }
+ break;
+ default:
+ ;
+ }
+
+ return;
+ }
+
+ if ( rDocument.GetHardRecalcState() != ScDocument::HardRecalcState::OFF )
+ return;
+
+ if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged)))
+ return;
+
+ bool bForceTrack = false;
+ if ( nHint == SfxHintId::ScTableOpDirty )
+ {
+ bForceTrack = !bTableOpDirty;
+ if ( !bTableOpDirty )
+ {
+ rDocument.AddTableOpFormulaCell( this );
+ bTableOpDirty = true;
+ }
+ }
+ else
+ {
+ bForceTrack = !bDirty;
+ SetDirtyVar();
+ }
+ // Don't remove from FormulaTree to put in FormulaTrack to
+ // put in FormulaTree again and again, only if necessary.
+ // Any other means except ScRecalcMode::ALWAYS by which a cell could
+ // be in FormulaTree if it would notify other cells through
+ // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
+ // Yes. The new TableOpDirty made it necessary to have a
+ // forced mode where formulas may still be in FormulaTree from
+ // TableOpDirty but have to notify dependents for normal dirty.
+ if ( (bForceTrack || !rDocument.IsInFormulaTree( this )
+ || pCode->IsRecalcModeAlways())
+ && !rDocument.IsInFormulaTrack( this ) )
+ rDocument.AppendToFormulaTrack( this );
+}
+
+void ScFormulaCell::Query( SvtListener::QueryBase& rQuery ) const
+{
+ switch (rQuery.getId())
+ {
+ case SC_LISTENER_QUERY_FORMULA_GROUP_POS:
+ {
+ sc::RefQueryFormulaGroup& rRefQuery =
+ static_cast<sc::RefQueryFormulaGroup&>(rQuery);
+ if (IsShared())
+ rRefQuery.add(aPos);
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+void ScFormulaCell::SetDirty( bool bDirtyFlag )
+{
+ if (IsInChangeTrack())
+ return;
+
+ if ( rDocument.GetHardRecalcState() != ScDocument::HardRecalcState::OFF )
+ {
+ SetDirtyVar();
+ rDocument.SetStreamValid(aPos.Tab(), false);
+ return;
+ }
+
+ // Avoid multiple formula tracking in Load() and in CompileAll()
+ // after CopyScenario() and CopyBlockFromClip().
+ // If unconditional formula tracking is needed, set bDirty=false
+ // before calling SetDirty(), for example in CompileTokenArray().
+ if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) )
+ {
+ if( bDirtyFlag )
+ SetDirtyVar();
+ rDocument.AppendToFormulaTrack( this );
+
+ // While loading a document listeners have not been established yet.
+ // Tracking would remove this cell from the FormulaTrack and add it to
+ // the FormulaTree, once in there it would be assumed that its
+ // dependents already had been tracked and it would be skipped on a
+ // subsequent notify. Postpone tracking until all listeners are set.
+ if (!rDocument.IsImportingXML() && !rDocument.IsInsertingFromOtherDoc())
+ rDocument.TrackFormulas();
+ }
+
+ rDocument.SetStreamValid(aPos.Tab(), false);
+}
+
+void ScFormulaCell::SetDirtyVar()
+{
+ bDirty = true;
+ mbPostponedDirty = false;
+ if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
+ {
+ mxGroup->meCalcState = sc::GroupCalcEnabled;
+ mxGroup->mbPartOfCycle = false;
+ }
+
+ // mark the sheet of this cell to be calculated
+ //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() );
+}
+
+void ScFormulaCell::SetDirtyAfterLoad()
+{
+ bDirty = true;
+ if ( rDocument.GetHardRecalcState() == ScDocument::HardRecalcState::OFF )
+ rDocument.PutInFormulaTree( this );
+}
+
+void ScFormulaCell::ResetTableOpDirtyVar()
+{
+ bTableOpDirty = false;
+}
+
+void ScFormulaCell::SetTableOpDirty()
+{
+ if ( IsInChangeTrack() )
+ return;
+
+ if ( rDocument.GetHardRecalcState() != ScDocument::HardRecalcState::OFF )
+ bTableOpDirty = true;
+ else
+ {
+ if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) )
+ {
+ if ( !bTableOpDirty )
+ {
+ rDocument.AddTableOpFormulaCell( this );
+ bTableOpDirty = true;
+ }
+ rDocument.AppendToFormulaTrack( this );
+ rDocument.TrackFormulas( SfxHintId::ScTableOpDirty );
+ }
+ }
+}
+
+void ScFormulaCell::SetResultDouble( double n )
+{
+ aResult.SetDouble(n);
+}
+
+void ScFormulaCell::SetResultToken( const formula::FormulaToken* pToken )
+{
+ aResult.SetToken(pToken);
+}
+
+const svl::SharedString & ScFormulaCell::GetResultString() const
+{
+ return aResult.GetString();
+}
+
+bool ScFormulaCell::HasHybridStringResult() const
+{
+ return aResult.GetType() == formula::svHybridCell && !aResult.GetString().isEmpty();
+}
+
+void ScFormulaCell::SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL )
+{
+ aResult.SetMatrix(nCols, nRows, pMat, pUL);
+}
+
+void ScFormulaCell::SetErrCode( FormulaError n )
+{
+ /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
+ * used whether it is solely for transport of a simple result error and get
+ * rid of that abuse. */
+ pCode->SetCodeError( n );
+ // Hard set errors are transported as result type value per convention,
+ // e.g. via clipboard. ScFormulaResult::IsValue() and
+ // ScFormulaResult::GetDouble() handle that.
+ aResult.SetResultError( n );
+}
+
+void ScFormulaCell::SetResultError( FormulaError n )
+{
+ aResult.SetResultError( n );
+}
+
+void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
+{
+ if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
+ SetDirtyVar();
+ pCode->AddRecalcMode( nBits );
+}
+
+void ScFormulaCell::SetHybridDouble( double n )
+{
+ aResult.SetHybridDouble( n);
+}
+
+void ScFormulaCell::SetHybridString( const svl::SharedString& r )
+{
+ aResult.SetHybridString( r);
+}
+
+void ScFormulaCell::SetHybridEmptyDisplayedAsString()
+{
+ aResult.SetHybridEmptyDisplayedAsString();
+}
+
+void ScFormulaCell::SetHybridFormula( const OUString& r,
+ const formula::FormulaGrammar::Grammar eGrammar )
+{
+ aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
+}
+
+OUString ScFormulaCell::GetHybridFormula() const
+{
+ return aResult.GetHybridFormula();
+}
+
+// Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
+void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
+{
+ OUString aCellString;
+
+ const Color* pColor;
+
+ // Cell Text uses the Cell format while the URL uses
+ // the default format for the type.
+ const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos );
+ SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
+
+ const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
+
+ if ( IsValue() )
+ {
+ double fValue = GetValue();
+ pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
+ }
+ else
+ {
+ aCellString = GetString().getString();
+ pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
+ }
+ ScConstMatrixRef xMat( aResult.GetMatrix());
+ if (xMat)
+ {
+ // determine if the matrix result is a string or value.
+ if (!xMat->IsValue(0, 1))
+ rURL = xMat->GetString(0, 1).getString();
+ else
+ pFormatter->GetOutputString(
+ xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
+ }
+
+ if(rURL.isEmpty())
+ {
+ if(IsValue())
+ pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
+ else
+ pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
+ }
+}
+
+bool ScFormulaCell::IsMultilineResult()
+{
+ if (!IsValue())
+ return aResult.IsMultiline();
+ return false;
+}
+
+bool ScFormulaCell::IsHyperLinkCell() const
+{
+ return pCode && pCode->IsHyperLink();
+}
+
+std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
+{
+ OUString aCellText;
+ OUString aURL;
+ GetURLResult( aURL, aCellText );
+
+ return ScEditUtil::CreateURLObjectFromURL( rDocument, aURL, aCellText );
+}
+
+bool ScFormulaCell::IsEmpty()
+{
+ MaybeInterpret();
+ return aResult.GetCellResultType() == formula::svEmptyCell;
+}
+
+bool ScFormulaCell::IsEmptyDisplayedAsString()
+{
+ MaybeInterpret();
+ return aResult.IsEmptyDisplayedAsString();
+}
+
+bool ScFormulaCell::IsValue()
+{
+ MaybeInterpret();
+ return aResult.IsValue();
+}
+
+bool ScFormulaCell::IsValueNoError()
+{
+ MaybeInterpret();
+ if (pCode->GetCodeError() != FormulaError::NONE)
+ return false;
+
+ return aResult.IsValueNoError();
+}
+
+bool ScFormulaCell::IsValueNoError() const
+{
+ if (NeedsInterpret())
+ // false if the cell is dirty & needs to be interpreted.
+ return false;
+
+ if (pCode->GetCodeError() != FormulaError::NONE)
+ return false;
+
+ return aResult.IsValueNoError();
+}
+
+double ScFormulaCell::GetValue()
+{
+ MaybeInterpret();
+ return GetRawValue();
+}
+
+const svl::SharedString & ScFormulaCell::GetString()
+{
+ MaybeInterpret();
+ return GetRawString();
+}
+
+double ScFormulaCell::GetRawValue() const
+{
+ if ((pCode->GetCodeError() == FormulaError::NONE) &&
+ aResult.GetResultError() == FormulaError::NONE)
+ return aResult.GetDouble();
+ return 0.0;
+}
+
+const svl::SharedString & ScFormulaCell::GetRawString() const
+{
+ if ((pCode->GetCodeError() == FormulaError::NONE) &&
+ aResult.GetResultError() == FormulaError::NONE)
+ return aResult.GetString();
+
+ return svl::SharedString::getEmptyString();
+}
+
+const ScMatrix* ScFormulaCell::GetMatrix()
+{
+ if ( rDocument.GetAutoCalc() )
+ {
+ if( IsDirtyOrInTableOpDirty()
+ // Was stored !bDirty but an accompanying matrix cell was bDirty?
+ || (!bDirty && cMatrixFlag == ScMatrixMode::Formula && !aResult.GetMatrix()))
+ Interpret();
+ }
+ return aResult.GetMatrix().get();
+}
+
+bool ScFormulaCell::GetMatrixOrigin( const ScDocument& rDoc, ScAddress& rPos ) const
+{
+ switch ( cMatrixFlag )
+ {
+ case ScMatrixMode::Formula :
+ rPos = aPos;
+ return true;
+ case ScMatrixMode::Reference :
+ {
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* t = aIter.GetNextReferenceRPN();
+ if( t )
+ {
+ ScSingleRefData& rRef = *t->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(rDoc, aPos);
+ if (rDoc.ValidAddress(aAbs))
+ {
+ rPos = aAbs;
+ return true;
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ return false;
+}
+
+sc::MatrixEdge ScFormulaCell::GetMatrixEdge( const ScDocument& rDoc, ScAddress& rOrgPos ) const
+{
+ switch ( cMatrixFlag )
+ {
+ case ScMatrixMode::Formula :
+ case ScMatrixMode::Reference :
+ {
+ static thread_local SCCOL nC;
+ static thread_local SCROW nR;
+ ScAddress aOrg;
+ if ( !GetMatrixOrigin( rDoc, aOrg ) )
+ return sc::MatrixEdge::Nothing;
+ if ( aOrg != rOrgPos )
+ { // First time or a different matrix than last time.
+ rOrgPos = aOrg;
+ const ScFormulaCell* pFCell;
+ if ( cMatrixFlag == ScMatrixMode::Reference )
+ pFCell = rDocument.GetFormulaCell(aOrg);
+ else
+ pFCell = this; // this ScMatrixMode::Formula
+ // There's only one this, don't compare pFCell==this.
+ if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
+ {
+ pFCell->GetMatColsRows( nC, nR );
+ if ( nC == 0 || nR == 0 )
+ {
+ // No ScMatrixFormulaCellToken available yet, calculate new.
+ nC = 1;
+ nR = 1;
+ ScAddress aTmpOrg;
+ ScFormulaCell* pCell;
+ ScAddress aAdr( aOrg );
+ aAdr.IncCol();
+ bool bCont = true;
+ do
+ {
+ pCell = rDocument.GetFormulaCell(aAdr);
+ if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
+ pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
+ {
+ nC++;
+ aAdr.IncCol();
+ }
+ else
+ bCont = false;
+ } while ( bCont );
+ aAdr = aOrg;
+ aAdr.IncRow();
+ bCont = true;
+ do
+ {
+ pCell = rDocument.GetFormulaCell(aAdr);
+ if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
+ pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
+ {
+ nR++;
+ aAdr.IncRow();
+ }
+ else
+ bCont = false;
+ } while ( bCont );
+
+ const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
+ }
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
+ << aPos.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument)
+ << ", MatOrg: "
+ << aOrg.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument) );
+#endif
+ return sc::MatrixEdge::Nothing;
+ }
+ }
+ // here we are, healthy and clean, somewhere in between
+ SCCOL dC = aPos.Col() - aOrg.Col();
+ SCROW dR = aPos.Row() - aOrg.Row();
+ sc::MatrixEdge nEdges = sc::MatrixEdge::Nothing;
+ if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
+ {
+ if ( dC == 0 )
+ nEdges |= sc::MatrixEdge::Left;
+ if ( dC+1 == nC )
+ nEdges |= sc::MatrixEdge::Right;
+ if ( dR == 0 )
+ nEdges |= sc::MatrixEdge::Top;
+ if ( dR+1 == nR )
+ nEdges |= sc::MatrixEdge::Bottom;
+ if ( nEdges == sc::MatrixEdge::Nothing )
+ nEdges = sc::MatrixEdge::Inside;
+ }
+ else
+ {
+ SAL_WARN( "sc", "broken Matrix, Pos: "
+ << aPos.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument)
+ << ", MatOrg: "
+ << aOrg.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument)
+ << ", MatCols: " << static_cast<sal_Int32>( nC )
+ << ", MatRows: " << static_cast<sal_Int32>( nR )
+ << ", DiffCols: " << static_cast<sal_Int32>( dC )
+ << ", DiffRows: " << static_cast<sal_Int32>( dR ));
+ }
+ return nEdges;
+ }
+ default:
+ return sc::MatrixEdge::Nothing;
+ }
+}
+
+FormulaError ScFormulaCell::GetErrCode()
+{
+ MaybeInterpret();
+
+ /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
+ * and not also abused for signaling other error conditions we could bail
+ * out even before attempting to interpret broken code. */
+ FormulaError nErr = pCode->GetCodeError();
+ if (nErr != FormulaError::NONE)
+ return nErr;
+ return aResult.GetResultError();
+}
+
+FormulaError ScFormulaCell::GetRawError() const
+{
+ FormulaError nErr = pCode->GetCodeError();
+ if (nErr != FormulaError::NONE)
+ return nErr;
+ return aResult.GetResultError();
+}
+
+bool ScFormulaCell::GetErrorOrValue( FormulaError& rErr, double& rVal )
+{
+ MaybeInterpret();
+
+ rErr = pCode->GetCodeError();
+ if (rErr != FormulaError::NONE)
+ return true;
+
+ return aResult.GetErrorOrDouble(rErr, rVal);
+}
+
+sc::FormulaResultValue ScFormulaCell::GetResult()
+{
+ MaybeInterpret();
+
+ FormulaError nErr = pCode->GetCodeError();
+ if (nErr != FormulaError::NONE)
+ return sc::FormulaResultValue(nErr);
+
+ return aResult.GetResult();
+}
+
+sc::FormulaResultValue ScFormulaCell::GetResult() const
+{
+ FormulaError nErr = pCode->GetCodeError();
+ if (nErr != FormulaError::NONE)
+ return sc::FormulaResultValue(nErr);
+
+ return aResult.GetResult();
+}
+
+bool ScFormulaCell::HasOneReference( ScRange& r ) const
+{
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* p = aIter.GetNextReferenceRPN();
+ if( p && !aIter.GetNextReferenceRPN() ) // only one!
+ {
+ SingleDoubleRefProvider aProv( *p );
+ r.aStart = aProv.Ref1.toAbs(rDocument, aPos);
+ r.aEnd = aProv.Ref2.toAbs(rDocument, aPos);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool
+ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
+{
+ /* If there appears just one reference in the formula, it's the same
+ as HasOneReference(). If there are more of them, they can denote
+ one range if they are (sole) arguments of one function.
+ Union of these references must form one range and their
+ intersection must be empty set.
+ */
+
+ // Detect the simple case of exactly one reference in advance without all
+ // overhead.
+ // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
+ // work again, where the function does not have only references.
+ if (HasOneReference( rRange))
+ return true;
+
+ // Get first reference, if any
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
+ if (pFirstReference)
+ {
+ // Collect all consecutive references, starting by the one
+ // already found
+ std::vector<formula::FormulaToken*> aReferences { pFirstReference };
+ FormulaToken* pToken(aIter.NextRPN());
+ FormulaToken* pFunction(nullptr);
+ while (pToken)
+ {
+ if (lcl_isReference(*pToken))
+ {
+ aReferences.push_back(pToken);
+ pToken = aIter.NextRPN();
+ }
+ else
+ {
+ if (pToken->IsFunction())
+ {
+ pFunction = pToken;
+ }
+ break;
+ }
+ }
+ if (pFunction && !aIter.GetNextReferenceRPN()
+ && (pFunction->GetParamCount() == aReferences.size()))
+ {
+ return lcl_refListFormsOneRange(rDocument, aPos, aReferences, rRange);
+ }
+ }
+ return false;
+}
+
+ScFormulaCell::RelNameRef ScFormulaCell::HasRelNameReference() const
+{
+ RelNameRef eRelNameRef = RelNameRef::NONE;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* t;
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ {
+ switch (t->GetType())
+ {
+ case formula::svSingleRef:
+ if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
+ eRelNameRef = RelNameRef::SINGLE;
+ break;
+ case formula::svDoubleRef:
+ if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
+ // May originate from individual cell names, in which case
+ // it needs recompilation.
+ return RelNameRef::DOUBLE;
+ /* TODO: have an extra flag at ScComplexRefData if range was
+ * extended? or too cumbersome? might narrow recompilation to
+ * only needed cases.
+ * */
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ return eRelNameRef;
+}
+
+bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.meMode != URM_INSDEL)
+ // Just in case...
+ return false;
+
+ if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
+ // No movement.
+ return false;
+
+ if (!rCxt.maRange.Contains(aPos))
+ return false;
+
+ // This formula cell itself is being shifted during cell range
+ // insertion or deletion. Update its position.
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ {
+ assert(!"can't move ScFormulaCell");
+ }
+
+ return true;
+}
+
+namespace {
+
+/**
+ * Check if we need to re-compile column or row names.
+ */
+bool checkCompileColRowName(
+ const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
+ const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
+{
+ switch (rCxt.meMode)
+ {
+ case URM_INSDEL:
+ {
+ if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
+ return false;
+
+ formula::FormulaTokenArrayPlainIterator aIter(rCode);
+ formula::FormulaToken* t;
+ ScRangePairList* pColList = rDoc.GetColNameRanges();
+ ScRangePairList* pRowList = rDoc.GetRowNameRanges();
+ while ((t = aIter.GetNextColRowName()) != nullptr)
+ {
+ ScSingleRefData& rRef = *t->GetSingleRef();
+ if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
+ { // ColName
+ ScAddress aAdr = rRef.toAbs(rDoc, aPos);
+ ScRangePair* pR = pColList->Find( aAdr );
+ if ( pR )
+ { // defined
+ if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
+ return true;
+ }
+ else
+ { // on the fly
+ if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
+ return true;
+ }
+ }
+ if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
+ { // RowName
+ ScAddress aAdr = rRef.toAbs(rDoc, aPos);
+ ScRangePair* pR = pRowList->Find( aAdr );
+ if ( pR )
+ { // defined
+ if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
+ return true;
+ }
+ else
+ { // on the fly
+ if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
+ return true;
+ }
+ }
+ }
+ }
+ break;
+ case URM_MOVE:
+ { // Recompile for Move/D&D when ColRowName was moved or this Cell
+ // points to one and was moved.
+ bool bMoved = (aPos != aOldPos);
+ if (bMoved)
+ return true;
+
+ formula::FormulaTokenArrayPlainIterator aIter(rCode);
+ const formula::FormulaToken* t = aIter.GetNextColRowName();
+ for (; t; t = aIter.GetNextColRowName())
+ {
+ const ScSingleRefData& rRef = *t->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(rDoc, aPos);
+ if (rDoc.ValidAddress(aAbs))
+ {
+ if (rCxt.maRange.Contains(aAbs))
+ return true;
+ }
+ }
+ }
+ break;
+ case URM_COPY:
+ return bValChanged;
+ default:
+ ;
+ }
+
+ return false;
+}
+
+void setOldCodeToUndo(
+ ScDocument& rUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
+{
+ // Copy the cell to aUndoPos, which is its current position in the document,
+ // so this works when UpdateReference is called before moving the cells
+ // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
+ // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
+
+ // If there is already a formula cell in the undo document, don't overwrite it,
+ // the first (oldest) is the important cell.
+ if (rUndoDoc.GetCellType(aUndoPos) == CELLTYPE_FORMULA)
+ return;
+
+ ScFormulaCell* pFCell =
+ new ScFormulaCell(
+ rUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(rUndoDoc), eTempGrammar, cMatrixFlag);
+
+ pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
+ rUndoDoc.SetFormulaCell(aUndoPos, pFCell);
+}
+
+}
+
+bool ScFormulaCell::UpdateReferenceOnShift(
+ const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
+{
+ if (rCxt.meMode != URM_INSDEL)
+ // Just in case...
+ return false;
+
+ bool bCellStateChanged = false;
+ ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
+ if ( pUndoCellPos )
+ aUndoPos = *pUndoCellPos;
+ ScAddress aOldPos( aPos );
+ bCellStateChanged = UpdatePosOnShift(rCxt);
+
+ // Check presence of any references or column row names.
+ bool bHasRefs = pCode->HasReferences();
+ bool bHasColRowNames = false;
+ if (!bHasRefs)
+ {
+ bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
+ bHasRefs = bHasColRowNames;
+ }
+ bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
+
+ if (!bHasRefs && !bOnRefMove)
+ // This formula cell contains no references, nor needs recalculating
+ // on reference update. Bail out.
+ return bCellStateChanged;
+
+ std::unique_ptr<ScTokenArray> pOldCode;
+ if (pUndoDoc)
+ pOldCode = pCode->Clone();
+
+ bool bValChanged = false;
+ bool bRefModified = false;
+ bool bRecompile = bCompile;
+
+ if (bHasRefs)
+ {
+ // Update cell or range references.
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
+ bRefModified = aRes.mbReferenceModified;
+ bValChanged = aRes.mbValueChanged;
+ if (aRes.mbNameModified)
+ bRecompile = true;
+ }
+
+ if (bValChanged || bRefModified)
+ bCellStateChanged = true;
+
+ if (bOnRefMove)
+ // Cell may reference itself, e.g. ocColumn, ocRow without parameter
+ bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
+
+ bool bNewListening = false;
+ bool bInDeleteUndo = false;
+
+ if (bHasRefs)
+ {
+ // Upon Insert ColRowNames have to be recompiled in case the
+ // insertion occurs right in front of the range.
+ if (bHasColRowNames && !bRecompile)
+ bRecompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
+
+ ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
+ bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
+
+ // RelNameRefs are always moved
+ bool bHasRelName = false;
+ if (!bRecompile)
+ {
+ RelNameRef eRelNameRef = HasRelNameReference();
+ bHasRelName = (eRelNameRef != RelNameRef::NONE);
+ bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
+ }
+ // Reference changed and new listening needed?
+ // Except in Insert/Delete without specialities.
+ bNewListening = (bRefModified || bRecompile
+ || (bValChanged && bInDeleteUndo) || bHasRelName);
+
+ if ( bNewListening )
+ EndListeningTo(rDocument, pOldCode.get(), aOldPos);
+ }
+
+ // NeedDirty for changes except for Copy and Move/Insert without RelNames
+ bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
+
+ if (pUndoDoc && (bValChanged || bOnRefMove))
+ setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
+
+ bCompile |= bRecompile;
+ if (bCompile)
+ {
+ CompileTokenArray( bNewListening ); // no Listening
+ bNeedDirty = true;
+ }
+
+ if ( !bInDeleteUndo )
+ { // In ChangeTrack Delete-Reject listeners are established in
+ // InsertCol/InsertRow
+ if ( bNewListening )
+ {
+ // Inserts/Deletes re-establish listeners after all
+ // UpdateReference calls.
+ // All replaced shared formula listeners have to be
+ // established after an Insert or Delete. Do nothing here.
+ SetNeedsListening( true);
+ }
+ }
+
+ if (bNeedDirty)
+ { // Cut off references, invalid or similar?
+ // Postpone SetDirty() until all listeners have been re-established in
+ // Inserts/Deletes.
+ mbPostponedDirty = true;
+ }
+
+ return bCellStateChanged;
+}
+
+bool ScFormulaCell::UpdateReferenceOnMove(
+ const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
+{
+ if (rCxt.meMode != URM_MOVE)
+ return false;
+
+ ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
+ if ( pUndoCellPos )
+ aUndoPos = *pUndoCellPos;
+ ScAddress aOldPos( aPos );
+
+ bool bCellInMoveTarget = rCxt.maRange.Contains(aPos);
+
+ if ( bCellInMoveTarget )
+ {
+ // The cell is being moved or copied to a new position. I guess the
+ // position has been updated prior to this call? Determine
+ // its original position before the move which will be used to adjust
+ // relative references later.
+ aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
+ }
+
+ // Check presence of any references or column row names.
+ bool bHasRefs = pCode->HasReferences();
+ bool bHasColRowNames = false;
+ if (!bHasRefs)
+ {
+ bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
+ bHasRefs = bHasColRowNames;
+ }
+ bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
+
+ if (!bHasRefs && !bOnRefMove)
+ // This formula cell contains no references, nor needs recalculating
+ // on reference update. Bail out.
+ return false;
+
+ bool bCellStateChanged = false;
+ std::unique_ptr<ScTokenArray> pOldCode;
+ if (pUndoDoc)
+ pOldCode = pCode->Clone();
+
+ bool bValChanged = false;
+ bool bRefModified = false;
+
+ if (bHasRefs)
+ {
+ // Update cell or range references.
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
+ bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
+ bValChanged = aRes.mbValueChanged;
+ if (aRes.mbNameModified)
+ // Re-compile to get the RPN token regenerated to reflect updated names.
+ bCompile = true;
+ }
+
+ if (bValChanged || bRefModified)
+ bCellStateChanged = true;
+
+ if (bOnRefMove)
+ // Cell may reference itself, e.g. ocColumn, ocRow without parameter
+ bOnRefMove = (bValChanged || (aPos != aOldPos));
+
+ bool bColRowNameCompile = false;
+ bool bHasRelName = false;
+ bool bNewListening = false;
+ bool bInDeleteUndo = false;
+
+ if (bHasRefs)
+ {
+ // Upon Insert ColRowNames have to be recompiled in case the
+ // insertion occurs right in front of the range.
+ if (bHasColRowNames)
+ bColRowNameCompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
+
+ ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
+ bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
+
+ // RelNameRefs are always moved
+ RelNameRef eRelNameRef = HasRelNameReference();
+ bHasRelName = (eRelNameRef != RelNameRef::NONE);
+ bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
+ // Reference changed and new listening needed?
+ // Except in Insert/Delete without specialties.
+ bNewListening = (bRefModified || bColRowNameCompile
+ || bValChanged || bHasRelName)
+ // #i36299# Don't duplicate action during cut&paste / drag&drop
+ // on a cell in the range moved, start/end listeners is done
+ // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
+ && !(rDocument.IsInsertingFromOtherDoc() && rCxt.maRange.Contains(aPos));
+
+ if ( bNewListening )
+ EndListeningTo(rDocument, pOldCode.get(), aOldPos);
+ }
+
+ bool bNeedDirty = false;
+ // NeedDirty for changes except for Copy and Move/Insert without RelNames
+ if ( bRefModified || bColRowNameCompile ||
+ (bValChanged && bHasRelName ) || bOnRefMove)
+ bNeedDirty = true;
+
+ if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
+ setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
+
+ bValChanged = false;
+
+ bCompile = (bCompile || bValChanged || bColRowNameCompile);
+ if ( bCompile )
+ {
+ CompileTokenArray( bNewListening ); // no Listening
+ bNeedDirty = true;
+ }
+
+ if ( !bInDeleteUndo )
+ { // In ChangeTrack Delete-Reject listeners are established in
+ // InsertCol/InsertRow
+ if ( bNewListening )
+ {
+ StartListeningTo( rDocument );
+ }
+ }
+
+ if (bNeedDirty)
+ { // Cut off references, invalid or similar?
+ sc::AutoCalcSwitch aACSwitch(rDocument, false);
+ SetDirty();
+ }
+
+ return bCellStateChanged;
+}
+
+bool ScFormulaCell::UpdateReferenceOnCopy(
+ const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
+{
+ if (rCxt.meMode != URM_COPY)
+ return false;
+
+ ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
+ if ( pUndoCellPos )
+ aUndoPos = *pUndoCellPos;
+ ScAddress aOldPos( aPos );
+
+ if (rCxt.maRange.Contains(aPos))
+ {
+ // The cell is being moved or copied to a new position. I guess the
+ // position has been updated prior to this call? Determine
+ // its original position before the move which will be used to adjust
+ // relative references later.
+ aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
+ }
+
+ // Check presence of any references or column row names.
+ bool bHasRefs = pCode->HasReferences();
+ bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
+ bHasRefs = bHasRefs || bHasColRowNames;
+ bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
+
+ if (!bHasRefs && !bOnRefMove)
+ // This formula cell contains no references, nor needs recalculating
+ // on reference update. Bail out.
+ return false;
+
+ std::unique_ptr<ScTokenArray> pOldCode;
+ if (pUndoDoc)
+ pOldCode = pCode->Clone();
+
+ if (bOnRefMove)
+ // Cell may reference itself, e.g. ocColumn, ocRow without parameter
+ bOnRefMove = (aPos != aOldPos);
+
+ bool bNeedDirty = bOnRefMove;
+
+ if (pUndoDoc && bOnRefMove)
+ setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
+
+ if (bCompile)
+ {
+ CompileTokenArray(); // no Listening
+ bNeedDirty = true;
+ }
+
+ if (bNeedDirty)
+ { // Cut off references, invalid or similar?
+ sc::AutoCalcSwitch aACSwitch(rDocument, false);
+ SetDirty();
+ }
+
+ return false;
+}
+
+bool ScFormulaCell::UpdateReference(
+ const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
+{
+ if (rDocument.IsClipOrUndo())
+ return false;
+
+ if (mxGroup && mxGroup->mpTopCell != this)
+ {
+ // This is not a top cell of a formula group. Don't update references.
+
+ switch (rCxt.meMode)
+ {
+ case URM_INSDEL:
+ return UpdatePosOnShift(rCxt);
+ default:
+ ;
+ }
+ return false;
+ }
+
+ switch (rCxt.meMode)
+ {
+ case URM_INSDEL:
+ return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
+ case URM_MOVE:
+ return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
+ case URM_COPY:
+ return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
+ default:
+ ;
+ }
+
+ return false;
+}
+
+void ScFormulaCell::UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt )
+{
+ // Adjust tokens only when it's not grouped or grouped top cell.
+ bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
+ bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
+ if (rDocument.IsClipOrUndo() || !pCode->HasReferences())
+ {
+ if (bPosChanged)
+ aPos.IncTab(rCxt.mnSheets);
+
+ return;
+ }
+
+ EndListeningTo( rDocument );
+ ScAddress aOldPos = aPos;
+ // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
+ if (bPosChanged)
+ aPos.IncTab(rCxt.mnSheets);
+
+ if (!bAdjustCode)
+ return;
+
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aOldPos);
+ if (aRes.mbNameModified)
+ // Re-compile after new sheet(s) have been inserted.
+ bCompile = true;
+
+ // no StartListeningTo because the new sheets have not been inserted yet.
+}
+
+void ScFormulaCell::UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt )
+{
+ // Adjust tokens only when it's not grouped or grouped top cell.
+ bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
+ bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
+ if (rDocument.IsClipOrUndo() || !pCode->HasReferences())
+ {
+ if (bPosChanged)
+ aPos.IncTab(-1*rCxt.mnSheets);
+ return;
+ }
+
+ EndListeningTo( rDocument );
+ // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
+ ScAddress aOldPos = aPos;
+ if (bPosChanged)
+ aPos.IncTab(-1*rCxt.mnSheets);
+
+ if (!bAdjustCode)
+ return;
+
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
+ if (aRes.mbNameModified)
+ // Re-compile after sheet(s) have been deleted.
+ bCompile = true;
+}
+
+void ScFormulaCell::UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
+{
+ // Adjust tokens only when it's not grouped or grouped top cell.
+ bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
+
+ if (!pCode->HasReferences() || rDocument.IsClipOrUndo())
+ {
+ aPos.SetTab(nTabNo);
+ return;
+ }
+
+ EndListeningTo(rDocument);
+ ScAddress aOldPos = aPos;
+ // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
+ aPos.SetTab(nTabNo);
+
+ // no StartListeningTo because pTab[nTab] not yet correct!
+
+ if (!bAdjustCode)
+ return;
+
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
+ if (aRes.mbNameModified)
+ // Re-compile after sheet(s) have been deleted.
+ bCompile = true;
+}
+
+void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
+{
+ if (rDocument.IsClipOrUndo())
+ return;
+
+ bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
+ if (!bAdjustCode)
+ return;
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* p = aIter.GetNextReferenceRPN();
+ while (p)
+ {
+ ScSingleRefData& rRef1 = *p->GetSingleRef();
+ if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
+ rRef1.IncTab(1);
+ if (p->GetType() == formula::svDoubleRef)
+ {
+ ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
+ if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
+ rRef2.IncTab(1);
+ }
+ p = aIter.GetNextReferenceRPN();
+ }
+}
+
+bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
+{
+ if (rDocument.IsClipOrUndo())
+ return false;
+
+ bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
+ if (!bAdjustCode)
+ return false;
+
+ bool bRet = false;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* p = aIter.GetNextReferenceRPN();
+ while (p)
+ {
+ ScSingleRefData& rRef1 = *p->GetSingleRef();
+ if (!rRef1.IsTabRel())
+ {
+ if (nTable != rRef1.Tab())
+ bRet = true;
+ else if (nTable != aPos.Tab())
+ rRef1.SetAbsTab(aPos.Tab());
+ }
+ if (p->GetType() == formula::svDoubleRef)
+ {
+ ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
+ if (!rRef2.IsTabRel())
+ {
+ if(nTable != rRef2.Tab())
+ bRet = true;
+ else if (nTable != aPos.Tab())
+ rRef2.SetAbsTab(aPos.Tab());
+ }
+ }
+ p = aIter.GetNextReferenceRPN();
+ }
+ return bRet;
+}
+
+void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
+{
+ if ( bForceIfNameInUse && !bCompile )
+ bCompile = pCode->HasNameOrColRowName();
+ if ( bCompile )
+ pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
+ CompileTokenArray();
+}
+
+static void lcl_TransposeReference(ScSingleRefData& rRef)
+{
+ // References to or over filtered rows are not adjusted
+ // analog to the normal (non-transposed) case
+ SCCOLROW nTemp = rRef.Col();
+ rRef.SetRelCol(rRef.Row());
+ rRef.SetRelRow(nTemp);
+}
+
+// Reference transposition is only called in Clipboard Document
+void ScFormulaCell::TransposeReference()
+{
+ bool bFound = false;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* t;
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() && rRef1.IsRowRel() )
+ {
+ bool bDouble = (t->GetType() == formula::svDoubleRef);
+ ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
+ if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
+ {
+ lcl_TransposeReference(rRef1);
+
+ if ( bDouble )
+ lcl_TransposeReference(rRef2);
+
+ bFound = true;
+ }
+ }
+ }
+
+ if (bFound)
+ bCompile = true;
+}
+
+void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
+ ScDocument* pUndoDoc )
+{
+ EndListeningTo( rDocument );
+
+ ScAddress aOldPos = aPos;
+ bool bPosChanged = false; // Whether this cell has been moved
+
+ // Dest range is transposed
+ ScRange aDestRange( rDest, ScAddress(
+ static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
+ static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
+ rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
+
+ // cell within range
+ if ( aDestRange.Contains( aOldPos ) )
+ {
+ // References of these cells were not changed by ScTokenArray::AdjustReferenceOnMove()
+ // Count back Positions
+ SCCOL nRelPosX = aOldPos.Col();
+ SCROW nRelPosY = aOldPos.Row();
+ SCTAB nRelPosZ = aOldPos.Tab();
+ ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, rDocument, aDestRange, rSource.aStart );
+ aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
+ bPosChanged = true;
+ }
+
+ std::unique_ptr<ScTokenArray> pOld;
+ if (pUndoDoc)
+ pOld = pCode->Clone();
+ bool bRefChanged = false;
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* t;
+ while( (t = aIter.GetNextReferenceOrName()) != nullptr )
+ {
+ if( t->GetOpCode() == ocName )
+ {
+ const ScRangeData* pName = rDocument.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
+ if (pName && pName->IsModified())
+ bRefChanged = true;
+ }
+ else if( t->GetType() != svIndex )
+ {
+ SingleDoubleRefModifier aMod(*t);
+ ScComplexRefData& rRef = aMod.Ref();
+ ScRange aAbs = rRef.toAbs(rDocument, aOldPos);
+ bool bMod = (ScRefUpdate::UpdateTranspose(rDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
+ if (bMod)
+ {
+ rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
+ bRefChanged = true;
+
+ // Absolute sheet reference => set 3D flag.
+ // More than one sheet referenced => has to have both 3D flags.
+ // If end part has 3D flag => start part must have it too.
+ // The same behavior as in ScTokenArray::AdjustReferenceOnMove() is used for 3D-Flags.
+ rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
+ rRef.Ref1.SetFlag3D(
+ (rSource.aStart.Tab() != rDest.Tab() && !bPosChanged)
+ || !rRef.Ref1.IsTabRel() || rRef.Ref2.IsFlag3D());
+ }
+ }
+ }
+
+ if (bRefChanged)
+ {
+ if (pUndoDoc)
+ {
+ // Similar to setOldCodeToUndo(), but it cannot be used due to the check
+ // pUndoDoc->GetCellType(aPos) == CELLTYPE_FORMULA
+ ScFormulaCell* pFCell = new ScFormulaCell(
+ *pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
+
+ pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
+ pUndoDoc->SetFormulaCell(aPos, pFCell);
+ }
+
+ bCompile = true;
+ CompileTokenArray(); // also call StartListeningTo
+ SetDirty();
+ }
+ else
+ StartListeningTo( rDocument ); // Listener as previous
+}
+
+void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ EndListeningTo( rDocument );
+
+ bool bRefChanged = false;
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ formula::FormulaToken* t;
+
+ while( (t = aIter.GetNextReferenceOrName()) != nullptr )
+ {
+ if( t->GetOpCode() == ocName )
+ {
+ const ScRangeData* pName = rDocument.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
+ if (pName && pName->IsModified())
+ bRefChanged = true;
+ }
+ else if( t->GetType() != svIndex )
+ {
+ SingleDoubleRefModifier aMod(*t);
+ ScComplexRefData& rRef = aMod.Ref();
+ ScRange aAbs = rRef.toAbs(rDocument, aPos);
+ bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
+ if (bMod)
+ {
+ rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos);
+ bRefChanged = true;
+ }
+ }
+ }
+
+ if (bRefChanged)
+ {
+ bCompile = true;
+ CompileTokenArray(); // Also call StartListeningTo
+ SetDirty();
+ }
+ else
+ StartListeningTo( rDocument ); // Listener as previous
+}
+
+// See also ScDocument::FindRangeNamesReferencingSheet()
+static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument& rDoc,
+ int nRecursion)
+{
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ sal_uInt16 nTokenIndex = p->GetIndex();
+ SCTAB nTab = p->GetSheet();
+ rIndexes.setUpdatedName( nTab, nTokenIndex);
+
+ if (nRecursion < 126) // whatever... 42*3
+ {
+ ScRangeData* pSubName = rDoc.FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
+ if (pSubName)
+ lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), rDoc, nRecursion+1);
+ }
+ }
+ }
+}
+
+void ScFormulaCell::FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes) const
+{
+ lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0);
+}
+
+void ScFormulaCell::SetChanged(bool b)
+{
+ bChanged = b;
+}
+
+void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
+{
+ assert(!mxGroup); // Don't call this if it's shared.
+ delete pCode;
+ pCode = pNew.release(); // takes ownership.
+}
+
+void ScFormulaCell::SetRunning( bool bVal )
+{
+ bRunning = bVal;
+}
+
+void ScFormulaCell::CompileDBFormula( sc::CompileFormulaContext& rCxt )
+{
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
+ {
+ OpCode eOp = p->GetOpCode();
+ if ( eOp == ocDBArea || eOp == ocTableRef )
+ {
+ bCompile = true;
+ CompileTokenArray(rCxt);
+ SetDirty();
+ break;
+ }
+ }
+}
+
+void ScFormulaCell::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
+{
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
+ {
+ if ( p->GetOpCode() == ocColRowName )
+ {
+ bCompile = true;
+ CompileTokenArray(rCxt);
+ SetDirty();
+ break;
+ }
+ }
+}
+
+void ScFormulaCell::SetPrevious( ScFormulaCell* pF ) { pPrevious = pF; }
+void ScFormulaCell::SetNext( ScFormulaCell* pF ) { pNext = pF; }
+void ScFormulaCell::SetPreviousTrack( ScFormulaCell* pF ) { pPreviousTrack = pF; }
+void ScFormulaCell::SetNextTrack( ScFormulaCell* pF ) { pNextTrack = pF; }
+
+ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant )
+{
+ if (mxGroup)
+ {
+ // You can't create a new group if the cell is already a part of a group.
+ // Is this a sign of some inconsistent or incorrect data structures? Or normal?
+ SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
+ return ScFormulaCellGroupRef();
+ }
+
+ mxGroup.reset(new ScFormulaCellGroup);
+ mxGroup->mpTopCell = this;
+ mxGroup->mbInvariant = bInvariant;
+ mxGroup->mnLength = nLen;
+ mxGroup->mpCode = std::move(*pCode); // Move this to the shared location.
+ delete pCode;
+ pCode = &*mxGroup->mpCode;
+ return mxGroup;
+}
+
+void ScFormulaCell::SetCellGroup( const ScFormulaCellGroupRef &xRef )
+{
+ if (!xRef)
+ {
+ // Make this cell a non-grouped cell.
+ if (mxGroup)
+ pCode = mxGroup->mpCode->Clone().release();
+
+ mxGroup = xRef;
+ return;
+ }
+
+ // Group object has shared token array.
+ if (!mxGroup)
+ // Currently not shared. Delete the existing token array first.
+ delete pCode;
+
+ mxGroup = xRef;
+ pCode = &*mxGroup->mpCode;
+ mxGroup->mnWeight = 0; // invalidate
+}
+
+ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( const ScFormulaCell& rOther ) const
+{
+ // no Matrix formulae yet.
+ if ( GetMatrixFlag() != ScMatrixMode::NONE )
+ return NotEqual;
+
+ // are these formulas at all similar ?
+ if ( GetHash() != rOther.GetHash() )
+ return NotEqual;
+
+ if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
+ return NotEqual;
+
+ FormulaToken **pThis = pCode->GetCode();
+ sal_uInt16 nThisLen = pCode->GetCodeLen();
+ FormulaToken **pOther = rOther.pCode->GetCode();
+ sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
+
+ if ( !pThis || !pOther )
+ {
+ // Error: no compiled code for cells !"
+ return NotEqual;
+ }
+
+ if ( nThisLen != nOtherLen )
+ return NotEqual;
+
+ // No tokens can be an error cell so check error code, otherwise we could
+ // end up with a series of equal error values instead of individual error
+ // values. Also if for any reason different errors are set even if all
+ // tokens are equal, the cells are not equal.
+ if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
+ return NotEqual;
+
+ bool bInvariant = true;
+
+ // check we are basically the same function
+ for ( sal_uInt16 i = 0; i < nThisLen; i++ )
+ {
+ formula::FormulaToken *pThisTok = pThis[i];
+ formula::FormulaToken *pOtherTok = pOther[i];
+
+ if ( pThisTok->GetType() != pOtherTok->GetType() ||
+ pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
+ pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
+ {
+ // Incompatible type, op-code or param counts.
+ return NotEqual;
+ }
+
+ switch (pThisTok->GetType())
+ {
+ case formula::svMatrix:
+ case formula::svExternalSingleRef:
+ case formula::svExternalDoubleRef:
+ // Ignoring matrix and external references for now.
+ return NotEqual;
+
+ case formula::svSingleRef:
+ {
+ // Single cell reference.
+ const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
+ if (rRef != *pOtherTok->GetSingleRef())
+ return NotEqual;
+
+ if (rRef.IsRowRel())
+ bInvariant = false;
+ }
+ break;
+ case formula::svDoubleRef:
+ {
+ // Range reference.
+ const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
+ const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
+ if (rRef1 != *pOtherTok->GetSingleRef())
+ return NotEqual;
+
+ if (rRef2 != *pOtherTok->GetSingleRef2())
+ return NotEqual;
+
+ if (rRef1.IsRowRel())
+ bInvariant = false;
+
+ if (rRef2.IsRowRel())
+ bInvariant = false;
+ }
+ break;
+ case formula::svDouble:
+ {
+ if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
+ return NotEqual;
+ }
+ break;
+ case formula::svString:
+ {
+ if(pThisTok->GetString() != pOtherTok->GetString())
+ return NotEqual;
+ }
+ break;
+ case formula::svIndex:
+ {
+ if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
+ return NotEqual;
+ }
+ break;
+ case formula::svByte:
+ {
+ if(pThisTok->GetByte() != pOtherTok->GetByte())
+ return NotEqual;
+ }
+ break;
+ case formula::svExternal:
+ {
+ if (pThisTok->GetExternal() != pOtherTok->GetExternal())
+ return NotEqual;
+
+ if (pThisTok->GetByte() != pOtherTok->GetByte())
+ return NotEqual;
+ }
+ break;
+ case formula::svError:
+ {
+ if (pThisTok->GetError() != pOtherTok->GetError())
+ return NotEqual;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ // If still the same, check lexical names as different names may result in
+ // identical RPN code.
+
+ pThis = pCode->GetArray();
+ nThisLen = pCode->GetLen();
+ pOther = rOther.pCode->GetArray();
+ nOtherLen = rOther.pCode->GetLen();
+
+ if ( !pThis || !pOther )
+ {
+ // Error: no code for cells !"
+ return NotEqual;
+ }
+
+ if ( nThisLen != nOtherLen )
+ return NotEqual;
+
+ for ( sal_uInt16 i = 0; i < nThisLen; i++ )
+ {
+ formula::FormulaToken *pThisTok = pThis[i];
+ formula::FormulaToken *pOtherTok = pOther[i];
+
+ if ( pThisTok->GetType() != pOtherTok->GetType() ||
+ pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
+ pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
+ {
+ // Incompatible type, op-code or param counts.
+ return NotEqual;
+ }
+
+ switch (pThisTok->GetType())
+ {
+ // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
+ // resulting in identical RPN references that could lead to creating
+ // a formula group from formulas that should not be merged into a group,
+ // so check also the formula itself.
+ case formula::svSingleRef:
+ {
+ // Single cell reference.
+ const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
+ if (rRef != *pOtherTok->GetSingleRef())
+ return NotEqual;
+
+ if (rRef.IsRowRel())
+ bInvariant = false;
+ }
+ break;
+ case formula::svDoubleRef:
+ {
+ // Range reference.
+ const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
+ const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
+ if (rRef1 != *pOtherTok->GetSingleRef())
+ return NotEqual;
+
+ if (rRef2 != *pOtherTok->GetSingleRef2())
+ return NotEqual;
+
+ if (rRef1.IsRowRel())
+ bInvariant = false;
+
+ if (rRef2.IsRowRel())
+ bInvariant = false;
+ }
+ break;
+ // All index tokens are names. Different categories already had
+ // different OpCode values.
+ case formula::svIndex:
+ {
+ if (pThisTok->GetIndex() != pOtherTok->GetIndex())
+ return NotEqual;
+ switch (pThisTok->GetOpCode())
+ {
+ case ocTableRef:
+ // nothing, sheet value assumed as -1, silence
+ // ScTableRefToken::GetSheet() SAL_WARN about
+ // unhandled
+ ;
+ break;
+ default: // ocName, ocDBArea
+ if (pThisTok->GetSheet() != pOtherTok->GetSheet())
+ return NotEqual;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ return bInvariant ? EqualInvariant : EqualRelativeRef;
+}
+
+namespace {
+
+// Split N into optimally equal-sized pieces, each not larger than K.
+// Return value P is number of pieces. A returns the number of pieces
+// one larger than N/P, 0..P-1.
+
+int splitup(int N, int K, int& A)
+{
+ assert(N > 0);
+ assert(K > 0);
+
+ A = 0;
+
+ if (N <= K)
+ return 1;
+
+ const int ideal_num_parts = N / K;
+ if (ideal_num_parts * K == N)
+ return ideal_num_parts;
+
+ const int num_parts = ideal_num_parts + 1;
+ const int nominal_part_size = N / num_parts;
+
+ A = N - num_parts * nominal_part_size;
+
+ return num_parts;
+}
+
+struct ScDependantsCalculator
+{
+ ScDocument& mrDoc;
+ const ScTokenArray& mrCode;
+ const ScFormulaCellGroupRef& mxGroup;
+ const SCROW mnLen;
+ const ScAddress& mrPos;
+ const bool mFromFirstRow;
+ const SCROW mnStartOffset;
+ const SCROW mnEndOffset;
+ const SCROW mnSpanLen;
+
+ ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
+ const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
+ mrDoc(rDoc),
+ mrCode(rCode),
+ mxGroup(rCell.GetCellGroup()),
+ mnLen(mxGroup->mnLength),
+ mrPos(rPos),
+ // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
+ // only from further rows. This data fetching could also lead to Interpret() calls, so
+ // in OpenCL mode the formula in practice depends on those cells too.
+ mFromFirstRow(fromFirstRow),
+ mnStartOffset(nStartOffset),
+ mnEndOffset(nEndOffset),
+ mnSpanLen(nEndOffset - nStartOffset + 1)
+ {
+ }
+
+ // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
+ // (note already modified a bit, mFromFirstRow)
+
+ // I think what this function does is to check whether the relative row reference nRelRow points
+ // to a row that is inside the range of rows covered by the formula group.
+
+ bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
+ {
+ if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
+ return false;
+
+ SCROW nEndRow = mrPos.Row() + mnLen - 1;
+
+ if (nRelRow <= 0)
+ {
+ SCROW nTest = nEndRow;
+ nTest += nRelRow;
+ if (nTest >= mrPos.Row())
+ return true;
+ }
+ else
+ {
+ SCROW nTest = mrPos.Row(); // top row.
+ nTest += nRelRow;
+ if (nTest <= nEndRow)
+ return true;
+ // If pointing below the formula, it's always included if going from first row.
+ if (mFromFirstRow)
+ return true;
+ }
+
+ return false;
+ }
+
+ // FIXME: another copy-paste
+
+ // And this correspondingly checks whether an absolute row is inside the range of rows covered
+ // by the formula group.
+
+ bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
+ {
+ if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
+ return false;
+
+ SCROW nEndRow = mrPos.Row() + mnLen - 1;
+
+ if (rRefPos.Row() < mrPos.Row())
+ return false;
+
+ // If pointing below the formula, it's always included if going from first row.
+ if (rRefPos.Row() > nEndRow && !mFromFirstRow)
+ return false;
+
+ return true;
+ }
+
+ // Checks if the doubleref engulfs all of formula group cells
+ // Note : does not check if there is a partial overlap, that can be done by calling
+ // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
+ bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
+ {
+ if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
+ || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
+ {
+ return false;
+ }
+
+ SCROW nStartRow = mrPos.Row();
+ SCROW nEndRow = nStartRow + mnLen - 1;
+ SCROW nRefStartRow = rAbs.aStart.Row();
+ SCROW nRefEndRow = rAbs.aEnd.Row();
+
+ if (bIsRef1RowRel && bIsRef2RowRel &&
+ ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
+ ((nRefStartRow + mnLen - 1) <= nStartRow &&
+ (nRefEndRow + mnLen - 1) >= nEndRow)))
+ return true;
+
+ if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
+ (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
+ return true;
+
+ if (!bIsRef2RowRel &&
+ nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
+ return true;
+
+ // If going from first row, the referenced range must be entirely above the formula,
+ // otherwise the formula would be included.
+ if (mFromFirstRow && nRefEndRow >= nStartRow)
+ return true;
+
+ return false;
+ }
+
+ // FIXME: another copy-paste
+ SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
+ {
+ SCROW nLastRow = nRow + nRowLen - 1; // current last row.
+ nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
+ if (nLastRow < (nRow + nRowLen - 1))
+ {
+ // This can end up negative! Was that the original intent, or
+ // is it accidental? Was it not like that originally but the
+ // surrounding conditions changed?
+ nRowLen = nLastRow - nRow + 1;
+ // Anyway, let's assume it doesn't make sense to return a
+ // negative or zero value here.
+ if (nRowLen <= 0)
+ nRowLen = 1;
+ }
+ else if (nLastRow == 0)
+ // Column is empty.
+ nRowLen = 1;
+
+ return nRowLen;
+ }
+
+ // Because Lookup will extend the Result Vector under certain circumstances listed at:
+ // https://wiki.documentfoundation.org/Documentation/Calc_Functions/LOOKUP
+ // then if the Lookup has a Result Vector only accept the Lookup for parallelization
+ // of the Result Vector has the same dimensions as the Search Vector.
+ bool LookupResultVectorMismatch(sal_Int32 nTokenIdx)
+ {
+ if (nTokenIdx >= 3)
+ {
+ FormulaToken** pRPNArray = mrCode.GetCode();
+ if (pRPNArray[nTokenIdx - 1]->GetOpCode() == ocPush && // <- result vector
+ pRPNArray[nTokenIdx - 2]->GetOpCode() == ocPush && // <- search vector
+ pRPNArray[nTokenIdx - 2]->GetType() == svDoubleRef &&
+ pRPNArray[nTokenIdx - 3]->GetOpCode() == ocPush) // <- search criterion
+ {
+ auto res = pRPNArray[nTokenIdx - 1];
+ // If Result vector is just a single cell reference
+ // LOOKUP extends it as a column vector.
+ if (res->GetType() == svSingleRef)
+ return true;
+
+ // If Result vector is a cell range and the match position
+ // falls outside its length, it gets automatically extended
+ // to the length of Search vector, but in the direction of
+ // Result vector.
+ if (res->GetType() == svDoubleRef)
+ {
+ ScComplexRefData aRef1 = *res->GetDoubleRef();
+ ScComplexRefData aRef2 = *pRPNArray[nTokenIdx - 2]->GetDoubleRef();
+ ScRange resultRange = aRef1.toAbs(mrDoc, mrPos);
+ ScRange sourceRange = aRef2.toAbs(mrDoc, mrPos);
+
+ SCROW nResultRows = resultRange.aEnd.Row() - resultRange.aStart.Row();
+ SCROW nSourceRows = sourceRange.aEnd.Row() - sourceRange.aStart.Row();
+ if (nResultRows != nSourceRows)
+ return true;
+
+ SCCOL nResultCols = resultRange.aEnd.Col() - resultRange.aStart.Col();
+ SCCOL nSourceCols = sourceRange.aEnd.Col() - sourceRange.aStart.Col();
+ if (nResultCols != nSourceCols)
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool DoIt(ScRangeList* pSuccessfulDependencies, ScAddress* pDirtiedAddress)
+ {
+ // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
+
+ ScRangeList aRangeList;
+
+ // Self references should be checked by considering the entire formula-group not just the provided span.
+ bool bHasSelfReferences = false;
+ bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
+
+ FormulaToken** pRPNArray = mrCode.GetCode();
+ sal_uInt16 nCodeLen = mrCode.GetCodeLen();
+ for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
+ {
+ auto p = pRPNArray[nTokenIdx];
+ if (!bInDocShellRecalc)
+ {
+ // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
+ // of the result of the condition expression.
+ // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
+ // in the document. So lets disable threading and stop dependency evaluation if
+ // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
+ // for formulae with IF/IFS/SWITCH
+ OpCode nOpCode = p->GetOpCode();
+ if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
+ return false;
+ }
+
+ if (p->GetOpCode() == ocLookup && LookupResultVectorMismatch(nTokenIdx))
+ {
+ SAL_INFO("sc.core.formulacell", "Lookup Result Vector size doesn't match Search Vector");
+ return false;
+ }
+
+ if (p->GetOpCode() == ocRange)
+ {
+ // We are just looking at svSingleRef/svDoubleRef, so we will miss that ocRange constructs
+ // a range from its arguments, and only examining the individual args doesn't capture the
+ // true range of dependencies
+ SAL_WARN("sc.core.formulacell", "dynamic range, dropping as candidate for parallelizing");
+ return false;
+ }
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
+ if( aRef.IsDeleted())
+ return false;
+ ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
+
+ if (!mrDoc.HasTable(aRefPos.Tab()))
+ return false; // or true?
+
+ if (aRef.IsRowRel())
+ {
+ if (isSelfReferenceRelative(aRefPos, aRef.Row()))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+
+ // Trim data array length to actual data range.
+ SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
+
+ aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
+ aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
+ }
+ else
+ {
+ if (isSelfReferenceAbsolute(aRefPos))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+
+ aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData aRef = *p->GetDoubleRef();
+ if( aRef.IsDeleted())
+ return false;
+ ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
+
+ // Multiple sheet
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ return false;
+
+ bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
+ // Check for self reference.
+ if (bIsRef1RowRel)
+ {
+ if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+ }
+ else if (isSelfReferenceAbsolute(aAbs.aStart))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+
+ bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
+ if (bIsRef2RowRel)
+ {
+ if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+ }
+ else if (isSelfReferenceAbsolute(aAbs.aEnd))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+
+ if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
+ {
+ bHasSelfReferences = true;
+ continue;
+ }
+
+ // The first row that will be referenced through the doubleref.
+ SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
+ // The last row that will be referenced through the doubleref.
+ SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
+ // Number of rows to be evaluated from nFirstRefRow.
+ SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
+ assert(nArrayLength > 0);
+
+ // Trim trailing empty rows.
+ nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
+
+ aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
+ aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Compute dependencies irrespective of the presence of any self references.
+ // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
+ // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
+ for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
+ {
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ if( mFromFirstRow )
+ { // include also all previous rows
+ nLength += nStartRow;
+ nStartRow = 0;
+ }
+ if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
+ nLength, mxGroup, pDirtiedAddress))
+ return false;
+ }
+ }
+
+ if (bHasSelfReferences)
+ mxGroup->mbPartOfCycle = true;
+
+ if (pSuccessfulDependencies && !bHasSelfReferences)
+ *pSuccessfulDependencies = aRangeList;
+
+ return !bHasSelfReferences;
+ }
+};
+
+} // anonymous namespace
+
+bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset)
+{
+ if (!mxGroup || !pCode)
+ return false;
+
+ auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this);
+ ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
+
+ if (mxGroup->mbPartOfCycle)
+ {
+ aScope.addMessage("This formula-group is part of a cycle");
+ return false;
+ }
+
+ if (mxGroup->meCalcState == sc::GroupCalcDisabled)
+ {
+ static constexpr OUStringLiteral MESSAGE = u"group calc disabled";
+ aScope.addMessage(MESSAGE);
+ return false;
+ }
+
+ // Use SC_FORCE_CALCULATION=opencl/threads to force calculation e.g. for unittests
+ static ForceCalculationType forceType = ScCalcConfig::getForceCalculationType();
+ if (forceType == ForceCalculationCore
+ || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
+ && forceType != ForceCalculationOpenCL
+ && forceType != ForceCalculationThreads))
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ aScope.addGroupSizeThresholdMessage(*this);
+ return false;
+ }
+
+ if (cMatrixFlag != ScMatrixMode::NONE)
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ aScope.addMessage("matrix skipped");
+ return false;
+ }
+
+ if( forceType != ForceCalculationNone )
+ {
+ // ScConditionEntry::Interpret() creates a temporary cell and interprets it
+ // without it actually being in the document at the specified position.
+ // That would confuse opencl/threading code, as they refer to the cell group
+ // also using the position. This is normally not triggered (single cells
+ // are normally not in a cell group), but if forced, check for this explicitly.
+ if( rDocument.GetFormulaCell( aPos ) != this )
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ aScope.addMessage("cell not in document");
+ return false;
+ }
+ }
+
+ // Get rid of -1's in offsets (defaults) or any invalid offsets.
+ SCROW nMaxOffset = mxGroup->mnLength - 1;
+ nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
+ nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
+
+ if (nEndOffset < nStartOffset)
+ {
+ nStartOffset = 0;
+ nEndOffset = nMaxOffset;
+ }
+
+ if (nEndOffset == nStartOffset && forceType == ForceCalculationNone)
+ return false; // Do not use threads for a single row.
+
+ // Guard against endless recursion of Interpret() calls, for this to work
+ // ScFormulaCell::InterpretFormulaGroup() must never be called through
+ // anything else than ScFormulaCell::Interpret(), same as
+ // ScFormulaCell::InterpretTail()
+ RecursionCounter aRecursionCounter( rRecursionHelper, this);
+
+ bool bDependencyComputed = false;
+ bool bDependencyCheckFailed = false;
+
+ // Preference order: First try OpenCL, then threading.
+ // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
+ if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
+ return true;
+
+ if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
+ return true;
+
+ return false;
+}
+
+bool ScFormulaCell::CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope, bool fromFirstRow,
+ SCROW nStartOffset, SCROW nEndOffset,
+ bool bCalcDependencyOnly,
+ ScRangeList* pSuccessfulDependencies,
+ ScAddress* pDirtiedAddress)
+{
+ ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
+ // iterate over code in the formula ...
+ // ensure all input is pre-calculated -
+ // to avoid writing during the calculation
+ if (bCalcDependencyOnly)
+ {
+ // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
+ // "ScFormulaGroupCycleCheckGuard" for this formula-group.
+ // (We can only reach here from a multi-group dependency evaluation attempt).
+ // (These two have to be in pairs always for any given formula-group)
+ ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
+ return aCalculator.DoIt(pSuccessfulDependencies, pDirtiedAddress);
+ }
+
+ bool bOKToParallelize = false;
+ {
+ ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
+ if (mxGroup->mbPartOfCycle)
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ rScope.addMessage("found circular formula-group dependencies");
+ return false;
+ }
+
+ ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
+ ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
+ bOKToParallelize = aCalculator.DoIt(pSuccessfulDependencies, pDirtiedAddress);
+
+ }
+
+ if (rRecursionHelper.IsInRecursionReturn())
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
+ return false;
+ }
+
+ if (mxGroup->mbPartOfCycle)
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ rScope.addMessage("found circular formula-group dependencies");
+ return false;
+ }
+
+ if (!rRecursionHelper.AreGroupsIndependent())
+ {
+ // This call resulted from a dependency calculation for a multigroup-threading attempt,
+ // but found dependency among the groups.
+ rScope.addMessage("multi-group-dependency failed");
+ return false;
+ }
+
+ if (!bOKToParallelize)
+ {
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+ rScope.addMessage("could not do new dependencies calculation thing");
+ return false;
+ }
+
+ return true;
+}
+
+static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc,
+ o3tl::sorted_vector<ScFormulaCellGroup*>& rFGSet,
+ std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
+{
+ const SCROW nLen = xGroup->mnLength;
+ const sal_Int32 nWt = xGroup->mnWeight;
+ ScAddress aAddr(xGroup->mpTopCell->aPos);
+
+ SCCOL nColRet = aAddr.Col();
+
+ const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
+ if (bLeft)
+ --nColRet;
+ else
+ ++nColRet;
+
+ while (nColRet >= 0 && nColRet <= nMaxCol)
+ {
+ aAddr.SetCol(nColRet);
+ const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
+ if (!pCell)
+ break;
+
+ if (!pCell->NeedsInterpret())
+ break;
+
+ const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
+ if (!xNGroup)
+ break;
+
+ if (!pCell->GetCode()->IsEnabledForThreading())
+ break;
+
+ if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
+ break;
+
+ const SCROW nNLen = xNGroup->mnLength;
+ const sal_Int32 nNWt = pCell->GetWeight();
+ if (nNLen != nLen || nNWt != nWt)
+ break;
+
+ rFGSet.insert(xNGroup.get());
+ rFGMap[nColRet] = xNGroup->mpTopCell;
+
+ if (bLeft)
+ --nColRet;
+ else
+ ++nColRet;
+ }
+
+ if (bLeft)
+ ++nColRet;
+ else
+ --nColRet;
+
+ return nColRet;
+}
+
+// To be called only from InterpretFormulaGroup().
+bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope& aScope,
+ bool& bDependencyComputed,
+ bool& bDependencyCheckFailed,
+ SCROW nStartOffset,
+ SCROW nEndOffset)
+{
+ static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
+ if (!bDependencyCheckFailed && !bThreadingProhibited &&
+ pCode->IsEnabledForThreading() &&
+ ScCalcConfig::isThreadingEnabled())
+ {
+ ScRangeList aOrigDependencies;
+ if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, false, &aOrigDependencies))
+ {
+ bDependencyComputed = true;
+ bDependencyCheckFailed = true;
+ return false;
+ }
+
+ bDependencyComputed = true;
+
+ // Then do the threaded calculation
+
+ class Executor : public comphelper::ThreadTask
+ {
+ private:
+ const unsigned mnThisThread;
+ const unsigned mnThreadsTotal;
+ ScDocument* mpDocument;
+ ScInterpreterContext* mpContext;
+ const ScAddress& mrTopPos;
+ SCCOL mnStartCol;
+ SCCOL mnEndCol;
+ SCROW mnStartOffset;
+ SCROW mnEndOffset;
+
+ public:
+ Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
+ unsigned nThisThread,
+ unsigned nThreadsTotal,
+ ScDocument* pDocument2,
+ ScInterpreterContext* pContext,
+ const ScAddress& rTopPos,
+ SCCOL nStartCol,
+ SCCOL nEndCol,
+ SCROW nStartOff,
+ SCROW nEndOff) :
+ comphelper::ThreadTask(rTag),
+ mnThisThread(nThisThread),
+ mnThreadsTotal(nThreadsTotal),
+ mpDocument(pDocument2),
+ mpContext(pContext),
+ mrTopPos(rTopPos),
+ mnStartCol(nStartCol),
+ mnEndCol(nEndCol),
+ mnStartOffset(nStartOff),
+ mnEndOffset(nEndOff)
+ {
+ }
+
+ virtual void doWork() override
+ {
+ ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
+ mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
+ mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
+ }
+
+ };
+
+ SvNumberFormatter* pNonThreadedFormatter = rDocument.GetNonThreadedContext().GetFormatTable();
+
+ comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
+ sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
+
+ SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
+
+ o3tl::sorted_vector<ScFormulaCellGroup*> aFGSet;
+ std::map<SCCOL, ScFormulaCell*> aFGMap;
+ aFGSet.insert(mxGroup.get());
+
+ ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
+ SCCOL nColStart = aPos.Col();
+ SCCOL nColEnd = nColStart;
+ if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc())
+ {
+ nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true);
+ nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false);
+ }
+
+ bool bFGOK = true;
+ ScAddress aDirtiedAddress(ScAddress::INITIALIZE_INVALID);
+ if (nColStart != nColEnd)
+ {
+ ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
+ for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
+ {
+ if (nCurrCol == aPos.Col())
+ continue;
+
+ bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset,
+ true, nullptr, &aDirtiedAddress);
+ if (!bFGOK || !aGuard.AreGroupsIndependent())
+ {
+ nColEnd = nColStart = aPos.Col();
+ break;
+ }
+ }
+ }
+
+ // tdf#156677 it is possible that if a check of a column in the new range fails that the check has
+ // now left a cell that the original range depended on in a Dirty state. So if the dirtied cell
+ // was part of the original dependencies re-run the initial CheckComputeDependencies to fix it.
+ if (!bFGOK && aDirtiedAddress.IsValid() && aOrigDependencies.Find(aDirtiedAddress))
+ {
+ SAL_WARN("sc.core.formulacell", "rechecking dependencies due to a dirtied cell during speculative probe");
+ const bool bRedoEntryCheckSucceeded = CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset);
+ assert(bRedoEntryCheckSucceeded && "if it worked on the original range it should work again on that range");
+ (void)bRedoEntryCheckSucceeded;
+ }
+
+ std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount);
+ {
+ assert(!rDocument.IsThreadedGroupCalcInProgress());
+ rDocument.SetThreadedGroupCalcInProgress(true);
+
+ ScMutationDisable aGuard(rDocument, ScMutationGuardFlags::CORE);
+
+ // Start nThreadCount new threads
+ std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
+ ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter);
+ ScInterpreterContext* context = nullptr;
+
+ for (int i = 0; i < nThreadCount; ++i)
+ {
+ context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
+ assert(!context->pInterpreter);
+ aInterpreters[i].reset(new ScInterpreter(this, rDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true));
+ context->pInterpreter = aInterpreters[i].get();
+ rDocument.SetupContextFromNonThreadedContext(*context, i);
+ rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, &rDocument, context, mxGroup->mpTopCell->aPos,
+ nColStart, nColEnd, nStartOffset, nEndOffset));
+ }
+
+ SAL_INFO("sc.threaded", "Waiting for threads to finish work");
+ // Do not join the threads here. They will get joined in ScDocument destructor
+ // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone).
+ rThreadPool.waitUntilDone(aTag, false);
+
+ rDocument.SetThreadedGroupCalcInProgress(false);
+
+ for (int i = 0; i < nThreadCount; ++i)
+ {
+ context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
+ // This is intentionally done in this main thread in order to avoid locking.
+ rDocument.MergeContextBackIntoNonThreadedContext(*context, i);
+ context->pInterpreter = nullptr;
+ }
+
+ SAL_INFO("sc.threaded", "Done");
+ }
+
+ ScAddress aStartPos(mxGroup->mpTopCell->aPos);
+ SCROW nSpanLen = nEndOffset - nStartOffset + 1;
+ aStartPos.SetRow(aStartPos.Row() + nStartOffset);
+ // Reuse one of the previously allocated interpreter objects here.
+ rDocument.HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen,
+ aStartPos.Tab(), aInterpreters[0].get());
+
+ return true;
+ }
+
+ return false;
+}
+
+// To be called only from InterpretFormulaGroup().
+bool ScFormulaCell::InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope& aScope,
+ bool& bDependencyComputed,
+ bool& bDependencyCheckFailed)
+{
+ bool bCanVectorize = pCode->IsEnabledForOpenCL();
+ switch (pCode->GetVectorState())
+ {
+ case FormulaVectorEnabled:
+ case FormulaVectorCheckReference:
+ break;
+
+ // Not good.
+ case FormulaVectorDisabledByOpCode:
+ aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
+ break;
+ case FormulaVectorDisabledByStackVariable:
+ aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
+ break;
+ case FormulaVectorDisabledNotInSubSet:
+ aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
+ break;
+ case FormulaVectorDisabled:
+ case FormulaVectorUnknown:
+ default:
+ aScope.addMessage("group calc disabled due to vector state (unknown)");
+ return false;
+ }
+
+ if (!bCanVectorize)
+ return false;
+
+ if (!ScCalcConfig::isOpenCLEnabled())
+ {
+ aScope.addMessage("opencl not enabled");
+ return false;
+ }
+
+ // TableOp does tricks with using a cell with different values, just bail out.
+ if(rDocument.IsInInterpreterTableOp())
+ return false;
+
+ if (bDependencyCheckFailed)
+ return false;
+
+ if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1))
+ {
+ bDependencyComputed = true;
+ bDependencyCheckFailed = true;
+ return false;
+ }
+
+ bDependencyComputed = true;
+
+ // TODO : Disable invariant formula group interpretation for now in order
+ // to get implicit intersection to work.
+ if (mxGroup->mbInvariant && false)
+ return InterpretInvariantFormulaGroup();
+
+ int nMaxGroupLength = INT_MAX;
+
+#ifdef _WIN32
+ // Heuristic: Certain old low-end OpenCL implementations don't
+ // work for us with too large group lengths. 1000 was determined
+ // empirically to be a good compromise.
+ if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
+ nMaxGroupLength = 1000;
+#endif
+
+ if (std::getenv("SC_MAX_GROUP_LENGTH"))
+ nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
+
+ int nNumOnePlus;
+ const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
+
+ int nOffset = 0;
+ int nCurChunkSize;
+ ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
+ for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
+ {
+ nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
+
+ ScFormulaCellGroupRef xGroup;
+
+ if (nNumParts == 1)
+ xGroup = mxGroup;
+ else
+ {
+ // Ugly hack
+ xGroup = new ScFormulaCellGroup();
+ xGroup->mpTopCell = mxGroup->mpTopCell;
+ xGroup->mpTopCell->aPos = aOrigPos;
+ xGroup->mpTopCell->aPos.IncRow(nOffset);
+ xGroup->mbInvariant = mxGroup->mbInvariant;
+ xGroup->mnLength = nCurChunkSize;
+ xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer
+ }
+
+ ScTokenArray aCode(rDocument);
+ ScGroupTokenConverter aConverter(aCode, rDocument, *this, xGroup->mpTopCell->aPos);
+ // TODO avoid this extra compilation
+ ScCompiler aComp( rDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
+ aComp.CompileTokenArray();
+ if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
+ {
+ if(aComp.HasUnhandledPossibleImplicitIntersections())
+ {
+ SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
+#ifdef DBG_UTIL
+ for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
+ {
+ SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
+ << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
+ << "(" << int(opcode) << ")");
+ }
+#endif
+ }
+ else
+ SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
+
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+
+ // Undo the hack above
+ if (nNumParts > 1)
+ {
+ mxGroup->mpTopCell->aPos = aOrigPos;
+ xGroup->mpTopCell = nullptr;
+ mxGroup->mpCode = std::move(xGroup->mpCode);
+ }
+
+ aScope.addMessage("group token conversion failed");
+ return false;
+ }
+
+ // The converted code does not have RPN tokens yet. The interpreter will
+ // generate them.
+ xGroup->meCalcState = mxGroup->meCalcState = sc::GroupCalcRunning;
+ sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic();
+
+ if (pInterpreter == nullptr ||
+ !pInterpreter->interpret(rDocument, xGroup->mpTopCell->aPos, xGroup, aCode))
+ {
+ SAL_INFO("sc.opencl", "interpreting group " << mxGroup->mpTopCell->aPos
+ << " (state " << static_cast<int>(mxGroup->meCalcState) << ") failed, disabling");
+ mxGroup->meCalcState = sc::GroupCalcDisabled;
+
+ // Undo the hack above
+ if (nNumParts > 1)
+ {
+ mxGroup->mpTopCell->aPos = aOrigPos;
+ xGroup->mpTopCell = nullptr;
+ mxGroup->mpCode = std::move(xGroup->mpCode);
+ }
+
+ aScope.addMessage("group interpretation unsuccessful");
+ return false;
+ }
+
+ aScope.setCalcComplete();
+
+ if (nNumParts > 1)
+ {
+ xGroup->mpTopCell = nullptr;
+ mxGroup->mpCode = std::move(xGroup->mpCode);
+ }
+ }
+
+ if (nNumParts > 1)
+ mxGroup->mpTopCell->aPos = aOrigPos;
+ mxGroup->meCalcState = sc::GroupCalcEnabled;
+ return true;
+}
+
+bool ScFormulaCell::InterpretInvariantFormulaGroup()
+{
+ if (pCode->GetVectorState() == FormulaVectorCheckReference)
+ {
+ // An invariant group should only have absolute row references, and no
+ // external references are allowed.
+
+ ScTokenArray aCode(rDocument);
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData aRef = *p->GetSingleRef();
+ ScAddress aRefPos = aRef.toAbs(rDocument, aPos);
+ formula::FormulaTokenRef pNewToken = rDocument.ResolveStaticReference(aRefPos);
+ if (!pNewToken)
+ return false;
+
+ aCode.AddToken(*pNewToken);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData aRef = *p->GetDoubleRef();
+ ScRange aRefRange = aRef.toAbs(rDocument, aPos);
+ formula::FormulaTokenRef pNewToken = rDocument.ResolveStaticReference(aRefRange);
+ if (!pNewToken)
+ return false;
+
+ aCode.AddToken(*pNewToken);
+ }
+ break;
+ default:
+ aCode.AddToken(*p);
+ }
+ }
+
+ ScCompiler aComp(rDocument, aPos, aCode, rDocument.GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
+ aComp.CompileTokenArray(); // Create RPN token array.
+ ScInterpreter aInterpreter(this, rDocument, rDocument.GetNonThreadedContext(), aPos, aCode);
+ aInterpreter.Interpret();
+ aResult.SetToken(aInterpreter.GetResultToken().get());
+ }
+ else
+ {
+ // Formula contains no references.
+ ScInterpreter aInterpreter(this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode);
+ aInterpreter.Interpret();
+ aResult.SetToken(aInterpreter.GetResultToken().get());
+ }
+
+ for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ )
+ {
+ ScAddress aTmpPos = aPos;
+ aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i);
+ ScFormulaCell* pCell = rDocument.GetFormulaCell(aTmpPos);
+ if (!pCell)
+ {
+ SAL_WARN("sc.core.formulacell", "GetFormulaCell not found");
+ continue;
+ }
+
+ // FIXME: this set of horrors is unclear to me ... certainly
+ // the above GetCell is profoundly nasty & slow ...
+ // Ensure the cell truly has a result:
+ pCell->aResult = aResult;
+ pCell->ResetDirty();
+ pCell->SetChanged(true);
+ }
+
+ return true;
+}
+
+namespace {
+
+void startListeningArea(
+ ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
+{
+ const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
+ const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
+ ScAddress aCell1 = rRef1.toAbs(rDoc, rPos);
+ ScAddress aCell2 = rRef2.toAbs(rDoc, rPos);
+ if (!(aCell1.IsValid() && aCell2.IsValid()))
+ return;
+
+ if (rToken.GetOpCode() == ocColRowNameAuto)
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ aCell2.SetRow(rDoc.MaxRow());
+ }
+ else
+ { // RowName
+ aCell2.SetCol(rDoc.MaxCol());
+ }
+ }
+ rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell);
+}
+
+}
+
+void ScFormulaCell::StartListeningTo( ScDocument& rDoc )
+{
+ if (mxGroup)
+ mxGroup->endAllGroupListening(rDoc);
+
+ if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
+ return;
+
+ rDoc.SetDetectiveDirty(true); // It has changed something
+
+ ScTokenArray* pArr = GetCode();
+ if( pArr->IsRecalcModeAlways() )
+ {
+ rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
+ SetNeedsListening( false);
+ return;
+ }
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ formula::FormulaToken* t;
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ {
+ switch (t->GetType())
+ {
+ case svSingleRef:
+ {
+ ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aPos);
+ if (aCell.IsValid())
+ rDoc.StartListeningCell(aCell, this);
+ }
+ break;
+ case svDoubleRef:
+ startListeningArea(this, rDoc, aPos, *t);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ SetNeedsListening( false);
+}
+
+void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
+{
+ ScDocument& rDoc = rCxt.getDoc();
+
+ if (mxGroup)
+ mxGroup->endAllGroupListening(rDoc);
+
+ if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
+ return;
+
+ rDoc.SetDetectiveDirty(true); // It has changed something
+
+ ScTokenArray* pArr = GetCode();
+ if( pArr->IsRecalcModeAlways() )
+ {
+ rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
+ SetNeedsListening( false);
+ return;
+ }
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ formula::FormulaToken* t;
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ {
+ switch (t->GetType())
+ {
+ case svSingleRef:
+ {
+ ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aPos);
+ if (aCell.IsValid())
+ rDoc.StartListeningCell(rCxt, aCell, *this);
+ }
+ break;
+ case svDoubleRef:
+ startListeningArea(this, rDoc, aPos, *t);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ SetNeedsListening( false);
+}
+
+namespace {
+
+void endListeningArea(
+ ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
+{
+ const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
+ const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
+ ScAddress aCell1 = rRef1.toAbs(rDoc, rPos);
+ ScAddress aCell2 = rRef2.toAbs(rDoc, rPos);
+ if (!(aCell1.IsValid() && aCell2.IsValid()))
+ return;
+
+ if (rToken.GetOpCode() == ocColRowNameAuto)
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ aCell2.SetRow(rDoc.MaxRow());
+ }
+ else
+ { // RowName
+ aCell2.SetCol(rDoc.MaxCol());
+ }
+ }
+
+ rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell);
+}
+
+}
+
+void ScFormulaCell::EndListeningTo( ScDocument& rDoc, ScTokenArray* pArr,
+ ScAddress aCellPos )
+{
+ if (mxGroup)
+ mxGroup->endAllGroupListening(rDoc);
+
+ if (rDoc.IsClipOrUndo() || IsInChangeTrack())
+ return;
+
+ if (!HasBroadcaster())
+ return;
+
+ rDoc.SetDetectiveDirty(true); // It has changed something
+
+ if ( GetCode()->IsRecalcModeAlways() )
+ {
+ rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
+ return;
+ }
+
+ if (!pArr)
+ {
+ pArr = GetCode();
+ aCellPos = aPos;
+ }
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ formula::FormulaToken* t;
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ {
+ switch (t->GetType())
+ {
+ case svSingleRef:
+ {
+ ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aCellPos);
+ if (aCell.IsValid())
+ rDoc.EndListeningCell(aCell, this);
+ }
+ break;
+ case svDoubleRef:
+ endListeningArea(this, rDoc, aCellPos, *t);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+}
+
+void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
+{
+ if (mxGroup)
+ mxGroup->endAllGroupListening(rCxt.getDoc());
+
+ if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
+ return;
+
+ if (!HasBroadcaster())
+ return;
+
+ ScDocument& rDoc = rCxt.getDoc();
+ rDoc.SetDetectiveDirty(true); // It has changed something
+
+ ScTokenArray* pArr = rCxt.getOldCode();
+ ScAddress aCellPos = rCxt.getOldPosition(aPos);
+ if (!pArr)
+ pArr = pCode;
+
+ if (pArr->IsRecalcModeAlways())
+ {
+ rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
+ return;
+ }
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ formula::FormulaToken* t;
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ {
+ switch (t->GetType())
+ {
+ case svSingleRef:
+ {
+ ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aCellPos);
+ if (aCell.IsValid())
+ rDoc.EndListeningCell(rCxt, aCell, *this);
+ }
+ break;
+ case svDoubleRef:
+ endListeningArea(this, rDoc, aCellPos, *t);
+ break;
+ default:
+ ; // nothing
+ }
+ }
+}
+
+bool ScFormulaCell::IsShared() const
+{
+ return bool(mxGroup);
+}
+
+bool ScFormulaCell::IsSharedTop() const
+{
+ if (!mxGroup)
+ return false;
+
+ return mxGroup->mpTopCell == this;
+}
+
+SCROW ScFormulaCell::GetSharedTopRow() const
+{
+ return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1;
+}
+
+SCROW ScFormulaCell::GetSharedLength() const
+{
+ return mxGroup ? mxGroup->mnLength : 0;
+}
+
+sal_Int32 ScFormulaCell::GetWeight() const
+{
+ if (!mxGroup)
+ return 1;
+
+ if (mxGroup->mnWeight > 0)
+ return mxGroup->mnWeight;
+
+ double nSharedCodeWeight = GetSharedCode()->GetWeight();
+ double nResult = nSharedCodeWeight * GetSharedLength();
+ if (nResult < SAL_MAX_INT32)
+ mxGroup->mnWeight = nResult;
+ else
+ mxGroup->mnWeight = SAL_MAX_INT32;
+
+ return mxGroup->mnWeight;
+}
+
+ScTokenArray* ScFormulaCell::GetSharedCode()
+{
+ return mxGroup ? &*mxGroup->mpCode : nullptr;
+}
+
+const ScTokenArray* ScFormulaCell::GetSharedCode() const
+{
+ return mxGroup ? &*mxGroup->mpCode : nullptr;
+}
+
+void ScFormulaCell::SyncSharedCode()
+{
+ if (!mxGroup)
+ // Not a shared formula cell.
+ return;
+
+ pCode = &*mxGroup->mpCode;
+}
+
+#if DUMP_COLUMN_STORAGE
+
+void ScFormulaCell::Dump() const
+{
+ cout << "-- formula cell (" << aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDocument) << ")" << endl;
+ cout << " * shared: " << (mxGroup ? "true" : "false") << endl;
+ if (mxGroup)
+ {
+ cout << " * shared length: " << mxGroup->mnLength << endl;
+ cout << " * shared calc state: " << mxGroup->meCalcState << endl;
+ }
+
+ sc::TokenStringContext aCxt(rDocument, rDocument.GetGrammar());
+ cout << " * code: " << pCode->CreateString(aCxt, aPos) << endl;
+
+ FormulaError nErrCode = pCode->GetCodeError();
+ cout << " * code error: ";
+ if (nErrCode == FormulaError::NONE)
+ cout << "(none)";
+ else
+ {
+ OUString aStr = ScGlobal::GetErrorString(nErrCode);
+ cout << " * code error: " << aStr << " (" << int(nErrCode) << ")";
+ }
+ cout << endl;
+
+ cout << " * result: ";
+ sc::FormulaResultValue aRV = aResult.GetResult();
+ switch (aRV.meType)
+ {
+ case sc::FormulaResultValue::Value:
+ cout << aRV.mfValue << " (value)";
+ break;
+ case sc::FormulaResultValue::String:
+ cout << aRV.maString.getString() << " (string)";
+ break;
+ case sc::FormulaResultValue::Error:
+ cout << ScGlobal::GetErrorString(aRV.mnError) << " (error: " << int(aRV.mnError) << ")";
+ break;
+ case sc::FormulaResultValue::Invalid:
+ cout << "(invalid)";
+ break;
+ default:
+ ;
+ }
+ cout << endl;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/formulaiter.cxx b/sc/source/core/data/formulaiter.cxx
new file mode 100644
index 0000000000..d7f183b2a0
--- /dev/null
+++ b/sc/source/core/data/formulaiter.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 <formulaiter.hxx>
+
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <formula/token.hxx>
+#include <token.hxx>
+
+using namespace formula;
+
+ScDetectiveRefIter::ScDetectiveRefIter( const ScDocument& rDoc, ScFormulaCell* pCell ) :
+ mrDoc(rDoc),
+ maIter(*pCell->GetCode()),
+ aPos(pCell->aPos)
+{
+}
+
+static bool lcl_ScDetectiveRefIter_SkipRef( const ScDocument& rDoc, formula::FormulaToken* p, const ScAddress& rPos )
+{
+ ScSingleRefData& rRef1 = *p->GetSingleRef();
+ ScAddress aAbs1 = rRef1.toAbs(rDoc, rPos);
+ if (!rDoc.ValidAddress(aAbs1))
+ return true;
+ if ( p->GetType() == svDoubleRef || p->GetType() == svExternalDoubleRef )
+ {
+ ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
+ ScAddress aAbs2 = rRef2.toAbs(rDoc, rPos);
+ if (!rDoc.ValidAddress(aAbs2))
+ return true;
+ }
+ return false;
+}
+
+bool ScDetectiveRefIter::GetNextRef( ScRange& rRange )
+{
+ bool bRet = false;
+ formula::FormulaToken* p = GetNextRefToken();
+ if( p )
+ {
+ SingleDoubleRefProvider aProv( *p );
+ rRange.aStart = aProv.Ref1.toAbs(mrDoc, aPos);
+ rRange.aEnd = aProv.Ref2.toAbs(mrDoc, aPos);
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+formula::FormulaToken* ScDetectiveRefIter::GetNextRefToken()
+{
+ formula::FormulaToken* p = maIter.GetNextReferenceRPN();
+ while (p && lcl_ScDetectiveRefIter_SkipRef(mrDoc, p, aPos))
+ {
+ p = maIter.GetNextReferenceRPN();
+ }
+ return p;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/funcdesc.cxx b/sc/source/core/data/funcdesc.cxx
new file mode 100644
index 0000000000..e1fafe145d
--- /dev/null
+++ b/sc/source/core/data/funcdesc.cxx
@@ -0,0 +1,1250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <funcdesc.hxx>
+
+#include <addincol.hxx>
+#include <appoptio.hxx>
+#include <callform.hxx>
+#include <compiler.hxx>
+#include <compiler.hrc>
+#include <global.hxx>
+#include <scfuncs.hrc>
+#include <scmod.hxx>
+#include <scresid.hxx>
+#include <helpids.h>
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <formula/funcvarargs.h>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+namespace {
+
+struct ScFuncDescCore
+{
+ /*
+ * An opcode from include/formula/compiler.hxx
+ */
+ sal_uInt16 nOpCode;
+ /*
+ * Pointer to list of strings
+ */
+ const TranslateId* pResource;
+ /*
+ * Count of list of strings
+ */
+ size_t nResourceLen;
+ /*
+ * 16-bit value:
+ *
+ * Bit 1: boolean flag whether function is suppressed. Usually 0. This
+ * may be used to add UI string resources before UI freeze if
+ * implementation isn't ready yet without displaying them in the
+ * function wizard, most recent used list and other UI elements. Also
+ * not available via API then.
+ *
+ * Bit 2: boolean flag whether function is hidden in the Function
+ * Wizard unless used in an expression.
+ */
+ sal_uInt16 nFunctionFlags;
+ /*
+ * Function group (text, math, ...), one of ID_FUNCTION_GRP_...
+ */
+ sal_uInt16 nCategory;
+ /*
+ * Help ID, HID_FUNC_...
+ */
+ OUString pHelpId;
+ /*
+ * Number of parameters. VAR_ARGS if variable number, or
+ * VAR_ARGS+number if number of fixed parameters and variable
+ * arguments following. Or PAIRED_VAR_ARGS if variable number of
+ * paired parameters, or PAIRED_VAR_ARGS+number if number of fixed
+ * parameters and variable paired arguments following.
+ */
+ sal_uInt16 nArgs;
+ /*
+ * For every parameter:
+ * Boolean flag whether the parameter is optional.
+ */
+ sal_uInt8 aOptionalArgs[7];
+ /*
+ * Limited number of maximum (variable) parameters, or 0 if no specific
+ * limit other than the general VAR_ARGS-1 value.
+ */
+ sal_uInt16 nVarArgsLimit;
+};
+
+}
+
+static void ScFuncRes(const ScFuncDescCore &rEntry, ScFuncDesc*, bool& rbSuppressed);
+
+ScFuncDesc::ScFuncDesc() :
+ pDefArgFlags (nullptr),
+ nFIndex (0),
+ nCategory (0),
+ nArgCount (0),
+ nVarArgsStart (0),
+ nVarArgsLimit (0),
+ bIncomplete (false),
+ mbHidden (false)
+{}
+
+ScFuncDesc::~ScFuncDesc()
+{
+ Clear();
+}
+
+void ScFuncDesc::Clear()
+{
+ sal_uInt16 nArgs = nArgCount;
+ if (nArgs >= PAIRED_VAR_ARGS)
+ nArgs -= PAIRED_VAR_ARGS - 2;
+ else if (nArgs >= VAR_ARGS)
+ nArgs -= VAR_ARGS - 1;
+ if (nArgs)
+ {
+ delete [] pDefArgFlags;
+ }
+ nArgCount = 0;
+ nVarArgsStart = 0;
+ nVarArgsLimit = 0;
+ maDefArgNames.clear();
+ maDefArgDescs.clear();
+ pDefArgFlags = nullptr;
+
+ mxFuncName.reset();
+ mxFuncDesc.reset();
+
+ nFIndex = 0;
+ nCategory = 0;
+ sHelpId.clear();
+ bIncomplete = false;
+ mbHidden = false;
+}
+
+OUString ScFuncDesc::GetParamList() const
+{
+ OUString sep(ScCompiler::GetNativeSymbol(ocSep));
+
+ OUStringBuffer aSig;
+
+ if ( nArgCount > 0 )
+ {
+ if ( nArgCount < VAR_ARGS )
+ {
+ sal_uInt16 nLastSuppressed = nArgCount;
+ sal_uInt16 nLastAdded = nArgCount;
+ for ( sal_uInt16 i=0; i<nArgCount; i++ )
+ {
+ nLastAdded = i;
+ aSig.append(maDefArgNames[i]);
+ if ( i != nArgCount-1 )
+ {
+ aSig.append(sep + " " );
+ }
+ }
+ // If only suppressed parameters follow the last added parameter,
+ // remove one "; "
+ if (nLastSuppressed < nArgCount && nLastAdded < nLastSuppressed &&
+ aSig.getLength() >= 2)
+ aSig.setLength(aSig.getLength() - 2);
+ }
+ else if ( nArgCount < PAIRED_VAR_ARGS)
+ {
+ for ( sal_uInt16 nArg = 0; nArg < nVarArgsStart; nArg++ )
+ {
+ aSig.append(maDefArgNames[nArg] + sep + " ");
+ }
+ /* NOTE: Currently there are no suppressed var args parameters. If
+ * there were, we'd have to cope with it here and above for the fix
+ * parameters. For now parameters are always added, so no special
+ * treatment of a trailing "; " necessary. */
+ aSig.append(maDefArgNames[nVarArgsStart]
+ + "1"
+ + sep + " "
+ + maDefArgNames[nVarArgsStart]
+ + "2"
+ + sep + " ... ");
+ }
+ else
+ {
+ for ( sal_uInt16 nArg = 0; nArg < nVarArgsStart; nArg++ )
+ {
+ aSig.append(maDefArgNames[nArg] + sep + " ");
+ }
+
+ aSig.append(maDefArgNames[nVarArgsStart]
+ + "1" + sep
+ + maDefArgNames[nVarArgsStart+1]
+ + "1" + sep
+ + " "
+ + maDefArgNames[nVarArgsStart]
+ + "2" + sep
+ + maDefArgNames[nVarArgsStart+1]
+ + "2" + sep + " ... " );
+ }
+ }
+
+ return aSig.makeStringAndClear();
+}
+
+OUString ScFuncDesc::getSignature() const
+{
+ OUStringBuffer aSig;
+
+ if(mxFuncName)
+ {
+ aSig.append(*mxFuncName);
+
+ OUString aParamList = GetParamList();
+ if( !aParamList.isEmpty() )
+ {
+ aSig.append( "( " + aParamList
+ // U+00A0 (NBSP) prevents automatic line break
+ + u"\x00A0" ")" );
+ }
+ else
+ aSig.append( "()" );
+ }
+ return aSig.makeStringAndClear();
+}
+
+OUString ScFuncDesc::getFormula( const ::std::vector< OUString >& _aArguments ) const
+{
+ OUString sep = ScCompiler::GetNativeSymbol(ocSep);
+
+ OUStringBuffer aFormula;
+
+ if(mxFuncName)
+ {
+ aFormula.append( *mxFuncName + "(" );
+ if ( nArgCount > 0 && !_aArguments.empty() && !_aArguments[0].isEmpty())
+ {
+ ::std::vector< OUString >::const_iterator aIter = _aArguments.begin();
+ ::std::vector< OUString >::const_iterator aEnd = _aArguments.end();
+
+ aFormula.append( *aIter );
+ ++aIter;
+ while( aIter != aEnd && !aIter->isEmpty() )
+ {
+ aFormula.append( sep + *aIter );
+ ++aIter;
+ }
+ }
+
+ aFormula.append( ")" );
+ }
+ return aFormula.makeStringAndClear();
+}
+
+sal_uInt16 ScFuncDesc::GetSuppressedArgCount() const
+{
+ return nArgCount;
+}
+
+OUString ScFuncDesc::getFunctionName() const
+{
+ OUString sRet;
+ if ( mxFuncName )
+ sRet = *mxFuncName;
+ return sRet;
+}
+
+const formula::IFunctionCategory* ScFuncDesc::getCategory() const
+{
+ return ScGlobal::GetStarCalcFunctionMgr()->getCategory(nCategory - 1);
+}
+
+OUString ScFuncDesc::getDescription() const
+{
+ OUString sRet;
+ if ( mxFuncDesc )
+ sRet = *mxFuncDesc;
+ return sRet;
+}
+
+sal_Int32 ScFuncDesc::getSuppressedArgumentCount() const
+{
+ return GetSuppressedArgCount();
+}
+
+void ScFuncDesc::fillVisibleArgumentMapping(::std::vector<sal_uInt16>& _rArguments) const
+{
+ _rArguments.resize( nArgCount);
+ sal_uInt16 value = 0;
+ for (auto & argument : _rArguments)
+ argument = value++;
+
+ sal_uInt16 nArgs = nArgCount;
+ if (nArgs >= PAIRED_VAR_ARGS)
+ nArgs -= PAIRED_VAR_ARGS - 2;
+ else if (nArgs >= VAR_ARGS)
+ nArgs -= VAR_ARGS - 1;
+ for (sal_uInt16 i=0; i < nArgs; ++i)
+ {
+ _rArguments.push_back(i);
+ }
+}
+
+void ScFuncDesc::initArgumentInfo() const
+{
+ // get the full argument description
+ // (add-in has to be instantiated to get the type information)
+
+ if ( !(bIncomplete && mxFuncName) )
+ return;
+
+ ScUnoAddInCollection& rAddIns = *ScGlobal::GetAddInCollection();
+ OUString aIntName(rAddIns.FindFunction( *mxFuncName, true )); // pFuncName is upper-case
+
+ if ( !aIntName.isEmpty() )
+ {
+ // GetFuncData with bComplete=true loads the component and updates
+ // the global function list if needed.
+
+ rAddIns.GetFuncData( aIntName, true );
+ }
+
+ if ( bIncomplete )
+ {
+ OSL_FAIL( "couldn't initialize add-in function" );
+ const_cast<ScFuncDesc*>(this)->bIncomplete = false; // even if there was an error, don't try again
+ }
+}
+
+OUString ScFuncDesc::getHelpId() const
+{
+ return sHelpId;
+}
+
+bool ScFuncDesc::isHidden() const
+{
+ return mbHidden;
+}
+
+sal_uInt32 ScFuncDesc::getParameterCount() const
+{
+ return nArgCount;
+}
+
+sal_uInt32 ScFuncDesc::getVarArgsStart() const
+{
+ return nVarArgsStart;
+}
+
+sal_uInt32 ScFuncDesc::getVarArgsLimit() const
+{
+ return nVarArgsLimit;
+}
+
+OUString ScFuncDesc::getParameterName(sal_uInt32 _nPos) const
+{
+ return maDefArgNames[_nPos];
+}
+
+OUString ScFuncDesc::getParameterDescription(sal_uInt32 _nPos) const
+{
+ return maDefArgDescs[_nPos];
+}
+
+bool ScFuncDesc::isParameterOptional(sal_uInt32 _nPos) const
+{
+ return pDefArgFlags[_nPos].bOptional;
+}
+
+bool ScFuncDesc::compareByName(const ScFuncDesc* a, const ScFuncDesc* b)
+{
+ return (ScGlobal::GetCaseCollator().compareString(*a->mxFuncName, *b->mxFuncName ) < 0);
+}
+
+#define ENTRY(CODE) CODE, SAL_N_ELEMENTS(CODE)
+
+ScFunctionList::ScFunctionList( bool bEnglishFunctionNames )
+ : mbEnglishFunctionNames( bEnglishFunctionNames )
+{
+ sal_Int32 nMaxFuncNameLen = 0; // Length of longest function name
+
+ // See ScFuncDescCore definition for format details.
+ // This list must be sorted in order of the opcode, dbgutil builds enable _GLIBCXX_DEBUG
+ // which will concept check that the list is sorted on first use to ensure this holds
+ static const ScFuncDescCore aDescs[] =
+ {
+ { SC_OPCODE_IF, ENTRY(SC_OPCODE_IF_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_WENN, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_IF_ERROR, ENTRY(SC_OPCODE_IF_ERROR_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_IFERROR, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_IF_NA, ENTRY(SC_OPCODE_IF_NA_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_IFNA, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CHOOSE, ENTRY(SC_OPCODE_CHOOSE_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_WAHL, VAR_ARGS+1, { 0, 0 }, 31 },
+ { SC_OPCODE_AND, ENTRY(SC_OPCODE_AND_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_UND, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_OR, ENTRY(SC_OPCODE_OR_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_ODER, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_PI, ENTRY(SC_OPCODE_PI_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_PI, 0, { }, 0 },
+ { SC_OPCODE_RANDOM, ENTRY(SC_OPCODE_RANDOM_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ZUFALLSZAHL, 0, { }, 0 },
+ { SC_OPCODE_TRUE, ENTRY(SC_OPCODE_TRUE_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_WAHR, 0, { }, 0 },
+ { SC_OPCODE_FALSE, ENTRY(SC_OPCODE_FALSE_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_FALSCH, 0, { }, 0 },
+ { SC_OPCODE_GET_ACT_DATE, ENTRY(SC_OPCODE_GET_ACT_DATE_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_HEUTE, 0, { }, 0 },
+ { SC_OPCODE_GET_ACT_TIME, ENTRY(SC_OPCODE_GET_ACT_TIME_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_JETZT, 0, { }, 0 },
+ { SC_OPCODE_NO_VALUE, ENTRY(SC_OPCODE_NO_VALUE_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_NV, 0, { }, 0 },
+ { SC_OPCODE_CURRENT, ENTRY(SC_OPCODE_CURRENT_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_AKTUELL, 0, { }, 0 },
+ { SC_OPCODE_RANDOM_NV, ENTRY(SC_OPCODE_RANDOM_NV_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_RAND_NV, 0, { }, 0 },
+ { SC_OPCODE_DEG, ENTRY(SC_OPCODE_DEG_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_DEG, 1, { 0 }, 0 },
+ { SC_OPCODE_RAD, ENTRY(SC_OPCODE_RAD_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_RAD, 1, { 0 }, 0 },
+ { SC_OPCODE_SIN, ENTRY(SC_OPCODE_SIN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SIN, 1, { 0 }, 0 },
+ { SC_OPCODE_COS, ENTRY(SC_OPCODE_COS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COS, 1, { 0 }, 0 },
+ { SC_OPCODE_TAN, ENTRY(SC_OPCODE_TAN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_TAN, 1, { 0 }, 0 },
+ { SC_OPCODE_COT, ENTRY(SC_OPCODE_COT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COT, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_SIN, ENTRY(SC_OPCODE_ARC_SIN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCSIN, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_COS, ENTRY(SC_OPCODE_ARC_COS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCCOS, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_TAN, ENTRY(SC_OPCODE_ARC_TAN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCTAN, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_COT, ENTRY(SC_OPCODE_ARC_COT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCCOT, 1, { 0 }, 0 },
+ { SC_OPCODE_SIN_HYP, ENTRY(SC_OPCODE_SIN_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SINHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_COS_HYP, ENTRY(SC_OPCODE_COS_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COSHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_TAN_HYP, ENTRY(SC_OPCODE_TAN_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_TANHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_COT_HYP, ENTRY(SC_OPCODE_COT_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COTHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_SIN_HYP, ENTRY(SC_OPCODE_ARC_SIN_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARSINHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_COS_HYP, ENTRY(SC_OPCODE_ARC_COS_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCOSHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_TAN_HYP, ENTRY(SC_OPCODE_ARC_TAN_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARTANHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_COT_HYP, ENTRY(SC_OPCODE_ARC_COT_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCOTHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_COSECANT, ENTRY(SC_OPCODE_COSECANT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COSECANT, 1, { 0 }, 0 },
+ { SC_OPCODE_SECANT, ENTRY(SC_OPCODE_SECANT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SECANT, 1, { 0 }, 0 },
+ { SC_OPCODE_COSECANT_HYP, ENTRY(SC_OPCODE_COSECANT_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COSECANTHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_SECANT_HYP, ENTRY(SC_OPCODE_SECANT_HYP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SECANTHYP, 1, { 0 }, 0 },
+ { SC_OPCODE_EXP, ENTRY(SC_OPCODE_EXP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_EXP, 1, { 0 }, 0 },
+ { SC_OPCODE_LN, ENTRY(SC_OPCODE_LN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_LN, 1, { 0 }, 0 },
+ { SC_OPCODE_SQRT, ENTRY(SC_OPCODE_SQRT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_WURZEL, 1, { 0 }, 0 },
+ { SC_OPCODE_FACT, ENTRY(SC_OPCODE_FACT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_FAKULTAET, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_YEAR, ENTRY(SC_OPCODE_GET_YEAR_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_JAHR, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_MONTH, ENTRY(SC_OPCODE_GET_MONTH_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_MONAT, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_DAY, ENTRY(SC_OPCODE_GET_DAY_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_TAG, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_HOUR, ENTRY(SC_OPCODE_GET_HOUR_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_STUNDE, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_MIN, ENTRY(SC_OPCODE_GET_MIN_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_MINUTE, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_SEC, ENTRY(SC_OPCODE_GET_SEC_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_SEKUNDE, 1, { 0 }, 0 },
+ { SC_OPCODE_PLUS_MINUS, ENTRY(SC_OPCODE_PLUS_MINUS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_VORZEICHEN, 1, { 0 }, 0 },
+ { SC_OPCODE_ABS, ENTRY(SC_OPCODE_ABS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ABS, 1, { 0 }, 0 },
+ { SC_OPCODE_INT, ENTRY(SC_OPCODE_INT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_GANZZAHL, 1, { 0 }, 0 },
+ { SC_OPCODE_PHI, ENTRY(SC_OPCODE_PHI_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_PHI, 1, { 0 }, 0 },
+ { SC_OPCODE_GAUSS, ENTRY(SC_OPCODE_GAUSS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAUSS, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_EMPTY, ENTRY(SC_OPCODE_IS_EMPTY_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTLEER, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_STRING, ENTRY(SC_OPCODE_IS_STRING_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTTEXT, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_NON_STRING, ENTRY(SC_OPCODE_IS_NON_STRING_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTKTEXT, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_LOGICAL, ENTRY(SC_OPCODE_IS_LOGICAL_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTLOG, 1, { 0 }, 0 },
+ { SC_OPCODE_TYPE, ENTRY(SC_OPCODE_TYPE_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_TYP, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_REF, ENTRY(SC_OPCODE_IS_REF_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTBEZUG, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_VALUE, ENTRY(SC_OPCODE_IS_VALUE_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTZAHL, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_FORMULA, ENTRY(SC_OPCODE_IS_FORMULA_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTFORMEL, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_NV, ENTRY(SC_OPCODE_IS_NV_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTNV, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_ERR, ENTRY(SC_OPCODE_IS_ERR_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTFEHL, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_ERROR, ENTRY(SC_OPCODE_IS_ERROR_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTFEHLER, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_EVEN, ENTRY(SC_OPCODE_IS_EVEN_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTGERADE, 1, { 0 }, 0 },
+ { SC_OPCODE_IS_ODD, ENTRY(SC_OPCODE_IS_ODD_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ISTUNGERADE, 1, { 0 }, 0 },
+ { SC_OPCODE_N, ENTRY(SC_OPCODE_N_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_N, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_DATE_VALUE, ENTRY(SC_OPCODE_GET_DATE_VALUE_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_DATWERT, 1, { 0 }, 0 },
+ { SC_OPCODE_GET_TIME_VALUE, ENTRY(SC_OPCODE_GET_TIME_VALUE_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_ZEITWERT, 1, { 0 }, 0 },
+ { SC_OPCODE_CODE, ENTRY(SC_OPCODE_CODE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_CODE, 1, { 0 }, 0 },
+ { SC_OPCODE_TRIM, ENTRY(SC_OPCODE_TRIM_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_GLAETTEN, 1, { 0 }, 0 },
+ { SC_OPCODE_UPPER, ENTRY(SC_OPCODE_UPPER_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_GROSS, 1, { 0 }, 0 },
+ { SC_OPCODE_PROPER, ENTRY(SC_OPCODE_PROPER_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_GROSS2, 1, { 0 }, 0 },
+ { SC_OPCODE_LOWER, ENTRY(SC_OPCODE_LOWER_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_KLEIN, 1, { 0 }, 0 },
+ { SC_OPCODE_LEN, ENTRY(SC_OPCODE_LEN_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_LAENGE, 1, { 0 }, 0 },
+ { SC_OPCODE_T, ENTRY(SC_OPCODE_T_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_T, 1, { 0 }, 0 },
+ { SC_OPCODE_VALUE, ENTRY(SC_OPCODE_VALUE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_WERT, 1, { 0 }, 0 },
+ { SC_OPCODE_CLEAN, ENTRY(SC_OPCODE_CLEAN_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_SAEUBERN, 1, { 0 }, 0 },
+ { SC_OPCODE_CHAR, ENTRY(SC_OPCODE_CHAR_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_ZEICHEN, 1, { 0 }, 0 },
+ { SC_OPCODE_LOG10, ENTRY(SC_OPCODE_LOG10_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_LOG10, 1, { 0 }, 0 },
+ { SC_OPCODE_EVEN, ENTRY(SC_OPCODE_EVEN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_GERADE, 1, { 0 }, 0 },
+ { SC_OPCODE_ODD, ENTRY(SC_OPCODE_ODD_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_UNGERADE, 1, { 0 }, 0 },
+ { SC_OPCODE_STD_NORM_DIST, ENTRY(SC_OPCODE_STD_NORM_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STANDNORMVERT, 1, { 0 }, 0 },
+ { SC_OPCODE_FISHER, ENTRY(SC_OPCODE_FISHER_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FISHER, 1, { 0 }, 0 },
+ { SC_OPCODE_FISHER_INV, ENTRY(SC_OPCODE_FISHER_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FISHERINV, 1, { 0 }, 0 },
+ { SC_OPCODE_S_NORM_INV, ENTRY(SC_OPCODE_S_NORM_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STANDNORMINV, 1, { 0 }, 0 },
+ { SC_OPCODE_GAMMA_LN, ENTRY(SC_OPCODE_GAMMA_LN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMALN, 1, { 0 }, 0 },
+ { SC_OPCODE_ERROR_TYPE, ENTRY(SC_OPCODE_ERROR_TYPE_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_FEHLERTYP, 1, { 0 }, 0 },
+ { SC_OPCODE_FORMULA, ENTRY(SC_OPCODE_FORMULA_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_FORMEL, 1, { 0 }, 0 },
+ { SC_OPCODE_ARABIC, ENTRY(SC_OPCODE_ARABIC_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_ARABISCH, 1, { 0 }, 0 },
+ { SC_OPCODE_INFO, ENTRY(SC_OPCODE_INFO_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_INFO, 1, { 0 }, 0 },
+ { SC_OPCODE_BAHTTEXT, ENTRY(SC_OPCODE_BAHTTEXT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_BAHTTEXT, 1, { 0 }, 0 },
+ { SC_OPCODE_JIS, ENTRY(SC_OPCODE_JIS_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_JIS, 1, { 0 }, 0 },
+ { SC_OPCODE_ASC, ENTRY(SC_OPCODE_ASC_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_ASC, 1, { 0 }, 0 },
+ { SC_OPCODE_UNICODE, ENTRY(SC_OPCODE_UNICODE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_UNICODE, 1, { 0 }, 0 },
+ { SC_OPCODE_UNICHAR, ENTRY(SC_OPCODE_UNICHAR_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_UNICHAR, 1, { 0 }, 0 },
+ { SC_OPCODE_GAMMA, ENTRY(SC_OPCODE_GAMMA_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMA, 1, { 0 }, 0 },
+ { SC_OPCODE_GAMMA_LN_MS, ENTRY(SC_OPCODE_GAMMA_LN_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMALN_MS, 1, { 0 }, 0 },
+ { SC_OPCODE_ERF_MS, ENTRY(SC_OPCODE_ERF_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ERF_MS, 1, { 0 }, 0 },
+ { SC_OPCODE_ERFC_MS, ENTRY(SC_OPCODE_ERFC_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ERFC_MS, 1, { 0 }, 0 },
+ { SC_OPCODE_ERROR_TYPE_ODF, ENTRY(SC_OPCODE_ERROR_TYPE_ODF_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_ERROR_TYPE_ODF, 1, { 0 }, 0 },
+ { SC_OPCODE_ENCODEURL, ENTRY(SC_OPCODE_ENCODEURL_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_ENCODEURL, 1, { 0 }, 0 },
+ { SC_OPCODE_ISOWEEKNUM, ENTRY(SC_OPCODE_ISOWEEKNUM_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_ISOWEEKNUM, 1, { 0 }, 0 },
+ { SC_OPCODE_NOT, ENTRY(SC_OPCODE_NOT_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_NICHT, 1, { 0 }, 0 },
+ { SC_OPCODE_ARC_TAN_2, ENTRY(SC_OPCODE_ARC_TAN_2_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ARCTAN2, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CEIL, ENTRY(SC_OPCODE_CEIL_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_OBERGRENZE, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_FLOOR, ENTRY(SC_OPCODE_FLOOR_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_UNTERGRENZE, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_ROUND, ENTRY(SC_OPCODE_ROUND_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_RUNDEN, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_ROUND_UP, ENTRY(SC_OPCODE_ROUND_UP_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_AUFRUNDEN, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_ROUND_DOWN, ENTRY(SC_OPCODE_ROUND_DOWN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ABRUNDEN, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_TRUNC, ENTRY(SC_OPCODE_TRUNC_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_KUERZEN, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_LOG, ENTRY(SC_OPCODE_LOG_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_LOG, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_POWER, ENTRY(SC_OPCODE_POWER_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_POTENZ, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_GCD, ENTRY(SC_OPCODE_GCD_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_GGT, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_LCM, ENTRY(SC_OPCODE_LCM_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_KGV, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MOD, ENTRY(SC_OPCODE_MOD_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_REST, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SUM_PRODUCT, ENTRY(SC_OPCODE_SUM_PRODUCT_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_SUMMENPRODUKT, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_SUM_SQ, ENTRY(SC_OPCODE_SUM_SQ_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_QUADRATESUMME, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_SUM_X2MY2, ENTRY(SC_OPCODE_SUM_X2MY2_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_SUMMEX2MY2, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SUM_X2DY2, ENTRY(SC_OPCODE_SUM_X2DY2_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_SUMMEX2PY2, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SUM_XMY2, ENTRY(SC_OPCODE_SUM_XMY2_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_SUMMEXMY2, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_GET_DATE, ENTRY(SC_OPCODE_GET_DATE_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_DATUM, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_GET_TIME, ENTRY(SC_OPCODE_GET_TIME_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_ZEIT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_GET_DIFF_DATE, ENTRY(SC_OPCODE_GET_DIFF_DATE_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_TAGE, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_GET_DIFF_DATE_360, ENTRY(SC_OPCODE_GET_DIFF_DATE_360_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_TAGE360, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_MIN, ENTRY(SC_OPCODE_MIN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MIN, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MAX, ENTRY(SC_OPCODE_MAX_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MAX, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_SUM, ENTRY(SC_OPCODE_SUM_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SUMME, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_PRODUCT, ENTRY(SC_OPCODE_PRODUCT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_PRODUKT, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_AVERAGE, ENTRY(SC_OPCODE_AVERAGE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MITTELWERT, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_COUNT, ENTRY(SC_OPCODE_COUNT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ANZAHL, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_COUNT_2, ENTRY(SC_OPCODE_COUNT_2_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ANZAHL2, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_NPV, ENTRY(SC_OPCODE_NPV_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_NBW, VAR_ARGS+1, { 0, 0 }, 0 },
+ { SC_OPCODE_IRR, ENTRY(SC_OPCODE_IRR_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_IKV, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_VAR, ENTRY(SC_OPCODE_VAR_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VARIANZ, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_VAR_P, ENTRY(SC_OPCODE_VAR_P_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VARIANZEN, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_ST_DEV, ENTRY(SC_OPCODE_ST_DEV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STABW, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_ST_DEV_P, ENTRY(SC_OPCODE_ST_DEV_P_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STABWN, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_B, ENTRY(SC_OPCODE_B_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_B, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_NORM_DIST, ENTRY(SC_OPCODE_NORM_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_NORMVERT, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_EXP_DIST, ENTRY(SC_OPCODE_EXP_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_EXPONVERT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_BINOM_DIST, ENTRY(SC_OPCODE_BINOM_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BINOMVERT, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_POISSON_DIST, ENTRY(SC_OPCODE_POISSON_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_POISSON, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_COMBIN, ENTRY(SC_OPCODE_COMBIN_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_KOMBINATIONEN, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_COMBIN_A, ENTRY(SC_OPCODE_COMBIN_A_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_KOMBINATIONEN2, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PERMUT, ENTRY(SC_OPCODE_PERMUT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VARIATIONEN, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PERMUTATION_A, ENTRY(SC_OPCODE_PERMUTATION_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VARIATIONEN2, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PV, ENTRY(SC_OPCODE_PV_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_BW, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_SYD, ENTRY(SC_OPCODE_SYD_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_DIA, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_DDB, ENTRY(SC_OPCODE_DDB_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_GDA, 5, { 0, 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_DB, ENTRY(SC_OPCODE_DB_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_GDA2, 5, { 0, 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_VBD , ENTRY(SC_OPCODE_VBD_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_VDB, 7, { 0, 0, 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_PDURATION, ENTRY(SC_OPCODE_PDURATION_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_LAUFZEIT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_SLN, ENTRY(SC_OPCODE_SLN_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_LIA, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_PMT, ENTRY(SC_OPCODE_PMT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_RMZ, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_COLUMNS, ENTRY(SC_OPCODE_COLUMNS_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_SPALTEN, 1, { 0 }, 0 },
+ { SC_OPCODE_ROWS, ENTRY(SC_OPCODE_ROWS_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_ZEILEN, 1, { 0 }, 0 },
+ { SC_OPCODE_COLUMN, ENTRY(SC_OPCODE_COLUMN_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_SPALTE, 1, { 1 }, 0 },
+ { SC_OPCODE_ROW, ENTRY(SC_OPCODE_ROW_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_ZEILE, 1, { 1 }, 0 },
+ { SC_OPCODE_RRI, ENTRY(SC_OPCODE_RRI_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ZGZ, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_FV, ENTRY(SC_OPCODE_FV_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ZW, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_NPER, ENTRY(SC_OPCODE_NPER_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ZZR, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_RATE, ENTRY(SC_OPCODE_RATE_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ZINS, 6, { 0, 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_IPMT, ENTRY(SC_OPCODE_IPMT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ZINSZ, 6, { 0, 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_PPMT, ENTRY(SC_OPCODE_PPMT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_KAPZ, 6, { 0, 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_CUM_IPMT, ENTRY(SC_OPCODE_CUM_IPMT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_KUMZINSZ, 6, { 0, 0, 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_CUM_PRINC, ENTRY(SC_OPCODE_CUM_PRINC_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_KUMKAPITAL, 6, { 0, 0, 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_EFFECT, ENTRY(SC_OPCODE_EFFECT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_EFFEKTIV, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_NOMINAL, ENTRY(SC_OPCODE_NOMINAL_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_NOMINAL, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SUB_TOTAL, ENTRY(SC_OPCODE_SUB_TOTAL_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_TEILERGEBNIS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_DB_SUM, ENTRY(SC_OPCODE_DB_SUM_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBSUMME, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_COUNT, ENTRY(SC_OPCODE_DB_COUNT_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBANZAHL, 3, { 0, 1, 0 }, 0 },
+ { SC_OPCODE_DB_COUNT_2, ENTRY(SC_OPCODE_DB_COUNT_2_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBANZAHL2, 3, { 0, 1, 0 }, 0 },
+ { SC_OPCODE_DB_AVERAGE, ENTRY(SC_OPCODE_DB_AVERAGE_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBMITTELWERT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_GET, ENTRY(SC_OPCODE_DB_GET_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBAUSZUG, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_MAX, ENTRY(SC_OPCODE_DB_MAX_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBMAX, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_MIN, ENTRY(SC_OPCODE_DB_MIN_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBMIN, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_PRODUCT, ENTRY(SC_OPCODE_DB_PRODUCT_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBPRODUKT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_STD_DEV, ENTRY(SC_OPCODE_DB_STD_DEV_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBSTDABW, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_STD_DEV_P, ENTRY(SC_OPCODE_DB_STD_DEV_P_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBSTDABWN, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_VAR, ENTRY(SC_OPCODE_DB_VAR_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBVARIANZ, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_DB_VAR_P, ENTRY(SC_OPCODE_DB_VAR_P_ARY), 0, ID_FUNCTION_GRP_DATABASE, HID_FUNC_DBVARIANZEN, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_INDIRECT, ENTRY(SC_OPCODE_INDIRECT_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_INDIREKT, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_ADDRESS, ENTRY(SC_OPCODE_ADDRESS_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_ADRESSE, 5, { 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_MATCH, ENTRY(SC_OPCODE_MATCH_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_VERGLEICH, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_COUNT_EMPTY_CELLS, ENTRY(SC_OPCODE_COUNT_EMPTY_CELLS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ANZAHLLEEREZELLEN, 1, { 0 }, 0 },
+ { SC_OPCODE_COUNT_IF, ENTRY(SC_OPCODE_COUNT_IF_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ZAEHLENWENN, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SUM_IF, ENTRY(SC_OPCODE_SUM_IF_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SUMMEWENN, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_LOOKUP, ENTRY(SC_OPCODE_LOOKUP_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_VERWEIS, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_V_LOOKUP, ENTRY(SC_OPCODE_V_LOOKUP_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_SVERWEIS, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_H_LOOKUP, ENTRY(SC_OPCODE_H_LOOKUP_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_WVERWEIS, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_OFFSET, ENTRY(SC_OPCODE_OFFSET_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_VERSCHIEBUNG, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_INDEX, ENTRY(SC_OPCODE_INDEX_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_INDEX, 4, { 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_AREAS, ENTRY(SC_OPCODE_AREAS_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_BEREICHE, 1, { 0 }, 0 },
+ { SC_OPCODE_CURRENCY, ENTRY(SC_OPCODE_CURRENCY_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_DM, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_REPLACE, ENTRY(SC_OPCODE_REPLACE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_ERSETZEN, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_FIXED, ENTRY(SC_OPCODE_FIXED_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_FEST, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_FIND, ENTRY(SC_OPCODE_FIND_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_FINDEN, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_EXACT, ENTRY(SC_OPCODE_EXACT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_IDENTISCH, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_LEFT, ENTRY(SC_OPCODE_LEFT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_LINKS, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_RIGHT, ENTRY(SC_OPCODE_RIGHT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_RECHTS, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_SEARCH, ENTRY(SC_OPCODE_SEARCH_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_SUCHEN, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_MID, ENTRY(SC_OPCODE_MID_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_TEIL, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_TEXT, ENTRY(SC_OPCODE_TEXT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_TEXT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SUBSTITUTE, ENTRY(SC_OPCODE_SUBSTITUTE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_WECHSELN, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_REPT, ENTRY(SC_OPCODE_REPT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_WIEDERHOLEN, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CONCAT, ENTRY(SC_OPCODE_CONCAT_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_VERKETTEN, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MAT_DET, ENTRY(SC_OPCODE_MAT_DET_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_MDET, 1, { 0 }, 0 },
+ { SC_OPCODE_MAT_INV, ENTRY(SC_OPCODE_MAT_INV_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_MINV, 1, { 0 }, 0 },
+ { SC_OPCODE_MAT_MULT, ENTRY(SC_OPCODE_MAT_MULT_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_MMULT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_MAT_TRANS, ENTRY(SC_OPCODE_MAT_TRANS_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_MTRANS, 1, { 0 }, 0 },
+ { SC_OPCODE_MATRIX_UNIT, ENTRY(SC_OPCODE_MATRIX_UNIT_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_EINHEITSMATRIX, 1, { 0 }, 0 },
+ { SC_OPCODE_HYP_GEOM_DIST, ENTRY(SC_OPCODE_HYP_GEOM_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_HYPGEOMVERT, 5, { 0, 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_LOG_NORM_DIST, ENTRY(SC_OPCODE_LOG_NORM_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_LOGNORMVERT, 4, { 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_T_DIST, ENTRY(SC_OPCODE_T_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TVERT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_F_DIST, ENTRY(SC_OPCODE_F_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FVERT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CHI_DIST, ENTRY(SC_OPCODE_CHI_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHIVERT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_WEIBULL, ENTRY(SC_OPCODE_WEIBULL_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_WEIBULL, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_NEG_BINOM_VERT, ENTRY(SC_OPCODE_NEG_BINOM_VERT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_NEGBINOMVERT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CRIT_BINOM, ENTRY(SC_OPCODE_CRIT_BINOM_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KRITBINOM, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_KURT, ENTRY(SC_OPCODE_KURT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KURT, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_HAR_MEAN, ENTRY(SC_OPCODE_HAR_MEAN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_HARMITTEL, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_GEO_MEAN, ENTRY(SC_OPCODE_GEO_MEAN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GEOMITTEL, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_STANDARD, ENTRY(SC_OPCODE_STANDARD_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STANDARDISIERUNG, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_AVE_DEV, ENTRY(SC_OPCODE_AVE_DEV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MITTELABW, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_SKEW, ENTRY(SC_OPCODE_SKEW_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_SCHIEFE, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_DEV_SQ, ENTRY(SC_OPCODE_DEV_SQ_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_SUMQUADABW, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MEDIAN, ENTRY(SC_OPCODE_MEDIAN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MEDIAN, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MODAL_VALUE, ENTRY(SC_OPCODE_MODAL_VALUE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MODALWERT, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_Z_TEST, ENTRY(SC_OPCODE_Z_TEST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GTEST, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_T_TEST, ENTRY(SC_OPCODE_T_TEST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TTEST, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_RANK, ENTRY(SC_OPCODE_RANK_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_RANG, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_PERCENTILE, ENTRY(SC_OPCODE_PERCENTILE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_QUANTIL, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PERCENT_RANK, ENTRY(SC_OPCODE_PERCENT_RANK_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_QUANTILSRANG, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_LARGE, ENTRY(SC_OPCODE_LARGE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KGROESSTE, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SMALL, ENTRY(SC_OPCODE_SMALL_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KKLEINSTE, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_FREQUENCY, ENTRY(SC_OPCODE_FREQUENCY_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_HAEUFIGKEIT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_QUARTILE, ENTRY(SC_OPCODE_QUARTILE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_QUARTILE, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_NORM_INV, ENTRY(SC_OPCODE_NORM_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_NORMINV, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CONFIDENCE, ENTRY(SC_OPCODE_CONFIDENCE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KONFIDENZ, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_F_TEST, ENTRY(SC_OPCODE_F_TEST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FTEST, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_TRIM_MEAN, ENTRY(SC_OPCODE_TRIM_MEAN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GESTUTZTMITTEL, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PROB, ENTRY(SC_OPCODE_PROB_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_WAHRSCHBEREICH, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_CORREL, ENTRY(SC_OPCODE_CORREL_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KORREL, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_COVAR, ENTRY(SC_OPCODE_COVAR_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_KOVAR, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PEARSON, ENTRY(SC_OPCODE_PEARSON_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_PEARSON, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_RSQ, ENTRY(SC_OPCODE_RSQ_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BESTIMMTHEITSMASS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_STEYX, ENTRY(SC_OPCODE_STEYX_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STFEHLERYX, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_SLOPE, ENTRY(SC_OPCODE_SLOPE_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STEIGUNG, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_INTERCEPT, ENTRY(SC_OPCODE_INTERCEPT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ACHSENABSCHNITT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_TREND, ENTRY(SC_OPCODE_TREND_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_TREND, 4, { 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_GROWTH, ENTRY(SC_OPCODE_GROWTH_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_VARIATION, 4, { 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_LINEST, ENTRY(SC_OPCODE_LINEST_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_RGP, 4, { 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_LOGEST, ENTRY(SC_OPCODE_LOGEST_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_RKP, 4, { 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST, ENTRY(SC_OPCODE_FORECAST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_SCHAETZER, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CHI_INV, ENTRY(SC_OPCODE_CHI_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHIINV, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_GAMMA_DIST, ENTRY(SC_OPCODE_GAMMA_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMAVERT, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_GAMMA_INV, ENTRY(SC_OPCODE_GAMMA_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMAINV, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_T_INV, ENTRY(SC_OPCODE_T_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TINV, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_F_INV, ENTRY(SC_OPCODE_F_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FINV, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CHI_TEST, ENTRY(SC_OPCODE_CHI_TEST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHITEST, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_LOG_INV, ENTRY(SC_OPCODE_LOG_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_LOGINV, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_BETA_DIST, ENTRY(SC_OPCODE_BETA_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BETAVERT, 6, { 0, 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_BETA_INV, ENTRY(SC_OPCODE_BETA_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BETAINV, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_WEEK, ENTRY(SC_OPCODE_WEEK_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_KALENDERWOCHE, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_GET_DAY_OF_WEEK, ENTRY(SC_OPCODE_GET_DAY_OF_WEEK_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_WOCHENTAG, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_STYLE, ENTRY(SC_OPCODE_STYLE_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_VORLAGE, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_DDE, ENTRY(SC_OPCODE_DDE_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_DDE, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_BASE, ENTRY(SC_OPCODE_BASE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_BASIS, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_SHEET, ENTRY(SC_OPCODE_SHEET_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_TABELLE, 1, { 1 }, 0 },
+ { SC_OPCODE_SHEETS, ENTRY(SC_OPCODE_SHEETS_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_TABELLEN, 1, { 1 }, 0 },
+ { SC_OPCODE_MIN_A, ENTRY(SC_OPCODE_MIN_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MINA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MAX_A, ENTRY(SC_OPCODE_MAX_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MAXA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_AVERAGE_A, ENTRY(SC_OPCODE_AVERAGE_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MITTELWERTA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_ST_DEV_A, ENTRY(SC_OPCODE_ST_DEV_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STABWA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_ST_DEV_P_A, ENTRY(SC_OPCODE_ST_DEV_P_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STABWNA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_VAR_A, ENTRY(SC_OPCODE_VAR_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VARIANZA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_VAR_P_A, ENTRY(SC_OPCODE_VAR_P_A_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VARIANZENA, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_EASTERSUNDAY, ENTRY(SC_OPCODE_EASTERSUNDAY_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_OSTERSONNTAG, 1, { 0 }, 0 },
+ { SC_OPCODE_DECIMAL, ENTRY(SC_OPCODE_DECIMAL_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_DEZIMAL, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CONVERT_OOO, ENTRY(SC_OPCODE_CONVERT_OOO_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_UMRECHNEN, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_ROMAN, ENTRY(SC_OPCODE_ROMAN_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_ROEMISCH, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_MIRR, ENTRY(SC_OPCODE_MIRR_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_QIKV, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CELL, ENTRY(SC_OPCODE_CELL_ARY), 0, ID_FUNCTION_GRP_INFO, HID_FUNC_ZELLE, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_ISPMT, ENTRY(SC_OPCODE_ISPMT_ARY), 0, ID_FUNCTION_GRP_FINANCIAL, HID_FUNC_ISPMT, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_HYPERLINK, ENTRY(SC_OPCODE_HYPERLINK_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_HYPERLINK, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_GET_PIVOT_DATA, ENTRY(SC_OPCODE_GET_PIVOT_DATA_ARY), 0, ID_FUNCTION_GRP_TABLE, HID_FUNC_GETPIVOTDATA, VAR_ARGS+2, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_EUROCONVERT, ENTRY(SC_OPCODE_EUROCONVERT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_EUROCONVERT, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_NUMBERVALUE, ENTRY(SC_OPCODE_NUMBERVALUE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_NUMBERVALUE, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_CHISQ_DIST, ENTRY(SC_OPCODE_CHISQ_DIST_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHISQDIST, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_CHISQ_INV, ENTRY(SC_OPCODE_CHISQ_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHISQINV, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_BITAND, ENTRY(SC_OPCODE_BITAND_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_BITAND, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_BITOR, ENTRY(SC_OPCODE_BITOR_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_BITOR, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_BITXOR, ENTRY(SC_OPCODE_BITXOR_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_BITXOR, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_BITRSHIFT, ENTRY(SC_OPCODE_BITRSHIFT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_BITRSHIFT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_BITLSHIFT, ENTRY(SC_OPCODE_BITLSHIFT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_BITLSHIFT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_GET_DATEDIF, ENTRY(SC_OPCODE_GET_DATEDIF_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_DATEDIF, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_XOR, ENTRY(SC_OPCODE_XOR_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_XOR, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_AVERAGE_IF, ENTRY(SC_OPCODE_AVERAGE_IF_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_AVERAGEIF, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_SUM_IFS, ENTRY(SC_OPCODE_SUM_IFS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_SUMIFS, PAIRED_VAR_ARGS+1, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_AVERAGE_IFS, ENTRY(SC_OPCODE_AVERAGE_IFS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_AVERAGEIFS, PAIRED_VAR_ARGS+1, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_COUNT_IFS, ENTRY(SC_OPCODE_COUNT_IFS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_COUNTIFS, PAIRED_VAR_ARGS, { 0, 0 }, 0 },
+ { SC_OPCODE_SKEWP, ENTRY(SC_OPCODE_SKEWP_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_SKEWP, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_LENB, ENTRY(SC_OPCODE_LENB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_LENB, 1, { 0 }, 0 },
+ { SC_OPCODE_RIGHTB, ENTRY(SC_OPCODE_RIGHTB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_RIGHTB, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_LEFTB, ENTRY(SC_OPCODE_LEFTB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_LEFTB, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_MIDB, ENTRY(SC_OPCODE_MIDB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_MIDB, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_FILTERXML, ENTRY(SC_OPCODE_FILTERXML_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_FILTERXML, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_WEBSERVICE, ENTRY(SC_OPCODE_WEBSERVICE_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_WEBSERVICE, 1, { 0, 0 }, 0 },
+ { SC_OPCODE_COVARIANCE_S, ENTRY(SC_OPCODE_COVARIANCE_S_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_COVARIANCE_S, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_COVARIANCE_P, ENTRY(SC_OPCODE_COVARIANCE_P_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_COVARIANCE_P, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_ST_DEV_P_MS, ENTRY(SC_OPCODE_ST_DEV_P_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ST_DEV_P_MS, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_ST_DEV_S, ENTRY(SC_OPCODE_ST_DEV_S_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_ST_DEV_S, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_VAR_P_MS, ENTRY(SC_OPCODE_VAR_P_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VAR_P_MS, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_VAR_S, ENTRY(SC_OPCODE_VAR_S_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_VAR_S, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_BETA_DIST_MS, ENTRY(SC_OPCODE_BETA_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BETADIST_MS, 6, { 0, 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_BETA_INV_MS, ENTRY(SC_OPCODE_BETA_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BETAINV_MS, 5, { 0, 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_BINOM_DIST_MS, ENTRY(SC_OPCODE_BINOM_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BINOM_DIST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_BINOM_INV, ENTRY(SC_OPCODE_BINOM_INV_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_BINOM_INV_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CHI_DIST_MS, ENTRY(SC_OPCODE_CHI_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHIVERT_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CHI_INV_MS, ENTRY(SC_OPCODE_CHI_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHIINV_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CHI_TEST_MS, ENTRY(SC_OPCODE_CHI_TEST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHITEST_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CHISQ_DIST_MS, ENTRY(SC_OPCODE_CHISQ_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHISQDIST_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CHISQ_INV_MS, ENTRY(SC_OPCODE_CHISQ_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CHISQINV_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CONFIDENCE_N, ENTRY(SC_OPCODE_CONFIDENCE_N_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CONFIDENCE_N, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CONFIDENCE_T, ENTRY(SC_OPCODE_CONFIDENCE_T_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_CONFIDENCE_T, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_F_DIST_LT, ENTRY(SC_OPCODE_F_DIST_LT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_F_DIST_LT, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_F_DIST_RT, ENTRY(SC_OPCODE_F_DIST_RT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_F_DIST_RT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_F_INV_LT, ENTRY(SC_OPCODE_F_INV_LT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_F_INV_LT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_F_INV_RT, ENTRY(SC_OPCODE_F_INV_RT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_F_INV_RT, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_F_TEST_MS, ENTRY(SC_OPCODE_F_TEST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_F_TEST_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_EXP_DIST_MS, ENTRY(SC_OPCODE_EXP_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_EXP_DIST_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_HYP_GEOM_DIST_MS, ENTRY(SC_OPCODE_HYP_GEOM_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_HYP_GEOM_DIST_MS, 5, { 0, 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_POISSON_DIST_MS, ENTRY(SC_OPCODE_POISSON_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_POISSON_DIST_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_WEIBULL_MS, ENTRY(SC_OPCODE_WEIBULL_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_WEIBULL_DIST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_GAMMA_DIST_MS, ENTRY(SC_OPCODE_GAMMA_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMADIST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_GAMMA_INV_MS, ENTRY(SC_OPCODE_GAMMA_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_GAMMAINV_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_LOG_NORM_DIST_MS, ENTRY(SC_OPCODE_LOG_NORM_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_LOGNORMDIST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_LOG_INV_MS, ENTRY(SC_OPCODE_LOG_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_LOGINV, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_NORM_DIST_MS, ENTRY(SC_OPCODE_NORM_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_NORMDIST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_NORM_INV_MS, ENTRY(SC_OPCODE_NORM_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_NORMINV_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_STD_NORM_DIST_MS, ENTRY(SC_OPCODE_STD_NORM_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STD_NORMDIST_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_S_NORM_INV_MS, ENTRY(SC_OPCODE_S_NORM_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_STD_NORMINV_MS, 1, { 0 }, 0 },
+ { SC_OPCODE_T_DIST_MS, ENTRY(SC_OPCODE_T_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TDIST_MS, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_T_DIST_RT, ENTRY(SC_OPCODE_T_DIST_RT_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TDIST_RT, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_T_DIST_2T, ENTRY(SC_OPCODE_T_DIST_2T_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TDIST_2T, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_T_INV_2T, ENTRY(SC_OPCODE_T_INV_2T_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TINV_2T, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_T_INV_MS, ENTRY(SC_OPCODE_T_INV_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TINV_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_T_TEST_MS, ENTRY(SC_OPCODE_T_TEST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_TTEST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_PERCENTILE_INC, ENTRY(SC_OPCODE_PERCENTILE_INC_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_PERCENTILE_INC, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PERCENT_RANK_INC, ENTRY(SC_OPCODE_PERCENT_RANK_INC_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_PERCENTRANK_INC, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_QUARTILE_INC, ENTRY(SC_OPCODE_QUARTILE_INC_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_QUARTILE_INC, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_RANK_EQ, ENTRY(SC_OPCODE_RANK_EQ_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_RANK_EQ, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_PERCENTILE_EXC, ENTRY(SC_OPCODE_PERCENTILE_EXC_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_PERCENTILE_EXC, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_PERCENT_RANK_EXC, ENTRY(SC_OPCODE_PERCENT_RANK_EXC_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_PERCENTRANK_EXC, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_QUARTILE_EXC, ENTRY(SC_OPCODE_QUARTILE_EXC_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_QUARTILE_EXC, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_RANK_AVG, ENTRY(SC_OPCODE_RANK_AVG_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_RANK_AVG, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_MODAL_VALUE_MS, ENTRY(SC_OPCODE_MODAL_VALUE_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MODAL_VALUE_MS, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_MODAL_VALUE_MULTI, ENTRY(SC_OPCODE_MODAL_VALUE_MULTI_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MODAL_VALUE_MULTI, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_NEG_BINOM_DIST_MS, ENTRY(SC_OPCODE_NEG_BINOM_DIST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_NEGBINOMDIST_MS, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_Z_TEST_MS, ENTRY(SC_OPCODE_Z_TEST_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_Z_TEST_MS, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_CEIL_MS, ENTRY(SC_OPCODE_CEIL_MS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_CEIL_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_CEIL_ISO, ENTRY(SC_OPCODE_CEIL_ISO_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_CEIL_ISO, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_FLOOR_MS, ENTRY(SC_OPCODE_FLOOR_MS_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_FLOOR_MS, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_NETWORKDAYS_MS, ENTRY(SC_OPCODE_NETWORKDAYS_MS_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_NETWORKDAYS_MS, 4, { 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_WORKDAY_MS, ENTRY(SC_OPCODE_WORKDAY_MS_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_WORKDAY_MS, 4, { 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_AGGREGATE, ENTRY(SC_OPCODE_AGGREGATE_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_AGGREGATE, VAR_ARGS+3, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_COLOR, ENTRY(SC_OPCODE_COLOR_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_COLOR, 4, { 0, 0, 0, 1 }, 0 },
+ { SC_OPCODE_CEIL_MATH, ENTRY(SC_OPCODE_CEIL_MATH_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_CEIL_MATH, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_CEIL_PRECISE, ENTRY(SC_OPCODE_CEIL_PRECISE_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_CEIL_PRECISE, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_NETWORKDAYS, ENTRY(SC_OPCODE_NETWORKDAYS_ARY), 0, ID_FUNCTION_GRP_DATETIME, HID_FUNC_NETWORKDAYS, 4, { 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_FLOOR_MATH, ENTRY(SC_OPCODE_FLOOR_MATH_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_FLOOR_MATH, 3, { 0, 1, 1 }, 0 },
+ { SC_OPCODE_FLOOR_PRECISE, ENTRY(SC_OPCODE_FLOOR_PRECISE_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_FLOOR_PRECISE, 2, { 0, 1 }, 0 },
+ { SC_OPCODE_RAWSUBTRACT, ENTRY(SC_OPCODE_RAWSUBTRACT_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_RAWSUBTRACT, VAR_ARGS+2, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_WEEKNUM_OOO, ENTRY(SC_OPCODE_WEEKNUM_OOO_ARY), 2, ID_FUNCTION_GRP_DATETIME, HID_FUNC_WEEKNUM_OOO, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_ADD, ENTRY(SC_OPCODE_FORECAST_ETS_ADD_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_ADD, 6, { 0, 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_SEA, ENTRY(SC_OPCODE_FORECAST_ETS_SEA_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_SEA, 4, { 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_MUL, ENTRY(SC_OPCODE_FORECAST_ETS_MUL_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_MUL, 6, { 0, 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_PIA, ENTRY(SC_OPCODE_FORECAST_ETS_PIA_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_PIA, 7, { 0, 0, 0, 1, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_PIM, ENTRY(SC_OPCODE_FORECAST_ETS_PIM_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_PIM, 7, { 0, 0, 0, 1, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_STA, ENTRY(SC_OPCODE_FORECAST_ETS_STA_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_STA, 6, { 0, 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_ETS_STM, ENTRY(SC_OPCODE_FORECAST_ETS_STM_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_ETS_STM, 6, { 0, 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_FORECAST_LIN, ENTRY(SC_OPCODE_FORECAST_LIN_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_FORECAST_LIN, 3, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_CONCAT_MS, ENTRY(SC_OPCODE_CONCAT_MS_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_CONCAT_MS, VAR_ARGS, { 0 }, 0 },
+ { SC_OPCODE_TEXTJOIN_MS, ENTRY(SC_OPCODE_TEXTJOIN_MS_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_TEXTJOIN_MS, VAR_ARGS + 2, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_IFS_MS, ENTRY(SC_OPCODE_IFS_MS_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_IFS_MS, PAIRED_VAR_ARGS, { 0, 0 }, 0 },
+ { SC_OPCODE_SWITCH_MS, ENTRY(SC_OPCODE_SWITCH_MS_ARY), 0, ID_FUNCTION_GRP_LOGIC, HID_FUNC_SWITCH_MS, PAIRED_VAR_ARGS + 1, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_MINIFS_MS, ENTRY(SC_OPCODE_MINIFS_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MINIFS_MS, PAIRED_VAR_ARGS + 1, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_MAXIFS_MS, ENTRY(SC_OPCODE_MAXIFS_MS_ARY), 0, ID_FUNCTION_GRP_STATISTIC, HID_FUNC_MAXIFS_MS, PAIRED_VAR_ARGS + 1, { 0, 0, 0 }, 0 },
+ { SC_OPCODE_ROUNDSIG, ENTRY(SC_OPCODE_ROUNDSIG_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_ROUNDSIG, 2, { 0, 0 }, 0 },
+ { SC_OPCODE_REPLACEB, ENTRY(SC_OPCODE_REPLACEB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_REPLACEB, 4, { 0, 0, 0, 0 }, 0 },
+ { SC_OPCODE_FINDB, ENTRY(SC_OPCODE_FINDB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_FINDB, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_SEARCHB, ENTRY(SC_OPCODE_SEARCHB_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_SEARCHB, 3, { 0, 0, 1 }, 0 },
+ { SC_OPCODE_REGEX, ENTRY(SC_OPCODE_REGEX_ARY), 0, ID_FUNCTION_GRP_TEXT, HID_FUNC_REGEX, 4, { 0, 0, 1, 1 }, 0 },
+ { SC_OPCODE_FOURIER, ENTRY(SC_OPCODE_FOURIER_ARY), 0, ID_FUNCTION_GRP_MATRIX, HID_FUNC_FOURIER, 5, { 0, 0, 1, 1, 1 }, 0 },
+ { SC_OPCODE_RANDBETWEEN_NV, ENTRY(SC_OPCODE_RANDBETWEEN_NV_ARY), 0, ID_FUNCTION_GRP_MATH, HID_FUNC_RANDBETWEEN_NV, 2, { 0, 0 }, 0 }
+ };
+
+ ScFuncDesc* pDesc = nullptr;
+ sal_Int32 nStrLen = 0;
+ ::std::vector<const ScFuncDesc*> tmpFuncVector;
+
+ // Browse for all possible OpCodes. This is not the fastest method, but
+ // otherwise the sub resources within the resource blocks and the
+ // resource blocks themselves would had to be ordered according to
+ // OpCodes, which is utopian...
+ ScFuncDescCore const * pDescsEnd = aDescs + SAL_N_ELEMENTS(aDescs);
+ for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
+ {
+ const ScFuncDescCore *pEntry = std::lower_bound(aDescs, pDescsEnd, i,
+ [](const ScFuncDescCore &rItem, sal_uInt16 key)
+ {
+ return rItem.nOpCode < key;
+ }
+ );
+
+ // Opcode Resource available?
+ if (pEntry != pDescsEnd && pEntry->nOpCode == i && pEntry->pResource)
+ {
+ pDesc = new ScFuncDesc;
+ bool bSuppressed = false;
+
+ ScFuncRes(*pEntry, pDesc, bSuppressed);
+ // Instead of dealing with this exceptional case at 1001 places
+ // we simply don't add an entirely suppressed function to the
+ // list and delete it.
+ if (bSuppressed)
+ delete pDesc;
+ else
+ {
+ pDesc->nFIndex = i;
+ tmpFuncVector.push_back(pDesc);
+
+ nStrLen = pDesc->mxFuncName->getLength();
+ if (nStrLen > nMaxFuncNameLen)
+ nMaxFuncNameLen = nStrLen;
+ }
+ }
+ }
+
+ // legacy binary AddIn functions
+
+ sal_uInt16 nNextId = SC_OPCODE_LAST_OPCODE_ID + 1; // FuncID for AddIn functions
+
+ // Interpretation of AddIn list
+ OUString aDefArgNameValue = "value";
+ OUString aDefArgNameString = "string";
+ OUString aDefArgNameValues = "values";
+ OUString aDefArgNameStrings = "strings";
+ OUString aDefArgNameCells = "cells";
+ OUString aDefArgNameNone = "none";
+ OUString aDefArgDescValue = "a value";
+ OUString aDefArgDescString = "a string";
+ OUString aDefArgDescValues = "array of values";
+ OUString aDefArgDescStrings = "array of strings";
+ OUString aDefArgDescCells = "range of cells";
+ OUString aDefArgDescNone = "none";
+
+ OUString aArgName, aArgDesc;
+ const LegacyFuncCollection& rLegacyFuncColl = *ScGlobal::GetLegacyFuncCollection();
+ for (auto const& legacyFunc : rLegacyFuncColl)
+ {
+ const LegacyFuncData *const pLegacyFuncData = legacyFunc.second.get();
+ pDesc = new ScFuncDesc;
+ sal_uInt16 nArgs = pLegacyFuncData->GetParamCount() - 1;
+ pLegacyFuncData->getParamDesc( aArgName, aArgDesc, 0 );
+ pDesc->nFIndex = nNextId++; // ??? OpCode vergeben
+ pDesc->nCategory = ID_FUNCTION_GRP_ADDINS;
+ pDesc->mxFuncName = pLegacyFuncData->GetInternalName().toAsciiUpperCase();
+ pDesc->mxFuncDesc = aArgDesc + "\n"
+ "( AddIn: " + pLegacyFuncData->GetModuleName() + " )";
+ pDesc->nArgCount = nArgs;
+ if (nArgs)
+ {
+ pDesc->maDefArgNames.clear();
+ pDesc->maDefArgNames.resize(nArgs);
+ pDesc->maDefArgDescs.clear();
+ pDesc->maDefArgDescs.resize(nArgs);
+ pDesc->pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgs];
+ for (sal_uInt16 j = 0; j < nArgs; ++j)
+ {
+ pDesc->pDefArgFlags[j].bOptional = false;
+ pLegacyFuncData->getParamDesc( aArgName, aArgDesc, j+1 );
+ if ( !aArgName.isEmpty() )
+ pDesc->maDefArgNames[j] = aArgName;
+ else
+ {
+ switch (pLegacyFuncData->GetParamType(j+1))
+ {
+ case ParamType::PTR_DOUBLE:
+ pDesc->maDefArgNames[j] = aDefArgNameValue;
+ break;
+ case ParamType::PTR_STRING:
+ pDesc->maDefArgNames[j] = aDefArgNameString;
+ break;
+ case ParamType::PTR_DOUBLE_ARR:
+ pDesc->maDefArgNames[j] = aDefArgNameValues;
+ break;
+ case ParamType::PTR_STRING_ARR:
+ pDesc->maDefArgNames[j] = aDefArgNameStrings;
+ break;
+ case ParamType::PTR_CELL_ARR:
+ pDesc->maDefArgNames[j] = aDefArgNameCells;
+ break;
+ default:
+ pDesc->maDefArgNames[j] = aDefArgNameNone;
+ break;
+ }
+ }
+ if ( !aArgDesc.isEmpty() )
+ pDesc->maDefArgDescs[j] = aArgDesc;
+ else
+ {
+ switch (pLegacyFuncData->GetParamType(j+1))
+ {
+ case ParamType::PTR_DOUBLE:
+ pDesc->maDefArgDescs[j] = aDefArgDescValue;
+ break;
+ case ParamType::PTR_STRING:
+ pDesc->maDefArgDescs[j] = aDefArgDescString;
+ break;
+ case ParamType::PTR_DOUBLE_ARR:
+ pDesc->maDefArgDescs[j] = aDefArgDescValues;
+ break;
+ case ParamType::PTR_STRING_ARR:
+ pDesc->maDefArgDescs[j] = aDefArgDescStrings;
+ break;
+ case ParamType::PTR_CELL_ARR:
+ pDesc->maDefArgDescs[j] = aDefArgDescCells;
+ break;
+ default:
+ pDesc->maDefArgDescs[j] = aDefArgDescNone;
+ break;
+ }
+ }
+ }
+ }
+
+ tmpFuncVector.push_back(pDesc);
+ nStrLen = pDesc->mxFuncName->getLength();
+ if ( nStrLen > nMaxFuncNameLen)
+ nMaxFuncNameLen = nStrLen;
+ }
+
+ // StarOne AddIns
+
+ ScUnoAddInCollection* pUnoAddIns = ScGlobal::GetAddInCollection();
+ tools::Long nUnoCount = pUnoAddIns->GetFuncCount();
+ for (tools::Long nFunc=0; nFunc<nUnoCount; nFunc++)
+ {
+ pDesc = new ScFuncDesc;
+ pDesc->nFIndex = nNextId++;
+
+ if ( pUnoAddIns->FillFunctionDesc( nFunc, *pDesc, mbEnglishFunctionNames ) )
+ {
+ tmpFuncVector.push_back(pDesc);
+ nStrLen = pDesc->mxFuncName->getLength();
+ if (nStrLen > nMaxFuncNameLen)
+ nMaxFuncNameLen = nStrLen;
+ }
+ else
+ delete pDesc;
+ }
+
+ aFunctionList.swap(tmpFuncVector);
+
+ //Initialize iterator
+ aFunctionListIter = aFunctionList.end();
+}
+
+ScFunctionList::~ScFunctionList()
+{
+ const ScFuncDesc* pDesc = First();
+ while (pDesc)
+ {
+ delete pDesc;
+ pDesc = Next();
+ }
+}
+
+const ScFuncDesc* ScFunctionList::First()
+{
+ const ScFuncDesc* pDesc = nullptr;
+ aFunctionListIter = aFunctionList.begin();
+ if(aFunctionListIter != aFunctionList.end())
+ pDesc = *aFunctionListIter;
+
+ return pDesc;
+}
+
+const ScFuncDesc* ScFunctionList::Next()
+{
+ const ScFuncDesc* pDesc = nullptr;
+ if(aFunctionListIter != aFunctionList.end())
+ {
+ if((++aFunctionListIter) != aFunctionList.end())
+ pDesc = *aFunctionListIter;
+ }
+ return pDesc;
+}
+
+const ScFuncDesc* ScFunctionList::GetFunction( sal_uInt32 nIndex ) const
+{
+ const ScFuncDesc* pDesc = nullptr;
+ if(nIndex < aFunctionList.size())
+ pDesc = aFunctionList.at(nIndex);
+
+ return pDesc;
+}
+
+
+sal_uInt32 ScFunctionCategory::getCount() const
+{
+ return m_rCategory.size();
+}
+
+OUString ScFunctionCategory::getName() const
+{
+ if ( m_sName.isEmpty() )
+ m_sName = ScFunctionMgr::GetCategoryName(m_nCategory);
+ return m_sName;
+}
+
+const formula::IFunctionDescription* ScFunctionCategory::getFunction(sal_uInt32 _nPos) const
+{
+ const ScFuncDesc* pDesc = nullptr;
+ if(_nPos < m_rCategory.size())
+ pDesc = m_rCategory.at(_nPos);
+ return pDesc;
+}
+
+sal_uInt32 ScFunctionCategory::getNumber() const
+{
+ return m_nCategory;
+}
+
+
+ScFunctionMgr::ScFunctionMgr()
+{
+ ScFunctionList* pFuncList /**< list of all calc functions */
+ = ScGlobal::GetStarCalcFunctionList();
+
+ OSL_ENSURE( pFuncList, "Functionlist not found." );
+ sal_uInt32 catCount[MAX_FUNCCAT] = {0};
+
+ aCatLists[0].reserve(pFuncList->GetCount());
+
+ // Retrieve all functions, store in cumulative ("All") category, and count
+ // number of functions in each category
+ for(const ScFuncDesc* pDesc = pFuncList->First(); pDesc; pDesc = pFuncList->Next())
+ {
+ OSL_ENSURE((pDesc->nCategory) < MAX_FUNCCAT, "Unknown category");
+ if ((pDesc->nCategory) < MAX_FUNCCAT)
+ ++catCount[pDesc->nCategory];
+ aCatLists[0].push_back(pDesc);
+ }
+
+ // Sort functions in cumulative category by name
+ ::std::sort(aCatLists[0].begin(), aCatLists[0].end(), ScFuncDesc::compareByName);
+
+ // Allocate correct amount of space for categories
+ for (sal_uInt16 i = 1; i < MAX_FUNCCAT; ++i)
+ {
+ aCatLists[i].reserve(catCount[i]);
+ }
+
+ // Fill categories with the corresponding functions (still sorted by name)
+ for (auto const& elemList : aCatLists[0])
+ {
+ if ((elemList->nCategory) < MAX_FUNCCAT)
+ aCatLists[elemList->nCategory].push_back(elemList);
+ }
+
+ // Initialize iterators
+ pCurCatListIter = aCatLists[0].end();
+ pCurCatListEnd = aCatLists[0].end();
+}
+
+ScFunctionMgr::~ScFunctionMgr()
+{
+}
+
+
+const ScFuncDesc* ScFunctionMgr::Get( sal_uInt16 nFIndex ) const
+{
+ const ScFuncDesc* pDesc;
+ for (pDesc = First(); pDesc; pDesc = Next())
+ if (pDesc->nFIndex == nFIndex)
+ break;
+ return pDesc;
+}
+
+const ScFuncDesc* ScFunctionMgr::First( sal_uInt16 nCategory ) const
+{
+ OSL_ENSURE( nCategory < MAX_FUNCCAT, "Unknown category" );
+ const ScFuncDesc* pDesc = nullptr;
+ if ( nCategory < MAX_FUNCCAT )
+ {
+ pCurCatListIter = aCatLists[nCategory].begin();
+ pCurCatListEnd = aCatLists[nCategory].end();
+ pDesc = *pCurCatListIter;
+ }
+ else
+ {
+ pCurCatListIter = aCatLists[0].end();
+ pCurCatListEnd = aCatLists[0].end();
+ }
+ return pDesc;
+}
+
+const ScFuncDesc* ScFunctionMgr::Next() const
+{
+ const ScFuncDesc* pDesc = nullptr;
+ if ( pCurCatListIter != pCurCatListEnd )
+ {
+ if ( (++pCurCatListIter) != pCurCatListEnd )
+ {
+ pDesc = *pCurCatListIter;
+ }
+ }
+ return pDesc;
+}
+
+sal_uInt32 ScFunctionMgr::getCount() const
+{
+ return MAX_FUNCCAT - 1;
+}
+
+const formula::IFunctionCategory* ScFunctionMgr::getCategory(sal_uInt32 nCategory) const
+{
+ if ( nCategory < (MAX_FUNCCAT-1) )
+ {
+ if (m_aCategories.find(nCategory) == m_aCategories.end())
+ m_aCategories[nCategory] = std::make_shared<ScFunctionCategory>(aCatLists[nCategory+1],nCategory); // aCatLists[0] is "all"
+ return m_aCategories[nCategory].get();
+ }
+ return nullptr;
+}
+
+void ScFunctionMgr::fillLastRecentlyUsedFunctions(::std::vector< const formula::IFunctionDescription*>& _rLastRUFunctions) const
+{
+ const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions();
+ sal_uInt16 nLRUFuncCount = std::min( rAppOpt.GetLRUFuncListCount(), sal_uInt16(LRU_MAX) );
+ sal_uInt16* pLRUListIds = rAppOpt.GetLRUFuncList();
+ _rLastRUFunctions.clear();
+
+ if ( pLRUListIds )
+ {
+ for (sal_uInt16 i = 0; i < nLRUFuncCount; ++i)
+ {
+ _rLastRUFunctions.push_back( Get( pLRUListIds[i] ) );
+ }
+ }
+}
+
+OUString ScFunctionMgr::GetCategoryName(sal_uInt32 _nCategoryNumber )
+{
+ if (_nCategoryNumber >= SC_FUNCGROUP_COUNT)
+ {
+ OSL_FAIL("Invalid category number!");
+ return OUString();
+ }
+
+ return ScResId(RID_FUNCTION_CATEGORIES[_nCategoryNumber]);
+}
+
+sal_Unicode ScFunctionMgr::getSingleToken(const formula::IFunctionManager::EToken _eToken) const
+{
+ switch(_eToken)
+ {
+ case eOk:
+ return ScCompiler::GetNativeSymbolChar(ocOpen);
+ case eClose:
+ return ScCompiler::GetNativeSymbolChar(ocClose);
+ case eSep:
+ return ScCompiler::GetNativeSymbolChar(ocSep);
+ case eArrayOpen:
+ return ScCompiler::GetNativeSymbolChar(ocArrayOpen);
+ case eArrayClose:
+ return ScCompiler::GetNativeSymbolChar(ocArrayClose);
+ }
+ return 0;
+}
+
+static void ScFuncRes(const ScFuncDescCore &rEntry, ScFuncDesc* pDesc, bool& rbSuppressed)
+{
+ const sal_uInt16 nOpCode = rEntry.nOpCode;
+ sal_uInt16 nFunctionFlags = rEntry.nFunctionFlags;
+ // Bit 1: entirely suppressed
+ // Bit 2: hidden unless used
+ rbSuppressed = ((nFunctionFlags & 1) != 0);
+ pDesc->mbHidden = ((nFunctionFlags & 2) != 0);
+ pDesc->nCategory = rEntry.nCategory;
+ pDesc->sHelpId = rEntry.pHelpId;
+ pDesc->nArgCount = rEntry.nArgs;
+ sal_uInt16 nArgs = pDesc->nArgCount;
+ sal_uInt16 nVarArgsSet = 0;
+ if (nArgs >= PAIRED_VAR_ARGS)
+ {
+ nVarArgsSet = 2;
+ nArgs -= PAIRED_VAR_ARGS - nVarArgsSet;
+ }
+ else if (nArgs >= VAR_ARGS)
+ {
+ nVarArgsSet = 1;
+ nArgs -= VAR_ARGS - nVarArgsSet;
+ }
+ assert(nArgs <= SAL_N_ELEMENTS(rEntry.aOptionalArgs));
+ if (nArgs)
+ {
+ pDesc->nVarArgsStart = nArgs - nVarArgsSet;
+ pDesc->nVarArgsLimit = rEntry.nVarArgsLimit;
+ pDesc->pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgs];
+ for (sal_uInt16 i = 0; i < nArgs; ++i)
+ {
+ pDesc->pDefArgFlags[i].bOptional = static_cast<bool>(rEntry.aOptionalArgs[i]);
+ }
+ }
+
+ pDesc->mxFuncName = ScCompiler::GetNativeSymbol(static_cast<OpCode>(nOpCode));
+ pDesc->mxFuncDesc = ScResId(rEntry.pResource[0]);
+
+ if (!nArgs)
+ return;
+
+ pDesc->maDefArgNames.clear();
+ pDesc->maDefArgNames.resize(nArgs);
+ pDesc->maDefArgDescs.clear();
+ pDesc->maDefArgDescs.resize(nArgs);
+ for (sal_uInt16 i = 0; i < nArgs; ++i)
+ {
+ size_t nIndex = (i * 2) + 1;
+ if (nIndex < rEntry.nResourceLen)
+ pDesc->maDefArgNames[i] = ScResId(rEntry.pResource[nIndex]);
+ if (nIndex + 1 < rEntry.nResourceLen)
+ pDesc->maDefArgDescs[i] = ScResId(rEntry.pResource[nIndex + 1]);
+ // If empty and variable number of arguments and last parameter and
+ // parameter is optional and the previous is not optional, repeat
+ // previous parameter name and description.
+ if ((pDesc->maDefArgNames[i].isEmpty() || pDesc->maDefArgDescs[i].isEmpty()) &&
+ nVarArgsSet > 0 && i > nVarArgsSet && (i == nArgs-1 || i == nArgs-2) &&
+ pDesc->pDefArgFlags[i].bOptional)
+ {
+ sal_uInt16 nPrev = i - nVarArgsSet;
+ if (!pDesc->pDefArgFlags[nPrev].bOptional)
+ {
+ if (pDesc->maDefArgNames[i].isEmpty())
+ pDesc->maDefArgNames[i] = pDesc->maDefArgNames[nPrev];
+ if (pDesc->maDefArgDescs[i].isEmpty())
+ pDesc->maDefArgDescs[i] = pDesc->maDefArgDescs[nPrev];
+ // This also means that variable arguments start one
+ // parameter set earlier.
+ pDesc->nVarArgsStart -= nVarArgsSet;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/global.cxx b/sc/source/core/data/global.cxx
new file mode 100644
index 0000000000..29a616c6b6
--- /dev/null
+++ b/sc/source/core/data/global.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 <scitems.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/editobj.hxx>
+#include <svl/itempool.hxx>
+#include <svl/srchitem.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/securityoptions.hxx>
+#include <osl/diagnose.h>
+
+#include <i18nlangtag/mslangid.hxx>
+#include <comphelper/doublecheckedinit.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/calendarwrapper.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/transliterationwrapper.hxx>
+
+#include <comphelper/lok.hxx>
+
+#include <global.hxx>
+#include <scresid.hxx>
+#include <autoform.hxx>
+#include <patattr.hxx>
+#include <addincol.hxx>
+#include <adiasync.hxx>
+#include <userlist.hxx>
+#include <interpre.hxx>
+#include <unitconv.hxx>
+#include <compiler.hxx>
+#include <parclass.hxx>
+#include <funcdesc.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <scmod.hxx>
+#include <editutil.hxx>
+#include <docsh.hxx>
+#include <sharedstringpoolpurge.hxx>
+#include <formulaopt.hxx>
+
+tools::SvRef<ScDocShell> ScGlobal::xDrawClipDocShellRef;
+std::unique_ptr<SvxSearchItem> ScGlobal::xSearchItem;
+std::unique_ptr<ScAutoFormat> ScGlobal::xAutoFormat;
+std::atomic<LegacyFuncCollection*> ScGlobal::pLegacyFuncCollection(nullptr);
+std::atomic<ScUnoAddInCollection*> ScGlobal::pAddInCollection(nullptr);
+std::unique_ptr<ScUserList> ScGlobal::xUserList;
+LanguageType ScGlobal::eLnge = LANGUAGE_SYSTEM;
+std::atomic<css::lang::Locale*> ScGlobal::pLocale(nullptr);
+std::optional<SvtSysLocale> ScGlobal::oSysLocale;
+std::optional<CalendarWrapper> ScGlobal::oCalendar;
+std::atomic<CollatorWrapper*> ScGlobal::pCollator(nullptr);
+std::atomic<CollatorWrapper*> ScGlobal::pCaseCollator(nullptr);
+std::atomic<::utl::TransliterationWrapper*> ScGlobal::pTransliteration(nullptr);
+std::atomic<::utl::TransliterationWrapper*> ScGlobal::pCaseTransliteration(nullptr);
+css::uno::Reference< css::i18n::XOrdinalSuffix> ScGlobal::xOrdinalSuffix;
+OUString ScGlobal::aStrClipDocName;
+
+std::unique_ptr<SvxBrushItem> ScGlobal::xEmptyBrushItem;
+std::unique_ptr<SvxBrushItem> ScGlobal::xButtonBrushItem;
+
+std::unique_ptr<ScFunctionList> ScGlobal::xStarCalcFunctionList;
+std::unique_ptr<ScFunctionMgr> ScGlobal::xStarCalcFunctionMgr;
+
+std::atomic<ScUnitConverter*> ScGlobal::pUnitConverter(nullptr);
+std::unique_ptr<SvNumberFormatter> ScGlobal::xEnglishFormatter;
+std::unique_ptr<ScFieldEditEngine> ScGlobal::xFieldEditEngine;
+std::atomic<sc::SharedStringPoolPurge*> ScGlobal::pSharedStringPoolPurge;
+
+double ScGlobal::nScreenPPTX = 96.0;
+double ScGlobal::nScreenPPTY = 96.0;
+
+sal_uInt16 ScGlobal::nDefFontHeight = 225;
+sal_uInt16 ScGlobal::nStdRowHeight = 256;
+
+tools::Long ScGlobal::nLastRowHeightExtra = 0;
+tools::Long ScGlobal::nLastColWidthExtra = STD_EXTRA_WIDTH;
+
+SfxViewShell* pScActiveViewShell = nullptr; //FIXME: Make this a member
+sal_uInt16 nScClickMouseModifier = 0; //FIXME: This too
+sal_uInt16 nScFillModeMouseModifier = 0; //FIXME: And this
+
+bool ScGlobal::bThreadedGroupCalcInProgress = false;
+
+InputHandlerFunctionNames ScGlobal::maInputHandlerFunctionNames;
+
+
+// Static functions
+
+bool ScGlobal::HasAttrChanged( const SfxItemSet& rNewAttrs,
+ const SfxItemSet& rOldAttrs,
+ const sal_uInt16 nWhich )
+{
+ bool bInvalidate = false;
+ const SfxPoolItem* pNewItem = nullptr;
+ const SfxItemState eNewState = rNewAttrs.GetItemState( nWhich, true, &pNewItem );
+ const SfxPoolItem* pOldItem = nullptr;
+ const SfxItemState eOldState = rOldAttrs.GetItemState( nWhich, true, &pOldItem );
+
+ if ( eNewState == eOldState )
+ {
+ // Both Items set
+ // PoolItems, meaning comparing pointers is valid
+ if ( SfxItemState::SET == eOldState )
+ bInvalidate = !SfxPoolItem::areSame(pNewItem, pOldItem);
+ }
+ else
+ {
+ // Contains a Default Item
+ // PoolItems, meaning Item comparison necessary
+ if (!pOldItem)
+ pOldItem = &rOldAttrs.GetPool()->GetDefaultItem( nWhich );
+
+ if (!pNewItem)
+ pNewItem = &rNewAttrs.GetPool()->GetDefaultItem( nWhich );
+
+ bInvalidate = (*pNewItem != *pOldItem);
+ }
+
+ return bInvalidate;
+}
+
+sal_uInt32 ScGlobal::GetStandardFormat( SvNumberFormatter& rFormatter,
+ sal_uInt32 nFormat, SvNumFormatType nType )
+{
+ const SvNumberformat* pFormat = rFormatter.GetEntry( nFormat );
+ if ( pFormat )
+ return rFormatter.GetStandardFormat( nFormat, nType, pFormat->GetLanguage() );
+ return rFormatter.GetStandardFormat( nType, eLnge );
+}
+
+sal_uInt16 ScGlobal::GetStandardRowHeight()
+{
+ return nStdRowHeight;
+}
+
+SvNumberFormatter* ScGlobal::GetEnglishFormatter()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if ( !xEnglishFormatter )
+ {
+ xEnglishFormatter.reset( new SvNumberFormatter(
+ ::comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) );
+ xEnglishFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT );
+ }
+ return xEnglishFormatter.get();
+}
+
+bool ScGlobal::CheckWidthInvalidate( bool& bNumFormatChanged,
+ const SfxItemSet& rNewAttrs,
+ const SfxItemSet& rOldAttrs )
+{
+ // Here ScPatternAttr::FastEqualPatternSets was used before. This implies that
+ // the two given SfxItemSet are internal ones from ScPatternAttr, but there is
+ // no guarantee here for that. Also that former method contained the comment
+ // "Actually test_tdf133629 from UITest_calc_tests9 somehow manages to have
+ // a different range (and I don't understand enough why), so better be safe and compare fully."
+ // which may be based on this usage. I check for that already in
+ // ScPatternAttr::operator==, seems not to be triggered there.
+ // All in all: Better use SfxItemSet::operator== here, and not one specialized
+ // on the SfxItemSets of ScPatternAttr
+ if (rNewAttrs == rOldAttrs)
+ {
+ bNumFormatChanged = false;
+ return false;
+ }
+ // Check whether attribute changes in rNewAttrs compared to rOldAttrs render
+ // the text width at a cell invalid
+ bNumFormatChanged =
+ HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_VALUE_FORMAT );
+ return ( bNumFormatChanged
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_LANGUAGE_FORMAT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_HEIGHT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_HEIGHT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_HEIGHT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_WEIGHT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_WEIGHT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_WEIGHT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_POSTURE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_POSTURE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_POSTURE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_UNDERLINE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_OVERLINE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_CROSSEDOUT )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_CONTOUR )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_SHADOWED )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_STACKED )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_ROTATE_VALUE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_ROTATE_MODE )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_LINEBREAK )
+ || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_MARGIN )
+ );
+}
+
+const SvxSearchItem& ScGlobal::GetSearchItem()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if (!xSearchItem)
+ {
+ xSearchItem.reset(new SvxSearchItem( SID_SEARCH_ITEM ));
+ xSearchItem->SetAppFlag( SvxSearchApp::CALC );
+ }
+ return *xSearchItem;
+}
+
+void ScGlobal::SetSearchItem( const SvxSearchItem& rNew )
+{
+ assert(!bThreadedGroupCalcInProgress);
+ // FIXME: An assignment operator would be nice here
+ xSearchItem.reset(rNew.Clone());
+
+ xSearchItem->SetWhich( SID_SEARCH_ITEM );
+ xSearchItem->SetAppFlag( SvxSearchApp::CALC );
+}
+
+void ScGlobal::ClearAutoFormat()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if (xAutoFormat)
+ {
+ // When modified via StarOne then only the SaveLater flag is set and no saving is done.
+ // If the flag is set then save now.
+ if (xAutoFormat->IsSaveLater())
+ xAutoFormat->Save();
+ xAutoFormat.reset();
+ }
+}
+
+ScAutoFormat* ScGlobal::GetAutoFormat()
+{
+ return xAutoFormat.get();
+}
+
+ScAutoFormat* ScGlobal::GetOrCreateAutoFormat()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if ( !xAutoFormat )
+ {
+ xAutoFormat.reset(new ScAutoFormat);
+ xAutoFormat->Load();
+ }
+
+ return xAutoFormat.get();
+}
+
+LegacyFuncCollection* ScGlobal::GetLegacyFuncCollection()
+{
+ return comphelper::doubleCheckedInit( pLegacyFuncCollection, []() { return new LegacyFuncCollection(); });
+}
+
+ScUnoAddInCollection* ScGlobal::GetAddInCollection()
+{
+ return comphelper::doubleCheckedInit( pAddInCollection, []() { return new ScUnoAddInCollection(); });
+}
+
+ScUserList* ScGlobal::GetUserList()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ // Hack: Load Cfg item at the App
+ global_InitAppOptions();
+
+ if (!xUserList)
+ xUserList.reset(new ScUserList());
+ return xUserList.get();
+}
+
+void ScGlobal::SetUserList( const ScUserList* pNewList )
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if ( pNewList )
+ {
+ if ( !xUserList )
+ xUserList.reset( new ScUserList( *pNewList ) );
+ else
+ *xUserList = *pNewList;
+ }
+ else
+ {
+ xUserList.reset();
+ }
+}
+
+OUString ScGlobal::GetErrorString(FormulaError nErr)
+{
+ TranslateId pErrNumber;
+ switch (nErr)
+ {
+ case FormulaError::NoRef:
+ pErrNumber = STR_NO_REF_TABLE;
+ break;
+ case FormulaError::NoAddin:
+ pErrNumber = STR_NO_ADDIN;
+ break;
+ case FormulaError::NoMacro:
+ pErrNumber = STR_NO_MACRO;
+ break;
+ case FormulaError::NotAvailable:
+ return ScCompiler::GetNativeSymbol(ocErrNA);
+ case FormulaError::NoName:
+ return ScCompiler::GetNativeSymbol(ocErrName);
+ case FormulaError::NoValue:
+ return ScCompiler::GetNativeSymbol(ocErrValue);
+ case FormulaError::NoCode:
+ return ScCompiler::GetNativeSymbol(ocErrNull);
+ case FormulaError::DivisionByZero:
+ return ScCompiler::GetNativeSymbol(ocErrDivZero);
+ case FormulaError::IllegalFPOperation:
+ return ScCompiler::GetNativeSymbol(ocErrNum);
+ default:
+ return ScResId(STR_ERROR_STR) + OUString::number( static_cast<int>(nErr) );
+ }
+ return ScResId(pErrNumber);
+}
+
+OUString ScGlobal::GetLongErrorString(FormulaError nErr)
+{
+ TranslateId pErrNumber;
+ switch (nErr)
+ {
+ case FormulaError::NONE:
+ return OUString();
+ case FormulaError::IllegalArgument:
+ pErrNumber = STR_LONG_ERR_ILL_ARG;
+ break;
+ case FormulaError::IllegalFPOperation:
+ pErrNumber = STR_LONG_ERR_ILL_FPO;
+ break;
+ case FormulaError::IllegalChar:
+ pErrNumber = STR_LONG_ERR_ILL_CHAR;
+ break;
+ case FormulaError::IllegalParameter:
+ pErrNumber = STR_LONG_ERR_ILL_PAR;
+ break;
+ case FormulaError::Pair:
+ case FormulaError::PairExpected:
+ pErrNumber = STR_LONG_ERR_PAIR;
+ break;
+ case FormulaError::OperatorExpected:
+ pErrNumber = STR_LONG_ERR_OP_EXP;
+ break;
+ case FormulaError::VariableExpected:
+ case FormulaError::ParameterExpected:
+ pErrNumber = STR_LONG_ERR_VAR_EXP;
+ break;
+ case FormulaError::CodeOverflow:
+ pErrNumber = STR_LONG_ERR_CODE_OVF;
+ break;
+ case FormulaError::StringOverflow:
+ pErrNumber = STR_LONG_ERR_STR_OVF;
+ break;
+ case FormulaError::StackOverflow:
+ pErrNumber = STR_LONG_ERR_STACK_OVF;
+ break;
+ case FormulaError::MatrixSize:
+ pErrNumber = STR_LONG_ERR_MATRIX_SIZE;
+ break;
+ case FormulaError::UnknownState:
+ case FormulaError::UnknownVariable:
+ case FormulaError::UnknownOpCode:
+ case FormulaError::UnknownStackVariable:
+ case FormulaError::UnknownToken:
+ pErrNumber = STR_LONG_ERR_SYNTAX;
+ break;
+ case FormulaError::NoCode:
+ pErrNumber = STR_LONG_ERR_NO_CODE;
+ break;
+ case FormulaError::CircularReference:
+ pErrNumber = STR_LONG_ERR_CIRC_REF;
+ break;
+ case FormulaError::NoConvergence:
+ pErrNumber = STR_LONG_ERR_NO_CONV;
+ break;
+ case FormulaError::NoRef:
+ pErrNumber = STR_LONG_ERR_NO_REF;
+ break;
+ case FormulaError::NoName:
+ pErrNumber = STR_LONG_ERR_NO_NAME;
+ break;
+ case FormulaError::NoAddin:
+ pErrNumber = STR_LONG_ERR_NO_ADDIN;
+ break;
+ case FormulaError::NoMacro:
+ pErrNumber = STR_LONG_ERR_NO_MACRO;
+ break;
+ case FormulaError::DivisionByZero:
+ pErrNumber = STR_LONG_ERR_DIV_ZERO;
+ break;
+ case FormulaError::NestedArray:
+ pErrNumber = STR_ERR_LONG_NESTED_ARRAY;
+ break;
+ case FormulaError::BadArrayContent:
+ pErrNumber = STR_ERR_LONG_BAD_ARRAY_CONTENT;
+ break;
+ case FormulaError::LinkFormulaNeedingCheck:
+ pErrNumber = STR_ERR_LONG_LINK_FORMULA_NEEDING_CHECK;
+ break;
+ case FormulaError::NoValue:
+ pErrNumber = STR_LONG_ERR_NO_VALUE;
+ break;
+ case FormulaError::NotAvailable:
+ pErrNumber = STR_LONG_ERR_NV;
+ break;
+ default:
+ return ScResId(STR_ERROR_STR) + OUString::number( static_cast<int>(nErr) );
+ }
+ return ScResId(pErrNumber);
+}
+
+SvxBrushItem* ScGlobal::GetButtonBrushItem()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ xButtonBrushItem->SetColor( Application::GetSettings().GetStyleSettings().GetFaceColor() );
+ return xButtonBrushItem.get();
+}
+
+void ScGlobal::Init()
+{
+ // The default language for number formats (ScGlobal::eLnge) must
+ // always be LANGUAGE_SYSTEM
+ // FIXME: So remove this variable?
+ eLnge = LANGUAGE_SYSTEM;
+
+ oSysLocale.emplace();
+
+ xEmptyBrushItem = std::make_unique<SvxBrushItem>( COL_TRANSPARENT, ATTR_BACKGROUND );
+ xButtonBrushItem = std::make_unique<SvxBrushItem>( Color(), ATTR_BACKGROUND );
+
+ InitPPT();
+ //ScCompiler::InitSymbolsNative();
+ // ScParameterClassification _after_ Compiler, needs function resources if
+ // arguments are to be merged in, which in turn need strings of function
+ // names from the compiler.
+ ScParameterClassification::Init();
+
+ InitAddIns();
+
+ aStrClipDocName = ScResId( SCSTR_NONAME ) + "1";
+
+ // ScDocumentPool::InitVersionMaps() has been called earlier already
+}
+
+void ScGlobal::InitPPT()
+{
+ OutputDevice* pDev = Application::GetDefaultDevice();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // LOK: the below limited precision is not enough for RowColumnHeader.
+ nScreenPPTX = o3tl::convert<double>(pDev->GetDPIX(), o3tl::Length::twip, o3tl::Length::in);
+ nScreenPPTY = o3tl::convert<double>(pDev->GetDPIY(), o3tl::Length::twip, o3tl::Length::in);
+ }
+ else
+ {
+ // Avoid cumulative placement errors by intentionally limiting
+ // precision.
+ Point aPix1000 = pDev->LogicToPixel(Point(1000, 1000), MapMode(MapUnit::MapTwip));
+ nScreenPPTX = aPix1000.X() / 1000.0;
+ nScreenPPTY = aPix1000.Y() / 1000.0;
+ }
+}
+
+const OUString& ScGlobal::GetClipDocName()
+{
+ return aStrClipDocName;
+}
+
+void ScGlobal::SetClipDocName( const OUString& rNew )
+{
+ assert(!bThreadedGroupCalcInProgress);
+ aStrClipDocName = rNew;
+}
+
+void ScGlobal::InitTextHeight(const SfxItemPool* pPool)
+{
+ if (!pPool)
+ {
+ OSL_FAIL("ScGlobal::InitTextHeight: No Pool");
+ return;
+ }
+
+ const ScPatternAttr& rPattern = pPool->GetDefaultItem(ATTR_PATTERN);
+
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ ScopedVclPtrInstance< VirtualDevice > pVirtWindow( *pDefaultDev );
+ pVirtWindow->SetMapMode(MapMode(MapUnit::MapPixel));
+ vcl::Font aDefFont;
+ rPattern.fillFontOnly(aDefFont, pVirtWindow); // Font color doesn't matter here
+ pVirtWindow->SetFont(aDefFont);
+ sal_uInt16 nTest = static_cast<sal_uInt16>(
+ pVirtWindow->PixelToLogic(Size(0, pVirtWindow->GetTextHeight()), MapMode(MapUnit::MapTwip)).Height());
+
+ if (nTest > nDefFontHeight)
+ nDefFontHeight = nTest;
+
+ const SvxMarginItem& rMargin = rPattern.GetItem(ATTR_MARGIN);
+
+ nTest = static_cast<sal_uInt16>(nDefFontHeight + rMargin.GetTopMargin()
+ + rMargin.GetBottomMargin() - STD_ROWHEIGHT_DIFF);
+
+ if (nTest > nStdRowHeight)
+ nStdRowHeight = nTest;
+}
+
+void ScGlobal::Clear()
+{
+ // Destroy asyncs _before_ ExitExternalFunc!
+ theAddInAsyncTbl.clear();
+ ExitExternalFunc();
+ ClearAutoFormat();
+ xSearchItem.reset();
+ delete pLegacyFuncCollection.exchange(nullptr);
+ delete pAddInCollection.exchange(nullptr);
+ xUserList.reset();
+ xStarCalcFunctionList.reset(); // Destroy before ResMgr!
+ xStarCalcFunctionMgr.reset();
+ ScParameterClassification::Exit();
+ ScCompiler::DeInit();
+ ScInterpreter::GlobalExit(); // Delete static Stack
+
+ xEmptyBrushItem.reset();
+ xButtonBrushItem.reset();
+ xEnglishFormatter.reset();
+ delete pCaseTransliteration.exchange(nullptr);
+ delete pTransliteration.exchange(nullptr);
+ delete pCaseCollator.exchange(nullptr);
+ delete pCollator.exchange(nullptr);
+ oCalendar.reset();
+ oSysLocale.reset();
+ delete pLocale.exchange(nullptr);
+
+ delete pUnitConverter.exchange(nullptr);
+ xFieldEditEngine.reset();
+ delete pSharedStringPoolPurge.exchange(nullptr);
+
+ xDrawClipDocShellRef.clear();
+}
+
+rtl_TextEncoding ScGlobal::GetCharsetValue( std::u16string_view rCharSet )
+{
+ // new TextEncoding values
+ if ( CharClass::isAsciiNumeric( rCharSet ) )
+ {
+ sal_Int32 nVal = o3tl::toInt32(rCharSet);
+ if ( nVal == RTL_TEXTENCODING_DONTKNOW )
+ return osl_getThreadTextEncoding();
+ return static_cast<rtl_TextEncoding>(nVal);
+ }
+ // old CharSet values for compatibility
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"ANSI") ) return RTL_TEXTENCODING_MS_1252;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"MAC") ) return RTL_TEXTENCODING_APPLE_ROMAN;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC") ) return RTL_TEXTENCODING_IBM_850;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC_437")) return RTL_TEXTENCODING_IBM_437;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC_850")) return RTL_TEXTENCODING_IBM_850;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC_860")) return RTL_TEXTENCODING_IBM_860;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC_861")) return RTL_TEXTENCODING_IBM_861;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC_863")) return RTL_TEXTENCODING_IBM_863;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"IBMPC_865")) return RTL_TEXTENCODING_IBM_865;
+ // Some wrong "help" on the net mentions UTF8 and even unoconv uses it,
+ // which worked accidentally if the system encoding is UTF-8 anyway, so
+ // support it ;) but only when reading.
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"UTF8")) return RTL_TEXTENCODING_UTF8;
+ else if (o3tl::equalsIgnoreAsciiCase(rCharSet, u"UTF-8")) return RTL_TEXTENCODING_UTF8;
+ else return osl_getThreadTextEncoding();
+}
+
+OUString ScGlobal::GetCharsetString( rtl_TextEncoding eVal )
+{
+ const char* pChar;
+ switch ( eVal )
+ {
+ // old CharSet strings for compatibility
+ case RTL_TEXTENCODING_MS_1252: pChar = "ANSI"; break;
+ case RTL_TEXTENCODING_APPLE_ROMAN: pChar = "MAC"; break;
+ // IBMPC == IBMPC_850
+ case RTL_TEXTENCODING_IBM_437: pChar = "IBMPC_437"; break;
+ case RTL_TEXTENCODING_IBM_850: pChar = "IBMPC_850"; break;
+ case RTL_TEXTENCODING_IBM_860: pChar = "IBMPC_860"; break;
+ case RTL_TEXTENCODING_IBM_861: pChar = "IBMPC_861"; break;
+ case RTL_TEXTENCODING_IBM_863: pChar = "IBMPC_863"; break;
+ case RTL_TEXTENCODING_IBM_865: pChar = "IBMPC_865"; break;
+ case RTL_TEXTENCODING_DONTKNOW: pChar = "SYSTEM"; break;
+ // new string of TextEncoding value
+ default:
+ return OUString::number( eVal );
+ }
+ return OUString::createFromAscii(pChar);
+}
+
+bool ScGlobal::HasStarCalcFunctionList()
+{
+ return bool(xStarCalcFunctionList);
+}
+
+ScFunctionList* ScGlobal::GetStarCalcFunctionList()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if ( !xStarCalcFunctionList )
+ xStarCalcFunctionList.reset( new ScFunctionList( SC_MOD()->GetFormulaOptions().GetUseEnglishFuncName()));
+
+ return xStarCalcFunctionList.get();
+}
+
+ScFunctionMgr* ScGlobal::GetStarCalcFunctionMgr()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if ( !xStarCalcFunctionMgr )
+ xStarCalcFunctionMgr.reset(new ScFunctionMgr);
+
+ return xStarCalcFunctionMgr.get();
+}
+
+void ScGlobal::ResetFunctionList()
+{
+ // FunctionMgr has pointers into FunctionList, must also be updated
+ xStarCalcFunctionMgr.reset();
+ xStarCalcFunctionList.reset();
+ // Building new names also needs InputHandler data to be refreshed.
+ maInputHandlerFunctionNames = InputHandlerFunctionNames();
+}
+
+const InputHandlerFunctionNames& ScGlobal::GetInputHandlerFunctionNames()
+{
+ if (maInputHandlerFunctionNames.maFunctionData.empty())
+ {
+ const OUString aParenthesesReplacement( cParenthesesReplacement);
+ const ScFunctionList* pFuncList = GetStarCalcFunctionList();
+ const sal_uInt32 nListCount = pFuncList->GetCount();
+ const CharClass* pCharClass = (pFuncList->IsEnglishFunctionNames()
+ ? ScCompiler::GetCharClassEnglish()
+ : ScCompiler::GetCharClassLocalized());
+ for (sal_uInt32 i=0; i < nListCount; ++i)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction( i );
+ if ( pDesc->mxFuncName )
+ {
+ OUString aFuncName(pCharClass->uppercase(*(pDesc->mxFuncName)));
+ // fdo#75264 fill maFormulaChar with all characters used in formula names
+ for (sal_Int32 j = 0; j < aFuncName.getLength(); j++)
+ maInputHandlerFunctionNames.maFunctionChar.insert(aFuncName[j]);
+ maInputHandlerFunctionNames.maFunctionData.insert(
+ ScTypedStrData(*(pDesc->mxFuncName) + aParenthesesReplacement, 0.0, 0.0,
+ ScTypedStrData::Standard));
+ pDesc->initArgumentInfo();
+ OUString aEntry = pDesc->getSignature();
+ maInputHandlerFunctionNames.maFunctionDataPara.insert(
+ ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::Standard));
+ }
+ }
+ }
+ return maInputHandlerFunctionNames;
+}
+
+ScUnitConverter* ScGlobal::GetUnitConverter()
+{
+ return comphelper::doubleCheckedInit( pUnitConverter,
+ []() { return new ScUnitConverter; });
+}
+
+const sal_Unicode* ScGlobal::UnicodeStrChr( const sal_Unicode* pStr,
+ sal_Unicode c )
+{
+ if ( !pStr )
+ return nullptr;
+ while ( *pStr )
+ {
+ if ( *pStr == c )
+ return pStr;
+ pStr++;
+ }
+ return nullptr;
+}
+
+OUString ScGlobal::addToken(std::u16string_view rTokenList, std::u16string_view rToken,
+ sal_Unicode cSep, sal_Int32 nSepCount, bool bForceSep)
+{
+ OUStringBuffer aBuf(rTokenList);
+ if( bForceSep || (!rToken.empty() && !rTokenList.empty()) )
+ comphelper::string::padToLength(aBuf, aBuf.getLength() + nSepCount, cSep);
+ aBuf.append(rToken);
+ return aBuf.makeStringAndClear();
+}
+
+bool ScGlobal::IsQuoted( std::u16string_view rString, sal_Unicode cQuote )
+{
+ return (rString.size() >= 2) && (rString[0] == cQuote) && (rString[ rString.size() - 1 ] == cQuote);
+}
+
+void ScGlobal::AddQuotes( OUString& rString, sal_Unicode cQuote, bool bEscapeEmbedded )
+{
+ if (bEscapeEmbedded)
+ {
+ sal_Unicode pQ[3];
+ pQ[0] = pQ[1] = cQuote;
+ pQ[2] = 0;
+ OUString aQuotes( pQ );
+ rString = rString.replaceAll( OUStringChar(cQuote), aQuotes);
+ }
+ rString = OUStringChar( cQuote ) + rString + OUStringChar( cQuote );
+}
+
+void ScGlobal::EraseQuotes( OUString& rString, sal_Unicode cQuote, bool bUnescapeEmbedded )
+{
+ if ( IsQuoted( rString, cQuote ) )
+ {
+ rString = rString.copy( 1, rString.getLength() - 2 );
+ if (bUnescapeEmbedded)
+ {
+ sal_Unicode pQ[3];
+ pQ[0] = pQ[1] = cQuote;
+ pQ[2] = 0;
+ OUString aQuotes( pQ );
+ rString = rString.replaceAll( aQuotes, OUStringChar(cQuote));
+ }
+ }
+}
+
+sal_Int32 ScGlobal::FindUnquoted( const OUString& rString, sal_Unicode cChar, sal_Int32 nStart )
+{
+ assert(nStart >= 0);
+ const sal_Unicode cQuote = '\'';
+ const sal_Unicode* const pStart = rString.getStr();
+ const sal_Unicode* const pStop = pStart + rString.getLength();
+ const sal_Unicode* p = pStart + nStart;
+ bool bQuoted = false;
+ while (p < pStop)
+ {
+ if (*p == cChar && !bQuoted)
+ return sal::static_int_cast< sal_Int32 >( p - pStart );
+ else if (*p == cQuote)
+ {
+ if (!bQuoted)
+ bQuoted = true;
+ else if (p < pStop-1 && *(p+1) == cQuote)
+ ++p;
+ else
+ bQuoted = false;
+ }
+ ++p;
+ }
+ return -1;
+}
+
+const sal_Unicode* ScGlobal::FindUnquoted( const sal_Unicode* pString, sal_Unicode cChar )
+{
+ sal_Unicode cQuote = '\'';
+ const sal_Unicode* p = pString;
+ bool bQuoted = false;
+ while (*p)
+ {
+ if (*p == cChar && !bQuoted)
+ return p;
+ else if (*p == cQuote)
+ {
+ if (!bQuoted)
+ bQuoted = true;
+ else if (*(p+1) == cQuote)
+ ++p;
+ else
+ bQuoted = false;
+ }
+ ++p;
+ }
+ return nullptr;
+}
+
+bool ScGlobal::EETextObjEqual( const EditTextObject* pObj1,
+ const EditTextObject* pObj2 )
+{
+ if ( pObj1 == pObj2 ) // Both empty or the same object
+ return true;
+
+ if ( pObj1 && pObj2 )
+ return pObj1->Equals( *pObj2);
+
+ return false;
+}
+
+void ScGlobal::OpenURL(const OUString& rURL, const OUString& rTarget, bool bIgnoreSettings)
+{
+ // OpenURL is always called in the GridWindow by mouse clicks in some way or another.
+ // That's why pScActiveViewShell and nScClickMouseModifier are correct.
+
+ // Fragments pointing into the current document should be always opened.
+ if (!bIgnoreSettings && !(ShouldOpenURL() || rURL.startsWith("#")))
+ return;
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (!pViewFrm)
+ return;
+
+ OUString aUrlName( rURL );
+ SfxViewFrame* pFrame = nullptr;
+ SfxObjectShell* pObjShell = nullptr;
+ OUString aReferName;
+ if ( pScActiveViewShell )
+ {
+ pFrame = &pScActiveViewShell->GetViewFrame();
+ pObjShell = pFrame->GetObjectShell();
+ const SfxMedium* pMed = pObjShell->GetMedium();
+ if (pMed)
+ aReferName = pMed->GetName();
+ }
+
+ // Don't fiddle with fragments pointing into current document.
+ // Also don't mess around with a vnd.sun.star.script or service or other
+ // internal "URI".
+ if (!aUrlName.startsWith("#")
+ && !aUrlName.startsWithIgnoreAsciiCase("vnd.sun.star.script:")
+ && !aUrlName.startsWithIgnoreAsciiCase("macro:")
+ && !aUrlName.startsWithIgnoreAsciiCase("slot:")
+ && !aUrlName.startsWithIgnoreAsciiCase("service:")
+ && !aUrlName.startsWithIgnoreAsciiCase(".uno:"))
+ {
+ // Any relative reference would fail with "not an absolute URL"
+ // error, try to construct an absolute URI with the path relative
+ // to the current document's path or work path, as usual for all
+ // external references.
+ // This then also, as ScGlobal::GetAbsDocName() uses
+ // INetURLObject::smartRel2Abs(), supports "\\" UNC path names as
+ // smb:// Samba shares and DOS path separators converted to proper
+ // file:// URI.
+ const OUString aNewUrlName( ScGlobal::GetAbsDocName( aUrlName, pObjShell));
+ if (!aNewUrlName.isEmpty())
+ aUrlName = aNewUrlName;
+ }
+
+ if (!SfxObjectShell::AllowedLinkProtocolFromDocument(aUrlName, pObjShell, pFrame ? pFrame->GetFrameWeld() : nullptr))
+ return;
+
+ SfxStringItem aUrl( SID_FILE_NAME, aUrlName );
+ SfxStringItem aTarget( SID_TARGETNAME, rTarget );
+ if ( nScClickMouseModifier & KEY_SHIFT ) // control-click -> into new window
+ aTarget.SetValue("_blank");
+
+ SfxFrameItem aFrm( SID_DOCFRAME, pFrame );
+ SfxStringItem aReferer( SID_REFERER, aReferName );
+
+ SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false );
+ SfxBoolItem aBrowsing( SID_BROWSE, true );
+
+ // No SID_SILENT anymore
+ pViewFrm->GetDispatcher()->ExecuteList(SID_OPENDOC,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
+ { &aUrl, &aTarget, &aFrm, &aReferer, &aNewView, &aBrowsing });
+}
+
+bool ScGlobal::ShouldOpenURL()
+{
+ bool bCtrlClickHappened = (nScClickMouseModifier & KEY_MOD1);
+ bool bCtrlClickSecOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink );
+ if( bCtrlClickHappened && ! bCtrlClickSecOption )
+ {
+ // return since ctrl+click happened when the
+ // ctrl+click security option was disabled, link should not open
+ return false;
+ }
+ else if( ! bCtrlClickHappened && bCtrlClickSecOption )
+ {
+ // ctrl+click did not happen; only click happened maybe with some
+ // other key combo. and security option is set, so return
+ return false;
+ }
+ return true;
+}
+
+bool ScGlobal::IsSystemRTL()
+{
+ return MsLangId::isRightToLeft( Application::GetSettings().GetLanguageTag().getLanguageType() );
+}
+
+SvtScriptType ScGlobal::GetDefaultScriptType()
+{
+ // Used when text contains only WEAK characters.
+ // Script type of office language is used then (same as GetEditDefaultLanguage,
+ // to get consistent behavior of text in simple cells and EditEngine,
+ // also same as GetAppLanguage() in Writer)
+ return SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
+}
+
+LanguageType ScGlobal::GetEditDefaultLanguage()
+{
+ // Used for EditEngine::SetDefaultLanguage
+ return Application::GetSettings().GetLanguageTag().getLanguageType();
+}
+
+sal_uInt16 ScGlobal::GetScriptedWhichID( SvtScriptType nScriptType, sal_uInt16 nWhich )
+{
+ switch ( nScriptType )
+ {
+ case SvtScriptType::LATIN:
+ case SvtScriptType::ASIAN:
+ case SvtScriptType::COMPLEX:
+ break; // take exact matches
+ default: // prefer one, first COMPLEX, then ASIAN
+ if ( nScriptType & SvtScriptType::COMPLEX )
+ nScriptType = SvtScriptType::COMPLEX;
+ else if ( nScriptType & SvtScriptType::ASIAN )
+ nScriptType = SvtScriptType::ASIAN;
+ }
+ switch ( nScriptType )
+ {
+ case SvtScriptType::COMPLEX:
+ {
+ switch ( nWhich )
+ {
+ case ATTR_FONT:
+ case ATTR_CJK_FONT:
+ nWhich = ATTR_CTL_FONT;
+ break;
+ case ATTR_FONT_HEIGHT:
+ case ATTR_CJK_FONT_HEIGHT:
+ nWhich = ATTR_CTL_FONT_HEIGHT;
+ break;
+ case ATTR_FONT_WEIGHT:
+ case ATTR_CJK_FONT_WEIGHT:
+ nWhich = ATTR_CTL_FONT_WEIGHT;
+ break;
+ case ATTR_FONT_POSTURE:
+ case ATTR_CJK_FONT_POSTURE:
+ nWhich = ATTR_CTL_FONT_POSTURE;
+ break;
+ }
+ }
+ break;
+ case SvtScriptType::ASIAN:
+ {
+ switch ( nWhich )
+ {
+ case ATTR_FONT:
+ case ATTR_CTL_FONT:
+ nWhich = ATTR_CJK_FONT;
+ break;
+ case ATTR_FONT_HEIGHT:
+ case ATTR_CTL_FONT_HEIGHT:
+ nWhich = ATTR_CJK_FONT_HEIGHT;
+ break;
+ case ATTR_FONT_WEIGHT:
+ case ATTR_CTL_FONT_WEIGHT:
+ nWhich = ATTR_CJK_FONT_WEIGHT;
+ break;
+ case ATTR_FONT_POSTURE:
+ case ATTR_CTL_FONT_POSTURE:
+ nWhich = ATTR_CJK_FONT_POSTURE;
+ break;
+ }
+ }
+ break;
+ default:
+ {
+ switch ( nWhich )
+ {
+ case ATTR_CTL_FONT:
+ case ATTR_CJK_FONT:
+ nWhich = ATTR_FONT;
+ break;
+ case ATTR_CTL_FONT_HEIGHT:
+ case ATTR_CJK_FONT_HEIGHT:
+ nWhich = ATTR_FONT_HEIGHT;
+ break;
+ case ATTR_CTL_FONT_WEIGHT:
+ case ATTR_CJK_FONT_WEIGHT:
+ nWhich = ATTR_FONT_WEIGHT;
+ break;
+ case ATTR_CTL_FONT_POSTURE:
+ case ATTR_CJK_FONT_POSTURE:
+ nWhich = ATTR_FONT_POSTURE;
+ break;
+ }
+ }
+ }
+ return nWhich;
+}
+
+void ScGlobal::AddLanguage( SfxItemSet& rSet, const SvNumberFormatter& rFormatter )
+{
+ OSL_ENSURE( rSet.GetItemState( ATTR_LANGUAGE_FORMAT, false ) == SfxItemState::DEFAULT,
+ "ScGlobal::AddLanguage - language already added");
+
+ const SfxUInt32Item* pHardItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT, false );
+ if ( !pHardItem )
+ return;
+
+ const SvNumberformat* pHardFormat = rFormatter.GetEntry(
+ pHardItem->GetValue() );
+
+ sal_uInt32 nParentFmt = 0; // Pool default
+ const SfxItemSet* pParent = rSet.GetParent();
+ if ( pParent )
+ nParentFmt = pParent->Get( ATTR_VALUE_FORMAT ).GetValue();
+ const SvNumberformat* pParFormat = rFormatter.GetEntry( nParentFmt );
+
+ if ( pHardFormat && pParFormat &&
+ (pHardFormat->GetLanguage() != pParFormat->GetLanguage()) )
+ rSet.Put( SvxLanguageItem( pHardFormat->GetLanguage(), ATTR_LANGUAGE_FORMAT ) );
+}
+
+utl::TransliterationWrapper& ScGlobal::GetTransliteration()
+{
+ return *comphelper::doubleCheckedInit( pTransliteration,
+ []()
+ {
+ const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+ ::utl::TransliterationWrapper* p = new ::utl::TransliterationWrapper(
+ ::comphelper::getProcessComponentContext(), TransliterationFlags::IGNORE_CASE );
+ p->loadModuleIfNeeded( eOfficeLanguage );
+ return p;
+ });
+}
+::utl::TransliterationWrapper& ScGlobal::GetCaseTransliteration()
+{
+ return *comphelper::doubleCheckedInit( pCaseTransliteration,
+ []()
+ {
+ const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+ ::utl::TransliterationWrapper* p = new ::utl::TransliterationWrapper(
+ ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
+ p->loadModuleIfNeeded( eOfficeLanguage );
+ return p;
+ });
+}
+utl::TransliterationWrapper& ScGlobal::GetTransliteration(bool bCaseSensitive)
+{
+ return bCaseSensitive ? GetCaseTransliteration() : GetTransliteration();
+}
+
+const LocaleDataWrapper& ScGlobal::getLocaleData()
+{
+ OSL_ENSURE(
+ oSysLocale,
+ "ScGlobal::getLocaleDataPtr() called before ScGlobal::Init()");
+
+ return oSysLocale->GetLocaleData();
+}
+
+const CharClass& ScGlobal::getCharClass()
+{
+ OSL_ENSURE(
+ oSysLocale,
+ "ScGlobal::getCharClassPtr() called before ScGlobal::Init()");
+
+ return oSysLocale->GetCharClass();
+}
+
+CalendarWrapper& ScGlobal::GetCalendar()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if ( !oCalendar )
+ {
+ oCalendar.emplace( ::comphelper::getProcessComponentContext() );
+ oCalendar->loadDefaultCalendar( GetLocale() );
+ }
+ return *oCalendar;
+}
+
+namespace {
+
+struct GetMutex {
+ osl::Mutex * operator ()() {
+ static osl::Mutex m;
+ return &m;
+ }
+};
+
+}
+
+CollatorWrapper& ScGlobal::GetCollator()
+{
+ return *comphelper::doubleCheckedInit( pCollator,
+ []()
+ {
+ CollatorWrapper* p = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
+ p->loadDefaultCollator( GetLocale(), SC_COLLATOR_IGNORES );
+ return p;
+ },
+ GetMutex());
+}
+CollatorWrapper& ScGlobal::GetCaseCollator()
+{
+ return *comphelper::doubleCheckedInit( pCaseCollator,
+ []()
+ {
+ CollatorWrapper* p = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
+ p->loadDefaultCollator( GetLocale(), 0 );
+ return p;
+ },
+ GetMutex());
+}
+CollatorWrapper& ScGlobal::GetCollator(bool bCaseSensitive)
+{
+ return bCaseSensitive ? GetCaseCollator() : GetCollator();
+}
+css::lang::Locale& ScGlobal::GetLocale()
+{
+ return *comphelper::doubleCheckedInit( pLocale,
+ []() { return new css::lang::Locale( Application::GetSettings().GetLanguageTag().getLocale()); });
+}
+
+ScFieldEditEngine& ScGlobal::GetStaticFieldEditEngine()
+{
+ assert(!bThreadedGroupCalcInProgress);
+ if (!xFieldEditEngine)
+ {
+ // Creating a ScFieldEditEngine with pDocument=NULL leads to document
+ // specific fields not being resolvable! See
+ // ScFieldEditEngine::CalcFieldValue(). pEnginePool=NULL lets
+ // EditEngine internally create and delete a default pool.
+ xFieldEditEngine.reset(new ScFieldEditEngine( nullptr, nullptr));
+ }
+ return *xFieldEditEngine;
+}
+
+sc::SharedStringPoolPurge& ScGlobal::GetSharedStringPoolPurge()
+{
+ return *comphelper::doubleCheckedInit( pSharedStringPoolPurge,
+ []() { return new sc::SharedStringPoolPurge; });
+}
+
+OUString ScGlobal::ReplaceOrAppend( const OUString& rString,
+ std::u16string_view rPlaceholder, const OUString& rReplacement )
+{
+ if (rString.isEmpty())
+ return rReplacement;
+ sal_Int32 nFound = rString.indexOf( rPlaceholder);
+ if (nFound < 0)
+ {
+ if (rString[rString.getLength()-1] == ' ')
+ return rString + rReplacement;
+ return rString + " " + rReplacement;
+ }
+ return rString.replaceFirst( rPlaceholder, rReplacement, &nFound);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/global2.cxx b/sc/source/core/data/global2.cxx
new file mode 100644
index 0000000000..6f8352fd18
--- /dev/null
+++ b/sc/source/core/data/global2.cxx
@@ -0,0 +1,592 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <formula/errorcodes.hxx>
+#include <sal/log.hxx>
+#include <rtl/character.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <global.hxx>
+#include <rangeutl.hxx>
+#include <compiler.hxx>
+#include <paramisc.hxx>
+#include <calcconfig.hxx>
+
+// struct ScImportParam:
+
+ScImportParam::ScImportParam() :
+ nCol1(0),
+ nRow1(0),
+ nCol2(0),
+ nRow2(0),
+ bImport(false),
+ bNative(false),
+ bSql(true),
+ nType(ScDbTable)
+{
+}
+
+ScImportParam::ScImportParam( const ScImportParam& r ) :
+ nCol1 (r.nCol1),
+ nRow1 (r.nRow1),
+ nCol2 (r.nCol2),
+ nRow2 (r.nRow2),
+ bImport (r.bImport),
+ aDBName (r.aDBName),
+ aStatement (r.aStatement),
+ bNative (r.bNative),
+ bSql (r.bSql),
+ nType (r.nType)
+{
+}
+
+ScImportParam::~ScImportParam()
+{
+}
+
+ScImportParam& ScImportParam::operator=( const ScImportParam& r )
+{
+ nCol1 = r.nCol1;
+ nRow1 = r.nRow1;
+ nCol2 = r.nCol2;
+ nRow2 = r.nRow2;
+ bImport = r.bImport;
+ aDBName = r.aDBName;
+ aStatement = r.aStatement;
+ bNative = r.bNative;
+ bSql = r.bSql;
+ nType = r.nType;
+
+ return *this;
+}
+
+bool ScImportParam::operator==( const ScImportParam& rOther ) const
+{
+ return( nCol1 == rOther.nCol1 &&
+ nRow1 == rOther.nRow1 &&
+ nCol2 == rOther.nCol2 &&
+ nRow2 == rOther.nRow2 &&
+ bImport == rOther.bImport &&
+ aDBName == rOther.aDBName &&
+ aStatement == rOther.aStatement &&
+ bNative == rOther.bNative &&
+ bSql == rOther.bSql &&
+ nType == rOther.nType );
+
+ //TODO: are nQuerySh and pConnection equal ?
+}
+
+// struct ScConsolidateParam:
+
+ScConsolidateParam::ScConsolidateParam()
+{
+ Clear();
+}
+
+ScConsolidateParam::ScConsolidateParam( const ScConsolidateParam& r )
+{
+ operator=(r);
+}
+
+ScConsolidateParam::~ScConsolidateParam()
+{
+}
+
+void ScConsolidateParam::ClearDataAreas()
+{
+ pDataAreas.reset();
+ nDataAreaCount = 0;
+}
+
+void ScConsolidateParam::Clear()
+{
+ ClearDataAreas();
+
+ nCol = 0;
+ nRow = 0;
+ nTab = 0;
+ bByCol = bByRow = bReferenceData = false;
+ eFunction = SUBTOTAL_FUNC_SUM;
+}
+
+ScConsolidateParam& ScConsolidateParam::operator=( const ScConsolidateParam& r )
+{
+ if (this != &r)
+ {
+ nCol = r.nCol;
+ nRow = r.nRow;
+ nTab = r.nTab;
+ bByCol = r.bByCol;
+ bByRow = r.bByRow;
+ bReferenceData = r.bReferenceData;
+ eFunction = r.eFunction;
+ nDataAreaCount = r.nDataAreaCount;
+ if ( r.nDataAreaCount > 0 )
+ {
+ nDataAreaCount = r.nDataAreaCount;
+ pDataAreas.reset( new ScArea[nDataAreaCount] );
+ for ( sal_uInt16 i=0; i<nDataAreaCount; i++ )
+ pDataAreas[i] = r.pDataAreas[i];
+ }
+ else
+ pDataAreas.reset();
+ }
+ return *this;
+}
+
+bool ScConsolidateParam::operator==( const ScConsolidateParam& r ) const
+{
+ bool bEqual = (nCol == r.nCol)
+ && (nRow == r.nRow)
+ && (nTab == r.nTab)
+ && (bByCol == r.bByCol)
+ && (bByRow == r.bByRow)
+ && (bReferenceData == r.bReferenceData)
+ && (nDataAreaCount == r.nDataAreaCount)
+ && (eFunction == r.eFunction);
+
+ if ( nDataAreaCount == 0 )
+ bEqual = bEqual && (pDataAreas == nullptr) && (r.pDataAreas == nullptr);
+ else
+ bEqual = bEqual && (pDataAreas != nullptr) && (r.pDataAreas != nullptr);
+
+ if ( bEqual && (nDataAreaCount > 0) )
+ for ( sal_uInt16 i=0; i<nDataAreaCount && bEqual; i++ )
+ bEqual = pDataAreas[i] == r.pDataAreas[i];
+
+ return bEqual;
+}
+
+void ScConsolidateParam::SetAreas( std::unique_ptr<ScArea[]> pAreas, sal_uInt16 nCount )
+{
+ pDataAreas = std::move(pAreas);
+ nDataAreaCount = nCount;
+}
+
+// struct ScSolveParam
+
+ScSolveParam::ScSolveParam()
+{
+}
+
+ScSolveParam::ScSolveParam( const ScSolveParam& r )
+ : aRefFormulaCell ( r.aRefFormulaCell ),
+ aRefVariableCell( r.aRefVariableCell ),
+ pStrTargetVal ( r.pStrTargetVal )
+{
+}
+
+ScSolveParam::ScSolveParam( const ScAddress& rFormulaCell,
+ const ScAddress& rVariableCell,
+ const OUString& rTargetValStr )
+ : aRefFormulaCell ( rFormulaCell ),
+ aRefVariableCell( rVariableCell ),
+ pStrTargetVal ( rTargetValStr )
+{
+}
+
+ScSolveParam::~ScSolveParam()
+{
+}
+
+ScSolveParam& ScSolveParam::operator=( const ScSolveParam& r )
+{
+ aRefFormulaCell = r.aRefFormulaCell;
+ aRefVariableCell = r.aRefVariableCell;
+ pStrTargetVal = r.pStrTargetVal;
+ return *this;
+}
+
+bool ScSolveParam::operator==( const ScSolveParam& r ) const
+{
+ bool bEqual = (aRefFormulaCell == r.aRefFormulaCell)
+ && (aRefVariableCell == r.aRefVariableCell);
+
+ if ( bEqual )
+ {
+ if ( !pStrTargetVal && !r.pStrTargetVal )
+ bEqual = true;
+ else if ( !pStrTargetVal || !r.pStrTargetVal )
+ bEqual = false;
+ else
+ bEqual = ( *pStrTargetVal == *(r.pStrTargetVal) );
+ }
+
+ return bEqual;
+}
+
+// struct ScTabOpParam
+
+ScTabOpParam::ScTabOpParam() : meMode(Column) {}
+
+ScTabOpParam::ScTabOpParam( const ScTabOpParam& r )
+ : aRefFormulaCell ( r.aRefFormulaCell ),
+ aRefFormulaEnd ( r.aRefFormulaEnd ),
+ aRefRowCell ( r.aRefRowCell ),
+ aRefColCell ( r.aRefColCell ),
+ meMode(r.meMode)
+{
+}
+
+ScTabOpParam::ScTabOpParam( const ScRefAddress& rFormulaCell,
+ const ScRefAddress& rFormulaEnd,
+ const ScRefAddress& rRowCell,
+ const ScRefAddress& rColCell,
+ Mode eMode )
+ : aRefFormulaCell ( rFormulaCell ),
+ aRefFormulaEnd ( rFormulaEnd ),
+ aRefRowCell ( rRowCell ),
+ aRefColCell ( rColCell ),
+ meMode(eMode)
+{
+}
+
+ScTabOpParam& ScTabOpParam::operator=( const ScTabOpParam& r )
+{
+ aRefFormulaCell = r.aRefFormulaCell;
+ aRefFormulaEnd = r.aRefFormulaEnd;
+ aRefRowCell = r.aRefRowCell;
+ aRefColCell = r.aRefColCell;
+ meMode = r.meMode;
+ return *this;
+}
+
+bool ScTabOpParam::operator==( const ScTabOpParam& r ) const
+{
+ return ( (aRefFormulaCell == r.aRefFormulaCell)
+ && (aRefFormulaEnd == r.aRefFormulaEnd)
+ && (aRefRowCell == r.aRefRowCell)
+ && (aRefColCell == r.aRefColCell)
+ && (meMode == r.meMode) );
+}
+
+OUString ScGlobal::GetAbsDocName( const OUString& rFileName,
+ const SfxObjectShell* pShell )
+{
+ OUString aAbsName;
+ if (!pShell || !pShell->HasName())
+ { // maybe relative to document path working directory
+ INetURLObject aObj;
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ aObj.SetSmartURL(SvtPathOptions().GetWorkPath());
+ aObj.setFinalSlash(); // it IS a path
+ }
+ else
+ aObj.SetSmartURL(u"file:///tmp/document");
+ bool bWasAbs = true;
+ aAbsName = aObj.smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ // returned string must be encoded because it's used directly to create SfxMedium
+ }
+ else
+ {
+ const SfxMedium* pMedium = pShell->GetMedium();
+ if ( pMedium )
+ {
+ bool bWasAbs = true;
+ aAbsName = pMedium->GetURLObject().smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+ else
+ { // This can't happen, but ...
+ // just to be sure to have the same encoding
+ INetURLObject aObj;
+ aObj.SetSmartURL( aAbsName );
+ aAbsName = aObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+ }
+ return aAbsName;
+}
+
+OUString ScGlobal::GetDocTabName( std::u16string_view rFileName,
+ std::u16string_view rTabName )
+{
+ OUString aDocTab(rFileName);
+ // "'Doc'#Tab"
+ aDocTab = "'" + aDocTab.replaceAll(u"'", u"\\'") + "'" + OUStringChar(SC_COMPILER_FILE_TAB_SEP) + rTabName;
+ return aDocTab;
+}
+
+namespace
+{
+bool isEmptyString( const OUString& rStr )
+{
+ if (rStr.isEmpty())
+ return true;
+ else if (rStr[0] == ' ')
+ {
+ const sal_Unicode* p = rStr.getStr() + 1;
+ const sal_Unicode* const pStop = p - 1 + rStr.getLength();
+ while (p < pStop && *p == ' ')
+ ++p;
+ if (p == pStop)
+ return true;
+ }
+ return false;
+}
+}
+
+double ScGlobal::ConvertStringToValue( const OUString& rStr, const ScCalcConfig& rConfig,
+ FormulaError & rError, FormulaError nStringNoValueError,
+ SvNumberFormatter* pFormatter, SvNumFormatType & rCurFmtType )
+{
+ // We keep ScCalcConfig::StringConversion::LOCALE default until
+ // we provide a friendly way to convert string numbers into numbers in the UI.
+
+ double fValue = 0.0;
+ if (nStringNoValueError == FormulaError::CellNoValue)
+ {
+ // Requested that all strings result in 0, error handled by caller.
+ rError = nStringNoValueError;
+ return fValue;
+ }
+
+ switch (rConfig.meStringConversion)
+ {
+ case ScCalcConfig::StringConversion::ILLEGAL:
+ rError = nStringNoValueError;
+ return fValue;
+ case ScCalcConfig::StringConversion::ZERO:
+ return fValue;
+ case ScCalcConfig::StringConversion::LOCALE:
+ {
+ if (rConfig.mbEmptyStringAsZero)
+ {
+ // The number scanner does not accept empty strings or strings
+ // containing only spaces, be on par in these cases with what was
+ // accepted in OOo and is in AOO (see also the
+ // StringConversion::UNAMBIGUOUS branch) and convert to 0 to prevent
+ // interoperability nightmares.
+
+ if (isEmptyString( rStr))
+ return fValue;
+ }
+
+ if (!pFormatter)
+ goto Label_fallback_to_unambiguous;
+
+ sal_uInt32 nFIndex = 0;
+ if (!pFormatter->IsNumberFormat(rStr, nFIndex, fValue))
+ {
+ rError = nStringNoValueError;
+ fValue = 0.0;
+ }
+ return fValue;
+ }
+ break;
+ case ScCalcConfig::StringConversion::UNAMBIGUOUS:
+Label_fallback_to_unambiguous:
+ {
+ if (!rConfig.mbEmptyStringAsZero)
+ {
+ if (isEmptyString( rStr))
+ {
+ rError = nStringNoValueError;
+ return fValue;
+ }
+ }
+ }
+ // continue below, pulled from switch case for better readability
+ break;
+ }
+
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nParseEnd;
+ // Decimal and group separator 0 => only integer and possibly exponent,
+ // stops at first non-digit non-sign.
+ fValue = ::rtl::math::stringToDouble( rStr, 0, 0, &eStatus, &nParseEnd);
+ sal_Int32 nLen = rStr.getLength();
+ if (eStatus == rtl_math_ConversionStatus_Ok && nParseEnd < nLen)
+ {
+ // Not at string end, check for trailing blanks or switch to date or
+ // time parsing or bail out.
+ const sal_Unicode* const pStart = rStr.getStr();
+ const sal_Unicode* p = pStart + nParseEnd;
+ const sal_Unicode* const pStop = pStart + nLen;
+ switch (*p++)
+ {
+ case ' ':
+ while (p < pStop && *p == ' ')
+ ++p;
+ if (p < pStop)
+ rError = nStringNoValueError;
+ break;
+ case '-':
+ case ':':
+ {
+ bool bDate = (*(p-1) == '-');
+ enum State { year = 0, month, day, hour, minute, second, fraction, done, blank, stop };
+ sal_Int32 nUnit[done] = {0,0,0,0,0,0,0};
+ const sal_Int32 nLimit[done] = {0,12,31,0,59,59,0};
+ State eState = (bDate ? month : minute);
+ rCurFmtType = (bDate ? SvNumFormatType::DATE : SvNumFormatType::TIME);
+ nUnit[eState-1] = o3tl::toInt32(rStr.subView( 0, nParseEnd));
+ const sal_Unicode* pLastStart = p;
+ // Ensure there's no preceding sign. Negative dates
+ // currently aren't handled correctly. Also discard
+ // +CCYY-MM-DD
+ p = pStart;
+ while (p < pStop && *p == ' ')
+ ++p;
+ if (p < pStop && !rtl::isAsciiDigit(*p))
+ rError = nStringNoValueError;
+ p = pLastStart;
+ while (p < pStop && rError == FormulaError::NONE && eState < blank)
+ {
+ if (eState == minute)
+ rCurFmtType |= SvNumFormatType::TIME;
+ if (rtl::isAsciiDigit(*p))
+ {
+ // Maximum 2 digits per unit, except fractions.
+ if (p - pLastStart >= 2 && eState != fraction)
+ rError = nStringNoValueError;
+ }
+ else if (p > pLastStart)
+ {
+ // We had at least one digit.
+ if (eState < done)
+ {
+ nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
+ if (nLimit[eState] && nLimit[eState] < nUnit[eState])
+ rError = nStringNoValueError;
+ }
+ pLastStart = p + 1; // hypothetical next start
+ // Delimiters must match, a trailing delimiter
+ // yields an invalid date/time.
+ switch (eState)
+ {
+ case month:
+ // Month must be followed by separator and
+ // day, no trailing blanks.
+ if (*p != '-' || (p+1 == pStop))
+ rError = nStringNoValueError;
+ break;
+ case day:
+ if ((*p != 'T' || (p+1 == pStop)) && *p != ' ')
+ rError = nStringNoValueError;
+ // Take one blank as a valid delimiter
+ // between date and time.
+ break;
+ case hour:
+ // Hour must be followed by separator and
+ // minute, no trailing blanks.
+ if (*p != ':' || (p+1 == pStop))
+ rError = nStringNoValueError;
+ break;
+ case minute:
+ if ((*p != ':' || (p+1 == pStop)) && *p != ' ')
+ rError = nStringNoValueError;
+ if (*p == ' ')
+ eState = done;
+ break;
+ case second:
+ if (((*p != ',' && *p != '.') || (p+1 == pStop)) && *p != ' ')
+ rError = nStringNoValueError;
+ if (*p == ' ')
+ eState = done;
+ break;
+ case fraction:
+ eState = done;
+ break;
+ default:
+ rError = nStringNoValueError;
+ break;
+ }
+ eState = static_cast<State>(eState + 1);
+ }
+ else
+ rError = nStringNoValueError;
+ ++p;
+ }
+ if (eState == blank)
+ {
+ while (p < pStop && *p == ' ')
+ ++p;
+ if (p < pStop)
+ rError = nStringNoValueError;
+ eState = stop;
+ }
+
+ // Month without day, or hour without minute.
+ if (eState == month || (eState == day && p <= pLastStart) ||
+ eState == hour || (eState == minute && p <= pLastStart))
+ rError = nStringNoValueError;
+
+ if (rError == FormulaError::NONE)
+ {
+ // Catch the very last unit at end of string.
+ if (p > pLastStart && eState < done)
+ {
+ nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
+ if (nLimit[eState] && nLimit[eState] < nUnit[eState])
+ rError = nStringNoValueError;
+ }
+ if (bDate && nUnit[hour] > 23)
+ rError = nStringNoValueError;
+ if (rError == FormulaError::NONE)
+ {
+ if (bDate && nUnit[day] == 0)
+ nUnit[day] = 1;
+ double fFraction = (nUnit[fraction] <= 0 ? 0.0 :
+ ::rtl::math::pow10Exp( nUnit[fraction],
+ static_cast<int>( -ceil( log10( static_cast<double>( nUnit[fraction]))))));
+ if (!bDate)
+ fValue = 0.0;
+ else
+ {
+ Date aDate(
+ sal::static_int_cast<sal_Int16>(nUnit[day]),
+ sal::static_int_cast<sal_Int16>(nUnit[month]),
+ sal::static_int_cast<sal_Int16>(nUnit[year]));
+ if (!aDate.IsValidDate())
+ rError = nStringNoValueError;
+ else
+ {
+ if (pFormatter)
+ fValue = aDate - pFormatter->GetNullDate();
+ else
+ {
+ SAL_WARN("sc.core","ScGlobal::ConvertStringToValue - fixed null date");
+ static Date aDefaultNullDate( 30, 12, 1899);
+ fValue = aDate - aDefaultNullDate;
+ }
+ }
+ }
+ fValue += ((nUnit[hour] * 3600) + (nUnit[minute] * 60) + nUnit[second] + fFraction) / 86400.0;
+ }
+ }
+ }
+ break;
+ default:
+ rError = nStringNoValueError;
+ }
+ if (rError != FormulaError::NONE)
+ fValue = 0.0;
+ }
+ return fValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/globalx.cxx b/sc/source/core/data/globalx.cxx
new file mode 100644
index 0000000000..56d48e039b
--- /dev/null
+++ b/sc/source/core/data/globalx.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <callform.hxx>
+#include <global.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <ucbhelper/content.hxx>
+
+#include <unotools/pathoptions.hxx>
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+
+#include <com/sun/star/i18n/OrdinalSuffix.hpp>
+#include <comphelper/processfactory.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+namespace com::sun::star::ucb { class XCommandEnvironment; }
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+void ScGlobal::InitAddIns()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+
+ // multi paths separated by semicolons
+ SvtPathOptions aPathOpt;
+ const OUString& aMultiPath = aPathOpt.GetAddinPath();
+ if (aMultiPath.isEmpty())
+ return;
+
+ sal_Int32 nIdx {0};
+ do
+ {
+ OUString aPath = aMultiPath.getToken(0, ';', nIdx);
+ if (aPath.isEmpty())
+ continue;
+
+ OUString aUrl;
+ if ( osl::FileBase::getFileURLFromSystemPath( aPath, aUrl ) == osl::FileBase::E_None )
+ aPath = aUrl;
+
+ INetURLObject aObj;
+ aObj.SetSmartURL( aPath );
+ aObj.setFinalSlash();
+ try
+ {
+ ::ucbhelper::Content aCnt( aObj.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ Reference< XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ Reference< sdbc::XResultSet > xResultSet;
+ Sequence< OUString > aProps;
+ try
+ {
+ xResultSet = aCnt.createCursor(
+ aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY );
+ }
+ catch ( Exception& )
+ {
+ // ucb may throw different exceptions on failure now
+ // no assertion if AddIn directory doesn't exist
+ }
+
+ if ( xResultSet.is() )
+ {
+ Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ try
+ {
+ if ( xResultSet->first() )
+ {
+ do
+ {
+ OUString aId = xContentAccess->queryContentIdentifierString();
+ InitExternalFunc( aId );
+ }
+ while ( xResultSet->next() );
+ }
+ }
+ catch ( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "" );
+ }
+ }
+ }
+ catch ( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "" );
+ }
+ catch ( ... )
+ {
+ OSL_FAIL( "unexpected exception caught!" );
+ }
+ }
+ while (nIdx>0);
+}
+
+OUString ScGlobal::GetOrdinalSuffix( sal_Int32 nNumber)
+{
+ try
+ {
+ if (!xOrdinalSuffix.is())
+ {
+ xOrdinalSuffix = i18n::OrdinalSuffix::create( ::comphelper::getProcessComponentContext() );
+ }
+ uno::Sequence< OUString > aSuffixes = xOrdinalSuffix->getOrdinalSuffix( nNumber,
+ ScGlobal::getLocaleData().getLanguageTag().getLocale());
+ if ( aSuffixes.hasElements() )
+ return aSuffixes[0];
+ else
+ return OUString();
+ }
+ catch ( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "GetOrdinalSuffix: exception caught during init" );
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/grouptokenconverter.cxx b/sc/source/core/data/grouptokenconverter.cxx
new file mode 100644
index 0000000000..4d427fc32b
--- /dev/null
+++ b/sc/source/core/data/grouptokenconverter.cxx
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <grouptokenconverter.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <refdata.hxx>
+
+#include <formula/token.hxx>
+#include <formula/vectortoken.hxx>
+
+using namespace formula;
+
+bool ScGroupTokenConverter::isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
+{
+ if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
+ return false;
+
+ SCROW nLen = mrCell.GetCellGroup()->mnLength;
+ SCROW nEndRow = mrPos.Row() + nLen - 1;
+
+ if (nRelRow < 0)
+ {
+ SCROW nTest = nEndRow;
+ nTest += nRelRow;
+ if (nTest >= mrPos.Row())
+ return true;
+ }
+ else if (nRelRow > 0)
+ {
+ SCROW nTest = mrPos.Row(); // top row.
+ nTest += nRelRow;
+ if (nTest <= nEndRow)
+ return true;
+ }
+
+ return false;
+}
+
+bool ScGroupTokenConverter::isSelfReferenceAbsolute(const ScAddress& rRefPos)
+{
+ if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
+ return false;
+
+ SCROW nLen = mrCell.GetCellGroup()->mnLength;
+ SCROW nEndRow = mrPos.Row() + nLen - 1;
+
+ if (rRefPos.Row() < mrPos.Row())
+ return false;
+
+ if (rRefPos.Row() > nEndRow)
+ return false;
+
+ return true;
+}
+
+SCROW ScGroupTokenConverter::trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
+{
+ SCROW nLastRow = nRow + nRowLen - 1; // current last row.
+ nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
+ if (nLastRow < (nRow + nRowLen - 1))
+ {
+ // This can end up negative! Was that the original intent, or
+ // is it accidental? Was it not like that originally but the
+ // surrounding conditions changed?
+ nRowLen = nLastRow - nRow + 1;
+ // Anyway, let's assume it doesn't make sense to return a
+ // negative value here. But should we then return 0 or 1? In
+ // the "Column is empty" case below, we return 1, why!? And,
+ // at the callsites there are tests for a zero value returned
+ // from this function (but not for a negative one).
+ if (nRowLen < 0)
+ nRowLen = 0;
+ }
+ else if (nLastRow == 0)
+ // Column is empty.
+ nRowLen = 1;
+
+ return nRowLen;
+}
+
+ScGroupTokenConverter::ScGroupTokenConverter(
+ ScTokenArray& rGroupTokens, ScDocument& rDoc, const ScFormulaCell& rCell, const ScAddress& rPos) :
+ mrGroupTokens(rGroupTokens),
+ mrDoc(rDoc),
+ mrCell(rCell),
+ mrPos(rPos)
+{
+}
+
+bool ScGroupTokenConverter::convert( const ScTokenArray& rCode, sc::FormulaLogger::GroupScope& rScope )
+{
+#if 0
+ { // debug to start with:
+ ScCompiler aComp( &mrDoc, mrPos, rCode, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
+ OUStringBuffer aAsString;
+ aComp.CreateStringFromTokenArray(aAsString);
+ }
+#endif
+
+ const SCROW nLen = mrCell.GetCellGroup()->mnLength;
+ formula::FormulaTokenArrayPlainIterator aIter(rCode);
+ for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ // A reference can be either absolute or relative. If it's absolute,
+ // convert it to a static value token. If relative, convert it to a
+ // vector reference token. Note: we only care about relative vs
+ // absolute reference state for row directions.
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData aRef = *p->GetSingleRef();
+ if( aRef.IsDeleted())
+ return false;
+ ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
+ if (aRef.IsRowRel())
+ {
+ if (isSelfReferenceRelative(aRefPos, aRef.Row()))
+ return false;
+
+ // Trim data array length to actual data range.
+ SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row(), nLen);
+ // Fetch double array guarantees that the length of the
+ // returned array equals or greater than the requested
+ // length.
+
+ formula::VectorRefArray aArray;
+ if (nTrimLen)
+ {
+#ifdef DBG_UTIL
+ // All the necessary Interpret() calls for all the cells
+ // should have been already handled by ScDependantsCalculator
+ // calling HandleRefArrayForParallelism(), and that handling also checks
+ // for cycles etc. Recursively calling Interpret() from here (which shouldn't
+ // happen) could lead to unhandled problems.
+ // Also, because of caching FetchVectorRefArray() fetches values for all rows
+ // up to the maximum one, so check those too.
+ mrDoc.AssertNoInterpretNeeded(
+ ScAddress(aRefPos.Col(), 0, aRefPos.Tab()), nTrimLen + aRefPos.Row());
+#endif
+ aArray = mrDoc.FetchVectorRefArray(aRefPos, nTrimLen);
+ }
+
+ if (!aArray.isValid())
+ return false;
+
+ formula::SingleVectorRefToken aTok(aArray, nTrimLen);
+ mrGroupTokens.AddToken(aTok);
+ rScope.addRefMessage(mrPos, aRefPos, nLen, aArray);
+
+ if (nTrimLen && !mxFormulaGroupContext)
+ {
+ //tdf#98880 if the SingleVectorRefToken relies on the
+ //underlying storage provided by the Document
+ //FormulaGroupContext, take a reference to it here to
+ //ensure that backing storage exists for our lifetime
+ mxFormulaGroupContext = mrDoc.GetFormulaGroupContext();
+ }
+ }
+ else
+ {
+ // Absolute row reference.
+ if (isSelfReferenceAbsolute(aRefPos))
+ return false;
+
+ formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefPos);
+ if (!pNewToken)
+ return false;
+
+ mrGroupTokens.AddToken(*pNewToken);
+ rScope.addRefMessage(mrPos, aRefPos, *pNewToken);
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ // This code may break in case of implicit intersection, leading to unnecessarily large
+ // matrix operations and possibly incorrect results (=C:C/D:D). That is handled by
+ // having ScCompiler check that there are no possible implicit intersections.
+ // Additionally some functions such as INDEX() and OFFSET() require a reference,
+ // that is handled by denylisting those opcodes in ScTokenArray::CheckToken().
+
+ ScComplexRefData aRef = *p->GetDoubleRef();
+ if( aRef.IsDeleted())
+ return false;
+ ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
+
+ // Multiple sheets not handled by vector/matrix.
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ return false;
+
+ // Check for self reference.
+ if (aRef.Ref1.IsRowRel())
+ {
+ if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
+ return false;
+ }
+ else if (isSelfReferenceAbsolute(aAbs.aStart))
+ return false;
+
+ if (aRef.Ref2.IsRowRel())
+ {
+ if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
+ return false;
+ }
+ else if (isSelfReferenceAbsolute(aAbs.aEnd))
+ return false;
+
+ // Row reference is relative.
+ bool bAbsFirst = !aRef.Ref1.IsRowRel();
+ bool bAbsLast = !aRef.Ref2.IsRowRel();
+ ScAddress aRefPos = aAbs.aStart;
+ size_t nCols = aAbs.aEnd.Col() - aAbs.aStart.Col() + 1;
+ std::vector<formula::VectorRefArray> aArrays;
+ aArrays.reserve(nCols);
+ SCROW nRefRowSize = aAbs.aEnd.Row() - aAbs.aStart.Row() + 1;
+ SCROW nArrayLength = nRefRowSize;
+ if (!bAbsLast)
+ {
+ // range end position is relative. Extend the array length.
+ SCROW nLastRefRowOffset = aAbs.aEnd.Row() - mrPos.Row();
+ SCROW nLastRefRow = mrPos.Row() + nLen - 1 + nLastRefRowOffset;
+ SCROW nNewLength = nLastRefRow - aAbs.aStart.Row() + 1;
+ if (nNewLength > nArrayLength)
+ nArrayLength = nNewLength;
+ }
+
+ // Trim trailing empty rows.
+ SCROW nRequestedLength = nArrayLength; // keep the original length.
+ nArrayLength = trimLength(aRefPos.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), aRefPos.Row(), nArrayLength);
+
+ for (SCCOL i = aAbs.aStart.Col(); i <= aAbs.aEnd.Col(); ++i)
+ {
+ aRefPos.SetCol(i);
+ formula::VectorRefArray aArray;
+ if (nArrayLength)
+ {
+#ifdef DBG_UTIL
+ mrDoc.AssertNoInterpretNeeded(
+ ScAddress(aRefPos.Col(), 0, aRefPos.Tab()), nArrayLength + aRefPos.Row());
+#endif
+ aArray = mrDoc.FetchVectorRefArray(aRefPos, nArrayLength);
+ }
+
+ if (!aArray.isValid())
+ return false;
+
+ aArrays.push_back(aArray);
+ }
+
+ std::vector<formula::VectorRefArray> aArraysTmp = aArrays;
+ formula::DoubleVectorRefToken aTok( std::move(aArraysTmp), nArrayLength, nRefRowSize, bAbsFirst, bAbsLast );
+ mrGroupTokens.AddToken(aTok);
+ rScope.addRefMessage(mrPos, aAbs.aStart, nRequestedLength, aArrays);
+
+ if (nArrayLength && !aArrays.empty() && !mxFormulaGroupContext)
+ {
+ //tdf#98880 if the DoubleVectorRefToken relies on the
+ //underlying storage provided by the Document
+ //FormulaGroupContext, take a reference to it here to
+ //ensure that backing storage exists for our lifetime
+ mxFormulaGroupContext = mrDoc.GetFormulaGroupContext();
+ }
+ }
+ break;
+ case svIndex:
+ {
+ if (p->GetOpCode() != ocName)
+ {
+ // May be DB-range or TableRef
+ mrGroupTokens.AddToken(*p);
+ break;
+ }
+
+ // Named range.
+ ScRangeName* pNames = mrDoc.GetRangeName();
+ if (!pNames)
+ // This should never fail.
+ return false;
+
+ ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
+ if (!pRange)
+ // No named range exists by that index.
+ return false;
+
+ ScTokenArray* pNamedTokens = pRange->GetCode();
+ if (!pNamedTokens)
+ // This named range is empty.
+ return false;
+
+ mrGroupTokens.AddOpCode(ocOpen);
+
+ if (!convert(*pNamedTokens, rScope))
+ return false;
+
+ mrGroupTokens.AddOpCode(ocClose);
+ }
+ break;
+ default:
+ mrGroupTokens.AddToken(*p);
+ }
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/listenercontext.cxx b/sc/source/core/data/listenercontext.cxx
new file mode 100644
index 0000000000..e8bfd49ae7
--- /dev/null
+++ b/sc/source/core/data/listenercontext.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/.
+ */
+
+#include <listenercontext.hxx>
+#include <document.hxx>
+#include <mtvelements.hxx>
+#include <utility>
+
+namespace sc {
+
+StartListeningContext::StartListeningContext(ScDocument& rDoc) :
+ mrDoc(rDoc), mpSet(std::make_shared<ColumnBlockPositionSet>(rDoc)) {}
+
+StartListeningContext::StartListeningContext(
+ ScDocument& rDoc, std::shared_ptr<ColumnBlockPositionSet> pSet) :
+ mrDoc(rDoc), mpSet(std::move(pSet)) {}
+
+void StartListeningContext::setColumnSet( const std::shared_ptr<const ColumnSet>& rpColSet )
+{
+ mpColSet = rpColSet;
+}
+
+const std::shared_ptr<const ColumnSet>& StartListeningContext::getColumnSet() const
+{
+ return mpColSet;
+}
+
+ColumnBlockPosition* StartListeningContext::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+ return mpSet->getBlockPosition(nTab, nCol);
+}
+
+EndListeningContext::EndListeningContext(ScDocument& rDoc, ScTokenArray* pOldCode) :
+ mrDoc(rDoc), mpPosSet(std::make_shared<ColumnBlockPositionSet>(rDoc)),
+ mpOldCode(pOldCode), maPosDelta(0,0,0) {}
+
+EndListeningContext::EndListeningContext(
+ ScDocument& rDoc, std::shared_ptr<ColumnBlockPositionSet> pSet, ScTokenArray* pOldCode) :
+ mrDoc(rDoc), mpPosSet(std::move(pSet)),
+ mpOldCode(pOldCode), maPosDelta(0,0,0) {}
+
+void EndListeningContext::setPositionDelta( const ScAddress& rDelta )
+{
+ maPosDelta = rDelta;
+}
+
+ScAddress EndListeningContext::getOldPosition( const ScAddress& rPos ) const
+{
+ ScAddress aOldPos = rPos;
+ aOldPos.IncCol(maPosDelta.Col());
+ aOldPos.IncRow(maPosDelta.Row());
+ aOldPos.IncTab(maPosDelta.Tab());
+ return aOldPos;
+}
+
+ColumnBlockPosition* EndListeningContext::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+ return mpPosSet->getBlockPosition(nTab, nCol);
+}
+
+void EndListeningContext::addEmptyBroadcasterPosition(SCTAB nTab, SCCOL nCol, SCROW nRow)
+{
+ maSet.set(mrDoc, nTab, nCol, nRow, true);
+}
+
+void EndListeningContext::purgeEmptyBroadcasters()
+{
+ PurgeListenerAction aAction(mrDoc);
+ maSet.executeAction(mrDoc, aAction);
+}
+
+PurgeListenerAction::PurgeListenerAction(ScDocument& rDoc) :
+ mrDoc(rDoc), mpBlockPos(new ColumnBlockPosition) {}
+
+void PurgeListenerAction::startColumn( SCTAB nTab, SCCOL nCol )
+{
+ mrDoc.InitColumnBlockPosition(*mpBlockPos, nTab, nCol);
+}
+
+void PurgeListenerAction::execute( const ScAddress& rPos, SCROW nLength, bool bVal )
+{
+ if (bVal)
+ {
+ mrDoc.DeleteBroadcasters(*mpBlockPos, rPos, nLength);
+ }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/markarr.cxx b/sc/source/core/data/markarr.cxx
new file mode 100644
index 0000000000..9b93c32f2d
--- /dev/null
+++ b/sc/source/core/data/markarr.cxx
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <markarr.hxx>
+#include <address.hxx>
+#include <sheetlimits.hxx>
+#include <vector>
+
+ScMarkArray::ScMarkArray(const ScSheetLimits& rLimits) :
+ mrSheetLimits(rLimits)
+{
+ Reset(false);
+}
+
+// Move constructor
+ScMarkArray::ScMarkArray( ScMarkArray&& rOther ) noexcept
+ : mrSheetLimits(rOther.mrSheetLimits)
+{
+ operator=(std::move(rOther));
+}
+
+// Copy constructor
+ScMarkArray::ScMarkArray( const ScMarkArray & rOther )
+ : mrSheetLimits(rOther.mrSheetLimits)
+{
+ operator=(rOther);
+}
+
+void ScMarkArray::Reset( bool bMarked, SCSIZE nNeeded )
+{
+ // always create pData here
+ // (or have separate method to ensure pData)
+
+ assert(nNeeded);
+ mvData.resize(1);
+ mvData.reserve(nNeeded);
+ mvData[0].nRow = mrSheetLimits.mnMaxRow;
+ mvData[0].bMarked = bMarked;
+}
+
+// Iterative implementation of Binary Search
+bool ScMarkArray::Search( SCROW nRow, SCSIZE& nIndex ) const
+{
+ assert(mvData.size() > 0);
+ SCSIZE nHi = mvData.size() - 1;
+ SCSIZE nLo = 0;
+
+ while ( nLo <= nHi )
+ {
+ SCSIZE i = (nLo + nHi) / 2;
+
+ if (mvData[i].nRow < nRow)
+ {
+ // If [nRow] greater, ignore left half
+ nLo = i + 1;
+ }
+ else if ((i > 0) && (mvData[i - 1].nRow >= nRow))
+ {
+ // If [nRow] is smaller, ignore right half
+ nHi = i - 1;
+ }
+ else
+ {
+ // found
+ nIndex=i;
+ return true;
+ }
+ }
+
+ // not found
+ nIndex=0;
+ return false;
+}
+
+bool ScMarkArray::GetMark( SCROW nRow ) const
+{
+ SCSIZE i;
+ if (Search( nRow, i ))
+ return mvData[i].bMarked;
+ else
+ return false;
+
+}
+
+void ScMarkArray::SetMarkArea( SCROW nStartRow, SCROW nEndRow, bool bMarked )
+{
+ if (!(mrSheetLimits.ValidRow(nStartRow) && mrSheetLimits.ValidRow(nEndRow)))
+ return;
+
+ if ((nStartRow == 0) && (nEndRow == mrSheetLimits.mnMaxRow))
+ {
+ Reset(bMarked);
+ }
+ else
+ {
+ SCSIZE ni; // number of entries in beginning
+ SCSIZE nInsert; // insert position (mnMaxRow+1 := no insert)
+ bool bCombined = false;
+ bool bSplit = false;
+ if ( nStartRow > 0 )
+ {
+ // skip beginning
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ ni = nIndex;
+
+ nInsert = mrSheetLimits.GetMaxRowCount();
+ if ( mvData[ni].bMarked != bMarked )
+ {
+ if ( ni == 0 || (mvData[ni-1].nRow < nStartRow - 1) )
+ { // may be a split or a simple insert or just a shrink,
+ // row adjustment is done further down
+ if ( mvData[ni].nRow > nEndRow )
+ bSplit = true;
+ ni++;
+ nInsert = ni;
+ }
+ else if ( ni > 0 && mvData[ni-1].nRow == nStartRow - 1 )
+ nInsert = ni;
+ }
+ if ( ni > 0 && mvData[ni-1].bMarked == bMarked )
+ { // combine
+ mvData[ni-1].nRow = nEndRow;
+ nInsert = mrSheetLimits.GetMaxRowCount();
+ bCombined = true;
+ }
+ }
+ else
+ {
+ nInsert = 0;
+ ni = 0;
+ }
+
+ SCSIZE nj = ni; // stop position of range to replace
+ while ( nj < mvData.size() && mvData[nj].nRow <= nEndRow )
+ nj++;
+ if ( !bSplit )
+ {
+ if ( nj < mvData.size() && mvData[nj].bMarked == bMarked )
+ { // combine
+ if ( ni > 0 )
+ {
+ if ( mvData[ni-1].bMarked == bMarked )
+ { // adjacent entries
+ mvData[ni-1].nRow = mvData[nj].nRow;
+ nj++;
+ }
+ else if ( ni == nInsert )
+ mvData[ni-1].nRow = nStartRow - 1; // shrink
+ }
+ nInsert = mrSheetLimits.GetMaxRowCount();
+ bCombined = true;
+ }
+ else if ( ni > 0 && ni == nInsert )
+ mvData[ni-1].nRow = nStartRow - 1; // shrink
+ }
+ if ( ni < nj )
+ { // remove middle entries
+ if ( !bCombined )
+ { // replace one entry
+ mvData[ni].nRow = nEndRow;
+ mvData[ni].bMarked = bMarked;
+ ni++;
+ nInsert = mrSheetLimits.GetMaxRowCount();
+ }
+ if ( ni < nj )
+ { // remove entries
+ mvData.erase(mvData.begin() + ni, mvData.begin() + nj);
+ }
+ }
+
+ if ( nInsert < sal::static_int_cast<SCSIZE>(mrSheetLimits.GetMaxRowCount()) )
+ { // insert or append new entry
+ if ( nInsert <= mvData.size() )
+ {
+ if ( !bSplit )
+ mvData.insert(mvData.begin() + nInsert, { nEndRow, bMarked });
+ else
+ {
+ mvData.insert(mvData.begin() + nInsert, 2, { nEndRow, bMarked });
+ mvData[nInsert+1] = mvData[nInsert-1];
+ }
+ }
+ else
+ mvData.push_back(ScMarkEntry{ nEndRow, bMarked });
+ if ( nInsert )
+ mvData[nInsert-1].nRow = nStartRow - 1;
+ }
+ }
+}
+
+/**
+ optimised init-from-range-list. Specifically this is optimised for cases
+ where we have very large data columns with lots and lots of ranges.
+*/
+void ScMarkArray::Set( std::vector<ScMarkEntry> && rMarkEntries )
+{
+ mvData = std::move(rMarkEntries);
+}
+
+bool ScMarkArray::IsAllMarked( SCROW nStartRow, SCROW nEndRow ) const
+{
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+
+ if (Search( nStartRow, nStartIndex ))
+ if (mvData[nStartIndex].bMarked)
+ if (Search( nEndRow, nEndIndex ))
+ if (nEndIndex==nStartIndex)
+ return true;
+
+ return false;
+}
+
+bool ScMarkArray::HasOneMark( SCROW& rStartRow, SCROW& rEndRow ) const
+{
+ bool bRet = false;
+ if ( mvData.size() == 1 )
+ {
+ if ( mvData[0].bMarked )
+ {
+ rStartRow = 0;
+ rEndRow = mrSheetLimits.mnMaxRow;
+ bRet = true;
+ }
+ }
+ else if ( mvData.size() == 2 )
+ {
+ if ( mvData[0].bMarked )
+ {
+ rStartRow = 0;
+ rEndRow = mvData[0].nRow;
+ }
+ else
+ {
+ rStartRow = mvData[0].nRow + 1;
+ rEndRow = mrSheetLimits.mnMaxRow;
+ }
+ bRet = true;
+ }
+ else if ( mvData.size() == 3 )
+ {
+ if ( mvData[1].bMarked )
+ {
+ rStartRow = mvData[0].nRow + 1;
+ rEndRow = mvData[1].nRow;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool ScMarkArray::operator==( const ScMarkArray& rOther ) const
+{
+ return mvData == rOther.mvData;
+}
+
+ScMarkArray& ScMarkArray::operator=( const ScMarkArray& rOther )
+{
+ mvData = rOther.mvData;
+ return *this;
+}
+
+ScMarkArray& ScMarkArray::operator=(ScMarkArray&& rOther) noexcept
+{
+ mvData = std::move(rOther.mvData);
+ return *this;
+}
+
+SCROW ScMarkArray::GetNextMarked( SCROW nRow, bool bUp ) const
+{
+ SCROW nRet = nRow;
+ if (mrSheetLimits.ValidRow(nRow))
+ {
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ if (!mvData[nIndex].bMarked)
+ {
+ if (bUp)
+ {
+ if (nIndex>0)
+ nRet = mvData[nIndex-1].nRow;
+ else
+ nRet = -1;
+ }
+ else
+ nRet = mvData[nIndex].nRow + 1;
+ }
+ }
+ return nRet;
+}
+
+SCROW ScMarkArray::GetMarkEnd( SCROW nRow, bool bUp ) const
+{
+ SCROW nRet;
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ assert( mvData[nIndex].bMarked && "GetMarkEnd without bMarked" );
+ if (bUp)
+ {
+ if (nIndex>0)
+ nRet = mvData[nIndex-1].nRow + 1;
+ else
+ nRet = 0;
+ }
+ else
+ nRet = mvData[nIndex].nRow;
+
+ return nRet;
+}
+
+void ScMarkArray::Shift(SCROW nStartRow, tools::Long nOffset)
+{
+ if (nOffset == 0 || nStartRow > mrSheetLimits.mnMaxRow)
+ return;
+
+ for (size_t i=0; i < mvData.size(); ++i)
+ {
+ auto& rEntry = mvData[i];
+
+ if (rEntry.nRow < nStartRow)
+ continue;
+ rEntry.nRow += nOffset;
+ if (rEntry.nRow < 0)
+ {
+ rEntry.nRow = 0;
+ }
+ else if (rEntry.nRow > mrSheetLimits.mnMaxRow)
+ {
+ rEntry.nRow = mrSheetLimits.mnMaxRow;
+ }
+ }
+}
+
+void ScMarkArray::Intersect(const ScMarkArray& rOther)
+{
+ size_t i = 0;
+ size_t j = 0;
+
+ std::vector<ScMarkEntry> aEntryArray;
+ aEntryArray.reserve(std::max(mvData.size(), rOther.mvData.size()));
+
+ while (i < mvData.size() && j < rOther.mvData.size())
+ {
+ const auto& rEntry = mvData[i];
+ const auto& rOtherEntry = rOther.mvData[j];
+
+ if (rEntry.bMarked != rOtherEntry.bMarked)
+ {
+ if (!rOtherEntry.bMarked)
+ {
+ aEntryArray.push_back(rOther.mvData[j++]);
+ while (i < mvData.size() && mvData[i].nRow <= rOtherEntry.nRow)
+ ++i;
+ }
+ else // rEntry not marked
+ {
+ aEntryArray.push_back(mvData[i++]);
+ while (j < rOther.mvData.size() && rOther.mvData[j].nRow <= rEntry.nRow)
+ ++j;
+ }
+ }
+ else // rEntry.bMarked == rOtherEntry.bMarked
+ {
+ if (rEntry.bMarked) // both marked
+ {
+ if (rEntry.nRow <= rOtherEntry.nRow)
+ {
+ aEntryArray.push_back(mvData[i++]); // upper row
+ if (rEntry.nRow == rOtherEntry.nRow)
+ ++j;
+ }
+ else
+ {
+ aEntryArray.push_back(rOther.mvData[j++]); // upper row
+ }
+ }
+ else // both not marked
+ {
+ if (rEntry.nRow <= rOtherEntry.nRow)
+ {
+ aEntryArray.push_back(rOther.mvData[j++]); // lower row
+ while (i < mvData.size() && mvData[i].nRow <= rOtherEntry.nRow)
+ ++i;
+ }
+ else
+ {
+ aEntryArray.push_back(mvData[i++]); // lower row
+ while (j < rOther.mvData.size() && rOther.mvData[j].nRow <= rEntry.nRow)
+ ++j;
+ }
+ }
+ }
+ }
+
+ assert((i == mvData.size() || j == rOther.mvData.size()) && "Unexpected case.");
+
+ if (i == mvData.size())
+ {
+ aEntryArray.insert(aEntryArray.end(), rOther.mvData.begin() + j, rOther.mvData.end());
+ }
+ else // j == rOther.nCount
+ {
+ aEntryArray.insert(aEntryArray.end(), mvData.begin() + i, mvData.end());
+ }
+
+ mvData = std::move(aEntryArray);
+}
+
+
+// -------------- Iterator ----------------------------------------------
+
+ScMarkArrayIter::ScMarkArrayIter( const ScMarkArray* pNewArray ) :
+ pArray( pNewArray ),
+ nPos( 0 )
+{
+}
+
+void ScMarkArrayIter::reset( const ScMarkArray* pNewArray )
+{
+ pArray = pNewArray;
+ nPos = 0;
+}
+
+bool ScMarkArrayIter::Next( SCROW& rTop, SCROW& rBottom )
+{
+ if (!pArray)
+ return false;
+ if ( nPos >= pArray->mvData.size() )
+ return false;
+ while (!pArray->mvData[nPos].bMarked)
+ {
+ ++nPos;
+ if ( nPos >= pArray->mvData.size() )
+ return false;
+ }
+ rBottom = pArray->mvData[nPos].nRow;
+ if (nPos==0)
+ rTop = 0;
+ else
+ rTop = pArray->mvData[nPos-1].nRow + 1;
+ ++nPos;
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/markdata.cxx b/sc/source/core/data/markdata.cxx
new file mode 100644
index 0000000000..54379a5547
--- /dev/null
+++ b/sc/source/core/data/markdata.cxx
@@ -0,0 +1,950 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <markdata.hxx>
+#include <markarr.hxx>
+#include <markmulti.hxx>
+#include <rangelst.hxx>
+#include <segmenttree.hxx>
+#include <sheetlimits.hxx>
+#include <document.hxx>
+#include <columnspanset.hxx>
+#include <fstalgorithm.hxx>
+#include <unordered_map>
+
+#include <osl/diagnose.h>
+
+#include <mdds/flat_segment_tree.hpp>
+#include <cassert>
+
+
+ScMarkData::ScMarkData(const ScSheetLimits& rSheetLimits) :
+ aMultiSel(rSheetLimits),
+ mrSheetLimits(rSheetLimits)
+{
+ ResetMark();
+}
+
+ScMarkData& ScMarkData::operator=(const ScMarkData& rOther)
+{
+ maTabMarked = rOther.maTabMarked;
+ aMarkRange = rOther.aMarkRange;
+ aMultiRange = rOther.aMultiRange;
+ aMultiSel = rOther.aMultiSel;
+ aTopEnvelope = rOther.aTopEnvelope;
+ aBottomEnvelope = rOther.aBottomEnvelope;
+ aLeftEnvelope = rOther.aLeftEnvelope;
+ aRightEnvelope = rOther.aRightEnvelope;
+ bMarked = rOther.bMarked;
+ bMultiMarked = rOther.bMultiMarked;
+ bMarking = rOther.bMarking;
+ bMarkIsNeg = rOther.bMarkIsNeg;
+ return *this;
+}
+
+ScMarkData& ScMarkData::operator=(ScMarkData&& rOther)
+{
+ maTabMarked = std::move(rOther.maTabMarked);
+ aMarkRange = std::move(rOther.aMarkRange);
+ aMultiRange = std::move(rOther.aMultiRange);
+ aMultiSel = std::move(rOther.aMultiSel);
+ aTopEnvelope = std::move(rOther.aTopEnvelope);
+ aBottomEnvelope = std::move(rOther.aBottomEnvelope);
+ aLeftEnvelope = std::move(rOther.aLeftEnvelope);
+ aRightEnvelope = std::move(rOther.aRightEnvelope);
+ bMarked = rOther.bMarked;
+ bMultiMarked = rOther.bMultiMarked;
+ bMarking = rOther.bMarking;
+ bMarkIsNeg = rOther.bMarkIsNeg;
+ return *this;
+}
+
+
+void ScMarkData::ResetMark()
+{
+ aMultiSel.Clear();
+
+ bMarked = bMultiMarked = false;
+ bMarking = bMarkIsNeg = false;
+ aTopEnvelope.RemoveAll();
+ aBottomEnvelope.RemoveAll();
+ aLeftEnvelope.RemoveAll();
+ aRightEnvelope.RemoveAll();
+}
+
+void ScMarkData::SetMarkArea( const ScRange& rRange )
+{
+ aMarkRange = rRange;
+ aMarkRange.PutInOrder();
+ if ( !bMarked )
+ {
+ // Upon creation of a document ScFormatShell GetTextAttrState
+ // may query (default) attributes although no sheet is marked yet.
+ // => mark that one.
+ if ( !GetSelectCount() )
+ maTabMarked.insert( aMarkRange.aStart.Tab() );
+ bMarked = true;
+ }
+}
+
+void ScMarkData::SetMultiMarkArea( const ScRange& rRange, bool bMark, bool bSetupMulti )
+{
+ if ( aMultiSel.IsEmpty() )
+ {
+ // if simple mark range is set, copy to multi marks
+ if ( bMarked && !bMarkIsNeg && !bSetupMulti )
+ {
+ bMarked = false;
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ PutInOrder( nStartCol, nEndCol );
+ SetMultiMarkArea( aMarkRange, true, true );
+ }
+ }
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartCol, nEndCol );
+
+ aMultiSel.SetMarkArea( nStartCol, nEndCol, nStartRow, nEndRow, bMark );
+
+ if ( bMultiMarked ) // Update aMultiRange
+ {
+ if ( nStartCol < aMultiRange.aStart.Col() )
+ aMultiRange.aStart.SetCol( nStartCol );
+ if ( nStartRow < aMultiRange.aStart.Row() )
+ aMultiRange.aStart.SetRow( nStartRow );
+ if ( nEndCol > aMultiRange.aEnd.Col() )
+ aMultiRange.aEnd.SetCol( nEndCol );
+ if ( nEndRow > aMultiRange.aEnd.Row() )
+ aMultiRange.aEnd.SetRow( nEndRow );
+ }
+ else
+ {
+ aMultiRange = rRange; // new
+ bMultiMarked = true;
+ }
+}
+
+void ScMarkData::SetAreaTab( SCTAB nTab )
+{
+ aMarkRange.aStart.SetTab(nTab);
+ aMarkRange.aEnd.SetTab(nTab);
+ aMultiRange.aStart.SetTab(nTab);
+ aMultiRange.aEnd.SetTab(nTab);
+}
+
+void ScMarkData::SelectTable( SCTAB nTab, bool bNew )
+{
+ if ( bNew )
+ {
+ maTabMarked.insert( nTab );
+ }
+ else
+ {
+ maTabMarked.erase( nTab );
+ }
+}
+
+bool ScMarkData::GetTableSelect( SCTAB nTab ) const
+{
+ return (maTabMarked.find( nTab ) != maTabMarked.end());
+}
+
+void ScMarkData::SelectOneTable( SCTAB nTab )
+{
+ maTabMarked.clear();
+ maTabMarked.insert( nTab );
+}
+
+SCTAB ScMarkData::GetSelectCount() const
+{
+ return static_cast<SCTAB> ( maTabMarked.size() );
+}
+
+SCTAB ScMarkData::GetFirstSelected() const
+{
+ if (!maTabMarked.empty())
+ return (*maTabMarked.begin());
+
+ OSL_FAIL("GetFirstSelected: nothing selected");
+ return 0;
+}
+
+SCTAB ScMarkData::GetLastSelected() const
+{
+ if (!maTabMarked.empty())
+ return (*maTabMarked.rbegin());
+
+ OSL_FAIL("GetLastSelected: nothing selected");
+ return 0;
+}
+
+void ScMarkData::SetSelectedTabs(const MarkedTabsType& rTabs)
+{
+ MarkedTabsType aTabs(rTabs.begin(), rTabs.end());
+ maTabMarked.swap(aTabs);
+}
+
+void ScMarkData::MarkToMulti()
+{
+ if ( bMarked && !bMarking )
+ {
+ SetMultiMarkArea( aMarkRange, !bMarkIsNeg );
+ bMarked = false;
+
+ // check if all multi mark ranges have been removed
+ if ( bMarkIsNeg && !HasAnyMultiMarks() )
+ ResetMark();
+ }
+}
+
+void ScMarkData::MarkToSimple()
+{
+ if ( bMarking )
+ return;
+
+ if ( bMultiMarked && bMarked )
+ MarkToMulti(); // may result in bMarked and bMultiMarked reset
+
+ if ( !bMultiMarked )
+ return;
+
+ ScRange aNew = aMultiRange;
+
+ bool bOk = false;
+ SCCOL nStartCol = aNew.aStart.Col();
+ SCCOL nEndCol = aNew.aEnd.Col();
+
+ while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nStartCol ) )
+ ++nStartCol;
+ while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nEndCol ) )
+ --nEndCol;
+
+ // Rows are only taken from MarkArray
+ SCROW nStartRow, nEndRow;
+ if ( aMultiSel.HasOneMark( nStartCol, nStartRow, nEndRow ) )
+ {
+ bOk = true;
+ SCROW nCmpStart, nCmpEnd;
+ for (SCCOL nCol=nStartCol+1; nCol<=nEndCol && bOk; nCol++)
+ if ( !aMultiSel.HasOneMark( nCol, nCmpStart, nCmpEnd )
+ || nCmpStart != nStartRow || nCmpEnd != nEndRow )
+ bOk = false;
+ }
+
+ if (bOk)
+ {
+ aNew.aStart.SetCol(nStartCol);
+ aNew.aStart.SetRow(nStartRow);
+ aNew.aEnd.SetCol(nEndCol);
+ aNew.aEnd.SetRow(nEndRow);
+
+ ResetMark();
+ aMarkRange = aNew;
+ bMarked = true;
+ bMarkIsNeg = false;
+ }
+}
+
+bool ScMarkData::IsCellMarked( SCCOL nCol, SCROW nRow, bool bNoSimple ) const
+{
+ if ( bMarked && !bNoSimple && !bMarkIsNeg )
+ if ( aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
+ aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
+ return true;
+
+ if (bMultiMarked)
+ {
+ //TODO: test here for negative Marking ?
+
+ return aMultiSel.GetMark( nCol, nRow );
+ }
+
+ return false;
+}
+
+bool ScMarkData::IsColumnMarked( SCCOL nCol ) const
+{
+ // bMarkIsNeg meanwhile also for columns heads
+ //TODO: GetMarkColumnRanges for completely marked column
+
+ if ( bMarked && !bMarkIsNeg &&
+ aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
+ aMarkRange.aStart.Row() == 0 && aMarkRange.aEnd.Row() == mrSheetLimits.mnMaxRow )
+ return true;
+
+ if ( bMultiMarked && aMultiSel.IsAllMarked( nCol, 0, mrSheetLimits.mnMaxRow ) )
+ return true;
+
+ return false;
+}
+
+bool ScMarkData::IsRowMarked( SCROW nRow ) const
+{
+ // bMarkIsNeg meanwhile also for row heads
+ //TODO: GetMarkRowRanges for completely marked rows
+
+ if ( bMarked && !bMarkIsNeg &&
+ aMarkRange.aStart.Col() == 0 && aMarkRange.aEnd.Col() == mrSheetLimits.mnMaxCol &&
+ aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
+ return true;
+
+ if ( bMultiMarked )
+ return aMultiSel.IsRowMarked( nRow );
+
+ return false;
+}
+
+void ScMarkData::MarkFromRangeList( const ScRangeList& rList, bool bReset )
+{
+ if (bReset)
+ {
+ maTabMarked.clear();
+ ResetMark();
+ }
+
+ size_t nCount = rList.size();
+ if ( nCount == 1 && !bMarked && !bMultiMarked )
+ {
+ const ScRange& rRange = rList[ 0 ];
+ SetMarkArea( rRange );
+ SelectTable( rRange.aStart.Tab(), true );
+ }
+ else
+ {
+ for (size_t i=0; i < nCount; i++)
+ {
+ const ScRange& rRange = rList[ i ];
+ SetMultiMarkArea( rRange );
+ SelectTable( rRange.aStart.Tab(), true );
+ }
+ }
+}
+
+/**
+ Optimise the case of constructing from a range list, speeds up import.
+*/
+ScMarkData::ScMarkData(const ScSheetLimits& rLimits, const ScRangeList& rList)
+ : aMultiSel(rLimits),
+ mrSheetLimits(rLimits)
+{
+ ResetMark();
+
+ for (const ScRange& rRange : rList)
+ maTabMarked.insert( rRange.aStart.Tab() );
+
+ if (rList.size() > 1)
+ {
+ bMultiMarked = true;
+ aMultiRange = rList.Combine();
+
+ aMultiSel.Set( rList );
+ }
+ else if (rList.size() == 1)
+ {
+ const ScRange& rRange = rList[ 0 ];
+ SetMarkArea( rRange );
+ }
+}
+
+
+void ScMarkData::FillRangeListWithMarks( ScRangeList* pList, bool bClear, SCTAB nForTab ) const
+{
+ if (!pList)
+ return;
+
+ if (bClear)
+ pList->RemoveAll();
+
+ //TODO: for multiple selected tables enter multiple ranges !!!
+
+ if ( bMultiMarked )
+ {
+ SCTAB nTab = (nForTab < 0 ? aMultiRange.aStart.Tab() : nForTab);
+
+ SCCOL nStartCol = aMultiRange.aStart.Col();
+ SCCOL nEndCol = aMultiRange.aEnd.Col();
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ if (aMultiSel.HasMarks( nCol ))
+ {
+ // Feeding column-wise fragments to ScRangeList::Join() is a
+ // huge bottleneck, speed this up for multiple columns
+ // consisting of identical row sets by building a column span
+ // first. This is usually the case for filtered data, for
+ // example.
+ SCCOL nToCol = nCol+1;
+ for ( ; nToCol <= nEndCol; ++nToCol)
+ {
+ if (!aMultiSel.HasEqualRowsMarked(nCol, nToCol))
+ break;
+ }
+ --nToCol;
+ ScRange aRange( nCol, 0, nTab, nToCol, 0, nTab );
+ SCROW nTop, nBottom;
+ ScMultiSelIter aMultiIter( aMultiSel, nCol );
+ while ( aMultiIter.Next( nTop, nBottom ) )
+ {
+ aRange.aStart.SetRow( nTop );
+ aRange.aEnd.SetRow( nBottom );
+ pList->Join( aRange );
+ }
+ nCol = nToCol;
+ }
+ }
+ }
+
+ if ( bMarked )
+ {
+ if (nForTab < 0)
+ pList->push_back( aMarkRange );
+ else
+ {
+ ScRange aRange( aMarkRange );
+ aRange.aStart.SetTab( nForTab );
+ aRange.aEnd.SetTab( nForTab );
+ pList->push_back( aRange );
+ }
+ }
+}
+
+void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const
+{
+ if (!pList)
+ return;
+
+ ScRangeList aOldList(*pList);
+ pList->RemoveAll(); //TODO: or skip the existing below
+
+ for (const auto& rTab : maTabMarked)
+ for ( size_t i=0, nCount = aOldList.size(); i<nCount; i++)
+ {
+ ScRange aRange = aOldList[ i ];
+ aRange.aStart.SetTab(rTab);
+ aRange.aEnd.SetTab(rTab);
+ pList->push_back( aRange );
+ }
+}
+
+ScRangeList ScMarkData::GetMarkedRanges() const
+{
+ ScRangeList aRet;
+ FillRangeListWithMarks(&aRet, false);
+ return aRet;
+}
+
+ScRangeList ScMarkData::GetMarkedRangesForTab( SCTAB nTab ) const
+{
+ ScRangeList aRet;
+ FillRangeListWithMarks(&aRet, false, nTab);
+ return aRet;
+}
+
+std::vector<sc::ColRowSpan> ScMarkData::GetMarkedRowSpans() const
+{
+ typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
+
+ ScRangeList aRanges = GetMarkedRanges();
+ SpansType aSpans(0, mrSheetLimits.mnMaxRow+1, false);
+ SpansType::const_iterator itPos = aSpans.begin();
+
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = aRanges[i];
+ itPos = aSpans.insert(itPos, r.aStart.Row(), r.aEnd.Row()+1, true).first;
+ }
+
+ return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
+}
+
+std::vector<sc::ColRowSpan> ScMarkData::GetMarkedColSpans() const
+{
+
+ if (bMultiMarked)
+ {
+ SCCOL nStartCol = aMultiRange.aStart.Col();
+ SCCOL nEndCol = aMultiRange.aEnd.Col();
+ if (bMarked)
+ {
+ // Use segment tree to merge marked with multi marked.
+ typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
+ SpansType aSpans(0, mrSheetLimits.mnMaxCol+1, false);
+ SpansType::const_iterator itPos = aSpans.begin();
+ do
+ {
+ if (aMultiSel.GetRowSelArray().HasMarks())
+ {
+ itPos = aSpans.insert(itPos, nStartCol, nEndCol+1, true).first;
+ break; // do; all columns marked
+ }
+
+ /* XXX if it turns out that span insert is too slow for lots of
+ * subsequent columns we could gather each span first and then
+ * insert. */
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
+ if (pMultiArray && pMultiArray->HasMarks())
+ itPos = aSpans.insert(itPos, nCol, nCol+1, true).first;
+ }
+ }
+ while(false);
+
+ // Merge marked.
+ aSpans.insert(itPos, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col()+1, true);
+
+ return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
+ }
+ else
+ {
+ // A plain vector is sufficient, avoid segment tree and conversion
+ // to vector overhead.
+ std::vector<sc::ColRowSpan> aVec;
+ if (aMultiSel.GetRowSelArray().HasMarks())
+ {
+ aVec.emplace_back( nStartCol, nEndCol);
+ return aVec; // all columns marked
+ }
+ sc::ColRowSpan aSpan( -1, -1);
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
+ if (pMultiArray && pMultiArray->HasMarks())
+ {
+ if (aSpan.mnStart == -1)
+ aSpan.mnStart = nCol;
+ aSpan.mnEnd = nCol;
+ }
+ else
+ {
+ // Add span gathered so far, if any.
+ if (aSpan.mnStart != -1)
+ {
+ aVec.push_back( aSpan);
+ aSpan.mnStart = -1;
+ }
+ }
+ }
+ // Add last span, if any.
+ if (aSpan.mnStart != -1)
+ aVec.push_back( aSpan);
+ return aVec;
+ }
+ }
+
+ // Only reached if not multi marked.
+ std::vector<sc::ColRowSpan> aVec;
+ if (bMarked)
+ {
+ aVec.emplace_back( aMarkRange.aStart.Col(), aMarkRange.aEnd.Col());
+ }
+ return aVec;
+}
+
+bool ScMarkData::IsAllMarked( const ScRange& rRange ) const
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+
+ if ( !bMultiMarked )
+ {
+ if ( bMarked && !bMarkIsNeg &&
+ aMarkRange.aStart.Col() <= nStartCol && aMarkRange.aEnd.Col() >= nEndCol &&
+ aMarkRange.aStart.Row() <= nStartRow && aMarkRange.aEnd.Row() >= nEndRow )
+ return true;
+ return false;
+ }
+
+ bool bOk = true;
+
+ if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
+ return aMultiSel.IsRowRangeMarked( nStartRow, nEndRow );
+
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol && bOk; nCol++)
+ if ( !aMultiSel.IsAllMarked( nCol, nStartRow, nEndRow ) )
+ bOk = false;
+
+ return bOk;
+}
+
+SCCOL ScMarkData::GetStartOfEqualColumns( SCCOL nLastCol, SCCOL nMinCol ) const
+{
+ if( !bMultiMarked )
+ {
+ if ( bMarked && !bMarkIsNeg )
+ {
+ if( aMarkRange.aEnd.Col() >= nMinCol && aMarkRange.aStart.Col() < nLastCol )
+ return aMarkRange.aEnd.Col() + 1;
+ if( aMarkRange.aEnd.Col() >= nLastCol && aMarkRange.aStart.Col() <= nMinCol )
+ return aMarkRange.aStart.Col();
+ }
+ return nMinCol;
+ }
+ return aMultiSel.GetStartOfEqualColumns( nLastCol, nMinCol );
+}
+
+SCROW ScMarkData::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
+{
+ if ( !bMultiMarked )
+ return nRow;
+
+ return aMultiSel.GetNextMarked( nCol, nRow, bUp );
+}
+
+bool ScMarkData::HasMultiMarks( SCCOL nCol ) const
+{
+ if ( !bMultiMarked )
+ return false;
+
+ return aMultiSel.HasMarks( nCol );
+}
+
+bool ScMarkData::HasAnyMultiMarks() const
+{
+ if ( !bMultiMarked )
+ return false;
+
+ return aMultiSel.HasAnyMarks();
+}
+
+void ScMarkData::InsertTab( SCTAB nTab )
+{
+ std::set<SCTAB> tabMarked;
+ for (const auto& rTab : maTabMarked)
+ {
+ if (rTab < nTab)
+ tabMarked.insert(rTab);
+ else
+ tabMarked.insert(rTab + 1);
+ }
+ maTabMarked.swap(tabMarked);
+}
+
+void ScMarkData::DeleteTab( SCTAB nTab )
+{
+ std::set<SCTAB> tabMarked;
+ for (const auto& rTab : maTabMarked)
+ {
+ if (rTab < nTab)
+ tabMarked.insert(rTab);
+ else if (rTab > nTab)
+ tabMarked.insert(rTab - 1);
+ }
+ maTabMarked.swap(tabMarked);
+}
+
+void ScMarkData::ShiftCols(const ScDocument& rDoc, SCCOL nStartCol, sal_Int32 nColOffset)
+{
+ if (bMarked)
+ {
+ aMarkRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
+ }
+ else if (bMultiMarked)
+ {
+ aMultiSel.ShiftCols(nStartCol, nColOffset);
+ aMultiRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
+ }
+}
+
+void ScMarkData::ShiftRows(const ScDocument& rDoc, SCROW nStartRow, sal_Int32 nRowOffset)
+{
+ if (bMarked)
+ {
+ aMarkRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
+ }
+ else if (bMultiMarked)
+ {
+ aMultiSel.ShiftRows(nStartRow, nRowOffset);
+ aMultiRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
+ }
+}
+
+static void lcl_AddRanges(ScRange& rRangeDest, const ScRange& rNewRange )
+{
+ SCCOL nStartCol = rNewRange.aStart.Col();
+ SCROW nStartRow = rNewRange.aStart.Row();
+ SCCOL nEndCol = rNewRange.aEnd.Col();
+ SCROW nEndRow = rNewRange.aEnd.Row();
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartCol, nEndCol );
+ if ( nStartCol < rRangeDest.aStart.Col() )
+ rRangeDest.aStart.SetCol( nStartCol );
+ if ( nStartRow < rRangeDest.aStart.Row() )
+ rRangeDest.aStart.SetRow( nStartRow );
+ if ( nEndCol > rRangeDest.aEnd.Col() )
+ rRangeDest.aEnd.SetCol( nEndCol );
+ if ( nEndRow > rRangeDest.aEnd.Row() )
+ rRangeDest.aEnd.SetRow( nEndRow );
+}
+
+void ScMarkData::GetSelectionCover( ScRange& rRange )
+{
+ if( bMultiMarked )
+ {
+ rRange = aMultiRange;
+ SCCOL nStartCol = aMultiRange.aStart.Col(), nEndCol = aMultiRange.aEnd.Col();
+ PutInOrder( nStartCol, nEndCol );
+ nStartCol = ( nStartCol == 0 ) ? nStartCol : nStartCol - 1;
+ nEndCol = ( nEndCol == mrSheetLimits.mnMaxCol ) ? nEndCol : nEndCol + 1;
+ std::unique_ptr<ScFlatBoolRowSegments> pPrevColMarkedRows;
+ std::unique_ptr<ScFlatBoolRowSegments> pCurColMarkedRows;
+ std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInTopEnvelope;
+ std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInBottomEnvelope;
+ ScFlatBoolRowSegments aNoRowsMarked(mrSheetLimits.mnMaxRow);
+ aNoRowsMarked.setFalse( 0, mrSheetLimits.mnMaxRow );
+
+ bool bPrevColUnMarked = false;
+
+ for ( SCCOL nCol=nStartCol; nCol <= nEndCol; nCol++ )
+ {
+ SCROW nTop, nBottom;
+ bool bCurColUnMarked = !aMultiSel.HasMarks( nCol );
+ if ( !bCurColUnMarked )
+ {
+ pCurColMarkedRows.reset( new ScFlatBoolRowSegments(mrSheetLimits.mnMaxRow) );
+ pCurColMarkedRows->setFalse( 0, mrSheetLimits.mnMaxRow );
+ ScMultiSelIter aMultiIter( aMultiSel, nCol );
+ ScFlatBoolRowSegments::ForwardIterator aPrevItr(
+ pPrevColMarkedRows ? *pPrevColMarkedRows
+ : aNoRowsMarked); // For finding left envelope
+ ScFlatBoolRowSegments::ForwardIterator aPrevItr1(
+ pPrevColMarkedRows ? *pPrevColMarkedRows
+ : aNoRowsMarked); // For finding right envelope
+ SCROW nTopPrev = 0, nBottomPrev = 0; // For right envelope
+ while ( aMultiIter.Next( nTop, nBottom ) )
+ {
+ pCurColMarkedRows->setTrue( nTop, nBottom );
+ if( bPrevColUnMarked && ( nCol > nStartCol ))
+ {
+ ScRange aAddRange(nCol - 1, nTop, aMultiRange.aStart.Tab(),
+ nCol - 1, nBottom, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Left envelope
+ aLeftEnvelope.push_back( aAddRange );
+ }
+ else if( nCol > nStartCol )
+ {
+ SCROW nTop1 = nTop, nBottom1 = nTop;
+ while( nTop1 <= nBottom && nBottom1 <= nBottom )
+ {
+ bool bRangeMarked = false;
+ const bool bHasValue = aPrevItr.getValue( nTop1, bRangeMarked );
+ assert(bHasValue); (void)bHasValue;
+ if( bRangeMarked )
+ {
+ nTop1 = aPrevItr.getLastPos() + 1;
+ nBottom1 = nTop1;
+ }
+ else
+ {
+ nBottom1 = aPrevItr.getLastPos();
+ if( nBottom1 > nBottom )
+ nBottom1 = nBottom;
+ ScRange aAddRange( nCol - 1, nTop1, aMultiRange.aStart.Tab(),
+ nCol - 1, nBottom1, aMultiRange.aStart.Tab() );
+ lcl_AddRanges( rRange, aAddRange ); // Left envelope
+ aLeftEnvelope.push_back( aAddRange );
+ nTop1 = ++nBottom1;
+ }
+ }
+ while( nTopPrev <= nBottom && nBottomPrev <= nBottom )
+ {
+ bool bRangeMarked;
+ const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
+ assert(bHasValue); (void)bHasValue;
+ if( bRangeMarked )
+ {
+ nBottomPrev = aPrevItr1.getLastPos();
+ if( nTopPrev < nTop )
+ {
+ if( nBottomPrev >= nTop )
+ {
+ nBottomPrev = nTop - 1;
+ ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
+ nCol, nBottomPrev, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Right envelope
+ aRightEnvelope.push_back( aAddRange );
+ nTopPrev = nBottomPrev = (nBottom + 1);
+ }
+ else
+ {
+ ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
+ nCol, nBottomPrev, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Right envelope
+ aRightEnvelope.push_back( aAddRange );
+ nTopPrev = ++nBottomPrev;
+ }
+ }
+ else
+ nTopPrev = nBottomPrev = ( nBottom + 1 );
+ }
+ else
+ {
+ nBottomPrev = aPrevItr1.getLastPos();
+ nTopPrev = ++nBottomPrev;
+ }
+ }
+ }
+ if( nTop )
+ {
+ ScRange aAddRange( nCol, nTop - 1, aMultiRange.aStart.Tab(),
+ nCol, nTop - 1, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Top envelope
+ auto it = aRowToColSegmentsInTopEnvelope.find(nTop - 1);
+ if (it == aRowToColSegmentsInTopEnvelope.end())
+ it = aRowToColSegmentsInTopEnvelope.emplace(nTop - 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
+ it->second.setTrue( nCol, nCol );
+ }
+ if( nBottom < mrSheetLimits.mnMaxRow )
+ {
+ ScRange aAddRange(nCol, nBottom + 1, aMultiRange.aStart.Tab(),
+ nCol, nBottom + 1, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Bottom envelope
+ auto it = aRowToColSegmentsInBottomEnvelope.find(nBottom + 1);
+ if (it == aRowToColSegmentsInBottomEnvelope.end())
+ it = aRowToColSegmentsInBottomEnvelope.emplace(nBottom + 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
+ it->second.setTrue( nCol, nCol );
+ }
+ }
+
+ while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow && ( nCol > nStartCol ) )
+ {
+ bool bRangeMarked;
+ const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
+ assert(bHasValue); (void)bHasValue;
+ if( bRangeMarked )
+ {
+ nBottomPrev = aPrevItr1.getLastPos();
+ ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
+ nCol, nBottomPrev, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Right envelope
+ aRightEnvelope.push_back( aAddRange );
+ nTopPrev = ++nBottomPrev;
+ }
+ else
+ {
+ nBottomPrev = aPrevItr1.getLastPos();
+ nTopPrev = ++nBottomPrev;
+ }
+ }
+ }
+ else if( nCol > nStartCol )
+ {
+ bPrevColUnMarked = true;
+ SCROW nTopPrev = 0, nBottomPrev = 0;
+ bool bRangeMarked = false;
+ ScFlatBoolRowSegments::ForwardIterator aPrevItr(
+ pPrevColMarkedRows ? *pPrevColMarkedRows : aNoRowsMarked);
+ while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow )
+ {
+ const bool bHasValue = aPrevItr.getValue(nTopPrev, bRangeMarked);
+ assert(bHasValue); (void)bHasValue;
+ if( bRangeMarked )
+ {
+ nBottomPrev = aPrevItr.getLastPos();
+ ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
+ nCol, nBottomPrev, aMultiRange.aStart.Tab());
+ lcl_AddRanges( rRange, aAddRange ); // Right envelope
+ aRightEnvelope.push_back( aAddRange );
+ nTopPrev = ++nBottomPrev;
+ }
+ else
+ {
+ nBottomPrev = aPrevItr.getLastPos();
+ nTopPrev = ++nBottomPrev;
+ }
+ }
+ }
+ if ( bCurColUnMarked )
+ pPrevColMarkedRows.reset();
+ else
+ pPrevColMarkedRows = std::move( pCurColMarkedRows );
+ }
+ for( auto& rKV : aRowToColSegmentsInTopEnvelope )
+ {
+ SCCOL nStart = nStartCol;
+ ScFlatBoolColSegments::RangeData aRange;
+ while( nStart <= nEndCol )
+ {
+ if( !rKV.second.getRangeData( nStart, aRange ) )
+ break;
+ if( aRange.mbValue ) // is marked
+ aTopEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
+ aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
+ nStart = aRange.mnCol2 + 1;
+ }
+ }
+ for( auto& rKV : aRowToColSegmentsInBottomEnvelope )
+ {
+ SCCOL nStart = nStartCol;
+ ScFlatBoolColSegments::RangeData aRange;
+ while( nStart <= nEndCol )
+ {
+ if( !rKV.second.getRangeData( nStart, aRange ) )
+ break;
+ if( aRange.mbValue ) // is marked
+ aBottomEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
+ aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
+ nStart = aRange.mnCol2 + 1;
+ }
+ }
+ }
+ else if( bMarked )
+ {
+ aMarkRange.PutInOrder();
+ SCROW nRow1, nRow2, nRow1New, nRow2New;
+ SCCOL nCol1, nCol2, nCol1New, nCol2New;
+ SCTAB nTab1, nTab2;
+ aMarkRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ nCol1New = nCol1;
+ nCol2New = nCol2;
+ nRow1New = nRow1;
+ nRow2New = nRow2;
+ // Each envelope will have zero or more ranges for single rectangle selection.
+ if( nCol1 > 0 )
+ {
+ aLeftEnvelope.push_back( ScRange( nCol1 - 1, nRow1, nTab1, nCol1 - 1, nRow2, nTab2 ) );
+ --nCol1New;
+ }
+ if( nRow1 > 0 )
+ {
+ aTopEnvelope.push_back( ScRange( nCol1, nRow1 - 1, nTab1, nCol2, nRow1 - 1, nTab2 ) );
+ --nRow1New;
+ }
+ if( nCol2 < mrSheetLimits.mnMaxCol )
+ {
+ aRightEnvelope.push_back( ScRange( nCol2 + 1, nRow1, nTab1, nCol2 + 1, nRow2, nTab2 ) );
+ ++nCol2New;
+ }
+ if( nRow2 < mrSheetLimits.mnMaxRow )
+ {
+ aBottomEnvelope.push_back( ScRange( nCol1, nRow2 + 1, nTab1, nCol2, nRow2 + 1, nTab2 ) );
+ ++nRow2New;
+ }
+ rRange = ScRange( nCol1New, nRow1New, nTab1, nCol2New, nRow2New, nTab2 );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/markmulti.cxx b/sc/source/core/data/markmulti.cxx
new file mode 100644
index 0000000000..4c92f5f25a
--- /dev/null
+++ b/sc/source/core/data/markmulti.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 <markmulti.hxx>
+#include <markarr.hxx>
+#include <rangelst.hxx>
+#include <segmenttree.hxx>
+#include <sheetlimits.hxx>
+
+#include <o3tl/safeint.hxx>
+
+#include <algorithm>
+
+ScMultiSel::ScMultiSel(const ScSheetLimits& rSheetLimits)
+ : aRowSel(rSheetLimits), mrSheetLimits(rSheetLimits)
+{
+}
+
+ScMultiSel& ScMultiSel::operator=(const ScMultiSel& rOther)
+{
+ aMultiSelContainer = rOther.aMultiSelContainer;
+ aRowSel = rOther.aRowSel;
+ return *this;
+}
+
+ScMultiSel& ScMultiSel::operator=(ScMultiSel&& rOther)
+{
+ aMultiSelContainer = std::move(rOther.aMultiSelContainer);
+ aRowSel = std::move(rOther.aRowSel);
+ return *this;
+}
+
+
+void ScMultiSel::Clear()
+{
+ aMultiSelContainer.clear();
+ aRowSel.Reset();
+}
+
+SCCOL ScMultiSel::GetMultiSelectionCount() const
+{
+ SCCOL nCount = 0;
+ for (const auto & i : aMultiSelContainer)
+ if (i.HasMarks())
+ ++nCount;
+ return nCount;
+}
+
+bool ScMultiSel::HasMarks( SCCOL nCol ) const
+{
+ if ( aRowSel.HasMarks() )
+ return true;
+ return nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].HasMarks();
+}
+
+bool ScMultiSel::HasOneMark( SCCOL nCol, SCROW& rStartRow, SCROW& rEndRow ) const
+{
+ SCROW nRow1 = -1, nRow2 = -1, nRow3 = -1, nRow4 = -1;
+ bool aResult1 = aRowSel.HasOneMark( nRow1, nRow2 );
+ bool aResult2 = nCol < static_cast<SCCOL>(aMultiSelContainer.size())
+ && aMultiSelContainer[nCol].HasOneMark( nRow3, nRow4 );
+
+ if ( aResult1 || aResult2 )
+ {
+ if ( aResult1 && aResult2 )
+ {
+ if ( ( nRow2 + 1 ) < nRow3 )
+ return false;
+ if ( ( nRow4 + 1 ) < nRow1 )
+ return false;
+
+ auto aRows = std::minmax( { nRow1, nRow2, nRow3, nRow4 } );
+ rStartRow = aRows.first;
+ rEndRow = aRows.second;
+ return true;
+ }
+ if ( aResult1 )
+ {
+ rStartRow = nRow1;
+ rEndRow = nRow2;
+ return true;
+ }
+
+ rStartRow = nRow3;
+ rEndRow = nRow4;
+ return true;
+ }
+
+ return false;
+}
+
+bool ScMultiSel::GetMark( SCCOL nCol, SCROW nRow ) const
+{
+ if ( aRowSel.GetMark( nRow ) )
+ return true;
+ return nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].GetMark(nRow);
+}
+
+bool ScMultiSel::IsAllMarked( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
+{
+ bool bHasMarks1 = aRowSel.HasMarks();
+ bool bHasMarks2 = nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].HasMarks();
+
+ if ( !bHasMarks1 && !bHasMarks2 )
+ return false;
+
+ if ( bHasMarks1 && bHasMarks2 )
+ {
+ if ( aRowSel.IsAllMarked( nStartRow, nEndRow ) ||
+ aMultiSelContainer[nCol].IsAllMarked( nStartRow, nEndRow ) )
+ return true;
+ ScMultiSelIter aMultiIter( *this, nCol );
+ ScFlatBoolRowSegments::RangeData aRowRange;
+ bool bRet = aMultiIter.GetRangeData( nStartRow, aRowRange );
+ return bRet && aRowRange.mbValue && aRowRange.mnRow2 >= nEndRow;
+ }
+
+ if ( bHasMarks1 )
+ return aRowSel.IsAllMarked( nStartRow, nEndRow );
+
+ return aMultiSelContainer[nCol].IsAllMarked( nStartRow, nEndRow );
+}
+
+bool ScMultiSel::HasEqualRowsMarked( SCCOL nCol1, SCCOL nCol2 ) const
+{
+ bool bCol1Exists = nCol1 < static_cast<SCCOL>(aMultiSelContainer.size());
+ bool bCol2Exists = nCol2 < static_cast<SCCOL>(aMultiSelContainer.size());
+ if ( bCol1Exists || bCol2Exists )
+ {
+ if ( bCol1Exists && bCol2Exists )
+ return aMultiSelContainer[nCol1] == aMultiSelContainer[nCol2];
+ else if ( bCol1Exists )
+ return !aMultiSelContainer[nCol1].HasMarks();
+ else
+ return !aMultiSelContainer[nCol2].HasMarks();
+ }
+
+ return true;
+}
+
+SCCOL ScMultiSel::GetStartOfEqualColumns( SCCOL nLastCol, SCCOL nMinCol ) const
+{
+ if( nMinCol > nLastCol )
+ return nMinCol;
+ if( nLastCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
+ {
+ if( nMinCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
+ return nMinCol;
+ SCCOL nCol = static_cast<SCCOL>(aMultiSelContainer.size()) - 1;
+ while( nCol >= nMinCol && aMultiSelContainer[nCol] == aRowSel )
+ --nCol;
+ return nCol + 1;
+ }
+ SCCOL nCol = nLastCol - 1;
+ while( nCol >= nMinCol && aMultiSelContainer[nCol] == aMultiSelContainer[nLastCol] )
+ --nCol;
+ return nCol + 1;
+}
+
+SCROW ScMultiSel::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
+{
+ if ( nCol >= static_cast<SCCOL>(aMultiSelContainer.size()) || !aMultiSelContainer[nCol].HasMarks() )
+ return aRowSel.GetNextMarked( nRow, bUp );
+
+ SCROW nRow1, nRow2;
+ nRow1 = aRowSel.GetNextMarked( nRow, bUp );
+ nRow2 = aMultiSelContainer[nCol].GetNextMarked( nRow, bUp );
+ if ( nRow1 == nRow2 )
+ return nRow1;
+ if ( nRow1 == -1 )
+ return nRow2;
+ if ( nRow2 == -1 )
+ return nRow1;
+
+ PutInOrder( nRow1, nRow2 );
+ return ( bUp ? nRow2 : nRow1 );
+}
+
+void ScMultiSel::MarkAllCols( SCROW nStartRow, SCROW nEndRow )
+{
+ aMultiSelContainer.resize(mrSheetLimits.mnMaxCol+1, ScMarkArray(mrSheetLimits));
+ for ( SCCOL nCol = mrSheetLimits.mnMaxCol; nCol >= 0; --nCol )
+ {
+ aMultiSelContainer[nCol].SetMarkArea( nStartRow, nEndRow, true );
+ }
+}
+
+void ScMultiSel::SetMarkArea( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow, bool bMark )
+{
+ if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
+ {
+ aRowSel.SetMarkArea( nStartRow, nEndRow, bMark );
+ if ( !bMark )
+ {
+ // Remove any per column marks for the row range.
+ for ( auto& aIter : aMultiSelContainer )
+ if ( aIter.HasMarks() )
+ aIter.SetMarkArea( nStartRow, nEndRow, false );
+ }
+ return;
+ }
+
+ // Bad case - we need to extend aMultiSelContainer size to MAXCOL
+ // and move row marks from aRowSel to aMultiSelContainer
+ if ( !bMark && aRowSel.HasMarks() )
+ {
+ SCROW nBeg, nLast = nEndRow;
+ if ( aRowSel.GetMark( nStartRow ) )
+ {
+ nBeg = nStartRow;
+ nLast = aRowSel.GetMarkEnd( nStartRow, false );
+ }
+ else
+ {
+ nBeg = aRowSel.GetNextMarked( nStartRow, false );
+ if ( nBeg != mrSheetLimits.GetMaxRowCount() )
+ nLast = aRowSel.GetMarkEnd( nBeg, false );
+ }
+
+ if ( nBeg != mrSheetLimits.GetMaxRowCount() && nLast >= nEndRow && nBeg <= nEndRow )
+ MarkAllCols( nBeg, nEndRow );
+ else
+ {
+ while ( nBeg != mrSheetLimits.GetMaxRowCount() && nLast < nEndRow )
+ {
+ MarkAllCols( nBeg, nLast );
+ nBeg = aRowSel.GetNextMarked( nLast + 1, false );
+ if ( nBeg != mrSheetLimits.GetMaxRowCount() )
+ nLast = aRowSel.GetMarkEnd( nBeg, false );
+ }
+ if ( nBeg != mrSheetLimits.GetMaxRowCount() && nLast >= nEndRow && nBeg <= nEndRow )
+ MarkAllCols( nBeg, nEndRow );
+ }
+
+ aRowSel.SetMarkArea( nStartRow, nEndRow, false );
+ }
+
+ if (nEndCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
+ aMultiSelContainer.resize(nEndCol+1, ScMarkArray(mrSheetLimits));
+ for ( SCCOL nColIter = nEndCol; nColIter >= nStartCol; --nColIter )
+ aMultiSelContainer[nColIter].SetMarkArea( nStartRow, nEndRow, bMark );
+}
+
+/**
+ optimised init-from-range-list. Specifically this is optimised for cases
+ where we have very large data columns with lots and lots of ranges.
+*/
+void ScMultiSel::Set( ScRangeList const & rList )
+{
+ Clear();
+ if (rList.size() == 0)
+ return;
+
+ // sort by row to make the combining/merging faster
+ auto aNewList = rList;
+ std::sort(aNewList.begin(), aNewList.end(),
+ [](const ScRange& lhs, const ScRange& rhs)
+ {
+ return lhs.aStart.Row() < rhs.aStart.Row();
+ });
+
+ std::vector<std::vector<ScMarkEntry>> aMarkEntriesPerCol(mrSheetLimits.mnMaxCol+1);
+
+ SCCOL nMaxCol = -1;
+ for (const ScRange& rRange : aNewList)
+ {
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ assert( nEndRow >= nStartRow && "this method assumes the input data has ranges with endrow>=startrow");
+ assert( nEndCol >= nStartCol && "this method assumes the input data has ranges with endcol>=startcol");
+ if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
+ aRowSel.SetMarkArea( nStartRow, nEndRow, /*bMark*/true );
+ else
+ {
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ auto & rMarkEntries = aMarkEntriesPerCol[nCol];
+ int nEntries = rMarkEntries.size();
+ if (nEntries > 1 && nStartRow >= rMarkEntries[nEntries-2].nRow+1
+ && nStartRow <= rMarkEntries[nEntries-1].nRow+1)
+ {
+ // overlaps or directly adjacent previous range
+ rMarkEntries.back().nRow = std::max(nEndRow, rMarkEntries.back().nRow);
+ }
+ else
+ {
+ // new range
+ if (nStartRow > 0)
+ rMarkEntries.emplace_back(ScMarkEntry{nStartRow-1, false});
+ rMarkEntries.emplace_back(ScMarkEntry{nEndRow, true});
+ }
+ }
+ nMaxCol = std::max(nMaxCol, nEndCol);
+ }
+ }
+
+ aMultiSelContainer.resize(nMaxCol+1, ScMarkArray(mrSheetLimits));
+ for (SCCOL nCol = 0; nCol<=nMaxCol; ++nCol)
+ if (!aMarkEntriesPerCol[nCol].empty())
+ aMultiSelContainer[nCol].Set( std::move(aMarkEntriesPerCol[nCol]) );
+}
+
+bool ScMultiSel::IsRowMarked( SCROW nRow ) const
+{
+ return aRowSel.GetMark( nRow );
+}
+
+bool ScMultiSel::IsRowRangeMarked( SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( !aRowSel.GetMark( nStartRow ) )
+ return false;
+ SCROW nLast = aRowSel.GetMarkEnd( nStartRow, false );
+ return ( nLast >= nEndRow );
+}
+
+ScMarkArray ScMultiSel::GetMarkArray( SCCOL nCol ) const
+{
+ ScMultiSelIter aMultiIter( *this, nCol );
+ ScMarkArray aMarkArray(mrSheetLimits);
+ SCROW nTop, nBottom;
+ while( aMultiIter.Next( nTop, nBottom ) )
+ aMarkArray.SetMarkArea( nTop, nBottom, true );
+ return aMarkArray;
+}
+
+bool ScMultiSel::HasAnyMarks() const
+{
+ if ( aRowSel.HasMarks() )
+ return true;
+ for ( const auto& aPair : aMultiSelContainer )
+ if ( aPair.HasMarks() )
+ return true;
+ return false;
+}
+
+void ScMultiSel::ShiftCols(SCCOL nStartCol, sal_Int32 nColOffset)
+{
+ if (nStartCol > mrSheetLimits.mnMaxCol)
+ return;
+
+ ScMultiSel aNewMultiSel(*this);
+ Clear();
+
+ if (nColOffset < 0)
+ {
+ // columns that would be moved on the left of nStartCol must be removed
+ const SCCOL nEndPos = std::min<SCCOL>(aNewMultiSel.aMultiSelContainer.size(), nStartCol - nColOffset);
+ for (SCCOL nSearchPos = nStartCol; nSearchPos < nEndPos; ++nSearchPos)
+ aNewMultiSel.aMultiSelContainer[nSearchPos].Reset();
+ }
+
+ SCCOL nCol = 0;
+ for (const auto& aSourceArray : aNewMultiSel.aMultiSelContainer)
+ {
+ SCCOL nDestCol = nCol;
+ if (nDestCol >= nStartCol)
+ {
+ nDestCol += nColOffset;
+ if (nDestCol < 0)
+ nDestCol = 0;
+ else if (nDestCol > mrSheetLimits.mnMaxCol)
+ nDestCol = mrSheetLimits.mnMaxCol;
+ }
+ if (nDestCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
+ aMultiSelContainer.resize(nDestCol, ScMarkArray(mrSheetLimits));
+ aMultiSelContainer[nDestCol] = aSourceArray;
+ ++nCol;
+ }
+ aRowSel = aNewMultiSel.aRowSel;
+
+ if (!(nColOffset > 0 && nStartCol > 0 && o3tl::make_unsigned(nStartCol) < aNewMultiSel.aMultiSelContainer.size()))
+ return;
+
+ // insert nColOffset new columns, and select their cells if they are selected
+ // both in the old column at nStartPos and in the previous column
+ auto& rPrevPos = aNewMultiSel.aMultiSelContainer[nStartCol - 1];
+ auto& rStartPos = aNewMultiSel.aMultiSelContainer[nStartCol];
+ auto& rNewCol = aMultiSelContainer[nStartCol];
+ rNewCol = rStartPos;
+ rNewCol.Intersect(rPrevPos);
+ if (nStartCol + nColOffset >= static_cast<SCCOL>(aNewMultiSel.aMultiSelContainer.size()))
+ aNewMultiSel.aMultiSelContainer.resize(nStartCol + nColOffset, ScMarkArray(mrSheetLimits));
+ for (tools::Long i = 1; i < nColOffset; ++i)
+ aMultiSelContainer[nStartCol + i] = rNewCol;
+}
+
+void ScMultiSel::ShiftRows(SCROW nStartRow, sal_Int32 nRowOffset)
+{
+ for (auto& aPair: aMultiSelContainer)
+ aPair.Shift(nStartRow, nRowOffset);
+ aRowSel.Shift(nStartRow, nRowOffset);
+}
+
+const ScMarkArray* ScMultiSel::GetMultiSelArray( SCCOL nCol ) const
+{
+ if (nCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
+ return nullptr;
+ return &aMultiSelContainer[nCol];
+}
+
+ScMultiSelIter::ScMultiSelIter( const ScMultiSel& rMultiSel, SCCOL nCol ) :
+ aMarkArrayIter(nullptr),
+ nNextSegmentStart(0)
+{
+ bool bHasMarks1 = rMultiSel.aRowSel.HasMarks();
+ bool bHasMarks2 = nCol < static_cast<SCCOL>(rMultiSel.aMultiSelContainer.size())
+ && rMultiSel.aMultiSelContainer[nCol].HasMarks();
+
+ if (bHasMarks1 && bHasMarks2)
+ {
+ pRowSegs.reset( new ScFlatBoolRowSegments(rMultiSel.mrSheetLimits.mnMaxRow) );
+ pRowSegs->setFalse( 0, rMultiSel.mrSheetLimits.mnMaxRow );
+ {
+ ScMarkArrayIter aMarkIter( &rMultiSel.aRowSel );
+ SCROW nTop, nBottom;
+ while ( aMarkIter.Next( nTop, nBottom ) )
+ pRowSegs->setTrue( nTop, nBottom );
+ }
+
+ {
+ ScMarkArrayIter aMarkIter( &rMultiSel.aMultiSelContainer[nCol] );
+ SCROW nTop, nBottom;
+ while ( aMarkIter.Next( nTop, nBottom ) )
+ pRowSegs->setTrue( nTop, nBottom );
+ }
+ }
+ else if (bHasMarks1)
+ {
+ aMarkArrayIter.reset( &rMultiSel.aRowSel);
+ }
+ else if (bHasMarks2)
+ {
+ aMarkArrayIter.reset( &rMultiSel.aMultiSelContainer[nCol]);
+ }
+}
+
+bool ScMultiSelIter::Next( SCROW& rTop, SCROW& rBottom )
+{
+ if (pRowSegs)
+ {
+ ScFlatBoolRowSegments::RangeData aRowRange;
+ bool bRet = pRowSegs->getRangeData( nNextSegmentStart, aRowRange );
+ if ( bRet && !aRowRange.mbValue )
+ {
+ nNextSegmentStart = aRowRange.mnRow2 + 1;
+ bRet = pRowSegs->getRangeData( nNextSegmentStart, aRowRange );
+ }
+ if ( bRet )
+ {
+ rTop = aRowRange.mnRow1;
+ rBottom = aRowRange.mnRow2;
+ nNextSegmentStart = rBottom + 1;
+ }
+ return bRet;
+ }
+
+ return aMarkArrayIter.Next( rTop, rBottom);
+}
+
+bool ScMultiSelIter::GetRangeData( SCROW nRow, ScFlatBoolRowSegments::RangeData& rRowRange ) const
+{
+ assert(pRowSegs);
+ return pRowSegs->getRangeData( nRow, rRowRange);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/mtvcellfunc.cxx b/sc/source/core/data/mtvcellfunc.cxx
new file mode 100644
index 0000000000..98f6998cc1
--- /dev/null
+++ b/sc/source/core/data/mtvcellfunc.cxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <mtvcellfunc.hxx>
+
+namespace sc {
+
+CellStoreType::iterator ProcessFormula(
+ const CellStoreType::iterator& it, CellStoreType& rStore, SCROW nRow1, SCROW nRow2,
+ std::function<void(size_t,ScFormulaCell*)> aFuncElem )
+{
+ using FuncType = std::function<void(size_t,ScFormulaCell*)>;
+ using ElseFuncType = std::function<void(mdds::mtv::element_t, size_t, size_t)>;
+
+ // empty function for handling the 'else' part.
+ static ElseFuncType aFuncElse =
+ [](mdds::mtv::element_t,size_t,size_t) {};
+
+ return ProcessElements1<
+ CellStoreType, formula_block,
+ FuncType, ElseFuncType>(
+ it, rStore, nRow1, nRow2, aFuncElem, aFuncElse);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/mtvelements.cxx b/sc/source/core/data/mtvelements.cxx
new file mode 100644
index 0000000000..9bc28056bd
--- /dev/null
+++ b/sc/source/core/data/mtvelements.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/.
+ */
+
+#include <mtvelements.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <column.hxx>
+#include <table.hxx>
+
+#include <sstream>
+
+namespace sc {
+
+CellStoreEvent::CellStoreEvent() : mpCol(nullptr) {}
+
+CellStoreEvent::CellStoreEvent(ScColumn* pCol) : mpCol(pCol) {}
+
+void CellStoreEvent::element_block_acquired(const mdds::mtv::base_element_block* block)
+{
+ if (!mpCol)
+ return;
+
+ switch (mdds::mtv::get_block_type(*block))
+ {
+ case sc::element_type_formula:
+ ++mpCol->mnBlkCountFormula;
+ break;
+ default:
+ ;
+ }
+}
+
+void CellStoreEvent::element_block_released(const mdds::mtv::base_element_block* block)
+{
+ if (!mpCol)
+ return;
+
+ switch (mdds::mtv::get_block_type(*block))
+ {
+ case sc::element_type_formula:
+ --mpCol->mnBlkCountFormula;
+ break;
+ default:
+ ;
+ }
+}
+
+void CellStoreEvent::stop()
+{
+ mpCol = nullptr;
+}
+
+void CellStoreEvent::swap(CellStoreEvent& other)
+{
+ std::swap(mpCol, other.mpCol);
+}
+
+const ScColumn* CellStoreEvent::getColumn() const
+{
+ return mpCol;
+}
+
+ColumnBlockPositionSet::ColumnBlockPositionSet(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+ColumnBlockPosition* ColumnBlockPositionSet::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+ std::scoped_lock aGuard(maMtxTables);
+
+ TablesType::iterator itTab = maTables.find(nTab);
+ if (itTab == maTables.end())
+ {
+ std::pair<TablesType::iterator,bool> r =
+ maTables.emplace(nTab, ColumnsType());
+ if (!r.second)
+ // insertion failed.
+ return nullptr;
+
+ itTab = r.first;
+ }
+
+ ColumnsType& rCols = itTab->second;
+
+ ColumnsType::iterator it = rCols.find(nCol);
+ if (it != rCols.end())
+ // Block position for this column has already been fetched.
+ return &it->second;
+
+ std::pair<ColumnsType::iterator,bool> r =
+ rCols.emplace(nCol, ColumnBlockPosition());
+
+ if (!r.second)
+ // insertion failed.
+ return nullptr;
+
+ it = r.first;
+
+ if (!mrDoc.InitColumnBlockPosition(it->second, nTab, nCol))
+ return nullptr;
+
+ return &it->second;
+}
+
+void ColumnBlockPositionSet::clear()
+{
+ std::scoped_lock aGuard(maMtxTables);
+ maTables.clear();
+}
+
+struct TableColumnBlockPositionSet::Impl
+{
+ typedef std::unordered_map<SCCOL, ColumnBlockPosition> ColumnsType;
+
+ ScTable* mpTab;
+ ColumnsType maColumns;
+
+ Impl() : mpTab(nullptr) {}
+};
+
+TableColumnBlockPositionSet::TableColumnBlockPositionSet( ScDocument& rDoc, SCTAB nTab ) :
+ mpImpl(std::make_unique<Impl>())
+{
+ mpImpl->mpTab = rDoc.FetchTable(nTab);
+
+ if (!mpImpl->mpTab)
+ {
+ std::ostringstream os;
+ os << "Passed table index " << nTab << " is invalid.";
+ throw std::invalid_argument(os.str());
+ }
+}
+
+TableColumnBlockPositionSet::TableColumnBlockPositionSet( TableColumnBlockPositionSet&& rOther ) noexcept :
+ mpImpl(std::move(rOther.mpImpl)) {}
+
+TableColumnBlockPositionSet::~TableColumnBlockPositionSet() {}
+
+ColumnBlockPosition* TableColumnBlockPositionSet::getBlockPosition( SCCOL nCol )
+{
+ using ColumnsType = Impl::ColumnsType;
+
+ ColumnsType::iterator it = mpImpl->maColumns.find(nCol);
+
+ if (it != mpImpl->maColumns.end())
+ // Block position for this column has already been fetched.
+ return &it->second;
+
+ std::pair<ColumnsType::iterator,bool> r =
+ mpImpl->maColumns.emplace(nCol, ColumnBlockPosition());
+
+ if (!r.second)
+ // insertion failed.
+ return nullptr;
+
+ it = r.first;
+
+ if (!mpImpl->mpTab->InitColumnBlockPosition(it->second, nCol))
+ return nullptr;
+
+ return &it->second;
+}
+
+void TableColumnBlockPositionSet::invalidate()
+{
+ mpImpl->maColumns.clear();
+}
+
+ScRefCellValue toRefCell( const sc::CellStoreType::const_iterator& itPos, size_t nOffset )
+{
+ switch (itPos->type)
+ {
+ case sc::element_type_numeric:
+ // Numeric cell
+ return ScRefCellValue(sc::numeric_block::get_value(*itPos->data, nOffset));
+ case sc::element_type_string:
+ // String cell
+ return ScRefCellValue(&sc::string_block::at(*itPos->data, nOffset));
+ case sc::element_type_edittext:
+ // Edit cell
+ return ScRefCellValue(sc::edittext_block::get_value(*itPos->data, nOffset));
+ case sc::element_type_formula:
+ // Formula cell
+ return ScRefCellValue(sc::formula_block::get_value(*itPos->data, nOffset));
+ default:
+ ;
+ }
+
+ return ScRefCellValue();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/olinetab.cxx b/sc/source/core/data/olinetab.cxx
new file mode 100644
index 0000000000..13fc17a7b3
--- /dev/null
+++ b/sc/source/core/data/olinetab.cxx
@@ -0,0 +1,873 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <olinetab.hxx>
+#include <address.hxx>
+#include <table.hxx>
+
+#include <osl/diagnose.h>
+
+ScOutlineEntry::ScOutlineEntry( SCCOLROW nNewStart, SCCOLROW nNewSize, bool bNewHidden ) :
+ nStart ( nNewStart ),
+ nSize ( nNewSize ),
+ bHidden ( bNewHidden ),
+ bVisible( true )
+{
+}
+
+ScOutlineEntry::ScOutlineEntry( const ScOutlineEntry& rEntry ) :
+ nStart ( rEntry.nStart ),
+ nSize ( rEntry.nSize ),
+ bHidden ( rEntry.bHidden ),
+ bVisible( rEntry.bVisible )
+{
+}
+
+SCCOLROW ScOutlineEntry::GetEnd() const
+{
+ return nStart+nSize-1;
+}
+
+void ScOutlineEntry::Move( SCCOLROW nDelta )
+{
+ SCCOLROW nNewPos = nStart + nDelta;
+ if (nNewPos<0)
+ {
+ OSL_FAIL("OutlineEntry < 0");
+ nNewPos = 0;
+ }
+ nStart = nNewPos;
+}
+
+void ScOutlineEntry::SetSize( SCSIZE nNewSize )
+{
+ if (nNewSize>0)
+ nSize = nNewSize;
+ else
+ {
+ OSL_FAIL("ScOutlineEntry Size == 0");
+ }
+}
+
+void ScOutlineEntry::SetPosSize( SCCOLROW nNewPos, SCSIZE nNewSize )
+{
+ nStart = nNewPos;
+ SetSize( nNewSize );
+}
+
+void ScOutlineEntry::SetHidden( bool bNewHidden )
+{
+ bHidden = bNewHidden;
+}
+
+void ScOutlineEntry::SetVisible( bool bNewVisible )
+{
+ bVisible = bNewVisible;
+}
+
+OString ScOutlineEntry::dumpAsString() const
+{
+ const char* const pSep = ":";
+ return OString::number(nStart) + pSep + OString::number(nSize) +
+ pSep + (bHidden ? "1" : "0") + pSep + (bVisible ? "1" : "0");
+}
+
+ScOutlineCollection::ScOutlineCollection() {}
+
+size_t ScOutlineCollection::size() const
+{
+ return m_Entries.size();
+}
+
+void ScOutlineCollection::clear()
+{
+ m_Entries.clear();
+}
+
+void ScOutlineCollection::insert(ScOutlineEntry const& rEntry)
+{
+ SCCOLROW nStart = rEntry.GetStart();
+ m_Entries.insert(std::make_pair(nStart, rEntry));
+}
+
+ScOutlineCollection::iterator ScOutlineCollection::begin()
+{
+ return m_Entries.begin();
+}
+
+ScOutlineCollection::iterator ScOutlineCollection::end()
+{
+ return m_Entries.end();
+}
+
+ScOutlineCollection::const_iterator ScOutlineCollection::begin() const
+{
+ return m_Entries.begin();
+}
+
+ScOutlineCollection::const_iterator ScOutlineCollection::end() const
+{
+ return m_Entries.end();
+}
+
+ScOutlineCollection::iterator ScOutlineCollection::erase(const iterator& pos)
+{
+ return m_Entries.erase(pos);
+}
+
+bool ScOutlineCollection::empty() const
+{
+ return m_Entries.empty();
+}
+
+ScOutlineCollection::iterator ScOutlineCollection::FindStart(SCCOLROW nMinStart)
+{
+ return m_Entries.lower_bound(nMinStart);
+}
+
+OString ScOutlineCollection::dumpAsString() const
+{
+ OString aOutput;
+ const char* const pGroupEntrySep = ",";
+ for (const auto& rKeyValuePair : m_Entries)
+ aOutput += rKeyValuePair.second.dumpAsString() + pGroupEntrySep;
+
+ return aOutput;
+}
+
+ScOutlineArray::ScOutlineArray() :
+ nDepth(0) {}
+
+ScOutlineArray::ScOutlineArray( const ScOutlineArray& rArray ) :
+ nDepth( rArray.nDepth )
+{
+ for (size_t nLevel = 0; nLevel < nDepth; ++nLevel)
+ {
+ const ScOutlineCollection& rColl = rArray.aCollections[nLevel];
+ for (const auto& rEntry : rColl)
+ {
+ const ScOutlineEntry *const pEntry = &rEntry.second;
+ aCollections[nLevel].insert(*pEntry);
+ }
+ }
+}
+
+void ScOutlineArray::FindEntry(
+ SCCOLROW nSearchPos, size_t& rFindLevel, size_t& rFindIndex,
+ size_t nMaxLevel )
+{
+ rFindLevel = rFindIndex = 0;
+
+ if (nMaxLevel > nDepth)
+ nMaxLevel = nDepth;
+
+ for (size_t nLevel = 0; nLevel < nMaxLevel; ++nLevel) //TODO: Search backwards?
+ {
+ ScOutlineCollection* pCollect = &aCollections[nLevel];
+ size_t nIndex = 0;
+ for (auto& rEntry : *pCollect)
+ {
+ ScOutlineEntry *const pEntry = &rEntry.second;
+ if (pEntry->GetStart() <= nSearchPos && pEntry->GetEnd() >= nSearchPos)
+ {
+ rFindLevel = nLevel + 1; // Next Level (for insertion)
+ rFindIndex = nIndex;
+ }
+ ++nIndex;
+ }
+ }
+}
+
+bool ScOutlineArray::Insert(
+ SCCOLROW nStartCol, SCCOLROW nEndCol, bool& rSizeChanged, bool bHidden )
+{
+ rSizeChanged = false;
+
+ size_t nStartLevel, nEndLevel, nStartIndex, nEndIndex;
+ bool bFound = false;
+
+ bool bCont;
+ sal_uInt16 nFindMax;
+ FindEntry( nStartCol, nStartLevel, nStartIndex ); // nLevel = new Level (old+1)
+ FindEntry( nEndCol, nEndLevel, nEndIndex );
+ nFindMax = std::max(nStartLevel,nEndLevel);
+ do
+ {
+ bCont = false;
+
+ if (nStartLevel == nEndLevel && nStartIndex == nEndIndex && nStartLevel < SC_OL_MAXDEPTH)
+ bFound = true;
+
+ if (!bFound && nFindMax>0)
+ {
+ --nFindMax;
+ if (nStartLevel)
+ {
+ ScOutlineCollection::const_iterator it = aCollections[nStartLevel-1].begin();
+ std::advance(it, nStartIndex);
+ if (it->second.GetStart() == nStartCol)
+ FindEntry(nStartCol, nStartLevel, nStartIndex, nFindMax);
+ }
+
+ if (nEndLevel)
+ {
+ ScOutlineCollection::const_iterator it = aCollections[nEndLevel-1].begin();
+ std::advance(it, nEndIndex);
+ if (it->second.GetEnd() == nEndCol)
+ FindEntry(nEndCol, nEndLevel, nEndIndex, nFindMax);
+ }
+ bCont = true;
+ }
+ }
+ while ( !bFound && bCont );
+
+ if (!bFound)
+ return false;
+
+ size_t nLevel = nStartLevel;
+
+ // Move the ones underneath
+ bool bNeedSize = false;
+ if (nDepth > 0)
+ {
+ for (size_t nMoveLevel = nDepth-1; nMoveLevel >= nLevel; --nMoveLevel)
+ {
+ ScOutlineCollection& rColl = aCollections[nMoveLevel];
+ ScOutlineCollection::iterator it = rColl.begin(), itEnd = rColl.end();
+ while (it != itEnd)
+ {
+ ScOutlineEntry *const pEntry = &it->second;
+ SCCOLROW nEntryStart = pEntry->GetStart();
+ if (nEntryStart >= nStartCol && nEntryStart <= nEndCol)
+ {
+ if (nMoveLevel >= SC_OL_MAXDEPTH - 1)
+ {
+ rSizeChanged = false; // No more room
+ return false;
+ }
+ aCollections[nMoveLevel+1].insert(*pEntry);
+ it = rColl.erase(it);
+ if (nMoveLevel == nDepth - 1)
+ bNeedSize = true;
+ }
+ else
+ ++it;
+ }
+ if (nMoveLevel == 0)
+ break;
+ }
+ }
+
+ if (bNeedSize)
+ {
+ ++nDepth;
+ rSizeChanged = true;
+ }
+
+ if (nDepth <= nLevel)
+ {
+ nDepth = nLevel+1;
+ rSizeChanged = true;
+ }
+
+ ScOutlineEntry aNewEntry(nStartCol, nEndCol+1-nStartCol, bHidden);
+ aNewEntry.SetVisible( true );
+ aCollections[nLevel].insert(aNewEntry);
+
+ return true;
+}
+
+bool ScOutlineArray::FindTouchedLevel(
+ SCCOLROW nBlockStart, SCCOLROW nBlockEnd, size_t& rFindLevel) const
+{
+ bool bFound = false;
+ rFindLevel = 0;
+
+ for (size_t nLevel = 0; nLevel < nDepth; ++nLevel)
+ {
+ const ScOutlineCollection* pCollect = &aCollections[nLevel];
+ for (const auto& rEntry : *pCollect)
+ {
+ const ScOutlineEntry *const pEntry = &rEntry.second;
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ if ( ( nBlockStart>=nStart && nBlockStart<=nEnd ) ||
+ ( nBlockEnd >=nStart && nBlockEnd <=nEnd ) )
+ {
+ rFindLevel = nLevel; // Actual Level
+ bFound = true;
+ }
+ }
+ }
+
+ return bFound;
+}
+
+void ScOutlineArray::PromoteSub(SCCOLROW nStartPos, SCCOLROW nEndPos, size_t nStartLevel)
+{
+ if (nStartLevel==0)
+ {
+ OSL_FAIL("PromoteSub with Level 0");
+ return;
+ }
+
+ for (size_t nLevel = nStartLevel; nLevel < nDepth; ++nLevel)
+ {
+ ScOutlineCollection& rColl = aCollections[nLevel];
+ ScOutlineCollection::iterator it = rColl.begin(), itEnd = rColl.end();
+ while (it != itEnd)
+ {
+ ScOutlineEntry *const pEntry = &it->second;
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+ if (nStart >= nStartPos && nEnd <= nEndPos)
+ {
+ aCollections[nLevel-1].insert(*pEntry);
+
+ it = rColl.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ it = rColl.begin();
+ itEnd = rColl.end();
+
+ while (it != itEnd)
+ {
+ ScOutlineEntry *const pEntry = &it->second;
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+ if (nStart >= nStartPos && nEnd <= nEndPos)
+ {
+ aCollections[nLevel-1].insert(*pEntry);
+
+ it = rColl.erase(it);
+ }
+ else
+ ++it;
+ }
+ }
+}
+
+/**
+ * Adapt nDepth for empty Levels
+ */
+bool ScOutlineArray::DecDepth()
+{
+ bool bChanged = false;
+ bool bCont;
+ do
+ {
+ bCont = false;
+ if (nDepth)
+ {
+ if (aCollections[nDepth-1].empty())
+ {
+ --nDepth;
+ bChanged = true;
+ bCont = true;
+ }
+ }
+ }
+ while (bCont);
+
+ return bChanged;
+}
+
+bool ScOutlineArray::Remove( SCCOLROW nBlockStart, SCCOLROW nBlockEnd, bool& rSizeChanged )
+{
+ size_t nLevel;
+ FindTouchedLevel( nBlockStart, nBlockEnd, nLevel );
+
+ ScOutlineCollection* pCollect = &aCollections[nLevel];
+ ScOutlineCollection::iterator it = pCollect->begin(), itEnd = pCollect->end();
+ bool bAny = false;
+ while (it != itEnd)
+ {
+ ScOutlineEntry *const pEntry = &it->second;
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+ if (nBlockStart <= nEnd && nBlockEnd >= nStart)
+ {
+ // Overlaps
+ pCollect->erase(it);
+ PromoteSub( nStart, nEnd, nLevel+1 );
+ itEnd = pCollect->end();
+ it = pCollect->FindStart( nEnd+1 );
+ bAny = true;
+ }
+ else
+ ++it;
+ }
+
+ if (bAny) // Adapt Depth
+ if (DecDepth())
+ rSizeChanged = true;
+
+ return bAny;
+}
+
+ScOutlineEntry* ScOutlineArray::GetEntry(size_t nLevel, size_t nIndex)
+{
+ if (nLevel >= nDepth)
+ return nullptr;
+
+ ScOutlineCollection& rColl = aCollections[nLevel];
+ if (nIndex >= rColl.size())
+ return nullptr;
+
+ ScOutlineCollection::iterator it = rColl.begin();
+ std::advance(it, nIndex);
+ return &it->second;
+}
+
+const ScOutlineEntry* ScOutlineArray::GetEntry(size_t nLevel, size_t nIndex) const
+{
+ if (nLevel >= nDepth)
+ return nullptr;
+
+ const ScOutlineCollection& rColl = aCollections[nLevel];
+ if (nIndex >= rColl.size())
+ return nullptr;
+
+ ScOutlineCollection::const_iterator it = rColl.begin();
+ std::advance(it, nIndex);
+ return &it->second;
+}
+
+size_t ScOutlineArray::GetCount(size_t nLevel) const
+{
+ if (nLevel >= nDepth)
+ return 0;
+
+ return aCollections[nLevel].size();
+}
+
+const ScOutlineEntry* ScOutlineArray::GetEntryByPos(size_t nLevel, SCCOLROW nPos) const
+{
+ if (nLevel >= nDepth)
+ return nullptr;
+
+ const ScOutlineCollection& rColl = aCollections[nLevel];
+ ScOutlineCollection::const_iterator it = std::find_if(rColl.begin(), rColl.end(),
+ [&nPos](const auto& rEntry) {
+ const ScOutlineEntry *const pEntry = &rEntry.second;
+ return pEntry->GetStart() <= nPos && nPos <= pEntry->GetEnd();
+ });
+ if (it != rColl.end())
+ return &it->second;
+
+ return nullptr;
+}
+
+bool ScOutlineArray::GetEntryIndex(size_t nLevel, SCCOLROW nPos, size_t& rnIndex) const
+{
+ if (nLevel >= nDepth)
+ return false;
+
+ // Found entry contains passed position
+ const ScOutlineCollection& rColl = aCollections[nLevel];
+ ScOutlineCollection::const_iterator it = std::find_if(rColl.begin(), rColl.end(),
+ [&nPos](const auto& rEntry) {
+ const ScOutlineEntry *const p = &rEntry.second;
+ return p->GetStart() <= nPos && nPos <= p->GetEnd();
+ });
+ if (it != rColl.end())
+ {
+ rnIndex = std::distance(rColl.begin(), it);
+ return true;
+ }
+ return false;
+}
+
+bool ScOutlineArray::GetEntryIndexInRange(
+ size_t nLevel, SCCOLROW nBlockStart, SCCOLROW nBlockEnd, size_t& rnIndex) const
+{
+ if (nLevel >= nDepth)
+ return false;
+
+ // Found entry will be completely inside of passed range
+ const ScOutlineCollection& rColl = aCollections[nLevel];
+ ScOutlineCollection::const_iterator it = std::find_if(rColl.begin(), rColl.end(),
+ [&nBlockStart, &nBlockEnd](const auto& rEntry) {
+ const ScOutlineEntry *const p = &rEntry.second;
+ return nBlockStart <= p->GetStart() && p->GetEnd() <= nBlockEnd;
+ });
+ if (it != rColl.end())
+ {
+ rnIndex = std::distance(rColl.begin(), it);
+ return true;
+ }
+ return false;
+}
+
+void ScOutlineArray::SetVisibleBelow(
+ size_t nLevel, size_t nEntry, bool bValue, bool bSkipHidden)
+{
+ const ScOutlineEntry* pEntry = GetEntry( nLevel, nEntry );
+ if (!pEntry)
+ return;
+
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ for (size_t nSubLevel = nLevel+1; nSubLevel < nDepth; ++nSubLevel)
+ {
+ ScOutlineCollection& rColl = aCollections[nSubLevel];
+ size_t nPos = 0;
+ for (auto& rEntry : rColl)
+ {
+ ScOutlineEntry *const p = &rEntry.second;
+ if (p->GetStart() >= nStart && p->GetEnd() <= nEnd)
+ {
+ p->SetVisible(bValue);
+ if (bSkipHidden && !p->IsHidden())
+ {
+ SetVisibleBelow(nSubLevel, nPos, bValue, true);
+ }
+ }
+ ++nPos;
+ }
+
+ if (bSkipHidden)
+ nSubLevel = nDepth; // Bail out
+ }
+}
+
+void ScOutlineArray::GetRange(SCCOLROW& rStart, SCCOLROW& rEnd) const
+{
+ const ScOutlineCollection& rColl = aCollections[0];
+ if (!rColl.empty())
+ {
+ ScOutlineCollection::const_iterator it = rColl.begin();
+ rStart = it->second.GetStart();
+ std::advance(it, rColl.size()-1);
+ rEnd = it->second.GetEnd();
+ }
+ else
+ rStart = rEnd = 0;
+}
+
+void ScOutlineArray::ExtendBlock(size_t nLevel, SCCOLROW& rBlkStart, SCCOLROW& rBlkEnd)
+{
+ if (nLevel >= nDepth)
+ return;
+
+ const ScOutlineCollection& rColl = aCollections[nLevel];
+ for (const auto& rEntry : rColl)
+ {
+ const ScOutlineEntry *const pEntry = &rEntry.second;
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ if (rBlkStart <= nEnd && rBlkEnd >= nStart)
+ {
+ if (nStart < rBlkStart)
+ rBlkStart = nStart;
+ if (nEnd > rBlkEnd)
+ rBlkEnd = nEnd;
+ }
+ }
+}
+
+bool ScOutlineArray::TestInsertSpace(SCSIZE nSize, SCCOLROW nMaxVal) const
+{
+ const ScOutlineCollection& rColl = aCollections[0];
+ if (rColl.empty())
+ return true;
+
+ ScOutlineCollection::const_iterator it = rColl.begin();
+ std::advance(it, rColl.size()-1);
+ SCCOLROW nEnd = it->second.GetEnd();
+ return sal::static_int_cast<SCCOLROW>(nEnd+nSize) <= nMaxVal;
+}
+
+void ScOutlineArray::InsertSpace(SCCOLROW nStartPos, SCSIZE nSize)
+{
+ ScSubOutlineIterator aIter( this );
+ ScOutlineEntry* pEntry;
+ while ((pEntry = aIter.GetNext()) != nullptr)
+ {
+ if ( pEntry->GetStart() >= nStartPos )
+ pEntry->Move(static_cast<SCCOLROW>(nSize));
+ else
+ {
+ SCCOLROW nEnd = pEntry->GetEnd();
+ // Always expand if inserted within the group
+ // When inserting at the end, only if the group is not hidden
+ if ( nEnd >= nStartPos || ( nEnd+1 >= nStartPos && !pEntry->IsHidden() ) )
+ {
+ SCSIZE nEntrySize = pEntry->GetSize();
+ nEntrySize += nSize;
+ pEntry->SetSize( nEntrySize );
+ }
+ }
+ }
+}
+
+bool ScOutlineArray::DeleteSpace(SCCOLROW nStartPos, SCSIZE nSize)
+{
+ SCCOLROW nEndPos = nStartPos + nSize - 1;
+ bool bNeedSave = false; // Do we need the original one for Undo?
+ bool bChanged = false; // For Level test
+
+ ScSubOutlineIterator aIter( this );
+ ScOutlineEntry* pEntry;
+ while((pEntry=aIter.GetNext())!=nullptr)
+ {
+ SCCOLROW nEntryStart = pEntry->GetStart();
+ SCCOLROW nEntryEnd = pEntry->GetEnd();
+ SCSIZE nEntrySize = pEntry->GetSize();
+
+ if ( nEntryEnd >= nStartPos )
+ {
+ if ( nEntryStart > nEndPos ) // Right
+ pEntry->Move(-static_cast<SCCOLROW>(nSize));
+ else if ( nEntryStart < nStartPos && nEntryEnd >= nEndPos ) // Outside
+ pEntry->SetSize( nEntrySize-nSize );
+ else
+ {
+ bNeedSave = true;
+ if ( nEntryStart >= nStartPos && nEntryEnd <= nEndPos ) // Inside
+ {
+ aIter.DeleteLast();
+ bChanged = true;
+ }
+ else if ( nEntryStart >= nStartPos ) // Top right
+ pEntry->SetPosSize( nStartPos, static_cast<SCSIZE>(nEntryEnd-nEndPos) );
+ else // Top left
+ pEntry->SetSize( static_cast<SCSIZE>(nStartPos-nEntryStart) );
+ }
+ }
+ }
+
+ if (bChanged)
+ DecDepth();
+
+ return bNeedSave;
+}
+
+bool ScOutlineArray::ManualAction(
+ SCCOLROW nStartPos, SCCOLROW nEndPos, bool bShow, const ScTable& rTable, bool bCol)
+{
+ bool bModified = false;
+ ScSubOutlineIterator aIter( this );
+ ScOutlineEntry* pEntry;
+ while((pEntry=aIter.GetNext())!=nullptr)
+ {
+ SCCOLROW nEntryStart = pEntry->GetStart();
+ SCCOLROW nEntryEnd = pEntry->GetEnd();
+
+ if (nEntryEnd>=nStartPos && nEntryStart<=nEndPos)
+ {
+ if ( pEntry->IsHidden() == bShow )
+ {
+ // #i12341# hide if all columns/rows are hidden, show if at least one
+ // is visible
+ SCCOLROW nEnd = rTable.LastHiddenColRow(nEntryStart, bCol);
+ bool bAllHidden = (nEntryEnd <= nEnd && nEnd <
+ ::std::numeric_limits<SCCOLROW>::max());
+
+ bool bToggle = ( bShow != bAllHidden );
+ if ( bToggle )
+ {
+ pEntry->SetHidden( !bShow );
+ SetVisibleBelow( aIter.LastLevel(), aIter.LastEntry(), bShow, bShow );
+ bModified = true;
+ }
+ }
+ }
+ }
+ return bModified;
+}
+
+void ScOutlineArray::RemoveAll()
+{
+ for (size_t nLevel = 0; nLevel < nDepth; ++nLevel)
+ aCollections[nLevel].clear();
+
+ nDepth = 0;
+}
+
+void ScOutlineArray::finalizeImport(const ScTable& rTable)
+{
+ ScSubOutlineIterator aIter( this );
+ ScOutlineEntry* pEntry;
+ while((pEntry=aIter.GetNext())!=nullptr)
+ {
+
+ if (!pEntry->IsHidden())
+ continue;
+
+ SCCOLROW nEntryStart = pEntry->GetStart();
+ SCCOLROW nEntryEnd = pEntry->GetEnd();
+ SCCOLROW nEnd = rTable.LastHiddenColRow(nEntryStart, false/*bCol*/);
+ bool bAllHidden = (nEntryEnd <= nEnd && nEnd <
+ ::std::numeric_limits<SCCOLROW>::max());
+
+ pEntry->SetHidden(bAllHidden);
+ SetVisibleBelow(aIter.LastLevel(), aIter.LastEntry(), !bAllHidden, !bAllHidden);
+ }
+}
+
+OString ScOutlineArray::dumpAsString() const
+{
+ OString aOutput;
+ const char* const pLevelSep = " ";
+ for (const auto& rCollection : aCollections)
+ {
+ if (rCollection.empty())
+ continue;
+ aOutput += rCollection.dumpAsString() + pLevelSep;
+ }
+
+ return aOutput;
+}
+
+ScOutlineTable::ScOutlineTable()
+{
+}
+
+ScOutlineTable::ScOutlineTable( const ScOutlineTable& rOutline ) :
+ aColOutline( rOutline.aColOutline ),
+ aRowOutline( rOutline.aRowOutline )
+{
+}
+
+bool ScOutlineTable::TestInsertCol( SCSIZE nSize )
+{
+ return aColOutline.TestInsertSpace( nSize, MAXCOL );
+}
+
+void ScOutlineTable::InsertCol( SCCOL nStartCol, SCSIZE nSize )
+{
+ aColOutline.InsertSpace( nStartCol, nSize );
+}
+
+bool ScOutlineTable::DeleteCol( SCCOL nStartCol, SCSIZE nSize )
+{
+ return aColOutline.DeleteSpace( nStartCol, nSize );
+}
+
+bool ScOutlineTable::TestInsertRow( SCSIZE nSize )
+{
+ return aRowOutline.TestInsertSpace( nSize, MAXROW );
+}
+
+void ScOutlineTable::InsertRow( SCROW nStartRow, SCSIZE nSize )
+{
+ aRowOutline.InsertSpace( nStartRow, nSize );
+}
+
+bool ScOutlineTable::DeleteRow( SCROW nStartRow, SCSIZE nSize )
+{
+ return aRowOutline.DeleteSpace( nStartRow, nSize );
+}
+
+ScSubOutlineIterator::ScSubOutlineIterator( ScOutlineArray* pOutlineArray ) :
+ pArray( pOutlineArray ),
+ nStart( 0 ),
+ nEnd( SCCOLROW_MAX ), // Iterate over all of them
+ nSubLevel( 0 ),
+ nSubEntry( 0 )
+{
+ nDepth = pArray->nDepth;
+}
+
+ScSubOutlineIterator::ScSubOutlineIterator(
+ ScOutlineArray* pOutlineArray, size_t nLevel, size_t nEntry ) :
+ pArray( pOutlineArray )
+{
+ const ScOutlineCollection& rColl = pArray->aCollections[nLevel];
+ ScOutlineCollection::const_iterator it = rColl.begin();
+ std::advance(it, nEntry);
+ const ScOutlineEntry* pEntry = &it->second;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ nSubLevel = nLevel + 1;
+ nSubEntry = 0;
+ nDepth = pArray->nDepth;
+}
+
+ScOutlineEntry* ScSubOutlineIterator::GetNext()
+{
+ ScOutlineEntry* pEntry = nullptr;
+ bool bFound = false;
+ do
+ {
+ if (nSubLevel >= nDepth)
+ return nullptr;
+
+ ScOutlineCollection& rColl = pArray->aCollections[nSubLevel];
+ if (nSubEntry < rColl.size())
+ {
+ ScOutlineCollection::iterator it = rColl.begin();
+ std::advance(it, nSubEntry);
+ pEntry = &it->second;
+
+ if (pEntry->GetStart() >= nStart && pEntry->GetEnd() <= nEnd)
+ bFound = true;
+
+ ++nSubEntry;
+ }
+ else
+ {
+ // Go to the next sub-level
+ nSubEntry = 0;
+ ++nSubLevel;
+ }
+ }
+ while (!bFound);
+ return pEntry; // nSubLevel valid, if pEntry != 0
+}
+
+size_t ScSubOutlineIterator::LastEntry() const
+{
+ if (nSubEntry == 0)
+ {
+ OSL_FAIL("ScSubOutlineIterator::LastEntry before GetNext");
+ return 0;
+ }
+ return nSubEntry-1;
+}
+
+void ScSubOutlineIterator::DeleteLast()
+{
+ if (nSubLevel >= nDepth)
+ {
+ OSL_FAIL("ScSubOutlineIterator::DeleteLast after End");
+ return;
+ }
+ if (nSubEntry == 0)
+ {
+ OSL_FAIL("ScSubOutlineIterator::DeleteLast before GetNext");
+ return;
+ }
+
+ --nSubEntry;
+ ScOutlineCollection& rColl = pArray->aCollections[nSubLevel];
+ assert(nSubEntry < rColl.size());
+ ScOutlineCollection::iterator it = rColl.begin();
+ std::advance(it, nSubEntry);
+ rColl.erase(it);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/pagepar.cxx b/sc/source/core/data/pagepar.cxx
new file mode 100644
index 0000000000..7e996fc64b
--- /dev/null
+++ b/sc/source/core/data/pagepar.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 <pagepar.hxx>
+
+// struct ScPageTableParam:
+
+ScPageTableParam::ScPageTableParam()
+{
+ Reset();
+}
+
+void ScPageTableParam::Reset()
+{
+ bCellContent = true;
+ bNotes=bGrid=bHeaders=bDrawings=
+ bLeftRight=bScaleAll=bScaleTo=bScalePageNum=
+ bFormulas=bNullVals=bSkipEmpty=bForceBreaks = false;
+ bTopDown=bScaleNone=bCharts=bObjects = true;
+ nScaleAll = 100;
+ nScalePageNum = nScaleWidth = nScaleHeight = 0;
+ nFirstPageNo = 1;
+}
+
+// struct ScPageAreaParam:
+
+ScPageAreaParam::ScPageAreaParam()
+{
+ Reset();
+}
+
+void ScPageAreaParam::Reset()
+{
+ bPrintArea = bRepeatRow = bRepeatCol = false;
+
+ aPrintArea = ScRange();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/patattr.cxx b/sc/source/core/data/patattr.cxx
new file mode 100644
index 0000000000..4bb79828da
--- /dev/null
+++ b/sc/source/core/data/patattr.cxx
@@ -0,0 +1,1513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+#include <scitems.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/postitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/whiter.hxx>
+#include <svl/zforlist.hxx>
+#include <vcl/outdev.hxx>
+#include <tools/fract.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <validat.hxx>
+#include <scmod.hxx>
+#include <fillinfo.hxx>
+#include <comphelper/lok.hxx>
+#include <tabvwsh.hxx>
+
+const WhichRangesContainer aScPatternAttrSchema(svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>);
+
+ScPatternAttr::ScPatternAttr( SfxItemSet&& pItemSet, const OUString& rStyleName )
+ : SfxSetItem ( ATTR_PATTERN, std::move(pItemSet) ),
+ pName ( rStyleName ),
+ pStyle ( nullptr ),
+ mnPAKey(0)
+{
+ setExceptionalSCItem();
+
+ // We need to ensure that ScPatternAttr is using the correct WhichRange,
+ // see comments in commit message. This does transfers the items with
+ // minimized overhead, too
+ if (GetItemSet().GetRanges() != aScPatternAttrSchema)
+ GetItemSet().SetRanges(aScPatternAttrSchema);
+}
+
+ScPatternAttr::ScPatternAttr( SfxItemSet&& pItemSet )
+ : SfxSetItem ( ATTR_PATTERN, std::move(pItemSet) ),
+ pStyle ( nullptr ),
+ mnPAKey(0)
+{
+ setExceptionalSCItem();
+
+ // We need to ensure that ScPatternAttr is using the correct WhichRange,
+ // see comments in commit message. This does transfers the items with
+ // minimized overhead, too
+ if (GetItemSet().GetRanges() != aScPatternAttrSchema)
+ GetItemSet().SetRanges(aScPatternAttrSchema);
+}
+
+ScPatternAttr::ScPatternAttr( SfxItemPool* pItemPool )
+ : SfxSetItem ( ATTR_PATTERN, SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( *pItemPool ) ),
+ pStyle ( nullptr ),
+ mnPAKey(0)
+{
+ setExceptionalSCItem();
+}
+
+ScPatternAttr::ScPatternAttr( const ScPatternAttr& rPatternAttr )
+ : SfxSetItem ( rPatternAttr ),
+ pName ( rPatternAttr.pName ),
+ pStyle ( rPatternAttr.pStyle ),
+ mnPAKey(rPatternAttr.mnPAKey)
+{
+ setExceptionalSCItem();
+}
+
+ScPatternAttr* ScPatternAttr::Clone( SfxItemPool *pPool ) const
+{
+ ScPatternAttr* pPattern = new ScPatternAttr( GetItemSet().CloneAsValue(true, pPool) );
+
+ pPattern->pStyle = pStyle;
+ pPattern->pName = pName;
+
+ return pPattern;
+}
+
+static bool StrCmp( const OUString* pStr1, const OUString* pStr2 )
+{
+ if (pStr1 == pStr2)
+ return true;
+ if (pStr1 && !pStr2)
+ return false;
+ if (!pStr1 && pStr2)
+ return false;
+ return *pStr1 == *pStr2;
+}
+
+constexpr size_t compareSize = ATTR_PATTERN_END - ATTR_PATTERN_START + 1;
+
+bool ScPatternAttr::operator==( const SfxPoolItem& rCmp ) const
+{
+ // check if same incarnation
+ if (this == &rCmp)
+ return true;
+
+ // check SfxPoolItem base class
+ if (!SfxPoolItem::operator==(rCmp) )
+ return false;
+
+ // check everything except the SfxItemSet from base class SfxSetItem
+ const ScPatternAttr& rOther(static_cast<const ScPatternAttr&>(rCmp));
+ if (!StrCmp(GetStyleName(), rOther.GetStyleName()))
+ return false;
+
+ // here we need to compare the SfxItemSet. We *know* that these are
+ // all simple (one range, same range)
+ const SfxItemSet& rSet1(GetItemSet());
+ const SfxItemSet& rSet2(rOther.GetItemSet());
+
+ // the former method 'FastEqualPatternSets' mentioned:
+ // "Actually test_tdf133629 from UITest_calc_tests9 somehow manages to have
+ // a different range (and I don't understand enough why), so better be safe and compare fully."
+ // in that case the hash code above would already fail, too
+ if (rSet1.TotalCount() != compareSize || rSet2.TotalCount() != compareSize)
+ {
+ // assert this for now, should not happen. If it does, look for it and evtl.
+ // enable SfxItemSet::operator== below
+ assert(false);
+ return rSet1 == rSet2;
+ }
+
+ // check pools, do not accept different pools
+ if (rSet1.GetPool() != rSet2.GetPool())
+ return false;
+
+ // check count of set items, has to be equal
+ if (rSet1.Count() != rSet2.Count())
+ return false;
+
+ // compare each item separately
+ const SfxPoolItem **ppItem1(rSet1.GetItems_Impl());
+ const SfxPoolItem **ppItem2(rSet2.GetItems_Impl());
+
+ // are all pointers the same?
+ if (0 == memcmp(ppItem1, ppItem2, compareSize * sizeof(ppItem1[0])))
+ return true;
+
+ for (sal_uInt16 nPos(0); nPos < compareSize; nPos++)
+ {
+ if (!SfxPoolItem::areSame(*ppItem1, *ppItem2))
+ return false;
+ ++ppItem1;
+ ++ppItem2;
+ }
+
+ return true;
+}
+
+SvxCellOrientation ScPatternAttr::GetCellOrientation( const SfxItemSet& rItemSet, const SfxItemSet* pCondSet )
+{
+ SvxCellOrientation eOrient = SvxCellOrientation::Standard;
+
+ if( GetItem( ATTR_STACKED, rItemSet, pCondSet ).GetValue() )
+ {
+ eOrient = SvxCellOrientation::Stacked;
+ }
+ else
+ {
+ Degree100 nAngle = GetItem( ATTR_ROTATE_VALUE, rItemSet, pCondSet ).GetValue();
+ if( nAngle == 9000_deg100 )
+ eOrient = SvxCellOrientation::BottomUp;
+ else if( nAngle == 27000_deg100 )
+ eOrient = SvxCellOrientation::TopBottom;
+ }
+
+ return eOrient;
+}
+
+SvxCellOrientation ScPatternAttr::GetCellOrientation( const SfxItemSet* pCondSet ) const
+{
+ return GetCellOrientation( GetItemSet(), pCondSet );
+}
+
+namespace {
+
+void getFontIDsByScriptType(SvtScriptType nScript,
+ TypedWhichId<SvxFontItem>& nFontId,
+ TypedWhichId<SvxFontHeightItem>& nHeightId,
+ TypedWhichId<SvxWeightItem>& nWeightId,
+ TypedWhichId<SvxPostureItem>& nPostureId,
+ TypedWhichId<SvxLanguageItem>& nLangId)
+{
+ if ( nScript == SvtScriptType::ASIAN )
+ {
+ nFontId = ATTR_CJK_FONT;
+ nHeightId = ATTR_CJK_FONT_HEIGHT;
+ nWeightId = ATTR_CJK_FONT_WEIGHT;
+ nPostureId = ATTR_CJK_FONT_POSTURE;
+ nLangId = ATTR_CJK_FONT_LANGUAGE;
+ }
+ else if ( nScript == SvtScriptType::COMPLEX )
+ {
+ nFontId = ATTR_CTL_FONT;
+ nHeightId = ATTR_CTL_FONT_HEIGHT;
+ nWeightId = ATTR_CTL_FONT_WEIGHT;
+ nPostureId = ATTR_CTL_FONT_POSTURE;
+ nLangId = ATTR_CTL_FONT_LANGUAGE;
+ }
+ else
+ {
+ nFontId = ATTR_FONT;
+ nHeightId = ATTR_FONT_HEIGHT;
+ nWeightId = ATTR_FONT_WEIGHT;
+ nPostureId = ATTR_FONT_POSTURE;
+ nLangId = ATTR_FONT_LANGUAGE;
+ }
+}
+
+}
+
+void ScPatternAttr::fillFont(
+ vcl::Font& rFont, const SfxItemSet& rItemSet, ScAutoFontColorMode eAutoMode,
+ const OutputDevice* pOutDev, const Fraction* pScale,
+ const SfxItemSet* pCondSet, SvtScriptType nScript,
+ const Color* pBackConfigColor, const Color* pTextConfigColor)
+{
+ model::ComplexColor aComplexColor;
+
+ // determine effective font color
+ ScPatternAttr::fillFontOnly(rFont, rItemSet, pOutDev, pScale, pCondSet, nScript);
+ ScPatternAttr::fillColor(aComplexColor, rItemSet, eAutoMode, pCondSet, pBackConfigColor, pTextConfigColor);
+
+ // set font effects
+ rFont.SetColor(aComplexColor.getFinalColor());
+}
+
+void ScPatternAttr::fillFontOnly(
+ vcl::Font& rFont, const SfxItemSet& rItemSet,
+ const OutputDevice* pOutDev, const Fraction* pScale,
+ const SfxItemSet* pCondSet, SvtScriptType nScript)
+{
+ // Read items
+
+ const SvxFontItem* pFontAttr;
+ sal_uInt32 nFontHeight;
+ FontWeight eWeight;
+ FontItalic eItalic;
+ FontLineStyle eUnder;
+ FontLineStyle eOver;
+ bool bWordLine;
+ FontStrikeout eStrike;
+ bool bOutline;
+ bool bShadow;
+ FontEmphasisMark eEmphasis;
+ FontRelief eRelief;
+ LanguageType eLang;
+
+ TypedWhichId<SvxFontItem> nFontId(0);
+ TypedWhichId<SvxFontHeightItem> nHeightId(0);
+ TypedWhichId<SvxWeightItem> nWeightId(0);
+ TypedWhichId<SvxPostureItem> nPostureId(0);
+ TypedWhichId<SvxLanguageItem> nLangId(0);
+ getFontIDsByScriptType(nScript, nFontId, nHeightId, nWeightId, nPostureId, nLangId);
+
+ if (pCondSet)
+ {
+ pFontAttr = pCondSet->GetItemIfSet( nFontId );
+ if ( !pFontAttr )
+ pFontAttr = &rItemSet.Get( nFontId );
+
+ const SvxFontHeightItem* pFontHeightItem = pCondSet->GetItemIfSet( nHeightId );
+ if ( !pFontHeightItem )
+ pFontHeightItem = &rItemSet.Get( nHeightId );
+ nFontHeight = pFontHeightItem->GetHeight();
+
+ const SvxWeightItem* pFontHWeightItem = pCondSet->GetItemIfSet( nWeightId );
+ if ( !pFontHWeightItem )
+ pFontHWeightItem = &rItemSet.Get( nWeightId );
+ eWeight = pFontHWeightItem->GetValue();
+
+ const SvxPostureItem* pPostureItem = pCondSet->GetItemIfSet( nPostureId );
+ if ( !pPostureItem )
+ pPostureItem = &rItemSet.Get( nPostureId );
+ eItalic = pPostureItem->GetValue();
+
+ const SvxUnderlineItem* pUnderlineItem = pCondSet->GetItemIfSet( ATTR_FONT_UNDERLINE );
+ if ( !pUnderlineItem )
+ pUnderlineItem = &rItemSet.Get( ATTR_FONT_UNDERLINE );
+ eUnder = pUnderlineItem->GetValue();
+
+ const SvxOverlineItem* pOverlineItem = pCondSet->GetItemIfSet( ATTR_FONT_OVERLINE );
+ if ( !pOverlineItem )
+ pOverlineItem = &rItemSet.Get( ATTR_FONT_OVERLINE );
+ eOver = pOverlineItem->GetValue();
+
+ const SvxWordLineModeItem* pWordlineItem = pCondSet->GetItemIfSet( ATTR_FONT_WORDLINE );
+ if ( !pWordlineItem )
+ pWordlineItem = &rItemSet.Get( ATTR_FONT_WORDLINE );
+ bWordLine = pWordlineItem->GetValue();
+
+ const SvxCrossedOutItem* pCrossedOutItem = pCondSet->GetItemIfSet( ATTR_FONT_CROSSEDOUT );
+ if ( !pCrossedOutItem )
+ pCrossedOutItem = &rItemSet.Get( ATTR_FONT_CROSSEDOUT );
+ eStrike = pCrossedOutItem->GetValue();
+
+ const SvxContourItem* pContourItem = pCondSet->GetItemIfSet( ATTR_FONT_CONTOUR );
+ if ( !pContourItem )
+ pContourItem = &rItemSet.Get( ATTR_FONT_CONTOUR );
+ bOutline = pContourItem->GetValue();
+
+ const SvxShadowedItem* pShadowedItem = pCondSet->GetItemIfSet( ATTR_FONT_SHADOWED );
+ if ( !pShadowedItem )
+ pShadowedItem = &rItemSet.Get( ATTR_FONT_SHADOWED );
+ bShadow = pShadowedItem->GetValue();
+
+ const SvxEmphasisMarkItem* pEmphasisMarkItem = pCondSet->GetItemIfSet( ATTR_FONT_EMPHASISMARK );
+ if ( !pEmphasisMarkItem )
+ pEmphasisMarkItem = &rItemSet.Get( ATTR_FONT_EMPHASISMARK );
+ eEmphasis = pEmphasisMarkItem->GetEmphasisMark();
+
+ const SvxCharReliefItem* pCharReliefItem = pCondSet->GetItemIfSet( ATTR_FONT_RELIEF );
+ if ( !pCharReliefItem )
+ pCharReliefItem = &rItemSet.Get( ATTR_FONT_RELIEF );
+ eRelief = pCharReliefItem->GetValue();
+
+ const SvxLanguageItem* pLanguageItem = pCondSet->GetItemIfSet( nLangId );
+ if ( !pLanguageItem )
+ pLanguageItem = &rItemSet.Get( nLangId );
+ eLang = pLanguageItem->GetLanguage();
+ }
+ else // Everything from rItemSet
+ {
+ pFontAttr = &rItemSet.Get( nFontId );
+ nFontHeight = rItemSet.Get( nHeightId ).GetHeight();
+ eWeight = rItemSet.Get( nWeightId ).GetValue();
+ eItalic = rItemSet.Get( nPostureId ).GetValue();
+ eUnder = rItemSet.Get( ATTR_FONT_UNDERLINE ).GetValue();
+ eOver = rItemSet.Get( ATTR_FONT_OVERLINE ).GetValue();
+ bWordLine = rItemSet.Get( ATTR_FONT_WORDLINE ).GetValue();
+ eStrike = rItemSet.Get( ATTR_FONT_CROSSEDOUT ).GetValue();
+ bOutline = rItemSet.Get( ATTR_FONT_CONTOUR ).GetValue();
+ bShadow = rItemSet.Get( ATTR_FONT_SHADOWED ).GetValue();
+ eEmphasis = rItemSet.Get( ATTR_FONT_EMPHASISMARK ).GetEmphasisMark();
+ eRelief = rItemSet.Get( ATTR_FONT_RELIEF ).GetValue();
+ // for graphite language features
+ eLang = rItemSet.Get( nLangId ).GetLanguage();
+ }
+ OSL_ENSURE(pFontAttr,"Oops?");
+
+ // Evaluate
+
+ // FontItem:
+
+ if (rFont.GetFamilyName() != pFontAttr->GetFamilyName())
+ rFont.SetFamilyName( pFontAttr->GetFamilyName() );
+ if (rFont.GetStyleName() != pFontAttr->GetStyleName())
+ rFont.SetStyleName( pFontAttr->GetStyleName() );
+
+ rFont.SetFamily( pFontAttr->GetFamily() );
+ rFont.SetCharSet( pFontAttr->GetCharSet() );
+ rFont.SetPitch( pFontAttr->GetPitch() );
+
+ rFont.SetLanguage(eLang);
+
+ // Size
+
+ if ( pOutDev != nullptr )
+ {
+ Size aEffSize;
+ Fraction aFraction( 1,1 );
+ if (pScale)
+ aFraction = *pScale;
+ Size aSize( 0, static_cast<tools::Long>(nFontHeight) );
+ MapMode aDestMode = pOutDev->GetMapMode();
+ MapMode aSrcMode( MapUnit::MapTwip, Point(), aFraction, aFraction );
+ if (aDestMode.GetMapUnit() == MapUnit::MapPixel && pOutDev->GetDPIX() > 0)
+ aEffSize = pOutDev->LogicToPixel( aSize, aSrcMode );
+ else
+ {
+ Fraction aFractOne(1,1);
+ aDestMode.SetScaleX( aFractOne );
+ aDestMode.SetScaleY( aFractOne );
+ aEffSize = OutputDevice::LogicToLogic( aSize, aSrcMode, aDestMode );
+ }
+ rFont.SetFontSize( aEffSize );
+ }
+ else /* if pOutDev != NULL */
+ {
+ rFont.SetFontSize( Size( 0, static_cast<tools::Long>(nFontHeight) ) );
+ }
+
+ // set font effects
+ rFont.SetWeight( eWeight );
+ rFont.SetItalic( eItalic );
+ rFont.SetUnderline( eUnder );
+ rFont.SetOverline( eOver );
+ rFont.SetWordLineMode( bWordLine );
+ rFont.SetStrikeout( eStrike );
+ rFont.SetOutline( bOutline );
+ rFont.SetShadow( bShadow );
+ rFont.SetEmphasisMark( eEmphasis );
+ rFont.SetRelief( eRelief );
+ rFont.SetTransparent( true );
+}
+
+void ScPatternAttr::fillColor(model::ComplexColor& rComplexColor, const SfxItemSet& rItemSet, ScAutoFontColorMode eAutoMode, const SfxItemSet* pCondSet, const Color* pBackConfigColor, const Color* pTextConfigColor)
+{
+ model::ComplexColor aComplexColor;
+
+ Color aColor;
+
+ SvxColorItem const* pColorItem = nullptr;
+
+ if (pCondSet)
+ pColorItem = pCondSet->GetItemIfSet(ATTR_FONT_COLOR);
+
+ if (!pColorItem)
+ pColorItem = &rItemSet.Get(ATTR_FONT_COLOR);
+
+ if (pColorItem)
+ {
+ aComplexColor = pColorItem->getComplexColor();
+ aColor = pColorItem->GetValue();
+ }
+
+ if (aComplexColor.getType() == model::ColorType::Unused)
+ {
+ aComplexColor.setColor(aColor);
+ }
+
+ if ((aColor == COL_AUTO && eAutoMode != ScAutoFontColorMode::Raw)
+ || eAutoMode == ScAutoFontColorMode::IgnoreFont
+ || eAutoMode == ScAutoFontColorMode::IgnoreAll)
+ {
+ // get background color from conditional or own set
+ Color aBackColor;
+ if ( pCondSet )
+ {
+ const SvxBrushItem* pItem = pCondSet->GetItemIfSet(ATTR_BACKGROUND);
+ if (!pItem)
+ pItem = &rItemSet.Get(ATTR_BACKGROUND);
+ aBackColor = pItem->GetColor();
+ }
+ else
+ {
+ aBackColor = rItemSet.Get(ATTR_BACKGROUND).GetColor();
+ }
+
+ // if background color attribute is transparent, use window color for brightness comparisons
+ if (aBackColor == COL_TRANSPARENT
+ || eAutoMode == ScAutoFontColorMode::IgnoreBack
+ || eAutoMode == ScAutoFontColorMode::IgnoreAll)
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ if ( eAutoMode == ScAutoFontColorMode::Print )
+ aBackColor = COL_WHITE;
+ else if ( pBackConfigColor )
+ {
+ // pBackConfigColor can be used to avoid repeated lookup of the configured color
+ aBackColor = *pBackConfigColor;
+ }
+ else
+ aBackColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ }
+ else
+ {
+ // Get document color from current view instead
+ SfxViewShell* pSfxViewShell = SfxViewShell::Current();
+ ScTabViewShell* pViewShell = dynamic_cast<ScTabViewShell*>(pSfxViewShell);
+ if (pViewShell)
+ {
+ const ScViewData& pViewData = pViewShell->GetViewData();
+ const ScViewOptions& aViewOptions = pViewData.GetOptions();
+ aBackColor = aViewOptions.GetDocColor();
+ }
+ }
+ }
+
+ // get system text color for comparison
+ Color aSysTextColor;
+ if (eAutoMode == ScAutoFontColorMode::Print)
+ {
+ aSysTextColor = COL_BLACK;
+ }
+ else if (pTextConfigColor)
+ {
+ // pTextConfigColor can be used to avoid repeated lookup of the configured color
+ aSysTextColor = *pTextConfigColor;
+ }
+ else
+ {
+ aSysTextColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ }
+
+ // select the resulting color
+ if ( aBackColor.IsDark() && aSysTextColor.IsDark() )
+ {
+ // use white instead of dark on dark
+ aColor = COL_WHITE;
+ }
+ else if ( aBackColor.IsBright() && aSysTextColor.IsBright() )
+ {
+ // use black instead of bright on bright
+ aColor = COL_BLACK;
+ }
+ else
+ {
+ // use aSysTextColor (black for ScAutoFontColorMode::Print, from style settings otherwise)
+ aColor = aSysTextColor;
+ }
+ }
+ aComplexColor.setFinalColor(aColor);
+ rComplexColor = aComplexColor;
+}
+
+ScDxfFont ScPatternAttr::GetDxfFont(const SfxItemSet& rItemSet, SvtScriptType nScript)
+{
+ TypedWhichId<SvxFontItem> nFontId(0);
+ TypedWhichId<SvxFontHeightItem> nHeightId(0);
+ TypedWhichId<SvxWeightItem> nWeightId(0);
+ TypedWhichId<SvxPostureItem> nPostureId(0);
+ TypedWhichId<SvxLanguageItem> nLangId(0);
+ getFontIDsByScriptType(nScript, nFontId, nHeightId, nWeightId, nPostureId, nLangId);
+
+ ScDxfFont aReturn;
+
+ if ( const SvxFontItem* pItem = rItemSet.GetItemIfSet( nFontId ) )
+ {
+ aReturn.pFontAttr = pItem;
+ }
+
+ if ( const SvxFontHeightItem* pItem = rItemSet.GetItemIfSet( nHeightId ) )
+ {
+ aReturn.nFontHeight = pItem->GetHeight();
+ }
+
+ if ( const SvxWeightItem* pItem = rItemSet.GetItemIfSet( nWeightId ) )
+ {
+ aReturn.eWeight = pItem->GetValue();
+ }
+
+ if ( const SvxPostureItem* pItem = rItemSet.GetItemIfSet( nPostureId ) )
+ {
+ aReturn.eItalic = pItem->GetValue();
+ }
+
+ if ( const SvxUnderlineItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_UNDERLINE ) )
+ {
+ pItem = &rItemSet.Get( ATTR_FONT_UNDERLINE );
+ aReturn.eUnder = pItem->GetValue();
+ }
+
+ if ( const SvxOverlineItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_OVERLINE ) )
+ {
+ aReturn.eOver = pItem->GetValue();
+ }
+
+ if ( const SvxWordLineModeItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_WORDLINE ) )
+ {
+ aReturn.bWordLine = pItem->GetValue();
+ }
+
+ if ( const SvxCrossedOutItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_CROSSEDOUT ) )
+ {
+ pItem = &rItemSet.Get( ATTR_FONT_CROSSEDOUT );
+ aReturn.eStrike = pItem->GetValue();
+ }
+
+ if ( const SvxContourItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_CONTOUR ) )
+ {
+ aReturn.bOutline = pItem->GetValue();
+ }
+
+ if ( const SvxShadowedItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_SHADOWED ) )
+ {
+ pItem = &rItemSet.Get( ATTR_FONT_SHADOWED );
+ aReturn.bShadow = pItem->GetValue();
+ }
+
+ if ( const SvxEmphasisMarkItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_EMPHASISMARK ) )
+ {
+ aReturn.eEmphasis = pItem->GetEmphasisMark();
+ }
+
+ if ( const SvxCharReliefItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_RELIEF ) )
+ {
+ aReturn.eRelief = pItem->GetValue();
+ }
+
+ if ( const SvxColorItem* pItem = rItemSet.GetItemIfSet( ATTR_FONT_COLOR ) )
+ {
+ aReturn.aColor = pItem->GetValue();
+ }
+
+ if ( const SvxLanguageItem* pItem = rItemSet.GetItemIfSet( nLangId ) )
+ {
+ aReturn.eLang = pItem->GetLanguage();
+ }
+
+ return aReturn;
+}
+
+template <class T>
+static void lcl_populate( std::unique_ptr<T>& rxItem, TypedWhichId<T> nWhich, const SfxItemSet& rSrcSet, const SfxItemSet* pCondSet )
+{
+ const T* pItem = pCondSet->GetItemIfSet( nWhich );
+ if ( !pItem )
+ pItem = &rSrcSet.Get( nWhich );
+ rxItem.reset(pItem->Clone());
+}
+
+void ScPatternAttr::FillToEditItemSet( SfxItemSet& rEditSet, const SfxItemSet& rSrcSet, const SfxItemSet* pCondSet )
+{
+ // Read Items
+
+ std::unique_ptr<SvxColorItem> aColorItem(std::make_unique<SvxColorItem>(EE_CHAR_COLOR)); // use item as-is
+ std::unique_ptr<SvxFontItem> aFontItem(std::make_unique<SvxFontItem>(EE_CHAR_FONTINFO)); // use item as-is
+ std::unique_ptr<SvxFontItem> aCjkFontItem(std::make_unique<SvxFontItem>(EE_CHAR_FONTINFO_CJK)); // use item as-is
+ std::unique_ptr<SvxFontItem> aCtlFontItem(std::make_unique<SvxFontItem>(EE_CHAR_FONTINFO_CTL)); // use item as-is
+ tools::Long nTHeight, nCjkTHeight, nCtlTHeight; // Twips
+ FontWeight eWeight, eCjkWeight, eCtlWeight;
+ std::unique_ptr<SvxUnderlineItem> aUnderlineItem(std::make_unique<SvxUnderlineItem>(LINESTYLE_NONE, EE_CHAR_UNDERLINE));
+ std::unique_ptr<SvxOverlineItem> aOverlineItem(std::make_unique<SvxOverlineItem>(LINESTYLE_NONE, EE_CHAR_OVERLINE));
+ bool bWordLine;
+ FontStrikeout eStrike;
+ FontItalic eItalic, eCjkItalic, eCtlItalic;
+ bool bOutline;
+ bool bShadow;
+ bool bForbidden;
+ FontEmphasisMark eEmphasis;
+ FontRelief eRelief;
+ LanguageType eLang, eCjkLang, eCtlLang;
+ bool bHyphenate;
+ SvxFrameDirection eDirection;
+
+ //TODO: additional parameter to control if language is needed?
+
+ if ( pCondSet )
+ {
+ lcl_populate(aColorItem, ATTR_FONT_COLOR, rSrcSet, pCondSet);
+ lcl_populate(aFontItem, ATTR_FONT, rSrcSet, pCondSet);
+ lcl_populate(aCjkFontItem, ATTR_CJK_FONT, rSrcSet, pCondSet);
+ lcl_populate(aCtlFontItem, ATTR_CTL_FONT, rSrcSet, pCondSet);
+
+ const SvxFontHeightItem* pFontHeightItem = pCondSet->GetItemIfSet( ATTR_FONT_HEIGHT );
+ if (!pFontHeightItem)
+ pFontHeightItem = &rSrcSet.Get( ATTR_FONT_HEIGHT );
+ nTHeight = pFontHeightItem->GetHeight();
+ pFontHeightItem = pCondSet->GetItemIfSet( ATTR_CJK_FONT_HEIGHT );
+ if ( !pFontHeightItem )
+ pFontHeightItem = &rSrcSet.Get( ATTR_CJK_FONT_HEIGHT );
+ nCjkTHeight = pFontHeightItem->GetHeight();
+ pFontHeightItem = pCondSet->GetItemIfSet( ATTR_CTL_FONT_HEIGHT );
+ if ( !pFontHeightItem )
+ pFontHeightItem = &rSrcSet.Get( ATTR_CTL_FONT_HEIGHT );
+ nCtlTHeight = pFontHeightItem->GetHeight();
+
+ const SvxWeightItem* pWeightItem = pCondSet->GetItemIfSet( ATTR_FONT_WEIGHT );
+ if ( !pWeightItem )
+ pWeightItem = &rSrcSet.Get( ATTR_FONT_WEIGHT );
+ eWeight = pWeightItem->GetValue();
+ pWeightItem = pCondSet->GetItemIfSet( ATTR_CJK_FONT_WEIGHT );
+ if ( !pWeightItem )
+ pWeightItem = &rSrcSet.Get( ATTR_CJK_FONT_WEIGHT );
+ eCjkWeight = pWeightItem->GetValue();
+ pWeightItem = pCondSet->GetItemIfSet( ATTR_CTL_FONT_WEIGHT );
+ if ( !pWeightItem )
+ pWeightItem = &rSrcSet.Get( ATTR_CTL_FONT_WEIGHT );
+ eCtlWeight = pWeightItem->GetValue();
+
+ const SvxPostureItem* pPostureItem = pCondSet->GetItemIfSet( ATTR_FONT_POSTURE );
+ if ( !pPostureItem )
+ pPostureItem = &rSrcSet.Get( ATTR_FONT_POSTURE );
+ eItalic = pPostureItem->GetValue();
+ pPostureItem = pCondSet->GetItemIfSet( ATTR_CJK_FONT_POSTURE );
+ if ( !pPostureItem )
+ pPostureItem = &rSrcSet.Get( ATTR_CJK_FONT_POSTURE );
+ eCjkItalic = pPostureItem->GetValue();
+ pPostureItem = pCondSet->GetItemIfSet( ATTR_CTL_FONT_POSTURE );
+ if ( !pPostureItem )
+ pPostureItem = &rSrcSet.Get( ATTR_CTL_FONT_POSTURE );
+ eCtlItalic = pPostureItem->GetValue();
+
+ lcl_populate(aUnderlineItem, ATTR_FONT_UNDERLINE, rSrcSet, pCondSet);
+ lcl_populate(aOverlineItem, ATTR_FONT_OVERLINE, rSrcSet, pCondSet);
+
+ const SvxWordLineModeItem* pWordLineModeItem = pCondSet->GetItemIfSet( ATTR_FONT_WORDLINE );
+ if ( !pWordLineModeItem )
+ pWordLineModeItem = &rSrcSet.Get( ATTR_FONT_WORDLINE );
+ bWordLine = pWordLineModeItem->GetValue();
+
+ const SvxCrossedOutItem* pCrossedOutItem = pCondSet->GetItemIfSet( ATTR_FONT_CROSSEDOUT );
+ if ( !pCrossedOutItem )
+ pCrossedOutItem = &rSrcSet.Get( ATTR_FONT_CROSSEDOUT );
+ eStrike = pCrossedOutItem->GetValue();
+
+ const SvxContourItem* pContourItem = pCondSet->GetItemIfSet( ATTR_FONT_CONTOUR );
+ if ( !pContourItem )
+ pContourItem = &rSrcSet.Get( ATTR_FONT_CONTOUR );
+ bOutline = pContourItem->GetValue();
+
+ const SvxShadowedItem* pShadowedItem = pCondSet->GetItemIfSet( ATTR_FONT_SHADOWED );
+ if ( !pShadowedItem )
+ pShadowedItem = &rSrcSet.Get( ATTR_FONT_SHADOWED );
+ bShadow = pShadowedItem->GetValue();
+
+ const SvxForbiddenRuleItem* pForbiddenRuleItem = pCondSet->GetItemIfSet( ATTR_FORBIDDEN_RULES );
+ if ( !pForbiddenRuleItem )
+ pForbiddenRuleItem = &rSrcSet.Get( ATTR_FORBIDDEN_RULES );
+ bForbidden = pForbiddenRuleItem->GetValue();
+
+ const SvxEmphasisMarkItem* pEmphasisMarkItem = pCondSet->GetItemIfSet( ATTR_FONT_EMPHASISMARK );
+ if ( !pEmphasisMarkItem )
+ pEmphasisMarkItem = &rSrcSet.Get( ATTR_FONT_EMPHASISMARK );
+ eEmphasis = pEmphasisMarkItem->GetEmphasisMark();
+ const SvxCharReliefItem* pCharReliefItem = pCondSet->GetItemIfSet( ATTR_FONT_RELIEF );
+ if ( !pCharReliefItem )
+ pCharReliefItem = &rSrcSet.Get( ATTR_FONT_RELIEF );
+ eRelief = pCharReliefItem->GetValue();
+
+ const SvxLanguageItem* pLanguageItem = pCondSet->GetItemIfSet( ATTR_FONT_LANGUAGE );
+ if ( !pLanguageItem )
+ pLanguageItem = &rSrcSet.Get( ATTR_FONT_LANGUAGE );
+ eLang = pLanguageItem->GetLanguage();
+ pLanguageItem = pCondSet->GetItemIfSet( ATTR_CJK_FONT_LANGUAGE );
+ if ( !pLanguageItem )
+ pLanguageItem = &rSrcSet.Get( ATTR_CJK_FONT_LANGUAGE );
+ eCjkLang = pLanguageItem->GetLanguage();
+ pLanguageItem = pCondSet->GetItemIfSet( ATTR_CTL_FONT_LANGUAGE );
+ if ( !pLanguageItem )
+ pLanguageItem = &rSrcSet.Get( ATTR_CTL_FONT_LANGUAGE );
+ eCtlLang = pLanguageItem->GetLanguage();
+
+ const ScHyphenateCell* pHyphenateCell = pCondSet->GetItemIfSet( ATTR_HYPHENATE );
+ if ( !pHyphenateCell )
+ pHyphenateCell = &rSrcSet.Get( ATTR_HYPHENATE );
+ bHyphenate = pHyphenateCell->GetValue();
+
+ const SvxFrameDirectionItem* pFrameDirectionItem = pCondSet->GetItemIfSet( ATTR_WRITINGDIR );
+ if ( !pFrameDirectionItem )
+ pFrameDirectionItem = &rSrcSet.Get( ATTR_WRITINGDIR );
+ eDirection = pFrameDirectionItem->GetValue();
+ }
+ else // Everything directly from Pattern
+ {
+ aColorItem.reset(rSrcSet.Get(ATTR_FONT_COLOR).Clone());
+ aFontItem.reset(rSrcSet.Get(ATTR_FONT).Clone());
+ aCjkFontItem.reset(rSrcSet.Get(ATTR_CJK_FONT).Clone());
+ aCtlFontItem.reset(rSrcSet.Get(ATTR_CTL_FONT).Clone());
+ nTHeight = rSrcSet.Get( ATTR_FONT_HEIGHT ).GetHeight();
+ nCjkTHeight = rSrcSet.Get( ATTR_CJK_FONT_HEIGHT ).GetHeight();
+ nCtlTHeight = rSrcSet.Get( ATTR_CTL_FONT_HEIGHT ).GetHeight();
+ eWeight = rSrcSet.Get( ATTR_FONT_WEIGHT ).GetValue();
+ eCjkWeight = rSrcSet.Get( ATTR_CJK_FONT_WEIGHT ).GetValue();
+ eCtlWeight = rSrcSet.Get( ATTR_CTL_FONT_WEIGHT ).GetValue();
+ eItalic = rSrcSet.Get( ATTR_FONT_POSTURE ).GetValue();
+ eCjkItalic = rSrcSet.Get( ATTR_CJK_FONT_POSTURE ).GetValue();
+ eCtlItalic = rSrcSet.Get( ATTR_CTL_FONT_POSTURE ).GetValue();
+ aUnderlineItem.reset(rSrcSet.Get(ATTR_FONT_UNDERLINE).Clone());
+ aOverlineItem.reset(rSrcSet.Get(ATTR_FONT_OVERLINE).Clone());
+ bWordLine = rSrcSet.Get( ATTR_FONT_WORDLINE ).GetValue();
+ eStrike = rSrcSet.Get( ATTR_FONT_CROSSEDOUT ).GetValue();
+ bOutline = rSrcSet.Get( ATTR_FONT_CONTOUR ).GetValue();
+ bShadow = rSrcSet.Get( ATTR_FONT_SHADOWED ).GetValue();
+ bForbidden = rSrcSet.Get( ATTR_FORBIDDEN_RULES ).GetValue();
+ eEmphasis = rSrcSet.Get( ATTR_FONT_EMPHASISMARK ).GetEmphasisMark();
+ eRelief = rSrcSet.Get( ATTR_FONT_RELIEF ).GetValue();
+ eLang = rSrcSet.Get( ATTR_FONT_LANGUAGE ).GetLanguage();
+ eCjkLang = rSrcSet.Get( ATTR_CJK_FONT_LANGUAGE ).GetLanguage();
+ eCtlLang = rSrcSet.Get( ATTR_CTL_FONT_LANGUAGE ).GetLanguage();
+ bHyphenate = rSrcSet.Get( ATTR_HYPHENATE ).GetValue();
+ eDirection = rSrcSet.Get( ATTR_WRITINGDIR ).GetValue();
+ }
+
+ // Expect to be compatible to LogicToLogic, ie. 2540/1440 = 127/72, and round
+
+ tools::Long nHeight = convertTwipToMm100(nTHeight);
+ tools::Long nCjkHeight = convertTwipToMm100(nCjkTHeight);
+ tools::Long nCtlHeight = convertTwipToMm100(nCtlTHeight);
+
+ // put items into EditEngine ItemSet
+
+ if ( aColorItem->GetValue() == COL_AUTO )
+ {
+ // When cell attributes are converted to EditEngine paragraph attributes,
+ // don't create a hard item for automatic color, because that would be converted
+ // to black when the item's Store method is used in CreateTransferable/WriteBin.
+ // COL_AUTO is the EditEngine's pool default, so ClearItem will result in automatic
+ // color, too, without having to store the item.
+ rEditSet.ClearItem( EE_CHAR_COLOR );
+ }
+ else
+ {
+ // tdf#125054 adapt WhichID
+ rEditSet.Put( std::move(aColorItem), EE_CHAR_COLOR );
+ }
+
+ // tdf#125054 adapt WhichID
+ rEditSet.Put( std::move(aFontItem), EE_CHAR_FONTINFO );
+ rEditSet.Put( std::move(aCjkFontItem), EE_CHAR_FONTINFO_CJK );
+ rEditSet.Put( std::move(aCtlFontItem), EE_CHAR_FONTINFO_CTL );
+
+ rEditSet.Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ rEditSet.Put( SvxFontHeightItem( nCjkHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ rEditSet.Put( SvxFontHeightItem( nCtlHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+ rEditSet.Put( SvxWeightItem ( eWeight, EE_CHAR_WEIGHT ) );
+ rEditSet.Put( SvxWeightItem ( eCjkWeight, EE_CHAR_WEIGHT_CJK ) );
+ rEditSet.Put( SvxWeightItem ( eCtlWeight, EE_CHAR_WEIGHT_CTL ) );
+
+ // tdf#125054 adapt WhichID
+ rEditSet.Put( std::move(aUnderlineItem), EE_CHAR_UNDERLINE );
+ rEditSet.Put( std::move(aOverlineItem), EE_CHAR_OVERLINE );
+
+ rEditSet.Put( SvxWordLineModeItem( bWordLine, EE_CHAR_WLM ) );
+ rEditSet.Put( SvxCrossedOutItem( eStrike, EE_CHAR_STRIKEOUT ) );
+ rEditSet.Put( SvxPostureItem ( eItalic, EE_CHAR_ITALIC ) );
+ rEditSet.Put( SvxPostureItem ( eCjkItalic, EE_CHAR_ITALIC_CJK ) );
+ rEditSet.Put( SvxPostureItem ( eCtlItalic, EE_CHAR_ITALIC_CTL ) );
+ rEditSet.Put( SvxContourItem ( bOutline, EE_CHAR_OUTLINE ) );
+ rEditSet.Put( SvxShadowedItem ( bShadow, EE_CHAR_SHADOW ) );
+ rEditSet.Put( SvxForbiddenRuleItem(bForbidden, EE_PARA_FORBIDDENRULES) );
+ rEditSet.Put( SvxEmphasisMarkItem( eEmphasis, EE_CHAR_EMPHASISMARK ) );
+ rEditSet.Put( SvxCharReliefItem( eRelief, EE_CHAR_RELIEF ) );
+ rEditSet.Put( SvxLanguageItem ( eLang, EE_CHAR_LANGUAGE ) );
+ rEditSet.Put( SvxLanguageItem ( eCjkLang, EE_CHAR_LANGUAGE_CJK ) );
+ rEditSet.Put( SvxLanguageItem ( eCtlLang, EE_CHAR_LANGUAGE_CTL ) );
+ rEditSet.Put( SfxBoolItem ( EE_PARA_HYPHENATE, bHyphenate ) );
+ rEditSet.Put( SvxFrameDirectionItem( eDirection, EE_PARA_WRITINGDIR ) );
+
+ // Script spacing is always off.
+ // The cell attribute isn't used here as long as there is no UI to set it
+ // (don't evaluate attributes that can't be changed).
+ // If a locale-dependent default is needed, it has to go into the cell
+ // style, like the fonts.
+ rEditSet.Put( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) );
+}
+
+void ScPatternAttr::FillEditItemSet( SfxItemSet* pEditSet, const SfxItemSet* pCondSet ) const
+{
+ if( pEditSet )
+ FillToEditItemSet( *pEditSet, GetItemSet(), pCondSet );
+}
+
+void ScPatternAttr::GetFromEditItemSet( SfxItemSet& rDestSet, const SfxItemSet& rEditSet )
+{
+ if (const SvxColorItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_COLOR))
+ rDestSet.Put( *pItem, ATTR_FONT_COLOR );
+
+ if (const SvxFontItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_FONTINFO))
+ rDestSet.Put( *pItem, ATTR_FONT );
+ if (const SvxFontItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_FONTINFO_CJK))
+ rDestSet.Put( *pItem, ATTR_CJK_FONT );
+ if (const SvxFontItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_FONTINFO_CTL))
+ rDestSet.Put( *pItem, ATTR_CTL_FONT );
+
+ if (const SvxFontHeightItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_FONTHEIGHT))
+ rDestSet.Put( SvxFontHeightItem(o3tl::toTwips(pItem->GetHeight(), o3tl::Length::mm100),
+ 100, ATTR_FONT_HEIGHT ) );
+ if (const SvxFontHeightItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_FONTHEIGHT_CJK))
+ rDestSet.Put( SvxFontHeightItem(o3tl::toTwips(pItem->GetHeight(), o3tl::Length::mm100),
+ 100, ATTR_CJK_FONT_HEIGHT ) );
+ if (const SvxFontHeightItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_FONTHEIGHT_CTL))
+ rDestSet.Put( SvxFontHeightItem(o3tl::toTwips(pItem->GetHeight(), o3tl::Length::mm100),
+ 100, ATTR_CTL_FONT_HEIGHT ) );
+
+ if (const SvxWeightItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_WEIGHT))
+ rDestSet.Put( SvxWeightItem( pItem->GetValue(),
+ ATTR_FONT_WEIGHT) );
+ if (const SvxWeightItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_WEIGHT_CJK))
+ rDestSet.Put( SvxWeightItem( pItem->GetValue(),
+ ATTR_CJK_FONT_WEIGHT) );
+ if (const SvxWeightItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_WEIGHT_CTL))
+ rDestSet.Put( SvxWeightItem( pItem->GetValue(),
+ ATTR_CTL_FONT_WEIGHT) );
+
+ // SvxTextLineItem contains enum and color
+ if (const SvxUnderlineItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_UNDERLINE))
+ rDestSet.Put( *pItem, ATTR_FONT_UNDERLINE );
+ if (const SvxOverlineItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_OVERLINE))
+ rDestSet.Put( *pItem, ATTR_FONT_OVERLINE );
+ if (const SvxWordLineModeItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_WLM))
+ rDestSet.Put( SvxWordLineModeItem( pItem->GetValue(),
+ ATTR_FONT_WORDLINE) );
+
+ if (const SvxCrossedOutItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_STRIKEOUT))
+ rDestSet.Put( SvxCrossedOutItem( pItem->GetValue(),
+ ATTR_FONT_CROSSEDOUT) );
+
+ if (const SvxPostureItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_ITALIC))
+ rDestSet.Put( SvxPostureItem( pItem->GetValue(),
+ ATTR_FONT_POSTURE) );
+ if (const SvxPostureItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_ITALIC_CJK))
+ rDestSet.Put( SvxPostureItem( pItem->GetValue(),
+ ATTR_CJK_FONT_POSTURE) );
+ if (const SvxPostureItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_ITALIC_CTL))
+ rDestSet.Put( SvxPostureItem( pItem->GetValue(),
+ ATTR_CTL_FONT_POSTURE) );
+
+ if (const SvxContourItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_OUTLINE))
+ rDestSet.Put( SvxContourItem( pItem->GetValue(),
+ ATTR_FONT_CONTOUR) );
+ if (const SvxShadowedItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_SHADOW))
+ rDestSet.Put( SvxShadowedItem( pItem->GetValue(),
+ ATTR_FONT_SHADOWED) );
+ if (const SvxEmphasisMarkItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_EMPHASISMARK))
+ rDestSet.Put( SvxEmphasisMarkItem( pItem->GetEmphasisMark(),
+ ATTR_FONT_EMPHASISMARK) );
+ if (const SvxCharReliefItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_RELIEF))
+ rDestSet.Put( SvxCharReliefItem( pItem->GetValue(),
+ ATTR_FONT_RELIEF) );
+
+ if (const SvxLanguageItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_LANGUAGE))
+ rDestSet.Put( SvxLanguageItem(pItem->GetValue(), ATTR_FONT_LANGUAGE) );
+ if (const SvxLanguageItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_LANGUAGE_CJK))
+ rDestSet.Put( SvxLanguageItem(pItem->GetValue(), ATTR_CJK_FONT_LANGUAGE) );
+ if (const SvxLanguageItem* pItem = rEditSet.GetItemIfSet(EE_CHAR_LANGUAGE_CTL))
+ rDestSet.Put( SvxLanguageItem(pItem->GetValue(), ATTR_CTL_FONT_LANGUAGE) );
+
+ const SvxAdjustItem* pAdjustItem = rEditSet.GetItemIfSet(EE_PARA_JUST);
+
+ if (!pAdjustItem)
+ return;
+
+ SvxCellHorJustify eVal;
+ switch ( pAdjustItem->GetAdjust() )
+ {
+ case SvxAdjust::Left:
+ // EditEngine Default is always set in the GetAttribs() ItemSet !
+ // whether left or right, is decided in text / number
+ eVal = SvxCellHorJustify::Standard;
+ break;
+ case SvxAdjust::Right:
+ eVal = SvxCellHorJustify::Right;
+ break;
+ case SvxAdjust::Block:
+ eVal = SvxCellHorJustify::Block;
+ break;
+ case SvxAdjust::Center:
+ eVal = SvxCellHorJustify::Center;
+ break;
+ case SvxAdjust::BlockLine:
+ eVal = SvxCellHorJustify::Block;
+ break;
+ case SvxAdjust::End:
+ eVal = SvxCellHorJustify::Right;
+ break;
+ default:
+ eVal = SvxCellHorJustify::Standard;
+ }
+ if ( eVal != SvxCellHorJustify::Standard )
+ rDestSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY) );
+}
+
+void ScPatternAttr::GetFromEditItemSet( const SfxItemSet* pEditSet )
+{
+ if( !pEditSet )
+ return;
+ GetFromEditItemSet( GetItemSet(), *pEditSet );
+ mxVisible.reset();
+}
+
+void ScPatternAttr::FillEditParaItems( SfxItemSet* pEditSet ) const
+{
+ // already there in GetFromEditItemSet, but not in FillEditItemSet
+ // Default horizontal alignment is always implemented as left
+
+ const SfxItemSet& rMySet = GetItemSet();
+
+ SvxCellHorJustify eHorJust = rMySet.Get(ATTR_HOR_JUSTIFY).GetValue();
+
+ SvxAdjust eSvxAdjust;
+ switch (eHorJust)
+ {
+ case SvxCellHorJustify::Right: eSvxAdjust = SvxAdjust::Right; break;
+ case SvxCellHorJustify::Center: eSvxAdjust = SvxAdjust::Center; break;
+ case SvxCellHorJustify::Block: eSvxAdjust = SvxAdjust::Block; break;
+ default: eSvxAdjust = SvxAdjust::Left; break;
+ }
+ pEditSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
+}
+
+void ScPatternAttr::DeleteUnchanged( const ScPatternAttr* pOldAttrs )
+{
+ SfxItemSet& rThisSet = GetItemSet();
+ const SfxItemSet& rOldSet = pOldAttrs->GetItemSet();
+
+ const SfxPoolItem* pThisItem;
+ const SfxPoolItem* pOldItem;
+
+ for ( sal_uInt16 nSubWhich=ATTR_PATTERN_START; nSubWhich<=ATTR_PATTERN_END; nSubWhich++ )
+ {
+ // only items that are set are interesting
+ if ( rThisSet.GetItemState( nSubWhich, false, &pThisItem ) == SfxItemState::SET )
+ {
+ SfxItemState eOldState = rOldSet.GetItemState( nSubWhich, true, &pOldItem );
+ if ( eOldState == SfxItemState::SET )
+ {
+ // item is set in OldAttrs (or its parent) -> compare pointers
+ if (SfxPoolItem::areSame( pThisItem, pOldItem ))
+ {
+ rThisSet.ClearItem( nSubWhich );
+ mxVisible.reset();
+ }
+ }
+ else if ( eOldState != SfxItemState::DONTCARE )
+ {
+ // not set in OldAttrs -> compare item value to default item
+ if ( *pThisItem == rThisSet.GetPool()->GetDefaultItem( nSubWhich ) )
+ {
+ rThisSet.ClearItem( nSubWhich );
+ mxVisible.reset();
+ }
+ }
+ }
+ }
+}
+
+bool ScPatternAttr::HasItemsSet( const sal_uInt16* pWhich ) const
+{
+ const SfxItemSet& rSet = GetItemSet();
+ for (sal_uInt16 i=0; pWhich[i]; i++)
+ if ( rSet.GetItemState( pWhich[i], false ) == SfxItemState::SET )
+ return true;
+ return false;
+}
+
+void ScPatternAttr::ClearItems( const sal_uInt16* pWhich )
+{
+ SfxItemSet& rSet = GetItemSet();
+ for (sal_uInt16 i=0; pWhich[i]; i++)
+ rSet.ClearItem(pWhich[i]);
+ mxVisible.reset();
+}
+
+static SfxStyleSheetBase* lcl_CopyStyleToPool
+ (
+ SfxStyleSheetBase* pSrcStyle,
+ SfxStyleSheetBasePool* pSrcPool,
+ SfxStyleSheetBasePool* pDestPool,
+ const SvNumberFormatterIndexTable* pFormatExchangeList
+ )
+{
+ if ( !pSrcStyle || !pDestPool || !pSrcPool )
+ {
+ OSL_FAIL( "CopyStyleToPool: Invalid Arguments :-/" );
+ return nullptr;
+ }
+
+ const OUString aStrSrcStyle = pSrcStyle->GetName();
+ const SfxStyleFamily eFamily = pSrcStyle->GetFamily();
+ SfxStyleSheetBase* pDestStyle = pDestPool->Find( aStrSrcStyle, eFamily );
+
+ if ( !pDestStyle )
+ {
+ const OUString aStrParent = pSrcStyle->GetParent();
+ const SfxItemSet& rSrcSet = pSrcStyle->GetItemSet();
+
+ pDestStyle = &pDestPool->Make( aStrSrcStyle, eFamily, SfxStyleSearchBits::UserDefined );
+ SfxItemSet& rDestSet = pDestStyle->GetItemSet();
+ rDestSet.Put( rSrcSet );
+
+ // number format exchange list has to be handled here, too
+ // (only called for cell styles)
+
+ const SfxUInt32Item* pSrcItem;
+ if ( pFormatExchangeList &&
+ (pSrcItem = rSrcSet.GetItemIfSet( ATTR_VALUE_FORMAT, false )) )
+ {
+ sal_uLong nOldFormat = pSrcItem->GetValue();
+ SvNumberFormatterIndexTable::const_iterator it = pFormatExchangeList->find(nOldFormat);
+ if (it != pFormatExchangeList->end())
+ {
+ sal_uInt32 nNewFormat = it->second;
+ rDestSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ }
+ }
+
+ // if necessary create derivative Styles, if not available:
+
+ if ( ScResId(STR_STYLENAME_STANDARD) != aStrParent &&
+ aStrSrcStyle != aStrParent &&
+ !pDestPool->Find( aStrParent, eFamily ) )
+ {
+ lcl_CopyStyleToPool( pSrcPool->Find( aStrParent, eFamily ),
+ pSrcPool, pDestPool, pFormatExchangeList );
+ }
+
+ pDestStyle->SetParent( aStrParent );
+ }
+
+ return pDestStyle;
+}
+
+ScPatternAttr* ScPatternAttr::PutInPool( ScDocument* pDestDoc, ScDocument* pSrcDoc ) const
+{
+ const SfxItemSet* pSrcSet = &GetItemSet();
+
+ ScPatternAttr aDestPattern( pDestDoc->GetPool() );
+ SfxItemSet* pDestSet = &aDestPattern.GetItemSet();
+
+ // Copy cell pattern style to other document:
+
+ if ( pDestDoc != pSrcDoc )
+ {
+ OSL_ENSURE( pStyle, "Missing Pattern-Style! :-/" );
+
+ // if pattern in DestDoc is available, use this, otherwise copy
+ // parent style to style or create if necessary and attach DestDoc
+
+ SfxStyleSheetBase* pStyleCpy = lcl_CopyStyleToPool( pStyle,
+ pSrcDoc->GetStyleSheetPool(),
+ pDestDoc->GetStyleSheetPool(),
+ pDestDoc->GetFormatExchangeList() );
+
+ aDestPattern.SetStyleSheet( static_cast<ScStyleSheet*>(pStyleCpy) );
+ }
+
+ for ( sal_uInt16 nAttrId = ATTR_PATTERN_START; nAttrId <= ATTR_PATTERN_END; nAttrId++ )
+ {
+ const SfxPoolItem* pSrcItem;
+ SfxItemState eItemState = pSrcSet->GetItemState( nAttrId, false, &pSrcItem );
+ if (eItemState==SfxItemState::SET)
+ {
+ std::unique_ptr<SfxPoolItem> pNewItem;
+
+ if ( nAttrId == ATTR_VALIDDATA )
+ {
+ // Copy validity to the new document
+
+ sal_uInt32 nNewIndex = 0;
+ ScValidationDataList* pSrcList = pSrcDoc->GetValidationList();
+ if ( pSrcList )
+ {
+ sal_uInt32 nOldIndex = static_cast<const SfxUInt32Item*>(pSrcItem)->GetValue();
+ const ScValidationData* pOldData = pSrcList->GetData( nOldIndex );
+ if ( pOldData )
+ nNewIndex = pDestDoc->AddValidationEntry( *pOldData );
+ }
+ pNewItem.reset(new SfxUInt32Item( ATTR_VALIDDATA, nNewIndex ));
+ }
+ else if ( nAttrId == ATTR_VALUE_FORMAT && pDestDoc->GetFormatExchangeList() )
+ {
+ // Number format to Exchange List
+
+ sal_uLong nOldFormat = static_cast<const SfxUInt32Item*>(pSrcItem)->GetValue();
+ SvNumberFormatterIndexTable::const_iterator it = pDestDoc->GetFormatExchangeList()->find(nOldFormat);
+ if (it != pDestDoc->GetFormatExchangeList()->end())
+ {
+ sal_uInt32 nNewFormat = it->second;
+ pNewItem.reset(new SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ));
+ }
+ }
+
+ if ( pNewItem )
+ {
+ pDestSet->Put(std::move(pNewItem));
+ }
+ else
+ pDestSet->Put(*pSrcItem);
+ }
+ }
+
+ ScPatternAttr* pPatternAttr = const_cast<ScPatternAttr*>( &pDestDoc->GetPool()->DirectPutItemInPool(aDestPattern) );
+ return pPatternAttr;
+}
+
+bool ScPatternAttr::IsVisible() const
+{
+ if (!mxVisible.has_value())
+ mxVisible = CalcVisible();
+ return *mxVisible;
+}
+
+bool ScPatternAttr::CalcVisible() const
+{
+ const SfxItemSet& rSet = GetItemSet();
+
+ if ( const SvxBrushItem* pItem = rSet.GetItemIfSet( ATTR_BACKGROUND ) )
+ if ( pItem->GetColor() != COL_TRANSPARENT )
+ return true;
+
+ if ( const SvxBoxItem* pBoxItem = rSet.GetItemIfSet( ATTR_BORDER ) )
+ {
+ if ( pBoxItem->GetTop() || pBoxItem->GetBottom() ||
+ pBoxItem->GetLeft() || pBoxItem->GetRight() )
+ return true;
+ }
+
+ if ( const SvxLineItem* pItem = rSet.GetItemIfSet( ATTR_BORDER_TLBR ) )
+ if( pItem->GetLine() )
+ return true;
+
+ if ( const SvxLineItem* pItem = rSet.GetItemIfSet( ATTR_BORDER_BLTR ) )
+ if( pItem->GetLine() )
+ return true;
+
+ if ( const SvxShadowItem* pItem = rSet.GetItemIfSet( ATTR_SHADOW ) )
+ if ( pItem->GetLocation() != SvxShadowLocation::NONE )
+ return true;
+
+ return false;
+}
+
+bool ScPatternAttr::IsVisibleEqual( const ScPatternAttr& rOther ) const
+{
+ // This method is hot, so we do an optimised comparison here, by
+ // walking the two itemsets in parallel, avoiding doing repeated searches.
+ auto IsInterestingWhich = [](sal_uInt16 n)
+ {
+ return n == ATTR_BORDER_TLBR || n == ATTR_BORDER_BLTR || n == ATTR_BACKGROUND
+ || n == ATTR_BORDER || n == ATTR_SHADOW;
+ };
+ SfxWhichIter aIter1(GetItemSet());
+ SfxWhichIter aIter2(rOther.GetItemSet());
+ sal_uInt16 nWhich1 = aIter1.FirstWhich();
+ sal_uInt16 nWhich2 = aIter2.FirstWhich();
+ for (;;)
+ {
+ while (nWhich1 != nWhich2)
+ {
+ SfxWhichIter* pIterToIncrement;
+ sal_uInt16* pSmallerWhich;
+ if (nWhich1 == 0 || nWhich1 > nWhich2)
+ {
+ pSmallerWhich = &nWhich2;
+ pIterToIncrement = &aIter2;
+ }
+ else
+ {
+ pSmallerWhich = &nWhich1;
+ pIterToIncrement = &aIter1;
+ }
+
+ if (IsInterestingWhich(*pSmallerWhich))
+ {
+ // the iter with larger which has already passed this point, and has no interesting
+ // item available in the other - so indeed these are unequal
+ return false;
+ }
+
+ *pSmallerWhich = pIterToIncrement->NextWhich();
+ }
+
+ // Here nWhich1 == nWhich2
+
+ if (!nWhich1 /* && !nWhich2*/)
+ return true;
+
+ if (IsInterestingWhich(nWhich1))
+ {
+ const SfxPoolItem* pItem1 = nullptr;
+ const SfxPoolItem* pItem2 = nullptr;
+ SfxItemState state1 = aIter1.GetItemState(true, &pItem1);
+ SfxItemState state2 = aIter2.GetItemState(true, &pItem2);
+ if (state1 != state2
+ && (state1 < SfxItemState::DEFAULT || state2 < SfxItemState::DEFAULT))
+ return false;
+ if (!SfxPoolItem::areSame(pItem1, pItem2))
+ return false;
+ }
+ nWhich1 = aIter1.NextWhich();
+ nWhich2 = aIter2.NextWhich();
+ }
+ //TODO: also here only check really visible values !!!
+}
+
+const OUString* ScPatternAttr::GetStyleName() const
+{
+ return pName ? &*pName : ( pStyle ? &pStyle->GetName() : nullptr );
+}
+
+void ScPatternAttr::SetStyleSheet( ScStyleSheet* pNewStyle, bool bClearDirectFormat )
+{
+ if (pNewStyle)
+ {
+ SfxItemSet& rPatternSet = GetItemSet();
+ const SfxItemSet& rStyleSet = pNewStyle->GetItemSet();
+
+ if (bClearDirectFormat)
+ {
+ for (sal_uInt16 i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END; i++)
+ {
+ if (rStyleSet.GetItemState(i) == SfxItemState::SET)
+ rPatternSet.ClearItem(i);
+ }
+ }
+ rPatternSet.SetParent(&pNewStyle->GetItemSet());
+ pStyle = pNewStyle;
+ pName.reset();
+ }
+ else
+ {
+ OSL_FAIL( "ScPatternAttr::SetStyleSheet( NULL ) :-|" );
+ GetItemSet().SetParent(nullptr);
+ pStyle = nullptr;
+ }
+ mxVisible.reset();
+}
+
+void ScPatternAttr::UpdateStyleSheet(const ScDocument& rDoc)
+{
+ if (pName)
+ {
+ pStyle = static_cast<ScStyleSheet*>(rDoc.GetStyleSheetPool()->Find(*pName, SfxStyleFamily::Para));
+
+ // use Standard if Style is not found,
+ // to avoid empty display in Toolbox-Controller
+ // Assumes that "Standard" is always the 1st entry!
+ if (!pStyle)
+ {
+ std::unique_ptr<SfxStyleSheetIterator> pIter = rDoc.GetStyleSheetPool()->CreateIterator(SfxStyleFamily::Para);
+ pStyle = dynamic_cast< ScStyleSheet* >(pIter->First());
+ }
+
+ if (pStyle)
+ {
+ GetItemSet().SetParent(&pStyle->GetItemSet());
+ pName.reset();
+ }
+ }
+ else
+ pStyle = nullptr;
+ mxVisible.reset();
+}
+
+void ScPatternAttr::StyleToName()
+{
+ // Style was deleted, remember name:
+
+ if ( pStyle )
+ {
+ pName = pStyle->GetName();
+ pStyle = nullptr;
+ GetItemSet().SetParent( nullptr );
+ mxVisible.reset();
+ }
+}
+
+bool ScPatternAttr::IsSymbolFont() const
+{
+ if( const SvxFontItem* pItem = GetItemSet().GetItemIfSet( ATTR_FONT ) )
+ return pItem->GetCharSet() == RTL_TEXTENCODING_SYMBOL;
+ else
+ return false;
+}
+
+namespace {
+
+sal_uInt32 getNumberFormatKey(const SfxItemSet& rSet)
+{
+ return rSet.Get(ATTR_VALUE_FORMAT).GetValue();
+}
+
+LanguageType getLanguageType(const SfxItemSet& rSet)
+{
+ return rSet.Get(ATTR_LANGUAGE_FORMAT).GetLanguage();
+}
+
+}
+
+sal_uInt32 ScPatternAttr::GetNumberFormat( SvNumberFormatter* pFormatter ) const
+{
+ sal_uInt32 nFormat = getNumberFormatKey(GetItemSet());
+ LanguageType eLang = getLanguageType(GetItemSet());
+ if ( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLang == LANGUAGE_SYSTEM )
+ ; // it remains as it is
+ else if ( pFormatter )
+ nFormat = pFormatter->GetFormatForLanguageIfBuiltIn( nFormat, eLang );
+ return nFormat;
+}
+
+// the same if conditional formatting is in play:
+
+sal_uInt32 ScPatternAttr::GetNumberFormat( SvNumberFormatter* pFormatter,
+ const SfxItemSet* pCondSet ) const
+{
+ assert(pFormatter);
+ if (!pCondSet)
+ return GetNumberFormat(pFormatter);
+
+ // Conditional format takes precedence over style and even hard format.
+
+ sal_uInt32 nFormat;
+ LanguageType eLang;
+ if (pCondSet->GetItemState(ATTR_VALUE_FORMAT) == SfxItemState::SET )
+ {
+ nFormat = getNumberFormatKey(*pCondSet);
+ if (pCondSet->GetItemState(ATTR_LANGUAGE_FORMAT) == SfxItemState::SET)
+ eLang = getLanguageType(*pCondSet);
+ else
+ eLang = getLanguageType(GetItemSet());
+ }
+ else
+ {
+ nFormat = getNumberFormatKey(GetItemSet());
+ eLang = getLanguageType(GetItemSet());
+ }
+
+ return pFormatter->GetFormatForLanguageIfBuiltIn(nFormat, eLang);
+}
+
+const SfxPoolItem& ScPatternAttr::GetItem( sal_uInt16 nWhich, const SfxItemSet& rItemSet, const SfxItemSet* pCondSet )
+{
+ const SfxPoolItem* pCondItem;
+ if ( pCondSet && pCondSet->GetItemState( nWhich, true, &pCondItem ) == SfxItemState::SET )
+ return *pCondItem;
+ return rItemSet.Get(nWhich);
+}
+
+const SfxPoolItem& ScPatternAttr::GetItem( sal_uInt16 nSubWhich, const SfxItemSet* pCondSet ) const
+{
+ return GetItem( nSubWhich, GetItemSet(), pCondSet );
+}
+
+// GetRotateVal is tested before ATTR_ORIENTATION
+
+Degree100 ScPatternAttr::GetRotateVal( const SfxItemSet* pCondSet ) const
+{
+ Degree100 nAttrRotate(0);
+ if ( GetCellOrientation() == SvxCellOrientation::Standard )
+ {
+ bool bRepeat = ( GetItem(ATTR_HOR_JUSTIFY, pCondSet).
+ GetValue() == SvxCellHorJustify::Repeat );
+ // ignore orientation/rotation if "repeat" is active
+ if ( !bRepeat )
+ nAttrRotate = GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue();
+ }
+ return nAttrRotate;
+}
+
+ScRotateDir ScPatternAttr::GetRotateDir( const SfxItemSet* pCondSet ) const
+{
+ ScRotateDir nRet = ScRotateDir::NONE;
+
+ Degree100 nAttrRotate = GetRotateVal( pCondSet );
+ if ( nAttrRotate )
+ {
+ SvxRotateMode eRotMode = GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD || nAttrRotate == 18000_deg100 )
+ nRet = ScRotateDir::Standard;
+ else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
+ nRet = ScRotateDir::Center;
+ else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
+ {
+ Degree100 nRot180 = nAttrRotate % 18000_deg100; // 1/100 degrees
+ if ( nRot180 == 9000_deg100 )
+ nRet = ScRotateDir::Center;
+ else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000_deg100 ) ||
+ ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000_deg100 ) )
+ nRet = ScRotateDir::Left;
+ else
+ nRet = ScRotateDir::Right;
+ }
+ }
+
+ return nRet;
+}
+
+void ScPatternAttr::SetPAKey(sal_uInt64 nKey)
+{
+ mnPAKey = nKey;
+}
+
+sal_uInt64 ScPatternAttr::GetPAKey() const
+{
+ return mnPAKey;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/pivot2.cxx b/sc/source/core/data/pivot2.cxx
new file mode 100644
index 0000000000..c854b452c2
--- /dev/null
+++ b/sc/source/core/data/pivot2.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 <pivot.hxx>
+#include <utility>
+
+#if DEBUG_PIVOT_TABLE
+using std::cout;
+using std::endl;
+#endif
+
+
+
+// ScDPName
+
+ScDPName::ScDPName() : mnDupCount(0)
+{}
+
+ScDPName::ScDPName(OUString aName, OUString aLayoutName, sal_uInt8 nDupCount) :
+ maName(std::move(aName)), maLayoutName(std::move(aLayoutName)), mnDupCount(nDupCount)
+{}
+
+// ScDPLabelData
+
+ScDPLabelData::Member::Member() :
+ mbVisible(true),
+ mbShowDetails(true)
+{}
+
+OUString const & ScDPLabelData::Member::getDisplayName() const
+{
+ if (!maLayoutName.isEmpty())
+ return maLayoutName;
+
+ return maName;
+}
+
+ScDPLabelData::ScDPLabelData() :
+ mnCol(-1),
+ mnOriginalDim(-1),
+ mnFuncMask(PivotFunc::NONE),
+ mnUsedHier(0),
+ mnFlags(0),
+ mnDupCount(0),
+ mbShowAll(false),
+ mbIsValue(false),
+ mbDataLayout(false),
+ mbRepeatItemLabels(false)
+{}
+
+OUString const & ScDPLabelData::getDisplayName() const
+{
+ if (!maLayoutName.isEmpty())
+ return maLayoutName;
+
+ return maName;
+}
+
+// ScPivotField
+
+ScPivotField::ScPivotField(SCCOL nNewCol) :
+ mnOriginalDim(-1),
+ nFuncMask(PivotFunc::NONE),
+ nCol(nNewCol),
+ mnDupCount(0)
+{}
+
+tools::Long ScPivotField::getOriginalDim() const
+{
+ return mnOriginalDim >= 0 ? mnOriginalDim : static_cast<tools::Long>(nCol);
+}
+
+// ScPivotParam
+
+ScPivotParam::ScPivotParam() :
+ nCol(0), nRow(0), nTab(0),
+ bIgnoreEmptyRows(false), bDetectCategories(false),
+ bMakeTotalCol(true), bMakeTotalRow(true)
+{}
+
+ScPivotParam::ScPivotParam( const ScPivotParam& r )
+ : nCol( r.nCol ), nRow( r.nRow ), nTab( r.nTab ),
+ maPageFields(r.maPageFields),
+ maColFields(r.maColFields),
+ maRowFields(r.maRowFields),
+ maDataFields(r.maDataFields),
+ bIgnoreEmptyRows(r.bIgnoreEmptyRows),
+ bDetectCategories(r.bDetectCategories),
+ bMakeTotalCol(r.bMakeTotalCol),
+ bMakeTotalRow(r.bMakeTotalRow)
+{
+ SetLabelData(r.maLabelArray);
+}
+
+ScPivotParam::~ScPivotParam()
+{}
+
+void ScPivotParam::SetLabelData(const ScDPLabelDataVector& rVector)
+{
+ ScDPLabelDataVector aNewArray;
+ aNewArray.reserve(rVector.size());
+ for (const auto& rxData : rVector)
+ {
+ aNewArray.push_back(std::make_unique<ScDPLabelData>(*rxData));
+ }
+ maLabelArray.swap(aNewArray);
+}
+
+ScPivotParam& ScPivotParam::operator=( const ScPivotParam& rPivotParam )
+{
+ nCol = rPivotParam.nCol;
+ nRow = rPivotParam.nRow;
+ nTab = rPivotParam.nTab;
+ bIgnoreEmptyRows = rPivotParam.bIgnoreEmptyRows;
+ bDetectCategories = rPivotParam.bDetectCategories;
+ bMakeTotalCol = rPivotParam.bMakeTotalCol;
+ bMakeTotalRow = rPivotParam.bMakeTotalRow;
+
+ maPageFields = rPivotParam.maPageFields;
+ maColFields = rPivotParam.maColFields;
+ maRowFields = rPivotParam.maRowFields;
+ maDataFields = rPivotParam.maDataFields;
+
+ SetLabelData(rPivotParam.maLabelArray);
+ return *this;
+}
+
+// ScPivotFuncData
+
+ScPivotFuncData::ScPivotFuncData( SCCOL nCol, PivotFunc nFuncMask ) :
+ mnOriginalDim(-1),
+ mnFuncMask(nFuncMask),
+ mnCol( nCol ),
+ mnDupCount(0)
+{}
+
+#if DEBUG_PIVOT_TABLE
+void ScPivotFuncData::Dump() const
+{
+ cout << "ScPivotFuncData: (col=" << mnCol << ", original dim=" << mnOriginalDim
+ << ", func mask=" << static_cast<int>(mnFuncMask) << ", duplicate count=" << static_cast<int>(mnDupCount)
+ << ")" << endl;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/poolcach.cxx b/sc/source/core/data/poolcach.cxx
new file mode 100644
index 0000000000..d929933d0e
--- /dev/null
+++ b/sc/source/core/data/poolcach.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 <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/setitem.hxx>
+#include <poolcach.hxx>
+#include <tools/debug.hxx>
+#include <patattr.hxx>
+
+ScItemPoolCache::ScItemPoolCache( SfxItemPool *pItemPool,
+ const SfxPoolItem *pPutItem ):
+ pPool(pItemPool),
+ pSetToPut( nullptr ),
+ pItemToPut( &pItemPool->DirectPutItemInPool(*pPutItem) )
+{
+ DBG_ASSERT(pItemPool, "No Pool provided");
+}
+
+
+ScItemPoolCache::ScItemPoolCache( SfxItemPool *pItemPool,
+ const SfxItemSet *pPutSet ):
+ pPool(pItemPool),
+ pSetToPut( pPutSet ),
+ pItemToPut( nullptr )
+{
+ DBG_ASSERT(pItemPool, "No Pool provided");
+}
+
+
+ScItemPoolCache::~ScItemPoolCache()
+{
+ for (const SfxItemModifyImpl & rImpl : m_aCache) {
+ pPool->DirectRemoveItemFromPool( *rImpl.pPoolItem );
+ pPool->DirectRemoveItemFromPool( *rImpl.pOrigItem );
+ }
+
+ if ( pItemToPut )
+ pPool->DirectRemoveItemFromPool( *pItemToPut );
+}
+
+
+const ScPatternAttr& ScItemPoolCache::ApplyTo( const ScPatternAttr &rOrigItem )
+{
+ DBG_ASSERT( pPool == rOrigItem.GetItemSet().GetPool(), "invalid Pool" );
+ DBG_ASSERT( IsDefaultItem( &rOrigItem ) || IsPooledItem( &rOrigItem ),
+ "original not in pool" );
+
+ // Find whether this Transformations ever occurred
+ for (const SfxItemModifyImpl & rMapEntry : m_aCache)
+ {
+ // use Item PtrCompare OK
+ if ( areSfxPoolItemPtrsEqual(rMapEntry.pOrigItem, &rOrigItem) )
+ {
+ // Did anything change at all? use Item PtrCompare OK
+ if ( !areSfxPoolItemPtrsEqual(rMapEntry.pPoolItem, &rOrigItem) )
+ {
+ rMapEntry.pPoolItem->AddRef(2); // One for the cache
+ pPool->DirectPutItemInPool( rOrigItem ); //FIXME: AddRef?
+ }
+ return *rMapEntry.pPoolItem;
+ }
+ }
+
+ // Insert the new attributes in a new Set
+ std::unique_ptr<ScPatternAttr> pNewItem(rOrigItem.Clone());
+ if ( pItemToPut )
+ {
+ pNewItem->GetItemSet().Put( *pItemToPut );
+ DBG_ASSERT( areSfxPoolItemPtrsEqual(&pNewItem->GetItemSet().Get( pItemToPut->Which() ), pItemToPut),
+ "wrong item in temporary set" );
+ }
+ else
+ pNewItem->GetItemSet().Put( *pSetToPut );
+ const ScPatternAttr* pNewPoolItem = &pPool->DirectPutItemInPool( std::move(pNewItem) );
+
+ // Adapt refcount; one each for the cache
+ pNewPoolItem->AddRef( !areSfxPoolItemPtrsEqual(pNewPoolItem, &rOrigItem) ? 2 : 1 );
+ pPool->DirectPutItemInPool( rOrigItem ); //FIXME: AddRef?
+
+ // Add the transformation to the cache
+ SfxItemModifyImpl aModify;
+ aModify.pOrigItem = &rOrigItem;
+ aModify.pPoolItem = const_cast<ScPatternAttr*>(pNewPoolItem);
+ m_aCache.push_back( aModify );
+
+ DBG_ASSERT( !pItemToPut ||
+ areSfxPoolItemPtrsEqual(&pNewPoolItem->GetItemSet().Get( pItemToPut->Which() ), pItemToPut),
+ "wrong item in resulting set" );
+
+ return *pNewPoolItem;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/poolhelp.cxx b/sc/source/core/data/poolhelp.cxx
new file mode 100644
index 0000000000..64978eb2d5
--- /dev/null
+++ b/sc/source/core/data/poolhelp.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 <comphelper/processfactory.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <editeng/editeng.hxx>
+
+#include <poolhelp.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+
+ScPoolHelper::ScPoolHelper( ScDocument& rSourceDoc )
+ : pDocPool(new ScDocumentPool)
+{
+ pDocPool->FreezeIdRanges();
+
+ mxStylePool = new ScStyleSheetPool( *pDocPool, &rSourceDoc );
+}
+
+ScPoolHelper::~ScPoolHelper()
+{
+ pEnginePool.clear();
+ pEditPool.clear();
+ pFormTable.reset();
+ mxStylePool.clear();
+ pDocPool.clear();
+}
+
+SfxItemPool* ScPoolHelper::GetEditPool() const
+{
+ if ( !pEditPool )
+ {
+ pEditPool = EditEngine::CreatePool();
+ pEditPool->SetDefaultMetric( MapUnit::Map100thMM );
+ pEditPool->FreezeIdRanges();
+ }
+ return pEditPool.get();
+}
+
+SfxItemPool* ScPoolHelper::GetEnginePool() const
+{
+ if ( !pEnginePool )
+ {
+ pEnginePool = EditEngine::CreatePool();
+ pEnginePool->SetDefaultMetric( MapUnit::Map100thMM );
+ pEnginePool->FreezeIdRanges();
+ } // ifg ( pEnginePool )
+ return pEnginePool.get();
+}
+SvNumberFormatter* ScPoolHelper::GetFormTable() const
+{
+ if (!pFormTable)
+ pFormTable = CreateNumberFormatter();
+ return pFormTable.get();
+}
+
+void ScPoolHelper::SetFormTableOpt(const ScDocOptions& rOpt)
+{
+ aOpt = rOpt;
+ // #i105512# if the number formatter exists, update its settings
+ if (pFormTable)
+ {
+ sal_uInt16 d,m;
+ sal_Int16 y;
+ aOpt.GetDate( d,m,y );
+ pFormTable->ChangeNullDate( d,m,y );
+ pFormTable->ChangeStandardPrec( aOpt.GetStdPrecision() );
+ pFormTable->SetYear2000( aOpt.GetYear2000() );
+ }
+}
+
+std::unique_ptr<SvNumberFormatter> ScPoolHelper::CreateNumberFormatter() const
+{
+ std::unique_ptr<SvNumberFormatter> p;
+ {
+ std::scoped_lock aGuard(maMtxCreateNumFormatter);
+ p.reset(new SvNumberFormatter(comphelper::getProcessComponentContext(), LANGUAGE_SYSTEM));
+ }
+ assert(mxStylePool->GetDocument());
+ p->SetColorLink( LINK(mxStylePool->GetDocument(), ScDocument, GetUserDefinedColor));
+ p->SetEvalDateFormat(NF_EVALDATEFORMAT_INTL_FORMAT);
+
+ sal_uInt16 d,m;
+ sal_Int16 y;
+ aOpt.GetDate(d, m, y);
+ p->ChangeNullDate(d, m, y);
+ p->ChangeStandardPrec(aOpt.GetStdPrecision());
+ p->SetYear2000(aOpt.GetYear2000());
+ return p;
+}
+
+void ScPoolHelper::SourceDocumentGone()
+{
+ // reset all pointers to the source document
+ mxStylePool->SetDocument( nullptr );
+ if ( pFormTable )
+ pFormTable->SetColorLink( Link<sal_uInt16,Color*>() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/postit.cxx b/sc/source/core/data/postit.cxx
new file mode 100644
index 0000000000..fa84f0212b
--- /dev/null
+++ b/sc/source/core/data/postit.cxx
@@ -0,0 +1,1028 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <postit.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <unotools/useroptions.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoshape.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+
+#include <scitems.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/sxcecitm.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <tools/gen.hxx>
+
+#include <document.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <stylehelper.hxx>
+#include <patattr.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <detfunc.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <utility>
+#include <strings.hrc>
+
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextAppend.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace com::sun::star;
+
+namespace {
+
+const tools::Long SC_NOTECAPTION_WIDTH = 2900; /// Default width of note caption textbox.
+const tools::Long SC_NOTECAPTION_MAXWIDTH_TEMP = 12000; /// Maximum width of temporary note caption textbox.
+const tools::Long SC_NOTECAPTION_HEIGHT = 1800; /// Default height of note caption textbox.
+const tools::Long SC_NOTECAPTION_CELLDIST = 600; /// Default distance of note captions to border of anchor cell.
+const tools::Long SC_NOTECAPTION_OFFSET_Y = -1500; /// Default Y offset of note captions to top border of anchor cell.
+const tools::Long SC_NOTECAPTION_OFFSET_X = 1500; /// Default X offset of note captions to left border of anchor cell.
+const tools::Long SC_NOTECAPTION_BORDERDIST_TEMP = 100; /// Distance of temporary note captions to visible sheet area.
+
+/** Static helper functions for caption objects. */
+class ScCaptionUtil
+{
+public:
+ /** Moves the caption object to the correct layer according to passed visibility. */
+ static void SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown );
+ /** Sets basic caption settings required for note caption objects. */
+ static void SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown );
+ /** Stores the cell position of the note in the user data area of the caption. */
+ static void SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos );
+ /** Sets all hard formatting attributes to the caption object. */
+ static void SetExtraItems( SdrCaptionObj& rCaption, const SfxItemSet& rExtraItemSet );
+};
+
+void ScCaptionUtil::SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown )
+{
+ SdrLayerID nLayer = bShown ? SC_LAYER_INTERN : SC_LAYER_HIDDEN;
+ if( nLayer != rCaption.GetLayer() )
+ rCaption.SetLayer( nLayer );
+}
+
+void ScCaptionUtil::SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown )
+{
+ rCaption.SetFixedTail();
+ rCaption.SetSpecialTextBoxShadow();
+ SetCaptionLayer( rCaption, bShown );
+}
+
+void ScCaptionUtil::SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos )
+{
+ // pass true to ScDrawLayer::GetObjData() to create the object data entry
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData( &rCaption, true );
+ OSL_ENSURE( pObjData, "ScCaptionUtil::SetCaptionUserData - missing drawing object user data" );
+ pObjData->maStart = rPos;
+ pObjData->meType = ScDrawObjData::CellNote;
+}
+
+void ScCaptionUtil::SetExtraItems( SdrCaptionObj& rCaption, const SfxItemSet& rExtraItemSet )
+{
+ SfxItemSet aItemSet = rCaption.GetMergedItemSet();
+
+ aItemSet.Put(rExtraItemSet);
+ // reset shadow visibility (see also ScNoteUtil::CreateNoteFromCaption)
+ aItemSet.ClearItem(SDRATTR_SHADOW);
+ // ... but not distance, as that will fallback to wrong values
+ // if the comment is shown and then opened in older versions:
+ aItemSet.Put( makeSdrShadowXDistItem( 100 ) );
+ aItemSet.Put( makeSdrShadowYDistItem( 100 ) );
+
+ rCaption.SetMergedItemSet( aItemSet );
+}
+
+/** Helper for creation and manipulation of caption drawing objects independent
+ from cell annotations. */
+class ScCaptionCreator
+{
+public:
+ /** Create a new caption. The caption will not be inserted into the document. */
+ explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bTailFront );
+ /** Manipulate an existing caption. */
+ explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, const rtl::Reference<SdrCaptionObj>& xCaption );
+
+ /** Returns the drawing layer page of the sheet contained in maPos. */
+ SdrPage* GetDrawPage();
+ /** Returns the caption drawing object. */
+ rtl::Reference<SdrCaptionObj> & GetCaption() { return mxCaption; }
+
+ /** Moves the caption inside the passed rectangle. Uses page area if 0 is passed. */
+ void FitCaptionToRect( const tools::Rectangle* pVisRect = nullptr );
+ /** Places the caption inside the passed rectangle, tries to keep the cell rectangle uncovered. Uses page area if 0 is passed. */
+ void AutoPlaceCaption( const tools::Rectangle* pVisRect = nullptr );
+ /** Updates caption tail and textbox according to current cell position. Uses page area if 0 is passed. */
+ void UpdateCaptionPos();
+
+protected:
+ /** Helper constructor for derived classes. */
+ explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos );
+
+ /** Calculates the caption tail position according to current cell position. */
+ Point CalcTailPos( bool bTailFront );
+ /** Implements creation of the caption object. The caption will not be inserted into the document. */
+ void CreateCaption( bool bShown, bool bTailFront );
+
+private:
+ /** Initializes all members. */
+ void Initialize();
+ /** Returns the passed rectangle if existing, page rectangle otherwise. */
+ const tools::Rectangle& GetVisRect( const tools::Rectangle* pVisRect ) const { return pVisRect ? *pVisRect : maPageRect; }
+
+private:
+ ScDocument& mrDoc;
+ ScAddress maPos;
+ rtl::Reference<SdrCaptionObj> mxCaption;
+ tools::Rectangle maPageRect;
+ tools::Rectangle maCellRect;
+ bool mbNegPage;
+};
+
+ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bTailFront ) :
+ mrDoc( rDoc ),
+ maPos( rPos )
+{
+ Initialize();
+ CreateCaption( true/*bShown*/, bTailFront );
+}
+
+ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, const rtl::Reference<SdrCaptionObj>& xCaption ) :
+ mrDoc( rDoc ),
+ maPos( rPos ),
+ mxCaption( xCaption )
+{
+ Initialize();
+}
+
+ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos ) :
+ mrDoc( rDoc ),
+ maPos( rPos )
+{
+ Initialize();
+}
+
+SdrPage* ScCaptionCreator::GetDrawPage()
+{
+ ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
+ return pDrawLayer ? pDrawLayer->GetPage( static_cast< sal_uInt16 >( maPos.Tab() ) ) : nullptr;
+}
+
+void ScCaptionCreator::FitCaptionToRect( const tools::Rectangle* pVisRect )
+{
+ const tools::Rectangle& rVisRect = GetVisRect( pVisRect );
+
+ // tail position
+ Point aTailPos = mxCaption->GetTailPos();
+ aTailPos.setX( ::std::clamp( aTailPos.X(), rVisRect.Left(), rVisRect.Right() ) );
+ aTailPos.setY( ::std::clamp( aTailPos.Y(), rVisRect.Top(), rVisRect.Bottom() ) );
+ mxCaption->SetTailPos( aTailPos );
+
+ // caption rectangle
+ tools::Rectangle aCaptRect = mxCaption->GetLogicRect();
+ Point aCaptPos = aCaptRect.TopLeft();
+ // move textbox inside right border of visible area
+ aCaptPos.setX( ::std::min< tools::Long >( aCaptPos.X(), rVisRect.Right() - aCaptRect.GetWidth() ) );
+ // move textbox inside left border of visible area (this may move it outside on right side again)
+ aCaptPos.setX( ::std::max< tools::Long >( aCaptPos.X(), rVisRect.Left() ) );
+ // move textbox inside bottom border of visible area
+ aCaptPos.setY( ::std::min< tools::Long >( aCaptPos.Y(), rVisRect.Bottom() - aCaptRect.GetHeight() ) );
+ // move textbox inside top border of visible area (this may move it outside on bottom side again)
+ aCaptPos.setY( ::std::max< tools::Long >( aCaptPos.Y(), rVisRect.Top() ) );
+ // update caption
+ aCaptRect.SetPos( aCaptPos );
+ mxCaption->SetLogicRect( aCaptRect );
+}
+
+void ScCaptionCreator::AutoPlaceCaption( const tools::Rectangle* pVisRect )
+{
+ const tools::Rectangle& rVisRect = GetVisRect( pVisRect );
+
+ // caption rectangle
+ tools::Rectangle aCaptRect = mxCaption->GetLogicRect();
+ tools::Long nWidth = aCaptRect.GetWidth();
+ tools::Long nHeight = aCaptRect.GetHeight();
+
+ // n***Space contains available space between border of visible area and cell
+ tools::Long nLeftSpace = maCellRect.Left() - rVisRect.Left() + 1;
+ tools::Long nRightSpace = rVisRect.Right() - maCellRect.Right() + 1;
+ tools::Long nTopSpace = maCellRect.Top() - rVisRect.Top() + 1;
+ tools::Long nBottomSpace = rVisRect.Bottom() - maCellRect.Bottom() + 1;
+
+ // nNeeded*** contains textbox dimensions plus needed distances to cell or border of visible area
+ tools::Long nNeededSpaceX = nWidth + SC_NOTECAPTION_CELLDIST;
+ tools::Long nNeededSpaceY = nHeight + SC_NOTECAPTION_CELLDIST;
+
+ // bFitsWidth*** == true means width of textbox fits into horizontal free space of visible area
+ bool bFitsWidthLeft = nNeededSpaceX <= nLeftSpace; // text box width fits into the width left of cell
+ bool bFitsWidthRight = nNeededSpaceX <= nRightSpace; // text box width fits into the width right of cell
+ bool bFitsWidth = nWidth <= rVisRect.GetWidth(); // text box width fits into width of visible area
+
+ // bFitsHeight*** == true means height of textbox fits into vertical free space of visible area
+ bool bFitsHeightTop = nNeededSpaceY <= nTopSpace; // text box height fits into the height above cell
+ bool bFitsHeightBottom = nNeededSpaceY <= nBottomSpace; // text box height fits into the height below cell
+ bool bFitsHeight = nHeight <= rVisRect.GetHeight(); // text box height fits into height of visible area
+
+ // bFits*** == true means the textbox fits completely into free space of visible area
+ bool bFitsLeft = bFitsWidthLeft && bFitsHeight;
+ bool bFitsRight = bFitsWidthRight && bFitsHeight;
+ bool bFitsTop = bFitsWidth && bFitsHeightTop;
+ bool bFitsBottom = bFitsWidth && bFitsHeightBottom;
+
+ Point aCaptPos;
+ // use left/right placement if possible, or if top/bottom placement not possible
+ if( bFitsLeft || bFitsRight || (!bFitsTop && !bFitsBottom) )
+ {
+ // prefer left in RTL sheet and right in LTR sheets
+ bool bPreferLeft = bFitsLeft && (mbNegPage || !bFitsRight);
+ bool bPreferRight = bFitsRight && (!mbNegPage || !bFitsLeft);
+ // move to left, if left is preferred, or if neither left nor right fit and there is more space to the left
+ if( bPreferLeft || (!bPreferRight && (nLeftSpace > nRightSpace)) )
+ aCaptPos.setX( maCellRect.Left() - SC_NOTECAPTION_CELLDIST - nWidth );
+ else // to right
+ aCaptPos.setX( maCellRect.Right() + SC_NOTECAPTION_CELLDIST );
+ // Y position according to top cell border
+ aCaptPos.setY( maCellRect.Top() + SC_NOTECAPTION_OFFSET_Y );
+ }
+ else // top or bottom placement
+ {
+ // X position
+ aCaptPos.setX( maCellRect.Left() + SC_NOTECAPTION_OFFSET_X );
+ // top placement, if possible
+ if( bFitsTop )
+ aCaptPos.setY( maCellRect.Top() - SC_NOTECAPTION_CELLDIST - nHeight );
+ else // bottom placement
+ aCaptPos.setY( maCellRect.Bottom() + SC_NOTECAPTION_CELLDIST );
+ }
+
+ // update textbox position in note caption object
+ aCaptRect.SetPos( aCaptPos );
+ mxCaption->SetLogicRect( aCaptRect );
+ FitCaptionToRect( pVisRect );
+}
+
+void ScCaptionCreator::UpdateCaptionPos()
+{
+ ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
+
+ // update caption position
+ const Point& rOldTailPos = mxCaption->GetTailPos();
+ Point aTailPos = CalcTailPos( false );
+ if( rOldTailPos != aTailPos )
+ {
+ // create drawing undo action
+ if( pDrawLayer && pDrawLayer->IsRecording() )
+ pDrawLayer->AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *mxCaption ) );
+ // calculate new caption rectangle (#i98141# handle LTR<->RTL switch correctly)
+ tools::Rectangle aCaptRect = mxCaption->GetLogicRect();
+ tools::Long nDiffX = (rOldTailPos.X() >= 0) ? (aCaptRect.Left() - rOldTailPos.X()) : (rOldTailPos.X() - aCaptRect.Right());
+ if( mbNegPage ) nDiffX = -nDiffX - aCaptRect.GetWidth();
+ tools::Long nDiffY = aCaptRect.Top() - rOldTailPos.Y();
+ aCaptRect.SetPos( aTailPos + Point( nDiffX, nDiffY ) );
+ // set new tail position and caption rectangle
+ mxCaption->SetTailPos( aTailPos );
+ mxCaption->SetLogicRect( aCaptRect );
+ // fit caption into draw page
+ FitCaptionToRect();
+ }
+
+ // update cell position in caption user data
+ ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( mxCaption.get(), maPos.Tab() );
+ if( pCaptData && (maPos != pCaptData->maStart) )
+ {
+ // create drawing undo action
+ if( pDrawLayer && pDrawLayer->IsRecording() )
+ pDrawLayer->AddCalcUndo( std::make_unique<ScUndoObjData>( mxCaption.get(), pCaptData->maStart, pCaptData->maEnd, maPos, pCaptData->maEnd ) );
+ // set new position
+ pCaptData->maStart = maPos;
+ }
+}
+
+Point ScCaptionCreator::CalcTailPos( bool bTailFront )
+{
+ // tail position
+ bool bTailLeft = bTailFront != mbNegPage;
+ Point aTailPos = bTailLeft ? maCellRect.TopLeft() : maCellRect.TopRight();
+ // move caption point 1/10 mm inside cell
+ if( bTailLeft ) aTailPos.AdjustX(10 ); else aTailPos.AdjustX( -10 );
+ aTailPos.AdjustY(10);
+ return aTailPos;
+}
+
+void ScCaptionCreator::CreateCaption( bool bShown, bool bTailFront )
+{
+ // create the caption drawing object
+ tools::Rectangle aTextRect( Point( 0 , 0 ), Size( SC_NOTECAPTION_WIDTH, SC_NOTECAPTION_HEIGHT ) );
+ Point aTailPos = CalcTailPos( bTailFront );
+ mxCaption =
+ new SdrCaptionObj(
+ *mrDoc.GetDrawLayer(), // TTTT should ret a ref?
+ aTextRect,
+ aTailPos);
+ // basic caption settings
+ ScCaptionUtil::SetBasicCaptionSettings( *mxCaption, bShown );
+}
+
+void ScCaptionCreator::Initialize()
+{
+ maCellRect = ScDrawLayer::GetCellRect( mrDoc, maPos, true );
+ mbNegPage = mrDoc.IsNegativePage( maPos.Tab() );
+ if( SdrPage* pDrawPage = GetDrawPage() )
+ {
+ maPageRect = tools::Rectangle( Point( 0, 0 ), pDrawPage->GetSize() );
+ /* #i98141# SdrPage::GetSize() returns negative width in RTL mode.
+ The call to Rectangle::Adjust() orders left/right coordinate
+ accordingly. */
+ maPageRect.Normalize();
+ }
+}
+
+/** Helper for creation of permanent caption drawing objects for cell notes. */
+class ScNoteCaptionCreator : public ScCaptionCreator
+{
+public:
+ /** Create a new caption object and inserts it into the document. */
+ explicit ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData );
+ /** Manipulate an existing caption. */
+ explicit ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, rtl::Reference<SdrCaptionObj>& xCaption, bool bShown );
+};
+
+ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData ) :
+ ScCaptionCreator( rDoc, rPos ) // use helper c'tor that does not create the caption yet
+{
+ SdrPage* pDrawPage = GetDrawPage();
+ OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" );
+ if( !pDrawPage )
+ return;
+
+ // create the caption drawing object
+ CreateCaption( rNoteData.mbShown, false );
+ rNoteData.mxCaption = GetCaption();
+ OSL_ENSURE( rNoteData.mxCaption, "ScNoteCaptionCreator::ScNoteCaptionCreator - missing caption object" );
+ if( rNoteData.mxCaption )
+ {
+ // store note position in user data of caption object
+ ScCaptionUtil::SetCaptionUserData( *rNoteData.mxCaption, rPos );
+ // insert object into draw page
+ pDrawPage->InsertObject( rNoteData.mxCaption.get() );
+ }
+}
+
+ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, rtl::Reference<SdrCaptionObj>& xCaption, bool bShown ) :
+ ScCaptionCreator( rDoc, rPos, xCaption )
+{
+ SdrPage* pDrawPage = GetDrawPage();
+ OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" );
+ OSL_ENSURE( xCaption->getSdrPageFromSdrObject() == pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - wrong drawing page in caption" );
+ if( pDrawPage && (xCaption->getSdrPageFromSdrObject() == pDrawPage) )
+ {
+ // store note position in user data of caption object
+ ScCaptionUtil::SetCaptionUserData( *xCaption, rPos );
+ // basic caption settings
+ ScCaptionUtil::SetBasicCaptionSettings( *xCaption, bShown );
+ // set correct tail position
+ xCaption->SetTailPos( CalcTailPos( false ) );
+ }
+}
+
+} // namespace
+
+struct ScCaptionInitData
+{
+ std::optional< SfxItemSet > moItemSet; /// Caption object formatting.
+ std::optional< OutlinerParaObject > mxOutlinerObj; /// Text object with all text portion formatting.
+ std::unique_ptr< GenerateNoteCaption > mxGenerator; /// Operator to generate Caption Object from import data
+ OUString maStyleName; /// Drawing style associated with the caption object.
+ OUString maSimpleText; /// Simple text without formatting.
+ Point maCaptionOffset; /// Caption position relative to cell corner.
+ Size maCaptionSize; /// Size of the caption object.
+ bool mbDefaultPosSize; /// True = use default position and size for caption.
+
+ explicit ScCaptionInitData();
+};
+
+ScCaptionInitData::ScCaptionInitData() :
+ mbDefaultPosSize( true )
+{
+}
+
+ScNoteData::ScNoteData( bool bShown ) :
+ mbShown( bShown )
+{
+}
+
+sal_uInt32 ScPostIt::mnLastPostItId = 1;
+
+ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, sal_uInt32 nPostItId ) :
+ mrDoc( rDoc ),
+ maNoteData( false )
+{
+ mnPostItId = nPostItId == 0 ? mnLastPostItId++ : nPostItId;
+ AutoStamp();
+ CreateCaption( rPos );
+}
+
+ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScPostIt& rNote, sal_uInt32 nPostItId ) :
+ mrDoc( rDoc ),
+ maNoteData( rNote.maNoteData )
+{
+ mnPostItId = nPostItId == 0 ? mnLastPostItId++ : nPostItId;
+ maNoteData.mxCaption.clear();
+ CreateCaption( rPos, rNote.maNoteData.mxCaption.get() );
+}
+
+ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, ScNoteData aNoteData, bool bAlwaysCreateCaption, sal_uInt32 nPostItId ) :
+ mrDoc( rDoc ),
+ maNoteData(std::move( aNoteData ))
+{
+ mnPostItId = nPostItId == 0 ? mnLastPostItId++ : nPostItId;
+ if( bAlwaysCreateCaption || maNoteData.mbShown )
+ CreateCaptionFromInitData( rPos );
+}
+
+ScPostIt::~ScPostIt()
+{
+ RemoveCaption();
+}
+
+std::unique_ptr<ScPostIt> ScPostIt::Clone( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, bool bCloneCaption ) const
+{
+ // tdf#117307: Don't clone comment, if it is in the same position
+ if ( (rOwnPos == rDestPos) && !mrDoc.IsClipboard() )
+ bCloneCaption = false;
+ CreateCaptionFromInitData( rOwnPos );
+ sal_uInt32 nPostItId = comphelper::LibreOfficeKit::isActive() ? 0 : mnPostItId;
+ return bCloneCaption ? std::make_unique<ScPostIt>( rDestDoc, rDestPos, *this, nPostItId ) : std::make_unique<ScPostIt>( rDestDoc, rDestPos, maNoteData, false, mnPostItId );
+}
+
+void ScPostIt::SetDate( const OUString& rDate )
+{
+ maNoteData.maDate = rDate;
+}
+
+void ScPostIt::SetAuthor( const OUString& rAuthor )
+{
+ maNoteData.maAuthor = rAuthor;
+}
+
+void ScPostIt::AutoStamp()
+{
+ maNoteData.maDate = ScGlobal::getLocaleData().getDate( Date( Date::SYSTEM ) ) + " " +
+ ScGlobal::getLocaleData().getTime(DateTime(DateTime::SYSTEM), false);
+ const OUString aAuthor = SvtUserOptions().GetFullName();
+ maNoteData.maAuthor = !aAuthor.isEmpty() ? aAuthor : ScResId(STR_CHG_UNKNOWN_AUTHOR);
+}
+
+const OutlinerParaObject* ScPostIt::GetOutlinerObject() const
+{
+ if( maNoteData.mxCaption )
+ return maNoteData.mxCaption->GetOutlinerParaObject();
+ if( maNoteData.mxInitData && maNoteData.mxInitData->mxOutlinerObj )
+ return &*maNoteData.mxInitData->mxOutlinerObj;
+ return nullptr;
+}
+
+const EditTextObject* ScPostIt::GetEditTextObject() const
+{
+ const OutlinerParaObject* pOPO = GetOutlinerObject();
+ return pOPO ? &pOPO->GetTextObject() : nullptr;
+}
+
+OUString ScPostIt::GetText() const
+{
+ if( const EditTextObject* pEditObj = GetEditTextObject() )
+ {
+ OUStringBuffer aBuffer;
+ ScNoteEditEngine& rEngine = mrDoc.GetNoteEngine();
+ rEngine.SetTextCurrentDefaults(*pEditObj);
+ sal_Int32 nParaCount = rEngine.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ if( nPara > 0 )
+ aBuffer.append( '\n' );
+ aBuffer.append(rEngine.GetText(nPara));
+ }
+ return aBuffer.makeStringAndClear();
+ }
+ if( maNoteData.mxInitData )
+ return maNoteData.mxInitData->maSimpleText;
+ return OUString();
+}
+
+void ScPostIt::SetText( const ScAddress& rPos, const OUString& rText )
+{
+ CreateCaptionFromInitData( rPos );
+ if( maNoteData.mxCaption )
+ maNoteData.mxCaption->SetText( rText );
+}
+
+SdrCaptionObj* ScPostIt::GetOrCreateCaption( const ScAddress& rPos ) const
+{
+ CreateCaptionFromInitData( rPos );
+ return maNoteData.mxCaption.get();
+}
+
+void ScPostIt::ForgetCaption( bool bPreserveData )
+{
+ if (bPreserveData)
+ {
+ // Used in clipboard when the originating document is destructed to be
+ // able to paste into another document. Caption size and relative
+ // position are not preserved but default created when pasted. Also the
+ // MergedItemSet can not be carried over or it had to be adapted to
+ // defaults and pool. At least preserve the text and outline object if
+ // possible.
+ ScCaptionInitData* pInitData = new ScCaptionInitData;
+ const OutlinerParaObject* pOPO = GetOutlinerObject();
+ if (pOPO)
+ pInitData->mxOutlinerObj = *pOPO;
+ pInitData->maSimpleText = GetText();
+
+ maNoteData.mxInitData.reset(pInitData);
+ maNoteData.mxCaption.clear();
+ }
+ else
+ {
+ /* This function is used in undo actions to give up the responsibility for
+ the caption object which is handled by separate drawing undo actions. */
+ maNoteData.mxCaption.clear();
+ maNoteData.mxInitData.reset();
+ }
+}
+
+void ScPostIt::ShowCaption( const ScAddress& rPos, bool bShow )
+{
+ CreateCaptionFromInitData( rPos );
+ // no separate drawing undo needed, handled completely inside ScUndoShowHideNote
+ maNoteData.mbShown = bShow;
+ if( maNoteData.mxCaption )
+ ScCaptionUtil::SetCaptionLayer( *maNoteData.mxCaption, bShow );
+}
+
+void ScPostIt::ShowCaptionTemp( const ScAddress& rPos, bool bShow )
+{
+ CreateCaptionFromInitData( rPos );
+ if( maNoteData.mxCaption )
+ ScCaptionUtil::SetCaptionLayer( *maNoteData.mxCaption, maNoteData.mbShown || bShow );
+}
+
+void ScPostIt::UpdateCaptionPos( const ScAddress& rPos )
+{
+ CreateCaptionFromInitData( rPos );
+ if( maNoteData.mxCaption )
+ {
+ ScCaptionCreator aCreator( mrDoc, rPos, maNoteData.mxCaption );
+ aCreator.UpdateCaptionPos();
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void ScPostIt::CreateCaptionFromInitData( const ScAddress& rPos ) const
+{
+ // Captions are not created in Undo documents and only rarely in Clipboard,
+ // but otherwise we need caption or initial data.
+ assert((maNoteData.mxCaption || maNoteData.mxInitData) || mrDoc.IsUndo() || mrDoc.IsClipboard());
+ if( !maNoteData.mxInitData )
+ return;
+
+
+ /* This function is called from ScPostIt::Clone() when copying cells
+ to the clipboard/undo document, and when copying cells from the
+ clipboard/undo document. The former should always be called first,
+ so if called in a clipboard/undo document, the caption should have
+ been created already. However, for clipboard in case the
+ originating document was destructed a new caption has to be
+ created. */
+ OSL_ENSURE( !mrDoc.IsUndo() && (!mrDoc.IsClipboard() || !maNoteData.mxCaption),
+ "ScPostIt::CreateCaptionFromInitData - note caption should not be created in undo/clip documents" );
+
+ // going to forget the initial caption data struct when this method returns
+ auto xInitData = std::move(maNoteData.mxInitData);
+
+ /* #i104915# Never try to create notes in Undo document, leads to
+ crash due to missing document members (e.g. row height array). */
+ if( maNoteData.mxCaption || mrDoc.IsUndo() )
+ return;
+
+ if (mrDoc.IsClipboard())
+ mrDoc.InitDrawLayer(); // ensure there is a drawing layer
+
+ // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData
+ ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData );
+ if( !maNoteData.mxCaption )
+ return;
+
+ // Prevent triple change broadcasts of the same object.
+ bool bWasLocked = maNoteData.mxCaption->getSdrModelFromSdrObject().isLocked();
+ maNoteData.mxCaption->getSdrModelFromSdrObject().setLock(true);
+
+ if (xInitData->mxGenerator)
+ xInitData->mxGenerator->Generate(*maNoteData.mxCaption);
+ else
+ {
+ // transfer ownership of outliner object to caption, or set simple text
+ OSL_ENSURE( xInitData->mxOutlinerObj || !xInitData->maSimpleText.isEmpty(),
+ "ScPostIt::CreateCaptionFromInitData - need either outliner para object or simple text" );
+ if (xInitData->mxOutlinerObj)
+ maNoteData.mxCaption->SetOutlinerParaObject( std::move(xInitData->mxOutlinerObj) );
+ else
+ maNoteData.mxCaption->SetText( xInitData->maSimpleText );
+ }
+
+ if (!xInitData->maStyleName.isEmpty())
+ {
+ if (auto pStyleSheet = mrDoc.GetStyleSheetPool()->Find(xInitData->maStyleName, SfxStyleFamily::Frame))
+ maNoteData.mxCaption->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), true);
+
+ if (xInitData->moItemSet)
+ maNoteData.mxCaption->SetMergedItemSet(*xInitData->moItemSet);
+ }
+ else
+ {
+ if (auto pStyleSheet = mrDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame))
+ maNoteData.mxCaption->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), true);
+
+ // copy all items and reset shadow items
+ if (xInitData->moItemSet)
+ ScCaptionUtil::SetExtraItems(*maNoteData.mxCaption, *xInitData->moItemSet);
+ }
+
+ // set position and size of the caption object
+ if( xInitData->mbDefaultPosSize )
+ {
+ // set other items and fit caption size to text
+ maNoteData.mxCaption->SetMergedItem( makeSdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) );
+ maNoteData.mxCaption->SetMergedItem( makeSdrTextMaxFrameWidthItem( SC_NOTECAPTION_MAXWIDTH_TEMP ) );
+ maNoteData.mxCaption->AdjustTextFrameWidthAndHeight();
+ aCreator.AutoPlaceCaption();
+ }
+ else
+ {
+ tools::Rectangle aCellRect = ScDrawLayer::GetCellRect( mrDoc, rPos, true );
+ bool bNegPage = mrDoc.IsNegativePage( rPos.Tab() );
+ tools::Long nPosX = bNegPage ? (aCellRect.Left() - xInitData->maCaptionOffset.X()) : (aCellRect.Right() + xInitData->maCaptionOffset.X());
+ tools::Long nPosY = aCellRect.Top() + xInitData->maCaptionOffset.Y();
+ tools::Rectangle aCaptRect( Point( nPosX, nPosY ), xInitData->maCaptionSize );
+ maNoteData.mxCaption->SetLogicRect( aCaptRect );
+ aCreator.FitCaptionToRect();
+ }
+
+ // End prevent triple change broadcasts of the same object.
+ maNoteData.mxCaption->getSdrModelFromSdrObject().setLock(bWasLocked);
+ maNoteData.mxCaption->BroadcastObjectChange();
+}
+
+void ScPostIt::CreateCaption( const ScAddress& rPos, const SdrCaptionObj* pCaption )
+{
+ OSL_ENSURE( !maNoteData.mxCaption, "ScPostIt::CreateCaption - unexpected caption object found" );
+ maNoteData.mxCaption.clear();
+
+ /* #i104915# Never try to create notes in Undo document, leads to
+ crash due to missing document members (e.g. row height array). */
+ OSL_ENSURE( !mrDoc.IsUndo(), "ScPostIt::CreateCaption - note caption should not be created in undo documents" );
+ if( mrDoc.IsUndo() )
+ return;
+
+ // drawing layer may be missing, if a note is copied into a clipboard document
+ if( mrDoc.IsClipboard() )
+ mrDoc.InitDrawLayer();
+
+ // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData
+ ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData );
+ if( !maNoteData.mxCaption )
+ return;
+
+ // clone settings of passed caption
+ if( pCaption )
+ {
+ // copy edit text object (object must be inserted into page already)
+ if( OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() )
+ maNoteData.mxCaption->SetOutlinerParaObject( *pOPO );
+ // copy formatting items (after text has been copied to apply font formatting)
+ if (auto pStyleSheet = pCaption->GetStyleSheet())
+ {
+ auto pPool = mrDoc.GetStyleSheetPool();
+ pPool->CopyStyleFrom(pStyleSheet->GetPool(), pStyleSheet->GetName(), pStyleSheet->GetFamily(), true);
+
+ if (auto pDestStyleSheet = pPool->Find(pStyleSheet->GetName(), pStyleSheet->GetFamily()))
+ maNoteData.mxCaption->SetStyleSheet(static_cast<SfxStyleSheet*>(pDestStyleSheet), true);
+ }
+ maNoteData.mxCaption->SetMergedItemSetAndBroadcast( pCaption->GetMergedItemSet() );
+ // move textbox position relative to new cell, copy textbox size
+ tools::Rectangle aCaptRect = pCaption->GetLogicRect();
+ Point aDist = maNoteData.mxCaption->GetTailPos() - pCaption->GetTailPos();
+ aCaptRect.Move( aDist.X(), aDist.Y() );
+ maNoteData.mxCaption->SetLogicRect( aCaptRect );
+ aCreator.FitCaptionToRect();
+ }
+ else
+ {
+ if (auto pStyleSheet = mrDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame))
+ maNoteData.mxCaption->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), true);
+ // set default size, undoing sdr::TextProperties::SetStyleSheet's
+ // adjustment that use a wrong min height.
+ tools::Rectangle aCaptRect = maNoteData.mxCaption->GetLogicRect();
+ aCaptRect.SetSize({ SC_NOTECAPTION_WIDTH, SC_NOTECAPTION_HEIGHT });
+ maNoteData.mxCaption->SetLogicRect(aCaptRect);
+ // set default position
+ aCreator.AutoPlaceCaption();
+ }
+
+ // create undo action
+ if( ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer() )
+ if( pDrawLayer->IsRecording() )
+ pDrawLayer->AddCalcUndo( std::make_unique<SdrUndoNewObj>( *maNoteData.mxCaption ) );
+}
+
+void ScPostIt::RemoveCaption()
+{
+ if (!maNoteData.mxCaption)
+ return;
+
+ /* Remove caption object only, if this note is its owner (e.g. notes in
+ undo documents refer to captions in original document, do not remove
+ them from drawing layer here). */
+ // TTTT maybe no longer needed - can that still happen?
+ ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
+ if (pDrawLayer == &maNoteData.mxCaption->getSdrModelFromSdrObject())
+ {
+ SdrPage* pDrawPage(maNoteData.mxCaption->getSdrPageFromSdrObject());
+ SAL_WARN_IF( !pDrawPage, "sc.core", "ScCaptionPtr::removeFromDrawPageAndFree - object without drawing page");
+ if (pDrawPage)
+ {
+ pDrawPage->RecalcObjOrdNums();
+ // create drawing undo action (before removing the object to have valid draw page in undo action)
+ bool bRecording = (pDrawLayer && pDrawLayer->IsRecording());
+ if (bRecording)
+ pDrawLayer->AddCalcUndo( std::make_unique<SdrUndoDelObj>( *maNoteData.mxCaption ));
+ // remove the object from the drawing page
+ rtl::Reference<SdrObject> pRemovedObj = pDrawPage->RemoveObject( maNoteData.mxCaption->GetOrdNum() );
+ assert(pRemovedObj.get() == maNoteData.mxCaption.get()); (void)pRemovedObj;
+ }
+ }
+
+ SAL_INFO("sc.core","ScPostIt::RemoveCaption -"
+ " IsUndo: " << mrDoc.IsUndo() << " IsClip: " << mrDoc.IsClipboard() <<
+ " Dtor: " << mrDoc.IsInDtorClear());
+
+ // Forget the caption object if removeFromDrawPageAndFree() did not free it.
+ if (maNoteData.mxCaption)
+ {
+ SAL_INFO("sc.core","ScPostIt::RemoveCaption - forgetting one ref");
+ maNoteData.mxCaption.clear();
+ }
+}
+
+static void lcl_FormatAndInsertAuthorAndDatepara(SdrCaptionObj* pCaption, OUStringBuffer& aUserData, bool bUserWithTrackText)
+{
+ uno::Reference<drawing::XShape> xShape = pCaption->getUnoShape();
+ uno::Reference<text::XText> xText(xShape, uno::UNO_QUERY);
+ uno::Reference<text::XTextAppend> xBodyTextAppend(xText, uno::UNO_QUERY);
+
+ if (xBodyTextAppend.is())
+ {
+ uno::Sequence< beans::PropertyValue > aArgs;
+ if (bUserWithTrackText)
+ {
+ xBodyTextAppend->insertTextPortion(aUserData.makeStringAndClear(), aArgs, xText->getStart());
+ }
+ else
+ {
+ xBodyTextAppend->insertTextPortion("\n--------\n", aArgs, xText->getStart());
+ aArgs = {
+ comphelper::makePropertyValue("CharWeight", uno::Any(awt::FontWeight::BOLD)),
+ };
+ xBodyTextAppend->insertTextPortion(aUserData.makeStringAndClear(), aArgs, xText->getStart());
+ }
+ }
+}
+
+rtl::Reference<SdrCaptionObj> ScNoteUtil::CreateTempCaption(
+ ScDocument& rDoc, const ScAddress& rPos, SdrPage& rDrawPage,
+ std::u16string_view rUserText, const tools::Rectangle& rVisRect, bool bTailFront )
+{
+ bool bUserWithTrackText = false;
+ OUStringBuffer aBuffer( rUserText );
+ // add plain text of invisible (!) cell note (no formatting etc.)
+ SdrCaptionObj* pNoteCaption = nullptr;
+ const ScPostIt* pNote = rDoc.GetNote( rPos );
+ if( pNote && !pNote->IsCaptionShown() )
+ {
+ if (!aBuffer.isEmpty())
+ {
+ bUserWithTrackText = true;
+ aBuffer.append("\n--------\n");
+ }
+ else
+ {
+ aBuffer.append(pNote->GetAuthor()
+ + ", "
+ + pNote->GetDate());
+ }
+ pNoteCaption = pNote->GetOrCreateCaption( rPos );
+ }
+
+ // prepare visible rectangle (add default distance to all borders)
+ tools::Rectangle aVisRect(
+ rVisRect.Left() + SC_NOTECAPTION_BORDERDIST_TEMP,
+ rVisRect.Top() + SC_NOTECAPTION_BORDERDIST_TEMP,
+ rVisRect.Right() - SC_NOTECAPTION_BORDERDIST_TEMP,
+ rVisRect.Bottom() - SC_NOTECAPTION_BORDERDIST_TEMP );
+
+ // create the caption object
+ ScCaptionCreator aCreator( rDoc, rPos, bTailFront );
+
+ // insert caption into page (needed to set caption text)
+ rtl::Reference<SdrCaptionObj> pCaption = aCreator.GetCaption(); // just for ease of use
+ rDrawPage.InsertObject( pCaption.get() );
+
+ // clone the edit text object, then seta and format the Author and date text
+ if (pNoteCaption)
+ {
+ if( OutlinerParaObject* pOPO = pNoteCaption->GetOutlinerParaObject() )
+ pCaption->SetOutlinerParaObject( *pOPO );
+ // Setting and formatting rUserText: Author name and date time
+ lcl_FormatAndInsertAuthorAndDatepara(pCaption.get(), aBuffer, bUserWithTrackText);
+ // set formatting (must be done after setting text) and resize the box to fit the text
+ if (auto pStyleSheet = pNoteCaption->GetStyleSheet())
+ pCaption->SetStyleSheet(pStyleSheet, true);
+ pCaption->SetMergedItemSetAndBroadcast(pNoteCaption->GetMergedItemSet());
+ }
+ else
+ {
+ pCaption->SetText(aBuffer.makeStringAndClear());
+ if (auto pStyleSheet = rDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame))
+ pCaption->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), true);
+ }
+
+ // adjust caption size to text size
+ tools::Long nMaxWidth = ::std::min< tools::Long >( aVisRect.GetWidth() * 2 / 3, SC_NOTECAPTION_MAXWIDTH_TEMP );
+ pCaption->SetMergedItem( makeSdrTextAutoGrowWidthItem( true ) );
+ pCaption->SetMergedItem( makeSdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) );
+ pCaption->SetMergedItem( makeSdrTextMaxFrameWidthItem( nMaxWidth ) );
+ pCaption->SetMergedItem( makeSdrTextAutoGrowHeightItem( true ) );
+ pCaption->AdjustTextFrameWidthAndHeight();
+
+ // move caption into visible area
+ aCreator.AutoPlaceCaption( &aVisRect );
+
+ // XXX Note it is already inserted to the draw page.
+ return aCreator.GetCaption();
+}
+
+ScPostIt* ScNoteUtil::CreateNoteFromCaption(
+ ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj* pCaption, bool bHasStyle )
+{
+ ScNoteData aNoteData( true/*bShown*/ );
+ aNoteData.mxCaption = pCaption;
+ ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, false );
+ pNote->AutoStamp();
+
+ rDoc.SetNote(rPos, std::unique_ptr<ScPostIt>(pNote));
+
+ // ScNoteCaptionCreator c'tor updates the caption object to be part of a note
+ ScNoteCaptionCreator aCreator( rDoc, rPos, aNoteData.mxCaption, true/*bShown*/ );
+
+ if (!bHasStyle)
+ {
+ if (auto pStyleSheet = rDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame))
+ aNoteData.mxCaption->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), true);
+
+ /* We used to show a shadow despite of the shadow item being set to false.
+ Clear the existing item, so it inherits the true setting from the style.
+ Setting explicitly to true would corrupt the shadow when opened in older versions. */
+ aNoteData.mxCaption->ClearMergedItem(SDRATTR_SHADOW);
+ }
+
+ return pNote;
+}
+
+ScNoteData ScNoteUtil::CreateNoteData(ScDocument& rDoc, const ScAddress& rPos,
+ const tools::Rectangle& rCaptionRect, bool bShown)
+{
+ ScNoteData aNoteData( bShown );
+ aNoteData.mxInitData = std::make_shared<ScCaptionInitData>();
+ ScCaptionInitData& rInitData = *aNoteData.mxInitData;
+
+ // convert absolute caption position to relative position
+ rInitData.mbDefaultPosSize = rCaptionRect.IsEmpty();
+ if( !rInitData.mbDefaultPosSize )
+ {
+ tools::Rectangle aCellRect = ScDrawLayer::GetCellRect( rDoc, rPos, true );
+ bool bNegPage = rDoc.IsNegativePage( rPos.Tab() );
+ rInitData.maCaptionOffset.setX( bNegPage ? (aCellRect.Left() - rCaptionRect.Right()) : (rCaptionRect.Left() - aCellRect.Right()) );
+ rInitData.maCaptionOffset.setY( rCaptionRect.Top() - aCellRect.Top() );
+ rInitData.maCaptionSize = rCaptionRect.GetSize();
+ }
+
+ return aNoteData;
+}
+
+ScPostIt* ScNoteUtil::CreateNoteFromObjectData(
+ ScDocument& rDoc, const ScAddress& rPos, const SfxItemSet& rItemSet, const OUString& rStyleName,
+ const OutlinerParaObject& rOutlinerObj, const tools::Rectangle& rCaptionRect,
+ bool bShown )
+{
+ ScNoteData aNoteData(CreateNoteData(rDoc, rPos, rCaptionRect, bShown));
+ ScCaptionInitData& rInitData = *aNoteData.mxInitData;
+ rInitData.mxOutlinerObj = rOutlinerObj;
+ rInitData.moItemSet.emplace(rItemSet);
+ rInitData.maStyleName = ScStyleNameConversion::ProgrammaticToDisplayName(rStyleName, SfxStyleFamily::Frame);
+
+ return InsertNote(rDoc, rPos, std::move(aNoteData), /*bAlwaysCreateCaption*/false, 0/*nPostItId*/);
+}
+
+ScPostIt* ScNoteUtil::CreateNoteFromGenerator(
+ ScDocument& rDoc, const ScAddress& rPos,
+ std::unique_ptr<GenerateNoteCaption> xGenerator,
+ const tools::Rectangle& rCaptionRect,
+ bool bShown )
+{
+ ScNoteData aNoteData(CreateNoteData(rDoc, rPos, rCaptionRect, bShown));
+ ScCaptionInitData& rInitData = *aNoteData.mxInitData;
+ rInitData.mxGenerator = std::move(xGenerator);
+ // because the Caption is generated on demand, we will need to create the
+ // simple text now to supply any queries for that which don't require
+ // creation of a full Caption
+ rInitData.maSimpleText = rInitData.mxGenerator->GetSimpleText();
+
+ return InsertNote(rDoc, rPos, std::move(aNoteData), /*bAlwaysCreateCaption*/false, 0/*nPostItId*/);
+}
+
+ScPostIt* ScNoteUtil::InsertNote(ScDocument& rDoc, const ScAddress& rPos, ScNoteData&& rNoteData,
+ bool bAlwaysCreateCaption, sal_uInt32 nPostItId)
+{
+ /* Create the note and insert it into the document. If the note is
+ visible, the caption object will be created automatically. */
+ ScPostIt* pNote = new ScPostIt( rDoc, rPos, std::move(rNoteData), bAlwaysCreateCaption, nPostItId );
+ pNote->AutoStamp();
+ //insert takes ownership
+ rDoc.SetNote(rPos, std::unique_ptr<ScPostIt>(pNote));
+ return pNote;
+}
+
+ScPostIt* ScNoteUtil::CreateNoteFromString(
+ ScDocument& rDoc, const ScAddress& rPos, const OUString& rNoteText,
+ bool bShown, bool bAlwaysCreateCaption, sal_uInt32 nPostItId )
+{
+ ScPostIt* pNote = nullptr;
+ if( !rNoteText.isEmpty() )
+ {
+ ScNoteData aNoteData( bShown );
+ aNoteData.mxInitData = std::make_shared<ScCaptionInitData>();
+ ScCaptionInitData& rInitData = *aNoteData.mxInitData;
+ rInitData.maSimpleText = rNoteText;
+ rInitData.maStyleName = ScResId(STR_STYLENAME_NOTE);
+ rInitData.mbDefaultPosSize = true;
+
+ pNote = InsertNote(rDoc, rPos, std::move(aNoteData), bAlwaysCreateCaption, nPostItId);
+ }
+ return pNote;
+}
+
+namespace sc {
+
+NoteEntry::NoteEntry( const ScAddress& rPos, const ScPostIt* pNote ) :
+ maPos(rPos), mpNote(pNote) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/queryevaluator.cxx b/sc/source/core/data/queryevaluator.cxx
new file mode 100644
index 0000000000..a8a08d9edf
--- /dev/null
+++ b/sc/source/core/data/queryevaluator.cxx
@@ -0,0 +1,902 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <queryevaluator.hxx>
+
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+#include <colorscale.hxx>
+#include <document.hxx>
+#include <docoptio.hxx>
+#include <queryparam.hxx>
+#include <scitems.hxx>
+#include <table.hxx>
+
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <svl/zformat.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+bool ScQueryEvaluator::isPartialTextMatchOp(ScQueryOp eOp)
+{
+ switch (eOp)
+ {
+ // these operators can only be used with textural comparisons.
+ case SC_CONTAINS:
+ case SC_DOES_NOT_CONTAIN:
+ case SC_BEGINS_WITH:
+ case SC_ENDS_WITH:
+ case SC_DOES_NOT_BEGIN_WITH:
+ case SC_DOES_NOT_END_WITH:
+ return true;
+ default:;
+ }
+ return false;
+}
+
+bool ScQueryEvaluator::isTextMatchOp(ScQueryOp eOp)
+{
+ if (isPartialTextMatchOp(eOp))
+ return true;
+
+ switch (eOp)
+ {
+ // these operators can be used for either textural or value comparison.
+ case SC_EQUAL:
+ case SC_NOT_EQUAL:
+ return true;
+ default:;
+ }
+ return false;
+}
+
+bool ScQueryEvaluator::isMatchWholeCellHelper(bool docMatchWholeCell, ScQueryOp eOp)
+{
+ bool bMatchWholeCell = docMatchWholeCell;
+ if (isPartialTextMatchOp(eOp))
+ // may have to do partial textural comparison.
+ bMatchWholeCell = false;
+ return bMatchWholeCell;
+}
+
+bool ScQueryEvaluator::isMatchWholeCell(ScQueryOp eOp) const
+{
+ return isMatchWholeCellHelper(mbMatchWholeCell, eOp);
+}
+
+bool ScQueryEvaluator::isMatchWholeCell(const ScDocument& rDoc, ScQueryOp eOp)
+{
+ return isMatchWholeCellHelper(rDoc.GetDocOptions().IsMatchWholeCell(), eOp);
+}
+
+void ScQueryEvaluator::setupTransliteratorIfNeeded()
+{
+ if (!mpTransliteration)
+ mpTransliteration = &ScGlobal::GetTransliteration(mrParam.bCaseSens);
+}
+
+void ScQueryEvaluator::setupCollatorIfNeeded()
+{
+ if (!mpCollator)
+ mpCollator = &ScGlobal::GetCollator(mrParam.bCaseSens);
+}
+
+ScQueryEvaluator::ScQueryEvaluator(ScDocument& rDoc, const ScTable& rTab,
+ const ScQueryParam& rParam, const ScInterpreterContext* pContext,
+ bool* pTestEqualCondition)
+ : mrDoc(rDoc)
+ , mrStrPool(rDoc.GetSharedStringPool())
+ , mrTab(rTab)
+ , mrParam(rParam)
+ , mpTestEqualCondition(pTestEqualCondition)
+ , mpTransliteration(nullptr)
+ , mpCollator(nullptr)
+ , mbMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell())
+ , mbCaseSensitive(rParam.bCaseSens)
+ , mpContext(pContext)
+ , mnEntryCount(mrParam.GetEntryCount())
+{
+ if (mnEntryCount <= nFixedBools)
+ {
+ mpPasst = &maBool[0];
+ mpTest = &maTest[0];
+ }
+ else
+ {
+ mpBoolDynamic.reset(new bool[mnEntryCount]);
+ mpTestDynamic.reset(new bool[mnEntryCount]);
+ mpPasst = mpBoolDynamic.get();
+ mpTest = mpTestDynamic.get();
+ }
+}
+
+bool ScQueryEvaluator::isRealWildOrRegExp(const ScQueryEntry& rEntry) const
+{
+ if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
+ return false;
+
+ return isTextMatchOp(rEntry.eOp);
+}
+
+bool ScQueryEvaluator::isTestWildOrRegExp(const ScQueryEntry& rEntry) const
+{
+ if (!mpTestEqualCondition)
+ return false;
+
+ if (mrParam.eSearchType == utl::SearchParam::SearchType::Normal)
+ return false;
+
+ return (rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL);
+}
+
+bool ScQueryEvaluator::isQueryByValue(ScQueryOp eOp, ScQueryEntry::QueryType eType,
+ const ScRefCellValue& rCell)
+{
+ if (eType == ScQueryEntry::ByString || isPartialTextMatchOp(eOp))
+ return false;
+
+ return isQueryByValueForCell(rCell);
+}
+
+bool ScQueryEvaluator::isQueryByValueForCell(const ScRefCellValue& rCell)
+{
+ if (rCell.getType() == CELLTYPE_FORMULA
+ && rCell.getFormula()->GetErrCode() != FormulaError::NONE)
+ // Error values are compared as string.
+ return false;
+
+ return rCell.hasNumeric();
+}
+
+bool ScQueryEvaluator::isQueryByString(ScQueryOp eOp, ScQueryEntry::QueryType eType,
+ const ScRefCellValue& rCell)
+{
+ if (isTextMatchOp(eOp))
+ return true;
+
+ if (eType != ScQueryEntry::ByString)
+ return false;
+
+ return rCell.hasString();
+}
+
+sal_uInt32 ScQueryEvaluator::getNumFmt(SCCOL nCol, SCROW nRow)
+{
+ sal_uInt32 nNumFmt
+ = (mpContext ? mrTab.GetNumberFormat(*mpContext, ScAddress(nCol, nRow, mrTab.GetTab()))
+ : mrTab.GetNumberFormat(nCol, nRow));
+ if (nNumFmt && (nNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ // Any General of any locale is irrelevant for rounding.
+ nNumFmt = 0;
+ return nNumFmt;
+}
+
+std::pair<bool, bool> ScQueryEvaluator::compareByValue(const ScRefCellValue& rCell, SCCOL nCol,
+ SCROW nRow, const ScQueryEntry& rEntry,
+ const ScQueryEntry::Item& rItem)
+{
+ bool bOk = false;
+ bool bTestEqual = false;
+ double nCellVal;
+ double fQueryVal = rItem.mfVal;
+ // Defer all number format detection to as late as possible as it's a
+ // bottle neck, even if that complicates the code. Also do not
+ // unnecessarily call ScDocument::RoundValueAsShown() for the same
+ // reason.
+ sal_uInt32 nNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ nCellVal = rCell.getDouble();
+ break;
+ case CELLTYPE_FORMULA:
+ nCellVal = rCell.getFormula()->GetValue();
+ break;
+ default:
+ nCellVal = 0.0;
+ }
+ if (rItem.mbRoundForFilter && nCellVal != 0.0)
+ {
+ nNumFmt = getNumFmt(nCol, nRow);
+ if (nNumFmt)
+ {
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ case CELLTYPE_FORMULA:
+ nCellVal = mrDoc.RoundValueAsShown(nCellVal, nNumFmt, mpContext);
+ break;
+ default:
+ assert(!"can't be");
+ }
+ }
+ }
+
+ /* NOTE: lcl_PrepareQuery() prepares a filter query such that if a
+ * date+time format was queried rEntry.bQueryByDate is not set. In
+ * case other queries wanted to use this mechanism they should do
+ * the same, in other words only if rEntry.nVal is an integer value
+ * rEntry.bQueryByDate should be true and the time fraction be
+ * stripped here. */
+
+ if (rItem.meType == ScQueryEntry::ByDate)
+ {
+ if (nNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ nNumFmt = getNumFmt(nCol, nRow);
+ if (nNumFmt)
+ {
+ SvNumberFormatter* pFormatter
+ = mpContext ? mpContext->GetFormatTable() : mrDoc.GetFormatTable();
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nNumFmt);
+ if (pEntry)
+ {
+ SvNumFormatType nNumFmtType = pEntry->GetType();
+ /* NOTE: Omitting the check for absence of
+ * css::util::NumberFormat::TIME would include also date+time formatted
+ * values of the same day. That may be desired in some
+ * cases, querying all time values of a day, but confusing
+ * in other cases. A user can always setup a standard
+ * filter query for x >= date AND x < date+1 */
+ if ((nNumFmtType & SvNumFormatType::DATE) && !(nNumFmtType & SvNumFormatType::TIME))
+ {
+ // The format is of date type. Strip off the time
+ // element.
+ nCellVal = ::rtl::math::approxFloor(nCellVal);
+ }
+ }
+ }
+ }
+
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ bOk = ::rtl::math::approxEqual(nCellVal, fQueryVal);
+ break;
+ case SC_LESS:
+ bOk = (nCellVal < fQueryVal) && !::rtl::math::approxEqual(nCellVal, fQueryVal);
+ break;
+ case SC_GREATER:
+ bOk = (nCellVal > fQueryVal) && !::rtl::math::approxEqual(nCellVal, fQueryVal);
+ break;
+ case SC_LESS_EQUAL:
+ bOk = (nCellVal < fQueryVal) || ::rtl::math::approxEqual(nCellVal, fQueryVal);
+ if (bOk && mpTestEqualCondition)
+ bTestEqual = ::rtl::math::approxEqual(nCellVal, fQueryVal);
+ break;
+ case SC_GREATER_EQUAL:
+ bOk = (nCellVal > fQueryVal) || ::rtl::math::approxEqual(nCellVal, fQueryVal);
+ if (bOk && mpTestEqualCondition)
+ bTestEqual = ::rtl::math::approxEqual(nCellVal, fQueryVal);
+ break;
+ case SC_NOT_EQUAL:
+ bOk = !::rtl::math::approxEqual(nCellVal, fQueryVal);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ return std::pair<bool, bool>(bOk, bTestEqual);
+}
+
+OUString ScQueryEvaluator::getCellString(const ScRefCellValue& rCell, SCROW nRow, SCCOL nCol,
+ const svl::SharedString** sharedString)
+{
+ if (rCell.getType() == CELLTYPE_FORMULA
+ && rCell.getFormula()->GetErrCode() != FormulaError::NONE)
+ {
+ // Error cell is evaluated as string (for now).
+ const FormulaError error = rCell.getFormula()->GetErrCode();
+ auto it = mCachedSharedErrorStrings.find(error);
+ if (it == mCachedSharedErrorStrings.end())
+ {
+ svl::SharedString str = mrStrPool.intern(ScGlobal::GetErrorString(error));
+ auto pos = mCachedSharedErrorStrings.insert({ error, str });
+ assert(pos.second); // inserted
+ it = pos.first;
+ }
+ *sharedString = &it->second;
+ return OUString();
+ }
+ else if (rCell.getType() == CELLTYPE_STRING)
+ {
+ *sharedString = rCell.getSharedString();
+ return OUString();
+ }
+ else
+ {
+ sal_uInt32 nFormat
+ = mpContext ? mrTab.GetNumberFormat(*mpContext, ScAddress(nCol, nRow, mrTab.GetTab()))
+ : mrTab.GetNumberFormat(nCol, nRow);
+ SvNumberFormatter* pFormatter
+ = mpContext ? mpContext->GetFormatTable() : mrDoc.GetFormatTable();
+ return ScCellFormat::GetInputString(rCell, nFormat, *pFormatter, mrDoc, sharedString, true);
+ }
+}
+
+bool ScQueryEvaluator::isFastCompareByString(const ScQueryEntry& rEntry) const
+{
+ // If this is true, then there's a fast path in compareByString() which
+ // can be selected using the template argument to get fast code
+ // that will not check the same conditions every time. This makes a difference
+ // in fast lookups that search for an exact value (case sensitive or not).
+ const bool bRealWildOrRegExp = isRealWildOrRegExp(rEntry);
+ const bool bTestWildOrRegExp = isTestWildOrRegExp(rEntry);
+ // SC_EQUAL is part of isTextMatchOp(rEntry)
+ return rEntry.eOp == SC_EQUAL && !bRealWildOrRegExp && !bTestWildOrRegExp
+ && isMatchWholeCell(rEntry.eOp);
+}
+
+// The value is placed inside one parameter: [pValueSource1] or [pValueSource2] but never in both.
+// For the template argument see isFastCompareByString().
+template <bool bFast>
+std::pair<bool, bool> ScQueryEvaluator::compareByString(const ScQueryEntry& rEntry,
+ const ScQueryEntry::Item& rItem,
+ const svl::SharedString* pValueSource1,
+ const OUString* pValueSource2)
+{
+ bool bOk = false;
+ bool bTestEqual = false;
+ bool bMatchWholeCell;
+ if (bFast)
+ bMatchWholeCell = true;
+ else
+ bMatchWholeCell = isMatchWholeCell(rEntry.eOp);
+ const bool bRealWildOrRegExp = !bFast && isRealWildOrRegExp(rEntry);
+ const bool bTestWildOrRegExp = !bFast && isTestWildOrRegExp(rEntry);
+
+ assert(!bFast || pValueSource1 != nullptr); // shared string for fast path
+ // [pValueSource1] or [pValueSource2] but never both of them or none of them
+ assert((pValueSource1 != nullptr) != (pValueSource2 != nullptr));
+
+ if (!bFast && (bRealWildOrRegExp || bTestWildOrRegExp))
+ {
+ const OUString& rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
+
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = rValue.getLength();
+
+ // from 614 on, nEnd is behind the found text
+ bool bMatch = false;
+ if (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH)
+ {
+ nEnd = 0;
+ nStart = rValue.getLength();
+ bMatch
+ = rEntry.GetSearchTextPtr(mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell)
+ ->SearchBackward(rValue, &nStart, &nEnd);
+ }
+ else
+ {
+ bMatch
+ = rEntry.GetSearchTextPtr(mrParam.eSearchType, mrParam.bCaseSens, bMatchWholeCell)
+ ->SearchForward(rValue, &nStart, &nEnd);
+ }
+ if (bMatch && bMatchWholeCell && (nStart != 0 || nEnd != rValue.getLength()))
+ bMatch = false; // RegExp must match entire cell string
+ if (bRealWildOrRegExp)
+ {
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ case SC_CONTAINS:
+ bOk = bMatch;
+ break;
+ case SC_NOT_EQUAL:
+ case SC_DOES_NOT_CONTAIN:
+ bOk = !bMatch;
+ break;
+ case SC_BEGINS_WITH:
+ bOk = (bMatch && (nStart == 0));
+ break;
+ case SC_DOES_NOT_BEGIN_WITH:
+ bOk = !(bMatch && (nStart == 0));
+ break;
+ case SC_ENDS_WITH:
+ bOk = (bMatch && (nEnd == rValue.getLength()));
+ break;
+ case SC_DOES_NOT_END_WITH:
+ bOk = !(bMatch && (nEnd == rValue.getLength()));
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ else
+ bTestEqual = bMatch;
+ }
+ if (bFast || !bRealWildOrRegExp)
+ {
+ // Simple string matching i.e. no regexp match.
+ if (bFast || isTextMatchOp(rEntry.eOp))
+ {
+ // Check this even with bFast.
+ if (rItem.meType != ScQueryEntry::ByString && rItem.maString.isEmpty())
+ {
+ // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup),
+ // the query value is assigned directly, and the string is empty. In that case,
+ // don't find any string (isEqual would find empty string results in formula cells).
+ bOk = false;
+ if (rEntry.eOp == SC_NOT_EQUAL)
+ bOk = !bOk;
+ }
+ else if (bFast || bMatchWholeCell)
+ {
+ if (bFast || pValueSource1)
+ {
+ // Fast string equality check by comparing string identifiers.
+ // This is the bFast path, all conditions should lead here on bFast == true.
+ if (mrParam.bCaseSens)
+ {
+ bOk = pValueSource1->getData() == rItem.maString.getData();
+ }
+ else
+ {
+ bOk = pValueSource1->getDataIgnoreCase()
+ == rItem.maString.getDataIgnoreCase();
+ }
+ }
+ else // if (pValueSource2)
+ {
+ if (mrParam.bCaseSens)
+ {
+ bOk = (*pValueSource2 == rItem.maString.getString());
+ }
+ else
+ {
+ // fallback
+ const svl::SharedString rSource2(mrStrPool.intern(*pValueSource2));
+ // Fast string equality check by comparing string identifiers.
+ bOk = rSource2.getDataIgnoreCase() == rItem.maString.getDataIgnoreCase();
+ }
+ }
+
+ if (!bFast && rEntry.eOp == SC_NOT_EQUAL)
+ bOk = !bOk;
+ }
+ else
+ {
+ // Where do we find a match (if at all)
+ sal_Int32 nStrPos;
+
+ if (!mbCaseSensitive)
+ { // Common case for vlookup etc.
+ const svl::SharedString rSource(
+ pValueSource1 ? *pValueSource1 : mrStrPool.intern(*pValueSource2));
+
+ const rtl_uString* pQuer = rItem.maString.getDataIgnoreCase();
+ const rtl_uString* pCellStr = rSource.getDataIgnoreCase();
+
+ assert(pCellStr != nullptr);
+ if (pQuer == nullptr)
+ pQuer = svl::SharedString::getEmptyString().getDataIgnoreCase();
+
+ const sal_Int32 nIndex
+ = (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH)
+ ? (pCellStr->length - pQuer->length)
+ : 0;
+
+ if (nIndex < 0)
+ nStrPos = -1;
+ else
+ { // OUString::indexOf
+ nStrPos = rtl_ustr_indexOfStr_WithLength(pCellStr->buffer + nIndex,
+ pCellStr->length - nIndex,
+ pQuer->buffer, pQuer->length);
+
+ if (nStrPos >= 0)
+ nStrPos += nIndex;
+ }
+ }
+ else
+ {
+ const OUString& rValue
+ = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
+ const OUString aQueryStr = rItem.maString.getString();
+ const LanguageType nLang
+ = ScGlobal::oSysLocale->GetLanguageTag().getLanguageType();
+ setupTransliteratorIfNeeded();
+ const OUString aCell(mpTransliteration->transliterate(
+ rValue, nLang, 0, rValue.getLength(), nullptr));
+
+ const OUString aQuer(mpTransliteration->transliterate(
+ aQueryStr, nLang, 0, aQueryStr.getLength(), nullptr));
+
+ const sal_Int32 nIndex
+ = (rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH)
+ ? (aCell.getLength() - aQuer.getLength())
+ : 0;
+ nStrPos = ((nIndex < 0) ? -1 : aCell.indexOf(aQuer, nIndex));
+ }
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL:
+ case SC_CONTAINS:
+ bOk = (nStrPos != -1);
+ break;
+ case SC_NOT_EQUAL:
+ case SC_DOES_NOT_CONTAIN:
+ bOk = (nStrPos == -1);
+ break;
+ case SC_BEGINS_WITH:
+ bOk = (nStrPos == 0);
+ break;
+ case SC_DOES_NOT_BEGIN_WITH:
+ bOk = (nStrPos != 0);
+ break;
+ case SC_ENDS_WITH:
+ bOk = (nStrPos >= 0);
+ break;
+ case SC_DOES_NOT_END_WITH:
+ bOk = (nStrPos < 0);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+ else
+ { // use collator here because data was probably sorted
+ const OUString& rValue = pValueSource1 ? pValueSource1->getString() : *pValueSource2;
+ setupCollatorIfNeeded();
+ sal_Int32 nCompare = mpCollator->compareString(rValue, rItem.maString.getString());
+ switch (rEntry.eOp)
+ {
+ case SC_LESS:
+ bOk = (nCompare < 0);
+ break;
+ case SC_GREATER:
+ bOk = (nCompare > 0);
+ break;
+ case SC_LESS_EQUAL:
+ bOk = (nCompare <= 0);
+ if (bOk && mpTestEqualCondition && !bTestEqual)
+ bTestEqual = (nCompare == 0);
+ break;
+ case SC_GREATER_EQUAL:
+ bOk = (nCompare >= 0);
+ if (bOk && mpTestEqualCondition && !bTestEqual)
+ bTestEqual = (nCompare == 0);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+
+ return std::pair<bool, bool>(bOk, bTestEqual);
+}
+
+std::pair<bool, bool> ScQueryEvaluator::compareByTextColor(SCCOL nCol, SCROW nRow,
+ const ScQueryEntry::Item& rItem)
+{
+ ScAddress aPos(nCol, nRow, mrTab.GetTab());
+ Color color = mrTab.GetCellTextColor(aPos);
+
+ bool bMatch = rItem.maColor == color;
+ return std::pair<bool, bool>(bMatch, false);
+}
+
+std::pair<bool, bool> ScQueryEvaluator::compareByBackgroundColor(SCCOL nCol, SCROW nRow,
+ const ScQueryEntry::Item& rItem)
+{
+ ScAddress aPos(nCol, nRow, mrTab.GetTab());
+ Color color = mrTab.GetCellBackgroundColor(aPos);
+
+ bool bMatch = rItem.maColor == color;
+ return std::pair<bool, bool>(bMatch, false);
+}
+
+// To be called only if both isQueryByValue() and isQueryByString()
+// returned false and range lookup is wanted! In range lookup comparison
+// numbers are less than strings. Nothing else is compared.
+std::pair<bool, bool> ScQueryEvaluator::compareByRangeLookup(const ScRefCellValue& rCell,
+ const ScQueryEntry& rEntry,
+ const ScQueryEntry::Item& rItem)
+{
+ bool bTestEqual = false;
+
+ if (rItem.meType == ScQueryEntry::ByString && rEntry.eOp != SC_LESS
+ && rEntry.eOp != SC_LESS_EQUAL)
+ return std::pair<bool, bool>(false, bTestEqual);
+
+ if (rItem.meType != ScQueryEntry::ByString && rEntry.eOp != SC_GREATER
+ && rEntry.eOp != SC_GREATER_EQUAL)
+ return std::pair<bool, bool>(false, bTestEqual);
+
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ if (rCell.getType() == CELLTYPE_FORMULA
+ && rCell.getFormula()->GetErrCode() != FormulaError::NONE)
+ // Error values are compared as string.
+ return std::pair<bool, bool>(false, bTestEqual);
+
+ return std::pair<bool, bool>(rCell.hasNumeric(), bTestEqual);
+ }
+
+ return std::pair<bool, bool>(!rCell.hasNumeric(), bTestEqual);
+}
+
+std::pair<bool, bool> ScQueryEvaluator::processEntry(SCROW nRow, SCCOL nCol, ScRefCellValue& aCell,
+ const ScQueryEntry& rEntry, size_t nEntryIndex)
+{
+ std::pair<bool, bool> aRes(false, false);
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ if (rItems.size() == 1 && rItems.front().meType == ScQueryEntry::ByEmpty)
+ {
+ if (rEntry.IsQueryByEmpty())
+ aRes.first = aCell.isEmpty();
+ else
+ {
+ assert(rEntry.IsQueryByNonEmpty());
+ aRes.first = !aCell.isEmpty();
+ }
+ return aRes;
+ }
+ if (rEntry.eOp == SC_EQUAL && rItems.size() >= 10)
+ {
+ // If there are many items to query for (autofilter does this), then try to search
+ // efficiently in those items. So first search all the items of the relevant type,
+ // If that does not find anything, fall back to the generic code.
+ double value = 0;
+ bool valid = true;
+ // For ScQueryEntry::ByValue check that the cell either is a value or is a formula
+ // that has a value and is not an error (those are compared as strings). This
+ // is basically simplified isQueryByValue().
+ if (aCell.getType() == CELLTYPE_VALUE)
+ value = aCell.getDouble();
+ else if (aCell.getType() == CELLTYPE_FORMULA
+ && aCell.getFormula()->GetErrCode() != FormulaError::NONE
+ && aCell.getFormula()->IsValue())
+ {
+ value = aCell.getFormula()->GetValue();
+ }
+ else
+ valid = false;
+ if (valid)
+ {
+ if (rItems.size() >= 100)
+ {
+ // Sort, cache and binary search for the value in items.
+ // Don't bother comparing approximately.
+ if (mCachedSortedItemValues.size() <= nEntryIndex)
+ {
+ mCachedSortedItemValues.resize(nEntryIndex + 1);
+ auto& values = mCachedSortedItemValues[nEntryIndex];
+ values.reserve(rItems.size());
+ for (const auto& rItem : rItems)
+ if (rItem.meType == ScQueryEntry::ByValue)
+ values.push_back(rItem.mfVal);
+ std::sort(values.begin(), values.end());
+ }
+ auto& values = mCachedSortedItemValues[nEntryIndex];
+ auto it = std::lower_bound(values.begin(), values.end(), value);
+ if (it != values.end() && *it == value)
+ return std::make_pair(true, true);
+ }
+ else
+ {
+ for (const auto& rItem : rItems)
+ {
+ // For speed don't bother comparing approximately here, usually there either
+ // will be an exact match or it wouldn't match anyway.
+ if (rItem.meType == ScQueryEntry::ByValue && value == rItem.mfVal)
+ {
+ return std::make_pair(true, true);
+ }
+ }
+ }
+ }
+ }
+ const svl::SharedString* cellSharedString = nullptr;
+ std::optional<OUString> oCellString;
+ const bool bFastCompareByString = isFastCompareByString(rEntry);
+ if (rEntry.eOp == SC_EQUAL && rItems.size() >= 10 && bFastCompareByString)
+ {
+ // The same as above but for strings. Try to optimize the case when
+ // it's a svl::SharedString comparison. That happens when SC_EQUAL is used
+ // and simple matching is used, see compareByString()
+ if (!oCellString)
+ oCellString = getCellString(aCell, nRow, rEntry.nField, &cellSharedString);
+ // Allow also checking ScQueryEntry::ByValue if the cell is not numeric,
+ // as in that case isQueryByNumeric() would be false and isQueryByString() would
+ // be true because of SC_EQUAL making isTextMatchOp() true.
+ bool compareByValue = !isQueryByValueForCell(aCell);
+ // For ScQueryEntry::ByString check that the cell is represented by a shared string,
+ // which means it's either a string cell or a formula error. This is not as
+ // generous as isQueryByString() but it should be enough and better be safe.
+ if (cellSharedString != nullptr)
+ {
+ if (rItems.size() >= 100)
+ {
+ // Sort, cache and binary search for the string in items.
+ // Since each SharedString is identified by pointer value,
+ // sorting by pointer value is enough.
+ if (mCachedSortedItemStrings.size() <= nEntryIndex)
+ {
+ mCachedSortedItemStrings.resize(nEntryIndex + 1);
+ auto& values = mCachedSortedItemStrings[nEntryIndex];
+ values.reserve(rItems.size());
+ for (const auto& rItem : rItems)
+ {
+ if (rItem.meType == ScQueryEntry::ByString
+ || (compareByValue && rItem.meType == ScQueryEntry::ByValue))
+ {
+ values.push_back(mrParam.bCaseSens
+ ? rItem.maString.getData()
+ : rItem.maString.getDataIgnoreCase());
+ }
+ }
+ std::sort(values.begin(), values.end());
+ }
+ auto& values = mCachedSortedItemStrings[nEntryIndex];
+ const rtl_uString* string = mrParam.bCaseSens
+ ? cellSharedString->getData()
+ : cellSharedString->getDataIgnoreCase();
+ auto it = std::lower_bound(values.begin(), values.end(), string);
+ if (it != values.end() && *it == string)
+ return std::make_pair(true, true);
+ }
+ else
+ {
+ for (const auto& rItem : rItems)
+ {
+ if ((rItem.meType == ScQueryEntry::ByString
+ || (compareByValue && rItem.meType == ScQueryEntry::ByValue))
+ && (mrParam.bCaseSens
+ ? cellSharedString->getData() == rItem.maString.getData()
+ : cellSharedString->getDataIgnoreCase()
+ == rItem.maString.getDataIgnoreCase()))
+ {
+ return std::make_pair(true, true);
+ }
+ }
+ }
+ }
+ }
+ // Generic handling.
+ for (const auto& rItem : rItems)
+ {
+ if (rItem.meType == ScQueryEntry::ByTextColor)
+ {
+ std::pair<bool, bool> aThisRes = compareByTextColor(nCol, nRow, rItem);
+ aRes.first |= aThisRes.first;
+ aRes.second |= aThisRes.second;
+ }
+ else if (rItem.meType == ScQueryEntry::ByBackgroundColor)
+ {
+ std::pair<bool, bool> aThisRes = compareByBackgroundColor(nCol, nRow, rItem);
+ aRes.first |= aThisRes.first;
+ aRes.second |= aThisRes.second;
+ }
+ else if (isQueryByValue(rEntry.eOp, rItem.meType, aCell))
+ {
+ std::pair<bool, bool> aThisRes = compareByValue(aCell, nCol, nRow, rEntry, rItem);
+ aRes.first |= aThisRes.first;
+ aRes.second |= aThisRes.second;
+ }
+ else if (isQueryByString(rEntry.eOp, rItem.meType, aCell))
+ {
+ if (!oCellString)
+ oCellString = getCellString(aCell, nRow, rEntry.nField, &cellSharedString);
+ std::pair<bool, bool> aThisRes;
+ if (cellSharedString && bFastCompareByString) // fast
+ aThisRes = compareByString<true>(rEntry, rItem, cellSharedString, nullptr);
+ else if (cellSharedString)
+ aThisRes = compareByString(rEntry, rItem, cellSharedString, nullptr);
+ else
+ aThisRes = compareByString(rEntry, rItem, nullptr, &*oCellString);
+ aRes.first |= aThisRes.first;
+ aRes.second |= aThisRes.second;
+ }
+ else if (mrParam.mbRangeLookup)
+ {
+ std::pair<bool, bool> aThisRes = compareByRangeLookup(aCell, rEntry, rItem);
+ aRes.first |= aThisRes.first;
+ aRes.second |= aThisRes.second;
+ }
+
+ if (aRes.first && (aRes.second || mpTestEqualCondition == nullptr))
+ break;
+ }
+ return aRes;
+}
+
+bool ScQueryEvaluator::ValidQuery(SCROW nRow, const ScRefCellValue* pCell,
+ sc::TableColumnBlockPositionSet* pBlockPos)
+{
+ if (!mrParam.GetEntry(0).bDoQuery)
+ return true;
+
+ tools::Long nPos = -1;
+ ScQueryParam::const_iterator it, itBeg = mrParam.begin(), itEnd = mrParam.end();
+ for (it = itBeg; it != itEnd && it->bDoQuery; ++it)
+ {
+ const ScQueryEntry& rEntry = *it;
+
+ // Short-circuit the test at the end of the loop - if this is SC_AND
+ // and the previous value is false, this value will not be needed.
+ // Disable this if pbTestEqualCondition is present as that one may get set
+ // even if the result is false (that also means pTest doesn't need to be
+ // handled here).
+ if (rEntry.eConnect == SC_AND && mpTestEqualCondition == nullptr && nPos != -1
+ && !mpPasst[nPos])
+ {
+ continue;
+ }
+
+ SCCOL nCol = static_cast<SCCOL>(rEntry.nField);
+
+ // We can only handle one single direct query passed as a known pCell,
+ // subsequent queries have to obtain the cell.
+ ScRefCellValue aCell;
+ if (pCell && it == itBeg)
+ aCell = *pCell;
+ else if (pBlockPos)
+ { // hinted mdds access
+ aCell = const_cast<ScTable&>(mrTab).GetCellValue(
+ nCol, *pBlockPos->getBlockPosition(nCol), nRow);
+ }
+ else
+ aCell = mrTab.GetCellValue(nCol, nRow);
+
+ std::pair<bool, bool> aRes = processEntry(nRow, nCol, aCell, rEntry, it - itBeg);
+
+ if (nPos == -1)
+ {
+ nPos++;
+ mpPasst[nPos] = aRes.first;
+ mpTest[nPos] = aRes.second;
+ }
+ else
+ {
+ if (rEntry.eConnect == SC_AND)
+ {
+ mpPasst[nPos] = mpPasst[nPos] && aRes.first;
+ mpTest[nPos] = mpTest[nPos] && aRes.second;
+ }
+ else
+ {
+ nPos++;
+ mpPasst[nPos] = aRes.first;
+ mpTest[nPos] = aRes.second;
+ }
+ }
+ }
+
+ for (tools::Long j = 1; j <= nPos; j++)
+ {
+ mpPasst[0] = mpPasst[0] || mpPasst[j];
+ mpTest[0] = mpTest[0] || mpTest[j];
+ }
+
+ bool bRet = mpPasst[0];
+ if (mpTestEqualCondition)
+ *mpTestEqualCondition = mpTest[0];
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/queryiter.cxx b/sc/source/core/data/queryiter.cxx
new file mode 100644
index 0000000000..6c5635bd51
--- /dev/null
+++ b/sc/source/core/data/queryiter.cxx
@@ -0,0 +1,1486 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <queryiter.hxx>
+
+#include <comphelper/flagguard.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+
+#include <global.hxx>
+#include <dociter.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <formulacell.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <docoptio.hxx>
+#include <cellform.hxx>
+#include <segmenttree.hxx>
+#include <progress.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <cellvalue.hxx>
+#include <scmatrix.hxx>
+#include <rowheightcontext.hxx>
+#include <queryevaluator.hxx>
+#include <rangecache.hxx>
+#include <refdata.hxx>
+
+#include <tools/fract.hxx>
+#include <editeng/editobj.hxx>
+#include <svl/sharedstring.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
+ScQueryCellIteratorBase< accessType, queryType >::ScQueryCellIteratorBase(ScDocument& rDocument,
+ ScInterpreterContext& rContext, SCTAB nTable, const ScQueryParam& rParam, bool bMod )
+ : AccessBase( rDocument, rContext, rParam )
+ , nStopOnMismatch( nStopOnMismatchDisabled )
+ , nTestEqualCondition( nTestEqualConditionDisabled )
+ , bAdvanceQuery( false )
+ , bIgnoreMismatchOnLeadingStrings( false )
+{
+ nTab = nTable;
+ nCol = maParam.nCol1;
+ nRow = maParam.nRow1;
+ SCSIZE i;
+ if (!bMod) // Or else it's already inserted
+ return;
+
+ SCSIZE nCount = maParam.GetEntryCount();
+ for (i = 0; (i < nCount) && (maParam.GetEntry(i).bDoQuery); ++i)
+ {
+ ScQueryEntry& rEntry = maParam.GetEntry(i);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ sal_uInt32 nIndex = 0;
+ bool bNumber = mrContext.GetFormatTable()->IsNumberFormat(
+ rItem.maString.getString(), nIndex, rItem.mfVal);
+ rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ }
+}
+
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
+void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
+{
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ const ScQueryEntry& rEntry = maParam.GetEntry(0);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+
+ const bool bSingleQueryItem = rEntry.GetQueryItems().size() == 1;
+ SCCOLROW nFirstQueryField = rEntry.nField;
+ bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings &&
+ rItem.meType != ScQueryEntry::ByString;
+ bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
+ !maParam.bHasHeader && rItem.meType == ScQueryEntry::ByString &&
+ ((maParam.bByRow && nRow == maParam.nRow1) ||
+ (!maParam.bByRow && nCol == maParam.nCol1));
+ bool bTestEqualCondition = false;
+ ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], maParam, &mrContext,
+ (nTestEqualCondition ? &bTestEqualCondition : nullptr));
+ if( queryType == ScQueryCellIteratorType::CountIf )
+ {
+ // These are not used for COUNTIF, so should not be set, make the compiler
+ // explicitly aware of it so that the relevant parts are optimized away.
+ assert( !bAllStringIgnore );
+ assert( !bIgnoreMismatchOnLeadingStrings );
+ assert( nStopOnMismatch == nStopOnMismatchDisabled );
+ assert( nTestEqualCondition == nTestEqualConditionDisabled );
+ bAllStringIgnore = false;
+ bIgnoreMismatchOnLeadingStrings = false;
+ nStopOnMismatch = nStopOnMismatchDisabled;
+ nTestEqualCondition = nTestEqualConditionDisabled;
+ // This one is always set.
+ assert( bAdvanceQuery );
+ bAdvanceQuery = true;
+ }
+
+ const ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
+ while (true)
+ {
+ bool bNextColumn = maCurPos.first == pCol->maCells.end();
+ if (!bNextColumn)
+ {
+ if (nRow > maParam.nRow2)
+ bNextColumn = true;
+ }
+
+ if (bNextColumn)
+ {
+ do
+ {
+ ++nCol;
+ if (nCol > maParam.nCol2 || nCol >= rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
+ return;
+ if ( bAdvanceQuery )
+ {
+ AdvanceQueryParamEntryField();
+ nFirstQueryField = rEntry.nField;
+ }
+ pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
+ }
+ while (!rItem.mbMatchEmpty && pCol->IsEmptyData());
+
+ InitPos();
+
+ bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
+ !maParam.bHasHeader && rItem.meType == ScQueryEntry::ByString &&
+ maParam.bByRow;
+ }
+
+ if (maCurPos.first->type == sc::element_type_empty)
+ {
+ if (rItem.mbMatchEmpty && bSingleQueryItem)
+ {
+ // This shortcut, instead of determining if any SC_OR query
+ // exists or this query is SC_AND'ed (which wouldn't make
+ // sense, but..) and evaluating them in ValidQuery(), is
+ // possible only because the interpreter is the only caller
+ // that sets mbMatchEmpty and there is only one item in those
+ // cases.
+ // XXX this would have to be reworked if other filters used it
+ // in different manners and evaluation would have to be done in
+ // ValidQuery().
+ if(HandleItemFound())
+ return;
+ IncPos();
+ continue;
+ }
+ else
+ {
+ IncBlock();
+ continue;
+ }
+ }
+
+ ScRefCellValue aCell = sc::toRefCell(maCurPos.first, maCurPos.second);
+
+ if (bAllStringIgnore && aCell.hasString())
+ IncPos();
+ else
+ {
+ if ( queryEvaluator.ValidQuery( nRow,
+ (nCol == static_cast<SCCOL>(nFirstQueryField) ? &aCell : nullptr)))
+ {
+ if ( nTestEqualCondition && bTestEqualCondition )
+ nTestEqualCondition |= nTestEqualConditionMatched;
+ if ( aCell.isEmpty())
+ return;
+ if( HandleItemFound())
+ return;
+ IncPos();
+ continue;
+ }
+ else if ( nStopOnMismatch )
+ {
+ // Yes, even a mismatch may have a fulfilled equal
+ // condition if regular expressions were involved and
+ // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried.
+ if ( nTestEqualCondition && bTestEqualCondition )
+ {
+ nTestEqualCondition |= nTestEqualConditionMatched;
+ nStopOnMismatch |= nStopOnMismatchOccurred;
+ return;
+ }
+ bool bStop;
+ if (bFirstStringIgnore)
+ {
+ if (aCell.hasString())
+ {
+ IncPos();
+ bStop = false;
+ }
+ else
+ bStop = true;
+ }
+ else
+ bStop = true;
+ if (bStop)
+ {
+ nStopOnMismatch |= nStopOnMismatchOccurred;
+ return;
+ }
+ }
+ else
+ IncPos();
+ }
+ bFirstStringIgnore = false;
+ }
+}
+
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
+void ScQueryCellIteratorBase< accessType, queryType >::InitPos()
+{
+ if constexpr( accessType != ScQueryCellIteratorAccess::SortedCache )
+ AccessBase::InitPos();
+ else
+ {
+ // This should be all in AccessBase::InitPos(), but that one can't call
+ // BinarySearch(), so do it this way instead.
+ AccessBase::InitPosStart();
+ ScQueryOp& op = maParam.GetEntry(0).eOp;
+ SCROW beforeRow = -1;
+ SCROW lastRow = -1;
+ if( op == SC_EQUAL )
+ {
+ if( BinarySearch( nCol ))
+ {
+ // BinarySearch() searches for the last item that matches. Now we
+ // also need to find the first item where to start. Find the last
+ // non-matching position using SC_LESS and the start position
+ // is the one after it.
+ lastRow = nRow;
+ ScQueryOp saveOp = op;
+ op = SC_LESS;
+ if( BinarySearch( nCol, true ))
+ beforeRow = nRow;
+ // If BinarySearch() returns false, there was no match, which means
+ // there's no value smaller. In that case BinarySearch() has set
+ // the position to the first row in the range.
+ op = saveOp; // back to SC_EQUAL
+ }
+ else if( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
+ && rDoc.IsEmptyData(nCol, maParam.nRow1, nCol, maParam.nRow2, nTab))
+ {
+ // BinarySearch() returns false in case it's all empty data,
+ // handle that specially.
+ beforeRow = -1;
+ lastRow = maParam.nRow2;
+ }
+ }
+ else
+ { // The range is from the start up to and including the last matching.
+ if( BinarySearch( nCol ))
+ lastRow = nRow;
+ }
+ AccessBase::InitPosFinish( beforeRow, lastRow );
+ }
+}
+
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
+void ScQueryCellIteratorBase< accessType, queryType >::AdvanceQueryParamEntryField()
+{
+ SCSIZE nEntries = maParam.GetEntryCount();
+ for ( SCSIZE j = 0; j < nEntries; j++ )
+ {
+ ScQueryEntry& rEntry = maParam.GetEntry( j );
+ if ( rEntry.bDoQuery )
+ {
+ if ( rEntry.nField < rDoc.MaxCol() )
+ rEntry.nField++;
+ else
+ {
+ assert(!"AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL");
+ }
+ }
+ else
+ break; // for
+ }
+}
+
+namespace {
+
+template<typename Iter>
+void incBlock(std::pair<Iter, size_t>& rPos)
+{
+ // Move to the next block.
+ ++rPos.first;
+ rPos.second = 0;
+}
+
+template<typename Iter>
+void decBlock(std::pair<Iter, size_t>& rPos)
+{
+ // Move to the last element of the previous block.
+ --rPos.first;
+ rPos.second = rPos.first->size - 1;
+}
+
+}
+
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType >
+bool ScQueryCellIteratorBase< accessType, queryType >::BinarySearch( SCCOL col, bool forEqual )
+{
+ assert(maParam.GetEntry(0).bDoQuery && !maParam.GetEntry(1).bDoQuery
+ && maParam.GetEntry(0).GetQueryItems().size() == 1 );
+ assert(maParam.eSearchType == utl::SearchParam::SearchType::Normal);
+ assert(maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString
+ || maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue);
+ assert(maParam.bByRow);
+ assert(maParam.GetEntry(0).eOp == SC_LESS || maParam.GetEntry(0).eOp == SC_LESS_EQUAL
+ || maParam.GetEntry(0).eOp == SC_GREATER || maParam.GetEntry(0).eOp == SC_GREATER_EQUAL
+ || maParam.GetEntry(0).eOp == SC_EQUAL);
+
+ // TODO: This will be extremely slow with mdds::multi_type_vector.
+
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ nCol = col;
+ nRow = maParam.nRow1;
+
+ if (nCol >= rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
+ return false;
+
+ const ScColumn* pCol = &(rDoc.maTabs[nTab])->aCol[nCol];
+ if (pCol->IsEmptyData())
+ return false;
+
+ CollatorWrapper& rCollator = ScGlobal::GetCollator(maParam.bCaseSens);
+ SvNumberFormatter& rFormatter = *(mrContext.GetFormatTable());
+ const ScQueryEntry& rEntry = maParam.GetEntry(0);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bAscending = rEntry.eOp == SC_LESS || rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_EQUAL;
+ bool bByString = rItem.meType == ScQueryEntry::ByString;
+ bool bForceStr = bByString && ( rEntry.eOp == SC_EQUAL || forEqual );
+ bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !bByString;
+ bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings &&
+ !maParam.bHasHeader && bByString;
+
+ if (maParam.bHasHeader)
+ ++nRow;
+
+ if (bFirstStringIgnore)
+ {
+ sc::CellStoreType::const_position_type aPos = pCol->maCells.position(nRow);
+ if (aPos.first->type == sc::element_type_string || aPos.first->type == sc::element_type_edittext)
+ {
+ ScRefCellValue aCell = sc::toRefCell(aPos.first, aPos.second);
+ sal_uInt32 nFormat = pCol->GetNumberFormat(mrContext, nRow);
+ OUString aCellStr = ScCellFormat::GetInputString(aCell, nFormat, rFormatter, rDoc);
+ sal_Int32 nTmp = rCollator.compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
+ if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) ||
+ (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) ||
+ (rEntry.eOp == SC_EQUAL && nTmp != 0) ||
+ (rEntry.eOp == SC_LESS && nTmp >= 0) ||
+ (rEntry.eOp == SC_GREATER && nTmp <= 0))
+ ++nRow;
+ }
+ }
+
+ // Skip leading empty block, if any.
+ sc::CellStoreType::const_position_type startPos = pCol->maCells.position(nRow);
+ if (startPos.first->type == sc::element_type_empty)
+ incBlock(startPos);
+ if(bAllStringIgnore)
+ {
+ // Skip all leading string or empty blocks.
+ while (startPos.first != pCol->maCells.end()
+ && (startPos.first->type == sc::element_type_string ||
+ startPos.first->type == sc::element_type_edittext ||
+ startPos.first->type == sc::element_type_empty))
+ {
+ incBlock(startPos);
+ }
+ }
+ if(startPos.first == pCol->maCells.end())
+ return false;
+ nRow = startPos.first->position + startPos.second;
+ if (nRow > maParam.nRow2)
+ return false;
+
+ auto aIndexer = MakeBinarySearchIndexer(pCol->maCells, nRow, maParam.nRow2);
+ if (!aIndexer.isValid())
+ return false;
+
+ size_t nLo = aIndexer.getLowIndex();
+ size_t nHi = aIndexer.getHighIndex();
+ BinarySearchCellType aCellData;
+
+ // Bookkeeping values for breaking up the binary search in case the data
+ // range isn't strictly sorted.
+ size_t nLastInRange = nLo;
+ double fLastInRangeValue = bAscending ?
+ -(::std::numeric_limits<double>::max()) :
+ ::std::numeric_limits<double>::max();
+ OUString aLastInRangeString;
+ if (!bAscending)
+ aLastInRangeString = OUString(u'\xFFFF');
+
+ aCellData = aIndexer.getCell(nLastInRange);
+ ScRefCellValue aCell = aCellData.first;
+ if (bForceStr || aCell.hasString())
+ {
+ sal_uInt32 nFormat = pCol->GetNumberFormat(mrContext, aCellData.second);
+ aLastInRangeString = ScCellFormat::GetInputString(aCell, nFormat, rFormatter, rDoc);
+ }
+ else
+ {
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ fLastInRangeValue = aCell.getDouble();
+ break;
+ case CELLTYPE_FORMULA :
+ fLastInRangeValue = aCell.getFormula()->GetValue();
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ sal_Int32 nRes = 0;
+ std::optional<size_t> found;
+ bool bDone = false;
+ bool orderBroken = false;
+ while (nLo <= nHi && !bDone)
+ {
+ size_t nMid = (nLo+nHi)/2;
+ size_t i = nMid;
+
+ aCellData = aIndexer.getCell(i);
+ aCell = aCellData.first;
+ bool bStr = bForceStr || aCell.hasString();
+ nRes = 0;
+
+ // compares are content<query:-1, content>query:1
+ // Cell value comparison similar to ScTable::ValidQuery()
+ if (!bStr && !bByString)
+ {
+ double nCellVal;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ case CELLTYPE_FORMULA :
+ nCellVal = aCell.getValue();
+ break;
+ default:
+ nCellVal = 0.0;
+ }
+ if ((nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(
+ nCellVal, rItem.mfVal))
+ {
+ nRes = -1;
+ if (bAscending)
+ {
+ if (fLastInRangeValue <= nCellVal)
+ {
+ fLastInRangeValue = nCellVal;
+ nLastInRange = i;
+ }
+ else if (fLastInRangeValue >= nCellVal)
+ {
+ // not strictly sorted, continue with GetThis()
+ orderBroken = true;
+ bDone = true;
+ }
+ }
+ }
+ else if ((nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(
+ nCellVal, rItem.mfVal))
+ {
+ nRes = 1;
+ if (!bAscending)
+ {
+ if (fLastInRangeValue >= nCellVal)
+ {
+ fLastInRangeValue = nCellVal;
+ nLastInRange = i;
+ }
+ else if (fLastInRangeValue <= nCellVal)
+ {
+ // not strictly sorted, continue with GetThis()
+ orderBroken = true;
+ bDone = true;
+ }
+ }
+ }
+ }
+ else if (bStr && bByString)
+ {
+ sal_uInt32 nFormat = pCol->GetNumberFormat(mrContext, aCellData.second);
+ OUString aCellStr = ScCellFormat::GetInputString(aCell, nFormat, rFormatter, rDoc);
+
+ nRes = rCollator.compareString(aCellStr, rEntry.GetQueryItem().maString.getString());
+ if (nRes < 0 && bAscending)
+ {
+ sal_Int32 nTmp = rCollator.compareString( aLastInRangeString,
+ aCellStr);
+ if (nTmp <= 0)
+ {
+ aLastInRangeString = aCellStr;
+ nLastInRange = i;
+ }
+ else if (nTmp > 0)
+ {
+ // not strictly sorted, continue with GetThis()
+ orderBroken = true;
+ bDone = true;
+ }
+ }
+ else if (nRes > 0 && !bAscending)
+ {
+ sal_Int32 nTmp = rCollator.compareString( aLastInRangeString,
+ aCellStr);
+ if (nTmp >= 0)
+ {
+ aLastInRangeString = aCellStr;
+ nLastInRange = i;
+ }
+ else if (nTmp < 0)
+ {
+ // not strictly sorted, continue with GetThis()
+ orderBroken = true;
+ bDone = true;
+ }
+ }
+ }
+ else if (!bStr && bByString)
+ {
+ nRes = -1; // numeric < string
+ if (bAscending)
+ nLastInRange = i;
+ }
+ else // if (bStr && !bByString)
+ {
+ nRes = 1; // string > numeric
+ if (!bAscending)
+ nLastInRange = i;
+ }
+ if (nRes < 0)
+ {
+ if (bAscending)
+ nLo = nMid + 1;
+ else // assumed to be SC_GREATER_EQUAL
+ {
+ if (nMid > 0)
+ nHi = nMid - 1;
+ else
+ bDone = true;
+ }
+ }
+ else if (nRes > 0)
+ {
+ if (bAscending)
+ {
+ if (nMid > 0)
+ nHi = nMid - 1;
+ else
+ bDone = true;
+ }
+ else // assumed to be SC_GREATER_EQUAL
+ nLo = nMid + 1;
+ }
+ else
+ {
+ if(rEntry.eOp == SC_LESS_EQUAL || rEntry.eOp == SC_GREATER_EQUAL || rEntry.eOp == SC_EQUAL)
+ {
+ found = i;
+ nLastInRange = i;
+ // But keep searching to find the last matching one.
+ nLo = nMid + 1;
+ }
+ else if (bAscending)
+ {
+ if (nMid > 0)
+ nHi = nMid - 1;
+ else
+ bDone = true;
+ }
+ else
+ {
+ if (nMid > 0)
+ nHi = nMid - 1;
+ else
+ bDone = true;
+ }
+ }
+ }
+
+ bool isInRange;
+ if (orderBroken)
+ {
+ // Reset position to the first row in range and force caller
+ // to search from start.
+ nLo = aIndexer.getLowIndex();
+ isInRange = false;
+ }
+ else if (found)
+ {
+ nLo = *found;
+ isInRange = true;
+ }
+ else
+ {
+ // Not nothing was found and the search position is at the start,
+ // then the possible match would need to be before the data range.
+ // In that case return false to force the caller to search from the start
+ // and detect this.
+ isInRange = nLo != aIndexer.getLowIndex();
+ // If nothing was found, that is either because there is no value
+ // that would match exactly, or the data range is not properly sorted
+ // and we failed to detect (doing so reliably would require a linear scan).
+ // Set the position to the last one that was in matching range (i.e. before
+ // where the exact match would be), and leave sorting it out to GetThis()
+ // or whatever the caller uses.
+ nLo = nLastInRange;
+ }
+
+ aCellData = aIndexer.getCell(nLo);
+ if (nLo <= nHi && aCellData.second <= maParam.nRow2)
+ {
+ nRow = aCellData.second;
+ maCurPos = aIndexer.getPosition(nLo);
+ return isInRange;
+ }
+ else
+ {
+ nRow = maParam.nRow2 + 1;
+ // Set current position to the last possible row.
+ maCurPos.first = pCol->maCells.end();
+ --maCurPos.first;
+ maCurPos.second = maCurPos.first->size - 1;
+ return false;
+ }
+}
+
+
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
+ SCROW& nFoundRow )
+{
+ // Set and automatically reset mpParam->mbRangeLookup when returning.
+ comphelper::FlagRestorationGuard aRangeLookupResetter( maParam.mbRangeLookup, true );
+
+ nFoundCol = rDoc.MaxCol()+1;
+ nFoundRow = rDoc.MaxRow()+1;
+ SetStopOnMismatch( true ); // assume sorted keys
+ SetTestEqualCondition( true );
+ bIgnoreMismatchOnLeadingStrings = true;
+ bool bLiteral = maParam.eSearchType == utl::SearchParam::SearchType::Normal &&
+ maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString;
+ bool bBinary = maParam.bByRow &&
+ (bLiteral || maParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue) &&
+ (maParam.GetEntry(0).eOp == SC_LESS_EQUAL || maParam.GetEntry(0).eOp == SC_GREATER_EQUAL);
+ bool bFound = false;
+ if (bBinary)
+ {
+ if (BinarySearch( maParam.nCol1 ))
+ {
+ // BinarySearch() already positions correctly and only needs real
+ // query comparisons afterwards, skip the verification check below.
+ maParam.mbRangeLookup = false;
+ bFound = GetThis();
+ }
+ else // Not sorted properly, or before the range (in which case GetFirst() will be simple).
+ bFound = GetFirst();
+ }
+ else
+ {
+ bFound = GetFirst();
+ }
+ if (bFound)
+ {
+ // First equal entry or last smaller than (greater than) entry.
+ PositionType aPosSave;
+ bool bNext = false;
+ SCSIZE nEntries = maParam.GetEntryCount();
+ std::vector<SCCOL> aFoundFieldPositions(nEntries);
+ do
+ {
+ nFoundCol = GetCol();
+ nFoundRow = GetRow();
+ aPosSave = maCurPos;
+ // If we might need to rewind below, save the position to rewind to
+ // rather than calculate it as a diff between nCol and nFoundCol as
+ // PerformQuery can return early if nCol is greater than
+ // maParam.nCol2 or AllocatedColumns
+ if (maParam.mbRangeLookup && bAdvanceQuery)
+ {
+ for (SCSIZE j=0; j < nEntries; ++j)
+ {
+ ScQueryEntry& rEntry = maParam.GetEntry( j );
+ if (rEntry.bDoQuery)
+ aFoundFieldPositions[j] = maParam.GetEntry(j).nField;
+ else
+ break; // for
+ }
+ }
+ if (IsEqualConditionFulfilled())
+ break;
+ bNext = GetNext();
+ }
+ while (bNext);
+
+ // There may be no pNext but equal condition fulfilled if regular
+ // expressions are involved. Keep the found entry and proceed.
+ if (!bNext && !IsEqualConditionFulfilled())
+ {
+ // Step back to last in range and adjust position markers for
+ // GetNumberFormat() or similar.
+ bool bColDiff = nCol != nFoundCol;
+ nCol = nFoundCol;
+ nRow = nFoundRow;
+ maCurPos = aPosSave;
+ if (maParam.mbRangeLookup)
+ {
+ // Verify that the found entry does not only fulfill the range
+ // lookup but also the real query, i.e. not numeric was found
+ // if query is ByString and vice versa.
+ maParam.mbRangeLookup = false;
+ // Step back the last field advance if GetNext() did one.
+ if (bAdvanceQuery && bColDiff)
+ {
+ for (SCSIZE j=0; j < nEntries; ++j)
+ {
+ ScQueryEntry& rEntry = maParam.GetEntry( j );
+ if (rEntry.bDoQuery)
+ {
+ rEntry.nField = aFoundFieldPositions[j];
+ assert(rEntry.nField >= 0);
+ }
+ else
+ break; // for
+ }
+ }
+ // Check it.
+ if (!GetThis())
+ {
+ nFoundCol = rDoc.MaxCol()+1;
+ nFoundRow = rDoc.MaxRow()+1;
+ }
+ }
+ }
+ }
+ if ( IsEqualConditionFulfilled() )
+ {
+ // Position on last equal entry.
+ SCSIZE nEntries = maParam.GetEntryCount();
+ for ( SCSIZE j = 0; j < nEntries; j++ )
+ {
+ ScQueryEntry& rEntry = maParam.GetEntry( j );
+ if ( rEntry.bDoQuery )
+ {
+ switch ( rEntry.eOp )
+ {
+ case SC_LESS_EQUAL :
+ case SC_GREATER_EQUAL :
+ rEntry.eOp = SC_EQUAL;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ else
+ break; // for
+ }
+ PositionType aPosSave;
+ bIgnoreMismatchOnLeadingStrings = false;
+ SetTestEqualCondition( false );
+ do
+ {
+ nFoundCol = GetCol();
+ nFoundRow = GetRow();
+ aPosSave = maCurPos;
+ } while (GetNext());
+
+ // Step back conditions are the same as above
+ nCol = nFoundCol;
+ nRow = nFoundRow;
+ maCurPos = aPosSave;
+ return true;
+ }
+ if ( (maParam.eSearchType != utl::SearchParam::SearchType::Normal) &&
+ StoppedOnMismatch() )
+ {
+ // Assume found entry to be the last value less than respectively
+ // greater than the query. But keep on searching for an equal match.
+ SCSIZE nEntries = maParam.GetEntryCount();
+ for ( SCSIZE j = 0; j < nEntries; j++ )
+ {
+ ScQueryEntry& rEntry = maParam.GetEntry( j );
+ if ( rEntry.bDoQuery )
+ {
+ switch ( rEntry.eOp )
+ {
+ case SC_LESS_EQUAL :
+ case SC_GREATER_EQUAL :
+ rEntry.eOp = SC_EQUAL;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ else
+ break; // for
+ }
+ SetStopOnMismatch( false );
+ SetTestEqualCondition( false );
+ if (GetNext())
+ {
+ // Last of a consecutive area, avoid searching the entire parameter
+ // range as it is a real performance bottleneck in case of regular
+ // expressions.
+ PositionType aPosSave;
+ do
+ {
+ nFoundCol = GetCol();
+ nFoundRow = GetRow();
+ aPosSave = maCurPos;
+ SetStopOnMismatch( true );
+ } while (GetNext());
+ nCol = nFoundCol;
+ nRow = nFoundRow;
+ maCurPos = aPosSave;
+ }
+ }
+ return (nFoundCol <= rDoc.MaxCol()) && (nFoundRow <= rDoc.MaxRow());
+}
+
+// Direct linear cell access using mdds.
+
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
+ ::ScQueryCellIteratorAccessSpecific( ScDocument& rDocument,
+ ScInterpreterContext& rContext, const ScQueryParam& rParam )
+ : maParam( rParam )
+ , rDoc( rDocument )
+ , mrContext( rContext )
+{
+ // coverity[uninit_member] - this just contains data, subclass will initialize some of it
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::InitPos()
+{
+ nRow = maParam.nRow1;
+ if (maParam.bHasHeader && maParam.bByRow)
+ ++nRow;
+ const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
+ maCurPos = rCol.maCells.position(nRow);
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::IncPos()
+{
+ if (maCurPos.second + 1 < maCurPos.first->size)
+ {
+ // Move within the same block.
+ ++maCurPos.second;
+ ++nRow;
+ }
+ else
+ // Move to the next block.
+ IncBlock();
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::IncBlock()
+{
+ ++maCurPos.first;
+ maCurPos.second = 0;
+
+ nRow = maCurPos.first->position;
+}
+
+/**
+ * This class sequentially indexes non-empty cells in order, from the top of
+ * the block where the start row position is, to the bottom of the block
+ * where the end row position is. It skips all empty blocks that may be
+ * present in between.
+ *
+ * The index value is an offset from the first element of the first block
+ * disregarding all empty cell blocks.
+ */
+class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::NonEmptyCellIndexer
+{
+ typedef std::map<size_t, sc::CellStoreType::const_iterator> BlockMapType;
+
+ BlockMapType maBlockMap;
+
+ const sc::CellStoreType& mrCells;
+
+ size_t mnLowIndex;
+ size_t mnHighIndex;
+
+ bool mbValid;
+
+public:
+ /**
+ * @param rCells cell storage container
+ * @param nStartRow logical start row position
+ * @param nEndRow logical end row position, inclusive.
+ */
+ NonEmptyCellIndexer(
+ const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow) :
+ mrCells(rCells), mnLowIndex(0), mnHighIndex(0), mbValid(true)
+ {
+ // Find the low position.
+
+ sc::CellStoreType::const_position_type aLoPos = mrCells.position(nStartRow);
+ assert(aLoPos.first->type != sc::element_type_empty);
+ assert(aLoPos.first != rCells.end());
+
+ SCROW nFirstRow = aLoPos.first->position;
+ SCROW nLastRow = aLoPos.first->position + aLoPos.first->size - 1;
+
+ if (nFirstRow > nEndRow)
+ {
+ // Both start and end row positions are within the leading skipped
+ // blocks.
+ mbValid = false;
+ return;
+ }
+
+ // Calculate the index of the low position.
+ if (nFirstRow < nStartRow)
+ mnLowIndex = nStartRow - nFirstRow;
+ else
+ {
+ // Start row is within the skipped block(s). Set it to the first
+ // element of the low block.
+ mnLowIndex = 0;
+ }
+
+ if (nEndRow < nLastRow)
+ {
+ assert(nEndRow >= nFirstRow);
+ mnHighIndex = nEndRow - nFirstRow;
+
+ maBlockMap.emplace(aLoPos.first->size, aLoPos.first);
+ return;
+ }
+
+ // Find the high position.
+
+ sc::CellStoreType::const_position_type aHiPos = mrCells.position(aLoPos.first, nEndRow);
+ if (aHiPos.first->type == sc::element_type_empty)
+ {
+ // Move to the last position of the previous block.
+ decBlock(aHiPos);
+
+ // Check the row position of the end of the previous block, and make sure it's valid.
+ SCROW nBlockEndRow = aHiPos.first->position + aHiPos.first->size - 1;
+ if (nBlockEndRow < nStartRow)
+ {
+ mbValid = false;
+ return;
+ }
+ }
+
+ // Tag the start and end blocks, and all blocks in between in order
+ // but skip all empty blocks.
+
+ size_t nPos = 0;
+ sc::CellStoreType::const_iterator itBlk = aLoPos.first;
+ while (itBlk != aHiPos.first)
+ {
+ if (itBlk->type == sc::element_type_empty)
+ {
+ ++itBlk;
+ continue;
+ }
+
+ nPos += itBlk->size;
+ maBlockMap.emplace(nPos, itBlk);
+ ++itBlk;
+
+ if (itBlk->type == sc::element_type_empty)
+ ++itBlk;
+
+ assert(itBlk != mrCells.end());
+ }
+
+ assert(itBlk == aHiPos.first);
+ nPos += itBlk->size;
+ maBlockMap.emplace(nPos, itBlk);
+
+ // Calculate the high index.
+ BlockMapType::const_reverse_iterator ri = maBlockMap.rbegin();
+ mnHighIndex = ri->first;
+ mnHighIndex -= ri->second->size;
+ mnHighIndex += aHiPos.second;
+ }
+
+ sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
+ {
+ assert(mbValid);
+ assert(mnLowIndex <= nIndex);
+ assert(nIndex <= mnHighIndex);
+
+ sc::CellStoreType::const_position_type aRet(mrCells.end(), 0);
+
+ BlockMapType::const_iterator it = maBlockMap.upper_bound(nIndex);
+ if (it == maBlockMap.end())
+ return aRet;
+
+ sc::CellStoreType::const_iterator itBlk = it->second;
+ size_t nBlkIndex = it->first - itBlk->size; // index of the first element of the block.
+ assert(nBlkIndex <= nIndex);
+ assert(nIndex < it->first);
+
+ size_t nOffset = nIndex - nBlkIndex;
+ aRet.first = itBlk;
+ aRet.second = nOffset;
+ return aRet;
+ }
+
+ BinarySearchCellType getCell( size_t nIndex ) const
+ {
+ BinarySearchCellType aRet;
+ aRet.second = -1;
+
+ sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
+ if (aPos.first == mrCells.end())
+ return aRet;
+
+ aRet.first = sc::toRefCell(aPos.first, aPos.second);
+ aRet.second = aPos.first->position + aPos.second;
+ return aRet;
+ }
+
+ size_t getLowIndex() const { return mnLowIndex; }
+
+ size_t getHighIndex() const { return mnHighIndex; }
+
+ bool isValid() const { return mbValid; }
+};
+
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::NonEmptyCellIndexer
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::MakeBinarySearchIndexer(
+ const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow )
+{
+ return NonEmptyCellIndexer(rCells, nStartRow, nEndRow);
+}
+
+// Sorted access using ScSortedRangeCache.
+
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >
+ ::ScQueryCellIteratorAccessSpecific( ScDocument& rDocument,
+ ScInterpreterContext& rContext, const ScQueryParam& rParam )
+ : maParam( rParam )
+ , rDoc( rDocument )
+ , mrContext( rContext )
+{
+ // coverity[uninit_member] - this just contains data, subclass will initialize some of it
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::SetSortedRangeCache(
+ const ScSortedRangeCache& cache)
+{
+ sortedCache = &cache;
+}
+
+// The idea in iterating using the sorted cache is that the iteration is instead done
+// over indexes of the sorted cache (which is a stable sort of the cell contents) in the range
+// that fits the query condition and then that is mapped to rows. This will result in iterating
+// over only matching rows in their sorted order (and for equal rows in their row order).
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::InitPosStart()
+{
+ ScRange aSortedRangeRange( nCol, maParam.nRow1, nTab, nCol, maParam.nRow2, nTab );
+ // We want all matching values first in the sort order,
+ SetSortedRangeCache( rDoc.GetSortedRangeCache( aSortedRangeRange, maParam, &mrContext ));
+ // InitPosFinish() needs to be called after this, ScQueryCellIteratorBase::InitPos()
+ // will handle that
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::InitPosFinish(
+ SCROW beforeRow, SCROW lastRow )
+{
+ pColumn = &rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
+ if(lastRow >= 0)
+ {
+ sortedCachePos = beforeRow >= 0 ? sortedCache->indexForRow(beforeRow) + 1 : 0;
+ sortedCachePosLast = sortedCache->indexForRow(lastRow);
+ if(sortedCachePos <= sortedCachePosLast)
+ {
+ nRow = sortedCache->rowForIndex(sortedCachePos);
+ maCurPos = pColumn->maCells.position(nRow);
+ return;
+ }
+ }
+ // No rows, set to end.
+ sortedCachePos = sortedCachePosLast = 0;
+ maCurPos.first = pColumn->maCells.end();
+ maCurPos.second = 0;
+}
+
+template<bool fast>
+bool ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::IncPosImpl()
+{
+ if(sortedCachePos < sortedCachePosLast)
+ {
+ ++sortedCachePos;
+ nRow = sortedCache->rowForIndex(sortedCachePos);
+#ifndef DBG_UTIL
+ if constexpr (!fast)
+#endif
+ {
+ // Avoid mdds position() call if row is in the same block.
+ if(maCurPos.first != pColumn->maCells.end() && o3tl::make_unsigned(nRow) >= maCurPos.first->position
+ && o3tl::make_unsigned(nRow) < maCurPos.first->position + maCurPos.first->size)
+ maCurPos.second = nRow - maCurPos.first->position;
+ else
+ maCurPos = pColumn->maCells.position(nRow);
+ }
+ return true;
+ }
+ else
+ {
+ // This will make PerformQuery() go to next column.
+ // Necessary even in fast mode, as GetNext() will call GetThis() in this case.
+ maCurPos.first = pColumn->maCells.end();
+ maCurPos.second = 0;
+ return false;
+ }
+}
+
+// Helper that allows binary search of unsorted cells using ScSortedRangeCache.
+// Rows in the given range are kept in a sorted vector and that vector is binary-searched.
+class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::SortedCacheIndexer
+{
+ std::vector<SCROW> mSortedRowsCopy;
+ const std::vector<SCROW>& mSortedRows;
+ const sc::CellStoreType& mCells;
+ size_t mLowIndex;
+ size_t mHighIndex;
+ bool mValid;
+
+ const std::vector<SCROW>& makeSortedRows( const ScSortedRangeCache* cache, SCROW startRow, SCROW endRow )
+ {
+ // Keep a reference to rows from the cache if equal, otherwise make a copy.
+ if(startRow == cache->getRange().aStart.Row() && endRow == cache->getRange().aEnd.Row())
+ return cache->sortedRows();
+ else
+ {
+ mSortedRowsCopy.reserve( cache->sortedRows().size());
+ for( SCROW row : cache->sortedRows())
+ if( row >= startRow && row <= endRow )
+ mSortedRowsCopy.emplace_back( row );
+ return mSortedRowsCopy;
+ }
+ }
+
+public:
+ SortedCacheIndexer( const sc::CellStoreType& cells, SCROW startRow, SCROW endRow,
+ const ScSortedRangeCache* cache )
+ : mSortedRows( makeSortedRows( cache, startRow, endRow ))
+ , mCells( cells )
+ , mValid( false )
+ {
+ if(mSortedRows.empty())
+ {
+ // coverity[uninit_member] - these are initialized only if valid
+ return;
+ }
+ mLowIndex = 0;
+ mHighIndex = mSortedRows.size() - 1;
+ mValid = true;
+ }
+
+ sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
+ {
+ // TODO optimize?
+ SCROW row = mSortedRows[ nIndex ];
+ return mCells.position(row);
+ }
+
+ BinarySearchCellType getCell( size_t nIndex ) const
+ {
+ BinarySearchCellType aRet;
+ aRet.second = -1;
+
+ sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
+ if (aPos.first == mCells.end())
+ return aRet;
+
+ aRet.first = sc::toRefCell(aPos.first, aPos.second);
+ aRet.second = aPos.first->position + aPos.second;
+ return aRet;
+ }
+
+ size_t getLowIndex() const { return mLowIndex; }
+
+ size_t getHighIndex() const { return mHighIndex; }
+
+ bool isValid() const { return mValid; }
+};
+
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::SortedCacheIndexer
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::MakeBinarySearchIndexer(
+ const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow)
+{
+ return SortedCacheIndexer(rCells, nStartRow, nEndRow, sortedCache);
+}
+
+static bool CanBeUsedForSorterCache(ScDocument& /*rDoc*/, const ScQueryParam& /*rParam*/,
+ SCTAB /*nTab*/, const ScFormulaCell* /*cell*/, const ScComplexRefData* /*refData*/,
+ ScInterpreterContext& /*context*/)
+{
+#if 1
+ /* TODO: tdf#151958 broken by string query of binary search on sorted
+ * cache, use the direct query instead for releases and fix SortedCache
+ * implementation after. Not only COUNTIF() is broken, but also COUNTIFS(),
+ * and maybe lcl_LookupQuery() for VLOOKUP() etc. as well. Just disable
+ * this for now.
+ * Can't just return false because below would be unreachable code. Can't
+ * just #if/#else/#endif either because parameters would be unused. Crap
+ * this and comment out parameter names. */
+ return false;
+#else
+ if(!rParam.GetEntry(0).bDoQuery || rParam.GetEntry(1).bDoQuery
+ || rParam.GetEntry(0).GetQueryItems().size() != 1 )
+ return false;
+ if(rParam.eSearchType != utl::SearchParam::SearchType::Normal)
+ return false;
+ if(rParam.GetEntry(0).GetQueryItem().meType != ScQueryEntry::ByValue
+ && rParam.GetEntry(0).GetQueryItem().meType != ScQueryEntry::ByString)
+ return false;
+ if(!rParam.bByRow)
+ return false;
+ if(rParam.bHasHeader)
+ return false;
+ if(rParam.mbRangeLookup)
+ return false;
+ if(rParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString
+ && !ScQueryEvaluator::isMatchWholeCell(rDoc, rParam.GetEntry(0).eOp))
+ return false; // substring matching cannot be sorted
+ if(rParam.GetEntry(0).eOp != SC_LESS && rParam.GetEntry(0).eOp != SC_LESS_EQUAL
+ && rParam.GetEntry(0).eOp != SC_GREATER && rParam.GetEntry(0).eOp != SC_GREATER_EQUAL
+ && rParam.GetEntry(0).eOp != SC_EQUAL)
+ return false;
+ // For unittests allow inefficient caching, in order for the code to be checked.
+ static bool inUnitTest = getenv("LO_TESTNAME") != nullptr;
+ if(refData == nullptr || refData->Ref1.IsRowRel() || refData->Ref2.IsRowRel())
+ {
+ // If this is not a range, then a cache is not worth it. If rows are relative, then each
+ // computation will use a different area, so the cache wouldn't be reused. Tab/cols are
+ // not a problem, because formula group computations are done for the same tab/col.
+ if(!inUnitTest)
+ return false;
+ }
+ if(rParam.nRow2 - rParam.nRow1 < 10)
+ {
+ if(!inUnitTest)
+ return false;
+ }
+ if( !cell )
+ return false;
+ if( !cell->GetCellGroup() || cell->GetCellGroup()->mnLength < 10 )
+ {
+ if(!inUnitTest)
+ return false;
+ }
+ // Check that all the relevant caches would be valid (may not be the case when mixing
+ // numeric and string cells for ByValue lookups).
+ for(SCCOL col : rDoc.GetAllocatedColumnsRange(nTab, rParam.nCol1, rParam.nCol2))
+ {
+ ScRange aSortedRangeRange( col, rParam.nRow1, nTab, col, rParam.nRow2, nTab);
+ if( aSortedRangeRange.Contains( cell->aPos ))
+ return false; // self-referencing, can't create cache
+ ScSortedRangeCache& cache = rDoc.GetSortedRangeCache( aSortedRangeRange, rParam, &context );
+ if(!cache.isValid())
+ return false;
+ }
+ return true;
+#endif
+}
+
+// Generic query implementation.
+
+bool ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::Generic >::HandleItemFound()
+{
+ getThisResult = true;
+ return true; // Return from PerformQuery().
+}
+
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::GetThis()
+{
+ getThisResult = false;
+ PerformQuery();
+ return getThisResult;
+}
+
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::GetFirst()
+{
+ assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
+ nCol = maParam.nCol1;
+ InitPos();
+ return GetThis();
+}
+
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::GetNext()
+{
+ IncPos();
+ if ( nStopOnMismatch )
+ nStopOnMismatch = nStopOnMismatchEnabled;
+ if ( nTestEqualCondition )
+ nTestEqualCondition = nTestEqualConditionEnabled;
+ return GetThis();
+}
+
+template<>
+bool ScQueryCellIterator< ScQueryCellIteratorAccess::SortedCache >::GetNext()
+{
+ assert( !nStopOnMismatch );
+ assert( !nTestEqualCondition );
+ // When searching using sorted cache, we should always find cells that match,
+ // because InitPos()/IncPos() select only such rows, so skip GetThis() (and thus
+ // the somewhat expensive PerformQuery) as long as we're not at the end
+ // of a column. As an optimization IncPosFast() returns true if not at the end,
+ // in which case in non-DBG_UTIL mode it doesn't even bother to set maCurPos.
+ if( IncPosFast())
+ {
+#ifdef DBG_UTIL
+ assert(GetThis());
+#endif
+ return true;
+ }
+ return GetThis();
+}
+
+bool ScQueryCellIteratorSortedCache::CanBeUsed(ScDocument& rDoc, const ScQueryParam& rParam,
+ SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
+ ScInterpreterContext& context)
+{
+ return CanBeUsedForSorterCache(rDoc, rParam, nTab, cell, refData, context);
+}
+
+// Countifs implementation.
+
+bool ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::CountIf >::HandleItemFound()
+{
+ ++countIfCount;
+ return false; // Continue searching.
+}
+
+template< ScQueryCellIteratorAccess accessType >
+sal_uInt64 ScCountIfCellIterator< accessType >::GetCount()
+{
+ // Keep Entry.nField in iterator on column change
+ SetAdvanceQueryParamEntryField( true );
+ assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, FIX IT");
+ maParam.nCol1 = rDoc.ClampToAllocatedColumns(nTab, maParam.nCol1);
+ maParam.nCol2 = rDoc.ClampToAllocatedColumns(nTab, maParam.nCol2);
+ nCol = maParam.nCol1;
+ InitPos();
+ countIfCount = 0;
+ PerformQuery();
+ return countIfCount;
+}
+
+
+bool ScCountIfCellIteratorSortedCache::CanBeUsed(ScDocument& rDoc, const ScQueryParam& rParam,
+ SCTAB nTab, const ScFormulaCell* cell, const ScComplexRefData* refData,
+ ScInterpreterContext& context)
+{
+ return CanBeUsedForSorterCache(rDoc, rParam, nTab, cell, refData, context);
+}
+
+template<>
+sal_uInt64 ScCountIfCellIterator< ScQueryCellIteratorAccess::SortedCache >::GetCount()
+{
+ // Keep Entry.nField in iterator on column change
+ SetAdvanceQueryParamEntryField( true );
+ assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, FIX IT");
+ sal_uInt64 count = 0;
+ // Each column must be sorted separately.
+ for(SCCOL col : rDoc.GetAllocatedColumnsRange(nTab, maParam.nCol1, maParam.nCol2))
+ {
+ nCol = col;
+ nRow = maParam.nRow1;
+ ScRange aSortedRangeRange( col, maParam.nRow1, nTab, col, maParam.nRow2, nTab);
+ ScQueryOp& op = maParam.GetEntry(0).eOp;
+ SetSortedRangeCache( rDoc.GetSortedRangeCache( aSortedRangeRange, maParam, &mrContext ));
+ if( op == SC_EQUAL )
+ {
+ // BinarySearch() searches for the last item that matches. Therefore first
+ // find the last non-matching position using SC_LESS and then find the last
+ // matching position using SC_EQUAL.
+ ScQueryOp saveOp = op;
+ op = SC_LESS;
+ if( BinarySearch( nCol, true ))
+ {
+ op = saveOp; // back to SC_EQUAL
+ size_t lastNonMatching = sortedCache->indexForRow(nRow);
+ if( BinarySearch( nCol ))
+ {
+ size_t lastMatching = sortedCache->indexForRow(nRow);
+ assert(lastMatching >= lastNonMatching);
+ count += lastMatching - lastNonMatching;
+ }
+ else
+ {
+ // BinarySearch() should at least find the same result as the SC_LESS
+ // call, so this should not happen.
+ assert(false);
+ }
+ }
+ else
+ {
+ // BinarySearch() returning false means that all values are larger,
+ // so try to find matching ones and count those up to and including
+ // the found one.
+ op = saveOp; // back to SC_EQUAL
+ if( BinarySearch( nCol ))
+ {
+ size_t lastMatching = sortedCache->indexForRow(nRow) + 1;
+ count += lastMatching;
+ }
+ else if( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
+ && rDoc.IsEmptyData(col, maParam.nRow1, col, maParam.nRow2, nTab))
+ {
+ // BinarySearch() returns false in case it's all empty data,
+ // handle that specially.
+ count += maParam.nRow2 - maParam.nRow1 + 1;
+ }
+ }
+ }
+ else
+ {
+ // BinarySearch() searches for the last item that matches. Therefore everything
+ // up to and including the found row matches the condition.
+ if( BinarySearch( nCol ))
+ count += sortedCache->indexForRow(nRow) + 1;
+ }
+ }
+ if( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
+ && maParam.nCol2 >= rDoc.GetAllocatedColumnsCount( nTab ))
+ {
+ const sal_uInt64 nRows = maParam.nRow2 - maParam.nRow1 + 1;
+ count += (maParam.nCol2 - rDoc.GetAllocatedColumnsCount(nTab)) * nRows;
+ }
+ return count;
+}
+
+template class ScQueryCellIterator< ScQueryCellIteratorAccess::Direct >;
+template class ScQueryCellIterator< ScQueryCellIteratorAccess::SortedCache >;
+template class ScCountIfCellIterator< ScQueryCellIteratorAccess::Direct >;
+template class ScCountIfCellIterator< ScQueryCellIteratorAccess::SortedCache >;
+
+// gcc for some reason needs these too
+template class ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::Generic >;
+template class ScQueryCellIteratorBase< ScQueryCellIteratorAccess::SortedCache, ScQueryCellIteratorType::Generic >;
+template class ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, ScQueryCellIteratorType::CountIf >;
+template class ScQueryCellIteratorBase< ScQueryCellIteratorAccess::SortedCache, ScQueryCellIteratorType::CountIf >;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/refupdatecontext.cxx b/sc/source/core/data/refupdatecontext.cxx
new file mode 100644
index 0000000000..0ce3f175e4
--- /dev/null
+++ b/sc/source/core/data/refupdatecontext.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/.
+ */
+
+#include <refupdatecontext.hxx>
+#include <algorithm>
+#include <clipparam.hxx>
+#include <mtvelements.hxx>
+
+namespace sc {
+
+void UpdatedRangeNames::setUpdatedName(SCTAB nTab, sal_uInt16 nIndex)
+{
+ // Map anything <-1 to global names. Unless we really want to come up with
+ // some classification there...
+ if (nTab < -1)
+ nTab = -1;
+
+ UpdatedNamesType::iterator it = maUpdatedNames.find(nTab);
+ if (it == maUpdatedNames.end())
+ {
+ // Insert a new container for this sheet index.
+ NameIndicesType aIndices;
+ std::pair<UpdatedNamesType::iterator,bool> r =
+ maUpdatedNames.emplace( nTab, aIndices);
+
+ if (!r.second)
+ // Insertion failed for whatever reason.
+ return;
+
+ it = r.first;
+ }
+
+ NameIndicesType& rIndices = it->second;
+ rIndices.insert(nIndex);
+}
+
+bool UpdatedRangeNames::isNameUpdated(SCTAB nTab, sal_uInt16 nIndex) const
+{
+ UpdatedNamesType::const_iterator it = maUpdatedNames.find(nTab);
+ if (it == maUpdatedNames.end())
+ return false;
+
+ const NameIndicesType& rIndices = it->second;
+ return rIndices.count(nIndex) > 0;
+}
+
+UpdatedRangeNames::NameIndicesType UpdatedRangeNames::getUpdatedNames(SCTAB nTab) const
+{
+ UpdatedNamesType::const_iterator it = maUpdatedNames.find(nTab);
+ if (it == maUpdatedNames.end())
+ return NameIndicesType();
+ return it->second;
+}
+
+bool UpdatedRangeNames::isEmpty(SCTAB nTab) const
+{
+ UpdatedNamesType::const_iterator it = maUpdatedNames.find(nTab);
+ return it == maUpdatedNames.end();
+}
+
+RefUpdateContext::RefUpdateContext(ScDocument& rDoc, ScDocument* pClipdoc)
+ : mrDoc(rDoc)
+ , meMode(URM_INSDEL)
+ , mbTransposed(pClipdoc != nullptr && pClipdoc->GetClipParam().isTransposed())
+ , mnColDelta(0)
+ , mnRowDelta(0)
+ , mnTabDelta(0)
+ , mpBlockPos(nullptr)
+{
+ assert((pClipdoc == nullptr || pClipdoc->IsClipboard()) && "only nullptr or clipdoc allowed");
+}
+
+bool RefUpdateContext::isInserted() const
+{
+ return (meMode == URM_INSDEL) && (mnColDelta > 0 || mnRowDelta > 0 || mnTabDelta > 0);
+}
+
+bool RefUpdateContext::isDeleted() const
+{
+ return (meMode == URM_INSDEL) && (mnColDelta < 0 || mnRowDelta < 0 || mnTabDelta < 0);
+}
+
+void RefUpdateContext::setBlockPositionReference( ColumnBlockPositionSet* blockPos )
+{
+ mpBlockPos = blockPos;
+}
+
+ColumnBlockPosition* RefUpdateContext::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+ return mpBlockPos ? mpBlockPos->getBlockPosition(nTab, nCol) : nullptr;
+}
+
+RefUpdateResult::RefUpdateResult()
+ : mbValueChanged(false), mbReferenceModified(false), mbNameModified(false), mnTab(-1) {}
+
+RefUpdateInsertTabContext::RefUpdateInsertTabContext(ScDocument& rDoc, SCTAB nInsertPos, SCTAB nSheets) :
+ mrDoc(rDoc), mnInsertPos(nInsertPos), mnSheets(nSheets) {}
+
+RefUpdateDeleteTabContext::RefUpdateDeleteTabContext(ScDocument& rDoc, SCTAB nDeletePos, SCTAB nSheets) :
+ mrDoc(rDoc), mnDeletePos(nDeletePos), mnSheets(nSheets) {}
+
+RefUpdateMoveTabContext::RefUpdateMoveTabContext(ScDocument& rDoc, SCTAB nOldPos, SCTAB nNewPos) :
+ mrDoc(rDoc), mnOldPos(nOldPos), mnNewPos(nNewPos) {}
+
+SCTAB RefUpdateMoveTabContext::getNewTab(SCTAB nOldTab) const
+{
+ // Sheets below the lower bound or above the upper bound will not change.
+ SCTAB nLowerBound = std::min(mnOldPos, mnNewPos);
+ SCTAB nUpperBound = std::max(mnOldPos, mnNewPos);
+
+ if (nOldTab < nLowerBound || nUpperBound < nOldTab)
+ // Outside the boundary. Nothing to adjust.
+ return nOldTab;
+
+ if (nOldTab == mnOldPos)
+ return mnNewPos;
+
+ // It's somewhere in between.
+ if (mnOldPos < mnNewPos)
+ {
+ // Moving a sheet to the right. The rest of the sheets shifts to the left.
+ return nOldTab - 1;
+ }
+
+ // Moving a sheet to the left. The rest of the sheets shifts to the right.
+ return nOldTab + 1;
+}
+
+SetFormulaDirtyContext::SetFormulaDirtyContext() :
+ mnTabDeletedStart(-1), mnTabDeletedEnd(-1), mbClearTabDeletedFlag(false) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/rowheightcontext.cxx b/sc/source/core/data/rowheightcontext.cxx
new file mode 100644
index 0000000000..c5f615c666
--- /dev/null
+++ b/sc/source/core/data/rowheightcontext.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/.
+ */
+
+#include <rowheightcontext.hxx>
+
+namespace sc {
+
+RowHeightContext::RowHeightContext(SCROW nMaxRow,
+ double fPPTX, double fPPTY, const Fraction& rZoomX, const Fraction& rZoomY,
+ OutputDevice* pOutDev ) :
+ maHeights(nMaxRow, 0),
+ mfPPTX(fPPTX), mfPPTY(fPPTY),
+ maZoomX(rZoomX), maZoomY(rZoomY),
+ mpOutDev(pOutDev),
+ mnExtraHeight(0),
+ mbForceAutoSize(false) {}
+
+RowHeightContext::~RowHeightContext() {}
+
+void RowHeightContext::setExtraHeight( sal_uInt16 nH )
+{
+ mnExtraHeight = nH;
+}
+
+void RowHeightContext::setForceAutoSize( bool b )
+{
+ mbForceAutoSize = b;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/segmenttree.cxx b/sc/source/core/data/segmenttree.cxx
new file mode 100644
index 0000000000..aa10d32544
--- /dev/null
+++ b/sc/source/core/data/segmenttree.cxx
@@ -0,0 +1,711 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <segmenttree.hxx>
+#include <o3tl/safeint.hxx>
+#include <mdds/flat_segment_tree.hpp>
+#include <sal/log.hxx>
+#include <algorithm>
+#include <limits>
+#include <string_view>
+#include <global.hxx>
+
+using ::std::numeric_limits;
+
+namespace {
+
+template<typename ValueType_, typename ExtValueType_ = ValueType_>
+class ScFlatSegmentsImpl
+{
+public:
+ typedef ValueType_ ValueType;
+ typedef ExtValueType_ ExtValueType;
+
+ struct RangeData
+ {
+ SCCOLROW mnPos1;
+ SCCOLROW mnPos2;
+ ValueType mnValue;
+ };
+
+ ScFlatSegmentsImpl(SCCOLROW nMax, ValueType nDefault);
+ ScFlatSegmentsImpl(const ScFlatSegmentsImpl& r);
+
+ bool setValue(SCCOLROW nPos1, SCCOLROW nPos2, ValueType nValue);
+ void setValueIf(SCCOLROW nPos1, SCCOLROW nPos2, ValueType nValue, const std::function<bool(ValueType)>& rPredicate);
+ ValueType getValue(SCCOLROW nPos);
+ sal_uInt64 getSumValue(SCCOLROW nPos1, SCCOLROW nPos2);
+ bool getRangeData(SCCOLROW nPos, RangeData& rData);
+ bool getRangeDataLeaf(SCCOLROW nPos, RangeData& rData);
+ void removeSegment(SCCOLROW nPos1, SCCOLROW nPos2);
+ void insertSegment(SCCOLROW nPos, SCCOLROW nSize, bool bSkipStartBoundary);
+
+ SCCOLROW findLastTrue(ValueType nValue) const;
+
+ // range iteration
+ bool getFirst(RangeData& rData);
+ bool getNext(RangeData& rData);
+
+ void enableTreeSearch(bool b)
+ {
+ mbTreeSearchEnabled = b;
+ }
+
+ void makeReady();
+
+private:
+ typedef ::mdds::flat_segment_tree<SCCOLROW, ValueType> fst_type;
+ fst_type maSegments;
+ typename fst_type::const_iterator maItr;
+
+ bool mbTreeSearchEnabled:1;
+};
+
+}
+
+template<typename ValueType_, typename ExtValueType_>
+ScFlatSegmentsImpl<ValueType_, ExtValueType_>::ScFlatSegmentsImpl(SCCOLROW nMax, ValueType nDefault) :
+ maSegments(0, nMax+1, nDefault),
+ mbTreeSearchEnabled(true)
+{
+}
+
+template<typename ValueType_, typename ExtValueType_>
+ScFlatSegmentsImpl<ValueType_, ExtValueType_>::ScFlatSegmentsImpl(const ScFlatSegmentsImpl<ValueType_, ExtValueType_>& r) :
+ maSegments(r.maSegments),
+ mbTreeSearchEnabled(r.mbTreeSearchEnabled)
+{
+}
+
+template<typename ValueType_, typename ExtValueType_>
+bool ScFlatSegmentsImpl<ValueType_, ExtValueType_>::setValue(SCCOLROW nPos1, SCCOLROW nPos2, ValueType nValue)
+{
+ ::std::pair<typename fst_type::const_iterator, bool> ret;
+ ret = maSegments.insert(maItr, nPos1, nPos2+1, nValue);
+ maItr = ret.first;
+ return ret.second;
+}
+
+template<typename ValueType_, typename ExtValueType_>
+void ScFlatSegmentsImpl<ValueType_, ExtValueType_>::setValueIf(SCCOLROW nPos1, SCCOLROW nPos2,
+ ValueType nValue, const std::function<bool(ValueType)>& rPredicate)
+{
+ SCCOLROW nCurrentStartRow = nPos1;
+ while (nCurrentStartRow <= nPos2)
+ {
+ RangeData aRangeData;
+ getRangeData(nCurrentStartRow, aRangeData);
+ if (rPredicate(aRangeData.mnValue))
+ {
+ // set value from current iteration point on, til end of range.
+ // Note that aRangeData may well contain much lower values for nPos1
+ setValue(nCurrentStartRow, std::min<SCCOLROW>(nPos2, aRangeData.mnPos2), nValue);
+ }
+
+ // even if nPos2 is bigger than nPos2 this should terminate the loop
+ nCurrentStartRow = aRangeData.mnPos2 + 1;
+ }
+}
+
+template<typename ValueType_, typename ExtValueType_>
+typename ScFlatSegmentsImpl<ValueType_, ExtValueType_>::ValueType ScFlatSegmentsImpl<ValueType_, ExtValueType_>::getValue(SCCOLROW nPos)
+{
+ ValueType nValue = 0;
+ if (!mbTreeSearchEnabled)
+ {
+ maSegments.search(nPos, nValue);
+ return nValue;
+ }
+
+ if (!maSegments.is_tree_valid())
+ {
+ assert(!ScGlobal::bThreadedGroupCalcInProgress);
+ maSegments.build_tree();
+ }
+
+ maSegments.search_tree(nPos, nValue);
+ return nValue;
+}
+
+template<typename ValueType_, typename ExtValueType_>
+sal_uInt64 ScFlatSegmentsImpl<ValueType_, ExtValueType_>::getSumValue(SCCOLROW nPos1, SCCOLROW nPos2)
+{
+ if (mbTreeSearchEnabled)
+ {
+
+ if (!maSegments.is_tree_valid())
+ {
+ assert(!ScGlobal::bThreadedGroupCalcInProgress);
+ maSegments.build_tree();
+ }
+
+ RangeData aData;
+ auto [it, found] = maSegments.search_tree(nPos1, aData.mnValue, &aData.mnPos1, &aData.mnPos2);
+ if (!found)
+ return 0;
+ aData.mnPos2 = aData.mnPos2-1; // end point is not inclusive.
+
+ sal_uInt64 nValue = 0;
+
+ SCROW nCurPos = nPos1;
+ SCROW nEndPos = aData.mnPos2;
+ while (nEndPos <= nPos2)
+ {
+ sal_uInt64 nRes;
+ if (o3tl::checked_multiply<sal_uInt64>(aData.mnValue, nEndPos - nCurPos + 1, nRes))
+ {
+ SAL_WARN("sc.core", "row height overflow");
+ nRes = SAL_MAX_INT64;
+ }
+ nValue = o3tl::saturating_add(nValue, nRes);
+ nCurPos = nEndPos + 1;
+ auto itPair = maSegments.search(it, nCurPos, aData.mnValue, &aData.mnPos1, &aData.mnPos2);
+ if (!itPair.second)
+ break;
+ it = itPair.first;
+ aData.mnPos2 = aData.mnPos2-1; // end point is not inclusive.
+ nEndPos = aData.mnPos2;
+ }
+ if (nCurPos <= nPos2)
+ {
+ nEndPos = ::std::min(nEndPos, nPos2);
+ sal_uInt64 nRes;
+ if (o3tl::checked_multiply<sal_uInt64>(aData.mnValue, nEndPos - nCurPos + 1, nRes))
+ {
+ SAL_WARN("sc.core", "row height overflow");
+ nRes = SAL_MAX_INT64;
+ }
+ nValue = o3tl::saturating_add(nValue, nRes);
+ }
+ return nValue;
+ }
+ else
+ {
+ RangeData aData;
+ if (!getRangeDataLeaf(nPos1, aData))
+ return 0;
+
+ sal_uInt64 nValue = 0;
+
+ SCROW nCurPos = nPos1;
+ SCROW nEndPos = aData.mnPos2;
+ while (nEndPos <= nPos2)
+ {
+ sal_uInt64 nRes;
+ if (o3tl::checked_multiply<sal_uInt64>(aData.mnValue, nEndPos - nCurPos + 1, nRes))
+ {
+ SAL_WARN("sc.core", "row height overflow");
+ nRes = SAL_MAX_INT64;
+ }
+ nValue = o3tl::saturating_add(nValue, nRes);
+ nCurPos = nEndPos + 1;
+ if (!getRangeDataLeaf(nCurPos, aData))
+ break;
+
+ nEndPos = aData.mnPos2;
+ }
+ if (nCurPos <= nPos2)
+ {
+ nEndPos = ::std::min(nEndPos, nPos2);
+ sal_uInt64 nRes;
+ if (o3tl::checked_multiply<sal_uInt64>(aData.mnValue, nEndPos - nCurPos + 1, nRes))
+ {
+ SAL_WARN("sc.core", "row height overflow");
+ nRes = SAL_MAX_INT64;
+ }
+ nValue = o3tl::saturating_add(nValue, nRes);
+ }
+ return nValue;
+ }
+}
+
+template<typename ValueType_, typename ExtValueType_>
+bool ScFlatSegmentsImpl<ValueType_, ExtValueType_>::getRangeData(SCCOLROW nPos, RangeData& rData)
+{
+ if (!mbTreeSearchEnabled)
+ return getRangeDataLeaf(nPos, rData);
+
+ if (!maSegments.is_tree_valid())
+ {
+ assert(!ScGlobal::bThreadedGroupCalcInProgress);
+ maSegments.build_tree();
+ }
+
+ auto [it,found] = maSegments.search_tree(nPos, rData.mnValue, &rData.mnPos1, &rData.mnPos2);
+ if (!found)
+ return false;
+ maItr = it; // cache the iterator to speed up ForwardIterator.
+ rData.mnPos2 = rData.mnPos2-1; // end point is not inclusive.
+ return true;
+}
+
+template<typename ValueType_, typename ExtValueType_>
+bool ScFlatSegmentsImpl<ValueType_, ExtValueType_>::getRangeDataLeaf(SCCOLROW nPos, RangeData& rData)
+{
+ // Conduct leaf-node only search. Faster when searching between range insertion.
+ const ::std::pair<typename fst_type::const_iterator, bool> &ret =
+ maSegments.search(maItr, nPos, rData.mnValue, &rData.mnPos1, &rData.mnPos2);
+
+ if (!ret.second)
+ return false;
+
+ maItr = ret.first;
+
+ rData.mnPos2 = rData.mnPos2-1; // end point is not inclusive.
+ return true;
+}
+
+template<typename ValueType_, typename ExtValueType_>
+void ScFlatSegmentsImpl<ValueType_, ExtValueType_>::removeSegment(SCCOLROW nPos1, SCCOLROW nPos2)
+{
+ maSegments.shift_left(nPos1, nPos2);
+ maItr = maSegments.begin();
+}
+
+template<typename ValueType_, typename ExtValueType_>
+void ScFlatSegmentsImpl<ValueType_, ExtValueType_>::insertSegment(SCCOLROW nPos, SCCOLROW nSize, bool bSkipStartBoundary)
+{
+ maSegments.shift_right(nPos, nSize, bSkipStartBoundary);
+ maItr = maSegments.begin();
+}
+
+template<typename ValueType_, typename ExtValueType_>
+SCCOLROW ScFlatSegmentsImpl<ValueType_, ExtValueType_>::findLastTrue(ValueType nValue) const
+{
+ SCCOLROW nPos = numeric_limits<SCCOLROW>::max(); // position not found.
+ typename fst_type::const_reverse_iterator itr = maSegments.rbegin(), itrEnd = maSegments.rend();
+ // Note that when searching in reverse direction, we need to skip the first
+ // node, since the right-most leaf node does not store a valid value.
+ for (++itr; itr != itrEnd; ++itr)
+ {
+ if (itr->second != nValue)
+ {
+ nPos = (--itr)->first - 1;
+ break;
+ }
+ }
+ return nPos;
+}
+
+template<typename ValueType_, typename ExtValueType_>
+bool ScFlatSegmentsImpl<ValueType_, ExtValueType_>::getFirst(RangeData& rData)
+{
+ maItr = maSegments.begin();
+ return getNext(rData);
+}
+
+template<typename ValueType_, typename ExtValueType_>
+bool ScFlatSegmentsImpl<ValueType_, ExtValueType_>::getNext(RangeData& rData)
+{
+ typename fst_type::const_iterator itrEnd = maSegments.end();
+ if (maItr == itrEnd)
+ return false;
+
+ rData.mnPos1 = maItr->first;
+ rData.mnValue = maItr->second;
+
+ ++maItr;
+ if (maItr == itrEnd)
+ return false;
+
+ rData.mnPos2 = maItr->first - 1;
+ return true;
+}
+
+template<typename ValueType_, typename ExtValueType_>
+void ScFlatSegmentsImpl<ValueType_, ExtValueType_>::makeReady()
+{
+ assert(!ScGlobal::bThreadedGroupCalcInProgress);
+ if (!maSegments.is_tree_valid())
+ maSegments.build_tree();
+}
+
+class ScFlatUInt16SegmentsImpl : public ScFlatSegmentsImpl<sal_uInt16, sal_uInt32>
+{
+public:
+ explicit ScFlatUInt16SegmentsImpl(SCCOLROW nMax, sal_uInt16 nDefault) :
+ ScFlatSegmentsImpl<sal_uInt16, sal_uInt32>(nMax, nDefault)
+ {
+ }
+};
+
+class ScFlatBoolSegmentsImpl : public ScFlatSegmentsImpl<bool>
+{
+public:
+ explicit ScFlatBoolSegmentsImpl(SCCOLROW nMax) :
+ ScFlatSegmentsImpl<bool>(nMax, false)
+ {
+ }
+
+ bool setTrue(SCCOLROW nPos1, SCCOLROW nPos2);
+ bool setFalse(SCCOLROW nPos1, SCCOLROW nPos2);
+};
+
+bool ScFlatBoolSegmentsImpl::setTrue(SCCOLROW nPos1, SCCOLROW nPos2)
+{
+ return setValue(nPos1, nPos2, true);
+}
+
+bool ScFlatBoolSegmentsImpl::setFalse(SCCOLROW nPos1, SCCOLROW nPos2)
+{
+ return setValue(nPos1, nPos2, false);
+}
+
+ScFlatBoolRowSegments::ForwardIterator::ForwardIterator(ScFlatBoolRowSegments& rSegs) :
+ mrSegs(rSegs), mnCurPos(0), mnLastPos(-1), mbCurValue(false)
+{
+}
+
+bool ScFlatBoolRowSegments::ForwardIterator::getValue(SCROW nPos, bool& rVal)
+{
+ if (nPos >= mnCurPos)
+ // It can only go in a forward direction.
+ mnCurPos = nPos;
+
+ if (mnCurPos > mnLastPos)
+ {
+ // position not in the current segment. Update the current value.
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!mrSegs.getRangeData(mnCurPos, aData))
+ return false;
+
+ mbCurValue = aData.mbValue;
+ mnLastPos = aData.mnRow2;
+ }
+
+ rVal = mbCurValue;
+ return true;
+}
+
+ScFlatBoolRowSegments::RangeIterator::RangeIterator(ScFlatBoolRowSegments const & rSegs) :
+ mrSegs(rSegs)
+{
+}
+
+bool ScFlatBoolRowSegments::RangeIterator::getFirst(RangeData& rRange)
+{
+ ScFlatBoolSegmentsImpl::RangeData aData;
+ if (!mrSegs.mpImpl->getFirst(aData))
+ return false;
+
+ rRange.mnRow1 = static_cast<SCROW>(aData.mnPos1);
+ rRange.mnRow2 = static_cast<SCROW>(aData.mnPos2);
+ rRange.mbValue = static_cast<bool>(aData.mnValue);
+ return true;
+}
+
+bool ScFlatBoolRowSegments::RangeIterator::getNext(RangeData& rRange)
+{
+ ScFlatBoolSegmentsImpl::RangeData aData;
+ if (!mrSegs.mpImpl->getNext(aData))
+ return false;
+
+ rRange.mnRow1 = static_cast<SCROW>(aData.mnPos1);
+ rRange.mnRow2 = static_cast<SCROW>(aData.mnPos2);
+ rRange.mbValue = static_cast<bool>(aData.mnValue);
+ return true;
+}
+
+ScFlatBoolRowSegments::ScFlatBoolRowSegments(SCROW nMaxRow) :
+ mpImpl(new ScFlatBoolSegmentsImpl(nMaxRow))
+{
+}
+
+ScFlatBoolRowSegments::ScFlatBoolRowSegments(const ScFlatBoolRowSegments& r) :
+ mpImpl(new ScFlatBoolSegmentsImpl(*r.mpImpl))
+{
+}
+
+ScFlatBoolRowSegments::~ScFlatBoolRowSegments()
+{
+}
+
+bool ScFlatBoolRowSegments::setTrue(SCROW nRow1, SCROW nRow2)
+{
+ return mpImpl->setTrue(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2));
+}
+
+bool ScFlatBoolRowSegments::setFalse(SCROW nRow1, SCROW nRow2)
+{
+ return mpImpl->setFalse(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2));
+}
+
+bool ScFlatBoolRowSegments::getRangeData(SCROW nRow, RangeData& rData) const
+{
+ ScFlatBoolSegmentsImpl::RangeData aData;
+ if (!mpImpl->getRangeData(static_cast<SCCOLROW>(nRow), aData))
+ return false;
+
+ rData.mbValue = aData.mnValue;
+ rData.mnRow1 = static_cast<SCROW>(aData.mnPos1);
+ rData.mnRow2 = static_cast<SCROW>(aData.mnPos2);
+ return true;
+}
+
+bool ScFlatBoolRowSegments::getRangeDataLeaf(SCROW nRow, RangeData& rData)
+{
+ ScFlatBoolSegmentsImpl::RangeData aData;
+ if (!mpImpl->getRangeDataLeaf(static_cast<SCCOLROW>(nRow), aData))
+ return false;
+
+ rData.mbValue = aData.mnValue;
+ rData.mnRow1 = static_cast<SCROW>(aData.mnPos1);
+ rData.mnRow2 = static_cast<SCROW>(aData.mnPos2);
+ return true;
+}
+
+void ScFlatBoolRowSegments::removeSegment(SCROW nRow1, SCROW nRow2)
+{
+ mpImpl->removeSegment(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2));
+}
+
+void ScFlatBoolRowSegments::insertSegment(SCROW nRow, SCROW nSize)
+{
+ mpImpl->insertSegment(static_cast<SCCOLROW>(nRow), static_cast<SCCOLROW>(nSize), true/*bSkipStartBoundary*/);
+}
+
+SCROW ScFlatBoolRowSegments::findLastTrue() const
+{
+ return mpImpl->findLastTrue(false);
+}
+
+void ScFlatBoolRowSegments::makeReady()
+{
+ mpImpl->makeReady();
+}
+
+OString ScFlatBoolRowSegments::dumpAsString()
+{
+ OString aOutput;
+ OString aSegment;
+ RangeData aRange;
+ SCROW nRow = 0;
+ while (getRangeData(nRow, aRange))
+ {
+ if (!nRow)
+ aSegment = (aRange.mbValue ? std::string_view("1") : std::string_view("0")) + OString::Concat(":");
+ else
+ aSegment.clear();
+
+ aSegment += OString::number(aRange.mnRow2) + " ";
+ aOutput += aSegment;
+ nRow = aRange.mnRow2 + 1;
+ }
+
+ return aOutput;
+}
+
+ScFlatBoolColSegments::ScFlatBoolColSegments(SCCOL nMaxCol) :
+ mpImpl(new ScFlatBoolSegmentsImpl(nMaxCol))
+{
+}
+
+ScFlatBoolColSegments::ScFlatBoolColSegments(const ScFlatBoolColSegments& r) :
+ mpImpl(new ScFlatBoolSegmentsImpl(*r.mpImpl))
+{
+}
+
+ScFlatBoolColSegments::~ScFlatBoolColSegments()
+{
+}
+
+bool ScFlatBoolColSegments::setTrue(SCCOL nCol1, SCCOL nCol2)
+{
+ return mpImpl->setTrue(static_cast<SCCOLROW>(nCol1), static_cast<SCCOLROW>(nCol2));
+}
+
+bool ScFlatBoolColSegments::setFalse(SCCOL nCol1, SCCOL nCol2)
+{
+ return mpImpl->setFalse(static_cast<SCCOLROW>(nCol1), static_cast<SCCOLROW>(nCol2));
+}
+
+bool ScFlatBoolColSegments::getRangeData(SCCOL nCol, RangeData& rData)
+{
+ ScFlatBoolSegmentsImpl::RangeData aData;
+ if (!mpImpl->getRangeData(static_cast<SCCOLROW>(nCol), aData))
+ return false;
+
+ rData.mbValue = aData.mnValue;
+ rData.mnCol1 = static_cast<SCCOL>(aData.mnPos1);
+ rData.mnCol2 = static_cast<SCCOL>(aData.mnPos2);
+ return true;
+}
+
+void ScFlatBoolColSegments::removeSegment(SCCOL nCol1, SCCOL nCol2)
+{
+ mpImpl->removeSegment(static_cast<SCCOLROW>(nCol1), static_cast<SCCOLROW>(nCol2));
+}
+
+void ScFlatBoolColSegments::insertSegment(SCCOL nCol, SCCOL nSize)
+{
+ mpImpl->insertSegment(static_cast<SCCOLROW>(nCol), static_cast<SCCOLROW>(nSize), true/*bSkipStartBoundary*/);
+}
+
+void ScFlatBoolColSegments::makeReady()
+{
+ mpImpl->makeReady();
+}
+
+OString ScFlatBoolColSegments::dumpAsString()
+{
+ OString aOutput;
+ OString aSegment;
+ RangeData aRange;
+ SCCOL nCol = 0;
+ while (getRangeData(nCol, aRange))
+ {
+ if (!nCol)
+ aSegment = (aRange.mbValue ? OString::Concat("1") : OString::Concat("0")) + OString::Concat(":");
+ else
+ aSegment.clear();
+
+ aSegment += OString::number(aRange.mnCol2) + " ";
+ aOutput += aSegment;
+ nCol = aRange.mnCol2 + 1;
+ }
+
+ return aOutput;
+}
+
+ScFlatUInt16RowSegments::ForwardIterator::ForwardIterator(ScFlatUInt16RowSegments& rSegs) :
+ mrSegs(rSegs), mnCurPos(0), mnLastPos(-1), mnCurValue(0)
+{
+}
+
+bool ScFlatUInt16RowSegments::ForwardIterator::getValue(SCROW nPos, sal_uInt16& rVal)
+{
+ if (nPos >= mnCurPos)
+ // It can only go in a forward direction.
+ mnCurPos = nPos;
+
+ if (mnCurPos > mnLastPos)
+ {
+ // position not in the current segment. Update the current value.
+ ScFlatUInt16SegmentsImpl::RangeData aData;
+ if (mnLastPos == -1)
+ {
+ // first time in this method, use the tree search based method
+ if (!mrSegs.mpImpl->getRangeData(mnCurPos, aData))
+ return false;
+ }
+ else
+ {
+ // but on subsequent calls, use the leaf method, which is faster
+ // because we have a cached iterator.
+ if (!mrSegs.mpImpl->getRangeDataLeaf(mnCurPos, aData))
+ return false;
+ }
+
+ mnCurValue = aData.mnValue;
+ mnLastPos = aData.mnPos2;
+ }
+
+ rVal = mnCurValue;
+ return true;
+}
+
+ScFlatUInt16RowSegments::ScFlatUInt16RowSegments(SCROW nMaxRow, sal_uInt16 nDefault) :
+ mpImpl(new ScFlatUInt16SegmentsImpl(nMaxRow, nDefault))
+{
+}
+
+ScFlatUInt16RowSegments::ScFlatUInt16RowSegments(const ScFlatUInt16RowSegments& r) :
+ mpImpl(new ScFlatUInt16SegmentsImpl(*r.mpImpl))
+{
+}
+
+ScFlatUInt16RowSegments::~ScFlatUInt16RowSegments()
+{
+}
+
+void ScFlatUInt16RowSegments::setValue(SCROW nRow1, SCROW nRow2, sal_uInt16 nValue)
+{
+ mpImpl->setValue(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2), nValue);
+}
+
+sal_uInt16 ScFlatUInt16RowSegments::getValue(SCROW nRow)
+{
+ return mpImpl->getValue(static_cast<SCCOLROW>(nRow));
+}
+
+sal_uInt64 ScFlatUInt16RowSegments::getSumValue(SCROW nRow1, SCROW nRow2)
+{
+ return mpImpl->getSumValue(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2));
+}
+
+bool ScFlatUInt16RowSegments::getRangeData(SCROW nRow, RangeData& rData)
+{
+ ScFlatUInt16SegmentsImpl::RangeData aData;
+ if (!mpImpl->getRangeData(static_cast<SCCOLROW>(nRow), aData))
+ return false;
+
+ rData.mnRow1 = aData.mnPos1;
+ rData.mnRow2 = aData.mnPos2;
+ rData.mnValue = aData.mnValue;
+ return true;
+}
+
+void ScFlatUInt16RowSegments::removeSegment(SCROW nRow1, SCROW nRow2)
+{
+ mpImpl->removeSegment(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2));
+}
+
+void ScFlatUInt16RowSegments::insertSegment(SCROW nRow, SCROW nSize)
+{
+ mpImpl->insertSegment(static_cast<SCCOLROW>(nRow), static_cast<SCCOLROW>(nSize), false/*bSkipStartBoundary*/);
+}
+
+SCROW ScFlatUInt16RowSegments::findLastTrue(sal_uInt16 nValue) const
+{
+ return mpImpl->findLastTrue(nValue);
+}
+
+void ScFlatUInt16RowSegments::enableTreeSearch(bool bEnable)
+{
+ mpImpl->enableTreeSearch(bEnable);
+}
+
+void ScFlatUInt16RowSegments::setValueIf(SCROW nRow1, SCROW nRow2, sal_uInt16 nValue, const std::function<bool(sal_uInt16)>& rPredicate)
+{
+ mpImpl->setValueIf(static_cast<SCCOLROW>(nRow1), static_cast<SCCOLROW>(nRow2), nValue, rPredicate);
+}
+
+void ScFlatUInt16RowSegments::makeReady()
+{
+ mpImpl->makeReady();
+}
+
+OString ScFlatUInt16RowSegments::dumpAsString()
+{
+ OString aOutput;
+ OString aSegment;
+ RangeData aRange;
+ SCROW nRow = 0;
+ while (getRangeData(nRow, aRange))
+ {
+ aSegment = OString::number(aRange.mnValue) + ":" +
+ OString::number(aRange.mnRow2) + " ";
+ aOutput += aSegment;
+ nRow = aRange.mnRow2 + 1;
+ }
+
+ return aOutput;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/sheetevents.cxx b/sc/source/core/data/sheetevents.cxx
new file mode 100644
index 0000000000..7d2152226b
--- /dev/null
+++ b/sc/source/core/data/sheetevents.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 <sheetevents.hxx>
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <optional>
+
+OUString ScSheetEvents::GetEventName(ScSheetEventId nEvent)
+{
+ static const char* aEventNames[] =
+ {
+ "OnFocus", // SC_SHEETEVENT_FOCUS
+ "OnUnfocus", // SC_SHEETEVENT_UNFOCUS
+ "OnSelect", // SC_SHEETEVENT_SELECT
+ "OnDoubleClick", // SC_SHEETEVENT_DOUBLECLICK
+ "OnRightClick", // SC_SHEETEVENT_RIGHTCLICK
+ "OnChange", // SC_SHEETEVENT_CHANGE
+ "OnCalculate" // SC_SHEETEVENT_CALCULATE
+ };
+ return OUString::createFromAscii(aEventNames[static_cast<int>(nEvent)]);
+}
+
+sal_Int32 ScSheetEvents::GetVbaSheetEventId(ScSheetEventId nEvent)
+{
+ using namespace ::com::sun::star::script::vba::VBAEventId;
+
+ static const sal_Int32 nVbaEventIds[] =
+ {
+ WORKSHEET_ACTIVATE, // SC_SHEETEVENT_FOCUS
+ WORKSHEET_DEACTIVATE, // SC_SHEETEVENT_UNFOCUS
+ WORKSHEET_SELECTIONCHANGE, // SC_SHEETEVENT_SELECT
+ WORKSHEET_BEFOREDOUBLECLICK, // SC_SHEETEVENT_DOUBLECLICK
+ WORKSHEET_BEFORERIGHTCLICK, // SC_SHEETEVENT_RIGHTCLICK
+ WORKSHEET_CHANGE, // SC_SHEETEVENT_CHANGE
+ WORKSHEET_CALCULATE // SC_SHEETEVENT_CALCULATE
+ };
+ return nVbaEventIds[static_cast<int>(nEvent)];
+}
+
+const int COUNT = static_cast<int>(ScSheetEventId::COUNT);
+
+sal_Int32 ScSheetEvents::GetVbaDocumentEventId(ScSheetEventId nEvent)
+{
+ using namespace ::com::sun::star::script::vba::VBAEventId;
+ sal_Int32 nSheetEventId = GetVbaSheetEventId(nEvent);
+ return (nSheetEventId != NO_EVENT) ? (nSheetEventId + USERDEFINED_START) : NO_EVENT;
+}
+
+ScSheetEvents::ScSheetEvents()
+{
+}
+
+ScSheetEvents::~ScSheetEvents()
+{
+ Clear();
+}
+
+void ScSheetEvents::Clear()
+{
+ mpScriptNames.reset();
+}
+
+ScSheetEvents::ScSheetEvents(const ScSheetEvents& rOther)
+{
+ *this = rOther;
+}
+
+ScSheetEvents& ScSheetEvents::operator=(const ScSheetEvents& rOther)
+{
+ if (this != &rOther)
+ {
+ Clear();
+ if (rOther.mpScriptNames)
+ {
+ mpScriptNames.reset( new std::optional<OUString>[COUNT] );
+ for (sal_Int32 nEvent=0; nEvent<COUNT; ++nEvent)
+ mpScriptNames[nEvent] = rOther.mpScriptNames[nEvent];
+ }
+ }
+ return *this;
+}
+
+const OUString* ScSheetEvents::GetScript(ScSheetEventId nEvent) const
+{
+ if (mpScriptNames)
+ {
+ std::optional<OUString> const & r = mpScriptNames[static_cast<int>(nEvent)];
+ if (r)
+ return &*r;
+ }
+ return nullptr;
+}
+
+void ScSheetEvents::SetScript(ScSheetEventId eEvent, const OUString* pNew)
+{
+ int nEvent = static_cast<int>(eEvent);
+ if (!mpScriptNames)
+ {
+ mpScriptNames.reset( new std::optional<OUString>[COUNT] );
+ }
+ if (pNew)
+ mpScriptNames[nEvent] = *pNew;
+ else
+ mpScriptNames[nEvent].reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/simpleformulacalc.cxx b/sc/source/core/data/simpleformulacalc.cxx
new file mode 100644
index 0000000000..cd2559e9f2
--- /dev/null
+++ b/sc/source/core/data/simpleformulacalc.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/.
+ */
+
+#include <memory>
+#include <simpleformulacalc.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <tokenarray.hxx>
+#include <interpre.hxx>
+#include <compiler.hxx>
+#include <sfx2/linkmgr.hxx>
+
+#define DISPLAY_LEN 66
+
+ScSimpleFormulaCalculator::ScSimpleFormulaCalculator( ScDocument& rDoc, const ScAddress& rAddr,
+ const OUString& rFormula, bool bMatrixFormula, formula::FormulaGrammar::Grammar eGram )
+ : mnFormatType(SvNumFormatType::ALL)
+ , mbCalculated(false)
+ , maAddr(rAddr)
+ , mrDoc(rDoc)
+ , maGram(eGram)
+ , mbMatrixResult(false)
+ , mbLimitString(false)
+ , mbMatrixFormula(bMatrixFormula)
+{
+ // compile already here
+ ScCompiler aComp(mrDoc, maAddr, eGram, true, bMatrixFormula);
+ mpCode = aComp.CompileString(rFormula);
+ if(mpCode->GetCodeError() == FormulaError::NONE && mpCode->GetLen())
+ aComp.CompileTokenArray();
+}
+
+ScSimpleFormulaCalculator::~ScSimpleFormulaCalculator()
+{
+}
+
+void ScSimpleFormulaCalculator::Calculate()
+{
+ if(mbCalculated)
+ return;
+
+ mbCalculated = true;
+
+ ScInterpreter aInt(mrDoc.GetFormulaCell( maAddr ), mrDoc, mrDoc.GetNonThreadedContext(), maAddr, *mpCode);
+ if (mbMatrixFormula)
+ aInt.AssertFormulaMatrix();
+
+ sfx2::LinkManager aNewLinkMgr( mrDoc.GetDocumentShell() );
+ aInt.SetLinkManager( &aNewLinkMgr );
+
+ formula::StackVar aIntType = aInt.Interpret();
+ if ( aIntType == formula::svMatrixCell )
+ {
+ ScCompiler aComp(mrDoc, maAddr, maGram);
+ OUStringBuffer aStr;
+ aComp.CreateStringFromToken(aStr, aInt.GetResultToken().get());
+
+ mbMatrixResult = true;
+
+ if (mbLimitString)
+ {
+ const sal_Unicode cCol = ScCompiler::GetNativeSymbol(ocArrayColSep)[0];
+ const sal_Unicode cRow = ScCompiler::GetNativeSymbol(ocArrayRowSep)[0];
+ const sal_Int32 n = aStr.getLength();
+ for (sal_Int32 i = DISPLAY_LEN; i < n; ++i)
+ {
+ const sal_Unicode c = aStr[i];
+ if (c == cCol || c == cRow)
+ {
+ aStr.truncate(i+1);
+ aStr.append("...");
+ break;
+ }
+ }
+ }
+
+ maMatrixFormulaResult = aStr.makeStringAndClear();
+ }
+ mnFormatType = aInt.GetRetFormatType();
+ maResult.SetToken(aInt.GetResultToken().get());
+}
+
+bool ScSimpleFormulaCalculator::IsValue()
+{
+ Calculate();
+
+ if (mbMatrixResult)
+ return false;
+
+ return maResult.IsValue();
+}
+
+bool ScSimpleFormulaCalculator::IsMatrix()
+{
+ Calculate();
+
+ return mbMatrixResult;
+}
+
+FormulaError ScSimpleFormulaCalculator::GetErrCode()
+{
+ Calculate();
+
+ FormulaError nErr = mpCode->GetCodeError();
+ if (nErr != FormulaError::NONE)
+ return nErr;
+ return maResult.GetResultError();
+}
+
+double ScSimpleFormulaCalculator::GetValue()
+{
+ Calculate();
+
+ if ((mpCode->GetCodeError() == FormulaError::NONE) &&
+ maResult.GetResultError() == FormulaError::NONE)
+ return maResult.GetDouble();
+
+ return 0.0;
+}
+
+svl::SharedString ScSimpleFormulaCalculator::GetString()
+{
+ Calculate();
+
+ if (mbMatrixResult)
+ return svl::SharedString( maMatrixFormulaResult); // string not interned
+
+ if ((mpCode->GetCodeError() == FormulaError::NONE) &&
+ maResult.GetResultError() == FormulaError::NONE)
+ return maResult.GetString();
+
+ return svl::SharedString::getEmptyString();
+}
+
+bool ScSimpleFormulaCalculator::HasColRowName() const
+{
+ return formula::FormulaTokenArrayPlainIterator(*mpCode).GetNextColRowName() != nullptr;
+}
+
+ScTokenArray* ScSimpleFormulaCalculator::GetCode()
+{
+ return mpCode.get();
+}
+
+void ScSimpleFormulaCalculator::SetLimitString(bool bLimitString)
+{
+ mbLimitString = bLimitString;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/sortparam.cxx b/sc/source/core/data/sortparam.cxx
new file mode 100644
index 0000000000..3e1506dbad
--- /dev/null
+++ b/sc/source/core/data/sortparam.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 <sortparam.hxx>
+#include <global.hxx>
+#include <address.hxx>
+#include <queryparam.hxx>
+#include <subtotalparam.hxx>
+
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+ScSortParam::ScSortParam()
+{
+ Clear();
+}
+
+ScSortParam::ScSortParam( const ScSortParam& r ) :
+ nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2),
+ aDataAreaExtras(r.aDataAreaExtras),
+ nUserIndex(r.nUserIndex),
+ bHasHeader(r.bHasHeader),bByRow(r.bByRow),bCaseSens(r.bCaseSens),
+ bNaturalSort(r.bNaturalSort),
+ bUserDef(r.bUserDef),
+ bInplace(r.bInplace),
+ nDestTab(r.nDestTab),nDestCol(r.nDestCol),nDestRow(r.nDestRow),
+ maKeyState( r.maKeyState ),
+ aCollatorLocale( r.aCollatorLocale ), aCollatorAlgorithm( r.aCollatorAlgorithm ),
+ nCompatHeader( r.nCompatHeader )
+{
+}
+
+ScSortParam::~ScSortParam() {}
+
+void ScSortParam::Clear()
+{
+ ScSortKeyState aKeyState;
+
+ nCol1=nCol2=nDestCol = 0;
+ nRow1=nRow2=nDestRow = 0;
+ aDataAreaExtras = ScDataAreaExtras();
+ aDataAreaExtras.mbCellDrawObjects = true;
+ aDataAreaExtras.mbCellFormats = true;
+ nCompatHeader = 2;
+ nDestTab = 0;
+ nUserIndex = 0;
+ bHasHeader=bCaseSens=bUserDef=bNaturalSort = false;
+ bByRow = bInplace = true;
+ aCollatorLocale = css::lang::Locale();
+ aCollatorAlgorithm.clear();
+
+ aKeyState.bDoSort = false;
+ aKeyState.nField = 0;
+ aKeyState.bAscending = true;
+ aKeyState.aColorSortMode = ScColorSortMode::None;
+
+ // Initialize to default size
+ maKeyState.assign( DEFSORT, aKeyState );
+}
+
+ScSortParam& ScSortParam::operator=( const ScSortParam& r )
+{
+ nCol1 = r.nCol1;
+ nRow1 = r.nRow1;
+ nCol2 = r.nCol2;
+ nRow2 = r.nRow2;
+ aDataAreaExtras = r.aDataAreaExtras;
+ nUserIndex = r.nUserIndex;
+ bHasHeader = r.bHasHeader;
+ bByRow = r.bByRow;
+ bCaseSens = r.bCaseSens;
+ bNaturalSort = r.bNaturalSort;
+ bUserDef = r.bUserDef;
+ bInplace = r.bInplace;
+ nDestTab = r.nDestTab;
+ nDestCol = r.nDestCol;
+ nDestRow = r.nDestRow;
+ maKeyState = r.maKeyState;
+ aCollatorLocale = r.aCollatorLocale;
+ aCollatorAlgorithm = r.aCollatorAlgorithm;
+ nCompatHeader = r.nCompatHeader;
+
+ return *this;
+}
+
+bool ScSortParam::operator==( const ScSortParam& rOther ) const
+{
+ bool bEqual = false;
+ // Number of Sorts the same?
+ sal_uInt16 nLast = 0;
+ sal_uInt16 nOtherLast = 0;
+ sal_uInt16 nSortSize = GetSortKeyCount();
+
+ if ( !maKeyState.empty() )
+ {
+ while ( maKeyState[nLast++].bDoSort && nLast < nSortSize ) ;
+ nLast--;
+ }
+
+ if ( !rOther.maKeyState.empty() )
+ {
+ while ( rOther.maKeyState[nOtherLast++].bDoSort && nOtherLast < nSortSize ) ;
+ nOtherLast--;
+ }
+
+ if ( (nLast == nOtherLast)
+ && (nCol1 == rOther.nCol1)
+ && (nRow1 == rOther.nRow1)
+ && (nCol2 == rOther.nCol2)
+ && (nRow2 == rOther.nRow2)
+ && (aDataAreaExtras == rOther.aDataAreaExtras)
+ && (bHasHeader == rOther.bHasHeader)
+ && (bByRow == rOther.bByRow)
+ && (bCaseSens == rOther.bCaseSens)
+ && (bNaturalSort == rOther.bNaturalSort)
+ && (bUserDef == rOther.bUserDef)
+ && (nUserIndex == rOther.nUserIndex)
+ && (bInplace == rOther.bInplace)
+ && (nDestTab == rOther.nDestTab)
+ && (nDestCol == rOther.nDestCol)
+ && (nDestRow == rOther.nDestRow)
+ && (aCollatorLocale.Language == rOther.aCollatorLocale.Language)
+ && (aCollatorLocale.Country == rOther.aCollatorLocale.Country)
+ && (aCollatorLocale.Variant == rOther.aCollatorLocale.Variant)
+ && (aCollatorAlgorithm == rOther.aCollatorAlgorithm)
+ && ( !maKeyState.empty() || !rOther.maKeyState.empty() )
+ )
+ {
+ bEqual = true;
+ for ( sal_uInt16 i=0; i<=nLast && bEqual; i++ )
+ bEqual = ( maKeyState[i].nField == rOther.maKeyState[i].nField ) &&
+ ( maKeyState[i].bAscending == rOther.maKeyState[i].bAscending );
+ }
+ if ( maKeyState.empty() && rOther.maKeyState.empty() )
+ bEqual = true;
+
+ return bEqual;
+}
+
+ScSortParam::ScSortParam( const ScSubTotalParam& rSub, const ScSortParam& rOld ) :
+ nCol1(rSub.nCol1),nRow1(rSub.nRow1),nCol2(rSub.nCol2),nRow2(rSub.nRow2),
+ aDataAreaExtras(rOld.aDataAreaExtras),
+ nUserIndex(rSub.nUserIndex),
+ bHasHeader(true),bByRow(true),bCaseSens(rSub.bCaseSens),bNaturalSort(rOld.bNaturalSort),
+ bUserDef(rSub.bUserDef),
+ bInplace(true),
+ nDestTab(0),nDestCol(0),nDestRow(0),
+ aCollatorLocale( rOld.aCollatorLocale ), aCollatorAlgorithm( rOld.aCollatorAlgorithm ),
+ nCompatHeader( rOld.nCompatHeader )
+{
+ aDataAreaExtras.mbCellFormats = rSub.bIncludePattern;
+ aDataAreaExtras.resetArea();
+
+ sal_uInt16 i;
+
+ // first the groups from the partial results
+ if (rSub.bDoSort)
+ for (i=0; i<MAXSUBTOTAL; i++)
+ if (rSub.bGroupActive[i])
+ {
+ ScSortKeyState key;
+ key.bDoSort = true;
+ key.nField = rSub.nField[i];
+ key.bAscending = rSub.bAscending;
+ key.aColorSortMode = ScColorSortMode::None;
+ maKeyState.push_back(key);
+ }
+
+ // then the old settings
+ for (i=0; i < rOld.GetSortKeyCount(); i++)
+ if (rOld.maKeyState[i].bDoSort)
+ {
+ SCCOLROW nThisField = rOld.maKeyState[i].nField;
+ bool bDouble = false;
+ for (sal_uInt16 j = 0; j < GetSortKeyCount(); j++)
+ if ( maKeyState[j].nField == nThisField )
+ bDouble = true;
+ if (!bDouble) // do not enter a field twice
+ {
+ ScSortKeyState key;
+ key.bDoSort = true;
+ key.nField = nThisField;
+ key.bAscending = rOld.maKeyState[i].bAscending;
+ key.aColorSortMode = ScColorSortMode::None;
+ maKeyState.push_back(key);
+ }
+ }
+}
+
+ScSortParam::ScSortParam( const ScQueryParam& rParam, SCCOL nCol ) :
+ nCol1(nCol),nRow1(rParam.nRow1),nCol2(nCol),nRow2(rParam.nRow2),nUserIndex(0),
+ bHasHeader(rParam.bHasHeader),bByRow(true),bCaseSens(rParam.bCaseSens),
+ bNaturalSort(false),
+//TODO: what about Locale and Algorithm?
+ bUserDef(false),
+ bInplace(true),
+ nDestTab(0),nDestCol(0),nDestRow(0), nCompatHeader(2)
+{
+ aDataAreaExtras.mbCellDrawObjects = true;
+
+ ScSortKeyState aKeyState;
+ aKeyState.bDoSort = true;
+ aKeyState.nField = nCol;
+ aKeyState.bAscending = true;
+ aKeyState.aColorSortMode = ScColorSortMode::None;
+
+ maKeyState.push_back( aKeyState );
+
+ // Set the rest
+ aKeyState.bDoSort = false;
+ aKeyState.nField = 0;
+
+ for (sal_uInt16 i=1; i<GetSortKeyCount(); i++)
+ maKeyState.push_back( aKeyState );
+}
+
+void ScSortParam::MoveToDest()
+{
+ if (!bInplace)
+ {
+ SCCOL nDifX = nDestCol - nCol1;
+ SCROW nDifY = nDestRow - nRow1;
+
+ nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nDifX );
+ nRow1 = sal::static_int_cast<SCROW>( nRow1 + nDifY );
+ nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nDifX );
+ nRow2 = sal::static_int_cast<SCROW>( nRow2 + nDifY );
+ for (sal_uInt16 i=0; i<GetSortKeyCount(); i++)
+ if (bByRow)
+ maKeyState[i].nField += nDifX;
+ else
+ maKeyState[i].nField += nDifY;
+
+ bInplace = true;
+ }
+ else
+ {
+ OSL_FAIL("MoveToDest, bInplace == TRUE");
+ }
+}
+
+namespace sc {
+
+namespace {
+
+struct ReorderIndex
+{
+ struct LessByPos2
+ {
+ bool operator() ( const ReorderIndex& r1, const ReorderIndex& r2 ) const
+ {
+ return r1.mnPos2 < r2.mnPos2;
+ }
+ };
+
+ SCCOLROW mnPos1;
+ SCCOLROW mnPos2;
+
+ ReorderIndex( SCCOLROW nPos1, SCCOLROW nPos2 ) : mnPos1(nPos1), mnPos2(nPos2) {}
+};
+
+}
+
+void ReorderParam::reverse()
+{
+ SCCOLROW nStart;
+ if (mbByRow)
+ nStart = maSortRange.aStart.Row();
+ else
+ nStart = maSortRange.aStart.Col();
+
+ size_t n = maOrderIndices.size();
+ std::vector<ReorderIndex> aBucket;
+ aBucket.reserve(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ SCCOLROW nPos1 = i + nStart;
+ SCCOLROW nPos2 = maOrderIndices[i];
+ aBucket.emplace_back(nPos1, nPos2);
+ }
+
+ std::sort(aBucket.begin(), aBucket.end(), ReorderIndex::LessByPos2());
+ std::vector<SCCOLROW> aNew;
+ aNew.reserve(n);
+ for (size_t i = 0; i < n; ++i)
+ aNew.push_back(aBucket[i].mnPos1);
+
+ maOrderIndices.swap(aNew);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/stlpool.cxx b/sc/source/core/data/stlpool.cxx
new file mode 100644
index 0000000000..6fa08b8112
--- /dev/null
+++ b/sc/source/core/data/stlpool.cxx
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/fontitem.hxx>
+#include <svx/pageitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/IndexedStyleSheets.hxx>
+#include <unotools/charclass.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+
+#include <sc.hrc>
+#include <attrib.hxx>
+#include <global.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <editutil.hxx>
+#include <stylehelper.hxx>
+
+ScStyleSheetPool::ScStyleSheetPool( const SfxItemPool& rPoolP,
+ ScDocument* pDocument )
+ : SfxStyleSheetPool( rPoolP ),
+ pActualStyleSheet( nullptr ),
+ pDoc( pDocument ),
+ bHasStandardStyles( false )
+{
+}
+
+ScStyleSheetPool::~ScStyleSheetPool()
+{
+}
+
+void ScStyleSheetPool::SetDocument( ScDocument* pDocument )
+{
+ pDoc = pDocument;
+}
+
+SfxStyleSheetBase& ScStyleSheetPool::Make( const OUString& rName,
+ SfxStyleFamily eFam, SfxStyleSearchBits mask)
+{
+ if ( rName == STRING_STANDARD && Find( rName, eFam ) != nullptr )
+ {
+ // When updating styles from a template, Office 5.1 sometimes created
+ // files with multiple default styles.
+ // Create new styles in that case:
+
+ //TODO: only when loading?
+
+ OSL_FAIL("renaming additional default style");
+ sal_uInt32 nCount = GetIndexedStyleSheets().GetNumberOfStyleSheets();
+ for ( sal_uInt32 nAdd = 1; nAdd <= nCount; nAdd++ )
+ {
+ OUString aNewName = ScResId(STR_STYLENAME_STANDARD) + OUString::number( nAdd );
+ if ( Find( aNewName, eFam ) == nullptr )
+ return SfxStyleSheetPool::Make(aNewName, eFam, mask);
+ }
+ }
+
+ // Core uses translated names for both naming and display.
+ // This for all three, loading standard builtin styles from styles.xml
+ // configuration, loading documents and updating from templates.
+ return SfxStyleSheetPool::Make( ScStyleNameConversion::ProgrammaticToDisplayName( rName, eFam), eFam, mask);
+}
+
+rtl::Reference<SfxStyleSheetBase> ScStyleSheetPool::Create( const OUString& rName,
+ SfxStyleFamily eFamily,
+ SfxStyleSearchBits nMaskP )
+{
+ rtl::Reference<ScStyleSheet> pSheet = new ScStyleSheet( rName, *this, eFamily, nMaskP );
+ if ( eFamily != SfxStyleFamily::Page && ScResId(STR_STYLENAME_STANDARD) != rName )
+ pSheet->SetParent( ScResId(STR_STYLENAME_STANDARD) );
+
+ return pSheet;
+}
+
+rtl::Reference<SfxStyleSheetBase> ScStyleSheetPool::Create( const SfxStyleSheetBase& rStyle )
+{
+ OSL_ENSURE( rStyle.isScStyleSheet(), "Invalid StyleSheet-class! :-/" );
+ return new ScStyleSheet( static_cast<const ScStyleSheet&>(rStyle) );
+}
+
+void ScStyleSheetPool::Remove( SfxStyleSheetBase* pStyle )
+{
+ if ( pStyle )
+ {
+ OSL_ENSURE( SfxStyleSearchBits::UserDefined & pStyle->GetMask(),
+ "SfxStyleSearchBits::UserDefined not set!" );
+
+ static_cast<ScDocumentPool&>(rPool).StyleDeleted(static_cast<ScStyleSheet*>(pStyle));
+ SfxStyleSheetPool::Remove(pStyle);
+ }
+}
+
+void ScStyleSheetPool::CopyStyleFrom( SfxStyleSheetBasePool* pSrcPool,
+ const OUString& rName, SfxStyleFamily eFamily,
+ bool bNewStyleHierarchy )
+{
+ // this is the Dest-Pool
+
+ SfxStyleSheetBase* pStyleSheet = pSrcPool->Find( rName, eFamily );
+ if (!pStyleSheet)
+ return;
+
+ const SfxItemSet& rSourceSet = pStyleSheet->GetItemSet();
+ SfxStyleSheetBase* pDestSheet = Find( rName, eFamily );
+ if (pDestSheet && bNewStyleHierarchy)
+ return;
+ if (!pDestSheet)
+ pDestSheet = &Make( rName, eFamily, pStyleSheet->GetMask() );
+ SfxItemSet& rDestSet = pDestSheet->GetItemSet();
+ rDestSet.PutExtended( rSourceSet, SfxItemState::DONTCARE, SfxItemState::DEFAULT );
+
+ if ( eFamily == SfxStyleFamily::Page )
+ {
+ // Set-Items
+
+ if ( const SvxSetItem* pSetItem = rSourceSet.GetItemIfSet( ATTR_PAGE_HEADERSET, false ) )
+ {
+ const SfxItemSet& rSrcSub = pSetItem->GetItemSet();
+ SfxItemSet aDestSub( *rDestSet.GetPool(), rSrcSub.GetRanges() );
+ aDestSub.PutExtended( rSrcSub, SfxItemState::DONTCARE, SfxItemState::DEFAULT );
+ }
+ if ( const SvxSetItem* pSetItem = rSourceSet.GetItemIfSet( ATTR_PAGE_FOOTERSET, false ) )
+ {
+ const SfxItemSet& rSrcSub = pSetItem->GetItemSet();
+ SfxItemSet aDestSub( *rDestSet.GetPool(), rSrcSub.GetRanges() );
+ aDestSub.PutExtended( rSrcSub, SfxItemState::DONTCARE, SfxItemState::DEFAULT );
+ rDestSet.Put( SvxSetItem( ATTR_PAGE_FOOTERSET, aDestSub ) );
+ }
+ }
+ else if ( eFamily == SfxStyleFamily::Para )
+ {
+ // number format exchange list has to be handled here, too
+
+ const SfxUInt32Item* pItem;
+ if ( pDoc && pDoc->GetFormatExchangeList() &&
+ (pItem = rSourceSet.GetItemIfSet( ATTR_VALUE_FORMAT, false )) )
+ {
+ sal_uLong nOldFormat = pItem->GetValue();
+ SvNumberFormatterIndexTable::const_iterator it = pDoc->GetFormatExchangeList()->find(nOldFormat);
+ if (it != pDoc->GetFormatExchangeList()->end())
+ {
+ sal_uInt32 nNewFormat = it->second;
+ rDestSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ }
+ }
+ }
+
+ const OUString aParentName = pStyleSheet->GetParent();
+ if (!bNewStyleHierarchy || aParentName.isEmpty())
+ return;
+
+ CopyStyleFrom(pSrcPool, aParentName, eFamily, bNewStyleHierarchy);
+ pDestSheet->SetParent(aParentName);
+}
+
+void ScStyleSheetPool::CopyUsedGraphicStylesFrom(SfxStyleSheetBasePool* pSrcPool)
+{
+ // this is the Dest-Pool
+
+ std::vector<std::pair<SfxStyleSheetBase*, OUString>> aNewStyles;
+
+ auto pSrcSheet = pSrcPool->First(SfxStyleFamily::Frame);
+ while (pSrcSheet)
+ {
+ if (pSrcSheet->IsUsed() && !Find(pSrcSheet->GetName(), pSrcSheet->GetFamily()))
+ {
+ auto pDestSheet = &Make(pSrcSheet->GetName(), pSrcSheet->GetFamily(), pSrcSheet->GetMask());
+ aNewStyles.emplace_back(pDestSheet, pSrcSheet->GetParent());
+
+ SfxItemSet& rDestSet = pDestSheet->GetItemSet();
+ rDestSet.Put(pSrcSheet->GetItemSet());
+ }
+
+ pSrcSheet = pSrcPool->Next();
+ }
+
+ for (const auto& style : aNewStyles)
+ style.first->SetParent(style.second);
+}
+
+// Standard templates
+
+void ScStyleSheetPool::CopyStdStylesFrom( ScStyleSheetPool* pSrcPool )
+{
+ // Copy Default styles
+
+ CopyStyleFrom( pSrcPool, ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para );
+ CopyStyleFrom( pSrcPool, ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Frame );
+ CopyStyleFrom( pSrcPool, ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Page );
+ CopyStyleFrom( pSrcPool, ScResId(STR_STYLENAME_REPORT), SfxStyleFamily::Page );
+}
+
+static void lcl_CheckFont( SfxItemSet& rSet, LanguageType eLang, DefaultFontType nFontType, sal_uInt16 nItemId )
+{
+ if ( eLang != LANGUAGE_NONE && eLang != LANGUAGE_DONTKNOW && eLang != LANGUAGE_SYSTEM )
+ {
+ vcl::Font aDefFont = OutputDevice::GetDefaultFont( nFontType, eLang, GetDefaultFontFlags::OnlyOne );
+ SvxFontItem aNewItem( aDefFont.GetFamilyType(), aDefFont.GetFamilyName(), aDefFont.GetStyleName(),
+ aDefFont.GetPitch(), aDefFont.GetCharSet(), nItemId );
+ if ( aNewItem != rSet.Get( nItemId ) )
+ {
+ // put item into style's ItemSet only if different from (static) default
+ rSet.Put( aNewItem );
+ }
+ }
+}
+
+void ScStyleSheetPool::CreateStandardStyles()
+{
+ // Add new entries even for CopyStdStylesFrom
+
+ Color aColBlack ( COL_BLACK );
+ OUString aStr;
+ sal_Int32 nStrLen;
+ const OUString aHelpFile;//which text???
+ SfxItemSet* pSet = nullptr;
+ SfxItemSet* pHFSet = nullptr;
+ ScEditEngineDefaulter aEdEngine( EditEngine::CreatePool().get(), true );
+ aEdEngine.SetUpdateLayout( false );
+ std::unique_ptr<EditTextObject> pEmptyTxtObj = aEdEngine.CreateTextObject();
+ std::unique_ptr<EditTextObject> pTxtObj;
+ ScPageHFItem aHeaderItem( ATTR_PAGE_HEADERRIGHT );
+ ScPageHFItem aFooterItem( ATTR_PAGE_FOOTERRIGHT );
+ ScStyleSheet* pSheet = nullptr;
+ ::editeng::SvxBorderLine aBorderLine ( &aColBlack, SvxBorderLineWidth::Medium );
+ SvxBoxItem aBoxItem ( ATTR_BORDER );
+ SvxBoxInfoItem aBoxInfoItem ( ATTR_BORDER_INNER );
+
+ OUString aStrStandard = ScResId(STR_STYLENAME_STANDARD);
+
+ // Cell format templates:
+
+ // 1. Standard
+
+ pSheet = static_cast<ScStyleSheet*>( &Make( aStrStandard, SfxStyleFamily::Para, SfxStyleSearchBits::ScStandard ) );
+ pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_CELL_STD );
+
+ // if default fonts for the document's languages are different from the pool default,
+ // put them into the default style
+ // (not as pool defaults, because pool defaults can't be changed by the user)
+ // the document languages must be set before creating the default styles!
+
+ pSet = &pSheet->GetItemSet();
+ LanguageType eLatin, eCjk, eCtl;
+ pDoc->GetLanguage( eLatin, eCjk, eCtl );
+
+ // If the UI language is Korean, the default Latin font has to
+ // be queried for Korean, too (the Latin language from the document can't be Korean).
+ // This is the same logic as in SwDocShell::InitNew.
+ LanguageType eUiLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ if (MsLangId::isKorean(eUiLanguage))
+ eLatin = eUiLanguage;
+
+ lcl_CheckFont( *pSet, eLatin, DefaultFontType::LATIN_SPREADSHEET, ATTR_FONT );
+ lcl_CheckFont( *pSet, eCjk, DefaultFontType::CJK_SPREADSHEET, ATTR_CJK_FONT );
+ lcl_CheckFont( *pSet, eCtl, DefaultFontType::CTL_SPREADSHEET, ATTR_CTL_FONT );
+
+ // #i55300# default CTL font size for Thai has to be larger
+ // #i59408# The 15 point size causes problems with row heights, so no different
+ // size is used for Thai in Calc for now.
+// if ( eCtl == LANGUAGE_THAI )
+// pSet->Put( SvxFontHeightItem( 300, 100, ATTR_CTL_FONT_HEIGHT ) ); // 15 pt
+
+ // Page format template:
+
+ // 1. Standard
+
+ pSheet = static_cast<ScStyleSheet*>( &Make( aStrStandard,
+ SfxStyleFamily::Page,
+ SfxStyleSearchBits::ScStandard ) );
+
+ pSet = &pSheet->GetItemSet();
+ pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_PAGE_STD );
+
+ // distance to header/footer for the sheet
+ SvxSetItem aHFSetItem = pSet->Get( ATTR_PAGE_HEADERSET );
+ aHFSetItem.SetWhich(ATTR_PAGE_HEADERSET);
+ pSet->Put( aHFSetItem );
+ aHFSetItem.SetWhich(ATTR_PAGE_FOOTERSET);
+ pSet->Put( aHFSetItem );
+
+ // Header:
+ // [empty][\sheet\][empty]
+
+ aEdEngine.SetTextCurrentDefaults(OUString());
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), ESelection() );
+ pTxtObj = aEdEngine.CreateTextObject();
+ aHeaderItem.SetLeftArea ( *pEmptyTxtObj );
+ aHeaderItem.SetCenterArea( *pTxtObj );
+ aHeaderItem.SetRightArea ( *pEmptyTxtObj );
+ pSet->Put( aHeaderItem );
+
+ // Footer:
+ // [empty][Page \STR_PAGE\][empty]
+
+ aStr = ScResId( STR_PAGE ) + " ";
+ aEdEngine.SetTextCurrentDefaults( aStr );
+ nStrLen = aStr.getLength();
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(0,nStrLen,0,nStrLen) );
+ pTxtObj = aEdEngine.CreateTextObject();
+ aFooterItem.SetLeftArea ( *pEmptyTxtObj );
+ aFooterItem.SetCenterArea( *pTxtObj );
+ aFooterItem.SetRightArea ( *pEmptyTxtObj );
+ pSet->Put( aFooterItem );
+
+ // 2. Report
+
+ pSheet = static_cast<ScStyleSheet*>( &Make( ScResId( STR_STYLENAME_REPORT ),
+ SfxStyleFamily::Page,
+ SfxStyleSearchBits::ScStandard ) );
+ pSet = &pSheet->GetItemSet();
+ pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_PAGE_REP );
+
+ // Background and border
+ aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::TOP );
+ aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::BOTTOM );
+ aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::LEFT );
+ aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::RIGHT );
+ aBoxItem.SetAllDistances( 10 ); // 0.2mm
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::TOP );
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::BOTTOM );
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::LEFT );
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::RIGHT );
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ aBoxInfoItem.SetTable( false );
+ aBoxInfoItem.SetDist ( true );
+
+ SvxSetItem aHFSetItem2 = pSet->Get( ATTR_PAGE_HEADERSET );
+ pHFSet = &(aHFSetItem2.GetItemSet());
+
+ pHFSet->Put( SvxBrushItem( COL_LIGHTGRAY, ATTR_BACKGROUND ) );
+ pHFSet->Put( aBoxItem );
+ pHFSet->Put( aBoxInfoItem );
+ aHFSetItem2.SetWhich(ATTR_PAGE_HEADERSET);
+ pSet->Put( aHFSetItem2 );
+ aHFSetItem2.SetWhich(ATTR_PAGE_FOOTERSET);
+ pSet->Put( aHFSetItem2 );
+
+ // Footer:
+ // [\TABLE\ (\DATA\)][empty][\DATE\, \TIME\]
+
+ aStr = " ()";
+ aEdEngine.SetTextCurrentDefaults( aStr );
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxFileField(), EE_FEATURE_FIELD), ESelection(0,2,0,2) );
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), ESelection() );
+ pTxtObj = aEdEngine.CreateTextObject();
+ aHeaderItem.SetLeftArea( *pTxtObj );
+ aHeaderItem.SetCenterArea( *pEmptyTxtObj );
+ aStr = ", ";
+ aEdEngine.SetTextCurrentDefaults( aStr );
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxTimeField(), EE_FEATURE_FIELD), ESelection(0,2,0,2) );
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxDateField(Date( Date::SYSTEM ),SvxDateType::Var), EE_FEATURE_FIELD),
+ ESelection() );
+ pTxtObj = aEdEngine.CreateTextObject();
+ aHeaderItem.SetRightArea( *pTxtObj );
+ pSet->Put( aHeaderItem );
+
+ // Footer:
+ // [empty][Page: \PAGE\ / \PAGE\][empty]
+
+ aStr = ScResId( STR_PAGE ) + " ";
+ nStrLen = aStr.getLength();
+ aStr += " / ";
+ sal_Int32 nStrLen2 = aStr.getLength();
+ aEdEngine.SetTextCurrentDefaults( aStr );
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxPagesField(), EE_FEATURE_FIELD), ESelection(0,nStrLen2,0,nStrLen2) );
+ aEdEngine.QuickInsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(0,nStrLen,0,nStrLen) );
+ pTxtObj = aEdEngine.CreateTextObject();
+ aFooterItem.SetLeftArea ( *pEmptyTxtObj );
+ aFooterItem.SetCenterArea( *pTxtObj );
+ aFooterItem.SetRightArea ( *pEmptyTxtObj );
+ pSet->Put( aFooterItem );
+
+ bHasStandardStyles = true;
+}
+
+namespace {
+
+struct CaseInsensitiveNamePredicate : svl::StyleSheetPredicate
+{
+ CaseInsensitiveNamePredicate(const OUString& rName, SfxStyleFamily eFam)
+ : mUppercaseName(ScGlobal::getCharClass().uppercase(rName)), mFamily(eFam)
+ {
+ }
+
+ bool
+ Check(const SfxStyleSheetBase& rStyleSheet) override
+ {
+ if (rStyleSheet.GetFamily() == mFamily)
+ {
+ OUString aUpName = ScGlobal::getCharClass().uppercase(rStyleSheet.GetName());
+ if (mUppercaseName == aUpName)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ OUString mUppercaseName;
+ SfxStyleFamily mFamily;
+};
+
+}
+
+// Functor object to find all style sheets of a family which match a given name caseinsensitively
+ScStyleSheet* ScStyleSheetPool::FindCaseIns( const OUString& rName, SfxStyleFamily eFam )
+{
+ CaseInsensitiveNamePredicate aPredicate(rName, eFam);
+ std::vector<sal_Int32> aFoundPositions = GetIndexedStyleSheets().FindPositionsByPredicate(aPredicate);
+
+ ScStyleSheet* first = nullptr; // first case insensitive match found
+ for (const auto& rPos : aFoundPositions)
+ {
+ SfxStyleSheetBase *pFound = GetStyleSheetByPositionInIndex(rPos);
+ // we do not know what kind of sheets we have.
+ if (pFound->isScStyleSheet())
+ {
+ if (pFound->GetName() == rName) // exact case sensitive match
+ return static_cast<ScStyleSheet*>(pFound);
+ if (!first)
+ first = static_cast<ScStyleSheet*>(pFound);
+ }
+ }
+ return first;
+}
+
+ScStyleSheet* ScStyleSheetPool::FindAutoStyle(const OUString& rName)
+{
+ ScStyleSheet* pStyleSheet = FindCaseIns(rName, SfxStyleFamily::Para);
+ if (!pStyleSheet)
+ if (auto pFound = Find(ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para))
+ if (pFound->isScStyleSheet()) // we do not know what kind of sheets we have
+ pStyleSheet = static_cast<ScStyleSheet*>(pFound);
+ return pStyleSheet;
+}
+
+void ScStyleSheetPool::setAllParaStandard()
+{
+ SfxStyleSheetBase* pSheet = First(SfxStyleFamily::Para);
+ while (pSheet)
+ {
+ pSheet->SetMask(SfxStyleSearchBits::ScStandard);
+ pSheet = Next();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/stlsheet.cxx b/sc/source/core/data/stlsheet.cxx
new file mode 100644
index 0000000000..7679f1936a
--- /dev/null
+++ b/sc/source/core/data/stlsheet.cxx
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+
+#include <scitems.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <svx/pageitem.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/xdef.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <svl/hint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <attrib.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+
+constexpr auto TWO_CM = o3tl::convert(2, o3tl::Length::cm, o3tl::Length::twip); // 1134
+constexpr auto HFDIST_CM = o3tl::convert(250, o3tl::Length::mm100, o3tl::Length::twip); // 142
+
+ScStyleSheet::ScStyleSheet( const OUString& rName,
+ const ScStyleSheetPool& rPoolP,
+ SfxStyleFamily eFamily,
+ SfxStyleSearchBits nMaskP )
+
+ : SfxStyleSheet ( rName, rPoolP, eFamily, nMaskP )
+ , eUsage( Usage::UNKNOWN )
+{
+}
+
+ScStyleSheet::ScStyleSheet( const ScStyleSheet& rStyle )
+ : SfxStyleSheet ( rStyle )
+ , eUsage( Usage::UNKNOWN )
+{
+}
+
+ScStyleSheet::~ScStyleSheet()
+{
+}
+
+bool ScStyleSheet::HasFollowSupport() const
+{
+ return false;
+}
+
+bool ScStyleSheet::HasParentSupport () const
+{
+ bool bHasParentSupport = false;
+
+ switch ( GetFamily() )
+ {
+ case SfxStyleFamily::Para: bHasParentSupport = true; break;
+ case SfxStyleFamily::Frame: bHasParentSupport = true; break;
+ case SfxStyleFamily::Page: bHasParentSupport = false; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ return bHasParentSupport;
+}
+
+bool ScStyleSheet::SetParent( const OUString& rParentName )
+{
+ bool bResult = false;
+ OUString aEffName = rParentName;
+ SfxStyleSheetBase* pStyle = m_pPool->Find( aEffName, nFamily );
+ if (!pStyle)
+ {
+ std::unique_ptr<SfxStyleSheetIterator> pIter = m_pPool->CreateIterator(nFamily);
+ pStyle = pIter->First();
+ if (pStyle)
+ aEffName = pStyle->GetName();
+ }
+
+ if ( pStyle && aEffName != GetName() )
+ {
+ bResult = SfxStyleSheet::SetParent( aEffName );
+ if (bResult)
+ {
+ SfxItemSet& rParentSet = pStyle->GetItemSet();
+ GetItemSet().SetParent( &rParentSet );
+
+ // #i113491# Drag&Drop in the stylist's hierarchical view doesn't execute a slot,
+ // so the repaint has to come from here (after modifying the ItemSet).
+ // RepaintRange checks the document's IsVisible flag and locked repaints.
+ ScDocument* pDoc = static_cast<ScStyleSheetPool*>(GetPool())->GetDocument();
+ if (pDoc)
+ pDoc->RepaintRange( ScRange( 0,0,0, pDoc->MaxCol(),pDoc->MaxRow(),MAXTAB ) );
+ }
+ }
+
+ return bResult;
+}
+
+void ScStyleSheet::ResetParent()
+{
+ GetItemSet().SetParent(nullptr);
+}
+
+SfxItemSet& ScStyleSheet::GetItemSet()
+{
+ if ( !pSet )
+ {
+ switch ( GetFamily() )
+ {
+ case SfxStyleFamily::Page:
+ {
+ // Page templates should not be derivable,
+ // therefore suitable values are set at this point.
+ // (== Standard page template)
+
+ SfxItemPool& rItemPool = GetPool()->GetPool();
+ pSet = new SfxItemSetFixed<
+ ATTR_USERDEF, ATTR_USERDEF,
+ ATTR_WRITINGDIR, ATTR_WRITINGDIR,
+ ATTR_BACKGROUND, ATTR_BACKGROUND,
+ ATTR_BORDER, ATTR_SHADOW,
+ ATTR_LRSPACE, ATTR_PAGE_SCALETO>(rItemPool);
+
+ // If being loaded also the set is then filled in from the file,
+ // so the defaults do not need to be set.
+ // GetPrinter would then also create a new printer,
+ // because the stored Printer is not loaded yet!
+
+ ScDocument* pDoc = static_cast<ScStyleSheetPool*>(GetPool())->GetDocument();
+ if ( pDoc )
+ {
+ // Setting reasonable default values:
+ SvxPageItem aPageItem( ATTR_PAGE );
+ SvxSizeItem aPaperSizeItem( ATTR_PAGE_SIZE, SvxPaperInfo::GetDefaultPaperSize() );
+
+ SvxSetItem aHFSetItem(
+ rItemPool.GetDefaultItem(ATTR_PAGE_HEADERSET) );
+
+ SfxItemSet& rHFSet = aHFSetItem.GetItemSet();
+ SvxSizeItem aHFSizeItem( // 0,5 cm + distance
+ ATTR_PAGE_SIZE,
+ Size( 0, o3tl::convert(500, o3tl::Length::mm100, o3tl::Length::twip) + HFDIST_CM ) );
+
+ SvxULSpaceItem aHFDistItem ( HFDIST_CM,// nUp
+ HFDIST_CM,// nLow
+ ATTR_ULSPACE );
+
+ SvxLRSpaceItem aLRSpaceItem( TWO_CM, // nLeft
+ TWO_CM, // nRight
+ 0, // nFirstLineOffset
+ ATTR_LRSPACE );
+ SvxULSpaceItem aULSpaceItem( TWO_CM, // nUp
+ TWO_CM, // nLow
+ ATTR_ULSPACE );
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+
+ aBoxInfoItem.SetTable( false );
+ aBoxInfoItem.SetDist( true );
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+
+ aPageItem.SetLandscape( false );
+
+ rHFSet.Put( aBoxInfoItem );
+ rHFSet.Put( aHFSizeItem );
+ rHFSet.Put( aHFDistItem );
+ rHFSet.Put( SvxLRSpaceItem(0, 0, 0, ATTR_LRSPACE) ); // Set border to Null
+
+ aHFSetItem.SetWhich(ATTR_PAGE_HEADERSET);
+ pSet->Put( aHFSetItem );
+ aHFSetItem.SetWhich(ATTR_PAGE_FOOTERSET);
+ pSet->Put( aHFSetItem );
+ pSet->Put( aBoxInfoItem ); // Do not overwrite PoolDefault
+ // due to format templates
+
+
+ // Writing direction: not as pool default because the default for cells
+ // must remain SvxFrameDirection::Environment, and each page style's setting is
+ // supposed to be saved in the file format.
+ // The page default depends on the system language.
+ SvxFrameDirection eDirection = ScGlobal::IsSystemRTL() ?
+ SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB;
+ pSet->Put( SvxFrameDirectionItem( eDirection, ATTR_WRITINGDIR ) );
+
+ rItemPool.SetPoolDefaultItem( aPageItem );
+ rItemPool.SetPoolDefaultItem( aPaperSizeItem );
+ rItemPool.SetPoolDefaultItem( aLRSpaceItem );
+ rItemPool.SetPoolDefaultItem( aULSpaceItem );
+ rItemPool.SetPoolDefaultItem( SfxUInt16Item( ATTR_PAGE_SCALE, 100 ) );
+ ScPageScaleToItem aScaleToItem;
+ rItemPool.SetPoolDefaultItem( aScaleToItem );
+ rItemPool.SetPoolDefaultItem( SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, 0 ) );
+ }
+ }
+ break;
+
+ case SfxStyleFamily::Frame:
+ {
+ SfxItemPool* pItemPool = &GetPool()->GetPool();
+ if (dynamic_cast<SdrItemPool*>(pItemPool) == nullptr)
+ pItemPool = pItemPool->GetSecondaryPool();
+ assert(pItemPool);
+
+ pSet = new SfxItemSetFixed<
+ XATTR_LINE_FIRST, XATTR_LINE_LAST,
+ XATTR_FILL_FIRST, XATTR_FILL_LAST,
+ SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST,
+ SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_WORDWRAP,
+ SDRATTR_EDGE_FIRST, SDRATTR_MEASURE_LAST,
+ SDRATTR_3D_FIRST, SDRATTR_3D_LAST,
+ EE_PARA_START, EE_CHAR_END>(*pItemPool);
+ }
+ break;
+
+ case SfxStyleFamily::Para:
+ default:
+ pSet = new SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( GetPool()->GetPool() );
+ break;
+ }
+ bMySet = true;
+ }
+ if ( nHelpId == HID_SC_SHEET_CELL_ERG1 )
+ {
+ if ( !pSet->Count() )
+ {
+ // Hack to work around that when this code is called from
+ // ~ScStyleSheetPool -> ~SfxStyleSheetPool, GetPool() is no longer
+ // an ScStyleSheetPool:
+ ScStyleSheetPool * pool = dynamic_cast<ScStyleSheetPool *>(
+ GetPool());
+ if (pool != nullptr) {
+ ScDocument* pDoc = pool->GetDocument();
+ if ( pDoc )
+ {
+ sal_uInt32 nNumFmt = pDoc->GetFormatTable()->GetStandardFormat( SvNumFormatType::CURRENCY,ScGlobal::eLnge );
+ pSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumFmt ) );
+ }
+ }
+ }
+ }
+
+ return *pSet;
+}
+
+bool ScStyleSheet::IsUsed() const
+{
+ switch (GetFamily())
+ {
+ case SfxStyleFamily::Para:
+ {
+ // Always query the document to let it decide if a rescan is necessary,
+ // and store the state.
+ ScDocument* pDoc = static_cast<ScStyleSheetPool*>(m_pPool)->GetDocument();
+ if ( pDoc && pDoc->IsStyleSheetUsed( *this ) )
+ eUsage = Usage::USED;
+ else
+ eUsage = Usage::NOTUSED;
+ return eUsage == Usage::USED;
+ }
+ case SfxStyleFamily::Page:
+ {
+ // tdf#108188 - verify that the page style is actually used
+ ScDocument* pDoc = static_cast<ScStyleSheetPool*>(m_pPool)->GetDocument();
+ if (pDoc && pDoc->IsPageStyleInUse(GetName(), nullptr))
+ eUsage = Usage::USED;
+ else
+ eUsage = Usage::NOTUSED;
+ return eUsage == Usage::USED;
+ }
+ case SfxStyleFamily::Frame:
+ {
+ ForAllListeners([this] (SfxListener* pListener)
+ {
+ auto pUser(dynamic_cast<svl::StyleSheetUser*>(pListener));
+ if (pUser && pUser->isUsedByModel())
+ {
+ eUsage = Usage::USED;
+ return true; // break loop
+ }
+ else
+ eUsage = Usage::NOTUSED;
+ return false;
+ });
+ return eUsage == Usage::USED;
+ }
+ default:
+ return true;
+ }
+}
+
+void ScStyleSheet::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ GetItemSet().SetParent( nullptr );
+ if (GetFamily() == SfxStyleFamily::Frame)
+ SfxStyleSheet::Notify(rBC, rHint);
+}
+
+// Avoid creating a Style "Standard" if this is not the Standard-Name;
+// otherwise two styles would have the same name when storing.
+// (on loading the style is created directly per Make with the name; making this query
+// not applicable)
+//TODO: If at any time during loading SetName is called, a flag has to be set/checked for loading
+//TODO: The whole check has to be removed if for a new file version the name transformation is dropped.
+
+bool ScStyleSheet::SetName(const OUString& rNew, bool bReindexNow)
+{
+ OUString aFileStdName = STRING_STANDARD;
+ if ( rNew == aFileStdName && aFileStdName != ScResId(STR_STYLENAME_STANDARD) )
+ return false;
+ else
+ return SfxStyleSheet::SetName(rNew, bReindexNow);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/subtotalparam.cxx b/sc/source/core/data/subtotalparam.cxx
new file mode 100644
index 0000000000..e8f3295429
--- /dev/null
+++ b/sc/source/core/data/subtotalparam.cxx
@@ -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/.
+ */
+
+#include <subtotalparam.hxx>
+
+#include <osl/diagnose.h>
+
+ScSubTotalParam::ScSubTotalParam()
+{
+ for ( sal_uInt16 i=0; i<MAXSUBTOTAL; i++ )
+ {
+ nSubTotals[i] = 0;
+ pSubTotals[i] = nullptr;
+ pFunctions[i] = nullptr;
+ }
+
+ Clear();
+}
+
+ScSubTotalParam::ScSubTotalParam( const ScSubTotalParam& r ) :
+ nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2),nUserIndex(r.nUserIndex),
+ bRemoveOnly(r.bRemoveOnly),bReplace(r.bReplace),bPagebreak(r.bPagebreak),bCaseSens(r.bCaseSens),
+ bDoSort(r.bDoSort),bAscending(r.bAscending),bUserDef(r.bUserDef),
+ bIncludePattern(r.bIncludePattern)
+{
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ bGroupActive[i] = r.bGroupActive[i];
+ nField[i] = r.nField[i];
+
+ if ( (r.nSubTotals[i] > 0) && r.pSubTotals[i] && r.pFunctions[i] )
+ {
+ nSubTotals[i] = r.nSubTotals[i];
+ pSubTotals[i].reset(new SCCOL [r.nSubTotals[i]]);
+ pFunctions[i].reset(new ScSubTotalFunc [r.nSubTotals[i]]);
+
+ for (SCCOL j=0; j<r.nSubTotals[i]; j++)
+ {
+ pSubTotals[i][j] = r.pSubTotals[i][j];
+ pFunctions[i][j] = r.pFunctions[i][j];
+ }
+ }
+ else
+ {
+ nSubTotals[i] = 0;
+ }
+ }
+}
+
+void ScSubTotalParam::Clear()
+{
+ nCol1=nCol2= 0;
+ nRow1=nRow2 = 0;
+ nUserIndex = 0;
+ bPagebreak=bCaseSens=bUserDef=bIncludePattern=bRemoveOnly = false;
+ bAscending=bReplace=bDoSort = true;
+
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ bGroupActive[i] = false;
+ nField[i] = 0;
+
+ if ( (nSubTotals[i] > 0) && pSubTotals[i] && pFunctions[i] )
+ {
+ for ( SCCOL j=0; j<nSubTotals[i]; j++ ) {
+ pSubTotals[i][j] = 0;
+ pFunctions[i][j] = SUBTOTAL_FUNC_NONE;
+ }
+ }
+ }
+}
+
+ScSubTotalParam& ScSubTotalParam::operator=( const ScSubTotalParam& r )
+{
+ if(this == &r)
+ return *this;
+
+ nCol1 = r.nCol1;
+ nRow1 = r.nRow1;
+ nCol2 = r.nCol2;
+ nRow2 = r.nRow2;
+ bRemoveOnly = r.bRemoveOnly;
+ bReplace = r.bReplace;
+ bPagebreak = r.bPagebreak;
+ bCaseSens = r.bCaseSens;
+ bDoSort = r.bDoSort;
+ bAscending = r.bAscending;
+ bUserDef = r.bUserDef;
+ nUserIndex = r.nUserIndex;
+ bIncludePattern = r.bIncludePattern;
+
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ bGroupActive[i] = r.bGroupActive[i];
+ nField[i] = r.nField[i];
+ nSubTotals[i] = r.nSubTotals[i];
+
+ pSubTotals[i].reset();
+ pFunctions[i].reset();
+
+ if ( r.nSubTotals[i] > 0 )
+ {
+ pSubTotals[i].reset(new SCCOL [r.nSubTotals[i]]);
+ pFunctions[i].reset(new ScSubTotalFunc [r.nSubTotals[i]]);
+
+ for (SCCOL j=0; j<r.nSubTotals[i]; j++)
+ {
+ pSubTotals[i][j] = r.pSubTotals[i][j];
+ pFunctions[i][j] = r.pFunctions[i][j];
+ }
+ }
+ else
+ {
+ nSubTotals[i] = 0;
+ }
+ }
+
+ return *this;
+}
+
+bool ScSubTotalParam::operator==( const ScSubTotalParam& rOther ) const
+{
+ bool bEqual = (nCol1 == rOther.nCol1)
+ && (nRow1 == rOther.nRow1)
+ && (nCol2 == rOther.nCol2)
+ && (nRow2 == rOther.nRow2)
+ && (nUserIndex == rOther.nUserIndex)
+ && (bRemoveOnly == rOther.bRemoveOnly)
+ && (bReplace == rOther.bReplace)
+ && (bPagebreak == rOther.bPagebreak)
+ && (bDoSort == rOther.bDoSort)
+ && (bCaseSens == rOther.bCaseSens)
+ && (bAscending == rOther.bAscending)
+ && (bUserDef == rOther.bUserDef)
+ && (bIncludePattern== rOther.bIncludePattern);
+
+ if ( bEqual )
+ {
+ bEqual = true;
+ for ( sal_uInt16 i=0; i<MAXSUBTOTAL && bEqual; i++ )
+ {
+ bEqual = (bGroupActive[i] == rOther.bGroupActive[i])
+ && (nField[i] == rOther.nField[i])
+ && (nSubTotals[i] == rOther.nSubTotals[i]);
+
+ if ( bEqual && (nSubTotals[i] > 0) )
+ {
+ for (SCCOL j=0; (j<nSubTotals[i]) && bEqual; j++)
+ {
+ bEqual = bEqual
+ && (pSubTotals[i][j] == rOther.pSubTotals[i][j])
+ && (pFunctions[i][j] == rOther.pFunctions[i][j]);
+ }
+ }
+ }
+ }
+
+ return bEqual;
+}
+
+void ScSubTotalParam::SetSubTotals( sal_uInt16 nGroup,
+ const SCCOL* ptrSubTotals,
+ const ScSubTotalFunc* ptrFunctions,
+ sal_uInt16 nCount )
+{
+ OSL_ENSURE( (nGroup <= MAXSUBTOTAL),
+ "ScSubTotalParam::SetSubTotals(): nGroup > MAXSUBTOTAL!" );
+ OSL_ENSURE( ptrSubTotals,
+ "ScSubTotalParam::SetSubTotals(): ptrSubTotals == NULL!" );
+ OSL_ENSURE( ptrFunctions,
+ "ScSubTotalParam::SetSubTotals(): ptrFunctions == NULL!" );
+ OSL_ENSURE( (nCount > 0),
+ "ScSubTotalParam::SetSubTotals(): nCount <= 0!" );
+
+ if ( !(ptrSubTotals && ptrFunctions && (nCount > 0) && (nGroup <= MAXSUBTOTAL)) )
+ return;
+
+ // 0 is interpreted as 1, otherwise decrementing the array index
+ if (nGroup != 0)
+ nGroup--;
+
+ pSubTotals[nGroup].reset(new SCCOL[nCount]);
+ pFunctions[nGroup].reset(new ScSubTotalFunc[nCount]);
+ nSubTotals[nGroup] = static_cast<SCCOL>(nCount);
+
+ for ( sal_uInt16 i=0; i<nCount; i++ )
+ {
+ pSubTotals[nGroup][i] = ptrSubTotals[i];
+ pFunctions[nGroup][i] = ptrFunctions[i];
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/tabbgcolor.cxx b/sc/source/core/data/tabbgcolor.cxx
new file mode 100644
index 0000000000..5b14ff8303
--- /dev/null
+++ b/sc/source/core/data/tabbgcolor.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 <tabbgcolor.hxx>
+
+ScUndoTabColorInfo::ScUndoTabColorInfo(SCTAB nTab) :
+ mnTabId(nTab),
+ maOldTabBgColor(COL_AUTO),
+ maNewTabBgColor(COL_AUTO)
+{
+}
+
+ScUndoTabColorInfo::ScUndoTabColorInfo(const ScUndoTabColorInfo& r) :
+ mnTabId(r.mnTabId),
+ maOldTabBgColor(r.maOldTabBgColor),
+ maNewTabBgColor(r.maNewTabBgColor)
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
new file mode 100644
index 0000000000..93d023f962
--- /dev/null
+++ b/sc/source/core/data/table1.cxx
@@ -0,0 +1,2759 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/justifyitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <unotools/textsearch.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <patattr.hxx>
+#include <table.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <olinetab.hxx>
+#include <global.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <refupdat.hxx>
+#include <markdata.hxx>
+#include <progress.hxx>
+#include <prnsave.hxx>
+#include <printopt.hxx>
+#include <scmod.hxx>
+#include <tabprotection.hxx>
+#include <sheetevents.hxx>
+#include <segmenttree.hxx>
+#include <dbdata.hxx>
+#include <conditio.hxx>
+#include <globalnames.hxx>
+#include <cellvalue.hxx>
+#include <scmatrix.hxx>
+#include <refupdatecontext.hxx>
+#include <rowheightcontext.hxx>
+#include <compressedarray.hxx>
+#include <vcl/svapp.hxx>
+
+#include <formula/vectortoken.hxx>
+#include <token.hxx>
+
+#include <vector>
+#include <memory>
+
+using ::std::vector;
+
+namespace {
+
+ScProgress* GetProgressBar(
+ SCSIZE nCount, SCSIZE nTotalCount, ScProgress* pOuterProgress, const ScDocument* pDoc)
+{
+ if (nTotalCount < 1000)
+ {
+ // if the total number of rows is less than 1000, don't even bother
+ // with the progress bar because drawing progress bar can be very
+ // expensive especially in GTK.
+ return nullptr;
+ }
+
+ if (pOuterProgress)
+ return pOuterProgress;
+
+ if (nCount > 1)
+ return new ScProgress(
+ pDoc->GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nTotalCount, true);
+
+ return nullptr;
+}
+
+void GetOptimalHeightsInColumn(
+ sc::RowHeightContext& rCxt, ScColContainer& rCol, SCROW nStartRow, SCROW nEndRow,
+ ScProgress* pProgress, sal_uLong nProgressStart )
+{
+ assert(nStartRow <= nEndRow);
+
+ // first, one time over the whole range
+ // (with the last column in the hope that they most likely still are
+ // on standard format)
+
+
+ rCol.back().GetOptimalHeight(rCxt, nStartRow, nEndRow, 0, 0);
+
+ // from there search for the standard height that is in use in the lower part
+
+ RowHeightsArray& rHeights = rCxt.getHeightArray();
+ sal_uInt16 nMinHeight = rHeights.GetValue(nEndRow);
+ SCSIZE nPos = nEndRow - 1;
+ while ( nPos )
+ {
+ auto aRangeData = rHeights.GetRangeData(nPos-1);
+ if (aRangeData.maValue < nMinHeight)
+ break;
+ nPos = std::max<SCSIZE>(0, aRangeData.mnRow1);
+ }
+
+ const SCROW nMinStart = nPos;
+
+ sal_uInt64 nWeightedCount = nProgressStart + rCol.back().GetWeightedCount(nStartRow, nEndRow);
+ const SCCOL maxCol = rCol.size() - 1; // last col done already above
+ for (SCCOL nCol=0; nCol<maxCol; nCol++)
+ {
+ rCol[nCol].GetOptimalHeight(rCxt, nStartRow, nEndRow, nMinHeight, nMinStart);
+
+ if (pProgress)
+ {
+ nWeightedCount += rCol[nCol].GetWeightedCount(nStartRow, nEndRow);
+ pProgress->SetState( nWeightedCount );
+ }
+ }
+}
+
+struct OptimalHeightsFuncObjBase
+{
+ virtual ~OptimalHeightsFuncObjBase() {}
+ virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight, bool bApi) = 0;
+};
+
+struct SetRowHeightOnlyFunc : public OptimalHeightsFuncObjBase
+{
+ ScTable* mpTab;
+ explicit SetRowHeightOnlyFunc(ScTable* pTab) :
+ mpTab(pTab)
+ {}
+
+ virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight, bool /* bApi */) override
+ {
+ mpTab->SetRowHeightOnly(nStartRow, nEndRow, nHeight);
+ return false;
+ }
+};
+
+struct SetRowHeightRangeFunc : public OptimalHeightsFuncObjBase
+{
+ ScTable* mpTab;
+ double mnPPTY;
+
+ SetRowHeightRangeFunc(ScTable* pTab, double nPPTY) :
+ mpTab(pTab),
+ mnPPTY(nPPTY)
+ {}
+
+ virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight, bool bApi) override
+ {
+ return mpTab->SetRowHeightRange(nStartRow, nEndRow, nHeight, mnPPTY, bApi);
+ }
+};
+
+bool SetOptimalHeightsToRows(
+ sc::RowHeightContext& rCxt,
+ OptimalHeightsFuncObjBase& rFuncObj,
+ ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlags, SCROW nStartRow, SCROW nEndRow,
+ bool bApi )
+{
+ bool bChanged = false;
+ SCROW nRngStart = 0;
+ SCROW nRngEnd = 0;
+ sal_uInt16 nLast = 0;
+ sal_uInt16 nExtraHeight = rCxt.getExtraHeight();
+ for (SCSIZE i = nStartRow; i <= o3tl::make_unsigned(nEndRow); i++)
+ {
+ size_t nIndex;
+ SCROW nRegionEndRow;
+ CRFlags nRowFlag = pRowFlags->GetValue( i, nIndex, nRegionEndRow );
+ if ( nRegionEndRow > nEndRow )
+ nRegionEndRow = nEndRow;
+ SCSIZE nMoreRows = nRegionEndRow - i; // additional equal rows after first
+
+ bool bAutoSize = !(nRowFlag & CRFlags::ManualSize);
+ if (bAutoSize || rCxt.isForceAutoSize())
+ {
+ if (nExtraHeight)
+ {
+ if (bAutoSize)
+ pRowFlags->SetValue( i, nRegionEndRow, nRowFlag | CRFlags::ManualSize);
+ }
+ else if (!bAutoSize)
+ pRowFlags->SetValue( i, nRegionEndRow, nRowFlag & ~CRFlags::ManualSize);
+
+ for (SCSIZE nInner = i; nInner <= i + nMoreRows; ++nInner)
+ {
+ if (nLast)
+ {
+ SCROW nRangeRowEnd;
+ size_t nTmp;
+ sal_uInt16 nRangeValue = rCxt.getHeightArray().GetValue(nInner, nTmp, nRangeRowEnd);
+ if (nRangeValue + nExtraHeight == nLast)
+ {
+ nRngEnd = std::min<SCSIZE>(i + nMoreRows, nRangeRowEnd);
+ nInner = nRangeRowEnd;
+ }
+ else
+ {
+ bChanged |= rFuncObj(nRngStart, nRngEnd, nLast, bApi);
+ nLast = 0;
+ }
+ }
+ if (!nLast)
+ {
+ nLast = rCxt.getHeightArray().GetValue(nInner) + rCxt.getExtraHeight();
+ nRngStart = nInner;
+ nRngEnd = nInner;
+ }
+ }
+ }
+ else
+ {
+ if (nLast)
+ bChanged |= rFuncObj(nRngStart, nRngEnd, nLast, bApi);
+ nLast = 0;
+ }
+ i += nMoreRows; // already handled - skip
+ }
+ if (nLast)
+ bChanged |= rFuncObj(nRngStart, nRngEnd, nLast, bApi);
+
+ return bChanged;
+}
+
+}
+
+ScTable::ScTable( ScDocument& rDoc, SCTAB nNewTab, const OUString& rNewName,
+ bool bColInfo, bool bRowInfo ) :
+ aCol( rDoc.GetSheetLimits(), INITIALCOLCOUNT ),
+ aName( rNewName ),
+ aCodeName( rNewName ),
+ nLinkRefreshDelay( 0 ),
+ nLinkMode( ScLinkMode::NONE ),
+ aPageStyle( ScResId(STR_STYLENAME_STANDARD) ),
+ nRepeatStartX( SCCOL_REPEAT_NONE ),
+ nRepeatEndX( SCCOL_REPEAT_NONE ),
+ nRepeatStartY( SCROW_REPEAT_NONE ),
+ nRepeatEndY( SCROW_REPEAT_NONE ),
+ mnOptimalMinRowHeight(0),
+ mpRowHeights( static_cast<ScFlatUInt16RowSegments*>(nullptr) ),
+ mpHiddenCols(new ScFlatBoolColSegments(rDoc.MaxCol())),
+ mpHiddenRows(new ScFlatBoolRowSegments(rDoc.MaxRow())),
+ mpFilteredCols(new ScFlatBoolColSegments(rDoc.MaxCol())),
+ mpFilteredRows(new ScFlatBoolRowSegments(rDoc.MaxRow())),
+ nTableAreaX( 0 ),
+ nTableAreaY( 0 ),
+ nTableAreaVisibleX( 0 ),
+ nTableAreaVisibleY( 0 ),
+ nTab( nNewTab ),
+ rDocument( rDoc ),
+ pSortCollator( nullptr ),
+ nLockCount( 0 ),
+ aScenarioColor( COL_LIGHTGRAY ),
+ aTabBgColor( COL_AUTO ),
+ nScenarioFlags(ScScenarioFlags::NONE),
+ mpCondFormatList( new ScConditionalFormatList() ),
+ maLOKFreezeCell(-1, -1, nNewTab),
+ bScenario(false),
+ bLayoutRTL(false),
+ bLoadingRTL(false),
+ bPageSizeValid(false),
+ bTableAreaValid(false),
+ bTableAreaVisibleValid(false),
+ bVisible(true),
+ bPendingRowHeights(false),
+ bCalcNotification(false),
+ bGlobalKeepQuery(false),
+ bPrintEntireSheet(true),
+ bActiveScenario(false),
+ mbPageBreaksValid(false),
+ mbForceBreaks(false),
+ bStreamValid(false)
+{
+ aDefaultColData.InitAttrArray(new ScAttrArray(static_cast<SCCOL>(-1), nNewTab, rDoc, nullptr));
+ if (bColInfo)
+ {
+ mpColWidth.reset( new ScCompressedArray<SCCOL, sal_uInt16>( rDocument.MaxCol()+1, STD_COL_WIDTH ) );
+ mpColFlags.reset( new ScBitMaskCompressedArray<SCCOL, CRFlags>( rDocument.MaxCol()+1, CRFlags::NONE ) );
+ }
+
+ if (bRowInfo)
+ {
+ mpRowHeights.reset(new ScFlatUInt16RowSegments(rDocument.MaxRow(), GetOptimalMinRowHeight()));
+ pRowFlags.reset(new ScBitMaskCompressedArray<SCROW, CRFlags>( rDocument.MaxRow(), CRFlags::NONE));
+ }
+
+ if ( rDocument.IsDocVisible() )
+ {
+ // when a sheet is added to a visible document,
+ // initialize its RTL flag from the system locale
+ bLayoutRTL = ScGlobal::IsSystemRTL();
+ }
+
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if (pDrawLayer)
+ {
+ if ( pDrawLayer->ScAddPage( nTab ) ) // sal_False (not inserted) during Undo
+ {
+ pDrawLayer->ScRenamePage( nTab, aName );
+ sal_uLong const nx = o3tl::convert((rDocument.MaxCol()+1) * STD_COL_WIDTH, o3tl::Length::twip, o3tl::Length::mm100);
+ sal_uLong ny = o3tl::convert((rDocument.MaxRow() + 1) * GetOptimalMinRowHeight(),
+ o3tl::Length::twip, o3tl::Length::mm10);
+ pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( nx, ny ), false );
+ }
+ }
+
+ for (SCCOL k=0; k < aCol.size(); k++)
+ aCol[k].Init( k, nTab, rDocument, true );
+}
+
+ScTable::~ScTable() COVERITY_NOEXCEPT_FALSE
+{
+ if (!rDocument.IsInDtorClear())
+ {
+ for (SCCOL nCol = 0; nCol < aCol.size(); ++nCol)
+ {
+ aCol[nCol].FreeNotes();
+ }
+ // In the dtor, don't delete the pages in the wrong order.
+ // (or else nTab does not reflect the page number!)
+ // In ScDocument::Clear is afterwards used from Clear at the Draw Layer to delete everything.
+
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if (pDrawLayer)
+ pDrawLayer->ScRemovePage( nTab );
+ }
+
+ pRowFlags.reset();
+ pSheetEvents.reset();
+ pOutlineTable.reset();
+ pSearchText.reset();
+ moRepeatColRange.reset();
+ moRepeatRowRange.reset();
+ pScenarioRanges.reset();
+ mpRangeName.reset();
+ pDBDataNoName.reset();
+ DestroySortCollator();
+}
+
+sal_Int64 ScTable::GetHashCode() const
+{
+ return sal::static_int_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this));
+}
+
+void ScTable::SetName( const OUString& rNewName )
+{
+ aName = rNewName;
+ aUpperName.clear(); // invalidated if the name is changed
+
+ // SetStreamValid is handled in ScDocument::RenameTab
+}
+
+const OUString& ScTable::GetUpperName() const
+{
+ if (aUpperName.isEmpty() && !aName.isEmpty())
+ aUpperName = ScGlobal::getCharClass().uppercase(aName);
+ return aUpperName;
+}
+
+void ScTable::SetVisible( bool bVis )
+{
+ if (bVisible != bVis)
+ SetStreamValid(false);
+
+ bVisible = bVis;
+}
+
+void ScTable::SetStreamValid( bool bSet, bool bIgnoreLock )
+{
+ if (!bStreamValid && !bSet)
+ return; // shortcut
+ if ( bIgnoreLock || !rDocument.IsStreamValidLocked() )
+ bStreamValid = bSet;
+}
+
+void ScTable::SetPendingRowHeights( bool bSet )
+{
+ bPendingRowHeights = bSet;
+}
+
+void ScTable::SetLayoutRTL( bool bSet )
+{
+ bLayoutRTL = bSet;
+}
+
+void ScTable::SetLoadingRTL( bool bSet )
+{
+ bLoadingRTL = bSet;
+}
+
+void ScTable::SetTabBgColor(const Color& rColor)
+{
+ if (aTabBgColor != rColor)
+ {
+ // The tab color has changed. Set this table 'modified'.
+ aTabBgColor = rColor;
+ SetStreamValid(false);
+ }
+}
+
+void ScTable::SetScenario( bool bFlag )
+{
+ bScenario = bFlag;
+}
+
+void ScTable::SetLink( ScLinkMode nMode,
+ const OUString& rDoc, const OUString& rFlt, const OUString& rOpt,
+ const OUString& rTab, sal_uLong nRefreshDelay )
+{
+ nLinkMode = nMode;
+ aLinkDoc = rDoc; // File
+ aLinkFlt = rFlt; // Filter
+ aLinkOpt = rOpt; // Filter options
+ aLinkTab = rTab; // Sheet name in source file
+ nLinkRefreshDelay = nRefreshDelay; // refresh delay in seconds, 0==off
+
+ SetStreamValid(false);
+}
+
+sal_uInt16 ScTable::GetOptimalColWidth( SCCOL nCol, OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bFormula, const ScMarkData* pMarkData,
+ const ScColWidthParam* pParam )
+{
+ if ( nCol >= aCol.size() )
+ return ( STD_COL_WIDTH - STD_EXTRA_WIDTH );
+
+ return aCol[nCol].GetOptimalColWidth( pDev, nPPTX, nPPTY, rZoomX, rZoomY,
+ bFormula, STD_COL_WIDTH - STD_EXTRA_WIDTH, pMarkData, pParam );
+}
+
+tools::Long ScTable::GetNeededSize( SCCOL nCol, SCROW nRow,
+ OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bWidth, bool bTotalSize, bool bInPrintTwips )
+{
+ if ( nCol >= aCol.size() )
+ return 0;
+
+ ScNeededSizeOptions aOptions;
+ aOptions.bSkipMerged = false; // count merged cells
+ aOptions.bTotalSize = bTotalSize;
+
+ return aCol[nCol].GetNeededSize
+ ( nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, aOptions, nullptr, bInPrintTwips );
+}
+
+bool ScTable::SetOptimalHeight(
+ sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, bool bApi,
+ ScProgress* pOuterProgress, sal_uInt64 nProgressStart )
+{
+ assert(nStartRow <= nEndRow);
+
+ OSL_ENSURE( rCxt.getExtraHeight() == 0 || rCxt.isForceAutoSize(),
+ "automatic OptimalHeight with Extra" );
+
+ if ( rDocument.IsAdjustHeightLocked() )
+ {
+ return false;
+ }
+
+ SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
+
+ ScProgress* pProgress = GetProgressBar(nCount, GetWeightedCount(), pOuterProgress, &rDocument);
+
+ mpRowHeights->enableTreeSearch(false);
+
+ GetOptimalHeightsInColumn(rCxt, aCol, nStartRow, nEndRow, pProgress, nProgressStart);
+
+ SetRowHeightRangeFunc aFunc(this, rCxt.getPPTY());
+ bool bChanged = SetOptimalHeightsToRows(rCxt, aFunc, pRowFlags.get(), nStartRow, nEndRow, bApi);
+
+ if ( pProgress != pOuterProgress )
+ delete pProgress;
+
+ mpRowHeights->enableTreeSearch(true);
+
+ return bChanged;
+}
+
+void ScTable::SetOptimalHeightOnly(
+ sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow,
+ ScProgress* pOuterProgress, sal_uInt64 nProgressStart )
+{
+ OSL_ENSURE( rCxt.getExtraHeight() == 0 || rCxt.isForceAutoSize(),
+ "automatic OptimalHeight with Extra" );
+
+ if ( rDocument.IsAdjustHeightLocked() )
+ return;
+
+ SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
+
+ ScProgress* pProgress = GetProgressBar(nCount, GetWeightedCount(), pOuterProgress, &rDocument);
+
+ GetOptimalHeightsInColumn(rCxt, aCol, nStartRow, nEndRow, pProgress, nProgressStart);
+
+ SetRowHeightOnlyFunc aFunc(this);
+
+ SetOptimalHeightsToRows(rCxt, aFunc, pRowFlags.get(), nStartRow, nEndRow, true);
+
+ if ( pProgress != pOuterProgress )
+ delete pProgress;
+}
+
+bool ScTable::GetCellArea( SCCOL& rEndCol, SCROW& rEndRow ) const
+{
+ bool bFound = false;
+ SCCOL nMaxX = 0;
+ SCROW nMaxY = 0;
+ for (SCCOL i=0; i<aCol.size(); i++)
+ {
+ if (!aCol[i].IsEmptyData())
+ {
+ bFound = true;
+ nMaxX = i;
+ SCROW nRow = aCol[i].GetLastDataPos();
+ if (nRow > nMaxY)
+ nMaxY = nRow;
+ }
+ if ( aCol[i].HasCellNotes() )
+ {
+ SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow();
+ if (maxNoteRow >= nMaxY)
+ {
+ bFound = true;
+ nMaxY = maxNoteRow;
+ }
+ if (i>nMaxX)
+ {
+ bFound = true;
+ nMaxX = i;
+ }
+ }
+ if (aCol[i].HasSparklines())
+ {
+ SCROW maxSparklineRow = aCol[i].GetSparklinesMaxRow();
+ if (maxSparklineRow >= nMaxY)
+ {
+ bFound = true;
+ nMaxY = maxSparklineRow;
+ }
+ if (i > nMaxX)
+ {
+ bFound = true;
+ nMaxX = i;
+ }
+ }
+ }
+
+ rEndCol = nMaxX;
+ rEndRow = nMaxY;
+ return bFound;
+}
+
+bool ScTable::GetTableArea( SCCOL& rEndCol, SCROW& rEndRow, bool bCalcHiddens) const
+{
+ bool bRet = true; //TODO: remember?
+ if (bCalcHiddens)
+ {
+ if (!bTableAreaValid)
+ {
+ bRet = GetPrintArea(nTableAreaX, nTableAreaY, true, bCalcHiddens);
+ bTableAreaValid = true;
+ }
+ rEndCol = nTableAreaX;
+ rEndRow = nTableAreaY;
+ }
+ else
+ {
+ if (!bTableAreaVisibleValid)
+ {
+ bRet = GetPrintArea(nTableAreaVisibleX, nTableAreaVisibleY, true, bCalcHiddens);
+ bTableAreaVisibleValid = true;
+ }
+ rEndCol = nTableAreaVisibleX;
+ rEndRow = nTableAreaVisibleY;
+ }
+ return bRet;
+}
+
+const SCCOL SC_COLUMNS_STOP = 30;
+
+bool ScTable::GetPrintArea( SCCOL& rEndCol, SCROW& rEndRow, bool bNotes, bool bCalcHiddens ) const
+{
+ bool bFound = false;
+ SCCOL nMaxX = 0;
+ SCROW nMaxY = 0;
+ SCCOL i;
+
+ bool bSkipEmpty = SC_MOD()->GetPrintOptions().GetSkipEmpty();
+
+ for (i=0; i<aCol.size(); i++) // Test data
+ {
+ if (bCalcHiddens || !rDocument.ColHidden(i, nTab))
+ {
+ if (!aCol[i].IsEmptyData())
+ {
+ bFound = true;
+ if (i>nMaxX)
+ nMaxX = i;
+ SCROW nColY = aCol[i].GetLastDataPos();
+ if (nColY > nMaxY)
+ nMaxY = nColY;
+ }
+ if (bNotes && aCol[i].HasCellNotes() )
+ {
+ SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow();
+ if (maxNoteRow >= nMaxY)
+ {
+ bFound = true;
+ nMaxY = maxNoteRow;
+ }
+ if (i>nMaxX)
+ {
+ bFound = true;
+ nMaxX = i;
+ }
+ }
+ if (aCol[i].HasSparklines())
+ {
+ SCROW maxSparklineRow = aCol[i].GetSparklinesMaxRow();
+ if (maxSparklineRow >= nMaxY)
+ {
+ bFound = true;
+ nMaxY = maxSparklineRow;
+ }
+ if (i > nMaxX)
+ {
+ bFound = true;
+ nMaxX = i;
+ }
+ }
+ }
+ }
+
+ SCCOL nMaxDataX = nMaxX;
+
+ for (i=0; i<aCol.size(); i++) // Test attribute
+ {
+ if (bCalcHiddens || !rDocument.ColHidden(i, nTab))
+ {
+ SCROW nLastRow;
+ if (aCol[i].GetLastVisibleAttr( nLastRow, bSkipEmpty ))
+ {
+ bFound = true;
+ nMaxX = i;
+ if (nLastRow > nMaxY)
+ nMaxY = nLastRow;
+ }
+ }
+ }
+
+ if (nMaxX == rDocument.MaxCol()) // omit attribute at the right
+ {
+ --nMaxX;
+ while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], 0, rDocument.MaxRow()) )
+ --nMaxX;
+ }
+
+ if ( nMaxX < nMaxDataX )
+ {
+ nMaxX = nMaxDataX;
+ }
+ else if ( nMaxX > nMaxDataX )
+ {
+ SCCOL nAttrStartX = nMaxDataX + 1;
+ while ( nAttrStartX < (aCol.size()-1) )
+ {
+ SCCOL nAttrEndX = nAttrStartX;
+ while ( nAttrEndX < (aCol.size()-1) && aCol[nAttrStartX].IsVisibleAttrEqual(aCol[nAttrEndX+1], 0, rDocument.MaxRow()) )
+ ++nAttrEndX;
+ if ( nAttrEndX + 1 - nAttrStartX >= SC_COLUMNS_STOP )
+ {
+ // found equally-formatted columns behind data -> stop before these columns
+ nMaxX = nAttrStartX - 1;
+
+ // also don't include default-formatted columns before that
+ SCROW nDummyRow;
+ while ( nMaxX > nMaxDataX && !aCol[nMaxX].GetLastVisibleAttr( nDummyRow, bSkipEmpty ) )
+ --nMaxX;
+ break;
+ }
+ nAttrStartX = nAttrEndX + 1;
+ }
+ }
+
+ rEndCol = nMaxX;
+ rEndRow = nMaxY;
+ return bFound;
+}
+
+bool ScTable::GetPrintAreaHor( SCROW nStartRow, SCROW nEndRow,
+ SCCOL& rEndCol ) const
+{
+ bool bFound = false;
+ SCCOL nMaxX = 0;
+ SCCOL i;
+
+ for (i=0; i<aCol.size(); i++) // Test attribute
+ {
+ if (aCol[i].HasVisibleAttrIn( nStartRow, nEndRow ))
+ {
+ bFound = true;
+ nMaxX = i;
+ }
+ }
+
+ if (nMaxX == rDocument.MaxCol()) // omit attribute at the right
+ {
+ --nMaxX;
+ while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], nStartRow, nEndRow) )
+ --nMaxX;
+ }
+
+ for (i=0; i<aCol.size(); i++) // test the data
+ {
+ if (!aCol[i].IsEmptyData( nStartRow, nEndRow )) //TODO: bNotes ??????
+ {
+ bFound = true;
+ if (i > nMaxX)
+ nMaxX = i;
+ }
+ else if (aCol[i].HasSparklines())
+ {
+ if (i > nMaxX)
+ {
+ bFound = true;
+ nMaxX = i;
+ }
+ }
+ }
+
+ rEndCol = nMaxX;
+ return bFound;
+}
+
+bool ScTable::GetPrintAreaVer( SCCOL nStartCol, SCCOL nEndCol,
+ SCROW& rEndRow, bool bNotes ) const
+{
+ bool bFound = false;
+ SCROW nMaxY = 0;
+ SCCOL i;
+
+ bool bSkipEmpty = SC_MOD()->GetPrintOptions().GetSkipEmpty();
+
+ for (i=nStartCol; i<=nEndCol && i < aCol.size(); i++) // Test attribute
+ {
+ SCROW nLastRow;
+ if (aCol[i].GetLastVisibleAttr( nLastRow, bSkipEmpty ))
+ {
+ bFound = true;
+ if (nLastRow > nMaxY)
+ nMaxY = nLastRow;
+ }
+ }
+
+ for (i=nStartCol; i<=nEndCol && i < aCol.size(); i++) // Test data
+ {
+ if (!aCol[i].IsEmptyData())
+ {
+ bFound = true;
+ SCROW nColY = aCol[i].GetLastDataPos();
+ if (nColY > nMaxY)
+ nMaxY = nColY;
+ }
+ if (bNotes && aCol[i].HasCellNotes() )
+ {
+ SCROW maxNoteRow =aCol[i].GetCellNotesMaxRow();
+ if (maxNoteRow > nMaxY)
+ {
+ bFound = true;
+ nMaxY = maxNoteRow;
+ }
+ }
+ if (aCol[i].HasSparklines())
+ {
+ SCROW maxNoteRow = aCol[i].GetSparklinesMaxRow();
+ if (maxNoteRow > nMaxY)
+ {
+ bFound = true;
+ nMaxY = maxNoteRow;
+ }
+ }
+ }
+
+ rEndRow = nMaxY;
+ return bFound;
+}
+
+bool ScTable::GetDataStart( SCCOL& rStartCol, SCROW& rStartRow ) const
+{
+ bool bFound = false;
+ SCCOL nMinX = aCol.size()-1;
+ SCROW nMinY = rDocument.MaxRow();
+ SCCOL i;
+
+ for (i=0; i<aCol.size(); i++) // Test attribute
+ {
+ SCROW nFirstRow;
+ if (aCol[i].GetFirstVisibleAttr( nFirstRow ))
+ {
+ if (!bFound)
+ nMinX = i;
+ bFound = true;
+ if (nFirstRow < nMinY)
+ nMinY = nFirstRow;
+ }
+ }
+
+ if (nMinX == 0) // omit attribute at the right
+ {
+ if ( aCol.size() > 1 && aCol[0].IsVisibleAttrEqual(aCol[1], 0, rDocument.MaxRow())) // no single ones
+ {
+ ++nMinX;
+ while ( nMinX<(aCol.size()-1) && aCol[nMinX].IsVisibleAttrEqual(aCol[nMinX-1], 0, rDocument.MaxRow()))
+ ++nMinX;
+ }
+ }
+
+ bool bDatFound = false;
+ for (i=0; i<aCol.size(); i++) // Test data
+ {
+ if (!aCol[i].IsEmptyData())
+ {
+ if (!bDatFound && i<nMinX)
+ nMinX = i;
+ bFound = bDatFound = true;
+ SCROW nRow = aCol[i].GetFirstDataPos();
+ if (nRow < nMinY)
+ nMinY = nRow;
+ }
+ if ( aCol[i].HasCellNotes() )
+ {
+ SCROW minNoteRow = aCol[i].GetCellNotesMinRow();
+ if (minNoteRow <= nMinY)
+ {
+ bFound = true;
+ nMinY = minNoteRow;
+ }
+ if (i<nMinX)
+ {
+ bFound = true;
+ nMinX = i;
+ }
+ }
+ if (aCol[i].HasSparklines())
+ {
+ SCROW minSparkline = aCol[i].GetSparklinesMinRow();
+ if (minSparkline <= nMinY)
+ {
+ bFound = true;
+ nMinY = minSparkline;
+ }
+ if (i < nMinX)
+ {
+ bFound = true;
+ nMinX = i;
+ }
+ }
+ }
+ rStartCol = nMinX;
+ rStartRow = nMinY;
+ return bFound;
+}
+
+void ScTable::GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow,
+ bool bIncludeOld, bool bOnlyDown ) const
+{
+ // return the smallest area containing at least all contiguous cells having data. This area
+ // is a square containing also empty cells. It may shrink or extend the area given as input
+ // Flags as modifiers:
+ //
+ // bIncludeOld = true ensure that the returned area contains at least the initial area,
+ // independently of the emptiness of rows / columns (i.e. does not allow shrinking)
+ // bOnlyDown = true means extend / shrink the inputted area only down, i.e modify only rEndRow
+
+ rStartCol = std::min<SCCOL>( rStartCol, aCol.size()-1 );
+ rEndCol = std::min<SCCOL>( rEndCol, aCol.size()-1 );
+
+ bool bLeft = false;
+ bool bRight = false;
+ bool bTop = false;
+ bool bBottom = false;
+ bool bChanged = false;
+
+ // We need to cache sc::ColumnBlockConstPosition per each column.
+ std::vector< sc::ColumnBlockConstPosition > blockPos( rEndCol + 1 );
+ for( SCCOL i = 0; i <= rEndCol; ++i )
+ aCol[ i ].InitBlockPosition( blockPos[ i ] );
+
+ do
+ {
+ bChanged = false;
+
+ if (!bOnlyDown)
+ {
+ SCROW nStart = rStartRow;
+ SCROW nEnd = rEndRow;
+ if (nStart>0) --nStart;
+ if (nEnd<rDocument.MaxRow()) ++nEnd;
+
+ if (rEndCol < (aCol.size()-1))
+ if (!aCol[rEndCol+1].IsEmptyData(nStart,nEnd))
+ {
+ assert( int( blockPos.size()) == rEndCol + 1 );
+ ++rEndCol;
+ blockPos.resize( blockPos.size() + 1 );
+ aCol[ rEndCol ].InitBlockPosition( blockPos[ rEndCol ] );
+ bChanged = true;
+ bRight = true;
+ }
+
+ if (rStartCol > 0)
+ if (!aCol[rStartCol-1].IsEmptyData(nStart,nEnd))
+ {
+ --rStartCol;
+ bChanged = true;
+ bLeft = true;
+ }
+
+ if (rStartRow > 0)
+ {
+ SCROW nTest = rStartRow-1;
+ bool needExtend = false;
+ for ( SCCOL i = rStartCol; i<=rEndCol && !needExtend; i++)
+ if (aCol[i].HasDataAt(blockPos[i], nTest))
+ needExtend = true;
+ if (needExtend)
+ {
+ --rStartRow;
+ bChanged = true;
+ bTop = true;
+ }
+ }
+ }
+
+ if (rEndRow < rDocument.MaxRow())
+ {
+ SCROW nTest = rEndRow+1;
+ bool needExtend = false;
+ for ( SCCOL i = rStartCol; i<=rEndCol && !needExtend; i++)
+ if (aCol[i].HasDataAt(blockPos[ i ], nTest))
+ needExtend = true;
+ if (needExtend)
+ {
+ ++rEndRow;
+ bChanged = true;
+ bBottom = true;
+ }
+ }
+ }
+ while( bChanged );
+
+ if ( !bIncludeOld && !bOnlyDown )
+ {
+ if ( !bLeft )
+ while ( rStartCol < rEndCol && rStartCol < (aCol.size()-1) && aCol[rStartCol].IsEmptyData(rStartRow,rEndRow) )
+ ++rStartCol;
+
+ if ( !bRight )
+ while ( rEndCol > 0 && rStartCol < rEndCol && aCol[rEndCol].IsEmptyData(rStartRow,rEndRow) )
+ --rEndCol;
+
+ if ( !bTop && rStartRow < rDocument.MaxRow() && rStartRow < rEndRow )
+ {
+ bool bShrink = true;
+ do
+ {
+ for ( SCCOL i = rStartCol; i<=rEndCol && bShrink; i++)
+ if (aCol[i].HasDataAt(rStartRow))
+ bShrink = false;
+ if (bShrink)
+ ++rStartRow;
+ } while (bShrink && rStartRow < rDocument.MaxRow() && rStartRow < rEndRow);
+ }
+ }
+
+ if ( !bIncludeOld )
+ {
+ if ( !bBottom && rEndRow > 0 && rStartRow < rEndRow )
+ {
+ SCROW nLastDataRow = GetLastDataRow( rStartCol, rEndCol, rEndRow);
+ if (nLastDataRow < rEndRow)
+ rEndRow = std::max( rStartRow, nLastDataRow);
+ }
+ }
+}
+
+bool ScTable::GetDataAreaSubrange( ScRange& rRange ) const
+{
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+
+ if ( nCol1 >= aCol.size() )
+ return false;
+
+ nCol2 = std::min<SCCOL>( nCol2, aCol.size()-1 );
+
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+
+ SCCOL nFirstNonEmptyCol = -1, nLastNonEmptyCol = -1;
+ SCROW nRowStart = nRow2, nRowEnd = nRow1;
+
+ for ( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
+ {
+ SCROW nRowStartThis = nRow1, nRowEndThis = nRow2;
+ bool bTrimmed = aCol[nCol].TrimEmptyBlocks(nRowStartThis, nRowEndThis);
+ if ( bTrimmed )
+ {
+ if ( nFirstNonEmptyCol == -1 )
+ nFirstNonEmptyCol = nCol;
+ nLastNonEmptyCol = nCol;
+
+ nRowStart = std::min<SCROW>(nRowStart, nRowStartThis);
+ nRowEnd = std::max<SCROW>(nRowEnd, nRowEndThis);
+ }
+ }
+
+ if ( nFirstNonEmptyCol == -1 )
+ return false;
+
+ assert(nFirstNonEmptyCol <= nLastNonEmptyCol);
+ assert(nRowStart <= nRowEnd);
+
+ rRange.aStart.Set(nFirstNonEmptyCol, nRowStart, rRange.aStart.Tab());
+ rRange.aEnd.Set(nLastNonEmptyCol, nRowEnd, rRange.aEnd.Tab());
+
+ return true;
+}
+
+bool ScTable::ShrinkToUsedDataArea( bool& o_bShrunk, SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly, bool bStickyTopRow, bool bStickyLeftCol,
+ ScDataAreaExtras* pDataAreaExtras ) const
+{
+ rStartCol = std::min<SCCOL>( rStartCol, aCol.size()-1 );
+ // check for rEndCol is done below.
+
+ o_bShrunk = false;
+
+ PutInOrder( rStartCol, rEndCol);
+ PutInOrder( rStartRow, rEndRow);
+ if (rStartCol < 0)
+ {
+ rStartCol = 0;
+ o_bShrunk = true;
+ }
+ if (rStartRow < 0)
+ {
+ rStartRow = 0;
+ o_bShrunk = true;
+ }
+ if (rEndCol >= aCol.size())
+ {
+ rEndCol = aCol.size()-1;
+ o_bShrunk = true;
+ }
+ if (rEndRow > rDocument.MaxRow())
+ {
+ rEndRow = rDocument.MaxRow();
+ o_bShrunk = true;
+ }
+
+ while (rStartCol < rEndCol)
+ {
+ if (aCol[rEndCol].IsEmptyData( rStartRow, rEndRow))
+ {
+ if (pDataAreaExtras && pDataAreaExtras->mnEndCol < rEndCol)
+ {
+ // Check in order of likeliness.
+ if ( (pDataAreaExtras->mbCellFormats
+ && aCol[rEndCol].GetPatternCount( rStartRow, rEndRow) > 1
+ && aCol[rEndCol].HasVisibleAttrIn( rStartRow, rEndRow)) ||
+ (pDataAreaExtras->mbCellNotes
+ && !aCol[rEndCol].IsNotesEmptyBlock( rStartRow, rEndRow)) ||
+ (pDataAreaExtras->mbCellDrawObjects
+ && !aCol[rEndCol].IsDrawObjectsEmptyBlock( rStartRow, rEndRow)))
+ pDataAreaExtras->mnEndCol = rEndCol;
+ }
+
+ --rEndCol;
+ o_bShrunk = true;
+ }
+ else
+ break; // while
+ }
+
+ if (!bStickyLeftCol)
+ {
+ while (rStartCol < rEndCol)
+ {
+ if (aCol[rStartCol].IsEmptyData( rStartRow, rEndRow))
+ {
+ if (pDataAreaExtras && pDataAreaExtras->mnStartCol > rStartCol)
+ {
+ // Check in order of likeliness.
+ if ( (pDataAreaExtras->mbCellFormats
+ && aCol[rStartCol].GetPatternCount( rStartRow, rEndRow) > 1
+ && aCol[rStartCol].HasVisibleAttrIn( rStartRow, rEndRow)) ||
+ (pDataAreaExtras->mbCellNotes
+ && !aCol[rStartCol].IsNotesEmptyBlock( rStartRow, rEndRow)) ||
+ (pDataAreaExtras->mbCellDrawObjects
+ && !aCol[rStartCol].IsDrawObjectsEmptyBlock( rStartRow, rEndRow)))
+ pDataAreaExtras->mnStartCol = rStartCol;
+ }
+
+ ++rStartCol;
+ o_bShrunk = true;
+ }
+ else
+ break; // while
+ }
+ }
+
+ if (!bColumnsOnly)
+ {
+ while (rStartRow < rEndRow)
+ {
+ SCROW nLastDataRow = GetLastDataRow(rStartCol, rEndCol, rEndRow, pDataAreaExtras);
+ if (0 <= nLastDataRow && nLastDataRow < rEndRow)
+ {
+ rEndRow = std::max( rStartRow, nLastDataRow);
+ o_bShrunk = true;
+ }
+ else
+ break; // while
+ }
+
+ if (!bStickyTopRow)
+ {
+ while (rStartRow < rEndRow)
+ {
+ bool bFound = false;
+ for (SCCOL i=rStartCol; i<=rEndCol && !bFound; i++)
+ {
+ if (aCol[i].HasDataAt(rStartRow, pDataAreaExtras))
+ bFound = true;
+ }
+ if (!bFound)
+ {
+ ++rStartRow;
+ o_bShrunk = true;
+ }
+ else
+ break; // while
+ }
+ }
+ }
+
+ return rStartCol != rEndCol || (bColumnsOnly ?
+ !aCol[rStartCol].IsEmptyData( rStartRow, rEndRow) :
+ (rStartRow != rEndRow ||
+ aCol[rStartCol].HasDataAt( rStartRow, pDataAreaExtras)));
+}
+
+SCROW ScTable::GetLastDataRow( SCCOL nCol1, SCCOL nCol2, SCROW nLastRow, ScDataAreaExtras* pDataAreaExtras ) const
+{
+ if ( !IsColValid( nCol1 ) || !ValidCol( nCol2 ) )
+ return -1;
+
+ nCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
+
+ SCROW nNewLastRow = 0;
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ {
+ SCROW nThis = aCol[i].GetLastDataPos(nLastRow, pDataAreaExtras);
+ if (nNewLastRow < nThis)
+ nNewLastRow = nThis;
+ }
+
+ return nNewLastRow;
+}
+
+bool ScTable::IsEmptyData( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow ) const
+{
+ for( SCCOL col : GetAllocatedColumnsRange( nStartCol, nEndCol ))
+ if( !aCol[col].IsEmptyData( nStartRow, nEndRow ))
+ return false;
+ return true;
+}
+
+SCSIZE ScTable::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, ScDirection eDir ) const
+{
+ SCCOL nStartColOrig = nStartCol;
+ SCCOL nEndColOrig = nEndCol;
+ nStartCol = std::min<SCCOL>( nStartCol, aCol.size()-1 );
+ nEndCol = std::min<SCCOL>( nEndCol, aCol.size()-1 );
+
+ // The region is not allocated and does not contain any data.
+ if ( nStartColOrig != nStartCol )
+ return ( ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP)) ?
+ static_cast<SCSIZE>(nEndRow - nStartRow + 1) :
+ static_cast<SCSIZE>(nEndColOrig - nStartColOrig + 1) );
+
+ SCSIZE nGapRight = static_cast<SCSIZE>(nEndColOrig - nEndCol);
+ SCSIZE nCount = 0;
+ SCCOL nCol;
+ if ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP))
+ {
+ nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+ for (nCol = nStartCol; nCol <= nEndCol; nCol++)
+ nCount = std::min(nCount, aCol[nCol].GetEmptyLinesInBlock(nStartRow, nEndRow, eDir));
+ }
+ else if (eDir == DIR_RIGHT)
+ {
+ nCol = nEndCol;
+ while ((nCol >= nStartCol) &&
+ aCol[nCol].IsEmptyData(nStartRow, nEndRow))
+ {
+ nCount++;
+ nCol--;
+ }
+ nCount += nGapRight;
+ }
+ else
+ {
+ nCol = nStartCol;
+ while ((nCol <= nEndCol) && aCol[nCol].IsEmptyData(nStartRow, nEndRow))
+ {
+ nCount++;
+ nCol++;
+ }
+
+ // If the area between nStartCol and nEndCol are empty,
+ // add the count of unallocated columns on the right.
+ if ( nCol > nEndCol )
+ nCount += nGapRight;
+ }
+ return nCount;
+}
+
+bool ScTable::IsEmptyLine( SCROW nRow, SCCOL nStartCol, SCCOL nEndCol ) const
+{
+ // The range of columns are unallocated hence empty.
+ if ( nStartCol >= aCol.size() )
+ return true;
+
+ nEndCol = std::min<SCCOL>( nEndCol, aCol.size()-1 );
+
+ for (SCCOL i=nStartCol; i<=nEndCol; i++)
+ if (aCol[i].HasDataAt(nRow))
+ return false;
+ return true;
+}
+
+void ScTable::LimitChartArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow ) const
+{
+ rStartCol = std::min<SCCOL>( rStartCol, aCol.size()-1 );
+ rEndCol = std::min<SCCOL>( rEndCol, aCol.size()-1 );
+
+ while ( rStartCol<rEndCol && aCol[rStartCol].IsEmptyData(rStartRow,rEndRow) )
+ ++rStartCol;
+
+ while ( rStartCol<rEndCol && aCol[rEndCol].IsEmptyData(rStartRow,rEndRow) )
+ --rEndCol;
+
+ while ( rStartRow<rEndRow && IsEmptyLine(rStartRow, rStartCol, rEndCol) )
+ ++rStartRow;
+
+ // Optimised loop for finding the bottom of the area, can be costly in large
+ // spreadsheets.
+ SCROW lastDataPos = 0;
+ for (SCCOL i=rStartCol; i<=rEndCol; i++)
+ lastDataPos = std::max(lastDataPos, aCol[i].GetLastDataPos());
+ // reduce EndRow to the last row with data
+ rEndRow = std::min(rEndRow, lastDataPos);
+ // but make sure EndRow is >= StartRow
+ rEndRow = std::max(rStartRow, rEndRow);
+}
+
+SCCOL ScTable::FindNextVisibleCol( SCCOL nCol, bool bRight ) const
+{
+ if(bRight)
+ {
+ nCol++;
+ SCCOL nEnd = 0;
+ bool bHidden = rDocument.ColHidden(nCol, nTab, nullptr, &nEnd);
+ if(bHidden)
+ nCol = nEnd +1;
+
+ return std::min<SCCOL>(rDocument.MaxCol(), nCol);
+ }
+ else
+ {
+ nCol--;
+ SCCOL nStart = rDocument.MaxCol();
+ bool bHidden = rDocument.ColHidden(nCol, nTab, &nStart);
+ if(bHidden)
+ nCol = nStart - 1;
+
+ return std::max<SCCOL>(0, nCol);
+ }
+}
+
+SCCOL ScTable::FindNextVisibleColWithContent( SCCOL nCol, bool bRight, SCROW nRow ) const
+{
+ const SCCOL nLastCol = aCol.size() - 1;
+ if(bRight)
+ {
+ // If nCol is the last allocated column index, there won't be any content to its right.
+ // To maintain the original return behaviour, return rDocument.MaxCol().
+ if(nCol >= nLastCol)
+ return rDocument.MaxCol();
+
+ do
+ {
+ nCol++;
+ SCCOL nEndCol = 0;
+ bool bHidden = rDocument.ColHidden( nCol, nTab, nullptr, &nEndCol );
+ if(bHidden)
+ {
+ nCol = nEndCol +1;
+ // Can end search early as there is no data after nLastCol.
+ // For nCol == nLastCol, it may still have data so don't want to return rDocument.MaxCol().
+ if(nCol > nLastCol)
+ return rDocument.MaxCol();
+ }
+
+ if(aCol[nCol].HasVisibleDataAt(nRow))
+ return nCol;
+ }
+ while(nCol < nLastCol); // Stop search as soon as the last allocated column is searched.
+
+ return rDocument.MaxCol();
+ }
+ else
+ {
+ if(nCol == 0)
+ return 0;
+
+ // If nCol is in the unallocated range [nLastCol+1, rDocument.MaxCol()], then move it directly after nLastCol
+ // as there is no data in the unallocated range. This also makes the search faster and avoids
+ // the need for more range checks in the loop below.
+ if ( nCol > nLastCol )
+ nCol = nLastCol + 1;
+
+ do
+ {
+ nCol--;
+ SCCOL nStartCol = rDocument.MaxCol();
+ bool bHidden = rDocument.ColHidden( nCol, nTab, &nStartCol );
+ if(bHidden)
+ {
+ nCol = nStartCol -1;
+ if(nCol <= 0)
+ return 0;
+ }
+
+ if(aCol[nCol].HasVisibleDataAt(nRow))
+ return nCol;
+ }
+ while(nCol > 0);
+
+ return 0;
+ }
+}
+
+void ScTable::FindAreaPos( SCCOL& rCol, SCROW& rRow, ScMoveDirection eDirection ) const
+{
+ const SCCOL nLastCol = aCol.size() - 1;
+
+ if (eDirection == SC_MOVE_LEFT || eDirection == SC_MOVE_RIGHT)
+ {
+ SCCOL nNewCol = rCol;
+ bool bThere = ( nNewCol <= nLastCol ) && aCol[nNewCol].HasVisibleDataAt(rRow);
+ bool bRight = (eDirection == SC_MOVE_RIGHT);
+ if (bThere)
+ {
+ if(nNewCol >= rDocument.MaxCol() && eDirection == SC_MOVE_RIGHT)
+ return;
+ else if(nNewCol == 0 && eDirection == SC_MOVE_LEFT)
+ return;
+
+ SCCOL nNextCol = FindNextVisibleCol( nNewCol, bRight );
+
+ if( nNextCol <= nLastCol && aCol[nNextCol].HasVisibleDataAt(rRow) )
+ {
+ bool bFound = false;
+ nNewCol = nNextCol;
+ do
+ {
+ nNextCol = FindNextVisibleCol( nNewCol, bRight );
+ if( nNextCol <= nLastCol && aCol[nNextCol].HasVisibleDataAt(rRow) )
+ nNewCol = nNextCol;
+ else
+ bFound = true;
+ }
+ while(!bFound && nNextCol > 0 && nNextCol < rDocument.MaxCol());
+ }
+ else
+ {
+ nNewCol = FindNextVisibleColWithContent(nNewCol, bRight, rRow);
+ }
+ }
+ else
+ {
+ nNewCol = FindNextVisibleColWithContent(nNewCol, bRight, rRow);
+ }
+
+ if (nNewCol<0)
+ nNewCol=0;
+ if (nNewCol>rDocument.MaxCol())
+ nNewCol=rDocument.MaxCol();
+ rCol = nNewCol;
+ }
+ else
+ {
+ if ( rCol <= nLastCol )
+ aCol[rCol].FindDataAreaPos(rRow,eDirection == SC_MOVE_DOWN);
+ else
+ {
+ // The cell (rCol, rRow) is equivalent to an empty cell (although not allocated).
+ // Set rRow to 0 or rDocument.MaxRow() depending on eDirection to maintain the behaviour of
+ // ScColumn::FindDataAreaPos() when the given column is empty.
+ rRow = ( eDirection == SC_MOVE_DOWN ) ? rDocument.MaxRow() : 0;
+ }
+ }
+}
+
+bool ScTable::ValidNextPos( SCCOL nCol, SCROW nRow, const ScMarkData& rMark,
+ bool bMarked, bool bUnprotected ) const
+{
+ if (!ValidCol(nCol) || !ValidRow(nRow))
+ return false;
+
+ if (rDocument.HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Overlapped))
+ // Skip an overlapped cell.
+ return false;
+
+ if (bMarked && !rMark.IsCellMarked(nCol,nRow))
+ return false;
+
+ /* TODO: for cursor movement *only* this should even take the protection
+ * options (select locked, select unlocked) into account, see
+ * ScTabView::SkipCursorHorizontal() and ScTabView::SkipCursorVertical(). */
+ if (bUnprotected && rDocument.HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Protected))
+ return false;
+
+ if (bMarked || bUnprotected) //TODO: also in other case ???
+ {
+ // Hidden cells must be skipped, as the cursor would end up on the next cell
+ // even if it is protected or not marked.
+ //TODO: control per Extra-Parameter, only for Cursor movement ???
+
+ if (RowHidden(nRow))
+ return false;
+
+ if (ColHidden(nCol))
+ return false;
+ }
+
+ return true;
+}
+
+// Skips the current cell if it is Hidden, Overlapped or Protected and Sheet is Protected
+bool ScTable::SkipRow( const SCCOL nCol, SCROW& rRow, const SCROW nMovY,
+ const ScMarkData& rMark, const bool bUp, const SCROW nUsedY,
+ const bool bMarked, const bool bSheetProtected ) const
+{
+ if ( !ValidRow( rRow ))
+ return false;
+
+ if (bSheetProtected && rDocument.HasAttrib( nCol, rRow, nTab, nCol, rRow, nTab, HasAttrFlags::Protected))
+ {
+ if ( rRow > nUsedY )
+ rRow = (bUp ? nUsedY : rDocument.MaxRow() + nMovY);
+ else
+ rRow += nMovY;
+
+ if (bMarked)
+ rRow = rMark.GetNextMarked( nCol, rRow, bUp );
+
+ return true;
+ }
+ else
+ {
+ bool bRowHidden = RowHidden( rRow );
+ bool bOverlapped = rDocument.HasAttrib( nCol, rRow, nTab, nCol, rRow, nTab, HasAttrFlags::Overlapped );
+
+ if ( bRowHidden || bOverlapped )
+ {
+ rRow += nMovY;
+ if (bMarked)
+ rRow = rMark.GetNextMarked( nCol, rRow, bUp );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScTable::GetNextPos( SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY,
+ bool bMarked, bool bUnprotected, const ScMarkData& rMark, SCCOL nTabStartCol ) const
+{
+ // Ensure bMarked is set only if there is a mark.
+ assert( !bMarked || rMark.IsMarked() || rMark.IsMultiMarked());
+
+ const bool bSheetProtected = IsProtected();
+
+ if ( bUnprotected && !bSheetProtected ) // Is sheet really protected?
+ bUnprotected = false;
+
+ SCCOL nCol = rCol + nMovX;
+ SCROW nRow = rRow + nMovY;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ if (bMarked)
+ {
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ if (rMark.IsMarked())
+ aRange = rMark.GetMarkArea();
+ else if (rMark.IsMultiMarked())
+ aRange = rMark.GetMultiMarkArea();
+ else
+ {
+ // Covered by assert() above, but for NDEBUG build.
+ if (ValidColRow(nCol,nRow))
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return;
+ }
+ nStartCol = aRange.aStart.Col();
+ nStartRow = aRange.aStart.Row();
+ nEndCol = aRange.aEnd.Col();
+ nEndRow = aRange.aEnd.Row();
+ }
+ else if (bUnprotected)
+ {
+ nStartCol = 0;
+ nStartRow = 0;
+ nEndCol = rCol;
+ nEndRow = rRow;
+ rDocument.GetPrintArea( nTab, nEndCol, nEndRow, true );
+ // Add some cols/rows to the print area (which is "content or
+ // visually different from empty") to enable travelling through
+ // protected forms with empty cells and no visual indicator.
+ // 42 might be good enough and not too much...
+ nEndCol = std::min<SCCOL>( nEndCol+42, rDocument.MaxCol());
+ nEndRow = std::min<SCROW>( nEndRow+42, rDocument.MaxRow());
+ }
+ else
+ {
+ // Invalid values show up for instance for Tab, when nothing is
+ // selected and not protected (left / right edge), then leave values
+ // unchanged.
+ if (ValidColRow(nCol,nRow))
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+
+ // Caller ensures actually moving nMovY to jump to prev/next row's
+ // start col.
+ if (nTabStartCol != SC_TABSTART_NONE)
+ rCol = nTabStartCol;
+
+ return;
+ }
+
+ if ( nMovY && (bMarked || bUnprotected))
+ {
+ do
+ {
+ const bool bUp = (nMovY < 0);
+ const SCCOL nColAdd = (bUp ? -1 : 1);
+
+ if (bMarked)
+ nRow = rMark.GetNextMarked( nCol, nRow, bUp );
+
+ if (nTabStartCol != SC_TABSTART_NONE)
+ {
+ /* NOTE: If current rCol < nTabStartCol when going down, there
+ * is no way to detect if the previous Tab wrapped around to
+ * the next row or if it was a Shift+Tab going backwards. The
+ * result after a wrap is an odd jump to the next row's
+ * nTabStartCol, which is logical though and always has been
+ * the case. Similar for rCol > nTabStartCol when going up.
+ * Related, it would be nice to limit advancing the position
+ * within bounds even if another wrap would occur, but again we
+ * can't tell if previously Tab or Shift+Tab was used, so we
+ * don't know if it would be nTabStartCol to nEndCol (for Tab)
+ * or nStartCol to nTabStartCol (for Shift+Tab). */
+
+ // Continue moving horizontally.
+ nMovX = nColAdd;
+ nCol = nTabStartCol;
+ break; // do
+ }
+
+ while ( SkipRow( nCol, nRow, nMovY, rMark, bUp, nEndRow, bMarked, bSheetProtected ))
+ ;
+
+ sal_uInt16 nWrap = 0;
+ while ( nRow < nStartRow || nRow > nEndRow )
+ {
+ nCol += nColAdd;
+
+ while (nStartCol <= nCol && nCol <= nEndCol && ValidCol(nCol) && ColHidden(nCol))
+ nCol += nColAdd; // skip hidden cols
+
+ if (nCol < nStartCol)
+ {
+ nCol = nEndCol;
+
+ if (++nWrap >= 2)
+ return;
+ }
+ else if (nCol > nEndCol)
+ {
+ nCol = nStartCol;
+
+ if (++nWrap >= 2)
+ return;
+ }
+ if (nRow < nStartRow)
+ nRow = nEndRow;
+ else if (nRow > nEndRow)
+ nRow = nStartRow;
+
+ if (bMarked)
+ nRow = rMark.GetNextMarked( nCol, nRow, bUp );
+
+ while ( SkipRow( nCol, nRow, nMovY, rMark, bUp, nEndRow, bMarked, bSheetProtected ))
+ ;
+ }
+ } while (false);
+ }
+
+ if ( nMovX && ( bMarked || bUnprotected ) )
+ {
+ // wrap initial skip counting:
+ if (nCol < nStartCol)
+ {
+ nCol = nEndCol;
+ --nRow;
+ if (nRow < nStartRow)
+ nRow = nEndRow;
+ }
+ if (nCol > nEndCol)
+ {
+ nCol = nStartCol;
+ ++nRow;
+ if (nRow > nEndRow)
+ nRow = nStartRow;
+ }
+
+ if ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) )
+ {
+ const SCCOL nColCount = nEndCol - nStartCol + 1;
+ std::unique_ptr<SCROW[]> pNextRows( new SCROW[nColCount]);
+ const SCCOL nLastCol = aCol.size() - 1;
+ const bool bUp = (nMovX < 0); // Moving left also means moving up in rows.
+ const SCROW nRowAdd = (bUp ? -1 : 1);
+ sal_uInt16 nWrap = 0;
+
+ if (bUp)
+ {
+ for (SCCOL i = 0; i < nColCount; ++i)
+ pNextRows[i] = (i + nStartCol > nCol) ? (nRow + nRowAdd) : nRow;
+ }
+ else
+ {
+ for (SCCOL i = 0; i < nColCount; ++i)
+ pNextRows[i] = (i + nStartCol < nCol) ? (nRow + nRowAdd) : nRow;
+ }
+ do
+ {
+ SCROW nNextRow = pNextRows[nCol - nStartCol] + nRowAdd;
+ if ( bMarked )
+ nNextRow = rMark.GetNextMarked( nCol, nNextRow, bUp );
+ if ( bUnprotected )
+ nNextRow = ( nCol <= nLastCol ) ? aCol[nCol].GetNextUnprotected( nNextRow, bUp ) :
+ aDefaultColData.GetNextUnprotected( nNextRow, bUp );
+ pNextRows[nCol - nStartCol] = nNextRow;
+
+ if (bUp)
+ {
+ SCROW nMaxRow = nStartRow - 1;
+ for (SCCOL i = 0; i < nColCount; ++i)
+ {
+ if (pNextRows[i] >= nMaxRow) // when two equal the right one
+ {
+ nMaxRow = pNextRows[i];
+ nCol = i + nStartCol;
+ }
+ }
+ nRow = nMaxRow;
+
+ if ( nRow < nStartRow )
+ {
+ if (++nWrap >= 2)
+ return;
+ nCol = nEndCol;
+ nRow = nEndRow;
+ for (SCCOL i = 0; i < nColCount; ++i)
+ pNextRows[i] = nEndRow; // do it all over again
+ }
+ }
+ else
+ {
+ SCROW nMinRow = nEndRow + 1;
+ for (SCCOL i = 0; i < nColCount; ++i)
+ {
+ if (pNextRows[i] < nMinRow) // when two equal the left one
+ {
+ nMinRow = pNextRows[i];
+ nCol = i + nStartCol;
+ }
+ }
+ nRow = nMinRow;
+
+ if ( nRow > nEndRow )
+ {
+ if (++nWrap >= 2)
+ return;
+ nCol = nStartCol;
+ nRow = nStartRow;
+ for (SCCOL i = 0; i < nColCount; ++i)
+ pNextRows[i] = nStartRow; // do it all over again
+ }
+ }
+ }
+ while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) );
+ }
+ }
+
+ if (ValidColRow(nCol,nRow))
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+}
+
+bool ScTable::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark ) const
+{
+ ++rRow; // next row
+
+ while ( rCol < aCol.size() )
+ {
+ ScMarkArray aArray( rMark.GetMarkArray( rCol ) );
+ while ( rRow <= rDocument.MaxRow() )
+ {
+ SCROW nStart = aArray.GetNextMarked( rRow, false );
+ if ( nStart <= rDocument.MaxRow() )
+ {
+ SCROW nEnd = aArray.GetMarkEnd( nStart, false );
+
+ const sc::CellStoreType& rCells = aCol[rCol].maCells;
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nStart);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ SCROW nTestRow = nStart;
+ if (it->type == sc::element_type_empty)
+ {
+ // Skip the empty block.
+ nTestRow += it->size - aPos.second;
+ ++it;
+ if (it == rCells.end())
+ {
+ // No more block. Move on to the next column.
+ rRow = rDocument.MaxRow() + 1;
+ continue;
+ }
+ }
+
+ if (nTestRow <= nEnd)
+ {
+ // Cell found.
+ rRow = nTestRow;
+ return true;
+ }
+
+ rRow = nEnd + 1; // Search for next selected range
+ }
+ else
+ rRow = rDocument.MaxRow() + 1; // End of column
+ }
+ rRow = 0;
+ ++rCol; // test next column
+ }
+
+ // Though searched only the allocated columns, it is equivalent to a search till rDocument.MaxCol().
+ rCol = rDocument.MaxCol() + 1;
+ return false; // Through all columns
+}
+
+void ScTable::UpdateDrawRef( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ SCCOL nDx, SCROW nDy, SCTAB nDz, bool bUpdateNoteCaptionPos )
+{
+ if ( !(nTab >= nTab1 && nTab <= nTab2 && nDz == 0) ) // only within the table
+ return;
+
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if ( eUpdateRefMode != URM_COPY && pDrawLayer )
+ {
+ if ( eUpdateRefMode == URM_MOVE )
+ { // source range
+ nCol1 = sal::static_int_cast<SCCOL>( nCol1 - nDx );
+ nRow1 = sal::static_int_cast<SCROW>( nRow1 - nDy );
+ nCol2 = sal::static_int_cast<SCCOL>( nCol2 - nDx );
+ nRow2 = sal::static_int_cast<SCROW>( nRow2 - nDy );
+ }
+ pDrawLayer->MoveArea( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy,
+ (eUpdateRefMode == URM_INSDEL), bUpdateNoteCaptionPos );
+ }
+}
+
+void ScTable::UpdateReference(
+ sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, bool bIncludeDraw, bool bUpdateNoteCaptionPos )
+{
+ bool bUpdated = false;
+ UpdateRefMode eUpdateRefMode = rCxt.meMode;
+ SCCOL nDx = rCxt.mnColDelta;
+ SCROW nDy = rCxt.mnRowDelta;
+ SCTAB nDz = rCxt.mnTabDelta;
+ SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
+ SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
+ SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
+
+ // Named expressions need to be updated before formulas accessing them.
+ if (mpRangeName)
+ mpRangeName->UpdateReference(rCxt, nTab);
+
+ if (rCxt.meMode == URM_COPY )
+ {
+ for( SCCOL col : GetAllocatedColumnsRange( rCxt.maRange.aStart.Col(), rCxt.maRange.aEnd.Col()))
+ bUpdated |= aCol[col].UpdateReference(rCxt, pUndoDoc);
+ }
+ else
+ {
+ for (SCCOL col : GetAllocatedColumnsRange(0, rDocument.MaxCol()))
+ bUpdated |= aCol[col].UpdateReference(rCxt, pUndoDoc);
+ // When deleting row(s), delete same row from the default attribute
+ if (nDy < 0)
+ aDefaultColData.DeleteRow(nRow1+nDy, -nDy);
+ }
+
+ if ( bIncludeDraw )
+ UpdateDrawRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz, bUpdateNoteCaptionPos );
+
+ if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 ) // print ranges: only within the table
+ {
+ SCTAB nSTab = nTab;
+ SCTAB nETab = nTab;
+ SCCOL nSCol = 0;
+ SCROW nSRow = 0;
+ SCCOL nECol = 0;
+ SCROW nERow = 0;
+ bool bRecalcPages = false;
+
+ for ( auto& rPrintRange : aPrintRanges )
+ {
+ nSCol = rPrintRange.aStart.Col();
+ nSRow = rPrintRange.aStart.Row();
+ nECol = rPrintRange.aEnd.Col();
+ nERow = rPrintRange.aEnd.Row();
+
+ // do not try to modify sheet index of print range
+ if ( ScRefUpdate::Update( &rDocument, eUpdateRefMode,
+ nCol1,nRow1,nTab, nCol2,nRow2,nTab,
+ nDx,nDy,0,
+ nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
+ {
+ rPrintRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
+ bRecalcPages = true;
+ }
+ }
+
+ if ( moRepeatColRange )
+ {
+ nSCol = moRepeatColRange->aStart.Col();
+ nSRow = moRepeatColRange->aStart.Row();
+ nECol = moRepeatColRange->aEnd.Col();
+ nERow = moRepeatColRange->aEnd.Row();
+
+ // do not try to modify sheet index of repeat range
+ if ( ScRefUpdate::Update( &rDocument, eUpdateRefMode,
+ nCol1,nRow1,nTab, nCol2,nRow2,nTab,
+ nDx,nDy,0,
+ nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
+ {
+ *moRepeatColRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
+ bRecalcPages = true;
+ nRepeatStartX = nSCol; // for UpdatePageBreaks
+ nRepeatEndX = nECol;
+ }
+ }
+
+ if ( moRepeatRowRange )
+ {
+ nSCol = moRepeatRowRange->aStart.Col();
+ nSRow = moRepeatRowRange->aStart.Row();
+ nECol = moRepeatRowRange->aEnd.Col();
+ nERow = moRepeatRowRange->aEnd.Row();
+
+ // do not try to modify sheet index of repeat range
+ if ( ScRefUpdate::Update( &rDocument, eUpdateRefMode,
+ nCol1,nRow1,nTab, nCol2,nRow2,nTab,
+ nDx,nDy,0,
+ nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
+ {
+ *moRepeatRowRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
+ bRecalcPages = true;
+ nRepeatStartY = nSRow; // for UpdatePageBreaks
+ nRepeatEndY = nERow;
+ }
+ }
+
+ // updating print ranges is not necessary with multiple print ranges
+ if ( bRecalcPages && GetPrintRangeCount() <= 1 )
+ {
+ UpdatePageBreaks(nullptr);
+
+ rDocument.RepaintRange( ScRange(0,0,nTab,rDocument.MaxCol(),rDocument.MaxRow(),nTab) );
+ }
+ }
+
+ if (bUpdated)
+ SetStreamValid(false);
+
+ if(mpCondFormatList)
+ mpCondFormatList->UpdateReference(rCxt);
+
+ if (pTabProtection)
+ pTabProtection->updateReference( eUpdateRefMode, rDocument, rCxt.maRange, nDx, nDy, nDz);
+}
+
+void ScTable::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
+ ScDocument* pUndoDoc )
+{
+ for (auto const & rpCol : aCol)
+ rpCol->UpdateTranspose( rSource, rDest, pUndoDoc );
+}
+
+void ScTable::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ for (auto const & rpCol : aCol)
+ rpCol->UpdateGrow( rArea, nGrowX, nGrowY );
+}
+
+void ScTable::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ // Store the old tab number in sc::UpdatedRangeNames for
+ // ScTokenArray::AdjustReferenceOnInsertedTab() to check with
+ // isNameModified()
+ if (mpRangeName)
+ mpRangeName->UpdateInsertTab(rCxt, nTab);
+
+ if (nTab >= rCxt.mnInsertPos)
+ {
+ nTab += rCxt.mnSheets;
+ if (pDBDataNoName)
+ pDBDataNoName->UpdateMoveTab(nTab - 1 ,nTab);
+ }
+
+ if (mpCondFormatList)
+ mpCondFormatList->UpdateInsertTab(rCxt);
+
+ if (pTabProtection)
+ pTabProtection->updateReference( URM_INSDEL, rDocument,
+ ScRange( 0, 0, rCxt.mnInsertPos, rDocument.MaxCol(), rDocument.MaxRow(), MAXTAB),
+ 0, 0, rCxt.mnSheets);
+
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].UpdateInsertTab(rCxt);
+
+ SetStreamValid(false);
+}
+
+void ScTable::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ // Store the old tab number in sc::UpdatedRangeNames for
+ // ScTokenArray::AdjustReferenceOnDeletedTab() to check with
+ // isNameModified()
+ if (mpRangeName)
+ mpRangeName->UpdateDeleteTab(rCxt, nTab);
+
+ if (nTab > rCxt.mnDeletePos)
+ {
+ nTab -= rCxt.mnSheets;
+ if (pDBDataNoName)
+ pDBDataNoName->UpdateMoveTab(nTab + 1,nTab);
+ }
+
+ if (mpCondFormatList)
+ mpCondFormatList->UpdateDeleteTab(rCxt);
+
+ if (pTabProtection)
+ pTabProtection->updateReference( URM_INSDEL, rDocument,
+ ScRange( 0, 0, rCxt.mnDeletePos, rDocument.MaxCol(), rDocument.MaxRow(), MAXTAB),
+ 0, 0, -rCxt.mnSheets);
+
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].UpdateDeleteTab(rCxt);
+
+ SetStreamValid(false);
+}
+
+void ScTable::UpdateMoveTab(
+ sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo, ScProgress* pProgress )
+{
+ nTab = nTabNo;
+ if (mpRangeName)
+ mpRangeName->UpdateMoveTab(rCxt, nTab);
+
+ if (pDBDataNoName)
+ pDBDataNoName->UpdateMoveTab(rCxt.mnOldPos, rCxt.mnNewPos);
+
+ if(mpCondFormatList)
+ mpCondFormatList->UpdateMoveTab(rCxt);
+
+ if (pTabProtection)
+ pTabProtection->updateReference( URM_REORDER, rDocument,
+ ScRange( 0, 0, rCxt.mnOldPos, rDocument.MaxCol(), rDocument.MaxRow(), MAXTAB),
+ 0, 0, rCxt.mnNewPos - rCxt.mnOldPos);
+
+ for ( SCCOL i=0; i < aCol.size(); i++ )
+ {
+ aCol[i].UpdateMoveTab(rCxt, nTabNo);
+ if (pProgress)
+ pProgress->SetState(pProgress->GetState() + aCol[i].GetCodeCount());
+ }
+
+ SetStreamValid(false);
+}
+
+void ScTable::UpdateCompile( bool bForceIfNameInUse )
+{
+ for (SCCOL i=0; i < aCol.size(); i++)
+ {
+ aCol[i].UpdateCompile( bForceIfNameInUse );
+ }
+}
+
+void ScTable::SetTabNo(SCTAB nNewTab)
+{
+ nTab = nNewTab;
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].SetTabNo(nNewTab);
+}
+
+void ScTable::FindRangeNamesInUse(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sc::UpdatedRangeNames& rIndexes) const
+{
+ for (SCCOL i = nCol1; i <= nCol2 && IsColValid( i ); i++)
+ aCol[i].FindRangeNamesInUse(nRow1, nRow2, rIndexes);
+}
+
+void ScTable::ExtendPrintArea( OutputDevice* pDev,
+ SCCOL /* nStartCol */, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow )
+{
+ if ( !mpColFlags || !pRowFlags )
+ {
+ OSL_FAIL("ExtendPrintArea: No ColInfo or RowInfo");
+ return;
+ }
+
+ Point aPix1000 = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ double nPPTX = aPix1000.X() / 1000.0;
+ double nPPTY = aPix1000.Y() / 1000.0;
+
+ // First, mark those columns that we need to skip i.e. hidden and empty columns.
+
+ ScFlatBoolColSegments aSkipCols(rDocument.MaxCol());
+ aSkipCols.setFalse(0, rDocument.MaxCol());
+ for (SCCOL i = 0; i <= rDocument.MaxCol(); ++i)
+ {
+ SCCOL nLastCol = i;
+ if (ColHidden(i, nullptr, &nLastCol))
+ {
+ // Columns are hidden in this range.
+ aSkipCols.setTrue(i, nLastCol);
+ }
+ else
+ {
+ // These columns are visible. Check for empty columns.
+ for (SCCOL j = i; j <= nLastCol; ++j)
+ {
+ if ( j >= aCol.size() )
+ {
+ aSkipCols.setTrue( j, rDocument.MaxCol() );
+ break;
+ }
+ if (aCol[j].GetCellCount() == 0)
+ // empty
+ aSkipCols.setTrue(j,j);
+ }
+ }
+ i = nLastCol;
+ }
+
+ ScFlatBoolColSegments::RangeData aColData;
+ for (SCCOL nCol = rEndCol; nCol >= 0; --nCol)
+ {
+ if (!aSkipCols.getRangeData(nCol, aColData))
+ // Failed to get the data. This should never happen!
+ return;
+
+ if (aColData.mbValue)
+ {
+ // Skip these columns.
+ nCol = aColData.mnCol1; // move toward 0.
+ continue;
+ }
+
+ // These are visible and non-empty columns.
+ for (SCCOL nDataCol = nCol; 0 <= nDataCol && nDataCol >= aColData.mnCol1; --nDataCol)
+ {
+ SCCOL nPrintCol = nDataCol;
+ VisibleDataCellIterator aIter(rDocument, *mpHiddenRows, aCol[nDataCol]);
+ ScRefCellValue aCell = aIter.reset(nStartRow);
+ if (aCell.isEmpty())
+ // No visible cells found in this column. Skip it.
+ continue;
+
+ while (!aCell.isEmpty())
+ {
+ SCCOL nNewCol = nDataCol;
+ SCROW nRow = aIter.getRow();
+ if (nRow > nEndRow)
+ // Went past the last row position. Bail out.
+ break;
+
+ MaybeAddExtraColumn(nNewCol, nRow, pDev, nPPTX, nPPTY);
+ if (nNewCol > nPrintCol)
+ nPrintCol = nNewCol;
+ aCell = aIter.next();
+ }
+
+ if (nPrintCol > rEndCol)
+ // Make sure we don't shrink the print area.
+ rEndCol = nPrintCol;
+ }
+ nCol = aColData.mnCol1; // move toward 0.
+ }
+}
+
+void ScTable::MaybeAddExtraColumn(SCCOL& rCol, SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY)
+{
+ // tdf#128873 we do not need to calculate text width (heavy operation)
+ // when we for sure know that an additional column will not be added
+ if (GetAllocatedColumnsCount() > rCol + 1)
+ {
+ ScRefCellValue aNextCell = aCol[rCol + 1].GetCellValue(nRow);
+ if (!aNextCell.isEmpty())
+ {
+ // return rCol as is
+ return;
+ }
+ }
+
+ ScColumn& rColumn = aCol[rCol];
+ ScRefCellValue aCell = rColumn.GetCellValue(nRow);
+ if (!aCell.hasString())
+ return;
+
+ tools::Long nPixel = rColumn.GetTextWidth(nRow);
+
+ // Width already calculated in Idle-Handler ?
+ if ( TEXTWIDTH_DIRTY == nPixel )
+ {
+ ScNeededSizeOptions aOptions;
+ aOptions.bTotalSize = true;
+ aOptions.bFormula = false; //TODO: pass as parameter
+ aOptions.bSkipMerged = false;
+
+ Fraction aZoom(1,1);
+ nPixel = rColumn.GetNeededSize(
+ nRow, pDev, nPPTX, nPPTY, aZoom, aZoom, true, aOptions, nullptr );
+
+ rColumn.SetTextWidth(nRow, static_cast<sal_uInt16>(nPixel));
+ }
+
+ tools::Long nTwips = static_cast<tools::Long>(nPixel / nPPTX);
+ tools::Long nDocW = GetColWidth( rCol );
+
+ tools::Long nMissing = nTwips - nDocW;
+ if ( nMissing > 0 )
+ {
+ // look at alignment
+
+ const ScPatternAttr* pPattern = GetPattern( rCol, nRow );
+ const SfxItemSet* pCondSet = rDocument.GetCondResult( rCol, nRow, nTab );
+
+ SvxCellHorJustify eHorJust =
+ pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue();
+ if ( eHorJust == SvxCellHorJustify::Center )
+ nMissing /= 2; // distributed into both directions
+ else
+ {
+ // STANDARD is LEFT (only text is handled here)
+ bool bRight = ( eHorJust == SvxCellHorJustify::Right );
+ if ( IsLayoutRTL() )
+ bRight = !bRight;
+ if ( bRight )
+ nMissing = 0; // extended only to the left (logical)
+ }
+ }
+
+ SCCOL nNewCol = rCol;
+ while (nMissing > 0 && nNewCol < rDocument.MaxCol())
+ {
+ auto nNextCol = nNewCol + 1;
+ bool bNextEmpty = true;
+ if (GetAllocatedColumnsCount() > nNextCol)
+ {
+ ScRefCellValue aNextCell = aCol[nNextCol].GetCellValue(nRow);
+ bNextEmpty = aNextCell.isEmpty();
+ }
+ if (!bNextEmpty)
+ {
+ // Cell content in a next column ends display of this string.
+ nMissing = 0;
+ }
+ else
+ nMissing -= GetColWidth(++nNewCol);
+ }
+ rCol = nNewCol;
+}
+
+namespace {
+
+class SetTableIndex
+{
+ SCTAB mnTab;
+public:
+ explicit SetTableIndex(SCTAB nTab) : mnTab(nTab) {}
+
+ void operator() (ScRange& rRange) const
+ {
+ rRange.aStart.SetTab(mnTab);
+ rRange.aEnd.SetTab(mnTab);
+ }
+};
+
+}
+
+void ScTable::CopyPrintRange(const ScTable& rTable)
+{
+ // The table index shouldn't be used when the print range is used, but
+ // just in case set the correct table index.
+
+ aPrintRanges = rTable.aPrintRanges;
+ ::std::for_each(aPrintRanges.begin(), aPrintRanges.end(), SetTableIndex(nTab));
+
+ bPrintEntireSheet = rTable.bPrintEntireSheet;
+
+ moRepeatColRange.reset();
+ if (rTable.moRepeatColRange)
+ {
+ moRepeatColRange.emplace(*rTable.moRepeatColRange);
+ moRepeatColRange->aStart.SetTab(nTab);
+ moRepeatColRange->aEnd.SetTab(nTab);
+ }
+
+ moRepeatRowRange.reset();
+ if (rTable.moRepeatRowRange)
+ {
+ moRepeatRowRange.emplace(*rTable.moRepeatRowRange);
+ moRepeatRowRange->aStart.SetTab(nTab);
+ moRepeatRowRange->aEnd.SetTab(nTab);
+ }
+}
+
+void ScTable::SetRepeatColRange( std::optional<ScRange> oNew )
+{
+ moRepeatColRange = std::move(oNew);
+
+ SetStreamValid(false);
+
+ InvalidatePageBreaks();
+}
+
+void ScTable::SetRepeatRowRange( std::optional<ScRange> oNew )
+{
+ moRepeatRowRange = std::move(oNew);
+
+ SetStreamValid(false);
+
+ InvalidatePageBreaks();
+}
+
+void ScTable::ClearPrintRanges()
+{
+ aPrintRanges.clear();
+ bPrintEntireSheet = false;
+
+ SetStreamValid(false);
+
+ InvalidatePageBreaks(); // #i117952# forget page breaks for an old print range
+}
+
+void ScTable::ClearPrintNamedRanges()
+{
+ // tdf#100034 Clearing print ranges also requires to remove all print named ranges
+ // Iterate over all named ranges to determine which are print areas to be removed
+ if (mpRangeName)
+ {
+ std::vector<ScRangeData*> aRangesToRemove;
+ for (auto it = mpRangeName->begin(); it != mpRangeName->end(); it++)
+ {
+ ScRangeData* pData = it->second.get();
+ if (pData->HasType(ScRangeData::Type::PrintArea))
+ aRangesToRemove.push_back(pData);
+ }
+
+ // Effectively remove all named ranges that refer to print ranges
+ for (auto pItem : aRangesToRemove)
+ mpRangeName->erase(*pItem);
+ }
+}
+
+void ScTable::AddPrintRange( const ScRange& rNew )
+{
+ bPrintEntireSheet = false;
+ if( aPrintRanges.size() < 0xFFFF )
+ aPrintRanges.push_back( rNew );
+
+ SetStreamValid(false);
+
+ InvalidatePageBreaks();
+}
+
+void ScTable::SetPrintEntireSheet()
+{
+ if( !IsPrintEntireSheet() )
+ {
+ ClearPrintRanges();
+ bPrintEntireSheet = true;
+ }
+}
+
+const ScRange* ScTable::GetPrintRange(sal_uInt16 nPos) const
+{
+ return (nPos < GetPrintRangeCount()) ? &aPrintRanges[ nPos ] : nullptr;
+}
+
+void ScTable::FillPrintSaver( ScPrintSaverTab& rSaveTab ) const
+{
+ rSaveTab.SetAreas( std::vector(aPrintRanges), bPrintEntireSheet );
+ rSaveTab.SetRepeat( moRepeatColRange, moRepeatRowRange );
+}
+
+void ScTable::RestorePrintRanges( const ScPrintSaverTab& rSaveTab )
+{
+ aPrintRanges = rSaveTab.GetPrintRanges();
+ bPrintEntireSheet = rSaveTab.IsEntireSheet();
+ SetRepeatColRange( rSaveTab.GetRepeatCol() );
+ SetRepeatRowRange( rSaveTab.GetRepeatRow() );
+
+ InvalidatePageBreaks(); // #i117952# forget page breaks for an old print range
+ UpdatePageBreaks(nullptr);
+}
+
+ScTable::VisibleDataCellIterator::VisibleDataCellIterator(const ScDocument& rDoc, ScFlatBoolRowSegments& rRowSegs, ScColumn& rColumn) :
+ mrDocument(rDoc),
+ mrRowSegs(rRowSegs),
+ mrColumn(rColumn),
+ mnCurRow(ROW_NOT_FOUND),
+ mnUBound(ROW_NOT_FOUND)
+{
+}
+
+ScRefCellValue ScTable::VisibleDataCellIterator::reset(SCROW nRow)
+{
+ if (nRow > mrDocument.MaxRow())
+ {
+ mnCurRow = ROW_NOT_FOUND;
+ return ScRefCellValue();
+ }
+
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!mrRowSegs.getRangeData(nRow, aData))
+ {
+ mnCurRow = ROW_NOT_FOUND;
+ return ScRefCellValue();
+ }
+
+ if (!aData.mbValue)
+ {
+ // specified row is visible. Take it.
+ mnCurRow = nRow;
+ mnUBound = aData.mnRow2;
+ }
+ else
+ {
+ // specified row is not-visible. The first visible row is the start of
+ // the next segment.
+ mnCurRow = aData.mnRow2 + 1;
+ mnUBound = mnCurRow; // get range data on the next iteration.
+ if (mnCurRow > mrDocument.MaxRow())
+ {
+ // Make sure the row doesn't exceed our current limit.
+ mnCurRow = ROW_NOT_FOUND;
+ return ScRefCellValue();
+ }
+ }
+
+ maCell = mrColumn.GetCellValue(mnCurRow);
+ if (!maCell.isEmpty())
+ // First visible cell found.
+ return maCell;
+
+ // Find a first visible cell below this row (if any).
+ return next();
+}
+
+ScRefCellValue ScTable::VisibleDataCellIterator::next()
+{
+ if (mnCurRow == ROW_NOT_FOUND)
+ return ScRefCellValue();
+
+ while (mrColumn.GetNextDataPos(mnCurRow))
+ {
+ if (mnCurRow > mnUBound)
+ {
+ // We don't know the visibility of this row range. Query it.
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!mrRowSegs.getRangeData(mnCurRow, aData))
+ {
+ mnCurRow = ROW_NOT_FOUND;
+ return ScRefCellValue();
+ }
+
+ if (aData.mbValue)
+ {
+ // This row is invisible. Skip to the last invisible row and
+ // try again.
+ mnCurRow = mnUBound = aData.mnRow2;
+ continue;
+ }
+
+ // This row is visible.
+ mnUBound = aData.mnRow2;
+ }
+
+ maCell = mrColumn.GetCellValue(mnCurRow);
+ if (!maCell.isEmpty())
+ return maCell;
+ }
+
+ mnCurRow = ROW_NOT_FOUND;
+ return ScRefCellValue();
+}
+
+void ScTable::SetAnonymousDBData(std::unique_ptr<ScDBData> pDBData)
+{
+ pDBDataNoName = std::move(pDBData);
+}
+
+sal_uLong ScTable::AddCondFormat( std::unique_ptr<ScConditionalFormat> pNew )
+{
+ if(!mpCondFormatList)
+ mpCondFormatList.reset(new ScConditionalFormatList());
+
+ sal_uInt32 nMax = mpCondFormatList->getMaxKey();
+
+ pNew->SetKey(nMax+1);
+ mpCondFormatList->InsertNew(std::move(pNew));
+
+ return nMax + 1;
+}
+
+SvtScriptType ScTable::GetScriptType( SCCOL nCol, SCROW nRow ) const
+{
+ if ( !IsColValid( nCol ) )
+ return SvtScriptType::NONE;
+
+ return aCol[nCol].GetScriptType(nRow);
+}
+
+void ScTable::SetScriptType( SCCOL nCol, SCROW nRow, SvtScriptType nType )
+{
+ if (!ValidCol(nCol))
+ return;
+
+ aCol[nCol].SetScriptType(nRow, nType);
+}
+
+SvtScriptType ScTable::GetRangeScriptType(
+ sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
+{
+ if ( !IsColValid( nCol ) )
+ return SvtScriptType::NONE;
+
+ sc::CellStoreType::iterator itr = aCol[nCol].maCells.begin();
+ return aCol[nCol].GetRangeScriptType(rBlockPos.miCellTextAttrPos, nRow1, nRow2, itr);
+}
+
+formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol, SCROW nRow )
+{
+ if ( !ValidCol( nCol ) || !ValidRow( nRow ) )
+ return formula::FormulaTokenRef();
+ if ( nCol >= aCol.size() )
+ // Return a value of 0.0 if column not exists
+ return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
+ return aCol[nCol].ResolveStaticReference(nRow);
+}
+
+formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ if (nCol2 < nCol1 || nRow2 < nRow1)
+ return formula::FormulaTokenRef();
+
+ if ( !ValidCol( nCol1 ) || !ValidCol( nCol2 ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) )
+ return formula::FormulaTokenRef();
+
+ SCCOL nMaxCol;
+ if ( nCol2 >= aCol.size() )
+ nMaxCol = aCol.size() - 1;
+ else
+ nMaxCol = nCol2;
+
+ ScMatrixRef pMat(new ScMatrix(nCol2-nCol1+1, nRow2-nRow1+1, 0.0));
+ for (SCCOL nCol = nCol1; nCol <= nMaxCol; ++nCol)
+ {
+ if (!aCol[nCol].ResolveStaticReference(*pMat, nCol2-nCol1, nRow1, nRow2))
+ // Column contains non-static cell. Failed.
+ return formula::FormulaTokenRef();
+ }
+
+ return formula::FormulaTokenRef(new ScMatrixToken(std::move(pMat)));
+}
+
+formula::VectorRefArray ScTable::FetchVectorRefArray( SCCOL nCol, SCROW nRow1, SCROW nRow2 )
+{
+ if (nRow2 < nRow1)
+ return formula::VectorRefArray();
+
+ if ( !IsColValid( nCol ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) )
+ return formula::VectorRefArray();
+
+ return aCol[nCol].FetchVectorRefArray(nRow1, nRow2);
+}
+
+#ifdef DBG_UTIL
+void ScTable::AssertNoInterpretNeeded( SCCOL nCol, SCROW nRow1, SCROW nRow2 )
+{
+ assert( nRow2 >= nRow1 );
+ assert( IsColValid( nCol ) && ValidRow( nRow1 ) && ValidRow( nRow2 ) );
+ return aCol[nCol].AssertNoInterpretNeeded(nRow1, nRow2);
+}
+#endif
+
+bool ScTable::HandleRefArrayForParallelism( SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScFormulaCellGroupRef& mxGroup, ScAddress* pDirtiedAddress )
+{
+ if (nRow2 < nRow1)
+ return false;
+
+ if ( !IsColValid( nCol ) || !ValidRow( nRow1 ) || !ValidRow( nRow2 ) )
+ return false;
+
+ mpHiddenCols->makeReady();
+ mpHiddenRows->makeReady();
+ mpFilteredCols->makeReady();
+ mpFilteredRows->makeReady();
+
+ return aCol[nCol].HandleRefArrayForParallelism(nRow1, nRow2, mxGroup, pDirtiedAddress);
+}
+
+ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow )
+{
+ if ( !IsColRowValid( nCol, nRow ) )
+ return ScRefCellValue();
+
+ return aCol[nCol].GetCellValue(nRow);
+}
+
+ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow, sc::ColumnBlockPosition& rBlockPos )
+{
+ if ( !IsColRowValid( nCol, nRow ) )
+ return ScRefCellValue();
+
+ return aCol[nCol].GetCellValue(rBlockPos, nRow);
+}
+
+SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow )
+{
+ if ( !IsColRowValid( nCol, nRow ) )
+ return nullptr;
+
+ return aCol[nCol].GetBroadcaster(nRow);
+}
+
+void ScTable::DeleteBroadcasters(
+ sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
+{
+ if ( !IsColValid( nCol ) )
+ return;
+
+ aCol[nCol].DeleteBroadcasters(rBlockPos, nRow1, nRow2);
+}
+
+void ScTable::DeleteEmptyBroadcasters()
+{
+ for( auto& col : aCol )
+ col->DeleteEmptyBroadcasters();
+}
+
+void ScTable::FillMatrix( ScMatrix& rMat, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool* pPool ) const
+{
+ size_t nMatCol = 0;
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nMatCol)
+ aCol[nCol].FillMatrix(rMat, nMatCol, nRow1, nRow2, pPool);
+}
+
+void ScTable::InterpretDirtyCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ aCol[nCol].InterpretDirtyCells(nRow1, nRow2);
+}
+
+bool ScTable::InterpretCellsIfNeeded( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ bool allInterpreted = true;
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ if(!aCol[nCol].InterpretCellsIfNeeded(nRow1, nRow2))
+ allInterpreted = false;
+ return allInterpreted;
+}
+
+void ScTable::SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen )
+{
+ if (!ValidCol(nCol))
+ return;
+
+ aCol[nCol].SetFormulaResults(nRow, pResults, nLen);
+}
+
+void ScTable::CalculateInColumnInThread( ScInterpreterContext& rContext,
+ SCCOL nColStart, SCCOL nColEnd,
+ SCROW nRowStart, SCROW nRowEnd,
+ unsigned nThisThread, unsigned nThreadsTotal)
+{
+ if (!ValidCol(nColStart) || !ValidCol(nColEnd))
+ return;
+
+ size_t nLen = nRowEnd - nRowStart + 1;
+ size_t nOffset = 0;
+ for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
+ {
+ aCol[nCurrCol].CalculateInThread( rContext, nRowStart, nLen, nOffset, nThisThread, nThreadsTotal );
+ nOffset += nLen;
+ }
+}
+
+void ScTable::HandleStuffAfterParallelCalculation( SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen,
+ ScInterpreter* pInterpreter)
+{
+ assert(ValidCol(nColStart) && ValidCol(nColEnd));
+
+ for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
+ aCol[nCurrCol].HandleStuffAfterParallelCalculation( nRow, nLen, pInterpreter );
+}
+
+#if DUMP_COLUMN_STORAGE
+void ScTable::DumpColumnStorage( SCCOL nCol ) const
+{
+ if ( !IsColValid( nCol ) )
+ return;
+
+ aCol[nCol].DumpColumnStorage();
+}
+#endif
+
+const SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow ) const
+{
+ if ( !IsColRowValid( nCol, nRow ) )
+ return nullptr;
+
+ return aCol[nCol].GetBroadcaster(nRow);
+}
+
+void ScTable::DeleteConditionalFormat( sal_uLong nIndex )
+{
+ mpCondFormatList->erase(nIndex);
+}
+
+void ScTable::SetCondFormList( ScConditionalFormatList* pNew )
+{
+ mpCondFormatList.reset( pNew );
+}
+
+ScConditionalFormatList* ScTable::GetCondFormList()
+{
+ if(!mpCondFormatList)
+ mpCondFormatList.reset( new ScConditionalFormatList() );
+
+ return mpCondFormatList.get();
+}
+
+const ScConditionalFormatList* ScTable::GetCondFormList() const
+{
+ return mpCondFormatList.get();
+}
+
+ScColumnsRange ScTable::GetWritableColumnsRange(SCCOL nColBegin, SCCOL nColEnd)
+{
+ // because the range is inclusive, some code will pass nColEnd<nColBegin to indicate an empty range
+ if (nColEnd < nColBegin)
+ return ScColumnsRange(-1, -1);
+ assert( nColEnd >= 0 && nColEnd <= GetDoc().MaxCol());
+ CreateColumnIfNotExists(nColEnd);
+ return GetColumnsRange(nColBegin, nColEnd);
+}
+
+ScColumnsRange ScTable::GetAllocatedColumnsRange(SCCOL nColBegin, SCCOL nColEnd) const
+{
+ if (nColBegin >= aCol.size())
+ return ScColumnsRange(-1, -1);
+ // clamp end of range to available columns
+ if (nColEnd >= aCol.size())
+ nColEnd = aCol.size() - 1;
+ return GetColumnsRange(nColBegin, nColEnd);
+}
+
+ScColumnsRange ScTable::GetColumnsRange(SCCOL nColBegin, SCCOL nColEnd) const
+{
+ // because the range is inclusive, some code will pass nColEnd<nColBegin to indicate an empty range
+ if (nColEnd < nColBegin)
+ return ScColumnsRange(-1, -1);
+ assert( nColBegin >= 0 && nColBegin <= GetDoc().MaxCol());
+ assert( nColEnd >= 0 && nColEnd <= GetDoc().MaxCol());
+ return ScColumnsRange(nColBegin, nColEnd + 1); // change inclusive end to past-end
+}
+
+// out-of-line the cold part of the CreateColumnIfNotExists function
+void ScTable::CreateColumnIfNotExistsImpl( const SCCOL nScCol )
+{
+ // When doing multi-threaded load of, e.g. XLS files, we can hit this, which calls
+ // into SfxItemPool::Put, in parallel with other code that calls into SfxItemPool::Put,
+ // which is bad since that code is not thread-safe.
+ SolarMutexGuard aGuard;
+ const SCCOL aOldColSize = aCol.size();
+ aCol.resize( rDocument.GetSheetLimits(), static_cast< size_t >( nScCol + 1 ) );
+ for (SCCOL i = aOldColSize; i <= nScCol; i++)
+ aCol[i].Init( i, nTab, rDocument, false );
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
new file mode 100644
index 0000000000..afe2216b1a
--- /dev/null
+++ b/sc/source/core/data/table2.cxx
@@ -0,0 +1,4354 @@
+/* -*- 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 <algorithm>
+#include <memory>
+#include <table.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <olinetab.hxx>
+#include <stlpool.hxx>
+#include <attarray.hxx>
+#include <markdata.hxx>
+#include <dociter.hxx>
+#include <conditio.hxx>
+#include <chartlis.hxx>
+#include <fillinfo.hxx>
+#include <bcaslot.hxx>
+#include <postit.hxx>
+#include <sheetevents.hxx>
+#include <segmenttree.hxx>
+#include <dbdata.hxx>
+#include <tokenarray.hxx>
+#include <clipcontext.hxx>
+#include <types.hxx>
+#include <editutil.hxx>
+#include <mtvcellfunc.hxx>
+#include <refupdatecontext.hxx>
+#include <scopetools.hxx>
+#include <tabprotection.hxx>
+#include <columnspanset.hxx>
+#include <rowheightcontext.hxx>
+#include <listenercontext.hxx>
+#include <compressedarray.hxx>
+#include <refdata.hxx>
+#include <docsh.hxx>
+
+#include <scitems.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/editobj.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <poolcach.hxx>
+#include <unotools/charclass.hxx>
+#include <math.h>
+
+namespace {
+
+class ColumnRegroupFormulaCells
+{
+ ScColContainer& mrCols;
+ std::vector<ScAddress>* mpGroupPos;
+
+public:
+ ColumnRegroupFormulaCells( ScColContainer& rCols, std::vector<ScAddress>* pGroupPos ) :
+ mrCols(rCols), mpGroupPos(pGroupPos) {}
+
+ void operator() (SCCOL nCol)
+ {
+ mrCols[nCol].RegroupFormulaCells(mpGroupPos);
+ }
+};
+
+}
+
+sal_uInt16 ScTable::GetTextWidth(SCCOL nCol, SCROW nRow) const
+{
+ return aCol[nCol].GetTextWidth(nRow);
+}
+
+bool ScTable::SetOutlineTable( const ScOutlineTable* pNewOutline )
+{
+ sal_uInt16 nOldSizeX = 0;
+ sal_uInt16 nOldSizeY = 0;
+ sal_uInt16 nNewSizeX = 0;
+ sal_uInt16 nNewSizeY = 0;
+
+ if (pOutlineTable)
+ {
+ nOldSizeX = pOutlineTable->GetColArray().GetDepth();
+ nOldSizeY = pOutlineTable->GetRowArray().GetDepth();
+ pOutlineTable.reset();
+ }
+
+ if (pNewOutline)
+ {
+ pOutlineTable.reset(new ScOutlineTable( *pNewOutline ));
+ nNewSizeX = pOutlineTable->GetColArray().GetDepth();
+ nNewSizeY = pOutlineTable->GetRowArray().GetDepth();
+ }
+
+ return ( nNewSizeX != nOldSizeX || nNewSizeY != nOldSizeY ); // changed size?
+}
+
+void ScTable::StartOutlineTable()
+{
+ if (!pOutlineTable)
+ pOutlineTable.reset(new ScOutlineTable);
+}
+
+void ScTable::SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew )
+{
+ pSheetEvents = std::move(pNew);
+
+ SetCalcNotification( false ); // discard notifications before the events were set
+
+ SetStreamValid(false);
+}
+
+void ScTable::SetCalcNotification( bool bSet )
+{
+ bCalcNotification = bSet;
+}
+
+bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) const
+{
+ if ( nStartCol==0 && nEndCol==rDocument.MaxCol() && pOutlineTable )
+ if (!pOutlineTable->TestInsertRow(nSize))
+ return false;
+
+ SCCOL maxCol = ClampToAllocatedColumns(nEndCol);
+ for (SCCOL i=nStartCol; i<=maxCol; i++)
+ if (!aCol[i].TestInsertRow(nStartRow, nSize))
+ return false;
+
+ if( maxCol != nEndCol )
+ if (!aDefaultColData.TestInsertRow(nSize))
+ return false;
+
+ return true;
+}
+
+void ScTable::InsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize )
+{
+ if (nStartCol==0 && nEndCol==rDocument.MaxCol())
+ {
+ if (mpRowHeights && pRowFlags)
+ {
+ mpRowHeights->insertSegment(nStartRow, nSize);
+ CRFlags nNewFlags = pRowFlags->Insert( nStartRow, nSize);
+ // only copy manual size flag, clear all others
+ if (nNewFlags != CRFlags::NONE && (nNewFlags != CRFlags::ManualSize))
+ pRowFlags->SetValue( nStartRow, nStartRow + nSize - 1,
+ nNewFlags & CRFlags::ManualSize);
+ }
+
+ if (pOutlineTable)
+ pOutlineTable->InsertRow( nStartRow, nSize );
+
+ mpFilteredRows->insertSegment(nStartRow, nSize);
+ mpHiddenRows->insertSegment(nStartRow, nSize);
+
+ if (!maRowManualBreaks.empty())
+ {
+ // Copy all breaks up to nStartRow (non-inclusive).
+ ::std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow);
+ ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1);
+
+ // Copy all breaks from nStartRow (inclusive) to the last element,
+ // but add nSize to each value.
+ ::std::set<SCROW>::iterator itr2 = maRowManualBreaks.end();
+ for (; itr1 != itr2; ++itr1)
+ aNewBreaks.insert(static_cast<SCROW>(*itr1 + nSize));
+
+ maRowManualBreaks.swap(aNewBreaks);
+ }
+ }
+
+ for (SCCOL j : GetAllocatedColumnsRange(nStartCol, nEndCol))
+ aCol[j].InsertRow( nStartRow, nSize );
+ aDefaultColData.InsertRow( nStartRow, nSize );
+
+ mpCondFormatList->InsertRow(nTab, nStartCol, nEndCol, nStartRow, nSize);
+
+ InvalidatePageBreaks();
+
+ // TODO: In the future we may want to check if the table has been
+ // really modified before setting the stream invalid.
+ SetStreamValid(false);
+}
+
+void ScTable::DeleteRow(
+ const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize,
+ bool* pUndoOutline, std::vector<ScAddress>* pGroupPos )
+{
+ if (nStartCol==0 && nEndCol==rDocument.MaxCol())
+ {
+ if (pRowFlags)
+ pRowFlags->Remove( nStartRow, nSize);
+
+ if (mpRowHeights)
+ mpRowHeights->removeSegment(nStartRow, nStartRow+nSize);
+
+ if (pOutlineTable)
+ if (pOutlineTable->DeleteRow( nStartRow, nSize ))
+ if (pUndoOutline)
+ *pUndoOutline = true;
+
+ mpFilteredRows->removeSegment(nStartRow, nStartRow+nSize);
+ mpHiddenRows->removeSegment(nStartRow, nStartRow+nSize);
+
+ if (!maRowManualBreaks.empty())
+ {
+ // Erase all manual breaks between nStartRow and nStartRow + nSize - 1 (inclusive).
+ std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow);
+ std::set<SCROW>::iterator itr2 = maRowManualBreaks.upper_bound(static_cast<SCROW>(nStartRow + nSize - 1));
+ maRowManualBreaks.erase(itr1, itr2);
+
+ // Copy all breaks from the 1st element up to nStartRow to the new container.
+ itr1 = maRowManualBreaks.lower_bound(nStartRow);
+ ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1);
+
+ // Copy all breaks from nStartRow to the last element, but subtract each value by nSize.
+ itr2 = maRowManualBreaks.end();
+ for (; itr1 != itr2; ++itr1)
+ aNewBreaks.insert(static_cast<SCROW>(*itr1 - nSize));
+
+ maRowManualBreaks.swap(aNewBreaks);
+ }
+ }
+
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
+ for (SCCOL j=nStartCol; j<=ClampToAllocatedColumns(nEndCol); j++)
+ aCol[j].DeleteRow(nStartRow, nSize, pGroupPos);
+ }
+
+ std::vector<SCCOL> aRegroupCols;
+ rRegroupCols.getColumns(nTab, aRegroupCols);
+ std::for_each(
+ aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, pGroupPos));
+
+ InvalidatePageBreaks();
+
+ // TODO: In the future we may want to check if the table has been
+ // really modified before setting the stream invalid.
+ SetStreamValid(false);
+}
+
+bool ScTable::TestInsertCol( SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) const
+{
+ if ( nSize > o3tl::make_unsigned(rDocument.MaxCol()) )
+ return false;
+
+ if ( nStartRow==0 && nEndRow==rDocument.MaxRow() && pOutlineTable
+ && ! pOutlineTable->TestInsertCol(nSize) )
+ return false;
+
+ auto range = GetAllocatedColumnsRange( rDocument.MaxCol() - static_cast<SCCOL>(nSize) + 1, rDocument.MaxCol() );
+ for (auto it = range.rbegin(); it != range.rend(); ++it )
+ if (! aCol[*it].TestInsertCol(nStartRow, nEndRow))
+ return false;
+
+ return true;
+}
+
+void ScTable::InsertCol(
+ const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize )
+{
+ if (nStartRow==0 && nEndRow==rDocument.MaxRow())
+ {
+ if (mpColWidth && mpColFlags)
+ {
+ mpColWidth->InsertPreservingSize(nStartCol, nSize, STD_COL_WIDTH);
+ // The inserted columns have the same widths as the columns, which were selected for insert.
+ for (SCSIZE i=0; i < std::min(rDocument.MaxCol()-nSize-nStartCol, nSize); ++i)
+ mpColWidth->SetValue(nStartCol + i, mpColWidth->GetValue(nStartCol+i+nSize));
+ mpColFlags->InsertPreservingSize(nStartCol, nSize, CRFlags::NONE);
+ }
+ if (pOutlineTable)
+ pOutlineTable->InsertCol( nStartCol, nSize );
+
+ mpHiddenCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize));
+ mpFilteredCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize));
+
+ if (!maColManualBreaks.empty())
+ {
+ // Copy all breaks up to nStartCol (non-inclusive).
+ ::std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol);
+ ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1);
+
+ // Copy all breaks from nStartCol (inclusive) to the last element,
+ // but add nSize to each value.
+ ::std::set<SCCOL>::iterator itr2 = maColManualBreaks.end();
+ for (; itr1 != itr2; ++itr1)
+ aNewBreaks.insert(static_cast<SCCOL>(*itr1 + nSize));
+
+ maColManualBreaks.swap(aNewBreaks);
+ }
+ }
+
+ // Make sure there are enough columns at the end.
+ CreateColumnIfNotExists(std::min<SCCOL>(rDocument.MaxCol(), std::max(nStartCol, aCol.size()) + nSize - 1 ));
+ if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
+ {
+ // Move existing columns back, this will swap last empty columns in the inserted place.
+ for (SCCOL nCol = aCol.size() - 1 - nSize; nCol >= nStartCol; --nCol)
+ aCol[nCol].SwapCol(aCol[nCol+nSize]);
+ }
+ else
+ {
+ for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++)
+ aCol[aCol.size() - 1 - nSize - i].MoveTo(nStartRow, nEndRow, aCol[aCol.size() - 1 - i]);
+ }
+
+ std::vector<SCCOL> aRegroupCols;
+ rRegroupCols.getColumns(nTab, aRegroupCols);
+ std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr));
+
+ if (nStartCol>0) // copy old attributes
+ {
+ sal_uInt16 nWhichArray[2];
+ nWhichArray[0] = ATTR_MERGE;
+ nWhichArray[1] = 0;
+
+ sc::CopyToDocContext aCxt(rDocument);
+ for (SCSIZE i=0; i<nSize; i++)
+ {
+ aCol[nStartCol-1].CopyToColumn(aCxt, nStartRow, nEndRow, InsertDeleteFlags::ATTRIB,
+ false, aCol[nStartCol+i] );
+ aCol[nStartCol+i].RemoveFlags( nStartRow, nEndRow,
+ ScMF::Hor | ScMF::Ver | ScMF::Auto );
+ aCol[nStartCol+i].ClearItems( nStartRow, nEndRow, nWhichArray );
+ }
+ }
+
+ mpCondFormatList->InsertCol(nTab, nStartRow, nEndRow, nStartCol, nSize);
+
+ InvalidatePageBreaks();
+
+ // TODO: In the future we may want to check if the table has been
+ // really modified before setting the stream invalid.
+ SetStreamValid(false);
+}
+
+void ScTable::DeleteCol(
+ const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize, bool* pUndoOutline )
+{
+ if (nStartRow==0 && nEndRow==rDocument.MaxRow())
+ {
+ if (mpColWidth && mpColFlags)
+ {
+ assert( nStartCol + nSize <= o3tl::make_unsigned(rDocument.MaxCol()+1) ); // moving 0 if ==rDocument.MaxCol()+1 is correct
+ mpColWidth->RemovePreservingSize(nStartCol, nSize, STD_COL_WIDTH);
+ mpColFlags->RemovePreservingSize(nStartCol, nSize, CRFlags::NONE);
+ }
+ if (pOutlineTable)
+ if (pOutlineTable->DeleteCol( nStartCol, nSize ))
+ if (pUndoOutline)
+ *pUndoOutline = true;
+
+ SCCOL nRmSize = nStartCol + static_cast<SCCOL>(nSize);
+ mpHiddenCols->removeSegment(nStartCol, nRmSize);
+ mpFilteredCols->removeSegment(nStartCol, nRmSize);
+
+ if (!maColManualBreaks.empty())
+ {
+ // Erase all manual breaks between nStartCol and nStartCol + nSize - 1 (inclusive).
+ std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol);
+ std::set<SCCOL>::iterator itr2 = maColManualBreaks.upper_bound(static_cast<SCCOL>(nStartCol + nSize - 1));
+ maColManualBreaks.erase(itr1, itr2);
+
+ // Copy all breaks from the 1st element up to nStartCol to the new container.
+ itr1 = maColManualBreaks.lower_bound(nStartCol);
+ ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1);
+
+ // Copy all breaks from nStartCol to the last element, but subtract each value by nSize.
+ itr2 = maColManualBreaks.end();
+ for (; itr1 != itr2; ++itr1)
+ aNewBreaks.insert(static_cast<SCCOL>(*itr1 - nSize));
+
+ maColManualBreaks.swap(aNewBreaks);
+ }
+ }
+
+ for (SCCOL col = nStartCol; col <= ClampToAllocatedColumns(nStartCol + nSize - 1); ++col)
+ aCol[col].DeleteArea(nStartRow, nEndRow, InsertDeleteFlags::ALL, false);
+
+ if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
+ {
+ for (SCCOL nCol = nStartCol + nSize; nCol < aCol.size(); ++nCol)
+ aCol[nCol].SwapCol(aCol[nCol - nSize]);
+ // When delete column(s), initialize the last columns from the default attributes
+ for (SCCOL nCol = aCol.size() < static_cast<SCCOL>(nSize) ? 0 : aCol.size() - nSize; nCol < aCol.size(); ++nCol)
+ aCol[nCol].Init(nCol, aCol[nCol].GetTab(), rDocument, false);
+ }
+ else
+ {
+ for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++)
+ aCol[nStartCol + nSize + i].MoveTo(nStartRow, nEndRow, aCol[nStartCol + i]);
+ }
+
+ std::vector<SCCOL> aRegroupCols;
+ rRegroupCols.getColumns(nTab, aRegroupCols);
+ std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr));
+
+ InvalidatePageBreaks();
+
+ // TODO: In the future we may want to check if the table has been
+ // really modified before setting the stream invalid.
+ SetStreamValid(false);
+}
+
+void ScTable::DeleteArea(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, InsertDeleteFlags nDelFlag,
+ bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
+{
+ if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
+ if (nRow2 > rDocument.MaxRow()) nRow2 = rDocument.MaxRow();
+ if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
+ {
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ aCol[i].DeleteArea(nRow1, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
+ }
+
+ // Do not set protected cell in a protected table
+
+ if ( IsProtected() && (nDelFlag & InsertDeleteFlags::ATTRIB) )
+ {
+ ScPatternAttr aPattern(rDocument.GetPool());
+ aPattern.GetItemSet().Put( ScProtectionAttr( false ) );
+ ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern );
+ }
+
+ if( nDelFlag & InsertDeleteFlags::ATTRIB )
+ mpCondFormatList->DeleteArea( nCol1, nRow1, nCol2, nRow2 );
+ }
+
+ // TODO: In the future we may want to check if the table has been
+ // really modified before setting the stream invalid.
+ SetStreamValid(false);
+}
+
+void ScTable::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
+{
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].DeleteSelection(nDelFlag, rMark, bBroadcast);
+ }
+
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks(&aRangeList, false);
+
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+
+ if((nDelFlag & InsertDeleteFlags::ATTRIB) && rRange.aStart.Tab() == nTab)
+ mpCondFormatList->DeleteArea( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+
+ // Do not set protected cell in a protected sheet
+
+ if ( IsProtected() && (nDelFlag & InsertDeleteFlags::ATTRIB) )
+ {
+ ScDocumentPool* pPool = rDocument.GetPool();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aSet( *pPool );
+ aSet.Put( ScProtectionAttr( false ) );
+ ScItemPoolCache aCache( pPool, &aSet );
+ ApplySelectionCache( &aCache, rMark );
+ }
+
+ // TODO: In the future we may want to check if the table has been
+ // really modified before setting the stream invalid.
+ SetStreamValid(false);
+}
+
+// pTable = Clipboard
+void ScTable::CopyToClip(
+ sc::CopyToClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ ScTable* pTable )
+{
+ if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
+ return;
+
+ // copy content
+ //local range names need to be copied first for formula cells
+ if (!pTable->mpRangeName && mpRangeName)
+ pTable->mpRangeName.reset( new ScRangeName(*mpRangeName) );
+
+ nCol2 = ClampToAllocatedColumns(nCol2);
+
+ for ( SCCOL i = nCol1; i <= nCol2; i++)
+ aCol[i].CopyToClip(rCxt, nRow1, nRow2, pTable->CreateColumnIfNotExists(i)); // notes are handled at column level
+
+ // copy widths/heights, and only "hidden", "filtered" and "manual" flags
+ // also for all preceding columns/rows, to have valid positions for drawing objects
+
+ if (mpColWidth && pTable->mpColWidth)
+ pTable->mpColWidth->CopyFrom(*mpColWidth, 0, nCol2);
+
+ pTable->CopyColHidden(*this, 0, nCol2);
+ pTable->CopyColFiltered(*this, 0, nCol2);
+ if (pDBDataNoName)
+ pTable->SetAnonymousDBData(std::unique_ptr<ScDBData>(new ScDBData(*pDBDataNoName)));
+
+ if (pRowFlags && pTable->pRowFlags && mpRowHeights && pTable->mpRowHeights)
+ {
+ pTable->pRowFlags->CopyFromAnded( *pRowFlags, 0, nRow2, CRFlags::ManualSize);
+ pTable->CopyRowHeight(*this, 0, nRow2, 0);
+ }
+
+ pTable->CopyRowHidden(*this, 0, nRow2);
+ pTable->CopyRowFiltered(*this, 0, nRow2);
+
+ // If necessary replace formulas with values
+
+ if ( IsProtected() )
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ pTable->aCol[i].RemoveProtected(nRow1, nRow2);
+
+ mpCondFormatList->startRendering();
+ mpCondFormatList->updateValues();
+ pTable->mpCondFormatList.reset(new ScConditionalFormatList(pTable->rDocument, *mpCondFormatList));
+ mpCondFormatList->endRendering();
+}
+
+void ScTable::CopyToClip(
+ sc::CopyToClipContext& rCxt, const ScRangeList& rRanges, ScTable* pTable )
+{
+ for ( size_t i = 0, nListSize = rRanges.size(); i < nListSize; ++i )
+ {
+ const ScRange & r = rRanges[ i ];
+ CopyToClip( rCxt, r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), pTable);
+ }
+}
+
+void ScTable::CopyStaticToDocument(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScTable* pDestTab )
+{
+ if (nCol1 > nCol2 || nRow1 > nRow2)
+ return;
+
+ const SCCOL nFirstUnallocated = std::clamp<SCCOL>(GetAllocatedColumnsCount(), nCol1, nCol2 + 1);
+ if (nFirstUnallocated > nCol1)
+ pDestTab->CreateColumnIfNotExists(nFirstUnallocated - 1);
+
+ for (SCCOL i = nCol1; i < nFirstUnallocated; ++i)
+ {
+ ScColumn& rSrcCol = aCol[i];
+ ScColumn& rDestCol = pDestTab->aCol[i];
+ rSrcCol.CopyStaticToDocument(nRow1, nRow2, rMap, rDestCol);
+ }
+
+ // Maybe copy this table's default attrs to dest not limiting to already allocated in dest?
+ const SCCOL nLastInDest = std::min<SCCOL>(pDestTab->GetAllocatedColumnsCount() - 1, nCol2);
+ for (SCCOL i = nFirstUnallocated; i <= nLastInDest; ++i)
+ {
+ ScColumn& rDestCol = pDestTab->aCol[i];
+ rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2);
+ rDestCol.maCells.set_empty(nRow1, nRow2);
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ sal_uInt32 nNumFmt = aDefaultColData.GetPattern(nRow)->GetNumberFormat(
+ rDocument.GetNonThreadedContext().GetFormatTable());
+ SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
+ if (itNum != rMap.end())
+ nNumFmt = itNum->second;
+
+ rDestCol.SetNumberFormat(nRow, nNumFmt);
+ }
+ rDestCol.CellStorageModified();
+ }
+}
+
+void ScTable::CopyCellToDocument(SCCOL nSrcCol, SCROW nSrcRow, SCCOL nDestCol, SCROW nDestRow, ScTable& rDestTab )
+{
+ if (!ValidColRow(nSrcCol, nSrcRow) || !ValidColRow(nDestCol, nDestRow))
+ return;
+
+ if (nSrcCol >= GetAllocatedColumnsCount())
+ {
+ if (nDestCol < rDestTab.GetAllocatedColumnsCount())
+ {
+ ScColumn& rDestCol = rDestTab.aCol[nDestCol];
+ rDestCol.maCells.set_empty(nDestRow, nDestRow);
+ rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
+ rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
+ rDestCol.CellStorageModified();
+ }
+ return;
+ }
+
+ ScColumn& rSrcCol = aCol[nSrcCol];
+ ScColumn& rDestCol = rDestTab.CreateColumnIfNotExists(nDestCol);
+ rSrcCol.CopyCellToDocument(nSrcRow, nDestRow, rDestCol);
+}
+
+namespace {
+
+bool CheckAndDeduplicateCondFormat(ScDocument& rDocument, ScConditionalFormat* pOldFormat, const ScConditionalFormat* pNewFormat, SCTAB nTab)
+{
+ if (!pOldFormat)
+ return false;
+
+ if (pOldFormat->EqualEntries(*pNewFormat, true))
+ {
+ const ScRangeList& rNewRangeList = pNewFormat->GetRange();
+ ScRangeList& rDstRangeList = pOldFormat->GetRangeList();
+ for (size_t i = 0; i < rNewRangeList.size(); ++i)
+ {
+ rDstRangeList.Join(rNewRangeList[i]);
+ }
+ rDocument.AddCondFormatData(rNewRangeList, nTab, pOldFormat->GetKey());
+ return true;
+ }
+
+ return false;
+}
+
+}
+
+void ScTable::CopyConditionalFormat( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ SCCOL nDx, SCROW nDy, const ScTable* pTable)
+{
+ ScRange aOldRange( nCol1 - nDx, nRow1 - nDy, pTable->nTab, nCol2 - nDx, nRow2 - nDy, pTable->nTab);
+ ScRange aNewRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+ bool bSameDoc = rDocument.GetStyleSheetPool() == pTable->rDocument.GetStyleSheetPool();
+
+ for(const auto& rxCondFormat : *pTable->mpCondFormatList)
+ {
+ const ScRangeList& rCondFormatRange = rxCondFormat->GetRange();
+ if(!rCondFormatRange.Intersects( aOldRange ))
+ continue;
+
+ ScRangeList aIntersectedRange = rCondFormatRange.GetIntersectedRange(aOldRange);
+ std::unique_ptr<ScConditionalFormat> pNewFormat = rxCondFormat->Clone(&rDocument);
+
+ pNewFormat->SetRange(aIntersectedRange);
+ sc::RefUpdateContext aRefCxt(rDocument);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = aNewRange;
+ aRefCxt.mnColDelta = nDx;
+ aRefCxt.mnRowDelta = nDy;
+ aRefCxt.mnTabDelta = nTab - pTable->nTab;
+ pNewFormat->UpdateReference(aRefCxt, true);
+
+ if (bSameDoc && pTable->nTab == nTab && CheckAndDeduplicateCondFormat(rDocument, mpCondFormatList->GetFormat(rxCondFormat->GetKey()), pNewFormat.get(), nTab))
+ {
+ continue;
+ }
+ sal_uLong nMax = 0;
+ bool bDuplicate = false;
+ for(const auto& rxCond : *mpCondFormatList)
+ {
+ // Check if there is the same format in the destination
+ // If there is, then simply expand its range
+ if (CheckAndDeduplicateCondFormat(rDocument, rxCond.get(), pNewFormat.get(), nTab))
+ {
+ bDuplicate = true;
+ break;
+ }
+
+ if (rxCond->GetKey() > nMax)
+ nMax = rxCond->GetKey();
+ }
+ // Do not add duplicate entries
+ if (bDuplicate)
+ {
+ continue;
+ }
+
+ pNewFormat->SetKey(nMax + 1);
+ auto pNewFormatTmp = pNewFormat.get();
+ mpCondFormatList->InsertNew(std::move(pNewFormat));
+
+ if(!bSameDoc)
+ {
+ for(size_t i = 0, n = pNewFormatTmp->size();
+ i < n; ++i)
+ {
+ OUString aStyleName;
+ const ScFormatEntry* pEntry = pNewFormatTmp->GetEntry(i);
+ if(pEntry->GetType() == ScFormatEntry::Type::Condition ||
+ pEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle();
+ else if(pEntry->GetType() == ScFormatEntry::Type::Date)
+ aStyleName = static_cast<const ScCondDateFormatEntry*>(pEntry)->GetStyleName();
+
+ if(!aStyleName.isEmpty())
+ rDocument.GetStyleSheetPool()->CopyStyleFrom(
+ pTable->rDocument.GetStyleSheetPool(), aStyleName, SfxStyleFamily::Para, true );
+ }
+ }
+
+ rDocument.AddCondFormatData( pNewFormatTmp->GetRange(), nTab, pNewFormatTmp->GetKey() );
+ }
+}
+
+bool ScTable::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol )
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ CreateColumnIfNotExists(nCol).InitBlockPosition(rBlockPos);
+ return true;
+}
+
+// pTable is source
+
+void ScTable::CopyFromClip(
+ sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ SCCOL nDx, SCROW nDy, ScTable* pTable )
+{
+ if (nCol2 > rDocument.MaxCol())
+ nCol2 = rDocument.MaxCol();
+ if (nRow2 > rDocument.MaxRow())
+ nRow2 = rDocument.MaxRow();
+
+ if (!(ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)))
+ return;
+
+ CreateColumnIfNotExists(nCol2);
+ for ( SCCOL i = nCol1; i <= nCol2; i++)
+ {
+ pTable->CreateColumnIfNotExists(i - nDx);
+ aCol[i].CopyFromClip(rCxt, nRow1, nRow2, nDy, pTable->aCol[i - nDx]); // notes are handles at column level
+ }
+
+ if (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB)
+ {
+ // make sure that there are no old references to the cond formats
+ sal_uInt16 nWhichArray[2];
+ nWhichArray[0] = ATTR_CONDITIONAL;
+ nWhichArray[1] = 0;
+ for ( SCCOL i = nCol1; i <= nCol2; ++i)
+ aCol[i].ClearItems(nRow1, nRow2, nWhichArray);
+ }
+
+ if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) == InsertDeleteFlags::NONE)
+ return;
+
+ if (nRow1==0 && nRow2==rDocument.MaxRow() && mpColWidth && pTable->mpColWidth)
+ mpColWidth->CopyFrom(*pTable->mpColWidth, nCol1, nCol2, nCol1 - nDx);
+
+ if (nCol1==0 && nCol2==rDocument.MaxCol() && mpRowHeights && pTable->mpRowHeights &&
+ pRowFlags && pTable->pRowFlags)
+ {
+ CopyRowHeight(*pTable, nRow1, nRow2, -nDy);
+ // Must copy CRFlags::ManualSize bit too, otherwise pRowHeight doesn't make sense
+ for (SCROW j=nRow1; j<=nRow2; j++)
+ {
+ if ( pTable->pRowFlags->GetValue(j-nDy) & CRFlags::ManualSize )
+ pRowFlags->OrValue( j, CRFlags::ManualSize);
+ else
+ pRowFlags->AndValue( j, ~CRFlags::ManualSize);
+ }
+ }
+
+ // Do not set protected cell in a protected sheet
+ if (IsProtected() && (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB))
+ {
+ ScPatternAttr aPattern(rDocument.GetPool());
+ aPattern.GetItemSet().Put( ScProtectionAttr( false ) );
+ ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern );
+ }
+
+ // create deep copies for conditional formatting
+ CopyConditionalFormat( nCol1, nRow1, nCol2, nRow2, nDx, nDy, pTable);
+}
+
+void ScTable::MixData(
+ sc::MixDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ ScPasteFunc nFunction, bool bSkipEmpty, const ScTable* pSrcTab )
+{
+ for (SCCOL nCol : pSrcTab->GetAllocatedColumnsRange(nCol1, nCol2))
+ aCol[nCol].MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, pSrcTab->aCol[nCol]);
+}
+
+// Selection form this document
+void ScTable::MixMarked(
+ sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction,
+ bool bSkipEmpty, const ScTable* pSrcTab )
+{
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].MixMarked(rCxt, rMark, nFunction, bSkipEmpty, pSrcTab->aCol[i]);
+}
+
+namespace {
+
+class TransClipHandler
+{
+ ScTable& mrClipTab;
+ const ScTable& mrSrcTab;
+ SCTAB mnSrcTab;
+ SCCOL mnCol1;
+ SCCOL mnSrcCol;
+ size_t mnTopRow;
+ size_t mnEndRow;
+ SCROW mnTransRow;
+ SCROW mnFilteredRows = 0;
+ SCROW mnRowDestOffset = 0;
+ bool mbAsLink;
+ bool mbWasCut;
+ bool mbIncludeFiltered;
+ InsertDeleteFlags mnFlags;
+
+ ScAddress getDestPos(size_t nRow) const
+ {
+ return ScAddress(static_cast<SCCOL>(mnCol1 + nRow - mnTopRow), mnTransRow,
+ mrClipTab.GetTab());
+ }
+
+ ScFormulaCell* createRefCell(size_t nSrcRow, const ScAddress& rDestPos) const
+ {
+ ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab);
+ ScSingleRefData aRef;
+ aRef.InitAddress(aSrcPos); // Absolute reference.
+ aRef.SetFlag3D(true);
+
+ ScTokenArray aArr(mrClipTab.GetDoc());
+ aArr.AddSingleReference(aRef);
+ return new ScFormulaCell(mrClipTab.GetDoc(), rDestPos, aArr);
+ }
+
+ void setLink(size_t nRow)
+ {
+ SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
+ mrClipTab.SetFormulaCell(nTransCol, mnTransRow,
+ createRefCell(nRow, getDestPos(nRow)));
+ }
+
+public:
+ TransClipHandler(ScTable& rClipTab, const ScTable& rSrcTab, SCTAB nSrcTab, SCCOL nCol1,
+ SCCOL nSrcCol, size_t nTopRow, size_t nEndRow, SCROW nCombinedStartRow,
+ SCROW nRowDestOffset, bool bAsLink, bool bWasCut,
+ const InsertDeleteFlags& nFlags, const bool bIncludeFiltered,
+ std::vector<SCROW>& rFilteredRows)
+ : mrClipTab(rClipTab)
+ , mrSrcTab(rSrcTab)
+ , mnSrcTab(nSrcTab)
+ , mnCol1(nCol1)
+ , mnSrcCol(nSrcCol)
+ , mnTopRow(nTopRow)
+ , mnEndRow(nEndRow)
+ , mnTransRow(nSrcCol - nCol1 + nCombinedStartRow)
+ , mnRowDestOffset(nRowDestOffset)
+ , mbAsLink(bAsLink)
+ , mbWasCut(bWasCut)
+ , mbIncludeFiltered(bIncludeFiltered)
+ , mnFlags(nFlags)
+ {
+ // Create list of filtered rows.
+ if (!mbIncludeFiltered)
+ {
+ for (SCROW curRow = nTopRow; curRow <= static_cast<SCROW>(mnEndRow); ++curRow)
+ {
+ // maybe this loop could be optimized
+ bool bFiltered = mrSrcTab.RowFiltered(curRow, nullptr, nullptr);
+ if (bFiltered)
+ rFilteredRows.push_back(curRow);
+ }
+ }
+ }
+
+ void operator() (size_t nRow, double fVal)
+ {
+ bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
+ if (!mbIncludeFiltered && bFiltered)
+ {
+ mnFilteredRows++;
+ return;
+ }
+
+ if (mbAsLink)
+ {
+ setLink(nRow);
+ return;
+ }
+
+ SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
+ mrClipTab.SetValue(nTransCol, mnTransRow, fVal);
+ }
+
+ void operator() (size_t nRow, const svl::SharedString& rStr)
+ {
+ bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
+ if (!mbIncludeFiltered && bFiltered)
+ {
+ mnFilteredRows++;
+ return;
+ }
+
+ if (mbAsLink)
+ {
+ setLink(nRow);
+ return;
+ }
+
+ SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
+ mrClipTab.SetRawString(nTransCol, mnTransRow, rStr);
+ }
+
+ void operator() (size_t nRow, const EditTextObject* p)
+ {
+ bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
+ if (!mbIncludeFiltered && bFiltered)
+ {
+ mnFilteredRows++;
+ return;
+ }
+
+ if (mbAsLink)
+ {
+ setLink(nRow);
+ return;
+ }
+
+ SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
+ mrClipTab.SetEditText(nTransCol, mnTransRow, ScEditUtil::Clone(*p, mrClipTab.GetDoc()));
+ }
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
+ if (!mbIncludeFiltered && bFiltered)
+ {
+ mnFilteredRows++;
+ return;
+ }
+
+ if (mbAsLink)
+ {
+ setLink(nRow);
+ return;
+ }
+
+ ScFormulaCell* pNew = new ScFormulaCell(*p, mrClipTab.GetDoc(),
+ getDestPos(nRow - mnFilteredRows + mnRowDestOffset),
+ ScCloneFlags::StartListening);
+
+ // rotate reference
+ // for Cut, the references are later adjusted through UpdateTranspose
+
+ if (!mbWasCut)
+ pNew->TransposeReference();
+
+ SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
+ mrClipTab.SetFormulaCell(nTransCol, mnTransRow, pNew);
+ }
+
+ // empty cells
+ void operator()(const int /*type*/, size_t nRow, size_t nDataSize)
+ {
+ for (size_t curRow = nRow; curRow < nRow + nDataSize; ++curRow)
+ {
+ bool bFiltered = mrSrcTab.RowFiltered(curRow, nullptr, nullptr);
+ if (!mbIncludeFiltered && bFiltered)
+ {
+ mnFilteredRows++;
+ continue;
+ }
+
+ if (mbAsLink && mnFlags == InsertDeleteFlags::ALL)
+ {
+ // with InsertDeleteFlags::ALL, also create links (formulas) for empty cells
+ setLink(nRow);
+ continue;
+ }
+ }
+ }
+};
+}
+
+void ScTable::TransposeClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ SCROW nCombinedStartRow, SCROW nRowDestOffset, ScTable* pTransClip,
+ InsertDeleteFlags nFlags, bool bAsLink, bool bIncludeFiltered)
+{
+ bool bWasCut = rDocument.IsCutMode();
+
+ for (SCCOL nCol : GetWritableColumnsRange(nCol1, nCol2))
+ {
+ std::vector<SCROW> aFilteredRows;
+
+ TransClipHandler aFunc(*pTransClip, *this, nTab, nCol1, nCol, nRow1, nRow2,
+ nCombinedStartRow, nRowDestOffset, bAsLink, bWasCut, nFlags,
+ bIncludeFiltered, aFilteredRows);
+
+ const sc::CellStoreType& rCells = aCol[nCol].maCells;
+
+ // Loop through all rows by iterator and call aFunc operators
+ sc::ParseAll(rCells.begin(), rCells, nRow1, nRow2, aFunc,
+ aFunc);
+
+ // Attributes
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ TransposeColPatterns(pTransClip, nCol1, nCol, nRow1, nRow2, nCombinedStartRow,
+ bIncludeFiltered, aFilteredRows, nRowDestOffset);
+
+ // Cell Notes - fdo#68381 paste cell notes on Transpose
+ if ((nFlags & InsertDeleteFlags::NOTE) && rDocument.HasColNotes(nCol, nTab))
+ TransposeColNotes(pTransClip, nCol1, nCol, nRow1, nRow2, nCombinedStartRow,
+ bIncludeFiltered, nRowDestOffset);
+ }
+}
+
+static void lcl_SetTransposedPatternInRows(ScTable* pTransClip, SCROW nAttrRow1, SCROW nAttrRow2,
+ SCCOL nCol1, SCROW nRow1, SCROW nCombinedStartRow, SCCOL nCol,
+ const ScPatternAttr& rPatternAttr, bool bIncludeFiltered,
+ const std::vector<SCROW>& rFilteredRows,
+ SCROW nRowDestOffset)
+{
+ for (SCROW nRow = nAttrRow1; nRow <= nAttrRow2; nRow++)
+ {
+ size_t nFilteredRowAdjustment = 0;
+ if (!bIncludeFiltered)
+ {
+ // aFilteredRows is sorted thus lower_bound() can be used.
+ // lower_bound() has a logarithmic complexity O(log(n))
+ auto itRow1 = std::lower_bound(rFilteredRows.begin(), rFilteredRows.end(), nRow1);
+ auto itRow = std::lower_bound(rFilteredRows.begin(), rFilteredRows.end(), nRow);
+ bool bRefRowIsFiltered = itRow != rFilteredRows.end() && *itRow == nRow;
+ if (bRefRowIsFiltered)
+ continue;
+
+ // How many filtered rows are between the formula cell and the reference?
+ // distance() has a constant complexity O(1) for vectors
+ nFilteredRowAdjustment = std::distance(itRow1, itRow);
+ }
+
+ pTransClip->SetPattern(
+ static_cast<SCCOL>(nCol1 + nRow - nRow1 - nFilteredRowAdjustment + nRowDestOffset),
+ static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), rPatternAttr);
+ }
+}
+
+void ScTable::TransposeColPatterns(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1,
+ SCROW nRow2, SCROW nCombinedStartRow, bool bIncludeFiltered,
+ const std::vector<SCROW>& rFilteredRows, SCROW nRowDestOffset)
+{
+ SCROW nAttrRow1 = {}; // spurious -Werror=maybe-uninitialized
+ SCROW nAttrRow2 = {}; // spurious -Werror=maybe-uninitialized
+ const ScPatternAttr* pPattern;
+ std::unique_ptr<ScAttrIterator> pAttrIter(aCol[nCol].CreateAttrIterator( nRow1, nRow2 ));
+ while ( (pPattern = pAttrIter->Next( nAttrRow1, nAttrRow2 )) != nullptr )
+ {
+ if ( !IsDefaultItem( pPattern ) )
+ {
+ const SfxItemSet& rSet = pPattern->GetItemSet();
+ if ( rSet.GetItemState( ATTR_MERGE, false ) == SfxItemState::DEFAULT &&
+ rSet.GetItemState( ATTR_MERGE_FLAG, false ) == SfxItemState::DEFAULT &&
+ rSet.GetItemState( ATTR_BORDER, false ) == SfxItemState::DEFAULT )
+ {
+ // Set pattern in cells from nAttrRow1 to nAttrRow2
+ // no borders or merge items involved - use pattern as-is
+ lcl_SetTransposedPatternInRows(pTransClip, nAttrRow1, nAttrRow2, nCol1, nRow1,
+ nCombinedStartRow, nCol, *pPattern,
+ bIncludeFiltered, rFilteredRows, nRowDestOffset);
+ }
+ else
+ {
+ // transpose borders and merge values, remove merge flags (refreshed after pasting)
+ ScPatternAttr aNewPattern( *pPattern );
+ SfxItemSet& rNewSet = aNewPattern.GetItemSet();
+
+ const SvxBoxItem& rOldBox = rSet.Get(ATTR_BORDER);
+ if ( rOldBox.GetTop() || rOldBox.GetBottom() || rOldBox.GetLeft() || rOldBox.GetRight() )
+ {
+ SvxBoxItem aNew( ATTR_BORDER );
+ aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT );
+ aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP );
+ aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT );
+ aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM );
+ aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT );
+ aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP );
+ aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT );
+ aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM );
+ rNewSet.Put( aNew );
+ }
+
+ const ScMergeAttr& rOldMerge = rSet.Get(ATTR_MERGE);
+ if (rOldMerge.IsMerged())
+ rNewSet.Put( ScMergeAttr( std::min(
+ static_cast<SCCOL>(rOldMerge.GetRowMerge()),
+ static_cast<SCCOL>(rDocument.MaxCol()+1 - (nAttrRow2-nRow1))),
+ std::min(
+ static_cast<SCROW>(rOldMerge.GetColMerge()),
+ static_cast<SCROW>(rDocument.MaxRow()+1 - (nCol-nCol1)))));
+ const ScMergeFlagAttr& rOldFlag = rSet.Get(ATTR_MERGE_FLAG);
+ if (rOldFlag.IsOverlapped())
+ {
+ ScMF nNewFlags = rOldFlag.GetValue() & ~ScMF( ScMF::Hor | ScMF::Ver );
+ if ( nNewFlags != ScMF::NONE )
+ rNewSet.Put( ScMergeFlagAttr( nNewFlags ) );
+ else
+ rNewSet.ClearItem( ATTR_MERGE_FLAG );
+ }
+
+ // Set pattern in cells from nAttrRow1 to nAttrRow2
+ lcl_SetTransposedPatternInRows(pTransClip, nAttrRow1, nAttrRow2, nCol1, nRow1,
+ nCombinedStartRow, nCol, aNewPattern,
+ bIncludeFiltered, rFilteredRows, nRowDestOffset);
+ }
+ }
+ }
+}
+
+void ScTable::TransposeColNotes(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1,
+ SCROW nRow2, SCROW nCombinedStartRow, bool bIncludeFiltered,
+ SCROW nRowDestOffset)
+{
+ sc::CellNoteStoreType::const_iterator itBlk = aCol[nCol].maCellNotes.begin(), itBlkEnd = aCol[nCol].maCellNotes.end();
+
+ // Locate the top row position.
+ size_t nOffsetInBlock = 0;
+ size_t nBlockStart = 0, nBlockEnd = 0, nRowPos = static_cast<size_t>(nRow1);
+ for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd)
+ {
+ nBlockEnd = nBlockStart + itBlk->size;
+ if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
+ {
+ // Found.
+ nOffsetInBlock = nRowPos - nBlockStart;
+ break;
+ }
+ }
+
+ if (itBlk == itBlkEnd)
+ // Specified range found
+ return;
+
+ nRowPos = static_cast<size_t>(nRow2); // End row position.
+ SCCOL nFilteredRows = 0;
+
+ // Keep processing until we hit the end row position.
+ sc::cellnote_block::const_iterator itData, itDataEnd;
+ for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
+ {
+ nBlockEnd = nBlockStart + itBlk->size;
+
+ if (itBlk->data)
+ {
+ itData = sc::cellnote_block::begin(*itBlk->data);
+ std::advance(itData, nOffsetInBlock);
+
+ // selected area is smaller than the iteration block
+ if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
+ {
+ // This block contains the end row. Only process partially.
+ size_t nOffsetEnd = nRowPos - nBlockStart + 1;
+ itDataEnd = sc::cellnote_block::begin(*itBlk->data);
+ std::advance(itDataEnd, nOffsetEnd);
+ size_t curRow = nBlockStart + nOffsetInBlock;
+ for (; itData != itDataEnd; ++itData, ++curRow)
+ {
+ bool bFiltered = this->RowFiltered(curRow, nullptr, nullptr);
+ if (!bIncludeFiltered && bFiltered)
+ {
+ nFilteredRows++;
+ continue;
+ }
+
+ ScAddress aDestPos(
+ static_cast<SCCOL>(nCol1 + curRow - nRow1 - nFilteredRows + nRowDestOffset),
+ static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), pTransClip->nTab);
+ pTransClip->rDocument.ReleaseNote(aDestPos);
+ ScPostIt* pNote = *itData;
+ if (pNote)
+ {
+ std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), pTransClip->rDocument, aDestPos, true );
+ pTransClip->rDocument.SetNote(aDestPos, std::move(pClonedNote));
+ }
+ }
+ break; // we reached the last valid block
+ }
+ else
+ {
+ itDataEnd = sc::cellnote_block::end(*itBlk->data);
+ size_t curRow = nBlockStart + nOffsetInBlock;
+ for (; itData != itDataEnd; ++itData, ++curRow)
+ {
+ bool bFiltered = this->RowFiltered(curRow, nullptr, nullptr);
+ if (!bIncludeFiltered && bFiltered)
+ {
+ nFilteredRows++;
+ continue;
+ }
+
+ ScAddress aDestPos(
+ static_cast<SCCOL>(nCol1 + curRow - nRow1 - nFilteredRows + nRowDestOffset),
+ static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), pTransClip->nTab);
+ pTransClip->rDocument.ReleaseNote(aDestPos);
+ ScPostIt* pNote = *itData;
+ if (pNote)
+ {
+ std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), pTransClip->rDocument, aDestPos, true );
+ pTransClip->rDocument.SetNote(aDestPos, std::move(pClonedNote));
+ }
+ }
+ }
+ }
+ else // remove dest notes for rows without notes
+ {
+ for (size_t curRow = nBlockStart + nOffsetInBlock;
+ curRow <= nBlockEnd && curRow <= nRowPos; ++curRow)
+ {
+ bool bFiltered = this->RowFiltered(curRow, nullptr, nullptr);
+ if (!bIncludeFiltered && bFiltered && curRow < nBlockEnd)
+ {
+ nFilteredRows++;
+ continue;
+ }
+
+ ScAddress aDestPos(
+ static_cast<SCCOL>(nCol1 + curRow - nRow1 - nFilteredRows + nRowDestOffset),
+ static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), pTransClip->nTab);
+ pTransClip->rDocument.ReleaseNote(aDestPos);
+ }
+ }
+ }
+}
+
+ScColumn* ScTable::FetchColumn( SCCOL nCol )
+{
+ if (!ValidCol(nCol))
+ return nullptr;
+
+ return &CreateColumnIfNotExists(nCol);
+}
+
+const ScColumn* ScTable::FetchColumn( SCCOL nCol ) const
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return nullptr;
+
+ return &aCol[nCol];
+}
+
+void ScTable::StartListeners( sc::StartListeningContext& rCxt, bool bAll )
+{
+ std::shared_ptr<const sc::ColumnSet> pColSet = rCxt.getColumnSet();
+ if (!pColSet)
+ {
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].StartListeners(rCxt, bAll);
+ }
+ else if (pColSet->hasTab( nTab))
+ {
+ std::vector<SCCOL> aColumns;
+ pColSet->getColumns( nTab, aColumns);
+ for (auto i : aColumns)
+ {
+ if (0 <= i && i < aCol.size())
+ aCol[i].StartListeners(rCxt, bAll);
+ }
+ }
+}
+
+void ScTable::AttachFormulaCells(
+ sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ aCol[nCol].AttachFormulaCells(rCxt, nRow1, nRow2);
+}
+
+void ScTable::DetachFormulaCells(
+ sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2);
+}
+
+void ScTable::SetDirtyFromClip(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
+ if (nCol2 > rDocument.MaxCol()) nCol2 = rDocument.MaxCol();
+ if (nRow2 > rDocument.MaxRow()) nRow2 = rDocument.MaxRow();
+ if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ aCol[i].SetDirtyFromClip(nRow1, nRow2, rBroadcastSpans);
+}
+
+void ScTable::StartListeningFormulaCells(
+ sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
+ if (nCol2 > rDocument.MaxCol()) nCol2 = rDocument.MaxCol();
+ if (nRow2 > rDocument.MaxRow()) nRow2 = rDocument.MaxRow();
+ if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ aCol[i].StartListeningFormulaCells(rStartCxt, rEndCxt, nRow1, nRow2);
+}
+
+void ScTable::CopyToTable(
+ sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab, const ScMarkData* pMarkData,
+ bool bAsLink, bool bColRowFlags, bool bGlobalNamesToLocal, bool bCopyCaptions )
+{
+ if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
+ return;
+
+ const bool bToUndoDoc = pDestTab->rDocument.IsUndo();
+ const bool bFromUndoDoc = rDocument.IsUndo();
+
+ if (bToUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB) && nCol2 >= aCol.size())
+ {
+ // tdf#154044: Copy also the default column data
+ aDefaultColData.AttrArray().CopyArea(nRow1, nRow2, 0,
+ pDestTab->aDefaultColData.AttrArray());
+ }
+
+ if ((bToUndoDoc || bFromUndoDoc) && (nFlags & InsertDeleteFlags::CONTENTS) && mpRangeName)
+ {
+ // Copying formulas may create sheet-local named expressions on the
+ // destination sheet. Add existing to Undo first.
+ // During Undo restore the previous named expressions.
+ pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName())));
+ if (!pDestTab->rDocument.IsClipOrUndo())
+ {
+ ScDocShell* pDocSh = pDestTab->rDocument.GetDocumentShell();
+ if (pDocSh)
+ pDocSh->SetAreasChangedNeedBroadcast();
+ }
+ }
+
+ if (nFlags != InsertDeleteFlags::NONE)
+ {
+ InsertDeleteFlags nTempFlags( nFlags &
+ ~InsertDeleteFlags( InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES));
+ // tdf#102364 - in some pathological cases CopyToTable() replacing cells with new cells
+ // can lead to repetitive splitting and rejoining of the same formula group, which can get
+ // quadratically expensive with large groups. So do the grouping just once at the end.
+ sc::DelayFormulaGroupingSwitch delayGrouping( pDestTab->rDocument, true );
+ for (SCCOL i = nCol1; i <= ClampToAllocatedColumns(nCol2); i++)
+ aCol[i].CopyToColumn(rCxt, nRow1, nRow2, bToUndoDoc ? nFlags : nTempFlags, bMarked,
+ pDestTab->CreateColumnIfNotExists(i), pMarkData, bAsLink, bGlobalNamesToLocal);
+ // tdf#154044: Restore from the default column data
+ if (bFromUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB) && nCol2 >= aCol.size())
+ {
+ aDefaultColData.AttrArray().CopyArea(nRow1, nRow2, 0,
+ pDestTab->aDefaultColData.AttrArray());
+ SCCOL nMaxSetDefault = pDestTab->ClampToAllocatedColumns(nCol2);
+ for (SCCOL i = aCol.size(); i <= nMaxSetDefault; i++)
+ aDefaultColData.AttrArray().CopyArea(nRow1, nRow2, 0,
+ pDestTab->aCol[i].AttrArray());
+ }
+ }
+
+ if (!bColRowFlags) // Column widths/Row heights/Flags
+ return;
+
+ if (bToUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB))
+ {
+ pDestTab->mpCondFormatList.reset(new ScConditionalFormatList(pDestTab->rDocument, *mpCondFormatList));
+ }
+
+ if (pDBDataNoName)
+ {
+ std::unique_ptr<ScDBData> pNewDBData(new ScDBData(*pDBDataNoName));
+ SCCOL aCol1, aCol2;
+ SCROW aRow1, aRow2;
+ SCTAB aTab;
+ pNewDBData->GetArea(aTab, aCol1, aRow1, aCol2, aRow2);
+ pNewDBData->MoveTo(pDestTab->nTab, aCol1, aRow1, aCol2, aRow2);
+ pDestTab->SetAnonymousDBData(std::move(pNewDBData));
+ }
+ // Charts have to be adjusted when hide/show
+ ScChartListenerCollection* pCharts = pDestTab->rDocument.GetChartListenerCollection();
+
+ bool bFlagChange = false;
+
+ bool bWidth = (nRow1==0 && nRow2==rDocument.MaxRow() && mpColWidth && pDestTab->mpColWidth);
+ bool bHeight = (nCol1==0 && nCol2==rDocument.MaxCol() && mpRowHeights && pDestTab->mpRowHeights);
+
+ if (bWidth || bHeight)
+ {
+ if (bWidth)
+ {
+ auto destTabColWidthIt = pDestTab->mpColWidth->begin() + nCol1;
+ auto thisTabColWidthIt = mpColWidth->begin() + nCol1;
+ pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2);
+ pDestTab->mpColFlags->CopyFrom(*mpColFlags, nCol1, nCol2);
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ {
+ bool bThisHidden = ColHidden(i);
+ bool bHiddenChange = (pDestTab->ColHidden(i) != bThisHidden);
+ bool bChange = bHiddenChange || (*destTabColWidthIt != *thisTabColWidthIt);
+ pDestTab->SetColHidden(i, i, bThisHidden);
+ //TODO: collect changes?
+ if (bHiddenChange && pCharts)
+ pCharts->SetRangeDirty(ScRange( i, 0, nTab, i, rDocument.MaxRow(), nTab ));
+
+ if (bChange)
+ bFlagChange = true;
+
+ ++destTabColWidthIt;
+ ++thisTabColWidthIt;
+ }
+ pDestTab->SetColManualBreaks( std::set(maColManualBreaks) );
+ }
+
+ if (bHeight)
+ {
+ bool bChange = pDestTab->GetRowHeight(nRow1, nRow2) != GetRowHeight(nRow1, nRow2);
+
+ if (bChange)
+ bFlagChange = true;
+
+ pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0);
+ pDestTab->pRowFlags->CopyFrom(*pRowFlags, nRow1, nRow2);
+
+ // Hidden flags.
+ for (SCROW i = nRow1; i <= nRow2; ++i)
+ {
+ SCROW nLastRow;
+ bool bHidden = RowHidden(i, nullptr, &nLastRow);
+ if (nLastRow >= nRow2)
+ // the last row shouldn't exceed the upper bound the caller specified.
+ nLastRow = nRow2;
+
+ bool bHiddenChanged = pDestTab->SetRowHidden(i, nLastRow, bHidden);
+ if (bHiddenChanged && pCharts)
+ // Hidden flags differ.
+ pCharts->SetRangeDirty(ScRange(0, i, nTab, rDocument.MaxCol(), nLastRow, nTab));
+
+ if (bHiddenChanged)
+ bFlagChange = true;
+
+ // Jump to the last row of the identical flag segment.
+ i = nLastRow;
+ }
+
+ // Filtered flags.
+ for (SCROW i = nRow1; i <= nRow2; ++i)
+ {
+ SCROW nLastRow;
+ bool bFiltered = RowFiltered(i, nullptr, &nLastRow);
+ if (nLastRow >= nRow2)
+ // the last row shouldn't exceed the upper bound the caller specified.
+ nLastRow = nRow2;
+ pDestTab->SetRowFiltered(i, nLastRow, bFiltered);
+ i = nLastRow;
+ }
+ pDestTab->SetRowManualBreaks( std::set(maRowManualBreaks) );
+ }
+ }
+
+ if (bFlagChange)
+ pDestTab->InvalidatePageBreaks();
+
+ if(nFlags & InsertDeleteFlags::ATTRIB)
+ {
+ pDestTab->mpCondFormatList->DeleteArea(nCol1, nRow1, nCol2, nRow2);
+ pDestTab->CopyConditionalFormat(nCol1, nRow1, nCol2, nRow2, 0, 0, this);
+ }
+
+ if(nFlags & InsertDeleteFlags::OUTLINE) // also only when bColRowFlags
+ pDestTab->SetOutlineTable( pOutlineTable.get() );
+
+ if (nFlags & InsertDeleteFlags::SPARKLINES)
+ {
+ CopySparklinesToTable(nCol1, nRow1, nCol2, nRow2, pDestTab);
+ }
+
+ if (!bToUndoDoc && bCopyCaptions && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)))
+ {
+ bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ CopyCaptionsToTable( nCol1, nRow1, nCol2, nRow2, pDestTab, bCloneCaption);
+ }
+}
+
+void ScTable::CopySparklinesToTable(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab)
+{
+ if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
+ return;
+
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ {
+ aCol[i].CopyCellSparklinesToDocument(nRow1, nRow2, pDestTab->CreateColumnIfNotExists(i));
+ }
+}
+
+void ScTable::CopyCaptionsToTable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab,
+ bool bCloneCaption )
+{
+ if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
+ return;
+
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ {
+ aCol[i].CopyCellNotesToDocument(nRow1, nRow2, pDestTab->CreateColumnIfNotExists(i), bCloneCaption);
+ pDestTab->aCol[i].UpdateNoteCaptions(nRow1, nRow2, false /* address unchanged from initial create */);
+ }
+}
+
+void ScTable::UndoToTable(
+ sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab )
+{
+ if (!(ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)))
+ return;
+
+ bool bWidth = (nRow1==0 && nRow2==rDocument.MaxRow() && mpColWidth && pDestTab->mpColWidth);
+ bool bHeight = (nCol1==0 && nCol2==rDocument.MaxCol() && mpRowHeights && pDestTab->mpRowHeights);
+
+ if ((nFlags & InsertDeleteFlags::CONTENTS) && mpRangeName)
+ {
+ // Undo sheet-local named expressions created during copying
+ // formulas. If mpRangeName is not set then the Undo wasn't even
+ // set to an empty ScRangeName map so don't "undo" that.
+ pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName())));
+ if (!pDestTab->rDocument.IsClipOrUndo())
+ {
+ ScDocShell* pDocSh = pDestTab->rDocument.GetDocumentShell();
+ if (pDocSh)
+ pDocSh->SetAreasChangedNeedBroadcast();
+ }
+
+ }
+
+ for ( SCCOL i = 0; i < aCol.size(); i++)
+ {
+ auto& rDestCol = pDestTab->CreateColumnIfNotExists(i);
+ if ( i >= nCol1 && i <= nCol2 )
+ aCol[i].UndoToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, rDestCol);
+ else
+ aCol[i].CopyToColumn(rCxt, 0, rDocument.MaxRow(), InsertDeleteFlags::FORMULA, false, rDestCol);
+ }
+
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ pDestTab->mpCondFormatList.reset(new ScConditionalFormatList(pDestTab->rDocument, *mpCondFormatList));
+
+ if (!(bWidth||bHeight))
+ return;
+
+ if (bWidth)
+ {
+ pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2);
+ pDestTab->SetColManualBreaks( std::set(maColManualBreaks) );
+ }
+ if (bHeight)
+ {
+ pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0);
+ pDestTab->SetRowManualBreaks( std::set(maRowManualBreaks) );
+ }
+}
+
+void ScTable::CopyUpdated( const ScTable* pPosTab, ScTable* pDestTab ) const
+{
+ pDestTab->CreateColumnIfNotExists(aCol.size()-1);
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].CopyUpdated( pPosTab->FetchColumn(i), pDestTab->aCol[i] );
+}
+
+void ScTable::InvalidateTableArea()
+{
+ bTableAreaValid = false;
+ bTableAreaVisibleValid = false;
+}
+
+void ScTable::InvalidatePageBreaks()
+{
+ mbPageBreaksValid = false;
+}
+
+void ScTable::CopyScenarioTo( ScTable* pDestTab ) const
+{
+ OSL_ENSURE( bScenario, "bScenario == FALSE" );
+
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].CopyScenarioTo( pDestTab->CreateColumnIfNotExists(i) );
+}
+
+void ScTable::CopyScenarioFrom( const ScTable* pSrcTab )
+{
+ OSL_ENSURE( bScenario, "bScenario == FALSE" );
+
+ SCCOL nEndCol = pSrcTab->aCol.size();
+ CreateColumnIfNotExists(nEndCol);
+ for (SCCOL i=0; i < nEndCol; i++)
+ aCol[i].CopyScenarioFrom( pSrcTab->aCol[i] );
+}
+
+void ScTable::MarkScenarioIn( ScMarkData& rDestMark, ScScenarioFlags nNeededBits ) const
+{
+ OSL_ENSURE( bScenario, "bScenario == FALSE" );
+
+ if ( ( nScenarioFlags & nNeededBits ) != nNeededBits ) // Are all Bits set?
+ return;
+
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].MarkScenarioIn( rDestMark );
+}
+
+bool ScTable::HasScenarioRange( const ScRange& rRange ) const
+{
+ OSL_ENSURE( bScenario, "bScenario == FALSE" );
+
+ ScRange aTabRange = rRange;
+ aTabRange.aStart.SetTab( nTab );
+ aTabRange.aEnd.SetTab( nTab );
+
+ const ScRangeList* pList = GetScenarioRanges();
+
+ if (pList)
+ {
+ for ( size_t j = 0, n = pList->size(); j < n; j++ )
+ {
+ const ScRange & rR = (*pList)[j];
+ if ( rR.Intersects( aTabRange ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScTable::InvalidateScenarioRanges()
+{
+ pScenarioRanges.reset();
+}
+
+const ScRangeList* ScTable::GetScenarioRanges() const
+{
+ OSL_ENSURE( bScenario, "bScenario == FALSE" );
+
+ if (!pScenarioRanges)
+ {
+ const_cast<ScTable*>(this)->pScenarioRanges.reset(new ScRangeList);
+ ScMarkData aMark(rDocument.GetSheetLimits());
+ MarkScenarioIn( aMark, ScScenarioFlags::NONE ); // always
+ aMark.FillRangeListWithMarks( pScenarioRanges.get(), false );
+ }
+ return pScenarioRanges.get();
+}
+
+bool ScTable::TestCopyScenarioTo( const ScTable* pDestTab ) const
+{
+ OSL_ENSURE( bScenario, "bScenario == FALSE" );
+
+ if (!pDestTab->IsProtected())
+ return true;
+
+ bool bOk = true;
+ for (SCCOL i=0; i < aCol.size() && bOk; i++)
+ bOk = aCol[i].TestCopyScenarioTo( pDestTab->aCol[i] );
+ return bOk;
+}
+
+bool ScTable::SetString( SCCOL nCol, SCROW nRow, SCTAB nTabP, const OUString& rString,
+ const ScSetStringParam * pParam )
+{
+ if (!ValidColRow(nCol,nRow))
+ {
+ return false;
+ }
+
+ return CreateColumnIfNotExists(nCol).SetString(
+ nRow, nTabP, rString, rDocument.GetAddressConvention(), pParam);
+}
+
+bool ScTable::SetEditText( SCCOL nCol, SCROW nRow, std::unique_ptr<EditTextObject> pEditText )
+{
+ if (!ValidColRow(nCol, nRow))
+ {
+ return false;
+ }
+
+ CreateColumnIfNotExists(nCol).SetEditText(nRow, std::move(pEditText));
+ return true;
+}
+
+void ScTable::SetEditText( SCCOL nCol, SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
+{
+ if (!ValidColRow(nCol, nRow))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetEditText(nRow, rEditText, pEditPool);
+}
+
+SCROW ScTable::GetFirstEditTextRow( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
+{
+ if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol2 < nCol1)
+ return -1;
+
+ if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow2 < nRow1)
+ return -1;
+
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ SCROW nFirst = rDocument.MaxRow()+1;
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ {
+ const ScColumn& rCol = aCol[i];
+ SCROW nThisFirst = -1;
+ if (const_cast<ScColumn&>(rCol).HasEditCells(nRow1, nRow2, nThisFirst))
+ {
+ if (nThisFirst == nRow1)
+ return nRow1;
+
+ if (nThisFirst < nFirst)
+ nFirst = nThisFirst;
+ }
+ }
+
+ return nFirst == (rDocument.MaxRow()+1) ? -1 : nFirst;
+}
+
+void ScTable::SetEmptyCell( SCCOL nCol, SCROW nRow )
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return;
+
+ aCol[nCol].Delete(nRow);
+}
+
+void ScTable::SetFormula(
+ SCCOL nCol, SCROW nRow, const ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram )
+{
+ if (!ValidColRow(nCol, nRow))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetFormula(nRow, rArray, eGram);
+}
+
+void ScTable::SetFormula(
+ SCCOL nCol, SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram )
+{
+ if (!ValidColRow(nCol, nRow))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetFormula(nRow, rFormula, eGram);
+}
+
+ScFormulaCell* ScTable::SetFormulaCell( SCCOL nCol, SCROW nRow, ScFormulaCell* pCell )
+{
+ if (!ValidColRow(nCol, nRow))
+ {
+ delete pCell;
+ return nullptr;
+ }
+
+ return CreateColumnIfNotExists(nCol).SetFormulaCell(nRow, pCell, sc::ConvertToGroupListening);
+}
+
+bool ScTable::SetFormulaCells( SCCOL nCol, SCROW nRow, std::vector<ScFormulaCell*>& rCells )
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ return CreateColumnIfNotExists(nCol).SetFormulaCells(nRow, rCells);
+}
+
+svl::SharedString ScTable::GetSharedString( SCCOL nCol, SCROW nRow ) const
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return svl::SharedString();
+
+ return aCol[nCol].GetSharedString(nRow);
+}
+
+void ScTable::SetValue( SCCOL nCol, SCROW nRow, const double& rVal )
+{
+ if (ValidColRow(nCol, nRow))
+ CreateColumnIfNotExists(nCol).SetValue(nRow, rVal);
+}
+
+void ScTable::SetRawString( SCCOL nCol, SCROW nRow, const svl::SharedString& rStr )
+{
+ if (ValidColRow(nCol, nRow))
+ CreateColumnIfNotExists(nCol).SetRawString(nRow, rStr);
+}
+
+OUString ScTable::GetString( SCCOL nCol, SCROW nRow, const ScInterpreterContext* pContext ) const
+{
+ if (ValidColRow(nCol,nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].GetString( nRow, pContext );
+ else
+ return OUString();
+}
+
+double* ScTable::GetValueCell( SCCOL nCol, SCROW nRow )
+{
+ if (!ValidColRow(nCol, nRow))
+ return nullptr;
+
+ return CreateColumnIfNotExists(nCol).GetValueCell(nRow);
+}
+
+OUString ScTable::GetInputString( SCCOL nCol, SCROW nRow, bool bForceSystemLocale ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].GetInputString( nRow, bForceSystemLocale );
+ else
+ return OUString();
+}
+
+double ScTable::GetValue( SCCOL nCol, SCROW nRow ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].GetValue( nRow );
+ return 0.0;
+}
+
+const EditTextObject* ScTable::GetEditText( SCCOL nCol, SCROW nRow ) const
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return nullptr;
+
+ return aCol[nCol].GetEditText(nRow);
+}
+
+void ScTable::RemoveEditTextCharAttribs( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr )
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return;
+
+ return aCol[nCol].RemoveEditTextCharAttribs(nRow, rAttr);
+}
+
+OUString ScTable::GetFormula( SCCOL nCol, SCROW nRow ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].GetFormula( nRow );
+ else
+ return OUString();
+}
+
+const ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, SCROW nRow ) const
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return nullptr;
+
+ return aCol[nCol].GetFormulaCell(nRow);
+}
+
+ScFormulaCell* ScTable::GetFormulaCell( SCCOL nCol, SCROW nRow )
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return nullptr;
+
+ return aCol[nCol].GetFormulaCell(nRow);
+}
+
+// Sparklines
+
+std::shared_ptr<sc::Sparkline> ScTable::GetSparkline(SCCOL nCol, SCROW nRow)
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return std::shared_ptr<sc::Sparkline>();
+
+ sc::SparklineCell* pSparklineCell = aCol[nCol].GetSparklineCell(nRow);
+ if (!pSparklineCell)
+ return std::shared_ptr<sc::Sparkline>();
+
+ return pSparklineCell->getSparkline();
+}
+
+sc::Sparkline* ScTable::CreateSparkline(SCCOL nCol, SCROW nRow, std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup)
+{
+ if (!ValidCol(nCol))
+ return nullptr;
+
+ ScColumn& rColumn = CreateColumnIfNotExists(nCol);
+
+ std::shared_ptr<sc::Sparkline> pSparkline(new sc::Sparkline(nCol, nRow, pSparklineGroup));
+ rColumn.CreateSparklineCell(nRow, pSparkline);
+
+ return pSparkline.get();
+}
+
+bool ScTable::DeleteSparkline(SCCOL nCol, SCROW nRow)
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return false;
+
+ aCol[nCol].DeleteSparkline(nRow);
+
+ return true;
+}
+
+sc::SparklineList& ScTable::GetSparklineList()
+{
+ return maSparklineList;
+}
+
+// Notes
+
+std::unique_ptr<ScPostIt> ScTable::ReleaseNote( SCCOL nCol, SCROW nRow )
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return nullptr;
+
+ return aCol[nCol].ReleaseNote(nRow);
+}
+
+ScPostIt* ScTable::GetNote( SCCOL nCol, SCROW nRow )
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return nullptr;
+ return aCol[nCol].GetCellNote(nRow);
+}
+
+void ScTable::SetNote( SCCOL nCol, SCROW nRow, std::unique_ptr<ScPostIt> pNote )
+{
+ if (!ValidColRow(nCol, nRow))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetCellNote(nRow, std::move(pNote));
+}
+
+size_t ScTable::GetNoteCount( SCCOL nCol ) const
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return 0;
+
+ return aCol[nCol].GetNoteCount();
+}
+
+SCROW ScTable::GetNotePosition( SCCOL nCol, size_t nIndex ) const
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return -1;
+
+ return aCol[nCol].GetNotePosition(nIndex);
+}
+
+void ScTable::CreateAllNoteCaptions()
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].CreateAllNoteCaptions();
+}
+
+void ScTable::ForgetNoteCaptions( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bPreserveData )
+{
+ if (!ValidCol(nCol1) || !ValidCol(nCol2))
+ return;
+ if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ aCol[i].ForgetNoteCaptions(nRow1, nRow2, bPreserveData);
+}
+
+void ScTable::CommentNotifyAddressChange( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ // Only in use in kit mode for now, but looks to me a good idea to revisit why (since OOo times)
+ // on deleting/inserting a column that we generate all the captions, while on deleting/inserting
+ // a row we do not. Presumably we should skip generating captions if we don't have to.
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (!ValidCol(nCol1) || !ValidCol(nCol2))
+ return;
+ if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ aCol[i].CommentNotifyAddressChange(nRow1, nRow2);
+}
+
+void ScTable::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
+{
+ for (SCCOL nCol = 0; nCol < aCol.size(); ++nCol)
+ aCol[nCol].GetAllNoteEntries(rNotes);
+}
+
+void ScTable::GetNotesInRange( const ScRange& rRange, std::vector<sc::NoteEntry>& rNotes ) const
+{
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col());
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol)
+ {
+ aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes);
+ }
+}
+
+CommentCaptionState ScTable::GetAllNoteCaptionsState(const ScRange& rRange, std::vector<sc::NoteEntry>& rNotes )
+{
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ bool bIsFirstNoteShownState = true; // because of error: -Werror=maybe-uninitialized
+ bool bFirstControl = true;
+
+ ScTable* pTab = rDocument.FetchTable(nTab);
+ assert(pTab);
+ const SCCOL nEndCol = pTab->ClampToAllocatedColumns(rRange.aEnd.Col());
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol)
+ {
+ if (bFirstControl && rDocument.HasColNotes(nCol, nTab)) // detect status of first note caption
+ {
+ aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes);
+ bIsFirstNoteShownState = rNotes.begin()->mpNote->IsCaptionShown();
+ bFirstControl = false;
+ }
+
+ if (rDocument.HasColNotes(nCol, nTab))
+ {
+ aCol[nCol].GetNotesInRange(nStartRow, nEndRow, rNotes);
+
+ bool bIsMixedState = std::any_of(rNotes.begin(), rNotes.end(), [bIsFirstNoteShownState](const sc::NoteEntry& rNote) {
+ // compare the first note caption with others
+ return bIsFirstNoteShownState != rNote.mpNote->IsCaptionShown(); });
+ if (bIsMixedState)
+ return CommentCaptionState::MIXED;
+ }
+ }
+ return bIsFirstNoteShownState ? CommentCaptionState::ALLSHOWN : CommentCaptionState::ALLHIDDEN;
+}
+
+void ScTable::GetUnprotectedCells( ScRangeList& rRangeList ) const
+{
+ for (auto const & pCol : aCol)
+ pCol->GetUnprotectedCells(0, rDocument.MaxRow(), rRangeList);
+}
+
+bool ScTable::ContainsNotesInRange( const ScRange& rRange ) const
+{
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col());
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; ++nCol)
+ {
+ bool bContainsNote = !aCol[nCol].IsNotesEmptyBlock(nStartRow, nEndRow);
+ if(bContainsNote)
+ return true;
+ }
+
+ return false;
+}
+
+CellType ScTable::GetCellType( SCCOL nCol, SCROW nRow ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].GetCellType( nRow );
+ return CELLTYPE_NONE;
+}
+
+ScRefCellValue ScTable::GetCellValue( SCCOL nCol, SCROW nRow ) const
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return ScRefCellValue();
+
+ return aCol[nCol].GetCellValue(nRow);
+}
+
+ScRefCellValue ScTable::GetCellValue( SCCOL nCol, sc::ColumnBlockPosition& rBlockPos, SCROW nRow )
+{
+ if (!ValidColRow(nCol, nRow) || nCol >= GetAllocatedColumnsCount())
+ return ScRefCellValue();
+
+ return aCol[nCol].GetCellValue(rBlockPos, nRow);
+}
+
+void ScTable::GetFirstDataPos(SCCOL& rCol, SCROW& rRow) const
+{
+ rCol = 0;
+ rRow = rDocument.MaxRow()+1;
+ while (rCol < (aCol.size() - 1) && aCol[rCol].IsEmptyData() )
+ ++rCol;
+ SCCOL nCol = rCol;
+ while (nCol < aCol.size() && rRow > 0)
+ {
+ if (!aCol[nCol].IsEmptyData())
+ rRow = ::std::min( rRow, aCol[nCol].GetFirstDataPos());
+ ++nCol;
+ }
+}
+
+void ScTable::GetLastDataPos(SCCOL& rCol, SCROW& rRow) const
+{
+ rCol = aCol.size() - 1;
+ rRow = 0;
+ while (aCol[rCol].IsEmptyData() && (rCol > 0))
+ rCol--;
+ SCCOL nCol = rCol;
+ while (nCol >= 0 && rRow < rDocument.MaxRow())
+ rRow = ::std::max( rRow, aCol[nCol--].GetLastDataPos());
+}
+
+bool ScTable::HasData( SCCOL nCol, SCROW nRow ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].HasDataAt( nRow );
+ else
+ return false;
+}
+
+bool ScTable::HasStringData( SCCOL nCol, SCROW nRow ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].HasStringData( nRow );
+ else
+ return false;
+}
+
+bool ScTable::HasValueData( SCCOL nCol, SCROW nRow ) const
+{
+ if (ValidColRow(nCol, nRow) && nCol < GetAllocatedColumnsCount())
+ return aCol[nCol].HasValueData( nRow );
+ else
+ return false;
+}
+
+bool ScTable::HasStringCells( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow ) const
+{
+ if (ValidCol(nEndCol))
+ {
+ nEndCol = ClampToAllocatedColumns(nEndCol);
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++)
+ if (aCol[nCol].HasStringCells(nStartRow, nEndRow))
+ return true;
+ }
+
+ return false;
+}
+
+void ScTable::SetDirtyVar()
+{
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].SetDirtyVar();
+}
+
+void ScTable::CheckVectorizationState()
+{
+ sc::AutoCalcSwitch aACSwitch(rDocument, false);
+
+ for (SCCOL i = 0; i < aCol.size(); i++)
+ aCol[i].CheckVectorizationState();
+}
+
+void ScTable::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
+{
+ sc::AutoCalcSwitch aACSwitch(rDocument, false);
+
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].SetAllFormulasDirty(rCxt);
+}
+
+void ScTable::SetDirty( const ScRange& rRange, ScColumn::BroadcastMode eMode )
+{
+ sc::AutoCalcSwitch aSwitch(rDocument, false);
+ SCCOL nCol2 = rRange.aEnd.Col();
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++)
+ aCol[i].SetDirty(rRange.aStart.Row(), rRange.aEnd.Row(), eMode);
+}
+
+void ScTable::SetTableOpDirty( const ScRange& rRange )
+{
+ sc::AutoCalcSwitch aSwitch(rDocument, false);
+ const SCCOL nCol2 = ClampToAllocatedColumns(rRange.aEnd.Col());
+ for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++)
+ aCol[i].SetTableOpDirty( rRange );
+}
+
+void ScTable::SetDirtyAfterLoad()
+{
+ sc::AutoCalcSwitch aSwitch(rDocument, false);
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].SetDirtyAfterLoad();
+}
+
+void ScTable::SetDirtyIfPostponed()
+{
+ sc::AutoCalcSwitch aSwitch(rDocument, false);
+ ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].SetDirtyIfPostponed();
+}
+
+void ScTable::BroadcastRecalcOnRefMove()
+{
+ sc::AutoCalcSwitch aSwitch(rDocument, false);
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].BroadcastRecalcOnRefMove();
+}
+
+bool ScTable::BroadcastBroadcasters( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, SfxHintId nHint )
+{
+ bool bBroadcasted = false;
+ sc::AutoCalcSwitch aSwitch(rDocument, false);
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ bBroadcasted |= aCol[nCol].BroadcastBroadcasters( nRow1, nRow2, nHint);
+ return bBroadcasted;
+}
+
+void ScTable::SetLoadingMedium(bool bLoading)
+{
+ mpRowHeights->enableTreeSearch(!bLoading);
+}
+
+void ScTable::CalcAll()
+{
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].CalcAll();
+
+ mpCondFormatList->CalcAll();
+}
+
+void ScTable::CompileAll( sc::CompileFormulaContext& rCxt )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].CompileAll(rCxt);
+
+ if(mpCondFormatList)
+ mpCondFormatList->CompileAll();
+}
+
+void ScTable::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
+{
+ if (mpRangeName)
+ mpRangeName->CompileUnresolvedXML(rCxt);
+
+ for (SCCOL i=0; i < aCol.size(); i++)
+ {
+ aCol[i].CompileXML(rCxt, rProgress);
+ }
+
+ if(mpCondFormatList)
+ mpCondFormatList->CompileXML();
+}
+
+bool ScTable::CompileErrorCells( sc::CompileFormulaContext& rCxt, FormulaError nErrCode )
+{
+ bool bCompiled = false;
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ {
+ if (aCol[i].CompileErrorCells(rCxt, nErrCode))
+ bCompiled = true;
+ }
+
+ return bCompiled;
+}
+
+void ScTable::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].CalcAfterLoad(rCxt, bStartListening);
+}
+
+void ScTable::ResetChanged( const ScRange& rRange )
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = ClampToAllocatedColumns(rRange.aEnd.Col());
+ SCROW nEndRow = rRange.aEnd.Row();
+
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ aCol[nCol].ResetChanged(nStartRow, nEndRow);
+}
+
+// Attribute
+
+const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW nRow, sal_uInt16 nWhich ) const
+{
+ if (!ValidColRow(nCol, nRow))
+ return nullptr;
+ return &ColumnData(nCol).GetAttr( nRow, nWhich );
+}
+
+const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW nRow, sal_uInt16 nWhich, SCROW& nStartRow, SCROW& nEndRow ) const
+{
+ if (!ValidColRow(nCol, nRow))
+ return nullptr;
+ return &ColumnData(nCol).GetAttr( nRow, nWhich, nStartRow, nEndRow );
+}
+
+sal_uInt32 ScTable::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
+{
+ if (ValidColRow(rPos.Col(), rPos.Row()))
+ return ColumnData(rPos.Col()).GetNumberFormat(rContext, rPos.Row());
+ return 0;
+}
+
+sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW nRow ) const
+{
+ return GetNumberFormat(rDocument.GetNonThreadedContext(), ScAddress(nCol, nRow, nTab));
+}
+
+sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
+{
+ if (!ValidCol(nCol) || !ValidRow(nStartRow) || !ValidRow(nEndRow))
+ return 0;
+
+ return ColumnData(nCol).GetNumberFormat(nStartRow, nEndRow);
+}
+
+void ScTable::SetNumberFormat( SCCOL nCol, SCROW nRow, sal_uInt32 nNumberFormat )
+{
+ if (!ValidColRow(nCol, nRow))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetNumberFormat(nRow, nNumberFormat);
+}
+
+const ScPatternAttr* ScTable::GetPattern( SCCOL nCol, SCROW nRow ) const
+{
+ if (!ValidColRow(nCol,nRow))
+ return nullptr;
+ return ColumnData(nCol).GetPattern( nRow );
+}
+
+const ScPatternAttr* ScTable::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( ValidColRow( nCol, nStartRow ) && ValidRow( nEndRow ) && (nStartRow <= nEndRow))
+ return ColumnData(nCol).GetMostUsedPattern( nStartRow, nEndRow );
+ return nullptr;
+}
+
+bool ScTable::HasAttrib( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, HasAttrFlags nMask ) const
+{
+ for(SCCOL nCol = nCol1; nCol <= nCol2 && nCol < aCol.size(); ++nCol )
+ if( aCol[nCol].HasAttrib( nRow1, nRow2, nMask ))
+ return true;
+ if( nCol2 >= aCol.size())
+ return aDefaultColData.HasAttrib( nRow1, nRow2, nMask );
+ return false;
+}
+
+bool ScTable::HasAttrib( SCCOL nCol, SCROW nRow, HasAttrFlags nMask, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ return ColumnData(nCol).HasAttrib( nRow, nMask, nStartRow, nEndRow );
+}
+
+bool ScTable::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const
+{
+ std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans();
+
+ for (const sc::ColRowSpan & aSpan : aSpans)
+ {
+ for (SCCOLROW j = aSpan.mnStart; j <= aSpan.mnEnd; ++j)
+ {
+ if (aCol[j].HasAttribSelection(rMark, nMask))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScTable::ExtendMerge( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow,
+ bool bRefresh )
+{
+ if (!(ValidCol(nStartCol) && ValidCol(rEndCol)))
+ {
+ OSL_FAIL("ScTable::ExtendMerge: invalid column number");
+ return false;
+ }
+ if( rEndCol >= aCol.size())
+ assert( !aDefaultColData.GetAttr( nStartRow, ATTR_MERGE ).IsMerged());
+ bool bFound = false;
+ SCCOL nOldEndX = ClampToAllocatedColumns(rEndCol);
+ SCROW nOldEndY = rEndRow;
+ for (SCCOL i=nStartCol; i<=nOldEndX; i++)
+ bFound |= aCol[i].ExtendMerge( i, nStartRow, nOldEndY, rEndCol, rEndRow, bRefresh );
+ return bFound;
+}
+
+void ScTable::SetMergedCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ ScMergeAttr aAttr(nCol2-nCol1+1, nRow2-nRow1+1);
+ ApplyAttr(nCol1, nRow1, aAttr);
+
+ if (nCol1 < nCol2)
+ ApplyFlags(nCol1+1, nRow1, nCol2, nRow2, ScMF::Hor);
+
+ if (nRow1 < nRow2)
+ ApplyFlags(nCol1, nRow1+1, nCol1, nRow2, ScMF::Ver);
+
+ if (nCol1 < nCol2 && nRow1 < nRow2)
+ ApplyFlags(nCol1+1, nRow1+1, nCol2, nRow2, ScMF::Hor | ScMF::Ver);
+}
+
+bool ScTable::IsBlockEmpty( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
+{
+ if (!(ValidCol(nCol1) && ValidCol(nCol2)))
+ {
+ OSL_FAIL("ScTable::IsBlockEmpty: invalid column number");
+ return false;
+ }
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ bool bEmpty = true;
+ for (SCCOL i=nCol1; i<=nCol2 && bEmpty; i++)
+ {
+ bEmpty = aCol[i].IsEmptyData( nRow1, nRow2 );
+ if (bEmpty)
+ {
+ bEmpty = aCol[i].IsSparklinesEmptyBlock(nRow1, nRow2);
+ }
+ if (bEmpty)
+ {
+ bEmpty = aCol[i].IsNotesEmptyBlock(nRow1, nRow2);
+ }
+ }
+ return bEmpty;
+}
+
+SCSIZE ScTable::FillMaxRot( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2,
+ SCCOL nCol, SCROW nAttrRow1, SCROW nAttrRow2, SCSIZE nArrY,
+ const ScPatternAttr* pPattern, const SfxItemSet* pCondSet )
+{
+ // Return value = new nArrY
+
+ ScRotateDir nRotDir = pPattern->GetRotateDir( pCondSet );
+ if ( nRotDir != ScRotateDir::NONE )
+ {
+ bool bHit = true;
+ if ( nCol+1 < nX1 ) // column to the left
+ bHit = ( nRotDir != ScRotateDir::Left );
+ else if ( nCol > nX2+1 ) // column to the right
+ bHit = ( nRotDir != ScRotateDir::Right ); // ScRotateDir::Standard may now also be extended to the left
+
+ if ( bHit )
+ {
+ double nFactor = 0.0;
+ if ( nCol > nX2+1 )
+ {
+ Degree100 nRotVal = pPattern->
+ GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue();
+ double nRealOrient = toRadians(nRotVal);
+ double nCos = cos( nRealOrient );
+ double nSin = sin( nRealOrient );
+ //TODO: limit !!!
+ //TODO: additional factor for varying PPT X/Y !!!
+
+ // for ScRotateDir::Left this gives a negative value,
+ // if the mode is considered
+ nFactor = -fabs( nCos / nSin );
+ }
+
+ for ( SCROW nRow = nAttrRow1; nRow <= nAttrRow2; nRow++ )
+ {
+ if (!RowHidden(nRow))
+ {
+ bool bHitOne = true;
+ if ( nCol > nX2+1 )
+ {
+ // Does the rotated cell extend into the visible range?
+
+ SCCOL nTouchedCol = nCol;
+ tools::Long nWidth = static_cast<tools::Long>(mpRowHeights->getValue(nRow) * nFactor);
+ OSL_ENSURE(nWidth <= 0, "Wrong direction");
+ while ( nWidth < 0 && nTouchedCol > 0 )
+ {
+ --nTouchedCol;
+ nWidth += GetColWidth( nTouchedCol );
+ }
+ if ( nTouchedCol > nX2 )
+ bHitOne = false;
+ }
+
+ if (bHitOne)
+ {
+ while ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo < nRow )
+ ++nArrY;
+ if ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo == nRow )
+ pRowInfo[nArrY].nRotMaxCol = nCol;
+ }
+ }
+ }
+ }
+ }
+
+ return nArrY;
+}
+
+void ScTable::FindMaxRotCol( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2 )
+{
+ if ( !mpColWidth || !mpRowHeights || !mpColFlags || !pRowFlags )
+ {
+ OSL_FAIL( "Row/column info missing" );
+ return;
+ }
+
+ // nRotMaxCol is initialized to SC_ROTMAX_NONE, nRowNo is already set
+
+ SCROW nY1 = pRowInfo[0].nRowNo;
+ SCROW nY2 = pRowInfo[nArrCount-1].nRowNo;
+
+ for (SCCOL nCol : GetColumnsRange(0, rDocument.MaxCol()))
+ {
+ if (!ColHidden(nCol))
+ {
+ SCSIZE nArrY = 0;
+ ScDocAttrIterator aIter( rDocument, nTab, nCol, nY1, nCol, nY2 );
+ SCCOL nAttrCol;
+ SCROW nAttrRow1, nAttrRow2;
+ const ScPatternAttr* pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 );
+ while ( pPattern )
+ {
+ if ( const ScCondFormatItem* pCondItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ) )
+ {
+ // Run through all formats, so that each cell does not have to be
+ // handled individually
+
+ const ScCondFormatIndexes& rCondFormatData = pCondItem->GetCondFormatData();
+ ScStyleSheetPool* pStylePool = rDocument.GetStyleSheetPool();
+ if (mpCondFormatList && pStylePool && !rCondFormatData.empty())
+ {
+ for(const auto& rItem : rCondFormatData)
+ {
+ const ScConditionalFormat* pFormat = mpCondFormatList->GetFormat(rItem);
+ if ( pFormat )
+ {
+ size_t nEntryCount = pFormat->size();
+ for (size_t nEntry=0; nEntry<nEntryCount; nEntry++)
+ {
+ const ScFormatEntry* pEntry = pFormat->GetEntry(nEntry);
+ if(pEntry->GetType() != ScFormatEntry::Type::Condition &&
+ pEntry->GetType() != ScFormatEntry::Type::ExtCondition)
+ continue;
+
+ OUString aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle();
+ if (!aStyleName.isEmpty())
+ {
+ SfxStyleSheetBase* pStyleSheet =
+ pStylePool->Find( aStyleName, SfxStyleFamily::Para );
+ if ( pStyleSheet )
+ {
+ FillMaxRot( pRowInfo, nArrCount, nX1, nX2,
+ nCol, nAttrRow1, nAttrRow2,
+ nArrY, pPattern, &pStyleSheet->GetItemSet() );
+ // not changing nArrY
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ nArrY = FillMaxRot( pRowInfo, nArrCount, nX1, nX2,
+ nCol, nAttrRow1, nAttrRow2,
+ nArrY, pPattern, nullptr );
+
+ pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 );
+ }
+ }
+ }
+}
+
+bool ScTable::HasBlockMatrixFragment( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2,
+ bool bNoMatrixAtAll ) const
+{
+ using namespace sc;
+
+ if ( !IsColValid( nCol1 ) )
+ return false;
+
+ const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
+
+ MatrixEdge nEdges = MatrixEdge::Nothing;
+
+ if ( nCol1 == nMaxCol2 )
+ { // left and right column
+ const MatrixEdge n = MatrixEdge::Left | MatrixEdge::Right;
+ nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, n, bNoMatrixAtAll );
+ if ((nEdges != MatrixEdge::Nothing) && (((nEdges & n)!=n) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open))))
+ return true; // left or right edge is missing or open
+ }
+ else
+ { // left column
+ nEdges = aCol[nCol1].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdge::Left, bNoMatrixAtAll);
+ if ((nEdges != MatrixEdge::Nothing) && ((!(nEdges & MatrixEdge::Left)) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open))))
+ return true; // left edge missing or open
+ // right column
+ nEdges = aCol[nMaxCol2].GetBlockMatrixEdges(nRow1, nRow2, MatrixEdge::Right, bNoMatrixAtAll);
+ if ((nEdges != MatrixEdge::Nothing) && ((!(nEdges & MatrixEdge::Right)) || (nEdges & (MatrixEdge::Inside|MatrixEdge::Open))))
+ return true; // right edge is missing or open
+ }
+
+ if (bNoMatrixAtAll)
+ {
+ for (SCCOL i=nCol1; i<=nMaxCol2; i++)
+ {
+ nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow2, MatrixEdge::Nothing, bNoMatrixAtAll);
+ if (nEdges != MatrixEdge::Nothing
+ && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right)))
+ return true;
+ }
+ }
+ else if ( nRow1 == nRow2 )
+ { // Row on top and on bottom
+ bool bOpen = false;
+ const MatrixEdge n = MatrixEdge::Bottom | MatrixEdge::Top;
+ for ( SCCOL i=nCol1; i<=nMaxCol2; i++)
+ {
+ nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow1, n, bNoMatrixAtAll );
+ if (nEdges != MatrixEdge::Nothing)
+ {
+ if ( (nEdges & n) != n )
+ return true; // Top or bottom edge missing
+ if (nEdges & MatrixEdge::Left)
+ bOpen = true; // left edge open, continue
+ else if ( !bOpen )
+ return true; // Something exist that has not been opened
+ if (nEdges & MatrixEdge::Right)
+ bOpen = false; // Close right edge
+ }
+ }
+ if ( bOpen )
+ return true;
+ }
+ else
+ {
+ int j;
+ MatrixEdge n;
+ SCROW nR;
+ // first top row, then bottom row
+ for ( j=0, n = MatrixEdge::Top, nR=nRow1; j<2;
+ j++, n = MatrixEdge::Bottom, nR=nRow2)
+ {
+ bool bOpen = false;
+ for ( SCCOL i=nCol1; i<=nMaxCol2; i++)
+ {
+ nEdges = aCol[i].GetBlockMatrixEdges( nR, nR, n, bNoMatrixAtAll );
+ if ( nEdges != MatrixEdge::Nothing)
+ {
+ // in top row no top edge respectively
+ // in bottom row no bottom edge
+ if ( (nEdges & n) != n )
+ return true;
+ if (nEdges & MatrixEdge::Left)
+ bOpen = true; // open left edge, continue
+ else if ( !bOpen )
+ return true; // Something exist that has not been opened
+ if (nEdges & MatrixEdge::Right)
+ bOpen = false; // Close right edge
+ }
+ }
+ if ( bOpen )
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScTable::HasSelectionMatrixFragment( const ScMarkData& rMark ) const
+{
+ std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans();
+ ScRangeList rangeList = rMark.GetMarkedRanges();
+
+ for (const sc::ColRowSpan & aSpan : aSpans)
+ {
+ SCCOL nEndCol = ClampToAllocatedColumns(aSpan.mnEnd);
+ for ( SCCOLROW j=aSpan.mnStart; j<=nEndCol; j++ )
+ {
+ if ( aCol[j].HasSelectionMatrixFragment(rMark, rangeList) )
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScTable::IsBlockEditable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2,
+ SCROW nRow2, bool* pOnlyNotBecauseOfMatrix /* = NULL */,
+ bool bNoMatrixAtAll ) const
+{
+ if ( !ValidColRow( nCol2, nRow2 ) )
+ {
+ SAL_WARN("sc", "IsBlockEditable: invalid column or row " << nCol2 << " " << nRow2);
+ if (pOnlyNotBecauseOfMatrix)
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+ }
+
+ bool bIsEditable = true;
+ if ( nLockCount )
+ bIsEditable = false;
+ else if ( IsProtected() && !rDocument.IsScenario(nTab) )
+ {
+ bIsEditable = !HasAttrib( nCol1, nRow1, nCol2, nRow2, HasAttrFlags::Protected );
+ if (!bIsEditable)
+ {
+ // An enhanced protection permission may override the attribute.
+ if (pTabProtection)
+ bIsEditable = pTabProtection->isBlockEditable( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab));
+ }
+ if (bIsEditable)
+ {
+ // If Sheet is protected and cells are not protected then
+ // check the active scenario protect flag if this range is
+ // on the active scenario range. Note the 'copy back' must also
+ // be set to apply protection.
+ sal_uInt16 nScenTab = nTab+1;
+ while(rDocument.IsScenario(nScenTab))
+ {
+ ScRange aEditRange(nCol1, nRow1, nScenTab, nCol2, nRow2, nScenTab);
+ if(rDocument.IsActiveScenario(nScenTab) && rDocument.HasScenarioRange(nScenTab, aEditRange))
+ {
+ ScScenarioFlags nFlags;
+ rDocument.GetScenarioFlags(nScenTab,nFlags);
+ bIsEditable = !((nFlags & ScScenarioFlags::Protected) && (nFlags & ScScenarioFlags::TwoWay));
+ break;
+ }
+ nScenTab++;
+ }
+ }
+ }
+ else if (rDocument.IsScenario(nTab))
+ {
+ // Determine if the preceding sheet is protected
+ SCTAB nActualTab = nTab;
+ do
+ {
+ nActualTab--;
+ }
+ while(rDocument.IsScenario(nActualTab));
+
+ if(rDocument.IsTabProtected(nActualTab))
+ {
+ ScRange aEditRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
+ if(rDocument.HasScenarioRange(nTab, aEditRange))
+ {
+ ScScenarioFlags nFlags;
+ rDocument.GetScenarioFlags(nTab,nFlags);
+ bIsEditable = !(nFlags & ScScenarioFlags::Protected);
+ }
+ }
+ }
+ if ( bIsEditable )
+ {
+ if (HasBlockMatrixFragment( nCol1, nRow1, nCol2, nRow2, bNoMatrixAtAll))
+ {
+ bIsEditable = false;
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = true;
+ }
+ else if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ }
+ else if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return bIsEditable;
+}
+
+bool ScTable::IsSelectionEditable( const ScMarkData& rMark,
+ bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const
+{
+ bool bIsEditable = true;
+ if ( nLockCount )
+ bIsEditable = false;
+ else if ( IsProtected() && !rDocument.IsScenario(nTab) )
+ {
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ bIsEditable = !HasAttribSelection( rMark, HasAttrFlags::Protected );
+ if (!bIsEditable)
+ {
+ // An enhanced protection permission may override the attribute.
+ if (pTabProtection)
+ bIsEditable = pTabProtection->isSelectionEditable( aRanges);
+ }
+ if (bIsEditable)
+ {
+ // If Sheet is protected and cells are not protected then
+ // check the active scenario protect flag if this area is
+ // in the active scenario range.
+ SCTAB nScenTab = nTab+1;
+ while(rDocument.IsScenario(nScenTab) && bIsEditable)
+ {
+ if(rDocument.IsActiveScenario(nScenTab))
+ {
+ for (size_t i=0, nRange = aRanges.size(); (i < nRange) && bIsEditable; i++ )
+ {
+ const ScRange & rRange = aRanges[ i ];
+ if(rDocument.HasScenarioRange(nScenTab, rRange))
+ {
+ ScScenarioFlags nFlags;
+ rDocument.GetScenarioFlags(nScenTab,nFlags);
+ bIsEditable = !((nFlags & ScScenarioFlags::Protected) && (nFlags & ScScenarioFlags::TwoWay));
+ }
+ }
+ }
+ nScenTab++;
+ }
+ }
+ }
+ else if (rDocument.IsScenario(nTab))
+ {
+ // Determine if the preceding sheet is protected
+ SCTAB nActualTab = nTab;
+ do
+ {
+ nActualTab--;
+ }
+ while(rDocument.IsScenario(nActualTab));
+
+ if(rDocument.IsTabProtected(nActualTab))
+ {
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ for (size_t i = 0, nRange = aRanges.size(); (i < nRange) && bIsEditable; i++)
+ {
+ const ScRange & rRange = aRanges[ i ];
+ if(rDocument.HasScenarioRange(nTab, rRange))
+ {
+ ScScenarioFlags nFlags;
+ rDocument.GetScenarioFlags(nTab,nFlags);
+ bIsEditable = !(nFlags & ScScenarioFlags::Protected);
+ }
+ }
+ }
+ }
+ if ( bIsEditable )
+ {
+ if ( HasSelectionMatrixFragment( rMark ) )
+ {
+ bIsEditable = false;
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = true;
+ }
+ else if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ }
+ else if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return bIsEditable;
+}
+
+void ScTable::LockTable()
+{
+ ++nLockCount;
+}
+
+void ScTable::UnlockTable()
+{
+ if (nLockCount)
+ --nLockCount;
+ else
+ {
+ OSL_FAIL("UnlockTable without LockTable");
+ }
+}
+
+void ScTable::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const
+{
+ std::vector<sc::ColRowSpan> aSpans = rMark.GetMarkedColSpans();
+
+ for (const sc::ColRowSpan & rSpan : aSpans)
+ {
+ SCCOL maxCol = ClampToAllocatedColumns(rSpan.mnEnd);
+ for (SCCOL i = rSpan.mnStart; i <= maxCol; ++i)
+ {
+ aCol[i].MergeSelectionPattern( rState, rMark, bDeep );
+ }
+ }
+}
+
+void ScTable::MergePatternArea( ScMergePatternState& rState, SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2, bool bDeep ) const
+{
+ const SCCOL nEndCol = ClampToAllocatedColumns(nCol2);
+ for (SCCOL i=nCol1; i<=nEndCol; i++)
+ aCol[i].MergePatternArea( rState, nRow1, nRow2, bDeep );
+ if (nEndCol != nCol2)
+ aDefaultColData.MergePatternArea( rState, nRow1, nRow2, bDeep );
+}
+
+void ScTable::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, ScLineFlags& rFlags,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
+{
+ if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
+ {
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ nEndCol = ClampToAllocatedColumns(nEndCol);
+ for (SCCOL i=nStartCol; i<=nEndCol; i++)
+ aCol[i].MergeBlockFrame( pLineOuter, pLineInner, rFlags,
+ nStartRow, nEndRow, (i==nStartCol), nEndCol-i );
+ }
+}
+
+void ScTable::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow)
+{
+ if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
+ {
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ CreateColumnIfNotExists(nEndCol);
+ for (SCCOL i=nStartCol; i<=nEndCol; i++)
+ aCol[i].ApplyBlockFrame(rLineOuter, pLineInner,
+ nStartRow, nEndRow, (i==nStartCol), nEndCol-i);
+ }
+}
+
+void ScTable::ApplyPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr )
+{
+ if (ValidColRow(nCol,nRow))
+ CreateColumnIfNotExists(nCol).ApplyPattern( nRow, rAttr );
+}
+
+void ScTable::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const ScPatternAttr& rAttr, ScEditDataArray* pDataArray,
+ bool* const pIsChanged )
+{
+ if (!ValidColRow(nStartCol, nStartRow) || !ValidColRow(nEndCol, nEndRow))
+ return;
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ SCCOL maxCol = nEndCol;
+ if( nEndCol == GetDoc().MaxCol())
+ {
+ // For the same unallocated columns until the end we can change just the default.
+ maxCol = std::max( nStartCol, aCol.size()) - 1;
+ if( maxCol >= 0 )
+ CreateColumnIfNotExists(maxCol); // Allocate needed different columns before changing the default.
+ aDefaultColData.ApplyPatternArea(nStartRow, nEndRow, rAttr, pDataArray, pIsChanged);
+ }
+ for (SCCOL i = nStartCol; i <= maxCol; i++)
+ CreateColumnIfNotExists(i).ApplyPatternArea(nStartRow, nEndRow, rAttr, pDataArray, pIsChanged);
+}
+
+namespace
+{
+ std::vector<ScAttrEntry> duplicateScAttrEntries(ScDocument& rDocument, const std::vector<ScAttrEntry>& rOrigData)
+ {
+ std::vector<ScAttrEntry> aData(rOrigData);
+ for (size_t nIdx = 0; nIdx < aData.size(); ++nIdx)
+ {
+ aData[nIdx].pPattern = &rDocument.GetPool()->DirectPutItemInPool(*aData[nIdx].pPattern);
+ }
+ return aData;
+ }
+}
+
+void ScTable::SetAttrEntries( SCCOL nStartCol, SCCOL nEndCol, std::vector<ScAttrEntry> && vNewData)
+{
+ if (!ValidCol(nStartCol) || !ValidCol(nEndCol))
+ return;
+ if ( nEndCol == rDocument.MaxCol() )
+ {
+ if ( nStartCol < aCol.size() )
+ {
+ // If we would like set all columns to same attrs, then change only attrs for not existing columns
+ nEndCol = aCol.size() - 1;
+ for (SCCOL i = nStartCol; i <= nEndCol; i++)
+ aCol[i].SetAttrEntries(duplicateScAttrEntries(rDocument, vNewData));
+ aDefaultColData.SetAttrEntries(std::move(vNewData));
+ }
+ else
+ {
+ CreateColumnIfNotExists( nStartCol - 1 );
+ aDefaultColData.SetAttrEntries(std::move(vNewData));
+ }
+ }
+ else
+ {
+ CreateColumnIfNotExists( nEndCol );
+ for (SCCOL i = nStartCol; i < nEndCol; i++) // all but last need a copy
+ aCol[i].SetAttrEntries(duplicateScAttrEntries(rDocument, vNewData));
+ aCol[nEndCol].SetAttrEntries( std::move(vNewData));
+ }
+}
+
+
+
+void ScTable::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
+ const ScPatternAttr& rPattern, SvNumFormatType nNewType )
+{
+ SCCOL nEndCol = rRange.aEnd.Col();
+ for ( SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ aCol[nCol].ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType );
+ }
+}
+
+void ScTable::AddCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nIndex )
+{
+ size_t n = rRangeList.size();
+ for(size_t i = 0; i < n; ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ SCCOL nColStart = rRange.aStart.Col();
+ SCCOL nColEnd = rRange.aEnd.Col();
+ SCROW nRowStart = rRange.aStart.Row();
+ SCROW nRowEnd = rRange.aEnd.Row();
+ for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ {
+ CreateColumnIfNotExists(nCol).AddCondFormat(nRowStart, nRowEnd, nIndex);
+ }
+ }
+}
+
+void ScTable::RemoveCondFormatData( const ScRangeList& rRangeList, sal_uInt32 nIndex )
+{
+ size_t n = rRangeList.size();
+ for(size_t i = 0; i < n; ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ SCCOL nColStart = rRange.aStart.Col();
+ SCCOL nColEnd = ClampToAllocatedColumns(rRange.aEnd.Col());
+ SCROW nRowStart = rRange.aStart.Row();
+ SCROW nRowEnd = rRange.aEnd.Row();
+ for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ {
+ aCol[nCol].RemoveCondFormat(nRowStart, nRowEnd, nIndex);
+ }
+ }
+}
+
+void ScTable::SetPatternAreaCondFormat( SCCOL nCol, SCROW nStartRow, SCROW nEndRow,
+ const ScPatternAttr& rAttr, const ScCondFormatIndexes& rCondFormatIndexes )
+{
+ CreateColumnIfNotExists(nCol).SetPatternArea( nStartRow, nEndRow, rAttr);
+
+ for (const auto& rIndex : rCondFormatIndexes)
+ {
+ ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex);
+ if (pCondFormat)
+ {
+ ScRangeList aRange = pCondFormat->GetRange();
+ aRange.Join( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab));
+ pCondFormat->SetRange(aRange);
+ }
+ }
+}
+
+void ScTable::ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet* rStyle )
+{
+ if (ValidColRow(nCol,nRow))
+ // If column not exists then we need to create it
+ CreateColumnIfNotExists( nCol ).ApplyStyle( nRow, rStyle );
+}
+
+void ScTable::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, const ScStyleSheet& rStyle )
+{
+ if (!(ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)))
+ return;
+
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ if ( nEndCol == rDocument.MaxCol() )
+ {
+ if ( nStartCol < aCol.size() )
+ {
+ // If we would like set all columns to specific style, then change only default style for not existing columns
+ nEndCol = aCol.size() - 1;
+ for (SCCOL i = nStartCol; i <= nEndCol; i++)
+ aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle);
+ aDefaultColData.ApplyStyleArea(nStartRow, nEndRow, rStyle );
+ }
+ else
+ {
+ CreateColumnIfNotExists( nStartCol - 1 );
+ aDefaultColData.ApplyStyleArea(nStartRow, nEndRow, rStyle );
+ }
+ }
+ else
+ {
+ CreateColumnIfNotExists( nEndCol );
+ for (SCCOL i = nStartCol; i <= nEndCol; i++)
+ aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle);
+ }
+}
+
+void ScTable::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
+{
+ ApplyWithAllocation(rMark, [&rStyle](ScColumnData& applyTo, SCROW nTop, SCROW nBottom)
+ { applyTo.ApplySelectionStyle(rStyle, nTop, nBottom); });
+}
+
+void ScTable::ApplySelectionLineStyle( const ScMarkData& rMark,
+ const ::editeng::SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].ApplySelectionLineStyle( rMark, pLine, bColorOnly );
+}
+
+const ScStyleSheet* ScTable::GetStyle( SCCOL nCol, SCROW nRow ) const
+{
+ if ( !ValidColRow( nCol, nRow ) )
+ return nullptr;
+ return ColumnData(nCol).GetStyle( nRow );
+}
+
+const ScStyleSheet* ScTable::GetSelectionStyle( const ScMarkData& rMark, bool& rFound ) const
+{
+ rFound = false;
+
+ bool bEqual = true;
+ bool bColFound;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ for (SCCOL i=0; i < aCol.size() && bEqual; i++)
+ if (rMark.HasMultiMarks(i))
+ {
+ pNewStyle = aCol[i].GetSelectionStyle( rMark, bColFound );
+ if (bColFound)
+ {
+ rFound = true;
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false;
+ pStyle = pNewStyle;
+ }
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+const ScStyleSheet* ScTable::GetAreaStyle( bool& rFound, SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2 ) const
+{
+ rFound = false;
+
+ bool bEqual = true;
+ bool bColFound;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+ nCol2 = ClampToAllocatedColumns(nCol2);
+ for (SCCOL i=nCol1; i<=nCol2 && bEqual; i++)
+ {
+ pNewStyle = aCol[i].GetAreaStyle(bColFound, nRow1, nRow2);
+ if (bColFound)
+ {
+ rFound = true;
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false;
+ pStyle = pNewStyle;
+ }
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+bool ScTable::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
+{
+ bool bIsUsed = false;
+
+ for ( SCCOL i=0; i < aCol.size(); i++ )
+ {
+ if ( aCol[i].IsStyleSheetUsed( rStyle ) )
+ {
+ bIsUsed = true;
+ }
+ }
+
+ return bIsUsed;
+}
+
+void ScTable::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved,
+ OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY )
+{
+ ScFlatBoolRowSegments aUsedRows(rDocument.MaxRow());
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].FindStyleSheet(pStyleSheet, aUsedRows, bRemoved);
+
+ sc::RowHeightContext aCxt(rDocument.MaxRow(), nPPTX, nPPTY, rZoomX, rZoomY, pDev);
+ SCROW nRow = 0;
+ while (nRow <= rDocument.MaxRow())
+ {
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!aUsedRows.getRangeData(nRow, aData))
+ // search failed!
+ return;
+
+ SCROW nEndRow = aData.mnRow2;
+ if (aData.mbValue)
+ SetOptimalHeight(aCxt, nRow, nEndRow, true);
+
+ nRow = nEndRow + 1;
+ }
+}
+
+bool ScTable::ApplyFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScMF nFlags )
+{
+ bool bChanged = false;
+ if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
+ for (SCCOL i = nStartCol; i <= nEndCol; i++)
+ bChanged |= CreateColumnIfNotExists(i).ApplyFlags(nStartRow, nEndRow, nFlags);
+ return bChanged;
+}
+
+bool ScTable::RemoveFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScMF nFlags )
+{
+ if (!ValidColRow(nStartCol, nStartRow) || !ValidColRow(nEndCol, nEndRow))
+ return false;
+ bool bChanged = false;
+ nEndCol = ClampToAllocatedColumns(nEndCol);
+ for (SCCOL i = nStartCol; i <= nEndCol; i++)
+ bChanged |= aCol[i].RemoveFlags(nStartRow, nEndRow, nFlags);
+ return bChanged;
+}
+
+void ScTable::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr )
+{
+ if (ValidColRow(rPos.Col(),rPos.Row()))
+ CreateColumnIfNotExists(rPos.Col()).SetPattern(rPos.Row(), rAttr);
+}
+
+const ScPatternAttr* ScTable::SetPattern( SCCOL nCol, SCROW nRow, std::unique_ptr<ScPatternAttr> pAttr )
+{
+ if (ValidColRow(nCol,nRow))
+ return CreateColumnIfNotExists(nCol).SetPattern(nRow, std::move(pAttr));
+ return nullptr;
+}
+
+void ScTable::SetPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr )
+{
+ if (ValidColRow(nCol,nRow))
+ CreateColumnIfNotExists(nCol).SetPattern(nRow, rAttr);
+}
+
+void ScTable::ApplyAttr( SCCOL nCol, SCROW nRow, const SfxPoolItem& rAttr )
+{
+ if (ValidColRow(nCol,nRow))
+ CreateColumnIfNotExists(nCol).ApplyAttr( nRow, rAttr );
+}
+
+void ScTable::ApplySelectionCache( ScItemPoolCache* pCache, const ScMarkData& rMark,
+ ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+ ApplyWithAllocation(
+ rMark, [pCache, pDataArray, pIsChanged](ScColumnData& applyTo, SCROW nTop, SCROW nBottom)
+ { applyTo.ApplySelectionCache(pCache, nTop, nBottom, pDataArray, pIsChanged); });
+}
+
+void ScTable::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark )
+{
+ ApplyWithAllocation(rMark, [&bIncrement](ScColumnData& applyTo, SCROW nTop, SCROW nBottom)
+ { applyTo.ChangeSelectionIndent(bIncrement, nTop, nBottom); });
+}
+
+void ScTable::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark )
+{
+ ApplyWithAllocation(rMark, [pWhich](ScColumnData& applyTo, SCROW nTop, SCROW nBottom)
+ { applyTo.ClearSelectionItems(pWhich, nTop, nBottom); });
+}
+
+// Column widths / Row heights
+
+void ScTable::SetColWidth( SCCOL nCol, sal_uInt16 nNewWidth )
+{
+ if (ValidCol(nCol) && mpColWidth)
+ {
+ if (!nNewWidth)
+ {
+ nNewWidth = STD_COL_WIDTH;
+ }
+
+ if ( nNewWidth != mpColWidth->GetValue(nCol) )
+ {
+ mpColWidth->SetValue(nCol, nNewWidth);
+ InvalidatePageBreaks();
+ }
+ }
+ else
+ {
+ OSL_FAIL("Invalid column number or no widths");
+ }
+}
+
+void ScTable::SetColWidthOnly( SCCOL nCol, sal_uInt16 nNewWidth )
+{
+ if (!ValidCol(nCol) || !mpColWidth)
+ return;
+
+ if (!nNewWidth)
+ nNewWidth = STD_COL_WIDTH;
+
+ if (nNewWidth != mpColWidth->GetValue(nCol))
+ mpColWidth->SetValue(nCol, nNewWidth);
+}
+
+void ScTable::SetRowHeight( SCROW nRow, sal_uInt16 nNewHeight )
+{
+ if (ValidRow(nRow) && mpRowHeights)
+ {
+ if (!nNewHeight)
+ {
+ OSL_FAIL("SetRowHeight: Row height zero");
+ nNewHeight = GetOptimalMinRowHeight();
+ }
+
+ sal_uInt16 nOldHeight = mpRowHeights->getValue(nRow);
+ if ( nNewHeight != nOldHeight )
+ {
+ mpRowHeights->setValue(nRow, nRow, nNewHeight);
+ InvalidatePageBreaks();
+ }
+ }
+ else
+ {
+ OSL_FAIL("Invalid row number or no heights");
+ }
+}
+
+namespace {
+
+/**
+ * Check if the new pixel size is different from the old size between
+ * specified ranges.
+ */
+bool lcl_pixelSizeChanged(
+ ScFlatUInt16RowSegments& rRowHeights, SCROW nStartRow, SCROW nEndRow,
+ sal_uInt16 nNewHeight, double nPPTY, bool bApi)
+{
+ tools::Long nNewPix = static_cast<tools::Long>(nNewHeight * nPPTY);
+
+ ScFlatUInt16RowSegments::ForwardIterator aFwdIter(rRowHeights);
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ sal_uInt16 nHeight;
+ if (!aFwdIter.getValue(nRow, nHeight))
+ break;
+
+ if (nHeight != nNewHeight)
+ {
+ tools::Long nOldPix = static_cast<tools::Long>(nHeight * nPPTY);
+
+ // Heuristic: Don't bother when handling interactive input, if changing just one row and
+ // the height will shrink.
+ bool bChanged = (nNewPix != nOldPix) && (bApi || nEndRow - nStartRow > 0 || nNewPix > nOldPix);
+ if (bChanged)
+ return true;
+ }
+
+ // Skip ahead to the last position of the current range.
+ nRow = aFwdIter.getLastPos();
+ }
+ return false;
+}
+
+}
+
+bool ScTable::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, sal_uInt16 nNewHeight,
+ double nPPTY, bool bApi )
+{
+ bool bChanged = false;
+ if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights)
+ {
+ if (!nNewHeight)
+ {
+ OSL_FAIL("SetRowHeight: Row height zero");
+ nNewHeight = GetOptimalMinRowHeight();
+ }
+
+ bool bSingle = false; // true = process every row for its own
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if (pDrawLayer)
+ if (pDrawLayer->HasObjectsInRows( nTab, nStartRow, nEndRow ))
+ bSingle = true;
+
+ if (bSingle)
+ {
+ ScFlatUInt16RowSegments::RangeData aData;
+ if (mpRowHeights->getRangeData(nStartRow, aData) &&
+ nNewHeight == aData.mnValue && nEndRow <= aData.mnRow2)
+ {
+ bSingle = false; // no difference in this range
+ }
+ }
+
+ // No idea why 20 is used here
+ if (!bSingle || nEndRow - nStartRow < 20)
+ {
+ bChanged = lcl_pixelSizeChanged(*mpRowHeights, nStartRow, nEndRow, nNewHeight, nPPTY, bApi);
+ if (bChanged)
+ mpRowHeights->setValue(nStartRow, nEndRow, nNewHeight);
+ }
+ else
+ {
+ SCROW nMid = (nStartRow + nEndRow) / 2;
+ // No idea why nPPTY is ignored in these recursive calls and instead 1.0 is used
+ if (SetRowHeightRange(nStartRow, nMid, nNewHeight, 1.0, bApi))
+ bChanged = true;
+ if (SetRowHeightRange(nMid + 1, nEndRow, nNewHeight, 1.0, bApi))
+ bChanged = true;
+ }
+
+ if (bChanged)
+ InvalidatePageBreaks();
+ }
+ else
+ {
+ OSL_FAIL("Invalid row number or no heights");
+ }
+
+ return bChanged;
+}
+
+void ScTable::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, sal_uInt16 nNewHeight )
+{
+ if (!ValidRow(nStartRow) || !ValidRow(nEndRow) || !mpRowHeights)
+ return;
+
+ if (!nNewHeight)
+ nNewHeight = GetOptimalMinRowHeight();
+
+ mpRowHeights->setValue(nStartRow, nEndRow, nNewHeight);
+}
+
+void ScTable::SetManualHeight( SCROW nStartRow, SCROW nEndRow, bool bManual )
+{
+ if (ValidRow(nStartRow) && ValidRow(nEndRow) && pRowFlags)
+ {
+ if (bManual)
+ pRowFlags->OrValue( nStartRow, nEndRow, CRFlags::ManualSize);
+ else
+ pRowFlags->AndValue( nStartRow, nEndRow, ~CRFlags::ManualSize);
+ }
+ else
+ {
+ OSL_FAIL("Invalid row number or no column flags");
+ }
+}
+
+sal_uInt16 ScTable::GetColWidth( SCCOL nCol, bool bHiddenAsZero ) const
+{
+ OSL_ENSURE(ValidCol(nCol),"wrong column number");
+
+ if (ValidCol(nCol) && mpColFlags && mpColWidth)
+ {
+ if (bHiddenAsZero && ColHidden(nCol))
+ return 0;
+ else
+ return mpColWidth->GetValue(nCol);
+ }
+ else
+ return sal_uInt16(STD_COL_WIDTH);
+}
+
+tools::Long ScTable::GetColWidth( SCCOL nStartCol, SCCOL nEndCol ) const
+{
+ if (!ValidCol(nStartCol) || !ValidCol(nEndCol) || nStartCol > nEndCol)
+ return 0;
+
+ tools::Long nW = 0;
+ bool bHidden = false;
+ SCCOL nLastHiddenCol = -1;
+ auto colWidthIt = mpColWidth->begin() + nStartCol;
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; (++nCol <= nEndCol) ? ++colWidthIt : (void)false)
+ {
+ if (nCol > nLastHiddenCol)
+ bHidden = ColHidden(nCol, nullptr, &nLastHiddenCol);
+
+ if (bHidden)
+ continue;
+
+ nW += *colWidthIt;
+ }
+ return nW;
+}
+
+sal_uInt16 ScTable::GetOriginalWidth( SCCOL nCol ) const // always the set value
+{
+ OSL_ENSURE(ValidCol(nCol),"wrong column number");
+
+ if (ValidCol(nCol) && mpColWidth)
+ return mpColWidth->GetValue(nCol);
+ else
+ return sal_uInt16(STD_COL_WIDTH);
+}
+
+sal_uInt16 ScTable::GetCommonWidth( SCCOL nEndCol ) const
+{
+ // get the width that is used in the largest continuous column range (up to nEndCol)
+
+ if ( !ValidCol(nEndCol) )
+ {
+ OSL_FAIL("wrong column");
+ nEndCol = rDocument.MaxCol();
+ }
+
+ sal_uInt16 nMaxWidth = 0;
+ sal_uInt16 nMaxCount = 0;
+ SCCOL nRangeStart = 0;
+ while ( nRangeStart <= nEndCol )
+ {
+ // skip hidden columns
+ while ( nRangeStart <= nEndCol && ColHidden(nRangeStart) )
+ ++nRangeStart;
+ if ( nRangeStart <= nEndCol )
+ {
+ sal_uInt16 nThisCount = 0;
+ auto colWidthIt = mpColWidth->begin() + nRangeStart;
+ sal_uInt16 nThisWidth = *colWidthIt;
+ SCCOL nRangeEnd = nRangeStart;
+ while ( nRangeEnd <= nEndCol && *colWidthIt == nThisWidth )
+ {
+ ++nThisCount;
+ ++nRangeEnd;
+ ++colWidthIt;
+
+ // skip hidden columns
+ while ( nRangeEnd <= nEndCol && ColHidden(nRangeEnd) )
+ {
+ ++nRangeEnd;
+ ++colWidthIt;
+ }
+ }
+
+ if ( nThisCount > nMaxCount )
+ {
+ nMaxCount = nThisCount;
+ nMaxWidth = nThisWidth;
+ }
+
+ nRangeStart = nRangeEnd; // next range
+ }
+ }
+
+ return nMaxWidth;
+}
+
+sal_uInt16 ScTable::GetRowHeight( SCROW nRow, SCROW* pStartRow, SCROW* pEndRow, bool bHiddenAsZero ) const
+{
+ SAL_WARN_IF(!ValidRow(nRow), "sc", "Invalid row number " << nRow);
+
+ if (ValidRow(nRow) && mpRowHeights)
+ {
+ if (bHiddenAsZero && RowHidden( nRow, pStartRow, pEndRow))
+ return 0;
+ else
+ {
+ ScFlatUInt16RowSegments::RangeData aData;
+ if (!mpRowHeights->getRangeData(nRow, aData))
+ {
+ if (pStartRow)
+ *pStartRow = nRow;
+ if (pEndRow)
+ *pEndRow = nRow;
+ // TODO: What should we return in case the search fails?
+ return 0;
+ }
+
+ // If bHiddenAsZero, pStartRow and pEndRow were initialized to
+ // boundaries of a non-hidden segment. Assume that the previous and
+ // next segment are hidden then and limit the current height
+ // segment.
+ if (pStartRow)
+ *pStartRow = (bHiddenAsZero ? std::max( *pStartRow, aData.mnRow1) : aData.mnRow1);
+ if (pEndRow)
+ *pEndRow = (bHiddenAsZero ? std::min( *pEndRow, aData.mnRow2) : aData.mnRow2);
+ return aData.mnValue;
+ }
+ }
+ else
+ {
+ if (pStartRow)
+ *pStartRow = nRow;
+ if (pEndRow)
+ *pEndRow = nRow;
+ return GetOptimalMinRowHeight();
+ }
+}
+
+tools::Long ScTable::GetRowHeight( SCROW nStartRow, SCROW nEndRow, bool bHiddenAsZero ) const
+{
+ OSL_ENSURE(ValidRow(nStartRow) && ValidRow(nEndRow),"wrong row number");
+
+ if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights)
+ {
+ tools::Long nHeight = 0;
+ SCROW nRow = nStartRow;
+ while (nRow <= nEndRow)
+ {
+ SCROW nLastRow = -1;
+ if (!( ( RowHidden(nRow, nullptr, &nLastRow) ) && bHiddenAsZero ) )
+ {
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+ nHeight += mpRowHeights->getSumValue(nRow, nLastRow);
+ }
+ nRow = nLastRow + 1;
+ }
+ return nHeight;
+ }
+ else
+ return (nEndRow - nStartRow + 1) * static_cast<tools::Long>(GetOptimalMinRowHeight());
+}
+
+tools::Long ScTable::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow, double fScale ) const
+{
+ OSL_ENSURE(ValidRow(nStartRow) && ValidRow(nEndRow),"wrong row number");
+
+ if (ValidRow(nStartRow) && ValidRow(nEndRow) && mpRowHeights)
+ {
+ tools::Long nHeight = 0;
+ SCROW nRow = nStartRow;
+ while (nRow <= nEndRow)
+ {
+ SCROW nLastRow = -1;
+ if (!RowHidden(nRow, nullptr, &nLastRow))
+ {
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+
+ // #i117315# can't use getSumValue, because individual values must be rounded
+ ScFlatUInt16RowSegments::ForwardIterator aSegmentIter(*mpRowHeights);
+ while (nRow <= nLastRow)
+ {
+ sal_uInt16 nRowVal;
+ if (!aSegmentIter.getValue(nRow, nRowVal))
+ return nHeight; // shouldn't happen
+
+ SCROW nSegmentEnd = std::min( nLastRow, aSegmentIter.getLastPos() );
+
+ // round-down a single height value, multiply resulting (pixel) values
+ tools::Long nOneHeight = static_cast<tools::Long>( nRowVal * fScale );
+ nHeight += nOneHeight * ( nSegmentEnd + 1 - nRow );
+
+ nRow = nSegmentEnd + 1;
+ }
+ }
+ nRow = nLastRow + 1;
+ }
+ return nHeight;
+ }
+ else
+ return static_cast<tools::Long>((nEndRow - nStartRow + 1) * GetOptimalMinRowHeight() * fScale);
+}
+
+sal_uInt16 ScTable::GetOriginalHeight( SCROW nRow ) const // non-0 even if hidden
+{
+ OSL_ENSURE(ValidRow(nRow),"wrong row number");
+
+ if (ValidRow(nRow) && mpRowHeights)
+ return mpRowHeights->getValue(nRow);
+ else
+ return GetOptimalMinRowHeight();
+}
+
+// Column/Row -Flags
+
+SCROW ScTable::GetHiddenRowCount( SCROW nRow ) const
+{
+ if (!ValidRow(nRow))
+ return 0;
+
+ SCROW nLastRow = -1;
+ if (!RowHidden(nRow, nullptr, &nLastRow) || !ValidRow(nLastRow))
+ return 0;
+
+ return nLastRow - nRow + 1;
+}
+
+//TODO: combine ShowRows / DBShowRows
+
+void ScTable::ShowCol(SCCOL nCol, bool bShow)
+{
+ if (ValidCol(nCol))
+ {
+ bool bWasVis = !ColHidden(nCol);
+ if (bWasVis != bShow)
+ {
+ SetColHidden(nCol, nCol, !bShow);
+
+ ScChartListenerCollection* pCharts = rDocument.GetChartListenerCollection();
+ if ( pCharts )
+ pCharts->SetRangeDirty(ScRange( nCol, 0, nTab, nCol, rDocument.MaxRow(), nTab ));
+ }
+ }
+ else
+ {
+ OSL_FAIL("Invalid column number or no flags");
+ }
+}
+
+void ScTable::ShowRow(SCROW nRow, bool bShow)
+{
+ if (ValidRow(nRow) && pRowFlags)
+ {
+ bool bWasVis = !RowHidden(nRow);
+ if (bWasVis != bShow)
+ {
+ SetRowHidden(nRow, nRow, !bShow);
+ if (bShow)
+ SetRowFiltered(nRow, nRow, false);
+ ScChartListenerCollection* pCharts = rDocument.GetChartListenerCollection();
+ if ( pCharts )
+ pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, rDocument.MaxCol(), nRow, nTab ));
+
+ InvalidatePageBreaks();
+ }
+ }
+ else
+ {
+ OSL_FAIL("Invalid row number or no flags");
+ }
+}
+
+void ScTable::DBShowRow(SCROW nRow, bool bShow)
+{
+ if (ValidRow(nRow) && pRowFlags)
+ {
+ // Always set filter flag; unchanged when Hidden
+ bool bChanged = SetRowHidden(nRow, nRow, !bShow);
+ SetRowFiltered(nRow, nRow, !bShow);
+
+ if (bChanged)
+ {
+ ScChartListenerCollection* pCharts = rDocument.GetChartListenerCollection();
+ if ( pCharts )
+ pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, rDocument.MaxCol(), nRow, nTab ));
+
+ if (pOutlineTable)
+ UpdateOutlineRow( nRow, nRow, bShow );
+
+ InvalidatePageBreaks();
+ }
+ }
+ else
+ {
+ OSL_FAIL("Invalid row number or no flags");
+ }
+}
+
+void ScTable::DBShowRows(SCROW nRow1, SCROW nRow2, bool bShow)
+{
+ SCROW nStartRow = nRow1;
+ while (nStartRow <= nRow2)
+ {
+ SCROW nEndRow = -1;
+ bool bWasVis = !RowHiddenLeaf(nStartRow, nullptr, &nEndRow);
+ if (nEndRow > nRow2)
+ nEndRow = nRow2;
+
+ bool bChanged = ( bWasVis != bShow );
+
+ SetRowHidden(nStartRow, nEndRow, !bShow);
+ SetRowFiltered(nStartRow, nEndRow, !bShow);
+
+ if ( bChanged )
+ {
+ ScChartListenerCollection* pCharts = rDocument.GetChartListenerCollection();
+ if ( pCharts )
+ pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, rDocument.MaxCol(), nEndRow, nTab ));
+ }
+
+ nStartRow = nEndRow + 1;
+ }
+
+ // #i12341# For Show/Hide rows, the outlines are updated separately from the outside.
+ // For filtering, the changes aren't visible to the caller, so UpdateOutlineRow has
+ // to be done here.
+ if (pOutlineTable)
+ UpdateOutlineRow( nRow1, nRow2, bShow );
+}
+
+void ScTable::ShowRows(SCROW nRow1, SCROW nRow2, bool bShow)
+{
+ SCROW nStartRow = nRow1;
+
+ // #i116164# if there are no drawing objects within the row range, a single HeightChanged call is enough
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ bool bHasObjects = pDrawLayer && pDrawLayer->HasObjectsInRows( nTab, nRow1, nRow2 );
+
+ while (nStartRow <= nRow2)
+ {
+ SCROW nEndRow = -1;
+ bool bWasVis = !RowHiddenLeaf(nStartRow, nullptr, &nEndRow);
+ if (nEndRow > nRow2)
+ nEndRow = nRow2;
+
+ bool bChanged = ( bWasVis != bShow );
+
+ SetRowHidden(nStartRow, nEndRow, !bShow);
+ if (bShow)
+ SetRowFiltered(nStartRow, nEndRow, false);
+
+ if ( bChanged )
+ {
+ ScChartListenerCollection* pCharts = rDocument.GetChartListenerCollection();
+ if ( pCharts )
+ pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, rDocument.MaxCol(), nEndRow, nTab ));
+
+ InvalidatePageBreaks();
+ }
+
+ nStartRow = nEndRow + 1;
+ }
+
+ if ( !bHasObjects )
+ {
+ // #i116164# set the flags for the whole range at once
+ SetRowHidden(nRow1, nRow2, !bShow);
+ if (bShow)
+ SetRowFiltered(nRow1, nRow2, false);
+ }
+}
+
+bool ScTable::IsDataFiltered(SCCOL nColStart, SCROW nRowStart, SCCOL nColEnd, SCROW nRowEnd) const
+{
+ assert(nColStart <= nColEnd && nRowStart <= nRowEnd
+ && "range must be normalized to obtain a valid result");
+ for (SCROW i = nRowStart; i <= nRowEnd; ++i)
+ {
+ if (RowHidden(i))
+ return true;
+ }
+ for (SCCOL i = nColStart; i <= nColEnd; ++i)
+ {
+ if (ColHidden(i))
+ return true;
+ }
+ return false;
+}
+
+bool ScTable::IsDataFiltered(const ScRange& rRange) const
+{
+ ScRange aNormalized(rRange.aStart, rRange.aEnd);
+ return IsDataFiltered(aNormalized.aStart.Col(), aNormalized.aStart.Row(),
+ aNormalized.aEnd.Col(), aNormalized.aEnd.Row());
+}
+
+void ScTable::SetRowFlags( SCROW nRow, CRFlags nNewFlags )
+{
+ if (ValidRow(nRow) && pRowFlags)
+ pRowFlags->SetValue( nRow, nNewFlags);
+ else
+ {
+ OSL_FAIL("Invalid row number or no flags");
+ }
+}
+
+void ScTable::SetRowFlags( SCROW nStartRow, SCROW nEndRow, CRFlags nNewFlags )
+{
+ if (ValidRow(nStartRow) && ValidRow(nEndRow) && pRowFlags)
+ pRowFlags->SetValue( nStartRow, nEndRow, nNewFlags);
+ else
+ {
+ OSL_FAIL("Invalid row number(s) or no flags");
+ }
+}
+
+CRFlags ScTable::GetColFlags( SCCOL nCol ) const
+{
+ if (ValidCol(nCol) && mpColFlags)
+ return mpColFlags->GetValue(nCol);
+ else
+ return CRFlags::NONE;
+}
+
+CRFlags ScTable::GetRowFlags( SCROW nRow ) const
+{
+ if (ValidRow(nRow) && pRowFlags)
+ return pRowFlags->GetValue(nRow);
+ else
+ return CRFlags::NONE;
+}
+
+SCROW ScTable::GetLastFlaggedRow() const
+{
+ SCROW nLastFound = 0;
+ if (pRowFlags)
+ {
+ SCROW nRow = pRowFlags->GetLastAnyBitAccess( CRFlags::All );
+ if (ValidRow(nRow))
+ nLastFound = nRow;
+ }
+
+ if (!maRowManualBreaks.empty())
+ nLastFound = ::std::max(nLastFound, *maRowManualBreaks.rbegin());
+
+ if (mpHiddenRows)
+ {
+ SCROW nRow = mpHiddenRows->findLastTrue();
+ if (ValidRow(nRow))
+ nLastFound = ::std::max(nLastFound, nRow);
+ }
+
+ if (mpFilteredRows)
+ {
+ SCROW nRow = mpFilteredRows->findLastTrue();
+ if (ValidRow(nRow))
+ nLastFound = ::std::max(nLastFound, nRow);
+ }
+
+ return nLastFound;
+}
+
+SCCOL ScTable::GetLastChangedColFlagsWidth() const
+{
+ if ( !mpColFlags )
+ return 0;
+
+ SCCOL nLastFound = 0;
+ auto colWidthIt = mpColWidth->begin() + 1;
+ for (SCCOL nCol = 1; nCol <= GetDoc().MaxCol(); (++nCol <= GetDoc().MaxCol()) ? ++colWidthIt : (void)false)
+ if ((mpColFlags->GetValue(nCol) & CRFlags::All) || (*colWidthIt != STD_COL_WIDTH))
+ nLastFound = nCol;
+
+ return nLastFound;
+}
+
+SCROW ScTable::GetLastChangedRowFlagsWidth() const
+{
+ if ( !pRowFlags )
+ return 0;
+
+ SCROW nLastFlags = GetLastFlaggedRow();
+
+ // Find the last row position where the height is NOT the standard row
+ // height.
+ // KOHEI: Test this to make sure it does what it's supposed to.
+ SCROW nLastHeight = mpRowHeights->findLastTrue(GetOptimalMinRowHeight());
+ if (!ValidRow(nLastHeight))
+ nLastHeight = 0;
+
+ return std::max( nLastFlags, nLastHeight);
+}
+
+bool ScTable::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, bool bShow )
+{
+ if (pOutlineTable && mpColFlags)
+ {
+ return pOutlineTable->GetColArray().ManualAction( nStartCol, nEndCol, bShow, *this, true );
+ }
+ else
+ return false;
+}
+
+bool ScTable::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, bool bShow )
+{
+ if (pOutlineTable && pRowFlags)
+ return pOutlineTable->GetRowArray().ManualAction( nStartRow, nEndRow, bShow, *this, false );
+ else
+ return false;
+}
+
+void ScTable::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 )
+{
+ // Column-wise expansion
+
+ while (rX1 > 0 && ColHidden(rX1-1))
+ --rX1;
+
+ while (rX2 < rDocument.MaxCol() && ColHidden(rX2+1))
+ ++rX2;
+
+ // Row-wise expansion
+
+ if (rY1 > 0)
+ {
+ ScFlatBoolRowSegments::RangeData aData;
+ if (mpHiddenRows->getRangeData(rY1-1, aData) && aData.mbValue)
+ {
+ SCROW nStartRow = aData.mnRow1;
+ if (ValidRow(nStartRow))
+ rY1 = nStartRow;
+ }
+ }
+ if (rY2 < rDocument.MaxRow())
+ {
+ SCROW nEndRow = -1;
+ if (RowHidden(rY2+1, nullptr, &nEndRow) && ValidRow(nEndRow))
+ rY2 = nEndRow;
+ }
+}
+
+void ScTable::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 )
+{
+ while ( rX2>rX1 && ColHidden(rX2) )
+ --rX2;
+ while ( rX2>rX1 && ColHidden(rX1) )
+ ++rX1;
+
+ if (rY1 < rY2)
+ {
+ ScFlatBoolRowSegments::RangeData aData;
+ if (mpHiddenRows->getRangeData(rY2, aData) && aData.mbValue)
+ {
+ SCROW nStartRow = aData.mnRow1;
+ if (ValidRow(nStartRow) && nStartRow >= rY1)
+ rY2 = nStartRow;
+ }
+ }
+
+ if (rY1 < rY2)
+ {
+ SCROW nEndRow = -1;
+ if (RowHidden(rY1, nullptr, &nEndRow) && ValidRow(nEndRow) && nEndRow <= rY2)
+ rY1 = nEndRow;
+ }
+}
+
+// Auto-Outline
+
+template< typename T >
+static short DiffSign( T a, T b )
+{
+ return (a<b) ? -1 :
+ (a>b) ? 1 : 0;
+}
+
+namespace {
+
+class OutlineArrayFinder
+{
+ ScRange maRef;
+ SCCOL mnCol;
+ SCTAB mnTab;
+ ScOutlineArray* mpArray;
+ bool mbSizeChanged;
+
+public:
+ OutlineArrayFinder(const ScRange& rRef, SCCOL nCol, SCTAB nTab, ScOutlineArray* pArray, bool bSizeChanged) :
+ maRef(rRef), mnCol(nCol), mnTab(nTab), mpArray(pArray),
+ mbSizeChanged(bSizeChanged) {}
+
+ bool operator() (size_t nRow, const ScFormulaCell* pCell)
+ {
+ SCROW nRow2 = static_cast<SCROW>(nRow);
+
+ if (!pCell->HasRefListExpressibleAsOneReference(maRef))
+ return false;
+
+ if (maRef.aStart.Row() != nRow2 || maRef.aEnd.Row() != nRow2 ||
+ maRef.aStart.Tab() != mnTab || maRef.aEnd.Tab() != mnTab)
+ return false;
+
+ if (DiffSign(maRef.aStart.Col(), mnCol) != DiffSign(maRef.aEnd.Col(), mnCol))
+ return false;
+
+ return mpArray->Insert(maRef.aStart.Col(), maRef.aEnd.Col(), mbSizeChanged);
+ }
+};
+
+}
+
+void ScTable::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ typedef mdds::flat_segment_tree<SCROW, bool> UsedRowsType;
+
+ bool bSizeChanged = false;
+
+ SCCOL nCol;
+ SCROW nRow;
+ bool bFound;
+ ScRange aRef;
+
+ nEndCol = ClampToAllocatedColumns(nEndCol);
+
+ StartOutlineTable();
+
+ // Rows
+
+ UsedRowsType aUsed(0, rDocument.MaxRow()+1, false);
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ aCol[nCol].FindUsed(nStartRow, nEndRow, aUsed);
+ aUsed.build_tree();
+
+ ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ for (nRow=nStartRow; nRow<=nEndRow; nRow++)
+ {
+ bool bUsed = false;
+ SCROW nLastRow = nRow;
+ aUsed.search_tree(nRow, bUsed, nullptr, &nLastRow);
+ if (!bUsed)
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ bFound = false;
+ for (nCol=nStartCol; nCol<=nEndCol && !bFound; nCol++)
+ {
+ ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
+
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ if (!aCell.getFormula()->HasRefListExpressibleAsOneReference(aRef))
+ continue;
+
+ if ( aRef.aStart.Col() == nCol && aRef.aEnd.Col() == nCol &&
+ aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab &&
+ DiffSign( aRef.aStart.Row(), nRow ) ==
+ DiffSign( aRef.aEnd.Row(), nRow ) )
+ {
+ if (rRowArray.Insert( aRef.aStart.Row(), aRef.aEnd.Row(), bSizeChanged ))
+ {
+ bFound = true;
+ }
+ }
+ }
+ }
+
+ // Column
+ ScOutlineArray& rColArray = pOutlineTable->GetColArray();
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ if (aCol[nCol].IsEmptyData())
+ continue;
+
+ OutlineArrayFinder aFunc(aRef, nCol, nTab, &rColArray, bSizeChanged);
+ sc::FindFormula(aCol[nCol].maCells, nStartRow, nEndRow, aFunc);
+ }
+}
+
+ // CopyData - for Query in other range
+
+void ScTable::CopyData( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCCOL nDestCol, SCROW nDestRow, SCTAB nDestTab )
+{
+ //TODO: if used for multiple rows, optimize after columns!
+
+ ScAddress aSrc( nStartCol, nStartRow, nTab );
+ ScAddress aDest( nDestCol, nDestRow, nDestTab );
+ ScRange aRange( aSrc, aDest );
+ bool bThisTab = ( nDestTab == nTab );
+ SCROW nDestY = nDestRow;
+ for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
+ {
+ aSrc.SetRow( nRow );
+ aDest.SetRow( nDestY );
+ SCCOL nDestX = nDestCol;
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ aSrc.SetCol( nCol );
+ aDest.SetCol( nDestX );
+ ScCellValue aCell;
+ aCell.assign(rDocument, ScAddress(nCol, nRow, nTab));
+
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ sc::RefUpdateContext aCxt(rDocument);
+ aCxt.meMode = URM_COPY;
+ aCxt.maRange = aRange;
+ aCxt.mnColDelta = nDestCol - nStartCol;
+ aCxt.mnRowDelta = nDestRow - nStartRow;
+ aCxt.mnTabDelta = nDestTab - nTab;
+ aCell.getFormula()->UpdateReference(aCxt);
+ aCell.getFormula()->aPos = aDest;
+ }
+
+ if (bThisTab)
+ {
+ aCell.release(CreateColumnIfNotExists(nDestX), nDestY);
+ SetPattern( nDestX, nDestY, *GetPattern( nCol, nRow ) );
+ }
+ else
+ {
+ aCell.release(rDocument, aDest);
+ rDocument.SetPattern( aDest, *GetPattern( nCol, nRow ) );
+ }
+
+ ++nDestX;
+ }
+ ++nDestY;
+ }
+}
+
+bool ScTable::RefVisible(const ScFormulaCell* pCell)
+{
+ ScRange aRef;
+
+ if (pCell->HasOneReference(aRef))
+ {
+ if (aRef.aStart.Col()==aRef.aEnd.Col() && aRef.aStart.Tab()==aRef.aEnd.Tab())
+ {
+ SCROW nEndRow;
+ if (!RowFiltered(aRef.aStart.Row(), nullptr, &nEndRow))
+ // row not filtered.
+ nEndRow = ::std::numeric_limits<SCROW>::max();
+
+ if (!ValidRow(nEndRow) || nEndRow < aRef.aEnd.Row())
+ return true; // at least partly visible
+ return false; // completely invisible
+ }
+ }
+
+ return true; // somehow different
+}
+
+OUString ScTable::GetUpperCellString(SCCOL nCol, SCROW nRow)
+{
+ return ScGlobal::getCharClass().uppercase(GetInputString(nCol, nRow).trim());
+}
+
+// Calculate the size of the sheet and set the size on DrawPage
+
+void ScTable::SetDrawPageSize(bool bResetStreamValid, bool bUpdateNoteCaptionPos,
+ ScObjectHandling eObjectHandling)
+{
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if( pDrawLayer )
+ {
+ const sal_Int64 nMax = ::std::numeric_limits<tools::Long>::max();
+ // #i113884# Avoid int32 overflow with possible negative results than can cause bad effects.
+ // If the draw page size is smaller than all rows, only the bottom of the sheet is affected.
+ tools::Long x = std::min(o3tl::convert(GetColOffset(rDocument.MaxCol() + 1),
+ o3tl::Length::twip, o3tl::Length::mm100),
+ nMax);
+ tools::Long y = std::min(o3tl::convert(GetRowOffset(rDocument.MaxRow() + 1),
+ o3tl::Length::twip, o3tl::Length::mm100),
+ nMax);
+
+ if ( IsLayoutRTL() ) // IsNegativePage
+ x = -x;
+
+ pDrawLayer->SetPageSize(static_cast<sal_uInt16>(nTab), Size(x, y), bUpdateNoteCaptionPos,
+ eObjectHandling);
+ }
+
+ // #i102616# actions that modify the draw page size count as sheet modification
+ // (exception: InitDrawLayer)
+ if (bResetStreamValid)
+ SetStreamValid(false);
+}
+
+void ScTable::SetRangeName(std::unique_ptr<ScRangeName> pNew)
+{
+ mpRangeName = std::move(pNew);
+
+ //fdo#39792: mark stream as invalid, otherwise new ScRangeName will not be written to file
+ SetStreamValid(false);
+}
+
+ScRangeName* ScTable::GetRangeName() const
+{
+ if (!mpRangeName)
+ mpRangeName.reset(new ScRangeName);
+ return mpRangeName.get();
+}
+
+tools::Long ScTable::GetRowOffset( SCROW nRow, bool bHiddenAsZero ) const
+{
+ tools::Long n = 0;
+ if ( mpHiddenRows && mpRowHeights )
+ {
+ if (nRow == 0)
+ return 0;
+ else if (nRow == 1)
+ return GetRowHeight(0, nullptr, nullptr, bHiddenAsZero );
+
+ n = GetTotalRowHeight(0, nRow-1, bHiddenAsZero);
+#if OSL_DEBUG_LEVEL > 0
+ if (n == ::std::numeric_limits<tools::Long>::max())
+ OSL_FAIL("ScTable::GetRowOffset: row heights overflow");
+#endif
+ }
+ else
+ {
+ OSL_FAIL("GetRowOffset: Data missing");
+ }
+ return n;
+}
+
+SCROW ScTable::GetRowForHeight(tools::Long nHeight) const
+{
+ tools::Long nSum = 0;
+
+ ScFlatBoolRowSegments::RangeData aData;
+
+ ScFlatUInt16RowSegments::RangeData aRowHeightRange;
+ aRowHeightRange.mnRow2 = -1;
+ aRowHeightRange.mnValue = 1; // silence MSVC C4701
+
+ for (SCROW nRow = 0; nRow <= rDocument.MaxRow(); ++nRow)
+ {
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ // Failed to fetch the range data for whatever reason.
+ break;
+
+ if (aData.mbValue)
+ {
+ // This row is hidden. Skip ahead all hidden rows.
+ nRow = aData.mnRow2;
+ continue;
+ }
+
+ if (aRowHeightRange.mnRow2 < nRow)
+ {
+ if (!mpRowHeights->getRangeData(nRow, aRowHeightRange))
+ // Failed to fetch the range data for whatever reason.
+ break;
+ }
+
+ // find the last common row between hidden & height spans
+ SCROW nLastCommon = std::min(aData.mnRow2, aRowHeightRange.mnRow2);
+ assert (nLastCommon >= nRow);
+ SCROW nCommon = nLastCommon - nRow + 1;
+
+ // how much further to go ?
+ tools::Long nPixelsLeft = nHeight - nSum;
+ tools::Long nCommonPixels = static_cast<tools::Long>(aRowHeightRange.mnValue) * nCommon;
+
+ // are we in the zone ?
+ if (nCommonPixels > nPixelsLeft)
+ {
+ nRow += (nPixelsLeft + aRowHeightRange.mnValue - 1) / aRowHeightRange.mnValue;
+
+ // FIXME: finding this next row is far from elegant,
+ // we have a single caller, which subtracts one as well(!?)
+ if (nRow >= rDocument.MaxRow())
+ return rDocument.MaxRow();
+
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ // Failed to fetch the range data for whatever reason.
+ break;
+
+ if (aData.mbValue)
+ // These rows are hidden.
+ nRow = aData.mnRow2 + 1;
+
+ return nRow <= rDocument.MaxRow() ? nRow : rDocument.MaxRow();
+ }
+
+ // skip the range and keep hunting
+ nSum += nCommonPixels;
+ nRow = nLastCommon;
+ }
+ return -1;
+}
+
+tools::Long ScTable::GetColOffset( SCCOL nCol, bool bHiddenAsZero ) const
+{
+ tools::Long n = 0;
+ if ( mpColWidth )
+ {
+ auto colWidthIt = mpColWidth->begin();
+ for (SCCOL i = 0; i < nCol; (++i < nCol) ? ++colWidthIt : (void)false)
+ if (!( bHiddenAsZero && ColHidden(i) ))
+ n += *colWidthIt;
+ }
+ else
+ {
+ OSL_FAIL("GetColumnOffset: Data missing");
+ }
+ return n;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
new file mode 100644
index 0000000000..359cc5dcc4
--- /dev/null
+++ b/sc/source/core/data/table3.cxx
@@ -0,0 +1,3148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/random.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <stdlib.h>
+#include <com/sun/star/i18n/KParseTokens.hpp>
+#include <com/sun/star/i18n/KParseType.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <refdata.hxx>
+#include <table.hxx>
+#include <scitems.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <stlpool.hxx>
+#include <patattr.hxx>
+#include <subtotal.hxx>
+#include <markdata.hxx>
+#include <rangelst.hxx>
+#include <userlist.hxx>
+#include <progress.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <subtotalparam.hxx>
+#include <docpool.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <mtvcellfunc.hxx>
+#include <columnspanset.hxx>
+#include <fstalgorithm.hxx>
+#include <listenercontext.hxx>
+#include <sharedformula.hxx>
+#include <stlsheet.hxx>
+#include <refhint.hxx>
+#include <listenerquery.hxx>
+#include <bcaslot.hxx>
+#include <reordermap.hxx>
+#include <drwlayer.hxx>
+#include <queryevaluator.hxx>
+#include <scopetools.hxx>
+
+#include <svl/sharedstringpool.hxx>
+
+#include <memory>
+#include <set>
+#include <unordered_set>
+#include <vector>
+#include <mdds/flat_segment_tree.hpp>
+
+using namespace ::com::sun::star;
+
+namespace naturalsort {
+
+using namespace ::com::sun::star::i18n;
+
+/** Splits a given string into three parts: the prefix, number string, and
+ the suffix.
+
+ @param sWhole
+ Original string to be split into pieces
+
+ @param sPrefix
+ Prefix string that consists of the part before the first number token.
+ If no number was found, sPrefix is unchanged.
+
+ @param sSuffix
+ String after the last number token. This may still contain number strings.
+ If no number was found, sSuffix is unchanged.
+
+ @param fNum
+ Number converted from the middle number string
+ If no number was found, fNum is unchanged.
+
+ @return Returns TRUE if a numeral element is found in a given string, or
+ FALSE if no numeral element is found.
+*/
+static bool SplitString( const OUString &sWhole,
+ OUString &sPrefix, OUString &sSuffix, double &fNum )
+{
+ // Get prefix element, search for any digit and stop.
+ sal_Int32 nPos = 0;
+ while (nPos < sWhole.getLength())
+ {
+ const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos);
+ if (nType & KCharacterType::DIGIT)
+ break;
+ sWhole.iterateCodePoints( &nPos );
+ }
+
+ // Return FALSE if no numeral element is found
+ if ( nPos == sWhole.getLength() )
+ return false;
+
+ // Get numeral element
+ const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep();
+ ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken(
+ KParseType::ANY_NUMBER, sWhole, nPos,
+ KParseTokens::ANY_NUMBER, "", KParseTokens::ANY_NUMBER, sUser );
+
+ if ( aPRNum.EndPos == nPos )
+ {
+ SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
+ nPos << " : " << sWhole);
+ return false;
+ }
+
+ sPrefix = sWhole.copy( 0, nPos );
+ fNum = aPRNum.Value;
+ sSuffix = sWhole.copy( aPRNum.EndPos );
+
+ return true;
+}
+
+/** Naturally compares two given strings.
+
+ This is the main function that should be called externally. It returns
+ either 1, 0, or -1 depending on the comparison result of given two strings.
+
+ @param sInput1
+ Input string 1
+
+ @param sInput2
+ Input string 2
+
+ @param bCaseSens
+ Boolean value for case sensitivity
+
+ @param pData
+ Pointer to user defined sort list
+
+ @param pCW
+ Pointer to collator wrapper for normal string comparison
+
+ @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
+ sInput2 is greater.
+*/
+static short Compare( const OUString &sInput1, const OUString &sInput2,
+ const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
+{
+ OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
+
+ do
+ {
+ double nNum1, nNum2;
+ bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
+ bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
+
+ short nPreRes; // Prefix comparison result
+ if ( pData )
+ {
+ if ( bCaseSens )
+ {
+ if ( !bNumFound1 || !bNumFound2 )
+ return static_cast<short>(pData->Compare( sStr1, sStr2 ));
+ else
+ nPreRes = pData->Compare( sPre1, sPre2 );
+ }
+ else
+ {
+ if ( !bNumFound1 || !bNumFound2 )
+ return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
+ else
+ nPreRes = pData->ICompare( sPre1, sPre2 );
+ }
+ }
+ else
+ {
+ if ( !bNumFound1 || !bNumFound2 )
+ return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
+ else
+ nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
+ }
+
+ // Prefix strings differ. Return immediately.
+ if ( nPreRes != 0 ) return nPreRes;
+
+ if ( nNum1 != nNum2 )
+ {
+ if ( nNum1 < nNum2 ) return -1;
+ return (nNum1 > nNum2) ? 1 : 0;
+ }
+
+ // The prefix and the first numerical elements are equal, but the suffix
+ // strings may still differ. Stay in the loop.
+
+ sStr1 = sSuf1;
+ sStr2 = sSuf2;
+
+ } while (true);
+
+ return 0;
+}
+
+}
+
+namespace {
+
+struct ScSortInfo final
+{
+ ScRefCellValue maCell;
+ SCCOLROW nOrg;
+};
+
+}
+
+class ScSortInfoArray
+{
+public:
+
+ struct Cell
+ {
+ ScRefCellValue maCell;
+ const sc::CellTextAttr* mpAttr;
+ const ScPostIt* mpNote;
+ std::vector<SdrObject*> maDrawObjects;
+ const ScPatternAttr* mpPattern;
+
+ Cell() : mpAttr(nullptr), mpNote(nullptr), mpPattern(nullptr) {}
+ };
+
+ struct Row
+ {
+ std::vector<Cell> maCells;
+
+ bool mbHidden:1;
+ bool mbFiltered:1;
+
+ explicit Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
+ };
+
+ typedef std::vector<Row> RowsType;
+
+private:
+ std::unique_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
+
+ std::vector<std::unique_ptr<ScSortInfo[]>> mvppInfo;
+ SCCOLROW nStart;
+ SCCOLROW mnLastIndex; /// index of last non-empty cell position.
+
+ std::vector<SCCOLROW> maOrderIndices;
+ bool mbKeepQuery;
+ bool mbUpdateRefs;
+
+public:
+ ScSortInfoArray(const ScSortInfoArray&) = delete;
+ const ScSortInfoArray& operator=(const ScSortInfoArray&) = delete;
+
+ ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
+ mvppInfo(nSorts),
+ nStart( nInd1 ),
+ mnLastIndex(nInd2),
+ mbKeepQuery(false),
+ mbUpdateRefs(false)
+ {
+ SCSIZE nCount( nInd2 - nInd1 + 1 );
+ if (nSorts)
+ {
+ for ( sal_uInt16 nSort = 0; nSort < nSorts; nSort++ )
+ {
+ mvppInfo[nSort].reset(new ScSortInfo[nCount]);
+ }
+ }
+
+ for (size_t i = 0; i < nCount; ++i)
+ maOrderIndices.push_back(i+nStart);
+ }
+
+ void SetKeepQuery( bool b ) { mbKeepQuery = b; }
+
+ bool IsKeepQuery() const { return mbKeepQuery; }
+
+ void SetUpdateRefs( bool b ) { mbUpdateRefs = b; }
+
+ bool IsUpdateRefs() const { return mbUpdateRefs; }
+
+ /**
+ * Call this only during normal sorting, not from reordering.
+ */
+ std::unique_ptr<ScSortInfo[]> const & GetFirstArray() const
+ {
+ return mvppInfo[0];
+ }
+
+ /**
+ * Call this only during normal sorting, not from reordering.
+ */
+ ScSortInfo & Get( sal_uInt16 nSort, SCCOLROW nInd )
+ {
+ return mvppInfo[nSort][ nInd - nStart ];
+ }
+
+ /**
+ * Call this only during normal sorting, not from reordering.
+ */
+ void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
+ {
+ if (nInd1 == nInd2) // avoid self-move-assign
+ return;
+ SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
+ SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
+ for ( sal_uInt16 nSort = 0; nSort < static_cast<sal_uInt16>(mvppInfo.size()); nSort++ )
+ {
+ auto & ppInfo = mvppInfo[nSort];
+ std::swap(ppInfo[n1], ppInfo[n2]);
+ }
+
+ std::swap(maOrderIndices[n1], maOrderIndices[n2]);
+
+ if (mpRows)
+ {
+ // Swap rows in data table.
+ RowsType& rRows = *mpRows;
+ std::swap(rRows[n1], rRows[n2]);
+ }
+ }
+
+ void SetOrderIndices( std::vector<SCCOLROW>&& rIndices )
+ {
+ maOrderIndices = std::move(rIndices);
+ }
+
+ /**
+ * @param rIndices indices are actual row positions on the sheet, not an
+ * offset from the top row.
+ */
+ void ReorderByRow( const std::vector<SCCOLROW>& rIndices )
+ {
+ if (!mpRows)
+ return;
+
+ RowsType& rRows = *mpRows;
+
+ std::vector<SCCOLROW> aOrderIndices2;
+ aOrderIndices2.reserve(rIndices.size());
+
+ RowsType aRows2;
+ aRows2.reserve(rRows.size());
+
+ for (const auto& rIndex : rIndices)
+ {
+ size_t nPos = rIndex - nStart; // switch to an offset to top row.
+ aRows2.push_back(rRows[nPos]);
+ aOrderIndices2.push_back(maOrderIndices[nPos]);
+ }
+
+ rRows.swap(aRows2);
+ maOrderIndices.swap(aOrderIndices2);
+ }
+
+ sal_uInt16 GetUsedSorts() const { return mvppInfo.size(); }
+
+ SCCOLROW GetStart() const { return nStart; }
+ SCCOLROW GetLast() const { return mnLastIndex; }
+
+ const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; }
+
+ RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
+ {
+ mpRows.reset(new RowsType);
+ mpRows->resize(nRowSize, Row(nColSize));
+ return *mpRows;
+ }
+
+ RowsType* GetDataRows()
+ {
+ return mpRows.get();
+ }
+};
+
+// Assume that we can handle 512MB, which with a ~100 bytes
+// ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
+// overhead in one chunk.
+constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell);
+
+namespace {
+
+void initDataRows(
+ ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ bool bHiddenFiltered, bool bPattern, bool bCellNotes, bool bCellDrawObjects, bool bOnlyDataAreaExtras )
+{
+ // Fill row-wise data table.
+ ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
+
+ const std::vector<SCCOLROW>& rOrderIndices = rArray.GetOrderIndices();
+ assert(!bOnlyDataAreaExtras || (rOrderIndices.size() == static_cast<size_t>(nRow2 - nRow1 + 1)
+ && nRow1 == rArray.GetStart()));
+
+ ScDrawLayer* pDrawLayer = (bCellDrawObjects ? rTab.GetDoc().GetDrawLayer() : nullptr);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ ScColumn& rCol = rCols[nCol];
+
+ // Skip reordering of cell formats if the whole span is on the same pattern entry.
+ bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
+
+ sc::ColumnBlockConstPosition aBlockPos;
+ rCol.InitBlockPosition(aBlockPos);
+ std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects;
+ if (pDrawLayer)
+ aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2);
+
+ for (SCROW nR = nRow1; nR <= nRow2; ++nR)
+ {
+ const SCROW nRow = (bOnlyDataAreaExtras ? rOrderIndices[nR - rArray.GetStart()] : nR);
+ ScSortInfoArray::Row& rRow = rRows[nR-nRow1];
+ ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
+ if (!bOnlyDataAreaExtras)
+ {
+ rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
+ rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
+ }
+ if (bCellNotes)
+ rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
+ if (pDrawLayer)
+ rCell.maDrawObjects = aRowDrawObjects[nRow];
+
+ if (!bUniformPattern && bPattern)
+ rCell.mpPattern = rCol.GetPattern(nRow);
+ }
+ }
+
+ if (!bOnlyDataAreaExtras && bHiddenFiltered)
+ {
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
+ rRow.mbHidden = rTab.RowHidden(nRow);
+ rRow.mbFiltered = rTab.RowFiltered(nRow);
+ }
+ }
+}
+
+}
+
+std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
+{
+ std::unique_ptr<ScSortInfoArray> pArray;
+
+ if (rParam.mbByRow)
+ {
+ // Create a sort info array with just the data table.
+ SCROW nRow1 = rParam.maSortRange.aStart.Row();
+ SCROW nRow2 = rParam.maSortRange.aEnd.Row();
+ SCCOL nCol1 = rParam.maSortRange.aStart.Col();
+ SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
+
+ pArray.reset(new ScSortInfoArray(0, nRow1, nRow2));
+ pArray->SetKeepQuery(rParam.mbHiddenFiltered);
+ pArray->SetUpdateRefs(rParam.mbUpdateRefs);
+
+ CreateColumnIfNotExists(nCol2);
+ initDataRows( *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2, rParam.mbHiddenFiltered,
+ rParam.maDataAreaExtras.mbCellFormats, true, true, false);
+ }
+ else
+ {
+ SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
+ SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
+
+ pArray.reset(new ScSortInfoArray(0, nCol1, nCol2));
+ pArray->SetKeepQuery(rParam.mbHiddenFiltered);
+ pArray->SetUpdateRefs(rParam.mbUpdateRefs);
+ }
+
+ return pArray;
+}
+
+std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray(
+ const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
+ bool bKeepQuery, bool bUpdateRefs )
+{
+ sal_uInt16 nUsedSorts = 1;
+ while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
+ nUsedSorts++;
+ std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ));
+ pArray->SetKeepQuery(bKeepQuery);
+ pArray->SetUpdateRefs(bUpdateRefs);
+
+ if ( rSortParam.bByRow )
+ {
+ for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
+ {
+ SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
+ ScColumn* pCol = &aCol[nCol];
+ sc::ColumnBlockConstPosition aBlockPos;
+ pCol->InitBlockPosition(aBlockPos);
+ for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
+ {
+ ScSortInfo & rInfo = pArray->Get( nSort, nRow );
+ rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow);
+ rInfo.nOrg = nRow;
+ }
+ }
+
+ CreateColumnIfNotExists(rSortParam.nCol2);
+ initDataRows( *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2, bKeepQuery,
+ rSortParam.aDataAreaExtras.mbCellFormats, true, true, false);
+ }
+ else
+ {
+ for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
+ {
+ SCROW nRow = rSortParam.maKeyState[nSort].nField;
+ for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
+ nCol <= static_cast<SCCOL>(nInd2); nCol++ )
+ {
+ ScSortInfo & rInfo = pArray->Get( nSort, nCol );
+ rInfo.maCell = GetCellValue(nCol, nRow);
+ rInfo.nOrg = nCol;
+ }
+ }
+ }
+ return pArray;
+}
+
+namespace {
+
+struct SortedColumn
+{
+ typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
+
+ sc::CellStoreType maCells;
+ sc::CellTextAttrStoreType maCellTextAttrs;
+ sc::BroadcasterStoreType maBroadcasters;
+ sc::CellNoteStoreType maCellNotes;
+ std::vector<std::vector<SdrObject*>> maCellDrawObjects;
+
+ PatRangeType maPatterns;
+ PatRangeType::const_iterator miPatternPos;
+
+ SortedColumn(const SortedColumn&) = delete;
+ const SortedColumn operator=(const SortedColumn&) = delete;
+
+ explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) :
+ maCells(nTopEmptyRows),
+ maCellTextAttrs(nTopEmptyRows),
+ maBroadcasters(nTopEmptyRows),
+ maCellNotes(nTopEmptyRows),
+ maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr),
+ miPatternPos(maPatterns.begin()) {}
+
+ void setPattern( SCROW nRow, const ScPatternAttr* pPat )
+ {
+ miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
+ }
+};
+
+struct SortedRowFlags
+{
+ typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
+
+ FlagsType maRowsHidden;
+ FlagsType maRowsFiltered;
+ FlagsType::const_iterator miPosHidden;
+ FlagsType::const_iterator miPosFiltered;
+
+ SortedRowFlags(const ScSheetLimits& rSheetLimits) :
+ maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false),
+ maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false),
+ miPosHidden(maRowsHidden.begin()),
+ miPosFiltered(maRowsFiltered.begin()) {}
+
+ void setRowHidden( SCROW nRow, bool b )
+ {
+ miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
+ }
+
+ void setRowFiltered( SCROW nRow, bool b )
+ {
+ miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
+ }
+
+ void swap( SortedRowFlags& r )
+ {
+ maRowsHidden.swap(r.maRowsHidden);
+ maRowsFiltered.swap(r.maRowsFiltered);
+
+ // Just reset the position hints.
+ miPosHidden = maRowsHidden.begin();
+ miPosFiltered = maRowsFiltered.begin();
+ }
+};
+
+struct PatternSpan
+{
+ SCROW mnRow1;
+ SCROW mnRow2;
+ const ScPatternAttr* mpPattern;
+
+ PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
+ mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
+};
+
+}
+
+bool ScTable::IsSortCollatorGlobal() const
+{
+ return pSortCollator == &ScGlobal::GetCollator() ||
+ pSortCollator == &ScGlobal::GetCaseCollator();
+}
+
+void ScTable::InitSortCollator( const ScSortParam& rPar )
+{
+ if ( !rPar.aCollatorLocale.Language.isEmpty() )
+ {
+ if ( !pSortCollator || IsSortCollatorGlobal() )
+ pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
+ pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
+ rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
+ }
+ else
+ { // SYSTEM
+ DestroySortCollator();
+ pSortCollator = &ScGlobal::GetCollator(rPar.bCaseSens);
+ }
+}
+
+void ScTable::DestroySortCollator()
+{
+ if ( pSortCollator )
+ {
+ if ( !IsSortCollatorGlobal() )
+ delete pSortCollator;
+ pSortCollator = nullptr;
+ }
+}
+
+namespace {
+
+template<typename Hint, typename ReorderMap, typename Index>
+class ReorderNotifier
+{
+ Hint maHint;
+public:
+ ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) :
+ maHint(rMap, nTab, nPos1, nPos2) {}
+
+ void operator() ( SvtListener* p )
+ {
+ p->Notify(maHint);
+ }
+};
+
+class FormulaGroupPosCollector
+{
+ sc::RefQueryFormulaGroup& mrQuery;
+
+public:
+ explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
+
+ void operator() ( const SvtListener* p )
+ {
+ p->Query(mrQuery);
+ }
+};
+
+void fillSortedColumnArray(
+ std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
+ SortedRowFlags& rRowFlags,
+ std::vector<SvtListener*>& rCellListeners,
+ ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable,
+ bool bOnlyDataAreaExtras )
+{
+ assert(!bOnlyDataAreaExtras || !pArray->IsUpdateRefs());
+
+ SCROW nRow1 = pArray->GetStart();
+ ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
+ std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices();
+
+ size_t nColCount = nCol2 - nCol1 + 1;
+ std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
+ SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
+ aSortedCols.reserve(nColCount);
+ for (size_t i = 0; i < nColCount; ++i)
+ {
+ // In the sorted column container, element positions and row
+ // positions must match, else formula cells may mis-behave during
+ // grouping.
+ aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
+ }
+
+ for (size_t i = 0; i < pRows->size(); ++i)
+ {
+ const SCROW nRow = nRow1 + i;
+
+ ScSortInfoArray::Row& rRow = (*pRows)[i];
+ for (size_t j = 0; j < rRow.maCells.size(); ++j)
+ {
+ ScSortInfoArray::Cell& rCell = rRow.maCells[j];
+
+ // If bOnlyDataAreaExtras,
+ // sc::CellStoreType aSortedCols.at(j)->maCells
+ // and
+ // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs
+ // are by definition all empty mdds::multi_type_vector, so nothing
+ // needs to be done to push *all* empty.
+
+ if (!bOnlyDataAreaExtras)
+ {
+ sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells;
+ switch (rCell.maCell.getType())
+ {
+ case CELLTYPE_STRING:
+ assert(rCell.mpAttr);
+ rCellStore.push_back(*rCell.maCell.getSharedString());
+ break;
+ case CELLTYPE_VALUE:
+ assert(rCell.mpAttr);
+ rCellStore.push_back(rCell.maCell.getDouble());
+ break;
+ case CELLTYPE_EDIT:
+ assert(rCell.mpAttr);
+ rCellStore.push_back(rCell.maCell.getEditText()->Clone().release());
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ assert(rCell.mpAttr);
+ ScAddress aOldPos = rCell.maCell.getFormula()->aPos;
+
+ const ScAddress aCellPos(nCol1 + j, nRow, nTab);
+ ScFormulaCell* pNew = rCell.maCell.getFormula()->Clone( aCellPos );
+ if (pArray->IsUpdateRefs())
+ {
+ pNew->CopyAllBroadcasters(*rCell.maCell.getFormula());
+ pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
+ }
+ else
+ {
+ pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
+ }
+
+ if (!rCellListeners.empty())
+ {
+ // Original source cells will be deleted during
+ // sc::CellStoreType::transfer(), SvtListener is a base
+ // class, so we need to replace it.
+ auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.getFormula()));
+ if (it != rCellListeners.end())
+ *it = pNew;
+ }
+
+ rCellStore.push_back(pNew);
+ }
+ break;
+ default:
+ //assert(!rCell.mpAttr);
+ // This assert doesn't hold, for example
+ // CopyCellsFromClipHandler may omit copying cells during
+ // PasteSpecial for which CopyTextAttrsFromClipHandler
+ // still copies a CellTextAttr. So if that really is not
+ // expected then fix it there.
+ rCellStore.push_back_empty();
+ }
+
+ sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs;
+ if (rCell.mpAttr)
+ rAttrStore.push_back(*rCell.mpAttr);
+ else
+ rAttrStore.push_back_empty();
+ }
+
+ if (pArray->IsUpdateRefs())
+ {
+ // At this point each broadcaster instance is managed by 2
+ // containers. We will release those in the original storage
+ // below before transferring them to the document.
+ const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
+ sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters;
+ if (pBroadcaster)
+ // A const pointer would be implicitly converted to a bool type.
+ rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster));
+ else
+ rBCStore.push_back_empty();
+ }
+
+ // The same with cell note instances ...
+ sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes;
+ if (rCell.mpNote)
+ rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
+ else
+ rNoteStore.push_back_empty();
+
+ // Add cell anchored images
+ aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects);
+
+ if (rCell.mpPattern)
+ aSortedCols.at(j)->setPattern(nRow, rCell.mpPattern);
+ }
+
+ if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
+ {
+ // Hidden and filtered flags are first converted to segments.
+ aRowFlags.setRowHidden(nRow, rRow.mbHidden);
+ aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
+ }
+
+ if (pProgress)
+ pProgress->SetStateOnPercent(i);
+ }
+
+ rSortedCols.swap(aSortedCols);
+ rRowFlags.swap(aRowFlags);
+}
+
+void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
+{
+ if (nTop < rRange.aStart.Row())
+ rRange.aStart.SetRow(nTop);
+
+ if (rRange.aEnd.Row() < nBottom)
+ rRange.aEnd.SetRow(nBottom);
+}
+
+class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
+{
+ std::vector<ScFormulaCell*>& mrCells;
+ ScColumn* mpCol;
+
+public:
+ explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
+ mrCells(rCells), mpCol(nullptr) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mpCol = pCol;
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ assert(mpCol);
+
+ if (!bVal)
+ return;
+
+ mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
+ }
+};
+
+class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
+{
+ ScColumn* mpCol;
+
+ std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
+ sc::StartListeningContext maStartCxt;
+ sc::EndListeningContext maEndCxt;
+
+public:
+ explicit ListenerStartAction( ScDocument& rDoc ) :
+ mpCol(nullptr),
+ mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)),
+ maStartCxt(rDoc, mpPosSet),
+ maEndCxt(rDoc, mpPosSet) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mpCol = pCol;
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ assert(mpCol);
+
+ if (!bVal)
+ return;
+
+ mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
+ }
+};
+
+}
+
+void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray* pArray,
+ SCCOL nDataCol1, SCCOL nDataCol2,
+ const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
+{
+ const SCROW nRow1 = pArray->GetStart();
+ const SCROW nLastRow = pArray->GetLast();
+ const SCCOL nChunkCols = std::max<SCCOL>( 1, kSortCellsChunk / (nLastRow - nRow1 + 1));
+ // Before data area.
+ for (SCCOL nCol = rDataAreaExtras.mnStartCol; nCol < nDataCol1; nCol += nChunkCols)
+ {
+ const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, nDataCol1 - 1);
+ CreateColumnIfNotExists(nEndCol);
+ initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
+ rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
+ SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
+ }
+ // Behind data area.
+ for (SCCOL nCol = nDataCol2 + 1; nCol <= rDataAreaExtras.mnEndCol; nCol += nChunkCols)
+ {
+ const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, rDataAreaExtras.mnEndCol);
+ CreateColumnIfNotExists(nEndCol);
+ initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
+ rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
+ SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
+ }
+}
+
+void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray* pArray,
+ SCROW nDataRow1, SCROW nDataRow2, const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
+{
+ const SCCOL nCol1 = static_cast<SCCOL>(pArray->GetStart());
+ const SCCOL nLastCol = static_cast<SCCOL>(pArray->GetLast());
+ const SCROW nChunkRows = std::max<SCROW>( 1, kSortCellsChunk / (nLastCol - nCol1 + 1));
+ // Above data area.
+ for (SCROW nRow = rDataAreaExtras.mnStartRow; nRow < nDataRow1; nRow += nChunkRows)
+ {
+ const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, nDataRow1 - 1);
+ SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
+ }
+ // Below data area.
+ for (SCROW nRow = nDataRow2 + 1; nRow <= rDataAreaExtras.mnEndRow; nRow += nChunkRows)
+ {
+ const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, rDataAreaExtras.mnEndRow);
+ SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
+ }
+}
+
+void ScTable::SortReorderByColumn(
+ const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
+{
+ SCCOLROW nStart = pArray->GetStart();
+ SCCOLROW nLast = pArray->GetLast();
+
+ std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
+ size_t nCount = aIndices.size();
+
+ // Cut formula grouping at row and reference boundaries before the reordering.
+ ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
+ for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
+ aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
+
+ // Collect all listeners of cell broadcasters of sorted range.
+ std::vector<SvtListener*> aCellListeners;
+
+ if (!pArray->IsUpdateRefs())
+ {
+ // Collect listeners of cell broadcasters.
+ for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
+ aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
+
+ // Remove any duplicate listener entries. We must ensure that we
+ // notify each unique listener only once.
+ std::sort(aCellListeners.begin(), aCellListeners.end());
+ aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
+
+ // Notify the cells' listeners to stop listening.
+ /* TODO: for performance this could be enhanced to stop and later
+ * restart only listening to within the reordered range and keep
+ * listening to everything outside untouched. */
+ sc::RefStopListeningHint aHint;
+ for (auto const & l : aCellListeners)
+ l->Notify(aHint);
+ }
+
+ // table to keep track of column index to position in the index table.
+ std::vector<SCCOLROW> aPosTable(nCount);
+ for (size_t i = 0; i < nCount; ++i)
+ aPosTable[aIndices[i]-nStart] = i;
+
+ SCCOLROW nDest = nStart;
+ for (size_t i = 0; i < nCount; ++i, ++nDest)
+ {
+ SCCOLROW nSrc = aIndices[i];
+ if (nDest != nSrc)
+ {
+ aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
+
+ // Update the position of the index that was originally equal to nDest.
+ size_t nPos = aPosTable[nDest-nStart];
+ aIndices[nPos] = nSrc;
+ aPosTable[nSrc-nStart] = nPos;
+ }
+
+ if (pProgress)
+ pProgress->SetStateOnPercent(i);
+ }
+
+ // Reset formula cell positions which became out-of-sync after column reordering.
+ bool bUpdateRefs = pArray->IsUpdateRefs();
+ for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
+ aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
+
+ if (pArray->IsUpdateRefs())
+ {
+ // Set up column reorder map (for later broadcasting of reference updates).
+ sc::ColRowReorderMapType aColMap;
+ const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
+ for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
+ {
+ SCCOL nNew = i + nStart;
+ SCCOL nOld = rOldIndices[i];
+ aColMap.emplace(nOld, nNew);
+ }
+
+ // Collect all listeners within sorted range ahead of time.
+ std::vector<SvtListener*> aListeners;
+
+ for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
+ aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
+
+ // Get all area listeners that listen on one column within the range
+ // and end their listening.
+ ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
+ std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
+ aMoveRange, sc::AreaOverlapType::OneColumnInside);
+ {
+ for (auto& rAreaListener : aAreaListeners)
+ {
+ rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
+ aListeners.push_back( rAreaListener.mpListener);
+ }
+ }
+
+ // Remove any duplicate listener entries and notify all listeners
+ // afterward. We must ensure that we notify each unique listener only
+ // once.
+ std::sort(aListeners.begin(), aListeners.end());
+ aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
+ ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
+ std::for_each(aListeners.begin(), aListeners.end(), aFunc);
+
+ // Re-start area listeners on the reordered columns.
+ {
+ for (auto& rAreaListener : aAreaListeners)
+ {
+ ScRange aNewRange = rAreaListener.maArea;
+ sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
+ if (itCol != aColMap.end())
+ {
+ aNewRange.aStart.SetCol( itCol->second);
+ aNewRange.aEnd.SetCol( itCol->second);
+ }
+ rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
+ }
+ }
+ }
+ else // !(pArray->IsUpdateRefs())
+ {
+ // Notify the cells' listeners to (re-)start listening.
+ sc::RefStartListeningHint aHint;
+ for (auto const & l : aCellListeners)
+ l->Notify(aHint);
+ }
+
+ // Re-join formulas at row boundaries now that all the references have
+ // been adjusted for column reordering.
+ for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
+ {
+ sc::CellStoreType& rCells = aCol[nCol].maCells;
+ sc::CellStoreType::position_type aPos = rCells.position(nRow1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (nRow2 < rDocument.MaxRow())
+ {
+ aPos = rCells.position(aPos.first, nRow2+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+ }
+}
+
+void ScTable::SortReorderByRow( ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2,
+ ScProgress* pProgress, bool bOnlyDataAreaExtras )
+{
+ assert(!pArray->IsUpdateRefs());
+
+ if (nCol2 < nCol1)
+ return;
+
+ // bOnlyDataAreaExtras:
+ // Data area extras by definition do not have any cell content so no
+ // formula cells either, so that handling doesn't need to be executed.
+ // However, there may be listeners of formulas listening to broadcasters of
+ // empty cells.
+
+ SCROW nRow1 = pArray->GetStart();
+ SCROW nRow2 = pArray->GetLast();
+
+ // Collect all listeners of cell broadcasters of sorted range.
+ std::vector<SvtListener*> aCellListeners;
+
+ // When the update ref mode is disabled, we need to detach all formula
+ // cells in the sorted range before reordering, and re-start them
+ // afterward.
+ if (!bOnlyDataAreaExtras)
+ {
+ sc::EndListeningContext aCxt(rDocument);
+ DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
+ }
+
+ // Collect listeners of cell broadcasters.
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
+
+ // Remove any duplicate listener entries. We must ensure that we notify
+ // each unique listener only once.
+ std::sort(aCellListeners.begin(), aCellListeners.end());
+ aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
+
+ // Notify the cells' listeners to stop listening.
+ /* TODO: for performance this could be enhanced to stop and later
+ * restart only listening to within the reordered range and keep
+ * listening to everything outside untouched. */
+ {
+ sc::RefStopListeningHint aHint;
+ for (auto const & l : aCellListeners)
+ l->Notify(aHint);
+ }
+
+ // Split formula groups at the sort range boundaries (if applicable).
+ if (!bOnlyDataAreaExtras)
+ {
+ std::vector<SCROW> aRowBounds
+ {
+ nRow1,
+ nRow2+1
+ };
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ SplitFormulaGroups(nCol, aRowBounds);
+ }
+
+ // Cells in the data rows only reference values in the document. Make
+ // a copy before updating the document.
+ std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
+ SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
+ fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2,
+ pProgress, this, bOnlyDataAreaExtras);
+
+ for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
+ {
+ SCCOL nThisCol = i + nCol1;
+
+ if (!bOnlyDataAreaExtras)
+ {
+ {
+ sc::CellStoreType& rDest = aCol[nThisCol].maCells;
+ sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
+
+ {
+ sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
+ sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
+ }
+
+ {
+ sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
+ sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
+
+ // Do the same as broadcaster storage transfer (to prevent double deletion).
+ rDest.release_range(nRow1, nRow2);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
+ }
+
+ // Update draw object positions
+ aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
+
+ {
+ // Get all row spans where the pattern is not NULL.
+ std::vector<PatternSpan> aSpans =
+ sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
+ aSortedCols[i]->maPatterns);
+
+ for (const auto& rSpan : aSpans)
+ {
+ assert(rSpan.mpPattern); // should never be NULL.
+ rDocument.GetPool()->DirectPutItemInPool(*rSpan.mpPattern);
+ }
+
+ for (const auto& rSpan : aSpans)
+ {
+ aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
+ rDocument.GetPool()->DirectRemoveItemFromPool(*rSpan.mpPattern);
+ }
+ }
+
+ aCol[nThisCol].CellStorageModified();
+ }
+
+ if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
+ {
+ aRowFlags.maRowsHidden.build_tree();
+ aRowFlags.maRowsFiltered.build_tree();
+
+ // Remove all flags in the range first.
+ SetRowHidden(nRow1, nRow2, false);
+ SetRowFiltered(nRow1, nRow2, false);
+
+ std::vector<sc::RowSpan> aSpans =
+ sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
+
+ for (const auto& rSpan : aSpans)
+ SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
+
+ aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
+
+ for (const auto& rSpan : aSpans)
+ SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
+ }
+
+ // Notify the cells' listeners to (re-)start listening.
+ {
+ sc::RefStartListeningHint aHint;
+ for (auto const & l : aCellListeners)
+ l->Notify(aHint);
+ }
+
+ if (!bOnlyDataAreaExtras)
+ {
+ // Re-group columns in the sorted range too.
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ aCol[i].RegroupFormulaCells();
+
+ {
+ sc::StartListeningContext aCxt(rDocument);
+ AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
+ }
+ }
+}
+
+void ScTable::SortReorderByRowRefUpdate(
+ ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
+{
+ assert(pArray->IsUpdateRefs());
+
+ if (nCol2 < nCol1)
+ return;
+
+ SCROW nRow1 = pArray->GetStart();
+ SCROW nRow2 = pArray->GetLast();
+
+ ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
+ sc::ColumnSpanSet aGrpListenerRanges;
+
+ {
+ // Get the range of formula group listeners within sorted range (if any).
+ sc::QueryRange aQuery;
+
+ ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM();
+ std::vector<sc::AreaListener> aGrpListeners =
+ pBASM->GetAllListeners(
+ aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group);
+
+ {
+ for (const auto& rGrpListener : aGrpListeners)
+ {
+ assert(rGrpListener.mbGroupListening);
+ SvtListener* pGrpLis = rGrpListener.mpListener;
+ pGrpLis->Query(aQuery);
+ rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis);
+ }
+ }
+
+ ScRangeList aTmp;
+ aQuery.swapRanges(aTmp);
+
+ // If the range is within the sorted range, we need to expand its rows
+ // to the top and bottom of the sorted range, since the formula cells
+ // could be anywhere in the sorted range after reordering.
+ for (size_t i = 0, n = aTmp.size(); i < n; ++i)
+ {
+ ScRange aRange = aTmp[i];
+ if (!aMoveRange.Intersects(aRange))
+ {
+ // Doesn't overlap with the sorted range at all.
+ aGrpListenerRanges.set(GetDoc(), aRange, true);
+ continue;
+ }
+
+ if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
+ {
+ // Its column range is within the column range of the sorted range.
+ expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
+ aGrpListenerRanges.set(GetDoc(), aRange, true);
+ continue;
+ }
+
+ // It intersects with the sorted range, but its column range is
+ // not within the column range of the sorted range. Split it into
+ // 2 ranges.
+ ScRange aR1 = aRange;
+ ScRange aR2 = aRange;
+ if (aRange.aStart.Col() < aMoveRange.aStart.Col())
+ {
+ // Left half is outside the sorted range while the right half is inside.
+ aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
+ aR2.aStart.SetCol(aMoveRange.aStart.Col());
+ expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
+ }
+ else
+ {
+ // Left half is inside the sorted range while the right half is outside.
+ aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
+ aR2.aStart.SetCol(aMoveRange.aEnd.Col());
+ expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
+ }
+
+ aGrpListenerRanges.set(GetDoc(), aR1, true);
+ aGrpListenerRanges.set(GetDoc(), aR2, true);
+ }
+ }
+
+ // Split formula groups at the sort range boundaries (if applicable).
+ std::vector<SCROW> aRowBounds
+ {
+ nRow1,
+ nRow2+1
+ };
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ SplitFormulaGroups(nCol, aRowBounds);
+
+ // Cells in the data rows only reference values in the document. Make
+ // a copy before updating the document.
+ std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
+ SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
+ std::vector<SvtListener*> aListenersDummy;
+ fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false);
+
+ for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
+ {
+ SCCOL nThisCol = i + nCol1;
+
+ {
+ sc::CellStoreType& rDest = aCol[nThisCol].maCells;
+ sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
+
+ {
+ sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
+ sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
+
+ {
+ sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters;
+ sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
+
+ // Release current broadcasters first, to prevent them from getting deleted.
+ rDest.release_range(nRow1, nRow2);
+
+ // Transfer sorted broadcaster segment to the document.
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ }
+
+ {
+ sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
+ sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
+
+ // Do the same as broadcaster storage transfer (to prevent double deletion).
+ rDest.release_range(nRow1, nRow2);
+ rSrc.transfer(nRow1, nRow2, rDest, nRow1);
+ aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
+ }
+
+ // Update draw object positions
+ aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
+
+ {
+ // Get all row spans where the pattern is not NULL.
+ std::vector<PatternSpan> aSpans =
+ sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
+ aSortedCols[i]->maPatterns);
+
+ for (const auto& rSpan : aSpans)
+ {
+ assert(rSpan.mpPattern); // should never be NULL.
+ rDocument.GetPool()->DirectPutItemInPool(*rSpan.mpPattern);
+ }
+
+ for (const auto& rSpan : aSpans)
+ {
+ aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
+ rDocument.GetPool()->DirectRemoveItemFromPool(*rSpan.mpPattern);
+ }
+ }
+
+ aCol[nThisCol].CellStorageModified();
+ }
+
+ if (pArray->IsKeepQuery())
+ {
+ aRowFlags.maRowsHidden.build_tree();
+ aRowFlags.maRowsFiltered.build_tree();
+
+ // Remove all flags in the range first.
+ SetRowHidden(nRow1, nRow2, false);
+ SetRowFiltered(nRow1, nRow2, false);
+
+ std::vector<sc::RowSpan> aSpans =
+ sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
+
+ for (const auto& rSpan : aSpans)
+ SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
+
+ aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
+
+ for (const auto& rSpan : aSpans)
+ SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
+ }
+
+ // Set up row reorder map (for later broadcasting of reference updates).
+ sc::ColRowReorderMapType aRowMap;
+ const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
+ for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
+ {
+ SCROW nNew = i + nRow1;
+ SCROW nOld = rOldIndices[i];
+ aRowMap.emplace(nOld, nNew);
+ }
+
+ // Collect all listeners within sorted range ahead of time.
+ std::vector<SvtListener*> aListeners;
+
+ // Collect listeners of cell broadcasters.
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
+
+ // Get all area listeners that listen on one row within the range and end
+ // their listening.
+ std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
+ aMoveRange, sc::AreaOverlapType::OneRowInside);
+ {
+ for (auto& rAreaListener : aAreaListeners)
+ {
+ rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
+ aListeners.push_back( rAreaListener.mpListener);
+ }
+ }
+
+ {
+ // Get all formula cells from the former group area listener ranges.
+
+ std::vector<ScFormulaCell*> aFCells;
+ FormulaCellCollectAction aAction(aFCells);
+ aGrpListenerRanges.executeColumnAction(rDocument, aAction);
+
+ aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() );
+ }
+
+ // Remove any duplicate listener entries. We must ensure that we notify
+ // each unique listener only once.
+ std::sort(aListeners.begin(), aListeners.end());
+ aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
+
+ // Collect positions of all shared formula cells outside the sorted range,
+ // and make them unshared before notifying them.
+ sc::RefQueryFormulaGroup aFormulaGroupPos;
+ aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
+
+ std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
+ const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
+ for (const auto& [rTab, rCols] : rGroupTabs)
+ {
+ for (const auto& [nCol, rCol] : rCols)
+ {
+ std::vector<SCROW> aBounds(rCol);
+ rDocument.UnshareFormulaCells(rTab, nCol, aBounds);
+ }
+ }
+
+ // Notify the listeners to update their references.
+ ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
+ std::for_each(aListeners.begin(), aListeners.end(), aFunc);
+
+ // Re-group formulas in affected columns.
+ for (const auto& [rTab, rCols] : rGroupTabs)
+ {
+ for (const auto& rEntry : rCols)
+ rDocument.RegroupFormulaCells(rTab, rEntry.first);
+ }
+
+ // Re-start area listeners on the reordered rows.
+ for (const auto& rAreaListener : aAreaListeners)
+ {
+ ScRange aNewRange = rAreaListener.maArea;
+ sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
+ if (itRow != aRowMap.end())
+ {
+ aNewRange.aStart.SetRow( itRow->second);
+ aNewRange.aEnd.SetRow( itRow->second);
+ }
+ rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
+ }
+
+ // Re-group columns in the sorted range too.
+ for (SCCOL i = nCol1; i <= nCol2; ++i)
+ aCol[i].RegroupFormulaCells();
+
+ {
+ // Re-start area listeners on the old group listener ranges.
+ ListenerStartAction aAction(rDocument);
+ aGrpListenerRanges.executeColumnAction(rDocument, aAction);
+ }
+}
+
+short ScTable::CompareCell(
+ sal_uInt16 nSort,
+ ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
+ ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
+{
+ short nRes = 0;
+
+ CellType eType1 = rCell1.getType(), eType2 = rCell2.getType();
+
+ // tdf#95520 Sort by color - selected color goes on top, everything else according to compare function
+ if (aSortParam.maKeyState[nSort].aColorSortMode == ScColorSortMode::TextColor
+ || aSortParam.maKeyState[nSort].aColorSortMode == ScColorSortMode::BackgroundColor)
+ {
+ ScAddress aPos1(nCell1Col, nCell1Row, GetTab());
+ ScAddress aPos2(nCell2Col, nCell2Row, GetTab());
+ Color aTheChosenColor = aSortParam.maKeyState[nSort].aColorSortColor;
+ Color aColor1;
+ Color aColor2;
+ if (aSortParam.maKeyState[nSort].aColorSortMode == ScColorSortMode::TextColor)
+ {
+ aColor1 = GetCellTextColor(aPos1);
+ aColor2 = GetCellTextColor(aPos2);
+ }
+ else
+ {
+ aColor1 = GetCellBackgroundColor(aPos1);
+ aColor2 = GetCellBackgroundColor(aPos2);
+ }
+ if (aTheChosenColor == aColor1)
+ return -1;
+ if (aTheChosenColor == aColor2)
+ return 1;
+ if (aColor1 == aColor2)
+ return 0;
+ if (aColor1 > aColor2)
+ return 1;
+ if (aColor1 < aColor2)
+ return -1;
+ }
+
+ if (!rCell1.isEmpty())
+ {
+ if (!rCell2.isEmpty())
+ {
+ bool bErr1 = false;
+ bool bStr1 = ( eType1 != CELLTYPE_VALUE );
+ if (eType1 == CELLTYPE_FORMULA)
+ {
+ if (rCell1.getFormula()->GetErrCode() != FormulaError::NONE)
+ {
+ bErr1 = true;
+ bStr1 = false;
+ }
+ else if (rCell1.getFormula()->IsValue())
+ {
+ bStr1 = false;
+ }
+ }
+
+ bool bErr2 = false;
+ bool bStr2 = ( eType2 != CELLTYPE_VALUE );
+ if (eType2 == CELLTYPE_FORMULA)
+ {
+ if (rCell2.getFormula()->GetErrCode() != FormulaError::NONE)
+ {
+ bErr2 = true;
+ bStr2 = false;
+ }
+ else if (rCell2.getFormula()->IsValue())
+ {
+ bStr2 = false;
+ }
+ }
+
+ if ( bStr1 && bStr2 ) // only compare strings as strings!
+ {
+ OUString aStr1;
+ OUString aStr2;
+ if (eType1 == CELLTYPE_STRING)
+ aStr1 = rCell1.getSharedString()->getString();
+ else
+ aStr1 = GetString(nCell1Col, nCell1Row);
+ if (eType2 == CELLTYPE_STRING)
+ aStr2 = rCell2.getSharedString()->getString();
+ else
+ aStr2 = GetString(nCell2Col, nCell2Row);
+
+ bool bUserDef = aSortParam.bUserDef; // custom sort order
+ bool bNaturalSort = aSortParam.bNaturalSort; // natural sort
+ bool bCaseSens = aSortParam.bCaseSens; // case sensitivity
+
+ ScUserList* pList = ScGlobal::GetUserList();
+ if (bUserDef && pList && pList->size() > aSortParam.nUserIndex )
+ {
+ const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
+
+ if ( bNaturalSort )
+ nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator );
+ else
+ {
+ if ( bCaseSens )
+ nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) );
+ else
+ nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) );
+ }
+
+ }
+ if (!bUserDef)
+ {
+ if ( bNaturalSort )
+ nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator );
+ else
+ nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
+ }
+ }
+ else if ( bStr1 ) // String <-> Number or Error
+ {
+ if (bErr2)
+ nRes = -1; // String in front of Error
+ else
+ nRes = 1; // Number in front of String
+ }
+ else if ( bStr2 ) // Number or Error <-> String
+ {
+ if (bErr1)
+ nRes = 1; // String in front of Error
+ else
+ nRes = -1; // Number in front of String
+ }
+ else if (bErr1 && bErr2)
+ {
+ // nothing, two Errors are equal
+ }
+ else if (bErr1) // Error <-> Number
+ {
+ nRes = 1; // Number in front of Error
+ }
+ else if (bErr2) // Number <-> Error
+ {
+ nRes = -1; // Number in front of Error
+ }
+ else // Mixed numbers
+ {
+ double nVal1 = rCell1.getValue();
+ double nVal2 = rCell2.getValue();
+ if (nVal1 < nVal2)
+ nRes = -1;
+ else if (nVal1 > nVal2)
+ nRes = 1;
+ }
+ if ( !aSortParam.maKeyState[nSort].bAscending )
+ nRes = -nRes;
+ }
+ else
+ nRes = -1;
+ }
+ else
+ {
+ if (!rCell2.isEmpty())
+ nRes = 1;
+ else
+ nRes = 0; // both empty
+ }
+ return nRes;
+}
+
+short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
+{
+ short nRes;
+ sal_uInt16 nSort = 0;
+ do
+ {
+ ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 );
+ ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 );
+ if ( aSortParam.bByRow )
+ nRes = CompareCell( nSort,
+ rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg,
+ rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg );
+ else
+ nRes = CompareCell( nSort,
+ rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField,
+ rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField );
+ } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
+ if( nRes == 0 )
+ {
+ ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 );
+ ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 );
+ if( rInfo1.nOrg < rInfo2.nOrg )
+ nRes = -1;
+ else if( rInfo1.nOrg > rInfo2.nOrg )
+ nRes = 1;
+ }
+ return nRes;
+}
+
+void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi )
+{
+ if ((nHi - nLo) == 1)
+ {
+ if (Compare(pArray, nLo, nHi) > 0)
+ pArray->Swap( nLo, nHi );
+ }
+ else
+ {
+ SCCOLROW ni = nLo;
+ SCCOLROW nj = nHi;
+ do
+ {
+ while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
+ ni++;
+ while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
+ nj--;
+ if (ni <= nj)
+ {
+ if (ni != nj)
+ pArray->Swap( ni, nj );
+ ni++;
+ nj--;
+ }
+ } while (ni < nj);
+ if ((nj - nLo) < (nHi - ni))
+ {
+ if (nLo < nj)
+ QuickSort(pArray, nLo, nj);
+ if (ni < nHi)
+ QuickSort(pArray, ni, nHi);
+ }
+ else
+ {
+ if (ni < nHi)
+ QuickSort(pArray, ni, nHi);
+ if (nLo < nj)
+ QuickSort(pArray, nLo, nj);
+ }
+ }
+}
+
+short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
+{
+ short nRes;
+ sal_uInt16 nSort = 0;
+ const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
+ if (aSortParam.bByRow)
+ {
+ do
+ {
+ SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
+ nRes = 0;
+ if(nCol < GetAllocatedColumnsCount())
+ {
+ ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
+ ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
+ nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
+ }
+ } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
+ }
+ else
+ {
+ do
+ {
+ SCROW nRow = aSortParam.maKeyState[nSort].nField;
+ ScRefCellValue aCell1;
+ ScRefCellValue aCell2;
+ if(nIndex1 < GetAllocatedColumnsCount())
+ aCell1 = aCol[nIndex1].GetCellValue(nRow);
+ if(nIndex2 < GetAllocatedColumnsCount())
+ aCell2 = aCol[nIndex2].GetCellValue(nRow);
+ nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
+ nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
+ } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
+ }
+ return nRes;
+}
+
+bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // over aSortParam
+{
+ for (SCCOLROW i=nStart; i<nEnd; i++)
+ {
+ if (Compare( i, i+1 ) > 0)
+ return false;
+ }
+ return true;
+}
+
+void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
+{
+ SCROW nRow;
+ int nMax = nRow2 - nRow1;
+ for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
+ {
+ nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
+ pArray->Swap(i, nRow1 + nRow);
+ }
+}
+
+void ScTable::Sort(
+ const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
+ ScProgress* pProgress, sc::ReorderParam* pUndo )
+{
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(GetDoc());
+ InitSortCollator( rSortParam );
+ bGlobalKeepQuery = bKeepQuery;
+
+ if (pUndo)
+ {
+ // Copy over the basic sort parameters.
+ pUndo->maDataAreaExtras = rSortParam.aDataAreaExtras;
+ pUndo->mbByRow = rSortParam.bByRow;
+ pUndo->mbHiddenFiltered = bKeepQuery;
+ pUndo->mbUpdateRefs = bUpdateRefs;
+ pUndo->mbHasHeaders = rSortParam.bHasHeader;
+ }
+
+ // It is assumed that the data area has already been trimmed as necessary.
+
+ aSortParam = rSortParam; // must be assigned before calling IsSorted()
+ if (rSortParam.bByRow)
+ {
+ const SCROW nLastRow = rSortParam.nRow2;
+ const SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
+ if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
+ {
+ if(pProgress)
+ pProgress->SetState( 0, nLastRow-nRow1 );
+
+ std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
+ aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
+
+ if ( nLastRow - nRow1 > 255 )
+ DecoladeRow(pArray.get(), nRow1, nLastRow);
+
+ QuickSort(pArray.get(), nRow1, nLastRow);
+ if (pArray->IsUpdateRefs())
+ SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
+ else
+ {
+ SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress, false);
+ if (rSortParam.aDataAreaExtras.anyExtrasWanted())
+ SortReorderAreaExtrasByRow( pArray.get(), aSortParam.nCol1, aSortParam.nCol2,
+ rSortParam.aDataAreaExtras, pProgress);
+ }
+
+ if (pUndo)
+ {
+ // Stored is the first data row without header row.
+ pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
+ pUndo->maDataAreaExtras.mnStartRow = nRow1;
+ pUndo->maOrderIndices = pArray->GetOrderIndices();
+ }
+ }
+ }
+ else
+ {
+ const SCCOL nLastCol = rSortParam.nCol2;
+ const SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
+ if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
+ {
+ if(pProgress)
+ pProgress->SetState( 0, nLastCol-nCol1 );
+
+ std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
+ aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
+
+ QuickSort(pArray.get(), nCol1, nLastCol);
+ SortReorderByColumn(pArray.get(), rSortParam.nRow1, rSortParam.nRow2,
+ rSortParam.aDataAreaExtras.mbCellFormats, pProgress);
+ if (rSortParam.aDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
+ SortReorderAreaExtrasByColumn( pArray.get(),
+ rSortParam.nRow1, rSortParam.nRow2, rSortParam.aDataAreaExtras, pProgress);
+
+ if (pUndo)
+ {
+ // Stored is the first data column without header column.
+ pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
+ pUndo->maDataAreaExtras.mnStartCol = nCol1;
+ pUndo->maOrderIndices = pArray->GetOrderIndices();
+ }
+ }
+ }
+ DestroySortCollator();
+}
+
+void ScTable::Reorder( const sc::ReorderParam& rParam )
+{
+ if (rParam.maOrderIndices.empty())
+ return;
+
+ std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
+ if (!pArray)
+ return;
+
+ if (rParam.mbByRow)
+ {
+ // Re-play sorting from the known sort indices.
+ pArray->ReorderByRow(rParam.maOrderIndices);
+ if (pArray->IsUpdateRefs())
+ SortReorderByRowRefUpdate(
+ pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
+ else
+ {
+ SortReorderByRow( pArray.get(),
+ rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr, false);
+ if (rParam.maDataAreaExtras.anyExtrasWanted())
+ SortReorderAreaExtrasByRow( pArray.get(),
+ rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(),
+ rParam.maDataAreaExtras, nullptr);
+ }
+ }
+ else
+ {
+ // Ordering by column is much simpler. Just set the order indices and we are done.
+ pArray->SetOrderIndices(std::vector(rParam.maOrderIndices));
+ SortReorderByColumn(
+ pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
+ rParam.maDataAreaExtras.mbCellFormats, nullptr);
+ if (rParam.maDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
+ SortReorderAreaExtrasByColumn( pArray.get(),
+ rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
+ rParam.maDataAreaExtras, nullptr);
+ }
+}
+
+namespace {
+
+class SubTotalRowFinder
+{
+ const ScTable& mrTab;
+ const ScSubTotalParam& mrParam;
+
+public:
+ SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
+ mrTab(rTab), mrParam(rParam) {}
+
+ bool operator() (size_t nRow, const ScFormulaCell* pCell)
+ {
+ if (!pCell->IsSubTotal())
+ return false;
+
+ SCCOL nStartCol = mrParam.nCol1;
+ SCCOL nEndCol = mrParam.nCol2;
+
+ for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(0, nStartCol - 1))
+ {
+ if (mrTab.HasData(nCol, nRow))
+ return true;
+ }
+ for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol()))
+ {
+ if (mrTab.HasData(nCol, nRow))
+ return true;
+ }
+ return false;
+ }
+};
+
+}
+
+bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
+{
+ SCCOL nStartCol = rParam.nCol1;
+ SCROW nStartRow = rParam.nRow1 + 1; // Header
+ SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
+ SCROW nEndRow = rParam.nRow2;
+
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ const sc::CellStoreType& rCells = aCol[nCol].maCells;
+ SubTotalRowFinder aFunc(*this, rParam);
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
+ sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
+ if (aPos.first != rCells.end())
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+struct RemoveSubTotalsHandler
+{
+ std::set<SCROW> aRemoved;
+
+ void operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ if (p->IsSubTotal())
+ aRemoved.insert(nRow);
+ }
+};
+
+}
+
+void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
+{
+ SCCOL nStartCol = rParam.nCol1;
+ SCROW nStartRow = rParam.nRow1 + 1; // Header
+ SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
+ SCROW nEndRow = rParam.nRow2; // will change
+
+ RemoveSubTotalsHandler aFunc;
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ const sc::CellStoreType& rCells = aCol[nCol].maCells;
+ sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
+ }
+
+ auto& aRows = aFunc.aRemoved;
+
+ std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) {
+ RemoveRowBreak(nRow+1, false, true);
+ rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1);
+ });
+
+ rParam.nRow2 -= aRows.size();
+}
+
+// Delete hard number formats (for result formulas)
+
+static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
+{
+ const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
+ if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
+ == SfxItemState::SET )
+ {
+ auto pNewPattern = std::make_unique<ScPatternAttr>( *pPattern );
+ SfxItemSet& rSet = pNewPattern->GetItemSet();
+ rSet.ClearItem( ATTR_VALUE_FORMAT );
+ rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
+ pTab->SetPattern( nCol, nRow, std::move(pNewPattern) );
+ }
+}
+
+namespace {
+
+struct RowEntry
+{
+ sal_uInt16 nGroupNo;
+ SCROW nSubStartRow;
+ SCROW nDestRow;
+ SCROW nFuncStart;
+ SCROW nFuncEnd;
+};
+
+}
+
+static TranslateId lcl_GetSubTotalStrId(int id)
+{
+ switch ( id )
+ {
+ case SUBTOTAL_FUNC_AVE: return STR_FUN_TEXT_AVG;
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2: return STR_FUN_TEXT_COUNT;
+ case SUBTOTAL_FUNC_MAX: return STR_FUN_TEXT_MAX;
+ case SUBTOTAL_FUNC_MIN: return STR_FUN_TEXT_MIN;
+ case SUBTOTAL_FUNC_PROD: return STR_FUN_TEXT_PRODUCT;
+ case SUBTOTAL_FUNC_STD:
+ case SUBTOTAL_FUNC_STDP: return STR_FUN_TEXT_STDDEV;
+ case SUBTOTAL_FUNC_SUM: return STR_FUN_TEXT_SUM;
+ case SUBTOTAL_FUNC_VAR:
+ case SUBTOTAL_FUNC_VARP: return STR_FUN_TEXT_VAR;
+ default:
+ {
+ return STR_EMPTYDATA;
+ // added to avoid warnings
+ }
+ }
+}
+
+// Gets the string used for "Grand" results
+static TranslateId lcl_GetGrandSubTotalStrId(int id)
+{
+ switch ( id )
+ {
+ case SUBTOTAL_FUNC_AVE: return STR_TABLE_GRAND_AVG;
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2: return STR_TABLE_GRAND_COUNT;
+ case SUBTOTAL_FUNC_MAX: return STR_TABLE_GRAND_MAX;
+ case SUBTOTAL_FUNC_MIN: return STR_TABLE_GRAND_MIN;
+ case SUBTOTAL_FUNC_PROD: return STR_TABLE_GRAND_PRODUCT;
+ case SUBTOTAL_FUNC_STD:
+ case SUBTOTAL_FUNC_STDP: return STR_TABLE_GRAND_STDDEV;
+ case SUBTOTAL_FUNC_SUM: return STR_TABLE_GRAND_SUM;
+ case SUBTOTAL_FUNC_VAR:
+ case SUBTOTAL_FUNC_VARP: return STR_TABLE_GRAND_VAR;
+ default:
+ {
+ return STR_EMPTYDATA;
+ // added to avoid warnings
+ }
+ }
+}
+
+// new intermediate results
+// rParam.nRow2 is changed!
+
+bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
+{
+ SCCOL nStartCol = rParam.nCol1;
+ SCROW nStartRow = rParam.nRow1 + 1; // Header
+ SCCOL nEndCol = rParam.nCol2;
+ SCROW nEndRow = rParam.nRow2; // will change
+ sal_uInt16 i;
+
+ // Remove empty rows at the end
+ // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
+ // If sorted, all empty rows are at the end.
+ SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
+ nEndRow -= nEmpty;
+
+ sal_uInt16 nLevelCount = 0; // Number of levels
+ bool bDoThis = true;
+ for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
+ if (rParam.bGroupActive[i])
+ nLevelCount = i+1;
+ else
+ bDoThis = false;
+
+ if (nLevelCount==0) // do nothing
+ return true;
+
+ SCCOL* nGroupCol = rParam.nField; // columns which will be used when grouping
+
+ // With (blank) as a separate category, subtotal rows from
+ // the other columns must always be tested
+ // (previously only when a column occurred more than once)
+ bool bTestPrevSub = ( nLevelCount > 1 );
+
+ OUString aSubString;
+
+ bool bIgnoreCase = !rParam.bCaseSens;
+
+ OUString aCompString[MAXSUBTOTAL];
+
+ //TODO: sort?
+
+ ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find(
+ ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para ));
+
+ bool bSpaceLeft = true; // Success when inserting?
+
+ // For performance reasons collect formula entries so their
+ // references don't have to be tested for updates each time a new row is
+ // inserted
+ RowEntry aRowEntry;
+ ::std::vector< RowEntry > aRowVector;
+
+ for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++)
+ {
+ aRowEntry.nGroupNo = nLevelCount - nLevel - 1;
+
+ // how many results per level
+ SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo];
+ // result functions
+ ScSubTotalFunc* pResFunc = rParam.pFunctions[aRowEntry.nGroupNo].get();
+
+ if (nResCount > 0) // otherwise only sort
+ {
+ for (i=0; i<=aRowEntry.nGroupNo; i++)
+ {
+ aSubString = GetString( nGroupCol[i], nStartRow );
+ if ( bIgnoreCase )
+ aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
+ else
+ aCompString[i] = aSubString;
+ } // aSubString stays on the last
+
+ bool bBlockVis = false; // group visible?
+ aRowEntry.nSubStartRow = nStartRow;
+ for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
+ {
+ bool bChanged;
+ if (nRow>nEndRow)
+ bChanged = true;
+ else
+ {
+ bChanged = false;
+ OUString aString;
+ for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
+ {
+ aString = GetString( nGroupCol[i], nRow );
+ if (bIgnoreCase)
+ aString = ScGlobal::getCharClass().uppercase(aString);
+ // when sorting, blanks are separate group
+ // otherwise blank cells are allowed below
+ bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
+ aString != aCompString[i] );
+ }
+ if ( bChanged && bTestPrevSub )
+ {
+ // No group change on rows that will contain subtotal formulas
+ bChanged = std::none_of(aRowVector.begin(), aRowVector.end(),
+ [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; });
+ }
+ }
+ if ( bChanged )
+ {
+ aRowEntry.nDestRow = nRow;
+ aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
+ aRowEntry.nFuncEnd = nRow-1;
+
+ bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab,
+ aRowEntry.nDestRow, 1 );
+ DBShowRow( aRowEntry.nDestRow, bBlockVis );
+ if ( rParam.bPagebreak && nRow < rDocument.MaxRow() &&
+ aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
+ SetRowBreak(aRowEntry.nSubStartRow, false, true);
+
+ if (bSpaceLeft)
+ {
+ for ( auto& rRowEntry : aRowVector)
+ {
+ if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow )
+ ++rRowEntry.nSubStartRow;
+ if ( aRowEntry.nDestRow <= rRowEntry.nDestRow )
+ ++rRowEntry.nDestRow;
+ if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart )
+ ++rRowEntry.nFuncStart;
+ if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd )
+ ++rRowEntry.nFuncEnd;
+ }
+ // collect formula positions
+ aRowVector.push_back( aRowEntry );
+
+ OUString aOutString = aSubString;
+ if (aOutString.isEmpty())
+ aOutString = ScResId( STR_EMPTYDATA );
+ aOutString += " ";
+ TranslateId pStrId = STR_TABLE_ERGEBNIS;
+ if ( nResCount == 1 )
+ pStrId = lcl_GetSubTotalStrId(pResFunc[0]);
+ aOutString += ScResId(pStrId);
+ SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
+ ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle );
+
+ ++nRow;
+ ++nEndRow;
+ aRowEntry.nSubStartRow = nRow;
+ for (i=0; i<=aRowEntry.nGroupNo; i++)
+ {
+ aSubString = GetString( nGroupCol[i], nRow );
+ if ( bIgnoreCase )
+ aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
+ else
+ aCompString[i] = aSubString;
+ }
+ }
+ }
+ bBlockVis = !RowFiltered(nRow);
+ }
+ }
+ }
+
+ if (!aRowVector.empty())
+ {
+ // generate global total
+ SCROW nGlobalStartRow = aRowVector[0].nSubStartRow;
+ SCROW nGlobalStartFunc = aRowVector[0].nFuncStart;
+ SCROW nGlobalEndRow = 0;
+ SCROW nGlobalEndFunc = 0;
+ for (const auto& rRowEntry : aRowVector)
+ {
+ nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow;
+ nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow;
+ }
+
+ for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++)
+ {
+ const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1;
+ const ScSubTotalFunc* pResFunc = rParam.pFunctions[nGroupNo].get();
+ if (!pResFunc)
+ {
+ // No subtotal function given for this group => no formula or
+ // label and do not insert a row.
+ continue;
+ }
+
+ // increment end row
+ nGlobalEndRow++;
+
+ // add row entry for formula
+ aRowEntry.nGroupNo = nGroupNo;
+ aRowEntry.nSubStartRow = nGlobalStartRow;
+ aRowEntry.nFuncStart = nGlobalStartFunc;
+ aRowEntry.nDestRow = nGlobalEndRow;
+ aRowEntry.nFuncEnd = nGlobalEndFunc;
+
+ // increment row
+ nGlobalEndFunc++;
+
+ bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1);
+
+ if (bSpaceLeft)
+ {
+ aRowVector.push_back(aRowEntry);
+ nEndRow++;
+ DBShowRow(aRowEntry.nDestRow, true);
+
+ // insert label
+ OUString label = ScResId(lcl_GetGrandSubTotalStrId(pResFunc[0]));
+ SetString(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, label);
+ ApplyStyle(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle);
+ }
+ }
+ }
+
+ // now insert the formulas
+ ScComplexRefData aRef;
+ aRef.InitFlags();
+ aRef.Ref1.SetAbsTab(nTab);
+ aRef.Ref2.SetAbsTab(nTab);
+ for (const auto& rRowEntry : aRowVector)
+ {
+ SCCOL nResCount = rParam.nSubTotals[rRowEntry.nGroupNo];
+ SCCOL* nResCols = rParam.pSubTotals[rRowEntry.nGroupNo].get();
+ ScSubTotalFunc* pResFunc = rParam.pFunctions[rRowEntry.nGroupNo].get();
+ for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
+ {
+ aRef.Ref1.SetAbsCol(nResCols[nResult]);
+ aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart);
+ aRef.Ref2.SetAbsCol(nResCols[nResult]);
+ aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd);
+
+ ScTokenArray aArr(rDocument);
+ aArr.AddOpCode( ocSubTotal );
+ aArr.AddOpCode( ocOpen );
+ aArr.AddDouble( static_cast<double>(pResFunc[nResult]) );
+ aArr.AddOpCode( ocSep );
+ aArr.AddDoubleReference( aRef );
+ aArr.AddOpCode( ocClose );
+ aArr.AddOpCode( ocStop );
+ ScFormulaCell* pCell = new ScFormulaCell(
+ rDocument, ScAddress(nResCols[nResult], rRowEntry.nDestRow, nTab), aArr);
+ if ( rParam.bIncludePattern )
+ pCell->SetNeedNumberFormat(true);
+
+ SetFormulaCell(nResCols[nResult], rRowEntry.nDestRow, pCell);
+ if ( nResCols[nResult] != nGroupCol[rRowEntry.nGroupNo] )
+ {
+ ApplyStyle( nResCols[nResult], rRowEntry.nDestRow, pStyle );
+
+ lcl_RemoveNumberFormat( this, nResCols[nResult], rRowEntry.nDestRow );
+ }
+ }
+
+ }
+
+ //TODO: according to setting, shift intermediate-sum rows up?
+
+ //TODO: create Outlines directly?
+
+ if (bSpaceLeft)
+ DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
+
+ rParam.nRow2 = nEndRow; // new end
+ return bSpaceLeft;
+}
+
+void ScTable::TopTenQuery( ScQueryParam& rParam )
+{
+ bool bSortCollatorInitialized = false;
+ SCSIZE nEntryCount = rParam.GetEntryCount();
+ SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
+ SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
+ for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
+ {
+ ScQueryEntry& rEntry = rParam.GetEntry(i);
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+
+ for (ScQueryEntry::Item& rItem : rItems)
+ {
+ switch (rEntry.eOp)
+ {
+ case SC_TOPVAL:
+ case SC_BOTVAL:
+ case SC_TOPPERC:
+ case SC_BOTPERC:
+ {
+ ScSortParam aLocalSortParam(rParam, static_cast<SCCOL>(rEntry.nField));
+ aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
+ if (!bSortCollatorInitialized)
+ {
+ bSortCollatorInitialized = true;
+ InitSortCollator(aLocalSortParam);
+ }
+ std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
+ DecoladeRow(pArray.get(), nRow1, rParam.nRow2);
+ QuickSort(pArray.get(), nRow1, rParam.nRow2);
+ std::unique_ptr<ScSortInfo[]> const& ppInfo = pArray->GetFirstArray();
+ SCSIZE nValidCount = nCount;
+ // Don't count note or blank cells, they are sorted to the end
+ while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.isEmpty())
+ nValidCount--;
+ // Don't count Strings, they are between Value and blank
+ while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.hasString())
+ nValidCount--;
+ if (nValidCount > 0)
+ {
+ if (rItem.meType == ScQueryEntry::ByString)
+ { // by string ain't going to work
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = 10; // 10 and 10% respectively
+ }
+ SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
+ SCSIZE nOffset = 0;
+ switch (rEntry.eOp)
+ {
+ case SC_TOPVAL:
+ {
+ rEntry.eOp = SC_GREATER_EQUAL;
+ if (nVal > nValidCount)
+ nVal = nValidCount;
+ nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
+ }
+ break;
+ case SC_BOTVAL:
+ {
+ rEntry.eOp = SC_LESS_EQUAL;
+ if (nVal > nValidCount)
+ nVal = nValidCount;
+ nOffset = nVal - 1; // 1 <= nVal <= nValidCount
+ }
+ break;
+ case SC_TOPPERC:
+ {
+ rEntry.eOp = SC_GREATER_EQUAL;
+ if (nVal > 100)
+ nVal = 100;
+ nOffset = nValidCount - (nValidCount * nVal / 100);
+ if (nOffset >= nValidCount)
+ nOffset = nValidCount - 1;
+ }
+ break;
+ case SC_BOTPERC:
+ {
+ rEntry.eOp = SC_LESS_EQUAL;
+ if (nVal > 100)
+ nVal = 100;
+ nOffset = (nValidCount * nVal / 100);
+ if (nOffset >= nValidCount)
+ nOffset = nValidCount - 1;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ ScRefCellValue aCell = ppInfo[nOffset].maCell;
+ if (aCell.hasNumeric())
+ rItem.mfVal = aCell.getValue();
+ else
+ {
+ OSL_FAIL("TopTenQuery: pCell no ValueData");
+ rEntry.eOp = SC_GREATER_EQUAL;
+ rItem.mfVal = 0;
+ }
+ }
+ else
+ {
+ rEntry.eOp = SC_GREATER_EQUAL;
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = 0;
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ if ( bSortCollatorInitialized )
+ DestroySortCollator();
+}
+
+namespace {
+
+bool CanOptimizeQueryStringToNumber( const SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat )
+{
+ // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
+ // The problem with this optimization is that the autofilter dialog apparently converts
+ // the value to text and then converts that back to a number for filtering.
+ // If that leads to any change of value (such as when time is rounded to seconds),
+ // even matching values will be filtered out. Therefore query by value only for formats
+ // where no such change should occur.
+ if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex))
+ {
+ switch(pEntry->GetType())
+ {
+ case SvNumFormatType::NUMBER:
+ case SvNumFormatType::FRACTION:
+ case SvNumFormatType::SCIENTIFIC:
+ return true;
+ case SvNumFormatType::DATE:
+ case SvNumFormatType::DATETIME:
+ bDateFormat = true;
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+class PrepareQueryItem
+{
+ const ScDocument& mrDoc;
+ const bool mbRoundForFilter;
+public:
+ explicit PrepareQueryItem(const ScDocument& rDoc, bool bRoundForFilter) :
+ mrDoc(rDoc), mbRoundForFilter(bRoundForFilter) {}
+
+ void operator() (ScQueryEntry::Item& rItem)
+ {
+ rItem.mbRoundForFilter = mbRoundForFilter;
+
+ if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
+ return;
+
+ sal_uInt32 nIndex = 0;
+ bool bNumber = mrDoc.GetFormatTable()->
+ IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
+
+ // Advanced Filter creates only ByString queries that need to be
+ // converted to ByValue if appropriate. rItem.mfVal now holds the value
+ // if bNumber==true.
+
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ bool bDateFormat = false;
+ if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat ))
+ rItem.meType = ScQueryEntry::ByValue;
+ if (!bDateFormat)
+ return;
+ }
+
+ // Double-check if the query by date is really appropriate.
+
+ if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
+ {
+ const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
+ if (pEntry)
+ {
+ SvNumFormatType nNumFmtType = pEntry->GetType();
+ if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME))
+ rItem.meType = ScQueryEntry::ByValue; // not a date only
+ else
+ rItem.meType = ScQueryEntry::ByDate; // date only
+ }
+ else
+ rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
+ }
+ else
+ rItem.meType = ScQueryEntry::ByValue; // not a date
+ }
+};
+
+void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, bool bRoundForFilter )
+{
+ bool bTopTen = false;
+ SCSIZE nEntryCount = rParam.GetEntryCount();
+
+ for ( SCSIZE i = 0; i < nEntryCount; ++i )
+ {
+ ScQueryEntry& rEntry = rParam.GetEntry(i);
+ if (!rEntry.bDoQuery)
+ continue;
+
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc, bRoundForFilter));
+
+ if ( !bTopTen )
+ {
+ switch ( rEntry.eOp )
+ {
+ case SC_TOPVAL:
+ case SC_BOTVAL:
+ case SC_TOPPERC:
+ case SC_BOTPERC:
+ {
+ bTopTen = true;
+ }
+ break;
+ default:
+ {
+ }
+ }
+ }
+ }
+
+ if ( bTopTen )
+ {
+ pTab->TopTenQuery( rParam );
+ }
+}
+
+}
+
+void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
+{
+ lcl_PrepareQuery(&rDocument, this, rQueryParam, false);
+}
+
+SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
+{
+ ScQueryParam aParam( rParamOrg );
+ typedef std::unordered_set<OUString> StrSetType;
+ StrSetType aStrSet;
+
+ bool bStarted = false;
+ bool bOldResult = true;
+ SCROW nOldStart = 0;
+ SCROW nOldEnd = 0;
+
+ SCSIZE nCount = 0;
+ SCROW nOutRow = 0;
+ SCROW nHeader = aParam.bHasHeader ? 1 : 0;
+
+ lcl_PrepareQuery(&rDocument, this, aParam, true);
+
+ if (!aParam.bInplace)
+ {
+ nOutRow = aParam.nDestRow + nHeader;
+ if (nHeader > 0)
+ CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
+ aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
+ }
+
+ sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access
+ ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam);
+
+ SCROW nRealRow2 = aParam.nRow2;
+ for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
+ {
+ bool bResult; // Filter result
+ bool bValid = queryEvaluator.ValidQuery(j, nullptr, &blockPos);
+ if (!bValid && bKeepSub) // Keep subtotals
+ {
+ for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
+ {
+ ScRefCellValue aCell = GetCellValue(nCol, j);
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ if (!aCell.getFormula()->IsSubTotal())
+ continue;
+
+ if (RefVisible(aCell.getFormula()))
+ bValid = true;
+ }
+ }
+ if (bValid)
+ {
+ if (aParam.bDuplicate)
+ bResult = true;
+ else
+ {
+ OUStringBuffer aStr;
+ for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
+ {
+ OUString aCellStr = GetString(k, j);
+ aStr.append(aCellStr + u"\x0001");
+ }
+
+ bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted.
+ }
+ }
+ else
+ bResult = false;
+
+ if (aParam.bInplace)
+ {
+ if (bResult == bOldResult && bStarted)
+ nOldEnd = j;
+ else
+ {
+ if (bStarted)
+ DBShowRows(nOldStart,nOldEnd, bOldResult);
+ nOldStart = nOldEnd = j;
+ bOldResult = bResult;
+ }
+ bStarted = true;
+ }
+ else
+ {
+ if (bResult)
+ {
+ CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
+ if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints
+ blockPos.invalidate();
+ ++nOutRow;
+ }
+ }
+ if (bResult)
+ ++nCount;
+ }
+
+ if (aParam.bInplace && bStarted)
+ DBShowRows(nOldStart,nOldEnd, bOldResult);
+
+ if (aParam.bInplace)
+ SetDrawPageSize();
+
+ return nCount;
+}
+
+bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
+{
+ bool bValid = true;
+ std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]);
+ OUString aCellStr;
+ SCCOL nCol = nCol1;
+ OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
+ SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
+ SCROW nDBRow1 = rQueryParam.nRow1;
+ SCCOL nDBCol2 = rQueryParam.nCol2;
+ // First row must be column headers
+ while (bValid && (nCol <= nCol2))
+ {
+ OUString aQueryStr = GetUpperCellString(nCol, nRow1);
+ bool bFound = false;
+ SCCOL i = rQueryParam.nCol1;
+ while (!bFound && (i <= nDBCol2))
+ {
+ if ( nTab == nDBTab )
+ aCellStr = GetUpperCellString(i, nDBRow1);
+ else
+ aCellStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
+ bFound = (aCellStr == aQueryStr);
+ if (!bFound) i++;
+ }
+ if (bFound)
+ pFields[nCol - nCol1] = i;
+ else
+ bValid = false;
+ nCol++;
+ }
+ if (bValid)
+ {
+ sal_uLong nVisible = 0;
+ for ( nCol=nCol1; nCol<=ClampToAllocatedColumns(nCol2); nCol++ )
+ nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
+
+ if ( nVisible > SCSIZE_MAX / sizeof(void*) )
+ {
+ OSL_FAIL("too many filter criteria");
+ nVisible = 0;
+ }
+
+ SCSIZE nNewEntries = nVisible;
+ rQueryParam.Resize( nNewEntries );
+
+ SCSIZE nIndex = 0;
+ SCROW nRow = nRow1 + 1;
+ svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
+ while (nRow <= nRow2)
+ {
+ nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ aCellStr = GetInputString( nCol, nRow );
+ if (!aCellStr.isEmpty())
+ {
+ if (nIndex < nNewEntries)
+ {
+ rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
+ rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
+ nIndex++;
+ if (nIndex < nNewEntries)
+ rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
+ }
+ else
+ bValid = false;
+ }
+ nCol++;
+ }
+ nRow++;
+ if (nIndex < nNewEntries)
+ rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
+ }
+ }
+ return bValid;
+}
+
+bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
+{
+ // A valid StarQuery must be at least 4 columns wide. To be precise it
+ // should be exactly 4 columns ...
+ // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
+ // column Excel style query range immediately left to itself would result
+ // in a circular reference when the field name or operator or value (first
+ // to third query range column) is obtained (#i58354#). Furthermore, if the
+ // range wasn't sufficiently specified data changes wouldn't flag formula
+ // cells for recalculation.
+ if (nCol2 - nCol1 < 3)
+ return false;
+
+ bool bValid;
+ OUString aCellStr;
+ SCSIZE nIndex = 0;
+ SCROW nRow = nRow1;
+ OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
+ SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
+ SCROW nDBRow1 = rQueryParam.nRow1;
+ SCCOL nDBCol2 = rQueryParam.nCol2;
+
+ SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
+ rQueryParam.Resize( nNewEntries );
+ svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
+
+ do
+ {
+ ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
+
+ bValid = false;
+ // First column AND/OR
+ if (nIndex > 0)
+ {
+ aCellStr = GetUpperCellString(nCol1, nRow);
+ if ( aCellStr == ScResId(STR_TABLE_AND) )
+ {
+ rEntry.eConnect = SC_AND;
+ bValid = true;
+ }
+ else if ( aCellStr == ScResId(STR_TABLE_OR) )
+ {
+ rEntry.eConnect = SC_OR;
+ bValid = true;
+ }
+ }
+ // Second column field name
+ if ((nIndex < 1) || bValid)
+ {
+ bool bFound = false;
+ aCellStr = GetUpperCellString(nCol1 + 1, nRow);
+ for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
+ {
+ OUString aFieldStr;
+ if ( nTab == nDBTab )
+ aFieldStr = GetUpperCellString(i, nDBRow1);
+ else
+ aFieldStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
+ bFound = (aCellStr == aFieldStr);
+ if (bFound)
+ {
+ rEntry.nField = i;
+ bValid = true;
+ }
+ else
+ bValid = false;
+ }
+ }
+ // Third column operator =<>...
+ if (bValid)
+ {
+ aCellStr = GetUpperCellString(nCol1 + 2, nRow);
+ if (aCellStr.startsWith("<"))
+ {
+ if (aCellStr[1] == '>')
+ rEntry.eOp = SC_NOT_EQUAL;
+ else if (aCellStr[1] == '=')
+ rEntry.eOp = SC_LESS_EQUAL;
+ else
+ rEntry.eOp = SC_LESS;
+ }
+ else if (aCellStr.startsWith(">"))
+ {
+ if (aCellStr[1] == '=')
+ rEntry.eOp = SC_GREATER_EQUAL;
+ else
+ rEntry.eOp = SC_GREATER;
+ }
+ else if (aCellStr.startsWith("="))
+ rEntry.eOp = SC_EQUAL;
+
+ }
+ // Fourth column values
+ if (bValid)
+ {
+ OUString aStr = GetString(nCol1 + 3, nRow);
+ rEntry.GetQueryItem().maString = rPool.intern(aStr);
+ rEntry.bDoQuery = true;
+ }
+ nIndex++;
+ nRow++;
+ }
+ while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
+ return bValid;
+}
+
+bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
+{
+ SCSIZE i, nCount;
+ PutInOrder(nCol1, nCol2);
+ PutInOrder(nRow1, nRow2);
+
+ nCount = rQueryParam.GetEntryCount();
+ for (i=0; i < nCount; i++)
+ rQueryParam.GetEntry(i).Clear();
+
+ // Standard query table
+ bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
+ // Excel Query table
+ if (!bValid)
+ bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
+
+ SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
+ nCount = rQueryParam.GetEntryCount();
+ if (bValid)
+ {
+ // query type must be set
+ for (i=0; i < nCount; i++)
+ {
+ ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem();
+ sal_uInt32 nIndex = 0;
+ bool bNumber = pFormatter->IsNumberFormat(
+ rItem.maString.getString(), nIndex, rItem.mfVal);
+ bool bDateFormat = false;
+ rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat )
+ ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString);
+ }
+ }
+ else
+ {
+ for (i=0; i < nCount; i++)
+ rQueryParam.GetEntry(i).Clear();
+ }
+ return bValid;
+}
+
+bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
+{
+ if (nStartRow == nEndRow)
+ // Assume only data.
+ /* XXX NOTE: previous behavior still checked this one row and could
+ * evaluate it has header row, but that doesn't make much sense. */
+ return false;
+
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ CellType eType = GetCellType( nCol, nStartRow );
+ // Any non-text cell in first row => not headers.
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return false;
+ }
+
+ // First row all text cells, any non-text cell in second row => headers.
+ SCROW nTestRow = nStartRow + 1;
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ CellType eType = GetCellType( nCol, nTestRow );
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return true;
+ }
+
+ // Also second row all text cells => first row not headers.
+ return false;
+}
+
+bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
+{
+ if (nStartCol == nEndCol)
+ // Assume only data.
+ /* XXX NOTE: previous behavior still checked this one column and could
+ * evaluate it has header column, but that doesn't make much sense. */
+ return false;
+
+ for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
+ {
+ CellType eType = GetCellType( nStartCol, nRow );
+ // Any non-text cell in first column => not headers.
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return false;
+ }
+
+ // First column all text cells, any non-text cell in second column => headers.
+ SCCOL nTestCol = nStartCol + 1;
+ for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
+ {
+ CellType eType = GetCellType( nRow, nTestCol );
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return true;
+ }
+
+ // Also second column all text cells => first column not headers.
+ return false;
+}
+
+void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering )
+{
+ if (nCol >= aCol.size())
+ return;
+
+ sc::ColumnBlockConstPosition aBlockPos;
+ aCol[nCol].InitBlockPosition(aBlockPos);
+ aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, bFiltering, false /*bFilteredRow*/);
+
+ SCROW nLastRow = aBlockPos.miCellPos->position;
+ if (nLastRow < nRow2)
+ {
+ aCol[nCol].GetBackColorFilterEntries(nLastRow, nRow2, rFilterEntries);
+ }
+}
+
+void ScTable::GetFilteredFilterEntries(
+ SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering )
+{
+ if (nCol >= aCol.size())
+ return;
+
+ sc::ColumnBlockConstPosition aBlockPos;
+ aCol[nCol].InitBlockPosition(aBlockPos);
+
+ // remove the entry for this column from the query parameter
+ ScQueryParam aParam( rParam );
+ aParam.RemoveEntryByField(nCol);
+
+ lcl_PrepareQuery(&rDocument, this, aParam, true);
+ ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam);
+ for ( SCROW j = nRow1; j <= nRow2; ++j )
+ {
+ if (queryEvaluator.ValidQuery(j))
+ {
+ aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, false/*bFilteredRow*/);
+ }
+ else
+ {
+ aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, true/*bFilteredRow*/);
+ }
+ }
+}
+
+bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings)
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
+ return false;
+ return aCol[nCol].GetDataEntries( nRow, rStrings);
+}
+
+sal_uInt64 ScTable::GetCellCount() const
+{
+ sal_uInt64 nCellCount = 0;
+
+ for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
+ nCellCount += aCol[nCol].GetCellCount();
+
+ return nCellCount;
+}
+
+sal_uInt64 ScTable::GetWeightedCount() const
+{
+ sal_uInt64 nCellCount = 0;
+
+ for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
+ nCellCount += aCol[nCol].GetWeightedCount();
+
+ return nCellCount;
+}
+
+sal_uInt64 ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
+{
+ sal_uInt64 nCellCount = 0;
+
+ for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
+ nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow);
+
+ return nCellCount;
+}
+
+sal_uInt64 ScTable::GetCodeCount() const
+{
+ sal_uInt64 nCodeCount = 0;
+
+ for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
+ if ( aCol[nCol].GetCellCount() )
+ nCodeCount += aCol[nCol].GetCodeCount();
+
+ return nCodeCount;
+}
+
+sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
+ SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
+{
+ if ( IsColValid( nCol ) )
+ return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
+ else
+ return 0;
+}
+
+sal_Int32 ScTable::GetMaxNumberStringLen(
+ sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
+{
+ if ( IsColValid( nCol ) )
+ return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
+ else
+ return 0;
+}
+
+void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
+{
+ ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab );
+ ScRange aMarkArea( ScAddress::UNINITIALIZED );
+ if (rMark.IsMultiMarked())
+ aMarkArea = rMark.GetMultiMarkArea();
+ else if (rMark.IsMarked())
+ aMarkArea = rMark.GetMarkArea();
+ else
+ {
+ assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
+ aMarkArea.aStart.SetCol(0);
+ aMarkArea.aEnd.SetCol(rDocument.MaxCol());
+ }
+ const SCCOL nStartCol = aMarkArea.aStart.Col();
+ const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col());
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol)
+ {
+ if (mpColFlags && ColHidden(nCol))
+ continue;
+
+ aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx
new file mode 100644
index 0000000000..62b9dbb9e0
--- /dev/null
+++ b/sc/source/core/data/table4.cxx
@@ -0,0 +1,3055 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <vcl/keycodes.hxx>
+#include <rtl/math.hxx>
+#include <unotools/charclass.hxx>
+#include <tools/duration.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <formulacell.hxx>
+#include <table.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <autoform.hxx>
+#include <userlist.hxx>
+#include <zforauto.hxx>
+#include <subtotal.hxx>
+#include <formula/errorcodes.hxx>
+#include <docpool.hxx>
+#include <progress.hxx>
+#include <conditio.hxx>
+#include <editutil.hxx>
+#include <listenercontext.hxx>
+#include <scopetools.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <math.h>
+#include <memory>
+#include <list>
+#include <string_view>
+
+#define D_MAX_LONG_ double(0x7fffffff)
+
+namespace {
+
+short lcl_DecompValueString( OUString& rValue, sal_Int32& nVal, sal_uInt16* pMinDigits = nullptr )
+{
+ if ( rValue.isEmpty() )
+ {
+ nVal = 0;
+ return 0;
+ }
+ const sal_Unicode* p = rValue.getStr();
+ sal_Int32 nSign = 0;
+ sal_Int32 nNum = 0;
+ if ( p[nNum] == '-' || p[nNum] == '+' )
+ nNum = nSign = 1;
+ while ( p[nNum] && CharClass::isAsciiNumeric( std::u16string_view(&p[nNum], 1) ) )
+ nNum++;
+
+ sal_Unicode cNext = p[nNum]; // 0 if at the end
+ sal_Unicode cLast = p[rValue.getLength()-1];
+
+ // #i5550# If there are numbers at the beginning and the end,
+ // prefer the one at the beginning only if it's followed by a space.
+ // Otherwise, use the number at the end, to enable things like IP addresses.
+ if ( nNum > nSign && ( cNext == 0 || cNext == ' ' || !CharClass::isAsciiNumeric(std::u16string_view(&cLast, 1)) ) )
+ { // number at the beginning
+ nVal = o3tl::toInt32(rValue.subView( 0, nNum ));
+ // any number with a leading zero sets the minimum number of digits
+ if ( p[nSign] == '0' && pMinDigits && ( nNum - nSign > *pMinDigits ) )
+ *pMinDigits = nNum - nSign;
+ rValue = rValue.copy(nNum);
+ return -1;
+ }
+ else
+ {
+ nSign = 0;
+ sal_Int32 nEnd = nNum = rValue.getLength() - 1;
+ while ( nNum && CharClass::isAsciiNumeric( std::u16string_view(&p[nNum], 1) ) )
+ nNum--;
+ if ( p[nNum] == '-' || p[nNum] == '+' )
+ {
+ nNum--;
+ nSign = 1;
+ }
+ if ( nNum < nEnd - nSign )
+ { // number at the end
+ nVal = o3tl::toInt32(rValue.subView( nNum + 1 ));
+ // any number with a leading zero sets the minimum number of digits
+ if ( p[nNum+1+nSign] == '0' && pMinDigits && ( nEnd - nNum - nSign > *pMinDigits ) )
+ *pMinDigits = nEnd - nNum - nSign;
+ rValue = rValue.copy(0, nNum + 1);
+ if (nSign) // use the return value = 2 to put back the '+'
+ return 2;
+ else
+ return 1;
+ }
+ }
+ nVal = 0;
+ return 0;
+}
+
+OUString lcl_ValueString( sal_Int32 nValue, sal_uInt16 nMinDigits )
+{
+ if ( nMinDigits <= 1 )
+ return OUString::number( nValue ); // simple case...
+ else
+ {
+ OUString aStr = OUString::number( std::abs( nValue ) );
+ if ( aStr.getLength() < nMinDigits )
+ {
+ OUStringBuffer aZero(nMinDigits);
+ comphelper::string::padToLength(aZero, nMinDigits - aStr.getLength(), '0');
+ aStr = aZero.append(aStr).makeStringAndClear();
+ }
+ // nMinDigits doesn't include the '-' sign -> add after inserting zeros
+ if ( nValue < 0 )
+ aStr = "-" + aStr;
+ return aStr;
+ }
+}
+
+void setSuffixCell(
+ ScColumn& rColumn, SCROW nRow, sal_Int32 nValue, sal_uInt16 nDigits,
+ std::u16string_view rSuffix,
+ CellType eCellType, bool bIsOrdinalSuffix )
+{
+ ScDocument& rDoc = rColumn.GetDoc();
+ OUString aValue = lcl_ValueString(nValue, nDigits);
+ if (!bIsOrdinalSuffix)
+ {
+ aValue += rSuffix;
+ rColumn.SetRawString(nRow, aValue);
+ return;
+ }
+
+ OUString aOrdinalSuffix = ScGlobal::GetOrdinalSuffix(nValue);
+ if (eCellType != CELLTYPE_EDIT)
+ {
+ aValue += aOrdinalSuffix;
+ rColumn.SetRawString(nRow, aValue);
+ return;
+ }
+
+ EditEngine aEngine(rDoc.GetEnginePool());
+ aEngine.SetEditTextObjectPool(rDoc.GetEditPool());
+
+ SfxItemSet aAttr = aEngine.GetEmptyItemSet();
+ aAttr.Put( SvxEscapementItem( SvxEscapement::Superscript, EE_CHAR_ESCAPEMENT));
+ aEngine.SetText( aValue );
+ aEngine.QuickInsertText(
+ aOrdinalSuffix,
+ ESelection(0, aValue.getLength(), 0, aValue.getLength() + aOrdinalSuffix.getLength()));
+
+ aEngine.QuickSetAttribs(
+ aAttr,
+ ESelection(0, aValue.getLength(), 0, aValue.getLength() + aOrdinalSuffix.getLength()));
+
+ // Text object instance will be owned by the cell.
+ rColumn.SetEditText(nRow, aEngine.CreateTextObject());
+}
+
+}
+
+namespace {
+/* TODO: move this to rtl::math::approxDiff() ? Though the name is funny, the
+ * approx is expected to be more correct than the raw diff. */
+/** Calculate a-b trying to diminish precision errors such as for 0.11-0.12
+ not return -0.009999999999999995 but -0.01 instead.
+ */
+double approxDiff( double a, double b )
+{
+ if (a == b)
+ return 0.0;
+ if (a == 0.0)
+ return -b;
+ if (b == 0.0)
+ return a;
+ const double c = a - b;
+ const double aa = fabs(a);
+ const double ab = fabs(b);
+ if (aa < 1e-16 || aa > 1e+16 || ab < 1e-16 || ab > 1e+16)
+ // This is going nowhere, live with the result.
+ return c;
+
+ const double q = aa < ab ? b / a : a / b;
+ const double d = (a * q - b * q) / q;
+ if (d == c)
+ // No differing error, live with the result.
+ return c;
+
+ // We now have two subtractions with a similar but not equal error. Obtain
+ // the exponent of the error magnitude and round accordingly.
+ const double e = fabs(d - c);
+ const int nExp = static_cast<int>(floor(log10(e))) + 1;
+ // tdf#129606: Limit precision to the 16th significant digit of the least precise argument.
+ // Cf. mnMaxGeneralPrecision in sc/source/core/data/column3.cxx.
+ const int nExpArg = static_cast<int>(floor(log10(std::max(aa, ab)))) - 15;
+ return rtl::math::round(c, -std::max(nExp, nExpArg));
+}
+
+double approxTypedDiff( double a, double b, bool bTime, tools::Duration& rDuration )
+{
+ if (bTime)
+ {
+ rDuration = tools::Duration(a - b);
+ return rDuration.GetInDays();
+ }
+ return approxDiff( a, b);
+}
+}
+
+void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ FillCmd& rCmd, FillDateCmd& rDateCmd,
+ double& rInc, tools::Duration& rDuration, sal_uInt16& rMinDigits,
+ ScUserListData*& rListData, sal_uInt16& rListIndex,
+ bool bHasFiltered, bool& rSkipOverlappedCells,
+ std::vector<sal_Int32>& rNonOverlappedCellIdx)
+{
+ OSL_ENSURE( nCol1==nCol2 || nRow1==nRow2, "FillAnalyse: invalid range" );
+
+ rInc = 0.0;
+ rDuration = tools::Duration();
+ rMinDigits = 0;
+ rListData = nullptr;
+ rCmd = FILL_SIMPLE;
+ rSkipOverlappedCells = false;
+ if ( nScFillModeMouseModifier & KEY_MOD1 )
+ return ; // Ctrl-key: Copy
+
+ SCCOL nAddX;
+ SCROW nAddY;
+ SCSIZE nCount;
+ if (nCol1 == nCol2)
+ {
+ nAddX = 0;
+ nAddY = 1;
+ nCount = static_cast<SCSIZE>(nRow2 - nRow1 + 1);
+ }
+ else
+ {
+ nAddX = 1;
+ nAddY = 0;
+ nCount = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
+ }
+
+ // Try to analyse the merged cells only if there are no filtered rows in the destination area
+ // Else fallback to the old way to avoid regression.
+ // Filling merged cells into an area with filtered (hidden) rows, is a very complex task
+ // that is not implemented, but not even decided how to do, even excel can't handle that well
+ if (!bHasFiltered)
+ {
+ bool bHasOverlappedCells = false;
+ bool bSkipOverlappedCells = true;
+ SCCOL nColCurr = nCol1;
+ SCROW nRowCurr = nRow1;
+
+ // collect cells that are not empty or not overlapped
+ rNonOverlappedCellIdx.resize(nCount);
+ SCSIZE nValueCount = 0;
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ const ScPatternAttr* pPattern = GetPattern(nColCurr, nRowCurr);
+ bool bOverlapped
+ = pPattern->GetItemSet().GetItemState(ATTR_MERGE_FLAG, false) == SfxItemState::SET
+ && pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped();
+
+ if (bOverlapped)
+ bHasOverlappedCells = true;
+
+ if (!bOverlapped || GetCellValue(nColCurr, nRowCurr).getType() != CELLTYPE_NONE)
+ {
+ rNonOverlappedCellIdx[nValueCount++] = i;
+ // if there is at least 1 non empty overlapped cell, then no cell should be skipped
+ if (bOverlapped)
+ bSkipOverlappedCells = false;
+ }
+
+ nColCurr += nAddX;
+ nRowCurr += nAddY;
+ }
+ rNonOverlappedCellIdx.resize(nValueCount);
+
+ // if all the values are overlapped CELLTYPE_NONE, then there is no need to analyse it.
+ if (nValueCount == 0)
+ return;
+
+ // if there is no overlapped cells, there is nothing to skip
+ if (!bHasOverlappedCells)
+ bSkipOverlappedCells = false;
+
+ if (bSkipOverlappedCells)
+ {
+ nColCurr = nCol1 + rNonOverlappedCellIdx[0] * nAddX;
+ nRowCurr = nRow1 + rNonOverlappedCellIdx[0] * nAddY;
+ ScRefCellValue aPrevCell, aCurrCell;
+ aCurrCell = GetCellValue(nColCurr, nRowCurr);
+ CellType eCellType = aCurrCell.getType();
+ if (eCellType == CELLTYPE_VALUE)
+ {
+ bool bVal = true;
+ double fVal;
+ SvNumFormatType nCurrCellFormatType
+ = rDocument.GetFormatTable()->GetType(GetNumberFormat(nColCurr, nRowCurr));
+ if (nCurrCellFormatType == SvNumFormatType::DATE)
+ {
+ if (nValueCount >= 2)
+ {
+ tools::Long nCmpInc = 0;
+ FillDateCmd eType = FILL_YEAR; // just some temporary default values
+ tools::Long nDDiff = 0, nMDiff = 0, nYDiff = 0; // to avoid warnings
+ Date aNullDate = rDocument.GetFormatTable()->GetNullDate();
+ Date aCurrDate = aNullDate, aPrevDate = aNullDate;
+ aCurrDate.AddDays(aCurrCell.getDouble());
+ for (SCSIZE i = 1; i < nValueCount && bVal; i++)
+ {
+ aPrevCell = aCurrCell;
+ aPrevDate = aCurrDate;
+ nColCurr = nCol1 + rNonOverlappedCellIdx[i] * nAddX;
+ nRowCurr = nRow1 + rNonOverlappedCellIdx[i] * nAddY;
+ aCurrCell = GetCellValue(nColCurr, nRowCurr);
+ if (aCurrCell.getType() == CELLTYPE_VALUE)
+ {
+ aCurrDate = aNullDate + static_cast<sal_Int32>(aCurrCell.getDouble());
+ if (eType != FILL_DAY) {
+ nDDiff = aCurrDate.GetDay()
+ - static_cast<tools::Long>(aPrevDate.GetDay());
+ nMDiff = aCurrDate.GetMonth()
+ - static_cast<tools::Long>(aPrevDate.GetMonth());
+ nYDiff = aCurrDate.GetYear()
+ - static_cast<tools::Long>(aPrevDate.GetYear());
+ }
+ if (i == 1)
+ {
+ if (nDDiff != 0)
+ {
+ eType = FILL_DAY;
+ nCmpInc = aCurrDate - aPrevDate;
+ }
+ else
+ {
+ eType = FILL_MONTH;
+ nCmpInc = nMDiff + 12 * nYDiff;
+ }
+ }
+ else if (eType == FILL_DAY)
+ {
+ if (aCurrDate - aPrevDate != nCmpInc)
+ bVal = false;
+ }
+ else
+ {
+ if (nDDiff || (nMDiff + 12 * nYDiff != nCmpInc))
+ bVal = false;
+ }
+ }
+ else
+ bVal = false; // No date is also not ok
+ }
+ if (bVal)
+ {
+ if (eType == FILL_MONTH && (nCmpInc % 12 == 0))
+ {
+ eType = FILL_YEAR;
+ nCmpInc /= 12;
+ }
+ rCmd = FILL_DATE;
+ rDateCmd = eType;
+ rInc = nCmpInc;
+ rSkipOverlappedCells = true;
+ return;
+ }
+ }
+ else
+ {
+ rCmd = FILL_DATE;
+ rDateCmd = FILL_DAY;
+ rInc = 1.0;
+ rSkipOverlappedCells = true;
+ return;
+ }
+ }
+ else if (nCurrCellFormatType == SvNumFormatType::LOGICAL
+ && ((fVal = aCurrCell.getDouble()) == 0.0 || fVal == 1.0))
+ {
+ }
+ else if (nValueCount >= 2)
+ {
+ tools::Duration aDuration;
+ for (SCSIZE i = 1; i < nValueCount && bVal; i++)
+ {
+ aPrevCell = aCurrCell;
+ nColCurr = nCol1 + rNonOverlappedCellIdx[i] * nAddX;
+ nRowCurr = nRow1 + rNonOverlappedCellIdx[i] * nAddY;
+ aCurrCell = GetCellValue(nColCurr, nRowCurr);
+ if (aCurrCell.getType() == CELLTYPE_VALUE)
+ {
+ const bool bTime = (nCurrCellFormatType == SvNumFormatType::TIME ||
+ nCurrCellFormatType == SvNumFormatType::DATETIME);
+ double nDiff = approxTypedDiff(aCurrCell.getDouble(), aPrevCell.getDouble(),
+ bTime, aDuration);
+ if (i == 1)
+ {
+ rInc = nDiff;
+ if (bTime)
+ rDuration = aDuration;
+ }
+ if (!::rtl::math::approxEqual(nDiff, rInc, 13))
+ bVal = false;
+ else if ((aCurrCell.getDouble() == 0.0 || aCurrCell.getDouble() == 1.0)
+ && (rDocument.GetFormatTable()->GetType(
+ GetNumberFormat(nColCurr, nRowCurr))
+ == SvNumFormatType::LOGICAL))
+ bVal = false;
+ }
+ else
+ bVal = false;
+ }
+ if (bVal)
+ {
+ rCmd = FILL_LINEAR;
+ rSkipOverlappedCells = true;
+ return;
+ }
+ }
+ }
+ else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT)
+ {
+ OUString aStr = GetString(nColCurr, nRowCurr );
+ OUString aStr2;
+
+ rListData = const_cast<ScUserListData*>(ScGlobal::GetUserList()->GetData(aStr));
+ if (rListData)
+ {
+ bool bMatchCase = false;
+ (void)rListData->GetSubIndex(aStr, rListIndex, bMatchCase);
+ size_t nListStrCount = rListData->GetSubCount();
+ sal_uInt16 nPrevListIndex, nInc = 1;
+ for (SCSIZE i = 1; i < nValueCount && rListData; i++)
+ {
+ nColCurr = nCol1 + rNonOverlappedCellIdx[i] * nAddX;
+ nRowCurr = nRow1 + rNonOverlappedCellIdx[i] * nAddY;
+ aStr2 = GetString(nColCurr, nRowCurr);
+
+ nPrevListIndex = rListIndex;
+ if (!rListData->GetSubIndex(aStr2, rListIndex, bMatchCase))
+ rListData = nullptr;
+ else
+ {
+ sal_Int32 nIncCurr = rListIndex - nPrevListIndex;
+ if (nIncCurr < 0)
+ nIncCurr += nListStrCount;
+ if (i == 1)
+ nInc = nIncCurr;
+ else if (nInc != nIncCurr)
+ rListData = nullptr;
+ }
+ }
+ if (rListData) {
+ rInc = nInc;
+ rSkipOverlappedCells = true;
+ return;
+ }
+ }
+ short nFlag1, nFlag2;
+ sal_Int32 nVal1, nVal2;
+ nFlag1 = lcl_DecompValueString(aStr, nVal1, &rMinDigits);
+ if (nFlag1)
+ {
+ bool bVal = true;
+ rInc = 1;
+ for (SCSIZE i = 1; i < nValueCount && bVal; i++)
+ {
+ nColCurr = nCol1 + rNonOverlappedCellIdx[i] * nAddX;
+ nRowCurr = nRow1 + rNonOverlappedCellIdx[i] * nAddY;
+ ScRefCellValue aCell = GetCellValue(nColCurr, nRowCurr);
+ CellType eType = aCell.getType();
+ if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT)
+ {
+ aStr2 = aCell.getString(&rDocument);
+ nFlag2 = lcl_DecompValueString(aStr2, nVal2, &rMinDigits);
+ if (nFlag1 == nFlag2 && aStr == aStr2)
+ {
+ double nDiff = approxDiff(nVal2, nVal1);
+ if (i == 1)
+ rInc = nDiff;
+ else if (!::rtl::math::approxEqual(nDiff, rInc, 13))
+ bVal = false;
+ nVal1 = nVal2;
+ }
+ else
+ bVal = false;
+ }
+ else
+ bVal = false;
+ }
+ if (bVal)
+ {
+ rCmd = FILL_LINEAR;
+ rSkipOverlappedCells = true;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ //if it is not a FILL_LINEAR - CELLTYPE_VALUE - with merged cells [without hidden values]
+ //then do it in the old way
+
+ SCCOL nCol = nCol1;
+ SCROW nRow = nRow1;
+
+ ScRefCellValue aFirstCell = GetCellValue(nCol, nRow);
+ CellType eCellType = aFirstCell.getType();
+
+ if (eCellType == CELLTYPE_VALUE)
+ {
+ double fVal;
+ sal_uInt32 nFormat = GetAttr(nCol,nRow,ATTR_VALUE_FORMAT)->GetValue();
+ const SvNumFormatType nFormatType = rDocument.GetFormatTable()->GetType(nFormat);
+ bool bDate = (nFormatType == SvNumFormatType::DATE); // date without time
+ bool bTime = (nFormatType == SvNumFormatType::TIME || nFormatType == SvNumFormatType::DATETIME);
+ bool bBooleanCell = (nFormatType == SvNumFormatType::LOGICAL);
+ if (bDate)
+ {
+ if (nCount > 1)
+ {
+ double nVal;
+ Date aNullDate = rDocument.GetFormatTable()->GetNullDate();
+ Date aDate1 = aNullDate;
+ nVal = aFirstCell.getDouble();
+ aDate1.AddDays(nVal);
+ Date aDate2 = aNullDate;
+ nVal = GetValue(nCol+nAddX, nRow+nAddY);
+ aDate2.AddDays(nVal);
+ if ( aDate1 != aDate2 )
+ {
+ tools::Long nCmpInc = 0;
+ FillDateCmd eType;
+ tools::Long nDDiff = aDate2.GetDay() - static_cast<tools::Long>(aDate1.GetDay());
+ tools::Long nMDiff = aDate2.GetMonth() - static_cast<tools::Long>(aDate1.GetMonth());
+ tools::Long nYDiff = aDate2.GetYear() - static_cast<tools::Long>(aDate1.GetYear());
+ if (nMDiff && aDate1.IsEndOfMonth() && aDate2.IsEndOfMonth())
+ {
+ eType = FILL_END_OF_MONTH;
+ nCmpInc = nMDiff + 12 * nYDiff;
+ }
+ else if (nDDiff)
+ {
+ eType = FILL_DAY;
+ nCmpInc = aDate2 - aDate1;
+ }
+ else
+ {
+ eType = FILL_MONTH;
+ nCmpInc = nMDiff + 12 * nYDiff;
+ }
+
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ bool bVal = true;
+ for (SCSIZE i=1; i<nCount && bVal; i++)
+ {
+ ScRefCellValue aCell = GetCellValue(nCol,nRow);
+ if (aCell.getType() == CELLTYPE_VALUE)
+ {
+ nVal = aCell.getDouble();
+ aDate2 = aNullDate + static_cast<sal_Int32>(nVal);
+ if ( eType == FILL_DAY )
+ {
+ if ( aDate2-aDate1 != nCmpInc )
+ bVal = false;
+ }
+ else
+ {
+ nDDiff = aDate2.GetDay() - static_cast<tools::Long>(aDate1.GetDay());
+ nMDiff = aDate2.GetMonth() - static_cast<tools::Long>(aDate1.GetMonth());
+ nYDiff = aDate2.GetYear() - static_cast<tools::Long>(aDate1.GetYear());
+ if ((nDDiff && !aDate1.IsEndOfMonth() && !aDate2.IsEndOfMonth())
+ || (nMDiff + 12 * nYDiff != nCmpInc))
+ bVal = false;
+ }
+ aDate1 = aDate2;
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ }
+ else
+ bVal = false; // No date is also not ok
+ }
+ if (bVal)
+ {
+ if ((eType == FILL_MONTH || eType == FILL_END_OF_MONTH)
+ && (nCmpInc % 12 == 0))
+ {
+ eType = FILL_YEAR;
+ nCmpInc /= 12;
+ }
+ rCmd = FILL_DATE;
+ rDateCmd = eType;
+ rInc = nCmpInc;
+ }
+ }
+ else
+ {
+ // tdf#89754 - don't increment non different consecutive date cells
+ rCmd = FILL_DATE;
+ rDateCmd = FILL_DAY;
+ rInc = 0.0;
+ }
+ }
+ else // single date -> increment by days
+ {
+ rCmd = FILL_DATE;
+ rDateCmd = FILL_DAY;
+ rInc = 1.0;
+ }
+ }
+ else if (bBooleanCell && ((fVal = aFirstCell.getDouble()) == 0.0 || fVal == 1.0))
+ {
+ // Nothing, rInc stays 0.0, no specific fill mode.
+ }
+ else
+ {
+ if (nCount > 1)
+ {
+ tools::Duration aDuration;
+ double nVal1 = aFirstCell.getDouble();
+ double nVal2 = GetValue(nCol+nAddX, nRow+nAddY);
+ rInc = approxTypedDiff( nVal2, nVal1, bTime, aDuration);
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ bool bVal = true;
+ for (SCSIZE i=1; i<nCount && bVal; i++)
+ {
+ ScRefCellValue aCell = GetCellValue(nCol,nRow);
+ if (aCell.getType() == CELLTYPE_VALUE)
+ {
+ nVal2 = aCell.getDouble();
+ double nDiff = approxTypedDiff( nVal2, nVal1, bTime, aDuration);
+ if ( !::rtl::math::approxEqual( nDiff, rInc, 13 ) )
+ bVal = false;
+ else if ((nVal2 == 0.0 || nVal2 == 1.0) &&
+ (rDocument.GetFormatTable()->GetType(GetNumberFormat(nCol,nRow)) ==
+ SvNumFormatType::LOGICAL))
+ bVal = false;
+ nVal1 = nVal2;
+ }
+ else
+ bVal = false;
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ if (bVal && bTime)
+ rDuration = aDuration;
+ }
+ if (bVal)
+ rCmd = FILL_LINEAR;
+ }
+ else if(nFormatType == SvNumFormatType::PERCENT)
+ {
+ rInc = 0.01; // tdf#89998 increment by 1% at a time
+ }
+ }
+ }
+ else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT)
+ {
+ OUString aStr = GetString(nCol, nRow);
+
+ rListData = const_cast<ScUserListData*>(ScGlobal::GetUserList()->GetData(aStr));
+ if (rListData)
+ {
+ bool bMatchCase = false;
+ (void)rListData->GetSubIndex(aStr, rListIndex, bMatchCase);
+ size_t nListStrCount = rListData->GetSubCount();
+ sal_uInt16 nPrevListIndex, nInc = 1;
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ for (SCSIZE i=1; i<nCount && rListData; i++)
+ {
+ nPrevListIndex = rListIndex;
+ aStr = GetString(nCol, nRow);
+ if (!rListData->GetSubIndex(aStr, rListIndex, bMatchCase))
+ rListData = nullptr;
+ else
+ {
+ sal_Int32 nIncCurr = rListIndex - nPrevListIndex;
+ if (nIncCurr < 0)
+ nIncCurr += nListStrCount;
+ if (i == 1)
+ nInc = nIncCurr;
+ else if (nInc != nIncCurr)
+ rListData = nullptr;
+ }
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ }
+ if (rListData)
+ rInc = nInc;
+ }
+ else if ( nCount > 1 )
+ {
+ // pass rMinDigits to all DecompValueString calls
+ // -> longest number defines rMinDigits
+
+ sal_Int32 nVal1;
+ short nFlag1 = lcl_DecompValueString( aStr, nVal1, &rMinDigits );
+ if ( nFlag1 )
+ {
+ sal_Int32 nVal2;
+ aStr = GetString( nCol+nAddX, nRow+nAddY );
+ short nFlag2 = lcl_DecompValueString( aStr, nVal2, &rMinDigits );
+ if ( nFlag1 == nFlag2 )
+ {
+ rInc = approxDiff( nVal2, nVal1);
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ bool bVal = true;
+ for (SCSIZE i=1; i<nCount && bVal; i++)
+ {
+ ScRefCellValue aCell = GetCellValue(nCol, nRow);
+ CellType eType = aCell.getType();
+ if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT )
+ {
+ aStr = aCell.getString(&rDocument);
+ nFlag2 = lcl_DecompValueString( aStr, nVal2, &rMinDigits );
+ if ( nFlag1 == nFlag2 )
+ {
+ double nDiff = approxDiff( nVal2, nVal1);
+ if ( !::rtl::math::approxEqual( nDiff, rInc, 13 ) )
+ bVal = false;
+ nVal1 = nVal2;
+ }
+ else
+ bVal = false;
+ }
+ else
+ bVal = false;
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
+ nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
+ }
+ if (bVal)
+ rCmd = FILL_LINEAR;
+ }
+ }
+ }
+ else
+ {
+ // call DecompValueString to set rMinDigits
+ sal_Int32 nDummy;
+ lcl_DecompValueString( aStr, nDummy, &rMinDigits );
+ }
+ }
+}
+
+void ScTable::FillFormula(
+ const ScFormulaCell* pSrcCell, SCCOL nDestCol, SCROW nDestRow, bool bLast )
+{
+
+ rDocument.SetNoListening( true ); // still the wrong reference
+ ScAddress aAddr( nDestCol, nDestRow, nTab );
+ ScFormulaCell* pDestCell = new ScFormulaCell( *pSrcCell, rDocument, aAddr );
+ aCol[nDestCol].SetFormulaCell(nDestRow, pDestCell);
+
+ if ( bLast && pDestCell->GetMatrixFlag() != ScMatrixMode::NONE )
+ {
+ ScAddress aOrg;
+ if ( pDestCell->GetMatrixOrigin( GetDoc(), aOrg ) )
+ {
+ if ( nDestCol >= aOrg.Col() && nDestRow >= aOrg.Row() )
+ {
+ ScFormulaCell* pOrgCell = rDocument.GetFormulaCell(aOrg);
+ if (pOrgCell && pOrgCell->GetMatrixFlag() == ScMatrixMode::Formula)
+ {
+ pOrgCell->SetMatColsRows(
+ nDestCol - aOrg.Col() + 1,
+ nDestRow - aOrg.Row() + 1 );
+ }
+ else
+ {
+ OSL_FAIL( "FillFormula: MatrixOrigin no formula cell with ScMatrixMode::Formula" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "FillFormula: MatrixOrigin bottom right" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "FillFormula: no MatrixOrigin" );
+ }
+ }
+ rDocument.SetNoListening( false );
+ pDestCell->StartListeningTo( rDocument );
+}
+
+void ScTable::FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt64 nFillCount, FillDir eFillDir, ScProgress* pProgress )
+{
+ if ( (nFillCount == 0) || !ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2) )
+ return;
+
+ // Detect direction
+
+ bool bVertical = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP);
+ bool bPositive = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_RIGHT);
+
+ SCCOLROW nCol = 0;
+ SCCOLROW nRow = 0;
+ SCCOLROW& rInner = bVertical ? nRow : nCol; // loop variables
+ SCCOLROW& rOuter = bVertical ? nCol : nRow;
+ SCCOLROW nOStart;
+ SCCOLROW nOEnd;
+ SCCOLROW nIStart;
+ SCCOLROW nIEnd;
+ SCCOLROW nISrcStart;
+ SCCOLROW nISrcEnd;
+ ScRange aFillRange;
+
+ if (bVertical)
+ {
+ nOStart = nCol1;
+ nOEnd = nCol2;
+ if (bPositive)
+ {
+ nISrcStart = nRow1;
+ nISrcEnd = nRow2;
+ nIStart = nRow2 + 1;
+ nIEnd = nRow2 + nFillCount;
+ aFillRange = ScRange(nCol1, nRow2+1, 0, nCol2, nRow2 + nFillCount, 0);
+ }
+ else
+ {
+ nISrcStart = nRow2;
+ nISrcEnd = nRow1;
+ nIStart = nRow1 - 1;
+ nIEnd = nRow1 - nFillCount;
+ aFillRange = ScRange(nCol1, nRow1-1, 0, nCol2, nRow2 - nFillCount, 0);
+ }
+ }
+ else
+ {
+ nOStart = nRow1;
+ nOEnd = nRow2;
+ if (bPositive)
+ {
+ nISrcStart = nCol1;
+ nISrcEnd = nCol2;
+ nIStart = nCol2 + 1;
+ nIEnd = nCol2 + nFillCount;
+ aFillRange = ScRange(nCol2 + 1, nRow1, 0, nCol2 + nFillCount, nRow2, 0);
+ }
+ else
+ {
+ nISrcStart = nCol2;
+ nISrcEnd = nCol1;
+ nIStart = nCol1 - 1;
+ nIEnd = nCol1 - nFillCount;
+ aFillRange = ScRange(nCol1 - 1, nRow1, 0, nCol1 - nFillCount, nRow2, 0);
+ }
+ }
+ sal_uInt64 nIMin = nIStart;
+ sal_uInt64 nIMax = nIEnd;
+ PutInOrder(nIMin,nIMax);
+ bool bHasFiltered = IsDataFiltered(aFillRange);
+
+ if (!bHasFiltered)
+ {
+ if (bVertical)
+ DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), InsertDeleteFlags::AUTOFILL);
+ else
+ DeleteArea(static_cast<SCCOL>(nIMin), nRow1, static_cast<SCCOL>(nIMax), nRow2, InsertDeleteFlags::AUTOFILL);
+ }
+
+ sal_uInt64 nProgress = 0;
+ if (pProgress)
+ nProgress = pProgress->GetState();
+
+ // Avoid possible repeated calls to StartListeningFormulaCells() (tdf#132165).
+ std::list< sc::DelayStartListeningFormulaCells > delayStartListening;
+ SCCOL delayStartColumn, delayEndColumn;
+ if(bVertical)
+ {
+ delayStartColumn = std::min( nOStart, nOEnd );
+ delayEndColumn = std::max( nOStart, nOEnd );
+ }
+ else
+ {
+ delayStartColumn = std::min( nIStart, nIEnd );
+ delayEndColumn = std::max( nIStart, nIEnd );
+ }
+ for( SCROW col = delayStartColumn; col <= delayEndColumn; ++col )
+ {
+ if( ScColumn* column = FetchColumn( col ))
+ delayStartListening.emplace_back( *column, true );
+ }
+
+ // execute
+
+ sal_uInt64 nActFormCnt = 0;
+ for (rOuter = nOStart; rOuter <= nOEnd; rOuter++)
+ {
+ sal_uInt64 nMaxFormCnt = 0; // for formulas
+
+ // transfer attributes
+
+ const ScPatternAttr* pSrcPattern = nullptr;
+ const ScStyleSheet* pStyleSheet = nullptr;
+ SCCOLROW nAtSrc = nISrcStart;
+ std::unique_ptr<ScPatternAttr> pNewPattern;
+ bool bGetPattern = true;
+ rInner = nIStart;
+ while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
+ {
+ if (!ColHidden(nCol) && !RowHidden(nRow))
+ {
+ if ( bGetPattern )
+ {
+ if (bVertical) // rInner&:=nRow, rOuter&:=nCol
+ pSrcPattern = GetColumnData(nCol).GetPattern(static_cast<SCROW>(nAtSrc));
+ else // rInner&:=nCol, rOuter&:=nRow
+ pSrcPattern = GetColumnData(nAtSrc).GetPattern(static_cast<SCROW>(nRow));
+ bGetPattern = false;
+ pStyleSheet = pSrcPattern->GetStyleSheet();
+ // do transfer ATTR_MERGE / ATTR_MERGE_FLAG
+ //
+ // Note: ATTR_MERGE is an attribute of the top left cell of a merged area
+ // containing the size of the area. ATTR_MERGE_FLAGs are attributes of the
+ // other cells of a merged area, containing the information about also
+ // overlapping, i.e. visibility of their content.
+ //
+ // TODO: extend the similar incomplete selections to a bounding rectangle to
+ // avoid incomplete fill, where not all AUTO_MERGE_FLAGs are synchronized with
+ // the copied ATTR_MERGE, resulting broken grid and visibility during run-time.
+ //
+ // +--+ +--+--+
+ // | | | | |
+ // +--+--+ +--+--+
+ // | | -> | |
+ // +--+--+ +--+--+
+ // | | | | |
+ // +--+ +--+--+
+ //
+ // TODO: protect incompatible merged cells of the destination area, for example
+ // by skipping the fill operation.
+ //
+ // TODO: by dragging the fill handle select only the multiples of the height
+ // of the originally selected area which is merged vertically to avoid of
+ // incomplete fill.
+ //
+ // +--+ +--+
+ // |XX| |XX|
+ // +XX+ +XX+
+ // |XX| -> |XX|
+ // +--+ +--+
+ // | | | |
+ // +--+ +--+
+ // | |
+ // +--+
+ //
+ // Other things stored in ATTR_MERGE_FLAG, like autofilter button, will be
+ // deleted now, but may need to be repaired later, like at ScDocument::Fill.
+ const SfxItemSet& rSet = pSrcPattern->GetItemSet();
+ if ( rSet.GetItemState(ATTR_MERGE_FLAG, false) == SfxItemState::SET )
+ {
+ ScMF nOldValue = pSrcPattern->GetItem(ATTR_MERGE_FLAG).GetValue();
+ ScMF nOldValueMerge = nOldValue & (ScMF::Hor | ScMF::Ver);
+ // keep only the merge flags
+ if ( nOldValue != nOldValueMerge )
+ {
+ pNewPattern.reset(new ScPatternAttr(*pSrcPattern));
+ SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+ if ( nOldValueMerge == ScMF::NONE )
+ rNewSet.ClearItem(ATTR_MERGE_FLAG);
+ else
+ rNewSet.Put(ScMergeFlagAttr(nOldValueMerge));
+ }
+ else
+ pNewPattern.reset();
+ }
+ else
+ pNewPattern.reset();
+ }
+
+ const ScCondFormatItem& rCondFormatItem = pSrcPattern->GetItem(ATTR_CONDITIONAL);
+ const ScCondFormatIndexes& rCondFormatIndex = rCondFormatItem.GetCondFormatData();
+
+ if ( bVertical && nISrcStart == nISrcEnd && !bHasFiltered )
+ {
+ // set all attributes at once (en bloc)
+ if (pNewPattern || !SfxPoolItem::areSame(pSrcPattern, rDocument.GetDefPattern()))
+ {
+ // Default is already present (DeleteArea)
+ SCROW nY1 = static_cast<SCROW>(std::min( nIStart, nIEnd ));
+ SCROW nY2 = static_cast<SCROW>(std::max( nIStart, nIEnd ));
+ if ( pStyleSheet )
+ aCol[nCol].ApplyStyleArea( nY1, nY2, *pStyleSheet );
+ if ( pNewPattern )
+ aCol[nCol].ApplyPatternArea( nY1, nY2, *pNewPattern );
+ else
+ aCol[nCol].ApplyPatternArea( nY1, nY2, *pSrcPattern );
+
+ for(const auto& rIndex : rCondFormatIndex)
+ {
+ ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex);
+ if (pCondFormat)
+ {
+ ScRangeList aRange = pCondFormat->GetRange();
+ aRange.Join(ScRange(nCol, nY1, nTab, nCol, nY2, nTab));
+ pCondFormat->SetRange(aRange);
+ }
+ }
+ }
+
+ break;
+ }
+
+ if ( bHasFiltered )
+ DeleteArea(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow),
+ static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), InsertDeleteFlags::AUTOFILL);
+
+ if ( !SfxPoolItem::areSame(pSrcPattern, aCol[nCol].GetPattern( static_cast<SCROW>(nRow) ) ) )
+ {
+ // Transfer template too
+ //TODO: Merge ApplyPattern to AttrArray ??
+ if ( pStyleSheet )
+ aCol[nCol].ApplyStyle( static_cast<SCROW>(nRow), pStyleSheet );
+
+ // Use ApplyPattern instead of SetPattern to keep old MergeFlags
+ if ( pNewPattern )
+ aCol[nCol].ApplyPattern( static_cast<SCROW>(nRow), *pNewPattern );
+ else
+ aCol[nCol].ApplyPattern( static_cast<SCROW>(nRow), *pSrcPattern );
+
+ for(const auto& rIndex : rCondFormatIndex)
+ {
+ ScConditionalFormat* pCondFormat = mpCondFormatList->GetFormat(rIndex);
+ if (pCondFormat)
+ {
+ ScRangeList aRange = pCondFormat->GetRange();
+ aRange.Join(ScRange(nCol, nRow, nTab, nCol, nRow, nTab));
+ pCondFormat->SetRange(aRange);
+ }
+ }
+ }
+
+ if (nAtSrc==nISrcEnd)
+ {
+ if ( nAtSrc != nISrcStart )
+ { // More than one source cell
+ nAtSrc = nISrcStart;
+ bGetPattern = true;
+ }
+ }
+ else if (bPositive)
+ {
+ ++nAtSrc;
+ bGetPattern = true;
+ }
+ else
+ {
+ --nAtSrc;
+ bGetPattern = true;
+ }
+ }
+
+ if (rInner == nIEnd) break;
+ if (bPositive) ++rInner; else --rInner;
+ }
+ pNewPattern.reset();
+
+ // Analyse
+
+ FillCmd eFillCmd;
+ FillDateCmd eDateCmd = {};
+ double nInc;
+ tools::Duration aDurationInc;
+ sal_uInt16 nMinDigits;
+ ScUserListData* pListData = nullptr;
+ sal_uInt16 nListIndex;
+ bool bSkipOverlappedCells;
+ std::vector<sal_Int32> aNonOverlappedCellIdx;
+ if (bVertical)
+ FillAnalyse(static_cast<SCCOL>(nCol),nRow1,
+ static_cast<SCCOL>(nCol),nRow2, eFillCmd,eDateCmd,
+ nInc, aDurationInc, nMinDigits, pListData, nListIndex,
+ bHasFiltered, bSkipOverlappedCells, aNonOverlappedCellIdx);
+ else
+ FillAnalyse(nCol1,static_cast<SCROW>(nRow),
+ nCol2,static_cast<SCROW>(nRow), eFillCmd,eDateCmd,
+ nInc, aDurationInc, nMinDigits, pListData, nListIndex,
+ bHasFiltered, bSkipOverlappedCells, aNonOverlappedCellIdx);
+
+ if (pListData)
+ {
+ sal_uInt16 nListCount = pListData->GetSubCount();
+ if (bSkipOverlappedCells)
+ {
+ int nFillerCount = 1 + ( nISrcEnd - nISrcStart ) * (bPositive ? 1 : -1);
+ std::vector<bool> aIsNonEmptyCell(nFillerCount, false);
+ SCCOLROW nLastValueIdx;
+ if (bPositive)
+ {
+ nLastValueIdx = nISrcEnd - (nFillerCount - 1 - aNonOverlappedCellIdx.back());
+ for (auto i : aNonOverlappedCellIdx)
+ aIsNonEmptyCell[i] = true;
+ }
+ else
+ {
+ nLastValueIdx = nISrcEnd + aNonOverlappedCellIdx[0];
+ for (auto i : aNonOverlappedCellIdx)
+ aIsNonEmptyCell[nFillerCount - 1 - i] = true;
+ }
+
+ OUString aStr;
+ if (bVertical)
+ aStr = GetString(rOuter, nLastValueIdx);
+ else
+ aStr = GetString(nLastValueIdx, rOuter);
+
+ bool bMatchCase = false;
+ (void)pListData->GetSubIndex(aStr, nListIndex, bMatchCase);
+
+ sal_Int32 nFillerIdx = 0;
+ rInner = nIStart;
+ while (true)
+ {
+ if (aIsNonEmptyCell[nFillerIdx])
+ {
+ if (bPositive)
+ {
+ nListIndex += nInc;
+ if (nListIndex >= nListCount) nListIndex -= nListCount;
+ }
+ else
+ {
+ if (nListIndex < nInc) nListIndex += nListCount;
+ nListIndex -= nInc;
+ }
+ aCol[nCol].SetRawString(static_cast<SCROW>(nRow), pListData->GetSubStr(nListIndex));
+
+ }
+ if (rInner == nIEnd) break;
+ nFillerIdx = (nFillerIdx + 1) % nFillerCount;
+ if (bPositive)
+ ++rInner;
+ else
+ --rInner;
+ }
+ }
+ else
+ {
+ if (!bPositive)
+ {
+ // nListIndex of FillAnalyse points to the last entry -> adjust
+ sal_Int64 nAdjust = nListIndex - (nISrcStart - nISrcEnd) * nInc;
+ nAdjust = nAdjust % nListCount;
+ if (nAdjust < 0)
+ nAdjust += nListCount;
+ nListIndex = nAdjust;
+ }
+
+ rInner = nIStart;
+ while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
+ {
+ if (!ColHidden(nCol) && !RowHidden(nRow))
+ {
+ if (bPositive)
+ {
+ nListIndex += nInc;
+ if (nListIndex >= nListCount) nListIndex -= nListCount;
+ }
+ else
+ {
+ if (nListIndex < nInc) nListIndex += nListCount;
+ nListIndex -= nInc;
+ }
+ aCol[nCol].SetRawString(static_cast<SCROW>(nRow), pListData->GetSubStr(nListIndex));
+ }
+
+ if (rInner == nIEnd) break;
+ if (bPositive) ++rInner; else --rInner;
+ }
+ }
+ if(pProgress)
+ {
+ nProgress += nIMax - nIMin + 1;
+ pProgress->SetStateOnPercent( nProgress );
+ }
+ }
+ else if (eFillCmd == FILL_SIMPLE) // fill with pattern/sample
+ {
+ FillAutoSimple(
+ nISrcStart, nISrcEnd, nIStart, nIEnd, rInner, nCol, nRow,
+ nActFormCnt, nMaxFormCnt, bHasFiltered, bVertical, bPositive, pProgress, nProgress);
+ }
+ else
+ {
+ if (!bPositive)
+ {
+ nInc = -nInc;
+ aDurationInc = -aDurationInc;
+ }
+ double nEndVal = (nInc>=0.0) ? MAXDOUBLE : -MAXDOUBLE;
+ if (bVertical)
+ FillSeries( static_cast<SCCOL>(nCol), nRow1,
+ static_cast<SCCOL>(nCol), nRow2, nFillCount, eFillDir,
+ eFillCmd, eDateCmd, nInc, aDurationInc, nEndVal, nMinDigits, false,
+ pProgress, bSkipOverlappedCells, &aNonOverlappedCellIdx);
+ else
+ FillSeries( nCol1, static_cast<SCROW>(nRow), nCol2,
+ static_cast<SCROW>(nRow), nFillCount, eFillDir,
+ eFillCmd, eDateCmd, nInc, aDurationInc, nEndVal, nMinDigits, false,
+ pProgress, bSkipOverlappedCells, &aNonOverlappedCellIdx);
+ if (pProgress)
+ nProgress = pProgress->GetState();
+ }
+
+ if (bVertical)
+ FillSparkline(bVertical, nCol, nRow1, nRow2, nIStart, nIEnd);
+ else
+ FillSparkline(bVertical, nRow, nCol1, nCol2, nIStart, nIEnd);
+
+ nActFormCnt += nMaxFormCnt;
+ }
+}
+
+void ScTable::FillSparkline(bool bVertical, SCCOLROW nFixed,
+ SCCOLROW nStart, SCCOLROW nEnd,
+ SCCOLROW nFillStart, SCCOLROW nFillEnd)
+{
+ bool bHasSparklines = false;
+ std::vector<std::shared_ptr<sc::Sparkline>> aSparklineSeries;
+
+ for (SCROW nCurrent = nStart; nCurrent <= nEnd; nCurrent++)
+ {
+ auto pSparkline = bVertical ? GetSparkline(nFixed, nCurrent) : GetSparkline(nCurrent, nFixed);
+ bHasSparklines = bHasSparklines || pSparkline;
+ aSparklineSeries.push_back(pSparkline);
+ }
+
+ if (bHasSparklines)
+ {
+ for (SCCOLROW nCurrent = nFillStart; nCurrent <= nFillEnd; nCurrent++)
+ {
+ size_t nIndex = size_t(nFillStart - nCurrent) % aSparklineSeries.size();
+ if (auto& rpSparkline = aSparklineSeries[nIndex])
+ {
+ auto pGroup = rpSparkline->getSparklineGroup();
+
+ auto* pNewSparkline = bVertical ? CreateSparkline(nFixed, nCurrent, pGroup)
+ : CreateSparkline(nCurrent, nFixed, pGroup);
+ if (pNewSparkline)
+ {
+ SCCOLROW nPosition = bVertical ? rpSparkline->getRow()
+ : rpSparkline->getColumn();
+ SCCOLROW nDelta = nCurrent - nPosition;
+ ScRangeList aRangeList(rpSparkline->getInputRange());
+ for (ScRange& rRange : aRangeList)
+ {
+ if (bVertical)
+ {
+ rRange.aStart.IncRow(nDelta);
+ rRange.aEnd.IncRow(nDelta);
+ }
+ else
+ {
+ rRange.aStart.IncCol(nDelta);
+ rRange.aEnd.IncCol(nDelta);
+ }
+ }
+ pNewSparkline->setInputRange(aRangeList);
+ }
+ }
+ }
+ }
+}
+
+void ScTable::GetBackColorArea(SCCOL& rStartCol, SCROW& /*rStartRow*/,
+ SCCOL& rEndCol, SCROW& rEndRow ) const
+{
+ bool bExtend;
+ const SvxBrushItem* pDefBackground = &rDocument.GetPool()->GetDefaultItem(ATTR_BACKGROUND);
+
+ rStartCol = std::min<SCCOL>(rStartCol, aCol.size() - 1);
+ rEndCol = std::min<SCCOL>(rEndCol, aCol.size() - 1);
+
+ do
+ {
+ bExtend = false;
+
+ if (rEndRow < rDocument.MaxRow())
+ {
+ for (SCCOL nCol = rStartCol; nCol <= rEndCol; ++nCol)
+ {
+ const ScPatternAttr* pPattern = ColumnData(nCol).GetPattern(rEndRow + 1);
+ const SvxBrushItem* pBackground = &pPattern->GetItem(ATTR_BACKGROUND);
+ if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty() ||
+ pBackground != pDefBackground)
+ {
+ bExtend = true;
+ break;
+ }
+ }
+
+ if (bExtend)
+ ++rEndRow;
+ }
+ } while (bExtend);
+}
+
+OUString ScTable::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY )
+{
+ OUString aValue;
+
+ SCCOL nCol1 = rSource.aStart.Col();
+ SCROW nRow1 = rSource.aStart.Row();
+ SCCOL nCol2 = rSource.aEnd.Col();
+ SCROW nRow2 = rSource.aEnd.Row();
+ bool bOk = true;
+ tools::Long nIndex = 0;
+ sal_uInt64 nSrcCount = 0;
+ FillDir eFillDir = FILL_TO_BOTTOM;
+ if ( nEndX == nCol2 && nEndY == nRow2 ) // empty
+ bOk = false;
+ else if ( nEndX == nCol2 ) // to up / down
+ {
+ nCol2 = nCol1; // use only first column
+ nSrcCount = nRow2 - nRow1 + 1;
+ nIndex = static_cast<tools::Long>(nEndY) - nRow1; // can be negative
+ if ( nEndY >= nRow1 )
+ eFillDir = FILL_TO_BOTTOM;
+ else
+ eFillDir = FILL_TO_TOP;
+ }
+ else if ( nEndY == nRow2 ) // to left / right
+ {
+ nEndY = nRow2 = nRow1; // use only first row
+ nSrcCount = nCol2 - nCol1 + 1;
+ nIndex = static_cast<tools::Long>(nEndX) - nCol1; // can be negative
+ if ( nEndX >= nCol1 )
+ eFillDir = FILL_TO_RIGHT;
+ else
+ eFillDir = FILL_TO_LEFT;
+ }
+ else // direction not clear
+ bOk = false;
+
+ if ( bOk )
+ {
+ tools::Long nBegin = 0;
+ tools::Long nEnd = 0;
+ tools::Long nHidden = 0;
+ if (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP)
+ {
+ if (nEndY > nRow1)
+ {
+ nBegin = nRow2+1;
+ nEnd = nEndY;
+ }
+ else
+ {
+ nBegin = nEndY;
+ nEnd = nRow1 -1;
+ }
+
+ tools::Long nVisible = CountVisibleRows(nBegin, nEnd);
+ nHidden = nEnd + 1 - nBegin - nVisible;
+ }
+ else
+ {
+ if (nEndX > nCol1)
+ {
+ nBegin = nCol2+1;
+ nEnd = nEndX;
+ }
+ else
+ {
+ nBegin = nEndX;
+ nEnd = nCol1 -1;
+ }
+
+ tools::Long nVisible = CountVisibleCols(nBegin, nEnd);
+ nHidden = nEnd + 1 - nBegin - nVisible;
+ }
+ if (nHidden)
+ {
+ if (nIndex > 0)
+ nIndex = nIndex - nHidden;
+ else
+ nIndex = nIndex + nHidden;
+ }
+
+ FillCmd eFillCmd;
+ FillDateCmd eDateCmd;
+ double nInc;
+ tools::Duration aDurationInc;
+ sal_uInt16 nMinDigits;
+ ScUserListData* pListData = nullptr;
+ sal_uInt16 nListIndex;
+ bool bSkipOverlappedCells;
+ std::vector<sal_Int32> aNonOverlappedCellIdx;
+
+ // Todo: update this function to calculate with merged cell fills,
+ // after FillAnalyse / FillSeries fully handle them.
+ // Now FillAnalyse called as if there are filtered rows, so it will work in the old way.
+ FillAnalyse(nCol1, nRow1, nCol2, nRow2, eFillCmd, eDateCmd,
+ nInc, aDurationInc, nMinDigits, pListData, nListIndex,
+ true, bSkipOverlappedCells, aNonOverlappedCellIdx);
+
+ if ( pListData ) // user defined list
+ {
+ sal_uInt16 nListCount = pListData->GetSubCount();
+ if ( nListCount )
+ {
+ sal_uInt64 nSub = nSrcCount - 1; // nListIndex is from last source entry
+ while ( nIndex < sal::static_int_cast<tools::Long>(nSub) )
+ nIndex += nListCount;
+ sal_uInt64 nPos = ( nListIndex + nIndex - nSub ) % nListCount;
+ aValue = pListData->GetSubStr(sal::static_int_cast<sal_uInt16>(nPos));
+ }
+ }
+ else if ( eFillCmd == FILL_SIMPLE ) // fill with pattern/sample
+ {
+ tools::Long nPosIndex = nIndex;
+ while ( nPosIndex < 0 )
+ nPosIndex += nSrcCount;
+ sal_uInt64 nPos = nPosIndex % nSrcCount;
+ SCCOL nSrcX = nCol1;
+ SCROW nSrcY = nRow1;
+ if ( eFillDir == FILL_TO_TOP || eFillDir == FILL_TO_BOTTOM )
+ nSrcY = sal::static_int_cast<SCROW>( nSrcY + static_cast<SCROW>(nPos) );
+ else
+ nSrcX = sal::static_int_cast<SCCOL>( nSrcX + static_cast<SCCOL>(nPos) );
+
+ ScRefCellValue aCell = GetCellValue(nSrcX, nSrcY);
+ if (!aCell.isEmpty())
+ {
+ sal_Int32 nDelta;
+ if (nIndex >= 0)
+ nDelta = nIndex / nSrcCount;
+ else
+ nDelta = ( nIndex - nSrcCount + 1 ) / nSrcCount; // -1 -> -1
+
+ CellType eType = aCell.getType();
+ switch ( eType )
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ aValue = aCell.getString(&rDocument);
+
+ if ( !(nScFillModeMouseModifier & KEY_MOD1) )
+ {
+ sal_Int32 nVal;
+ sal_uInt16 nCellDigits = 0; // look at each source cell individually
+ short nFlag = lcl_DecompValueString( aValue, nVal, &nCellDigits );
+ if ( nFlag < 0 )
+ {
+ if (aValue == ScGlobal::GetOrdinalSuffix( nVal))
+ aValue = ScGlobal::GetOrdinalSuffix( nVal + nDelta);
+ aValue = lcl_ValueString( nVal + nDelta, nCellDigits ) + aValue;
+ }
+ else if ( nFlag > 0 )
+ {
+ sal_Int32 nNextValue;
+ if ( nVal < 0 )
+ nNextValue = nVal - nDelta;
+ else
+ nNextValue = nVal + nDelta;
+ if ( nFlag == 2 && nNextValue >= 0 ) // Put back the '+'
+ aValue += "+";
+ aValue += lcl_ValueString( nNextValue, nCellDigits );
+ }
+ }
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ sal_uInt32 nNumFmt = GetNumberFormat( nSrcX, nSrcY );
+ // overflow is possible...
+ double nVal = aCell.getDouble();
+ if ( !(nScFillModeMouseModifier & KEY_MOD1) )
+ {
+ const SvNumFormatType nFormatType = rDocument.GetFormatTable()->GetType(nNumFmt);
+ bool bPercentCell = (nFormatType == SvNumFormatType::PERCENT);
+ if (bPercentCell)
+ {
+ // tdf#89998 increment by 1% at a time
+ nVal += static_cast<double>(nDelta) * 0.01;
+ }
+ else if (nVal == 0.0 || nVal == 1.0)
+ {
+ bool bBooleanCell = (nFormatType == SvNumFormatType::LOGICAL);
+ if (!bBooleanCell)
+ nVal += static_cast<double>(nDelta);
+ }
+ else
+ {
+ nVal += static_cast<double>(nDelta);
+ }
+ }
+
+ const Color* pColor;
+ rDocument.GetFormatTable()->GetOutputString( nVal, nNumFmt, aValue, &pColor );
+ }
+ break;
+ // not for formulas
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ else if ( eFillCmd == FILL_LINEAR || eFillCmd == FILL_DATE ) // values
+ {
+ bool bValueOk;
+ double nStart;
+ sal_Int32 nVal = 0;
+ short nHeadNoneTail = 0;
+ ScRefCellValue aCell = GetCellValue(nCol1, nRow1);
+ if (!aCell.isEmpty())
+ {
+ CellType eType = aCell.getType();
+ switch ( eType )
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ aValue = aCell.getString(&rDocument);
+ nHeadNoneTail = lcl_DecompValueString( aValue, nVal );
+ if ( nHeadNoneTail )
+ nStart = static_cast<double>(nVal);
+ else
+ nStart = 0.0;
+ }
+ break;
+ case CELLTYPE_VALUE:
+ nStart = aCell.getDouble();
+ break;
+ case CELLTYPE_FORMULA:
+ nStart = aCell.getFormula()->GetValue();
+ break;
+ default:
+ nStart = 0.0;
+ }
+ }
+ else
+ nStart = 0.0;
+ if ( eFillCmd == FILL_LINEAR )
+ {
+ if (aDurationInc)
+ {
+ bool bOverflow;
+ tools::Duration aDuration( aDurationInc.Mult( nIndex, bOverflow));
+ bValueOk = SubTotal::SafePlus( nStart, aDuration.GetInDays()) && !bOverflow;
+ }
+ else
+ {
+ double nAdd = nInc;
+ bValueOk = ( SubTotal::SafeMult( nAdd, static_cast<double>(nIndex) ) &&
+ SubTotal::SafePlus( nStart, nAdd ) );
+ }
+ }
+ else // date
+ {
+ bValueOk = true;
+ sal_uInt16 nDayOfMonth = 0;
+ if ( nIndex < 0 )
+ {
+ nIndex = -nIndex;
+ nInc = -nInc;
+ }
+ for (tools::Long i=0; i<nIndex; i++)
+ IncDate( nStart, nDayOfMonth, nInc, eDateCmd );
+ }
+
+ if (bValueOk)
+ {
+ if ( nHeadNoneTail )
+ {
+ if ( nHeadNoneTail < 0 )
+ {
+ if (aValue == ScGlobal::GetOrdinalSuffix( nVal))
+ aValue = ScGlobal::GetOrdinalSuffix( static_cast<sal_Int32>(nStart) );
+
+ aValue = lcl_ValueString( static_cast<sal_Int32>(nStart), nMinDigits ) + aValue;
+ }
+ else
+ {
+ if ( nHeadNoneTail == 2 && nStart >= 0 ) // Put back the '+'
+ aValue += "+";
+ aValue += lcl_ValueString( static_cast<sal_Int32>(nStart), nMinDigits );
+ }
+ }
+ else
+ {
+ //TODO: get number format according to Index?
+ const Color* pColor;
+ sal_uInt32 nNumFmt = GetNumberFormat( nCol1, nRow1 );
+ rDocument.GetFormatTable()->GetOutputString( nStart, nNumFmt, aValue, &pColor );
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("GetAutoFillPreview: invalid mode");
+ }
+ }
+
+ return aValue;
+}
+
+void ScTable::IncDate(double& rVal, sal_uInt16& nDayOfMonth, double nStep, FillDateCmd eCmd)
+{
+ if (eCmd == FILL_DAY)
+ {
+ rVal += nStep;
+ return;
+ }
+
+ // class Date limits
+ const sal_uInt16 nMinYear = 1583;
+ const sal_uInt16 nMaxYear = 9956;
+
+ tools::Long nInc = static_cast<tools::Long>(nStep); // upper/lower limits ?
+ Date aNullDate = rDocument.GetFormatTable()->GetNullDate();
+ Date aDate = aNullDate;
+ aDate.AddDays(rVal);
+ switch (eCmd)
+ {
+ case FILL_WEEKDAY:
+ {
+ aDate.AddDays(nInc);
+ DayOfWeek eWeekDay = aDate.GetDayOfWeek();
+ if (nInc >= 0)
+ {
+ if (eWeekDay == SATURDAY)
+ aDate.AddDays(2);
+ else if (eWeekDay == SUNDAY)
+ aDate.AddDays(1);
+ }
+ else
+ {
+ if (eWeekDay == SATURDAY)
+ aDate.AddDays(-1);
+ else if (eWeekDay == SUNDAY)
+ aDate.AddDays(-2);
+ }
+ }
+ break;
+ case FILL_MONTH:
+ case FILL_END_OF_MONTH:
+ {
+ if ( nDayOfMonth == 0 )
+ nDayOfMonth = aDate.GetDay(); // init
+ tools::Long nMonth = aDate.GetMonth();
+ tools::Long nYear = aDate.GetYear();
+
+ nMonth += nInc;
+
+ if (nInc >= 0)
+ {
+ if (nMonth > 12)
+ {
+ tools::Long nYAdd = (nMonth-1) / 12;
+ nMonth -= nYAdd * 12;
+ nYear += nYAdd;
+ }
+ }
+ else
+ {
+ if (nMonth < 1)
+ {
+ tools::Long nYAdd = 1 - nMonth / 12; // positive
+ nMonth += nYAdd * 12;
+ nYear -= nYAdd;
+ }
+ }
+
+ if ( nYear < nMinYear )
+ aDate = Date( 1,1, nMinYear );
+ else if ( nYear > nMaxYear )
+ aDate = Date( 31,12, nMaxYear );
+ else
+ {
+ aDate.SetMonth(static_cast<sal_uInt16>(nMonth));
+ aDate.SetYear(static_cast<sal_uInt16>(nYear));
+ if (eCmd == FILL_END_OF_MONTH)
+ {
+ aDate.SetDay(Date::GetDaysInMonth(nMonth, nYear));
+ }
+ else
+ {
+ aDate.SetDay(std::min(Date::GetDaysInMonth(nMonth, nYear), nDayOfMonth));
+ }
+ }
+ }
+ break;
+ case FILL_YEAR:
+ {
+ tools::Long nYear = aDate.GetYear();
+ nYear += nInc;
+ if ( nYear < nMinYear )
+ aDate = Date( 1,1, nMinYear );
+ else if ( nYear > nMaxYear )
+ aDate = Date( 31,12, nMaxYear );
+ else
+ aDate.SetYear(static_cast<sal_uInt16>(nYear));
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ rVal = aDate - aNullDate;
+}
+
+namespace {
+
+bool HiddenRowColumn(const ScTable* pTable, SCCOLROW nRowColumn, bool bVertical, SCCOLROW& rLastPos)
+{
+ bool bHidden = false;
+ if(bVertical)
+ {
+ SCROW nLast;
+ bHidden = pTable->RowHidden(nRowColumn, nullptr, &nLast);
+ rLastPos = nLast;
+ }
+ else
+ {
+ SCCOL nLast;
+ bHidden = pTable->ColHidden(static_cast<SCCOL>(nRowColumn), nullptr, &nLast);
+ rLastPos = nLast;
+ }
+ return bHidden;
+}
+
+}
+
+void ScTable::FillFormulaVertical(
+ const ScFormulaCell& rSrcCell,
+ SCCOLROW& rInner, SCCOL nCol, SCROW nRow1, SCROW nRow2,
+ ScProgress* pProgress, sal_uInt64& rProgress )
+{
+ // rInner is the row position when filling vertically. Also, when filling
+ // across hidden regions, it may create multiple dis-jointed spans of
+ // formula cells.
+
+ bool bHidden = false;
+ SCCOLROW nHiddenLast = -1;
+
+ SCCOLROW nRowStart = -1, nRowEnd = -1;
+ std::vector<sc::RowSpan> aSpans;
+ PutInOrder(nRow1, nRow2);
+ for (rInner = nRow1; rInner <= nRow2; ++rInner)
+ {
+ if (rInner > nHiddenLast)
+ bHidden = HiddenRowColumn(this, rInner, true, nHiddenLast);
+
+ if (bHidden)
+ {
+ if (nRowStart >= 0)
+ {
+ nRowEnd = rInner - 1;
+ aSpans.emplace_back(nRowStart, nRowEnd);
+ nRowStart = -1;
+ }
+ rInner = nHiddenLast;
+ continue;
+ }
+
+ if (nRowStart < 0)
+ nRowStart = rInner;
+ }
+
+ if (nRowStart >= 0)
+ {
+ nRowEnd = rInner - 1;
+ aSpans.emplace_back(nRowStart, nRowEnd);
+ }
+
+ if (aSpans.empty())
+ return;
+
+ aCol[nCol].DeleteRanges(aSpans, InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME | InsertDeleteFlags::STRING | InsertDeleteFlags::FORMULA | InsertDeleteFlags::OUTLINE);
+ aCol[nCol].CloneFormulaCell(rSrcCell, sc::CellTextAttr(), aSpans);
+
+ auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(rDocument);
+ sc::StartListeningContext aStartCxt(rDocument, pSet);
+ sc::EndListeningContext aEndCxt(rDocument, pSet);
+
+ SCROW nStartRow = aSpans.front().mnRow1;
+ SCROW nEndRow = aSpans.back().mnRow2;
+ aCol[nCol].EndListeningFormulaCells(aEndCxt, nStartRow, nEndRow, &nStartRow, &nEndRow);
+ aCol[nCol].StartListeningFormulaCells(aStartCxt, aEndCxt, nStartRow, nEndRow);
+
+ for (const auto& rSpan : aSpans)
+ aCol[nCol].SetDirty(rSpan.mnRow1, rSpan.mnRow2, ScColumn::BROADCAST_NONE);
+
+ rProgress += nRow2 - nRow1 + 1;
+ if (pProgress)
+ pProgress->SetStateOnPercent(rProgress);
+}
+
+void ScTable::FillSeriesSimple(
+ const ScCellValue& rSrcCell, SCCOLROW& rInner, SCCOLROW nIMin, SCCOLROW nIMax,
+ const SCCOLROW& rCol, const SCCOLROW& rRow, bool bVertical, ScProgress* pProgress, sal_uInt64& rProgress )
+{
+ bool bHidden = false;
+ SCCOLROW nHiddenLast = -1;
+
+ if (bVertical)
+ {
+ switch (rSrcCell.getType())
+ {
+ case CELLTYPE_FORMULA:
+ {
+ FillFormulaVertical(
+ *rSrcCell.getFormula(), rInner, rCol, nIMin, nIMax, pProgress, rProgress);
+ }
+ break;
+ default:
+ {
+ for (rInner = nIMin; rInner <= nIMax; ++rInner)
+ {
+ if (rInner > nHiddenLast)
+ bHidden = HiddenRowColumn(this, rInner, bVertical, nHiddenLast);
+
+ if (bHidden)
+ {
+ rInner = nHiddenLast;
+ continue;
+ }
+
+ ScAddress aDestPos(rCol, rRow, nTab);
+ rSrcCell.commit(aCol[rCol], aDestPos.Row());
+ }
+ rProgress += nIMax - nIMin + 1;
+ if (pProgress)
+ pProgress->SetStateOnPercent(rProgress);
+ }
+ }
+ }
+ else
+ {
+ switch (rSrcCell.getType())
+ {
+ case CELLTYPE_FORMULA:
+ {
+ for (rInner = nIMin; rInner <= nIMax; ++rInner)
+ {
+ if (rInner > nHiddenLast)
+ bHidden = HiddenRowColumn(this, rInner, bVertical, nHiddenLast);
+
+ if (bHidden)
+ continue;
+
+ FillFormula(rSrcCell.getFormula(), rCol, rRow, (rInner == nIMax));
+ if (pProgress)
+ pProgress->SetStateOnPercent(++rProgress);
+ }
+ }
+ break;
+ default:
+ {
+ for (rInner = nIMin; rInner <= nIMax; ++rInner)
+ {
+ if (rInner > nHiddenLast)
+ bHidden = HiddenRowColumn(this, rInner, bVertical, nHiddenLast);
+
+ if (bHidden)
+ continue;
+
+ ScAddress aDestPos(rCol, rRow, nTab);
+ rSrcCell.commit(aCol[rCol], aDestPos.Row());
+ }
+ rProgress += nIMax - nIMin + 1;
+ if (pProgress)
+ pProgress->SetStateOnPercent(rProgress);
+ }
+ }
+ }
+}
+
+void ScTable::FillAutoSimple(
+ SCCOLROW nISrcStart, SCCOLROW nISrcEnd, SCCOLROW nIStart, SCCOLROW nIEnd,
+ SCCOLROW& rInner, const SCCOLROW& rCol, const SCCOLROW& rRow, sal_uInt64 nActFormCnt,
+ sal_uInt64 nMaxFormCnt, bool bHasFiltered, bool bVertical, bool bPositive,
+ ScProgress* pProgress, sal_uInt64& rProgress )
+{
+ SCCOLROW nSource = nISrcStart;
+ double nDelta;
+ if ( nScFillModeMouseModifier & KEY_MOD1 )
+ nDelta = 0.0;
+ else if ( bPositive )
+ nDelta = 1.0;
+ else
+ nDelta = -1.0;
+ sal_uInt64 nFormulaCounter = nActFormCnt;
+ bool bGetCell = true;
+ bool bBooleanCell = false;
+ bool bPercentCell = false;
+ sal_uInt16 nCellDigits = 0;
+ short nHeadNoneTail = 0;
+ sal_Int32 nStringValue = 0;
+ OUString aValue;
+ ScCellValue aSrcCell;
+ bool bIsOrdinalSuffix = false;
+
+ bool bColHidden = false, bRowHidden = false;
+ SCCOL nColHiddenFirst = rDocument.MaxCol();
+ SCCOL nColHiddenLast = -1;
+ SCROW nRowHiddenFirst = rDocument.MaxRow();
+ SCROW nRowHiddenLast = -1;
+
+ rInner = nIStart;
+ while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
+ {
+ if (bPositive)
+ {
+ if (rCol > nColHiddenLast)
+ bColHidden = ColHidden(rCol, nullptr, &nColHiddenLast);
+ if (rRow > nRowHiddenLast)
+ bRowHidden = RowHidden(rRow, nullptr, &nRowHiddenLast);
+ }
+ else
+ {
+ if (rCol < nColHiddenFirst)
+ bColHidden = ColHidden(rCol, &nColHiddenFirst);
+ if (rRow < nRowHiddenFirst)
+ bRowHidden = RowHidden(rRow, &nRowHiddenFirst);
+ }
+
+ if (!bColHidden && !bRowHidden)
+ {
+ if ( bGetCell )
+ {
+ if (bVertical) // rInner&:=nRow, rOuter&:=nCol
+ {
+ aSrcCell = GetCellValue(rCol, nSource);
+ if (nISrcStart == nISrcEnd && aSrcCell.getType() == CELLTYPE_FORMULA)
+ {
+ FillFormulaVertical(*aSrcCell.getFormula(), rInner, rCol, nIStart, nIEnd, pProgress, rProgress);
+ return;
+ }
+ const SvNumFormatType nFormatType = rDocument.GetFormatTable()->GetType(
+ GetColumnData(rCol).GetNumberFormat( rDocument.GetNonThreadedContext(), nSource));
+ bBooleanCell = (nFormatType == SvNumFormatType::LOGICAL);
+ bPercentCell = (nFormatType == SvNumFormatType::PERCENT);
+
+ }
+ else // rInner&:=nCol, rOuter&:=nRow
+ {
+ aSrcCell = GetCellValue(nSource, rRow);
+ const SvNumFormatType nFormatType = rDocument.GetFormatTable()->GetType(
+ GetColumnData(nSource).GetNumberFormat( rDocument.GetNonThreadedContext(), rRow));
+ bBooleanCell = (nFormatType == SvNumFormatType::LOGICAL);
+ bPercentCell = (nFormatType == SvNumFormatType::PERCENT);
+ }
+
+ bGetCell = false;
+ if (!aSrcCell.isEmpty())
+ {
+ switch (aSrcCell.getType())
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ if (aSrcCell.getType() == CELLTYPE_STRING)
+ aValue = aSrcCell.getSharedString()->getString();
+ else
+ aValue = ScEditUtil::GetString(*aSrcCell.getEditText(), &rDocument);
+ if ( !(nScFillModeMouseModifier & KEY_MOD1) && !bHasFiltered )
+ {
+ nCellDigits = 0; // look at each source cell individually
+ nHeadNoneTail = lcl_DecompValueString(
+ aValue, nStringValue, &nCellDigits );
+
+ bIsOrdinalSuffix = aValue ==
+ ScGlobal::GetOrdinalSuffix(nStringValue);
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+
+ switch (aSrcCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ double fVal;
+ if (bBooleanCell && ((fVal = aSrcCell.getDouble()) == 0.0 || fVal == 1.0))
+ aCol[rCol].SetValue(rRow, aSrcCell.getDouble());
+ else if(bPercentCell)
+ aCol[rCol].SetValue(rRow, aSrcCell.getDouble() + nDelta * 0.01); // tdf#89998 increment by 1% at a time
+ else
+ aCol[rCol].SetValue(rRow, aSrcCell.getDouble() + nDelta);
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ if ( nHeadNoneTail )
+ {
+ sal_Int32 nNextValue;
+ if (nStringValue < 0)
+ nNextValue = nStringValue - static_cast<sal_Int32>(nDelta);
+ else
+ nNextValue = nStringValue + static_cast<sal_Int32>(nDelta);
+
+ if ( nHeadNoneTail < 0 )
+ {
+ setSuffixCell(
+ aCol[rCol], rRow,
+ nNextValue, nCellDigits, aValue,
+ aSrcCell.getType(), bIsOrdinalSuffix);
+ }
+ else
+ {
+ OUString aStr;
+ if (nHeadNoneTail == 2 && nNextValue >= 0) // Put back the '+'
+ aStr = aValue + "+" + lcl_ValueString(nNextValue, nCellDigits);
+ else
+ aStr = aValue + lcl_ValueString(nNextValue, nCellDigits);
+
+ aCol[rCol].SetRawString(rRow, aStr);
+ }
+ }
+ else
+ aSrcCell.commit(aCol[rCol], rRow);
+
+ break;
+ case CELLTYPE_FORMULA :
+ FillFormula(
+ aSrcCell.getFormula(), rCol, rRow, (rInner == nIEnd));
+ if (nFormulaCounter - nActFormCnt > nMaxFormCnt)
+ nMaxFormCnt = nFormulaCounter - nActFormCnt;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (nSource == nISrcEnd)
+ {
+ if ( nSource != nISrcStart )
+ { // More than one source cell
+ nSource = nISrcStart;
+ bGetCell = true;
+ }
+ if ( !(nScFillModeMouseModifier & KEY_MOD1) )
+ {
+ if ( bPositive )
+ nDelta += 1.0;
+ else
+ nDelta -= 1.0;
+ }
+ nFormulaCounter = nActFormCnt;
+ }
+ else if (bPositive)
+ {
+ ++nSource;
+ bGetCell = true;
+ }
+ else
+ {
+ --nSource;
+ bGetCell = true;
+ }
+ }
+
+ if (rInner == nIEnd)
+ break;
+ if (bPositive)
+ ++rInner;
+ else
+ --rInner;
+
+ // Progress in inner loop only for expensive cells,
+ // and even then not individually for each one
+
+ ++rProgress;
+ if ( pProgress && (aSrcCell.getType() == CELLTYPE_FORMULA || aSrcCell.getType() == CELLTYPE_EDIT) )
+ pProgress->SetStateOnPercent( rProgress );
+
+ }
+ if (pProgress)
+ pProgress->SetStateOnPercent( rProgress );
+}
+
+namespace
+{
+// Target value exceeded?
+inline bool isOverflow( const double& rVal, const double& rMax, const double& rStep,
+ const double& rStartVal, FillCmd eFillCmd )
+{
+ switch (eFillCmd)
+ {
+ case FILL_LINEAR:
+ case FILL_DATE:
+ if (rStep >= 0.0)
+ return rVal > rMax;
+ else
+ return rVal < rMax;
+ case FILL_GROWTH:
+ if (rStep > 0.0)
+ {
+ if (rStep >= 1.0)
+ {
+ // Growing away from zero, including zero growth (1.0).
+ if (rVal >= 0.0)
+ return rVal > rMax;
+ else
+ return rVal < rMax;
+ }
+ else
+ {
+ // Shrinking towards zero.
+ if (rVal >= 0.0)
+ return rVal < rMax;
+ else
+ return rVal > rMax;
+ }
+ }
+ else if (rStep < 0.0)
+ {
+ // Alternating positive and negative values.
+ if (rStep <= -1.0)
+ {
+ // Growing away from zero, including zero growth (-1.0).
+ if (rVal >= 0.0)
+ {
+ if (rMax >= 0.0)
+ return rVal > rMax;
+ else
+ // Regard negative rMax as lower limit, which will
+ // be reached only by a negative rVal.
+ return false;
+ }
+ else
+ {
+ if (rMax <= 0.0)
+ return rVal < rMax;
+ else
+ // Regard positive rMax as upper limit, which will
+ // be reached only by a positive rVal.
+ return false;
+ }
+ }
+ else
+ {
+ // Shrinking towards zero.
+ if (rVal >= 0.0)
+ return rVal < rMax;
+ else
+ return rVal > rMax;
+ }
+ }
+ else // if (rStep == 0.0)
+ {
+ // All values become zero.
+ // Corresponds with bEntireArea in FillSeries().
+ if (rMax > 0.0)
+ return rMax < rStartVal;
+ else if (rMax < 0.0)
+ return rStartVal < rMax;
+ }
+ break;
+ default:
+ assert(!"eFillCmd");
+ }
+ return false;
+}
+}
+
+void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt64 nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd,
+ double nStepValue, const tools::Duration& rDurationStep,
+ double nMaxValue, sal_uInt16 nArgMinDigits,
+ bool bAttribs, ScProgress* pProgress,
+ bool bSkipOverlappedCells, std::vector<sal_Int32>* pNonOverlappedCellIdx )
+{
+ // The term 'inner' here refers to the loop in the filling direction i.e.
+ // when filling vertically, the inner position is the row position whereas
+ // when filling horizontally the column position becomes the inner
+ // position. The term 'outer' refers to the column position when filling
+ // vertically, or the row position when filling horizontally. The fill is
+ // performed once in each 'outer' position e.g. when filling vertically,
+ // we perform the fill once in each column.
+
+ // Detect direction
+
+ bool bVertical = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP);
+ bool bPositive = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_RIGHT);
+
+ SCCOLROW nCol = 0;
+ SCCOLROW nRow = 0;
+ SCCOLROW& rInner = bVertical ? nRow : nCol; // loop variables
+ SCCOLROW& rOuter = bVertical ? nCol : nRow;
+ SCCOLROW nOStart;
+ SCCOLROW nOEnd;
+ SCCOLROW nIStart;
+ SCCOLROW nIEnd;
+ SCCOLROW nISource;
+ ScRange aFillRange;
+ sal_uInt64 nFillerCount;
+ std::vector<bool> aIsNonEmptyCell;
+
+ if (bVertical)
+ {
+ nFillerCount = (nRow2 - nRow1) + 1;
+ nFillCount += (nRow2 - nRow1);
+ if (nFillCount == 0)
+ return;
+ nOStart = nCol1;
+ nOEnd = nCol2;
+ if (bPositive)
+ {
+ // downward fill
+ nISource = nRow1; // top row of the source range.
+ nIStart = nRow1 + 1; // first row where we start filling.
+ nIEnd = nRow1 + nFillCount;
+ aFillRange = ScRange(nCol1, nRow1 + 1, nTab, nCol2, nRow1 + nFillCount, nTab);
+ }
+ else
+ {
+ // upward fill
+ nISource = nRow2;
+ nIStart = nRow2 - 1;
+ nIEnd = nRow2 - nFillCount;
+ aFillRange = ScRange(nCol1, nRow2 -1, nTab, nCol2, nRow2 - nFillCount, nTab);
+ }
+ }
+ else
+ {
+ nFillerCount = (nCol2 - nCol1) + 1;
+ nFillCount += (nCol2 - nCol1);
+ if (nFillCount == 0)
+ return;
+ nOStart = nRow1;
+ nOEnd = nRow2;
+ if (bPositive)
+ {
+ // to the right
+ nISource = nCol1;
+ nIStart = nCol1 + 1;
+ nIEnd = nCol1 + nFillCount;
+ aFillRange = ScRange(nCol1 + 1, nRow1, nTab, nCol1 + nFillCount, nRow2, nTab);
+ }
+ else
+ {
+ // to the left
+ nISource = nCol2;
+ nIStart = nCol2 - 1;
+ nIEnd = nCol2 - nFillCount;
+ aFillRange = ScRange(nCol2 - 1, nRow1, nTab, nCol2 - nFillCount, nRow2, nTab);
+ }
+ }
+
+ SCCOLROW nIMin = nIStart;
+ SCCOLROW nIMax = nIEnd;
+ PutInOrder(nIMin,nIMax);
+
+ const bool bIsFiltered = IsDataFiltered(aFillRange);
+ bool bEntireArea = (!bIsFiltered && eFillCmd == FILL_SIMPLE);
+ if (!bIsFiltered && !bEntireArea && (eFillCmd == FILL_LINEAR || eFillCmd == FILL_GROWTH)
+ && (nOEnd - nOStart == 0))
+ {
+ // For the usual case of one col/row determine if a numeric series is
+ // at least as long as the area to be filled and does not end earlier,
+ // so we can treat it as entire area for performance reasons at least
+ // in the vertical case.
+ // This is not exact in case of merged cell fills with skipping overlapped parts, but
+ // it is still a good upper estimation.
+ ScCellValue aSrcCell;
+ if (bVertical)
+ aSrcCell = GetCellValue(static_cast<SCCOL>(nOStart), static_cast<SCROW>(nISource));
+ else
+ aSrcCell = GetCellValue(static_cast<SCCOL>(nISource), static_cast<SCROW>(nOStart));
+ // Same logic as for the actual series.
+ if (!aSrcCell.isEmpty() && (aSrcCell.getType() == CELLTYPE_VALUE || aSrcCell.getType() == CELLTYPE_FORMULA))
+ {
+ double nStartVal;
+ if (aSrcCell.getType() == CELLTYPE_VALUE)
+ nStartVal = aSrcCell.getDouble();
+ else
+ nStartVal = aSrcCell.getFormula()->GetValue();
+ if (eFillCmd == FILL_LINEAR)
+ {
+ if (nStepValue == 0.0)
+ bEntireArea = (nStartVal <= nMaxValue); // fill with same value
+ else if (((nMaxValue - nStartVal) / nStepValue) >= nFillCount)
+ bEntireArea = true;
+ }
+ else if (eFillCmd == FILL_GROWTH)
+ {
+ if (nStepValue == 1.0)
+ bEntireArea = (nStartVal <= nMaxValue); // fill with same value
+ else if (nStepValue == -1.0)
+ bEntireArea = (fabs(nStartVal) <= fabs(nMaxValue)); // fill with alternating value
+ else if (nStepValue == 0.0)
+ bEntireArea = (nStartVal == 0.0
+ || (nStartVal < 0.0 && nMaxValue >= 0.0)
+ || (nStartVal > 0.0 && nMaxValue <= 0.0)); // fill with 0.0
+ }
+ }
+ }
+ if (bEntireArea)
+ {
+ InsertDeleteFlags nDel = (bAttribs ? InsertDeleteFlags::AUTOFILL :
+ (InsertDeleteFlags::AUTOFILL & InsertDeleteFlags::CONTENTS));
+ if (bVertical)
+ DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), nDel);
+ else
+ DeleteArea(static_cast<SCCOL>(nIMin), nRow1, static_cast<SCCOL>(nIMax), nRow2, nDel);
+ }
+
+ sal_uInt64 nProgress = 0;
+ if (pProgress)
+ nProgress = pProgress->GetState();
+
+ // Perform the fill once per each 'outer' position i.e. one per column
+ // when filling vertically.
+
+ for (rOuter = nOStart; rOuter <= nOEnd; rOuter++)
+ {
+ rInner = nISource;
+
+ CreateColumnIfNotExists(nCol);
+
+ // Source cell value. We need to clone the value since it may be inserted repeatedly.
+ ScCellValue aSrcCell = GetCellValue(nCol, static_cast<SCROW>(nRow));
+
+ // Maybe another source cell need to be searched, if the fill is going through merged cells,
+ // where overlapped parts does not contain any information, so they can be skipped.
+ if (bSkipOverlappedCells)
+ {
+ // create a vector to make it easier to decide if a cell need to be filled, or skipped.
+ aIsNonEmptyCell.resize(nFillerCount, false);
+
+ SCCOLROW nFirstValueIdx;
+ if (bPositive)
+ {
+ nFirstValueIdx = nISource + (*pNonOverlappedCellIdx)[0];
+ for (auto i : (*pNonOverlappedCellIdx))
+ aIsNonEmptyCell[i] = true;
+ }
+ else
+ {
+ nFirstValueIdx = nISource - (nFillerCount - 1 - (*pNonOverlappedCellIdx).back());
+ for (auto i : (*pNonOverlappedCellIdx))
+ aIsNonEmptyCell[nFillerCount - 1 - i] = true;
+ }
+
+ //Set the real source cell
+ if (bVertical)
+ aSrcCell = GetCellValue(nOStart, static_cast<SCROW>(nFirstValueIdx));
+ else
+ aSrcCell = GetCellValue(nFirstValueIdx, static_cast<SCROW>(nOStart));
+ }
+
+ const ScPatternAttr* pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nRow));
+ const ScCondFormatItem& rCondFormatItem = pSrcPattern->GetItem(ATTR_CONDITIONAL);
+ const ScCondFormatIndexes& rCondFormatIndex = rCondFormatItem.GetCondFormatData();
+
+ if (bAttribs)
+ {
+ if (bVertical)
+ {
+ // If entire area (not filtered and simple fill) use the faster
+ // method, else hidden cols/rows should be skipped and series
+ // fill needs to determine the end row dynamically.
+ if (bEntireArea)
+ {
+ SetPatternAreaCondFormat( nCol, static_cast<SCROW>(nIMin),
+ static_cast<SCROW>(nIMax), *pSrcPattern, rCondFormatIndex);
+ }
+ else if (eFillCmd == FILL_SIMPLE)
+ {
+ assert(bIsFiltered);
+ for(SCROW nAtRow = static_cast<SCROW>(nIMin); nAtRow <= static_cast<SCROW>(nIMax); ++nAtRow)
+ {
+ if(!RowHidden(nAtRow))
+ {
+ SetPatternAreaCondFormat( nCol, nAtRow, nAtRow, *pSrcPattern, rCondFormatIndex);
+ }
+ }
+
+ }
+ }
+ else if (bEntireArea || eFillCmd == FILL_SIMPLE)
+ {
+ for (SCCOL nAtCol = static_cast<SCCOL>(nIMin); nAtCol <= sal::static_int_cast<SCCOL>(nIMax); nAtCol++)
+ {
+ if(!ColHidden(nAtCol))
+ {
+ SetPatternAreaCondFormat( nAtCol, nRow, nRow, *pSrcPattern, rCondFormatIndex);
+ }
+ }
+ }
+ }
+
+ if (!aSrcCell.isEmpty())
+ {
+ CellType eCellType = aSrcCell.getType();
+
+ if (eFillCmd == FILL_SIMPLE) // copy
+ {
+ FillSeriesSimple(aSrcCell, rInner, nIMin, nIMax, nCol, nRow, bVertical, pProgress, nProgress);
+ }
+ else if (eCellType == CELLTYPE_VALUE || eCellType == CELLTYPE_FORMULA)
+ {
+ const double nStartVal = (eCellType == CELLTYPE_VALUE ? aSrcCell.getDouble() :
+ aSrcCell.getFormula()->GetValue());
+ double nVal = nStartVal;
+ tools::Long nIndex = 0;
+
+ bool bError = false;
+ bool bOverflow = false;
+ bool bNonEmpty = true;
+
+ sal_uInt16 nDayOfMonth = 0;
+ sal_Int32 nFillerIdx = 0;
+ if (bSkipOverlappedCells && !aIsNonEmptyCell[0])
+ --nIndex;
+ rInner = nIStart;
+ while (true)
+ {
+ if (bSkipOverlappedCells)
+ {
+ nFillerIdx = (nFillerIdx + 1) % nFillerCount;
+ bNonEmpty = aIsNonEmptyCell[nFillerIdx];
+ }
+
+ if(!ColHidden(nCol) && !RowHidden(nRow))
+ {
+ if (!bError && bNonEmpty)
+ {
+ switch (eFillCmd)
+ {
+ case FILL_LINEAR:
+ {
+ // use multiplication instead of repeated addition
+ // to avoid accumulating rounding errors
+ nVal = nStartVal;
+ if (rDurationStep)
+ {
+ tools::Duration aDuration( rDurationStep.Mult( ++nIndex, bError));
+ bError |= !SubTotal::SafePlus( nVal, aDuration.GetInDays());
+ }
+ else
+ {
+ double nAdd = nStepValue;
+ if ( !SubTotal::SafeMult( nAdd, static_cast<double>(++nIndex) ) ||
+ !SubTotal::SafePlus( nVal, nAdd ) )
+ bError = true;
+ }
+ }
+ break;
+ case FILL_GROWTH:
+ if (!SubTotal::SafeMult(nVal, nStepValue))
+ bError = true;
+ break;
+ case FILL_DATE:
+ if (fabs(nVal) > D_MAX_LONG_)
+ bError = true;
+ else
+ IncDate(nVal, nDayOfMonth, nStepValue, eFillDateCmd);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (!bError)
+ bOverflow = isOverflow( nVal, nMaxValue, nStepValue, nStartVal, eFillCmd);
+ }
+
+ CreateColumnIfNotExists(nCol);
+ if (bError)
+ aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::NoValue);
+ else if (!bOverflow && bNonEmpty)
+ aCol[nCol].SetValue(static_cast<SCROW>(nRow), nVal);
+
+ if (bAttribs && !bEntireArea && !bOverflow)
+ SetPatternAreaCondFormat( nCol, nRow, nRow, *pSrcPattern, rCondFormatIndex);
+ }
+
+ if (rInner == nIEnd || bOverflow)
+ break;
+ if (bPositive)
+ {
+ ++rInner;
+ }
+ else
+ {
+ --rInner;
+ }
+ }
+ nProgress += nIMax - nIMin + 1;
+ if(pProgress)
+ pProgress->SetStateOnPercent( nProgress );
+ }
+ else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT)
+ {
+ if ( nStepValue >= 0 )
+ {
+ if ( nMaxValue >= double(LONG_MAX) )
+ nMaxValue = double(LONG_MAX) - 1;
+ }
+ else
+ {
+ if ( nMaxValue <= double(LONG_MIN) )
+ nMaxValue = double(LONG_MIN) + 1;
+ }
+ OUString aValue;
+ if (eCellType == CELLTYPE_STRING)
+ aValue = aSrcCell.getSharedString()->getString();
+ else
+ aValue = ScEditUtil::GetString(*aSrcCell.getEditText(), &rDocument);
+ sal_Int32 nStringValue;
+ sal_uInt16 nMinDigits = nArgMinDigits;
+ short nHeadNoneTail = lcl_DecompValueString( aValue, nStringValue, &nMinDigits );
+ if ( nHeadNoneTail )
+ {
+ const double nStartVal = static_cast<double>(nStringValue);
+ double nVal = nStartVal;
+ tools::Long nIndex = 0;
+ bool bError = false;
+ bool bOverflow = false;
+ bool bNonEmpty = true;
+
+ bool bIsOrdinalSuffix = aValue == ScGlobal::GetOrdinalSuffix(
+ static_cast<sal_Int32>(nStartVal));
+
+ sal_Int32 nFillerIdx = 0;
+ if (bSkipOverlappedCells && !aIsNonEmptyCell[0])
+ --nIndex;
+ rInner = nIStart;
+ while (true)
+ {
+ if (bSkipOverlappedCells)
+ {
+ nFillerIdx = (nFillerIdx + 1) % nFillerCount;
+ bNonEmpty = aIsNonEmptyCell[nFillerIdx];
+ }
+ if(!ColHidden(nCol) && !RowHidden(nRow))
+ {
+ if (!bError && bNonEmpty)
+ {
+ switch (eFillCmd)
+ {
+ case FILL_LINEAR:
+ {
+ // use multiplication instead of repeated addition
+ // to avoid accumulating rounding errors
+ nVal = nStartVal;
+ if (rDurationStep)
+ {
+ tools::Duration aDuration( rDurationStep.Mult( ++nIndex, bError));
+ bError |= !SubTotal::SafePlus( nVal, aDuration.GetInDays());
+ }
+ else
+ {
+ double nAdd = nStepValue;
+ if ( !SubTotal::SafeMult( nAdd, static_cast<double>(++nIndex) ) ||
+ !SubTotal::SafePlus( nVal, nAdd ) )
+ bError = true;
+ }
+ }
+ break;
+ case FILL_GROWTH:
+ if (!SubTotal::SafeMult(nVal, nStepValue))
+ bError = true;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (!bError)
+ bOverflow = isOverflow( nVal, nMaxValue, nStepValue, nStartVal, eFillCmd);
+ }
+
+ if (bError)
+ aCol[nCol].SetError(static_cast<SCROW>(nRow), FormulaError::NoValue);
+ else if (!bOverflow && bNonEmpty)
+ {
+ nStringValue = static_cast<sal_Int32>(nVal);
+ if ( nHeadNoneTail < 0 )
+ {
+ setSuffixCell(
+ aCol[nCol], static_cast<SCROW>(nRow),
+ nStringValue, nMinDigits, aValue,
+ eCellType, bIsOrdinalSuffix);
+ }
+ else
+ {
+ OUString aStr;
+ if (nHeadNoneTail == 2 && nStringValue >= 0) // Put back the '+'
+ aStr = aValue + "+";
+ else
+ aStr = aValue;
+ aStr += lcl_ValueString( nStringValue, nMinDigits );
+ aCol[nCol].SetRawString(static_cast<SCROW>(nRow), aStr);
+ }
+ }
+
+ if (bAttribs && !bEntireArea && !bOverflow)
+ SetPatternAreaCondFormat( nCol, nRow, nRow, *pSrcPattern, rCondFormatIndex);
+ }
+
+ if (rInner == nIEnd || bOverflow)
+ break;
+ if (bPositive)
+ ++rInner;
+ else
+ --rInner;
+ }
+ }
+ if(pProgress)
+ {
+ nProgress += nIMax - nIMin + 1;
+ pProgress->SetStateOnPercent( nProgress );
+ }
+ }
+ }
+ else if(pProgress)
+ {
+ nProgress += nIMax - nIMin + 1;
+ pProgress->SetStateOnPercent( nProgress );
+ }
+ }
+}
+
+void ScTable::Fill( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt64 nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd,
+ double nStepValue, const tools::Duration& rDurationStep,
+ double nMaxValue, ScProgress* pProgress)
+{
+ if (eFillCmd == FILL_AUTO)
+ FillAuto(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir, pProgress);
+ else
+ FillSeries(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir,
+ eFillCmd, eFillDateCmd, nStepValue, rDurationStep, nMaxValue, 0, true, pProgress);
+}
+
+void ScTable::AutoFormatArea(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const ScPatternAttr& rAttr, sal_uInt16 nFormatNo)
+{
+ ScAutoFormat& rFormat = *ScGlobal::GetOrCreateAutoFormat();
+ ScAutoFormatData* pData = rFormat.findByIndex(nFormatNo);
+ if (pData)
+ {
+ ApplyPatternArea(nStartCol, nStartRow, nEndCol, nEndRow, rAttr);
+ }
+}
+
+void ScTable::AutoFormat( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ sal_uInt16 nFormatNo )
+{
+ if (!(ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)))
+ return;
+
+ ScAutoFormat& rFormat = *ScGlobal::GetOrCreateAutoFormat();
+ ScAutoFormatData* pData = rFormat.findByIndex(nFormatNo);
+ if (!pData)
+ return;
+
+ std::unique_ptr<ScPatternAttr> pPatternAttrs[16];
+ for (sal_uInt8 i = 0; i < 16; ++i)
+ {
+ pPatternAttrs[i].reset(new ScPatternAttr(rDocument.GetPool()));
+ pData->FillToItemSet(i, pPatternAttrs[i]->GetItemSet(), rDocument);
+ }
+
+ SCCOL nCol = nStartCol;
+ SCROW nRow = nStartRow;
+ sal_uInt16 nIndex = 0;
+ // Left top corner
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ // Left column
+ if (pData->HasSameData(4, 8))
+ AutoFormatArea(nStartCol, nStartRow + 1, nStartCol, nEndRow - 1, *pPatternAttrs[4], nFormatNo);
+ else
+ {
+ nIndex = 4;
+ for (nRow = nStartRow + 1; nRow < nEndRow; nRow++)
+ {
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ if (nIndex == 4)
+ nIndex = 8;
+ else
+ nIndex = 4;
+ }
+ }
+ // Left bottom corner
+ nRow = nEndRow;
+ nIndex = 12;
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ // Right top corner
+ nCol = nEndCol;
+ nRow = nStartRow;
+ nIndex = 3;
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ // Right column
+ if (pData->HasSameData(7, 11))
+ AutoFormatArea(nEndCol, nStartRow + 1, nEndCol, nEndRow - 1, *pPatternAttrs[7], nFormatNo);
+ else
+ {
+ nIndex = 7;
+ for (nRow = nStartRow + 1; nRow < nEndRow; nRow++)
+ {
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ if (nIndex == 7)
+ nIndex = 11;
+ else
+ nIndex = 7;
+ }
+ }
+ // Right bottom corner
+ nRow = nEndRow;
+ nIndex = 15;
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ nRow = nStartRow;
+ nIndex = 1;
+ for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
+ {
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ if (nIndex == 1)
+ nIndex = 2;
+ else
+ nIndex = 1;
+ }
+ // Bottom row
+ nRow = nEndRow;
+ nIndex = 13;
+ for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
+ {
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ if (nIndex == 13)
+ nIndex = 14;
+ else
+ nIndex = 13;
+ }
+ // Body
+ if ((pData->HasSameData(5, 6)) && (pData->HasSameData(9, 10)) && (pData->HasSameData(5, 9)))
+ AutoFormatArea(nStartCol + 1, nStartRow + 1, nEndCol-1, nEndRow - 1, *pPatternAttrs[5], nFormatNo);
+ else
+ {
+ if ((pData->HasSameData(5, 9)) && (pData->HasSameData(6, 10)))
+ {
+ nIndex = 5;
+ for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
+ {
+ AutoFormatArea(nCol, nStartRow + 1, nCol, nEndRow - 1, *pPatternAttrs[nIndex], nFormatNo);
+ if (nIndex == 5)
+ nIndex = 6;
+ else
+ nIndex = 5;
+ }
+ }
+ else
+ {
+ nIndex = 5;
+ for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
+ {
+ for (nRow = nStartRow + 1; nRow < nEndRow; nRow++)
+ {
+ AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
+ if ((nIndex == 5) || (nIndex == 9))
+ {
+ if (nIndex == 5)
+ nIndex = 9;
+ else
+ nIndex = 5;
+ }
+ else
+ {
+ if (nIndex == 6)
+ nIndex = 10;
+ else
+ nIndex = 6;
+ }
+ } // for nRow
+ if ((nIndex == 5) || (nIndex == 9))
+ nIndex = 6;
+ else
+ nIndex = 5;
+ } // for nCol
+ } // if not equal Column
+ } // if not all equal
+}
+
+void ScTable::GetAutoFormatAttr(SCCOL nCol, SCROW nRow, sal_uInt16 nIndex, ScAutoFormatData& rData)
+{
+ sal_uInt32 nFormatIndex = GetNumberFormat( nCol, nRow );
+ ScNumFormatAbbrev aNumFormat( nFormatIndex, *rDocument.GetFormatTable() );
+ rData.GetFromItemSet( nIndex, GetPattern( nCol, nRow )->GetItemSet(), aNumFormat );
+}
+
+#define LF_LEFT 1
+#define LF_TOP 2
+#define LF_RIGHT 4
+#define LF_BOTTOM 8
+#define LF_ALL (LF_LEFT | LF_TOP | LF_RIGHT | LF_BOTTOM)
+
+void ScTable::GetAutoFormatFrame(SCCOL nCol, SCROW nRow, sal_uInt16 nFlags, sal_uInt16 nIndex, ScAutoFormatData& rData)
+{
+ const SvxBoxItem* pTheBox = GetAttr(nCol, nRow, ATTR_BORDER);
+ const SvxBoxItem* pLeftBox = GetAttr(nCol - 1, nRow, ATTR_BORDER);
+ const SvxBoxItem* pTopBox = GetAttr(nCol, nRow - 1, ATTR_BORDER);
+ const SvxBoxItem* pRightBox = GetAttr(nCol + 1, nRow, ATTR_BORDER);
+ const SvxBoxItem* pBottomBox = GetAttr(nCol, nRow + 1, ATTR_BORDER);
+
+ SvxBoxItem aBox( ATTR_BORDER );
+ if (nFlags & LF_LEFT)
+ {
+ if (pLeftBox)
+ {
+ if (ScHasPriority(pTheBox->GetLeft(), pLeftBox->GetRight()))
+ aBox.SetLine(pTheBox->GetLeft(), SvxBoxItemLine::LEFT);
+ else
+ aBox.SetLine(pLeftBox->GetRight(), SvxBoxItemLine::LEFT);
+ }
+ else
+ aBox.SetLine(pTheBox->GetLeft(), SvxBoxItemLine::LEFT);
+ }
+ if (nFlags & LF_TOP)
+ {
+ if (pTopBox)
+ {
+ if (ScHasPriority(pTheBox->GetTop(), pTopBox->GetBottom()))
+ aBox.SetLine(pTheBox->GetTop(), SvxBoxItemLine::TOP);
+ else
+ aBox.SetLine(pTopBox->GetBottom(), SvxBoxItemLine::TOP);
+ }
+ else
+ aBox.SetLine(pTheBox->GetTop(), SvxBoxItemLine::TOP);
+ }
+ if (nFlags & LF_RIGHT)
+ {
+ if (pRightBox)
+ {
+ if (ScHasPriority(pTheBox->GetRight(), pRightBox->GetLeft()))
+ aBox.SetLine(pTheBox->GetRight(), SvxBoxItemLine::RIGHT);
+ else
+ aBox.SetLine(pRightBox->GetLeft(), SvxBoxItemLine::RIGHT);
+ }
+ else
+ aBox.SetLine(pTheBox->GetRight(), SvxBoxItemLine::RIGHT);
+ }
+ if (nFlags & LF_BOTTOM)
+ {
+ if (pBottomBox)
+ {
+ if (ScHasPriority(pTheBox->GetBottom(), pBottomBox->GetTop()))
+ aBox.SetLine(pTheBox->GetBottom(), SvxBoxItemLine::BOTTOM);
+ else
+ aBox.SetLine(pBottomBox->GetTop(), SvxBoxItemLine::BOTTOM);
+ }
+ else
+ aBox.SetLine(pTheBox->GetBottom(), SvxBoxItemLine::BOTTOM);
+ }
+ rData.PutItem( nIndex, aBox );
+}
+
+void ScTable::GetAutoFormatData(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, ScAutoFormatData& rData)
+{
+ if (!(ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)))
+ return;
+
+ if ((nEndCol - nStartCol < 3) || (nEndRow - nStartRow < 3))
+ return;
+
+ // Left top corner
+ GetAutoFormatAttr(nStartCol, nStartRow, 0, rData);
+ GetAutoFormatFrame(nStartCol, nStartRow, LF_ALL, 0, rData);
+ // Left column
+ GetAutoFormatAttr(nStartCol, nStartRow + 1, 4, rData);
+ GetAutoFormatAttr(nStartCol, nStartRow + 2, 8, rData);
+ GetAutoFormatFrame(nStartCol, nStartRow + 1, LF_LEFT | LF_RIGHT | LF_BOTTOM, 4, rData);
+ if (nEndRow - nStartRow >= 4)
+ GetAutoFormatFrame(nStartCol, nStartRow + 2, LF_LEFT | LF_RIGHT | LF_BOTTOM, 8, rData);
+ else
+ rData.CopyItem( 8, 4, ATTR_BORDER );
+ // Left bottom corner
+ GetAutoFormatAttr(nStartCol, nEndRow, 12, rData);
+ GetAutoFormatFrame(nStartCol, nEndRow, LF_ALL, 12, rData);
+ // Right top corner
+ GetAutoFormatAttr(nEndCol, nStartRow, 3, rData);
+ GetAutoFormatFrame(nEndCol, nStartRow, LF_ALL, 3, rData);
+ // Right column
+ GetAutoFormatAttr(nEndCol, nStartRow + 1, 7, rData);
+ GetAutoFormatAttr(nEndCol, nStartRow + 2, 11, rData);
+ GetAutoFormatFrame(nEndCol, nStartRow + 1, LF_LEFT | LF_RIGHT | LF_BOTTOM, 7, rData);
+ if (nEndRow - nStartRow >= 4)
+ GetAutoFormatFrame(nEndCol, nStartRow + 2, LF_LEFT | LF_RIGHT | LF_BOTTOM, 11, rData);
+ else
+ rData.CopyItem( 11, 7, ATTR_BORDER );
+ // Right bottom corner
+ GetAutoFormatAttr(nEndCol, nEndRow, 15, rData);
+ GetAutoFormatFrame(nEndCol, nEndRow, LF_ALL, 15, rData);
+ // Top row
+ GetAutoFormatAttr(nStartCol + 1, nStartRow, 1, rData);
+ GetAutoFormatAttr(nStartCol + 2, nStartRow, 2, rData);
+ GetAutoFormatFrame(nStartCol + 1, nStartRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 1, rData);
+ if (nEndCol - nStartCol >= 4)
+ GetAutoFormatFrame(nStartCol + 2, nStartRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 2, rData);
+ else
+ rData.CopyItem( 2, 1, ATTR_BORDER );
+ // Bottom row
+ GetAutoFormatAttr(nStartCol + 1, nEndRow, 13, rData);
+ GetAutoFormatAttr(nStartCol + 2, nEndRow, 14, rData);
+ GetAutoFormatFrame(nStartCol + 1, nEndRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 13, rData);
+ if (nEndCol - nStartCol >= 4)
+ GetAutoFormatFrame(nStartCol + 2, nEndRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 14, rData);
+ else
+ rData.CopyItem( 14, 13, ATTR_BORDER );
+ // Body
+ GetAutoFormatAttr(nStartCol + 1, nStartRow + 1, 5, rData);
+ GetAutoFormatAttr(nStartCol + 2, nStartRow + 1, 6, rData);
+ GetAutoFormatAttr(nStartCol + 1, nStartRow + 2, 9, rData);
+ GetAutoFormatAttr(nStartCol + 2, nStartRow + 2, 10, rData);
+ GetAutoFormatFrame(nStartCol + 1, nStartRow + 1, LF_RIGHT | LF_BOTTOM, 5, rData);
+ if ((nEndCol - nStartCol >= 4) && (nEndRow - nStartRow >= 4))
+ {
+ GetAutoFormatFrame(nStartCol + 2, nStartRow + 1, LF_RIGHT | LF_BOTTOM, 6, rData);
+ GetAutoFormatFrame(nStartCol + 1, nStartRow + 2, LF_RIGHT | LF_BOTTOM, 9, rData);
+ GetAutoFormatFrame(nStartCol + 2, nStartRow + 2, LF_RIGHT | LF_BOTTOM, 10, rData);
+ }
+ else
+ {
+ rData.CopyItem( 6, 5, ATTR_BORDER );
+ rData.CopyItem( 9, 5, ATTR_BORDER );
+ rData.CopyItem( 10, 5, ATTR_BORDER );
+ }
+}
+
+void ScTable::SetError( SCCOL nCol, SCROW nRow, FormulaError nError)
+{
+ if (ValidColRow(nCol, nRow))
+ aCol[nCol].SetError( nRow, nError );
+}
+
+void ScTable::UpdateInsertTabAbs(SCTAB nTable)
+{
+ for (SCCOL i=0; i < aCol.size(); i++)
+ aCol[i].UpdateInsertTabAbs(nTable);
+}
+
+bool ScTable::GetNextSpellingCell(SCCOL& rCol, SCROW& rRow, bool bInSel,
+ const ScMarkData& rMark) const
+{
+ if (rRow == rDocument.MaxRow()+2) // end of table
+ {
+ rRow = 0;
+ rCol = 0;
+ }
+ else
+ {
+ rRow++;
+ if (rRow == rDocument.MaxRow()+1)
+ {
+ rCol++;
+ rRow = 0;
+ }
+ }
+ if (rCol == rDocument.MaxCol()+1)
+ return true;
+ for (;;)
+ {
+ if (!ValidCol(rCol))
+ return true;
+ if (rCol >= GetAllocatedColumnsCount())
+ return true;
+ if (aCol[rCol].GetNextSpellingCell(rRow, bInSel, rMark))
+ return true;
+ /*else (rRow == rDocument.MaxRow()+1) */
+ rCol++;
+ rRow = 0;
+ }
+}
+
+void ScTable::TestTabRefAbs(SCTAB nTable) const
+{
+ for (SCCOL i=0; i < aCol.size(); i++)
+ if (aCol[i].TestTabRefAbs(nTable))
+ return;
+}
+
+void ScTable::CompileDBFormula( sc::CompileFormulaContext& rCxt )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].CompileDBFormula(rCxt);
+}
+
+void ScTable::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].CompileColRowNameFormula(rCxt);
+}
+
+SCSIZE ScTable::GetPatternCount( SCCOL nCol ) const
+{
+ if( ValidCol( nCol ) )
+ return aCol[nCol].GetPatternCount();
+ else
+ return 0;
+}
+
+SCSIZE ScTable::GetPatternCount( SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
+{
+ if( ValidCol( nCol ) && ValidRow( nRow1 ) && ValidRow( nRow2 ) )
+ return aCol[nCol].GetPatternCount( nRow1, nRow2 );
+ else
+ return 0;
+}
+
+bool ScTable::ReservePatternCount( SCCOL nCol, SCSIZE nReserve )
+{
+ if( ValidCol( nCol ) )
+ return aCol[nCol].ReservePatternCount( nReserve );
+ else
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table5.cxx b/sc/source/core/data/table5.cxx
new file mode 100644
index 0000000000..39837464ca
--- /dev/null
+++ b/sc/source/core/data/table5.cxx
@@ -0,0 +1,1372 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <attrib.hxx>
+#include <formulacell.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <global.hxx>
+#include <stlpool.hxx>
+#include <tabprotection.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <segmenttree.hxx>
+#include <columniterator.hxx>
+#include <globalnames.hxx>
+#include <scmod.hxx>
+#include <printopt.hxx>
+#include <bcaslot.hxx>
+#include <compressedarray.hxx>
+#include <userdat.hxx>
+#include <conditio.hxx>
+#include <colorscale.hxx>
+#include <cellform.hxx>
+
+#include <com/sun/star/sheet/TablePageBreakData.hpp>
+
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+
+#include <algorithm>
+#include <limits>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::sheet::TablePageBreakData;
+using ::std::set;
+
+void ScTable::UpdatePageBreaks(const ScRange* pUserArea)
+{
+ if (rDocument.IsImportingXML())
+ return;
+
+ // pUserArea != NULL -> print area is specified. We need to force-update
+ // the page breaks.
+
+ if (!pUserArea)
+ {
+ if (!bPageSizeValid)
+ return;
+
+ // Always update breaks if force breaks option has changed
+ if (mbPageBreaksValid && mbForceBreaks == SC_MOD()->GetPrintOptions().GetForceBreaks())
+ return;
+ }
+
+ SfxStyleSheetBase* pStyle
+ = rDocument.GetStyleSheetPool()->Find(aPageStyle, SfxStyleFamily::Page);
+ if (!pStyle)
+ {
+ OSL_FAIL("UpdatePageBreaks: Style not found");
+ return;
+ }
+ SfxItemSet* pStyleSet = &pStyle->GetItemSet();
+
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = rDocument.MaxCol();
+ SCROW nEndRow = rDocument.MaxRow();
+ if (pUserArea)
+ {
+ nStartCol = pUserArea->aStart.Col();
+ nStartRow = pUserArea->aStart.Row();
+ nEndCol = pUserArea->aEnd.Col();
+ nEndRow = pUserArea->aEnd.Row();
+ }
+ else
+ {
+ sal_uInt16 nAreaCount = GetPrintRangeCount();
+ if (nAreaCount > 1)
+ {
+ // Show nothing, when multiple ranges
+
+ for (SCCOL nX : GetColumnsRange(0, rDocument.MaxCol()))
+ RemoveColBreak(nX, true, false);
+
+ RemoveRowPageBreaks(0, rDocument.MaxRow() - 1);
+
+ return;
+ }
+ else if (nAreaCount == 1)
+ {
+ const ScRange* pArea = GetPrintRange(0);
+ if (pArea)
+ {
+ nStartCol = pArea->aStart.Col();
+ nStartRow = pArea->aStart.Row();
+ nEndCol = pArea->aEnd.Col();
+ nEndRow = pArea->aEnd.Row();
+ }
+ } // otherwise show everything
+ }
+
+ // get bSkipColBreaks/bSkipRowBreaks flags:
+ // fdo#40788 - print range scale settings can cause manual breaks to be
+ // ignored (see below). This behaviour may now be set by the user.
+ mbForceBreaks = SC_MOD()->GetPrintOptions().GetForceBreaks();
+ bool bSkipColBreaks = false;
+ bool bSkipRowBreaks = false;
+
+ if (!mbForceBreaks)
+ {
+ if (const SfxUInt16Item* pItem = pStyleSet->GetItemIfSet(ATTR_PAGE_SCALETOPAGES, false))
+ {
+ bSkipColBreaks = bSkipRowBreaks = pItem->GetValue() > 0;
+ }
+
+ const ScPageScaleToItem* pScaleToItem;
+ if (!bSkipColBreaks && (pScaleToItem = pStyleSet->GetItemIfSet(ATTR_PAGE_SCALETO, false)))
+ {
+ // #i54993# when fitting to width or height, ignore only manual breaks in that direction
+ if (pScaleToItem->GetWidth() > 0)
+ bSkipColBreaks = true;
+ if (pScaleToItem->GetHeight() > 0)
+ bSkipRowBreaks = true;
+ }
+ }
+
+ tools::Long nPageSizeX = aPageSizeTwips.Width();
+ tools::Long nPageSizeY = aPageSizeTwips.Height();
+
+ // Beginning: Remove breaks
+
+ for (SCCOL nX : GetColumnsRange(0, nStartCol - 1))
+ RemoveColBreak(nX, true, false);
+ RemoveRowPageBreaks(0, nStartRow - 1);
+
+ if (nStartCol > 0)
+ SetColBreak(nStartCol, true, false); // AREABREAK
+ if (nStartRow > 0)
+ SetRowBreak(nStartRow, true, false); // AREABREAK
+
+ // Middle part: Distribute breaks
+
+ bool bRepeatCol = (nRepeatStartX != SCCOL_REPEAT_NONE);
+ bool bColFound = false;
+ tools::Long nSizeX = 0;
+ for (SCCOL nX = nStartCol; nX <= nEndCol; nX++)
+ {
+ bool bStartOfPage = false;
+ tools::Long nThisX = ColHidden(nX) ? 0 : mpColWidth->GetValue(nX);
+ bool bManualBreak = HasColManualBreak(nX);
+ if ((nSizeX + nThisX > nPageSizeX) || (bManualBreak && !bSkipColBreaks))
+ {
+ SetColBreak(nX, true, false);
+ nSizeX = 0;
+ bStartOfPage = true;
+ }
+ else if (nX != nStartCol)
+ RemoveColBreak(nX, true, false);
+ else
+ bStartOfPage = true;
+
+ if (bStartOfPage && bRepeatCol && nX > nRepeatStartX && !bColFound)
+ {
+ // subtract size of repeat columns from page size
+ for (SCCOL i = nRepeatStartX; i <= nRepeatEndX; i++)
+ nPageSizeX -= ColHidden(i) ? 0 : mpColWidth->GetValue(i);
+ while (nX <= nRepeatEndX)
+ RemoveColBreak(++nX, true, false);
+ bColFound = true;
+ }
+
+ nSizeX += nThisX;
+ }
+
+ // Remove all page breaks in range.
+ RemoveRowPageBreaks(nStartRow + 1, nEndRow);
+
+ // And set new page breaks.
+ bool bRepeatRow = (nRepeatStartY != SCROW_REPEAT_NONE);
+ bool bRowFound = false;
+ tools::Long nSizeY = 0;
+ ScFlatBoolRowSegments::ForwardIterator aIterHidden(*mpHiddenRows);
+ ScFlatUInt16RowSegments::ForwardIterator aIterHeights(*mpRowHeights);
+ SCROW nNextManualBreak = GetNextManualBreak(nStartRow); // -1 => no more manual breaks
+ for (SCROW nY = nStartRow; nY <= nEndRow; ++nY)
+ {
+ bool bStartOfPage = false;
+ bool bThisRowHidden = false;
+ const bool bHasValue = aIterHidden.getValue(nY, bThisRowHidden);
+ assert(bHasValue);
+ (void)bHasValue;
+ tools::Long nThisY = 0;
+ if (!bThisRowHidden)
+ {
+ sal_uInt16 nTmp;
+ const bool bHasHeight = aIterHeights.getValue(nY, nTmp);
+ assert(bHasHeight);
+ if (bHasHeight)
+ nThisY = static_cast<tools::Long>(nTmp);
+ }
+
+ bool bManualBreak = false;
+ if (nNextManualBreak >= 0)
+ {
+ bManualBreak = (nY == nNextManualBreak);
+ if (nY >= nNextManualBreak)
+ // Query the next manual break position.
+ nNextManualBreak = GetNextManualBreak(nY + 1);
+ }
+
+ if ((nSizeY + nThisY > nPageSizeY) || (bManualBreak && !bSkipRowBreaks))
+ {
+ SetRowBreak(nY, true, false);
+ nSizeY = 0;
+ bStartOfPage = true;
+ }
+ else if (nY != nStartRow)
+ ; // page break already removed
+ else
+ bStartOfPage = true;
+
+ if (bStartOfPage && bRepeatRow && nY > nRepeatStartY && !bRowFound)
+ {
+ // subtract size of repeat rows from page size
+ tools::Long nHeights = GetTotalRowHeight(nRepeatStartY, nRepeatEndY);
+#if OSL_DEBUG_LEVEL > 0
+ if (nHeights == ::std::numeric_limits<tools::Long>::max())
+ OSL_FAIL("ScTable::UpdatePageBreaks: row heights overflow");
+#endif
+ nPageSizeY -= nHeights;
+ if (nY <= nRepeatEndY)
+ RemoveRowPageBreaks(nY, nRepeatEndY);
+ bRowFound = true;
+ }
+
+ if (bThisRowHidden)
+ {
+ // Hidden row range. Skip them unless there is a manual break.
+ SCROW nLastCommon = aIterHidden.getLastPos();
+ if (nNextManualBreak >= 0)
+ nLastCommon = ::std::min(nLastCommon, nNextManualBreak - 1);
+ nY = nLastCommon;
+ }
+ else
+ {
+ // Visible row range.
+
+ SCROW nLastHidden = aIterHidden.getLastPos();
+ SCROW nLastHeight = aIterHeights.getLastPos();
+ SCROW nLastCommon = ::std::min(nLastHidden, nLastHeight);
+ if (nNextManualBreak >= 0)
+ nLastCommon = ::std::min(nLastCommon, nNextManualBreak - 1);
+
+ if (nLastCommon > nY)
+ {
+ tools::Long nMaxMultiple = static_cast<tools::Long>(nLastCommon - nY);
+ tools::Long nMultiple = (nPageSizeY - nSizeY) / nThisY;
+ if (nMultiple > nMaxMultiple)
+ nMultiple = nMaxMultiple;
+ if (nMultiple > 1)
+ {
+ nSizeY += nThisY * (nMultiple - 1);
+ nY += nMultiple - 1;
+ }
+ }
+ }
+
+ nSizeY += nThisY;
+ }
+
+ // End: Remove Break
+
+ if (nEndCol < rDocument.MaxCol())
+ {
+ SetColBreak(nEndCol + 1, true, false); // AREABREAK
+ for (SCCOL nCol : GetColumnsRange(nEndCol + 2, rDocument.MaxCol()))
+ RemoveColBreak(nCol, true, false);
+ }
+ if (nEndRow < rDocument.MaxRow())
+ {
+ SetRowBreak(nEndRow + 1, true, false); // AREABREAK
+ if (nEndRow + 2 <= rDocument.MaxRow())
+ RemoveRowPageBreaks(nEndRow + 2, rDocument.MaxRow());
+ }
+ mbPageBreaksValid
+ = !pUserArea; // #i116881# the valid flag can only apply to the "no user area" case
+}
+
+void ScTable::RemoveManualBreaks()
+{
+ maRowManualBreaks.clear();
+ maColManualBreaks.clear();
+ InvalidatePageBreaks();
+
+ SetStreamValid(false);
+}
+
+bool ScTable::HasManualBreaks() const
+{
+ return !maRowManualBreaks.empty() || !maColManualBreaks.empty();
+}
+
+void ScTable::SetRowManualBreaks(::std::set<SCROW>&& rBreaks)
+{
+ maRowManualBreaks = std::move(rBreaks);
+ InvalidatePageBreaks();
+ SetStreamValid(false);
+}
+
+void ScTable::SetColManualBreaks(::std::set<SCCOL>&& rBreaks)
+{
+ maColManualBreaks = std::move(rBreaks);
+ InvalidatePageBreaks();
+ SetStreamValid(false);
+}
+
+void ScTable::GetAllRowBreaks(set<SCROW>& rBreaks, bool bPage, bool bManual) const
+{
+ if (bPage)
+ rBreaks = maRowPageBreaks;
+
+ if (bManual)
+ {
+ copy(maRowManualBreaks.begin(), maRowManualBreaks.end(),
+ inserter(rBreaks, rBreaks.begin()));
+ }
+}
+
+void ScTable::GetAllColBreaks(set<SCCOL>& rBreaks, bool bPage, bool bManual) const
+{
+ if (bPage)
+ rBreaks = maColPageBreaks;
+
+ if (bManual)
+ {
+ copy(maColManualBreaks.begin(), maColManualBreaks.end(),
+ inserter(rBreaks, rBreaks.begin()));
+ }
+}
+
+bool ScTable::HasRowPageBreak(SCROW nRow) const
+{
+ if (!ValidRow(nRow))
+ return false;
+
+ return maRowPageBreaks.find(nRow) != maRowPageBreaks.end();
+}
+
+bool ScTable::HasColPageBreak(SCCOL nCol) const
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ return maColPageBreaks.find(nCol) != maColPageBreaks.end();
+}
+
+bool ScTable::HasRowManualBreak(SCROW nRow) const
+{
+ if (!ValidRow(nRow))
+ return false;
+
+ return maRowManualBreaks.find(nRow) != maRowManualBreaks.end();
+}
+
+bool ScTable::HasColManualBreak(SCCOL nCol) const
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ return maColManualBreaks.find(nCol) != maColManualBreaks.end();
+}
+
+SCROW ScTable::GetNextManualBreak(SCROW nRow) const
+{
+ set<SCROW>::const_iterator itr = maRowManualBreaks.lower_bound(nRow);
+ return itr == maRowManualBreaks.end() ? -1 : *itr;
+}
+
+void ScTable::RemoveRowPageBreaks(SCROW nStartRow, SCROW nEndRow)
+{
+ if (!ValidRow(nStartRow) || !ValidRow(nEndRow))
+ return;
+
+ set<SCROW>::iterator low = maRowPageBreaks.lower_bound(nStartRow);
+ set<SCROW>::iterator high = maRowPageBreaks.upper_bound(nEndRow);
+ maRowPageBreaks.erase(low, high);
+}
+
+void ScTable::RemoveRowBreak(SCROW nRow, bool bPage, bool bManual)
+{
+ if (!ValidRow(nRow))
+ return;
+
+ if (bPage)
+ maRowPageBreaks.erase(nRow);
+
+ if (bManual)
+ {
+ maRowManualBreaks.erase(nRow);
+ InvalidatePageBreaks();
+ }
+}
+
+void ScTable::RemoveColBreak(SCCOL nCol, bool bPage, bool bManual)
+{
+ if (!ValidCol(nCol))
+ return;
+
+ if (bPage)
+ maColPageBreaks.erase(nCol);
+
+ if (bManual)
+ {
+ maColManualBreaks.erase(nCol);
+ InvalidatePageBreaks();
+ }
+}
+
+void ScTable::SetRowBreak(SCROW nRow, bool bPage, bool bManual)
+{
+ if (!ValidRow(nRow))
+ return;
+
+ if (bPage)
+ maRowPageBreaks.insert(nRow);
+
+ if (bManual)
+ {
+ maRowManualBreaks.insert(nRow);
+ InvalidatePageBreaks();
+ }
+}
+
+void ScTable::SetColBreak(SCCOL nCol, bool bPage, bool bManual)
+{
+ if (!ValidCol(nCol))
+ return;
+
+ if (bPage)
+ maColPageBreaks.insert(nCol);
+
+ if (bManual)
+ {
+ maColManualBreaks.insert(nCol);
+ InvalidatePageBreaks();
+ }
+}
+
+Sequence<TablePageBreakData> ScTable::GetRowBreakData() const
+{
+ using ::std::inserter;
+
+ set<SCROW> aRowBreaks = maRowPageBreaks;
+ copy(maRowManualBreaks.begin(), maRowManualBreaks.end(),
+ inserter(aRowBreaks, aRowBreaks.begin()));
+
+ Sequence<TablePageBreakData> aSeq(aRowBreaks.size());
+ std::transform(aRowBreaks.begin(), aRowBreaks.end(), aSeq.getArray(), [this](const SCROW nRow) {
+ return TablePageBreakData(nRow, HasRowManualBreak(nRow));
+ });
+
+ return aSeq;
+}
+
+bool ScTable::RowHidden(SCROW nRow, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (!ValidRow(nRow))
+ {
+ if (pFirstRow)
+ *pFirstRow = nRow;
+ if (pLastRow)
+ *pLastRow = nRow;
+ return true;
+ }
+
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ {
+ // search failed.
+ if (pFirstRow)
+ *pFirstRow = nRow;
+ if (pLastRow)
+ *pLastRow = nRow;
+ return true;
+ }
+
+ if (pFirstRow)
+ *pFirstRow = aData.mnRow1;
+ if (pLastRow)
+ *pLastRow = aData.mnRow2;
+
+ return aData.mbValue;
+}
+
+bool ScTable::RowHiddenLeaf(SCROW nRow, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (!ValidRow(nRow))
+ {
+ if (pFirstRow)
+ *pFirstRow = nRow;
+ if (pLastRow)
+ *pLastRow = nRow;
+ return true;
+ }
+
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!mpHiddenRows->getRangeDataLeaf(nRow, aData))
+ {
+ // search failed.
+ if (pFirstRow)
+ *pFirstRow = nRow;
+ if (pLastRow)
+ *pLastRow = nRow;
+ return true;
+ }
+
+ if (pFirstRow)
+ *pFirstRow = aData.mnRow1;
+ if (pLastRow)
+ *pLastRow = aData.mnRow2;
+
+ return aData.mbValue;
+}
+
+bool ScTable::HasHiddenRows(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nRow = nStartRow;
+ while (nRow <= nEndRow)
+ {
+ SCROW nLastRow = -1;
+ bool bHidden = RowHidden(nRow, nullptr, &nLastRow);
+ if (bHidden)
+ return true;
+
+ nRow = nLastRow + 1;
+ }
+ return false;
+}
+
+bool ScTable::ColHidden(SCCOL nCol, SCCOL* pFirstCol, SCCOL* pLastCol) const
+{
+ if (!ValidCol(nCol))
+ return true;
+
+ ScFlatBoolColSegments::RangeData aData;
+ if (!mpHiddenCols->getRangeData(nCol, aData))
+ return true;
+
+ if (pFirstCol)
+ *pFirstCol = aData.mnCol1;
+ if (pLastCol)
+ *pLastCol = aData.mnCol2;
+
+ return aData.mbValue;
+}
+
+bool ScTable::SetRowHidden(SCROW nStartRow, SCROW nEndRow, bool bHidden)
+{
+ bool bChanged = false;
+ if (bHidden)
+ bChanged = mpHiddenRows->setTrue(nStartRow, nEndRow);
+ else
+ bChanged = mpHiddenRows->setFalse(nStartRow, nEndRow);
+
+ // Cell anchored objects might change visibility
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if (pDrawLayer)
+ {
+ std::vector<SdrObject*> aRowDrawObjects;
+ aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRows(GetTab(), nStartRow, nEndRow);
+ for (auto aObj : aRowDrawObjects)
+ {
+ ScDrawObjData* pData = ScDrawLayer::GetObjData(aObj);
+ if (pData)
+ {
+ if (bHidden)
+ aObj->SetVisible(false);
+ else if (!GetDoc().ColHidden(pData->maStart.Col(), pData->maStart.Tab()))
+ {
+ // Only change visibility if object is not hidden by a hidden col
+ aObj->SetVisible(true);
+ }
+ }
+ }
+ }
+
+ if (bChanged)
+ {
+ SetStreamValid(false);
+
+ { // Scoped bulk broadcast.
+ // Only subtotal formula cells will accept the notification of
+ // SfxHintId::ScHiddenRowsChanged, leaving the bulk will track
+ // those and broadcast SfxHintId::ScDataChanged to notify all
+ // dependents.
+ ScBulkBroadcast aBulkBroadcast(rDocument.GetBASM(), SfxHintId::ScDataChanged);
+ for (SCCOL i = 0; i < aCol.size(); i++)
+ {
+ aCol[i].BroadcastRows(nStartRow, nEndRow, SfxHintId::ScHiddenRowsChanged);
+ }
+ }
+ }
+
+ return bChanged;
+}
+
+void ScTable::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, bool bHidden)
+{
+ bool bChanged = false;
+ if (bHidden)
+ bChanged = mpHiddenCols->setTrue(nStartCol, nEndCol);
+ else
+ bChanged = mpHiddenCols->setFalse(nStartCol, nEndCol);
+
+ // Cell anchored objects might change visibility
+ ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer();
+ if (pDrawLayer)
+ {
+ std::vector<SdrObject*> aColDrawObjects;
+ aColDrawObjects = pDrawLayer->GetObjectsAnchoredToCols(GetTab(), nStartCol, nEndCol);
+ for (auto aObj : aColDrawObjects)
+ {
+ ScDrawObjData* pData = ScDrawLayer::GetObjData(aObj);
+ if (pData)
+ {
+ if (bHidden)
+ aObj->SetVisible(false);
+ else if (!GetDoc().RowHidden(pData->maStart.Row(), pData->maStart.Tab()))
+ {
+ // Only change visibility if object is not hidden by a hidden row
+ aObj->SetVisible(true);
+ }
+ }
+ }
+ }
+
+ if (bChanged)
+ SetStreamValid(false);
+}
+
+void ScTable::CopyColHidden(const ScTable& rTable, SCCOL nStartCol, SCCOL nEndCol)
+{
+ SCCOL nCol = nStartCol;
+ while (nCol <= nEndCol)
+ {
+ SCCOL nLastCol = -1;
+ bool bHidden = rTable.ColHidden(nCol, nullptr, &nLastCol);
+ if (nLastCol > nEndCol)
+ nLastCol = nEndCol;
+
+ SetColHidden(nCol, nLastCol, bHidden);
+ nCol = nLastCol + 1;
+ }
+}
+
+void ScTable::CopyRowHidden(const ScTable& rTable, SCROW nStartRow, SCROW nEndRow)
+{
+ SCROW nRow = nStartRow;
+ while (nRow <= nEndRow)
+ {
+ SCROW nLastRow = -1;
+ bool bHidden = rTable.RowHidden(nRow, nullptr, &nLastRow);
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+ SetRowHidden(nRow, nLastRow, bHidden);
+ nRow = nLastRow + 1;
+ }
+}
+
+void ScTable::CopyRowHeight(const ScTable& rSrcTable, SCROW nStartRow, SCROW nEndRow,
+ SCROW nSrcOffset)
+{
+ SCROW nRow = nStartRow;
+ ScFlatUInt16RowSegments::RangeData aSrcData;
+ while (nRow <= nEndRow)
+ {
+ if (!rSrcTable.mpRowHeights->getRangeData(nRow + nSrcOffset, aSrcData))
+ // Something is wrong !
+ return;
+
+ SCROW nLastRow = aSrcData.mnRow2 - nSrcOffset;
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+
+ mpRowHeights->setValue(nRow, nLastRow, aSrcData.mnValue);
+ nRow = nLastRow + 1;
+ }
+}
+
+SCROW ScTable::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nRow = nStartRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow <= nEndRow)
+ {
+ if (!ValidRow(nRow))
+ break;
+
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ // failed to get range data.
+ break;
+
+ if (!aData.mbValue)
+ // visible row found
+ return nRow;
+
+ nRow = aData.mnRow2 + 1;
+ }
+
+ return ::std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScTable::LastVisibleRow(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nRow = nEndRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow >= nStartRow)
+ {
+ if (!ValidRow(nRow))
+ break;
+
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ // failed to get range data.
+ break;
+
+ if (!aData.mbValue)
+ // visible row found
+ return nRow;
+
+ nRow = aData.mnRow1 - 1;
+ }
+
+ return ::std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScTable::CountVisibleRows(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nCount = 0;
+ SCROW nRow = nStartRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow <= nEndRow)
+ {
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ break;
+
+ if (aData.mnRow2 > nEndRow)
+ aData.mnRow2 = nEndRow;
+
+ if (!aData.mbValue)
+ nCount += aData.mnRow2 - nRow + 1;
+
+ nRow = aData.mnRow2 + 1;
+ }
+ return nCount;
+}
+
+tools::Long ScTable::GetTotalRowHeight(SCROW nStartRow, SCROW nEndRow, bool bHiddenAsZero) const
+{
+ tools::Long nHeight = 0;
+ SCROW nRow = nStartRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow <= nEndRow)
+ {
+ if (!mpHiddenRows->getRangeData(nRow, aData))
+ break;
+
+ if (aData.mnRow2 > nEndRow)
+ aData.mnRow2 = nEndRow;
+
+ if (!(bHiddenAsZero && aData.mbValue))
+ // visible row range.
+ nHeight += mpRowHeights->getSumValue(nRow, aData.mnRow2);
+
+ nRow = aData.mnRow2 + 1;
+ }
+
+ return nHeight;
+}
+
+SCCOL ScTable::CountVisibleCols(SCCOL nStartCol, SCCOL nEndCol) const
+{
+ assert(nStartCol <= nEndCol);
+ SCCOL nCount = 0;
+ SCCOL nCol = nStartCol;
+ ScFlatBoolColSegments::RangeData aData;
+ while (nCol <= nEndCol)
+ {
+ if (!mpHiddenCols->getRangeData(nCol, aData))
+ break;
+
+ if (aData.mnCol2 > nEndCol)
+ aData.mnCol2 = nEndCol;
+
+ if (!aData.mbValue)
+ nCount += aData.mnCol2 - nCol + 1;
+
+ nCol = aData.mnCol2 + 1;
+ }
+ return nCount;
+}
+
+SCCOLROW ScTable::LastHiddenColRow(SCCOLROW nPos, bool bCol) const
+{
+ if (bCol)
+ {
+ SCCOL nCol = static_cast<SCCOL>(nPos);
+ if (ColHidden(nCol))
+ {
+ for (SCCOL i = nCol + 1; i <= rDocument.MaxCol(); ++i)
+ {
+ if (!ColHidden(i))
+ return i - 1;
+ }
+ }
+ }
+ else
+ {
+ SCROW nRow = static_cast<SCROW>(nPos);
+ SCROW nLastRow;
+ if (RowHidden(nRow, nullptr, &nLastRow))
+ return static_cast<SCCOLROW>(nLastRow);
+ }
+ return ::std::numeric_limits<SCCOLROW>::max();
+}
+
+bool ScTable::RowFiltered(SCROW nRow, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (!ValidRow(nRow))
+ return false;
+
+ ScFlatBoolRowSegments::RangeData aData;
+ if (!mpFilteredRows->getRangeData(nRow, aData))
+ // search failed.
+ return false;
+
+ if (pFirstRow)
+ *pFirstRow = aData.mnRow1;
+ if (pLastRow)
+ *pLastRow = aData.mnRow2;
+
+ return aData.mbValue;
+}
+
+bool ScTable::ColFiltered(SCCOL nCol, SCCOL* pFirstCol, SCCOL* pLastCol) const
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ ScFlatBoolColSegments::RangeData aData;
+ if (!mpFilteredCols->getRangeData(nCol, aData))
+ // search failed.
+ return false;
+
+ if (pFirstCol)
+ *pFirstCol = aData.mnCol1;
+ if (pLastCol)
+ *pLastCol = aData.mnCol2;
+
+ return aData.mbValue;
+}
+
+bool ScTable::HasFilteredRows(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nRow = nStartRow;
+ while (nRow <= nEndRow)
+ {
+ SCROW nLastRow = nRow;
+ bool bFiltered = RowFiltered(nRow, nullptr, &nLastRow);
+ if (bFiltered)
+ return true;
+
+ nRow = nLastRow + 1;
+ }
+ return false;
+}
+
+void ScTable::CopyColFiltered(const ScTable& rTable, SCCOL nStartCol, SCCOL nEndCol)
+{
+ SCCOL nCol = nStartCol;
+ while (nCol <= nEndCol)
+ {
+ SCCOL nLastCol = -1;
+ bool bFiltered = rTable.ColFiltered(nCol, nullptr, &nLastCol);
+ if (nLastCol > nEndCol)
+ nLastCol = nEndCol;
+
+ SetColFiltered(nCol, nLastCol, bFiltered);
+ nCol = nLastCol + 1;
+ }
+}
+
+void ScTable::CopyRowFiltered(const ScTable& rTable, SCROW nStartRow, SCROW nEndRow)
+{
+ SCROW nRow = nStartRow;
+ while (nRow <= nEndRow)
+ {
+ SCROW nLastRow = -1;
+ bool bFiltered = rTable.RowFiltered(nRow, nullptr, &nLastRow);
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+ SetRowFiltered(nRow, nLastRow, bFiltered);
+ nRow = nLastRow + 1;
+ }
+}
+
+void ScTable::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, bool bFiltered)
+{
+ if (bFiltered)
+ mpFilteredRows->setTrue(nStartRow, nEndRow);
+ else
+ mpFilteredRows->setFalse(nStartRow, nEndRow);
+}
+
+void ScTable::SetColFiltered(SCCOL nStartCol, SCCOL nEndCol, bool bFiltered)
+{
+ if (bFiltered)
+ mpFilteredCols->setTrue(nStartCol, nEndCol);
+ else
+ mpFilteredCols->setFalse(nStartCol, nEndCol);
+}
+
+SCROW ScTable::FirstNonFilteredRow(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nRow = nStartRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow <= nEndRow)
+ {
+ if (!ValidRow(nRow))
+ break;
+
+ if (!mpFilteredRows->getRangeData(nRow, aData))
+ // failed to get range data.
+ break;
+
+ if (!aData.mbValue)
+ // non-filtered row found
+ return nRow;
+
+ nRow = aData.mnRow2 + 1;
+ }
+
+ return ::std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScTable::LastNonFilteredRow(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nRow = nEndRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow >= nStartRow)
+ {
+ if (!ValidRow(nRow))
+ break;
+
+ if (!mpFilteredRows->getRangeData(nRow, aData))
+ // failed to get range data.
+ break;
+
+ if (!aData.mbValue)
+ // non-filtered row found
+ return nRow;
+
+ nRow = aData.mnRow1 - 1;
+ }
+
+ return ::std::numeric_limits<SCROW>::max();
+}
+
+SCROW ScTable::CountNonFilteredRows(SCROW nStartRow, SCROW nEndRow) const
+{
+ SCROW nCount = 0;
+ SCROW nRow = nStartRow;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow <= nEndRow)
+ {
+ if (!mpFilteredRows->getRangeData(nRow, aData))
+ break;
+
+ if (aData.mnRow2 > nEndRow)
+ aData.mnRow2 = nEndRow;
+
+ if (!aData.mbValue)
+ nCount += aData.mnRow2 - nRow + 1;
+
+ nRow = aData.mnRow2 + 1;
+ }
+ return nCount;
+}
+
+Color ScTable::GetCellBackgroundColor(ScAddress aPos) const
+{
+ Color backgroundColor;
+ bool bHasConditionalBackgroundColor = false;
+ // Check background color from cond. formatting
+ const ScPatternAttr* pPattern = GetDoc().GetPattern(aPos.Col(), aPos.Row(), aPos.Tab());
+ if (pPattern)
+ {
+ if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
+ {
+ const SfxItemSet* pCondSet = GetDoc().GetCondResult(aPos.Col(), aPos.Row(), aPos.Tab());
+ const SvxBrushItem* pBackgroundColor = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
+ backgroundColor = pBackgroundColor->GetColor();
+ bHasConditionalBackgroundColor = true;
+ }
+ }
+
+ // Color scale needs a different handling
+ ScConditionalFormat* pCondFormat = GetDoc().GetCondFormat(aPos.Col(), aPos.Row(), aPos.Tab());
+ if (pCondFormat)
+ {
+ for (size_t i = 0; i < pCondFormat->size(); i++)
+ {
+ auto aEntry = pCondFormat->GetEntry(i);
+ if (aEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ {
+ const ScColorScaleFormat* pColFormat
+ = static_cast<const ScColorScaleFormat*>(aEntry);
+ std::optional<Color> oColor = pColFormat->GetColor(aPos);
+ if (oColor)
+ {
+ backgroundColor = oColor.value();
+ bHasConditionalBackgroundColor = true;
+ }
+ }
+ }
+ }
+ return bHasConditionalBackgroundColor ? backgroundColor
+ : GetDoc().GetAttr(aPos, ATTR_BACKGROUND)->GetColor();
+}
+
+Color ScTable::GetCellTextColor(ScAddress aPos) const
+{
+ // Check text & background color from cond. formatting
+ const ScPatternAttr* pPattern = GetDoc().GetPattern(aPos.Col(), aPos.Row(), aPos.Tab());
+ if (pPattern)
+ {
+ if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
+ {
+ const SfxItemSet* pCondSet = GetDoc().GetCondResult(aPos.Col(), aPos.Row(), aPos.Tab());
+ const SvxColorItem* pColor = &pPattern->GetItem(ATTR_FONT_COLOR, pCondSet);
+ return pColor->GetValue();
+ }
+
+ if (pPattern->GetItem(ATTR_VALUE_FORMAT).GetValue())
+ {
+ SvNumberFormatter* pNumberFormatter = GetDoc().GetFormatTable();
+ const SfxUInt32Item pItem = pPattern->GetItem(ATTR_VALUE_FORMAT);
+ auto& rDoc = const_cast<ScDocument&>(GetDoc());
+ const Color* pColor;
+ ScRefCellValue aCell(rDoc, aPos);
+ ScCellFormat::GetString(rDoc, aPos, pItem.GetValue(), &pColor, *pNumberFormatter, false,
+ false);
+ if (pColor)
+ return *pColor;
+ }
+ }
+
+ const SvxColorItem* pColor = GetDoc().GetAttr(aPos, ATTR_FONT_COLOR);
+ return pColor->GetValue();
+}
+
+bool ScTable::IsManualRowHeight(SCROW nRow) const
+{
+ return bool(pRowFlags->GetValue(nRow) & CRFlags::ManualSize);
+}
+
+namespace
+{
+void lcl_syncFlags(const ScDocument* pDocument, ScFlatBoolColSegments& rColSegments,
+ const ScFlatBoolRowSegments& rRowSegments,
+ ScBitMaskCompressedArray<SCCOL, CRFlags>* pColFlags,
+ ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlags, const CRFlags nFlagMask)
+{
+ CRFlags nFlagMaskComplement = ~nFlagMask;
+
+ pRowFlags->AndValue(0, pDocument->MaxRow(), nFlagMaskComplement);
+ pColFlags->AndValue(0, pDocument->MaxCol() + 1, nFlagMaskComplement);
+
+ {
+ // row hidden flags.
+
+ SCROW nRow = 0;
+ ScFlatBoolRowSegments::RangeData aData;
+ while (nRow <= pDocument->MaxRow())
+ {
+ if (!rRowSegments.getRangeData(nRow, aData))
+ break;
+
+ if (aData.mbValue)
+ pRowFlags->OrValue(nRow, aData.mnRow2, nFlagMask);
+
+ nRow = aData.mnRow2 + 1;
+ }
+ }
+
+ {
+ // column hidden flags.
+
+ SCCOL nCol = 0;
+ ScFlatBoolColSegments::RangeData aData;
+ while (nCol <= pDocument->MaxCol())
+ {
+ if (!rColSegments.getRangeData(nCol, aData))
+ break;
+
+ if (aData.mbValue)
+ pColFlags->OrValue(nCol, aData.mnCol2, nFlagMask);
+
+ nCol = aData.mnCol2 + 1;
+ }
+ }
+}
+}
+
+void ScTable::SyncColRowFlags()
+{
+ CRFlags nManualBreakComplement = ~CRFlags::ManualBreak;
+
+ // Manual breaks.
+ pRowFlags->AndValue(0, rDocument.MaxRow(), nManualBreakComplement);
+ mpColFlags->AndValue(0, rDocument.MaxCol() + 1, nManualBreakComplement);
+
+ for (const auto& rBreakPos : maRowManualBreaks)
+ pRowFlags->OrValue(rBreakPos, CRFlags::ManualBreak);
+
+ for (const auto& rBreakPos : maColManualBreaks)
+ mpColFlags->OrValue(rBreakPos, CRFlags::ManualBreak);
+
+ // Hidden flags.
+ lcl_syncFlags(&rDocument, *mpHiddenCols, *mpHiddenRows, mpColFlags.get(), pRowFlags.get(),
+ CRFlags::Hidden);
+ lcl_syncFlags(&rDocument, *mpFilteredCols, *mpFilteredRows, mpColFlags.get(), pRowFlags.get(),
+ CRFlags::Filtered);
+}
+
+void ScTable::SetPageSize(const Size& rSize)
+{
+ if (!rSize.IsEmpty())
+ {
+ if (aPageSizeTwips != rSize)
+ InvalidatePageBreaks();
+
+ bPageSizeValid = true;
+ aPageSizeTwips = rSize;
+ }
+ else
+ bPageSizeValid = false;
+}
+
+bool ScTable::IsProtected() const { return pTabProtection && pTabProtection->isProtected(); }
+
+void ScTable::SetProtection(const ScTableProtection* pProtect)
+{
+ if (pProtect)
+ pTabProtection.reset(new ScTableProtection(*pProtect));
+ else
+ pTabProtection.reset();
+
+ SetStreamValid(false);
+}
+
+const ScTableProtection* ScTable::GetProtection() const { return pTabProtection.get(); }
+
+Size ScTable::GetPageSize() const
+{
+ if (bPageSizeValid)
+ return aPageSizeTwips;
+ else
+ return Size(); // blank
+}
+
+void ScTable::SetRepeatArea(SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow)
+{
+ // #i117952# page break calculation uses these values (set from ScPrintFunc), not pRepeatColRange/pRepeatRowRange
+ if (nStartCol != nRepeatStartX || nEndCol != nRepeatEndX || nStartRow != nRepeatStartY
+ || nEndRow != nRepeatEndY)
+ InvalidatePageBreaks();
+
+ nRepeatStartX = nStartCol;
+ nRepeatEndX = nEndCol;
+ nRepeatStartY = nStartRow;
+ nRepeatEndY = nEndRow;
+}
+
+void ScTable::StartListening(const ScAddress& rAddress, SvtListener* pListener)
+{
+ if (!ValidCol(rAddress.Col()))
+ return;
+
+ CreateColumnIfNotExists(rAddress.Col()).StartListening(*pListener, rAddress.Row());
+}
+
+void ScTable::EndListening(const ScAddress& rAddress, SvtListener* pListener)
+{
+ if (!ValidCol(rAddress.Col()))
+ return;
+
+ if (rAddress.Col() < aCol.size())
+ aCol[rAddress.Col()].EndListening(*pListener, rAddress.Row());
+}
+
+void ScTable::StartListening(sc::StartListeningContext& rCxt, const ScAddress& rAddress,
+ SvtListener& rListener)
+{
+ if (!ValidCol(rAddress.Col()))
+ return;
+
+ CreateColumnIfNotExists(rAddress.Col()).StartListening(rCxt, rAddress, rListener);
+}
+
+void ScTable::EndListening(sc::EndListeningContext& rCxt, const ScAddress& rAddress,
+ SvtListener& rListener)
+{
+ if (!ValidCol(rAddress.Col()))
+ return;
+
+ if (rAddress.Col() < aCol.size())
+ aCol[rAddress.Col()].EndListening(rCxt, rAddress, rListener);
+}
+
+void ScTable::SetPageStyle(const OUString& rName)
+{
+ if (aPageStyle == rName)
+ return;
+
+ OUString aStrNew = rName;
+ SfxStyleSheetBasePool* pStylePool = rDocument.GetStyleSheetPool();
+ SfxStyleSheetBase* pNewStyle = pStylePool->Find(aStrNew, SfxStyleFamily::Page);
+
+ if (!pNewStyle)
+ {
+ aStrNew = ScResId(STR_STYLENAME_STANDARD);
+ pNewStyle = pStylePool->Find(aStrNew, SfxStyleFamily::Page);
+ }
+
+ if (aPageStyle == aStrNew)
+ return;
+
+ SfxStyleSheetBase* pOldStyle = pStylePool->Find(aPageStyle, SfxStyleFamily::Page);
+ if (pOldStyle && pNewStyle)
+ {
+ SfxItemSet& rOldSet = pOldStyle->GetItemSet();
+ SfxItemSet& rNewSet = pNewStyle->GetItemSet();
+ auto getScaleValue = [](const SfxItemSet& rSet, sal_uInt16 nId) {
+ return static_cast<const SfxUInt16Item&>(rSet.Get(nId)).GetValue();
+ };
+
+ const sal_uInt16 nOldScale = getScaleValue(rOldSet, ATTR_PAGE_SCALE);
+ const sal_uInt16 nOldScaleToPages = getScaleValue(rOldSet, ATTR_PAGE_SCALETOPAGES);
+ const sal_uInt16 nNewScale = getScaleValue(rNewSet, ATTR_PAGE_SCALE);
+ const sal_uInt16 nNewScaleToPages = getScaleValue(rNewSet, ATTR_PAGE_SCALETOPAGES);
+
+ if ((nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages))
+ InvalidateTextWidth(nullptr, nullptr, false, false);
+ }
+
+ if (pNewStyle) // also without the old one (for UpdateStdNames)
+ aPageStyle = aStrNew;
+
+ SetStreamValid(false);
+}
+
+void ScTable::PageStyleModified(const OUString& rNewName)
+{
+ aPageStyle = rNewName;
+ InvalidateTextWidth(nullptr, nullptr, false, false); // don't know what was in the style before
+}
+
+void ScTable::InvalidateTextWidth(const ScAddress* pAdrFrom, const ScAddress* pAdrTo,
+ bool bNumFormatChanged, bool bBroadcast)
+{
+ if (pAdrFrom && !pAdrTo)
+ {
+ // Special case: only process the "from" cell.
+ SCCOL nCol = pAdrFrom->Col();
+ SCROW nRow = pAdrFrom->Row();
+ if (nCol >= aCol.size())
+ return;
+ ScColumn& rCol = aCol[nCol];
+ ScRefCellValue aCell = rCol.GetCellValue(nRow);
+ if (aCell.isEmpty())
+ return;
+
+ rCol.SetTextWidth(nRow, TEXTWIDTH_DIRTY);
+
+ if (bNumFormatChanged)
+ rCol.SetScriptType(nRow, SvtScriptType::UNKNOWN);
+
+ if (bBroadcast)
+ { // Only with CalcAsShown
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ rCol.Broadcast(nRow);
+ break;
+ case CELLTYPE_FORMULA:
+ aCell.getFormula()->SetDirty();
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return;
+ }
+
+ const SCCOL nCol1 = pAdrFrom ? pAdrFrom->Col() : 0;
+ const SCROW nRow1 = pAdrFrom ? pAdrFrom->Row() : 0;
+ const SCCOL nCol2 = pAdrTo ? pAdrTo->Col() : aCol.size() - 1;
+ const SCROW nRow2 = pAdrTo ? pAdrTo->Row() : rDocument.MaxRow();
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ ScColumnTextWidthIterator aIter(GetDoc(), aCol[nCol], nRow1, nRow2);
+ sc::ColumnBlockPosition blockPos; // cache mdds position
+ InitColumnBlockPosition(blockPos, nCol);
+
+ for (; aIter.hasCell(); aIter.next())
+ {
+ SCROW nRow = aIter.getPos();
+ aIter.setValue(TEXTWIDTH_DIRTY);
+ ScRefCellValue aCell = aCol[nCol].GetCellValue(blockPos, nRow);
+ if (aCell.isEmpty())
+ continue;
+
+ if (bNumFormatChanged)
+ aCol[nCol].SetScriptType(nRow, SvtScriptType::UNKNOWN);
+
+ if (bBroadcast)
+ { // Only with CalcAsShown
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ aCol[nCol].Broadcast(nRow);
+ break;
+ case CELLTYPE_FORMULA:
+ aCell.getFormula()->SetDirty();
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table6.cxx b/sc/source/core/data/table6.cxx
new file mode 100644
index 0000000000..a6b03f6b3d
--- /dev/null
+++ b/sc/source/core/data/table6.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 <unotools/textsearch.hxx>
+#include <com/sun/star/util/SearchResult.hpp>
+#include <svl/srchitem.hxx>
+#include <editeng/editobj.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <table.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <markdata.hxx>
+#include <editutil.hxx>
+#include <postit.hxx>
+
+namespace {
+
+void lcl_GetTextWithBreaks( const EditTextObject& rData, ScDocument* pDoc, OUString& rVal )
+{
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(rData);
+ rVal = rEngine.GetText();
+}
+
+}
+
+bool ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ if ( !IsColRowValid( nCol, nRow ) )
+ return false;
+
+ bool bFound = false;
+ bool bDoSearch = true;
+ bool bDoBack = rSearchItem.GetBackward();
+ bool bSearchFormatted = rSearchItem.IsSearchFormatted();
+
+ OUString aString;
+ ScRefCellValue aCell;
+ if (rSearchItem.GetSelection())
+ bDoSearch = rMark.IsCellMarked(nCol, nRow);
+
+ if (!bDoSearch)
+ return false;
+
+ ScPostIt* pNote;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ {
+ pNote = aCol[nCol].GetCellNote(rBlockPos, nRow);
+ if (!pNote)
+ return false;
+ }
+ else
+ {
+ aCell = aCol[nCol].GetCellValue(rBlockPos, nRow);
+ if (aCell.isEmpty())
+ return false;
+ pNote = nullptr;
+ }
+
+ CellType eCellType = aCell.getType();
+ switch (rSearchItem.GetCellType())
+ {
+ case SvxSearchCellType::FORMULA:
+ {
+ if ( eCellType == CELLTYPE_FORMULA )
+ aString = aCell.getFormula()->GetFormula(rDocument.GetGrammar());
+ else if ( eCellType == CELLTYPE_EDIT )
+ lcl_GetTextWithBreaks(*aCell.getEditText(), &rDocument, aString);
+ else
+ {
+ if( !bSearchFormatted )
+ aString = aCol[nCol].GetInputString( rBlockPos, nRow );
+ else
+ aString = aCol[nCol].GetString( rBlockPos, nRow );
+ }
+ break;
+ }
+ case SvxSearchCellType::VALUE:
+ if ( eCellType == CELLTYPE_EDIT )
+ lcl_GetTextWithBreaks(*aCell.getEditText(), &rDocument, aString);
+ else
+ {
+ if( !bSearchFormatted )
+ aString = aCol[nCol].GetInputString( rBlockPos, nRow );
+ else
+ aString = aCol[nCol].GetString( rBlockPos, nRow );
+ }
+ break;
+ case SvxSearchCellType::NOTE:
+ {
+ if (pNote)
+ aString = pNote->GetText();
+ break;
+ }
+ default:
+ break;
+ }
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd = aString.getLength();
+ css::util::SearchResult aSearchResult;
+ if (pSearchText)
+ {
+ if ( bDoBack )
+ {
+ sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
+ bFound = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+ else
+ {
+ bFound = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+
+ if (bFound && rSearchItem.GetWordOnly())
+ bFound = (nStart == 0 && nEnd == aString.getLength() - 1);
+ }
+ else
+ {
+ OSL_FAIL("pSearchText == NULL");
+ return bFound;
+ }
+
+ if (!bFound)
+ return false;
+ if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE
+ && rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL )
+ return bFound;
+
+ if (!IsBlockEditable(nCol, nRow, nCol, nRow))
+ return bFound;
+
+ ScMatrixMode cMatrixFlag = ScMatrixMode::NONE;
+
+ // Don't split the matrix, only replace Matrix formulas
+ if (eCellType == CELLTYPE_FORMULA)
+ {
+ cMatrixFlag = aCell.getFormula()->GetMatrixFlag();
+ if(cMatrixFlag == ScMatrixMode::Reference)
+ return bFound;
+ }
+ // No UndoDoc => Matrix not restorable => don't replace
+ if (cMatrixFlag != ScMatrixMode::NONE && !pUndoDoc)
+ return bFound;
+
+ if ( cMatrixFlag == ScMatrixMode::NONE && rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
+ rUndoStr = aString;
+ else if (pUndoDoc)
+ {
+ ScAddress aAdr( nCol, nRow, nTab );
+ aCell.commit(*pUndoDoc, aAdr);
+ }
+
+ bool bRepeat = !rSearchItem.GetWordOnly();
+ do
+ {
+ // don't continue search if the found text is empty,
+ // otherwise it would never stop (#35410#)
+ if ( nEnd < nStart )
+ bRepeat = false;
+
+ OUString sReplStr = rSearchItem.GetReplaceString();
+ if (rSearchItem.GetRegExp())
+ {
+ pSearchText->ReplaceBackReferences( sReplStr, aString, aSearchResult );
+ OUStringBuffer aStrBuffer(aString);
+ aStrBuffer.remove(nStart, nEnd-nStart+1);
+ aStrBuffer.insert(nStart, sReplStr);
+ aString = aStrBuffer.makeStringAndClear();
+ }
+ else
+ {
+ OUStringBuffer aStrBuffer(aString);
+ aStrBuffer.remove(nStart, nEnd-nStart+1);
+ aStrBuffer.insert(nStart, rSearchItem.GetReplaceString());
+ aString = aStrBuffer.makeStringAndClear();
+ }
+
+ // Adjust index
+ if (bDoBack)
+ {
+ nEnd = nStart;
+ nStart = 0;
+ }
+ else
+ {
+ nStart = nStart + sReplStr.getLength();
+ nEnd = aString.getLength();
+ }
+
+ // continue search ?
+ if (bRepeat)
+ {
+ if ( rSearchItem.GetCommand() != SvxSearchCmd::REPLACE_ALL || nStart >= nEnd )
+ bRepeat = false;
+ else if (bDoBack)
+ {
+ sal_Int32 nTemp=nStart; nStart=nEnd; nEnd=nTemp;
+ bRepeat = pSearchText->SearchBackward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+ else
+ {
+ bRepeat = pSearchText->SearchForward(aString, &nStart, &nEnd, &aSearchResult);
+ // change results to definition before 614:
+ --nEnd;
+ }
+ }
+ }
+ while (bRepeat);
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ {
+ // NB: rich text format is lost.
+ // This is also true of Cells.
+ if (pNote)
+ pNote->SetText( ScAddress( nCol, nRow, nTab ), aString );
+ }
+ else if ( cMatrixFlag != ScMatrixMode::NONE )
+ { // don't split Matrix
+ if ( aString.getLength() > 2 )
+ { // remove {} here so that "{=" can be replaced by "{=..."
+ if ( aString[ aString.getLength()-1 ] == '}' )
+ aString = aString.copy( 0, aString.getLength()-1 );
+ if ( aString[0] == '{' )
+ aString = aString.copy( 1 );
+ }
+ ScAddress aAdr( nCol, nRow, nTab );
+ ScFormulaCell* pFCell = new ScFormulaCell( rDocument, aAdr,
+ aString, rDocument.GetGrammar(), cMatrixFlag );
+ SCCOL nMatCols;
+ SCROW nMatRows;
+ aCell.getFormula()->GetMatColsRows(nMatCols, nMatRows);
+ pFCell->SetMatColsRows( nMatCols, nMatRows );
+ aCol[nCol].SetFormulaCell(nRow, pFCell);
+ }
+ else if (eCellType != CELLTYPE_FORMULA && aString.indexOf('\n') != -1)
+ {
+ ScFieldEditEngine& rEngine = rDocument.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aString);
+ SetEditText(nCol, nRow, rEngine.CreateTextObject());
+ }
+ else
+ aCol[nCol].SetString(nRow, nTab, aString, rDocument.GetAddressConvention());
+ // pCell is invalid now (deleted)
+ aCol[nCol].InitBlockPosition( rBlockPos ); // invalidate also the cached position
+
+ return bFound;
+}
+
+void ScTable::SkipFilteredRows(SCROW& rRow, SCROW& rLastNonFilteredRow, bool bForward)
+{
+ if (bForward)
+ {
+ // forward search
+
+ if (rRow <= rLastNonFilteredRow)
+ return;
+
+ SCROW nLastRow = rRow;
+ if (RowFiltered(rRow, nullptr, &nLastRow))
+ // move to the first non-filtered row.
+ rRow = nLastRow + 1;
+ else
+ // record the last non-filtered row to avoid checking
+ // the filtered state for each and every row.
+ rLastNonFilteredRow = nLastRow;
+ }
+ else
+ {
+ // backward search
+
+ if (rRow >= rLastNonFilteredRow)
+ return;
+
+ SCROW nFirstRow = rRow;
+ if (RowFiltered(rRow, &nFirstRow))
+ // move to the first non-filtered row.
+ rRow = nFirstRow - 1;
+ else
+ // record the last non-filtered row to avoid checking
+ // the filtered state for each and every row.
+ rLastNonFilteredRow = nFirstRow;
+ }
+}
+
+bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ GetCellArea( nLastCol, nLastRow);
+ else
+ GetLastDataPos(nLastCol, nLastRow);
+ std::vector< sc::ColumnBlockConstPosition > blockPos;
+ return Search(rSearchItem, rCol, rRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
+}
+
+bool ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ SCCOL nLastCol, SCROW nLastRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc,
+ std::vector< sc::ColumnBlockConstPosition >& blockPos)
+{
+ bool bFound = false;
+ bool bAll = (rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL)
+ ||(rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL);
+ SCCOL nCol = rCol;
+ SCROW nRow = rRow;
+
+ bool bSkipFiltered = !rSearchItem.IsSearchFiltered();
+ bool bSearchNotes = (rSearchItem.GetCellType() == SvxSearchCellType::NOTE);
+ // We need to cache sc::ColumnBlockConstPosition per each column.
+ if (static_cast<SCCOL>(blockPos.size()) != nLastCol + 1)
+ {
+ blockPos.resize( nLastCol + 1 );
+ for( SCCOL i = 0; i <= nLastCol; ++i )
+ aCol[ i ].InitBlockPosition( blockPos[ i ] );
+ }
+ if (!bAll && rSearchItem.GetBackward())
+ {
+ SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ if (rSearchItem.GetRowDirection())
+ {
+ nCol--;
+ nCol = std::min(nCol, nLastCol);
+ nRow = std::min(nRow, nLastRow);
+ while (!bFound && (nRow >= 0))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+
+ while (!bFound && (nCol >= 0))
+ {
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ], nRow,
+ rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ bool bIsEmpty;
+ do
+ {
+ nCol--;
+ if (nCol >= 0)
+ {
+ if (bSearchNotes)
+ bIsEmpty = !aCol[nCol].HasCellNotes();
+ else
+ bIsEmpty = aCol[nCol].IsEmptyData();
+ }
+ else
+ bIsEmpty = true;
+ }
+ while ((nCol >= 0) && bIsEmpty);
+ }
+ }
+ if (!bFound)
+ {
+ nCol = nLastCol;
+ nRow--;
+ }
+ }
+ }
+ else
+ {
+ nRow--;
+ nCol = std::min(nCol, nLastCol);
+ nRow = std::min(nRow, nLastRow);
+ while (!bFound && (nCol >= 0))
+ {
+ while (!bFound && (nRow >= 0))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
+ nRow, rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ if (bSearchNotes)
+ {
+ /* TODO: can we look for the previous cell note instead? */
+ --nRow;
+ }
+ else
+ {
+ if (!aCol[nCol].GetPrevDataPos(nRow))
+ nRow = -1;
+ }
+ }
+ }
+ if (!bFound)
+ {
+ // Not found in this column. Move to the next column.
+ bool bIsEmpty;
+ nRow = nLastRow;
+ nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ do
+ {
+ nCol--;
+ if (nCol >= 0)
+ {
+ if (bSearchNotes)
+ bIsEmpty = !aCol[nCol].HasCellNotes();
+ else
+ bIsEmpty = aCol[nCol].IsEmptyData();
+ }
+ else
+ bIsEmpty = true;
+ }
+ while ((nCol >= 0) && bIsEmpty);
+ }
+ }
+ }
+ }
+ else
+ {
+ SCROW nLastNonFilteredRow = -1;
+ if (rSearchItem.GetRowDirection())
+ {
+ nCol++;
+ while (!bFound && (nRow <= nLastRow))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+
+ while (!bFound && (nCol <= nLastCol))
+ {
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
+ nRow, rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ nCol++;
+ while ((nCol <= nLastCol) &&
+ (bSearchNotes ? !aCol[nCol].HasCellNotes() : aCol[nCol].IsEmptyData()))
+ nCol++;
+ }
+ }
+ if (!bFound)
+ {
+ nCol = 0;
+ nRow++;
+ }
+ }
+ }
+ else
+ {
+ nRow++;
+ while (!bFound && (nCol <= nLastCol))
+ {
+ while (!bFound && (nRow <= nLastRow))
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+
+ // GetSearchAndReplaceStart sets a nCol of -1 for
+ // ColDirection() only if rSearchItem.GetPattern() is true,
+ // so a negative column shouldn't be possible here.
+ assert(nCol >= 0 && "negative nCol for ColDirection");
+
+ bFound = SearchCell(rSearchItem, nCol, blockPos[ nCol ],
+ nRow, rMark, rUndoStr, pUndoDoc);
+ if (!bFound)
+ {
+ if (bSearchNotes)
+ {
+ /* TODO: can we look for the next cell note instead? */
+ ++nRow;
+ }
+ else
+ {
+ if (!aCol[nCol].GetNextDataPos(nRow))
+ nRow = rDocument.MaxRow() + 1;
+ }
+ }
+ }
+ if (!bFound)
+ {
+ // Not found in this column. Move to the next column.
+ nRow = 0;
+ nLastNonFilteredRow = -1;
+ nCol++;
+ while ((nCol <= nLastCol) &&
+ (bSearchNotes ? !aCol[nCol].HasCellNotes() : aCol[nCol].IsEmptyData()))
+ nCol++;
+ }
+ }
+ }
+ }
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return bFound;
+}
+
+bool ScTable::SearchAll(const SvxSearchItem& rSearchItem, const ScMarkData& rMark,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ bool bFound = true;
+ SCCOL nCol = 0;
+ SCROW nRow = -1;
+ bool bEverFound = false;
+
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ GetCellArea( nLastCol, nLastRow);
+ else
+ GetLastDataPos(nLastCol, nLastRow);
+
+ std::vector< sc::ColumnBlockConstPosition > blockPos;
+ do
+ {
+ bFound = Search(rSearchItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
+ if (bFound)
+ {
+ bEverFound = true;
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
+ }
+ }
+ while (bFound);
+
+ return bEverFound;
+}
+
+void ScTable::UpdateSearchItemAddressForReplace( const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow )
+{
+ if (rSearchItem.GetBackward())
+ {
+ if (rSearchItem.GetRowDirection())
+ rCol += 1;
+ else
+ rRow += 1;
+ }
+ else
+ {
+ if (rSearchItem.GetRowDirection())
+ rCol -= 1;
+ else
+ rRow -= 1;
+ }
+}
+
+bool ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nCol = rCol;
+ SCROW nRow = rRow;
+
+ UpdateSearchItemAddressForReplace( rSearchItem, nCol, nRow );
+ bool bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc);
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return bFound;
+}
+
+bool ScTable::ReplaceAll(
+ const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
+ OUString& rUndoStr, ScDocument* pUndoDoc, bool& bMatchedRangesWereClamped)
+{
+ SCCOL nCol = 0;
+ SCROW nRow = -1;
+
+ SCCOL nLastCol;
+ SCROW nLastRow;
+ if (rSearchItem.GetCellType() == SvxSearchCellType::NOTE)
+ GetCellArea( nLastCol, nLastRow);
+ else
+ GetLastDataPos(nLastCol, nLastRow);
+
+ // tdf#92160 - columnar replace is faster, and more memory efficient.
+ SvxSearchItem aCopyItem(rSearchItem);
+ aCopyItem.SetRowDirection(false);
+
+ std::vector< sc::ColumnBlockConstPosition > blockPos;
+ bool bEverFound = false;
+ while (true)
+ {
+ bool bFound = Search(aCopyItem, nCol, nRow, nLastCol, nLastRow, rMark, rUndoStr, pUndoDoc, blockPos);
+
+ if (bFound)
+ {
+ bEverFound = true;
+ // The combination of this loop and the Join() algorithm is O(n^2),
+ // so just give up if the list gets too big.
+ if (rMatchedRanges.size() < 1000)
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
+ else
+ bMatchedRangesWereClamped = true;
+ }
+ else
+ break;
+ }
+ return bEverFound;
+}
+
+bool ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark)
+{
+ const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetSearchString(), SfxStyleFamily::Para ));
+
+ SCCOL nCol = rCol;
+ SCROW nRow = rRow;
+ bool bFound = false;
+
+ bool bSelect = rSearchItem.GetSelection();
+ bool bRows = rSearchItem.GetRowDirection();
+ bool bBack = rSearchItem.GetBackward();
+ short nAdd = bBack ? -1 : 1;
+
+ if (bRows) // by row
+ {
+ if ( !IsColValid( nCol ) )
+ {
+ SAL_WARN( "sc.core", "SearchStyle: bad column " << nCol);
+ return false;
+ }
+ nRow += nAdd;
+ do
+ {
+ SCROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark );
+ if (!ValidRow(nNextRow))
+ {
+ nRow = bBack ? rDocument.MaxRow() : 0;
+ nCol = sal::static_int_cast<SCCOL>( nCol + nAdd );
+ }
+ else
+ {
+ nRow = nNextRow;
+ bFound = true;
+ }
+ }
+ while ( !bFound && IsColValid( nCol ) );
+ }
+ else // by column
+ {
+ SCCOL aColSize = aCol.size();
+ std::vector< SCROW > nNextRows ( aColSize );
+ SCCOL i;
+ for (i=0; i < aColSize; ++i)
+ {
+ SCROW nSRow = nRow;
+ if (bBack)
+ {
+ if (i>=nCol) --nSRow;
+ }
+ else
+ {
+ if (i<=nCol) ++nSRow;
+ }
+ nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark );
+ }
+ if (bBack) // backwards
+ {
+ nRow = -1;
+ for (i = aColSize - 1; i>=0; --i)
+ if (nNextRows[i]>nRow)
+ {
+ nCol = i;
+ nRow = nNextRows[i];
+ bFound = true;
+ }
+ }
+ else // forwards
+ {
+ nRow = rDocument.MaxRow()+1;
+ for (i=0; i < aColSize; ++i)
+ if (nNextRows[i]<nRow)
+ {
+ nCol = i;
+ nRow = nNextRows[i];
+ bFound = true;
+ }
+ }
+ }
+
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ }
+ return bFound;
+}
+
+//TODO: return single Pattern for Undo
+
+bool ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow,
+ const ScMarkData& rMark, bool bIsUndo)
+{
+ bool bRet;
+ if (bIsUndo)
+ bRet = true;
+ else
+ bRet = SearchStyle(rSearchItem, rCol, rRow, rMark);
+ if (bRet)
+ {
+ const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetReplaceString(), SfxStyleFamily::Para ));
+
+ if (pReplaceStyle)
+ ApplyStyle( rCol, rRow, pReplaceStyle );
+ else
+ {
+ OSL_FAIL("pReplaceStyle==0");
+ }
+ }
+
+ return bRet;
+}
+
+bool ScTable::SearchAllStyle(
+ const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges)
+{
+ const ScStyleSheet* pSearchStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetSearchString(), SfxStyleFamily::Para ));
+ bool bSelect = rSearchItem.GetSelection();
+ bool bBack = rSearchItem.GetBackward();
+ bool bEverFound = false;
+
+ for (SCCOL i=0; i < aCol.size(); ++i)
+ {
+ bool bFound = true;
+ SCROW nRow = 0;
+ SCROW nEndRow;
+ while (bFound && nRow <= rDocument.MaxRow())
+ {
+ bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark );
+ if (bFound)
+ {
+ if (nEndRow<nRow)
+ std::swap( nRow, nEndRow );
+ rMatchedRanges.Join(ScRange(i, nRow, nTab, i, nEndRow, nTab));
+ nRow = nEndRow + 1;
+ bEverFound = true;
+ }
+ }
+ }
+
+ return bEverFound;
+}
+
+bool ScTable::ReplaceAllStyle(
+ const SvxSearchItem& rSearchItem, const ScMarkData& rMark, ScRangeList& rMatchedRanges,
+ ScDocument* pUndoDoc)
+{
+ bool bRet = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
+ if (bRet)
+ {
+ const ScStyleSheet* pReplaceStyle = static_cast<const ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->Find(
+ rSearchItem.GetReplaceString(), SfxStyleFamily::Para ));
+
+ if (pReplaceStyle)
+ {
+ if (pUndoDoc)
+ rDocument.CopyToDocument(0, 0 ,nTab, rDocument.MaxCol(),rDocument.MaxRow(),nTab,
+ InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark);
+ ApplySelectionStyle( *pReplaceStyle, rMark );
+ }
+ else
+ {
+ OSL_FAIL("pReplaceStyle==0");
+ }
+ }
+
+ return bRet;
+}
+
+bool ScTable::SearchAndReplace(
+ const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc, bool& bMatchedRangesWereClamped)
+{
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ bool bFound = false;
+ if ( ValidColRow(rCol, rRow) ||
+ ((nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE) &&
+ (((rCol == GetDoc().GetMaxColCount() || rCol == -1) && ValidRow(rRow)) ||
+ ((rRow == GetDoc().GetMaxRowCount() || rRow == -1) && ValidCol(rCol))
+ )
+ )
+ )
+ {
+ bool bStyles = rSearchItem.GetPattern();
+ if (bStyles)
+ {
+ if (nCommand == SvxSearchCmd::FIND)
+ bFound = SearchStyle(rSearchItem, rCol, rRow, rMark);
+ else if (nCommand == SvxSearchCmd::REPLACE)
+ bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, false);
+ else if (nCommand == SvxSearchCmd::FIND_ALL)
+ bFound = SearchAllStyle(rSearchItem, rMark, rMatchedRanges);
+ else if (nCommand == SvxSearchCmd::REPLACE_ALL)
+ bFound = ReplaceAllStyle(rSearchItem, rMark, rMatchedRanges, pUndoDoc);
+ }
+ else if (ScDocument::IsEmptyCellSearch( rSearchItem))
+ {
+ // Search for empty cells.
+ bFound = SearchAndReplaceEmptyCells(rSearchItem, rCol, rRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ }
+ else
+ {
+ // SearchParam no longer needed - SearchOptions contains all settings
+ i18nutil::SearchOptions2 aSearchOptions = rSearchItem.GetSearchOptions();
+ aSearchOptions.Locale = ScGlobal::GetLocale();
+
+ // reflect UseAsianOptions flag in SearchOptions
+ // (use only ignore case and width if asian options are disabled).
+ // This is also done in SvxSearchDialog CommandHdl, but not in API object.
+ if ( !rSearchItem.IsUseAsianOptions() )
+ aSearchOptions.transliterateFlags &=
+ TransliterationFlags::IGNORE_CASE |
+ TransliterationFlags::IGNORE_WIDTH;
+
+ pSearchText.reset( new utl::TextSearch( aSearchOptions ) );
+
+ if (nCommand == SvxSearchCmd::FIND)
+ bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
+ else if (nCommand == SvxSearchCmd::FIND_ALL)
+ bFound = SearchAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ else if (nCommand == SvxSearchCmd::REPLACE)
+ bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc);
+ else if (nCommand == SvxSearchCmd::REPLACE_ALL)
+ bFound = ReplaceAll(rSearchItem, rMark, rMatchedRanges, rUndoStr, pUndoDoc, bMatchedRangesWereClamped);
+
+ pSearchText.reset();
+ }
+ }
+ return bFound;
+}
+
+bool ScTable::SearchAndReplaceEmptyCells(
+ const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ SCCOL nColStart, nColEnd;
+ SCROW nRowStart, nRowEnd;
+ GetFirstDataPos(nColStart, nRowStart);
+ GetLastDataPos(nColEnd, nRowEnd);
+
+ ScRangeList aRanges(ScRange(nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab));
+
+ if (rSearchItem.GetSelection())
+ {
+ // current selection only.
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ // There is no selection. Bail out.
+ return false;
+
+ ScRangeList aMarkedRanges, aNewRanges;
+ rMark.FillRangeListWithMarks(&aMarkedRanges, true);
+ for ( size_t i = 0, n = aMarkedRanges.size(); i < n; ++i )
+ {
+ ScRange & rRange = aMarkedRanges[ i ];
+ if (rRange.aStart.Col() > nColEnd || rRange.aStart.Row() > nRowEnd || rRange.aEnd.Col() < nColStart || rRange.aEnd.Row() < nRowStart)
+ // This range is outside the data area. Skip it.
+ continue;
+
+ // Shrink the range into data area only.
+ if (rRange.aStart.Col() < nColStart)
+ rRange.aStart.SetCol(nColStart);
+ if (rRange.aStart.Row() < nRowStart)
+ rRange.aStart.SetRow(nRowStart);
+
+ if (rRange.aEnd.Col() > nColEnd)
+ rRange.aEnd.SetCol(nColEnd);
+ if (rRange.aEnd.Row() > nRowEnd)
+ rRange.aEnd.SetRow(nRowEnd);
+
+ aNewRanges.push_back(rRange);
+ }
+ aRanges = aNewRanges;
+ }
+
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ if (nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE)
+ {
+ if (rSearchItem.GetBackward())
+ {
+ for ( size_t i = aRanges.size(); i > 0; --i )
+ {
+ const ScRange & rRange = aRanges[ i - 1 ];
+ if (SearchRangeForEmptyCell(rRange, rSearchItem, rCol, rRow, rUndoStr))
+ return true;
+ }
+ }
+ else
+ {
+ for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
+ {
+ const ScRange & rRange = aRanges[ i ];
+ if (SearchRangeForEmptyCell(rRange, rSearchItem, rCol, rRow, rUndoStr))
+ return true;
+ }
+ }
+ }
+ else if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
+ {
+ bool bFound = false;
+ for ( size_t i = 0, nListSize = aRanges.size(); i < nListSize; ++i )
+ {
+ ScRange const & rRange = aRanges[ i ];
+ bFound |= SearchRangeForAllEmptyCells(rRange, rSearchItem, rMatchedRanges, rUndoStr, pUndoDoc);
+ }
+ return bFound;
+ }
+ return false;
+}
+
+namespace {
+
+bool lcl_maybeReplaceCellString(
+ ScColumn& rColObj, SCCOL& rCol, SCROW& rRow, OUString& rUndoStr, SCCOL nCol, SCROW nRow, const SvxSearchItem& rSearchItem)
+{
+ ScRefCellValue aCell = rColObj.GetCellValue(nRow);
+ if (aCell.isEmpty())
+ {
+ // empty cell found.
+ rCol = nCol;
+ rRow = nRow;
+ if (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE &&
+ !rSearchItem.GetReplaceString().isEmpty())
+ {
+ rColObj.SetRawString(nRow, rSearchItem.GetReplaceString());
+ rUndoStr.clear();
+ }
+ return true;
+ }
+ return false;
+}
+
+}
+
+bool ScTable::SearchRangeForEmptyCell(
+ const ScRange& rRange, const SvxSearchItem& rSearchItem,
+ SCCOL& rCol, SCROW& rRow, OUString& rUndoStr)
+{
+ SvxSearchCmd nCmd = rSearchItem.GetCommand();
+ bool bSkipFiltered = rSearchItem.IsSearchFiltered();
+ if (rSearchItem.GetBackward())
+ {
+ // backward search
+ if (rSearchItem.GetRowDirection())
+ {
+ // row direction.
+ SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ SCROW nBeginRow = std::min(rRange.aEnd.Row(), rRow);
+ for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+ if (nRow < rRange.aStart.Row())
+ break;
+
+ SCCOL nBeginCol = rRange.aEnd.Col();
+ if (nRow == rRow && nBeginCol >= rCol)
+ // always start from one cell before the cursor.
+ nBeginCol = rCol - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+
+ for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
+ {
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // column direction.
+ SCCOL nBeginCol = std::min(rRange.aEnd.Col(), rCol);
+ for (SCCOL nCol = nBeginCol; nCol >= rRange.aStart.Col(); --nCol)
+ {
+ SCROW nLastNonFilteredRow = rDocument.MaxRow() + 1;
+ SCROW nBeginRow = rRange.aEnd.Row();
+ if (nCol == rCol && nBeginRow >= rRow)
+ // always start from one cell before the cursor.
+ nBeginRow = rRow - (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+ for (SCROW nRow = nBeginRow; nRow >= rRange.aStart.Row(); --nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, false);
+ if (nRow < rRange.aStart.Row())
+ break;
+
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // forward search
+ if (rSearchItem.GetRowDirection())
+ {
+ // row direction.
+ SCROW nLastNonFilteredRow = -1;
+ SCROW nBeginRow = rRange.aStart.Row() < rRow ? rRow : rRange.aStart.Row();
+ for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+ if (nRow > rRange.aEnd.Row())
+ break;
+
+ SCCOL nBeginCol = rRange.aStart.Col();
+ if (nRow == rRow && nBeginCol <= rCol)
+ // always start from one cell past the cursor.
+ nBeginCol = rCol + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+ for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // column direction.
+ SCCOL nBeginCol = rRange.aStart.Col() < rCol ? rCol : rRange.aStart.Col();
+ for (SCCOL nCol = nBeginCol; nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ SCROW nLastNonFilteredRow = -1;
+ SCROW nBeginRow = rRange.aStart.Row();
+ if (nCol == rCol && nBeginRow <= rRow)
+ // always start from one cell past the cursor.
+ nBeginRow = rRow + (nCmd == SvxSearchCmd::FIND ? 1 : 0);
+ for (SCROW nRow = nBeginRow; nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+ if (nRow > rRange.aEnd.Row())
+ break;
+
+ if (lcl_maybeReplaceCellString(aCol[nCol], rCol, rRow, rUndoStr, nCol, nRow, rSearchItem))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool ScTable::SearchRangeForAllEmptyCells(
+ const ScRange& rRange, const SvxSearchItem& rSearchItem,
+ ScRangeList& rMatchedRanges, OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ bool bFound = false;
+ bool bReplace = (rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL) &&
+ !rSearchItem.GetReplaceString().isEmpty();
+ bool bSkipFiltered = rSearchItem.IsSearchFiltered();
+
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ SCROW nLastNonFilteredRow = -1;
+ if (aCol[nCol].IsEmptyData())
+ {
+ // The entire column is empty.
+ const SCROW nEndRow = rRange.aEnd.Row();
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; ++nRow)
+ {
+ SCROW nLastRow;
+ const bool bFiltered = RowFiltered(nRow, nullptr, &nLastRow);
+ if (nLastRow > nEndRow)
+ nLastRow = nEndRow;
+ if (!bFiltered)
+ {
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab, nCol, nLastRow, nTab));
+ if (bReplace)
+ {
+ const OUString& rNewStr = rSearchItem.GetReplaceString();
+ for (SCROW i = nRow; i <= nLastRow; ++i)
+ {
+ aCol[nCol].SetRawString(i, rNewStr);
+ if (pUndoDoc)
+ {
+ // TODO: I'm using a string cell with empty content to
+ // trigger deletion of cell instance on undo. Maybe I
+ // should create a new cell type for this?
+ pUndoDoc->SetString(ScAddress(nCol, i, nTab), OUString());
+ }
+ }
+ rUndoStr.clear();
+ }
+ }
+
+ nRow = nLastRow; // move to the last filtered row.
+ }
+ bFound = true;
+ continue;
+ }
+
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ if (bSkipFiltered)
+ SkipFilteredRows(nRow, nLastNonFilteredRow, true);
+ if (nRow > rRange.aEnd.Row())
+ break;
+
+ ScRefCellValue aCell = aCol[nCol].GetCellValue(nRow);
+ if (aCell.isEmpty())
+ {
+ // empty cell found
+ rMatchedRanges.Join(ScRange(nCol, nRow, nTab));
+ bFound = true;
+
+ if (bReplace)
+ {
+ aCol[nCol].SetRawString(nRow, rSearchItem.GetReplaceString());
+ if (pUndoDoc)
+ {
+ // TODO: I'm using a string cell with empty content to
+ // trigger deletion of cell instance on undo. Maybe I
+ // should create a new cell type for this?
+ pUndoDoc->SetString(ScAddress(nCol, nRow, nTab), OUString());
+ }
+ }
+ }
+ }
+ }
+ return bFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
new file mode 100644
index 0000000000..f9e7a878c8
--- /dev/null
+++ b/sc/source/core/data/table7.cxx
@@ -0,0 +1,664 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <table.hxx>
+#include <clipcontext.hxx>
+#include <document.hxx>
+#include <clipparam.hxx>
+#include <segmenttree.hxx>
+#include <sharedformula.hxx>
+#include <cellvalues.hxx>
+#include <olinetab.hxx>
+#include <tabprotection.hxx>
+#include <columniterator.hxx>
+#include <drwlayer.hxx>
+#include <compressedarray.hxx>
+
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+
+bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const
+{
+ if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount() )
+ return false;
+
+ return aCol[nCol].IsMerged(nRow);
+}
+
+sc::MultiDataCellState ScTable::HasMultipleDataCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
+{
+ if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
+ return sc::MultiDataCellState();
+
+ if (nCol1 > nCol2 || nRow1 > nRow2)
+ // invalid range.
+ return sc::MultiDataCellState();
+
+ if (aCol.empty())
+ return sc::MultiDataCellState(sc::MultiDataCellState::Empty);
+
+ auto setFirstCell = []( sc::MultiDataCellState& rRet, SCCOL nCurCol, SCROW nCurRow )
+ {
+ if (rRet.mnCol1 < 0)
+ {
+ // First cell not yet set. Set it.
+ rRet.mnCol1 = nCurCol;
+ rRet.mnRow1 = nCurRow;
+ }
+ };
+
+ SCCOL nMaxCol = aCol.size()-1;
+ bool bHasOne = false;
+ sc::MultiDataCellState aRet(sc::MultiDataCellState::Empty);
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2 && nCol <= nMaxCol; ++nCol)
+ {
+ SCROW nFirstDataRow = -1;
+ switch (aCol[nCol].HasDataCellsInRange(nRow1, nRow2, &nFirstDataRow))
+ {
+ case sc::MultiDataCellState::HasOneCell:
+ {
+ setFirstCell(aRet, nCol, nFirstDataRow);
+
+ if (bHasOne)
+ {
+ // We've already found one data cell in another column.
+ aRet.meState = sc::MultiDataCellState::HasMultipleCells;
+ return aRet;
+ }
+ bHasOne = true;
+ break;
+ }
+ case sc::MultiDataCellState::HasMultipleCells:
+ {
+ setFirstCell(aRet, nCol, nFirstDataRow);
+
+ aRet.meState = sc::MultiDataCellState::HasMultipleCells;
+ return aRet;
+ }
+ case sc::MultiDataCellState::Empty:
+ default:
+ ;
+ }
+ }
+
+ if (bHasOne)
+ aRet.meState = sc::MultiDataCellState::HasOneCell;
+
+ return aRet;
+}
+
+void ScTable::DeleteBeforeCopyFromClip(
+ sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
+ if (!ValidCol(aRange.mnCol1) || !ValidCol(aRange.mnCol2))
+ return;
+
+ // Pass some stuff to the columns via context.
+ rCxt.setTableProtected(IsProtected());
+ rCxt.setCondFormatList(mpCondFormatList.get());
+
+ ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
+ SCCOL nClipCol = aClipRange.aStart.Col();
+ {
+ const SCCOL nMaxCol2 = std::min<SCCOL>( aRange.mnCol2, aCol.size() - 1 );
+ for (SCCOL nCol = aRange.mnCol1; nCol <= nMaxCol2; ++nCol, ++nClipCol)
+ {
+ if (nClipCol > aClipRange.aEnd.Col())
+ nClipCol = aClipRange.aStart.Col(); // loop through columns.
+
+ const ScColumn& rClipCol = const_cast<ScTable&>(rClipTab).CreateColumnIfNotExists(nClipCol);
+ aCol[nCol].DeleteBeforeCopyFromClip(rCxt, rClipCol, rBroadcastSpans);
+ }
+ }
+
+ SetStreamValid(false);
+}
+
+void ScTable::CopyOneCellFromClip(
+ sc::CopyFromClipContext& rCxt, const SCCOL nCol1, const SCROW nRow1, const SCCOL nCol2, const SCROW nRow2, const SCROW nSrcRow, const ScTable* pSrcTab )
+{
+ ScRange aSrcRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
+ SCCOL nSrcColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ SCCOL nColOffset = nCol - nCol1;
+ nColOffset = nColOffset % nSrcColSize;
+ assert(nColOffset >= 0);
+ CreateColumnIfNotExists(nCol).CopyOneCellFromClip(rCxt, nRow1, nRow2, nColOffset);
+
+ if (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB)
+ {
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ CopyConditionalFormat(nCol, nRow, nCol, nRow, nCol - aSrcRange.aStart.Col() - nColOffset,
+ nRow - nSrcRow, pSrcTab);
+ }
+ }
+
+ if (nCol1 == 0 && nCol2 == rDocument.MaxCol() && mpRowHeights)
+ {
+ mpRowHeights->setValue(nRow1, nRow2, pSrcTab->GetOriginalHeight(nSrcRow));
+
+ if (pRowFlags && pSrcTab->pRowFlags) {
+ if (pSrcTab->pRowFlags->GetValue(nSrcRow) & CRFlags::ManualSize)
+ pRowFlags->OrValue(nRow1, CRFlags::ManualSize);
+ else
+ pRowFlags->AndValue(nRow1, ~CRFlags::ManualSize);
+ }
+ }
+
+ // Copy graphics over too
+ bool bCopyGraphics
+ = (rCxt.getInsertFlag() & InsertDeleteFlags::OBJECTS) != InsertDeleteFlags::NONE;
+ if (!(bCopyGraphics && rCxt.getClipDoc()->mpDrawLayer))
+ return;
+
+ ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
+ OSL_ENSURE(pDrawLayer, "No drawing layer");
+ if (pDrawLayer)
+ {
+ const ScAddress aSrcStartPos
+ = rCxt.getClipDoc()->GetClipParam().getWholeRange().aStart;
+ const ScAddress aSrcEndPos = rCxt.getClipDoc()->GetClipParam().getWholeRange().aEnd;
+ ScRange aSourceRange(aSrcStartPos, aSrcEndPos);
+ ScRange aDestRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
+ pDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), aSrcStartPos.Tab(),
+ aSourceRange, ScAddress(nCol1, nRow1, nTab), aDestRange);
+ }
+}
+
+void ScTable::SetValues( const SCCOL nCol, const SCROW nRow, const std::vector<double>& rVals )
+{
+ if (!ValidCol(nCol))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetValues(nRow, rVals);
+}
+
+void ScTable::TransferCellValuesTo( const SCCOL nCol, SCROW nRow, size_t nLen, sc::CellValues& rDest )
+{
+ if (!ValidCol(nCol))
+ return;
+
+ CreateColumnIfNotExists(nCol).TransferCellValuesTo(nRow, nLen, rDest);
+}
+
+void ScTable::CopyCellValuesFrom( const SCCOL nCol, SCROW nRow, const sc::CellValues& rSrc )
+{
+ if (!ValidCol(nCol))
+ return;
+
+ CreateColumnIfNotExists(nCol).CopyCellValuesFrom(nRow, rSrc);
+}
+
+void ScTable::ConvertFormulaToValue(
+ sc::EndListeningContext& rCxt, const SCCOL nCol1, const SCROW nRow1, const SCCOL nCol2, const SCROW nRow2,
+ sc::TableValues* pUndo )
+{
+ if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol1 > nCol2)
+ return;
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ CreateColumnIfNotExists(nCol).ConvertFormulaToValue(rCxt, nRow1, nRow2, pUndo);
+}
+
+void ScTable::SwapNonEmpty(
+ sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
+{
+ const ScRange& rRange = rValues.getRange();
+ assert(rRange.IsValid());
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ CreateColumnIfNotExists(nCol).SwapNonEmpty(rValues, rStartCxt, rEndCxt);
+}
+
+void ScTable::PreprocessRangeNameUpdate(
+ sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].PreprocessRangeNameUpdate(rEndListenCxt, rCompileCxt);
+}
+
+void ScTable::PreprocessDBDataUpdate(
+ sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].PreprocessDBDataUpdate(rEndListenCxt, rCompileCxt);
+}
+
+void ScTable::CompileHybridFormula(
+ sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
+{
+ for (SCCOL i = 0; i < aCol.size(); ++i)
+ aCol[i].CompileHybridFormula(rStartListenCxt, rCompileCxt);
+}
+
+void ScTable::UpdateScriptTypes( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2 )
+{
+ if (!IsColValid(nCol1) || !ValidCol(nCol2) || nCol1 > nCol2)
+ return;
+
+ const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
+
+ for (SCCOL nCol = nCol1; nCol <= nMaxCol2; ++nCol)
+ aCol[nCol].UpdateScriptTypes(nRow1, nRow2);
+}
+
+bool ScTable::HasUniformRowHeight( SCROW nRow1, SCROW nRow2 ) const
+{
+ if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
+ return false;
+
+ ScFlatUInt16RowSegments::RangeData aData;
+ if (!mpRowHeights->getRangeData(nRow1, aData))
+ // Search failed.
+ return false;
+
+ return nRow2 <= aData.mnRow2;
+}
+
+void ScTable::SplitFormulaGroups( SCCOL nCol, std::vector<SCROW>& rRows )
+{
+ if (!IsColValid(nCol))
+ return;
+
+ sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), aCol[nCol].maCells, rRows);
+}
+
+void ScTable::UnshareFormulaCells( SCCOL nCol, std::vector<SCROW>& rRows )
+{
+ if (!IsColValid(nCol))
+ return;
+
+ sc::SharedFormulaUtil::unshareFormulaCells(rDocument, aCol[nCol].maCells, rRows);
+}
+
+void ScTable::RegroupFormulaCells( SCCOL nCol )
+{
+ if (!IsColValid(nCol))
+ return;
+
+ aCol[nCol].RegroupFormulaCells();
+}
+
+bool ScTable::HasFormulaCell( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2 ) const
+{
+ if (nCol2 < nCol1 || !IsColValid(nCol1) || !ValidCol(nCol2))
+ return false;
+
+ const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
+
+ for (SCCOL nCol = nCol1; nCol <= nMaxCol2; ++nCol)
+ if (aCol[nCol].HasFormulaCell(nRow1, nRow2))
+ return true;
+
+ return false;
+}
+
+void ScTable::EndListeningIntersectedGroup(
+ sc::EndListeningContext& rCxt, SCCOL nCol, SCROW nRow, std::vector<ScAddress>* pGroupPos )
+{
+ if (!IsColValid(nCol))
+ return;
+
+ aCol[nCol].EndListeningIntersectedGroup(rCxt, nRow, pGroupPos);
+}
+
+void ScTable::EndListeningIntersectedGroups(
+ sc::EndListeningContext& rCxt, const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2,
+ std::vector<ScAddress>* pGroupPos )
+{
+ if (nCol2 < nCol1 || !IsColValid(nCol1) || !ValidCol(nCol2))
+ return;
+
+ for (SCCOL nCol : GetAllocatedColumnsRange(nCol1, nCol2))
+ aCol[nCol].EndListeningIntersectedGroups(rCxt, nRow1, nRow2, pGroupPos);
+}
+
+void ScTable::EndListeningGroup( sc::EndListeningContext& rCxt, const SCCOL nCol, SCROW nRow )
+{
+ if (!IsColValid(nCol))
+ return;
+
+ aCol[nCol].EndListeningGroup(rCxt, nRow);
+}
+
+void ScTable::SetNeedsListeningGroup( SCCOL nCol, SCROW nRow )
+{
+ if (!ValidCol(nCol))
+ return;
+
+ CreateColumnIfNotExists(nCol).SetNeedsListeningGroup(nRow);
+}
+
+bool ScTable::IsEditActionAllowed(
+ sc::ColRowEditAction eAction, SCCOLROW nStart, SCCOLROW nEnd ) const
+{
+ if (!IsProtected())
+ {
+ SCCOL nCol1 = 0, nCol2 = aCol.size() - 1;
+ SCROW nRow1 = 0, nRow2 = rDocument.MaxRow();
+
+ switch (eAction)
+ {
+ case sc::ColRowEditAction::InsertColumnsBefore:
+ case sc::ColRowEditAction::InsertColumnsAfter:
+ case sc::ColRowEditAction::DeleteColumns:
+ {
+ nCol1 = nStart;
+ nCol2 = nEnd;
+ break;
+ }
+ case sc::ColRowEditAction::InsertRowsBefore:
+ case sc::ColRowEditAction::InsertRowsAfter:
+ case sc::ColRowEditAction::DeleteRows:
+ {
+ nRow1 = nStart;
+ nRow2 = nEnd;
+ break;
+ }
+ default:
+ ;
+ }
+
+ return IsBlockEditable(nCol1, nRow1, nCol2, nRow2, nullptr);
+ }
+
+ if (IsScenario())
+ // TODO: I don't even know what this scenario thingie is. Perhaps we
+ // should check it against the scenario ranges?
+ return false;
+
+ assert(pTabProtection);
+
+ switch (eAction)
+ {
+ case sc::ColRowEditAction::InsertColumnsBefore:
+ case sc::ColRowEditAction::InsertColumnsAfter:
+ {
+ // TODO: improve the matrix range handling for the insert-before action.
+ if (HasBlockMatrixFragment(nStart, 0, nEnd, rDocument.MaxRow()))
+ return false;
+
+ return pTabProtection->isOptionEnabled(ScTableProtection::INSERT_COLUMNS);
+ }
+ case sc::ColRowEditAction::InsertRowsBefore:
+ case sc::ColRowEditAction::InsertRowsAfter:
+ {
+ // TODO: improve the matrix range handling for the insert-before action.
+ if (HasBlockMatrixFragment(0, nStart, rDocument.MaxCol(), nEnd))
+ return false;
+
+ return pTabProtection->isOptionEnabled(ScTableProtection::INSERT_ROWS);
+ }
+ case sc::ColRowEditAction::DeleteColumns:
+ {
+ if (!pTabProtection->isOptionEnabled(ScTableProtection::DELETE_COLUMNS))
+ return false;
+
+ return !HasAttrib(nStart, 0, nEnd, rDocument.MaxRow(), HasAttrFlags::Protected);
+ }
+ case sc::ColRowEditAction::DeleteRows:
+ {
+ if (!pTabProtection->isOptionEnabled(ScTableProtection::DELETE_ROWS))
+ return false;
+
+ return !HasAttrib(0, nStart, rDocument.MaxCol(), nEnd, HasAttrFlags::Protected);
+ }
+ default:
+ ;
+ }
+
+ return false;
+}
+
+std::optional<sc::ColumnIterator> ScTable::GetColumnIterator( SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
+{
+ if (!ValidCol(nCol))
+ return {};
+
+ return const_cast<ScTable*>(this)->CreateColumnIfNotExists(nCol).GetColumnIterator(nRow1, nRow2);
+}
+
+bool ScTable::EnsureFormulaCellResults( const SCCOL nCol1, SCROW nRow1, const SCCOL nCol2, SCROW nRow2, bool bSkipRunning )
+{
+ if (nCol2 < nCol1 || !IsColValid(nCol1) || !ValidCol(nCol2))
+ return false;
+
+ const SCCOL nMaxCol2 = std::min<SCCOL>( nCol2, aCol.size() - 1 );
+
+ bool bAnyDirty = false;
+
+ for (SCCOL nCol = nCol1; nCol <= nMaxCol2; ++nCol)
+ {
+ bool bRet = aCol[nCol].EnsureFormulaCellResults(nRow1, nRow2, bSkipRunning);
+ bAnyDirty = bAnyDirty || bRet;
+ }
+
+ return bAnyDirty;
+}
+
+void ScTable::finalizeOutlineImport()
+{
+ if (pOutlineTable && pRowFlags)
+ {
+ pOutlineTable->GetRowArray().finalizeImport(*this);
+ }
+}
+
+void ScTable::StoreToCache(SvStream& rStrm) const
+{
+ SCCOL nStartCol = 0;
+ SCCOL nEndCol = rDocument.MaxCol();
+ SCROW nStartRow = 0;
+ SCROW nEndRow = rDocument.MaxRow();
+
+ GetDataArea(nStartCol, nStartRow, nEndCol, nEndRow, false, false);
+
+ rStrm.WriteUInt64(nEndCol + 1);
+ for (SCCOL nCol = 0; nCol <= nEndCol; ++nCol)
+ {
+ aCol[nCol].StoreToCache(rStrm);
+ }
+}
+
+void ScTable::RestoreFromCache(SvStream& rStrm)
+{
+ sal_uInt64 nCols = 0;
+ rStrm.ReadUInt64(nCols);
+ for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(nCols); ++nCol)
+ {
+ aCol[nCol].RestoreFromCache(rStrm);
+ }
+}
+
+OString ScTable::dumpSheetGeomData(bool bColumns, SheetGeomType eGeomType)
+{
+ switch (eGeomType)
+ {
+ case SheetGeomType::SIZES:
+ // returns a non-empty space separated list of spans with trailing space.
+ // The format of the span is <size of any row/col in the span in print twips>:<last row/col of the span>
+ // Example (for columns with three spans if MAXCOL is 1023): "1280:3 1049:50 1280:1023"
+ return dumpColumnRowSizes(bColumns);
+ case SheetGeomType::HIDDEN:
+ // returns a non-empty space separated list of spans with trailing space.
+ // The format of the span is:
+ // 1) First span: <1 (span is hidden) / 0 (not hidden)>:<last row/col of the span>
+ // 2) Rest of the spans: <last row/col of the span>
+ // The hidden state of the spans after the first can be inferred from the first span's flag as no adjacent
+ // spans can have the same state by definition of span.
+ return dumpHiddenFiltered(bColumns, /*bHidden*/ true);
+ case SheetGeomType::FILTERED:
+ // has exactly the same format as 'hidden'.
+ return dumpHiddenFiltered(bColumns, /*bHidden*/ false);
+ case SheetGeomType::GROUPS:
+ // returns a space separated list of 'levels' with trailing space.
+ // A 'level' is a comma separated list of groups(outline entries) with trailing comma.
+ // format of a group is:
+ // <start row/col of group>:<number of rows/cols in the group>:<1/0(group is hidden?)>:<1/0(control is visible?)>
+ return dumpColumnRowGroups(bColumns);
+ default:
+ ;
+ }
+
+ return ""_ostr;
+}
+
+OString ScTable::dumpColumnRowSizes(bool bColumns)
+{
+ // If the data-structures are not available, just report that all
+ // rows/cols have the default sizes.
+ static const OString aDefaultForCols
+ = OString::number(STD_COL_WIDTH) + ":" + OString::number(GetDoc().MaxCol()) + " ";
+ static const OString aDefaultForRows
+ = OString::number(GetOptimalMinRowHeight()) + ":" + OString::number(GetDoc().MaxRow()) + " ";
+
+ // ScCompressedArray is a template class and we don't want to impose
+ // the restriction that its value type should be string serializable,
+ // instead just operate on the specialized object.
+ typedef ScCompressedArray<SCCOL, sal_uInt16> ColWidthsType;
+ auto dumpColWidths = [this](const ColWidthsType& rWidths) -> OString {
+ OString aOutput;
+ OString aSegment;
+ SCCOL nStartCol = 0;
+ const SCCOL nMaxCol = std::min(rWidths.GetLastPos(), GetDoc().MaxCol());
+ size_t nDummy = 0;
+ while (nStartCol <= nMaxCol)
+ {
+ SCCOL nEndCol;
+ sal_uInt16 nWidth = rWidths.GetValue(nStartCol, nDummy, nEndCol);
+ // The last span nEndCol is always MAXCOL+1 for some reason, and we don't want that.
+ if (nEndCol > nMaxCol)
+ nEndCol = nMaxCol;
+ aSegment = OString::number(nWidth) + ":" + OString::number(nEndCol) + " ";
+ aOutput += aSegment;
+ nStartCol = nEndCol + 1;
+ }
+
+ return aOutput;
+ };
+
+ if (bColumns)
+ return mpColWidth ? dumpColWidths(*mpColWidth) : aDefaultForCols;
+
+ return mpRowHeights ? mpRowHeights->dumpAsString() : aDefaultForRows;
+}
+
+OString ScTable::dumpHiddenFiltered(bool bColumns, bool bHidden)
+{
+ // defaults to no hidden/filtered row/cols.
+ static const OString aDefaultForCols = "0:" + OString::number(GetDoc().MaxCol()) + " ";
+ static const OString aDefaultForRows = "0:" + OString::number(GetDoc().MaxRow()) + " ";
+
+ if (bHidden)
+ {
+ if (bColumns)
+ return mpHiddenCols ? mpHiddenCols->dumpAsString() : aDefaultForCols;
+
+ return mpHiddenRows ? mpHiddenRows->dumpAsString() : aDefaultForRows;
+ }
+
+ if (bColumns)
+ return mpFilteredCols ? mpFilteredCols->dumpAsString() : aDefaultForCols;
+
+ return mpFilteredRows ? mpFilteredRows->dumpAsString() : aDefaultForRows;
+}
+
+OString ScTable::dumpColumnRowGroups(bool bColumns) const
+{
+ if (!pOutlineTable)
+ return ""_ostr;
+
+ if (bColumns)
+ return pOutlineTable->GetColArray().dumpAsString();
+
+ return pOutlineTable->GetRowArray().dumpAsString();
+}
+
+SCCOL ScTable::GetLOKFreezeCol() const
+{
+ return maLOKFreezeCell.Col();
+}
+
+SCROW ScTable::GetLOKFreezeRow() const
+{
+ return maLOKFreezeCell.Row();
+}
+
+bool ScTable::SetLOKFreezeCol(SCCOL nFreezeCol)
+{
+ if (!ValidCol(nFreezeCol))
+ {
+ SAL_WARN("sc.core", "ScTable::SetLOKFreezeCol : invalid nFreezeCol = " << nFreezeCol);
+ return false;
+ }
+
+ if (maLOKFreezeCell.Col() != nFreezeCol)
+ {
+ maLOKFreezeCell.SetCol(nFreezeCol);
+ return true;
+ }
+
+ return false;
+}
+
+bool ScTable::SetLOKFreezeRow(SCROW nFreezeRow)
+{
+ if (!ValidRow(nFreezeRow))
+ {
+ SAL_WARN("sc.core", "ScTable::SetLOKFreezeRow : invalid nFreezeRow = " << nFreezeRow);
+ return false;
+ }
+
+ if (maLOKFreezeCell.Row() != nFreezeRow)
+ {
+ maLOKFreezeCell.SetRow(nFreezeRow);
+ return true;
+ }
+
+ return false;
+}
+
+std::set<SCCOL> ScTable::QueryColumnsWithFormulaCells() const
+{
+ std::set<SCCOL> aColIndices;
+
+ for (const auto& pCol : aCol)
+ {
+ if (pCol->HasFormulaCell())
+ aColIndices.insert(pCol->GetCol());
+ }
+
+ return aColIndices;
+}
+
+void ScTable::CheckIntegrity() const
+{
+ for (const auto& pCol : aCol)
+ pCol->CheckIntegrity();
+}
+
+void ScTable::CollectBroadcasterState(sc::BroadcasterState& rState) const
+{
+ for (const auto& pCol : aCol)
+ pCol->CollectBroadcasterState(rState);
+}
+
+std::shared_ptr<sc::SolverSettings> ScTable::GetSolverSettings()
+{
+ if (!m_pSolverSettings)
+ m_pSolverSettings = std::make_shared<sc::SolverSettings>(*this);
+
+ return m_pSolverSettings;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/tabprotection.cxx b/sc/source/core/data/tabprotection.cxx
new file mode 100644
index 0000000000..cc4a783d53
--- /dev/null
+++ b/sc/source/core/data/tabprotection.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 <tabprotection.hxx>
+#include <svl/PasswordHelper.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/hash.hxx>
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <document.hxx>
+
+#include <vector>
+
+#define DEBUG_TAB_PROTECTION 0
+
+constexpr OUString URI_SHA1 = u"http://www.w3.org/2000/09/xmldsig#sha1"_ustr;
+constexpr OUString URI_SHA256_ODF12 = u"http://www.w3.org/2000/09/xmldsig#sha256"_ustr;
+constexpr OUStringLiteral URI_SHA256_W3C = u"http://www.w3.org/2001/04/xmlenc#sha256";
+constexpr OUString URI_XLS_LEGACY = u"http://docs.oasis-open.org/office/ns/table/legacy-hash-excel"_ustr;
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Sequence;
+using ::std::vector;
+
+bool ScPassHashHelper::needsPassHashRegen(const ScDocument& rDoc, ScPasswordHash eHash1, ScPasswordHash eHash2)
+{
+ if (rDoc.IsDocProtected())
+ {
+ const ScDocProtection* p = rDoc.GetDocProtection();
+ if (!p->isPasswordEmpty() && !p->hasPasswordHash(eHash1, eHash2))
+ return true;
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ const ScTableProtection* p = rDoc.GetTabProtection(i);
+ if (!p || !p->isProtected())
+ // Sheet not protected. Skip it.
+ continue;
+
+ if (!p->isPasswordEmpty() && !p->hasPasswordHash(eHash1, eHash2))
+ return true;
+ }
+
+ return false;
+}
+
+OUString ScPassHashHelper::getHashURI(ScPasswordHash eHash)
+{
+ switch (eHash)
+ {
+ case PASSHASH_SHA256:
+ return URI_SHA256_ODF12;
+ case PASSHASH_SHA1:
+ return URI_SHA1;
+ case PASSHASH_XL:
+ return URI_XLS_LEGACY;
+ case PASSHASH_UNSPECIFIED:
+ default:
+ ;
+ }
+ return OUString();
+}
+
+ScPasswordHash ScPassHashHelper::getHashTypeFromURI(std::u16string_view rURI)
+{
+ if (rURI == URI_SHA256_ODF12 || rURI == URI_SHA256_W3C)
+ return PASSHASH_SHA256;
+ if ( rURI == URI_SHA1 )
+ return PASSHASH_SHA1;
+ else if ( rURI == URI_XLS_LEGACY )
+ return PASSHASH_XL;
+ return PASSHASH_UNSPECIFIED;
+}
+
+bool ScOoxPasswordHash::verifyPassword( const OUString& aPassText ) const
+{
+ if (!hasPassword())
+ return false;
+
+ const OUString aHash( comphelper::DocPasswordHelper::GetOoxHashAsBase64(
+ aPassText, maSaltValue, mnSpinCount, comphelper::Hash::IterCount::APPEND, maAlgorithmName));
+ if (aHash.isEmpty())
+ // unsupported algorithm
+ return false;
+
+ return aHash == maHashValue;
+}
+
+ScPassHashProtectable::~ScPassHashProtectable()
+{
+}
+
+class ScTableProtectionImpl
+{
+public:
+ static Sequence<sal_Int8> hashPassword(std::u16string_view aPassText, ScPasswordHash eHash);
+ static Sequence<sal_Int8> hashPassword(const Sequence<sal_Int8>& rPassHash, ScPasswordHash eHash);
+
+ explicit ScTableProtectionImpl(SCSIZE nOptSize);
+ explicit ScTableProtectionImpl(const ScTableProtectionImpl& r);
+
+ bool isProtected() const { return mbProtected;}
+ bool isProtectedWithPass() const;
+ void setProtected(bool bProtected);
+
+ bool isPasswordEmpty() const { return mbEmptyPass;}
+ bool hasPasswordHash(ScPasswordHash eHash, ScPasswordHash eHash2) const;
+ void setPassword(const OUString& aPassText);
+ css::uno::Sequence<sal_Int8> getPasswordHash(
+ ScPasswordHash eHash, ScPasswordHash eHash2) const;
+ const ScOoxPasswordHash& getPasswordHash() const;
+ void setPasswordHash(
+ const css::uno::Sequence<sal_Int8>& aPassword,
+ ScPasswordHash eHash, ScPasswordHash eHash2);
+ void setPasswordHash( const OUString& rAlgorithmName, const OUString& rHashValue,
+ const OUString& rSaltValue, sal_uInt32 nSpinCount );
+ bool verifyPassword(const OUString& aPassText) const;
+
+ bool isOptionEnabled(SCSIZE nOptId) const;
+ void setOption(SCSIZE nOptId, bool bEnabled);
+
+ void setEnhancedProtection( ::std::vector< ScEnhancedProtection > && rProt );
+ const ::std::vector< ScEnhancedProtection > & getEnhancedProtection() const { return maEnhancedProtection;}
+ bool updateReference( UpdateRefMode, const ScDocument&, const ScRange& rWhere, SCCOL nDx, SCROW nDy, SCTAB nDz );
+ bool isBlockEditable( const ScRange& rRange ) const;
+ bool isSelectionEditable( const ScRangeList& rRangeList ) const;
+
+private:
+ OUString maPassText;
+ css::uno::Sequence<sal_Int8> maPassHash;
+ ::std::vector<bool> maOptions;
+ bool mbEmptyPass;
+ bool mbProtected;
+ ScPasswordHash meHash1;
+ ScPasswordHash meHash2;
+ ScOoxPasswordHash maPasswordHash;
+ ::std::vector< ScEnhancedProtection > maEnhancedProtection;
+};
+
+Sequence<sal_Int8> ScTableProtectionImpl::hashPassword(std::u16string_view aPassText, ScPasswordHash eHash)
+{
+ Sequence<sal_Int8> aHash;
+ switch (eHash)
+ {
+ case PASSHASH_XL:
+ aHash = ::comphelper::DocPasswordHelper::GetXLHashAsSequence( aPassText );
+ break;
+ case PASSHASH_SHA1:
+ SvPasswordHelper::GetHashPassword(aHash, aPassText);
+ break;
+ case PASSHASH_SHA1_UTF8:
+ SvPasswordHelper::GetHashPasswordSHA1UTF8(aHash, aPassText);
+ break;
+ case PASSHASH_SHA256:
+ SvPasswordHelper::GetHashPasswordSHA256(aHash, aPassText);
+ break;
+ default:
+ ;
+ }
+ return aHash;
+}
+
+Sequence<sal_Int8> ScTableProtectionImpl::hashPassword(
+ const Sequence<sal_Int8>& rPassHash, ScPasswordHash eHash)
+{
+ if (!rPassHash.hasElements() || eHash == PASSHASH_UNSPECIFIED)
+ return rPassHash;
+
+ // TODO: Right now, we only support double-hash by SHA1.
+ if (eHash == PASSHASH_SHA1)
+ {
+ auto aChars = comphelper::sequenceToContainer<vector<char>>(rPassHash);
+
+ Sequence<sal_Int8> aNewHash;
+ SvPasswordHelper::GetHashPassword(aNewHash, aChars.data(), aChars.size());
+ return aNewHash;
+ }
+
+ return rPassHash;
+}
+
+ScTableProtectionImpl::ScTableProtectionImpl(SCSIZE nOptSize) :
+ maOptions(nOptSize),
+ mbEmptyPass(true),
+ mbProtected(false),
+ meHash1(PASSHASH_SHA1),
+ meHash2(PASSHASH_UNSPECIFIED)
+{
+}
+
+ScTableProtectionImpl::ScTableProtectionImpl(const ScTableProtectionImpl& r) :
+ maPassText(r.maPassText),
+ maPassHash(r.maPassHash),
+ maOptions(r.maOptions),
+ mbEmptyPass(r.mbEmptyPass),
+ mbProtected(r.mbProtected),
+ meHash1(r.meHash1),
+ meHash2(r.meHash2),
+ maPasswordHash(r.maPasswordHash),
+ maEnhancedProtection(r.maEnhancedProtection)
+{
+}
+
+bool ScTableProtectionImpl::isProtectedWithPass() const
+{
+ if (!mbProtected)
+ return false;
+
+ return !maPassText.isEmpty() || maPassHash.hasElements() || maPasswordHash.hasPassword();
+}
+
+void ScTableProtectionImpl::setProtected(bool bProtected)
+{
+ mbProtected = bProtected;
+ // We need to keep the old password even when the protection is off. So,
+ // don't erase the password data here.
+}
+
+void ScTableProtectionImpl::setPassword(const OUString& aPassText)
+{
+ // We can't hash it here because we don't know whether this document will
+ // get saved to Excel or ODF, depending on which we will need to use a
+ // different hashing algorithm. One alternative is to hash it using all
+ // hash algorithms that we support, and store them all.
+
+ maPassText = aPassText;
+ mbEmptyPass = aPassText.isEmpty();
+ if (mbEmptyPass)
+ {
+ maPassHash = Sequence<sal_Int8>();
+ }
+ maPasswordHash.clear();
+}
+
+bool ScTableProtectionImpl::hasPasswordHash(ScPasswordHash eHash, ScPasswordHash eHash2) const
+{
+ if (mbEmptyPass)
+ return true;
+
+ if (!maPassText.isEmpty())
+ return true;
+
+ if (meHash1 == eHash)
+ {
+ if (meHash2 == PASSHASH_UNSPECIFIED)
+ // single hash.
+ return true;
+
+ return meHash2 == eHash2;
+ }
+
+ return false;
+}
+
+Sequence<sal_Int8> ScTableProtectionImpl::getPasswordHash(
+ ScPasswordHash eHash, ScPasswordHash eHash2) const
+{
+ Sequence<sal_Int8> aPassHash;
+
+ if (mbEmptyPass)
+ // Flagged as empty.
+ return aPassHash;
+
+ if (!maPassText.isEmpty())
+ {
+ // Cleartext password exists. Hash it.
+ aPassHash = hashPassword(maPassText, eHash);
+ if (eHash2 != PASSHASH_UNSPECIFIED)
+ // Double-hash it.
+ aPassHash = hashPassword(aPassHash, eHash2);
+
+ return aPassHash;
+ }
+ else
+ {
+ // No clear text password. Check if we have a hash value of the right hash type.
+ if (meHash1 == eHash)
+ {
+ aPassHash = maPassHash;
+
+ if (meHash2 == eHash2)
+ // Matching double-hash requested.
+ return aPassHash;
+ else if (meHash2 == PASSHASH_UNSPECIFIED)
+ // primary hashing type match. Double hash it by the requested
+ // double-hash type.
+ return hashPassword(aPassHash, eHash2);
+ }
+ }
+
+ // failed.
+ return Sequence<sal_Int8>();
+}
+
+const ScOoxPasswordHash& ScTableProtectionImpl::getPasswordHash() const
+{
+ return maPasswordHash;
+}
+
+void ScTableProtectionImpl::setPasswordHash(
+ const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash, ScPasswordHash eHash2)
+{
+ sal_Int32 nLen = aPassword.getLength();
+ mbEmptyPass = nLen <= 0;
+ meHash1 = eHash;
+ meHash2 = eHash2;
+ maPassHash = aPassword;
+
+#if DEBUG_TAB_PROTECTION
+ for (sal_Int8 n : aPassword)
+ printf("%2.2X ", static_cast<sal_uInt8>(n));
+ printf("\n");
+#endif
+}
+
+void ScTableProtectionImpl::setPasswordHash( const OUString& rAlgorithmName, const OUString& rHashValue,
+ const OUString& rSaltValue, sal_uInt32 nSpinCount )
+{
+ if (!rHashValue.isEmpty())
+ {
+ // Invalidate the other hashes.
+ setPasswordHash( uno::Sequence<sal_Int8>(), PASSHASH_UNSPECIFIED, PASSHASH_UNSPECIFIED);
+
+ // We don't know whether this is an empty password (or would
+ // unnecessarily have to try to verify an empty password), assume it is
+ // not. A later verifyPassword() with an empty password will determine.
+ // If this was not set to false then a verifyPassword() with an empty
+ // password would unlock even if this hash here wasn't for an empty
+ // password. Ugly stuff.
+ mbEmptyPass = false;
+ }
+
+ maPasswordHash.maAlgorithmName = rAlgorithmName;
+ maPasswordHash.maHashValue = rHashValue;
+ maPasswordHash.maSaltValue = rSaltValue;
+ maPasswordHash.mnSpinCount = nSpinCount;
+}
+
+bool ScTableProtectionImpl::verifyPassword(const OUString& aPassText) const
+{
+#if DEBUG_TAB_PROTECTION
+ fprintf(stdout, "ScTableProtectionImpl::verifyPassword: input = '%s'\n",
+ OUStringToOString(aPassText, RTL_TEXTENCODING_UTF8).getStr());
+#endif
+
+ if (mbEmptyPass)
+ return aPassText.isEmpty();
+
+ if (!maPassText.isEmpty())
+ // Clear text password exists, and this one takes precedence.
+ return aPassText == maPassText;
+
+ // For PASSHASH_UNSPECIFIED also maPassHash is empty and any aPassText
+ // would yield an empty hash as well and thus compare true. Don't.
+ if (meHash1 != PASSHASH_UNSPECIFIED)
+ {
+ Sequence<sal_Int8> aHash = hashPassword(aPassText, meHash1);
+ aHash = hashPassword(aHash, meHash2);
+
+#if DEBUG_TAB_PROTECTION
+ fprintf(stdout, "ScTableProtectionImpl::verifyPassword: hash = ");
+ for (sal_Int32 i = 0; i < aHash.getLength(); ++i)
+ printf("%2.2X ", static_cast<sal_uInt8>(aHash[i]));
+ printf("\n");
+#endif
+
+ if (aHash == maPassHash)
+ {
+ return true;
+ }
+ }
+
+ // tdf#115483 compat hack for ODF 1.2; for now UTF8-SHA1 passwords are only
+ // verified, not generated
+ if (meHash1 == PASSHASH_SHA1 && meHash2 == PASSHASH_UNSPECIFIED)
+ {
+ Sequence<sal_Int8> const aHash2 = hashPassword(aPassText, PASSHASH_SHA1_UTF8);
+ return aHash2 == maPassHash;
+ }
+
+ // Not yet generated or tracked with meHash1 or meHash2, but can be read
+ // from OOXML.
+ return maPasswordHash.verifyPassword( aPassText);
+}
+
+bool ScTableProtectionImpl::isOptionEnabled(SCSIZE nOptId) const
+{
+ if ( maOptions.size() <= static_cast<size_t>(nOptId) )
+ {
+ OSL_FAIL("ScTableProtectionImpl::isOptionEnabled: wrong size");
+ return false;
+ }
+
+ return maOptions[nOptId];
+}
+
+void ScTableProtectionImpl::setOption(SCSIZE nOptId, bool bEnabled)
+{
+ if ( maOptions.size() <= static_cast<size_t>(nOptId) )
+ {
+ OSL_FAIL("ScTableProtectionImpl::setOption: wrong size");
+ return;
+ }
+
+ maOptions[nOptId] = bEnabled;
+}
+
+void ScTableProtectionImpl::setEnhancedProtection( ::std::vector< ScEnhancedProtection > && rProt )
+{
+ maEnhancedProtection = std::move(rProt);
+}
+
+bool ScTableProtectionImpl::updateReference( UpdateRefMode eMode, const ScDocument& rDoc,
+ const ScRange& rWhere, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ bool bChanged = false;
+ for (auto& rEnhancedProtection : maEnhancedProtection)
+ {
+ if (rEnhancedProtection.maRangeList.is())
+ bChanged |= rEnhancedProtection.maRangeList->UpdateReference( eMode, &rDoc, rWhere, nDx, nDy, nDz);
+ }
+ return bChanged;
+}
+
+bool ScTableProtectionImpl::isBlockEditable( const ScRange& rRange ) const
+{
+ /* TODO: ask for password (and remember) if a password was set for
+ * a matching range and no matching range without password was encountered.
+ * Would need another return type than boolean to reflect
+ * "password required for a specific protection". */
+
+ // No protection exception or overriding permission to edit if empty.
+ if (maEnhancedProtection.empty())
+ return false;
+
+ // No security descriptor in an enhanced protection means the ranges of
+ // that protection are editable. If there is any security descriptor
+ // present we assume the permission to edit is not granted. Until we
+ // actually can evaluate the descriptors...
+
+ auto lIsEditable = [rRange](const ScEnhancedProtection& rEnhancedProtection) {
+ return !rEnhancedProtection.hasSecurityDescriptor()
+ && rEnhancedProtection.maRangeList.is() && rEnhancedProtection.maRangeList->Contains( rRange)
+ && !rEnhancedProtection.hasPassword(); // Range is editable if no password is assigned.
+ };
+ if (std::any_of(maEnhancedProtection.begin(), maEnhancedProtection.end(), lIsEditable))
+ return true;
+
+ // For a single address, a simple check with single ranges was sufficient.
+ if (rRange.aStart == rRange.aEnd)
+ return false;
+
+ // Test also for cases where rRange is encompassed by a union of two or
+ // more ranges of the list. The original ranges are not necessarily joined.
+ for (const auto& rEnhancedProtection : maEnhancedProtection)
+ {
+ if (!rEnhancedProtection.hasSecurityDescriptor() && rEnhancedProtection.maRangeList.is())
+ {
+ ScRangeList aList( rEnhancedProtection.maRangeList->GetIntersectedRange( rRange));
+ if (aList.size() == 1 && aList[0] == rRange)
+ {
+ // Range is editable if no password is assigned.
+ if (!rEnhancedProtection.hasPassword())
+ return true;
+ }
+ }
+ }
+
+ // Ranges may even be distributed over different protection records, for
+ // example if they are assigned different names, and can have different
+ // passwords. Combine the ones that can be edited.
+ /* TODO: once we handle passwords, remember a successful unlock at
+ * ScEnhancedProtection so we can use that here. */
+ ScRangeList aRangeList;
+ for (const auto& rEnhancedProtection : maEnhancedProtection)
+ {
+ if (!rEnhancedProtection.hasSecurityDescriptor() && rEnhancedProtection.maRangeList.is())
+ {
+ // Ranges are editable if no password is assigned.
+ if (!rEnhancedProtection.hasPassword())
+ {
+ const ScRangeList& rRanges = *rEnhancedProtection.maRangeList;
+ size_t nRanges = rRanges.size();
+ for (size_t i=0; i < nRanges; ++i)
+ {
+ aRangeList.push_back( rRanges[i]);
+ }
+ }
+ }
+ }
+ ScRangeList aResultList( aRangeList.GetIntersectedRange( rRange));
+ return aResultList.size() == 1 && aResultList[0] == rRange;
+}
+
+bool ScTableProtectionImpl::isSelectionEditable( const ScRangeList& rRangeList ) const
+{
+ if (rRangeList.empty())
+ return false;
+
+ for (size_t i=0, nRanges = rRangeList.size(); i < nRanges; ++i)
+ {
+ if (!isBlockEditable( rRangeList[i]))
+ return false;
+ }
+ return true;
+}
+
+ScDocProtection::ScDocProtection() :
+ mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScDocProtection::NONE)))
+{
+}
+
+ScDocProtection::ScDocProtection(const ScDocProtection& r) :
+ ScPassHashProtectable(),
+ mpImpl(new ScTableProtectionImpl(*r.mpImpl))
+{
+}
+
+ScDocProtection::~ScDocProtection()
+{
+}
+
+bool ScDocProtection::isProtected() const
+{
+ return mpImpl->isProtected();
+}
+
+bool ScDocProtection::isProtectedWithPass() const
+{
+ return mpImpl->isProtectedWithPass();
+}
+
+void ScDocProtection::setProtected(bool bProtected)
+{
+ mpImpl->setProtected(bProtected);
+
+ // Currently Calc doesn't support document protection options. So, let's
+ // assume that when the document is protected, its structure is protected.
+ // We need to do this for Excel export.
+ mpImpl->setOption(ScDocProtection::STRUCTURE, bProtected);
+}
+
+bool ScDocProtection::isPasswordEmpty() const
+{
+ return mpImpl->isPasswordEmpty();
+}
+
+bool ScDocProtection::hasPasswordHash(ScPasswordHash eHash, ScPasswordHash eHash2) const
+{
+ return mpImpl->hasPasswordHash(eHash, eHash2);
+}
+
+void ScDocProtection::setPassword(const OUString& aPassText)
+{
+ mpImpl->setPassword(aPassText);
+}
+
+uno::Sequence<sal_Int8> ScDocProtection::getPasswordHash(ScPasswordHash eHash, ScPasswordHash eHash2) const
+{
+ return mpImpl->getPasswordHash(eHash, eHash2);
+}
+
+const ScOoxPasswordHash& ScDocProtection::getPasswordHash() const
+{
+ return mpImpl->getPasswordHash();
+}
+
+void ScDocProtection::setPasswordHash(
+ const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash, ScPasswordHash eHash2)
+{
+ mpImpl->setPasswordHash(aPassword, eHash, eHash2);
+}
+
+void ScDocProtection::setPasswordHash( const OUString& rAlgorithmName, const OUString& rHashValue,
+ const OUString& rSaltValue, sal_uInt32 nSpinCount )
+{
+ mpImpl->setPasswordHash( rAlgorithmName, rHashValue, rSaltValue, nSpinCount);
+}
+
+bool ScDocProtection::verifyPassword(const OUString& aPassText) const
+{
+ return mpImpl->verifyPassword(aPassText);
+}
+
+bool ScDocProtection::isOptionEnabled(Option eOption) const
+{
+ return mpImpl->isOptionEnabled(eOption);
+}
+
+void ScDocProtection::setOption(Option eOption, bool bEnabled)
+{
+ mpImpl->setOption(eOption, bEnabled);
+}
+
+ScTableProtection::ScTableProtection() :
+ mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScTableProtection::NONE)))
+{
+ // Set default values for the options.
+ mpImpl->setOption(SELECT_LOCKED_CELLS, true);
+ mpImpl->setOption(SELECT_UNLOCKED_CELLS, true);
+}
+
+ScTableProtection::ScTableProtection(const ScTableProtection& r) :
+ ScPassHashProtectable(),
+ mpImpl(new ScTableProtectionImpl(*r.mpImpl))
+{
+}
+
+ScTableProtection::~ScTableProtection()
+{
+}
+
+bool ScTableProtection::isProtected() const
+{
+ return mpImpl->isProtected();
+}
+
+bool ScTableProtection::isProtectedWithPass() const
+{
+ return mpImpl->isProtectedWithPass();
+}
+
+void ScTableProtection::setProtected(bool bProtected)
+{
+ mpImpl->setProtected(bProtected);
+}
+
+bool ScTableProtection::isPasswordEmpty() const
+{
+ return mpImpl->isPasswordEmpty();
+}
+
+bool ScTableProtection::hasPasswordHash(ScPasswordHash eHash, ScPasswordHash eHash2) const
+{
+ return mpImpl->hasPasswordHash(eHash, eHash2);
+}
+
+void ScTableProtection::setPassword(const OUString& aPassText)
+{
+ mpImpl->setPassword(aPassText);
+}
+
+Sequence<sal_Int8> ScTableProtection::getPasswordHash(ScPasswordHash eHash, ScPasswordHash eHash2) const
+{
+ return mpImpl->getPasswordHash(eHash, eHash2);
+}
+
+const ScOoxPasswordHash& ScTableProtection::getPasswordHash() const
+{
+ return mpImpl->getPasswordHash();
+}
+
+void ScTableProtection::setPasswordHash(
+ const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash, ScPasswordHash eHash2)
+{
+ mpImpl->setPasswordHash(aPassword, eHash, eHash2);
+}
+
+void ScTableProtection::setPasswordHash( const OUString& rAlgorithmName, const OUString& rHashValue,
+ const OUString& rSaltValue, sal_uInt32 nSpinCount )
+{
+ mpImpl->setPasswordHash( rAlgorithmName, rHashValue, rSaltValue, nSpinCount);
+}
+
+bool ScTableProtection::verifyPassword(const OUString& aPassText) const
+{
+ return mpImpl->verifyPassword(aPassText);
+}
+
+bool ScTableProtection::isOptionEnabled(Option eOption) const
+{
+ return mpImpl->isOptionEnabled(eOption);
+}
+
+void ScTableProtection::setOption(Option eOption, bool bEnabled)
+{
+ mpImpl->setOption(eOption, bEnabled);
+}
+
+void ScTableProtection::setEnhancedProtection( ::std::vector< ScEnhancedProtection > && rProt )
+{
+ mpImpl->setEnhancedProtection(std::move(rProt));
+}
+
+const ::std::vector< ScEnhancedProtection > & ScTableProtection::getEnhancedProtection() const
+{
+ return mpImpl->getEnhancedProtection();
+}
+
+bool ScTableProtection::updateReference( UpdateRefMode eMode, const ScDocument& rDoc, const ScRange& rWhere,
+ SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ return mpImpl->updateReference( eMode, rDoc, rWhere, nDx, nDy, nDz);
+}
+
+bool ScTableProtection::isBlockEditable( const ScRange& rRange ) const
+{
+ return mpImpl->isBlockEditable( rRange);
+}
+
+bool ScTableProtection::isSelectionEditable( const ScRangeList& rRangeList ) const
+{
+ return mpImpl->isSelectionEditable( rRangeList);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/types.cxx b/sc/source/core/data/types.cxx
new file mode 100644
index 0000000000..e5f97c92da
--- /dev/null
+++ b/sc/source/core/data/types.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/.
+ */
+
+#include <types.hxx>
+#include <scmatrix.hxx>
+
+namespace sc {
+
+RangeMatrix::RangeMatrix() :
+ mpMat(nullptr), mnCol1(-1), mnRow1(-1), mnTab1(-1), mnCol2(-1), mnRow2(-1), mnTab2(-1) {}
+
+bool RangeMatrix::isRangeValid() const
+{
+ return mnCol1 >= 0 && mnRow1 >= 0 && mnTab1 >=0 &&
+ mnCol2 >= 0 && mnRow2 >= 0 && mnTab2 >= 0 &&
+ mnCol1 <= mnCol2 && mnRow1 <= mnRow2 && mnTab1 <= mnTab2;
+}
+
+MultiDataCellState::MultiDataCellState() :
+ mnRow1(-1), mnCol1(-1), meState(StateType::Invalid) {}
+MultiDataCellState::MultiDataCellState( StateType eState ) :
+ mnRow1(-1), mnCol1(-1), meState(eState) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/userdat.cxx b/sc/source/core/data/userdat.cxx
new file mode 100644
index 0000000000..9a65caaa11
--- /dev/null
+++ b/sc/source/core/data/userdat.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <userdat.hxx>
+
+
+ScDrawObjData::ScDrawObjData() :
+ SdrObjUserData( SdrInventor::ScOrSwDraw, SC_UD_OBJDATA ),
+ maStart( ScAddress::INITIALIZE_INVALID ),
+ maEnd( ScAddress::INITIALIZE_INVALID ),
+ meType( DrawingObject ),
+ mbResizeWithCell( false )
+{
+}
+
+std::unique_ptr<SdrObjUserData> ScDrawObjData::Clone( SdrObject* ) const
+{
+ return std::unique_ptr<SdrObjUserData>(new ScDrawObjData( *this ));
+}
+
+ScMacroInfo::ScMacroInfo() :
+ SdrObjUserData( SdrInventor::ScOrSwDraw, SC_UD_MACRODATA )
+{
+}
+
+ScMacroInfo::~ScMacroInfo()
+{
+}
+
+std::unique_ptr<SdrObjUserData> ScMacroInfo::Clone( SdrObject* /*pObj*/ ) const
+{
+ return std::unique_ptr<SdrObjUserData>(new ScMacroInfo( *this ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/validat.cxx b/sc/source/core/data/validat.cxx
new file mode 100644
index 0000000000..a46b09986b
--- /dev/null
+++ b/sc/source/core/data/validat.cxx
@@ -0,0 +1,1185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <validat.hxx>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sberrors.hxx>
+
+#include <basic/sbx.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <rtl/math.hxx>
+#include <osl/diagnose.h>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <formulacell.hxx>
+#include <patattr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <typedstrdata.hxx>
+#include <editutil.hxx>
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <cellvalue.hxx>
+#include <comphelper/lok.hxx>
+#include <simpleformulacalc.hxx>
+
+#include <math.h>
+#include <memory>
+
+using namespace formula;
+
+// Entries for validation (with only one condition)
+
+ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
+ const OUString& rExpr1, const OUString& rExpr2,
+ ScDocument& rDocument, const ScAddress& rPos,
+ const OUString& rExprNmsp1, const OUString& rExprNmsp2,
+ FormulaGrammar::Grammar eGrammar1,
+ FormulaGrammar::Grammar eGrammar2 )
+ : ScConditionEntry( eOper, rExpr1, rExpr2, rDocument, rPos, rExprNmsp1,
+ rExprNmsp2, eGrammar1, eGrammar2 )
+ , nKey( 0 )
+ , eDataMode( eMode )
+ , bShowInput(false)
+ , bShowError(false)
+ , eErrorStyle( SC_VALERR_STOP )
+ , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
+{
+}
+
+ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
+ const ScTokenArray* pArr1, const ScTokenArray* pArr2,
+ ScDocument& rDocument, const ScAddress& rPos )
+ : ScConditionEntry( eOper, pArr1, pArr2, rDocument, rPos )
+ , nKey( 0 )
+ , eDataMode( eMode )
+ , bShowInput(false)
+ , bShowError(false)
+ , eErrorStyle( SC_VALERR_STOP )
+ , mnListType( css::sheet::TableValidationVisibility::UNSORTED )
+{
+}
+
+ScValidationData::ScValidationData( const ScValidationData& r )
+ : ScConditionEntry( r )
+ , nKey( r.nKey )
+ , eDataMode( r.eDataMode )
+ , bShowInput( r.bShowInput )
+ , bShowError( r.bShowError )
+ , eErrorStyle( r.eErrorStyle )
+ , mnListType( r.mnListType )
+ , aInputTitle( r.aInputTitle )
+ , aInputMessage( r.aInputMessage )
+ , aErrorTitle( r.aErrorTitle )
+ , aErrorMessage( r.aErrorMessage )
+{
+ // Formulae copied by RefCount
+}
+
+ScValidationData::ScValidationData( ScDocument& rDocument, const ScValidationData& r )
+ : ScConditionEntry( rDocument, r )
+ , nKey( r.nKey )
+ , eDataMode( r.eDataMode )
+ , bShowInput( r.bShowInput )
+ , bShowError( r.bShowError )
+ , eErrorStyle( r.eErrorStyle )
+ , mnListType( r.mnListType )
+ , aInputTitle( r.aInputTitle )
+ , aInputMessage( r.aInputMessage )
+ , aErrorTitle( r.aErrorTitle )
+ , aErrorMessage( r.aErrorMessage )
+{
+ // Formulae really copied
+}
+
+ScValidationData::~ScValidationData()
+{
+}
+
+bool ScValidationData::IsEmpty() const
+{
+ ScValidationData aDefault( SC_VALID_ANY, ScConditionMode::Equal, "", "", *GetDocument(), ScAddress() );
+ return EqualEntries( aDefault );
+}
+
+bool ScValidationData::EqualEntries( const ScValidationData& r ) const
+{
+ // test same parameters (excluding Key)
+
+ return ScConditionEntry::operator==(r) &&
+ eDataMode == r.eDataMode &&
+ bShowInput == r.bShowInput &&
+ bShowError == r.bShowError &&
+ eErrorStyle == r.eErrorStyle &&
+ mnListType == r.mnListType &&
+ aInputTitle == r.aInputTitle &&
+ aInputMessage == r.aInputMessage &&
+ aErrorTitle == r.aErrorTitle &&
+ aErrorMessage == r.aErrorMessage;
+}
+
+void ScValidationData::ResetInput()
+{
+ bShowInput = false;
+}
+
+void ScValidationData::ResetError()
+{
+ bShowError = false;
+}
+
+void ScValidationData::SetInput( const OUString& rTitle, const OUString& rMsg )
+{
+ bShowInput = true;
+ aInputTitle = rTitle;
+ aInputMessage = rMsg;
+}
+
+void ScValidationData::SetError( const OUString& rTitle, const OUString& rMsg,
+ ScValidErrorStyle eStyle )
+{
+ bShowError = true;
+ eErrorStyle = eStyle;
+ aErrorTitle = rTitle;
+ aErrorMessage = rMsg;
+}
+
+bool ScValidationData::GetErrMsg( OUString& rTitle, OUString& rMsg,
+ ScValidErrorStyle& rStyle ) const
+{
+ rTitle = aErrorTitle;
+ rMsg = aErrorMessage;
+ rStyle = eErrorStyle;
+ return bShowError;
+}
+
+bool ScValidationData::DoScript( const ScAddress& rPos, const OUString& rInput,
+ ScFormulaCell* pCell, weld::Window* pParent ) const
+{
+ ScDocument* pDocument = GetDocument();
+ ScDocShell* pDocSh = pDocument->GetDocumentShell();
+ if ( !pDocSh )
+ return false;
+
+ bool bScriptReturnedFalse = false; // default: do not abort
+
+ // 1) entered or calculated value
+ css::uno::Any aParam0(rInput);
+ if ( pCell ) // if cell exists, call interpret
+ {
+ if ( pCell->IsValue() )
+ aParam0 <<= pCell->GetValue();
+ else
+ aParam0 <<= pCell->GetString().getString();
+ }
+
+ // 2) Position of the cell
+ OUString aPosStr(rPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, pDocument, pDocument->GetAddressConvention()));
+
+ // Set up parameters
+ css::uno::Sequence< css::uno::Any > aParams{ aParam0, css::uno::Any(aPosStr) };
+
+ // use link-update flag to prevent closing the document
+ // while the macro is running
+ bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
+ if ( !bWasInLinkUpdate )
+ pDocument->SetInLinkUpdate( true );
+
+ if ( pCell )
+ pDocument->LockTable( rPos.Tab() );
+
+ css::uno::Any aRet;
+ css::uno::Sequence< sal_Int16 > aOutArgsIndex;
+ css::uno::Sequence< css::uno::Any > aOutArgs;
+
+ ErrCode eRet = pDocSh->CallXScript(
+ aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs );
+
+ if ( pCell )
+ pDocument->UnlockTable( rPos.Tab() );
+
+ if ( !bWasInLinkUpdate )
+ pDocument->SetInLinkUpdate( false );
+
+ // Check the return value from the script
+ // The contents of the cell get reset if the script returns false
+ bool bTmp = false;
+ if ( eRet == ERRCODE_NONE &&
+ aRet.getValueType() == cppu::UnoType<bool>::get() &&
+ ( aRet >>= bTmp ) &&
+ !bTmp )
+ {
+ bScriptReturnedFalse = true;
+ }
+
+ if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell )
+ // Macro not found (only with input)
+ {
+ //TODO: different error message, if found, but not bAllowed ??
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_VALID_MACRONOTFOUND)));
+ xBox->run();
+ }
+
+ return bScriptReturnedFalse;
+}
+
+ // true -> abort
+
+bool ScValidationData::DoMacro( const ScAddress& rPos, const OUString& rInput,
+ ScFormulaCell* pCell, weld::Window* pParent ) const
+{
+ if ( SfxApplication::IsXScriptURL( aErrorTitle ) )
+ {
+ return DoScript( rPos, rInput, pCell, pParent );
+ }
+
+ ScDocument* pDocument = GetDocument();
+ ScDocShell* pDocSh = pDocument->GetDocumentShell();
+ if ( !pDocSh )
+ return false;
+
+ bool bDone = false;
+ bool bRet = false; // default: do not abort
+
+ // If the Doc was loaded during a Basic-Calls,
+ // the Sbx-object may not be created (?)
+// pDocSh->GetSbxObject();
+
+#if HAVE_FEATURE_SCRIPTING
+ // no security check ahead (only CheckMacroWarn), that happens in CallBasic
+
+ // Function search by their simple name,
+ // then assemble aBasicStr, aMacroStr for SfxObjectShell::CallBasic
+
+ StarBASIC* pRoot = pDocSh->GetBasic();
+ SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxClassType::Method );
+ if (SbMethod* pMethod = dynamic_cast<SbMethod*>(pVar))
+ {
+ SbModule* pModule = pMethod->GetModule();
+ SbxObject* pObject = pModule->GetParent();
+ OUString aMacroStr(
+ pObject->GetName() + "." + pModule->GetName() + "." + pMethod->GetName());
+ OUString aBasicStr;
+
+ // the distinction between document- and app-basic has to be done
+ // by checking the parent (as in ScInterpreter::ScMacro), not by looping
+ // over all open documents, because this may be called from within loading,
+ // when SfxObjectShell::GetFirst/GetNext won't find the document.
+
+ if ( pObject->GetParent() )
+ aBasicStr = pObject->GetParent()->GetName(); // Basic of document
+ else
+ aBasicStr = SfxGetpApp()->GetName(); // Basic of application
+
+ // Parameter for Macro
+ SbxArrayRef refPar = new SbxArray;
+
+ // 1) entered or calculated value
+ OUString aValStr = rInput;
+ double nValue = 0.0;
+ bool bIsValue = false;
+ if ( pCell ) // if cell set, called from interpret
+ {
+ bIsValue = pCell->IsValue();
+ if ( bIsValue )
+ nValue = pCell->GetValue();
+ else
+ aValStr = pCell->GetString().getString();
+ }
+ if ( bIsValue )
+ refPar->Get(1)->PutDouble(nValue);
+ else
+ refPar->Get(1)->PutString(aValStr);
+
+ // 2) Position of the cell
+ OUString aPosStr(rPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, pDocument, pDocument->GetAddressConvention()));
+ refPar->Get(2)->PutString(aPosStr);
+
+ // use link-update flag to prevent closing the document
+ // while the macro is running
+ bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
+ if ( !bWasInLinkUpdate )
+ pDocument->SetInLinkUpdate( true );
+
+ if ( pCell )
+ pDocument->LockTable( rPos.Tab() );
+ SbxVariableRef refRes = new SbxVariable;
+ ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar.get(), refRes.get() );
+ if ( pCell )
+ pDocument->UnlockTable( rPos.Tab() );
+
+ if ( !bWasInLinkUpdate )
+ pDocument->SetInLinkUpdate( false );
+
+ // Interrupt input if Basic macro returns false
+ if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && !refRes->GetBool() )
+ bRet = true;
+ bDone = true;
+ }
+#endif
+ if ( !bDone && !pCell ) // Macro not found (only with input)
+ {
+ //TODO: different error message, if found, but not bAllowed ??
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_VALID_MACRONOTFOUND)));
+ xBox->run();
+ }
+
+ return bRet;
+}
+
+void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const
+{
+ if ( eErrorStyle == SC_VALERR_MACRO )
+ DoMacro( pCell->aPos, OUString(), pCell, nullptr );
+}
+
+IMPL_STATIC_LINK_NOARG(ScValidationData, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return SfxViewShell::Current();
+}
+
+ // true -> abort
+
+bool ScValidationData::DoError(weld::Window* pParent, const OUString& rInput,
+ const ScAddress& rPos) const
+{
+ if ( eErrorStyle == SC_VALERR_MACRO )
+ return DoMacro(rPos, rInput, nullptr, pParent);
+
+ // Output error message
+
+ OUString aTitle = aErrorTitle;
+ if (aTitle.isEmpty())
+ aTitle = ScResId( STR_MSSG_DOSUBTOTALS_0 ); // application title
+ OUString aMessage = aErrorMessage;
+ if (aMessage.isEmpty())
+ aMessage = ScResId( STR_VALID_DEFERROR );
+
+ VclButtonsType eStyle = VclButtonsType::Ok;
+ VclMessageType eType = VclMessageType::Error;
+ switch (eErrorStyle)
+ {
+ case SC_VALERR_INFO:
+ eType = VclMessageType::Info;
+ eStyle = VclButtonsType::OkCancel;
+ break;
+ case SC_VALERR_WARNING:
+ eType = VclMessageType::Warning;
+ eStyle = VclButtonsType::OkCancel;
+ break;
+ default:
+ break;
+ }
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, eType,
+ eStyle, aMessage, SfxViewShell::Current()));
+ xBox->set_title(aTitle);
+ xBox->SetInstallLOKNotifierHdl(LINK(nullptr, ScValidationData, InstallLOKNotifierHdl));
+
+ switch (eErrorStyle)
+ {
+ case SC_VALERR_INFO:
+ xBox->set_default_response(RET_OK);
+ break;
+ case SC_VALERR_WARNING:
+ xBox->set_default_response(RET_CANCEL);
+ break;
+ default:
+ break;
+ }
+
+ short nRet = xBox->run();
+
+ return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL );
+}
+
+bool ScValidationData::IsDataValidCustom(
+ const OUString& rTest,
+ const ScPatternAttr& rPattern,
+ const ScAddress& rPos,
+ const CustomValidationPrivateAccess& ) const
+{
+ OSL_ENSURE(GetDataMode() == SC_VALID_CUSTOM,
+ "ScValidationData::IsDataValidCustom invoked for a non-custom validation");
+
+ if (rTest.isEmpty()) // check whether empty cells are allowed
+ return IsIgnoreBlank();
+
+ SvNumberFormatter* pFormatter = nullptr;
+ sal_uInt32 nFormat = 0;
+ double nVal = 0.0;
+ OUString rStrResult = "";
+ bool bIsVal = false;
+
+ if (rTest[0] == '=')
+ {
+ if (!isFormulaResultsValidatable(rTest, rPos, pFormatter, rStrResult, nVal, nFormat, bIsVal))
+ return false;
+
+ // check whether empty cells are allowed
+ if (rStrResult.isEmpty())
+ return IsIgnoreBlank();
+ }
+ else
+ {
+ pFormatter = GetDocument()->GetFormatTable();
+
+ // get the value if any
+ nFormat = rPattern.GetNumberFormat(pFormatter);
+ bIsVal = pFormatter->IsNumberFormat(rTest, nFormat, nVal);
+ rStrResult = rTest;
+ }
+
+ ScRefCellValue aTmpCell;
+ svl::SharedString aSS;
+ if (bIsVal)
+ {
+ aTmpCell = ScRefCellValue(nVal);
+ }
+ else
+ {
+ aSS = mpDoc->GetSharedStringPool().intern(rStrResult);
+ aTmpCell = ScRefCellValue(&aSS);
+ }
+
+ ScCellValue aOriginalCellValue(ScRefCellValue(*GetDocument(), rPos));
+
+ aTmpCell.commit(*GetDocument(), rPos);
+ bool bRet = IsCellValid(aTmpCell, rPos);
+ aOriginalCellValue.commit(*GetDocument(), rPos);
+
+ return bRet;
+}
+
+/** To test numeric data text length in IsDataValidTextLen().
+
+ If mpFormatter is not set, it is obtained from the document and the format
+ key is determined from the cell position's attribute pattern.
+ */
+struct ScValidationDataIsNumeric
+{
+ SvNumberFormatter* mpFormatter;
+ double mfVal;
+ sal_uInt32 mnFormat;
+
+ ScValidationDataIsNumeric( double fVal, SvNumberFormatter* pFormatter = nullptr, sal_uInt32 nFormat = 0 )
+ : mpFormatter(pFormatter), mfVal(fVal), mnFormat(nFormat)
+ {
+ }
+
+ void init( const ScDocument& rDoc, const ScAddress& rPos )
+ {
+ const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab());
+ mpFormatter = rDoc.GetFormatTable();
+ mnFormat = pPattern->GetNumberFormat( mpFormatter);
+ }
+};
+
+bool ScValidationData::IsDataValidTextLen( std::u16string_view rTest, const ScAddress& rPos,
+ ScValidationDataIsNumeric* pDataNumeric ) const
+{
+ sal_Int32 nLen;
+ if (!pDataNumeric)
+ nLen = rTest.size();
+ else
+ {
+ if (!pDataNumeric->mpFormatter)
+ pDataNumeric->init( *GetDocument(), rPos);
+
+ // For numeric values use the resulting input line string to
+ // determine length, otherwise an once accepted value maybe could
+ // not be edited again, for example abbreviated dates or leading
+ // zeros or trailing zeros after decimal separator change length.
+ OUString aStr;
+ pDataNumeric->mpFormatter->GetInputLineString( pDataNumeric->mfVal, pDataNumeric->mnFormat, aStr);
+ nLen = aStr.getLength();
+ }
+ ScRefCellValue aTmpCell( static_cast<double>(nLen));
+ return IsCellValid( aTmpCell, rPos);
+}
+
+bool ScValidationData::IsDataValid(
+ const OUString& rTest, const ScPatternAttr& rPattern, const ScAddress& rPos ) const
+{
+ if ( eDataMode == SC_VALID_ANY ) // check if any cell content is allowed
+ return true;
+
+ if (rTest.isEmpty()) // check whether empty cells are allowed
+ return IsIgnoreBlank();
+
+ SvNumberFormatter* pFormatter = nullptr;
+ sal_uInt32 nFormat = 0;
+ double nVal = 0.0;
+ OUString rStrResult = "";
+ bool bIsVal = false;
+
+ if (rTest[0] == '=')
+ {
+ if (!isFormulaResultsValidatable(rTest, rPos, pFormatter, rStrResult, nVal, nFormat, bIsVal))
+ return false;
+
+ // check whether empty cells are allowed
+ if (rStrResult.isEmpty())
+ return IsIgnoreBlank();
+ }
+ else
+ {
+ pFormatter = GetDocument()->GetFormatTable();
+
+ // get the value if any
+ nFormat = rPattern.GetNumberFormat(pFormatter);
+ bIsVal = pFormatter->IsNumberFormat(rTest, nFormat, nVal);
+ rStrResult = rTest;
+ }
+
+ bool bRet;
+ if (SC_VALID_TEXTLEN == eDataMode)
+ {
+ if (!bIsVal)
+ bRet = IsDataValidTextLen( rStrResult, rPos, nullptr);
+ else
+ {
+ ScValidationDataIsNumeric aDataNumeric( nVal, pFormatter, nFormat);
+ bRet = IsDataValidTextLen( rStrResult, rPos, &aDataNumeric);
+ }
+ }
+ else
+ {
+ if (bIsVal)
+ {
+ ScRefCellValue aTmpCell(nVal);
+ bRet = IsDataValid(aTmpCell, rPos);
+ }
+ else
+ {
+ svl::SharedString aSS = mpDoc->GetSharedStringPool().intern( rStrResult );
+ ScRefCellValue aTmpCell(&aSS);
+ bRet = IsDataValid(aTmpCell, rPos);
+ }
+ }
+
+ return bRet;
+}
+
+bool ScValidationData::IsDataValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
+{
+ if( eDataMode == SC_VALID_LIST )
+ return IsListValid(rCell, rPos);
+
+ if ( eDataMode == SC_VALID_CUSTOM )
+ return IsCellValid(rCell, rPos);
+
+ double nVal = 0.0;
+ OUString aString;
+ bool bIsVal = true;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ nVal = rCell.getDouble();
+ break;
+ case CELLTYPE_STRING:
+ aString = rCell.getSharedString()->getString();
+ bIsVal = false;
+ break;
+ case CELLTYPE_EDIT:
+ if (rCell.getEditText())
+ aString = ScEditUtil::GetString(*rCell.getEditText(), GetDocument());
+ bIsVal = false;
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ bIsVal = pFCell->IsValue();
+ if ( bIsVal )
+ nVal = pFCell->GetValue();
+ else
+ aString = pFCell->GetString().getString();
+ }
+ break;
+ default: // Notes, Broadcaster
+ return IsIgnoreBlank(); // as set
+ }
+
+ bool bOk = true;
+ switch (eDataMode)
+ {
+ // SC_VALID_ANY already above
+
+ case SC_VALID_WHOLE:
+ case SC_VALID_DECIMAL:
+ case SC_VALID_DATE: // Date/Time is only formatting
+ case SC_VALID_TIME:
+ bOk = bIsVal;
+ if ( bOk && eDataMode == SC_VALID_WHOLE )
+ bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // integers
+ if ( bOk )
+ bOk = IsCellValid(rCell, rPos);
+ break;
+
+ case SC_VALID_TEXTLEN:
+ if (!bIsVal)
+ bOk = IsDataValidTextLen( aString, rPos, nullptr);
+ else
+ {
+ ScValidationDataIsNumeric aDataNumeric( nVal);
+ bOk = IsDataValidTextLen( aString, rPos, &aDataNumeric);
+ }
+ break;
+
+ default:
+ OSL_FAIL("not yet done");
+ break;
+ }
+
+ return bOk;
+}
+
+bool ScValidationData::isFormulaResultsValidatable(const OUString& rTest, const ScAddress& rPos, SvNumberFormatter* pFormatter,
+ OUString& rStrResult, double& nVal, sal_uInt32& nFormat, bool& bIsVal) const
+{
+ std::optional<ScSimpleFormulaCalculator> pFCell(std::in_place, *mpDoc, rPos, rTest, true);
+ pFCell->SetLimitString(true);
+
+ bool bColRowName = pFCell->HasColRowName();
+ if (bColRowName)
+ {
+ // ColRowName from RPN-Code?
+ if (pFCell->GetCode()->GetCodeLen() <= 1)
+ { // ==1: area
+ // ==0: would be an area if...
+ OUString aBraced = "(" + rTest + ")";
+ pFCell.emplace(*mpDoc, rPos, aBraced, true);
+ pFCell->SetLimitString(true);
+ }
+ else
+ bColRowName = false;
+ }
+
+ FormulaError nErrCode = pFCell->GetErrCode();
+ if (nErrCode == FormulaError::NONE || pFCell->IsMatrix())
+ {
+ pFormatter = mpDoc->GetFormatTable();
+ const Color* pColor;
+ if (pFCell->IsMatrix())
+ {
+ rStrResult = pFCell->GetString().getString();
+ }
+ else if (pFCell->IsValue())
+ {
+ nVal = pFCell->GetValue();
+ nFormat = pFormatter->GetStandardFormat(nVal, 0,
+ pFCell->GetFormatType(), ScGlobal::eLnge);
+ pFormatter->GetOutputString(nVal, nFormat, rStrResult, &pColor);
+ bIsVal = true;
+ }
+ else
+ {
+ nFormat = pFormatter->GetStandardFormat(
+ pFCell->GetFormatType(), ScGlobal::eLnge);
+ pFormatter->GetOutputString(pFCell->GetString().getString(), nFormat,
+ rStrResult, &pColor);
+ // Indicate it's a string, so a number string doesn't look numeric.
+ // Escape embedded quotation marks first by doubling them, as
+ // usual. Actually the result can be copy-pasted from the result
+ // box as literal into a formula expression.
+ rStrResult = "\"" + rStrResult.replaceAll("\"", "\"\"") + "\"";
+ }
+
+ ScRange aTestRange;
+ if (bColRowName || (aTestRange.Parse(rTest, *mpDoc) & ScRefFlags::VALID))
+ rStrResult += " ...";
+ // area
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+namespace {
+
+/** Token array helper. Iterates over all string tokens.
+ @descr The token array must contain separated string tokens only.
+ @param bSkipEmpty true = Ignores string tokens with empty strings. */
+class ScStringTokenIterator
+{
+public:
+ explicit ScStringTokenIterator( const ScTokenArray& rTokArr ) :
+ maIter( rTokArr ), mbOk( true ) {}
+
+ /** Returns the string of the first string token or NULL on error or empty token array. */
+ rtl_uString* First();
+ /** Returns the string of the next string token or NULL on error or end of token array. */
+ rtl_uString* Next();
+
+ /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
+ bool Ok() const { return mbOk; }
+
+private:
+ svl::SharedString maCurString; /// Current string.
+ FormulaTokenArrayPlainIterator maIter;
+ bool mbOk; /// true = correct token or end of token array.
+};
+
+rtl_uString* ScStringTokenIterator::First()
+{
+ maIter.Reset();
+ mbOk = true;
+ return Next();
+}
+
+rtl_uString* ScStringTokenIterator::Next()
+{
+ if( !mbOk )
+ return nullptr;
+
+ // seek to next non-separator token
+ const FormulaToken* pToken = maIter.NextNoSpaces();
+ while( pToken && (pToken->GetOpCode() == ocSep) )
+ pToken = maIter.NextNoSpaces();
+
+ mbOk = !pToken || (pToken->GetType() == formula::svString);
+
+ maCurString = svl::SharedString(); // start with invalid string.
+ if (mbOk && pToken)
+ maCurString = pToken->GetString();
+
+ // string found but empty -> get next token; otherwise return it
+ return (maCurString.isValid() && maCurString.isEmpty()) ? Next() : maCurString.getData();
+}
+
+/** Returns the number format of the passed cell, or the standard format. */
+sal_uLong lclGetCellFormat( const ScDocument& rDoc, const ScAddress& rPos )
+{
+ const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
+ if( !pPattern )
+ pPattern = rDoc.GetDefPattern();
+ return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
+}
+
+} // namespace
+
+bool ScValidationData::HasSelectionList() const
+{
+ return (eDataMode == SC_VALID_LIST) && (mnListType != css::sheet::TableValidationVisibility::INVISIBLE);
+}
+
+bool ScValidationData::GetSelectionFromFormula(
+ std::vector<ScTypedStrData>* pStrings, ScRefCellValue& rCell, const ScAddress& rPos,
+ const ScTokenArray& rTokArr, int& rMatch) const
+{
+ bool bOk = true;
+
+ // pDoc is private in condition, use an accessor and a long winded name.
+ ScDocument* pDocument = GetDocument();
+ if( nullptr == pDocument )
+ return false;
+
+ ScFormulaCell aValidationSrc(
+ *pDocument, rPos, rTokArr, formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::Formula);
+
+ // Make sure the formula gets interpreted and a result is delivered,
+ // regardless of the AutoCalc setting.
+ aValidationSrc.Interpret();
+
+ ScMatrixRef xMatRef;
+ const ScMatrix *pValues = aValidationSrc.GetMatrix();
+ if (!pValues)
+ {
+ // The somewhat nasty case of either an error occurred, or the
+ // dereferenced value of a single cell reference or an immediate result
+ // is stored as a single value.
+
+ // Use an interim matrix to create the TypedStrData below.
+ xMatRef = new ScMatrix(1, 1, 0.0);
+
+ FormulaError nErrCode = aValidationSrc.GetErrCode();
+ if (nErrCode != FormulaError::NONE)
+ {
+ /* TODO : to use later in an alert box?
+ * OUString rStrResult = "...";
+ * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
+ */
+
+ xMatRef->PutError( nErrCode, 0, 0);
+ bOk = false;
+ }
+ else if (aValidationSrc.IsValue())
+ xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
+ else
+ {
+ svl::SharedString aStr = aValidationSrc.GetString();
+ xMatRef->PutString(aStr, 0);
+ }
+
+ pValues = xMatRef.get();
+ }
+
+ // which index matched. We will want it eventually to pre-select that item.
+ rMatch = -1;
+
+ SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();
+ sal_uInt32 nDestFormat = pDocument->GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab());
+
+ SCSIZE nCol, nRow, nCols, nRows, n = 0;
+ pValues->GetDimensions( nCols, nRows );
+
+ bool bRef = false;
+ ScRange aRange;
+
+ ScTokenArray* pArr = const_cast<ScTokenArray*>(&rTokArr);
+ if (pArr->GetLen() == 1)
+ {
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ formula::FormulaToken* t = aIter.GetNextReferenceOrName();
+ if (t)
+ {
+ OpCode eOpCode = t->GetOpCode();
+ if (eOpCode == ocDBArea || eOpCode == ocTableRef)
+ {
+ if (const ScDBData* pDBData = pDocument->GetDBCollection()->getNamedDBs().findByIndex(t->GetIndex()))
+ {
+ pDBData->GetArea(aRange);
+ bRef = true;
+ }
+ }
+ else if (eOpCode == ocName)
+ {
+ const ScRangeData* pName = pDocument->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
+ if (pName && pName->IsReference(aRange))
+ {
+ bRef = true;
+ }
+ }
+ else if (t->GetType() != svIndex)
+ {
+ if (pArr->IsValidReference(aRange, rPos))
+ {
+ bRef = true;
+ }
+ }
+ }
+ }
+
+ bool bHaveEmpty = false;
+ svl::SharedStringPool& rSPool = pDocument->GetSharedStringPool();
+
+ /* XL artificially limits things to a single col or row in the UI but does
+ * not list the constraint in MOOXml. If a defined name or INDIRECT
+ * resulting in 1D is entered in the UI and the definition later modified
+ * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead
+ * of the curve and support 2d. In XL, values are listed row-wise, do the
+ * same. */
+ for( nRow = 0; nRow < nRows ; nRow++ )
+ {
+ for( nCol = 0; nCol < nCols ; nCol++ )
+ {
+ ScTokenArray aCondTokArr(*pDocument);
+ std::unique_ptr<ScTypedStrData> pEntry;
+ OUString aValStr;
+ ScMatrixValue nMatVal = pValues->Get( nCol, nRow);
+
+ // strings and empties
+ if( ScMatrix::IsNonValueType( nMatVal.nType ) )
+ {
+ aValStr = nMatVal.GetString().getString();
+
+ // Do not add multiple empty strings to the validation list,
+ // especially not if they'd bloat the tail with a million empty
+ // entries for a column range, fdo#61520
+ if (aValStr.isEmpty())
+ {
+ if (bHaveEmpty)
+ continue;
+ bHaveEmpty = true;
+ }
+
+ if( nullptr != pStrings )
+ pEntry.reset(new ScTypedStrData(aValStr, 0.0, 0.0, ScTypedStrData::Standard));
+
+ if (!rCell.isEmpty() && rMatch < 0)
+ aCondTokArr.AddString(rSPool.intern(aValStr));
+ }
+ else
+ {
+ FormulaError nErr = nMatVal.GetError();
+
+ if( FormulaError::NONE != nErr )
+ {
+ aValStr = ScGlobal::GetErrorString( nErr );
+ }
+ else
+ {
+ // FIXME FIXME FIXME
+ // Feature regression. Date formats are lost passing through the matrix
+ //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
+ //For external reference and a formula that results in an area or array, date formats are still lost.
+ if ( bRef )
+ {
+ aValStr = pDocument->GetInputString(static_cast<SCCOL>(nCol+aRange.aStart.Col()),
+ static_cast<SCROW>(nRow+aRange.aStart.Row()), aRange.aStart.Tab());
+ }
+ else
+ {
+ pFormatter->GetInputLineString( nMatVal.fVal, nDestFormat, aValStr );
+ }
+ }
+
+ if (!rCell.isEmpty() && rMatch < 0)
+ {
+ // I am not sure errors will work here, but a user can no
+ // manually enter an error yet so the point is somewhat moot.
+ aCondTokArr.AddDouble( nMatVal.fVal );
+ }
+ if( nullptr != pStrings )
+ pEntry.reset(new ScTypedStrData(aValStr, nMatVal.fVal, nMatVal.fVal, ScTypedStrData::Value));
+ }
+
+ if (rMatch < 0 && !rCell.isEmpty() && IsEqualToTokenArray(rCell, rPos, aCondTokArr))
+ {
+ rMatch = n;
+ // short circuit on the first match if not filling the list
+ if( nullptr == pStrings )
+ return true;
+ }
+
+ if( pEntry )
+ {
+ assert(pStrings);
+ pStrings->push_back(*pEntry);
+ n++;
+ }
+ }
+ }
+
+ // In case of no match needed and an error occurred, return that error
+ // entry as valid instead of silently failing.
+ return bOk || rCell.isEmpty();
+}
+
+bool ScValidationData::FillSelectionList(std::vector<ScTypedStrData>& rStrColl, const ScAddress& rPos) const
+{
+ bool bOk = false;
+
+ if( HasSelectionList() )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr( CreateFlatCopiedTokenArray(0) );
+
+ // *** try if formula is a string list ***
+
+ sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
+ ScStringTokenIterator aIt( *pTokArr );
+ for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
+ {
+ double fValue;
+ OUString aStr(pString);
+ bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue);
+ rStrColl.emplace_back(
+ aStr, fValue, fValue, bIsValue ? ScTypedStrData::Value : ScTypedStrData::Standard);
+ }
+ bOk = aIt.Ok();
+
+ // *** if not a string list, try if formula results in a cell range or
+ // anything else we recognize as valid ***
+
+ if (!bOk)
+ {
+ int nMatch;
+ ScRefCellValue aEmptyCell;
+ bOk = GetSelectionFromFormula(&rStrColl, aEmptyCell, rPos, *pTokArr, nMatch);
+ }
+ }
+
+ return bOk;
+}
+
+bool ScValidationData::IsEqualToTokenArray( ScRefCellValue& rCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
+{
+ // create a condition entry that tests on equality and set the passed token array
+ ScConditionEntry aCondEntry( ScConditionMode::Equal, &rTokArr, nullptr, *GetDocument(), rPos );
+ return aCondEntry.IsCellValid(rCell, rPos);
+}
+
+bool ScValidationData::IsListValid( ScRefCellValue& rCell, const ScAddress& rPos ) const
+{
+ bool bIsValid = false;
+
+ /* Compare input cell with all supported tokens from the formula.
+ Currently a formula may contain:
+ 1) A list of strings (at least one string).
+ 2) A single cell or range reference.
+ 3) A single defined name (must contain a cell/range reference, another
+ name, or DB range, or a formula resulting in a cell/range reference
+ or matrix/array).
+ 4) A single database range.
+ 5) A formula resulting in a cell/range reference or matrix/array.
+ */
+
+ std::unique_ptr< ScTokenArray > pTokArr( CreateFlatCopiedTokenArray( 0 ) );
+
+ // *** try if formula is a string list ***
+
+ svl::SharedStringPool& rSPool = GetDocument()->GetSharedStringPool();
+ sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
+ ScStringTokenIterator aIt( *pTokArr );
+ for (rtl_uString* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next())
+ {
+ /* Do not break the loop, if a valid string has been found.
+ This is to find invalid tokens following in the formula. */
+ if( !bIsValid )
+ {
+ // create a formula containing a single string or number
+ ScTokenArray aCondTokArr(*GetDocument());
+ double fValue;
+ OUString aStr(pString);
+ if (GetDocument()->GetFormatTable()->IsNumberFormat(aStr, nFormat, fValue))
+ aCondTokArr.AddDouble( fValue );
+ else
+ aCondTokArr.AddString(rSPool.intern(aStr));
+
+ bIsValid = IsEqualToTokenArray(rCell, rPos, aCondTokArr);
+ }
+ }
+
+ if( !aIt.Ok() )
+ bIsValid = false;
+
+ // *** if not a string list, try if formula results in a cell range or
+ // anything else we recognize as valid ***
+
+ if (!bIsValid)
+ {
+ int nMatch;
+ bIsValid = GetSelectionFromFormula(nullptr, rCell, rPos, *pTokArr, nMatch);
+ bIsValid = bIsValid && nMatch >= 0;
+ }
+
+ return bIsValid;
+}
+
+ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList)
+{
+ // for Ref-Undo - real copy with new tokens!
+
+ for (const auto& rxItem : rList)
+ {
+ InsertNew( std::unique_ptr<ScValidationData>(rxItem->Clone()) );
+ }
+
+ //TODO: faster insert for sorted entries from rList ???
+}
+
+ScValidationDataList::ScValidationDataList(ScDocument& rNewDoc,
+ const ScValidationDataList& rList)
+{
+ // for new documents - real copy with new tokens!
+
+ for (const auto& rxItem : rList)
+ {
+ InsertNew( std::unique_ptr<ScValidationData>(rxItem->Clone(&rNewDoc)) );
+ }
+
+ //TODO: faster insert for sorted entries from rList ???
+}
+
+ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
+{
+ //TODO: binary search
+
+ for( iterator it = begin(); it != end(); ++it )
+ if( (*it)->GetKey() == nKey )
+ return it->get();
+
+ OSL_FAIL("ScValidationDataList: Entry not found");
+ return nullptr;
+}
+
+void ScValidationDataList::CompileXML()
+{
+ for( iterator it = begin(); it != end(); ++it )
+ (*it)->CompileXML();
+}
+
+void ScValidationDataList::UpdateReference( sc::RefUpdateContext& rCxt )
+{
+ for( iterator it = begin(); it != end(); ++it )
+ (*it)->UpdateReference(rCxt);
+}
+
+void ScValidationDataList::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ for (iterator it = begin(); it != end(); ++it)
+ (*it)->UpdateInsertTab(rCxt);
+}
+
+void ScValidationDataList::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ for (iterator it = begin(); it != end(); ++it)
+ (*it)->UpdateDeleteTab(rCxt);
+}
+
+void ScValidationDataList::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
+{
+ for (iterator it = begin(); it != end(); ++it)
+ (*it)->UpdateMoveTab(rCxt);
+}
+
+ScValidationDataList::iterator ScValidationDataList::begin()
+{
+ return maData.begin();
+}
+
+ScValidationDataList::const_iterator ScValidationDataList::begin() const
+{
+ return maData.begin();
+}
+
+ScValidationDataList::iterator ScValidationDataList::end()
+{
+ return maData.end();
+}
+
+ScValidationDataList::const_iterator ScValidationDataList::end() const
+{
+ return maData.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/addinhelpid.hxx b/sc/source/core/inc/addinhelpid.hxx
new file mode 100644
index 0000000000..ccc5406d8f
--- /dev/null
+++ b/sc/source/core/inc/addinhelpid.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+
+struct ScUnoAddInHelpId;
+
+/** Generates help IDs for standard Calc AddIns. */
+class ScUnoAddInHelpIdGenerator
+{
+private:
+ const ScUnoAddInHelpId* pCurrHelpIds; /// Array of function names and help IDs.
+ sal_uInt32 nArrayCount; /// Count of array entries.
+
+public:
+ ScUnoAddInHelpIdGenerator() = delete;
+ ScUnoAddInHelpIdGenerator( std::u16string_view rServiceName );
+
+ /** Sets service name of the AddIn. Has to be done before requesting help IDs. */
+ void SetServiceName( std::u16string_view rServiceName );
+
+ /** @return The help ID of the function with given built-in name or 0 if not found. */
+ OUString GetHelpId( std::u16string_view rFuncName ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/addinlis.hxx b/sc/source/core/inc/addinlis.hxx
new file mode 100644
index 0000000000..d1cc7f3dd8
--- /dev/null
+++ b/sc/source/core/inc/addinlis.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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "adiasync.hxx"
+#include <com/sun/star/sheet/XResultListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::sheet { class XVolatileResult; }
+namespace rtl { template <class reference_type> class Reference; }
+
+class ScDocument;
+
+class ScAddInListener final : public cppu::WeakImplHelper<
+ css::sheet::XResultListener,
+ css::lang::XServiceInfo >,
+ public SvtBroadcaster
+{
+private:
+ css::uno::Reference<css::sheet::XVolatileResult> xVolRes;
+ css::uno::Any aResult;
+ std::unique_ptr<ScAddInDocs> pDocs; // documents where this is used
+
+ static ::std::vector<rtl::Reference<ScAddInListener>> aAllListeners;
+
+ // always allocated via CreateListener
+ ScAddInListener( css::uno::Reference<css::sheet::XVolatileResult> xVR,
+ ScDocument* pD );
+
+public:
+ virtual ~ScAddInListener() override;
+
+ // create Listener and put it into global list
+ static ScAddInListener* CreateListener(
+ const css::uno::Reference<css::sheet::XVolatileResult>& xVR,
+ ScDocument* pDoc );
+
+ static ScAddInListener* Get( const css::uno::Reference<css::sheet::XVolatileResult>& xVR );
+
+ static void RemoveDocument( ScDocument* pDocument );
+
+ bool HasDocument( ScDocument* pDoc ) const
+ { return pDocs->find( pDoc ) != pDocs->end(); }
+
+ void AddDocument( ScDocument* pDoc )
+ { pDocs->insert( pDoc ); }
+
+ const css::uno::Any& GetResult() const
+ { return aResult; }
+
+ // XResultListener
+ virtual void SAL_CALL modified( const css::sheet::ResultEvent& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/adiasync.hxx b/sc/source/core/inc/adiasync.hxx
new file mode 100644
index 0000000000..b693f12a28
--- /dev/null
+++ b/sc/source/core/inc/adiasync.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svl/broadcast.hxx>
+#include <set>
+#include <tools/solar.h>
+
+#include <callform.hxx>
+
+extern "C" {
+void CALLTYPE ScAddInAsyncCallBack( double& nHandle, void* pData );
+}
+
+class ScDocument;
+using ScAddInDocs = std::set<ScDocument*>;
+
+class ScAddInAsync final : public SvtBroadcaster
+{
+private:
+ union
+ {
+ double nVal; // current value
+ OUString* pStr;
+ };
+ std::unique_ptr<ScAddInDocs> pDocs; // List of using documents
+ LegacyFuncData* mpFuncData; // Pointer to data in collection
+ sal_uLong nHandle; // is casted from double to sal_uLong
+ ParamType meType; // result of type PTR_DOUBLE or PTR_STRING
+ bool bValid; // is value valid?
+
+public:
+ // cTor only if ScAddInAsync::Get fails.
+ // nIndex: Index from FunctionCollection
+ ScAddInAsync(sal_uLong nHandle, LegacyFuncData* pFuncData, ScDocument* pDoc);
+ virtual ~ScAddInAsync() override;
+ static ScAddInAsync* Get( sal_uLong nHandle );
+ static void CallBack( sal_uLong nHandle, void* pData );
+ static void RemoveDocument( ScDocument* pDocument );
+ bool IsValid() const { return bValid; }
+ ParamType GetType() const { return meType; }
+ double GetValue() const { return nVal; }
+ const OUString& GetString() const { return *pStr; }
+ bool HasDocument( ScDocument* pDoc ) const
+ { return pDocs->find( pDoc ) != pDocs->end(); }
+ void AddDocument( ScDocument* pDoc ) { pDocs->insert( pDoc ); }
+
+ // Comparators for PtrArrSort
+ bool operator< ( const ScAddInAsync& r ) const { return nHandle < r.nHandle; }
+};
+
+struct CompareScAddInAsync
+{
+ bool operator()( std::unique_ptr<ScAddInAsync> const& lhs, std::unique_ptr<ScAddInAsync> const& rhs ) const { return (*lhs)<(*rhs); }
+};
+using ScAddInAsyncs = std::set<std::unique_ptr<ScAddInAsync>, CompareScAddInAsync>;
+
+extern ScAddInAsyncs theAddInAsyncTbl; // in adiasync.cxx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx
new file mode 100644
index 0000000000..6e891e8aab
--- /dev/null
+++ b/sc/source/core/inc/bcaslot.hxx
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include <unordered_set>
+
+#include <svl/broadcast.hxx>
+#include <svl/hint.hxx>
+#include <tools/solar.h>
+
+#include <document.hxx>
+#include <global.hxx>
+
+namespace sc {
+
+struct BroadcasterState;
+class ColumnSpanSet;
+
+}
+class ScHint;
+
+namespace sc {
+
+struct AreaListener
+{
+ ScRange maArea;
+ bool mbGroupListening;
+ SvtListener* mpListener;
+};
+
+}
+
+/**
+ Used in a Unique Associative Container.
+ */
+
+class ScBroadcastArea
+{
+private:
+ ScBroadcastArea* pUpdateChainNext;
+ SvtBroadcaster aBroadcaster;
+ ScRange aRange;
+ sal_uLong nRefCount;
+
+ bool mbInUpdateChain:1;
+ bool mbGroupListening:1;
+
+public:
+ ScBroadcastArea(const ScBroadcastArea&) = delete;
+ const ScBroadcastArea& operator=(const ScBroadcastArea&) = delete;
+
+ ScBroadcastArea( const ScRange& rRange );
+
+ SvtBroadcaster& GetBroadcaster() { return aBroadcaster; }
+ const SvtBroadcaster& GetBroadcaster() const { return aBroadcaster; }
+ void UpdateRange( const ScRange& rNewRange )
+ { aRange = rNewRange; }
+ const ScRange& GetRange() const { return aRange; }
+ void IncRef() { ++nRefCount; }
+ sal_uLong DecRef() { return nRefCount ? --nRefCount : 0; }
+ sal_uLong GetRef() const { return nRefCount; }
+ ScBroadcastArea* GetUpdateChainNext() const { return pUpdateChainNext; }
+ void SetUpdateChainNext( ScBroadcastArea* p ) { pUpdateChainNext = p; }
+ bool IsInUpdateChain() const { return mbInUpdateChain; }
+ void SetInUpdateChain( bool b ) { mbInUpdateChain = b; }
+
+ bool IsGroupListening() const { return mbGroupListening; }
+ void SetGroupListening( bool b ) { mbGroupListening = b; }
+
+ /** Equalness of this or range. */
+ inline bool operator==( const ScBroadcastArea & rArea ) const;
+};
+
+inline bool ScBroadcastArea::operator==( const ScBroadcastArea & rArea ) const
+{
+ return aRange == rArea.aRange && mbGroupListening == rArea.mbGroupListening;
+}
+
+struct ScBroadcastAreaEntry
+{
+ ScBroadcastArea* mpArea;
+ mutable bool mbErasure; ///< TRUE if marked for erasure in this set
+
+ ScBroadcastAreaEntry( ScBroadcastArea* p ) : mpArea( p), mbErasure( false) {}
+};
+
+struct ScBroadcastAreaHash
+{
+ size_t operator()( const ScBroadcastAreaEntry& rEntry ) const
+ {
+ return rEntry.mpArea->GetRange().hashArea() + static_cast<size_t>(rEntry.mpArea->IsGroupListening());
+ }
+};
+
+struct ScBroadcastAreaEqual
+{
+ bool operator()( const ScBroadcastAreaEntry& rEntry1, const ScBroadcastAreaEntry& rEntry2) const
+ {
+ return *rEntry1.mpArea == *rEntry2.mpArea;
+ }
+};
+
+typedef std::unordered_set< ScBroadcastAreaEntry, ScBroadcastAreaHash, ScBroadcastAreaEqual > ScBroadcastAreas;
+
+struct ScBroadcastAreaBulkHash
+{
+ size_t operator()( const ScBroadcastArea* p ) const
+ {
+ return reinterpret_cast<size_t>(p);
+ }
+};
+
+struct ScBroadcastAreaBulkEqual
+{
+ bool operator()( const ScBroadcastArea* p1, const ScBroadcastArea* p2) const
+ {
+ return p1 == p2;
+ }
+};
+
+typedef std::unordered_set< const ScBroadcastArea*, ScBroadcastAreaBulkHash,
+ ScBroadcastAreaBulkEqual > ScBroadcastAreasBulk;
+
+class ScBroadcastAreaSlotMachine;
+
+/// Collection of BroadcastAreas
+class ScBroadcastAreaSlot
+{
+private:
+ ScBroadcastAreas aBroadcastAreaTbl;
+ mutable ScBroadcastArea aTmpSeekBroadcastArea; // for FindBroadcastArea()
+ ScDocument* pDoc;
+ ScBroadcastAreaSlotMachine* pBASM;
+ bool mbInBroadcastIteration;
+
+ /**
+ * If true, the slot has at least one area broadcaster marked for removal.
+ * This flag is used only during broadcast iteration, to speed up
+ * iteration. Using this flag is cheaper than dereferencing each iterator
+ * and checking its own flag inside especially when no areas are marked
+ * for removal.
+ */
+ bool mbHasErasedArea;
+
+ ScBroadcastAreas::iterator FindBroadcastArea( const ScRange& rRange, bool bGroupListening );
+
+ /**
+ More hypothetical (memory would probably be doomed anyway) check
+ whether there would be an overflow when adding an area, setting the
+ proper state if so.
+
+ @return HardRecalcState::ETERNAL if a HardRecalcState is effective and
+ area is not to be added.
+ */
+ ScDocument::HardRecalcState CheckHardRecalcStateCondition() const;
+
+ /** Finally erase all areas pushed as to-be-erased. */
+ void FinallyEraseAreas();
+
+ static bool isMarkedErased( const ScBroadcastAreas::const_iterator& rIter )
+ {
+ return rIter->mbErasure;
+ }
+
+public:
+ ScBroadcastAreaSlot( ScDocument* pDoc,
+ ScBroadcastAreaSlotMachine* pBASM );
+ ~ScBroadcastAreaSlot();
+
+ /**
+ Only here new ScBroadcastArea objects are created, prevention of dupes.
+
+ @param rpArea
+ If NULL, a new ScBroadcastArea is created and assigned ton the
+ reference if a matching area wasn't found. If a matching area was
+ found, that is assigned. In any case, the SvtListener is added to
+ the broadcaster.
+
+ If not NULL then no listeners are started, only the area is
+ inserted and the reference count incremented. Effectively the same
+ as InsertListeningArea(), so use that instead.
+
+ @return
+ true if rpArea passed was NULL and ScBroadcastArea is newly
+ created.
+ */
+ bool StartListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea );
+
+ /**
+ Insert a ScBroadcastArea obtained via StartListeningArea() to
+ subsequent slots.
+ */
+ void InsertListeningArea( ScBroadcastArea* pArea );
+
+ void EndListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea );
+
+ bool AreaBroadcast( const ScRange& rRange, SfxHintId nHint );
+ bool AreaBroadcast( const ScHint& rHint );
+ void DelBroadcastAreasInRange( const ScRange& rRange );
+ void UpdateRemove( UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange,
+ SCCOL nDx, SCROW nDy, SCTAB nDz );
+ void UpdateRemoveArea( ScBroadcastArea* pArea );
+ void UpdateInsert( ScBroadcastArea* pArea );
+
+ bool IsInBroadcastIteration() const { return mbInBroadcastIteration; }
+
+ /** Erase an area from set and delete it if last reference, or if
+ mbInBroadcastIteration is set push it to the vector of to-be-erased
+ areas instead.
+
+ Meant to be used internally and from ScBroadcastAreaSlotMachine only.
+ */
+ void EraseArea( ScBroadcastAreas::iterator& rIter );
+
+ void GetAllListeners(
+ const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
+ sc::AreaOverlapType eType, sc::ListenerGroupType eGroup );
+
+ void CollectBroadcasterState(sc::BroadcasterState& rState) const;
+};
+
+/**
+ BroadcastAreaSlots and their management, once per document.
+ */
+
+class ScBroadcastAreaSlotMachine
+{
+private:
+ typedef std::map<ScBroadcastArea*, sc::ColumnSpanSet> BulkGroupAreasType;
+
+ /**
+ Slot offset arrangement of columns and rows, once per sheet.
+
+ +---+---+
+ | 0 | 3 |
+ +---+---+
+ | 1 | 4 |
+ +---+---+
+ | 2 | 5 |
+ +---+---+
+ */
+
+ class TableSlots
+ {
+ public:
+ TableSlots(SCSIZE nBcaSlots);
+ TableSlots(TableSlots&&) noexcept;
+ ~TableSlots();
+ ScBroadcastAreaSlot** getSlots() const { return ppSlots.get(); }
+
+ private:
+ SCSIZE mnBcaSlots;
+ std::unique_ptr<ScBroadcastAreaSlot*[]> ppSlots;
+
+ TableSlots( const TableSlots& ) = delete;
+ TableSlots& operator=( const TableSlots& ) = delete;
+ };
+
+ typedef ::std::map< SCTAB, TableSlots > TableSlotsMap;
+
+ typedef ::std::vector< ::std::pair< ScBroadcastAreaSlot*, ScBroadcastAreas::iterator > > AreasToBeErased;
+
+private:
+ struct ScSlotData
+ {
+ SCROW nStartRow; // first row of this segment
+ SCROW nStopRow; // first row of next segment
+ SCSIZE nSliceRow; // row slice size in this segment
+ SCSIZE nCumulatedRow; // cumulated slots of previous segments (previous rows)
+ SCROW nStartCol; // first column of this segment
+ SCROW nStopCol; // first column of next segment
+ SCSIZE nSliceCol; // column slice size in this segment
+ SCSIZE nCumulatedCol; // cumulated slots of previous segments (previous columns)
+
+ ScSlotData( SCROW r1, SCROW r2, SCSIZE sr, SCSIZE cr, SCCOL c1, SCCOL c2, SCSIZE sc, SCSIZE cc )
+ : nStartRow(r1)
+ , nStopRow(r2)
+ , nSliceRow(sr)
+ , nCumulatedRow(cr)
+ , nStartCol(c1)
+ , nStopCol(c2)
+ , nSliceCol(sc)
+ , nCumulatedCol(cc) {}
+ };
+ typedef ::std::vector< ScSlotData > ScSlotDistribution;
+ ScSlotDistribution maSlotDistribution;
+ SCSIZE mnBcaSlotsCol;
+ SCSIZE mnBcaSlots;
+ ScBroadcastAreasBulk aBulkBroadcastAreas;
+ BulkGroupAreasType m_BulkGroupAreas;
+ TableSlotsMap aTableSlotsMap;
+ AreasToBeErased maAreasToBeErased;
+ std::unique_ptr<SvtBroadcaster> pBCAlways; // for the RC_ALWAYS special range
+ ScDocument *pDoc;
+ ScBroadcastArea *pUpdateChain;
+ ScBroadcastArea *pEOUpdateChain;
+ sal_uInt32 nInBulkBroadcast;
+
+ inline SCSIZE ComputeSlotOffset( const ScAddress& rAddress ) const;
+ void ComputeAreaPoints( const ScRange& rRange,
+ SCSIZE& nStart, SCSIZE& nEnd,
+ SCSIZE& nRowBreak ) const;
+#ifdef DBG_UTIL
+ void DoChecks();
+#endif
+
+public:
+ ScBroadcastAreaSlotMachine( ScDocument* pDoc );
+ ~ScBroadcastAreaSlotMachine();
+ void StartListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener );
+
+ void EndListeningArea(
+ const ScRange& rRange, bool bGroupListening, SvtListener* pListener );
+
+ bool AreaBroadcast( const ScRange& rRange, SfxHintId nHint );
+ bool AreaBroadcast( const ScHint& rHint ) const;
+ // return: at least one broadcast occurred
+ void DelBroadcastAreasInRange( const ScRange& rRange );
+ void UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange,
+ SCCOL nDx, SCROW nDy, SCTAB nDz );
+ void EnterBulkBroadcast();
+ void LeaveBulkBroadcast( SfxHintId nHintId );
+ bool InsertBulkArea( const ScBroadcastArea* p );
+
+ void InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange );
+ void RemoveBulkGroupArea( ScBroadcastArea* pArea );
+ bool BulkBroadcastGroupAreas( SfxHintId nHintId );
+
+ /// @return: how many removed
+ size_t RemoveBulkArea( const ScBroadcastArea* p );
+ void SetUpdateChain( ScBroadcastArea* p ) { pUpdateChain = p; }
+ ScBroadcastArea* GetEOUpdateChain() const { return pEOUpdateChain; }
+ void SetEOUpdateChain( ScBroadcastArea* p ) { pEOUpdateChain = p; }
+ bool IsInBulkBroadcast() const { return nInBulkBroadcast > 0; }
+
+ // only for ScBroadcastAreaSlot
+ void PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
+ ScBroadcastAreas::iterator& rIter );
+ // only for ScBroadcastAreaSlot
+ void FinallyEraseAreas( ScBroadcastAreaSlot* pSlot );
+
+ std::vector<sc::AreaListener> GetAllListeners(
+ const ScRange& rRange, sc::AreaOverlapType eType,
+ sc::ListenerGroupType eGroup = sc::ListenerGroupType::Both );
+
+ void CollectBroadcasterState(sc::BroadcasterState& rState) const;
+};
+
+class ScBulkBroadcast
+{
+ ScBroadcastAreaSlotMachine* pBASM;
+ SfxHintId mnHintId;
+
+ ScBulkBroadcast(ScBulkBroadcast const &) = delete;
+ ScBulkBroadcast(ScBulkBroadcast &&) = delete;
+ ScBulkBroadcast & operator =(ScBulkBroadcast const &) = delete;
+ ScBulkBroadcast & operator =(ScBulkBroadcast &&) = delete;
+
+public:
+ explicit ScBulkBroadcast( ScBroadcastAreaSlotMachine* p, SfxHintId nHintId ) :
+ pBASM(p),
+ mnHintId(nHintId)
+ {
+ if (pBASM)
+ pBASM->EnterBulkBroadcast();
+ }
+ ~ScBulkBroadcast() COVERITY_NOEXCEPT_FALSE
+ {
+ if (pBASM)
+ pBASM->LeaveBulkBroadcast( mnHintId );
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/cellkeytranslator.hxx b/sc/source/core/inc/cellkeytranslator.hxx
new file mode 100644
index 0000000000..953e0c99ed
--- /dev/null
+++ b/sc/source/core/inc/cellkeytranslator.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <formula/opcode.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <vector>
+#include <memory>
+#include <unordered_map>
+
+namespace com::sun::star::lang
+{
+struct Locale;
+}
+
+struct TransItem;
+
+struct ScCellKeyword
+{
+ const char* mpName;
+ OpCode meOpCode;
+ const css::lang::Locale& mrLocale;
+
+ ScCellKeyword(const char* pName, OpCode eOpCode, const css::lang::Locale& rLocale);
+};
+
+typedef std::unordered_map<OUString, ::std::vector<ScCellKeyword>> ScCellKeywordHashMap;
+
+/** Translate cell function keywords.
+
+ This class provides a convenient way to translate a string keyword used as
+ a cell function argument. Since Calc's built-in cell functions don't
+ localize string keywords, this class is used mainly to deal with an Excel
+ document where string names may be localized.
+
+ To use, simply call the
+
+ ScCellKeywordTranslator::transKeyword(...)
+
+ function.
+
+ Note that when the locale and/or the opcode is specified, the function
+ tries to find a string with matching locale and/or opcode. But when it
+ fails to find one that satisfies the specified locale and/or opcode, it
+ returns a translated string with non-matching locale and/or opcode if
+ available. */
+class ScCellKeywordTranslator
+{
+public:
+ static void transKeyword(OUString& rName, const css::lang::Locale* pLocale, OpCode eOpCode);
+ ~ScCellKeywordTranslator();
+
+private:
+ ScCellKeywordTranslator();
+
+ void addToMap(const OUString& rKey, const char* pName, const css::lang::Locale& rLocale,
+ OpCode eOpCode);
+ void addToMap(const TransItem* pItems, const css::lang::Locale& rLocale);
+
+ static ::std::unique_ptr<ScCellKeywordTranslator> spInstance;
+ ScCellKeywordHashMap maStringNameMap;
+ ::utl::TransliterationWrapper maTransWrapper;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/ddelink.hxx b/sc/source/core/inc/ddelink.hxx
new file mode 100644
index 0000000000..d872a102cc
--- /dev/null
+++ b/sc/source/core/inc/ddelink.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 .
+ */
+
+#pragma once
+
+#include <sfx2/lnkbase.hxx>
+#include <svl/broadcast.hxx>
+#include <types.hxx>
+
+namespace com::sun::star::uno { class Any; }
+
+class ScDocument;
+class ScMultipleReadHeader;
+class ScMultipleWriteHeader;
+class SvStream;
+
+class ScDdeLink final : public ::sfx2::SvBaseLink, public SvtBroadcaster
+{
+private:
+static bool bIsInUpdate;
+
+ ScDocument& rDoc;
+
+ OUString aAppl; // connection/ link data
+ OUString aTopic;
+ OUString aItem;
+ sal_uInt8 nMode; // number format mode
+
+ bool bNeedUpdate; // is set, if update was not possible
+
+ ScMatrixRef pResult;
+
+public:
+
+ ScDdeLink( ScDocument& rD,
+ OUString aA, OUString aT, OUString aI,
+ sal_uInt8 nM );
+ ScDdeLink( ScDocument& rD, SvStream& rStream, ScMultipleReadHeader& rHdr );
+ ScDdeLink( ScDocument& rD, const ScDdeLink& rOther );
+ virtual ~ScDdeLink() override;
+
+ void Store( SvStream& rStream, ScMultipleWriteHeader& rHdr ) const;
+
+ // SvBaseLink override:
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ // SvtBroadcaster override:
+ virtual void ListenersGone() override;
+
+ // for interpreter:
+
+ const ScMatrix* GetResult() const;
+ void SetResult( const ScMatrixRef& pRes );
+
+ const OUString& GetAppl() const { return aAppl; }
+ const OUString& GetTopic() const { return aTopic; }
+ const OUString& GetItem() const { return aItem; }
+ sal_uInt8 GetMode() const { return nMode; }
+
+ void TryUpdate();
+
+ bool NeedsUpdate() const { return bNeedUpdate; }
+
+ static bool IsInUpdate() { return bIsInUpdate; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/doubleref.hxx b/sc/source/core/inc/doubleref.hxx
new file mode 100644
index 0000000000..cda9241590
--- /dev/null
+++ b/sc/source/core/inc/doubleref.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <address.hxx>
+#include <types.hxx>
+
+class ScDocument;
+struct ScDBQueryParamBase;
+struct ScQueryParamBase;
+enum class FormulaError : sal_uInt16;
+
+/**
+ * Base class for abstracting range data backends for database functions.
+ */
+class ScDBRangeBase
+{
+public:
+ ScDBRangeBase() = delete;
+
+ virtual ~ScDBRangeBase() = 0;
+
+ bool fillQueryEntries(ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef) const;
+
+ virtual SCCOL getColSize() const = 0;
+ virtual SCROW getRowSize() const = 0;
+ virtual SCSIZE getVisibleDataCellCount() const = 0;
+
+ /**
+ * Get a string value of a specified cell position. Note that the
+ * position of the upper left cell of the range is always (0, 0) even if
+ * the reference type is of internal range.
+ *
+ * @param nCol column position (0 to column size-1)
+ * @param nRow row position (0 to row size-1)
+ */
+ virtual OUString getString(SCCOL nCol, SCROW nRow) const = 0;
+
+ virtual SCCOL getFirstFieldColumn() const = 0;
+
+ /**
+ * Get a <i>0-based</i> column index that corresponds with the passed field
+ * index. Note that the field index passed as the 1st parameter is
+ * <i>1-based.</i>
+ *
+ * @param nIndex 1-based field index.
+ *
+ * @return 0-based column index
+ */
+ virtual SCCOL findFieldColumn(SCCOL nIndex) const = 0;
+ virtual SCCOL findFieldColumn(const OUString& rStr, FormulaError* pErr = nullptr) const = 0;
+ virtual std::unique_ptr<ScDBQueryParamBase>
+ createQueryParam(const ScDBRangeBase* pQueryRef) const = 0;
+ virtual bool isRangeEqual(const ScRange& rRange) const = 0;
+
+protected:
+ ScDBRangeBase(ScDocument* pDoc);
+ ScDocument* getDoc() const { return mpDoc; }
+
+ /**
+ * Populate query options that are always the same for all database
+ * queries.
+ */
+ static void fillQueryOptions(ScQueryParamBase* pParam);
+
+private:
+ ScDocument* mpDoc;
+};
+
+class ScDBInternalRange final : public ScDBRangeBase
+{
+public:
+ explicit ScDBInternalRange(ScDocument* pDoc, const ScRange& rRange);
+ virtual ~ScDBInternalRange() override;
+
+ const ScRange& getRange() const { return maRange; }
+
+ virtual SCCOL getColSize() const override;
+ virtual SCROW getRowSize() const override;
+ virtual SCSIZE getVisibleDataCellCount() const override;
+
+ /**
+ * Get a string value of a specified cell position. Note that the
+ * position of the upper left cell of the range is always (0, 0) even if
+ * the reference type is of internal range.
+ *
+ * @param nCol column position (0 to column size-1)
+ * @param nRow row position (0 to row size-1)
+ */
+ virtual OUString getString(SCCOL nCol, SCROW nRow) const override;
+
+ virtual SCCOL getFirstFieldColumn() const override;
+ /**
+ * Get a <i>0-based</i> column index that corresponds with the passed field
+ * index. Note that the field index passed as the 1st parameter is
+ * <i>1-based.</i>
+ *
+ * @param nIndex 1-based field index.
+ *
+ * @return 0-based column index
+ */
+ virtual SCCOL findFieldColumn(SCCOL nIndex) const override;
+ virtual SCCOL findFieldColumn(const OUString& rStr,
+ FormulaError* pErr = nullptr) const override;
+ virtual std::unique_ptr<ScDBQueryParamBase>
+ createQueryParam(const ScDBRangeBase* pQueryRef) const override;
+ virtual bool isRangeEqual(const ScRange& rRange) const override;
+
+private:
+ ScRange maRange;
+};
+
+class ScDBExternalRange final : public ScDBRangeBase
+{
+public:
+ explicit ScDBExternalRange(ScDocument* pDoc, ScMatrixRef pMat);
+ virtual ~ScDBExternalRange() override;
+
+ virtual SCCOL getColSize() const override;
+ virtual SCROW getRowSize() const override;
+ virtual SCSIZE getVisibleDataCellCount() const override;
+
+ /**
+ * Get a string value of a specified cell position. Note that the
+ * position of the upper left cell of the range is always (0, 0) even if
+ * the reference type is of internal range.
+ *
+ * @param nCol column position (0 to column size-1)
+ * @param nRow row position (0 to row size-1)
+ */
+ virtual OUString getString(SCCOL nCol, SCROW nRow) const override;
+
+ virtual SCCOL getFirstFieldColumn() const override;
+
+ /**
+ * Get a <i>0-based</i> column index that corresponds with the passed field
+ * index. Note that the field index passed as the 1st parameter is
+ * <i>1-based.</i>
+ *
+ * @param nIndex 1-based field index.
+ *
+ * @return 0-based column index
+ */
+ virtual SCCOL findFieldColumn(SCCOL nIndex) const override;
+ virtual SCCOL findFieldColumn(const OUString& rStr,
+ FormulaError* pErr = nullptr) const override;
+ virtual std::unique_ptr<ScDBQueryParamBase>
+ createQueryParam(const ScDBRangeBase* pQueryRef) const override;
+ virtual bool isRangeEqual(const ScRange& rRange) const override;
+
+private:
+ const ScMatrixRef mpMatrix;
+ SCCOL mnCols;
+ SCROW mnRows;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/formulagroupcl.hxx b/sc/source/core/inc/formulagroupcl.hxx
new file mode 100644
index 0000000000..586b21f4c5
--- /dev/null
+++ b/sc/source/core/inc/formulagroupcl.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <formulagroup.hxx>
+
+namespace sc::opencl {
+
+class FormulaGroupInterpreterOpenCL final : public FormulaGroupInterpreter
+{
+public:
+ FormulaGroupInterpreterOpenCL();
+ virtual ~FormulaGroupInterpreterOpenCL() override;
+
+ virtual ScMatrixRef inverseMatrix( const ScMatrix& rMat ) override;
+ virtual bool interpret( ScDocument& rDoc, const ScAddress& rTopPos,
+ ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode ) override;
+};
+
+} // namespace sc::opencl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/grouptokenconverter.hxx b/sc/source/core/inc/grouptokenconverter.hxx
new file mode 100644
index 0000000000..0d2f02227b
--- /dev/null
+++ b/sc/source/core/inc/grouptokenconverter.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include <formulalogger.hxx>
+
+class ScDocument;
+class ScFormulaCell;
+class ScTokenArray;
+namespace sc { struct FormulaGroupContext; }
+
+class ScGroupTokenConverter
+{
+ ScTokenArray& mrGroupTokens;
+ ScDocument& mrDoc;
+ std::shared_ptr<sc::FormulaGroupContext> mxFormulaGroupContext;
+ const ScFormulaCell& mrCell;
+ const ScAddress& mrPos;
+
+ bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow);
+ bool isSelfReferenceAbsolute(const ScAddress& rRefPos);
+ SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen);
+
+public:
+ ScGroupTokenConverter(ScTokenArray& rGroupTokens, ScDocument& rDoc, const ScFormulaCell& rCell, const ScAddress& rPos);
+
+ bool convert( const ScTokenArray& rCode, sc::FormulaLogger::GroupScope& rScope );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
new file mode 100644
index 0000000000..64b9a8ae5b
--- /dev/null
+++ b/sc/source/core/inc/interpre.hxx
@@ -0,0 +1,1167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <unotools/textsearch.hxx>
+#include <formula/errorcodes.hxx>
+#include <formula/tokenarray.hxx>
+#include <types.hxx>
+#include <externalrefmgr.hxx>
+#include <calcconfig.hxx>
+#include <token.hxx>
+#include <math.hxx>
+#include <kahan.hxx>
+#include "parclass.hxx"
+
+#include <map>
+#include <memory>
+#include <vector>
+#include <limits>
+#include <ostream>
+
+namespace sfx2 { class LinkManager; }
+
+class ScDocument;
+class SbxVariable;
+class ScFormulaCell;
+class ScDBRangeBase;
+struct ScQueryParam;
+struct ScDBQueryParamBase;
+struct ScQueryEntry;
+
+struct ScSingleRefData;
+struct ScComplexRefData;
+struct ScInterpreterContext;
+
+class ScJumpMatrix;
+struct ScRefCellValue;
+
+namespace sc {
+
+struct CompareOptions;
+
+struct ParamIfsResult
+{
+ KahanSum mfSum = 0.0;
+ double mfCount = 0.0;
+ double mfMin = std::numeric_limits<double>::max();
+ double mfMax = std::numeric_limits<double>::lowest();
+};
+
+template<typename charT, typename traits>
+inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT, traits> & stream, const ParamIfsResult& rRes)
+{
+ stream << "{" <<
+ "sum=" << rRes.mfSum.get() << "," <<
+ "count=" << rRes.mfCount << "," <<
+ "min=" << rRes.mfMin << "," <<
+ "max=" << rRes.mfMax << "," <<
+ "}";
+
+ return stream;
+}
+
+}
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+/// Arbitrary 256MB result string length limit.
+constexpr sal_Int32 kScInterpreterMaxStrLen = SAL_MAX_INT32 / 8;
+
+constexpr size_t MAXSTACK = 512;
+
+class ScTokenStack
+{
+public:
+ const formula::FormulaToken* pPointer[ MAXSTACK ];
+};
+
+enum ScIterFunc {
+ ifSUM, // Add up
+ ifSUMSQ, // Sums of squares
+ ifPRODUCT, // Product
+ ifAVERAGE, // Average
+ ifCOUNT, // Count Values
+ ifCOUNT2, // Count Values (not empty)
+ ifMIN, // Minimum
+ ifMAX // Maximum
+};
+
+enum ScIterFuncIf
+{
+ ifSUMIF, // Conditional sum
+ ifAVERAGEIF // Conditional average
+};
+
+enum ScETSType
+{
+ etsAdd,
+ etsMult,
+ etsSeason,
+ etsPIAdd,
+ etsPIMult,
+ etsStatAdd,
+ etsStatMult
+};
+
+struct FormulaTokenRef_less
+{
+ bool operator () ( const formula::FormulaConstTokenRef& r1, const formula::FormulaConstTokenRef& r2 ) const
+ { return r1.get() < r2.get(); }
+};
+typedef ::std::map< const formula::FormulaConstTokenRef, formula::FormulaConstTokenRef, FormulaTokenRef_less> ScTokenMatrixMap;
+
+class ScInterpreter
+{
+ // distribution function objects need the GetxxxDist methods
+ friend class ScGammaDistFunction;
+ friend class ScBetaDistFunction;
+ friend class ScTDistFunction;
+ friend class ScFDistFunction;
+ friend class ScChiDistFunction;
+ friend class ScChiSqDistFunction;
+
+public:
+ static void SetGlobalConfig(const ScCalcConfig& rConfig);
+ static const ScCalcConfig& GetGlobalConfig();
+
+ static void GlobalExit(); // called by ScGlobal::Clear()
+
+ /** Detect if string should be used as regular expression or wildcard
+ expression or literal string.
+ */
+ static utl::SearchParam::SearchType DetectSearchType(std::u16string_view rStr, const ScDocument& rDoc );
+
+ /// Fail safe division, returning a FormulaError::DivisionByZero coded into a double
+ /// if denominator is 0.0
+ static inline double div( const double& fNumerator, const double& fDenominator );
+
+ ScMatrixRef GetNewMat(SCSIZE nC, SCSIZE nR, bool bEmpty = false);
+
+ ScMatrixRef GetNewMat(SCSIZE nC, SCSIZE nR, const std::vector<double>& rValues);
+
+ enum VolatileType {
+ VOLATILE,
+ VOLATILE_MACRO,
+ NOT_VOLATILE
+ };
+
+ VolatileType GetVolatileType() const { return meVolatileType;}
+
+private:
+ static ScCalcConfig& GetOrCreateGlobalConfig();
+ static ScCalcConfig *mpGlobalConfig;
+
+ static thread_local std::unique_ptr<ScTokenStack> pGlobalStack;
+ static thread_local bool bGlobalStackInUse;
+
+ ScCalcConfig maCalcConfig;
+ formula::FormulaTokenIterator aCode;
+ ScAddress aPos;
+ ScTokenArray* pArr;
+ ScInterpreterContext& mrContext;
+ ScDocument& mrDoc;
+ sfx2::LinkManager* mpLinkManager;
+ svl::SharedStringPool& mrStrPool;
+ formula::FormulaConstTokenRef xResult;
+ ScJumpMatrix* pJumpMatrix; // currently active array condition, if any
+ ScTokenMatrixMap maTokenMatrixMap; // map FormulaToken* to formula::FormulaTokenRef if in array condition
+ ScFormulaCell* pMyFormulaCell; // the cell of this formula expression
+ SvNumberFormatter* pFormatter;
+
+ const formula::FormulaToken* pCur; // current token
+ ScTokenStack* pStackObj; // contains the stacks
+ const formula::FormulaToken ** pStack; // the current stack
+ FormulaError nGlobalError; // global (local to this formula expression) error
+ sal_uInt16 sp; // stack pointer
+ sal_uInt16 maxsp; // the maximal used stack pointer
+ sal_uInt32 nFuncFmtIndex; // NumberFormatIndex of a function
+ sal_uInt32 nCurFmtIndex; // current NumberFormatIndex
+ sal_uInt32 nRetFmtIndex; // NumberFormatIndex of an expression, if any
+ SvNumFormatType nFuncFmtType; // NumberFormatType of a function
+ SvNumFormatType nCurFmtType; // current NumberFormatType
+ SvNumFormatType nRetFmtType; // NumberFormatType of an expression
+ FormulaError mnStringNoValueError; // the error set in ConvertStringToValue() if no value
+ SubtotalFlags mnSubTotalFlags; // flags for subtotal and aggregate functions
+ sal_uInt8 cPar; // current count of parameters
+ bool bCalcAsShown; // precision as shown
+ bool bMatrixFormula; // formula cell is a matrix formula
+
+ VolatileType meVolatileType;
+
+ void MakeMatNew(ScMatrixRef& rMat, SCSIZE nC, SCSIZE nR);
+
+ /// Merge global and document specific settings.
+ void MergeCalcConfig();
+
+ // nMust <= nAct <= nMax ? ok : PushError
+ inline bool MustHaveParamCount( short nAct, short nMust );
+ inline bool MustHaveParamCount( short nAct, short nMust, short nMax );
+ inline bool MustHaveParamCountMin( short nAct, short nMin );
+ inline bool MustHaveParamCountMinWithStackCheck( short nAct, short nMin );
+ void PushParameterExpected();
+ void PushIllegalParameter();
+ void PushIllegalArgument();
+ void PushNoValue();
+ void PushNA();
+
+ // Functions for accessing a document
+
+ void ReplaceCell( ScAddress& ); // for TableOp
+ bool IsTableOpInRange( const ScRange& );
+ sal_uInt32 GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell );
+ double ConvertStringToValue( const OUString& );
+public:
+ static double ScGetGCD(double fx, double fy);
+ /** For matrix back calls into the current interpreter.
+ Uses rError instead of nGlobalError and rCurFmtType instead of nCurFmtType. */
+ double ConvertStringToValue( const OUString&, FormulaError& rError, SvNumFormatType& rCurFmtType );
+private:
+ double GetCellValue( const ScAddress&, ScRefCellValue& rCell );
+ double GetCellValueOrZero( const ScAddress&, ScRefCellValue& rCell );
+ double GetValueCellValue( const ScAddress&, double fOrig );
+ void GetCellString( svl::SharedString& rStr, ScRefCellValue& rCell );
+ static FormulaError GetCellErrCode( const ScRefCellValue& rCell );
+
+ bool CreateDoubleArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr);
+ bool CreateStringArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr);
+ bool CreateCellArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr);
+
+ // Stack operations
+
+ /** Does substitute with formula::FormulaErrorToken in case nGlobalError is set and the token
+ passed is not formula::FormulaErrorToken.
+ Increments RefCount of the original token if not substituted. */
+ void Push( const formula::FormulaToken& r );
+
+ /** Does not substitute with formula::FormulaErrorToken in case nGlobalError is set.
+ Used to push RPN tokens or from within Push() or tokens that are already
+ explicit formula::FormulaErrorToken. Increments RefCount. */
+ void PushWithoutError( const formula::FormulaToken& r );
+
+ /** Does substitute with formula::FormulaErrorToken in case nGlobalError is set and the token
+ passed is not formula::FormulaErrorToken.
+ Increments RefCount of the original token if not substituted.
+ ATTENTION! The token had to be allocated with `new' and must not be used
+ after this call if no RefCount was set because possibly it gets immediately
+ deleted in case of a FormulaError::StackOverflow or if substituted with formula::FormulaErrorToken! */
+ void PushTempToken( formula::FormulaToken* );
+
+ /** Pushes the token or substitutes with formula::FormulaErrorToken in case
+ nGlobalError is set and the token passed is not formula::FormulaErrorToken.
+ Increments RefCount of the original token if not substituted. */
+ void PushTokenRef( const formula::FormulaConstTokenRef& );
+
+ /** Does not substitute with formula::FormulaErrorToken in case nGlobalError is set.
+ Used to push tokens from within PushTempToken() or tokens that are already
+ explicit formula::FormulaErrorToken. Increments RefCount.
+ ATTENTION! The token had to be allocated with `new' and must not be used
+ after this call if no RefCount was set because possibly it gets immediately
+ decremented again and thus deleted in case of a FormulaError::StackOverflow! */
+ void PushTempTokenWithoutError( const formula::FormulaToken* );
+
+ /** If nGlobalError is set push formula::FormulaErrorToken.
+ If nGlobalError is not set do nothing.
+ Used in PushTempToken() and alike to simplify handling.
+ @return: <TRUE/> if nGlobalError. */
+ bool IfErrorPushError()
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushTempTokenWithoutError( new formula::FormulaErrorToken( nGlobalError));
+ return true;
+ }
+ return false;
+ }
+
+ /** Obtain cell result / content from address and push as temp token.
+
+ @param bDisplayEmptyAsString
+ is passed to ScEmptyCell in case of an empty cell result.
+
+ @param pRetTypeExpr
+ @param pRetIndexExpr
+ Obtain number format and type if _both_, type and index pointer,
+ are not NULL.
+
+ @param bFinalResult
+ If TRUE, only a standard FormulaDoubleToken is pushed.
+ If FALSE, PushDouble() is used that may push either a
+ FormulaDoubleToken or a FormulaTypedDoubleToken.
+ */
+ void PushCellResultToken( bool bDisplayEmptyAsString, const ScAddress & rAddress,
+ SvNumFormatType * pRetTypeExpr, sal_uInt32 * pRetIndexExpr, bool bFinalResult = false );
+
+ formula::FormulaConstTokenRef PopToken();
+ void Pop();
+ void PopError();
+ double PopDouble();
+ const svl::SharedString & PopString();
+ void ValidateRef( const ScSingleRefData & rRef );
+ void ValidateRef( const ScComplexRefData & rRef );
+ void ValidateRef( const ScRefList & rRefList );
+ void SingleRefToVars( const ScSingleRefData & rRef, SCCOL & rCol, SCROW & rRow, SCTAB & rTab );
+ void PopSingleRef( ScAddress& );
+ void PopSingleRef(SCCOL& rCol, SCROW &rRow, SCTAB& rTab);
+ void DoubleRefToRange( const ScComplexRefData&, ScRange&, bool bDontCheckForTableOp = false );
+ /** If formula::StackVar formula::svDoubleRef pop ScDoubleRefToken and return values of
+ ScComplexRefData.
+ Else if StackVar svRefList return values of the ScComplexRefData where
+ rRefInList is pointing to. rRefInList is incremented. If rRefInList was the
+ last element in list pop ScRefListToken and set rRefInList to 0, else
+ rParam is incremented (!) to allow usage as in
+ while(nParamCount--) PopDoubleRef(aRange,nParamCount,nRefInList);
+ */
+ void PopDoubleRef( ScRange & rRange, short & rParam, size_t & rRefInList );
+ void PopDoubleRef( ScRange&, bool bDontCheckForTableOp = false );
+ void DoubleRefToVars( const formula::FormulaToken* p,
+ SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
+ SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2 );
+ ScDBRangeBase* PopDBDoubleRef();
+ void PopDoubleRef(SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
+ SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2 );
+ // peek double ref data
+ const ScComplexRefData* GetStackDoubleRef(size_t rRefInList = 0);
+
+ void PopExternalSingleRef(sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef);
+
+ /** Guarantees that nGlobalError is set if rToken could not be obtained. */
+ void PopExternalSingleRef(ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt = nullptr);
+
+ /** Guarantees that nGlobalError is set if rToken could not be obtained. */
+ void PopExternalSingleRef(sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef,
+ ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt = nullptr);
+
+ void PopExternalDoubleRef(sal_uInt16& rFileId, OUString& rTabName, ScComplexRefData& rRef);
+ void PopExternalDoubleRef(ScExternalRefCache::TokenArrayRef& rArray);
+ void PopExternalDoubleRef(ScMatrixRef& rMat);
+ void GetExternalDoubleRef(sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& aData, ScExternalRefCache::TokenArrayRef& rArray);
+ bool PopDoubleRefOrSingleRef( ScAddress& rAdr );
+ void PopDoubleRefPushMatrix();
+ void PopRefListPushMatrixOrRef();
+ // If MatrixFormula: convert svDoubleRef to svMatrix, create JumpMatrix.
+ // Else convert area reference parameters marked as ForceArray to array.
+ // Returns true if JumpMatrix created.
+ bool ConvertMatrixParameters();
+ // If MatrixFormula: ConvertMatrixJumpConditionToMatrix()
+ inline void MatrixJumpConditionToMatrix();
+ // For MatrixFormula (preconditions already checked by
+ // MatrixJumpConditionToMatrix()): convert svDoubleRef to svMatrix, or if
+ // JumpMatrix currently in effect convert also other types to svMatrix so
+ // another JumpMatrix will be created by jump commands.
+ void ConvertMatrixJumpConditionToMatrix();
+ // If MatrixFormula or ForceArray: ConvertMatrixParameters()
+ inline bool MatrixParameterConversion();
+ // If MatrixFormula or ForceArray. Can be used within spreadsheet functions
+ // that do not depend on the formula cell's matrix size, for which only
+ // bMatrixFormula can be used.
+ inline bool IsInArrayContext() const;
+ ScMatrixRef PopMatrix();
+ sc::RangeMatrix PopRangeMatrix();
+ void QueryMatrixType(const ScMatrixRef& xMat, SvNumFormatType& rRetTypeExpr, sal_uInt32& rRetIndexExpr);
+
+ formula::FormulaToken* CreateFormulaDoubleToken( double fVal, SvNumFormatType nFmt = SvNumFormatType::NUMBER );
+ formula::FormulaToken* CreateDoubleOrTypedToken( double fVal );
+
+ void PushDouble(double nVal);
+ void PushInt( int nVal );
+ void PushStringBuffer( const sal_Unicode* pString );
+ void PushString( const OUString& rStr );
+ void PushString( const svl::SharedString& rString );
+ void PushSingleRef(SCCOL nCol, SCROW nRow, SCTAB nTab);
+ void PushDoubleRef(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2);
+ void PushExternalSingleRef(sal_uInt16 nFileId, const OUString& rTabName,
+ SCCOL nCol, SCROW nRow, SCTAB nTab);
+ void PushExternalDoubleRef(sal_uInt16 nFileId, const OUString& rTabName,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2);
+ void PushSingleRef( const ScRefAddress& rRef );
+ void PushDoubleRef( const ScRefAddress& rRef1, const ScRefAddress& rRef2 );
+ void PushMatrix( const sc::RangeMatrix& rMat );
+ void PushMatrix(const ScMatrixRef& pMat);
+ void PushError( FormulaError nError );
+ /// Raw stack type without default replacements.
+ formula::StackVar GetRawStackType();
+ /// Stack type with replacement of defaults, e.g. svMissing and formula::svEmptyCell will result in formula::svDouble.
+ formula::StackVar GetStackType();
+ // peek StackType of Parameter, Parameter 1 == TOS, 2 == TOS-1, ...
+ formula::StackVar GetStackType( sal_uInt8 nParam );
+ sal_uInt8 GetByte() const { return cPar; }
+ // reverse order of stack
+ void ReverseStack( sal_uInt8 nParamCount );
+ // generates a position-dependent SingleRef out of a DoubleRef
+ bool DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& rAdr );
+ double GetDoubleFromMatrix(const ScMatrixRef& pMat);
+ double GetDouble();
+ double GetDoubleWithDefault(double nDefault);
+ bool IsMissing() const;
+ sal_Int32 double_to_int32(double fVal);
+ /** if GetDouble() not within int32 limits sets nGlobalError and returns SAL_MAX_INT32 */
+ sal_Int32 GetInt32();
+ /** if GetDoubleWithDefault() not within int32 limits sets nGlobalError and returns SAL_MAX_INT32 */
+ sal_Int32 GetInt32WithDefault( sal_Int32 nDefault );
+ /** if GetDouble() not within int32 limits sets nGlobalError and returns SAL_MAX_INT32 */
+ sal_Int32 GetFloor32();
+ /** if GetDouble() not within int16 limits sets nGlobalError and returns SAL_MAX_INT16 */
+ sal_Int16 GetInt16();
+ /** if GetDouble() not within uint32 limits sets nGlobalError and returns SAL_MAX_UINT32 */
+ sal_uInt32 GetUInt32();
+ bool GetBool() { return GetDouble() != 0.0; }
+ /// returns TRUE if double (or error, check nGlobalError), else FALSE
+ bool GetDoubleOrString( double& rValue, svl::SharedString& rString );
+ svl::SharedString GetString();
+ svl::SharedString GetStringFromMatrix(const ScMatrixRef& pMat);
+ svl::SharedString GetStringFromDouble( const double fVal);
+ // pop matrix and obtain one element, upper left or according to jump matrix
+ ScMatValType GetDoubleOrStringFromMatrix( double& rDouble, svl::SharedString& rString );
+ ScMatrixRef CreateMatrixFromDoubleRef( const formula::FormulaToken* pToken,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2 );
+ inline ScTokenMatrixMap& GetTokenMatrixMap();
+ ScMatrixRef GetMatrix();
+ ScMatrixRef GetMatrix( short & rParam, size_t & rInRefList );
+ sc::RangeMatrix GetRangeMatrix();
+
+ void ScTableOp(); // repeated operations
+
+ // common helper functions
+
+ void CurFmtToFuncFmt()
+ { nFuncFmtType = nCurFmtType; nFuncFmtIndex = nCurFmtIndex; }
+
+ /** Check if a double is suitable as string position or length argument.
+
+ If fVal is Inf or NaN it is changed to -1, if it is less than 0 it is
+ sanitized to 0, if it is greater than some implementation defined max
+ string length it is sanitized to that max.
+
+ @return TRUE if double value fVal is suitable as string argument and was
+ not sanitized.
+ FALSE if not and fVal was adapted.
+ */
+ static inline bool CheckStringPositionArgument( double & fVal );
+
+ /** Obtain a sal_Int32 suitable as string position or length argument.
+ Returns -1 if the number is Inf or NaN or less than 0 or greater than some
+ implementation defined max string length. In these cases also sets
+ nGlobalError to FormulaError::IllegalArgument, if not already set. */
+ inline sal_Int32 GetStringPositionArgument();
+
+ // Check for String overflow of rResult+rAdd and set error and erase rResult
+ // if so. Return true if ok, false if overflow
+ inline bool CheckStringResultLen( OUString& rResult, sal_Int32 nIncrease );
+
+ // Check for String overflow of rResult+rAdd and set error and erase rResult
+ // if so. Return true if ok, false if overflow
+ inline bool CheckStringResultLen( OUStringBuffer& rResult, sal_Int32 nIncrease );
+
+ // Set error according to rVal, and set rVal to 0.0 if there was an error.
+ inline void TreatDoubleError( double& rVal );
+ // Lookup using ScLookupCache, @returns true if found and result address
+ bool LookupQueryWithCache( ScAddress & o_rResultPos,
+ const ScQueryParam & rParam, const ScComplexRefData* refData ) const;
+
+ void ScIfJump();
+ void ScIfError( bool bNAonly );
+ void ScChooseJump();
+
+ // Be sure to only call this if pStack[sp-nStackLevel] really contains a
+ // ScJumpMatrixToken, no further checks are applied!
+ // Returns true if last jump was executed and result matrix pushed.
+ bool JumpMatrix( short nStackLevel );
+
+ double Compare( ScQueryOp eOp );
+ /** @param pOptions
+ NULL means case sensitivity document option is to be used!
+ */
+ sc::RangeMatrix CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions = nullptr );
+ ScMatrixRef QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions );
+ void ScEqual();
+ void ScNotEqual();
+ void ScLess();
+ void ScGreater();
+ void ScLessEqual();
+ void ScGreaterEqual();
+ void ScAnd();
+ void ScOr();
+ void ScXor();
+ void ScNot();
+ void ScNeg();
+ void ScPercentSign();
+ void ScIntersect();
+ void ScRangeFunc();
+ void ScUnionFunc();
+ void ScPi();
+ void ScRandom();
+ void ScRandbetween();
+ void ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
+ double fFirst, double fLast );
+ void ScTrue();
+ void ScFalse();
+ void ScDeg();
+ void ScRad();
+ void ScSin();
+ void ScCos();
+ void ScTan();
+ void ScCot();
+ void ScArcSin();
+ void ScArcCos();
+ void ScArcTan();
+ void ScArcCot();
+ void ScSinHyp();
+ void ScCosHyp();
+ void ScTanHyp();
+ void ScCotHyp();
+ void ScArcSinHyp();
+ void ScArcCosHyp();
+ void ScArcTanHyp();
+ void ScArcCotHyp();
+ void ScCosecant();
+ void ScSecant();
+ void ScCosecantHyp();
+ void ScSecantHyp();
+ void ScExp();
+ void ScLn();
+ void ScLog10();
+ void ScSqrt();
+ void ScIsEmpty();
+ bool IsString();
+ void ScIsString();
+ void ScIsNonString();
+ void ScIsLogical();
+ void ScType();
+ void ScCell();
+ void ScCellExternal();
+ void ScIsRef();
+ void ScIsValue();
+ void ScIsFormula();
+ void ScFormula();
+ void ScRoman();
+ void ScArabic();
+ void ScIsNV();
+ void ScIsErr();
+ void ScIsError();
+ bool IsEven();
+ void ScIsEven();
+ void ScIsOdd();
+ void ScN();
+ void ScCode();
+ void ScTrim();
+ void ScUpper();
+ void ScProper();
+ void ScLower();
+ void ScLen();
+ void ScT();
+ void ScValue();
+ void ScNumberValue();
+ void ScClean();
+ void ScChar();
+ void ScJis();
+ void ScAsc();
+ void ScUnicode();
+ void ScUnichar();
+ void ScMin( bool bTextAsZero = false );
+ void ScMax( bool bTextAsZero = false );
+ /** Check for array of references to determine the maximum size of a return
+ column vector if in array context. */
+ size_t GetRefListArrayMaxSize( short nParamCount );
+ /** Switch to array reference list if current TOS is one and create/init or
+ update matrix and return true. Else return false. */
+ bool SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
+ const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp );
+ void IterateParameters( ScIterFunc, bool bTextAsZero = false );
+ void ScSumSQ();
+ void ScSum();
+ void ScProduct();
+ void ScAverage( bool bTextAsZero = false );
+ void ScCount();
+ void ScCount2();
+ void GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) );
+ void ScVar( bool bTextAsZero = false );
+ void ScVarP( bool bTextAsZero = false );
+ void ScStDev( bool bTextAsZero = false );
+ void ScStDevP( bool bTextAsZero = false );
+ void ScRawSubtract();
+ void ScColumns();
+ void ScRows();
+ void ScSheets();
+ void ScColumn();
+ void ScRow();
+ void ScSheet();
+ void ScMatch();
+ void IterateParametersIf( ScIterFuncIf );
+ void ScCountIf();
+ void ScSumIf();
+ void ScAverageIf();
+ void IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) );
+ void ScSumIfs();
+ void ScAverageIfs();
+ void ScCountIfs();
+ void ScCountEmptyCells();
+ void ScLookup();
+ void ScHLookup();
+ void ScVLookup();
+ void ScSubTotal();
+
+ // If upon call rMissingField==true then the database field parameter may be
+ // missing (Xcl DCOUNT() syntax), or may be faked as missing by having the
+ // value 0.0 or being exactly the entire database range reference (old SO
+ // compatibility). If this was the case then rMissingField is set to true upon
+ // return. If rMissingField==false upon call all "missing cases" are considered
+ // to be an error.
+ std::unique_ptr<ScDBQueryParamBase> GetDBParams( bool& rMissingField );
+
+ void DBIterator( ScIterFunc );
+ void ScDBSum();
+ void ScDBCount();
+ void ScDBCount2();
+ void ScDBAverage();
+ void ScDBGet();
+ void ScDBMax();
+ void ScDBMin();
+ void ScDBProduct();
+ void GetDBStVarParams( double& rVal, double& rValCount );
+ void ScDBStdDev();
+ void ScDBStdDevP();
+ void ScDBVar();
+ void ScDBVarP();
+ void ScIndirect();
+ void ScAddressFunc();
+ void ScOffset();
+ void ScIndex();
+ void ScMultiArea();
+ void ScAreas();
+ void ScCurrency();
+ void ScReplace();
+ void ScFixed();
+ void ScFind();
+ void ScExact();
+ void ScLeft();
+ void ScRight();
+ void ScSearch();
+ void ScMid();
+ void ScText();
+ void ScSubstitute();
+ void ScRept();
+ void ScRegex();
+ void ScConcat();
+ void ScConcat_MS();
+ void ScTextJoin_MS();
+ void ScIfs_MS();
+ void ScSwitch_MS();
+ void ScMinIfs_MS();
+ void ScMaxIfs_MS();
+ void ScExternal();
+ void ScMissing();
+ void ScMacro();
+ bool SetSbxVariable( SbxVariable* pVar, const ScAddress& );
+ FormulaError GetErrorType();
+ void ScErrorType();
+ void ScErrorType_ODF();
+ void ScDBArea();
+ void ScColRowNameAuto();
+ void ScGetPivotData();
+ void ScHyperLink();
+ void ScBahtText();
+ void ScBitAnd();
+ void ScBitOr();
+ void ScBitXor();
+ void ScBitRshift();
+ void ScBitLshift();
+ void ScTTT();
+ void ScDebugVar();
+
+ /** Obtain the date serial number for a given date.
+ @param bStrict
+ If false, nYear < 100 takes the two-digit year setting into account,
+ and rollover of invalid calendar dates takes place, e.g. 1999-02-31 =>
+ 1999-03-03.
+ If true, the date passed must be a valid Gregorian calendar date. No
+ two-digit expanding or rollover is done.
+
+ Date must be Gregorian, i.e. >= 1582-10-15.
+ */
+ double GetDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bStrict );
+
+ void ScGetActDate();
+ void ScGetActTime();
+ void ScGetYear();
+ void ScGetMonth();
+ void ScGetDay();
+ void ScGetDayOfWeek();
+ void ScGetWeekOfYear();
+ void ScGetIsoWeekOfYear();
+ void ScWeeknumOOo();
+ void ScEasterSunday();
+ FormulaError GetWeekendAndHolidayMasks( const sal_uInt8 nParamCount, const sal_uInt32 nNullDate,
+ ::std::vector<double>& rSortArray, bool bWeekendMask[ 7 ] );
+ FormulaError GetWeekendAndHolidayMasks_MS( const sal_uInt8 nParamCount, const sal_uInt32 nNullDate,
+ ::std::vector<double>& rSortArray, bool bWeekendMask[ 7 ], bool bWorkdayFunction );
+ static inline sal_Int16 GetDayOfWeek( sal_Int32 n );
+ void ScNetWorkdays( bool bOOXML_Version );
+ void ScWorkday_MS();
+ void ScGetHour();
+ void ScGetMin();
+ void ScGetSec();
+ void ScPlusMinus();
+ void ScAbs();
+ void ScInt();
+ void ScEven();
+ void ScOdd();
+ void ScCeil( bool bODFF );
+ void ScCeil_MS();
+ void ScCeil_Precise();
+ void ScFloor( bool bODFF );
+ void ScFloor_MS();
+ void ScFloor_Precise();
+ void RoundNumber( rtl_math_RoundingMode eMode );
+ void ScRound();
+ void ScRoundUp();
+ void ScRoundDown();
+ void ScGetDateValue();
+ void ScGetTimeValue();
+ void ScArcTan2();
+ void ScLog();
+ void ScGetDate();
+ void ScGetTime();
+ void ScGetDiffDate();
+ void ScGetDiffDate360();
+ void ScGetDateDif();
+ void ScPower();
+ void ScAmpersand();
+ void ScAdd();
+ void ScSub();
+ void ScMul();
+ void ScDiv();
+ void ScPow();
+ void ScCurrent();
+ void ScStyle();
+ void ScDde();
+ void ScBase();
+ void ScDecimal();
+ void ScConvertOOo();
+ void ScEuroConvert();
+ void ScRoundSignificant();
+ static void RoundSignificant( double fX, double fDigits, double &fRes );
+
+ // financial functions
+ void ScNPV();
+ void ScIRR();
+ void ScMIRR();
+ void ScISPMT();
+
+ static double ScGetPV(double fRate, double fNper, double fPmt,
+ double fFv, bool bPayInAdvance);
+ void ScPV();
+ void ScSYD();
+ static double ScGetDDB(double fCost, double fSalvage, double fLife,
+ double fPeriod, double fFactor);
+ void ScDDB();
+ void ScDB();
+ static double ScInterVDB(double fCost, double fSalvage, double fLife, double fLife1,
+ double fPeriod, double fFactor);
+ void ScVDB();
+ void ScPDuration();
+ void ScSLN();
+ static double ScGetPMT(double fRate, double fNper, double fPv,
+ double fFv, bool bPayInAdvance);
+ void ScPMT();
+ void ScRRI();
+ static double ScGetFV(double fRate, double fNper, double fPmt,
+ double fPv, bool bPayInAdvance);
+ void ScFV();
+ void ScNper();
+ static bool RateIteration(double fNper, double fPayment, double fPv,
+ double fFv, bool bPayType, double& fGuess);
+ void ScRate();
+ double ScGetIpmt(double fRate, double fPer, double fNper, double fPv,
+ double fFv, bool bPayInAdvance, double& fPmt);
+ void ScIpmt();
+ void ScPpmt();
+ void ScCumIpmt();
+ void ScCumPrinc();
+ void ScEffect();
+ void ScNominal();
+ void ScMod();
+ void ScIntercept();
+ void ScGCD();
+ void ScLCM();
+
+ // matrix functions
+ void ScMatValue();
+ static void MEMat(const ScMatrixRef& mM, SCSIZE n);
+ void ScMatDet();
+ void ScMatInv();
+ void ScMatMult();
+ void ScMatTrans();
+ void ScEMat();
+ void ScMatRef();
+ ScMatrixRef MatConcat(const ScMatrixRef& pMat1, const ScMatrixRef& pMat2);
+ void ScSumProduct();
+ void ScSumX2MY2();
+ void ScSumX2DY2();
+ void ScSumXMY2();
+ void ScGrowth();
+ bool CalculateSkew(KahanSum& fSum, double& fCount, std::vector<double>& values);
+ void CalculateSkewOrSkewp( bool bSkewp );
+ void CalculateSlopeIntercept(bool bSlope);
+ void CalculateSmallLarge(bool bSmall);
+ void CalculatePearsonCovar( bool _bPearson, bool _bStexy, bool _bSample ); //fdo#70000 argument _bSample is ignored if _bPearson == true
+ bool CalculateTest( bool _bTemplin
+ ,const SCSIZE nC1, const SCSIZE nC2,const SCSIZE nR1,const SCSIZE nR2
+ ,const ScMatrixRef& pMat1,const ScMatrixRef& pMat2
+ ,double& fT,double& fF);
+ void CalculateLookup(bool bHLookup);
+ bool FillEntry(ScQueryEntry& rEntry);
+ void CalculateAddSub(bool _bSub);
+ void CalculateTrendGrowth(bool _bGrowth);
+ void CalculateRGPRKP(bool _bRKP);
+ void CalculateSumX2MY2SumX2DY2(bool _bSumX2DY2);
+ void CalculateMatrixValue(const ScMatrix* pMat,SCSIZE nC,SCSIZE nR);
+ bool CheckMatrix(bool _bLOG,sal_uInt8& nCase,SCSIZE& nCX,SCSIZE& nCY,SCSIZE& nRX,SCSIZE& nRY,SCSIZE& M,SCSIZE& N,ScMatrixRef& pMatX,ScMatrixRef& pMatY);
+ void ScLinest();
+ void ScLogest();
+ void ScForecast();
+ void ScForecast_Ets( ScETSType eETSType );
+ void ScFourier();
+ void ScNoName();
+ void ScBadName();
+ // Statistics:
+ static double taylor(const double* pPolynom, sal_uInt16 nMax, double x);
+ static double gauss(double x);
+
+public:
+ static double phi(double x);
+ static double integralPhi(double x);
+ static double gaussinv(double x);
+ static double GetPercentile( ::std::vector<double> & rArray, double fPercentile );
+
+
+private:
+ double GetBetaDist(double x, double alpha, double beta); //cumulative distribution function
+ double GetBetaDistPDF(double fX, double fA, double fB); //probability density function)
+ double GetChiDist(double fChi, double fDF); // for LEGACY.CHIDIST, returns right tail
+ double GetChiSqDistCDF(double fX, double fDF); // for CHISQDIST, returns left tail
+ static double GetChiSqDistPDF(double fX, double fDF); // probability density function
+ double GetFDist(double x, double fF1, double fF2);
+ double GetTDist( double T, double fDF, int nType );
+ double Fakultaet(double x);
+ static double BinomKoeff(double n, double k);
+ double GetGamma(double x);
+ static double GetLogGamma(double x);
+ double GetBeta(double fAlpha, double fBeta);
+ static double GetLogBeta(double fAlpha, double fBeta);
+ double GetBinomDistPMF(double x, double n, double p); //probability mass function
+ double GetHypGeomDist( double x, double n, double M, double N );
+ void ScLogGamma();
+ void ScGamma();
+ void ScPhi();
+ void ScGauss();
+ void ScStdNormDist();
+ void ScStdNormDist_MS();
+ void ScFisher();
+ void ScFisherInv();
+ void ScFact();
+ void ScNormDist( int nMinParamCount );
+ void ScGammaDist( bool bODFF );
+ void ScGammaInv();
+ void ScExpDist();
+ void ScBinomDist();
+ void ScPoissonDist( bool bODFF );
+ void ScCombin();
+ void ScCombinA();
+ void ScPermut();
+ void ScPermutationA();
+ void ScB();
+ void ScHypGeomDist( int nMinParamCount );
+ void ScLogNormDist( int nMinParamCount );
+ void ScLogNormInv();
+ void ScTDist();
+ void ScTDist_MS();
+ void ScTDist_T( int nTails );
+ void ScFDist();
+ void ScFDist_LT();
+ void ScChiDist( bool bODFF); // for LEGACY.CHIDIST, returns right tail
+ void ScChiSqDist(); // returns left tail or density
+ void ScChiSqDist_MS();
+ void ScChiSqInv(); // inverse to CHISQDIST
+ void ScWeibull();
+ void ScBetaDist();
+ void ScBetaDist_MS();
+ void ScFInv();
+ void ScFInv_LT();
+ void ScTInv( int nType );
+ void ScChiInv();
+ void ScBetaInv();
+ void ScCritBinom();
+ void ScNegBinomDist();
+ void ScNegBinomDist_MS();
+ void ScKurt();
+ void ScHarMean();
+ void ScGeoMean();
+ void ScStandard();
+ void ScSkew();
+ void ScSkewp();
+ void ScMedian();
+ double GetMedian( ::std::vector<double> & rArray );
+ double GetPercentileExclusive( ::std::vector<double> & rArray, double fPercentile );
+ std::vector<double> GetRankNumberArray( SCSIZE& rCol, SCSIZE& rRow );
+ void GetNumberSequenceArray( sal_uInt8 nParamCount, ::std::vector<double>& rArray, bool bConvertTextInArray );
+ void GetSortArray( sal_uInt8 nParamCount, ::std::vector<double>& rSortArray, ::std::vector<tools::Long>* pIndexOrder, bool bConvertTextInArray, bool bAllowEmptyArray );
+ static void QuickSort(::std::vector<double>& rSortArray, ::std::vector<tools::Long>* pIndexOrder);
+ void ScModalValue();
+ void ScModalValue_MS( bool bSingle );
+ void ScAveDev();
+ void ScAggregate();
+ void ScDevSq();
+ void ScZTest();
+ void ScTTest();
+ void ScFTest();
+ void ScChiTest();
+ void ScRank( bool bAverage );
+ void ScPercentile( bool bInclusive );
+ void ScPercentrank( bool bInclusive );
+ static double GetPercentrank( ::std::vector<double> & rArray, double fVal, bool bInclusive );
+ void ScLarge();
+ void ScSmall();
+ void ScFrequency();
+ void ScQuartile( bool bInclusive );
+ void ScNormInv();
+ void ScSNormInv();
+ void ScConfidence();
+ void ScConfidenceT();
+ void ScTrimMean();
+ void ScProbability();
+ void ScCorrel();
+ void ScCovarianceP();
+ void ScCovarianceS();
+ void ScPearson();
+ void ScRSQ();
+ void ScSTEYX();
+ void ScSlope();
+ void ScTrend();
+ void ScInfo();
+ void ScLenB();
+ void ScRightB();
+ void ScLeftB();
+ void ScMidB();
+ void ScReplaceB();
+ void ScFindB();
+ void ScSearchB();
+
+ void ScFilterXML();
+ void ScWebservice();
+ void ScEncodeURL();
+ void ScColor();
+ void ScErf();
+ void ScErfc();
+
+ static const double fMaxGammaArgument;
+
+ double GetGammaContFraction(double fA,double fX);
+ double GetGammaSeries(double fA,double fX);
+ double GetLowRegIGamma(double fA,double fX); // lower regularized incomplete gamma function, GAMMAQ
+ double GetUpRegIGamma(double fA,double fX); // upper regularized incomplete gamma function, GAMMAP
+ // probability density function; fLambda is "scale" parameter
+ double GetGammaDistPDF(double fX, double fAlpha, double fLambda);
+ // cumulative distribution function; fLambda is "scale" parameter
+ double GetGammaDist(double fX, double fAlpha, double fLambda);
+ double GetTInv( double fAlpha, double fSize, int nType );
+
+public:
+ ScInterpreter( ScFormulaCell* pCell, ScDocument& rDoc, ScInterpreterContext& rContext,
+ const ScAddress&, ScTokenArray&, bool bForGroupThreading = false );
+ ~ScInterpreter();
+
+ // Used only for threaded formula-groups.
+ // Resets the interpreter object, allowing reuse of interpreter object for each cell
+ // in the group.
+ void Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenArray& rTokArray );
+
+ formula::StackVar Interpret();
+
+ void SetError(FormulaError nError)
+ { if (nError != FormulaError::NONE && nGlobalError == FormulaError::NONE) nGlobalError = nError; }
+ void AssertFormulaMatrix();
+
+ void SetLinkManager(sfx2::LinkManager* pLinkMgr)
+ { mpLinkManager = pLinkMgr; }
+
+ FormulaError GetError() const { return nGlobalError; }
+ formula::StackVar GetResultType() const { return xResult->GetType(); }
+ const svl::SharedString & GetStringResult() const;
+ double GetNumResult() const { return xResult->GetDouble(); }
+ const formula::FormulaConstTokenRef& GetResultToken() const { return xResult; }
+ SvNumFormatType GetRetFormatType() const { return nRetFmtType; }
+ sal_uLong GetRetFormatIndex() const { return nRetFmtIndex; }
+};
+
+inline bool ScInterpreter::IsInArrayContext() const
+{
+ return bMatrixFormula || pCur->IsInForceArray();
+}
+
+inline void ScInterpreter::MatrixJumpConditionToMatrix()
+{
+ if (IsInArrayContext())
+ ConvertMatrixJumpConditionToMatrix();
+}
+
+inline bool ScInterpreter::MatrixParameterConversion()
+{
+ if ( (IsInArrayContext() || ScParameterClassification::HasForceArray( pCur->GetOpCode())) &&
+ !pJumpMatrix && sp > 0 )
+ return ConvertMatrixParameters();
+ return false;
+}
+
+inline ScTokenMatrixMap& ScInterpreter::GetTokenMatrixMap()
+{
+ return maTokenMatrixMap;
+}
+
+inline bool ScInterpreter::MustHaveParamCount( short nAct, short nMust )
+{
+ if ( nAct == nMust )
+ return true;
+ if ( nAct < nMust )
+ PushParameterExpected();
+ else
+ PushIllegalParameter();
+ return false;
+}
+
+inline bool ScInterpreter::MustHaveParamCount( short nAct, short nMust, short nMax )
+{
+ if ( nMust <= nAct && nAct <= nMax )
+ return true;
+ if ( nAct < nMust )
+ PushParameterExpected();
+ else
+ PushIllegalParameter();
+ return false;
+}
+
+inline bool ScInterpreter::MustHaveParamCountMin( short nAct, short nMin )
+{
+ if ( nAct >= nMin )
+ return true;
+ PushParameterExpected();
+ return false;
+}
+
+inline bool ScInterpreter::MustHaveParamCountMinWithStackCheck( short nAct, short nMin )
+{
+ assert(sp >= nAct);
+ if (sp < nAct)
+ {
+ PushError(FormulaError::UnknownStackVariable);
+ return false;
+ }
+ return MustHaveParamCountMin( nAct, nMin);
+}
+
+inline bool ScInterpreter::CheckStringPositionArgument( double & fVal )
+{
+ if (!std::isfinite( fVal))
+ {
+ fVal = -1.0;
+ return false;
+ }
+ else if (fVal < 0.0)
+ {
+ fVal = 0.0;
+ return false;
+ }
+ else if (fVal > SAL_MAX_INT32)
+ {
+ fVal = static_cast<double>(SAL_MAX_INT32);
+ return false;
+ }
+ return true;
+}
+
+inline sal_Int32 ScInterpreter::GetStringPositionArgument()
+{
+ double fVal = rtl::math::approxFloor( GetDouble());
+ if (!CheckStringPositionArgument( fVal))
+ {
+ fVal = -1.0;
+ SetError( FormulaError::IllegalArgument);
+ }
+ return static_cast<sal_Int32>(fVal);
+}
+
+inline bool ScInterpreter::CheckStringResultLen( OUString& rResult, sal_Int32 nIncrease )
+{
+ if (nIncrease > kScInterpreterMaxStrLen - rResult.getLength())
+ {
+ SetError( FormulaError::StringOverflow );
+ rResult.clear();
+ return false;
+ }
+ return true;
+}
+
+inline bool ScInterpreter::CheckStringResultLen( OUStringBuffer& rResult, sal_Int32 nIncrease )
+{
+ if (nIncrease > kScInterpreterMaxStrLen - rResult.getLength())
+ {
+ SetError( FormulaError::StringOverflow );
+ rResult.setLength(0);
+ return false;
+ }
+ return true;
+}
+
+inline void ScInterpreter::TreatDoubleError( double& rVal )
+{
+ if ( !std::isfinite( rVal ) )
+ {
+ FormulaError nErr = GetDoubleErrorValue( rVal );
+ if ( nErr != FormulaError::NONE )
+ SetError( nErr );
+ else
+ SetError( FormulaError::NoValue );
+ rVal = 0.0;
+ }
+}
+
+inline double ScInterpreter::div( const double& fNumerator, const double& fDenominator )
+{
+ return sc::div(fNumerator, fDenominator);
+}
+
+inline sal_Int16 ScInterpreter::GetDayOfWeek( sal_Int32 n )
+{ // monday = 0, ..., sunday = 6
+ return static_cast< sal_Int16 >( ( n - 1 ) % 7 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/jumpmatrix.hxx b/sc/source/core/inc/jumpmatrix.hxx
new file mode 100644
index 0000000000..f0fb9c1abb
--- /dev/null
+++ b/sc/source/core/inc/jumpmatrix.hxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <limits.h>
+#include <vector>
+#include <types.hxx>
+#include <address.hxx>
+#include <token.hxx>
+
+namespace formula { class FormulaToken; }
+
+typedef ::std::vector< const formula::FormulaToken*> ScTokenVec;
+
+struct ScJumpMatrixEntry
+{
+ double fBool; // 0:= false 1:= true also if no-path
+ // other values may contain error conditions like NAN and INF
+ short nStart; // start of path (actually start-1, see formula::FormulaTokenIterator)
+ short nNext; // next after path
+ // jump path exists if nStart != nNext, else no path
+ short nStop; // optional stop of path (nPC < nStop)
+
+ void SetJump( double fBoolP, short nStartP, short nNextP, short nStopP )
+ {
+ fBool = fBoolP;
+ nStart = nStartP;
+ nNext = nNextP;
+ nStop = nStopP;
+ }
+ void GetJump( double& rBool, short& rStart, short& rNext, short& rStop ) const
+ {
+ rBool = fBool;
+ rStart = nStart;
+ rNext = nNext;
+ rStop = nStop;
+ }
+};
+
+class ScJumpMatrix
+{
+ std::vector<ScJumpMatrixEntry> mvJump; // the jumps
+ ScMatrixRef pMat; // the results
+ ScRefList mvRefList; // array of references result, if any
+ ScTokenVec mvParams; // parameter stack
+ SCSIZE nCols;
+ SCSIZE nRows;
+ SCSIZE nCurCol;
+ SCSIZE nCurRow;
+ SCSIZE nResMatCols;
+ SCSIZE nResMatRows;
+ OpCode meOp;
+ bool bStarted;
+
+ // Buffer result ranges to be able to set a range of identically typed
+ // values at the result matrix in order to avoid multiple shrinks and
+ // growths of multi_type_vector segments, which is a major performance
+ // bottleneck, see fdo#72929
+ ::std::vector< svl::SharedString > mvBufferStrings;
+ ::std::vector< double > mvBufferDoubles;
+ SCSIZE mnBufferCol;
+ SCSIZE mnBufferRowStart;
+ SCSIZE mnBufferEmptyCount;
+ SCSIZE mnBufferEmptyPathCount;
+
+ enum BufferType
+ {
+ BUFFER_NONE,
+ BUFFER_DOUBLE,
+ BUFFER_STRING,
+ BUFFER_EMPTY,
+ BUFFER_EMPTYPATH
+ };
+
+ /** Flush different types or non-consecutive buffers. */
+ void FlushBufferOtherThan( BufferType eType, SCSIZE nC, SCSIZE nR );
+
+ ScJumpMatrix( const ScJumpMatrix& ) = delete;
+ ScJumpMatrix& operator=( const ScJumpMatrix& ) = delete;
+
+public:
+ ScJumpMatrix( OpCode eOp, SCSIZE nColsP, SCSIZE nRowsP );
+ ~ScJumpMatrix();
+ void GetDimensions( SCSIZE& rCols, SCSIZE& rRows ) const;
+ void SetJump( SCSIZE nCol, SCSIZE nRow, double fBool, short nStart, short nNext );
+ void GetJump( SCSIZE nCol, SCSIZE nRow, double& rBool, short& rStart, short& rNext, short& rStop ) const;
+ void SetAllJumps( double fBool, short nStart, short nNext, short nStop = SHRT_MAX );
+ void SetJumpParameters( ScTokenVec&& p );
+ const ScTokenVec & GetJumpParameters() const { return mvParams;}
+ bool HasResultMatrix() const;
+ ScMatrix* GetResultMatrix(); ///< also applies pending buffered values
+ void GetPos( SCSIZE& rCol, SCSIZE& rRow ) const;
+ bool Next( SCSIZE& rCol, SCSIZE& rRow );
+ void GetResMatDimensions( SCSIZE& rCols, SCSIZE& rRows );
+ void SetNewResMat( SCSIZE nNewCols, SCSIZE nNewRows );
+ ScRefList& GetRefList();
+ OpCode GetOpCode() const { return meOp; }
+
+ void PutResultDouble( double fVal, SCSIZE nC, SCSIZE nR );
+ void PutResultString( const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR );
+ void PutResultEmpty( SCSIZE nC, SCSIZE nR );
+ void PutResultEmptyPath( SCSIZE nC, SCSIZE nR );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/parclass.hxx b/sc/source/core/inc/parclass.hxx
new file mode 100644
index 0000000000..4b043fd7fe
--- /dev/null
+++ b/sc/source/core/inc/parclass.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 .
+ */
+
+#pragma once
+
+#include <formula/opcode.hxx>
+#include <formula/paramclass.hxx>
+
+/** Activate parameter classification documentation.
+
+ Building with DEBUG_SC_PARCLASSDOC 1 enables generation of parameter
+ classification documentation when instantiating the first Calc document if
+ the environment variable OOO_CALC_GENPARCLASSDOC is set and SAL_LOG
+ contains +INFO.sc.core
+
+ Generated output contains CALC_GENPARCLASSDOC that can be easily grep'ed for.
+
+ Activation adds overhead to Calc initialization time to gather information
+ that otherwise is not needed for anything else.
+ */
+#define DEBUG_SC_PARCLASSDOC 0
+
+namespace formula
+{
+ class FormulaToken;
+}
+
+class ScParameterClassification
+{
+public:
+
+ /// MUST be called once before any other method.
+ static void Init();
+
+ static void Exit();
+
+ /** Get one parameter type for function eOp.
+ @param nParameter
+ Which parameter, 0-based.
+ SAL_MAX_UINT16 for return type of eOp.
+ */
+ static formula::ParamClass GetParameterType( const formula::FormulaToken* pToken,
+ sal_uInt16 nParameter);
+
+ /** Whether OpCode has a parameter of type
+ ForceArray or ReferenceOrForceArray. */
+ static bool HasForceArray( OpCode eOp)
+ {
+ return 0 <= static_cast<short>(eOp) &&
+ eOp <= SC_OPCODE_LAST_OPCODE_ID &&
+ pData[eOp].bHasForceArray;
+ }
+
+private:
+
+ struct CommonData
+ {
+ const static sal_Int32 nMaxParams = 7;
+
+ formula::ParamClass nParam[nMaxParams];
+ sal_uInt8 nRepeatLast;
+ formula::ParamClass eReturn;
+ };
+
+ struct RawData
+ {
+ OpCode eOp;
+ CommonData aData;
+ };
+
+ struct RunData;
+ friend struct ScParameterClassification::RunData;
+ struct RunData
+ {
+ CommonData aData;
+ sal_uInt8 nMinParams; // fix or minimum, or repeat start
+ bool bHasForceArray;
+ };
+
+ static const RawData pRawData[];
+ static RunData* pData;
+
+ // ocExternal AddIns
+ static formula::ParamClass GetExternalParameterType(
+ const formula::FormulaToken* pToken, sal_uInt16 nParameter);
+
+#if DEBUG_SC_PARCLASSDOC
+ // Generate documentation to stdout if environment variable
+ // OOO_CALC_GENPARCLASSDOC is set.
+ static void GenerateDocumentation();
+
+ /* OpCodes not specified in the implementation are taken from the global
+ * function list and all parameters, if any, are assumed to be of type
+ * Value. This could also be done in the product version if needed, but we
+ * don't want to spoil startup time. However, doing so could propagate the
+ * minimum parameter count to the formula compiler, which, together with
+ * additional information about optional parameters, could react on missing
+ * parameters then. */
+ static void MergeArgumentsFromFunctionResource();
+
+ /** Minimum number of parameters, or fix number
+ of parameters if HasRepeatParameters()
+ returns sal_False. For opcodes not specified in
+ the implementation a parameter count of 1
+ is assumed, for opcodes out of range 0 is
+ assumed. If HasRepeatParameters() returns
+ sal_True, information is NOT related to whether
+ any parameters are optional, only the type
+ of parameters is significant. */
+ static inline sal_uInt8 GetMinimumParameters( OpCode eOp)
+ {
+ if ( eOp <= SC_OPCODE_LAST_OPCODE_ID )
+ return pData[eOp].aData.nParam[0]
+ == formula::ParamClass::Unknown ? 1 :
+ pData[eOp].nMinParams;
+ return 0;
+ }
+
+ /** Whether last parameter types are repeated. */
+ static inline bool HasRepeatParameters( OpCode eOp)
+ {
+ return eOp <= SC_OPCODE_LAST_OPCODE_ID
+ && pData[eOp].aData.nRepeatLast > 0;
+ }
+#endif // DEBUG_SC_PARCLASSDOC
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/poolhelp.hxx b/sc/source/core/inc/poolhelp.hxx
new file mode 100644
index 0000000000..860e1b3a8b
--- /dev/null
+++ b/sc/source/core/inc/poolhelp.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+#include <docoptio.hxx>
+#include <svl/itempool.hxx>
+#include <mutex>
+
+class ScDocument;
+class ScDocumentPool;
+class ScStyleSheetPool;
+class SvNumberFormatter;
+class SfxItemPool;
+
+class ScPoolHelper final : public salhelper::SimpleReferenceObject
+{
+private:
+ mutable std::mutex maMtxCreateNumFormatter;
+ ScDocOptions aOpt;
+ rtl::Reference<ScDocumentPool> pDocPool;
+ rtl::Reference< ScStyleSheetPool > mxStylePool;
+ mutable std::unique_ptr<SvNumberFormatter> pFormTable;
+ mutable rtl::Reference<SfxItemPool> pEditPool; // EditTextObjectPool
+ mutable rtl::Reference<SfxItemPool> pEnginePool; // EditEnginePool
+
+public:
+ ScPoolHelper( ScDocument& rSourceDoc );
+ virtual ~ScPoolHelper() override;
+
+ // called in dtor of main document
+ void SourceDocumentGone();
+
+ // access to pointers (are never 0):
+ ScDocumentPool* GetDocPool() const { return pDocPool.get(); }
+ ScStyleSheetPool* GetStylePool() const { return mxStylePool.get(); }
+ SvNumberFormatter* GetFormTable() const;
+ SfxItemPool* GetEditPool() const;
+ SfxItemPool* GetEnginePool() const;
+
+ void SetFormTableOpt(const ScDocOptions& rOpt);
+
+ std::unique_ptr<SvNumberFormatter> CreateNumberFormatter() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/refupdat.hxx b/sc/source/core/inc/refupdat.hxx
new file mode 100644
index 0000000000..cd3ab69dfa
--- /dev/null
+++ b/sc/source/core/inc/refupdat.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 .
+ */
+
+#pragma once
+
+#include <global.hxx>
+
+class ScDocument;
+class ScBigRange;
+struct ScComplexRefData;
+class ScAddress;
+class ScRange;
+
+enum ScRefUpdateRes {
+ UR_NOTHING = 0, ///< Reference not affected, no change at all.
+ UR_UPDATED = 1, ///< Reference was adjusted/updated.
+ UR_INVALID = 2, ///< Some part of the reference became invalid.
+ UR_STICKY = 3 /**< Not updated because the reference is sticky,
+ but would had been updated if it wasn't. For
+ entire columns/rows. Essentially the same as
+ not UR_NOTHING for the caller but allows
+ differentiation. */
+};
+
+class ScRefUpdate
+{
+public:
+
+ static ScRefUpdateRes Update
+ ( const ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ SCCOL nDx, SCROW nDy, SCTAB nDz,
+ SCCOL& theCol1, SCROW& theRow1, SCTAB& theTab1,
+ SCCOL& theCol2, SCROW& theRow2, SCTAB& theTab2 );
+
+ static ScRefUpdateRes Update( UpdateRefMode eUpdateRefMode,
+ const ScBigRange& rWhere,
+ sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz,
+ ScBigRange& rWhat );
+
+ static void MoveRelWrap( const ScDocument& rDoc, const ScAddress& rPos,
+ SCCOL nMaxCol, SCROW nMaxRow, ScComplexRefData& rRef );
+
+ static ScRefUpdateRes UpdateTranspose(
+ const ScDocument& rDoc, const ScRange& rSource, const ScAddress& rDest, ScRange& rRef );
+
+ static void DoTranspose( SCCOL& rCol, SCROW& rRow, SCTAB& rTab, const ScDocument& rDoc,
+ const ScRange& rSource, const ScAddress& rDest );
+
+ static ScRefUpdateRes UpdateGrow(
+ const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY, ScRange& rRef );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/scrdata.hxx b/sc/source/core/inc/scrdata.hxx
new file mode 100644
index 0000000000..0698e12573
--- /dev/null
+++ b/sc/source/core/inc/scrdata.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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star::i18n
+{
+class XBreakIterator;
+}
+
+class ScScriptTypeData
+{
+public:
+ css::uno::Reference<css::i18n::XBreakIterator> xBreakIter;
+
+ ScScriptTypeData() {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/sharedstringpoolpurge.hxx b/sc/source/core/inc/sharedstringpoolpurge.hxx
new file mode 100644
index 0000000000..87b11f3a05
--- /dev/null
+++ b/sc/source/core/inc/sharedstringpoolpurge.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/.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <svl/sharedstringpool.hxx>
+#include <vcl/timer.hxx>
+
+namespace svl
+{
+class SharedStringPool;
+}
+
+namespace sc
+{
+/*
+Calls svl::SharedStringPool::purge() after a delay when idle. Can be
+used to compress repeated calls, as purge() may be somewhat expensive
+with large documents. And since vcl links to svl, it's not possible
+to use VCL timers in svl, so a separate class is needed.
+*/
+class SharedStringPoolPurge
+{
+public:
+ SharedStringPoolPurge();
+ ~SharedStringPoolPurge();
+ void delayedPurge(const std::shared_ptr<svl::SharedStringPool>& pool);
+
+private:
+ void cleanup();
+ std::vector<std::shared_ptr<svl::SharedStringPool>> mPoolsToPurge;
+ Timer mTimer;
+ DECL_LINK(timerHandler, Timer*, void);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/webservicelink.hxx b/sc/source/core/inc/webservicelink.hxx
new file mode 100644
index 0000000000..8504a8c1b6
--- /dev/null
+++ b/sc/source/core/inc/webservicelink.hxx
@@ -0,0 +1,49 @@
+/* -*- 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/lnkbase.hxx>
+#include <svl/broadcast.hxx>
+
+namespace com::sun::star::uno
+{
+class Any;
+}
+
+class ScDocument;
+
+class ScWebServiceLink final : public ::sfx2::SvBaseLink, public SvtBroadcaster
+{
+private:
+ ScDocument* pDoc;
+ OUString aURL; // connection/ link data
+ bool bHasResult; // is set aResult is useful
+ OUString aResult;
+
+public:
+ ScWebServiceLink(ScDocument* pD, OUString aURL);
+ virtual ~ScWebServiceLink() override;
+
+ // SvBaseLink override:
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(const OUString& rMimeType,
+ const css::uno::Any& rValue) override;
+
+ // SvtBroadcaster override:
+ virtual void ListenersGone() override;
+
+ // for interpreter:
+
+ const OUString& GetResult() const { return aResult; }
+ bool HasResult() const { return bHasResult; }
+
+ const OUString& GetURL() const { return aURL; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/source/core/opencl/cl-test.ods b/sc/source/core/opencl/cl-test.ods
new file mode 100644
index 0000000000..7e2bae4cb3
--- /dev/null
+++ b/sc/source/core/opencl/cl-test.ods
Binary files differ
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
new file mode 100644
index 0000000000..4d61b00ed3
--- /dev/null
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -0,0 +1,3047 @@
+/* -*- 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 <formulagroup.hxx>
+#include <formulagroupcl.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <compiler.hxx>
+#include <comphelper/random.hxx>
+#include <scmatrix.hxx>
+#include <sal/log.hxx>
+
+#include <opencl/openclwrapper.hxx>
+#include <opencl/OpenCLZone.hxx>
+
+#include "op_financial.hxx"
+#include "op_math.hxx"
+#include "op_logical.hxx"
+#include "op_statistical.hxx"
+#include "op_array.hxx"
+#include "op_spreadsheet.hxx"
+#include "op_addin.hxx"
+
+#include <limits>
+
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+
+const char* const publicFunc =
+ "\n"
+ "#define IllegalArgument 502\n"
+ "#define IllegalFPOperation 503 // #NUM!\n"
+ "#define NoValue 519 // #VALUE!\n"
+ "#define NoConvergence 523\n"
+ "#define DivisionByZero 532 // #DIV/0!\n"
+ "#define NOTAVAILABLE 0x7fff // #N/A\n"
+ "\n"
+ "double CreateDoubleError(ulong nErr)\n"
+ "{\n"
+ // At least nVidia on Linux and Intel on Windows seem to ignore the argument to nan(),
+ // so using that would not propagate the type of error, work that around
+ // by directly constructing the proper IEEE double NaN value
+ // TODO: maybe use a better way to detect such systems?
+ " return as_double(0x7FF8000000000000+nErr);\n"
+// " return nan(nErr);\n"
+ "}\n"
+ "\n"
+ "double fsum(double a, double b) { return isnan(a)?b:a+b; }\n"
+ "double legalize(double a, double b) { return isnan(a)?b:a;}\n"
+ ;
+
+#include <utility>
+#include <vector>
+#include <map>
+#include <iostream>
+#include <algorithm>
+
+#include <rtl/digest.h>
+
+#include <memory>
+
+using namespace formula;
+
+namespace sc::opencl {
+
+namespace {
+
+std::string linenumberify(const std::string& s)
+{
+ outputstream ss;
+ int linenumber = 1;
+ size_t start = 0;
+ size_t newline;
+ while ((newline = s.find('\n', start)) != std::string::npos)
+ {
+ ss << "/*" << std::setw(4) << linenumber++ << "*/ " << s.substr(start, newline-start+1);
+ start = newline + 1;
+ }
+ if (start < s.size())
+ ss << "/*" << std::setw(4) << linenumber++ << "*/ " << s.substr(start, std::string::npos);
+ return ss.str();
+}
+
+bool AllStringsAreNull(const rtl_uString* const* pStringArray, size_t nLength)
+{
+ if (pStringArray == nullptr)
+ return true;
+
+ for (size_t i = 0; i < nLength; i++)
+ if (pStringArray[i] != nullptr)
+ return false;
+
+ return true;
+}
+
+OUString LimitedString( std::u16string_view str )
+{
+ if( str.size() < 20 )
+ return OUString::Concat("\"") + str + "\"";
+ else
+ return OUString::Concat("\"") + str.substr( 0, 20 ) + "\"...";
+}
+
+const int MAX_PEEK_ELEMENTS = 5;
+// Returns formatted contents of the data (possibly shortened), to be used in debug output.
+std::string DebugPeekData(const FormulaToken* ref, int doubleRefIndex = 0)
+{
+ if (ref->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>(ref);
+ outputstream buf;
+ buf << "SingleRef {";
+ for( size_t i = 0; i < std::min< size_t >( MAX_PEEK_ELEMENTS, pSVR->GetArrayLength()); ++i )
+ {
+ if( i != 0 )
+ buf << ",";
+ if( pSVR->GetArray().mpStringArray != nullptr
+ && pSVR->GetArray().mpStringArray[ i ] != nullptr )
+ {
+ buf << LimitedString( OUString( pSVR->GetArray().mpStringArray[ i ] ));
+ }
+ else if( pSVR->GetArray().mpNumericArray != nullptr )
+ buf << pSVR->GetArray().mpNumericArray[ i ];
+ }
+ if( pSVR->GetArrayLength() > MAX_PEEK_ELEMENTS )
+ buf << ",...";
+ buf << "}";
+ return buf.str();
+ }
+ else if (ref->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken*>(ref);
+ outputstream buf;
+ buf << "DoubleRef {";
+ for( size_t i = 0; i < std::min< size_t >( MAX_PEEK_ELEMENTS, pDVR->GetArrayLength()); ++i )
+ {
+ if( i != 0 )
+ buf << ",";
+ if( pDVR->GetArrays()[doubleRefIndex].mpStringArray != nullptr
+ && pDVR->GetArrays()[doubleRefIndex].mpStringArray[ i ] != nullptr )
+ {
+ buf << LimitedString( OUString( pDVR->GetArrays()[doubleRefIndex].mpStringArray[ i ] ));
+ }
+ else if( pDVR->GetArrays()[doubleRefIndex].mpNumericArray != nullptr )
+ buf << pDVR->GetArrays()[doubleRefIndex].mpNumericArray[ i ];
+ }
+ if( pDVR->GetArrayLength() > MAX_PEEK_ELEMENTS )
+ buf << ",...";
+ buf << "}";
+ return buf.str();
+ }
+ else if (ref->GetType() == formula::svString)
+ {
+ outputstream buf;
+ buf << "String " << LimitedString( ref->GetString().getString());
+ return buf.str();
+ }
+ else if (ref->GetType() == formula::svDouble)
+ {
+ return preciseFloat(ref->GetDouble());
+ }
+ else
+ {
+ return "?";
+ }
+}
+
+// Returns formatted contents of a doubles buffer, to be used in debug output.
+std::string DebugPeekDoubles(const double* data, int size)
+{
+ outputstream buf;
+ buf << "{";
+ for( int i = 0; i < std::min( MAX_PEEK_ELEMENTS, size ); ++i )
+ {
+ if( i != 0 )
+ buf << ",";
+ buf << data[ i ];
+ }
+ if( size > MAX_PEEK_ELEMENTS )
+ buf << ",...";
+ buf << "}";
+ return buf.str();
+}
+
+} // anonymous namespace
+
+/// Map the buffer used by an argument and do necessary argument setting
+size_t VectorRef::Marshal( cl_kernel k, int argno, int, cl_program )
+{
+ OpenCLZone zone;
+ FormulaToken* ref = mFormulaTree->GetFormulaToken();
+ double* pHostBuffer = nullptr;
+ size_t szHostBuffer = 0;
+ if (ref->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>(ref);
+
+ SAL_INFO("sc.opencl", "SingleVectorRef len=" << pSVR->GetArrayLength() << " mpNumericArray=" << pSVR->GetArray().mpNumericArray << " (mpStringArray=" << pSVR->GetArray().mpStringArray << ")");
+
+ if( forceStringsToZero && pSVR->GetArray().mpStringArray != nullptr )
+ {
+ dataBuffer.resize( pSVR->GetArrayLength());
+ for( size_t i = 0; i < pSVR->GetArrayLength(); ++i )
+ if( pSVR->GetArray().mpStringArray[ i ] != nullptr )
+ dataBuffer[ i ] = 0;
+ else
+ dataBuffer[ i ] = pSVR->GetArray().mpNumericArray[ i ];
+ pHostBuffer = dataBuffer.data();
+ SAL_INFO("sc.opencl", "Forced strings to zero : " << DebugPeekDoubles( pHostBuffer, pSVR->GetArrayLength()));
+ }
+ else
+ {
+ pHostBuffer = const_cast<double*>(pSVR->GetArray().mpNumericArray);
+ }
+ szHostBuffer = pSVR->GetArrayLength() * sizeof(double);
+ }
+ else if (ref->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken*>(ref);
+
+ SAL_INFO("sc.opencl", "DoubleVectorRef index=" << mnIndex << " len=" << pDVR->GetArrayLength() << " mpNumericArray=" << pDVR->GetArrays()[mnIndex].mpNumericArray << " (mpStringArray=" << pDVR->GetArrays()[mnIndex].mpStringArray << ")");
+
+ if( forceStringsToZero && pDVR->GetArrays()[mnIndex].mpStringArray != nullptr )
+ {
+ dataBuffer.resize( pDVR->GetArrayLength());
+ for( size_t i = 0; i < pDVR->GetArrayLength(); ++i )
+ if( pDVR->GetArrays()[mnIndex].mpStringArray[ i ] != nullptr )
+ dataBuffer[ i ] = 0;
+ else
+ dataBuffer[ i ] = pDVR->GetArrays()[mnIndex].mpNumericArray[ i ];
+ pHostBuffer = dataBuffer.data();
+ SAL_INFO("sc.opencl", "Forced strings to zero : " << DebugPeekDoubles( pHostBuffer, pDVR->GetArrayLength()));
+ }
+ else
+ {
+ pHostBuffer = const_cast<double*>(pDVR->GetArrays()[mnIndex].mpNumericArray);
+ }
+ szHostBuffer = pDVR->GetArrayLength() * sizeof(double);
+ }
+ else
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+ cl_int err;
+ if (pHostBuffer)
+ {
+ mpClmem = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_ONLY) | CL_MEM_USE_HOST_PTR,
+ szHostBuffer,
+ pHostBuffer, &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem << " size " << szHostBuffer << " using host buffer " << pHostBuffer);
+ }
+ else
+ {
+ if (szHostBuffer == 0)
+ szHostBuffer = sizeof(double); // a dummy small value
+ // Marshal as a buffer of NANs
+ mpClmem = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_ONLY) | CL_MEM_ALLOC_HOST_PTR,
+ szHostBuffer, nullptr, &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem << " size " << szHostBuffer);
+
+ double* pNanBuffer = static_cast<double*>(clEnqueueMapBuffer(
+ kEnv.mpkCmdQueue, mpClmem, CL_TRUE, CL_MAP_WRITE, 0,
+ szHostBuffer, 0, nullptr, nullptr, &err));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueMapBuffer", err, __FILE__, __LINE__);
+
+ for (size_t i = 0; i < szHostBuffer / sizeof(double); i++)
+ pNanBuffer[i] = std::numeric_limits<double>::quiet_NaN();
+ err = clEnqueueUnmapMemObject(kEnv.mpkCmdQueue, mpClmem,
+ pNanBuffer, 0, nullptr, nullptr);
+ // FIXME: Is it intentional to not throw an OpenCLError even if the clEnqueueUnmapMemObject() fails?
+ if (CL_SUCCESS != err)
+ SAL_WARN("sc.opencl", "clEnqueueUnmapMemObject failed: " << openclwrapper::errorString(err));
+ }
+
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": cl_mem: " << mpClmem << " (" << DebugPeekData(ref, mnIndex) << ")");
+ err = clSetKernelArg(k, argno, sizeof(cl_mem), static_cast<void*>(&mpClmem));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+}
+
+namespace {
+
+class DynamicKernelPiArgument : public DynamicKernelArgument
+{
+public:
+ DynamicKernelPiArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft ) :
+ DynamicKernelArgument(config, s, ft) { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ ss << "double " << mSymName;
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ ss << "M_PI";
+ }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ GenDecl(ss);
+ }
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const override
+ {
+ return mSymName;
+ }
+ virtual size_t GetWindowSize() const override
+ {
+ return 1;
+ }
+ /// Create buffer and pass the buffer to a given kernel
+ virtual size_t Marshal( cl_kernel k, int argno, int, cl_program ) override
+ {
+ OpenCLZone zone;
+ double tmp = 0.0;
+ // Pass the scalar result back to the rest of the formula kernel
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": double: " << preciseFloat( tmp ) << " (PI)");
+ cl_int err = clSetKernelArg(k, argno, sizeof(double), static_cast<void*>(&tmp));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+ }
+};
+
+class DynamicKernelRandomArgument : public DynamicKernelArgument
+{
+public:
+ DynamicKernelRandomArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft ) :
+ DynamicKernelArgument(config, s, ft) { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ ss << "double " << mSymName;
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ ss << mSymName;
+ }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ ss << "int " << mSymName;
+ }
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const override
+ {
+ return mSymName + "_Random(" + mSymName + ")";
+ }
+ virtual void GenSlidingWindowFunction( outputstream& ss ) override
+ {
+ // This string is from the pi_opencl_kernel.i file as
+ // generated when building the Random123 examples. Unused
+ // stuff has been removed, and the actual kernel is not the
+ // same as in the totally different use case of that example,
+ // of course. Only the code that calculates the counter-based
+ // random number and what it needs is left.
+ ss << "\
+\n\
+#ifndef DEFINED_RANDOM123_STUFF\n\
+#define DEFINED_RANDOM123_STUFF\n\
+\n\
+/*\n\
+Copyright 2010-2011, D. E. Shaw Research.\n\
+All rights reserved.\n\
+\n\
+Redistribution and use in source and binary forms, with or without\n\
+modification, are permitted provided that the following conditions are\n\
+met:\n\
+\n\
+* Redistributions of source code must retain the above copyright\n\
+ notice, this list of conditions, and the following disclaimer.\n\
+\n\
+* Redistributions in binary form must reproduce the above copyright\n\
+ notice, this list of conditions, and the following disclaimer in the\n\
+ documentation and/or other materials provided with the distribution.\n\
+\n\
+* Neither the name of D. E. Shaw Research nor the names of its\n\
+ contributors may be used to endorse or promote products derived from\n\
+ this software without specific prior written permission.\n\
+\n\
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
+\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n\
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n\
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n\
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n\
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n\
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n\
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n\
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
+*/\n\
+\n\
+typedef uint uint32_t;\n\
+struct r123array2x32\n\
+{\n\
+ uint32_t v[2];\n\
+};\n\
+enum r123_enum_threefry32x2\n\
+{\n\
+ R_32x2_0_0 = 13,\n\
+ R_32x2_1_0 = 15,\n\
+ R_32x2_2_0 = 26,\n\
+ R_32x2_3_0 = 6,\n\
+ R_32x2_4_0 = 17,\n\
+ R_32x2_5_0 = 29,\n\
+ R_32x2_6_0 = 16,\n\
+ R_32x2_7_0 = 24\n\
+};\n\
+inline uint32_t RotL_32 (uint32_t x, unsigned int N)\n\
+ __attribute__ ((always_inline));\n\
+inline uint32_t\n\
+RotL_32 (uint32_t x, unsigned int N)\n\
+{\n\
+ return (x << (N & 31)) | (x >> ((32 - N) & 31));\n\
+}\n\
+\n\
+typedef struct r123array2x32 threefry2x32_ctr_t;\n\
+typedef struct r123array2x32 threefry2x32_key_t;\n\
+typedef struct r123array2x32 threefry2x32_ukey_t;\n\
+inline threefry2x32_key_t\n\
+threefry2x32keyinit (threefry2x32_ukey_t uk)\n\
+{\n\
+ return uk;\n\
+}\n\
+\n\
+inline threefry2x32_ctr_t threefry2x32_R (unsigned int Nrounds,\n\
+ threefry2x32_ctr_t in,\n\
+ threefry2x32_key_t k)\n\
+ __attribute__ ((always_inline));\n\
+inline threefry2x32_ctr_t\n\
+threefry2x32_R (unsigned int Nrounds, threefry2x32_ctr_t in,\n\
+ threefry2x32_key_t k)\n\
+{\n\
+ threefry2x32_ctr_t X;\n\
+ uint32_t ks[2 + 1];\n\
+ int i;\n\
+ ks[2] = 0x1BD11BDA;\n\
+ for (i = 0; i < 2; i++) {\n\
+ ks[i] = k.v[i];\n\
+ X.v[i] = in.v[i];\n\
+ ks[2] ^= k.v[i];\n\
+ }\n\
+ X.v[0] += ks[0];\n\
+ X.v[1] += ks[1];\n\
+ if (Nrounds > 0) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_0_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 1) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_1_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 2) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_2_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 3) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_3_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 3) {\n\
+ X.v[0] += ks[1];\n\
+ X.v[1] += ks[2];\n\
+ X.v[1] += 1;\n\
+ }\n\
+ if (Nrounds > 4) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_4_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 5) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_5_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 6) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_6_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 7) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_7_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 7) {\n\
+ X.v[0] += ks[2];\n\
+ X.v[1] += ks[0];\n\
+ X.v[1] += 2;\n\
+ }\n\
+ if (Nrounds > 8) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_0_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 9) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_1_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 10) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_2_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 11) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_3_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 11) {\n\
+ X.v[0] += ks[0];\n\
+ X.v[1] += ks[1];\n\
+ X.v[1] += 3;\n\
+ }\n\
+ if (Nrounds > 12) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_4_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 13) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_5_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 14) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_6_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 15) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_7_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 15) {\n\
+ X.v[0] += ks[1];\n\
+ X.v[1] += ks[2];\n\
+ X.v[1] += 4;\n\
+ }\n\
+ if (Nrounds > 16) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_0_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 17) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_1_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 18) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_2_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 19) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_3_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 19) {\n\
+ X.v[0] += ks[2];\n\
+ X.v[1] += ks[0];\n\
+ X.v[1] += 5;\n\
+ }\n\
+ if (Nrounds > 20) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_4_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 21) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_5_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 22) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_6_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 23) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_7_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 23) {\n\
+ X.v[0] += ks[0];\n\
+ X.v[1] += ks[1];\n\
+ X.v[1] += 6;\n\
+ }\n\
+ if (Nrounds > 24) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_0_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 25) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_1_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 26) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_2_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 27) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_3_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 27) {\n\
+ X.v[0] += ks[1];\n\
+ X.v[1] += ks[2];\n\
+ X.v[1] += 7;\n\
+ }\n\
+ if (Nrounds > 28) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_4_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 29) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_5_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 30) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_6_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 31) {\n\
+ X.v[0] += X.v[1];\n\
+ X.v[1] = RotL_32 (X.v[1], R_32x2_7_0);\n\
+ X.v[1] ^= X.v[0];\n\
+ }\n\
+ if (Nrounds > 31) {\n\
+ X.v[0] += ks[2];\n\
+ X.v[1] += ks[0];\n\
+ X.v[1] += 8;\n\
+ }\n\
+ return X;\n\
+}\n\
+\n\
+enum r123_enum_threefry2x32\n\
+{ threefry2x32_rounds = 20 };\n\
+inline threefry2x32_ctr_t threefry2x32 (threefry2x32_ctr_t in,\n\
+ threefry2x32_key_t k)\n\
+ __attribute__ ((always_inline));\n\
+inline threefry2x32_ctr_t\n\
+threefry2x32 (threefry2x32_ctr_t in, threefry2x32_key_t k)\n\
+{\n\
+ return threefry2x32_R (threefry2x32_rounds, in, k);\n\
+}\n\
+#endif\n\
+\n\
+";
+ ss << "double " << mSymName << "_Random (int seed)\n\
+{\n\
+ unsigned tid = get_global_id(0);\n\
+ threefry2x32_key_t k = { {tid, 0xdecafbad} };\n\
+ threefry2x32_ctr_t c = { {seed, 0xf00dcafe} };\n\
+ c = threefry2x32_R(threefry2x32_rounds, c, k);\n\
+ const double factor = 1./(" << SAL_MAX_UINT32 << ".0 + 1.0);\n\
+ const double halffactor = 0.5*factor;\n\
+ return c.v[0] * factor + halffactor;\n\
+}\n\
+";
+ }
+ virtual size_t GetWindowSize() const override
+ {
+ return 1;
+ }
+ /// Create buffer and pass the buffer to a given kernel
+ virtual size_t Marshal( cl_kernel k, int argno, int, cl_program ) override
+ {
+ OpenCLZone zone;
+ cl_int seed = comphelper::rng::uniform_int_distribution(0, SAL_MAX_INT32);
+ // Pass the scalar result back to the rest of the formula kernel
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": cl_int: " << seed << "(RANDOM)");
+ cl_int err = clSetKernelArg(k, argno, sizeof(cl_int), static_cast<void*>(&seed));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+ }
+};
+
+// Arguments that are actually compile-time constant string
+class ConstStringArgument : public DynamicKernelArgument
+{
+public:
+ ConstStringArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft ) :
+ DynamicKernelArgument(config, s, ft) { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ ss << "double " << mSymName;
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ ss << GenSlidingWindowDeclRef();
+ }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ GenDecl(ss);
+ }
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const override
+ {
+ outputstream ss;
+ if (GetFormulaToken()->GetType() != formula::svString)
+ throw Unhandled(__FILE__, __LINE__);
+ FormulaToken* Tok = GetFormulaToken();
+ ss << GetStringId(Tok->GetString().getData());
+ return ss.str();
+ }
+ virtual std::string GenIsString( bool = false ) const override
+ {
+ return "true";
+ }
+ virtual size_t GetWindowSize() const override
+ {
+ return 1;
+ }
+ virtual size_t Marshal( cl_kernel k, int argno, int, cl_program ) override
+ {
+ FormulaToken* ref = mFormulaTree->GetFormulaToken();
+ if (ref->GetType() != formula::svString)
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+ cl_double stringId = GetStringId(ref->GetString().getData());
+
+ // Pass the scalar result back to the rest of the formula kernel
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno
+ << ": stringId: " << stringId << " (" << DebugPeekData(ref) << ")" );
+ cl_int err = clSetKernelArg(k, argno, sizeof(cl_double), static_cast<void*>(&stringId));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+ }
+};
+
+} // namespace
+
+// Marshal a string vector reference
+size_t DynamicKernelStringArgument::Marshal( cl_kernel k, int argno, int, cl_program )
+{
+ OpenCLZone zone;
+ FormulaToken* ref = mFormulaTree->GetFormulaToken();
+
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+ cl_int err;
+ formula::VectorRefArray vRef;
+ size_t nStrings = 0;
+ if (ref->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>(ref);
+ nStrings = pSVR->GetArrayLength();
+ vRef = pSVR->GetArray();
+ }
+ else if (ref->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken*>(ref);
+ nStrings = pDVR->GetArrayLength();
+ vRef = pDVR->GetArrays()[mnIndex];
+ }
+ size_t szHostBuffer = nStrings * sizeof(cl_double);
+ cl_double* pStringIdsBuffer = nullptr;
+
+ if (vRef.mpStringArray != nullptr)
+ {
+ // Marshal strings. See GetStringId().
+ mpClmem = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_ONLY) | CL_MEM_ALLOC_HOST_PTR,
+ szHostBuffer, nullptr, &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem << " size " << szHostBuffer);
+
+ pStringIdsBuffer = static_cast<cl_double*>(clEnqueueMapBuffer(
+ kEnv.mpkCmdQueue, mpClmem, CL_TRUE, CL_MAP_WRITE, 0,
+ szHostBuffer, 0, nullptr, nullptr, &err));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueMapBuffer", err, __FILE__, __LINE__);
+
+ for (size_t i = 0; i < nStrings; i++)
+ {
+ if (vRef.mpStringArray[i])
+ pStringIdsBuffer[i] = GetStringId(vRef.mpStringArray[i]);
+ else
+ rtl::math::setNan(&pStringIdsBuffer[i]);
+ }
+ }
+ else
+ {
+ if (nStrings == 0)
+ szHostBuffer = sizeof(cl_double); // a dummy small value
+ // Marshal as a buffer of NANs
+ mpClmem = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_ONLY) | CL_MEM_ALLOC_HOST_PTR,
+ szHostBuffer, nullptr, &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem << " size " << szHostBuffer);
+
+ pStringIdsBuffer = static_cast<cl_double*>(clEnqueueMapBuffer(
+ kEnv.mpkCmdQueue, mpClmem, CL_TRUE, CL_MAP_WRITE, 0,
+ szHostBuffer, 0, nullptr, nullptr, &err));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueMapBuffer", err, __FILE__, __LINE__);
+
+ for (size_t i = 0; i < szHostBuffer / sizeof(cl_double); i++)
+ rtl::math::setNan(&pStringIdsBuffer[i]);
+ }
+ err = clEnqueueUnmapMemObject(kEnv.mpkCmdQueue, mpClmem,
+ pStringIdsBuffer, 0, nullptr, nullptr);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueUnmapMemObject", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": cl_mem: " << mpClmem
+ << " (stringIds: " << DebugPeekDoubles(pStringIdsBuffer, nStrings) << " "
+ << DebugPeekData(ref,mnIndex) << ")");
+ err = clSetKernelArg(k, argno, sizeof(cl_mem), static_cast<void*>(&mpClmem));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+}
+
+std::string DynamicKernelStringArgument::GenIsString( bool nested ) const
+{
+ if( nested )
+ return "!isnan(" + mSymName + "[gid0])";
+ FormulaToken* ref = mFormulaTree->GetFormulaToken();
+ size_t nStrings = 0;
+ if (ref->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>(ref);
+ nStrings = pSVR->GetArrayLength();
+ }
+ else if (ref->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken*>(ref);
+ nStrings = pDVR->GetArrayLength();
+ }
+ else
+ return "!isnan(" + mSymName + "[gid0])";
+ outputstream ss;
+ ss << "(gid0 < " << nStrings << "? !isnan(" << mSymName << "[gid0]):NAN)";
+ return ss.str();
+}
+
+namespace {
+
+/// A mixed string/numeric vector
+class DynamicKernelMixedArgument : public VectorRef
+{
+public:
+ DynamicKernelMixedArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft ) :
+ VectorRef(config, s, ft), mStringArgument(config, s + "s", ft) { }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ VectorRef::GenSlidingWindowDecl(ss);
+ ss << ", ";
+ mStringArgument.GenSlidingWindowDecl(ss);
+ }
+ virtual void GenSlidingWindowFunction( outputstream& ) override { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ VectorRef::GenDecl(ss);
+ ss << ", ";
+ mStringArgument.GenDecl(ss);
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ VectorRef::GenDeclRef(ss);
+ ss << ",";
+ mStringArgument.GenDeclRef(ss);
+ }
+ virtual std::string GenSlidingWindowDeclRef( bool nested ) const override
+ {
+ outputstream ss;
+ ss << "(!isnan(" << VectorRef::GenSlidingWindowDeclRef(nested);
+ ss << ")?" << VectorRef::GenSlidingWindowDeclRef(nested);
+ ss << ":" << mStringArgument.GenSlidingWindowDeclRef(nested);
+ ss << ")";
+ return ss.str();
+ }
+ virtual std::string GenDoubleSlidingWindowDeclRef( bool nested = false ) const override
+ {
+ outputstream ss;
+ ss << VectorRef::GenSlidingWindowDeclRef( nested );
+ return ss.str();
+ }
+ virtual std::string GenStringSlidingWindowDeclRef( bool nested = false ) const override
+ {
+ outputstream ss;
+ ss << mStringArgument.GenSlidingWindowDeclRef( nested );
+ return ss.str();
+ }
+ virtual std::string GenIsString( bool nested = false ) const override
+ {
+ return mStringArgument.GenIsString( nested );
+ }
+ virtual size_t Marshal( cl_kernel k, int argno, int vw, cl_program p ) override
+ {
+ int i = VectorRef::Marshal(k, argno, vw, p);
+ i += mStringArgument.Marshal(k, argno + i, vw, p);
+ return i;
+ }
+
+protected:
+ DynamicKernelStringArgument mStringArgument;
+};
+
+}
+
+template<class Base>
+DynamicKernelSlidingArgument<Base>::DynamicKernelSlidingArgument(
+ const ScCalcConfig& config, const std::string& s, const FormulaTreeNodeRef& ft,
+ std::shared_ptr<SlidingFunctionBase> CodeGen, int index)
+ : Base(config, s, ft, index)
+ , mpCodeGen(std::move(CodeGen))
+{
+ FormulaToken* t = ft->GetFormulaToken();
+ if (t->GetType() != formula::svDoubleVectorRef)
+ throw Unhandled(__FILE__, __LINE__);
+ mpDVR = static_cast<const formula::DoubleVectorRefToken*>(t);
+ bIsStartFixed = mpDVR->IsStartFixed();
+ bIsEndFixed = mpDVR->IsEndFixed();
+}
+
+template<class Base>
+bool DynamicKernelSlidingArgument<Base>::NeedParallelReduction() const
+{
+ assert(dynamic_cast<OpSumIfs*>(mpCodeGen.get()));
+ return GetWindowSize() > 100 &&
+ ((GetStartFixed() && GetEndFixed()) ||
+ (!GetStartFixed() && !GetEndFixed()));
+}
+
+template<class Base>
+std::string DynamicKernelSlidingArgument<Base>::GenSlidingWindowDeclRef( bool nested ) const
+{
+ size_t nArrayLength = mpDVR->GetArrayLength();
+ outputstream ss;
+ if (!bIsStartFixed && !bIsEndFixed)
+ {
+ if (!nested)
+ ss << "((i+gid0) <" << nArrayLength << "?";
+ ss << Base::GetName() << "[i + gid0]";
+ if (!nested)
+ ss << ":NAN)";
+ }
+ else
+ {
+ if (!nested)
+ ss << "(i <" << nArrayLength << "?";
+ ss << Base::GetName() << "[i]";
+ if (!nested)
+ ss << ":NAN)";
+ }
+ return ss.str();
+}
+
+template<class Base>
+size_t DynamicKernelSlidingArgument<Base>::GenReductionLoopHeader( outputstream& ss, bool& needBody )
+{
+ assert(mpDVR);
+ size_t nCurWindowSize = mpDVR->GetRefRowSize();
+
+ if (!mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ {
+ ss << "for (int i = ";
+ ss << "gid0; i < " << mpDVR->GetArrayLength();
+ ss << " && i < " << nCurWindowSize << "; i++){\n\t\t";
+ needBody = true;
+ return nCurWindowSize;
+ }
+ else if (mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ {
+ ss << "for (int i = ";
+ ss << "0; i < " << mpDVR->GetArrayLength();
+ ss << " && i < gid0+" << nCurWindowSize << "; i++){\n\t\t";
+ needBody = true;
+ return nCurWindowSize;
+ }
+ else if (!mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ {
+ ss << "tmpBottom = " << mpCodeGen->GetBottom() << ";\n\t";
+ ss << "{int i;\n\t";
+ outputstream temp1, temp2;
+ int outLoopSize = UNROLLING_FACTOR;
+ if (nCurWindowSize / outLoopSize != 0)
+ {
+ ss << "for(int outLoop=0; outLoop<" << nCurWindowSize / outLoopSize << "; outLoop++){\n\t";
+ for (int count = 0; count < outLoopSize; count++)
+ {
+ ss << "i = outLoop*" << outLoopSize << "+" << count << ";\n\t";
+ if (count == 0)
+ {
+ temp1 << "if(i + gid0 < " << mpDVR->GetArrayLength();
+ temp1 << "){\n\t\t";
+ temp1 << "tmp = legalize(";
+ temp1 << mpCodeGen->Gen2(GenSlidingWindowDeclRef(), "tmp");
+ temp1 << ", tmp);\n\t\t\t";
+ temp1 << "}\n\t";
+ }
+ ss << temp1.str();
+ }
+ ss << "}\n\t";
+ }
+ // The residual of mod outLoopSize
+ for (size_t count = nCurWindowSize / outLoopSize * outLoopSize; count < nCurWindowSize; count++)
+ {
+ ss << "i = " << count << ";\n\t";
+ if (count == nCurWindowSize / outLoopSize * outLoopSize)
+ {
+ temp2 << "if(i + gid0 < " << mpDVR->GetArrayLength();
+ temp2 << "){\n\t\t";
+ temp2 << "tmp = legalize(";
+ temp2 << mpCodeGen->Gen2(GenSlidingWindowDeclRef(), "tmp");
+ temp2 << ", tmp);\n\t\t\t";
+ temp2 << "}\n\t";
+ }
+ ss << temp2.str();
+ }
+ ss << "}\n";
+ needBody = false;
+ return nCurWindowSize;
+ }
+ // (mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ else
+ {
+ ss << "\n\t";
+ ss << "tmpBottom = " << mpCodeGen->GetBottom() << ";\n\t";
+ ss << "{int i;\n\t";
+ outputstream temp1, temp2;
+ int outLoopSize = UNROLLING_FACTOR;
+ if (nCurWindowSize / outLoopSize != 0)
+ {
+ ss << "for(int outLoop=0; outLoop<" << nCurWindowSize / outLoopSize << "; outLoop++){\n\t";
+ for (int count = 0; count < outLoopSize; count++)
+ {
+ ss << "i = outLoop*" << outLoopSize << "+" << count << ";\n\t";
+ if (count == 0)
+ {
+ temp1 << "if(i < " << mpDVR->GetArrayLength();
+ temp1 << "){\n\t\t";
+ temp1 << "tmp = legalize(";
+ temp1 << mpCodeGen->Gen2(GenSlidingWindowDeclRef(), "tmp");
+ temp1 << ", tmp);\n\t\t\t";
+ temp1 << "}\n\t";
+ }
+ ss << temp1.str();
+ }
+ ss << "}\n\t";
+ }
+ // The residual of mod outLoopSize
+ for (size_t count = nCurWindowSize / outLoopSize * outLoopSize; count < nCurWindowSize; count++)
+ {
+ ss << "i = " << count << ";\n\t";
+ if (count == nCurWindowSize / outLoopSize * outLoopSize)
+ {
+ temp2 << "if(i < " << mpDVR->GetArrayLength();
+ temp2 << "){\n\t\t";
+ temp2 << "tmp = legalize(";
+ temp2 << mpCodeGen->Gen2(GenSlidingWindowDeclRef(), "tmp");
+ temp2 << ", tmp);\n\t\t\t";
+ temp2 << "}\n\t";
+ }
+ ss << temp2.str();
+ }
+ ss << "}\n";
+ needBody = false;
+ return nCurWindowSize;
+ }
+}
+
+template class DynamicKernelSlidingArgument<VectorRef>;
+template class DynamicKernelSlidingArgument<VectorRefStringsToZero>;
+template class DynamicKernelSlidingArgument<DynamicKernelStringArgument>;
+
+namespace {
+
+/// A mixed string/numeric vector
+class DynamicKernelMixedSlidingArgument : public VectorRef
+{
+public:
+ DynamicKernelMixedSlidingArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft, const std::shared_ptr<SlidingFunctionBase>& CodeGen,
+ int index ) :
+ VectorRef(config, s, ft),
+ mDoubleArgument(mCalcConfig, s, ft, CodeGen, index),
+ mStringArgument(mCalcConfig, s + "s", ft, CodeGen, index) { }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ mDoubleArgument.GenSlidingWindowDecl(ss);
+ ss << ", ";
+ mStringArgument.GenSlidingWindowDecl(ss);
+ }
+ virtual void GenSlidingWindowFunction( outputstream& ) override { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ mDoubleArgument.GenDecl(ss);
+ ss << ", ";
+ mStringArgument.GenDecl(ss);
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ mDoubleArgument.GenDeclRef(ss);
+ ss << ",";
+ mStringArgument.GenDeclRef(ss);
+ }
+ virtual std::string GenSlidingWindowDeclRef( bool nested ) const override
+ {
+ outputstream ss;
+ ss << "(!isnan(" << mDoubleArgument.GenSlidingWindowDeclRef(nested);
+ ss << ")?" << mDoubleArgument.GenSlidingWindowDeclRef(nested);
+ ss << ":" << mStringArgument.GenSlidingWindowDeclRef(nested);
+ ss << ")";
+ return ss.str();
+ }
+ virtual std::string GenDoubleSlidingWindowDeclRef( bool = false ) const override
+ {
+ outputstream ss;
+ ss << mDoubleArgument.GenSlidingWindowDeclRef();
+ return ss.str();
+ }
+ virtual std::string GenStringSlidingWindowDeclRef( bool = false ) const override
+ {
+ outputstream ss;
+ ss << mStringArgument.GenSlidingWindowDeclRef();
+ return ss.str();
+ }
+ virtual size_t Marshal( cl_kernel k, int argno, int vw, cl_program p ) override
+ {
+ int i = mDoubleArgument.Marshal(k, argno, vw, p);
+ i += mStringArgument.Marshal(k, argno + i, vw, p);
+ return i;
+ }
+
+protected:
+ DynamicKernelSlidingArgument<VectorRef> mDoubleArgument;
+ DynamicKernelSlidingArgument<DynamicKernelStringArgument> mStringArgument;
+};
+
+/// Holds the symbol table for a given dynamic kernel
+class SymbolTable
+{
+public:
+ typedef std::map<const formula::FormulaToken*, DynamicKernelArgumentRef> ArgumentMap;
+ // This avoids instability caused by using pointer as the key type
+ SymbolTable() : mCurId(0) { }
+ template <class T>
+ const DynamicKernelArgument* DeclRefArg(const ScCalcConfig& config, const FormulaTreeNodeRef&,
+ std::shared_ptr<SlidingFunctionBase> pCodeGen, int nResultSize);
+ /// Used to generate sliding window helpers
+ void DumpSlidingWindowFunctions( outputstream& ss )
+ {
+ for (auto const& argument : mParams)
+ {
+ argument->GenSlidingWindowFunction(ss);
+ ss << "\n";
+ }
+ }
+ /// Memory mapping from host to device and pass buffers to the given kernel as
+ /// arguments
+ void Marshal( cl_kernel, int, cl_program );
+
+private:
+ unsigned int mCurId;
+ ArgumentMap mSymbols;
+ std::vector<DynamicKernelArgumentRef> mParams;
+};
+
+void SymbolTable::Marshal( cl_kernel k, int nVectorWidth, cl_program pProgram )
+{
+ int i = 1; //The first argument is reserved for results
+ for (auto const& argument : mParams)
+ {
+ i += argument->Marshal(k, i, nVectorWidth, pProgram);
+ }
+}
+
+}
+
+template<class Base>
+ParallelReductionVectorRef<Base>::ParallelReductionVectorRef(
+ const ScCalcConfig& config, const std::string& s, const FormulaTreeNodeRef& ft,
+ std::shared_ptr<SlidingFunctionBase> CodeGen, int index)
+ : Base(config, s, ft, index)
+ , mpCodeGen(std::move(CodeGen))
+ , mpClmem2(nullptr)
+{
+ FormulaToken* t = ft->GetFormulaToken();
+ if (t->GetType() != formula::svDoubleVectorRef)
+ throw Unhandled(__FILE__, __LINE__);
+ mpDVR = static_cast<const formula::DoubleVectorRefToken*>(t);
+ bIsStartFixed = mpDVR->IsStartFixed();
+ bIsEndFixed = mpDVR->IsEndFixed();
+}
+
+template<class Base>
+void ParallelReductionVectorRef<Base>::GenSlidingWindowFunction( outputstream& ss )
+{
+ if (!dynamic_cast<OpAverage*>(mpCodeGen.get()))
+ {
+ std::string name = Base::GetName();
+ ss << "__kernel void " << name;
+ ss << "_reduction(__global double* A, "
+ "__global double *result,int arrayLength,int windowSize){\n";
+ ss << " double tmp, current_result =" <<
+ mpCodeGen->GetBottom();
+ ss << ";\n";
+ ss << " int writePos = get_group_id(1);\n";
+ ss << " int lidx = get_local_id(0);\n";
+ ss << " __local double shm_buf[256];\n";
+ if (mpDVR->IsStartFixed())
+ ss << " int offset = 0;\n";
+ else // if (!mpDVR->IsStartFixed())
+ ss << " int offset = get_group_id(1);\n";
+ if (mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ ss << " int end = windowSize;\n";
+ else if (!mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ ss << " int end = offset + windowSize;\n";
+ else if (mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ ss << " int end = windowSize + get_group_id(1);\n";
+ else if (!mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ ss << " int end = windowSize;\n";
+ ss << " end = min(end, arrayLength);\n";
+
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " int loop = arrayLength/512 + 1;\n";
+ ss << " for (int l=0; l<loop; l++){\n";
+ ss << " tmp = " << mpCodeGen->GetBottom() << ";\n";
+ ss << " int loopOffset = l*512;\n";
+ ss << " if((loopOffset + lidx + offset + 256) < end) {\n";
+ ss << " tmp = legalize(" << mpCodeGen->Gen2(
+ "A[loopOffset + lidx + offset]", "tmp") << ", tmp);\n";
+ ss << " tmp = legalize(" << mpCodeGen->Gen2(
+ "A[loopOffset + lidx + offset + 256]", "tmp") << ", tmp);\n";
+ ss << " } else if ((loopOffset + lidx + offset) < end)\n";
+ ss << " tmp = legalize(" << mpCodeGen->Gen2(
+ "A[loopOffset + lidx + offset]", "tmp") << ", tmp);\n";
+ ss << " shm_buf[lidx] = tmp;\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " for (int i = 128; i >0; i/=2) {\n";
+ ss << " if (lidx < i)\n";
+ ss << " shm_buf[lidx] = ";
+ // Special case count
+ if (dynamic_cast<OpCount*>(mpCodeGen.get()))
+ ss << "shm_buf[lidx] + shm_buf[lidx + i];\n";
+ else
+ ss << mpCodeGen->Gen2("shm_buf[lidx]", "shm_buf[lidx + i]") << ";\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " current_result =";
+ if (dynamic_cast<OpCount*>(mpCodeGen.get()))
+ ss << "current_result + shm_buf[0]";
+ else
+ ss << mpCodeGen->Gen2("current_result", "shm_buf[0]");
+ ss << ";\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " result[writePos] = current_result;\n";
+ ss << "}\n";
+ }
+ else
+ {
+ std::string name = Base::GetName();
+ /*sum reduction*/
+ ss << "__kernel void " << name << "_sum";
+ ss << "_reduction(__global double* A, "
+ "__global double *result,int arrayLength,int windowSize){\n";
+ ss << " double tmp, current_result =" <<
+ mpCodeGen->GetBottom();
+ ss << ";\n";
+ ss << " int writePos = get_group_id(1);\n";
+ ss << " int lidx = get_local_id(0);\n";
+ ss << " __local double shm_buf[256];\n";
+ if (mpDVR->IsStartFixed())
+ ss << " int offset = 0;\n";
+ else // if (!mpDVR->IsStartFixed())
+ ss << " int offset = get_group_id(1);\n";
+ if (mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ ss << " int end = windowSize;\n";
+ else if (!mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ ss << " int end = offset + windowSize;\n";
+ else if (mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ ss << " int end = windowSize + get_group_id(1);\n";
+ else if (!mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ ss << " int end = windowSize;\n";
+ ss << " end = min(end, arrayLength);\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " int loop = arrayLength/512 + 1;\n";
+ ss << " for (int l=0; l<loop; l++){\n";
+ ss << " tmp = " << mpCodeGen->GetBottom() << ";\n";
+ ss << " int loopOffset = l*512;\n";
+ ss << " if((loopOffset + lidx + offset + 256) < end) {\n";
+ ss << " tmp = legalize(";
+ ss << "(A[loopOffset + lidx + offset]+ tmp)";
+ ss << ", tmp);\n";
+ ss << " tmp = legalize((A[loopOffset + lidx + offset + 256]+ tmp)";
+ ss << ", tmp);\n";
+ ss << " } else if ((loopOffset + lidx + offset) < end)\n";
+ ss << " tmp = legalize((A[loopOffset + lidx + offset] + tmp)";
+ ss << ", tmp);\n";
+ ss << " shm_buf[lidx] = tmp;\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " for (int i = 128; i >0; i/=2) {\n";
+ ss << " if (lidx < i)\n";
+ ss << " shm_buf[lidx] = ";
+ ss << "shm_buf[lidx] + shm_buf[lidx + i];\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " current_result =";
+ ss << "current_result + shm_buf[0]";
+ ss << ";\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " result[writePos] = current_result;\n";
+ ss << "}\n";
+ /*count reduction*/
+ ss << "__kernel void " << name << "_count";
+ ss << "_reduction(__global double* A, "
+ "__global double *result,int arrayLength,int windowSize){\n";
+ ss << " double tmp, current_result =" <<
+ mpCodeGen->GetBottom();
+ ss << ";\n";
+ ss << " int writePos = get_group_id(1);\n";
+ ss << " int lidx = get_local_id(0);\n";
+ ss << " __local double shm_buf[256];\n";
+ if (mpDVR->IsStartFixed())
+ ss << " int offset = 0;\n";
+ else // if (!mpDVR->IsStartFixed())
+ ss << " int offset = get_group_id(1);\n";
+ if (mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ ss << " int end = windowSize;\n";
+ else if (!mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ ss << " int end = offset + windowSize;\n";
+ else if (mpDVR->IsStartFixed() && !mpDVR->IsEndFixed())
+ ss << " int end = windowSize + get_group_id(1);\n";
+ else if (!mpDVR->IsStartFixed() && mpDVR->IsEndFixed())
+ ss << " int end = windowSize;\n";
+ ss << " end = min(end, arrayLength);\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " int loop = arrayLength/512 + 1;\n";
+ ss << " for (int l=0; l<loop; l++){\n";
+ ss << " tmp = " << mpCodeGen->GetBottom() << ";\n";
+ ss << " int loopOffset = l*512;\n";
+ ss << " if((loopOffset + lidx + offset + 256) < end) {\n";
+ ss << " tmp = legalize((isnan(A[loopOffset + lidx + offset])?tmp:tmp+1.0)";
+ ss << ", tmp);\n";
+ ss << " tmp = legalize((isnan(A[loopOffset + lidx + offset+256])?tmp:tmp+1.0)";
+ ss << ", tmp);\n";
+ ss << " } else if ((loopOffset + lidx + offset) < end)\n";
+ ss << " tmp = legalize((isnan(A[loopOffset + lidx + offset])?tmp:tmp+1.0)";
+ ss << ", tmp);\n";
+ ss << " shm_buf[lidx] = tmp;\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " for (int i = 128; i >0; i/=2) {\n";
+ ss << " if (lidx < i)\n";
+ ss << " shm_buf[lidx] = ";
+ ss << "shm_buf[lidx] + shm_buf[lidx + i];\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " current_result =";
+ ss << "current_result + shm_buf[0];";
+ ss << ";\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " result[writePos] = current_result;\n";
+ ss << "}\n";
+ }
+}
+
+template<class Base>
+std::string ParallelReductionVectorRef<Base>::GenSlidingWindowDeclRef( bool ) const
+{
+ outputstream ss;
+ if (!bIsStartFixed && !bIsEndFixed)
+ ss << Base::GetName() << "[i + gid0]";
+ else
+ ss << Base::GetName() << "[i]";
+ return ss.str();
+}
+
+template<class Base>
+size_t ParallelReductionVectorRef<Base>::GenReductionLoopHeader(
+ outputstream& ss, int nResultSize, bool& needBody )
+{
+ assert(mpDVR);
+ size_t nCurWindowSize = mpDVR->GetRefRowSize();
+ std::string temp = Base::GetName() + "[gid0]";
+ ss << "tmp = ";
+ // Special case count
+ if (dynamic_cast<OpAverage*>(mpCodeGen.get()))
+ {
+ ss << mpCodeGen->Gen2(temp, "tmp") << ";\n";
+ ss << "nCount = nCount-1;\n";
+ ss << "nCount = nCount +"; /*re-assign nCount from count reduction*/
+ ss << Base::GetName() << "[gid0+" << nResultSize << "]" << ";\n";
+ }
+ else if (dynamic_cast<OpCount*>(mpCodeGen.get()))
+ ss << temp << "+ tmp";
+ else
+ ss << mpCodeGen->Gen2(temp, "tmp");
+ ss << ";\n\t";
+ needBody = false;
+ return nCurWindowSize;
+}
+
+template<class Base>
+size_t ParallelReductionVectorRef<Base>::Marshal( cl_kernel k, int argno, int w, cl_program mpProgram )
+{
+ assert(Base::mpClmem == nullptr);
+
+ OpenCLZone zone;
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+ cl_int err;
+ size_t nInput = mpDVR->GetArrayLength();
+ size_t nCurWindowSize = mpDVR->GetRefRowSize();
+ // create clmem buffer
+ if (mpDVR->GetArrays()[Base::mnIndex].mpNumericArray == nullptr)
+ throw Unhandled(__FILE__, __LINE__);
+ double* pHostBuffer = const_cast<double*>(
+ mpDVR->GetArrays()[Base::mnIndex].mpNumericArray);
+ size_t szHostBuffer = nInput * sizeof(double);
+ Base::mpClmem = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_ONLY) | CL_MEM_USE_HOST_PTR,
+ szHostBuffer,
+ pHostBuffer, &err);
+ SAL_INFO("sc.opencl", "Created buffer " << Base::mpClmem << " size " << nInput << "*" << sizeof(double) << "=" << szHostBuffer << " using host buffer " << pHostBuffer);
+
+ mpClmem2 = clCreateBuffer(kEnv.mpkContext,
+ CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR,
+ sizeof(double) * w, nullptr, nullptr);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem2 << " size " << sizeof(double) << "*" << w << "=" << (sizeof(double)*w));
+
+ // reproduce the reduction function name
+ std::string kernelName;
+ if (!dynamic_cast<OpAverage*>(mpCodeGen.get()))
+ kernelName = Base::GetName() + "_reduction";
+ else
+ kernelName = Base::GetName() + "_sum_reduction";
+ cl_kernel redKernel = clCreateKernel(mpProgram, kernelName.c_str(), &err);
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clCreateKernel", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created kernel " << redKernel << " with name " << kernelName << " in program " << mpProgram);
+
+ // set kernel arg of reduction kernel
+ // TODO(Wei Wei): use unique name for kernel
+ cl_mem buf = Base::GetCLBuffer();
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 0 << ": cl_mem: " << buf);
+ err = clSetKernelArg(redKernel, 0, sizeof(cl_mem),
+ static_cast<void*>(&buf));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 1 << ": cl_mem: " << mpClmem2);
+ err = clSetKernelArg(redKernel, 1, sizeof(cl_mem), &mpClmem2);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 2 << ": cl_int: " << nInput);
+ err = clSetKernelArg(redKernel, 2, sizeof(cl_int), static_cast<void*>(&nInput));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 3 << ": cl_int: " << nCurWindowSize);
+ err = clSetKernelArg(redKernel, 3, sizeof(cl_int), static_cast<void*>(&nCurWindowSize));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ // set work group size and execute
+ size_t global_work_size[] = { 256, static_cast<size_t>(w) };
+ size_t const local_work_size[] = { 256, 1 };
+ SAL_INFO("sc.opencl", "Enqueuing kernel " << redKernel);
+ err = clEnqueueNDRangeKernel(kEnv.mpkCmdQueue, redKernel, 2, nullptr,
+ global_work_size, local_work_size, 0, nullptr, nullptr);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueNDRangeKernel", err, __FILE__, __LINE__);
+ err = clFinish(kEnv.mpkCmdQueue);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clFinish", err, __FILE__, __LINE__);
+ if (dynamic_cast<OpAverage*>(mpCodeGen.get()))
+ {
+ /*average need more reduction kernel for count computing*/
+ std::unique_ptr<double[]> pAllBuffer(new double[2 * w]);
+ double* resbuf = static_cast<double*>(clEnqueueMapBuffer(kEnv.mpkCmdQueue,
+ mpClmem2,
+ CL_TRUE, CL_MAP_READ, 0,
+ sizeof(double) * w, 0, nullptr, nullptr,
+ &err));
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clEnqueueMapBuffer", err, __FILE__, __LINE__);
+
+ for (int i = 0; i < w; i++)
+ pAllBuffer[i] = resbuf[i];
+ err = clEnqueueUnmapMemObject(kEnv.mpkCmdQueue, mpClmem2, resbuf, 0, nullptr, nullptr);
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clEnqueueUnmapMemObject", err, __FILE__, __LINE__);
+
+ kernelName = Base::GetName() + "_count_reduction";
+ redKernel = clCreateKernel(mpProgram, kernelName.c_str(), &err);
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clCreateKernel", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created kernel " << redKernel << " with name " << kernelName << " in program " << mpProgram);
+
+ // set kernel arg of reduction kernel
+ buf = Base::GetCLBuffer();
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 0 << ": cl_mem: " << buf);
+ err = clSetKernelArg(redKernel, 0, sizeof(cl_mem),
+ static_cast<void*>(&buf));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 1 << ": cl_mem: " << mpClmem2);
+ err = clSetKernelArg(redKernel, 1, sizeof(cl_mem), &mpClmem2);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 2 << ": cl_int: " << nInput);
+ err = clSetKernelArg(redKernel, 2, sizeof(cl_int), static_cast<void*>(&nInput));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << 3 << ": cl_int: " << nCurWindowSize);
+ err = clSetKernelArg(redKernel, 3, sizeof(cl_int), static_cast<void*>(&nCurWindowSize));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ // set work group size and execute
+ size_t global_work_size1[] = { 256, static_cast<size_t>(w) };
+ size_t const local_work_size1[] = { 256, 1 };
+ SAL_INFO("sc.opencl", "Enqueuing kernel " << redKernel);
+ err = clEnqueueNDRangeKernel(kEnv.mpkCmdQueue, redKernel, 2, nullptr,
+ global_work_size1, local_work_size1, 0, nullptr, nullptr);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueNDRangeKernel", err, __FILE__, __LINE__);
+ err = clFinish(kEnv.mpkCmdQueue);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clFinish", err, __FILE__, __LINE__);
+ resbuf = static_cast<double*>(clEnqueueMapBuffer(kEnv.mpkCmdQueue,
+ mpClmem2,
+ CL_TRUE, CL_MAP_READ, 0,
+ sizeof(double) * w, 0, nullptr, nullptr,
+ &err));
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clEnqueueMapBuffer", err, __FILE__, __LINE__);
+ for (int i = 0; i < w; i++)
+ pAllBuffer[i + w] = resbuf[i];
+ err = clEnqueueUnmapMemObject(kEnv.mpkCmdQueue, mpClmem2, resbuf, 0, nullptr, nullptr);
+ // FIXME: Is it intentional to not throw an OpenCLError even if the clEnqueueUnmapMemObject() fails?
+ if (CL_SUCCESS != err)
+ SAL_WARN("sc.opencl", "clEnqueueUnmapMemObject failed: " << openclwrapper::errorString(err));
+ if (mpClmem2)
+ {
+ err = clReleaseMemObject(mpClmem2);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseMemObject failed: " << openclwrapper::errorString(err));
+ mpClmem2 = nullptr;
+ }
+ mpClmem2 = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_WRITE) | CL_MEM_COPY_HOST_PTR,
+ w * sizeof(double) * 2, pAllBuffer.get(), &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem2 << " size " << w << "*" << sizeof(double) << "=" << (w*sizeof(double)) << " copying host buffer " << pAllBuffer.get());
+ }
+ // set kernel arg
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": cl_mem: " << mpClmem2);
+ err = clSetKernelArg(k, argno, sizeof(cl_mem), &mpClmem2);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+}
+
+template<class Base>
+ParallelReductionVectorRef<Base>::~ParallelReductionVectorRef()
+{
+ if (mpClmem2)
+ {
+ cl_int err;
+ err = clReleaseMemObject(mpClmem2);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseMemObject failed: " << openclwrapper::errorString(err));
+ mpClmem2 = nullptr;
+ }
+}
+
+template class ParallelReductionVectorRef<VectorRef>;
+
+namespace {
+
+struct SumIfsArgs
+{
+ explicit SumIfsArgs(cl_mem x) : mCLMem(x), mConst(0.0) { }
+ explicit SumIfsArgs(double x) : mCLMem(nullptr), mConst(x) { }
+ cl_mem mCLMem;
+ double mConst;
+};
+
+/// Helper functions that have multiple buffers
+class DynamicKernelSoPArguments : public DynamicKernelArgument
+{
+public:
+ typedef std::vector<DynamicKernelArgumentRef> SubArgumentsType;
+
+ DynamicKernelSoPArguments( const ScCalcConfig& config,
+ const std::string& s, const FormulaTreeNodeRef& ft,
+ std::shared_ptr<SlidingFunctionBase> pCodeGen, int nResultSize );
+
+ /// Create buffer and pass the buffer to a given kernel
+ virtual size_t Marshal( cl_kernel k, int argno, int nVectorWidth, cl_program pProgram ) override
+ {
+ OpenCLZone zone;
+ unsigned i = 0;
+ for (const auto& rxSubArgument : mvSubArguments)
+ {
+ i += rxSubArgument->Marshal(k, argno + i, nVectorWidth, pProgram);
+ }
+ if (OpSumIfs* OpSumCodeGen = dynamic_cast<OpSumIfs*>(mpCodeGen.get()))
+ {
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+ cl_int err;
+ DynamicKernelArgument* Arg = mvSubArguments[0].get();
+ DynamicKernelSlidingArgument<VectorRef>* slidingArgPtr =
+ static_cast<DynamicKernelSlidingArgument<VectorRef>*>(Arg);
+ mpClmem2 = nullptr;
+
+ if (OpSumCodeGen->NeedReductionKernel())
+ {
+ size_t nInput = slidingArgPtr->GetArrayLength();
+ size_t nCurWindowSize = slidingArgPtr->GetWindowSize();
+ std::vector<SumIfsArgs> vclmem;
+
+ for (const auto& rxSubArgument : mvSubArguments)
+ {
+ if (VectorRef* VR = dynamic_cast<VectorRef*>(rxSubArgument.get()))
+ vclmem.emplace_back(VR->GetCLBuffer());
+ else if (DynamicKernelConstantArgument* CA = dynamic_cast<DynamicKernelConstantArgument*>(rxSubArgument.get()))
+ vclmem.emplace_back(CA->GetDouble());
+ else
+ vclmem.emplace_back(nullptr);
+ }
+ mpClmem2 = clCreateBuffer(kEnv.mpkContext, CL_MEM_READ_WRITE,
+ sizeof(double) * nVectorWidth, nullptr, &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpClmem2 << " size " << sizeof(double) << "*" << nVectorWidth << "=" << (sizeof(double)*nVectorWidth));
+
+ std::string kernelName = mvSubArguments[0]->GetName() + "_SumIfs_reduction";
+ cl_kernel redKernel = clCreateKernel(pProgram, kernelName.c_str(), &err);
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clCreateKernel", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created kernel " << redKernel << " with name " << kernelName << " in program " << pProgram);
+
+ // set kernel arg of reduction kernel
+ for (size_t j = 0; j < vclmem.size(); j++)
+ {
+ if (vclmem[j].mCLMem)
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << j << ": cl_mem: " << vclmem[j].mCLMem);
+ else
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << j << ": double: " << preciseFloat( vclmem[j].mConst ));
+ err = clSetKernelArg(redKernel, j,
+ vclmem[j].mCLMem ? sizeof(cl_mem) : sizeof(double),
+ vclmem[j].mCLMem ? static_cast<void*>(&vclmem[j].mCLMem) :
+ static_cast<void*>(&vclmem[j].mConst));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ }
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << vclmem.size() << ": cl_mem: " << mpClmem2);
+ err = clSetKernelArg(redKernel, vclmem.size(), sizeof(cl_mem), static_cast<void*>(&mpClmem2));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << (vclmem.size() + 1) << ": cl_int: " << nInput);
+ err = clSetKernelArg(redKernel, vclmem.size() + 1, sizeof(cl_int), static_cast<void*>(&nInput));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Kernel " << redKernel << " arg " << (vclmem.size() + 2) << ": cl_int: " << nCurWindowSize);
+ err = clSetKernelArg(redKernel, vclmem.size() + 2, sizeof(cl_int), static_cast<void*>(&nCurWindowSize));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ // set work group size and execute
+ size_t global_work_size[] = { 256, static_cast<size_t>(nVectorWidth) };
+ size_t const local_work_size[] = { 256, 1 };
+ SAL_INFO("sc.opencl", "Enqueuing kernel " << redKernel);
+ err = clEnqueueNDRangeKernel(kEnv.mpkCmdQueue, redKernel, 2, nullptr,
+ global_work_size, local_work_size, 0, nullptr, nullptr);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueNDRangeKernel", err, __FILE__, __LINE__);
+
+ err = clFinish(kEnv.mpkCmdQueue);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clFinish", err, __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "Releasing kernel " << redKernel);
+ err = clReleaseKernel(redKernel);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseKernel failed: " << openclwrapper::errorString(err));
+
+ // Pass mpClmem2 to the "real" kernel
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": cl_mem: " << mpClmem2);
+ err = clSetKernelArg(k, argno, sizeof(cl_mem), static_cast<void*>(&mpClmem2));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ }
+ }
+ return i;
+ }
+
+ virtual void GenSlidingWindowFunction( outputstream& ss ) override
+ {
+ for (DynamicKernelArgumentRef & rArg : mvSubArguments)
+ rArg->GenSlidingWindowFunction(ss);
+ mpCodeGen->GenSlidingWindowFunction(ss, mSymName, mvSubArguments);
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ for (size_t i = 0; i < mvSubArguments.size(); i++)
+ {
+ if (i)
+ ss << ",";
+ mvSubArguments[i]->GenDeclRef(ss);
+ }
+ }
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ for (SubArgumentsType::const_iterator it = mvSubArguments.begin(), e = mvSubArguments.end(); it != e;
+ ++it)
+ {
+ if (it != mvSubArguments.begin())
+ ss << ", ";
+ (*it)->GenDecl(ss);
+ }
+ }
+
+ virtual size_t GetWindowSize() const override
+ {
+ size_t nCurWindowSize = 0;
+ for (const auto & rSubArgument : mvSubArguments)
+ {
+ size_t nCurChildWindowSize = rSubArgument->GetWindowSize();
+ nCurWindowSize = (nCurWindowSize < nCurChildWindowSize) ?
+ nCurChildWindowSize : nCurWindowSize;
+ }
+ return nCurWindowSize;
+ }
+
+ /// When declared as input to a sliding window function
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ for (SubArgumentsType::const_iterator it = mvSubArguments.begin(), e = mvSubArguments.end(); it != e;
+ ++it)
+ {
+ if (it != mvSubArguments.begin())
+ ss << ", ";
+ (*it)->GenSlidingWindowDecl(ss);
+ }
+ }
+ /// Generate either a function call to each children
+ /// or directly inline it if we are already inside a loop
+ virtual std::string GenSlidingWindowDeclRef( bool nested = false ) const override
+ {
+ outputstream ss;
+ if (!nested)
+ {
+ ss << mSymName << "_" << mpCodeGen->BinFuncName() << "(";
+ for (size_t i = 0; i < mvSubArguments.size(); i++)
+ {
+ if (i)
+ ss << ", ";
+ mvSubArguments[i]->GenDeclRef(ss);
+ }
+ ss << ")";
+ }
+ else
+ {
+ if (mvSubArguments.size() != 2)
+ throw Unhandled(__FILE__, __LINE__);
+ bool bArgument1_NeedNested =
+ mvSubArguments[0]->GetFormulaToken()->GetType()
+ != formula::svSingleVectorRef;
+ bool bArgument2_NeedNested =
+ mvSubArguments[1]->GetFormulaToken()->GetType()
+ != formula::svSingleVectorRef;
+ ss << "(";
+ ss << mpCodeGen->
+ Gen2(mvSubArguments[0]
+ ->GenSlidingWindowDeclRef(bArgument1_NeedNested),
+ mvSubArguments[1]
+ ->GenSlidingWindowDeclRef(bArgument2_NeedNested));
+ ss << ")";
+ }
+ return ss.str();
+ }
+ virtual std::string DumpOpName() const override
+ {
+ std::string t = "_" + mpCodeGen->BinFuncName();
+ for (const auto & rSubArgument : mvSubArguments)
+ t += rSubArgument->DumpOpName();
+ return t;
+ }
+ virtual void DumpInlineFun( std::set<std::string>& decls,
+ std::set<std::string>& funs ) const override
+ {
+ mpCodeGen->BinInlineFun(decls, funs);
+ for (const auto & rSubArgument : mvSubArguments)
+ rSubArgument->DumpInlineFun(decls, funs);
+ }
+ virtual bool IsEmpty() const override
+ {
+ for (const auto & rSubArgument : mvSubArguments)
+ if( !rSubArgument->IsEmpty())
+ return false;
+ return true;
+ }
+ virtual ~DynamicKernelSoPArguments() override
+ {
+ if (mpClmem2)
+ {
+ cl_int err;
+ err = clReleaseMemObject(mpClmem2);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseMemObject failed: " << openclwrapper::errorString(err));
+ mpClmem2 = nullptr;
+ }
+ }
+
+private:
+ SubArgumentsType mvSubArguments;
+ std::shared_ptr<SlidingFunctionBase> mpCodeGen;
+ cl_mem mpClmem2;
+};
+
+}
+
+static DynamicKernelArgumentRef SoPHelper( const ScCalcConfig& config,
+ const std::string& ts, const FormulaTreeNodeRef& ft, std::shared_ptr<SlidingFunctionBase> pCodeGen,
+ int nResultSize )
+{
+ return std::make_shared<DynamicKernelSoPArguments>(config, ts, ft, std::move(pCodeGen), nResultSize);
+}
+
+template<class Base>
+static std::shared_ptr<DynamicKernelArgument> VectorRefFactory( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft,
+ std::shared_ptr<SlidingFunctionBase>& pCodeGen,
+ int index )
+{
+ //Black lists ineligible classes here ..
+ // SUMIFS does not perform parallel reduction at DoubleVectorRef level
+ if (dynamic_cast<OpSumIfs*>(pCodeGen.get()))
+ {
+ // coverity[identical_branches] - only identical if Base happens to be VectorRef
+ if (index == 0) // the first argument of OpSumIfs cannot be strings anyway
+ return std::make_shared<DynamicKernelSlidingArgument<VectorRef>>(config, s, ft, pCodeGen, index);
+ return std::make_shared<DynamicKernelSlidingArgument<Base>>(config, s, ft, pCodeGen, index);
+ }
+ // AVERAGE is not supported yet
+ //Average has been supported by reduction kernel
+ /*else if (dynamic_cast<OpAverage*>(pCodeGen.get()))
+ {
+ return new DynamicKernelSlidingArgument<Base>(config, s, ft, pCodeGen, index);
+ }*/
+ // MUL is not supported yet
+ else if (dynamic_cast<OpMul*>(pCodeGen.get()))
+ {
+ return std::make_shared<DynamicKernelSlidingArgument<Base>>(config, s, ft, pCodeGen, index);
+ }
+ // Sub is not a reduction per se
+ else if (dynamic_cast<OpSub*>(pCodeGen.get()))
+ {
+ return std::make_shared<DynamicKernelSlidingArgument<Base>>(config, s, ft, pCodeGen, index);
+ }
+ // Only child class of Reduction is supported
+ else if (!dynamic_cast<Reduction*>(pCodeGen.get()))
+ {
+ return std::make_shared<DynamicKernelSlidingArgument<Base>>(config, s, ft, pCodeGen, index);
+ }
+
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken*>(
+ ft->GetFormulaToken());
+ // Window being too small to justify a parallel reduction
+ if (pDVR->GetRefRowSize() < REDUCE_THRESHOLD)
+ return std::make_shared<DynamicKernelSlidingArgument<Base>>(config, s, ft, pCodeGen, index);
+ if (pDVR->IsStartFixed() == pDVR->IsEndFixed())
+ return std::make_shared<ParallelReductionVectorRef<Base>>(config, s, ft, pCodeGen, index);
+ else // Other cases are not supported as well
+ return std::make_shared<DynamicKernelSlidingArgument<Base>>(config, s, ft, pCodeGen, index);
+}
+
+DynamicKernelSoPArguments::DynamicKernelSoPArguments(const ScCalcConfig& config,
+ const std::string& s, const FormulaTreeNodeRef& ft, std::shared_ptr<SlidingFunctionBase> pCodeGen, int nResultSize ) :
+ DynamicKernelArgument(config, s, ft), mpCodeGen(pCodeGen), mpClmem2(nullptr)
+{
+ size_t nChildren = ft->Children.size();
+
+ for (size_t i = 0; i < nChildren; i++)
+ {
+ FormulaTreeNodeRef rChild = ft->Children[i];
+ if (!rChild)
+ throw Unhandled(__FILE__, __LINE__);
+ FormulaToken* pChild = rChild->GetFormulaToken();
+ if (!pChild)
+ throw Unhandled(__FILE__, __LINE__);
+ OpCode opc = pChild->GetOpCode();
+ outputstream tmpname;
+ tmpname << s << "_" << i;
+ std::string ts = tmpname.str();
+ switch (opc)
+ {
+ case ocPush:
+ if (pChild->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken*>(pChild);
+
+ // The code below will split one svDoubleVectorRef into one subargument
+ // for each column of data, and then all these subarguments will be later
+ // passed to the code generating the function. Most of the code then
+ // simply treats each subargument as one argument to the function, and thus
+ // could break in this case.
+ // As a simple solution, simply prevent this case, unless the code in question
+ // explicitly claims it will handle this situation properly.
+ if( pDVR->GetArrays().size() > 1 )
+ {
+ if( !pCodeGen->canHandleMultiVector())
+ throw UnhandledToken(("Function '" + pCodeGen->BinFuncName()
+ + "' cannot handle multi-column DoubleRef").c_str(), __FILE__, __LINE__);
+
+ SAL_INFO("sc.opencl", "multi-column DoubleRef");
+
+ }
+
+ // FIXME: The Right Thing to do would be to compare the accumulated kernel
+ // parameter size against the CL_DEVICE_MAX_PARAMETER_SIZE of the device, but
+ // let's just do this sanity check for now. The kernel compilation will
+ // hopefully fail anyway if the size of parameters exceeds the limit and this
+ // sanity check is just to make us bail out a bit earlier.
+
+ // The number 50 comes from the fact that the minimum size of
+ // CL_DEVICE_MAX_PARAMETER_SIZE is 256, which for 32-bit code probably means 64
+ // of them. Round down a bit.
+
+ if (pDVR->GetArrays().size() > 50)
+ throw UnhandledToken(("Kernel would have ridiculously many parameters (" + std::to_string(2 + pDVR->GetArrays().size()) + ")").c_str(), __FILE__, __LINE__);
+
+ for (size_t j = 0; j < pDVR->GetArrays().size(); ++j)
+ {
+ SAL_INFO("sc.opencl", "i=" << i << " j=" << j <<
+ " mpNumericArray=" << pDVR->GetArrays()[j].mpNumericArray <<
+ " mpStringArray=" << pDVR->GetArrays()[j].mpStringArray <<
+ " allStringsAreNull=" << (AllStringsAreNull(pDVR->GetArrays()[j].mpStringArray, pDVR->GetArrayLength())?"YES":"NO") <<
+ " takeNumeric=" << (pCodeGen->takeNumeric()?"YES":"NO") <<
+ " takeString=" << (pCodeGen->takeString()?"YES":"NO"));
+
+ if (pDVR->GetArrays()[j].mpNumericArray &&
+ pCodeGen->takeNumeric() &&
+ pDVR->GetArrays()[j].mpStringArray &&
+ pCodeGen->takeString())
+ {
+ // Function takes numbers or strings, there are both
+ SAL_INFO("sc.opencl", "Numbers and strings");
+ mvSubArguments.push_back(
+ std::make_shared<DynamicKernelMixedSlidingArgument>(mCalcConfig,
+ ts, ft->Children[i], mpCodeGen, j));
+ }
+ else if (pDVR->GetArrays()[j].mpNumericArray &&
+ pCodeGen->takeNumeric() &&
+ (AllStringsAreNull(pDVR->GetArrays()[j].mpStringArray, pDVR->GetArrayLength())
+ || mCalcConfig.meStringConversion == ScCalcConfig::StringConversion::ZERO
+ || pCodeGen->forceStringsToZero()))
+ {
+ // Function takes numbers, and either there
+ // are no strings, or there are strings but
+ // they are to be treated as zero
+ SAL_INFO("sc.opencl", "Numbers (no strings or strings treated as zero)");
+ if(!AllStringsAreNull(pDVR->GetArrays()[j].mpStringArray, pDVR->GetArrayLength()))
+ {
+ mvSubArguments.push_back(
+ VectorRefFactory<VectorRefStringsToZero>(mCalcConfig,
+ ts, ft->Children[i], mpCodeGen, j));
+ }
+ else
+ {
+ mvSubArguments.push_back(
+ VectorRefFactory<VectorRef>(mCalcConfig,
+ ts, ft->Children[i], mpCodeGen, j));
+ }
+ }
+ else if (pDVR->GetArrays()[j].mpNumericArray == nullptr &&
+ pCodeGen->takeNumeric() &&
+ pDVR->GetArrays()[j].mpStringArray &&
+ ( mCalcConfig.meStringConversion == ScCalcConfig::StringConversion::ZERO
+ || pCodeGen->forceStringsToZero()))
+ {
+ // Function takes numbers, and there are only
+ // strings, but they are to be treated as zero
+ SAL_INFO("sc.opencl", "Only strings even if want numbers but should be treated as zero");
+ mvSubArguments.push_back(
+ VectorRefFactory<VectorRefStringsToZero>(mCalcConfig,
+ ts, ft->Children[i], mpCodeGen, j));
+ }
+ else if (pDVR->GetArrays()[j].mpStringArray &&
+ pCodeGen->takeString())
+ {
+ // There are strings, and the function takes strings.
+ SAL_INFO("sc.opencl", "Strings only");
+ mvSubArguments.push_back(
+ VectorRefFactory
+ <DynamicKernelStringArgument>(mCalcConfig,
+ ts, ft->Children[i], mpCodeGen, j));
+ }
+ else if (AllStringsAreNull(pDVR->GetArrays()[j].mpStringArray, pDVR->GetArrayLength()) &&
+ pDVR->GetArrays()[j].mpNumericArray == nullptr)
+ {
+ // There are only empty cells. Push as an
+ // array of NANs
+ SAL_INFO("sc.opencl", "Only empty cells");
+ mvSubArguments.push_back(
+ VectorRefFactory<VectorRef>(mCalcConfig,
+ ts, ft->Children[i], mpCodeGen, j));
+ }
+ else
+ {
+ SAL_INFO("sc.opencl", "Unhandled case, rejecting for OpenCL");
+ throw UnhandledToken(("Unhandled numbers/strings combination for '"
+ + pCodeGen->BinFuncName() + "'").c_str(), __FILE__, __LINE__);
+ }
+ }
+ }
+ else if (pChild->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>(pChild);
+
+ SAL_INFO("sc.opencl", "i=" << i <<
+ " mpNumericArray=" << pSVR->GetArray().mpNumericArray <<
+ " mpStringArray=" << pSVR->GetArray().mpStringArray <<
+ " allStringsAreNull=" << (AllStringsAreNull(pSVR->GetArray().mpStringArray, pSVR->GetArrayLength())?"YES":"NO") <<
+ " takeNumeric=" << (pCodeGen->takeNumeric()?"YES":"NO") <<
+ " takeString=" << (pCodeGen->takeString()?"YES":"NO"));
+
+ if (pSVR->GetArray().mpNumericArray &&
+ pCodeGen->takeNumeric() &&
+ pSVR->GetArray().mpStringArray &&
+ pCodeGen->takeString())
+ {
+ // Function takes numbers or strings, there are both
+ SAL_INFO("sc.opencl", "Numbers and strings");
+ mvSubArguments.push_back(
+ std::make_shared<DynamicKernelMixedArgument>(mCalcConfig,
+ ts, ft->Children[i]));
+ }
+ else if (pSVR->GetArray().mpNumericArray &&
+ pCodeGen->takeNumeric() &&
+ (AllStringsAreNull(pSVR->GetArray().mpStringArray, pSVR->GetArrayLength())
+ || mCalcConfig.meStringConversion == ScCalcConfig::StringConversion::ZERO
+ || pCodeGen->forceStringsToZero()))
+ {
+ // Function takes numbers, and either there
+ // are no strings, or there are strings but
+ // they are to be treated as zero
+ SAL_INFO("sc.opencl", "Numbers (no strings or strings treated as zero)");
+ if( !AllStringsAreNull(pSVR->GetArray().mpStringArray, pSVR->GetArrayLength()))
+ mvSubArguments.push_back(
+ std::make_shared<VectorRefStringsToZero>(mCalcConfig, ts,
+ ft->Children[i]));
+ else
+ mvSubArguments.push_back(
+ std::make_shared<VectorRef>(mCalcConfig, ts,
+ ft->Children[i]));
+ }
+ else if (pSVR->GetArray().mpNumericArray == nullptr &&
+ pCodeGen->takeNumeric() &&
+ pSVR->GetArray().mpStringArray &&
+ (mCalcConfig.meStringConversion == ScCalcConfig::StringConversion::ZERO
+ || pCodeGen->forceStringsToZero()))
+ {
+ // Function takes numbers, and there are only
+ // strings, but they are to be treated as zero
+ SAL_INFO("sc.opencl", "Only strings even if want numbers but should be treated as zero");
+ mvSubArguments.push_back(
+ std::make_shared<VectorRefStringsToZero>(mCalcConfig, ts,
+ ft->Children[i]));
+ }
+ else if (pSVR->GetArray().mpStringArray &&
+ pCodeGen->takeString())
+ {
+ // There are strings, and the function takes strings.
+ SAL_INFO("sc.opencl", "Strings only");
+ mvSubArguments.push_back(
+ std::make_shared<DynamicKernelStringArgument>(mCalcConfig,
+ ts, ft->Children[i]));
+ }
+ else if (AllStringsAreNull(pSVR->GetArray().mpStringArray, pSVR->GetArrayLength()) &&
+ pSVR->GetArray().mpNumericArray == nullptr)
+ {
+ // There are only empty cells. Push as an
+ // array of NANs
+ SAL_INFO("sc.opencl", "Only empty cells");
+ mvSubArguments.push_back(
+ std::make_shared<VectorRef>(mCalcConfig, ts,
+ ft->Children[i]));
+ }
+ else
+ {
+ SAL_INFO("sc.opencl", "Unhandled case, rejecting for OpenCL");
+ throw UnhandledToken(("Unhandled numbers/strings combination for '"
+ + pCodeGen->BinFuncName() + "'").c_str(), __FILE__, __LINE__);
+ }
+ }
+ else if (pChild->GetType() == formula::svDouble)
+ {
+ SAL_INFO("sc.opencl", "Constant number case");
+ mvSubArguments.push_back(
+ std::make_shared<DynamicKernelConstantArgument>(mCalcConfig, ts,
+ ft->Children[i]));
+ }
+ else if (pChild->GetType() == formula::svString
+ && pCodeGen->takeString())
+ {
+ SAL_INFO("sc.opencl", "Constant string case");
+ mvSubArguments.push_back(
+ std::make_shared<ConstStringArgument>(mCalcConfig, ts,
+ ft->Children[i]));
+ }
+ else if (pChild->GetType() == formula::svString
+ && !pCodeGen->takeString()
+ && pCodeGen->takeNumeric()
+ && pCodeGen->forceStringsToZero())
+ {
+ SAL_INFO("sc.opencl", "Constant string case, treated as zero");
+ mvSubArguments.push_back(
+ DynamicKernelArgumentRef(new DynamicKernelStringToZeroArgument(mCalcConfig, ts,
+ ft->Children[i])));
+ }
+ else
+ {
+ SAL_INFO("sc.opencl", "Unhandled operand, rejecting for OpenCL");
+ throw UnhandledToken(("unhandled operand " + StackVarEnumToString(pChild->GetType()) + " for ocPush").c_str(), __FILE__, __LINE__);
+ }
+ break;
+ case ocPi:
+ mvSubArguments.push_back(
+ std::make_shared<DynamicKernelPiArgument>(mCalcConfig, ts,
+ ft->Children[i]));
+ break;
+ case ocRandom:
+ mvSubArguments.push_back(
+ std::make_shared<DynamicKernelRandomArgument>(mCalcConfig, ts,
+ ft->Children[i]));
+ break;
+#define CASE(opcode, createCode) \
+ case opcode: \
+ mvSubArguments.push_back(SoPHelper(mCalcConfig, ts, ft->Children[i], createCode, nResultSize)); \
+ break;
+ CASE(ocAbs, std::make_shared<OpAbs>())
+ CASE(ocAdd, std::make_shared<OpSum>(nResultSize))
+ CASE(ocAnd, std::make_shared<OpAnd>())
+ CASE(ocArcCos, std::make_shared<OpArcCos>())
+ CASE(ocArcCosHyp, std::make_shared<OpArcCosHyp>())
+ CASE(ocArcCot, std::make_shared<OpArcCot>())
+ CASE(ocArcCotHyp, std::make_shared<OpArcCotHyp>())
+ CASE(ocArcSin, std::make_shared<OpArcSin>())
+ CASE(ocArcSinHyp, std::make_shared<OpArcSinHyp>())
+ CASE(ocArcTan, std::make_shared<OpArcTan>())
+ CASE(ocArcTan2, std::make_shared<OpArcTan2>())
+ CASE(ocArcTanHyp, std::make_shared<OpArcTanH>())
+ CASE(ocAveDev, std::make_shared<OpAveDev>())
+ CASE(ocAverage, std::make_shared<OpAverage>(nResultSize))
+ CASE(ocAverageA, std::make_shared<OpAverageA>(nResultSize))
+ CASE(ocAverageIf, std::make_shared<OpAverageIf>())
+ CASE(ocAverageIfs, std::make_shared<OpAverageIfs>())
+ CASE(ocB, std::make_shared<OpB>())
+ CASE(ocBetaDist, std::make_shared<OpBetaDist>())
+ CASE(ocBetaInv, std::make_shared<OpBetainv>())
+ CASE(ocBinomDist, std::make_shared<OpBinomdist>())
+ CASE(ocBitAnd, std::make_shared<OpBitAnd>())
+ CASE(ocBitLshift, std::make_shared<OpBitLshift>())
+ CASE(ocBitOr, std::make_shared<OpBitOr>())
+ CASE(ocBitRshift, std::make_shared<OpBitRshift>())
+ CASE(ocBitXor, std::make_shared<OpBitXor>())
+ CASE(ocCeil, std::make_shared<OpCeil>())
+ CASE(ocChiDist, std::make_shared<OpChiDist>())
+ CASE(ocChiInv, std::make_shared<OpChiInv>())
+ CASE(ocChiSqDist, std::make_shared<OpChiSqDist>())
+ CASE(ocChiSqInv, std::make_shared<OpChiSqInv>())
+ CASE(ocCombin, std::make_shared<OpCombin>())
+ CASE(ocCombinA, std::make_shared<OpCombinA>())
+ CASE(ocConfidence, std::make_shared<OpConfidence>())
+ CASE(ocCorrel, std::make_shared<OpCorrel>())
+ CASE(ocCos, std::make_shared<OpCos>())
+ CASE(ocCosHyp, std::make_shared<OpCosh>())
+ CASE(ocCosecant, std::make_shared<OpCsc>())
+ CASE(ocCosecantHyp, std::make_shared<OpCscH>())
+ CASE(ocCot, std::make_shared<OpCot>())
+ CASE(ocCotHyp, std::make_shared<OpCoth>())
+ CASE(ocCount, std::make_shared<OpCount>(nResultSize))
+ CASE(ocCount2, std::make_shared<OpCountA>(nResultSize))
+ CASE(ocCountIf, std::make_shared<OpCountIf>())
+ CASE(ocCountIfs, std::make_shared<OpCountIfs>())
+ CASE(ocCovar, std::make_shared<OpCovar>())
+ CASE(ocCritBinom, std::make_shared<OpCritBinom>())
+ CASE(ocDB, std::make_shared<OpDB>())
+ CASE(ocDDB, std::make_shared<OpDDB>())
+ CASE(ocDeg, std::make_shared<OpDeg>())
+ CASE(ocDevSq, std::make_shared<OpDevSq>())
+ CASE(ocDiv, std::make_shared<OpDiv>(nResultSize))
+ CASE(ocEqual, std::make_shared<OpEqual>())
+ CASE(ocEven, std::make_shared<OpEven>())
+ CASE(ocExp, std::make_shared<OpExp>())
+ CASE(ocExpDist, std::make_shared<OpExponDist>())
+ CASE(ocFDist, std::make_shared<OpFdist>())
+ CASE(ocFInv, std::make_shared<OpFInv>())
+ CASE(ocFTest, std::make_shared<OpFTest>())
+ CASE(ocFV, std::make_shared<OpFV>())
+ CASE(ocFact, std::make_shared<OpFact>())
+ CASE(ocFisher, std::make_shared<OpFisher>())
+ CASE(ocFisherInv, std::make_shared<OpFisherInv>())
+ CASE(ocFloor, std::make_shared<OpFloor>())
+ CASE(ocForecast, std::make_shared<OpForecast>())
+ CASE(ocGamma, std::make_shared<OpGamma>())
+ CASE(ocGammaDist, std::make_shared<OpGammaDist>())
+ CASE(ocGammaInv, std::make_shared<OpGammaInv>())
+ CASE(ocGammaLn, std::make_shared<OpGammaLn>())
+ CASE(ocGauss, std::make_shared<OpGauss>())
+ CASE(ocGeoMean, std::make_shared<OpGeoMean>())
+ CASE(ocGreater, std::make_shared<OpGreater>())
+ CASE(ocGreaterEqual, std::make_shared<OpGreaterEqual>())
+ CASE(ocHarMean, std::make_shared<OpHarMean>())
+ CASE(ocHypGeomDist, std::make_shared<OpHypGeomDist>())
+ CASE(ocIRR, std::make_shared<OpIRR>())
+ CASE(ocISPMT, std::make_shared<OpISPMT>())
+ CASE(ocIf, std::make_shared<OpIf>())
+ CASE(ocInt, std::make_shared<OpInt>())
+ CASE(ocIntercept, std::make_shared<OpIntercept>())
+ CASE(ocIpmt, std::make_shared<OpIPMT>())
+ CASE(ocIsEven, std::make_shared<OpIsEven>())
+ CASE(ocIsOdd, std::make_shared<OpIsOdd>())
+ CASE(ocKurt, std::make_shared<OpKurt>())
+ CASE(ocLess, std::make_shared<OpLess>())
+ CASE(ocLessEqual, std::make_shared<OpLessEqual>())
+ CASE(ocLn, std::make_shared<OpLn>())
+ CASE(ocLog, std::make_shared<OpLog>())
+ CASE(ocLog10, std::make_shared<OpLog10>())
+ CASE(ocLogInv, std::make_shared<OpLogInv>())
+ CASE(ocLogNormDist, std::make_shared<OpLogNormDist>())
+ CASE(ocMIRR, std::make_shared<OpMIRR>())
+ CASE(ocMax, std::make_shared<OpMax>(nResultSize))
+ CASE(ocMaxA, std::make_shared<OpMaxA>(nResultSize))
+ CASE(ocMin, std::make_shared<OpMin>(nResultSize))
+ CASE(ocMinA, std::make_shared<OpMinA>(nResultSize))
+ CASE(ocMod, std::make_shared<OpMod>())
+ CASE(ocMul, std::make_shared<OpMul>(nResultSize))
+ CASE(ocNPV, std::make_shared<OpNPV>())
+ CASE(ocNegBinomVert , std::make_shared<OpNegbinomdist>())
+ CASE(ocNegSub, std::make_shared<OpNegSub>())
+ CASE(ocNormDist, std::make_shared<OpNormdist>())
+ CASE(ocNormInv, std::make_shared<OpNorminv>())
+ CASE(ocNot, std::make_shared<OpNot>())
+ CASE(ocNotEqual, std::make_shared<OpNotEqual>())
+ CASE(ocNper, std::make_shared<OpNper>())
+ CASE(ocOdd, std::make_shared<OpOdd>())
+ CASE(ocOr, std::make_shared<OpOr>())
+ CASE(ocPDuration, std::make_shared<OpPDuration>())
+ CASE(ocPMT, std::make_shared<OpPMT>())
+ CASE(ocPV, std::make_shared<OpPV>())
+ CASE(ocPearson, std::make_shared<OpPearson>())
+ CASE(ocPermut, std::make_shared<OpPermut>())
+ CASE(ocPermutationA, std::make_shared<OpPermutationA>())
+ CASE(ocPhi, std::make_shared<OpPhi>())
+ CASE(ocPoissonDist, std::make_shared<OpPoisson>())
+ CASE(ocPow, std::make_shared<OpPower>())
+ CASE(ocPower, std::make_shared<OpPower>())
+ CASE(ocPpmt, std::make_shared<OpPPMT>())
+ CASE(ocProduct, std::make_shared<OpProduct>())
+ CASE(ocRRI, std::make_shared<OpRRI>())
+ CASE(ocRSQ, std::make_shared<OpRsq>())
+ CASE(ocRad, std::make_shared<OpRadians>())
+ CASE(ocRate, std::make_shared<OpRate>())
+ CASE(ocRound, std::make_shared<OpRound>())
+ CASE(ocRoundDown, std::make_shared<OpRoundDown>())
+ CASE(ocRoundUp, std::make_shared<OpRoundUp>())
+ CASE(ocSLN, std::make_shared<OpSLN>())
+ CASE(ocSNormInv, std::make_shared<OpNormsinv>())
+ CASE(ocSTEYX, std::make_shared<OpSTEYX>())
+ CASE(ocSYD, std::make_shared<OpSYD>())
+ CASE(ocSecant, std::make_shared<OpSec>())
+ CASE(ocSecantHyp, std::make_shared<OpSecH>())
+ CASE(ocSin, std::make_shared<OpSin>())
+ CASE(ocSinHyp, std::make_shared<OpSinh>())
+ CASE(ocSkew, std::make_shared<OpSkew>())
+ CASE(ocSkewp, std::make_shared<OpSkewp>())
+ CASE(ocSlope, std::make_shared<OpSlope>())
+ CASE(ocSqrt, std::make_shared<OpSqrt>())
+ CASE(ocStDev, std::make_shared<OpStDev>())
+ CASE(ocStDevA, std::make_shared<OpStDevA>())
+ CASE(ocStDevP, std::make_shared<OpStDevP>())
+ CASE(ocStDevPA, std::make_shared<OpStDevPA>())
+ CASE(ocStandard, std::make_shared<OpStandard>())
+ CASE(ocStdNormDist, std::make_shared<OpNormsdist>())
+ CASE(ocSub, std::make_shared<OpSub>(nResultSize))
+ CASE(ocSum, std::make_shared<OpSum>(nResultSize))
+ CASE(ocSumIf, std::make_shared<OpSumIf>())
+ CASE(ocSumIfs, std::make_shared<OpSumIfs>())
+ CASE(ocSumProduct, std::make_shared<OpSumProduct>())
+ CASE(ocSumSQ, std::make_shared<OpSumSQ>())
+ CASE(ocSumX2DY2, std::make_shared<OpSumX2PY2>())
+ CASE(ocSumX2MY2, std::make_shared<OpSumX2MY2>())
+ CASE(ocSumXMY2, std::make_shared<OpSumXMY2>())
+ CASE(ocTDist, std::make_shared<OpTDist>())
+ CASE(ocTInv, std::make_shared<OpTInv>())
+ CASE(ocTTest, std::make_shared<OpTTest>())
+ CASE(ocTan, std::make_shared<OpTan>())
+ CASE(ocTanHyp, std::make_shared<OpTanH>())
+ CASE(ocTrunc, std::make_shared<OpTrunc>())
+ CASE(ocVBD, std::make_shared<OpVDB>())
+ CASE(ocVLookup, std::make_shared<OpVLookup>())
+ CASE(ocVar, std::make_shared<OpVar>())
+ CASE(ocVarA, std::make_shared<OpVarA>())
+ CASE(ocVarP, std::make_shared<OpVarP>())
+ CASE(ocVarPA, std::make_shared<OpVarPA>())
+ CASE(ocWeibull, std::make_shared<OpWeibull>())
+ CASE(ocXor, std::make_shared<OpXor>())
+ CASE(ocZTest, std::make_shared<OpZTest>())
+#undef CASE
+ case ocExternal:
+#define EXTCASE( name, createCode ) \
+ else if (pChild->GetExternal() == name) \
+ { \
+ mvSubArguments.push_back(SoPHelper(mCalcConfig, ts, ft->Children[i], createCode, nResultSize)); \
+ }
+
+ if(false) // start else-if chain
+ ;
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getAccrint", std::make_shared<OpAccrint>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getAccrintm", std::make_shared<OpAccrintm>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getAmordegrc", std::make_shared<OpAmordegrc>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getAmorlinc", std::make_shared<OpAmorlinc>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getBesselj", std::make_shared<OpBesselj>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCoupdaybs", std::make_shared<OpCoupdaybs>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCoupdays", std::make_shared<OpCoupdays>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCoupdaysnc", std::make_shared<OpCoupdaysnc>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCoupncd", std::make_shared<OpCoupncd>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCoupnum", std::make_shared<OpCoupnum>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCouppcd", std::make_shared<OpCouppcd>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCumipmt", std::make_shared<OpCumipmt>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getCumprinc", std::make_shared<OpCumprinc>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getDisc", std::make_shared<OpDISC>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getDollarde", std::make_shared<OpDollarde>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getDollarfr", std::make_shared<OpDollarfr>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getDuration", std::make_shared<OpDuration_ADD>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getEffect", std::make_shared<OpEffective>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getFvschedule", std::make_shared<OpFvschedule>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getGestep", std::make_shared<OpGestep>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getIntrate", std::make_shared<OpINTRATE>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getIseven", std::make_shared<OpIsEven>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getIsodd", std::make_shared<OpIsOdd>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getMduration", std::make_shared<OpMDuration>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getMround", std::make_shared<OpMROUND>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getNominal", std::make_shared<OpNominal>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getOddlprice", std::make_shared<OpOddlprice>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getOddlyield", std::make_shared<OpOddlyield>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getPrice", std::make_shared<OpPrice>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getPricedisc", std::make_shared<OpPriceDisc>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getPricemat", std::make_shared<OpPriceMat>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getQuotient", std::make_shared<OpQuotient>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getReceived", std::make_shared<OpReceived>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getSeriessum", std::make_shared<OpSeriesSum>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getSqrtpi", std::make_shared<OpSqrtPi>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getTbilleq", std::make_shared<OpTbilleq>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getTbillprice", std::make_shared<OpTbillprice>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getTbillyield", std::make_shared<OpTbillyield>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getXirr", std::make_shared<OpXirr>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getXnpv", std::make_shared<OpXNPV>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getYield", std::make_shared<OpYield>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getYielddisc", std::make_shared<OpYielddisc>())
+ EXTCASE("com.sun.star.sheet.addin.Analysis.getYieldmat", std::make_shared<OpYieldmat>())
+ else
+ throw UnhandledToken(OUString("unhandled external " + pChild->GetExternal()).toUtf8().getStr(), __FILE__, __LINE__);
+ break;
+#undef EXTCASE
+
+ default:
+ throw UnhandledToken(OUString("unhandled opcode "
+ + formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opc)
+ + "(" + OUString::number(opc) + ")").toUtf8().getStr(), __FILE__, __LINE__);
+ }
+ }
+}
+
+namespace {
+
+class DynamicKernel : public CompiledFormula
+{
+public:
+ DynamicKernel( ScCalcConfig config, FormulaTreeNodeRef r, int nResultSize );
+ virtual ~DynamicKernel() override;
+
+ static std::shared_ptr<DynamicKernel> create( const ScCalcConfig& config, const ScTokenArray& rCode, int nResultSize );
+
+ /// OpenCL code generation
+ void CodeGen();
+
+ /// Produce kernel hash
+ std::string const & GetMD5();
+
+ /// Create program, build, and create kernel
+ /// TODO cache results based on kernel body hash
+ /// TODO: abstract OpenCL part out into OpenCL wrapper.
+ void CreateKernel();
+
+ /// Prepare buffers, marshal them to GPU, and launch the kernel
+ /// TODO: abstract OpenCL part out into OpenCL wrapper.
+ void Launch( size_t nr );
+
+ cl_mem GetResultBuffer() const { return mpResClmem; }
+
+private:
+ ScCalcConfig mCalcConfig;
+ FormulaTreeNodeRef mpRoot;
+ SymbolTable mSyms;
+ std::string mKernelSignature, mKernelHash;
+ std::string mFullProgramSrc;
+ cl_program mpProgram;
+ cl_kernel mpKernel;
+ cl_mem mpResClmem; // Results
+ std::set<std::string> inlineDecl;
+ std::set<std::string> inlineFun;
+
+ int mnResultSize;
+};
+
+}
+
+DynamicKernel::DynamicKernel( ScCalcConfig config, FormulaTreeNodeRef x, int nResultSize ) :
+ mCalcConfig(std::move(config)),
+ mpRoot(std::move(x)),
+ mpProgram(nullptr),
+ mpKernel(nullptr),
+ mpResClmem(nullptr),
+ mnResultSize(nResultSize) {}
+
+DynamicKernel::~DynamicKernel()
+{
+ cl_int err;
+ if (mpResClmem)
+ {
+ err = clReleaseMemObject(mpResClmem);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseMemObject failed: " << openclwrapper::errorString(err));
+ }
+ if (mpKernel)
+ {
+ SAL_INFO("sc.opencl", "Releasing kernel " << mpKernel);
+ err = clReleaseKernel(mpKernel);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseKernel failed: " << openclwrapper::errorString(err));
+ }
+ // mpProgram is not going to be released here -- it's cached.
+}
+
+void DynamicKernel::CodeGen()
+{
+ // Traverse the tree of expression and declare symbols used
+ const DynamicKernelArgument* DK = mSyms.DeclRefArg<DynamicKernelSoPArguments>(mCalcConfig, mpRoot, std::make_shared<OpNop>(mnResultSize), mnResultSize);
+
+ outputstream decl;
+ if (openclwrapper::gpuEnv.mnKhrFp64Flag)
+ {
+ decl << "#if __OPENCL_VERSION__ < 120\n";
+ decl << "#pragma OPENCL EXTENSION cl_khr_fp64: enable\n";
+ decl << "#endif\n";
+ }
+ else if (openclwrapper::gpuEnv.mnAmdFp64Flag)
+ {
+ decl << "#pragma OPENCL EXTENSION cl_amd_fp64: enable\n";
+ }
+ // preambles
+ decl << publicFunc;
+ DK->DumpInlineFun(inlineDecl, inlineFun);
+ for (const auto& rItem : inlineDecl)
+ {
+ decl << rItem;
+ }
+
+ for (const auto& rItem : inlineFun)
+ {
+ decl << rItem;
+ }
+ mSyms.DumpSlidingWindowFunctions(decl);
+ mKernelSignature = DK->DumpOpName();
+ decl << "__kernel void DynamicKernel" << mKernelSignature;
+ decl << "(__global double *result";
+ if( !DK->IsEmpty())
+ {
+ decl << ", ";
+ DK->GenSlidingWindowDecl(decl);
+ }
+ decl << ") {\n\tint gid0 = get_global_id(0);\n\tresult[gid0] = " <<
+ DK->GenSlidingWindowDeclRef() << ";\n}\n";
+ mFullProgramSrc = decl.str();
+ SAL_INFO(
+ "sc.opencl.source",
+ (mKernelSignature[0] == '_'
+ ? mKernelSignature.substr(1, std::string::npos) : mKernelSignature)
+ << " program to be compiled:\n" << linenumberify(mFullProgramSrc));
+}
+
+std::string const & DynamicKernel::GetMD5()
+{
+ if (mKernelHash.empty())
+ {
+ outputstream md5s;
+ // Compute MD5SUM of kernel body to obtain the name
+ sal_uInt8 result[RTL_DIGEST_LENGTH_MD5];
+ rtl_digest_MD5(
+ mFullProgramSrc.c_str(),
+ mFullProgramSrc.length(), result,
+ RTL_DIGEST_LENGTH_MD5);
+ for (sal_uInt8 i : result)
+ {
+ md5s << std::hex << static_cast<int>(i);
+ }
+ mKernelHash = md5s.str();
+ }
+ return mKernelHash;
+}
+
+/// Build code
+void DynamicKernel::CreateKernel()
+{
+ if (mpKernel)
+ // already created.
+ return;
+
+ cl_int err;
+ std::string kname = "DynamicKernel" + mKernelSignature;
+ // Compile kernel here!!!
+
+ OpenCLZone zone;
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+ const char* src = mFullProgramSrc.c_str();
+ static std::string lastOneKernelHash;
+ static std::string lastSecondKernelHash;
+ static cl_program lastOneProgram = nullptr;
+ static cl_program lastSecondProgram = nullptr;
+ std::string KernelHash = mKernelSignature + GetMD5();
+ if (lastOneKernelHash == KernelHash && lastOneProgram)
+ {
+ mpProgram = lastOneProgram;
+ }
+ else if (lastSecondKernelHash == KernelHash && lastSecondProgram)
+ {
+ mpProgram = lastSecondProgram;
+ }
+ else
+ { // doesn't match the last compiled formula.
+
+ if (lastSecondProgram)
+ {
+ SAL_INFO("sc.opencl", "Releasing program " << lastSecondProgram);
+ err = clReleaseProgram(lastSecondProgram);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseProgram failed: " << openclwrapper::errorString(err));
+ lastSecondProgram = nullptr;
+ }
+ if (openclwrapper::buildProgramFromBinary("",
+ &openclwrapper::gpuEnv, KernelHash.c_str(), 0))
+ {
+ mpProgram = openclwrapper::gpuEnv.mpArryPrograms[0];
+ openclwrapper::gpuEnv.mpArryPrograms[0] = nullptr;
+ }
+ else
+ {
+ mpProgram = clCreateProgramWithSource(kEnv.mpkContext, 1,
+ &src, nullptr, &err);
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clCreateProgramWithSource", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created program " << mpProgram);
+
+ err = clBuildProgram(mpProgram, 1,
+ &openclwrapper::gpuEnv.mpDevID, "", nullptr, nullptr);
+ if (err != CL_SUCCESS)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ if (err == CL_BUILD_PROGRAM_FAILURE)
+ {
+ cl_build_status stat;
+ cl_int e = clGetProgramBuildInfo(
+ mpProgram, openclwrapper::gpuEnv.mpDevID,
+ CL_PROGRAM_BUILD_STATUS, sizeof(cl_build_status),
+ &stat, nullptr);
+ SAL_WARN_IF(
+ e != CL_SUCCESS, "sc.opencl",
+ "after CL_BUILD_PROGRAM_FAILURE,"
+ " clGetProgramBuildInfo(CL_PROGRAM_BUILD_STATUS)"
+ " fails with " << openclwrapper::errorString(e));
+ if (e == CL_SUCCESS)
+ {
+ size_t n;
+ e = clGetProgramBuildInfo(
+ mpProgram, openclwrapper::gpuEnv.mpDevID,
+ CL_PROGRAM_BUILD_LOG, 0, nullptr, &n);
+ SAL_WARN_IF(
+ e != CL_SUCCESS || n == 0, "sc.opencl",
+ "after CL_BUILD_PROGRAM_FAILURE,"
+ " clGetProgramBuildInfo(CL_PROGRAM_BUILD_LOG)"
+ " fails with " << openclwrapper::errorString(e) << ", n=" << n);
+ if (e == CL_SUCCESS && n != 0)
+ {
+ std::vector<char> log(n);
+ e = clGetProgramBuildInfo(
+ mpProgram, openclwrapper::gpuEnv.mpDevID,
+ CL_PROGRAM_BUILD_LOG, n, log.data(), nullptr);
+ SAL_WARN_IF(
+ e != CL_SUCCESS || n == 0, "sc.opencl",
+ "after CL_BUILD_PROGRAM_FAILURE,"
+ " clGetProgramBuildInfo("
+ "CL_PROGRAM_BUILD_LOG) fails with " << openclwrapper::errorString(e));
+ if (e == CL_SUCCESS)
+ SAL_WARN(
+ "sc.opencl",
+ "CL_BUILD_PROGRAM_FAILURE, status " << stat
+ << ", log \"" << log.data() << "\"");
+ }
+ }
+ }
+#endif
+#ifdef DBG_UTIL
+ SAL_WARN("sc.opencl", "Program failed to build, aborting.");
+ abort(); // make sure errors such as typos don't accidentally go unnoticed
+#else
+ throw OpenCLError("clBuildProgram", err, __FILE__, __LINE__);
+#endif
+ }
+ SAL_INFO("sc.opencl", "Built program " << mpProgram);
+
+ // Generate binary out of compiled kernel.
+ openclwrapper::generatBinFromKernelSource(mpProgram,
+ (mKernelSignature + GetMD5()).c_str());
+ }
+ lastSecondKernelHash = lastOneKernelHash;
+ lastSecondProgram = lastOneProgram;
+ lastOneKernelHash = KernelHash;
+ lastOneProgram = mpProgram;
+ }
+ mpKernel = clCreateKernel(mpProgram, kname.c_str(), &err);
+ if (err != CL_SUCCESS)
+ throw OpenCLError("clCreateKernel", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created kernel " << mpKernel << " with name " << kname << " in program " << mpProgram);
+}
+
+void DynamicKernel::Launch( size_t nr )
+{
+ OpenCLZone zone;
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+ cl_int err;
+ // The results
+ mpResClmem = clCreateBuffer(kEnv.mpkContext,
+ cl_mem_flags(CL_MEM_READ_WRITE) | CL_MEM_ALLOC_HOST_PTR,
+ nr * sizeof(double), nullptr, &err);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clCreateBuffer", err, __FILE__, __LINE__);
+ SAL_INFO("sc.opencl", "Created buffer " << mpResClmem << " size " << nr << "*" << sizeof(double) << "=" << (nr*sizeof(double)));
+
+ SAL_INFO("sc.opencl", "Kernel " << mpKernel << " arg " << 0 << ": cl_mem: " << mpResClmem << " (result)");
+ err = clSetKernelArg(mpKernel, 0, sizeof(cl_mem), static_cast<void*>(&mpResClmem));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ // The rest of buffers
+ mSyms.Marshal(mpKernel, nr, mpProgram);
+ size_t global_work_size[] = { nr };
+ SAL_INFO("sc.opencl", "Enqueuing kernel " << mpKernel);
+ err = clEnqueueNDRangeKernel(kEnv.mpkCmdQueue, mpKernel, 1, nullptr,
+ global_work_size, nullptr, 0, nullptr, nullptr);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clEnqueueNDRangeKernel", err, __FILE__, __LINE__);
+ err = clFlush(kEnv.mpkCmdQueue);
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clFlush", err, __FILE__, __LINE__);
+}
+
+// Symbol lookup. If there is no such symbol created, allocate one
+// kernel with argument with unique name and return so.
+// The template argument T must be a subclass of DynamicKernelArgument
+template <typename T>
+const DynamicKernelArgument* SymbolTable::DeclRefArg(const ScCalcConfig& config,
+ const FormulaTreeNodeRef& t,
+ std::shared_ptr<SlidingFunctionBase> pCodeGen, int nResultSize)
+{
+ FormulaToken* ref = t->GetFormulaToken();
+ ArgumentMap::iterator it = mSymbols.find(ref);
+ if (it == mSymbols.end())
+ {
+ // Allocate new symbols
+ outputstream ss;
+ ss << "tmp" << mCurId++;
+ DynamicKernelArgumentRef new_arg = std::make_shared<T>(config, ss.str(), t, std::move(pCodeGen), nResultSize);
+ mSymbols[ref] = new_arg;
+ mParams.push_back(new_arg);
+ return new_arg.get();
+ }
+ else
+ {
+ return it->second.get();
+ }
+}
+
+FormulaGroupInterpreterOpenCL::FormulaGroupInterpreterOpenCL() {}
+
+FormulaGroupInterpreterOpenCL::~FormulaGroupInterpreterOpenCL() {}
+
+ScMatrixRef FormulaGroupInterpreterOpenCL::inverseMatrix( const ScMatrix& )
+{
+ return nullptr;
+}
+
+std::shared_ptr<DynamicKernel> DynamicKernel::create( const ScCalcConfig& rConfig, const ScTokenArray& rCode, int nResultSize )
+{
+ // Constructing "AST"
+ FormulaTokenIterator aCode(rCode);
+ std::vector<FormulaToken*> aTokenVector;
+ std::map<FormulaToken*, FormulaTreeNodeRef> aHashMap;
+ FormulaToken* pCur;
+ while ((pCur = const_cast<FormulaToken*>(aCode.Next())) != nullptr)
+ {
+ OpCode eOp = pCur->GetOpCode();
+ if (eOp != ocPush)
+ {
+ FormulaTreeNodeRef pCurNode = std::make_shared<FormulaTreeNode>(pCur);
+ sal_uInt8 nParamCount = pCur->GetParamCount();
+ for (sal_uInt8 i = 0; i < nParamCount; i++)
+ {
+ if( aTokenVector.empty())
+ return nullptr;
+ FormulaToken* pTempFormula = aTokenVector.back();
+ aTokenVector.pop_back();
+ if (pTempFormula->GetOpCode() != ocPush)
+ {
+ if (aHashMap.find(pTempFormula) == aHashMap.end())
+ return nullptr;
+ pCurNode->Children.push_back(aHashMap[pTempFormula]);
+ }
+ else
+ {
+ FormulaTreeNodeRef pChildTreeNode =
+ std::make_shared<FormulaTreeNode>(pTempFormula);
+ pCurNode->Children.push_back(pChildTreeNode);
+ }
+ }
+ std::reverse(pCurNode->Children.begin(), pCurNode->Children.end());
+ aHashMap[pCur] = pCurNode;
+ }
+ aTokenVector.push_back(pCur);
+ }
+
+ FormulaTreeNodeRef Root = std::make_shared<FormulaTreeNode>(nullptr);
+ Root->Children.push_back(aHashMap[aTokenVector.back()]);
+
+ auto pDynamicKernel = std::make_shared<DynamicKernel>(rConfig, Root, nResultSize);
+
+ // OpenCL source code generation and kernel compilation
+ try
+ {
+ pDynamicKernel->CodeGen();
+ pDynamicKernel->CreateKernel();
+ }
+ catch (const UnhandledToken& ut)
+ {
+ SAL_INFO("sc.opencl", "Dynamic formula compiler: UnhandledToken: " << ut.mMessage << " at " << ut.mFile << ":" << ut.mLineNumber);
+ return nullptr;
+ }
+ catch (const InvalidParameterCount& ipc)
+ {
+ SAL_INFO("sc.opencl", "Dynamic formula compiler: InvalidParameterCount " << ipc.mParameterCount
+ << " at " << ipc.mFile << ":" << ipc.mLineNumber);
+ return nullptr;
+ }
+ catch (const OpenCLError& oce)
+ {
+ // I think OpenCLError exceptions are actually exceptional (unexpected), so do use SAL_WARN
+ // here.
+ SAL_WARN("sc.opencl", "Dynamic formula compiler: OpenCLError from " << oce.mFunction << ": " << openclwrapper::errorString(oce.mError) << " at " << oce.mFile << ":" << oce.mLineNumber);
+
+ // OpenCLError used to go to the catch-all below, and not delete pDynamicKernel. Was that
+ // intentional, should we not do it here then either?
+ openclwrapper::kernelFailures++;
+ return nullptr;
+ }
+ catch (const Unhandled& uh)
+ {
+ SAL_INFO("sc.opencl", "Dynamic formula compiler: Unhandled at " << uh.mFile << ":" << uh.mLineNumber);
+
+ // Unhandled used to go to the catch-all below, and not delete pDynamicKernel. Was that
+ // intentional, should we not do it here then either?
+ openclwrapper::kernelFailures++;
+ return nullptr;
+ }
+ catch (...)
+ {
+ // FIXME: Do we really want to catch random exceptions here?
+ SAL_WARN("sc.opencl", "Dynamic formula compiler: unexpected exception");
+ openclwrapper::kernelFailures++;
+ return nullptr;
+ }
+ return pDynamicKernel;
+}
+
+namespace {
+
+class CLInterpreterResult
+{
+ DynamicKernel* mpKernel;
+
+ SCROW mnGroupLength;
+
+ cl_mem mpCLResBuf;
+ double* mpResBuf;
+
+public:
+ CLInterpreterResult() : mpKernel(nullptr), mnGroupLength(0), mpCLResBuf(nullptr), mpResBuf(nullptr) {}
+ CLInterpreterResult( DynamicKernel* pKernel, SCROW nGroupLength ) :
+ mpKernel(pKernel), mnGroupLength(nGroupLength), mpCLResBuf(nullptr), mpResBuf(nullptr) {}
+
+ bool isValid() const { return mpKernel != nullptr; }
+
+ void fetchResultFromKernel()
+ {
+ if (!isValid())
+ return;
+
+ OpenCLZone zone;
+
+ // Map results back
+ mpCLResBuf = mpKernel->GetResultBuffer();
+
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+
+ cl_int err;
+ mpResBuf = static_cast<double*>(clEnqueueMapBuffer(kEnv.mpkCmdQueue,
+ mpCLResBuf,
+ CL_TRUE, CL_MAP_READ, 0,
+ mnGroupLength * sizeof(double), 0, nullptr, nullptr,
+ &err));
+
+ if (err != CL_SUCCESS)
+ {
+ SAL_WARN("sc.opencl", "clEnqueueMapBuffer failed:: " << openclwrapper::errorString(err));
+ mpResBuf = nullptr;
+ return;
+ }
+ SAL_INFO("sc.opencl", "Kernel results: cl_mem: " << mpResBuf << " (" << DebugPeekDoubles(mpResBuf, mnGroupLength) << ")");
+ }
+
+ bool pushResultToDocument( ScDocument& rDoc, const ScAddress& rTopPos )
+ {
+ if (!mpResBuf)
+ return false;
+
+ OpenCLZone zone;
+
+ rDoc.SetFormulaResults(rTopPos, mpResBuf, mnGroupLength);
+
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+
+ cl_int err;
+ err = clEnqueueUnmapMemObject(kEnv.mpkCmdQueue, mpCLResBuf, mpResBuf, 0, nullptr, nullptr);
+
+ if (err != CL_SUCCESS)
+ {
+ SAL_WARN("sc.opencl", "clEnqueueUnmapMemObject failed: " << openclwrapper::errorString(err));
+ return false;
+ }
+
+ return true;
+ }
+};
+
+class CLInterpreterContext
+{
+ std::shared_ptr<DynamicKernel> mpKernelStore; /// for managed kernel instance.
+ DynamicKernel* mpKernel;
+
+ SCROW mnGroupLength;
+
+public:
+ explicit CLInterpreterContext(SCROW nGroupLength, std::shared_ptr<DynamicKernel> pKernel )
+ : mpKernelStore(std::move(pKernel))
+ , mpKernel(mpKernelStore.get())
+ , mnGroupLength(nGroupLength) {}
+
+ ~CLInterpreterContext()
+ {
+ DynamicKernelArgument::ClearStringIds();
+ }
+
+ bool isValid() const
+ {
+ return mpKernel != nullptr;
+ }
+
+ CLInterpreterResult launchKernel()
+ {
+ if (!isValid())
+ return CLInterpreterResult();
+
+ try
+ {
+ // Run the kernel.
+ mpKernel->Launch(mnGroupLength);
+ }
+ catch (const UnhandledToken& ut)
+ {
+ SAL_INFO("sc.opencl", "Dynamic formula compiler: UnhandledToken: " << ut.mMessage << " at " << ut.mFile << ":" << ut.mLineNumber);
+ openclwrapper::kernelFailures++;
+ return CLInterpreterResult();
+ }
+ catch (const OpenCLError& oce)
+ {
+ SAL_WARN("sc.opencl", "Dynamic formula compiler: OpenCLError from " << oce.mFunction << ": " << openclwrapper::errorString(oce.mError) << " at " << oce.mFile << ":" << oce.mLineNumber);
+ openclwrapper::kernelFailures++;
+ return CLInterpreterResult();
+ }
+ catch (const Unhandled& uh)
+ {
+ SAL_INFO("sc.opencl", "Dynamic formula compiler: Unhandled at " << uh.mFile << ":" << uh.mLineNumber);
+ openclwrapper::kernelFailures++;
+ return CLInterpreterResult();
+ }
+ catch (...)
+ {
+ SAL_WARN("sc.opencl", "Dynamic formula compiler: unexpected exception");
+ openclwrapper::kernelFailures++;
+ return CLInterpreterResult();
+ }
+
+ return CLInterpreterResult(mpKernel, mnGroupLength);
+ }
+};
+
+
+CLInterpreterContext createCLInterpreterContext( const ScCalcConfig& rConfig,
+ const ScFormulaCellGroupRef& xGroup, const ScTokenArray& rCode )
+{
+ return CLInterpreterContext(xGroup->mnLength, DynamicKernel::create(rConfig, rCode, xGroup->mnLength));
+}
+
+void genRPNTokens( ScDocument& rDoc, const ScAddress& rTopPos, ScTokenArray& rCode )
+{
+ ScCompiler aComp(rDoc, rTopPos, rCode, rDoc.GetGrammar());
+ // Disable special ordering for jump commands for the OpenCL interpreter.
+ aComp.EnableJumpCommandReorder(false);
+ aComp.CompileTokenArray(); // Regenerate RPN tokens.
+}
+
+bool waitForResults()
+{
+ OpenCLZone zone;
+ openclwrapper::KernelEnv kEnv;
+ openclwrapper::setKernelEnv(&kEnv);
+
+ cl_int err = clFinish(kEnv.mpkCmdQueue);
+ if (err != CL_SUCCESS)
+ SAL_WARN("sc.opencl", "clFinish failed: " << openclwrapper::errorString(err));
+
+ return err == CL_SUCCESS;
+}
+
+}
+
+bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
+ const ScAddress& rTopPos, ScFormulaCellGroupRef& xGroup,
+ ScTokenArray& rCode )
+{
+ SAL_INFO("sc.opencl", "Interpret cell group " << rTopPos);
+ MergeCalcConfig(rDoc);
+
+ genRPNTokens(rDoc, rTopPos, rCode);
+
+ if( rCode.GetCodeLen() == 0 )
+ return false;
+
+ CLInterpreterContext aCxt = createCLInterpreterContext(maCalcConfig, xGroup, rCode);
+ if (!aCxt.isValid())
+ return false;
+
+ CLInterpreterResult aRes = aCxt.launchKernel();
+ if (!aRes.isValid())
+ return false;
+
+ if (!waitForResults())
+ return false;
+
+ aRes.fetchResultFromKernel();
+
+ return aRes.pushResultToDocument(rDoc, rTopPos);
+}
+
+} // namespace sc::opencl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_addin.cxx b/sc/source/core/opencl/op_addin.cxx
new file mode 100644
index 0000000000..602ed2dfb3
--- /dev/null
+++ b/sc/source/core/opencl/op_addin.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/.
+ */
+
+#include "op_addin.hxx"
+
+#include <formula/vectortoken.hxx>
+#include <sstream>
+
+using namespace formula;
+
+namespace sc::opencl {
+
+void OpBesselj::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "N", 1, vSubArguments, ss );
+ ss << " double f_2_DIV_PI = 2.0 / M_PI;\n";
+ ss << " if( N < 0.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if (x == 0.0)\n";
+ ss << " return (N == 0.0) ? 1.0 : 0.0;\n";
+ ss << " double fSign = ((int)N % 2 == 1 && x < 0.0) ? -1.0 : 1.0;\n";
+ ss << " double fX = fabs(x);\n";
+ ss << " double fMaxIteration = 9000000.0;\n";
+ ss << " double fEstimateIteration = fX * 1.5 + N;\n";
+ ss << " bool bAsymptoticPossible = pow(fX,0.4) > N;\n";
+ ss << " if (fEstimateIteration > fMaxIteration)\n";
+ ss << " {\n";
+ ss << " if (bAsymptoticPossible)\n";
+ ss << " return fSign * sqrt(f_2_DIV_PI/fX)";
+ ss << "* cos(fX-N*M_PI_2-M_PI_4);\n";
+ ss << " else\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << " }\n";
+ ss << " double epsilon = 1.0e-15;\n";
+ ss << " bool bHasfound = false;\n";
+ ss << " double k= 0.0;\n";
+ ss << " double u ;\n";
+ ss << " double m_bar;\n";
+ ss << " double g_bar;\n";
+ ss << " double g_bar_delta_u;\n";
+ ss << " double g = 0.0;\n";
+ ss << " double delta_u = 0.0;\n";
+ ss << " double f_bar = -1.0;\n";
+ ss << " if (N==0)\n";
+ ss << " {\n";
+ ss << " u = 1.0;\n";
+ ss << " g_bar_delta_u = 0.0;\n";
+ ss << " g_bar = - 2.0/fX; \n";
+ ss << " delta_u = g_bar_delta_u / g_bar;\n";
+ ss << " u = u + delta_u ;\n";
+ ss << " g = -1.0 / g_bar; \n";
+ ss << " f_bar = f_bar * g;\n";
+ ss << " k = 2.0;\n";
+ ss << " }\n";
+ ss << " if (N!=0)\n";
+ ss << " {\n";
+ ss << " u=0.0;\n";
+ ss << " for (k =1.0; k<= N-1; k = k + 1.0)\n";
+ ss << " {\n";
+ ss << " m_bar=2.0 * fmod(k-1.0, 2.0) * f_bar;\n";
+ ss << " g_bar_delta_u = - g * delta_u - m_bar * u;\n";
+ ss << " g_bar = m_bar - 2.0*k/fX + g;\n";
+ ss << " delta_u = g_bar_delta_u / g_bar;\n";
+ ss << " u = u + delta_u;\n";
+ ss << " g = -1.0/g_bar;\n";
+ ss << " f_bar=f_bar * g;\n";
+ ss << " }\n";
+ ss << " m_bar=2.0 * fmod(k-1.0, 2.0) * f_bar;\n";
+ ss << " g_bar_delta_u = f_bar - g * delta_u - m_bar * u;\n";
+ ss << " g_bar = m_bar - 2.0*k/fX + g;\n";
+ ss << " delta_u = g_bar_delta_u / g_bar;\n";
+ ss << " u = u + delta_u;\n";
+ ss << " g = -1.0/g_bar;\n";
+ ss << " f_bar = f_bar * g;\n";
+ ss << " k = k + 1.0;\n";
+ ss << " }\n";
+ ss << " do\n";
+ ss << " {\n";
+ ss << " m_bar = 2.0 * fmod(k-1.0, 2.0) * f_bar;\n";
+ ss << " g_bar_delta_u = - g * delta_u - m_bar * u;\n";
+ ss << " g_bar = m_bar - 2.0*k/fX + g;\n";
+ ss << " delta_u = g_bar_delta_u / g_bar;\n";
+ ss << " u = u + delta_u;\n";
+ ss << " g = -pow(g_bar,-1.0);\n";
+ ss << " f_bar = f_bar * g;\n";
+ ss << " bHasfound = (fabs(delta_u)<=fabs(u)*epsilon);\n";
+ ss << " k = k + 1.0;\n";
+ ss << " }\n";
+ ss << " while (!bHasfound && k <= fMaxIteration);\n";
+ ss << " if (bHasfound)\n";
+ ss << " return u * fSign;\n";
+ ss << " else\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << "}";
+}
+void OpGestep::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp=0;\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss <<"\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ ss << " tmp =tmp0 >= tmp1 ? 1 : 0;\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_addin.hxx b/sc/source/core/opencl/op_addin.hxx
new file mode 100644
index 0000000000..5a5799377c
--- /dev/null
+++ b/sc/source/core/opencl/op_addin.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+class OpBesselj: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Besselj"; }
+};
+class OpGestep: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Gestep"; }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_array.cxx b/sc/source/core/opencl/op_array.cxx
new file mode 100644
index 0000000000..47ae152185
--- /dev/null
+++ b/sc/source/core/opencl/op_array.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/.
+ */
+
+#include "op_array.hxx"
+
+#include <formula/vectortoken.hxx>
+#include <sstream>
+
+using namespace formula;
+
+namespace sc::opencl {
+
+void OpSumX2MY2::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp =0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, EmptyIsZero,
+ " tmp +=pow(arg1,2) - pow(arg2,2);\n"
+ );
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpSumX2PY2::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp =0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, EmptyIsZero,
+ " tmp +=pow(arg1,2) + pow(arg2,2);\n"
+ );
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpSumXMY2::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp =0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, EmptyIsZero,
+ " tmp +=pow((arg1-arg2),2);\n"
+ );
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_array.hxx b/sc/source/core/opencl/op_array.hxx
new file mode 100644
index 0000000000..20ee011887
--- /dev/null
+++ b/sc/source/core/opencl/op_array.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+class OpSumX2MY2: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SumX2MY2"; }
+};
+
+class OpSumX2PY2: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SumX2PY2"; }
+};
+
+class OpSumXMY2: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SumXMY2"; }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_financial.cxx b/sc/source/core/opencl/op_financial.cxx
new file mode 100644
index 0000000000..6ab2874dc7
--- /dev/null
+++ b/sc/source/core/opencl/op_financial.cxx
@@ -0,0 +1,1845 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "op_financial.hxx"
+
+#include <formula/vectortoken.hxx>
+#include <sstream>
+
+using namespace formula;
+
+namespace sc::opencl {
+// Definitions of inline functions
+#include "op_financial_helpers.hxx"
+#include "op_math_helpers.hxx"
+
+void OpRRI::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "nper", 0, vSubArguments, ss );
+ GenerateArg( "pv", 1, vSubArguments, ss );
+ GenerateArg( "fv", 2, vSubArguments, ss );
+ ss << " if ( nper <= 0.0 || pv == 0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " tmp = pow(fv/pv,1.0/nper)-1;\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpNominal::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ ss << "double tmp = 0;\n\t";
+ ss<<"if(tmp1==0)\n\t";
+ ss<<"\treturn 0;\n\t";
+ ss<<"tmp= 1.0 / tmp1;\n\t";
+ ss<<"tmp=( pow( tmp0+ 1.0, tmp ) - 1.0 ) *";
+ ss<<"tmp1;\n\t";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpDollarde::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = " << GetBottom() <<";\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ ss << "double fInt = " << GetBottom() <<";\n\t";
+ GenerateArg( "dollar", 0, vSubArguments, ss );
+ GenerateArg( "fFrac", 1, vSubArguments, ss );
+ ss <<"fFrac = (int)fFrac;\n\t";
+ ss << "tmp = modf( dollar , &fInt );\n\t";
+ ss << "tmp /= fFrac;\n\t";
+ ss << "tmp *= pow( 10.0 , ceil( log10(fFrac ) ) );\n\t";
+ ss << "tmp += fInt;\t";
+ ss << "\n\treturn tmp;\n";
+ ss << "}";
+}
+
+void OpDollarfr::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = " << GetBottom() <<";\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ ss << "double fInt = " << GetBottom() <<";\n\t";
+ GenerateArg( "dollar", 0, vSubArguments, ss );
+ GenerateArg( "fFrac", 1, vSubArguments, ss );
+ ss <<"fFrac = (int)fFrac;\n\t";
+ ss << "tmp = modf( dollar , &fInt );\n\t";
+ ss << "tmp *= fFrac;\n\t";
+ ss << "tmp *= pow( 10.0 , -ceil( log10(fFrac ) ) );\n\t";
+ ss << "tmp += fInt;\t";
+ ss << "\n\treturn tmp;\n";
+ ss << "}";
+}
+
+void OpDISC::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearFracDecl);decls.insert(DaysToDateDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+ funs.insert(GetYearFrac);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+}
+
+void OpDISC::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 5, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ GenerateArg( 4, vSubArguments, ss );
+ ss << " int nNullDate = 693594;\n";
+ ss << " tmp = 1.0 - arg2 / arg3;\n";
+ ss << " tmp /=";
+ ss << " GetYearFrac(nNullDate, (int)arg0, (int)arg1, (int)arg4);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpINTRATE::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearDiffDecl);decls.insert(GetDiffDateDecl);
+ decls.insert(DaysToDateDecl);decls.insert(GetNullDateDecl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(IsLeapYearDecl);
+ funs.insert(GetYearDiff);funs.insert(GetDiffDate);
+ funs.insert(DaysToDate);funs.insert(GetNullDate);
+ funs.insert(DateToDays);funs.insert(DaysInMonth);
+ funs.insert(IsLeapYear);
+}
+
+void OpINTRATE::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 5, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ GenerateArg( 4, vSubArguments, ss );
+ ss << " int nNullDate = GetNullDate();\n";
+ ss << " tmp = ((arg3 / arg2) - 1) / GetYearDiff(nNullDate, (int)arg0,";
+ ss << " (int)arg1,(int)arg4);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpFV::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetFVDecl);
+ funs.insert(GetFV);
+}
+
+void OpFV::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 5, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ GenerateArg( 4, vSubArguments, ss );
+ ss << " tmp = GetFV(arg0, arg1, arg2, arg3, arg4);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpIPMT::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetFVDecl);
+ funs.insert(GetFV);
+ decls.insert(GetPMTDecl);
+ funs.insert(GetPMT);
+ decls.insert(GetIpmtDecl);
+ funs.insert(GetIpmt);
+}
+
+void OpIPMT::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fPer", 1, vSubArguments, ss );
+ GenerateArg( "fNper", 2, vSubArguments, ss );
+ GenerateArg( "fPv", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "fFv", 4, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fPayInAdvance", 5, 0, vSubArguments, ss );
+ ss << " if (fPer < 1.0 || fPer > fNper)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fPmt;\n";
+ ss << " return GetIpmt(fRate, fPer, fNper, fPv, fFv, fPayInAdvance != 0, &fPmt);\n";
+ ss << " }\n";
+ ss << "}\n";
+}
+void OpISPMT::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ ss << " tmp = arg3 * arg0 * ( arg1 - arg2) / arg2;\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpPDuration::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ ss << " if ( arg0 <= 0.0 || arg1 <= 0.0 || arg2 <= 0.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " tmp = log(arg2 / arg1) / log1p(arg0);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpDuration_ADD::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetDurationDecl);decls.insert(lcl_GetcoupnumDecl);
+ decls.insert(GetYearFracDecl);decls.insert(DaysToDateDecl);
+ decls.insert(GetNullDateDecl);decls.insert(DateToDaysDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+ funs.insert(GetDuration);funs.insert(lcl_Getcoupnum);
+ funs.insert(GetYearFrac);funs.insert(DaysToDate);
+ funs.insert(GetNullDate);funs.insert(DateToDays);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+}
+
+void OpDuration_ADD::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ GenerateArg( 4, vSubArguments, ss );
+ GenerateArg( 5, vSubArguments, ss );
+ ss << " int nNullDate = GetNullDate();\n";
+ ss << " tmp = GetDuration( nNullDate, (int)arg0, (int)arg1, arg2,";
+ ss << " arg3, (int)arg4, (int)arg5);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+void OpMDuration::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetDurationDecl);decls.insert(lcl_GetcoupnumDecl);
+ decls.insert(addMonthsDecl);decls.insert(checklessthanDecl);
+ decls.insert(setDayDecl);decls.insert(ScaDateDecl);
+ decls.insert(GetYearFracDecl);decls.insert(DaysToDateDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+ funs.insert(GetDuration);funs.insert(lcl_Getcoupnum);
+ funs.insert(addMonths);funs.insert(checklessthan);
+ funs.insert(setDay);funs.insert(ScaDate);
+ funs.insert(GetYearFrac);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+}
+
+void OpMDuration::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() << ";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ GenerateArg( 4, vSubArguments, ss );
+ GenerateArg( 5, vSubArguments, ss );
+ ss << " int nNullDate = 693594;\n";
+ ss << " tmp = GetDuration( nNullDate, (int)arg0, (int)arg1, arg2,";
+ ss << " arg3, (int)arg4, (int)arg5);\n";
+ ss << " tmp = tmp * pow(1.0 + arg3 * pow((int)arg4, -1.0), -1);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+void Fvschedule::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = 1.0;\n\t";
+ ss << "int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << "\t";
+ GenerateRangeArg( 1, vSubArguments, ss, SkipEmpty,
+ " tmp *= arg + 1;\n"
+ );
+ ss << "\t";
+ ss << "return (double)tmp * arg0";
+ ss << ";\n}";
+}
+void Cumipmt::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetPMTDecl); decls.insert(GetFV);
+ funs.insert(GetPMT);funs.insert(GetFV);
+}
+void Cumipmt::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &
+vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fNumPeriouds", 1, vSubArguments, ss );
+ GenerateArg( "fVal", 2, vSubArguments, ss );
+ GenerateArg( "fStartPer", 3, vSubArguments, ss );
+ GenerateArg( "fEndPer", 4, vSubArguments, ss );
+ GenerateArg( "fPayType", 5, vSubArguments, ss );
+ ss << " int nNumPeriods = (int)fNumPeriods;\n";
+ ss << " int nStartPer = (int)fStartPer;\n";
+ ss << " int nEndPer = (int)fEndPer;\n";
+ ss << " int nPayType = (int)fPayType;\n";
+ ss <<" double fPmt;\n";
+ ss <<" fPmt = GetPMT( fRate, nNumPeriods, fVal, 0.0, nPayType != 0 );\n";
+ ss <<" double tmp = 0.0;\n";
+ ss <<" if( nStartPer == 1 )\n";
+ ss <<" {\n";
+ ss <<" if( nPayType <= 0 )\n";
+ ss <<" tmp = -fVal;\n";
+ ss <<" nStartPer++;\n";
+ ss <<" }\n";
+ ss <<" for( ; nStartPer<= nEndPer ; nStartPer++ )\n";
+ ss <<" {\n";
+ ss <<" if( nPayType > 0 )\n";
+ ss <<" tmp += GetFV( fRate, nStartPer - 2 , ";
+ ss <<"fPmt, fVal, 1 ) - fPmt;\n";
+ ss <<" else\n";
+ ss <<" tmp += GetFV( fRate, nStartPer - 1 , ";
+ ss <<"fPmt, fVal, 0 );\n";
+ ss <<" }\n";
+ ss <<" tmp *= fRate;\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+
+void OpIRR::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " #define Epsilon 1.0E-7\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArgWithDefault( "fEstimated", 1, 0.1, vSubArguments, ss );
+ ss << " double fEps = 1.0;\n";
+ ss << " double xNew = 0.0, fNumerator = 0.0, fDenominator = 0.0;\n";
+ ss << " double nCount = 0.0;\n";
+ ss << " unsigned short nItCount = 0;\n";
+ ss << " double x = fEstimated;\n";
+ ss << " while (fEps > Epsilon && nItCount < 20)\n";
+ ss << " {\n";
+ ss << " nCount = 0.0; fNumerator = 0.0; fDenominator = 0.0;\n";
+ GenerateRangeArg( 0, vSubArguments, ss, SkipEmpty,
+ " fNumerator += arg / pow(1.0 + x, nCount);\n"
+ " fDenominator+=-1*nCount*arg/pow(1.0+x,nCount+1.0);\n"
+ " nCount += 1;\n"
+ );
+ ss << " xNew = x - fNumerator / fDenominator;\n";
+ ss << " fEps = fabs(xNew - x);\n";
+ ss << " x = xNew;\n";
+ ss << " nItCount++;\n";
+ ss << " }\n";
+ ss << " if (fEstimated == 0.0 && fabs(x) < Epsilon)\n";
+ ss << " x = 0.0;\n";
+ ss << " if (fEps < Epsilon)\n";
+ ss << " return x;\n";
+ ss << " else\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << "}\n";
+}
+
+void XNPV::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double result = 0.0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "rate", 0, vSubArguments, ss );
+ GenerateRangeArgElement( "dateNull", 2, "0", vSubArguments, ss, EmptyIsZero );
+ GenerateRangeArgPair( 1, 2, vSubArguments, ss, SkipEmpty,
+ " result += arg1/(pow((rate+1),(arg2-dateNull)/365));\n"
+ );
+ ss << " return result;\n";
+ ss << "}";
+}
+
+void PriceMat::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearFracDecl);decls.insert(GetNullDateDecl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysToDateDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+
+ funs.insert(GetYearFrac);funs.insert(GetNullDate);
+ funs.insert(DateToDays);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+}
+void PriceMat::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 5, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ ss << "double result=0;\n\t";
+ ss<< "int nNullDate = GetNullDate( );\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fIssue", 2, vSubArguments, ss );
+ GenerateArg( "rate", 3, vSubArguments, ss );
+ GenerateArg( "yield", 4, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 5, 0, vSubArguments, ss );
+ ss << "\t";
+ ss <<"int settle = fSettle;\n\t";
+ ss <<"int mat = fMat;\n\t";
+ ss <<"int issue = fIssue;\n\t";
+ ss <<"int nBase = fBase;\n\t";
+ ss<< "double fIssMat = GetYearFrac( nNullDate, issue, mat, nBase);\n";
+ ss<<"double fIssSet = GetYearFrac( nNullDate, issue, settle,nBase);\n";
+ ss<<"double fSetMat = GetYearFrac( nNullDate, settle, mat, nBase);\n";
+ ss<<"result = 1.0 + fIssMat * rate;\n\t";
+ ss<<"result /= 1.0 + fSetMat * yield;\n\t";
+ ss<<"result -= fIssSet * rate;\n\t";
+ ss<<"result*= 100.0;\n\t";
+ ss<<"return result;\n\t";
+ ss<<"}\n";
+}
+
+void OpSYD::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "cost", 0, vSubArguments, ss );
+ GenerateArg( "salvage", 1, vSubArguments, ss );
+ GenerateArg( "life", 2, vSubArguments, ss );
+ GenerateArg( "period", 3, vSubArguments, ss );
+ ss << " double result=0;\n";
+ ss <<" double tmpvalue = ((life*(life+1))/2.0);\n";
+ ss <<" result = ((cost-salvage)*(life-period+1)/tmpvalue);\n";
+ ss <<" return result;\n";
+ ss <<"}\n";
+}
+
+void OpMIRR::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp;\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ ss << "double invest = arg1 + 1.0;\n\t";
+ ss << "double reinvest = arg2 + 1.0;\n\t";
+ ss << "double NPV_invest = 0.0;\n\t";
+ ss << "double Pow_invest = 1.0;\n\t";
+ ss << "double NPV_reinvest = 0.0;\n\t";
+ ss << "double Pow_reinvest = 1.0;\n\t";
+ ss << "int nCount = 0;\n\t";
+ ss << "bool bHasPosValue = false;\n";
+ ss << "bool bHasNegValue = false;\n";
+ GenerateRangeArg( 0, vSubArguments, ss, SkipEmpty,
+ " if (arg > 0.0)\n"
+ " {\n"
+ " NPV_reinvest += arg * Pow_reinvest;\n"
+ " bHasPosValue = true;\n"
+ " }\n"
+ " else if (arg < 0.0)\n"
+ " {\n"
+ " NPV_invest += arg * Pow_invest;\n"
+ " bHasNegValue = true;\n"
+ " }\n"
+ " Pow_reinvest /= reinvest;\n"
+ " Pow_invest /= invest;\n"
+ " nCount++;\n"
+ );
+ ss << "if ( !( bHasPosValue && bHasNegValue ) )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << "tmp = ";
+ ss << "-NPV_reinvest /NPV_invest * pow(reinvest,(double)nCount-1);\n\t";
+ ss << "tmp = pow(tmp, 1.0 / (nCount - 1)) - 1.0;\n\t";
+ ss << "return (double)tmp;\n";
+ ss << "}";
+}
+
+void OpEffective::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() <<";\n";
+ ss << " int gid0 = get_global_id(0);\n\t";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ ss << " tmp = pow(1.0 + arg0 / arg1, arg1)-1.0;\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+ void OpTbilleq::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetDiffDate360_Decl);decls.insert(GetDiffDate360Decl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysToDate_LocalBarrierDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(GetNullDateDecl);
+ decls.insert(IsLeapYearDecl);
+ funs.insert(GetDiffDate360_);funs.insert(GetDiffDate360);
+ funs.insert(DateToDays);funs.insert(DaysToDate_LocalBarrier);
+ funs.insert(DaysInMonth);funs.insert(GetNullDate);
+ funs.insert(IsLeapYear);
+}
+void OpTbilleq::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << "double tmp = 0;\n\t";
+ GenerateArg( "tmp000", 0, vSubArguments, ss );
+ GenerateArg( "tmp001", 1, vSubArguments, ss );
+ GenerateArg( "tmp002", 2, vSubArguments, ss );
+ ss<<"tmp001+=1.0;\n";
+ ss<<"int nDiff =GetDiffDate360(GetNullDate(),tmp000,tmp001,true);\n";
+ ss<<"tmp =( 365 * tmp002 ) / ( 360 - ( tmp002 * ( nDiff ) ) );\n";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+void OpCumprinc::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetPMTDecl); decls.insert(GetFVDecl);
+ funs.insert(GetPMT);funs.insert(GetFV);
+}
+void OpCumprinc::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() <<";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fNumPeriouds", 1, vSubArguments, ss );
+ GenerateArg( "fVal", 2, vSubArguments, ss );
+ GenerateArg( "fStartPer", 3, vSubArguments, ss );
+ GenerateArg( "fEndPer", 4, vSubArguments, ss );
+ GenerateArg( "fPayType", 5, vSubArguments, ss );
+ ss << " int nNumPeriods = (int)fNumPeriods;\n";
+ ss << " int nStartPer = (int)fStartPer;\n";
+ ss << " int nEndPer = (int)fEndPer;\n";
+ ss << " int nPayType = (int)fPayType;\n";
+ ss <<" double fPmt;\n";
+ ss <<" fPmt = GetPMT( fRate, nNumPeriods,fVal,0.0,nPayType != 0 );\n";
+ ss <<" if(nStartPer == 1)\n";
+ ss <<" {\n";
+ ss <<" if( nPayType <= 0 )\n";
+ ss <<" tmp = fPmt + fVal * fRate;\n";
+ ss <<" else\n";
+ ss <<" tmp = fPmt;\n";
+ ss <<" nStartPer=nStartPer+1;\n";
+ ss <<" }\n";
+ ss <<" for( int i = nStartPer ; i <= nEndPer ; i++ )\n";
+ ss <<" {\n";
+ ss <<" if( nPayType > 0 )\n";
+ ss <<" tmp += fPmt - ( GetFV( fRate,i - 2,";
+ ss <<"fPmt,fVal,1)- fPmt ) * fRate;\n";
+ ss <<" else\n";
+ ss <<" tmp += fPmt - GetFV( fRate, i - 1,";
+ ss <<"fPmt,fVal,0 ) * fRate;\n";
+ ss <<" }\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpAccrint::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl); decls.insert(GetDiffDateDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);funs.insert(GetDiffDate);
+}
+void OpAccrint::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 7, 7 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double tmp = 0;\n";
+ GenerateArg( "fStartDate", 0, vSubArguments, ss );
+ // 1 is ignored
+ GenerateArg( "fEndDate", 2, vSubArguments, ss );
+ GenerateArg( "fRate", 3, vSubArguments, ss );
+ GenerateArg( "fVal", 4, vSubArguments, ss );
+ GenerateArg( "fFreq", 5, vSubArguments, ss );
+ GenerateArg( "fMode", 6, vSubArguments, ss );
+ ss << " int nStartDate = fStartDate;\n";
+ ss << " int nEndDate = fEndDate;\n";
+ ss << " int mode = fMode;\n";
+ ss << " int freq = fFreq;\n";
+ ss << " int nDays1stYear=0;\n";
+ ss <<" int nNullDate=GetNullDate();\n";
+ ss <<" int nTotalDays = GetDiffDate(nNullDate,nStartDate,";
+ ss <<"nEndDate, mode,&nDays1stYear);\n";
+ ss <<" tmp = fVal*fRate*convert_double(nTotalDays)";
+ ss <<"/convert_double(nDays1stYear);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+
+void OpAccrintm::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl); decls.insert(GetDiffDateDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);funs.insert(GetDiffDate);
+}
+void OpAccrintm::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 5, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ ss << "double tmp = " << GetBottom() <<";\n\t";
+
+ GenerateArg( "fStartDate", 0, vSubArguments, ss );
+ GenerateArg( "fEndDate", 1, vSubArguments, ss );
+ GenerateArg( "fRate", 2, vSubArguments, ss );
+ GenerateArg( "fVal", 3, vSubArguments, ss );
+ GenerateArg( "fMode", 4, vSubArguments, ss );
+ ss << " int nStartDate = fStartDate;\n";
+ ss << " int nEndDate = fEndDate;\n";
+ ss << " int mode = fMode;\n";
+ ss <<"int nDays1stYear=0;\n\t";
+ ss <<"int nNullDate=GetNullDate();\n\t";
+ ss <<"int nTotalDays = GetDiffDate(nNullDate,nStartDate,";
+ ss <<"nEndDate, mode,&nDays1stYear);\n\t";
+ ss <<"tmp = fVal*fRate*convert_double(nTotalDays)";
+ ss <<"/convert_double(nDays1stYear);\n\t";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpYield::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(getYield_Decl);decls.insert(getPriceDecl);
+ decls.insert(coupnumDecl);decls.insert(coupdaysncDecl);
+ decls.insert(coupdaybsDecl);decls.insert(coupdaysDecl);
+ decls.insert(lcl_GetcoupnumDecl);decls.insert(lcl_GetcoupdaysDecl);
+ decls.insert(lcl_GetcoupdaybsDecl);decls.insert(getDiffDecl);
+ decls.insert(getDaysInYearRangeDecl);decls.insert(GetDaysInYearDecl);
+ decls.insert(GetDaysInYearsDecl);decls.insert(getDaysInMonthRangeDecl);
+ decls.insert(addMonthsDecl);decls.insert(ScaDateDecl);
+ decls.insert(GetNullDateDecl);decls.insert(DateToDaysDecl);
+ decls.insert(DaysToDateDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(IsLeapYearDecl);decls.insert(setDayDecl);
+ decls.insert(checklessthanDecl);
+
+ funs.insert(getYield_);funs.insert(getPrice);
+ funs.insert(coupnum);funs.insert(coupdaysnc);
+ funs.insert(coupdaybs);funs.insert(coupdays);
+ funs.insert(lcl_Getcoupnum);funs.insert(lcl_Getcoupdays);
+ funs.insert(lcl_Getcoupdaybs);funs.insert(getDiff);
+ funs.insert(getDaysInYearRange);funs.insert(GetDaysInYear);
+ funs.insert(GetDaysInYears);funs.insert(getDaysInMonthRange);
+ funs.insert(addMonths);funs.insert(ScaDate);
+ funs.insert(GetNullDate);funs.insert(DateToDays);
+ funs.insert(DaysToDate);funs.insert(DaysInMonth);
+ funs.insert(IsLeapYear);decls.insert(setDay);
+ funs.insert(checklessthan);
+}
+
+void OpYield::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 7, 7 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = 0;\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ GenerateArg( "tmp000", 0, vSubArguments, ss );
+ GenerateArg( "tmp001", 1, vSubArguments, ss );
+ GenerateArg( "tmp002", 2, vSubArguments, ss );
+ GenerateArg( "tmp003", 3, vSubArguments, ss );
+ GenerateArg( "tmp004", 4, vSubArguments, ss );
+ GenerateArg( "tmp005", 5, vSubArguments, ss );
+ GenerateArg( "tmp006", 6, vSubArguments, ss );
+ ss << "tmp = getYield_(";
+ ss << "GetNullDate(),tmp000,tmp001,tmp002,tmp003,tmp004,tmp005,tmp006);\n\t ";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpSLN::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "cost", 0, vSubArguments, ss );
+ GenerateArg( "salvage", 1, vSubArguments, ss );
+ GenerateArg( "life", 2, vSubArguments, ss );
+ ss << " tmp = (cost-salvage)/life;\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+ void OpYieldmat::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearFracDecl);decls.insert(GetNullDateDecl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysToDateDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+ decls.insert(GetYieldmatDecl);
+
+ funs.insert(GetYearFrac);funs.insert(GetNullDate);
+ funs.insert(DateToDays);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+ funs.insert(GetYieldmat);
+}
+
+void OpYieldmat::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = 0;\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ GenerateArg( "tmp000", 0, vSubArguments, ss );
+ GenerateArg( "tmp001", 1, vSubArguments, ss );
+ GenerateArg( "tmp002", 2, vSubArguments, ss );
+ GenerateArg( "tmp003", 3, vSubArguments, ss );
+ GenerateArg( "tmp004", 4, vSubArguments, ss );
+ GenerateArg( "tmp005", 5, vSubArguments, ss );
+ ss << "tmp = GetYieldmat(";
+ ss<<"GetNullDate(),tmp000,tmp001,tmp002,tmp003,tmp004,tmp005);\n\t";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpPMT::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetPMTDecl);
+ funs.insert(GetPMT);
+}
+
+void OpPMT::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss<< " double tmp = 0;\n";
+ ss<< " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fNper", 1, vSubArguments, ss );
+ GenerateArg( "fPv", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fFv", 3, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fPayInAdvance", 4, 0, vSubArguments, ss );
+ ss << " return GetPMT( fRate, fNper, fPv, fFv, fPayInAdvance != 0 );\n";
+ ss << "}";
+}
+
+void OpNPV::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 31 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0.0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " int nCount = 1;\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateRangeArgs( 1, vSubArguments.size() - 1, vSubArguments, ss, SkipEmpty,
+ " tmp += arg / pow( 1 + arg0, nCount );\n"
+ " nCount += 1;\n"
+ );
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+ void OpPrice::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+ {
+ decls.insert(getPriceDecl);
+ decls.insert(IsLeapYearDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl);
+ decls.insert(DateToDaysDecl);
+ decls.insert(ScaDateDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(addMonthsDecl);decls.insert(lcl_GetcoupnumDecl);
+ decls.insert(coupnumDecl);
+ decls.insert(DateToDaysDecl);
+ decls.insert(getDaysInMonthRangeDecl);
+ decls.insert(GetDaysInYearsDecl); decls.insert(GetDaysInYearDecl);
+ decls.insert(getDaysInYearRangeDecl); decls.insert(getDiffDecl);
+ decls.insert(coupdaybsDecl);
+ decls.insert(lcl_GetcoupdaysDecl);
+ decls.insert(lcl_GetcoupdaybsDecl);
+ decls.insert(coupdaysDecl);
+ decls.insert(coupdaysncDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(DateToDays);
+ funs.insert(ScaDate);
+ funs.insert(addMonths);funs.insert(getDaysInMonthRange);
+ funs.insert(GetDaysInYears);funs.insert(GetDaysInYear);
+ funs.insert(getDaysInYearRange);funs.insert(getDiff);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(lcl_Getcoupdaybs);
+ funs.insert(coupdaybs);
+ funs.insert(lcl_Getcoupdays);
+ funs.insert(coupdaysnc);
+ funs.insert(coupdays);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(lcl_Getcoupnum);
+ funs.insert(coupnum);funs.insert(getPrice);
+ }
+void OpPrice::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 7 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss<<" double tmp = 0;\n";
+ ss<<" int gid0 = get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArg( "tmp2", 2, vSubArguments, ss );
+ GenerateArg( "tmp3", 3, vSubArguments, ss );
+ GenerateArg( "tmp4", 4, vSubArguments, ss );
+ GenerateArg( "tmp5", 5, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp6", 6, 0, vSubArguments, ss );
+ ss << " if(tmp4*tmp5 == 0) return NAN;\n";
+ ss << " tmp = getPrice(tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6);\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpOddlprice::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetOddlpriceDecl);decls.insert(GetDiffDateDecl);
+ decls.insert(GetYearDiffDecl);decls.insert(IsLeapYearDecl);
+ decls.insert(GetNullDateDecl);decls.insert(DateToDaysDecl);
+ decls.insert(DaysToDateDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(GetYearFracDecl);
+ funs.insert(GetOddlprice);funs.insert(GetDiffDate);
+ funs.insert(GetYearDiff);funs.insert(IsLeapYear);
+ funs.insert(GetNullDate);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetYearFrac);
+}
+void OpOddlprice::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 7, 8 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" double tmp = 0;\n";
+ ss <<" int gid0 = get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArg( "tmp2", 2, vSubArguments, ss );
+ GenerateArg( "tmp3", 3, vSubArguments, ss );
+ GenerateArg( "tmp4", 4, vSubArguments, ss );
+ GenerateArg( "tmp5", 5, vSubArguments, ss );
+ GenerateArg( "tmp6", 6, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp7", 7, 0, vSubArguments, ss );
+ ss <<" int nNullDate = GetNullDate();\n";
+ ss <<" tmp = GetOddlprice(nNullDate,tmp0,tmp1,";
+ ss <<"tmp2,tmp3,tmp4,tmp5,tmp6,tmp7);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpOddlyield::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetDiffDateDecl);decls.insert(DaysToDateDecl);
+ decls.insert(GetYearDiffDecl);decls.insert(IsLeapYearDecl);
+ decls.insert(GetNullDateDecl);decls.insert(DateToDaysDecl);
+ decls.insert(DaysInMonthDecl);
+ decls.insert(GetYearFracDecl);decls.insert(GetOddlyieldDecl);
+ funs.insert(GetDiffDate);funs.insert(DaysToDate);
+ funs.insert(GetYearDiff);funs.insert(IsLeapYear);
+ funs.insert(GetNullDate);funs.insert(DaysInMonth);
+ funs.insert(DateToDays);
+ funs.insert(GetYearFrac);funs.insert(GetOddlyield);
+}
+void OpOddlyield::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 7, 8 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" double tmp = 0;\n";
+ ss <<" int gid0 = get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArg( "tmp2", 2, vSubArguments, ss );
+ GenerateArg( "tmp3", 3, vSubArguments, ss );
+ GenerateArg( "tmp4", 4, vSubArguments, ss );
+ GenerateArg( "tmp5", 5, vSubArguments, ss );
+ GenerateArg( "tmp6", 6, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp7", 7, 0, vSubArguments, ss );
+ ss <<" int nNullDate = GetNullDate();\n";
+ ss <<" tmp = GetOddlyield(nNullDate,tmp0,tmp1";
+ ss <<",tmp2,tmp3,tmp4,tmp5,tmp6,tmp7);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpPriceDisc::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearDiffDecl);decls.insert(getDiffDecl);
+ decls.insert(getDaysInYearRangeDecl);decls.insert(GetDaysInYearDecl);
+ decls.insert(GetDaysInYearsDecl);decls.insert(getDaysInMonthRangeDecl);
+ decls.insert(addMonthsDecl);decls.insert(ScaDateDecl);
+ decls.insert(GetNullDateDecl);decls.insert(DateToDaysDecl);
+ decls.insert(DaysToDateDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(IsLeapYearDecl);decls.insert(GetDiffDateDecl);
+ funs.insert(GetYearDiff);funs.insert(getDiff);
+ funs.insert(getDaysInYearRange);funs.insert(GetDaysInYear);
+ funs.insert(GetDaysInYears);funs.insert(getDaysInMonthRange);
+ funs.insert(addMonths);funs.insert(ScaDate);
+ funs.insert(GetNullDate);funs.insert(DateToDays);
+ funs.insert(DaysToDate);funs.insert(DaysInMonth);
+ funs.insert(IsLeapYear);funs.insert(GetDiffDate);
+}
+void OpPriceDisc::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArg( "tmp2", 2, vSubArguments, ss );
+ GenerateArg( "tmp3", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp4", 4, 0, vSubArguments, ss );
+ ss <<" int nNullDate = GetNullDate();\n";
+ ss <<" tmp=tmp3* ( 1.0 -tmp2*GetYearDiff( nNullDate, ";
+ ss <<"tmp0,tmp1,tmp4));\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpNper::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fPmt", 1, vSubArguments, ss );
+ GenerateArg( "fPV", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fFV", 3, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fPayInAdvance", 4, 0, vSubArguments, ss );
+ ss << " if ( fPV + fFV == 0.0 )\n";
+ ss << " return 0.0;\n";
+ ss << " else if (fRate == 0.0)\n";
+ ss << " return -(fPV + fFV)/fPmt;\n";
+ ss << " else if (fPayInAdvance != 0)\n";
+ ss << " return log(-(fRate*fFV-fPmt*(1.0+fRate))/(fRate*fPV+fPmt*(1.0+fRate)))\n";
+ ss << " / log1p(fRate);\n";
+ ss << " else\n";
+ ss << " return log(-(fRate*fFV-fPmt)/(fRate*fPV+fPmt)) / log1p(fRate);\n";
+ ss << "}\n";
+ }
+
+void OpPPMT::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetFVDecl);
+ funs.insert(GetFV);
+ decls.insert(GetPMTDecl);
+ funs.insert(GetPMT);
+ decls.insert(GetIpmtDecl);
+ funs.insert(GetIpmt);
+}
+
+void OpPPMT::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double arg=0;\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fPer", 1, vSubArguments, ss );
+ GenerateArg( "fNper", 2, vSubArguments, ss );
+ GenerateArg( "fPv", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "fFv", 4, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fPayInAdvance", 5, 0, vSubArguments, ss );
+ ss << " if (fPer < 1.0 || fPer > fNper)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fPmt;\n";
+ ss << " double fInterestPer = GetIpmt(fRate, fPer, fNper, fPv, fFv, fPayInAdvance != 0, &fPmt);\n";
+ ss << " return fPmt - fInterestPer;\n";
+ ss << " }\n";
+ ss << "}\n";
+}
+
+void OpCoupdaybs::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl); decls.insert(ScaDateDecl);
+ decls.insert(addMonthsDecl); decls.insert(getDaysInMonthRangeDecl);
+ decls.insert(GetDaysInYearsDecl);
+ decls.insert(getDaysInYearRangeDecl); decls.insert(getDiffDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(lcl_GetcoupdaybsDecl);
+ decls.insert(coupdaybsDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);funs.insert(ScaDate);
+ funs.insert(addMonths);funs.insert(getDaysInMonthRange);
+ funs.insert(GetDaysInYears);
+ funs.insert(getDaysInYearRange);funs.insert(getDiff);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(lcl_Getcoupdaybs);
+ funs.insert(coupdaybs);
+}
+void OpCoupdaybs::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fFreq", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 3, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int nFreq = fFreq;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" tmp = coupdaybs(nSettle,nMat,nFreq,nBase);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+
+void OpCoupdays::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl); decls.insert(ScaDateDecl);
+ decls.insert(addMonthsDecl); decls.insert(getDaysInMonthRangeDecl);
+ decls.insert(GetDaysInYearsDecl); decls.insert(GetDaysInYearDecl);
+ decls.insert(getDaysInYearRangeDecl); decls.insert(getDiffDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(lcl_GetcoupdaysDecl);
+ decls.insert(coupdaysDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);funs.insert(ScaDate);
+ funs.insert(addMonths);funs.insert(getDaysInMonthRange);
+ funs.insert(GetDaysInYears);funs.insert(GetDaysInYear);
+ funs.insert(getDaysInYearRange);funs.insert(getDiff);
+ funs.insert(lcl_Getcoupdays);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(coupdays);
+}
+void OpCoupdays::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fFreq", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 3, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int nFreq = fFreq;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" tmp = coupdays(nSettle,nMat,nFreq,nBase);\n";
+ ss <<" return tmp;\n";
+ ss << "}";
+}
+void OpCouppcd::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl);
+ decls.insert(ScaDateDecl);
+ decls.insert(addMonthsDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(lcl_GetCouppcdDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);
+ funs.insert(ScaDate);
+ funs.insert(addMonths);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(lcl_GetCouppcd);
+}
+void OpCouppcd::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fFreq", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 3, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int nFreq = fFreq;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" int nNullDate=693594;\n";
+ ss <<" tmp = lcl_GetCouppcd(nNullDate,nSettle,nMat,nFreq,nBase);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpCoupncd::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl);
+ decls.insert(ScaDateDecl);
+ decls.insert(addMonthsDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(lcl_GetCoupncdDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);
+ funs.insert(ScaDate);
+ funs.insert(addMonths);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(lcl_GetCoupncd);
+}
+void OpCoupncd::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fFreq", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 3, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int nFreq = fFreq;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" int nNullDate=693594;\n";
+ ss <<" tmp = lcl_GetCoupncd(nNullDate,nSettle,nMat,nFreq,nBase);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+
+void OpCoupdaysnc::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl); decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(DateToDaysDecl);
+ decls.insert(ScaDateDecl);
+ decls.insert(addMonthsDecl); decls.insert(getDaysInMonthRangeDecl);
+ decls.insert(GetDaysInYearsDecl); decls.insert(GetDaysInYearDecl);
+ decls.insert(getDaysInYearRangeDecl); decls.insert(getDiffDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(coupdaybsDecl);
+ decls.insert(lcl_GetcoupdaysDecl);
+ decls.insert(lcl_GetcoupdaybsDecl);
+ decls.insert(coupdaysDecl);
+ decls.insert(coupdaysncDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(DateToDays);
+ funs.insert(ScaDate);
+ funs.insert(addMonths);funs.insert(getDaysInMonthRange);
+ funs.insert(GetDaysInYears);funs.insert(GetDaysInYear);
+ funs.insert(getDaysInYearRange);funs.insert(getDiff);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(lcl_Getcoupdaybs);
+ funs.insert(coupdaybs);
+ funs.insert(lcl_Getcoupdays);
+ funs.insert(coupdaysnc);
+ funs.insert(coupdays);
+}
+void OpCoupdaysnc::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fFreq", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 3, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int nFreq = fFreq;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" tmp = coupdaysnc(nSettle,nMat,nFreq,nBase);\n";
+ ss <<" return tmp;\n";
+ ss << "}";
+}
+
+void OpCoupnum::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(IsLeapYearDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl);
+ decls.insert(DateToDaysDecl);
+ decls.insert(ScaDateDecl);
+ decls.insert(setDayDecl);decls.insert(checklessthanDecl);
+ decls.insert(addMonthsDecl);decls.insert(lcl_GetcoupnumDecl);
+ decls.insert(coupnumDecl);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);
+ funs.insert(DateToDays);
+ funs.insert(ScaDate);
+ funs.insert(setDay);funs.insert(checklessthan);
+ funs.insert(addMonths);funs.insert(lcl_Getcoupnum);
+ funs.insert(coupnum);
+}
+void OpCoupnum::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fFreq", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 3, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int nFreq = fFreq;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" tmp = coupnum(nSettle,nMat,nFreq,nBase);\n";
+ ss <<" return tmp;\n";
+ ss << "}";
+}
+void OpAmordegrc::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(nCorrValDecl); decls.insert(RoundDecl);
+ decls.insert(IsLeapYearDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetNullDateDecl); decls.insert(GetYearFracDecl);
+ funs.insert(Round);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetNullDate);funs.insert(GetYearFrac);
+}
+void OpAmordegrc::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 7 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double tmp = " << GetBottom() <<";\n";
+ GenerateArg( "fCost", 0, vSubArguments, ss );
+ GenerateArg( "fDate", 1, vSubArguments, ss );
+ GenerateArg( "fFirstPer", 2, vSubArguments, ss );
+ GenerateArg( "fRestVal", 3, vSubArguments, ss );
+ GenerateArg( "fPer", 4, vSubArguments, ss );
+ GenerateArg( "fRate", 5, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 6, 0, vSubArguments, ss );
+ ss << " int nDate = fDate;\n";
+ ss << " int nFirstPer = fFirstPer;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" uint nPer = convert_int( fPer );\n";
+ ss <<" double fUsePer = 1.0 / fRate;\n";
+ ss <<" double fAmorCoeff;\n";
+ ss <<" if( fUsePer < 3.0 )\n";
+ ss <<" fAmorCoeff = 1.0;\n";
+ ss <<" else if( fUsePer < 5.0 )\n";
+ ss <<" fAmorCoeff = 1.5;\n";
+ ss <<" else if( fUsePer <= 6.0 )\n";
+ ss <<" fAmorCoeff = 2.0;\n";
+ ss <<" else\n";
+ ss <<" fAmorCoeff = 2.5;\n";
+ ss <<" fRate *= fAmorCoeff;\n";
+ ss <<" tmp = Round( GetYearFrac( 693594,";
+ ss <<"nDate, nFirstPer, nBase ) * fRate * fCost);\n";
+ ss <<" fCost = fCost-tmp;\n";
+ ss <<" double fRest = fCost - fRestVal;\n";
+ ss <<" for( uint n = 0 ; n < nPer ; n++ )\n";
+ ss <<" {\n";
+ ss <<" tmp = Round( fRate * fCost);\n";
+ ss <<" fRest -= tmp;\n";
+ ss <<" if( fRest < 0.0 )\n";
+ ss <<" {\n";
+ ss <<" switch( nPer - n )\n";
+ ss <<" {\n";
+ ss <<" case 0:\n";
+ ss <<" case 1:\n";
+ ss <<" tmp = Round( fCost * 0.5);\n";
+ ss <<" default:\n";
+ ss <<" tmp = 0.0;\n";
+ ss <<" }\n";
+ ss <<" }\n";
+ ss <<" fCost -= tmp;\n";
+ ss <<" }\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpAmorlinc::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(nCorrValDecl); decls.insert(RoundDecl);
+ decls.insert(IsLeapYearDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(DaysToDateDecl); decls.insert(DateToDaysDecl);
+ decls.insert(GetYearFracDecl);
+ funs.insert(Round);
+ funs.insert(IsLeapYear);funs.insert(DaysInMonth);
+ funs.insert(DaysToDate);funs.insert(DateToDays);
+ funs.insert(GetYearFrac);
+}
+void OpAmorlinc::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 6, 7 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double tmp = 0;\n";
+ GenerateArg( "fCost", 0, vSubArguments, ss );
+ GenerateArg( "fDate", 1, vSubArguments, ss );
+ GenerateArg( "fFirstPer", 2, vSubArguments, ss );
+ GenerateArg( "fRestVal", 3, vSubArguments, ss );
+ GenerateArg( "fPer", 4, vSubArguments, ss );
+ GenerateArg( "fRate", 5, vSubArguments, ss );
+ GenerateArgWithDefault( "fBase", 6, 0, vSubArguments, ss );
+ ss << " int nDate = fDate;\n";
+ ss << " int nFirstPer = fFirstPer;\n";
+ ss << " int nBase = fBase;\n";
+ ss <<" int nPer = convert_int( fPer );\n";
+ ss <<" double fOneRate = fCost * fRate;\n";
+ ss <<" double fCostDelta = fCost - fRestVal;\n";
+ ss <<" double f0Rate = GetYearFrac( 693594,";
+ ss <<"nDate, nFirstPer, nBase )* fRate * fCost;\n";
+ ss <<" int nNumOfFullPeriods = (int)";
+ ss <<"( ( fCost - fRestVal - f0Rate) / fOneRate );\n";
+ ss <<" if( nPer == 0 )\n";
+ ss <<" tmp = f0Rate;\n";
+ ss <<" else if( nPer <= nNumOfFullPeriods )\n";
+ ss <<" tmp = fOneRate;\n";
+ ss <<" else if( nPer == nNumOfFullPeriods + 1 )\n";
+ ss <<" tmp = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;\n";
+ ss <<" else\n";
+ ss <<" tmp = 0.0;\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpReceived::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearDiffDecl);decls.insert(GetDiffDateDecl);
+ decls.insert(DaysToDateDecl);decls.insert(DaysInMonthDecl);
+ decls.insert(GetNullDateDecl);decls.insert(IsLeapYearDecl);
+ decls.insert(DateToDaysDecl);
+ funs.insert(GetDiffDate);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(GetNullDate);
+ funs.insert(DateToDays);funs.insert(IsLeapYear);
+ funs.insert(GetYearDiff);
+}
+
+void OpReceived::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() <<";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fSettle", 0, vSubArguments, ss );
+ GenerateArg( "fMat", 1, vSubArguments, ss );
+ GenerateArg( "fInvest", 2, vSubArguments, ss );
+ GenerateArg( "fDisc", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "fOB", 4, 0, vSubArguments, ss );
+ ss << " int nSettle = fSettle;\n";
+ ss << " int nMat = fMat;\n";
+ ss << " int rOB = fOB;\n";
+ ss << " double tmpvalue = (1.0-(fDisc";
+ ss <<" * GetYearDiff( GetNullDate()";
+ ss <<",nSettle,nMat,rOB)));\n";
+ ss << " tmp = fInvest/tmpvalue;\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpYielddisc::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearFracDecl);decls.insert(GetNullDateDecl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysToDateDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+
+ funs.insert(GetYearFrac);funs.insert(GetNullDate);
+ funs.insert(DateToDays);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+}
+void OpYielddisc::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT(5,5);
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = 0;\n\t";
+ ss << "int gid0 = get_global_id(0);\n";
+ GenerateArg( "tmp000", 0, vSubArguments, ss );
+ GenerateArg( "tmp001", 1, vSubArguments, ss );
+ GenerateArg( "tmp002", 2, vSubArguments, ss );
+ GenerateArg( "tmp003", 3, vSubArguments, ss );
+ GenerateArg( "tmp004", 4, vSubArguments, ss );
+ ss<< "\t";
+ ss<< "if(tmp002 <= 0 || tmp003 <= 0 || tmp000 >= tmp001 )\n\t";
+ ss<< " return CreateDoubleError(IllegalArgument);\n\t";
+ ss<< "tmp = (tmp003/tmp002)-1;\n\t";
+ ss << "tmp /= GetYearFrac( GetNullDate(),tmp000,tmp001,tmp004);\n\t";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpTbillprice::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetYearFracDecl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysToDateDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(IsLeapYearDecl);
+
+ funs.insert(GetYearFrac);
+ funs.insert(DateToDays);funs.insert(DaysToDate);
+ funs.insert(DaysInMonth);funs.insert(IsLeapYear);
+}
+
+void OpTbillprice::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double tmp = 0;\n";
+
+ ss << " int singleIndex = gid0;\n";
+ ss << " int doubleIndex = gid0;\n";
+ ss << " int i = gid0;\n";
+ GenTmpVariables(ss,vSubArguments);
+ CheckAllSubArgumentIsNan(ss,vSubArguments);
+
+ ss << " tmp1+=1.0;\n";
+ ss << " double fFraction =GetYearFrac(693594,tmp0,tmp1,0);\n";
+ ss << " tmp = 100.0 * ( 1.0 - tmp2 * fFraction );\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpRate::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(RateIterationDecl);
+ funs.insert(RateIteration);
+}
+
+void OpRate::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fNper", 0, vSubArguments, ss );
+ GenerateArg( "fPayment", 1, vSubArguments, ss );
+ GenerateArg( "fPv", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fFv", 3, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fPayType", 4, 0, vSubArguments, ss );
+ ss << " bool bPayType = fPayType != 0;\n";
+ if( vSubArguments.size() == 6 )
+ {
+ GenerateArgWithDefault( "fGuess", 5, 0.1, vSubArguments, ss );
+ ss << " double fOrigGuess = fGuess;\n";
+ ss << " bool bDefaultGuess = false;\n";
+ }
+ else
+ {
+ ss << " double fGuess = 0.1, fOrigGuess = 0.1;\n";
+ ss << " bool bDefaultGuess = true;\n";
+ }
+ ss << " if( fNper <= 0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " bool bValid = RateIteration(fNper, fPayment, fPv, fFv, bPayType, &fGuess);\n";
+ ss << " if (!bValid)\n";
+ ss << " {\n";
+ ss << " if (bDefaultGuess)\n";
+ ss << " {\n";
+ ss << " double fX = fOrigGuess;\n";
+ ss << " for (int nStep = 2; nStep <= 10 && !bValid; ++nStep)\n";
+ ss << " {\n";
+ ss << " fGuess = fX * nStep;\n";
+ ss << " bValid = RateIteration( fNper, fPayment, fPv, fFv, bPayType, &fGuess);\n";
+ ss << " if (!bValid)\n";
+ ss << " {\n";
+ ss << " fGuess = fX / nStep;\n";
+ ss << " bValid = RateIteration( fNper, fPayment, fPv, fFv, bPayType, &fGuess);\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " if (!bValid)\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << " }\n";
+ ss << " return fGuess;\n";
+ ss << "}\n";
+}
+
+void OpTbillyield::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetDiffDate360Decl);decls.insert(IsLeapYearDecl);
+ decls.insert(DateToDaysDecl);decls.insert(DaysToDate_LocalBarrierDecl);
+ decls.insert(DaysInMonthDecl);decls.insert(GetNullDateDecl);
+ decls.insert(GetDiffDate360_Decl);
+ funs.insert(GetDiffDate360);funs.insert(DateToDays);
+ funs.insert(DaysToDate_LocalBarrier);funs.insert(IsLeapYear);
+ funs.insert(DaysInMonth);funs.insert(GetNullDate);
+ funs.insert(GetDiffDate360_);
+
+}
+
+void OpTbillyield::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double tmp = 0;\n";
+ GenerateArg( "tmp000", 0, vSubArguments, ss );
+ GenerateArg( "tmp001", 1, vSubArguments, ss );
+ GenerateArg( "tmp002", 2, vSubArguments, ss );
+ ss <<" int nDiff=GetDiffDate360(GetNullDate(),tmp000,tmp001,true);\n";
+ ss <<" nDiff++;\n";
+ ss <<" tmp=100.0;\n";
+ ss <<" tmp = tmp / tmp002;\n";
+ ss <<" tmp = tmp - 1.0;\n";
+ ss <<" tmp = tmp / nDiff;\n";
+ ss <<" tmp = tmp * 360.0;\n";
+ ss <<" return tmp;\n";
+ ss << "}\n";
+}
+
+void OpDDB::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(ScGetDDBDecl);
+ funs.insert(ScGetDDB);
+}
+
+void OpDDB::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double tmp = 0;\n";
+ GenerateArg( "fCost", 0, vSubArguments, ss );
+ GenerateArg( "fSalvage", 1, vSubArguments, ss );
+ GenerateArg( "fLife", 2, vSubArguments, ss );
+ GenerateArg( "fPeriod", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "fFactor", 4, 2, vSubArguments, ss );
+ ss << " if (fCost < 0.0 || fSalvage < 0.0 || fFactor <= 0.0 || fSalvage > fCost\n";
+ ss << " || fPeriod < 1.0 || fPeriod > fLife)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return ScGetDDB( fCost, fSalvage, fLife, fPeriod, fFactor );\n";
+ ss << "}\n";
+}
+
+void OpPV::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double result = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fRate", 0, vSubArguments, ss );
+ GenerateArg( "fNper", 1, vSubArguments, ss );
+ GenerateArg( "fPmt", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fFv", 3, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fPayInAdvance", 4, 0, vSubArguments, ss );
+ ss << " double fPv;\n";
+ ss << " if (fRate == 0.0)\n";
+ ss << " fPv = fFv + fPmt * fNper;\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if (fPayInAdvance != 0)\n";
+ ss << " fPv = (fFv * pow(1.0 + fRate, -fNper))\n";
+ ss << " + (fPmt * (1.0 - pow(1.0 + fRate, -fNper + 1.0)) / fRate)\n";
+ ss << " + fPmt;\n";
+ ss << " else\n";
+ ss << " fPv = (fFv * pow(1.0 + fRate, -fNper))\n";
+ ss << " + (fPmt * (1.0 - pow(1.0 + fRate, -fNper)) / fRate);\n";
+ ss << " }\n";
+ ss << " return -fPv;\n";
+ ss << "}\n";
+}
+
+void OpVDB::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ decls.insert(ScGetDDBDecl);decls.insert(approx_equalDecl);
+ decls.insert(ScInterVDBDecl);decls.insert(VDBImplementDecl);
+ funs.insert(is_representable_integer);
+ funs.insert(ScGetDDB);funs.insert(approx_equal);
+ funs.insert(ScInterVDB);funs.insert(VDBImplement);
+}
+
+void OpVDB::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 5, 7 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " int singleIndex = gid0;\n";
+ ss << " double result = 0;\n";
+ GenerateArg( "fCost", 0, vSubArguments, ss );
+ GenerateArg( "fSalvage", 1, vSubArguments, ss );
+ GenerateArg( "fLife", 2, vSubArguments, ss );
+ GenerateArg( "fStart", 3, vSubArguments, ss );
+ GenerateArg( "fEnd", 4, vSubArguments, ss );
+ GenerateArgWithDefault( "fFactor", 5, 2, vSubArguments, ss );
+ GenerateArgWithDefault( "fNoSwitch", 6, 0, vSubArguments, ss );
+ ss << " if (fStart < 0.0 || fEnd < fStart || fEnd > fLife || fCost < 0.0\n";
+ ss << " || fSalvage > fCost || fFactor <= 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return VDBImplement(fCost, fSalvage, fLife, fStart, fEnd, fFactor, fNoSwitch != 0);\n";
+ ss << "}";
+}
+
+void OpXirr::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArgWithDefault( "fResultRate", 2, 0.1, vSubArguments, ss );
+ ss << " if(fResultRate<=-1)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double fMaxEps = 1e-10;\n";
+ ss << " int nMaxIter = 50;\n";
+ ss << " int nIter = 0;\n";
+ ss << " double fResultValue;\n";
+ ss << " int nIterScan = 0;\n";
+ ss << " bool bContLoop = false;\n";
+ ss << " bool bResultRateScanEnd = false;\n";
+ // Make 'V_0' and 'D_0' be the first elements of arguments 0 and 1.
+ GenerateRangeArgElement( "V_0", 0, "0", vSubArguments, ss, EmptyIsZero );
+ GenerateRangeArgElement( "D_0", 1, "0", vSubArguments, ss, EmptyIsZero );
+ ss << " do\n";
+ ss << " {\n";
+ ss << " if (nIterScan >=1)\n";
+ ss << " fResultRate = -0.99 + (nIterScan -1)* 0.01;\n";
+ ss << " do\n";
+ ss << " {\n";
+ ss << " double r = fResultRate + 1;\n";
+ ss << " fResultValue = V_0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " fResultValue += arg1/pow(r,(arg2 - D_0)/365.0);\n"
+ , "1" // start from 2nd element
+ );
+ ss << " double fResultValue2 = 0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " double E_i = (arg2 - D_0)/365.0;\n"
+ " fResultValue2 -= E_i * arg1 / pow(r,E_i + 1.0);\n"
+ , "1" // start from 2nd element
+ );
+ ss << " double fNewRate = fResultRate - fResultValue / fResultValue2;\n";
+ ss << " double fRateEps = fabs( fNewRate - fResultRate );\n";
+ ss << " fResultRate = fNewRate;\n";
+ ss << " bContLoop = (fRateEps > fMaxEps) && (fabs( fResultValue ) > fMaxEps);\n";
+ ss << " } while( bContLoop && (++nIter < nMaxIter) );\n";
+ ss << " nIter = 0;\n";
+ ss << " if( isnan(fResultRate) || isinf(fResultRate) || isnan(fResultValue) || isinf(fResultValue))\n";
+ ss << " bContLoop = true;\n";
+ ss << " ++nIterScan;\n";
+ ss << " bResultRateScanEnd = (nIterScan >= 200);\n";
+ ss << " } while(bContLoop && !bResultRateScanEnd);\n";
+ ss << " if( bContLoop )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return fResultRate;\n";
+ ss << "}";
+}
+
+void OpDB::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "fCost", 0, vSubArguments, ss );
+ GenerateArg( "fSalvage", 1, vSubArguments, ss );
+ GenerateArg( "fLife", 2, vSubArguments, ss );
+ GenerateArg( "fPeriod", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "fMonths", 4, 12, vSubArguments, ss );
+ ss << " int nMonths = (int)fMonths;\n";
+ ss << " if (fMonths < 1.0 || fMonths > 12.0 || fLife > 1200.0 || fSalvage < 0.0 ||\n";
+ ss << " fPeriod > (fLife + 1.0) || fSalvage > fCost || fCost <= 0.0 ||\n";
+ ss << " fLife <= 0 || fPeriod <= 0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double tmp = 0;\n";
+ ss <<" double fDeprRate = 1.0 - pow(fSalvage / fCost, 1.0 / fLife);\n";
+ ss <<" fDeprRate = ((int)(fDeprRate * 1000.0 + 0.5)) / 1000.0;\n";
+ ss <<" double fFirstDeprRate = fCost * fDeprRate * nMonths / 12.0;\n";
+ ss <<" double fDb = 0.0;\n";
+ ss <<" if ((int)(fPeriod) == 1)\n";
+ ss <<" fDb = fFirstDeprRate;\n";
+ ss <<" else\n";
+ ss <<" {\n";
+ ss <<" double fSumDeprRate = fFirstDeprRate;\n";
+ ss <<" double fMin = fLife;\n";
+ ss <<" if (fMin > fPeriod) fMin = fPeriod;\n";
+ ss <<" int nMax = (int)fMin;\n";
+ ss <<" for (int i = 2; i <= nMax; i++)\n";
+ ss <<" {\n";
+ ss <<" fDb = (fCost - fSumDeprRate) * fDeprRate;\n";
+ ss <<" fSumDeprRate += fDb;\n";
+ ss <<" }\n";
+ ss <<" if (fPeriod > fLife)\n";
+ ss <<" fDb = ((fCost - fSumDeprRate)";
+ ss <<"* fDeprRate * (12.0 - nMonths)) / 12.0;\n";
+ ss <<" }\n";
+ ss <<" tmp = fDb;\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_financial.hxx b/sc/source/core/opencl/op_financial.hxx
new file mode 100644
index 0000000000..a834259722
--- /dev/null
+++ b/sc/source/core/opencl/op_financial.hxx
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+class OpRRI: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "RRI"; }
+};
+
+class OpNominal: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "NOMINAL_ADD"; }
+};
+
+class OpDollarde:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "Dollarde"; }
+
+};
+
+class OpDollarfr:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "Dollarfr"; }
+
+};
+
+class OpDISC: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override { return "DISC"; }
+};
+
+class OpINTRATE: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override { return "INTRATE"; }
+};
+
+class OpFV: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override {
+ return "FV"; }
+};
+
+class OpIPMT: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override {
+ return "IPMT"; }
+};
+
+class OpISPMT: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "ISPMT"; }
+};
+
+class OpPDuration: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "Duration"; }
+};
+
+class OpDuration_ADD: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override {
+ return "Duration_ADD"; }
+};
+class OpMDuration: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override {return "MDuration"; }
+};
+
+class Fvschedule: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override {return "Fvschedule"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class Cumipmt: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpIRR: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "IRR"; }
+};
+
+class OpMIRR: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual bool canHandleMultiVector() const override { return true; }
+ virtual std::string BinFuncName() const override { return "MIRR"; }
+};
+
+class OpXirr: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Xirr"; }
+};
+
+class XNPV: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+};
+
+class PriceMat: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+class OpSYD: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "SYD"; }
+};
+
+class OpEffective:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "Effect_Add"; }
+};
+
+class OpCumipmt: public Cumipmt
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string BinFuncName() const override { return "Cumipmt"; }
+};
+
+class OpXNPV: public XNPV
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string BinFuncName() const override { return "XNPV"; }
+
+};
+
+class OpTbilleq: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "fTbilleq"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpCumprinc: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "cumprinc"; }
+};
+
+class OpAccrintm: public Normal
+{
+ public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Accrintm"; }
+};
+class OpAccrint: public Normal
+{
+ public:
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Accrint"; }
+};
+
+class OpYield: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Yield"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpSLN: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SLN"; }
+};
+
+class OpFvschedule: public Fvschedule
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string BinFuncName() const override { return "Fvschedule"; }
+};
+
+class OpYieldmat: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Yieldmat"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpPMT: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "PMT"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+class OpNPV: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "NPV"; }
+ // doesn't handle svDoubleVectorRef properly, it should iterate horizontally
+ // virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpPrice: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Price"; }
+};
+
+class OpNper: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "NPER"; }
+};
+class OpOddlprice: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>&,
+ std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Oddlprice"; }
+};
+class OpOddlyield: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Oddlyield"; }
+};
+class OpPriceDisc: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>&,
+ std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "PriceDisc"; }
+};
+class OpPPMT: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "PPMT"; }
+};
+
+class OpCoupdaybs:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0";}
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Coupdaybs"; }
+
+};
+
+class OpCoupdays:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0";}
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Coupdays";}
+
+};
+
+class OpCoupdaysnc:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0";}
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Coupdaysnc"; }
+
+};
+
+class OpCouppcd:public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Couppcd"; }
+
+};
+
+class OpCoupncd:public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "Coupncd"; }
+
+};
+
+class OpCoupnum:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0";}
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "Coupnum"; }
+
+};
+class OpDDB:public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "DDB"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+class OpVDB: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "VDB"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+class OpDB:public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "DB"; }
+};
+class OpAmordegrc:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0";}
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "Amordegrc"; }
+};
+class OpAmorlinc:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0";}
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "Amorlinc"; }
+};
+
+class OpReceived:public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Received"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpYielddisc: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Yielddisc"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpTbillprice: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "fTbillprice"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpPriceMat:public PriceMat
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string BinFuncName() const override { return "PriceMat"; }
+};
+
+class OpRate: public Normal {
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string BinFuncName() const override { return "rate"; }
+};
+
+class OpTbillyield: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "fTbillyield"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpPV: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "PV"; }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_financial_helpers.hxx b/sc/source/core/opencl/op_financial_helpers.hxx
new file mode 100644
index 0000000000..013334a045
--- /dev/null
+++ b/sc/source/core/opencl/op_financial_helpers.hxx
@@ -0,0 +1,1412 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+const char GetPMTDecl[] =
+"double GetPMT( double fRate, double fNper, double fPv, double fFv, bool bPayInAdvance);\n";
+
+const char GetPMT[] =
+"double GetPMT( double fRate, double fNper, double fPv, double fFv, bool bPayInAdvance )\n"
+"{\n"
+" double fPayment;\n"
+" if (fRate == 0.0)\n"
+" fPayment = (fPv + fFv) / fNper;\n"
+" else\n"
+" {\n"
+" if (bPayInAdvance)\n"
+" fPayment = (fFv + fPv * exp( fNper * log1p(fRate) ) ) * fRate\n"
+" / (expm1( (fNper + 1) * log1p(fRate) ) - fRate);\n"
+" else\n"
+" fPayment = (fFv + fPv * exp(fNper * log1p(fRate) ) ) * fRate\n"
+" / expm1( fNper * log1p(fRate) );\n"
+" }\n"
+" return -fPayment;\n"
+"}\n";
+
+const char GetIpmtDecl[] =
+"double GetIpmt(double fRate, double fPer, double fNper, double fPv,\n"
+" double fFv, bool bPayInAdvance, double* fPmt);\n";
+
+const char GetIpmt[] =
+"double GetIpmt(double fRate, double fPer, double fNper, double fPv,\n"
+" double fFv, bool bPayInAdvance, double* fPmt)\n"
+"{\n"
+" *fPmt = GetPMT(fRate, fNper, fPv, fFv, bPayInAdvance);\n"
+" double fIpmt;\n"
+" if (fPer == 1.0)\n"
+" {\n"
+" if (bPayInAdvance)\n"
+" fIpmt = 0.0;\n"
+" else\n"
+" fIpmt = -fPv;\n"
+" }\n"
+" else\n"
+" {\n"
+" if (bPayInAdvance)\n"
+" fIpmt = GetFV(fRate, fPer-2.0, *fPmt, fPv, true) - *fPmt;\n"
+" else\n"
+" fIpmt = GetFV(fRate, fPer-1.0, *fPmt, fPv, false);\n"
+" }\n"
+" return fIpmt * fRate;\n"
+"}\n";
+
+const char GetFVDecl[] =
+"double GetFV( double fRate, double fNper, double fPmt,"
+" double fPv, bool bPayInAdvance );\n";
+
+const char GetFV[] =
+"double GetFV( double fRate, double fNper, double fPmt,"
+" double fPv, bool bPayInAdvance )\n"
+"{\n"
+" double fFv;\n"
+" if (fRate == 0.0)\n"
+" fFv = fPv + fPmt * fNper;\n"
+" else\n"
+" {\n"
+" double fTerm = pow(1.0 + fRate, fNper);\n"
+" if (bPayInAdvance)\n"
+" fFv = fPv * fTerm + fPmt*(1.0 + fRate)*(fTerm - 1.0)/fRate;\n"
+" else\n"
+" fFv = fPv * fTerm + fPmt*(fTerm - 1.0)/fRate;\n"
+" }\n"
+" return -fFv;\n"
+"}\n";
+
+const char IsLeapYearDecl[] =
+"bool IsLeapYear( int n );\n";
+
+const char IsLeapYear[] =
+"bool IsLeapYear( int n )\n"
+"{\n"
+" return ( (( ( n % 4 ) == 0 ) && ( ( n % 100 ) != 0)) || ( ( n % 400 ) == "
+"0 ) );\n"
+"}\n";
+
+const char DaysInMonthDecl[] =
+"int DaysInMonth( int nMonth, int nYear );\n";
+
+const char DaysInMonth[] =
+"int DaysInMonth( int nMonth, int nYear )\n"
+"{\n"
+" int tmp = 0;\n"
+" switch(nMonth)\n"
+" {\n"
+" case 1:\n"
+" case 3:\n"
+" case 5:\n"
+" case 7:\n"
+" case 8:\n"
+" case 10:\n"
+" case 12:\n"
+" tmp = 31;\n"
+" break;\n"
+" case 4:\n"
+" case 6:\n"
+" case 9:\n"
+" case 11:\n"
+" tmp =30;\n"
+" break;\n"
+" case 2:\n"
+" if ( IsLeapYear(nYear)==1)\n"
+" tmp = 29;\n"
+" else\n"
+" tmp = 28;\n"
+" break;\n"
+" }\n"
+" return tmp;\n"
+"}\n";
+
+const char DateToDaysDecl[] =
+"int DateToDays( int nDay, int nMonth, int nYear );\n";
+
+const char DateToDays[] =
+"int DateToDays( int nDay, int nMonth, int nYear )\n"
+"{\n"
+" int nDays = (nYear-1) * 365;\n"
+" nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);\n"
+" for( int i = 1; i < nMonth; i++ )\n"
+" nDays += DaysInMonth(i,nYear);\n"
+" nDays += nDay;\n"
+"\n"
+" return nDays;\n"
+"}\n";
+
+const char GetNullDateDecl[] =
+"int GetNullDate();\n";
+
+const char GetNullDate[] =
+"int GetNullDate()\n"
+"{\n"
+" return DateToDays(30,12,1899 );\n"
+"}\n";
+
+const char ScaDateDecl[] =
+"void ScaDate( int nNullDate, int nDate, int nBase,int *nOrigDay, "
+"int *nMonth,int *nYear,int *bLastDayMode,int *bLastDay,"
+"int *b30Days,int *bUSMode,int *nDay);\n";
+
+const char ScaDate[] =
+"void ScaDate( int nNullDate, int nDate, int nBase,int *nOrigDay, "
+"int *nMonth,int *nYear,int *bLastDayMode,int *bLastDay,"
+"int *b30Days,int *bUSMode,int *nDay)\n"
+"{\n"
+" DaysToDate( nNullDate + nDate, nOrigDay, nMonth, nYear );\n"
+" *bLastDayMode = (nBase != 5);\n"
+" *bLastDay = (*nOrigDay >= DaysInMonth( *nMonth, *nYear ));\n"
+" *b30Days = (nBase == 0) || (nBase == 4);\n"
+" *bUSMode = (nBase == 0);\n"
+" if( *b30Days)\n"
+" {\n"
+" *nDay = min( *nOrigDay, 30);\n"
+" if( *bLastDay || (*nDay >=DaysInMonth( *nMonth, *nYear )) )\n"
+" *nDay = 30;\n"
+" }\n"
+" else\n"
+" {\n"
+" int nLastDay = DaysInMonth( *nMonth, *nYear );\n"
+" *nDay = *bLastDay ? nLastDay : min( *nOrigDay, nLastDay );\n"
+" }\n"
+"}\n";
+
+const char lcl_GetCouppcdDecl[] =
+"int lcl_GetCouppcd(int nNullDate,int nSettle,int nMat,int nFreq,int nBase);\n";
+
+const char lcl_GetCouppcd[] =
+"int lcl_GetCouppcd(int nNullDate,int nSettle, int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int aDate = nMat;\n"
+" int rDay=0,rMonth=0, rYear=0,rbLastDayMode=0, rbLastDay=0,rb30Days=0,"
+"rbUSMode=0,rnDay=0;\n"
+" int sDay=0,sMonth=0, sYear=0,sbLastDayMode=0, sbLastDay=0,sb30Days=0,"
+"sbUSMode=0,snDay=0;\n"
+" ScaDate( nNullDate,nSettle,nBase,&sDay,&sMonth,&sYear,&sbLastDayMode,"
+"&sbLastDay,&sb30Days,&sbUSMode,&snDay);\n"
+" ScaDate( nNullDate,aDate,nBase,&rDay,&rMonth,&rYear,&rbLastDayMode,"
+"&rbLastDay,&rb30Days,&rbUSMode,&rnDay);\n"
+" rYear=sYear;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" if(checklessthan(rYear,sYear,rMonth,sMonth,rnDay,snDay,rbLastDay,"
+"sbLastDay,rDay,sDay))\n"
+" {\n"
+" rYear+=1;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" }\n"
+" while(checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay))\n"
+" {\n"
+" double d = -1*(12/nFreq);\n"
+" addMonths(rb30Days,rbLastDay,&rnDay,rDay,&rMonth,d,&rYear);\n"
+" }\n"
+" int nLastDay = DaysInMonth( rMonth, rYear );\n"
+" int nRealDay = (rbLastDayMode && rbLastDay) ? nLastDay :"
+"min( nLastDay, rDay );\n"
+" return DateToDays( nRealDay, rMonth, rYear ) - nNullDate;\n"
+"}\n";
+
+const char lcl_GetCoupncdDecl[] =
+"int lcl_GetCoupncd(int nNullDate,int nSettle,int nMat,int nFreq,int nBase);\n";
+
+const char lcl_GetCoupncd[] =
+"int lcl_GetCoupncd(int nNullDate,int nSettle, int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int aDate = nMat;\n"
+" int rDay=0,rMonth=0, rYear=0,rbLastDayMode=0, rbLastDay=0,rb30Days=0,"
+"rbUSMode=0,rnDay=0;\n"
+" int sDay=0,sMonth=0, sYear=0,sbLastDayMode=0, sbLastDay=0,sb30Days=0,"
+"sbUSMode=0,snDay=0;\n"
+" ScaDate( nNullDate,nSettle,nBase,&sDay,&sMonth,&sYear,&sbLastDayMode,"
+"&sbLastDay,&sb30Days,&sbUSMode,&snDay);\n"
+" ScaDate( nNullDate,aDate,nBase,&rDay,&rMonth,&rYear,&rbLastDayMode,"
+"&rbLastDay,&rb30Days,&rbUSMode,&rnDay);\n"
+" rYear=sYear;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" if(checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,rbLastDay"
+",sDay,rDay))\n"
+" {\n"
+" rYear-=1;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" }\n"
+" while(!checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay))\n"
+" {\n"
+" addMonths(rb30Days,rbLastDay,&rnDay,rDay,&rMonth,12/nFreq,&rYear);\n"
+" }\n"
+" int nLastDay = DaysInMonth( rMonth, rYear );\n"
+" int nRealDay = (rbLastDayMode && rbLastDay) ? nLastDay :"
+"min( nLastDay, rDay );\n"
+" return DateToDays( nRealDay, rMonth, rYear ) - nNullDate;\n"
+"}\n";
+
+const char addMonthsDecl[] =
+"void addMonths(int b30Days,int bLastDay,int *nDay,int nOrigDay,"
+"int *nMonth,int nMonthCount,int *year);\n";
+
+const char addMonths[] =
+"void addMonths(int b30Days,int bLastDay,int *nDay,int nOrigDay,"
+"int *nMonth,int nMonthCount,int *year)\n"
+"{\n"
+" int nNewMonth = nMonthCount + *nMonth;\n"
+" if( nNewMonth > 12 )\n"
+" {\n"
+" --nNewMonth;\n"
+" *year+=nNewMonth / 12 ;\n"
+" *nMonth = ( nNewMonth % 12 ) + 1;\n"
+" }\n"
+" else if( nNewMonth < 1 )\n"
+" {\n"
+" *year+= nNewMonth / 12 - 1 ;\n"
+" *nMonth = nNewMonth % 12 + 12 ;\n"
+" }\n"
+" else\n"
+" *nMonth = nNewMonth ;\n"
+" if( b30Days )\n"
+" {\n"
+" *nDay = min( nOrigDay, 30);\n"
+" if( bLastDay || (*nDay >= DaysInMonth( *nMonth, *year )) )\n"
+" *nDay = 30;\n"
+" }\n"
+" else\n"
+" {\n"
+" int nLastDay = DaysInMonth( *nMonth, *year );\n"
+" *nDay = bLastDay ? nLastDay : min( nOrigDay, nLastDay );\n"
+" }\n"
+"}\n";
+
+const char getDaysInMonthRangeDecl[] =
+"int getDaysInMonthRange( int nFrom, int nTo,int b30Days,int year);\n";
+
+const char getDaysInMonthRange[] =
+"int getDaysInMonthRange( int nFrom, int nTo,int b30Days,int year)\n"
+"{\n"
+" if( nFrom > nTo )\n"
+" return 0;\n"
+" int nRet = 0;\n"
+" if( b30Days )\n"
+" nRet = (nTo - nFrom + 1) * 30;\n"
+" else\n"
+" {\n"
+" for( int nMonthIx = nFrom; nMonthIx <= nTo; ++nMonthIx )\n"
+" nRet += b30Days ? 30 : DaysInMonth( nMonthIx, year );\n"
+" }\n"
+" return nRet;\n"
+"}\n";
+
+const char GetDaysInYearsDecl[] =
+"int GetDaysInYears( int nYear1, int nYear2 );\n";
+
+const char GetDaysInYears[] =
+"int GetDaysInYears( int nYear1, int nYear2 )\n"
+"{\n"
+" int nLeaps = 0;\n"
+" for( int n = nYear1 ; n <= nYear2 ; n++ )\n"
+" {\n"
+" if( IsLeapYear( n ) )\n"
+" nLeaps++;\n"
+" }\n"
+" int nSum = 1;\n"
+" nSum += nYear2;\n"
+" nSum -= nYear1;\n"
+" nSum *= 365;\n"
+" nSum += nLeaps;\n"
+" return nSum;\n"
+"}\n";
+
+const char GetDaysInYearDecl[] =
+"int GetDaysInYear( int nNullDate, int nDate, int nMode );\n";
+
+const char GetDaysInYear[] =
+"int GetDaysInYear( int nNullDate, int nDate, int nMode )\n"
+"{\n"
+" switch( nMode )\n"
+" {\n"
+" case 0:\n"
+" case 2:\n"
+" case 4:\n"
+" return 360;\n"
+" case 1:\n"
+" {\n"
+" int nD=0, nM=0, nY=0;\n"
+" nDate += nNullDate;\n"
+" DaysToDate( nDate, &nD, &nM, &nY );\n"
+" return IsLeapYear( nY )? 366 : 365;\n"
+" }\n"
+" case 3:\n"
+" return 365;\n"
+" }\n"
+"}\n";
+
+const char getDaysInYearRangeDecl[] =
+"int getDaysInYearRange( int nFrom, int nTo,int b30Days );\n";
+
+const char getDaysInYearRange[] =
+"int getDaysInYearRange( int nFrom, int nTo,int b30Days )\n"
+"{\n"
+" if( nFrom > nTo )\n"
+" return 0;\n"
+" return b30Days ? ((nTo - nFrom + 1) * 360) : GetDaysInYears( nFrom, nTo)"
+";\n"
+"}\n";
+
+const char getDiffDecl[] =
+"int getDiff(int rFrom,int rTo,int fDay,int fMonth,int fYear,int fbLastDayMode,"
+"int fbLastDay,int fb30Days,int fbUSMode,int fnDay,int tDay,int tMonth,"
+"int tYear,int tbLastDayMode,int tbLastDay,int tb30Days,"
+"int tbUSMode,int tnDay);\n";
+
+const char getDiff[] =
+"int getDiff(int rFrom,int rTo,int fDay,int fMonth,int fYear,int fbLastDayMode,"
+"int fbLastDay,int fb30Days,int fbUSMode,int fnDay,int tDay,int tMonth,"
+"int tYear,int tbLastDayMode,int tbLastDay,int tb30Days,"
+"int tbUSMode,int tnDay)\n"
+"{\n"
+" if(rFrom>rTo)\n"
+" {\n"
+" int d=fDay;fDay=tDay;tDay=d;\n"
+" int m=fMonth;fMonth=tMonth;tMonth=m;\n"
+" int y=fYear;fYear=tYear;tYear=y;\n"
+" int a=fbLastDayMode;fbLastDayMode=tbLastDayMode;tbLastDayMode=a;\n"
+" int b=fbLastDay;fbLastDay=tbLastDay;tbLastDay=b;\n"
+" int c=fb30Days;fb30Days=tb30Days;tb30Days=c;\n"
+" int e=fbUSMode;fbUSMode=tbUSMode;tbUSMode=e;\n"
+" int f=fnDay;fnDay=tnDay;tnDay=f;\n"
+" }\n"
+" int nDiff=0;\n"
+" if( tb30Days )\n"
+" {\n"
+" if( tbUSMode )\n"
+" {\n"
+" if( ((fMonth == 2) || (fnDay < 30)) && (tDay == 31) )\n"
+" tnDay = 31;\n"
+" else if( (tMonth == 2) && tbLastDay )\n"
+" tnDay = DaysInMonth( 2, tYear );\n"
+" }\n"
+" else\n"
+" {\n"
+" if( (fMonth == 2) && (fnDay == 30) )\n"
+" fnDay = DaysInMonth( 2, fYear );\n"
+" if( (tMonth == 2) && (tnDay == 30) )\n"
+" tnDay = DaysInMonth( 2, tYear );\n"
+" }\n"
+" }\n"
+" if( (fYear < tYear) || ((fYear == tYear) && (fMonth < tMonth)) )\n"
+" {\n"
+" int d = fb30Days ? 30:DaysInMonth(fMonth,fYear);\n"
+" nDiff = d- fnDay + 1;\n"
+" fDay = fnDay = 1;\n"
+" fbLastDay = 0;\n"
+" addMonths(fb30Days,fbLastDay,&fnDay,fDay,&fMonth,1,&fYear);\n"
+" if( fYear < tYear )\n"
+" {\n"
+" nDiff += getDaysInMonthRange( fMonth, 12,fb30Days,fYear);\n"
+" addMonths(fb30Days,fbLastDay,&fnDay,fDay,&fMonth,13-fMonth,&fYear"
+");\n"
+" nDiff += getDaysInYearRange( fYear, tYear - 1,fb30Days);\n"
+" fYear+=tYear - fYear;\n"
+" }\n"
+" nDiff += getDaysInMonthRange(fMonth, tMonth - 1,fb30Days ,fYear );\n"
+" addMonths(fb30Days,fbLastDay,&fnDay,fDay,&fMonth,tMonth-fMonth,&fYear"
+");\n"
+" }\n"
+" nDiff += tnDay - fnDay;\n"
+" return nDiff > 0 ? nDiff : 0;\n"
+"}\n";
+
+const char lcl_GetcoupdaybsDecl[] =
+"int lcl_Getcoupdaybs(int nNullDate,int nSettle,int nMat,int nFreq,"
+"int nBase);\n";
+
+const char lcl_Getcoupdaybs[] =
+"int lcl_Getcoupdaybs(int nNullDate,int nSettle,int nMat,int nFreq,"
+"int nBase)\n"
+"{\n"
+" int aDate = nMat;\n"
+" int rDay=0,rMonth=0, rYear=0,rbLastDayMode=0, rbLastDay=0,rb30Days=0,"
+"rbUSMode=0,rnDay=0;\n"
+" int sDay=0,sMonth=0, sYear=0,sbLastDayMode=0, sbLastDay=0,sb30Days=0,"
+"sbUSMode=0,snDay=0;\n"
+" ScaDate( nNullDate,nSettle,nBase,&sDay,&sMonth,&sYear,&sbLastDayMode,"
+"&sbLastDay,&sb30Days,&sbUSMode,&snDay);\n"
+" ScaDate( nNullDate,aDate,nBase,&rDay,&rMonth,&rYear,&rbLastDayMode,"
+"&rbLastDay,&rb30Days,&rbUSMode,&rnDay);\n"
+" rYear=sYear;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear);\n"
+" if(checklessthan(rYear,sYear,rMonth,sMonth,rnDay,snDay,rbLastDay,"
+"sbLastDay,rDay,sDay))\n"
+" {\n"
+" rYear+=1;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear );\n"
+" }\n"
+" while(checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay))\n"
+" {\n"
+" double d = -1*(12/nFreq);\n"
+" addMonths(rb30Days,rbLastDay,&rnDay,rDay,&rMonth,d,&rYear);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear );\n"
+" }\n"
+" return getDiff( aDate,nSettle+nNullDate,rDay,rMonth,rYear,rbLastDayMode,"
+"rbLastDay,rb30Days,rbUSMode,rnDay,sDay,sMonth,sYear,sbLastDayMode,sbLastDay,"
+"sb30Days,sbUSMode, snDay);\n"
+"}\n";
+
+const char lcl_GetcoupdaysDecl[] =
+"int lcl_Getcoupdays(int nNullDate,int nSettle, "
+"int nMat,int nFreq,int nBase);\n";
+
+const char lcl_Getcoupdays[] =
+"int lcl_Getcoupdays(int nNullDate,int nSettle, "
+"int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int aDate = nMat;\n"
+" int rDay=0,rMonth=0, rYear=0,rbLastDayMode=0, rbLastDay=0,rb30Days=0,"
+"rbUSMode=0,rnDay=0;\n"
+" int sDay=0,sMonth=0, sYear=0,sbLastDayMode=0, sbLastDay=0,sb30Days=0,"
+"sbUSMode=0,snDay=0;\n"
+" ScaDate( nNullDate,nSettle,nBase,&sDay,&sMonth,&sYear,&sbLastDayMode,"
+"&sbLastDay,&sb30Days,&sbUSMode,&snDay);\n"
+" ScaDate( nNullDate,aDate,nBase,&rDay,&rMonth,&rYear,&rbLastDayMode,"
+"&rbLastDay,&rb30Days,&rbUSMode,&rnDay);\n"
+" rYear=sYear;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear);\n"
+" if(checklessthan(rYear,sYear,rMonth,sMonth,rnDay,snDay,rbLastDay,"
+"sbLastDay,rDay,sDay))\n"
+" {\n"
+" rYear+=1;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear );\n"
+" }\n"
+" while(checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay))\n"
+" {\n"
+" double d = -1*12/(double)nFreq;\n"
+" addMonths(rb30Days,rbLastDay,&rnDay,rDay,&rMonth,d,&rYear);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear );\n"
+" }\n"
+" int aNextDate=aDate;int aDay=rDay,aMonth=rMonth, aYear=rYear;\n"
+" int abLastDayMode=rbLastDayMode, abLastDay=rbLastDay,ab30Days=rb30Days,"
+"abUSMode=rbUSMode,anDay=rnDay;\n"
+" int tmp = (int)(12/(double)nFreq);\n"
+" addMonths(ab30Days,abLastDay,&anDay,aDay,&aMonth,tmp,&aYear);\n"
+" return getDiff( aDate, aNextDate, rDay, rMonth, rYear, rbLastDayMode, "
+"rbLastDay, rb30Days, rbUSMode, rnDay, aDay, aMonth, aYear, abLastDayMode,"
+"abLastDay, ab30Days, abUSMode, anDay);\n"
+"}\n";
+
+const char lcl_GetcoupnumDecl[] =
+"double lcl_Getcoupnum(int nNullDate,int nSettle,int nMat,int nFreq,int"
+" nBase);\n";
+const char lcl_Getcoupnum[] =
+"double lcl_Getcoupnum(int nNullDate,int nSettle, int nMat,int nFreq,int"
+" nBase)\n"
+"{\n"
+" int aDate = nMat;\n"
+" int mDay=0,mMonth=0, mYear=0;\n"
+" int rDay=0,rMonth=0, rYear=0,rbLastDayMode=0, rbLastDay=0,rb30Days=0,"
+"rbUSMode=0,rnDay=0;\n"
+" int sDay=0,sMonth=0, sYear=0,sbLastDayMode=0, sbLastDay=0,sb30Days=0,"
+"sbUSMode=0,snDay=0;\n"
+" ScaDate( nNullDate,nSettle,nBase,&sDay,&sMonth,&sYear,&sbLastDayMode,"
+"&sbLastDay,&sb30Days,&sbUSMode,&snDay);\n"
+" ScaDate( nNullDate,aDate,nBase,&rDay,&rMonth,&rYear,&rbLastDayMode,"
+"&rbLastDay,&rb30Days,&rbUSMode,&rnDay);\n"
+" mMonth = rMonth, mYear = rYear;\n"
+" rYear=sYear;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" if(checklessthan(rYear,sYear,rMonth,sMonth,rnDay,snDay,rbLastDay,"
+"sbLastDay,rDay,sDay))\n"
+" {\n"
+" rYear+=1;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" }\n"
+" int m= checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay);\n"
+" while(m)\n"
+" {\n"
+" double d = -1*(12/nFreq);\n"
+" addMonths(rb30Days,rbLastDay,&rnDay,rDay,&rMonth,d,&rYear);\n"
+" m = checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay);\n"
+" }\n"
+" int n=(mYear-rYear)*12+mMonth-rMonth;\n"
+" double tmp = (double)(n*nFreq)/12.0;\n"
+" return tmp;\n"
+"}\n";
+
+const char setDayDecl[] =
+"void setDay(int nOrigDay, int nMonth,int nYear,int bLastDay,int b30Days,"
+"int *nDay);\n";
+const char setDay[] =
+"void setDay(int nOrigDay, int nMonth,int nYear,int bLastDay,int b30Days,"
+"int *nDay)\n"
+"{\n"
+" if( b30Days )\n"
+" {\n"
+" *nDay = min( nOrigDay, 30);\n"
+" if( bLastDay || (*nDay >= DaysInMonth( nMonth, nYear )) )\n"
+" *nDay = 30;\n"
+" }\n"
+" else\n"
+" {\n"
+" int nLastDay = DaysInMonth( nMonth, nYear );\n"
+" *nDay = bLastDay ? nLastDay : min( nOrigDay, nLastDay );\n"
+" }\n"
+"}\n";
+
+const char coupdaysDecl[] =
+"double coupdays(int nSettle,int nMat,int nFreq,int nBase);\n";
+
+const char coupdays[] =
+"double coupdays(int nSettle,int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int nNullDate=693594;\n"
+" if( nBase == 1 )\n"
+" return lcl_Getcoupdays(nNullDate, nSettle, nMat,nFreq, nBase);\n"
+" else\n"
+" return (double)GetDaysInYear(0,0,nBase)/(double)nFreq;\n"
+"}\n";
+
+const char coupdaybsDecl[] =
+"double coupdaybs( int nSettle,int nMat,int nFreq,int nBase);\n";
+
+const char coupdaybs[] =
+"double coupdaybs( int nSettle,int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int nNullDate=693594;\n"
+" return lcl_Getcoupdaybs(nNullDate, nSettle, nMat,nFreq, nBase);\n"
+"}\n";
+
+const char coupdaysncDecl[] =
+"double coupdaysnc( int nSettle,int nMat,int nFreq,int nBase);\n";
+
+const char coupdaysnc[] =
+"double coupdaysnc( int nSettle,int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int nNullDate=693594;\n"
+" if((nBase != 0) && (nBase != 4))\n"
+" {\n"
+" int aDate = nMat;\n"
+" int rDay=0,rMonth=0, rYear=0,rbLastDayMode=0, rbLastDay=0,rb30Days=0,"
+"rbUSMode=0,rnDay=0;\n"
+" int sDay=0,sMonth=0, sYear=0,sbLastDayMode=0, sbLastDay=0,sb30Days=0,"
+"sbUSMode=0,snDay=0;\n"
+" ScaDate( nNullDate,aDate,nBase,&rDay,&rMonth,&rYear,&rbLastDayMode,"
+"&rbLastDay,&rb30Days,&rbUSMode,&rnDay);\n"
+" ScaDate( nNullDate,nSettle,nBase,&sDay,&sMonth,&sYear,&sbLastDayMode,"
+"&sbLastDay,&sb30Days,&sbUSMode,&snDay);\n"
+" rYear=sYear;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear);\n"
+" if(checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,rbLastDay"
+",sDay,rDay))\n"
+" {\n"
+" rYear-=1;\n"
+" setDay(rDay,rMonth,rYear,rbLastDay,rb30Days,&rnDay);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear );\n"
+" }\n"
+" while(!checklessthan(sYear,rYear,sMonth,rMonth,snDay,rnDay,sbLastDay,"
+"rbLastDay,sDay,rDay))\n"
+" {\n"
+" addMonths(rb30Days,rbLastDay,&rnDay,rDay,&rMonth,12/nFreq,&rYear);\n"
+" aDate=DateToDays( rnDay,rMonth,rYear );\n"
+" }\n"
+" return getDiff( nSettle+nNullDate,aDate,sDay,sMonth,sYear,sbLastDayMode, "
+"sbLastDay, sb30Days, sbUSMode, snDay, rDay, rMonth, rYear, rbLastDayMode, "
+"rbLastDay, rb30Days, rbUSMode, rnDay);\n"
+" }\n"
+" else\n"
+" return coupdays(nSettle,nMat,nFreq,nBase)- coupdaybs( nSettle,"
+"nMat,nFreq,nBase);\n"
+"}\n";
+
+const char checklessthanDecl[] =
+"int checklessthan(int aYear,int bYear,int aMonth,int bMonth,int anDay,int "
+"bnDay,int abLastDay,int bbLastDay,int anOrigDay,int bnOrigDay);\n";
+const char checklessthan[] =
+"int checklessthan(int aYear,int bYear,int aMonth,int bMonth,int anDay,int "
+"bnDay,int abLastDay,int bbLastDay,int anOrigDay,int bnOrigDay)\n"
+"{\n"
+" if( aYear != bYear )\n"
+" return aYear < bYear;\n"
+" if( aMonth != bMonth )\n"
+" return aMonth < bMonth;\n"
+" if( anDay != bnDay )\n"
+" return anDay < bnDay;\n"
+" if( abLastDay || bbLastDay )\n"
+" return !abLastDay && bbLastDay;\n"
+" return anOrigDay < bnOrigDay;\n"
+"}\n";
+
+const char coupnumDecl[] =
+"double coupnum( int nSettle,int nMat,int nFreq,int nBase);\n";
+
+const char coupnum[] =
+"double coupnum( int nSettle,int nMat,int nFreq,int nBase)\n"
+"{\n"
+" int nNullDate=693594;\n"
+" return lcl_Getcoupnum(nNullDate,nSettle,nMat,nFreq,nBase);\n"
+"}\n";
+
+const char getPriceDecl[] =
+"double getPrice(int nSettle, int nMat, double fRate, double fYield,\n"
+ "double fRedemp, int nFreq, int nBase );\n";
+
+const char getPrice[] =
+"double getPrice(int nSettle, int nMat, double fRate, double fYield,\n"
+ "double fRedemp, int nFreq, int nBase )\n"
+"{\n"
+" double fFreq = nFreq;\n"
+" double fE = coupdays( nSettle, nMat, nFreq, nBase );\n"
+" double fDSC_E = coupdaysnc( nSettle, nMat, nFreq, nBase ) / fE;\n"
+" double fN = coupnum( nSettle, nMat, nFreq, nBase );\n"
+" double fA = coupdaybs( nSettle, nMat, nFreq, nBase );\n"
+" double fRet = fRedemp / ( pow( 1.0 + fYield / fFreq, fN - 1.0 + "
+"fDSC_E ) );\n"
+" fRet -= 100.0 * fRate / fFreq * fA / fE;\n"
+" double fT1 = 100.0 * fRate / fFreq;\n"
+" double fT2 = 1.0 + fYield / fFreq;\n"
+" for( double fK = 0.0 ; fK < fN ; fK+=1.0 )\n"
+" fRet += fT1 / pow( fT2, fK + fDSC_E );\n"
+" return fRet;\n"
+"}\n";
+
+const char getYield_Decl[] =
+"double getYield_( int nNullDate, int nSettle, int nMat, double fCoup,"
+"double fPrice,double fRedemp, int nFreq, int nBase);\n";
+
+const char getYield_[] =
+"double getYield_( int nNullDate, int nSettle, int nMat, double fCoup,"
+"double fPrice,double fRedemp, int nFreq, int nBase )\n"
+"{\n"
+" double fRate = fCoup;\n"
+" double fPriceN = 0.0;\n"
+" double fYield1 = 0.0;\n"
+" double fYield2 = 1.0;\n"
+" double fPrice1 = getPrice(nSettle, nMat, fRate, fYield1, fRedemp, "
+"nFreq, nBase );\n"
+" double fPrice2 = getPrice(nSettle, nMat, fRate, fYield2, fRedemp, "
+"nFreq, nBase );\n"
+" double fYieldN = ( fYield2 - fYield1 ) * 0.5;\n"
+" for( unsigned int nIter = 0 ; nIter < 100 && fPriceN != fPrice ; nIter++ "
+")\n"
+" {\n"
+" fPriceN = getPrice(nSettle, nMat, fRate, fYieldN, fRedemp, nFreq, "
+"nBase );\n"
+" if( fPrice == fPrice1 )\n"
+" return fYield1;\n"
+" else if( fPrice == fPrice2 )\n"
+" return fYield2;\n"
+" else if( fPrice == fPriceN )\n"
+" return fYieldN;\n"
+" else if( fPrice < fPrice2 )\n"
+" {\n"
+" fYield2 *= 2.0;\n"
+" fPrice2 = getPrice(nSettle, nMat, fRate, fYield2, fRedemp, nFreq"
+", nBase );\n"
+" fYieldN = ( fYield2 - fYield1 ) * 0.5;\n"
+" }\n"
+" else\n"
+" {\n"
+" if( fPrice < fPriceN )\n"
+" {\n"
+" fYield1 = fYieldN;\n"
+" fPrice1 = fPriceN;\n"
+" }\n"
+" else\n"
+" {\n"
+" fYield2 = fYieldN;\n"
+" fPrice2 = fPriceN;\n"
+" }\n"
+" fYieldN = fYield2 - ( fYield2 - fYield1 ) * ( ( fPrice - fPrice2 "
+") / ( fPrice1 - fPrice2 ) );\n"
+" }\n"
+" }\n"
+" return fYieldN;\n"
+"}\n";
+
+const char GetYieldmatDecl[] =
+ "double GetYieldmat( int nNullDate, int nSettle, int nMat, int nIssue,\n"
+ "double fRate, double fPrice, int nBase );\n";
+
+const char GetYieldmat[] =
+ "double GetYieldmat( int nNullDate, int nSettle, int nMat, int nIssue,\n"
+ "double fRate, double fPrice, int nBase )\n"
+"{\n"
+" double fIssMat = GetYearFrac( nNullDate, nIssue, nMat, nBase );\n"
+" double fIssSet = GetYearFrac( nNullDate, nIssue, nSettle, nBase );\n"
+" double fSetMat = GetYearFrac( nNullDate, nSettle, nMat, nBase );\n"
+" double y = 1.0 + fIssMat * fRate;\n"
+" y =y / (fPrice / 100.0 + fIssSet * fRate);\n"
+" y-=1.0;\n"
+" y = y / fSetMat;\n"
+" return y;\n"
+"}\n";
+
+const char GetDiffDate360_Decl[] =
+"int GetDiffDate360_(\n"
+" int nDay1, int nMonth1, int nYear1, bool bLeapYear1,\n"
+" int nDay2, int nMonth2, int nYear2,\n"
+" bool bUSAMethod );\n";
+
+const char GetDiffDate360_[] =
+"int GetDiffDate360_(\n"
+" int nDay1, int nMonth1, int nYear1, bool bLeapYear1,\n"
+" int nDay2, int nMonth2, int nYear2,\n"
+" bool bUSAMethod )\n"
+"{\n"
+" if( nDay1 == 31 )\n"
+" nDay1--;\n"
+" else if( bUSAMethod && ( nMonth1 == 2 && ( nDay1 == 29 || ( nDay1 == 28 "
+"&& !bLeapYear1 ) ) ) )\n"
+" nDay1 = 30;\n"
+" if( nDay2 == 31 )\n"
+" {\n"
+" if( bUSAMethod && nDay1 != 30 )\n"
+" {\n"
+" nDay2 = 1;\n"
+" if( nMonth2 == 12 )\n"
+" {\n"
+" nYear2++;\n"
+" nMonth2 = 1;\n"
+" }\n"
+" else\n"
+" nMonth2++;\n"
+" }\n"
+" else\n"
+" nDay2 = 30;\n"
+" }\n"
+" return nDay2 + nMonth2 * 30 + nYear2 * 360 - nDay1 - nMonth1 * 30 - "
+"nYear1 * 360;\n"
+"}\n";
+
+const char GetDiffDate360Decl[] =
+"int GetDiffDate360( int nNullDate, int nDate1, int nDate2,"
+"bool bUSAMethod);\n";
+
+const char GetDiffDate360[] =
+"int GetDiffDate360( int nNullDate, int nDate1, int nDate2,"
+"bool bUSAMethod )\n"
+"{\n"
+" nDate1 += nNullDate;\n"
+" nDate2 += nNullDate;\n"
+" int nDay1, nMonth1, nYear1, nDay2, nMonth2, nYear2;\n"
+" DaysToDate( nDate1, &nDay1, &nMonth1, &nYear1 );\n"
+" DaysToDate( nDate2, &nDay2, &nMonth2, &nYear2 );\n"
+" return GetDiffDate360_( nDay1, nMonth1, nYear1, IsLeapYear( nYear1 ), "
+"nDay2, nMonth2, nYear2, bUSAMethod );\n"
+"}\n";
+
+const char GetDurationDecl[] =
+"double GetDuration( \n"
+" int nNullDate, int nSettle, int nMat, double fCoup,\n"
+" double fYield, int nFreq, int nBase );\n";
+
+const char GetDuration[] =
+"double GetDuration( \n"
+" int nNullDate, int nSettle, int nMat, double fCoup,\n"
+" double fYield, int nFreq, int nBase )\n"
+" {\n"
+" double fYearfrac = GetYearFrac(nNullDate,nSettle,nMat,nBase);\n"
+" double fNumOfCoups = lcl_Getcoupnum(nNullDate,nSettle,nMat,"
+"nFreq,nBase);\n"
+" double fDur = 0.0;\n"
+" fCoup = fCoup * 100.0 / nFreq;\n"
+" fYield = fYield / nFreq;\n"
+" fYield += 1.0;\n"
+" double nDiff = fYearfrac * nFreq - fNumOfCoups;\n"
+" int t;\n"
+" double tmp0 = 0, tmp1 = 0, tmp2 = 0;\n"
+" for( t = 1 ; t < fNumOfCoups ; t++ ){\n"
+" tmp0 = (t + nDiff) * ( fCoup ) ;\n"
+" tmp1 = pow( fYield, t + nDiff ) ;\n"
+" tmp2 = tmp0 / tmp1;\n"
+" fDur += tmp2;\n"
+" }\n"
+" fDur += (fNumOfCoups + nDiff) * (fCoup + 100.0) * pow(pow(fYield,"
+" fNumOfCoups + nDiff ),-1);\n"
+" double p = 0.0;\n"
+" for( t = 1 ; t < fNumOfCoups ; t++ ){\n"
+" tmp0 = pow( fYield, t + nDiff );\n"
+" p += fCoup / tmp0;}\n"
+" p += (fCoup + 100.0) / pow(fYield, fNumOfCoups + nDiff);\n"
+" fDur = fDur / p;\n"
+" fDur = fDur / nFreq;\n"
+" return fDur;\n"
+" }\n";
+
+const char ScGetDDBDecl[] =
+"double ScGetDDB(double fCost, double fSalvage, double fLife, double fPeriod,"
+"double fFactor);\n";
+
+const char ScGetDDB[] =
+"double ScGetDDB(double fCost, double fSalvage, double fLife, double fPeriod,"
+"double fFactor)\n"
+"{\n"
+" double fDdb, fRate, fOldValue, fNewValue;\n"
+" fRate = fFactor / fLife;\n"
+" if (fRate >= 1.0)\n"
+" {\n"
+" fRate = 1.0;\n"
+" if (fPeriod == 1.0)\n"
+" fOldValue = fCost;\n"
+" else\n"
+" fOldValue = 0.0;\n"
+" }\n"
+" else\n"
+" fOldValue = fCost * pow(1.0 - fRate, fPeriod - 1.0);\n"
+" fNewValue = fCost * pow(1.0 - fRate, fPeriod);\n"
+
+" if (fNewValue < fSalvage)\n"
+" fDdb = fOldValue - fSalvage;\n"
+" else\n"
+" fDdb = fOldValue - fNewValue;\n"
+" if (fDdb < 0.0)\n"
+" fDdb = 0.0;\n"
+" return fDdb;\n"
+"}\n";
+
+const char ScInterVDBDecl[] =
+"double ScInterVDB(double fCost, double fSalvage, double fLife, double fLife1,"
+"double fPeriod, double fFactor);\n";
+
+const char ScInterVDB[] =
+"double ScInterVDB(double fCost, double fSalvage, double fLife, double fLife1,"
+"double fPeriod, double fFactor)\n"
+"{\n"
+" double fVdb=0;\n"
+" double fIntEnd = ceil(fPeriod);\n"
+" int nLoopEnd = fIntEnd;\n"
+
+" double fTerm, fSln;\n"
+" double fSalvageValue = fCost - fSalvage;\n"
+" int nNowSln = 0;\n"
+" double fDdb;\n"
+" int i;\n"
+" fSln=0;\n"
+" for ( i = 1; i <= nLoopEnd; i++)\n"
+" {\n"
+" if(!nNowSln)\n"
+" {\n"
+" fDdb = ScGetDDB(fCost, fSalvage, fLife, (double) i, fFactor);\n"
+" fSln = fSalvageValue/ (fLife1 - (double) (i-1));\n"
+" if (fSln > fDdb)\n"
+" {\n"
+" fTerm = fSln;\n"
+" nNowSln = 1;\n"
+" }\n"
+" else\n"
+" {\n"
+" fTerm = fDdb;\n"
+" fSalvageValue =fSalvageValue- fDdb;\n"
+" }\n"
+" }\n"
+" else\n"
+" {\n"
+" fTerm = fSln;\n"
+" }\n"
+
+" if ( i == nLoopEnd)\n"
+" fTerm *= ( fPeriod + 1.0 - fIntEnd );\n"
+
+" fVdb += fTerm;\n"
+" }\n"
+" return fVdb;\n"
+"}\n";
+
+const char VDBImplementDecl[] =
+"double VDBImplement(double fCost, double fSalvage, double fLife, double fStart"
+", double fEnd, double fFactor, bool bNoSwitch);\n";
+
+const char VDBImplement[] =
+"double VDBImplement(double fCost, double fSalvage, double fLife, double fStart"
+", double fEnd, double fFactor, bool bNoSwitch)\n"
+"{\n"
+" double fIntStart = floor(fStart);\n"
+" double fIntEnd = ceil(fEnd);\n"
+" long nLoopStart = (long)(fIntStart);\n"
+" long nLoopEnd = (long)(fIntEnd);\n"
+"\n"
+" double fVdb = 0.0;\n"
+" if (bNoSwitch)\n"
+" {\n"
+" for (long i = nLoopStart + 1; i <= nLoopEnd; i++)\n"
+" {\n"
+" double fTerm = ScGetDDB(fCost, fSalvage, fLife, i, fFactor);\n"
+"\n"
+" if ( i == nLoopStart+1 )\n"
+" fTerm *= ( min( fEnd, fIntStart + 1.0 ) - fStart );\n"
+" else if ( i == nLoopEnd )\n"
+" fTerm *= ( fEnd + 1.0 - fIntEnd );\n"
+"\n"
+" fVdb += fTerm;\n"
+" }\n"
+" }\n"
+" else\n"
+" {\n"
+" double fPart = 0.0;\n"
+" if ( !approx_equal( fStart, fIntStart ) ||\n"
+" !approx_equal( fEnd, fIntEnd ) )\n"
+" {\n"
+" if ( !approx_equal( fStart, fIntStart ) )\n"
+" {\n"
+" double fTempIntEnd = fIntStart + 1.0;\n"
+" double fTempValue = fCost -\n"
+" ScInterVDB( fCost, fSalvage, fLife, fLife, fIntStart, fFactor );\n"
+" fPart += ( fStart - fIntStart ) *\n"
+" ScInterVDB( fTempValue, fSalvage, fLife, fLife - fIntStart,\n"
+" fTempIntEnd - fIntStart, fFactor);\n"
+" }\n"
+" if ( !approx_equal( fEnd, fIntEnd ) )\n"
+" {\n"
+" double fTempIntStart = fIntEnd - 1.0;\n"
+" double fTempValue = fCost -\n"
+" ScInterVDB( fCost, fSalvage, fLife, fLife, fTempIntStart, fFactor );\n"
+" fPart += ( fIntEnd - fEnd ) *\n"
+" ScInterVDB( fTempValue, fSalvage, fLife, fLife - fTempIntStart,\n"
+" fIntEnd - fTempIntStart, fFactor);\n"
+" }\n"
+" }\n"
+" fCost -= ScInterVDB( fCost, fSalvage, fLife, fLife, fIntStart, fFactor );\n"
+" fVdb = ScInterVDB( fCost, fSalvage, fLife, fLife - fIntStart,\n"
+" fIntEnd - fIntStart, fFactor);\n"
+" fVdb -= fPart;\n"
+" }\n"
+" return fVdb;\n"
+"}\n";
+
+const char GetOddlpriceDecl[] =
+"double GetOddlprice( int nNullDate, int nSettle, int nMat, int nLastCoup,\n"
+" double fRate, double fYield, double fRedemp, int nFreq, int nBase );\n";
+
+const char GetOddlprice[] =
+"double GetOddlprice( int nNullDate, int nSettle, int nMat, int nLastCoup,\n"
+" double fRate, double fYield, double fRedemp, int nFreq, int nBase )\n"
+"{\n"
+" double fFreq = nFreq ;\n"
+" double fDCi = GetYearFrac( nNullDate, nLastCoup,"
+"nMat, nBase ) * fFreq;\n"
+" double fDSCi = GetYearFrac( nNullDate, nSettle,"
+"nMat, nBase ) * fFreq;\n"
+" double fAi = GetYearFrac( nNullDate, nLastCoup,"
+"nSettle, nBase ) * fFreq;\n"
+" double p = fRedemp + fDCi * 100.0 * fRate / fFreq;\n"
+" p /= fDSCi * fYield / fFreq + 1.0;\n"
+" p -= fAi * 100.0 * fRate / fFreq;\n"
+" return p;\n"
+"}\n";
+
+const char GetOddlyieldDecl[] =
+"double GetOddlyield( int nNullDate, int nSettle, int nMat, int nLastCoup,\n"
+" double fRate, double fPrice, double fRedemp, int nFreq, int nBase );\n";
+
+const char GetOddlyield[] =
+"double GetOddlyield( int nNullDate, int nSettle, int nMat, int nLastCoup,\n"
+" double fRate, double fPrice, double fRedemp, int nFreq, int nBase ) \n"
+"{\n"
+" double fFreq = nFreq ;\n"
+" double fDCi= GetYearFrac( nNullDate, nLastCoup, nMat, nBase ) * fFreq;\n"
+" double fDSCi= GetYearFrac( nNullDate, nSettle, nMat, nBase ) * fFreq;\n"
+" double fAi= GetYearFrac( nNullDate, nLastCoup, nSettle, nBase )*fFreq;\n"
+" double y = fRedemp + fDCi * 100.0 * fRate / fFreq;\n"
+" y /= fPrice + fAi * 100.0 * fRate / fFreq;\n"
+" y -= 1.0;\n"
+" y *= fFreq / fDSCi;\n"
+" return y;\n"
+"}\n";
+
+const char GetYearFracDecl[] =
+"double GetYearFrac( int nNullDate, int nStartDate, int nEndDate,"
+"int nMode );\n";
+
+const char GetYearFrac[] =
+"double GetYearFrac( int nNullDate, int nStartDate, int nEndDate,"
+"int nMode ) \n"
+"{\n"
+" if( nStartDate == nEndDate )\n"
+" return 0.0; \n"
+" if( nStartDate > nEndDate )\n"
+" {\n"
+" int n = nEndDate;\n"
+" nEndDate = nStartDate;\n"
+" nStartDate = n;\n"
+" }\n"
+" int nDate1 = nStartDate + nNullDate;\n"
+" int nDate2 = nEndDate + nNullDate;\n"
+" int nDay1, nDay2;\n"
+" int nMonth1, nMonth2;\n"
+" int nYear1, nYear2;\n"
+" DaysToDate( nDate1, &nDay1, &nMonth1, &nYear1 );\n"
+" DaysToDate( nDate2, &nDay2, &nMonth2, &nYear2 );\n"
+" int nDayDiff;\n"
+" switch( nMode )\n"
+" {\n"
+" case 0: \n"
+" if ( nDay1 == 31 )\n"
+" {\n"
+" nDay1--;\n"
+" }\n"
+" if ( nDay1 == 30 && nDay2 == 31 )\n"
+" {\n"
+" nDay2--;\n"
+" }\n"
+" else\n"
+" {\n"
+" if ( nMonth1 == 2 && nDay1 == "
+"( IsLeapYear( nYear1 ) ? 29 : 28 ) )\n"
+" {\n"
+" nDay1 = 30;\n"
+" if ( nMonth2 == 2 && nDay2 == "
+"( IsLeapYear( nYear2 ) ? 29 : 28 ) )\n"
+" {\n"
+" nDay2 = 30;\n"
+" }\n"
+" }\n"
+" }\n"
+" nDayDiff = ( nYear2 - nYear1 ) * 360 + "
+"( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );\n"
+" break;\n"
+" case 1: \n"
+" case 2: \n"
+" case 3: \n"
+" nDayDiff = nDate2 - nDate1;\n"
+" break;\n"
+" case 4: \n"
+" if ( nDay1 == 31 )\n"
+" {\n"
+" nDay1--;\n"
+" }\n"
+" if ( nDay2 == 31 )\n"
+" {\n"
+" nDay2--;\n"
+" }\n"
+" nDayDiff = ( nYear2 - nYear1 ) * 360 + "
+"( nMonth2 - nMonth1 ) * 30 + ( nDay2 - nDay1 );\n"
+" break;\n"
+" }\n"
+" double nDaysInYear;\n"
+" switch( nMode )\n"
+" {\n"
+" case 0: \n"
+" case 2: \n"
+" case 4: \n"
+" nDaysInYear = 360;\n"
+" break;\n"
+" case 1: \n"
+" {\n"
+" bool isYearDifferent = ( nYear1 != nYear2 );\n"
+" if ( isYearDifferent &&\n"
+" ( ( nYear2 != nYear1 + 1 ) ||\n"
+" ( nMonth1 < nMonth2 ) ||\n"
+" ( nMonth1 == nMonth2 && nDay1 < nDay2 ) ) )\n"
+" {\n"
+" int nDayCount = 0;\n"
+" for ( int i = nYear1; i <= nYear2; i++ )\n"
+" nDayCount += ( IsLeapYear( i ) ? 366 : 365 );\n"
+" nDaysInYear = ( double ) nDayCount / "
+"( double ) ( nYear2 - nYear1 + 1 );\n"
+" }\n"
+" else\n"
+" {\n"
+" if ( isYearDifferent && IsLeapYear( nYear1 ) )\n"
+" {\n"
+" nDaysInYear = 366;\n"
+" }\n"
+" else\n"
+" {\n"
+" if ( ( IsLeapYear( nYear1 ) && nMonth1 <= 2 "
+"&& nDay1 <= 29 ) ||\n"
+" ( IsLeapYear( nYear2 ) && ( nMonth2 > 3 || "
+"( nMonth2 == 2 && nDay1 == 29 ) ) ) )\n"
+" {\n"
+" nDaysInYear = 366;\n"
+" }\n"
+" else\n"
+" {\n"
+" nDaysInYear = 365;\n"
+" for ( int i = nYear1; i <= nYear2; i++ )\n"
+" {\n"
+" if ( IsLeapYear( i ) )\n"
+" {\n"
+" nDaysInYear = 366;\n"
+" break;\n"
+" }\n"
+" }\n"
+" }\n"
+" }\n"
+" }\n"
+" }\n"
+" break;\n"
+" case 3: \n"
+" nDaysInYear = 365;\n"
+" break;\n"
+" }\n"
+" return (double)( nDayDiff ) / (nDaysInYear);\n"
+"}\n";
+
+const char DaysToDateDecl[] =
+"void DaysToDate( int nDays, int *rDay, int* rMonth, int* rYear );\n";
+
+const char DaysToDate[] =
+"void DaysToDate( int nDays, int *rDay, int* rMonth, int* rYear )\n"
+"{\n"
+" int nTempDays;\n"
+" int i = 0;\n"
+" bool bCalc;\n"
+" do\n"
+" {\n"
+" nTempDays = nDays;\n"
+" *rYear = (int)((nTempDays / 365) - i);\n"
+" nTempDays -= ((int) *rYear -1) * 365;\n"
+" nTempDays -= ((*rYear -1) / 4) - ((*rYear -1) / 100) + ((*rYear -1)"
+" / 400);\n"
+" bCalc = false;\n"
+" if ( nTempDays < 1 )\n"
+" {\n"
+" i++;\n"
+" bCalc = true;\n"
+" }\n"
+" else\n"
+" {\n"
+" if ( nTempDays > 365 )\n"
+" {\n"
+" if ( (nTempDays != 366) || !IsLeapYear( *rYear ) )\n"
+" {\n"
+" i--;\n"
+" bCalc = true;\n"
+" }\n"
+" }\n"
+" }\n"
+" }\n"
+" while ( bCalc );\n"
+" if(nTempDays != 0){\n"
+" for (*rMonth = 1; (int)nTempDays > DaysInMonth( *rMonth, *rYear );"
+" *rMonth += 1)\n"
+" {\n"
+" nTempDays -= DaysInMonth( *rMonth, *rYear ); \n"
+" }\n"
+" *rDay = (int)nTempDays;\n"
+" }\n"
+"}\n";
+
+const char DaysToDate_LocalBarrierDecl[] =
+"void DaysToDate( int nDays, int *rDay, int* rMonth, int* rYear );\n";
+
+const char DaysToDate_LocalBarrier[] =
+"void DaysToDate( int nDays, int *rDay, int* rMonth, int* rYear )\n"
+"{\n"
+" int nTempDays;\n"
+" int i = 0;\n"
+" bool bCalc;\n"
+" do\n"
+" {\n"
+" nTempDays = nDays;\n"
+" *rYear = (int)((nTempDays / 365) - i);\n"
+" nTempDays -= ((int) *rYear -1) * 365;\n"
+" nTempDays -= ((*rYear -1) / 4) - ((*rYear -1) / 100) + ((*rYear -1)"
+" / 400);\n"
+" bCalc = false;\n"
+" if ( nTempDays < 1 )\n"
+" {\n"
+" i++;\n"
+" bCalc = true;\n"
+" }\n"
+" else\n"
+" {\n"
+" if ( nTempDays > 365 )\n"
+" {\n"
+" if ( (nTempDays != 366) || !IsLeapYear( *rYear ) )\n"
+" {\n"
+" i--;\n"
+" bCalc = true;\n"
+" }\n"
+" }\n"
+" }\n"
+" }\n"
+" while ( bCalc );\n"
+" barrier(CLK_LOCAL_MEM_FENCE);\n"
+" if(nTempDays != 0){\n"
+" for (*rMonth = 1; (int)nTempDays > DaysInMonth( *rMonth, *rYear );"
+" *rMonth += 1)\n"
+" {\n"
+" nTempDays -= DaysInMonth( *rMonth, *rYear ); \n"
+" }\n"
+" *rDay = (int)nTempDays;\n"
+" }\n"
+"}\n";
+
+const char GetYearDiffDecl[] =
+"double GetYearDiff( int nNullDate, int nStartDate, int nEndDate,"
+"int nMode);\n";
+
+const char GetYearDiff[] =
+"double GetYearDiff( int nNullDate, int nStartDate, int nEndDate,"
+"int nMode )\n"
+"{\n"
+" int nDays1stYear;\n"
+" int nTotalDays = GetDiffDate( nNullDate, nStartDate, nEndDate,"
+"nMode, &"
+"nDays1stYear );\n"
+" return (double)(nTotalDays) / (double)nDays1stYear;\n"
+"}\n";
+
+const char GetDiffDateDecl[] =
+"int GetDiffDate( int nNullDate, int nStartDate, int nEndDate, int nMode,"
+" int* pOptDaysIn1stYear );\n";
+
+const char GetDiffDate[] =
+"int GetDiffDate( int nNullDate, int nStartDate, int nEndDate, int nMode,"
+" int* pOptDaysIn1stYear )\n"
+"{\n"
+" bool bNeg = nStartDate > nEndDate;\n"
+" if( bNeg )\n"
+" {\n"
+" int n = nEndDate;\n"
+" nEndDate = nStartDate;\n"
+" nStartDate = n;\n"
+" }\n"
+" int nRet;\n"
+" switch( nMode )\n"
+" {\n"
+" case 0: \n"
+" case 4: \n"
+" {\n"
+" int nD1, nM1, nY1, nD2, nM2, nY2;\n"
+" nStartDate += nNullDate;\n"
+" nEndDate += nNullDate;\n"
+" DaysToDate( nStartDate, &nD1, &nM1, &nY1 );\n"
+" DaysToDate( nEndDate, &nD2, &nM2, &nY2 );\n"
+" bool bLeap = IsLeapYear( nY1 );\n"
+" int nDays, nMonths;\n"
+" nMonths = nM2 - nM1;\n"
+" nDays = nD2 - nD1;\n"
+" nMonths += ( nY2 - nY1 ) * 12;\n"
+" nRet = nMonths * 30 + nDays;\n"
+" if( nMode == 0 && nM1 == 2 && nM2 != 2 && nY1 == nY2 )\n"
+" nRet -= bLeap? 1 : 2;\n"
+" if( pOptDaysIn1stYear )\n"
+" *pOptDaysIn1stYear = 360;\n"
+" }\n"
+" break;\n"
+" case 1: \n"
+" if( pOptDaysIn1stYear )\n"
+" {\n"
+" int nD, nM, nY;\n"
+" DaysToDate( nStartDate + nNullDate, &nD, &nM, &nY );\n"
+" *pOptDaysIn1stYear = IsLeapYear( nY )? 366 : 365;\n"
+" }\n"
+" nRet = nEndDate - nStartDate;\n"
+" break;\n"
+" case 2: \n"
+" nRet = nEndDate - nStartDate;\n"
+" if( pOptDaysIn1stYear )\n"
+" *pOptDaysIn1stYear = 360;\n"
+" break;\n"
+" case 3: \n"
+" nRet = nEndDate - nStartDate;\n"
+" if( pOptDaysIn1stYear )\n"
+" *pOptDaysIn1stYear = 365;\n"
+" break;\n"
+" }\n"
+" return bNeg? -nRet : nRet;\n"
+"}\n";
+
+const char RateIterationDecl[] =
+"bool RateIteration( double fNper, double fPayment, double fPv,"
+" double fFv, bool bPayType, double* fGuess );\n";
+
+const char RateIteration[] =
+"bool RateIteration( double fNper, double fPayment, double fPv,"
+" double fFv, bool bPayType, double* fGuess )\n"
+"{\n"
+"#define SCdEpsilon 1.0E-7\n"
+" bool bValid = true, bFound = false;\n"
+" double fX, fXnew, fTerm, fTermDerivation;\n"
+" double fGeoSeries, fGeoSeriesDerivation;\n"
+" const int nIterationsMax = 150;\n"
+" int nCount = 0;\n"
+" const double fEpsilonSmall = 1.0E-14;\n"
+" if ( bPayType )\n"
+" {\n"
+" fFv = fFv - fPayment;\n"
+" fPv = fPv + fPayment;\n"
+" }\n"
+" if (fNper == round( fNper ))\n"
+" {\n"
+" fX = *fGuess;\n"
+" while (!bFound && nCount < nIterationsMax)\n"
+" {\n"
+" double fPowN, fPowNminus1;\n"
+" fPowNminus1 = pow( 1.0+fX, fNper-1.0);\n"
+" fPowN = fPowNminus1 * (1.0+fX);\n"
+" if (fX == 0.0)\n"
+" {\n"
+" fGeoSeries = fNper;\n"
+" fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;\n"
+" }\n"
+" else\n"
+" {\n"
+" fGeoSeries = (fPowN-1.0)/fX;\n"
+" fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX;\n"
+" }\n"
+" fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries;\n"
+" fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation;\n"
+" if (fabs(fTerm) < fEpsilonSmall)\n"
+" bFound = true;\n"
+" else\n"
+" {\n"
+" if (fTermDerivation == 0.0)\n"
+" fXnew = fX + 1.1 * SCdEpsilon;\n"
+" else\n"
+" fXnew = fX - fTerm / fTermDerivation;\n"
+" nCount++;\n"
+" bFound = (fabs(fXnew - fX) < SCdEpsilon);\n"
+" fX = fXnew;\n"
+" }\n"
+" }\n"
+" bValid = (fX > -1.0);\n"
+" }\n"
+" else\n"
+" {\n"
+" fX = (*fGuess < -1.0) ? -1.0 : *fGuess;\n"
+" while (bValid && !bFound && nCount < nIterationsMax)\n"
+" {\n"
+" if (fX == 0.0)\n"
+" {\n"
+" fGeoSeries = fNper;\n"
+" fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;\n"
+" }\n"
+" else\n"
+" {\n"
+" fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX;\n"
+" fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX;\n"
+" }\n"
+" fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries;\n"
+" fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation;\n"
+" if (fabs(fTerm) < fEpsilonSmall)\n"
+" bFound = true;\n"
+" else\n"
+" {\n"
+" if (fTermDerivation == 0.0)\n"
+" fXnew = fX + 1.1 * SCdEpsilon;\n"
+" else\n"
+" fXnew = fX - fTerm / fTermDerivation;\n"
+" nCount++;\n"
+" bFound = (fabs(fXnew - fX) < SCdEpsilon);\n"
+" fX = fXnew;\n"
+" bValid = (fX >= -1.0);\n"
+" }\n"
+" }\n"
+" }\n"
+" *fGuess = fX;\n"
+" return bValid && bFound;\n"
+"}\n";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_logical.cxx b/sc/source/core/opencl/op_logical.cxx
new file mode 100644
index 0000000000..3a3869d591
--- /dev/null
+++ b/sc/source/core/opencl/op_logical.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/.
+ */
+
+#include "op_logical.hxx"
+
+#include <formula/vectortoken.hxx>
+#include <sstream>
+
+using namespace formula;
+
+namespace sc::opencl {
+
+void OpLogicalBinaryOperator::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " bool t = false;\n";
+ for(size_t j = 0; j< vSubArguments.size(); j++)
+ {
+ GenerateArg( j, vSubArguments, ss );
+ ss << " t = t " << openclOperator() << " (arg" << j << " != 0);\n";
+ }
+ ss << " return t;\n";
+ ss << "}\n";
+}
+
+void OpAnd::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " bool t = true;\n";
+ for(size_t j = 0; j< vSubArguments.size(); j++)
+ {
+ GenerateArg( j, vSubArguments, ss, EmptyIsNan );
+ // AND() with a svSingleVectorRef pointing to an empty cell skips that cell.
+ // See ScInterpreter::ScAnd().
+ ss << " if( !isnan( arg" << j << " ))\n";
+ ss << " t = t " << openclOperator() << " (arg" << j << " != 0);\n";
+ }
+ ss << " return t;\n";
+ ss << "}\n";
+}
+
+void OpNot::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << " return arg0 == 0;\n";
+ ss << "}\n";
+}
+
+void OpIf::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ if(vSubArguments.size()>1)
+ GenerateArg( 1, vSubArguments, ss );
+ else
+ ss << " double arg1 = 1;\n";
+ if(vSubArguments.size()>2)
+ GenerateArg( 2, vSubArguments, ss );
+ else
+ ss << " double arg2 = 0;\n";
+
+ ss << " if(arg0 != 0)\n";
+ ss << " return arg1;\n";
+ ss << " else\n";
+ ss << " return arg2;\n";
+ ss << "}\n";
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_logical.hxx b/sc/source/core/opencl/op_logical.hxx
new file mode 100644
index 0000000000..15659a0625
--- /dev/null
+++ b/sc/source/core/opencl/op_logical.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+/// Implements OpAnd, OpOr, OpXor.
+class OpLogicalBinaryOperator : public Normal
+{
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual bool canHandleMultiVector() const override { return true; }
+ /// The C operator implementing the function.
+ virtual const char* openclOperator() const = 0;
+};
+
+class OpAnd: public OpLogicalBinaryOperator
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "And"; }
+ virtual const char* openclOperator() const override { return "&&"; };
+};
+
+class OpOr: public OpLogicalBinaryOperator
+{
+public:
+ virtual std::string BinFuncName() const override { return "Or"; }
+ virtual const char* openclOperator() const override { return "||"; };
+};
+
+class OpNot: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Not"; }
+};
+
+class OpXor: public OpLogicalBinaryOperator
+{
+public:
+ virtual std::string BinFuncName() const override { return "Xor"; }
+ virtual const char* openclOperator() const override { return "^"; };
+};
+
+class OpIf:public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "IF"; }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_math.cxx b/sc/source/core/opencl/op_math.cxx
new file mode 100644
index 0000000000..d0e033f884
--- /dev/null
+++ b/sc/source/core/opencl/op_math.cxx
@@ -0,0 +1,1657 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "op_math.hxx"
+
+#include <formula/vectortoken.hxx>
+#include "op_math_helpers.hxx"
+#include <sstream>
+
+using namespace formula;
+
+namespace sc::opencl {
+
+void OpMathOneArgument::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateCode( ss );
+ ss << "}";
+}
+
+void OpMathTwoArguments::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateCode( ss );
+ ss << "}";
+}
+
+void OpCos::GenerateCode( outputstream& ss ) const
+{
+ ss << " return cos(arg0);\n";
+}
+
+void OpSec::GenerateCode( outputstream& ss ) const
+{
+ ss << " return 1.0 / cos(arg0);\n";
+}
+
+void OpSecH::GenerateCode( outputstream& ss ) const
+{
+ ss << " return 1.0 / cosh(arg0);\n";
+}
+
+void OpCosh::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(local_coshDecl);
+ funs.insert(local_cosh);
+}
+
+void OpCosh::GenerateCode( outputstream& ss ) const
+{
+ ss << " return local_cosh(arg0);\n";
+}
+
+void OpCot::GenerateCode( outputstream& ss ) const
+{
+ ss << " arg0 = arg0 * M_1_PI;\n";
+ ss << " return cospi(arg0) / sinpi(arg0);\n";
+}
+
+void OpCoth::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(local_cothDecl);
+ funs.insert(local_coth);
+}
+
+void OpCoth::GenerateCode( outputstream& ss ) const
+{
+ ss << " return local_coth(arg0);\n";
+}
+
+void OpEven::GenerateCode( outputstream& ss ) const
+{
+ ss << " double tmp = fabs(arg0 / 2);\n";
+ ss << " if ( trunc(tmp) == tmp )\n";
+ ss << " tmp = tmp * 2;\n";
+ ss << " else\n";
+ ss << " tmp = (trunc(tmp) + 1) * 2;\n";
+ ss << " if (arg0 < 0)\n";
+ ss << " tmp = tmp * -1.0;\n";
+ ss << " return tmp;\n";
+}
+
+void OpCsc::GenerateCode( outputstream& ss ) const
+{
+ ss << " return 1/sin(arg0);\n";
+}
+
+void OpCscH::GenerateCode( outputstream& ss ) const
+{
+ ss << " return 1/sinh(arg0);\n";
+}
+
+void OpExp::GenerateCode( outputstream& ss ) const
+{
+ ss << " return pow(M_E, arg0);\n";
+}
+
+void OpLog10::GenerateCode( outputstream& ss ) const
+{
+ ss << " return log10(arg0);\n";
+}
+
+void OpSinh::GenerateCode( outputstream& ss ) const
+{
+ ss << " return ( exp(arg0)-exp(-arg0) )/2;\n";
+}
+
+void OpSin::GenerateCode( outputstream& ss ) const
+{
+ ss << " arg0 = arg0 * M_1_PI;\n";
+ ss << " return sinpi(arg0);\n";
+}
+
+void OpAbs::GenerateCode( outputstream& ss ) const
+{
+ ss << " return fabs(arg0);\n";
+}
+
+void OpArcCos::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(atan2Decl);
+ funs.insert(atan2Content);
+}
+
+void OpArcCos::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arctan2(sqrt(1.0 - pow(arg0, 2)), arg0);\n";
+}
+
+void OpArcCosHyp::GenerateCode( outputstream& ss ) const
+{
+ ss << " if( arg0 < 1 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return log( arg0 + pow( (pown(arg0, 2) - 1.0), 0.5));\n";
+}
+
+void OpTan::GenerateCode( outputstream& ss ) const
+{
+ ss << " arg0 = arg0 * M_1_PI;\n";
+ ss << " return sinpi(arg0) / cospi(arg0);\n";
+}
+
+void OpTanH::GenerateCode( outputstream& ss ) const
+{
+ ss << " return tanh(arg0);\n";
+}
+
+void OpSqrt::GenerateCode( outputstream& ss ) const
+{
+ ss << " if( arg0 < 0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return sqrt(arg0);\n";
+}
+
+void OpArcCot::GenerateCode( outputstream& ss ) const
+{
+ ss << " return M_PI_2 - atan(arg0);\n";
+}
+
+void OpArcCotHyp::GenerateCode( outputstream& ss ) const
+{
+ ss << " return 0.5 * log(1 + 2 / (arg0 - 1.0));\n";
+}
+
+void OpArcSin::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(atan2Decl);
+ funs.insert(atan2Content);
+}
+
+void OpArcSin::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arctan2(arg0, sqrt(1.0 - pow(arg0, 2)));\n";
+}
+
+void OpArcSinHyp::GenerateCode( outputstream& ss ) const
+{
+ ss << " return log( arg0 + pow((pown(arg0, 2) + 1.0), 0.5));\n";
+}
+
+void OpArcTan::GenerateCode( outputstream& ss ) const
+{
+ ss << " return atan(arg0);\n";
+}
+
+void OpArcTanH::GenerateCode( outputstream& ss ) const
+{
+ ss << " double a = 1.0 + arg0;\n";
+ ss << " double b = 1.0 - arg0;\n";
+ ss << " return log(pow(a/b, 0.5));\n";
+}
+
+void OpLn::GenerateCode( outputstream& ss ) const
+{
+ ss << " return log1p(arg0-1);\n";
+}
+
+void OpInt::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(value_approxDecl);
+ funs.insert(value_approx);
+}
+
+void OpInt::GenerateCode( outputstream& ss ) const
+{
+ ss << " return floor( value_approx( arg0 ));\n";
+}
+
+void OpNegSub::GenerateCode( outputstream& ss ) const
+{
+ ss << " return -arg0;\n";
+}
+
+void OpRadians::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arg0 * M_PI / 180.0;\n";
+}
+
+void OpIsEven::GenerateCode( outputstream& ss ) const
+{
+ ss << " return (fmod(floor(fabs(arg0)), 2.0)<0.5);\n";
+}
+
+void OpIsOdd::GenerateCode( outputstream& ss ) const
+{
+ ss << " return !(fmod(floor(fabs(arg0)), 2.0)<0.5);\n";
+}
+
+void OpSqrtPi::GenerateCode( outputstream& ss ) const
+{
+ ss << " return (double)sqrt(arg0 * M_PI);\n";
+}
+
+void OpDeg::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arg0 / M_PI * 180;;\n";
+}
+
+void OpFact::GenerateCode( outputstream& ss ) const
+{
+ ss << " arg0 = floor(arg0);\n";
+ ss << " if (arg0 < 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else if (arg0 == 0.0)\n";
+ ss << " return 1.0;\n";
+ ss << " else if (arg0 <= 170.0)\n";
+ ss << " {\n";
+ ss << " double fTemp = arg0;\n";
+ ss << " while (fTemp > 2.0)\n";
+ ss << " {\n";
+ ss << " fTemp = fTemp - 1;\n";
+ ss << " arg0 = arg0 * fTemp;\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " return arg0;\n";
+}
+
+void OpOdd::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(Math_IntgDecl);
+ funs.insert(Math_Intg);
+}
+
+void OpOdd::GenerateCode( outputstream& ss ) const
+{
+ ss << " double tmp;\n";
+ ss << " if (arg0 > 0.0 ){\n";
+ ss << " tmp=Intg(arg0);\n";
+ ss << " if(tmp-trunc(tmp/2)*2 == 0)\n";
+ ss << " tmp=tmp+1;\n";
+ ss << " }else if (arg0 < 0.0 ){\n";
+ ss << " tmp=Intg(arg0);\n";
+ ss << " if(tmp-trunc(tmp/2)*2 == 0)\n";
+ ss << " tmp=tmp-1.0;\n";
+ ss << " }else\n";
+ ss << " tmp=1.0;\n";
+ ss << " return tmp;\n";
+}
+
+void OpMROUND::GenerateCode( outputstream& ss ) const
+{
+ ss<<" if(arg1==0)\n";
+ ss<<" return arg1;\n";
+ ss<<" tmp=arg1 * round(arg0 / arg1);\n";
+ ss<<" return tmp;\n";
+}
+
+void OpCombinA::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(bikDecl);
+ funs.insert(bik);
+}
+
+void OpCombinA::GenerateCode( outputstream& ss ) const
+{
+ ss << " arg0 = trunc(arg0);\n";
+ ss << " arg1 = trunc(arg1);\n";
+ ss << " if (arg0 < 0.0 || arg1 < 0.0 || arg1 > arg0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double tem;\n";
+ ss << " if(arg0 >= arg1 && arg0 > 0 && arg1 > 0)\n";
+ ss << " tem = bik(arg0+arg1-1,arg1);\n";
+ ss << " else if(arg0 == 0 && arg1 == 0)\n";
+ ss << " tem = 0;\n";
+ ss << " else if(arg0 > 0 && arg1 == 0)\n";
+ ss << " tem = 1;\n";
+ ss << " else\n";
+ ss << " tem = -1;\n";
+ ss << " double i = tem - trunc(tem);\n";
+ ss << " if(i < 0.5)\n";
+ ss << " tem = trunc(tem);\n";
+ ss << " else\n";
+ ss << " tem = trunc(tem) + 1;\n";
+ ss << " return tem;\n";
+}
+
+void OpCombin::GenerateCode( outputstream& ss ) const
+{
+ ss << " double result = -1.0;\n";
+ ss << " double num = floor( arg0 );\n";
+ ss << " double num_chosen = floor( arg1 );\n";
+ ss << " if(num < 0 || num_chosen < 0 || num < num_chosen )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " result = select(result, 0.0, (ulong)(num < num_chosen));\n";
+ ss << " result = select(result, 1.0, (ulong)(num_chosen == 0.0));\n";
+ ss << " if(result == 0 || result ==1)\n";
+ ss << " return result;\n";
+ ss << " double4 db4num;\n";
+ ss << " double4 db4num_chosen;\n";
+ ss << " double4 db4result;\n";
+ ss << " double2 db2result;\n";
+ ss << " result = 1.0;\n";
+ ss << " int loop = num_chosen/4;\n";
+ ss << " for(int i=0; i<loop; i++)\n";
+ ss << " {\n";
+ ss << " db4num = (double4){num,\n";
+ ss << " num-1.0,\n";
+ ss << " num-2.0,\n";
+ ss << " num-3.0};\n";
+ ss << " db4num_chosen = (double4){num_chosen,\n";
+ ss << " num_chosen-1.0,\n";
+ ss << " num_chosen-2.0,\n";
+ ss << " num_chosen-3.0};\n";
+ ss << " db4result = db4num / db4num_chosen;\n";
+ ss << " db2result = db4result.xy * db4result.zw;\n";
+ ss << " result *= db2result.x * db2result.y;\n";
+ ss << " num = num - 4.0;\n";
+ ss << " num_chosen = num_chosen - 4.0;\n";
+ ss << " }\n";
+ ss << " while ( num_chosen > 0){\n";
+ ss << " result *= num / num_chosen;\n";
+ ss << " num = num - 1.0;\n";
+ ss << " num_chosen = num_chosen - 1.0;\n";
+ ss << " }\n";
+ ss << " return result;\n";
+}
+
+void OpMod::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+ decls.insert(fsub_approxDecl);
+ funs.insert(fsub_approx);
+ decls.insert(value_approxDecl);
+ funs.insert(value_approx);
+}
+
+void OpMod::GenerateCode( outputstream& ss ) const
+{
+ ss << " double fNum = arg0;\n";
+ ss << " double fDenom = arg1;\n";
+ ss << " if(fDenom == 0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " double fRes = fsub_approx( fNum, floor( value_approx( fNum / fDenom )) * fDenom );\n";
+ ss << " if ( ( fDenom > 0 && fRes >= 0 && fRes < fDenom ) ||\n";
+ ss << " ( fDenom < 0 && fRes <= 0 && fRes > fDenom ) )\n";
+ ss << " return fRes;\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+}
+
+void OpPower::GenerateCode( outputstream& ss ) const
+{
+ ss << " return pow(arg0,arg1);\n";
+}
+
+void OpArcTan2::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(atan2Decl);
+ funs.insert(atan2Content);
+}
+
+void OpArcTan2::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arctan2(arg1, arg0);\n";
+}
+
+void OpBitAnd::GenerateCode( outputstream& ss ) const
+{
+ ss << " if( arg0 < 0 || arg1 < 0 || arg0 >= 281474976710656.0 || arg1 >= 281474976710656.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return (long)arg0 & (long)arg1;\n";
+}
+
+void OpBitOr::GenerateCode( outputstream& ss ) const
+{
+ ss << " if( arg0 < 0 || arg1 < 0 || arg0 >= 281474976710656.0 || arg1 >= 281474976710656.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return (long)arg0 | (long)arg1;\n";
+}
+
+void OpBitXor::GenerateCode( outputstream& ss ) const
+{
+ ss << " if( arg0 < 0 || arg1 < 0 || arg0 >= 281474976710656.0 || arg1 >= 281474976710656.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return (long)arg0 ^ (long)arg1;\n";
+}
+
+void OpBitLshift::GenerateCode( outputstream& ss ) const
+{
+ ss << " double num = floor( arg0 );\n";
+ ss << " double shift_amount = floor( arg1 );\n";
+ ss << " if( num < 0 || num >= 281474976710656.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return floor(shift_amount >= 0 ? ";
+ ss << "num * pow(2.0, shift_amount) : ";
+ ss << "num / pow(2.0, fabs(shift_amount)));\n";
+}
+
+void OpBitRshift::GenerateCode( outputstream& ss ) const
+{
+ ss << " double num = floor( arg0 );\n";
+ ss << " double shift_amount = floor( arg1 );\n";
+ ss << " if( num < 0 || num >= 281474976710656.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return floor(";
+ ss << "shift_amount >= 0 ? num / pow(2.0, shift_amount) : ";
+ ss << "num * pow(2.0, fabs(shift_amount)));\n";
+}
+
+void OpQuotient::GenerateCode( outputstream& ss ) const
+{
+ ss << " return trunc(arg0/arg1);\n";
+}
+
+void OpEqual::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+ decls.insert(cell_equalDecl);
+ funs.insert(cell_equal);
+}
+
+void OpEqual::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss, EmptyIsNan, GenerateArgType );
+ GenerateArg( 1, vSubArguments, ss, EmptyIsNan, GenerateArgType );
+ ss << " return cell_equal( arg0, arg1, arg0_is_string, arg1_is_string );\n";
+ ss << "}";
+}
+
+void OpNotEqual::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+ decls.insert(cell_equalDecl);
+ funs.insert(cell_equal);
+}
+
+void OpNotEqual::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss, EmptyIsNan, GenerateArgType );
+ GenerateArg( 1, vSubArguments, ss, EmptyIsNan, GenerateArgType );
+ ss << " return !cell_equal( arg0, arg1, arg0_is_string, arg1_is_string );\n";
+ ss << "}";
+}
+
+void OpLessEqual::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+}
+
+void OpLessEqual::GenerateCode( outputstream& ss ) const
+{
+ ss << " return approx_equal( arg0, arg1 ) || arg0 <= arg1;\n";
+}
+
+void OpLess::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arg0 < arg1;\n";
+}
+
+void OpGreaterEqual::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+}
+
+void OpGreaterEqual::GenerateCode( outputstream& ss ) const
+{
+ ss << " return approx_equal( arg0, arg1 ) || arg0 >= arg1;\n";
+}
+
+void OpGreater::GenerateCode( outputstream& ss ) const
+{
+ ss << " return arg0 > arg1;\n";
+}
+
+void OpLog::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "arg0", 0, vSubArguments, ss );
+ GenerateArgWithDefault( "arg1", 1, 10, vSubArguments, ss );
+ ss << " return log10(arg0)/log10(arg1);;\n";
+ ss << "}";
+}
+
+void OpCountIfs::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ FormulaToken *tmpCur = vSubArguments[0]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur);
+ size_t nCurWindowSize = pCurDVR->GetArrayLength() <
+ pCurDVR->GetRefRowSize() ? pCurDVR->GetArrayLength():
+ pCurDVR->GetRefRowSize() ;
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ ss << " int tmp =0;\n";
+ ss << " int loop;\n";
+ GenTmpVariables(ss,vSubArguments);
+
+ ss<< " int singleIndex =gid0;\n";
+ int m=0;
+
+ outputstream tmpss;
+
+ for(size_t j=0;j<vSubArguments.size();j+=2,m++)
+ {
+ CheckSubArgumentIsNan(tmpss,vSubArguments,j);
+ CheckSubArgumentIsNan(ss,vSubArguments,j+1);
+ tmpss <<" if(isequal(";
+ tmpss <<"tmp";
+ tmpss <<j;
+ tmpss <<" , ";
+ tmpss << "tmp";
+ tmpss << j+1;
+ tmpss << ")){\n";
+ }
+ tmpss << " tmp ++;\n";
+ for(size_t j=0;j<vSubArguments.size();j+=2,m--)
+ {
+ for(int n = 0;n<m+1;n++)
+ {
+ tmpss << " ";
+ }
+ tmpss<< "}\n";
+ }
+ UnrollDoubleVector(ss,tmpss,pCurDVR,nCurWindowSize);
+
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpSumIfs::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ FormulaToken *tmpCur = vSubArguments[0]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur);
+ size_t nCurWindowSize = pCurDVR->GetArrayLength() <
+ pCurDVR->GetRefRowSize() ? pCurDVR->GetArrayLength():
+ pCurDVR->GetRefRowSize() ;
+
+ mNeedReductionKernel = vSubArguments[0]->NeedParallelReduction();
+ if (mNeedReductionKernel)
+ {
+ // generate reduction functions
+
+ ss << "__kernel void ";
+ ss << vSubArguments[0]->GetName();
+ ss << "_SumIfs_reduction( ";
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ if (i)
+ ss << ",";
+ vSubArguments[i]->GenSlidingWindowDecl(ss);
+ }
+ ss << ", __global double *result,int arrayLength,int windowSize";
+
+ ss << ")\n{\n";
+ ss << " double tmp =0;\n";
+ ss << " int i ;\n";
+
+ GenTmpVariables(ss,vSubArguments);
+ ss << " double current_result = 0.0;\n";
+ ss << " int writePos = get_group_id(1);\n";
+ if (pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed())
+ ss << " int offset = 0;\n";
+ else if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ ss << " int offset = get_group_id(1);\n";
+ else
+ throw Unhandled(__FILE__, __LINE__);
+ // actually unreachable
+ ss << " int lidx = get_local_id(0);\n";
+ ss << " __local double shm_buf[256];\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " int loop = arrayLength/512 + 1;\n";
+ ss << " for (int l=0; l<loop; l++){\n";
+ ss << " tmp = 0.0;\n";
+ ss << " int loopOffset = l*512;\n";
+
+ ss << " int p1 = loopOffset + lidx + offset, p2 = p1 + 256;\n";
+ ss << " if (p2 < min(offset + windowSize, arrayLength)) {\n";
+ ss << " tmp0 = 0.0;\n";
+ int mm=0;
+ std::string p1 = "p1";
+ std::string p2 = "p2";
+ for(size_t j=1;j<vSubArguments.size();j+=2,mm++)
+ {
+ CheckSubArgumentIsNan2(ss,vSubArguments,j,p1);
+ CheckSubArgumentIsNan2(ss,vSubArguments,j+1,p1);
+ ss << "";
+ ss <<" if(isequal(";
+ ss <<"tmp";
+ ss <<j;
+ ss <<" , ";
+ ss << "tmp";
+ ss << j+1;
+ ss << "))";
+ ss << "{\n";
+ }
+ CheckSubArgumentIsNan2(ss,vSubArguments,0,p1);
+ ss << " tmp += tmp0;\n";
+ for(size_t j=1;j<vSubArguments.size();j+=2,mm--)
+ {
+ for(int n = 0;n<mm+1;n++)
+ {
+ ss << " ";
+ }
+ ss<< "}\n\n";
+ }
+ mm=0;
+ for(size_t j=1;j<vSubArguments.size();j+=2,mm++)
+ {
+ CheckSubArgumentIsNan2(ss,vSubArguments,j,p2);
+ CheckSubArgumentIsNan2(ss,vSubArguments,j+1,p2);
+ ss <<" if(isequal(";
+ ss <<"tmp";
+ ss <<j;
+ ss <<" , ";
+ ss << "tmp";
+ ss << j+1;
+ ss << ")){\n";
+ }
+ CheckSubArgumentIsNan2(ss,vSubArguments,0,p2);
+ ss << " tmp += tmp0;\n";
+ for(size_t j=1;j< vSubArguments.size();j+=2,mm--)
+ {
+ for(int n = 0;n<mm+1;n++)
+ {
+ ss << " ";
+ }
+ ss<< "}\n";
+ }
+ ss << " }\n";
+
+ ss << " else if (p1 < min(arrayLength, offset + windowSize)) {\n";
+ mm=0;
+ for(size_t j=1;j<vSubArguments.size();j+=2,mm++)
+ {
+ CheckSubArgumentIsNan2(ss,vSubArguments,j,p1);
+ CheckSubArgumentIsNan2(ss,vSubArguments,j+1,p1);
+
+ ss <<" if(isequal(";
+ ss <<"tmp";
+ ss <<j;
+ ss <<" , ";
+ ss << "tmp";
+ ss << j+1;
+ ss << ")){\n";
+ }
+ CheckSubArgumentIsNan2(ss,vSubArguments,0,p1);
+ ss << " tmp += tmp0;\n";
+ for(size_t j=1;j<vSubArguments.size();j+=2,mm--)
+ {
+ for(int n = 0;n<mm+1;n++)
+ {
+ ss << " ";
+ }
+ ss<< "}\n\n";
+ }
+
+ ss << " }\n";
+ ss << " shm_buf[lidx] = tmp;\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " for (int i = 128; i >0; i/=2) {\n";
+ ss << " if (lidx < i)\n";
+ ss << " shm_buf[lidx] += shm_buf[lidx + i];\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+ ss << " if (lidx == 0)\n";
+ ss << " current_result += shm_buf[0];\n";
+ ss << " barrier(CLK_LOCAL_MEM_FENCE);\n";
+ ss << " }\n";
+
+ ss << " if (lidx == 0)\n";
+ ss << " result[writePos] = current_result;\n";
+ ss << "}\n";
+ }// finish generate reduction code
+ // generate functions as usual
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ ss << " double tmp =0;\n";
+ if (!mNeedReductionKernel)
+ {
+ ss << " int i ;\n";
+ GenTmpVariables(ss,vSubArguments);
+ ss << " for (i = ";
+ if (!pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed()) {
+ ss << "gid0; i < "<< nCurWindowSize <<"; i++)\n";
+ } else if (pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed()) {
+ ss << "0; i < gid0+"<< nCurWindowSize <<"; i++)\n";
+ } else {
+ ss << "0; i < "<< nCurWindowSize <<"; i++)\n";
+ }
+ ss << " {\n";
+ if(!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss<< " int doubleIndex =i+gid0;\n";
+ }else
+ {
+ ss<< " int doubleIndex =i;\n";
+ }
+ ss<< " int singleIndex =gid0;\n";
+ int m=0;
+ for(size_t j=1;j<vSubArguments.size();j+=2,m++)
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments,j);
+ CheckSubArgumentIsNan(ss,vSubArguments,j+1);
+ ss <<" if(isequal(";
+ ss <<"tmp";
+ ss <<j;
+ ss <<" , ";
+ ss << "tmp";
+ ss << j+1;
+ ss << ")){\n";
+ }
+ CheckSubArgumentIsNan(ss,vSubArguments,0);
+ ss << " tmp += tmp0;\n";
+ for(size_t j=1;j<=vSubArguments.size();j+=2,m--)
+ {
+ for(int n = 0;n<m+1;n++)
+ {
+ ss << " ";
+ }
+ ss<< "}\n";
+ }
+ }
+ if (mNeedReductionKernel)
+ {
+ ss << "tmp =";
+ vSubArguments[0]->GenDeclRef(ss);
+ ss << "[gid0];\n";
+ }
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpAverageIfs::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ FormulaToken *tmpCur = vSubArguments[0]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur);
+ size_t nCurWindowSize = pCurDVR->GetArrayLength() <
+ pCurDVR->GetRefRowSize() ? pCurDVR->GetArrayLength():
+ pCurDVR->GetRefRowSize() ;
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ ss << " double tmp =0;\n";
+ ss << " int count=0;\n";
+ ss << " int loop;";
+ GenTmpVariables(ss,vSubArguments);
+ ss<< " int singleIndex =gid0;\n";
+ int m=0;
+ outputstream tmpss;
+ for(size_t j=1;j<vSubArguments.size();j+=2,m++)
+ {
+ CheckSubArgumentIsNan(tmpss,vSubArguments,j);
+ CheckSubArgumentIsNan(ss,vSubArguments,j+1);
+ tmpss <<" if(isequal(";
+ tmpss <<"tmp";
+ tmpss <<j;
+ tmpss <<" , ";
+ tmpss << "tmp";
+ tmpss << j+1;
+ tmpss << ")){\n";
+ }
+ CheckSubArgumentIsNan(tmpss,vSubArguments,0);
+ tmpss << " tmp += tmp0;\n";
+ tmpss << " count++;\n";
+ for(size_t j=1;j<vSubArguments.size();j+=2,m--)
+ {
+ for(int n = 0;n<m+1;n++)
+ {
+ tmpss << " ";
+ }
+ tmpss<< "}\n";
+ }
+
+ UnrollDoubleVector(ss,tmpss,pCurDVR,nCurWindowSize);
+
+ ss << " if(count!=0)\n";
+ ss << " tmp=tmp/count;\n";
+ ss << " else\n";
+ ss << " tmp= 0 ;\n";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpRound::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(nCorrValDecl);
+ decls.insert(RoundDecl);
+ funs.insert(Round);
+}
+
+void OpRound::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "value", 0, vSubArguments, ss );
+ if(vSubArguments.size() ==1)
+ ss << " return round(value);\n";
+ else
+ {
+ GenerateArg( "fDec", 1, vSubArguments, ss );
+ ss << " int dec = floor( fDec );\n";
+ ss << " if( dec < -20 || dec > 20 )\n";
+ ss << " return CreateDoubleError( IllegalArgument );\n";
+ ss << " if( dec == 0 )\n";
+ ss << " return round(value);\n";
+ ss << " double orig_value = value;\n";
+ ss << " value = fabs(value);\n";
+ ss << " double multiply = pown(10.0, dec);\n";
+ ss << " double tmp = value*multiply;\n";
+ ss << " tmp = Round( tmp );\n";
+ ss << " return copysign(tmp/multiply, orig_value);\n";
+ }
+ ss << "}";
+}
+
+void OpRoundUp::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "value", 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fDec", 1, 0, vSubArguments, ss );
+ ss << " int dec = floor( fDec );\n";
+ ss << " if( dec < -20 || dec > 20 )\n";
+ ss << " return CreateDoubleError( IllegalArgument );\n";
+ ss << " double orig_value = value;\n";
+ ss << " value = fabs(value);\n";
+ ss << " double multiply = pown(10.0, dec);\n";
+ ss << " double tmp = value*multiply;\n";
+ ss << " double integral;\n";
+ // The pown() above increases rounding error, so compensate for it here.
+ // If the fractional part is close above zero, adjusted for rounding error,
+ // the number just needs to be rounded (=truncated).
+ ss << " if( modf( tmp, &integral ) / multiply < 1e-12 )\n";
+ ss << " tmp = integral;\n";
+ ss << " else\n";
+ ss << " tmp = integral + 1;\n";
+ ss << " return copysign(tmp/multiply, orig_value);\n";
+ ss << "}";
+}
+
+void OpRoundDown::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "value", 0, vSubArguments, ss );
+ GenerateArgWithDefault( "fDec", 1, 0, vSubArguments, ss );
+ ss << " int dec = floor( fDec );\n";
+ ss << " if( dec < -20 || dec > 20 )\n";
+ ss << " return CreateDoubleError( IllegalArgument );\n";
+ ss << " double orig_value = value;\n";
+ ss << " value = fabs(value);\n";
+ ss << " double multiply = pown(10.0, dec);\n";
+ ss << " double tmp = value*multiply;\n";
+ ss << " double integral;\n";
+ // The pown() above increases rounding error, so compensate for it here.
+ // If the fractional part is close below one, adjusted for rounding error,
+ // the number just needs to be rounded (=truncated + 1).
+ ss << " if(( 1 - modf( tmp, &integral )) / multiply < 1e-12 )\n";
+ ss << " tmp = integral + 1;\n";
+ ss << " else\n";
+ ss << " tmp = integral;\n";
+ ss << " return copysign(tmp/multiply, orig_value);\n";
+ ss << "}";
+}
+
+void OpCountIf::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double vara, varb;\n";
+ ss << " int varc = 0;\n";
+ FormulaToken *tmpCur = vSubArguments[1]->GetFormulaToken();
+ assert(tmpCur);
+ if(ocPush == vSubArguments[1]->GetFormulaToken()->GetOpCode())
+ {
+ if(tmpCur->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* tmpCurDVR=
+ static_cast<
+ const formula::SingleVectorRefToken *>(tmpCur);
+ ss << " varb = ";
+ ss << vSubArguments[1]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if(isnan(varb)||(gid0>=";
+ ss << tmpCurDVR->GetArrayLength();
+ ss << "))\n";
+ ss << " varb = 0;\n";
+ }
+ else if(tmpCur->GetType() == formula::svDouble)
+ {
+ ss << " varb = ";
+ ss << tmpCur->GetDouble() << ";\n";
+ }
+ }
+ else
+ {
+ ss << " varb = ";
+ ss << vSubArguments[1]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ }
+ tmpCur = vSubArguments[0]->GetFormulaToken();
+ assert(tmpCur);
+ if(ocPush == vSubArguments[0]->GetFormulaToken()->GetOpCode())
+ {
+ //TODO DoubleVector
+ if (tmpCur->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken *>(tmpCur);
+ size_t nCurWindowSize = pDVR->GetRefRowSize();
+ ss << " for (int i = ";
+ if (!pDVR->IsStartFixed() && pDVR->IsEndFixed())
+ {
+ ss << "gid0; i < " << pDVR->GetArrayLength();
+ ss << " && i < " << nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ else if (pDVR->IsStartFixed() && !pDVR->IsEndFixed())
+ {
+ ss << "0; i < " << pDVR->GetArrayLength();
+ ss << " && i < gid0+"<< nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ else if (!pDVR->IsStartFixed() && !pDVR->IsEndFixed())
+ {
+ ss << "0; i + gid0 < " << pDVR->GetArrayLength();
+ ss << " && i < "<< nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ else
+ {
+ ss << "0; i < "<< nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ ss << " vara = ";
+ ss << vSubArguments[0]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if (isnan(vara))\n";
+ ss << " continue;\n";
+ ss << " (vara == varb) && varc++;\n";
+ ss << " }\n";
+ }
+ else if(tmpCur->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* tmpCurDVR=
+ static_cast<
+ const formula::SingleVectorRefToken *>(tmpCur);
+ ss << " vara = ";
+ ss << vSubArguments[0]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if(isnan(vara)||(gid0>=";
+ ss << tmpCurDVR->GetArrayLength();
+ ss << "))\n";
+ ss << " return 0;\n";
+ ss << " (vara == varb) && varc++;\n";
+ }
+ }
+ ss << " return varc;\n";
+ ss << "}";
+}
+
+void OpSumIf::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double vara, varb, varc, sum = 0.0f;\n";
+ int flag = 3 == vSubArguments.size() ? 2 : 0;
+ FormulaToken *tmpCur = vSubArguments[1]->GetFormulaToken();
+ assert(tmpCur);
+ if(ocPush == vSubArguments[1]->GetFormulaToken()->GetOpCode())
+ {
+ if(tmpCur->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* tmpCurDVR=
+ static_cast<
+ const formula::SingleVectorRefToken *>(tmpCur);
+ ss << " varb = ";
+ ss << vSubArguments[1]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if(isnan(varb)||(gid0>=";
+ ss << tmpCurDVR->GetArrayLength();
+ ss << "))\n";
+ ss << " varb = 0;\n";
+ }
+ else if(tmpCur->GetType() == formula::svDouble)
+ {
+ ss << " varb = ";
+ ss << tmpCur->GetDouble() << ";\n";
+ }
+ }
+ else
+ {
+ ss << " varb = ";
+ ss << vSubArguments[1]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ }
+ tmpCur = vSubArguments[0]->GetFormulaToken();
+ assert(tmpCur);
+ if(ocPush == vSubArguments[0]->GetFormulaToken()->GetOpCode())
+ {
+ //TODO DoubleVector
+ if (tmpCur->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken *>(tmpCur);
+ size_t nCurWindowSize = pDVR->GetRefRowSize();
+ ss << " for (int i = ";
+ if (!pDVR->IsStartFixed() && pDVR->IsEndFixed())
+ {
+ ss << "gid0; i < " << pDVR->GetArrayLength();
+ ss << " && i < " << nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ else if (pDVR->IsStartFixed() && !pDVR->IsEndFixed())
+ {
+ ss << "0; i < " << pDVR->GetArrayLength();
+ ss << " && i < gid0+"<< nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ else if (!pDVR->IsStartFixed() && !pDVR->IsEndFixed())
+ {
+ ss << "0; i + gid0 < " << pDVR->GetArrayLength();
+ ss << " && i < "<< nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ else
+ {
+ ss << "0; i < "<< nCurWindowSize << "; ++i)\n";
+ ss << " {\n";
+ }
+ ss << " vara = ";
+ ss << vSubArguments[0]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if (isnan(vara))\n";
+ ss << " continue;\n";
+ ss << " varc = ";
+ ss << vSubArguments[flag]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if (isnan(varc))\n";
+ ss << " varc = 0.0f;\n";
+ ss << " (vara == varb)&&(sum = sum + varc);\n";
+ ss << " }\n";
+ }
+ else if(tmpCur->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* tmpCurDVR=
+ static_cast<
+ const formula::SingleVectorRefToken *>(tmpCur);
+ ss << " vara = ";
+ ss << vSubArguments[0]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if(isnan(vara)||(gid0>=";
+ ss << tmpCurDVR->GetArrayLength();
+ ss << "))\n";
+ ss << " return 0;\n";
+ ss << " int i = 0;\n";
+ ss << " varc = ";
+ ss << vSubArguments[flag]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ ss << " if(isnan(varc)||(gid0>=";
+ ss << tmpCurDVR->GetArrayLength();
+ ss << "))\n";
+ ss << " varc = 0.0f;\n";
+
+ ss << " (vara == varb)&&(sum = sum + varc);\n";
+
+ }
+ }
+ ss << " return sum;\n";
+ ss << "}";
+}
+
+void OpFloor::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "arg0", 0, vSubArguments, ss );
+ GenerateArg( "arg1", 1, vSubArguments, ss );
+ GenerateArgWithDefault( "arg2", 2, 0, vSubArguments, ss );
+ ss << " if(isnan(arg0) || isnan(arg1))\n";
+ ss << " return 0;\n";
+ ss << " if(isnan(arg2))\n";
+ ss << " arg2 = 0.0;\n";
+ ss << " if(arg0*arg1<0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if(arg1 == 0.0)\n";
+ ss << " return 0.0;\n";
+ ss << " else if(arg2==0.0&&arg0<0.0)\n";
+ ss << " return (trunc(arg0/arg1)+1)*arg1;\n";
+ ss << " else\n";
+ ss << " return trunc(arg0/arg1)*arg1;\n";
+ ss << "}\n";
+}
+
+void OpSumSQ::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double sum = 0.0f, arg;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " sum += pown(arg, 2);\n"
+ );
+ ss << " return sum;\n";
+ ss << "}";
+}
+
+void OpCeil::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "num", 0, vSubArguments, ss );
+ GenerateArg( "significance", 1, vSubArguments, ss );
+ GenerateArgWithDefault( "bAbs", 2, 0, vSubArguments, ss );
+ ss << " if(num*significance < 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if(significance == 0.0)\n";
+ ss << " return 0.0;\n";
+ ss << " return ";
+ ss << "( !(int)bAbs && num < 0.0 ? floor( num / significance ) : ";
+ ss << "ceil( num / significance ) )";
+ ss << "*significance;\n";
+ ss << "}";
+}
+
+void OpProduct::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double product=1.0;\n";
+ ss << " int count = 0;\n\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " product = product*arg;\n"
+ " ++count;\n"
+ );
+ ss << " if(count == 0)\n";
+ ss << " return 0;\n";
+ ss << " return product;\n";
+ ss << "}";
+}
+
+void OpAverageIf::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp =0;\n";
+ ss << " double count=0;\n";
+ ss << " int singleIndex =gid0;\n";
+ ss << " int doubleIndex;\n";
+ ss << " int i ;\n";
+ ss << " int j ;\n";
+ GenTmpVariables(ss,vSubArguments);
+
+ unsigned paraOneIsDoubleVector = 0;
+ unsigned paraOneWidth = 1;
+ unsigned paraTwoWidth = 1;
+ unsigned loopTimes = 0;
+
+ if(vSubArguments[0]->GetFormulaToken()->GetType() == formula::svDoubleVectorRef)
+ {
+ paraOneIsDoubleVector = 1;
+ FormulaToken *tmpCur0 = vSubArguments[0]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR0= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur0);
+ paraOneWidth = pCurDVR0->GetArrays().size();
+ loopTimes = paraOneWidth;
+ if(paraOneWidth > 1)
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+ }
+
+ if(vSubArguments[paraOneWidth]->GetFormulaToken()->GetType() ==
+ formula::svDoubleVectorRef)
+
+ {
+ FormulaToken *tmpCur1 = vSubArguments[1]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR1= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur1);
+ paraTwoWidth = pCurDVR1->GetArrays().size();
+ if(paraTwoWidth > 1)
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+ ss << " i = ";
+ if (!pCurDVR1->IsStartFixed() && pCurDVR1->IsEndFixed()) {
+ ss << "gid0;\n";
+ } else {
+ ss << "0;\n";
+ }
+ if(!pCurDVR1->IsStartFixed() && !pCurDVR1->IsEndFixed())
+ {
+ ss << " doubleIndex =i+gid0;\n";
+ }else
+ {
+ ss << " doubleIndex =i;\n";
+ }
+ }
+
+ CheckSubArgumentIsNan(ss,vSubArguments,paraOneWidth);
+
+ unsigned paraThreeIndex = paraOneWidth + paraTwoWidth;
+ if(vSubArguments.size() > paraThreeIndex)
+ {
+ if(vSubArguments[paraThreeIndex]->GetFormulaToken()->GetType() ==
+ formula::svDoubleVectorRef)
+ {
+ FormulaToken *tmpCur2 =
+ vSubArguments[paraThreeIndex]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR2= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur2);
+ unsigned paraThreeWidth = pCurDVR2->GetArrays().size();
+ if(paraThreeWidth > 1)
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+ }
+ }
+
+ if(paraOneIsDoubleVector)
+ {
+ unsigned loopIndex = 0;
+ FormulaToken *tmpCur0 = vSubArguments[0]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR0= static_cast<const
+ formula::DoubleVectorRefToken *>(tmpCur0);
+ size_t nCurWindowSize = pCurDVR0->GetArrayLength() <
+ pCurDVR0->GetRefRowSize() ? pCurDVR0->GetArrayLength():
+ pCurDVR0->GetRefRowSize() ;
+
+ for(loopIndex =0; loopIndex < loopTimes; loopIndex++)
+ {
+ ss << " for (i = ";
+ if (!pCurDVR0->IsStartFixed() && pCurDVR0->IsEndFixed()) {
+ ss << "gid0; i < "<< nCurWindowSize <<"; i++)\n";
+ } else if (pCurDVR0->IsStartFixed() && !pCurDVR0->IsEndFixed()) {
+ ss << "0; i < gid0+"<< nCurWindowSize <<"; i++)\n";
+ } else {
+ ss << "0; i < "<< nCurWindowSize <<"; i++)\n";
+ }
+ ss << " {\n";
+ if(!pCurDVR0->IsStartFixed() && !pCurDVR0->IsEndFixed())
+ {
+ ss << " doubleIndex =i+gid0;\n";
+ }else
+ {
+ ss << " doubleIndex =i;\n";
+ }
+
+ CheckSubArgumentIsNan(ss,vSubArguments, loopIndex);
+
+ ss << " if ( isequal( tmp";
+ ss << loopIndex<<" , tmp"<<paraOneWidth<<") ) \n";
+ ss << " {\n";
+ if(vSubArguments.size() == paraThreeIndex)
+ ss << " tmp += tmp"<<loopIndex<<";\n";
+ else
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments,
+ paraThreeIndex+loopIndex);
+ ss << " tmp += tmp";
+ ss << paraThreeIndex+loopIndex<<";\n";
+ }
+ ss << " count+=1.0;\n";
+ ss << " }\n";
+ ss << " }\n";
+ }
+ }
+ else
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments, 0);
+ ss << " if ( isequal( tmp0 , tmp1 ) ) \n";
+ ss << " {\n";
+ if(vSubArguments.size() == 2)
+ ss << " tmp += tmp0;\n";
+ else
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments,2);
+ ss << " tmp += tmp2;\n";
+ }
+ ss << " count+=1.0;\n";
+ ss << " }\n";
+ }
+
+ ss << " if(count!=0)\n";
+ ss << " tmp=tmp/count;\n";
+ ss << " else\n";
+ ss << " tmp= 0 ;\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpSeriesSum::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT(4,4);
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double var[3], coeff, res = 0.0f;\n";
+ GenerateArg( "var0", 0, vSubArguments, ss );
+ GenerateArg( "var1", 1, vSubArguments, ss );
+ GenerateArg( "var2", 2, vSubArguments, ss );
+ ss << " if( var0 == 0 && var1 == 0 )\n";
+ ss << " return CreateDoubleError(NoValue);\n"; // pow(0,0)
+ ss << " var[0] = var0;\n";
+ ss << " var[1] = var1;\n";
+ ss << " var[2] = var2;\n";
+ ss << " int j = 0;\n";
+ GenerateRangeArg( 3, vSubArguments, ss, SkipEmpty,
+ " double coeff = arg;\n"
+ " res = res + coeff * pow(var[0], var[1] + j * var[2]);\n"
+ " ++j;\n"
+ );
+ ss << " return res;\n";
+ ss << "}";
+}
+
+void SumOfProduct::GenSlidingWindowFunction( outputstream& ss,
+ const std::string& sSymName, SubArguments& vSubArguments )
+{
+ size_t nCurWindowSize = 0;
+ FormulaToken* tmpCur = nullptr;
+ const formula::DoubleVectorRefToken* pCurDVR = nullptr;
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ size_t nCurChildWindowSize = vSubArguments[i]->GetWindowSize();
+ nCurWindowSize = (nCurWindowSize < nCurChildWindowSize) ?
+ nCurChildWindowSize : nCurWindowSize;
+ tmpCur = vSubArguments[i]->GetFormulaToken();
+ if (ocPush == tmpCur->GetOpCode())
+ {
+ pCurDVR = static_cast<const formula::DoubleVectorRefToken*>(tmpCur);
+ if (pCurDVR->IsStartFixed() != pCurDVR->IsEndFixed())
+ throw Unhandled(__FILE__, __LINE__);
+ }
+ }
+ ss << " double tmp = 0.0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+
+ ss << "\tint i;\n\t";
+ ss << "int currentCount0;\n";
+ for (size_t i = 0; i < vSubArguments.size() - 1; i++)
+ ss << "int currentCount" << i + 1 << ";\n";
+ outputstream temp3, temp4;
+ int outLoopSize = UNROLLING_FACTOR;
+ if (nCurWindowSize / outLoopSize != 0)
+ {
+ ss << "for(int outLoop=0; outLoop<" <<
+ nCurWindowSize / outLoopSize << "; outLoop++){\n\t";
+ for (int count = 0; count < outLoopSize; count++)
+ {
+ ss << "i = outLoop*" << outLoopSize << "+" << count << ";\n";
+ if (count == 0)
+ {
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ tmpCur = vSubArguments[i]->GetFormulaToken();
+ if (ocPush == tmpCur->GetOpCode())
+ {
+ pCurDVR = static_cast<const formula::DoubleVectorRefToken*>(tmpCur);
+ if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ temp3 << " currentCount";
+ temp3 << i;
+ temp3 << " =i+gid0+1;\n";
+ }
+ else
+ {
+ temp3 << " currentCount";
+ temp3 << i;
+ temp3 << " =i+1;\n";
+ }
+ }
+ }
+
+ temp3 << "tmp = fsum(";
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ if (i)
+ temp3 << "*";
+ if (ocPush == vSubArguments[i]->GetFormulaToken()->GetOpCode())
+ {
+ temp3 << "(";
+ temp3 << "(currentCount";
+ temp3 << i;
+ temp3 << ">";
+ if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>
+ (vSubArguments[i]->GetFormulaToken());
+ temp3 << pSVR->GetArrayLength();
+ temp3 << ")||isnan(" << vSubArguments[i]
+ ->GenSlidingWindowDeclRef();
+ temp3 << ")?0:";
+ temp3 << vSubArguments[i]->GenSlidingWindowDeclRef();
+ temp3 << ")";
+ }
+ else if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pSVR =
+ static_cast<const formula::DoubleVectorRefToken*>
+ (vSubArguments[i]->GetFormulaToken());
+ temp3 << pSVR->GetArrayLength();
+ temp3 << ")||isnan(" << vSubArguments[i]
+ ->GenSlidingWindowDeclRef(true);
+ temp3 << ")?0:";
+ temp3 << vSubArguments[i]->GenSlidingWindowDeclRef(true);
+ temp3 << ")";
+ }
+
+ }
+ else
+ temp3 << vSubArguments[i]->GenSlidingWindowDeclRef(true);
+ }
+ temp3 << ", tmp);\n\t";
+ }
+ ss << temp3.str();
+ }
+ ss << "}\n\t";
+ }
+ //The residual of mod outLoopSize
+ for (size_t count = nCurWindowSize / outLoopSize * outLoopSize;
+ count < nCurWindowSize; count++)
+ {
+ ss << "i =" << count << ";\n";
+ if (count == nCurWindowSize / outLoopSize * outLoopSize)
+ {
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ tmpCur = vSubArguments[i]->GetFormulaToken();
+ if (ocPush == tmpCur->GetOpCode())
+ {
+ pCurDVR = static_cast<const formula::DoubleVectorRefToken*>(tmpCur);
+ if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ temp4 << " currentCount";
+ temp4 << i;
+ temp4 << " =i+gid0+1;\n";
+ }
+ else
+ {
+ temp4 << " currentCount";
+ temp4 << i;
+ temp4 << " =i+1;\n";
+ }
+ }
+ }
+
+ temp4 << "tmp = fsum(";
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ if (i)
+ temp4 << "*";
+ if (ocPush == vSubArguments[i]->GetFormulaToken()->GetOpCode())
+ {
+ temp4 << "(";
+ temp4 << "(currentCount";
+ temp4 << i;
+ temp4 << ">";
+ if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast<const formula::SingleVectorRefToken*>
+ (vSubArguments[i]->GetFormulaToken());
+ temp4 << pSVR->GetArrayLength();
+ temp4 << ")||isnan(" << vSubArguments[i]
+ ->GenSlidingWindowDeclRef();
+ temp4 << ")?0:";
+ temp4 << vSubArguments[i]->GenSlidingWindowDeclRef();
+ temp4 << ")";
+ }
+ else if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pSVR =
+ static_cast<const formula::DoubleVectorRefToken*>
+ (vSubArguments[i]->GetFormulaToken());
+ temp4 << pSVR->GetArrayLength();
+ temp4 << ")||isnan(" << vSubArguments[i]
+ ->GenSlidingWindowDeclRef(true);
+ temp4 << ")?0:";
+ temp4 << vSubArguments[i]->GenSlidingWindowDeclRef(true);
+ temp4 << ")";
+ }
+
+ }
+ else
+ {
+ temp4 << vSubArguments[i]
+ ->GenSlidingWindowDeclRef(true);
+ }
+ }
+ temp4 << ", tmp);\n\t";
+ }
+ ss << temp4.str();
+ }
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void OpSum::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+ decls.insert(fsum_approxDecl);
+ funs.insert(fsum_approx);
+}
+
+void OpSub::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+ decls.insert(fsub_approxDecl);
+ funs.insert(fsub_approx);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_math.hxx b/sc/source/core/opencl/op_math.hxx
new file mode 100644
index 0000000000..92f9701eef
--- /dev/null
+++ b/sc/source/core/opencl/op_math.hxx
@@ -0,0 +1,673 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+/// Implements functions in the form of FUNC(x), e.g. COS().
+/// The function should take one simple argument (i.e. no ranges).
+class OpMathOneArgument : public Normal
+{
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ /// This writes out OpenCL code returning the computed value, the argument is "arg0".
+ virtual void GenerateCode( outputstream& ss ) const = 0;
+};
+
+/// Implements functions in the form of FUNC(x1, x2), e.g. ATAN2().
+/// The function should take exactly two simple arguments (i.e. no ranges).
+class OpMathTwoArguments : public Normal
+{
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ /// This writes out OpenCL code returning the computed value, the arguments are "arg0" and "arg1".
+ virtual void GenerateCode( outputstream& ss ) const = 0;
+};
+
+
+class OpCos: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Cos"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSec: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Sec"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSecH: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "SecH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpMROUND: public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "MROUND"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpCsc: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Csc"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSumIfs final : public CheckVariables
+{
+public:
+ OpSumIfs(): CheckVariables(), mNeedReductionKernel(false) {}
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SumIfs"; }
+ bool NeedReductionKernel() const { return mNeedReductionKernel; }
+private:
+ bool mNeedReductionKernel;
+};
+
+class OpCosh: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Cosh"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSinh: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Sinh"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSin: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Sin"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpAbs: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScAbs"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcCos: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScACos"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcCosHyp : public OpMathOneArgument
+{
+public:
+ virtual std::string GetBottom() override { return "1.0"; }
+ virtual std::string BinFuncName() const override { return "ScACosH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpTan: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Tan"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpTanH: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "TanH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSqrt: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Sqrt"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcCot : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScACot"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcCotHyp : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScACotH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcSin : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScASin"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcSinHyp : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScASinH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcTan2 : public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScATan2"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcTan : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScATan"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpArcTanH : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScATanH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpBitAnd : public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScBitAnd"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpBitOr : public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScBitOr"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpBitXor : public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScBitXor"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpBitLshift : public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScBitLshift"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpBitRshift : public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScBitRshift"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpLn: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Ln"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpRound: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Round"; }
+ virtual void BinInlineFun(std::set<std::string>& , std::set<std::string>& ) override;
+};
+class OpRoundUp: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "RoundUp"; }
+};
+class OpRoundDown: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "RoundDown"; }
+};
+class OpTrunc: public OpRoundDown
+{
+public:
+ virtual std::string BinFuncName() const override { return "Trunc"; }
+};
+class OpInt: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Int"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+ virtual void BinInlineFun(std::set<std::string>& , std::set<std::string>& ) override;
+};
+
+class OpRadians: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Radians"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpIsEven: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "IsEven"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpIsOdd: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "IsOdd"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpCot: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Cot"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSumSQ: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "SumSQ"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpCoth: public OpMathOneArgument
+{
+public:
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Coth"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpPower: public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "Power"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpOdd: public OpMathOneArgument
+{
+public:
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "Odd"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpFloor: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Floor"; }
+};
+
+class OpCscH: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "CscH"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpCeil:public Normal{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string GetBottom() override { return "0.0"; }
+ virtual std::string BinFuncName() const override { return "ScCeil"; }
+};
+
+class OpExp: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Exp"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpLog10: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Log10"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpEven: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Even"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpAverageIfs: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "AverageIfs"; }
+};
+class OpCountIfs: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "CountIfs"; }
+};
+class OpMod: public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "Mod"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpProduct: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Product"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpSqrtPi: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "SqrtPi"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpCombinA: public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "Combina"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpLog: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "Log"; }
+};
+
+class OpCombin: public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "ScCombin"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpAverageIf: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+
+ virtual std::string BinFuncName() const override { return "AverageIf"; }
+};
+class OpDeg: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Degrees"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpCountIf: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Countif"; }
+};
+class OpFact : public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "Fact"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpSeriesSum: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SeriesSum"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+class OpSumIf: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "SumIf"; }
+};
+
+class OpQuotient: public OpMathTwoArguments
+{
+public:
+ virtual std::string BinFuncName() const override { return "Quotient"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpNegSub: public OpMathOneArgument
+{
+public:
+ virtual std::string BinFuncName() const override { return "NegSub"; }
+ virtual void GenerateCode( outputstream& ss ) const override;
+};
+
+class OpEqual : public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "eq"; }
+ virtual void BinInlineFun(std::set<std::string>& , std::set<std::string>& ) override;
+ virtual bool takeString() const override { return true; }
+};
+
+class OpNotEqual : public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "neq"; }
+ virtual void BinInlineFun(std::set<std::string>& , std::set<std::string>& ) override;
+ virtual bool takeString() const override { return true; }
+};
+
+class OpLessEqual : public OpMathTwoArguments
+{
+public:
+ virtual void GenerateCode( outputstream& ss ) const override;
+ virtual std::string BinFuncName() const override { return "le"; }
+ virtual void BinInlineFun(std::set<std::string>& , std::set<std::string>& ) override;
+};
+
+class OpLess : public OpMathTwoArguments
+{
+public:
+ virtual void GenerateCode( outputstream& ss ) const override;
+ virtual std::string BinFuncName() const override { return "lt"; }
+};
+
+class OpGreaterEqual : public OpMathTwoArguments
+{
+public:
+ virtual void GenerateCode( outputstream& ss ) const override;
+ virtual std::string BinFuncName() const override { return "ge"; }
+ virtual void BinInlineFun(std::set<std::string>& , std::set<std::string>& ) override;
+};
+
+class OpGreater : public OpMathTwoArguments
+{
+public:
+ virtual void GenerateCode( outputstream& ss ) const override;
+ virtual std::string BinFuncName() const override { return "gt"; }
+};
+
+class SumOfProduct : public SlidingFunctionBase
+{
+public:
+ virtual void GenSlidingWindowFunction( outputstream& ss,
+ const std::string& sSymName, SubArguments& vSubArguments ) override;
+ virtual bool takeString() const override { return false; }
+ virtual bool takeNumeric() const override { return true; }
+};
+
+class OpSumProduct : public SumOfProduct
+{
+public:
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ return lhs + "*" + rhs;
+ }
+ virtual std::string BinFuncName() const override { return "fsop"; }
+};
+
+/// operator traits
+class OpNop : public Reduction
+{
+public:
+ explicit OpNop(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& ) const override
+ {
+ return lhs;
+ }
+ virtual std::string BinFuncName() const override { return "nop"; }
+};
+
+class OpSum : public Reduction
+{
+public:
+ explicit OpSum(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ outputstream ss;
+ ss << "fsum_approx((" << lhs << "),(" << rhs << "))";
+ return ss.str();
+ }
+ virtual void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual std::string BinFuncName() const override { return "fsum"; }
+ // All arguments are simply summed, so it doesn't matter if SvDoubleVector is split.
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpSub : public Reduction
+{
+public:
+ explicit OpSub(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ return "fsub_approx(" + lhs + "," + rhs + ")";
+ }
+ virtual void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual std::string BinFuncName() const override { return "fsub"; }
+};
+
+class OpMul : public Reduction
+{
+public:
+ explicit OpMul(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "1"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ return lhs + "*" + rhs;
+ }
+ virtual std::string BinFuncName() const override { return "fmul"; }
+ virtual bool ZeroReturnZero() override { return true; }
+};
+
+/// Technically not a reduction, but fits the framework.
+class OpDiv : public Reduction
+{
+public:
+ explicit OpDiv(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "1.0"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ return "(" + rhs + "==0 ? CreateDoubleError(DivisionByZero) : (" + lhs + "/" + rhs + ") )";
+ }
+ virtual std::string BinFuncName() const override { return "fdiv"; }
+
+ virtual bool HandleNaNArgument( outputstream& ss, unsigned argno, SubArguments& vSubArguments ) const override
+ {
+ if (argno == 1)
+ {
+ ss <<
+ "if (isnan(" << vSubArguments[argno]->GenSlidingWindowDeclRef() << ")) {\n"
+ " return CreateDoubleError(DivisionByZero);\n"
+ "}\n";
+ return true;
+ }
+ else if (argno == 0)
+ {
+ ss <<
+ "if (isnan(" << vSubArguments[argno]->GenSlidingWindowDeclRef() << ") &&\n"
+ " !(isnan(" << vSubArguments[1]->GenSlidingWindowDeclRef() << ") || " << vSubArguments[1]->GenSlidingWindowDeclRef() << " == 0)) {\n"
+ " return 0;\n"
+ "}\n";
+ }
+ return false;
+ }
+
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_math_helpers.hxx b/sc/source/core/opencl/op_math_helpers.hxx
new file mode 100644
index 0000000000..24aa73c560
--- /dev/null
+++ b/sc/source/core/opencl/op_math_helpers.hxx
@@ -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/.
+ */
+
+#pragma once
+
+const char Math_IntgDecl[] ="double Intg(double n);\n";
+const char Math_Intg[] =
+"double Intg(double n)\n"
+"{\n"
+" if(trunc(n)==n )\n"
+" return n;\n"
+" else if(n<0)\n"
+" return trunc(n)-1;\n"
+" else\n"
+" return trunc(n)+1;\n"
+"}\n";
+
+const char nCorrValDecl[] ="double constant nCorrVal[]"
+"= {0, 9e-1, 9e-2, 9e-3, 9e-4, 9e-5, 9e-6, 9e-7, "
+"9e-8,9e-9, 9e-10, 9e-11, 9e-12, 9e-13, 9e-14, 9e-15};\n";
+
+const char RoundDecl[] = "double Round(double fValue);\n";
+
+const char Round[] =
+"double Round(double fValue)\n"
+"{\n"
+" if ( fValue == 0.0 )\n"
+" return fValue;\n"
+"\n"
+" double fFac = 0;\n"
+" int nExp;\n"
+" if ( fValue > 0.0 )\n"
+" nExp = ( floor( log10( fValue ) ) );\n"
+" else\n"
+" nExp = 0;\n"
+" int nIndex = 15 - nExp;\n"
+" if ( nIndex > 15 )\n"
+" nIndex = 15;\n"
+" else if ( nIndex <= 1 )\n"
+" nIndex = 0;\n"
+" fValue = floor( fValue + 0.5 + nCorrVal[nIndex] );\n"
+" return fValue;\n"
+"}\n";
+
+const char bikDecl[] = "double bik(double n,double k);\n";
+const char bik[] =
+"double bik(double n,double k)\n"
+"{\n"
+" double nVal1 = n;\n"
+" double nVal2 = k;\n"
+" n = n - 1;\n"
+" k = k - 1;\n"
+" while (k > 0)\n"
+" {\n"
+" nVal1 = nVal1 * n;\n"
+" nVal2 = nVal2 * k;\n"
+" k = k - 1;\n"
+" n = n - 1;\n"
+" }\n"
+" return (nVal1 / nVal2);\n"
+"}\n";
+
+const char local_cothDecl[] = "double local_coth(double n);\n";
+const char local_coth[] =
+"double local_coth(double n)\n"
+"{\n"
+" double a = exp(n);\n"
+" double b = exp(-n);\n"
+" double nVal = (a + b) / (a - b);\n"
+" return nVal;\n"
+"}\n";
+
+const char local_coshDecl[] = "double local_cosh(double n);\n";
+const char local_cosh[] =
+"double local_cosh(double n)\n"
+"{\n"
+" double nVal = (exp(n) + exp(-n)) / 2;\n"
+" return nVal;\n"
+"}\n";
+const char atan2Decl[] = "double arctan2(double y, double x);\n";
+const char atan2Content[] =
+"double arctan2(double y, double x)\n"
+"{\n"
+" if(y==0.0)\n"
+" return x >= 0 ? 0.0 : M_PI;\n"
+" double a,num,den,tmpPi;\n"
+" int flag;\n"
+" tmpPi = 0;\n"
+" if (fabs(x) >= fabs(y))\n"
+" {\n"
+" num = y;\n"
+" den = x;\n"
+" flag = 1;\n"
+" if (x < 0.0)\n"
+" tmpPi = M_PI;\n"
+" }\n"
+" if(fabs(x) < fabs(y))\n"
+" {\n"
+" num = x;\n"
+" den = y;\n"
+" flag = -1;\n"
+" tmpPi = M_PI_2;\n"
+" }\n"
+" a = atan(num/den);\n"
+" a = flag==1?a:-a;\n"
+" a = a + (y >= 0.0 ? tmpPi : -tmpPi);\n"
+" return a;\n"
+"}\n";
+
+const char is_representable_integerDecl[] = "int is_representable_integer(double a);\n";
+const char is_representable_integer[] =
+"int is_representable_integer(double a) {\n"
+" long kMaxInt = (1L << 53) - 1;\n"
+" if (a <= (double)kMaxInt)\n"
+" {\n"
+" long nInt = (long)a;\n"
+" double fInt;\n"
+" return (nInt <= kMaxInt &&\n"
+" (!((fInt = (double)nInt) < a) && !(fInt > a)));\n"
+" }\n"
+" return 0;\n"
+"}\n";
+
+const char approx_equalDecl[] = "int approx_equal(double a, double b);\n";
+const char approx_equal[] =
+"int approx_equal(double a, double b) {\n"
+" double e48 = 1.0 / (16777216.0 * 16777216.0);\n"
+" double e44 = e48 * 16.0;\n"
+" if (a == b)\n"
+" return 1;\n"
+" if (a == 0.0 || b == 0.0)\n"
+" return 0;\n"
+" double d = fabs(a - b);\n"
+" if (!isfinite(d))\n"
+" return 0; // Nan or Inf involved\n"
+" if (d > ((a = fabs(a)) * e44) || d > ((b = fabs(b)) * e44))\n"
+" return 0;\n"
+" if (is_representable_integer(d) && is_representable_integer(a) && is_representable_integer(b))\n"
+" return 0; // special case for representable integers.\n"
+" return (d < a * e48 && d < b * e48);\n"
+"}\n";
+
+const char fsum_approxDecl[] = "double fsum_approx(double a, double b);\n";
+const char fsum_approx[] =
+"double fsum_approx(double a, double b) {\n"
+" if ( ((a < 0.0 && b > 0.0) || (b < 0.0 && a > 0.0))\n"
+" && approx_equal( a, -b ) )\n"
+" return 0.0;\n"
+" return a + b;\n"
+"}\n";
+
+const char fsub_approxDecl[] = "double fsub_approx(double a, double b);\n";
+const char fsub_approx[] =
+"double fsub_approx(double a, double b) {\n"
+" if ( ((a < 0.0 && b < 0.0) || (a > 0.0 && b > 0.0)) && approx_equal( a, b ) )\n"
+" return 0.0;\n"
+" return a - b;\n"
+"}\n";
+
+const char value_approxDecl[] = "double value_approx( double fValue );\n";
+const char value_approx[] =
+"double value_approx( double fValue )\n"
+"{\n"
+" const double fBigInt = 2199023255552.0;\n"
+" if (fValue == 0.0 || fValue == HUGE_VAL || !isfinite(fValue))\n"
+" return fValue;\n"
+" double fOrigValue = fValue;\n"
+" fValue = fabs(fValue);\n"
+" if (fValue > fBigInt)\n"
+" return fOrigValue;\n"
+" if (is_representable_integer(fValue))\n" // TODO? || getBitsInFracPart(fValue) <= 11)\n"
+" return fOrigValue;\n"
+" int nExp = (int)(floor(log10(fValue)));\n"
+" nExp = 14 - nExp;\n"
+" double fExpValue = pow(10.0,nExp);\n"
+" fValue *= fExpValue;\n"
+" if (!isfinite(fValue))\n"
+" return fOrigValue;\n"
+" fValue = round(fValue);\n"
+" fValue /= fExpValue;\n"
+" if (!isfinite(fValue))\n"
+" return fOrigValue;\n"
+" return copysign(fValue, fOrigValue);\n"
+"}\n";
+
+// This compares two cell contents, including possible string equalities (based on string ids
+// coming from DynamicKernelArgument::GetStringId()).
+// The EmptyIsZero conversion must not be have been done for the arguments.
+const char cell_equalDecl[] = "bool cell_equal(double a, double b, bool a_is_string, bool b_is_string);\n";
+const char cell_equal[] =
+"bool cell_equal(double a, double b, bool a_is_string, bool b_is_string) {\n"
+" if( !a_is_string && !b_is_string )\n"
+" return approx_equal( isnan(a) ? 0 : a, isnan(b) ? 0 : b );\n"
+" if( a_is_string && b_is_string )\n"
+" return a == b;\n"
+// empty strings and empty cells compare equal
+" if(( a_is_string && a == 0 && isnan(b)) || ( b_is_string && b == 0 && isnan(a)))\n"
+" return true;\n"
+" return false;\n"
+"}\n";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_spreadsheet.cxx b/sc/source/core/opencl/op_spreadsheet.cxx
new file mode 100644
index 0000000000..db99a1e7cf
--- /dev/null
+++ b/sc/source/core/opencl/op_spreadsheet.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/.
+ */
+
+#include "op_spreadsheet.hxx"
+
+#include <rtl/math.hxx>
+#include <formula/vectortoken.hxx>
+
+#include <algorithm>
+#include <sstream>
+
+using namespace formula;
+
+namespace sc::opencl {
+
+void OpVLookup::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp = CreateDoubleError(NOTAVAILABLE);\n";
+ ss << " double intermediate = DBL_MAX;\n";
+ ss << " int singleIndex = gid0;\n";
+ ss << " int rowNum = -1;\n";
+
+ GenTmpVariables(ss,vSubArguments);
+ int arg=0;
+ CheckSubArgumentIsNan(ss,vSubArguments,arg++);
+ int secondParaWidth = 1;
+
+ // tdf#99512 - for now only allow non-dynamic indices (the
+ // common-case) to validate consistent return types vs. the input.
+ int index = 0;
+ int indexArg = vSubArguments.size() - 2;
+ if (vSubArguments[indexArg]->GetFormulaToken()->GetType() == formula::svDouble)
+ {
+ const formula::FormulaDoubleToken *dblToken = static_cast<const FormulaDoubleToken *>(vSubArguments[indexArg]->GetFormulaToken());
+ index = ::rtl::math::approxFloor(dblToken->GetDouble());
+ }
+
+ if (vSubArguments[1]->GetFormulaToken()->GetType() != formula::svDoubleVectorRef)
+ throw Unhandled(__FILE__, __LINE__); // unusual vlookup.
+
+ FormulaToken *tmpCur = vSubArguments[1]->GetFormulaToken();
+ const formula::DoubleVectorRefToken*pCurDVR = static_cast<const formula::DoubleVectorRefToken *>(tmpCur);
+ const std::vector<VectorRefArray> items = pCurDVR->GetArrays();
+
+ secondParaWidth = items.size();
+
+ if (index < 1 || index > secondParaWidth)
+ throw Unhandled(__FILE__, __LINE__); // oob index.
+
+ if (items[index - 1].mpStringArray)
+ {
+ rtl_uString **pStrings = items[index - 1].mpStringArray;
+ for (size_t i = 0; i < pCurDVR->GetArrayLength(); ++i)
+ {
+ if (pStrings[i] != nullptr)
+ { // TODO: the GroupTokenConverter should do better.
+ throw Unhandled(__FILE__, __LINE__); // mixed arguments.
+ }
+ }
+ }
+
+
+ arg += secondParaWidth;
+ CheckSubArgumentIsNan(ss,vSubArguments,arg++);
+
+ if (vSubArguments.size() == static_cast<unsigned int>(3+(secondParaWidth-1)))
+ {
+ ss << " double tmp";
+ ss << 3+(secondParaWidth-1);
+ ss << "= 1;\n";
+ }
+ else
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments,arg++);
+ }
+
+ if (vSubArguments[1]->GetFormulaToken()->GetType() == formula::svDoubleVectorRef)
+ {
+ tmpCur = vSubArguments[1]->GetFormulaToken();
+ pCurDVR = static_cast<const formula::DoubleVectorRefToken *>(tmpCur);
+ size_t nCurWindowSize = std::min(pCurDVR->GetArrayLength(), pCurDVR->GetRefRowSize());
+ const int unrollSize = 8;
+
+ ss << "\n";
+ ss << " int loop = ";
+ if (!pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed())
+ {
+ ss << "("<<nCurWindowSize<<" - gid0)/";
+ ss << unrollSize<<";\n";
+ }
+ else if (pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << "("<<nCurWindowSize<<" + gid0)/";
+ ss << unrollSize<<";\n";
+ }
+ else
+ {
+ ss << nCurWindowSize<<"/"<< unrollSize<<";\n";
+ }
+
+ ss << " if(tmp";
+ ss << 3+(secondParaWidth-1);
+ ss << " == 0) /* unsorted vlookup */\n";
+ ss << " {\n";
+
+ for( int sorted = 0; sorted < 2; ++sorted ) // sorted vs unsorted vlookup cases
+ {
+ if( sorted == 1 )
+ {
+ ss << " }\n";
+ ss << " else\n";
+ ss << " { /* sorted vlookup */ \n";
+ }
+
+ ss << " for ( int j = 0;j< loop; j++)\n";
+ ss << " {\n";
+ ss << " int i = ";
+ if (!pCurDVR->IsStartFixed()&& pCurDVR->IsEndFixed())
+ {
+ ss << "gid0 + j * "<< unrollSize <<";\n";
+ }
+ else
+ {
+ ss << "j * "<< unrollSize <<";\n";
+ }
+ if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << " int doubleIndex = i+gid0;\n";
+ }
+ else
+ {
+ ss << " int doubleIndex = i;\n";
+ }
+
+ for (int j = 0;j < unrollSize; j++)
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments,1);
+
+ if( sorted == 1 )
+ {
+ ss << " if((tmp0 - tmp1)>=0 && intermediate > (tmp0 -tmp1))\n";
+ ss << " {\n";
+ ss << " rowNum = doubleIndex;\n";
+ ss << " intermediate = tmp0 - tmp1;\n";
+ ss << " }\n";
+ ss << " i++;\n";
+ ss << " doubleIndex++;\n";
+ }
+ else
+ {
+ ss << " if(tmp0 == tmp1)\n";
+ ss << " {\n";
+ ss << " rowNum = doubleIndex;\n";
+ ss << " break;\n";
+ ss << " }\n";
+ ss << " i++;\n";
+ ss << " doubleIndex++;\n";
+ }
+ }
+ ss << " }\n\n";
+ }
+
+ ss << " }\n";
+ ss << " if(rowNum!=-1)\n";
+ ss << " {\n";
+ for (int j = 0; j < secondParaWidth; j++)
+ {
+ ss << " if(tmp";
+ ss << 2+(secondParaWidth-1);
+ ss << " == ";
+ ss << j+1;
+ ss << ")\n";
+ ss << " tmp = ";
+ vSubArguments[1+j]->GenDeclRef(ss);
+ ss << "[rowNum];\n";
+ }
+ ss << " return tmp;\n";
+ ss << " }\n";
+
+ ss << " if(tmp";
+ ss << 3+(secondParaWidth-1);
+ ss << " == 0) /* unsorted vlookup */\n";
+ ss << " {\n";
+
+ for( int sorted = 0; sorted < 2; ++sorted ) // sorted vs unsorted vlookup cases
+ {
+ if( sorted == 1 )
+ {
+ ss << " }\n";
+ ss << " else\n";
+ ss << " { /* sorted vlookup */ \n";
+ }
+
+ ss << " for (int i = ";
+ if (!pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed())
+ {
+ ss << "gid0 + loop *"<<unrollSize<<"; i < ";
+ ss << nCurWindowSize <<"; i++)\n";
+ }
+ else if (pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << "0 + loop *"<<unrollSize<<"; i < gid0+";
+ ss << nCurWindowSize <<"; i++)\n";
+ }
+ else
+ {
+ ss << "0 + loop *"<<unrollSize<<"; i < ";
+ ss << nCurWindowSize <<"; i++)\n";
+ }
+ ss << " {\n";
+ if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << " int doubleIndex = i+gid0;\n";
+ }
+ else
+ {
+ ss << " int doubleIndex = i;\n";
+ }
+ CheckSubArgumentIsNan(ss,vSubArguments,1);
+
+ if( sorted == 1 )
+ {
+ ss << " if((tmp0 - tmp1)>=0 && intermediate > (tmp0 -tmp1))\n";
+ ss << " {\n";
+ ss << " rowNum = doubleIndex;\n";
+ ss << " intermediate = tmp0 - tmp1;\n";
+ ss << " }\n";
+ }
+ else
+ {
+ ss << " if(tmp0 == tmp1)\n";
+ ss << " {\n";
+ ss << " rowNum = doubleIndex;\n";
+ ss << " break;\n";
+ ss << " }\n";
+ }
+ ss << " }\n\n";
+ }
+
+ ss << " }\n";
+ ss << " if(rowNum!=-1)\n";
+ ss << " {\n";
+
+ for (int j = 0; j < secondParaWidth; j++)
+ {
+ ss << " if(tmp";
+ ss << 2+(secondParaWidth-1);
+ ss << " == ";
+ ss << j+1;
+ ss << ")\n";
+ ss << " tmp = ";
+ vSubArguments[1+j]->GenDeclRef(ss);
+ ss << "[rowNum];\n";
+ }
+ ss << " return tmp;\n";
+ ss << " }\n";
+ }
+ else
+ {
+ CheckSubArgumentIsNan(ss,vSubArguments,1);
+ ss << " if(tmp3 == 1)\n";
+ ss << " {\n";
+ ss << " tmp = tmp1;\n";
+ ss << " }else\n";
+ ss << " {\n";
+ ss << " if(tmp0 == tmp1)\n";
+ ss << " tmp = tmp1;\n";
+ ss << " }\n";
+ }
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_spreadsheet.hxx b/sc/source/core/opencl/op_spreadsheet.hxx
new file mode 100644
index 0000000000..2d76fb8fe4
--- /dev/null
+++ b/sc/source/core/opencl/op_spreadsheet.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+class OpVLookup: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "VLookup"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_statistical.cxx b/sc/source/core/opencl/op_statistical.cxx
new file mode 100644
index 0000000000..4c79d162e6
--- /dev/null
+++ b/sc/source/core/opencl/op_statistical.cxx
@@ -0,0 +1,2161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "op_statistical.hxx"
+
+#include <formula/vectortoken.hxx>
+#include <sstream>
+#include "op_statistical_helpers.hxx"
+
+using namespace formula;
+
+#include "op_math_helpers.hxx"
+
+namespace sc::opencl {
+
+void OpZTest::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(phiDecl);
+ funs.insert(phi);
+ decls.insert(taylorDecl);
+ funs.insert(taylor);
+ decls.insert(gaussDecl);
+ funs.insert(gauss);
+}
+void OpZTest::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSum = 0.0;\n";
+ ss << " double fSumSqr = 0.0;\n";
+ ss << " double mue = 0.0;\n";
+ ss << " double fCount = 0.0;\n";
+ GenerateRangeArg( 0, vSubArguments, ss, SkipEmpty,
+ " fSum += arg;\n"
+ " fSumSqr += arg * arg;\n"
+ " fCount += 1.0;\n"
+ );
+ ss << " if(fCount <= 1.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " mue = fSum / fCount;\n";
+ GenerateArg( "mu", 1, vSubArguments, ss );
+ if(vSubArguments.size() == 3)
+ {
+ GenerateArg( "sigma", 2, vSubArguments, ss );
+ ss << " if(sigma <= 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return 0.5 - gauss((mue-mu)*sqrt(fCount)/sigma);\n";
+ }
+ else
+ {
+ ss << " double sigma = (fSumSqr-fSum*fSum/fCount)/(fCount-1.0);\n";
+ ss << " if(sigma == 0.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " return 0.5 - gauss((mue-mu)/sqrt(sigma/fCount));\n";
+ }
+ ss << "}\n";
+}
+
+void OpTTest::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);
+ funs.insert("");
+ decls.insert(fMaxGammaArgumentDecl);
+ funs.insert("");
+ decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(lcl_getLanczosSum);
+ decls.insert(GetBetaDecl);
+ funs.insert(GetBeta);
+ decls.insert(GetLogBetaDecl);
+ funs.insert(GetLogBeta);
+ decls.insert(GetBetaDistPDFDecl);
+ funs.insert(GetBetaDistPDF);
+ decls.insert(lcl_GetBetaHelperContFracDecl);
+ funs.insert(lcl_GetBetaHelperContFrac);
+ decls.insert(GetBetaDistDecl);
+ funs.insert(GetBetaDist);
+ decls.insert(GetTDistDecl);
+ funs.insert(GetTDist);
+}
+
+void OpTTest::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSum1 = 0.0;\n";
+ ss << " double fSum2 = 0.0;\n";
+ ss << " double fSumSqr1 = 0.0;\n";
+ ss << " double fSumSqr2 = 0.0;\n";
+ ss << " double fCount1 = 0.0;\n";
+ ss << " double fCount2 = 0.0;\n";
+ ss << " double fT = 0.0;\n";
+ ss << " double fF = 0.0;\n";
+ GenerateArg( "mode", 2, vSubArguments, ss );
+ GenerateArg( "type", 3, vSubArguments, ss );
+ ss << " mode = floor(mode);\n";
+ ss << " type = floor(type);\n";
+ ss << " if(mode != 1.0 && mode != 2.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if(type != 1.0 && type != 2.0 && type != 3.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+
+ ss << " if(type == 1.0)\n";
+ ss << " {\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " fSum1 += arg1;\n"
+ " fSum2 += arg2;\n"
+ " fSumSqr1 += (arg1 - arg2)*(arg1 - arg2);\n"
+ " fCount1 += 1;\n"
+ );
+ ss << " if(fCount1 < 1.0)\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " double divider = sqrt(fCount1 * fSumSqr1 - (fSum1-fSum2)*(fSum1-fSum2));\n";
+ ss << " if(divider == 0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " fT = sqrt(fCount1-1.0) * fabs(fSum1 - fSum2) / divider;\n";
+ ss << " fF = fCount1 - 1.0;\n";
+ ss << " }\n";
+ ss << " if(type == 2.0 || type == 3.0)\n";
+ ss << " {\n";
+ GenerateRangeArg( 0, vSubArguments, ss, SkipEmpty,
+ " fSum1 += arg;\n"
+ " fSumSqr1 += arg * arg;\n"
+ " fCount1 += 1;\n"
+ );
+ GenerateRangeArg( 1, vSubArguments, ss, SkipEmpty,
+ " fSum2 += arg;\n"
+ " fSumSqr2 += arg * arg;\n"
+ " fCount2 += 1;\n"
+ );
+ ss << " if (fCount1 < 2.0 || fCount2 < 2.0)\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " }\n";
+ ss << " if(type == 3.0)\n";
+ ss << " {\n";
+ ss << " double fS1 = (fSumSqr1-fSum1*fSum1/fCount1)\n";
+ ss << " /(fCount1-1.0)/fCount1;\n";
+ ss << " double fS2 = (fSumSqr2-fSum2*fSum2/fCount2)\n";
+ ss << " /(fCount2-1.0)/fCount2;\n";
+ ss << " if (fS1 + fS2 == 0.0)\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " fT = fabs(fSum1/fCount1 - fSum2/fCount2)\n";
+ ss << " /sqrt(fS1+fS2);\n";
+ ss << " double c = fS1/(fS1+fS2);\n";
+ ss << " fF = 1.0/(c*c/(fCount1-1.0)+(1.0-c)*(1.0-c)\n";
+ ss << " /(fCount2-1.0));\n";
+ ss << " }\n";
+ ss << " if(type == 2.0)\n";
+ ss << " {\n";
+ ss << " double fS1 = (fSumSqr1 - fSum1*fSum1/fCount1)\n";
+ ss << " /(fCount1 - 1.0);\n";
+ ss << " double fS2 = (fSumSqr2 - fSum2*fSum2/fCount2)\n";
+ ss << " /(fCount2 - 1.0);\n";
+ ss << " fT = fabs( fSum1/fCount1 - fSum2/fCount2 )\n";
+ ss << " /sqrt( (fCount1-1.0)*fS1 + (fCount2-1.0)*fS2 )\n";
+ ss << " *sqrt( fCount1*fCount2*(fCount1+fCount2-2)\n";
+ ss << " /(fCount1+fCount2) );\n";
+ ss << " fF = fCount1 + fCount2 - 2;\n";
+ ss << " }\n";
+ ss << " double tdist=GetTDist(fT, fF);\n";
+ ss << " if (mode==1)\n";
+ ss << " return tdist;\n";
+ ss << " else\n";
+ ss << " return 2.0*tdist;\n";
+ ss << "}\n";
+}
+
+void OpTDist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);
+ funs.insert("");
+ decls.insert(fMaxGammaArgumentDecl);
+ funs.insert("");
+ decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(lcl_getLanczosSum);
+ decls.insert(GetBetaDecl);
+ funs.insert(GetBeta);
+ decls.insert(GetLogBetaDecl);
+ funs.insert(GetLogBeta);
+ decls.insert(GetBetaDistPDFDecl);
+ funs.insert(GetBetaDistPDF);
+ decls.insert(lcl_GetBetaHelperContFracDecl);
+ funs.insert(lcl_GetBetaHelperContFrac);
+ decls.insert(GetBetaDistDecl);
+ funs.insert(GetBetaDist);
+ decls.insert(GetTDistDecl);
+ funs.insert(GetTDist);
+}
+void OpTDist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "fDF", 1, vSubArguments, ss );
+ GenerateArg( "fFlag", 2, vSubArguments, ss );
+ ss << " fDF = floor( fDF );\n";
+ ss << " fFlag = floor( fFlag );\n";
+ ss << " if(fDF < 1.0 || x < 0.0 || (fFlag != 1.0 && fFlag != 2.0))\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double R = GetTDist(x, fDF);\n";
+ ss << " if (fFlag == 1.0)\n";
+ ss << " return R;\n";
+ ss << " else\n";
+ ss << " return 2.0 * R;\n";
+ ss << "}\n";
+}
+
+void OpExponDist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "rx", 0, vSubArguments, ss );
+ GenerateArg( "rlambda", 1, vSubArguments, ss );
+ GenerateArg( "rkum", 2, vSubArguments, ss );
+ ss <<" if(rlambda <= 0.0)\n";
+ ss <<" return CreateDoubleError(IllegalArgument);\n";
+ ss <<" else if(rkum == 0)\n";
+ ss <<" {\n";
+ ss <<" if(rx >= 0)\n";
+ ss <<" tmp = rlambda*exp(-rlambda*rx);\n";
+ ss <<" else\n";
+ ss <<" tmp = 0.0;\n";
+ ss <<" }\n";
+ ss <<" else\n";
+ ss <<" {\n";
+ ss <<" if(rx > 0)\n";
+ ss <<" tmp = 1.0 - exp(-rlambda*rx);\n";
+ ss <<" else\n";
+ ss <<" tmp = 0.0;\n";
+ ss <<" }\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+void OpFdist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(GetFDistDecl);decls.insert(GetBetaDistDecl);
+ decls.insert(GetBetaDecl);decls.insert(fMaxGammaArgumentDecl);
+ decls.insert(lcl_GetBetaHelperContFracDecl);
+ decls.insert(GetBetaDistPDFDecl);
+ decls.insert(GetLogBetaDecl);decls.insert(lcl_getLanczosSumDecl);
+ decls.insert(fMachEpsDecl);
+ funs.insert(GetFDist);funs.insert(GetBetaDist);
+ funs.insert(GetBeta);
+ funs.insert(lcl_GetBetaHelperContFrac);funs.insert(GetBetaDistPDF);
+ funs.insert(GetLogBeta);
+ funs.insert(lcl_getLanczosSum);
+}
+void OpFdist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = 0;\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "rX", 0, vSubArguments, ss );
+ GenerateArg( "rF1", 1, vSubArguments, ss );
+ GenerateArg( "rF2", 2, vSubArguments, ss );
+ ss <<" rF1 = floor(rF1);\n";
+ ss <<" rF2 = floor(rF2);\n";
+ ss <<" if (rX < 0.0 || rF1 < 1.0 || rF2 < 1.0 || rF1 >= 1.0E10 ||";
+ ss <<"rF2 >= 1.0E10)\n";
+ ss <<" return CreateDoubleError(IllegalArgument);\n";
+ ss <<" tmp = GetFDist(rX, rF1, rF2);\n";
+ ss <<" return tmp;\n";
+ ss <<"}";
+}
+
+void OpStandard::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "mu", 1, vSubArguments, ss );
+ GenerateArg( "sigma", 2, vSubArguments, ss );
+ ss << " if(sigma < 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else if(sigma == 0.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " else\n";
+ ss << " return (x - mu)/sigma;\n";
+ ss << "}";
+}
+
+void OpWeibull::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "alpha", 1, vSubArguments, ss );
+ GenerateArg( "beta", 2, vSubArguments, ss );
+ GenerateArg( "kum", 3, vSubArguments, ss );
+ ss << " if(alpha <= 0.0 || beta <=0.0 || x < 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if (kum == 0.0)\n";
+ ss << " return alpha/pow(beta,alpha)*pow(x,alpha-1.0)*\n";
+ ss << " exp(-pow(x/beta,alpha));\n";
+ ss << " else\n";
+ ss << " return 1.0 - exp(-pow(x/beta,alpha));\n";
+ ss << "}\n";
+}
+
+void OpTInv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);
+ funs.insert("");
+ decls.insert(fMaxGammaArgumentDecl);
+ funs.insert("");
+ decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(lcl_getLanczosSum);
+ decls.insert(GetBetaDecl);
+ funs.insert(GetBeta);
+ decls.insert(GetLogBetaDecl);
+ funs.insert(GetLogBeta);
+ decls.insert(GetBetaDistPDFDecl);
+ funs.insert(GetBetaDistPDF);
+ decls.insert(lcl_GetBetaHelperContFracDecl);
+ funs.insert(lcl_GetBetaHelperContFrac);
+ decls.insert(GetBetaDistDecl);
+ funs.insert(GetBetaDist);
+ decls.insert(GetTDistDecl);
+ funs.insert(GetTDist);
+ decls.insert(GetValueDecl);
+ funs.insert(GetValue);
+ decls.insert(lcl_HasChangeOfSignDecl);
+ funs.insert(lcl_HasChangeOfSign);
+ decls.insert(lcl_IterateInverseDecl);
+ funs.insert(lcl_IterateInverse);
+}
+
+void OpTInv::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "fDF", 1, vSubArguments, ss );
+ ss << " fDF = floor(fDF);\n";
+ ss << " if (x > 1.0||fDF < 1.0 || fDF > 1.0E10 || x <= 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " bool bConvError;\n";
+ ss << " double fVal = lcl_IterateInverse(\n";
+ ss << " fDF*0.5, fDF, &bConvError,x,fDF );\n";
+ ss << " if (bConvError)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return fVal;\n";
+ ss << "}\n";
+}
+
+void OpFisher::GenSlidingWindowFunction( outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << " if (fabs(arg0) >= 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double tmp=0.5*log((1+arg0)/(1-arg0));\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpFisherInv::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << " double tmp=tanh(arg0);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpGamma::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << " double tmp=tgamma(arg0);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpNegbinomdist::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "f", 0, vSubArguments, ss );
+ GenerateArg( "s", 1, vSubArguments, ss );
+ GenerateArg( "p", 2, vSubArguments, ss );
+ ss << " f = floor( f );\n";
+ ss << " s = floor( s );\n";
+ ss << " if ((f + s) <= 1.0 || p < 0.0 || p > 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double q = 1.0 - p;\n";
+ ss << " double fFactor = pow(p,s);\n";
+ ss << " for(int i=0; i<f; i++)\n";
+ ss << " fFactor *= (i+s)/(i+1.0)*q;\n";
+ ss << " return fFactor;\n";
+ ss << "}\n";
+}
+
+void OpGammaLn::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss <<"int gid0=get_global_id(0);\n\t";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << "double tmp=lgamma(arg0);\n\t";
+ ss << "return tmp;\n";
+ ss << "}\n";
+}
+void OpGauss::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(taylorDecl);decls.insert(phiDecl);
+ decls.insert(gaussDecl);
+ funs.insert(taylor);funs.insert(phi);
+ funs.insert(gauss);
+}
+
+void OpGauss::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &
+vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ ss << " double tmp=gauss(arg0);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpGeoMean::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &
+vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double nVal=0.0;\n";
+ ss << " double tmp = 0;\n";
+ ss << " int length;\n";
+ ss << " int totallength=0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " if( arg < 0 )\n"
+ " return CreateDoubleError(IllegalArgument);\n"
+ " if( arg == 0 )\n"
+ " return 0;\n"
+ " nVal += log(arg);\n"
+ " ++totallength;\n"
+ );
+ ss << " return exp(nVal/totallength);\n";
+ ss << "}";
+}
+
+void OpHarMean::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &
+vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double nVal=0.0;\n";
+ ss << " double tmp = 0;\n";
+ ss << " int length;\n";
+ ss << " int totallength=0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " if( arg <= 0 )\n"
+ " return CreateDoubleError(IllegalArgument);\n"
+ " nVal += (1.0 / arg);\n"
+ " ++totallength;\n"
+ );
+ ss << " return totallength/nVal;\n";
+ ss << "}";
+}
+
+void OpConfidence::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(gaussinvDecl);
+ funs.insert(gaussinv);
+}
+
+void OpConfidence::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() <<";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "alpha", 0, vSubArguments, ss );
+ GenerateArg( "sigma", 1, vSubArguments, ss );
+ GenerateArg( "size", 2, vSubArguments, ss );
+ ss << " double rn = floor(size);\n";
+ ss << " if(sigma <= 0.0 || alpha <= 0.0 || alpha >= 1.0";
+ ss << "|| rn < 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else\n";
+ ss << " tmp = gaussinv(1.0 - alpha / 2.0) * sigma / sqrt( rn );\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpCritBinom::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(MinDecl);
+ funs.insert("");
+}
+
+void OpCritBinom::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp = " << GetBottom() <<";\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ GenerateArg( "n", 0, vSubArguments, ss );
+ GenerateArg( "p", 1, vSubArguments, ss );
+ GenerateArg( "alpha", 2, vSubArguments, ss );
+ ss << " double rn = floor(n);\n";
+ ss << " if (rn < 0.0 || alpha < 0.0 || alpha > 1.0 || p < 0.0";
+ ss << " || p > 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else if ( alpha == 0 )\n";
+ ss << " return 0;\n";
+ ss << " else if ( alpha == 1 )\n";
+ ss << " return p == 0 ? 0 : rn;\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double rq = (0.5 - p) + 0.5;\n";
+ ss << " double fFactor = pow(rq, rn);\n";
+ ss << " if (fFactor <= Min)\n";
+ ss << " {\n";
+ ss << " fFactor = pow(p, rn);\n";
+ ss << " if (fFactor <= Min)\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fSum = 1.0 - fFactor;\n";
+ ss << " uint max =(uint)(rn), i;\n";
+ ss << " for (i = 0; i < max && fSum >= alpha; i++)\n";
+ ss << " {\n";
+ ss << " fFactor *= (rn - i) / (double)(i + 1) * rq / p;\n";
+ ss << " fSum -= fFactor;\n";
+ ss << " }\n";
+ ss << " tmp = (rn - i);\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fSum = fFactor;\n";
+ ss << " uint max = (uint)(rn), i;\n";
+ ss << " for (i = 0; i < max && fSum < alpha; i++)\n";
+ ss << " {\n";
+ ss << " fFactor *= (rn - i) / (double)(i + 1) *";
+ ss << " p / rq;\n";
+ ss << " fSum += fFactor;\n";
+ ss << " }\n";
+ ss << " tmp = (i);\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " return tmp;\n";
+ ss << "}";
+}
+
+void OpChiInv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);
+ funs.insert("");
+ decls.insert(fBigInvDecl);
+ funs.insert("");
+ decls.insert(fHalfMachEpsDecl);
+ funs.insert("");
+ decls.insert(lcl_IterateInverseChiInvDecl);
+ funs.insert(lcl_IterateInverseChiInv);
+ decls.insert(GetChiDistDecl);
+ funs.insert(GetChiDist);
+ decls.insert(lcl_HasChangeOfSignDecl);
+ funs.insert(lcl_HasChangeOfSign);
+ decls.insert(GetUpRegIGammaDecl);
+ funs.insert(GetUpRegIGamma);
+ decls.insert(GetGammaContFractionDecl);
+ funs.insert(GetGammaContFraction);
+ decls.insert(GetGammaSeriesDecl);
+ funs.insert(GetGammaSeries);
+}
+void OpChiInv::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp;\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss <<"\n ";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ ss << " tmp1 = floor(tmp1);";
+ ss << " if (tmp1 < 1.0 || tmp0 <= 0.0 || tmp0 > 1.0 )\n";
+ ss << " {\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " }\n";
+ ss << " bool bConvError;\n";
+ ss << " double fVal = lcl_IterateInverseChiInv";
+ ss << "(tmp0, tmp1, tmp1*0.5, tmp1, &bConvError);\n";
+ ss << " if(bConvError)\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << " return fVal;\n";
+ ss << "}\n";
+}
+void OpNormdist::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT(3,4);
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "mue", 1, vSubArguments, ss );
+ GenerateArg( "sigma", 2, vSubArguments, ss );
+ GenerateArg( "c", 3, vSubArguments, ss );
+ ss << "if(sigma <= 0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << "double mid,tmp;\n";
+ ss << "mid = (x - mue)/sigma;\n";
+ ss << "if(c)\n";
+ ss << " tmp = 0.5 *erfc(-mid * 0.7071067811865475);\n";
+ ss << "else \n";
+ ss <<" tmp=(0.39894228040143268*exp(-pow(mid,2)/2.0))/sigma;\n";
+ ss << "return tmp;\n";
+ ss << "}\n";
+}
+void OpNormsdist::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ ss << " double tmp = 0.5 * erfc((-1)*x * 0.7071067811865475);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpPermut::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ ss <<" double tmp = 1 ;\n";
+ GenerateArg( "inA", 0, vSubArguments, ss );
+ GenerateArg( "inB", 1, vSubArguments, ss );
+ ss << " inA = floor( inA );\n";
+ ss << " inB = floor( inB );\n";
+ ss << " if (inA < 0.0 || inB < 0.0 || inB > inA)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " for( int i = 0; i<inB; i++)\n";
+ ss << " {\n";
+ ss << " tmp *= inA ;\n";
+ ss << " inA = inA - 1.0;\n";
+ ss << " }\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+void OpPermutationA::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss <<" int gid0=get_global_id(0);\n";
+ ss <<" double tmp = 1.0;\n";
+ GenerateArg( "inA", 0, vSubArguments, ss );
+ GenerateArg( "inB", 1, vSubArguments, ss );
+ ss << " inA = floor( inA );\n";
+ ss << " inB = floor( inB );\n";
+ ss << " if (inA < 0.0 || inB < 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return pow(inA, inB);\n";
+ ss << "}\n";
+}
+
+void OpPhi::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ ss << " double tmp = 0.39894228040143268 * exp((-1)*pow(x,2) / 2.0);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpNorminv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(gaussinvDecl);
+ funs.insert(gaussinv);
+}
+
+void OpNorminv::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "mue", 1, vSubArguments, ss );
+ GenerateArg( "sigma", 2, vSubArguments, ss );
+ ss << " if (sigma <= 0.0 || x < 0.0 || x > 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else if (x == 0.0 || x == 1.0)\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " return gaussinv(x)*sigma + mue;\n";
+ ss << "}\n";
+}
+
+void OpNormsinv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(gaussinvDecl);
+ funs.insert(gaussinv);
+}
+
+void OpNormsinv:: GenSlidingWindowFunction
+ (outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ ss << " if (x < 0.0 || x > 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else if (x == 0.0 || x == 1.0)\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " return gaussinv(x);\n";
+ ss << "}\n";
+}
+
+void OpLogInv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(gaussinvDecl);
+ funs.insert(gaussinv);
+}
+
+void OpLogInv:: GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp;\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArgWithDefault( "mue", 1, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "sigma", 2, 1, vSubArguments, ss );
+ ss << " if ( sigma <= 0.0 || x <= 0.0 || x >= 1.0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " return exp(mue+sigma*gaussinv(x));\n";
+ ss << "}\n";
+}
+
+void OpLogNormDist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArgWithDefault( "mue", 1, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "sigma", 2, 1, vSubArguments, ss );
+ GenerateArgWithDefault( "fCumulative", 3, 1, vSubArguments, ss );
+ ss << " if (sigma <= 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double tmp;\n";
+ ss << " double temp = (log(x)-mue)/sigma;\n";
+ ss << " if(fCumulative != 0)\n";
+ ss << " {\n";
+ ss << " if(x<=0)\n";
+ ss << " tmp = 0.0;\n";
+ ss << " else\n";
+ ss << " tmp = 0.5 * erfc(-temp * 0.7071067811865475);\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " if(x<=0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else\n";
+ ss << " tmp = (0.39894228040143268 * exp((-1)*pow(temp, 2)";
+ ss << " / 2.0))/(sigma*x);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpGammaDist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fBigInvDecl);decls.insert(fLogDblMaxDecl);
+ decls.insert(fHalfMachEpsDecl);decls.insert(fMaxGammaArgumentDecl);
+ decls.insert(GetGammaSeriesDecl);decls.insert(GetGammaContFractionDecl);
+ decls.insert(GetLowRegIGammaDecl);decls.insert(GetGammaDistDecl);
+ decls.insert(GetGammaDistPDFDecl);
+ funs.insert(GetGammaSeries);funs.insert(GetGammaContFraction);
+ funs.insert(GetLowRegIGamma);funs.insert(GetGammaDist);
+ funs.insert(GetGammaDistPDF);
+}
+
+void OpGammaDist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArgWithDefault( "arg3", 3, 1, vSubArguments, ss );
+ ss << " if(arg1 <= 0 || arg2 <= 0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " double tmp;\n";
+ ss << " if (arg3)\n";
+ ss << " tmp=GetGammaDist( arg0, arg1, arg2);\n";
+ ss << " else\n";
+ ss << " tmp=GetGammaDistPDF( arg0, arg1, arg2);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+void OpChiDist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fBigInvDecl);
+ funs.insert("");
+ decls.insert(fHalfMachEpsDecl);
+ funs.insert("");
+ decls.insert(GetUpRegIGammaDecl);
+ funs.insert(GetUpRegIGamma);
+ decls.insert(GetGammaSeriesDecl);
+ funs.insert(GetGammaSeries);
+ decls.insert(GetGammaContFractionDecl);
+ funs.insert(GetGammaContFraction);
+ decls.insert(GetChiDistDecl);
+ funs.insert(GetChiDist);
+}
+void OpChiDist::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double fx,fDF,tmp=0;\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ ss << " fx = tmp0;\n";
+ ss << " fDF = floor(tmp1);\n";
+ ss << " if(fDF < 1.0)\n";
+ ss << " {\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " }\n";
+ ss << " tmp = GetChiDist( fx, fDF);\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+void OpBinomdist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);
+ funs.insert("");
+ decls.insert(MinDecl);
+ funs.insert("");
+ decls.insert(fMaxGammaArgumentDecl);
+ funs.insert("");
+ decls.insert(GetBinomDistPMFDecl);
+ funs.insert(GetBinomDistPMF);
+ decls.insert(GetBetaDistDecl);
+ funs.insert(GetBetaDist);
+ decls.insert(lcl_GetBinomDistRangeDecl);
+ funs.insert(lcl_GetBinomDistRange);
+ decls.insert(lcl_GetBetaHelperContFracDecl);
+ funs.insert(lcl_GetBetaHelperContFrac);
+ decls.insert(GetBetaDistPDFDecl);
+ funs.insert(GetBetaDistPDF);
+ decls.insert(GetLogBetaDecl);
+ funs.insert(GetLogBeta);
+ decls.insert(GetBetaDecl);
+ funs.insert(GetBeta);
+ decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(lcl_getLanczosSum);
+}
+void OpBinomdist::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArg( "tmp2", 2, vSubArguments, ss );
+ GenerateArg( "tmp3", 3, vSubArguments, ss );
+ ss << " tmp0 = floor(tmp0);\n";
+ ss << " tmp1 = floor(tmp1);\n";
+ ss << " double rq = (0.5 - tmp2) + 0.5;\n";
+ ss << " if (tmp1 < 0.0 || tmp0 < 0.0 || tmp0 > tmp1 ||";
+ ss << "tmp2 < 0.0 || tmp2 > 1.0)\n";
+ ss << " {\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " }\n";
+ ss << " if(tmp2 == 0.0)\n";
+ ss << " return ( (tmp0 == 0.0 || tmp3) ? 1.0 : 0.0 );\n";
+ ss << " if(tmp2 == 1.0)\n";
+ ss << " return ( (tmp0 == tmp1) ? 1.0 : 0.0);\n";
+ ss << " if(!tmp3)\n";
+ ss << " return ( GetBinomDistPMF(tmp0, tmp1, tmp2));\n";
+ ss << " else \n";
+ ss << " {\n";
+ ss << " if(tmp0 == tmp1)\n";
+ ss << " return 1.0;\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fFactor = pow(rq,tmp1);\n";
+ ss << " if(tmp0 == 0.0)\n";
+ ss << " return (fFactor);\n";
+ ss << " else if(fFactor <= Min)\n";
+ ss << " {\n";
+ ss << " fFactor = pow(tmp2,tmp1);\n";
+ ss << " if(fFactor <= Min)\n";
+ ss << " return GetBetaDist";
+ ss << "(rq, tmp1 - tmp0, tmp0 + 1.0);\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if(fFactor > fMachEps)\n";
+ ss << " {\n";
+ ss << " double fSum = 1.0 - fFactor;\n";
+ ss << " unsigned int max = ";
+ ss << "(unsigned int)((tmp1 - tmp0)-1);\n";
+ ss << " for (uint i = 0; i < max && fFactor > 0.0;";
+ ss << " i++)\n";
+ ss << " {\n";
+ ss << " fFactor *= (tmp1 - i)/(i + 1)*rq/tmp2;\n";
+ ss << " fSum -= fFactor;\n";
+ ss << " }\n";
+ ss << " return ( (fSum < 0.0) ? 0.0 : fSum );\n";
+ ss << " }\n";
+ ss << " else \n";
+ ss << " return (lcl_GetBinomDistRange";
+ ss << "(tmp1, tmp1 - tmp0, tmp1, fFactor, rq, tmp2));\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double rtmp = ( lcl_GetBinomDistRange";
+ ss << "(tmp1, 0.0, tmp0, fFactor, tmp2, rq));\n";
+ ss << " return rtmp;\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << "}\n";
+}
+
+void OpChiSqDist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMaxGammaArgumentDecl);decls.insert(GetChiSqDistCDFDecl);
+ decls.insert(GetChiSqDistPDFDecl);decls.insert(GetLowRegIGammaDecl);
+ decls.insert(GetGammaContFractionDecl);decls.insert(GetGammaSeriesDecl);
+ decls.insert(fHalfMachEpsDecl);
+ decls.insert(fBigInvDecl);
+
+ funs.insert(GetGammaContFraction);funs.insert(GetChiSqDistCDF);
+ funs.insert(GetChiSqDistPDF);funs.insert(GetLowRegIGamma);
+ funs.insert(GetGammaSeries);
+}
+
+void OpChiSqDist::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &
+vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double result = 0;\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp2", 2, 1, vSubArguments, ss );
+ ss << " tmp1 = floor(tmp1);\n";
+ ss << " if(tmp1 < 1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if(tmp2)\n";
+ ss << " result =GetChiSqDistCDF(tmp0,tmp1);\n";
+ ss << " else\n";
+ ss << " result =GetChiSqDistPDF(tmp0,tmp1);\n";
+ ss << " }\n";
+ ss << " return result;\n";
+ ss << "}";
+}
+
+ void OpChiSqInv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMaxGammaArgumentDecl);decls.insert(GetChiSqDistCDFDecl);
+ decls.insert(GetLowRegIGammaDecl);decls.insert(lcl_IterateInverseChiSQInvDecl);
+ decls.insert(GetGammaContFractionDecl);decls.insert(GetGammaSeriesDecl);
+ decls.insert(fHalfMachEpsDecl);
+ decls.insert(fBigInvDecl);decls.insert(lcl_HasChangeOfSignDecl);
+ decls.insert(fMachEpsDecl);
+
+ funs.insert(GetGammaContFraction);funs.insert(GetChiSqDistCDF);
+ funs.insert(GetLowRegIGamma);funs.insert(lcl_HasChangeOfSign);
+ funs.insert(GetGammaSeries);funs.insert(lcl_IterateInverseChiSQInv);
+}
+
+void OpChiSqInv::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &
+vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double result = 0;\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ ss << " tmp1 = floor(tmp1);\n";
+ ss << " bool bConvError;\n";
+ ss << " if(tmp1 < 1.0 || tmp0 < 0 || tmp0>=1.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " result =lcl_IterateInverseChiSQInv( tmp0, tmp1,";
+ ss << "tmp1*0.5, tmp1, &bConvError );\n";
+ ss << " }\n";
+ ss << " if(bConvError)\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << " return result;\n";
+ ss << "}";
+}
+
+void OpGammaInv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fBigInvDecl);decls.insert(fHalfMachEpsDecl);
+ decls.insert(GetGammaSeriesDecl);decls.insert(GetGammaContFractionDecl);
+ decls.insert(GetGammaInvValueDecl);
+ funs.insert(GetGammaSeries);funs.insert(GetGammaContFraction);
+ funs.insert(GetGammaInvValue);
+}
+
+void OpGammaInv::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp;\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ ss << " if( arg0 < 0 || arg0 >= 1 || arg1 <= 0 || arg2 <= 0 )\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if (arg0 == 0.0)\n"
+ " {\n"
+ " tmp=0.0;\n"
+ " return tmp;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " bool bConvError;\n"
+ " double fStart = arg1 * arg2;\n"
+ " double fAx=fStart*0.5;\n"
+ " double fBx=fStart;\n"
+ " bConvError = false;\n"
+ " double fYEps = 1.0E-307;\n"
+ " double fXEps = 2.22045e-016;\n"
+ " double fAy = arg0-GetGammaInvValue(arg1,arg2,fAx);\n"
+ " double fBy = arg0-GetGammaInvValue(arg1,arg2,fBx);\n"
+ " double fTemp;\n"
+ " unsigned short nCount;\n"
+ " for (nCount = 0; nCount < 1000 && !((fAy < 0.0 && fBy > 0.0)"
+ " || (fAy > 0.0 && fBy < 0.0)); nCount++)\n"
+ " {\n"
+ " if (fabs(fAy) <= fabs(fBy))\n"
+ " {\n"
+ " fTemp = fAx;\n"
+ " fAx += 2.0 * (fAx - fBx);\n"
+ " if (fAx < 0.0)\n"
+ " fAx = 0.0;\n"
+ " fBx = fTemp;\n"
+ " fBy = fAy;\n"
+ " fAy = arg0-GetGammaInvValue(arg1,arg2,fAx);\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " fTemp = fBx;\n"
+ " fBx += 2.0 * (fBx - fAx);\n"
+ " fAx = fTemp;\n"
+ " fAy = fBy;\n"
+ " fBy = arg0-GetGammaInvValue(arg1,arg2,fBx);\n"
+ " }\n"
+ " }\n"
+ " if (fAy == 0.0)\n"
+ " {\n"
+ " tmp = fAx;\n"
+ " return tmp;\n"
+ " }\n"
+ " if (fBy == 0.0)\n"
+ " {\n"
+ " tmp = fBx;\n"
+ " return tmp;\n"
+ " }\n"
+ " if (!((fAy < 0.0 && fBy > 0.0) || (fAy > 0.0 && fBy < 0.0)))\n"
+ " {\n"
+ " bConvError = true;\n"
+ " tmp = 0.0;\n"
+ " return tmp;\n"
+ " }\n"
+ " double fPx = fAx;\n"
+ " double fPy = fAy;\n"
+ " double fQx = fBx;\n"
+ " double fQy = fBy;\n"
+ " double fRx = fAx;\n"
+ " double fRy = fAy;\n"
+ " double fSx = 0.5 * (fAx + fBx);\n"
+ " bool bHasToInterpolate = true;\n"
+ " nCount = 0;\n"
+ " while ( nCount < 500 && fabs(fRy) > fYEps &&"
+ "(fBx-fAx) > fmax( fabs(fAx), fabs(fBx)) * fXEps )\n"
+ " {\n"
+ " if (bHasToInterpolate)\n"
+ " {\n"
+ " if (fPy!=fQy && fQy!=fRy && fRy!=fPy)\n"
+ " {\n"
+ " fSx = fPx * fRy * fQy / (fRy-fPy) / (fQy-fPy)"
+ "+ fRx * fQy * fPy / (fQy-fRy) / (fPy-fRy)"
+ "+ fQx * fPy * fRy / (fPy-fQy) / (fRy-fQy);\n"
+ " bHasToInterpolate = (fAx < fSx) && (fSx < fBx);\n"
+ " }\n"
+ " else\n"
+ " bHasToInterpolate = false;\n"
+ " }\n"
+ " if(!bHasToInterpolate)\n"
+ " {\n"
+ " fSx = 0.5 * (fAx + fBx);\n"
+ " fPx = fAx; fPy = fAy;\n"
+ " fQx = fBx; fQy = fBy;\n"
+ " bHasToInterpolate = true;\n"
+ " }\n"
+ " fPx = fQx; fQx = fRx; fRx = fSx;\n"
+ " fPy = fQy; fQy = fRy;\n"
+ " fRy = arg0-GetGammaInvValue(arg1,arg2,fSx);\n"
+ " if ((fAy < 0.0 && fRy > 0.0) || (fAy > 0.0 && fRy < 0.0))\n"
+ " {\n"
+ " fBx = fRx;\n"
+ " fBy = fRy;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " fAx = fRx;\n"
+ " fAy = fRy;\n"
+ " }\n"
+ " bHasToInterpolate = bHasToInterpolate && (fabs(fRy)"
+ " * 2.0 <= fabs(fQy));\n"
+ " ++nCount;\n"
+ " }\n"
+ " tmp = fRx;\n"
+ " return tmp;\n"
+ " }\n"
+ "}\n";
+}
+void OpFInv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);decls.insert(fMaxGammaArgumentDecl);
+ decls.insert(lcl_getLanczosSumDecl);decls.insert(GetBetaDecl);
+ decls.insert(GetLogBetaDecl);decls.insert(GetBetaDistPDFDecl);
+ decls.insert(lcl_GetBetaHelperContFracDecl);decls.insert(GetFInvValueDecl);
+ funs.insert(lcl_getLanczosSum);funs.insert(GetBeta);
+ funs.insert(GetLogBeta);funs.insert(GetBetaDistPDF);
+ funs.insert(lcl_GetBetaHelperContFrac);funs.insert(GetFInvValue);
+}
+
+void OpFInv::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp;\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ ss << " double fF2=floor(arg2);\n"
+ " double fF1=floor(arg1);\n"
+ " if( arg0 <= 0 || arg1 < 1 || arg2 < 1 || arg1 >= 1.0e10 || arg2 >= 1.0e10 || arg0 > 1 )\n"
+ " return CreateDoubleError(IllegalArgument);\n"
+ " double fAx=fF1*0.5;\n"
+ " double fBx=fF1;\n"
+ " const double fYEps = 1.0E-307;\n"
+ " const double fXEps = 2.22045e-016;\n"
+ " double fAy = arg0-GetFInvValue(fF1,fF2,fAx);\n"
+ " double fBy = arg0-GetFInvValue(fF1,fF2,fBx);\n"
+ " double fTemp;\n"
+ " unsigned short nCount;\n"
+ " for (nCount = 0; nCount < 1000 && !((fAy < 0.0 && fBy > 0.0)"
+ " || (fAy > 0.0 && fBy < 0.0)); nCount++)\n"
+ " {\n"
+ " if (fabs(fAy) <= fabs(fBy))\n"
+ " {\n"
+ " fTemp = fAx;\n"
+ " fAx += 2.0 * (fAx - fBx);\n"
+ " if (fAx < 0.0)\n"
+ " fAx = 0.0;\n"
+ " fBx = fTemp;\n"
+ " fBy = fAy;\n"
+ " fAy = arg0-GetFInvValue(fF1,fF2,fAx);\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " fTemp = fBx;\n"
+ " fBx += 2.0 * (fBx - fAx);\n"
+ " fAx = fTemp;\n"
+ " fAy = fBy;\n"
+ " fBy = arg0-GetFInvValue(fF1,fF2,fBx);\n"
+ " }\n"
+ " }\n"
+ " if (fAy == 0.0)\n"
+ " {\n"
+ " tmp = fAx;\n"
+ " return tmp;\n"
+ " }\n"
+ " if (fBy == 0.0)\n"
+ " {\n"
+ " tmp = fBx;\n"
+ " return tmp;\n"
+ " }\n"
+ " if (!((fAy < 0.0 && fBy > 0.0) || (fAy > 0.0 && fBy < 0.0)))\n"
+ " return CreateDoubleError(NoConvergence);\n"
+ " double fPx = fAx;\n"
+ " double fPy = fAy;\n"
+ " double fQx = fBx;\n"
+ " double fQy = fBy;\n"
+ " double fRx = fAx;\n"
+ " double fRy = fAy;\n"
+ " double fSx = 0.5 * (fAx + fBx);\n"
+ " bool bHasToInterpolate = true;\n"
+ " nCount = 0;\n"
+ " while ( nCount < 500 && fabs(fRy) > fYEps &&"
+ "(fBx-fAx) > fmax( fabs(fAx), fabs(fBx)) * fXEps )\n"
+ " {\n"
+ " if (bHasToInterpolate)\n"
+ " {\n"
+ " if (fPy!=fQy && fQy!=fRy && fRy!=fPy)\n"
+ " {\n"
+ " fSx = fPx * fRy * fQy / (fRy-fPy)"
+ " / (fQy-fPy)+fRx * fQy * fPy / (fQy-fRy)"
+ " / (fPy-fRy)+ fQx * fPy * fRy / (fPy-fQy)"
+ " / (fRy-fQy);\n"
+ " bHasToInterpolate = (fAx < fSx) && (fSx < fBx);\n"
+ " }\n"
+ " else\n"
+ " bHasToInterpolate = false;\n"
+ " }\n"
+ " if(!bHasToInterpolate)\n"
+ " {\n"
+ " fSx = 0.5 * (fAx + fBx);\n"
+ " fPx = fAx; fPy = fAy;\n"
+ " fQx = fBx; fQy = fBy;\n"
+ " bHasToInterpolate = true;\n"
+ " }\n"
+ " fPx = fQx; fQx = fRx; fRx = fSx;\n"
+ " fPy = fQy; fQy = fRy;\n"
+ " fRy = arg0-GetFInvValue(fF1,fF2,fSx);\n"
+ " if ((fAy < 0.0 && fRy > 0.0) || (fAy > 0.0 && fRy < 0.0))\n"
+ " {\n"
+ " fBx = fRx; fBy = fRy;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " fAx = fRx; fAy = fRy;\n"
+ " }\n"
+ " bHasToInterpolate = bHasToInterpolate && (fabs(fRy)"
+ " * 2.0 <= fabs(fQy));\n"
+ " ++nCount;\n"
+ " }\n"
+ " tmp = fRx;\n"
+ " return tmp;"
+ "}";
+}
+void OpFTest::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);decls.insert(fMaxGammaArgumentDecl);
+ decls.insert(lcl_getLanczosSumDecl);decls.insert(GetBetaDecl);
+ decls.insert(GetLogBetaDecl);decls.insert(GetBetaDistPDFDecl);
+ decls.insert(lcl_GetBetaHelperContFracDecl);decls.insert(GetBetaDistDecl);
+ decls.insert(GetFDistDecl);
+ funs.insert(lcl_getLanczosSum);funs.insert(GetBeta);
+ funs.insert(GetLogBeta);funs.insert(GetBetaDistPDF);
+ funs.insert(lcl_GetBetaHelperContFrac);funs.insert(GetBetaDist);
+ funs.insert(GetFDist);
+}
+void OpFTest::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSum1 = 0.0;\n";
+ ss << " double fSumSqr1 = 0.0;\n";
+ ss << " double fSum2 = 0.0;\n";
+ ss << " double fSumSqr2 = 0.0;\n";
+ ss << " double fLength1 = 0.0;\n";
+ ss << " double fLength2 = 0.0;\n";
+ ss << " double tmp = 0;\n";
+ GenerateRangeArg( 0, vSubArguments, ss, SkipEmpty,
+ " fSum1 += arg;\n"
+ " fSumSqr1 += arg * arg;\n"
+ " fLength1 += 1;\n"
+ );
+ GenerateRangeArg( 1, vSubArguments, ss, SkipEmpty,
+ " fSum2 += arg;\n"
+ " fSumSqr2 += arg * arg;\n"
+ " fLength2 += 1;\n"
+ );
+ ss << " if(fLength1 < 2 || fLength2 < 2)\n"
+ " return CreateDoubleError(NoValue);\n"
+ " double fS1 = (fSumSqr1-fSum1*fSum1/fLength1)/(fLength1-1.0);\n"
+ " double fS2 = (fSumSqr2-fSum2*fSum2/fLength2)/(fLength2-1.0);\n"
+ " if(fS1 == 0 || fS2 == 0)\n"
+ " return CreateDoubleError(NoValue);\n"
+ " double fF, fF1, fF2;\n"
+ " if (fS1 > fS2)\n"
+ " {\n"
+ " fF = fS1/fS2;\n"
+ " fF1 = fLength1-1.0;\n"
+ " fF2 = fLength2-1.0;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " fF = fS2/fS1;\n"
+ " fF1 = fLength2-1.0;\n"
+ " fF2 = fLength1-1.0;\n"
+ " }\n"
+ " double fFcdf = GetFDist(fF, fF1, fF2);\n"
+ " return 2.0*min(fFcdf, 1 - fFcdf);\n";
+ ss << "}";
+}
+void OpB::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ //decls.insert(fBigInvDecl);decls.insert(fLogDblMaxDecl);
+ decls.insert(GetBinomDistPMFDecl);decls.insert(MinDecl);
+ decls.insert(fMachEpsDecl);decls.insert(fMaxGammaArgumentDecl);
+ decls.insert(GetBetaDistDecl);decls.insert(GetBetaDistPDFDecl);
+ decls.insert(lcl_GetBetaHelperContFracDecl);decls.insert(GetLogBetaDecl);
+ decls.insert(lcl_getLanczosSumDecl); decls.insert(GetBetaDecl);
+ funs.insert(GetBinomDistPMF);funs.insert(lcl_GetBinomDistRange);
+ funs.insert(GetBetaDist);funs.insert(GetBetaDistPDF);
+ funs.insert(lcl_GetBetaHelperContFrac);funs.insert(GetLogBeta);
+ funs.insert(lcl_getLanczosSum);funs.insert(GetBeta);
+}
+
+void OpB::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 4 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double min = 2.22507e-308;\n";
+ ss << " double tmp;\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArg( 3, vSubArguments, ss );
+ ss << " double rxs = floor(arg2);\n"
+ " double rxe = floor(arg3);\n"
+ " double rn = floor(arg0);\n"
+ " double rq = (0.5 - arg1) + 0.5;\n"
+ " bool bIsValidX = (0.0 <= rxs && rxs <= rxe && rxe <= rn);\n"
+ " if (bIsValidX && 0.0 < arg1 && arg1 < 1.0)\n"
+ " {\n"
+ " if (rxs == rxe)\n"
+ " tmp = GetBinomDistPMF(rxs, rn, arg1);\n"
+ " else\n"
+ " {\n"
+ " double fFactor = pow(rq, rn);\n"
+ " if (fFactor > min)\n"
+ " tmp ="
+ " lcl_GetBinomDistRange(rn, rxs, rxe, fFactor, arg1, rq);\n"
+ " else\n"
+ " {\n"
+ " fFactor = pow(arg1, rn);\n"
+ " if (fFactor > min)\n"
+ " {\n"
+ " tmp ="
+ "lcl_GetBinomDistRange(rn, rn - rxe, rn - rxs, fFactor, rq, arg1);\n"
+ " }\n"
+ " else\n"
+ " tmp ="
+ "GetBetaDist(rq, rn - rxe, rxe + 1.0)"
+ "- GetBetaDist(rq, rn - rxs + 1, rxs);\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " if (bIsValidX)\n"
+ " {\n"
+ " if (arg1 == 0.0)\n"
+ " tmp = (rxs == 0.0 ? 1.0 : 0.0);\n"
+ " else if (arg1 == 1.0)\n"
+ " tmp = (rxe == rn ? 1.0 : 0.0);\n"
+ " else\n"
+ " {\n"
+ " tmp = DBL_MIN;\n"
+ " }\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " tmp = DBL_MIN;\n"
+ " }\n"
+ " }\n"
+ " return tmp;"
+ "}\n";
+}
+void OpBetaDist::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);decls.insert(fMaxGammaArgumentDecl);
+ decls.insert(GetBetaDistDecl);decls.insert(GetBetaDistPDFDecl);
+ decls.insert(lcl_GetBetaHelperContFracDecl);decls.insert(GetLogBetaDecl);
+ decls.insert(GetBetaDecl);decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(GetBetaDist);funs.insert(GetBetaDistPDF);
+ funs.insert(lcl_GetBetaHelperContFrac);funs.insert(GetLogBeta);
+ funs.insert(GetBeta);funs.insert(lcl_getLanczosSum);
+}
+void OpPoisson::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fHalfMachEpsDecl);
+ funs.insert("");
+ decls.insert(fMaxGammaArgumentDecl);
+ funs.insert("");
+ decls.insert(fBigInvDecl);
+ funs.insert("");
+ decls.insert(GetLogGammaDecl);
+ funs.insert(GetLogGamma);
+ decls.insert(lcl_GetLogGammaHelperDecl);
+ funs.insert(lcl_GetLogGammaHelper);
+ decls.insert(lcl_GetGammaHelperDecl);
+ funs.insert(lcl_GetGammaHelper);
+ decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(lcl_getLanczosSum);
+ decls.insert(GetUpRegIGammaDecl);
+ funs.insert(GetUpRegIGamma);
+ decls.insert(GetGammaContFractionDecl);
+ funs.insert(GetGammaContFraction);
+ decls.insert(GetGammaSeriesDecl);
+ funs.insert(GetGammaSeries);
+}
+void OpPoisson::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 3 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " double tmp;\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "lambda", 1, vSubArguments, ss );
+ GenerateArgWithDefault( "bCumulative", 2, 1, vSubArguments, ss );
+ ss << " x = floor(x);\n";
+ ss << " if (lambda <= 0.0 || x < 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " if (!bCumulative)\n";
+ ss << " {\n";
+ ss << " if(lambda == 0.0)\n";
+ ss << " {\n";
+ ss << " return 0;\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if (lambda >712)\n";
+ ss << " {\n";
+ ss << " tmp = (exp(x*log(lambda)-lambda-GetLogGamma(x+1.0)));\n";
+ ss << " return tmp;\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fPoissonVar = 1.0;\n";
+ ss << " for ( int f = 0; f < x; ++f )\n";
+ ss << " fPoissonVar *= lambda / ( (double)f + 1.0 );\n";
+ ss << " tmp = ( fPoissonVar * exp( -lambda ) );\n";
+ ss << " return tmp;\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " } \n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if (lambda == 0.0)\n";
+ ss << " {\n";
+ ss << " return 1;\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if (lambda > 712 )\n";
+ ss << " {\n";
+ ss << " tmp = (GetUpRegIGamma(x+1.0,lambda));\n";
+ ss << " return tmp;\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " if (x >= 936.0)\n";
+ ss << " {\n";
+ ss << " return 1;\n";
+ ss << " }\n";
+ ss << " else\n";
+ ss << " {\n";
+ ss << " double fSummand = exp(-lambda);\n";
+ ss << " double fSum = fSummand;\n";
+ ss << " int nEnd = (int) (x + 0.5);\n";
+ ss << " for (int i = 1; i <= nEnd; i++)\n";
+ ss << " {\n";
+ ss << " fSummand = (fSummand*lambda)/((double)i);\n";
+ ss << " fSum += fSummand;\n";
+ ss << " }\n";
+ ss << " tmp = fSum;\n";
+ ss << " return tmp;\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << " }\n";
+ ss << "}\n";
+}
+
+void OpBetaDist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 6 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ ss << " double tmp;\n";
+ GenerateArg( 0, vSubArguments, ss );
+ GenerateArg( 1, vSubArguments, ss );
+ GenerateArg( 2, vSubArguments, ss );
+ GenerateArgWithDefault( "arg3", 3, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "arg4", 4, 1, vSubArguments, ss );
+ GenerateArgWithDefault( "arg5", 5, 1, vSubArguments, ss );
+ ss << " double fScale = arg4 - arg3;\n"
+ " if (fScale <= 0.0 || arg1 <= 0.0 || arg2 <= 0.0)\n"
+ " return CreateDoubleError(IllegalArgument);\n"
+ " if (arg5)\n"
+ " {\n"
+ " if (arg0< arg3)\n"
+ " {\n"
+ " tmp = 0.0;\n"
+ " return tmp;\n"
+ " }\n"
+ " if (arg0 > arg4)\n"
+ " {\n"
+ " tmp = 1.0;\n"
+ " return tmp;\n"
+ " }\n"
+ " arg0 = (arg0-arg3)/fScale;\n"
+ " tmp = GetBetaDist(arg0, arg1, arg2);\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " if (arg0 < arg3 || arg0 > arg4 )\n"
+ " {\n"
+ " tmp = 0.0;\n"
+ " return tmp;\n"
+ " }\n"
+ " arg0 = (arg0 - arg3)/fScale;\n"
+ " tmp = GetBetaDistPDF(arg0, arg1, arg2)/fScale;\n"
+ " }\n";
+ ss << " return tmp;\n";
+ ss << "}\n";
+}
+void OpBetainv::BinInlineFun(std::set<std::string>& decls,
+ std::set<std::string>& funs)
+{
+ decls.insert(fMachEpsDecl);
+ funs.insert("");
+ decls.insert(fMaxGammaArgumentDecl);
+ funs.insert("");
+ decls.insert(lcl_IterateInverseBetaInvDecl);
+ funs.insert(lcl_IterateInverseBetaInv);
+ decls.insert(GetBetaDistDecl);
+ funs.insert(GetBetaDist);
+ decls.insert(lcl_HasChangeOfSignDecl);
+ funs.insert(lcl_HasChangeOfSign);
+ decls.insert(lcl_HasChangeOfSignDecl);
+ funs.insert(lcl_HasChangeOfSign);
+ decls.insert(lcl_HasChangeOfSignDecl);
+ funs.insert(lcl_HasChangeOfSign);
+ decls.insert(lcl_GetBetaHelperContFracDecl);
+ funs.insert(lcl_GetBetaHelperContFrac);
+ decls.insert(GetBetaDistPDFDecl);
+ funs.insert(GetBetaDistPDF);
+ decls.insert(GetLogBetaDecl);
+ funs.insert(GetLogBeta);
+ decls.insert(GetBetaDecl);
+ funs.insert(GetBeta);
+ decls.insert(lcl_getLanczosSumDecl);
+ funs.insert(lcl_getLanczosSum);
+}
+void OpBetainv::GenSlidingWindowFunction(
+ outputstream &ss,const std::string &sSymName,
+ SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "tmp0", 0, vSubArguments, ss );
+ GenerateArg( "tmp1", 1, vSubArguments, ss );
+ GenerateArg( "tmp2", 2, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp3", 3, 0, vSubArguments, ss );
+ GenerateArgWithDefault( "tmp4", 4, 1, vSubArguments, ss );
+ ss << " if (tmp0 < 0.0 || tmp0 > 1.0 ||";
+ ss << "tmp3 >= tmp4 || tmp1 <= 0.0 || tmp2 <= 0.0)\n";
+ ss << " {\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ ss << " }\n";
+ ss << " bool bConvError;\n";
+ ss << " double fVal = lcl_IterateInverseBetaInv";
+ ss << "(tmp0, tmp1, tmp2, 0.0, 1.0, &bConvError);\n";
+ ss << " if(bConvError)\n";
+ ss << " return CreateDoubleError(NoConvergence);\n";
+ ss << " return (tmp3 + fVal*(tmp4 - tmp3));\n";
+ ss << "}\n";
+}
+void OpDevSq::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double vSum = 0.0;\n";
+ ss << " double vMean = 0.0;\n";
+ ss << " int cnt = 0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " vSum += arg;\n"
+ " ++cnt;\n"
+ );
+ ss << " vMean = vSum / cnt;\n";
+ ss << " vSum = 0.0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " vSum += ( arg - vMean ) * ( arg - vMean );\n"
+ );
+ ss << " return vSum;\n";
+ ss << "}\n";
+}
+
+void OpHypGeomDist::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 4, 5 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0=get_global_id(0);\n";
+ GenerateArg( "x", 0, vSubArguments, ss );
+ GenerateArg( "n", 1, vSubArguments, ss );
+ GenerateArg( "M", 2, vSubArguments, ss );
+ GenerateArg( "N", 3, vSubArguments, ss );
+ GenerateArgWithDefault( "fCumulative", 4, 0, vSubArguments, ss );
+ ss <<
+ " x = floor(x);\n"
+ " n = floor(n);\n"
+ " M = floor(M);\n"
+ " N = floor(N);\n"
+ " double num[9];\n"
+ " double tmp = 0;\n"
+ " if( (x < 0.0) || (n < x) || (N < n) ||"
+ "(N < M) || (M < 0.0) )\n"
+ " {\n"
+ " return CreateDoubleError(IllegalArgument);\n"
+ " }\n"
+ " for(int i = (fCumulative ? 0 : x); i <= x; ++i )\n"
+ " {\n"
+ " if( (M < i) || (i < n - N + M) )\n"
+ " continue;\n"
+ " num[0]=M;\n"
+ " num[1]=i;\n"
+ " num[2]=M-i;\n"
+ " num[3]=N-M;\n"
+ " num[4]=n-i;\n"
+ " num[5]=N-M-n+i;\n"
+ " num[6]=N;\n"
+ " num[7]=n;\n"
+ " num[8]=N-n;\n"
+ " for(int i=0;i<9;i++)\n"
+ " {\n"
+ " if(num[i]<171)\n"
+ " {\n"
+ " if(num[i]==0)\n"
+ " num[i]=0;\n"
+ " else\n"
+ " num[i]=log(tgamma(num[i])*num[i]);\n"
+ " }\n"
+ " else\n"
+ " num[i]=0.5*log(2.0*M_PI)+(num[i]+0.5)*log(num[i])-num[i]+\n"
+ " (1.0/(12.0*num[i])-1.0/(360*pow(num[i],3)));\n"
+ " }\n"
+ " tmp+=pow(M_E,(num[0]+num[3]+num[7]+num[8]-num[1]-num[2]-num[4]-num[5]-num[6]));\n"
+ " }\n"
+ " return tmp;\n";
+ ss << "}\n";
+}
+
+void OpAveDev:: GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double sum=0.0;\n";
+ ss << " double totallength=0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " sum += arg;\n"
+ " ++totallength;\n"
+ );
+ ss << " double mean = sum / totallength;\n";
+ ss << " sum = 0.0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " sum += fabs(arg-mean);\n"
+ );
+ ss << " return sum/totallength;\n";
+ ss << "}";
+}
+
+// Block of functions OpCovar-OpRsq that are rather similar.
+
+void OpCovar::GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSumX = 0.0;\n";
+ ss << " double fSumY = 0.0;\n";
+ ss << " double fMeanX = 0.0;\n";
+ ss << " double fMeanY = 0.0;\n";
+ ss << " double fSumDeltaXDeltaY = 0.0;\n";
+ ss << " double fCount = 0.0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " fSumX += arg1;\n"
+ " fSumY += arg2;\n"
+ " fCount += 1.0;\n"
+ );
+ ss << " if( fCount < 1 )\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " fMeanX = fSumX / fCount;\n";
+ ss << " fMeanY = fSumY / fCount;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " fSumDeltaXDeltaY +=(arg1-fMeanX)*(arg2-fMeanY);\n"
+ );
+ ss << " return fSumDeltaXDeltaY / fCount;\n";
+ ss << "}\n";
+}
+
+void OpForecast::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 3, 3 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 2 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSumX = 0.0;\n";
+ ss << " double fSumY = 0.0;\n";
+ ss << " double fMeanX = 0.0;\n";
+ ss << " double fMeanY = 0.0;\n";
+ ss << " double fSumDeltaXDeltaY = 0.0;\n";
+ ss << " double fSumSqrDeltaX = 0.0;\n";
+ ss << " double fCount = 0.0;\n";
+ GenerateArg( "arg0", 0, vSubArguments, ss );
+ GenerateRangeArgPair( 1, 2, vSubArguments, ss, SkipEmpty,
+ // note that arg1 -> Y, arg2 -> X
+ " fSumX += arg2;\n"
+ " fSumY += arg1;\n"
+ " fCount += 1.0;\n"
+ );
+ ss << " if( fCount < 1 )\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " fMeanX = fSumX / fCount;\n";
+ ss << " fMeanY = fSumY / fCount;\n";
+ GenerateRangeArgPair( 1, 2, vSubArguments, ss, SkipEmpty,
+ " fSumDeltaXDeltaY +=(arg2-fMeanX)*(arg1-fMeanY);\n"
+ " fSumSqrDeltaX += (arg2-fMeanX)*(arg2-fMeanX);\n"
+ );
+ ss << " if(fSumSqrDeltaX == 0.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " return fMeanY + fSumDeltaXDeltaY / fSumSqrDeltaX * (arg0 - fMeanX);\n";
+ ss << "}\n";
+}
+
+void OpInterceptSlopeBase::GenerateCode( outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments, const char* finalComputeCode )
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSumX = 0.0;\n";
+ ss << " double fSumY = 0.0;\n";
+ ss << " double fMeanX = 0.0;\n";
+ ss << " double fMeanY = 0.0;\n";
+ ss << " double fSumDeltaXDeltaY = 0.0;\n";
+ ss << " double fSumSqrDeltaX = 0.0;\n";
+ ss << " double fCount = 0.0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ // note that arg1 -> Y, arg2 -> X
+ " fSumX += arg2;\n"
+ " fSumY += arg1;\n"
+ " fCount += 1.0;\n"
+ );
+ ss << " if( fCount < 1 )\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " fMeanX = fSumX / fCount;\n";
+ ss << " fMeanY = fSumY / fCount;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " fSumDeltaXDeltaY +=(arg2-fMeanX)*(arg1-fMeanY);\n"
+ " fSumSqrDeltaX += (arg2-fMeanX)*(arg2-fMeanX);\n"
+ );
+ ss << finalComputeCode;
+ ss << "}\n";
+}
+
+void OpIntercept::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments,
+ " if(fSumSqrDeltaX == 0.0)\n"
+ " return CreateDoubleError(DivisionByZero);\n"
+ " return fMeanY - (fSumDeltaXDeltaY/fSumSqrDeltaX)*fMeanX;\n"
+ );
+}
+
+void OpSlope::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments,
+ " if(fSumSqrDeltaX == 0.0)\n"
+ " return CreateDoubleError(DivisionByZero);\n"
+ " return fSumDeltaXDeltaY / fSumSqrDeltaX;\n"
+ );
+}
+
+void OpPearsonCovarBase::GenerateCode( outputstream &ss, const std::string &sSymName,
+ SubArguments &vSubArguments, double minimalCountValue, const char* finalComputeCode )
+{
+ CHECK_PARAMETER_COUNT( 2, 2 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 0 );
+ CHECK_PARAMETER_DOUBLEVECTORREF( 1 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSumX = 0.0;\n";
+ ss << " double fSumY = 0.0;\n";
+ ss << " double fMeanX = 0.0;\n";
+ ss << " double fMeanY = 0.0;\n";
+ ss << " double fSumDeltaXDeltaY = 0.0;\n";
+ ss << " double fSumSqrDeltaX = 0.0;\n";
+ ss << " double fSumSqrDeltaY = 0.0;\n";
+ ss << " double fCount = 0.0;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ // note that arg1 -> Y, arg2 -> X
+ " fSumX += arg2;\n"
+ " fSumY += arg1;\n"
+ " fCount += 1.0;\n"
+ );
+ ss << " if( fCount < " << minimalCountValue <<" )\n";
+ ss << " return CreateDoubleError(NoValue);\n";
+ ss << " fMeanX = fSumX / fCount;\n";
+ ss << " fMeanY = fSumY / fCount;\n";
+ GenerateRangeArgPair( 0, 1, vSubArguments, ss, SkipEmpty,
+ " fSumDeltaXDeltaY +=(arg2-fMeanX)*(arg1-fMeanY);\n"
+ " fSumSqrDeltaX += (arg2-fMeanX)*(arg2-fMeanX);\n"
+ " fSumSqrDeltaY += (arg1-fMeanY)*(arg1-fMeanY);\n"
+ );
+ ss << finalComputeCode;
+ ss << "}\n";
+}
+
+void OpPearson::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments, 1,
+ " if (fSumSqrDeltaX == 0 || fSumSqrDeltaY == 0)\n"
+ " return CreateDoubleError(DivisionByZero);\n"
+ " return ( fSumDeltaXDeltaY / sqrt( fSumSqrDeltaX * fSumSqrDeltaY));\n"
+ );
+}
+
+void OpSTEYX::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments, 3,
+ " if(fSumSqrDeltaX == 0.0)\n"
+ " return CreateDoubleError(DivisionByZero);\n"
+ " return sqrt((fSumSqrDeltaY - fSumDeltaXDeltaY * \n"
+ " fSumDeltaXDeltaY / fSumSqrDeltaX)\n"
+ " /(fCount - 2.0));\n"
+ );
+}
+
+void OpRsq::GenSlidingWindowFunction(
+ outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ // This is basically pow(OpPearson,2)
+ GenerateCode( ss, sSymName, vSubArguments, 1,
+ " if (fSumSqrDeltaX == 0 || fSumSqrDeltaY == 0)\n"
+ " return CreateDoubleError(DivisionByZero);\n"
+ " return ( fSumDeltaXDeltaY * fSumDeltaXDeltaY / (fSumSqrDeltaX * fSumSqrDeltaY));\n"
+ );
+}
+
+void OpVarStDevBase::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(is_representable_integerDecl);
+ funs.insert(is_representable_integer);
+ decls.insert(approx_equalDecl);
+ funs.insert(approx_equal);
+ decls.insert(fsub_approxDecl);
+ funs.insert(fsub_approx);
+}
+
+void OpVarStDevBase::GenerateCode(outputstream &ss, const std::string &sSymName, SubArguments &vSubArguments)
+{
+ CHECK_PARAMETER_COUNT( 1, 30 );
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n"; // this must be matched by whoever calls this function
+ ss << " int gid0 = get_global_id(0);\n";
+ ss << " double fSum = 0.0;\n";
+ ss << " double fCount = 0.0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " fSum += arg;\n"
+ " fCount += 1.0;\n"
+ );
+ ss << " if (fCount == 0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " double fMean = fSum / fCount;\n";
+ ss << " double vSum = 0.0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " vSum += pown( fsub_approx(arg, fMean), 2 );\n"
+ );
+}
+
+void OpVar::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if (fCount <= 1.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " else\n";
+ ss << " return vSum / (fCount - 1.0);\n";
+ ss << "}\n";
+}
+
+void OpVarP::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if (fCount == 0.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " else\n";
+ ss << " return vSum / fCount;\n";
+ ss << "}\n";
+}
+
+void OpStDev::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if (fCount <= 1.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " else\n";
+ ss << " return sqrt(vSum / (fCount - 1.0));\n";
+ ss << "}\n";
+}
+
+void OpStDevP::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if (fCount <= 0.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " else\n";
+ ss << " return sqrt(vSum / fCount);\n";
+ ss << "}\n";
+}
+
+void OpSkew::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if(fCount <= 2.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " double fStdDev = sqrt(vSum / (fCount - 1.0));\n";
+ ss << " double dx = 0.0;\n";
+ ss << " double xcube = 0.0;\n";
+ ss << " if(fStdDev == 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " dx = fsub_approx(arg, fMean) / fStdDev;\n"
+ " xcube = xcube + dx * dx * dx;\n"
+ );
+ ss << " return ((xcube * fCount) / (fCount - 1.0)) / (fCount - 2.0);\n";
+ ss << "}\n";
+}
+
+void OpSkewp::GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if(fCount <= 2.0)\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " double fStdDev = sqrt(vSum / fCount);\n";
+ ss << " double dx = 0.0;\n";
+ ss << " double xcube = 0.0;\n";
+ ss << " if(fStdDev == 0.0)\n";
+ ss << " return CreateDoubleError(IllegalArgument);\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " dx = fsub_approx(arg, fMean) / fStdDev;\n"
+ " xcube = xcube + dx * dx * dx;\n"
+ );
+ ss << " return xcube / fCount;\n";
+ ss << "}\n";
+}
+
+void OpKurt:: GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments)
+{
+ GenerateCode( ss, sSymName, vSubArguments );
+ ss << " if( fCount < 4 )\n";
+ ss << " return CreateDoubleError(DivisionByZero);\n";
+ ss << " double fStdDev = sqrt(vSum / (fCount - 1.0));\n";
+ ss << " double dx = 0.0;\n";
+ ss << " double xpower4 = 0.0;\n";
+ GenerateRangeArgs( vSubArguments, ss, SkipEmpty,
+ " dx = (arg -fMean) / fStdDev;\n"
+ " xpower4 = xpower4 + (dx * dx * dx * dx);\n"
+ );
+ ss<< " double k_d = (fCount - 2.0) * (fCount - 3.0);\n";
+ ss<< " double k_l = fCount * (fCount + 1.0) / ((fCount - 1.0) * k_d);\n";
+ ss<< " double k_t = 3.0 * (fCount - 1.0) * (fCount - 1.0) / k_d;\n";
+ ss<< " return xpower4 * k_l - k_t;\n";
+ ss << "}";
+}
+
+void OpMin::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(fmin_countDecl);
+ funs.insert(fmin_count);
+}
+
+void OpMax::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(fmax_countDecl);
+ funs.insert(fmax_count);
+}
+
+void OpAverage::BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs)
+{
+ decls.insert(fsum_countDecl);
+ funs.insert(fsum_count);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_statistical.hxx b/sc/source/core/opencl/op_statistical.hxx
new file mode 100644
index 0000000000..827c6a0456
--- /dev/null
+++ b/sc/source/core/opencl/op_statistical.hxx
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "opbase.hxx"
+#include "utils.hxx"
+
+namespace sc::opencl {
+
+class OpStandard: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Standard"; }
+};
+class OpExponDist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "ExponDist"; }
+};
+class OpZTest: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "ZTest"; }
+};
+class OpWeibull: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Weibull"; }
+};
+class OpFdist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "Fdist"; }
+};
+class OpTDist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "TDist"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+};
+class OpTInv: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "TInv"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+};
+class OpTTest: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "TTest"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+};
+class OpFisher: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Fisher"; }
+};
+
+class OpFisherInv: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "FisherInv"; }
+};
+
+class OpGamma: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Gamma"; }
+};
+
+class OpNegbinomdist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpNegbinomdist"; }
+};
+
+class OpGammaLn: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "GammaLn"; }
+};
+
+class OpGauss: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+ virtual std::string BinFuncName() const override { return "Gauss"; }
+};
+
+class OpGeoMean: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "GeoMean"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpHarMean: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "HarMean"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpNormdist:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpNormdist"; }
+};
+class OpNormsdist:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpNormsdist"; }
+};
+class OpNorminv:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpNorminv"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+};
+class OpNormsinv:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpNormsinv"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+};
+class OpPhi:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpPhi"; }
+};
+
+class OpPermut:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpPermut"; }
+};
+class OpPermutationA:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpPermutationA";}
+};
+
+class OpConfidence: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override { return "Confidence"; }
+};
+class OpLogInv: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "LogInv"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+};
+class OpCritBinom: public Normal
+{
+public:
+ virtual std::string GetBottom() override { return "0"; }
+
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,
+ std::set<std::string>& ) override;
+
+ virtual std::string BinFuncName() const override { return "CritBinom"; }
+};
+class OpLogNormDist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "LogNormdist"; }
+};
+class OpGammaDist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual std::string BinFuncName() const override { return "GammaDist"; }
+};
+class OpHypGeomDist:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpHypGeomDist"; }
+};
+class OpChiDist:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "OpChiDist"; }
+};
+class OpBinomdist:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "OpBinomdist"; }
+};
+class OpChiSqDist: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "ChiSqDist"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+
+class OpChiSqInv: public CheckVariables
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "ChiSqInv"; }
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>& ) override;
+};
+class OpChiInv:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "OpChiInv"; }
+};
+class OpPoisson:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "OpPoisson"; }
+};
+
+class OpGammaInv: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs
+) override;
+ virtual std::string BinFuncName() const override { return "GammaInv"; }
+};
+class OpFInv: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs
+) override;
+ virtual std::string BinFuncName() const override { return "FInv"; }
+};
+class OpFTest: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs
+) override;
+ virtual std::string BinFuncName() const override { return "FTest"; }
+};
+class OpDevSq: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "DevSq"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+class OpB: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs
+) override;
+ virtual std::string BinFuncName() const override { return "B"; }
+};
+class OpBetaDist: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs
+) override;
+ virtual std::string BinFuncName() const override { return "BetaDist"; }
+};
+class OpBetainv:public Normal{
+ public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual void BinInlineFun(std::set<std::string>& ,std::set<std::string>&) override;
+ virtual std::string BinFuncName() const override { return "OpBetainv"; }
+};
+
+class OpAveDev: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "AveDev"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpCovar: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream& ss,
+ const std::string &sSymName, SubArguments& vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Covar"; }
+};
+
+class OpForecast: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Forecast"; }
+};
+
+class OpInterceptSlopeBase: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override = 0;
+protected:
+ void GenerateCode( outputstream& ss, const std::string &sSymName,
+ SubArguments &vSubArguments, const char* finalComputeCode );
+};
+
+class OpIntercept: public OpInterceptSlopeBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Intercept"; }
+};
+
+class OpSlope: public OpInterceptSlopeBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Slope"; }
+};
+
+class OpPearsonCovarBase: public Normal
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override = 0;
+protected:
+ void GenerateCode( outputstream& ss, const std::string &sSymName,
+ SubArguments &vSubArguments, double minimalCountValue, const char* finalComputeCode );
+};
+
+class OpPearson: public OpPearsonCovarBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpPearson"; }
+};
+
+class OpCorrel: public OpPearson // they are identical
+{
+public:
+ virtual std::string BinFuncName() const override { return "Correl"; }
+};
+
+class OpSTEYX: public OpPearsonCovarBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "STEYX"; }
+};
+
+class OpRsq: public OpPearsonCovarBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "OpRsq"; }
+};
+
+class OpVarStDevBase : public Normal
+{
+public:
+ virtual void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual bool canHandleMultiVector() const override { return true; }
+protected:
+ // Generates function setup and checks, then generates a loop that will calculate
+ // fMean and fCount from all arguments (ranges) and then a second loop that will
+ // calculate vSum (pown(fsub_approx(arg,fMean),2)) from all arguments.
+ void GenerateCode( outputstream& ss, const std::string& sSymName, SubArguments &vSubArguments );
+};
+
+class OpVar: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Var"; }
+};
+
+class OpVarP: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "VarP"; }
+};
+
+class OpStDev: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "StDev"; }
+};
+
+class OpStDevP: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "StDevP"; }
+};
+
+class OpSkew: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Skew"; }
+};
+
+class OpSkewp: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Skewp"; }
+};
+
+class OpKurt: public OpVarStDevBase
+{
+public:
+ virtual void GenSlidingWindowFunction(outputstream &ss,
+ const std::string &sSymName, SubArguments &vSubArguments) override;
+ virtual std::string BinFuncName() const override { return "Kurt"; }
+};
+
+class OpMin : public Reduction
+{
+public:
+ explicit OpMin(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "NAN"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ return "fmin_count(" + lhs + "," + rhs + ", &nCount)";
+ }
+ virtual void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual std::string BinFuncName() const override { return "min"; }
+ virtual bool isMinOrMax() const override { return true; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpMax : public Reduction
+{
+public:
+ explicit OpMax(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "NAN"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ return "fmax_count(" + lhs + "," + rhs + ", &nCount)";
+ }
+ virtual void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual std::string BinFuncName() const override { return "max"; }
+ virtual bool isMinOrMax() const override { return true; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpAverage : public Reduction
+{
+public:
+ explicit OpAverage(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ outputstream ss;
+ ss << "fsum_count(" << lhs << "," << rhs << ", &nCount)";
+ return ss.str();
+ }
+ virtual void BinInlineFun(std::set<std::string>& decls,std::set<std::string>& funs) override;
+ virtual std::string BinFuncName() const override { return "average"; }
+ virtual bool isAverage() const override { return true; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpCount : public Reduction
+{
+public:
+ explicit OpCount(int nResultSize) : Reduction(nResultSize) {}
+
+ virtual std::string GetBottom() override { return "0"; }
+ virtual std::string Gen2( const std::string& lhs, const std::string& rhs ) const override
+ {
+ outputstream ss;
+ ss << "(isnan(" << lhs << ")?" << rhs << ":" << rhs << "+1.0)";
+ return ss.str();
+ }
+ virtual std::string BinFuncName() const override { return "fcount"; }
+ virtual bool canHandleMultiVector() const override { return true; }
+};
+
+class OpCountA: public OpCount
+{
+public:
+ explicit OpCountA(int nResultSize) : OpCount(nResultSize) {}
+ virtual std::string BinFuncName() const override { return "OpCountA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpMaxA: public OpMax
+{
+public:
+ explicit OpMaxA(int nResultSize) : OpMax(nResultSize) {}
+ virtual std::string BinFuncName() const override { return "OpMaxA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpMinA : public OpMin
+{
+public:
+ explicit OpMinA(int nResultSize) : OpMin(nResultSize) {}
+ virtual std::string BinFuncName() const override { return "OpMinA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpVarA: public OpVar
+{
+public:
+ virtual std::string BinFuncName() const override { return "OpVarA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpVarPA: public OpVarP
+{
+public:
+ virtual std::string BinFuncName() const override { return "OpVarPA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpStDevPA: public OpStDevP
+{
+public:
+ virtual std::string BinFuncName() const override { return "OpStDevPA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpAverageA: public OpAverage
+{
+public:
+ explicit OpAverageA(int nResultSize) : OpAverage(nResultSize) {}
+ virtual std::string BinFuncName() const override { return "OpAverageA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+class OpStDevA: public OpStDev
+{
+public:
+ virtual std::string BinFuncName() const override { return "OpStDevA"; }
+ virtual bool forceStringsToZero() const override { return true; }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/op_statistical_helpers.hxx b/sc/source/core/opencl/op_statistical_helpers.hxx
new file mode 100644
index 0000000000..54914e1127
--- /dev/null
+++ b/sc/source/core/opencl/op_statistical_helpers.hxx
@@ -0,0 +1,1383 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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
+
+const char MinDecl[] = "#define Min 2.22507e-308\n";
+const char fBigInvDecl[] ="#define fBigInv 2.22045e-016\n";
+const char fMachEpsDecl[] ="#define fMachEps 2.22045e-016\n";
+const char fLogDblMaxDecl[] ="#define fLogDblMax log(1.79769e+308)\n";
+const char fHalfMachEpsDecl[] ="#define fHalfMachEps 0.5*2.22045e-016\n";
+const char fMaxGammaArgumentDecl[] =
+"#define fMaxGammaArgument 171.624376956302\n";
+const char GetValueDecl[] =
+"double GetValue( double x,double fp,double fDF );\n";
+const char GetValue[] =
+"double GetValue( double x,double fp,double fDF )\n"
+"{\n"
+" return fp - 2 * GetTDist(x, fDF);\n"
+"}\n";
+const char fmin_countDecl[] = "double fmin_count(double a, double b, __private int *p);\n";
+const char fmin_count[] =
+"double fmin_count(double a, double b, __private int *p) {\n"
+" double result = fmin(a, b);\n"
+" bool t = isnan(result);\n"
+" (*p) += t?0:1;\n"
+" return result;\n"
+"}\n";
+const char fmax_countDecl[] = "double fmax_count(double a, double b, __private int *p);\n";
+const char fmax_count[] =
+"double fmax_count(double a, double b, __private int *p) {\n"
+" double result = fmax(a, b);\n"
+" bool t = isnan(result);\n"
+" (*p) += t?0:1;\n"
+" return result;\n"
+"}\n";
+const char fsum_countDecl[] = "double fsum_count(double a, double b, __private int *p);\n";
+const char fsum_count[] =
+"double fsum_count(double a, double b, __private int *p) {\n"
+" bool t = isnan(a);\n"
+" (*p) += t?0:1;\n"
+" return t?b:a+b;\n"
+"}\n";
+const char GetGammaSeriesDecl[] =
+"double GetGammaSeries( double fA, double fX );\n";
+const char GetGammaSeries[] =
+"double GetGammaSeries( double fA, double fX )\n"
+"{\n"
+" double fDenomfactor = fA;\n"
+" double fSummand = 1.0/fA;\n"
+" double fSum = fSummand;\n"
+" int nCount=1;\n"
+" do\n"
+" {\n"
+" fDenomfactor = fDenomfactor + 1.0;\n"
+" fSummand = fSummand * fX/fDenomfactor;\n"
+" fSum = fSum + fSummand;\n"
+" nCount = nCount+1;\n"
+" } while ( fSummand/fSum > fHalfMachEps && nCount<=10000);\n"
+" if (nCount>10000)\n"
+" {\n"
+" }\n"
+" return fSum;\n"
+"}\n";
+const char GetGammaContFractionDecl[] = "double GetGammaContFraction( double "
+"fA, double fX );\n";
+const char GetGammaContFraction[] =
+"double GetGammaContFraction( double fA, double fX )\n"
+"{\n"
+" double fBig = 1.0/fBigInv;\n"
+" double fCount = 0.0;\n"
+" double fNum = 0.0;\n"
+" double fY = 1.0 - fA;\n"
+" double fDenom = fX + 2.0-fA;\n"
+" double fPk = 0.0;\n"
+" double fPkm1 = fX + 1.0;\n"
+" double fPkm2 = 1.0;\n"
+" double fQk = 1.0;\n"
+" double fQkm1 = fDenom * fX;\n"
+" double fQkm2 = fX;\n"
+" double fApprox = fPkm1/fQkm1;\n"
+" bool bFinished = false;\n"
+" double fR = 0.0;\n"
+" do\n"
+" {\n"
+" fCount = fCount +1.0;\n"
+" fY = fY+ 1.0;\n"
+" fNum = fY * fCount;\n"
+" fDenom = fDenom +2.0;\n"
+" fPk = fPkm1 * fDenom - fPkm2 * fNum;\n"
+" fQk = fQkm1 * fDenom - fQkm2 * fNum;\n"
+" if (fQk != 0.0)\n"
+" {\n"
+" fR = fPk/fQk;\n"
+" bFinished = (fabs( (fApprox - fR)/fR ) <= fHalfMachEps);\n"
+" fApprox = fR;\n"
+" }\n"
+" fPkm2 = fPkm1;\n"
+" fPkm1 = fPk;\n"
+" fQkm2 = fQkm1;\n"
+" fQkm1 = fQk;\n"
+" if (fabs(fPk) > fBig)\n"
+" {\n"
+" fPkm2 = fPkm2 * fBigInv;\n"
+" fPkm1 = fPkm1 * fBigInv;\n"
+" fQkm2 = fQkm2 * fBigInv;\n"
+" fQkm1 = fQkm1 * fBigInv;\n"
+" }\n"
+" } while (!bFinished && fCount<10000);\n"
+" if (!bFinished)\n"
+" {\n"
+" }\n"
+" return fApprox;\n"
+"}\n";
+const char GetLowRegIGammaDecl[] = "double GetLowRegIGamma( double "
+"fA, double fX );\n";
+const char GetLowRegIGamma[] =
+"double GetLowRegIGamma( double fA, double fX )\n"
+"{\n"
+" double fLnFactor = fA * log(fX) - fX - lgamma(fA);\n"
+" double fFactor = exp(fLnFactor);\n"
+" if (fX>fA+1.0) \n"
+" return 1.0 - fFactor * GetGammaContFraction(fA,fX);\n"
+" else\n"
+" return fFactor * GetGammaSeries(fA,fX);\n"
+"}\n";
+const char GetGammaDistDecl[] = "double GetGammaDist( double fX, double "
+"fAlpha, double fLambda );\n";
+const char GetGammaDist[] =
+"double GetGammaDist( double fX, double fAlpha, double fLambda )\n"
+"{\n"
+" if (fX <= 0.0)\n"
+" return 0.0;\n"
+" else\n"
+" return GetLowRegIGamma( fAlpha, fX / fLambda);\n"
+"}\n";
+const char GetGammaDistPDFDecl[] = "double GetGammaDistPDF( double fX"
+", double fAlpha, double fLambda );\n";
+const char GetGammaDistPDF[] =
+"double GetGammaDistPDF( double fX, double fAlpha, double fLambda )\n"
+"{\n"
+" if (fX < 0.0)\n"
+" return 0.0;\n"
+" else if (fX == 0)\n"
+" {\n"
+" if (fAlpha < 1.0)\n"
+" {\n"
+" return HUGE_VAL;\n"
+" }\n"
+" else if (fAlpha == 1)\n"
+" {\n"
+" return (1.0 / fLambda);\n"
+" }\n"
+" else\n"
+" {\n"
+" return 0.0;\n"
+" }\n"
+" }\n"
+" else\n"
+" {\n"
+" double fXr = fX / fLambda;\n"
+" if (fXr > 1.0)\n"
+" {\n"
+" if (log(fXr) * (fAlpha-1.0) < fLogDblMax &&"
+"fAlpha < fMaxGammaArgument)\n"
+" {\n"
+" return pow( fXr, fAlpha-1.0) * exp(-fXr) / "
+"fLambda / tgamma(fAlpha);\n"
+" }\n"
+" else\n"
+" {\n"
+" return exp( (fAlpha-1.0) * log(fXr) - fXr - "
+"log(fLambda) - lgamma(fAlpha));\n"
+" }\n"
+" }\n"
+" else\n"
+" {\n"
+" if (fAlpha<fMaxGammaArgument)\n"
+" {\n"
+" return pow( fXr, fAlpha-1.0) * exp(-fXr) / "
+"fLambda / tgamma(fAlpha);\n"
+" }\n"
+" else\n"
+" {\n"
+" return pow( fXr, fAlpha-1.0) * exp(-fXr) / "
+"fLambda / exp( lgamma(fAlpha));\n"
+" }\n"
+" }\n"
+" }\n"
+"}\n";
+const char GetBetaDistDecl[] =
+"double GetBetaDist(double fXin, double fAlpha, double fBeta);\n";
+const char GetBetaDist[] =
+"double GetBetaDist(double fXin, double fAlpha, double fBeta)\n"
+"{\n"
+" if (fXin <= 0.0)\n"
+" return 0.0;\n"
+" if (fXin >= 1.0)\n"
+" return 1.0;\n"
+" if (fBeta == 1.0)\n"
+" return pow(fXin, fAlpha);\n"
+" if (fAlpha == 1.0)\n"
+" return -expm1(fBeta * log1p(-fXin));\n"
+" double fResult;\n"
+" double fY = (0.5-fXin)+0.5;\n"
+" double flnY = log1p(-fXin);\n"
+" double fX = fXin;\n"
+" double flnX = log(fXin);\n"
+" double fA = fAlpha;\n"
+" double fB = fBeta;\n"
+" bool bReflect = fXin > fAlpha/(fAlpha+fBeta);\n"
+" if (bReflect)\n"
+" {\n"
+" fA = fBeta;\n"
+" fB = fAlpha;\n"
+" fX = fY;\n"
+" fY = fXin;\n"
+" flnX = flnY;\n"
+" flnY = log(fXin);\n"
+" }\n"
+" fResult = lcl_GetBetaHelperContFrac(fX,fA,fB)/fA;\n"
+" double fP = fA/(fA+fB);\n"
+" double fQ = fB/(fA+fB);\n"
+" if (fA > 1.0 && fB > 1.0 && fP < 0.97 && fQ < 0.97)\n"
+" fResult *= GetBetaDistPDF(fX,fA,fB)*fX*fY;\n"
+" else\n"
+" fResult *= pow(exp(1.0),(fA*flnX + fB*flnY - GetLogBeta(fA,fB)));\n"
+" if (bReflect)\n"
+" fResult = 0.5 - fResult + 0.5;\n"
+" if (fResult > 1.0)\n"
+" fResult = 1.0;\n"
+" if (fResult < 0.0)\n"
+" fResult = 0.0;\n"
+" return fResult;\n"
+"}\n";
+
+const char GetFDistDecl[] =
+ "double GetFDist(double x, double fF1, double fF2);\n";
+const char GetFDist[] =
+"double GetFDist(double x, double fF1, double fF2)\n"
+"{\n"
+" double arg = fF2/(fF2+fF1*x);\n"
+" double alpha = fF2/2.0;\n"
+" double beta = fF1/2.0;\n"
+" return (GetBetaDist(arg, alpha, beta));\n"
+"}\n";
+const char GetGammaInvValueDecl[] = "double"
+" GetGammaInvValue(double fAlpha,double fBeta,double fX1 );\n";
+const char GetGammaInvValue[] =
+"double GetGammaInvValue(double fAlpha,double fBeta,double fX1 )\n"
+"{\n"
+" if (fX1 <= 0.0)\n"
+" return 0.0;\n"
+" else\n"
+" {\n"
+" double fX=fX1/fBeta;\n"
+" double fLnFactor = fAlpha * log(fX) - fX - lgamma(fAlpha);\n"
+" double fFactor = exp(fLnFactor);\n"
+" if (fX>fAlpha+1.0)\n"
+" return 1.0 - fFactor * GetGammaContFraction(fAlpha,fX);\n"
+" else\n"
+" return fFactor * GetGammaSeries(fAlpha,fX);\n"
+" }\n"
+"}\n";
+const char GetFInvValueDecl[] = "double GetFInvValue(double fF1,double fF2"
+",double fX1 );";
+const char GetFInvValue[] =
+"double GetFInvValue(double fF1,double fF2,double fX1 )\n"
+"{\n"
+" double arg = fF2/(fF2+fF1*fX1);\n"
+" double alpha = fF2/2.0;\n"
+" double beta = fF1/2.0;\n"
+" double fXin,fAlpha,fBeta;\n"
+" fXin=arg;fAlpha=alpha;fBeta=beta;\n"
+" if (fXin <= 0.0)\n"
+" return 0.0;\n"
+" if (fXin >= 1.0)\n"
+" return 1.0;\n"
+" if (fBeta == 1.0)\n"
+" return pow(fXin, fAlpha);\n"
+" if (fAlpha == 1.0)\n"
+" return -expm1(fBeta * log1p(-fXin));\n"
+" double fResult;\n"
+" double fY = (0.5-fXin)+0.5;\n"
+" double flnY = log1p(-fXin);\n"
+" double fX = fXin;\n"
+" double flnX = log(fXin);\n"
+" double fA = fAlpha;\n"
+" double fB = fBeta;\n"
+" bool bReflect = fXin > fAlpha/(fAlpha+fBeta);\n"
+" if (bReflect)\n"
+" {\n"
+" fA = fBeta;\n"
+" fB = fAlpha;\n"
+" fX = fY;\n"
+" fY = fXin;\n"
+" flnX = flnY;\n"
+" flnY = log(fXin);\n"
+" }\n"
+" fResult = lcl_GetBetaHelperContFrac(fX,fA,fB);\n"
+" fResult = fResult/fA;\n"
+" double fP = fA/(fA+fB);\n"
+" double fQ = fB/(fA+fB);\n"
+" double fTemp;\n"
+" if (fA > 1.0 && fB > 1.0 && fP < 0.97 && fQ < 0.97)\n"
+" fTemp = GetBetaDistPDF(fX,fA,fB)*fX*fY;\n"
+" else\n"
+" fTemp = exp(fA*flnX + fB*flnY - GetLogBeta(fA,fB));\n"
+" fResult *= fTemp;\n"
+" if (bReflect)\n"
+" fResult = 0.5 - fResult + 0.5;\n"
+" if (fResult > 1.0)\n"
+" fResult = 1.0;\n"
+" if (fResult < 0.0)\n"
+" fResult = 0.0;\n"
+" return fResult;\n"
+"}\n";
+const char GetBinomDistPMFDecl[] =
+ "double GetBinomDistPMF(double x, double n, double p);";
+const char GetBinomDistPMF[] =
+"double GetBinomDistPMF(double x, double n, double p)\n"
+"{\n"
+" double q = (0.5 - p) + 0.5;\n"
+" double fFactor = pow(q, n);\n"
+" if (fFactor <= Min)\n"
+" {\n"
+" fFactor = pow(p, n);\n"
+" if (fFactor <= Min)\n"
+" return GetBetaDistPDF(p, x + 1.0, n - x + 1.0)/(n + 1.0);\n"
+" else\n"
+" {\n"
+" uint max = (uint)(n - x);\n"
+" for (uint i = 0; i < max && fFactor > 0.0; ++i)\n"
+" fFactor *= (n - i)/(double)(i + 1)*q/p;\n"
+" return fFactor;\n"
+" }\n"
+" }\n"
+" else\n"
+" {\n"
+" uint max = (uint)x;\n"
+" for (uint i = 0; i < max && fFactor > 0.0; ++i)\n"
+" fFactor *= (n - i)/(double)(i + 1)*p/q;\n"
+" return fFactor;\n"
+" }\n"
+"}\n";
+
+const char lcl_GetBinomDistRangeDecl[] =
+ "double lcl_GetBinomDistRange(double n, \n"
+"double xs, double xe, double fFactor, double p, double q);";
+const char lcl_GetBinomDistRange[] =
+"double lcl_GetBinomDistRange(double n, double xs, double xe,\n"
+" double fFactor, double p, double q)\n"
+"{\n"
+" uint i;\n"
+" double fSum;\n"
+" uint nXs = (uint)xs;\n"
+" for (i = 1; i <= nXs && fFactor > 0.0; ++i)\n"
+" fFactor *= (n - i + 1)/(double)(i)*p/q;\n"
+" fSum = fFactor;\n"
+" uint nXe =(uint)xe;\n"
+" for (i = nXs + 1; i <= nXe && fFactor > 0.0; ++i)\n"
+" {\n"
+" fFactor *= (n - i + 1)/(double)(i)*p/q;\n"
+" fSum += fFactor;\n"
+" }\n"
+" return (fSum > 1.0) ? 1.0 : fSum;\n"
+"}\n";
+
+const char GetLogGammaDecl[] = "double GetLogGamma(double fZ);\n";
+const char GetLogGamma[] =
+"double GetLogGamma(double fZ)\n"
+"{\n"
+" if (fZ >= fMaxGammaArgument)\n"
+" return lcl_GetLogGammaHelper(fZ);\n"
+" if (fZ >= 1.0)\n"
+" return log(lcl_GetGammaHelper(fZ));\n"
+" if (fZ >= 0.5)\n"
+" return log( lcl_GetGammaHelper(fZ+1) / fZ);\n"
+" return lcl_GetLogGammaHelper(fZ+2) - log(fZ+1) - log(fZ);\n"
+"}\n";
+
+const char GetChiDistDecl[] = "double GetChiDist(double fX, double fDF);\n";
+const char GetChiDist[] =
+"double GetChiDist(double fX, double fDF)\n"
+"{\n"
+" if (fX <= 0.0)\n"
+" return 1.0;\n"
+" else\n"
+" return GetUpRegIGamma( fDF/2.0, fX/2.0);\n"
+"}\n";
+
+const char GetChiSqDistCDFDecl[] =
+"double GetChiSqDistCDF(double fX, double fDF);\n";
+const char GetChiSqDistCDF[] =
+"double GetChiSqDistCDF(double fX, double fDF)\n"
+"{\n"
+" if (fX <= 0.0)\n"
+" return 0.0;"
+" else\n"
+" return GetLowRegIGamma( fDF/2.0, fX/2.0);\n"
+"}\n";
+
+const char GetChiSqDistPDFDecl[] =
+"double GetChiSqDistPDF(double fX, double fDF);\n";
+const char GetChiSqDistPDF[] =
+"double GetChiSqDistPDF(double fX, double fDF)\n"
+"{\n"
+" double fValue;\n"
+" if (fX <= 0.0)\n"
+" return 0.0;\n"
+" if (fDF*fX > 1391000.0)\n"
+" {\n"
+" fValue = exp((0.5*fDF - 1) * log(fX*0.5) - 0.5 * fX - log(2.0)"
+" - lgamma(0.5*fDF));\n"
+" }\n"
+" else\n"
+" {\n"
+" double fCount;\n"
+" if (fmod(fDF,2.0)<0.5)\n"
+" {\n"
+" fValue = 0.5;\n"
+" fCount = 2.0;\n"
+" }\n"
+" else\n"
+" {\n"
+" fValue = 1.0/sqrt(fX*2*M_PI);\n"
+" fCount = 1.0;\n"
+" }\n"
+" while ( fCount < fDF)\n"
+" {\n"
+" fValue *= (fX / fCount);\n"
+" fCount += 2.0;\n"
+" }\n"
+" if (fX>=1425.0)\n"
+" fValue = exp(log(fValue)-fX/2);\n"
+" else\n"
+" fValue *= exp(-fX/2);\n"
+" }\n"
+" return fValue;\n"
+"}\n";
+
+const char lcl_IterateInverseBetaInvDecl[] =
+"static double lcl_IterateInverseBetaInv(double fp, double fAlpha, \n"
+" double fBeta, double fAx, double fBx, bool *rConvError );\n";
+const char lcl_IterateInverseBetaInv[] =
+"static double lcl_IterateInverseBetaInv(double fp, double fAlpha, \n"
+" double fBeta, double fAx, double fBx, bool *rConvError )\n"
+"{\n"
+" *rConvError = false;\n"
+" double fYEps = 1.0E-307;\n"
+" double fXEps = fMachEps;\n"
+" if(!(fAx < fBx))\n"
+" {\n"
+" //print error\n"
+" }\n"
+" double fAy = fp - GetBetaDist(fAx, fAlpha, fBeta);\n"
+" double fBy = fp - GetBetaDist(fBx, fAlpha, fBeta);\n"
+" double fTemp;\n"
+" unsigned short nCount;\n"
+" for (nCount = 0; nCount < 1000 && !lcl_HasChangeOfSign(fAy,fBy);"
+" nCount++)\n"
+" {\n"
+" if (fabs(fAy) <= fabs(fBy))\n"
+" {\n"
+" fTemp = fAx;\n"
+" fAx += 2.0 * (fAx - fBx);\n"
+" if (fAx < 0.0)\n"
+" fAx = 0.0;\n"
+" fBx = fTemp;\n"
+" fBy = fAy;\n"
+" fAy = fp - GetBetaDist(fAx, fAlpha, fBeta);\n"
+" }\n"
+" else\n"
+" {\n"
+" fTemp = fBx;\n"
+" fBx += 2.0 * (fBx - fAx);\n"
+" fAx = fTemp;\n"
+" fAy = fBy;\n"
+" fBy = fp - GetBetaDist(fBx, fAlpha, fBeta);\n"
+" }\n"
+" }\n"
+" if (fAy == 0.0)\n"
+" return fAx;\n"
+" if (fBy == 0.0)\n"
+" return fBx;\n"
+" if (!lcl_HasChangeOfSign( fAy, fBy))\n"
+" {\n"
+" *rConvError = true;\n"
+" return 0.0;\n"
+" }\n"
+" double fPx = fAx;\n"
+" double fPy = fAy;\n"
+" double fQx = fBx;\n"
+" double fQy = fBy;\n"
+" double fRx = fAx;\n"
+" double fRy = fAy;\n"
+" double fSx = 0.5 * (fAx + fBx);\n"
+" bool bHasToInterpolate = true;\n"
+" nCount = 0;\n"
+" while ( nCount < 500 && fabs(fRy) > fYEps &&\n"
+" (fBx-fAx) > fmax( fabs(fAx), fabs(fBx)) * fXEps )\n"
+" {\n"
+" if (bHasToInterpolate)\n"
+" {\n"
+" if (fPy!=fQy && fQy!=fRy && fRy!=fPy)\n"
+" {\n"
+" fSx = fPx*fRy*fQy/(fRy-fPy)/(fQy-fPy)\n"
+" + fRx*fQy*fPy/(fQy-fRy)/(fPy-fRy)\n"
+" + fQx*fPy*fRy/(fPy-fQy)/(fRy-fQy);\n"
+" bHasToInterpolate = (fAx < fSx) && (fSx < fBx);\n"
+" }\n"
+" else\n"
+" bHasToInterpolate = false;\n"
+" }\n"
+" if(!bHasToInterpolate)\n"
+" {\n"
+" fSx = 0.5 * (fAx + fBx);\n"
+" fPx = fAx; fPy = fAy;\n"
+" fQx = fBx; fQy = fBy;\n"
+" bHasToInterpolate = true;\n"
+" }\n"
+" fPx = fQx; fQx = fRx; fRx = fSx;\n"
+" fPy = fQy; fQy = fRy; fRy = fp - GetBetaDist(fSx, fAlpha, fBeta);\n"
+" if (lcl_HasChangeOfSign( fAy, fRy))\n"
+" {\n"
+" fBx = fRx; fBy = fRy;\n"
+" }\n"
+" else\n"
+" {\n"
+" fAx = fRx; fAy = fRy;\n"
+" }\n"
+" bHasToInterpolate = bHasToInterpolate && (fabs(fRy) *"
+" 2.0 <= fabs(fQy));\n"
+" ++nCount;\n"
+" }\n"
+" return fRx;\n"
+"}\n";
+
+const char lcl_IterateInverseChiInvDecl[] =
+"static double lcl_IterateInverseChiInv"
+"(double fp, double fdf, double fAx, double fBx, bool *rConvError);\n";
+const char lcl_IterateInverseChiInv[] =
+"static double lcl_IterateInverseChiInv"
+"(double fp, double fdf, double fAx, double fBx, bool *rConvError)\n"
+"{\n"
+" *rConvError = false;\n"
+" double fYEps = 1.0E-307;\n"
+" double fXEps = fMachEps;\n"
+" if(!(fAx < fBx))\n"
+" {\n"
+" //print error\n"
+" }"
+" double fAy = fp - GetChiDist(fAx, fdf);\n"
+" double fBy = fp - GetChiDist(fBx, fdf);\n"
+" double fTemp;\n"
+" unsigned short nCount;\n"
+" for (nCount = 0; nCount < 1000 && "
+"!lcl_HasChangeOfSign(fAy,fBy); nCount++)\n"
+" {\n"
+" if (fabs(fAy) <= fabs(fBy))\n"
+" {\n"
+" fTemp = fAx;\n"
+" fAx += 2.0 * (fAx - fBx);\n"
+" if (fAx < 0.0)\n"
+" fAx = 0.0;\n"
+" fBx = fTemp;\n"
+" fBy = fAy;\n"
+" fAy = fp - GetChiDist(fAx, fdf);\n"
+" }\n"
+" else\n"
+" {\n"
+" fTemp = fBx;\n"
+" fBx += 2.0 * (fBx - fAx);\n"
+" fAx = fTemp;\n"
+" fAy = fBy;\n"
+" fBy = fp - GetChiDist(fBx, fdf);\n"
+" }\n"
+" }\n"
+" if (fAy == 0.0)\n"
+" return fAx;\n"
+" if (fBy == 0.0)\n"
+" return fBx;\n"
+" if (!lcl_HasChangeOfSign( fAy, fBy))\n"
+" {\n"
+" *rConvError = true;\n"
+" return 0.0;\n"
+" }\n"
+" double fPx = fAx;\n"
+" double fPy = fAy;\n"
+" double fQx = fBx;\n"
+" double fQy = fBy;\n"
+" double fRx = fAx;\n"
+" double fRy = fAy;\n"
+" double fSx = 0.5 * (fAx + fBx);\n"
+" bool bHasToInterpolate = true;\n"
+" nCount = 0;\n"
+" while ( nCount < 500 && fabs(fRy) > fYEps &&\n"
+" (fBx-fAx) > fmax( fabs(fAx), fabs(fBx)) * fXEps )\n"
+" {\n"
+" if (bHasToInterpolate)\n"
+" {\n"
+" if (fPy!=fQy && fQy!=fRy && fRy!=fPy)\n"
+" {\n"
+" fSx = fPx * fRy * fQy/(fRy-fPy)/(fQy-fPy)\n"
+" + fRx * fQy * fPy/(fQy-fRy)/(fPy-fRy)\n"
+" + fQx * fPy * fRy/(fPy-fQy)/(fRy-fQy);\n"
+" bHasToInterpolate = (fAx < fSx) && (fSx < fBx);\n"
+" }\n"
+" else\n"
+" bHasToInterpolate = false;\n"
+" }\n"
+" if(!bHasToInterpolate)\n"
+" {\n"
+" fSx = 0.5 * (fAx + fBx);\n"
+" fPx = fAx; fPy = fAy;\n"
+" fQx = fBx; fQy = fBy;\n"
+" bHasToInterpolate = true;\n"
+" }\n"
+" fPx = fQx; fQx = fRx; fRx = fSx;\n"
+" fPy = fQy; fQy = fRy; fRy = fp - GetChiDist(fSx, fdf);\n"
+" if (lcl_HasChangeOfSign( fAy, fRy))\n"
+" {\n"
+" fBx = fRx; fBy = fRy;\n"
+" }\n"
+" else\n"
+" {\n"
+" fAx = fRx; fAy = fRy;\n"
+" }\n"
+" bHasToInterpolate = bHasToInterpolate && (fabs(fRy)"
+" * 2.0 <= fabs(fQy));\n"
+" ++nCount;\n"
+" }\n"
+" return fRx;\n"
+"}\n";
+
+const char lcl_IterateInverseChiSQInvDecl[] =
+"static double lcl_IterateInverseChiSQInv( double fp, double fdf, \n"
+" double fAx, double fBx, bool *rConvError );\n";
+const char lcl_IterateInverseChiSQInv[] =
+"static double lcl_IterateInverseChiSQInv( double fp, double fdf, \n"
+" double fAx, double fBx, bool *rConvError )\n"
+"{\n"
+" *rConvError = false;\n"
+" double fYEps = 1.0E-307;\n"
+" double fXEps = fMachEps;\n"
+
+" if(!(fAx < fBx))\n"
+" {\n"
+" //print error\n"
+" }\n"
+" double fAy = fp - GetChiSqDistCDF(fAx, fdf);\n"
+" double fBy = fp - GetChiSqDistCDF(fBx, fdf);\n"
+" double fTemp;\n"
+" unsigned short nCount;\n"
+" for (nCount = 0; nCount < 1000 && !lcl_HasChangeOfSign(fAy,fBy);"
+" nCount++)\n"
+" {\n"
+" if (fabs(fAy) <= fabs(fBy))\n"
+" {\n"
+" fTemp = fAx;\n"
+" fAx += 2.0 * (fAx - fBx);\n"
+" if (fAx < 0.0)\n"
+" fAx = 0.0;\n"
+" fBx = fTemp;\n"
+" fBy = fAy;\n"
+" fAy = fp - GetChiSqDistCDF(fAx, fdf);\n"
+" }\n"
+" else\n"
+" {\n"
+" fTemp = fBx;\n"
+" fBx += 2.0 * (fBx - fAx);\n"
+" fAx = fTemp;\n"
+" fAy = fBy;\n"
+" fBy = fp - GetChiSqDistCDF(fBx, fdf);\n"
+" }\n"
+" }\n"
+" if (fAy == 0.0)\n"
+" return fAx;\n"
+" if (fBy == 0.0)\n"
+" return fBx;\n"
+" if (!lcl_HasChangeOfSign( fAy, fBy))\n"
+" {\n"
+" *rConvError = true;\n"
+" return 0.0;\n"
+" }\n"
+" double fPx = fAx;\n"
+" double fPy = fAy;\n"
+" double fQx = fBx;\n"
+" double fQy = fBy;\n"
+" double fRx = fAx;\n"
+" double fRy = fAy;\n"
+" double fSx = 0.5 * (fAx + fBx);\n"
+" bool bHasToInterpolate = true;\n"
+" nCount = 0;\n"
+" while ( nCount < 500 && fabs(fRy) > fYEps &&\n"
+" (fBx-fAx) > fmax( fabs(fAx), fabs(fBx)) * fXEps )\n"
+" {\n"
+" if (bHasToInterpolate)\n"
+" {\n"
+" if (fPy!=fQy && fQy!=fRy && fRy!=fPy)\n"
+" {\n"
+" fSx = fPx * fRy * fQy / (fRy-fPy) / (fQy-fPy)\n"
+" + fRx * fQy * fPy / (fQy-fRy) / (fPy-fRy)\n"
+" + fQx * fPy * fRy / (fPy-fQy) / (fRy-fQy);\n"
+" bHasToInterpolate = (fAx < fSx) && (fSx < fBx);\n"
+" }\n"
+" else\n"
+" bHasToInterpolate = false;\n"
+" }\n"
+" if(!bHasToInterpolate)\n"
+" {\n"
+" fSx = 0.5 * (fAx + fBx);\n"
+" fPx = fAx; fPy = fAy;\n"
+" fQx = fBx; fQy = fBy;\n"
+" bHasToInterpolate = true;\n"
+" }\n"
+" fPx = fQx; fQx = fRx; fRx = fSx;\n"
+" fPy = fQy; fQy = fRy; fRy = fp - GetChiSqDistCDF(fSx, fdf);\n"
+" if (lcl_HasChangeOfSign( fAy, fRy))\n"
+" {\n"
+" fBx = fRx; fBy = fRy;\n"
+" }\n"
+" else\n"
+" {\n"
+" fAx = fRx; fAy = fRy;\n"
+" }\n"
+" bHasToInterpolate = bHasToInterpolate && (fabs(fRy) * 2.0"
+" <= fabs(fQy));\n"
+" ++nCount;\n"
+" }\n"
+" return fRx;\n"
+"}\n";
+
+const char gaussinvDecl[] = "double gaussinv(double x);\n";
+const char gaussinv[] =
+"double gaussinv(double x)\n"
+"{\n"
+" double q,t,z;\n"
+" q=x-0.5;\n"
+" if(fabs(q)<=.425)\n"
+" {\n"
+" t=0.180625-q*q;\n"
+" z=\n"
+" q*\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" t*2509.0809287301226727+33430.575583588128105\n"
+" )\n"
+" *t+67265.770927008700853\n"
+" )\n"
+" *t+45921.953931549871457\n"
+" )\n"
+" *t+13731.693765509461125\n"
+" )\n"
+" *t+1971.5909503065514427\n"
+" )\n"
+" *t+133.14166789178437745\n"
+" )\n"
+" *t+3.387132872796366608\n"
+" )\n"
+" /\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" t*5226.495278852854561+28729.085735721942674\n"
+" )\n"
+" *t+39307.89580009271061\n"
+" )\n"
+" *t+21213.794301586595867\n"
+" )\n"
+" *t+5394.1960214247511077\n"
+" )\n"
+" *t+687.1870074920579083\n"
+" )\n"
+" *t+42.313330701600911252\n"
+" )\n"
+" *t+1.0);\n"
+" }\n"
+" else\n"
+" {\n"
+" if(q>0) t=1-x;\n"
+" else t=x;\n"
+" t=sqrt(-log(t));\n"
+" if(t<=5.0)\n"
+" {\n"
+" t+=-1.6;\n"
+" z=\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" t*7.7454501427834140764e-4+0.0227238449892691845833\n"
+" )\n"
+" *t+0.24178072517745061177\n"
+" )\n"
+" *t+1.27045825245236838258\n"
+" )\n"
+" *t+3.64784832476320460504\n"
+" )\n"
+" *t+5.7694972214606914055\n"
+" )\n"
+" *t+4.6303378461565452959\n"
+" )\n"
+" *t+1.42343711074968357734\n"
+" )\n"
+" /\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" t*1.05075007164441684324e-9+5.475938084995344946e-4\n"
+" )\n"
+" *t+0.0151986665636164571966\n"
+" )\n"
+" *t+0.14810397642748007459\n"
+" )\n"
+" *t+0.68976733498510000455\n"
+" )\n"
+" *t+1.6763848301838038494\n"
+" )\n"
+" *t+2.05319162663775882187\n"
+" )\n"
+" *t+1.0);\n"
+" }\n"
+" else\n"
+" {\n"
+" t+=-5.0;\n"
+" z=\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" t*2.01033439929228813265e-7+2.71155556874348757815e-5\n"
+" )\n"
+" *t+0.0012426609473880784386\n"
+" )\n"
+" *t+0.026532189526576123093\n"
+" )\n"
+" *t+0.29656057182850489123\n"
+" )\n"
+" *t+1.7848265399172913358\n"
+" )\n"
+" *t+5.4637849111641143699\n"
+" )\n"
+" *t+6.6579046435011037772\n"
+" )\n"
+" /\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" (\n"
+" t*2.04426310338993978564e-15+1.4215117583164458887e-7\n"
+" )\n"
+" *t+1.8463183175100546818e-5\n"
+" )\n"
+" *t+7.868691311456132591e-4\n"
+" )\n"
+" *t+0.0148753612908506148525\n"
+" )\n"
+" *t+0.13692988092273580531\n"
+" )\n"
+" *t+0.59983220655588793769\n"
+" )\n"
+" *t+1.0);\n"
+" }\n"
+" if(q<0.0) z=-z;\n"
+" }\n"
+" return z;\n"
+"}\n";
+
+const char lcl_GetLogGammaHelperDecl[] =
+"static double lcl_GetLogGammaHelper(double fZ);\n";
+const char lcl_GetLogGammaHelper[] =
+"static double lcl_GetLogGammaHelper(double fZ)\n"
+"{\n"
+" double fg = 6.024680040776729583740234375;\n"
+" double fZgHelp = fZ + fg - 0.5;\n"
+" return log( lcl_getLanczosSum(fZ)) + (fZ-0.5) * log(fZgHelp) - fZgHelp;\n"
+"}\n";
+const char lcl_GetGammaHelperDecl[] =
+"static double lcl_GetGammaHelper(double fZ);\n";
+const char lcl_GetGammaHelper[] =
+"static double lcl_GetGammaHelper(double fZ)\n"
+"{\n"
+" double fGamma = lcl_getLanczosSum(fZ);\n"
+" double fg = 6.024680040776729583740234375;\n"
+" double fZgHelp = fZ + fg - 0.5;\n"
+" double fHalfpower = pow( fZgHelp, fZ/2 - 0.25);\n"
+" fGamma *= fHalfpower;\n"
+" fGamma = fGamma/exp(fZgHelp);\n"
+" fGamma *= fHalfpower;\n"
+" fGamma = 120.4;\n"
+" if (fZ <= 20.0 && fZ == (int)fZ)\n"
+" {\n"
+" fGamma = (int)(fGamma+0.5);\n"
+" }\n"
+" return fGamma;\n"
+"}\n";
+const char lcl_getLanczosSumDecl[] =
+"static double lcl_getLanczosSum(double fZ);\n";
+const char lcl_getLanczosSum[] =
+"static double lcl_getLanczosSum(double fZ) \n"
+"{ \n"
+" double fNum[13] ={ \n"
+" 23531376880.41075968857200767445163675473, \n"
+" 42919803642.64909876895789904700198885093, \n"
+" 35711959237.35566804944018545154716670596, \n"
+" 17921034426.03720969991975575445893111267, \n"
+" 6039542586.35202800506429164430729792107, \n"
+" 1439720407.311721673663223072794912393972, \n"
+" 248874557.8620541565114603864132294232163, \n"
+" 31426415.58540019438061423162831820536287, \n"
+" 2876370.628935372441225409051620849613599, \n"
+" 186056.2653952234950402949897160456992822, \n"
+" 8071.672002365816210638002902272250613822, \n"
+" 210.8242777515793458725097339207133627117, \n"
+" 2.506628274631000270164908177133837338626 \n"
+" }; \n"
+" double fDenom[13] = { \n"
+" 0,\n"
+" 39916800,\n"
+" 120543840,\n"
+" 150917976,\n"
+" 105258076,\n"
+" 45995730,\n"
+" 13339535,\n"
+" 2637558,\n"
+" 357423,\n"
+" 32670,\n"
+" 1925,\n"
+" 66,\n"
+" 1\n"
+" };\n"
+" double fSumNum;\n"
+" double fSumDenom;\n"
+" int nI;\n"
+" if (fZ<=1.0)\n"
+" {\n"
+" fSumNum = fNum[12];\n"
+" fSumDenom = fDenom[12];\n"
+" nI = 11;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 10;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 9;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 8;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 7;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 6;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 5;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 4;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 3;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 2;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 1;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" nI = 0;\n"
+" fSumNum = fSumNum*fZ+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZ+fDenom[nI];\n"
+" }\n"
+" if (fZ>1.0)\n"
+" {\n"
+" double fZInv = 1.0/fZ;\n"
+" fSumNum = fNum[0];\n"
+" fSumDenom = fDenom[0];\n"
+" nI = 1;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 2;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 3;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 4;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 5;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 6;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 7;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 8;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 9;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 10;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 11;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" nI = 12;\n"
+" fSumNum = fSumNum*fZInv+fNum[nI];\n"
+" fSumDenom = fSumDenom*fZInv+fDenom[nI];\n"
+" }\n"
+" return fSumNum/fSumDenom;\n"
+"}\n";
+
+const char GetUpRegIGammaDecl[] =
+" double GetUpRegIGamma( double fA, double fX ) ;\n";
+const char GetUpRegIGamma[] =
+"double GetUpRegIGamma( double fA, double fX )\n"
+"{\n"
+" double fLnFactor= fA*log(fX)-fX-lgamma(fA);\n"
+" double fFactor = exp(fLnFactor); \n"
+" if (fX>fA+1.0) \n"
+" return fFactor * GetGammaContFraction(fA,fX);\n"
+" else \n"
+" return 1.0 -fFactor * GetGammaSeries(fA,fX);\n"
+"}\n";
+
+const char lcl_HasChangeOfSignDecl[] =
+"static inline bool lcl_HasChangeOfSign( double u, double w );\n";
+const char lcl_HasChangeOfSign[] =
+"static inline bool lcl_HasChangeOfSign( double u, double w )\n"
+"{\n"
+" return (u < 0.0 && w > 0.0) || (u > 0.0 && w < 0.0);\n"
+"}\n";
+
+const char GetTDistDecl[] =" double GetTDist(double T, double fDF);\n";
+const char GetTDist[] =
+"double GetTDist(double T, double fDF)\n"
+"{\n"
+" return 0.5 * GetBetaDist(fDF/(fDF+T*T),fDF/2.0, 0.5);\n"
+"}\n";
+
+const char GetBetaDecl[] =" double GetBeta(double fAlpha, double fBeta);\n";
+const char GetBeta[] =
+"double GetBeta(double fAlpha, double fBeta)\n"
+"{\n"
+" double fA;\n"
+" double fB;\n"
+" fAlpha>fBeta?(fA = fAlpha,fB = fBeta):(fA = fBeta,fB = fAlpha);\n"
+" double fAB = fA + fB;\n"
+
+" if (fAB < fMaxGammaArgument)\n"
+" return tgamma(fA)/tgamma(fAB)*tgamma(fB);\n"
+" double fgm = 5.524680040776729583740234375;\n"
+" double fLanczos = lcl_getLanczosSum(fA)*lcl_getLanczosSum(fB)\n"
+" /lcl_getLanczosSum(fAB);\n"
+" fLanczos *= sqrt(((fAB + fgm)/(fA + fgm))/(fB + fgm));\n"
+" return fLanczos * pow(exp(1.0),(-fA*log1p(fB/(fA + fgm)))"
+" - fB*log1p(fA/(fB + fgm)) - fgm);\n"
+"}\n";
+
+const char GetLogBetaDecl[] =
+" double GetLogBeta(double fAlpha, double fBeta);\n";
+const char GetLogBeta[] =
+"double GetLogBeta(double fAlpha, double fBeta)\n"
+"{\n"
+" double fA;\n"
+" double fB;\n"
+" fAlpha>fBeta?(fA = fAlpha,fB = fBeta):(fA = fBeta,fB = fAlpha);\n"
+" double fgm = 5.524680040776729583740234375;\n"
+
+" double fLanczos = lcl_getLanczosSum(fA)*lcl_getLanczosSum(fB)\n"
+" /lcl_getLanczosSum(fA + fB);\n"
+" double fResult= -fA *log1p(fB/(fA + fgm))"
+"-fB *log1p(fA/(fB + fgm))-fgm;\n"
+" fResult += log(fLanczos)+0.5*(log(fA + fB + fgm) - log(fA + fgm)\n"
+" - log(fB + fgm));\n"
+" return fResult;\n"
+"}\n";
+
+const char GetBetaDistPDFDecl[] =
+"double GetBetaDistPDF(double fX, double fA, double fB);\n";
+const char GetBetaDistPDF[] =
+"double GetBetaDistPDF(double fX, double fA, double fB)\n"
+"{\n"
+" if (fA == 1.0) \n"
+" {\n"
+" if (fB == 1.0)\n"
+" return 1.0;\n"
+" if (fB == 2.0)\n"
+" return -2.0*fX + 2.0;\n"
+" if (fX == 1.0 && fB < 1.0)\n"
+" {\n"
+" return HUGE_VAL;\n"
+" }\n"
+" if (fX <= 0.01)\n"
+" return fB + fB * expm1((fB-1.0) * log1p(-fX));\n"
+" else \n"
+" return fB * pow(0.5-fX+0.5,fB-1.0);\n"
+" }\n"
+" if (fB == 1.0) \n"
+" {\n"
+" if (fA == 2.0)\n"
+" return fA * fX;\n"
+" if (fX == 0.0 && fA < 1.0)\n"
+" {\n"
+" return HUGE_VAL;\n"
+" }\n"
+" return fA * pow(fX,fA-1);\n"
+" }\n"
+" if (fX <= 0.0)\n"
+" {\n"
+" if (fA < 1.0 && fX == 0.0)\n"
+" {\n"
+" return HUGE_VAL;\n"
+" }\n"
+" else\n"
+" return 0.0;\n"
+" }\n"
+" if (fX >= 1.0)\n"
+" {\n"
+" if (fB < 1.0 && fX == 1.0)\n"
+" {\n"
+" return HUGE_VAL;\n"
+" }\n"
+" else \n"
+" return 0.0;\n"
+" }\n"
+" double fLogDblMax = log( 1.79769e+308 );\n"
+" double fLogDblMin = log( 2.22507e-308 );\n"
+" double fLogY = (fX < 0.1) ? log1p(-fX) : log(0.5-fX+0.5);\n"
+" double fLogX = log(fX);\n"
+" double fAm1LogX = (fA-1.0) * fLogX;\n"
+" double fBm1LogY = (fB-1.0) * fLogY;\n"
+" double fLogBeta = GetLogBeta(fA,fB);\n"
+" if ( fAm1LogX < fLogDblMax && fAm1LogX > fLogDblMin\n"
+" && fBm1LogY < fLogDblMax && fBm1LogY > fLogDblMin\n"
+" && fLogBeta < fLogDblMax && fLogBeta > fLogDblMin\n"
+" && fAm1LogX + fBm1LogY < fLogDblMax && fAm1LogX + fBm1LogY > \n"
+" fLogDblMin)\n"
+" return pow(fX,fA-1.0)*pow(0.5-fX+0.5,fB-1.0)/GetBeta(fA,fB);\n"
+" else \n"
+" return exp( fAm1LogX + fBm1LogY - fLogBeta);\n"
+"}\n";
+
+const char lcl_GetBetaHelperContFracDecl[] =
+"double lcl_GetBetaHelperContFrac(double fX, double fA, double fB);\n";
+const char lcl_GetBetaHelperContFrac[] =
+"double lcl_GetBetaHelperContFrac(double fX, double fA, double fB)\n"
+"{ \n"
+
+" double a1, b1, a2, b2, fnorm, apl2m, d2m, d2m1, cfnew, cf;\n"
+" a1 = 1.0; b1 = 1.0;\n"
+" b2 = 1.0 - (fA+fB)/(fA+1.0)*fX;\n"
+" b2==0.0?(a2 = 0.0,fnorm = 1.0,cf = 1.0):\n"
+" (a2 = 1.0,fnorm = 1.0/b2,cf = a2*fnorm);\n"
+" cfnew = 1.0;\n"
+" double rm = 1.0;\n"
+" double fMaxIter = 50000.0;\n"
+" bool bfinished = false;\n"
+" do\n"
+" {\n"
+" apl2m = fA + 2.0*rm;\n"
+" d2m = (rm*(fB-rm))*fX/(apl2m*(apl2m-1.0));\n"
+" d2m1 = -((fA+rm)*(fA+rm+fB))*fX/(apl2m*(apl2m+1.0));\n"
+" a1 = (a2+d2m*a1)*fnorm;\n"
+" b1 = (b2+d2m*b1)*fnorm;\n"
+" a2 = a1 + d2m1*a2*fnorm;\n"
+" b2 = b1 + d2m1*b2*fnorm;\n"
+" if (b2 != 0.0) \n"
+" {\n"
+" fnorm = 1.0/b2;\n"
+" cfnew = a2*fnorm;\n"
+" bfinished = (fabs(cf-cfnew) < fabs(cf)*fMachEps);\n"
+" }\n"
+" cf = cfnew;\n"
+" rm += 1.0;\n"
+" }\n"
+" while (rm < fMaxIter && !bfinished);\n"
+" return cf;\n"
+"}\n";
+
+const char lcl_IterateInverseDecl[] =
+"double lcl_IterateInverse("
+"double fAx, double fBx, bool* rConvError,double fp,double fDF );\n";
+const char lcl_IterateInverse[] =
+"double lcl_IterateInverse( "
+"double fAx, double fBx, bool* rConvError,double fp,double fDF )\n"
+"{\n"
+" *rConvError = false;\n"
+" double fYEps = 1.0E-307;\n"
+" double fXEps =DBL_EPSILON;\n"
+" if(fAx>fBx)\n"
+" return DBL_MAX;\n"
+" double fAy = GetValue(fAx,fp,fDF);\n"
+" double fBy = GetValue(fBx,fp,fDF);\n"
+" double fTemp;\n"
+" unsigned short nCount;\n"
+" double inter;\n"
+" bool sign;\n"
+" for (nCount =0;nCount<1000&&!lcl_HasChangeOfSign(fAy,fBy);nCount++)\n"
+" {\n"
+" inter = 2.0 * (fAx - fBx);\n"
+" if (fabs(fAy) <= fabs(fBy)) \n"
+" {\n"
+" sign = true;\n"
+" fTemp = fAx;\n"
+" fAx += inter;\n"
+" if (fAx < 0.0)\n"
+" fAx = 0.0;\n"
+" fBx = fTemp;\n"
+" fBy = fAy;\n"
+" fTemp = fAx;\n"
+" }\n"
+" else\n"
+" {\n"
+" sign = false;\n"
+" fTemp = fBx;\n"
+" fBx -= inter;\n"
+" fAx = fTemp;\n"
+" fAy = fBy;\n"
+" fTemp = fBx;\n"
+" }\n"
+" fTemp = GetValue(fTemp,fp,fDF);\n"
+" sign?(fAy = fTemp):(fBy = fTemp);\n"
+" }\n"
+" if (fAy == 0.0)\n"
+" return fAx;\n"
+" if (fBy == 0.0)\n"
+" return fBx;\n"
+" if (!lcl_HasChangeOfSign( fAy, fBy))\n"
+" {\n"
+" *rConvError = true;\n"
+" return 0.0;\n"
+" }\n"
+" double fPx = fAx;\n"
+" double fPy = fAy;\n"
+" double fQx = fBx;\n"
+" double fQy = fBy;\n"
+" double fRx = fAx;\n"
+" double fRy = fAy;\n"
+" double fSx = 0.5 * (fAx + fBx); \n"
+" bool bHasToInterpolate = true;\n"
+" nCount = 0;\n"
+" while ( nCount < 500 && fabs(fRy) > fYEps &&\n"
+" (fBx-fAx) > max( fabs(fAx), fabs(fBx)) * fXEps )\n"
+" {\n"
+" if (bHasToInterpolate)\n"
+" {\n"
+" if (fPy!=fQy && fQy!=fRy && fRy!=fPy)\n"
+" {\n"
+" fSx = fPx * fRy * fQy / (fRy-fPy) / (fQy-fPy)\n"
+" + fRx * fQy * fPy / (fQy-fRy) / (fPy-fRy)\n"
+" + fQx * fPy * fRy / (fPy-fQy) / (fRy-fQy);\n"
+" bHasToInterpolate = (fAx < fSx) && (fSx < fBx);\n"
+" }\n"
+" else\n"
+" bHasToInterpolate = false;\n"
+" }\n"
+" if(!bHasToInterpolate)\n"
+" {\n"
+" fSx = 0.5 * (fAx + fBx);\n"
+" \n"
+" fPx = fAx; fPy = fAy;\n"
+" fQx = fBx; fQy = fBy;\n"
+" bHasToInterpolate = true;\n"
+" }\n"
+" fPx = fQx; fQx = fRx; fRx = fSx;\n"
+" fPy = fQy; fQy = fRy; fRy = GetValue(fSx,fp,fDF);\n"
+" lcl_HasChangeOfSign( fAy, fRy)?(fBx = fRx,fBy = fRy):\n"
+" (fAx = fRx,fAy = fRy);\n"
+" bHasToInterpolate =\n"
+" bHasToInterpolate && (fabs(fRy) * 2.0 <= fabs(fQy));\n"
+" ++nCount;\n"
+" }\n"
+" return fRx;\n"
+"}\n";
+const char phiDecl[] =
+"double phi(double x);\n";
+const char phi[] =
+"double phi(double x)\n"
+"{\n"
+" return 0.39894228040143268 * exp(-(x * x) / 2.0);\n"
+"}\n";
+const char taylorDecl[] =
+"double taylor(double* pPolynom, uint nMax, double x);\n";
+const char taylor[] =
+"double taylor(double* pPolynom, uint nMax, double x)\n"
+"{\n"
+" double nVal = pPolynom[nMax];\n"
+" for (short i = nMax-1; i >= 0; i--)\n"
+" {\n"
+" nVal = pPolynom[i] + (nVal * x);\n"
+" }\n"
+" return nVal;\n"
+"}";
+const char gaussDecl[] = "double gauss(double x);\n";
+const char gauss[] =
+"double gauss(double x)\n"
+"{\n"
+" double xAbs = fabs(x);\n"
+" uint xShort = (uint)(floor(xAbs));\n"
+" double nVal = 0.0;\n"
+" if (xShort == 0)\n"
+" {\n"
+" double t0[] =\n"
+" { 0.39894228040143268, -0.06649038006690545, 0.00997355701003582,\n"
+" -0.00118732821548045, 0.00011543468761616, -0.00000944465625950,\n"
+" 0.00000066596935163, -0.00000004122667415, 0.00000000227352982,\n"
+" 0.00000000011301172, 0.00000000000511243, -0.00000000000021218 };\n"
+" nVal = taylor(t0, 11, (xAbs * xAbs)) * xAbs;\n"
+" }\n"
+" else if ((xShort >= 1) && (xShort <= 2))\n"
+" {\n"
+" double t2[] =\n"
+" { 0.47724986805182079, 0.05399096651318805, -0.05399096651318805,\n"
+" 0.02699548325659403, -0.00449924720943234, -0.00224962360471617,\n"
+" 0.00134977416282970, -0.00011783742691370, -0.00011515930357476,\n"
+" 0.00003704737285544, 0.00000282690796889, -0.00000354513195524,\n"
+" 0.00000037669563126, 0.00000019202407921, -0.00000005226908590,\n"
+" -0.00000000491799345, 0.00000000366377919, -0.00000000015981997,\n"
+" -0.00000000017381238, 0.00000000002624031, 0.00000000000560919,\n"
+" -0.00000000000172127, -0.00000000000008634, 0.00000000000007894 };\n"
+" nVal = taylor(t2, 23, (xAbs - 2.0));\n"
+" }\n"
+" else if ((xShort >= 3) && (xShort <= 4))\n"
+" {\n"
+" double t4[] =\n"
+" { 0.49996832875816688, 0.00013383022576489, -0.00026766045152977,\n"
+" 0.00033457556441221, -0.00028996548915725, 0.00018178605666397,\n"
+" -0.00008252863922168, 0.00002551802519049, -0.00000391665839292,\n"
+" -0.00000074018205222, 0.00000064422023359, -0.00000017370155340,\n"
+" 0.00000000909595465, 0.00000000944943118, -0.00000000329957075,\n"
+" 0.00000000029492075, 0.00000000011874477, -0.00000000004420396,\n"
+" 0.00000000000361422, 0.00000000000143638, -0.00000000000045848 };\n"
+" nVal = taylor(t4, 20, (xAbs - 4.0));\n"
+" }\n"
+" else\n"
+" {\n"
+" double asympt[] = { -1.0, 1.0, -3.0, 15.0, -105.0 };\n"
+" nVal = 0.5 + phi(xAbs) * taylor(asympt, 4, 1.0/(xAbs * xAbs))/xAbs;\n"
+" }\n"
+" if (x < 0.0)\n"
+" return -nVal;\n"
+" else\n"
+" return nVal;\n"
+"}\n";
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/opbase.cxx b/sc/source/core/opencl/opbase.cxx
new file mode 100644
index 0000000000..4ea248daaa
--- /dev/null
+++ b/sc/source/core/opencl/opbase.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/.
+ */
+
+#include <opencl/openclwrapper.hxx>
+#include <formula/vectortoken.hxx>
+#include <rtl/string.hxx>
+#include <sal/log.hxx>
+#include <utility>
+#include <unordered_map>
+
+#include "opbase.hxx"
+
+using namespace formula;
+
+namespace sc::opencl {
+
+UnhandledToken::UnhandledToken(
+ const char* m, std::string fn, int ln ) :
+ mMessage(m), mFile(std::move(fn)), mLineNumber(ln) {}
+
+OpenCLError::OpenCLError( std::string function, cl_int error, std::string file, int line ) :
+ mFunction(std::move(function)), mError(error), mFile(std::move(file)), mLineNumber(line)
+{
+ // Not sure if this SAL_INFO() is useful; the place in
+ // CLInterpreterContext::launchKernel() where OpenCLError is
+ // caught already uses SAL_WARN() to display it.
+
+ // SAL_INFO("sc.opencl", "OpenCL error: " << openclwrapper::errorString(mError));
+}
+
+Unhandled::Unhandled( std::string fn, int ln ) :
+ mFile(std::move(fn)), mLineNumber(ln) {}
+
+InvalidParameterCount::InvalidParameterCount( int parameterCount, std::string file, int ln ) :
+ mParameterCount(parameterCount), mFile(std::move(file)), mLineNumber(ln) {}
+
+DynamicKernelArgument::DynamicKernelArgument( const ScCalcConfig& config, std::string s,
+ FormulaTreeNodeRef ft ) :
+ mCalcConfig(config), mSymName(std::move(s)), mFormulaTree(std::move(ft)) { }
+
+std::string DynamicKernelArgument::GenDoubleSlidingWindowDeclRef( bool ) const
+{
+ return std::string("");
+}
+
+/// When Mix, it will be called
+std::string DynamicKernelArgument::GenStringSlidingWindowDeclRef( bool ) const
+{
+ return std::string("");
+}
+
+/// Generate use/references to the argument
+void DynamicKernelArgument::GenDeclRef( outputstream& ss ) const
+{
+ ss << mSymName;
+}
+
+void DynamicKernelArgument::GenSlidingWindowFunction( outputstream& ) {}
+
+FormulaToken* DynamicKernelArgument::GetFormulaToken() const
+{
+ return mFormulaTree->GetFormulaToken();
+}
+
+std::string DynamicKernelArgument::DumpOpName() const
+{
+ return std::string("");
+}
+
+void DynamicKernelArgument::DumpInlineFun( std::set<std::string>&, std::set<std::string>& ) const {}
+
+const std::string& DynamicKernelArgument::GetName() const
+{
+ return mSymName;
+}
+
+bool DynamicKernelArgument::NeedParallelReduction() const
+{
+ return false;
+}
+
+// Strings and OpenCL:
+// * Strings are non-trivial types and so passing them to OpenCL and handling them there
+// would be rather complex. However, in practice most string operations are checking
+// string equality, so only such string usage is supported (other cases will be
+// handled by Calc core when they get rejected for OpenCL).
+// * Strings from Calc core come from svl::SharedString, which already ensures that
+// equal strings have equal rtl_uString.
+// * Strings are passed to opencl as integer IDs, each uniquely identifying a different
+// string.
+// * OpenCL code generally handles all values as doubles, so merely converting rtl_uString*
+// to double could lead to loss of precision (double can store 52bits of precision).
+// This could lead to two strings possibly being considered equal by mistake (unlikely,
+// but not impossible). Therefore all rtl_uString* are mapped to internal integer IDs.
+// * Functions that can handle strings properly should override OpBase::takeString()
+// to return true. They should
+// * Empty string Id is 0. Empty cell Id is NAN.
+// * Since strings are marshalled as doubles too, it is important to check whether a value
+// is a real double or a string. Use e.g. GenerateArgType to generate also 'xxx_is_string'
+// variable, there is cell_equal() function to compare two cells.
+
+static std::unordered_map<const rtl_uString*, int>* stringIdsMap;
+
+int DynamicKernelArgument::GetStringId( const rtl_uString* string )
+{
+ assert( string != nullptr );
+ if( string->length == 0 )
+ return 0;
+ if( stringIdsMap == nullptr )
+ stringIdsMap = new std::unordered_map<const rtl_uString*, int>;
+ int newId = stringIdsMap->size() + 1;
+ auto aItInsertedPair = stringIdsMap->insert( std::pair( string, newId ));
+ return aItInsertedPair.first->second;
+}
+
+void DynamicKernelArgument::ClearStringIds()
+{
+ delete stringIdsMap;
+ stringIdsMap = nullptr;
+}
+
+VectorRef::VectorRef( const ScCalcConfig& config, const std::string& s, const FormulaTreeNodeRef& ft, int idx ) :
+ DynamicKernelArgument(config, s, ft), mpClmem(nullptr), mnIndex(idx), forceStringsToZero( false )
+{
+ if (mnIndex)
+ {
+ outputstream ss;
+ ss << mSymName << "s" << mnIndex;
+ mSymName = ss.str();
+ }
+}
+
+VectorRef::~VectorRef()
+{
+ if (mpClmem)
+ {
+ cl_int err;
+ err = clReleaseMemObject(mpClmem);
+ SAL_WARN_IF(err != CL_SUCCESS, "sc.opencl", "clReleaseMemObject failed: " << openclwrapper::errorString(err));
+ }
+}
+
+/// Generate declaration
+void VectorRef::GenDecl( outputstream& ss ) const
+{
+ ss << "__global double *" << mSymName;
+}
+
+/// When declared as input to a sliding window function
+void VectorRef::GenSlidingWindowDecl( outputstream& ss ) const
+{
+ VectorRef::GenDecl(ss);
+}
+
+/// When referenced in a sliding window function
+std::string VectorRef::GenSlidingWindowDeclRef( bool nested ) const
+{
+ outputstream ss;
+ formula::SingleVectorRefToken* pSVR =
+ dynamic_cast<formula::SingleVectorRefToken*>(DynamicKernelArgument::GetFormulaToken());
+ if (pSVR && !nested)
+ ss << "(gid0 < " << pSVR->GetArrayLength() << "?";
+ ss << mSymName << "[gid0]";
+ if (pSVR && !nested)
+ ss << ":NAN)";
+ return ss.str();
+}
+
+void VectorRef::GenSlidingWindowFunction( outputstream& ) {}
+
+size_t VectorRef::GetWindowSize() const
+{
+ FormulaToken* pCur = mFormulaTree->GetFormulaToken();
+ assert(pCur);
+ if (const formula::DoubleVectorRefToken* pCurDVR =
+ dynamic_cast<const formula::DoubleVectorRefToken*>(pCur))
+ {
+ return pCurDVR->GetRefRowSize();
+ }
+ else if (dynamic_cast<const formula::SingleVectorRefToken*>(pCur))
+ {
+ // Prepare intermediate results (on CPU for now)
+ return 1;
+ }
+ else
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+}
+
+std::string VectorRef::DumpOpName() const
+{
+ return std::string("");
+}
+
+void VectorRef::DumpInlineFun( std::set<std::string>&, std::set<std::string>& ) const {}
+
+const std::string& VectorRef::GetName() const
+{
+ return mSymName;
+}
+
+cl_mem VectorRef::GetCLBuffer() const
+{
+ return mpClmem;
+}
+
+bool VectorRef::NeedParallelReduction() const
+{
+ return false;
+}
+
+VectorRefStringsToZero::VectorRefStringsToZero( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft, int index )
+ : VectorRef( config, s, ft, index )
+{
+ forceStringsToZero = true;
+}
+
+void SlidingFunctionBase::GenerateArg( const char* name, int arg, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, GenerateArgTypeType generateType )
+{
+ assert( arg < int( vSubArguments.size()));
+ FormulaToken *token = vSubArguments[arg]->GetFormulaToken();
+ if( token == nullptr )
+ throw Unhandled( __FILE__, __LINE__ );
+ if(token->GetOpCode() == ocPush)
+ {
+ if(token->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* svr =
+ static_cast<const formula::SingleVectorRefToken *>(token);
+ ss << " double " << name << " = NAN;\n";
+ if( generateType == GenerateArgType )
+ ss << " bool " << name << "_is_string = false;\n";
+ ss << " if (gid0 < " << svr->GetArrayLength() << ")\n";
+ if( generateType == GenerateArgType )
+ ss << " {\n";
+ ss << " " << name << " = ";
+ ss << vSubArguments[arg]->GenSlidingWindowDeclRef( true ) << ";\n";
+ if( generateType == GenerateArgType )
+ {
+ ss << " " << name << "_is_string = ";
+ ss << vSubArguments[arg]->GenIsString( true ) << ";\n";
+ ss << " }\n";
+ }
+ switch( empty )
+ {
+ case EmptyIsZero:
+ ss << " if( isnan( " << name << " ))\n";
+ ss << " " << name << " = 0;\n";
+ break;
+ case EmptyIsNan:
+ break;
+ case SkipEmpty:
+ abort();
+ break;
+ }
+ }
+ else if(token->GetType() == formula::svDouble)
+ {
+ ss << " double " << name << " = " << token->GetDouble() << ";\n";
+ if( generateType == GenerateArgType )
+ ss << " bool " << name << "_is_string = "
+ << vSubArguments[arg]->GenIsString() << ";\n";
+ }
+ else if(token->GetType() == formula::svString)
+ {
+ if( forceStringsToZero())
+ assert( dynamic_cast<DynamicKernelStringToZeroArgument*>(vSubArguments[arg].get()));
+ else if( !takeString())
+ throw Unhandled( __FILE__, __LINE__ );
+ ss << " double " << name << " = 0.0;\n";
+ if( generateType == GenerateArgType )
+ ss << " bool " << name << "_is_string = "
+ << vSubArguments[arg]->GenIsString() << ";\n";
+ }
+ else
+ throw Unhandled( __FILE__, __LINE__ );
+ }
+ else
+ {
+ ss << " double " << name << " = ";
+ ss << vSubArguments[arg]->GenSlidingWindowDeclRef() << ";\n";
+ if( generateType == GenerateArgType )
+ ss << " bool " << name << "_is_string = "
+ << vSubArguments[arg]->GenIsString() << ";\n";
+ }
+}
+
+void SlidingFunctionBase::GenerateArg( int arg, SubArguments& vSubArguments, outputstream& ss,
+ EmptyArgType empty, GenerateArgTypeType generateType )
+{
+ OString buf = "arg" + OString::number(arg);
+ GenerateArg( buf.getStr(), arg, vSubArguments, ss, empty, generateType );
+}
+
+void SlidingFunctionBase::GenerateArgWithDefault( const char* name, int arg, double def,
+ SubArguments& vSubArguments, outputstream& ss, EmptyArgType empty )
+{
+ if( arg < int(vSubArguments.size()))
+ GenerateArg( name, arg, vSubArguments, ss, empty );
+ else
+ ss << " double " << name << " = " << def << ";\n";
+}
+
+void SlidingFunctionBase::GenerateRangeArgs( int firstArg, int lastArg, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code )
+{
+ assert( firstArg >= 0 );
+ assert( firstArg <= lastArg );
+ assert( lastArg < int( vSubArguments.size()));
+ for( int i = firstArg;
+ i <= lastArg;
+ ++i )
+ {
+ FormulaToken *token = vSubArguments[i]->GetFormulaToken();
+ if( token == nullptr )
+ throw Unhandled( __FILE__, __LINE__ );
+ if(token->GetOpCode() == ocPush)
+ {
+ if (token->GetType() == formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken *>(token);
+ GenerateDoubleVectorLoopHeader( ss, pDVR, nullptr );
+ ss << " double arg = ";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ switch( empty )
+ {
+ case EmptyIsZero:
+ ss << " if( isnan( arg ))\n";
+ ss << " arg = 0;\n";
+ break;
+ case EmptyIsNan:
+ break;
+ case SkipEmpty:
+ ss << " if( isnan( arg ))\n";
+ ss << " continue;\n";
+ break;
+ }
+ ss << code;
+ ss << " }\n";
+ }
+ else if (token->GetType() == formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pSVR =
+ static_cast< const formula::SingleVectorRefToken*>(token);
+ ss << " if (gid0 < " << pSVR->GetArrayLength() << ")\n";
+ ss << " {\n";
+ ss << " double arg = ";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef() << ";\n";
+ switch( empty )
+ {
+ case EmptyIsZero:
+ ss << " if( isnan( arg ))\n";
+ ss << " arg = 0;\n";
+ ss << code;
+ break;
+ case EmptyIsNan:
+ ss << code;
+ break;
+ case SkipEmpty:
+ ss << " if( !isnan( arg ))\n";
+ ss << " {\n";
+ ss << code;
+ ss << " }\n";
+ break;
+ }
+ ss << " }\n";
+ }
+ else if(token->GetType() == formula::svDouble)
+ {
+ ss << " {\n";
+ ss << " double arg = " << token->GetDouble() << ";\n";
+ ss << code;
+ ss << " }\n";
+ }
+ else if(token->GetType() == formula::svString)
+ {
+ assert( dynamic_cast<DynamicKernelStringToZeroArgument*>(vSubArguments[i].get()));
+ ss << " {\n";
+ ss << " double arg = 0.0;\n";
+ ss << code;
+ ss << " }\n";
+ }
+ else
+ throw Unhandled( __FILE__, __LINE__ );
+ }
+ else
+ {
+ ss << " {\n";
+ ss << " double arg = ";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef() << ";\n";
+ ss << code;
+ ss << " }\n";
+ }
+ }
+}
+
+void SlidingFunctionBase::GenerateRangeArgs( SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code )
+{
+ GenerateRangeArgs( 0, vSubArguments.size() - 1, vSubArguments, ss, empty, code );
+}
+
+void SlidingFunctionBase::GenerateRangeArg( int arg, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code )
+{
+ GenerateRangeArgs( arg, arg, vSubArguments, ss, empty, code );
+}
+
+void SlidingFunctionBase::GenerateRangeArgPair( int arg1, int arg2, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code, const char* firstElementDiff )
+{
+ assert( arg1 >= 0 && arg1 < int (vSubArguments.size()));
+ assert( arg2 >= 0 && arg2 < int (vSubArguments.size()));
+ assert( arg1 != arg2 );
+ FormulaToken *token1 = vSubArguments[arg1]->GetFormulaToken();
+ if( token1 == nullptr )
+ throw Unhandled( __FILE__, __LINE__ );
+ FormulaToken *token2 = vSubArguments[arg2]->GetFormulaToken();
+ if( token2 == nullptr )
+ throw Unhandled( __FILE__, __LINE__ );
+ if(token1->GetType() != formula::svDoubleVectorRef
+ || token2->GetType() != formula::svDoubleVectorRef)
+ {
+ throw Unhandled( __FILE__, __LINE__ );
+ }
+ const formula::DoubleVectorRefToken* pDVR1 =
+ static_cast<const formula::DoubleVectorRefToken *>(token1);
+ const formula::DoubleVectorRefToken* pDVR2 =
+ static_cast<const formula::DoubleVectorRefToken *>(token2);
+
+ size_t nCurWindowSize1 = pDVR1->GetRefRowSize();
+ size_t nCurWindowSize2 = pDVR2->GetRefRowSize();
+
+ if(nCurWindowSize1 != nCurWindowSize2)
+ throw Unhandled( __FILE__, __LINE__ );
+ if(pDVR1->IsStartFixed() != pDVR2->IsStartFixed()
+ || pDVR1->IsEndFixed() != pDVR2->IsEndFixed())
+ {
+ throw Unhandled( __FILE__, __LINE__ );
+ }
+
+ // If either of the ranges ends with empty cells, it will not include those last
+ // nan values (its GetArrayLength() will be less than its GetRefRowSize().
+ // If we skip empty cells, just iterate until both ranges have elements, but if
+ // we need to iterate even over empty cells, so use the longer one.
+ // FIXME: If both ranges end with empty cells, this does not actually iterate
+ // over all empty cells.
+ const formula::DoubleVectorRefToken* loopDVR;
+ bool checkBounds;
+ if( empty == SkipEmpty )
+ {
+ loopDVR = pDVR1->GetArrayLength() < pDVR2->GetArrayLength() ? pDVR1 : pDVR2;
+ checkBounds = false;
+ }
+ else
+ {
+ loopDVR = pDVR1->GetArrayLength() > pDVR2->GetArrayLength() ? pDVR1 : pDVR2;
+ checkBounds = true;
+ }
+ GenerateDoubleVectorLoopHeader( ss, loopDVR, firstElementDiff );
+ ss << " double arg1 = ";
+ ss << vSubArguments[arg1]->GenSlidingWindowDeclRef(!checkBounds) << ";\n";
+ ss << " double arg2 = ";
+ ss << vSubArguments[arg2]->GenSlidingWindowDeclRef(!checkBounds) << ";\n";
+ switch( empty )
+ {
+ case EmptyIsZero:
+ ss << " if( isnan( arg1 ))\n";
+ ss << " arg1 = 0;\n";
+ ss << " if( isnan( arg2 ))\n";
+ ss << " arg2 = 0;\n";
+ break;
+ case EmptyIsNan:
+ break;
+ case SkipEmpty:
+ ss << " if( isnan( arg1 ) || isnan( arg2 ))\n";
+ ss << " continue;\n";
+ break;
+ }
+ ss << code;
+ ss << " }\n";
+}
+
+void SlidingFunctionBase::GenerateRangeArgElement( const char* name, int arg, const char* element,
+ SubArguments& vSubArguments, outputstream& ss, EmptyArgType empty )
+{
+ assert( arg >= 0 && arg < int (vSubArguments.size()));
+ FormulaToken *token = vSubArguments[arg]->GetFormulaToken();
+ if( token == nullptr )
+ throw Unhandled( __FILE__, __LINE__ );
+ if(token->GetType() != formula::svDoubleVectorRef)
+ throw Unhandled( __FILE__, __LINE__ );
+ const formula::DoubleVectorRefToken* pDVR =
+ static_cast<const formula::DoubleVectorRefToken *>(token);
+ ss << " double " << name << " = NAN;\n";
+ ss << " {\n";
+ // GenSlidingWindowDeclRef() may refer to 'i' variable.
+ ss << " int i = 0;\n";
+ ss << " if( ";
+ if( !pDVR->IsStartFixed())
+ ss << "gid0 + ";
+ ss << element << " < " << pDVR->GetArrayLength() << " )\n";
+ ss << " " << name << " = " << vSubArguments[arg]->GenSlidingWindowDeclRef(true) << ";\n";
+ ss << " }\n";
+ switch( empty )
+ {
+ case EmptyIsZero:
+ ss << " if( isnan( " << name << " ))\n";
+ ss << " " << name << " = 0;\n";
+ break;
+ case EmptyIsNan:
+ break;
+ case SkipEmpty:
+ abort();
+ break;
+ }
+}
+
+void SlidingFunctionBase::GenerateDoubleVectorLoopHeader( outputstream& ss,
+ const formula::DoubleVectorRefToken* pDVR, const char* firstElementDiff )
+{
+ size_t nCurWindowSize = pDVR->GetRefRowSize();
+ std::string startDiff;
+ if( firstElementDiff )
+ startDiff = std::string( " + " ) + firstElementDiff;
+ ss << " for (int i = ";
+ if (!pDVR->IsStartFixed() && pDVR->IsEndFixed())
+ {
+ ss << "gid0" << startDiff << "; i < " << pDVR->GetArrayLength();
+ ss << " && i < " << nCurWindowSize << "; i++)\n";
+ ss << " {\n";
+ }
+ else if (pDVR->IsStartFixed() && !pDVR->IsEndFixed())
+ {
+ ss << "0" << startDiff << "; i < " << pDVR->GetArrayLength();
+ ss << " && i < gid0+" << nCurWindowSize << "; i++)\n";
+ ss << " {\n";
+ }
+ else if (!pDVR->IsStartFixed() && !pDVR->IsEndFixed())
+ {
+ ss << "0" << startDiff << "; i + gid0 < " << pDVR->GetArrayLength();
+ ss << " && i < " << nCurWindowSize << "; i++)\n";
+ ss << " {\n";
+ }
+ else
+ {
+ ss << "0" << startDiff << "; i < " << pDVR->GetArrayLength() << "; i++)\n";
+ ss << " {\n";
+ }
+}
+
+void SlidingFunctionBase::GenerateFunctionDeclaration( const std::string& sSymName,
+ SubArguments& vSubArguments, outputstream& ss )
+{
+ ss << "\ndouble " << sSymName;
+ ss << "_"<< BinFuncName() <<"(";
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ if (i)
+ ss << ", ";
+ vSubArguments[i]->GenSlidingWindowDecl(ss);
+ }
+ ss << ")\n";
+}
+
+void Normal::GenSlidingWindowFunction(
+ outputstream& ss, const std::string& sSymName, SubArguments& vSubArguments )
+{
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n\t";
+ ss << "double tmp = " << GetBottom() << ";\n\t";
+ ss << "int gid0 = get_global_id(0);\n\t";
+ ss << "tmp = ";
+ std::vector<std::string> argVector;
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ argVector.push_back(vSubArguments[i]->GenSlidingWindowDeclRef());
+ ss << Gen(argVector);
+ ss << ";\n\t";
+ ss << "return tmp;\n";
+ ss << "}";
+}
+
+void CheckVariables::GenTmpVariables(
+ outputstream& ss, const SubArguments& vSubArguments )
+{
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ ss << " double tmp";
+ ss << i;
+ ss << ";\n";
+ }
+}
+
+void CheckVariables::CheckSubArgumentIsNan( outputstream& ss,
+ SubArguments& vSubArguments, int argumentNum )
+{
+ int i = argumentNum;
+ if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svSingleVectorRef)
+ {
+ const formula::SingleVectorRefToken* pTmpDVR1 =
+ static_cast<const formula::SingleVectorRefToken*>(vSubArguments[i]->GetFormulaToken());
+ ss << " if(singleIndex>=";
+ ss << pTmpDVR1->GetArrayLength();
+ ss << " ||";
+ ss << "isnan(";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef(true);
+ ss << "))\n";
+ ss << " tmp";
+ ss << i;
+ ss << "=0;\n else \n";
+ ss << " tmp";
+ ss << i;
+ ss << "=";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef(true);
+ ss << ";\n";
+ }
+ if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svDoubleVectorRef)
+ {
+ const formula::DoubleVectorRefToken* pTmpDVR2 =
+ static_cast<const formula::DoubleVectorRefToken*>(vSubArguments[i]->GetFormulaToken());
+ ss << " if(doubleIndex>=";
+ ss << pTmpDVR2->GetArrayLength();
+ ss << " ||";
+ ss << "isnan(";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef();
+ ss << "))\n";
+ ss << " tmp";
+ ss << i;
+ ss << "=0;\n else \n";
+ ss << " tmp";
+ ss << i;
+ ss << "=";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+ }
+ if (vSubArguments[i]->GetFormulaToken()->GetType() == formula::svDouble ||
+ vSubArguments[i]->GetFormulaToken()->GetOpCode() != ocPush)
+ {
+ ss << " if(";
+ ss << "isnan(";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef();
+ ss << "))\n";
+ ss << " tmp";
+ ss << i;
+ ss << "=0;\n else \n";
+ ss << " tmp";
+ ss << i;
+ ss << "=";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef();
+ ss << ";\n";
+
+ }
+
+}
+
+void CheckVariables::CheckSubArgumentIsNan2( outputstream& ss,
+ SubArguments& vSubArguments, int argumentNum, const std::string& p )
+{
+ int i = argumentNum;
+ if (vSubArguments[i]->GetFormulaToken()->GetType() == formula::svDouble)
+ {
+ ss << " tmp";
+ ss << i;
+ ss << "=";
+ vSubArguments[i]->GenDeclRef(ss);
+ ss << ";\n";
+ return;
+ }
+
+ ss << " tmp";
+ ss << i;
+ ss << "= fsum(";
+ vSubArguments[i]->GenDeclRef(ss);
+ if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svDoubleVectorRef)
+ ss << "[" << p.c_str() << "]";
+ else if (vSubArguments[i]->GetFormulaToken()->GetType() ==
+ formula::svSingleVectorRef)
+ ss << "[get_group_id(1)]";
+ ss << ", 0);\n";
+}
+
+void CheckVariables::CheckAllSubArgumentIsNan(
+ outputstream& ss, SubArguments& vSubArguments )
+{
+ ss << " int k = gid0;\n";
+ for (size_t i = 0; i < vSubArguments.size(); i++)
+ {
+ CheckSubArgumentIsNan(ss, vSubArguments, i);
+ }
+}
+
+void CheckVariables::UnrollDoubleVector( outputstream& ss,
+ const outputstream& unrollstr, const formula::DoubleVectorRefToken* pCurDVR,
+ int nCurWindowSize )
+{
+ int unrollSize = 16;
+ if (!pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed())
+ {
+ ss << " loop = (" << nCurWindowSize << " - gid0)/";
+ ss << unrollSize << ";\n";
+ }
+ else if (pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << " loop = (" << nCurWindowSize << " + gid0)/";
+ ss << unrollSize << ";\n";
+
+ }
+ else
+ {
+ ss << " loop = " << nCurWindowSize << "/" << unrollSize << ";\n";
+ }
+
+ ss << " for ( int j = 0;j< loop; j++)\n";
+ ss << " {\n";
+ ss << " int i = ";
+ if (!pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed())
+ {
+ ss << "gid0 + j * " << unrollSize << ";\n";
+ }
+ else
+ {
+ ss << "j * " << unrollSize << ";\n";
+ }
+
+ if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << " int doubleIndex = i+gid0;\n";
+ }
+ else
+ {
+ ss << " int doubleIndex = i;\n";
+ }
+
+ for (int j = 0; j < unrollSize; j++)
+ {
+ ss << unrollstr.str();
+ ss << "i++;\n";
+ ss << "doubleIndex++;\n";
+ }
+ ss << " }\n";
+ ss << " for (int i = ";
+ if (!pCurDVR->IsStartFixed() && pCurDVR->IsEndFixed())
+ {
+ ss << "gid0 + loop *" << unrollSize << "; i < ";
+ ss << nCurWindowSize << "; i++)\n";
+ }
+ else if (pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << "0 + loop *" << unrollSize << "; i < gid0+";
+ ss << nCurWindowSize << "; i++)\n";
+ }
+ else
+ {
+ ss << "0 + loop *" << unrollSize << "; i < ";
+ ss << nCurWindowSize << "; i++)\n";
+ }
+ ss << " {\n";
+ if (!pCurDVR->IsStartFixed() && !pCurDVR->IsEndFixed())
+ {
+ ss << " int doubleIndex = i+gid0;\n";
+ }
+ else
+ {
+ ss << " int doubleIndex = i;\n";
+ }
+ ss << unrollstr.str();
+ ss << " }\n";
+}
+
+void Reduction::GenSlidingWindowFunction( outputstream& ss,
+ const std::string& sSymName, SubArguments& vSubArguments )
+{
+ GenerateFunctionDeclaration( sSymName, vSubArguments, ss );
+ ss << "{\n";
+ ss << "double tmp = " << GetBottom() << ";\n";
+ ss << "int gid0 = get_global_id(0);\n";
+ if (isAverage() || isMinOrMax())
+ ss << "int nCount = 0;\n";
+ ss << "double tmpBottom;\n";
+ unsigned i = vSubArguments.size();
+ while (i--)
+ {
+ if (NumericRange* NR = dynamic_cast<NumericRange*>(vSubArguments[i].get()))
+ {
+ bool needBody;
+ NR->GenReductionLoopHeader(ss, needBody);
+ if (!needBody)
+ continue;
+ }
+ else if (NumericRangeStringsToZero* NRS = dynamic_cast<NumericRangeStringsToZero*>(vSubArguments[i].get()))
+ {
+ bool needBody;
+ NRS->GenReductionLoopHeader(ss, needBody);
+ if (!needBody)
+ continue;
+ }
+ else if (ParallelNumericRange* PNR = dynamic_cast<ParallelNumericRange*>(vSubArguments[i].get()))
+ {
+ //did not handle yet
+ bool bNeedBody = false;
+ PNR->GenReductionLoopHeader(ss, mnResultSize, bNeedBody);
+ if (!bNeedBody)
+ continue;
+ }
+ else if (StringRange* SR = dynamic_cast<StringRange*>(vSubArguments[i].get()))
+ {
+ //did not handle yet
+ bool needBody;
+ SR->GenReductionLoopHeader(ss, needBody);
+ if (!needBody)
+ continue;
+ }
+ else
+ {
+ FormulaToken* pCur = vSubArguments[i]->GetFormulaToken();
+ if( pCur == nullptr || pCur->GetType() == formula::svDoubleVectorRef )
+ {
+ throw Unhandled(__FILE__, __LINE__);
+ }
+ ss << "{\n";
+ }
+ if (ocPush == vSubArguments[i]->GetFormulaToken()->GetOpCode())
+ {
+ bool bNanHandled = HandleNaNArgument(ss, i, vSubArguments);
+
+ ss << " tmpBottom = " << GetBottom() << ";\n";
+
+ if (!bNanHandled)
+ {
+ ss << " if (isnan(";
+ ss << vSubArguments[i]->GenSlidingWindowDeclRef();
+ ss << "))\n";
+ if (ZeroReturnZero())
+ ss << " return 0;\n";
+ else
+ {
+ ss << " tmp = ";
+ ss << Gen2("tmpBottom", "tmp") << ";\n";
+ }
+ ss << " else\n";
+ }
+ ss << " tmp = ";
+ ss << Gen2(vSubArguments[i]->GenSlidingWindowDeclRef(), "tmp");
+ ss << ";\n";
+ }
+ else
+ {
+ ss << " tmp = ";
+ ss << Gen2(vSubArguments[i]->GenSlidingWindowDeclRef(), "tmp");
+ ss << ";\n";
+ }
+ ss << "}\n";
+ }
+ if (isAverage())
+ ss <<
+ "if (nCount==0)\n"
+ " return CreateDoubleError(DivisionByZero);\n";
+ else if (isMinOrMax())
+ ss <<
+ "if (nCount==0)\n"
+ " return 0;\n";
+ ss << "return tmp";
+ if (isAverage())
+ ss << "/(double)nCount";
+ ss << ";\n}";
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/opbase.hxx b/sc/source/core/opencl/opbase.hxx
new file mode 100644
index 0000000000..04ebab1ee7
--- /dev/null
+++ b/sc/source/core/opencl/opbase.hxx
@@ -0,0 +1,525 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <clew/clew.h>
+#include <formula/token.hxx>
+#include <formula/types.hxx>
+#include <formula/vectortoken.hxx>
+#include <opencl/OpenCLZone.hxx>
+#include <sal/log.hxx>
+#include <memory>
+#include <set>
+#include <vector>
+#include "utils.hxx"
+
+struct ScCalcConfig;
+
+namespace sc::opencl {
+
+// FIXME: The idea that somebody would bother to (now and then? once a year? once a month?) manually
+// edit a source file and change the value of some #defined constant and run some ill-defined
+// "correctness test" is of course ludicrous. Either things are checked in normal unit tests, in
+// every 'make check', or not at all. The below comments are ridiculous.
+
+#define REDUCE_THRESHOLD 201 // set to 4 for correctness testing. priority 1
+#define UNROLLING_FACTOR 16 // set to 4 for correctness testing (if no reduce)
+
+
+class FormulaTreeNode;
+
+/// Exceptions
+
+/// Failed in parsing
+class UnhandledToken
+{
+public:
+ UnhandledToken( const char* m, std::string fn, int ln );
+
+ std::string mMessage;
+ std::string mFile;
+ int mLineNumber;
+};
+
+/// Failed in marshaling
+class OpenCLError
+{
+public:
+ OpenCLError( std::string function, cl_int error, std::string file, int line );
+
+ std::string mFunction;
+ cl_int mError;
+ std::string mFile;
+ int mLineNumber;
+};
+
+/// Inconsistent state
+class Unhandled
+{
+public:
+ Unhandled( std::string fn, int ln );
+
+ std::string mFile;
+ int mLineNumber;
+};
+
+class InvalidParameterCount
+{
+public:
+ InvalidParameterCount( int parameterCount, std::string file, int ln );
+
+ int mParameterCount;
+ std::string mFile;
+ int const mLineNumber;
+};
+
+// Helper macros to be used in code emitting OpenCL code for Calc functions.
+// Require the vSubArguments parameter.
+#define CHECK_PARAMETER_COUNT(min, max) \
+ do { \
+ const int count = vSubArguments.size(); \
+ if( count < ( min ) || count > ( max )) \
+ throw InvalidParameterCount( count, __FILE__, __LINE__ ); \
+ } while( false )
+#define CHECK_PARAMETER_COUNT_MIN(min) \
+ do { \
+ const int count = vSubArguments.size(); \
+ if( count < ( min )) \
+ throw InvalidParameterCount( count, __FILE__, __LINE__ ); \
+ } while( false )
+#define CHECK_PARAMETER_DOUBLEVECTORREF(arg) \
+ do { \
+ formula::FormulaToken *token = vSubArguments[arg]->GetFormulaToken(); \
+ if (token == nullptr || token->GetType() != formula::svDoubleVectorRef) \
+ throw Unhandled(__FILE__, __LINE__); \
+ } while( false )
+
+typedef std::shared_ptr<FormulaTreeNode> FormulaTreeNodeRef;
+
+class FormulaTreeNode
+{
+public:
+ explicit FormulaTreeNode( const formula::FormulaToken* ft ) : mpCurrentFormula(ft)
+ {
+ Children.reserve(8);
+ }
+ std::vector<FormulaTreeNodeRef> Children;
+ formula::FormulaToken* GetFormulaToken() const
+ {
+ return const_cast<formula::FormulaToken*>(mpCurrentFormula.get());
+ }
+
+private:
+ formula::FormulaConstTokenRef mpCurrentFormula;
+};
+
+/// (Partially) abstract base class for an operand
+class DynamicKernelArgument
+{
+public:
+ /// delete copy constructor
+ DynamicKernelArgument( const DynamicKernelArgument& ) = delete;
+
+ /// delete copy-assignment operator
+ const DynamicKernelArgument& operator=( const DynamicKernelArgument& ) = delete;
+
+ DynamicKernelArgument( const ScCalcConfig& config, std::string s, FormulaTreeNodeRef ft );
+ virtual ~DynamicKernelArgument() {}
+
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const = 0;
+
+ /// When declared as input to a sliding window function
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const = 0;
+
+ /// When referenced in a sliding window function
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const = 0;
+
+ /// Create buffer and pass the buffer to a given kernel
+ virtual size_t Marshal( cl_kernel, int, int, cl_program ) = 0;
+
+ virtual size_t GetWindowSize() const = 0;
+
+ /// When Mix, it will be called
+ virtual std::string GenDoubleSlidingWindowDeclRef( bool = false ) const;
+
+ /// When Mix, it will be called
+ virtual std::string GenStringSlidingWindowDeclRef( bool = false ) const;
+
+ /// Will generate value saying whether the value is a string.
+ virtual std::string GenIsString( bool = false ) const { return "false"; }
+
+ /// Generate use/references to the argument
+ virtual void GenDeclRef( outputstream& ss ) const;
+
+ virtual void GenSlidingWindowFunction( outputstream& );
+ formula::FormulaToken* GetFormulaToken() const;
+ virtual std::string DumpOpName() const;
+ virtual void DumpInlineFun( std::set<std::string>&, std::set<std::string>& ) const;
+ const std::string& GetName() const;
+ virtual bool NeedParallelReduction() const;
+ /// If there's actually no argument, i.e. it expands to no code.
+ virtual bool IsEmpty() const { return false; }
+
+ static void ClearStringIds();
+
+protected:
+ const ScCalcConfig& mCalcConfig;
+ std::string mSymName;
+ FormulaTreeNodeRef mFormulaTree;
+ static int GetStringId( const rtl_uString* string );
+};
+
+typedef std::shared_ptr<DynamicKernelArgument> DynamicKernelArgumentRef;
+
+/// Holds an input (read-only) argument reference to a SingleVectorRef.
+/// or a DoubleVectorRef for non-sliding-window argument of complex functions
+/// like SumOfProduct
+/// In most of the cases the argument is introduced
+/// by a Push operation in the given RPN.
+class VectorRef : public DynamicKernelArgument
+{
+public:
+ VectorRef( const ScCalcConfig& config, const std::string& s, const FormulaTreeNodeRef& ft, int index = 0 );
+ virtual ~VectorRef() override;
+
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override;
+ /// When declared as input to a sliding window function
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override;
+
+ /// When referenced in a sliding window function
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const override;
+
+ /// Create buffer and pass the buffer to a given kernel
+ virtual size_t Marshal( cl_kernel, int, int, cl_program ) override;
+
+ virtual void GenSlidingWindowFunction( outputstream& ) override;
+ virtual size_t GetWindowSize() const override;
+ virtual std::string DumpOpName() const override;
+ virtual void DumpInlineFun( std::set<std::string>&, std::set<std::string>& ) const override;
+ const std::string& GetName() const;
+ cl_mem GetCLBuffer() const;
+ virtual bool NeedParallelReduction() const override;
+
+protected:
+ // Used by marshaling
+ cl_mem mpClmem;
+ // index in multiple double vector refs that have multiple ranges
+ const int mnIndex;
+ // Makes Marshall convert strings to 0 values.
+ bool forceStringsToZero;
+ // Used for storing when the data needs to be modified before sending to OpenCL.
+ std::vector< double > dataBuffer;
+};
+
+// Sets VectorRef::forceStringsToZero.
+class VectorRefStringsToZero : public VectorRef
+{
+public:
+ VectorRefStringsToZero( const ScCalcConfig& config, const std::string& s, const FormulaTreeNodeRef& ft, int index = 0 );
+};
+
+/// A vector of strings
+class DynamicKernelStringArgument : public VectorRef
+{
+public:
+ DynamicKernelStringArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft, int index = 0 ) :
+ VectorRef(config, s, ft, index) { }
+
+ virtual void GenSlidingWindowFunction( outputstream& ) override { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ ss << "__global double *" << mSymName;
+ }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ DynamicKernelStringArgument::GenDecl(ss);
+ }
+ virtual std::string GenIsString( bool = false ) const override;
+ virtual size_t Marshal( cl_kernel, int, int, cl_program ) override;
+};
+
+/// Arguments that are actually compile-time constants
+class DynamicKernelConstantArgument : public DynamicKernelArgument
+{
+public:
+ DynamicKernelConstantArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft ) :
+ DynamicKernelArgument(config, s, ft) { }
+ /// Generate declaration
+ virtual void GenDecl( outputstream& ss ) const override
+ {
+ ss << "double " << mSymName;
+ }
+ virtual void GenDeclRef( outputstream& ss ) const override
+ {
+ ss << mSymName;
+ }
+ virtual void GenSlidingWindowDecl( outputstream& ss ) const override
+ {
+ GenDecl(ss);
+ }
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const override
+ {
+ if (GetFormulaToken()->GetType() != formula::svDouble)
+ throw Unhandled(__FILE__, __LINE__);
+ return mSymName;
+ }
+ virtual size_t GetWindowSize() const override
+ {
+ return 1;
+ }
+ virtual double GetDouble() const
+ {
+ formula::FormulaToken* Tok = GetFormulaToken();
+ if (Tok->GetType() != formula::svDouble)
+ throw Unhandled(__FILE__, __LINE__);
+ return Tok->GetDouble();
+ }
+ /// Create buffer and pass the buffer to a given kernel
+ virtual size_t Marshal( cl_kernel k, int argno, int, cl_program ) override
+ {
+ OpenCLZone zone;
+ double tmp = GetDouble();
+ // Pass the scalar result back to the rest of the formula kernel
+ SAL_INFO("sc.opencl", "Kernel " << k << " arg " << argno << ": double: " << preciseFloat( tmp ));
+ cl_int err = clSetKernelArg(k, argno, sizeof(double), static_cast<void*>(&tmp));
+ if (CL_SUCCESS != err)
+ throw OpenCLError("clSetKernelArg", err, __FILE__, __LINE__);
+ return 1;
+ }
+};
+
+// Constant 0 argument when a string is forced to zero.
+class DynamicKernelStringToZeroArgument : public DynamicKernelConstantArgument
+{
+public:
+ DynamicKernelStringToZeroArgument( const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft ) :
+ DynamicKernelConstantArgument(config, s, ft) { }
+ virtual std::string GenSlidingWindowDeclRef( bool = false ) const override
+ {
+ return mSymName;
+ }
+ virtual double GetDouble() const override { return 0; }
+};
+
+
+/// Abstract class for code generation
+class OpBase
+{
+public:
+ // FIXME: What exactly is this? It seems to be a starting value for some calculations
+ // (1 for OpMul, MAXFLOAT for OpMin), but it's often used pointlessly and sometimes
+ // even incorrectly (default value for when the cell is empty).
+ virtual std::string GetBottom() { return "";};
+ virtual std::string Gen2( const std::string&/*lhs*/,
+ const std::string&/*rhs*/ ) const { return "";}
+ static std::string Gen( std::vector<std::string>& /*argVector*/ ) { return "";};
+ virtual std::string BinFuncName() const { return "";};
+ virtual void BinInlineFun( std::set<std::string>&,
+ std::set<std::string>& ) { }
+ virtual bool takeString() const = 0;
+ virtual bool takeNumeric() const = 0;
+ // Whether DoubleRef containing more than one column is handled properly.
+ virtual bool canHandleMultiVector() const { return false; }
+ //Continue process 'Zero' or Not(like OpMul, not continue process when meet
+ // 'Zero'
+ virtual bool ZeroReturnZero() { return false;}
+ // For use with COUNTA() etc, input strings will be converted to 0 in data.
+ virtual bool forceStringsToZero() const { return false; }
+ virtual ~OpBase() { }
+};
+
+class SlidingFunctionBase : public OpBase
+{
+public:
+ typedef std::vector<DynamicKernelArgumentRef> SubArguments;
+ virtual void GenSlidingWindowFunction( outputstream&,
+ const std::string&, SubArguments& ) = 0;
+protected:
+ // This enum controls how the generated code will handle empty cells in ranges.
+ enum EmptyArgType
+ {
+ EmptyIsZero, // empty cells become 0.0
+ EmptyIsNan, // empty cells become NAN, use isnan() to check in code
+ SkipEmpty // empty cells will be skipped
+ };
+ // This enum controls whether the generated code will also include variable
+ // <name>_is_string that will be set depending on the value type.
+ enum GenerateArgTypeType
+ {
+ DoNotGenerateArgType,
+ GenerateArgType
+ };
+ void GenerateFunctionDeclaration( const std::string& sSymName,
+ SubArguments& vSubArguments, outputstream& ss );
+ // Generate code for "double <name> = <value>;" from vSubArguments, svDoubleVectorRef is not supported.
+ void GenerateArg( const char* name, int arg, SubArguments& vSubArguments, outputstream& ss,
+ EmptyArgType empty = EmptyIsZero, GenerateArgTypeType generateType = DoNotGenerateArgType );
+ // overload, variable will be named "arg<arg>"
+ void GenerateArg( int arg, SubArguments& vSubArguments, outputstream& ss,
+ EmptyArgType empty = EmptyIsZero, GenerateArgTypeType generateType = DoNotGenerateArgType );
+ // generate code for "double <name> = <value>;" from vSubArguments, if it exists,
+ // otherwise set to <def>
+ void GenerateArgWithDefault( const char* name, int arg, double def, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty = EmptyIsZero );
+ // Generate code that will handle all arguments firstArg-lastArg (zero-based, inclusive),
+ // including range arguments (svDoubleVectorRef) and each value will be processed by 'code',
+ // value will be named "arg".
+ static void GenerateRangeArgs( int firstArg, int lastArg, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code );
+ // overload, handle all arguments
+ static void GenerateRangeArgs( SubArguments& vSubArguments, outputstream& ss,
+ EmptyArgType empty, const char* code );
+ // overload, handle the given argument
+ static void GenerateRangeArg( int arg, SubArguments& vSubArguments, outputstream& ss,
+ EmptyArgType empty, const char* code );
+ // Overload.
+ // Both arguments must be svDoubleRef of the same size.
+ // If 'firstElementDiff' is set, the loop start will be offset by '+ firstElementDiff'.
+ void GenerateRangeArg( int arg1, int arg2, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code, const char* firstElementDiff = nullptr );
+ // Generate code that will handle the given two arguments in one loop where n-th element of arg1 and arg2
+ // will be handled at the same time, named 'arg1' and 'arg2'.
+ // Both arguments must be svDoubleRef of the same size.
+ // If 'firstElementDiff' is set, the loop start will be offset by '+ firstElementDiff'.
+ static void GenerateRangeArgPair( int arg1, int arg2, SubArguments& vSubArguments,
+ outputstream& ss, EmptyArgType empty, const char* code, const char* firstElementDiff = nullptr );
+ // Generate code for "double <name> = range[<element>]" from vSubArguments.
+ // The argument must be svDoubleRef.
+ static void GenerateRangeArgElement( const char* name, int arg, const char* element,
+ SubArguments& vSubArguments, outputstream& ss, EmptyArgType empty );
+ static void GenerateDoubleVectorLoopHeader( outputstream& ss,
+ const formula::DoubleVectorRefToken* pDVR, const char* firstElementDiff );
+};
+
+class Normal : public SlidingFunctionBase
+{
+public:
+ virtual void GenSlidingWindowFunction( outputstream& ss,
+ const std::string& sSymName, SubArguments& vSubArguments ) override;
+ virtual bool takeString() const override { return false; }
+ virtual bool takeNumeric() const override { return true; }
+};
+
+class CheckVariables : public Normal
+{
+public:
+ static void GenTmpVariables( outputstream& ss, const SubArguments& vSubArguments );
+ static void CheckSubArgumentIsNan( outputstream& ss,
+ SubArguments& vSubArguments, int argumentNum );
+ static void CheckAllSubArgumentIsNan( outputstream& ss,
+ SubArguments& vSubArguments );
+ // only check isnan
+ static void CheckSubArgumentIsNan2( outputstream& ss,
+ SubArguments& vSubArguments, int argumentNum, const std::string& p );
+ static void UnrollDoubleVector( outputstream& ss,
+ const outputstream& unrollstr, const formula::DoubleVectorRefToken* pCurDVR,
+ int nCurWindowSize );
+};
+
+class OpAverage;
+class OpCount;
+
+/// Handling a Double Vector that is used as a sliding window input
+/// to either a sliding window average or sum-of-products
+/// Generate a sequential loop for reductions
+template<class Base>
+class DynamicKernelSlidingArgument : public Base
+{
+public:
+ DynamicKernelSlidingArgument(const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft,
+ std::shared_ptr<SlidingFunctionBase> CodeGen, int index);
+ // Should only be called by SumIfs. Yikes!
+ virtual bool NeedParallelReduction() const;
+ virtual void GenSlidingWindowFunction( outputstream& ) { }
+
+ std::string GenSlidingWindowDeclRef( bool nested = false ) const;
+ /// Controls how the elements in the DoubleVectorRef are traversed
+ size_t GenReductionLoopHeader( outputstream& ss, bool& needBody );
+
+ size_t GetArrayLength() const { return mpDVR->GetArrayLength(); }
+
+ size_t GetWindowSize() const { return mpDVR->GetRefRowSize(); }
+
+ bool GetStartFixed() const { return bIsStartFixed; }
+
+ bool GetEndFixed() const { return bIsEndFixed; }
+
+protected:
+ bool bIsStartFixed, bIsEndFixed;
+ const formula::DoubleVectorRefToken* mpDVR;
+ // from parent nodes
+ std::shared_ptr<SlidingFunctionBase> mpCodeGen;
+};
+
+/// Handling a Double Vector that is used as a sliding window input
+/// Performs parallel reduction based on given operator
+template<class Base>
+class ParallelReductionVectorRef : public Base
+{
+public:
+ ParallelReductionVectorRef(const ScCalcConfig& config, const std::string& s,
+ const FormulaTreeNodeRef& ft,
+ std::shared_ptr<SlidingFunctionBase> CodeGen, int index);
+ ~ParallelReductionVectorRef();
+
+ /// Emit the definition for the auxiliary reduction kernel
+ virtual void GenSlidingWindowFunction( outputstream& ss );
+ virtual std::string GenSlidingWindowDeclRef( bool ) const;
+ /// Controls how the elements in the DoubleVectorRef are traversed
+ size_t GenReductionLoopHeader( outputstream& ss, int nResultSize, bool& needBody );
+ virtual size_t Marshal( cl_kernel k, int argno, int w, cl_program mpProgram );
+ size_t GetArrayLength() const { return mpDVR->GetArrayLength(); }
+ size_t GetWindowSize() const { return mpDVR->GetRefRowSize(); }
+ bool GetStartFixed() const { return bIsStartFixed; }
+ bool GetEndFixed() const { return bIsEndFixed; }
+
+protected:
+ bool bIsStartFixed, bIsEndFixed;
+ const formula::DoubleVectorRefToken* mpDVR;
+ // from parent nodes
+ std::shared_ptr<SlidingFunctionBase> mpCodeGen;
+ // controls whether to invoke the reduction kernel during marshaling or not
+ cl_mem mpClmem2;
+};
+
+class Reduction : public SlidingFunctionBase
+{
+ int const mnResultSize;
+public:
+ explicit Reduction(int nResultSize) : mnResultSize(nResultSize) {}
+
+ typedef DynamicKernelSlidingArgument<VectorRef> NumericRange;
+ typedef DynamicKernelSlidingArgument<VectorRefStringsToZero> NumericRangeStringsToZero;
+ typedef DynamicKernelSlidingArgument<DynamicKernelStringArgument> StringRange;
+ typedef ParallelReductionVectorRef<VectorRef> ParallelNumericRange;
+
+ virtual bool HandleNaNArgument( outputstream&, unsigned, SubArguments& ) const
+ {
+ return false;
+ }
+
+ virtual void GenSlidingWindowFunction( outputstream& ss,
+ const std::string& sSymName, SubArguments& vSubArguments ) override;
+ virtual bool isAverage() const { return false; }
+ virtual bool isMinOrMax() const { return false; }
+ virtual bool takeString() const override { return false; }
+ virtual bool takeNumeric() const override { return true; }
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/utils.cxx b/sc/source/core/opencl/utils.cxx
new file mode 100644
index 0000000000..0d43d8c3ed
--- /dev/null
+++ b/sc/source/core/opencl/utils.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/.
+ */
+
+#include "utils.hxx"
+
+#include <cassert>
+#include <cmath>
+#include <cfloat>
+#include <limits>
+#include <sstream>
+
+namespace sc
+{
+namespace opencl
+{
+namespace
+{
+#ifdef SAL_LOG_INFO
+class outputstream_num_put : public std::num_put<char>
+{
+protected:
+ virtual iter_type do_put(iter_type s, std::ios_base&, char_type, double v) const override
+ {
+ std::string str = preciseFloat(v);
+ return std::copy(str.begin(), str.end(), s);
+ }
+ virtual iter_type do_put(iter_type, std::ios_base&, char_type, long double) const override
+ {
+ abort(); // we do not use these
+ }
+ using std::num_put<char>::do_put;
+};
+#endif
+} // namespace
+
+outputstream::outputstream()
+{
+ precision(DECIMAL_DIG);
+ setf(std::ios::showpoint);
+#ifdef SAL_LOG_INFO
+ // Calling precision() makes the output have trailing insignificant zeroes, which
+ // makes reading the source annoying. So override this stream's floating output
+ // handling to force usage of our preciseFloat(), which has a saner output.
+ imbue(std::locale(std::locale("C"), new outputstream_num_put));
+#endif
+}
+
+#undef stringstream
+std::string preciseFloat(double f)
+{
+ std::stringstream stream;
+ stream.precision(std::numeric_limits<double>::digits10 + 1);
+ stream.setf(std::ios::showpoint);
+ stream << f;
+ std::string str = stream.str();
+ size_t end = str.find('e');
+ if (end == std::string::npos)
+ end = str.size();
+ for (size_t pos = end - 1; pos > 0; --pos)
+ {
+ if (str[pos] != '0')
+ {
+ if (str[pos] == '.')
+ {
+ ++pos;
+ if (pos == end) // 10. without trailing 0
+ {
+ return str + '0';
+ }
+ }
+ ++pos;
+ assert(pos <= end);
+ str.resize(std::copy(str.begin() + end, str.end(), str.begin() + pos) - str.begin());
+ return str;
+ }
+ }
+ abort();
+}
+}
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/utils.hxx b/sc/source/core/opencl/utils.hxx
new file mode 100644
index 0000000000..a0c8ebfd72
--- /dev/null
+++ b/sc/source/core/opencl/utils.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/.
+ */
+
+#pragma once
+
+#include <sstream>
+
+namespace sc::opencl
+{
+// The way ostream handles floating point output is pretty broken for our usecase.
+// The default precision (6) is low, leading to possible precision loss of constant
+// float arguments in the generated code. Moreover numbers without a fractional
+// part will be printed as e.g. '2', making them integers in the generated code
+// (causing problems e.g. with floor() overloads being ambiguous). The std::showpoint
+// manipulator forces a decimal point, but then all numbers will be printed
+// at the maximum precision even with insignificant trailing 0's, making the numbers
+// a pain to read.
+// And these flags need to be set on every std::stringstream instance we use,
+// which may be easy to omit.
+// So as a solution to this:
+// - provide our own wrapper class
+// - prohibit direct usage of std::stringstream
+
+class outputstream : public std::stringstream
+{
+private:
+ typedef std::stringstream base;
+
+public:
+ outputstream();
+};
+
+// Returns a string containing the string representation of the given floating pointer number.
+// Unlike printf/iostream, this tries to be as precise and representative as possible
+// (it always includes a decimal point, and it uses the highest precision necessary/possible).
+std::string preciseFloat(double f);
+std::string preciseFloat(long double f) = delete; // we do not use these
+
+#define stringstream do_not_use_std_stringstream
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/addincfg.cxx b/sc/source/core/tool/addincfg.cxx
new file mode 100644
index 0000000000..3f09b7defd
--- /dev/null
+++ b/sc/source/core/tool/addincfg.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <sal/log.hxx>
+
+#include <global.hxx>
+#include <addincol.hxx>
+#include <addincfg.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+
+using namespace com::sun::star;
+
+constexpr OUStringLiteral CFGPATH_ADDINS = u"Office.CalcAddIns/AddInInfo";
+
+ScAddInCfg::ScAddInCfg()
+ : ConfigItem(CFGPATH_ADDINS)
+{
+ EnableNotification({ {} });
+}
+
+void ScAddInCfg::ImplCommit() { SAL_WARN("sc", "ScAddInCfg shouldn't be modified"); }
+
+void ScAddInCfg::Notify(const uno::Sequence<OUString>&)
+{
+ // forget all add-in information, re-initialize when needed next time
+ ScGlobal::GetAddInCollection()->Clear();
+
+ // function list must also be rebuilt, but can't be modified while function
+ // autopilot is open (function list for autopilot is then still old)
+ if (SC_MOD()->GetCurRefDlgId() != SID_OPENDLG_FUNCTION)
+ ScGlobal::ResetFunctionList();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/addincol.cxx b/sc/source/core/tool/addincol.cxx
new file mode 100644
index 0000000000..a18800781b
--- /dev/null
+++ b/sc/source/core/tool/addincol.cxx
@@ -0,0 +1,1731 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <i18nlangtag/languagetag.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/objsh.hxx>
+#include <unotools/charclass.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/reflection/XIdlClass.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/MethodConcept.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/sheet/XCompatibilityNames.hpp>
+#include <com/sun/star/sheet/NoConvergenceException.hpp>
+#include <com/sun/star/sheet/XAddIn.hpp>
+#include <com/sun/star/sheet/XVolatileResult.hpp>
+
+#include <addincol.hxx>
+#include <addinhelpid.hxx>
+#include <scmatrix.hxx>
+#include <formula/errorcodes.hxx>
+#include <formula/funcvarargs.h>
+#include <optutil.hxx>
+#include <addincfg.hxx>
+#include <scmod.hxx>
+#include <rangeseq.hxx>
+#include <funcdesc.hxx>
+#include <svl/sharedstring.hxx>
+#include <formulaopt.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+#define SC_CALLERPOS_NONE (-1)
+
+ScUnoAddInFuncData::ScUnoAddInFuncData( const OUString& rNam, const OUString& rLoc,
+ OUString aDesc,
+ sal_uInt16 nCat, OUString sHelp,
+ uno::Reference<reflection::XIdlMethod> xFunc,
+ uno::Any aO,
+ tools::Long nAC, const ScAddInArgDesc* pAD,
+ tools::Long nCP ) :
+ aOriginalName( rNam ),
+ aLocalName( rLoc ),
+ aUpperName( rNam ),
+ aUpperLocal( rLoc ),
+ aDescription(std::move( aDesc )),
+ xFunction(std::move( xFunc )),
+ aObject(std::move( aO )),
+ nArgCount( nAC ),
+ nCallerPos( nCP ),
+ nCategory( nCat ),
+ sHelpId(std::move( sHelp )),
+ bCompInitialized( false )
+{
+ if ( nArgCount )
+ {
+ pArgDescs.reset( new ScAddInArgDesc[nArgCount] );
+ for (tools::Long i=0; i<nArgCount; i++)
+ pArgDescs[i] = pAD[i];
+ }
+
+ aUpperName = aUpperName.toAsciiUpperCase(); // programmatic name
+ aUpperLocal = ScGlobal::getCharClass().uppercase(aUpperLocal);
+}
+
+ScUnoAddInFuncData::~ScUnoAddInFuncData()
+{
+}
+
+const ::std::vector<ScUnoAddInFuncData::LocalizedName>& ScUnoAddInFuncData::GetCompNames() const
+{
+ if ( !bCompInitialized )
+ {
+ // read sequence of compatibility names on demand
+
+ uno::Reference<sheet::XAddIn> xAddIn;
+ if ( aObject >>= xAddIn )
+ {
+ uno::Reference<sheet::XCompatibilityNames> xComp( xAddIn, uno::UNO_QUERY );
+ if ( xComp.is() && xFunction.is() )
+ {
+ OUString aMethodName = xFunction->getName();
+ const uno::Sequence< sheet::LocalizedName> aCompNames( xComp->getCompatibilityNames( aMethodName ));
+ maCompNames.clear();
+ for (const sheet::LocalizedName& rCompName : aCompNames)
+ {
+ maCompNames.emplace_back(
+ LanguageTag::convertToBcp47( rCompName.Locale, false),
+ rCompName.Name);
+ }
+ }
+ }
+
+ bCompInitialized = true; // also if not successful
+ }
+ return maCompNames;
+}
+
+void ScUnoAddInFuncData::SetCompNames( ::std::vector< ScUnoAddInFuncData::LocalizedName >&& rNew )
+{
+ OSL_ENSURE( !bCompInitialized, "SetCompNames after initializing" );
+
+ maCompNames = std::move(rNew);
+
+ bCompInitialized = true;
+}
+
+void ScUnoAddInFuncData::SetEnglishName( const OUString& rEnglishName )
+{
+ if (!rEnglishName.isEmpty())
+ aUpperEnglish = ScCompiler::GetCharClassEnglish()->uppercase(rEnglishName);
+ else
+ {
+ // A dumb fallback to not have an empty name, mainly just for the
+ // assignment to ScFuncDesc::mxFuncName for the Function Wizard and
+ // formula input tooltips.
+ aUpperEnglish = aUpperLocal;
+ }
+}
+
+bool ScUnoAddInFuncData::GetExcelName( const LanguageTag& rDestLang, OUString& rRetExcelName, bool bFallbackToAny ) const
+{
+ const ::std::vector<LocalizedName>& rCompNames = GetCompNames();
+ if ( !rCompNames.empty() )
+ {
+ const OUString& aSearch( rDestLang.getBcp47());
+
+ // First, check exact match without fallback overhead.
+ ::std::vector<LocalizedName>::const_iterator itNames = std::find_if(rCompNames.begin(), rCompNames.end(),
+ [&aSearch](const LocalizedName& rName) { return rName.maLocale == aSearch; });
+ if (itNames != rCompNames.end())
+ {
+ rRetExcelName = (*itNames).maName;
+ return true;
+ }
+
+ // Second, try match of fallback search with fallback locales,
+ // appending also 'en-US' and 'en' to search if not queried.
+ ::std::vector< OUString > aFallbackSearch( rDestLang.getFallbackStrings( true));
+ if (aSearch != "en-US")
+ {
+ aFallbackSearch.emplace_back("en-US");
+ if (aSearch != "en")
+ {
+ aFallbackSearch.emplace_back("en");
+ }
+ }
+ for (const auto& rSearch : aFallbackSearch)
+ {
+ for (const auto& rCompName : rCompNames)
+ {
+ // We checked already the full tag, start with second.
+ ::std::vector< OUString > aFallbackLocales( LanguageTag( rCompName.maLocale).getFallbackStrings( false));
+ if (std::find(aFallbackLocales.begin(), aFallbackLocales.end(), rSearch) != aFallbackLocales.end())
+ {
+ rRetExcelName = rCompName.maName;
+ return true;
+ }
+ }
+ }
+
+ if (bFallbackToAny)
+ {
+ // Third, last resort, use first (default) entry.
+ rRetExcelName = rCompNames[0].maName;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ScUnoAddInFuncData::SetFunction( const uno::Reference< reflection::XIdlMethod>& rNewFunc, const uno::Any& rNewObj )
+{
+ xFunction = rNewFunc;
+ aObject = rNewObj;
+}
+
+void ScUnoAddInFuncData::SetArguments( tools::Long nNewCount, const ScAddInArgDesc* pNewDescs )
+{
+ nArgCount = nNewCount;
+ if ( nArgCount )
+ {
+ pArgDescs.reset( new ScAddInArgDesc[nArgCount] );
+ for (tools::Long i=0; i<nArgCount; i++)
+ pArgDescs[i] = pNewDescs[i];
+ }
+ else
+ pArgDescs.reset();
+}
+
+void ScUnoAddInFuncData::SetCallerPos( tools::Long nNewPos )
+{
+ nCallerPos = nNewPos;
+}
+
+ScUnoAddInCollection::ScUnoAddInCollection() :
+ nFuncCount( 0 ),
+ bInitialized( false )
+{
+}
+
+ScUnoAddInCollection::~ScUnoAddInCollection()
+{
+}
+
+void ScUnoAddInCollection::Clear()
+{
+ pExactHashMap.reset();
+ pNameHashMap.reset();
+ pLocalHashMap.reset();
+ pEnglishHashMap.reset();
+ ppFuncData.reset();
+ nFuncCount = 0;
+
+ bInitialized = false;
+}
+
+void ScUnoAddInCollection::Initialize()
+{
+ OSL_ENSURE( !bInitialized, "Initialize twice?" );
+
+ uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
+ uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
+ if ( xEnAc.is() )
+ {
+ uno::Reference<container::XEnumeration> xEnum =
+ xEnAc->createContentEnumeration( "com.sun.star.sheet.AddIn" );
+ if ( xEnum.is() )
+ {
+ // loop through all AddIns
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Any aAddInAny = xEnum->nextElement();
+
+ try
+ {
+ uno::Reference<uno::XInterface> xIntFac;
+ aAddInAny >>= xIntFac;
+ if ( xIntFac.is() )
+ {
+ // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
+ // passing the context to the component
+
+ uno::Reference<uno::XInterface> xInterface;
+ uno::Reference<uno::XComponentContext> xCtx(
+ comphelper::getComponentContext(xManager));
+ uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
+ if (xCFac.is())
+ {
+ xInterface = xCFac->createInstanceWithContext(xCtx);
+ if (xInterface.is())
+ ReadFromAddIn( xInterface );
+ }
+
+ if (!xInterface.is())
+ {
+ uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
+ if ( xFac.is() )
+ {
+ xInterface = xFac->createInstance();
+ if (xInterface.is())
+ ReadFromAddIn( xInterface );
+ }
+ }
+ }
+ } catch ( const uno::Exception& ) {
+ SAL_WARN ( "sc", "Failed to initialize create instance of sheet.AddIn" );
+ }
+ }
+ }
+ }
+
+ // ReadConfiguration is called after looking at the AddIn implementations.
+ // Duplicated are skipped (by using the service information, they don't have to be updated again
+ // when argument information is needed).
+ ReadConfiguration();
+
+ bInitialized = true; // with or without functions
+}
+
+static sal_uInt16 lcl_GetCategory( std::u16string_view rName )
+{
+ static const char* aFuncNames[SC_FUNCGROUP_COUNT] =
+ {
+ // array index = ID - 1 (ID starts at 1)
+ // all upper case
+ "Database", // ID_FUNCTION_GRP_DATABASE
+ "Date&Time", // ID_FUNCTION_GRP_DATETIME
+ "Financial", // ID_FUNCTION_GRP_FINANCIAL
+ "Information", // ID_FUNCTION_GRP_INFO
+ "Logical", // ID_FUNCTION_GRP_LOGIC
+ "Mathematical", // ID_FUNCTION_GRP_MATH
+ "Matrix", // ID_FUNCTION_GRP_MATRIX
+ "Statistical", // ID_FUNCTION_GRP_STATISTIC
+ "Spreadsheet", // ID_FUNCTION_GRP_TABLE
+ "Text", // ID_FUNCTION_GRP_TEXT
+ "Add-In" // ID_FUNCTION_GRP_ADDINS
+ };
+ for (sal_uInt16 i=0; i<SC_FUNCGROUP_COUNT; i++)
+ if ( o3tl::equalsAscii( rName, aFuncNames[i] ) )
+ return i+1; // IDs start at 1
+
+ return ID_FUNCTION_GRP_ADDINS; // if not found, use Add-In group
+}
+
+constexpr OUStringLiteral CFGPATH_ADDINS = u"Office.CalcAddIns/AddInInfo";
+constexpr OUStringLiteral CFGSTR_ADDINFUNCTIONS = u"AddInFunctions";
+
+#define CFG_FUNCPROP_DISPLAYNAME 0
+#define CFG_FUNCPROP_DESCRIPTION 1
+#define CFG_FUNCPROP_CATEGORY 2
+#define CFG_FUNCPROP_COUNT 3
+constexpr OUString CFGSTR_DISPLAYNAME = u"DisplayName"_ustr;
+constexpr OUString CFGSTR_DESCRIPTION = u"Description"_ustr;
+constexpr OUString CFGSTR_CATEGORY = u"Category"_ustr;
+// CategoryDisplayName is ignored for now
+
+constexpr OUStringLiteral CFGSTR_COMPATIBILITYNAME = u"CompatibilityName";
+constexpr OUStringLiteral CFGSTR_PARAMETERS = u"Parameters";
+
+void ScUnoAddInCollection::ReadConfiguration()
+{
+ // called only from Initialize
+
+ ScAddInCfg& rAddInConfig = SC_MOD()->GetAddInCfg();
+
+ // Additional, temporary config item for the display names and
+ // compatibility names.
+ ScLinkConfigItem aAllLocalesConfig( CFGPATH_ADDINS, ConfigItemMode::AllLocales );
+ // CommitLink is not used (only reading values)
+
+ const OUString sSlash('/');
+
+ // get the list of add-ins (services)
+ const uno::Sequence<OUString> aServiceNames = rAddInConfig.GetNodeNames( "" );
+
+ for ( const OUString& aServiceName : aServiceNames )
+ {
+ ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );
+
+ OUString aFunctionsPath(aServiceName + sSlash + CFGSTR_ADDINFUNCTIONS);
+
+ uno::Sequence<OUString> aFunctionNames = rAddInConfig.GetNodeNames( aFunctionsPath );
+ sal_Int32 nNewCount = aFunctionNames.getLength();
+
+ // allocate pointers
+
+ tools::Long nOld = nFuncCount;
+ nFuncCount = nNewCount+nOld;
+ if ( nOld )
+ {
+ std::unique_ptr<std::unique_ptr<ScUnoAddInFuncData>[]> ppNew(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
+ for (tools::Long i=0; i<nOld; i++)
+ ppNew[i] = std::move(ppFuncData[i]);
+ ppFuncData = std::move(ppNew);
+ }
+ else
+ ppFuncData.reset( new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount] );
+
+ //TODO: adjust bucket count?
+ if ( !pExactHashMap )
+ pExactHashMap.reset( new ScAddInHashMap );
+ if ( !pNameHashMap )
+ pNameHashMap.reset( new ScAddInHashMap );
+ if ( !pLocalHashMap )
+ pLocalHashMap.reset( new ScAddInHashMap );
+ if ( !pEnglishHashMap )
+ pEnglishHashMap.reset( new ScAddInHashMap );
+
+ //TODO: get the function information in a single call for all functions?
+
+ const OUString* pFuncNameArray = aFunctionNames.getConstArray();
+ for ( sal_Int32 nFuncPos = 0; nFuncPos < nNewCount; nFuncPos++ )
+ {
+ ppFuncData[nFuncPos+nOld] = nullptr;
+
+ // stored function name: (service name).(function)
+ OUString aFuncName = aServiceName + "." + pFuncNameArray[nFuncPos];
+
+ // skip the function if already known (read from old AddIn service)
+
+ if ( pExactHashMap->find( aFuncName ) == pExactHashMap->end() )
+ {
+ OUString aEnglishName;
+ OUString aLocalName;
+ OUString aDescription;
+ sal_uInt16 nCategory = ID_FUNCTION_GRP_ADDINS;
+
+ // get direct information on the function
+
+ OUString aFuncPropPath = aFunctionsPath + sSlash + pFuncNameArray[nFuncPos] + sSlash;
+
+ uno::Sequence<OUString> aFuncPropNames{
+ (aFuncPropPath + CFGSTR_DISPLAYNAME), // CFG_FUNCPROP_DISPLAYNAME
+ (aFuncPropPath + CFGSTR_DESCRIPTION), // CFG_FUNCPROP_DESCRIPTION
+ (aFuncPropPath + CFGSTR_CATEGORY)}; // CFG_FUNCPROP_CATEGORY
+
+ uno::Sequence<uno::Any> aFuncProperties = rAddInConfig.GetProperties( aFuncPropNames );
+ if ( aFuncProperties.getLength() == CFG_FUNCPROP_COUNT )
+ {
+ aFuncProperties[CFG_FUNCPROP_DISPLAYNAME] >>= aLocalName;
+ aFuncProperties[CFG_FUNCPROP_DESCRIPTION] >>= aDescription;
+
+ OUString aCategoryName;
+ aFuncProperties[CFG_FUNCPROP_CATEGORY] >>= aCategoryName;
+ nCategory = lcl_GetCategory( aCategoryName );
+ }
+
+ // get English display name
+
+ OUString aDisplayNamePath(aFuncPropPath + CFGSTR_DISPLAYNAME);
+ uno::Sequence<OUString> aDisplayNamePropNames( &aDisplayNamePath, 1 );
+
+ uno::Sequence<uno::Any> aDisplayNameProperties = aAllLocalesConfig.GetProperties( aDisplayNamePropNames );
+ if ( aDisplayNameProperties.getLength() == 1 )
+ {
+ uno::Sequence<beans::PropertyValue> aLocalEntries;
+ if ( aDisplayNameProperties[0] >>= aLocalEntries )
+ {
+ for ( const beans::PropertyValue& rConfig : std::as_const(aLocalEntries) )
+ {
+ // PropertyValue name is the locale ("convert" from
+ // string to canonicalize).
+ OUString aLocale( LanguageTag( rConfig.Name, true).getBcp47( false));
+ // PropertyValue value is the localized value (string in this case).
+ OUString aName;
+ rConfig.Value >>= aName;
+ // Accept 'en' and 'en-...' but prefer 'en-US'.
+ if (aLocale == "en-US" && !aName.isEmpty())
+ aEnglishName = aName;
+ else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
+ aEnglishName = aName;
+ }
+ }
+ }
+ bool bNeedEnglish = aEnglishName.isEmpty();
+
+ // get compatibility names
+
+ ::std::vector<ScUnoAddInFuncData::LocalizedName> aCompNames;
+
+ OUString aCompPath(aFuncPropPath + CFGSTR_COMPATIBILITYNAME);
+ uno::Sequence<OUString> aCompPropNames( &aCompPath, 1 );
+
+ uno::Sequence<uno::Any> aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames );
+ if ( aCompProperties.getLength() == 1 )
+ {
+ uno::Sequence<beans::PropertyValue> aLocalEntries;
+ if ( aCompProperties[0] >>= aLocalEntries )
+ {
+ for ( const beans::PropertyValue& rConfig : std::as_const(aLocalEntries) )
+ {
+ // PropertyValue name is the locale ("convert" from
+ // string to canonicalize).
+ OUString aLocale( LanguageTag( rConfig.Name, true).getBcp47( false));
+ // PropertyValue value is the localized value (string in this case).
+ OUString aName;
+ rConfig.Value >>= aName;
+ if (!aName.isEmpty())
+ {
+ aCompNames.emplace_back( aLocale, aName);
+ if (bNeedEnglish)
+ {
+ // Accept 'en' and 'en-...' but prefer 'en-US'.
+ if (aLocale == "en-US")
+ aEnglishName = aName;
+ else if (aEnglishName.isEmpty() && (aLocale == "en" || aLocale.startsWith("en-")))
+ aEnglishName = aName;
+ }
+ }
+ }
+ }
+ }
+
+ // get argument info
+
+ std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
+ tools::Long nVisibleCount = 0;
+
+ OUString aArgumentsPath(aFuncPropPath + CFGSTR_PARAMETERS);
+
+ const uno::Sequence<OUString> aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath );
+ sal_Int32 nArgumentCount = aArgumentNames.getLength();
+ if ( nArgumentCount )
+ {
+ // get DisplayName and Description for each argument
+ uno::Sequence<OUString> aArgPropNames( nArgumentCount * 2 );
+ OUString* pPropNameArray = aArgPropNames.getArray();
+
+ sal_Int32 nIndex = 0;
+ for ( const OUString& rArgName : aArgumentNames )
+ {
+ OUString aOneArgPath = aArgumentsPath + sSlash + rArgName + sSlash;
+
+ pPropNameArray[nIndex++] = aOneArgPath
+ + CFGSTR_DISPLAYNAME;
+ pPropNameArray[nIndex++] = aOneArgPath
+ + CFGSTR_DESCRIPTION;
+ }
+
+ uno::Sequence<uno::Any> aArgProperties = rAddInConfig.GetProperties( aArgPropNames );
+ if ( aArgProperties.getLength() == aArgPropNames.getLength() )
+ {
+ const OUString* pArgNameArray = aArgumentNames.getConstArray();
+ const uno::Any* pPropArray = aArgProperties.getConstArray();
+ OUString sDisplayName;
+ OUString sDescription;
+
+ ScAddInArgDesc aDesc;
+ aDesc.eType = SC_ADDINARG_NONE; // arg type is not in configuration
+ aDesc.bOptional = false;
+
+ nVisibleCount = nArgumentCount;
+ pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
+
+ nIndex = 0;
+ for ( sal_Int32 nArgument = 0; nArgument < nArgumentCount; nArgument++ )
+ {
+ pPropArray[nIndex++] >>= sDisplayName;
+ pPropArray[nIndex++] >>= sDescription;
+
+ aDesc.aInternalName = pArgNameArray[nArgument];
+ aDesc.aName = sDisplayName;
+ aDesc.aDescription = sDescription;
+
+ pVisibleArgs[nArgument] = aDesc;
+ }
+ }
+ }
+
+ OUString sHelpId = aHelpIdGenerator.GetHelpId( pFuncNameArray[nFuncPos] );
+
+ uno::Reference<reflection::XIdlMethod> xFunc; // remains empty
+ uno::Any aObject; // also empty
+
+ // create and insert into the array
+
+ ScUnoAddInFuncData* pData = new ScUnoAddInFuncData(
+ aFuncName, aLocalName, aDescription,
+ nCategory, sHelpId,
+ xFunc, aObject,
+ nVisibleCount, pVisibleArgs.get(), SC_CALLERPOS_NONE );
+
+ pData->SetCompNames( std::move(aCompNames) );
+
+ ppFuncData[nFuncPos+nOld].reset(pData);
+
+ pExactHashMap->emplace(
+ pData->GetOriginalName(),
+ pData );
+ pNameHashMap->emplace(
+ pData->GetUpperName(),
+ pData );
+ pLocalHashMap->emplace(
+ pData->GetUpperLocal(),
+ pData );
+
+ if (aEnglishName.isEmpty())
+ SAL_WARN("sc.core", "no English name for " << aLocalName << " " << aFuncName);
+ else
+ {
+ pEnglishHashMap->emplace(
+ ScCompiler::GetCharClassEnglish()->uppercase(aEnglishName),
+ pData );
+ }
+ pData->SetEnglishName(aEnglishName); // takes care of handling empty
+ }
+ }
+ }
+}
+
+void ScUnoAddInCollection::LoadComponent( const ScUnoAddInFuncData& rFuncData )
+{
+ const OUString& aFullName = rFuncData.GetOriginalName();
+ sal_Int32 nPos = aFullName.lastIndexOf( '.' );
+ if ( nPos <= 0 )
+ return;
+
+ OUString aServiceName = aFullName.copy( 0, nPos );
+
+ try
+ {
+ uno::Reference<lang::XMultiServiceFactory> xServiceFactory = comphelper::getProcessServiceFactory();
+ uno::Reference<uno::XInterface> xInterface( xServiceFactory->createInstance( aServiceName ) );
+
+ if (xInterface.is())
+ UpdateFromAddIn( xInterface, aServiceName );
+ }
+ catch (const uno::Exception &)
+ {
+ SAL_WARN ("sc", "Failed to create addin component '"
+ << aServiceName << "'");
+ }
+}
+
+bool ScUnoAddInCollection::GetExcelName( const OUString& rCalcName,
+ LanguageType eDestLang, OUString& rRetExcelName )
+{
+ const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName );
+ if ( pFuncData )
+ return pFuncData->GetExcelName( LanguageTag( eDestLang), rRetExcelName);
+ return false;
+}
+
+bool ScUnoAddInCollection::GetCalcName( const OUString& rExcelName, OUString& rRetCalcName )
+{
+ if (!bInitialized)
+ Initialize();
+
+ OUString aUpperCmp = ScGlobal::getCharClass().uppercase(rExcelName);
+
+ for (tools::Long i=0; i<nFuncCount; i++)
+ {
+ ScUnoAddInFuncData* pFuncData = ppFuncData[i].get();
+ if ( pFuncData )
+ {
+ const ::std::vector<ScUnoAddInFuncData::LocalizedName>& rNames = pFuncData->GetCompNames();
+ auto bFound = std::any_of(rNames.begin(), rNames.end(),
+ [&aUpperCmp](const ScUnoAddInFuncData::LocalizedName& rName) {
+ return ScGlobal::getCharClass().uppercase( rName.maName ) == aUpperCmp; });
+ if (bFound)
+ {
+ //TODO: store upper case for comparing?
+
+ // use the first function that has this name for any language
+ rRetCalcName = pFuncData->GetOriginalName();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool IsTypeName( std::u16string_view rName, const uno::Type& rType )
+{
+ return rName == rType.getTypeName();
+}
+
+static bool lcl_ValidReturnType( const uno::Reference<reflection::XIdlClass>& xClass )
+{
+ // this must match with ScUnoAddInCall::SetResult
+
+ if ( !xClass.is() ) return false;
+
+ switch (xClass->getTypeClass())
+ {
+ case uno::TypeClass_ANY: // variable type
+ case uno::TypeClass_ENUM: //TODO: ???
+ case uno::TypeClass_BOOLEAN:
+ case uno::TypeClass_CHAR:
+ case uno::TypeClass_BYTE:
+ case uno::TypeClass_SHORT:
+ case uno::TypeClass_UNSIGNED_SHORT:
+ case uno::TypeClass_LONG:
+ case uno::TypeClass_UNSIGNED_LONG:
+ case uno::TypeClass_FLOAT:
+ case uno::TypeClass_DOUBLE:
+ case uno::TypeClass_STRING:
+ return true; // values or string
+
+ case uno::TypeClass_INTERFACE:
+ {
+ // return type XInterface may contain a XVolatileResult
+ //TODO: XIdlClass needs getType() method!
+
+ OUString sName = xClass->getName();
+ return (
+ IsTypeName( sName, cppu::UnoType<sheet::XVolatileResult>::get()) ||
+ IsTypeName( sName, cppu::UnoType<uno::XInterface>::get()) );
+ }
+
+ default:
+ {
+ // nested sequences for arrays
+ //TODO: XIdlClass needs getType() method!
+
+ OUString sName = xClass->getName();
+ return (
+ IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) ||
+ IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) ||
+ IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) ||
+ IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) );
+ }
+ }
+}
+
+static ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>& xClass )
+{
+ if (!xClass.is())
+ return SC_ADDINARG_NONE;
+
+ uno::TypeClass eType = xClass->getTypeClass();
+
+ if ( eType == uno::TypeClass_LONG ) //TODO: other integer types?
+ return SC_ADDINARG_INTEGER;
+
+ if ( eType == uno::TypeClass_DOUBLE )
+ return SC_ADDINARG_DOUBLE;
+
+ if ( eType == uno::TypeClass_STRING )
+ return SC_ADDINARG_STRING;
+
+ //TODO: XIdlClass needs getType() method!
+ OUString sName = xClass->getName();
+
+ if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ))
+ return SC_ADDINARG_INTEGER_ARRAY;
+
+ if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ))
+ return SC_ADDINARG_DOUBLE_ARRAY;
+
+ if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ))
+ return SC_ADDINARG_STRING_ARRAY;
+
+ if (IsTypeName( sName, cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ))
+ return SC_ADDINARG_MIXED_ARRAY;
+
+ if (IsTypeName( sName, cppu::UnoType<uno::Any>::get()))
+ return SC_ADDINARG_VALUE_OR_ARRAY;
+
+ if (IsTypeName( sName, cppu::UnoType<table::XCellRange>::get()))
+ return SC_ADDINARG_CELLRANGE;
+
+ if (IsTypeName( sName, cppu::UnoType<beans::XPropertySet>::get()))
+ return SC_ADDINARG_CALLER;
+
+ if (IsTypeName( sName, cppu::UnoType<uno::Sequence<uno::Any>>::get() ))
+ return SC_ADDINARG_VARARGS;
+
+ return SC_ADDINARG_NONE;
+}
+
+void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface )
+{
+ uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY );
+ uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY );
+ if ( !(xAddIn.is() && xName.is()) )
+ return;
+
+ // Even if GetUseEnglishFunctionName() would return true, do not set the
+ // locale to en-US to get English function names as that also would mix in
+ // English descriptions and parameter names. Also, setting a locale will
+ // reinitialize the Add-In completely, so switching back and forth isn't a
+ // good idea either.
+ xAddIn->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());
+
+ // Instead, in a second run with 'en-US' obtain English names.
+ struct FuncNameData
+ {
+ OUString aFuncU;
+ ScUnoAddInFuncData* pData;
+ };
+ std::vector<FuncNameData> aFuncNameData;
+
+ OUString aServiceName( xName->getServiceName() );
+ ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );
+
+ //TODO: pass XIntrospection to ReadFromAddIn
+
+ uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference<beans::XIntrospection> xIntro = beans::theIntrospection::get( xContext );
+ uno::Any aObject;
+ aObject <<= xAddIn;
+ uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
+ if (!xAcc.is())
+ return;
+
+ uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
+ xAcc->getMethods( beans::MethodConcept::ALL );
+ tools::Long nNewCount = aMethods.getLength();
+ if ( !nNewCount )
+ return;
+
+ tools::Long nOld = nFuncCount;
+ nFuncCount = nNewCount+nOld;
+ if ( nOld )
+ {
+ std::unique_ptr<std::unique_ptr<ScUnoAddInFuncData>[]> ppNew(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
+ for (tools::Long i=0; i<nOld; i++)
+ ppNew[i] = std::move(ppFuncData[i]);
+ ppFuncData = std::move(ppNew);
+ }
+ else
+ ppFuncData.reset(new std::unique_ptr<ScUnoAddInFuncData>[nFuncCount]);
+
+ //TODO: adjust bucket count?
+ if ( !pExactHashMap )
+ pExactHashMap.reset( new ScAddInHashMap );
+ if ( !pNameHashMap )
+ pNameHashMap.reset( new ScAddInHashMap );
+ if ( !pLocalHashMap )
+ pLocalHashMap.reset( new ScAddInHashMap );
+ if ( !pEnglishHashMap )
+ pEnglishHashMap.reset( new ScAddInHashMap );
+
+ const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
+ for (tools::Long nFuncPos=0; nFuncPos<nNewCount; nFuncPos++)
+ {
+ ppFuncData[nFuncPos+nOld] = nullptr;
+
+ uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
+ if (xFunc.is())
+ {
+ // leave out internal functions
+ uno::Reference<reflection::XIdlClass> xClass =
+ xFunc->getDeclaringClass();
+ bool bSkip = true;
+ if ( xClass.is() )
+ {
+ //TODO: XIdlClass needs getType() method!
+ OUString sName = xClass->getName();
+ bSkip = (
+ IsTypeName( sName,
+ cppu::UnoType<uno::XInterface>::get()) ||
+ IsTypeName( sName,
+ cppu::UnoType<lang::XServiceName>::get()) ||
+ IsTypeName( sName,
+ cppu::UnoType<lang::XServiceInfo>::get()) ||
+ IsTypeName( sName,
+ cppu::UnoType<sheet::XAddIn>::get()) );
+ }
+ if (!bSkip)
+ {
+ uno::Reference<reflection::XIdlClass> xReturn =
+ xFunc->getReturnType();
+ if ( !lcl_ValidReturnType( xReturn ) )
+ bSkip = true;
+ }
+ if (!bSkip)
+ {
+ OUString aFuncU = xFunc->getName();
+
+ // stored function name: (service name).(function)
+ OUString aFuncName = aServiceName + "." + aFuncU;
+
+ bool bValid = true;
+ tools::Long nVisibleCount = 0;
+ tools::Long nCallerPos = SC_CALLERPOS_NONE;
+
+ uno::Sequence<reflection::ParamInfo> aParams =
+ xFunc->getParameterInfos();
+ tools::Long nParamCount = aParams.getLength();
+ const reflection::ParamInfo* pParArr = aParams.getConstArray();
+ tools::Long nParamPos;
+ for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
+ {
+ if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
+ bValid = false;
+ uno::Reference<reflection::XIdlClass> xParClass =
+ pParArr[nParamPos].aType;
+ ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
+ if ( eArgType == SC_ADDINARG_NONE )
+ bValid = false;
+ else if ( eArgType == SC_ADDINARG_CALLER )
+ nCallerPos = nParamPos;
+ else
+ ++nVisibleCount;
+ }
+ if (bValid)
+ {
+ sal_uInt16 nCategory = lcl_GetCategory(
+ xAddIn->getProgrammaticCategoryName( aFuncU ) );
+
+ OUString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU );
+
+ OUString aLocalName;
+ try
+ {
+ aLocalName = xAddIn->
+ getDisplayFunctionName( aFuncU );
+ }
+ catch(uno::Exception&)
+ {
+ aLocalName = "###";
+ }
+
+ OUString aDescription;
+ try
+ {
+ aDescription = xAddIn->
+ getFunctionDescription( aFuncU );
+ }
+ catch(uno::Exception&)
+ {
+ aDescription = "###";
+ }
+
+ std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
+ if ( nVisibleCount > 0 )
+ {
+ ScAddInArgDesc aDesc;
+ pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
+ tools::Long nDestPos = 0;
+ for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
+ {
+ uno::Reference<reflection::XIdlClass> xParClass =
+ pParArr[nParamPos].aType;
+ ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
+ if ( eArgType != SC_ADDINARG_CALLER )
+ {
+ OUString aArgName;
+ try
+ {
+ aArgName = xAddIn->
+ getDisplayArgumentName( aFuncU, nParamPos );
+ }
+ catch(uno::Exception&)
+ {
+ aArgName = "###";
+ }
+ OUString aArgDesc;
+ try
+ {
+ aArgDesc = xAddIn->
+ getArgumentDescription( aFuncU, nParamPos );
+ }
+ catch(uno::Exception&)
+ {
+ aArgDesc = "###";
+ }
+
+ bool bOptional =
+ ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
+ eArgType == SC_ADDINARG_VARARGS );
+
+ aDesc.eType = eArgType;
+ aDesc.aName = aArgName;
+ aDesc.aDescription = aArgDesc;
+ aDesc.bOptional = bOptional;
+ //TODO: initialize aInternalName only from config?
+ aDesc.aInternalName = pParArr[nParamPos].aName;
+
+ pVisibleArgs[nDestPos++] = aDesc;
+ }
+ }
+ OSL_ENSURE( nDestPos==nVisibleCount, "wrong count" );
+ }
+
+ ppFuncData[nFuncPos+nOld].reset( new ScUnoAddInFuncData(
+ aFuncName, aLocalName, aDescription,
+ nCategory, sHelpId,
+ xFunc, aObject,
+ nVisibleCount, pVisibleArgs.get(), nCallerPos ) );
+
+ ScUnoAddInFuncData* pData = ppFuncData[nFuncPos+nOld].get();
+ pExactHashMap->emplace(
+ pData->GetOriginalName(),
+ pData );
+ pNameHashMap->emplace(
+ pData->GetUpperName(),
+ pData );
+ pLocalHashMap->emplace(
+ pData->GetUpperLocal(),
+ pData );
+
+ aFuncNameData.push_back({aFuncU, pData});
+ }
+ }
+ }
+ }
+
+ const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
+ xAddIn->setLocale( aEnglishLanguageTag.getLocale());
+ for (const auto& rFunc : aFuncNameData)
+ {
+ OUString aEnglishName;
+ try
+ {
+ aEnglishName = xAddIn->getDisplayFunctionName( rFunc.aFuncU );
+ }
+ catch(uno::Exception&)
+ {
+ }
+ if (aEnglishName.isEmpty()
+ && rFunc.pData->GetExcelName( aEnglishLanguageTag, aEnglishName, false /*bFallbackToAny*/))
+ {
+ // Check our known suffixes and append if not present. Note this
+ // depends on localization (that should not add such suffix, but..)
+ // and is really only a last resort.
+ if (rFunc.pData->GetLocalName().endsWith("_ADD") && !aEnglishName.endsWith("_ADD"))
+ aEnglishName += "_ADD";
+ else if (rFunc.pData->GetLocalName().endsWith("_EXCEL2003") && !aEnglishName.endsWith("_EXCEL2003"))
+ aEnglishName += "_EXCEL2003";
+ SAL_WARN("sc.core", "obtaining English name for " << rFunc.pData->GetLocalName() << " "
+ << rFunc.pData->GetOriginalName() << " as ExcelName '" << aEnglishName << "'");
+ }
+ SAL_WARN_IF(aEnglishName.isEmpty(), "sc.core", "no English name for "
+ << rFunc.pData->GetLocalName() << " " << rFunc.pData->GetOriginalName());
+ rFunc.pData->SetEnglishName(aEnglishName); // takes care of handling empty
+ pEnglishHashMap->emplace( rFunc.pData->GetUpperEnglish(), rFunc.pData);
+ }
+}
+
+static void lcl_UpdateFunctionList( const ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData,
+ bool bEnglishFunctionNames )
+{
+ // as used in FillFunctionDescFromData
+ const OUString& aCompare = (bEnglishFunctionNames ? rFuncData.GetUpperEnglish() : rFuncData.GetUpperLocal());
+
+ sal_uLong nCount = rFunctionList.GetCount();
+ for (sal_uLong nPos=0; nPos<nCount; nPos++)
+ {
+ const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos );
+ if ( pDesc && pDesc->mxFuncName && *pDesc->mxFuncName == aCompare )
+ {
+ ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(pDesc),
+ bEnglishFunctionNames);
+ break;
+ }
+ }
+}
+
+static const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, std::u16string_view rArgIntName )
+{
+ tools::Long nArgCount = rFuncData.GetArgumentCount();
+ const ScAddInArgDesc* pArguments = rFuncData.GetArguments();
+ for (tools::Long nPos=0; nPos<nArgCount; nPos++)
+ {
+ if ( pArguments[nPos].aInternalName == rArgIntName )
+ return &pArguments[nPos];
+ }
+ return nullptr;
+}
+
+void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& xInterface,
+ std::u16string_view rServiceName )
+{
+ const bool bEnglishFunctionNames = SC_MOD()->GetFormulaOptions().GetUseEnglishFuncName();
+ uno::Reference<lang::XLocalizable> xLoc( xInterface, uno::UNO_QUERY );
+ if ( xLoc.is() ) // optional in new add-ins
+ xLoc->setLocale( Application::GetSettings().GetUILanguageTag().getLocale());
+
+ // if function list was already initialized, it must be updated
+
+ ScFunctionList* pFunctionList = nullptr;
+ if ( ScGlobal::HasStarCalcFunctionList() )
+ pFunctionList = ScGlobal::GetStarCalcFunctionList();
+
+ // only get the function information from Introspection
+
+ uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference<beans::XIntrospection> xIntro = beans::theIntrospection::get(xContext);
+ uno::Any aObject;
+ aObject <<= xInterface;
+ uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
+ if (!xAcc.is())
+ return;
+
+ const uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
+ xAcc->getMethods( beans::MethodConcept::ALL );
+ for (const uno::Reference<reflection::XIdlMethod>& xFunc : aMethods)
+ {
+ if (xFunc.is())
+ {
+ OUString aFuncU = xFunc->getName();
+
+ // stored function name: (service name).(function)
+ OUString aFuncName = OUString::Concat(rServiceName) + "." + aFuncU;
+
+ // internal names are skipped because no FuncData exists
+ ScUnoAddInFuncData* pOldData = const_cast<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) );
+ if ( pOldData )
+ {
+ // Create new (complete) argument info.
+ // As in ReadFromAddIn, the reflection information is authoritative.
+ // Local names and descriptions from pOldData are looked up using the
+ // internal argument name.
+
+ bool bValid = true;
+ tools::Long nVisibleCount = 0;
+ tools::Long nCallerPos = SC_CALLERPOS_NONE;
+
+ const uno::Sequence<reflection::ParamInfo> aParams =
+ xFunc->getParameterInfos();
+ tools::Long nParamCount = aParams.getLength();
+ const reflection::ParamInfo* pParArr = aParams.getConstArray();
+ for (tools::Long nParamPos=0; nParamPos<nParamCount; nParamPos++)
+ {
+ if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
+ bValid = false;
+ uno::Reference<reflection::XIdlClass> xParClass =
+ pParArr[nParamPos].aType;
+ ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
+ if ( eArgType == SC_ADDINARG_NONE )
+ bValid = false;
+ else if ( eArgType == SC_ADDINARG_CALLER )
+ nCallerPos = nParamPos;
+ else
+ ++nVisibleCount;
+ }
+ if (bValid)
+ {
+ std::unique_ptr<ScAddInArgDesc[]> pVisibleArgs;
+ if ( nVisibleCount > 0 )
+ {
+ ScAddInArgDesc aDesc;
+ pVisibleArgs.reset(new ScAddInArgDesc[nVisibleCount]);
+ tools::Long nDestPos = 0;
+ for (const auto& rParam : aParams)
+ {
+ uno::Reference<reflection::XIdlClass> xParClass =
+ rParam.aType;
+ ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
+ if ( eArgType != SC_ADDINARG_CALLER )
+ {
+ const ScAddInArgDesc* pOldArgDesc =
+ lcl_FindArgDesc( *pOldData, rParam.aName );
+ if ( pOldArgDesc )
+ {
+ aDesc.aName = pOldArgDesc->aName;
+ aDesc.aDescription = pOldArgDesc->aDescription;
+ }
+ else
+ aDesc.aName = aDesc.aDescription = "###";
+
+ bool bOptional =
+ ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
+ eArgType == SC_ADDINARG_VARARGS );
+
+ aDesc.eType = eArgType;
+ aDesc.bOptional = bOptional;
+ //TODO: initialize aInternalName only from config?
+ aDesc.aInternalName = rParam.aName;
+
+ pVisibleArgs[nDestPos++] = aDesc;
+ }
+ }
+ OSL_ENSURE( nDestPos==nVisibleCount, "wrong count" );
+ }
+
+ pOldData->SetFunction( xFunc, aObject );
+ pOldData->SetArguments( nVisibleCount, pVisibleArgs.get() );
+ pOldData->SetCallerPos( nCallerPos );
+
+ if ( pFunctionList )
+ lcl_UpdateFunctionList( *pFunctionList, *pOldData, bEnglishFunctionNames );
+ }
+ }
+ }
+ }
+}
+
+OUString ScUnoAddInCollection::FindFunction( const OUString& rUpperName, bool bLocalFirst )
+{
+ if (!bInitialized)
+ Initialize();
+
+ if (nFuncCount == 0)
+ return OUString();
+
+ if ( bLocalFirst )
+ {
+ // Only scan local names (used for entering formulas).
+
+ ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) );
+ if ( iLook != pLocalHashMap->end() )
+ return iLook->second->GetOriginalName();
+ }
+ else
+ {
+ // First scan international programmatic names (used when calling a
+ // function).
+
+ ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) );
+ if ( iLook != pNameHashMap->end() )
+ return iLook->second->GetOriginalName();
+
+ // Then scan English names (as FunctionAccess API could expect).
+
+ iLook = pEnglishHashMap->find( rUpperName );
+ if ( iLook != pEnglishHashMap->end() )
+ return iLook->second->GetOriginalName();
+
+ // After that, scan all local names; either to allow replacing old
+ // AddIns with Uno, or for functions where the AddIn did not provide an
+ // English name.
+
+ iLook = pLocalHashMap->find( rUpperName );
+ if ( iLook != pLocalHashMap->end() )
+ return iLook->second->GetOriginalName();
+ }
+
+ return OUString();
+}
+
+const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const OUString& rName, bool bComplete )
+{
+ if (!bInitialized)
+ Initialize();
+
+ // rName must be the exact internal name
+
+ ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
+ if ( iLook != pExactHashMap->end() )
+ {
+ const ScUnoAddInFuncData* pFuncData = iLook->second;
+
+ if ( bComplete && !pFuncData->GetFunction().is() ) //TODO: extra flag?
+ LoadComponent( *pFuncData );
+
+ return pFuncData;
+ }
+
+ return nullptr;
+}
+
+const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( tools::Long nIndex )
+{
+ if (!bInitialized)
+ Initialize();
+
+ if (nIndex < nFuncCount)
+ return ppFuncData[nIndex].get();
+ return nullptr;
+}
+
+void ScUnoAddInCollection::LocalizeString( OUString& rName )
+{
+ if (!bInitialized)
+ Initialize();
+
+ // modify rName - input: exact name
+
+ ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
+ if ( iLook != pExactHashMap->end() )
+ rName = iLook->second->GetUpperLocal(); //TODO: upper?
+}
+
+tools::Long ScUnoAddInCollection::GetFuncCount()
+{
+ if (!bInitialized)
+ Initialize();
+
+ return nFuncCount;
+}
+
+bool ScUnoAddInCollection::FillFunctionDesc( tools::Long nFunc, ScFuncDesc& rDesc, bool bEnglishFunctionNames )
+{
+ if (!bInitialized)
+ Initialize();
+
+ if (nFunc >= nFuncCount || !ppFuncData[nFunc])
+ return false;
+
+ const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc];
+
+ return FillFunctionDescFromData( rFuncData, rDesc, bEnglishFunctionNames );
+}
+
+bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc,
+ bool bEnglishFunctionNames )
+{
+ rDesc.Clear();
+
+ bool bIncomplete = !rFuncData.GetFunction().is(); //TODO: extra flag?
+
+ tools::Long nArgCount = rFuncData.GetArgumentCount();
+ if ( nArgCount > SAL_MAX_UINT16 )
+ return false;
+
+ if ( bIncomplete )
+ nArgCount = 0; // if incomplete, fill without argument info (no wrong order)
+
+ // nFIndex is set from outside
+
+ rDesc.mxFuncName = (bEnglishFunctionNames ? rFuncData.GetUpperEnglish() : rFuncData.GetUpperLocal());
+ rDesc.nCategory = rFuncData.GetCategory();
+ rDesc.sHelpId = rFuncData.GetHelpId();
+
+ OUString aDesc = rFuncData.GetDescription();
+ if (aDesc.isEmpty())
+ aDesc = rFuncData.GetLocalName(); // use name if no description is available
+ rDesc.mxFuncDesc = aDesc ;
+
+ // AddInArgumentType_CALLER is already left out in FuncData
+
+ rDesc.nArgCount = static_cast<sal_uInt16>(nArgCount);
+ if ( nArgCount )
+ {
+ bool bMultiple = false;
+ const ScAddInArgDesc* pArgs = rFuncData.GetArguments();
+
+ rDesc.maDefArgNames.clear();
+ rDesc.maDefArgNames.resize(nArgCount);
+ rDesc.maDefArgDescs.clear();
+ rDesc.maDefArgDescs.resize(nArgCount);
+ rDesc.pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgCount];
+ for ( tools::Long nArg=0; nArg<nArgCount; nArg++ )
+ {
+ rDesc.maDefArgNames[nArg] = pArgs[nArg].aName;
+ rDesc.maDefArgDescs[nArg] = pArgs[nArg].aDescription;
+ rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional;
+
+ // no empty names...
+ if ( rDesc.maDefArgNames[nArg].isEmpty() )
+ {
+ OUString aDefName = "arg" + OUString::number( nArg+1 );
+ rDesc.maDefArgNames[nArg] = aDefName;
+ }
+
+ // last argument repeated?
+ if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) )
+ bMultiple = true;
+ }
+
+ if ( bMultiple )
+ rDesc.nArgCount += VAR_ARGS - 1; // VAR_ARGS means just one repeated arg
+ }
+
+ rDesc.bIncomplete = bIncomplete;
+
+ return true;
+}
+
+ScUnoAddInCall::ScUnoAddInCall( ScDocument& rDoc, ScUnoAddInCollection& rColl, const OUString& rName,
+ tools::Long nParamCount ) :
+ mrDoc( rDoc ),
+ bValidCount( false ),
+ nErrCode( FormulaError::NoCode ), // before function was called
+ bHasString( true ),
+ fValue( 0.0 ),
+ xMatrix( nullptr )
+{
+ pFuncData = rColl.GetFuncData( rName, true ); // need fully initialized data
+ OSL_ENSURE( pFuncData, "Function Data missing" );
+ if ( !pFuncData )
+ return;
+
+ tools::Long nDescCount = pFuncData->GetArgumentCount();
+ const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
+
+ // is aVarArg sequence needed?
+ if ( nParamCount >= nDescCount && nDescCount > 0 &&
+ pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS )
+ {
+ tools::Long nVarCount = nParamCount - ( nDescCount - 1 ); // size of last argument
+ aVarArg.realloc( nVarCount );
+ bValidCount = true;
+ }
+ else if ( nParamCount <= nDescCount )
+ {
+ // all args behind nParamCount must be optional
+ bValidCount = true;
+ for (tools::Long i=nParamCount; i<nDescCount; i++)
+ if ( !pArgs[i].bOptional )
+ bValidCount = false;
+ }
+ // else invalid (too many arguments)
+
+ if ( bValidCount )
+ aArgs.realloc( nDescCount ); // sequence must always match function signature
+}
+
+ScUnoAddInCall::~ScUnoAddInCall()
+{
+ // pFuncData is deleted with ScUnoAddInCollection
+}
+
+ScAddInArgumentType ScUnoAddInCall::GetArgType( tools::Long nPos )
+{
+ if ( pFuncData )
+ {
+ tools::Long nCount = pFuncData->GetArgumentCount();
+ const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
+
+ // if last arg is sequence, use "any" type
+ if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
+ return SC_ADDINARG_VALUE_OR_ARRAY;
+
+ if ( nPos < nCount )
+ return pArgs[nPos].eType;
+ }
+ return SC_ADDINARG_VALUE_OR_ARRAY; //TODO: error code !!!!
+}
+
+bool ScUnoAddInCall::NeedsCaller() const
+{
+ return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE;
+}
+
+void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface )
+{
+ xCaller = rInterface;
+}
+
+void ScUnoAddInCall::SetCallerFromObjectShell( const SfxObjectShell* pObjSh )
+{
+ if (pObjSh)
+ {
+ uno::Reference<uno::XInterface> xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY );
+ SetCaller( xInt );
+ }
+}
+
+void ScUnoAddInCall::SetParam( tools::Long nPos, const uno::Any& rValue )
+{
+ if ( !pFuncData )
+ return;
+
+ tools::Long nCount = pFuncData->GetArgumentCount();
+ const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
+ if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
+ {
+ tools::Long nVarPos = nPos-(nCount-1);
+ if ( nVarPos < aVarArg.getLength() )
+ aVarArg.getArray()[nVarPos] = rValue;
+ else
+ {
+ OSL_FAIL("wrong argument number");
+ }
+ }
+ else if ( nPos < aArgs.getLength() )
+ aArgs.getArray()[nPos] = rValue;
+ else
+ {
+ OSL_FAIL("wrong argument number");
+ }
+}
+
+void ScUnoAddInCall::ExecuteCall()
+{
+ if ( !pFuncData )
+ return;
+
+ tools::Long nCount = pFuncData->GetArgumentCount();
+ const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
+ if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
+ {
+ // insert aVarArg as last argument
+ //TODO: after inserting caller (to prevent copying twice)?
+
+ OSL_ENSURE( aArgs.getLength() == nCount, "wrong argument count" );
+ aArgs.getArray()[nCount-1] <<= aVarArg;
+ }
+
+ if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE )
+ {
+ uno::Any aCallerAny;
+ aCallerAny <<= xCaller;
+
+ tools::Long nUserLen = aArgs.getLength();
+ tools::Long nCallPos = pFuncData->GetCallerPos();
+ if (nCallPos>nUserLen) // should not happen
+ {
+ OSL_FAIL("wrong CallPos");
+ nCallPos = nUserLen;
+ }
+
+ tools::Long nDestLen = nUserLen + 1;
+ uno::Sequence<uno::Any> aRealArgs( nDestLen );
+ uno::Any* pDest = aRealArgs.getArray();
+
+ pDest = std::copy_n(std::cbegin(aArgs), nCallPos, pDest);
+ *pDest = aCallerAny;
+ std::copy(std::next(std::cbegin(aArgs), nCallPos), std::cend(aArgs), std::next(pDest));
+
+ ExecuteCallWithArgs( aRealArgs );
+ }
+ else
+ ExecuteCallWithArgs( aArgs );
+}
+
+void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence<uno::Any>& rCallArgs)
+{
+ // rCallArgs may not match argument descriptions (because of caller)
+
+ uno::Reference<reflection::XIdlMethod> xFunction;
+ uno::Any aObject;
+ if ( pFuncData )
+ {
+ xFunction = pFuncData->GetFunction();
+ aObject = pFuncData->GetObject();
+ }
+
+ if ( !xFunction.is() )
+ return;
+
+ uno::Any aAny;
+ nErrCode = FormulaError::NONE;
+
+ try
+ {
+ aAny = xFunction->invoke( aObject, rCallArgs );
+ }
+ catch(lang::IllegalArgumentException&)
+ {
+ nErrCode = FormulaError::IllegalArgument;
+ }
+ catch(const reflection::InvocationTargetException& rWrapped)
+ {
+ if ( rWrapped.TargetException.getValueType().equals(
+ cppu::UnoType<lang::IllegalArgumentException>::get()) )
+ nErrCode = FormulaError::IllegalArgument;
+ else if ( rWrapped.TargetException.getValueType().equals(
+ cppu::UnoType<sheet::NoConvergenceException>::get()) )
+ nErrCode = FormulaError::NoConvergence;
+ else
+ nErrCode = FormulaError::NoValue;
+ }
+ catch(uno::Exception&)
+ {
+ nErrCode = FormulaError::NoValue;
+ }
+
+ if (nErrCode == FormulaError::NONE)
+ SetResult( aAny ); // convert result to Calc types
+}
+
+template <typename T>
+static sal_Int32 lcl_GetMaxColCount(const uno::Sequence< uno::Sequence<T> >* pRowSeq)
+{
+ if (!pRowSeq->hasElements())
+ return 0;
+
+ auto pRow = std::max_element(pRowSeq->begin(), pRowSeq->end(),
+ [](const uno::Sequence<T>& a, const uno::Sequence<T>& b) {
+ return a.getLength() < b.getLength(); });
+ return pRow->getLength();
+}
+
+void ScUnoAddInCall::SetResult( const uno::Any& rNewRes )
+{
+ nErrCode = FormulaError::NONE;
+ xVarRes = nullptr;
+
+ // Reflection* pRefl = rNewRes.getReflection();
+
+ uno::TypeClass eClass = rNewRes.getValueTypeClass();
+ const uno::Type& aType = rNewRes.getValueType();
+ switch (eClass)
+ {
+ case uno::TypeClass_VOID:
+ nErrCode = FormulaError::NotAvailable; // #NA
+ break;
+
+ case uno::TypeClass_ENUM:
+ case uno::TypeClass_BOOLEAN:
+ case uno::TypeClass_CHAR:
+ case uno::TypeClass_BYTE:
+ case uno::TypeClass_SHORT:
+ case uno::TypeClass_UNSIGNED_SHORT:
+ case uno::TypeClass_LONG:
+ case uno::TypeClass_UNSIGNED_LONG:
+ case uno::TypeClass_FLOAT:
+ case uno::TypeClass_DOUBLE:
+ {
+ uno::TypeClass eMyClass;
+ ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes);
+ bHasString = false;
+ }
+ break;
+
+ case uno::TypeClass_STRING:
+ {
+ rNewRes >>= aString;
+ bHasString = true;
+ }
+ break;
+
+ case uno::TypeClass_INTERFACE:
+ {
+ //TODO: directly extract XVolatileResult from any?
+ uno::Reference<uno::XInterface> xInterface;
+ rNewRes >>= xInterface;
+ if ( xInterface.is() )
+ xVarRes.set( xInterface, uno::UNO_QUERY );
+
+ if (!xVarRes.is())
+ nErrCode = FormulaError::NoValue; // unknown interface
+ }
+ break;
+
+ default:
+ if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) )
+ {
+ const uno::Sequence< uno::Sequence<sal_Int32> >* pRowSeq = nullptr;
+
+ //TODO: use pointer from any!
+ uno::Sequence< uno::Sequence<sal_Int32> > aSequence;
+ if ( rNewRes >>= aSequence )
+ pRowSeq = &aSequence;
+
+ if ( pRowSeq )
+ {
+ sal_Int32 nRowCount = pRowSeq->getLength();
+ sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
+ if ( nMaxColCount && nRowCount )
+ {
+ const uno::Sequence<sal_Int32>* pRowArr = pRowSeq->getConstArray();
+ xMatrix = new ScMatrix(
+ static_cast<SCSIZE>(nMaxColCount),
+ static_cast<SCSIZE>(nRowCount), 0.0);
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ sal_Int32 nColCount = pRowArr[nRow].getLength();
+ const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ xMatrix->PutDouble( pColArr[nCol],
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
+ xMatrix->PutDouble( 0.0,
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ }
+ }
+ }
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) )
+ {
+ const uno::Sequence< uno::Sequence<double> >* pRowSeq = nullptr;
+
+ //TODO: use pointer from any!
+ uno::Sequence< uno::Sequence<double> > aSequence;
+ if ( rNewRes >>= aSequence )
+ pRowSeq = &aSequence;
+
+ if ( pRowSeq )
+ {
+ sal_Int32 nRowCount = pRowSeq->getLength();
+ sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
+ if ( nMaxColCount && nRowCount )
+ {
+ const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray();
+ xMatrix = new ScMatrix(
+ static_cast<SCSIZE>(nMaxColCount),
+ static_cast<SCSIZE>(nRowCount), 0.0);
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ sal_Int32 nColCount = pRowArr[nRow].getLength();
+ const double* pColArr = pRowArr[nRow].getConstArray();
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ xMatrix->PutDouble( pColArr[nCol],
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
+ xMatrix->PutDouble( 0.0,
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ }
+ }
+ }
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) )
+ {
+ const uno::Sequence< uno::Sequence<OUString> >* pRowSeq = nullptr;
+
+ //TODO: use pointer from any!
+ uno::Sequence< uno::Sequence<OUString> > aSequence;
+ if ( rNewRes >>= aSequence )
+ pRowSeq = &aSequence;
+
+ if ( pRowSeq )
+ {
+ sal_Int32 nRowCount = pRowSeq->getLength();
+ sal_Int32 nMaxColCount = lcl_GetMaxColCount(pRowSeq);
+ if ( nMaxColCount && nRowCount )
+ {
+ const uno::Sequence<OUString>* pRowArr = pRowSeq->getConstArray();
+ xMatrix = new ScMatrix(
+ static_cast<SCSIZE>(nMaxColCount),
+ static_cast<SCSIZE>(nRowCount), 0.0);
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ sal_Int32 nColCount = pRowArr[nRow].getLength();
+ const OUString* pColArr = pRowArr[nRow].getConstArray();
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ {
+ xMatrix->PutString(
+ mrDoc.GetSharedStringPool().intern(pColArr[nCol]),
+ static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
+ }
+ for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
+ {
+ xMatrix->PutString(
+ svl::SharedString::getEmptyString(),
+ static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
+ }
+ }
+ }
+ }
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) )
+ {
+ xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes );
+ }
+
+ if (!xMatrix) // no array found
+ nErrCode = FormulaError::NoValue; //TODO: code for error in return type???
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/addinhelpid.cxx b/sc/source/core/tool/addinhelpid.cxx
new file mode 100644
index 0000000000..9c44e269d0
--- /dev/null
+++ b/sc/source/core/tool/addinhelpid.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 <addinhelpid.hxx>
+#include <helpids.h>
+#include <o3tl/string_view.hxx>
+
+// A struct containing the built-in function name and the built-in help ID.
+struct ScUnoAddInHelpId
+{
+ const char* pFuncName;
+ OUString sHelpId;
+};
+
+// Help IDs for Analysis AddIn. MUST BE SORTED for binary search.
+const ScUnoAddInHelpId pAnalysisHelpIds[] =
+{
+ { "getAccrint" , HID_AAI_FUNC_ACCRINT },
+ { "getAccrintm" , HID_AAI_FUNC_ACCRINTM },
+ { "getAmordegrc" , HID_AAI_FUNC_AMORDEGRC },
+ { "getAmorlinc" , HID_AAI_FUNC_AMORLINC },
+ { "getBesseli" , HID_AAI_FUNC_BESSELI },
+ { "getBesselj" , HID_AAI_FUNC_BESSELJ },
+ { "getBesselk" , HID_AAI_FUNC_BESSELK },
+ { "getBessely" , HID_AAI_FUNC_BESSELY },
+ { "getBin2Dec" , HID_AAI_FUNC_BIN2DEC },
+ { "getBin2Hex" , HID_AAI_FUNC_BIN2HEX },
+ { "getBin2Oct" , HID_AAI_FUNC_BIN2OCT },
+ { "getComplex" , HID_AAI_FUNC_COMPLEX },
+ { "getConvert" , HID_AAI_FUNC_CONVERT },
+ { "getCoupdaybs" , HID_AAI_FUNC_COUPDAYBS },
+ { "getCoupdays" , HID_AAI_FUNC_COUPDAYS },
+ { "getCoupdaysnc" , HID_AAI_FUNC_COUPDAYSNC },
+ { "getCoupncd" , HID_AAI_FUNC_COUPNCD },
+ { "getCoupnum" , HID_AAI_FUNC_COUPNUM },
+ { "getCouppcd" , HID_AAI_FUNC_COUPPCD },
+ { "getCumipmt" , HID_AAI_FUNC_CUMIPMT },
+ { "getCumprinc" , HID_AAI_FUNC_CUMPRINC },
+ { "getDec2Bin" , HID_AAI_FUNC_DEC2BIN },
+ { "getDec2Hex" , HID_AAI_FUNC_DEC2HEX },
+ { "getDec2Oct" , HID_AAI_FUNC_DEC2OCT },
+ { "getDelta" , HID_AAI_FUNC_DELTA },
+ { "getDisc" , HID_AAI_FUNC_DISC },
+ { "getDollarde" , HID_AAI_FUNC_DOLLARDE },
+ { "getDollarfr" , HID_AAI_FUNC_DOLLARFR },
+ { "getDuration" , HID_AAI_FUNC_DURATION },
+ { "getEdate" , HID_AAI_FUNC_EDATE },
+ { "getEffect" , HID_AAI_FUNC_EFFECT },
+ { "getEomonth" , HID_AAI_FUNC_EOMONTH },
+ { "getErf" , HID_AAI_FUNC_ERF },
+ { "getErfc" , HID_AAI_FUNC_ERFC },
+ { "getFactdouble" , HID_AAI_FUNC_FACTDOUBLE },
+ { "getFvschedule" , HID_AAI_FUNC_FVSCHEDULE },
+ { "getGcd" , HID_AAI_FUNC_GCD },
+ { "getGestep" , HID_AAI_FUNC_GESTEP },
+ { "getHex2Bin" , HID_AAI_FUNC_HEX2BIN },
+ { "getHex2Dec" , HID_AAI_FUNC_HEX2DEC },
+ { "getHex2Oct" , HID_AAI_FUNC_HEX2OCT },
+ { "getImabs" , HID_AAI_FUNC_IMABS },
+ { "getImaginary" , HID_AAI_FUNC_IMAGINARY },
+ { "getImargument" , HID_AAI_FUNC_IMARGUMENT },
+ { "getImconjugate" , HID_AAI_FUNC_IMCONJUGATE },
+ { "getImcos" , HID_AAI_FUNC_IMCOS },
+ { "getImcosh" , HID_AAI_FUNC_IMCOSH },
+ { "getImcot" , HID_AAI_FUNC_IMCOT },
+ { "getImcsc" , HID_AAI_FUNC_IMCSC },
+ { "getImcsch" , HID_AAI_FUNC_IMCSCH },
+ { "getImdiv" , HID_AAI_FUNC_IMDIV },
+ { "getImexp" , HID_AAI_FUNC_IMEXP },
+ { "getImln" , HID_AAI_FUNC_IMLN },
+ { "getImlog10" , HID_AAI_FUNC_IMLOG10 },
+ { "getImlog2" , HID_AAI_FUNC_IMLOG2 },
+ { "getImpower" , HID_AAI_FUNC_IMPOWER },
+ { "getImproduct" , HID_AAI_FUNC_IMPRODUCT },
+ { "getImreal" , HID_AAI_FUNC_IMREAL },
+ { "getImsec" , HID_AAI_FUNC_IMSEC },
+ { "getImsech" , HID_AAI_FUNC_IMSECH },
+ { "getImsin" , HID_AAI_FUNC_IMSIN },
+ { "getImsinh" , HID_AAI_FUNC_IMSINH },
+ { "getImsqrt" , HID_AAI_FUNC_IMSQRT },
+ { "getImsub" , HID_AAI_FUNC_IMSUB },
+ { "getImsum" , HID_AAI_FUNC_IMSUM },
+ { "getImtan" , HID_AAI_FUNC_IMTAN },
+ { "getIntrate" , HID_AAI_FUNC_INTRATE },
+ { "getIseven" , HID_AAI_FUNC_ISEVEN },
+ { "getIsodd" , HID_AAI_FUNC_ISODD },
+ { "getLcm" , HID_AAI_FUNC_LCM },
+ { "getMduration" , HID_AAI_FUNC_MDURATION },
+ { "getMround" , HID_AAI_FUNC_MROUND },
+ { "getMultinomial" , HID_AAI_FUNC_MULTINOMIAL },
+ { "getNetworkdays" , HID_AAI_FUNC_NETWORKDAYS },
+ { "getNominal" , HID_AAI_FUNC_NOMINAL },
+ { "getOct2Bin" , HID_AAI_FUNC_OCT2BIN },
+ { "getOct2Dec" , HID_AAI_FUNC_OCT2DEZ },
+ { "getOct2Hex" , HID_AAI_FUNC_OCT2HEX },
+ { "getOddfprice" , HID_AAI_FUNC_ODDFPRICE },
+ { "getOddfyield" , HID_AAI_FUNC_ODDFYIELD },
+ { "getOddlprice" , HID_AAI_FUNC_ODDLPRICE },
+ { "getOddlyield" , HID_AAI_FUNC_ODDLYIELD },
+ { "getPrice" , HID_AAI_FUNC_PRICE },
+ { "getPricedisc" , HID_AAI_FUNC_PRICEDISC },
+ { "getPricemat" , HID_AAI_FUNC_PRICEMAT },
+ { "getQuotient" , HID_AAI_FUNC_QUOTIENT },
+ { "getRandbetween" , HID_AAI_FUNC_RANDBETWEEN },
+ { "getReceived" , HID_AAI_FUNC_RECEIVED },
+ { "getSeriessum" , HID_AAI_FUNC_SERIESSUM },
+ { "getSqrtpi" , HID_AAI_FUNC_SQRTPI },
+ { "getTbilleq" , HID_AAI_FUNC_TBILLEQ },
+ { "getTbillprice" , HID_AAI_FUNC_TBILLPRICE },
+ { "getTbillyield" , HID_AAI_FUNC_TBILLYIELD },
+ { "getWeeknum" , HID_AAI_FUNC_WEEKNUM },
+ { "getWorkday" , HID_AAI_FUNC_WORKDAY },
+ { "getXirr" , HID_AAI_FUNC_XIRR },
+ { "getXnpv" , HID_AAI_FUNC_XNPV },
+ { "getYearfrac" , HID_AAI_FUNC_YEARFRAC },
+ { "getYield" , HID_AAI_FUNC_YIELD },
+ { "getYielddisc" , HID_AAI_FUNC_YIELDDISC },
+ { "getYieldmat" , HID_AAI_FUNC_YIELDMAT }
+};
+
+// Help IDs for DateFunc AddIn. MUST BE SORTED for binary search.
+const ScUnoAddInHelpId pDateFuncHelpIds[] =
+{
+ { "getDaysInMonth" , HID_DAI_FUNC_DAYSINMONTH },
+ { "getDaysInYear" , HID_DAI_FUNC_DAYSINYEAR },
+ { "getDiffMonths" , HID_DAI_FUNC_DIFFMONTHS },
+ { "getDiffWeeks" , HID_DAI_FUNC_DIFFWEEKS },
+ { "getDiffYears" , HID_DAI_FUNC_DIFFYEARS },
+ { "getRot13" , HID_DAI_FUNC_ROT13 },
+ { "getWeeksInYear" , HID_DAI_FUNC_WEEKSINYEAR }
+};
+
+// Help IDs for Pricing AddIn. MUST BE SORTED for binary search.
+const ScUnoAddInHelpId pPricingFuncHelpIds[] =
+{
+ { "getOptBarrier" , HID_PAI_FUNC_OPT_BARRIER },
+ { "getOptProbHit" , HID_PAI_FUNC_OPT_PROB_HIT },
+ { "getOptProbInMoney" , HID_PAI_FUNC_OPT_PROB_INMONEY },
+ { "getOptTouch" , HID_PAI_FUNC_OPT_TOUCH }
+};
+
+ScUnoAddInHelpIdGenerator::ScUnoAddInHelpIdGenerator( std::u16string_view rServiceName )
+{
+ SetServiceName( rServiceName );
+}
+
+void ScUnoAddInHelpIdGenerator::SetServiceName( std::u16string_view rServiceName )
+{
+ pCurrHelpIds = nullptr;
+ sal_uInt32 nSize = 0;
+
+ if ( rServiceName == u"com.sun.star.sheet.addin.Analysis" )
+ {
+ pCurrHelpIds = pAnalysisHelpIds;
+ nSize = sizeof( pAnalysisHelpIds );
+ }
+ else if ( rServiceName == u"com.sun.star.sheet.addin.DateFunctions" )
+ {
+ pCurrHelpIds = pDateFuncHelpIds;
+ nSize = sizeof( pDateFuncHelpIds );
+ }
+ else if ( rServiceName == u"com.sun.star.sheet.addin.PricingFunctions")
+ {
+ pCurrHelpIds = pPricingFuncHelpIds;
+ nSize = sizeof( pPricingFuncHelpIds);
+ }
+
+ nArrayCount = nSize / sizeof( ScUnoAddInHelpId );
+}
+
+OUString ScUnoAddInHelpIdGenerator::GetHelpId( std::u16string_view rFuncName ) const
+{
+ if( !pCurrHelpIds || !nArrayCount )
+ return {};
+
+ const ScUnoAddInHelpId* pFirst = pCurrHelpIds;
+ const ScUnoAddInHelpId* pLast = pCurrHelpIds + nArrayCount - 1;
+
+ while( pFirst <= pLast )
+ {
+ const ScUnoAddInHelpId* pMiddle = pFirst + (pLast - pFirst) / 2;
+ sal_Int32 nResult = o3tl::compareToAscii( rFuncName, pMiddle->pFuncName );
+ if( !nResult )
+ return pMiddle->sHelpId;
+ else if( nResult < 0 )
+ pLast = pMiddle - 1;
+ else
+ pFirst = pMiddle + 1;
+ }
+
+ return {};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/addinlis.cxx b/sc/source/core/tool/addinlis.cxx
new file mode 100644
index 0000000000..f8a236780e
--- /dev/null
+++ b/sc/source/core/tool/addinlis.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 <sfx2/objsh.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <addinlis.hxx>
+#include <miscuno.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <brdcst.hxx>
+
+#include <com/sun/star/sheet/XVolatileResult.hpp>
+
+using namespace com::sun::star;
+
+SC_SIMPLE_SERVICE_INFO( ScAddInListener, "ScAddInListener", "stardiv.one.sheet.AddInListener" )
+
+::std::vector<rtl::Reference<ScAddInListener>> ScAddInListener::aAllListeners;
+
+ScAddInListener* ScAddInListener::CreateListener(
+ const uno::Reference<sheet::XVolatileResult>& xVR, ScDocument* pDoc )
+{
+ rtl::Reference<ScAddInListener> xNew = new ScAddInListener( xVR, pDoc );
+
+ aAllListeners.push_back( xNew );
+
+ if ( xVR.is() )
+ xVR->addResultListener( xNew ); // after at least 1 ref exists!
+
+ return xNew.get();
+}
+
+ScAddInListener::ScAddInListener( uno::Reference<sheet::XVolatileResult> xVR, ScDocument* pDoc ) :
+ xVolRes(std::move( xVR )),
+ pDocs( new ScAddInDocs )
+{
+ pDocs->insert( pDoc );
+}
+
+ScAddInListener::~ScAddInListener()
+{
+}
+
+ScAddInListener* ScAddInListener::Get( const uno::Reference<sheet::XVolatileResult>& xVR )
+{
+ ScAddInListener* pLst = nullptr;
+ sheet::XVolatileResult* pComp = xVR.get();
+
+ for (auto const& listener : aAllListeners)
+ {
+ if ( pComp == listener->xVolRes.get() )
+ {
+ pLst = listener.get();
+ break;
+ }
+ }
+ return pLst;
+}
+
+//TODO: move to some container object?
+void ScAddInListener::RemoveDocument( ScDocument* pDocumentP )
+{
+ auto iter = aAllListeners.begin();
+ while(iter != aAllListeners.end())
+ {
+ ScAddInDocs* p = (*iter)->pDocs.get();
+ ScAddInDocs::iterator iter2 = p->find( pDocumentP );
+ if( iter2 != p->end() )
+ {
+ p->erase( iter2 );
+ if ( p->empty() )
+ {
+ if ( (*iter)->xVolRes.is() )
+ (*iter)->xVolRes->removeResultListener( *iter );
+
+ iter = aAllListeners.erase( iter );
+ continue;
+ }
+ }
+ ++iter;
+ }
+}
+
+// XResultListener
+
+void SAL_CALL ScAddInListener::modified( const css::sheet::ResultEvent& aEvent )
+{
+ SolarMutexGuard aGuard; //TODO: or generate a UserEvent
+
+ aResult = aEvent.Value; // store result
+
+ // notify document of changes
+
+ Broadcast( ScHint(SfxHintId::ScDataChanged, ScAddress()) );
+
+ for (auto const& pDoc : *pDocs)
+ {
+ pDoc->TrackFormulas();
+ pDoc->GetDocumentShell()->Broadcast( SfxHint( SfxHintId::ScDataChanged ) );
+ }
+}
+
+// XEventListener
+
+void SAL_CALL ScAddInListener::disposing( const css::lang::EventObject& /* Source */ )
+{
+ // hold a ref so this is not deleted at removeResultListener
+ uno::Reference<sheet::XResultListener> xKeepAlive( this );
+
+ if ( xVolRes.is() )
+ {
+ xVolRes->removeResultListener( this );
+ xVolRes = nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx
new file mode 100644
index 0000000000..7b0c848a2b
--- /dev/null
+++ b/sc/source/core/tool/address.cxx
@@ -0,0 +1,2531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <address.hxx>
+#include <global.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <externalrefmgr.hxx>
+
+#include <osl/diagnose.h>
+#include <o3tl/underlyingenumvalue.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/ExternalLinkInfo.hpp>
+#include <com/sun/star/sheet/ExternalLinkType.hpp>
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+#include <rtl/character.hxx>
+#include <unotools/charclass.hxx>
+
+using namespace css;
+
+const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
+
+ScAddress::Details::Details ( const ScDocument& rDoc,
+ const ScAddress& rAddr ) :
+ eConv( rDoc.GetAddressConvention() ),
+ nRow( rAddr.Row() ),
+ nCol( rAddr.Col() )
+{}
+
+namespace {
+
+const sal_Unicode* parseQuotedNameWithBuffer( const sal_Unicode* pStart, const sal_Unicode* p, OUString& rName )
+{
+ // The current character must be on the 2nd quote.
+
+ // Push all the characters up to the current, but skip the very first
+ // character which is the opening quote.
+ OUStringBuffer aBuf(std::u16string_view(pStart+1, p-pStart-1));
+
+ ++p; // Skip the 2nd quote.
+ sal_Unicode cPrev = 0;
+ for (; *p; ++p)
+ {
+ if (*p == '\'')
+ {
+ if (cPrev == '\'')
+ {
+ // double single-quote equals one single quote.
+ aBuf.append(*p);
+ cPrev = 0;
+ continue;
+ }
+ }
+ else if (cPrev == '\'')
+ {
+ // We are past the closing quote. We're done!
+ rName = aBuf.makeStringAndClear();
+ return p;
+ }
+ else
+ aBuf.append(*p);
+ cPrev = *p;
+ }
+
+ return pStart;
+}
+
+/**
+ * Parse from the opening single quote to the closing single quote. Inside
+ * the quotes, a single quote character is encoded by double single-quote
+ * characters.
+ *
+ * @param p pointer to the first character to begin parsing.
+ * @param rName (reference) parsed name within the quotes. If the name is
+ * empty, either the parsing failed or it's an empty quote.
+ *
+ * @return pointer to the character immediately after the closing single
+ * quote.
+ */
+const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName )
+{
+ if (*p != '\'')
+ return p;
+
+ const sal_Unicode* pStart = p;
+ sal_Unicode cPrev = 0;
+ for (++p; *p; ++p)
+ {
+ if (*p == '\'')
+ {
+ if (cPrev == '\'')
+ {
+ // double single-quote equals one single quote.
+ return parseQuotedNameWithBuffer(pStart, p, rName);
+ }
+ }
+ else if (cPrev == '\'')
+ {
+ // We are past the closing quote. We're done! Skip the opening
+ // and closing quotes.
+ rName = OUString(pStart+1, p - pStart-2);
+ return p;
+ }
+
+ cPrev = *p;
+ }
+
+ rName.clear();
+ return pStart;
+}
+
+}
+
+static sal_Int64 sal_Unicode_strtol ( const sal_Unicode* p, const sal_Unicode** pEnd )
+{
+ sal_Int64 accum = 0, prev = 0;
+ bool is_neg = false;
+
+ if( *p == '-' )
+ {
+ is_neg = true;
+ p++;
+ }
+ else if( *p == '+' )
+ p++;
+
+ while (rtl::isAsciiDigit( *p ))
+ {
+ accum = accum * 10 + *p - '0';
+ if( accum < prev )
+ {
+ *pEnd = nullptr;
+ return 0;
+ }
+ prev = accum;
+ p++;
+ }
+
+ *pEnd = p;
+ return is_neg ? -accum : accum;
+}
+
+static const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
+{
+ if ( p )
+ {
+ while( *p == ' ' )
+ ++p;
+ }
+ return p;
+}
+
+// Compare ignore case ASCII.
+static bool lcl_isString( const sal_Unicode* p1, const OUString& rStr )
+{
+ const size_t n = rStr.getLength();
+ if (!n)
+ return false;
+ const sal_Unicode* p2 = rStr.getStr();
+ for (size_t i=0; i<n; ++i)
+ {
+ if (!p1[i])
+ return false;
+ if (p1[i] != p2[i])
+ {
+ sal_Unicode c1 = p1[i];
+ if ('A' <= c1 && c1 <= 'Z')
+ c1 += 0x20;
+ if (c1 < 'a' || 'z' < c1)
+ return false; // not a letter
+
+ sal_Unicode c2 = p2[i];
+ if ('A' <= c2 && c2 <= 'Z')
+ c2 += 0x20;
+ if (c2 < 'a' || 'z' < c2)
+ return false; // not a letter to match
+
+ if (c1 != c2)
+ return false; // lower case doesn't match either
+ }
+ }
+ return true;
+}
+
+/** Determines the number of sheets an external reference spans and sets
+ rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
+ bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
+ cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
+ is set to rEndTabName.
+ @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
+ result in the identical file ID. Else <TRUE/>.
+ */
+static bool lcl_ScRange_External_TabSpan(
+ ScRange & rRange,
+ ScRefFlags & rFlags,
+ ScAddress::ExternalInfo* pExtInfo,
+ const OUString & rExternDocName,
+ const OUString & rStartTabName,
+ const OUString & rEndTabName,
+ const ScDocument& rDoc )
+{
+ if (rExternDocName.isEmpty())
+ return !pExtInfo || !pExtInfo->mbExternal;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ if (pRefMgr->isOwnDocument( rExternDocName))
+ {
+ // This is an internal document. Get the sheet positions from the
+ // ScDocument instance.
+ if (!rStartTabName.isEmpty())
+ {
+ SCTAB nTab;
+ if (rDoc.GetTable(rStartTabName, nTab))
+ rRange.aStart.SetTab(nTab);
+ }
+
+ if (!rEndTabName.isEmpty())
+ {
+ SCTAB nTab;
+ if (rDoc.GetTable(rEndTabName, nTab))
+ rRange.aEnd.SetTab(nTab);
+ }
+ return !pExtInfo || !pExtInfo->mbExternal;
+ }
+
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
+
+ if (pExtInfo)
+ {
+ if (pExtInfo->mbExternal)
+ {
+ if (pExtInfo->mnFileId != nFileId)
+ return false;
+ }
+ else
+ {
+ pExtInfo->mbExternal = true;
+ pExtInfo->maTabName = rStartTabName;
+ pExtInfo->mnFileId = nFileId;
+ }
+ }
+
+ if (rEndTabName.isEmpty() || rStartTabName == rEndTabName)
+ {
+ rRange.aEnd.SetTab( rRange.aStart.Tab());
+ return true;
+ }
+
+ SCTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
+ if (nSpan == -1)
+ rFlags &= ~ScRefFlags(ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID);
+ else if (nSpan == 0)
+ rFlags &= ~ScRefFlags::TAB2_VALID;
+ else if (nSpan >= 1)
+ rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
+ else // (nSpan < -1)
+ {
+ rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
+ if (pExtInfo)
+ pExtInfo->maTabName = rEndTabName;
+ }
+ return true;
+}
+
+/** Returns NULL if the string should be a sheet name, but is invalid.
+ Returns a pointer to the first character after the sheet name, if there was
+ any, else pointer to start.
+ @param pMsoxlQuoteStop
+ Starting _within_ a quoted name, but still may be 3D; quoted name stops
+ at pMsoxlQuoteStop
+ */
+static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
+ OUString& rExternTabName,
+ bool bAllow3D,
+ const sal_Unicode* pMsoxlQuoteStop,
+ const OUString* pErrRef )
+{
+ OUString aTabName;
+ const sal_Unicode *p = start;
+
+ // XL only seems to use single quotes for sheet names.
+ if (pMsoxlQuoteStop)
+ {
+ const sal_Unicode* pCurrentStart = p;
+ while (p < pMsoxlQuoteStop)
+ {
+ if (*p == '\'')
+ {
+ // We pre-analyzed the quoting, no checks needed here.
+ if (*++p == '\'')
+ {
+ aTabName += std::u16string_view( pCurrentStart,
+ sal::static_int_cast<sal_Int32>( p - pCurrentStart));
+ pCurrentStart = ++p;
+ }
+ }
+ else if (*p == ':')
+ {
+ break; // while
+ }
+ else
+ ++p;
+ }
+ if (pCurrentStart < p)
+ aTabName += std::u16string_view( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart));
+ if (aTabName.isEmpty())
+ return nullptr;
+ if (p == pMsoxlQuoteStop)
+ ++p; // position on ! of ...'!...
+ if( *p != '!' && ( !bAllow3D || *p != ':' ) )
+ return (!bAllow3D && *p == ':') ? p : start;
+ }
+ else if( *p == '\'')
+ {
+ p = parseQuotedName(p, aTabName);
+ if (aTabName.isEmpty())
+ return nullptr;
+ }
+ else if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '!')
+ {
+ p += pErrRef->getLength(); // position after "#REF!" on '!'
+ // XXX NOTE: caller has to check the name and that it wasn't quoted.
+ aTabName = *pErrRef;
+ }
+ else
+ {
+ bool only_digits = true;
+
+ /*
+ * Valid: Normal!a1
+ * Valid: x.y!a1
+ * Invalid: .y!a1
+ *
+ * Some names starting with digits are actually valid, but
+ * unparse quoted. Things are quite tricky: most sheet names
+ * starting with a digit are ok, but not those starting with
+ * "[0-9]*\." or "[0-9]+[eE]".
+ *
+ * Valid: 42!a1
+ * Valid: 4x!a1
+ * Invalid: 1.!a1
+ * Invalid: 1e!a1
+ */
+ while( true )
+ {
+ const sal_Unicode uc = *p;
+ if( rtl::isAsciiAlpha( uc ) || uc == '_' )
+ {
+ if( only_digits && p != start &&
+ (uc == 'e' || uc == 'E' ) )
+ {
+ p = start;
+ break;
+ }
+ only_digits = false;
+ p++;
+ }
+ else if( rtl::isAsciiDigit( uc ))
+ {
+ p++;
+ }
+ else if( uc == '.' )
+ {
+ if( only_digits ) // Valid, except after only digits.
+ {
+ p = start;
+ break;
+ }
+ p++;
+ }
+ else if (uc > 127)
+ {
+ // non ASCII character is allowed.
+ ++p;
+ }
+ else
+ break;
+ }
+
+ if( *p != '!' && ( !bAllow3D || *p != ':' ) )
+ return (!bAllow3D && *p == ':') ? p : start;
+
+ aTabName += std::u16string_view( start, sal::static_int_cast<sal_Int32>( p - start ) );
+ }
+
+ rExternTabName = aTabName;
+ return p;
+}
+
+/** Tries to obtain the external document index and replace by actual document
+ name.
+
+ @param ppErrRet
+ Contains the default pointer the caller would return if this method
+ returns FALSE, may be replaced by NULL for type or data errors.
+
+ @returns FALSE only if the input name is numeric and not within the index
+ sequence, or the link type cannot be determined or data mismatch. Returns
+ TRUE in all other cases, also when there is no index sequence or the input
+ name is not numeric.
+ */
+static bool lcl_XL_getExternalDoc( const sal_Unicode** ppErrRet, OUString& rExternDocName,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
+{
+ // 1-based, sequence starts with an empty element.
+ if (pExternalLinks && pExternalLinks->hasElements())
+ {
+ // A numeric "document name" is an index into the sequence.
+ if (CharClass::isAsciiNumeric( rExternDocName))
+ {
+ sal_Int32 i = rExternDocName.toInt32();
+ if (i < 0 || i >= pExternalLinks->getLength())
+ return false; // with default *ppErrRet
+ const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
+ switch (rInfo.Type)
+ {
+ case sheet::ExternalLinkType::DOCUMENT :
+ {
+ OUString aStr;
+ if (!(rInfo.Data >>= aStr))
+ {
+ SAL_INFO(
+ "sc.core",
+ "Data type mismatch for ExternalLinkInfo "
+ << i);
+ *ppErrRet = nullptr;
+ return false;
+ }
+ rExternDocName = aStr;
+ }
+ break;
+ case sheet::ExternalLinkType::SELF :
+ return false; // ???
+ case sheet::ExternalLinkType::SPECIAL :
+ // silently return nothing (do not assert), caller has to handle this
+ *ppErrRet = nullptr;
+ return false;
+ default:
+ SAL_INFO(
+ "sc.core",
+ "unhandled ExternalLinkType " << rInfo.Type
+ << " for index " << i);
+ *ppErrRet = nullptr;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+const sal_Unicode* ScRange::Parse_XL_Header(
+ const sal_Unicode* p,
+ const ScDocument& rDoc,
+ OUString& rExternDocName,
+ OUString& rStartTabName,
+ OUString& rEndTabName,
+ ScRefFlags& nFlags,
+ bool bOnlyAcceptSingle,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ const OUString* pErrRef )
+{
+ const sal_Unicode* startTabs, *start = p;
+ ScRefFlags nSaveFlags = nFlags;
+
+ // Is this an external reference ?
+ rStartTabName.clear();
+ rEndTabName.clear();
+ rExternDocName.clear();
+ const sal_Unicode* pMsoxlQuoteStop = nullptr;
+ if (*p == '[')
+ {
+ ++p;
+ // Only single quotes are correct, and a double single quote escapes a
+ // single quote text inside the quoted text.
+ if (*p == '\'')
+ {
+ p = parseQuotedName(p, rExternDocName);
+ if (*p != ']' || rExternDocName.isEmpty())
+ {
+ rExternDocName.clear();
+ return start;
+ }
+ }
+ else
+ {
+ // non-quoted file name.
+ p = ScGlobal::UnicodeStrChr( start+1, ']' );
+ if( p == nullptr )
+ return start;
+ rExternDocName += std::u16string_view( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) );
+ }
+ ++p;
+
+ const sal_Unicode* pErrRet = start;
+ if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
+ return pErrRet;
+
+ rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, rDoc.GetDocumentShell());
+ }
+ else if (*p == '\'')
+ {
+ // Sickness in Excel's ODF msoxl namespace:
+ // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
+ // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
+ // But, 'Sheet1'!B3 would also be a valid!
+ // Excel does not allow [ and ] characters in sheet names though.
+ // But, more sickness comes with MOOXML as there may be
+ // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
+ p = parseQuotedName(p, rExternDocName);
+ if (*p != '!')
+ {
+ rExternDocName.clear();
+ return start;
+ }
+ if (!rExternDocName.isEmpty())
+ {
+ sal_Int32 nOpen = rExternDocName.indexOf( '[');
+ if (nOpen == -1)
+ rExternDocName.clear();
+ else
+ {
+ sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1);
+ if (nClose == -1)
+ rExternDocName.clear();
+ else
+ {
+ rExternDocName = rExternDocName.copy(0, nClose);
+ rExternDocName = rExternDocName.replaceAt( nOpen, 1, u"");
+ pMsoxlQuoteStop = p - 1; // the ' quote char
+ // There may be embedded escaped quotes, just matching the
+ // doc name's length may not work.
+ for (p = start; *p != '['; ++p)
+ ;
+ for ( ; *p != ']'; ++p)
+ ;
+ ++p;
+
+ // Handle '[1]Sheet 4'!$A$1
+ if (nOpen == 0)
+ {
+ const sal_Unicode* pErrRet = start;
+ if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
+ return pErrRet;
+ }
+ }
+ }
+ }
+ if (rExternDocName.isEmpty())
+ p = start;
+ }
+
+ startTabs = p;
+ p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop, pErrRef);
+ if( nullptr == p )
+ return start; // invalid tab
+ if (bOnlyAcceptSingle && *p == ':')
+ return nullptr; // 3D
+ const sal_Unicode* startEndTabs = nullptr;
+ if( p != startTabs )
+ {
+ nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D | ScRefFlags::TAB_ABS;
+ if( *p == ':' ) // 3d ref
+ {
+ startEndTabs = p + 1;
+ p = lcl_XL_ParseSheetRef( startEndTabs, rEndTabName, false, pMsoxlQuoteStop, pErrRef);
+ if( p == nullptr )
+ {
+ nFlags = nSaveFlags;
+ return start; // invalid tab
+ }
+ nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
+ }
+ else
+ {
+ // If only one sheet is given, the full reference is still valid,
+ // only the second 3D flag is not set.
+ nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_ABS;
+ aEnd.SetTab( aStart.Tab() );
+ }
+
+ if( *p++ != '!' )
+ {
+ nFlags = nSaveFlags;
+ return start; // syntax error
+ }
+ else
+ p = lcl_eatWhiteSpace( p );
+ }
+ else
+ {
+ nFlags |= ScRefFlags::TAB_VALID | ScRefFlags::TAB2_VALID;
+ // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
+ }
+
+ if (!rExternDocName.isEmpty())
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ pRefMgr->convertToAbsName(rExternDocName);
+ }
+ else
+ {
+ // Internal reference.
+ if (rStartTabName.isEmpty())
+ {
+ nFlags = nSaveFlags;
+ return start;
+ }
+
+ SCTAB nTab;
+ if ((pErrRef && *startTabs != '\'' && rStartTabName == *pErrRef) || !rDoc.GetTable(rStartTabName, nTab))
+ {
+ // invalid table name.
+ nFlags &= ~ScRefFlags::TAB_VALID;
+ nTab = -1;
+ }
+
+ aStart.SetTab(nTab);
+ aEnd.SetTab(nTab);
+
+ if (!rEndTabName.isEmpty())
+ {
+ if ((pErrRef && startEndTabs && *startEndTabs != '\'' && rEndTabName == *pErrRef) ||
+ !rDoc.GetTable(rEndTabName, nTab))
+ {
+ // invalid table name.
+ nFlags &= ~ScRefFlags::TAB2_VALID;
+ nTab = -1;
+ }
+
+ aEnd.SetTab(nTab);
+ }
+ }
+ return p;
+}
+
+static const sal_Unicode* lcl_r1c1_get_col( const ScSheetLimits& rSheetLimits,
+ const sal_Unicode* p,
+ const ScAddress::Details& rDetails,
+ ScAddress* pAddr, ScRefFlags* nFlags )
+{
+ const sal_Unicode *pEnd;
+ sal_Int64 n;
+ bool isRelative;
+
+ if( p[0] == '\0' )
+ return nullptr;
+
+ p++;
+ isRelative = *p == '[';
+ if( isRelative )
+ p++;
+ n = sal_Unicode_strtol( p, &pEnd );
+ if( nullptr == pEnd )
+ return nullptr;
+
+ if( p == pEnd ) // C is a relative ref with offset 0
+ {
+ if( isRelative )
+ return nullptr;
+ n = rDetails.nCol;
+ }
+ else if( isRelative )
+ {
+ if( *pEnd != ']' )
+ return nullptr;
+ n += rDetails.nCol;
+ pEnd++;
+ }
+ else
+ {
+ *nFlags |= ScRefFlags::COL_ABS;
+ n--;
+ }
+
+ if( n < 0 || n >= rSheetLimits.GetMaxColCount())
+ return nullptr;
+ pAddr->SetCol( static_cast<SCCOL>( n ) );
+ *nFlags |= ScRefFlags::COL_VALID;
+
+ return pEnd;
+}
+
+static const sal_Unicode* lcl_r1c1_get_row(
+ const ScSheetLimits& rSheetLimits,
+ const sal_Unicode* p,
+ const ScAddress::Details& rDetails,
+ ScAddress* pAddr, ScRefFlags* nFlags )
+{
+ const sal_Unicode *pEnd;
+ bool isRelative;
+
+ if( p[0] == '\0' )
+ return nullptr;
+
+ p++;
+ isRelative = *p == '[';
+ if( isRelative )
+ p++;
+ sal_Int64 n = sal_Unicode_strtol( p, &pEnd );
+ if( nullptr == pEnd )
+ return nullptr;
+
+ if( p == pEnd ) // R is a relative ref with offset 0
+ {
+ if( isRelative )
+ return nullptr;
+ n = rDetails.nRow;
+ }
+ else if( isRelative )
+ {
+ if( *pEnd != ']' )
+ return nullptr;
+ n += rDetails.nRow;
+ pEnd++;
+ }
+ else
+ {
+ *nFlags |= ScRefFlags::ROW_ABS;
+ n--;
+ }
+
+ if( n < 0 || n >= rSheetLimits.GetMaxRowCount() )
+ return nullptr;
+ pAddr->SetRow( static_cast<SCROW>( n ) );
+ *nFlags |= ScRefFlags::ROW_VALID;
+
+ return pEnd;
+}
+
+static ScRefFlags lcl_ScRange_Parse_XL_R1C1( ScRange& r,
+ const sal_Unicode* p,
+ const ScDocument& rDoc,
+ const ScAddress::Details& rDetails,
+ bool bOnlyAcceptSingle,
+ ScAddress::ExternalInfo* pExtInfo,
+ sal_Int32* pSheetEndPos )
+{
+ const sal_Unicode* const pStart = p;
+ if (pSheetEndPos)
+ *pSheetEndPos = 0;
+ const sal_Unicode* pTmp = nullptr;
+ OUString aExternDocName, aStartTabName, aEndTabName;
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID;
+ // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
+ ScRefFlags nFlags2 = ScRefFlags::TAB_VALID;
+
+ p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
+ aEndTabName, nFlags, bOnlyAcceptSingle );
+
+ ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
+ if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
+ {
+ *pSheetEndPos = p - pStart;
+ nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
+ }
+
+ if (!aExternDocName.isEmpty())
+ lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
+ aStartTabName, aEndTabName, rDoc);
+
+ if( nullptr == p )
+ return ScRefFlags::ZERO;
+
+ if( *p == 'R' || *p == 'r' )
+ {
+ if( nullptr == (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )) )
+ return nBailOutFlags;
+
+ if( *p != 'C' && *p != 'c' ) // full row R#
+ {
+ if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
+ nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )))
+ {
+ // Only the initial row number is given, or the second row
+ // number is invalid. Fallback to just the initial R
+ applyStartToEndFlags(nFlags);
+ r.aEnd.SetRow( r.aStart.Row() );
+ }
+ else // pTmp != nullptr
+ {
+ // Full row range successfully parsed.
+ applyStartToEndFlags(nFlags, nFlags2);
+ p = pTmp;
+ }
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ nFlags |=
+ ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
+ ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
+ r.aStart.SetCol( 0 );
+ r.aEnd.SetCol( rDoc.MaxCol() );
+
+ return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
+ }
+ else if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )))
+ {
+ return ScRefFlags::ZERO;
+ }
+
+ if( p[0] != ':' ||
+ (p[1] != 'R' && p[1] != 'r') ||
+ nullptr == (pTmp = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )) ||
+ (*pTmp != 'C' && *pTmp != 'c') ||
+ nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), pTmp, rDetails, &r.aEnd, &nFlags2 )))
+ {
+ // single cell reference
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
+ return nFlags;
+ }
+
+ return bOnlyAcceptSingle ? nFlags : ScRefFlags::ZERO;
+ }
+ assert(pTmp);
+ p = pTmp;
+
+ // double reference
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole range.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ applyStartToEndFlags(nFlags, nFlags2);
+ return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
+ }
+ else if( *p == 'C' || *p == 'c' ) // full col C#
+ {
+ if( nullptr == (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &r.aStart, &nFlags )))
+ return nBailOutFlags;
+
+ if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
+ nullptr == (pTmp = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &r.aEnd, &nFlags2 )))
+ { // Fallback to just the initial C
+ applyStartToEndFlags(nFlags);
+ r.aEnd.SetCol( r.aStart.Col() );
+ }
+ else // pTmp != nullptr
+ {
+ applyStartToEndFlags(nFlags, nFlags2);
+ p = pTmp;
+ }
+
+ if (p[0] != 0)
+ {
+ // any trailing invalid character must invalidate the whole address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ nFlags |=
+ ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
+ ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
+ r.aStart.SetRow( 0 );
+ r.aEnd.SetRow( rDoc.MaxRow() );
+
+ return bOnlyAcceptSingle ? ScRefFlags::ZERO : nFlags;
+ }
+
+ return nBailOutFlags;
+}
+
+static const sal_Unicode* lcl_a1_get_col( const ScDocument& rDoc,
+ const sal_Unicode* p,
+ ScAddress* pAddr,
+ ScRefFlags* nFlags,
+ const OUString* pErrRef )
+{
+ if( *p == '$' )
+ {
+ *nFlags |= ScRefFlags::COL_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ p += pErrRef->getLength();
+ *nFlags &= ~ScRefFlags::COL_VALID;
+ pAddr->SetCol(-1);
+ return p;
+ }
+
+ if( !rtl::isAsciiAlpha( *p ) )
+ return nullptr;
+
+ sal_Int64 nCol = rtl::toAsciiUpperCase( *p++ ) - 'A';
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ while (nCol <= nMaxCol && rtl::isAsciiAlpha(*p))
+ nCol = ((nCol + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
+ if( nCol > nMaxCol || nCol < 0 || rtl::isAsciiAlpha( *p ) )
+ return nullptr;
+
+ *nFlags |= ScRefFlags::COL_VALID;
+ pAddr->SetCol( sal::static_int_cast<SCCOL>( nCol ));
+
+ return p;
+}
+
+static const sal_Unicode* lcl_a1_get_row( const ScDocument& rDoc,
+ const sal_Unicode* p,
+ ScAddress* pAddr,
+ ScRefFlags* nFlags,
+ const OUString* pErrRef )
+{
+ const sal_Unicode *pEnd;
+
+ if( *p == '$' )
+ {
+ *nFlags |= ScRefFlags::ROW_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ p += pErrRef->getLength();
+ *nFlags &= ~ScRefFlags::ROW_VALID;
+ pAddr->SetRow(-1);
+ return p;
+ }
+
+ sal_Int64 n = sal_Unicode_strtol( p, &pEnd ) - 1;
+ if( nullptr == pEnd || p == pEnd || n < 0 || n > rDoc.MaxRow() )
+ return nullptr;
+
+ *nFlags |= ScRefFlags::ROW_VALID;
+ pAddr->SetRow( sal::static_int_cast<SCROW>(n) );
+
+ return pEnd;
+}
+
+/// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
+static bool isValidSingleton( ScRefFlags nFlags, ScRefFlags nFlags2 )
+{
+ bool bCols = (nFlags & ScRefFlags::COL_VALID) && ((nFlags & ScRefFlags::COL2_VALID) || (nFlags2 & ScRefFlags::COL_VALID));
+ bool bRows = (nFlags & ScRefFlags::ROW_VALID) && ((nFlags & ScRefFlags::ROW2_VALID) || (nFlags2 & ScRefFlags::ROW_VALID));
+ return bCols != bRows;
+}
+
+static ScRefFlags lcl_ScRange_Parse_XL_A1( ScRange& r,
+ const sal_Unicode* p,
+ const ScDocument& rDoc,
+ bool bOnlyAcceptSingle,
+ ScAddress::ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ const sal_Unicode* const pStart = p;
+ if (pSheetEndPos)
+ *pSheetEndPos = 0;
+ const sal_Unicode* tmp1, *tmp2;
+ OUString aExternDocName, aStartTabName, aEndTabName; // for external link table
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID, nFlags2 = ScRefFlags::TAB_VALID;
+
+ p = r.Parse_XL_Header( p, rDoc, aExternDocName, aStartTabName,
+ aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks, pErrRef );
+
+ ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
+ if (pSheetEndPos && pStart < p && (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
+ {
+ *pSheetEndPos = p - pStart;
+ nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
+ }
+
+ if (!aExternDocName.isEmpty())
+ lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
+ aStartTabName, aEndTabName, rDoc);
+
+ if( nullptr == p )
+ return nBailOutFlags;
+
+ tmp1 = lcl_a1_get_col( rDoc, p, &r.aStart, &nFlags, pErrRef);
+ if( tmp1 == nullptr ) // Is it a row only reference 3:5
+ {
+ if( bOnlyAcceptSingle ) // by definition full row refs are ranges
+ return nBailOutFlags;
+
+ tmp1 = lcl_a1_get_row( rDoc, p, &r.aStart, &nFlags, pErrRef);
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
+ return nBailOutFlags;
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton.
+ return nBailOutFlags;
+
+ r.aStart.SetCol( 0 ); r.aEnd.SetCol( rDoc.MaxCol() );
+ nFlags |=
+ ScRefFlags::COL_VALID | ScRefFlags::COL2_VALID |
+ ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
+ applyStartToEndFlags(nFlags, nFlags2);
+ return nFlags;
+ }
+
+ tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aStart, &nFlags, pErrRef);
+ if( tmp2 == nullptr ) // check for col only reference F:H
+ {
+ if( bOnlyAcceptSingle ) // by definition full col refs are ranges
+ return nBailOutFlags;
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F)
+ return nBailOutFlags;
+
+ tmp1 = lcl_eatWhiteSpace( tmp1 );
+ tmp2 = lcl_a1_get_col( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton.
+ return nBailOutFlags;
+
+ r.aStart.SetRow( 0 ); r.aEnd.SetRow( rDoc.MaxRow() );
+ nFlags |=
+ ScRefFlags::ROW_VALID | ScRefFlags::ROW2_VALID |
+ ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
+ applyStartToEndFlags(nFlags, nFlags2);
+ return nFlags;
+ }
+
+ // prepare as if it's a singleton, in case we want to fall back */
+ r.aEnd.SetCol( r.aStart.Col() );
+ r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
+
+ if ( bOnlyAcceptSingle )
+ {
+ if ( *tmp2 == 0 )
+ return nFlags;
+ else
+ {
+ // any trailing invalid character must invalidate the address.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID);
+ return nFlags;
+ }
+ }
+
+ tmp2 = lcl_eatWhiteSpace( tmp2 );
+ if( *tmp2 != ':' )
+ {
+ // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
+ // not. Any trailing invalid character invalidates the range.
+ if (*tmp2 == 0 && (nFlags & ScRefFlags::TAB2_3D))
+ {
+ if (nFlags & ScRefFlags::COL_ABS)
+ nFlags |= ScRefFlags::COL2_ABS;
+ if (nFlags & ScRefFlags::ROW_ABS)
+ nFlags |= ScRefFlags::ROW2_ABS;
+ }
+ else
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID |
+ ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ p = lcl_eatWhiteSpace( tmp2+1 ); // after ':'
+ tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range
+ {
+ p = lcl_XL_ParseSheetRef( p, aEndTabName, false, nullptr, pErrRef);
+ if( p )
+ {
+ SCTAB nTab = 0;
+ if( !aEndTabName.isEmpty() && rDoc.GetTable( aEndTabName, nTab ) )
+ {
+ r.aEnd.SetTab( nTab );
+ nFlags |= ScRefFlags::TAB2_VALID | ScRefFlags::TAB2_3D | ScRefFlags::TAB2_ABS;
+ }
+ if (*p == '!' || *p == ':')
+ p = lcl_eatWhiteSpace( p+1 );
+ tmp1 = lcl_a1_get_col( rDoc, p, &r.aEnd, &nFlags2, pErrRef);
+ }
+ }
+ if( !tmp1 ) // strange, but maybe valid singleton
+ return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
+
+ tmp2 = lcl_a1_get_row( rDoc, tmp1, &r.aEnd, &nFlags2, pErrRef);
+ if( !tmp2 ) // strange, but maybe valid singleton
+ return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~ScRefFlags::VALID);
+
+ if ( *tmp2 != 0 )
+ {
+ // any trailing invalid character must invalidate the range.
+ nFlags &= ~ScRefFlags(ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID | ScRefFlags::TAB_VALID |
+ ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID);
+ return nFlags;
+ }
+
+ applyStartToEndFlags(nFlags, nFlags2);
+ return nFlags;
+}
+
+/**
+ @param p pointer to null-terminated sal_Unicode string
+ @param rRawRes returns ScRefFlags::... flags without the final check for full
+ validity that is applied to the return value, with which
+ two addresses that form a column or row singleton range,
+ e.g. A:A or 1:1, can be detected. Used in
+ lcl_ScRange_Parse_OOo().
+ @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
+ used in conjunction with pExtInfo to determine the tab span
+ of a 3D reference.
+ */
+static ScRefFlags lcl_ScAddress_Parse_OOo( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
+ ScRefFlags& rRawRes,
+ ScAddress::ExternalInfo* pExtInfo,
+ ScRange* pRange,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ const sal_Unicode* const pStart = p;
+ if (pSheetEndPos)
+ *pSheetEndPos = 0;
+ ScRefFlags nRes = ScRefFlags::ZERO;
+ rRawRes = ScRefFlags::ZERO;
+ OUString aDocName; // the pure Document Name
+ OUString aTab;
+ bool bExtDoc = false;
+ bool bExtDocInherited = false;
+
+ // Lets see if this is a reference to something in an external file. A
+ // document name is always quoted and has a trailing #.
+ if (*p == '\'')
+ {
+ OUString aTmp;
+ p = parseQuotedName(p, aTmp);
+ aDocName = aTmp;
+ if (*p++ == SC_COMPILER_FILE_TAB_SEP)
+ bExtDoc = true;
+ else
+ // This is not a document name. Perhaps a quoted relative table
+ // name.
+ p = pStart;
+ }
+ else if (pExtInfo && pExtInfo->mbExternal)
+ {
+ // This is an external reference.
+ bExtDoc = bExtDocInherited = true;
+ }
+
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ SCTAB nTab = 0;
+ ScRefFlags nBailOutFlags = ScRefFlags::ZERO;
+ ScRefFlags nBits = ScRefFlags::TAB_VALID;
+ const sal_Unicode* q;
+ if ( ScGlobal::FindUnquoted( p, '.') )
+ {
+ nRes |= ScRefFlags::TAB_3D;
+ if ( bExtDoc )
+ nRes |= ScRefFlags::TAB_ABS;
+ if (*p == '$')
+ {
+ nRes |= ScRefFlags::TAB_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef) && p[pErrRef->getLength()] == '.')
+ {
+ // #REF! particle of an invalidated reference plus sheet separator.
+ p += pErrRef->getLength() + 1;
+ nRes &= ~ScRefFlags::TAB_VALID;
+ nTab = -1;
+ }
+ else
+ {
+ if (*p == '\'')
+ {
+ // Tokens that start at ' can have anything in them until a final
+ // ' but '' marks an escaped '. We've earlier guaranteed that a
+ // string containing '' will be surrounded by '.
+ p = parseQuotedName(p, aTab);
+ }
+ else
+ {
+ OUStringBuffer aTabAcc;
+ while (*p)
+ {
+ if( *p == '.')
+ break;
+
+ if( *p == '\'' )
+ {
+ p++; break;
+ }
+ aTabAcc.append(*p);
+ p++;
+ }
+ aTab = aTabAcc.makeStringAndClear();
+ }
+ if (*p != '.')
+ nBits = ScRefFlags::ZERO;
+ else
+ {
+ ++p;
+ if (!bExtDoc && !rDoc.GetTable( aTab, nTab ))
+ nBits = ScRefFlags::ZERO;
+ }
+ }
+
+ if (pSheetEndPos && (nBits & ScRefFlags::TAB_VALID))
+ {
+ *pSheetEndPos = p - pStart;
+ nBailOutFlags = ScRefFlags::TAB_VALID | ScRefFlags::TAB_3D;
+ }
+ }
+ else
+ {
+ if (bExtDoc && !bExtDocInherited)
+ return nRes; // After a document a sheet must follow.
+ nTab = rAddr.Tab();
+ }
+ nRes |= nBits;
+
+ q = p;
+ if (*p)
+ {
+ nBits = ScRefFlags::COL_VALID;
+ if (*p == '$')
+ {
+ nBits |= ScRefFlags::COL_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ // #REF! particle of an invalidated reference.
+ p += pErrRef->getLength();
+ nBits &= ~ScRefFlags::COL_VALID;
+ nCol = -1;
+ }
+ else
+ {
+ if (rtl::isAsciiAlpha( *p ))
+ {
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ sal_Int64 n = rtl::toAsciiUpperCase( *p++ ) - 'A';
+ while (n < nMaxCol && rtl::isAsciiAlpha(*p))
+ n = ((n + 1) * 26) + rtl::toAsciiUpperCase( *p++ ) - 'A';
+ if (n > nMaxCol || n < 0 || (*p && *p != '$' && !rtl::isAsciiDigit( *p ) &&
+ (!pErrRef || !lcl_isString( p, *pErrRef))))
+ nBits = ScRefFlags::ZERO;
+ else
+ nCol = sal::static_int_cast<SCCOL>( n );
+ }
+ else
+ nBits = ScRefFlags::ZERO;
+
+ if( nBits == ScRefFlags::ZERO )
+ p = q;
+ }
+ nRes |= nBits;
+ }
+
+ q = p;
+ if (*p)
+ {
+ nBits = ScRefFlags::ROW_VALID;
+ if (*p == '$')
+ {
+ nBits |= ScRefFlags::ROW_ABS;
+ p++;
+ }
+
+ if (pErrRef && lcl_isString( p, *pErrRef))
+ {
+ // #REF! particle of an invalidated reference.
+ p += pErrRef->getLength();
+ // Clearing the ROW_VALID bit here is not possible because of the
+ // check at the end whether only a valid column was detected in
+ // which case all bits are cleared because it could be any other
+ // name. Instead, set to an absolute invalid row value. This will
+ // display a $#REF! instead of #REF! if the error value was
+ // relative, but live with it.
+ nBits |= ScRefFlags::ROW_ABS;
+ nRow = -1;
+ }
+ else
+ {
+ if( !rtl::isAsciiDigit( *p ) )
+ {
+ nBits = ScRefFlags::ZERO;
+ nRow = -1;
+ }
+ else
+ {
+ sal_Int64 n = rtl_ustr_toInt32( p, 10 ) - 1;
+ while (rtl::isAsciiDigit( *p ))
+ p++;
+ const SCROW nMaxRow = rDoc.MaxRow();
+ if( n < 0 || n > nMaxRow )
+ nBits = ScRefFlags::ZERO;
+ nRow = sal::static_int_cast<SCROW>(n);
+ }
+ if( nBits == ScRefFlags::ZERO )
+ p = q;
+ }
+ nRes |= nBits;
+ }
+
+ rAddr.Set( nCol, nRow, nTab );
+
+ if (!*p && bExtDoc)
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+
+ // Need document name if inherited.
+ if (bExtDocInherited)
+ {
+ // The FileId was created using the original file name, so
+ // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
+ // retrieve a FileId for the real name and bail out if that
+ // differed from pExtInfo->mnFileId, as is the case when
+ // loading documents that refer external files relative to the
+ // current own document but were saved from a different path
+ // than loaded.
+ const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true);
+ if (pFileName)
+ aDocName = *pFileName;
+ else
+ nRes = ScRefFlags::ZERO;
+ }
+ pRefMgr->convertToAbsName(aDocName);
+
+ if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
+ {
+ if (!rDoc.GetTable( aTab, nTab ))
+ nRes = ScRefFlags::ZERO;
+ else
+ {
+ rAddr.SetTab( nTab);
+ nRes |= ScRefFlags::TAB_VALID;
+ }
+ }
+ else
+ {
+ if (!pExtInfo)
+ nRes = ScRefFlags::ZERO;
+ else
+ {
+ if (!pExtInfo->mbExternal)
+ {
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
+
+ pExtInfo->mbExternal = true;
+ pExtInfo->maTabName = aTab;
+ pExtInfo->mnFileId = nFileId;
+
+ if (pRefMgr->getSingleRefToken(nFileId, aTab,
+ ScAddress(nCol, nRow, 0), nullptr,
+ &nTab))
+ {
+ rAddr.SetTab( nTab);
+ nRes |= ScRefFlags::TAB_VALID;
+ }
+ else
+ nRes = ScRefFlags::ZERO;
+ }
+ else
+ {
+ // This is a call for the second part of the reference,
+ // we must have the range to adapt tab span.
+ if (!pRange)
+ nRes = ScRefFlags::ZERO;
+ else
+ {
+ ScRefFlags nFlags = nRes | ScRefFlags::TAB2_VALID;
+ if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
+ pExtInfo, aDocName,
+ pExtInfo->maTabName, aTab, rDoc))
+ nRes &= ~ScRefFlags::TAB_VALID;
+ else
+ {
+ if (nFlags & ScRefFlags::TAB2_VALID)
+ {
+ rAddr.SetTab( pRange->aEnd.Tab());
+ nRes |= ScRefFlags::TAB_VALID;
+ }
+ else
+ nRes &= ~ScRefFlags::TAB_VALID;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (bExtDoc && pExtInfo && !bExtDocInherited && !pExtInfo->mbExternal && pSheetEndPos)
+ {
+ // Pass partial info up to caller, there may be an external name
+ // following, and if after *pSheetEndPos it's external sheet-local.
+ // For global names aTab is empty and *pSheetEndPos==0.
+ pExtInfo->mbExternal = true;
+ pExtInfo->maTabName = aTab;
+ pExtInfo->mnFileId = rDoc.GetExternalRefManager()->getExternalFileId(aDocName);
+ }
+
+ rRawRes |= nRes;
+
+ if ( !(nRes & ScRefFlags::ROW_VALID) && (nRes & ScRefFlags::COL_VALID)
+ && !( (nRes & ScRefFlags::TAB_3D) && (nRes & ScRefFlags::TAB_VALID)) )
+ { // no Row, no Tab, but Col => DM (...), B (...) et al
+ nRes = ScRefFlags::ZERO;
+ }
+ if( !*p )
+ {
+ ScRefFlags nMask = nRes & ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID );
+ if( nMask == ( ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID ) )
+ nRes |= ScRefFlags::VALID;
+ }
+ else
+ nRes = rRawRes = nBailOutFlags;
+ return nRes;
+}
+
+static ScRefFlags lcl_ScAddress_Parse ( const sal_Unicode* p, const ScDocument& rDoc, ScAddress& rAddr,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ if( !*p )
+ return ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ {
+ ScRange rRange = rAddr;
+ ScRefFlags nFlags = lcl_ScRange_Parse_XL_A1(
+ rRange, p, rDoc, true, pExtInfo,
+ (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
+ pSheetEndPos, pErrRef);
+ rAddr = rRange.aStart;
+ return nFlags;
+ }
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ {
+ ScRange rRange = rAddr;
+ ScRefFlags nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, rDoc, rDetails, true, pExtInfo, pSheetEndPos);
+ rAddr = rRange.aStart;
+ return nFlags;
+ }
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ {
+ ScRefFlags nRawRes = ScRefFlags::ZERO;
+ return lcl_ScAddress_Parse_OOo( p, rDoc, rAddr, nRawRes, pExtInfo, nullptr, pSheetEndPos, pErrRef);
+ }
+ }
+}
+
+bool ConvertSingleRef( const ScDocument& rDoc, const OUString& rRefString,
+ SCTAB nDefTab, ScRefAddress& rRefAddress,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
+{
+ bool bRet = false;
+ if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
+ {
+ ScAddress aAddr( 0, 0, nDefTab );
+ ScRefFlags nRes = aAddr.Parse( rRefString, rDoc, rDetails, pExtInfo);
+ if ( nRes & ScRefFlags::VALID )
+ {
+ rRefAddress.Set( aAddr,
+ ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool ConvertDoubleRef( const ScDocument& rDoc, const OUString& rRefString, SCTAB nDefTab,
+ ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
+{
+ bool bRet = false;
+ if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
+ {
+ ScRange aRange( ScAddress( 0, 0, nDefTab));
+ ScRefFlags nRes = aRange.Parse( rRefString, rDoc, rDetails, pExtInfo);
+ if ( nRes & ScRefFlags::VALID )
+ {
+ rStartRefAddress.Set( aRange.aStart,
+ ((nRes & ScRefFlags::COL_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO));
+ rEndRefAddress.Set( aRange.aEnd,
+ ((nRes & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO),
+ ((nRes & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO));
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+ScRefFlags ScAddress::Parse( const OUString& r, const ScDocument& rDoc,
+ const Details& rDetails,
+ ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ sal_Int32* pSheetEndPos,
+ const OUString* pErrRef )
+{
+ return lcl_ScAddress_Parse( r.getStr(), rDoc, *this, rDetails, pExtInfo, pExternalLinks, pSheetEndPos, pErrRef);
+}
+
+ScRange ScRange::Intersection( const ScRange& rOther ) const
+{
+ SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
+ SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
+ SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
+ SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
+ SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
+ SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
+
+ if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
+ return ScRange(ScAddress::INITIALIZE_INVALID);
+
+ return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+}
+
+void ScRange::ExtendTo( const ScRange& rRange )
+{
+ OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
+ if( IsValid() )
+ {
+ aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
+ aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
+ aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
+ aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) );
+ aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) );
+ aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
+ }
+ else
+ *this = rRange;
+}
+
+static ScRefFlags lcl_ScRange_Parse_OOo( ScRange& rRange,
+ const OUString& r,
+ const ScDocument& rDoc,
+ ScAddress::ExternalInfo* pExtInfo,
+ const OUString* pErrRef )
+{
+ ScRefFlags nRes1 = ScRefFlags::ZERO, nRes2 = ScRefFlags::ZERO;
+ sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
+ if (nPos != -1)
+ {
+ OUStringBuffer aTmp(r);
+ aTmp[nPos] = 0;
+ const sal_Unicode* p = aTmp.getStr();
+ ScRefFlags nRawRes1 = ScRefFlags::ZERO;
+ nRes1 = lcl_ScAddress_Parse_OOo( p, rDoc, rRange.aStart, nRawRes1, pExtInfo, nullptr, nullptr, pErrRef);
+ if ((nRes1 != ScRefFlags::ZERO) ||
+ ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
+ (nRawRes1 & ScRefFlags::TAB_VALID)))
+ {
+ rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet
+ ScRefFlags nRawRes2 = ScRefFlags::ZERO;
+ nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, rDoc, rRange.aEnd, nRawRes2,
+ pExtInfo, &rRange, nullptr, pErrRef);
+ if (!((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID)) &&
+ // If not fully valid addresses, check if both have a valid
+ // column or row, and both have valid (or omitted) sheet references.
+ (nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
+ (nRawRes1 & ScRefFlags::TAB_VALID) &&
+ (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) &&
+ (nRawRes2 & ScRefFlags::TAB_VALID) &&
+ // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
+ ((nRawRes1 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID)) ==
+ (nRawRes2 & (ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID))))
+ {
+ nRes1 = nRawRes1 | ScRefFlags::VALID;
+ nRes2 = nRawRes2 | ScRefFlags::VALID;
+ if (nRawRes1 & ScRefFlags::COL_VALID)
+ {
+ rRange.aStart.SetRow(0);
+ rRange.aEnd.SetRow(rDoc.MaxRow());
+ nRes1 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
+ nRes2 |= ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS;
+ }
+ else
+ {
+ rRange.aStart.SetCol(0);
+ rRange.aEnd.SetCol( rDoc.MaxCol() );
+ nRes1 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
+ nRes2 |= ScRefFlags::COL_VALID | ScRefFlags::COL_ABS;
+ }
+ }
+ else if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
+ {
+ // Flag entire column/row references so they can be displayed
+ // as such. If the sticky reference parts are not both
+ // absolute or relative, assume that the user thought about
+ // something we should not touch.
+ if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() &&
+ ((nRes1 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO) &&
+ ((nRes2 & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO))
+ {
+ nRes1 |= ScRefFlags::ROW_ABS;
+ nRes2 |= ScRefFlags::ROW_ABS;
+ }
+ else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
+ ((nRes1 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO) && ((nRes2 & ScRefFlags::COL_ABS) == ScRefFlags::ZERO))
+ {
+ nRes1 |= ScRefFlags::COL_ABS;
+ nRes2 |= ScRefFlags::COL_ABS;
+ }
+ }
+ if ((nRes1 & ScRefFlags::VALID) && (nRes2 & ScRefFlags::VALID))
+ {
+ // PutInOrder / Justify
+ ScRefFlags nMask, nBits1, nBits2;
+ SCCOL nTempCol;
+ if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
+ {
+ rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
+ nMask = (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
+ nBits1 = nRes1 & nMask;
+ nBits2 = nRes2 & nMask;
+ nRes1 = (nRes1 & ~nMask) | nBits2;
+ nRes2 = (nRes2 & ~nMask) | nBits1;
+ }
+ SCROW nTempRow;
+ if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
+ {
+ rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
+ nMask = (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS);
+ nBits1 = nRes1 & nMask;
+ nBits2 = nRes2 & nMask;
+ nRes1 = (nRes1 & ~nMask) | nBits2;
+ nRes2 = (nRes2 & ~nMask) | nBits1;
+ }
+ SCTAB nTempTab;
+ if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
+ {
+ rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
+ nMask = (ScRefFlags::TAB_VALID | ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D);
+ nBits1 = nRes1 & nMask;
+ nBits2 = nRes2 & nMask;
+ nRes1 = (nRes1 & ~nMask) | nBits2;
+ nRes2 = (nRes2 & ~nMask) | nBits1;
+ }
+ if ( ((nRes1 & ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
+ == ( ScRefFlags::TAB_ABS | ScRefFlags::TAB_3D ))
+ && !(nRes2 & ScRefFlags::TAB_3D) )
+ nRes2 |= ScRefFlags::TAB_ABS;
+ }
+ else
+ {
+ // Don't leave around valid half references.
+ nRes1 = nRes2 = ScRefFlags::ZERO;
+ }
+ }
+ }
+ applyStartToEndFlags(nRes1, nRes2 & ScRefFlags::BITS);
+ nRes1 |= nRes2 & ScRefFlags::VALID;
+ return nRes1;
+}
+
+ScRefFlags ScRange::Parse( const OUString& rString, const ScDocument& rDoc,
+ const ScAddress::Details& rDetails,
+ ScAddress::ExternalInfo* pExtInfo,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks,
+ const OUString* pErrRef )
+{
+ if (rString.isEmpty())
+ return ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ {
+ return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), rDoc, false, pExtInfo,
+ (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : nullptr),
+ nullptr, pErrRef );
+ }
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ {
+ return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), rDoc, rDetails, false, pExtInfo, nullptr );
+ }
+
+ default:
+ case formula::FormulaGrammar::CONV_OOO:
+ {
+ return lcl_ScRange_Parse_OOo( *this, rString, rDoc, pExtInfo, pErrRef );
+ }
+ }
+}
+
+// Accept a full range, or an address
+ScRefFlags ScRange::ParseAny( const OUString& rString, const ScDocument& rDoc,
+ const ScAddress::Details& rDetails )
+{
+ ScRefFlags nRet = Parse( rString, rDoc, rDetails );
+ const ScRefFlags nValid = ScRefFlags::VALID | ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID;
+
+ if ( (nRet & nValid) != nValid )
+ {
+ ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
+ nRet = aAdr.Parse( rString, rDoc, rDetails );
+ if ( nRet & ScRefFlags::VALID )
+ aStart = aEnd = aAdr;
+ }
+ return nRet;
+}
+
+// Parse only full row references
+ScRefFlags ScRange::ParseCols( const ScDocument& rDoc,
+ const OUString& rStr,
+ const ScAddress::Details& rDetails )
+{
+ if (rStr.isEmpty())
+ return ScRefFlags::ZERO;
+
+ const sal_Unicode* p = rStr.getStr();
+ ScRefFlags nRes = ScRefFlags::ZERO;
+ ScRefFlags ignored = ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (nullptr != (p = lcl_a1_get_col( rDoc, p, &aStart, &ignored, nullptr) ) )
+ {
+ if( p[0] == ':')
+ {
+ if( nullptr != (p = lcl_a1_get_col( rDoc, p+1, &aEnd, &ignored, nullptr)))
+ {
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ else
+ {
+ aEnd = aStart;
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if ((p[0] == 'C' || p[0] == 'c') &&
+ nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
+ {
+ if( p[0] == ':')
+ {
+ if( (p[1] == 'C' || p[1] == 'c') &&
+ nullptr != (p = lcl_r1c1_get_col( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored )))
+ {
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ else
+ {
+ aEnd = aStart;
+ nRes = ScRefFlags::COL_VALID;
+ }
+ }
+ break;
+ }
+
+ return (p != nullptr && *p == '\0') ? nRes : ScRefFlags::ZERO;
+}
+
+// Parse only full row references
+void ScRange::ParseRows( const ScDocument& rDoc,
+ const OUString& rStr,
+ const ScAddress::Details& rDetails )
+{
+ if (rStr.isEmpty())
+ return;
+
+ const sal_Unicode* p = rStr.getStr();
+ ScRefFlags ignored = ScRefFlags::ZERO;
+
+ switch (rDetails.eConv)
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (nullptr != (p = lcl_a1_get_row( rDoc, p, &aStart, &ignored, nullptr) ) )
+ {
+ if( p[0] == ':')
+ {
+ lcl_a1_get_row( rDoc, p+1, &aEnd, &ignored, nullptr);
+ }
+ else
+ {
+ aEnd = aStart;
+ }
+ }
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if ((p[0] == 'R' || p[0] == 'r') &&
+ nullptr != (p = lcl_r1c1_get_row( rDoc.GetSheetLimits(), p, rDetails, &aStart, &ignored )))
+ {
+ if( p[0] == ':')
+ {
+ if( p[1] == 'R' || p[1] == 'r' )
+ {
+ lcl_r1c1_get_row( rDoc.GetSheetLimits(), p+1, rDetails, &aEnd, &ignored );
+ }
+ }
+ else
+ {
+ aEnd = aStart;
+ }
+ }
+ break;
+ }
+}
+
+template<typename T > static void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
+{
+ if (nCol < 26*26)
+ {
+ if (nCol < 26)
+ rBuf.append( static_cast<char>( 'A' + nCol ));
+ else
+ {
+ rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
+ rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
+ }
+ }
+ else
+ {
+ sal_Int32 nInsert = rBuf.getLength();
+ while (nCol >= 26)
+ {
+ SCCOL nC = nCol % 26;
+ rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
+ nCol = sal::static_int_cast<SCCOL>( nCol - nC );
+ nCol = nCol / 26 - 1;
+ }
+ rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
+ }
+}
+
+void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
+{
+ lcl_ScColToAlpha(rBuf, nCol);
+}
+
+template <typename T > static void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
+{
+ if( bIsAbs )
+ rString.append("$");
+ lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
+}
+
+template <typename T > static void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
+{
+ if ( bIsAbs )
+ rString.append("$");
+ rString.append( nRow + 1 );
+}
+
+template <typename T > static void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
+ const ScAddress::Details& rDetails )
+{
+ rString.append("C");
+ if (bIsAbs)
+ {
+ rString.append( nCol + 1 );
+ }
+ else
+ {
+ nCol -= rDetails.nCol;
+ if (nCol != 0) {
+ rString.append("[").append(nCol).append("]");
+ }
+ }
+}
+
+template <typename T > static void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
+ const ScAddress::Details& rDetails )
+{
+ rString.append("R");
+ if (bIsAbs)
+ {
+ rString.append( nRow + 1 );
+ }
+ else
+ {
+ nRow -= rDetails.nRow;
+ if (nRow != 0) {
+ rString.append("[").append(nRow).append("]");
+ }
+ }
+}
+
+static OUString getFileNameFromDoc( const ScDocument* pDoc )
+{
+ // TODO : er points at ScGlobal::GetAbsDocName()
+ // as a better template. Look into it
+ OUString sFileName;
+ SfxObjectShell* pShell;
+
+ if( nullptr != pDoc &&
+ nullptr != (pShell = pDoc->GetDocumentShell() ) )
+ {
+ uno::Reference< frame::XModel > xModel = pShell->GetModel();
+ if( xModel.is() )
+ {
+ if( !xModel->getURL().isEmpty() )
+ {
+ INetURLObject aURL( xModel->getURL() );
+ sFileName = aURL.GetLastName();
+ }
+ else
+ sFileName = pShell->GetTitle();
+ }
+ }
+ return sFileName;
+}
+
+
+static void lcl_string_append(OUStringBuffer &rString, std::u16string_view sString)
+{
+ rString.append(sString);
+}
+
+static void lcl_string_append(OStringBuffer &rString, std::u16string_view sString)
+{
+ rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 ));
+}
+
+template<typename T > static void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, ScRefFlags nFlags,
+ const ScDocument* pDoc,
+ const ScAddress::Details& rDetails)
+{
+ if( nFlags & ScRefFlags::VALID )
+ nFlags |= ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID | ScRefFlags::TAB_VALID;
+ if( pDoc && (nFlags & ScRefFlags::TAB_VALID ) )
+ {
+ if ( nTab < 0 || nTab >= pDoc->GetTableCount() )
+ {
+ lcl_string_append(r, ScCompiler::GetNativeSymbol(ocErrRef));
+ return;
+ }
+ if( nFlags & ScRefFlags::TAB_3D )
+ {
+ OUString aTabName, aDocName;
+ pDoc->GetName(nTab, aTabName);
+ assert( !aTabName.isEmpty() && "empty sheet name");
+ // External Reference, same as in ScCompiler::MakeTabStr()
+ if( aTabName[0] == '\'' )
+ { // "'Doc'#Tab"
+ sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
+ if (nPos != -1)
+ {
+ aDocName = aTabName.copy( 0, nPos + 1 );
+ aTabName = aTabName.copy( nPos + 1 );
+ }
+ }
+ else if( nFlags & ScRefFlags::FORCE_DOC )
+ {
+ // VBA has an 'external' flag that forces the addition of the
+ // tab name _and_ the doc name. The VBA code would be
+ // needlessly complicated if it constructed an actual external
+ // reference so we add this somewhat cheesy kludge to force the
+ // addition of the document name even for non-external references
+ aDocName = getFileNameFromDoc( pDoc );
+ }
+ ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
+
+ switch( rDetails.eConv )
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ lcl_string_append(r, aDocName);
+ if( nFlags & ScRefFlags::TAB_ABS )
+ r.append("$");
+ lcl_string_append(r, aTabName);
+ r.append(".");
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (!aTabName.isEmpty() && aTabName[0] == '\'')
+ {
+ if (!aDocName.isEmpty())
+ {
+ lcl_string_append(r.append("'["), aDocName);
+ r.append("]");
+ lcl_string_append(r, aTabName.subView(1));
+ }
+ else
+ {
+ lcl_string_append(r, aTabName);
+ }
+ r.append("!");
+ break;
+ }
+ [[fallthrough]];
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if (!aDocName.isEmpty())
+ {
+ lcl_string_append(r.append("["), aDocName);
+ r.append("]");
+ }
+ lcl_string_append(r, aTabName);
+ r.append("!");
+ break;
+ }
+ }
+ }
+ switch( rDetails.eConv )
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if( nFlags & ScRefFlags::COL_VALID )
+ lcl_a1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
+ if( nFlags & ScRefFlags::ROW_VALID )
+ lcl_a1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ if( nFlags & ScRefFlags::ROW_VALID )
+ lcl_r1c1_append_r ( r, nRow, (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
+ if( nFlags & ScRefFlags::COL_VALID )
+ lcl_r1c1_append_c ( r, nCol, (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
+ break;
+ }
+}
+
+void ScAddress::Format( OStringBuffer& r, ScRefFlags nFlags,
+ const ScDocument* pDoc,
+ const Details& rDetails) const
+{
+ lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
+}
+
+OUString ScAddress::Format(ScRefFlags nFlags, const ScDocument* pDoc,
+ const Details& rDetails) const
+{
+ OUStringBuffer r;
+ lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
+ return r.makeStringAndClear();
+}
+
+static void lcl_Split_DocTab( const ScDocument& rDoc, SCTAB nTab,
+ const ScAddress::Details& rDetails,
+ ScRefFlags nFlags,
+ OUString& rTabName, OUString& rDocName )
+{
+ rDoc.GetName(nTab, rTabName);
+ rDocName.clear();
+ // External reference, same as in ScCompiler::MakeTabStr()
+ if (!rTabName.isEmpty() && rTabName[0] == '\'')
+ { // "'Doc'#Tab"
+ sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
+ if (nPos != -1)
+ {
+ rDocName = rTabName.copy( 0, nPos + 1 );
+ rTabName = rTabName.copy( nPos + 1 );
+ }
+ }
+ else if( nFlags & ScRefFlags::FORCE_DOC )
+ {
+ // VBA has an 'external' flag that forces the addition of the
+ // tab name _and_ the doc name. The VBA code would be
+ // needlessly complicated if it constructed an actual external
+ // reference so we add this somewhat cheesy kludge to force the
+ // addition of the document name even for non-external references
+ rDocName = getFileNameFromDoc(&rDoc);
+ }
+ ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
+}
+
+static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
+ ScRefFlags nFlags, const ScDocument& rDoc,
+ const ScAddress::Details& rDetails )
+{
+ if( !(nFlags & ScRefFlags::TAB_3D) )
+ return;
+
+ OUString aTabName, aDocName;
+ lcl_Split_DocTab( rDoc, rRange.aStart.Tab(), rDetails, nFlags, aTabName, aDocName );
+ switch (rDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ if (!aTabName.isEmpty() && aTabName[0] == '\'')
+ {
+ if (!aDocName.isEmpty())
+ {
+ rString.append("'[" + aDocName + "]" + aTabName.subView(1));
+ }
+ else
+ {
+ rString.append(aTabName);
+ }
+ break;
+ }
+ [[fallthrough]];
+ default:
+ if (!aDocName.isEmpty())
+ {
+ rString.append("[" + aDocName + "]");
+ }
+ rString.append(aTabName);
+ break;
+ }
+ if( nFlags & ScRefFlags::TAB2_3D )
+ {
+ lcl_Split_DocTab( rDoc, rRange.aEnd.Tab(), rDetails, nFlags, aTabName, aDocName );
+ rString.append(":");
+ rString.append(aTabName);
+ }
+ rString.append("!");
+}
+
+// helpers used in ScRange::Format
+static bool lcl_ColAbsFlagDiffer(const ScRefFlags nFlags)
+{
+ return static_cast<bool>(nFlags & ScRefFlags::COL_ABS) != static_cast<bool>(nFlags & ScRefFlags::COL2_ABS);
+}
+static bool lcl_RowAbsFlagDiffer(const ScRefFlags nFlags)
+{
+ return static_cast<bool>(nFlags & ScRefFlags::ROW_ABS) != static_cast<bool>(nFlags & ScRefFlags::ROW2_ABS);
+}
+
+OUString ScRange::Format( const ScDocument& rDoc, ScRefFlags nFlags,
+ const ScAddress::Details& rDetails, bool bFullAddressNotation ) const
+{
+ if( !( nFlags & ScRefFlags::VALID ) )
+ {
+ return ScCompiler::GetNativeSymbol(ocErrRef);
+ }
+
+ OUStringBuffer r;
+ switch( rDetails.eConv ) {
+ default :
+ case formula::FormulaGrammar::CONV_OOO: {
+ bool bOneTab = (aStart.Tab() == aEnd.Tab());
+ if ( !bOneTab )
+ nFlags |= ScRefFlags::TAB_3D;
+ r = aStart.Format(nFlags, &rDoc, rDetails);
+ if( aStart != aEnd ||
+ lcl_ColAbsFlagDiffer( nFlags ) ||
+ lcl_RowAbsFlagDiffer( nFlags ))
+ {
+ const ScDocument* pDoc = &rDoc;
+ // move flags of end reference to start reference, mask with BITS to exclude FORCE_DOC flag
+ nFlags = ScRefFlags::VALID | (ScRefFlags(o3tl::to_underlying(nFlags) >> 4) & ScRefFlags::BITS);
+ if ( bOneTab )
+ pDoc = nullptr;
+ else
+ nFlags |= ScRefFlags::TAB_3D;
+ r.append(":" + aEnd.Format(nFlags, pDoc, rDetails));
+ }
+ break;
+ }
+
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX: {
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+
+ lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
+ if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
+ {
+ // Full col refs always require 2 rows (2:2)
+ lcl_a1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
+ r.append(":");
+ lcl_a1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
+ }
+ else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
+ {
+ // Full row refs always require 2 cols (A:A)
+ lcl_a1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
+ r.append(":");
+ lcl_a1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
+ }
+ else
+ {
+ lcl_a1_append_c ( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO );
+ lcl_a1_append_r ( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO );
+ if( aStart.Col() != aEnd.Col() ||
+ lcl_ColAbsFlagDiffer( nFlags ) ||
+ aStart.Row() != aEnd.Row() ||
+ lcl_RowAbsFlagDiffer( nFlags ) ) {
+ r.append(":");
+ lcl_a1_append_c ( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO );
+ lcl_a1_append_r ( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO );
+ }
+ }
+ break;
+ }
+
+ case formula::FormulaGrammar::CONV_XL_R1C1: {
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+
+ lcl_ScRange_Format_XL_Header( r, *this, nFlags, rDoc, rDetails );
+ if( aStart.Col() == 0 && aEnd.Col() >= nMaxCol && !bFullAddressNotation )
+ {
+ lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
+ if( aStart.Row() != aEnd.Row() ||
+ lcl_RowAbsFlagDiffer( nFlags ) ) {
+ r.append(":");
+ lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
+ }
+ }
+ else if( aStart.Row() == 0 && aEnd.Row() >= nMaxRow && !bFullAddressNotation )
+ {
+ lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
+ if( aStart.Col() != aEnd.Col() ||
+ lcl_ColAbsFlagDiffer( nFlags )) {
+ r.append(":");
+ lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
+ }
+ }
+ else
+ {
+ lcl_r1c1_append_r( r, aStart.Row(), (nFlags & ScRefFlags::ROW_ABS) != ScRefFlags::ZERO, rDetails );
+ lcl_r1c1_append_c( r, aStart.Col(), (nFlags & ScRefFlags::COL_ABS) != ScRefFlags::ZERO, rDetails );
+ if( aStart.Col() != aEnd.Col() ||
+ lcl_ColAbsFlagDiffer( nFlags ) ||
+ aStart.Row() != aEnd.Row() ||
+ lcl_RowAbsFlagDiffer( nFlags ) ) {
+ r.append(":");
+ lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & ScRefFlags::ROW2_ABS) != ScRefFlags::ZERO, rDetails );
+ lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & ScRefFlags::COL2_ABS) != ScRefFlags::ZERO, rDetails );
+ }
+ }
+ break;
+ }
+ }
+ return r.makeStringAndClear();
+}
+
+bool ScAddress::Move( SCCOL dx, SCROW dy, SCTAB dz, ScAddress& rErrorPos, const ScDocument& rDoc )
+{
+ SCTAB nMaxTab = rDoc.GetTableCount();
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+ dx = Col() + dx;
+ dy = Row() + dy;
+ dz = Tab() + dz;
+ bool bValid = true;
+ rErrorPos.SetCol(dx);
+ if( dx < 0 )
+ {
+ dx = 0;
+ bValid = false;
+ }
+ else if( dx > nMaxCol )
+ {
+ dx = nMaxCol;
+ bValid =false;
+ }
+ rErrorPos.SetRow(dy);
+ if( dy < 0 )
+ {
+ dy = 0;
+ bValid = false;
+ }
+ else if( dy > nMaxRow )
+ {
+ dy = nMaxRow;
+ bValid =false;
+ }
+ rErrorPos.SetTab(dz);
+ if( dz < 0 )
+ {
+ dz = 0;
+ bValid = false;
+ }
+ else if( dz > nMaxTab )
+ {
+ // Always set MAXTAB+1 so further checks without ScDocument detect invalid.
+ rErrorPos.SetTab(MAXTAB+1);
+ dz = nMaxTab;
+ bValid =false;
+ }
+ Set( dx, dy, dz );
+ return bValid;
+}
+
+bool ScRange::Move( SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange, const ScDocument& rDoc )
+{
+ SCCOL nMaxCol = rDoc.MaxCol();
+ SCROW nMaxRow = rDoc.MaxRow();
+ if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
+ dy = 0; // Entire column not to be moved.
+ if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
+ dx = 0; // Entire row not to be moved.
+ bool b = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc );
+ b &= aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc );
+ return b;
+}
+
+bool ScRange::MoveSticky( const ScDocument& rDoc, SCCOL dx, SCROW dy, SCTAB dz, ScRange& rErrorRange )
+{
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ const SCROW nMaxRow = rDoc.MaxRow();
+ bool bColRange = (aStart.Col() < aEnd.Col());
+ bool bRowRange = (aStart.Row() < aEnd.Row());
+ if (dy && aStart.Row() == 0 && aEnd.Row() == nMaxRow)
+ dy = 0; // Entire column not to be moved.
+ if (dx && aStart.Col() == 0 && aEnd.Col() == nMaxCol)
+ dx = 0; // Entire row not to be moved.
+ bool b1 = aStart.Move( dx, dy, dz, rErrorRange.aStart, rDoc );
+ if (dx && bColRange && aEnd.Col() == nMaxCol)
+ dx = 0; // End column sticky.
+ if (dy && bRowRange && aEnd.Row() == nMaxRow)
+ dy = 0; // End row sticky.
+ SCTAB nOldTab = aEnd.Tab();
+ bool b2 = aEnd.Move( dx, dy, dz, rErrorRange.aEnd, rDoc );
+ if (!b2)
+ {
+ // End column or row of a range may have become sticky.
+ bColRange = (!dx || (bColRange && aEnd.Col() == nMaxCol));
+ if (dx && bColRange)
+ rErrorRange.aEnd.SetCol(nMaxCol);
+ bRowRange = (!dy || (bRowRange && aEnd.Row() == nMaxRow));
+ if (dy && bRowRange)
+ rErrorRange.aEnd.SetRow(nMaxRow);
+ b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz);
+ }
+ return b1 && b2;
+}
+
+void ScRange::IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset)
+{
+ if (aStart.Col() >= nStartCol)
+ {
+ aStart.IncCol(nOffset);
+ if (aStart.Col() < 0)
+ aStart.SetCol(0);
+ else if(aStart.Col() > rDoc.MaxCol())
+ aStart.SetCol(rDoc.MaxCol());
+ }
+ if (aEnd.Col() >= nStartCol)
+ {
+ aEnd.IncCol(nOffset);
+ if (aEnd.Col() < 0)
+ aEnd.SetCol(0);
+ else if(aEnd.Col() > rDoc.MaxCol())
+ aEnd.SetCol(rDoc.MaxCol());
+ }
+}
+
+void ScRange::IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset)
+{
+ if (aStart.Row() >= nStartRow)
+ {
+ aStart.IncRow(nOffset);
+ if (aStart.Row() < 0)
+ aStart.SetRow(0);
+ else if(aStart.Row() > rDoc.MaxRow())
+ aStart.SetRow(rDoc.MaxRow());
+ }
+ if (aEnd.Row() >= nStartRow)
+ {
+ aEnd.IncRow(nOffset);
+ if (aEnd.Row() < 0)
+ aEnd.SetRow(0);
+ else if(aEnd.Row() > rDoc.MaxRow())
+ aEnd.SetRow(rDoc.MaxRow());
+ }
+}
+
+bool ScRange::IsEndColSticky( const ScDocument& rDoc ) const
+{
+ // Only in an actual column range, i.e. not if both columns are MAXCOL.
+ return aEnd.Col() == rDoc.MaxCol() && aStart.Col() < aEnd.Col();
+}
+
+bool ScRange::IsEndRowSticky( const ScDocument& rDoc ) const
+{
+ // Only in an actual row range, i.e. not if both rows are MAXROW.
+ return aEnd.Row() == rDoc.MaxRow() && aStart.Row() < aEnd.Row();
+}
+
+void ScRange::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta )
+{
+ SCCOL nCol = aEnd.Col();
+ if (aStart.Col() >= nCol)
+ {
+ // Less than two columns => not sticky.
+ aEnd.IncCol( nDelta);
+ return;
+ }
+
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ if (nCol == nMaxCol)
+ // already sticky
+ return;
+
+ if (nCol < nMaxCol)
+ aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), nMaxCol));
+ else
+ aEnd.IncCol( nDelta); // was greater than nMaxCol, caller should know...
+}
+
+void ScRange::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta )
+{
+ SCROW nRow = aEnd.Row();
+ if (aStart.Row() >= nRow)
+ {
+ // Less than two rows => not sticky.
+ aEnd.IncRow( nDelta);
+ return;
+ }
+
+ if (nRow == rDoc.MaxRow())
+ // already sticky
+ return;
+
+ if (nRow < rDoc.MaxRow())
+ aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), rDoc.MaxRow()));
+ else
+ aEnd.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know...
+}
+
+OUString ScAddress::GetColRowString() const
+{
+ OUStringBuffer aString;
+
+ switch( detailsOOOa1.eConv )
+ {
+ default :
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ lcl_ScColToAlpha( aString, nCol);
+ aString.append(nRow+1);
+ break;
+
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ lcl_r1c1_append_r ( aString, nRow, false/*bAbsolute*/, detailsOOOa1 );
+ lcl_r1c1_append_c ( aString, nCol, false/*bAbsolute*/, detailsOOOa1 );
+ break;
+ }
+
+ return aString.makeStringAndClear();
+}
+
+OUString ScRefAddress::GetRefString( const ScDocument& rDoc, SCTAB nActTab,
+ const ScAddress::Details& rDetails ) const
+{
+ if ( Tab()+1 > rDoc.GetTableCount() )
+ return ScCompiler::GetNativeSymbol(ocErrRef);
+
+ ScRefFlags nFlags = ScRefFlags::VALID;
+ if ( nActTab != Tab() )
+ {
+ nFlags |= ScRefFlags::TAB_3D;
+ if ( !bRelTab )
+ nFlags |= ScRefFlags::TAB_ABS;
+ }
+ if ( !bRelCol )
+ nFlags |= ScRefFlags::COL_ABS;
+ if ( !bRelRow )
+ nFlags |= ScRefFlags::ROW_ABS;
+
+ return aAdr.Format(nFlags, &rDoc, rDetails);
+}
+
+bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, std::u16string_view rStr)
+{
+ SCCOL nResult = 0;
+ sal_Int32 nStop = rStr.size();
+ sal_Int32 nPos = 0;
+ sal_Unicode c;
+ const SCCOL nMaxCol = rDoc.MaxCol();
+ while (nResult <= nMaxCol && nPos < nStop && (c = rStr[nPos]) != 0 &&
+ rtl::isAsciiAlpha(c))
+ {
+ if (nPos > 0)
+ nResult = (nResult + 1) * 26;
+ nResult += ScGlobal::ToUpperAlpha(c) - 'A';
+ ++nPos;
+ }
+ bool bOk = (rDoc.ValidCol(nResult) && nPos > 0);
+ if (bOk)
+ rCol = nResult;
+ return bOk;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/adiasync.cxx b/sc/source/core/tool/adiasync.cxx
new file mode 100644
index 0000000000..ba2b49eb38
--- /dev/null
+++ b/sc/source/core/tool/adiasync.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 <sal/config.h>
+
+#include <algorithm>
+
+#include <sfx2/objsh.hxx>
+
+#include <adiasync.hxx>
+#include <brdcst.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+ScAddInAsyncs theAddInAsyncTbl;
+
+extern "C" {
+void CALLTYPE ScAddInAsyncCallBack( double& nHandle, void* pData )
+{
+ ScAddInAsync::CallBack( sal_uLong( nHandle ), pData );
+}
+}
+
+ScAddInAsync::ScAddInAsync(sal_uLong nHandleP, LegacyFuncData* pFuncData, ScDocument* pDoc) :
+ pStr( nullptr ),
+ mpFuncData(pFuncData),
+ nHandle( nHandleP ),
+ meType(pFuncData->GetAsyncType()),
+ bValid( false )
+{
+ pDocs.reset(new ScAddInDocs);
+ pDocs->insert( pDoc );
+ theAddInAsyncTbl.emplace( this );
+}
+
+ScAddInAsync::~ScAddInAsync()
+{
+ // in dTor because of theAddInAsyncTbl.DeleteAndDestroy in ScGlobal::Clear
+ mpFuncData->Unadvice( static_cast<double>(nHandle) );
+ if ( meType == ParamType::PTR_STRING && pStr ) // include type comparison because of union
+ delete pStr;
+ pDocs.reset();
+}
+
+ScAddInAsync* ScAddInAsync::Get( sal_uLong nHandleP )
+{
+ ScAddInAsync* pRet = nullptr;
+ auto it = std::find_if(
+ theAddInAsyncTbl.begin(), theAddInAsyncTbl.end(),
+ [nHandleP](std::unique_ptr<ScAddInAsync> const & el)
+ { return el->nHandle == nHandleP; });
+ if ( it != theAddInAsyncTbl.end() )
+ pRet = it->get();
+ return pRet;
+}
+
+void ScAddInAsync::CallBack( sal_uLong nHandleP, void* pData )
+{
+ auto asyncIt = std::find_if(
+ theAddInAsyncTbl.begin(), theAddInAsyncTbl.end(),
+ [nHandleP](std::unique_ptr<ScAddInAsync> const & el)
+ { return el->nHandle == nHandleP; });
+ if ( asyncIt == theAddInAsyncTbl.end() )
+ return;
+ ScAddInAsync* p = asyncIt->get();
+
+ if ( !p->HasListeners() )
+ {
+ // not in dTor because of theAddInAsyncTbl.DeleteAndDestroy in ScGlobal::Clear
+ theAddInAsyncTbl.erase( asyncIt );
+ return ;
+ }
+ switch ( p->meType )
+ {
+ case ParamType::PTR_DOUBLE :
+ p->nVal = *static_cast<double*>(pData);
+ break;
+ case ParamType::PTR_STRING :
+ {
+ char* pChar = static_cast<char*>(pData);
+ if ( p->pStr )
+ *p->pStr = OUString( pChar, strlen(pChar),osl_getThreadTextEncoding() );
+ else
+ p->pStr = new OUString( pChar, strlen(pChar), osl_getThreadTextEncoding() );
+ break;
+ }
+ default :
+ OSL_FAIL( "unknown AsyncType" );
+ return;
+ }
+ p->bValid = true;
+ p->Broadcast( ScHint(SfxHintId::ScDataChanged, ScAddress()) );
+
+ for ( ScDocument* pDoc : *p->pDocs )
+ {
+ pDoc->TrackFormulas();
+ pDoc->GetDocumentShell()->Broadcast( SfxHint( SfxHintId::ScDataChanged ) );
+ }
+}
+
+void ScAddInAsync::RemoveDocument( ScDocument* pDocumentP )
+{
+ for( ScAddInAsyncs::reverse_iterator iter1 = theAddInAsyncTbl.rbegin(); iter1 != theAddInAsyncTbl.rend(); ++iter1 )
+ { // backwards because of pointer-movement in array
+ ScAddInAsync* pAsync = iter1->get();
+ ScAddInDocs* p = pAsync->pDocs.get();
+ ScAddInDocs::iterator iter2 = p->find( pDocumentP );
+ if( iter2 != p->end() )
+ {
+ p->erase( iter2 );
+ if ( p->empty() )
+ { // this AddIn is not used anymore
+ theAddInAsyncTbl.erase( --(iter1.base()) );
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/appoptio.cxx b/sc/source/core/tool/appoptio.cxx
new file mode 100644
index 0000000000..516c1a67e9
--- /dev/null
+++ b/sc/source/core/tool/appoptio.cxx
@@ -0,0 +1,668 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <appoptio.hxx>
+#include <global.hxx>
+#include <userlist.hxx>
+#include <formula/compiler.hxx>
+#include <miscuno.hxx>
+#include <vector>
+#include <osl/diagnose.h>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+// ScAppOptions - Application Options
+
+ScAppOptions::ScAppOptions()
+{
+ SetDefaults();
+}
+
+ScAppOptions::ScAppOptions( const ScAppOptions& rCpy )
+{
+ *this = rCpy;
+}
+
+ScAppOptions::~ScAppOptions()
+{
+}
+
+void ScAppOptions::SetDefaults()
+{
+ if ( ScOptionsUtil::IsMetricSystem() )
+ eMetric = FieldUnit::CM; // default for countries with metric system
+ else
+ eMetric = FieldUnit::INCH; // default for others
+
+ nZoom = 100;
+ eZoomType = SvxZoomType::PERCENT;
+ bSynchronizeZoom = true;
+ nStatusFunc = ( 1 << SUBTOTAL_FUNC_SUM );
+ bAutoComplete = true;
+ bDetectiveAuto = true;
+
+ pLRUList.reset( new sal_uInt16[5] ); // sensible initialization
+ pLRUList[0] = SC_OPCODE_SUM;
+ pLRUList[1] = SC_OPCODE_AVERAGE;
+ pLRUList[2] = SC_OPCODE_MIN;
+ pLRUList[3] = SC_OPCODE_MAX;
+ pLRUList[4] = SC_OPCODE_IF;
+ nLRUFuncCount = 5;
+
+ nTrackContentColor = COL_TRANSPARENT;
+ nTrackInsertColor = COL_TRANSPARENT;
+ nTrackDeleteColor = COL_TRANSPARENT;
+ nTrackMoveColor = COL_TRANSPARENT;
+ eLinkMode = LM_ON_DEMAND;
+
+ nDefaultObjectSizeWidth = 8000;
+ nDefaultObjectSizeHeight = 5000;
+
+ mbShowSharedDocumentWarning = true;
+
+ meKeyBindingType = ScOptionsUtil::KEY_DEFAULT;
+ mbLinksInsertedLikeMSExcel = false;
+}
+
+ScAppOptions& ScAppOptions::operator=( const ScAppOptions& rCpy )
+{
+ eMetric = rCpy.eMetric;
+ eZoomType = rCpy.eZoomType;
+ bSynchronizeZoom = rCpy.bSynchronizeZoom;
+ nZoom = rCpy.nZoom;
+ SetLRUFuncList( rCpy.pLRUList.get(), rCpy.nLRUFuncCount );
+ nStatusFunc = rCpy.nStatusFunc;
+ bAutoComplete = rCpy.bAutoComplete;
+ bDetectiveAuto = rCpy.bDetectiveAuto;
+ nTrackContentColor = rCpy.nTrackContentColor;
+ nTrackInsertColor = rCpy.nTrackInsertColor;
+ nTrackDeleteColor = rCpy.nTrackDeleteColor;
+ nTrackMoveColor = rCpy.nTrackMoveColor;
+ eLinkMode = rCpy.eLinkMode;
+ nDefaultObjectSizeWidth = rCpy.nDefaultObjectSizeWidth;
+ nDefaultObjectSizeHeight = rCpy.nDefaultObjectSizeHeight;
+ mbShowSharedDocumentWarning = rCpy.mbShowSharedDocumentWarning;
+ meKeyBindingType = rCpy.meKeyBindingType;
+ mbLinksInsertedLikeMSExcel = rCpy.mbLinksInsertedLikeMSExcel;
+ return *this;
+}
+
+void ScAppOptions::SetLRUFuncList( const sal_uInt16* pList, const sal_uInt16 nCount )
+{
+ nLRUFuncCount = nCount;
+
+ if ( nLRUFuncCount > 0 )
+ {
+ pLRUList.reset( new sal_uInt16[nLRUFuncCount] );
+
+ for ( sal_uInt16 i=0; i<nLRUFuncCount; i++ )
+ pLRUList[i] = pList[i];
+ }
+ else
+ pLRUList.reset();
+}
+
+// Config Item containing app options
+
+static void lcl_GetLastFunctions( Any& rDest, const ScAppOptions& rOpt )
+{
+ tools::Long nCount = rOpt.GetLRUFuncListCount();
+ sal_uInt16* pUShorts = rOpt.GetLRUFuncList();
+ if ( nCount && pUShorts )
+ {
+ Sequence<sal_Int32> aSeq( nCount );
+ sal_Int32* pArray = aSeq.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArray[i] = pUShorts[i];
+ rDest <<= aSeq;
+ }
+ else
+ rDest <<= Sequence<sal_Int32>(0); // empty
+}
+
+static void lcl_GetSortList( Any& rDest )
+{
+ const ScUserList* pUserList = ScGlobal::GetUserList();
+ if (pUserList)
+ {
+ size_t nCount = pUserList->size();
+ Sequence<OUString> aSeq( nCount );
+ OUString* pArray = aSeq.getArray();
+ for (size_t i=0; i<nCount; ++i)
+ pArray[i] = (*pUserList)[sal::static_int_cast<sal_uInt16>(i)].GetString();
+ rDest <<= aSeq;
+ }
+ else
+ rDest <<= Sequence<OUString>(0); // empty
+}
+
+constexpr OUStringLiteral CFGPATH_LAYOUT = u"Office.Calc/Layout";
+
+#define SCLAYOUTOPT_MEASURE 0
+#define SCLAYOUTOPT_STATUSBAR 1
+#define SCLAYOUTOPT_ZOOMVAL 2
+#define SCLAYOUTOPT_ZOOMTYPE 3
+#define SCLAYOUTOPT_SYNCZOOM 4
+#define SCLAYOUTOPT_STATUSBARMULTI 5
+
+constexpr OUStringLiteral CFGPATH_INPUT = u"Office.Calc/Input";
+
+#define SCINPUTOPT_LASTFUNCS 0
+#define SCINPUTOPT_AUTOINPUT 1
+#define SCINPUTOPT_DET_AUTO 2
+
+constexpr OUStringLiteral CFGPATH_REVISION = u"Office.Calc/Revision/Color";
+
+#define SCREVISOPT_CHANGE 0
+#define SCREVISOPT_INSERTION 1
+#define SCREVISOPT_DELETION 2
+#define SCREVISOPT_MOVEDENTRY 3
+
+constexpr OUStringLiteral CFGPATH_CONTENT = u"Office.Calc/Content/Update";
+
+#define SCCONTENTOPT_LINK 0
+
+constexpr OUStringLiteral CFGPATH_SORTLIST = u"Office.Calc/SortList";
+
+#define SCSORTLISTOPT_LIST 0
+
+constexpr OUStringLiteral CFGPATH_MISC = u"Office.Calc/Misc";
+
+#define SCMISCOPT_DEFOBJWIDTH 0
+#define SCMISCOPT_DEFOBJHEIGHT 1
+#define SCMISCOPT_SHOWSHAREDDOCWARN 2
+
+constexpr OUStringLiteral CFGPATH_COMPAT = u"Office.Calc/Compatibility";
+
+#define SCCOMPATOPT_KEY_BINDING 0
+#define SCCOMPATOPT_LINK_LIKE_MS 1
+
+// Default value of Layout/Other/StatusbarMultiFunction
+#define SCLAYOUTOPT_STATUSBARMULTI_DEFAULTVAL 514
+// Default value of Layout/Other/StatusbarFunction
+#define SCLAYOUTOPT_STATUSBAR_DEFAULTVAL 1
+// Legacy default value of Layout/Other/StatusbarFunction
+// prior to multiple statusbar functions feature addition
+#define SCLAYOUTOPT_STATUSBAR_DEFAULTVAL_LEGACY 9
+
+static sal_uInt32 lcl_ConvertStatusBarFuncSetToSingle( sal_uInt32 nFuncSet )
+{
+ if ( !nFuncSet )
+ return 0;
+ for ( sal_uInt32 nFunc = 1; nFunc < 32; ++nFunc )
+ if ( nFuncSet & ( 1U << nFunc ) )
+ return nFunc;
+ return 0;
+}
+
+Sequence<OUString> ScAppCfg::GetLayoutPropertyNames()
+{
+ const bool bIsMetric = ScOptionsUtil::IsMetricSystem();
+
+ return {(bIsMetric ? OUString("Other/MeasureUnit/Metric")
+ : OUString("Other/MeasureUnit/NonMetric")), // SCLAYOUTOPT_MEASURE
+ "Other/StatusbarFunction", // SCLAYOUTOPT_STATUSBAR
+ "Zoom/Value", // SCLAYOUTOPT_ZOOMVAL
+ "Zoom/Type", // SCLAYOUTOPT_ZOOMTYPE
+ "Zoom/Synchronize", // SCLAYOUTOPT_SYNCZOOM
+ "Other/StatusbarMultiFunction"}; // SCLAYOUTOPT_STATUSBARMULTI
+}
+
+Sequence<OUString> ScAppCfg::GetInputPropertyNames()
+{
+ return {"LastFunctions", // SCINPUTOPT_LASTFUNCS
+ "AutoInput", // SCINPUTOPT_AUTOINPUT
+ "DetectiveAuto"}; // SCINPUTOPT_DET_AUTO
+}
+
+Sequence<OUString> ScAppCfg::GetRevisionPropertyNames()
+{
+ return {"Change", // SCREVISOPT_CHANGE
+ "Insertion", // SCREVISOPT_INSERTION
+ "Deletion", // SCREVISOPT_DELETION
+ "MovedEntry"}; // SCREVISOPT_MOVEDENTRY
+}
+
+Sequence<OUString> ScAppCfg::GetContentPropertyNames()
+{
+ return {"Link"}; // SCCONTENTOPT_LINK
+}
+
+Sequence<OUString> ScAppCfg::GetSortListPropertyNames()
+{
+ return {"List"}; // SCSORTLISTOPT_LIST
+}
+
+Sequence<OUString> ScAppCfg::GetMiscPropertyNames()
+{
+ return {"DefaultObjectSize/Width", // SCMISCOPT_DEFOBJWIDTH
+ "DefaultObjectSize/Height", // SCMISCOPT_DEFOBJHEIGHT
+ "SharedDocument/ShowWarning"}; // SCMISCOPT_SHOWSHAREDDOCWARN
+}
+
+Sequence<OUString> ScAppCfg::GetCompatPropertyNames()
+{
+ return {"KeyBindings/BaseGroup", // SCCOMPATOPT_KEY_BINDING
+ "Links" }; // SCCOMPATOPT_LINK_LIKE_MS
+}
+
+ScAppCfg::ScAppCfg() :
+ aLayoutItem( CFGPATH_LAYOUT ),
+ aInputItem( CFGPATH_INPUT ),
+ aRevisionItem( CFGPATH_REVISION ),
+ aContentItem( CFGPATH_CONTENT ),
+ aSortListItem( CFGPATH_SORTLIST ),
+ aMiscItem( CFGPATH_MISC ),
+ aCompatItem( CFGPATH_COMPAT )
+{
+ aLayoutItem.EnableNotification(GetLayoutPropertyNames());
+ ReadLayoutCfg();
+ aLayoutItem.SetCommitLink( LINK( this, ScAppCfg, LayoutCommitHdl ) );
+ aLayoutItem.SetNotifyLink( LINK( this, ScAppCfg, LayoutNotifyHdl ) );
+
+ aInputItem.EnableNotification(GetInputPropertyNames());
+ ReadInputCfg();
+ aInputItem.SetCommitLink( LINK( this, ScAppCfg, InputCommitHdl ) );
+ aInputItem.SetNotifyLink( LINK( this, ScAppCfg, InputNotifyHdl ) );
+
+ aRevisionItem.EnableNotification(GetRevisionPropertyNames());
+ ReadRevisionCfg();
+ aRevisionItem.SetCommitLink( LINK( this, ScAppCfg, RevisionCommitHdl ) );
+ aRevisionItem.SetNotifyLink( LINK( this, ScAppCfg, RevisionNotifyHdl ) );
+
+ aContentItem.EnableNotification(GetContentPropertyNames());
+ ReadContentCfg();
+ aContentItem.SetCommitLink( LINK( this, ScAppCfg, ContentCommitHdl ) );
+ aContentItem.SetNotifyLink( LINK( this, ScAppCfg, ContentNotifyHdl ) );
+
+ aSortListItem.EnableNotification(GetSortListPropertyNames());
+ ReadSortListCfg();
+ aSortListItem.SetCommitLink( LINK( this, ScAppCfg, SortListCommitHdl ) );
+ aSortListItem.SetNotifyLink( LINK( this, ScAppCfg, SortListNotifyHdl ) );
+
+ aMiscItem.EnableNotification(GetMiscPropertyNames());
+ ReadMiscCfg();
+ aMiscItem.SetCommitLink( LINK( this, ScAppCfg, MiscCommitHdl ) );
+ aMiscItem.SetNotifyLink( LINK( this, ScAppCfg, MiscNotifyHdl ) );
+
+ aCompatItem.EnableNotification(GetCompatPropertyNames());
+ ReadCompatCfg();
+ aCompatItem.SetCommitLink( LINK(this, ScAppCfg, CompatCommitHdl) );
+ aCompatItem.SetNotifyLink( LINK(this, ScAppCfg, CompatNotifyHdl) );
+}
+
+void ScAppCfg::ReadLayoutCfg()
+{
+ const Sequence<OUString> aNames = GetLayoutPropertyNames();
+ const Sequence<Any> aValues = aLayoutItem.GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ sal_uInt32 nStatusBarFuncSingle = 0;
+ sal_uInt32 nStatusBarFuncMulti = 0;
+
+ if (sal_Int32 nIntVal; aValues[SCLAYOUTOPT_MEASURE] >>= nIntVal)
+ SetAppMetric(static_cast<FieldUnit>(nIntVal));
+ if (sal_uInt32 nUIntVal; aValues[SCLAYOUTOPT_STATUSBAR] >>= nUIntVal)
+ nStatusBarFuncSingle = nUIntVal;
+ if (sal_uInt32 nUIntVal; aValues[SCLAYOUTOPT_STATUSBARMULTI] >>= nUIntVal)
+ nStatusBarFuncMulti = nUIntVal;
+ if (sal_Int32 nIntVal; aValues[SCLAYOUTOPT_ZOOMVAL] >>= nIntVal)
+ SetZoom(static_cast<sal_uInt16>(nIntVal));
+ if (sal_Int32 nIntVal; aValues[SCLAYOUTOPT_ZOOMTYPE] >>= nIntVal)
+ SetZoomType(static_cast<SvxZoomType>(nIntVal));
+ SetSynchronizeZoom(ScUnoHelpFunctions::GetBoolFromAny(aValues[SCLAYOUTOPT_SYNCZOOM]));
+
+ if (nStatusBarFuncMulti != SCLAYOUTOPT_STATUSBARMULTI_DEFAULTVAL)
+ SetStatusFunc(nStatusBarFuncMulti);
+ else if (nStatusBarFuncSingle != SCLAYOUTOPT_STATUSBAR_DEFAULTVAL
+ && nStatusBarFuncSingle != SCLAYOUTOPT_STATUSBAR_DEFAULTVAL_LEGACY)
+ {
+ if (nStatusBarFuncSingle)
+ SetStatusFunc(1 << nStatusBarFuncSingle);
+ else
+ SetStatusFunc(0);
+ }
+ else
+ SetStatusFunc(SCLAYOUTOPT_STATUSBARMULTI_DEFAULTVAL);
+}
+
+void ScAppCfg::ReadInputCfg()
+{
+ const Sequence<OUString> aNames = GetInputPropertyNames();
+ const Sequence<Any> aValues = aInputItem.GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ if (Sequence<sal_Int32> aSeq; aValues[SCINPUTOPT_LASTFUNCS] >>= aSeq)
+ {
+ sal_Int32 nCount = aSeq.getLength();
+ if (nCount < SAL_MAX_UINT16)
+ {
+ std::vector<sal_uInt16> pUShorts(nCount);
+ for (sal_Int32 i = 0; i < nCount; i++)
+ pUShorts[i] = aSeq[i];
+
+ SetLRUFuncList(pUShorts.data(), nCount);
+ }
+ }
+ SetAutoComplete(ScUnoHelpFunctions::GetBoolFromAny(aValues[SCINPUTOPT_AUTOINPUT]));
+ SetDetectiveAuto(ScUnoHelpFunctions::GetBoolFromAny(aValues[SCINPUTOPT_DET_AUTO]));
+}
+
+void ScAppCfg::ReadRevisionCfg()
+{
+ const Sequence<OUString> aNames = GetRevisionPropertyNames();
+ const Sequence<Any> aValues = aRevisionItem.GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ if (sal_Int32 nIntVal; aValues[SCREVISOPT_CHANGE] >>= nIntVal)
+ SetTrackContentColor(Color(ColorTransparency, nIntVal));
+ if (sal_Int32 nIntVal; aValues[SCREVISOPT_INSERTION] >>= nIntVal)
+ SetTrackInsertColor(Color(ColorTransparency, nIntVal));
+ if (sal_Int32 nIntVal; aValues[SCREVISOPT_DELETION] >>= nIntVal)
+ SetTrackDeleteColor(Color(ColorTransparency, nIntVal));
+ if (sal_Int32 nIntVal; aValues[SCREVISOPT_MOVEDENTRY] >>= nIntVal)
+ SetTrackMoveColor(Color(ColorTransparency, nIntVal));
+}
+
+void ScAppCfg::ReadContentCfg()
+{
+ const Sequence<OUString> aNames = GetContentPropertyNames();
+ const Sequence<Any> aValues = aContentItem.GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ if (sal_Int32 nIntVal; aValues[SCCONTENTOPT_LINK] >>= nIntVal)
+ SetLinkMode(static_cast<ScLkUpdMode>(nIntVal));
+}
+
+void ScAppCfg::ReadSortListCfg()
+{
+ const Sequence<OUString> aNames = GetSortListPropertyNames();
+ const Sequence<Any> aValues = aSortListItem.GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ if (Sequence<OUString> aSeq; aValues[SCSORTLISTOPT_LIST] >>= aSeq)
+ {
+ ScUserList aList(false); // Do not init defaults
+
+ // if setting is "default", keep default values
+ //TODO: mark "default" in a safe way
+ const bool bDefault = (aSeq.getLength() == 1 && aSeq[0] == "NULL");
+
+ if (bDefault)
+ {
+ aList.AddDefaults();
+ }
+ else
+ {
+ for (const OUString& rStr : std::as_const(aSeq))
+ {
+ aList.emplace_back(rStr);
+ }
+ }
+
+ ScGlobal::SetUserList(&aList);
+ }
+}
+
+void ScAppCfg::ReadMiscCfg()
+{
+ const Sequence<OUString> aNames = GetMiscPropertyNames();
+ const Sequence<Any> aValues = aMiscItem.GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ if (sal_Int32 nIntVal; aValues[SCMISCOPT_DEFOBJWIDTH] >>= nIntVal)
+ SetDefaultObjectSizeWidth(nIntVal);
+ if (sal_Int32 nIntVal; aValues[SCMISCOPT_DEFOBJHEIGHT] >>= nIntVal)
+ SetDefaultObjectSizeHeight(nIntVal);
+ SetShowSharedDocumentWarning(
+ ScUnoHelpFunctions::GetBoolFromAny(aValues[SCMISCOPT_SHOWSHAREDDOCWARN]));
+}
+
+void ScAppCfg::ReadCompatCfg()
+{
+ const Sequence<OUString> aNames = GetCompatPropertyNames();
+ const Sequence<Any> aValues = aCompatItem.GetProperties(aNames);
+ if (aValues.getLength() != aNames.getLength())
+ return;
+
+ sal_Int32 nIntVal = 0; // 0 = 'Default'
+ aValues[SCCOMPATOPT_KEY_BINDING] >>= nIntVal;
+ SetKeyBindingType(static_cast<ScOptionsUtil::KeyBindingType>(nIntVal));
+
+ if (aValues.getLength() > SCCOMPATOPT_LINK_LIKE_MS)
+ SetLinksInsertedLikeMSExcel(
+ ScUnoHelpFunctions::GetBoolFromAny(aValues[SCCOMPATOPT_LINK_LIKE_MS]));
+}
+
+IMPL_LINK_NOARG(ScAppCfg, LayoutCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetLayoutPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCLAYOUTOPT_MEASURE:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetAppMetric());
+ break;
+ case SCLAYOUTOPT_STATUSBAR:
+ pValues[nProp] <<= lcl_ConvertStatusBarFuncSetToSingle( GetStatusFunc() );
+ break;
+ case SCLAYOUTOPT_ZOOMVAL:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetZoom());
+ break;
+ case SCLAYOUTOPT_ZOOMTYPE:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetZoomType());
+ break;
+ case SCLAYOUTOPT_SYNCZOOM:
+ pValues[nProp] <<= GetSynchronizeZoom();
+ break;
+ case SCLAYOUTOPT_STATUSBARMULTI:
+ pValues[nProp] <<= GetStatusFunc();
+ break;
+ }
+ }
+ aLayoutItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, LayoutNotifyHdl, ScLinkConfigItem&, void) { ReadLayoutCfg(); }
+
+IMPL_LINK_NOARG(ScAppCfg, InputCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetInputPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCINPUTOPT_LASTFUNCS:
+ lcl_GetLastFunctions( pValues[nProp], *this );
+ break;
+ case SCINPUTOPT_AUTOINPUT:
+ pValues[nProp] <<= GetAutoComplete();
+ break;
+ case SCINPUTOPT_DET_AUTO:
+ pValues[nProp] <<= GetDetectiveAuto();
+ break;
+ }
+ }
+ aInputItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, InputNotifyHdl, ScLinkConfigItem&, void) { ReadInputCfg(); }
+
+IMPL_LINK_NOARG(ScAppCfg, RevisionCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetRevisionPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCREVISOPT_CHANGE:
+ pValues[nProp] <<= GetTrackContentColor();
+ break;
+ case SCREVISOPT_INSERTION:
+ pValues[nProp] <<= GetTrackInsertColor();
+ break;
+ case SCREVISOPT_DELETION:
+ pValues[nProp] <<= GetTrackDeleteColor();
+ break;
+ case SCREVISOPT_MOVEDENTRY:
+ pValues[nProp] <<= GetTrackMoveColor();
+ break;
+ }
+ }
+ aRevisionItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, RevisionNotifyHdl, ScLinkConfigItem&, void) { ReadRevisionCfg(); }
+
+IMPL_LINK_NOARG(ScAppCfg, ContentCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetContentPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCCONTENTOPT_LINK:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetLinkMode());
+ break;
+ }
+ }
+ aContentItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, ContentNotifyHdl, ScLinkConfigItem&, void) { ReadContentCfg(); }
+
+IMPL_LINK_NOARG(ScAppCfg, SortListCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetSortListPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCSORTLISTOPT_LIST:
+ lcl_GetSortList( pValues[nProp] );
+ break;
+ }
+ }
+ aSortListItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, SortListNotifyHdl, ScLinkConfigItem&, void) { ReadSortListCfg(); }
+
+IMPL_LINK_NOARG(ScAppCfg, MiscCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetMiscPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCMISCOPT_DEFOBJWIDTH:
+ pValues[nProp] <<= GetDefaultObjectSizeWidth();
+ break;
+ case SCMISCOPT_DEFOBJHEIGHT:
+ pValues[nProp] <<= GetDefaultObjectSizeHeight();
+ break;
+ case SCMISCOPT_SHOWSHAREDDOCWARN:
+ pValues[nProp] <<= GetShowSharedDocumentWarning();
+ break;
+ }
+ }
+ aMiscItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, MiscNotifyHdl, ScLinkConfigItem&, void) { ReadMiscCfg(); }
+
+IMPL_LINK_NOARG(ScAppCfg, CompatCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetCompatPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for (int nProp = 0; nProp < aNames.getLength(); ++nProp)
+ {
+ switch(nProp)
+ {
+ case SCCOMPATOPT_KEY_BINDING:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetKeyBindingType());
+ break;
+ case SCCOMPATOPT_LINK_LIKE_MS:
+ pValues[nProp] <<= GetLinksInsertedLikeMSExcel();
+ break;
+ }
+ }
+ aCompatItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScAppCfg, CompatNotifyHdl, ScLinkConfigItem&, void) { ReadCompatCfg(); }
+
+void ScAppCfg::SetOptions( const ScAppOptions& rNew )
+{
+ *static_cast<ScAppOptions*>(this) = rNew;
+
+ aLayoutItem.SetModified();
+ aInputItem.SetModified();
+ aRevisionItem.SetModified();
+ aContentItem.SetModified();
+ aSortListItem.SetModified();
+ aMiscItem.SetModified();
+ aCompatItem.SetModified();
+
+ aLayoutItem.Commit();
+ aInputItem.Commit();
+ aRevisionItem.Commit();
+ aContentItem.Commit();
+ aSortListItem.Commit();
+ aMiscItem.Commit();
+ aCompatItem.Commit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/arraysumSSE2.cxx b/sc/source/core/tool/arraysumSSE2.cxx
new file mode 100644
index 0000000000..f7b87070b7
--- /dev/null
+++ b/sc/source/core/tool/arraysumSSE2.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <arraysumfunctor.hxx>
+
+#include <tools/simdsupport.hxx>
+
+#include <stdlib.h>
+
+#if SC_USE_SSE2
+
+namespace sc::op
+{
+/** Kahan sum with SSE2.
+ */
+static inline void sumSSE2(__m128d& sum, __m128d& err, const __m128d& value)
+{
+ const __m128d ANNULATE_SIGN_BIT = _mm_castsi128_pd(_mm_set1_epi64x(0x7FFF'FFFF'FFFF'FFFF));
+ // Temporal parameter
+ __m128d t = _mm_add_pd(sum, value);
+ // Absolute value of the total sum
+ __m128d asum = _mm_and_pd(sum, ANNULATE_SIGN_BIT);
+ // Absolute value of the value to add
+ __m128d avalue = _mm_and_pd(value, ANNULATE_SIGN_BIT);
+ // Compare the absolute values sum >= value
+ __m128d mask = _mm_cmpge_pd(asum, avalue);
+ // The following code has this form ( a - t + b)
+ // Case 1: a = sum b = value
+ // Case 2: a = value b = sum
+ __m128d a = _mm_add_pd(_mm_and_pd(mask, sum), _mm_andnot_pd(mask, value));
+ __m128d b = _mm_add_pd(_mm_and_pd(mask, value), _mm_andnot_pd(mask, sum));
+ err = _mm_add_pd(err, _mm_add_pd(_mm_sub_pd(a, t), b));
+ // Store result
+ sum = t;
+}
+
+/** Execute Kahan sum with SSE2.
+ */
+KahanSum executeSSE2(size_t& i, size_t nSize, const double* pCurrent)
+{
+ // Make sure we don't fall out of bounds.
+ // This works by sums of 8 terms.
+ // So the 8'th term is i+7
+ // If we iterate until nSize won't fall out of bounds
+ if (nSize > i + 7)
+ {
+ // Setup sums and errors as 0
+ __m128d sum1 = _mm_setzero_pd();
+ __m128d err1 = _mm_setzero_pd();
+ __m128d sum2 = _mm_setzero_pd();
+ __m128d err2 = _mm_setzero_pd();
+ __m128d sum3 = _mm_setzero_pd();
+ __m128d err3 = _mm_setzero_pd();
+ __m128d sum4 = _mm_setzero_pd();
+ __m128d err4 = _mm_setzero_pd();
+
+ for (; i + 7 < nSize; i += 8)
+ {
+ // Kahan sum 1
+ __m128d load1 = _mm_loadu_pd(pCurrent);
+ sumSSE2(sum1, err1, load1);
+ pCurrent += 2;
+
+ // Kahan sum 2
+ __m128d load2 = _mm_loadu_pd(pCurrent);
+ sumSSE2(sum2, err2, load2);
+ pCurrent += 2;
+
+ // Kahan sum 3
+ __m128d load3 = _mm_loadu_pd(pCurrent);
+ sumSSE2(sum3, err3, load3);
+ pCurrent += 2;
+
+ // Kahan sum 4
+ __m128d load4 = _mm_loadu_pd(pCurrent);
+ sumSSE2(sum4, err4, load4);
+ pCurrent += 2;
+ }
+
+ // Now we combine pairwise summation with Kahan summation
+
+ // 1+2 3+4 -> 1, 3
+ sumSSE2(sum1, err1, sum2);
+ sumSSE2(sum1, err1, err2);
+ sumSSE2(sum3, err3, sum4);
+ sumSSE2(sum3, err3, err4);
+
+ // 1+3 -> 1
+ sumSSE2(sum1, err1, sum3);
+ sumSSE2(sum1, err1, err3);
+
+ // Store results
+ double sums[2];
+ double errs[2];
+ _mm_storeu_pd(&sums[0], sum1);
+ _mm_storeu_pd(&errs[0], err1);
+
+ // First Kahan & pairwise summation
+ // 0+1 -> 0
+ KahanSum::sumNeumaierNormal(sums[0], errs[0], sums[1]);
+ KahanSum::sumNeumaierNormal(sums[0], errs[0], errs[1]);
+
+ // Store result
+ return { sums[0], errs[0] };
+ }
+ return { 0.0, 0.0 };
+}
+
+} // namespace
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/autoform.cxx b/sc/source/core/tool/autoform.cxx
new file mode 100644
index 0000000000..d6a178bf18
--- /dev/null
+++ b/sc/source/core/tool/autoform.cxx
@@ -0,0 +1,934 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <autoform.hxx>
+
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/outdev.hxx>
+#include <svx/algitem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/rotmodit.hxx>
+#include <svx/strings.hrc>
+#include <editeng/adjustitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/fileformat.h>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <tools/tenccvt.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.hxx>
+
+#include <attrib.hxx>
+#include <globstr.hrc>
+#include <scitems.hxx>
+#include <scresid.hxx>
+#include <document.hxx>
+
+/*
+ * XXX: BIG RED NOTICE! Changes MUST be binary file format compatible and MUST
+ * be synchronized with Writer's SwTableAutoFmtTbl sw/source/core/doc/tblafmt.cxx
+ */
+
+constexpr OUString sAutoTblFmtName = u"autotbl.fmt"_ustr;
+
+// till SO5PF
+const sal_uInt16 AUTOFORMAT_ID_X = 9501;
+const sal_uInt16 AUTOFORMAT_ID_358 = 9601;
+const sal_uInt16 AUTOFORMAT_DATA_ID_X = 9502;
+
+// from SO5 on
+// in following versions the value of the IDs must be higher
+const sal_uInt16 AUTOFORMAT_ID_504 = 9801;
+const sal_uInt16 AUTOFORMAT_DATA_ID_504 = 9802;
+
+const sal_uInt16 AUTOFORMAT_DATA_ID_552 = 9902;
+
+// --- from 680/dr25 on: store strings as UTF-8
+const sal_uInt16 AUTOFORMAT_ID_680DR25 = 10021;
+
+// --- Bug fix to fdo#31005: Table Autoformats does not save/apply all properties (Writer and Calc)
+const sal_uInt16 AUTOFORMAT_ID_31005 = 10041;
+const sal_uInt16 AUTOFORMAT_DATA_ID_31005 = 10042;
+
+// current version
+const sal_uInt16 AUTOFORMAT_ID = AUTOFORMAT_ID_31005;
+const sal_uInt16 AUTOFORMAT_DATA_ID = AUTOFORMAT_DATA_ID_31005;
+
+namespace
+{
+ /// Read an AutoFormatSwBlob from stream.
+ SvStream& operator>>(SvStream &stream, AutoFormatSwBlob &blob)
+ {
+ blob.Reset();
+
+ sal_uInt64 endOfBlob = 0;
+ stream.ReadUInt64( endOfBlob );
+
+ const sal_uInt64 currentPosition = stream.Tell();
+ const sal_uInt64 blobSize = endOfBlob - currentPosition;
+ // A zero-size indicates an empty blob. This happens when Calc creates a new autoformat,
+ // since it (naturally) doesn't have any writer-specific data to write.
+ if (blobSize)
+ {
+ blob.pData.reset(new sal_uInt8[blobSize]);
+ blob.size = static_cast<std::size_t>(blobSize);
+ stream.ReadBytes(blob.pData.get(), blob.size);
+ }
+
+ return stream;
+ }
+
+ /// Write an AutoFormatSwBlob to stream.
+ SvStream& WriteAutoFormatSwBlob(SvStream &stream, const AutoFormatSwBlob &blob)
+ {
+ const sal_uInt64 endOfBlob = stream.Tell() + sizeof(sal_uInt64) + blob.size;
+ stream.WriteUInt64( endOfBlob );
+ if (blob.size)
+ stream.WriteBytes(blob.pData.get(), blob.size);
+
+ return stream;
+ }
+}
+
+ScAfVersions::ScAfVersions()
+{
+}
+
+void ScAfVersions::Load( SvStream& rStream, sal_uInt16 nVer )
+{
+ LoadBlockA(rStream, nVer);
+ if (nVer >= AUTOFORMAT_ID_31005)
+ {
+ rStream >> swVersions;
+ }
+ LoadBlockB(rStream, nVer);
+}
+
+void ScAfVersions::Write(SvStream& rStream, sal_uInt16 fileVersion)
+{
+ AutoFormatVersions::WriteBlockA(rStream, fileVersion);
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ {
+ WriteAutoFormatSwBlob( rStream, swVersions );
+ }
+
+ AutoFormatVersions::WriteBlockB(rStream, fileVersion);
+}
+
+ScAutoFormatDataField::ScAutoFormatDataField()
+{
+ // need to set default instances for base class AutoFormatBase here
+ // due to resource defines (e.g. ATTR_FONT) which are not available
+ // in svx and different in the different usages of derivations
+ m_aFont = std::make_unique<SvxFontItem>(ATTR_FONT);
+ m_aHeight = std::make_unique<SvxFontHeightItem>(240, 100, ATTR_FONT_HEIGHT);
+ m_aWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, ATTR_FONT_WEIGHT);
+ m_aPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, ATTR_FONT_POSTURE);
+ m_aCJKFont = std::make_unique<SvxFontItem>(ATTR_CJK_FONT);
+ m_aCJKHeight = std::make_unique<SvxFontHeightItem>(240, 100, ATTR_CJK_FONT_HEIGHT);
+ m_aCJKWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, ATTR_CJK_FONT_WEIGHT);
+ m_aCJKPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, ATTR_CJK_FONT_POSTURE);
+ m_aCTLFont = std::make_unique<SvxFontItem>(ATTR_CTL_FONT);
+ m_aCTLHeight = std::make_unique<SvxFontHeightItem>(240, 100, ATTR_CTL_FONT_HEIGHT);
+ m_aCTLWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, ATTR_CTL_FONT_WEIGHT);
+ m_aCTLPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, ATTR_CTL_FONT_POSTURE);
+ m_aUnderline = std::make_unique<SvxUnderlineItem>(LINESTYLE_NONE,ATTR_FONT_UNDERLINE);
+ m_aOverline = std::make_unique<SvxOverlineItem>(LINESTYLE_NONE,ATTR_FONT_OVERLINE);
+ m_aCrossedOut = std::make_unique<SvxCrossedOutItem>(STRIKEOUT_NONE, ATTR_FONT_CROSSEDOUT);
+ m_aContour = std::make_unique<SvxContourItem>(false, ATTR_FONT_CONTOUR);
+ m_aShadowed = std::make_unique<SvxShadowedItem>(false, ATTR_FONT_SHADOWED);
+ m_aColor = std::make_unique<SvxColorItem>(ATTR_FONT_COLOR);
+ m_aBox = std::make_unique<SvxBoxItem>(ATTR_BORDER);
+ m_aTLBR = std::make_unique<SvxLineItem>(ATTR_BORDER_TLBR);
+ m_aBLTR = std::make_unique<SvxLineItem>(ATTR_BORDER_BLTR);
+ m_aBackground = std::make_unique<SvxBrushItem>(ATTR_BACKGROUND);
+ m_aAdjust = std::make_unique<SvxAdjustItem>(SvxAdjust::Left, 0);
+ m_aHorJustify = std::make_unique<SvxHorJustifyItem>(SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY);
+ m_aVerJustify = std::make_unique<SvxVerJustifyItem>(SvxCellVerJustify::Standard, ATTR_VER_JUSTIFY);
+ m_aStacked = std::make_unique<ScVerticalStackCell>();
+ m_aMargin = std::make_unique<SvxMarginItem>(ATTR_MARGIN);
+ m_aLinebreak = std::make_unique<ScLineBreakCell>();
+ m_aRotateAngle = std::make_unique<ScRotateValueItem>(0_deg100);
+ m_aRotateMode = std::make_unique<SvxRotateModeItem>(SVX_ROTATE_MODE_STANDARD, ATTR_ROTATE_MODE);
+}
+
+ScAutoFormatDataField::ScAutoFormatDataField( const ScAutoFormatDataField& rCopy )
+: AutoFormatBase(rCopy),
+ // m_swFields was not copied in original, needed?
+ aNumFormat( rCopy.aNumFormat )
+{
+}
+
+ScAutoFormatDataField::~ScAutoFormatDataField()
+{
+}
+
+bool ScAutoFormatDataField::Load( SvStream& rStream, const ScAfVersions& rVersions, sal_uInt16 nVer )
+{
+ LoadBlockA( rStream, rVersions, nVer );
+
+ if (nVer >= AUTOFORMAT_DATA_ID_31005)
+ {
+ rStream >> m_swFields;
+ }
+
+ LoadBlockB( rStream, rVersions, nVer );
+
+ if( 0 == rVersions.nNumFormatVersion )
+ {
+ // --- from 680/dr25 on: store strings as UTF-8
+ rtl_TextEncoding eCharSet = (nVer >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet();
+ aNumFormat.Load( rStream, eCharSet );
+ }
+
+ // adjust charset in font
+ rtl_TextEncoding eSysSet = osl_getThreadTextEncoding();
+ rtl_TextEncoding eSrcSet = rStream.GetStreamCharSet();
+ if( eSrcSet != eSysSet && m_aFont->GetCharSet() == eSrcSet )
+ m_aFont->SetCharSet(eSysSet);
+
+ return (rStream.GetError() == ERRCODE_NONE);
+}
+
+bool ScAutoFormatDataField::Save( SvStream& rStream, sal_uInt16 fileVersion )
+{
+ SaveBlockA( rStream, fileVersion );
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ {
+ WriteAutoFormatSwBlob( rStream, m_swFields );
+ }
+
+ SaveBlockB( rStream, fileVersion );
+
+ // --- from 680/dr25 on: store strings as UTF-8
+ aNumFormat.Save( rStream, RTL_TEXTENCODING_UTF8 );
+
+ return (rStream.GetError() == ERRCODE_NONE);
+}
+
+ScAutoFormatData::ScAutoFormatData()
+{
+ nStrResId = USHRT_MAX;
+
+ bIncludeValueFormat =
+ bIncludeFont =
+ bIncludeJustify =
+ bIncludeFrame =
+ bIncludeBackground =
+ bIncludeWidthHeight = true;
+
+ for( sal_uInt16 nIndex = 0; nIndex < 16; ++nIndex )
+ ppDataField[ nIndex ].reset( new ScAutoFormatDataField );
+}
+
+ScAutoFormatData::ScAutoFormatData( const ScAutoFormatData& rData ) :
+ aName( rData.aName ),
+ nStrResId( rData.nStrResId ),
+ bIncludeFont( rData.bIncludeFont ),
+ bIncludeJustify( rData.bIncludeJustify ),
+ bIncludeFrame( rData.bIncludeFrame ),
+ bIncludeBackground( rData.bIncludeBackground ),
+ bIncludeValueFormat( rData.bIncludeValueFormat ),
+ bIncludeWidthHeight( rData.bIncludeWidthHeight )
+{
+ for( sal_uInt16 nIndex = 0; nIndex < 16; ++nIndex )
+ ppDataField[ nIndex ].reset( new ScAutoFormatDataField( rData.GetField( nIndex ) ) );
+}
+
+ScAutoFormatData::~ScAutoFormatData()
+{
+}
+
+ScAutoFormatDataField& ScAutoFormatData::GetField( sal_uInt16 nIndex )
+{
+ OSL_ENSURE( nIndex < 16, "ScAutoFormatData::GetField - illegal index" );
+ OSL_ENSURE( ppDataField[ nIndex ], "ScAutoFormatData::GetField - no data" );
+ return *ppDataField[ nIndex ];
+}
+
+const ScAutoFormatDataField& ScAutoFormatData::GetField( sal_uInt16 nIndex ) const
+{
+ OSL_ENSURE( nIndex < 16, "ScAutoFormatData::GetField - illegal index" );
+ OSL_ENSURE( ppDataField[ nIndex ], "ScAutoFormatData::GetField - no data" );
+ return *ppDataField[ nIndex ];
+}
+
+const SfxPoolItem* ScAutoFormatData::GetItem( sal_uInt16 nIndex, sal_uInt16 nWhich ) const
+{
+ const ScAutoFormatDataField& rField = GetField( nIndex );
+ switch( nWhich )
+ {
+ case ATTR_FONT: return &rField.GetFont();
+ case ATTR_FONT_HEIGHT: return &rField.GetHeight();
+ case ATTR_FONT_WEIGHT: return &rField.GetWeight();
+ case ATTR_FONT_POSTURE: return &rField.GetPosture();
+ case ATTR_CJK_FONT: return &rField.GetCJKFont();
+ case ATTR_CJK_FONT_HEIGHT: return &rField.GetCJKHeight();
+ case ATTR_CJK_FONT_WEIGHT: return &rField.GetCJKWeight();
+ case ATTR_CJK_FONT_POSTURE: return &rField.GetCJKPosture();
+ case ATTR_CTL_FONT: return &rField.GetCTLFont();
+ case ATTR_CTL_FONT_HEIGHT: return &rField.GetCTLHeight();
+ case ATTR_CTL_FONT_WEIGHT: return &rField.GetCTLWeight();
+ case ATTR_CTL_FONT_POSTURE: return &rField.GetCTLPosture();
+ case ATTR_FONT_UNDERLINE: return &rField.GetUnderline();
+ case ATTR_FONT_OVERLINE: return &rField.GetOverline();
+ case ATTR_FONT_CROSSEDOUT: return &rField.GetCrossedOut();
+ case ATTR_FONT_CONTOUR: return &rField.GetContour();
+ case ATTR_FONT_SHADOWED: return &rField.GetShadowed();
+ case ATTR_FONT_COLOR: return &rField.GetColor();
+ case ATTR_BORDER: return &rField.GetBox();
+ case ATTR_BORDER_TLBR: return &rField.GetTLBR();
+ case ATTR_BORDER_BLTR: return &rField.GetBLTR();
+ case ATTR_BACKGROUND: return &rField.GetBackground();
+ case ATTR_HOR_JUSTIFY: return &rField.GetHorJustify();
+ case ATTR_VER_JUSTIFY: return &rField.GetVerJustify();
+ case ATTR_STACKED: return &rField.GetStacked();
+ case ATTR_MARGIN: return &rField.GetMargin();
+ case ATTR_LINEBREAK: return &rField.GetLinebreak();
+ case ATTR_ROTATE_VALUE: return &rField.GetRotateAngle();
+ case ATTR_ROTATE_MODE: return &rField.GetRotateMode();
+ }
+ return nullptr;
+}
+
+void ScAutoFormatData::PutItem( sal_uInt16 nIndex, const SfxPoolItem& rItem )
+{
+ ScAutoFormatDataField& rField = GetField( nIndex );
+ switch( rItem.Which() )
+ {
+ case ATTR_FONT: rField.SetFont( rItem.StaticWhichCast(ATTR_FONT) ); break;
+ case ATTR_FONT_HEIGHT: rField.SetHeight( rItem.StaticWhichCast(ATTR_FONT_HEIGHT) ); break;
+ case ATTR_FONT_WEIGHT: rField.SetWeight( rItem.StaticWhichCast(ATTR_FONT_WEIGHT) ); break;
+ case ATTR_FONT_POSTURE: rField.SetPosture( rItem.StaticWhichCast(ATTR_FONT_POSTURE) ); break;
+ case ATTR_CJK_FONT: rField.SetCJKFont( rItem.StaticWhichCast(ATTR_CJK_FONT) ); break;
+ case ATTR_CJK_FONT_HEIGHT: rField.SetCJKHeight( rItem.StaticWhichCast(ATTR_CJK_FONT_HEIGHT) ); break;
+ case ATTR_CJK_FONT_WEIGHT: rField.SetCJKWeight( rItem.StaticWhichCast(ATTR_CJK_FONT_WEIGHT) ); break;
+ case ATTR_CJK_FONT_POSTURE: rField.SetCJKPosture( rItem.StaticWhichCast(ATTR_CJK_FONT_POSTURE) ); break;
+ case ATTR_CTL_FONT: rField.SetCTLFont( rItem.StaticWhichCast(ATTR_CTL_FONT) ); break;
+ case ATTR_CTL_FONT_HEIGHT: rField.SetCTLHeight( rItem.StaticWhichCast(ATTR_CTL_FONT_HEIGHT) ); break;
+ case ATTR_CTL_FONT_WEIGHT: rField.SetCTLWeight( rItem.StaticWhichCast(ATTR_CTL_FONT_WEIGHT) ); break;
+ case ATTR_CTL_FONT_POSTURE: rField.SetCTLPosture( rItem.StaticWhichCast(ATTR_CTL_FONT_POSTURE) ); break;
+ case ATTR_FONT_UNDERLINE: rField.SetUnderline( rItem.StaticWhichCast(ATTR_FONT_UNDERLINE) ); break;
+ case ATTR_FONT_OVERLINE: rField.SetOverline( rItem.StaticWhichCast(ATTR_FONT_OVERLINE) ); break;
+ case ATTR_FONT_CROSSEDOUT: rField.SetCrossedOut( rItem.StaticWhichCast(ATTR_FONT_CROSSEDOUT) ); break;
+ case ATTR_FONT_CONTOUR: rField.SetContour( rItem.StaticWhichCast(ATTR_FONT_CONTOUR) ); break;
+ case ATTR_FONT_SHADOWED: rField.SetShadowed( rItem.StaticWhichCast(ATTR_FONT_SHADOWED) ); break;
+ case ATTR_FONT_COLOR: rField.SetColor( rItem.StaticWhichCast(ATTR_FONT_COLOR) ); break;
+ case ATTR_BORDER: rField.SetBox( rItem.StaticWhichCast(ATTR_BORDER) ); break;
+ case ATTR_BORDER_TLBR: rField.SetTLBR( rItem.StaticWhichCast(ATTR_BORDER_TLBR) ); break;
+ case ATTR_BORDER_BLTR: rField.SetBLTR( rItem.StaticWhichCast(ATTR_BORDER_BLTR) ); break;
+ case ATTR_BACKGROUND: rField.SetBackground( rItem.StaticWhichCast(ATTR_BACKGROUND) ); break;
+ case ATTR_HOR_JUSTIFY: rField.SetHorJustify( rItem.StaticWhichCast(ATTR_HOR_JUSTIFY) ); break;
+ case ATTR_VER_JUSTIFY: rField.SetVerJustify( rItem.StaticWhichCast(ATTR_VER_JUSTIFY) ); break;
+ case ATTR_STACKED: rField.SetStacked( rItem.StaticWhichCast(ATTR_STACKED) ); break;
+ case ATTR_MARGIN: rField.SetMargin( rItem.StaticWhichCast(ATTR_MARGIN) ); break;
+ case ATTR_LINEBREAK: rField.SetLinebreak( rItem.StaticWhichCast(ATTR_LINEBREAK) ); break;
+ case ATTR_ROTATE_VALUE: rField.SetRotateAngle( rItem.StaticWhichCast(ATTR_ROTATE_VALUE) ); break;
+ case ATTR_ROTATE_MODE: rField.SetRotateMode( rItem.StaticWhichCast(ATTR_ROTATE_MODE) ); break;
+ }
+}
+
+void ScAutoFormatData::CopyItem( sal_uInt16 nToIndex, sal_uInt16 nFromIndex, sal_uInt16 nWhich )
+{
+ const SfxPoolItem* pItem = GetItem( nFromIndex, nWhich );
+ if( pItem )
+ PutItem( nToIndex, *pItem );
+}
+
+const ScNumFormatAbbrev& ScAutoFormatData::GetNumFormat( sal_uInt16 nIndex ) const
+{
+ return GetField( nIndex ).GetNumFormat();
+}
+
+bool ScAutoFormatData::HasSameData( sal_uInt16 nIndex1, sal_uInt16 nIndex2 ) const
+{
+ bool bEqual = true;
+ const ScAutoFormatDataField& rField1 = GetField( nIndex1 );
+ const ScAutoFormatDataField& rField2 = GetField( nIndex2 );
+
+ if( bIncludeValueFormat )
+ {
+ bEqual = bEqual
+ && (rField1.GetNumFormat() == rField2.GetNumFormat());
+ }
+ if( bIncludeFont )
+ {
+ bEqual = bEqual
+ && (rField1.GetFont() == rField2.GetFont())
+ && (rField1.GetHeight() == rField2.GetHeight())
+ && (rField1.GetWeight() == rField2.GetWeight())
+ && (rField1.GetPosture() == rField2.GetPosture())
+ && (rField1.GetCJKFont() == rField2.GetCJKFont())
+ && (rField1.GetCJKHeight() == rField2.GetCJKHeight())
+ && (rField1.GetCJKWeight() == rField2.GetCJKWeight())
+ && (rField1.GetCJKPosture() == rField2.GetCJKPosture())
+ && (rField1.GetCTLFont() == rField2.GetCTLFont())
+ && (rField1.GetCTLHeight() == rField2.GetCTLHeight())
+ && (rField1.GetCTLWeight() == rField2.GetCTLWeight())
+ && (rField1.GetCTLPosture() == rField2.GetCTLPosture())
+ && (rField1.GetUnderline() == rField2.GetUnderline())
+ && (rField1.GetOverline() == rField2.GetOverline())
+ && (rField1.GetCrossedOut() == rField2.GetCrossedOut())
+ && (rField1.GetContour() == rField2.GetContour())
+ && (rField1.GetShadowed() == rField2.GetShadowed())
+ && (rField1.GetColor() == rField2.GetColor());
+ }
+ if( bIncludeJustify )
+ {
+ bEqual = bEqual
+ && (rField1.GetHorJustify() == rField2.GetHorJustify())
+ && (rField1.GetVerJustify() == rField2.GetVerJustify())
+ && (rField1.GetStacked() == rField2.GetStacked())
+ && (rField1.GetLinebreak() == rField2.GetLinebreak())
+ && (rField1.GetMargin() == rField2.GetMargin())
+ && (rField1.GetRotateAngle() == rField2.GetRotateAngle())
+ && (rField1.GetRotateMode() == rField2.GetRotateMode());
+ }
+ if( bIncludeFrame )
+ {
+ bEqual = bEqual
+ && (rField1.GetBox() == rField2.GetBox())
+ && (rField1.GetTLBR() == rField2.GetTLBR())
+ && (rField1.GetBLTR() == rField2.GetBLTR());
+ }
+ if( bIncludeBackground )
+ {
+ bEqual = bEqual
+ && (rField1.GetBackground() == rField2.GetBackground());
+ }
+ return bEqual;
+}
+
+void ScAutoFormatData::FillToItemSet( sal_uInt16 nIndex, SfxItemSet& rItemSet, const ScDocument& rDoc ) const
+{
+ const ScAutoFormatDataField& rField = GetField( nIndex );
+
+ if( bIncludeValueFormat )
+ {
+ ScNumFormatAbbrev& rNumFormat = const_cast<ScNumFormatAbbrev&>(rField.GetNumFormat());
+ SfxUInt32Item aValueFormat( ATTR_VALUE_FORMAT, 0 );
+ aValueFormat.SetValue( rNumFormat.GetFormatIndex( *rDoc.GetFormatTable() ) );
+ rItemSet.Put( aValueFormat );
+ rItemSet.Put( SvxLanguageItem( rNumFormat.GetLanguage(), ATTR_LANGUAGE_FORMAT ) );
+ }
+ if( bIncludeFont )
+ {
+ rItemSet.Put( rField.GetFont() );
+ rItemSet.Put( rField.GetHeight() );
+ rItemSet.Put( rField.GetWeight() );
+ rItemSet.Put( rField.GetPosture() );
+ // do not insert empty CJK font
+ const SvxFontItem& rCJKFont = rField.GetCJKFont();
+ if (!rCJKFont.GetStyleName().isEmpty())
+ {
+ rItemSet.Put( rCJKFont );
+ rItemSet.Put( rField.GetCJKHeight() );
+ rItemSet.Put( rField.GetCJKWeight() );
+ rItemSet.Put( rField.GetCJKPosture() );
+ }
+ else
+ {
+ SvxFontHeightItem aFontHeightItem(rField.GetHeight());
+ aFontHeightItem.SetWhich(ATTR_CJK_FONT_HEIGHT);
+ rItemSet.Put( aFontHeightItem );
+ SvxWeightItem aWeightItem(rField.GetWeight());
+ aWeightItem.SetWhich(ATTR_CJK_FONT_WEIGHT);
+ rItemSet.Put( aWeightItem );
+ SvxPostureItem aPostureItem(rField.GetPosture());
+ aPostureItem.SetWhich(ATTR_CJK_FONT_POSTURE);
+ rItemSet.Put( aPostureItem );
+ }
+ // do not insert empty CTL font
+ const SvxFontItem& rCTLFont = rField.GetCTLFont();
+ if (!rCTLFont.GetStyleName().isEmpty())
+ {
+ rItemSet.Put( rCTLFont );
+ rItemSet.Put( rField.GetCTLHeight() );
+ rItemSet.Put( rField.GetCTLWeight() );
+ rItemSet.Put( rField.GetCTLPosture() );
+ }
+ else
+ {
+ SvxFontHeightItem aFontHeightItem(rField.GetHeight());
+ aFontHeightItem.SetWhich(ATTR_CTL_FONT_HEIGHT);
+ rItemSet.Put( aFontHeightItem );
+ SvxWeightItem aWeightItem(rField.GetWeight());
+ aWeightItem.SetWhich(ATTR_CTL_FONT_WEIGHT);
+ rItemSet.Put( aWeightItem );
+ SvxPostureItem aPostureItem(rField.GetPosture());
+ aPostureItem.SetWhich(ATTR_CTL_FONT_POSTURE);
+ rItemSet.Put( aPostureItem );
+ }
+ rItemSet.Put( rField.GetUnderline() );
+ rItemSet.Put( rField.GetOverline() );
+ rItemSet.Put( rField.GetCrossedOut() );
+ rItemSet.Put( rField.GetContour() );
+ rItemSet.Put( rField.GetShadowed() );
+ rItemSet.Put( rField.GetColor() );
+ }
+ if( bIncludeJustify )
+ {
+ rItemSet.Put( rField.GetHorJustify() );
+ rItemSet.Put( rField.GetVerJustify() );
+ rItemSet.Put( rField.GetStacked() );
+ rItemSet.Put( rField.GetLinebreak() );
+ rItemSet.Put( rField.GetMargin() );
+ rItemSet.Put( rField.GetRotateAngle() );
+ rItemSet.Put( rField.GetRotateMode() );
+ }
+ if( bIncludeFrame )
+ {
+ rItemSet.Put( rField.GetBox() );
+ rItemSet.Put( rField.GetTLBR() );
+ rItemSet.Put( rField.GetBLTR() );
+ }
+ if( bIncludeBackground )
+ rItemSet.Put( rField.GetBackground() );
+}
+
+void ScAutoFormatData::GetFromItemSet( sal_uInt16 nIndex, const SfxItemSet& rItemSet, const ScNumFormatAbbrev& rNumFormat )
+{
+ ScAutoFormatDataField& rField = GetField( nIndex );
+
+ rField.SetNumFormat ( rNumFormat);
+ rField.SetFont ( rItemSet.Get( ATTR_FONT ) );
+ rField.SetHeight ( rItemSet.Get( ATTR_FONT_HEIGHT ) );
+ rField.SetWeight ( rItemSet.Get( ATTR_FONT_WEIGHT ) );
+ rField.SetPosture ( rItemSet.Get( ATTR_FONT_POSTURE ) );
+ rField.SetCJKFont ( rItemSet.Get( ATTR_CJK_FONT ) );
+ rField.SetCJKHeight ( rItemSet.Get( ATTR_CJK_FONT_HEIGHT ) );
+ rField.SetCJKWeight ( rItemSet.Get( ATTR_CJK_FONT_WEIGHT ) );
+ rField.SetCJKPosture ( rItemSet.Get( ATTR_CJK_FONT_POSTURE ) );
+ rField.SetCTLFont ( rItemSet.Get( ATTR_CTL_FONT ) );
+ rField.SetCTLHeight ( rItemSet.Get( ATTR_CTL_FONT_HEIGHT ) );
+ rField.SetCTLWeight ( rItemSet.Get( ATTR_CTL_FONT_WEIGHT ) );
+ rField.SetCTLPosture ( rItemSet.Get( ATTR_CTL_FONT_POSTURE ) );
+ rField.SetUnderline ( rItemSet.Get( ATTR_FONT_UNDERLINE ) );
+ rField.SetOverline ( rItemSet.Get( ATTR_FONT_OVERLINE ) );
+ rField.SetCrossedOut ( rItemSet.Get( ATTR_FONT_CROSSEDOUT ) );
+ rField.SetContour ( rItemSet.Get( ATTR_FONT_CONTOUR ) );
+ rField.SetShadowed ( rItemSet.Get( ATTR_FONT_SHADOWED ) );
+ rField.SetColor ( rItemSet.Get( ATTR_FONT_COLOR ) );
+ rField.SetTLBR ( rItemSet.Get( ATTR_BORDER_TLBR ) );
+ rField.SetBLTR ( rItemSet.Get( ATTR_BORDER_BLTR ) );
+ rField.SetHorJustify ( rItemSet.Get( ATTR_HOR_JUSTIFY ) );
+ rField.SetVerJustify ( rItemSet.Get( ATTR_VER_JUSTIFY ) );
+ rField.SetStacked ( rItemSet.Get( ATTR_STACKED ) );
+ rField.SetLinebreak ( rItemSet.Get( ATTR_LINEBREAK ) );
+ rField.SetMargin ( rItemSet.Get( ATTR_MARGIN ) );
+ rField.SetBackground ( rItemSet.Get( ATTR_BACKGROUND ) );
+ rField.SetRotateAngle ( rItemSet.Get( ATTR_ROTATE_VALUE ) );
+ rField.SetRotateMode ( rItemSet.Get( ATTR_ROTATE_MODE ) );
+}
+
+const TranslateId RID_SVXSTR_TBLAFMT[] =
+{
+ RID_SVXSTR_TBLAFMT_3D,
+ RID_SVXSTR_TBLAFMT_BLACK1,
+ RID_SVXSTR_TBLAFMT_BLACK2,
+ RID_SVXSTR_TBLAFMT_BLUE,
+ RID_SVXSTR_TBLAFMT_BROWN,
+ RID_SVXSTR_TBLAFMT_CURRENCY,
+ RID_SVXSTR_TBLAFMT_CURRENCY_3D,
+ RID_SVXSTR_TBLAFMT_CURRENCY_GRAY,
+ RID_SVXSTR_TBLAFMT_CURRENCY_LAVENDER,
+ RID_SVXSTR_TBLAFMT_CURRENCY_TURQUOISE,
+ RID_SVXSTR_TBLAFMT_GRAY,
+ RID_SVXSTR_TBLAFMT_GREEN,
+ RID_SVXSTR_TBLAFMT_LAVENDER,
+ RID_SVXSTR_TBLAFMT_RED,
+ RID_SVXSTR_TBLAFMT_TURQUOISE,
+ RID_SVXSTR_TBLAFMT_YELLOW,
+ RID_SVXSTR_TBLAFMT_LO6_ACADEMIC,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_BLUE,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_GREEN,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_RED,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_YELLOW,
+ RID_SVXSTR_TBLAFMT_LO6_ELEGANT,
+ RID_SVXSTR_TBLAFMT_LO6_FINANCIAL,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_GRID_COLUMNS,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_GRID_ROWS,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_LIST_SHADED
+};
+
+bool ScAutoFormatData::Load( SvStream& rStream, const ScAfVersions& rVersions )
+{
+ sal_uInt16 nVer = 0;
+ rStream.ReadUInt16( nVer );
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+ if( bRet && (nVer == AUTOFORMAT_DATA_ID_X ||
+ (AUTOFORMAT_DATA_ID_504 <= nVer && nVer <= AUTOFORMAT_DATA_ID)) )
+ {
+ // --- from 680/dr25 on: store strings as UTF-8
+ if (nVer >= AUTOFORMAT_ID_680DR25)
+ {
+ aName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStream,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ aName = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() );
+
+ if( AUTOFORMAT_DATA_ID_552 <= nVer )
+ {
+ rStream.ReadUInt16( nStrResId );
+ if (nStrResId < SAL_N_ELEMENTS(RID_SVXSTR_TBLAFMT))
+ aName = SvxResId(RID_SVXSTR_TBLAFMT[nStrResId]);
+ else
+ nStrResId = USHRT_MAX;
+ }
+
+ bool b;
+ rStream.ReadCharAsBool( b ); bIncludeFont = b;
+ rStream.ReadCharAsBool( b ); bIncludeJustify = b;
+ rStream.ReadCharAsBool( b ); bIncludeFrame = b;
+ rStream.ReadCharAsBool( b ); bIncludeBackground = b;
+ rStream.ReadCharAsBool( b ); bIncludeValueFormat = b;
+ rStream.ReadCharAsBool( b ); bIncludeWidthHeight = b;
+
+ if (nVer >= AUTOFORMAT_DATA_ID_31005)
+ rStream >> m_swFields;
+
+ bRet = ERRCODE_NONE == rStream.GetError();
+ for( sal_uInt16 i = 0; bRet && i < 16; ++i )
+ bRet = GetField( i ).Load( rStream, rVersions, nVer );
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+bool ScAutoFormatData::Save(SvStream& rStream, sal_uInt16 fileVersion)
+{
+ rStream.WriteUInt16( AUTOFORMAT_DATA_ID );
+ // --- from 680/dr25 on: store strings as UTF-8
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStream, aName, RTL_TEXTENCODING_UTF8);
+
+ rStream.WriteUInt16( nStrResId );
+ rStream.WriteBool( bIncludeFont );
+ rStream.WriteBool( bIncludeJustify );
+ rStream.WriteBool( bIncludeFrame );
+ rStream.WriteBool( bIncludeBackground );
+ rStream.WriteBool( bIncludeValueFormat );
+ rStream.WriteBool( bIncludeWidthHeight );
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ WriteAutoFormatSwBlob( rStream, m_swFields );
+
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+ for (sal_uInt16 i = 0; bRet && (i < 16); i++)
+ bRet = GetField( i ).Save( rStream, fileVersion );
+
+ return bRet;
+}
+
+ScAutoFormat::ScAutoFormat() :
+ mbSaveLater(false)
+{
+ // create default autoformat
+ std::unique_ptr<ScAutoFormatData> pData(new ScAutoFormatData);
+ OUString aName(ScResId(STR_STYLENAME_STANDARD));
+ pData->SetName(aName);
+
+ // default font, default height
+ vcl::Font aStdFont = OutputDevice::GetDefaultFont(
+ DefaultFontType::LATIN_SPREADSHEET, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::OnlyOne );
+ SvxFontItem aFontItem(
+ aStdFont.GetFamilyType(), aStdFont.GetFamilyName(), aStdFont.GetStyleName(),
+ aStdFont.GetPitch(), aStdFont.GetCharSet(), ATTR_FONT );
+
+ aStdFont = OutputDevice::GetDefaultFont(
+ DefaultFontType::CJK_SPREADSHEET, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::OnlyOne );
+ SvxFontItem aCJKFontItem(
+ aStdFont.GetFamilyType(), aStdFont.GetFamilyName(), aStdFont.GetStyleName(),
+ aStdFont.GetPitch(), aStdFont.GetCharSet(), ATTR_CJK_FONT );
+
+ aStdFont = OutputDevice::GetDefaultFont(
+ DefaultFontType::CTL_SPREADSHEET, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::OnlyOne );
+ SvxFontItem aCTLFontItem(
+ aStdFont.GetFamilyType(), aStdFont.GetFamilyName(), aStdFont.GetStyleName(),
+ aStdFont.GetPitch(), aStdFont.GetCharSet(), ATTR_CTL_FONT );
+
+ SvxFontHeightItem aHeight( 200, 100, ATTR_FONT_HEIGHT ); // 10 pt;
+
+ // black thin border
+ Color aBlack( COL_BLACK );
+ ::editeng::SvxBorderLine aLine( &aBlack, SvxBorderLineWidth::VeryThin );
+ SvxBoxItem aBox( ATTR_BORDER );
+ aBox.SetLine(&aLine, SvxBoxItemLine::LEFT);
+ aBox.SetLine(&aLine, SvxBoxItemLine::TOP);
+ aBox.SetLine(&aLine, SvxBoxItemLine::RIGHT);
+ aBox.SetLine(&aLine, SvxBoxItemLine::BOTTOM);
+
+ Color aWhite(COL_WHITE);
+ SvxColorItem aWhiteText( aWhite, ATTR_FONT_COLOR );
+ SvxColorItem aBlackText( aBlack, ATTR_FONT_COLOR );
+ SvxBrushItem aBlueBack( COL_BLUE, ATTR_BACKGROUND );
+ SvxBrushItem aWhiteBack( aWhite, ATTR_BACKGROUND );
+ SvxBrushItem aGray70Back( Color(0x4d, 0x4d, 0x4d), ATTR_BACKGROUND );
+ SvxBrushItem aGray20Back( Color(0xcc, 0xcc, 0xcc), ATTR_BACKGROUND );
+
+ for (sal_uInt16 i=0; i<16; i++)
+ {
+ pData->PutItem( i, aBox );
+ pData->PutItem( i, aFontItem );
+ pData->PutItem( i, aCJKFontItem );
+ pData->PutItem( i, aCTLFontItem );
+ aHeight.SetWhich( ATTR_FONT_HEIGHT );
+ pData->PutItem( i, aHeight );
+ aHeight.SetWhich( ATTR_CJK_FONT_HEIGHT );
+ pData->PutItem( i, aHeight );
+ aHeight.SetWhich( ATTR_CTL_FONT_HEIGHT );
+ pData->PutItem( i, aHeight );
+ if (i<4) // top: white on blue
+ {
+ pData->PutItem( i, aWhiteText );
+ pData->PutItem( i, aBlueBack );
+ }
+ else if ( i%4 == 0 ) // left: white on gray70
+ {
+ pData->PutItem( i, aWhiteText );
+ pData->PutItem( i, aGray70Back );
+ }
+ else if ( i%4 == 3 || i >= 12 ) // right and bottom: black on gray20
+ {
+ pData->PutItem( i, aBlackText );
+ pData->PutItem( i, aGray20Back );
+ }
+ else // center: black on white
+ {
+ pData->PutItem( i, aBlackText );
+ pData->PutItem( i, aWhiteBack );
+ }
+ }
+
+ insert(std::move(pData));
+}
+
+bool DefaultFirstEntry::operator() (const OUString& left, const OUString& right) const
+{
+ OUString aStrStandard(ScResId(STR_STYLENAME_STANDARD));
+ if (ScGlobal::GetTransliteration().isEqual( left, right ) )
+ return false;
+ if ( ScGlobal::GetTransliteration().isEqual( left, aStrStandard ) )
+ return true;
+ if ( ScGlobal::GetTransliteration().isEqual( right, aStrStandard ) )
+ return false;
+ return ScGlobal::GetCollator().compareString( left, right) < 0;
+}
+
+void ScAutoFormat::SetSaveLater( bool bSet )
+{
+ mbSaveLater = bSet;
+}
+
+const ScAutoFormatData* ScAutoFormat::findByIndex(size_t nIndex) const
+{
+ if (nIndex >= m_Data.size())
+ return nullptr;
+
+ MapType::const_iterator it = m_Data.begin();
+ std::advance(it, nIndex);
+ return it->second.get();
+}
+
+ScAutoFormatData* ScAutoFormat::findByIndex(size_t nIndex)
+{
+ if (nIndex >= m_Data.size())
+ return nullptr;
+
+ MapType::iterator it = m_Data.begin();
+ std::advance(it, nIndex);
+ return it->second.get();
+}
+
+ScAutoFormat::iterator ScAutoFormat::find(const OUString& rName)
+{
+ return m_Data.find(rName);
+}
+
+ScAutoFormat::iterator ScAutoFormat::insert(std::unique_ptr<ScAutoFormatData> pNew)
+{
+ OUString aName = pNew->GetName();
+ return m_Data.insert(std::make_pair(aName, std::move(pNew))).first;
+}
+
+void ScAutoFormat::erase(const iterator& it)
+{
+ m_Data.erase(it);
+}
+
+size_t ScAutoFormat::size() const
+{
+ return m_Data.size();
+}
+
+ScAutoFormat::const_iterator ScAutoFormat::begin() const
+{
+ return m_Data.begin();
+}
+
+ScAutoFormat::const_iterator ScAutoFormat::end() const
+{
+ return m_Data.end();
+}
+
+ScAutoFormat::iterator ScAutoFormat::begin()
+{
+ return m_Data.begin();
+}
+
+ScAutoFormat::iterator ScAutoFormat::end()
+{
+ return m_Data.end();
+}
+
+void ScAutoFormat::Load()
+{
+ INetURLObject aURL;
+ SvtPathOptions aPathOpt;
+ aURL.SetSmartURL( aPathOpt.GetUserConfigPath() );
+ aURL.setFinalSlash();
+ aURL.Append( sAutoTblFmtName );
+
+ SfxMedium aMedium( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ );
+ SvStream* pStream = aMedium.GetInStream();
+ bool bRet = (pStream && pStream->GetError() == ERRCODE_NONE);
+ if (bRet)
+ {
+ SvStream& rStream = *pStream;
+ // Attention: A common header has to be read
+ sal_uInt16 nVal = 0;
+ rStream.ReadUInt16( nVal );
+ bRet = ERRCODE_NONE == rStream.GetError();
+
+ if (bRet)
+ {
+ if( nVal == AUTOFORMAT_ID_358 ||
+ (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) )
+ {
+ sal_uInt8 nChrSet, nCnt;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.ReadUChar( nCnt ).ReadUChar( nChrSet );
+ if( rStream.Tell() != sal_uLong(nPos + nCnt) )
+ {
+ OSL_FAIL( "header contains more/newer data" );
+ rStream.Seek( nPos + nCnt );
+ }
+ rStream.SetStreamCharSet( GetSOLoadTextEncoding( nChrSet ) );
+ rStream.SetVersion( SOFFICE_FILEFORMAT_40 );
+ }
+
+ if( nVal == AUTOFORMAT_ID_358 || nVal == AUTOFORMAT_ID_X ||
+ (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) )
+ {
+ m_aVersions.Load( rStream, nVal ); // item versions
+
+ sal_uInt16 nCnt = 0;
+ rStream.ReadUInt16( nCnt );
+ bRet = (rStream.GetError() == ERRCODE_NONE);
+
+ // there has to at least be a sal_uInt16 header
+ const size_t nMaxRecords = rStream.remainingSize() / sizeof(sal_uInt16);
+ if (nCnt > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCnt << " claimed, truncating");
+ nCnt = nMaxRecords;
+ }
+
+ for (sal_uInt16 i=0; bRet && (i < nCnt); i++)
+ {
+ std::unique_ptr<ScAutoFormatData> pData(new ScAutoFormatData());
+ bRet = pData->Load(rStream, m_aVersions);
+ insert(std::move(pData));
+ }
+ }
+ }
+ }
+ mbSaveLater = false;
+}
+
+bool ScAutoFormat::Save()
+{
+ INetURLObject aURL;
+ SvtPathOptions aPathOpt;
+ aURL.SetSmartURL( aPathOpt.GetUserConfigPath() );
+ aURL.setFinalSlash();
+ aURL.Append(sAutoTblFmtName);
+
+ SfxMedium aMedium( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE );
+ SvStream* pStream = aMedium.GetOutStream();
+ bool bRet = (pStream && pStream->GetError() == ERRCODE_NONE);
+ if (bRet)
+ {
+ const sal_uInt16 fileVersion = SOFFICE_FILEFORMAT_50;
+ SvStream& rStream = *pStream;
+ rStream.SetVersion( fileVersion );
+
+ // Attention: A common header has to be saved
+ rStream.WriteUInt16( AUTOFORMAT_ID )
+ .WriteUChar( 2 ) // Number of chars of the header including this
+ .WriteUChar( ::GetSOStoreTextEncoding(
+ osl_getThreadTextEncoding() ) );
+ m_aVersions.Write(rStream, fileVersion);
+
+ bRet &= (rStream.GetError() == ERRCODE_NONE);
+
+ rStream.WriteUInt16( m_Data.size() - 1 );
+ bRet &= (rStream.GetError() == ERRCODE_NONE);
+ MapType::iterator it = m_Data.begin(), itEnd = m_Data.end();
+ if (it != itEnd)
+ {
+ for (++it; bRet && it != itEnd; ++it) // Skip the first item.
+ {
+ bRet &= it->second->Save(rStream, fileVersion);
+ }
+ }
+
+ rStream.FlushBuffer();
+
+ aMedium.Commit();
+ }
+ mbSaveLater = false;
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/calcconfig.cxx b/sc/source/core/tool/calcconfig.cxx
new file mode 100644
index 0000000000..7eb36d73a9
--- /dev/null
+++ b/sc/source/core/tool/calcconfig.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <ostream>
+
+#include <formula/FormulaCompiler.hxx>
+#include <formula/grammar.hxx>
+#include <formula/opcode.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <calcconfig.hxx>
+
+#include <comphelper/configurationlistener.hxx>
+
+using comphelper::ConfigurationListener;
+
+static rtl::Reference<ConfigurationListener> const & getMiscListener()
+{
+ static rtl::Reference<ConfigurationListener> xListener(new ConfigurationListener("/org.openoffice.Office.Common/Misc"));
+ return xListener;
+}
+
+static rtl::Reference<ConfigurationListener> const & getFormulaCalculationListener()
+{
+ static rtl::Reference<ConfigurationListener> xListener(new ConfigurationListener("/org.openoffice.Office.Calc/Formula/Calculation"));
+ return xListener;
+}
+
+static ForceCalculationType forceCalculationTypeInit()
+{
+ const char* env = getenv( "SC_FORCE_CALCULATION" );
+ if( env != nullptr )
+ {
+ if( strcmp( env, "opencl" ) == 0 )
+ {
+ SAL_INFO("sc.core.formulagroup", "Forcing calculations to use OpenCL");
+ return ForceCalculationOpenCL;
+ }
+ if( strcmp( env, "threads" ) == 0 )
+ {
+ SAL_INFO("sc.core.formulagroup", "Forcing calculations to use threads");
+ return ForceCalculationThreads;
+ }
+ if( strcmp( env, "core" ) == 0 )
+ {
+ SAL_INFO("sc.core.formulagroup", "Forcing calculations to use core");
+ return ForceCalculationCore;
+ }
+ SAL_WARN("sc.core.formulagroup", "Unrecognized value of SC_FORCE_CALCULATION");
+ abort();
+ }
+ return ForceCalculationNone;
+}
+
+ForceCalculationType ScCalcConfig::getForceCalculationType()
+{
+ static const ForceCalculationType type = forceCalculationTypeInit();
+ return type;
+}
+
+bool ScCalcConfig::isOpenCLEnabled()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ static ForceCalculationType force = getForceCalculationType();
+ if( force != ForceCalculationNone )
+ return force == ForceCalculationOpenCL;
+ static comphelper::ConfigurationListenerProperty<bool> gOpenCLEnabled(getMiscListener(), "UseOpenCL");
+ return gOpenCLEnabled.get();
+}
+
+bool ScCalcConfig::isThreadingEnabled()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ static ForceCalculationType force = getForceCalculationType();
+ if( force != ForceCalculationNone )
+ return force == ForceCalculationThreads;
+ static comphelper::ConfigurationListenerProperty<bool> gThreadingEnabled(getFormulaCalculationListener(), "UseThreadedCalculationForFormulaGroups");
+ return gThreadingEnabled.get();
+}
+
+ScCalcConfig::ScCalcConfig() :
+ meStringRefAddressSyntax(formula::FormulaGrammar::CONV_UNSPECIFIED),
+ meStringConversion(StringConversion::LOCALE), // old LibreOffice behavior
+ mbEmptyStringAsZero(false),
+ mbHasStringRefSyntax(false)
+{
+ setOpenCLConfigToDefault();
+}
+
+void ScCalcConfig::setOpenCLConfigToDefault()
+{
+ // Keep in order of opcode value, is that clearest? (Random order,
+ // at least, would make no sense at all.)
+ static const OpCodeSet pDefaultOpenCLSubsetOpCodes(new o3tl::sorted_vector<OpCode>({
+ ocAdd,
+ ocSub,
+ ocNegSub,
+ ocMul,
+ ocDiv,
+ ocPow,
+ ocRandom,
+ ocSin,
+ ocCos,
+ ocTan,
+ ocArcTan,
+ ocExp,
+ ocLn,
+ ocSqrt,
+ ocStdNormDist,
+ ocSNormInv,
+ ocRound,
+ ocPower,
+ ocSumProduct,
+ ocMin,
+ ocMax,
+ ocSum,
+ ocProduct,
+ ocAverage,
+ ocCount,
+ ocVar,
+ ocNormDist,
+ ocVLookup,
+ ocCorrel,
+ ocCovar,
+ ocPearson,
+ ocSlope,
+ ocSumIfs}));
+
+ // Note that these defaults better be kept in sync with those in
+ // officecfg/registry/schema/org/openoffice/Office/Calc.xcs.
+ // Crazy.
+ mbOpenCLSubsetOnly = true;
+ mbOpenCLAutoSelect = true;
+ mnOpenCLMinimumFormulaGroupSize = 100;
+ mpOpenCLSubsetOpCodes = pDefaultOpenCLSubsetOpCodes;
+}
+
+void ScCalcConfig::reset()
+{
+ *this = ScCalcConfig();
+}
+
+void ScCalcConfig::MergeDocumentSpecific( const ScCalcConfig& r )
+{
+ // String conversion options are per document.
+ meStringConversion = r.meStringConversion;
+ mbEmptyStringAsZero = r.mbEmptyStringAsZero;
+ // INDIRECT ref syntax is per document.
+ meStringRefAddressSyntax = r.meStringRefAddressSyntax;
+ mbHasStringRefSyntax = r.mbHasStringRefSyntax;
+}
+
+void ScCalcConfig::SetStringRefSyntax( formula::FormulaGrammar::AddressConvention eConv )
+{
+ meStringRefAddressSyntax = eConv;
+ mbHasStringRefSyntax = true;
+}
+
+bool ScCalcConfig::operator== (const ScCalcConfig& r) const
+{
+ return meStringRefAddressSyntax == r.meStringRefAddressSyntax &&
+ meStringConversion == r.meStringConversion &&
+ mbEmptyStringAsZero == r.mbEmptyStringAsZero &&
+ mbHasStringRefSyntax == r.mbHasStringRefSyntax &&
+ mbOpenCLSubsetOnly == r.mbOpenCLSubsetOnly &&
+ mbOpenCLAutoSelect == r.mbOpenCLAutoSelect &&
+ maOpenCLDevice == r.maOpenCLDevice &&
+ mnOpenCLMinimumFormulaGroupSize == r.mnOpenCLMinimumFormulaGroupSize &&
+ *mpOpenCLSubsetOpCodes == *r.mpOpenCLSubsetOpCodes;
+}
+
+bool ScCalcConfig::operator!= (const ScCalcConfig& r) const
+{
+ return !operator==(r);
+}
+
+OUString ScOpCodeSetToSymbolicString(const ScCalcConfig::OpCodeSet& rOpCodes)
+{
+ OUStringBuffer result(256);
+ formula::FormulaCompiler aCompiler;
+ formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));
+
+ for (auto i = rOpCodes->begin(); i != rOpCodes->end(); ++i)
+ {
+ if (i != rOpCodes->begin())
+ result.append(';');
+ result.append(pOpCodeMap->getSymbol(*i));
+ }
+
+ return result.makeStringAndClear();
+}
+
+ScCalcConfig::OpCodeSet ScStringToOpCodeSet(std::u16string_view rOpCodes)
+{
+ ScCalcConfig::OpCodeSet result = std::make_shared<o3tl::sorted_vector< OpCode >>();
+ formula::FormulaCompiler aCompiler;
+ formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));
+
+ const formula::OpCodeHashMap& rHashMap(pOpCodeMap->getHashMap());
+
+ sal_Int32 fromIndex(0);
+ sal_Int32 semicolon;
+ OUString s(OUString::Concat(rOpCodes) + ";");
+
+ while ((semicolon = s.indexOf(';', fromIndex)) >= 0)
+ {
+ if (semicolon > fromIndex)
+ {
+ OUString element(s.copy(fromIndex, semicolon - fromIndex));
+ sal_Int32 n = element.toInt32();
+ if (n > 0 || (n == 0 && element == "0"))
+ result->insert(static_cast<OpCode>(n));
+ else
+ {
+ auto opcode(rHashMap.find(element));
+ if (opcode != rHashMap.end())
+ result->insert(opcode->second);
+ else
+ SAL_WARN("sc.opencl", "Unrecognized OpCode " << element << " in OpCode set string");
+ }
+ }
+ fromIndex = semicolon+1;
+ }
+ // HACK: Both unary and binary minus have the same string but different opcodes.
+ if( result->find( ocSub ) != result->end())
+ result->insert( ocNegSub );
+
+ return result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/callform.cxx b/sc/source/core/tool/callform.cxx
new file mode 100644
index 0000000000..2c441164cf
--- /dev/null
+++ b/sc/source/core/tool/callform.cxx
@@ -0,0 +1,412 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/module.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <memory>
+
+#include <callform.hxx>
+#include <global.hxx>
+#include <adiasync.hxx>
+
+extern "C" {
+
+typedef void (CALLTYPE* ExFuncPtr1)(void*);
+typedef void (CALLTYPE* ExFuncPtr2)(void*, void*);
+typedef void (CALLTYPE* ExFuncPtr3)(void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr4)(void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr5)(void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr6)(void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr7)(void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr8)(void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr9)(void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr10)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr11)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr12)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr13)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr14)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr15)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+typedef void (CALLTYPE* ExFuncPtr16)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
+
+typedef void (CALLTYPE* GetFuncCountPtr)(sal_uInt16& nCount);
+typedef void (CALLTYPE* GetFuncDataPtr)
+ (sal_uInt16& nNo, char* pFuncName, sal_uInt16& nParamCount, ParamType* peType, char* pInternalName);
+
+typedef void (CALLTYPE* SetLanguagePtr)( sal_uInt16& nLanguage );
+typedef void (CALLTYPE* GetParamDesc)
+ (sal_uInt16& nNo, sal_uInt16& nParam, char* pName, char* pDesc );
+
+typedef void (CALLTYPE* IsAsync) ( sal_uInt16& nNo,
+ ParamType* peType );
+typedef void (CALLTYPE* Advice) ( sal_uInt16& nNo,
+ AdvData& pfCallback );
+typedef void (CALLTYPE* Unadvice)( double& nHandle );
+
+}
+
+#ifndef DISABLE_DYNLOADING
+constexpr OUStringLiteral GETFUNCTIONCOUNT = u"GetFunctionCount";
+constexpr OUStringLiteral GETFUNCTIONDATA = u"GetFunctionData";
+constexpr OUStringLiteral SETLANGUAGE = u"SetLanguage";
+constexpr OUStringLiteral GETPARAMDESC = u"GetParameterDescription";
+constexpr OUStringLiteral ISASYNC = u"IsAsync";
+constexpr OUStringLiteral ADVICE = u"Advice";
+constexpr OUStringLiteral UNADVICE = u"Unadvice";
+#endif
+
+class ModuleData
+{
+friend class ModuleCollection;
+ OUString aName;
+ std::unique_ptr<osl::Module> pInstance;
+public:
+ ModuleData(const ModuleData&) = delete;
+ const ModuleData& operator=(const ModuleData&) = delete;
+
+ ModuleData(OUString aStr, std::unique_ptr<osl::Module> pInst) : aName(std::move(aStr)), pInstance(std::move(pInst)) {}
+
+ const OUString& GetName() const { return aName; }
+ osl::Module* GetInstance() const { return pInstance.get(); }
+};
+
+LegacyFuncData::LegacyFuncData(const ModuleData*pModule,
+ OUString aIName,
+ OUString aFName,
+ sal_uInt16 nNo,
+ sal_uInt16 nCount,
+ const ParamType* peType,
+ ParamType eType) :
+ pModuleData (pModule),
+ aInternalName (std::move(aIName)),
+ aFuncName (std::move(aFName)),
+ nNumber (nNo),
+ nParamCount (nCount),
+ eAsyncType (eType)
+{
+ for (sal_uInt16 i = 0; i < MAXFUNCPARAM; i++)
+ eParamType[i] = peType[i];
+}
+
+LegacyFuncData::LegacyFuncData(const LegacyFuncData& rData) :
+ pModuleData (rData.pModuleData),
+ aInternalName (rData.aInternalName),
+ aFuncName (rData.aFuncName),
+ nNumber (rData.nNumber),
+ nParamCount (rData.nParamCount),
+ eAsyncType (rData.eAsyncType)
+{
+ for (sal_uInt16 i = 0; i < MAXFUNCPARAM; i++)
+ eParamType[i] = rData.eParamType[i];
+}
+
+namespace {
+
+class ModuleCollection
+{
+ typedef std::map<OUString, std::unique_ptr<ModuleData>> MapType;
+ MapType m_Data;
+public:
+ ModuleCollection() {}
+
+ const ModuleData* findByName(const OUString& rName) const;
+ void insert(ModuleData* pNew);
+ void clear();
+};
+
+const ModuleData* ModuleCollection::findByName(const OUString& rName) const
+{
+ MapType::const_iterator it = m_Data.find(rName);
+ return it == m_Data.end() ? nullptr : it->second.get();
+}
+
+void ModuleCollection::insert(ModuleData* pNew)
+{
+ if (!pNew)
+ return;
+
+ OUString aName = pNew->GetName();
+ m_Data.insert(std::make_pair(aName, std::unique_ptr<ModuleData>(pNew)));
+}
+
+void ModuleCollection::clear()
+{
+ m_Data.clear();
+}
+
+ModuleCollection aModuleCollection;
+
+}
+
+bool InitExternalFunc(const OUString& rModuleName)
+{
+#ifdef DISABLE_DYNLOADING
+ (void) rModuleName;
+ return false;
+#else
+ // Module already loaded?
+ const ModuleData* pTemp = aModuleCollection.findByName(rModuleName);
+ if (pTemp)
+ return false;
+
+ OUString aNP = rModuleName;
+
+ std::unique_ptr<osl::Module> pLib(new osl::Module( aNP ));
+ if (!pLib->is())
+ return false;
+
+ oslGenericFunction fpGetCount = pLib->getFunctionSymbol(GETFUNCTIONCOUNT);
+ oslGenericFunction fpGetData = pLib->getFunctionSymbol(GETFUNCTIONDATA);
+ if ((fpGetCount == nullptr) || (fpGetData == nullptr))
+ return false;
+
+ oslGenericFunction fpIsAsync = pLib->getFunctionSymbol(ISASYNC);
+ oslGenericFunction fpAdvice = pLib->getFunctionSymbol(ADVICE);
+ oslGenericFunction fpSetLanguage = pLib->getFunctionSymbol(SETLANGUAGE);
+ if ( fpSetLanguage )
+ {
+ LanguageType eLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ sal_uInt16 nLanguage = static_cast<sal_uInt16>(eLanguage);
+ (*reinterpret_cast<SetLanguagePtr>(fpSetLanguage))( nLanguage );
+ }
+
+ // include module into the collection
+ ModuleData* pModuleData = new ModuleData(rModuleName, std::move(pLib));
+ aModuleCollection.insert(pModuleData);
+
+ // initialize interface
+ AdvData pfCallBack = &ScAddInAsyncCallBack;
+ LegacyFuncCollection* pLegacyFuncCol = ScGlobal::GetLegacyFuncCollection();
+ sal_uInt16 nCount;
+ (*reinterpret_cast<GetFuncCountPtr>(fpGetCount))(nCount);
+ for (sal_uInt16 i=0; i < nCount; i++)
+ {
+ char cFuncName[256];
+ char cInternalName[256];
+ sal_uInt16 nParamCount;
+ ParamType eParamType[MAXFUNCPARAM];
+ ParamType eAsyncType = ParamType::NONE;
+ // initialize all, in case the AddIn behaves bad
+ cFuncName[0] = 0;
+ cInternalName[0] = 0;
+ nParamCount = 0;
+ for (ParamType & rParamType : eParamType)
+ {
+ rParamType = ParamType::NONE;
+ }
+ (*reinterpret_cast<GetFuncDataPtr>(fpGetData))(i, cFuncName, nParamCount,
+ eParamType, cInternalName);
+ if( fpIsAsync )
+ {
+ (*reinterpret_cast<IsAsync>(fpIsAsync))(i, &eAsyncType);
+ if ( fpAdvice && eAsyncType != ParamType::NONE )
+ (*reinterpret_cast<Advice>(fpAdvice))( i, pfCallBack );
+ }
+ OUString aInternalName( cInternalName, strlen(cInternalName), osl_getThreadTextEncoding() );
+ OUString aFuncName( cFuncName, strlen(cFuncName), osl_getThreadTextEncoding() );
+ LegacyFuncData* pLegacyFuncData = new LegacyFuncData( pModuleData,
+ aInternalName,
+ aFuncName,
+ i,
+ nParamCount,
+ eParamType,
+ eAsyncType );
+ pLegacyFuncCol->insert(pLegacyFuncData);
+ }
+ return true;
+#endif
+}
+
+void ExitExternalFunc()
+{
+ aModuleCollection.clear();
+}
+
+void LegacyFuncData::Call(void** ppParam) const
+{
+#ifdef DISABLE_DYNLOADING
+ (void) ppParam;
+#else
+ osl::Module* pLib = pModuleData->GetInstance();
+ oslGenericFunction fProc = pLib->getFunctionSymbol(aFuncName);
+ if (fProc == nullptr)
+ return;
+
+ switch (nParamCount)
+ {
+ case 1 :
+ (*reinterpret_cast<ExFuncPtr1>(fProc))(ppParam[0]);
+ break;
+ case 2 :
+ (*reinterpret_cast<ExFuncPtr2>(fProc))(ppParam[0], ppParam[1]);
+ break;
+ case 3 :
+ (*reinterpret_cast<ExFuncPtr3>(fProc))(ppParam[0], ppParam[1], ppParam[2]);
+ break;
+ case 4 :
+ (*reinterpret_cast<ExFuncPtr4>(fProc))(ppParam[0], ppParam[1], ppParam[2], ppParam[3]);
+ break;
+ case 5 :
+ (*reinterpret_cast<ExFuncPtr5>(fProc))(ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4]);
+ break;
+ case 6 :
+ (*reinterpret_cast<ExFuncPtr6>(fProc))(ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5]);
+ break;
+ case 7 :
+ (*reinterpret_cast<ExFuncPtr7>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6]);
+ break;
+ case 8 :
+ (*reinterpret_cast<ExFuncPtr8>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7]);
+ break;
+ case 9 :
+ (*reinterpret_cast<ExFuncPtr9>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8]);
+ break;
+ case 10 :
+ (*reinterpret_cast<ExFuncPtr10>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9]);
+ break;
+ case 11 :
+ (*reinterpret_cast<ExFuncPtr11>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10]);
+ break;
+ case 12:
+ (*reinterpret_cast<ExFuncPtr12>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11]);
+ break;
+ case 13:
+ (*reinterpret_cast<ExFuncPtr13>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11],
+ ppParam[12]);
+ break;
+ case 14 :
+ (*reinterpret_cast<ExFuncPtr14>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11],
+ ppParam[12], ppParam[13]);
+ break;
+ case 15 :
+ (*reinterpret_cast<ExFuncPtr15>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11],
+ ppParam[12], ppParam[13], ppParam[14]);
+ break;
+ case 16 :
+ (*reinterpret_cast<ExFuncPtr16>(fProc))( ppParam[0], ppParam[1], ppParam[2], ppParam[3], ppParam[4], ppParam[5],
+ ppParam[6], ppParam[7], ppParam[8], ppParam[9], ppParam[10], ppParam[11],
+ ppParam[12], ppParam[13], ppParam[14], ppParam[15]);
+ break;
+ default : break;
+ }
+#endif
+}
+
+void LegacyFuncData::Unadvice( double nHandle )
+{
+#ifdef DISABLE_DYNLOADING
+ (void) nHandle;
+#else
+ osl::Module* pLib = pModuleData->GetInstance();
+ oslGenericFunction fProc = pLib->getFunctionSymbol(UNADVICE);
+ if (fProc != nullptr)
+ {
+ reinterpret_cast< ::Unadvice>(fProc)(nHandle);
+ }
+#endif
+}
+
+const OUString& LegacyFuncData::GetModuleName() const
+{
+ return pModuleData->GetName();
+}
+
+void LegacyFuncData::getParamDesc( OUString& aName, OUString& aDesc, sal_uInt16 nParam ) const
+{
+#ifdef DISABLE_DYNLOADING
+ (void) aName;
+ (void) aDesc;
+ (void) nParam;
+#else
+ bool bRet = false;
+ if ( nParam <= nParamCount )
+ {
+ osl::Module* pLib = pModuleData->GetInstance();
+ oslGenericFunction fProc = pLib->getFunctionSymbol(GETPARAMDESC);
+ if ( fProc != nullptr )
+ {
+ char pcName[256];
+ char pcDesc[256];
+ *pcName = *pcDesc = 0;
+ sal_uInt16 nFuncNo = nNumber; // don't let it mess up via reference...
+ reinterpret_cast< ::GetParamDesc>(fProc)( nFuncNo, nParam, pcName, pcDesc );
+ aName = OUString( pcName, 256, osl_getThreadTextEncoding() );
+ aDesc = OUString( pcDesc, 256, osl_getThreadTextEncoding() );
+ bRet = true;
+ }
+ }
+ if ( !bRet )
+ {
+ aName.clear();
+ aDesc.clear();
+ }
+#endif
+}
+
+LegacyFuncCollection::LegacyFuncCollection() {}
+LegacyFuncCollection::LegacyFuncCollection(const LegacyFuncCollection& r)
+{
+ for (auto const& it : r.m_Data)
+ {
+ m_Data.insert(std::make_pair(it.first, std::make_unique<LegacyFuncData>(*it.second)));
+ }
+}
+
+const LegacyFuncData* LegacyFuncCollection::findByName(const OUString& rName) const
+{
+ MapType::const_iterator it = m_Data.find(rName);
+ return it == m_Data.end() ? nullptr : it->second.get();
+}
+
+LegacyFuncData* LegacyFuncCollection::findByName(const OUString& rName)
+{
+ MapType::iterator it = m_Data.find(rName);
+ return it == m_Data.end() ? nullptr : it->second.get();
+}
+
+void LegacyFuncCollection::insert(LegacyFuncData* pNew)
+{
+ OUString aName = pNew->GetInternalName();
+ m_Data.insert(std::make_pair(aName, std::unique_ptr<LegacyFuncData>(pNew)));
+}
+
+LegacyFuncCollection::const_iterator LegacyFuncCollection::begin() const
+{
+ return m_Data.begin();
+}
+
+LegacyFuncCollection::const_iterator LegacyFuncCollection::end() const
+{
+ return m_Data.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/cellform.cxx b/sc/source/core/tool/cellform.cxx
new file mode 100644
index 0000000000..9d1a731929
--- /dev/null
+++ b/sc/source/core/tool/cellform.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 <cellform.hxx>
+
+#include <svl/numformat.hxx>
+#include <svl/sharedstring.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <formula/errorcodes.hxx>
+#include <editutil.hxx>
+
+OUString ScCellFormat::GetString( const ScRefCellValue& rCell, sal_uInt32 nFormat,
+ const Color** ppColor, SvNumberFormatter& rFormatter, const ScDocument& rDoc,
+ bool bNullVals, bool bFormula, bool bUseStarFormat )
+{
+ *ppColor = nullptr;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ OUString str;
+ rFormatter.GetOutputString(rCell.getSharedString()->getString(), nFormat, str, ppColor, bUseStarFormat);
+ return str;
+ }
+ case CELLTYPE_EDIT:
+ {
+ OUString str;
+ rFormatter.GetOutputString(rCell.getString(&rDoc), nFormat, str, ppColor );
+ return str;
+ }
+ case CELLTYPE_VALUE:
+ {
+ const double nValue = rCell.getDouble();
+ if (!bNullVals && nValue == 0.0)
+ return OUString();
+ else
+ {
+ OUString str;
+ rFormatter.GetOutputString( nValue, nFormat, str, ppColor, bUseStarFormat );
+ return str;
+ }
+ }
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if ( bFormula )
+ {
+ return pFCell->GetFormula();
+ }
+ else
+ {
+ // A macro started from the interpreter, which has
+ // access to Formula Cells, becomes a CellText, even if
+ // that triggers further interpretation, except if those
+ // cells are already being interpreted.
+ // IdleCalc generally doesn't trigger further interpretation,
+ // as not to get Err522 (circular).
+ if ( pFCell->GetDocument().IsInInterpreter() &&
+ (!pFCell->GetDocument().GetMacroInterpretLevel()
+ || pFCell->IsRunning()) )
+ {
+ return "...";
+ }
+ else
+ {
+ const FormulaError nErrCode = pFCell->GetErrCode();
+
+ if (nErrCode != FormulaError::NONE)
+ return ScGlobal::GetErrorString(nErrCode);
+ else if ( pFCell->IsEmptyDisplayedAsString() )
+ return OUString();
+ else if ( pFCell->IsValue() )
+ {
+ double fValue = pFCell->GetValue();
+ if ( !bNullVals && fValue == 0.0 )
+ return OUString();
+ else
+ {
+ OUString str;
+ rFormatter.GetOutputString( fValue, nFormat, str, ppColor, bUseStarFormat );
+ return str;
+ }
+ }
+ else
+ {
+ OUString str;
+ rFormatter.GetOutputString( pFCell->GetString().getString(),
+ nFormat, str, ppColor, bUseStarFormat );
+ return str;
+ }
+ }
+ }
+ }
+ default:
+ return OUString();
+ }
+}
+
+OUString ScCellFormat::GetString(
+ ScDocument& rDoc, const ScAddress& rPos, sal_uInt32 nFormat, const Color** ppColor,
+ SvNumberFormatter& rFormatter, bool bNullVals, bool bFormula )
+{
+ *ppColor = nullptr;
+
+ ScRefCellValue aCell(rDoc, rPos);
+ return GetString(aCell, nFormat, ppColor, rFormatter, rDoc, bNullVals, bFormula);
+}
+
+OUString ScCellFormat::GetInputString(
+ const ScRefCellValue& rCell, sal_uInt32 nFormat, SvNumberFormatter& rFormatter, const ScDocument& rDoc,
+ const svl::SharedString** pShared, bool bFiltering, bool bForceSystemLocale )
+{
+ if(pShared != nullptr)
+ *pShared = nullptr;
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ return rCell.getString(&rDoc);
+ case CELLTYPE_VALUE:
+ {
+ OUString str;
+ rFormatter.GetInputLineString(rCell.getDouble(), nFormat, str, bFiltering, bForceSystemLocale);
+ return str;
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ std::optional<OUString> str;
+ ScFormulaCell* pFC = rCell.getFormula();
+ if (pFC->IsEmptyDisplayedAsString())
+ ; // empty
+ else if (pFC->IsValue())
+ {
+ str.emplace();
+ rFormatter.GetInputLineString(pFC->GetValue(), nFormat, *str, bFiltering, bForceSystemLocale);
+ }
+ else
+ {
+ const svl::SharedString& shared = pFC->GetString();
+ // Allow callers to optimize by avoiding converting later back to OUString.
+ // To avoid refcounting that won't be needed, do not even return the OUString.
+ if( pShared != nullptr )
+ *pShared = &shared;
+ else
+ str = shared.getString();
+ }
+
+ const FormulaError nErrCode = pFC->GetErrCode();
+ if (nErrCode != FormulaError::NONE)
+ {
+ str.reset();
+ if( pShared != nullptr )
+ *pShared = nullptr;
+ }
+
+ return str ? std::move(*str) : svl::SharedString::EMPTY_STRING;
+ }
+ case CELLTYPE_NONE:
+ if( pShared != nullptr )
+ *pShared = &svl::SharedString::getEmptyString();
+ return svl::SharedString::EMPTY_STRING;
+ default:
+ return svl::SharedString::EMPTY_STRING;
+ }
+}
+
+OUString ScCellFormat::GetOutputString( ScDocument& rDoc, const ScAddress& rPos, const ScRefCellValue& rCell )
+{
+ if (rCell.isEmpty())
+ return OUString();
+
+ if (rCell.getType() == CELLTYPE_EDIT)
+ {
+ // GetString converts line breaks into spaces in EditCell,
+ // but here we need the line breaks
+ const EditTextObject* pData = rCell.getEditText();
+ if (pData)
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(*pData);
+ return rEngine.GetText();
+ }
+ // also do not format EditCells as numbers
+ // (fitting to output)
+ return OUString();
+ }
+ else
+ {
+ // like in GetString for document (column)
+ const Color* pColor;
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat(rPos);
+ return GetString(rCell, nNumFmt, &pColor, *rDoc.GetFormatTable(), rDoc);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/cellkeytranslator.cxx b/sc/source/core/tool/cellkeytranslator.cxx
new file mode 100644
index 0000000000..8e2218f931
--- /dev/null
+++ b/sc/source/core/tool/cellkeytranslator.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <global.hxx>
+#include <cellkeytranslator.hxx>
+#include <comphelper/processfactory.hxx>
+#include <i18nlangtag/lang.h>
+#include <i18nutil/transliteration.hxx>
+#include <rtl/ustring.hxx>
+#include <unotools/syslocale.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using ::com::sun::star::uno::Sequence;
+
+using namespace ::com::sun::star;
+
+namespace {
+
+enum LocaleMatch
+{
+ LOCALE_MATCH_NONE = 0,
+ LOCALE_MATCH_LANG,
+ LOCALE_MATCH_LANG_SCRIPT,
+ LOCALE_MATCH_LANG_SCRIPT_COUNTRY,
+ LOCALE_MATCH_ALL
+};
+
+}
+
+static LocaleMatch lclLocaleCompare(const lang::Locale& rLocale1, const LanguageTag& rLanguageTag2)
+{
+ LocaleMatch eMatchLevel = LOCALE_MATCH_NONE;
+ LanguageTag aLanguageTag1( rLocale1);
+
+ if ( aLanguageTag1.getLanguage() == rLanguageTag2.getLanguage() )
+ eMatchLevel = LOCALE_MATCH_LANG;
+ else
+ return eMatchLevel;
+
+ if ( aLanguageTag1.getScript() == rLanguageTag2.getScript() )
+ eMatchLevel = LOCALE_MATCH_LANG_SCRIPT;
+ else
+ return eMatchLevel;
+
+ if ( aLanguageTag1.getCountry() == rLanguageTag2.getCountry() )
+ eMatchLevel = LOCALE_MATCH_LANG_SCRIPT_COUNTRY;
+ else
+ return eMatchLevel;
+
+ if (aLanguageTag1 == rLanguageTag2)
+ return LOCALE_MATCH_ALL;
+
+ return eMatchLevel;
+}
+
+ScCellKeyword::ScCellKeyword(const char* pName, OpCode eOpCode, const lang::Locale& rLocale) :
+ mpName(pName),
+ meOpCode(eOpCode),
+ mrLocale(rLocale)
+{
+}
+
+::std::unique_ptr<ScCellKeywordTranslator> ScCellKeywordTranslator::spInstance;
+
+static void lclMatchKeyword(OUString& rName, const ScCellKeywordHashMap& aMap,
+ OpCode eOpCode, const lang::Locale* pLocale)
+{
+ ScCellKeywordHashMap::const_iterator itrEnd = aMap.end();
+ ScCellKeywordHashMap::const_iterator itr = aMap.find(rName);
+
+ if ( itr == itrEnd || itr->second.empty() )
+ // No candidate strings exist. Bail out.
+ return;
+
+ if ( eOpCode == ocNone && !pLocale )
+ {
+ // Since no locale nor opcode matching is needed, simply return
+ // the first item on the list.
+ rName = OUString::createFromAscii( itr->second.front().mpName );
+ return;
+ }
+
+ LanguageTag aLanguageTag( pLocale ? *pLocale : lang::Locale("","",""));
+ const char* aBestMatchName = itr->second.front().mpName;
+ LocaleMatch eLocaleMatchLevel = LOCALE_MATCH_NONE;
+ bool bOpCodeMatched = false;
+
+ for (auto const& elem : itr->second)
+ {
+ if ( eOpCode != ocNone && pLocale )
+ {
+ if (elem.meOpCode == eOpCode)
+ {
+ LocaleMatch eLevel = lclLocaleCompare(elem.mrLocale, aLanguageTag);
+ if ( eLevel == LOCALE_MATCH_ALL )
+ {
+ // Name with matching opcode and locale found.
+ rName = OUString::createFromAscii( elem.mpName );
+ return;
+ }
+ else if ( eLevel > eLocaleMatchLevel )
+ {
+ // Name with a better matching locale.
+ eLocaleMatchLevel = eLevel;
+ aBestMatchName = elem.mpName;
+ }
+ else if ( !bOpCodeMatched )
+ // At least the opcode matches.
+ aBestMatchName = elem.mpName;
+
+ bOpCodeMatched = true;
+ }
+ }
+ else if ( eOpCode != ocNone && !pLocale )
+ {
+ if ( elem.meOpCode == eOpCode )
+ {
+ // Name with a matching opcode preferred.
+ rName = OUString::createFromAscii( elem.mpName );
+ return;
+ }
+ }
+ else if ( pLocale )
+ {
+ LocaleMatch eLevel = lclLocaleCompare(elem.mrLocale, aLanguageTag);
+ if ( eLevel == LOCALE_MATCH_ALL )
+ {
+ // Name with matching locale preferred.
+ rName = OUString::createFromAscii( elem.mpName );
+ return;
+ }
+ else if ( eLevel > eLocaleMatchLevel )
+ {
+ // Name with a better matching locale.
+ eLocaleMatchLevel = eLevel;
+ aBestMatchName = elem.mpName;
+ }
+ }
+ }
+
+ // No preferred strings found. Return the best matching name.
+ rName = OUString::createFromAscii(aBestMatchName);
+}
+
+void ScCellKeywordTranslator::transKeyword(OUString& rName, const lang::Locale* pLocale, OpCode eOpCode)
+{
+ if (!spInstance)
+ spInstance.reset( new ScCellKeywordTranslator );
+
+ LanguageType nLang = pLocale ?
+ LanguageTag(*pLocale).makeFallback().getLanguageType() : ScGlobal::oSysLocale->GetLanguageTag().getLanguageType();
+ Sequence<sal_Int32> aOffsets;
+ rName = spInstance->maTransWrapper.transliterate(rName, nLang, 0, rName.getLength(), &aOffsets);
+ lclMatchKeyword(rName, spInstance->maStringNameMap, eOpCode, pLocale);
+}
+
+struct TransItem
+{
+ const sal_Unicode* from;
+ const char* to;
+ OpCode func;
+};
+
+ScCellKeywordTranslator::ScCellKeywordTranslator() :
+ maTransWrapper( ::comphelper::getProcessComponentContext(),
+ TransliterationFlags::LOWERCASE_UPPERCASE )
+{
+ // The file below has been autogenerated by sc/workben/celltrans/parse.py.
+ // To add new locale keywords, edit sc/workben/celltrans/keywords_utf16.txt
+ // and re-run the parse.py script.
+ //
+ // All keywords must be uppercase, and the mapping must be from the
+ // localized keyword to the English keyword.
+ //
+ // Make sure that the original keyword file (keywords_utf16.txt) is
+ // encoded in UCS-2/UTF-16!
+
+ #include "cellkeywords.inl"
+}
+
+ScCellKeywordTranslator::~ScCellKeywordTranslator()
+{
+}
+
+void ScCellKeywordTranslator::addToMap(const OUString& rKey, const char* pName, const lang::Locale& rLocale, OpCode eOpCode)
+{
+ ScCellKeyword aKeyItem( pName, eOpCode, rLocale );
+
+ ScCellKeywordHashMap::iterator itrEnd = maStringNameMap.end();
+ ScCellKeywordHashMap::iterator itr = maStringNameMap.find(rKey);
+
+ if ( itr == itrEnd )
+ {
+ // New keyword.
+ std::vector<ScCellKeyword> aVector { aKeyItem };
+ maStringNameMap.emplace(rKey, aVector);
+ }
+ else
+ itr->second.push_back(aKeyItem);
+}
+
+void ScCellKeywordTranslator::addToMap(const TransItem* pItems, const lang::Locale& rLocale)
+{
+ for (sal_uInt16 i = 0; pItems[i].from != nullptr; ++i)
+ addToMap(OUString(pItems[i].from), pItems[i].to, rLocale, pItems[i].func);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/cellkeywords.inl b/sc/source/core/tool/cellkeywords.inl
new file mode 100644
index 0000000000..b56e3eeadf
--- /dev/null
+++ b/sc/source/core/tool/cellkeywords.inl
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// This file has been automatically generated. Do not hand-edit this!
+
+
+// French language locale (automatically generated)
+
+static const lang::Locale aFr("fr", "", "");
+
+// pre instantiations of localized function names
+static const sal_Unicode cell_address_fr[] = {
+ 0x0041, 0x0044, 0x0052, 0x0045, 0x0053, 0x0053, 0x0045, 0x0000};
+static const sal_Unicode cell_col_fr[] = {
+ 0x0043, 0x004F, 0x004C, 0x004F, 0x004E, 0x004E, 0x0045, 0x0000};
+static const sal_Unicode cell_contents_fr[] = {
+ 0x0043, 0x004F, 0x004E, 0x0054, 0x0045, 0x004E, 0x0055, 0x0000};
+static const sal_Unicode cell_color_fr[] = {
+ 0x0043, 0x004F, 0x0055, 0x004C, 0x0045, 0x0055, 0x0052, 0x0000};
+static const sal_Unicode cell_width_fr[] = {
+ 0x004C, 0x0041, 0x0052, 0x0047, 0x0045, 0x0055, 0x0052, 0x0000};
+static const sal_Unicode cell_row_fr[] = {
+ 0x004C, 0x0049, 0x0047, 0x004E, 0x0045, 0x0000};
+static const sal_Unicode cell_filename_fr[] = {
+ 0x004E, 0x004F, 0x004D, 0x0046, 0x0049, 0x0043, 0x0048, 0x0049, 0x0045, 0x0052, 0x0000};
+static const sal_Unicode cell_prefix_fr[] = {
+ 0x0050, 0x0052, 0x0045, 0x0046, 0x0049, 0x0058, 0x0045, 0x0000};
+static const sal_Unicode cell_protect_fr[] = {
+ 0x0050, 0x0052, 0x004F, 0x0054, 0x0045, 0x0047, 0x0045, 0x0000};
+static const sal_Unicode info_numfile_fr[] = {
+ 0x004E, 0x0042, 0x0046, 0x0049, 0x0043, 0x0048, 0x0000};
+static const sal_Unicode info_recalc_fr[] = {
+ 0x0052, 0x0045, 0x0043, 0x0041, 0x004C, 0x0043, 0x0055, 0x004C, 0x0000};
+static const sal_Unicode info_system_fr[] = {
+ 0x0053, 0x0059, 0x0053, 0x0054, 0x0045, 0x0058, 0x0050, 0x004C, 0x0000};
+static const sal_Unicode info_release_fr[] = {
+ 0x0056, 0x0045, 0x0052, 0x0053, 0x0049, 0x004F, 0x004E, 0x0000};
+static const sal_Unicode info_osversion_fr[] = {
+ 0x0056, 0x0045, 0x0052, 0x0053, 0x0049, 0x004F, 0x004E, 0x0053, 0x0045, 0x0000};
+
+static const TransItem pFr[] = {
+ {cell_address_fr, "ADDRESS", ocCell},
+ {cell_col_fr, "COL", ocCell},
+ {cell_contents_fr, "CONTENTS", ocCell},
+ {cell_color_fr, "COLOR", ocCell},
+ {cell_width_fr, "WIDTH", ocCell},
+ {cell_row_fr, "ROW", ocCell},
+ {cell_filename_fr, "FILENAME", ocCell},
+ {cell_prefix_fr, "PREFIX", ocCell},
+ {cell_protect_fr, "PROTECT", ocCell},
+ {info_numfile_fr, "NUMFILE", ocInfo},
+ {info_recalc_fr, "RECALC", ocInfo},
+ {info_system_fr, "SYSTEM", ocInfo},
+ {info_release_fr, "RELEASE", ocInfo},
+ {info_osversion_fr, "OSVERSION", ocInfo},
+ {nullptr, nullptr, ocNone}
+};
+
+addToMap(pFr, aFr);
+
+
+// Hungarian language locale (automatically generated)
+
+static const lang::Locale aHu("hu", "", "");
+
+// pre instantiations of localized function names
+static const sal_Unicode cell_address_hu[] = {
+ 0x0043, 0x00CD, 0x004D, 0x0000};
+static const sal_Unicode cell_col_hu[] = {
+ 0x004F, 0x0053, 0x005A, 0x004C, 0x004F, 0x0050, 0x0000};
+static const sal_Unicode cell_color_hu[] = {
+ 0x0053, 0x005A, 0x00CD, 0x004E, 0x0000};
+static const sal_Unicode cell_contents_hu[] = {
+ 0x0054, 0x0041, 0x0052, 0x0054, 0x0041, 0x004C, 0x004F, 0x004D, 0x0000};
+static const sal_Unicode cell_width_hu[] = {
+ 0x0053, 0x005A, 0x00C9, 0x004C, 0x0045, 0x0053, 0x0000};
+static const sal_Unicode cell_row_hu[] = {
+ 0x0053, 0x004F, 0x0052, 0x0000};
+static const sal_Unicode cell_filename_hu[] = {
+ 0x0046, 0x0049, 0x004C, 0x0045, 0x004E, 0x00C9, 0x0056, 0x0000};
+static const sal_Unicode cell_prefix_hu[] = {
+ 0x0050, 0x0052, 0x0045, 0x0046, 0x0049, 0x0058, 0x0000};
+static const sal_Unicode cell_protect_hu[] = {
+ 0x0056, 0x00C9, 0x0044, 0x0045, 0x0054, 0x0054, 0x0000};
+static const sal_Unicode cell_coord_hu[] = {
+ 0x004B, 0x004F, 0x004F, 0x0052, 0x0044, 0x0000};
+static const sal_Unicode cell_format_hu[] = {
+ 0x0046, 0x004F, 0x0052, 0x004D, 0x0041, 0x0000};
+static const sal_Unicode cell_parentheses_hu[] = {
+ 0x005A, 0x00C1, 0x0052, 0x00D3, 0x004A, 0x0045, 0x004C, 0x0045, 0x004B, 0x0000};
+static const sal_Unicode cell_sheet_hu[] = {
+ 0x004C, 0x0041, 0x0050, 0x0000};
+static const sal_Unicode cell_type_hu[] = {
+ 0x0054, 0x00CD, 0x0050, 0x0055, 0x0053, 0x0000};
+static const sal_Unicode info_numfile_hu[] = {
+ 0x0046, 0x0049, 0x004C, 0x0045, 0x0053, 0x005A, 0x00C1, 0x004D, 0x0000};
+static const sal_Unicode info_recalc_hu[] = {
+ 0x0053, 0x005A, 0x00C1, 0x004D, 0x004F, 0x004C, 0x00C1, 0x0053, 0x0000};
+static const sal_Unicode info_system_hu[] = {
+ 0x0052, 0x0045, 0x004E, 0x0044, 0x0053, 0x005A, 0x0045, 0x0052, 0x0000};
+static const sal_Unicode info_release_hu[] = {
+ 0x0056, 0x0045, 0x0052, 0x005A, 0x0049, 0x00D3, 0x0000};
+static const sal_Unicode info_osversion_hu[] = {
+ 0x004F, 0x0050, 0x0052, 0x0045, 0x004E, 0x0044, 0x0053, 0x005A, 0x0045, 0x0052, 0x0000};
+
+static const TransItem pHu[] = {
+ {cell_address_hu, "ADDRESS", ocCell},
+ {cell_col_hu, "COL", ocCell},
+ {cell_color_hu, "COLOR", ocCell},
+ {cell_contents_hu, "CONTENTS", ocCell},
+ {cell_width_hu, "WIDTH", ocCell},
+ {cell_row_hu, "ROW", ocCell},
+ {cell_filename_hu, "FILENAME", ocCell},
+ {cell_prefix_hu, "PREFIX", ocCell},
+ {cell_protect_hu, "PROTECT", ocCell},
+ {cell_coord_hu, "COORD", ocCell},
+ {cell_format_hu, "FORMAT", ocCell},
+ {cell_parentheses_hu, "PARENTHESES", ocCell},
+ {cell_sheet_hu, "SHEET", ocCell},
+ {cell_type_hu, "TYPE", ocCell},
+ {info_numfile_hu, "NUMFILE", ocInfo},
+ {info_recalc_hu, "RECALC", ocInfo},
+ {info_system_hu, "SYSTEM", ocInfo},
+ {info_release_hu, "RELEASE", ocInfo},
+ {info_osversion_hu, "OSVERSION", ocInfo},
+ {nullptr, nullptr, ocNone}
+};
+
+addToMap(pHu, aHu);
+
+
+// German language locale (automatically generated)
+
+static const lang::Locale aDe("de", "", "");
+
+// pre instantiations of localized function names
+static const sal_Unicode cell_row_de[] = {
+ 0x005A, 0x0045, 0x0049, 0x004C, 0x0045, 0x0000};
+static const sal_Unicode cell_col_de[] = {
+ 0x0053, 0x0050, 0x0041, 0x004C, 0x0054, 0x0045, 0x0000};
+static const sal_Unicode cell_width_de[] = {
+ 0x0042, 0x0052, 0x0045, 0x0049, 0x0054, 0x0045, 0x0000};
+static const sal_Unicode cell_address_de[] = {
+ 0x0041, 0x0044, 0x0052, 0x0045, 0x0053, 0x0053, 0x0045, 0x0000};
+static const sal_Unicode cell_filename_de[] = {
+ 0x0044, 0x0041, 0x0054, 0x0045, 0x0049, 0x004E, 0x0041, 0x004D, 0x0045, 0x0000};
+static const sal_Unicode cell_color_de[] = {
+ 0x0046, 0x0041, 0x0052, 0x0042, 0x0045, 0x0000};
+static const sal_Unicode cell_format_de[] = {
+ 0x0046, 0x004F, 0x0052, 0x004D, 0x0041, 0x0054, 0x0000};
+static const sal_Unicode cell_contents_de[] = {
+ 0x0049, 0x004E, 0x0048, 0x0041, 0x004C, 0x0054, 0x0000};
+static const sal_Unicode cell_parentheses_de[] = {
+ 0x004B, 0x004C, 0x0041, 0x004D, 0x004D, 0x0045, 0x0052, 0x004E, 0x0000};
+static const sal_Unicode cell_protect_de[] = {
+ 0x0053, 0x0043, 0x0048, 0x0055, 0x0054, 0x005A, 0x0000};
+static const sal_Unicode cell_type_de[] = {
+ 0x0054, 0x0059, 0x0050, 0x0000};
+static const sal_Unicode cell_prefix_de[] = {
+ 0x0050, 0x0052, 0x00C4, 0x0046, 0x0049, 0x0058, 0x0000};
+static const sal_Unicode cell_sheet_de[] = {
+ 0x0042, 0x004C, 0x0041, 0x0054, 0x0054, 0x0000};
+static const sal_Unicode cell_coord_de[] = {
+ 0x004B, 0x004F, 0x004F, 0x0052, 0x0044, 0x0000};
+
+static const TransItem pDe[] = {
+ {cell_row_de, "ROW", ocCell},
+ {cell_col_de, "COL", ocCell},
+ {cell_width_de, "WIDTH", ocCell},
+ {cell_address_de, "ADDRESS", ocCell},
+ {cell_filename_de, "FILENAME", ocCell},
+ {cell_color_de, "COLOR", ocCell},
+ {cell_format_de, "FORMAT", ocCell},
+ {cell_contents_de, "CONTENTS", ocCell},
+ {cell_parentheses_de, "PARENTHESES", ocCell},
+ {cell_protect_de, "PROTECT", ocCell},
+ {cell_type_de, "TYPE", ocCell},
+ {cell_prefix_de, "PREFIX", ocCell},
+ {cell_sheet_de, "SHEET", ocCell},
+ {cell_coord_de, "COORD", ocCell},
+ {nullptr, nullptr, ocNone}
+};
+
+addToMap(pDe, aDe);
diff --git a/sc/source/core/tool/chartarr.cxx b/sc/source/core/tool/chartarr.cxx
new file mode 100644
index 0000000000..58f1b12215
--- /dev/null
+++ b/sc/source/core/tool/chartarr.cxx
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <float.h>
+
+#include <chartarr.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <formulacell.hxx>
+#include <docoptio.hxx>
+
+#include <formula/errorcodes.hxx>
+
+#include <memory>
+#include <vector>
+
+using ::std::vector;
+
+ScMemChart::ScMemChart(SCCOL nCols, SCROW nRows)
+{
+ nRowCnt = nRows;
+ nColCnt = nCols;
+ pData.reset( new double[nColCnt * nRowCnt] );
+
+ memset( pData.get(), 0.0, nColCnt * nRowCnt );
+
+ pColText.reset( new OUString[nColCnt] );
+ pRowText.reset( new OUString[nRowCnt] );
+}
+
+ScMemChart::~ScMemChart()
+{
+}
+
+ScChartArray::ScChartArray(
+ ScDocument& rDoc, const ScRangeListRef& rRangeList ) :
+ rDocument( rDoc ),
+ aPositioner(rDoc, rRangeList) {}
+
+std::unique_ptr<ScMemChart> ScChartArray::CreateMemChart()
+{
+ ScRangeListRef aRangeListRef(GetRangeList());
+ size_t nCount = aRangeListRef->size();
+ if ( nCount > 1 )
+ return CreateMemChartMulti();
+ else if ( nCount == 1 )
+ {
+ const ScRange & rR = aRangeListRef->front();
+ if ( rR.aStart.Tab() != rR.aEnd.Tab() )
+ return CreateMemChartMulti();
+ else
+ return CreateMemChartSingle();
+ }
+ else
+ return CreateMemChartMulti(); // Can handle 0 range better than Single
+}
+
+namespace {
+
+double getCellValue( ScDocument& rDoc, const ScAddress& rPos, double fDefault, bool bCalcAsShown )
+{
+ double fRet = fDefault;
+
+ ScRefCellValue aCell(rDoc, rPos);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ fRet = aCell.getValue();
+ if (bCalcAsShown && fRet != 0.0)
+ {
+ sal_uInt32 nFormat = rDoc.GetNumberFormat(rPos);
+ fRet = rDoc.RoundValueAsShown(fRet, nFormat);
+ }
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ if (pFCell && pFCell->GetErrCode() == FormulaError::NONE && pFCell->IsValue())
+ fRet = pFCell->GetValue();
+ }
+ break;
+ default:
+ ;
+ }
+ return fRet;
+}
+
+}
+
+std::unique_ptr<ScMemChart> ScChartArray::CreateMemChartSingle()
+{
+ SCSIZE nCol;
+ SCSIZE nRow;
+
+ // real size (without hidden rows/columns)
+
+ SCCOL nColAdd = HasRowHeaders() ? 1 : 0;
+ SCROW nRowAdd = HasColHeaders() ? 1 : 0;
+
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ ScRangeListRef aRangeListRef(GetRangeList());
+ aRangeListRef->front().GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+
+ SCCOL nStrCol = nCol1; // remember for labeling
+ SCROW nStrRow = nRow1;
+ // Skip hidden columns.
+ // TODO: make use of last column value once implemented.
+ SCCOL nLastCol = -1;
+ while (rDocument.ColHidden(nCol1, nTab1, nullptr, &nLastCol))
+ ++nCol1;
+
+ // Skip hidden rows.
+ SCROW nLastRow = -1;
+ if (rDocument.RowHidden(nRow1, nTab1, nullptr, &nLastRow))
+ nRow1 = nLastRow + 1;
+
+ // if everything is hidden then the label remains at the beginning
+ if ( nCol1 <= nCol2 )
+ {
+ nStrCol = nCol1;
+ nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nColAdd );
+ }
+ if ( nRow1 <= nRow2 )
+ {
+ nStrRow = nRow1;
+ nRow1 = sal::static_int_cast<SCROW>( nRow1 + nRowAdd );
+ }
+
+ SCSIZE nTotalCols = ( nCol1 <= nCol2 ? nCol2 - nCol1 + 1 : 0 );
+ vector<SCCOL> aCols;
+ aCols.reserve(nTotalCols);
+ for (SCSIZE i=0; i<nTotalCols; i++)
+ {
+ SCCOL nThisCol = sal::static_int_cast<SCCOL>(nCol1+i);
+ if (!rDocument.ColHidden(nThisCol, nTab1, nullptr, &nLastCol))
+ aCols.push_back(nThisCol);
+ }
+ SCSIZE nColCount = aCols.size();
+
+ SCSIZE nTotalRows = ( nRow1 <= nRow2 ? nRow2 - nRow1 + 1 : 0 );
+ vector<SCROW> aRows;
+ aRows.reserve(nTotalRows);
+ if (nRow1 <= nRow2)
+ {
+ // Get all visible rows between nRow1 and nRow2.
+ SCROW nThisRow = nRow1;
+ while (nThisRow <= nRow2)
+ {
+ if (rDocument.RowHidden(nThisRow, nTab1, nullptr, &nLastRow))
+ nThisRow = nLastRow;
+ else
+ aRows.push_back(nThisRow);
+ ++nThisRow;
+ }
+ }
+ SCSIZE nRowCount = aRows.size();
+
+ // May happen at least with more than 32k rows.
+ if (nColCount > SHRT_MAX || nRowCount > SHRT_MAX)
+ {
+ nColCount = 0;
+ nRowCount = 0;
+ }
+
+ bool bValidData = true;
+ if ( !nColCount )
+ {
+ bValidData = false;
+ nColCount = 1;
+ aCols.push_back(nStrCol);
+ }
+ if ( !nRowCount )
+ {
+ bValidData = false;
+ nRowCount = 1;
+ aRows.push_back(nStrRow);
+ }
+
+ // Data
+ std::unique_ptr<ScMemChart> pMemChart(new ScMemChart( nColCount, nRowCount ));
+
+ if ( bValidData )
+ {
+ bool bCalcAsShown = rDocument.GetDocOptions().IsCalcAsShown();
+ for (nCol=0; nCol<nColCount; nCol++)
+ {
+ for (nRow=0; nRow<nRowCount; nRow++)
+ {
+ // DBL_MIN is a Hack for Chart to recognize empty cells.
+ ScAddress aPos(aCols[nCol], aRows[nRow], nTab1);
+ double nVal = getCellValue(rDocument, aPos, DBL_MIN, bCalcAsShown);
+ pMemChart->SetData(nCol, nRow, nVal);
+ }
+ }
+ }
+ else
+ {
+ // Flag marking data as invalid?
+ for (nCol=0; nCol<nColCount; nCol++)
+ for (nRow=0; nRow<nRowCount; nRow++)
+ pMemChart->SetData( nCol, nRow, DBL_MIN );
+ }
+
+ // Column Header
+
+ for (nCol=0; nCol<nColCount; nCol++)
+ {
+ OUString aString;
+ if (HasColHeaders())
+ aString = rDocument.GetString(aCols[nCol], nStrRow, nTab1);
+ if (aString.isEmpty())
+ {
+ ScAddress aPos( aCols[ nCol ], 0, 0 );
+ aString = ScResId(STR_COLUMN) + " " +
+ aPos.Format(ScRefFlags::COL_VALID);
+ }
+ pMemChart->SetColText( nCol, aString);
+ }
+
+ // Row Header
+
+ for (nRow=0; nRow<nRowCount; nRow++)
+ {
+ OUString aString;
+ if (HasRowHeaders())
+ {
+ aString = rDocument.GetString(nStrCol, aRows[nRow], nTab1);
+ }
+ if (aString.isEmpty())
+ {
+ aString = ScResId(STR_ROW) + " " +
+ OUString::number(static_cast<sal_Int32>(aRows[nRow]+1));
+ }
+ pMemChart->SetRowText( nRow, aString);
+ }
+
+ return pMemChart;
+}
+
+std::unique_ptr<ScMemChart> ScChartArray::CreateMemChartMulti()
+{
+ SCSIZE nColCount = GetPositionMap()->GetColCount();
+ SCSIZE nRowCount = GetPositionMap()->GetRowCount();
+
+ // May happen at least with more than 32k rows.
+ if (nColCount > SHRT_MAX || nRowCount > SHRT_MAX)
+ {
+ nColCount = 0;
+ nRowCount = 0;
+ }
+
+ bool bValidData = true;
+ if ( !nColCount )
+ {
+ bValidData = false;
+ nColCount = 1;
+ }
+ if ( !nRowCount )
+ {
+ bValidData = false;
+ nRowCount = 1;
+ }
+
+ // Data
+ std::unique_ptr<ScMemChart> pMemChart(new ScMemChart( nColCount, nRowCount ));
+
+ SCSIZE nCol = 0;
+ SCSIZE nRow = 0;
+ bool bCalcAsShown = rDocument.GetDocOptions().IsCalcAsShown();
+ sal_uLong nIndex = 0;
+ if (bValidData)
+ {
+ for ( nCol = 0; nCol < nColCount; nCol++ )
+ {
+ for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ )
+ {
+ double nVal = DBL_MIN; // Hack for Chart to recognize empty cells
+ const ScAddress* pPos = GetPositionMap()->GetPosition( nIndex );
+ if (pPos)
+ // otherwise: Gap
+ nVal = getCellValue(rDocument, *pPos, DBL_MIN, bCalcAsShown);
+
+ pMemChart->SetData(nCol, nRow, nVal);
+ }
+ }
+ }
+ else
+ {
+ for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ )
+ {
+ double nVal = DBL_MIN; // Hack for Chart to recognize empty cells
+ const ScAddress* pPos = GetPositionMap()->GetPosition( nIndex );
+ if (pPos)
+ // otherwise: Gap
+ nVal = getCellValue(rDocument, *pPos, DBL_MIN, bCalcAsShown);
+
+ pMemChart->SetData(nCol, nRow, nVal);
+ }
+ }
+
+ //TODO: Label when gaps
+
+ // Column header
+
+ SCCOL nPosCol = 0;
+ for ( nCol = 0; nCol < nColCount; nCol++ )
+ {
+ OUString aString;
+ const ScAddress* pPos = GetPositionMap()->GetColHeaderPosition( static_cast<SCCOL>(nCol) );
+ if ( HasColHeaders() && pPos )
+ aString = rDocument.GetString(pPos->Col(), pPos->Row(), pPos->Tab());
+
+ if (aString.isEmpty())
+ {
+ if ( pPos )
+ nPosCol = pPos->Col() + 1;
+ else
+ nPosCol++;
+ ScAddress aPos( nPosCol - 1, 0, 0 );
+ aString = ScResId(STR_COLUMN) + " " + aPos.Format(ScRefFlags::COL_VALID);
+ }
+ pMemChart->SetColText( nCol, aString);
+ }
+
+ // Row header
+
+ SCROW nPosRow = 0;
+ for ( nRow = 0; nRow < nRowCount; nRow++ )
+ {
+ OUString aString;
+ const ScAddress* pPos = GetPositionMap()->GetRowHeaderPosition( nRow );
+ if ( HasRowHeaders() && pPos )
+ aString = rDocument.GetString(pPos->Col(), pPos->Row(), pPos->Tab());
+
+ if (aString.isEmpty())
+ {
+ if ( pPos )
+ nPosRow = pPos->Row() + 1;
+ else
+ nPosRow++;
+ aString = ScResId(STR_ROW) + " " + OUString::number(static_cast<sal_Int32>(nPosRow));
+ }
+ pMemChart->SetRowText( nRow, aString);
+ }
+
+ return pMemChart;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/charthelper.cxx b/sc/source/core/tool/charthelper.cxx
new file mode 100644
index 0000000000..82e11c0550
--- /dev/null
+++ b/sc/source/core/tool/charthelper.cxx
@@ -0,0 +1,432 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <charthelper.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <rangelst.hxx>
+#include <chartlis.hxx>
+#include <docuno.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svtools/embedhlp.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+namespace
+{
+
+sal_uInt16 lcl_DoUpdateCharts( ScDocument& rDoc )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return 0;
+
+ sal_uInt16 nFound = 0;
+
+ sal_uInt16 nPageCount = pModel->GetPageCount();
+ for (sal_uInt16 nPageNo=0; nPageNo<nPageCount; nPageNo++)
+ {
+ SdrPage* pPage = pModel->GetPage(nPageNo);
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart( pObject ) )
+ {
+ OUString aName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+ rDoc.UpdateChart( aName );
+ ++nFound;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ return nFound;
+}
+
+bool lcl_AdjustRanges( ScRangeList& rRanges, SCTAB nSourceTab, SCTAB nDestTab, SCTAB nTabCount )
+{
+ //TODO: if multiple sheets are copied, update references into the other copied sheets?
+
+ bool bChanged = false;
+
+ for ( size_t i=0, nCount = rRanges.size(); i < nCount; i++ )
+ {
+ ScRange & rRange = rRanges[ i ];
+ if ( rRange.aStart.Tab() == nSourceTab && rRange.aEnd.Tab() == nSourceTab )
+ {
+ rRange.aStart.SetTab( nDestTab );
+ rRange.aEnd.SetTab( nDestTab );
+ bChanged = true;
+ }
+ if ( rRange.aStart.Tab() >= nTabCount )
+ {
+ rRange.aStart.SetTab( nTabCount > 0 ? ( nTabCount - 1 ) : 0 );
+ bChanged = true;
+ }
+ if ( rRange.aEnd.Tab() >= nTabCount )
+ {
+ rRange.aEnd.SetTab( nTabCount > 0 ? ( nTabCount - 1 ) : 0 );
+ bChanged = true;
+ }
+ }
+
+ return bChanged;
+}
+
+}//end anonymous namespace
+
+// ScChartHelper
+//static
+sal_uInt16 ScChartHelper::DoUpdateAllCharts( ScDocument& rDoc )
+{
+ return lcl_DoUpdateCharts( rDoc );
+}
+
+void ScChartHelper::AdjustRangesOfChartsOnDestinationPage( const ScDocument& rSrcDoc, ScDocument& rDestDoc, const SCTAB nSrcTab, const SCTAB nDestTab )
+{
+ ScDrawLayer* pDrawLayer = rDestDoc.GetDrawLayer();
+ if( !pDrawLayer )
+ return;
+
+ SdrPage* pDestPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nDestTab));
+ if( !pDestPage )
+ return;
+
+ SdrObjListIter aIter( pDestPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while( pObject )
+ {
+ if( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && static_cast<SdrOle2Obj*>(pObject)->IsChart() )
+ {
+ OUString aChartName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+
+ Reference< chart2::XChartDocument > xChartDoc( rDestDoc.GetChartByName( aChartName ) );
+ Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY );
+ if( xChartDoc.is() && xReceiver.is() && !xChartDoc->hasInternalDataProvider() )
+ {
+ ::std::vector< ScRangeList > aRangesVector;
+ rDestDoc.GetChartRanges( aChartName, aRangesVector, rSrcDoc );
+
+ for( ScRangeList& rScRangeList : aRangesVector )
+ {
+ lcl_AdjustRanges( rScRangeList, nSrcTab, nDestTab, rDestDoc.GetTableCount() );
+ }
+ rDestDoc.SetChartRanges( aChartName, aRangesVector );
+ }
+ }
+ pObject = aIter.Next();
+ }
+}
+
+void ScChartHelper::UpdateChartsOnDestinationPage( ScDocument& rDestDoc, const SCTAB nDestTab )
+{
+ ScDrawLayer* pDrawLayer = rDestDoc.GetDrawLayer();
+ if( !pDrawLayer )
+ return;
+
+ SdrPage* pDestPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nDestTab));
+ if( !pDestPage )
+ return;
+
+ SdrObjListIter aIter( pDestPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while( pObject )
+ {
+ if( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && static_cast<SdrOle2Obj*>(pObject)->IsChart() )
+ {
+ OUString aChartName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+ Reference< chart2::XChartDocument > xChartDoc( rDestDoc.GetChartByName( aChartName ) );
+ Reference< util::XModifiable > xModif(xChartDoc, uno::UNO_QUERY_THROW);
+ xModif->setModified( true);
+ }
+ pObject = aIter.Next();
+ }
+}
+
+uno::Reference< chart2::XChartDocument > ScChartHelper::GetChartFromSdrObject( const SdrObject* pObject )
+{
+ uno::Reference< chart2::XChartDocument > xReturn;
+ if( pObject )
+ {
+ if( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && static_cast<const SdrOle2Obj*>(pObject)->IsChart() )
+ {
+ uno::Reference< embed::XEmbeddedObject > xIPObj = static_cast<const SdrOle2Obj*>(pObject)->GetObjRef();
+ if( xIPObj.is() )
+ {
+ (void)svt::EmbeddedObjectRef::TryRunningState( xIPObj );
+ uno::Reference< util::XCloseable > xComponent = xIPObj->getComponent();
+ xReturn.set( uno::Reference< chart2::XChartDocument >( xComponent, uno::UNO_QUERY ) );
+ }
+ }
+ }
+ return xReturn;
+}
+
+void ScChartHelper::GetChartRanges( const uno::Reference< chart2::XChartDocument >& xChartDoc,
+ std::vector< OUString >& rRanges )
+{
+ rRanges.clear();
+ uno::Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY );
+ if( !xDataSource.is() )
+ return;
+
+ const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledDataSequences( xDataSource->getDataSequences() );
+ rRanges.reserve(2*aLabeledDataSequences.getLength());
+ for(const uno::Reference<chart2::data::XLabeledDataSequence>& xLabeledSequence : aLabeledDataSequences)
+ {
+ if(!xLabeledSequence.is())
+ continue;
+ uno::Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel());
+ uno::Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues());
+
+ if (xLabel.is())
+ rRanges.push_back( xLabel->getSourceRangeRepresentation() );
+ if (xValues.is())
+ rRanges.push_back( xValues->getSourceRangeRepresentation() );
+ }
+}
+
+void ScChartHelper::SetChartRanges( const uno::Reference< chart2::XChartDocument >& xChartDoc,
+ const uno::Sequence< OUString >& rRanges )
+{
+ uno::Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY );
+ if( !xDataSource.is() )
+ return;
+ uno::Reference< chart2::data::XDataProvider > xDataProvider = xChartDoc->getDataProvider();
+ if( !xDataProvider.is() )
+ return;
+
+ xChartDoc->lockControllers();
+
+ try
+ {
+ OUString aPropertyNameRole( "Role" );
+
+ uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledDataSequences( xDataSource->getDataSequences() );
+ sal_Int32 nRange=0;
+ for( uno::Reference<chart2::data::XLabeledDataSequence>& xLabeledSequence : asNonConstRange(aLabeledDataSequences) )
+ {
+ if( nRange >= rRanges.getLength() )
+ break;
+
+ if(!xLabeledSequence.is())
+ continue;
+ uno::Reference< beans::XPropertySet > xLabel( xLabeledSequence->getLabel(), uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySet > xValues( xLabeledSequence->getValues(), uno::UNO_QUERY );
+
+ if( xLabel.is())
+ {
+ uno::Reference< chart2::data::XDataSequence > xNewSeq(
+ xDataProvider->createDataSequenceByRangeRepresentation( rRanges[nRange++] ));
+
+ uno::Reference< beans::XPropertySet > xNewProps( xNewSeq, uno::UNO_QUERY );
+ if( xNewProps.is() )
+ xNewProps->setPropertyValue( aPropertyNameRole, xLabel->getPropertyValue( aPropertyNameRole ) );
+
+ xLabeledSequence->setLabel( xNewSeq );
+ }
+
+ if( nRange >= rRanges.getLength() )
+ break;
+
+ if( xValues.is())
+ {
+ uno::Reference< chart2::data::XDataSequence > xNewSeq(
+ xDataProvider->createDataSequenceByRangeRepresentation( rRanges[nRange++] ));
+
+ uno::Reference< beans::XPropertySet > xNewProps( xNewSeq, uno::UNO_QUERY );
+ if( xNewProps.is() )
+ xNewProps->setPropertyValue( aPropertyNameRole, xValues->getPropertyValue( aPropertyNameRole ) );
+
+ xLabeledSequence->setValues( xNewSeq );
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Exception in ScChartHelper::SetChartRanges - invalid range string?");
+ }
+
+ xChartDoc->unlockControllers();
+}
+
+void ScChartHelper::AddRangesIfProtectedChart( ScRangeListVector& rRangesVector, const ScDocument& rDocument, SdrObject* pObject )
+{
+ if ( !(pObject && ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 )) )
+ return;
+
+ SdrOle2Obj* pSdrOle2Obj = dynamic_cast< SdrOle2Obj* >( pObject );
+ if ( !(pSdrOle2Obj && pSdrOle2Obj->IsChart()) )
+ return;
+
+ const uno::Reference< embed::XEmbeddedObject >& xEmbeddedObj = pSdrOle2Obj->GetObjRef();
+ if ( !xEmbeddedObj.is() )
+ return;
+
+ bool bDisableDataTableDialog = false;
+ sal_Int32 nOldState = xEmbeddedObj->getCurrentState();
+ (void)svt::EmbeddedObjectRef::TryRunningState( xEmbeddedObj );
+ uno::Reference< beans::XPropertySet > xProps( xEmbeddedObj->getComponent(), uno::UNO_QUERY );
+ if ( xProps.is() &&
+ ( xProps->getPropertyValue("DisableDataTableDialog") >>= bDisableDataTableDialog ) &&
+ bDisableDataTableDialog )
+ {
+ ScChartListenerCollection* pCollection = rDocument.GetChartListenerCollection();
+ if (pCollection)
+ {
+ const OUString& aChartName = pSdrOle2Obj->GetPersistName();
+ const ScChartListener* pListener = pCollection->findByName(aChartName);
+ if (pListener)
+ {
+ const ScRangeListRef& rRangeList = pListener->GetRangeList();
+ if ( rRangeList.is() )
+ {
+ rRangesVector.push_back( *rRangeList );
+ }
+ }
+ }
+ }
+ if ( xEmbeddedObj->getCurrentState() != nOldState )
+ {
+ xEmbeddedObj->changeState( nOldState );
+ }
+}
+
+void ScChartHelper::FillProtectedChartRangesVector( ScRangeListVector& rRangesVector, const ScDocument& rDocument, const SdrPage* pPage )
+{
+ if ( pPage )
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while ( pObject )
+ {
+ AddRangesIfProtectedChart( rRangesVector, rDocument, pObject );
+ pObject = aIter.Next();
+ }
+ }
+}
+
+void ScChartHelper::GetChartNames( ::std::vector< OUString >& rChartNames, const SdrPage* pPage )
+{
+ if ( !pPage )
+ return;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while ( pObject )
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ SdrOle2Obj* pSdrOle2Obj = dynamic_cast< SdrOle2Obj* >( pObject );
+ if ( pSdrOle2Obj && pSdrOle2Obj->IsChart() )
+ {
+ rChartNames.push_back( pSdrOle2Obj->GetPersistName() );
+ }
+ }
+ pObject = aIter.Next();
+ }
+}
+
+void ScChartHelper::CreateProtectedChartListenersAndNotify( ScDocument& rDoc, const SdrPage* pPage, ScModelObj* pModelObj, SCTAB nTab,
+ const ScRangeListVector& rRangesVector, const ::std::vector< OUString >& rExcludedChartNames, bool bSameDoc )
+{
+ if ( !(pPage && pModelObj) )
+ return;
+
+ size_t nRangeListCount = rRangesVector.size();
+ size_t nRangeList = 0;
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while ( pObject )
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ SdrOle2Obj* pSdrOle2Obj = dynamic_cast< SdrOle2Obj* >( pObject );
+ if ( pSdrOle2Obj && pSdrOle2Obj->IsChart() )
+ {
+ const OUString& aChartName = pSdrOle2Obj->GetPersistName();
+ ::std::vector< OUString >::const_iterator aEnd = rExcludedChartNames.end();
+ ::std::vector< OUString >::const_iterator aFound = ::std::find( rExcludedChartNames.begin(), aEnd, aChartName );
+ if ( aFound == aEnd )
+ {
+ const uno::Reference< embed::XEmbeddedObject >& xEmbeddedObj = pSdrOle2Obj->GetObjRef();
+ if ( xEmbeddedObj.is() && ( nRangeList < nRangeListCount ) )
+ {
+ bool bDisableDataTableDialog = false;
+ (void)svt::EmbeddedObjectRef::TryRunningState( xEmbeddedObj );
+ uno::Reference< beans::XPropertySet > xProps( xEmbeddedObj->getComponent(), uno::UNO_QUERY );
+ if ( xProps.is() &&
+ ( xProps->getPropertyValue("DisableDataTableDialog") >>= bDisableDataTableDialog ) &&
+ bDisableDataTableDialog )
+ {
+ if ( bSameDoc )
+ {
+ ScChartListenerCollection* pCollection = rDoc.GetChartListenerCollection();
+ if (pCollection && !pCollection->findByName(aChartName))
+ {
+ ScRangeList aRangeList( rRangesVector[ nRangeList++ ] );
+ ScRangeListRef rRangeList( new ScRangeList( aRangeList ) );
+ ScChartListener* pChartListener = new ScChartListener( aChartName, rDoc, rRangeList );
+ pCollection->insert( pChartListener );
+ pChartListener->StartListeningTo();
+ }
+ }
+ else
+ {
+ xProps->setPropertyValue("DisableDataTableDialog",
+ uno::Any( false ) );
+ xProps->setPropertyValue("DisableComplexChartTypes",
+ uno::Any( false ) );
+ }
+ }
+ }
+
+ if (pModelObj->HasChangesListeners())
+ {
+ tools::Rectangle aRectangle = pSdrOle2Obj->GetSnapRect();
+ ScRange aRange( rDoc.GetRange( nTab, aRectangle ) );
+ ScRangeList aChangeRanges( aRange );
+
+ uno::Sequence< beans::PropertyValue > aProperties{
+ comphelper::makePropertyValue("Name", aChartName)
+ };
+
+ pModelObj->NotifyChanges( "insert-chart", aChangeRanges, aProperties );
+ }
+ }
+ }
+ }
+ pObject = aIter.Next();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/chartlis.cxx b/sc/source/core/tool/chartlis.cxx
new file mode 100644
index 0000000000..024ff205e1
--- /dev/null
+++ b/sc/source/core/tool/chartlis.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 <memory>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <chartlis.hxx>
+#include <brdcst.hxx>
+#include <document.hxx>
+#include <reftokenhelper.hxx>
+#include <formula/token.hxx>
+#include <com/sun/star/chart/XChartDataChangeEventListener.hpp>
+
+using namespace com::sun::star;
+using ::std::vector;
+using ::std::for_each;
+
+// Update chart listeners quickly, to get a similar behavior to loaded charts
+// which register UNO listeners.
+
+class ScChartUnoData
+{
+ uno::Reference< chart::XChartDataChangeEventListener > xListener;
+ uno::Reference< chart::XChartData > xSource;
+
+public:
+ ScChartUnoData( uno::Reference< chart::XChartDataChangeEventListener > xL,
+ uno::Reference< chart::XChartData > xS ) :
+ xListener(std::move( xL )), xSource(std::move( xS )) {}
+
+ const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const { return xListener; }
+ const uno::Reference< chart::XChartData >& GetSource() const { return xSource; }
+};
+
+// ScChartListener
+ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener& rParent, ScDocument& rDoc) :
+ mrParent(rParent), m_pDoc(&rDoc)
+{
+}
+
+ScChartListener::ExternalRefListener::~ExternalRefListener()
+{
+ if (!m_pDoc || m_pDoc->IsInDtorClear())
+ // The document is being destroyed. Do nothing.
+ return;
+
+ // Make sure to remove all pointers to this object.
+ m_pDoc->GetExternalRefManager()->removeLinkListener(this);
+}
+
+void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
+{
+ switch (eType)
+ {
+ case ScExternalRefManager::LINK_MODIFIED:
+ {
+ if (maFileIds.count(nFileId))
+ // We are listening to this external document. Send an update
+ // request to the chart.
+ mrParent.SetUpdateQueue();
+ }
+ break;
+ case ScExternalRefManager::LINK_BROKEN:
+ removeFileId(nFileId);
+ break;
+ case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE:
+ m_pDoc = nullptr;
+ break;
+ }
+}
+
+void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId)
+{
+ maFileIds.insert(nFileId);
+}
+
+void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId)
+{
+ maFileIds.erase(nFileId);
+}
+
+ScChartListener::ScChartListener( OUString aName, ScDocument& rDocP,
+ const ScRangeListRef& rRangeList ) :
+ maName(std::move(aName)),
+ mrDoc( rDocP ),
+ bUsed( false ),
+ bDirty( false )
+{
+ ScRefTokenHelper::getTokensFromRangeList(&rDocP, maTokens, *rRangeList);
+}
+
+ScChartListener::ScChartListener( OUString aName, ScDocument& rDocP, vector<ScTokenRef> aTokens ) :
+ maTokens(std::move(aTokens)),
+ maName(std::move(aName)),
+ mrDoc( rDocP ),
+ bUsed( false ),
+ bDirty( false )
+{
+}
+
+ScChartListener::~ScChartListener()
+{
+ if ( HasBroadcaster() )
+ EndListeningTo();
+ pUnoData.reset();
+
+ if (mpExtRefListener)
+ {
+ // Stop listening to all external files.
+ ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
+ const std::unordered_set<sal_uInt16>& rFileIds = mpExtRefListener->getAllFileIds();
+ for (const auto& rFileId : rFileIds)
+ pRefMgr->removeLinkListener(rFileId, mpExtRefListener.get());
+ }
+}
+
+void ScChartListener::SetUno(
+ const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
+ const uno::Reference< chart::XChartData >& rSource )
+{
+ pUnoData.reset( new ScChartUnoData( rListener, rSource ) );
+}
+
+uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const
+{
+ if ( pUnoData )
+ return pUnoData->GetListener();
+ return uno::Reference< chart::XChartDataChangeEventListener >();
+}
+
+uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const
+{
+ if ( pUnoData )
+ return pUnoData->GetSource();
+ return uno::Reference< chart::XChartData >();
+}
+
+void ScChartListener::Notify( const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ScDataChanged)
+ SetUpdateQueue();
+}
+
+void ScChartListener::Update()
+{
+ if ( mrDoc.IsInInterpreter() )
+ { // If interpreting do nothing and restart timer so we don't
+ // interfere with interpreter and don't produce an Err522 or similar.
+ // This may happen if we are rescheduled via Basic function.
+ mrDoc.GetChartListenerCollection()->StartTimer();
+ return ;
+ }
+ if ( pUnoData )
+ {
+ bDirty = false;
+ // recognize some day what has changed inside the Chart
+ chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(),
+ chart::ChartDataChangeType_ALL,
+ 0, 0, 0, 0 );
+ pUnoData->GetListener()->chartDataChanged( aEvent );
+ }
+ else if ( mrDoc.GetAutoCalc() )
+ {
+ bDirty = false;
+ mrDoc.UpdateChart(GetName());
+ }
+}
+
+ScRangeListRef ScChartListener::GetRangeList() const
+{
+ ScRangeListRef aRLRef(new ScRangeList);
+ ScRefTokenHelper::getRangeListFromTokens(&mrDoc, *aRLRef, maTokens, ScAddress());
+ return aRLRef;
+}
+
+void ScChartListener::SetRangeList( const ScRangeListRef& rNew )
+{
+ vector<ScTokenRef> aTokens;
+ ScRefTokenHelper::getTokensFromRangeList(&mrDoc, aTokens, *rNew);
+ maTokens.swap(aTokens);
+}
+
+namespace {
+
+class StartEndListening
+{
+public:
+ StartEndListening(ScDocument& rDoc, ScChartListener& rParent, bool bStart) :
+ mrDoc(rDoc), mrParent(rParent), mbStart(bStart) {}
+
+ void operator() (const ScTokenRef& pToken)
+ {
+ if (!ScRefTokenHelper::isRef(pToken))
+ return;
+
+ bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
+ if (bExternal)
+ {
+ sal_uInt16 nFileId = pToken->GetIndex();
+ ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
+ ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener();
+ if (mbStart)
+ {
+ pRefMgr->addLinkListener(nFileId, pExtRefListener);
+ pExtRefListener->addFileId(nFileId);
+ }
+ else
+ {
+ pRefMgr->removeLinkListener(nFileId, pExtRefListener);
+ pExtRefListener->removeFileId(nFileId);
+ }
+ }
+ else
+ {
+ ScRange aRange;
+ ScRefTokenHelper::getRangeFromToken(&mrDoc, aRange, pToken, ScAddress(), bExternal);
+ if (mbStart)
+ startListening(aRange);
+ else
+ endListening(aRange);
+ }
+ }
+private:
+ void startListening(const ScRange& rRange)
+ {
+ if (rRange.aStart == rRange.aEnd)
+ mrDoc.StartListeningCell(rRange.aStart, &mrParent);
+ else
+ mrDoc.StartListeningArea(rRange, false, &mrParent);
+ }
+
+ void endListening(const ScRange& rRange)
+ {
+ if (rRange.aStart == rRange.aEnd)
+ mrDoc.EndListeningCell(rRange.aStart, &mrParent);
+ else
+ mrDoc.EndListeningArea(rRange, false, &mrParent);
+ }
+private:
+ ScDocument& mrDoc;
+ ScChartListener& mrParent;
+ bool mbStart;
+};
+
+}
+
+void ScChartListener::StartListeningTo()
+{
+ if (maTokens.empty())
+ // no references to listen to.
+ return;
+
+ for_each(maTokens.begin(), maTokens.end(), StartEndListening(mrDoc, *this, true));
+}
+
+void ScChartListener::EndListeningTo()
+{
+ if (maTokens.empty())
+ // no references to listen to.
+ return;
+
+ for_each(maTokens.begin(), maTokens.end(), StartEndListening(mrDoc, *this, false));
+}
+
+void ScChartListener::ChangeListening( const ScRangeListRef& rRangeListRef,
+ bool bDirtyP )
+{
+ EndListeningTo();
+ SetRangeList( rRangeListRef );
+ StartListeningTo();
+ if ( bDirtyP )
+ SetDirty( true );
+}
+
+void ScChartListener::UpdateChartIntersecting( const ScRange& rRange )
+{
+ ScTokenRef pToken;
+ ScRefTokenHelper::getTokenFromRange(&mrDoc, pToken, rRange);
+
+ if (ScRefTokenHelper::intersects(&mrDoc, maTokens, pToken, ScAddress()))
+ {
+ // force update (chart has to be loaded), don't use ScChartListener::Update
+ mrDoc.UpdateChart(GetName());
+ }
+}
+
+ScChartListener::ExternalRefListener* ScChartListener::GetExtRefListener()
+{
+ if (!mpExtRefListener)
+ mpExtRefListener.reset(new ExternalRefListener(*this, mrDoc));
+
+ return mpExtRefListener.get();
+}
+
+void ScChartListener::SetUpdateQueue()
+{
+ bDirty = true;
+ mrDoc.GetChartListenerCollection()->StartTimer();
+}
+
+bool ScChartListener::operator==( const ScChartListener& r ) const
+{
+ bool b1 = !maTokens.empty();
+ bool b2 = !r.maTokens.empty();
+
+ if (&mrDoc != &r.mrDoc || bUsed != r.bUsed || bDirty != r.bDirty ||
+ GetName() != r.GetName() || b1 != b2)
+ return false;
+
+ if (!b1 && !b2)
+ // both token list instances are empty.
+ return true;
+
+ return maTokens == r.maTokens;
+}
+
+bool ScChartListener::operator!=( const ScChartListener& r ) const
+{
+ return !operator==(r);
+}
+
+ScChartHiddenRangeListener::ScChartHiddenRangeListener()
+{
+}
+
+ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
+{
+ // empty d'tor
+}
+
+void ScChartListenerCollection::Init()
+{
+ aIdle.SetInvokeHandler( LINK( this, ScChartListenerCollection, TimerHdl ) );
+ aIdle.SetPriority( TaskPriority::REPAINT );
+}
+
+ScChartListenerCollection::ScChartListenerCollection( ScDocument& rDocP ) :
+ meModifiedDuringUpdate( SC_CLCUPDATE_NONE ),
+ aIdle( "sc::ScChartListenerCollection aIdle" ),
+ rDoc( rDocP )
+{
+ Init();
+}
+
+ScChartListenerCollection::ScChartListenerCollection(
+ const ScChartListenerCollection& rColl ) :
+ meModifiedDuringUpdate( SC_CLCUPDATE_NONE ),
+ aIdle( "sc::ScChartListenerCollection aIdle" ),
+ rDoc( rColl.rDoc )
+{
+ Init();
+}
+
+ScChartListenerCollection::~ScChartListenerCollection()
+{
+ // remove ChartListener objects before aIdle dtor is called, because
+ // ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
+ // to be called if an empty ScNoteCell is deleted
+
+ m_Listeners.clear();
+}
+
+void ScChartListenerCollection::StartAllListeners()
+{
+ for (auto const& it : m_Listeners)
+ {
+ it.second->StartListeningTo();
+ }
+}
+
+bool ScChartListenerCollection::insert(ScChartListener* pListener)
+{
+ if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
+ meModifiedDuringUpdate = SC_CLCUPDATE_MODIFIED;
+ OUString aName = pListener->GetName();
+ return m_Listeners.insert(std::make_pair(aName, std::unique_ptr<ScChartListener>(pListener))).second;
+}
+
+void ScChartListenerCollection::removeByName(const OUString& rName)
+{
+ if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
+ meModifiedDuringUpdate = SC_CLCUPDATE_MODIFIED;
+ m_Listeners.erase(rName);
+}
+
+ScChartListener* ScChartListenerCollection::findByName(const OUString& rName)
+{
+ ListenersType::iterator const it = m_Listeners.find(rName);
+ return it == m_Listeners.end() ? nullptr : it->second.get();
+}
+
+const ScChartListener* ScChartListenerCollection::findByName(const OUString& rName) const
+{
+ ListenersType::const_iterator const it = m_Listeners.find(rName);
+ return it == m_Listeners.end() ? nullptr : it->second.get();
+}
+
+bool ScChartListenerCollection::hasListeners() const
+{
+ return !m_Listeners.empty();
+}
+
+OUString ScChartListenerCollection::getUniqueName(std::u16string_view rPrefix) const
+{
+ for (sal_Int32 nNum = 1; nNum < 10000; ++nNum) // arbitrary limit to prevent infinite loop.
+ {
+ OUString aTestName = rPrefix + OUString::number(nNum);
+ if (m_Listeners.find(aTestName) == m_Listeners.end())
+ return aTestName;
+ }
+ return OUString();
+}
+
+void ScChartListenerCollection::ChangeListening( const OUString& rName,
+ const ScRangeListRef& rRangeListRef )
+{
+ ScChartListener* pCL = findByName(rName);
+ if (pCL)
+ {
+ pCL->EndListeningTo();
+ pCL->SetRangeList( rRangeListRef );
+ }
+ else
+ {
+ pCL = new ScChartListener(rName, rDoc, rRangeListRef);
+ insert(pCL);
+ }
+ pCL->StartListeningTo();
+}
+
+void ScChartListenerCollection::FreeUnused()
+{
+ if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
+ meModifiedDuringUpdate = SC_CLCUPDATE_MODIFIED;
+
+ ListenersType aUsed;
+
+ for (auto & pair : m_Listeners)
+ {
+ ScChartListener* p = pair.second.get();
+ if (p->IsUno())
+ {
+ // We don't delete UNO charts; they are to be deleted separately via FreeUno().
+ aUsed.insert(std::make_pair(pair.first, std::move(pair.second)));
+ continue;
+ }
+
+ if (p->IsUsed())
+ {
+ p->SetUsed(false);
+ aUsed.insert(std::make_pair(pair.first, std::move(pair.second)));
+ }
+ }
+
+ m_Listeners = std::move(aUsed);
+}
+
+void ScChartListenerCollection::FreeUno( const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
+ const uno::Reference< chart::XChartData >& rSource )
+{
+ if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
+ meModifiedDuringUpdate = SC_CLCUPDATE_MODIFIED;
+
+ for (auto it = m_Listeners.begin(); it != m_Listeners.end(); )
+ {
+ ScChartListener *const p = it->second.get();
+ if (p->IsUno() && p->GetUnoListener() == rListener && p->GetUnoSource() == rSource)
+ it = m_Listeners.erase(it);
+ else
+ ++it;
+ }
+}
+
+void ScChartListenerCollection::StartTimer()
+{
+ aIdle.Start();
+}
+
+IMPL_LINK_NOARG(ScChartListenerCollection, TimerHdl, Timer *, void)
+{
+ if ( Application::AnyInput( VclInputFlags::KEYBOARD ) )
+ {
+ aIdle.Start();
+ return;
+ }
+ UpdateDirtyCharts();
+}
+
+void ScChartListenerCollection::UpdateDirtyCharts()
+{
+ // During ScChartListener::Update() the most nasty things can happen due to
+ // UNO listeners, e.g. reentrant calls via BASIC to insert() and FreeUno()
+ // and similar that modify m_Listeners and invalidate iterators.
+ meModifiedDuringUpdate = SC_CLCUPDATE_RUNNING;
+
+ for (auto const& it : m_Listeners)
+ {
+ ScChartListener *const p = it.second.get();
+ if (p->IsDirty())
+ p->Update();
+
+ if (meModifiedDuringUpdate == SC_CLCUPDATE_MODIFIED)
+ break; // iterator is invalid
+
+ if (aIdle.IsActive() && !rDoc.IsImportingXML())
+ break; // one interfered
+ }
+ meModifiedDuringUpdate = SC_CLCUPDATE_NONE;
+}
+
+void ScChartListenerCollection::SetDirty()
+{
+ for (auto const& it : m_Listeners)
+ {
+ it.second->SetDirty(true);
+ }
+
+ StartTimer();
+}
+
+void ScChartListenerCollection::SetDiffDirty(
+ const ScChartListenerCollection& rCmp, bool bSetChartRangeLists )
+{
+ bool bDirty = false;
+ for (auto const& it : m_Listeners)
+ {
+ ScChartListener *const pCL = it.second.get();
+ assert(pCL);
+ const ScChartListener* pCLCmp = rCmp.findByName(pCL->GetName());
+ if (!pCLCmp || *pCL != *pCLCmp)
+ {
+ if ( bSetChartRangeLists )
+ {
+ if (pCLCmp)
+ {
+ const ScRangeListRef& rList1 = pCL->GetRangeList();
+ const ScRangeListRef& rList2 = pCLCmp->GetRangeList();
+ bool b1 = rList1.is();
+ bool b2 = rList2.is();
+ if ( b1 != b2 || (b1 && b2 && (*rList1 != *rList2)) )
+ rDoc.SetChartRangeList( pCL->GetName(), rList1 );
+ }
+ else
+ rDoc.SetChartRangeList( pCL->GetName(), pCL->GetRangeList() );
+ }
+ bDirty = true;
+ pCL->SetDirty( true );
+ }
+ }
+ if ( bDirty )
+ StartTimer();
+}
+
+void ScChartListenerCollection::SetRangeDirty( const ScRange& rRange )
+{
+ bool bDirty = false;
+ for (auto const& it : m_Listeners)
+ {
+ ScChartListener *const pCL = it.second.get();
+ const ScRangeListRef& rList = pCL->GetRangeList();
+ if ( rList.is() && rList->Intersects( rRange ) )
+ {
+ bDirty = true;
+ pCL->SetDirty( true );
+ }
+ }
+ if ( bDirty )
+ StartTimer();
+
+ // New hidden range listener implementation
+ for (auto& [pListener, rHiddenRange] : maHiddenListeners)
+ {
+ if (rHiddenRange.Intersects(rRange))
+ {
+ pListener->notify();
+ }
+ }
+}
+
+void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab )
+{
+ ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
+ for (auto const& it : m_Listeners)
+ {
+ it.second->UpdateChartIntersecting(aRange);
+ }
+}
+
+bool ScChartListenerCollection::operator==( const ScChartListenerCollection& r ) const
+{
+ // Do not use ScStrCollection::operator==() here that uses IsEqual and Compare.
+ // Use ScChartListener::operator==() instead.
+ if (&rDoc != &r.rDoc)
+ return false;
+
+ return std::equal(m_Listeners.begin(), m_Listeners.end(), r.m_Listeners.begin(), r.m_Listeners.end(),
+ [](const ListenersType::value_type& lhs, const ListenersType::value_type& rhs) {
+ return (lhs.first == rhs.first) && (*lhs.second == *rhs.second);
+ });
+}
+
+void ScChartListenerCollection::StartListeningHiddenRange( const ScRange& rRange, ScChartHiddenRangeListener* pListener )
+{
+ maHiddenListeners.insert(std::make_pair<>(pListener, rRange));
+}
+
+void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener* pListener )
+{
+ auto range = maHiddenListeners.equal_range(pListener);
+ maHiddenListeners.erase(range.first, range.second);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/chartlock.cxx b/sc/source/core/tool/chartlock.cxx
new file mode 100644
index 0000000000..6b55b0bf7a
--- /dev/null
+++ b/sc/source/core/tool/chartlock.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/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <chartlock.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XComponentSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::WeakReference;
+
+#define SC_CHARTLOCKTIMEOUT 660
+
+namespace
+{
+
+std::vector< WeakReference< frame::XModel > > lcl_getAllLivingCharts( ScDocument* pDoc )
+{
+ std::vector< WeakReference< frame::XModel > > aRet;
+ if( !pDoc )
+ return aRet;
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ if (!pDrawLayer)
+ return aRet;
+
+ for (SCTAB nTab=0; nTab<=pDoc->GetMaxTableNumber(); nTab++)
+ {
+ if (pDoc->HasTable(nTab))
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if( ScDocument::IsChart( pObject ) )
+ {
+ uno::Reference< embed::XEmbeddedObject > xIPObj = static_cast<SdrOle2Obj*>(pObject)->GetObjRef();
+ uno::Reference< embed::XComponentSupplier > xCompSupp = xIPObj;
+ if( xCompSupp.is())
+ {
+ Reference< frame::XModel > xModel( xCompSupp->getComponent(), uno::UNO_QUERY );
+ if( xModel.is() )
+ aRet.emplace_back(xModel );
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ return aRet;
+}
+
+}//end anonymous namespace
+
+// ScChartLockGuard
+ScChartLockGuard::ScChartLockGuard( ScDocument* pDoc ) :
+ maChartModels( lcl_getAllLivingCharts( pDoc ) )
+{
+ for( const auto& rxChartModel : maChartModels )
+ {
+ try
+ {
+ Reference< frame::XModel > xModel( rxChartModel );
+ if( xModel.is())
+ xModel->lockControllers();
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in ScChartLockGuard");
+ }
+ }
+}
+
+ScChartLockGuard::~ScChartLockGuard()
+{
+ for( const auto& rxChartModel : maChartModels )
+ {
+ try
+ {
+ Reference< frame::XModel > xModel( rxChartModel );
+ if( xModel.is())
+ xModel->unlockControllers();
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in ScChartLockGuard");
+ }
+ }
+}
+
+void ScChartLockGuard::AlsoLockThisChart( const Reference< frame::XModel >& xModel )
+{
+ if(!xModel.is())
+ return;
+
+ WeakReference< frame::XModel > xWeakModel(xModel);
+
+ std::vector< WeakReference< frame::XModel > >::iterator aFindIter(
+ ::std::find( maChartModels.begin(), maChartModels.end(), xWeakModel ) );
+
+ if( aFindIter == maChartModels.end() )
+ {
+ try
+ {
+ xModel->lockControllers();
+ maChartModels.emplace_back(xModel );
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in ScChartLockGuard");
+ }
+ }
+}
+
+// ScTemporaryChartLock
+ScTemporaryChartLock::ScTemporaryChartLock( ScDocument* pDocP ) :
+ mpDoc( pDocP ), maTimer("ScTemporaryChartLock maTimer")
+{
+ maTimer.SetTimeout( SC_CHARTLOCKTIMEOUT );
+ maTimer.SetInvokeHandler( LINK( this, ScTemporaryChartLock, TimeoutHdl ) );
+}
+
+ScTemporaryChartLock::~ScTemporaryChartLock()
+{
+ mpDoc = nullptr;
+ StopLocking();
+}
+
+void ScTemporaryChartLock::StartOrContinueLocking()
+{
+ if (!mapScChartLockGuard)
+ mapScChartLockGuard.reset( new ScChartLockGuard(mpDoc) );
+ maTimer.Start();
+}
+
+void ScTemporaryChartLock::StopLocking()
+{
+ maTimer.Stop();
+ mapScChartLockGuard.reset();
+}
+
+void ScTemporaryChartLock::AlsoLockThisChart( const Reference< frame::XModel >& xModel )
+{
+ if (mapScChartLockGuard)
+ mapScChartLockGuard->AlsoLockThisChart( xModel );
+}
+
+IMPL_LINK_NOARG(ScTemporaryChartLock, TimeoutHdl, Timer *, void)
+{
+ mapScChartLockGuard.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/chartpos.cxx b/sc/source/core/tool/chartpos.cxx
new file mode 100644
index 0000000000..11af92f946
--- /dev/null
+++ b/sc/source/core/tool/chartpos.cxx
@@ -0,0 +1,524 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <chartpos.hxx>
+#include <document.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+
+#include <memory>
+#include <utility>
+
+namespace
+{
+ bool lcl_hasValueDataButNoDates( const ScDocument& rDocument, SCCOL nCol, SCROW nRow, SCTAB nTab )
+ {
+ bool bReturn = false;
+ if (rDocument.HasValueData( nCol, nRow, nTab ))
+ {
+ //treat dates like text #i25706#
+ sal_uInt32 nNumberFormat = rDocument.GetNumberFormat( ScAddress( nCol, nRow, nTab ) );
+ SvNumFormatType nType = rDocument.GetFormatTable()->GetType(nNumberFormat);
+ bool bIsDate(nType & SvNumFormatType::DATE);
+ bReturn = !bIsDate;
+ }
+ return bReturn;
+ }
+}
+
+ScChartPositioner::ScChartPositioner( ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP) :
+ rDocument( rDoc ),
+ eGlue( ScChartGlue::NA ),
+ nStartCol(0),
+ nStartRow(0),
+ bColHeaders( false ),
+ bRowHeaders( false ),
+ bDummyUpperLeft( false )
+{
+ SetRangeList( ScRange( nStartColP, nStartRowP, nTab, nEndColP, nEndRowP, nTab ) );
+ CheckColRowHeaders();
+}
+
+ScChartPositioner::ScChartPositioner( ScDocument& rDoc, ScRangeListRef xRangeList ) :
+ aRangeListRef(std::move( xRangeList )),
+ rDocument( rDoc ),
+ eGlue( ScChartGlue::NA ),
+ nStartCol(0),
+ nStartRow(0),
+ bColHeaders( false ),
+ bRowHeaders( false ),
+ bDummyUpperLeft( false )
+{
+ if ( aRangeListRef.is() )
+ CheckColRowHeaders();
+}
+
+ScChartPositioner::ScChartPositioner( const ScChartPositioner& rPositioner ) :
+ aRangeListRef( rPositioner.aRangeListRef ),
+ rDocument(rPositioner.rDocument),
+ eGlue(rPositioner.eGlue),
+ nStartCol(rPositioner.nStartCol),
+ nStartRow(rPositioner.nStartRow),
+ bColHeaders(rPositioner.bColHeaders),
+ bRowHeaders(rPositioner.bRowHeaders),
+ bDummyUpperLeft( rPositioner.bDummyUpperLeft )
+{
+}
+
+ScChartPositioner::~ScChartPositioner()
+{
+}
+
+void ScChartPositioner::SetRangeList( const ScRange& rRange )
+{
+ aRangeListRef = new ScRangeList( rRange );
+ InvalidateGlue();
+}
+
+void ScChartPositioner::GlueState()
+{
+ if ( eGlue != ScChartGlue::NA )
+ return;
+ bDummyUpperLeft = false;
+ ScRange* pR;
+ if ( aRangeListRef->size() <= 1 )
+ {
+ if ( !aRangeListRef->empty() )
+ {
+ pR = &aRangeListRef->front();
+ if ( pR->aStart.Tab() == pR->aEnd.Tab() )
+ eGlue = ScChartGlue::NONE;
+ else
+ eGlue = ScChartGlue::Cols; // several tables column by column
+ nStartCol = pR->aStart.Col();
+ nStartRow = pR->aStart.Row();
+ }
+ else
+ {
+ InvalidateGlue();
+ nStartCol = 0;
+ nStartRow = 0;
+ }
+ return;
+ }
+
+ pR = &aRangeListRef->front();
+ nStartCol = pR->aStart.Col();
+ nStartRow = pR->aStart.Row();
+ SCCOL nMaxCols, nEndCol;
+ SCROW nMaxRows, nEndRow;
+ nMaxCols = nEndCol = 0;
+ nMaxRows = nEndRow = 0;
+
+ // <= so 1 extra pass after last item
+ for ( size_t i = 1, nRanges = aRangeListRef->size(); i <= nRanges; ++i )
+ { // detect spanning/surrounding area etc.
+ SCCOLROW nTmp, n1, n2;
+ if ( (n1 = pR->aStart.Col()) < nStartCol ) nStartCol = static_cast<SCCOL>(n1 );
+ if ( (n2 = pR->aEnd.Col() ) > nEndCol ) nEndCol = static_cast<SCCOL>(n2 );
+ if ( (nTmp = n2 - n1 + 1 ) > nMaxCols ) nMaxCols = static_cast<SCCOL>(nTmp);
+ if ( (n1 = pR->aStart.Row()) < nStartRow ) nStartRow = static_cast<SCROW>(n1 );
+ if ( (n2 = pR->aEnd.Row() ) > nEndRow ) nEndRow = static_cast<SCROW>(n2 );
+ if ( (nTmp = n2 - n1 + 1 ) > nMaxRows ) nMaxRows = static_cast<SCROW>(nTmp);
+
+ // in last pass; i = nRanges so don't use at()
+ if ( i < nRanges )
+ pR = &(*aRangeListRef)[i];
+ }
+ SCCOL nC = nEndCol - nStartCol + 1;
+ if ( nC == 1 )
+ {
+ eGlue = ScChartGlue::Rows;
+ return;
+ }
+ SCROW nR = nEndRow - nStartRow + 1;
+ if ( nR == 1 )
+ {
+ eGlue = ScChartGlue::Cols;
+ return;
+ }
+ sal_uLong nCR = static_cast<sal_uLong>(nC) * nR;
+
+ /*
+ TODO:
+ First do it simple without bit masking. A maximum of 8MB could be allocated
+ this way (256 Cols x 32000 Rows). That could be reduced to 2MB by
+ using 2 Bits per entry, but it is faster this way.
+ Another optimization would be to store only used rows/columns in the array, but
+ would mean another iteration of the RangeList indirect access to the array. */
+
+ enum class CellState : sal_uInt8 { Hole, Occupied, Free, Glue };
+ CellState* p;
+ std::unique_ptr<CellState[]> pA(new CellState[ nCR ]);
+ memset( pA.get(), 0, nCR * sizeof(CellState) );
+
+ SCCOL nCol, nCol1, nCol2;
+ SCROW nRow, nRow1, nRow2;
+ for ( size_t i = 0, nRanges = aRangeListRef->size(); i < nRanges; ++i )
+ { // mark selections as used in 2D
+ pR = &(*aRangeListRef)[i];
+ nCol1 = pR->aStart.Col() - nStartCol;
+ nCol2 = pR->aEnd.Col() - nStartCol;
+ nRow1 = pR->aStart.Row() - nStartRow;
+ nRow2 = pR->aEnd.Row() - nStartRow;
+ for ( nCol = nCol1; nCol <= nCol2; nCol++ )
+ {
+ p = pA.get() + static_cast<sal_uLong>(nCol) * nR + nRow1;
+ for ( nRow = nRow1; nRow <= nRow2; nRow++, p++ )
+ *p = CellState::Occupied;
+ }
+ }
+ bool bGlue = true;
+
+ bool bGlueCols = false;
+ for ( nCol = 0; bGlue && nCol < nC; nCol++ )
+ { // iterate columns and try to mark as unused
+ p = pA.get() + static_cast<sal_uLong>(nCol) * nR;
+ for ( nRow = 0; bGlue && nRow < nR; nRow++, p++ )
+ {
+ if ( *p == CellState::Occupied )
+ { // If there's one right in the middle, we can't combine.
+ // If it were at the edge, we could combine, if in this Column
+ // in every set line, one is set.
+ if ( nRow > 0 && nCol > 0 )
+ bGlue = false; // nCol==0 can be DummyUpperLeft
+ else
+ nRow = nR;
+ }
+ else
+ *p = CellState::Free;
+ }
+ if ( bGlue )
+ {
+ p = pA.get() + (((static_cast<sal_uLong>(nCol)+1) * nR) - 1);
+ if (*p == CellState::Free)
+ { // mark column as totally unused
+ *p = CellState::Glue;
+ bGlueCols = true; // one unused column at least
+ }
+ }
+ }
+
+ bool bGlueRows = false;
+ for ( nRow = 0; bGlue && nRow < nR; nRow++ )
+ { // iterate rows and try to mark as unused
+ p = pA.get() + nRow;
+ for ( nCol = 0; bGlue && nCol < nC; nCol++, p+=nR )
+ {
+ if ( *p == CellState::Occupied )
+ {
+ if ( nCol > 0 && nRow > 0 )
+ bGlue = false; // nRow==0 can be DummyUpperLeft
+ else
+ nCol = nC;
+ }
+ else
+ *p = CellState::Free;
+ }
+ if ( bGlue )
+ {
+ p = pA.get() + (((static_cast<sal_uLong>(nC)-1) * nR) + nRow);
+ if (*p == CellState::Free )
+ { // mark row as totally unused
+ *p = CellState::Glue;
+ bGlueRows = true; // one unused row at least
+ }
+ }
+ }
+
+ // If n=1: The upper left corner could be automagically pulled in for labeling
+ p = pA.get() + 1;
+ for ( sal_uLong n = 1; bGlue && n < nCR; n++, p++ )
+ { // An untouched field means we could neither reach it through rows nor columns,
+ // thus we can't combine anything
+ if ( *p == CellState::Hole )
+ bGlue = false;
+ }
+ if ( bGlue )
+ {
+ if ( bGlueCols && bGlueRows )
+ eGlue = ScChartGlue::Both;
+ else if ( bGlueRows )
+ eGlue = ScChartGlue::Rows;
+ else
+ eGlue = ScChartGlue::Cols;
+ if ( pA[0] != CellState::Occupied )
+ bDummyUpperLeft = true;
+ }
+ else
+ {
+ eGlue = ScChartGlue::NONE;
+ }
+}
+
+void ScChartPositioner::CheckColRowHeaders()
+{
+ SCCOL nCol1, nCol2, iCol;
+ SCROW nRow1, nRow2, iRow;
+ SCTAB nTab1, nTab2;
+
+ bool bColStrings = true;
+ bool bRowStrings = true;
+ GlueState();
+ if ( aRangeListRef->size() == 1 )
+ {
+ aRangeListRef->front().GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( nCol1 > nCol2 || nRow1 > nRow2 )
+ bColStrings = bRowStrings = false;
+ else
+ {
+ for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++)
+ {
+ if (lcl_hasValueDataButNoDates( rDocument, iCol, nRow1, nTab1 ))
+ bColStrings = false;
+ }
+ for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++)
+ {
+ if (lcl_hasValueDataButNoDates( rDocument, nCol1, iRow, nTab1 ))
+ bRowStrings = false;
+ }
+ }
+ }
+ else
+ {
+ bool bVert = (eGlue == ScChartGlue::NONE || eGlue == ScChartGlue::Rows);
+ for ( size_t i = 0, nRanges = aRangeListRef->size();
+ (i < nRanges) && (bColStrings || bRowStrings);
+ ++i
+ )
+ {
+ const ScRange & rR = (*aRangeListRef)[i];
+ rR.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ bool bTopRow = (nRow1 == nStartRow);
+ if ( bRowStrings && (bVert || nCol1 == nStartCol) )
+ { // NONE or ROWS: RowStrings in every selection possible
+ // COLS or BOTH: only from first column
+ if ( nCol1 <= nCol2 )
+ for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++)
+ {
+ if (lcl_hasValueDataButNoDates( rDocument, nCol1, iRow, nTab1 ))
+ bRowStrings = false;
+ }
+ }
+ if ( bColStrings && bTopRow )
+ { // ColStrings only from first row
+ if ( nRow1 <= nRow2 )
+ for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++)
+ {
+ if (lcl_hasValueDataButNoDates( rDocument, iCol, nRow1, nTab1 ))
+ bColStrings = false;
+ }
+ }
+ }
+ }
+ bColHeaders = bColStrings;
+ bRowHeaders = bRowStrings;
+}
+
+const ScChartPositionMap* ScChartPositioner::GetPositionMap()
+{
+ CreatePositionMap();
+ return pPositionMap.get();
+}
+
+void ScChartPositioner::CreatePositionMap()
+{
+ if ( eGlue == ScChartGlue::NA && pPositionMap )
+ {
+ pPositionMap.reset();
+ }
+
+ if ( pPositionMap )
+ return ;
+
+ SCSIZE nColAdd = bRowHeaders ? 1 : 0;
+ SCSIZE nRowAdd = bColHeaders ? 1 : 0;
+
+ SCCOL nCol, nCol1, nCol2;
+ SCROW nRow, nRow1, nRow2;
+ SCTAB nTab, nTab1, nTab2;
+
+ // real size (without hidden rows/columns)
+
+ SCSIZE nColCount = 0;
+ SCSIZE nRowCount = 0;
+
+ GlueState();
+
+ const bool bNoGlue = (eGlue == ScChartGlue::NONE);
+ ColumnMap aColMap;
+ SCROW nNoGlueRow = 0;
+ for ( size_t i = 0, nRanges = aRangeListRef->size(); i < nRanges; ++i )
+ {
+ const ScRange & rR = (*aRangeListRef)[i];
+ rR.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ for ( nTab = nTab1; nTab <= nTab2; nTab++ )
+ {
+ // nTab in ColKey to allow to have the same col/row in another table
+ sal_uLong nInsCol = (static_cast<sal_uLong>(nTab) << 16) | (bNoGlue ? 0 :
+ static_cast<sal_uLong>(nCol1));
+ for ( nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol )
+ {
+ RowMap* pCol = &aColMap[nInsCol];
+
+ // in other table a new ColKey already was created,
+ // the rows must be equal to be filled with Dummy
+ sal_uLong nInsRow = (bNoGlue ? nNoGlueRow : nRow1);
+ for ( nRow = nRow1; nRow <= nRow2; nRow++, nInsRow++ )
+ {
+ if ( pCol->find( nInsRow ) == pCol->end() )
+ {
+ pCol->emplace( nInsRow, std::make_unique<ScAddress>( nCol, nRow, nTab ) );
+ }
+ }
+ }
+ }
+ // For NoGlue: associated tables will be rendered as ColGlue
+ nNoGlueRow += nRow2 - nRow1 + 1;
+ }
+
+ // count of data
+ nColCount = static_cast< SCSIZE >( aColMap.size());
+ if ( !aColMap.empty() )
+ {
+ RowMap& rCol = aColMap.begin()->second;
+ if ( bDummyUpperLeft )
+ rCol[ 0 ] = nullptr; // Dummy for labeling
+ nRowCount = static_cast< SCSIZE >( rCol.size());
+ }
+ else
+ nRowCount = 0;
+ if ( nColCount > 0 )
+ nColCount -= nColAdd;
+ if ( nRowCount > 0 )
+ nRowCount -= nRowAdd;
+
+ if ( nColCount==0 || nRowCount==0 )
+ { // create an entry without data
+ RowMap& rCol = aColMap[0];
+ nColCount = 1;
+ rCol[ 0 ] = nullptr;
+ nRowCount = 1;
+ nColAdd = 0;
+ nRowAdd = 0;
+ }
+ else
+ {
+ if ( bNoGlue )
+ { // fill gaps with Dummies, first column is master
+ RowMap& rFirstCol = aColMap.begin()->second;
+
+ for ( const auto& it1 : rFirstCol )
+ {
+ sal_uLong nKey = it1.first;
+ for (ColumnMap::iterator it2 = ++aColMap.begin(); it2 != aColMap.end(); ++it2 )
+ it2->second.emplace( nKey, nullptr ); // no data
+ }
+ }
+ }
+
+ pPositionMap.reset( new ScChartPositionMap( static_cast<SCCOL>(nColCount), static_cast<SCROW>(nRowCount),
+ static_cast<SCCOL>(nColAdd), static_cast<SCROW>(nRowAdd), aColMap ) );
+}
+
+void ScChartPositioner::InvalidateGlue()
+{
+ eGlue = ScChartGlue::NA;
+ pPositionMap.reset();
+}
+
+ScChartPositionMap::ScChartPositionMap( SCCOL nChartCols, SCROW nChartRows,
+ SCCOL nColAdd, SCROW nRowAdd, ColumnMap& rCols ) :
+ ppData( new std::unique_ptr<ScAddress> [ nChartCols * nChartRows ] ),
+ ppColHeader( new std::unique_ptr<ScAddress> [ nChartCols ] ),
+ ppRowHeader( new std::unique_ptr<ScAddress> [ nChartRows ] ),
+ nCount( static_cast<sal_uLong>(nChartCols) * nChartRows ),
+ nColCount( nChartCols ),
+ nRowCount( nChartRows )
+{
+ OSL_ENSURE( nColCount && nRowCount, "ScChartPositionMap without dimension" );
+
+ ColumnMap::iterator pColIter = rCols.begin();
+ RowMap& rCol1 = pColIter->second;
+
+ // row header
+ auto pPos1Iter = rCol1.begin();
+ if ( nRowAdd )
+ ++pPos1Iter;
+ if ( nColAdd )
+ { // independent
+ SCROW nRow = 0;
+ for ( ; nRow < nRowCount && pPos1Iter != rCol1.end(); nRow++ )
+ {
+ ppRowHeader[ nRow ] = std::move(pPos1Iter->second);
+ ++pPos1Iter;
+ }
+ }
+ else
+ { // copy
+ SCROW nRow = 0;
+ for ( ; nRow < nRowCount && pPos1Iter != rCol1.end(); nRow++ )
+ {
+ if (pPos1Iter->second)
+ ppRowHeader[ nRow ].reset(new ScAddress( *pPos1Iter->second ));
+ ++pPos1Iter;
+ }
+ }
+ if ( nColAdd )
+ {
+ ++pColIter;
+ }
+
+ // data column by column and column-header
+ sal_uLong nIndex = 0;
+ for ( SCCOL nCol = 0; nCol < nColCount; nCol++ )
+ {
+ if ( pColIter != rCols.end() )
+ {
+ RowMap& rCol2 = pColIter->second;
+ RowMap::iterator pPosIter = rCol2.begin();
+ if ( pPosIter != rCol2.end() )
+ {
+ if ( nRowAdd )
+ {
+ ppColHeader[ nCol ] = std::move(pPosIter->second); // independent
+ ++pPosIter;
+ }
+ else if ( pPosIter->second )
+ ppColHeader[ nCol ].reset( new ScAddress( *pPosIter->second ) );
+ }
+
+ SCROW nRow = 0;
+ for ( ; nRow < nRowCount && pPosIter != rCol2.end(); nRow++, nIndex++ )
+ {
+ ppData[ nIndex ] = std::move(pPosIter->second);
+ ++pPosIter;
+ }
+
+ ++pColIter;
+ }
+ }
+}
+
+ScChartPositionMap::~ScChartPositionMap()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/chgtrack.cxx b/sc/source/core/tool/chgtrack.cxx
new file mode 100644
index 0000000000..53fe660f10
--- /dev/null
+++ b/sc/source/core/tool/chgtrack.cxx
@@ -0,0 +1,4682 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <chgtrack.hxx>
+#include <compiler.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <dociter.hxx>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <patattr.hxx>
+#include <hints.hxx>
+#include <markdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <editutil.hxx>
+#include <tokenarray.hxx>
+#include <refupdatecontext.hxx>
+#include <refupdat.hxx>
+
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <sfx2/objsh.hxx>
+#include <unotools/useroptions.hxx>
+#include <unotools/datetime.hxx>
+#include <tools/json_writer.hxx>
+#include <algorithm>
+#include <memory>
+#include <strings.hrc>
+#include <utility>
+
+ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange )
+ :
+ aBigRange( rRange ),
+ aDateTime( DateTime::SYSTEM ),
+ pNext( nullptr ),
+ pPrev( nullptr ),
+ pLinkAny( nullptr ),
+ pLinkDeletedIn( nullptr ),
+ pLinkDeleted( nullptr ),
+ pLinkDependent( nullptr ),
+ nAction( 0 ),
+ nRejectAction( 0 ),
+ eType( eTypeP ),
+ eState( SC_CAS_VIRGIN )
+{
+ aDateTime.ConvertToUTC();
+}
+
+ScChangeAction::ScChangeAction(
+ ScChangeActionType eTypeP, ScBigRange aRange,
+ const sal_uLong nTempAction, const sal_uLong nTempRejectAction,
+ const ScChangeActionState eTempState, const DateTime& aTempDateTime,
+ OUString aTempUser, OUString aTempComment) :
+ aBigRange(std::move( aRange )),
+ aDateTime( aTempDateTime ),
+ aUser(std::move( aTempUser )),
+ aComment(std::move( aTempComment )),
+ pNext( nullptr ),
+ pPrev( nullptr ),
+ pLinkAny( nullptr ),
+ pLinkDeletedIn( nullptr ),
+ pLinkDeleted( nullptr ),
+ pLinkDependent( nullptr ),
+ nAction( nTempAction ),
+ nRejectAction( nTempRejectAction ),
+ eType( eTypeP ),
+ eState( eTempState )
+{
+}
+
+ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, ScBigRange aRange,
+ const sal_uLong nTempAction)
+ :
+ aBigRange(std::move( aRange )),
+ aDateTime( DateTime::SYSTEM ),
+ pNext( nullptr ),
+ pPrev( nullptr ),
+ pLinkAny( nullptr ),
+ pLinkDeletedIn( nullptr ),
+ pLinkDeleted( nullptr ),
+ pLinkDependent( nullptr ),
+ nAction( nTempAction ),
+ nRejectAction( 0 ),
+ eType( eTypeP ),
+ eState( SC_CAS_VIRGIN )
+{
+ aDateTime.ConvertToUTC();
+}
+
+ScChangeAction::~ScChangeAction()
+{
+ RemoveAllLinks();
+}
+
+bool ScChangeAction::IsInsertType() const
+{
+ return eType == SC_CAT_INSERT_COLS || eType == SC_CAT_INSERT_ROWS || eType == SC_CAT_INSERT_TABS;
+}
+
+bool ScChangeAction::IsDeleteType() const
+{
+ return eType == SC_CAT_DELETE_COLS || eType == SC_CAT_DELETE_ROWS || eType == SC_CAT_DELETE_TABS;
+}
+
+bool ScChangeAction::IsVirgin() const
+{
+ return eState == SC_CAS_VIRGIN;
+}
+
+bool ScChangeAction::IsAccepted() const
+{
+ return eState == SC_CAS_ACCEPTED;
+}
+
+bool ScChangeAction::IsRejected() const
+{
+ return eState == SC_CAS_REJECTED;
+}
+
+bool ScChangeAction::IsRejecting() const
+{
+ return nRejectAction != 0;
+}
+
+bool ScChangeAction::IsVisible() const
+{
+ // sequence order of execution is significant!
+ if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() )
+ return false;
+ if ( GetType() == SC_CAT_CONTENT )
+ return static_cast<const ScChangeActionContent*>(this)->IsTopContent();
+ return true;
+}
+
+bool ScChangeAction::IsTouchable() const
+{
+ // sequence order of execution is significant!
+ if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() )
+ return false;
+ // content may reject and be touchable if on top
+ if ( GetType() == SC_CAT_CONTENT )
+ return static_cast<const ScChangeActionContent*>(this)->IsTopContent();
+ if ( IsRejecting() )
+ return false;
+ return true;
+}
+
+bool ScChangeAction::IsClickable() const
+{
+ // sequence order of execution is significant!
+ if ( !IsVirgin() )
+ return false;
+ if ( IsDeletedIn() )
+ return false;
+ if ( GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContentCellType eCCT =
+ ScChangeActionContent::GetContentCellType(
+ static_cast<const ScChangeActionContent*>(this)->GetNewCell() );
+ if ( eCCT == SC_CACCT_MATREF )
+ return false;
+ if ( eCCT == SC_CACCT_MATORG )
+ { // no Accept-Select if one of the references is in a deleted col/row
+ const ScChangeActionLinkEntry* pL =
+ static_cast<const ScChangeActionContent*>(this)->GetFirstDependentEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p && p->IsDeletedIn() )
+ return false;
+ pL = pL->GetNext();
+ }
+ }
+ return true; // for Select() a content doesn't have to be touchable
+ }
+ return IsTouchable(); // Accept()/Reject() only on touchables
+}
+
+bool ScChangeAction::IsRejectable() const
+{
+ // sequence order of execution is significant!
+ if ( !IsClickable() )
+ return false;
+ if ( GetType() == SC_CAT_CONTENT )
+ {
+ if ( static_cast<const ScChangeActionContent*>(this)->IsOldMatrixReference() )
+ return false;
+ ScChangeActionContent* pNextContent =
+ static_cast<const ScChangeActionContent*>(this)->GetNextContent();
+ if ( pNextContent == nullptr )
+ return true; // *this is TopContent
+ return pNextContent->IsRejected(); // *this is next rejectable
+ }
+ return IsTouchable();
+}
+
+bool ScChangeAction::IsInternalRejectable() const
+{
+ // sequence order of execution is significant!
+ if ( !IsVirgin() )
+ return false;
+ if ( IsDeletedIn() )
+ return false;
+ if ( GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContent* pNextContent =
+ static_cast<const ScChangeActionContent*>(this)->GetNextContent();
+ if ( pNextContent == nullptr )
+ return true; // *this is TopContent
+ return pNextContent->IsRejected(); // *this is next rejectable
+ }
+ return IsTouchable();
+}
+
+bool ScChangeAction::IsDialogRoot() const
+{
+ return IsInternalRejectable(); // only rejectables in root
+}
+
+bool ScChangeAction::IsDialogParent() const
+{
+ // sequence order of execution is significant!
+ if ( GetType() == SC_CAT_CONTENT )
+ {
+ if ( !IsDialogRoot() )
+ return false;
+ if ( static_cast<const ScChangeActionContent*>(this)->IsMatrixOrigin() && HasDependent() )
+ return true;
+ ScChangeActionContent* pPrevContent =
+ static_cast<const ScChangeActionContent*>(this)->GetPrevContent();
+ return pPrevContent && pPrevContent->IsVirgin();
+ }
+ if ( HasDependent() )
+ return IsDeleteType() || !IsDeletedIn();
+ if ( HasDeleted() )
+ {
+ if ( IsDeleteType() )
+ {
+ if ( IsDialogRoot() )
+ return true;
+ ScChangeActionLinkEntry* pL = pLinkDeleted;
+ while ( pL )
+ {
+ ScChangeAction* p = pL->GetAction();
+ if ( p && p->GetType() != eType )
+ return true;
+ pL = pL->GetNext();
+ }
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+bool ScChangeAction::IsMasterDelete() const
+{
+ if ( !IsDeleteType() )
+ return false;
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(this);
+ return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable());
+}
+
+void ScChangeAction::RemoveAllLinks()
+{
+ while (pLinkAny)
+ {
+ // coverity[use_after_free] - Moves up by itself
+ delete pLinkAny;
+ }
+
+ RemoveAllDeletedIn();
+
+ while (pLinkDeleted)
+ {
+ // coverity[use_after_free] - Moves up by itself
+ delete pLinkDeleted;
+ }
+
+ RemoveAllDependent();
+}
+
+bool ScChangeAction::RemoveDeletedIn( const ScChangeAction* p )
+{
+ bool bRemoved = false;
+ ScChangeActionLinkEntry* pL = GetDeletedIn();
+ while ( pL )
+ {
+ ScChangeActionLinkEntry* pNextLink = pL->GetNext();
+ if ( pL->GetAction() == p )
+ {
+ delete pL;
+ bRemoved = true;
+ }
+ pL = pNextLink;
+ }
+ return bRemoved;
+}
+
+bool ScChangeAction::IsDeletedIn() const
+{
+ return GetDeletedIn() != nullptr;
+}
+
+bool ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const
+{
+ ScChangeActionLinkEntry* pL = GetDeletedIn();
+ while ( pL )
+ {
+ if ( pL->GetAction() == p )
+ return true;
+ pL = pL->GetNext();
+ }
+ return false;
+}
+
+void ScChangeAction::RemoveAllDeletedIn()
+{
+ //TODO: Not from TopContent, but really this one
+ while (pLinkDeletedIn)
+ {
+ // coverity[use_after_free] - Moves up by itself
+ delete pLinkDeletedIn;
+ }
+}
+
+bool ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const
+{
+ ScChangeActionLinkEntry* pL = GetDeletedIn();
+ if ( pL )
+ {
+ // InsertType for MergePrepare/MergeOwn
+ ScChangeActionType eInsType;
+ switch ( eDelType )
+ {
+ case SC_CAT_DELETE_COLS :
+ eInsType = SC_CAT_INSERT_COLS;
+ break;
+ case SC_CAT_DELETE_ROWS :
+ eInsType = SC_CAT_INSERT_ROWS;
+ break;
+ case SC_CAT_DELETE_TABS :
+ eInsType = SC_CAT_INSERT_TABS;
+ break;
+ default:
+ eInsType = SC_CAT_NONE;
+ }
+ while ( pL )
+ {
+ ScChangeAction* p = pL->GetAction();
+ if ( p != nullptr && (p->GetType() == eDelType || p->GetType() == eInsType) )
+ return true;
+ pL = pL->GetNext();
+ }
+ }
+ return false;
+}
+
+bool ScChangeAction::HasDependent() const
+{
+ return pLinkDependent != nullptr;
+}
+
+bool ScChangeAction::HasDeleted() const
+{
+ return pLinkDeleted != nullptr;
+}
+
+void ScChangeAction::SetDeletedIn( ScChangeAction* p )
+{
+ ScChangeActionLinkEntry* pLink1 = new ScChangeActionLinkEntry( GetDeletedInAddress(), p );
+ ScChangeActionLinkEntry* pLink2;
+ if ( GetType() == SC_CAT_CONTENT )
+ pLink2 = p->AddDeleted( static_cast<ScChangeActionContent*>(this)->GetTopContent() );
+ else
+ pLink2 = p->AddDeleted( this );
+ pLink1->SetLink( pLink2 );
+}
+
+void ScChangeAction::RemoveAllDependent()
+{
+ while (pLinkDependent)
+ {
+ // coverity[use_after_free] - Moves up by itself
+ delete pLinkDependent;
+ }
+}
+
+DateTime ScChangeAction::GetDateTime() const
+{
+ DateTime aDT( aDateTime );
+ aDT.ConvertToLocalTime();
+ return aDT;
+}
+
+void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */,
+ UpdateRefMode eMode, const ScBigRange& rRange,
+ sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
+{
+ ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
+}
+
+OUString ScChangeAction::GetDescription(
+ ScDocument& /* rDoc */, bool /* bSplitRange */, bool bWarning ) const
+{
+ if (!IsRejecting() || !bWarning)
+ return OUString();
+
+ // Add comment if rejection may have resulted in references
+ // not properly restored in formulas. See specification at
+ // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw
+
+ if (GetType() == SC_CAT_MOVE)
+ {
+ return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " ";
+ }
+
+ if (IsInsertType())
+ {
+ return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " ";
+ }
+
+ const ScChangeTrack* pCT = GetChangeTrack();
+ if (!pCT)
+ return OUString();
+
+ ScChangeAction* pReject = pCT->GetActionOrGenerated(GetRejectAction());
+
+ if (!pReject)
+ return OUString();
+
+ if (pReject->GetType() == SC_CAT_MOVE)
+ {
+ return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " ";
+ }
+
+ if (pReject->IsDeleteType())
+ {
+ return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " ";
+ }
+
+ if (!pReject->HasDependent())
+ return OUString();
+
+ ScChangeActionMap aMap;
+ pCT->GetDependents( pReject, aMap, false, true );
+ ScChangeActionMap::iterator itChangeAction = std::find_if(aMap.begin(), aMap.end(),
+ [&pReject](const ScChangeActionMap::value_type& rEntry) {
+ return rEntry.second->GetType() == SC_CAT_MOVE || pReject->IsDeleteType(); });
+ if (itChangeAction == aMap.end())
+ return OUString();
+
+ if( itChangeAction->second->GetType() == SC_CAT_MOVE)
+ return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " ";
+ else
+ return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " ";
+}
+
+OUString ScChangeAction::GetRefString(
+ const ScBigRange& rRange, const ScDocument& rDoc, bool bFlag3D ) const
+{
+ OUStringBuffer aBuf;
+ ScRefFlags nFlags = ( rRange.IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
+ if ( nFlags == ScRefFlags::ZERO )
+ aBuf.append(ScCompiler::GetNativeSymbol(ocErrRef));
+ else
+ {
+ ScRange aTmpRange( rRange.MakeRange( rDoc ) );
+ switch ( GetType() )
+ {
+ case SC_CAT_INSERT_COLS :
+ case SC_CAT_DELETE_COLS :
+ if ( bFlag3D )
+ {
+ OUString aTmp;
+ rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
+ aBuf.append(aTmp + ".");
+ }
+ aBuf.append(ScColToAlpha(aTmpRange.aStart.Col())
+ + ":" + ScColToAlpha(aTmpRange.aEnd.Col()));
+ break;
+ case SC_CAT_INSERT_ROWS :
+ case SC_CAT_DELETE_ROWS :
+ if ( bFlag3D )
+ {
+ OUString aTmp;
+ rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
+ aBuf.append(aTmp + ".");
+ }
+ aBuf.append(OUString::number(static_cast<sal_Int64>(aTmpRange.aStart.Row()+1))
+ + ":" + OUString::number(static_cast<sal_Int64>(aTmpRange.aEnd.Row()+1)));
+ break;
+ default:
+ {
+ if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS )
+ nFlags |= ScRefFlags::TAB_3D;
+
+ aBuf.append(aTmpRange.Format(rDoc, nFlags, rDoc.GetAddressConvention()));
+ }
+ }
+ if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() )
+ {
+ aBuf.insert(0, '(');
+ aBuf.append(')');
+ }
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void ScChangeAction::SetUser( const OUString& r )
+{
+ aUser = r;
+}
+
+void ScChangeAction::SetComment( const OUString& rStr )
+{
+ aComment = rStr;
+}
+
+OUString ScChangeAction::GetRefString( ScDocument& rDoc, bool bFlag3D ) const
+{
+ return GetRefString( GetBigRange(), rDoc, bFlag3D );
+}
+
+void ScChangeAction::Accept()
+{
+ if ( IsVirgin() )
+ {
+ SetState( SC_CAS_ACCEPTED );
+ DeleteCellEntries();
+ }
+}
+
+void ScChangeAction::SetRejected()
+{
+ if ( IsVirgin() )
+ {
+ SetState( SC_CAS_REJECTED );
+ RemoveAllLinks();
+ DeleteCellEntries();
+ }
+}
+
+void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack,
+ SCCOL nDx, SCROW nDy )
+{
+ // Construct list of Contents
+ std::vector<ScChangeActionContent*> aContentsList;
+ for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
+ {
+ ScChangeAction* p = pL->GetAction();
+ if ( p && p->GetType() == SC_CAT_CONTENT )
+ {
+ aContentsList.push_back(static_cast<ScChangeActionContent*>(p) );
+ }
+ }
+ SetState( SC_CAS_REJECTED ); // Before UpdateReference for Move
+ pTrack->UpdateReference( this, true ); // Free LinkDeleted
+ OSL_ENSURE( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" );
+
+ // Work through list of Contents and delete
+ ScDocument& rDoc = pTrack->GetDocument();
+ for (ScChangeActionContent* pContent : aContentsList)
+ {
+ if ( !pContent->IsDeletedIn() &&
+ pContent->GetBigRange().aStart.IsValid( rDoc ) )
+ pContent->PutNewValueToDoc( &rDoc, nDx, nDy );
+ }
+ DeleteCellEntries(); // Remove generated ones
+}
+
+void ScChangeAction::SetDeletedInThis( sal_uLong nActionNumber,
+ const ScChangeTrack* pTrack )
+{
+ if ( nActionNumber )
+ {
+ ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
+ OSL_ENSURE( pAct, "ScChangeAction::SetDeletedInThis: missing Action" );
+ if ( pAct )
+ pAct->SetDeletedIn( this );
+ }
+}
+
+void ScChangeAction::AddDependent( sal_uLong nActionNumber,
+ const ScChangeTrack* pTrack )
+{
+ if ( nActionNumber )
+ {
+ ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
+ OSL_ENSURE( pAct, "ScChangeAction::AddDependent: missing Action" );
+ if ( pAct )
+ {
+ ScChangeActionLinkEntry* pLink = AddDependent( pAct );
+ pAct->AddLink( this, pLink );
+ }
+ }
+}
+
+// ScChangeActionIns
+ScChangeActionIns::ScChangeActionIns( const ScDocument* pDoc, const ScRange& rRange, bool bEndOfList ) :
+ ScChangeAction(SC_CAT_NONE, rRange),
+ mbEndOfList(bEndOfList)
+{
+ if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
+ {
+ aBigRange.aStart.SetCol( ScBigRange::nRangeMin );
+ aBigRange.aEnd.SetCol( ScBigRange::nRangeMax );
+ if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
+ {
+ SetType( SC_CAT_INSERT_TABS );
+ aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
+ aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
+ }
+ else
+ SetType( SC_CAT_INSERT_ROWS );
+ }
+ else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
+ {
+ SetType( SC_CAT_INSERT_COLS );
+ aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
+ aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeActionIns: Block not supported!" );
+ }
+}
+
+ScChangeActionIns::ScChangeActionIns(
+ const sal_uLong nActionNumber, const ScChangeActionState eStateP,
+ const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
+ const OUString& aUserP, const DateTime& aDateTimeP,
+ const OUString& sComment, const ScChangeActionType eTypeP,
+ bool bEndOfList ) :
+ ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
+ mbEndOfList(bEndOfList)
+{
+}
+
+ScChangeActionIns::~ScChangeActionIns()
+{
+}
+
+OUString ScChangeActionIns::GetDescription( ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
+{
+ OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
+
+ TranslateId pWhatId;
+ switch ( GetType() )
+ {
+ case SC_CAT_INSERT_COLS :
+ pWhatId = STR_COLUMN;
+ break;
+ case SC_CAT_INSERT_ROWS :
+ pWhatId = STR_ROW;
+ break;
+ default:
+ pWhatId = STR_AREA;
+ }
+
+ OUString aRsc = ScResId(STR_CHANGED_INSERT);
+ sal_Int32 nPos = aRsc.indexOf("#1");
+ if (nPos < 0)
+ return str;
+
+ // Construct a range string to replace '#1' first.
+ OUString aRangeStr = ScResId(pWhatId) +
+ " " +
+ GetRefString(GetBigRange(), rDoc);
+
+ aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the range string.
+
+ return str + aRsc;
+}
+
+bool ScChangeActionIns::IsEndOfList() const
+{
+ return mbEndOfList;
+}
+
+bool ScChangeActionIns::Reject( ScDocument& rDoc )
+{
+ if ( !aBigRange.IsValid( rDoc ) )
+ return false;
+
+ ScRange aRange( aBigRange.MakeRange( rDoc ) );
+ if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
+ aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
+ return false;
+
+ switch ( GetType() )
+ {
+ case SC_CAT_INSERT_COLS :
+ rDoc.DeleteCol( aRange );
+ break;
+ case SC_CAT_INSERT_ROWS :
+ rDoc.DeleteRow( aRange );
+ break;
+ case SC_CAT_INSERT_TABS :
+ rDoc.DeleteTab( aRange.aStart.Tab() );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ SetState( SC_CAS_REJECTED );
+ RemoveAllLinks();
+ return true;
+}
+
+// ScChangeActionDel
+ScChangeActionDel::ScChangeActionDel( const ScDocument* pDoc, const ScRange& rRange,
+ SCCOL nDxP, SCROW nDyP, ScChangeTrack* pTrackP )
+ :
+ ScChangeAction( SC_CAT_NONE, rRange ),
+ pTrack( pTrackP ),
+ pCutOff( nullptr ),
+ nCutOff( 0 ),
+ pLinkMove( nullptr ),
+ nDx( nDxP ),
+ nDy( nDyP )
+{
+ if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
+ {
+ aBigRange.aStart.SetCol( ScBigRange::nRangeMin );
+ aBigRange.aEnd.SetCol( ScBigRange::nRangeMax );
+ if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
+ {
+ SetType( SC_CAT_DELETE_TABS );
+ aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
+ aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
+ }
+ else
+ SetType( SC_CAT_DELETE_ROWS );
+ }
+ else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
+ {
+ SetType( SC_CAT_DELETE_COLS );
+ aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
+ aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeActionDel: Block not supported!" );
+ }
+}
+
+ScChangeActionDel::ScChangeActionDel(
+ const sal_uLong nActionNumber, const ScChangeActionState eStateP,
+ const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
+ const OUString& aUserP, const DateTime& aDateTimeP, const OUString &sComment,
+ const ScChangeActionType eTypeP, const SCCOLROW nD, ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
+ ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
+ pTrack( pTrackP ),
+ pCutOff( nullptr ),
+ nCutOff( 0 ),
+ pLinkMove( nullptr ),
+ nDx( 0 ),
+ nDy( 0 )
+{
+ if (eType == SC_CAT_DELETE_COLS)
+ nDx = static_cast<SCCOL>(nD);
+ else if (eType == SC_CAT_DELETE_ROWS)
+ nDy = static_cast<SCROW>(nD);
+}
+
+ScChangeActionDel::~ScChangeActionDel()
+{
+ DeleteCellEntries();
+ while (pLinkMove)
+ {
+ // coverity[use_after_free] - Moves up by itself
+ delete pLinkMove;
+ }
+}
+
+void ScChangeActionDel::AddContent( ScChangeActionContent* pContent )
+{
+ mvCells.push_back(pContent);
+}
+
+void ScChangeActionDel::DeleteCellEntries()
+{
+ pTrack->DeleteCellEntries( mvCells, this );
+}
+
+bool ScChangeActionDel::IsBaseDelete() const
+{
+ return !GetDx() && !GetDy();
+}
+
+bool ScChangeActionDel::IsTopDelete() const
+{
+ const ScChangeAction* p = GetNext();
+ if ( !p || p->GetType() != GetType() )
+ return true;
+ return static_cast<const ScChangeActionDel*>(p)->IsBaseDelete();
+}
+
+bool ScChangeActionDel::IsMultiDelete() const
+{
+ if ( GetDx() || GetDy() )
+ return true;
+ const ScChangeAction* p = GetNext();
+ if ( !p || p->GetType() != GetType() )
+ return false;
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(p);
+ return (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) &&
+ pDel->GetBigRange() == aBigRange;
+}
+
+bool ScChangeActionDel::IsTabDeleteCol() const
+{
+ if ( GetType() != SC_CAT_DELETE_COLS )
+ return false;
+ const ScChangeAction* p = this;
+ while ( p && p->GetType() == SC_CAT_DELETE_COLS &&
+ !static_cast<const ScChangeActionDel*>(p)->IsTopDelete() )
+ p = p->GetNext();
+ return p && p->GetType() == SC_CAT_DELETE_TABS;
+}
+
+ScChangeActionDelMoveEntry* ScChangeActionDel::AddCutOffMove(
+ ScChangeActionMove* pMove, short nFrom, short nTo )
+{
+ return new ScChangeActionDelMoveEntry(&pLinkMove, pMove, nFrom, nTo);
+}
+
+void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */,
+ UpdateRefMode eMode, const ScBigRange& rRange,
+ sal_Int32 nDxP, sal_Int32 nDyP, sal_Int32 nDz )
+{
+ ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() );
+
+ if ( !IsDeletedIn() )
+ return ;
+
+ // Correct in the ones who slipped through
+ for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
+ {
+ ScChangeAction* p = pL->GetAction();
+ if ( p && p->GetType() == SC_CAT_CONTENT &&
+ !GetBigRange().Contains( p->GetBigRange() ) )
+ {
+ switch ( GetType() )
+ {
+ case SC_CAT_DELETE_COLS :
+ p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() );
+ p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() );
+ break;
+ case SC_CAT_DELETE_ROWS :
+ p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() );
+ p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() );
+ break;
+ case SC_CAT_DELETE_TABS :
+ p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() );
+ p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+}
+
+ScBigRange ScChangeActionDel::GetOverAllRange() const
+{
+ ScBigRange aTmpRange( GetBigRange() );
+ aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
+ aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
+ return aTmpRange;
+}
+
+OUString ScChangeActionDel::GetDescription( ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
+{
+ OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
+
+ TranslateId pWhatId;
+ switch ( GetType() )
+ {
+ case SC_CAT_DELETE_COLS :
+ pWhatId = STR_COLUMN;
+ break;
+ case SC_CAT_DELETE_ROWS :
+ pWhatId = STR_ROW;
+ break;
+ default:
+ pWhatId = STR_AREA;
+ }
+
+ ScBigRange aTmpRange( GetBigRange() );
+ if ( !IsRejected() )
+ {
+ if ( bSplitRange )
+ {
+ aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() );
+ aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() );
+ }
+ aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
+ aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
+ }
+
+ OUString aRsc = ScResId(STR_CHANGED_DELETE);
+ sal_Int32 nPos = aRsc.indexOf("#1");
+ if (nPos < 0)
+ return str;
+
+ // Build a string to replace with.
+ OUString aRangeStr = ScResId(pWhatId) + " " +
+ GetRefString(aTmpRange, rDoc);
+ aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the string.
+
+ return str + aRsc; // append to the original.
+}
+
+bool ScChangeActionDel::Reject( ScDocument& rDoc )
+{
+ if ( !aBigRange.IsValid( rDoc ) && GetType() != SC_CAT_DELETE_TABS )
+ return false;
+
+ if ( IsTopDelete() )
+ { // Restore whole section in one go
+ bool bOk = true;
+ ScBigRange aTmpRange( GetOverAllRange() );
+ if ( !aTmpRange.IsValid( rDoc ) )
+ {
+ if ( GetType() == SC_CAT_DELETE_TABS )
+ { // Do we attach a Tab?
+ if ( aTmpRange.aStart.Tab() > rDoc.GetMaxTableNumber() )
+ bOk = false;
+ }
+ else
+ bOk = false;
+ }
+ if ( bOk )
+ {
+ ScRange aRange( aTmpRange.MakeRange( rDoc ) );
+ // InDelete... for formula UpdateReference in Document
+ pTrack->SetInDeleteRange( aRange );
+ pTrack->SetInDeleteTop( true );
+ pTrack->SetInDeleteUndo( true );
+ pTrack->SetInDelete( true );
+ switch ( GetType() )
+ {
+ case SC_CAT_DELETE_COLS :
+ if ( aRange.aStart.Col() != 0 || aRange.aEnd.Col() != rDoc.MaxCol() )
+ { // Only if not TabDelete
+ bOk = rDoc.CanInsertCol( aRange ) && rDoc.InsertCol( aRange );
+ }
+ break;
+ case SC_CAT_DELETE_ROWS :
+ bOk = rDoc.CanInsertRow( aRange ) && rDoc.InsertRow( aRange );
+ break;
+ case SC_CAT_DELETE_TABS :
+ {
+ //TODO: Remember table names?
+ OUString aName;
+ rDoc.CreateValidTabName( aName );
+ bOk = rDoc.ValidNewTabName( aName ) && rDoc.InsertTab( aRange.aStart.Tab(), aName );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ pTrack->SetInDelete( false );
+ pTrack->SetInDeleteUndo( false );
+ }
+ if ( !bOk )
+ {
+ pTrack->SetInDeleteTop( false );
+ return false;
+ }
+ // Keep InDeleteTop for UpdateReference Undo
+ }
+
+ // Sets rejected and calls UpdateReference-Undo and DeleteCellEntries
+ RejectRestoreContents( pTrack, GetDx(), GetDy() );
+
+ pTrack->SetInDeleteTop( false );
+ RemoveAllLinks();
+ return true;
+}
+
+void ScChangeActionDel::UndoCutOffMoves()
+{ // Restore cut off Moves; delete Entries/Links
+ while ( pLinkMove )
+ {
+ // coverity[deref_arg] - the call on delete pLinkMove at the block end Moves a new entry into pLinkMode by itself
+ ScChangeActionMove* pMove = pLinkMove->GetMove();
+ short nFrom = pLinkMove->GetCutOffFrom();
+ short nTo = pLinkMove->GetCutOffTo();
+ switch ( GetType() )
+ {
+ case SC_CAT_DELETE_COLS :
+ if ( nFrom > 0 )
+ pMove->GetFromRange().aStart.IncCol( -nFrom );
+ else if ( nFrom < 0 )
+ pMove->GetFromRange().aEnd.IncCol( -nFrom );
+ if ( nTo > 0 )
+ pMove->GetBigRange().aStart.IncCol( -nTo );
+ else if ( nTo < 0 )
+ pMove->GetBigRange().aEnd.IncCol( -nTo );
+ break;
+ case SC_CAT_DELETE_ROWS :
+ if ( nFrom > 0 )
+ pMove->GetFromRange().aStart.IncRow( -nFrom );
+ else if ( nFrom < 0 )
+ pMove->GetFromRange().aEnd.IncRow( -nFrom );
+ if ( nTo > 0 )
+ pMove->GetBigRange().aStart.IncRow( -nTo );
+ else if ( nTo < 0 )
+ pMove->GetBigRange().aEnd.IncRow( -nTo );
+ break;
+ case SC_CAT_DELETE_TABS :
+ if ( nFrom > 0 )
+ pMove->GetFromRange().aStart.IncTab( -nFrom );
+ else if ( nFrom < 0 )
+ pMove->GetFromRange().aEnd.IncTab( -nFrom );
+ if ( nTo > 0 )
+ pMove->GetBigRange().aStart.IncTab( -nTo );
+ else if ( nTo < 0 )
+ pMove->GetBigRange().aEnd.IncTab( -nTo );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ delete pLinkMove; // Moves up by itself
+ }
+}
+
+void ScChangeActionDel::UndoCutOffInsert()
+{ //Restore cut off Insert
+ if ( !pCutOff )
+ return;
+
+ switch ( pCutOff->GetType() )
+ {
+ case SC_CAT_INSERT_COLS :
+ if ( nCutOff < 0 )
+ pCutOff->GetBigRange().aEnd.IncCol( -nCutOff );
+ else
+ pCutOff->GetBigRange().aStart.IncCol( -nCutOff );
+ break;
+ case SC_CAT_INSERT_ROWS :
+ if ( nCutOff < 0 )
+ pCutOff->GetBigRange().aEnd.IncRow( -nCutOff );
+ else
+ pCutOff->GetBigRange().aStart.IncRow( -nCutOff );
+ break;
+ case SC_CAT_INSERT_TABS :
+ if ( nCutOff < 0 )
+ pCutOff->GetBigRange().aEnd.IncTab( -nCutOff );
+ else
+ pCutOff->GetBigRange().aStart.IncTab( -nCutOff );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ SetCutOffInsert( nullptr, 0 );
+}
+
+// ScChangeActionMove
+ScChangeActionMove::ScChangeActionMove(
+ const sal_uLong nActionNumber, const ScChangeActionState eStateP,
+ const sal_uLong nRejectingNumber, const ScBigRange& aToBigRange,
+ const OUString& aUserP, const DateTime& aDateTimeP,
+ const OUString &sComment, ScBigRange aFromBigRange,
+ ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
+ ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
+ aFromRange(std::move(aFromBigRange)),
+ pTrack( pTrackP ),
+ nStartLastCut(0),
+ nEndLastCut(0)
+{
+}
+
+ScChangeActionMove::~ScChangeActionMove()
+{
+ DeleteCellEntries();
+}
+
+void ScChangeActionMove::AddContent( ScChangeActionContent* pContent )
+{
+ mvCells.push_back(pContent);
+}
+
+void ScChangeActionMove::DeleteCellEntries()
+{
+ pTrack->DeleteCellEntries( mvCells, this );
+}
+
+void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */,
+ UpdateRefMode eMode, const ScBigRange& rRange,
+ sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
+{
+ ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange );
+ ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
+}
+
+void ScChangeActionMove::GetDelta( sal_Int32& nDx, sal_Int32& nDy, sal_Int32& nDz ) const
+{
+ const ScBigAddress& rToPos = GetBigRange().aStart;
+ const ScBigAddress& rFromPos = GetFromRange().aStart;
+ nDx = rToPos.Col() - rFromPos.Col();
+ nDy = rToPos.Row() - rFromPos.Row();
+ nDz = rToPos.Tab() - rFromPos.Tab();
+}
+
+OUString ScChangeActionMove::GetDescription(
+ ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
+{
+ OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
+
+ bool bFlag3D = GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab();
+
+ OUString aRsc = ScResId(STR_CHANGED_MOVE);
+
+ OUString aTmpStr = ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D);
+ sal_Int32 nPos = aRsc.indexOf("#1");
+ if (nPos >= 0)
+ {
+ aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
+ nPos += aTmpStr.getLength();
+ }
+
+ aTmpStr = ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
+ nPos = nPos >= 0 ? aRsc.indexOf("#2", nPos) : -1;
+ if (nPos >= 0)
+ {
+ aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
+ }
+
+ return str + aRsc; // append to the original string.
+}
+
+OUString ScChangeActionMove::GetRefString( ScDocument& rDoc, bool bFlag3D ) const
+{
+ if ( !bFlag3D )
+ bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
+
+ return ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D)
+ + ", "
+ + ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
+}
+
+bool ScChangeActionMove::Reject( ScDocument& rDoc )
+{
+ if ( !(aBigRange.IsValid( rDoc ) && aFromRange.IsValid( rDoc )) )
+ return false;
+
+ ScRange aToRange( aBigRange.MakeRange( rDoc ) );
+ ScRange aFrmRange( aFromRange.MakeRange( rDoc ) );
+
+ bool bOk = rDoc.IsBlockEditable( aToRange.aStart.Tab(),
+ aToRange.aStart.Col(), aToRange.aStart.Row(),
+ aToRange.aEnd.Col(), aToRange.aEnd.Row() );
+ if ( bOk )
+ bOk = rDoc.IsBlockEditable( aFrmRange.aStart.Tab(),
+ aFrmRange.aStart.Col(), aFrmRange.aStart.Row(),
+ aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() );
+ if ( !bOk )
+ return false;
+
+ pTrack->LookUpContents( aToRange, &rDoc, 0, 0, 0 ); // Contents to be moved
+
+ rDoc.DeleteAreaTab( aToRange, InsertDeleteFlags::ALL );
+ rDoc.DeleteAreaTab( aFrmRange, InsertDeleteFlags::ALL );
+ // Adjust formula in the Document
+ sc::RefUpdateContext aCxt(rDoc);
+ aCxt.meMode = URM_MOVE;
+ aCxt.maRange = aFrmRange;
+ aCxt.mnColDelta = aFrmRange.aStart.Col() - aToRange.aStart.Col();
+ aCxt.mnRowDelta = aFrmRange.aStart.Row() - aToRange.aStart.Row();
+ aCxt.mnTabDelta = aFrmRange.aStart.Tab() - aToRange.aStart.Tab();
+ rDoc.UpdateReference(aCxt);
+
+ // Free LinkDependent, set succeeding UpdateReference Undo
+ // ToRange->FromRange Dependents
+ RemoveAllDependent();
+
+ // Sets rejected and calls UpdateReference Undo and DeleteCellEntries
+ RejectRestoreContents( pTrack, 0, 0 );
+
+ while ( pLinkDependent )
+ {
+ ScChangeAction* p = pLinkDependent->GetAction();
+ if ( p && p->GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(p);
+ if ( !pContent->IsDeletedIn() &&
+ pContent->GetBigRange().aStart.IsValid( rDoc ) )
+ pContent->PutNewValueToDoc( &rDoc, 0, 0 );
+ // Delete the ones created in LookUpContents
+ if ( pTrack->IsGenerated( pContent->GetActionNumber() ) &&
+ !pContent->IsDeletedIn() )
+ {
+ pLinkDependent->UnLink(); // Else this one is also deleted!
+ pTrack->DeleteGeneratedDelContent( pContent );
+ }
+ }
+ delete pLinkDependent;
+ }
+
+ RemoveAllLinks();
+ return true;
+}
+
+ScChangeActionContent::ScChangeActionContent( const ScRange& rRange ) :
+ ScChangeAction(SC_CAT_CONTENT, rRange),
+ pNextContent(nullptr),
+ pPrevContent(nullptr),
+ pNextInSlot(nullptr),
+ ppPrevInSlot(nullptr)
+{}
+
+ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
+ const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
+ const ScBigRange& aBigRangeP, const OUString& aUserP,
+ const DateTime& aDateTimeP, const OUString& sComment,
+ ScCellValue aOldCell, const ScDocument* pDoc, const OUString& sOldValue ) :
+ ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
+ maOldCell(std::move(aOldCell)),
+ maOldValue(sOldValue),
+ pNextContent(nullptr),
+ pPrevContent(nullptr),
+ pNextInSlot(nullptr),
+ ppPrevInSlot(nullptr)
+{
+ if (!maOldCell.isEmpty())
+ SetCell(maOldValue, maOldCell, 0, pDoc);
+
+ if (!sOldValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
+ maOldValue = sOldValue; // set again, because SetCell removes it
+}
+
+ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
+ ScCellValue aNewCell, const ScBigRange& aBigRangeP,
+ const ScDocument* pDoc, const OUString& sNewValue ) :
+ ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber),
+ maNewCell(std::move(aNewCell)),
+ maNewValue(sNewValue),
+ pNextContent(nullptr),
+ pPrevContent(nullptr),
+ pNextInSlot(nullptr),
+ ppPrevInSlot(nullptr)
+{
+ if (!maNewCell.isEmpty())
+ SetCell(maNewValue, maNewCell, 0, pDoc);
+
+ if (!sNewValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
+ maNewValue = sNewValue; // set again, because SetCell removes it
+}
+
+ScChangeActionContent::~ScChangeActionContent()
+{
+ ClearTrack();
+}
+
+void ScChangeActionContent::ClearTrack()
+{
+ RemoveFromSlot();
+ if ( pPrevContent )
+ pPrevContent->pNextContent = pNextContent;
+ if ( pNextContent )
+ pNextContent->pPrevContent = pPrevContent;
+}
+
+ScChangeActionContent* ScChangeActionContent::GetTopContent() const
+{
+ if ( pNextContent )
+ {
+ ScChangeActionContent* pContent = pNextContent;
+ while ( pContent->pNextContent && pContent != pContent->pNextContent )
+ pContent = pContent->pNextContent;
+ return pContent;
+ }
+ return const_cast<ScChangeActionContent*>(this);
+}
+
+ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const
+{
+ if ( pNextContent )
+ return GetTopContent()->pLinkDeletedIn;
+ return pLinkDeletedIn;
+}
+
+ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress()
+{
+ if ( pNextContent )
+ return GetTopContent()->GetDeletedInAddress();
+ return &pLinkDeletedIn;
+}
+
+void ScChangeActionContent::SetOldValue(
+ const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc, sal_uLong nFormat )
+{
+ SetValue(maOldValue, maOldCell, nFormat, rCell, pFromDoc, pToDoc);
+}
+
+void ScChangeActionContent::SetOldValue(
+ const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc )
+{
+ SetValue(maOldValue, maOldCell, aBigRange.aStart.MakeAddress(*pFromDoc), rCell, pFromDoc, pToDoc);
+}
+
+void ScChangeActionContent::SetNewValue( const ScCellValue& rCell, ScDocument* pDoc )
+{
+ SetValue(maNewValue, maNewCell, aBigRange.aStart.MakeAddress(*pDoc), rCell, pDoc, pDoc);
+}
+
+void ScChangeActionContent::SetOldNewCells(
+ const ScCellValue& rOldCell, sal_uLong nOldFormat, const ScCellValue& rNewCell,
+ sal_uLong nNewFormat, const ScDocument* pDoc )
+{
+ maOldCell = rOldCell;
+ maNewCell = rNewCell;
+ SetCell(maOldValue, maOldCell, nOldFormat, pDoc);
+ SetCell(maNewValue, maNewCell, nNewFormat, pDoc);
+}
+
+void ScChangeActionContent::SetNewCell(
+ const ScCellValue& rCell, const ScDocument* pDoc, const OUString& rFormatted )
+{
+ maNewCell = rCell;
+ SetCell(maNewValue, maNewCell, 0, pDoc);
+
+ // #i40704# allow to set formatted text here - don't call SetNewValue with string from XML filter
+ if (!rFormatted.isEmpty())
+ maNewValue = rFormatted;
+}
+
+void ScChangeActionContent::SetValueString(
+ OUString& rValue, ScCellValue& rCell, const OUString& rStr, ScDocument* pDoc )
+{
+ rCell.clear();
+ if ( rStr.getLength() > 1 && rStr[0] == '=' )
+ {
+ rValue.clear();
+ rCell.set(new ScFormulaCell(
+ *pDoc, aBigRange.aStart.MakeAddress(*pDoc), rStr,
+ pDoc->GetGrammar() ));
+ rCell.getFormula()->SetInChangeTrack(true);
+ }
+ else
+ rValue = rStr;
+}
+
+void ScChangeActionContent::SetOldValue( const OUString& rOld, ScDocument* pDoc )
+{
+ SetValueString(maOldValue, maOldCell, rOld, pDoc);
+}
+
+OUString ScChangeActionContent::GetOldString( const ScDocument* pDoc ) const
+{
+ return GetValueString(maOldValue, maOldCell, pDoc);
+}
+
+OUString ScChangeActionContent::GetNewString( const ScDocument* pDoc ) const
+{
+ return GetValueString(maNewValue, maNewCell, pDoc);
+}
+
+OUString ScChangeActionContent::GetDescription(
+ ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
+{
+ OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
+
+ OUString aRsc = ScResId(STR_CHANGED_CELL);
+
+ OUString aTmpStr = GetRefString(rDoc);
+
+ sal_Int32 nPos = aRsc.indexOf("#1", 0);
+ if (nPos >= 0)
+ {
+ aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
+ nPos += aTmpStr.getLength();
+ }
+
+ aTmpStr = GetOldString( &rDoc );
+ if (aTmpStr.isEmpty())
+ aTmpStr = ScResId( STR_CHANGED_BLANK );
+
+ nPos = nPos >= 0 ? aRsc.indexOf("#2", nPos) : -1;
+ if (nPos >= 0)
+ {
+ aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
+ nPos += aTmpStr.getLength();
+ }
+
+ aTmpStr = GetNewString( &rDoc );
+ if (aTmpStr.isEmpty())
+ aTmpStr = ScResId( STR_CHANGED_BLANK );
+
+ nPos = nPos >= 0 ? aRsc.indexOf("#3", nPos) : -1;
+ if (nPos >= 0)
+ {
+ aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
+ }
+
+ return str + aRsc; // append to the original string.
+}
+
+OUString ScChangeActionContent::GetRefString(
+ ScDocument& rDoc, bool bFlag3D ) const
+{
+ ScRefFlags nFlags = ( GetBigRange().IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
+ if ( nFlags != ScRefFlags::ZERO )
+ {
+ const ScCellValue& rCell = GetNewCell();
+ if ( GetContentCellType(rCell) == SC_CACCT_MATORG )
+ {
+ ScBigRange aLocalBigRange( GetBigRange() );
+ SCCOL nC;
+ SCROW nR;
+ rCell.getFormula()->GetMatColsRows( nC, nR );
+ aLocalBigRange.aEnd.IncCol( nC-1 );
+ aLocalBigRange.aEnd.IncRow( nR-1 );
+ return ScChangeAction::GetRefString( aLocalBigRange, rDoc, bFlag3D );
+ }
+
+ ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress( rDoc ) );
+ if ( bFlag3D )
+ nFlags |= ScRefFlags::TAB_3D;
+ OUString str = aTmpAddress.Format(nFlags, &rDoc, rDoc.GetAddressConvention());
+ if ( IsDeletedIn() )
+ {
+ // Insert the parentheses.
+ str = "(" + str + ")";
+ }
+ return str;
+ }
+ else
+ return ScCompiler::GetNativeSymbol(ocErrRef);
+}
+
+bool ScChangeActionContent::Reject( ScDocument& rDoc )
+{
+ if ( !aBigRange.IsValid( rDoc ) )
+ return false;
+
+ PutOldValueToDoc( &rDoc, 0, 0 );
+
+ SetState( SC_CAS_REJECTED );
+ RemoveAllLinks();
+
+ return true;
+}
+
+bool ScChangeActionContent::Select( ScDocument& rDoc, ScChangeTrack* pTrack,
+ bool bOldest, ::std::stack<ScChangeActionContent*>* pRejectActions )
+{
+ if ( !aBigRange.IsValid( rDoc ) )
+ return false;
+
+ ScChangeActionContent* pContent = this;
+ // accept previous contents
+ while ( ( pContent = pContent->pPrevContent ) != nullptr )
+ {
+ if ( pContent->IsVirgin() )
+ pContent->SetState( SC_CAS_ACCEPTED );
+ }
+ ScChangeActionContent* pEnd = pContent = this;
+ // reject subsequent contents
+ while ( ( pContent = pContent->pNextContent ) != nullptr )
+ {
+ // MatrixOrigin may have dependents, no dependency recursion needed
+ const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p )
+ p->SetRejected();
+ pL = pL->GetNext();
+ }
+ pContent->SetRejected();
+ pEnd = pContent;
+ }
+
+ // If not oldest: Is it anyone else than the last one?
+ if ( bOldest || pEnd != this )
+ { ScRange aRange( aBigRange.aStart.MakeAddress( rDoc ) );
+ const ScAddress& rPos = aRange.aStart;
+
+ ScChangeActionContent* pNew = new ScChangeActionContent( aRange );
+ ScCellValue aCell;
+ aCell.assign(rDoc, rPos);
+ pNew->SetOldValue(aCell, &rDoc, &rDoc);
+
+ if ( bOldest )
+ PutOldValueToDoc( &rDoc, 0, 0 );
+ else
+ PutNewValueToDoc( &rDoc, 0, 0 );
+
+ pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() );
+ pNew->SetState( SC_CAS_ACCEPTED );
+ if ( pRejectActions )
+ pRejectActions->push( pNew );
+ else
+ {
+ aCell.assign(rDoc, rPos);
+ pNew->SetNewValue(aCell, &rDoc);
+ pTrack->Append( pNew );
+ }
+ }
+
+ if ( bOldest )
+ SetRejected();
+ else
+ SetState( SC_CAS_ACCEPTED );
+
+ return true;
+}
+
+OUString ScChangeActionContent::GetStringOfCell(
+ const ScCellValue& rCell, const ScDocument* pDoc, const ScAddress& rPos )
+{
+ if (NeedsNumberFormat(rCell))
+ return GetStringOfCell(rCell, pDoc, pDoc->GetNumberFormat(rPos));
+ else
+ return GetStringOfCell(rCell, pDoc, 0);
+}
+
+OUString ScChangeActionContent::GetStringOfCell(
+ const ScCellValue& rCell, const ScDocument* pDoc, sal_uLong nFormat )
+{
+ if (!GetContentCellType(rCell))
+ return OUString();
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ OUString str;
+ pDoc->GetFormatTable()->GetInputLineString(rCell.getDouble(), nFormat, str);
+ return str;
+ }
+ case CELLTYPE_STRING:
+ return rCell.getSharedString()->getString();
+ case CELLTYPE_EDIT:
+ if (rCell.getEditText())
+ return ScEditUtil::GetString(*rCell.getEditText(), pDoc);
+ return OUString();
+ case CELLTYPE_FORMULA:
+ return rCell.getFormula()->GetFormula();
+ default:
+ return OUString();
+ }
+}
+
+ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ return SC_CACCT_NORMAL;
+ case CELLTYPE_FORMULA :
+ switch (rCell.getFormula()->GetMatrixFlag())
+ {
+ case ScMatrixMode::NONE :
+ return SC_CACCT_NORMAL;
+ case ScMatrixMode::Formula :
+ return SC_CACCT_MATORG;
+ case ScMatrixMode::Reference :
+ return SC_CACCT_MATREF;
+ }
+ return SC_CACCT_NORMAL;
+ default:
+ return SC_CACCT_NONE;
+ }
+}
+
+ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ return SC_CACCT_NORMAL;
+ case CELLTYPE_FORMULA:
+ {
+ const ScFormulaCell* pCell = rCell.getFormula();
+ switch (pCell->GetMatrixFlag())
+ {
+ case ScMatrixMode::NONE :
+ return SC_CACCT_NORMAL;
+ case ScMatrixMode::Formula :
+ return SC_CACCT_MATORG;
+ case ScMatrixMode::Reference :
+ return SC_CACCT_MATREF;
+ }
+ return SC_CACCT_NORMAL;
+ }
+ default:
+ ;
+ }
+
+ return SC_CACCT_NONE;
+}
+
+bool ScChangeActionContent::NeedsNumberFormat( const ScCellValue& rVal )
+{
+ return rVal.getType() == CELLTYPE_VALUE;
+}
+
+void ScChangeActionContent::SetValue(
+ OUString& rStr, ScCellValue& rCell, const ScAddress& rPos, const ScCellValue& rOrgCell,
+ const ScDocument* pFromDoc, ScDocument* pToDoc )
+{
+ sal_uInt32 nFormat = NeedsNumberFormat(rOrgCell) ? pFromDoc->GetNumberFormat(rPos) : 0;
+ SetValue(rStr, rCell, nFormat, rOrgCell, pFromDoc, pToDoc);
+}
+
+void ScChangeActionContent::SetValue(
+ OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScCellValue& rOrgCell,
+ const ScDocument* pFromDoc, ScDocument* pToDoc )
+{
+ rStr.clear();
+
+ if (GetContentCellType(rOrgCell))
+ {
+ rCell.assign(rOrgCell, *pToDoc);
+ switch (rOrgCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ { // E.g.: Remember date as such
+ pFromDoc->GetFormatTable()->GetInputLineString(
+ rOrgCell.getDouble(), nFormat, rStr);
+ }
+ break;
+ case CELLTYPE_FORMULA :
+ rCell.getFormula()->SetInChangeTrack(true);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ else
+ rCell.clear();
+}
+
+void ScChangeActionContent::SetCell( OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScDocument* pDoc )
+{
+ rStr.clear();
+ if (rCell.isEmpty())
+ return;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ // e.g. remember date as date string
+ pDoc->GetFormatTable()->GetInputLineString(rCell.getDouble(), nFormat, rStr);
+ break;
+ case CELLTYPE_FORMULA :
+ rCell.getFormula()->SetInChangeTrack(true);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+OUString ScChangeActionContent::GetValueString(
+ const OUString& rValue, const ScCellValue& rCell, const ScDocument* pDoc ) const
+{
+ if (!rValue.isEmpty())
+ {
+ return rValue;
+ }
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING :
+ return rCell.getSharedString()->getString();
+ case CELLTYPE_EDIT :
+ if (rCell.getEditText())
+ return ScEditUtil::GetString(*rCell.getEditText(), pDoc);
+ return OUString();
+ case CELLTYPE_VALUE : // Is always in rValue
+ return rValue;
+ case CELLTYPE_FORMULA :
+ return GetFormulaString(rCell.getFormula());
+ case CELLTYPE_NONE:
+ default:
+ return OUString();
+ }
+}
+
+OUString ScChangeActionContent::GetFormulaString(
+ const ScFormulaCell* pCell ) const
+{
+ ScAddress aPos( aBigRange.aStart.MakeAddress( pCell->GetDocument()) );
+ if ( aPos == pCell->aPos || IsDeletedIn() )
+ return pCell->GetFormula();
+ else
+ {
+ OSL_FAIL( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" );
+ ScFormulaCell aNew( *pCell, pCell->GetDocument(), aPos );
+ return aNew.GetFormula();
+ }
+}
+
+void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc,
+ SCCOL nDx, SCROW nDy ) const
+{
+ PutValueToDoc(maOldCell, maOldValue, pDoc, nDx, nDy);
+}
+
+void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc,
+ SCCOL nDx, SCROW nDy ) const
+{
+ PutValueToDoc(maNewCell, maNewValue, pDoc, nDx, nDy);
+}
+
+void ScChangeActionContent::PutValueToDoc(
+ const ScCellValue& rCell, const OUString& rValue, ScDocument* pDoc,
+ SCCOL nDx, SCROW nDy ) const
+{
+ ScAddress aPos( aBigRange.aStart.MakeAddress( *pDoc ) );
+ if ( nDx )
+ aPos.IncCol( nDx );
+ if ( nDy )
+ aPos.IncRow( nDy );
+
+ if (!rValue.isEmpty())
+ {
+ pDoc->SetString(aPos, rValue);
+ return;
+ }
+
+ if (rCell.isEmpty())
+ {
+ pDoc->SetEmptyCell(aPos);
+ return;
+ }
+
+ if (rCell.getType() == CELLTYPE_VALUE)
+ {
+ pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
+ return;
+ }
+
+ switch (GetContentCellType(rCell))
+ {
+ case SC_CACCT_MATORG :
+ {
+ SCCOL nC;
+ SCROW nR;
+ rCell.getFormula()->GetMatColsRows(nC, nR);
+ OSL_ENSURE( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" );
+ ScRange aRange( aPos );
+ if ( nC > 1 )
+ aRange.aEnd.IncCol( nC-1 );
+ if ( nR > 1 )
+ aRange.aEnd.IncRow( nR-1 );
+ ScMarkData aDestMark(pDoc->GetSheetLimits());
+ aDestMark.SelectOneTable( aPos.Tab() );
+ aDestMark.SetMarkArea( aRange );
+ pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ aDestMark, OUString(), rCell.getFormula()->GetCode());
+ }
+ break;
+ case SC_CACCT_MATREF :
+ // nothing
+ break;
+ default:
+ rCell.commit(*pDoc, aPos);
+ }
+}
+
+static void lcl_InvalidateReference( const ScDocument& rDoc, formula::FormulaToken& rTok, const ScBigAddress& rPos )
+{
+ ScSingleRefData& rRef1 = *rTok.GetSingleRef();
+ if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
+ {
+ rRef1.SetColDeleted( true );
+ }
+ if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
+ {
+ rRef1.SetRowDeleted( true );
+ }
+ if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
+ {
+ rRef1.SetTabDeleted( true );
+ }
+ if ( rTok.GetType() != formula::svDoubleRef )
+ return;
+
+ ScSingleRefData& rRef2 = rTok.GetDoubleRef()->Ref2;
+ if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
+ {
+ rRef2.SetColDeleted( true );
+ }
+ if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
+ {
+ rRef2.SetRowDeleted( true );
+ }
+ if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
+ {
+ rRef2.SetTabDeleted( true );
+ }
+}
+
+void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack,
+ UpdateRefMode eMode, const ScBigRange& rRange,
+ sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
+{
+ SCSIZE nOldSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
+ ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange );
+ SCSIZE nNewSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
+ if ( nNewSlot != nOldSlot )
+ {
+ RemoveFromSlot();
+ InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) );
+ }
+
+ if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() )
+ return ; // Formula only update whole range
+
+ bool bOldFormula = maOldCell.getType() == CELLTYPE_FORMULA;
+ bool bNewFormula = maNewCell.getType() == CELLTYPE_FORMULA;
+ if ( !(bOldFormula || bNewFormula) )
+ return;
+
+// Adjust UpdateReference via ScFormulaCell (there)
+ if ( pTrack->IsInDelete() )
+ {
+ const ScRange& rDelRange = pTrack->GetInDeleteRange();
+ if ( nDx > 0 )
+ nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1;
+ else if ( nDx < 0 )
+ nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1);
+ if ( nDy > 0 )
+ nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1;
+ else if ( nDy < 0 )
+ nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1);
+ if ( nDz > 0 )
+ nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1;
+ else if ( nDz < 0 )
+ nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1);
+ }
+ ScBigRange aTmpRange( rRange );
+ switch ( eMode )
+ {
+ case URM_INSDEL :
+ if ( nDx < 0 || nDy < 0 || nDz < 0 )
+ { // Delete starts there after removed range
+ // Position is changed there
+ if ( nDx )
+ aTmpRange.aStart.IncCol( -nDx );
+ if ( nDy )
+ aTmpRange.aStart.IncRow( -nDy );
+ if ( nDz )
+ aTmpRange.aStart.IncTab( -nDz );
+ }
+ break;
+ case URM_MOVE :
+ // Move is Source here and Target there
+ // Position needs to be adjusted before that
+ if ( bOldFormula )
+ maOldCell.getFormula()->aPos = aBigRange.aStart.MakeAddress(pTrack->GetDocument());
+ if ( bNewFormula )
+ maNewCell.getFormula()->aPos = aBigRange.aStart.MakeAddress(pTrack->GetDocument());
+ if ( nDx )
+ {
+ aTmpRange.aStart.IncCol( nDx );
+ aTmpRange.aEnd.IncCol( nDx );
+ }
+ if ( nDy )
+ {
+ aTmpRange.aStart.IncRow( nDy );
+ aTmpRange.aEnd.IncRow( nDy );
+ }
+ if ( nDz )
+ {
+ aTmpRange.aStart.IncTab( nDz );
+ aTmpRange.aEnd.IncTab( nDz );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ ScRange aRange( aTmpRange.MakeRange(pTrack->GetDocument()) );
+
+ sc::RefUpdateContext aRefCxt(pTrack->GetDocument());
+ aRefCxt.meMode = eMode;
+ aRefCxt.maRange = aRange;
+ aRefCxt.mnColDelta = nDx;
+ aRefCxt.mnRowDelta = nDy;
+ aRefCxt.mnTabDelta = nDz;
+
+ if ( bOldFormula )
+ maOldCell.getFormula()->UpdateReference(aRefCxt);
+ if ( bNewFormula )
+ maNewCell.getFormula()->UpdateReference(aRefCxt);
+
+ if ( aBigRange.aStart.IsValid( pTrack->GetDocument() ) )
+ return;
+
+//FIXME:
+ // UpdateReference cannot handle positions outside of the Document.
+ // Therefore set everything to #REF!
+ //TODO: Remove the need for this hack! This means big changes to ScAddress etc.!
+ const ScBigAddress& rPos = aBigRange.aStart;
+ if ( bOldFormula )
+ {
+ formula::FormulaToken* t;
+ ScTokenArray* pArr = maOldCell.getFormula()->GetCode();
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
+ aIter.Reset();
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
+ }
+ if ( bNewFormula )
+ {
+ formula::FormulaToken* t;
+ ScTokenArray* pArr = maNewCell.getFormula()->GetCode();
+ formula::FormulaTokenArrayPlainIterator aIter(*pArr);
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
+ aIter.Reset();
+ while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
+ lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
+ }
+}
+
+bool ScChangeActionContent::IsMatrixOrigin() const
+{
+ return GetContentCellType(GetNewCell()) == SC_CACCT_MATORG;
+}
+
+bool ScChangeActionContent::IsOldMatrixReference() const
+{
+ return GetContentCellType(GetOldCell()) == SC_CACCT_MATREF;
+}
+
+// ScChangeActionReject
+ScChangeActionReject::ScChangeActionReject(
+ const sal_uLong nActionNumber, const ScChangeActionState eStateP,
+ const sal_uLong nRejectingNumber,
+ const ScBigRange& aBigRangeP, const OUString& aUserP,
+ const DateTime& aDateTimeP, const OUString& sComment) :
+ ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
+{
+}
+
+bool ScChangeActionReject::Reject(ScDocument& /*rDoc*/)
+{
+ return false;
+}
+
+SCSIZE ScChangeTrack::ComputeContentSlot( sal_Int32 nRow ) const
+{
+ if ( nRow < 0 || nRow > rDoc.GetSheetLimits().mnMaxRow )
+ return mnContentSlots - 1;
+ return static_cast< SCSIZE >( nRow / mnContentRowsPerSlot );
+}
+
+SCROW ScChangeTrack::InitContentRowsPerSlot()
+{
+ const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2;
+ SCROW nRowsPerSlot = rDoc.GetMaxRowCount() / nMaxSlots;
+ if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(rDoc.GetMaxRowCount()) )
+ ++nRowsPerSlot;
+ return nRowsPerSlot;
+}
+
+ScChangeTrack::ScChangeTrack( ScDocument& rDocP ) :
+ aFixDateTime( DateTime::SYSTEM ),
+ rDoc( rDocP )
+{
+ Init();
+ SC_MOD()->GetUserOptions().AddListener(this);
+
+ ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
+ memset( ppContentSlots.get(), 0, mnContentSlots * sizeof( ScChangeActionContent* ) );
+}
+
+ScChangeTrack::ScChangeTrack( ScDocument& rDocP, std::set<OUString>&& aTempUserCollection) :
+ maUserCollection(std::move(aTempUserCollection)),
+ aFixDateTime( DateTime::SYSTEM ),
+ rDoc( rDocP )
+{
+ Init();
+ SC_MOD()->GetUserOptions().AddListener(this);
+ ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
+ memset( ppContentSlots.get(), 0, mnContentSlots * sizeof( ScChangeActionContent* ) );
+}
+
+ScChangeTrack::~ScChangeTrack()
+{
+ SC_MOD()->GetUserOptions().RemoveListener(this);
+ DtorClear();
+}
+
+void ScChangeTrack::Init()
+{
+ mnContentRowsPerSlot = InitContentRowsPerSlot();
+ mnContentSlots = rDoc.GetMaxRowCount() / InitContentRowsPerSlot() + 2;
+
+ pFirst = nullptr;
+ pLast = nullptr;
+ pFirstGeneratedDelContent = nullptr;
+ pLastCutMove = nullptr;
+ pLinkInsertCol = nullptr;
+ pLinkInsertRow = nullptr;
+ pLinkInsertTab = nullptr;
+ pLinkMove = nullptr;
+ xBlockModifyMsg.reset();
+ nActionMax = 0;
+ nGeneratedMin = SC_CHGTRACK_GENERATED_START;
+ nMarkLastSaved = 0;
+ nStartLastCut = 0;
+ nEndLastCut = 0;
+ nLastMerge = 0;
+ eMergeState = SC_CTMS_NONE;
+ bInDelete = false;
+ bInDeleteTop = false;
+ bInDeleteUndo = false;
+ bInPasteCut = false;
+ bUseFixDateTime = false;
+ bTimeNanoSeconds = true;
+
+ CreateAuthorName();
+}
+
+void ScChangeTrack::DtorClear()
+{
+ ScChangeAction* p;
+ ScChangeAction* pNext;
+ for ( p = GetFirst(); p; p = pNext )
+ {
+ pNext = p->GetNext();
+ delete p;
+ }
+ for ( p = pFirstGeneratedDelContent; p; p = pNext )
+ {
+ pNext = p->GetNext();
+ delete p;
+ }
+ for( const auto& rEntry : aPasteCutMap )
+ {
+ delete rEntry.second;
+ }
+ pLastCutMove.reset();
+ ClearMsgQueue();
+}
+
+void ScChangeTrack::ClearMsgQueue()
+{
+ xBlockModifyMsg.reset();
+ aMsgStackTmp.clear();
+ aMsgStackFinal.clear();
+ aMsgQueue.clear();
+}
+
+void ScChangeTrack::Clear()
+{
+ DtorClear();
+ aMap.clear();
+ aGeneratedMap.clear();
+ aPasteCutMap.clear();
+ maUserCollection.clear();
+ maUser.clear();
+ Init();
+}
+
+bool ScChangeTrack::IsGenerated( sal_uLong nAction ) const
+{
+ return nAction >= nGeneratedMin;
+}
+
+ScChangeAction* ScChangeTrack::GetAction( sal_uLong nAction ) const
+{
+ ScChangeActionMap::const_iterator it = aMap.find( nAction );
+ if( it != aMap.end() )
+ return it->second;
+ else
+ return nullptr;
+}
+
+ScChangeAction* ScChangeTrack::GetGenerated( sal_uLong nGenerated ) const
+{
+ ScChangeActionMap::const_iterator it = aGeneratedMap.find( nGenerated );
+ if( it != aGeneratedMap.end() )
+ return it->second;
+ else
+ return nullptr;
+}
+
+ScChangeAction* ScChangeTrack::GetActionOrGenerated( sal_uLong nAction ) const
+{
+ return IsGenerated( nAction ) ?
+ GetGenerated( nAction ) :
+ GetAction( nAction );
+}
+sal_uLong ScChangeTrack::GetLastSavedActionNumber() const
+{
+ return nMarkLastSaved;
+}
+
+void ScChangeTrack::SetLastSavedActionNumber(sal_uLong nNew)
+{
+ nMarkLastSaved = nNew;
+}
+
+ScChangeAction* ScChangeTrack::GetLastSaved() const
+{
+ ScChangeActionMap::const_iterator it = aMap.find( nMarkLastSaved );
+ if( it != aMap.end() )
+ return it->second;
+ else
+ return nullptr;
+}
+
+void ScChangeTrack::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints )
+{
+ if ( rDoc.IsInDtorClear() )
+ return;
+
+ size_t nOldCount = maUserCollection.size();
+
+ CreateAuthorName();
+
+ if ( maUserCollection.size() != nOldCount )
+ {
+ // New user in collection -> have to repaint because
+ // colors may be different now (#106697#).
+ // (Has to be done in the Notify handler, to be sure
+ // the user collection has already been updated)
+
+ ScDocShell* pDocSh = rDoc.GetDocumentShell();
+ if (pDocSh)
+ pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB), PaintPartFlags::Grid ) );
+ }
+}
+
+void ScChangeTrack::CreateAuthorName()
+{
+ const SvtUserOptions& rUserOptions = SC_MOD()->GetUserOptions();
+ OUString aFirstName(rUserOptions.GetFirstName());
+ OUString aLastName(rUserOptions.GetLastName());
+ if (aFirstName.isEmpty() && aLastName.isEmpty())
+ SetUser(ScResId(STR_CHG_UNKNOWN_AUTHOR));
+ else if(!aFirstName.isEmpty() && aLastName.isEmpty())
+ SetUser(aFirstName);
+ else if(aFirstName.isEmpty() && !aLastName.isEmpty())
+ SetUser(aLastName);
+ else
+ SetUser(aFirstName + " " + aLastName);
+}
+
+
+void ScChangeTrack::SetUser( const OUString& rUser )
+{
+ maUser = rUser;
+ maUserCollection.insert(maUser);
+}
+
+void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType,
+ sal_uLong nStartAction )
+{
+ if ( aModifiedLink.IsSet() )
+ {
+ if ( xBlockModifyMsg )
+ aMsgStackTmp.push_back( *xBlockModifyMsg ); // Block in Block
+ xBlockModifyMsg = ScChangeTrackMsgInfo();
+ xBlockModifyMsg->eMsgType = eMsgType;
+ xBlockModifyMsg->nStartAction = nStartAction;
+ xBlockModifyMsg->nEndAction = 0;
+ }
+}
+
+void ScChangeTrack::EndBlockModify( sal_uLong nEndAction )
+{
+ if ( !aModifiedLink.IsSet() )
+ return;
+
+ if ( xBlockModifyMsg )
+ {
+ if ( xBlockModifyMsg->nStartAction <= nEndAction )
+ {
+ xBlockModifyMsg->nEndAction = nEndAction;
+ // Blocks dissolved in Blocks
+ aMsgStackFinal.push_back( *xBlockModifyMsg );
+ }
+ else
+ xBlockModifyMsg.reset();
+ if (aMsgStackTmp.empty())
+ xBlockModifyMsg.reset();
+ else
+ {
+ xBlockModifyMsg = aMsgStackTmp.back(); // Maybe Block in Block
+ aMsgStackTmp.pop_back();
+ }
+ }
+ if ( !xBlockModifyMsg )
+ {
+ bool bNew = !aMsgStackFinal.empty();
+ aMsgQueue.reserve(aMsgQueue.size() + aMsgStackFinal.size());
+ aMsgQueue.insert(aMsgQueue.end(), aMsgStackFinal.rbegin(), aMsgStackFinal.rend());
+ aMsgStackFinal.clear();
+ if ( bNew )
+ aModifiedLink.Call( *this );
+ }
+}
+
+ScChangeTrackMsgQueue& ScChangeTrack::GetMsgQueue()
+{
+ return aMsgQueue;
+}
+
+void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType,
+ sal_uLong nStartAction, sal_uLong nEndAction )
+{
+ if ( aModifiedLink.IsSet() )
+ {
+ if ( !xBlockModifyMsg || xBlockModifyMsg->eMsgType != eMsgType ||
+ (IsGenerated( nStartAction ) &&
+ (eMsgType == ScChangeTrackMsgType::Append || eMsgType == ScChangeTrackMsgType::Remove)) )
+ { // Append within Append e.g. not
+ StartBlockModify( eMsgType, nStartAction );
+ EndBlockModify( nEndAction );
+ }
+ }
+}
+
+void ScChangeTrack::MasterLinks( ScChangeAction* pAppend )
+{
+ ScChangeActionType eType = pAppend->GetType();
+
+ if ( eType == SC_CAT_CONTENT )
+ {
+ if ( !IsGenerated( pAppend->GetActionNumber() ) )
+ {
+ SCSIZE nSlot = ComputeContentSlot(
+ pAppend->GetBigRange().aStart.Row() );
+ static_cast<ScChangeActionContent*>(pAppend)->InsertInSlot(
+ &ppContentSlots[nSlot] );
+ }
+ return ;
+ }
+
+ if ( pAppend->IsRejecting() )
+ return ; // Rejects do not have dependencies
+
+ switch ( eType )
+ {
+ case SC_CAT_INSERT_COLS :
+ {
+ ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
+ &pLinkInsertCol, pAppend );
+ pAppend->AddLink( nullptr, pLink );
+ }
+ break;
+ case SC_CAT_INSERT_ROWS :
+ {
+ ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
+ &pLinkInsertRow, pAppend );
+ pAppend->AddLink( nullptr, pLink );
+ }
+ break;
+ case SC_CAT_INSERT_TABS :
+ {
+ ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
+ &pLinkInsertTab, pAppend );
+ pAppend->AddLink( nullptr, pLink );
+ }
+ break;
+ case SC_CAT_MOVE :
+ {
+ ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
+ &pLinkMove, pAppend );
+ pAppend->AddLink( nullptr, pLink );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScChangeTrack::AppendLoaded( std::unique_ptr<ScChangeAction> pActionParam )
+{
+ ScChangeAction* pAppend = pActionParam.release();
+ aMap.insert( ::std::make_pair( pAppend->GetActionNumber(), pAppend ) );
+ if ( !pLast )
+ pFirst = pLast = pAppend;
+ else
+ {
+ pLast->pNext = pAppend;
+ pAppend->pPrev = pLast;
+ pLast = pAppend;
+ }
+ MasterLinks( pAppend );
+}
+
+void ScChangeTrack::Append( ScChangeAction* pAppend, sal_uLong nAction )
+{
+ if ( nActionMax < nAction )
+ nActionMax = nAction;
+ pAppend->SetUser( maUser );
+ if ( bUseFixDateTime )
+ pAppend->SetDateTimeUTC( aFixDateTime );
+ pAppend->SetActionNumber( nAction );
+ aMap.insert( ::std::make_pair( nAction, pAppend ) );
+ // UpdateReference Inserts before Dependencies.
+ // Delete rejecting Insert which had UpdateReference with Delete Undo.
+ // UpdateReference also with pLast==NULL, as pAppend can be a Delete,
+ // which could have generated DelContents.
+ if ( pAppend->IsInsertType() && !pAppend->IsRejecting() )
+ UpdateReference( pAppend, false );
+ if ( !pLast )
+ pFirst = pLast = pAppend;
+ else
+ {
+ pLast->pNext = pAppend;
+ pAppend->pPrev = pLast;
+ pLast = pAppend;
+ Dependencies( pAppend );
+ }
+ // UpdateReference does not Insert() after Dependencies.
+ // Move rejecting Move, which had UpdateReference with Move Undo.
+ // Do not delete content in ToRange.
+ if ( !pAppend->IsInsertType() &&
+ !(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) )
+ UpdateReference( pAppend, false );
+ MasterLinks( pAppend );
+
+ if ( !aModifiedLink.IsSet() )
+ return;
+
+ NotifyModified( ScChangeTrackMsgType::Append, nAction, nAction );
+ if ( pAppend->GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pAppend);
+ if ( ( pContent = pContent->GetPrevContent() ) != nullptr )
+ {
+ sal_uLong nMod = pContent->GetActionNumber();
+ NotifyModified( ScChangeTrackMsgType::Change, nMod, nMod );
+ }
+ }
+ else
+ NotifyModified( ScChangeTrackMsgType::Change, pFirst->GetActionNumber(),
+ pLast->GetActionNumber() );
+}
+
+void ScChangeTrack::Append( ScChangeAction* pAppend )
+{
+ Append( pAppend, ++nActionMax );
+}
+
+void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
+ ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction, SCTAB nDz )
+{
+ nStartAction = GetActionMax() + 1;
+ AppendDeleteRange( rRange, pRefDoc, nDz, 0 );
+ nEndAction = GetActionMax();
+}
+
+void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
+ ScDocument* pRefDoc, SCTAB nDz, sal_uLong nRejectingInsert )
+{
+ SetInDeleteRange( rRange );
+ StartBlockModify( ScChangeTrackMsgType::Append, GetActionMax() + 1 );
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
+ {
+ if ( !pRefDoc || nTab < pRefDoc->GetTableCount() )
+ {
+ if ( nCol1 == 0 && nCol2 == rDoc.MaxCol() )
+ { // Whole Row and/or Tables
+ if ( nRow1 == 0 && nRow2 == rDoc.MaxRow() )
+ { // Whole Table
+ // TODO: Can't we do the whole Table as a whole?
+ ScRange aRange( 0, 0, nTab, 0, rDoc.MaxRow(), nTab );
+ for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
+ { // Column by column is less than row by row
+ aRange.aStart.SetCol( nCol );
+ aRange.aEnd.SetCol( nCol );
+ if ( nCol == nCol2 )
+ SetInDeleteTop( true );
+ AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
+ nTab-nTab1 + nDz, nRejectingInsert );
+ }
+ // Still InDeleteTop!
+ AppendOneDeleteRange( rRange, pRefDoc, 0, 0,
+ nTab-nTab1 + nDz, nRejectingInsert );
+ }
+ else
+ { // Whole rows
+ ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), 0, nTab );
+ for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
+ {
+ aRange.aStart.SetRow( nRow );
+ aRange.aEnd.SetRow( nRow );
+ if ( nRow == nRow2 )
+ SetInDeleteTop( true );
+ AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1,
+ 0, nRejectingInsert );
+ }
+ }
+ }
+ else if ( nRow1 == 0 && nRow2 == rDoc.MaxRow() )
+ { // Whole columns
+ ScRange aRange( 0, 0, nTab, 0, rDoc.MaxRow(), nTab );
+ for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
+ {
+ aRange.aStart.SetCol( nCol );
+ aRange.aEnd.SetCol( nCol );
+ if ( nCol == nCol2 )
+ SetInDeleteTop( true );
+ AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
+ 0, nRejectingInsert );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeTrack::AppendDeleteRange: Block not supported!" );
+ }
+ SetInDeleteTop( false );
+ }
+ }
+ EndBlockModify( GetActionMax() );
+}
+
+void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange,
+ ScDocument* pRefDoc, SCCOL nDx, SCROW nDy, SCTAB nDz,
+ sal_uLong nRejectingInsert )
+{
+ ScRange aTrackRange( rOrgRange );
+ if ( nDx )
+ {
+ aTrackRange.aStart.IncCol( -nDx );
+ aTrackRange.aEnd.IncCol( -nDx );
+ }
+ if ( nDy )
+ {
+ aTrackRange.aStart.IncRow( -nDy );
+ aTrackRange.aEnd.IncRow( -nDy );
+ }
+ if ( nDz )
+ {
+ aTrackRange.aStart.IncTab( -nDz );
+ aTrackRange.aEnd.IncTab( -nDz );
+ }
+ ScChangeActionDel* pAct = new ScChangeActionDel( &rDoc, aTrackRange, nDx, nDy,
+ this );
+ // TabDelete not Contents; they are in separate columns
+ if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 &&
+ rOrgRange.aEnd.Col() == rDoc.MaxCol() && rOrgRange.aEnd.Row() == rDoc.MaxRow()) )
+ LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz );
+ if ( nRejectingInsert )
+ {
+ pAct->SetRejectAction( nRejectingInsert );
+ pAct->SetState( SC_CAS_ACCEPTED );
+ }
+ Append( pAct );
+}
+
+void ScChangeTrack::LookUpContents( const ScRange& rOrgRange,
+ ScDocument* pRefDoc, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ if (!pRefDoc)
+ return;
+
+ ScAddress aPos;
+ ScBigAddress aBigPos;
+ ScCellIterator aIter( *pRefDoc, rOrgRange );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (!ScChangeActionContent::GetContentCellType(aIter.getRefCellValue()))
+ continue;
+
+ aBigPos.Set( aIter.GetPos().Col() + nDx, aIter.GetPos().Row() + nDy,
+ aIter.GetPos().Tab() + nDz );
+ ScChangeActionContent* pContent = SearchContentAt( aBigPos, nullptr );
+ if (pContent)
+ continue;
+
+ // Untracked Contents
+ aPos.Set( aIter.GetPos().Col() + nDx, aIter.GetPos().Row() + nDy,
+ aIter.GetPos().Tab() + nDz );
+
+ GenerateDelContent(aPos, aIter.getCellValue(), pRefDoc);
+ // The Content is _not_ added with AddContent here, but in UpdateReference.
+ // We do this in order to e.g. handle intersecting Deletes correctly
+ }
+}
+
+void ScChangeTrack::AppendMove( const ScRange& rFromRange,
+ const ScRange& rToRange, ScDocument* pRefDoc )
+{
+ ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this );
+ LookUpContents( rToRange, pRefDoc, 0, 0, 0 ); // Overwritten Contents
+ Append( pAct );
+}
+
+bool ScChangeTrack::IsMatrixFormulaRangeDifferent(
+ const ScCellValue& rOldCell, const ScCellValue& rNewCell )
+{
+ SCCOL nC1, nC2;
+ SCROW nR1, nR2;
+ nC1 = nC2 = 0;
+ nR1 = nR2 = 0;
+
+ if (rOldCell.getType() == CELLTYPE_FORMULA && rOldCell.getFormula()->GetMatrixFlag() == ScMatrixMode::Formula)
+ rOldCell.getFormula()->GetMatColsRows(nC1, nR1);
+
+ if (rNewCell.getType() == CELLTYPE_FORMULA && rNewCell.getFormula()->GetMatrixFlag() == ScMatrixMode::Formula)
+ rNewCell.getFormula()->GetMatColsRows(nC1, nR1);
+
+ return nC1 != nC2 || nR1 != nR2;
+}
+
+void ScChangeTrack::AppendContent(
+ const ScAddress& rPos, const ScCellValue& rOldCell, sal_uLong nOldFormat, ScDocument* pRefDoc )
+{
+ if ( !pRefDoc )
+ pRefDoc = &rDoc;
+
+ OUString aOldValue = ScChangeActionContent::GetStringOfCell(rOldCell, pRefDoc, nOldFormat);
+
+ ScCellValue aNewCell;
+ aNewCell.assign(rDoc, rPos);
+ OUString aNewValue = ScChangeActionContent::GetStringOfCell(aNewCell, &rDoc, rPos);
+
+ if (aOldValue != aNewValue || IsMatrixFormulaRangeDifferent(rOldCell, aNewCell))
+ { // Only track real changes
+ ScRange aRange( rPos );
+ ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
+ pAct->SetOldValue(rOldCell, pRefDoc, &rDoc, nOldFormat);
+ pAct->SetNewValue(aNewCell, &rDoc);
+ Append( pAct );
+ }
+}
+
+void ScChangeTrack::AppendContent( const ScAddress& rPos,
+ const ScDocument* pRefDoc )
+{
+ ScCellValue aOldCell;
+ aOldCell.assign(*pRefDoc, rPos);
+ OUString aOldValue = ScChangeActionContent::GetStringOfCell(aOldCell, pRefDoc, rPos);
+
+ ScCellValue aNewCell;
+ aNewCell.assign(rDoc, rPos);
+ OUString aNewValue = ScChangeActionContent::GetStringOfCell(aNewCell, &rDoc, rPos);
+
+ if (aOldValue != aNewValue || IsMatrixFormulaRangeDifferent(aOldCell, aNewCell))
+ { // Only track real changes
+ ScRange aRange( rPos );
+ ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
+ pAct->SetOldValue(aOldCell, pRefDoc, &rDoc);
+ pAct->SetNewValue(aNewCell, &rDoc);
+ Append( pAct );
+ }
+}
+
+void ScChangeTrack::AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell )
+{
+ if (ScChangeActionContent::NeedsNumberFormat(rOldCell))
+ AppendContent(rPos, rOldCell, rDoc.GetNumberFormat(rPos), &rDoc);
+ else
+ AppendContent(rPos, rOldCell, 0, &rDoc);
+}
+
+void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange,
+ ScDocument* pRefDoc )
+{
+ if ( !pLastCutMove )
+ return;
+
+ // Do not link ToRange with Deletes and don't change its size
+ // This is actually unnecessary, as a delete triggers a ResetLastCut
+ // in ScViewFunc::PasteFromClip before that
+ ScBigRange& r = pLastCutMove->GetBigRange();
+ r.aEnd.SetCol( -1 );
+ r.aEnd.SetRow( -1 );
+ r.aEnd.SetTab( -1 );
+ r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) );
+ r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) );
+ r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) );
+ // Contents in FromRange we should overwrite
+ LookUpContents( rRange, pRefDoc, 0, 0, 0 );
+}
+
+void ScChangeTrack::AppendContentRange( const ScRange& rRange,
+ ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction,
+ ScChangeActionClipMode eClipMode )
+{
+ if ( eClipMode == SC_CACM_CUT )
+ {
+ ResetLastCut();
+ pLastCutMove.reset(new ScChangeActionMove( rRange, rRange, this ));
+ SetLastCutMoveRange( rRange, pRefDoc );
+ }
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ bool bDoContents;
+ if ( eClipMode == SC_CACM_PASTE && HasLastCut() )
+ {
+ bDoContents = false;
+ SetInPasteCut( true );
+ // Adjust Paste and Cut; Paste can be larger a Range
+ ScRange aRange( rRange );
+ ScBigRange& r = pLastCutMove->GetBigRange();
+ SCCOL nTmpCol;
+ if ( (nTmpCol = static_cast<SCCOL>(r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) )
+ {
+ aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol );
+ nCol1 += nTmpCol + 1;
+ bDoContents = true;
+ }
+ SCROW nTmpRow;
+ if ( (nTmpRow = static_cast<SCROW>(r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) )
+ {
+ aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow );
+ nRow1 += nTmpRow + 1;
+ bDoContents = true;
+ }
+ SCTAB nTmpTab;
+ if ( (nTmpTab = static_cast<SCTAB>(r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) )
+ {
+ aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab );
+ nTab1 += nTmpTab + 1;
+ bDoContents = true;
+ }
+ r = aRange;
+ Undo( nStartLastCut, nEndLastCut ); // Remember Cuts here
+ // StartAction only after Undo!
+ nStartAction = GetActionMax() + 1;
+ StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
+ // Contents to overwrite in ToRange
+ LookUpContents( aRange, pRefDoc, 0, 0, 0 );
+ pLastCutMove->SetStartLastCut( nStartLastCut );
+ pLastCutMove->SetEndLastCut( nEndLastCut );
+ Append( pLastCutMove.release() );
+ ResetLastCut();
+ SetInPasteCut( false );
+ }
+ else
+ {
+ bDoContents = true;
+ nStartAction = GetActionMax() + 1;
+ StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
+ }
+ if ( bDoContents )
+ {
+ ScAddress aPos;
+ for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
+ {
+ aPos.SetTab( nTab );
+ // AppendContent() is a no-op if both cells are empty.
+ SCCOL lastCol = std::max( pRefDoc->ClampToAllocatedColumns( nTab, nCol2 ),
+ rDoc.ClampToAllocatedColumns( nTab, nCol2 ));
+ for ( SCCOL nCol = nCol1; nCol <= lastCol; nCol++ )
+ {
+ aPos.SetCol( nCol );
+ SCROW lastRow = std::max( pRefDoc->GetLastDataRow( nTab, nCol, nCol, nRow2 ),
+ rDoc.GetLastDataRow( nTab, nCol, nCol, nRow2 ));
+ for ( SCROW nRow = nRow1; nRow <= lastRow; nRow++ )
+ {
+ aPos.SetRow( nRow );
+ AppendContent( aPos, pRefDoc );
+ }
+ }
+ }
+ }
+ nEndAction = GetActionMax();
+ EndBlockModify( nEndAction );
+ if ( eClipMode == SC_CACM_CUT )
+ {
+ nStartLastCut = nStartAction;
+ nEndLastCut = nEndAction;
+ }
+}
+
+void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument& rRefDoc,
+ sal_uLong& nStartAction, sal_uLong& nEndAction )
+{
+ ScCellIterator aIter(rRefDoc, ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB));
+ if (aIter.first())
+ {
+ nStartAction = GetActionMax() + 1;
+ StartBlockModify( ScChangeTrackMsgType::Append, nStartAction );
+ SvNumberFormatter* pFormatter = rRefDoc.GetFormatTable();
+ do
+ {
+ const ScAddress& rPos = aIter.GetPos();
+ const ScPatternAttr* pPat = rRefDoc.GetPattern(rPos);
+ AppendContent(
+ rPos, aIter.getCellValue(), pPat->GetNumberFormat(pFormatter), &rRefDoc);
+ }
+ while (aIter.next());
+
+ nEndAction = GetActionMax();
+ EndBlockModify( nEndAction );
+ }
+ else
+ nStartAction = nEndAction = 0;
+}
+
+ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly(
+ const ScAddress& rPos, const ScCellValue& rOldCell, const ScCellValue& rNewCell,
+ sal_uLong nOldFormat, sal_uLong nNewFormat )
+{
+ ScRange aRange( rPos );
+ ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
+ pAct->SetOldNewCells(rOldCell, nOldFormat, rNewCell, nNewFormat, &rDoc);
+ Append( pAct );
+ return pAct;
+}
+
+void ScChangeTrack::AppendInsert( const ScRange& rRange, bool bEndOfList )
+{
+ ScChangeActionIns* pAct = new ScChangeActionIns(&rDoc, rRange, bEndOfList);
+ Append( pAct );
+}
+
+void ScChangeTrack::DeleteCellEntries( std::vector<ScChangeActionContent*>& rCellList,
+ const ScChangeAction* pDeletor )
+{
+ for (ScChangeActionContent* pContent : rCellList)
+ {
+ pContent->RemoveDeletedIn( pDeletor );
+ if ( IsGenerated( pContent->GetActionNumber() ) &&
+ !pContent->IsDeletedIn() )
+ DeleteGeneratedDelContent( pContent );
+ }
+ rCellList.clear();
+}
+
+ScChangeActionContent* ScChangeTrack::GenerateDelContent(
+ const ScAddress& rPos, const ScCellValue& rCell, const ScDocument* pFromDoc )
+{
+ ScChangeActionContent* pContent = new ScChangeActionContent(
+ ScRange( rPos ) );
+ pContent->SetActionNumber( --nGeneratedMin );
+ // Only NewValue
+ ScChangeActionContent::SetValue( pContent->maNewValue, pContent->maNewCell,
+ rPos, rCell, pFromDoc, &rDoc );
+ // pNextContent and pPrevContent are not set
+ if ( pFirstGeneratedDelContent )
+ { // Insert at front
+ pFirstGeneratedDelContent->pPrev = pContent;
+ pContent->pNext = pFirstGeneratedDelContent;
+ }
+ pFirstGeneratedDelContent = pContent;
+ aGeneratedMap.insert( std::make_pair( nGeneratedMin, pContent ) );
+ NotifyModified( ScChangeTrackMsgType::Append, nGeneratedMin, nGeneratedMin );
+ return pContent;
+}
+
+void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent )
+{
+ sal_uLong nAct = pContent->GetActionNumber();
+ aGeneratedMap.erase( nAct );
+ if ( pFirstGeneratedDelContent == pContent )
+ pFirstGeneratedDelContent = static_cast<ScChangeActionContent*>(pContent->pNext);
+ if ( pContent->pNext )
+ pContent->pNext->pPrev = pContent->pPrev;
+ if ( pContent->pPrev )
+ pContent->pPrev->pNext = pContent->pNext;
+ delete pContent;
+ NotifyModified( ScChangeTrackMsgType::Remove, nAct, nAct );
+ if ( nAct == nGeneratedMin )
+ ++nGeneratedMin; // Only after NotifyModified due to IsGenerated!
+}
+
+ScChangeActionContent* ScChangeTrack::SearchContentAt(
+ const ScBigAddress& rPos, const ScChangeAction* pButNotThis ) const
+{
+ SCSIZE nSlot = ComputeContentSlot( rPos.Row() );
+ for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p;
+ p = p->GetNextInSlot() )
+ {
+ if ( p != pButNotThis && !p->IsDeletedIn() &&
+ p->GetBigRange().aStart == rPos )
+ {
+ ScChangeActionContent* pContent = p->GetTopContent();
+ if ( !pContent->IsDeletedIn() )
+ return pContent;
+ }
+ }
+ return nullptr;
+}
+
+void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent,
+ ScChangeAction* pDependent )
+{
+ ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent );
+ pDependent->AddLink( pParent, pLink );
+ if ( aModifiedLink.IsSet() )
+ {
+ sal_uLong nMod = pParent->GetActionNumber();
+ NotifyModified( ScChangeTrackMsgType::Parent, nMod, nMod );
+ }
+}
+
+void ScChangeTrack::Dependencies( ScChangeAction* pAct )
+{
+ // Find the last dependency for Col/Row/Tab each
+ // Concatenate Content at the same position
+ // Move dependencies
+ ScChangeActionType eActType = pAct->GetType();
+ if ( eActType == SC_CAT_REJECT ||
+ (eActType == SC_CAT_MOVE && pAct->IsRejecting()) )
+ return ; // These Rejects are not dependent
+
+ if ( eActType == SC_CAT_CONTENT )
+ {
+ if ( !(static_cast<ScChangeActionContent*>(pAct)->GetNextContent() ||
+ static_cast<ScChangeActionContent*>(pAct)->GetPrevContent()) )
+ { // Concatenate Contents at same position
+ ScChangeActionContent* pContent = SearchContentAt(
+ pAct->GetBigRange().aStart, pAct );
+ if ( pContent )
+ {
+ pContent->SetNextContent( static_cast<ScChangeActionContent*>(pAct) );
+ static_cast<ScChangeActionContent*>(pAct)->SetPrevContent( pContent );
+ }
+ }
+ const ScCellValue& rCell = static_cast<ScChangeActionContent*>(pAct)->GetNewCell();
+ if ( ScChangeActionContent::GetContentCellType(rCell) == SC_CACCT_MATREF )
+ {
+ ScAddress aOrg;
+ bool bOrgFound = rCell.getFormula()->GetMatrixOrigin(rDoc, aOrg);
+ ScChangeActionContent* pContent = (bOrgFound ? SearchContentAt( aOrg, pAct ) : nullptr);
+ if ( pContent && pContent->IsMatrixOrigin() )
+ {
+ AddDependentWithNotify( pContent, pAct );
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeTrack::Dependencies: MatOrg not found" );
+ }
+ }
+ }
+
+ if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) )
+ return ; // No Dependencies
+ if ( pAct->IsRejecting() )
+ return ; // Except for Content no Dependencies
+
+ // Insert in a corresponding Insert depends on it or else we would need
+ // to split the preceding one.
+ // Intersecting Inserts and Deletes are not dependent, everything else
+ // is dependent.
+ // The Insert last linked in is at the beginning of a chain, just the way we need it
+
+ const ScBigRange& rRange = pAct->GetBigRange();
+ bool bActNoInsert = !pAct->IsInsertType();
+ bool bActColDel = ( eActType == SC_CAT_DELETE_COLS );
+ bool bActRowDel = ( eActType == SC_CAT_DELETE_ROWS );
+ bool bActTabDel = ( eActType == SC_CAT_DELETE_TABS );
+
+ if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS ||
+ (bActNoInsert && !bActRowDel && !bActTabDel)) )
+ {
+ for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() )
+ {
+ ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
+ if ( !pTest->IsRejected() &&
+ pTest->GetBigRange().Intersects( rRange ) )
+ {
+ AddDependentWithNotify( pTest, pAct );
+ break; // for
+ }
+ }
+ }
+ if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS ||
+ (bActNoInsert && !bActColDel && !bActTabDel)) )
+ {
+ for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() )
+ {
+ ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
+ if ( !pTest->IsRejected() &&
+ pTest->GetBigRange().Intersects( rRange ) )
+ {
+ AddDependentWithNotify( pTest, pAct );
+ break; // for
+ }
+ }
+ }
+ if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS ||
+ (bActNoInsert && !bActColDel && !bActRowDel)) )
+ {
+ for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() )
+ {
+ ScChangeActionIns* pTest = static_cast<ScChangeActionIns*>(pL->GetAction());
+ if ( !pTest->IsRejected() &&
+ pTest->GetBigRange().Intersects( rRange ) )
+ {
+ AddDependentWithNotify( pTest, pAct );
+ break; // for
+ }
+ }
+ }
+
+ if ( !pLinkMove )
+ return;
+
+ if ( eActType == SC_CAT_CONTENT )
+ { // Content is depending on FromRange
+ const ScBigAddress& rPos = rRange.aStart;
+ for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
+ {
+ ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
+ if ( !pTest->IsRejected() &&
+ pTest->GetFromRange().Contains( rPos ) )
+ {
+ AddDependentWithNotify( pTest, pAct );
+ }
+ }
+ }
+ else if ( eActType == SC_CAT_MOVE )
+ { // Move FromRange is depending on ToRange
+ const ScBigRange& rFromRange = static_cast<ScChangeActionMove*>(pAct)->GetFromRange();
+ for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
+ {
+ ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
+ if ( !pTest->IsRejected() &&
+ pTest->GetBigRange().Intersects( rFromRange ) )
+ {
+ AddDependentWithNotify( pTest, pAct );
+ }
+ }
+ }
+ else
+ { // Inserts and Deletes are depending as soon as they cross FromRange or
+ // ToRange
+ for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
+ {
+ ScChangeActionMove* pTest = static_cast<ScChangeActionMove*>(pL->GetAction());
+ if ( !pTest->IsRejected() &&
+ (pTest->GetFromRange().Intersects( rRange ) ||
+ pTest->GetBigRange().Intersects( rRange )) )
+ {
+ AddDependentWithNotify( pTest, pAct );
+ }
+ }
+ }
+}
+
+void ScChangeTrack::Remove( ScChangeAction* pRemove )
+{
+ // Remove from Track
+ sal_uLong nAct = pRemove->GetActionNumber();
+ aMap.erase( nAct );
+ if ( nAct == nActionMax )
+ --nActionMax;
+ if ( pRemove == pLast )
+ pLast = pRemove->pPrev;
+ if ( pRemove == pFirst )
+ pFirst = pRemove->pNext;
+ if ( nAct == nMarkLastSaved )
+ nMarkLastSaved =
+ ( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 );
+
+ // Remove from global chain
+ if ( pRemove->pNext )
+ pRemove->pNext->pPrev = pRemove->pPrev;
+ if ( pRemove->pPrev )
+ pRemove->pPrev->pNext = pRemove->pNext;
+
+ // Don't delete Dependencies
+ // That happens automatically on delete by LinkEntry without traversing lists
+ if ( aModifiedLink.IsSet() )
+ {
+ NotifyModified( ScChangeTrackMsgType::Remove, nAct, nAct );
+ if ( pRemove->GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pRemove);
+ if ( ( pContent = pContent->GetPrevContent() ) != nullptr )
+ {
+ sal_uLong nMod = pContent->GetActionNumber();
+ NotifyModified( ScChangeTrackMsgType::Change, nMod, nMod );
+ }
+ }
+ else if ( pLast )
+ NotifyModified( ScChangeTrackMsgType::Change, pFirst->GetActionNumber(),
+ pLast->GetActionNumber() );
+ }
+
+ if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT )
+ { // Content is reused!
+ ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pRemove);
+ pContent->RemoveAllLinks();
+ pContent->ClearTrack();
+ pContent->pNext = pContent->pPrev = nullptr;
+ pContent->pNextContent = pContent->pPrevContent = nullptr;
+ }
+}
+
+void ScChangeTrack::Undo( sal_uLong nStartAction, sal_uLong nEndAction, bool bMerge )
+{
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ if ( bMerge )
+ {
+ SetMergeState( SC_CTMS_UNDO );
+ }
+
+ if ( nStartAction == 0 )
+ ++nStartAction;
+ if ( nEndAction > nActionMax )
+ nEndAction = nActionMax;
+ if ( nEndAction && nStartAction <= nEndAction )
+ {
+ if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut &&
+ !IsInPasteCut() )
+ ResetLastCut();
+ StartBlockModify( ScChangeTrackMsgType::Remove, nStartAction );
+ for ( sal_uLong j = nEndAction; j >= nStartAction; --j )
+ { // Traverse backwards to recycle nActionMax and for faster access via pLast
+ // Deletes are in right order
+ ScChangeAction* pAct = IsLastAction(j) ? pLast : GetAction(j);
+
+ if (!pAct)
+ continue;
+
+ if ( pAct->IsDeleteType() )
+ {
+ if (j == nEndAction || (pAct != pLast && static_cast<ScChangeActionDel*>(pAct)->IsTopDelete()))
+ {
+ SetInDeleteTop( true );
+ SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->GetOverAllRange().MakeRange( rDoc ) );
+ }
+ }
+ UpdateReference( pAct, true );
+ SetInDeleteTop( false );
+ Remove( pAct );
+ if ( IsInPasteCut() )
+ {
+ aPasteCutMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
+ continue;
+ }
+
+ if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE )
+ {
+ ScChangeActionMove* pMove = static_cast<ScChangeActionMove*>(pAct);
+ sal_uLong nStart = pMove->GetStartLastCut();
+ sal_uLong nEnd = pMove->GetEndLastCut();
+ if ( nStart && nStart <= nEnd )
+ { // Recover LastCut
+ // Break Links before Cut Append!
+ pMove->RemoveAllLinks();
+ StartBlockModify( ScChangeTrackMsgType::Append, nStart );
+ for ( sal_uLong nCut = nStart; nCut <= nEnd; nCut++ )
+ {
+ ScChangeActionMap::iterator itCut = aPasteCutMap.find( nCut );
+
+ if ( itCut != aPasteCutMap.end() )
+ {
+ OSL_ENSURE( aMap.find( nCut ) == aMap.end(), "ScChangeTrack::Undo: nCut dup" );
+ Append( itCut->second, nCut );
+ aPasteCutMap.erase( itCut );
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeTrack::Undo: nCut not found" );
+ }
+ }
+ EndBlockModify( nEnd );
+ ResetLastCut();
+ nStartLastCut = nStart;
+ nEndLastCut = nEnd;
+ pLastCutMove.reset(pMove);
+ SetLastCutMoveRange(
+ pMove->GetFromRange().MakeRange( rDoc ), &rDoc );
+ }
+ else
+ delete pMove;
+ }
+ else
+ delete pAct;
+ }
+ EndBlockModify( nEndAction );
+ }
+
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ if ( bMerge )
+ {
+ SetMergeState( SC_CTMS_OTHER );
+ }
+}
+
+bool ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, sal_uLong nFirstMerge )
+{
+ if ( rAction.IsRejected() )
+ return true; // There's still a suitable Reject Action coming
+
+ if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge )
+ return true; // There it is
+
+ return false; // Everything else
+}
+
+void ScChangeTrack::MergePrepare( const ScChangeAction* pFirstMerge, bool bShared )
+{
+ SetMergeState( SC_CTMS_PREPARE );
+ sal_uLong nFirstMerge = pFirstMerge->GetActionNumber();
+ ScChangeAction* pAct = GetLast();
+ if ( pAct )
+ {
+ SetLastMerge( pAct->GetActionNumber() );
+ while ( pAct )
+ { // Traverse backwards; Deletes in right order
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
+ {
+ if ( pAct->IsDeleteType() )
+ {
+ if ( static_cast<ScChangeActionDel*>(pAct)->IsTopDelete() )
+ {
+ SetInDeleteTop( true );
+ SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->
+ GetOverAllRange().MakeRange( rDoc ) );
+ }
+ }
+ UpdateReference( pAct, true );
+ SetInDeleteTop( false );
+ pAct->DeleteCellEntries(); // Else segfault in Track Clear()
+ }
+ pAct = ( pAct == pFirstMerge ? nullptr : pAct->GetPrev() );
+ }
+ }
+ SetMergeState( SC_CTMS_OTHER ); // Preceding by default MergeOther!
+}
+
+void ScChangeTrack::MergeOwn( ScChangeAction* pAct, sal_uLong nFirstMerge, bool bShared )
+{
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ if ( !bShared && ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
+ return;
+
+ SetMergeState( SC_CTMS_OWN );
+ if ( pAct->IsDeleteType() )
+ {
+ if ( static_cast<ScChangeActionDel*>(pAct)->IsTopDelete() )
+ {
+ SetInDeleteTop( true );
+ SetInDeleteRange( static_cast<ScChangeActionDel*>(pAct)->
+ GetOverAllRange().MakeRange( rDoc ) );
+ }
+ }
+ UpdateReference( pAct, false );
+ SetInDeleteTop( false );
+ SetMergeState( SC_CTMS_OTHER ); // Preceding by default MergeOther!
+}
+
+void ScChangeTrack::UpdateReference( ScChangeAction* pAct, bool bUndo )
+{
+ ScChangeActionType eActType = pAct->GetType();
+ if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT )
+ return ;
+
+ // Formula cells are not in the Document!
+ bool bOldAutoCalc = rDoc.GetAutoCalc();
+ rDoc.SetAutoCalc( false );
+ bool bOldNoListening = rDoc.GetNoListening();
+ rDoc.SetNoListening( true );
+
+ // Formula cells ExpandRefs synchronized to the ones in the Document!
+ bool bOldExpandRefs = rDoc.IsExpandRefs();
+ if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) )
+ rDoc.SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );
+
+ if ( pAct->IsDeleteType() )
+ {
+ SetInDeleteUndo( bUndo );
+ SetInDelete( true );
+ }
+ else if ( GetMergeState() == SC_CTMS_OWN )
+ {
+ // Recover references of formula cells
+ // Previous MergePrepare behaved like a Delete when Inserting
+ if ( pAct->IsInsertType() )
+ SetInDeleteUndo( true );
+ }
+
+ // First the generated ones, as if they were tracked previously!
+ if ( pFirstGeneratedDelContent )
+ UpdateReference( reinterpret_cast<ScChangeAction**>(&pFirstGeneratedDelContent), pAct,
+ bUndo );
+ UpdateReference( &pFirst, pAct, bUndo );
+
+ SetInDelete( false );
+ SetInDeleteUndo( false );
+
+ rDoc.SetExpandRefs( bOldExpandRefs );
+ rDoc.SetNoListening( bOldNoListening );
+ rDoc.SetAutoCalc( bOldAutoCalc );
+}
+
+void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction,
+ ScChangeAction* pAct, bool bUndo )
+{
+ ScChangeActionType eActType = pAct->GetType();
+ bool bGeneratedDelContents =
+ ( ppFirstAction == reinterpret_cast<ScChangeAction**>(&pFirstGeneratedDelContent) );
+ const ScBigRange& rOrgRange = pAct->GetBigRange();
+ ScBigRange aRange( rOrgRange );
+ ScBigRange aDelRange( rOrgRange );
+ sal_Int32 nDx, nDy, nDz;
+ nDx = nDy = nDz = 0;
+ UpdateRefMode eMode = URM_INSDEL;
+ bool bDel = false;
+ switch ( eActType )
+ {
+ case SC_CAT_INSERT_COLS :
+ aRange.aEnd.SetCol( ScBigRange::nRangeMax );
+ nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1;
+ break;
+ case SC_CAT_INSERT_ROWS :
+ aRange.aEnd.SetRow( ScBigRange::nRangeMax );
+ nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1;
+ break;
+ case SC_CAT_INSERT_TABS :
+ aRange.aEnd.SetTab( ScBigRange::nRangeMax );
+ nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1;
+ break;
+ case SC_CAT_DELETE_COLS :
+ aRange.aEnd.SetCol( ScBigRange::nRangeMax );
+ nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1);
+ aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 );
+ bDel = true;
+ break;
+ case SC_CAT_DELETE_ROWS :
+ aRange.aEnd.SetRow( ScBigRange::nRangeMax );
+ nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1);
+ aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 );
+ bDel = true;
+ break;
+ case SC_CAT_DELETE_TABS :
+ aRange.aEnd.SetTab( ScBigRange::nRangeMax );
+ nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1);
+ aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 );
+ bDel = true;
+ break;
+ case SC_CAT_MOVE :
+ eMode = URM_MOVE;
+ static_cast<ScChangeActionMove*>(pAct)->GetDelta( nDx, nDy, nDz );
+ break;
+ default:
+ OSL_FAIL( "ScChangeTrack::UpdateReference: unknown Type" );
+ }
+ if ( bUndo )
+ {
+ nDx = -nDx;
+ nDy = -nDy;
+ nDz = -nDz;
+ }
+ if ( bDel )
+ { // For this mechanism we assume:
+ // There's only a whole, simple deleted row/column
+ ScChangeActionDel* pActDel = static_cast<ScChangeActionDel*>(pAct);
+ if ( !bUndo )
+ { // Delete
+ ScChangeActionType eInsType = SC_CAT_NONE; // for Insert Undo "Deletes"
+ switch ( eActType )
+ {
+ case SC_CAT_DELETE_COLS :
+ eInsType = SC_CAT_INSERT_COLS;
+ break;
+ case SC_CAT_DELETE_ROWS :
+ eInsType = SC_CAT_INSERT_ROWS;
+ break;
+ case SC_CAT_DELETE_TABS :
+ eInsType = SC_CAT_INSERT_TABS;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ bool bUpdate = true;
+ if ( GetMergeState() == SC_CTMS_OTHER &&
+ p->GetActionNumber() <= GetLastMerge() )
+ { // Delete in merged Document, Action in the one to be merged
+ if ( p->IsInsertType() )
+ {
+ // On Insert only adjust references if the Delete does
+ // not intersect the Insert
+ if ( !aDelRange.Intersects( p->GetBigRange() ) )
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ bUpdate = false;
+ }
+ else if ( p->GetType() == SC_CAT_CONTENT &&
+ p->IsDeletedInDelType( eInsType ) )
+ { // Content in Insert Undo "Delete"
+ // Do not adjust if this Delete would be in the Insert "Delete" (was just moved)
+ if ( aDelRange.Contains( p->GetBigRange().aStart ) )
+ bUpdate = false;
+ else
+ {
+ const ScChangeActionLinkEntry* pLink = p->GetDeletedIn();
+ while ( pLink && bUpdate )
+ {
+ const ScChangeAction* pDel = pLink->GetAction();
+ if ( pDel && pDel->GetType() == eInsType &&
+ pDel->GetBigRange().Contains( aDelRange ) )
+ bUpdate = false;
+ pLink = pLink->GetNext();
+ }
+ }
+ }
+ if ( !bUpdate )
+ continue; // for
+ }
+ if ( aDelRange.Contains( p->GetBigRange() ) )
+ {
+ // Do not adjust within a just deleted range,
+ // instead assign the range.
+ // Stack up ranges that have been deleted multiple times.
+ // Intersecting Deletes cause "multiple delete" to be set.
+ if ( !p->IsDeletedInDelType( eActType ) )
+ {
+ p->SetDeletedIn( pActDel );
+ // Add GeneratedDelContent to the to-be-deleted list
+ if ( bGeneratedDelContents )
+ pActDel->AddContent( static_cast<ScChangeActionContent*>(p) );
+ }
+ bUpdate = false;
+ }
+ else
+ {
+ // Cut off inserted ranges, if Start/End is within the Delete,
+ // but the Insert is not completely within the Delete or
+ // the Delete is not completely within the Insert.
+ // The Delete remembers which Insert it has cut off from;
+ // it can also just be a single Insert (because Delete has
+ // a single column/is a single row).
+ // There can be a lot of cut-off Moves.
+ //
+ // ! A Delete is always a single column/a single row, therefore
+ // ! 1 without calculating the intersection.
+ switch ( p->GetType() )
+ {
+ case SC_CAT_INSERT_COLS :
+ if ( eActType == SC_CAT_DELETE_COLS )
+ {
+ if ( aDelRange.Contains( p->GetBigRange().aStart ) )
+ {
+ pActDel->SetCutOffInsert(
+ static_cast<ScChangeActionIns*>(p), 1 );
+ p->GetBigRange().aStart.IncCol();
+ }
+ else if ( aDelRange.Contains( p->GetBigRange().aEnd ) )
+ {
+ pActDel->SetCutOffInsert(
+ static_cast<ScChangeActionIns*>(p), -1 );
+ p->GetBigRange().aEnd.IncCol( -1 );
+ }
+ }
+ break;
+ case SC_CAT_INSERT_ROWS :
+ if ( eActType == SC_CAT_DELETE_ROWS )
+ {
+ if ( aDelRange.Contains( p->GetBigRange().aStart ) )
+ {
+ pActDel->SetCutOffInsert(
+ static_cast<ScChangeActionIns*>(p), 1 );
+ p->GetBigRange().aStart.IncRow();
+ }
+ else if ( aDelRange.Contains( p->GetBigRange().aEnd ) )
+ {
+ pActDel->SetCutOffInsert(
+ static_cast<ScChangeActionIns*>(p), -1 );
+ p->GetBigRange().aEnd.IncRow( -1 );
+ }
+ }
+ break;
+ case SC_CAT_INSERT_TABS :
+ if ( eActType == SC_CAT_DELETE_TABS )
+ {
+ if ( aDelRange.Contains( p->GetBigRange().aStart ) )
+ {
+ pActDel->SetCutOffInsert(
+ static_cast<ScChangeActionIns*>(p), 1 );
+ p->GetBigRange().aStart.IncTab();
+ }
+ else if ( aDelRange.Contains( p->GetBigRange().aEnd ) )
+ {
+ pActDel->SetCutOffInsert(
+ static_cast<ScChangeActionIns*>(p), -1 );
+ p->GetBigRange().aEnd.IncTab( -1 );
+ }
+ }
+ break;
+ case SC_CAT_MOVE :
+ {
+ ScChangeActionMove* pMove = static_cast<ScChangeActionMove*>(p);
+ short nFrom = 0;
+ short nTo = 0;
+ if ( aDelRange.Contains( pMove->GetBigRange().aStart ) )
+ nTo = 1;
+ else if ( aDelRange.Contains( pMove->GetBigRange().aEnd ) )
+ nTo = -1;
+ if ( aDelRange.Contains( pMove->GetFromRange().aStart ) )
+ nFrom = 1;
+ else if ( aDelRange.Contains( pMove->GetFromRange().aEnd ) )
+ nFrom = -1;
+ if ( nFrom )
+ {
+ switch ( eActType )
+ {
+ case SC_CAT_DELETE_COLS :
+ if ( nFrom > 0 )
+ pMove->GetFromRange().aStart.IncCol( nFrom );
+ else
+ pMove->GetFromRange().aEnd.IncCol( nFrom );
+ break;
+ case SC_CAT_DELETE_ROWS :
+ if ( nFrom > 0 )
+ pMove->GetFromRange().aStart.IncRow( nFrom );
+ else
+ pMove->GetFromRange().aEnd.IncRow( nFrom );
+ break;
+ case SC_CAT_DELETE_TABS :
+ if ( nFrom > 0 )
+ pMove->GetFromRange().aStart.IncTab( nFrom );
+ else
+ pMove->GetFromRange().aEnd.IncTab( nFrom );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ if ( nTo )
+ {
+ switch ( eActType )
+ {
+ case SC_CAT_DELETE_COLS :
+ if ( nTo > 0 )
+ pMove->GetBigRange().aStart.IncCol( nTo );
+ else
+ pMove->GetBigRange().aEnd.IncCol( nTo );
+ break;
+ case SC_CAT_DELETE_ROWS :
+ if ( nTo > 0 )
+ pMove->GetBigRange().aStart.IncRow( nTo );
+ else
+ pMove->GetBigRange().aEnd.IncRow( nTo );
+ break;
+ case SC_CAT_DELETE_TABS :
+ if ( nTo > 0 )
+ pMove->GetBigRange().aStart.IncTab( nTo );
+ else
+ pMove->GetBigRange().aEnd.IncTab( nTo );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ if ( nFrom || nTo )
+ {
+ ScChangeActionDelMoveEntry* pLink =
+ pActDel->AddCutOffMove( pMove, nFrom, nTo );
+ pMove->AddLink( pActDel, pLink );
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ if ( bUpdate )
+ {
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ if ( p->GetType() == eActType && !p->IsRejected() &&
+ !pActDel->IsDeletedIn() &&
+ p->GetBigRange().Contains( aDelRange ) )
+ pActDel->SetDeletedIn( p ); // Slipped underneath it
+ }
+ }
+ }
+ else
+ { // Undo Delete
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ bool bUpdate = true;
+ if ( aDelRange.Contains( p->GetBigRange() ) )
+ {
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() &&
+ ( p->GetType() == SC_CAT_CONTENT ||
+ p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
+ p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) )
+ {
+ p->SetDeletedIn( pAct );
+ }
+
+ if ( p->IsDeletedInDelType( eActType ) )
+ {
+ if ( p->IsDeletedIn( pActDel ) )
+ {
+ if ( p->GetType() != SC_CAT_CONTENT ||
+ static_cast<ScChangeActionContent*>(p)->IsTopContent() )
+ { // First really remove the TopContent
+ p->RemoveDeletedIn( pActDel );
+ // Do NOT delete GeneratedDelContent from the list, we might need
+ // it later on for Reject; we delete in DeleteCellEntries
+ }
+ }
+ bUpdate = false;
+ }
+ else if ( eActType != SC_CAT_DELETE_TABS &&
+ p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) )
+ { // Do not update in deleted Tables except for when moving Tables
+ bUpdate = false;
+ }
+ if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) )
+ {
+ pActDel->RemoveDeletedIn( p );// Slipped underneath
+ bUpdate = true;
+ }
+ }
+ if ( bUpdate )
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ }
+ if ( !bGeneratedDelContents )
+ { // These are else also needed for the real Undo
+ pActDel->UndoCutOffInsert();
+ pActDel->UndoCutOffMoves();
+ }
+ }
+ }
+ else if ( eActType == SC_CAT_MOVE )
+ {
+ ScChangeActionMove* pActMove = static_cast<ScChangeActionMove*>(pAct);
+ bool bLastCutMove = ( pActMove == pLastCutMove.get() );
+ const ScBigRange& rTo = pActMove->GetBigRange();
+ const ScBigRange& rFrom = pActMove->GetFromRange();
+ if ( !bUndo )
+ { // Move
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ if ( p->GetType() == SC_CAT_CONTENT )
+ {
+ // Delete content in Target (Move Content to Source)
+ if ( rTo.Contains( p->GetBigRange() ) )
+ {
+ if ( !p->IsDeletedIn( pActMove ) )
+ {
+ p->SetDeletedIn( pActMove );
+ // Add GeneratedDelContent to the to-be-deleted list
+ if ( bGeneratedDelContents )
+ pActMove->AddContent( static_cast<ScChangeActionContent*>(p) );
+ }
+ }
+ else if ( bLastCutMove &&
+ p->GetActionNumber() > nEndLastCut &&
+ rFrom.Contains( p->GetBigRange() ) )
+ { // Paste Cut: insert new Content inserted after stays
+ // Split up the ContentChain
+ ScChangeActionContent *pHere, *pTmp;
+ pHere = static_cast<ScChangeActionContent*>(p);
+ for (;;)
+ {
+ pTmp = pHere->GetPrevContent();
+ if (!pTmp || pTmp->GetActionNumber() <= nEndLastCut)
+ break;
+ pHere = pTmp;
+ }
+ if ( pTmp )
+ { // Becomes TopContent of the Move
+ pTmp->SetNextContent( nullptr );
+ pHere->SetPrevContent( nullptr );
+ }
+ do
+ { // Recover dependency from FromRange
+ AddDependentWithNotify( pActMove, pHere );
+ } while ( ( pHere = pHere->GetNextContent() ) != nullptr );
+ }
+ // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
+ else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
+ p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz );
+ }
+ }
+ }
+ else
+ { // Undo Move
+ bool bActRejected = pActMove->IsRejected();
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ if ( p->GetType() == SC_CAT_CONTENT )
+ {
+ // Move Content into Target if not deleted else to delete (FIXME: What?)
+ if ( p->IsDeletedIn( pActMove ) )
+ {
+ if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
+ { // First really remove the TopContent
+ p->RemoveDeletedIn( pActMove );
+ // Do NOT delete GeneratedDelContent from the list, we might need
+ // it later on for Reject; we delete in DeleteCellEntries
+ }
+ }
+ // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
+ else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
+ p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz );
+ if ( bActRejected &&
+ static_cast<ScChangeActionContent*>(p)->IsTopContent() &&
+ rFrom.Contains( p->GetBigRange() ) )
+ { // Recover dependency to write Content
+ ScChangeActionLinkEntry* pLink =
+ pActMove->AddDependent( p );
+ p->AddLink( pActMove, pLink );
+ }
+ }
+ }
+ }
+ }
+ else
+ { // Insert/Undo Insert
+ switch ( GetMergeState() )
+ {
+ case SC_CTMS_NONE :
+ case SC_CTMS_OTHER :
+ {
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ }
+ }
+ break;
+ case SC_CTMS_PREPARE :
+ {
+ // "Delete" in Insert-Undo
+ const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
+ while ( pLink )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pLink->GetAction());
+ if ( p )
+ p->SetDeletedIn( pAct );
+ pLink = pLink->GetNext();
+ }
+
+ // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ ( p->GetType() == SC_CAT_CONTENT ||
+ p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
+ p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
+ pAct->GetBigRange().Intersects( p->GetBigRange() ) )
+ {
+ p->SetDeletedIn( pAct );
+ }
+ }
+
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ if ( !p->IsDeletedIn( pAct )
+ // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
+ && p->GetActionNumber() <= pAct->GetActionNumber() )
+ {
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ }
+ }
+ }
+ break;
+ case SC_CTMS_OWN :
+ {
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ continue; // for
+ if ( !p->IsDeletedIn( pAct )
+ // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
+ && p->GetActionNumber() <= pAct->GetActionNumber() )
+ {
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ }
+ }
+ // Undo "Delete" in Insert-Undo
+ const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
+ while ( pLink )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pLink->GetAction());
+ if ( p )
+ p->RemoveDeletedIn( pAct );
+ pLink = pLink->GetNext();
+ }
+
+ // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ ( p->GetType() == SC_CAT_CONTENT ||
+ p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
+ p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
+ pAct->GetBigRange().Intersects( p->GetBigRange() ) )
+ {
+ p->RemoveDeletedIn( pAct );
+ }
+ }
+ }
+ break;
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ case SC_CTMS_UNDO :
+ {
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
+ ( p->GetType() == SC_CAT_CONTENT ||
+ p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
+ p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
+ pAct->GetBigRange().Intersects( p->GetBigRange() ) )
+ {
+ p->SetDeletedIn( pAct );
+ }
+ }
+
+ for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
+ {
+ if ( p == pAct )
+ {
+ continue;
+ }
+ if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() )
+ {
+ p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+void ScChangeTrack::GetDependents( ScChangeAction* pAct,
+ ScChangeActionMap& rMap, bool bListMasterDelete, bool bAllFlat ) const
+{
+ //TODO: bAllFlat==TRUE: called internally from Accept or Reject
+ //TODO: => Generated will not be added
+ bool bIsDelete = pAct->IsDeleteType();
+ bool bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() );
+
+ const ScChangeAction* pCur = nullptr;
+ ::std::stack<ScChangeAction*> cStack;
+ cStack.push(pAct);
+
+ while ( !cStack.empty() )
+ {
+ pCur = cStack.top();
+ cStack.pop();
+
+ if ( pCur->IsInsertType() )
+ {
+ const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p != pAct )
+ {
+ if ( bAllFlat )
+ {
+ sal_uLong n = p->GetActionNumber();
+ if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
+ if ( p->HasDependent() )
+ cStack.push( p );
+ }
+ else
+ {
+ if ( p->GetType() == SC_CAT_CONTENT )
+ {
+ if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ else
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ }
+ pL = pL->GetNext();
+ }
+ }
+ else if ( pCur->IsDeleteType() )
+ {
+ if ( bIsDelete )
+ { // Contents of deleted Ranges are only of interest on Delete
+ ScChangeActionDel* pDel = const_cast<ScChangeActionDel*>(static_cast<const ScChangeActionDel*>(pCur));
+ if ( !bAllFlat && bIsMasterDelete && pCur == pAct )
+ {
+ // Corresponding Deletes to this Delete to the same level,
+ // if this Delete is at the top of a Row
+ ScChangeActionType eType = pDel->GetType();
+ ScChangeAction* p = pDel;
+ for (;;)
+ {
+ p = p->GetPrev();
+ if (!p || p->GetType() != eType ||
+ static_cast<ScChangeActionDel*>(p)->IsTopDelete() )
+ break;
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ // delete this in the map too
+ rMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
+ }
+ else
+ {
+ const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p != pAct )
+ {
+ if ( bAllFlat )
+ {
+ // Only a TopContent of a chain is in LinkDeleted
+ sal_uLong n = p->GetActionNumber();
+ if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
+ if ( p->HasDeleted() ||
+ p->GetType() == SC_CAT_CONTENT )
+ cStack.push( p );
+ }
+ else
+ {
+ if ( p->IsDeleteType() )
+ { // Further TopDeletes to same level: it's not rejectable
+ if ( static_cast<ScChangeActionDel*>(p)->IsTopDelete() )
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ else
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ }
+ pL = pL->GetNext();
+ }
+ }
+ }
+ }
+ else if ( pCur->GetType() == SC_CAT_MOVE )
+ {
+ // Deleted Contents in ToRange
+ const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p != pAct && rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) ).second )
+ {
+ // Only one TopContent of a chain is in LinkDeleted
+ if ( bAllFlat && (p->HasDeleted() ||
+ p->GetType() == SC_CAT_CONTENT) )
+ cStack.push( p );
+ }
+ pL = pL->GetNext();
+ }
+ // New Contents in FromRange or new FromRange in ToRange
+ // or Inserts/Deletes in FromRange/ToRange
+ pL = pCur->GetFirstDependentEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p != pAct )
+ {
+ if ( bAllFlat )
+ {
+ sal_uLong n = p->GetActionNumber();
+ if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
+ if ( p->HasDependent() || p->HasDeleted() )
+ cStack.push( p );
+ }
+ else
+ {
+ if ( p->GetType() == SC_CAT_CONTENT )
+ {
+ if ( static_cast<ScChangeActionContent*>(p)->IsTopContent() )
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ else
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ }
+ pL = pL->GetNext();
+ }
+ }
+ else if ( pCur->GetType() == SC_CAT_CONTENT )
+ { // All changes at same position
+ ScChangeActionContent* pContent = const_cast<ScChangeActionContent*>(static_cast<const ScChangeActionContent*>(pCur));
+ // All preceding ones
+ while ( ( pContent = pContent->GetPrevContent() ) != nullptr )
+ {
+ if ( !pContent->IsRejected() )
+ rMap.insert( ::std::make_pair( pContent->GetActionNumber(), pContent ) );
+ }
+ pContent = const_cast<ScChangeActionContent*>(static_cast<const ScChangeActionContent*>(pCur));
+ // All succeeding ones
+ while ( ( pContent = pContent->GetNextContent() ) != nullptr )
+ {
+ if ( !pContent->IsRejected() )
+ rMap.insert( ::std::make_pair( pContent->GetActionNumber(), pContent ) );
+ }
+ // all MatrixReferences of a MatrixOrigin
+ const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p != pAct )
+ {
+ if ( bAllFlat )
+ {
+ sal_uLong n = p->GetActionNumber();
+ if ( !IsGenerated( n ) && rMap.insert( ::std::make_pair( n, p ) ).second )
+ if ( p->HasDependent() )
+ cStack.push( p );
+ }
+ else
+ rMap.insert( ::std::make_pair( p->GetActionNumber(), p ) );
+ }
+ pL = pL->GetNext();
+ }
+ }
+ else if ( pCur->GetType() == SC_CAT_REJECT )
+ {
+ if ( bAllFlat )
+ {
+ ScChangeAction* p = GetAction(
+ static_cast<const ScChangeActionReject*>(pCur)->GetRejectAction() );
+ if (p != pAct && rMap.find( p->GetActionNumber() ) == rMap.end())
+ cStack.push( p );
+ }
+ }
+ }
+}
+
+bool ScChangeTrack::SelectContent( ScChangeAction* pAct, bool bOldest )
+{
+ if ( pAct->GetType() != SC_CAT_CONTENT )
+ return false;
+
+ ScChangeActionContent* pContent = static_cast<ScChangeActionContent*>(pAct);
+ if ( bOldest )
+ {
+ pContent = pContent->GetTopContent();
+ for (;;)
+ {
+ ScChangeActionContent* pPrevContent = pContent->GetPrevContent();
+ if ( !pPrevContent || !pPrevContent->IsVirgin() )
+ break;
+ pContent = pPrevContent;
+ }
+ }
+
+ if ( !pContent->IsClickable() )
+ return false;
+
+ ScBigRange aBigRange( pContent->GetBigRange() );
+ const ScCellValue& rCell = (bOldest ? pContent->GetOldCell() : pContent->GetNewCell());
+ if ( ScChangeActionContent::GetContentCellType(rCell) == SC_CACCT_MATORG )
+ {
+ SCCOL nC;
+ SCROW nR;
+ rCell.getFormula()->GetMatColsRows(nC, nR);
+ aBigRange.aEnd.IncCol( nC-1 );
+ aBigRange.aEnd.IncRow( nR-1 );
+ }
+
+ if ( !aBigRange.IsValid( rDoc ) )
+ return false;
+
+ ScRange aRange( aBigRange.MakeRange( rDoc ) );
+ if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
+ aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
+ return false;
+
+ if ( pContent->HasDependent() )
+ {
+ bool bOk = true;
+ ::std::stack<ScChangeActionContent*> aRejectActions;
+ const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
+ while ( pL )
+ {
+ ScChangeAction* p = const_cast<ScChangeAction*>(pL->GetAction());
+ if ( p != pContent )
+ {
+ if ( p->GetType() == SC_CAT_CONTENT )
+ {
+ // we don't need no recursion here, do we?
+ bOk &= static_cast<ScChangeActionContent*>(p)->Select( rDoc, this,
+ bOldest, &aRejectActions );
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeTrack::SelectContent: content dependent no content" );
+ }
+ }
+ pL = pL->GetNext();
+ }
+
+ bOk &= pContent->Select( rDoc, this, bOldest, nullptr );
+ // now the matrix is inserted and new content values are ready
+
+ while ( !aRejectActions.empty() )
+ {
+ ScChangeActionContent* pNew = aRejectActions.top();
+ aRejectActions.pop();
+ ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress( rDoc ) );
+ ScCellValue aCell;
+ aCell.assign(rDoc, aPos);
+ pNew->SetNewValue(aCell, &rDoc);
+ Append( pNew );
+ }
+ return bOk;
+ }
+ else
+ return pContent->Select( rDoc, this, bOldest, nullptr );
+}
+
+void ScChangeTrack::AcceptAll()
+{
+ for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() )
+ {
+ p->Accept();
+ }
+}
+
+bool ScChangeTrack::Accept( ScChangeAction* pAct )
+{
+ if ( !pAct->IsClickable() )
+ return false;
+
+ if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionMap aActionMap;
+
+ GetDependents( pAct, aActionMap, false, true );
+
+ for( auto& rEntry : aActionMap )
+ {
+ rEntry.second->Accept();
+ }
+ }
+ pAct->Accept();
+ return true;
+}
+
+bool ScChangeTrack::RejectAll()
+{
+ bool bOk = true;
+ for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() )
+ { //TODO: Traverse backwards as dependencies attached to RejectActions
+ if ( p->IsInternalRejectable() )
+ bOk = Reject( p );
+ }
+ return bOk;
+}
+
+bool ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared )
+{
+ // #i100895# When collaboration changes are reversed, it must be possible
+ // to reject a deleted row above another deleted row.
+ if ( bShared && pAct->IsDeletedIn() )
+ pAct->RemoveAllDeletedIn();
+
+ if ( !pAct->IsRejectable() )
+ return false;
+
+ std::unique_ptr<ScChangeActionMap> pMap;
+ if ( pAct->HasDependent() )
+ {
+ pMap.reset(new ScChangeActionMap);
+ GetDependents( pAct, *pMap, false, true );
+ }
+ bool bRejected = Reject( pAct, pMap.get(), false );
+ return bRejected;
+}
+
+bool ScChangeTrack::Reject(
+ ScChangeAction* pAct, ScChangeActionMap* pMap, bool bRecursion )
+{
+ if ( !pAct->IsInternalRejectable() )
+ return false;
+
+ bool bOk = true;
+ bool bRejected = false;
+ if ( pAct->IsInsertType() )
+ {
+ if ( pAct->HasDependent() && !bRecursion )
+ {
+ OSL_ENSURE( pMap, "ScChangeTrack::Reject: Insert without map" );
+ ScChangeActionMap::reverse_iterator itChangeAction;
+ for (itChangeAction = pMap->rbegin();
+ itChangeAction != pMap->rend() && bOk; ++itChangeAction)
+ {
+ // Do not restore Contents which would end up being deleted anyways
+ if ( itChangeAction->second->GetType() == SC_CAT_CONTENT )
+ itChangeAction->second->SetRejected();
+ else if ( itChangeAction->second->IsDeleteType() )
+ itChangeAction->second->Accept(); // Deleted to Nirvana
+ else
+ bOk = Reject( itChangeAction->second, nullptr, true ); // Recursion!
+ }
+ }
+ if ( bOk )
+ {
+ bRejected = pAct->Reject( rDoc );
+ if ( bRejected )
+ {
+ // pRefDoc NULL := Do not save deleted Cells
+ AppendDeleteRange( pAct->GetBigRange().MakeRange( rDoc ), nullptr, short(0),
+ pAct->GetActionNumber() );
+ }
+ }
+ }
+ else if ( pAct->IsDeleteType() )
+ {
+ OSL_ENSURE( !pMap, "ScChangeTrack::Reject: Delete with map" );
+ ScBigRange aDelRange;
+ sal_uLong nRejectAction = pAct->GetActionNumber();
+ bool bTabDel, bTabDelOk;
+ if ( pAct->GetType() == SC_CAT_DELETE_TABS )
+ {
+ bTabDel = true;
+ aDelRange = pAct->GetBigRange();
+ bTabDelOk = pAct->Reject( rDoc );
+ bOk = bTabDelOk;
+ if ( bOk )
+ {
+ pAct = pAct->GetPrev();
+ bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS );
+ }
+ }
+ else
+ bTabDel = bTabDelOk = false;
+ ScChangeActionDel* pDel = static_cast<ScChangeActionDel*>(pAct);
+ if ( bOk )
+ {
+ aDelRange = pDel->GetOverAllRange();
+ bOk = aDelRange.IsValid( rDoc );
+ }
+ bool bOneOk = false;
+ if ( bOk )
+ {
+ ScChangeActionType eActType = pAct->GetType();
+ switch ( eActType )
+ {
+ case SC_CAT_DELETE_COLS :
+ aDelRange.aStart.SetCol( aDelRange.aEnd.Col() );
+ break;
+ case SC_CAT_DELETE_ROWS :
+ aDelRange.aStart.SetRow( aDelRange.aEnd.Row() );
+ break;
+ case SC_CAT_DELETE_TABS :
+ aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ ScChangeAction* p = pAct;
+ bool bLoop = true;
+ do
+ {
+ pDel = static_cast<ScChangeActionDel*>(p);
+ bOk = pDel->Reject( rDoc );
+ if ( bOk )
+ {
+ if ( bOneOk )
+ {
+ switch ( pDel->GetType() )
+ {
+ case SC_CAT_DELETE_COLS :
+ aDelRange.aStart.IncCol( -1 );
+ break;
+ case SC_CAT_DELETE_ROWS :
+ aDelRange.aStart.IncRow( -1 );
+ break;
+ case SC_CAT_DELETE_TABS :
+ aDelRange.aStart.IncTab( -1 );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ else
+ bOneOk = true;
+ }
+ if ( pDel->IsBaseDelete() )
+ bLoop = false;
+ else
+ p = p->GetPrev();
+ } while ( bOk && bLoop && p && p->GetType() == eActType &&
+ !static_cast<ScChangeActionDel*>(p)->IsTopDelete() );
+ }
+ bRejected = bOk;
+ if ( bOneOk || (bTabDel && bTabDelOk) )
+ {
+ // Delete Reject made UpdateReference Undo
+ ScChangeActionIns* pReject = new ScChangeActionIns( &rDoc,
+ aDelRange.MakeRange( rDoc ) );
+ pReject->SetRejectAction( nRejectAction );
+ pReject->SetState( SC_CAS_ACCEPTED );
+ Append( pReject );
+ }
+ }
+ else if ( pAct->GetType() == SC_CAT_MOVE )
+ {
+ if ( pAct->HasDependent() && !bRecursion )
+ {
+ OSL_ENSURE( pMap, "ScChangeTrack::Reject: Move without Map" );
+ ScChangeActionMap::reverse_iterator itChangeAction;
+
+ for( itChangeAction = pMap->rbegin(); itChangeAction != pMap->rend() && bOk; ++itChangeAction )
+ {
+ bOk = Reject( itChangeAction->second, nullptr, true ); // Recursion!
+ }
+ }
+ if ( bOk )
+ {
+ bRejected = pAct->Reject( rDoc );
+ if ( bRejected )
+ {
+ ScChangeActionMove* pReject = new ScChangeActionMove(
+ pAct->GetBigRange().MakeRange( rDoc ),
+ static_cast<ScChangeActionMove*>(pAct)->GetFromRange().MakeRange( rDoc ), this );
+ pReject->SetRejectAction( pAct->GetActionNumber() );
+ pReject->SetState( SC_CAS_ACCEPTED );
+ Append( pReject );
+ }
+ }
+ }
+ else if ( pAct->GetType() == SC_CAT_CONTENT )
+ {
+ ScRange aRange;
+ ScChangeActionContent* pReject;
+ if ( bRecursion )
+ pReject = nullptr;
+ else
+ {
+ aRange = pAct->GetBigRange().aStart.MakeAddress( rDoc );
+ pReject = new ScChangeActionContent( aRange );
+ ScCellValue aCell;
+ aCell.assign(rDoc, aRange.aStart);
+ pReject->SetOldValue(aCell, &rDoc, &rDoc);
+ }
+ bRejected = pAct->Reject( rDoc );
+ if ( bRejected && !bRecursion )
+ {
+ ScCellValue aCell;
+ aCell.assign(rDoc, aRange.aStart);
+ pReject->SetNewValue(aCell, &rDoc);
+ pReject->SetRejectAction( pAct->GetActionNumber() );
+ pReject->SetState( SC_CAS_ACCEPTED );
+ Append( pReject );
+ }
+ else
+ delete pReject;
+ }
+ else
+ {
+ OSL_FAIL( "ScChangeTrack::Reject: say what?" );
+ }
+
+ return bRejected;
+}
+
+bool ScChangeTrack::IsLastAction( sal_uLong nNum ) const
+{
+ return nNum == nActionMax && pLast && pLast->GetActionNumber() == nNum;
+}
+
+sal_uLong ScChangeTrack::AddLoadedGenerated(
+ const ScCellValue& rNewCell, const ScBigRange& aBigRange, const OUString& sNewValue )
+{
+ ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, rNewCell, aBigRange, &rDoc, sNewValue );
+ if ( pFirstGeneratedDelContent )
+ pFirstGeneratedDelContent->pPrev = pAct;
+ pAct->pNext = pFirstGeneratedDelContent;
+ pFirstGeneratedDelContent = pAct;
+ aGeneratedMap.insert( ::std::make_pair( pAct->GetActionNumber(), pAct ) );
+ return pAct->GetActionNumber();
+}
+
+void ScChangeTrack::AppendCloned( ScChangeAction* pAppend )
+{
+ aMap.insert( ::std::make_pair( pAppend->GetActionNumber(), pAppend ) );
+ if ( !pLast )
+ pFirst = pLast = pAppend;
+ else
+ {
+ pLast->pNext = pAppend;
+ pAppend->pPrev = pLast;
+ pLast = pAppend;
+ }
+}
+
+ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const
+{
+ if ( !pDocument )
+ {
+ return nullptr;
+ }
+
+ std::unique_ptr<ScChangeTrack> pClonedTrack(new ScChangeTrack( *pDocument ));
+ pClonedTrack->SetTimeNanoSeconds( IsTimeNanoSeconds() );
+
+ // clone generated actions
+ ::std::stack< const ScChangeAction* > aGeneratedStack;
+ const ScChangeAction* pGenerated = GetFirstGenerated();
+ while ( pGenerated )
+ {
+ aGeneratedStack.push( pGenerated );
+ pGenerated = pGenerated->GetNext();
+ }
+ while ( !aGeneratedStack.empty() )
+ {
+ pGenerated = aGeneratedStack.top();
+ aGeneratedStack.pop();
+ const ScChangeActionContent& rContent = dynamic_cast<const ScChangeActionContent&>(*pGenerated);
+ const ScCellValue& rNewCell = rContent.GetNewCell();
+ if (!rNewCell.isEmpty())
+ {
+ ScCellValue aClonedNewCell;
+ aClonedNewCell.assign(rNewCell, *pDocument);
+ OUString aNewValue = rContent.GetNewString( pDocument );
+ pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1;
+ pClonedTrack->AddLoadedGenerated(aClonedNewCell, pGenerated->GetBigRange(), aNewValue);
+ }
+ }
+
+ // clone actions
+ const ScChangeAction* pAction = GetFirst();
+ while ( pAction )
+ {
+ ScChangeAction* pClonedAction = nullptr;
+
+ switch ( pAction->GetType() )
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ {
+ bool bEndOfList = static_cast<const ScChangeActionIns*>(pAction)->IsEndOfList();
+ pClonedAction = new ScChangeActionIns(
+ pAction->GetActionNumber(),
+ pAction->GetState(),
+ pAction->GetRejectAction(),
+ pAction->GetBigRange(),
+ pAction->GetUser(),
+ pAction->GetDateTimeUTC(),
+ pAction->GetComment(),
+ pAction->GetType(),
+ bEndOfList );
+ }
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ const ScChangeActionDel& rDelete = dynamic_cast<const ScChangeActionDel&>(*pAction);
+
+ SCCOLROW nD = 0;
+ ScChangeActionType eType = pAction->GetType();
+ if ( eType == SC_CAT_DELETE_COLS )
+ {
+ nD = static_cast< SCCOLROW >( rDelete.GetDx() );
+ }
+ else if ( eType == SC_CAT_DELETE_ROWS )
+ {
+ nD = static_cast< SCCOLROW >( rDelete.GetDy() );
+ }
+
+ pClonedAction = new ScChangeActionDel(
+ pAction->GetActionNumber(),
+ pAction->GetState(),
+ pAction->GetRejectAction(),
+ pAction->GetBigRange(),
+ pAction->GetUser(),
+ pAction->GetDateTimeUTC(),
+ pAction->GetComment(),
+ eType,
+ nD,
+ pClonedTrack.get() );
+ }
+ break;
+ case SC_CAT_MOVE:
+ {
+ auto pMove = dynamic_cast<const ScChangeActionMove*>(pAction);
+ assert(pMove && "ScChangeTrack::Clone: pMove is null!");
+
+ pClonedAction = new ScChangeActionMove(
+ pAction->GetActionNumber(),
+ pAction->GetState(),
+ pAction->GetRejectAction(),
+ pAction->GetBigRange(),
+ pAction->GetUser(),
+ pAction->GetDateTimeUTC(),
+ pAction->GetComment(),
+ pMove->GetFromRange(),
+ pClonedTrack.get() );
+ }
+ break;
+ case SC_CAT_CONTENT:
+ {
+ const ScChangeActionContent& rContent = dynamic_cast<const ScChangeActionContent&>(*pAction);
+ const ScCellValue& rOldCell = rContent.GetOldCell();
+ ScCellValue aClonedOldCell;
+ aClonedOldCell.assign(rOldCell, *pDocument);
+ OUString aOldValue = rContent.GetOldString( pDocument );
+
+ ScChangeActionContent* pClonedContent = new ScChangeActionContent(
+ pAction->GetActionNumber(),
+ pAction->GetState(),
+ pAction->GetRejectAction(),
+ pAction->GetBigRange(),
+ pAction->GetUser(),
+ pAction->GetDateTimeUTC(),
+ pAction->GetComment(),
+ std::move(aClonedOldCell),
+ pDocument,
+ aOldValue );
+
+ const ScCellValue& rNewCell = rContent.GetNewCell();
+ if (!rNewCell.isEmpty())
+ {
+ ScCellValue aClonedNewCell;
+ aClonedNewCell.assign(rNewCell, *pDocument);
+ pClonedContent->SetNewValue(aClonedNewCell, pDocument);
+ }
+
+ pClonedAction = pClonedContent;
+ }
+ break;
+ case SC_CAT_REJECT:
+ {
+ pClonedAction = new ScChangeActionReject(
+ pAction->GetActionNumber(),
+ pAction->GetState(),
+ pAction->GetRejectAction(),
+ pAction->GetBigRange(),
+ pAction->GetUser(),
+ pAction->GetDateTimeUTC(),
+ pAction->GetComment() );
+ }
+ break;
+ default:
+ {
+ }
+ break;
+ }
+
+ if ( pClonedAction )
+ {
+ pClonedTrack->AppendCloned( pClonedAction );
+ }
+
+ pAction = pAction->GetNext();
+ }
+
+ if ( pClonedTrack->GetLast() )
+ {
+ pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() );
+ }
+
+ // set dependencies for Deleted/DeletedIn
+ pAction = GetFirst();
+ while ( pAction )
+ {
+ if ( pAction->HasDeleted() )
+ {
+ ::std::stack< sal_uLong > aStack;
+ const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry();
+ while ( pL )
+ {
+ const ScChangeAction* pDeleted = pL->GetAction();
+ if ( pDeleted )
+ {
+ aStack.push( pDeleted->GetActionNumber() );
+ }
+ pL = pL->GetNext();
+ }
+ ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
+ if ( pClonedAction )
+ {
+ while ( !aStack.empty() )
+ {
+ ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() );
+ aStack.pop();
+ if ( pClonedDeleted )
+ {
+ pClonedDeleted->SetDeletedIn( pClonedAction );
+ }
+ }
+ }
+ }
+ pAction = pAction->GetNext();
+ }
+
+ // set dependencies for Dependent/Any
+ pAction = GetLast();
+ while ( pAction )
+ {
+ if ( pAction->HasDependent() )
+ {
+ ::std::stack< sal_uLong > aStack;
+ const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry();
+ while ( pL )
+ {
+ const ScChangeAction* pDependent = pL->GetAction();
+ if ( pDependent )
+ {
+ aStack.push( pDependent->GetActionNumber() );
+ }
+ pL = pL->GetNext();
+ }
+ ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
+ if ( pClonedAction )
+ {
+ while ( !aStack.empty() )
+ {
+ ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() );
+ aStack.pop();
+ if ( pClonedDependent )
+ {
+ ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent );
+ pClonedDependent->AddLink( pClonedAction, pLink );
+ }
+ }
+ }
+ }
+ pAction = pAction->GetPrev();
+ }
+
+ // masterlinks
+ ScChangeAction* pClonedAction = pClonedTrack->GetFirst();
+ while ( pClonedAction )
+ {
+ pClonedTrack->MasterLinks( pClonedAction );
+ pClonedAction = pClonedAction->GetNext();
+ }
+
+ if ( IsProtected() )
+ {
+ pClonedTrack->SetProtection( GetProtection() );
+ }
+
+ if ( pClonedTrack->GetLast() )
+ {
+ pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() );
+ }
+
+ auto tmp = pClonedTrack.get();
+ pDocument->SetChangeTrack( std::move(pClonedTrack) );
+
+ return tmp;
+}
+
+void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct )
+{
+ if ( !pAct->IsVirgin() )
+ return;
+
+ if ( pOtherAct->IsAccepted() )
+ {
+ pAct->Accept();
+ if ( pOtherAct->IsRejecting() )
+ {
+ pAct->SetRejectAction( pOtherAct->GetRejectAction() );
+ }
+ }
+ else if ( pOtherAct->IsRejected() )
+ {
+ pAct->SetRejected();
+ }
+}
+
+/// Get info about a single ScChangeAction element.
+static void lcl_getTrackedChange(ScDocument& rDoc, int nIndex, const ScChangeAction* pAction, tools::JsonWriter& rRedlines)
+{
+ if (pAction->GetType() != SC_CAT_CONTENT)
+ return;
+
+ auto redlinesNode = rRedlines.startStruct();
+ rRedlines.put("index", static_cast<sal_Int64>(nIndex));
+
+ rRedlines.put("author", pAction->GetUser());
+
+ rRedlines.put("type", "Modify");
+
+ rRedlines.put("comment", pAction->GetComment());
+
+ OUString aDescription = pAction->GetDescription(rDoc, true);
+ rRedlines.put("description", aDescription);
+
+ OUString sDateTime = utl::toISO8601(pAction->GetDateTimeUTC().GetUNODateTime());
+ rRedlines.put("dateTime", sDateTime);
+}
+
+void ScChangeTrack::GetChangeTrackInfo(tools::JsonWriter& aRedlines)
+{
+ auto redlinesNode = aRedlines.startArray("redlines");
+
+ ScChangeAction* pAction = GetFirst();
+ if (pAction)
+ {
+ int i = 0;
+ lcl_getTrackedChange(rDoc, i++, pAction, aRedlines);
+ ScChangeAction* pLastAction = GetLast();
+ while (pAction != pLastAction)
+ {
+ pAction = pAction->GetNext();
+ lcl_getTrackedChange(rDoc, i++, pAction, aRedlines);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/chgviset.cxx b/sc/source/core/tool/chgviset.cxx
new file mode 100644
index 0000000000..0a394cc869
--- /dev/null
+++ b/sc/source/core/tool/chgviset.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 <unotools/textsearch.hxx>
+
+#include <chgviset.hxx>
+#include <chgtrack.hxx>
+#include <document.hxx>
+
+ScChangeViewSettings::~ScChangeViewSettings()
+{
+}
+
+ScChangeViewSettings::ScChangeViewSettings( const ScChangeViewSettings& r ):
+ aFirstDateTime( DateTime::EMPTY ),
+ aLastDateTime( DateTime::EMPTY )
+{
+ SetTheComment(r.aComment);
+
+ aFirstDateTime =r.aFirstDateTime;
+ aLastDateTime =r.aLastDateTime;
+ aAuthorToShow =r.aAuthorToShow;
+ aRangeList =r.aRangeList;
+ eDateMode =r.eDateMode;
+ bShowIt =r.bShowIt;
+ bIsDate =r.bIsDate;
+ bIsAuthor =r.bIsAuthor;
+ bIsComment =r.bIsComment;
+ bIsRange =r.bIsRange;
+ bShowAccepted =r.bShowAccepted;
+ bShowRejected =r.bShowRejected;
+ mbIsActionRange = r.mbIsActionRange;
+ mnFirstAction = r.mnFirstAction;
+ mnLastAction = r.mnLastAction;
+
+}
+
+ScChangeViewSettings& ScChangeViewSettings::operator=( const ScChangeViewSettings& r )
+{
+ pCommentSearcher = nullptr;
+ SetTheComment(r.aComment);
+
+ aFirstDateTime =r.aFirstDateTime;
+ aLastDateTime =r.aLastDateTime;
+ aAuthorToShow =r.aAuthorToShow;
+ aRangeList =r.aRangeList;
+ eDateMode =r.eDateMode;
+ bShowIt =r.bShowIt;
+ bIsDate =r.bIsDate;
+ bIsAuthor =r.bIsAuthor;
+ bIsComment =r.bIsComment;
+ bIsRange =r.bIsRange;
+ bShowAccepted =r.bShowAccepted;
+ bShowRejected =r.bShowRejected;
+ mbIsActionRange = r.mbIsActionRange;
+ mnFirstAction = r.mnFirstAction;
+ mnLastAction = r.mnLastAction;
+
+ return *this;
+}
+
+bool ScChangeViewSettings::IsValidComment(const OUString* pCommentStr) const
+{
+ bool bTheFlag = true;
+
+ if(pCommentSearcher)
+ {
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = pCommentStr->getLength();
+ bTheFlag = pCommentSearcher->SearchForward(*pCommentStr, &nStartPos, &nEndPos);
+ }
+ return bTheFlag;
+}
+
+void ScChangeViewSettings::SetTheComment(const OUString& rString)
+{
+ aComment = rString;
+ pCommentSearcher.reset();
+
+ if(!rString.isEmpty())
+ {
+ utl::SearchParam aSearchParam( rString,
+ utl::SearchParam::SearchType::Regexp,false );
+
+ pCommentSearcher.reset( new utl::TextSearch( aSearchParam, ScGlobal::getCharClass() ) );
+ }
+}
+
+void ScChangeViewSettings::AdjustDateMode( const ScDocument& rDoc )
+{
+ switch ( eDateMode )
+ { // corresponds with ScViewUtil::IsActionShown
+ case SvxRedlinDateMode::EQUAL :
+ case SvxRedlinDateMode::NOTEQUAL :
+ aFirstDateTime.SetTime( 0 );
+ aLastDateTime = aFirstDateTime;
+ aLastDateTime.SetTime( 23595999 );
+ break;
+ case SvxRedlinDateMode::SAVE:
+ {
+ const ScChangeAction* pLast = nullptr;
+ ScChangeTrack* pTrack = rDoc.GetChangeTrack();
+ if ( pTrack )
+ {
+ pLast = pTrack->GetLastSaved();
+ if ( pLast )
+ {
+ aFirstDateTime = pLast->GetDateTime();
+
+ // Set the next minute as the start time and assume that
+ // the document isn't saved, reloaded, edited and filter set
+ // all together during the gap between those two times.
+ aFirstDateTime += tools::Time( 0, 1 );
+ aFirstDateTime.SetSec(0);
+ aFirstDateTime.SetNanoSec(0);
+ }
+ }
+ if ( !pLast )
+ {
+ aFirstDateTime.SetDate( 18990101 );
+ aFirstDateTime.SetTime( 0 );
+ }
+ aLastDateTime = Date( Date::SYSTEM );
+ aLastDateTime.AddYears( 100 );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/compare.cxx b/sc/source/core/tool/compare.cxx
new file mode 100644
index 0000000000..58ed51f65f
--- /dev/null
+++ b/sc/source/core/tool/compare.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <compare.hxx>
+
+#include <document.hxx>
+#include <docoptio.hxx>
+
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/textsearch.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <rtl/math.hxx>
+#include <osl/diagnose.h>
+
+namespace sc {
+
+Compare::Cell::Cell() :
+ mfValue(0.0), mbValue(false), mbEmpty(false) {}
+
+Compare::Compare() :
+ meOp(SC_EQUAL), mbIgnoreCase(true) {}
+
+CompareOptions::CompareOptions( const ScDocument& rDoc, const ScQueryEntry& rEntry, utl::SearchParam::SearchType eSrchTyp ) :
+ aQueryEntry(rEntry),
+ eSearchType(eSrchTyp),
+ bMatchWholeCell(rDoc.GetDocOptions().IsMatchWholeCell())
+{
+ // Wildcard and Regex search work only with equal or not equal.
+ if (eSearchType != utl::SearchParam::SearchType::Normal &&
+ aQueryEntry.eOp != SC_EQUAL && aQueryEntry.eOp != SC_NOT_EQUAL)
+ eSearchType = utl::SearchParam::SearchType::Normal;
+
+ // Interpreter functions usually are case insensitive, except the simple
+ // comparison operators, for which these options aren't used. Override in
+ // struct if needed.
+}
+
+double CompareFunc( const Compare& rComp, CompareOptions* pOptions )
+{
+ const Compare::Cell& rCell1 = rComp.maCells[0];
+ const Compare::Cell& rCell2 = rComp.maCells[1];
+
+ // Keep DoubleError if encountered
+ // #i40539# if bEmpty is set, bVal/nVal are uninitialized
+ if (!rCell1.mbEmpty && rCell1.mbValue && !std::isfinite(rCell1.mfValue))
+ return rCell1.mfValue;
+ if (!rCell2.mbEmpty && rCell2.mbValue && !std::isfinite(rCell2.mfValue))
+ return rCell2.mfValue;
+
+ size_t nStringQuery = 0; // 0:=no, 1:=0, 2:=1
+ double fRes = 0;
+ if (rCell1.mbEmpty)
+ {
+ if (rCell2.mbEmpty)
+ ; // empty cell == empty cell, fRes 0
+ else if (rCell2.mbValue)
+ {
+ if (rCell2.mfValue != 0.0)
+ {
+ if (rCell2.mfValue < 0.0)
+ fRes = 1; // empty cell > -x
+ else
+ fRes = -1; // empty cell < x
+ }
+ // else: empty cell == 0.0
+ }
+ else
+ {
+ if (!rCell2.maStr.isEmpty())
+ fRes = -1; // empty cell < "..."
+ // else: empty cell == ""
+ }
+ }
+ else if (rCell2.mbEmpty)
+ {
+ if (rCell1.mbValue)
+ {
+ if (rCell1.mfValue != 0.0)
+ {
+ if (rCell1.mfValue < 0.0)
+ fRes = -1; // -x < empty cell
+ else
+ fRes = 1; // x > empty cell
+ }
+ // else: empty cell == 0.0
+ }
+ else
+ {
+ if (!rCell1.maStr.isEmpty())
+ fRes = 1; // "..." > empty cell
+ // else: "" == empty cell
+ }
+ }
+ else if (rCell1.mbValue)
+ {
+ if (rCell2.mbValue)
+ {
+ if (!rtl::math::approxEqual(rCell1.mfValue, rCell2.mfValue))
+ {
+ if (rCell1.mfValue - rCell2.mfValue < 0)
+ fRes = -1;
+ else
+ fRes = 1;
+ }
+ }
+ else
+ {
+ fRes = -1; // number is less than string
+ nStringQuery = 2; // 1+1
+ }
+ }
+ else if (rCell2.mbValue)
+ {
+ fRes = 1; // string is greater than number
+ nStringQuery = 1; // 0+1
+ }
+ else
+ {
+ // Both strings.
+ if (pOptions)
+ {
+ // All similar to ScTable::ValidQuery(), *rComp.pVal[1] actually
+ // is/must be identical to *rEntry.pStr, which is essential for
+ // regex to work through GetSearchTextPtr().
+ ScQueryEntry& rEntry = pOptions->aQueryEntry;
+ OSL_ENSURE(rEntry.GetQueryItem().maString == rCell2.maStr, "ScInterpreter::CompareFunc: broken options");
+ if (pOptions->eSearchType != utl::SearchParam::SearchType::Normal)
+ {
+ sal_Int32 nStart = 0;
+ sal_Int32 nStop = rCell1.maStr.getLength();
+ bool bMatch = rEntry.GetSearchTextPtr( pOptions->eSearchType, !rComp.mbIgnoreCase,
+ pOptions->bMatchWholeCell)->SearchForward( rCell1.maStr.getString(), &nStart, &nStop);
+ if (bMatch && pOptions->bMatchWholeCell && (nStart != 0 || nStop != rCell1.maStr.getLength()))
+ bMatch = false; // RegEx must match entire string.
+ fRes = (bMatch ? 0 : 1);
+ }
+ else if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
+ {
+ ::utl::TransliterationWrapper& rTransliteration =
+ ScGlobal::GetTransliteration(!rComp.mbIgnoreCase);
+ bool bMatch = false;
+ if (pOptions->bMatchWholeCell)
+ {
+ if (rComp.mbIgnoreCase)
+ bMatch = rCell1.maStr.getDataIgnoreCase() == rCell2.maStr.getDataIgnoreCase();
+ else
+ bMatch = rCell1.maStr.getData() == rCell2.maStr.getData();
+ }
+ else
+ {
+ const LanguageType nLang = ScGlobal::oSysLocale->GetLanguageTag().getLanguageType();
+ OUString aCell( rTransliteration.transliterate(
+ rCell1.maStr.getString(), nLang, 0,
+ rCell1.maStr.getLength(), nullptr));
+ OUString aQuer( rTransliteration.transliterate(
+ rCell2.maStr.getString(), nLang, 0,
+ rCell2.maStr.getLength(), nullptr));
+ bMatch = (aCell.indexOf( aQuer ) != -1);
+ }
+ fRes = (bMatch ? 0 : 1);
+ }
+ else if (rComp.mbIgnoreCase)
+ fRes = static_cast<double>(ScGlobal::GetCollator().compareString(
+ rCell1.maStr.getString(), rCell2.maStr.getString()));
+ else
+ fRes = static_cast<double>(ScGlobal::GetCaseCollator().compareString(
+ rCell1.maStr.getString(), rCell2.maStr.getString()));
+ }
+ else if (rComp.meOp == SC_EQUAL || rComp.meOp == SC_NOT_EQUAL)
+ {
+ if (rComp.mbIgnoreCase)
+ fRes = (rCell1.maStr.getDataIgnoreCase() == rCell2.maStr.getDataIgnoreCase()) ? 0 : 1;
+ else
+ fRes = (rCell1.maStr.getData() == rCell2.maStr.getData()) ? 0 : 1;
+ }
+ else if (rComp.mbIgnoreCase)
+ fRes = static_cast<double>(ScGlobal::GetCollator().compareString(
+ rCell1.maStr.getString(), rCell2.maStr.getString()));
+ else
+ fRes = static_cast<double>(ScGlobal::GetCaseCollator().compareString(
+ rCell1.maStr.getString(), rCell2.maStr.getString()));
+ }
+
+ if (nStringQuery && pOptions)
+ {
+ const ScQueryEntry& rEntry = pOptions->aQueryEntry;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ if (!rItems.empty())
+ {
+ const ScQueryEntry::Item& rItem = rItems[0];
+ if (rItem.meType != ScQueryEntry::ByString && !rItem.maString.isEmpty() &&
+ (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL))
+ {
+ // As in ScTable::ValidQuery() match a numeric string for a
+ // number query that originated from a string, e.g. in SUMIF
+ // and COUNTIF. Transliteration is not needed here.
+ bool bEqual = false;
+ if (nStringQuery == 1)
+ bEqual = rCell1.maStr == rItem.maString;
+ else
+ bEqual = rCell2.maStr == rItem.maString;
+
+ // match => fRes=0, else fRes=1
+ fRes = double((rEntry.eOp == SC_NOT_EQUAL) ? bEqual : !bEqual);
+ }
+ }
+ }
+
+ return fRes;
+}
+
+double CompareFunc( const Compare::Cell& rCell1, double fCell2, const CompareOptions* pOptions )
+{
+ // Keep DoubleError if encountered
+ // #i40539# if bEmpty is set, bVal/nVal are uninitialized
+ if (!rCell1.mbEmpty && rCell1.mbValue && !std::isfinite(rCell1.mfValue))
+ return rCell1.mfValue;
+ if (!std::isfinite(fCell2))
+ return fCell2;
+
+ bool bStringQuery = false;
+ double fRes = 0;
+ if (rCell1.mbEmpty)
+ {
+ if (fCell2 != 0.0)
+ {
+ if (fCell2 < 0.0)
+ fRes = 1; // empty cell > -x
+ else
+ fRes = -1; // empty cell < x
+ }
+ // else: empty cell == 0.0
+ }
+ else if (rCell1.mbValue)
+ {
+ if (!rtl::math::approxEqual(rCell1.mfValue, fCell2))
+ {
+ if (rCell1.mfValue - fCell2 < 0)
+ fRes = -1;
+ else
+ fRes = 1;
+ }
+ }
+ else
+ {
+ fRes = 1; // string is greater than number
+ bStringQuery = true;
+ }
+
+ if (bStringQuery && pOptions)
+ {
+ const ScQueryEntry& rEntry = pOptions->aQueryEntry;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ if (!rItems.empty())
+ {
+ const ScQueryEntry::Item& rItem = rItems[0];
+ if (rItem.meType != ScQueryEntry::ByString && !rItem.maString.isEmpty() &&
+ (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL))
+ {
+ // As in ScTable::ValidQuery() match a numeric string for a
+ // number query that originated from a string, e.g. in SUMIF
+ // and COUNTIF. Transliteration is not needed here.
+ bool bEqual = rCell1.maStr == rItem.maString;
+
+ // match => fRes=0, else fRes=1
+ fRes = double((rEntry.eOp == SC_NOT_EQUAL) ? bEqual : !bEqual);
+ }
+ }
+ }
+
+ return fRes;
+}
+
+double CompareFunc( double fCell1, double fCell2 )
+{
+ // Keep DoubleError if encountered
+ // #i40539# if bEmpty is set, bVal/nVal are uninitialized
+ if (!std::isfinite(fCell1))
+ return fCell1;
+ if (!std::isfinite(fCell2))
+ return fCell2;
+
+ double fRes = 0.0;
+
+ if (!rtl::math::approxEqual(fCell1, fCell2))
+ {
+ if (fCell1 - fCell2 < 0.0)
+ fRes = -1;
+ else
+ fRes = 1;
+ }
+
+ return fRes;
+}
+
+double CompareEmptyToNumericFunc( double fCell2 )
+{
+ // Keep DoubleError if encountered
+ // #i40539# if bEmpty is set, bVal/nVal are uninitialized
+ if (!std::isfinite(fCell2))
+ return fCell2;
+
+ double fRes = 0;
+ if (fCell2 != 0.0)
+ {
+ if (fCell2 < 0.0)
+ fRes = 1; // empty cell > -x
+ else
+ fRes = -1; // empty cell < x
+ }
+ // else: empty cell == 0.0
+
+ return fRes;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
new file mode 100644
index 0000000000..63b1f09692
--- /dev/null
+++ b/sc/source/core/tool/compiler.cxx
@@ -0,0 +1,6651 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <compiler.hxx>
+
+#include <mutex>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbstar.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+#include <com/sun/star/i18n/KParseTokens.hpp>
+#include <com/sun/star/i18n/KParseType.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <tools/urlobj.hxx>
+#include <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <stdlib.h>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <callform.hxx>
+#include <addincol.hxx>
+#include <refupdat.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <formulacell.hxx>
+#include <dociter.hxx>
+#include <docoptio.hxx>
+#include <formula/errorcodes.hxx>
+#include <parclass.hxx>
+#include <autonamecache.hxx>
+#include <externalrefmgr.hxx>
+#include <rangeutl.hxx>
+#include <convuno.hxx>
+#include <tokenuno.hxx>
+#include <formulaparserpool.hxx>
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <tokenstringcontext.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace formula;
+using namespace ::com::sun::star;
+using ::std::vector;
+
+const CharClass* ScCompiler::pCharClassEnglish = nullptr;
+const CharClass* ScCompiler::pCharClassLocalized = nullptr;
+const ScCompiler::Convention* ScCompiler::pConventions[ ] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
+
+namespace {
+
+enum ScanState
+{
+ ssGetChar,
+ ssGetBool,
+ ssGetValue,
+ ssGetString,
+ ssSkipString,
+ ssGetIdent,
+ ssGetReference,
+ ssSkipReference,
+ ssGetErrorConstant,
+ ssGetTableRefItem,
+ ssGetTableRefColumn,
+ ssStop
+};
+
+}
+
+static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
+
+using namespace ::com::sun::star::i18n;
+
+void ScCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& xMap,FormulaGrammar::Grammar _eGrammar ) const
+{
+ size_t nSymbolOffset;
+ switch( _eGrammar )
+ {
+ // XFunctionAccess and XCell::setFormula()/getFormula() API always used
+ // PODF grammar symbols, keep it.
+ case FormulaGrammar::GRAM_API:
+ case FormulaGrammar::GRAM_PODF:
+ nSymbolOffset = offsetof( AddInMap, pUpper);
+ break;
+ default:
+ case FormulaGrammar::GRAM_ODFF:
+ nSymbolOffset = offsetof( AddInMap, pODFF);
+ break;
+ case FormulaGrammar::GRAM_ENGLISH:
+ nSymbolOffset = offsetof( AddInMap, pEnglish);
+ break;
+ }
+ const AddInMap* const pStop = g_aAddInMap + GetAddInMapCount();
+ for (const AddInMap* pMap = g_aAddInMap; pMap < pStop; ++pMap)
+ {
+ char const * const * ppSymbol =
+ reinterpret_cast< char const * const * >(
+ reinterpret_cast< char const * >(pMap) + nSymbolOffset);
+ xMap->putExternal( OUString::createFromAscii( *ppSymbol),
+ OUString::createFromAscii( pMap->pOriginal));
+ }
+ if (_eGrammar == FormulaGrammar::GRAM_API)
+ {
+ // Add English names additionally to programmatic names, so they
+ // can be used in XCell::setFormula() non-localized API calls.
+ // Note the reverse map will still deliver programmatic names for
+ // XCell::getFormula().
+ nSymbolOffset = offsetof( AddInMap, pEnglish);
+ for (const AddInMap* pMap = g_aAddInMap; pMap < pStop; ++pMap)
+ {
+ char const * const * ppSymbol =
+ reinterpret_cast< char const * const * >(
+ reinterpret_cast< char const * >(pMap) + nSymbolOffset);
+ xMap->putExternal( OUString::createFromAscii( *ppSymbol),
+ OUString::createFromAscii( pMap->pOriginal));
+ }
+ }
+}
+
+void ScCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& xMap ) const
+{
+ ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
+ tools::Long nCount = pColl->GetFuncCount();
+ for (tools::Long i=0; i < nCount; ++i)
+ {
+ const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
+ if (pFuncData)
+ xMap->putExternalSoftly( pFuncData->GetUpperName(),
+ pFuncData->GetOriginalName());
+ }
+}
+
+void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& xMap ) const
+{
+ ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
+ tools::Long nCount = pColl->GetFuncCount();
+ for (tools::Long i=0; i < nCount; ++i)
+ {
+ const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
+ if (pFuncData)
+ {
+ const OUString aName( pFuncData->GetUpperEnglish());
+ if (!aName.isEmpty())
+ xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
+ else
+ xMap->putExternalSoftly( pFuncData->GetUpperName(),
+ pFuncData->GetOriginalName());
+ }
+ }
+}
+
+void ScCompiler::DeInit()
+{
+ if (pCharClassEnglish)
+ {
+ delete pCharClassEnglish;
+ pCharClassEnglish = nullptr;
+ }
+ if (pCharClassLocalized)
+ {
+ delete pCharClassLocalized;
+ pCharClassLocalized = nullptr;
+ }
+}
+
+bool ScCompiler::IsEnglishSymbol( const OUString& rName )
+{
+ // function names are always case-insensitive
+ OUString aUpper = GetCharClassEnglish()->uppercase(rName);
+
+ // 1. built-in function name
+ formula::FormulaCompiler aCompiler;
+ OpCode eOp = aCompiler.GetEnglishOpCode( aUpper );
+ if ( eOp != ocNone )
+ {
+ return true;
+ }
+ // 2. old add in functions
+ if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper))
+ {
+ return true;
+ }
+
+ // 3. new (uno) add in functions
+ OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
+ return !aIntName.isEmpty(); // no valid function name
+}
+
+static std::mutex& getCharClassMutex()
+{
+ static std::mutex aMutex;
+ return aMutex;
+}
+
+const CharClass* ScCompiler::GetCharClassEnglish()
+{
+ if (!pCharClassEnglish)
+ {
+ std::scoped_lock aGuard(getCharClassMutex());
+ if (!pCharClassEnglish)
+ {
+ pCharClassEnglish = new CharClass( ::comphelper::getProcessComponentContext(),
+ LanguageTag( LANGUAGE_ENGLISH_US));
+ }
+ }
+ return pCharClassEnglish;
+}
+
+const CharClass* ScCompiler::GetCharClassLocalized()
+{
+ if (!pCharClassLocalized)
+ {
+ // Switching UI language requires restart; if not, we would have to
+ // keep track of that.
+ std::scoped_lock aGuard(getCharClassMutex());
+ if (!pCharClassLocalized)
+ {
+ pCharClassLocalized = new CharClass( ::comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetUILanguageTag());
+ }
+ }
+ return pCharClassLocalized;
+}
+
+void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
+{
+ assert( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED && "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
+ if (eGrammar == GetGrammar())
+ return; // nothing to be done
+
+ if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
+ {
+ meGrammar = eGrammar;
+ mxSymbols = GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
+ }
+ else
+ {
+ FormulaGrammar::Grammar eMyGrammar = eGrammar;
+ const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
+ OpCodeMapPtr xMap = GetFinalOpCodeMap( nFormulaLanguage);
+ OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
+ if (!xMap)
+ {
+ xMap = GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
+ eMyGrammar = xMap->getGrammar();
+ }
+
+ // Save old grammar for call to SetGrammarAndRefConvention().
+ FormulaGrammar::Grammar eOldGrammar = GetGrammar();
+ // This also sets the grammar associated with the map!
+ SetFormulaLanguage( xMap);
+
+ // Override if necessary.
+ if (eMyGrammar != GetGrammar())
+ SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
+ }
+}
+
+// Unclear how this was intended to be refreshed when the
+// grammar or sheet count is changed ? Ideally this would be
+// a method on Document that would globally cache these.
+std::vector<OUString> &ScCompiler::GetSetupTabNames() const
+{
+ std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames;
+
+ if (rTabNames.empty())
+ {
+ rTabNames = rDoc.GetAllTableNames();
+ for (auto& rTabName : rTabNames)
+ ScCompiler::CheckTabQuotes(rTabName, formula::FormulaGrammar::extractRefConvention(meGrammar));
+ }
+
+ return rTabNames;
+}
+
+void ScCompiler::SetNumberFormatter( SvNumberFormatter* pFormatter )
+{
+ mpFormatter = pFormatter;
+}
+
+void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
+{
+ if (!xMap)
+ return;
+
+ mxSymbols = xMap;
+ if (mxSymbols->isEnglish())
+ pCharClass = GetCharClassEnglish();
+ else
+ pCharClass = GetCharClassLocalized();
+
+ // The difference is needed for an uppercase() call that usually does not
+ // result in different strings but for a few languages like Turkish;
+ // though even de-DE and de-CH may differ in ß/SS handling..
+ // At least don't care if both are English.
+ // The current locale is more likely to not be "en" so check first.
+ const LanguageTag& rLT1 = ScGlobal::getCharClass().getLanguageTag();
+ const LanguageTag& rLT2 = pCharClass->getLanguageTag();
+ mbCharClassesDiffer = (rLT1 != rLT2 && (rLT1.getLanguage() != "en" || rLT2.getLanguage() != "en"));
+
+ SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
+}
+
+void ScCompiler::SetGrammarAndRefConvention(
+ const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
+{
+ meGrammar = eNewGrammar; // SetRefConvention needs the new grammar set!
+ FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
+ if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
+ SetRefConvention( rDoc.GetAddressConvention());
+ else
+ SetRefConvention( eConv );
+}
+
+OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
+{
+ return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=false for english
+}
+
+ScCompiler::Convention::~Convention()
+{
+}
+
+ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
+ :
+ meConv( eConv )
+{
+ int i;
+ ScCharFlags *t= new ScCharFlags [128];
+
+ ScCompiler::pConventions[ meConv ] = this;
+ mpCharTable.reset( t );
+
+ for (i = 0; i < 128; i++)
+ t[i] = ScCharFlags::Illegal;
+
+// Allow tabs/newlines.
+// Allow saving whitespace as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace").
+/* tab */ t[ 9] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* lf */ t[10] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* cr */ t[13] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+
+/* */ t[32] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* ! */ t[33] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+ if (FormulaGrammar::CONV_ODF == meConv)
+/* ! */ t[33] |= ScCharFlags::OdfLabelOp;
+/* " */ t[34] = ScCharFlags::CharString | ScCharFlags::StringSep;
+/* # */ t[35] = ScCharFlags::WordSep | ScCharFlags::CharErrConst;
+/* $ */ t[36] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident;
+ if (FormulaGrammar::CONV_ODF == meConv)
+/* $ */ t[36] |= ScCharFlags::OdfNameMarker;
+/* % */ t[37] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* & */ t[38] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* ' */ t[39] = ScCharFlags::NameSep;
+/* ( */ t[40] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* ) */ t[41] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* * */ t[42] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* + */ t[43] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign;
+/* , */ t[44] = ScCharFlags::CharValue | ScCharFlags::Value;
+/* - */ t[45] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign;
+/* . */ t[46] = ScCharFlags::Word | ScCharFlags::CharValue | ScCharFlags::Value | ScCharFlags::Ident | ScCharFlags::Name;
+/* / */ t[47] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+
+ for (i = 48; i < 58; i++)
+/* 0-9 */ t[i] = ScCharFlags::CharValue | ScCharFlags::Word | ScCharFlags::Value | ScCharFlags::ValueExp | ScCharFlags::ValueValue | ScCharFlags::Ident | ScCharFlags::Name;
+
+/* : */ t[58] = ScCharFlags::Char | ScCharFlags::Word;
+/* ; */ t[59] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* < */ t[60] = ScCharFlags::CharBool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* = */ t[61] = ScCharFlags::Char | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* > */ t[62] = ScCharFlags::CharBool | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* ? */ t[63] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::Name;
+/* @ */ // FREE
+
+ for (i = 65; i < 91; i++)
+/* A-Z */ t[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
+
+ if (FormulaGrammar::CONV_ODF == meConv)
+ {
+/* [ */ t[91] = ScCharFlags::OdfLBracket;
+/* \ */ // FREE
+/* ] */ t[93] = ScCharFlags::OdfRBracket;
+ }
+ else if (FormulaGrammar::CONV_OOO == meConv)
+ {
+/* [ */ t[91] = ScCharFlags::Char;
+/* \ */ // FREE
+/* ] */ t[93] = ScCharFlags::Char;
+ }
+ else if (FormulaGrammar::CONV_XL_OOX == meConv)
+ {
+/* [ */ t[91] = ScCharFlags::Char | ScCharFlags::CharIdent;
+/* \ */ // FREE
+/* ] */ t[93] = ScCharFlags::Char | ScCharFlags::Ident;
+ }
+ else if (FormulaGrammar::CONV_XL_A1 == meConv)
+ {
+/* [ */ t[91] = ScCharFlags::Char;
+/* \ */ // FREE
+/* ] */ t[93] = ScCharFlags::Char;
+ }
+ else if( FormulaGrammar::CONV_XL_R1C1 == meConv )
+ {
+/* [ */ t[91] = ScCharFlags::Ident;
+/* \ */ // FREE
+/* ] */ t[93] = ScCharFlags::Ident;
+ }
+ else
+ {
+/* [ */ // FREE
+/* \ */ // FREE
+/* ] */ // FREE
+ }
+
+/* ^ */ t[94] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+/* _ */ t[95] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
+/* ` */ // FREE
+
+ for (i = 97; i < 123; i++)
+/* a-z */ t[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name;
+
+/* { */ t[123] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array open
+/* | */ t[124] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array row sep (Should be OOo specific)
+/* } */ t[125] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array close
+/* ~ */ t[126] = ScCharFlags::Char; // OOo specific
+/* 127 */ // FREE
+
+ if( !(FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv) )
+return;
+
+/* */ t[32] |= ScCharFlags::Word;
+/* ! */ t[33] |= ScCharFlags::Ident | ScCharFlags::Word;
+/* " */ t[34] |= ScCharFlags::Word;
+/* # */ t[35] &= ~ScCharFlags::WordSep;
+/* # */ t[35] |= ScCharFlags::Word;
+/* % */ t[37] |= ScCharFlags::Word;
+/* & */ t[38] |= ScCharFlags::Word;
+/* ' */ t[39] |= ScCharFlags::Word;
+/* ( */ t[40] |= ScCharFlags::Word;
+/* ) */ t[41] |= ScCharFlags::Word;
+/* * */ t[42] |= ScCharFlags::Word;
+/* + */ t[43] |= ScCharFlags::Word;
+#if 0 /* this really needs to be locale specific. */
+/* , */ t[44] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep;
+#else
+/* , */ t[44] |= ScCharFlags::Word;
+#endif
+/* - */ t[45] |= ScCharFlags::Word;
+
+/* ; */ t[59] |= ScCharFlags::Word;
+/* < */ t[60] |= ScCharFlags::Word;
+/* = */ t[61] |= ScCharFlags::Word;
+/* > */ t[62] |= ScCharFlags::Word;
+/* ? */ // question really is not permitted in sheet name
+/* @ */ t[64] |= ScCharFlags::Word;
+/* [ */ t[91] |= ScCharFlags::Word;
+/* ] */ t[93] |= ScCharFlags::Word;
+/* { */ t[123]|= ScCharFlags::Word;
+/* | */ t[124]|= ScCharFlags::Word;
+/* } */ t[125]|= ScCharFlags::Word;
+/* ~ */ t[126]|= ScCharFlags::Word;
+}
+
+static bool lcl_isValidQuotedText( std::u16string_view rFormula, size_t nSrcPos, ParseResult& rRes )
+{
+ // Tokens that start at ' can have anything in them until a final '
+ // but '' marks an escaped '
+ // We've earlier guaranteed that a string containing '' will be
+ // surrounded by '
+ if (nSrcPos < rFormula.size() && rFormula[nSrcPos] == '\'')
+ {
+ size_t nPos = nSrcPos+1;
+ while (nPos < rFormula.size())
+ {
+ if (rFormula[nPos] == '\'')
+ {
+ if ( (nPos+1 == rFormula.size()) || (rFormula[nPos+1] != '\'') )
+ {
+ rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
+ rRes.EndPos = nPos+1;
+ return true;
+ }
+ ++nPos;
+ }
+ ++nPos;
+ }
+ }
+
+ return false;
+}
+
+static bool lcl_parseExternalName(
+ const OUString& rSymbol,
+ OUString& rFile,
+ OUString& rName,
+ const sal_Unicode cSep,
+ const ScDocument& rDoc,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
+{
+ /* TODO: future versions will have to support sheet-local names too, thus
+ * return a possible sheet name as well. */
+ const sal_Unicode* const pStart = rSymbol.getStr();
+ const sal_Unicode* p = pStart;
+ sal_Int32 nLen = rSymbol.getLength();
+ OUString aTmpFile;
+ OUStringBuffer aTmpName;
+ sal_Int32 i = 0;
+ bool bInName = false;
+ if (cSep == '!')
+ {
+ // For XL use existing parser that resolves bracketed and quoted and
+ // indexed external document names.
+ ScRange aRange;
+ OUString aStartTabName, aEndTabName;
+ ScRefFlags nFlags = ScRefFlags::ZERO;
+ p = aRange.Parse_XL_Header( p, rDoc, aTmpFile, aStartTabName,
+ aEndTabName, nFlags, true, pExternalLinks );
+ if (!p || p == pStart)
+ return false;
+ i = sal_Int32(p - pStart);
+ }
+ for ( ; i < nLen; ++i, ++p)
+ {
+ sal_Unicode c = *p;
+ if (i == 0)
+ {
+ if (c == '.' || c == cSep)
+ return false;
+
+ if (c == '\'')
+ {
+ // Move to the next char and loop until the second single
+ // quote.
+ sal_Unicode cPrev = c;
+ ++i; ++p;
+ for (sal_Int32 j = i; j < nLen; ++j, ++p)
+ {
+ c = *p;
+ if (c == '\'')
+ {
+ if (j == i)
+ {
+ // empty quote e.g. (=''!Name)
+ return false;
+ }
+
+ if (cPrev == '\'')
+ {
+ // two consecutive quotes equal a single quote in
+ // the file name.
+ aTmpFile += OUStringChar(c);
+ cPrev = 'a';
+ }
+ else
+ cPrev = c;
+
+ continue;
+ }
+
+ if (cPrev == '\'' && j != i)
+ {
+ // this is not a quote but the previous one is. This
+ // ends the parsing of the quoted segment. At this
+ // point, the current char must equal the separator
+ // char.
+
+ i = j;
+ bInName = true;
+ aTmpName.append(c); // Keep the separator as part of the name.
+ break;
+ }
+ aTmpFile += OUStringChar(c);
+ cPrev = c;
+ }
+
+ if (!bInName)
+ {
+ // premature ending of the quoted segment.
+ return false;
+ }
+
+ if (c != cSep)
+ {
+ // only the separator is allowed after the closing quote.
+ return false;
+ }
+
+ continue;
+ }
+ }
+
+ if (bInName)
+ {
+ if (c == cSep)
+ {
+ // A second separator ? Not a valid external name.
+ return false;
+ }
+ aTmpName.append(c);
+ }
+ else
+ {
+ if (c == cSep)
+ {
+ bInName = true;
+ aTmpName.append(c); // Keep the separator as part of the name.
+ }
+ else
+ {
+ do
+ {
+ if (rtl::isAsciiAlphanumeric(c))
+ // allowed.
+ break;
+
+ if (c > 128)
+ // non-ASCII character is allowed.
+ break;
+
+ bool bValid = false;
+ switch (c)
+ {
+ case '_':
+ case '-':
+ case '.':
+ // these special characters are allowed.
+ bValid = true;
+ break;
+ }
+ if (bValid)
+ break;
+
+ return false;
+ }
+ while (false);
+ aTmpFile += OUStringChar(c);
+ }
+ }
+ }
+
+ if (!bInName)
+ {
+ // No name found - most likely the symbol has no '!'s.
+ return false;
+ }
+
+ sal_Int32 nNameLen = aTmpName.getLength();
+ if (nNameLen < 2)
+ {
+ // Name must be at least 2-char long (separator plus name).
+ return false;
+ }
+
+ if (aTmpName[0] != cSep)
+ {
+ // 1st char of the name must equal the separator.
+ return false;
+ }
+
+ if (aTmpName[nNameLen-1] == '!')
+ {
+ // Check against #REF!.
+ if (OUString::unacquired(aTmpName).equalsIgnoreAsciiCase("#REF!"))
+ return false;
+ }
+
+ rFile = aTmpFile;
+ rName = aTmpName.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
+ return true;
+}
+
+static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
+ const sal_Unicode cSep, bool bODF )
+{
+ OUString aEscQuote("''");
+ OUString aFile(rFile.replaceAll("'", aEscQuote));
+ OUString aName(rName);
+ if (bODF)
+ aName = aName.replaceAll("'", aEscQuote);
+ OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
+ if (bODF)
+ aBuf.append( '[');
+ aBuf.append( "'" + aFile + "'" + OUStringChar(cSep) );
+ if (bODF)
+ aBuf.append( "$$'" );
+ aBuf.append( aName);
+ if (bODF)
+ aBuf.append( "']" );
+ return aBuf.makeStringAndClear();
+}
+
+static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
+ const vector<OUString>& rTabNames, const ScRange& rRef )
+{
+ SCTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
+ if (nTabSpan > 0)
+ {
+ size_t nCount = rTabNames.size();
+ vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
+ vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
+ if (itr == rTabNames.end())
+ {
+ rTabName2 = ScResId(STR_NO_REF_TABLE);
+ return false;
+ }
+
+ size_t nDist = ::std::distance(itrBeg, itr);
+ if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
+ {
+ rTabName2 = ScResId(STR_NO_REF_TABLE);
+ return false;
+ }
+
+ rTabName2 = rTabNames[nDist+nTabSpan];
+ }
+ else
+ rTabName2 = rTabName1;
+
+ return true;
+}
+
+namespace {
+
+struct Convention_A1 : public ScCompiler::Convention
+{
+ explicit Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
+ static void MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol );
+ static void MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow );
+
+ ParseResult parseAnyToken( const OUString& rFormula,
+ sal_Int32 nSrcPos,
+ const CharClass* pCharClass,
+ bool bGroupSeparator) const override
+ {
+ ParseResult aRet;
+ if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
+ return aRet;
+
+ constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
+ KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
+ constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
+ // '?' allowed in range names because of Xcl :-/
+ static constexpr OUString aAddAllowed(u"?#"_ustr);
+ return pCharClass->parseAnyToken( rFormula,
+ nSrcPos, nStartFlags, aAddAllowed,
+ (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
+ aAddAllowed );
+ }
+
+ virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const override
+ {
+ return mpCharTable[static_cast<sal_uInt8>(c)];
+ }
+};
+
+}
+
+void Convention_A1::MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol )
+{
+ if ( !rLimits.ValidCol(nCol) )
+ rBuffer.append(ScResId(STR_NO_REF_TABLE));
+ else
+ ::ScColToAlpha( rBuffer, nCol);
+}
+
+void Convention_A1::MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow )
+{
+ if ( !rLimits.ValidRow(nRow) )
+ rBuffer.append(ScResId(STR_NO_REF_TABLE));
+ else
+ rBuffer.append(sal_Int32(nRow + 1));
+}
+
+namespace {
+
+struct ConventionOOO_A1 : public Convention_A1
+{
+ ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
+ explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
+
+ static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab )
+ {
+ if (o3tl::make_unsigned(nTab) >= rTabNames.size())
+ rBuf.append(ScResId(STR_NO_REF_TABLE));
+ else
+ rBuf.append(rTabNames[nTab]);
+ rBuf.append('.');
+ }
+
+ enum SingletonDisplay
+ {
+ SINGLETON_NONE,
+ SINGLETON_COL,
+ SINGLETON_ROW
+ };
+
+ static void MakeOneRefStrImpl(
+ const ScSheetLimits& rLimits, OUStringBuffer& rBuffer,
+ std::u16string_view rErrRef, const std::vector<OUString>& rTabNames,
+ const ScSingleRefData& rRef, const ScAddress& rAbsRef,
+ bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay )
+ {
+ if( rRef.IsFlag3D() || bForceTab )
+ {
+ if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
+ {
+ if (!rRef.IsTabRel())
+ rBuffer.append('$');
+ rBuffer.append(rErrRef);
+ rBuffer.append('.');
+ }
+ else
+ {
+ if (!rRef.IsTabRel())
+ rBuffer.append('$');
+ MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab());
+ }
+ }
+ else if (bODF)
+ rBuffer.append('.');
+
+ if (eSingletonDisplay != SINGLETON_ROW)
+ {
+ if (!rRef.IsColRel())
+ rBuffer.append('$');
+ if (!rLimits.ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
+ rBuffer.append(rErrRef);
+ else
+ MakeColStr(rLimits, rBuffer, rAbsRef.Col());
+ }
+
+ if (eSingletonDisplay != SINGLETON_COL)
+ {
+ if (!rRef.IsRowRel())
+ rBuffer.append('$');
+ if (!rLimits.ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
+ rBuffer.append(rErrRef);
+ else
+ MakeRowStr(rLimits, rBuffer, rAbsRef.Row());
+ }
+ }
+
+ static SingletonDisplay getSingletonDisplay( const ScSheetLimits& rLimits, const ScAddress& rAbs1, const ScAddress& rAbs2,
+ const ScComplexRefData& rRef, bool bFromRangeName )
+ {
+ // If any part is error, display as such.
+ if (!rLimits.ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !rLimits.ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() ||
+ !rLimits.ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !rLimits.ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted())
+ return SINGLETON_NONE;
+
+ // A:A or $A:$A or A:$A or $A:A
+ if (rRef.IsEntireCol(rLimits))
+ return SINGLETON_COL;
+
+ // Same if not in named expression and both rows of entire columns are
+ // relative references.
+ if (!bFromRangeName && rAbs1.Row() == 0 && rAbs2.Row() == rLimits.mnMaxRow &&
+ rRef.Ref1.IsRowRel() && rRef.Ref2.IsRowRel())
+ return SINGLETON_COL;
+
+ // 1:1 or $1:$1 or 1:$1 or $1:1
+ if (rRef.IsEntireRow(rLimits))
+ return SINGLETON_ROW;
+
+ // Same if not in named expression and both columns of entire rows are
+ // relative references.
+ if (!bFromRangeName && rAbs1.Col() == 0 && rAbs2.Col() == rLimits.mnMaxCol &&
+ rRef.Ref1.IsColRel() && rRef.Ref2.IsColRel())
+ return SINGLETON_ROW;
+
+ return SINGLETON_NONE;
+ }
+
+ virtual void makeRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer,
+ formula::FormulaGrammar::Grammar /*eGram*/,
+ const ScAddress& rPos,
+ const OUString& rErrRef, const std::vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef,
+ bool bSingleRef,
+ bool bFromRangeName ) const override
+ {
+ // In case absolute/relative positions weren't separately available:
+ // transform relative to absolute!
+ ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
+ if( !bSingleRef )
+ aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
+
+ SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
+ getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
+ MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, false, eSingleton);
+ if (!bSingleRef)
+ {
+ rBuffer.append(':');
+ MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false,
+ eSingleton);
+ }
+ }
+
+ virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
+ {
+ switch (eSymType)
+ {
+ case ScCompiler::Convention::ABS_SHEET_PREFIX:
+ return '$';
+ case ScCompiler::Convention::SHEET_SEPARATOR:
+ return '.';
+ }
+
+ return u'\0';
+ }
+
+ virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
+ const ScDocument& rDoc,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
+ {
+ return lcl_parseExternalName(rSymbol, rFile, rName, '#', rDoc, pExternalLinks);
+ }
+
+ virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
+ const OUString& rName ) const override
+ {
+ return lcl_makeExternalNameStr( rFile, rName, '#', false);
+ }
+
+ static bool makeExternalSingleRefStr(
+ const ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
+ const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl )
+ {
+ ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
+ if (bDisplayTabName)
+ {
+ OUString aFile;
+ if (bEncodeUrl)
+ aFile = rFileName;
+ else
+ aFile = INetURLObject::decode(rFileName, INetURLObject::DecodeMechanism::Unambiguous);
+
+ rBuffer.append("'" + aFile.replaceAll("'", "''") + "'#");
+
+ if (!rRef.IsTabRel())
+ rBuffer.append('$');
+ ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
+
+ rBuffer.append('.');
+ }
+
+ if (!rRef.IsColRel())
+ rBuffer.append('$');
+ MakeColStr( rLimits, rBuffer, aAbsRef.Col());
+ if (!rRef.IsRowRel())
+ rBuffer.append('$');
+ MakeRowStr( rLimits, rBuffer, aAbsRef.Row());
+
+ return true;
+ }
+
+ static void makeExternalRefStrImpl(
+ const ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
+ const OUString& rTabName, const ScSingleRefData& rRef, bool bODF )
+ {
+ if (bODF)
+ rBuffer.append( '[');
+
+ bool bEncodeUrl = bODF;
+ makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
+ if (bODF)
+ rBuffer.append( ']');
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const OUString& rTabName, const ScSingleRefData& rRef ) const override
+ {
+ makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, false);
+ }
+
+ static void makeExternalRefStrImpl(
+ const ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
+ const std::vector<OUString>& rTabNames, const OUString& rTabName,
+ const ScComplexRefData& rRef, bool bODF )
+ {
+ ScRange aAbsRange = rRef.toAbs(rLimits, rPos);
+
+ if (bODF)
+ rBuffer.append( '[');
+ // Ensure that there's always a closing bracket, no premature returns.
+ bool bEncodeUrl = bODF;
+
+ do
+ {
+ if (!makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
+ break;
+
+ rBuffer.append(':');
+
+ OUString aLastTabName;
+ bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
+ if (bDisplayTabName)
+ {
+ // Get the name of the last table.
+ if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
+ {
+ OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
+ // aLastTabName contains #REF!, proceed.
+ }
+ }
+ else if (bODF)
+ rBuffer.append( '.'); // need at least the sheet separator in ODF
+ makeExternalSingleRefStr(rLimits,
+ rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
+ } while (false);
+
+ if (bODF)
+ rBuffer.append( ']');
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const std::vector<OUString>& rTabNames, const OUString& rTabName,
+ const ScComplexRefData& rRef ) const override
+ {
+ makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
+ }
+};
+
+struct ConventionOOO_A1_ODF : public ConventionOOO_A1
+{
+ ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
+
+ virtual void makeRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer,
+ formula::FormulaGrammar::Grammar eGram,
+ const ScAddress& rPos,
+ const OUString& rErrRef, const std::vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef,
+ bool bSingleRef,
+ bool bFromRangeName ) const override
+ {
+ rBuffer.append('[');
+ // In case absolute/relative positions weren't separately available:
+ // transform relative to absolute!
+ ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
+ if( !bSingleRef )
+ aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
+
+ if (FormulaGrammar::isODFF(eGram) && (rRef.Ref1.IsDeleted() || !rLimits.ValidAddress(aAbs1) ||
+ (!bSingleRef && (rRef.Ref2.IsDeleted() || !rLimits.ValidAddress(aAbs2)))))
+ {
+ rBuffer.append(rErrRef);
+ // For ODFF write [#REF!], but not for PODF so apps reading ODF
+ // 1.0/1.1 may have a better chance if they implemented the old
+ // form.
+ }
+ else
+ {
+ SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
+ getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
+ MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, true, eSingleton);
+ if (!bSingleRef)
+ {
+ rBuffer.append(':');
+ MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true,
+ eSingleton);
+ }
+ }
+ rBuffer.append(']');
+ }
+
+ virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
+ const OUString& rName ) const override
+ {
+ return lcl_makeExternalNameStr( rFile, rName, '#', true);
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const OUString& rTabName, const ScSingleRefData& rRef ) const override
+ {
+ makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, true);
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const std::vector<OUString>& rTabNames,
+ const OUString& rTabName, const ScComplexRefData& rRef ) const override
+ {
+ makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
+ }
+};
+
+struct ConventionXL
+{
+ virtual ~ConventionXL()
+ {
+ }
+
+ static void GetTab(
+ const ScSheetLimits& rLimits,
+ const ScAddress& rPos, const std::vector<OUString>& rTabNames,
+ const ScSingleRefData& rRef, OUString& rTabName )
+ {
+ ScAddress aAbs = rRef.toAbs(rLimits, rPos);
+ if (rRef.IsTabDeleted() || o3tl::make_unsigned(aAbs.Tab()) >= rTabNames.size())
+ {
+ rTabName = ScResId( STR_NO_REF_TABLE );
+ return;
+ }
+ rTabName = rTabNames[aAbs.Tab()];
+ }
+
+ static void MakeTabStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf,
+ const ScAddress& rPos,
+ const std::vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef,
+ bool bSingleRef )
+ {
+ if( !rRef.Ref1.IsFlag3D() )
+ return;
+
+ OUString aStartTabName, aEndTabName;
+
+ GetTab(rLimits, rPos, rTabNames, rRef.Ref1, aStartTabName);
+
+ if( !bSingleRef && rRef.Ref2.IsFlag3D() )
+ {
+ GetTab(rLimits, rPos, rTabNames, rRef.Ref2, aEndTabName);
+ }
+
+ rBuf.append( aStartTabName );
+ if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
+ {
+ rBuf.append( ':' );
+ rBuf.append( aEndTabName );
+ }
+
+ rBuf.append( '!' );
+ }
+
+ static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
+ {
+ switch (eSymType)
+ {
+ case ScCompiler::Convention::ABS_SHEET_PREFIX:
+ return u'\0';
+ case ScCompiler::Convention::SHEET_SEPARATOR:
+ return '!';
+ }
+ return u'\0';
+ }
+
+ static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
+ const ScDocument& rDoc,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
+ {
+ return lcl_parseExternalName( rSymbol, rFile, rName, '!', rDoc, pExternalLinks);
+ }
+
+ static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
+ {
+ return lcl_makeExternalNameStr( rFile, rName, '!', false);
+ }
+
+ static void makeExternalDocStr( OUStringBuffer& rBuffer, std::u16string_view rFullName )
+ {
+ // Format that is easier to deal with inside OOo, because we use file
+ // URL, and all characters are allowed. Check if it makes sense to do
+ // it the way Gnumeric does it. Gnumeric doesn't use the URL form
+ // and allows relative file path.
+ //
+ // ['file:///path/to/source/filename.xls']
+
+ rBuffer.append('[');
+ rBuffer.append('\'');
+ OUString aFullName = INetURLObject::decode(rFullName, INetURLObject::DecodeMechanism::Unambiguous);
+
+ const sal_Unicode* pBuf = aFullName.getStr();
+ sal_Int32 nLen = aFullName.getLength();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ const sal_Unicode c = pBuf[i];
+ if (c == '\'')
+ rBuffer.append(c);
+ rBuffer.append(c);
+ }
+ rBuffer.append('\'');
+ rBuffer.append(']');
+ }
+
+ static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
+ const vector<OUString>& rTabNames,
+ const ScRange& rRef )
+ {
+ OUString aLastTabName;
+ if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
+ {
+ ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
+ return;
+ }
+
+ ScRangeStringConverter::AppendTableName(rBuf, rTabName);
+ if (rTabName != aLastTabName)
+ {
+ rBuf.append(':');
+ ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
+ }
+ }
+
+ virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos ) const
+ {
+ sal_Int32 nLen = rFormula.getLength();
+ const sal_Unicode* p = rFormula.getStr();
+ sal_Unicode cPrev = 0;
+ for (sal_Int32 i = rSrcPos; i < nLen; ++i)
+ {
+ sal_Unicode c = p[i];
+ if (i == rSrcPos)
+ {
+ // first character must be '['.
+ if (c != '[')
+ return;
+ }
+ else if (i == rSrcPos + 1)
+ {
+ // second character must be a single quote.
+ if (c != '\'')
+ return;
+ }
+ else if (c == '\'')
+ {
+ if (cPrev == '\'')
+ // two successive single quote is treated as a single
+ // valid character.
+ c = 'a';
+ }
+ else if (c == ']')
+ {
+ if (cPrev == '\'')
+ {
+ // valid source document path found. Increment the
+ // current position to skip the source path.
+ rSrcPos = i + 1;
+ if (rSrcPos >= nLen)
+ rSrcPos = nLen - 1;
+ return;
+ }
+ else
+ return;
+ }
+ else
+ {
+ // any other character
+ if (i > rSrcPos + 2 && cPrev == '\'')
+ // unless it's the 3rd character, a normal character
+ // following immediately a single quote is invalid.
+ return;
+ }
+ cPrev = c;
+ }
+ }
+};
+
+struct ConventionXL_A1 : public Convention_A1, public ConventionXL
+{
+ ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
+ explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
+
+ static void makeSingleCellStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs )
+ {
+ if (!rRef.IsColRel())
+ rBuf.append('$');
+ MakeColStr(rLimits, rBuf, rAbs.Col());
+ if (!rRef.IsRowRel())
+ rBuf.append('$');
+ MakeRowStr(rLimits, rBuf, rAbs.Row());
+ }
+
+ virtual void makeRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuf,
+ formula::FormulaGrammar::Grammar /*eGram*/,
+ const ScAddress& rPos,
+ const OUString& rErrRef, const std::vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef,
+ bool bSingleRef,
+ bool /*bFromRangeName*/ ) const override
+ {
+ ScComplexRefData aRef( rRef );
+
+ // Play fast and loose with invalid refs. There is not much point in producing
+ // Foo!A1:#REF! versus #REF! at this point
+ ScAddress aAbs1 = aRef.Ref1.toAbs(rLimits, rPos), aAbs2;
+
+ MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
+
+ if (!rLimits.ValidAddress(aAbs1))
+ {
+ rBuf.append(rErrRef);
+ return;
+ }
+
+ if( !bSingleRef )
+ {
+ aAbs2 = aRef.Ref2.toAbs(rLimits, rPos);
+ if (!rLimits.ValidAddress(aAbs2))
+ {
+ rBuf.append(rErrRef);
+ return;
+ }
+
+ if (aAbs1.Col() == 0 && aAbs2.Col() >= rLimits.mnMaxCol)
+ {
+ if (!aRef.Ref1.IsRowRel())
+ rBuf.append( '$' );
+ MakeRowStr(rLimits, rBuf, aAbs1.Row());
+ rBuf.append( ':' );
+ if (!aRef.Ref2.IsRowRel())
+ rBuf.append( '$' );
+ MakeRowStr(rLimits, rBuf, aAbs2.Row());
+ return;
+ }
+
+ if (aAbs1.Row() == 0 && aAbs2.Row() >= rLimits.mnMaxRow)
+ {
+ if (!aRef.Ref1.IsColRel())
+ rBuf.append( '$' );
+ MakeColStr(rLimits, rBuf, aAbs1.Col());
+ rBuf.append( ':' );
+ if (!aRef.Ref2.IsColRel())
+ rBuf.append( '$' );
+ MakeColStr(rLimits, rBuf, aAbs2.Col());
+ return;
+ }
+ }
+
+ makeSingleCellStr(rLimits, rBuf, aRef.Ref1, aAbs1);
+ if (!bSingleRef)
+ {
+ rBuf.append( ':' );
+ makeSingleCellStr(rLimits, rBuf, aRef.Ref2, aAbs2);
+ }
+ }
+
+ virtual ParseResult parseAnyToken( const OUString& rFormula,
+ sal_Int32 nSrcPos,
+ const CharClass* pCharClass,
+ bool bGroupSeparator) const override
+ {
+ parseExternalDocName(rFormula, nSrcPos);
+
+ ParseResult aRet;
+ if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
+ return aRet;
+
+ constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
+ KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
+ constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
+ // '?' allowed in range names
+ static constexpr OUString aAddAllowed(u"?!"_ustr);
+ return pCharClass->parseAnyToken( rFormula,
+ nSrcPos, nStartFlags, aAddAllowed,
+ (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
+ aAddAllowed );
+ }
+
+ virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
+ {
+ return ConventionXL::getSpecialSymbol(eSymType);
+ }
+
+ virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
+ const ScDocument& rDoc,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
+ {
+ return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
+ }
+
+ virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
+ const OUString& rName ) const override
+ {
+ return ConventionXL::makeExternalNameStr(rFile, rName);
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const OUString& rTabName, const ScSingleRefData& rRef ) const override
+ {
+ // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
+ // This is a little different from the format Excel uses, as Excel
+ // puts [] only around the file name. But we need to enclose the
+ // whole file path with [] because the file name can contain any
+ // characters.
+
+ ConventionXL::makeExternalDocStr(rBuffer, rFileName);
+ ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
+ rBuffer.append('!');
+
+ makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const std::vector<OUString>& rTabNames, const OUString& rTabName,
+ const ScComplexRefData& rRef ) const override
+ {
+ ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
+
+ ConventionXL::makeExternalDocStr(rBuffer, rFileName);
+ ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
+ rBuffer.append('!');
+
+ makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
+ if (aAbsRef.aStart != aAbsRef.aEnd)
+ {
+ rBuffer.append(':');
+ makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
+ }
+ }
+};
+
+struct ConventionXL_OOX : public ConventionXL_A1
+{
+ ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
+
+ virtual void makeRefStr( ScSheetLimits& rLimits,
+ OUStringBuffer& rBuf,
+ formula::FormulaGrammar::Grammar eGram,
+ const ScAddress& rPos,
+ const OUString& rErrRef, const std::vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef,
+ bool bSingleRef,
+ bool bFromRangeName ) const override
+ {
+ // In OOXML relative references in named expressions are relative to
+ // column 0 and row 0. Relative sheet references don't exist.
+ ScAddress aPos( rPos );
+ if (bFromRangeName)
+ {
+ // XXX NOTE: by decrementing the reference position we may end up
+ // with resolved references with negative values. There's no proper
+ // way to solve that or wrap them around without sheet dimensions
+ // that are stored along. That, or blindly assume fixed dimensions
+ // here and in import.
+ /* TODO: maybe do that blind fixed dimensions wrap? */
+ aPos.SetCol(0);
+ aPos.SetRow(0);
+ }
+
+ if (rRef.Ref1.IsDeleted() || (!bSingleRef && rRef.Ref2.IsDeleted()))
+ {
+ // For OOXML write plain "#REF!" instead of detailed sheet/col/row
+ // information.
+ rBuf.append(rErrRef);
+ return;
+ }
+
+ {
+ ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos);
+ if (!rLimits.ValidAddress(aAbs1)
+ || o3tl::make_unsigned(aAbs1.Tab()) >= rTabNames.size())
+ {
+ rBuf.append(rErrRef);
+ return;
+ }
+ }
+
+ if (!bSingleRef)
+ {
+ ScAddress aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
+ if (!rLimits.ValidAddress(aAbs2)
+ || o3tl::make_unsigned(aAbs2.Tab()) >= rTabNames.size())
+ {
+ rBuf.append(rErrRef);
+ return;
+ }
+ }
+
+ ConventionXL_A1::makeRefStr( rLimits, rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName);
+ }
+
+ virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/,
+ const OUString& rName ) const override
+ {
+ // [N]!DefinedName is a workbook global name.
+ return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName );
+
+ /* TODO: add support for sheet local names, would be
+ * [N]'Sheet Name'!DefinedName
+ * Similar to makeExternalRefStr() but with DefinedName instead of
+ * CellStr. */
+ }
+
+ virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPos) const override
+ {
+ sal_Int32 nLen = rFormula.getLength();
+ const sal_Unicode* p = rFormula.getStr();
+ for (sal_Int32 i = rSrcPos; i < nLen; ++i)
+ {
+ sal_Unicode c = p[i];
+ if (i == rSrcPos)
+ {
+ // first character must be '['.
+ if (c != '[')
+ return;
+ }
+ else if (c == ']')
+ {
+ rSrcPos = i + 1;
+ return;
+ }
+ }
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
+ const OUString& rTabName, const ScSingleRefData& rRef ) const override
+ {
+ // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
+ // Where N is a 1-based positive integer number of a file name in OOXML
+ // xl/externalLinks/externalLinkN.xml
+
+ OUString aQuotedTab( rTabName);
+ ScCompiler::CheckTabQuotes( aQuotedTab);
+ if (!aQuotedTab.isEmpty() && aQuotedTab[0] == '\'')
+ {
+ rBuffer.append('\'');
+ ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
+ rBuffer.append( aQuotedTab.subView(1));
+ }
+ else
+ {
+ ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
+ rBuffer.append( aQuotedTab);
+ }
+ rBuffer.append('!');
+
+ makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
+ const std::vector<OUString>& rTabNames, const OUString& rTabName,
+ const ScComplexRefData& rRef ) const override
+ {
+ // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
+ // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
+ // simpler to produce and more logical form with independently quoted
+ // sheet names as well. The [N] having to be within the quoted sheet
+ // name is ugly enough...
+
+ ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
+
+ OUStringBuffer aBuf;
+ ConventionXL::makeExternalTabNameRange( aBuf, rTabName, rTabNames, aAbsRef);
+ if (!aBuf.isEmpty() && aBuf[0] == '\'')
+ {
+ rBuffer.append('\'');
+ ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
+ rBuffer.append( aBuf.subView(1));
+ }
+ else
+ {
+ ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
+ rBuffer.append( aBuf);
+ }
+ rBuffer.append('!');
+
+ makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
+ if (aAbsRef.aStart != aAbsRef.aEnd)
+ {
+ rBuffer.append(':');
+ makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
+ }
+ }
+
+ static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId )
+ {
+ rBuffer.append("[" + OUString::number( static_cast<sal_Int32>(nFileId+1) ) + "]");
+ }
+};
+
+}
+
+static void
+r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
+{
+ rBuf.append( 'C' );
+ if( rRef.IsColRel() )
+ {
+ SCCOL nCol = rRef.Col();
+ if (nCol != 0)
+ rBuf.append("[" + OUString::number(nCol) + "]");
+ }
+ else
+ rBuf.append( static_cast<sal_Int32>(rAbsRef.Col() + 1) );
+}
+static void
+r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
+{
+ rBuf.append( 'R' );
+ if( rRef.IsRowRel() )
+ {
+ if (rRef.Row() != 0)
+ {
+ rBuf.append("[" + OUString::number(rRef.Row()) + "]");
+ }
+ }
+ else
+ rBuf.append( rAbsRef.Row() + 1 );
+}
+
+namespace {
+
+struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
+{
+ ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
+
+ virtual void makeRefStr( ScSheetLimits& rLimits,
+ OUStringBuffer& rBuf,
+ formula::FormulaGrammar::Grammar /*eGram*/,
+ const ScAddress& rPos,
+ const OUString& rErrRef, const std::vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef,
+ bool bSingleRef,
+ bool /*bFromRangeName*/ ) const override
+ {
+ ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
+ ScComplexRefData aRef( rRef );
+
+ MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
+
+ // Play fast and loose with invalid refs. There is not much point in producing
+ // Foo!A1:#REF! versus #REF! at this point
+ if (!rLimits.ValidCol(aAbsRef.aStart.Col()) || !rLimits.ValidRow(aAbsRef.aStart.Row()))
+ {
+ rBuf.append(rErrRef);
+ return;
+ }
+
+ if( !bSingleRef )
+ {
+ if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
+ {
+ rBuf.append(rErrRef);
+ return;
+ }
+
+ if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
+ {
+ r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
+ if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
+ rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
+ {
+ rBuf.append( ':' );
+ r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
+ }
+ return;
+
+ }
+
+ if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
+ {
+ r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
+ if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
+ rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
+ {
+ rBuf.append( ':' );
+ r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
+ }
+ return;
+ }
+ }
+
+ r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
+ r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
+ if (!bSingleRef)
+ {
+ rBuf.append( ':' );
+ r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
+ r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
+ }
+ }
+
+ ParseResult parseAnyToken( const OUString& rFormula,
+ sal_Int32 nSrcPos,
+ const CharClass* pCharClass,
+ bool bGroupSeparator) const override
+ {
+ parseExternalDocName(rFormula, nSrcPos);
+
+ ParseResult aRet;
+ if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
+ return aRet;
+
+ constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
+ KParseTokens::ASC_UNDERSCORE ;
+ constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
+ // '?' allowed in range names
+ static constexpr OUString aAddAllowed(u"?-[]!"_ustr);
+
+ return pCharClass->parseAnyToken( rFormula,
+ nSrcPos, nStartFlags, aAddAllowed,
+ (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
+ aAddAllowed );
+ }
+
+ virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
+ {
+ return ConventionXL::getSpecialSymbol(eSymType);
+ }
+
+ virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
+ const ScDocument& rDoc,
+ const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
+ {
+ return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
+ }
+
+ virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
+ const OUString& rName ) const override
+ {
+ return ConventionXL::makeExternalNameStr(rFile, rName);
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const OUString& rTabName, const ScSingleRefData& rRef ) const override
+ {
+ // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
+ // This is a little different from the format Excel uses, as Excel
+ // puts [] only around the file name. But we need to enclose the
+ // whole file path with [] because the file name can contain any
+ // characters.
+
+ ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
+ ConventionXL::makeExternalDocStr(rBuffer, rFileName);
+ ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
+ rBuffer.append('!');
+
+ r1c1_add_row(rBuffer, rRef, aAbsRef);
+ r1c1_add_col(rBuffer, rRef, aAbsRef);
+ }
+
+ virtual void makeExternalRefStr(
+ ScSheetLimits& rLimits,
+ OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
+ const std::vector<OUString>& rTabNames, const OUString& rTabName,
+ const ScComplexRefData& rRef ) const override
+ {
+ ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
+
+ ConventionXL::makeExternalDocStr(rBuffer, rFileName);
+ ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
+ rBuffer.append('!');
+
+ if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
+ {
+ rBuffer.append(ScResId(STR_NO_REF_TABLE));
+ return;
+ }
+
+ if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
+ {
+ r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
+ if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
+ {
+ rBuffer.append(':');
+ r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
+ }
+ return;
+ }
+
+ if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
+ {
+ r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
+ if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
+ {
+ rBuffer.append(':');
+ r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
+ }
+ return;
+ }
+
+ r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
+ r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
+ rBuffer.append(':');
+ r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
+ r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
+ }
+
+ virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const override
+ {
+ ScCharFlags nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
+ if (c == '-' && cLast == '[')
+ // '-' can occur within a reference string only after '[' e.g. R[-1]C.
+ nFlags |= ScCharFlags::Ident;
+ return nFlags;
+ }
+};
+
+}
+
+ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, ScTokenArray& rArr,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
+ rDoc(rCxt.getDoc()),
+ aPos(rPos),
+ mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
+ mpInterpreterContext(pContext),
+ mnCurrentSheetTab(-1),
+ mnCurrentSheetEndPos(0),
+ pCharClass(&ScGlobal::getCharClass()),
+ mbCharClassesDiffer(false),
+ mnPredetectedReference(0),
+ mnRangeOpPosInSymbol(-1),
+ pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
+ meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
+ mbCloseBrackets(true),
+ mbRewind(false),
+ mbRefConventionChartOOXML(false),
+ maTabNames(rCxt.getTabNames())
+{
+ SetGrammar(rCxt.getGrammar());
+}
+
+ScCompiler::ScCompiler( ScDocument& rDocument, const ScAddress& rPos, ScTokenArray& rArr,
+ formula::FormulaGrammar::Grammar eGrammar,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
+ rDoc( rDocument ),
+ aPos( rPos ),
+ mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
+ mpInterpreterContext(pContext),
+ mnCurrentSheetTab(-1),
+ mnCurrentSheetEndPos(0),
+ nSrcPos(0),
+ pCharClass( &ScGlobal::getCharClass() ),
+ mbCharClassesDiffer(false),
+ mnPredetectedReference(0),
+ mnRangeOpPosInSymbol(-1),
+ pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
+ meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
+ mbCloseBrackets( true ),
+ mbRewind( false ),
+ mbRefConventionChartOOXML( false )
+{
+ SetGrammar( (eGrammar == formula::FormulaGrammar::GRAM_UNSPECIFIED) ?
+ rDocument.GetGrammar() :
+ eGrammar );
+}
+
+ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(bComputeII, bMatrixFlag),
+ rDoc(rCxt.getDoc()),
+ aPos(rPos),
+ mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
+ mpInterpreterContext(pContext),
+ mnCurrentSheetTab(-1),
+ mnCurrentSheetEndPos(0),
+ pCharClass(&ScGlobal::getCharClass()),
+ mbCharClassesDiffer(false),
+ mnPredetectedReference(0),
+ mnRangeOpPosInSymbol(-1),
+ pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
+ meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
+ mbCloseBrackets(true),
+ mbRewind(false),
+ mbRefConventionChartOOXML(false),
+ maTabNames(rCxt.getTabNames())
+{
+ SetGrammar(rCxt.getGrammar());
+}
+
+ScCompiler::ScCompiler( ScDocument& rDocument, const ScAddress& rPos,
+ formula::FormulaGrammar::Grammar eGrammar,
+ bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
+ : FormulaCompiler(bComputeII, bMatrixFlag),
+ rDoc( rDocument ),
+ aPos( rPos ),
+ mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
+ mpInterpreterContext(pContext),
+ mnCurrentSheetTab(-1),
+ mnCurrentSheetEndPos(0),
+ nSrcPos(0),
+ pCharClass( &ScGlobal::getCharClass() ),
+ mbCharClassesDiffer(false),
+ mnPredetectedReference(0),
+ mnRangeOpPosInSymbol(-1),
+ pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
+ meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
+ mbCloseBrackets( true ),
+ mbRewind( false ),
+ mbRefConventionChartOOXML( false )
+{
+ SetGrammar( (eGrammar == formula::FormulaGrammar::GRAM_UNSPECIFIED) ?
+ rDocument.GetGrammar() :
+ eGrammar );
+}
+
+ScCompiler::~ScCompiler()
+{
+}
+
+void ScCompiler::CheckTabQuotes( OUString& rString,
+ const FormulaGrammar::AddressConvention eConv )
+{
+ sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
+ sal_Int32 nContFlags = nStartFlags;
+ ParseResult aRes = ScGlobal::getCharClass().parsePredefinedToken(
+ KParseType::IDENTNAME, rString, 0, nStartFlags, OUString(), nContFlags, OUString());
+ bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength());
+
+ switch ( eConv )
+ {
+ default :
+ case FormulaGrammar::CONV_UNSPECIFIED :
+ break;
+ case FormulaGrammar::CONV_OOO :
+ case FormulaGrammar::CONV_XL_A1 :
+ case FormulaGrammar::CONV_XL_R1C1 :
+ case FormulaGrammar::CONV_XL_OOX :
+ case FormulaGrammar::CONV_ODF :
+ if( bNeedsQuote )
+ {
+ // escape embedded quotes
+ rString = rString.replaceAll( "'", "''" );
+ }
+ break;
+ }
+
+ if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
+ {
+ // Prevent any possible confusion resulting from pure numeric sheet names.
+ bNeedsQuote = true;
+ }
+
+ if( bNeedsQuote )
+ {
+ rString = "'" + rString + "'";
+ }
+}
+
+sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString )
+{
+ if (rString[0] != '\'')
+ return -1;
+ sal_Int32 nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP);
+ // it must be 'Doc'#
+ if (nPos != -1 && rString[nPos-1] != '\'')
+ nPos = -1;
+ return nPos;
+}
+
+void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
+{
+ const Convention* p = GetRefConvention(eConv);
+ if (p)
+ SetRefConvention(p);
+}
+
+const ScCompiler::Convention* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv )
+{
+
+ switch (eConv)
+ {
+ case FormulaGrammar::CONV_OOO:
+ {
+ static const ConventionOOO_A1 ConvOOO_A1;
+ return &ConvOOO_A1;
+ }
+ case FormulaGrammar::CONV_ODF:
+ {
+ static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
+ return &ConvOOO_A1_ODF;
+ }
+ case FormulaGrammar::CONV_XL_A1:
+ {
+ static const ConventionXL_A1 ConvXL_A1;
+ return &ConvXL_A1;
+ }
+ case FormulaGrammar::CONV_XL_R1C1:
+ {
+ static const ConventionXL_R1C1 ConvXL_R1C1;
+ return &ConvXL_R1C1;
+ }
+ case FormulaGrammar::CONV_XL_OOX:
+ {
+ static const ConventionXL_OOX ConvXL_OOX;
+ return &ConvXL_OOX;
+ }
+ case FormulaGrammar::CONV_UNSPECIFIED:
+ default:
+ ;
+ }
+
+ return nullptr;
+}
+
+void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
+{
+ pConv = pConvP;
+ meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
+ assert( FormulaGrammar::isSupported( meGrammar));
+}
+
+void ScCompiler::SetError(FormulaError nError)
+{
+ if( pArr->GetCodeError() == FormulaError::NONE)
+ pArr->SetCodeError( nError);
+}
+
+static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
+{
+ const sal_Unicode* const pStop = pDst + nMax;
+ while ( pDst < pStop )
+ {
+ *pDst++ = *pSrc++;
+ }
+ *pDst = 0;
+ return pDst;
+}
+
+// p1 MUST contain at least n characters, or terminate with NIL.
+// p2 MUST pass upper case letters, if any.
+// n MUST not be greater than length of p2
+static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n )
+{
+ for (size_t i=0; i<n; ++i)
+ {
+ if (!p1[i])
+ return false;
+ if (p1[i] != p2[i])
+ {
+ if (p1[i] < 'a' || 'z' < p1[i])
+ return false; // not a lower case letter
+ if (p2[i] < 'A' || 'Z' < p2[i])
+ return false; // not a letter to match
+ if (p1[i] != p2[i] + 0x20)
+ return false; // lower case doesn't match either
+ }
+ }
+ return true;
+}
+
+// static
+void ScCompiler::addWhitespace( std::vector<ScCompiler::Whitespace> & rvSpaces,
+ ScCompiler::Whitespace & rSpace, sal_Unicode c, sal_Int32 n )
+{
+ if (rSpace.cChar != c)
+ {
+ if (rSpace.cChar && rSpace.nCount > 0)
+ rvSpaces.emplace_back(rSpace);
+ rSpace.reset(c);
+ }
+ rSpace.nCount += n;
+}
+
+// NextSymbol
+
+// Parses the formula into separate symbols for further processing.
+// XXX NOTE: this is a rough sketch of the original idea, there are other
+// states that were added and didn't make it into this table and things are
+// more complicated. Use the source, Luke.
+
+// initial state = GetChar
+
+// old state | read character | action | new state
+//---------------+-------------------+-----------------------+---------------
+// GetChar | ;()+-*/^=& | Symbol=char | Stop
+// | <> | Symbol=char | GetBool
+// | $ letter | Symbol=char | GetWord
+// | number | Symbol=char | GetValue
+// | " | none | GetString
+// | other | none | GetChar
+//---------------+-------------------+-----------------------+---------------
+// GetBool | => | Symbol=Symbol+char | Stop
+// | other | Dec(CharPos) | Stop
+//---------------+-------------------+-----------------------+---------------
+// GetWord | SepSymbol | Dec(CharPos) | Stop
+// | ()+-*/^=<>&~ | |
+// | space | Dec(CharPos) | Stop
+// | $_:. | |
+// | letter, number | Symbol=Symbol+char | GetWord
+// | other | error | Stop
+//---------------+-------------------+-----------------------+---------------
+// GetValue | ;()*/^=<>& | |
+// | space | Dec(CharPos) | Stop
+// | number E+-%,. | Symbol=Symbol+char | GetValue
+// | other | error | Stop
+//---------------+-------------------+-----------------------+---------------
+// GetString | " | none | Stop
+// | other | Symbol=Symbol+char | GetString
+//---------------+-------------------+-----------------------+---------------
+
+std::vector<ScCompiler::Whitespace> ScCompiler::NextSymbol(bool bInArray)
+{
+ std::vector<Whitespace> vSpaces;
+ cSymbol[MAXSTRLEN] = 0; // end
+ sal_Unicode* pSym = cSymbol;
+ const sal_Unicode* const pStart = aFormula.getStr();
+ const sal_Unicode* pSrc = pStart + nSrcPos;
+ bool bi18n = false;
+ sal_Unicode c = *pSrc;
+ sal_Unicode cLast = 0;
+ bool bQuote = false;
+ mnRangeOpPosInSymbol = -1;
+ ScanState eState = ssGetChar;
+ Whitespace aSpace;
+ sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
+ sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
+ sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
+ sal_Unicode cDecSep = (mxSymbols->isEnglishLocale() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0]);
+ sal_Unicode cDecSepAlt = (mxSymbols->isEnglishLocale() ? 0 : ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar());
+
+ // special symbols specific to address convention used
+ sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
+ sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
+
+ int nDecSeps = 0;
+ bool bAutoIntersection = false;
+ size_t nAutoIntersectionSpacesPos = 0;
+ int nRefInName = 0;
+ bool bErrorConstantHadSlash = false;
+ mnPredetectedReference = 0;
+ // try to parse simple tokens before calling i18n parser
+ while ((c != 0) && (eState != ssStop) )
+ {
+ pSrc++;
+ ScCharFlags nMask = GetCharTableFlags( c, cLast );
+
+ // The parameter separator and the array column and row separators end
+ // things unconditionally if not in string or reference.
+ if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
+ {
+ switch (eState)
+ {
+ // these are to be continued
+ case ssGetString:
+ case ssSkipString:
+ case ssGetReference:
+ case ssSkipReference:
+ case ssGetTableRefItem:
+ case ssGetTableRefColumn:
+ break;
+ default:
+ if (eState == ssGetChar)
+ *pSym++ = c;
+ else
+ pSrc--;
+ eState = ssStop;
+ }
+ }
+Label_MaskStateMachine:
+ switch (eState)
+ {
+ case ssGetChar :
+ {
+ // Order is important!
+ if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']')
+ {
+ *pSym++ = c;
+ eState = ssGetTableRefColumn;
+ }
+ else if( nMask & ScCharFlags::OdfLabelOp )
+ {
+ // '!!' automatic intersection
+ if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfLabelOp)
+ {
+ /* TODO: For now the UI "space operator" is used, this
+ * could be enhanced using a specialized OpCode to get
+ * rid of the space ambiguity, which would need some
+ * places to be adapted though. And we would still need
+ * to support the ambiguous space operator for UI
+ * purposes anyway. However, we then could check for
+ * invalid usage of '!!', which currently isn't
+ * possible. */
+ if (!bAutoIntersection)
+ {
+ ++pSrc;
+ // Add 2 because it must match the character count
+ // for bi18n.
+ addWhitespace( vSpaces, aSpace, 0x20, 2);
+ // Position of Whitespace where it will be added to
+ // vector.
+ nAutoIntersectionSpacesPos = vSpaces.size();
+ bAutoIntersection = true;
+ }
+ else
+ {
+ pSrc--;
+ eState = ssStop;
+ }
+ }
+ else
+ {
+ nMask &= ~ScCharFlags::OdfLabelOp;
+ goto Label_MaskStateMachine;
+ }
+ }
+ else if( nMask & ScCharFlags::OdfNameMarker )
+ {
+ // '$$' defined name marker
+ if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfNameMarker)
+ {
+ // both eaten, not added to pSym
+ ++pSrc;
+ }
+ else
+ {
+ nMask &= ~ScCharFlags::OdfNameMarker;
+ goto Label_MaskStateMachine;
+ }
+ }
+ else if( nMask & ScCharFlags::Char )
+ {
+ // '[' is a special case in Excel syntax, it can start an
+ // external reference, ID in OOXML like [1]Sheet1!A1 or
+ // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
+ // [filename]Sheet!R1C1 that needs to be scanned
+ // entirely, or can be ocTableRefOpen, of which the first
+ // transforms an ocDBArea into an ocTableRef.
+ if (c == '[' && FormulaGrammar::isExcelSyntax( meGrammar)
+ && eLastOp != ocDBArea && maTableRefs.empty())
+ {
+ // [0]!Global_Range_Name, is a special case in OOXML
+ // syntax, where the '0' is referencing to self and we
+ // do not need it, so we should skip it, in order to
+ // later it will be more recognisable for IsNamedRange.
+ if (FormulaGrammar::isRefConventionOOXML(meGrammar) &&
+ pSrc[0] == '0' && pSrc[1] == ']' && pSrc[2] == '!')
+ {
+ pSrc += 3;
+ c = *pSrc;
+ continue;
+ }
+
+ nMask &= ~ScCharFlags::Char;
+ goto Label_MaskStateMachine;
+ }
+ else
+ {
+ *pSym++ = c;
+ eState = ssStop;
+ }
+ }
+ else if( nMask & ScCharFlags::OdfLBracket )
+ {
+ // eaten, not added to pSym
+ eState = ssGetReference;
+ mnPredetectedReference = 1;
+ }
+ else if( nMask & ScCharFlags::CharBool )
+ {
+ *pSym++ = c;
+ eState = ssGetBool;
+ }
+ else if( nMask & ScCharFlags::CharValue )
+ {
+ *pSym++ = c;
+ eState = ssGetValue;
+ }
+ else if( nMask & ScCharFlags::CharString )
+ {
+ *pSym++ = c;
+ eState = ssGetString;
+ }
+ else if( nMask & ScCharFlags::CharErrConst )
+ {
+ *pSym++ = c;
+ if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
+ eState = ssGetTableRefItem;
+ else
+ eState = ssGetErrorConstant;
+ }
+ else if( nMask & ScCharFlags::CharDontCare )
+ {
+ addWhitespace( vSpaces, aSpace, c);
+ }
+ else if( nMask & ScCharFlags::CharIdent )
+ { // try to get a simple ASCII identifier before calling
+ // i18n, to gain performance during import
+ *pSym++ = c;
+ eState = ssGetIdent;
+ }
+ else
+ {
+ bi18n = true;
+ eState = ssStop;
+ }
+ }
+ break;
+ case ssGetIdent:
+ {
+ if ( nMask & ScCharFlags::Ident )
+ { // This catches also $Sheet1.A$1, for example.
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError(FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else
+ *pSym++ = c;
+ }
+ else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4))
+ {
+ // Completely ugly means to catch broken
+ // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
+ // references that were written in ODF named ranges
+ // (without embracing [] hence no predetected reference)
+ // and to OOXML and handle them as one symbol.
+ // Also catches these in UI, so we can process them
+ // further.
+ int i = 0;
+ for ( ; i<5; ++i)
+ {
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError(FormulaError::StringOverflow);
+ eState = ssStop;
+ break; // for
+ }
+ else
+ {
+ *pSym++ = c;
+ c = *pSrc++;
+ }
+ }
+ if (i == 5)
+ c = *((--pSrc)-1); // position last/next character correctly
+ }
+ else if (c == ':' && mnRangeOpPosInSymbol < 0)
+ {
+ // One range operator may form Sheet1.A:A, which we need to
+ // pass as one entity to IsReference().
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError(FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else
+ {
+ mnRangeOpPosInSymbol = pSym - &cSymbol[0];
+ *pSym++ = c;
+ }
+ }
+ else if ( 128 <= c || '\'' == c )
+ { // High values need reparsing with i18n,
+ // single quoted $'sheet' names too (otherwise we'd had to
+ // implement everything twice).
+ bi18n = true;
+ eState = ssStop;
+ }
+ else
+ {
+ pSrc--;
+ eState = ssStop;
+ }
+ }
+ break;
+ case ssGetBool :
+ {
+ if( nMask & ScCharFlags::Bool )
+ {
+ *pSym++ = c;
+ eState = ssStop;
+ }
+ else
+ {
+ pSrc--;
+ eState = ssStop;
+ }
+ }
+ break;
+ case ssGetValue :
+ {
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError(FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else if (c == cDecSep || (cDecSepAlt && c == cDecSepAlt))
+ {
+ if (++nDecSeps > 1)
+ {
+ // reparse with i18n, may be numeric sheet name as well
+ bi18n = true;
+ eState = ssStop;
+ }
+ else
+ *pSym++ = c;
+ }
+ else if( nMask & ScCharFlags::Value )
+ *pSym++ = c;
+ else if( nMask & ScCharFlags::ValueSep )
+ {
+ pSrc--;
+ eState = ssStop;
+ }
+ else if (c == 'E' || c == 'e')
+ {
+ if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueExp)
+ *pSym++ = c;
+ else
+ {
+ // reparse with i18n
+ bi18n = true;
+ eState = ssStop;
+ }
+ }
+ else if( nMask & ScCharFlags::ValueSign )
+ {
+ if (((cLast == 'E') || (cLast == 'e')) &&
+ (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueValue))
+ {
+ *pSym++ = c;
+ }
+ else
+ {
+ pSrc--;
+ eState = ssStop;
+ }
+ }
+ else
+ {
+ // reparse with i18n
+ bi18n = true;
+ eState = ssStop;
+ }
+ }
+ break;
+ case ssGetString :
+ {
+ if( nMask & ScCharFlags::StringSep )
+ {
+ if ( !bQuote )
+ {
+ if ( *pSrc == '"' )
+ bQuote = true; // "" => literal "
+ else
+ eState = ssStop;
+ }
+ else
+ bQuote = false;
+ }
+ if ( !bQuote )
+ {
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError(FormulaError::StringOverflow);
+ eState = ssSkipString;
+ }
+ else
+ *pSym++ = c;
+ }
+ }
+ break;
+ case ssSkipString:
+ if( nMask & ScCharFlags::StringSep )
+ eState = ssStop;
+ break;
+ case ssGetErrorConstant:
+ {
+ // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
+ // BUT, in UI these may have been translated! So don't
+ // check for ASCII alnum. Note that this construct can't be
+ // parsed with i18n.
+ /* TODO: be strict when reading ODFF, check for ASCII alnum
+ * and proper continuation after '/'. However, even with
+ * the lax parsing only the error constants we have defined
+ * as opcode symbols will be recognized and others result
+ * in ocBad, so the result is actually conformant. */
+ bool bAdd = true;
+ if ('?' == c)
+ eState = ssStop;
+ else if ('!' == c)
+ {
+ // Check if this is #REF! that starts an invalid reference.
+ // Note we have an implicit '!' here at the end.
+ if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) &&
+ (GetCharTableFlags( *pSrc, c) & ScCharFlags::Ident))
+ eState = ssGetIdent;
+ else
+ eState = ssStop;
+ }
+ else if ('/' == c)
+ {
+ if (!bErrorConstantHadSlash)
+ bErrorConstantHadSlash = true;
+ else
+ {
+ bAdd = false;
+ eState = ssStop;
+ }
+ }
+ else if ((nMask & ScCharFlags::WordSep) ||
+ (c < 128 && !rtl::isAsciiAlphanumeric( c)))
+ {
+ bAdd = false;
+ eState = ssStop;
+ }
+ if (!bAdd)
+ --pSrc;
+ else
+ {
+ if (pSym == &cSymbol[ MAXSTRLEN ])
+ {
+ SetError( FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else
+ *pSym++ = c;
+ }
+ }
+ break;
+ case ssGetTableRefItem:
+ {
+ // Scan whatever up to the next ']' closer.
+ if (c != ']')
+ {
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError( FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else
+ *pSym++ = c;
+ }
+ else
+ {
+ --pSrc;
+ eState = ssStop;
+ }
+ }
+ break;
+ case ssGetTableRefColumn:
+ {
+ // Scan whatever up to the next unescaped ']' closer.
+ if (c != ']' || cLast == '\'')
+ {
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError( FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else
+ *pSym++ = c;
+ }
+ else
+ {
+ --pSrc;
+ eState = ssStop;
+ }
+ }
+ break;
+ case ssGetReference:
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError( FormulaError::StringOverflow);
+ eState = ssSkipReference;
+ }
+ [[fallthrough]];
+ case ssSkipReference:
+ // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
+ // mandatory also if no sheet name. 'External'# is optional,
+ // sheet name is optional, quotes around sheet name are
+ // optional if no quote contained. [#REF!] is valid.
+ // 2nd usage: ['Sheet'.$$'DefinedName']
+ // 3rd usage: ['External'#$$'DefinedName']
+ // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
+ // Also for all these names quotes are optional if no quote
+ // contained.
+ {
+
+ // nRefInName: 0 := not in sheet name yet. 'External'
+ // is parsed as if it was a sheet name and nRefInName
+ // is reset when # is encountered immediately after closing
+ // quote. Same with 'DefinedName', nRefInName is cleared
+ // when : is encountered.
+
+ // Encountered leading $ before sheet name.
+ constexpr int kDollar = (1 << 1);
+ // Encountered ' opening quote, which may be after $ or
+ // not.
+ constexpr int kOpen = (1 << 2);
+ // Somewhere in name.
+ constexpr int kName = (1 << 3);
+ // Encountered ' in name, will be cleared if double or
+ // transformed to kClose if not, in which case kOpen is
+ // cleared.
+ constexpr int kQuote = (1 << 4);
+ // Past ' closing quote.
+ constexpr int kClose = (1 << 5);
+ // Encountered # file/sheet separator.
+ constexpr int kFileSep = (1 << 6);
+ // Past . sheet name separator.
+ constexpr int kPast = (1 << 7);
+ // Marked name $$ follows sheet name separator, detected
+ // while we're still on the separator. Will be cleared when
+ // entering the name.
+ constexpr int kMarkAhead = (1 << 8);
+ // In marked defined name.
+ constexpr int kDefName = (1 << 9);
+ // Encountered # of #REF!
+ constexpr int kRefErr = (1 << 10);
+
+ bool bAddToSymbol = true;
+ if ((nMask & ScCharFlags::OdfRBracket) && !(nRefInName & kOpen))
+ {
+ OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
+ "ScCompiler::NextSymbol: reference: "
+ "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
+ // eaten, not added to pSym
+ bAddToSymbol = false;
+ eState = ssStop;
+ }
+ else if (cSheetSep == c && nRefInName == 0)
+ {
+ // eat it, no sheet name [.A1]
+ bAddToSymbol = false;
+ nRefInName |= kPast;
+ if ('$' == pSrc[0] && '$' == pSrc[1])
+ nRefInName |= kMarkAhead;
+ }
+ else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
+ {
+ // Not in col/row yet.
+
+ if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
+ nRefInName = 0;
+ else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
+ {
+ nRefInName &= ~kMarkAhead;
+ if (!(nRefInName & kDefName))
+ {
+ // eaten, not added to pSym (2 chars)
+ bAddToSymbol = false;
+ ++pSrc;
+ nRefInName &= kPast;
+ nRefInName |= kDefName;
+ }
+ else
+ {
+ // ScAddress::Parse() will recognize this as
+ // invalid later.
+ if (eState != ssSkipReference)
+ {
+ *pSym++ = c;
+
+ if( pSym == &cSymbol[ MAXSTRLEN ] )
+ {
+ SetError( FormulaError::StringOverflow);
+ eState = ssStop;
+ }
+ else
+ *pSym++ = *pSrc++;
+ }
+ bAddToSymbol = false;
+ }
+ }
+ else if (cSheetPrefix == c && nRefInName == 0)
+ nRefInName |= kDollar;
+ else if ('\'' == c)
+ {
+ // TODO: The conventions' parseExternalName()
+ // should handle quoted names, but as long as they
+ // don't remove non-embedded quotes here.
+ if (!(nRefInName & kName))
+ {
+ nRefInName |= (kOpen | kName);
+ bAddToSymbol = !(nRefInName & kDefName);
+ }
+ else if (!(nRefInName & kOpen))
+ {
+ OSL_FAIL("ScCompiler::NextSymbol: reference: "
+ "a ''' without the name being enclosed in '...' violates ODF spec");
+ }
+ else if (nRefInName & kQuote)
+ {
+ // escaped embedded quote
+ nRefInName &= ~kQuote;
+ }
+ else
+ {
+ switch (pSrc[0])
+ {
+ case '\'':
+ // escapes embedded quote
+ nRefInName |= kQuote;
+ break;
+ case SC_COMPILER_FILE_TAB_SEP:
+ // sheet name should follow
+ nRefInName |= kFileSep;
+ [[fallthrough]];
+ default:
+ // quote not followed by quote => close
+ nRefInName |= kClose;
+ nRefInName &= ~kOpen;
+ }
+ bAddToSymbol = !(nRefInName & kDefName);
+ }
+ }
+ else if ('#' == c && nRefInName == 0)
+ nRefInName |= kRefErr;
+ else if (cSheetSep == c && !(nRefInName & kOpen))
+ {
+ // unquoted sheet name separator
+ nRefInName |= kPast;
+ if ('$' == pSrc[0] && '$' == pSrc[1])
+ nRefInName |= kMarkAhead;
+ }
+ else if (':' == c && !(nRefInName & kOpen))
+ {
+ OSL_FAIL("ScCompiler::NextSymbol: reference: "
+ "range operator ':' without prior sheet name separator '.' violates ODF spec");
+ nRefInName = 0;
+ ++mnPredetectedReference;
+ }
+ else if (!(nRefInName & kName))
+ {
+ // start unquoted name
+ nRefInName |= kName;
+ }
+ }
+ else if (':' == c)
+ {
+ // range operator
+ nRefInName = 0;
+ ++mnPredetectedReference;
+ }
+ if (bAddToSymbol && eState != ssSkipReference)
+ *pSym++ = c; // everything is part of reference
+ }
+ break;
+ case ssStop:
+ ; // nothing, prevent warning
+ break;
+ }
+ cLast = c;
+ c = *pSrc;
+ }
+
+ if (aSpace.nCount && aSpace.cChar)
+ vSpaces.emplace_back(aSpace);
+
+ if ( bi18n )
+ {
+ const sal_Int32 nOldSrcPos = nSrcPos;
+ for (const auto& r : vSpaces)
+ nSrcPos += r.nCount;
+ // If group separator is not a possible operator and not one of any
+ // separators then it may be parsed away in numbers. This is
+ // specifically the case with NO-BREAK SPACE, which actually triggers
+ // the bi18n case (which we don't want to include as yet another
+ // special case above as it is rare enough and doesn't generally occur
+ // in formulas).
+ const sal_Unicode cGroupSep = ScGlobal::getLocaleData().getNumThousandSep()[0];
+ const bool bGroupSeparator = (128 <= cGroupSep && cGroupSep != cSep &&
+ cGroupSep != cArrayColSep && cGroupSep != cArrayRowSep &&
+ cGroupSep != cDecSep && cGroupSep != cDecSepAlt &&
+ cGroupSep != cSheetPrefix && cGroupSep != cSheetSep);
+ // If a numeric context triggered bi18n then use the default locale's
+ // CharClass, this may accept group separator as well.
+ const CharClass* pMyCharClass = (ScGlobal::getCharClass().isDigit( OUString(pStart[nSrcPos]), 0) ?
+ &ScGlobal::getCharClass() : pCharClass);
+ OUStringBuffer aSymbol;
+ mnRangeOpPosInSymbol = -1;
+ FormulaError nErr = FormulaError::NONE;
+ do
+ {
+ bi18n = false;
+ // special case (e.g. $'sheetname' in OOO A1)
+ if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
+ aSymbol.append(pStart[nSrcPos++]);
+
+ ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pMyCharClass, bGroupSeparator);
+
+ if ( !aRes.TokenType )
+ {
+ nErr = FormulaError::IllegalChar;
+ SetError( nErr ); // parsed chars as string
+ }
+ if ( aRes.EndPos <= nSrcPos )
+ {
+ // Could not parse anything meaningful.
+ assert(!aRes.TokenType);
+ nErr = FormulaError::IllegalChar;
+ SetError( nErr );
+ // Caller has to act on an empty symbol for
+ // nSrcPos < aFormula.getLength()
+ nSrcPos = nOldSrcPos;
+ aSymbol.setLength(0);
+ }
+ else
+ {
+ // When having parsed a second reference part, ensure that the
+ // i18n parser did not mistakenly parse a number that included
+ // a separator which happened to be meant as a parameter
+ // separator instead.
+ if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER))
+ {
+ for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i)
+ {
+ if (pStart[i] == cSep)
+ aRes.EndPos = i; // also ends for
+ }
+ }
+ aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
+ nSrcPos = aRes.EndPos;
+ c = pStart[nSrcPos];
+ if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
+ { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
+ bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
+ }
+ // One range operator restarts parsing for second reference.
+ if (c == ':' && mnRangeOpPosInSymbol < 0)
+ {
+ mnRangeOpPosInSymbol = aSymbol.getLength();
+ bi18n = true;
+ }
+ if ( bi18n )
+ aSymbol.append(pStart[nSrcPos++]);
+ }
+ } while ( bi18n && nErr == FormulaError::NONE );
+ sal_Int32 nLen = aSymbol.getLength();
+ if ( nLen > MAXSTRLEN )
+ {
+ SetError( FormulaError::StringOverflow );
+ nLen = MAXSTRLEN;
+ }
+ if (mnRangeOpPosInSymbol >= nLen)
+ mnRangeOpPosInSymbol = -1;
+ lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
+ pSym = &cSymbol[nLen];
+ }
+ else
+ {
+ nSrcPos = pSrc - pStart;
+ *pSym = 0;
+ }
+ if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
+ {
+ // This is a trailing range operator, which is nonsense. Will be caught
+ // in next round.
+ mnRangeOpPosInSymbol = -1;
+ *--pSym = 0;
+ --nSrcPos;
+ }
+ if ( bAutoCorrect )
+ aCorrectedSymbol = OUString(cSymbol, pSym - cSymbol);
+ if (bAutoIntersection && vSpaces[nAutoIntersectionSpacesPos].nCount > 1)
+ --vSpaces[nAutoIntersectionSpacesPos].nCount; // replace '!!' with only one space
+ return vSpaces;
+}
+
+// Convert symbol to token
+
+bool ScCompiler::ParseOpCode( const OUString& rName, bool bInArray )
+{
+ OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
+ bool bFound = (iLook != mxSymbols->getHashMap().end());
+ if (bFound)
+ {
+ OpCode eOp = iLook->second;
+ if (bInArray)
+ {
+ if (rName == mxSymbols->getSymbol(ocArrayColSep))
+ eOp = ocArrayColSep;
+ else if (rName == mxSymbols->getSymbol(ocArrayRowSep))
+ eOp = ocArrayRowSep;
+ }
+ else if (eOp == ocArrayColSep || eOp == ocArrayRowSep)
+ {
+ if (rName == mxSymbols->getSymbol(ocSep))
+ eOp = ocSep;
+ else if (rName == ";")
+ {
+ switch (FormulaGrammar::extractFormulaLanguage( meGrammar))
+ {
+ // Only for languages/grammars that actually use ';'
+ // parameter separator.
+ case css::sheet::FormulaLanguage::NATIVE:
+ case css::sheet::FormulaLanguage::ENGLISH:
+ case css::sheet::FormulaLanguage::ODFF:
+ case css::sheet::FormulaLanguage::ODF_11:
+ eOp = ocSep;
+ }
+ }
+ }
+ else if (eOp == ocCeil && mxSymbols->isOOXML())
+ {
+ // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
+ // unassigned for import.
+ eOp = ocCeil_Math;
+ }
+ else if (eOp == ocFloor && mxSymbols->isOOXML())
+ {
+ // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
+ // unassigned for import.
+ eOp = ocFloor_Math;
+ }
+ maRawToken.SetOpCode(eOp);
+ }
+ else if (mxSymbols->isODFF())
+ {
+ // ODFF names that are not written in the current mapping but to be
+ // recognized. New names will be written in a future release, then
+ // exchange (!) with the names in
+ // formula/source/core/resource/core_resource.src to be able to still
+ // read the old names as well.
+ struct FunctionName
+ {
+ const char* pName;
+ OpCode eOp;
+ };
+ static const FunctionName aOdffAliases[] = {
+ // Renamed old names, still accept them:
+ { "B", ocB }, // B -> BINOM.DIST.RANGE
+ { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
+ { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
+ { "ZGZ", ocRRI }, // ZGZ -> RRI
+ { "COLOR", ocColor }, // COLOR -> ORG.LIBREOFFICE.COLOR
+ { "GOALSEEK", ocBackSolver }, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
+ { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
+ { "COM.MICROSOFT.F.INV", ocFInv_LT } // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
+ // Renamed new names, prepare to read future names:
+ //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
+ };
+ for (const FunctionName& rOdffAlias : aOdffAliases)
+ {
+ if (rName.equalsIgnoreAsciiCaseAscii( rOdffAlias.pName))
+ {
+ maRawToken.SetOpCode( rOdffAlias.eOp);
+ bFound = true;
+ break; // for
+ }
+ }
+ }
+ else if (mxSymbols->isOOXML())
+ {
+ // OOXML names that are not written in the current mapping but to be
+ // recognized as old versions wrote them.
+ struct FunctionName
+ {
+ const char* pName;
+ OpCode eOp;
+ };
+ static const FunctionName aOoxmlAliases[] = {
+ { "EFFECTIVE", ocEffect }, // EFFECTIVE -> EFFECT
+ { "ERRORTYPE", ocErrorType }, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
+ { "MULTIRANGE", ocMultiArea }, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
+ { "GOALSEEK", ocBackSolver }, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
+ { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
+ { "CURRENT", ocCurrent }, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
+ { "STYLE", ocStyle } // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
+ };
+ for (const FunctionName& rOoxmlAlias : aOoxmlAliases)
+ {
+ if (rName.equalsIgnoreAsciiCaseAscii( rOoxmlAlias.pName))
+ {
+ maRawToken.SetOpCode( rOoxmlAlias.eOp);
+ bFound = true;
+ break; // for
+ }
+ }
+ }
+ else if (mxSymbols->isPODF())
+ {
+ // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
+ // We can't rename them in
+ // formula/source/core/resource/core_resource.src but can add
+ // additional names to be recognized here so they match the UI names if
+ // those are renamed.
+ struct FunctionName
+ {
+ const char* pName;
+ OpCode eOp;
+ };
+ static const FunctionName aPodfAliases[] = {
+ { "EFFECT", ocEffect } // EFFECTIVE -> EFFECT
+ };
+ for (const FunctionName& rPodfAlias : aPodfAliases)
+ {
+ if (rName.equalsIgnoreAsciiCaseAscii( rPodfAlias.pName))
+ {
+ maRawToken.SetOpCode( rPodfAlias.eOp);
+ bFound = true;
+ break; // for
+ }
+ }
+ }
+
+ if (!bFound)
+ {
+ OUString aIntName;
+ if (mxSymbols->hasExternals())
+ {
+ // If symbols are set by filters get mapping to exact name.
+ ExternalHashMap::const_iterator iExt(
+ mxSymbols->getExternalHashMap().find( rName));
+ if (iExt != mxSymbols->getExternalHashMap().end())
+ {
+ if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
+ aIntName = (*iExt).second;
+ }
+ }
+ else
+ {
+ // Old (deprecated) addins first for legacy.
+ if (ScGlobal::GetLegacyFuncCollection()->findByName(OUString(cSymbol)))
+ {
+ aIntName = cSymbol;
+ }
+ else
+ // bLocalFirst=false for (English) upper full original name
+ // (service.function)
+ aIntName = ScGlobal::GetAddInCollection()->FindFunction(
+ rName, !mxSymbols->isEnglish());
+ }
+ if (!aIntName.isEmpty())
+ {
+ maRawToken.SetExternal( aIntName ); // international name
+ bFound = true;
+ }
+ }
+ if (!bFound)
+ return false;
+ OpCode eOp = maRawToken.GetOpCode();
+ if (eOp == ocSub || eOp == ocNegSub)
+ {
+ bool bShouldBeNegSub =
+ (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
+ (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
+ eLastOp == ocArrayOpen ||
+ eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
+ if (bShouldBeNegSub && eOp == ocSub)
+ maRawToken.NewOpCode( ocNegSub );
+ //TODO: if ocNegSub had ForceArray we'd have to set it here
+ else if (!bShouldBeNegSub && eOp == ocNegSub)
+ maRawToken.NewOpCode( ocSub );
+ }
+ return bFound;
+}
+
+bool ScCompiler::ParseOpCode2( std::u16string_view rName )
+{
+ for (sal_uInt16 i = ocInternalBegin; i <= ocInternalEnd; i++)
+ {
+ if (o3tl::equalsAscii(rName, pInternal[i - ocInternalBegin]))
+ {
+ maRawToken.SetOpCode(static_cast<OpCode>(i));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool lcl_ParenthesisFollows( const sal_Unicode* p )
+{
+ while (*p == ' ')
+ p++;
+ return *p == '(';
+}
+
+bool ScCompiler::ParseValue( const OUString& rSym )
+{
+ const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( GetGrammar());
+ if (nFormulaLanguage == css::sheet::FormulaLanguage::ODFF || nFormulaLanguage == css::sheet::FormulaLanguage::OOXML)
+ {
+ // Speedup things for ODFF, only well-formed numbers, not locale
+ // dependent nor user input.
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nParseEnd;
+ double fVal = rtl::math::stringToDouble( rSym, '.', 0, &eStatus, &nParseEnd);
+ if (nParseEnd != rSym.getLength())
+ {
+ // Not (only) a number.
+
+ if (nParseEnd > 0)
+ return false; // partially a number => no such thing
+
+ if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
+ return false; // some function name, not a constant
+
+ // Could be TRUE or FALSE constant.
+ OpCode eOpFunc = ocNone;
+ if (rSym.equalsIgnoreAsciiCase("TRUE"))
+ eOpFunc = ocTrue;
+ else if (rSym.equalsIgnoreAsciiCase("FALSE"))
+ eOpFunc = ocFalse;
+ if (eOpFunc != ocNone)
+ {
+ maRawToken.SetOpCode(eOpFunc);
+ // add missing trailing parentheses
+ maPendingOpCodes.push(ocOpen);
+ maPendingOpCodes.push(ocClose);
+ return true;
+ }
+ return false;
+ }
+ if (eStatus == rtl_math_ConversionStatus_OutOfRange)
+ {
+ // rtl::math::stringToDouble() recognizes XMLSchema-2 "INF" and
+ // "NaN" (case sensitive) that could be named expressions or DB
+ // areas as well.
+ // rSym is already upper so "NaN" is not possible here.
+ if (!std::isfinite(fVal) && rSym == "INF")
+ {
+ SCTAB nSheet = -1;
+ if (GetRangeData( nSheet, rSym))
+ return false;
+ if (rDoc.GetDBCollection()->getNamedDBs().findByUpperName(rSym))
+ return false;
+ }
+ /* TODO: is there a specific reason why we don't accept an infinity
+ * value that would raise an error in the interpreter, instead of
+ * setting the hard error at the token array already? */
+ SetError( FormulaError::IllegalArgument );
+ }
+ maRawToken.SetDouble( fVal );
+ return true;
+ }
+
+ double fVal;
+ sal_uInt32 nIndex = mxSymbols->isEnglishLocale() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
+
+ if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
+ return false;
+
+ SvNumFormatType nType = mpFormatter->GetType(nIndex);
+
+ // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
+ // Dates should never be entered directly and automatically converted
+ // to serial, because the serial would be wrong if null-date changed.
+ // Usually it wouldn't be accepted anyway because the date separator
+ // clashed with other separators or operators.
+ if (nType & (SvNumFormatType::TIME | SvNumFormatType::DATE))
+ return false;
+
+ if (nType == SvNumFormatType::LOGICAL)
+ {
+ if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
+ return false; // Boolean function instead.
+ }
+
+ if( nType == SvNumFormatType::TEXT )
+ // HACK: number too big!
+ SetError( FormulaError::IllegalArgument );
+ maRawToken.SetDouble( fVal );
+ return true;
+}
+
+bool ScCompiler::ParseString()
+{
+ if ( cSymbol[0] != '"' )
+ return false;
+ const sal_Unicode* p = cSymbol+1;
+ while ( *p )
+ p++;
+ sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 );
+ if (!nLen || cSymbol[nLen] != '"')
+ return false;
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern(OUString(cSymbol+1, nLen-1));
+ maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
+ return true;
+}
+
+bool ScCompiler::ParsePredetectedErrRefReference( const OUString& rName, const OUString* pErrRef )
+{
+ switch (mnPredetectedReference)
+ {
+ case 1:
+ return ParseSingleReference( rName, pErrRef);
+ case 2:
+ return ParseDoubleReference( rName, pErrRef);
+ default:
+ return false;
+ }
+}
+
+bool ScCompiler::ParsePredetectedReference( const OUString& rName )
+{
+ // Speedup documents with lots of broken references, e.g. sheet deleted.
+ // It could also be a broken invalidated reference that contains #REF!
+ // (but is not equal to), which we wrote prior to ODFF and also to ODFF
+ // between 2013 and 2016 until 5.1.4
+ constexpr OUString aErrRef(u"#REF!"_ustr); // not localized in ODFF
+ sal_Int32 nPos = rName.indexOf( aErrRef);
+ if (nPos != -1)
+ {
+ /* TODO: this may be enhanced by reusing scan information from
+ * NextSymbol(), the positions of quotes and special characters found
+ * there for $'sheet'.A1:... could be stored in a vector. We don't
+ * fully rescan here whether found positions are within single quotes
+ * for performance reasons. This code does not check for possible
+ * occurrences of insane "valid" sheet names like
+ * 'haha.#REF!1fooledyou' and will generate an error on such. */
+ if (nPos == 0)
+ {
+ // Per ODFF the correct string for a reference error is just #REF!,
+ // so pass it on.
+ if (rName.getLength() == 5)
+ return ParseErrorConstant( rName);
+ // #REF!.AB42 or #REF!42 or #REF!#REF!
+ return ParsePredetectedErrRefReference( rName, &aErrRef);
+ }
+ sal_Unicode c = rName[nPos-1]; // before #REF!
+ if ('$' == c)
+ {
+ if (nPos == 1)
+ {
+ // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
+ return ParsePredetectedErrRefReference( rName, &aErrRef);
+ }
+ c = rName[nPos-2]; // before $#REF!
+ }
+ sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF!
+ switch (c)
+ {
+ case '.':
+ if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
+ {
+ // sheet.#REF!42 or sheet.#REF!#REF!
+ return ParsePredetectedErrRefReference( rName, &aErrRef);
+ }
+ break;
+ case ':':
+ if (mnPredetectedReference > 1 &&
+ ('.' == c2 || '$' == c2 || '#' == c2 ||
+ ('0' <= c2 && c2 <= '9')))
+ {
+ // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
+ return ParsePredetectedErrRefReference( rName, &aErrRef);
+ }
+ break;
+ default:
+ if (rtl::isAsciiAlpha(c) &&
+ ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
+ {
+ // AB#REF!: or AB#REF!
+ return ParsePredetectedErrRefReference( rName, &aErrRef);
+ }
+ }
+ }
+ switch (mnPredetectedReference)
+ {
+ case 1:
+ return ParseSingleReference( rName);
+ case 2:
+ return ParseDoubleReference( rName);
+ }
+ return false;
+}
+
+bool ScCompiler::ParseDoubleReference( const OUString& rName, const OUString* pErrRef )
+{
+ ScRange aRange( aPos, aPos );
+ const ScAddress::Details aDetails( pConv->meConv, aPos );
+ ScAddress::ExternalInfo aExtInfo;
+ ScRefFlags nFlags = aRange.Parse( rName, rDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef );
+ if( nFlags & ScRefFlags::VALID )
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange( aRange );
+ aRef.Ref1.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
+ aRef.Ref1.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
+ aRef.Ref1.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
+ if ( !(nFlags & ScRefFlags::TAB_VALID) )
+ aRef.Ref1.SetTabDeleted( true ); // #REF!
+ aRef.Ref1.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
+ aRef.Ref2.SetColRel( (nFlags & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO );
+ aRef.Ref2.SetRowRel( (nFlags & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO );
+ aRef.Ref2.SetTabRel( (nFlags & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO );
+ if ( !(nFlags & ScRefFlags::TAB2_VALID) )
+ aRef.Ref2.SetTabDeleted( true ); // #REF!
+ aRef.Ref2.SetFlag3D( ( nFlags & ScRefFlags::TAB2_3D ) != ScRefFlags::ZERO );
+ aRef.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
+ if (aExtInfo.mbExternal)
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
+ maRawToken.SetExternalDoubleRef(
+ aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
+ maExternalFiles.push_back(aExtInfo.mnFileId);
+ }
+ else
+ {
+ maRawToken.SetDoubleReference(aRef);
+ }
+ }
+
+ return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
+}
+
+bool ScCompiler::ParseSingleReference( const OUString& rName, const OUString* pErrRef )
+{
+ mnCurrentSheetEndPos = 0;
+ mnCurrentSheetTab = -1;
+ ScAddress aAddr( aPos );
+ const ScAddress::Details aDetails( pConv->meConv, aPos );
+ ScAddress::ExternalInfo aExtInfo;
+ ScRefFlags nFlags = aAddr.Parse( rName, rDoc, aDetails,
+ &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef);
+ // Something must be valid in order to recognize Sheet1.blah or blah.a1
+ // as a (wrong) reference.
+ if( nFlags & ( ScRefFlags::COL_VALID|ScRefFlags::ROW_VALID|ScRefFlags::TAB_VALID ) )
+ {
+ // Valid given tab and invalid col or row may indicate a sheet-local
+ // named expression, bail out early and don't create a reference token.
+ if (!(nFlags & ScRefFlags::VALID) && mnCurrentSheetEndPos > 0 &&
+ (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
+ {
+ if (aExtInfo.mbExternal)
+ {
+ // External names are handled separately.
+ mnCurrentSheetEndPos = 0;
+ mnCurrentSheetTab = -1;
+ }
+ else
+ {
+ mnCurrentSheetTab = aAddr.Tab();
+ }
+ return false;
+ }
+
+ if( HasPossibleNamedRangeConflict( aAddr.Tab()))
+ {
+ // A named range named e.g. 'num1' is valid with 1k columns, but would become a reference
+ // when the document is opened later with 16k columns. Resolve the conflict by not
+ // considering it a reference.
+ OUString aUpper( ScGlobal::getCharClass().uppercase( rName ));
+ mnCurrentSheetTab = aAddr.Tab(); // temporarily set for ParseNamedRange()
+ if(ParseNamedRange( aUpper, true )) // only check
+ return false;
+ mnCurrentSheetTab = -1;
+ }
+
+ ScSingleRefData aRef;
+ aRef.InitAddress( aAddr );
+ aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
+ aRef.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
+ aRef.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
+ aRef.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
+ // the reference is really invalid
+ if( !( nFlags & ScRefFlags::VALID ) )
+ {
+ if( !( nFlags & ScRefFlags::COL_VALID ) )
+ aRef.SetColDeleted(true);
+ if( !( nFlags & ScRefFlags::ROW_VALID ) )
+ aRef.SetRowDeleted(true);
+ if( !( nFlags & ScRefFlags::TAB_VALID ) )
+ aRef.SetTabDeleted(true);
+ nFlags |= ScRefFlags::VALID;
+ }
+ aRef.SetAddress(rDoc.GetSheetLimits(), aAddr, aPos);
+
+ if (aExtInfo.mbExternal)
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
+ maRawToken.SetExternalSingleRef(
+ aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
+ maExternalFiles.push_back(aExtInfo.mnFileId);
+ }
+ else
+ maRawToken.SetSingleReference(aRef);
+ }
+
+ return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
+}
+
+bool ScCompiler::ParseReference( const OUString& rName, const OUString* pErrRef )
+{
+ // Has to be called before ParseValue
+
+ // A later ParseNamedRange() relies on these, being set in ParseSingleReference()
+ // if so, reset in all cases.
+ mnCurrentSheetEndPos = 0;
+ mnCurrentSheetTab = -1;
+
+ sal_Unicode ch1 = rName[0];
+ sal_Unicode cDecSep = ( mxSymbols->isEnglishLocale() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0] );
+ if ( ch1 == cDecSep )
+ return false;
+ // Code further down checks only if cDecSep=='.' so simply obtaining the
+ // alternative decimal separator if it's not is sufficient.
+ if (cDecSep != '.')
+ {
+ cDecSep = ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar();
+ if ( ch1 == cDecSep )
+ return false;
+ }
+ // Who was that imbecile introducing '.' as the sheet name separator!?!
+ if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' )
+ {
+ // Numerical sheet name is valid.
+ // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
+ // Don't create a #REF! of values. But also do not bail out on
+ // something like 3:3, meaning entire row 3.
+ do
+ {
+ const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
+ if ( nPos == -1 )
+ {
+ if (ScGlobal::FindUnquoted( rName, ':') != -1)
+ break; // may be 3:3, continue as usual
+ return false;
+ }
+ sal_Unicode const * const pTabSep = rName.getStr() + nPos;
+ sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
+ if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
+ return false;
+ if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
+ && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & ScCharFlags::ValueExp) )
+ {
+ // If it is an 1.E2 expression check if "1" is an existent sheet
+ // name. If so, a desired value 1.E2 would have to be entered as
+ // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
+ // require numerical sheet names always being entered quoted, which
+ // is not desirable (too many 1999, 2000, 2001 sheets in use).
+ // Furthermore, XML files created with versions prior to SRC640e
+ // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
+ // and would produce wrong formulas if the conditions here are met.
+ // If you can live with these restrictions you may remove the
+ // check and return an unconditional FALSE.
+ OUString aTabName( rName.copy( 0, nPos ) );
+ SCTAB nTab;
+ if ( !rDoc.GetTable( aTabName, nTab ) )
+ return false;
+ // If sheet "1" exists and the expression is 1.E+2 continue as
+ // usual, the ScRange/ScAddress parser will take care of it.
+ }
+ } while(false);
+ }
+
+ if (ParseSingleReference( rName, pErrRef))
+ return true;
+
+ // Though the range operator is handled explicitly, when encountering
+ // something like Sheet1.A:A we will have to treat it as one entity if it
+ // doesn't pass as single cell reference.
+ if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
+ {
+ if (ParseDoubleReference( rName, pErrRef))
+ return true;
+ // Now try with a symbol up to the range operator, rewind source
+ // position.
+ assert(mnRangeOpPosInSymbol < MAXSTRLEN); // We should have caught the maldoers.
+ if (mnRangeOpPosInSymbol >= MAXSTRLEN) // TODO: this check and return
+ return false; // can be removed when sure.
+ sal_Int32 nLen = mnRangeOpPosInSymbol;
+ while (cSymbol[++nLen])
+ ;
+ cSymbol[mnRangeOpPosInSymbol] = 0;
+ nSrcPos -= (nLen - mnRangeOpPosInSymbol);
+ mnRangeOpPosInSymbol = -1;
+ mbRewind = true;
+ return true; // end all checks
+ }
+ else
+ {
+ switch (pConv->meConv)
+ {
+ case FormulaGrammar::CONV_XL_A1:
+ case FormulaGrammar::CONV_XL_OOX:
+ // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
+ // sickness, mnRangeOpPosInSymbol did not catch the range
+ // operator as it is within a quoted name.
+ if (rName[0] != '\'')
+ return false; // Document name has to be single quoted.
+ [[fallthrough]];
+ case FormulaGrammar::CONV_XL_R1C1:
+ // C2 or C[1] are valid entire column references.
+ if (ParseDoubleReference( rName, pErrRef))
+ return true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ return false;
+}
+
+bool ScCompiler::ParseMacro( const OUString& rName )
+{
+#if !HAVE_FEATURE_SCRIPTING
+ (void) rName;
+
+ return false;
+#else
+
+ // Calling SfxObjectShell::GetBasic() may result in all sort of things
+ // including obtaining the model and deep down in
+ // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
+ // formulas are compiled from a threaded import may result in a deadlock.
+ // Check first if we actually could acquire it and if not bail out.
+ /* FIXME: yes, but how ... */
+ vcl::SolarMutexTryAndBuyGuard g;
+ if (!g.isAcquired())
+ {
+ SAL_WARN( "sc.core", "ScCompiler::ParseMacro - SolarMutex would deadlock, not obtaining Basic");
+ return false; // bad luck
+ }
+
+ OUString aName( rName);
+ StarBASIC* pObj = nullptr;
+ ScDocShell* pDocSh = rDoc.GetDocumentShell();
+
+ try
+ {
+ if( pDocSh )//XXX
+ pObj = pDocSh->GetBasic();
+ else
+ pObj = SfxApplication::GetBasic();
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ if (!pObj)
+ return false;
+
+ // ODFF recommends to store user-defined functions prefixed with "USER.",
+ // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
+ // function name so a function "USER.FOO" could not exist, and macro check
+ // is assigned the lowest priority in function name check.
+ if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
+ aName = aName.copy(5);
+
+ SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxClassType::Method ));
+ if( !pMeth )
+ {
+ return false;
+ }
+ // It really should be a BASIC function!
+ if( pMeth->GetType() == SbxVOID
+ || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
+ || dynamic_cast<const SbMethod*>( pMeth) == nullptr )
+ {
+ return false;
+ }
+ maRawToken.SetExternal( aName );
+ maRawToken.eOp = ocMacro;
+ return true;
+#endif
+}
+
+const ScRangeData* ScCompiler::GetRangeData( SCTAB& rSheet, const OUString& rUpperName ) const
+{
+ // try local names first
+ rSheet = aPos.Tab();
+ const ScRangeName* pRangeName = rDoc.GetRangeName(rSheet);
+ const ScRangeData* pData = nullptr;
+ if (pRangeName)
+ pData = pRangeName->findByUpperName(rUpperName);
+ if (!pData)
+ {
+ pRangeName = rDoc.GetRangeName();
+ if (pRangeName)
+ pData = pRangeName->findByUpperName(rUpperName);
+ if (pData)
+ rSheet = -1;
+ }
+ return pData;
+}
+
+bool ScCompiler::HasPossibleNamedRangeConflict( SCTAB nTab ) const
+{
+ const ScRangeName* pRangeName = rDoc.GetRangeName();
+ if (pRangeName && pRangeName->hasPossibleAddressConflict())
+ return true;
+ pRangeName = rDoc.GetRangeName(nTab);
+ if (pRangeName && pRangeName->hasPossibleAddressConflict())
+ return true;
+ return false;
+}
+
+bool ScCompiler::ParseNamedRange( const OUString& rUpperName, bool onlyCheck )
+{
+ // ParseNamedRange is called only from NextNewToken, with an upper-case string
+
+ SCTAB nSheet = -1;
+ const ScRangeData* pData = GetRangeData( nSheet, rUpperName);
+ if (pData)
+ {
+ if (!onlyCheck)
+ maRawToken.SetName( nSheet, pData->GetIndex());
+ return true;
+ }
+
+ // Sheet-local name with sheet specified.
+ if (mnCurrentSheetEndPos > 0 && mnCurrentSheetTab >= 0)
+ {
+ OUString aName( rUpperName.copy( mnCurrentSheetEndPos));
+ const ScRangeName* pRangeName = rDoc.GetRangeName( mnCurrentSheetTab);
+ if (pRangeName)
+ {
+ pData = pRangeName->findByUpperName(aName);
+ if (pData)
+ {
+ if (!onlyCheck)
+ maRawToken.SetName( mnCurrentSheetTab, pData->GetIndex());
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ScCompiler::ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
+{
+ /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
+ * correctly parses external named references in OOo, as required per RFE
+ * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
+ * spec first. Until then don't pretend to support external names that
+ * wouldn't survive a save and reload cycle, return false instead. */
+
+ rbInvalidExternalNameRange = false;
+
+ if (!pConv)
+ return false;
+
+ OUString aFile, aName;
+ if (!pConv->parseExternalName( rSymbol, aFile, aName, rDoc, &maExternalLinks))
+ return false;
+
+ if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
+ return false;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ OUString aTmp = aFile;
+ pRefMgr->convertToAbsName(aTmp);
+ aFile = aTmp;
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
+ if (!pRefMgr->isValidRangeName(nFileId, aName))
+ {
+ rbInvalidExternalNameRange = true;
+ // range name doesn't exist in the source document.
+ return false;
+ }
+
+ const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
+ maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : aTmp);
+ maExternalFiles.push_back(nFileId);
+ return true;
+}
+
+bool ScCompiler::ParseDBRange( const OUString& rName )
+{
+ ScDBCollection::NamedDBs& rDBs = rDoc.GetDBCollection()->getNamedDBs();
+ const ScDBData* p = rDBs.findByUpperName(rName);
+ if (!p)
+ return false;
+
+ maRawToken.SetName( -1, p->GetIndex()); // DB range is always global.
+ maRawToken.eOp = ocDBArea;
+ return true;
+}
+
+bool ScCompiler::ParseColRowName( const OUString& rName )
+{
+ bool bInList = false;
+ bool bFound = false;
+ ScSingleRefData aRef;
+ OUString aName( rName );
+ DeQuote( aName );
+ SCTAB nThisTab = aPos.Tab();
+ for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
+ { // first check ranges on this sheet, in case of duplicated names
+ for ( short jRow=0; jRow<2 && !bInList; jRow++ )
+ {
+ ScRangePairList* pRL;
+ if ( !jRow )
+ pRL = rDoc.GetColNameRanges();
+ else
+ pRL = rDoc.GetRowNameRanges();
+ for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
+ {
+ const ScRangePair & rR = (*pRL)[iPair];
+ const ScRange& rNameRange = rR.GetRange(0);
+ if ( jThisTab && (rNameRange.aStart.Tab() > nThisTab ||
+ nThisTab > rNameRange.aEnd.Tab()) )
+ continue; // for
+ ScCellIterator aIter( rDoc, rNameRange );
+ for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
+ {
+ // Don't crash if cell (via CompileNameFormula) encounters
+ // a formula cell without code and
+ // HasStringData/Interpret/Compile is executed and all that
+ // recursively...
+ // Furthermore, *this* cell won't be touched, since no RPN exists yet.
+ CellType eType = aIter.getType();
+ bool bOk = false;
+ if (eType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFC = aIter.getFormulaCell();
+ bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
+ }
+ else
+ bOk = true;
+
+ if (bOk && aIter.hasString())
+ {
+ OUString aStr = aIter.getString();
+ if ( ScGlobal::GetTransliteration().isEqual( aStr, aName ) )
+ {
+ aRef.InitFlags();
+ if ( !jRow )
+ aRef.SetColRel( true ); // ColName
+ else
+ aRef.SetRowRel( true ); // RowName
+ aRef.SetAddress(rDoc.GetSheetLimits(), aIter.GetPos(), aPos);
+ bInList = bFound = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( !bInList && rDoc.GetDocOptions().IsLookUpColRowNames() )
+ { // search in current sheet
+ tools::Long nDistance = 0, nMax = 0;
+ tools::Long nMyCol = static_cast<tools::Long>(aPos.Col());
+ tools::Long nMyRow = static_cast<tools::Long>(aPos.Row());
+ bool bTwo = false;
+ ScAddress aOne( 0, 0, aPos.Tab() );
+ ScAddress aTwo( rDoc.MaxCol(), rDoc.MaxRow(), aPos.Tab() );
+
+ ScAutoNameCache* pNameCache = rDoc.GetAutoNameCache();
+ if ( pNameCache )
+ {
+ // use GetNameOccurrences to collect all positions of aName on the sheet
+ // (only once), similar to the outer part of the loop in the "else" branch.
+
+ const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
+
+ // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
+ // The order of addresses in the vector is the same as from ScCellIterator.
+
+ for ( const ScAddress& aAddress : rAddresses )
+ {
+ if ( bFound )
+ { // stop if everything else is further away
+ if ( nMax < static_cast<tools::Long>(aAddress.Col()) )
+ break; // aIter
+ }
+ if ( aAddress != aPos )
+ {
+ // same treatment as in isEqual case below
+
+ SCCOL nCol = aAddress.Col();
+ SCROW nRow = aAddress.Row();
+ tools::Long nC = nMyCol - nCol;
+ tools::Long nR = nMyRow - nRow;
+ if ( bFound )
+ {
+ tools::Long nD = nC * nC + nR * nR;
+ if ( nD < nDistance )
+ {
+ if ( nC < 0 || nR < 0 )
+ { // right or below
+ bTwo = true;
+ aTwo.Set( nCol, nRow, aAddress.Tab() );
+ nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
+ nDistance = nD;
+ }
+ else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
+ {
+ // upper left, only if not further up than the
+ // current entry and nMyRow is below (CellIter
+ // runs column-wise)
+ bTwo = false;
+ aOne.Set( nCol, nRow, aAddress.Tab() );
+ nMax = std::max( nMyCol + nC, nMyRow + nR );
+ nDistance = nD;
+ }
+ }
+ }
+ else
+ {
+ aOne.Set( nCol, nRow, aAddress.Tab() );
+ nDistance = nC * nC + nR * nR;
+ nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
+
+ }
+ bFound = true;
+ }
+ }
+ }
+ else
+ {
+ ScCellIterator aIter( rDoc, ScRange( aOne, aTwo ) );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if ( bFound )
+ { // stop if everything else is further away
+ if ( nMax < static_cast<tools::Long>(aIter.GetPos().Col()) )
+ break; // aIter
+ }
+ CellType eType = aIter.getType();
+ bool bOk = false;
+ if (eType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFC = aIter.getFormulaCell();
+ bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
+ }
+ else
+ bOk = true;
+
+ if (bOk && aIter.hasString())
+ {
+ OUString aStr = aIter.getString();
+ if ( ScGlobal::GetTransliteration().isEqual( aStr, aName ) )
+ {
+ SCCOL nCol = aIter.GetPos().Col();
+ SCROW nRow = aIter.GetPos().Row();
+ tools::Long nC = nMyCol - nCol;
+ tools::Long nR = nMyRow - nRow;
+ if ( bFound )
+ {
+ tools::Long nD = nC * nC + nR * nR;
+ if ( nD < nDistance )
+ {
+ if ( nC < 0 || nR < 0 )
+ { // right or below
+ bTwo = true;
+ aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
+ nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
+ nDistance = nD;
+ }
+ else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
+ {
+ // upper left, only if not further up than the
+ // current entry and nMyRow is below (CellIter
+ // runs column-wise)
+ bTwo = false;
+ aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
+ nMax = std::max( nMyCol + nC, nMyRow + nR );
+ nDistance = nD;
+ }
+ }
+ }
+ else
+ {
+ aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
+ nDistance = nC * nC + nR * nR;
+ nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
+ }
+ bFound = true;
+ }
+ }
+ }
+ }
+
+ if ( bFound )
+ {
+ ScAddress aAdr;
+ if ( bTwo )
+ {
+ if ( nMyCol >= static_cast<tools::Long>(aOne.Col()) && nMyRow >= static_cast<tools::Long>(aOne.Row()) )
+ aAdr = aOne; // upper left takes precedence
+ else
+ {
+ if ( nMyCol < static_cast<tools::Long>(aOne.Col()) )
+ { // two to the right
+ if ( nMyRow >= static_cast<tools::Long>(aTwo.Row()) )
+ aAdr = aTwo; // directly right
+ else
+ aAdr = aOne;
+ }
+ else
+ { // two below or below and right, take the nearest
+ tools::Long nC1 = nMyCol - aOne.Col();
+ tools::Long nR1 = nMyRow - aOne.Row();
+ tools::Long nC2 = nMyCol - aTwo.Col();
+ tools::Long nR2 = nMyRow - aTwo.Row();
+ if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
+ aAdr = aOne;
+ else
+ aAdr = aTwo;
+ }
+ }
+ }
+ else
+ aAdr = aOne;
+ aRef.InitAddress( aAdr );
+ // Prioritize on column label; row label only if the next cell
+ // above/below the found label cell is text, or if both are not and
+ // the cell below is empty and the next cell to the right is
+ // numeric.
+ if ((aAdr.Row() < rDoc.MaxRow() && rDoc.HasStringData(
+ aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
+ || (aAdr.Row() > 0 && rDoc.HasStringData(
+ aAdr.Col(), aAdr.Row() - 1, aAdr.Tab()))
+ || (aAdr.Row() < rDoc.MaxRow() && rDoc.GetRefCellValue(
+ ScAddress( aAdr.Col(), aAdr.Row() + 1, aAdr.Tab())).isEmpty()
+ && aAdr.Col() < rDoc.MaxCol() && rDoc.GetRefCellValue(
+ ScAddress( aAdr.Col() + 1, aAdr.Row(), aAdr.Tab())).hasNumeric()))
+ aRef.SetRowRel( true ); // RowName
+ else
+ aRef.SetColRel( true ); // ColName
+ aRef.SetAddress(rDoc.GetSheetLimits(), aAdr, aPos);
+ }
+ }
+ if ( bFound )
+ {
+ maRawToken.SetSingleReference( aRef );
+ maRawToken.eOp = ocColRowName;
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScCompiler::ParseBoolean( const OUString& rName )
+{
+ OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName ) );
+ if( iLook != mxSymbols->getHashMap().end() &&
+ ((*iLook).second == ocTrue ||
+ (*iLook).second == ocFalse) )
+ {
+ maRawToken.SetOpCode( (*iLook).second );
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScCompiler::ParseErrorConstant( const OUString& rName )
+{
+ FormulaError nError = GetErrorConstant( rName);
+ if (nError != FormulaError::NONE)
+ {
+ maRawToken.SetErrorConstant( nError);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScCompiler::ParseTableRefItem( const OUString& rName )
+{
+ bool bItem = false;
+ OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
+ if (iLook != mxSymbols->getHashMap().end())
+ {
+ // Only called when there actually is a current TableRef, hence
+ // accessing maTableRefs.back() is safe.
+ ScTableRefToken* p = maTableRefs.back().mxToken.get();
+ assert(p); // not a ScTableRefToken can't be
+
+ switch ((*iLook).second)
+ {
+ case ocTableRefItemAll:
+ bItem = true;
+ p->AddItem( ScTableRefToken::ALL);
+ break;
+ case ocTableRefItemHeaders:
+ bItem = true;
+ p->AddItem( ScTableRefToken::HEADERS);
+ break;
+ case ocTableRefItemData:
+ bItem = true;
+ p->AddItem( ScTableRefToken::DATA);
+ break;
+ case ocTableRefItemTotals:
+ bItem = true;
+ p->AddItem( ScTableRefToken::TOTALS);
+ break;
+ case ocTableRefItemThisRow:
+ bItem = true;
+ p->AddItem( ScTableRefToken::THIS_ROW);
+ break;
+ default:
+ ;
+ }
+ if (bItem)
+ maRawToken.SetOpCode( (*iLook).second );
+ }
+ return bItem;
+}
+
+namespace {
+OUString unescapeTableRefColumnSpecifier( const OUString& rStr )
+{
+ // '#', '[', ']' and '\'' are escaped with '\''
+
+ if (rStr.indexOf( '\'' ) < 0)
+ return rStr;
+
+ const sal_Int32 n = rStr.getLength();
+ OUStringBuffer aBuf( n );
+ const sal_Unicode* p = rStr.getStr();
+ const sal_Unicode* const pStop = p + n;
+ bool bEscaped = false;
+ for ( ; p < pStop; ++p)
+ {
+ const sal_Unicode c = *p;
+ if (bEscaped)
+ {
+ aBuf.append( c );
+ bEscaped = false;
+ }
+ else if (c == '\'')
+ bEscaped = true; // unescaped escaping '\''
+ else
+ aBuf.append( c );
+ }
+ return aBuf.makeStringAndClear();
+}
+}
+
+bool ScCompiler::ParseTableRefColumn( const OUString& rName )
+{
+ // Only called when there actually is a current TableRef, hence
+ // accessing maTableRefs.back() is safe.
+ ScTableRefToken* p = maTableRefs.back().mxToken.get();
+ assert(p); // not a ScTableRefToken can't be
+
+ ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( p->GetIndex());
+ if (!pDBData)
+ return false;
+
+ OUString aName( unescapeTableRefColumnSpecifier( rName));
+
+ ScRange aRange;
+ pDBData->GetArea( aRange);
+ aRange.aEnd.SetTab( aRange.aStart.Tab());
+ aRange.aEnd.SetRow( aRange.aStart.Row());
+
+ // Prefer the stored internal table column name, which is also needed for
+ // named expressions during document load time when cell content isn't
+ // available yet. Also, avoiding a possible calculation step in case the
+ // header cell is a formula cell is "a good thing".
+ sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName);
+ if (nOffset >= 0)
+ {
+ // This is sneaky... we always use the top row of the database range,
+ // regardless of whether it is a header row or not. Code evaluating
+ // this reference must take that into account and may have to act
+ // differently if it is a header-less table. Which are two places,
+ // HandleTableRef() (no change necessary there) and
+ // CreateStringFromSingleRef() (must not fallback to cell lookup).
+ ScSingleRefData aRef;
+ ScAddress aAdr( aRange.aStart);
+ aAdr.IncCol( nOffset);
+ aRef.InitAddress( aAdr);
+ maRawToken.SetSingleReference( aRef );
+ return true;
+ }
+
+ if (pDBData->HasHeader())
+ {
+ // Quite similar to IsColRowName() but limited to one row of headers.
+ ScCellIterator aIter( rDoc, aRange);
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ CellType eType = aIter.getType();
+ bool bOk = false;
+ if (eType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFC = aIter.getFormulaCell();
+ bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
+ }
+ else
+ bOk = true;
+
+ if (bOk && aIter.hasString())
+ {
+ OUString aStr = aIter.getString();
+ if (ScGlobal::GetTransliteration().isEqual( aStr, aName))
+ {
+ // If this is successful and the internal column name
+ // lookup was not, it may be worth a warning.
+ SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
+
+ /* XXX NOTE: we could init the column as relative so copying a
+ * formula across columns would point to the relative column,
+ * but do it absolute because:
+ * a) it makes the reference work in named expressions without
+ * having to distinguish
+ * b) Excel does it the same. */
+ ScSingleRefData aRef;
+ aRef.InitAddress( aIter.GetPos());
+ maRawToken.SetSingleReference( aRef );
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ScCompiler::SetAutoCorrection( bool bVal )
+{
+ assert(mbJumpCommandReorder);
+ bAutoCorrect = bVal;
+ mbStopOnError = !bVal;
+}
+
+void ScCompiler::AutoCorrectParsedSymbol()
+{
+ sal_Int32 nPos = aCorrectedSymbol.getLength();
+ if ( !nPos )
+ return;
+
+ nPos--;
+ const sal_Unicode cQuote = '\"';
+ const sal_Unicode cx = 'x';
+ const sal_Unicode cX = 'X';
+ sal_Unicode c1 = aCorrectedSymbol[0];
+ sal_Unicode c2 = aCorrectedSymbol[nPos];
+ sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
+ if ( c1 == cQuote && c2 != cQuote )
+ { // "...
+ // What's not a word doesn't belong to it.
+ // Don't be pedantic: c < 128 should be sufficient here.
+ while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
+ ((GetCharTableFlags(aCorrectedSymbol[nPos], aCorrectedSymbol[nPos-1]) &
+ (ScCharFlags::Word | ScCharFlags::CharDontCare)) == ScCharFlags::NONE)) )
+ nPos--;
+ if ( nPos == MAXSTRLEN - 1 )
+ aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, rtl::OUStringChar(cQuote) ); // '"' the MAXSTRLENth character
+ else
+ aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, rtl::OUStringChar(cQuote) );
+ bCorrected = true;
+ }
+ else if ( c1 != cQuote && c2 == cQuote )
+ { // ..."
+ aCorrectedSymbol = OUStringChar(cQuote) + aCorrectedSymbol;
+ bCorrected = true;
+ }
+ else if ( nPos == 0 && (c1 == cx || c1 == cX) )
+ { // x => *
+ aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
+ bCorrected = true;
+ }
+ else if ( (GetCharTableFlags( c1, 0 ) & ScCharFlags::CharValue)
+ && (GetCharTableFlags( c2, c2p ) & ScCharFlags::CharValue) )
+ {
+ if ( aCorrectedSymbol.indexOf(cx) >= 0 ) // At least two tokens separated by cx
+ { // x => *
+ sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
+ aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cx), OUStringChar(c));
+ bCorrected = true;
+ }
+ if ( aCorrectedSymbol.indexOf(cX) >= 0 ) // At least two tokens separated by cX
+ { // X => *
+ sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
+ aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cX), OUStringChar(c));
+ bCorrected = true;
+ }
+ }
+ else
+ {
+ OUString aSymbol( aCorrectedSymbol );
+ OUString aDoc;
+ if ( aSymbol[0] == '\'' )
+ {
+ sal_Int32 nPosition = aSymbol.indexOf( "'#" );
+ if (nPosition != -1)
+ { // Split off 'Doc'#, may be d:\... or whatever
+ aDoc = aSymbol.copy(0, nPosition + 2);
+ aSymbol = aSymbol.copy(nPosition + 2);
+ }
+ }
+ sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
+ bool bColons;
+ if ( nRefs > 2 )
+ { // duplicated or too many ':'? B:2::C10 => B2:C10
+ bColons = true;
+ sal_Int32 nIndex = 0;
+ OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
+ sal_Int32 nLen1 = aTmp1.getLength();
+ OUStringBuffer aSym;
+ OUString aTmp2;
+ bool bLastAlp = true;
+ sal_Int32 nStrip = 0;
+ sal_Int32 nCount = nRefs;
+ for ( sal_Int32 j=1; j<nCount; j++ )
+ {
+ aTmp2 = aSymbol.getToken( 0, ':', nIndex );
+ sal_Int32 nLen2 = aTmp2.getLength();
+ if ( nLen1 || nLen2 )
+ {
+ if ( nLen1 )
+ {
+ aSym.append(aTmp1);
+ bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
+ }
+ if ( nLen2 )
+ {
+ bool bNextNum = CharClass::isAsciiNumeric( aTmp2 );
+ if ( bLastAlp == bNextNum && nStrip < 1 )
+ {
+ // Must be alternating number/string, only
+ // strip within a reference.
+ nRefs--;
+ nStrip++;
+ }
+ else
+ {
+ if ( !aSym.isEmpty() && aSym[aSym.getLength()-1] != ':')
+ aSym.append(":");
+ nStrip = 0;
+ }
+ bLastAlp = !bNextNum;
+ }
+ else
+ { // ::
+ nRefs--;
+ if ( nLen1 )
+ { // B10::C10 ? append ':' on next round
+ if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
+ nStrip++;
+ }
+ }
+ aTmp1 = aTmp2;
+ nLen1 = nLen2;
+ }
+ else
+ nRefs--;
+ }
+ aSymbol = aSym + aTmp1;
+ aSym.setLength(0);
+ }
+ else
+ bColons = false;
+ if ( nRefs && nRefs <= 2 )
+ { // reference twisted? 4A => A4 etc.
+ OUString aTab[2], aRef[2];
+ const ScAddress::Details aDetails( pConv->meConv, aPos );
+ if ( nRefs == 2 )
+ {
+ sal_Int32 nIdx{ 0 };
+ aRef[0] = aSymbol.getToken( 0, ':', nIdx );
+ aRef[1] = aSymbol.getToken( 0, ':', nIdx );
+ }
+ else
+ aRef[0] = aSymbol;
+
+ bool bChanged = false;
+ bool bOk = true;
+ ScRefFlags nMask = ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID;
+ for ( int j=0; j<nRefs; j++ )
+ {
+ sal_Int32 nTmp = 0;
+ sal_Int32 nDotPos = -1;
+ while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
+ nDotPos = nTmp++; // the last one counts
+ if ( nDotPos != -1 )
+ {
+ aTab[j] = aRef[j].copy( 0, nDotPos + 1 ); // with '.'
+ aRef[j] = aRef[j].copy( nDotPos + 1 );
+ }
+ OUString aOld( aRef[j] );
+ OUStringBuffer aStr2;
+ const sal_Unicode* p = aRef[j].getStr();
+ while ( *p && rtl::isAsciiDigit( *p ) )
+ aStr2.append(*p++);
+ aRef[j] = OUString( p );
+ aRef[j] += aStr2;
+ if ( bColons || aRef[j] != aOld )
+ {
+ bChanged = true;
+ ScAddress aAdr;
+ bOk &= ((aAdr.Parse( aRef[j], rDoc, aDetails ) & nMask) == nMask);
+ }
+ }
+ if ( bChanged && bOk )
+ {
+ aCorrectedSymbol = aDoc;
+ aCorrectedSymbol += aTab[0];
+ aCorrectedSymbol += aRef[0];
+ if ( nRefs == 2 )
+ {
+ aCorrectedSymbol += ":";
+ aCorrectedSymbol += aTab[1];
+ aCorrectedSymbol += aRef[1];
+ }
+ bCorrected = true;
+ }
+ }
+ }
+}
+
+bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString& rUpper, const OUString& rOrg ) const
+{
+ if (FormulaGrammar::isODFF( meGrammar) || FormulaGrammar::isOOXML( meGrammar))
+ {
+ // ODFF and OOXML have defined sets of English function names, avoid
+ // i18n overhead.
+ rUpper = rOrg.toAsciiUpperCase();
+ return true;
+ }
+ else
+ {
+ // One of localized or English.
+ rUpper = pCharClass->uppercase(rOrg);
+ return false;
+ }
+}
+
+bool ScCompiler::NextNewToken( bool bInArray )
+{
+ if (!maPendingOpCodes.empty())
+ {
+ maRawToken.SetOpCode(maPendingOpCodes.front());
+ maPendingOpCodes.pop();
+ return true;
+ }
+
+ bool bAllowBooleans = bInArray;
+ const std::vector<Whitespace> & vSpaces = NextSymbol(bInArray);
+
+ if (!cSymbol[0])
+ {
+ if (nSrcPos < aFormula.getLength())
+ {
+ // Nothing could be parsed, remainder as bad string.
+ // NextSymbol() must had set an error for this.
+ assert( pArr->GetCodeError() != FormulaError::NONE);
+ const OUString aBad( aFormula.copy( nSrcPos));
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern( aBad);
+ maRawToken.SetString( aSS.getData(), aSS.getDataIgnoreCase());
+ maRawToken.NewOpCode( ocBad);
+ nSrcPos = aFormula.getLength();
+ // Add bad string as last token.
+ return true;
+ }
+ return false;
+ }
+
+ if (!vSpaces.empty())
+ {
+ ScRawToken aToken;
+ for (const auto& rSpace : vSpaces)
+ {
+ if (rSpace.cChar == 0x20)
+ {
+ // For now keep this a FormulaByteToken for the nasty
+ // significant whitespace intersection. This probably can be
+ // changed to a FormulaSpaceToken but then other places may
+ // need to be adapted.
+ aToken.SetOpCode( ocSpaces );
+ aToken.sbyte.cByte = static_cast<sal_uInt8>( std::min<sal_Int32>(rSpace.nCount, 255) );
+ }
+ else
+ {
+ aToken.SetOpCode( ocWhitespace );
+ aToken.whitespace.nCount = static_cast<sal_uInt8>( std::min<sal_Int32>(rSpace.nCount, 255) );
+ aToken.whitespace.cChar = rSpace.cChar;
+ }
+ if (!static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ))
+ {
+ SetError(FormulaError::CodeOverflow);
+ return false;
+ }
+ }
+ }
+
+ // Short cut for references when reading ODF to speedup things.
+ if (mnPredetectedReference)
+ {
+ OUString aStr( cSymbol);
+ bool bInvalidExternalNameRange;
+ if (!ParsePredetectedReference( aStr) && !ParseExternalNamedRange( aStr, bInvalidExternalNameRange ))
+ {
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern(aStr);
+ maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
+ maRawToken.NewOpCode( ocBad );
+ }
+ return true;
+ }
+
+ if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
+ !bAutoCorrect )
+ { // special case to speed up broken [$]#REF documents
+ /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
+ * be processed as usual. That would need some special treatment,
+ * also in NextSymbol() because of possible combinations of
+ * #REF!.#REF!#REF! parts. In case of reading ODF that is all
+ * handled by IsPredetectedReference(), this case here remains for
+ * manual/API input. */
+ OUString aBad( aFormula.copy( nSrcPos-1 ) );
+ const FormulaToken* pBadToken = pArr->AddBad(aBad);
+ eLastOp = pBadToken ? pBadToken->GetOpCode() : ocNone;
+ return false;
+ }
+
+ if( ParseString() )
+ return true;
+
+ bool bMayBeFuncName;
+ bool bAsciiNonAlnum; // operators, separators, ...
+ if ( cSymbol[0] < 128 )
+ {
+ bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
+ if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') && !utl::ConfigManager::IsFuzzing())
+ {
+ bMayBeFuncName = officecfg::Office::Common::Misc::ExperimentalMode::get();
+ }
+
+ bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
+ }
+ else
+ {
+ OUString aTmpStr( cSymbol[0] );
+ bMayBeFuncName = pCharClass->isLetter( aTmpStr, 0 );
+ bAsciiNonAlnum = false;
+ }
+
+ // Within a TableRef anything except an unescaped '[' or ']' is an item
+ // or a column specifier, do not attempt to recognize any other single
+ // operator there so even [,] or [+] for a single character column
+ // specifier works. Note that space between two ocTableRefOpen is not
+ // supported (Table[ [ColumnSpec]]), not only here. Note also that Table[]
+ // without any item or column specifier is valid.
+ if (bAsciiNonAlnum && cSymbol[1] == 0 && (eLastOp != ocTableRefOpen || cSymbol[0] == '[' || cSymbol[0] == ']'))
+ {
+ // Shortcut for operators and separators that need no further checks or upper.
+ if (ParseOpCode( OUString( cSymbol), bInArray ))
+ return true;
+ }
+
+ if ( bMayBeFuncName )
+ {
+ // a function name must be followed by a parenthesis
+ const sal_Unicode* p = aFormula.getStr() + nSrcPos;
+ while( *p == ' ' )
+ p++;
+ bMayBeFuncName = ( *p == '(' );
+ }
+
+ // Italian ARCTAN.2 resulted in #REF! => ParseOpcode() before
+ // ParseReference().
+
+ OUString aUpper;
+ bool bAsciiUpper = false;
+
+Label_Rewind:
+
+ do
+ {
+ const OUString aOrg( cSymbol );
+
+ // Check for TableRef column specifier first, it may be anything.
+ if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel)
+ {
+ if (ParseTableRefColumn( aOrg ))
+ return true;
+ // Do not attempt to resolve as any other name.
+ aUpper = aOrg; // for ocBad
+ break; // do; create ocBad token or set error.
+ }
+
+ mbRewind = false;
+ aUpper.clear();
+ bAsciiUpper = false;
+
+ if (bAsciiNonAlnum)
+ {
+ bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
+ if (cSymbol[0] == '#')
+ {
+ // Check for TableRef item specifiers first.
+ if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
+ {
+ if (ParseTableRefItem( aUpper ))
+ return true;
+ }
+
+ // This can be either an error constant ...
+ if (ParseErrorConstant( aUpper))
+ return true;
+
+ // ... or some invalidated reference starting with #REF!
+ // which is handled after the do loop.
+
+ break; // do; create ocBad token or set error.
+ }
+ if (ParseOpCode( aUpper, bInArray ))
+ return true;
+ }
+
+ if (bMayBeFuncName)
+ {
+ if (aUpper.isEmpty())
+ bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
+ if (ParseOpCode( aUpper, bInArray ))
+ return true;
+ }
+
+ // Column 'DM' ("Deutsche Mark", German currency) couldn't be
+ // referred => ParseReference() before ParseValue().
+ // Preserve case of file names in external references.
+ if (ParseReference( aOrg ))
+ {
+ if (mbRewind) // Range operator, but no direct reference.
+ continue; // do; up to range operator.
+ // If a syntactically correct reference was recognized but invalid
+ // e.g. because of non-existing sheet name => entire reference
+ // ocBad to preserve input instead of #REF!.A1
+ if (!maRawToken.IsValidReference(rDoc))
+ {
+ aUpper = aOrg; // ensure for ocBad
+ break; // do; create ocBad token or set error.
+ }
+ return true;
+ }
+
+ if (aUpper.isEmpty())
+ bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
+
+ // ParseBoolean() before ParseValue() to catch inline bools without the kludge
+ // for inline arrays.
+ if (bAllowBooleans && ParseBoolean( aUpper ))
+ return true;
+
+ if (ParseValue( aUpper ))
+ return true;
+
+ // User defined names and such do need i18n upper also in ODF.
+ if (bAsciiUpper || mbCharClassesDiffer)
+ {
+ // Use current system locale here because user defined symbols are
+ // more likely in that localized language than in the formula
+ // language. This in corner cases needs to continue to work for
+ // existing documents and environments.
+ // Do not change bAsciiUpper from here on for the lowercase() call
+ // below in the ocBad case to use the correct CharClass.
+ aUpper = ScGlobal::getCharClass().uppercase( aOrg );
+ }
+
+ if (ParseNamedRange( aUpper ))
+ return true;
+
+ // Compiling a named expression during collecting them in import shall
+ // not match arbitrary names that otherwise if all named expressions
+ // were present would be recognized as named expression. Such name will
+ // flag an error below and will be recompiled in a second step later
+ // with ScRangeData::CompileUnresolvedXML()
+ if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_NO_BREAK && rDoc.IsImportingXML())
+ break; // while
+
+ // Preserve case of file names in external references.
+ bool bInvalidExternalNameRange;
+ if (ParseExternalNamedRange( aOrg, bInvalidExternalNameRange ))
+ return true;
+ // Preserve case of file names in external references even when range
+ // is not valid and previous check failed tdf#89330
+ if (bInvalidExternalNameRange)
+ {
+ // add ocBad but do not lowercase
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern(aOrg);
+ maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
+ maRawToken.NewOpCode( ocBad );
+ return true;
+ }
+ if (ParseDBRange( aUpper ))
+ return true;
+ // If followed by '(' (with or without space inbetween) it can not be a
+ // column/row label. Prevent arbitrary content detection.
+ if (!bMayBeFuncName && ParseColRowName( aUpper ))
+ return true;
+ if (bMayBeFuncName && ParseMacro( aUpper ))
+ return true;
+ if (bMayBeFuncName && ParseOpCode2( aUpper ))
+ return true;
+
+ } while (mbRewind);
+
+ // Last chance: it could be a broken invalidated reference that contains
+ // #REF! (but is not equal to), which we also wrote to ODFF between 2013
+ // and 2016 until 5.1.4
+ OUString aErrRef( mxSymbols->getSymbol( ocErrRef));
+ if (aUpper.indexOf( aErrRef) >= 0 && ParseReference( aUpper, &aErrRef))
+ {
+ if (mbRewind)
+ goto Label_Rewind;
+ return true;
+ }
+
+ if ( meExtendedErrorDetection != EXTENDED_ERROR_DETECTION_NONE )
+ {
+ // set an error
+ SetError( FormulaError::NoName );
+ if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_BREAK)
+ return false; // end compilation
+ }
+
+ // Provide single token information and continue. Do not set an error, that
+ // would prematurely end compilation. Simple unknown names are handled by
+ // the interpreter.
+ // Use the same CharClass that was used for uppercase.
+ aUpper = ((bAsciiUpper || mbCharClassesDiffer) ? ScGlobal::getCharClass() : *pCharClass).lowercase( aUpper );
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern(aUpper);
+ maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
+ maRawToken.NewOpCode( ocBad );
+ if ( bAutoCorrect )
+ AutoCorrectParsedSymbol();
+ return true;
+}
+
+void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
+{
+ bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
+ sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
+ OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
+ if( pArr->GetLen() == nExpectedCount )
+ {
+ FormulaToken** ppTokens = pArr->GetArray();
+ // string tokens expected, GetString() will assert if token type is wrong
+ rFormula = ppTokens[0]->GetString().getString();
+ if( bExternal )
+ rFormulaNmsp = ppTokens[1]->GetString().getString();
+ }
+}
+
+namespace {
+
+class ExternalFileInserter
+{
+ ScAddress maPos;
+ ScExternalRefManager& mrRefMgr;
+public:
+ ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
+ maPos(rPos), mrRefMgr(rRefMgr) {}
+
+ void operator() (sal_uInt16 nFileId) const
+ {
+ mrRefMgr.insertRefCell(nFileId, maPos);
+ }
+};
+
+}
+
+std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula )
+{
+ OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
+ if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
+ SetGrammar( FormulaGrammar::GRAM_PODF );
+
+ ScTokenArray aArr(rDoc);
+ pArr = &aArr;
+ maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
+ aFormula = comphelper::string::strip(rFormula, ' ');
+
+ nSrcPos = 0;
+ bCorrected = false;
+ if ( bAutoCorrect )
+ {
+ aCorrectedFormula.clear();
+ aCorrectedSymbol.clear();
+ }
+ sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
+ if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
+ {
+ nSrcPos++;
+ nForced++;
+ if ( bAutoCorrect )
+ aCorrectedFormula += "=";
+ }
+ if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
+ {
+ nSrcPos++;
+ nForced++;
+ if ( bAutoCorrect )
+ aCorrectedFormula += "=";
+ }
+ struct FunctionStack
+ {
+ OpCode eOp;
+ short nSep;
+ };
+ // FunctionStack only used if PODF or OOXML!
+ bool bPODF = FormulaGrammar::isPODF( meGrammar);
+ bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
+ bool bUseFunctionStack = (bPODF || bOOXML);
+ const size_t nAlloc = 512;
+ FunctionStack aFuncs[ nAlloc ];
+ FunctionStack* pFunctionStack = (bUseFunctionStack && o3tl::make_unsigned(rFormula.getLength()) > nAlloc ?
+ new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
+ pFunctionStack[0].eOp = ocNone;
+ pFunctionStack[0].nSep = 0;
+ size_t nFunction = 0;
+ short nBrackets = 0;
+ bool bInArray = false;
+ eLastOp = ocOpen;
+ while( NextNewToken( bInArray ) )
+ {
+ const OpCode eOp = maRawToken.GetOpCode();
+ if (eOp == ocSkip)
+ continue;
+
+ switch (eOp)
+ {
+ case ocOpen:
+ {
+ ++nBrackets;
+ if (bUseFunctionStack)
+ {
+ ++nFunction;
+ pFunctionStack[ nFunction ].eOp = eLastOp;
+ pFunctionStack[ nFunction ].nSep = 0;
+ }
+ }
+ break;
+ case ocClose:
+ {
+ if( !nBrackets )
+ {
+ SetError( FormulaError::PairExpected );
+ if ( bAutoCorrect )
+ {
+ bCorrected = true;
+ aCorrectedSymbol.clear();
+ }
+ }
+ else
+ nBrackets--;
+ if (bUseFunctionStack && nFunction)
+ --nFunction;
+ }
+ break;
+ case ocSep:
+ {
+ if (bUseFunctionStack)
+ ++pFunctionStack[ nFunction ].nSep;
+ }
+ break;
+ case ocArrayOpen:
+ {
+ if( bInArray )
+ SetError( FormulaError::NestedArray );
+ else
+ bInArray = true;
+ // Don't count following column separator as parameter separator.
+ if (bUseFunctionStack)
+ {
+ ++nFunction;
+ pFunctionStack[ nFunction ].eOp = eOp;
+ pFunctionStack[ nFunction ].nSep = 0;
+ }
+ }
+ break;
+ case ocArrayClose:
+ {
+ if( bInArray )
+ {
+ bInArray = false;
+ }
+ else
+ {
+ SetError( FormulaError::PairExpected );
+ if ( bAutoCorrect )
+ {
+ bCorrected = true;
+ aCorrectedSymbol.clear();
+ }
+ }
+ if (bUseFunctionStack && nFunction)
+ --nFunction;
+ }
+ break;
+ case ocTableRefOpen:
+ {
+ // Don't count following item separator as parameter separator.
+ if (bUseFunctionStack)
+ {
+ ++nFunction;
+ pFunctionStack[ nFunction ].eOp = eOp;
+ pFunctionStack[ nFunction ].nSep = 0;
+ }
+ }
+ break;
+ case ocTableRefClose:
+ {
+ if (bUseFunctionStack && nFunction)
+ --nFunction;
+ }
+ break;
+ case ocColRowName:
+ case ocColRowNameAuto:
+ // The current implementation of column / row labels doesn't
+ // function correctly in grouped cells.
+ aArr.SetShareable(false);
+ break;
+ default:
+ break;
+ }
+ if ((eLastOp != ocOpen || eOp != ocClose) &&
+ (eLastOp == ocOpen ||
+ eLastOp == ocSep ||
+ eLastOp == ocArrayRowSep ||
+ eLastOp == ocArrayColSep ||
+ eLastOp == ocArrayOpen) &&
+ (eOp == ocSep ||
+ eOp == ocClose ||
+ eOp == ocArrayRowSep ||
+ eOp == ocArrayColSep ||
+ eOp == ocArrayClose))
+ {
+ // TODO: should we check for known functions with optional empty
+ // args so the correction dialog can do better?
+ if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
+ {
+ SetError(FormulaError::CodeOverflow); break;
+ }
+ }
+ if (bOOXML)
+ {
+ // Append a parameter for WEEKNUM, all 1.0
+ // Function is already closed, parameter count is nSep+1
+ size_t nFunc = nFunction + 1;
+ if (eOp == ocClose &&
+ (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start
+ pFunctionStack[ nFunc ].nSep == 0))
+ {
+ if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
+ !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
+ {
+ SetError(FormulaError::CodeOverflow); break;
+ }
+ }
+ }
+ else if (bPODF)
+ {
+ /* TODO: for now this is the only PODF adapter. If there were more,
+ * factor this out. */
+ // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
+ if (eOp == ocSep &&
+ pFunctionStack[ nFunction ].eOp == ocAddress &&
+ pFunctionStack[ nFunction ].nSep == 3)
+ {
+ if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
+ !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
+ {
+ SetError(FormulaError::CodeOverflow); break;
+ }
+ ++pFunctionStack[ nFunction ].nSep;
+ }
+ }
+ FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken(rDoc.GetSheetLimits()));
+ if (!pNewToken && eOp == ocArrayClose && pArr->OpCodeBefore( pArr->GetLen()) == ocArrayClose)
+ {
+ // Nested inline array or non-value/non-string in array. The
+ // original tokens are still in the ScTokenArray and not merged
+ // into an ScMatrixToken. Set error but keep on tokenizing.
+ SetError( FormulaError::BadArrayContent);
+ }
+ else if (!pNewToken)
+ {
+ SetError(FormulaError::CodeOverflow);
+ break;
+ }
+ else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef)
+ {
+ static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
+ }
+ else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen)
+ {
+ sal_uInt16 nIdx = pArr->GetLen() - 1;
+ const FormulaToken* pPrev = pArr->PeekPrev( nIdx);
+ if (pPrev && pPrev->GetOpCode() == ocDBArea)
+ {
+ ScTableRefToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE);
+ maTableRefs.emplace_back( pTableRefToken);
+ // pPrev may be dead hereafter.
+ static_cast<ScTokenArray*>(pArr)->ReplaceToken( nIdx, pTableRefToken,
+ FormulaTokenArray::ReplaceMode::CODE_ONLY);
+ }
+ }
+ switch (eOp)
+ {
+ case ocTableRefOpen:
+ SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
+ if (maTableRefs.empty())
+ SetError(FormulaError::Pair);
+ else
+ ++maTableRefs.back().mnLevel;
+ break;
+ case ocTableRefClose:
+ SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
+ if (maTableRefs.empty())
+ SetError(FormulaError::Pair);
+ else
+ {
+ if (--maTableRefs.back().mnLevel == 0)
+ maTableRefs.pop_back();
+ }
+ break;
+ default:
+ break;
+ }
+ eLastOp = maRawToken.GetOpCode();
+ if ( bAutoCorrect )
+ aCorrectedFormula += aCorrectedSymbol;
+ }
+ if ( mbCloseBrackets )
+ {
+ if( bInArray )
+ {
+ FormulaByteToken aToken( ocArrayClose );
+ if( !pArr->AddToken( aToken ) )
+ {
+ SetError(FormulaError::CodeOverflow);
+ }
+ else if ( bAutoCorrect )
+ aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
+ }
+
+ if (nBrackets)
+ {
+ FormulaToken aToken( svSep, ocClose );
+ while( nBrackets-- )
+ {
+ if( !pArr->AddToken( aToken ) )
+ {
+ SetError(FormulaError::CodeOverflow);
+ break; // while
+ }
+ if ( bAutoCorrect )
+ aCorrectedFormula += mxSymbols->getSymbol(ocClose);
+ }
+ }
+ }
+ if ( nForced >= 2 )
+ pArr->SetRecalcModeForced();
+
+ if (pFunctionStack != &aFuncs[0])
+ delete [] pFunctionStack;
+
+ // remember pArr, in case a subsequent CompileTokenArray() is executed.
+ std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aArr ));
+ pNew->GenHash();
+ // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
+ pArr = pNew.get();
+ maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
+
+ if (!maExternalFiles.empty())
+ {
+ // Remove duplicates, and register all external files found in this cell.
+ std::sort(maExternalFiles.begin(), maExternalFiles.end());
+ std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
+ std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *rDoc.GetExternalRefManager()));
+ maExternalFiles.erase(itEnd, maExternalFiles.end());
+ }
+
+ return pNew;
+}
+
+std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
+{
+ OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
+ "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
+ if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
+ {
+ ScFormulaParserPool& rParserPool = rDoc.GetFormulaParserPool();
+ uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
+ table::CellAddress aReferencePos;
+ ScUnoConversion::FillApiAddress( aReferencePos, aPos );
+ uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
+ ScTokenArray aTokenArray(rDoc);
+ if( ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, aTokenSeq ) )
+ {
+ // remember pArr, in case a subsequent CompileTokenArray() is executed.
+ std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aTokenArray ));
+ // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
+ pArr = pNew.get();
+ maArrIterator = FormulaTokenArrayPlainIterator(*pArr);
+ return pNew;
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ // no success - fallback to some internal grammar and hope the best
+ return CompileString( rFormula );
+}
+
+ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const
+{
+ return rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
+}
+
+bool ScCompiler::HandleRange()
+{
+ ScTokenArray* pNew;
+ const ScRangeData* pRangeData = GetRangeData( *mpToken);
+ if (pRangeData)
+ {
+ FormulaError nErr = pRangeData->GetErrCode();
+ if( nErr != FormulaError::NONE )
+ SetError( nErr );
+ else if (mbJumpCommandReorder)
+ {
+ // put named formula into parentheses.
+ // But only if there aren't any yet, parenthetical
+ // ocSep doesn't work, e.g. SUM((...;...))
+ // or if not directly between ocSep/parenthesis,
+ // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
+ // in short: if it isn't a self-contained expression.
+ FormulaToken* p1 = maArrIterator.PeekPrevNoSpaces();
+ FormulaToken* p2 = maArrIterator.PeekNextNoSpaces();
+ OpCode eOp1 = (p1 ? p1->GetOpCode() : ocSep);
+ OpCode eOp2 = (p2 ? p2->GetOpCode() : ocSep);
+ bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
+ bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
+ bool bAddPair = !(bBorder1 && bBorder2);
+ if ( bAddPair )
+ {
+ pNew = new ScTokenArray(rDoc);
+ pNew->AddOpCode( ocClose );
+ PushTokenArray( pNew, true );
+ }
+ pNew = pRangeData->GetCode()->Clone().release();
+ pNew->SetFromRangeName( true );
+ PushTokenArray( pNew, true );
+ if( pRangeData->HasReferences() )
+ {
+ // Relative sheet references in sheet-local named expressions
+ // shall still point to the same sheet as if used on the
+ // original sheet, not shifted to the current position where
+ // they are used.
+ SCTAB nSheetTab = mpToken->GetSheet();
+ if (nSheetTab >= 0 && nSheetTab != aPos.Tab())
+ AdjustSheetLocalNameRelReferences( nSheetTab - aPos.Tab());
+
+ SetRelNameReference();
+ MoveRelWrap();
+ }
+ maArrIterator.Reset();
+ if ( bAddPair )
+ {
+ pNew = new ScTokenArray(rDoc);
+ pNew->AddOpCode( ocOpen );
+ PushTokenArray( pNew, true );
+ }
+ return GetToken();
+ }
+ }
+ else
+ {
+ // No ScRangeData for an already compiled token can happen in BIFF .xls
+ // import if the original range is not present in the document.
+ pNew = new ScTokenArray(rDoc);
+ pNew->Add( new FormulaErrorToken( FormulaError::NoName));
+ PushTokenArray( pNew, true );
+ return GetToken();
+ }
+ return true;
+}
+
+bool ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
+{
+ // Handle external range names.
+ switch (_aToken.GetType())
+ {
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ break;
+ case svExternalName:
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
+ if (!pFile)
+ {
+ SetError(FormulaError::NoName);
+ return true;
+ }
+
+ OUString aName = _aToken.GetString().getString();
+ ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
+ _aToken.GetIndex(), aName, &aPos);
+
+ if (!xNew)
+ {
+ SetError(FormulaError::NoName);
+ return true;
+ }
+
+ ScTokenArray* pNew = xNew->Clone().release();
+ PushTokenArray( pNew, true);
+ if (FormulaTokenArrayPlainIterator(*pNew).GetNextReference() != nullptr)
+ {
+ SetRelNameReference();
+ MoveRelWrap();
+ }
+ maArrIterator.Reset();
+ return GetToken();
+ }
+ default:
+ OSL_FAIL("Wrong type for external reference!");
+ return false;
+ }
+ return true;
+}
+
+void ScCompiler::AdjustSheetLocalNameRelReferences( SCTAB nDelta )
+{
+ for ( auto t: pArr->References() )
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if (rRef1.IsTabRel())
+ rRef1.IncTab( nDelta);
+ if ( t->GetType() == svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ if (rRef2.IsTabRel())
+ rRef2.IncTab( nDelta);
+ }
+ }
+}
+
+// reference of named range with relative references
+
+void ScCompiler::SetRelNameReference()
+{
+ for ( auto t: pArr->References() )
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
+ rRef1.SetRelName( true );
+ if ( t->GetType() == svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
+ rRef2.SetRelName( true );
+ }
+ }
+}
+
+// Wrap-adjust relative references of a RangeName to current position,
+// don't call for other token arrays!
+void ScCompiler::MoveRelWrap()
+{
+ for ( auto t: pArr->References() )
+ {
+ if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
+ ScRefUpdate::MoveRelWrap( rDoc, aPos, rDoc.MaxCol(), rDoc.MaxRow(), SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
+ else
+ ScRefUpdate::MoveRelWrap( rDoc, aPos, rDoc.MaxCol(), rDoc.MaxRow(), *t->GetDoubleRef() );
+ }
+}
+
+// Wrap-adjust relative references of a RangeName to current position,
+// don't call for other token arrays!
+void ScCompiler::MoveRelWrap( const ScTokenArray& rArr, const ScDocument& rDoc, const ScAddress& rPos,
+ SCCOL nMaxCol, SCROW nMaxRow )
+{
+ for ( auto t: rArr.References() )
+ {
+ if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
+ ScRefUpdate::MoveRelWrap( rDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
+ else
+ ScRefUpdate::MoveRelWrap( rDoc, rPos, nMaxCol, nMaxRow, *t->GetDoubleRef() );
+ }
+}
+
+bool ScCompiler::IsCharFlagAllConventions(
+ OUString const & rStr, sal_Int32 nPos, ScCharFlags nFlags )
+{
+ sal_Unicode c = rStr[ nPos ];
+ sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0;
+ if (c < 128)
+ {
+ for ( int nConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
+ ++nConv < formula::FormulaGrammar::CONV_LAST; )
+ {
+ if (pConventions[nConv] &&
+ ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags))
+ return false;
+ // convention not known => assume valid
+ }
+ return true;
+ }
+ else
+ return ScGlobal::getCharClass().isLetterNumeric( rStr, nPos );
+}
+
+void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
+{
+ const FormulaToken* t = pTokenP;
+ sal_uInt16 nFileId = t->GetIndex();
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ sal_uInt16 nUsedFileId = pRefMgr->convertFileIdToUsedFileId(nFileId);
+ const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
+ if (!pFileName)
+ return;
+
+ switch (t->GetType())
+ {
+ case svExternalName:
+ rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString()));
+ break;
+ case svExternalSingleRef:
+ pConv->makeExternalRefStr(rDoc.GetSheetLimits(),
+ rBuffer, GetPos(), nUsedFileId, *pFileName, t->GetString().getString(),
+ *t->GetSingleRef());
+ break;
+ case svExternalDoubleRef:
+ {
+ vector<OUString> aTabNames;
+ pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
+ // No sheet names is a valid case if external sheets were not
+ // cached in this document and external document is not reachable,
+ // else not and worth to be investigated.
+ SAL_WARN_IF( aTabNames.empty(), "sc.core", "wrecked cache of external document? '" <<
+ *pFileName << "' '" << t->GetString().getString() << "'");
+
+ pConv->makeExternalRefStr(
+ rDoc.GetSheetLimits(), rBuffer, GetPos(), nUsedFileId, *pFileName, aTabNames, t->GetString().getString(),
+ *t->GetDoubleRef());
+ }
+ break;
+ default:
+ // warning, not error, otherwise we may end up with a never
+ // ending message box loop if this was the cursor cell to be redrawn.
+ OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
+ }
+}
+
+void ScCompiler::CreateStringFromMatrix( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
+{
+ const ScMatrix* pMatrix = pTokenP->GetMatrix();
+ SCSIZE nC, nMaxC, nR, nMaxR;
+
+ pMatrix->GetDimensions( nMaxC, nMaxR);
+
+ rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
+ for( nR = 0 ; nR < nMaxR ; nR++)
+ {
+ if( nR > 0)
+ {
+ rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
+ }
+
+ for( nC = 0 ; nC < nMaxC ; nC++)
+ {
+ if( nC > 0)
+ {
+ rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
+ }
+
+ if( pMatrix->IsValue( nC, nR ) )
+ {
+ if (pMatrix->IsBoolean(nC, nR))
+ AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0);
+ else
+ {
+ FormulaError nErr = pMatrix->GetError(nC, nR);
+ if (nErr != FormulaError::NONE)
+ rBuffer.append(ScGlobal::GetErrorString(nErr));
+ else
+ AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR));
+ }
+ }
+ else if( pMatrix->IsEmpty( nC, nR ) )
+ ;
+ else if( pMatrix->IsStringOrEmpty( nC, nR ) )
+ AppendString( rBuffer, pMatrix->GetString(nC, nR).getString() );
+ }
+ }
+ rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
+}
+
+namespace {
+void escapeTableRefColumnSpecifier( OUString& rStr )
+{
+ const sal_Int32 n = rStr.getLength();
+ OUStringBuffer aBuf( n * 2 );
+ const sal_Unicode* p = rStr.getStr();
+ const sal_Unicode* const pStop = p + n;
+ for ( ; p < pStop; ++p)
+ {
+ const sal_Unicode c = *p;
+ switch (c)
+ {
+ case '\'':
+ case '[':
+ case '#':
+ case ']':
+ aBuf.append( '\'' );
+ break;
+ default:
+ ; // nothing
+ }
+ aBuf.append( c );
+ }
+ rStr = aBuf.makeStringAndClear();
+}
+}
+
+void ScCompiler::CreateStringFromSingleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
+{
+ const FormulaToken* p;
+ OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
+ const OpCode eOp = _pTokenP->GetOpCode();
+ const ScSingleRefData& rRef = *_pTokenP->GetSingleRef();
+ ScComplexRefData aRef;
+ aRef.Ref1 = aRef.Ref2 = rRef;
+ if ( eOp == ocColRowName )
+ {
+ ScAddress aAbs = rRef.toAbs(rDoc, aPos);
+ if (rDoc.HasStringData(aAbs.Col(), aAbs.Row(), aAbs.Tab()))
+ {
+ OUString aStr = rDoc.GetString(aAbs, mpInterpreterContext);
+ // Enquote to SingleQuoted.
+ aStr = aStr.replaceAll(u"'", u"''");
+ rBuffer.append('\'');
+ rBuffer.append(aStr);
+ rBuffer.append('\'');
+ }
+ else
+ {
+ rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
+ pConv->makeRefStr(rDoc.GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef,
+ GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
+ }
+ }
+ else if (pArr && (p = maArrIterator.PeekPrevNoSpaces()) && p->GetOpCode() == ocTableRefOpen)
+ {
+ OUString aStr;
+ ScAddress aAbs = rRef.toAbs(rDoc, aPos);
+ const ScDBData* pData = rDoc.GetDBAtCursor( aAbs.Col(), aAbs.Row(), aAbs.Tab(), ScDBDataPortion::AREA);
+ SAL_WARN_IF( !pData, "sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef without ScDBData: " <<
+ aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc));
+ if (pData)
+ aStr = pData->GetTableColumnName( aAbs.Col());
+ if (aStr.isEmpty())
+ {
+ if (pData && pData->HasHeader())
+ {
+ SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef falling back to cell: " <<
+ aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc));
+ aStr = rDoc.GetString(aAbs, mpInterpreterContext);
+ }
+ else
+ {
+ SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef of empty header-less: " <<
+ aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc));
+ aStr = aErrRef;
+ }
+ }
+ escapeTableRefColumnSpecifier( aStr);
+ rBuffer.append(aStr);
+ }
+ else
+ pConv->makeRefStr(rDoc.GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef,
+ GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
+}
+
+void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
+{
+ OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
+ pConv->makeRefStr(rDoc.GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef, GetSetupTabNames(),
+ *_pTokenP->GetDoubleRef(), false, (pArr && pArr->IsFromRangeName()));
+}
+
+void ScCompiler::CreateStringFromIndex( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
+{
+ const OpCode eOp = _pTokenP->GetOpCode();
+ OUStringBuffer aBuffer;
+ switch ( eOp )
+ {
+ case ocName:
+ {
+ const ScRangeData* pData = GetRangeData( *_pTokenP);
+ if (pData)
+ {
+ SCTAB nTab = _pTokenP->GetSheet();
+ if (nTab >= 0 && (nTab != aPos.Tab() || mbRefConventionChartOOXML))
+ {
+ // Sheet-local on other sheet.
+ OUString aName;
+ if (rDoc.GetName( nTab, aName))
+ {
+ ScCompiler::CheckTabQuotes( aName, pConv->meConv);
+ aBuffer.append( aName);
+ }
+ else
+ aBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
+ aBuffer.append( pConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR));
+ }
+ else if (mbRefConventionChartOOXML)
+ {
+ aBuffer.append("[0]"
+ + OUStringChar(pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR)));
+ }
+ aBuffer.append(pData->GetName());
+ }
+ }
+ break;
+ case ocDBArea:
+ {
+ const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
+ if (pDBData)
+ aBuffer.append(pDBData->GetName());
+ }
+ break;
+ case ocTableRef:
+ {
+ if (NeedsTableRefTransformation())
+ {
+ // Write the resulting reference if TableRef is not supported.
+ const ScTableRefToken* pTR = dynamic_cast<const ScTableRefToken*>(_pTokenP);
+ if (!pTR)
+ AppendErrorConstant( aBuffer, FormulaError::NoCode);
+ else
+ {
+ const FormulaToken* pRef = pTR->GetAreaRefRPN();
+ if (!pRef)
+ AppendErrorConstant( aBuffer, FormulaError::NoCode);
+ else
+ {
+ switch (pRef->GetType())
+ {
+ case svSingleRef:
+ CreateStringFromSingleRef( aBuffer, pRef);
+ break;
+ case svDoubleRef:
+ CreateStringFromDoubleRef( aBuffer, pRef);
+ break;
+ case svError:
+ AppendErrorConstant( aBuffer, pRef->GetError());
+ break;
+ default:
+ AppendErrorConstant( aBuffer, FormulaError::NoCode);
+ }
+ }
+ }
+ }
+ else
+ {
+ const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
+ if (pDBData)
+ aBuffer.append(pDBData->GetName());
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ if ( !aBuffer.isEmpty() )
+ rBuffer.append(aBuffer);
+ else
+ rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
+}
+
+void ScCompiler::LocalizeString( OUString& rName ) const
+{
+ ScGlobal::GetAddInCollection()->LocalizeString( rName );
+}
+
+FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2 )
+{
+ return extendRangeReference( rDoc.GetSheetLimits(), rTok1, rTok2, aPos, true/*bReuseDoubleRef*/ );
+}
+
+void ScCompiler::fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
+{
+ // All known AddIn functions.
+ sheet::FormulaOpCodeMapEntry aEntry;
+ aEntry.Token.OpCode = ocExternal;
+
+ const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
+ ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
+ const tools::Long nCount = pColl->GetFuncCount();
+ for (tools::Long i=0; i < nCount; ++i)
+ {
+ const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
+ if (pFuncData)
+ {
+ if ( _bIsEnglish )
+ {
+ // This is used with OOXML import, so GetExcelName() is really
+ // wanted here until we'll have a parameter to differentiate
+ // from the general css::sheet::XFormulaOpCodeMapper case and
+ // use pFuncData->GetUpperEnglish().
+ OUString aName;
+ if (pFuncData->GetExcelName( aEnglishLanguageTag, aName))
+ aEntry.Name = aName;
+ else
+ aEntry.Name = pFuncData->GetUpperName();
+ }
+ else
+ aEntry.Name = pFuncData->GetUpperLocal();
+ aEntry.Token.Data <<= pFuncData->GetOriginalName();
+ _rVec.push_back( aEntry);
+ }
+ }
+ // FIXME: what about those old non-UNO AddIns?
+}
+
+bool ScCompiler::HandleColRowName()
+{
+ ScSingleRefData& rRef = *mpToken->GetSingleRef();
+ const ScAddress aAbs = rRef.toAbs(rDoc, aPos);
+ if (!rDoc.ValidAddress(aAbs))
+ {
+ SetError( FormulaError::NoRef );
+ return true;
+ }
+ SCCOL nCol = aAbs.Col();
+ SCROW nRow = aAbs.Row();
+ SCTAB nTab = aAbs.Tab();
+ bool bColName = rRef.IsColRel();
+ SCCOL nMyCol = aPos.Col();
+ SCROW nMyRow = aPos.Row();
+ bool bInList = false;
+ bool bValidName = false;
+ ScRangePairList* pRL = (bColName ?
+ rDoc.GetColNameRanges() : rDoc.GetRowNameRanges());
+ ScRange aRange;
+ for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
+ {
+ const ScRangePair & rR = (*pRL)[i];
+ if ( rR.GetRange(0).Contains( aAbs ) )
+ {
+ bInList = bValidName = true;
+ aRange = rR.GetRange(1);
+ if ( bColName )
+ {
+ aRange.aStart.SetCol( nCol );
+ aRange.aEnd.SetCol( nCol );
+ }
+ else
+ {
+ aRange.aStart.SetRow( nRow );
+ aRange.aEnd.SetRow( nRow );
+ }
+ break; // for
+ }
+ }
+ if ( !bInList && rDoc.GetDocOptions().IsLookUpColRowNames() )
+ { // automagically or created by copying and NamePos isn't in list
+ ScRefCellValue aCell(rDoc, aAbs);
+ bool bString = aCell.hasString();
+ if (!bString && aCell.isEmpty())
+ bString = true; // empty cell is ok
+ if ( bString )
+ { // corresponds with ScInterpreter::ScColRowNameAuto()
+ bValidName = true;
+ if ( bColName )
+ { // ColName
+ SCROW nStartRow = nRow + 1;
+ if ( nStartRow > rDoc.MaxRow() )
+ nStartRow = rDoc.MaxRow();
+ SCROW nMaxRow = rDoc.MaxRow();
+ if ( nMyCol == nCol )
+ { // formula cell in same column
+ if ( nMyRow == nStartRow )
+ { // take remainder under name cell
+ nStartRow++;
+ if ( nStartRow > rDoc.MaxRow() )
+ nStartRow = rDoc.MaxRow();
+ }
+ else if ( nMyRow > nStartRow )
+ { // from name cell down to formula cell
+ nMaxRow = nMyRow - 1;
+ }
+ }
+ for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
+ { // next defined ColNameRange below limits row
+ const ScRangePair & rR = (*pRL)[i];
+ const ScRange& rRange = rR.GetRange(1);
+ if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
+ { // identical column range
+ SCROW nTmp = rRange.aStart.Row();
+ if ( nStartRow < nTmp && nTmp <= nMaxRow )
+ nMaxRow = nTmp - 1;
+ }
+ }
+ aRange.aStart.Set( nCol, nStartRow, nTab );
+ aRange.aEnd.Set( nCol, nMaxRow, nTab );
+ }
+ else
+ { // RowName
+ SCCOL nStartCol = nCol + 1;
+ if ( nStartCol > rDoc.MaxCol() )
+ nStartCol = rDoc.MaxCol();
+ SCCOL nMaxCol = rDoc.MaxCol();
+ if ( nMyRow == nRow )
+ { // formula cell in same row
+ if ( nMyCol == nStartCol )
+ { // take remainder right from name cell
+ nStartCol++;
+ if ( nStartCol > rDoc.MaxCol() )
+ nStartCol = rDoc.MaxCol();
+ }
+ else if ( nMyCol > nStartCol )
+ { // from name cell right to formula cell
+ nMaxCol = nMyCol - 1;
+ }
+ }
+ for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
+ { // next defined RowNameRange to the right limits column
+ const ScRangePair & rR = (*pRL)[i];
+ const ScRange& rRange = rR.GetRange(1);
+ if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
+ { // identical row range
+ SCCOL nTmp = rRange.aStart.Col();
+ if ( nStartCol < nTmp && nTmp <= nMaxCol )
+ nMaxCol = nTmp - 1;
+ }
+ }
+ aRange.aStart.Set( nStartCol, nRow, nTab );
+ aRange.aEnd.Set( nMaxCol, nRow, nTab );
+ }
+ }
+ }
+ if ( bValidName )
+ {
+ // And now the magic to distinguish between a range and a single
+ // cell thereof, which is picked position-dependent of the formula
+ // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
+ // SingleRef matching the column/row of the formula cell is
+ // generated. A ocColRowName or ocIntersect as a neighbor results
+ // in a range. Special case: if label is valid for a single cell, a
+ // position independent SingleRef is generated.
+ bool bSingle = (aRange.aStart == aRange.aEnd);
+ bool bFound;
+ if ( bSingle )
+ bFound = true;
+ else
+ {
+ FormulaToken* p1 = maArrIterator.PeekPrevNoSpaces();
+ FormulaToken* p2 = maArrIterator.PeekNextNoSpaces();
+ // begin/end of a formula => single
+ OpCode eOp1 = p1 ? p1->GetOpCode() : ocAdd;
+ OpCode eOp2 = p2 ? p2->GetOpCode() : ocAdd;
+ if ( eOp1 != ocColRowName && eOp1 != ocIntersect
+ && eOp2 != ocColRowName && eOp2 != ocIntersect )
+ {
+ if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
+ (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
+ bSingle = true;
+ }
+ if ( bSingle )
+ { // column and/or row must match range
+ if ( bColName )
+ {
+ bFound = (aRange.aStart.Row() <= nMyRow
+ && nMyRow <= aRange.aEnd.Row());
+ if ( bFound )
+ aRange.aStart.SetRow( nMyRow );
+ }
+ else
+ {
+ bFound = (aRange.aStart.Col() <= nMyCol
+ && nMyCol <= aRange.aEnd.Col());
+ if ( bFound )
+ aRange.aStart.SetCol( nMyCol );
+ }
+ }
+ else
+ bFound = true;
+ }
+ if ( !bFound )
+ SetError(FormulaError::NoRef);
+ else if (mbJumpCommandReorder)
+ {
+ ScTokenArray* pNew = new ScTokenArray(rDoc);
+ if ( bSingle )
+ {
+ ScSingleRefData aRefData;
+ aRefData.InitAddress( aRange.aStart );
+ if ( bColName )
+ aRefData.SetColRel( true );
+ else
+ aRefData.SetRowRel( true );
+ aRefData.SetAddress(rDoc.GetSheetLimits(), aRange.aStart, aPos);
+ pNew->AddSingleReference( aRefData );
+ }
+ else
+ {
+ ScComplexRefData aRefData;
+ aRefData.InitRange( aRange );
+ if ( bColName )
+ {
+ aRefData.Ref1.SetColRel( true );
+ aRefData.Ref2.SetColRel( true );
+ }
+ else
+ {
+ aRefData.Ref1.SetRowRel( true );
+ aRefData.Ref2.SetRowRel( true );
+ }
+ aRefData.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
+ if ( bInList )
+ pNew->AddDoubleReference( aRefData );
+ else
+ { // automagically
+ pNew->Add( new ScDoubleRefToken( rDoc.GetSheetLimits(), aRefData, ocColRowNameAuto ) );
+ }
+ }
+ PushTokenArray( pNew, true );
+ return GetToken();
+ }
+ }
+ else
+ SetError(FormulaError::NoName);
+ return true;
+}
+
+bool ScCompiler::HandleDbData()
+{
+ ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex(mpToken->GetIndex());
+ if ( !pDBData )
+ SetError(FormulaError::NoName);
+ else if (mbJumpCommandReorder)
+ {
+ ScComplexRefData aRefData;
+ aRefData.InitFlags();
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ aRange.aEnd.SetTab(aRange.aStart.Tab());
+ aRefData.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
+ ScTokenArray* pNew = new ScTokenArray(rDoc);
+ pNew->AddDoubleReference( aRefData );
+ PushTokenArray( pNew, true );
+ return GetToken();
+ }
+ return true;
+}
+
+bool ScCompiler::GetTokenIfOpCode( OpCode eOp )
+{
+ const formula::FormulaToken* p = maArrIterator.PeekNextNoSpaces();
+ if (p && p->GetOpCode() == eOp)
+ return GetToken();
+ return false;
+}
+
+
+/* Documentation on MS-Excel Table structured references:
+ * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f
+ * * as of Excel 2013
+ * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx
+ * * look for structure-reference
+ */
+
+bool ScCompiler::HandleTableRef()
+{
+ ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(mpToken.get());
+ if (!pTR)
+ {
+ SetError(FormulaError::UnknownToken);
+ return true;
+ }
+
+ ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( pTR->GetIndex());
+ if ( !pDBData )
+ SetError(FormulaError::NoName);
+ else if (mbJumpCommandReorder)
+ {
+ ScRange aDBRange;
+ pDBData->GetArea(aDBRange);
+ aDBRange.aEnd.SetTab(aDBRange.aStart.Tab());
+ ScRange aRange( aDBRange);
+ FormulaError nError = FormulaError::NONE;
+ bool bForwardToClose = false;
+ ScTableRefToken::Item eItem = pTR->GetItem();
+ switch (eItem)
+ {
+ case ScTableRefToken::TABLE:
+ {
+ // The table name without items references the table data,
+ // without headers or totals.
+ if (pDBData->HasHeader())
+ aRange.aStart.IncRow();
+ if (pDBData->HasTotals())
+ aRange.aEnd.IncRow(-1);
+ if (aRange.aEnd.Row() < aRange.aStart.Row())
+ nError = FormulaError::NoRef;
+ bForwardToClose = true;
+ }
+ break;
+ case ScTableRefToken::ALL:
+ {
+ bForwardToClose = true;
+ }
+ break;
+ case ScTableRefToken::HEADERS:
+ {
+ if (pDBData->HasHeader())
+ aRange.aEnd.SetRow( aRange.aStart.Row());
+ else
+ nError = FormulaError::NoRef;
+ bForwardToClose = true;
+ }
+ break;
+ case ScTableRefToken::DATA:
+ {
+ if (pDBData->HasHeader())
+ aRange.aStart.IncRow();
+ }
+ [[fallthrough]];
+ case ScTableRefToken::HEADERS_DATA:
+ {
+ if (pDBData->HasTotals())
+ aRange.aEnd.IncRow(-1);
+ if (aRange.aEnd.Row() < aRange.aStart.Row())
+ nError = FormulaError::NoRef;
+ bForwardToClose = true;
+ }
+ break;
+ case ScTableRefToken::TOTALS:
+ {
+ if (pDBData->HasTotals())
+ aRange.aStart.SetRow( aRange.aEnd.Row());
+ else
+ nError = FormulaError::NoRef;
+ bForwardToClose = true;
+ }
+ break;
+ case ScTableRefToken::DATA_TOTALS:
+ {
+ if (pDBData->HasHeader())
+ aRange.aStart.IncRow();
+ if (aRange.aEnd.Row() < aRange.aStart.Row())
+ nError = FormulaError::NoRef;
+ bForwardToClose = true;
+ }
+ break;
+ case ScTableRefToken::THIS_ROW:
+ {
+ if (aRange.aStart.Row() <= aPos.Row() && aPos.Row() <= aRange.aEnd.Row())
+ {
+ aRange.aStart.SetRow( aPos.Row());
+ aRange.aEnd.SetRow( aPos.Row());
+ }
+ else
+ {
+ nError = FormulaError::NoValue;
+ // For *some* relative row reference in named
+ // expressions' thisrow special handling below.
+ aRange.aEnd.SetRow( aRange.aStart.Row());
+ }
+ bForwardToClose = true;
+ }
+ break;
+ }
+ bool bColumnRange = false;
+ bool bCol1Rel = false;
+ bool bCol1RelName = false;
+ int nLevel = 0;
+ if (bForwardToClose && GetTokenIfOpCode( ocTableRefOpen))
+ {
+ ++nLevel;
+ enum
+ {
+ sOpen,
+ sItem,
+ sClose,
+ sSep,
+ sLast,
+ sStop
+ } eState = sOpen;
+ do
+ {
+ const formula::FormulaToken* p = maArrIterator.PeekNextNoSpaces();
+ if (!p)
+ eState = sStop;
+ else
+ {
+ switch (p->GetOpCode())
+ {
+ case ocTableRefOpen:
+ eState = ((eState == sOpen || eState == sSep) ? sOpen : sStop);
+ if (++nLevel > 2)
+ {
+ SetError( FormulaError::Pair);
+ eState = sStop;
+ }
+ break;
+ case ocTableRefItemAll:
+ case ocTableRefItemHeaders:
+ case ocTableRefItemData:
+ case ocTableRefItemTotals:
+ case ocTableRefItemThisRow:
+ eState = ((eState == sOpen) ? sItem : sStop);
+ break;
+ case ocTableRefClose:
+ eState = ((eState == sItem || eState == sClose) ? sClose : sStop);
+ if (eState != sStop && --nLevel == 0)
+ eState = sLast;
+ break;
+ case ocSep:
+ eState = ((eState == sClose) ? sSep : sStop);
+ break;
+ case ocPush:
+ if (eState == sOpen && p->GetType() == svSingleRef)
+ {
+ bColumnRange = true;
+ bCol1Rel = p->GetSingleRef()->IsColRel();
+ bCol1RelName = p->GetSingleRef()->IsRelName();
+ eState = sLast;
+ }
+ else
+ {
+ eState = sStop;
+ }
+ break;
+ case ocBad:
+ eState = sLast;
+ if (nError == FormulaError::NONE)
+ nError = FormulaError::NoName;
+ break;
+ default:
+ eState = sStop;
+ }
+ if (eState != sStop)
+ GetToken();
+ if (eState == sLast)
+ eState = sStop;
+ }
+ } while (eState != sStop);
+ }
+ ScTokenArray* pNew = new ScTokenArray(rDoc);
+ if (nError == FormulaError::NONE || nError == FormulaError::NoValue)
+ {
+ bool bCol2Rel = false;
+ bool bCol2RelName = false;
+ // The FormulaError::NoValue case generates a thisrow reference that can be
+ // used to save named expressions in A1 syntax notation.
+ if (bColumnRange)
+ {
+ // Limit range to specified columns.
+ ScRange aColRange( ScAddress::INITIALIZE_INVALID );
+ switch (mpToken->GetType())
+ {
+ case svSingleRef:
+ {
+ aColRange.aStart = aColRange.aEnd = mpToken->GetSingleRef()->toAbs(rDoc, aPos);
+ if ( GetTokenIfOpCode( ocTableRefClose) && (nLevel--) &&
+ GetTokenIfOpCode( ocRange) &&
+ GetTokenIfOpCode( ocTableRefOpen) && (++nLevel) &&
+ GetTokenIfOpCode( ocPush))
+ {
+ if (mpToken->GetType() != svSingleRef)
+ aColRange = ScRange( ScAddress::INITIALIZE_INVALID);
+ else
+ {
+ aColRange.aEnd = mpToken->GetSingleRef()->toAbs(rDoc, aPos);
+ aColRange.PutInOrder();
+ bCol2Rel = mpToken->GetSingleRef()->IsColRel();
+ bCol2RelName = mpToken->GetSingleRef()->IsRelName();
+ }
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ // coverity[copy_paste_error : FALSE] - this is correct, aStart in both aDBRange uses
+ if (aColRange.aStart.Row() != aDBRange.aStart.Row() || aColRange.aEnd.Row() != aDBRange.aStart.Row())
+ aRange = ScRange( ScAddress::INITIALIZE_INVALID);
+ else
+ {
+ aColRange.aEnd.SetRow( aRange.aEnd.Row());
+ aRange = aRange.Intersection( aColRange);
+ }
+ }
+ if (aRange.IsValid())
+ {
+ if (aRange.aStart == aRange.aEnd)
+ {
+ ScSingleRefData aRefData;
+ aRefData.InitFlags();
+ aRefData.SetColRel( bCol1Rel);
+ if (eItem == ScTableRefToken::THIS_ROW)
+ {
+ aRefData.SetRowRel( true);
+ if (!bCol1RelName)
+ bCol1RelName = pArr->IsFromRangeName();
+ }
+ aRefData.SetRelName( bCol1RelName);
+ aRefData.SetFlag3D( true);
+ if (nError != FormulaError::NONE)
+ {
+ aRefData.SetAddress( rDoc.GetSheetLimits(), aRange.aStart, aRange.aStart);
+ pTR->SetAreaRefRPN( new ScSingleRefToken(rDoc.GetSheetLimits(), aRefData)); // set reference at TableRef
+ pNew->Add( new FormulaErrorToken( nError)); // set error in RPN
+ }
+ else
+ {
+ aRefData.SetAddress( rDoc.GetSheetLimits(), aRange.aStart, aPos);
+ pTR->SetAreaRefRPN( pNew->AddSingleReference( aRefData));
+ }
+ }
+ else
+ {
+ ScComplexRefData aRefData;
+ aRefData.InitFlags();
+ aRefData.Ref1.SetColRel( bCol1Rel);
+ aRefData.Ref2.SetColRel( bCol2Rel);
+ bool bRelName = bCol1RelName || bCol2RelName;
+ if (eItem == ScTableRefToken::THIS_ROW)
+ {
+ aRefData.Ref1.SetRowRel( true);
+ aRefData.Ref2.SetRowRel( true);
+ if (!bRelName)
+ bRelName = pArr->IsFromRangeName();
+ }
+ aRefData.Ref1.SetRelName( bRelName);
+ aRefData.Ref2.SetRelName( bRelName);
+ aRefData.Ref1.SetFlag3D( true);
+ if (nError != FormulaError::NONE)
+ {
+ aRefData.SetRange( rDoc.GetSheetLimits(), aRange, aRange.aStart);
+ pTR->SetAreaRefRPN( new ScDoubleRefToken(rDoc.GetSheetLimits(), aRefData)); // set reference at TableRef
+ pNew->Add( new FormulaErrorToken( nError)); // set error in RPN
+ }
+ else
+ {
+ aRefData.SetRange( rDoc.GetSheetLimits(), aRange, aPos);
+ pTR->SetAreaRefRPN( pNew->AddDoubleReference( aRefData));
+ }
+ }
+ }
+ else
+ {
+ pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( FormulaError::NoRef)));
+ }
+ }
+ else
+ {
+ pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( nError)));
+ }
+ while (nLevel-- > 0)
+ {
+ if (!GetTokenIfOpCode( ocTableRefClose))
+ SetError( FormulaError::Pair);
+ }
+ PushTokenArray( pNew, true );
+ return GetToken();
+ }
+ return true;
+}
+
+formula::ParamClass ScCompiler::GetForceArrayParameter( const formula::FormulaToken* pToken, sal_uInt16 nParam ) const
+{
+ return ScParameterClassification::GetParameterType( pToken, nParam);
+}
+
+bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken* token, int parameter)
+{
+ formula::ParamClass param = ScParameterClassification::GetParameterType( token, parameter );
+ return param == Value || param == Array;
+}
+
+bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken* token) const
+{
+ if (mbMatrixFlag)
+ return true;
+ formula::ParamClass paramClass = token->GetInForceArray();
+ if (paramClass == formula::ForceArray
+ || paramClass == formula::ReferenceOrForceArray
+ || paramClass == formula::SuppressedReferenceOrForceArray
+ || paramClass == formula::ReferenceOrRefArray)
+ {
+ return true;
+ }
+ formula::ParamClass returnType = ScParameterClassification::GetParameterType( token, SAL_MAX_UINT16 );
+ return returnType == formula::Reference;
+}
+
+void ScCompiler::HandleIIOpCode(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams)
+{
+ if (!mbComputeII)
+ return;
+#ifdef DBG_UTIL
+ if(!HandleIIOpCodeInternal(token, pppToken, nNumParams))
+ mUnhandledPossibleImplicitIntersectionsOpCodes.insert(token->GetOpCode());
+#else
+ HandleIIOpCodeInternal(token, pppToken, nNumParams);
+#endif
+}
+
+// return true if opcode is handled
+bool ScCompiler::HandleIIOpCodeInternal(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams)
+{
+ if (nNumParams > 0 && *pppToken[0] == nullptr)
+ return false; // Bad expression (see the dummy creation in FormulaCompiler::CompileTokenArray())
+
+ const OpCode nOpCode = token->GetOpCode();
+
+ if (nOpCode == ocPush)
+ {
+ if(token->GetType() == svDoubleRef)
+ mUnhandledPossibleImplicitIntersections.insert( token );
+ return true;
+ }
+ else if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
+ {
+ if (nNumParams != 3)
+ return false;
+
+ if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
+ return false;
+
+ if ((*pppToken[0])->GetType() != svDoubleRef)
+ return false;
+
+ const StackVar eSumRangeType = (*pppToken[2])->GetType();
+
+ if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
+ return false;
+
+ const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
+
+ ScComplexRefData aSumRange;
+ if (eSumRangeType == svSingleRef)
+ {
+ aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
+ aSumRange.Ref2 = aSumRange.Ref1;
+ }
+ else
+ aSumRange = *(*pppToken[2])->GetDoubleRef();
+
+ CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
+ // TODO mark parameters as handled
+ return true;
+ }
+ else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
+ {
+ if (nNumParams != 1)
+ return false;
+
+ if( !ParameterMayBeImplicitIntersection( token, 0 ))
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
+
+ if ((*pppToken[0])->GetType() != svDoubleRef)
+ return false;
+
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ return true;
+ }
+ else if ((nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_BIN_OP
+ && nOpCode != ocAnd && nOpCode != ocOr)
+ || nOpCode == ocRound || nOpCode == ocRoundUp || nOpCode == ocRoundDown)
+ {
+ if (nNumParams != 2)
+ return false;
+
+ if( !ParameterMayBeImplicitIntersection( token, 0 ) || !ParameterMayBeImplicitIntersection( token, 1 ))
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
+
+ // Convert only if the other parameter is not a matrix (which would force the result to be a matrix).
+ if ((*pppToken[0])->GetType() == svDoubleRef && (*pppToken[1])->GetType() != svMatrix)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ if ((*pppToken[1])->GetType() == svDoubleRef && (*pppToken[0])->GetType() != svMatrix)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[1], token );
+ return true;
+ }
+ else if ((nOpCode >= SC_OPCODE_START_UN_OP && nOpCode < SC_OPCODE_STOP_UN_OP)
+ || nOpCode == ocPercentSign)
+ {
+ if (nNumParams != 1)
+ return false;
+
+ if( !ParameterMayBeImplicitIntersection( token, 0 ))
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
+
+ if ((*pppToken[0])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ return true;
+ }
+ else if (nOpCode == ocVLookup)
+ {
+ if (nNumParams != 3 && nNumParams != 4)
+ return false;
+
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
+
+ assert( ParameterMayBeImplicitIntersection( token, 0 ));
+ assert( !ParameterMayBeImplicitIntersection( token, 1 ));
+ assert( ParameterMayBeImplicitIntersection( token, 2 ));
+ assert( ParameterMayBeImplicitIntersection( token, 3 ));
+ if ((*pppToken[2])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[2], token );
+ if ((*pppToken[0])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ if (nNumParams == 4 && (*pppToken[3])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[3], token );
+ // a range for the second parameters is not an implicit intersection
+ mUnhandledPossibleImplicitIntersections.erase( *pppToken[ 1 ] );
+ return true;
+ }
+ else
+ {
+ bool possibleII = false;
+ for( int i = 0; i < nNumParams; ++i )
+ {
+ if( ParameterMayBeImplicitIntersection( token, i )
+ && (*pppToken[i])->GetType() == svDoubleRef)
+ {
+ possibleII = true;
+ break;
+ }
+ }
+ if( !possibleII )
+ {
+ // all parameters have been handled, they are not implicit intersections
+ for( int i = 0; i < nNumParams; ++i )
+ mUnhandledPossibleImplicitIntersections.erase( *pppToken[ i ] );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScCompiler::PostProcessCode()
+{
+ for( const PendingImplicitIntersectionOptimization& item : mPendingImplicitIntersectionOptimizations )
+ {
+ if( *item.parameterLocation != item.parameter ) // the parameter has been changed somehow
+ continue;
+ if( item.parameterLocation >= pCode ) // the location is not inside the code (pCode points after the end)
+ continue;
+ // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
+ if( item.operation->IsInForceArray())
+ continue;
+ ReplaceDoubleRefII( item.parameterLocation );
+ }
+ mPendingImplicitIntersectionOptimizations.clear();
+}
+
+void ScCompiler::AnnotateOperands()
+{
+ AnnotateTrimOnDoubleRefs();
+}
+
+void ScCompiler::ReplaceDoubleRefII(FormulaToken** ppDoubleRefTok)
+{
+ const ScComplexRefData* pRange = (*ppDoubleRefTok)->GetDoubleRef();
+ if (!pRange)
+ return;
+
+ const ScComplexRefData& rRange = *pRange;
+
+ // Can't do optimization reliably in this case (when row references are absolute).
+ // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100.
+ // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then
+ // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104)
+ // instead of the #VALUE! errors we would expect. We need to know the formula-group length to
+ // fix this, but that is unknown at this stage, so skip such cases.
+ if (!rRange.Ref1.IsRowRel() && !rRange.Ref2.IsRowRel())
+ return;
+
+ ScRange aAbsRange = rRange.toAbs(rDoc, aPos);
+ if (aAbsRange.aStart == aAbsRange.aEnd)
+ return; // Nothing to do (trivial case).
+
+ ScAddress aAddr;
+
+ if (!DoubleRefToPosSingleRefScalarCase(aAbsRange, aAddr, aPos))
+ return;
+
+ ScSingleRefData aSingleRef;
+ aSingleRef.InitFlags();
+ aSingleRef.SetColRel(rRange.Ref1.IsColRel());
+ aSingleRef.SetRowRel(true);
+ aSingleRef.SetTabRel(rRange.Ref1.IsTabRel());
+ aSingleRef.SetAddress(rDoc.GetSheetLimits(), aAddr, aPos);
+
+ // Replace the original doubleref token with computed singleref token
+ FormulaToken* pNewSingleRefTok = new ScSingleRefToken(rDoc.GetSheetLimits(), aSingleRef);
+ (*ppDoubleRefTok)->DecRef();
+ *ppDoubleRefTok = pNewSingleRefTok;
+ pNewSingleRefTok->IncRef();
+}
+
+bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr, const ScAddress& rFormulaPos)
+{
+ assert(rRange.aStart != rRange.aEnd);
+
+ bool bOk = false;
+ SCCOL nMyCol = rFormulaPos.Col();
+ SCROW nMyRow = rFormulaPos.Row();
+ SCTAB nMyTab = rFormulaPos.Tab();
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ SCTAB nTab;
+ nTab = rRange.aStart.Tab();
+ if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
+ {
+ nRow = rRange.aStart.Row();
+ if ( nRow == rRange.aEnd.Row() )
+ {
+ bOk = true;
+ nCol = nMyCol;
+ }
+ else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
+ && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
+ {
+ bOk = true;
+ nCol = nMyCol;
+ nRow = nMyRow;
+ }
+ }
+ else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
+ {
+ nCol = rRange.aStart.Col();
+ if ( nCol == rRange.aEnd.Col() )
+ {
+ bOk = true;
+ nRow = nMyRow;
+ }
+ else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
+ && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
+ {
+ bOk = true;
+ nCol = nMyCol;
+ nRow = nMyRow;
+ }
+ }
+ if ( bOk )
+ {
+ if ( nTab == rRange.aEnd.Tab() )
+ ; // all done
+ else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() )
+ nTab = nMyTab;
+ else
+ bOk = false;
+ if ( bOk )
+ rAdr.Set( nCol, nRow, nTab );
+ }
+
+ return bOk;
+}
+
+static void lcl_GetColRowDeltas(const ScRange& rRange, SCCOL& rXDelta, SCROW& rYDelta)
+{
+ rXDelta = rRange.aEnd.Col() - rRange.aStart.Col();
+ rYDelta = rRange.aEnd.Row() - rRange.aStart.Row();
+}
+
+bool ScCompiler::AdjustSumRangeShape(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange)
+{
+ ScRange aAbs = rSumRange.toAbs(rDoc, aPos);
+
+ // Current sum-range end col/row
+ SCCOL nEndCol = aAbs.aEnd.Col();
+ SCROW nEndRow = aAbs.aEnd.Row();
+
+ // Current behaviour is, we will get a #NAME? for the below case, so bail out.
+ // Note that sum-range's End[Col,Row] are same as Start[Col,Row] if the original formula
+ // has a single-ref as the sum-range.
+ if (!rDoc.ValidCol(nEndCol) || !rDoc.ValidRow(nEndRow))
+ return false;
+
+ SCCOL nXDeltaSum = 0;
+ SCROW nYDeltaSum = 0;
+
+ lcl_GetColRowDeltas(aAbs, nXDeltaSum, nYDeltaSum);
+
+ aAbs = rBaseRange.toAbs(rDoc, aPos);
+ SCCOL nXDelta = 0;
+ SCROW nYDelta = 0;
+
+ lcl_GetColRowDeltas(aAbs, nXDelta, nYDelta);
+
+ if (nXDelta == nXDeltaSum &&
+ nYDelta == nYDeltaSum)
+ return false; // shapes of base-range match current sum-range
+
+ // Try to make the sum-range to take the same shape as base-range,
+ // by adjusting Ref2 member of rSumRange if the resultant sum-range don't
+ // go out-of-bounds.
+
+ SCCOL nXInc = nXDelta - nXDeltaSum;
+ SCROW nYInc = nYDelta - nYDeltaSum;
+
+ // Don't let a valid End[Col,Row] go beyond (rDoc.MaxCol(),rDoc.MaxRow()) to match
+ // what happens in ScInterpreter::IterateParametersIf(), but there it also shrinks
+ // the base-range by the (out-of-bound)amount clipped off the sum-range.
+ // TODO: Probably we can optimize (from threading perspective) rBaseRange
+ // by shrinking it here correspondingly (?)
+ if (nEndCol + nXInc > rDoc.MaxCol())
+ nXInc = rDoc.MaxCol() - nEndCol;
+ if (nEndRow + nYInc > rDoc.MaxRow())
+ nYInc = rDoc.MaxRow() - nEndRow;
+
+ rSumRange.Ref2.IncCol(nXInc);
+ rSumRange.Ref2.IncRow(nYInc);
+
+ return true;
+}
+
+void ScCompiler::CorrectSumRange(const ScComplexRefData& rBaseRange,
+ ScComplexRefData& rSumRange,
+ FormulaToken** ppSumRangeToken)
+{
+ if (!AdjustSumRangeShape(rBaseRange, rSumRange))
+ return;
+
+ // Replace sum-range token
+ FormulaToken* pNewSumRangeTok = new ScDoubleRefToken(rDoc.GetSheetLimits(), rSumRange);
+ (*ppSumRangeToken)->DecRef();
+ *ppSumRangeToken = pNewSumRangeTok;
+ pNewSumRangeTok->IncRef();
+}
+
+void ScCompiler::AnnotateTrimOnDoubleRefs()
+{
+ if (!pCode || !(*(pCode - 1)))
+ return;
+
+ // OpCode of the "root" operator (which is already in RPN array).
+ OpCode eOpCode = (*(pCode - 1))->GetOpCode();
+ // eOpCode can be some operator which does not change with operands with or contains zero values.
+ if (eOpCode == ocSum)
+ {
+ FormulaToken** ppTok = pCode - 2; // exclude the root operator.
+ // The following loop runs till a "pattern" is found or there is a mismatch
+ // and marks the push DoubleRef arguments as trimmable when there is a match.
+ // The pattern is
+ // SUM(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
+ // such that one of the operands of ocEqual is a double-ref.
+ // Examples of formula that matches this are:
+ // SUM(IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)
+ // SUM((IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)*$H$2*5/$G$3)
+ // SUM(IF(E:E=16,F:F)*$H$1*100)
+ bool bTillClose = true;
+ bool bCloseTillIf = false;
+ sal_Int16 nToksTillIf = 0;
+ constexpr sal_Int16 MAXDIST_IF = 15;
+ while (*ppTok)
+ {
+ FormulaToken* pTok = *ppTok;
+ OpCode eCurrOp = pTok->GetOpCode();
+ ++nToksTillIf;
+
+ // TODO : Is there a better way to handle this ?
+ // ocIf is too far off from the sum opcode.
+ if (nToksTillIf > MAXDIST_IF)
+ return;
+
+ switch (eCurrOp)
+ {
+ case ocDiv:
+ case ocMul:
+ if (!bTillClose)
+ return;
+ break;
+ case ocPush:
+
+ break;
+ case ocClose:
+ if (bTillClose)
+ {
+ bTillClose = false;
+ bCloseTillIf = true;
+ }
+ else
+ return;
+ break;
+ case ocIf:
+ {
+ if (!bCloseTillIf)
+ return;
+
+ if (!pTok->IsInForceArray())
+ return;
+
+ const short nJumpCount = pTok->GetJump()[0];
+ if (nJumpCount != 2) // Should have THEN but no ELSE.
+ return;
+
+ OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
+ if (eCompOp != ocEqual)
+ return;
+
+ FormulaToken* pLHS = *(ppTok - 2);
+ FormulaToken* pRHS = *(ppTok - 3);
+ if (((pLHS->GetType() == svSingleRef || pLHS->GetType() == svDouble) && pRHS->GetType() == svDoubleRef) ||
+ ((pRHS->GetType() == svSingleRef || pRHS->GetType() == svDouble) && pLHS->GetType() == svDoubleRef))
+ {
+ if (pLHS->GetType() == svDoubleRef)
+ pLHS->GetDoubleRef()->SetTrimToData(true);
+ else
+ pRHS->GetDoubleRef()->SetTrimToData(true);
+ return;
+ }
+ }
+ break;
+ default:
+ return;
+ }
+ --ppTok;
+ }
+ }
+ else if (eOpCode == ocSumProduct)
+ {
+ FormulaToken** ppTok = pCode - 2; // exclude the root operator.
+ // The following loop runs till a "pattern" is found or there is a mismatch
+ // and marks the push DoubleRef arguments as trimmable when there is a match.
+ // The pattern is
+ // SUMPRODUCT(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
+ // such that one of the operands of ocEqual is a double-ref.
+ // Examples of formula that matches this are:
+ // SUMPRODUCT(IF($A:$A=$L12;$D:$D*G:G))
+ bool bTillClose = true;
+ bool bCloseTillIf = false;
+ sal_Int16 nToksTillIf = 0;
+ constexpr sal_Int16 MAXDIST_IF = 15;
+ while (*ppTok)
+ {
+ FormulaToken* pTok = *ppTok;
+ OpCode eCurrOp = pTok->GetOpCode();
+ ++nToksTillIf;
+
+ // TODO : Is there a better way to handle this ?
+ // ocIf is too far off from the sum opcode.
+ if (nToksTillIf > MAXDIST_IF)
+ return;
+
+ switch (eCurrOp)
+ {
+ case ocDiv:
+ case ocMul:
+ {
+ if (!pTok->IsInForceArray())
+ break;
+ FormulaToken* pLHS = *(ppTok - 1);
+ FormulaToken* pRHS = *(ppTok - 2);
+ StackVar lhsType = pLHS->GetType();
+ StackVar rhsType = pRHS->GetType();
+ if (lhsType == svDoubleRef && rhsType == svDoubleRef)
+ {
+ pLHS->GetDoubleRef()->SetTrimToData(true);
+ pRHS->GetDoubleRef()->SetTrimToData(true);
+ }
+ }
+ break;
+ case ocPush:
+ break;
+ case ocClose:
+ if (bTillClose)
+ {
+ bTillClose = false;
+ bCloseTillIf = true;
+ }
+ else
+ return;
+ break;
+ case ocIf:
+ {
+ if (!bCloseTillIf)
+ return;
+
+ if (!pTok->IsInForceArray())
+ return;
+
+ const short nJumpCount = pTok->GetJump()[0];
+ if (nJumpCount != 2) // Should have THEN but no ELSE.
+ return;
+
+ OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
+ if (eCompOp != ocEqual)
+ return;
+
+ FormulaToken* pLHS = *(ppTok - 2);
+ FormulaToken* pRHS = *(ppTok - 3);
+ StackVar lhsType = pLHS->GetType();
+ StackVar rhsType = pRHS->GetType();
+ if (lhsType == svDoubleRef && (rhsType == svSingleRef || rhsType == svDouble))
+ {
+ pLHS->GetDoubleRef()->SetTrimToData(true);
+ }
+ if ((lhsType == svSingleRef || lhsType == svDouble) && rhsType == svDoubleRef)
+ {
+ pRHS->GetDoubleRef()->SetTrimToData(true);
+ }
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+ --ppTok;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/consoli.cxx b/sc/source/core/tool/consoli.cxx
new file mode 100644
index 0000000000..aa8dbcc3ee
--- /dev/null
+++ b/sc/source/core/tool/consoli.cxx
@@ -0,0 +1,544 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <consoli.hxx>
+#include <document.hxx>
+#include <olinetab.hxx>
+#include <subtotal.hxx>
+#include <formula/errorcodes.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <osl/diagnose.h>
+#include <refdata.hxx>
+
+#include <string.h>
+#include <memory>
+
+#define SC_CONS_NOTFOUND -1
+
+const OpCode eOpCodeTable[] = { // order as for enum ScSubTotalFunc
+ ocBad, // none
+ ocAverage,
+ ocCount,
+ ocCount2,
+ ocMax,
+ ocMin,
+ ocProduct,
+ ocStDev,
+ ocStDevP,
+ ocSum,
+ ocVar,
+ ocVarP };
+
+template< typename T >
+static void lcl_AddString( ::std::vector<OUString>& rData, T& nCount, const OUString& rInsert )
+{
+ rData.push_back( rInsert);
+ ++nCount;
+}
+
+ScConsData::ScConsData() :
+ eFunction(SUBTOTAL_FUNC_SUM),
+ bReference(false),
+ bColByName(false),
+ bRowByName(false),
+ nColCount(0),
+ nRowCount(0),
+ nDataCount(0),
+ bCornerUsed(false)
+{
+}
+
+ScConsData::~ScConsData()
+{
+}
+
+void ScConsData::DeleteData()
+{
+ ppRefs.reset();
+ ppFunctionData.reset();
+ ppUsed.reset();
+ ppTitlePos.reset();
+ ::std::vector<OUString>().swap( maColHeaders);
+ ::std::vector<OUString>().swap( maRowHeaders);
+ ::std::vector<OUString>().swap( maTitles);
+ nDataCount = 0;
+
+ if (bColByName) nColCount = 0; // otherwise maColHeaders is wrong
+ if (bRowByName) nRowCount = 0;
+
+ bCornerUsed = false;
+ aCornerText.clear();
+}
+
+void ScConsData::InitData()
+{
+ if (bReference && nColCount && !ppRefs)
+ {
+ ppRefs.reset(new std::unique_ptr<ScReferenceList[]>[nColCount]);
+ for (SCSIZE i=0; i<nColCount; i++)
+ ppRefs[i].reset(new ScReferenceList[nRowCount]);
+ }
+ else if (nColCount && !ppFunctionData)
+ {
+ ppFunctionData.reset( new std::unique_ptr<ScFunctionData[]>[nColCount] );
+ for (SCSIZE i=0; i<nColCount; i++)
+ {
+ ppFunctionData[i].reset( new ScFunctionData[nRowCount] );
+ }
+ }
+
+ if (nColCount && !ppUsed)
+ {
+ ppUsed.reset( new std::unique_ptr<bool[]>[nColCount] );
+ for (SCSIZE i=0; i<nColCount; i++)
+ {
+ ppUsed[i].reset( new bool[nRowCount] );
+ memset( ppUsed[i].get(), 0, nRowCount * sizeof(bool) );
+ }
+ }
+
+ if (nRowCount && nDataCount && !ppTitlePos)
+ {
+ ppTitlePos.reset( new std::unique_ptr<SCSIZE[]>[nRowCount] );
+ for (SCSIZE i=0; i<nRowCount; i++)
+ {
+ ppTitlePos[i].reset( new SCSIZE[nDataCount] );
+ memset( ppTitlePos[i].get(), 0, nDataCount * sizeof(SCSIZE) ); //TODO: not necessary ?
+ }
+ }
+
+ // CornerText: single String
+}
+
+void ScConsData::DoneFields()
+{
+ InitData();
+}
+
+void ScConsData::SetSize( SCCOL nCols, SCROW nRows )
+{
+ DeleteData();
+ nColCount = static_cast<SCSIZE>(nCols);
+ nRowCount = static_cast<SCSIZE>(nRows);
+}
+
+void ScConsData::GetSize( SCCOL& rCols, SCROW& rRows ) const
+{
+ rCols = static_cast<SCCOL>(nColCount);
+ rRows = static_cast<SCROW>(nRowCount);
+}
+
+void ScConsData::SetFlags( ScSubTotalFunc eFunc, bool bColName, bool bRowName, bool bRef )
+{
+ DeleteData();
+ bReference = bRef;
+ bColByName = bColName;
+ if (bColName) nColCount = 0;
+ bRowByName = bRowName;
+ if (bRowName) nRowCount = 0;
+ eFunction = eFunc;
+}
+
+void ScConsData::AddFields( const ScDocument* pSrcDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ ++nDataCount;
+
+ OUString aTitle;
+
+ SCCOL nStartCol = nCol1;
+ SCROW nStartRow = nRow1;
+ if (bColByName) ++nStartRow;
+ if (bRowByName) ++nStartCol;
+
+ if (bColByName)
+ {
+ for (SCCOL nCol=nStartCol; nCol<=nCol2; nCol++)
+ {
+ aTitle = pSrcDoc->GetString(nCol, nRow1, nTab);
+ if (!aTitle.isEmpty())
+ {
+ bool bFound = false;
+ for (SCSIZE i=0; i<nColCount && !bFound; i++)
+ if ( maColHeaders[i] == aTitle )
+ bFound = true;
+ if (!bFound)
+ lcl_AddString( maColHeaders, nColCount, aTitle );
+ }
+ }
+ }
+
+ if (!bRowByName)
+ return;
+
+ for (SCROW nRow=nStartRow; nRow<=nRow2; nRow++)
+ {
+ aTitle = pSrcDoc->GetString(nCol1, nRow, nTab);
+ if (!aTitle.isEmpty())
+ {
+ bool bFound = false;
+ for (SCSIZE i=0; i<nRowCount && !bFound; i++)
+ if ( maRowHeaders[i] == aTitle )
+ bFound = true;
+ if (!bFound)
+ lcl_AddString( maRowHeaders, nRowCount, aTitle );
+ }
+ }
+}
+
+void ScConsData::AddName( const OUString& rName )
+{
+ SCSIZE nArrX;
+ SCSIZE nArrY;
+
+ if (!bReference)
+ return;
+
+ maTitles.push_back( rName);
+ size_t nTitleCount = maTitles.size();
+
+ for (nArrY=0; nArrY<nRowCount; nArrY++)
+ {
+ // set all data to same length
+
+ SCSIZE nMax = 0;
+ for (nArrX=0; nArrX<nColCount; nArrX++)
+ nMax = std::max( nMax, ppRefs[nArrX][nArrY].size() );
+
+ for (nArrX=0; nArrX<nColCount; nArrX++)
+ {
+ ppUsed[nArrX][nArrY] = true;
+ ppRefs[nArrX][nArrY].resize( nMax, { SC_CONS_NOTFOUND, SC_CONS_NOTFOUND, SC_CONS_NOTFOUND });
+ }
+
+ // store positions
+
+ if (ppTitlePos)
+ if (nTitleCount < nDataCount)
+ ppTitlePos[nArrY][nTitleCount] = nMax;
+ }
+}
+
+void ScConsData::AddData( ScDocument* pSrcDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ PutInOrder(nCol1,nCol2);
+ PutInOrder(nRow1,nRow2);
+ if ( nCol2 >= sal::static_int_cast<SCCOL>(nCol1 + nColCount) && !bColByName )
+ {
+ OSL_FAIL("range too big");
+ nCol2 = sal::static_int_cast<SCCOL>( nCol1 + nColCount - 1 );
+ }
+ if ( nRow2 >= sal::static_int_cast<SCROW>(nRow1 + nRowCount) && !bRowByName )
+ {
+ OSL_FAIL("range too big");
+ nRow2 = sal::static_int_cast<SCROW>( nRow1 + nRowCount - 1 );
+ }
+
+ SCCOL nCol;
+ SCROW nRow;
+
+ // left top corner
+
+ if ( bColByName && bRowByName )
+ {
+ OUString aThisCorner = pSrcDoc->GetString(nCol1, nRow1, nTab);
+ if (bCornerUsed)
+ {
+ if (aCornerText != aThisCorner)
+ aCornerText.clear();
+ }
+ else
+ {
+ aCornerText = aThisCorner;
+ bCornerUsed = true;
+ }
+ }
+
+ // search title
+
+ SCCOL nStartCol = nCol1;
+ SCROW nStartRow = nRow1;
+ if (bColByName) ++nStartRow;
+ if (bRowByName) ++nStartCol;
+ OUString aTitle;
+ std::unique_ptr<SCCOL[]> pDestCols;
+ std::unique_ptr<SCROW[]> pDestRows;
+ if (bColByName)
+ {
+ pDestCols.reset(new SCCOL[nCol2-nStartCol+1]);
+ for (nCol=nStartCol; nCol<=nCol2; nCol++)
+ {
+ aTitle = pSrcDoc->GetString(nCol, nRow1, nTab);
+ SCCOL nPos = SC_CONS_NOTFOUND;
+ if (!aTitle.isEmpty())
+ {
+ bool bFound = false;
+ for (SCSIZE i=0; i<nColCount && !bFound; i++)
+ if ( maColHeaders[i] == aTitle )
+ {
+ nPos = static_cast<SCCOL>(i);
+ bFound = true;
+ }
+ OSL_ENSURE(bFound, "column not found");
+ }
+ pDestCols[nCol-nStartCol] = nPos;
+ }
+ }
+ if (bRowByName)
+ {
+ pDestRows.reset(new SCROW[nRow2-nStartRow+1]);
+ for (nRow=nStartRow; nRow<=nRow2; nRow++)
+ {
+ aTitle = pSrcDoc->GetString(nCol1, nRow, nTab);
+ SCROW nPos = SC_CONS_NOTFOUND;
+ if (!aTitle.isEmpty())
+ {
+ bool bFound = false;
+ for (SCSIZE i=0; i<nRowCount && !bFound; i++)
+ if ( maRowHeaders[i] == aTitle )
+ {
+ nPos = static_cast<SCROW>(i);
+ bFound = true;
+ }
+ OSL_ENSURE(bFound, "row not found");
+ }
+ pDestRows[nRow-nStartRow] = nPos;
+ }
+ }
+ nCol1 = nStartCol;
+ nRow1 = nStartRow;
+
+ // data
+
+ bool bAnyCell = ( eFunction == SUBTOTAL_FUNC_CNT2 );
+ for (nCol=nCol1; nCol<=nCol2; nCol++)
+ {
+ SCCOL nArrX = nCol-nCol1;
+ if (bColByName) nArrX = pDestCols[nArrX];
+ if (nArrX != SC_CONS_NOTFOUND)
+ {
+ for (nRow=nRow1; nRow<=nRow2; nRow++)
+ {
+ SCROW nArrY = nRow-nRow1;
+ if (bRowByName) nArrY = pDestRows[nArrY];
+ if ( nArrY != SC_CONS_NOTFOUND && (
+ bAnyCell ? pSrcDoc->HasData( nCol, nRow, nTab )
+ : pSrcDoc->HasValueData( nCol, nRow, nTab ) ) )
+ {
+ if (bReference)
+ {
+ ppUsed[nArrX][nArrY] = true;
+ ppRefs[nArrX][nArrY].push_back( { nCol, nRow, nTab } );
+ }
+ else
+ {
+ double nVal = pSrcDoc->GetValue( nCol, nRow, nTab );
+ if (!ppUsed[nArrX][nArrY])
+ {
+ ppUsed[nArrX][nArrY] = true;
+ ppFunctionData[nArrX][nArrY] = ScFunctionData( eFunction);
+ }
+ ppFunctionData[nArrX][nArrY].update( nVal);
+ }
+ }
+ }
+ }
+ }
+}
+
+// check before, how many rows to insert (for Undo)
+
+SCROW ScConsData::GetInsertCount() const
+{
+ SCROW nInsert = 0;
+ SCSIZE nArrX;
+ SCSIZE nArrY;
+ if ( ppRefs && ppUsed )
+ {
+ for (nArrY=0; nArrY<nRowCount; nArrY++)
+ {
+ SCSIZE nNeeded = 0;
+ for (nArrX=0; nArrX<nColCount; nArrX++)
+ nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() );
+
+ nInsert += nNeeded;
+ }
+ }
+ return nInsert;
+}
+
+// store completed data to document
+//TODO: optimize on columns?
+
+void ScConsData::OutputToDocument( ScDocument& rDestDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ OpCode eOpCode = eOpCodeTable[eFunction];
+
+ SCSIZE nArrX;
+ SCSIZE nArrY;
+
+ // left top corner
+
+ if ( bColByName && bRowByName && !aCornerText.isEmpty() )
+ rDestDoc.SetString( nCol, nRow, nTab, aCornerText );
+
+ // title
+
+ SCCOL nStartCol = nCol;
+ SCROW nStartRow = nRow;
+ if (bColByName) ++nStartRow;
+ if (bRowByName) ++nStartCol;
+
+ if (bColByName)
+ for (SCSIZE i=0; i<nColCount; i++)
+ rDestDoc.SetString( sal::static_int_cast<SCCOL>(nStartCol+i), nRow, nTab, maColHeaders[i] );
+ if (bRowByName)
+ for (SCSIZE j=0; j<nRowCount; j++)
+ rDestDoc.SetString( nCol, sal::static_int_cast<SCROW>(nStartRow+j), nTab, maRowHeaders[j] );
+
+ nCol = nStartCol;
+ nRow = nStartRow;
+
+ // data
+
+ if ( ppFunctionData && ppUsed ) // insert values directly
+ {
+ for (nArrX=0; nArrX<nColCount; nArrX++)
+ for (nArrY=0; nArrY<nRowCount; nArrY++)
+ if (ppUsed[nArrX][nArrY])
+ {
+ double fVal = ppFunctionData[nArrX][nArrY].getResult();
+ if (ppFunctionData[nArrX][nArrY].getError())
+ rDestDoc.SetError( sal::static_int_cast<SCCOL>(nCol+nArrX),
+ sal::static_int_cast<SCROW>(nRow+nArrY), nTab, FormulaError::NoValue );
+ else
+ rDestDoc.SetValue( sal::static_int_cast<SCCOL>(nCol+nArrX),
+ sal::static_int_cast<SCROW>(nRow+nArrY), nTab, fVal );
+ }
+ }
+
+ if ( !(ppRefs && ppUsed) ) // insert Reference
+ return;
+
+ //TODO: differentiate, if split into categories
+ OUString aString;
+
+ ScSingleRefData aSRef; // data for Reference formula cells
+ aSRef.InitFlags(); // this reference is absolute at all times
+ aSRef.SetFlag3D(true);
+
+ ScComplexRefData aCRef; // data for Sum cells
+ aCRef.InitFlags();
+ aCRef.Ref1.SetColRel(true); aCRef.Ref1.SetRowRel(true); aCRef.Ref1.SetTabRel(true);
+ aCRef.Ref2.SetColRel(true); aCRef.Ref2.SetRowRel(true); aCRef.Ref2.SetTabRel(true);
+
+ for (nArrY=0; nArrY<nRowCount; nArrY++)
+ {
+ SCSIZE nNeeded = 0;
+ for (nArrX=0; nArrX<nColCount; nArrX++)
+ nNeeded = std::max( nNeeded, ppRefs[nArrX][nArrY].size() );
+
+ if (nNeeded)
+ {
+ rDestDoc.InsertRow( 0,nTab, rDestDoc.MaxCol(),nTab, nRow+nArrY, nNeeded );
+
+ for (nArrX=0; nArrX<nColCount; nArrX++)
+ if (ppUsed[nArrX][nArrY])
+ {
+ SCSIZE nCount = ppRefs[nArrX][nArrY].size();
+ if (nCount)
+ {
+ for (SCSIZE nPos=0; nPos<nCount; nPos++)
+ {
+ ScReferenceEntry aRef = ppRefs[nArrX][nArrY][nPos];
+ if (aRef.nTab != SC_CONS_NOTFOUND)
+ {
+ // insert reference (absolute, 3d)
+
+ aSRef.SetAddress(rDestDoc.GetSheetLimits(), ScAddress(aRef.nCol,aRef.nRow,aRef.nTab), ScAddress());
+
+ ScTokenArray aRefArr(rDestDoc);
+ aRefArr.AddSingleReference(aSRef);
+ aRefArr.AddOpCode(ocStop);
+ ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
+ sal::static_int_cast<SCROW>(nRow+nArrY+nPos), nTab );
+ ScFormulaCell* pCell = new ScFormulaCell(rDestDoc, aDest, aRefArr);
+ rDestDoc.SetFormulaCell(aDest, pCell);
+ }
+ }
+
+ // insert sum (relative, not 3d)
+
+ ScAddress aDest( sal::static_int_cast<SCCOL>(nCol+nArrX),
+ sal::static_int_cast<SCROW>(nRow+nArrY+nNeeded), nTab );
+
+ ScRange aRange(sal::static_int_cast<SCCOL>(nCol+nArrX), nRow+nArrY, nTab);
+ aRange.aEnd.SetRow(nRow+nArrY+nNeeded-1);
+ aCRef.SetRange(rDestDoc.GetSheetLimits(), aRange, aDest);
+
+ ScTokenArray aArr(rDestDoc);
+ aArr.AddOpCode(eOpCode); // selected function
+ aArr.AddOpCode(ocOpen);
+ aArr.AddDoubleReference(aCRef);
+ aArr.AddOpCode(ocClose);
+ aArr.AddOpCode(ocStop);
+ ScFormulaCell* pCell = new ScFormulaCell(rDestDoc, aDest, aArr);
+ rDestDoc.SetFormulaCell(aDest, pCell);
+ }
+ }
+
+ // insert outline
+
+ ScOutlineArray& rOutArr = rDestDoc.GetOutlineTable( nTab, true )->GetRowArray();
+ SCROW nOutStart = nRow+nArrY;
+ SCROW nOutEnd = nRow+nArrY+nNeeded-1;
+ bool bSize = false;
+ rOutArr.Insert( nOutStart, nOutEnd, bSize );
+ for (SCROW nOutRow=nOutStart; nOutRow<=nOutEnd; nOutRow++)
+ rDestDoc.ShowRow( nOutRow, nTab, false );
+ rDestDoc.SetDrawPageSize(nTab);
+ rDestDoc.UpdateOutlineRow( nOutStart, nOutEnd, nTab, false );
+
+ // sub title
+
+ if (ppTitlePos && !maTitles.empty() && !maRowHeaders.empty())
+ {
+ for (SCSIZE nPos=0; nPos<nDataCount; nPos++)
+ {
+ SCSIZE nTPos = ppTitlePos[nArrY][nPos];
+ bool bDo = true;
+ if (nPos+1<nDataCount)
+ if (ppTitlePos[nArrY][nPos+1] == nTPos)
+ bDo = false; // empty
+ if ( bDo && nTPos < nNeeded )
+ {
+ aString = maRowHeaders[nArrY] + " / " + maTitles[nPos];
+ rDestDoc.SetString( nCol-1, nRow+nArrY+nTPos, nTab, aString );
+ }
+ }
+ }
+
+ nRow += nNeeded;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
new file mode 100644
index 0000000000..c2096b39c4
--- /dev/null
+++ b/sc/source/core/tool/dbdata.cxx
@@ -0,0 +1,1656 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <unotools/charclass.hxx>
+
+#include <dbdata.hxx>
+#include <globalnames.hxx>
+#include <refupdat.hxx>
+#include <document.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <subtotalparam.hxx>
+#include <sortparam.hxx>
+#include <dociter.hxx>
+#include <brdcst.hxx>
+
+#include <comphelper/stl_types.hxx>
+
+#include <memory>
+#include <utility>
+
+bool ScDBData::less::operator() (const std::unique_ptr<ScDBData>& left, const std::unique_ptr<ScDBData>& right) const
+{
+ return ScGlobal::GetTransliteration().compareString(left->GetUpperName(), right->GetUpperName()) < 0;
+}
+
+ScDBData::ScDBData( const OUString& rName,
+ SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ bool bByR, bool bHasH, bool bTotals) :
+ // Listeners are to be setup by the "parent" container.
+ mpSortParam(new ScSortParam),
+ mpQueryParam(new ScQueryParam),
+ mpSubTotal(new ScSubTotalParam),
+ mpImportParam(new ScImportParam),
+ mpContainer (nullptr),
+ aName (rName),
+ aUpper (rName),
+ nTable (nTab),
+ nStartCol (nCol1),
+ nStartRow (nRow1),
+ nEndCol (nCol2),
+ nEndRow (nRow2),
+ bByRow (bByR),
+ bHasHeader (bHasH),
+ bHasTotals (bTotals),
+ bDoSize (false),
+ bKeepFmt (false),
+ bStripData (false),
+ bIsAdvanced (false),
+ bDBSelection(false),
+ nIndex (0),
+ bAutoFilter (false),
+ bModified (false),
+ mbTableColumnNamesDirty(true),
+ nFilteredRowCount(SCSIZE_MAX)
+{
+ aUpper = ScGlobal::getCharClass().uppercase(aUpper);
+}
+
+ScDBData::ScDBData( const ScDBData& rData ) :
+ // Listeners are to be setup by the "parent" container.
+ SvtListener (),
+ ScRefreshTimer ( rData ),
+ mpSortParam(new ScSortParam(*rData.mpSortParam)),
+ mpQueryParam(new ScQueryParam(*rData.mpQueryParam)),
+ mpSubTotal(new ScSubTotalParam(*rData.mpSubTotal)),
+ mpImportParam(new ScImportParam(*rData.mpImportParam)),
+ mpContainer (nullptr),
+ aName (rData.aName),
+ aUpper (rData.aUpper),
+ nTable (rData.nTable),
+ nStartCol (rData.nStartCol),
+ nStartRow (rData.nStartRow),
+ nEndCol (rData.nEndCol),
+ nEndRow (rData.nEndRow),
+ bByRow (rData.bByRow),
+ bHasHeader (rData.bHasHeader),
+ bHasTotals (rData.bHasTotals),
+ bDoSize (rData.bDoSize),
+ bKeepFmt (rData.bKeepFmt),
+ bStripData (rData.bStripData),
+ bIsAdvanced (rData.bIsAdvanced),
+ aAdvSource (rData.aAdvSource),
+ bDBSelection (rData.bDBSelection),
+ nIndex (rData.nIndex),
+ bAutoFilter (rData.bAutoFilter),
+ bModified (rData.bModified),
+ maTableColumnNames (rData.maTableColumnNames),
+ maTableColumnAttributes(rData.maTableColumnAttributes),
+ mbTableColumnNamesDirty(rData.mbTableColumnNamesDirty),
+ nFilteredRowCount (rData.nFilteredRowCount)
+{
+}
+
+ScDBData::ScDBData( const OUString& rName, const ScDBData& rData ) :
+ // Listeners are to be setup by the "parent" container.
+ SvtListener (),
+ ScRefreshTimer ( rData ),
+ mpSortParam(new ScSortParam(*rData.mpSortParam)),
+ mpQueryParam(new ScQueryParam(*rData.mpQueryParam)),
+ mpSubTotal(new ScSubTotalParam(*rData.mpSubTotal)),
+ mpImportParam(new ScImportParam(*rData.mpImportParam)),
+ mpContainer (nullptr),
+ aName (rName),
+ aUpper (rName),
+ nTable (rData.nTable),
+ nStartCol (rData.nStartCol),
+ nStartRow (rData.nStartRow),
+ nEndCol (rData.nEndCol),
+ nEndRow (rData.nEndRow),
+ bByRow (rData.bByRow),
+ bHasHeader (rData.bHasHeader),
+ bHasTotals (rData.bHasTotals),
+ bDoSize (rData.bDoSize),
+ bKeepFmt (rData.bKeepFmt),
+ bStripData (rData.bStripData),
+ bIsAdvanced (rData.bIsAdvanced),
+ aAdvSource (rData.aAdvSource),
+ bDBSelection (rData.bDBSelection),
+ nIndex (rData.nIndex),
+ bAutoFilter (rData.bAutoFilter),
+ bModified (rData.bModified),
+ maTableColumnNames (rData.maTableColumnNames),
+ maTableColumnAttributes(rData.maTableColumnAttributes),
+ mbTableColumnNamesDirty (rData.mbTableColumnNamesDirty),
+ nFilteredRowCount (rData.nFilteredRowCount)
+{
+ aUpper = ScGlobal::getCharClass().uppercase(aUpper);
+}
+
+ScDBData& ScDBData::operator= (const ScDBData& rData)
+{
+ if (this != &rData)
+ {
+ // Don't modify the name. The name is not mutable as it is used as a key
+ // in the container to keep the db ranges sorted by the name.
+
+ bool bHeaderRangeDiffers = (nTable != rData.nTable || nStartCol != rData.nStartCol ||
+ nEndCol != rData.nEndCol || nStartRow != rData.nStartRow);
+ bool bNeedsListening = ((bHasHeader && bHeaderRangeDiffers) || (!bHasHeader && rData.bHasHeader));
+ if (bHasHeader && (!rData.bHasHeader || bHeaderRangeDiffers))
+ {
+ EndTableColumnNamesListener();
+ }
+ ScRefreshTimer::operator=( rData );
+ mpSortParam.reset(new ScSortParam(*rData.mpSortParam));
+ mpQueryParam.reset(new ScQueryParam(*rData.mpQueryParam));
+ mpSubTotal.reset(new ScSubTotalParam(*rData.mpSubTotal));
+ mpImportParam.reset(new ScImportParam(*rData.mpImportParam));
+ // Keep mpContainer.
+ nTable = rData.nTable;
+ nStartCol = rData.nStartCol;
+ nStartRow = rData.nStartRow;
+ nEndCol = rData.nEndCol;
+ nEndRow = rData.nEndRow;
+ bByRow = rData.bByRow;
+ bHasHeader = rData.bHasHeader;
+ bHasTotals = rData.bHasTotals;
+ bDoSize = rData.bDoSize;
+ bKeepFmt = rData.bKeepFmt;
+ bStripData = rData.bStripData;
+ bIsAdvanced = rData.bIsAdvanced;
+ aAdvSource = rData.aAdvSource;
+ bDBSelection = rData.bDBSelection;
+ nIndex = rData.nIndex;
+ bAutoFilter = rData.bAutoFilter;
+ nFilteredRowCount = rData.nFilteredRowCount;
+
+ if (bHeaderRangeDiffers)
+ InvalidateTableColumnNames( true);
+ else
+ {
+ maTableColumnNames = rData.maTableColumnNames;
+ maTableColumnAttributes = rData.maTableColumnAttributes;
+ mbTableColumnNamesDirty = rData.mbTableColumnNamesDirty;
+ }
+
+ if (bNeedsListening)
+ StartTableColumnNamesListener();
+ }
+ return *this;
+}
+
+bool ScDBData::operator== (const ScDBData& rData) const
+{
+ // Data that is not in sort or query params.
+
+ if ( nTable != rData.nTable ||
+ bDoSize != rData.bDoSize ||
+ bKeepFmt != rData.bKeepFmt ||
+ bIsAdvanced!= rData.bIsAdvanced||
+ bStripData != rData.bStripData ||
+// SAB: I think this should be here, but I don't want to break something
+// bAutoFilter!= rData.bAutoFilter||
+ ScRefreshTimer::operator!=( rData )
+ )
+ return false;
+
+ if ( bIsAdvanced && aAdvSource != rData.aAdvSource )
+ return false;
+
+ ScSortParam aSort1, aSort2;
+ GetSortParam(aSort1);
+ rData.GetSortParam(aSort2);
+ if (!(aSort1 == aSort2))
+ return false;
+
+ ScQueryParam aQuery1, aQuery2;
+ GetQueryParam(aQuery1);
+ rData.GetQueryParam(aQuery2);
+ if (!(aQuery1 == aQuery2))
+ return false;
+
+ ScSubTotalParam aSubTotal1, aSubTotal2;
+ GetSubTotalParam(aSubTotal1);
+ rData.GetSubTotalParam(aSubTotal2);
+ if (!(aSubTotal1 == aSubTotal2))
+ return false;
+
+ ScImportParam aImport1, aImport2;
+ GetImportParam(aImport1);
+ rData.GetImportParam(aImport2);
+ return aImport1 == aImport2;
+}
+
+ScDBData::~ScDBData()
+{
+ StopRefreshTimer();
+}
+
+OUString ScDBData::GetSourceString() const
+{
+ if (mpImportParam->bImport)
+ return mpImportParam->aDBName + "/" + mpImportParam->aStatement;
+ return OUString();
+}
+
+OUString ScDBData::GetOperations() const
+{
+ OUStringBuffer aBuf;
+ if (mpQueryParam->GetEntryCount())
+ {
+ const ScQueryEntry& rEntry = mpQueryParam->GetEntry(0);
+ if (rEntry.bDoQuery)
+ aBuf.append(ScResId(STR_OPERATION_FILTER));
+ }
+
+ if (mpSortParam->maKeyState[0].bDoSort)
+ {
+ if (!aBuf.isEmpty())
+ aBuf.append(", ");
+ aBuf.append(ScResId(STR_OPERATION_SORT));
+ }
+
+ if (mpSubTotal->bGroupActive[0] && !mpSubTotal->bRemoveOnly)
+ {
+ if (!aBuf.isEmpty())
+ aBuf.append(", ");
+ aBuf.append(ScResId(STR_OPERATION_SUBTOTAL));
+ }
+
+ if (aBuf.isEmpty())
+ aBuf.append(ScResId(STR_OPERATION_NONE));
+
+ return aBuf.makeStringAndClear();
+}
+
+void ScDBData::GetArea(SCTAB& rTab, SCCOL& rCol1, SCROW& rRow1, SCCOL& rCol2, SCROW& rRow2) const
+{
+ rTab = nTable;
+ rCol1 = nStartCol;
+ rRow1 = nStartRow;
+ rCol2 = nEndCol;
+ rRow2 = nEndRow;
+}
+
+void ScDBData::GetArea(ScRange& rRange) const
+{
+ SCROW nNewEndRow = nEndRow;
+ rRange = ScRange( nStartCol, nStartRow, nTable, nEndCol, nNewEndRow, nTable );
+}
+
+ScRange ScDBData::GetHeaderArea() const
+{
+ if (HasHeader())
+ return ScRange( nStartCol, nStartRow, nTable, nEndCol, nStartRow, nTable);
+ return ScRange( ScAddress::INITIALIZE_INVALID);
+}
+
+void ScDBData::SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ bool bHeaderRangeChange = (nTab != nTable || nCol1 != nStartCol || nCol2 != nEndCol || nRow1 != nStartRow);
+ if (bHeaderRangeChange)
+ EndTableColumnNamesListener();
+
+ nTable = nTab;
+ nStartCol = nCol1;
+ nStartRow = nRow1;
+ nEndCol = nCol2;
+ nEndRow = nRow2;
+
+ if (bHeaderRangeChange)
+ {
+ SAL_WARN_IF( !maTableColumnNames.empty(), "sc.core", "ScDBData::SetArea - invalidating column names/offsets");
+ // Invalidate *after* new area has been set above to add the proper
+ // header range to dirty list.
+ InvalidateTableColumnNames( true);
+ StartTableColumnNamesListener();
+ }
+}
+
+void ScDBData::MoveTo(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ SCCOL nUpdateCol)
+{
+ tools::Long nDifX = static_cast<tools::Long>(nCol1) - static_cast<tools::Long>(nStartCol);
+ tools::Long nDifY = static_cast<tools::Long>(nRow1) - static_cast<tools::Long>(nStartRow);
+
+ tools::Long nSortDif = bByRow ? nDifX : nDifY;
+ tools::Long nSortEnd = bByRow ? static_cast<tools::Long>(nCol2) : static_cast<tools::Long>(nRow2);
+
+ for (sal_uInt16 i=0; i<mpSortParam->GetSortKeyCount(); i++)
+ {
+ mpSortParam->maKeyState[i].nField += nSortDif;
+ if (mpSortParam->maKeyState[i].nField > nSortEnd)
+ {
+ mpSortParam->maKeyState[i].nField = 0;
+ mpSortParam->maKeyState[i].bDoSort = false;
+ }
+ }
+
+ SCSIZE nCount = mpQueryParam->GetEntryCount();
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ ScQueryEntry& rEntry = mpQueryParam->GetEntry(i);
+ rEntry.nField += nDifX;
+
+ // tdf#48025, tdf#141946: update the column index of the filter criteria,
+ // when the deleted/inserted columns are inside the data range
+ if (nUpdateCol != -1)
+ {
+ nUpdateCol += nDifX;
+ tools::Long nDifX2
+ = static_cast<tools::Long>(nCol2) - static_cast<tools::Long>(nEndCol);
+ if (rEntry.nField >= nUpdateCol)
+ rEntry.nField += nDifX2;
+ else if (rEntry.nField >= nUpdateCol + nDifX2)
+ rEntry.Clear();
+ }
+
+ if (rEntry.nField > nCol2)
+ {
+ rEntry.nField = 0;
+ rEntry.bDoQuery = false;
+ }
+ }
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ mpSubTotal->nField[i] = sal::static_int_cast<SCCOL>( mpSubTotal->nField[i] + nDifX );
+ if (mpSubTotal->nField[i] > nCol2)
+ {
+ mpSubTotal->nField[i] = 0;
+ mpSubTotal->bGroupActive[i] = false;
+ }
+ }
+
+ SetArea( nTab, nCol1, nRow1, nCol2, nRow2 );
+}
+
+void ScDBData::GetSortParam( ScSortParam& rSortParam ) const
+{
+ rSortParam = *mpSortParam;
+ rSortParam.nCol1 = nStartCol;
+ rSortParam.nRow1 = nStartRow;
+ rSortParam.nCol2 = nEndCol;
+ rSortParam.nRow2 = nEndRow;
+ rSortParam.bByRow = bByRow;
+ rSortParam.bHasHeader = bHasHeader;
+ /* TODO: add Totals to ScSortParam? */
+}
+
+void ScDBData::SetSortParam( const ScSortParam& rSortParam )
+{
+ mpSortParam.reset(new ScSortParam(rSortParam));
+ bByRow = rSortParam.bByRow;
+}
+
+void ScDBData::UpdateFromSortParam( const ScSortParam& rSortParam )
+{
+ bHasHeader = rSortParam.bHasHeader;
+}
+
+void ScDBData::GetQueryParam( ScQueryParam& rQueryParam ) const
+{
+ rQueryParam = *mpQueryParam;
+ rQueryParam.nCol1 = nStartCol;
+ rQueryParam.nRow1 = nStartRow;
+ rQueryParam.nCol2 = nEndCol;
+ rQueryParam.nRow2 = nEndRow;
+ rQueryParam.nTab = nTable;
+ rQueryParam.bByRow = bByRow;
+ rQueryParam.bHasHeader = bHasHeader;
+ /* TODO: add Totals to ScQueryParam? */
+}
+
+void ScDBData::SetQueryParam(const ScQueryParam& rQueryParam)
+{
+ mpQueryParam.reset(new ScQueryParam(rQueryParam));
+
+ // set bIsAdvanced to false for everything that is not from the
+ // advanced filter dialog
+ bIsAdvanced = false;
+}
+
+void ScDBData::SetAdvancedQuerySource(const ScRange* pSource)
+{
+ if (pSource)
+ {
+ aAdvSource = *pSource;
+ bIsAdvanced = true;
+ }
+ else
+ bIsAdvanced = false;
+}
+
+bool ScDBData::GetAdvancedQuerySource(ScRange& rSource) const
+{
+ rSource = aAdvSource;
+ return bIsAdvanced;
+}
+
+void ScDBData::GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const
+{
+ rSubTotalParam = *mpSubTotal;
+
+ // Share the data range with the parent db data. The range in the subtotal
+ // param struct is not used.
+ rSubTotalParam.nCol1 = nStartCol;
+ rSubTotalParam.nRow1 = nStartRow;
+ rSubTotalParam.nCol2 = nEndCol;
+ rSubTotalParam.nRow2 = nEndRow;
+}
+
+void ScDBData::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam)
+{
+ mpSubTotal.reset(new ScSubTotalParam(rSubTotalParam));
+}
+
+void ScDBData::GetImportParam(ScImportParam& rImportParam) const
+{
+ rImportParam = *mpImportParam;
+ // set the range.
+ rImportParam.nCol1 = nStartCol;
+ rImportParam.nRow1 = nStartRow;
+ rImportParam.nCol2 = nEndCol;
+ rImportParam.nRow2 = nEndRow;
+}
+
+void ScDBData::SetImportParam(const ScImportParam& rImportParam)
+{
+ // the range is ignored.
+ mpImportParam.reset(new ScImportParam(rImportParam));
+}
+
+bool ScDBData::IsDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
+{
+ if (nTab == nTable)
+ {
+ switch (ePortion)
+ {
+ case ScDBDataPortion::TOP_LEFT:
+ return nCol == nStartCol && nRow == nStartRow;
+ case ScDBDataPortion::AREA:
+ return nCol >= nStartCol && nCol <= nEndCol && nRow >= nStartRow && nRow <= nEndRow;
+ }
+ }
+
+ return false;
+}
+
+bool ScDBData::IsDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
+{
+ return (nTab == nTable)
+ && (nCol1 == nStartCol) && (nRow1 == nStartRow)
+ && (nCol2 == nEndCol) && (nRow2 == nEndRow);
+}
+
+bool ScDBData::HasImportParam() const
+{
+ return mpImportParam && mpImportParam->bImport;
+}
+
+bool ScDBData::HasQueryParam() const
+{
+ if (!mpQueryParam)
+ return false;
+
+ if (!mpQueryParam->GetEntryCount())
+ return false;
+
+ return mpQueryParam->GetEntry(0).bDoQuery;
+}
+
+bool ScDBData::HasSortParam() const
+{
+ return mpSortParam &&
+ !mpSortParam->maKeyState.empty() &&
+ mpSortParam->maKeyState[0].bDoSort;
+}
+
+bool ScDBData::HasSubTotalParam() const
+{
+ return mpSubTotal && mpSubTotal->bGroupActive[0];
+}
+
+void ScDBData::UpdateMoveTab(SCTAB nOldPos, SCTAB nNewPos)
+{
+ ScRange aRange;
+ GetArea(aRange);
+ SCTAB nTab = aRange.aStart.Tab(); // a database range is only on one sheet
+
+ // customize as the current table as ScTablesHint (tabvwsh5.cxx)
+
+ if (nTab == nOldPos) // moved sheet
+ nTab = nNewPos;
+ else if (nOldPos < nNewPos) // moved to the back
+ {
+ if (nTab > nOldPos && nTab <= nNewPos) // move this sheet
+ --nTab;
+ }
+ else // moved to the front
+ {
+ if (nTab >= nNewPos && nTab < nOldPos) // move this sheet
+ ++nTab;
+ }
+
+ bool bChanged = (nTab != aRange.aStart.Tab());
+ if (bChanged)
+ {
+ // SetArea() invalidates column names, but it is the same column range
+ // just on a different sheet; remember and set new.
+ ::std::vector<OUString> aNames(maTableColumnNames);
+ bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
+ // Same column range.
+ SetArea(nTab, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
+ aRange.aEnd.Row());
+ // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty.
+ maTableColumnNames = aNames;
+ maTableColumnAttributes.resize(aNames.size());
+ mbTableColumnNamesDirty = bTableColumnNamesDirty;
+ }
+
+ // MoveTo() is not necessary if only the sheet changed.
+
+ SetModified(bChanged);
+}
+
+bool ScDBData::UpdateReference(const ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ SCCOL nDx, SCROW nDy, SCTAB nDz)
+{
+ SCCOL theCol1;
+ SCROW theRow1;
+ SCTAB theTab1;
+ SCCOL theCol2;
+ SCROW theRow2;
+ SCTAB theTab2;
+ GetArea( theTab1, theCol1, theRow1, theCol2, theRow2 );
+ theTab2 = theTab1;
+ SCCOL nOldCol1 = theCol1, nOldCol2 = theCol2;
+
+ ScRefUpdateRes eRet
+ = ScRefUpdate::Update(pDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx,
+ nDy, nDz, theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
+
+ bool bDoUpdate = eRet != UR_NOTHING;
+
+ if (bDoUpdate && eRet != UR_INVALID)
+ {
+ // MoveTo() invalidates column names via SetArea(); adjust, remember and set new.
+ AdjustTableColumnAttributes( eUpdateRefMode, nDx, nCol1, nOldCol1, nOldCol2, theCol1, theCol2);
+ ::std::vector<OUString> aNames( maTableColumnNames);
+ bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
+ // tdf#48025, tdf#141946: update the column index of the filter criteria,
+ // when the deleted/inserted columns are inside the data range
+ if (HasAutoFilter() && theCol1 - nOldCol1 != theCol2 - nOldCol2)
+ MoveTo(theTab1, theCol1, theRow1, theCol2, theRow2, nCol1);
+ else
+ MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
+ // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty.
+ maTableColumnNames = aNames;
+ maTableColumnAttributes.resize(aNames.size());
+ mbTableColumnNamesDirty = bTableColumnNamesDirty;
+ }
+
+ ScRange aRangeAdvSource;
+ if ( GetAdvancedQuerySource(aRangeAdvSource) )
+ {
+ aRangeAdvSource.GetVars( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
+ theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) )
+ {
+ aRangeAdvSource.aStart.Set( theCol1,theRow1,theTab1 );
+ aRangeAdvSource.aEnd.Set( theCol2,theRow2,theTab2 );
+ SetAdvancedQuerySource( &aRangeAdvSource );
+
+ bDoUpdate = true; // DBData is modified
+ }
+ }
+
+ SetModified(bDoUpdate);
+
+ return eRet == UR_INVALID;
+
+ //TODO: check if something was deleted/inserted with-in the range !!!
+}
+
+void ScDBData::ExtendDataArea(const ScDocument& rDoc)
+{
+ // Extend the DB area to include data rows immediately below.
+ SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol;
+ SCROW nOldEndRow = nEndRow;
+ rDoc.GetDataArea(nTable, nStartCol, nStartRow, nEndCol, nEndRow, false, true);
+ // nOldEndRow==rDoc.MaxRow() may easily happen when selecting whole columns and
+ // setting an AutoFilter (i.e. creating an anonymous database-range). We
+ // certainly don't want to iterate over nearly a million empty cells, but
+ // keep only an intentionally user selected range.
+ if (nOldEndRow < rDoc.MaxRow() && nEndRow < nOldEndRow)
+ nEndRow = nOldEndRow;
+ if (nStartCol != nOldCol1 || nEndCol != nOldCol2)
+ {
+ SAL_WARN_IF( !maTableColumnNames.empty(), "sc.core", "ScDBData::ExtendDataArea - invalidating column names/offsets");
+ InvalidateTableColumnNames( true);
+ }
+}
+
+void ScDBData::ExtendBackColorArea(const ScDocument& rDoc)
+{
+ // Extend the DB area to include data rows immediately below.
+ SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol;
+ SCROW nOldEndRow = nEndRow;
+ rDoc.GetBackColorArea(nTable, nStartCol, nStartRow, nEndCol, nEndRow);
+
+ if (nOldEndRow < rDoc.MaxRow() && nEndRow < nOldEndRow)
+ nEndRow = nOldEndRow;
+
+ if (nStartCol != nOldCol1 || nEndCol != nOldCol2)
+ {
+ SAL_WARN_IF( !maTableColumnNames.empty(), "sc.core", "ScDBData::ExtendBackColorArea - invalidating column names/offsets");
+ InvalidateTableColumnNames( true);
+ }
+}
+
+void ScDBData::StartTableColumnNamesListener()
+{
+ if (mpContainer && bHasHeader)
+ {
+ ScDocument& rDoc = mpContainer->GetDocument();
+ if (!rDoc.IsClipOrUndo())
+ rDoc.StartListeningArea( GetHeaderArea(), false, this);
+ }
+}
+
+void ScDBData::EndTableColumnNamesListener()
+{
+ EndListeningAll();
+}
+
+void ScDBData::SetTableColumnNames( ::std::vector< OUString >&& rNames )
+{
+ maTableColumnNames = std::move(rNames);
+ mbTableColumnNamesDirty = false;
+}
+
+void ScDBData::SetTableColumnAttributes( ::std::vector< TableColumnAttributes >&& rAttributes )
+{
+ maTableColumnAttributes = std::move(rAttributes);
+}
+
+void ScDBData::AdjustTableColumnAttributes( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1,
+ SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 )
+{
+ if (maTableColumnNames.empty())
+ return;
+
+ SCCOL nDiff1 = nNewCol1 - nOldCol1;
+ SCCOL nDiff2 = nNewCol2 - nOldCol2;
+ if (nDiff1 == nDiff2)
+ return; // not moved or entirely moved, nothing to do
+
+ ::std::vector<OUString> aNewNames;
+ ::std::vector<TableColumnAttributes> aNewAttributes;
+ if (eUpdateRefMode == URM_INSDEL)
+ {
+ if (nDx > 0)
+ mbTableColumnNamesDirty = true; // inserted columns will have empty names
+
+ // nCol1 is the first column of the block that gets shifted, determine
+ // the head and tail elements that are to be copied for deletion or
+ // insertion.
+ size_t nHead = static_cast<size_t>(::std::max( nCol1 + std::min<SCCOL>(nDx, 0) - nOldCol1, 0));
+ size_t nTail = static_cast<size_t>(::std::max( nOldCol2 - nCol1 + 1, 0));
+ size_t n = nHead + nTail;
+ if (0 < n && n <= maTableColumnNames.size())
+ {
+ if (nDx > 0)
+ n += nDx;
+ aNewNames.resize(n);
+ aNewAttributes.resize(n);
+ // Copy head.
+ for (size_t i = 0; i < nHead; ++i)
+ {
+ aNewNames[i] = maTableColumnNames[i];
+ aNewAttributes[i] = maTableColumnAttributes[i];
+ }
+ // Copy tail, inserted middle range, if any, stays empty.
+ for (size_t i = n - nTail, j = maTableColumnNames.size() - nTail; i < n; ++i, ++j)
+ {
+ aNewNames[i] = maTableColumnNames[j];
+ aNewAttributes[i] = maTableColumnAttributes[j];
+ }
+ }
+ } // else empty aNewNames invalidates names/offsets
+
+ SAL_WARN_IF( !maTableColumnNames.empty() && aNewNames.empty(),
+ "sc.core", "ScDBData::AdjustTableColumnAttributes - invalidating column attributes/offsets");
+ aNewNames.swap( maTableColumnNames);
+ aNewAttributes.swap(maTableColumnAttributes);
+ if (maTableColumnNames.empty())
+ mbTableColumnNamesDirty = true;
+ if (mbTableColumnNamesDirty)
+ InvalidateTableColumnNames( false); // preserve new column names array
+}
+
+void ScDBData::InvalidateTableColumnNames( bool bSwapToEmptyNames )
+{
+ mbTableColumnNamesDirty = true;
+ if (bSwapToEmptyNames && !maTableColumnNames.empty())
+ ::std::vector<OUString>().swap( maTableColumnNames);
+ if (mpContainer)
+ {
+ // Add header range to dirty list.
+ if (HasHeader())
+ mpContainer->GetDirtyTableColumnNames().Join( GetHeaderArea());
+ else
+ {
+ // We need *some* range in the dirty list even without header area,
+ // otherwise the container would not attempt to call a refresh.
+ mpContainer->GetDirtyTableColumnNames().Join( ScRange( nStartCol, nStartRow, nTable));
+ }
+ }
+}
+
+namespace {
+class TableColumnNameSearch
+{
+public:
+ explicit TableColumnNameSearch( OUString aSearchName ) :
+ maSearchName(std::move( aSearchName ))
+ {
+ }
+
+ bool operator()( const OUString& rName ) const
+ {
+ return ScGlobal::GetTransliteration().isEqual( maSearchName, rName);
+ }
+
+private:
+ OUString maSearchName;
+};
+
+/** Set a numbered table column name at given nIndex, preventing duplicates,
+ numbering starting at nCount. If nCount==0 then the first attempt is made
+ with an unnumbered name and if already present the next attempt with
+ nCount=2, so "Original" and "Original2". No check whether nIndex is valid. */
+void SetTableColumnName( ::std::vector<OUString>& rVec, size_t nIndex, const OUString& rName, size_t nCount )
+{
+ OUString aStr;
+ do
+ {
+ if (nCount)
+ aStr = rName + OUString::number( nCount);
+ else
+ {
+ aStr = rName;
+ ++nCount;
+ }
+
+ if (std::none_of( rVec.begin(), rVec.end(), TableColumnNameSearch( aStr)))
+ {
+ rVec[nIndex] = aStr;
+ break; // do while
+ }
+ ++nCount;
+ } while(true);
+}
+}
+
+void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
+{
+ ::std::vector<OUString> aNewNames;
+ aNewNames.resize( nEndCol - nStartCol + 1);
+ bool bHaveEmpty = false;
+ if (!HasHeader() || !pDoc)
+ bHaveEmpty = true; // Assume we have empty ones and fill below.
+ else
+ {
+ ScHorizontalCellIterator aIter(*pDoc, nTable, nStartCol, nStartRow, nEndCol, nStartRow); // header row only
+ ScRefCellValue* pCell;
+ SCCOL nCol, nLastColFilled = nStartCol - 1;
+ SCROW nRow;
+ while ((pCell = aIter.GetNext( nCol, nRow)) != nullptr)
+ {
+ if (pCell->hasString())
+ {
+ const OUString& rStr = pCell->getString( pDoc);
+ if (rStr.isEmpty())
+ bHaveEmpty = true;
+ else
+ {
+ SetTableColumnName( aNewNames, nCol-nStartCol, rStr, 0);
+ if (nLastColFilled < nCol-1)
+ bHaveEmpty = true;
+ }
+ nLastColFilled = nCol;
+ }
+ else
+ bHaveEmpty = true;
+ }
+ }
+
+ // Never leave us with empty names, try to remember previous name that
+ // might had been used to compile formulas, but only if same number of
+ // columns and no duplicates.
+ if (bHaveEmpty && aNewNames.size() == maTableColumnNames.size())
+ {
+ bHaveEmpty = false;
+ for (size_t i=0, n=aNewNames.size(); i < n; ++i)
+ {
+ if (aNewNames[i].isEmpty())
+ {
+ const OUString& rStr = maTableColumnNames[i];
+ if (rStr.isEmpty())
+ bHaveEmpty = true;
+ else
+ SetTableColumnName( aNewNames, i, rStr, 0);
+ }
+ }
+ }
+
+ // If we still have empty ones then fill those with "Column#" with #
+ // starting at the column offset number. Still no duplicates of course.
+ if (bHaveEmpty)
+ {
+ OUString aColumn( ScResId(STR_COLUMN));
+ for (size_t i=0, n=aNewNames.size(); i < n; ++i)
+ {
+ if (aNewNames[i].isEmpty())
+ SetTableColumnName( aNewNames, i, aColumn, i+1);
+ }
+ }
+
+ aNewNames.swap( maTableColumnNames);
+ maTableColumnAttributes.resize(maTableColumnNames.size());
+ mbTableColumnNamesDirty = false;
+}
+
+void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange )
+{
+ // Header-less tables get names generated, completely empty a full refresh.
+ if (mbTableColumnNamesDirty && (!HasHeader() || maTableColumnNames.empty()))
+ {
+ RefreshTableColumnNames( pDoc);
+ return;
+ }
+
+ // Check if this is affected for the range requested.
+ ScRange aIntersection( GetHeaderArea().Intersection( rRange));
+ if (!aIntersection.IsValid())
+ return;
+
+ // Always fully refresh, only one cell of a range was broadcasted per area
+ // listener if multiple cells were affected. We don't know if there were
+ // more. Also, we need the full check anyway in case a duplicated name was
+ // entered.
+ RefreshTableColumnNames( pDoc);
+}
+
+sal_Int32 ScDBData::GetColumnNameOffset( const OUString& rName ) const
+{
+ if (maTableColumnNames.empty())
+ return -1;
+
+ ::std::vector<OUString>::const_iterator it(
+ ::std::find_if( maTableColumnNames.begin(), maTableColumnNames.end(), TableColumnNameSearch( rName)));
+ if (it != maTableColumnNames.end())
+ return it - maTableColumnNames.begin();
+
+ return -1;
+}
+
+OUString ScDBData::GetTableColumnName( SCCOL nCol ) const
+{
+ if (maTableColumnNames.empty())
+ return OUString();
+
+ SCCOL nOffset = nCol - nStartCol;
+ if (nOffset < 0 || maTableColumnNames.size() <= o3tl::make_unsigned(nOffset))
+ return OUString();
+
+ return maTableColumnNames[nOffset];
+}
+
+void ScDBData::Notify( const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ScDataChanged)
+ return;
+ const ScHint* pScHint = static_cast<const ScHint*>(&rHint);
+
+ mbTableColumnNamesDirty = true;
+ if (!mpContainer)
+ assert(!"ScDBData::Notify - how did we end up here without container?");
+ else
+ {
+ // Only one cell of a range is broadcasted per area listener if
+ // multiple cells are affected. Expand the range to what this is
+ // listening to. Broadcasted address outside should not happen,
+ // but... let it trigger a refresh if.
+ const ScRange aHeaderRange( GetHeaderArea());
+ ScAddress aHintAddress( pScHint->GetStartAddress());
+ if (aHeaderRange.IsValid())
+ {
+ mpContainer->GetDirtyTableColumnNames().Join( aHeaderRange);
+ // Header range is one row.
+ // The ScHint's "range" is an address with row count.
+ // Though broadcasted is usually only one cell, check for the
+ // possible case of row block and for one cell in the same row.
+ if (aHintAddress.Row() <= aHeaderRange.aStart.Row()
+ && aHeaderRange.aStart.Row() < aHintAddress.Row() + pScHint->GetRowCount())
+ {
+ aHintAddress.SetRow( aHeaderRange.aStart.Row());
+ if (!aHeaderRange.Contains( aHintAddress))
+ mpContainer->GetDirtyTableColumnNames().Join( aHintAddress);
+ }
+ }
+ else
+ {
+ // We need *some* range in the dirty list even without header area,
+ // otherwise the container would not attempt to call a refresh.
+ aHintAddress.SetRow( nStartRow);
+ mpContainer->GetDirtyTableColumnNames().Join( aHintAddress);
+ }
+ }
+
+ // Do not refresh column names here, which might trigger unwanted
+ // recalculation.
+}
+
+void ScDBData::CalcSaveFilteredCount( SCSIZE nNonFilteredRowCount )
+{
+ SCSIZE nTotal = nEndRow - nStartRow + 1;
+ if ( bHasHeader )
+ nTotal -= 1;
+ nFilteredRowCount = nTotal - nNonFilteredRowCount;
+}
+
+void ScDBData::GetFilterSelCount( SCSIZE& nSelected, SCSIZE& nTotal )
+{
+ nTotal = nEndRow - nStartRow + 1;
+ if ( bHasHeader )
+ nTotal -= 1;
+ if( nFilteredRowCount != SCSIZE_MAX )
+ nSelected = nTotal - nFilteredRowCount;
+ else
+ nSelected = nFilteredRowCount;
+}
+
+namespace {
+
+class FindByTable
+{
+ SCTAB mnTab;
+public:
+ explicit FindByTable(SCTAB nTab) : mnTab(nTab) {}
+
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ ScRange aRange;
+ p->GetArea(aRange);
+ return aRange.aStart.Tab() == mnTab;
+ }
+};
+
+class UpdateMoveTabFunc
+{
+ SCTAB mnOldTab;
+ SCTAB mnNewTab;
+public:
+ UpdateMoveTabFunc(SCTAB nOld, SCTAB nNew) : mnOldTab(nOld), mnNewTab(nNew) {}
+ void operator() (std::unique_ptr<ScDBData> const& p)
+ {
+ p->UpdateMoveTab(mnOldTab, mnNewTab);
+ }
+};
+
+OUString lcl_IncrementNumberInNamedRange(ScDBCollection::NamedDBs& namedDBs,
+ std::u16string_view rOldName)
+{
+ // Append or increment a numeric suffix and do not generate names that
+ // could result in a cell reference by ensuring at least one underscore is
+ // present.
+ // "aa" => "aa_2"
+ // "aaaa1" => "aaaa1_2"
+ // "aa_a" => "aa_a_2"
+ // "aa_a_" => "aa_a__2"
+ // "aa_a1" => "aa_a1_2"
+ // "aa_1a" => "aa_1a_2"
+ // "aa_1" => "aa_2"
+ // "aa_2" => "aa_3"
+
+ size_t nLastIndex = rOldName.rfind('_');
+ sal_Int32 nOldNumber = 1;
+ OUString aPrefix;
+ if (nLastIndex != std::u16string_view::npos)
+ {
+ ++nLastIndex;
+ std::u16string_view sLastPart(rOldName.substr(nLastIndex));
+ nOldNumber = o3tl::toInt32(sLastPart);
+
+ // If that number is exactly at the end then increment the number; else
+ // append "_" and number.
+ // toInt32() returns 0 on failure and also stops at trailing non-digit
+ // characters (toInt32("1a")==1).
+ if (OUString::number(nOldNumber) == sLastPart)
+ aPrefix = rOldName.substr(0, nLastIndex);
+ else
+ {
+ aPrefix = OUString::Concat(rOldName) + "_";
+ nOldNumber = 1;
+ }
+ }
+ else // No "_" found, append "_" and number.
+ aPrefix = OUString::Concat(rOldName) + "_";
+ OUString sNewName;
+ do
+ {
+ sNewName = aPrefix + OUString::number(++nOldNumber);
+ } while (namedDBs.findByName(sNewName) != nullptr);
+ return sNewName;
+}
+
+class FindByCursor
+{
+ SCCOL mnCol;
+ SCROW mnRow;
+ SCTAB mnTab;
+ ScDBDataPortion mePortion;
+public:
+ FindByCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) :
+ mnCol(nCol), mnRow(nRow), mnTab(nTab), mePortion(ePortion) {}
+
+ bool operator() (std::unique_ptr<ScDBData> const& p)
+ {
+ return p->IsDBAtCursor(mnCol, mnRow, mnTab, mePortion);
+ }
+};
+
+class FindByRange
+{
+ const ScRange& mrRange;
+public:
+ explicit FindByRange(const ScRange& rRange) : mrRange(rRange) {}
+
+ bool operator() (std::unique_ptr<ScDBData> const& p)
+ {
+ return p->IsDBAtArea(
+ mrRange.aStart.Tab(), mrRange.aStart.Col(), mrRange.aStart.Row(), mrRange.aEnd.Col(), mrRange.aEnd.Row());
+ }
+};
+
+class FindByIndex
+{
+ sal_uInt16 mnIndex;
+public:
+ explicit FindByIndex(sal_uInt16 nIndex) : mnIndex(nIndex) {}
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ return p->GetIndex() == mnIndex;
+ }
+};
+
+class FindByUpperName
+{
+ const OUString& mrName;
+public:
+ explicit FindByUpperName(const OUString& rName) : mrName(rName) {}
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ return p->GetUpperName() == mrName;
+ }
+};
+
+class FindByName
+{
+ const OUString& mrName;
+public:
+ explicit FindByName(const OUString& rName) : mrName(rName) {}
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ return p->GetName() == mrName;
+ }
+};
+
+class FindByPointer
+{
+ const ScDBData* mpDBData;
+public:
+ explicit FindByPointer(const ScDBData* pDBData) : mpDBData(pDBData) {}
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ return p.get() == mpDBData;
+ }
+};
+
+}
+
+ScDocument& ScDBDataContainerBase::GetDocument() const
+{
+ return mrDoc;
+}
+
+ScRangeList& ScDBDataContainerBase::GetDirtyTableColumnNames()
+{
+ return maDirtyTableColumnNames;
+}
+
+ScDBCollection::NamedDBs::NamedDBs(ScDBCollection& rParent, ScDocument& rDoc) :
+ ScDBDataContainerBase(rDoc), mrParent(rParent) {}
+
+ScDBCollection::NamedDBs::NamedDBs(const NamedDBs& r, ScDBCollection& rParent)
+ : ScDBDataContainerBase(r.mrDoc)
+ , mrParent(rParent)
+{
+ for (auto const& it : r.m_DBs)
+ {
+ ScDBData* p = new ScDBData(*it);
+ std::unique_ptr<ScDBData> pData(p);
+ if (m_DBs.insert( std::move(pData)).second)
+ initInserted(p);
+ }
+}
+
+ScDBCollection::NamedDBs::~NamedDBs()
+{
+}
+
+void ScDBCollection::NamedDBs::initInserted( ScDBData* p )
+{
+ p->SetContainer( this);
+ if (mrDoc.IsClipOrUndo())
+ return;
+
+ p->StartTableColumnNamesListener(); // needs the container be set already
+ if (!p->AreTableColumnNamesDirty())
+ return;
+
+ if (p->HasHeader())
+ {
+ // Refresh table column names in next round.
+ maDirtyTableColumnNames.Join( p->GetHeaderArea());
+ }
+ else
+ {
+ // Header-less table can generate its column names
+ // already without accessing the document.
+ p->RefreshTableColumnNames( nullptr);
+ }
+}
+
+ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::begin()
+{
+ return m_DBs.begin();
+}
+
+ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::end()
+{
+ return m_DBs.end();
+}
+
+ScDBCollection::NamedDBs::const_iterator ScDBCollection::NamedDBs::begin() const
+{
+ return m_DBs.begin();
+}
+
+ScDBCollection::NamedDBs::const_iterator ScDBCollection::NamedDBs::end() const
+{
+ return m_DBs.end();
+}
+
+ScDBData* ScDBCollection::NamedDBs::findByIndex(sal_uInt16 nIndex)
+{
+ DBsType::iterator itr = find_if(
+ m_DBs.begin(), m_DBs.end(), FindByIndex(nIndex));
+ return itr == m_DBs.end() ? nullptr : itr->get();
+}
+
+ScDBData* ScDBCollection::NamedDBs::findByUpperName(const OUString& rName)
+{
+ DBsType::iterator itr = find_if(
+ m_DBs.begin(), m_DBs.end(), FindByUpperName(rName));
+ return itr == m_DBs.end() ? nullptr : itr->get();
+}
+
+auto ScDBCollection::NamedDBs::findByUpperName2(const OUString& rName) -> iterator
+{
+ return find_if(
+ m_DBs.begin(), m_DBs.end(), FindByUpperName(rName));
+}
+
+ScDBData* ScDBCollection::NamedDBs::findByName(const OUString& rName)
+{
+ DBsType::iterator itr = find_if(m_DBs.begin(), m_DBs.end(), FindByName(rName));
+ return itr == m_DBs.end() ? nullptr : itr->get();
+}
+
+bool ScDBCollection::NamedDBs::insert(std::unique_ptr<ScDBData> pData)
+{
+ auto p = pData.get();
+ if (!pData->GetIndex())
+ pData->SetIndex(mrParent.nEntryIndex++);
+
+ std::pair<DBsType::iterator, bool> r = m_DBs.insert(std::move(pData));
+
+ if (r.second)
+ {
+ initInserted(p);
+
+ /* TODO: shouldn't the import refresh not be setup for
+ * clipboard/undo documents? It was already like this before... */
+ if (p->HasImportParam() && !p->HasImportSelection())
+ {
+ p->SetRefreshHandler(mrParent.GetRefreshHandler());
+ p->SetRefreshControl(&mrDoc.GetRefreshTimerControlAddress());
+ }
+ }
+ return r.second;
+}
+
+ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::erase(const iterator& itr)
+{
+ return m_DBs.erase(itr);
+}
+
+bool ScDBCollection::NamedDBs::empty() const
+{
+ return m_DBs.empty();
+}
+
+size_t ScDBCollection::NamedDBs::size() const
+{
+ return m_DBs.size();
+}
+
+bool ScDBCollection::NamedDBs::operator== (const NamedDBs& r) const
+{
+ return ::comphelper::ContainerUniquePtrEquals(m_DBs, r.m_DBs);
+}
+
+ScDBCollection::AnonDBs::iterator ScDBCollection::AnonDBs::begin()
+{
+ return m_DBs.begin();
+}
+
+ScDBCollection::AnonDBs::iterator ScDBCollection::AnonDBs::end()
+{
+ return m_DBs.end();
+}
+
+ScDBCollection::AnonDBs::const_iterator ScDBCollection::AnonDBs::begin() const
+{
+ return m_DBs.begin();
+}
+
+ScDBCollection::AnonDBs::const_iterator ScDBCollection::AnonDBs::end() const
+{
+ return m_DBs.end();
+}
+
+const ScDBData* ScDBCollection::AnonDBs::findAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab,
+ ScDBDataPortion ePortion) const
+{
+ DBsType::const_iterator itr = find_if(
+ m_DBs.begin(), m_DBs.end(), FindByCursor(nCol, nRow, nTab, ePortion));
+ return itr == m_DBs.end() ? nullptr : itr->get();
+}
+
+const ScDBData* ScDBCollection::AnonDBs::findByRange(const ScRange& rRange) const
+{
+ DBsType::const_iterator itr = find_if(
+ m_DBs.begin(), m_DBs.end(), FindByRange(rRange));
+ return itr == m_DBs.end() ? nullptr : itr->get();
+}
+
+void ScDBCollection::AnonDBs::deleteOnTab(SCTAB nTab)
+{
+ FindByTable func(nTab);
+ std::erase_if(m_DBs, func);
+}
+
+ScDBData* ScDBCollection::AnonDBs::getByRange(const ScRange& rRange)
+{
+ const ScDBData* pData = findByRange(rRange);
+ if (!pData)
+ {
+ // Insert a new db data. They all have identical names.
+ ::std::unique_ptr<ScDBData> pNew(new ScDBData(
+ STR_DB_GLOBAL_NONAME, rRange.aStart.Tab(), rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), true, false, false));
+ pData = pNew.get();
+ m_DBs.push_back(std::move(pNew));
+ }
+ return const_cast<ScDBData*>(pData);
+}
+
+void ScDBCollection::AnonDBs::insert(ScDBData* p)
+{
+ m_DBs.push_back(std::unique_ptr<ScDBData>(p));
+}
+
+ScDBCollection::AnonDBs::iterator ScDBCollection::AnonDBs::erase(const iterator& itr)
+{
+ return m_DBs.erase(itr);
+}
+
+bool ScDBCollection::AnonDBs::empty() const
+{
+ return m_DBs.empty();
+}
+
+bool ScDBCollection::AnonDBs::has( const ScDBData* p ) const
+{
+ return any_of(m_DBs.begin(), m_DBs.end(), FindByPointer(p));
+}
+
+bool ScDBCollection::AnonDBs::operator== (const AnonDBs& r) const
+{
+ return ::comphelper::ContainerUniquePtrEquals(m_DBs, r.m_DBs);
+}
+
+ScDBCollection::AnonDBs::AnonDBs()
+{
+}
+
+ScDBCollection::AnonDBs::AnonDBs(AnonDBs const& r)
+{
+ m_DBs.reserve(r.m_DBs.size());
+ for (auto const& it : r.m_DBs)
+ {
+ m_DBs.push_back(std::make_unique<ScDBData>(*it));
+ }
+}
+
+ScDBCollection::ScDBCollection(ScDocument& rDocument) :
+ rDoc(rDocument), nEntryIndex(1), maNamedDBs(*this, rDocument) {}
+
+ScDBCollection::ScDBCollection(const ScDBCollection& r) :
+ rDoc(r.rDoc), nEntryIndex(r.nEntryIndex), maNamedDBs(r.maNamedDBs, *this), maAnonDBs(r.maAnonDBs) {}
+
+const ScDBData* ScDBCollection::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
+{
+ // First, search the global named db ranges.
+ NamedDBs::DBsType::const_iterator itr = find_if(
+ maNamedDBs.begin(), maNamedDBs.end(), FindByCursor(nCol, nRow, nTab, ePortion));
+ if (itr != maNamedDBs.end())
+ return itr->get();
+
+ // Check for the sheet-local anonymous db range.
+ const ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
+ if (pNoNameData)
+ if (pNoNameData->IsDBAtCursor(nCol,nRow,nTab,ePortion))
+ return pNoNameData;
+
+ // Check the global anonymous db ranges.
+ const ScDBData* pData = getAnonDBs().findAtCursor(nCol, nRow, nTab, ePortion);
+ if (pData)
+ return pData;
+
+ // Do NOT check for the document global temporary anonymous db range here.
+
+ return nullptr;
+}
+
+ScDBData* ScDBCollection::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion)
+{
+ // First, search the global named db ranges.
+ NamedDBs::DBsType::iterator itr = find_if(
+ maNamedDBs.begin(), maNamedDBs.end(), FindByCursor(nCol, nRow, nTab, ePortion));
+ if (itr != maNamedDBs.end())
+ return itr->get();
+
+ // Check for the sheet-local anonymous db range.
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
+ if (pNoNameData)
+ if (pNoNameData->IsDBAtCursor(nCol,nRow,nTab,ePortion))
+ return pNoNameData;
+
+ // Check the global anonymous db ranges.
+ const ScDBData* pData = getAnonDBs().findAtCursor(nCol, nRow, nTab, ePortion);
+ if (pData)
+ return const_cast<ScDBData*>(pData);
+
+ // Do NOT check for the document global temporary anonymous db range here.
+
+ return nullptr;
+}
+
+const ScDBData* ScDBCollection::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
+{
+ // First, search the global named db ranges.
+ ScRange aRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
+ NamedDBs::DBsType::const_iterator itr = find_if(
+ maNamedDBs.begin(), maNamedDBs.end(), FindByRange(aRange));
+ if (itr != maNamedDBs.end())
+ return itr->get();
+
+ // Check for the sheet-local anonymous db range.
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
+ if (pNoNameData)
+ if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
+ return pNoNameData;
+
+ // Lastly, check the global anonymous db ranges.
+ const ScDBData* pData = maAnonDBs.findByRange(aRange);
+ if (pData)
+ return pData;
+
+ // As a last resort, check for the document global temporary anonymous db range.
+ pNoNameData = rDoc.GetAnonymousDBData();
+ if (pNoNameData)
+ if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
+ return pNoNameData;
+
+ return nullptr;
+}
+
+ScDBData* ScDBCollection::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ // First, search the global named db ranges.
+ ScRange aRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
+ NamedDBs::DBsType::iterator itr = find_if(
+ maNamedDBs.begin(), maNamedDBs.end(), FindByRange(aRange));
+ if (itr != maNamedDBs.end())
+ return itr->get();
+
+ // Check for the sheet-local anonymous db range.
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
+ if (pNoNameData)
+ if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
+ return pNoNameData;
+
+ // Lastly, check the global anonymous db ranges.
+ const ScDBData* pData = getAnonDBs().findByRange(aRange);
+ if (pData)
+ return const_cast<ScDBData*>(pData);
+
+ // As a last resort, check for the document global temporary anonymous db range.
+ pNoNameData = rDoc.GetAnonymousDBData();
+ if (pNoNameData)
+ if (pNoNameData->IsDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2))
+ return pNoNameData;
+
+ return nullptr;
+}
+
+void ScDBCollection::RefreshDirtyTableColumnNames()
+{
+ for (size_t i=0; i < maNamedDBs.maDirtyTableColumnNames.size(); ++i)
+ {
+ const ScRange & rRange = maNamedDBs.maDirtyTableColumnNames[i];
+ for (auto const& it : maNamedDBs)
+ {
+ if (it->AreTableColumnNamesDirty())
+ it->RefreshTableColumnNames( &maNamedDBs.mrDoc, rRange);
+ }
+ }
+ maNamedDBs.maDirtyTableColumnNames.RemoveAll();
+}
+
+void ScDBCollection::DeleteOnTab( SCTAB nTab )
+{
+ FindByTable func(nTab);
+ // First, collect the positions of all items that need to be deleted.
+ ::std::vector<NamedDBs::DBsType::iterator> v;
+ {
+ NamedDBs::DBsType::iterator itr = maNamedDBs.begin(), itrEnd = maNamedDBs.end();
+ for (; itr != itrEnd; ++itr)
+ {
+ if (func(*itr))
+ v.push_back(itr);
+ }
+ }
+
+ // Delete them all.
+ for (const auto& rIter : v)
+ maNamedDBs.erase(rIter);
+
+ maAnonDBs.deleteOnTab(nTab);
+}
+
+void ScDBCollection::UpdateReference(UpdateRefMode eUpdateRefMode,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ ScDBData* pData = rDoc.GetAnonymousDBData(nTab1);
+ if (pData)
+ {
+ if (nTab1 == nTab2 && nDz == 0)
+ {
+ // Delete the database range, if some part of the reference became invalid.
+ if (pData->UpdateReference(&rDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2,
+ nTab2, nDx, nDy, nDz))
+ rDoc.SetAnonymousDBData(nTab1, nullptr);
+ }
+ else
+ {
+ //this will perhaps break undo
+ }
+ }
+
+ for (auto it = maNamedDBs.begin(); it != maNamedDBs.end(); )
+ {
+ // Delete the database range, if some part of the reference became invalid.
+ if (it->get()->UpdateReference(&rDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2,
+ nTab2, nDx, nDy, nDz))
+ it = maNamedDBs.erase(it);
+ else
+ ++it;
+ }
+ for (auto it = maAnonDBs.begin(); it != maAnonDBs.end(); )
+ {
+ // Delete the database range, if some part of the reference became invalid.
+ if (it->get()->UpdateReference(&rDoc, eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2,
+ nTab2, nDx, nDy, nDz))
+ it = maAnonDBs.erase(it);
+ else
+ ++it;
+ }
+}
+
+void ScDBCollection::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos )
+{
+ UpdateMoveTabFunc func(nOldPos, nNewPos);
+ for_each(maNamedDBs.begin(), maNamedDBs.end(), func);
+ for_each(maAnonDBs.begin(), maAnonDBs.end(), func);
+}
+
+void ScDBCollection::CopyToTable(SCTAB nOldPos, SCTAB nNewPos)
+{
+ // Create temporary copy of pointers to not insert in a set we are
+ // iterating over.
+ std::vector<const ScDBData*> aTemp;
+ aTemp.reserve( maNamedDBs.size());
+ for (const auto& rxNamedDB : maNamedDBs)
+ {
+ if (rxNamedDB->GetTab() != nOldPos)
+ continue;
+ aTemp.emplace_back( rxNamedDB.get());
+ }
+ for (const auto& rxNamedDB : aTemp)
+ {
+ const OUString newName( lcl_IncrementNumberInNamedRange( maNamedDBs, rxNamedDB->GetName()));
+ std::unique_ptr<ScDBData> pDataCopy = std::make_unique<ScDBData>(newName, *rxNamedDB);
+ pDataCopy->UpdateMoveTab(nOldPos, nNewPos);
+ pDataCopy->SetIndex(0);
+ maNamedDBs.insert(std::move(pDataCopy));
+ }
+}
+
+ScDBData* ScDBCollection::GetDBNearCursor(SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScDBData* pNearData = nullptr;
+ for (const auto& rxNamedDB : maNamedDBs)
+ {
+ SCTAB nAreaTab;
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ rxNamedDB->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ if ( nTab == nAreaTab && nCol+1 >= nStartCol && nCol <= nEndCol+1 &&
+ nRow+1 >= nStartRow && nRow <= nEndRow+1 )
+ {
+ if ( nCol < nStartCol || nCol > nEndCol || nRow < nStartRow || nRow > nEndRow )
+ {
+ if (!pNearData)
+ pNearData = rxNamedDB.get(); // remember first adjacent area
+ }
+ else
+ return rxNamedDB.get(); // not "unbenannt"/"unnamed" and cursor within
+ }
+ }
+ if (pNearData)
+ return pNearData; // adjacent, if no direct hit
+ return rDoc.GetAnonymousDBData(nTab); // "unbenannt"/"unnamed" only if nothing else
+}
+
+std::vector<ScDBData*> ScDBCollection::GetAllDBsFromTab(SCTAB nTab)
+{
+ std::vector<ScDBData*> pTabData;
+ for (const auto& rxNamedDB : maNamedDBs)
+ {
+ if (rxNamedDB->GetTab() == nTab)
+ pTabData.emplace_back(rxNamedDB.get());
+ }
+ auto pAnonDBData = rDoc.GetAnonymousDBData(nTab);
+ if (pAnonDBData)
+ pTabData.emplace_back(pAnonDBData);
+ return pTabData;
+}
+
+bool ScDBCollection::empty() const
+{
+ return maNamedDBs.empty() && maAnonDBs.empty();
+}
+
+bool ScDBCollection::operator== (const ScDBCollection& r) const
+{
+ return maNamedDBs == r.maNamedDBs && maAnonDBs == r.maAnonDBs &&
+ nEntryIndex == r.nEntryIndex && &rDoc == &r.rDoc && aRefreshHandler == r.aRefreshHandler;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/ddelink.cxx b/sc/source/core/tool/ddelink.cxx
new file mode 100644
index 0000000000..5000e0bf87
--- /dev/null
+++ b/sc/source/core/tool/ddelink.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 <comphelper/fileformat.h>
+#include <comphelper/string.hxx>
+#include <osl/thread.h>
+#include <sot/exchange.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <ddelink.hxx>
+#include <brdcst.hxx>
+#include <document.hxx>
+#include <scmatrix.hxx>
+#include <patattr.hxx>
+#include <rechead.hxx>
+#include <rangeseq.hxx>
+#include <sc.hrc>
+#include <hints.hxx>
+#include <utility>
+
+
+bool ScDdeLink::bIsInUpdate = false;
+
+ScDdeLink::ScDdeLink( ScDocument& rD, OUString aA, OUString aT, OUString aI,
+ sal_uInt8 nM ) :
+ ::sfx2::SvBaseLink(SfxLinkUpdateMode::ALWAYS,SotClipboardFormatId::STRING),
+ rDoc( rD ),
+ aAppl(std::move( aA )),
+ aTopic(std::move( aT )),
+ aItem(std::move( aI )),
+ nMode( nM ),
+ bNeedUpdate( false ),
+ pResult( nullptr )
+{
+}
+
+ScDdeLink::~ScDdeLink()
+{
+ // cancel connection
+
+ // pResult is refcounted
+}
+
+ScDdeLink::ScDdeLink( ScDocument& rD, const ScDdeLink& rOther ) :
+ ::sfx2::SvBaseLink(SfxLinkUpdateMode::ALWAYS,SotClipboardFormatId::STRING),
+ rDoc ( rD ),
+ aAppl ( rOther.aAppl ),
+ aTopic ( rOther.aTopic ),
+ aItem ( rOther.aItem ),
+ nMode ( rOther.nMode ),
+ bNeedUpdate( false ),
+ pResult ( nullptr )
+{
+ if (rOther.pResult)
+ pResult = rOther.pResult->Clone();
+}
+
+ScDdeLink::ScDdeLink( ScDocument& rD, SvStream& rStream, ScMultipleReadHeader& rHdr ) :
+ ::sfx2::SvBaseLink(SfxLinkUpdateMode::ALWAYS,SotClipboardFormatId::STRING),
+ rDoc( rD ),
+ bNeedUpdate( false ),
+ pResult( nullptr )
+{
+ rHdr.StartEntry();
+
+ rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
+ aAppl = rStream.ReadUniOrByteString( eCharSet );
+ aTopic = rStream.ReadUniOrByteString( eCharSet );
+ aItem = rStream.ReadUniOrByteString( eCharSet );
+
+ bool bHasValue;
+ rStream.ReadCharAsBool( bHasValue );
+ if ( bHasValue )
+ pResult = new ScMatrix(0, 0);
+
+ if (rHdr.BytesLeft()) // new in 388b and the 364w (RealTime Client) version
+ rStream.ReadUChar( nMode );
+ else
+ nMode = SC_DDE_DEFAULT;
+
+ rHdr.EndEntry();
+}
+
+void ScDdeLink::Store( SvStream& rStream, ScMultipleWriteHeader& rHdr ) const
+{
+ rHdr.StartEntry();
+
+ rtl_TextEncoding eCharSet = rStream.GetStreamCharSet();
+ rStream.WriteUniOrByteString( aAppl, eCharSet );
+ rStream.WriteUniOrByteString( aTopic, eCharSet );
+ rStream.WriteUniOrByteString( aItem, eCharSet );
+
+ bool bHasValue = ( pResult != nullptr );
+ rStream.WriteBool( bHasValue );
+
+ if( rStream.GetVersion() > SOFFICE_FILEFORMAT_40 ) // not with 4.0 Export
+ rStream.WriteUChar( nMode ); // since 388b
+
+ // links with Mode != SC_DDE_DEFAULT are completely omitted in 4.0 Export
+ // (from ScDocument::SaveDdeLinks)
+
+ rHdr.EndEntry();
+}
+
+sfx2::SvBaseLink::UpdateResult ScDdeLink::DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue )
+{
+ // we only master strings...
+ if ( SotClipboardFormatId::STRING != SotExchange::GetFormatIdFromMimeType( rMimeType ))
+ return SUCCESS;
+
+ OUString aLinkStr;
+ ScByteSequenceToString::GetString( aLinkStr, rValue );
+ aLinkStr = convertLineEnd(aLinkStr, LINEEND_LF);
+
+ // if string ends with line end, discard:
+
+ sal_Int32 nLen = aLinkStr.getLength();
+ if (nLen && aLinkStr[nLen-1] == '\n')
+ aLinkStr = aLinkStr.copy(0, nLen-1);
+
+ SCSIZE nCols = 1; // empty string -> an empty line
+ SCSIZE nRows = 1;
+ if (!aLinkStr.isEmpty())
+ {
+ nRows = static_cast<SCSIZE>(comphelper::string::getTokenCount(aLinkStr, '\n'));
+ std::u16string_view aLine = o3tl::getToken(aLinkStr, 0, '\n' );
+ if (!aLine.empty())
+ nCols = static_cast<SCSIZE>(comphelper::string::getTokenCount(aLine, '\t'));
+ }
+
+ if (!nRows || !nCols) // no data
+ {
+ pResult.reset();
+ }
+ else // split data
+ {
+ // always newly re-create matrix, so that bIsString doesn't get mixed up
+ pResult = new ScMatrix(nCols, nRows, 0.0);
+
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+
+ // nMode determines how the text is interpreted (#44455#/#49783#):
+ // SC_DDE_DEFAULT - number format from cell template "Standard"
+ // SC_DDE_ENGLISH - standard number format for English/US
+ // SC_DDE_TEXT - without NumberFormatter directly as string
+ sal_uLong nStdFormat = 0;
+ if ( nMode == SC_DDE_DEFAULT )
+ {
+ ScPatternAttr* pDefPattern = rDoc.GetDefPattern(); // contains standard template
+ if ( pDefPattern )
+ nStdFormat = pDefPattern->GetNumberFormat( pFormatter );
+ }
+ else if ( nMode == SC_DDE_ENGLISH )
+ nStdFormat = pFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US);
+
+ for (SCSIZE nR=0; nR<nRows; nR++)
+ {
+ std::u16string_view aLine = o3tl::getToken(aLinkStr, static_cast<sal_Int32>(nR), '\n' );
+ for (SCSIZE nC=0; nC<nCols; nC++)
+ {
+ OUString aEntry( o3tl::getToken(aLine, static_cast<sal_Int32>(nC), '\t' ) );
+ sal_uInt32 nIndex = nStdFormat;
+ double fVal = double();
+ if ( nMode != SC_DDE_TEXT && pFormatter->IsNumberFormat( aEntry, nIndex, fVal ) )
+ pResult->PutDouble( fVal, nC, nR );
+ else if (aEntry.isEmpty())
+ // empty cell
+ pResult->PutEmpty(nC, nR);
+ else
+ pResult->PutString(rPool.intern(aEntry), nC, nR);
+ }
+ }
+ }
+
+ // Something happened...
+
+ if (HasListeners())
+ {
+ Broadcast(ScHint(SfxHintId::ScDataChanged, ScAddress()));
+ rDoc.TrackFormulas(); // must happen immediately
+ rDoc.StartTrackTimer();
+
+ // StartTrackTimer asynchronously calls TrackFormulas, Broadcast(FID_DATACHANGED),
+ // ResetChanged, SetModified and Invalidate(SID_SAVEDOC/SID_DOC_MODIFIED)
+ // TrackFormulas additionally once again immediately, so that, e.g., a formula still
+ // located in the FormulaTrack doesn't get calculated by IdleCalc (#61676#)
+
+ // notify Uno objects (for XRefreshListener)
+ // must be after TrackFormulas
+ //TODO: do this asynchronously?
+ ScLinkRefreshedHint aHint;
+ aHint.SetDdeLink( aAppl, aTopic, aItem );
+ rDoc.BroadcastUno( aHint );
+ }
+
+ return SUCCESS;
+}
+
+void ScDdeLink::ListenersGone()
+{
+ bool bWas = bIsInUpdate;
+ bIsInUpdate = true; // Remove() can trigger reschedule??!?
+
+ ScDocument& rStackDoc = rDoc; // member rDoc can't be used after removing the link
+
+ sfx2::LinkManager* pLinkMgr = rDoc.GetLinkManager();
+ pLinkMgr->Remove( this); // deletes this
+
+ if ( pLinkMgr->GetLinks().empty() ) // deleted the last one ?
+ {
+ SfxBindings* pBindings = rStackDoc.GetViewBindings(); // don't use member rDoc!
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS );
+ }
+
+ bIsInUpdate = bWas;
+}
+
+const ScMatrix* ScDdeLink::GetResult() const
+{
+ return pResult.get();
+}
+
+void ScDdeLink::SetResult( const ScMatrixRef& pRes )
+{
+ pResult = pRes;
+}
+
+void ScDdeLink::TryUpdate()
+{
+ if (bIsInUpdate)
+ bNeedUpdate = true; // cannot be executed now
+ else
+ {
+ bIsInUpdate = true;
+ rDoc.IncInDdeLinkUpdate();
+ Update();
+ rDoc.DecInDdeLinkUpdate();
+ bIsInUpdate = false;
+ bNeedUpdate = false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/defaultsoptions.cxx b/sc/source/core/tool/defaultsoptions.cxx
new file mode 100644
index 0000000000..a55f154fef
--- /dev/null
+++ b/sc/source/core/tool/defaultsoptions.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/.
+ */
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/diagnose.h>
+
+#include <defaultsoptions.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <utility>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+
+ScDefaultsOptions::ScDefaultsOptions()
+{
+ SetDefaults();
+}
+
+void ScDefaultsOptions::SetDefaults()
+{
+ nInitTabCount = 1;
+ aInitTabPrefix = ScResId(STR_TABLE_DEF); // Default Prefix "Sheet"
+ bJumboSheets = false;
+}
+
+bool ScDefaultsOptions::operator==( const ScDefaultsOptions& rOpt ) const
+{
+ return rOpt.nInitTabCount == nInitTabCount
+ && rOpt.aInitTabPrefix == aInitTabPrefix
+ && rOpt.bJumboSheets == bJumboSheets;
+}
+
+ScTpDefaultsItem::ScTpDefaultsItem( ScDefaultsOptions aOpt ) :
+ SfxPoolItem ( SID_SCDEFAULTSOPTIONS ),
+ theOptions (std::move( aOpt ))
+{
+}
+
+ScTpDefaultsItem::~ScTpDefaultsItem()
+{
+}
+
+bool ScTpDefaultsItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScTpDefaultsItem& rPItem = static_cast<const ScTpDefaultsItem&>(rItem);
+ return ( theOptions == rPItem.theOptions );
+}
+
+ScTpDefaultsItem* ScTpDefaultsItem::Clone( SfxItemPool * ) const
+{
+ return new ScTpDefaultsItem( *this );
+}
+
+constexpr OUStringLiteral CFGPATH_FORMULA = u"Office.Calc/Defaults";
+
+#define SCDEFAULTSOPT_TAB_COUNT 0
+#define SCDEFAULTSOPT_TAB_PREFIX 1
+#define SCDEFAULTSOPT_JUMBO_SHEETS 2
+
+Sequence<OUString> ScDefaultsCfg::GetPropertyNames()
+{
+ return {"Sheet/SheetCount", // SCDEFAULTSOPT_TAB_COUNT
+ "Sheet/SheetPrefix", // SCDEFAULTSOPT_TAB_PREFIX
+ "Sheet/JumboSheets"}; // SCDEFAULTSOPT_JUMBO_SHEETS
+
+}
+
+ScDefaultsCfg::ScDefaultsCfg() :
+ ConfigItem( CFGPATH_FORMULA )
+{
+ OUString aPrefix;
+
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues = GetProperties(aNames);
+ const Any* pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ sal_Int32 nIntVal = 0;
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ if(pValues[nProp].hasValue())
+ {
+ switch (nProp)
+ {
+ case SCDEFAULTSOPT_TAB_COUNT:
+ if (pValues[nProp] >>= nIntVal)
+ SetInitTabCount( static_cast<SCTAB>(nIntVal) );
+ break;
+ case SCDEFAULTSOPT_TAB_PREFIX:
+ if (pValues[nProp] >>= aPrefix)
+ SetInitTabPrefix(aPrefix);
+ break;
+ case SCDEFAULTSOPT_JUMBO_SHEETS:
+#if HAVE_FEATURE_JUMBO_SHEETS
+ {
+ bool bValue;
+ if (pValues[nProp] >>= bValue)
+ SetInitJumboSheets(bValue);
+ }
+#endif
+ break;
+ }
+ }
+ }
+}
+
+void ScDefaultsCfg::ImplCommit()
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for (int nProp = 0; nProp < aNames.getLength(); ++nProp)
+ {
+ switch(nProp)
+ {
+ case SCDEFAULTSOPT_TAB_COUNT:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetInitTabCount());
+ break;
+ case SCDEFAULTSOPT_TAB_PREFIX:
+ pValues[nProp] <<= GetInitTabPrefix();
+ break;
+ case SCDEFAULTSOPT_JUMBO_SHEETS:
+ pValues[nProp] <<= GetInitJumboSheets();
+ break;
+ }
+ }
+ PutProperties(aNames, aValues);
+}
+
+void ScDefaultsCfg::SetOptions( const ScDefaultsOptions& rNew )
+{
+ *static_cast<ScDefaultsOptions*>(this) = rNew;
+ SetModified();
+}
+
+void ScDefaultsCfg::Notify( const css::uno::Sequence< OUString >& ) {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/detdata.cxx b/sc/source/core/tool/detdata.cxx
new file mode 100644
index 0000000000..8af4e0bf0c
--- /dev/null
+++ b/sc/source/core/tool/detdata.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 <algorithm>
+#include <detdata.hxx>
+#include <refupdat.hxx>
+
+ScDetOpList::ScDetOpList(const ScDetOpList& rList) :
+ bHasAddError( false ),
+ aDetOpDataVector( rList.aDetOpDataVector )
+{
+}
+
+void ScDetOpList::DeleteOnTab( SCTAB nTab )
+{
+ std::erase_if(aDetOpDataVector,
+ [&nTab](const ScDetOpData& rxDetOpData) {
+ return rxDetOpData.GetPos().Tab() == nTab; // look for operations on the deleted sheet
+ });
+}
+
+void ScDetOpList::UpdateReference( const ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
+ const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ for (auto& rxDetOpData : aDetOpDataVector )
+ {
+ ScAddress aPos = rxDetOpData.GetPos();
+ SCCOL nCol1 = aPos.Col();
+ SCROW nRow1 = aPos.Row();
+ SCTAB nTab1 = aPos.Tab();
+ SCCOL nCol2 = nCol1;
+ SCROW nRow2 = nRow1;
+ SCTAB nTab2 = nTab1;
+
+ ScRefUpdateRes eRes =
+ ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( eRes != UR_NOTHING )
+ rxDetOpData.SetPos( ScAddress( nCol1, nRow1, nTab1 ) );
+ }
+}
+
+void ScDetOpList::Append( const ScDetOpData& rDetOpData )
+{
+ if ( rDetOpData.GetOperation() == SCDETOP_ADDERROR )
+ bHasAddError = true;
+
+ aDetOpDataVector.push_back( rDetOpData );
+}
+
+bool ScDetOpList::operator==( const ScDetOpList& r ) const
+{
+ // for Ref-Undo
+
+ size_t nCount = Count();
+ bool bEqual = ( nCount == r.Count() );
+ for (size_t i=0; i<nCount && bEqual; i++) // order has to be the same
+ if ( !(aDetOpDataVector[i] == r.aDetOpDataVector[i]) ) // entries are different ?
+ bEqual = false;
+
+ return bEqual;
+}
+
+const ScDetOpData& ScDetOpList::GetObject( size_t nPos ) const
+{
+ return aDetOpDataVector[nPos];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/detfunc.cxx b/sc/source/core/tool/detfunc.cxx
new file mode 100644
index 0000000000..b5dc71b92a
--- /dev/null
+++ b/sc/source/core/tool/detfunc.cxx
@@ -0,0 +1,1653 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/eeitem.hxx>
+#include <formula/errorcodes.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlnclit.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/sdtagitm.hxx>
+#include <svx/sxcecitm.hxx>
+#include <svl/whiter.hxx>
+#include <osl/diagnose.h>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+#include <attrib.hxx>
+#include <detfunc.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <validat.hxx>
+#include <formulacell.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <undostyl.hxx>
+#include <stlpool.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <scmod.hxx>
+#include <postit.hxx>
+#include <reftokenhelper.hxx>
+#include <formulaiter.hxx>
+#include <cellvalue.hxx>
+
+#include <vector>
+#include <memory>
+
+using ::std::vector;
+using namespace com::sun::star;
+
+namespace {
+
+enum DetInsertResult { // return-values for inserting in one level
+ DET_INS_CONTINUE,
+ DET_INS_INSERTED,
+ DET_INS_EMPTY,
+ DET_INS_CIRCULAR };
+
+}
+
+class ScDetectiveData
+{
+private:
+ SfxItemSet aBoxSet;
+ SfxItemSet aArrowSet;
+ SfxItemSet aToTabSet;
+ SfxItemSet aFromTabSet;
+ SfxItemSet aCircleSet; //TODO: individually ?
+ sal_uInt16 nMaxLevel;
+
+public:
+ explicit ScDetectiveData( SdrModel* pModel );
+
+ SfxItemSet& GetBoxSet() { return aBoxSet; }
+ SfxItemSet& GetArrowSet() { return aArrowSet; }
+ SfxItemSet& GetToTabSet() { return aToTabSet; }
+ SfxItemSet& GetFromTabSet() { return aFromTabSet; }
+ SfxItemSet& GetCircleSet() { return aCircleSet; }
+
+ void SetMaxLevel( sal_uInt16 nVal ) { nMaxLevel = nVal; }
+ sal_uInt16 GetMaxLevel() const { return nMaxLevel; }
+};
+
+Color ScDetectiveFunc::nArrowColor = Color(0);
+Color ScDetectiveFunc::nErrorColor = Color(0);
+Color ScDetectiveFunc::nCommentColor = Color(0);
+bool ScDetectiveFunc::bColorsInitialized = false;
+
+static bool lcl_HasThickLine( const SdrObject& rObj )
+{
+ // thin lines get width 0 -> everything greater 0 is a thick line
+
+ return rObj.GetMergedItem(XATTR_LINEWIDTH).GetValue() > 0;
+}
+
+ScDetectiveData::ScDetectiveData( SdrModel* pModel ) :
+ aBoxSet( pModel->GetItemPool(), svl::Items<SDRATTR_START, SDRATTR_END> ),
+ aArrowSet( pModel->GetItemPool(), svl::Items<SDRATTR_START, SDRATTR_END> ),
+ aToTabSet( pModel->GetItemPool(), svl::Items<SDRATTR_START, SDRATTR_END> ),
+ aFromTabSet( pModel->GetItemPool(), svl::Items<SDRATTR_START, SDRATTR_END> ),
+ aCircleSet( pModel->GetItemPool(), svl::Items<SDRATTR_START, SDRATTR_END> ),
+ nMaxLevel(0)
+{
+
+ aBoxSet.Put( XLineColorItem( OUString(), ScDetectiveFunc::GetArrowColor() ) );
+ aBoxSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+
+ // create default line endings (like XLineEndList::Create)
+ // to be independent from the configured line endings
+
+ 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);
+
+ 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);
+
+ basegfx::B2DPolygon aCircle(basegfx::utils::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0));
+ aCircle.setClosed(true);
+
+ const OUString aName;
+
+ aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
+ aArrowSet.Put( XLineStartWidthItem( 200 ) );
+ aArrowSet.Put( XLineStartCenterItem( true ) );
+ aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
+ aArrowSet.Put( XLineEndWidthItem( 200 ) );
+ aArrowSet.Put( XLineEndCenterItem( false ) );
+
+ aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
+ aToTabSet.Put( XLineStartWidthItem( 200 ) );
+ aToTabSet.Put( XLineStartCenterItem( true ) );
+ aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
+ aToTabSet.Put( XLineEndWidthItem( 300 ) );
+ aToTabSet.Put( XLineEndCenterItem( false ) );
+
+ aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
+ aFromTabSet.Put( XLineStartWidthItem( 300 ) );
+ aFromTabSet.Put( XLineStartCenterItem( true ) );
+ aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
+ aFromTabSet.Put( XLineEndWidthItem( 200 ) );
+ aFromTabSet.Put( XLineEndCenterItem( false ) );
+
+ aCircleSet.Put( XLineColorItem( OUString(), ScDetectiveFunc::GetErrorColor() ) );
+ aCircleSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+ aCircleSet.Put( XLineWidthItem( 55 ) ); // 54 = 1 Pixel
+}
+
+void ScDetectiveFunc::Modified()
+{
+ rDoc.SetStreamValid(nTab, false);
+}
+
+static bool Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1,
+ SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 )
+{
+ return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 &&
+ nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1;
+}
+
+bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos )
+{
+ rErrPos = rRange.aStart;
+ FormulaError nError = FormulaError::NONE;
+
+ ScCellIterator aIter( rDoc, rRange);
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ nError = aIter.getFormulaCell()->GetErrCode();
+ if (nError != FormulaError::NONE)
+ rErrPos = aIter.GetPos();
+ }
+
+ return (nError != FormulaError::NONE);
+}
+
+Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const
+{
+ OSL_ENSURE( rDoc.ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
+ nCol = rDoc.SanitizeCol( nCol );
+ nRow = rDoc.SanitizeRow( nRow );
+
+ Point aPos;
+
+ switch( eMode )
+ {
+ case DrawPosMode::TopLeft:
+ break;
+ case DrawPosMode::BottomRight:
+ ++nCol;
+ ++nRow;
+ break;
+ case DrawPosMode::DetectiveArrow:
+ aPos.AdjustX(rDoc.GetColWidth( nCol, nTab ) / 4 );
+ aPos.AdjustY(rDoc.GetRowHeight( nRow, nTab ) / 2 );
+ break;
+ }
+
+ for ( SCCOL i = 0; i < nCol; ++i )
+ aPos.AdjustX(rDoc.GetColWidth( i, nTab ) );
+ aPos.AdjustY(rDoc.GetRowHeight( 0, nRow - 1, nTab ) );
+
+ aPos.setX(o3tl::convert(aPos.X(), o3tl::Length::twip, o3tl::Length::mm100));
+ aPos.setY(o3tl::convert(aPos.Y(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ if ( rDoc.IsNegativePage( nTab ) )
+ aPos.setX( aPos.X() * -1 );
+
+ return aPos;
+}
+
+tools::Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
+{
+ tools::Rectangle aRect(
+ GetDrawPos( ::std::min( nCol1, nCol2 ), ::std::min( nRow1, nRow2 ), DrawPosMode::TopLeft ),
+ GetDrawPos( ::std::max( nCol1, nCol2 ), ::std::max( nRow1, nRow2 ), DrawPosMode::BottomRight ) );
+ aRect.Normalize(); // reorder left/right in RTL sheets
+ return aRect;
+}
+
+tools::Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const
+{
+ return GetDrawRect( nCol, nRow, nCol, nRow );
+}
+
+static bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon )
+{
+ // test if rPolygon is the line end for "other table" (rectangle)
+ if(1 == rPolyPolygon.count())
+ {
+ const basegfx::B2DPolygon& aSubPoly(rPolyPolygon.getB2DPolygon(0));
+
+ // #i73305# circle consists of 4 segments, too, distinguishable from square by
+ // the use of control points
+ if(4 == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScDetectiveFunc::HasArrow( const ScAddress& rStart,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
+{
+ bool bStartAlien = ( rStart.Tab() != nTab );
+ bool bEndAlien = ( nEndTab != nTab );
+
+ if (bStartAlien && bEndAlien)
+ {
+ OSL_FAIL("bStartAlien && bEndAlien");
+ return true;
+ }
+
+ tools::Rectangle aStartRect;
+ tools::Rectangle aEndRect;
+ if (!bStartAlien)
+ aStartRect = GetDrawRect( rStart.Col(), rStart.Row() );
+ if (!bEndAlien)
+ aEndRect = GetDrawRect( nEndCol, nEndRow );
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ bool bFound = false;
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ if ( pObject->GetLayer()==SC_LAYER_INTERN &&
+ pObject->IsPolyObj() && pObject->GetPointCount()==2 )
+ {
+ const SfxItemSet& rSet = pObject->GetMergedItemSet();
+
+ bool bObjStartAlien =
+ lcl_IsOtherTab( rSet.Get(XATTR_LINESTART).GetLineStartValue() );
+ bool bObjEndAlien =
+ lcl_IsOtherTab( rSet.Get(XATTR_LINEEND).GetLineEndValue() );
+
+ bool bStartHit = bStartAlien ? bObjStartAlien :
+ ( !bObjStartAlien && aStartRect.Contains(pObject->GetPoint(0)) );
+ bool bEndHit = bEndAlien ? bObjEndAlien :
+ ( !bObjEndAlien && aEndRect.Contains(pObject->GetPoint(1)) );
+
+ if ( bStartHit && bEndHit )
+ bFound = true;
+ }
+ pObject = aIter.Next();
+ }
+
+ return bFound;
+}
+
+bool ScDetectiveFunc::IsNonAlienArrow( const SdrObject* pObject )
+{
+ if ( pObject->GetLayer()==SC_LAYER_INTERN &&
+ pObject->IsPolyObj() && pObject->GetPointCount()==2 )
+ {
+ const SfxItemSet& rSet = pObject->GetMergedItemSet();
+
+ bool bObjStartAlien =
+ lcl_IsOtherTab( rSet.Get(XATTR_LINESTART).GetLineStartValue() );
+ bool bObjEndAlien =
+ lcl_IsOtherTab( rSet.Get(XATTR_LINEEND).GetLineEndValue() );
+
+ return !bObjStartAlien && !bObjEndAlien;
+ }
+
+ return false;
+}
+
+// InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
+
+void ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow,
+ SCCOL nRefStartCol, SCROW nRefStartRow,
+ SCCOL nRefEndCol, SCROW nRefEndRow,
+ bool bFromOtherTab, bool bRed,
+ ScDetectiveData& rData )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+
+ bool bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow );
+ if (bArea && !bFromOtherTab)
+ {
+ // insert the rectangle before the arrow - this is relied on in FindFrameForObject
+
+ tools::Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow );
+ rtl::Reference<SdrRectObj> pBox = new SdrRectObj(
+ *pModel,
+ aRect);
+
+ pBox->NbcSetStyleSheet(nullptr, true);
+ pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
+
+ pBox->SetLayer( SC_LAYER_INTERN );
+ pPage->InsertObject( pBox.get() );
+ pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pBox ) );
+
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox.get(), true );
+ pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
+ pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab);
+ }
+
+ Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DrawPosMode::DetectiveArrow );
+ Point aEndPos = GetDrawPos( nCol, nRow, DrawPosMode::DetectiveArrow );
+
+ if (bFromOtherTab)
+ {
+ bool bNegativePage = rDoc.IsNegativePage( nTab );
+ tools::Long nPageSign = bNegativePage ? -1 : 1;
+
+ aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 );
+ if (aStartPos.X() * nPageSign < 0)
+ aStartPos.AdjustX(2000 * nPageSign );
+ if (aStartPos.Y() < 0)
+ aStartPos.AdjustY(2000 );
+ }
+
+ SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
+
+ if (bArea && !bFromOtherTab)
+ rAttrSet.Put( XLineWidthItem( 50 ) ); // range
+ else
+ rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
+
+ Color nColor = ( bRed ? GetErrorColor() : GetArrowColor() );
+ rAttrSet.Put( XLineColorItem( OUString(), nColor ) );
+
+ basegfx::B2DPolygon aTempPoly;
+ aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
+ aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
+ rtl::Reference<SdrPathObj> pArrow = new SdrPathObj(
+ *pModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aTempPoly));
+ pArrow->NbcSetStyleSheet(nullptr, true);
+ pArrow->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos,aEndPos)); //TODO: needed ???
+ pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
+
+ pArrow->SetLayer( SC_LAYER_INTERN );
+ pPage->InsertObject( pArrow.get() );
+ pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pArrow ) );
+
+ ScDrawObjData* pData = ScDrawLayer::GetObjData(pArrow.get(), true);
+ if (bFromOtherTab)
+ pData->maStart.SetInvalid();
+ else
+ pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
+
+ pData->maEnd.Set( nCol, nRow, nTab);
+ pData->meType = ScDrawObjData::DetectiveArrow;
+
+ Modified();
+}
+
+void ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, bool bRed,
+ ScDetectiveData& rData )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+
+ bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
+ if (bArea)
+ {
+ tools::Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
+ rtl::Reference<SdrRectObj> pBox = new SdrRectObj(
+ *pModel,
+ aRect);
+
+ pBox->NbcSetStyleSheet(nullptr, true);
+ pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
+
+ pBox->SetLayer( SC_LAYER_INTERN );
+ pPage->InsertObject( pBox.get() );
+ pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pBox ) );
+
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox.get(), true );
+ pData->maStart.Set( nStartCol, nStartRow, nTab);
+ pData->maEnd.Set( nEndCol, nEndRow, nTab);
+ }
+
+ bool bNegativePage = rDoc.IsNegativePage( nTab );
+ tools::Long nPageSign = bNegativePage ? -1 : 1;
+
+ Point aStartPos = GetDrawPos( nStartCol, nStartRow, DrawPosMode::DetectiveArrow );
+ Point aEndPos( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 );
+ if (aEndPos.Y() < 0)
+ aEndPos.AdjustY(2000 );
+
+ SfxItemSet& rAttrSet = rData.GetToTabSet();
+ if (bArea)
+ rAttrSet.Put( XLineWidthItem( 50 ) ); // range
+ else
+ rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
+
+ Color nColor = ( bRed ? GetErrorColor() : GetArrowColor() );
+ rAttrSet.Put( XLineColorItem( OUString(), nColor ) );
+
+ basegfx::B2DPolygon aTempPoly;
+ aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
+ aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
+ rtl::Reference<SdrPathObj> pArrow = new SdrPathObj(
+ *pModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aTempPoly));
+ pArrow->NbcSetStyleSheet(nullptr, true);
+ pArrow->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos,aEndPos)); //TODO: needed ???
+
+ pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
+
+ pArrow->SetLayer( SC_LAYER_INTERN );
+ pPage->InsertObject( pArrow.get() );
+ pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pArrow ) );
+
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow.get(), true );
+ pData->maStart.Set( nStartCol, nStartRow, nTab);
+ pData->maEnd.SetInvalid();
+
+ Modified();
+}
+
+// DrawEntry: formula from this spreadsheet,
+// reference on this or other
+// DrawAlienEntry: formula from other spreadsheet,
+// reference on this
+
+// return FALSE: there was already an arrow
+
+bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
+ const ScRange& rRef,
+ ScDetectiveData& rData )
+{
+ if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
+ return false;
+
+ ScAddress aErrorPos;
+ bool bError = HasError( rRef, aErrorPos );
+ bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab );
+
+ InsertArrow( nCol, nRow,
+ rRef.aStart.Col(), rRef.aStart.Row(),
+ rRef.aEnd.Col(), rRef.aEnd.Row(),
+ bAlien, bError, rData );
+ return true;
+}
+
+bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
+ ScDetectiveData& rData )
+{
+ if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
+ return false;
+
+ ScAddress aErrorPos;
+ bool bError = HasError( rRef, aErrorPos );
+
+ InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
+ rRef.aEnd.Col(), rRef.aEnd.Row(),
+ bError, rData );
+ return true;
+}
+
+void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+
+ tools::Rectangle aRect = ScDrawLayer::GetCellRect(rDoc, ScAddress(nCol, nRow, nTab), true);
+ aRect.AdjustLeft( -250 );
+ aRect.AdjustRight(250 );
+ aRect.AdjustTop( -70 );
+ aRect.AdjustBottom(70 );
+
+ rtl::Reference<SdrCircObj> pCircle = new SdrCircObj(
+ *pModel,
+ SdrCircKind::Full,
+ aRect);
+ SfxItemSet& rAttrSet = rData.GetCircleSet();
+
+ pCircle->NbcSetStyleSheet(nullptr, true);
+ pCircle->SetMergedItemSetAndBroadcast(rAttrSet);
+
+ pCircle->SetLayer( SC_LAYER_INTERN );
+ pPage->InsertObject( pCircle.get() );
+ pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pCircle ) );
+
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle.get(), true );
+ pData->maStart.Set( nCol, nRow, nTab);
+ pData->maEnd.SetInvalid();
+ pData->meType = ScDrawObjData::ValidationCircle;
+
+ Modified();
+}
+
+void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, bool bDestPnt )
+{
+ tools::Rectangle aRect = GetDrawRect( nCol, nRow );
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ pPage->RecalcObjOrdNums();
+
+ const size_t nObjCount = pPage->GetObjCount();
+ if (!nObjCount)
+ return;
+
+ size_t nDelCount = 0;
+ std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetLayer()==SC_LAYER_INTERN &&
+ pObject->IsPolyObj() && pObject->GetPointCount()==2 )
+ {
+ if (aRect.Contains(pObject->GetPoint(bDestPnt ? 1 : 0))) // start/destinationpoint
+ ppObj[nDelCount++] = pObject;
+ }
+
+ pObject = aIter.Next();
+ }
+
+ const bool bRecording = pModel->IsRecording();
+
+ if (bRecording)
+ {
+ for (size_t i=1; i<=nDelCount; ++i)
+ pModel->AddCalcUndo(std::make_unique<SdrUndoDelObj>(*ppObj[nDelCount-i]));
+ }
+
+ for (size_t i=1; i<=nDelCount; ++i)
+ {
+ // remove the object from the drawing page, delete if undo is disabled
+ pPage->RemoveObject(ppObj[nDelCount-i]->GetOrdNum());
+ }
+
+ ppObj.reset();
+
+ Modified();
+}
+
+ // delete box around reference
+
+#define SC_DET_TOLERANCE 50
+
+static bool RectIsPoints( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
+{
+ return rRect.Left() >= rStart.X() - SC_DET_TOLERANCE
+ && rRect.Left() <= rStart.X() + SC_DET_TOLERANCE
+ && rRect.Right() >= rEnd.X() - SC_DET_TOLERANCE
+ && rRect.Right() <= rEnd.X() + SC_DET_TOLERANCE
+ && rRect.Top() >= rStart.Y() - SC_DET_TOLERANCE
+ && rRect.Top() <= rStart.Y() + SC_DET_TOLERANCE
+ && rRect.Bottom() >= rEnd.Y() - SC_DET_TOLERANCE
+ && rRect.Bottom() <= rEnd.Y() + SC_DET_TOLERANCE;
+}
+
+#undef SC_DET_TOLERANCE
+
+void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ tools::Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 );
+ Point aStartCorner = aCornerRect.TopLeft();
+ Point aEndCorner = aCornerRect.BottomRight();
+ tools::Rectangle aObjRect;
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ pPage->RecalcObjOrdNums();
+
+ const size_t nObjCount = pPage->GetObjCount();
+ if (!nObjCount)
+ return;
+
+ size_t nDelCount = 0;
+ std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetLayer() == SC_LAYER_INTERN )
+ if ( auto pSdrRectObj = dynamic_cast< const SdrRectObj* >(pObject) )
+ {
+ aObjRect = pSdrRectObj->GetLogicRect();
+ aObjRect.Normalize();
+ if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
+ ppObj[nDelCount++] = pObject;
+ }
+
+ pObject = aIter.Next();
+ }
+
+ for (size_t i=1; i<=nDelCount; ++i)
+ pModel->AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) );
+
+ for (size_t i=1; i<=nDelCount; ++i)
+ pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
+
+ ppObj.reset();
+
+ Modified();
+}
+
+sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef,
+ ScDetectiveData& rData, sal_uInt16 nLevel )
+{
+ sal_uInt16 nResult = DET_INS_EMPTY;
+
+ ScCellIterator aIter( rDoc, rRef);
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ const ScAddress& rPos = aIter.GetPos();
+ switch (InsertPredLevel(rPos.Col(), rPos.Row(), rData, nLevel))
+ {
+ case DET_INS_INSERTED:
+ nResult = DET_INS_INSERTED;
+ break;
+ case DET_INS_CONTINUE:
+ if (nResult != DET_INS_INSERTED)
+ nResult = DET_INS_CONTINUE;
+ break;
+ case DET_INS_CIRCULAR:
+ if (nResult == DET_INS_EMPTY)
+ nResult = DET_INS_CIRCULAR;
+ break;
+ default:
+ ;
+ }
+ }
+
+ return nResult;
+}
+
+sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
+ sal_uInt16 nLevel )
+{
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ return DET_INS_EMPTY;
+
+ ScFormulaCell* pFCell = aCell.getFormula();
+ if (pFCell->IsRunning())
+ return DET_INS_CIRCULAR;
+
+ if (pFCell->GetDirty())
+ pFCell->Interpret(); // can't be called after SetRunning
+ pFCell->SetRunning(true);
+
+ sal_uInt16 nResult = DET_INS_EMPTY;
+
+ ScDetectiveRefIter aIter(rDoc, pFCell);
+ ScRange aRef;
+ while ( aIter.GetNextRef( aRef ) )
+ {
+ if (DrawEntry( nCol, nRow, aRef, rData ))
+ {
+ nResult = DET_INS_INSERTED; // insert new arrow
+ }
+ else
+ {
+ // continue
+
+ if ( nLevel < rData.GetMaxLevel() )
+ {
+ sal_uInt16 nSubResult;
+ bool bArea = (aRef.aStart != aRef.aEnd);
+ if (bArea)
+ nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 );
+ else
+ nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(),
+ rData, nLevel+1 );
+
+ switch (nSubResult)
+ {
+ case DET_INS_INSERTED:
+ nResult = DET_INS_INSERTED;
+ break;
+ case DET_INS_CONTINUE:
+ if (nResult != DET_INS_INSERTED)
+ nResult = DET_INS_CONTINUE;
+ break;
+ case DET_INS_CIRCULAR:
+ if (nResult == DET_INS_EMPTY)
+ nResult = DET_INS_CIRCULAR;
+ break;
+ // DET_INS_EMPTY: no change
+ }
+ }
+ else // nMaxLevel reached
+ if (nResult != DET_INS_INSERTED)
+ nResult = DET_INS_CONTINUE;
+ }
+ }
+
+ pFCell->SetRunning(false);
+
+ return nResult;
+}
+
+sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef,
+ sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
+{
+ sal_uInt16 nResult = nLevel;
+
+ ScCellIterator aCellIter( rDoc, rRef);
+ for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
+ {
+ if (aCellIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ sal_uInt16 nTemp = FindPredLevel(aCellIter.GetPos().Col(), aCellIter.GetPos().Row(), nLevel, nDeleteLevel);
+ if (nTemp > nResult)
+ nResult = nTemp;
+ }
+
+ return nResult;
+}
+
+ // nDeleteLevel != 0 -> delete
+
+sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
+{
+ OSL_ENSURE( nLevel<1000, "Level" );
+
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ return nLevel;
+
+ ScFormulaCell* pFCell = aCell.getFormula();
+ if (pFCell->IsRunning())
+ return nLevel;
+
+ if (pFCell->GetDirty())
+ pFCell->Interpret(); // can't be called after SetRunning
+ pFCell->SetRunning(true);
+
+ sal_uInt16 nResult = nLevel;
+ bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
+
+ if ( bDelete )
+ {
+ DeleteArrowsAt( nCol, nRow, true ); // arrows, that are pointing here
+ }
+
+ ScDetectiveRefIter aIter(rDoc, pFCell);
+ ScRange aRef;
+ while ( aIter.GetNextRef( aRef) )
+ {
+ bool bArea = ( aRef.aStart != aRef.aEnd );
+
+ if ( bDelete ) // delete frame ?
+ {
+ if (bArea)
+ {
+ DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
+ }
+ }
+ else // continue searching
+ {
+ if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) )
+ {
+ sal_uInt16 nTemp;
+ if (bArea)
+ nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel );
+ else
+ nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(),
+ nLevel+1, nDeleteLevel );
+ if (nTemp > nResult)
+ nResult = nTemp;
+ }
+ }
+ }
+
+ pFCell->SetRunning(false);
+
+ return nResult;
+}
+
+sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
+ sal_uInt16 nLevel )
+{
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ return DET_INS_EMPTY;
+
+ ScFormulaCell* pFCell = aCell.getFormula();
+ if (pFCell->IsRunning())
+ return DET_INS_CIRCULAR;
+
+ if (pFCell->GetDirty())
+ pFCell->Interpret(); // can't be called after SetRunning
+ pFCell->SetRunning(true);
+
+ sal_uInt16 nResult = DET_INS_EMPTY;
+
+ ScDetectiveRefIter aIter(rDoc, pFCell);
+ ScRange aRef;
+ ScAddress aErrorPos;
+ bool bHasError = false;
+ while ( aIter.GetNextRef( aRef ) )
+ {
+ if (HasError( aRef, aErrorPos ))
+ {
+ bHasError = true;
+ if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
+ nResult = DET_INS_INSERTED;
+
+ if ( nLevel < rData.GetMaxLevel() ) // hits most of the time
+ {
+ if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
+ rData, nLevel+1 ) == DET_INS_INSERTED)
+ nResult = DET_INS_INSERTED;
+ }
+ }
+ }
+
+ pFCell->SetRunning(false);
+
+ // leaves ?
+ if (!bHasError)
+ if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
+ nResult = DET_INS_INSERTED;
+
+ return nResult;
+}
+
+sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ ScDetectiveData& rData, sal_uInt16 nLevel )
+{
+ // over the entire document.
+
+ sal_uInt16 nResult = DET_INS_EMPTY;
+ ScCellIterator aCellIter(rDoc, ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB)); // all sheets
+ for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
+ {
+ if (aCellIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = aCellIter.getFormulaCell();
+ bool bRunning = pFCell->IsRunning();
+
+ if (pFCell->GetDirty())
+ pFCell->Interpret(); // can't be called after SetRunning
+ pFCell->SetRunning(true);
+
+ ScDetectiveRefIter aIter(rDoc, pFCell);
+ ScRange aRef;
+ while ( aIter.GetNextRef( aRef) )
+ {
+ if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
+ {
+ if (Intersect( nCol1,nRow1,nCol2,nRow2,
+ aRef.aStart.Col(),aRef.aStart.Row(),
+ aRef.aEnd.Col(),aRef.aEnd.Row() ))
+ {
+ bool bAlien = ( aCellIter.GetPos().Tab() != nTab );
+ bool bDrawRet;
+ if (bAlien)
+ bDrawRet = DrawAlienEntry( aRef, rData );
+ else
+ bDrawRet = DrawEntry( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
+ aRef, rData );
+ if (bDrawRet)
+ {
+ nResult = DET_INS_INSERTED; // insert new arrow
+ }
+ else
+ {
+ if (bRunning)
+ {
+ if (nResult == DET_INS_EMPTY)
+ nResult = DET_INS_CIRCULAR;
+ }
+ else
+ {
+
+ if ( nLevel < rData.GetMaxLevel() )
+ {
+ sal_uInt16 nSubResult = InsertSuccLevel(
+ aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
+ aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
+ rData, nLevel+1 );
+ switch (nSubResult)
+ {
+ case DET_INS_INSERTED:
+ nResult = DET_INS_INSERTED;
+ break;
+ case DET_INS_CONTINUE:
+ if (nResult != DET_INS_INSERTED)
+ nResult = DET_INS_CONTINUE;
+ break;
+ case DET_INS_CIRCULAR:
+ if (nResult == DET_INS_EMPTY)
+ nResult = DET_INS_CIRCULAR;
+ break;
+ // DET_INS_EMPTY: leave unchanged
+ }
+ }
+ else // nMaxLevel reached
+ if (nResult != DET_INS_INSERTED)
+ nResult = DET_INS_CONTINUE;
+ }
+ }
+ }
+ }
+ }
+ pFCell->SetRunning(bRunning);
+ }
+
+ return nResult;
+}
+
+sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
+{
+ OSL_ENSURE( nLevel<1000, "Level" );
+
+ sal_uInt16 nResult = nLevel;
+ bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
+
+ ScCellIterator aCellIter( rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab) );
+ for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
+ {
+ if (aCellIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = aCellIter.getFormulaCell();
+ bool bRunning = pFCell->IsRunning();
+
+ if (pFCell->GetDirty())
+ pFCell->Interpret(); // can't be called after SetRunning
+ pFCell->SetRunning(true);
+
+ ScDetectiveRefIter aIter(rDoc, pFCell);
+ ScRange aRef;
+ while ( aIter.GetNextRef( aRef) )
+ {
+ if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
+ {
+ if (Intersect( nCol1,nRow1,nCol2,nRow2,
+ aRef.aStart.Col(),aRef.aStart.Row(),
+ aRef.aEnd.Col(),aRef.aEnd.Row() ))
+ {
+ if ( bDelete ) // arrows, that are starting here
+ {
+ if (aRef.aStart != aRef.aEnd)
+ {
+ DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(),
+ aRef.aEnd.Col(), aRef.aEnd.Row() );
+ }
+ DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), false );
+ }
+ else if ( !bRunning &&
+ HasArrow( aRef.aStart,
+ aCellIter.GetPos().Col(),aCellIter.GetPos().Row(),aCellIter.GetPos().Tab() ) )
+ {
+ sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
+ aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
+ nLevel+1, nDeleteLevel );
+ if (nTemp > nResult)
+ nResult = nTemp;
+ }
+ }
+ }
+ }
+
+ pFCell->SetRunning(bRunning);
+ }
+
+ return nResult;
+}
+
+bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDetectiveData aData( pModel );
+
+ sal_uInt16 nMaxLevel = 0;
+ sal_uInt16 nResult = DET_INS_CONTINUE;
+ while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
+ {
+ aData.SetMaxLevel( nMaxLevel );
+ nResult = InsertPredLevel( nCol, nRow, aData, 0 );
+ ++nMaxLevel;
+ }
+
+ return ( nResult == DET_INS_INSERTED );
+}
+
+bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDetectiveData aData( pModel );
+
+ sal_uInt16 nMaxLevel = 0;
+ sal_uInt16 nResult = DET_INS_CONTINUE;
+ while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
+ {
+ aData.SetMaxLevel( nMaxLevel );
+ nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
+ ++nMaxLevel;
+ }
+
+ return ( nResult == DET_INS_INSERTED );
+}
+
+bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScRange aRange( nCol, nRow, nTab );
+ ScAddress aErrPos;
+ if ( !HasError( aRange,aErrPos ) )
+ return false;
+
+ ScDetectiveData aData( pModel );
+
+ aData.SetMaxLevel( 1000 );
+ sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
+
+ return ( nResult == DET_INS_INSERTED );
+}
+
+bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
+ if ( nLevelCount )
+ FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // delete
+
+ return ( nLevelCount != 0 );
+}
+
+bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
+ if ( nLevelCount )
+ FindPredLevel( nCol, nRow, 0, nLevelCount ); // delete
+
+ return ( nLevelCount != 0 );
+}
+
+bool ScDetectiveFunc::DeleteCirclesAt( SCCOL nCol, SCROW nRow )
+{
+ tools::Rectangle aRect = ScDrawLayer::GetCellRect(rDoc, ScAddress(nCol, nRow, nTab), true);
+ aRect.AdjustLeft(-250);
+ aRect.AdjustRight(250);
+ aRect.AdjustTop(-70);
+ aRect.AdjustBottom(70);
+
+ Point aStartCorner = aRect.TopLeft();
+ Point aEndCorner = aRect.BottomRight();
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage, "Page ?");
+
+ pPage->RecalcObjOrdNums();
+
+ const size_t nObjCount = pPage->GetObjCount();
+ size_t nDelCount = 0;
+ if (nObjCount)
+ {
+ std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
+
+ SdrObjListIter aIter(pPage, SdrIterMode::Flat);
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (pObject->GetLayer() == SC_LAYER_INTERN)
+ if (auto pSdrCircObj = dynamic_cast<const SdrCircObj*>(pObject) )
+ {
+ tools::Rectangle aObjRect = pSdrCircObj->GetLogicRect();
+ if (RectIsPoints(aObjRect, aStartCorner, aEndCorner))
+ ppObj[nDelCount++] = pObject;
+ }
+
+ pObject = aIter.Next();
+ }
+
+ for (size_t i = 1; i <= nDelCount; ++i)
+ pModel->AddCalcUndo(std::make_unique<SdrUndoRemoveObj>(*ppObj[nDelCount - i]));
+
+ for (size_t i = 1; i <= nDelCount; ++i)
+ pPage->RemoveObject(ppObj[nDelCount - i]->GetOrdNum());
+
+ ppObj.reset();
+
+ Modified();
+ }
+
+ return (nDelCount != 0);
+}
+
+bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+
+ pPage->RecalcObjOrdNums();
+
+ size_t nDelCount = 0;
+ const size_t nObjCount = pPage->GetObjCount();
+ if (nObjCount)
+ {
+ std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetLayer() == SC_LAYER_INTERN )
+ {
+ bool bDoThis = true;
+ bool bCircle = ( dynamic_cast<const SdrCircObj*>( pObject) != nullptr );
+ bool bCaption = ScDrawLayer::IsNoteCaption( pObject );
+ if ( eWhat == ScDetectiveDelete::Detective ) // detective, from menu
+ bDoThis = !bCaption; // also circles
+ else if ( eWhat == ScDetectiveDelete::Circles ) // circles, if new created
+ bDoThis = bCircle;
+ else if ( eWhat == ScDetectiveDelete::Arrows ) // DetectiveRefresh
+ bDoThis = !bCaption && !bCircle; // don't include circles
+ else
+ {
+ OSL_FAIL("what?");
+ }
+ if ( bDoThis )
+ ppObj[nDelCount++] = pObject;
+ }
+
+ pObject = aIter.Next();
+ }
+
+ for (size_t i=1; i<=nDelCount; ++i)
+ pModel->AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) );
+
+ for (size_t i=1; i<=nDelCount; ++i)
+ pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
+
+ ppObj.reset();
+
+ Modified();
+ }
+
+ return ( nDelCount != 0 );
+}
+
+bool ScDetectiveFunc::MarkInvalid(bool& rOverflow)
+{
+ rOverflow = false;
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ bool bDeleted = DeleteAll( ScDetectiveDelete::Circles ); // just circles
+
+ ScDetectiveData aData( pModel );
+ tools::Long nInsCount = 0;
+
+ // search for valid places
+ ScDocAttrIterator aAttrIter( rDoc, nTab, 0,0,rDoc.MaxCol(),rDoc.MaxRow() );
+ SCCOL nCol;
+ SCROW nRow1;
+ SCROW nRow2;
+ const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
+ while ( pPattern && nInsCount < SC_DET_MAXCIRCLE )
+ {
+ sal_uInt32 nIndex = pPattern->GetItem(ATTR_VALIDDATA).GetValue();
+ if (nIndex)
+ {
+ const ScValidationData* pData = rDoc.GetValidationEntry( nIndex );
+ if ( pData )
+ {
+ // pass cells in this area
+
+ bool bMarkEmpty = !pData->IsIgnoreBlank();
+ SCROW nNextRow = nRow1;
+ SCROW nRow;
+ ScCellIterator aCellIter( rDoc, ScRange(nCol, nRow1, nTab, nCol, nRow2, nTab) );
+ for (bool bHas = aCellIter.first(); bHas && nInsCount < SC_DET_MAXCIRCLE; bHas = aCellIter.next())
+ {
+ SCROW nCellRow = aCellIter.GetPos().Row();
+ if ( bMarkEmpty )
+ for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
+ {
+ if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
+ DrawCircle( nCol, nRow, aData );
+ ++nInsCount;
+ }
+ ScRefCellValue aCell = aCellIter.getRefCellValue();
+ if (!pData->IsDataValid(aCell, aCellIter.GetPos()))
+ {
+ if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
+ DrawCircle( nCol, nCellRow, aData );
+ ++nInsCount;
+ }
+ nNextRow = nCellRow + 1;
+ }
+ if ( bMarkEmpty )
+ for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
+ {
+ if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
+ DrawCircle(nCol, nRow, aData);
+ ++nInsCount;
+ }
+ }
+ }
+
+ pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
+ }
+
+ if ( nInsCount >= SC_DET_MAXCIRCLE )
+ rOverflow = true;
+
+ return ( bDeleted || nInsCount != 0 );
+}
+
+void ScDetectiveFunc::GetAllPreds(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ vector<ScTokenRef>& rRefTokens)
+{
+ ScCellIterator aIter(rDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = aIter.getFormulaCell();
+ ScDetectiveRefIter aRefIter(rDoc, pFCell);
+ for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
+ {
+ ScTokenRef pRef(p->Clone());
+ ScRefTokenHelper::join(&rDoc, rRefTokens, pRef, aIter.GetPos());
+ }
+ }
+}
+
+void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ vector<ScTokenRef>& rRefTokens)
+{
+ vector<ScTokenRef> aSrcRange;
+ aSrcRange.push_back(
+ ScRefTokenHelper::createRefToken(rDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)));
+
+ ScCellIterator aIter(rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab));
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = aIter.getFormulaCell();
+ ScDetectiveRefIter aRefIter(rDoc, pFCell);
+ for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
+ {
+ const ScAddress& aPos = aIter.GetPos();
+ ScTokenRef pRef(p->Clone());
+ if (ScRefTokenHelper::intersects(&rDoc, aSrcRange, pRef, aPos))
+ {
+ // This address is absolute.
+ pRef = ScRefTokenHelper::createRefToken(rDoc, aPos);
+ ScRefTokenHelper::join(&rDoc, rRefTokens, pRef, ScAddress());
+ }
+ }
+ }
+}
+
+void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return;
+
+ auto pStyleSheet = rDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame);
+ if (!pStyleSheet)
+ return;
+
+ ScStyleSaveData aOldData, aNewData;
+ aOldData.InitFromStyle(pStyleSheet);
+
+ auto& rSet = pStyleSheet->GetItemSet();
+ rSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ rSet.Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor()));
+ static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
+
+ aNewData.InitFromStyle(pStyleSheet);
+
+ ScDocShell* pDocSh = rDoc.GetDocumentShell();
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>(pDocSh, pStyleSheet->GetFamily(), aOldData, aNewData));
+}
+
+void ScDetectiveFunc::UpdateAllArrowColors()
+{
+ // no undo actions necessary
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return;
+
+ for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab )
+ {
+ SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
+ OSL_ENSURE( pPage, "Page ?" );
+ if( pPage )
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
+ {
+ if ( pObject->GetLayer() == SC_LAYER_INTERN )
+ {
+ bool bArrow = false;
+ bool bError = false;
+
+ ScAddress aPos;
+ ScRange aSource;
+ bool bDummy;
+ ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy );
+ if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB )
+ {
+ // source is valid, determine error flag from source range
+
+ ScAddress aErrPos;
+ if ( HasError( aSource, aErrPos ) )
+ bError = true;
+ else
+ bArrow = true;
+ }
+ else if ( eType == SC_DETOBJ_FROMOTHERTAB )
+ {
+ // source range is no longer known, take error flag from formula itself
+ // (this means, if the formula has an error, all references to other tables
+ // are marked red)
+
+ ScAddress aErrPos;
+ if ( HasError( ScRange( aPos), aErrPos ) )
+ bError = true;
+ else
+ bArrow = true;
+ }
+ else if ( eType == SC_DETOBJ_CIRCLE )
+ {
+ // circles (error marks) are always red
+
+ bError = true;
+ }
+ else if ( eType == SC_DETOBJ_NONE )
+ {
+ // frame for area reference has no ObjType, always gets arrow color
+
+ if ( dynamic_cast<const SdrRectObj*>( pObject) != nullptr && dynamic_cast<const SdrCaptionObj*>( pObject) == nullptr )
+ {
+ bArrow = true;
+ }
+ }
+
+ if ( bArrow || bError )
+ {
+ Color nColor = ( bError ? GetErrorColor() : GetArrowColor() );
+ pObject->SetMergedItem( XLineColorItem( OUString(), nColor ) );
+
+ // repaint only
+ pObject->ActionChanged();
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScDetectiveFunc::FindFrameForObject( const SdrObject* pObject, ScRange& rRange )
+{
+ // find the rectangle for an arrow (always the object directly before the arrow)
+ // rRange must be initialized to the source cell of the arrow (start of area)
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel) return;
+
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage) return;
+
+ // test if the object is a direct page member
+ if( !(pObject && pObject->getSdrPageFromSdrObject() && (pObject->getSdrPageFromSdrObject() == pObject->getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList())) )
+ return;
+
+ // Is there a previous object?
+ const size_t nOrdNum = pObject->GetOrdNum();
+
+ if(nOrdNum <= 0)
+ return;
+
+ SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
+
+ if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && dynamic_cast<const SdrRectObj*>( pPrevObj) != nullptr )
+ {
+ ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() );
+ if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) )
+ {
+ rRange.aEnd = pPrevData->maEnd;
+ return;
+ }
+ }
+}
+
+ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
+ ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
+{
+ rRedLine = false;
+ ScDetectiveObjType eType = SC_DETOBJ_NONE;
+
+ if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
+ {
+ if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) )
+ {
+ bool bValidStart = pData->maStart.IsValid();
+ bool bValidEnd = pData->maEnd.IsValid();
+
+ if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
+ {
+ // line object -> arrow
+
+ if ( bValidStart )
+ eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
+ else if ( bValidEnd )
+ eType = SC_DETOBJ_FROMOTHERTAB;
+
+ if ( bValidStart )
+ rSource = pData->maStart;
+ if ( bValidEnd )
+ rPosition = pData->maEnd;
+
+ if ( bValidStart && lcl_HasThickLine( *pObject ) )
+ {
+ // thick line -> look for frame before this object
+
+ FindFrameForObject( pObject, rSource ); // modifies rSource
+ }
+
+ Color nObjColor = pObject->GetMergedItem(XATTR_LINECOLOR).GetColorValue();
+ if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
+ rRedLine = true;
+ }
+ else if (dynamic_cast<const SdrCircObj*>(pObject) != nullptr)
+ {
+ if (bValidStart)
+ {
+ // cell position is returned in rPosition
+ rPosition = pData->maStart;
+ eType = SC_DETOBJ_CIRCLE;
+ }
+ }
+ else if (dynamic_cast<const SdrRectObj*>(pObject) != nullptr)
+ {
+ if (bValidStart)
+ {
+ // cell position is returned in rPosition
+ rPosition = pData->maStart;
+ eType = SC_DETOBJ_RECTANGLE;
+ }
+ }
+ }
+ }
+
+ return eType;
+}
+
+void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
+ const ScAddress& rPosition, const ScRange& rSource,
+ bool bRedLine )
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel) return;
+ ScDetectiveData aData( pModel );
+
+ switch (eType)
+ {
+ case SC_DETOBJ_ARROW:
+ case SC_DETOBJ_FROMOTHERTAB:
+ InsertArrow( rPosition.Col(), rPosition.Row(),
+ rSource.aStart.Col(), rSource.aStart.Row(),
+ rSource.aEnd.Col(), rSource.aEnd.Row(),
+ (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData );
+ break;
+ case SC_DETOBJ_TOOTHERTAB:
+ InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(),
+ rSource.aEnd.Col(), rSource.aEnd.Row(),
+ bRedLine, aData );
+ break;
+ case SC_DETOBJ_CIRCLE:
+ DrawCircle( rPosition.Col(), rPosition.Row(), aData );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+Color ScDetectiveFunc::GetArrowColor()
+{
+ if (!bColorsInitialized)
+ InitializeColors();
+ return nArrowColor;
+}
+
+Color ScDetectiveFunc::GetErrorColor()
+{
+ if (!bColorsInitialized)
+ InitializeColors();
+ return nErrorColor;
+}
+
+Color ScDetectiveFunc::GetCommentColor()
+{
+ if (!bColorsInitialized)
+ InitializeColors();
+ return nCommentColor;
+}
+
+void ScDetectiveFunc::InitializeColors()
+{
+ // may be called several times to update colors from configuration
+
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ nArrowColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor;
+ nErrorColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor;
+ nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
+
+ bColorsInitialized = true;
+}
+
+bool ScDetectiveFunc::IsColorsInitialized()
+{
+ return bColorsInitialized;
+}
+
+void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString &rDisplay)
+{
+ rDisplay += "\n--------\n";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/docoptio.cxx b/sc/source/core/tool/docoptio.cxx
new file mode 100644
index 0000000000..1f88b07c9b
--- /dev/null
+++ b/sc/source/core/tool/docoptio.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 <svl/numformat.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <docoptio.hxx>
+#include <miscuno.hxx>
+#include <global.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+
+using sc::TwipsToEvenHMM;
+
+static sal_uInt16 lcl_GetDefaultTabDist()
+{
+ if ( ScOptionsUtil::IsMetricSystem() )
+ return 709; // 1,25 cm
+ else
+ return 720; // 1/2"
+}
+
+// ScDocOptions - document options
+
+ScDocOptions::ScDocOptions()
+{
+ ResetDocOptions();
+}
+
+void ScDocOptions::ResetDocOptions()
+{
+ bIsIgnoreCase = false;
+ bIsIter = false;
+ nIterCount = 100;
+ fIterEps = 1.0E-3;
+ nPrecStandardFormat = SvNumberFormatter::UNLIMITED_PRECISION;
+ nDay = 30;
+ nMonth = 12;
+ nYear = 1899;
+ nYear2000 = SvNumberFormatter::GetYear2000Default();
+ nTabDistance = lcl_GetDefaultTabDist();
+ bCalcAsShown = false;
+ bMatchWholeCell = true;
+ bDoAutoSpell = false;
+ bLookUpColRowNames = true;
+ bFormulaRegexEnabled= false;
+ bFormulaWildcardsEnabled= true;
+ eFormulaSearchType = utl::SearchParam::SearchType::Wildcard;
+ bWriteCalcConfig = true;
+}
+
+void ScDocOptions::SetFormulaRegexEnabled( bool bVal )
+{
+ if (bVal)
+ {
+ bFormulaRegexEnabled = true;
+ bFormulaWildcardsEnabled = false;
+ eFormulaSearchType = utl::SearchParam::SearchType::Regexp;
+ }
+ else if (!bFormulaRegexEnabled)
+ ; // nothing changes for setting false to false
+ else
+ {
+ bFormulaRegexEnabled = false;
+ eFormulaSearchType = utl::SearchParam::SearchType::Unknown;
+ }
+}
+
+void ScDocOptions::SetFormulaWildcardsEnabled( bool bVal )
+{
+ if (bVal)
+ {
+ bFormulaRegexEnabled = false;
+ bFormulaWildcardsEnabled = true;
+ eFormulaSearchType = utl::SearchParam::SearchType::Wildcard;
+ }
+ else if (!bFormulaWildcardsEnabled)
+ ; // nothing changes for setting false to false
+ else
+ {
+ bFormulaWildcardsEnabled = false;
+ eFormulaSearchType = utl::SearchParam::SearchType::Unknown;
+ }
+}
+
+// ScTpCalcItem - data for the CalcOptions TabPage
+
+ScTpCalcItem::ScTpCalcItem( sal_uInt16 nWhichP, const ScDocOptions& rOpt )
+ : SfxPoolItem ( nWhichP ),
+ theOptions ( rOpt )
+{
+}
+
+ScTpCalcItem::~ScTpCalcItem()
+{
+}
+
+bool ScTpCalcItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScTpCalcItem& rPItem = static_cast<const ScTpCalcItem&>(rItem);
+
+ return ( theOptions == rPItem.theOptions );
+}
+
+ScTpCalcItem* ScTpCalcItem::Clone( SfxItemPool * ) const
+{
+ return new ScTpCalcItem( *this );
+}
+
+// Config Item containing document options
+
+constexpr OUStringLiteral CFGPATH_CALC = u"Office.Calc/Calculate";
+
+#define SCCALCOPT_ITER_ITER 0
+#define SCCALCOPT_ITER_STEPS 1
+#define SCCALCOPT_ITER_MINCHG 2
+#define SCCALCOPT_DATE_DAY 3
+#define SCCALCOPT_DATE_MONTH 4
+#define SCCALCOPT_DATE_YEAR 5
+#define SCCALCOPT_DECIMALS 6
+#define SCCALCOPT_CASESENSITIVE 7
+#define SCCALCOPT_PRECISION 8
+#define SCCALCOPT_SEARCHCRIT 9
+#define SCCALCOPT_FINDLABEL 10
+#define SCCALCOPT_REGEX 11
+#define SCCALCOPT_WILDCARDS 12
+
+constexpr OUStringLiteral CFGPATH_DOCLAYOUT = u"Office.Calc/Layout/Other";
+
+#define SCDOCLAYOUTOPT_TABSTOP 0
+
+Sequence<OUString> ScDocCfg::GetCalcPropertyNames()
+{
+ return {"IterativeReference/Iteration", // SCCALCOPT_ITER_ITER
+ "IterativeReference/Steps", // SCCALCOPT_ITER_STEPS
+ "IterativeReference/MinimumChange", // SCCALCOPT_ITER_MINCHG
+ "Other/Date/DD", // SCCALCOPT_DATE_DAY
+ "Other/Date/MM", // SCCALCOPT_DATE_MONTH
+ "Other/Date/YY", // SCCALCOPT_DATE_YEAR
+ "Other/DecimalPlaces", // SCCALCOPT_DECIMALS
+ "Other/CaseSensitive", // SCCALCOPT_CASESENSITIVE
+ "Other/Precision", // SCCALCOPT_PRECISION
+ "Other/SearchCriteria", // SCCALCOPT_SEARCHCRIT
+ "Other/FindLabel", // SCCALCOPT_FINDLABEL
+ "Other/RegularExpressions", // SCCALCOPT_REGEX
+ "Other/Wildcards"}; // SCCALCOPT_WILDCARDS
+}
+
+Sequence<OUString> ScDocCfg::GetLayoutPropertyNames()
+{
+ if (ScOptionsUtil::IsMetricSystem())
+ return {"TabStop/Metric"}; // SCDOCLAYOUTOPT_TABSTOP
+ else
+ return {"TabStop/NonMetric"}; // SCDOCLAYOUTOPT_TABSTOP
+}
+
+ScDocCfg::ScDocCfg() :
+ aCalcItem( CFGPATH_CALC ),
+ aLayoutItem(CFGPATH_DOCLAYOUT)
+{
+ sal_Int32 nIntVal = 0;
+
+ Sequence<OUString> aNames;
+ Sequence<Any> aValues;
+ const Any* pValues = nullptr;
+
+ sal_uInt16 nDateDay, nDateMonth;
+ sal_Int16 nDateYear;
+ GetDate( nDateDay, nDateMonth, nDateYear );
+
+ aNames = GetCalcPropertyNames();
+ aValues = aCalcItem.GetProperties(aNames);
+ aCalcItem.EnableNotification(aNames);
+ pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() == aNames.getLength())
+ {
+ double fDoubleVal = 0;
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ OSL_ENSURE(pValues[nProp].hasValue(), "property value missing");
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case SCCALCOPT_ITER_ITER:
+ SetIter( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCCALCOPT_ITER_STEPS:
+ if (pValues[nProp] >>= nIntVal) SetIterCount( static_cast<sal_uInt16>(nIntVal) );
+ break;
+ case SCCALCOPT_ITER_MINCHG:
+ if (pValues[nProp] >>= fDoubleVal) SetIterEps( fDoubleVal );
+ break;
+ case SCCALCOPT_DATE_DAY:
+ if (pValues[nProp] >>= nIntVal) nDateDay = static_cast<sal_uInt16>(nIntVal);
+ break;
+ case SCCALCOPT_DATE_MONTH:
+ if (pValues[nProp] >>= nIntVal) nDateMonth = static_cast<sal_uInt16>(nIntVal);
+ break;
+ case SCCALCOPT_DATE_YEAR:
+ if (pValues[nProp] >>= nIntVal) nDateYear = static_cast<sal_Int16>(nIntVal);
+ break;
+ case SCCALCOPT_DECIMALS:
+ if (pValues[nProp] >>= nIntVal) SetStdPrecision( static_cast<sal_uInt16>(nIntVal) );
+ break;
+ case SCCALCOPT_CASESENSITIVE:
+ // content is reversed
+ SetIgnoreCase( !ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCCALCOPT_PRECISION:
+ SetCalcAsShown( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCCALCOPT_SEARCHCRIT:
+ SetMatchWholeCell( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCCALCOPT_FINDLABEL:
+ SetLookUpColRowNames( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCCALCOPT_REGEX :
+ SetFormulaRegexEnabled( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCCALCOPT_WILDCARDS :
+ SetFormulaWildcardsEnabled( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ }
+ }
+ }
+ }
+ aCalcItem.SetCommitLink( LINK( this, ScDocCfg, CalcCommitHdl ) );
+
+ SetDate( nDateDay, nDateMonth, nDateYear );
+
+ aNames = GetLayoutPropertyNames();
+ aValues = aLayoutItem.GetProperties(aNames);
+ aLayoutItem.EnableNotification(aNames);
+ pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() == aNames.getLength())
+ {
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ OSL_ENSURE(pValues[nProp].hasValue(), "property value missing");
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case SCDOCLAYOUTOPT_TABSTOP:
+ // TabDistance in ScDocOptions is in twips
+ if (pValues[nProp] >>= nIntVal)
+ SetTabDistance(o3tl::toTwips(nIntVal, o3tl::Length::mm100));
+ break;
+ }
+ }
+ }
+ }
+ aLayoutItem.SetCommitLink( LINK( this, ScDocCfg, LayoutCommitHdl ) );
+}
+
+IMPL_LINK_NOARG(ScDocCfg, CalcCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetCalcPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ sal_uInt16 nDateDay, nDateMonth;
+ sal_Int16 nDateYear;
+ GetDate( nDateDay, nDateMonth, nDateYear );
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCCALCOPT_ITER_ITER:
+ pValues[nProp] <<= IsIter();
+ break;
+ case SCCALCOPT_ITER_STEPS:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetIterCount());
+ break;
+ case SCCALCOPT_ITER_MINCHG:
+ pValues[nProp] <<= GetIterEps();
+ break;
+ case SCCALCOPT_DATE_DAY:
+ pValues[nProp] <<= static_cast<sal_Int32>(nDateDay);
+ break;
+ case SCCALCOPT_DATE_MONTH:
+ pValues[nProp] <<= static_cast<sal_Int32>(nDateMonth);
+ break;
+ case SCCALCOPT_DATE_YEAR:
+ pValues[nProp] <<= static_cast<sal_Int32>(nDateYear);
+ break;
+ case SCCALCOPT_DECIMALS:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetStdPrecision());
+ break;
+ case SCCALCOPT_CASESENSITIVE:
+ // content is reversed
+ pValues[nProp] <<= !IsIgnoreCase();
+ break;
+ case SCCALCOPT_PRECISION:
+ pValues[nProp] <<= IsCalcAsShown();
+ break;
+ case SCCALCOPT_SEARCHCRIT:
+ pValues[nProp] <<= IsMatchWholeCell();
+ break;
+ case SCCALCOPT_FINDLABEL:
+ pValues[nProp] <<= IsLookUpColRowNames();
+ break;
+ case SCCALCOPT_REGEX :
+ pValues[nProp] <<= IsFormulaRegexEnabled();
+ break;
+ case SCCALCOPT_WILDCARDS :
+ pValues[nProp] <<= IsFormulaWildcardsEnabled();
+ break;
+ }
+ }
+ aCalcItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScDocCfg, LayoutCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetLayoutPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCDOCLAYOUTOPT_TABSTOP:
+ // TabDistance in ScDocOptions is in twips
+ // use only even numbers, so defaults don't get changed
+ // by modifying other settings in the same config item
+ pValues[nProp] <<= static_cast<sal_Int32>(TwipsToEvenHMM( GetTabDistance() ));
+ break;
+ }
+ }
+ aLayoutItem.PutProperties(aNames, aValues);
+}
+
+void ScDocCfg::SetOptions( const ScDocOptions& rNew )
+{
+ *static_cast<ScDocOptions*>(this) = rNew;
+
+ aCalcItem.SetModified();
+ aLayoutItem.SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/doubleref.cxx b/sc/source/core/tool/doubleref.cxx
new file mode 100644
index 0000000000..66ecea4a10
--- /dev/null
+++ b/sc/source/core/tool/doubleref.cxx
@@ -0,0 +1,474 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doubleref.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmatrix.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#include <unotools/transliterationwrapper.hxx>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+using ::std::unique_ptr;
+using ::std::vector;
+
+namespace {
+
+void lcl_uppercase(OUString& rStr)
+{
+ rStr = ScGlobal::getCharClass().uppercase(rStr.trim());
+}
+
+bool lcl_createStarQuery(
+ const ScDocument* pDoc,
+ svl::SharedStringPool& rPool, ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef, const ScDBRangeBase* pQueryRef)
+{
+ // A valid StarQuery must be at least 4 columns wide. To be precise it
+ // should be exactly 4 columns ...
+ // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
+ // column Excel style query range immediately left to itself would result
+ // in a circular reference when the field name or operator or value (first
+ // to third query range column) is obtained (#i58354#). Furthermore, if the
+ // range wasn't sufficiently specified data changes wouldn't flag formula
+ // cells for recalculation.
+
+ if (pQueryRef->getColSize() < 4)
+ return false;
+
+ bool bValid;
+ OUString aCellStr;
+ SCSIZE nIndex = 0;
+ SCROW nRow = 0;
+ SCROW nRows = pDBRef->getRowSize();
+ SCSIZE nNewEntries = static_cast<SCSIZE>(nRows);
+ pParam->Resize(nNewEntries);
+
+ do
+ {
+ ScQueryEntry& rEntry = pParam->GetEntry(nIndex);
+
+ bValid = false;
+
+ if (nIndex > 0)
+ {
+ // For all entries after the first one, check the and/or connector in the first column.
+ aCellStr = pQueryRef->getString(0, nRow);
+ lcl_uppercase(aCellStr);
+ if ( aCellStr == ScResId(STR_TABLE_AND) )
+ {
+ rEntry.eConnect = SC_AND;
+ bValid = true;
+ }
+ else if ( aCellStr == ScResId(STR_TABLE_OR) )
+ {
+ rEntry.eConnect = SC_OR;
+ bValid = true;
+ }
+ }
+
+ if ((nIndex < 1) || bValid)
+ {
+ // field name in the 2nd column.
+ aCellStr = pQueryRef->getString(1, nRow);
+ SCCOL nField = pDBRef->findFieldColumn(aCellStr); // TODO: must be case insensitive comparison.
+ if (pDoc->ValidCol(nField))
+ {
+ rEntry.nField = nField;
+ bValid = true;
+ }
+ else
+ bValid = false;
+ }
+
+ if (bValid)
+ {
+ // equality, non-equality operator in the 3rd column.
+ aCellStr = pQueryRef->getString(2, nRow);
+ lcl_uppercase(aCellStr);
+ const sal_Unicode* p = aCellStr.getStr();
+ if (p[0] == '<')
+ {
+ if (p[1] == '>')
+ rEntry.eOp = SC_NOT_EQUAL;
+ else if (p[1] == '=')
+ rEntry.eOp = SC_LESS_EQUAL;
+ else
+ rEntry.eOp = SC_LESS;
+ }
+ else if (p[0] == '>')
+ {
+ if (p[1] == '=')
+ rEntry.eOp = SC_GREATER_EQUAL;
+ else
+ rEntry.eOp = SC_GREATER;
+ }
+ else if (p[0] == '=')
+ rEntry.eOp = SC_EQUAL;
+
+ }
+
+ if (bValid)
+ {
+ // Finally, the right-hand-side value in the 4th column.
+ rEntry.GetQueryItem().maString =
+ rPool.intern(pQueryRef->getString(3, nRow));
+ rEntry.bDoQuery = true;
+ }
+ nIndex++;
+ nRow++;
+ }
+ while (bValid && (nRow < nRows) /* && (nIndex < MAXQUERY) */ );
+ return bValid;
+}
+
+bool lcl_createExcelQuery(
+ const ScDocument* pDoc,
+ svl::SharedStringPool& rPool, ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef, const ScDBRangeBase* pQueryRef)
+{
+ bool bValid = true;
+ SCCOL nCols = pQueryRef->getColSize();
+ SCROW nRows = pQueryRef->getRowSize();
+ vector<SCCOL> aFields(nCols);
+ SCCOL nCol = 0;
+ while (bValid && (nCol < nCols))
+ {
+ OUString aQueryStr = pQueryRef->getString(nCol, 0);
+ SCCOL nField = pDBRef->findFieldColumn(aQueryStr);
+ if (pDoc->ValidCol(nField))
+ aFields[nCol] = nField;
+ else
+ bValid = false;
+ ++nCol;
+ }
+
+ if (bValid)
+ {
+ // Count the number of visible cells (excluding the header row). Each
+ // visible cell corresponds with a single query.
+ SCSIZE nVisible = pQueryRef->getVisibleDataCellCount();
+ if ( nVisible > SCSIZE_MAX / sizeof(void*) )
+ {
+ OSL_FAIL("too many filter criteria");
+ nVisible = 0;
+ }
+
+ SCSIZE nNewEntries = nVisible;
+ pParam->Resize( nNewEntries );
+
+ SCSIZE nIndex = 0;
+ SCROW nRow = 1;
+ OUString aCellStr;
+ while (nRow < nRows)
+ {
+ nCol = 0;
+ while (nCol < nCols)
+ {
+ aCellStr = pQueryRef->getString(nCol, nRow);
+ aCellStr = ScGlobal::getCharClass().uppercase( aCellStr );
+ if (!aCellStr.isEmpty())
+ {
+ if (nIndex < nNewEntries)
+ {
+ pParam->GetEntry(nIndex).nField = aFields[nCol];
+ pParam->FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
+ nIndex++;
+ if (nIndex < nNewEntries)
+ pParam->GetEntry(nIndex).eConnect = SC_AND;
+ }
+ else
+ bValid = false;
+ }
+ nCol++;
+ }
+ nRow++;
+ if (nIndex < nNewEntries)
+ pParam->GetEntry(nIndex).eConnect = SC_OR;
+ }
+ }
+ return bValid;
+}
+
+bool lcl_fillQueryEntries(
+ const ScDocument* pDoc,
+ svl::SharedStringPool& rPool, ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef, const ScDBRangeBase* pQueryRef)
+{
+ SCSIZE nCount = pParam->GetEntryCount();
+ for (SCSIZE i = 0; i < nCount; ++i)
+ pParam->GetEntry(i).Clear();
+
+ // Standard QueryTabelle
+ bool bValid = lcl_createStarQuery(pDoc, rPool, pParam, pDBRef, pQueryRef);
+ // Excel QueryTabelle
+ if (!bValid)
+ bValid = lcl_createExcelQuery(pDoc, rPool, pParam, pDBRef, pQueryRef);
+
+ nCount = pParam->GetEntryCount();
+ if (bValid)
+ {
+ // bQueryByString must be set
+ for (SCSIZE i = 0; i < nCount; ++i)
+ pParam->GetEntry(i).GetQueryItem().meType = ScQueryEntry::ByString;
+ }
+ else
+ {
+ // nothing
+ for (SCSIZE i = 0; i < nCount; ++i)
+ pParam->GetEntry(i).Clear();
+ }
+ return bValid;
+}
+
+}
+
+ScDBRangeBase::ScDBRangeBase(ScDocument* pDoc) :
+ mpDoc(pDoc)
+{
+}
+
+ScDBRangeBase::~ScDBRangeBase()
+{
+}
+
+bool ScDBRangeBase::fillQueryEntries(ScQueryParamBase* pParam, const ScDBRangeBase* pDBRef) const
+{
+ if (!pDBRef)
+ return false;
+
+ return lcl_fillQueryEntries(getDoc(), getDoc()->GetSharedStringPool(), pParam, pDBRef, this);
+}
+
+void ScDBRangeBase::fillQueryOptions(ScQueryParamBase* pParam)
+{
+ pParam->bHasHeader = true;
+ pParam->bByRow = true;
+ pParam->bInplace = true;
+ pParam->bCaseSens = false;
+ pParam->eSearchType = utl::SearchParam::SearchType::Normal;
+ pParam->bDuplicate = true;
+}
+
+ScDBInternalRange::ScDBInternalRange(ScDocument* pDoc, const ScRange& rRange) :
+ ScDBRangeBase(pDoc), maRange(rRange)
+{
+}
+
+ScDBInternalRange::~ScDBInternalRange()
+{
+}
+
+SCCOL ScDBInternalRange::getColSize() const
+{
+ return maRange.aEnd.Col() - maRange.aStart.Col() + 1;
+}
+
+SCROW ScDBInternalRange::getRowSize() const
+{
+ return maRange.aEnd.Row() - maRange.aStart.Row() + 1;
+}
+
+SCSIZE ScDBInternalRange::getVisibleDataCellCount() const
+{
+ SCCOL nCols = getColSize();
+ SCROW nRows = getRowSize();
+ if (nRows <= 1)
+ return 0;
+
+ return (nRows-1)*nCols;
+}
+
+OUString ScDBInternalRange::getString(SCCOL nCol, SCROW nRow) const
+{
+ const ScAddress& s = maRange.aStart;
+ // #i109200# this is used in formula calculation, use GetInputString, not GetString
+ // (consistent with ScDBInternalRange::getCellString)
+ // GetStringForFormula is not used here, to allow querying for date values.
+ return getDoc()->GetInputString(s.Col() + nCol, s.Row() + nRow, maRange.aStart.Tab());
+}
+
+SCCOL ScDBInternalRange::getFirstFieldColumn() const
+{
+ return getRange().aStart.Col();
+}
+
+SCCOL ScDBInternalRange::findFieldColumn(SCCOL nIndex) const
+{
+ const ScRange& rRange = getRange();
+ const ScAddress& s = rRange.aStart;
+
+ SCCOL nDBCol1 = s.Col();
+
+ // Don't handle out-of-bound condition here. We'll do that later.
+ return nIndex + nDBCol1 - 1;
+}
+
+SCCOL ScDBInternalRange::findFieldColumn(const OUString& rStr, FormulaError* pErr) const
+{
+ const ScAddress& s = maRange.aStart;
+ const ScAddress& e = maRange.aEnd;
+ OUString aUpper = rStr;
+ lcl_uppercase(aUpper);
+
+ SCCOL nDBCol1 = s.Col();
+ SCROW nDBRow1 = s.Row();
+ SCTAB nDBTab1 = s.Tab();
+ SCCOL nDBCol2 = e.Col();
+
+ bool bFound = false;
+
+ OUString aCellStr;
+ ScAddress aLook( nDBCol1, nDBRow1, nDBTab1 );
+ while (!bFound && (aLook.Col() <= nDBCol2))
+ {
+ FormulaError nErr = getDoc()->GetStringForFormula( aLook, aCellStr );
+ if (pErr)
+ *pErr = nErr;
+ lcl_uppercase(aCellStr);
+ bFound = ScGlobal::GetTransliteration().isEqual(aCellStr, aUpper);
+ if (!bFound)
+ aLook.IncCol();
+ }
+ SCCOL nField = aLook.Col();
+
+ return bFound ? nField : -1;
+}
+
+std::unique_ptr<ScDBQueryParamBase> ScDBInternalRange::createQueryParam(const ScDBRangeBase* pQueryRef) const
+{
+ unique_ptr<ScDBQueryParamInternal> pParam(new ScDBQueryParamInternal);
+
+ // Set the database range first.
+ const ScAddress& s = maRange.aStart;
+ const ScAddress& e = maRange.aEnd;
+ pParam->nCol1 = s.Col();
+ pParam->nRow1 = s.Row();
+ pParam->nCol2 = e.Col();
+ pParam->nRow2 = e.Row();
+ pParam->nTab = s.Tab();
+
+ fillQueryOptions(pParam.get());
+
+ // Now construct the query entries from the query range.
+ if (!pQueryRef->fillQueryEntries(pParam.get(), this))
+ return nullptr;
+
+ return std::unique_ptr<ScDBQueryParamBase>(std::move(pParam));
+}
+
+bool ScDBInternalRange::isRangeEqual(const ScRange& rRange) const
+{
+ return maRange == rRange;
+}
+
+ScDBExternalRange::ScDBExternalRange(ScDocument* pDoc, ScMatrixRef pMat) :
+ ScDBRangeBase(pDoc), mpMatrix(std::move(pMat))
+{
+ SCSIZE nC, nR;
+ mpMatrix->GetDimensions(nC, nR);
+ mnCols = static_cast<SCCOL>(nC);
+ mnRows = static_cast<SCROW>(nR);
+}
+
+ScDBExternalRange::~ScDBExternalRange()
+{
+}
+
+SCCOL ScDBExternalRange::getColSize() const
+{
+ return mnCols;
+}
+
+SCROW ScDBExternalRange::getRowSize() const
+{
+ return mnRows;
+}
+
+SCSIZE ScDBExternalRange::getVisibleDataCellCount() const
+{
+ SCCOL nCols = getColSize();
+ SCROW nRows = getRowSize();
+ if (nRows <= 1)
+ return 0;
+
+ return (nRows-1)*nCols;
+}
+
+OUString ScDBExternalRange::getString(SCCOL nCol, SCROW nRow) const
+{
+ if (nCol >= mnCols || nRow >= mnRows)
+ return OUString();
+
+ return mpMatrix->GetString(nCol, nRow).getString();
+}
+
+SCCOL ScDBExternalRange::getFirstFieldColumn() const
+{
+ return 0;
+}
+
+SCCOL ScDBExternalRange::findFieldColumn(SCCOL nIndex) const
+{
+ return nIndex - 1;
+}
+
+SCCOL ScDBExternalRange::findFieldColumn(const OUString& rStr, FormulaError* pErr) const
+{
+ if (pErr)
+ *pErr = FormulaError::NONE;
+
+ OUString aUpper = rStr;
+ lcl_uppercase(aUpper);
+ for (SCCOL i = 0; i < mnCols; ++i)
+ {
+ OUString aUpperVal = mpMatrix->GetString(i, 0).getString();
+ lcl_uppercase(aUpperVal);
+ if (aUpper == aUpperVal)
+ return i;
+ }
+ return -1;
+}
+
+std::unique_ptr<ScDBQueryParamBase> ScDBExternalRange::createQueryParam(const ScDBRangeBase* pQueryRef) const
+{
+ unique_ptr<ScDBQueryParamMatrix> pParam(new ScDBQueryParamMatrix);
+ pParam->mpMatrix = mpMatrix;
+ fillQueryOptions(pParam.get());
+
+ // Now construct the query entries from the query range.
+ if (!pQueryRef->fillQueryEntries(pParam.get(), this))
+ return nullptr;
+
+ return std::unique_ptr<ScDBQueryParamBase>(std::move(pParam));
+}
+
+bool ScDBExternalRange::isRangeEqual(const ScRange& /*rRange*/) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/editdataarray.cxx b/sc/source/core/tool/editdataarray.cxx
new file mode 100644
index 0000000000..00a2b6c71d
--- /dev/null
+++ b/sc/source/core/tool/editdataarray.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 <editdataarray.hxx>
+
+ScEditDataArray::ScEditDataArray()
+{
+}
+
+ScEditDataArray::~ScEditDataArray()
+{
+}
+
+void ScEditDataArray::AddItem(SCTAB nTab, SCCOL nCol, SCROW nRow,
+ std::unique_ptr<EditTextObject> pOldData, std::unique_ptr<EditTextObject> pNewData)
+{
+ maArray.emplace_back(nTab, nCol, nRow, std::move(pOldData), std::move(pNewData));
+}
+
+const ScEditDataArray::Item* ScEditDataArray::First()
+{
+ maIter = maArray.begin();
+ if (maIter == maArray.end())
+ return nullptr;
+ return &(*maIter++);
+}
+
+const ScEditDataArray::Item* ScEditDataArray::Next()
+{
+ if (maIter == maArray.end())
+ return nullptr;
+ return &(*maIter++);
+}
+
+ScEditDataArray::Item::Item(SCTAB nTab, SCCOL nCol, SCROW nRow,
+ std::unique_ptr<EditTextObject> pOldData, std::unique_ptr<EditTextObject> pNewData) :
+ mpOldData(std::move(pOldData)),
+ mpNewData(std::move(pNewData)),
+ mnTab(nTab),
+ mnCol(nCol),
+ mnRow(nRow)
+{
+}
+
+ScEditDataArray::Item::~Item()
+{
+}
+
+const EditTextObject* ScEditDataArray::Item::GetOldData() const
+{
+ return mpOldData.get();
+}
+
+const EditTextObject* ScEditDataArray::Item::GetNewData() const
+{
+ return mpNewData.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/editutil.cxx b/sc/source/core/tool/editutil.cxx
new file mode 100644
index 0000000000..57e93d5138
--- /dev/null
+++ b/sc/source/core/tool/editutil.cxx
@@ -0,0 +1,941 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <comphelper/processfactory.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <svx/algitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/editobj.hxx>
+#include <vcl/outdev.hxx>
+#include <svl/numformat.hxx>
+#include <svl/inethist.hxx>
+#include <sfx2/objsh.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/text/textfield/Type.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <editutil.hxx>
+#include <global.hxx>
+#include <attrib.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <patattr.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <compiler.hxx>
+#include <mutex>
+
+using namespace com::sun::star;
+
+// delimiters additionally to EditEngine default:
+
+ScEditUtil::ScEditUtil( ScDocument* pDocument, SCCOL nX, SCROW nY, SCTAB nZ,
+ const Point& rCellPos,
+ OutputDevice* pDevice, double nScaleX, double nScaleY,
+ const Fraction& rX, const Fraction& rY, bool bPrintTwips ) :
+ pDoc(pDocument),nCol(nX),nRow(nY),nTab(nZ),
+ aCellPos(rCellPos),pDev(pDevice),
+ nPPTX(nScaleX),nPPTY(nScaleY),aZoomX(rX),aZoomY(rY),
+ bInPrintTwips(bPrintTwips) {}
+
+OUString ScEditUtil::ModifyDelimiters( const OUString& rOld )
+{
+ // underscore is used in function argument names
+ OUString aRet = rOld.replaceAll("_", "") +
+ "=()+-*/^&<>" +
+ ScCompiler::GetNativeSymbol(ocSep); // argument separator is localized.
+ return aRet;
+}
+
+static OUString lcl_GetDelimitedString( const EditEngine& rEngine, const char c )
+{
+ sal_Int32 nParCount = rEngine.GetParagraphCount();
+ // avoid creating a new string if possible
+ if (nParCount == 0)
+ return OUString();
+ else if (nParCount == 1)
+ return rEngine.GetText(0);
+ OUStringBuffer aRet( nParCount * 80 );
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ {
+ if (nPar > 0)
+ aRet.append(c);
+ aRet.append( rEngine.GetText( nPar ));
+ }
+ return aRet.makeStringAndClear();
+}
+
+static OUString lcl_GetDelimitedString( const EditTextObject& rEdit, const char c )
+{
+ sal_Int32 nParCount = rEdit.GetParagraphCount();
+ OUStringBuffer aRet( nParCount * 80 );
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ {
+ if (nPar > 0)
+ aRet.append(c);
+ aRet.append( rEdit.GetText( nPar ));
+ }
+ return aRet.makeStringAndClear();
+}
+
+OUString ScEditUtil::GetSpaceDelimitedString( const EditEngine& rEngine )
+{
+ return lcl_GetDelimitedString(rEngine, ' ');
+}
+OUString ScEditUtil::GetMultilineString( const EditEngine& rEngine )
+{
+ return lcl_GetDelimitedString(rEngine, '\n');
+}
+
+OUString ScEditUtil::GetMultilineString( const EditTextObject& rEdit )
+{
+ return lcl_GetDelimitedString(rEdit, '\n');
+}
+
+OUString ScEditUtil::GetString( const EditTextObject& rEditText, const ScDocument* pDoc )
+{
+ if( !rEditText.HasField())
+ return GetMultilineString( rEditText );
+
+ static std::mutex aMutex;
+ std::scoped_lock aGuard( aMutex);
+ // ScFieldEditEngine is needed to resolve field contents.
+ if (pDoc)
+ {
+ /* TODO: make ScDocument::GetEditEngine() const? Most likely it's only
+ * not const because of the pointer assignment, make that mutable, and
+ * then remove the ugly const_cast here. */
+ EditEngine& rEE = const_cast<ScDocument*>(pDoc)->GetEditEngine();
+ rEE.SetText( rEditText);
+ return GetMultilineString( rEE);
+ }
+ else
+ {
+ EditEngine& rEE = ScGlobal::GetStaticFieldEditEngine();
+ rEE.SetText( rEditText);
+ return GetMultilineString( rEE);
+ }
+}
+
+std::unique_ptr<EditTextObject> ScEditUtil::CreateURLObjectFromURL( ScDocument& rDoc, const OUString& rURL, const OUString& rText )
+{
+ SvxURLField aUrlField( rURL, rText, SvxURLFormat::AppDefault);
+ EditEngine& rEE = rDoc.GetEditEngine();
+ rEE.SetText( OUString() );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ),
+ ESelection( EE_PARA_MAX_COUNT, EE_TEXTPOS_MAX_COUNT ) );
+
+ return rEE.CreateTextObject();
+}
+
+void ScEditUtil::RemoveCharAttribs( EditTextObject& rEditText, const ScPatternAttr& rAttr )
+{
+ static const struct {
+ sal_uInt16 nAttrType;
+ sal_uInt16 nCharType;
+ } AttrTypeMap[] = {
+ { ATTR_FONT, EE_CHAR_FONTINFO },
+ { ATTR_CJK_FONT, EE_CHAR_FONTINFO_CJK },
+ { ATTR_CTL_FONT, EE_CHAR_FONTINFO_CTL },
+ { ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT },
+ { ATTR_CJK_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CJK },
+ { ATTR_CTL_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CTL },
+ { ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT },
+ { ATTR_CJK_FONT_WEIGHT, EE_CHAR_WEIGHT_CJK },
+ { ATTR_CTL_FONT_WEIGHT, EE_CHAR_WEIGHT_CTL },
+ { ATTR_FONT_POSTURE, EE_CHAR_ITALIC },
+ { ATTR_CJK_FONT_POSTURE, EE_CHAR_ITALIC_CJK },
+ { ATTR_CTL_FONT_POSTURE, EE_CHAR_ITALIC_CTL },
+ { ATTR_FONT_COLOR, EE_CHAR_COLOR },
+ { ATTR_FONT_UNDERLINE, EE_CHAR_UNDERLINE },
+ { ATTR_FONT_CROSSEDOUT, EE_CHAR_STRIKEOUT },
+ { ATTR_FONT_CONTOUR, EE_CHAR_OUTLINE },
+ { ATTR_FONT_SHADOWED, EE_CHAR_SHADOW }
+ };
+
+ const SfxItemSet& rSet = rAttr.GetItemSet();
+ const SfxPoolItem* pItem;
+ for (size_t i = 0; i < SAL_N_ELEMENTS(AttrTypeMap); ++i)
+ {
+ if ( rSet.GetItemState(AttrTypeMap[i].nAttrType, false, &pItem) == SfxItemState::SET )
+ rEditText.RemoveCharAttribs(AttrTypeMap[i].nCharType);
+ }
+}
+
+std::unique_ptr<EditTextObject> ScEditUtil::Clone( const EditTextObject& rObj, ScDocument& rDestDoc )
+{
+ std::unique_ptr<EditTextObject> pNew;
+
+ EditEngine& rEngine = rDestDoc.GetEditEngine();
+ if (rObj.HasOnlineSpellErrors())
+ {
+ EEControlBits nControl = rEngine.GetControlWord();
+ const EEControlBits nSpellControl = EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS;
+ bool bNewControl = ( (nControl & nSpellControl) != nSpellControl );
+ if (bNewControl)
+ rEngine.SetControlWord(nControl | nSpellControl);
+ rEngine.SetText(rObj);
+ pNew = rEngine.CreateTextObject();
+ if (bNewControl)
+ rEngine.SetControlWord(nControl);
+ }
+ else
+ {
+ rEngine.SetText(rObj);
+ pNew = rEngine.CreateTextObject();
+ }
+
+ return pNew;
+}
+
+OUString ScEditUtil::GetCellFieldValue(
+ const SvxFieldData& rFieldData, const ScDocument* pDoc, std::optional<Color>* ppTextColor, std::optional<FontLineStyle>* ppFldLineStyle )
+{
+ OUString aRet;
+ switch (rFieldData.GetClassId())
+ {
+ case text::textfield::Type::URL:
+ {
+ const SvxURLField& rField = static_cast<const SvxURLField&>(rFieldData);
+ const OUString& aURL = rField.GetURL();
+
+ switch (rField.GetFormat())
+ {
+ case SvxURLFormat::AppDefault: //TODO: configurable with App???
+ case SvxURLFormat::Repr:
+ aRet = rField.GetRepresentation();
+ break;
+ case SvxURLFormat::Url:
+ aRet = aURL;
+ break;
+ default:
+ ;
+ }
+
+ svtools::ColorConfigEntry eEntry =
+ INetURLHistory::GetOrCreate()->QueryUrl(aURL) ? svtools::LINKSVISITED : svtools::LINKS;
+
+ if (ppTextColor)
+ *ppTextColor = SC_MOD()->GetColorConfig().GetColorValue(eEntry).nColor;
+
+ if (ppFldLineStyle)
+ *ppFldLineStyle = FontLineStyle::LINESTYLE_SINGLE;
+ }
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ {
+ const SvxExtTimeField& rField = static_cast<const SvxExtTimeField&>(rFieldData);
+ if (pDoc)
+ aRet = rField.GetFormatted(*pDoc->GetFormatTable(), ScGlobal::eLnge);
+ else
+ {
+ /* TODO: quite expensive, we could have a global formatter? */
+ SvNumberFormatter aFormatter( comphelper::getProcessComponentContext(), ScGlobal::eLnge );
+ aRet = rField.GetFormatted(aFormatter, ScGlobal::eLnge);
+ }
+ }
+ break;
+ case text::textfield::Type::DATE:
+ {
+ Date aDate(Date::SYSTEM);
+ aRet = ScGlobal::getLocaleData().getDate(aDate);
+ }
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ {
+ if (pDoc)
+ {
+ ScDocShell* pDocShell = pDoc->GetDocumentShell();
+ if (pDocShell)
+ {
+ aRet = pDocShell->getDocProperties()->getTitle();
+ if (aRet.isEmpty())
+ aRet = pDocShell->GetTitle();
+ }
+ }
+ if (aRet.isEmpty())
+ aRet = "?";
+ }
+ break;
+ case text::textfield::Type::TABLE:
+ {
+ const SvxTableField& rField = static_cast<const SvxTableField&>(rFieldData);
+ SCTAB nTab = rField.GetTab();
+ OUString aName;
+ if (pDoc && pDoc->GetName(nTab, aName))
+ aRet = aName;
+ else
+ aRet = "?";
+ }
+ break;
+ default:
+ aRet = "?";
+ }
+
+ if (aRet.isEmpty()) // empty is yuck
+ aRet = " "; // space is default of EditEngine
+
+ return aRet;
+}
+
+tools::Long ScEditUtil::GetIndent(const ScPatternAttr* pPattern) const
+{
+ if (!pPattern)
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+
+ if ( pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue() ==
+ SvxCellHorJustify::Left )
+ {
+ tools::Long nIndent = pPattern->GetItem(ATTR_INDENT).GetValue();
+ if (!bInPrintTwips)
+ nIndent = static_cast<tools::Long>(nIndent * nPPTX);
+ return nIndent;
+ }
+
+ return 0;
+}
+
+void ScEditUtil::GetMargins(const ScPatternAttr* pPattern, tools::Long& nLeftMargin, tools::Long& nTopMargin,
+ tools::Long& nRightMargin, tools::Long& nBottomMargin) const
+{
+ if (!pPattern)
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+
+ const SvxMarginItem* pMargin = &pPattern->GetItem(ATTR_MARGIN);
+ if (!pMargin)
+ return;
+
+ nLeftMargin = bInPrintTwips ? pMargin->GetLeftMargin() : static_cast<tools::Long>(pMargin->GetLeftMargin() * nPPTX);
+ nRightMargin = bInPrintTwips ? pMargin->GetRightMargin() : static_cast<tools::Long>(pMargin->GetRightMargin() * nPPTX);
+ nTopMargin = bInPrintTwips ? pMargin->GetTopMargin() : static_cast<tools::Long>(pMargin->GetTopMargin() * nPPTY);
+ nBottomMargin = bInPrintTwips ? pMargin->GetBottomMargin() : static_cast<tools::Long>(pMargin->GetBottomMargin() * nPPTY);
+}
+
+tools::Rectangle ScEditUtil::GetEditArea( const ScPatternAttr* pPattern, bool bForceToTop )
+{
+ // bForceToTop = always align to top, for editing
+ // (sal_False for querying URLs etc.)
+
+ if (!pPattern)
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+
+ Point aStartPos = aCellPos;
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ bool bLayoutRTL = pDoc->IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = (bLayoutRTL && !bIsTiledRendering) ? -1 : 1;
+
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ tools::Long nCellX = pDoc->GetColWidth(nCol,nTab);
+ if (!bInPrintTwips)
+ nCellX = static_cast<tools::Long>( nCellX * nPPTX );
+ if ( pMerge->GetColMerge() > 1 )
+ {
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ {
+ tools::Long nColWidth = pDoc->GetColWidth(nCol+i,nTab);
+ nCellX += (bInPrintTwips ? nColWidth : static_cast<tools::Long>( nColWidth * nPPTX ));
+ }
+ }
+ tools::Long nCellY = pDoc->GetRowHeight(nRow,nTab);
+ if (!bInPrintTwips)
+ nCellY = static_cast<tools::Long>( nCellY * nPPTY );
+ if ( pMerge->GetRowMerge() > 1 )
+ {
+ SCROW nCountY = pMerge->GetRowMerge();
+ if (bInPrintTwips)
+ nCellY += pDoc->GetRowHeight(nRow + 1, nRow + nCountY - 1, nTab);
+ else
+ nCellY += pDoc->GetScaledRowHeight( nRow+1, nRow+nCountY-1, nTab, nPPTY);
+ }
+
+ tools::Long nRightMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nBottomMargin = 0;
+ tools::Long nDifX = 0;
+ {
+ tools::Long nLeftMargin = 0;
+ bool bInPrintTwipsOrig = bInPrintTwips;
+ bInPrintTwips = true;
+ tools::Long nIndent = GetIndent(pPattern);
+ GetMargins(pPattern, nLeftMargin, nTopMargin, nRightMargin, nBottomMargin);
+ bInPrintTwips = bInPrintTwipsOrig;
+ // Here rounding may be done only on the sum, ie nDifX,
+ // so need to get margin and indent in twips.
+ nDifX = nLeftMargin + nIndent;
+ if (!bInPrintTwips)
+ {
+ nDifX = static_cast<tools::Long>(nDifX * nPPTX);
+ nRightMargin = static_cast<tools::Long>(nRightMargin * nPPTX);
+ nTopMargin = static_cast<tools::Long>(nTopMargin * nPPTY);
+ nBottomMargin = static_cast<tools::Long>(nBottomMargin * nPPTY);
+ }
+ }
+
+
+ aStartPos.AdjustX(nDifX * nLayoutSign );
+ nCellX -= nDifX + nRightMargin; // due to line feed, etc.
+
+ // align vertical position to the one in the table
+
+ tools::Long nDifY;
+ SvxCellVerJustify eJust = pPattern->GetItem(ATTR_VER_JUSTIFY).GetValue();
+
+ // asian vertical is always edited top-aligned
+ bool bAsianVertical = pPattern->GetItem( ATTR_STACKED ).GetValue() &&
+ pPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue();
+
+ if ( eJust == SvxCellVerJustify::Top ||
+ ( bForceToTop && ( SC_MOD()->GetInputOptions().GetTextWysiwyg() || bAsianVertical ) ) )
+ nDifY = nTopMargin;
+ else
+ {
+ MapMode aMode = pDev->GetMapMode();
+ pDev->SetMapMode(MapMode(bInPrintTwips ? MapUnit::MapTwip : MapUnit::MapPixel));
+
+ tools::Long nTextHeight = pDoc->GetNeededSize( nCol, nRow, nTab,
+ pDev, nPPTX, nPPTY, aZoomX, aZoomY, false /* bWidth */,
+ false /* bTotalSize */, bInPrintTwips );
+ if (!nTextHeight)
+ { // empty cell
+ vcl::Font aFont;
+ // font color doesn't matter here
+ pPattern->fillFontOnly(aFont, pDev, &aZoomY );
+ pDev->SetFont(aFont);
+ nTextHeight = pDev->GetTextHeight() + nTopMargin + nBottomMargin;
+ }
+
+ pDev->SetMapMode(aMode);
+
+ if ( nTextHeight > nCellY + nTopMargin || bForceToTop )
+ nDifY = 0; // too large -> begin at the top
+ else
+ {
+ if ( eJust == SvxCellVerJustify::Center )
+ nDifY = nTopMargin + ( nCellY - nTextHeight ) / 2;
+ else
+ nDifY = nCellY - nTextHeight + nTopMargin; // JUSTIFY_BOTTOM
+ }
+ }
+
+ aStartPos.AdjustY(nDifY );
+ nCellY -= nDifY;
+
+ if ( bLayoutRTL && !bIsTiledRendering )
+ aStartPos.AdjustX( -(nCellX - 2) ); // excluding grid on both sides
+
+ // -1 -> don't overwrite grid
+ return tools::Rectangle( aStartPos, Size(nCellX-1,nCellY-1) );
+}
+
+ScEditAttrTester::ScEditAttrTester( ScEditEngineDefaulter* pEngine ) :
+ bNeedsObject( false ),
+ bNeedsCellAttr( false )
+{
+ if ( pEngine->GetParagraphCount() > 1 )
+ {
+ bNeedsObject = true; //TODO: find cell attributes ?
+ }
+ else
+ {
+ const SfxPoolItem* pItem = nullptr;
+ pEditAttrs.reset( new SfxItemSet( pEngine->GetAttribs(
+ ESelection(0,0,0,pEngine->GetTextLen(0)), EditEngineAttribs::OnlyHard ) ) );
+ const SfxItemSet& rEditDefaults = pEngine->GetDefaults();
+
+ for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bNeedsObject; nId++)
+ {
+ SfxItemState eState = pEditAttrs->GetItemState( nId, false, &pItem );
+ if (eState == SfxItemState::DONTCARE)
+ bNeedsObject = true;
+ else if (eState == SfxItemState::SET)
+ {
+ if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING ||
+ nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS )
+ {
+ // Escapement and kerning are kept in EditEngine because there are no
+ // corresponding cell format items. User defined attributes are kept in
+ // EditEngine because "user attributes applied to all the text" is different
+ // from "user attributes applied to the cell".
+
+ if ( *pItem != rEditDefaults.Get(nId) )
+ bNeedsObject = true;
+ }
+ else
+ if (!bNeedsCellAttr)
+ if ( *pItem != rEditDefaults.Get(nId) )
+ bNeedsCellAttr = true;
+ // rEditDefaults contains the defaults from the cell format
+ }
+ }
+
+ // contains field commands?
+
+ SfxItemState eFieldState = pEditAttrs->GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ bNeedsObject = true;
+
+ // not converted characters?
+
+ SfxItemState eConvState = pEditAttrs->GetItemState( EE_FEATURE_NOTCONV, false );
+ if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET )
+ bNeedsObject = true;
+ }
+}
+
+ScEditAttrTester::~ScEditAttrTester()
+{
+}
+
+ScEnginePoolHelper::ScEnginePoolHelper( SfxItemPool* pEnginePoolP,
+ bool bDeleteEnginePoolP )
+ :
+ pEnginePool( pEnginePoolP ),
+ pDefaults( nullptr ),
+ bDeleteEnginePool( bDeleteEnginePoolP ),
+ bDeleteDefaults( false )
+{
+}
+
+ScEnginePoolHelper::ScEnginePoolHelper( const ScEnginePoolHelper& rOrg )
+ :
+ pEnginePool( rOrg.bDeleteEnginePool ? rOrg.pEnginePool->Clone() : rOrg.pEnginePool ),
+ pDefaults( nullptr ),
+ bDeleteEnginePool( rOrg.bDeleteEnginePool ),
+ bDeleteDefaults( false )
+{
+}
+
+ScEnginePoolHelper::~ScEnginePoolHelper()
+{
+ if ( bDeleteDefaults )
+ delete pDefaults;
+}
+
+ScEditEngineDefaulter::ScEditEngineDefaulter( SfxItemPool* pEnginePoolP,
+ bool bDeleteEnginePoolP )
+ :
+ ScEnginePoolHelper( pEnginePoolP, bDeleteEnginePoolP ),
+ EditEngine( pEnginePoolP )
+{
+ // All EditEngines use ScGlobal::GetEditDefaultLanguage as DefaultLanguage.
+ // DefaultLanguage for InputHandler's EditEngine is updated later.
+
+ SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
+}
+
+ScEditEngineDefaulter::ScEditEngineDefaulter( const ScEditEngineDefaulter& rOrg )
+ :
+ ScEnginePoolHelper( rOrg ),
+ EditEngine( pEnginePool.get() )
+{
+ SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
+}
+
+ScEditEngineDefaulter::~ScEditEngineDefaulter()
+{
+}
+
+void ScEditEngineDefaulter::SetDefaults( const SfxItemSet& rSet, bool bRememberCopy )
+{
+ if ( bRememberCopy )
+ {
+ if ( bDeleteDefaults )
+ delete pDefaults;
+ pDefaults = new SfxItemSet( rSet );
+ bDeleteDefaults = true;
+ }
+ const SfxItemSet& rNewSet = bRememberCopy ? *pDefaults : rSet;
+ bool bUndo = IsUndoEnabled();
+ EnableUndo( false );
+ bool bUpdateMode = SetUpdateLayout( false );
+ sal_Int32 nPara = GetParagraphCount();
+ for ( sal_Int32 j=0; j<nPara; j++ )
+ {
+ SetParaAttribs( j, rNewSet );
+ }
+ if ( bUpdateMode )
+ SetUpdateLayout( true );
+ if ( bUndo )
+ EnableUndo( true );
+}
+
+void ScEditEngineDefaulter::SetDefaults( std::unique_ptr<SfxItemSet> pSet )
+{
+ if ( bDeleteDefaults )
+ delete pDefaults;
+ pDefaults = pSet.release();
+ bDeleteDefaults = true;
+ if ( pDefaults )
+ SetDefaults( *pDefaults, false );
+}
+
+void ScEditEngineDefaulter::SetDefaultItem( const SfxPoolItem& rItem )
+{
+ if ( !pDefaults )
+ {
+ pDefaults = new SfxItemSet( GetEmptyItemSet() );
+ bDeleteDefaults = true;
+ }
+ pDefaults->Put( rItem );
+ SetDefaults( *pDefaults, false );
+}
+
+const SfxItemSet& ScEditEngineDefaulter::GetDefaults()
+{
+ if ( !pDefaults )
+ {
+ pDefaults = new SfxItemSet( GetEmptyItemSet() );
+ bDeleteDefaults = true;
+ }
+ return *pDefaults;
+}
+
+void ScEditEngineDefaulter::SetTextCurrentDefaults( const EditTextObject& rTextObject )
+{
+ bool bUpdateMode = SetUpdateLayout( false );
+ SetText( rTextObject );
+ if ( pDefaults )
+ SetDefaults( *pDefaults, false );
+ if ( bUpdateMode )
+ SetUpdateLayout( true );
+}
+
+void ScEditEngineDefaulter::SetTextNewDefaults( const EditTextObject& rTextObject,
+ const SfxItemSet& rSet, bool bRememberCopy )
+{
+ bool bUpdateMode = SetUpdateLayout( false );
+ SetText( rTextObject );
+ SetDefaults( rSet, bRememberCopy );
+ if ( bUpdateMode )
+ SetUpdateLayout( true );
+}
+
+void ScEditEngineDefaulter::SetTextCurrentDefaults( const OUString& rText )
+{
+ bool bUpdateMode = SetUpdateLayout( false );
+ SetText( rText );
+ if ( pDefaults )
+ SetDefaults( *pDefaults, false );
+ if ( bUpdateMode )
+ SetUpdateLayout( true );
+}
+
+void ScEditEngineDefaulter::SetTextNewDefaults( const OUString& rText,
+ const SfxItemSet& rSet )
+{
+ bool bUpdateMode = SetUpdateLayout( false );
+ SetText( rText );
+ SetDefaults( rSet );
+ if ( bUpdateMode )
+ SetUpdateLayout( true );
+}
+
+void ScEditEngineDefaulter::RepeatDefaults()
+{
+ if ( pDefaults )
+ {
+ sal_Int32 nPara = GetParagraphCount();
+ for ( sal_Int32 j=0; j<nPara; j++ )
+ SetParaAttribs( j, *pDefaults );
+ }
+}
+
+void ScEditEngineDefaulter::RemoveParaAttribs()
+{
+ std::optional<SfxItemSet> pCharItems;
+ bool bUpdateMode = SetUpdateLayout( false );
+ sal_Int32 nParCount = GetParagraphCount();
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ {
+ const SfxItemSet& rParaAttribs = GetParaAttribs( nPar );
+ sal_uInt16 nWhich;
+ for (nWhich = EE_CHAR_START; nWhich <= EE_CHAR_END; nWhich ++)
+ {
+ const SfxPoolItem* pParaItem;
+ if ( rParaAttribs.GetItemState( nWhich, false, &pParaItem ) == SfxItemState::SET )
+ {
+ // if defaults are set, use only items that are different from default
+ if ( !pDefaults || *pParaItem != pDefaults->Get(nWhich) )
+ {
+ if (!pCharItems)
+ pCharItems.emplace( GetEmptyItemSet() );
+ pCharItems->Put( *pParaItem );
+ }
+ }
+ }
+
+ if ( pCharItems )
+ {
+ std::vector<sal_Int32> aPortions;
+ GetPortions( nPar, aPortions );
+
+ // loop through the portions of the paragraph, and set only those items
+ // that are not overridden by existing character attributes
+
+ sal_Int32 nStart = 0;
+ for ( const sal_Int32 nEnd : aPortions )
+ {
+ ESelection aSel( nPar, nStart, nPar, nEnd );
+ SfxItemSet aOldCharAttrs = GetAttribs( aSel );
+ SfxItemSet aNewCharAttrs = *pCharItems;
+ for (nWhich = EE_CHAR_START; nWhich <= EE_CHAR_END; nWhich ++)
+ {
+ // Clear those items that are different from existing character attributes.
+ // Where no character attributes are set, GetAttribs returns the paragraph attributes.
+ const SfxPoolItem* pItem;
+ if ( aNewCharAttrs.GetItemState( nWhich, false, &pItem ) == SfxItemState::SET &&
+ *pItem != aOldCharAttrs.Get(nWhich) )
+ {
+ aNewCharAttrs.ClearItem(nWhich);
+ }
+ }
+ if ( aNewCharAttrs.Count() )
+ QuickSetAttribs( aNewCharAttrs, aSel );
+
+ nStart = nEnd;
+ }
+
+ pCharItems.reset();
+ }
+
+ if ( rParaAttribs.Count() )
+ {
+ // clear all paragraph attributes (including defaults),
+ // so they are not contained in resulting EditTextObjects
+
+ SetParaAttribs( nPar, SfxItemSet( *rParaAttribs.GetPool(), rParaAttribs.GetRanges() ) );
+ }
+ }
+ if ( bUpdateMode )
+ SetUpdateLayout( true );
+}
+
+ScTabEditEngine::ScTabEditEngine( ScDocument* pDoc )
+ : ScFieldEditEngine( pDoc, pDoc->GetEnginePool() )
+{
+ SetEditTextObjectPool( pDoc->GetEditPool() );
+ Init(pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN));
+}
+
+ScTabEditEngine::ScTabEditEngine( const ScPatternAttr& rPattern,
+ SfxItemPool* pEngineItemPool, ScDocument* pDoc, SfxItemPool* pTextObjectPool )
+ : ScFieldEditEngine( pDoc, pEngineItemPool, pTextObjectPool )
+{
+ if ( pTextObjectPool )
+ SetEditTextObjectPool( pTextObjectPool );
+ Init( rPattern );
+}
+
+void ScTabEditEngine::Init( const ScPatternAttr& rPattern )
+{
+ SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ auto pEditDefaults = std::make_unique<SfxItemSet>( GetEmptyItemSet() );
+ rPattern.FillEditItemSet( pEditDefaults.get() );
+ SetDefaults( std::move(pEditDefaults) );
+ // we have no StyleSheets for text
+ SetControlWord( GetControlWord() & ~EEControlBits::RTFSTYLESHEETS );
+}
+
+// field commands for header and footer
+
+// numbers from \sw\source\core\doc\numbers.cxx
+
+static OUString lcl_GetCharStr( sal_Int32 nNo )
+{
+ OSL_ENSURE( nNo, "0 is an invalid number !!" );
+ OUString aStr;
+
+ const sal_Int32 coDiff = 'Z' - 'A' +1;
+ sal_Int32 nCalc;
+
+ do {
+ nCalc = nNo % coDiff;
+ if( !nCalc )
+ nCalc = coDiff;
+ aStr = OUStringChar( sal_Unicode('a' - 1 + nCalc) ) + aStr;
+ nNo = sal::static_int_cast<sal_Int32>( nNo - nCalc );
+ if( nNo )
+ nNo /= coDiff;
+ } while( nNo );
+ return aStr;
+}
+
+static OUString lcl_GetNumStr(sal_Int32 nNo, SvxNumType eType)
+{
+ OUString aTmpStr('0');
+ if( nNo )
+ {
+ switch( eType )
+ {
+ case css::style::NumberingType::CHARS_UPPER_LETTER:
+ case css::style::NumberingType::CHARS_LOWER_LETTER:
+ aTmpStr = lcl_GetCharStr( nNo );
+ break;
+
+ case css::style::NumberingType::ROMAN_UPPER:
+ case css::style::NumberingType::ROMAN_LOWER:
+ if( nNo < 4000 )
+ aTmpStr = SvxNumberFormat::CreateRomanString( nNo, ( eType == css::style::NumberingType::ROMAN_UPPER ) );
+ else
+ aTmpStr.clear();
+ break;
+
+ case css::style::NumberingType::NUMBER_NONE:
+ aTmpStr.clear();
+ break;
+
+// CHAR_SPECIAL:
+// ????
+
+// case ARABIC: is default now
+ default:
+ aTmpStr = OUString::number(nNo);
+ break;
+ }
+
+ if( css::style::NumberingType::CHARS_UPPER_LETTER == eType )
+ aTmpStr = aTmpStr.toAsciiUpperCase();
+ }
+ return aTmpStr;
+}
+
+ScHeaderFieldData::ScHeaderFieldData()
+ : aDateTime ( DateTime::EMPTY )
+{
+ nPageNo = nTotalPages = 0;
+ eNumType = SVX_NUM_ARABIC;
+}
+
+ScHeaderEditEngine::ScHeaderEditEngine( SfxItemPool* pEnginePoolP )
+ : ScEditEngineDefaulter( pEnginePoolP,true/*bDeleteEnginePoolP*/ )
+{
+}
+
+OUString ScHeaderEditEngine::CalcFieldValue( const SvxFieldItem& rField,
+ sal_Int32 /* nPara */, sal_Int32 /* nPos */,
+ std::optional<Color>& /* rTxtColor */, std::optional<Color>& /* rFldColor */,
+ std::optional<FontLineStyle>& /*rFldLineStyle*/ )
+{
+ const SvxFieldData* pFieldData = rField.GetField();
+ if (!pFieldData)
+ return "?";
+
+ OUString aRet;
+ sal_Int32 nClsId = pFieldData->GetClassId();
+ switch (nClsId)
+ {
+ case text::textfield::Type::PAGE:
+ aRet = lcl_GetNumStr( aData.nPageNo,aData.eNumType );
+ break;
+ case text::textfield::Type::PAGES:
+ aRet = lcl_GetNumStr( aData.nTotalPages,aData.eNumType );
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ case text::textfield::Type::TIME:
+ // For now, time field in the header / footer is always dynamic.
+ aRet = ScGlobal::getLocaleData().getTime(aData.aDateTime);
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ aRet = aData.aTitle;
+ break;
+ case text::textfield::Type::EXTENDED_FILE:
+ {
+ switch (static_cast<const SvxExtFileField*>(pFieldData)->GetFormat())
+ {
+ case SvxFileFormat::PathFull :
+ aRet = aData.aLongDocName;
+ break;
+ default:
+ aRet = aData.aShortDocName;
+ }
+ }
+ break;
+ case text::textfield::Type::TABLE:
+ aRet = aData.aTabName;
+ break;
+ case text::textfield::Type::DATE:
+ aRet = ScGlobal::getLocaleData().getDate(aData.aDateTime);
+ break;
+ default:
+ aRet = "?";
+ }
+
+ return aRet;
+}
+
+// field data
+
+ScFieldEditEngine::ScFieldEditEngine(
+ ScDocument* pDoc, SfxItemPool* pEnginePoolP,
+ SfxItemPool* pTextObjectPool, bool bDeleteEnginePoolP) :
+ ScEditEngineDefaulter( pEnginePoolP, bDeleteEnginePoolP ),
+ mpDoc(pDoc), bExecuteURL(true)
+{
+ if ( pTextObjectPool )
+ SetEditTextObjectPool( pTextObjectPool );
+ SetControlWord( EEControlBits(GetControlWord() | EEControlBits::MARKFIELDS) & ~EEControlBits::RTFSTYLESHEETS );
+}
+
+OUString ScFieldEditEngine::CalcFieldValue( const SvxFieldItem& rField,
+ sal_Int32 /* nPara */, sal_Int32 /* nPos */,
+ std::optional<Color>& rTxtColor, std::optional<Color>& /* rFldColor */,
+ std::optional<FontLineStyle>& rFldLineStyle )
+{
+ const SvxFieldData* pFieldData = rField.GetField();
+
+ if (!pFieldData)
+ return " ";
+
+ return ScEditUtil::GetCellFieldValue(*pFieldData, mpDoc, &rTxtColor, &rFldLineStyle);
+}
+
+bool ScFieldEditEngine::FieldClicked( const SvxFieldItem& rField )
+{
+ if (!bExecuteURL)
+ return false;
+
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(rField.GetField()))
+ {
+ ScGlobal::OpenURL(pURLField->GetURL(), pURLField->GetTargetFrame());
+ return true;
+ }
+ return false;
+}
+
+ScNoteEditEngine::ScNoteEditEngine( SfxItemPool* pEnginePoolP,
+ SfxItemPool* pTextObjectPool ) :
+ ScEditEngineDefaulter( pEnginePoolP, false/*bDeleteEnginePoolP*/ )
+{
+ if ( pTextObjectPool )
+ SetEditTextObjectPool( pTextObjectPool );
+ SetControlWord( EEControlBits(GetControlWord() | EEControlBits::MARKFIELDS) & ~EEControlBits::RTFSTYLESHEETS );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/filtopt.cxx b/sc/source/core/tool/filtopt.cxx
new file mode 100644
index 0000000000..13c856150d
--- /dev/null
+++ b/sc/source/core/tool/filtopt.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/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/diagnose.h>
+
+#include <filtopt.hxx>
+#include <miscuno.hxx>
+
+using namespace utl;
+using namespace css::uno;
+
+constexpr OUStringLiteral CFGPATH_FILTER = u"Office.Calc/Filter/Import";
+
+#define SCFILTOPT_WK3 2
+
+ScFilterOptions::ScFilterOptions() :
+ ConfigItem( CFGPATH_FILTER ),
+ bWK3Flag( false )
+{
+ Sequence<OUString> aNames { "MS_Excel/ColScale", // SCFILTOPT_COLSCALE
+ "MS_Excel/RowScale", // SCFILTOPT_ROWSCALE
+ "Lotus123/WK3" }; // SCFILTOPT_WK3
+ Sequence<Any> aValues = GetProperties(aNames);
+ const Any* pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ OSL_ENSURE(pValues[nProp].hasValue(), "property value missing");
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case SCFILTOPT_WK3:
+ bWK3Flag = ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] );
+ break;
+ }
+ }
+ }
+}
+
+void ScFilterOptions::ImplCommit()
+{
+ // options are never modified from office
+
+ OSL_FAIL("trying to commit changed ScFilterOptions?");
+}
+
+void ScFilterOptions::Notify( const Sequence<OUString>& /* aPropertyNames */ )
+{
+ OSL_FAIL("properties have been changed");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
new file mode 100644
index 0000000000..7b1965c2eb
--- /dev/null
+++ b/sc/source/core/tool/formulagroup.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/.
+ */
+
+#include <config_feature_opencl.h>
+
+#include <formulagroup.hxx>
+#include <formulagroupcl.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <interpre.hxx>
+#include <globalnames.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#if HAVE_FEATURE_OPENCL
+#include <opencl/platforminfo.hxx>
+#endif
+#include <sal/log.hxx>
+
+#include <cstdio>
+#include <limits>
+#include <unordered_map>
+#include <vector>
+
+#if HAVE_FEATURE_OPENCL
+# include <opencl/openclwrapper.hxx>
+#endif
+
+namespace sc {
+
+FormulaGroupEntry::FormulaGroupEntry( ScFormulaCell** pCells, size_t nRow, size_t nLength ) :
+ mpCells(pCells), mnRow(nRow), mnLength(nLength), mbShared(true) {}
+
+FormulaGroupEntry::FormulaGroupEntry( ScFormulaCell* pCell, size_t nRow ) :
+ mpCell(pCell), mnRow(nRow), mnLength(0), mbShared(false) {}
+
+size_t FormulaGroupContext::ColKey::Hash::operator ()( const FormulaGroupContext::ColKey& rKey ) const
+{
+ return rKey.mnTab * MAXCOLCOUNT_JUMBO + rKey.mnCol;
+}
+
+FormulaGroupContext::ColKey::ColKey( SCTAB nTab, SCCOL nCol ) : mnTab(nTab), mnCol(nCol) {}
+
+bool FormulaGroupContext::ColKey::operator== ( const ColKey& r ) const
+{
+ return mnTab == r.mnTab && mnCol == r.mnCol;
+}
+
+FormulaGroupContext::ColArray::ColArray( NumArrayType* pNumArray, StrArrayType* pStrArray ) :
+ mpNumArray(pNumArray), mpStrArray(pStrArray), mnSize(0)
+{
+ if (mpNumArray)
+ mnSize = mpNumArray->size();
+ else if (mpStrArray)
+ mnSize = mpStrArray->size();
+}
+
+FormulaGroupContext::ColArray* FormulaGroupContext::getCachedColArray( SCTAB nTab, SCCOL nCol, size_t nSize )
+{
+ ColArraysType::iterator itColArray = maColArrays.find(ColKey(nTab, nCol));
+ if (itColArray == maColArrays.end())
+ // Not cached for this column.
+ return nullptr;
+
+ ColArray& rCached = itColArray->second;
+ if (nSize > rCached.mnSize)
+ // Cached data array is not long enough for the requested range.
+ return nullptr;
+
+ return &rCached;
+}
+
+FormulaGroupContext::ColArray* FormulaGroupContext::setCachedColArray(
+ SCTAB nTab, SCCOL nCol, NumArrayType* pNumArray, StrArrayType* pStrArray )
+{
+ ColArraysType::iterator it = maColArrays.find(ColKey(nTab, nCol));
+ if (it == maColArrays.end())
+ {
+ std::pair<ColArraysType::iterator,bool> r =
+ maColArrays.emplace(ColKey(nTab, nCol), ColArray(pNumArray, pStrArray));
+
+ if (!r.second)
+ // Somehow the insertion failed.
+ return nullptr;
+
+ return &r.first->second;
+ }
+
+ // Prior array exists for this column. Overwrite it.
+ ColArray& rArray = it->second;
+ rArray = ColArray(pNumArray, pStrArray);
+ return &rArray;
+}
+
+void FormulaGroupContext::discardCachedColArray( SCTAB nTab, SCCOL nCol )
+{
+ ColArraysType::iterator itColArray = maColArrays.find(ColKey(nTab, nCol));
+ if (itColArray != maColArrays.end())
+ maColArrays.erase(itColArray);
+}
+
+void FormulaGroupContext::ensureStrArray( ColArray& rColArray, size_t nArrayLen )
+{
+ if (rColArray.mpStrArray)
+ return;
+
+ m_StrArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::StrArrayType>(nArrayLen, nullptr));
+ rColArray.mpStrArray = m_StrArrays.back().get();
+}
+
+void FormulaGroupContext::ensureNumArray( ColArray& rColArray, size_t nArrayLen )
+{
+ if (rColArray.mpNumArray)
+ return;
+
+ m_NumArrays.push_back(
+ std::make_unique<sc::FormulaGroupContext::NumArrayType>(nArrayLen,
+ std::numeric_limits<double>::quiet_NaN()));
+ rColArray.mpNumArray = m_NumArrays.back().get();
+}
+
+FormulaGroupContext::FormulaGroupContext()
+{
+}
+
+FormulaGroupContext::~FormulaGroupContext()
+{
+}
+
+CompiledFormula::CompiledFormula() {}
+
+CompiledFormula::~CompiledFormula() {}
+
+FormulaGroupInterpreter *FormulaGroupInterpreter::msInstance = nullptr;
+
+void FormulaGroupInterpreter::MergeCalcConfig(const ScDocument& rDoc)
+{
+ maCalcConfig = ScInterpreter::GetGlobalConfig();
+ maCalcConfig.MergeDocumentSpecific(rDoc.GetCalcConfig());
+}
+
+/// load and/or configure the correct formula group interpreter
+FormulaGroupInterpreter *FormulaGroupInterpreter::getStatic()
+{
+ if ( !msInstance )
+ {
+#if HAVE_FEATURE_OPENCL
+ if (ScCalcConfig::isOpenCLEnabled())
+ {
+ const ScCalcConfig& rConfig = ScInterpreter::GetGlobalConfig();
+ if( !switchOpenCLDevice(rConfig.maOpenCLDevice, rConfig.mbOpenCLAutoSelect))
+ {
+ if( ScCalcConfig::getForceCalculationType() == ForceCalculationOpenCL )
+ {
+ SAL_WARN( "opencl", "OpenCL forced but failed to initialize" );
+ abort();
+ }
+ }
+ }
+#endif
+ }
+
+ return msInstance;
+}
+
+#if HAVE_FEATURE_OPENCL
+void FormulaGroupInterpreter::fillOpenCLInfo(std::vector<OpenCLPlatformInfo>& rPlatforms)
+{
+ const std::vector<OpenCLPlatformInfo>& rPlatformsFromWrapper =
+ openclwrapper::fillOpenCLInfo();
+
+ rPlatforms.assign(rPlatformsFromWrapper.begin(), rPlatformsFromWrapper.end());
+}
+
+bool FormulaGroupInterpreter::switchOpenCLDevice(std::u16string_view rDeviceId, bool bAutoSelect, bool bForceEvaluation)
+{
+ bool bOpenCLEnabled = ScCalcConfig::isOpenCLEnabled();
+ if (!bOpenCLEnabled || (rDeviceId == u"" OPENCL_SOFTWARE_DEVICE_CONFIG_NAME))
+ {
+ delete msInstance;
+ msInstance = nullptr;
+ return false;
+ }
+
+ OUString aSelectedCLDeviceVersionID;
+ bool bSuccess = openclwrapper::switchOpenCLDevice(rDeviceId, bAutoSelect, bForceEvaluation, aSelectedCLDeviceVersionID);
+
+ if (!bSuccess)
+ return false;
+
+ delete msInstance;
+ msInstance = new sc::opencl::FormulaGroupInterpreterOpenCL();
+
+ return true;
+}
+
+void FormulaGroupInterpreter::getOpenCLDeviceInfo(sal_Int32& rDeviceId, sal_Int32& rPlatformId)
+{
+ rDeviceId = -1;
+ rPlatformId = -1;
+ bool bOpenCLEnabled = ScCalcConfig::isOpenCLEnabled();
+ if(!bOpenCLEnabled)
+ return;
+
+ size_t aDeviceId = static_cast<size_t>(-1);
+ size_t aPlatformId = static_cast<size_t>(-1);
+
+ openclwrapper::getOpenCLDeviceInfo(aDeviceId, aPlatformId);
+ rDeviceId = aDeviceId;
+ rPlatformId = aPlatformId;
+}
+
+void FormulaGroupInterpreter::enableOpenCL_UnitTestsOnly()
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::UseOpenCL::set(true, batch);
+ batch->commit();
+
+ ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig();
+
+ aConfig.mbOpenCLSubsetOnly = false;
+ aConfig.mnOpenCLMinimumFormulaGroupSize = 2;
+
+ ScInterpreter::SetGlobalConfig(aConfig);
+}
+
+void FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly()
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::UseOpenCL::set(false, batch);
+ batch->commit();
+}
+
+#endif
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/formulalogger.cxx b/sc/source/core/tool/formulalogger.cxx
new file mode 100644
index 0000000000..a5c01a596b
--- /dev/null
+++ b/sc/source/core/tool/formulalogger.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <formulalogger.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <tokenstringcontext.hxx>
+#include <address.hxx>
+#include <interpre.hxx>
+
+#include <osl/file.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/vectortoken.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <cstdlib>
+#include <utility>
+
+namespace sc {
+
+namespace {
+
+std::unique_ptr<osl::File> initFile()
+{
+ const char* pPath = std::getenv("LIBO_FORMULA_LOG_FILE");
+ if (!pPath)
+ return nullptr;
+
+ // Support both file:///... and system file path notations.
+ OUString aPath = OUString::createFromAscii(pPath);
+ INetURLObject aURL;
+ aURL.SetSmartURL(aPath);
+ aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ return std::make_unique<osl::File>(aPath);
+}
+
+ScRefFlags getRefFlags( const ScAddress& rCellPos, const ScAddress& rRefPos )
+{
+ ScRefFlags eFlags = ScRefFlags::VALID;
+ if (rCellPos.Tab() != rRefPos.Tab())
+ eFlags |= ScRefFlags::TAB_3D;
+ return eFlags;
+}
+
+}
+
+FormulaLogger& FormulaLogger::get()
+{
+ static FormulaLogger aLogger;
+ return aLogger;
+}
+
+struct FormulaLogger::GroupScope::Impl
+{
+ FormulaLogger& mrLogger;
+ const ScDocument& mrDoc;
+
+ OUString maPrefix;
+ std::vector<OUString> maMessages;
+
+ bool mbCalcComplete;
+ bool mbOutputEnabled;
+
+ Impl( FormulaLogger& rLogger, OUString aPrefix, const ScDocument& rDoc,
+ const ScFormulaCell& rCell, bool bOutputEnabled ) :
+ mrLogger(rLogger), mrDoc(rDoc), maPrefix(std::move(aPrefix)),
+ mbCalcComplete(false), mbOutputEnabled(bOutputEnabled)
+ {
+ ++mrLogger.mnNestLevel;
+
+ if (!mbOutputEnabled)
+ return;
+
+ sc::TokenStringContext aCxt(rDoc, rDoc.GetGrammar());
+ OUString aFormula = rCell.GetCode()->CreateString(aCxt, rCell.aPos);
+
+ mrLogger.write(maPrefix);
+ mrLogger.writeNestLevel();
+
+ mrLogger.writeAscii("-- enter (formula='");
+ mrLogger.write(aFormula);
+ mrLogger.writeAscii("', size=");
+ mrLogger.write(rCell.GetSharedLength());
+ mrLogger.writeAscii(")\n");
+ }
+
+ ~Impl()
+ {
+ if (mbOutputEnabled)
+ {
+ for (const OUString& rMsg : maMessages)
+ {
+ mrLogger.write(maPrefix);
+ mrLogger.writeNestLevel();
+ mrLogger.writeAscii(" * ");
+ mrLogger.write(rMsg);
+ mrLogger.writeAscii("\n");
+ }
+
+ mrLogger.write(maPrefix);
+ mrLogger.writeNestLevel();
+ mrLogger.writeAscii("-- exit (");
+ if (mbCalcComplete)
+ mrLogger.writeAscii("calculation complete");
+ else
+ mrLogger.writeAscii("without calculation");
+
+ mrLogger.writeAscii(")\n");
+
+ mrLogger.sync();
+ }
+
+ --mrLogger.mnNestLevel;
+ }
+};
+
+FormulaLogger::GroupScope::GroupScope(
+ FormulaLogger& rLogger, const OUString& rPrefix, const ScDocument& rDoc,
+ const ScFormulaCell& rCell, bool bOutputEnabled ) :
+ mpImpl(std::make_unique<Impl>(rLogger, rPrefix, rDoc, rCell, bOutputEnabled)) {}
+
+FormulaLogger::GroupScope::GroupScope(GroupScope&& r) noexcept : mpImpl(std::move(r.mpImpl)) {}
+
+FormulaLogger::GroupScope::~GroupScope() {}
+
+void FormulaLogger::GroupScope::addMessage( const OUString& rMsg )
+{
+ mpImpl->maMessages.push_back(rMsg);
+}
+
+void FormulaLogger::GroupScope::addRefMessage(
+ const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
+ const formula::VectorRefArray& rArray )
+{
+ ScRange aRefRange(rRefPos);
+ aRefRange.aEnd.IncRow(nLen-1);
+ OUString aRangeStr = aRefRange.Format(mpImpl->mrDoc, getRefFlags(rCellPos, rRefPos));
+
+ std::u16string_view aMsg;
+ if (rArray.mpNumericArray)
+ {
+ if (rArray.mpStringArray)
+ {
+ // mixture of numeric and string cells.
+ aMsg = u"numeric and string";
+ }
+ else
+ {
+ // numeric cells only.
+ aMsg = u"numeric only";
+ }
+ }
+ else
+ {
+ if (rArray.mpStringArray)
+ {
+ // string cells only.
+ aMsg = u"string only";
+ }
+ else
+ {
+ // empty cells.
+ aMsg = u"empty";
+ }
+ }
+
+ mpImpl->maMessages.push_back(aRangeStr + ": " + aMsg);
+}
+
+void FormulaLogger::GroupScope::addRefMessage(
+ const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
+ const std::vector<formula::VectorRefArray>& rArrays )
+{
+ ScAddress aPos(rRefPos); // copy
+ for (const formula::VectorRefArray& rArray : rArrays)
+ {
+ addRefMessage(rCellPos, aPos, nLen, rArray);
+ aPos.IncCol();
+ }
+}
+
+void FormulaLogger::GroupScope::addRefMessage(
+ const ScAddress& rCellPos, const ScAddress& rRefPos,
+ const formula::FormulaToken& rToken )
+{
+ OUString aPosStr = rRefPos.Format(getRefFlags(rCellPos, rRefPos), &mpImpl->mrDoc);
+ std::u16string_view aMsg;
+ switch (rToken.GetType())
+ {
+ case formula::svDouble:
+ aMsg = u"numeric value";
+ break;
+ case formula::svString:
+ aMsg = u"string value";
+ break;
+ default:
+ aMsg = u"unknown value";
+ }
+
+ mpImpl->maMessages.push_back(aPosStr + ": " + aMsg);
+}
+
+void FormulaLogger::GroupScope::addGroupSizeThresholdMessage( const ScFormulaCell& rCell )
+{
+ OUString aBuf = "group length below minimum threshold ("
+ + OUString::number(rCell.GetWeight())
+ + " < "
+ + OUString::number(ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize)
+ + ")";
+ mpImpl->maMessages.push_back(aBuf);
+}
+
+void FormulaLogger::GroupScope::setCalcComplete()
+{
+ mpImpl->mbCalcComplete = true;
+ addMessage("calculation performed");
+}
+
+FormulaLogger::FormulaLogger()
+{
+ mpLogFile = initFile();
+
+ if (!mpLogFile)
+ return;
+
+ osl::FileBase::RC eRC = mpLogFile->open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
+
+ if (eRC == osl::FileBase::E_EXIST)
+ {
+ eRC = mpLogFile->open(osl_File_OpenFlag_Write);
+
+ if (eRC != osl::FileBase::E_None)
+ {
+ // Failed to open an existing log file.
+ mpLogFile.reset();
+ return;
+ }
+
+ if (mpLogFile->setPos(osl_Pos_End, 0) != osl::FileBase::E_None)
+ {
+ // Failed to set the position to the end of the file.
+ mpLogFile.reset();
+ return;
+ }
+ }
+ else if (eRC != osl::FileBase::E_None)
+ {
+ // Failed to create a new file.
+ mpLogFile.reset();
+ return;
+ }
+
+ // Output the header information.
+ writeAscii("---\n");
+ writeAscii("OpenCL: ");
+ writeAscii(ScCalcConfig::isOpenCLEnabled() ? "enabled\n" : "disabled\n");
+ writeAscii("---\n");
+
+ sync();
+}
+
+FormulaLogger::~FormulaLogger()
+{
+ if (mpLogFile)
+ mpLogFile->close();
+}
+
+void FormulaLogger::writeAscii( const char* s )
+{
+ if (!mpLogFile)
+ return;
+
+ sal_uInt64 nBytes;
+ mpLogFile->write(s, strlen(s), nBytes);
+}
+
+void FormulaLogger::writeAscii( const char* s, size_t n )
+{
+ if (!mpLogFile)
+ return;
+
+ sal_uInt64 nBytes;
+ mpLogFile->write(s, n, nBytes);
+}
+
+void FormulaLogger::write( std::u16string_view ou )
+{
+ OString s = OUStringToOString(ou, RTL_TEXTENCODING_UTF8);
+ writeAscii(s.getStr(), s.getLength());
+}
+
+void FormulaLogger::write( sal_Int32 n )
+{
+ OString s = OString::number(n);
+ writeAscii(s.getStr(), s.getLength());
+}
+
+void FormulaLogger::sync()
+{
+ if (!mpLogFile)
+ return;
+
+ mpLogFile->sync();
+}
+
+void FormulaLogger::writeNestLevel()
+{
+ // Write the nest level, but keep it only 1-character length to avoid
+ // messing up the spacing.
+ if (mnNestLevel < 10)
+ write(mnNestLevel);
+ else
+ writeAscii("!");
+
+ writeAscii(": ");
+ for (sal_Int32 i = 1; i < mnNestLevel; ++i)
+ writeAscii(" ");
+}
+
+FormulaLogger::GroupScope FormulaLogger::enterGroup(
+ const ScDocument& rDoc, const ScFormulaCell& rCell )
+{
+ // Get the file name if available.
+ const ScDocShell* pShell = rDoc.GetDocumentShell();
+ const SfxMedium* pMedium = pShell ? pShell->GetMedium() : nullptr;
+ OUString aName;
+ if (pMedium)
+ aName = pMedium->GetURLObject().GetLastName();
+ if (aName.isEmpty())
+ aName = "-"; // unsaved document.
+
+ OUString aGroupPrefix = aName + ": formula-group: " +
+ rCell.aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc, rDoc.GetAddressConvention()) + ": ";
+
+ bool bOutputEnabled = mpLastGroup != rCell.GetCellGroup().get();
+ mpLastGroup = rCell.GetCellGroup().get();
+
+ return GroupScope(*this, aGroupPrefix, rDoc, rCell, bOutputEnabled);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/formulaopt.cxx b/sc/source/core/tool/formulaopt.cxx
new file mode 100644
index 0000000000..08c660a44a
--- /dev/null
+++ b/sc/source/core/tool/formulaopt.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/.
+ */
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <formulaopt.hxx>
+#include <global.hxx>
+#include <formulagroup.hxx>
+#include <sc.hrc>
+#include <utility>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+namespace lang = ::com::sun::star::lang;
+
+
+ScFormulaOptions::ScFormulaOptions()
+{
+ SetDefaults();
+}
+
+void ScFormulaOptions::SetDefaults()
+{
+ bUseEnglishFuncName = false;
+ eFormulaGrammar = ::formula::FormulaGrammar::GRAM_NATIVE;
+ mbWriteCalcConfig = true;
+ meOOXMLRecalc = RECALC_ASK;
+ meODFRecalc = RECALC_ASK;
+
+ // unspecified means use the current formula syntax.
+ aCalcConfig.reset();
+
+ ResetFormulaSeparators();
+}
+
+void ScFormulaOptions::ResetFormulaSeparators()
+{
+ GetDefaultFormulaSeparators(aFormulaSepArg, aFormulaSepArrayCol, aFormulaSepArrayRow);
+}
+
+void ScFormulaOptions::GetDefaultFormulaSeparators(
+ OUString& rSepArg, OUString& rSepArrayCol, OUString& rSepArrayRow)
+{
+ // Defaults to the old separator values.
+ rSepArg = ";";
+ rSepArrayCol = ";";
+ rSepArrayRow = "|";
+
+ const lang::Locale& rLocale = ScGlobal::GetLocale();
+ const OUString& rLang = rLocale.Language;
+ if (rLang == "ru")
+ // Don't do automatic guess for these languages, and fall back to
+ // the old separator set.
+ return;
+
+ const LocaleDataWrapper& rLocaleData = ScGlobal::getLocaleData();
+ const OUString& rDecSep = rLocaleData.getNumDecimalSep();
+ const OUString& rListSep = rLocaleData.getListSep();
+
+ if (rDecSep.isEmpty() || rListSep.isEmpty())
+ // Something is wrong. Stick with the default separators.
+ return;
+
+ sal_Unicode cDecSep = rDecSep[0];
+ sal_Unicode cListSep = rListSep[0];
+ sal_Unicode cDecSepAlt = rLocaleData.getNumDecimalSepAlt().toChar(); // usually 0 (empty)
+
+ // Excel by default uses system's list separator as the parameter
+ // separator, which in English locales is a comma. However, OOo's list
+ // separator value is set to ';' for all English locales. Because of this
+ // discrepancy, we will hardcode the separator value here, for now.
+ // Similar for decimal separator alternative.
+ // However, if the decimal separator alternative is '.' and the decimal
+ // separator is ',' this makes no sense, fall back to ';' in that case.
+ if (cDecSep == '.' || (cDecSepAlt == '.' && cDecSep != ','))
+ cListSep = ',';
+ else if (cDecSep == ',' && cDecSepAlt == '.')
+ cListSep = ';';
+
+ // Special case for de_CH locale.
+ if (rLocale.Language == "de" && rLocale.Country == "CH")
+ cListSep = ';';
+
+ // by default, the parameter separator equals the locale-specific
+ // list separator.
+ rSepArg = OUString(cListSep);
+
+ if (cDecSep == cListSep && cDecSep != ';')
+ // if the decimal and list separators are equal, set the
+ // parameter separator to be ';', unless they are both
+ // semicolon in which case don't change the decimal separator.
+ rSepArg = ";";
+
+ rSepArrayCol = ",";
+ if (cDecSep == ',')
+ rSepArrayCol = ".";
+ rSepArrayRow = ";";
+}
+
+bool ScFormulaOptions::operator==( const ScFormulaOptions& rOpt ) const
+{
+ return bUseEnglishFuncName == rOpt.bUseEnglishFuncName
+ && eFormulaGrammar == rOpt.eFormulaGrammar
+ && aCalcConfig == rOpt.aCalcConfig
+ && mbWriteCalcConfig == rOpt.mbWriteCalcConfig
+ && aFormulaSepArg == rOpt.aFormulaSepArg
+ && aFormulaSepArrayRow == rOpt.aFormulaSepArrayRow
+ && aFormulaSepArrayCol == rOpt.aFormulaSepArrayCol
+ && meOOXMLRecalc == rOpt.meOOXMLRecalc
+ && meODFRecalc == rOpt.meODFRecalc;
+}
+
+bool ScFormulaOptions::operator!=( const ScFormulaOptions& rOpt ) const
+{
+ return !(operator==(rOpt));
+}
+
+ScTpFormulaItem::ScTpFormulaItem( ScFormulaOptions aOpt ) :
+ SfxPoolItem ( SID_SCFORMULAOPTIONS ),
+ theOptions (std::move( aOpt ))
+{
+}
+
+ScTpFormulaItem::~ScTpFormulaItem()
+{
+}
+
+bool ScTpFormulaItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScTpFormulaItem& rPItem = static_cast<const ScTpFormulaItem&>(rItem);
+ return ( theOptions == rPItem.theOptions );
+}
+
+ScTpFormulaItem* ScTpFormulaItem::Clone( SfxItemPool * ) const
+{
+ return new ScTpFormulaItem( *this );
+}
+
+constexpr OUStringLiteral CFGPATH_FORMULA = u"Office.Calc/Formula";
+
+#define SCFORMULAOPT_GRAMMAR 0
+#define SCFORMULAOPT_ENGLISH_FUNCNAME 1
+#define SCFORMULAOPT_SEP_ARG 2
+#define SCFORMULAOPT_SEP_ARRAY_ROW 3
+#define SCFORMULAOPT_SEP_ARRAY_COL 4
+#define SCFORMULAOPT_STRING_REF_SYNTAX 5
+#define SCFORMULAOPT_STRING_CONVERSION 6
+#define SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO 7
+#define SCFORMULAOPT_OOXML_RECALC 8
+#define SCFORMULAOPT_ODF_RECALC 9
+#define SCFORMULAOPT_OPENCL_AUTOSELECT 10
+#define SCFORMULAOPT_OPENCL_DEVICE 11
+#define SCFORMULAOPT_OPENCL_SUBSET_ONLY 12
+#define SCFORMULAOPT_OPENCL_MIN_SIZE 13
+#define SCFORMULAOPT_OPENCL_SUBSET_OPS 14
+
+Sequence<OUString> ScFormulaCfg::GetPropertyNames()
+{
+ return {"Syntax/Grammar", // SCFORMULAOPT_GRAMMAR
+ "Syntax/EnglishFunctionName", // SCFORMULAOPT_ENGLISH_FUNCNAME
+ "Syntax/SeparatorArg", // SCFORMULAOPT_SEP_ARG
+ "Syntax/SeparatorArrayRow", // SCFORMULAOPT_SEP_ARRAY_ROW
+ "Syntax/SeparatorArrayCol", // SCFORMULAOPT_SEP_ARRAY_COL
+ "Syntax/StringRefAddressSyntax", // SCFORMULAOPT_STRING_REF_SYNTAX
+ "Syntax/StringConversion", // SCFORMULAOPT_STRING_CONVERSION
+ "Syntax/EmptyStringAsZero", // SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO
+ "Load/OOXMLRecalcMode", // SCFORMULAOPT_OOXML_RECALC
+ "Load/ODFRecalcMode", // SCFORMULAOPT_ODF_RECALC
+ "Calculation/OpenCLAutoSelect", // SCFORMULAOPT_OPENCL_AUTOSELECT
+ "Calculation/OpenCLDevice", // SCFORMULAOPT_OPENCL_DEVICE
+ "Calculation/OpenCLSubsetOnly", // SCFORMULAOPT_OPENCL_SUBSET_ONLY
+ "Calculation/OpenCLMinimumDataSize", // SCFORMULAOPT_OPENCL_MIN_SIZE
+ "Calculation/OpenCLSubsetOpCodes"}; // SCFORMULAOPT_OPENCL_SUBSET_OPS
+}
+
+ScFormulaCfg::PropsToIds ScFormulaCfg::GetPropNamesToId()
+{
+ Sequence<OUString> aPropNames = GetPropertyNames();
+ static sal_uInt16 aVals[] = {
+ SCFORMULAOPT_GRAMMAR,
+ SCFORMULAOPT_ENGLISH_FUNCNAME,
+ SCFORMULAOPT_SEP_ARG,
+ SCFORMULAOPT_SEP_ARRAY_ROW,
+ SCFORMULAOPT_SEP_ARRAY_COL,
+ SCFORMULAOPT_STRING_REF_SYNTAX,
+ SCFORMULAOPT_STRING_CONVERSION,
+ SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO,
+ SCFORMULAOPT_OOXML_RECALC,
+ SCFORMULAOPT_ODF_RECALC,
+ SCFORMULAOPT_OPENCL_AUTOSELECT,
+ SCFORMULAOPT_OPENCL_DEVICE,
+ SCFORMULAOPT_OPENCL_SUBSET_ONLY,
+ SCFORMULAOPT_OPENCL_MIN_SIZE,
+ SCFORMULAOPT_OPENCL_SUBSET_OPS,
+ };
+ OSL_ENSURE( SAL_N_ELEMENTS(aVals) == aPropNames.getLength(), "Properties and ids are out of Sync");
+ PropsToIds aPropIdMap;
+ for ( sal_Int32 i=0; i<aPropNames.getLength(); ++i )
+ aPropIdMap[aPropNames[i]] = aVals[ i ];
+ return aPropIdMap;
+}
+
+ScFormulaCfg::ScFormulaCfg() :
+ ConfigItem( CFGPATH_FORMULA )
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ UpdateFromProperties( aNames );
+ EnableNotification( aNames );
+}
+
+void ScFormulaCfg::UpdateFromProperties( const Sequence<OUString>& aNames )
+{
+ Sequence<Any> aValues = GetProperties(aNames);
+ const Any* pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ PropsToIds aPropMap = GetPropNamesToId();
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ sal_Int32 nIntVal = 0;
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ PropsToIds::iterator it_end = aPropMap.end();
+ PropsToIds::iterator it = aPropMap.find( aNames[nProp] );
+ if(pValues[nProp].hasValue() && it != it_end )
+ {
+ switch(it->second)
+ {
+ case SCFORMULAOPT_GRAMMAR:
+ {
+ // Get default value in case this option is not set.
+ ::formula::FormulaGrammar::Grammar eGram = GetFormulaSyntax();
+
+ do
+ {
+ if (!(pValues[nProp] >>= nIntVal))
+ // extracting failed.
+ break;
+
+ switch (nIntVal)
+ {
+ case 0: // Calc A1
+ eGram = ::formula::FormulaGrammar::GRAM_NATIVE;
+ break;
+ case 1: // Excel A1
+ eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1;
+ break;
+ case 2: // Excel R1C1
+ eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1;
+ break;
+ default:
+ ;
+ }
+ }
+ while (false);
+ SetFormulaSyntax(eGram);
+ }
+ break;
+ case SCFORMULAOPT_ENGLISH_FUNCNAME:
+ {
+ bool bEnglish = false;
+ if (pValues[nProp] >>= bEnglish)
+ SetUseEnglishFuncName(bEnglish);
+ }
+ break;
+ case SCFORMULAOPT_SEP_ARG:
+ {
+ OUString aSep;
+ if ((pValues[nProp] >>= aSep) && !aSep.isEmpty())
+ SetFormulaSepArg(aSep);
+ }
+ break;
+ case SCFORMULAOPT_SEP_ARRAY_ROW:
+ {
+ OUString aSep;
+ if ((pValues[nProp] >>= aSep) && !aSep.isEmpty())
+ SetFormulaSepArrayRow(aSep);
+ }
+ break;
+ case SCFORMULAOPT_SEP_ARRAY_COL:
+ {
+ OUString aSep;
+ if ((pValues[nProp] >>= aSep) && !aSep.isEmpty())
+ SetFormulaSepArrayCol(aSep);
+ }
+ break;
+ case SCFORMULAOPT_STRING_REF_SYNTAX:
+ {
+ // Get default value in case this option is not set.
+ ::formula::FormulaGrammar::AddressConvention eConv = GetCalcConfig().meStringRefAddressSyntax;
+
+ do
+ {
+ if (!(pValues[nProp] >>= nIntVal))
+ // extraction failed.
+ break;
+
+ switch (nIntVal)
+ {
+ case -1: // Same as the formula grammar.
+ eConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
+ break;
+ case 0: // Calc A1
+ eConv = formula::FormulaGrammar::CONV_OOO;
+ break;
+ case 1: // Excel A1
+ eConv = formula::FormulaGrammar::CONV_XL_A1;
+ break;
+ case 2: // Excel R1C1
+ eConv = formula::FormulaGrammar::CONV_XL_R1C1;
+ break;
+ case 3: // Calc A1 | Excel A1
+ eConv = formula::FormulaGrammar::CONV_A1_XL_A1;
+ break;
+ default:
+ ;
+ }
+ }
+ while (false);
+ GetCalcConfig().meStringRefAddressSyntax = eConv;
+ }
+ break;
+ case SCFORMULAOPT_STRING_CONVERSION:
+ {
+ // Get default value in case this option is not set.
+ ScCalcConfig::StringConversion eConv = GetCalcConfig().meStringConversion;
+
+ do
+ {
+ if (!(pValues[nProp] >>= nIntVal))
+ // extraction failed.
+ break;
+
+ switch (nIntVal)
+ {
+ case 0:
+ eConv = ScCalcConfig::StringConversion::ILLEGAL;
+ break;
+ case 1:
+ eConv = ScCalcConfig::StringConversion::ZERO;
+ break;
+ case 2:
+ eConv = ScCalcConfig::StringConversion::UNAMBIGUOUS;
+ break;
+ case 3:
+ eConv = ScCalcConfig::StringConversion::LOCALE;
+ break;
+ default:
+ SAL_WARN("sc", "unknown string conversion option!");
+ }
+ }
+ while (false);
+ GetCalcConfig().meStringConversion = eConv;
+ }
+ break;
+ case SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO:
+ {
+ bool bVal = GetCalcConfig().mbEmptyStringAsZero;
+ pValues[nProp] >>= bVal;
+ GetCalcConfig().mbEmptyStringAsZero = bVal;
+ }
+ break;
+ case SCFORMULAOPT_OOXML_RECALC:
+ {
+ ScRecalcOptions eOpt = RECALC_ASK;
+ if (pValues[nProp] >>= nIntVal)
+ {
+ switch (nIntVal)
+ {
+ case 0:
+ eOpt = RECALC_ALWAYS;
+ break;
+ case 1:
+ eOpt = RECALC_NEVER;
+ break;
+ case 2:
+ eOpt = RECALC_ASK;
+ break;
+ default:
+ SAL_WARN("sc", "unknown ooxml recalc option!");
+ }
+ }
+
+ SetOOXMLRecalcOptions(eOpt);
+ }
+ break;
+ case SCFORMULAOPT_ODF_RECALC:
+ {
+ ScRecalcOptions eOpt = RECALC_ASK;
+ if (pValues[nProp] >>= nIntVal)
+ {
+ switch (nIntVal)
+ {
+ case 0:
+ eOpt = RECALC_ALWAYS;
+ break;
+ case 1:
+ eOpt = RECALC_NEVER;
+ break;
+ case 2:
+ eOpt = RECALC_ASK;
+ break;
+ default:
+ SAL_WARN("sc", "unknown odf recalc option!");
+ }
+ }
+
+ SetODFRecalcOptions(eOpt);
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_AUTOSELECT:
+ {
+ bool bVal = GetCalcConfig().mbOpenCLAutoSelect;
+ pValues[nProp] >>= bVal;
+ GetCalcConfig().mbOpenCLAutoSelect = bVal;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_DEVICE:
+ {
+ OUString aOpenCLDevice = GetCalcConfig().maOpenCLDevice;
+ pValues[nProp] >>= aOpenCLDevice;
+ GetCalcConfig().maOpenCLDevice = aOpenCLDevice;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_SUBSET_ONLY:
+ {
+ bool bVal = GetCalcConfig().mbOpenCLSubsetOnly;
+ pValues[nProp] >>= bVal;
+ GetCalcConfig().mbOpenCLSubsetOnly = bVal;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_MIN_SIZE:
+ {
+ sal_Int32 nVal = GetCalcConfig().mnOpenCLMinimumFormulaGroupSize;
+ pValues[nProp] >>= nVal;
+ GetCalcConfig().mnOpenCLMinimumFormulaGroupSize = nVal;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_SUBSET_OPS:
+ {
+ OUString sVal = ScOpCodeSetToSymbolicString(GetCalcConfig().mpOpenCLSubsetOpCodes);
+ pValues[nProp] >>= sVal;
+ GetCalcConfig().mpOpenCLSubsetOpCodes = ScStringToOpCodeSet(sVal);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ScFormulaCfg::ImplCommit()
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ Sequence<Any> aOldValues = GetProperties(aNames);
+ Any* pOldValues = aOldValues.getArray();
+
+ bool bSetOpenCL = false;
+
+ for (int nProp = 0; nProp < aNames.getLength(); ++nProp)
+ {
+ switch (nProp)
+ {
+ case SCFORMULAOPT_GRAMMAR :
+ {
+ sal_Int32 nVal = 0;
+ switch (GetFormulaSyntax())
+ {
+ case ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1: nVal = 1; break;
+ case ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1: nVal = 2; break;
+ default: break;
+ }
+ pValues[nProp] <<= nVal;
+ }
+ break;
+ case SCFORMULAOPT_ENGLISH_FUNCNAME:
+ {
+ bool b = GetUseEnglishFuncName();
+ pValues[nProp] <<= b;
+ }
+ break;
+ case SCFORMULAOPT_SEP_ARG:
+ pValues[nProp] <<= GetFormulaSepArg();
+ break;
+ case SCFORMULAOPT_SEP_ARRAY_ROW:
+ pValues[nProp] <<= GetFormulaSepArrayRow();
+ break;
+ case SCFORMULAOPT_SEP_ARRAY_COL:
+ pValues[nProp] <<= GetFormulaSepArrayCol();
+ break;
+ case SCFORMULAOPT_STRING_REF_SYNTAX:
+ {
+ sal_Int32 nVal = -1;
+
+ if (GetWriteCalcConfig())
+ {
+ switch (GetCalcConfig().meStringRefAddressSyntax)
+ {
+ case ::formula::FormulaGrammar::CONV_OOO: nVal = 0; break;
+ case ::formula::FormulaGrammar::CONV_XL_A1: nVal = 1; break;
+ case ::formula::FormulaGrammar::CONV_XL_R1C1: nVal = 2; break;
+ case ::formula::FormulaGrammar::CONV_A1_XL_A1: nVal = 3; break;
+ default: break;
+ }
+ pValues[nProp] <<= nVal;
+ }
+ else
+ {
+ pValues[nProp] = pOldValues[nProp];
+ }
+ }
+ break;
+ case SCFORMULAOPT_STRING_CONVERSION:
+ {
+ if (GetWriteCalcConfig())
+ {
+ sal_Int32 nVal = 3;
+
+ switch (GetCalcConfig().meStringConversion)
+ {
+ case ScCalcConfig::StringConversion::ILLEGAL: nVal = 0; break;
+ case ScCalcConfig::StringConversion::ZERO: nVal = 1; break;
+ case ScCalcConfig::StringConversion::UNAMBIGUOUS: nVal = 2; break;
+ case ScCalcConfig::StringConversion::LOCALE: nVal = 3; break;
+ }
+ pValues[nProp] <<= nVal;
+ }
+ else
+ {
+ pValues[nProp] = pOldValues[nProp];
+ }
+ }
+ break;
+ case SCFORMULAOPT_EMPTY_OUSTRING_AS_ZERO:
+ {
+ if (GetWriteCalcConfig())
+ {
+ bool bVal = GetCalcConfig().mbEmptyStringAsZero;
+ pValues[nProp] <<= bVal;
+ }
+ else
+ {
+ pValues[nProp] = pOldValues[nProp];
+ }
+ }
+ break;
+ case SCFORMULAOPT_OOXML_RECALC:
+ {
+ sal_Int32 nVal = 2;
+ switch (GetOOXMLRecalcOptions())
+ {
+ case RECALC_ALWAYS:
+ nVal = 0;
+ break;
+ case RECALC_NEVER:
+ nVal = 1;
+ break;
+ case RECALC_ASK:
+ nVal = 2;
+ break;
+ }
+
+ pValues[nProp] <<= nVal;
+ }
+ break;
+ case SCFORMULAOPT_ODF_RECALC:
+ {
+ sal_Int32 nVal = 2;
+ switch (GetODFRecalcOptions())
+ {
+ case RECALC_ALWAYS:
+ nVal = 0;
+ break;
+ case RECALC_NEVER:
+ nVal = 1;
+ break;
+ case RECALC_ASK:
+ nVal = 2;
+ break;
+ }
+
+ pValues[nProp] <<= nVal;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_AUTOSELECT:
+ {
+ bool bVal = GetCalcConfig().mbOpenCLAutoSelect;
+ pValues[nProp] <<= bVal;
+ bSetOpenCL = true;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_DEVICE:
+ {
+ OUString aOpenCLDevice = GetCalcConfig().maOpenCLDevice;
+ pValues[nProp] <<= aOpenCLDevice;
+ bSetOpenCL = true;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_SUBSET_ONLY:
+ {
+ bool bVal = GetCalcConfig().mbOpenCLSubsetOnly;
+ pValues[nProp] <<= bVal;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_MIN_SIZE:
+ {
+ sal_Int32 nVal = GetCalcConfig().mnOpenCLMinimumFormulaGroupSize;
+ pValues[nProp] <<= nVal;
+ }
+ break;
+ case SCFORMULAOPT_OPENCL_SUBSET_OPS:
+ {
+ OUString sVal = ScOpCodeSetToSymbolicString(GetCalcConfig().mpOpenCLSubsetOpCodes);
+ pValues[nProp] <<= sVal;
+ }
+ break;
+ }
+ }
+#if !HAVE_FEATURE_OPENCL
+ (void) bSetOpenCL;
+#else
+ if(bSetOpenCL)
+ sc::FormulaGroupInterpreter::switchOpenCLDevice(
+ GetCalcConfig().maOpenCLDevice, GetCalcConfig().mbOpenCLAutoSelect);
+#endif
+ PutProperties(aNames, aValues);
+}
+
+void ScFormulaCfg::SetOptions( const ScFormulaOptions& rNew )
+{
+ *static_cast<ScFormulaOptions*>(this) = rNew;
+ SetModified();
+}
+
+void ScFormulaCfg::Notify( const css::uno::Sequence< OUString >& rNames)
+{
+ UpdateFromProperties( rNames );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/formulaparserpool.cxx b/sc/source/core/tool/formulaparserpool.cxx
new file mode 100644
index 0000000000..6ac873042d
--- /dev/null
+++ b/sc/source/core/tool/formulaparserpool.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 <formulaparserpool.hxx>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/sheet/XFilterFormulaParser.hpp>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/objsh.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class ScParserFactoryMap
+{
+public:
+ explicit ScParserFactoryMap();
+
+ Reference< XFormulaParser > createFormulaParser(
+ const Reference< XComponent >& rxComponent,
+ const OUString& rNamespace );
+
+private:
+ typedef std::unordered_map<
+ OUString, Reference< XSingleComponentFactory > > FactoryMap;
+
+ Reference< XComponentContext > mxContext; /// Global component context.
+ FactoryMap maFactories; /// All parser factories, mapped by formula namespace.
+};
+
+ScParserFactoryMap::ScParserFactoryMap() :
+ mxContext( ::comphelper::getProcessComponentContext() )
+{
+ if( !mxContext.is() )
+ return;
+
+ try
+ {
+ // enumerate all implementations of the FormulaParser service
+ Reference< XContentEnumerationAccess > xFactoryEA( mxContext->getServiceManager(), UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnum( xFactoryEA->createContentEnumeration( "com.sun.star.sheet.FilterFormulaParser" ), UNO_SET_THROW );
+ while( xEnum->hasMoreElements() ) try // single try/catch for every element
+ {
+ // create an instance of the formula parser implementation
+ Reference< XSingleComponentFactory > xCompFactory( xEnum->nextElement(), UNO_QUERY_THROW );
+ Reference< XFilterFormulaParser > xParser( xCompFactory->createInstanceWithContext( mxContext ), UNO_QUERY_THROW );
+
+ // store factory in the map
+ OUString aNamespace = xParser->getSupportedNamespace();
+ if( !aNamespace.isEmpty() )
+ maFactories[ aNamespace ] = xCompFactory;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+Reference< XFormulaParser > ScParserFactoryMap::createFormulaParser(
+ const Reference< XComponent >& rxComponent, const OUString& rNamespace )
+{
+ Reference< XFormulaParser > xParser;
+ FactoryMap::const_iterator aIt = maFactories.find( rNamespace );
+ if( aIt != maFactories.end() ) try
+ {
+ Sequence< Any > aArgs{ Any(rxComponent) };
+ xParser.set( aIt->second->createInstanceWithArgumentsAndContext( aArgs, mxContext ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ }
+ return xParser;
+}
+
+} // namespace
+
+ScFormulaParserPool::ScFormulaParserPool( const ScDocument& rDoc ) :
+ mrDoc( rDoc )
+{
+}
+
+ScFormulaParserPool::~ScFormulaParserPool()
+{
+}
+
+bool ScFormulaParserPool::hasFormulaParser( const OUString& rNamespace )
+{
+ return getFormulaParser( rNamespace ).is();
+}
+
+Reference< XFormulaParser > ScFormulaParserPool::getFormulaParser( const OUString& rNamespace )
+{
+ // try to find an existing parser entry
+ ParserMap::iterator aIt = maParsers.find( rNamespace );
+ if( aIt != maParsers.end() )
+ return aIt->second;
+
+ // always create a new entry in the map (even if the following initialization fails)
+ Reference< XFormulaParser >& rxParser = maParsers[ rNamespace ];
+
+ // try to create a new parser object
+ if( ScDocShell* pDocShell = mrDoc.GetDocumentShell() ) try
+ {
+ static ScParserFactoryMap theScParserFactoryMap;
+
+ Reference< XComponent > xComponent( pDocShell->GetModel() );
+ rxParser = theScParserFactoryMap.createFormulaParser( xComponent, rNamespace );
+ }
+ catch( Exception& )
+ {
+ }
+ return rxParser;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/formularesult.cxx b/sc/source/core/tool/formularesult.cxx
new file mode 100644
index 0000000000..b421354550
--- /dev/null
+++ b/sc/source/core/tool/formularesult.cxx
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <formularesult.hxx>
+#include <scmatrix.hxx>
+#include <token.hxx>
+
+#include <sal/log.hxx>
+#include <utility>
+
+namespace sc {
+
+FormulaResultValue::FormulaResultValue() : mfValue(0.0), meType(Invalid), mnError(FormulaError::NONE) {}
+FormulaResultValue::FormulaResultValue( double fValue ) : mfValue(fValue), meType(Value), mnError(FormulaError::NONE) {}
+FormulaResultValue::FormulaResultValue( svl::SharedString aStr, bool bMultiLine ) : mfValue(0.0), maString(std::move(aStr)), mbMultiLine(bMultiLine), meType(String), mnError(FormulaError::NONE) {}
+FormulaResultValue::FormulaResultValue( FormulaError nErr ) : mfValue(0.0), meType(Error), mnError(nErr) {}
+
+}
+
+ScFormulaResult::ScFormulaResult() :
+ mpToken(nullptr),
+ mbToken(true),
+ mbEmpty(false),
+ mbEmptyDisplayedAsString(false),
+ mbValueCached(false),
+ meMultiline(MULTILINE_UNKNOWN),
+ mnError(FormulaError::NONE) {}
+
+ScFormulaResult::ScFormulaResult( const ScFormulaResult & r ) :
+ mbToken( r.mbToken),
+ mbEmpty( r.mbEmpty),
+ mbEmptyDisplayedAsString( r.mbEmptyDisplayedAsString),
+ mbValueCached( r.mbValueCached),
+ meMultiline( r.meMultiline),
+ mnError( r.mnError)
+{
+ if (mbToken)
+ {
+ mpToken = r.mpToken;
+ if (mpToken)
+ {
+ // Since matrix dimension and
+ // results are assigned to a matrix
+ // cell formula token we have to
+ // clone that instead of sharing it.
+ const ScMatrixFormulaCellToken* pMatFormula =
+ r.GetMatrixFormulaCellToken();
+ if (pMatFormula)
+ mpToken = new ScMatrixFormulaCellToken( *pMatFormula);
+ mpToken->IncRef();
+ }
+ }
+ else
+ mfValue = r.mfValue;
+}
+
+ScFormulaResult::ScFormulaResult( const formula::FormulaToken* p ) :
+ mbToken(false),
+ mbEmpty(false),
+ mbEmptyDisplayedAsString(false),
+ mbValueCached(false),
+ meMultiline(MULTILINE_UNKNOWN),
+ mnError(FormulaError::NONE)
+{
+ SetToken( p);
+}
+
+ScFormulaResult::~ScFormulaResult()
+{
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+}
+
+void ScFormulaResult::ResetToDefaults()
+{
+ mnError = FormulaError::NONE;
+ mbEmpty = false;
+ mbEmptyDisplayedAsString = false;
+ meMultiline = MULTILINE_UNKNOWN;
+ mbValueCached = false;
+}
+
+void ScFormulaResult::ResolveToken( const formula::FormulaToken * p )
+{
+ ResetToDefaults();
+ if (!p)
+ {
+ mpToken = p;
+ mbToken = true;
+ }
+ else
+ {
+ switch (p->GetType())
+ {
+ case formula::svError:
+ mnError = p->GetError();
+ p->DecRef();
+ mbToken = false;
+ // set in case mnError is 0 now, which shouldn't happen but ...
+ mfValue = 0.0;
+ meMultiline = MULTILINE_FALSE;
+ break;
+ case formula::svEmptyCell:
+ mbEmpty = true;
+ mbEmptyDisplayedAsString = static_cast<const ScEmptyCellToken*>(p)->IsDisplayedAsString();
+ p->DecRef();
+ mbToken = false;
+ meMultiline = MULTILINE_FALSE;
+ // Take advantage of fast double result return for empty result token.
+ // by setting mfValue to 0 and turning on mbValueCached flag.
+ mfValue = 0.0;
+ mbValueCached = true;
+ break;
+ case formula::svDouble:
+ mfValue = p->GetDouble();
+ p->DecRef();
+ mbToken = false;
+ meMultiline = MULTILINE_FALSE;
+ mbValueCached = true;
+ break;
+ default:
+ mpToken = p;
+ mbToken = true;
+ }
+ }
+}
+
+ScFormulaResult & ScFormulaResult::operator=( const ScFormulaResult & r )
+{
+ Assign( r);
+ return *this;
+}
+
+void ScFormulaResult::Assign( const ScFormulaResult & r )
+{
+ if (this == &r)
+ return;
+
+ // It is important to reset the value-cache flag to that of the source
+ // unconditionally.
+ mbValueCached = r.mbValueCached;
+
+ if (r.mbEmpty)
+ {
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ mbToken = false;
+ mbEmpty = true;
+ mbEmptyDisplayedAsString = r.mbEmptyDisplayedAsString;
+ meMultiline = r.meMultiline;
+ // here r.mfValue will be 0.0 which is ensured in ResolveToken().
+ mfValue = 0.0;
+ }
+ else if (r.mbToken)
+ {
+ // Matrix formula cell token must be cloned, see copy-ctor.
+ const ScMatrixFormulaCellToken* pMatFormula =
+ r.GetMatrixFormulaCellToken();
+ if (pMatFormula)
+ SetToken( new ScMatrixFormulaCellToken( *pMatFormula));
+ else
+ SetToken( r.mpToken);
+ }
+ else
+ SetDouble( r.mfValue);
+ // If there was an error there will be an error, no matter what Set...()
+ // methods did.
+ SetResultError(r.mnError);
+}
+
+void ScFormulaResult::SetToken( const formula::FormulaToken* p )
+{
+ ResetToDefaults();
+ if (p)
+ p->IncRef();
+ // Handle a result obtained from the interpreter to be assigned to a matrix
+ // formula cell's ScMatrixFormulaCellToken.
+ ScMatrixFormulaCellToken* pMatFormula = GetMatrixFormulaCellTokenNonConst();
+ if (pMatFormula)
+ {
+ const ScMatrixCellResultToken* pMatResult =
+ (p && p->GetType() == formula::svMatrixCell ?
+ dynamic_cast<const ScMatrixCellResultToken*>(p) : nullptr);
+ if (pMatResult)
+ {
+ const ScMatrixFormulaCellToken* pNewMatFormula =
+ dynamic_cast<const ScMatrixFormulaCellToken*>(pMatResult);
+ if (pNewMatFormula && (pMatFormula->GetMatCols() <= 0 || pMatFormula->GetMatRows() <= 0))
+ {
+ SAL_WARN( "sc", "ScFormulaResult::SetToken: pNewMatFormula and pMatFormula, overriding matrix formula dimension; intended?");
+ pMatFormula->SetMatColsRows( pNewMatFormula->GetMatCols(),
+ pNewMatFormula->GetMatRows());
+ }
+ pMatFormula->Assign( *pMatResult);
+ p->DecRef();
+ }
+ else if (p)
+ {
+ // This may be the result of some constant expression like
+ // {="string"} that doesn't result in a matrix but still would
+ // display the result in all cells of this matrix formula.
+ pMatFormula->Assign( *p);
+ p->DecRef();
+ }
+ else
+ {
+ // NULL result? Well, if you say so ...
+ pMatFormula->ResetResult();
+ }
+ }
+ else
+ {
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ ResolveToken( p);
+ }
+}
+
+void ScFormulaResult::SetDouble( double f )
+{
+ ResetToDefaults();
+ // Handle a result obtained from the interpreter to be assigned to a matrix
+ // formula cell's ScMatrixFormulaCellToken.
+ ScMatrixFormulaCellToken* pMatFormula = GetMatrixFormulaCellTokenNonConst();
+ if (pMatFormula)
+ pMatFormula->SetUpperLeftDouble( f);
+ else
+ {
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ mfValue = f;
+ mbToken = false;
+ meMultiline = MULTILINE_FALSE;
+ mbValueCached = true;
+ }
+}
+
+formula::StackVar ScFormulaResult::GetType() const
+{
+ // Order is significant.
+ if (mnError != FormulaError::NONE)
+ return formula::svError;
+ if (mbEmpty)
+ return formula::svEmptyCell;
+ if (!mbToken)
+ return formula::svDouble;
+ if (mpToken)
+ return mpToken->GetType();
+ return formula::svUnknown;
+}
+
+formula::StackVar ScFormulaResult::GetCellResultType() const
+{
+ formula::StackVar sv = GetType();
+ if (sv == formula::svMatrixCell)
+ // don't need to test for mpToken here, GetType() already did it
+ sv = static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftType();
+ return sv;
+}
+
+bool ScFormulaResult::IsEmptyDisplayedAsString() const
+{
+ if (mbEmpty)
+ return mbEmptyDisplayedAsString;
+ switch (GetType())
+ {
+ case formula::svMatrixCell:
+ {
+ // don't need to test for mpToken here, GetType() already did it
+ const ScEmptyCellToken* p = dynamic_cast<const ScEmptyCellToken*>(
+ static_cast<const ScMatrixCellResultToken*>(
+ mpToken)->GetUpperLeftToken().get());
+ if (p)
+ return p->IsDisplayedAsString();
+ }
+ break;
+ case formula::svHybridCell:
+ {
+ const ScHybridCellToken* p = static_cast<const ScHybridCellToken*>(mpToken);
+ return p->IsEmptyDisplayedAsString();
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+namespace {
+
+bool isValue( formula::StackVar sv )
+{
+ return sv == formula::svDouble || sv == formula::svError
+ || sv == formula::svEmptyCell
+ // The initial uninitialized result value is double 0.0, even if the type
+ // is unknown, so the interpreter asking for it gets that double
+ // instead of having to convert a string which may result in #VALUE!
+ // (otherwise the unknown would be neither error nor double nor string)
+ || sv == formula::svUnknown;
+}
+
+bool isString( formula::StackVar sv )
+{
+ switch (sv)
+ {
+ case formula::svString:
+ case formula::svHybridCell:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+}
+
+bool ScFormulaResult::IsValue() const
+{
+ if (IsEmptyDisplayedAsString())
+ return true;
+
+ return isValue(GetCellResultType());
+}
+
+bool ScFormulaResult::IsValueNoError() const
+{
+ switch (GetCellResultType())
+ {
+ case formula::svDouble:
+ case formula::svEmptyCell:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool ScFormulaResult::IsMultiline() const
+{
+ if (meMultiline == MULTILINE_UNKNOWN)
+ {
+ svl::SharedString aStr = GetString();
+ if (!aStr.isEmpty() && aStr.getString().indexOf('\n') != -1)
+ const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_TRUE;
+ else
+ const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_FALSE;
+ }
+ return meMultiline == MULTILINE_TRUE;
+}
+
+bool ScFormulaResult::GetErrorOrDouble( FormulaError& rErr, double& rVal ) const
+{
+ if (mbValueCached)
+ {
+ rVal = mfValue;
+ return true;
+ }
+
+ if (mnError != FormulaError::NONE)
+ {
+ rErr = mnError;
+ return true;
+ }
+
+ formula::StackVar sv = GetCellResultType();
+ if (sv == formula::svError)
+ {
+ if (GetType() == formula::svMatrixCell)
+ {
+ // don't need to test for mpToken here, GetType() already did it
+ rErr = static_cast<const ScMatrixCellResultToken*>(mpToken)->
+ GetUpperLeftToken()->GetError();
+ }
+ else if (mpToken)
+ {
+ rErr = mpToken->GetError();
+ }
+ }
+
+ if (rErr != FormulaError::NONE)
+ return true;
+
+ if (!isValue(sv))
+ return false;
+
+ rVal = GetDouble();
+ return true;
+}
+
+sc::FormulaResultValue ScFormulaResult::GetResult() const
+{
+ if (mbValueCached)
+ return sc::FormulaResultValue(mfValue);
+
+ if (mnError != FormulaError::NONE)
+ return sc::FormulaResultValue(mnError);
+
+ formula::StackVar sv = GetCellResultType();
+ FormulaError nErr = FormulaError::NONE;
+ if (sv == formula::svError)
+ {
+ if (GetType() == formula::svMatrixCell)
+ {
+ // don't need to test for mpToken here, GetType() already did it
+ nErr = static_cast<const ScMatrixCellResultToken*>(mpToken)->
+ GetUpperLeftToken()->GetError();
+ }
+ else if (mpToken)
+ {
+ nErr = mpToken->GetError();
+ }
+ }
+
+ if (nErr != FormulaError::NONE)
+ return sc::FormulaResultValue(nErr);
+
+ if (isValue(sv))
+ return sc::FormulaResultValue(GetDouble());
+
+ if (!mbToken)
+ // String result type needs token.
+ return sc::FormulaResultValue();
+
+ if (isString(sv))
+ return sc::FormulaResultValue(GetString(), IsMultiline());
+
+ // Invalid
+ return sc::FormulaResultValue();
+}
+
+FormulaError ScFormulaResult::GetResultError() const
+{
+ if (mnError != FormulaError::NONE)
+ return mnError;
+ formula::StackVar sv = GetCellResultType();
+ if (sv == formula::svError)
+ {
+ if (GetType() == formula::svMatrixCell)
+ // don't need to test for mpToken here, GetType() already did it
+ return static_cast<const ScMatrixCellResultToken*>(mpToken)->
+ GetUpperLeftToken()->GetError();
+ if (mpToken)
+ return mpToken->GetError();
+ }
+ return FormulaError::NONE;
+}
+
+void ScFormulaResult::SetResultError( FormulaError nErr )
+{
+ mnError = nErr;
+ if (mnError != FormulaError::NONE)
+ mbValueCached = false;
+}
+
+formula::FormulaConstTokenRef ScFormulaResult::GetToken() const
+{
+ if (mbToken)
+ return mpToken;
+ return nullptr;
+}
+
+formula::FormulaConstTokenRef ScFormulaResult::GetCellResultToken() const
+{
+ if (GetType() == formula::svMatrixCell)
+ // don't need to test for mpToken here, GetType() already did it
+ return static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftToken();
+ return GetToken();
+}
+
+double ScFormulaResult::GetDouble() const
+{
+ if (mbValueCached)
+ return mfValue;
+
+ if (mbToken)
+ {
+ // Should really not be of type formula::svDouble here.
+ if (mpToken)
+ {
+ switch (mpToken->GetType())
+ {
+ case formula::svHybridCell:
+ return mpToken->GetDouble();
+ case formula::svMatrixCell:
+ {
+ const ScMatrixCellResultToken* p =
+ static_cast<const ScMatrixCellResultToken*>(mpToken);
+ if (p->GetUpperLeftType() == formula::svDouble)
+ return p->GetUpperLeftToken()->GetDouble();
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ // Note that we reach here also for the default ctor and
+ // formula::svUnknown from GetType().
+ return 0.0;
+ }
+ if (mbEmpty)
+ return 0.0;
+ return mfValue;
+}
+
+const svl::SharedString & ScFormulaResult::GetString() const
+{
+ if (mbToken && mpToken)
+ {
+ switch (mpToken->GetType())
+ {
+ case formula::svString:
+ case formula::svHybridCell:
+ return mpToken->GetString();
+ case formula::svMatrixCell:
+ {
+ const ScMatrixCellResultToken* p =
+ static_cast<const ScMatrixCellResultToken*>(mpToken);
+ if (p->GetUpperLeftType() == formula::svString)
+ return p->GetUpperLeftToken()->GetString();
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ return svl::SharedString::getEmptyString();
+}
+
+ScConstMatrixRef ScFormulaResult::GetMatrix() const
+{
+ if (GetType() == formula::svMatrixCell)
+ return mpToken->GetMatrix();
+ return nullptr;
+}
+
+OUString ScFormulaResult::GetHybridFormula() const
+{
+ if (GetType() == formula::svHybridCell)
+ {
+ const ScHybridCellToken* p = static_cast<const ScHybridCellToken*>(mpToken);
+ return p->GetFormula();
+ }
+ return OUString();
+}
+
+void ScFormulaResult::SetHybridDouble( double f )
+{
+ ResetToDefaults();
+ if (mbToken && mpToken)
+ {
+ if(GetType() == formula::svMatrixCell)
+ SetDouble(f);
+ else
+ {
+ svl::SharedString aString = GetString();
+ OUString aFormula( GetHybridFormula());
+ mpToken->DecRef();
+ mpToken = new ScHybridCellToken( f, aString, aFormula, false);
+ mpToken->IncRef();
+ }
+ }
+ else
+ {
+ mfValue = f;
+ mbToken = false;
+ meMultiline = MULTILINE_FALSE;
+ mbValueCached = true;
+ }
+}
+
+void ScFormulaResult::SetHybridString( const svl::SharedString& rStr )
+{
+ // Obtain values before changing anything.
+ double f = GetDouble();
+ OUString aFormula( GetHybridFormula());
+ ResetToDefaults();
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ mpToken = new ScHybridCellToken( f, rStr, aFormula, false);
+ mpToken->IncRef();
+ mbToken = true;
+}
+
+void ScFormulaResult::SetHybridEmptyDisplayedAsString()
+{
+ // Obtain values before changing anything.
+ double f = GetDouble();
+ OUString aFormula( GetHybridFormula());
+ svl::SharedString aStr = GetString();
+ ResetToDefaults();
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ // XXX NOTE: we can't use mbEmpty and mbEmptyDisplayedAsString here because
+ // GetType() intentionally returns svEmptyCell if mbEmpty==true. So stick
+ // it into the ScHybridCellToken.
+ mpToken = new ScHybridCellToken( f, aStr, aFormula, true);
+ mpToken->IncRef();
+ mbToken = true;
+}
+
+void ScFormulaResult::SetHybridFormula( const OUString & rFormula )
+{
+ // Obtain values before changing anything.
+ double f = GetDouble();
+ svl::SharedString aStr = GetString();
+ ResetToDefaults();
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ mpToken = new ScHybridCellToken( f, aStr, rFormula, false);
+ mpToken->IncRef();
+ mbToken = true;
+}
+
+void ScFormulaResult::SetMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL )
+{
+ ResetToDefaults();
+ if (mbToken && mpToken)
+ mpToken->DecRef();
+ mpToken = new ScMatrixFormulaCellToken(nCols, nRows, pMat, pUL);
+ mpToken->IncRef();
+ mbToken = true;
+}
+
+const ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellToken() const
+{
+ return (GetType() == formula::svMatrixCell ?
+ static_cast<const ScMatrixFormulaCellToken*>(mpToken) : nullptr);
+}
+
+ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellTokenNonConst()
+{
+ return const_cast<ScMatrixFormulaCellToken*>( GetMatrixFormulaCellToken());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/grouparealistener.cxx b/sc/source/core/tool/grouparealistener.cxx
new file mode 100644
index 0000000000..67862cd4a5
--- /dev/null
+++ b/sc/source/core/tool/grouparealistener.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <grouparealistener.hxx>
+#include <brdcst.hxx>
+#include <formulacell.hxx>
+#include <bulkdatahint.hxx>
+#include <columnspanset.hxx>
+#include <column.hxx>
+#include <listenerquery.hxx>
+#include <listenerqueryids.hxx>
+#include <document.hxx>
+#include <table.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+namespace sc {
+
+namespace {
+
+class Notifier
+{
+ const SfxHint& mrHint;
+public:
+ explicit Notifier( const SfxHint& rHint ) : mrHint(rHint) {}
+
+ void operator() ( ScFormulaCell* pCell )
+ {
+ pCell->Notify(mrHint);
+ }
+};
+
+class CollectCellAction : public sc::ColumnSpanSet::ColumnAction
+{
+ const FormulaGroupAreaListener& mrAreaListener;
+ ScAddress maPos;
+ std::vector<ScFormulaCell*> maCells;
+
+public:
+ explicit CollectCellAction( const FormulaGroupAreaListener& rAreaListener ) :
+ mrAreaListener(rAreaListener) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ maPos.SetTab(pCol->GetTab());
+ maPos.SetCol(pCol->GetCol());
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ mrAreaListener.collectFormulaCells(maPos.Tab(), maPos.Col(), nRow1, nRow2, maCells);
+ };
+
+ void swapCells( std::vector<ScFormulaCell*>& rCells )
+ {
+ // Remove duplicate before the swap. Take care to sort them by tab,col,row before sorting by pointer,
+ // as many calc algorithms perform better if cells are processed in this order.
+ std::sort(maCells.begin(), maCells.end(), [](const ScFormulaCell* cell1, const ScFormulaCell* cell2)
+ {
+ if( cell1->aPos != cell2->aPos )
+ return cell1->aPos < cell2->aPos;
+ return cell1 < cell2;
+ });
+ std::vector<ScFormulaCell*>::iterator it = std::unique(maCells.begin(), maCells.end());
+ maCells.erase(it, maCells.end());
+
+ rCells.swap(maCells);
+ }
+};
+
+}
+
+FormulaGroupAreaListener::FormulaGroupAreaListener( const ScRange& rRange, const ScDocument& rDocument,
+ const ScAddress& rTopCellPos, SCROW nGroupLen, bool bStartFixed, bool bEndFixed ) :
+ maRange(rRange),
+ mrDocument(rDocument),
+ mpColumn(nullptr),
+ mnTopCellRow(rTopCellPos.Row()),
+ mnGroupLen(nGroupLen),
+ mbStartFixed(bStartFixed),
+ mbEndFixed(bEndFixed)
+{
+ const ScTable* pTab = rDocument.FetchTable( rTopCellPos.Tab());
+ assert(pTab);
+ mpColumn = pTab->FetchColumn( rTopCellPos.Col());
+ assert(mpColumn);
+ SAL_INFO( "sc.core.grouparealistener",
+ "FormulaGroupAreaListener ctor this " << this <<
+ " range " << (maRange == BCA_LISTEN_ALWAYS ? "LISTEN-ALWAYS" : maRange.Format(mrDocument, ScRefFlags::VALID)) <<
+ " mnTopCellRow " << mnTopCellRow << " length " << mnGroupLen <<
+ ", col/tab " << mpColumn->GetCol() << "/" << mpColumn->GetTab());
+}
+
+FormulaGroupAreaListener::~FormulaGroupAreaListener()
+{
+ SAL_INFO( "sc.core.grouparealistener",
+ "FormulaGroupAreaListener dtor this " << this);
+}
+
+ScRange FormulaGroupAreaListener::getListeningRange() const
+{
+ ScRange aRet = maRange;
+ if (!mbEndFixed)
+ aRet.aEnd.IncRow(mnGroupLen-1);
+ return aRet;
+}
+
+void FormulaGroupAreaListener::Notify( const SfxHint& rHint )
+{
+ // BulkDataHint may include (SfxHintId::ScDataChanged |
+ // SfxHintId::ScTableOpDirty) so has to be checked first.
+ if ( const BulkDataHint* pBulkHint = dynamic_cast<const BulkDataHint*>(&rHint) )
+ {
+ notifyBulkChange(*pBulkHint);
+ }
+ else if (rHint.GetId() == SfxHintId::ScDataChanged || rHint.GetId() == SfxHintId::ScTableOpDirty)
+ {
+ const ScHint& rScHint = static_cast<const ScHint&>(rHint);
+ notifyCellChange(rHint, rScHint.GetStartAddress(), rScHint.GetRowCount());
+ }
+}
+
+void FormulaGroupAreaListener::Query( QueryBase& rQuery ) const
+{
+ switch (rQuery.getId())
+ {
+ case SC_LISTENER_QUERY_FORMULA_GROUP_RANGE:
+ {
+ const ScFormulaCell* pTop = getTopCell();
+ ScRange aRange(pTop->aPos);
+ aRange.aEnd.IncRow(mnGroupLen-1);
+ QueryRange& rQR = static_cast<QueryRange&>(rQuery);
+ rQR.add(aRange);
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+void FormulaGroupAreaListener::notifyBulkChange( const BulkDataHint& rHint )
+{
+ const ColumnSpanSet* pSpans = rHint.getSpans();
+ if (!pSpans)
+ return;
+
+ ScDocument& rDoc = const_cast<BulkDataHint&>(rHint).getDoc();
+
+ CollectCellAction aAction(*this);
+ pSpans->executeColumnAction(rDoc, aAction);
+
+ std::vector<ScFormulaCell*> aCells;
+ aAction.swapCells(aCells);
+ ScHint aHint(SfxHintId::ScDataChanged, ScAddress());
+ std::for_each(aCells.begin(), aCells.end(), Notifier(aHint));
+}
+
+void FormulaGroupAreaListener::collectFormulaCells(
+ SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const
+{
+ PutInOrder(nRow1, nRow2);
+
+ if (nTab < maRange.aStart.Tab() || maRange.aEnd.Tab() < nTab)
+ // Wrong sheet.
+ return;
+
+ if (nCol < maRange.aStart.Col() || maRange.aEnd.Col() < nCol)
+ // Outside the column range.
+ return;
+
+ collectFormulaCells(nRow1, nRow2, rCells);
+}
+
+void FormulaGroupAreaListener::collectFormulaCells(
+ SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const
+{
+ SAL_INFO( "sc.core.grouparealistener",
+ "FormulaGroupAreaListener::collectFormulaCells() this " << this <<
+ " range " << (maRange == BCA_LISTEN_ALWAYS ? "LISTEN-ALWAYS" : maRange.Format(mrDocument, ScRefFlags::VALID)) <<
+ " mnTopCellRow " << mnTopCellRow << " length " << mnGroupLen <<
+ ", col/tab " << mpColumn->GetCol() << "/" << mpColumn->GetTab());
+
+ size_t nBlockSize = 0;
+ ScFormulaCell* const * pp = mpColumn->GetFormulaCellBlockAddress( mnTopCellRow, nBlockSize);
+ if (!pp)
+ {
+ SAL_WARN("sc.core", "GetFormulaCellBlockAddress not found");
+ return;
+ }
+
+ /* FIXME: this is tdf#90717, when deleting a row fixed size area listeners
+ * such as BCA_ALWAYS or entire row listeners are (rightly) not destroyed,
+ * but mnTopCellRow and mnGroupLen also not updated, which needs fixing.
+ * Until then pull things as straight as possible here in such situation
+ * and prevent crash. */
+ if (!(*pp)->IsSharedTop())
+ {
+ SCROW nRow = (*pp)->GetSharedTopRow();
+ if (nRow < 0)
+ SAL_WARN("sc.core", "FormulaGroupAreaListener::collectFormulaCells() no shared top");
+ else
+ {
+ SAL_WARN("sc.core","FormulaGroupAreaListener::collectFormulaCells() syncing mnTopCellRow from " <<
+ mnTopCellRow << " to " << nRow);
+ const_cast<FormulaGroupAreaListener*>(this)->mnTopCellRow = nRow;
+ pp = mpColumn->GetFormulaCellBlockAddress( mnTopCellRow, nBlockSize);
+ if (!pp)
+ {
+ SAL_WARN("sc.core", "GetFormulaCellBlockAddress not found");
+ return;
+ }
+ }
+ }
+ SCROW nLen = (*pp)->GetSharedLength();
+ if (nLen != mnGroupLen)
+ {
+ SAL_WARN("sc.core", "FormulaGroupAreaListener::collectFormulaCells() syncing mnGroupLen from " <<
+ mnGroupLen << " to " << nLen);
+ const_cast<FormulaGroupAreaListener*>(this)->mnGroupLen = nLen;
+ }
+
+ /* With tdf#89957 it happened that the actual block size in column
+ * AP (shifted from AO) of sheet 'w' was smaller than the remembered group
+ * length and correct. This is just a very ugly workaround, the real cause
+ * is yet unknown, but at least don't crash in such case. The intermediate
+ * cause is that not all affected group area listeners are destroyed and
+ * newly created, so mpColumn still points to the old column that then has
+ * the content of a shifted column. Effectively this workaround has the
+ * consequence that the group area listener is fouled up and not all
+ * formula cells are notified... */
+ if (nBlockSize < o3tl::make_unsigned(mnGroupLen))
+ {
+ SAL_WARN("sc.core","FormulaGroupAreaListener::collectFormulaCells() nBlockSize " <<
+ nBlockSize << " < " << mnGroupLen << " mnGroupLen");
+ const_cast<FormulaGroupAreaListener*>(this)->mnGroupLen = static_cast<SCROW>(nBlockSize);
+
+ // erAck: 2016-11-09T18:30+01:00 XXX This doesn't occur anymore, at
+ // least not in the original bug scenario (insert a column before H on
+ // sheet w) of tdf#89957 with
+ // http://bugs.documentfoundation.org/attachment.cgi?id=114042
+ // Apparently this was fixed in the meantime, let's assume and get the
+ // assert bat out to hit us if it wasn't.
+ assert(!"something is still messing up the formula group and block size length");
+ }
+
+ ScFormulaCell* const * ppEnd = pp + mnGroupLen;
+
+ if (mbStartFixed)
+ {
+ if (mbEndFixed)
+ {
+ // Both top and bottom row positions are absolute. Use the original range as-is.
+ SCROW nRefRow1 = maRange.aStart.Row();
+ SCROW nRefRow2 = maRange.aEnd.Row();
+ if (nRow2 < nRefRow1 || nRefRow2 < nRow1)
+ return;
+
+ rCells.insert(rCells.end(), pp, ppEnd);
+ }
+ else
+ {
+ // Only the end row is relative.
+ SCROW nRefRow1 = maRange.aStart.Row();
+ SCROW nRefRow2 = maRange.aEnd.Row();
+ SCROW nMaxRefRow = nRefRow2 + mnGroupLen - 1;
+ if (nRow2 < nRefRow1 || nMaxRefRow < nRow1)
+ return;
+
+ if (nRefRow2 < nRow1)
+ {
+ // Skip ahead to the first hit.
+ SCROW nSkip = nRow1 - nRefRow2;
+ pp += nSkip;
+ nRefRow2 += nSkip;
+ }
+
+ assert(nRow1 <= nRefRow2);
+
+ // Notify the first hit cell and all subsequent ones.
+ rCells.insert(rCells.end(), pp, ppEnd);
+ }
+ }
+ else if (mbEndFixed)
+ {
+ // Only the start row is relative.
+ SCROW nRefRow1 = maRange.aStart.Row();
+ SCROW nRefRow2 = maRange.aEnd.Row();
+
+ if (nRow2 < nRefRow1 || nRefRow2 < nRow1)
+ return;
+
+ for (; pp != ppEnd && nRefRow1 <= nRefRow2; ++pp, ++nRefRow1)
+ rCells.push_back(*pp);
+ }
+ else
+ {
+ // Both top and bottom row positions are relative.
+ SCROW nRefRow1 = maRange.aStart.Row();
+ SCROW nRefRow2 = maRange.aEnd.Row();
+ SCROW nMaxRefRow = nRefRow2 + mnGroupLen - 1;
+ if (nMaxRefRow < nRow1)
+ return;
+
+ if (nRefRow2 < nRow1)
+ {
+ // The ref row range is above the changed row span. Skip ahead.
+ SCROW nSkip = nRow1 - nRefRow2;
+ pp += nSkip;
+ nRefRow1 += nSkip;
+ nRefRow2 += nSkip;
+ }
+
+ // At this point the initial ref row range should be overlapping the
+ // dirty cell range.
+ assert(nRow1 <= nRefRow2);
+
+ // Keep sliding down until the top ref row position is below the
+ // bottom row of the dirty cell range.
+ for (; pp != ppEnd && nRefRow1 <= nRow2; ++pp, ++nRefRow1, ++nRefRow2)
+ rCells.push_back(*pp);
+ }
+}
+
+const ScFormulaCell* FormulaGroupAreaListener::getTopCell() const
+{
+ size_t nBlockSize = 0;
+ const ScFormulaCell* const * pp = mpColumn->GetFormulaCellBlockAddress( mnTopCellRow, nBlockSize);
+ SAL_WARN_IF(!pp, "sc.core.grouparealistener", "GetFormulaCellBlockAddress not found");
+ return pp ? *pp : nullptr;
+}
+
+void FormulaGroupAreaListener::notifyCellChange( const SfxHint& rHint, const ScAddress& rPos, SCROW nNumRows )
+{
+ // Determine which formula cells within the group need to be notified of this change.
+ std::vector<ScFormulaCell*> aCells;
+ collectFormulaCells(rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Row() + (nNumRows - 1), aCells);
+ std::for_each(aCells.begin(), aCells.end(), Notifier(rHint));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/hints.cxx b/sc/source/core/tool/hints.cxx
new file mode 100644
index 0000000000..06ac946d9b
--- /dev/null
+++ b/sc/source/core/tool/hints.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 <hints.hxx>
+#include <utility>
+
+// ScPaintHint - info what has to be repainted
+
+ScPaintHint::ScPaintHint( const ScRange& rRng, PaintPartFlags nPaint ) :
+ aRange( rRng ),
+ nParts( nPaint )
+{
+}
+
+ScPaintHint::~ScPaintHint()
+{
+}
+
+// ScUpdateRefHint - update references
+
+ScUpdateRefHint::ScUpdateRefHint( UpdateRefMode eMode, const ScRange& rR,
+ SCCOL nX, SCROW nY, SCTAB nZ ) :
+ eUpdateRefMode( eMode ),
+ aRange( rR ),
+ nDx( nX ),
+ nDy( nY ),
+ nDz( nZ )
+{
+}
+
+ScUpdateRefHint::~ScUpdateRefHint()
+{
+}
+
+// ScLinkRefreshedHint - a link has been refreshed
+
+ScLinkRefreshedHint::ScLinkRefreshedHint() :
+ nLinkType( ScLinkRefType::NONE )
+{
+}
+
+ScLinkRefreshedHint::~ScLinkRefreshedHint()
+{
+}
+
+void ScLinkRefreshedHint::SetSheetLink( const OUString& rSourceUrl )
+{
+ nLinkType = ScLinkRefType::SHEET;
+ aUrl = rSourceUrl;
+}
+
+void ScLinkRefreshedHint::SetDdeLink(
+ const OUString& rA, const OUString& rT, const OUString& rI )
+{
+ nLinkType = ScLinkRefType::DDE;
+ aDdeAppl = rA;
+ aDdeTopic = rT;
+ aDdeItem = rI;
+}
+
+void ScLinkRefreshedHint::SetAreaLink( const ScAddress& rPos )
+{
+ nLinkType = ScLinkRefType::AREA;
+ aDestPos = rPos;
+}
+
+// ScAutoStyleHint - STYLE() function has been called
+
+ScAutoStyleHint::ScAutoStyleHint( const ScRange& rR, OUString aSt1,
+ sal_uLong nT, OUString aSt2 ) :
+ aRange( rR ),
+ aStyle1(std::move( aSt1 )),
+ aStyle2(std::move( aSt2 )),
+ nTimeout( nT )
+{
+}
+
+ScAutoStyleHint::~ScAutoStyleHint()
+{
+}
+
+ScDBRangeRefreshedHint::ScDBRangeRefreshedHint( const ScImportParam& rP )
+ : aParam(rP)
+{
+}
+ScDBRangeRefreshedHint::~ScDBRangeRefreshedHint()
+{
+}
+
+ScDataPilotModifiedHint::ScDataPilotModifiedHint( OUString aName )
+ : maName(std::move(aName))
+{
+}
+ScDataPilotModifiedHint::~ScDataPilotModifiedHint()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/inputopt.cxx b/sc/source/core/tool/inputopt.cxx
new file mode 100644
index 0000000000..13781040ee
--- /dev/null
+++ b/sc/source/core/tool/inputopt.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 <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/diagnose.h>
+
+#include <inputopt.hxx>
+#include <global.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+// ScInputOptions - input options
+
+ScInputOptions::ScInputOptions()
+ : nMoveDir(DIR_BOTTOM)
+ , bMoveSelection(true)
+ , bEnterEdit(false)
+ , bExtendFormat(false)
+ , bRangeFinder(true)
+ , bExpandRefs(false)
+ , mbSortRefUpdate(true)
+ , bMarkHeader(true)
+ , bUseTabCol(false)
+ , bTextWysiwyg(false)
+ , bReplCellsWarn(true)
+ , bLegacyCellSelection(false)
+ , bEnterPasteMode(false)
+{
+}
+
+// Config Item containing input options
+
+constexpr OUStringLiteral CFGPATH_INPUT = u"Office.Calc/Input";
+
+#define SCINPUTOPT_MOVEDIR 0
+#define SCINPUTOPT_MOVESEL 1
+#define SCINPUTOPT_EDTEREDIT 2
+#define SCINPUTOPT_EXTENDFMT 3
+#define SCINPUTOPT_RANGEFIND 4
+#define SCINPUTOPT_EXPANDREFS 5
+#define SCINPUTOPT_SORT_REF_UPDATE 6
+#define SCINPUTOPT_MARKHEADER 7
+#define SCINPUTOPT_USETABCOL 8
+#define SCINPUTOPT_REPLCELLSWARN 9
+#define SCINPUTOPT_LEGACY_CELL_SELECTION 10
+#define SCINPUTOPT_ENTER_PASTE_MODE 11
+
+Sequence<OUString> ScInputCfg::GetPropertyNames()
+{
+ return {"MoveSelectionDirection", // SCINPUTOPT_MOVEDIR
+ "MoveSelection", // SCINPUTOPT_MOVESEL
+ "SwitchToEditMode", // SCINPUTOPT_EDTEREDIT
+ "ExpandFormatting", // SCINPUTOPT_EXTENDFMT
+ "ShowReference", // SCINPUTOPT_RANGEFIND
+ "ExpandReference", // SCINPUTOPT_EXPANDREFS
+ "UpdateReferenceOnSort", // SCINPUTOPT_SORT_REF_UPDATE
+ "HighlightSelection", // SCINPUTOPT_MARKHEADER
+ "UseTabCol", // SCINPUTOPT_USETABCOL
+ "ReplaceCellsWarning", // SCINPUTOPT_REPLCELLSWARN
+ "LegacyCellSelection", // SCINPUTOPT_LEGACY_CELL_SELECTION
+ "EnterPasteMode"}; // SCINPUTOPT_ENTER_PASTE_MODE
+}
+
+ScInputCfg::ScInputCfg() :
+ ConfigItem( CFGPATH_INPUT )
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ EnableNotification(aNames);
+ ReadCfg();
+}
+
+void ScInputCfg::ReadCfg()
+{
+ const Sequence<OUString> aNames = GetPropertyNames();
+ const Sequence<Any> aValues = GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ if (sal_Int32 nVal; aValues[SCINPUTOPT_MOVEDIR] >>= nVal)
+ SetMoveDir(static_cast<sal_uInt16>(nVal));
+ if (bool bVal; aValues[SCINPUTOPT_MOVESEL] >>= bVal)
+ SetMoveSelection(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_EDTEREDIT] >>= bVal)
+ SetEnterEdit(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_EXTENDFMT] >>= bVal)
+ SetExtendFormat(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_RANGEFIND] >>= bVal)
+ SetRangeFinder(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_EXPANDREFS] >>= bVal)
+ SetExpandRefs(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_SORT_REF_UPDATE] >>= bVal)
+ SetSortRefUpdate(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_MARKHEADER] >>= bVal)
+ SetMarkHeader(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_USETABCOL] >>= bVal)
+ SetUseTabCol(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_REPLCELLSWARN] >>= bVal)
+ SetReplaceCellsWarn(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_LEGACY_CELL_SELECTION] >>= bVal)
+ SetLegacyCellSelection(bVal);
+ if (bool bVal; aValues[SCINPUTOPT_ENTER_PASTE_MODE] >>= bVal)
+ SetEnterPasteMode(bVal);
+}
+
+void ScInputCfg::ImplCommit()
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ pValues[SCINPUTOPT_MOVEDIR] <<= static_cast<sal_Int32>(GetMoveDir());
+ pValues[SCINPUTOPT_MOVESEL] <<= GetMoveSelection();
+ pValues[SCINPUTOPT_EDTEREDIT] <<= GetEnterEdit();
+ pValues[SCINPUTOPT_EXTENDFMT] <<= GetExtendFormat();
+ pValues[SCINPUTOPT_RANGEFIND] <<= GetRangeFinder();
+ pValues[SCINPUTOPT_EXPANDREFS] <<= GetExpandRefs();
+ pValues[SCINPUTOPT_SORT_REF_UPDATE] <<= GetSortRefUpdate();
+ pValues[SCINPUTOPT_MARKHEADER] <<= GetMarkHeader();
+ pValues[SCINPUTOPT_USETABCOL] <<= GetUseTabCol();
+ pValues[SCINPUTOPT_REPLCELLSWARN] <<= GetReplaceCellsWarn();
+ pValues[SCINPUTOPT_LEGACY_CELL_SELECTION] <<= GetLegacyCellSelection();
+ pValues[SCINPUTOPT_ENTER_PASTE_MODE] <<= GetEnterPasteMode();
+ PutProperties(aNames, aValues);
+}
+
+void ScInputCfg::Notify( const Sequence<OUString>& /* aPropertyNames */ )
+{
+ ReadCfg();
+}
+
+void ScInputCfg::SetOptions( const ScInputOptions& rNew )
+{
+ *static_cast<ScInputOptions*>(this) = rNew;
+ SetModified();
+ Commit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
new file mode 100644
index 0000000000..e372228721
--- /dev/null
+++ b/sc/source/core/tool/interpr1.cxx
@@ -0,0 +1,10332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <interpre.hxx>
+
+#include <scitems.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/temporary.hxx>
+#include <osl/thread.h>
+#include <unotools/textsearch.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/charclass.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <unicode/uchar.h>
+#include <unicode/regex.h>
+#include <i18nlangtag/mslangid.hxx>
+
+#include <patattr.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <docsh.hxx>
+#include <formulacell.hxx>
+#include <scmatrix.hxx>
+#include <docoptio.hxx>
+#include <attrib.hxx>
+#include <jumpmatrix.hxx>
+#include <cellkeytranslator.hxx>
+#include <lookupcache.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <compiler.hxx>
+#include <externalrefmgr.hxx>
+#include <doubleref.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <queryiter.hxx>
+#include <tokenarray.hxx>
+#include <compare.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/random.hxx>
+#include <comphelper/string.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <stdlib.h>
+#include <vector>
+#include <memory>
+#include <limits>
+#include <string_view>
+#include <cmath>
+
+const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
+
+ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr;
+
+using namespace formula;
+using ::std::unique_ptr;
+
+void ScInterpreter::ScIfJump()
+{
+ const short* pJump = pCur->GetJump();
+ short nJumpCount = pJump[ 0 ];
+ MatrixJumpConditionToMatrix();
+ switch ( GetStackType() )
+ {
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ if ( !pMat )
+ PushIllegalParameter();
+ else
+ {
+ FormulaConstTokenRef xNew;
+ ScTokenMatrixMap::const_iterator aMapIter;
+ // DoubleError handled by JumpMatrix
+ pMat->SetErrorInterpreter( nullptr);
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows );
+ if ( nCols == 0 || nRows == 0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ else if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
+ xNew = (*aMapIter).second;
+ else
+ {
+ std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
+ pCur->GetOpCode(), nCols, nRows));
+ for ( SCSIZE nC=0; nC < nCols; ++nC )
+ {
+ for ( SCSIZE nR=0; nR < nRows; ++nR )
+ {
+ double fVal;
+ bool bTrue;
+ bool bIsValue = pMat->IsValue(nC, nR);
+ if (bIsValue)
+ {
+ fVal = pMat->GetDouble(nC, nR);
+ bIsValue = std::isfinite(fVal);
+ bTrue = bIsValue && (fVal != 0.0);
+ if (bTrue)
+ fVal = 1.0;
+ }
+ else
+ {
+ // Treat empty and empty path as 0, but string
+ // as error. ScMatrix::IsValueOrEmpty() returns
+ // true for any empty, empty path, empty cell,
+ // empty result.
+ bIsValue = pMat->IsValueOrEmpty(nC, nR);
+ bTrue = false;
+ fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue));
+ }
+ if ( bTrue )
+ { // TRUE
+ if( nJumpCount >= 2 )
+ { // THEN path
+ pJumpMat->SetJump( nC, nR, fVal,
+ pJump[ 1 ],
+ pJump[ nJumpCount ]);
+ }
+ else
+ { // no parameter given for THEN
+ pJumpMat->SetJump( nC, nR, fVal,
+ pJump[ nJumpCount ],
+ pJump[ nJumpCount ]);
+ }
+ }
+ else
+ { // FALSE
+ if( nJumpCount == 3 && bIsValue )
+ { // ELSE path
+ pJumpMat->SetJump( nC, nR, fVal,
+ pJump[ 2 ],
+ pJump[ nJumpCount ]);
+ }
+ else
+ { // no parameter given for ELSE,
+ // or DoubleError
+ pJumpMat->SetJump( nC, nR, fVal,
+ pJump[ nJumpCount ],
+ pJump[ nJumpCount ]);
+ }
+ }
+ }
+ }
+ xNew = new ScJumpMatrixToken( pJumpMat );
+ GetTokenMatrixMap().emplace(pCur, xNew);
+ }
+ if (!xNew)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ PushTokenRef( xNew);
+ // set endpoint of path for main code line
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ }
+ }
+ break;
+ default:
+ {
+ const bool bCondition = GetBool();
+ if (nGlobalError != FormulaError::NONE)
+ { // Propagate error, not THEN- or ELSE-path, jump behind.
+ PushError(nGlobalError);
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ }
+ else if ( bCondition )
+ { // TRUE
+ if( nJumpCount >= 2 )
+ { // THEN path
+ aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
+ }
+ else
+ { // no parameter given for THEN
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ PushInt(1);
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ }
+ }
+ else
+ { // FALSE
+ if( nJumpCount == 3 )
+ { // ELSE path
+ aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
+ }
+ else
+ { // no parameter given for ELSE
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ PushInt(0);
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ }
+ }
+ }
+ }
+}
+
+/** Store a matrix value in another matrix in the context of that other matrix
+ is the result matrix of a jump matrix. All arguments must be valid and are
+ not checked. */
+static void lcl_storeJumpMatResult(
+ const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
+{
+ if ( pMat->IsValue( nC, nR ) )
+ {
+ double fVal = pMat->GetDouble( nC, nR );
+ pJumpMat->PutResultDouble( fVal, nC, nR );
+ }
+ else if ( pMat->IsEmpty( nC, nR ) )
+ {
+ pJumpMat->PutResultEmpty( nC, nR );
+ }
+ else
+ {
+ pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
+ }
+}
+
+void ScInterpreter::ScIfError( bool bNAonly )
+{
+ const short* pJump = pCur->GetJump();
+ short nJumpCount = pJump[ 0 ];
+ if (!sp || nJumpCount != 2)
+ {
+ // Reset nGlobalError here to not propagate the old error, if any.
+ nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
+ PushError( nGlobalError);
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ return;
+ }
+
+ FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
+ bool bError = false;
+ FormulaError nOldGlobalError = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+
+ MatrixJumpConditionToMatrix();
+ switch (GetStackType())
+ {
+ default:
+ Pop();
+ // Act on implicitly propagated error, if any.
+ if (nOldGlobalError != FormulaError::NONE)
+ nGlobalError = nOldGlobalError;
+ if (nGlobalError != FormulaError::NONE)
+ bError = true;
+ break;
+ case svError:
+ PopError();
+ bError = true;
+ break;
+ case svDoubleRef:
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ if (!PopDoubleRefOrSingleRef( aAdr))
+ bError = true;
+ else
+ {
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ nGlobalError = GetCellErrCode(aCell);
+ if (nGlobalError != FormulaError::NONE)
+ bError = true;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ double fVal;
+ svl::SharedString aStr;
+ // Handles also existing jump matrix case and sets error on
+ // elements.
+ GetDoubleOrStringFromMatrix( fVal, aStr);
+ if (nGlobalError != FormulaError::NONE)
+ bError = true;
+ }
+ break;
+ case svMatrix:
+ {
+ const ScMatrixRef pMat = PopMatrix();
+ if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
+ {
+ bError = true;
+ break; // switch
+ }
+ // If the matrix has no queried error at all we can simply use
+ // it as result and don't need to bother with jump matrix.
+ SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
+ nErrorRow = ::std::numeric_limits<SCSIZE>::max();
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows );
+ if (nCols == 0 || nRows == 0)
+ {
+ bError = true;
+ break; // switch
+ }
+ for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
+ {
+ for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
+ {
+ FormulaError nErr = pMat->GetError( nC, nR );
+ if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
+ {
+ bError = true;
+ nErrorCol = nC;
+ nErrorRow = nR;
+ }
+ }
+ }
+ if (!bError)
+ break; // switch, we're done and have the result
+
+ FormulaConstTokenRef xNew;
+ ScTokenMatrixMap::const_iterator aMapIter;
+ if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
+ {
+ xNew = (*aMapIter).second;
+ }
+ else
+ {
+ const ScMatrix* pMatPtr = pMat.get();
+ std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
+ pCur->GetOpCode(), nCols, nRows));
+ // Init all jumps to no error to save single calls. Error
+ // is the exceptional condition.
+ const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
+ pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ // Up to first error position simply store results, no need
+ // to evaluate error conditions again.
+ SCSIZE nC = 0, nR = 0;
+ for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
+ {
+ for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
+ {
+ lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
+ }
+ if (nC != nErrorCol && nR != nErrorRow)
+ ++nC;
+ }
+ // Now the mixed cases.
+ for ( ; nC < nCols; ++nC)
+ {
+ for ( ; nR < nRows; ++nR)
+ {
+ FormulaError nErr = pMat->GetError( nC, nR );
+ if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
+ { // TRUE, THEN path
+ pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
+ }
+ else
+ { // FALSE, EMPTY path, store result instead
+ lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
+ }
+ }
+ nR = 0;
+ }
+ xNew = new ScJumpMatrixToken( pJumpMat );
+ GetTokenMatrixMap().emplace( pCur, xNew );
+ }
+ nGlobalError = nOldGlobalError;
+ PushTokenRef( xNew );
+ // set endpoint of path for main code line
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ return;
+ }
+ break;
+ }
+
+ if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
+ {
+ // error, calculate 2nd argument
+ nGlobalError = FormulaError::NONE;
+ aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
+ }
+ else
+ {
+ // no error, push 1st argument and continue
+ nGlobalError = nOldGlobalError;
+ PushTokenRef( xToken);
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ }
+}
+
+void ScInterpreter::ScChooseJump()
+{
+ // We have to set a jump, if there was none chosen because of an error set
+ // it to endpoint.
+ bool bHaveJump = false;
+ const short* pJump = pCur->GetJump();
+ short nJumpCount = pJump[ 0 ];
+ MatrixJumpConditionToMatrix();
+ switch ( GetStackType() )
+ {
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ if ( !pMat )
+ PushIllegalParameter();
+ else
+ {
+ FormulaConstTokenRef xNew;
+ ScTokenMatrixMap::const_iterator aMapIter;
+ // DoubleError handled by JumpMatrix
+ pMat->SetErrorInterpreter( nullptr);
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows );
+ if ( nCols == 0 || nRows == 0 )
+ PushIllegalParameter();
+ else if ((aMapIter = maTokenMatrixMap.find(
+ pCur)) != maTokenMatrixMap.end())
+ xNew = (*aMapIter).second;
+ else
+ {
+ std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
+ pCur->GetOpCode(), nCols, nRows));
+ for ( SCSIZE nC=0; nC < nCols; ++nC )
+ {
+ for ( SCSIZE nR=0; nR < nRows; ++nR )
+ {
+ double fVal;
+ bool bIsValue = pMat->IsValue(nC, nR);
+ if ( bIsValue )
+ {
+ fVal = pMat->GetDouble(nC, nR);
+ bIsValue = std::isfinite( fVal );
+ if ( bIsValue )
+ {
+ fVal = ::rtl::math::approxFloor( fVal);
+ if ( (fVal < 1) || (fVal >= nJumpCount))
+ {
+ bIsValue = false;
+ fVal = CreateDoubleError(
+ FormulaError::IllegalArgument);
+ }
+ }
+ }
+ else
+ {
+ fVal = CreateDoubleError( FormulaError::NoValue);
+ }
+ if ( bIsValue )
+ {
+ pJumpMat->SetJump( nC, nR, fVal,
+ pJump[ static_cast<short>(fVal) ],
+ pJump[ nJumpCount ]);
+ }
+ else
+ {
+ pJumpMat->SetJump( nC, nR, fVal,
+ pJump[ nJumpCount ],
+ pJump[ nJumpCount ]);
+ }
+ }
+ }
+ xNew = new ScJumpMatrixToken( pJumpMat );
+ GetTokenMatrixMap().emplace(pCur, xNew);
+ }
+ if (xNew)
+ {
+ PushTokenRef( xNew);
+ // set endpoint of path for main code line
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+ bHaveJump = true;
+ }
+ }
+ }
+ break;
+ default:
+ {
+ sal_Int16 nJumpIndex = GetInt16();
+ if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
+ {
+ aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
+ bHaveJump = true;
+ }
+ else
+ PushIllegalArgument();
+ }
+ }
+ if (!bHaveJump)
+ aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
+}
+
+static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
+{
+ SCSIZE nJumpCols, nJumpRows;
+ SCSIZE nResCols, nResRows;
+ SCSIZE nAdjustCols, nAdjustRows;
+ pJumpM->GetDimensions( nJumpCols, nJumpRows );
+ pJumpM->GetResMatDimensions( nResCols, nResRows );
+ if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
+ ( nJumpRows == 1 && nParmRows > nResRows )))
+ return;
+
+ if ( nJumpCols == 1 && nJumpRows == 1 )
+ {
+ nAdjustCols = std::max(nParmCols, nResCols);
+ nAdjustRows = std::max(nParmRows, nResRows);
+ }
+ else if ( nJumpCols == 1 )
+ {
+ nAdjustCols = nParmCols;
+ nAdjustRows = nResRows;
+ }
+ else
+ {
+ nAdjustCols = nResCols;
+ nAdjustRows = nParmRows;
+ }
+ pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
+}
+
+bool ScInterpreter::JumpMatrix( short nStackLevel )
+{
+ pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
+ bool bHasResMat = pJumpMatrix->HasResultMatrix();
+ SCSIZE nC, nR;
+ if ( nStackLevel == 2 )
+ {
+ if ( aCode.HasStacked() )
+ aCode.Pop(); // pop what Jump() pushed
+ else
+ {
+ assert(!"pop goes the weasel");
+ }
+
+ if ( !bHasResMat )
+ {
+ Pop();
+ SetError( FormulaError::UnknownStackVariable );
+ }
+ else
+ {
+ pJumpMatrix->GetPos( nC, nR );
+ switch ( GetStackType() )
+ {
+ case svDouble:
+ {
+ double fVal = GetDouble();
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ fVal = CreateDoubleError( nGlobalError );
+ nGlobalError = FormulaError::NONE;
+ }
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ break;
+ case svString:
+ {
+ svl::SharedString aStr = GetString();
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
+ nC, nR);
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ pJumpMatrix->PutResultString(aStr, nC, nR);
+ }
+ break;
+ case svSingleRef:
+ {
+ FormulaConstTokenRef xRef = pStack[sp-1];
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
+ nC, nR);
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasEmptyValue())
+ pJumpMatrix->PutResultEmpty( nC, nR );
+ else if (aCell.hasNumeric())
+ {
+ double fVal = GetCellValue(aAdr, aCell);
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ fVal = CreateDoubleError(
+ nGlobalError);
+ nGlobalError = FormulaError::NONE;
+ }
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ else
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ pJumpMatrix->PutResultDouble( CreateDoubleError(
+ nGlobalError), nC, nR);
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ pJumpMatrix->PutResultString(aStr, nC, nR);
+ }
+ }
+
+ formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
+ if (eReturnType == ParamClass::Reference)
+ {
+ /* TODO: What about error handling and do we actually
+ * need the result matrix above at all in this case? */
+ ScComplexRefData aRef;
+ aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
+ pJumpMatrix->GetRefList().push_back( aRef);
+ }
+ }
+ break;
+ case svDoubleRef:
+ { // upper left plus offset within matrix
+ FormulaConstTokenRef xRef = pStack[sp-1];
+ double fVal;
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ fVal = CreateDoubleError( nGlobalError );
+ nGlobalError = FormulaError::NONE;
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ else
+ {
+ // Do not modify the original range because we use it
+ // to adjust the size of the result matrix if necessary.
+ ScAddress aAdr( aRange.aStart);
+ sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
+ sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
+ if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
+ aRange.aEnd.Col() != aRange.aStart.Col())
+ || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
+ aRange.aEnd.Row() != aRange.aStart.Row()))
+ {
+ fVal = CreateDoubleError( FormulaError::NotAvailable );
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ else
+ {
+ // Replicate column and/or row of a vector if it is
+ // one. Note that this could be a range reference
+ // that in fact consists of only one cell, e.g. A1:A1
+ if (aRange.aEnd.Col() == aRange.aStart.Col())
+ nCol = aRange.aStart.Col();
+ if (aRange.aEnd.Row() == aRange.aStart.Row())
+ nRow = aRange.aStart.Row();
+ aAdr.SetCol( static_cast<SCCOL>(nCol) );
+ aAdr.SetRow( static_cast<SCROW>(nRow) );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasEmptyValue())
+ pJumpMatrix->PutResultEmpty( nC, nR );
+ else if (aCell.hasNumeric())
+ {
+ double fCellVal = GetCellValue(aAdr, aCell);
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ fCellVal = CreateDoubleError(
+ nGlobalError);
+ nGlobalError = FormulaError::NONE;
+ }
+ pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
+ }
+ else
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ pJumpMatrix->PutResultDouble( CreateDoubleError(
+ nGlobalError), nC, nR);
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ pJumpMatrix->PutResultString(aStr, nC, nR);
+ }
+ }
+ SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
+ SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
+ lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
+ }
+
+ formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
+ if (eReturnType == ParamClass::Reference)
+ {
+ /* TODO: What about error handling and do we actually
+ * need the result matrix above at all in this case? */
+ pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ {
+ switch (pToken->GetType())
+ {
+ case svDouble:
+ pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
+ break;
+ case svString:
+ pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
+ break;
+ case svEmptyCell:
+ pJumpMatrix->PutResultEmpty( nC, nR );
+ break;
+ default:
+ // svError was already handled (set by
+ // PopExternalSingleRef()) with nGlobalError
+ // above.
+ assert(!"unhandled svExternalSingleRef case");
+ pJumpMatrix->PutResultDouble( CreateDoubleError(
+ FormulaError::UnknownStackVariable), nC, nR );
+ }
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ { // match matrix offsets
+ double fVal;
+ ScMatrixRef pMat = GetMatrix();
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ fVal = CreateDoubleError( nGlobalError );
+ nGlobalError = FormulaError::NONE;
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ else if ( !pMat )
+ {
+ fVal = CreateDoubleError( FormulaError::UnknownVariable );
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ else
+ {
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows );
+ if ((nCols <= nC && nCols != 1) ||
+ (nRows <= nR && nRows != 1))
+ {
+ fVal = CreateDoubleError( FormulaError::NotAvailable );
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ else
+ {
+ // GetMatrix() does SetErrorInterpreter() at the
+ // matrix, do not propagate an error from
+ // matrix->GetValue() as global error.
+ pMat->SetErrorInterpreter(nullptr);
+ lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
+ }
+ lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
+ }
+ }
+ break;
+ case svError:
+ {
+ PopError();
+ double fVal = CreateDoubleError( nGlobalError);
+ nGlobalError = FormulaError::NONE;
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ break;
+ default:
+ {
+ Pop();
+ double fVal = CreateDoubleError( FormulaError::IllegalArgument);
+ pJumpMatrix->PutResultDouble( fVal, nC, nR );
+ }
+ }
+ }
+ }
+ bool bCont = pJumpMatrix->Next( nC, nR );
+ if ( bCont )
+ {
+ double fBool;
+ short nStart, nNext, nStop;
+ pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
+ while ( bCont && nStart == nNext )
+ { // push all results that have no jump path
+ if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
+ {
+ // a false without path results in an empty path value
+ if ( fBool == 0.0 )
+ pJumpMatrix->PutResultEmptyPath( nC, nR );
+ else
+ pJumpMatrix->PutResultDouble( fBool, nC, nR );
+ }
+ bCont = pJumpMatrix->Next( nC, nR );
+ if ( bCont )
+ pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
+ }
+ if ( bCont && nStart != nNext )
+ {
+ const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
+ for ( auto const & i : rParams )
+ {
+ // This is not the current state of the interpreter, so
+ // push without error, and elements' errors are coded into
+ // double.
+ PushWithoutError(*i);
+ }
+ aCode.Jump( nStart, nNext, nStop );
+ }
+ }
+ if ( !bCont )
+ { // We're done with it, throw away jump matrix, keep result.
+ // For an intermediate result of Reference use the array of references
+ // if there are more than one reference and the current ForceArray
+ // context is ReferenceOrRefArray.
+ // Else (also for a final result of Reference) use the matrix.
+ // Treat the result of a jump command as final and use the matrix (see
+ // tdf#115493 for why).
+ if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
+ pJumpMatrix->GetRefList().size() > 1 &&
+ ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
+ !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
+ aCode.PeekNextOperator())
+ {
+ FormulaTokenRef xRef = new ScRefListToken(true);
+ *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
+ pJumpMatrix = nullptr;
+ Pop();
+ PushTokenRef( xRef);
+ maTokenMatrixMap.erase( pCur);
+ // There's no result matrix to remember in this case.
+ }
+ else
+ {
+ ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
+ pJumpMatrix = nullptr;
+ Pop();
+ PushMatrix( pResMat );
+ // Remove jump matrix from map and remember result matrix in case it
+ // could be reused in another path of the same condition.
+ maTokenMatrixMap.erase( pCur);
+ maTokenMatrixMap.emplace(pCur, pStack[sp-1]);
+ }
+ return true;
+ }
+ return false;
+}
+
+double ScInterpreter::Compare( ScQueryOp eOp )
+{
+ sc::Compare aComp;
+ aComp.meOp = eOp;
+ aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
+ for( short i = 1; i >= 0; i-- )
+ {
+ sc::Compare::Cell& rCell = aComp.maCells[i];
+
+ switch ( GetRawStackType() )
+ {
+ case svEmptyCell:
+ Pop();
+ rCell.mbEmpty = true;
+ break;
+ case svMissing:
+ case svDouble:
+ rCell.mfValue = GetDouble();
+ rCell.mbValue = true;
+ break;
+ case svString:
+ rCell.maStr = GetString();
+ rCell.mbValue = false;
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasEmptyValue())
+ rCell.mbEmpty = true;
+ else if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ rCell.maStr = aStr;
+ rCell.mbValue = false;
+ }
+ else
+ {
+ rCell.mfValue = GetCellValue(aAdr, aCell);
+ rCell.mbValue = true;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (!pMat)
+ {
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (!nC || !nR)
+ {
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ if (pMat->IsEmpty(0, 0))
+ rCell.mbEmpty = true;
+ else if (pMat->IsStringOrEmpty(0, 0))
+ {
+ rCell.maStr = pMat->GetString(0, 0);
+ rCell.mbValue = false;
+ }
+ else
+ {
+ rCell.mfValue = pMat->GetDouble(0, 0);
+ rCell.mbValue = true;
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ // TODO: Find out how to handle this...
+ // Xcl generates a position dependent intersection using
+ // col/row, as it seems to do for all range references, not
+ // only in compare context. We'd need a general implementation
+ // for that behavior similar to svDoubleRef in scalar and array
+ // mode. Which also means we'd have to change all places where
+ // it currently is handled along with svMatrix.
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ }
+ if( nGlobalError != FormulaError::NONE )
+ return 0;
+ nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
+ return sc::CompareFunc(aComp);
+}
+
+sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
+{
+ sc::Compare aComp;
+ aComp.meOp = eOp;
+ aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
+ sc::RangeMatrix aMat[2];
+ ScAddress aAdr;
+ for( short i = 1; i >= 0; i-- )
+ {
+ sc::Compare::Cell& rCell = aComp.maCells[i];
+
+ switch (GetRawStackType())
+ {
+ case svEmptyCell:
+ Pop();
+ rCell.mbEmpty = true;
+ break;
+ case svMissing:
+ case svDouble:
+ rCell.mfValue = GetDouble();
+ rCell.mbValue = true;
+ break;
+ case svString:
+ rCell.maStr = GetString();
+ rCell.mbValue = false;
+ break;
+ case svSingleRef:
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasEmptyValue())
+ rCell.mbEmpty = true;
+ else if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ rCell.maStr = aStr;
+ rCell.mbValue = false;
+ }
+ else
+ {
+ rCell.mfValue = GetCellValue(aAdr, aCell);
+ rCell.mbValue = true;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svDoubleRef:
+ case svMatrix:
+ aMat[i] = GetRangeMatrix();
+ if (!aMat[i].mpMat)
+ SetError( FormulaError::IllegalParameter);
+ else
+ aMat[i].mpMat->SetErrorInterpreter(nullptr);
+ // errors are transported as DoubleError inside matrix
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ }
+
+ sc::RangeMatrix aRes;
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
+ return aRes;
+ }
+
+ if (aMat[0].mpMat && aMat[1].mpMat)
+ {
+ SCSIZE nC0, nC1;
+ SCSIZE nR0, nR1;
+ aMat[0].mpMat->GetDimensions(nC0, nR0);
+ aMat[1].mpMat->GetDimensions(nC1, nR1);
+ SCSIZE nC = std::max( nC0, nC1 );
+ SCSIZE nR = std::max( nR0, nR1 );
+ aRes.mpMat = GetNewMat( nC, nR, /*bEmpty*/true );
+ if (!aRes.mpMat)
+ return aRes;
+ for ( SCSIZE j=0; j<nC; j++ )
+ {
+ for ( SCSIZE k=0; k<nR; k++ )
+ {
+ SCSIZE nCol = j, nRow = k;
+ if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
+ aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
+ {
+ for ( short i=1; i>=0; i-- )
+ {
+ sc::Compare::Cell& rCell = aComp.maCells[i];
+
+ if (aMat[i].mpMat->IsStringOrEmpty(j, k))
+ {
+ rCell.mbValue = false;
+ rCell.maStr = aMat[i].mpMat->GetString(j, k);
+ rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
+ }
+ else
+ {
+ rCell.mbValue = true;
+ rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
+ rCell.mbEmpty = false;
+ }
+ }
+ aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
+ }
+ else
+ aRes.mpMat->PutError( FormulaError::NoValue, j, k);
+ }
+ }
+
+ switch (eOp)
+ {
+ case SC_EQUAL:
+ aRes.mpMat->CompareEqual();
+ break;
+ case SC_LESS:
+ aRes.mpMat->CompareLess();
+ break;
+ case SC_GREATER:
+ aRes.mpMat->CompareGreater();
+ break;
+ case SC_LESS_EQUAL:
+ aRes.mpMat->CompareLessEqual();
+ break;
+ case SC_GREATER_EQUAL:
+ aRes.mpMat->CompareGreaterEqual();
+ break;
+ case SC_NOT_EQUAL:
+ aRes.mpMat->CompareNotEqual();
+ break;
+ default:
+ SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp));
+ aRes.mpMat.reset();
+ return aRes;
+ }
+ }
+ else if (aMat[0].mpMat || aMat[1].mpMat)
+ {
+ size_t i = ( aMat[0].mpMat ? 0 : 1);
+
+ aRes.mnCol1 = aMat[i].mnCol1;
+ aRes.mnRow1 = aMat[i].mnRow1;
+ aRes.mnTab1 = aMat[i].mnTab1;
+ aRes.mnCol2 = aMat[i].mnCol2;
+ aRes.mnRow2 = aMat[i].mnRow2;
+ aRes.mnTab2 = aMat[i].mnTab2;
+
+ ScMatrix& rMat = *aMat[i].mpMat;
+ aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
+ if (!aRes.mpMat)
+ return aRes;
+ }
+
+ nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
+ return aRes;
+}
+
+ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
+{
+ SvNumFormatType nSaveCurFmtType = nCurFmtType;
+ SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
+ PushMatrix( pMat);
+ const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
+ if (rItem.meType == ScQueryEntry::ByString)
+ PushString(rItem.maString.getString());
+ else
+ PushDouble(rItem.mfVal);
+ ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
+ nCurFmtType = nSaveCurFmtType;
+ nFuncFmtType = nSaveFuncFmtType;
+ if (nGlobalError != FormulaError::NONE || !pResultMatrix)
+ {
+ SetError( FormulaError::IllegalParameter);
+ return pResultMatrix;
+ }
+
+ return pResultMatrix;
+}
+
+void ScInterpreter::ScEqual()
+{
+ if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
+ {
+ sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
+ if (!aMat.mpMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ PushMatrix(aMat);
+ }
+ else
+ PushInt( int(Compare( SC_EQUAL) == 0) );
+}
+
+void ScInterpreter::ScNotEqual()
+{
+ if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
+ {
+ sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
+ if (!aMat.mpMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ PushMatrix(aMat);
+ }
+ else
+ PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
+}
+
+void ScInterpreter::ScLess()
+{
+ if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
+ {
+ sc::RangeMatrix aMat = CompareMat(SC_LESS);
+ if (!aMat.mpMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ PushMatrix(aMat);
+ }
+ else
+ PushInt( int(Compare( SC_LESS) < 0) );
+}
+
+void ScInterpreter::ScGreater()
+{
+ if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
+ {
+ sc::RangeMatrix aMat = CompareMat(SC_GREATER);
+ if (!aMat.mpMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ PushMatrix(aMat);
+ }
+ else
+ PushInt( int(Compare( SC_GREATER) > 0) );
+}
+
+void ScInterpreter::ScLessEqual()
+{
+ if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
+ {
+ sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
+ if (!aMat.mpMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ PushMatrix(aMat);
+ }
+ else
+ PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
+}
+
+void ScInterpreter::ScGreaterEqual()
+{
+ if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
+ {
+ sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
+ if (!aMat.mpMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ PushMatrix(aMat);
+ }
+ else
+ PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
+}
+
+void ScInterpreter::ScAnd()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+
+ bool bHaveValue = false;
+ bool bRes = true;
+ size_t nRefInList = 0;
+ while( nParamCount-- > 0)
+ {
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ switch ( GetStackType() )
+ {
+ case svDouble :
+ bHaveValue = true;
+ bRes &= ( PopDouble() != 0.0 );
+ break;
+ case svString :
+ Pop();
+ SetError( FormulaError::NoValue );
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ bHaveValue = true;
+ bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
+ }
+ // else: Xcl raises no error here
+ }
+ }
+ break;
+ case svDoubleRef:
+ case svRefList:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ double fVal;
+ FormulaError nErr = FormulaError::NONE;
+ ScValueIterator aValIter( mrContext, aRange );
+ if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
+ {
+ bHaveValue = true;
+ do
+ {
+ bRes &= ( fVal != 0.0 );
+ } while ( (nErr == FormulaError::NONE) &&
+ aValIter.GetNext( fVal, nErr ) );
+ }
+ SetError( nErr );
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ {
+ bHaveValue = true;
+ double fVal = pMat->And();
+ FormulaError nErr = GetDoubleErrorValue( fVal );
+ if ( nErr != FormulaError::NONE )
+ {
+ SetError( nErr );
+ bRes = false;
+ }
+ else
+ bRes &= (fVal != 0.0);
+ }
+ // else: GetMatrix did set FormulaError::IllegalParameter
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ Pop();
+ }
+ if ( bHaveValue )
+ PushInt( int(bRes) );
+ else
+ PushNoValue();
+}
+
+void ScInterpreter::ScOr()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+
+ bool bHaveValue = false;
+ bool bRes = false;
+ size_t nRefInList = 0;
+ while( nParamCount-- > 0)
+ {
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ switch ( GetStackType() )
+ {
+ case svDouble :
+ bHaveValue = true;
+ bRes |= ( PopDouble() != 0.0 );
+ break;
+ case svString :
+ Pop();
+ SetError( FormulaError::NoValue );
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ bHaveValue = true;
+ bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
+ }
+ // else: Xcl raises no error here
+ }
+ }
+ break;
+ case svDoubleRef:
+ case svRefList:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ double fVal;
+ FormulaError nErr = FormulaError::NONE;
+ ScValueIterator aValIter( mrContext, aRange );
+ if ( aValIter.GetFirst( fVal, nErr ) )
+ {
+ bHaveValue = true;
+ do
+ {
+ bRes |= ( fVal != 0.0 );
+ } while ( (nErr == FormulaError::NONE) &&
+ aValIter.GetNext( fVal, nErr ) );
+ }
+ SetError( nErr );
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ bHaveValue = true;
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ {
+ bHaveValue = true;
+ double fVal = pMat->Or();
+ FormulaError nErr = GetDoubleErrorValue( fVal );
+ if ( nErr != FormulaError::NONE )
+ {
+ SetError( nErr );
+ bRes = false;
+ }
+ else
+ bRes |= (fVal != 0.0);
+ }
+ // else: GetMatrix did set FormulaError::IllegalParameter
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ Pop();
+ }
+ if ( bHaveValue )
+ PushInt( int(bRes) );
+ else
+ PushNoValue();
+}
+
+void ScInterpreter::ScXor()
+{
+
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+
+ bool bHaveValue = false;
+ bool bRes = false;
+ size_t nRefInList = 0;
+ while( nParamCount-- > 0)
+ {
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ switch ( GetStackType() )
+ {
+ case svDouble :
+ bHaveValue = true;
+ bRes ^= ( PopDouble() != 0.0 );
+ break;
+ case svString :
+ Pop();
+ SetError( FormulaError::NoValue );
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ bHaveValue = true;
+ bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
+ }
+ /* TODO: set error? Excel doesn't have XOR, but
+ * doesn't set an error in this case for AND and
+ * OR. */
+ }
+ }
+ break;
+ case svDoubleRef:
+ case svRefList:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ double fVal;
+ FormulaError nErr = FormulaError::NONE;
+ ScValueIterator aValIter( mrContext, aRange );
+ if ( aValIter.GetFirst( fVal, nErr ) )
+ {
+ bHaveValue = true;
+ do
+ {
+ bRes ^= ( fVal != 0.0 );
+ } while ( (nErr == FormulaError::NONE) &&
+ aValIter.GetNext( fVal, nErr ) );
+ }
+ SetError( nErr );
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ bHaveValue = true;
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ {
+ bHaveValue = true;
+ double fVal = pMat->Xor();
+ FormulaError nErr = GetDoubleErrorValue( fVal );
+ if ( nErr != FormulaError::NONE )
+ {
+ SetError( nErr );
+ bRes = false;
+ }
+ else
+ bRes ^= ( fVal != 0.0 );
+ }
+ // else: GetMatrix did set FormulaError::IllegalParameter
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ Pop();
+ }
+ if ( bHaveValue )
+ PushInt( int(bRes) );
+ else
+ PushNoValue();
+}
+
+void ScInterpreter::ScNeg()
+{
+ // Simple negation doesn't change current format type to number, keep
+ // current type.
+ nFuncFmtType = nCurFmtType;
+ switch ( GetStackType() )
+ {
+ case svMatrix :
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ PushIllegalParameter();
+ else
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions( nC, nR );
+ ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true );
+ if ( !pResMat )
+ PushIllegalArgument();
+ else
+ {
+ pMat->NegOp( *pResMat);
+ PushMatrix( pResMat );
+ }
+ }
+ }
+ break;
+ default:
+ PushDouble( -GetDouble() );
+ }
+}
+
+void ScInterpreter::ScPercentSign()
+{
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ const FormulaToken* pSaveCur = pCur;
+ sal_uInt8 nSavePar = cPar;
+ PushInt( 100 );
+ cPar = 2;
+ FormulaByteToken aDivOp( ocDiv, cPar );
+ pCur = &aDivOp;
+ ScDiv();
+ pCur = pSaveCur;
+ cPar = nSavePar;
+}
+
+void ScInterpreter::ScNot()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ switch ( GetStackType() )
+ {
+ case svMatrix :
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ PushIllegalParameter();
+ else
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions( nC, nR );
+ ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true);
+ if ( !pResMat )
+ PushIllegalArgument();
+ else
+ {
+ pMat->NotOp( *pResMat);
+ PushMatrix( pResMat );
+ }
+ }
+ }
+ break;
+ default:
+ PushInt( int(GetDouble() == 0.0) );
+ }
+}
+
+void ScInterpreter::ScBitAnd()
+{
+
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double num1 = ::rtl::math::approxFloor( GetDouble());
+ double num2 = ::rtl::math::approxFloor( GetDouble());
+ if ( (num1 >= n2power48) || (num1 < 0) ||
+ (num2 >= n2power48) || (num2 < 0))
+ PushIllegalArgument();
+ else
+ PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
+}
+
+void ScInterpreter::ScBitOr()
+{
+
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double num1 = ::rtl::math::approxFloor( GetDouble());
+ double num2 = ::rtl::math::approxFloor( GetDouble());
+ if ( (num1 >= n2power48) || (num1 < 0) ||
+ (num2 >= n2power48) || (num2 < 0))
+ PushIllegalArgument();
+ else
+ PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
+}
+
+void ScInterpreter::ScBitXor()
+{
+
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double num1 = ::rtl::math::approxFloor( GetDouble());
+ double num2 = ::rtl::math::approxFloor( GetDouble());
+ if ( (num1 >= n2power48) || (num1 < 0) ||
+ (num2 >= n2power48) || (num2 < 0))
+ PushIllegalArgument();
+ else
+ PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
+}
+
+void ScInterpreter::ScBitLshift()
+{
+
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double fShift = ::rtl::math::approxFloor( GetDouble());
+ double num = ::rtl::math::approxFloor( GetDouble());
+ if ((num >= n2power48) || (num < 0))
+ PushIllegalArgument();
+ else
+ {
+ double fRes;
+ if (fShift < 0)
+ fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
+ else if (fShift == 0)
+ fRes = num;
+ else
+ fRes = num * pow( 2.0, fShift);
+ PushDouble( fRes);
+ }
+}
+
+void ScInterpreter::ScBitRshift()
+{
+
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double fShift = ::rtl::math::approxFloor( GetDouble());
+ double num = ::rtl::math::approxFloor( GetDouble());
+ if ((num >= n2power48) || (num < 0))
+ PushIllegalArgument();
+ else
+ {
+ double fRes;
+ if (fShift < 0)
+ fRes = num * pow( 2.0, -fShift);
+ else if (fShift == 0)
+ fRes = num;
+ else
+ fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
+ PushDouble( fRes);
+ }
+}
+
+void ScInterpreter::ScPi()
+{
+ PushDouble(M_PI);
+}
+
+void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
+ double fFirst, double fLast )
+{
+ if (bMatrixFormula)
+ {
+ SCCOL nCols = 0;
+ SCROW nRows = 0;
+ // In JumpMatrix context use its dimensions for the return matrix; the
+ // formula cell range selected may differ, for example if the result is
+ // to be transposed.
+ if (GetStackType(1) == svJumpMatrix)
+ {
+ SCSIZE nC, nR;
+ pStack[sp-1]->GetJumpMatrix()->GetDimensions( nC, nR);
+ nCols = std::max<SCCOL>(0, static_cast<SCCOL>(nC));
+ nRows = std::max<SCROW>(0, static_cast<SCROW>(nR));
+ }
+ else if (pMyFormulaCell)
+ pMyFormulaCell->GetMatColsRows( nCols, nRows);
+
+ if (nCols == 1 && nRows == 1)
+ {
+ // For compatibility with existing
+ // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
+ // default are executed in array context unless
+ // FA.setPropertyValue("IsArrayFunction",False) was set, return a
+ // scalar double instead of a 1x1 matrix object. tdf#128218
+ PushDouble( RandomFunc( fFirst, fLast));
+ return;
+ }
+
+ // ScViewFunc::EnterMatrix() might be asking for
+ // ScFormulaCell::GetResultDimensions(), which here are none so create
+ // a 1x1 matrix at least which exactly is the case when EnterMatrix()
+ // asks for a not selected range.
+ if (nCols == 0)
+ nCols = 1;
+ if (nRows == 0)
+ nRows = 1;
+ ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true );
+ if (!pResMat)
+ PushError( FormulaError::MatrixSize);
+ else
+ {
+ for (SCCOL i=0; i < nCols; ++i)
+ {
+ for (SCROW j=0; j < nRows; ++j)
+ {
+ pResMat->PutDouble( RandomFunc( fFirst, fLast),
+ static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
+ }
+ }
+ PushMatrix( pResMat);
+ }
+ }
+ else
+ {
+ PushDouble( RandomFunc( fFirst, fLast));
+ }
+}
+
+void ScInterpreter::ScRandom()
+{
+ auto RandomFunc = []( double, double )
+ {
+ return comphelper::rng::uniform_real_distribution();
+ };
+ ScRandomImpl( RandomFunc, 0.0, 0.0);
+}
+
+void ScInterpreter::ScRandbetween()
+{
+ if (!MustHaveParamCount( GetByte(), 2))
+ return;
+
+ // Same like scaddins/source/analysis/analysis.cxx
+ // AnalysisAddIn::getRandbetween()
+ double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
+ double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
+ if (nGlobalError != FormulaError::NONE || fMin > fMax)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ fMax = std::nextafter( fMax+1, -DBL_MAX);
+ auto RandomFunc = []( double fFirst, double fLast )
+ {
+ return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast));
+ };
+ ScRandomImpl( RandomFunc, fMin, fMax);
+}
+
+void ScInterpreter::ScTrue()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ PushInt(1);
+}
+
+void ScInterpreter::ScFalse()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ PushInt(0);
+}
+
+void ScInterpreter::ScDeg()
+{
+ PushDouble(basegfx::rad2deg(GetDouble()));
+}
+
+void ScInterpreter::ScRad()
+{
+ PushDouble(basegfx::deg2rad(GetDouble()));
+}
+
+void ScInterpreter::ScSin()
+{
+ PushDouble(::rtl::math::sin(GetDouble()));
+}
+
+void ScInterpreter::ScCos()
+{
+ PushDouble(::rtl::math::cos(GetDouble()));
+}
+
+void ScInterpreter::ScTan()
+{
+ PushDouble(::rtl::math::tan(GetDouble()));
+}
+
+void ScInterpreter::ScCot()
+{
+ PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
+}
+
+void ScInterpreter::ScArcSin()
+{
+ PushDouble(asin(GetDouble()));
+}
+
+void ScInterpreter::ScArcCos()
+{
+ PushDouble(acos(GetDouble()));
+}
+
+void ScInterpreter::ScArcTan()
+{
+ PushDouble(atan(GetDouble()));
+}
+
+void ScInterpreter::ScArcCot()
+{
+ PushDouble((M_PI_2) - atan(GetDouble()));
+}
+
+void ScInterpreter::ScSinHyp()
+{
+ PushDouble(sinh(GetDouble()));
+}
+
+void ScInterpreter::ScCosHyp()
+{
+ PushDouble(cosh(GetDouble()));
+}
+
+void ScInterpreter::ScTanHyp()
+{
+ PushDouble(tanh(GetDouble()));
+}
+
+void ScInterpreter::ScCotHyp()
+{
+ PushDouble(1.0 / tanh(GetDouble()));
+}
+
+void ScInterpreter::ScArcSinHyp()
+{
+ PushDouble( ::rtl::math::asinh( GetDouble()));
+}
+
+void ScInterpreter::ScArcCosHyp()
+{
+ double fVal = GetDouble();
+ if (fVal < 1.0)
+ PushIllegalArgument();
+ else
+ PushDouble( ::rtl::math::acosh( fVal));
+}
+
+void ScInterpreter::ScArcTanHyp()
+{
+ double fVal = GetDouble();
+ if (fabs(fVal) >= 1.0)
+ PushIllegalArgument();
+ else
+ PushDouble(::atanh(fVal));
+}
+
+void ScInterpreter::ScArcCotHyp()
+{
+ double nVal = GetDouble();
+ if (fabs(nVal) <= 1.0)
+ PushIllegalArgument();
+ else
+ PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
+}
+
+void ScInterpreter::ScCosecant()
+{
+ PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
+}
+
+void ScInterpreter::ScSecant()
+{
+ PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
+}
+
+void ScInterpreter::ScCosecantHyp()
+{
+ PushDouble(1.0 / sinh(GetDouble()));
+}
+
+void ScInterpreter::ScSecantHyp()
+{
+ PushDouble(1.0 / cosh(GetDouble()));
+}
+
+void ScInterpreter::ScExp()
+{
+ PushDouble(exp(GetDouble()));
+}
+
+void ScInterpreter::ScSqrt()
+{
+ double fVal = GetDouble();
+ if (fVal >= 0.0)
+ PushDouble(sqrt(fVal));
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScIsEmpty()
+{
+ short nRes = 0;
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ switch ( GetRawStackType() )
+ {
+ case svEmptyCell:
+ {
+ FormulaConstTokenRef p = PopToken();
+ if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
+ nRes = 1;
+ }
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+ // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
+ // may treat ="" in the referenced cell as blank for Excel
+ // interoperability.
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.getType() == CELLTYPE_NONE)
+ nRes = 1;
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
+ // else: false, not empty (which is what Xcl does)
+ }
+ }
+ break;
+ default:
+ Pop();
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( nRes );
+}
+
+bool ScInterpreter::IsString()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetRawStackType() )
+ {
+ case svString:
+ Pop();
+ bRes = true;
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (GetCellErrCode(aCell) == FormulaError::NONE)
+ {
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ bRes = true;
+ break;
+ case CELLTYPE_FORMULA :
+ bRes = (!aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString)
+ bRes = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0);
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR);
+ }
+ }
+ break;
+ default:
+ Pop();
+ }
+ nGlobalError = FormulaError::NONE;
+ return bRes;
+}
+
+void ScInterpreter::ScIsString()
+{
+ PushInt( int(IsString()) );
+}
+
+void ScInterpreter::ScIsNonString()
+{
+ PushInt( int(!IsString()) );
+}
+
+void ScInterpreter::ScIsLogical()
+{
+ bool bRes = false;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (GetCellErrCode(aCell) == FormulaError::NONE)
+ {
+ if (aCell.hasNumeric())
+ {
+ sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
+ bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL);
+ }
+ }
+ }
+ break;
+ case svMatrix:
+ {
+ double fVal;
+ svl::SharedString aStr;
+ ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
+ bRes = (nMatValType == ScMatValType::Boolean);
+ }
+ break;
+ default:
+ PopError();
+ if ( nGlobalError == FormulaError::NONE )
+ bRes = ( nCurFmtType == SvNumFormatType::LOGICAL );
+ }
+ nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+void ScInterpreter::ScType()
+{
+ short nType = 0;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (GetCellErrCode(aCell) == FormulaError::NONE)
+ {
+ switch (aCell.getType())
+ {
+ // NOTE: this is Xcl nonsense!
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ nType = 2;
+ break;
+ case CELLTYPE_VALUE :
+ {
+ sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
+ if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL)
+ nType = 4;
+ else
+ nType = 1;
+ }
+ break;
+ case CELLTYPE_NONE:
+ // always 1, s. tdf#73078
+ nType = 1;
+ break;
+ case CELLTYPE_FORMULA :
+ nType = 8;
+ break;
+ default:
+ PushIllegalArgument();
+ }
+ }
+ else
+ nType = 16;
+ }
+ break;
+ case svString:
+ PopError();
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ nType = 16;
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ nType = 2;
+ break;
+ case svMatrix:
+ PopMatrix();
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ nType = 16;
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ nType = 64;
+ // we could return the type of one element if in JumpMatrix or
+ // ForceArray mode, but Xcl doesn't ...
+ break;
+ default:
+ PopError();
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ nType = 16;
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ nType = 1;
+ }
+ PushInt( nType );
+}
+
+static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
+{
+ return pFormat && pFormat->GetColor( 1 );
+}
+
+static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
+{
+ return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
+}
+
+namespace {
+
+void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
+{
+ rFmtStr = pFormatter->GetCalcCellReturn( nFormat);
+}
+
+}
+
+void ScInterpreter::ScCell()
+{ // ATTRIBUTE ; [REF]
+ sal_uInt8 nParamCount = GetByte();
+ if( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ ScAddress aCellPos( aPos );
+ if( nParamCount == 2 )
+ {
+ switch (GetStackType())
+ {
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ // Let's handle external reference separately...
+ ScCellExternal();
+ return;
+ }
+ case svDoubleRef:
+ {
+ // Exceptionally not an intersecting position but top left.
+ // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
+ ScRange aRange;
+ PopDoubleRef( aRange);
+ aCellPos = aRange.aStart;
+ }
+ break;
+ case svSingleRef:
+ PopSingleRef( aCellPos);
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::NoRef);
+ }
+ }
+ OUString aInfoType = GetString().getString();
+ if (nGlobalError != FormulaError::NONE)
+ PushIllegalParameter();
+ else
+ {
+ ScRefCellValue aCell(mrDoc, aCellPos);
+
+ ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell);
+
+// *** ADDRESS INFO ***
+ if( aInfoType == "COL" )
+ { // column number (1-based)
+ PushInt( aCellPos.Col() + 1 );
+ }
+ else if( aInfoType == "ROW" )
+ { // row number (1-based)
+ PushInt( aCellPos.Row() + 1 );
+ }
+ else if( aInfoType == "SHEET" )
+ { // table number (1-based)
+ PushInt( aCellPos.Tab() + 1 );
+ }
+ else if( aInfoType == "ADDRESS" )
+ { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
+
+ // Follow the configurable string reference address syntax as also
+ // used by INDIRECT() (and ADDRESS() for the sheet separator).
+ FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
+ switch (eConv)
+ {
+ default:
+ // Use the current address syntax if unspecified or says
+ // one or the other or one we don't explicitly handle.
+ eConv = mrDoc.GetAddressConvention();
+ break;
+ case FormulaGrammar::CONV_OOO:
+ case FormulaGrammar::CONV_XL_A1:
+ case FormulaGrammar::CONV_XL_R1C1:
+ // Use that.
+ break;
+ }
+
+ ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
+ OUString aStr(aCellPos.Format(nFlags, &mrDoc, eConv));
+ PushString(aStr);
+ }
+ else if( aInfoType == "FILENAME" )
+ {
+ SCTAB nTab = aCellPos.Tab();
+ OUString aFuncResult;
+ if( nTab < mrDoc.GetTableCount() )
+ {
+ if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE )
+ mrDoc.GetName( nTab, aFuncResult );
+ else
+ {
+ ScDocShell* pShell = mrDoc.GetDocumentShell();
+ if( pShell && pShell->GetMedium() )
+ {
+ const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
+ OUString aTabName;
+ mrDoc.GetName( nTab, aTabName );
+
+ FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
+ if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
+ eConv = mrDoc.GetAddressConvention();
+
+ if (eConv == FormulaGrammar::CONV_XL_A1 ||
+ eConv == FormulaGrammar::CONV_XL_R1C1 ||
+ eConv == FormulaGrammar::CONV_XL_OOX)
+ {
+ // file name and table name: FILEPATH/[FILENAME]TABLE
+ aFuncResult = rURLObj.GetPartBeforeLastName()
+ + "[" + rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous)
+ + "]" + aTabName;
+ }
+ else
+ {
+ // file name and table name: 'FILEPATH/FILENAME'#$TABLE
+ aFuncResult = "'"
+ + rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)
+ + "'#$" + aTabName;
+ }
+ }
+ }
+ }
+ PushString( aFuncResult );
+ }
+ else if( aInfoType == "COORD" )
+ { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
+ // Yes, passing tab as col is intentional!
+ OUString aCellStr1 =
+ ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
+ (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() );
+ OUString aCellStr2 =
+ aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID),
+ nullptr, mrDoc.GetAddressConvention());
+ OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
+ PushString( aFuncResult );
+ }
+
+// *** CELL PROPERTIES ***
+ else if( aInfoType == "CONTENTS" )
+ { // contents of the cell, no formatting
+ if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ PushString( aStr );
+ }
+ else
+ PushDouble(GetCellValue(aCellPos, aCell));
+ }
+ else if( aInfoType == "TYPE" )
+ { // b = blank; l = string (label); v = otherwise (value)
+ sal_Unicode c;
+ if (aCell.hasString())
+ c = 'l';
+ else
+ c = aCell.hasNumeric() ? 'v' : 'b';
+ PushString( OUString(c) );
+ }
+ else if( aInfoType == "WIDTH" )
+ { // column width (rounded off as count of zero characters in standard font and size)
+ Printer* pPrinter = mrDoc.GetPrinter();
+ MapMode aOldMode( pPrinter->GetMapMode() );
+ vcl::Font aOldFont( pPrinter->GetFont() );
+ vcl::Font aDefFont;
+
+ pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
+ // font color doesn't matter here
+ mrDoc.GetDefPattern()->fillFontOnly(aDefFont, pPrinter);
+ pPrinter->SetFont(aDefFont);
+ tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
+ assert(nZeroWidth != 0);
+ pPrinter->SetFont( aOldFont );
+ pPrinter->SetMapMode( aOldMode );
+ int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
+ PushInt( nZeroCount );
+ }
+ else if( aInfoType == "PREFIX" )
+ { // ' = left; " = right; ^ = centered
+ sal_Unicode c = 0;
+ if (aCell.hasString())
+ {
+ const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
+ switch( pJustAttr->GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ case SvxCellHorJustify::Left:
+ case SvxCellHorJustify::Block: c = '\''; break;
+ case SvxCellHorJustify::Center: c = '^'; break;
+ case SvxCellHorJustify::Right: c = '"'; break;
+ case SvxCellHorJustify::Repeat: c = '\\'; break;
+ }
+ }
+ PushString( OUString(c) );
+ }
+ else if( aInfoType == "PROTECT" )
+ { // 1 = cell locked
+ const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
+ PushInt( pProtAttr->GetProtection() ? 1 : 0 );
+ }
+
+// *** FORMATTING ***
+ else if( aInfoType == "FORMAT" )
+ { // specific format code for standard formats
+ OUString aFuncResult;
+ sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
+ getFormatString(pFormatter, nFormat, aFuncResult);
+ PushString( aFuncResult );
+ }
+ else if( aInfoType == "COLOR" )
+ { // 1 = negative values are colored, otherwise 0
+ const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
+ PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
+ }
+ else if( aInfoType == "PARENTHESES" )
+ { // 1 = format string contains a '(' character, otherwise 0
+ const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
+ PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
+ }
+ else
+ PushIllegalArgument();
+ }
+}
+
+void ScInterpreter::ScCellExternal()
+{
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScSingleRefData aRef;
+ ScExternalRefCache::TokenRef pToken;
+ ScExternalRefCache::CellFormat aFmt;
+ PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ OUString aInfoType = GetString().getString();
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
+ SingleRefToVars(aRef, nCol, nRow, nTab);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ aRef.SetAbsTab(-1); // revert the value.
+
+ ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell);
+ ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
+
+ if ( aInfoType == "COL" )
+ PushInt(nCol + 1);
+ else if ( aInfoType == "ROW" )
+ PushInt(nRow + 1);
+ else if ( aInfoType == "SHEET" )
+ {
+ // For SHEET, No idea what number we should set, but let's always set
+ // 1 if the external sheet exists, no matter what sheet. Excel does
+ // the same.
+ if (pRefMgr->getCacheTable(nFileId, aTabName, false))
+ PushInt(1);
+ else
+ SetError(FormulaError::NoName);
+ }
+ else if ( aInfoType == "ADDRESS" )
+ {
+ // ODF 1.2 says we need to always display address using the ODF A1 grammar.
+ ScTokenArray aArray(mrDoc);
+ aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
+ ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1);
+ OUString aStr;
+ aComp.CreateStringFromTokenArray(aStr);
+ PushString(aStr);
+ }
+ else if ( aInfoType == "FILENAME" )
+ {
+ const OUString* p = pRefMgr->getExternalFileName(nFileId);
+ if (!p)
+ {
+ // In theory this should never happen...
+ SetError(FormulaError::NoName);
+ return;
+ }
+
+ OUString aBuf;
+ FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
+ if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
+ eConv = mrDoc.GetAddressConvention();
+
+ if (eConv == FormulaGrammar::CONV_XL_A1 ||
+ eConv == FormulaGrammar::CONV_XL_R1C1 ||
+ eConv == FormulaGrammar::CONV_XL_OOX)
+ {
+ // 'file URI/[FileName]SheetName
+ sal_Int32 nPos = p->lastIndexOf('/');
+ aBuf = OUString::Concat(p->subView(0, nPos + 1))
+ + "[" + p->subView(nPos + 1) + "]"
+ + aTabName;
+ }
+ else
+ {
+ // 'file URI'#$SheetName
+ aBuf = "'" + *p + "'#$" + aTabName;
+ }
+
+ PushString(aBuf);
+ }
+ else if ( aInfoType == "CONTENTS" )
+ {
+ switch (pToken->GetType())
+ {
+ case svString:
+ PushString(pToken->GetString());
+ break;
+ case svDouble:
+ PushString(OUString::number(pToken->GetDouble()));
+ break;
+ case svError:
+ PushString(ScGlobal::GetErrorString(pToken->GetError()));
+ break;
+ default:
+ PushString(OUString());
+ }
+ }
+ else if ( aInfoType == "TYPE" )
+ {
+ sal_Unicode c = 'v';
+ switch (pToken->GetType())
+ {
+ case svString:
+ c = 'l';
+ break;
+ case svEmptyCell:
+ c = 'b';
+ break;
+ default:
+ ;
+ }
+ PushString(OUString(c));
+ }
+ else if ( aInfoType == "FORMAT" )
+ {
+ OUString aFmtStr;
+ sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
+ getFormatString(pFormatter, nFmt, aFmtStr);
+ PushString(aFmtStr);
+ }
+ else if ( aInfoType == "COLOR" )
+ {
+ // 1 = negative values are colored, otherwise 0
+ int nVal = 0;
+ if (aFmt.mbIsSet)
+ {
+ const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
+ nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
+ }
+ PushInt(nVal);
+ }
+ else if ( aInfoType == "PARENTHESES" )
+ {
+ // 1 = format string contains a '(' character, otherwise 0
+ int nVal = 0;
+ if (aFmt.mbIsSet)
+ {
+ const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
+ nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
+ }
+ PushInt(nVal);
+ }
+ else
+ PushIllegalParameter();
+}
+
+void ScInterpreter::ScIsRef()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetStackType() )
+ {
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError == FormulaError::NONE )
+ bRes = true;
+ }
+ break;
+ case svDoubleRef :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if ( nGlobalError == FormulaError::NONE )
+ bRes = true;
+ }
+ break;
+ case svRefList :
+ {
+ FormulaConstTokenRef x = PopToken();
+ if ( nGlobalError == FormulaError::NONE )
+ bRes = !x->GetRefList()->empty();
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE)
+ bRes = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScExternalRefCache::TokenArrayRef pArray;
+ PopExternalDoubleRef(pArray);
+ if (nGlobalError == FormulaError::NONE)
+ bRes = true;
+ }
+ break;
+ default:
+ Pop();
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+void ScInterpreter::ScIsValue()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetRawStackType() )
+ {
+ case svDouble:
+ Pop();
+ bRes = true;
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (GetCellErrCode(aCell) == FormulaError::NONE)
+ {
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ bRes = true;
+ break;
+ case CELLTYPE_FORMULA :
+ bRes = (aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty());
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
+ bRes = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ {
+ if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
+ bRes = pMat->IsValue( 0, 0);
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
+ bRes = pMat->IsValue( nC, nR);
+ }
+ }
+ break;
+ default:
+ Pop();
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+void ScInterpreter::ScIsFormula()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ if (IsInArrayContext())
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ if (nTab1 != nTab2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
+ static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
+ if (!pResMat)
+ {
+ PushError( FormulaError::MatrixSize);
+ return;
+ }
+
+ /* TODO: we really should have a gap-aware cell iterator. */
+ SCSIZE i=0, j=0;
+ ScAddress aAdr( 0, 0, nTab1);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ aAdr.SetCol(nCol);
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ aAdr.SetRow(nRow);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ pResMat->PutBoolean( (aCell.getType() == CELLTYPE_FORMULA), i,j);
+ ++j;
+ }
+ ++i;
+ j = 0;
+ }
+
+ PushMatrix( pResMat);
+ return;
+ }
+ [[fallthrough]];
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
+ }
+ break;
+ default:
+ Pop();
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+void ScInterpreter::ScFormula()
+{
+ OUString aFormula;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ if (IsInArrayContext())
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ if (nTab1 != nTab2)
+ {
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+
+ ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
+ if (!pResMat)
+ break;
+
+ /* TODO: use a column iterator instead? */
+ SCSIZE i=0, j=0;
+ ScAddress aAdr(0,0,nTab1);
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ aAdr.SetCol(nCol);
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ aAdr.SetRow(nRow);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_FORMULA :
+ aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
+ pResMat->PutString( mrStrPool.intern( aFormula), i,j);
+ break;
+ default:
+ pResMat->PutError( FormulaError::NotAvailable, i,j);
+ }
+ ++j;
+ }
+ ++i;
+ j = 0;
+ }
+
+ PushMatrix( pResMat);
+ return;
+ }
+ [[fallthrough]];
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_FORMULA :
+ aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
+ break;
+ default:
+ SetError( FormulaError::NotAvailable );
+ }
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::NotAvailable );
+ }
+ PushString( aFormula );
+}
+
+void ScInterpreter::ScIsNV()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ bool bOk = PopDoubleRefOrSingleRef( aAdr );
+ if ( nGlobalError == FormulaError::NotAvailable )
+ bRes = true;
+ else if (bOk)
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ FormulaError nErr = GetCellErrCode(aCell);
+ bRes = (nErr == FormulaError::NotAvailable);
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NotAvailable ||
+ (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
+ bRes = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
+ }
+ }
+ break;
+ default:
+ PopError();
+ if ( nGlobalError == FormulaError::NotAvailable )
+ bRes = true;
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+void ScInterpreter::ScIsErr()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ bool bOk = PopDoubleRefOrSingleRef( aAdr );
+ if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
+ bRes = true;
+ else
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ FormulaError nErr = GetCellErrCode(aCell);
+ bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
+ (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
+ bRes = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( nGlobalError != FormulaError::NONE || !pMat )
+ bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
+ else if ( !pJumpMatrix )
+ {
+ FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
+ bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ {
+ FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
+ bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
+ }
+ }
+ }
+ break;
+ default:
+ PopError();
+ if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
+ bRes = true;
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+void ScInterpreter::ScIsError()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ bRes = true;
+ break;
+ }
+ if ( nGlobalError != FormulaError::NONE )
+ bRes = true;
+ else
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
+ bRes = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( nGlobalError != FormulaError::NONE || !pMat )
+ bRes = true;
+ else if ( !pJumpMatrix )
+ bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
+ }
+ }
+ break;
+ default:
+ PopError();
+ if ( nGlobalError != FormulaError::NONE )
+ bRes = true;
+ }
+ nGlobalError = FormulaError::NONE;
+ PushInt( int(bRes) );
+}
+
+bool ScInterpreter::IsEven()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ bool bRes = false;
+ double fVal = 0.0;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ FormulaError nErr = GetCellErrCode(aCell);
+ if (nErr != FormulaError::NONE)
+ SetError(nErr);
+ else
+ {
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ fVal = GetCellValue(aAdr, aCell);
+ bRes = true;
+ break;
+ case CELLTYPE_FORMULA :
+ if (aCell.getFormula()->IsValue())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ bRes = true;
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ break;
+ case svDouble:
+ {
+ fVal = PopDouble();
+ bRes = true;
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
+ {
+ fVal = pToken->GetDouble();
+ bRes = true;
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ {
+ bRes = pMat->IsValue( 0, 0);
+ if ( bRes )
+ fVal = pMat->GetDouble( 0, 0);
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ if ( nC < nCols && nR < nRows )
+ {
+ bRes = pMat->IsValue( nC, nR);
+ if ( bRes )
+ fVal = pMat->GetDouble( nC, nR);
+ }
+ else
+ SetError( FormulaError::NoValue);
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ if ( !bRes )
+ SetError( FormulaError::IllegalParameter);
+ else
+ bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
+ return bRes;
+}
+
+void ScInterpreter::ScIsEven()
+{
+ PushInt( int(IsEven()) );
+}
+
+void ScInterpreter::ScIsOdd()
+{
+ PushInt( int(!IsEven()) );
+}
+
+void ScInterpreter::ScN()
+{
+ FormulaError nErr = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+ // Temporarily override the ConvertStringToValue() error for
+ // GetCellValue() / GetCellValueOrZero()
+ FormulaError nSErr = mnStringNoValueError;
+ mnStringNoValueError = FormulaError::CellNoValue;
+ double fVal = GetDouble();
+ mnStringNoValueError = nSErr;
+ if (nErr != FormulaError::NONE)
+ nGlobalError = nErr; // preserve previous error if any
+ else if (nGlobalError == FormulaError::CellNoValue)
+ nGlobalError = FormulaError::NONE; // reset temporary detection error
+ PushDouble(fVal);
+}
+
+void ScInterpreter::ScTrim()
+{
+ // Doesn't only trim but also removes duplicated blanks within!
+ OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
+ OUStringBuffer aStr;
+ const sal_Unicode* p = aVal.getStr();
+ const sal_Unicode* const pEnd = p + aVal.getLength();
+ while ( p < pEnd )
+ {
+ if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
+ aStr.append(*p);
+ p++;
+ }
+ PushString(aStr.makeStringAndClear());
+}
+
+void ScInterpreter::ScUpper()
+{
+ OUString aString = ScGlobal::getCharClass().uppercase(GetString().getString());
+ PushString(aString);
+}
+
+void ScInterpreter::ScProper()
+{
+//2do: what to do with I18N-CJK ?!?
+ OUStringBuffer aStr(GetString().getString());
+ const sal_Int32 nLen = aStr.getLength();
+ if ( nLen > 0 )
+ {
+ OUString aUpr(ScGlobal::getCharClass().uppercase(aStr.toString()));
+ OUString aLwr(ScGlobal::getCharClass().lowercase(aStr.toString()));
+ aStr[0] = aUpr[0];
+ sal_Int32 nPos = 1;
+ while( nPos < nLen )
+ {
+ OUString aTmpStr( aStr[nPos-1] );
+ if ( !ScGlobal::getCharClass().isLetter( aTmpStr, 0 ) )
+ aStr[nPos] = aUpr[nPos];
+ else
+ aStr[nPos] = aLwr[nPos];
+ ++nPos;
+ }
+ }
+ PushString(aStr.makeStringAndClear());
+}
+
+void ScInterpreter::ScLower()
+{
+ OUString aString = ScGlobal::getCharClass().lowercase(GetString().getString());
+ PushString(aString);
+}
+
+void ScInterpreter::ScLen()
+{
+ OUString aStr = GetString().getString();
+ sal_Int32 nIdx = 0;
+ sal_Int32 nCnt = 0;
+ while ( nIdx < aStr.getLength() )
+ {
+ aStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ PushDouble( nCnt );
+}
+
+void ScInterpreter::ScT()
+{
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushInt(0);
+ return ;
+ }
+ bool bValue = false;
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (GetCellErrCode(aCell) == FormulaError::NONE)
+ {
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ bValue = true;
+ break;
+ case CELLTYPE_FORMULA :
+ bValue = aCell.getFormula()->IsValue();
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ if ( bValue )
+ PushString(OUString());
+ else
+ {
+ // like GetString()
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ PushString(aStr);
+ }
+ }
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ double fVal;
+ svl::SharedString aStr;
+ ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
+ if (ScMatrix::IsValueType( nMatValType))
+ PushString(svl::SharedString::getEmptyString());
+ else
+ PushString( aStr);
+ }
+ break;
+ case svDouble :
+ {
+ PopError();
+ PushString( OUString() );
+ }
+ break;
+ case svString :
+ ; // leave on stack
+ break;
+ default :
+ PushError( FormulaError::UnknownOpCode);
+ }
+}
+
+void ScInterpreter::ScValue()
+{
+ OUString aInputString;
+ double fVal;
+
+ switch ( GetRawStackType() )
+ {
+ case svMissing:
+ case svEmptyCell:
+ Pop();
+ PushInt(0);
+ return;
+ case svDouble:
+ return; // leave on stack
+
+ case svSingleRef:
+ case svDoubleRef:
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushInt(0);
+ return;
+ }
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasString())
+ {
+ svl::SharedString aSS;
+ GetCellString(aSS, aCell);
+ aInputString = aSS.getString();
+ }
+ else if (aCell.hasNumeric())
+ {
+ PushDouble( GetCellValue(aAdr, aCell) );
+ return;
+ }
+ else
+ {
+ PushDouble(0.0);
+ return;
+ }
+ }
+ break;
+ case svMatrix:
+ {
+ svl::SharedString aSS;
+ ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
+ aSS);
+ aInputString = aSS.getString();
+ switch (nType)
+ {
+ case ScMatValType::Empty:
+ fVal = 0.0;
+ [[fallthrough]];
+ case ScMatValType::Value:
+ case ScMatValType::Boolean:
+ PushDouble( fVal);
+ return;
+ case ScMatValType::String:
+ // evaluated below
+ break;
+ default:
+ PushIllegalArgument();
+ }
+ }
+ break;
+ default:
+ aInputString = GetString().getString();
+ break;
+ }
+
+ sal_uInt32 nFIndex = 0; // 0 for default locale
+ if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
+ PushDouble(fVal);
+ else
+ PushIllegalArgument();
+}
+
+// fdo#57180
+void ScInterpreter::ScNumberValue()
+{
+
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
+ return;
+
+ OUString aInputString;
+ OUString aGroupSeparator;
+ sal_Unicode cDecimalSeparator = 0;
+
+ if ( nParamCount == 3 )
+ aGroupSeparator = GetString().getString();
+
+ if ( nParamCount >= 2 )
+ {
+ OUString aDecimalSeparator = GetString().getString();
+ if ( aDecimalSeparator.getLength() == 1 )
+ cDecimalSeparator = aDecimalSeparator[ 0 ];
+ else
+ {
+ PushIllegalArgument(); //if given, separator length must be 1
+ return;
+ }
+ }
+
+ if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
+ {
+ PushIllegalArgument(); //decimal separator cannot appear in group separator
+ return;
+ }
+
+ switch (GetStackType())
+ {
+ case svDouble:
+ return; // leave on stack
+ default:
+ aInputString = GetString().getString();
+ }
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ PushError( nGlobalError );
+ return;
+ }
+ if ( aInputString.isEmpty() )
+ {
+ if ( maCalcConfig.mbEmptyStringAsZero )
+ PushDouble( 0.0 );
+ else
+ PushNoValue();
+ return;
+ }
+
+ sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
+ if ( nDecSep != 0 )
+ {
+ OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
+ sal_Int32 nIndex = 0;
+ while (nIndex < aGroupSeparator.getLength())
+ {
+ sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
+ aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
+ }
+ if ( nDecSep >= 0 )
+ aInputString = aTemporary + aInputString.subView( nDecSep );
+ else
+ aInputString = aTemporary;
+ }
+
+ for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
+ {
+ sal_Unicode c = aInputString[ i ];
+ if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
+ aInputString = aInputString.replaceAt( i, 1, u"" ); // remove spaces etc.
+ }
+ sal_Int32 nPercentCount = 0;
+ for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
+ {
+ aInputString = aInputString.replaceAt( i, 1, u"" ); // remove and count trailing '%'
+ nPercentCount++;
+ }
+
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nParseEnd;
+ double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
+ if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
+ {
+ if (nPercentCount)
+ fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
+ PushDouble(fVal);
+ return;
+ }
+ PushNoValue();
+}
+
+static bool lcl_ScInterpreter_IsPrintable( sal_uInt32 nCodePoint )
+{
+ return ( !u_isISOControl(nCodePoint) /*not in Cc*/
+ && u_isdefined(nCodePoint) /*not in Cn*/ );
+}
+
+
+void ScInterpreter::ScClean()
+{
+ OUString aStr = GetString().getString();
+
+ OUStringBuffer aBuf( aStr.getLength() );
+ sal_Int32 nIdx = 0;
+ while ( nIdx < aStr.getLength() )
+ {
+ sal_uInt32 c = aStr.iterateCodePoints( &nIdx );
+ if ( lcl_ScInterpreter_IsPrintable( c ) )
+ aBuf.appendUtf32( c );
+ }
+ PushString( aBuf.makeStringAndClear() );
+}
+
+
+void ScInterpreter::ScCode()
+{
+//2do: make it full range unicode?
+ OUString aStr = GetString().getString();
+ if (aStr.isEmpty())
+ PushInt(0);
+ else
+ {
+ //"classic" ByteString conversion flags
+ const sal_uInt32 convertFlags =
+ RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
+ RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
+ RTL_UNICODETOTEXT_FLAGS_FLUSH |
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
+ PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
+ }
+}
+
+void ScInterpreter::ScChar()
+{
+//2do: make it full range unicode?
+ double fVal = GetDouble();
+ if (fVal < 0.0 || fVal >= 256.0)
+ PushIllegalArgument();
+ else
+ {
+ //"classic" ByteString conversion flags
+ const sal_uInt32 convertFlags =
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
+ RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
+
+ char cEncodedChar = static_cast<char>(fVal);
+ OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
+ PushString(aStr);
+ }
+}
+
+/* #i70213# fullwidth/halfwidth conversion provided by
+ * Takashi Nakamoto <bluedwarf@ooo>
+ * erAck: added Excel compatibility conversions as seen in issue's test case. */
+
+static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
+{
+ // Make the initialization thread-safe. Since another function needs to be called, move it all to another
+ // function and thread-safely initialize a static reference in this function.
+ auto init = []() -> utl::TransliterationWrapper&
+ {
+ static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
+ trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEM );
+ return trans;
+ };
+ static utl::TransliterationWrapper& aTrans( init());
+ return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
+}
+
+static OUString lcl_convertIntoFullWidth( const OUString & rStr )
+{
+ auto init = []() -> utl::TransliterationWrapper&
+ {
+ static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
+ trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEM );
+ return trans;
+ };
+ static utl::TransliterationWrapper& aTrans( init());
+ return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
+}
+
+/* ODFF:
+ * Summary: Converts half-width to full-width ASCII and katakana characters.
+ * Semantics: Conversion is done for half-width ASCII and katakana characters,
+ * other characters are simply copied from T to the result. This is the
+ * complementary function to ASC.
+ * For references regarding halfwidth and fullwidth characters see
+ * http://www.unicode.org/reports/tr11/
+ * http://www.unicode.org/charts/charindex2.html#H
+ * http://www.unicode.org/charts/charindex2.html#F
+ */
+void ScInterpreter::ScJis()
+{
+ if (MustHaveParamCount( GetByte(), 1))
+ PushString( lcl_convertIntoFullWidth( GetString().getString()));
+}
+
+/* ODFF:
+ * Summary: Converts full-width to half-width ASCII and katakana characters.
+ * Semantics: Conversion is done for full-width ASCII and katakana characters,
+ * other characters are simply copied from T to the result. This is the
+ * complementary function to JIS.
+ */
+void ScInterpreter::ScAsc()
+{
+ if (MustHaveParamCount( GetByte(), 1))
+ PushString( lcl_convertIntoHalfWidth( GetString().getString()));
+}
+
+void ScInterpreter::ScUnicode()
+{
+ if ( MustHaveParamCount( GetByte(), 1 ) )
+ {
+ OUString aStr = GetString().getString();
+ if (aStr.isEmpty())
+ PushIllegalParameter();
+ else
+ {
+ PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
+ }
+ }
+}
+
+void ScInterpreter::ScUnichar()
+{
+ if ( MustHaveParamCount( GetByte(), 1 ) )
+ {
+ sal_uInt32 nCodePoint = GetUInt32();
+ if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
+ PushIllegalArgument();
+ else
+ {
+ OUString aStr( &nCodePoint, 1 );
+ PushString( aStr );
+ }
+ }
+}
+
+bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
+ const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
+{
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (!p || !p->IsArrayResult())
+ return false;
+
+ if (!xResMat)
+ {
+ // Create and init all elements with current value.
+ assert(nMatRows > 0);
+ xResMat = GetNewMat( 1, nMatRows, true);
+ xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
+ }
+ else if (bDoMatOp)
+ {
+ // Current value and values from vector are operands
+ // for each vector position.
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ MatOpFunc( i, fCurrent);
+ }
+ }
+ return true;
+}
+
+void ScInterpreter::ScMin( bool bTextAsZero )
+{
+ short nParamCount = GetByte();
+ if (!MustHaveParamCountMin( nParamCount, 1))
+ return;
+
+ ScMatrixRef xResMat;
+ double nMin = ::std::numeric_limits<double>::max();
+ auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
+ {
+ double fVecRes = xResMat->GetDouble(0,i);
+ if (fVecRes > fCurMin)
+ xResMat->PutDouble( fCurMin, 0,i);
+ };
+ const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
+
+ double nVal = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while (nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ nVal = GetDouble();
+ if (nMin > nVal) nMin = nVal;
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ }
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ nVal = GetCellValue(aAdr, aCell);
+ CurFmtToFuncFmt();
+ if (nMin > nVal) nMin = nVal;
+ }
+ else if (bTextAsZero && aCell.hasString())
+ {
+ if ( nMin > 0.0 )
+ nMin = 0.0;
+ }
+ }
+ break;
+ case svRefList :
+ {
+ // bDoMatOp only for non-array value when switching to
+ // ArrayRefList.
+ if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
+ nRefArrayPos == std::numeric_limits<size_t>::max()))
+ {
+ nRefArrayPos = nRefInList;
+ }
+ }
+ [[fallthrough]];
+ case svDoubleRef :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
+ if (aValIter.GetFirst(nVal, nErr))
+ {
+ if (nMin > nVal)
+ nMin = nVal;
+ aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
+ {
+ if (nMin > nVal)
+ nMin = nVal;
+ }
+ SetError(nErr);
+ }
+ if (nRefArrayPos != std::numeric_limits<size_t>::max())
+ {
+ // Update vector element with current value.
+ MatOpFunc( nRefArrayPos, nMin);
+
+ // Reset.
+ nMin = std::numeric_limits<double>::max();
+ nVal = 0.0;
+ nRefArrayPos = std::numeric_limits<size_t>::max();
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
+ if (nMin > nVal)
+ nMin = nVal;
+ }
+ }
+ break;
+ case svString :
+ {
+ Pop();
+ if ( bTextAsZero )
+ {
+ if ( nMin > 0.0 )
+ nMin = 0.0;
+ }
+ else
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ default :
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+
+ if (xResMat)
+ {
+ // Include value of last non-references-array type and calculate final result.
+ if (nMin < std::numeric_limits<double>::max())
+ {
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ MatOpFunc( i, nMin);
+ }
+ }
+ else
+ {
+ /* TODO: the awkward "no value is minimum 0.0" is likely the case
+ * if a value is numeric_limits::max. Still, that could be a valid
+ * minimum value as well, but nVal and nMin had been reset after
+ * the last svRefList... so we may lie here. */
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ double fVecRes = xResMat->GetDouble(0,i);
+ if (fVecRes == std::numeric_limits<double>::max())
+ xResMat->PutDouble( 0.0, 0,i);
+ }
+ }
+ PushMatrix( xResMat);
+ }
+ else
+ {
+ if (!std::isfinite(nVal))
+ PushError( GetDoubleErrorValue( nVal));
+ else if ( nVal < nMin )
+ PushDouble(0.0); // zero or only empty arguments
+ else
+ PushDouble(nMin);
+ }
+}
+
+void ScInterpreter::ScMax( bool bTextAsZero )
+{
+ short nParamCount = GetByte();
+ if (!MustHaveParamCountMin( nParamCount, 1))
+ return;
+
+ ScMatrixRef xResMat;
+ double nMax = std::numeric_limits<double>::lowest();
+ auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
+ {
+ double fVecRes = xResMat->GetDouble(0,i);
+ if (fVecRes < fCurMax)
+ xResMat->PutDouble( fCurMax, 0,i);
+ };
+ const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
+
+ double nVal = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while (nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ nVal = GetDouble();
+ if (nMax < nVal) nMax = nVal;
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ }
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ nVal = GetCellValue(aAdr, aCell);
+ CurFmtToFuncFmt();
+ if (nMax < nVal) nMax = nVal;
+ }
+ else if (bTextAsZero && aCell.hasString())
+ {
+ if ( nMax < 0.0 )
+ nMax = 0.0;
+ }
+ }
+ break;
+ case svRefList :
+ {
+ // bDoMatOp only for non-array value when switching to
+ // ArrayRefList.
+ if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
+ nRefArrayPos == std::numeric_limits<size_t>::max()))
+ {
+ nRefArrayPos = nRefInList;
+ }
+ }
+ [[fallthrough]];
+ case svDoubleRef :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
+ if (aValIter.GetFirst(nVal, nErr))
+ {
+ if (nMax < nVal)
+ nMax = nVal;
+ aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
+ {
+ if (nMax < nVal)
+ nMax = nVal;
+ }
+ SetError(nErr);
+ }
+ if (nRefArrayPos != std::numeric_limits<size_t>::max())
+ {
+ // Update vector element with current value.
+ MatOpFunc( nRefArrayPos, nMax);
+
+ // Reset.
+ nMax = std::numeric_limits<double>::lowest();
+ nVal = 0.0;
+ nRefArrayPos = std::numeric_limits<size_t>::max();
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
+ if (nMax < nVal)
+ nMax = nVal;
+ }
+ }
+ break;
+ case svString :
+ {
+ Pop();
+ if ( bTextAsZero )
+ {
+ if ( nMax < 0.0 )
+ nMax = 0.0;
+ }
+ else
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ default :
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+
+ if (xResMat)
+ {
+ // Include value of last non-references-array type and calculate final result.
+ if (nMax > std::numeric_limits<double>::lowest())
+ {
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ MatOpFunc( i, nMax);
+ }
+ }
+ else
+ {
+ /* TODO: the awkward "no value is maximum 0.0" is likely the case
+ * if a value is numeric_limits::lowest. Still, that could be a
+ * valid maximum value as well, but nVal and nMax had been reset
+ * after the last svRefList... so we may lie here. */
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ double fVecRes = xResMat->GetDouble(0,i);
+ if (fVecRes == -std::numeric_limits<double>::max())
+ xResMat->PutDouble( 0.0, 0,i);
+ }
+ }
+ PushMatrix( xResMat);
+ }
+ else
+ {
+ if (!std::isfinite(nVal))
+ PushError( GetDoubleErrorValue( nVal));
+ else if ( nVal > nMax )
+ PushDouble(0.0); // zero or only empty arguments
+ else
+ PushDouble(nMax);
+ }
+}
+
+void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
+{
+ short nParamCount = GetByte();
+ const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
+
+ struct ArrayRefListValue
+ {
+ std::vector<double> mvValues;
+ KahanSum mfSum;
+ ArrayRefListValue() = default;
+ double get() const { return mfSum.get(); }
+ };
+ std::vector<ArrayRefListValue> vArrayValues;
+
+ std::vector<double> values;
+ KahanSum fSum = 0.0;
+ double fVal = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ fVal = GetDouble();
+ if (nGlobalError == FormulaError::NONE)
+ {
+ values.push_back(fVal);
+ fSum += fVal;
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ values.push_back(fVal);
+ fSum += fVal;
+ }
+ }
+ else if (bTextAsZero && aCell.hasString())
+ {
+ values.push_back(0.0);
+ }
+ }
+ break;
+ case svRefList :
+ {
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (p && p->IsArrayResult())
+ {
+ size_t nRefArrayPos = nRefInList;
+ if (vArrayValues.empty())
+ {
+ // Create and init all elements with current value.
+ assert(nMatRows > 0);
+ vArrayValues.resize(nMatRows);
+ for (ArrayRefListValue & it : vArrayValues)
+ {
+ it.mvValues = values;
+ it.mfSum = fSum;
+ }
+ }
+ else
+ {
+ // Current value and values from vector are operands
+ // for each vector position.
+ for (ArrayRefListValue & it : vArrayValues)
+ {
+ it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
+ it.mfSum += fSum;
+ }
+ }
+ ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
+ if (aValIter.GetFirst(fVal, nErr))
+ {
+ do
+ {
+ rArrayValue.mvValues.push_back(fVal);
+ rArrayValue.mfSum += fVal;
+ }
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
+ }
+ if ( nErr != FormulaError::NONE )
+ {
+ rArrayValue.mfSum = CreateDoubleError( nErr);
+ }
+ // Reset.
+ std::vector<double>().swap(values);
+ fSum = 0.0;
+ break;
+ }
+ }
+ [[fallthrough]];
+ case svDoubleRef :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
+ if (aValIter.GetFirst(fVal, nErr))
+ {
+ do
+ {
+ values.push_back(fVal);
+ fSum += fVal;
+ }
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
+ }
+ if ( nErr != FormulaError::NONE )
+ {
+ SetError(nErr);
+ }
+ }
+ break;
+ case svExternalSingleRef :
+ case svExternalDoubleRef :
+ case svMatrix :
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
+ {
+ for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
+ {
+ if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
+ {
+ fVal= pMat->GetDouble(nMatCol,nMatRow);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ values.push_back(fVal);
+ fSum += fVal;
+ }
+ else if (bIgnoreErrVal)
+ nGlobalError = FormulaError::NONE;
+ }
+ else if ( bTextAsZero )
+ {
+ values.push_back(0.0);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case svString :
+ {
+ Pop();
+ if ( bTextAsZero )
+ {
+ values.push_back(0.0);
+ }
+ else
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ default :
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+
+ if (!vArrayValues.empty())
+ {
+ // Include value of last non-references-array type and calculate final result.
+ if (!values.empty())
+ {
+ for (auto & it : vArrayValues)
+ {
+ it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
+ it.mfSum += fSum;
+ }
+ }
+ ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
+ for (SCSIZE r=0; r < nMatRows; ++r)
+ {
+ ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
+ if (!n)
+ xResMat->PutError( FormulaError::DivisionByZero, 0, r);
+ else
+ {
+ ArrayRefListValue& rArrayValue = vArrayValues[r];
+ double vSum = 0.0;
+ const double vMean = rArrayValue.get() / n;
+ for (::std::vector<double>::size_type i = 0; i < n; i++)
+ vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
+ ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
+ xResMat->PutDouble( VarResult( vSum, n), 0, r);
+ }
+ }
+ PushMatrix( xResMat);
+ }
+ else
+ {
+ ::std::vector<double>::size_type n = values.size();
+ if (!n)
+ SetError( FormulaError::DivisionByZero);
+ double vSum = 0.0;
+ if (nGlobalError == FormulaError::NONE)
+ {
+ const double vMean = fSum.get() / n;
+ for (::std::vector<double>::size_type i = 0; i < n; i++)
+ vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
+ }
+ PushDouble( VarResult( vSum, n));
+ }
+}
+
+void ScInterpreter::ScVar( bool bTextAsZero )
+{
+ auto VarResult = []( double fVal, size_t nValCount )
+ {
+ if (nValCount <= 1)
+ return CreateDoubleError( FormulaError::DivisionByZero );
+ else
+ return fVal / (nValCount - 1);
+ };
+ GetStVarParams( bTextAsZero, VarResult );
+}
+
+void ScInterpreter::ScVarP( bool bTextAsZero )
+{
+ auto VarResult = []( double fVal, size_t nValCount )
+ {
+ return sc::div( fVal, nValCount);
+ };
+ GetStVarParams( bTextAsZero, VarResult );
+
+}
+
+void ScInterpreter::ScStDev( bool bTextAsZero )
+{
+ auto VarResult = []( double fVal, size_t nValCount )
+ {
+ if (nValCount <= 1)
+ return CreateDoubleError( FormulaError::DivisionByZero );
+ else
+ return sqrt( fVal / (nValCount - 1));
+ };
+ GetStVarParams( bTextAsZero, VarResult );
+}
+
+void ScInterpreter::ScStDevP( bool bTextAsZero )
+{
+ auto VarResult = []( double fVal, size_t nValCount )
+ {
+ if (nValCount == 0)
+ return CreateDoubleError( FormulaError::DivisionByZero );
+ else
+ return sqrt( fVal / nValCount);
+ };
+ GetStVarParams( bTextAsZero, VarResult );
+
+ /* this was: PushDouble( sqrt( div( nVal, nValCount)));
+ *
+ * Besides that the special NAN gets lost in the call through sqrt(),
+ * unxlngi6.pro then looped back and forth somewhere between div() and
+ * ::rtl::math::setNan(). Tests showed that
+ *
+ * sqrt( div( 1, 0));
+ *
+ * produced a loop, but
+ *
+ * double f1 = div( 1, 0);
+ * sqrt( f1 );
+ *
+ * was fine. There seems to be some compiler optimization problem. It does
+ * not occur when compiled with debug=t.
+ */
+}
+
+void ScInterpreter::ScColumns()
+{
+ sal_uInt8 nParamCount = GetByte();
+ sal_uLong nVal = 0;
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ while (nParamCount-- > 0)
+ {
+ switch ( GetStackType() )
+ {
+ case svSingleRef:
+ PopError();
+ nVal++;
+ break;
+ case svDoubleRef:
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
+ static_cast<sal_uLong>(nCol2 - nCol1 + 1);
+ break;
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ nVal += nC;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ PopError();
+ nVal++;
+ break;
+ case svExternalDoubleRef:
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aRef;
+ PopExternalDoubleRef( nFileId, aTabName, aRef);
+ ScRange aAbs = aRef.toAbs(mrDoc, aPos);
+ nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
+ static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
+ }
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+ PushDouble(static_cast<double>(nVal));
+}
+
+void ScInterpreter::ScRows()
+{
+ sal_uInt8 nParamCount = GetByte();
+ sal_uLong nVal = 0;
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ while (nParamCount-- > 0)
+ {
+ switch ( GetStackType() )
+ {
+ case svSingleRef:
+ PopError();
+ nVal++;
+ break;
+ case svDoubleRef:
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
+ static_cast<sal_uLong>(nRow2 - nRow1 + 1);
+ break;
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ nVal += nR;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ PopError();
+ nVal++;
+ break;
+ case svExternalDoubleRef:
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aRef;
+ PopExternalDoubleRef( nFileId, aTabName, aRef);
+ ScRange aAbs = aRef.toAbs(mrDoc, aPos);
+ nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
+ static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
+ }
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+ PushDouble(static_cast<double>(nVal));
+}
+
+void ScInterpreter::ScSheets()
+{
+ sal_uInt8 nParamCount = GetByte();
+ sal_uLong nVal;
+ if ( nParamCount == 0 )
+ nVal = mrDoc.GetTableCount();
+ else
+ {
+ nVal = 0;
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
+ {
+ switch ( GetStackType() )
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ PopError();
+ nVal++;
+ break;
+ case svDoubleRef:
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
+ break;
+ case svExternalDoubleRef:
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aRef;
+ PopExternalDoubleRef( nFileId, aTabName, aRef);
+ ScRange aAbs = aRef.toAbs(mrDoc, aPos);
+ nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter );
+ }
+ }
+ }
+ PushDouble( static_cast<double>(nVal) );
+}
+
+void ScInterpreter::ScColumn()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
+ return;
+
+ double nVal = 0.0;
+ if (nParamCount == 0)
+ {
+ nVal = aPos.Col() + 1;
+ if (bMatrixFormula)
+ {
+ SCCOL nCols = 0;
+ SCROW nRows = 0;
+ if (pMyFormulaCell)
+ pMyFormulaCell->GetMatColsRows( nCols, nRows);
+ bool bMayBeScalar;
+ if (nCols == 0)
+ {
+ // Happens if called via ScViewFunc::EnterMatrix()
+ // ScFormulaCell::GetResultDimensions() as of course a
+ // matrix result is not available yet.
+ nCols = 1;
+ bMayBeScalar = false;
+ }
+ else
+ {
+ bMayBeScalar = true;
+ }
+ if (!bMayBeScalar || nCols != 1 || nRows != 1)
+ {
+ ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1, /*bEmpty*/true );
+ if (pResMat)
+ {
+ for (SCCOL i=0; i < nCols; ++i)
+ pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
+ PushMatrix( pResMat);
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ switch ( GetStackType() )
+ {
+ case svSingleRef :
+ {
+ SCCOL nCol1(0);
+ SCROW nRow1(0);
+ SCTAB nTab1(0);
+ PopSingleRef( nCol1, nRow1, nTab1 );
+ nVal = static_cast<double>(nCol1 + 1);
+ }
+ break;
+ case svExternalSingleRef :
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScSingleRefData aRef;
+ PopExternalSingleRef( nFileId, aTabName, aRef );
+ ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
+ nVal = static_cast<double>( aAbsRef.Col() + 1 );
+ }
+ break;
+
+ case svDoubleRef :
+ case svExternalDoubleRef :
+ {
+ SCCOL nCol1;
+ SCCOL nCol2;
+ if ( GetStackType() == svDoubleRef )
+ {
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ }
+ else
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aRef;
+ PopExternalDoubleRef( nFileId, aTabName, aRef );
+ ScRange aAbs = aRef.toAbs(mrDoc, aPos);
+ nCol1 = aAbs.aStart.Col();
+ nCol2 = aAbs.aEnd.Col();
+ }
+ if (nCol2 > nCol1)
+ {
+ ScMatrixRef pResMat = GetNewMat(
+ static_cast<SCSIZE>(nCol2-nCol1+1), 1, /*bEmpty*/true);
+ if (pResMat)
+ {
+ for (SCCOL i = nCol1; i <= nCol2; i++)
+ pResMat->PutDouble(static_cast<double>(i+1),
+ static_cast<SCSIZE>(i-nCol1), 0);
+ PushMatrix(pResMat);
+ return;
+ }
+ }
+ else
+ nVal = static_cast<double>(nCol1 + 1);
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter );
+ }
+ }
+ PushDouble( nVal );
+}
+
+void ScInterpreter::ScRow()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
+ return;
+
+ double nVal = 0.0;
+ if (nParamCount == 0)
+ {
+ nVal = aPos.Row() + 1;
+ if (bMatrixFormula)
+ {
+ SCCOL nCols = 0;
+ SCROW nRows = 0;
+ if (pMyFormulaCell)
+ pMyFormulaCell->GetMatColsRows( nCols, nRows);
+ bool bMayBeScalar;
+ if (nRows == 0)
+ {
+ // Happens if called via ScViewFunc::EnterMatrix()
+ // ScFormulaCell::GetResultDimensions() as of course a
+ // matrix result is not available yet.
+ nRows = 1;
+ bMayBeScalar = false;
+ }
+ else
+ {
+ bMayBeScalar = true;
+ }
+ if (!bMayBeScalar || nCols != 1 || nRows != 1)
+ {
+ ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows), /*bEmpty*/true);
+ if (pResMat)
+ {
+ for (SCROW i=0; i < nRows; i++)
+ pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
+ PushMatrix( pResMat);
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ switch ( GetStackType() )
+ {
+ case svSingleRef :
+ {
+ SCCOL nCol1(0);
+ SCROW nRow1(0);
+ SCTAB nTab1(0);
+ PopSingleRef( nCol1, nRow1, nTab1 );
+ nVal = static_cast<double>(nRow1 + 1);
+ }
+ break;
+ case svExternalSingleRef :
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScSingleRefData aRef;
+ PopExternalSingleRef( nFileId, aTabName, aRef );
+ ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
+ nVal = static_cast<double>( aAbsRef.Row() + 1 );
+ }
+ break;
+ case svDoubleRef :
+ case svExternalDoubleRef :
+ {
+ SCROW nRow1;
+ SCROW nRow2;
+ if ( GetStackType() == svDoubleRef )
+ {
+ SCCOL nCol1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCTAB nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ }
+ else
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aRef;
+ PopExternalDoubleRef( nFileId, aTabName, aRef );
+ ScRange aAbs = aRef.toAbs(mrDoc, aPos);
+ nRow1 = aAbs.aStart.Row();
+ nRow2 = aAbs.aEnd.Row();
+ }
+ if (nRow2 > nRow1)
+ {
+ ScMatrixRef pResMat = GetNewMat( 1,
+ static_cast<SCSIZE>(nRow2-nRow1+1), /*bEmpty*/true);
+ if (pResMat)
+ {
+ for (SCROW i = nRow1; i <= nRow2; i++)
+ pResMat->PutDouble(static_cast<double>(i+1), 0,
+ static_cast<SCSIZE>(i-nRow1));
+ PushMatrix(pResMat);
+ return;
+ }
+ }
+ else
+ nVal = static_cast<double>(nRow1 + 1);
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter );
+ }
+ }
+ PushDouble( nVal );
+}
+
+void ScInterpreter::ScSheet()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
+ return;
+
+ SCTAB nVal = 0;
+ if ( nParamCount == 0 )
+ nVal = aPos.Tab() + 1;
+ else
+ {
+ switch ( GetStackType() )
+ {
+ case svString :
+ {
+ svl::SharedString aStr = PopString();
+ if ( mrDoc.GetTable(aStr.getString(), nVal))
+ ++nVal;
+ else
+ SetError( FormulaError::IllegalArgument );
+ }
+ break;
+ case svSingleRef :
+ {
+ SCCOL nCol1(0);
+ SCROW nRow1(0);
+ SCTAB nTab1(0);
+ PopSingleRef(nCol1, nRow1, nTab1);
+ nVal = nTab1 + 1;
+ }
+ break;
+ case svDoubleRef :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ nVal = nTab1 + 1;
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter );
+ }
+ if ( nGlobalError != FormulaError::NONE )
+ nVal = 0;
+ }
+ PushDouble( static_cast<double>(nVal) );
+}
+
+namespace {
+
+class VectorMatrixAccessor
+{
+public:
+ VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
+ mrMat(rMat), mbColVec(bColVec) {}
+
+ bool IsEmpty(SCSIZE i) const
+ {
+ return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
+ }
+
+ bool IsEmptyPath(SCSIZE i) const
+ {
+ return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
+ }
+
+ bool IsValue(SCSIZE i) const
+ {
+ return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
+ }
+
+ bool IsStringOrEmpty(SCSIZE i) const
+ {
+ return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
+ }
+
+ double GetDouble(SCSIZE i) const
+ {
+ return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
+ }
+
+ OUString GetString(SCSIZE i) const
+ {
+ return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
+ }
+
+ SCSIZE GetElementCount() const
+ {
+ SCSIZE nC, nR;
+ mrMat.GetDimensions(nC, nR);
+ return mbColVec ? nR : nC;
+ }
+
+private:
+ const ScMatrix& mrMat;
+ bool mbColVec;
+};
+
+/** returns -1 when the matrix value is smaller than the query value, 0 when
+ they are equal, and 1 when the matrix value is larger than the query
+ value. */
+sal_Int32 lcl_CompareMatrix2Query(
+ SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
+{
+ if (rMat.IsEmpty(i))
+ {
+ /* TODO: in case we introduced query for real empty this would have to
+ * be changed! */
+ return -1; // empty always less than anything else
+ }
+
+ /* FIXME: what is an empty path (result of IF(false;true_path) in
+ * comparisons? */
+
+ bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
+ if (rMat.IsValue(i))
+ {
+ const double nVal1 = rMat.GetDouble(i);
+ if (!std::isfinite(nVal1))
+ {
+ // XXX Querying for error values is not required, otherwise we'd
+ // need to check here.
+ return 1; // error always greater than numeric or string
+ }
+
+ if (bByString)
+ return -1; // numeric always less than string
+
+ const double nVal2 = rEntry.GetQueryItem().mfVal;
+ // XXX Querying for error values is not required, otherwise we'd need
+ // to check here and move that check before the bByString check.
+ if (nVal1 == nVal2)
+ return 0;
+
+ return nVal1 < nVal2 ? -1 : 1;
+ }
+
+ if (!bByString)
+ return 1; // string always greater than numeric
+
+ OUString aStr1 = rMat.GetString(i);
+ OUString aStr2 = rEntry.GetQueryItem().maString.getString();
+
+ return ScGlobal::GetCollator().compareString(aStr1, aStr2); // case-insensitive
+}
+
+/** returns the last item with the identical value as the original item
+ value. */
+void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
+ SCSIZE nMatCount)
+{
+ if (rMat.IsValue(rIndex))
+ {
+ double nVal = rMat.GetDouble(rIndex);
+ while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
+ nVal == rMat.GetDouble(rIndex+1))
+ ++rIndex;
+ }
+ // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
+ else if (rMat.IsEmptyPath(rIndex))
+ {
+ while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
+ ++rIndex;
+ }
+ else if (rMat.IsEmpty(rIndex))
+ {
+ while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
+ ++rIndex;
+ }
+ else if (rMat.IsStringOrEmpty(rIndex))
+ {
+ OUString aStr( rMat.GetString(rIndex));
+ while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
+ aStr == rMat.GetString(rIndex+1))
+ ++rIndex;
+ }
+ else
+ {
+ OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
+ }
+}
+
+}
+
+void ScInterpreter::ScMatch()
+{
+
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ double fTyp;
+ if (nParamCount == 3)
+ fTyp = GetDouble();
+ else
+ fTyp = 1.0;
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ ScMatrixRef pMatSrc = nullptr;
+
+ switch (GetStackType())
+ {
+ case svSingleRef:
+ PopSingleRef( nCol1, nRow1, nTab1);
+ nCol2 = nCol1;
+ nRow2 = nRow1;
+ break;
+ case svDoubleRef:
+ {
+ SCTAB nTab2 = 0;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
+ {
+ PushIllegalParameter();
+ return;
+ }
+ }
+ break;
+ case svMatrix:
+ case svExternalDoubleRef:
+ {
+ if (GetStackType() == svMatrix)
+ pMatSrc = PopMatrix();
+ else
+ PopExternalDoubleRef(pMatSrc);
+
+ if (!pMatSrc)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ }
+ break;
+ default:
+ PushIllegalParameter();
+ return;
+ }
+
+ if (nGlobalError == FormulaError::NONE)
+ {
+ double fVal;
+ ScQueryParam rParam;
+ rParam.nCol1 = nCol1;
+ rParam.nRow1 = nRow1;
+ rParam.nCol2 = nCol2;
+ rParam.nTab = nTab1;
+ const ScComplexRefData* refData = nullptr;
+
+ ScQueryEntry& rEntry = rParam.GetEntry(0);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ rEntry.bDoQuery = true;
+ if (fTyp < 0.0)
+ rEntry.eOp = SC_GREATER_EQUAL;
+ else if (fTyp > 0.0)
+ rEntry.eOp = SC_LESS_EQUAL;
+ switch ( GetStackType() )
+ {
+ case svDouble:
+ {
+ fVal = GetDouble();
+ rItem.mfVal = fVal;
+ rItem.meType = ScQueryEntry::ByValue;
+ }
+ break;
+ case svString:
+ {
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.maString = GetString();
+ }
+ break;
+ case svDoubleRef :
+ refData = GetStackDoubleRef();
+ [[fallthrough]];
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushInt(0);
+ return ;
+ }
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = fVal;
+ }
+ else
+ {
+ GetCellString(rItem.maString, aCell);
+ rItem.meType = ScQueryEntry::ByString;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ if (pToken->GetType() == svDouble)
+ {
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = pToken->GetDouble();
+ }
+ else
+ {
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.maString = pToken->GetString();
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix :
+ {
+ svl::SharedString aStr;
+ ScMatValType nType = GetDoubleOrStringFromMatrix(
+ rItem.mfVal, aStr);
+ rItem.maString = aStr;
+ rItem.meType = ScMatrix::IsNonValueType(nType) ?
+ ScQueryEntry::ByString : ScQueryEntry::ByValue;
+ }
+ break;
+ default:
+ {
+ PushIllegalParameter();
+ return;
+ }
+ }
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ bool bIsVBAMode = mrDoc.IsInVBAMode();
+
+ if ( bIsVBAMode )
+ rParam.eSearchType = utl::SearchParam::SearchType::Wildcard;
+ else
+ rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc);
+ }
+
+ if (pMatSrc) // The source data is matrix array.
+ {
+ SCSIZE nC, nR;
+ pMatSrc->GetDimensions( nC, nR);
+ if (nC > 1 && nR > 1)
+ {
+ // The source matrix must be a vector.
+ PushIllegalParameter();
+ return;
+ }
+
+ // Do not propagate errors from matrix while searching.
+ pMatSrc->SetErrorInterpreter( nullptr);
+
+ SCSIZE nMatCount = (nC == 1) ? nR : nC;
+ VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
+
+ // simple serial search for equality mode (source data doesn't
+ // need to be sorted).
+
+ if (rEntry.eOp == SC_EQUAL)
+ {
+ for (SCSIZE i = 0; i < nMatCount; ++i)
+ {
+ if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
+ {
+ PushDouble(i+1); // found !
+ return;
+ }
+ }
+ PushNA(); // not found
+ return;
+ }
+
+ // binary search for non-equality mode (the source data is
+ // assumed to be sorted).
+
+ bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
+ SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
+ for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
+ {
+ SCSIZE nMid = nFirst + nLen/2;
+ sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
+ if (nCmp == 0)
+ {
+ // exact match. find the last item with the same value.
+ lcl_GetLastMatch( nMid, aMatAcc, nMatCount);
+ PushDouble( nMid+1);
+ return;
+ }
+
+ if (nLen == 1) // first and last items are next to each other.
+ {
+ if (nCmp < 0)
+ nHitIndex = bAscOrder ? nLast : nFirst;
+ else
+ nHitIndex = bAscOrder ? nFirst : nLast;
+ break;
+ }
+
+ if (nCmp < 0)
+ {
+ if (bAscOrder)
+ nFirst = nMid;
+ else
+ nLast = nMid;
+ }
+ else
+ {
+ if (bAscOrder)
+ nLast = nMid;
+ else
+ nFirst = nMid;
+ }
+ }
+
+ if (nHitIndex == nMatCount-1) // last item
+ {
+ sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
+ if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
+ {
+ // either the last item is an exact match or the real
+ // hit is beyond the last item.
+ PushDouble( nHitIndex+1);
+ return;
+ }
+ }
+
+ if (nHitIndex > 0) // valid hit must be 2nd item or higher
+ {
+ if ( ! ( rItem.meType == ScQueryEntry::ByString && aMatAcc.IsValue( nHitIndex-1 ) ) &&
+ ! ( rItem.meType == ScQueryEntry::ByValue && !aMatAcc.IsValue( nHitIndex-1 ) ) )
+ PushDouble( nHitIndex); // non-exact match
+ else
+ PushNA();
+ return;
+ }
+
+ PushNA();
+ return;
+ }
+
+ // The source data is cell range.
+ SCCOLROW nDelta = 0;
+ if (nCol1 == nCol2)
+ { // search row in column
+ rParam.nRow2 = nRow2;
+ rEntry.nField = nCol1;
+ ScAddress aResultPos( nCol1, nRow1, nTab1);
+ if (!LookupQueryWithCache( aResultPos, rParam, refData))
+ {
+ PushNA();
+ return;
+ }
+ nDelta = aResultPos.Row() - nRow1;
+ }
+ else
+ { // search column in row
+ SCCOL nC;
+ rParam.bByRow = false;
+ rParam.nRow2 = nRow1;
+ rEntry.nField = nCol1;
+ ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
+ // Advance Entry.nField in Iterator if column changed
+ aCellIter.SetAdvanceQueryParamEntryField( true );
+ if (fTyp == 0.0)
+ { // EQUAL
+ if ( aCellIter.GetFirst() )
+ nC = aCellIter.GetCol();
+ else
+ {
+ PushNA();
+ return;
+ }
+ }
+ else
+ { // <= or >=
+ SCROW nR;
+ if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
+ {
+ PushNA();
+ return;
+ }
+ }
+ nDelta = nC - nCol1;
+ }
+ PushDouble(static_cast<double>(nDelta + 1));
+ }
+ else
+ PushIllegalParameter();
+}
+
+namespace {
+
+bool isCellContentEmpty( const ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ return false;
+ case CELLTYPE_FORMULA:
+ {
+ // NOTE: Excel treats ="" in a referenced cell as blank in
+ // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
+ // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
+ // the cell content.
+ // ODFF allows both for COUNTBLANK().
+ // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
+ // COUNTBLANK(), we now do for Excel interoperability.
+ /* TODO: introduce yet another compatibility option? */
+ sc::FormulaResultValue aRes = rCell.getFormula()->GetResult();
+ if (aRes.meType != sc::FormulaResultValue::String)
+ return false;
+ if (!aRes.maString.isEmpty())
+ return false;
+ }
+ break;
+ default:
+ ;
+ }
+
+ return true;
+}
+
+}
+
+void ScInterpreter::ScCountEmptyCells()
+{
+ if ( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
+ // There's either one RefList and nothing else, or none.
+ ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
+ sal_uLong nMaxCount = 0, nCount = 0;
+ switch (GetStackType())
+ {
+ case svSingleRef :
+ {
+ nMaxCount = 1;
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (!isCellContentEmpty(aCell))
+ nCount = 1;
+ }
+ break;
+ case svRefList :
+ case svDoubleRef :
+ {
+ ScRange aRange;
+ short nParam = 1;
+ SCSIZE nRefListArrayPos = 0;
+ size_t nRefInList = 0;
+ while (nParam-- > 0)
+ {
+ nRefListArrayPos = nRefInList;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ nMaxCount +=
+ static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
+ static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
+ static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
+
+ ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ const ScRefCellValue& rCell = aIter.getRefCellValue();
+ if (!isCellContentEmpty(rCell))
+ ++nCount;
+ }
+ if (xResMat)
+ {
+ xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
+ nMaxCount = nCount = 0;
+ }
+ }
+ }
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef xMat = GetMatrix();
+ if (!xMat)
+ SetError( FormulaError::IllegalParameter);
+ else
+ {
+ SCSIZE nC, nR;
+ xMat->GetDimensions( nC, nR);
+ nMaxCount = nC * nR;
+ // Numbers (implicit), strings and error values, ignore empty
+ // strings as those if not entered in an inline array are the
+ // result of a formula, to be par with a reference to formula
+ // cell as *visual* blank, see isCellContentEmpty() above.
+ nCount = xMat->Count( true, true, true);
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ if (xResMat)
+ PushMatrix( xResMat);
+ else
+ PushDouble(nMaxCount - nCount);
+}
+
+void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ SCCOL nCol3 = 0;
+ SCROW nRow3 = 0;
+ SCTAB nTab3 = 0;
+
+ ScMatrixRef pSumExtraMatrix;
+ bool bSumExtraRange = (nParamCount == 3);
+ if (bSumExtraRange)
+ {
+ // Save only the upperleft cell in case of cell range. The geometry
+ // of the 3rd parameter is taken from the 1st parameter.
+
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ {
+ SCCOL nColJunk = 0;
+ SCROW nRowJunk = 0;
+ SCTAB nTabJunk = 0;
+ PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
+ if ( nTabJunk != nTab3 )
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ }
+ break;
+ case svSingleRef :
+ PopSingleRef( nCol3, nRow3, nTab3 );
+ break;
+ case svMatrix:
+ pSumExtraMatrix = PopMatrix();
+ // nCol3, nRow3, nTab3 remain 0
+ break;
+ case svExternalSingleRef:
+ {
+ pSumExtraMatrix = GetNewMat(1,1);
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ if (pToken->GetType() == svDouble)
+ pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
+ else
+ pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
+ }
+ break;
+ case svExternalDoubleRef:
+ PopExternalDoubleRef(pSumExtraMatrix);
+ break;
+ default:
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ }
+
+ svl::SharedString aString;
+ double fVal = 0.0;
+ bool bIsString = true;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ fVal = GetCellValue(aAdr, aCell);
+ bIsString = false;
+ break;
+ case CELLTYPE_FORMULA :
+ if (aCell.getFormula()->IsValue())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ bIsString = false;
+ }
+ else
+ GetCellString(aString, aCell);
+ break;
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ GetCellString(aString, aCell);
+ break;
+ default:
+ fVal = 0.0;
+ bIsString = false;
+ }
+ }
+ break;
+ case svString:
+ aString = GetString();
+ break;
+ case svMatrix :
+ case svExternalDoubleRef:
+ {
+ ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
+ bIsString = ScMatrix::IsRealStringType( nType);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ if (pToken->GetType() == svDouble)
+ {
+ fVal = pToken->GetDouble();
+ bIsString = false;
+ }
+ else
+ aString = pToken->GetString();
+ }
+ }
+ break;
+ default:
+ {
+ fVal = GetDouble();
+ bIsString = false;
+ }
+ }
+
+ KahanSum fSum = 0.0;
+ double fRes = 0.0;
+ double fCount = 0.0;
+ short nParam = 1;
+ const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
+ // There's either one RefList and nothing else, or none.
+ ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
+ SCSIZE nRefListArrayPos = 0;
+ size_t nRefInList = 0;
+ while (nParam-- > 0)
+ {
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ SCTAB nTab2 = 0;
+ ScMatrixRef pQueryMatrix;
+ switch ( GetStackType() )
+ {
+ case svRefList :
+ if (bSumExtraRange)
+ {
+ /* TODO: this could resolve if all refs are of the same size */
+ SetError( FormulaError::IllegalParameter);
+ }
+ else
+ {
+ nRefListArrayPos = nRefInList;
+ ScRange aRange;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ }
+ break;
+ case svDoubleRef :
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ break;
+ case svSingleRef :
+ PopSingleRef( nCol1, nRow1, nTab1 );
+ nCol2 = nCol1;
+ nRow2 = nRow1;
+ nTab2 = nTab1;
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pQueryMatrix = GetMatrix();
+ if (!pQueryMatrix)
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ nCol1 = 0;
+ nRow1 = 0;
+ nTab1 = 0;
+ SCSIZE nC, nR;
+ pQueryMatrix->GetDimensions( nC, nR);
+ nCol2 = static_cast<SCCOL>(nC - 1);
+ nRow2 = static_cast<SCROW>(nR - 1);
+ nTab2 = 0;
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ if ( nTab1 != nTab2 )
+ {
+ SetError( FormulaError::IllegalParameter);
+ }
+
+ if (bSumExtraRange)
+ {
+ // Take the range geometry of the 1st parameter and apply it to
+ // the 3rd. If parts of the resulting range would point outside
+ // the sheet, don't complain but silently ignore and simply cut
+ // them away, this is what Xcl does :-/
+
+ // For the cut-away part we also don't need to determine the
+ // criteria match, so shrink the source range accordingly,
+ // instead of the result range.
+ SCCOL nColDelta = nCol2 - nCol1;
+ SCROW nRowDelta = nRow2 - nRow1;
+ SCCOL nMaxCol;
+ SCROW nMaxRow;
+ if (pSumExtraMatrix)
+ {
+ SCSIZE nC, nR;
+ pSumExtraMatrix->GetDimensions( nC, nR);
+ nMaxCol = static_cast<SCCOL>(nC - 1);
+ nMaxRow = static_cast<SCROW>(nR - 1);
+ }
+ else
+ {
+ nMaxCol = mrDoc.MaxCol();
+ nMaxRow = mrDoc.MaxRow();
+ }
+ if (nCol3 + nColDelta > nMaxCol)
+ {
+ SCCOL nNewDelta = nMaxCol - nCol3;
+ nCol2 = nCol1 + nNewDelta;
+ }
+
+ if (nRow3 + nRowDelta > nMaxRow)
+ {
+ SCROW nNewDelta = nMaxRow - nRow3;
+ nRow2 = nRow1 + nNewDelta;
+ }
+ }
+ else
+ {
+ nCol3 = nCol1;
+ nRow3 = nRow1;
+ nTab3 = nTab1;
+ }
+
+ if (nGlobalError == FormulaError::NONE)
+ {
+ ScQueryParam rParam;
+ rParam.nRow1 = nRow1;
+ rParam.nRow2 = nRow2;
+
+ ScQueryEntry& rEntry = rParam.GetEntry(0);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ rEntry.bDoQuery = true;
+ if (!bIsString)
+ {
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = fVal;
+ rEntry.eOp = SC_EQUAL;
+ }
+ else
+ {
+ rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
+ if (rItem.meType == ScQueryEntry::ByString)
+ rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
+ }
+ ScAddress aAdr;
+ aAdr.SetTab( nTab3 );
+ rParam.nCol1 = nCol1;
+ rParam.nCol2 = nCol2;
+ rEntry.nField = nCol1;
+ SCCOL nColDiff = nCol3 - nCol1;
+ SCROW nRowDiff = nRow3 - nRow1;
+ if (pQueryMatrix)
+ {
+ // Never case-sensitive.
+ sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
+ ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
+ if (nGlobalError != FormulaError::NONE || !pResultMatrix)
+ {
+ SetError( FormulaError::IllegalParameter);
+ }
+
+ if (pSumExtraMatrix)
+ {
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ if (pResultMatrix->IsValue( nCol, nRow) &&
+ pResultMatrix->GetDouble( nCol, nRow))
+ {
+ SCSIZE nC = nCol + nColDiff;
+ SCSIZE nR = nRow + nRowDiff;
+ if (pSumExtraMatrix->IsValue( nC, nR))
+ {
+ fVal = pSumExtraMatrix->GetDouble( nC, nR);
+ ++fCount;
+ fSum += fVal;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ if (pResultMatrix->GetDouble( nCol, nRow))
+ {
+ aAdr.SetCol( nCol + nColDiff);
+ aAdr.SetRow( nRow + nRowDiff);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ ++fCount;
+ fSum += fVal;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
+ // Increment Entry.nField in iterator when switching to next column.
+ aCellIter.SetAdvanceQueryParamEntryField( true );
+ if ( aCellIter.GetFirst() )
+ {
+ if (pSumExtraMatrix)
+ {
+ do
+ {
+ SCSIZE nC = aCellIter.GetCol() + nColDiff;
+ SCSIZE nR = aCellIter.GetRow() + nRowDiff;
+ if (pSumExtraMatrix->IsValue( nC, nR))
+ {
+ fVal = pSumExtraMatrix->GetDouble( nC, nR);
+ ++fCount;
+ fSum += fVal;
+ }
+ } while ( aCellIter.GetNext() );
+ }
+ else
+ {
+ do
+ {
+ aAdr.SetCol( aCellIter.GetCol() + nColDiff);
+ aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ ++fCount;
+ fSum += fVal;
+ }
+ } while ( aCellIter.GetNext() );
+ }
+ }
+ }
+ }
+ else
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ switch( eFunc )
+ {
+ case ifSUMIF: fRes = fSum.get(); break;
+ case ifAVERAGEIF: fRes = div( fSum.get(), fCount ); break;
+ }
+ if (xResMat)
+ {
+ if (nGlobalError == FormulaError::NONE)
+ xResMat->PutDouble( fRes, 0, nRefListArrayPos);
+ else
+ {
+ xResMat->PutError( nGlobalError, 0, nRefListArrayPos);
+ nGlobalError = FormulaError::NONE;
+ }
+ fRes = fCount = 0.0;
+ fSum = 0;
+ }
+ }
+ if (xResMat)
+ PushMatrix( xResMat);
+ else
+ PushDouble( fRes);
+}
+
+void ScInterpreter::ScSumIf()
+{
+ IterateParametersIf( ifSUMIF);
+}
+
+void ScInterpreter::ScAverageIf()
+{
+ IterateParametersIf( ifAVERAGEIF);
+}
+
+void ScInterpreter::ScCountIf()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ svl::SharedString aString;
+ double fVal = 0.0;
+ bool bIsString = true;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushInt(0);
+ return ;
+ }
+ ScRefCellValue aCell(mrDoc, aAdr);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ fVal = GetCellValue(aAdr, aCell);
+ bIsString = false;
+ break;
+ case CELLTYPE_FORMULA :
+ if (aCell.getFormula()->IsValue())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ bIsString = false;
+ }
+ else
+ GetCellString(aString, aCell);
+ break;
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ GetCellString(aString, aCell);
+ break;
+ default:
+ fVal = 0.0;
+ bIsString = false;
+ }
+ }
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
+ bIsString = ScMatrix::IsRealStringType( nType);
+ }
+ break;
+ case svString:
+ aString = GetString();
+ break;
+ default:
+ {
+ fVal = GetDouble();
+ bIsString = false;
+ }
+ }
+ double fCount = 0.0;
+ short nParam = 1;
+ const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
+ // There's either one RefList and nothing else, or none.
+ ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
+ SCSIZE nRefListArrayPos = 0;
+ size_t nRefInList = 0;
+ while (nParam-- > 0)
+ {
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ SCTAB nTab2 = 0;
+ ScMatrixRef pQueryMatrix;
+ const ScComplexRefData* refData = nullptr;
+ switch ( GetStackType() )
+ {
+ case svRefList :
+ nRefListArrayPos = nRefInList;
+ [[fallthrough]];
+ case svDoubleRef :
+ {
+ refData = GetStackDoubleRef(nRefInList);
+ ScRange aRange;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ }
+ break;
+ case svSingleRef :
+ PopSingleRef( nCol1, nRow1, nTab1 );
+ nCol2 = nCol1;
+ nRow2 = nRow1;
+ nTab2 = nTab1;
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pQueryMatrix = GetMatrix();
+ if (!pQueryMatrix)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ nCol1 = 0;
+ nRow1 = 0;
+ nTab1 = 0;
+ SCSIZE nC, nR;
+ pQueryMatrix->GetDimensions( nC, nR);
+ nCol2 = static_cast<SCCOL>(nC - 1);
+ nRow2 = static_cast<SCROW>(nR - 1);
+ nTab2 = 0;
+ }
+ break;
+ default:
+ PopError(); // Propagate it further
+ PushIllegalParameter();
+ return ;
+ }
+ if ( nTab1 != nTab2 )
+ {
+ PushIllegalParameter();
+ return;
+ }
+ if (nCol1 > nCol2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ if (nGlobalError == FormulaError::NONE)
+ {
+ ScQueryParam rParam;
+ rParam.nRow1 = nRow1;
+ rParam.nRow2 = nRow2;
+ rParam.nTab = nTab1;
+
+ ScQueryEntry& rEntry = rParam.GetEntry(0);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ rEntry.bDoQuery = true;
+ if (!bIsString)
+ {
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = fVal;
+ rEntry.eOp = SC_EQUAL;
+ }
+ else
+ {
+ rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
+ if (rItem.meType == ScQueryEntry::ByString)
+ rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
+ }
+ rParam.nCol1 = nCol1;
+ rParam.nCol2 = nCol2;
+ rEntry.nField = nCol1;
+ if (pQueryMatrix)
+ {
+ // Never case-sensitive.
+ sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
+ ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
+ if (nGlobalError != FormulaError::NONE || !pResultMatrix)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ SCSIZE nSize = pResultMatrix->GetElementCount();
+ for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
+ {
+ if (pResultMatrix->IsValue( nIndex) &&
+ pResultMatrix->GetDouble( nIndex))
+ ++fCount;
+ }
+ }
+ else
+ {
+ if(ScCountIfCellIteratorSortedCache::CanBeUsed(mrDoc, rParam, nTab1, pMyFormulaCell,
+ refData, mrContext))
+ {
+ ScCountIfCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false);
+ fCount += aCellIter.GetCount();
+ }
+ else
+ {
+ ScCountIfCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
+ fCount += aCellIter.GetCount();
+ }
+ }
+ }
+ else
+ {
+ PushIllegalParameter();
+ return;
+ }
+ if (xResMat)
+ {
+ xResMat->PutDouble( fCount, 0, nRefListArrayPos);
+ fCount = 0.0;
+ }
+ }
+ if (xResMat)
+ PushMatrix( xResMat);
+ else
+ PushDouble(fCount);
+}
+
+void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) )
+{
+ sal_uInt8 nParamCount = GetByte();
+ sal_uInt8 nQueryCount = nParamCount / 2;
+
+ std::vector<sal_uInt8>& vConditions = mrContext.maConditions;
+ // vConditions is cached, although it is clear'ed after every cell is interpreted,
+ // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
+ // with a single InterpretTail() call it results in evaluation of all the cells in the
+ // matrix formula.
+ vConditions.clear();
+
+ // Range-reduce optimization
+ SCCOL nStartColDiff = 0;
+ SCCOL nEndColDiff = 0;
+ SCROW nStartRowDiff = 0;
+ SCROW nEndRowDiff = 0;
+ bool bRangeReduce = false;
+ ScRange aMainRange;
+
+ bool bHasDoubleRefCriteriaRanges = true;
+ // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
+ // For COUNTIFS queries it's possible to range-reduce too, if the query is not supposed
+ // to match empty cells (will be checked and undone later if needed), so simply treat
+ // the first criteria range as the main range for purposes of detecting if this can be done.
+ for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
+ {
+ const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
+ if (pCriteriaRangeToken->GetType() != svDoubleRef )
+ {
+ bHasDoubleRefCriteriaRanges = false;
+ break;
+ }
+ }
+
+ // Probe the main range token, and try if we can shrink the range without altering results.
+ const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
+ if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
+ {
+ const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
+ if (!pRefData->IsDeleted())
+ {
+ DoubleRefToRange( *pRefData, aMainRange);
+ if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
+ {
+ // Shrink the range to actual data content.
+ ScRange aSubRange = aMainRange;
+ mrDoc.GetDataAreaSubrange(aSubRange);
+ nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
+ nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
+ nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
+ nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
+ bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
+ }
+ }
+ }
+
+ double fVal = 0.0;
+ SCCOL nDimensionCols = 0;
+ SCROW nDimensionRows = 0;
+ const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
+ std::vector<std::vector<sal_uInt8>> vRefArrayConditions;
+
+ while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
+ {
+ // take criteria
+ svl::SharedString aString;
+ fVal = 0.0;
+ bool bIsString = true;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ fVal = GetCellValue(aAdr, aCell);
+ bIsString = false;
+ break;
+ case CELLTYPE_FORMULA :
+ if (aCell.getFormula()->IsValue())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ bIsString = false;
+ }
+ else
+ GetCellString(aString, aCell);
+ break;
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ GetCellString(aString, aCell);
+ break;
+ default:
+ fVal = 0.0;
+ bIsString = false;
+ }
+ }
+ break;
+ case svString:
+ aString = GetString();
+ break;
+ case svMatrix :
+ case svExternalDoubleRef:
+ {
+ ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
+ bIsString = ScMatrix::IsRealStringType( nType);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ if (pToken->GetType() == svDouble)
+ {
+ fVal = pToken->GetDouble();
+ bIsString = false;
+ }
+ else
+ aString = pToken->GetString();
+ }
+ }
+ break;
+ default:
+ {
+ fVal = GetDouble();
+ bIsString = false;
+ }
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return; // and bail out, no need to evaluate other arguments
+ }
+
+ // take range
+ short nParam = nParamCount;
+ size_t nRefInList = 0;
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ SCTAB nTab2 = 0;
+ ScMatrixRef pQueryMatrix;
+ while (nParam-- == nParamCount)
+ {
+ const ScComplexRefData* refData = nullptr;
+ switch ( GetStackType() )
+ {
+ case svRefList :
+ {
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (p && p->IsArrayResult())
+ {
+ if (nRefInList == 0)
+ {
+ if (vRefArrayConditions.empty())
+ vRefArrayConditions.resize( nRefArrayRows);
+ if (!vConditions.empty())
+ {
+ // Similar to other reference list array
+ // handling, add/op the current value to
+ // all array positions.
+ for (auto & rVec : vRefArrayConditions)
+ {
+ if (rVec.empty())
+ rVec = vConditions;
+ else
+ {
+ assert(rVec.size() == vConditions.size()); // see dimensions below
+ for (size_t i=0, n = rVec.size(); i < n; ++i)
+ {
+ rVec[i] += vConditions[i];
+ }
+ }
+ }
+ // Reset condition results.
+ std::for_each( vConditions.begin(), vConditions.end(),
+ [](sal_uInt8 & r){ r = 0.0; } );
+ }
+ }
+ nRefArrayPos = nRefInList;
+ }
+ refData = GetStackDoubleRef(nRefInList);
+ ScRange aRange;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ }
+ break;
+ case svDoubleRef :
+ refData = GetStackDoubleRef();
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ break;
+ case svSingleRef :
+ PopSingleRef( nCol1, nRow1, nTab1 );
+ nCol2 = nCol1;
+ nRow2 = nRow1;
+ nTab2 = nTab1;
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pQueryMatrix = GetMatrix();
+ if (!pQueryMatrix)
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ nCol1 = 0;
+ nRow1 = 0;
+ nTab1 = 0;
+ SCSIZE nC, nR;
+ pQueryMatrix->GetDimensions( nC, nR);
+ nCol2 = static_cast<SCCOL>(nC - 1);
+ nRow2 = static_cast<SCROW>(nR - 1);
+ nTab2 = 0;
+ }
+ break;
+ default:
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ if ( nTab1 != nTab2 )
+ {
+ PushError( FormulaError::IllegalArgument);
+ return;
+ }
+
+ ScQueryParam rParam;
+ ScQueryEntry& rEntry = rParam.GetEntry(0);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ rEntry.bDoQuery = true;
+ if (!bIsString)
+ {
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = fVal;
+ rEntry.eOp = SC_EQUAL;
+ }
+ else
+ {
+ rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
+ if (rItem.meType == ScQueryEntry::ByString)
+ rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
+ }
+
+ // Undo bRangeReduce if asked to match empty cells for COUNTIFS (which should be rare).
+ assert(rEntry.GetQueryItems().size() == 1);
+ const bool isCountIfs = (nParamCount % 2) == 0;
+ if(isCountIfs && (rEntry.IsQueryByEmpty() || rItem.mbMatchEmpty) && bRangeReduce)
+ {
+ bRangeReduce = false;
+ // All criteria ranges are svDoubleRef's, so only vConditions needs adjusting.
+ assert(vRefArrayConditions.empty());
+ if(!vConditions.empty())
+ {
+ std::vector<sal_uInt8> newConditions;
+ SCCOL newDimensionCols = nCol2 - nCol1 + 1;
+ SCROW newDimensionRows = nRow2 - nRow1 + 1;
+ newConditions.reserve( newDimensionCols * newDimensionRows );
+ SCCOL col = nCol1;
+ for(; col < nCol1 + nStartColDiff; ++col)
+ newConditions.insert( newConditions.end(), newDimensionRows, 0 );
+ for(; col <= nCol2 - nStartColDiff; ++col)
+ {
+ newConditions.insert( newConditions.end(), nStartRowDiff, 0 );
+ SCCOL oldCol = col - ( nCol1 + nStartColDiff );
+ size_t nIndex = oldCol * nDimensionRows;
+ if (nIndex < vConditions.size())
+ {
+ auto it = vConditions.begin() + nIndex;
+ newConditions.insert( newConditions.end(), it, it + nDimensionRows );
+ }
+ else
+ newConditions.insert( newConditions.end(), nDimensionRows, 0 );
+ newConditions.insert( newConditions.end(), -nEndRowDiff, 0 );
+ }
+ for(; col <= nCol2; ++col)
+ newConditions.insert( newConditions.end(), newDimensionRows, 0 );
+ assert( newConditions.size() == o3tl::make_unsigned( newDimensionCols * newDimensionRows ));
+ vConditions = std::move( newConditions );
+ nDimensionCols = newDimensionCols;
+ nDimensionRows = newDimensionRows;
+ }
+ }
+
+ if (bRangeReduce)
+ {
+ // All reference ranges must be of the same size as the main range.
+ if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1
+ || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1)
+ {
+ PushError ( FormulaError::IllegalArgument);
+ return;
+ }
+ nCol1 += nStartColDiff;
+ nRow1 += nStartRowDiff;
+
+ nCol2 += nEndColDiff;
+ nRow2 += nEndRowDiff;
+ }
+
+ // All reference ranges must be of same dimension and size.
+ if (!nDimensionCols)
+ nDimensionCols = nCol2 - nCol1 + 1;
+ if (!nDimensionRows)
+ nDimensionRows = nRow2 - nRow1 + 1;
+ if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
+ {
+ PushError ( FormulaError::IllegalArgument);
+ return;
+ }
+
+ // recalculate matrix values
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ // initialize temporary result matrix
+ if (vConditions.empty())
+ vConditions.resize( nDimensionCols * nDimensionRows, 0);
+
+ rParam.nRow1 = nRow1;
+ rParam.nRow2 = nRow2;
+ rParam.nCol1 = nCol1;
+ rParam.nCol2 = nCol2;
+ rEntry.nField = nCol1;
+ SCCOL nColDiff = -nCol1;
+ SCROW nRowDiff = -nRow1;
+ if (pQueryMatrix)
+ {
+ // Never case-sensitive.
+ sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType);
+ ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
+ if (nGlobalError != FormulaError::NONE || !pResultMatrix)
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ // result matrix is filled with boolean values.
+ std::vector<double> aResValues;
+ pResultMatrix->GetDoubleArray(aResValues);
+ if (vConditions.size() != aResValues.size())
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ std::vector<double>::const_iterator itThisRes = aResValues.begin();
+ for (auto& rCondition : vConditions)
+ {
+ rCondition += *itThisRes;
+ ++itThisRes;
+ }
+ }
+ else
+ {
+ if( ScQueryCellIteratorSortedCache::CanBeUsed( mrDoc, rParam, nTab1, pMyFormulaCell,
+ refData, mrContext ))
+ {
+ ScQueryCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false);
+ // Increment Entry.nField in iterator when switching to next column.
+ aCellIter.SetAdvanceQueryParamEntryField( true );
+ if ( aCellIter.GetFirst() )
+ {
+ do
+ {
+ size_t nC = aCellIter.GetCol() + nColDiff;
+ size_t nR = aCellIter.GetRow() + nRowDiff;
+ ++vConditions[nC * nDimensionRows + nR];
+ } while ( aCellIter.GetNext() );
+ }
+ }
+ else
+ {
+ ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false);
+ // Increment Entry.nField in iterator when switching to next column.
+ aCellIter.SetAdvanceQueryParamEntryField( true );
+ if ( aCellIter.GetFirst() )
+ {
+ do
+ {
+ size_t nC = aCellIter.GetCol() + nColDiff;
+ size_t nR = aCellIter.GetRow() + nRowDiff;
+ ++vConditions[nC * nDimensionRows + nR];
+ } while ( aCellIter.GetNext() );
+ }
+ }
+ }
+ if (nRefArrayPos != std::numeric_limits<size_t>::max())
+ {
+ // Apply condition result to reference list array result position.
+ std::vector<sal_uInt8>& rVec = vRefArrayConditions[nRefArrayPos];
+ if (rVec.empty())
+ rVec = vConditions;
+ else
+ {
+ assert(rVec.size() == vConditions.size()); // see dimensions above
+ for (size_t i=0, n = rVec.size(); i < n; ++i)
+ {
+ rVec[i] += vConditions[i];
+ }
+ }
+ // Reset conditions vector.
+ // When leaving an svRefList this has to be emptied not set to
+ // 0.0 because it's checked when entering an svRefList.
+ if (nRefInList == 0)
+ std::vector<sal_uInt8>().swap( vConditions);
+ else
+ std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt8 & r){ r = 0; } );
+ }
+ }
+ nParamCount -= 2;
+ }
+
+ if (!vRefArrayConditions.empty() && !vConditions.empty())
+ {
+ // Add/op the last current value to all array positions.
+ for (auto & rVec : vRefArrayConditions)
+ {
+ if (rVec.empty())
+ rVec = vConditions;
+ else
+ {
+ assert(rVec.size() == vConditions.size()); // see dimensions above
+ for (size_t i=0, n = rVec.size(); i < n; ++i)
+ {
+ rVec[i] += vConditions[i];
+ }
+ }
+ }
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return; // bail out
+ }
+
+ sc::ParamIfsResult aRes;
+ ScMatrixRef xResMat;
+
+ // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
+ if (nParamCount == 1)
+ {
+ short nParam = nParamCount;
+ size_t nRefInList = 0;
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
+ bool bRefArrayMain = false;
+ while (nParam-- == nParamCount)
+ {
+ SCCOL nMainCol1 = 0;
+ SCROW nMainRow1 = 0;
+ SCTAB nMainTab1 = 0;
+ SCCOL nMainCol2 = 0;
+ SCROW nMainRow2 = 0;
+ SCTAB nMainTab2 = 0;
+ ScMatrixRef pMainMatrix;
+ switch ( GetStackType() )
+ {
+ case svRefList :
+ {
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (p && p->IsArrayResult())
+ {
+ if (vRefArrayConditions.empty())
+ {
+ // Replicate conditions if there wasn't a
+ // reference list array for criteria
+ // evaluation.
+ vRefArrayConditions.resize( nRefArrayRows);
+ for (auto & rVec : vRefArrayConditions)
+ {
+ rVec = vConditions;
+ }
+ }
+
+ bRefArrayMain = true;
+ nRefArrayPos = nRefInList;
+ }
+ ScRange aRange;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
+ }
+ break;
+ case svDoubleRef :
+ PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
+ break;
+ case svSingleRef :
+ PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
+ nMainCol2 = nMainCol1;
+ nMainRow2 = nMainRow1;
+ nMainTab2 = nMainTab1;
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pMainMatrix = GetMatrix();
+ if (!pMainMatrix)
+ {
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ nMainCol1 = 0;
+ nMainRow1 = 0;
+ nMainTab1 = 0;
+ SCSIZE nC, nR;
+ pMainMatrix->GetDimensions( nC, nR);
+ nMainCol2 = static_cast<SCCOL>(nC - 1);
+ nMainRow2 = static_cast<SCROW>(nR - 1);
+ nMainTab2 = 0;
+ }
+ break;
+ // Treat a scalar value as 1x1 matrix.
+ case svDouble:
+ pMainMatrix = GetNewMat(1,1);
+ nMainCol1 = nMainCol2 = 0;
+ nMainRow1 = nMainRow2 = 0;
+ nMainTab1 = nMainTab2 = 0;
+ pMainMatrix->PutDouble( GetDouble(), 0, 0);
+ break;
+ case svString:
+ pMainMatrix = GetNewMat(1,1);
+ nMainCol1 = nMainCol2 = 0;
+ nMainRow1 = nMainRow2 = 0;
+ nMainTab1 = nMainTab2 = 0;
+ pMainMatrix->PutString( GetString(), 0, 0);
+ break;
+ default:
+ PopError();
+ PushError( FormulaError::IllegalParameter);
+ return;
+ }
+ if ( nMainTab1 != nMainTab2 )
+ {
+ PushError( FormulaError::IllegalArgument);
+ return;
+ }
+
+ if (bRangeReduce)
+ {
+ nMainCol1 += nStartColDiff;
+ nMainRow1 += nStartRowDiff;
+
+ nMainCol2 += nEndColDiff;
+ nMainRow2 += nEndRowDiff;
+ }
+
+ // All reference ranges must be of same dimension and size.
+ if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
+ {
+ PushError ( FormulaError::IllegalArgument);
+ return;
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return; // bail out
+ }
+
+ // end-result calculation
+
+ // This gets weird... if conditions were calculated using a
+ // reference list array but the main calculation range is not a
+ // reference list array, then the conditions of the array are
+ // applied to the main range each in turn to form the array result.
+
+ size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos :
+ (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0));
+ const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0);
+
+ if (nRefArrayMainPos == 0)
+ xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
+
+ if (pMainMatrix)
+ {
+ std::vector<double> aMainValues;
+ pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
+
+ do
+ {
+ if (nRefArrayMainPos < vRefArrayConditions.size())
+ vConditions = vRefArrayConditions[nRefArrayMainPos];
+
+ if (vConditions.size() != aMainValues.size())
+ {
+ PushError( FormulaError::IllegalArgument);
+ return;
+ }
+
+ std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
+ std::vector<double>::const_iterator itMain = aMainValues.begin();
+ for (; itRes != itResEnd; ++itRes, ++itMain)
+ {
+ if (*itRes != nQueryCount)
+ continue;
+
+ fVal = *itMain;
+ if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
+ continue;
+
+ ++aRes.mfCount;
+ aRes.mfSum += fVal;
+ if ( aRes.mfMin > fVal )
+ aRes.mfMin = fVal;
+ if ( aRes.mfMax < fVal )
+ aRes.mfMax = fVal;
+ }
+ if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
+ {
+ xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
+ aRes = sc::ParamIfsResult();
+ }
+ }
+ while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
+ }
+ else
+ {
+ ScAddress aAdr;
+ aAdr.SetTab( nMainTab1 );
+ do
+ {
+ if (nRefArrayMainPos < vRefArrayConditions.size())
+ vConditions = vRefArrayConditions[nRefArrayMainPos];
+
+ SAL_WARN_IF(nDimensionCols && nDimensionRows && vConditions.empty(), "sc", "ScInterpreter::IterateParametersIfs vConditions is empty");
+ if (!vConditions.empty())
+ {
+ std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin();
+ for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
+ {
+ for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
+ {
+ if (*itRes == nQueryCount)
+ {
+ aAdr.SetCol( nCol + nMainCol1);
+ aAdr.SetRow( nRow + nMainRow1);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ ++aRes.mfCount;
+ aRes.mfSum += fVal;
+ if ( aRes.mfMin > fVal )
+ aRes.mfMin = fVal;
+ if ( aRes.mfMax < fVal )
+ aRes.mfMax = fVal;
+ }
+ }
+ }
+ }
+ }
+ if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
+ {
+ xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
+ aRes = sc::ParamIfsResult();
+ }
+ }
+ while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
+ }
+ }
+ }
+ else
+ {
+ // COUNTIFS only.
+ if (vRefArrayConditions.empty())
+ {
+ // The code below is this but optimized for most elements not matching.
+ // for (auto const & rCond : vConditions)
+ // if (rCond == nQueryCount)
+ // ++aRes.mfCount;
+ static_assert(sizeof(vConditions[0]) == 1);
+ const sal_uInt8* pos = vConditions.data();
+ const sal_uInt8* end = pos + vConditions.size();
+ for(;;)
+ {
+ pos = static_cast< const sal_uInt8* >( memchr( pos, nQueryCount, end - pos ));
+ if( pos == nullptr )
+ break;
+ ++aRes.mfCount;
+ ++pos;
+ }
+ }
+ else
+ {
+ xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
+ for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i)
+ {
+ double fCount = 0.0;
+ for (auto const & rCond : vRefArrayConditions[i])
+ {
+ if (rCond == nQueryCount)
+ ++fCount;
+ }
+ xResMat->PutDouble( fCount, 0, i);
+ }
+ }
+ }
+
+ if (xResMat)
+ PushMatrix( xResMat);
+ else
+ PushDouble( ResultFunc( aRes));
+}
+
+void ScInterpreter::ScSumIfs()
+{
+ // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE);
+ sal_uInt8 nParamCount = GetByte();
+
+ if (nParamCount < 3 || (nParamCount % 2 != 1))
+ {
+ PushError( FormulaError::ParameterExpected);
+ return;
+ }
+
+ auto ResultFunc = []( const sc::ParamIfsResult& rRes )
+ {
+ return rRes.mfSum.get();
+ };
+ IterateParametersIfs(ResultFunc);
+}
+
+void ScInterpreter::ScAverageIfs()
+{
+ sal_uInt8 nParamCount = GetByte();
+
+ if (nParamCount < 3 || (nParamCount % 2 != 1))
+ {
+ PushError( FormulaError::ParameterExpected);
+ return;
+ }
+
+ auto ResultFunc = []( const sc::ParamIfsResult& rRes )
+ {
+ return sc::div( rRes.mfSum.get(), rRes.mfCount);
+ };
+ IterateParametersIfs(ResultFunc);
+}
+
+void ScInterpreter::ScCountIfs()
+{
+ sal_uInt8 nParamCount = GetByte();
+
+ if (nParamCount < 2 || (nParamCount % 2 != 0))
+ {
+ PushError( FormulaError::ParameterExpected);
+ return;
+ }
+
+ auto ResultFunc = []( const sc::ParamIfsResult& rRes )
+ {
+ return rRes.mfCount;
+ };
+ IterateParametersIfs(ResultFunc);
+}
+
+void ScInterpreter::ScMinIfs_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+
+ if (nParamCount < 3 || (nParamCount % 2 != 1))
+ {
+ PushError( FormulaError::ParameterExpected);
+ return;
+ }
+
+ auto ResultFunc = []( const sc::ParamIfsResult& rRes )
+ {
+ return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0;
+ };
+ IterateParametersIfs(ResultFunc);
+}
+
+
+void ScInterpreter::ScMaxIfs_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+
+ if (nParamCount < 3 || (nParamCount % 2 != 1))
+ {
+ PushError( FormulaError::ParameterExpected);
+ return;
+ }
+
+ auto ResultFunc = []( const sc::ParamIfsResult& rRes )
+ {
+ return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0;
+ };
+ IterateParametersIfs(ResultFunc);
+}
+
+void ScInterpreter::ScLookup()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return ;
+
+ ScMatrixRef pDataMat = nullptr, pResMat = nullptr;
+ SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
+ SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
+ SCTAB nTab1 = 0, nResTab = 0;
+ SCSIZE nLenMajor = 0; // length of major direction
+ bool bVertical = true; // whether to lookup vertically or horizontally
+
+ // The third parameter, result array, double, string and reference.
+ double fResVal = 0.0;
+ svl::SharedString aResStr;
+ StackVar eResArrayType = svUnknown;
+
+ if (nParamCount == 3)
+ {
+ eResArrayType = GetStackType();
+ switch (eResArrayType)
+ {
+ case svDoubleRef:
+ {
+ SCTAB nTabJunk;
+ PopDoubleRef(nResCol1, nResRow1, nResTab,
+ nResCol2, nResRow2, nTabJunk);
+ if (nResTab != nTabJunk ||
+ ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
+ {
+ // The result array must be a vector.
+ PushIllegalParameter();
+ return;
+ }
+ }
+ break;
+ case svSingleRef:
+ PopSingleRef( nResCol1, nResRow1, nResTab);
+ nResCol2 = nResCol1;
+ nResRow2 = nResRow1;
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pResMat = GetMatrix();
+ if (!pResMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC, nR;
+ pResMat->GetDimensions(nC, nR);
+ if (nC != 1 && nR != 1)
+ {
+ // Result matrix must be a vector.
+ PushIllegalParameter();
+ return;
+ }
+ }
+ break;
+ case svDouble:
+ fResVal = GetDouble();
+ break;
+ case svString:
+ aResStr = GetString();
+ break;
+ default:
+ PushIllegalParameter();
+ return;
+ }
+ }
+
+ // For double, string and single reference.
+ double fDataVal = 0.0;
+ svl::SharedString aDataStr;
+ ScAddress aDataAdr;
+ bool bValueData = false;
+
+ // Get the data-result range and also determine whether this is vertical
+ // lookup or horizontal lookup.
+
+ StackVar eDataArrayType = GetStackType();
+ switch (eDataArrayType)
+ {
+ case svDoubleRef:
+ {
+ SCTAB nTabJunk;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
+ if (nTab1 != nTabJunk)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
+ nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
+ }
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pDataMat = GetMatrix();
+ if (!pDataMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ SCSIZE nC, nR;
+ pDataMat->GetDimensions(nC, nR);
+ bVertical = (nR >= nC);
+ nLenMajor = bVertical ? nR : nC;
+ }
+ break;
+ case svDouble:
+ {
+ fDataVal = GetDouble();
+ bValueData = true;
+ }
+ break;
+ case svString:
+ {
+ aDataStr = GetString();
+ }
+ break;
+ case svSingleRef:
+ {
+ PopSingleRef( aDataAdr );
+ ScRefCellValue aCell(mrDoc, aDataAdr);
+ if (aCell.hasEmptyValue())
+ {
+ // Empty cells aren't found anywhere, bail out early.
+ SetError( FormulaError::NotAvailable);
+ }
+ else if (aCell.hasNumeric())
+ {
+ fDataVal = GetCellValue(aDataAdr, aCell);
+ bValueData = true;
+ }
+ else
+ GetCellString(aDataStr, aCell);
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ // Get the lookup value.
+
+ ScQueryParam aParam;
+ ScQueryEntry& rEntry = aParam.GetEntry(0);
+ if ( !FillEntry(rEntry) )
+ return;
+
+ if ( eDataArrayType == svDouble || eDataArrayType == svString ||
+ eDataArrayType == svSingleRef )
+ {
+ // Delta position for a single value is always 0.
+
+ // Found if data <= query, but not if query is string and found data is
+ // numeric or vice versa. This is how Excel does it but doesn't
+ // document it.
+
+ bool bFound = false;
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+
+ if ( bValueData )
+ {
+ if (rItem.meType == ScQueryEntry::ByString)
+ bFound = false;
+ else
+ bFound = (fDataVal <= rItem.mfVal);
+ }
+ else
+ {
+ if (rItem.meType != ScQueryEntry::ByString)
+ bFound = false;
+ else
+ bFound = (ScGlobal::GetCollator().compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
+ }
+
+ if (!bFound)
+ {
+ PushNA();
+ return;
+ }
+
+ if (pResMat)
+ {
+ if (pResMat->IsValue( 0, 0 ))
+ PushDouble(pResMat->GetDouble( 0, 0 ));
+ else
+ PushString(pResMat->GetString(0, 0));
+ }
+ else if (nParamCount == 3)
+ {
+ switch (eResArrayType)
+ {
+ case svDouble:
+ PushDouble( fResVal );
+ break;
+ case svString:
+ PushString( aResStr );
+ break;
+ case svDoubleRef:
+ case svSingleRef:
+ PushCellResultToken( true, ScAddress( nResCol1, nResRow1, nResTab), nullptr, nullptr);
+ break;
+ default:
+ assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
+ PushIllegalParameter();
+ }
+ }
+ else
+ {
+ switch (eDataArrayType)
+ {
+ case svDouble:
+ PushDouble( fDataVal );
+ break;
+ case svString:
+ PushString( aDataStr );
+ break;
+ case svSingleRef:
+ PushCellResultToken( true, aDataAdr, nullptr, nullptr);
+ break;
+ default:
+ assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
+ PushIllegalParameter();
+ }
+ }
+ return;
+ }
+
+ // Now, perform the search to compute the delta position (nDelta).
+
+ if (pDataMat)
+ {
+ // Data array is given as a matrix.
+ rEntry.bDoQuery = true;
+ rEntry.eOp = SC_LESS_EQUAL;
+ bool bFound = false;
+
+ SCSIZE nC, nR;
+ pDataMat->GetDimensions(nC, nR);
+
+ // Do not propagate errors from matrix while copying to vector.
+ pDataMat->SetErrorInterpreter( nullptr);
+
+ // Excel has an undocumented behaviour in that it seems to internally
+ // sort an interim array (i.e. error values specifically #DIV/0! are
+ // sorted to the end) or ignore error values that makes these "get last
+ // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
+ // see tdf#117016
+ // Instead of sorting a million entries of which mostly only a bunch of
+ // rows are filled and moving error values to the end which most are
+ // already anyway, assume the matrix to be sorted except error values
+ // and omit the coded DoubleError values.
+ // Do this only for a numeric matrix (that includes errors coded as
+ // doubles), which covers the case in question.
+ /* TODO: it's unclear whether this really matches Excel behaviour in
+ * all constellations or if there are cases that include unsorted error
+ * values and thus yield arbitrary binary search results or something
+ * different or whether there are cases where error values are also
+ * omitted from mixed numeric/string arrays or if it's not an interim
+ * matrix but a cell range reference instead. */
+ const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric());
+
+ // In case of non-vector matrix, only search the first row or column.
+ ScMatrixRef pDataMat2;
+ std::vector<SCCOLROW> vIndex;
+ if (bOmitErrorValues)
+ {
+ std::vector<double> vArray;
+ VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
+ const SCSIZE nElements = aMatAcc.GetElementCount();
+ for (SCSIZE i=0; i < nElements; ++i)
+ {
+ const double fVal = aMatAcc.GetDouble(i);
+ if (std::isfinite(fVal))
+ {
+ vArray.push_back(fVal);
+ vIndex.push_back(i);
+ }
+ }
+ if (vArray.empty())
+ {
+ PushNA();
+ return;
+ }
+ const size_t nElems = vArray.size();
+ if (nElems == nElements)
+ {
+ // No error value omitted, use as is.
+ pDataMat2 = pDataMat;
+ std::vector<SCCOLROW>().swap( vIndex);
+ }
+ else
+ {
+ nLenMajor = nElems;
+ if (bVertical)
+ {
+ ScMatrixRef pTempMat = GetNewMat( 1, nElems, /*bEmpty*/true );
+ pTempMat->PutDoubleVector( vArray, 0, 0);
+ pDataMat2 = pTempMat;
+ }
+ else
+ {
+ ScMatrixRef pTempMat = GetNewMat( nElems, 1, /*bEmpty*/true );
+ for (size_t i=0; i < nElems; ++i)
+ pTempMat->PutDouble( vArray[i], i, 0);
+ pDataMat2 = pTempMat;
+ }
+ }
+ }
+ else
+ {
+ // Just use as is with the VectorMatrixAccessor.
+ pDataMat2 = pDataMat;
+ }
+
+ // Do not propagate errors from matrix while searching.
+ pDataMat2->SetErrorInterpreter( nullptr);
+
+ VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
+
+ // binary search for non-equality mode (the source data is
+ // assumed to be sorted in ascending order).
+
+ SCCOLROW nDelta = -1;
+
+ SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
+ for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
+ {
+ SCSIZE nMid = nFirst + nLen/2;
+ sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry);
+ if (nCmp == 0)
+ {
+ // exact match. find the last item with the same value.
+ lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor);
+ nDelta = nMid;
+ bFound = true;
+ break;
+ }
+
+ if (nLen == 1) // first and last items are next to each other.
+ {
+ nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
+ // If already the 1st item is greater there's nothing found.
+ bFound = (nDelta >= 0);
+ break;
+ }
+
+ if (nCmp < 0)
+ nFirst = nMid;
+ else
+ nLast = nMid;
+ }
+
+ if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
+ {
+ sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry);
+ if (nCmp <= 0)
+ {
+ // either the last item is an exact match or the real
+ // hit is beyond the last item.
+ nDelta += 1;
+ bFound = true;
+ }
+ }
+ else if (nDelta > 0) // valid hit must be 2nd item or higher
+ {
+ // non-exact match
+ bFound = true;
+ }
+
+ // With 0-9 < A-Z, if query is numeric and data found is string, or
+ // vice versa, the (yet another undocumented) Excel behavior is to
+ // return #N/A instead.
+
+ if (bFound)
+ {
+ if (!vIndex.empty())
+ nDelta = vIndex[nDelta];
+
+ VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
+ SCCOLROW i = nDelta;
+ SCSIZE n = aMatAcc.GetElementCount();
+ if (o3tl::make_unsigned(i) >= n)
+ i = static_cast<SCCOLROW>(n);
+ bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
+ if (bByString == aMatAcc.IsValue(i))
+ bFound = false;
+ }
+
+ if (!bFound)
+ {
+ PushNA();
+ return;
+ }
+
+ // Now that we've found the delta, push the result back to the cell.
+
+ if (pResMat)
+ {
+ VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
+ // Result array is matrix.
+ // Note this does not replicate the other dimension.
+ if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
+ {
+ PushNA();
+ return;
+ }
+ if (aResMatAcc.IsValue(nDelta))
+ PushDouble(aResMatAcc.GetDouble(nDelta));
+ else
+ PushString(aResMatAcc.GetString(nDelta));
+ }
+ else if (nParamCount == 3)
+ {
+ /* TODO: the entire switch is a copy of the cell range search
+ * result, factor out. */
+ switch (eResArrayType)
+ {
+ case svDoubleRef:
+ case svSingleRef:
+ {
+ // Use the result array vector. Note that the result array is assumed
+ // to be a vector (i.e. 1-dimensional array).
+
+ ScAddress aAdr;
+ aAdr.SetTab(nResTab);
+ bool bResVertical = (nResRow2 - nResRow1) > 0;
+ if (bResVertical)
+ {
+ SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
+ if (nTempRow > mrDoc.MaxRow())
+ {
+ PushDouble(0);
+ return;
+ }
+ aAdr.SetCol(nResCol1);
+ aAdr.SetRow(nTempRow);
+ }
+ else
+ {
+ SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
+ if (nTempCol > mrDoc.MaxCol())
+ {
+ PushDouble(0);
+ return;
+ }
+ aAdr.SetCol(nTempCol);
+ aAdr.SetRow(nResRow1);
+ }
+ PushCellResultToken( true, aAdr, nullptr, nullptr);
+ }
+ break;
+ case svDouble:
+ case svString:
+ {
+ if (nDelta != 0)
+ PushNA();
+ else
+ {
+ switch (eResArrayType)
+ {
+ case svDouble:
+ PushDouble( fResVal );
+ break;
+ case svString:
+ PushString( aResStr );
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ break;
+ default:
+ assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search");
+ PushIllegalParameter();
+ }
+ }
+ else
+ {
+ // No result array. Use the data array to get the final value from.
+ // Propagate errors from matrix again.
+ pDataMat->SetErrorInterpreter( this);
+ if (bVertical)
+ {
+ if (pDataMat->IsValue(nC-1, nDelta))
+ PushDouble(pDataMat->GetDouble(nC-1, nDelta));
+ else
+ PushString(pDataMat->GetString(nC-1, nDelta));
+ }
+ else
+ {
+ if (pDataMat->IsValue(nDelta, nR-1))
+ PushDouble(pDataMat->GetDouble(nDelta, nR-1));
+ else
+ PushString(pDataMat->GetString(nDelta, nR-1));
+ }
+ }
+
+ return;
+ }
+
+ // Perform cell range search.
+
+ aParam.nCol1 = nCol1;
+ aParam.nRow1 = nRow1;
+ aParam.nCol2 = bVertical ? nCol1 : nCol2;
+ aParam.nRow2 = bVertical ? nRow2 : nRow1;
+ aParam.bByRow = bVertical;
+
+ rEntry.bDoQuery = true;
+ rEntry.eOp = SC_LESS_EQUAL;
+ rEntry.nField = nCol1;
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (rItem.meType == ScQueryEntry::ByString)
+ aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
+
+ ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false);
+ SCCOL nC;
+ SCROW nR;
+ // Advance Entry.nField in iterator upon switching columns if
+ // lookup in row.
+ aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
+ if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
+ {
+ PushNA();
+ return;
+ }
+
+ SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
+
+ if (pResMat)
+ {
+ VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
+ // Use the matrix result array.
+ // Note this does not replicate the other dimension.
+ if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
+ {
+ PushNA();
+ return;
+ }
+ if (aResMatAcc.IsValue(nDelta))
+ PushDouble(aResMatAcc.GetDouble(nDelta));
+ else
+ PushString(aResMatAcc.GetString(nDelta));
+ }
+ else if (nParamCount == 3)
+ {
+ /* TODO: the entire switch is a copy of the array search result, factor
+ * out. */
+ switch (eResArrayType)
+ {
+ case svDoubleRef:
+ case svSingleRef:
+ {
+ // Use the result array vector. Note that the result array is assumed
+ // to be a vector (i.e. 1-dimensional array).
+
+ ScAddress aAdr;
+ aAdr.SetTab(nResTab);
+ bool bResVertical = (nResRow2 - nResRow1) > 0;
+ if (bResVertical)
+ {
+ SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
+ if (nTempRow > mrDoc.MaxRow())
+ {
+ PushDouble(0);
+ return;
+ }
+ aAdr.SetCol(nResCol1);
+ aAdr.SetRow(nTempRow);
+ }
+ else
+ {
+ SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
+ if (nTempCol > mrDoc.MaxCol())
+ {
+ PushDouble(0);
+ return;
+ }
+ aAdr.SetCol(nTempCol);
+ aAdr.SetRow(nResRow1);
+ }
+ PushCellResultToken( true, aAdr, nullptr, nullptr);
+ }
+ break;
+ case svDouble:
+ case svString:
+ {
+ if (nDelta != 0)
+ PushNA();
+ else
+ {
+ switch (eResArrayType)
+ {
+ case svDouble:
+ PushDouble( fResVal );
+ break;
+ case svString:
+ PushString( aResStr );
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ break;
+ default:
+ assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search");
+ PushIllegalParameter();
+ }
+ }
+ else
+ {
+ // Regardless of whether or not the result array exists, the last
+ // array is always used as the "result" array.
+
+ ScAddress aAdr;
+ aAdr.SetTab(nTab1);
+ if (bVertical)
+ {
+ SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
+ if (nTempRow > mrDoc.MaxRow())
+ {
+ PushDouble(0);
+ return;
+ }
+ aAdr.SetCol(nCol2);
+ aAdr.SetRow(nTempRow);
+ }
+ else
+ {
+ SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
+ if (nTempCol > mrDoc.MaxCol())
+ {
+ PushDouble(0);
+ return;
+ }
+ aAdr.SetCol(nTempCol);
+ aAdr.SetRow(nRow2);
+ }
+ PushCellResultToken(true, aAdr, nullptr, nullptr);
+ }
+}
+
+void ScInterpreter::ScHLookup()
+{
+ CalculateLookup(true);
+}
+
+void ScInterpreter::CalculateLookup(bool bHLookup)
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount(nParamCount, 3, 4))
+ return;
+
+ // Optional 4th argument to declare whether or not the range is sorted.
+ bool bSorted = true;
+ if (nParamCount == 4)
+ bSorted = GetBool();
+
+ // Index of column to search.
+ double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
+
+ ScMatrixRef pMat = nullptr;
+ SCSIZE nC = 0, nR = 0;
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ const ScComplexRefData* refData = nullptr;
+ StackVar eType = GetStackType();
+ if (eType == svDoubleRef)
+ {
+ refData = GetStackDoubleRef(0);
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nTab1 != nTab2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ }
+ else if (eType == svSingleRef)
+ {
+ PopSingleRef(nCol1, nRow1, nTab1);
+ nCol2 = nCol1;
+ nRow2 = nRow1;
+ }
+ else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
+ {
+ pMat = GetMatrix();
+
+ if (pMat)
+ pMat->GetDimensions(nC, nR);
+ else
+ {
+ PushIllegalParameter();
+ return;
+ }
+ }
+ else
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ SCROW nZIndex = static_cast<SCROW>(fIndex);
+ SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
+
+ if (!pMat)
+ {
+ nZIndex += nRow1; // value row
+ nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ ScQueryParam aParam;
+ aParam.nCol1 = nCol1;
+ aParam.nRow1 = nRow1;
+ if ( bHLookup )
+ {
+ aParam.nCol2 = nCol2;
+ aParam.nRow2 = nRow1; // search only in the first row
+ aParam.bByRow = false;
+ }
+ else
+ {
+ aParam.nCol2 = nCol1; // search only in the first column
+ aParam.nRow2 = nRow2;
+ aParam.nTab = nTab1;
+ }
+
+ ScQueryEntry& rEntry = aParam.GetEntry(0);
+ rEntry.bDoQuery = true;
+ if ( bSorted )
+ rEntry.eOp = SC_LESS_EQUAL;
+ if ( !FillEntry(rEntry) )
+ return;
+
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (rItem.meType == ScQueryEntry::ByString)
+ aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
+ if (pMat)
+ {
+ SCSIZE nMatCount = bHLookup ? nC : nR;
+ SCSIZE nDelta = SCSIZE_MAX;
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+//!!!!!!!
+//TODO: enable regex on matrix strings
+//!!!!!!!
+ svl::SharedString aParamStr = rItem.maString;
+ if ( bSorted )
+ {
+ CollatorWrapper& rCollator = ScGlobal::GetCollator();
+ for (SCSIZE i = 0; i < nMatCount; i++)
+ {
+ if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))
+ {
+ sal_Int32 nRes =
+ rCollator.compareString(
+ bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
+ if (nRes <= 0)
+ nDelta = i;
+ else if (i>0) // #i2168# ignore first mismatch
+ i = nMatCount+1;
+ }
+ else
+ nDelta = i;
+ }
+ }
+ else
+ {
+ if (bHLookup)
+ {
+ for (SCSIZE i = 0; i < nMatCount; i++)
+ {
+ if (pMat->IsStringOrEmpty(i, 0))
+ {
+ if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
+ {
+ nDelta = i;
+ i = nMatCount + 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ if ( bSorted )
+ {
+ // #i2168# ignore strings
+ for (SCSIZE i = 0; i < nMatCount; i++)
+ {
+ if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)))
+ {
+ if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
+ nDelta = i;
+ else
+ i = nMatCount+1;
+ }
+ }
+ }
+ else
+ {
+ if (bHLookup)
+ {
+ for (SCSIZE i = 0; i < nMatCount; i++)
+ {
+ if (! pMat->IsStringOrEmpty(i, 0) )
+ {
+ if ( pMat->GetDouble(i,0) == rItem.mfVal)
+ {
+ nDelta = i;
+ i = nMatCount + 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
+ }
+ }
+ }
+ if ( nDelta != SCSIZE_MAX )
+ {
+ SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
+ SCSIZE nY = nDelta;
+ SCSIZE nXs = 0;
+ SCSIZE nYs = nY;
+ if ( bHLookup )
+ {
+ nX = nDelta;
+ nY = static_cast<SCSIZE>(nZIndex);
+ nXs = nX;
+ nYs = 0;
+ }
+ assert( nX < nC && nY < nR );
+ if (!(rItem.meType == ScQueryEntry::ByString && pMat->IsValue( nXs, nYs)))
+ {
+ if (pMat->IsStringOrEmpty( nX, nY))
+ PushString(pMat->GetString( nX, nY).getString());
+ else
+ PushDouble(pMat->GetDouble( nX, nY));
+ }
+ else
+ PushNA();
+ return;
+ }
+ else
+ PushNA();
+ }
+ else
+ {
+ rEntry.nField = nCol1;
+ bool bFound = false;
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ if ( bSorted )
+ rEntry.eOp = SC_LESS_EQUAL;
+ if ( bHLookup )
+ {
+ ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false);
+ // advance Entry.nField in Iterator upon switching columns
+ aCellIter.SetAdvanceQueryParamEntryField( true );
+ if ( bSorted )
+ {
+ SCROW nRow1_temp;
+ bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
+ }
+ else if ( aCellIter.GetFirst() )
+ {
+ bFound = true;
+ nCol = aCellIter.GetCol();
+ }
+ nRow = nZIndex;
+ }
+ else
+ {
+ ScAddress aResultPos( nCol1, nRow1, nTab1);
+ bFound = LookupQueryWithCache( aResultPos, aParam, refData);
+ nRow = aResultPos.Row();
+ nCol = nSpIndex;
+ }
+
+ if ( bFound )
+ {
+ ScAddress aAdr( nCol, nRow, nTab1 );
+ PushCellResultToken( true, aAdr, nullptr, nullptr);
+ }
+ else
+ PushNA();
+ }
+}
+
+bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
+{
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ switch ( GetStackType() )
+ {
+ case svDouble:
+ {
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = GetDouble();
+ }
+ break;
+ case svString:
+ {
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.maString = GetString();
+ }
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ PushInt(0);
+ return false;
+ }
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ rItem.meType = ScQueryEntry::ByValue;
+ rItem.mfVal = GetCellValue(aAdr, aCell);
+ }
+ else
+ {
+ GetCellString(rItem.maString, aCell);
+ rItem.meType = ScQueryEntry::ByString;
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ case svExternalSingleRef:
+ case svMatrix:
+ {
+ svl::SharedString aStr;
+ const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
+ rItem.maString = aStr;
+ rItem.meType = ScMatrix::IsNonValueType(nType) ?
+ ScQueryEntry::ByString : ScQueryEntry::ByValue;
+ }
+ break;
+ default:
+ {
+ PushIllegalParameter();
+ return false;
+ }
+ } // switch ( GetStackType() )
+ return true;
+}
+
+void ScInterpreter::ScVLookup()
+{
+ CalculateLookup(false);
+}
+
+void ScInterpreter::ScSubTotal()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 2 ) )
+ return;
+
+ // We must fish the 1st parameter deep from the stack! And push it on top.
+ const FormulaToken* p = pStack[ sp - nParamCount ];
+ PushWithoutError( *p );
+ sal_Int32 nFunc = GetInt32();
+ mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered;
+ if (nFunc > 100)
+ {
+ // For opcodes 101 through 111, we need to skip hidden cells.
+ // Other than that these opcodes are identical to 1 through 11.
+ mnSubTotalFlags |= SubtotalFlags::IgnoreHidden;
+ nFunc -= 100;
+ }
+
+ if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 )
+ PushIllegalArgument(); // simulate return on stack, not SetError(...)
+ else
+ {
+ cPar = nParamCount - 1;
+ switch( nFunc )
+ {
+ case SUBTOTAL_FUNC_AVE : ScAverage(); break;
+ case SUBTOTAL_FUNC_CNT : ScCount(); break;
+ case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
+ case SUBTOTAL_FUNC_MAX : ScMax(); break;
+ case SUBTOTAL_FUNC_MIN : ScMin(); break;
+ case SUBTOTAL_FUNC_PROD : ScProduct(); break;
+ case SUBTOTAL_FUNC_STD : ScStDev(); break;
+ case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
+ case SUBTOTAL_FUNC_SUM : ScSum(); break;
+ case SUBTOTAL_FUNC_VAR : ScVar(); break;
+ case SUBTOTAL_FUNC_VARP : ScVarP(); break;
+ default : PushIllegalArgument(); break;
+ }
+ }
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ // Get rid of the 1st (fished) parameter.
+ FormulaConstTokenRef xRef( PopToken());
+ Pop();
+ PushTokenRef( xRef);
+}
+
+void ScInterpreter::ScAggregate()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 3 ) )
+ return;
+
+ const FormulaError nErr = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+
+ // fish the 1st parameter from the stack and push it on top.
+ const FormulaToken* p = pStack[ sp - nParamCount ];
+ PushWithoutError( *p );
+ sal_Int32 nFunc = GetInt32();
+ // fish the 2nd parameter from the stack and push it on top.
+ const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ];
+ PushWithoutError( *p2 );
+ sal_Int32 nOption = GetInt32();
+
+ if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 )
+ {
+ nGlobalError = nErr;
+ PushIllegalArgument();
+ }
+ else
+ {
+ switch ( nOption)
+ {
+ case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
+ mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg;
+ break;
+ case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
+ mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg;
+ break;
+ case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
+ mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
+ break;
+ case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
+ mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
+ break;
+ case 4 : // ignore nothing
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ break;
+ case 5 : // ignore hidden rows
+ mnSubTotalFlags = SubtotalFlags::IgnoreHidden ;
+ break;
+ case 6 : // ignore error values
+ mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ;
+ break;
+ case 7 : // ignore hidden rows and error values
+ mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ;
+ break;
+ default :
+ nGlobalError = nErr;
+ PushIllegalArgument();
+ return;
+ }
+
+ if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE)
+ nGlobalError = nErr;
+
+ cPar = nParamCount - 2;
+ switch ( nFunc )
+ {
+ case AGGREGATE_FUNC_AVE : ScAverage(); break;
+ case AGGREGATE_FUNC_CNT : ScCount(); break;
+ case AGGREGATE_FUNC_CNT2 : ScCount2(); break;
+ case AGGREGATE_FUNC_MAX : ScMax(); break;
+ case AGGREGATE_FUNC_MIN : ScMin(); break;
+ case AGGREGATE_FUNC_PROD : ScProduct(); break;
+ case AGGREGATE_FUNC_STD : ScStDev(); break;
+ case AGGREGATE_FUNC_STDP : ScStDevP(); break;
+ case AGGREGATE_FUNC_SUM : ScSum(); break;
+ case AGGREGATE_FUNC_VAR : ScVar(); break;
+ case AGGREGATE_FUNC_VARP : ScVarP(); break;
+ case AGGREGATE_FUNC_MEDIAN : ScMedian(); break;
+ case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break;
+ case AGGREGATE_FUNC_LARGE : ScLarge(); break;
+ case AGGREGATE_FUNC_SMALL : ScSmall(); break;
+ case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break;
+ case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break;
+ case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
+ case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break;
+ default:
+ nGlobalError = nErr;
+ PushIllegalArgument();
+ break;
+ }
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ }
+ FormulaConstTokenRef xRef( PopToken());
+ // Get rid of the 1st and 2nd (fished) parameters.
+ Pop();
+ Pop();
+ PushTokenRef( xRef);
+}
+
+std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField )
+{
+ bool bAllowMissingField = false;
+ if ( rMissingField )
+ {
+ bAllowMissingField = true;
+ rMissingField = false;
+ }
+ if ( GetByte() == 3 )
+ {
+ // First, get the query criteria range.
+ ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
+ if (!pQueryRef)
+ return nullptr;
+
+ bool bByVal = true;
+ double nVal = 0.0;
+ svl::SharedString aStr;
+ ScRange aMissingRange;
+ bool bRangeFake = false;
+ switch (GetStackType())
+ {
+ case svDouble :
+ nVal = ::rtl::math::approxFloor( GetDouble() );
+ if ( bAllowMissingField && nVal == 0.0 )
+ rMissingField = true; // fake missing parameter
+ break;
+ case svString :
+ bByVal = false;
+ aStr = GetString();
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ nVal = GetCellValue(aAdr, aCell);
+ else
+ {
+ bByVal = false;
+ GetCellString(aStr, aCell);
+ }
+ }
+ break;
+ case svDoubleRef :
+ if ( bAllowMissingField )
+ { // fake missing parameter for old SO compatibility
+ bRangeFake = true;
+ PopDoubleRef( aMissingRange );
+ }
+ else
+ {
+ PopError();
+ SetError( FormulaError::IllegalParameter );
+ }
+ break;
+ case svMissing :
+ PopError();
+ if ( bAllowMissingField )
+ rMissingField = true;
+ else
+ SetError( FormulaError::IllegalParameter );
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter );
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ return nullptr;
+
+ unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
+
+ if (nGlobalError != FormulaError::NONE || !pDBRef)
+ return nullptr;
+
+ if ( bRangeFake )
+ {
+ // range parameter must match entire database range
+ if (pDBRef->isRangeEqual(aMissingRange))
+ rMissingField = true;
+ else
+ SetError( FormulaError::IllegalParameter );
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ return nullptr;
+
+ SCCOL nField = pDBRef->getFirstFieldColumn();
+ if (rMissingField)
+ ; // special case
+ else if (bByVal)
+ nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
+ else
+ {
+ FormulaError nErr = FormulaError::NONE;
+ nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
+ SetError(nErr);
+ }
+
+ if (!mrDoc.ValidCol(nField))
+ return nullptr;
+
+ unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
+
+ if (pParam)
+ {
+ // An allowed missing field parameter sets the result field
+ // to any of the query fields, just to be able to return
+ // some cell from the iterator.
+ if ( rMissingField )
+ nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
+ pParam->mnField = nField;
+
+ SCSIZE nCount = pParam->GetEntryCount();
+ for ( SCSIZE i=0; i < nCount; i++ )
+ {
+ ScQueryEntry& rEntry = pParam->GetEntry(i);
+ if (!rEntry.bDoQuery)
+ break;
+
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ sal_uInt32 nIndex = 0;
+ OUString aQueryStr = rItem.maString.getString();
+ bool bNumber = pFormatter->IsNumberFormat(
+ aQueryStr, nIndex, rItem.mfVal);
+ rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+
+ if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal)
+ pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc);
+ }
+ return pParam;
+ }
+ }
+ return nullptr;
+}
+
+void ScInterpreter::DBIterator( ScIterFunc eFunc )
+{
+ double fRes = 0;
+ KahanSum fErg = 0;
+ sal_uLong nCount = 0;
+ bool bMissingField = false;
+ unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
+ if (pQueryParam)
+ {
+ if (!pQueryParam->IsValidFieldIndex())
+ {
+ SetError(FormulaError::NoValue);
+ return;
+ }
+ ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
+ ScDBQueryDataIterator::Value aValue;
+ if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
+ {
+ switch( eFunc )
+ {
+ case ifPRODUCT: fRes = 1; break;
+ case ifMAX: fRes = -MAXDOUBLE; break;
+ case ifMIN: fRes = MAXDOUBLE; break;
+ default: ; // nothing
+ }
+
+ do
+ {
+ nCount++;
+ switch( eFunc )
+ {
+ case ifAVERAGE:
+ case ifSUM:
+ fErg += aValue.mfValue;
+ break;
+ case ifSUMSQ:
+ fErg += aValue.mfValue * aValue.mfValue;
+ break;
+ case ifPRODUCT:
+ fRes *= aValue.mfValue;
+ break;
+ case ifMAX:
+ if( aValue.mfValue > fRes ) fRes = aValue.mfValue;
+ break;
+ case ifMIN:
+ if( aValue.mfValue < fRes ) fRes = aValue.mfValue;
+ break;
+ default: ; // nothing
+ }
+ }
+ while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
+ }
+ SetError(aValue.mnError);
+ }
+ else
+ SetError( FormulaError::IllegalParameter);
+ switch( eFunc )
+ {
+ case ifCOUNT: fRes = nCount; break;
+ case ifSUM: fRes = fErg.get(); break;
+ case ifSUMSQ: fRes = fErg.get(); break;
+ case ifAVERAGE: fRes = div(fErg.get(), nCount); break;
+ default: ; // nothing
+ }
+ PushDouble( fRes );
+}
+
+void ScInterpreter::ScDBSum()
+{
+ DBIterator( ifSUM );
+}
+
+void ScInterpreter::ScDBCount()
+{
+ bool bMissingField = true;
+ unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
+ if (pQueryParam)
+ {
+ sal_uLong nCount = 0;
+ if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
+ { // count all matching records
+ // TODO: currently the QueryIterators only return cell pointers of
+ // existing cells, so if a query matches an empty cell there's
+ // nothing returned, and therefore not counted!
+ // Since this has ever been the case and this code here only came
+ // into existence to fix #i6899 and it never worked before we'll
+ // have to live with it until we reimplement the iterators to also
+ // return empty cells, which would mean to adapt all callers of
+ // iterators.
+ ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
+ p->nCol2 = p->nCol1; // Don't forget to select only one column.
+ SCTAB nTab = p->nTab;
+ // ScQueryCellIteratorDirect doesn't make use of ScDBQueryParamBase::mnField,
+ // so the source range has to be restricted, like before the introduction
+ // of ScDBQueryParamBase.
+ p->nCol1 = p->nCol2 = p->mnField;
+ ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab, *p, true);
+ if ( aCellIter.GetFirst() )
+ {
+ do
+ {
+ nCount++;
+ } while ( aCellIter.GetNext() );
+ }
+ }
+ else
+ { // count only matching records with a value in the "result" field
+ if (!pQueryParam->IsValidFieldIndex())
+ {
+ SetError(FormulaError::NoValue);
+ return;
+ }
+ ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
+ ScDBQueryDataIterator::Value aValue;
+ if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
+ {
+ do
+ {
+ nCount++;
+ }
+ while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
+ }
+ SetError(aValue.mnError);
+ }
+ PushDouble( nCount );
+ }
+ else
+ PushIllegalParameter();
+}
+
+void ScInterpreter::ScDBCount2()
+{
+ bool bMissingField = true;
+ unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
+ if (pQueryParam)
+ {
+ if (!pQueryParam->IsValidFieldIndex())
+ {
+ SetError(FormulaError::NoValue);
+ return;
+ }
+ sal_uLong nCount = 0;
+ pQueryParam->mbSkipString = false;
+ ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
+ ScDBQueryDataIterator::Value aValue;
+ if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
+ {
+ do
+ {
+ nCount++;
+ }
+ while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
+ }
+ SetError(aValue.mnError);
+ PushDouble( nCount );
+ }
+ else
+ PushIllegalParameter();
+}
+
+void ScInterpreter::ScDBAverage()
+{
+ DBIterator( ifAVERAGE );
+}
+
+void ScInterpreter::ScDBMax()
+{
+ DBIterator( ifMAX );
+}
+
+void ScInterpreter::ScDBMin()
+{
+ DBIterator( ifMIN );
+}
+
+void ScInterpreter::ScDBProduct()
+{
+ DBIterator( ifPRODUCT );
+}
+
+void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
+{
+ std::vector<double> values;
+ KahanSum vSum = 0.0;
+ KahanSum fSum = 0.0;
+
+ rValCount = 0.0;
+ bool bMissingField = false;
+ unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
+ if (pQueryParam)
+ {
+ if (!pQueryParam->IsValidFieldIndex())
+ {
+ SetError(FormulaError::NoValue);
+ return;
+ }
+ ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
+ ScDBQueryDataIterator::Value aValue;
+ if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE)
+ {
+ do
+ {
+ rValCount++;
+ values.push_back(aValue.mfValue);
+ fSum += aValue.mfValue;
+ }
+ while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue));
+ }
+ SetError(aValue.mnError);
+ }
+ else
+ SetError( FormulaError::IllegalParameter);
+
+ double vMean = fSum.get() / values.size();
+
+ for (double v : values)
+ vSum += (v - vMean) * (v - vMean);
+
+ rVal = vSum.get();
+}
+
+void ScInterpreter::ScDBStdDev()
+{
+ double fVal, fCount;
+ GetDBStVarParams( fVal, fCount );
+ PushDouble( sqrt(fVal/(fCount-1)));
+}
+
+void ScInterpreter::ScDBStdDevP()
+{
+ double fVal, fCount;
+ GetDBStVarParams( fVal, fCount );
+ PushDouble( sqrt(fVal/fCount));
+}
+
+void ScInterpreter::ScDBVar()
+{
+ double fVal, fCount;
+ GetDBStVarParams( fVal, fCount );
+ PushDouble(fVal/(fCount-1));
+}
+
+void ScInterpreter::ScDBVarP()
+{
+ double fVal, fCount;
+ GetDBStVarParams( fVal, fCount );
+ PushDouble(fVal/fCount);
+}
+
+static bool lcl_IsTableStructuredRef(const OUString& sRefStr, sal_Int32& nIndex)
+{
+ nIndex = ScGlobal::FindUnquoted(sRefStr, '[');
+ return (nIndex > 0 && ScGlobal::FindUnquoted(sRefStr, ']', nIndex + 1) > nIndex);
+}
+
+void ScInterpreter::ScIndirect()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ // Reference address syntax for INDIRECT is configurable.
+ FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
+ if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
+ // Use the current address syntax if unspecified.
+ eConv = mrDoc.GetAddressConvention();
+
+ // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
+ // to determine which syntax to use during doc import
+ bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1);
+
+ if (nParamCount == 2 && 0.0 == GetDouble() )
+ {
+ // Overwrite the config and try Excel R1C1.
+ eConv = FormulaGrammar::CONV_XL_R1C1;
+ bTryXlA1 = false;
+ }
+
+ svl::SharedString sSharedRefStr = GetString();
+ const OUString & sRefStr = sSharedRefStr.getString();
+ if (sRefStr.isEmpty())
+ {
+ // Bail out early for empty cells, rely on "we do have a string" below.
+ PushError( FormulaError::NoRef);
+ return;
+ }
+
+ const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos );
+ const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
+ SCTAB nTab = aPos.Tab();
+
+ bool bTableRefNamed = false;
+ sal_Int32 nTableRefNamedIndex = -1;
+ OUString sTabRefStr;
+
+ // Named expressions and DB range names need to be tried first, as older 1K
+ // columns allowed names that would now match a 16k columns cell address.
+ do
+ {
+ ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString( sRefStr, nTab, mrDoc, eConv);
+ if (!pData)
+ break;
+
+ // We need this in order to obtain a good range.
+ pData->ValidateTabRefs();
+
+ ScRange aRange;
+
+ // This is the usual way to treat named ranges containing
+ // relative references.
+ if (!pData->IsReference(aRange, aPos))
+ {
+ sTabRefStr = pData->GetSymbol();
+ bTableRefNamed = lcl_IsTableStructuredRef(sTabRefStr, nTableRefNamedIndex);
+ // if bTableRefNamed is true, we have a name that maps to a table structured reference.
+ // Such a case is handled below.
+ break;
+ }
+
+ if (aRange.aStart == aRange.aEnd)
+ PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab());
+ else
+ PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), aRange.aEnd.Col(),
+ aRange.aEnd.Row(), aRange.aEnd.Tab());
+
+ // success!
+ return;
+ }
+ while (false);
+
+ do
+ {
+ if (bTableRefNamed)
+ break;
+
+ const OUString & aName( sSharedRefStr.getIgnoreCaseString() );
+ ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs();
+ const ScDBData* pData = rDBs.findByUpperName( aName);
+ if (!pData)
+ break;
+
+ ScRange aRange;
+ pData->GetArea( aRange);
+
+ // In Excel, specifying a table name without [] resolves to the
+ // same as with [], a range that excludes header and totals
+ // rows and contains only data rows. Do the same.
+ if (pData->HasHeader())
+ aRange.aStart.IncRow();
+ if (pData->HasTotals())
+ aRange.aEnd.IncRow(-1);
+
+ if (aRange.aStart.Row() > aRange.aEnd.Row())
+ break;
+
+ if (aRange.aStart == aRange.aEnd)
+ PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab());
+ else
+ PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), aRange.aEnd.Col(),
+ aRange.aEnd.Row(), aRange.aEnd.Tab());
+
+ // success!
+ return;
+ }
+ while (false);
+
+ ScRefAddress aRefAd, aRefAd2;
+ ScAddress::ExternalInfo aExtInfo;
+ if ( !bTableRefNamed &&
+ (ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
+ ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd,
+ aRefAd2, aDetailsXlA1, &aExtInfo) ) ) )
+ {
+ if (aExtInfo.mbExternal)
+ {
+ PushExternalDoubleRef(
+ aExtInfo.mnFileId, aExtInfo.maTabName,
+ aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
+ aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
+ }
+ else
+ PushDoubleRef( aRefAd, aRefAd2);
+ }
+ else if ( !bTableRefNamed &&
+ (ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
+ ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd,
+ aDetailsXlA1, &aExtInfo) ) ) )
+ {
+ if (aExtInfo.mbExternal)
+ {
+ PushExternalSingleRef(
+ aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
+ }
+ else
+ PushSingleRef( aRefAd);
+ }
+ else
+ {
+ // It may be even a TableRef or an external name.
+ // Anything else that resolves to one reference could be added
+ // here, but we don't want to compile every arbitrary string. This
+ // is already nasty enough...
+ sal_Int32 nIndex = bTableRefNamed ? nTableRefNamedIndex : -1;
+ bool bTableRef = bTableRefNamed;
+ if (!bTableRefNamed)
+ bTableRef = lcl_IsTableStructuredRef(sRefStr, nIndex);
+ bool bExternalName = false; // External references would had been consumed above already.
+ if (!bTableRef)
+ {
+ // This is our own file name reference representation centric.. but
+ // would work also for XL '[doc]'!name and also for
+ // '[doc]Sheet1'!name ... sickos.
+ if (sRefStr[0] == '\'')
+ {
+ // Minimum 'a'#name or 'a'!name
+ // bTryXlA1 means try both, first our own.
+ if (bTryXlA1 || eConv == FormulaGrammar::CONV_OOO)
+ {
+ nIndex = ScGlobal::FindUnquoted( sRefStr, '#');
+ if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
+ {
+ bExternalName = true;
+ eConv = FormulaGrammar::CONV_OOO;
+ }
+ }
+ if (!bExternalName && (bTryXlA1 || eConv != FormulaGrammar::CONV_OOO))
+ {
+ nIndex = ScGlobal::FindUnquoted( sRefStr, '!');
+ if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
+ {
+ bExternalName = true;
+ }
+ }
+ }
+
+ }
+ if (bExternalName || bTableRef)
+ {
+ do
+ {
+ ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar());
+ aComp.SetRefConvention( eConv); // must be after grammar
+ std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString(bTableRefNamed ? sTabRefStr : sRefStr));
+
+ if (pTokArr->GetCodeError() != FormulaError::NONE || !pTokArr->GetLen())
+ break;
+
+ // Whatever... use only the specific case.
+ if (bExternalName)
+ {
+ const formula::FormulaToken* pTok = pTokArr->FirstToken();
+ if (!pTok || pTok->GetType() != svExternalName)
+ break;
+ }
+ else if (!pTokArr->HasOpCode( ocTableRef))
+ break;
+
+ aComp.CompileTokenArray();
+
+ // A syntactically valid reference will generate exactly
+ // one RPN token, a reference or error. Discard everything
+ // else as error.
+ if (pTokArr->GetCodeLen() != 1)
+ break;
+
+ ScTokenRef xTok( pTokArr->FirstRPNToken());
+ if (!xTok)
+ break;
+
+ switch (xTok->GetType())
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svError:
+ PushTokenRef( xTok);
+ // success!
+ return;
+ default:
+ ; // nothing
+ }
+ }
+ while (false);
+ }
+
+ PushError( FormulaError::NoRef);
+ }
+}
+
+void ScInterpreter::ScAddressFunc()
+{
+ OUString sTabStr;
+
+ sal_uInt8 nParamCount = GetByte();
+ if( !MustHaveParamCount( nParamCount, 2, 5 ) )
+ return;
+
+ if( nParamCount >= 5 )
+ sTabStr = GetString().getString();
+
+ FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
+ if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0))
+ eConv = FormulaGrammar::CONV_XL_R1C1;
+ else
+ {
+ // If A1 syntax is requested then the actual sheet separator and format
+ // convention depends on the syntax configured for INDIRECT to match
+ // that, and if it is unspecified then the document's address syntax.
+ FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax;
+ if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED)
+ eForceConv = mrDoc.GetAddressConvention();
+ if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1)
+ eConv = FormulaGrammar::CONV_XL_A1; // for anything Excel use Excel A1
+ }
+
+ ScRefFlags nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; // default
+ if( nParamCount >= 3 )
+ {
+ sal_Int32 n = GetInt32WithDefault(1);
+ switch ( n )
+ {
+ default :
+ PushNoValue();
+ return;
+
+ case 5:
+ case 1 : break; // default
+ case 6:
+ case 2 : nFlags = ScRefFlags::ROW_ABS; break;
+ case 7:
+ case 3 : nFlags = ScRefFlags::COL_ABS; break;
+ case 8:
+ case 4 : nFlags = ScRefFlags::ZERO; break; // both relative
+ }
+ }
+ nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
+
+ SCCOL nCol = static_cast<SCCOL>(GetInt16());
+ SCROW nRow = static_cast<SCROW>(GetInt32());
+ if( eConv == FormulaGrammar::CONV_XL_R1C1 )
+ {
+ // YUCK! The XL interface actually treats rel R1C1 refs differently
+ // than A1
+ if( !(nFlags & ScRefFlags::COL_ABS) )
+ nCol += aPos.Col() + 1;
+ if( !(nFlags & ScRefFlags::ROW_ABS) )
+ nRow += aPos.Row() + 1;
+ }
+
+ --nCol;
+ --nRow;
+ if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow))
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ const ScAddress::Details aDetails( eConv, aPos );
+ const ScAddress aAdr( nCol, nRow, 0);
+ OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails));
+
+ if( nParamCount >= 5 && !sTabStr.isEmpty() )
+ {
+ OUString aDoc;
+ if (eConv == FormulaGrammar::CONV_OOO)
+ {
+ // Isolate Tab from 'Doc'#Tab
+ sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
+ if (nPos != -1)
+ {
+ if (sTabStr[nPos+1] == '$')
+ ++nPos; // also split 'Doc'#$Tab
+ aDoc = sTabStr.copy( 0, nPos+1);
+ sTabStr = sTabStr.copy( nPos+1);
+ }
+ }
+ /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
+ * need some extra handling to isolate Tab from Doc. */
+ if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
+ ScCompiler::CheckTabQuotes( sTabStr, eConv);
+ if (!aDoc.isEmpty())
+ sTabStr = aDoc + sTabStr;
+ sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ?
+ std::u16string_view(u"!") : std::u16string_view(u".");
+ sTabStr += aRefStr;
+ PushString( sTabStr );
+ }
+ else
+ PushString( aRefStr );
+}
+
+void ScInterpreter::ScOffset()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+
+ bool bNewWidth = false;
+ bool bNewHeight = false;
+ sal_Int32 nColNew = 1, nRowNew = 1;
+ if (nParamCount == 5)
+ {
+ if (IsMissing())
+ PopError();
+ else
+ {
+ nColNew = GetInt32();
+ bNewWidth = true;
+ }
+ }
+ if (nParamCount >= 4)
+ {
+ if (IsMissing())
+ PopError();
+ else
+ {
+ nRowNew = GetInt32();
+ bNewHeight = true;
+ }
+ }
+ sal_Int32 nColPlus = GetInt32();
+ sal_Int32 nRowPlus = GetInt32();
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ if (nColNew <= 0 || nRowNew <= 0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ SCCOL nCol1(0);
+ SCROW nRow1(0);
+ SCTAB nTab1(0);
+ SCCOL nCol2(0);
+ SCROW nRow2(0);
+ SCTAB nTab2(0);
+ switch (GetStackType())
+ {
+ case svSingleRef:
+ {
+ PopSingleRef(nCol1, nRow1, nTab1);
+ if (!bNewWidth && !bNewHeight)
+ {
+ nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
+ nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
+ if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
+ PushIllegalArgument();
+ else
+ PushSingleRef(nCol1, nRow1, nTab1);
+ }
+ else
+ {
+ nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
+ nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
+ nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
+ nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
+ if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
+ !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
+ PushIllegalArgument();
+ else
+ PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
+ }
+ break;
+ }
+ case svExternalSingleRef:
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScSingleRefData aRef;
+ PopExternalSingleRef(nFileId, aTabName, aRef);
+ ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
+ nCol1 = aAbsRef.Col();
+ nRow1 = aAbsRef.Row();
+ nTab1 = aAbsRef.Tab();
+
+ if (!bNewWidth && !bNewHeight)
+ {
+ nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
+ nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
+ if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
+ PushIllegalArgument();
+ else
+ PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
+ }
+ else
+ {
+ nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
+ nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
+ nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
+ nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
+ nTab2 = nTab1;
+ if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
+ !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
+ PushIllegalArgument();
+ else
+ PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ }
+ break;
+ }
+ case svDoubleRef:
+ {
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (!bNewWidth)
+ nColNew = nCol2 - nCol1 + 1;
+ if (!bNewHeight)
+ nRowNew = nRow2 - nRow1 + 1;
+ nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
+ nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
+ nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
+ nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
+ if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
+ !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
+ PushIllegalArgument();
+ else
+ PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
+ break;
+ }
+ case svExternalDoubleRef:
+ {
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aRef;
+ PopExternalDoubleRef(nFileId, aTabName, aRef);
+ ScRange aAbs = aRef.toAbs(mrDoc, aPos);
+ nCol1 = aAbs.aStart.Col();
+ nRow1 = aAbs.aStart.Row();
+ nTab1 = aAbs.aStart.Tab();
+ nCol2 = aAbs.aEnd.Col();
+ nRow2 = aAbs.aEnd.Row();
+ nTab2 = aAbs.aEnd.Tab();
+ if (!bNewWidth)
+ nColNew = nCol2 - nCol1 + 1;
+ if (!bNewHeight)
+ nRowNew = nRow2 - nRow1 + 1;
+ nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
+ nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
+ nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
+ nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
+ if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
+ !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
+ PushIllegalArgument();
+ else
+ PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ break;
+ }
+ default:
+ PushIllegalParameter();
+ break;
+ } // end switch
+}
+
+void ScInterpreter::ScIndex()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 4 ) )
+ return;
+
+ sal_uInt32 nArea;
+ size_t nAreaCount;
+ SCCOL nCol;
+ SCROW nRow;
+ if (nParamCount == 4)
+ nArea = GetUInt32();
+ else
+ nArea = 1;
+ bool bColMissing;
+ if (nParamCount >= 3)
+ {
+ bColMissing = IsMissing();
+ nCol = static_cast<SCCOL>(GetInt16());
+ }
+ else
+ {
+ bColMissing = false;
+ nCol = 0;
+ }
+ if (nParamCount >= 2)
+ nRow = static_cast<SCROW>(GetInt32());
+ else
+ nRow = 0;
+ if (GetStackType() == svRefList)
+ nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0);
+ else
+ nAreaCount = 1; // one reference or array or whatever
+ if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount)
+ {
+ PushError( FormulaError::NoRef);
+ return;
+ }
+ else if (nArea < 1 || nCol < 0 || nRow < 0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ switch (GetStackType())
+ {
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ if (nArea != 1)
+ SetError(FormulaError::IllegalArgument);
+ sal_uInt16 nOldSp = sp;
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+
+ // Access one element of a vector independent of col/row
+ // orientation. Excel documentation does not mention, but
+ // i62850 had a .xls example of a row vector accessed by
+ // row number returning one element. This
+ // INDEX(row_vector;element) behaves the same as
+ // INDEX(row_vector;0;element) and thus contradicts Excel
+ // documentation where the second parameter is always
+ // row_num.
+ //
+ // ODFF v1.3 in 6.14.6 INDEX states "If DataSource is a
+ // one-dimensional row vector, Row is optional, which
+ // effectively makes Row act as the column offset into the
+ // vector". Guess the first Row is a typo and should read
+ // Column instead.
+
+ const bool bRowVectorSpecial = (nParamCount == 2 || bColMissing);
+ const bool bRowVectorElement = (nR == 1 && (nCol != 0 || (bRowVectorSpecial && nRow != 0)));
+ const bool bVectorElement = (bRowVectorElement || (nC == 1 && nRow != 0));
+
+ if (nC == 0 || nR == 0 ||
+ (!bVectorElement && (o3tl::make_unsigned(nCol) > nC ||
+ o3tl::make_unsigned(nRow) > nR)))
+ PushError( FormulaError::NoRef);
+ else if (nCol == 0 && nRow == 0)
+ sp = nOldSp;
+ else if (bVectorElement)
+ {
+ // Vectors here don't replicate to the other dimension.
+ SCSIZE nElement, nOtherDimension;
+ if (bRowVectorElement && !bRowVectorSpecial)
+ {
+ nElement = o3tl::make_unsigned(nCol);
+ nOtherDimension = o3tl::make_unsigned(nRow);
+ }
+ else
+ {
+ nElement = o3tl::make_unsigned(nRow);
+ nOtherDimension = o3tl::make_unsigned(nCol);
+ }
+
+ if (nElement == 0 || nElement > nC * nR || nOtherDimension > 1)
+ PushError( FormulaError::NoRef);
+ else
+ {
+ --nElement;
+ if (pMat->IsStringOrEmpty( nElement))
+ PushString( pMat->GetString(nElement).getString());
+ else
+ PushDouble( pMat->GetDouble( nElement));
+ }
+ }
+ else if (nCol == 0)
+ {
+ ScMatrixRef pResMat = GetNewMat(nC, 1, /*bEmpty*/true);
+ if (pResMat)
+ {
+ SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
+ for (SCSIZE i = 0; i < nC; i++)
+ if (!pMat->IsStringOrEmpty(i, nRowMinus1))
+ pResMat->PutDouble(pMat->GetDouble(i,
+ nRowMinus1), i, 0);
+ else
+ pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
+
+ PushMatrix(pResMat);
+ }
+ else
+ PushError( FormulaError::NoRef);
+ }
+ else if (nRow == 0)
+ {
+ ScMatrixRef pResMat = GetNewMat(1, nR, /*bEmpty*/true);
+ if (pResMat)
+ {
+ SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
+ for (SCSIZE i = 0; i < nR; i++)
+ if (!pMat->IsStringOrEmpty(nColMinus1, i))
+ pResMat->PutDouble(pMat->GetDouble(nColMinus1,
+ i), i);
+ else
+ pResMat->PutString(pMat->GetString(nColMinus1, i), i);
+ PushMatrix(pResMat);
+ }
+ else
+ PushError( FormulaError::NoRef);
+ }
+ else
+ {
+ if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1),
+ static_cast<SCSIZE>(nRow-1)))
+ PushDouble( pMat->GetDouble(
+ static_cast<SCSIZE>(nCol-1),
+ static_cast<SCSIZE>(nRow-1)));
+ else
+ PushString( pMat->GetString(
+ static_cast<SCSIZE>(nCol-1),
+ static_cast<SCSIZE>(nRow-1)).getString());
+ }
+ }
+ }
+ break;
+ case svSingleRef:
+ {
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ PopSingleRef( nCol1, nRow1, nTab1);
+ if (nCol > 1 || nRow > 1)
+ PushError( FormulaError::NoRef);
+ else
+ PushSingleRef( nCol1, nRow1, nTab1);
+ }
+ break;
+ case svDoubleRef:
+ case svRefList:
+ {
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ SCTAB nTab2 = 0;
+ bool bRowArray = false;
+ if (GetStackType() == svRefList)
+ {
+ FormulaConstTokenRef xRef = PopToken();
+ if (nGlobalError != FormulaError::NONE || !xRef)
+ {
+ PushError( FormulaError::NoRef);
+ return;
+ }
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange);
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if ( nParamCount == 2 && nRow1 == nRow2 )
+ bRowArray = true;
+ }
+ else
+ {
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if ( nParamCount == 2 && nRow1 == nRow2 )
+ bRowArray = true;
+ }
+ if ( nTab1 != nTab2 ||
+ (nCol > 0 && nCol1+nCol-1 > nCol2) ||
+ (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
+ ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
+ PushError( FormulaError::NoRef);
+ else if (nCol == 0 && nRow == 0)
+ {
+ if ( nCol1 == nCol2 && nRow1 == nRow2 )
+ PushSingleRef( nCol1, nRow1, nTab1 );
+ else
+ PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
+ }
+ else if (nRow == 0)
+ {
+ if ( nRow1 == nRow2 )
+ PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
+ else
+ PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
+ nCol1+nCol-1, nRow2, nTab1 );
+ }
+ else if (nCol == 0)
+ {
+ if ( nCol1 == nCol2 )
+ PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
+ else if ( bRowArray )
+ {
+ nCol =static_cast<SCCOL>(nRow);
+ nRow = 1;
+ PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
+ }
+ else
+ PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
+ nCol2, nRow1+nRow-1, nTab1);
+ }
+ else
+ PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
+ }
+ break;
+ default:
+ PopError();
+ PushError( FormulaError::NoRef);
+ }
+}
+
+void ScInterpreter::ScMultiArea()
+{
+ // Legacy support, convert to RefList
+ sal_uInt8 nParamCount = GetByte();
+ if (MustHaveParamCountMin( nParamCount, 1))
+ {
+ while (nGlobalError == FormulaError::NONE && nParamCount-- > 1)
+ {
+ ScUnionFunc();
+ }
+ }
+}
+
+void ScInterpreter::ScAreas()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount( nParamCount, 1))
+ return;
+
+ size_t nCount = 0;
+ switch (GetStackType())
+ {
+ case svSingleRef:
+ {
+ FormulaConstTokenRef xT = PopToken();
+ ValidateRef( *xT->GetSingleRef());
+ ++nCount;
+ }
+ break;
+ case svDoubleRef:
+ {
+ FormulaConstTokenRef xT = PopToken();
+ ValidateRef( *xT->GetDoubleRef());
+ ++nCount;
+ }
+ break;
+ case svRefList:
+ {
+ FormulaConstTokenRef xT = PopToken();
+ ValidateRef( *(xT->GetRefList()));
+ nCount += xT->GetRefList()->size();
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ PushDouble( double(nCount));
+}
+
+void ScInterpreter::ScCurrency()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ OUString aStr;
+ double fDec;
+ if (nParamCount == 2)
+ {
+ fDec = ::rtl::math::approxFloor(GetDouble());
+ if (fDec < -15.0 || fDec > 15.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ else
+ fDec = 2.0;
+ double fVal = GetDouble();
+ double fFac;
+ if ( fDec != 0.0 )
+ fFac = pow( double(10), fDec );
+ else
+ fFac = 1.0;
+ if (fVal < 0.0)
+ fVal = ceil(fVal*fFac-0.5)/fFac;
+ else
+ fVal = floor(fVal*fFac+0.5)/fFac;
+ const Color* pColor = nullptr;
+ if ( fDec < 0.0 )
+ fDec = 0.0;
+ sal_uLong nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::CURRENCY,
+ ScGlobal::eLnge);
+ if ( static_cast<sal_uInt16>(fDec) != pFormatter->GetFormatPrecision( nIndex ) )
+ {
+ OUString sFormatString = pFormatter->GenerateFormat(
+ nIndex,
+ ScGlobal::eLnge,
+ true, // with thousands separator
+ false, // not red
+ static_cast<sal_uInt16>(fDec));// decimal places
+ if (!pFormatter->GetPreviewString(sFormatString,
+ fVal,
+ aStr,
+ &pColor,
+ ScGlobal::eLnge))
+ SetError(FormulaError::IllegalArgument);
+ }
+ else
+ {
+ pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
+ }
+ PushString(aStr);
+}
+
+void ScInterpreter::ScReplace()
+{
+ if ( !MustHaveParamCount( GetByte(), 4 ) )
+ return;
+
+ OUString aNewStr = GetString().getString();
+ sal_Int32 nCount = GetStringPositionArgument();
+ sal_Int32 nPos = GetStringPositionArgument();
+ OUString aOldStr = GetString().getString();
+ if (nPos < 1 || nCount < 0)
+ PushIllegalArgument();
+ else
+ {
+ sal_Int32 nLen = aOldStr.getLength();
+ if (nPos > nLen + 1)
+ nPos = nLen + 1;
+ if (nCount > nLen - nPos + 1)
+ nCount = nLen - nPos + 1;
+ sal_Int32 nIdx = 0;
+ sal_Int32 nCnt = 0;
+ while ( nIdx < nLen && nPos > nCnt + 1 )
+ {
+ aOldStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ sal_Int32 nStart = nIdx;
+ while ( nIdx < nLen && nPos + nCount - 1 > nCnt )
+ {
+ aOldStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ if ( CheckStringResultLen( aOldStr, aNewStr.getLength() - (nIdx - nStart) ) )
+ aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, aNewStr );
+ PushString( aOldStr );
+ }
+}
+
+void ScInterpreter::ScFixed()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
+ return;
+
+ OUString aStr;
+ double fDec;
+ bool bThousand;
+ if (nParamCount == 3)
+ bThousand = !GetBool(); // Param true: no thousands separator
+ else
+ bThousand = true;
+ if (nParamCount >= 2)
+ {
+ fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
+ if (fDec < -15.0 || fDec > 15.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ else
+ fDec = 2.0;
+ double fVal = GetDouble();
+ double fFac;
+ if ( fDec != 0.0 )
+ fFac = pow( double(10), fDec );
+ else
+ fFac = 1.0;
+ if (fVal < 0.0)
+ fVal = ceil(fVal*fFac-0.5)/fFac;
+ else
+ fVal = floor(fVal*fFac+0.5)/fFac;
+ const Color* pColor = nullptr;
+ if (fDec < 0.0)
+ fDec = 0.0;
+ sal_uLong nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ OUString sFormatString = pFormatter->GenerateFormat(
+ nIndex,
+ ScGlobal::eLnge,
+ bThousand, // with thousands separator
+ false, // not red
+ static_cast<sal_uInt16>(fDec));// decimal places
+ if (!pFormatter->GetPreviewString(sFormatString,
+ fVal,
+ aStr,
+ &pColor,
+ ScGlobal::eLnge))
+ PushIllegalArgument();
+ else
+ PushString(aStr);
+}
+
+void ScInterpreter::ScFind()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ sal_Int32 nCnt;
+ if (nParamCount == 3)
+ nCnt = GetDouble();
+ else
+ nCnt = 1;
+ OUString sStr = GetString().getString();
+ if (nCnt < 1 || nCnt > sStr.getLength())
+ PushNoValue();
+ else
+ {
+ sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1);
+ if (nPos == -1)
+ PushNoValue();
+ else
+ {
+ sal_Int32 nIdx = 0;
+ nCnt = 0;
+ while ( nIdx < nPos )
+ {
+ sStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ PushDouble( static_cast<double>(nCnt + 1) );
+ }
+ }
+}
+
+void ScInterpreter::ScExact()
+{
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ svl::SharedString s1 = GetString();
+ svl::SharedString s2 = GetString();
+ PushInt( int(s1.getData() == s2.getData()) );
+ }
+}
+
+void ScInterpreter::ScLeft()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ sal_Int32 n;
+ if (nParamCount == 2)
+ {
+ n = GetStringPositionArgument();
+ if (n < 0)
+ {
+ PushIllegalArgument();
+ return ;
+ }
+ }
+ else
+ n = 1;
+ OUString aStr = GetString().getString();
+ sal_Int32 nIdx = 0;
+ sal_Int32 nCnt = 0;
+ while ( nIdx < aStr.getLength() && n > nCnt++ )
+ aStr.iterateCodePoints( &nIdx );
+ aStr = aStr.copy( 0, nIdx );
+ PushString( aStr );
+}
+
+namespace {
+
+struct UBlockScript {
+ UBlockCode from;
+ UBlockCode to;
+};
+
+}
+
+const UBlockScript scriptList[] = {
+ {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
+ {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
+ {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
+ {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
+ {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
+ {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
+ {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
+ {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
+};
+static bool IsDBCS(sal_Unicode currentChar)
+{
+ // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
+ if( (currentChar == 0x005c || currentChar == 0x20ac) &&
+ (MsLangId::getConfiguredSystemLanguage() == LANGUAGE_JAPANESE) )
+ return true;
+ sal_uInt16 i;
+ bool bRet = false;
+ UBlockCode block = ublock_getCode(currentChar);
+ for ( i = 0; i < SAL_N_ELEMENTS(scriptList); i++) {
+ if (block <= scriptList[i].to) break;
+ }
+ bRet = (i < SAL_N_ELEMENTS(scriptList) && block >= scriptList[i].from);
+ return bRet;
+}
+static sal_Int32 lcl_getLengthB( std::u16string_view str, sal_Int32 nPos )
+{
+ sal_Int32 index = 0;
+ sal_Int32 length = 0;
+ while ( index < nPos )
+ {
+ if (IsDBCS(str[index]))
+ length += 2;
+ else
+ length++;
+ index++;
+ }
+ return length;
+}
+static sal_Int32 getLengthB(std::u16string_view str)
+{
+ if(str.empty())
+ return 0;
+ else
+ return lcl_getLengthB( str, str.size() );
+}
+void ScInterpreter::ScLenB()
+{
+ PushDouble( getLengthB(GetString().getString()) );
+}
+static OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
+{
+ if( n < getLengthB(rStr) )
+ {
+ OUStringBuffer aBuf(rStr);
+ sal_Int32 index = aBuf.getLength();
+ while(index-- >= 0)
+ {
+ if(0 == n)
+ {
+ aBuf.remove( 0, index + 1);
+ break;
+ }
+ if(-1 == n)
+ {
+ aBuf.remove( 0, index + 2 );
+ aBuf.insert( 0, " ");
+ break;
+ }
+ if(IsDBCS(aBuf[index]))
+ n -= 2;
+ else
+ n--;
+ }
+ return aBuf.makeStringAndClear();
+ }
+ return rStr;
+}
+void ScInterpreter::ScRightB()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ sal_Int32 n;
+ if (nParamCount == 2)
+ {
+ n = GetStringPositionArgument();
+ if (n < 0)
+ {
+ PushIllegalArgument();
+ return ;
+ }
+ }
+ else
+ n = 1;
+ OUString aStr(lcl_RightB(GetString().getString(), n));
+ PushString( aStr );
+}
+static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
+{
+ if( n < getLengthB(rStr) )
+ {
+ OUStringBuffer aBuf(rStr);
+ sal_Int32 index = -1;
+ while(index++ < aBuf.getLength())
+ {
+ if(0 == n)
+ {
+ aBuf.truncate(index);
+ break;
+ }
+ if(-1 == n)
+ {
+ aBuf.truncate( index - 1 );
+ aBuf.append(" ");
+ break;
+ }
+ if(IsDBCS(aBuf[index]))
+ n -= 2;
+ else
+ n--;
+ }
+ return aBuf.makeStringAndClear();
+ }
+ return rStr;
+}
+void ScInterpreter::ScLeftB()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ sal_Int32 n;
+ if (nParamCount == 2)
+ {
+ n = GetStringPositionArgument();
+ if (n < 0)
+ {
+ PushIllegalArgument();
+ return ;
+ }
+ }
+ else
+ n = 1;
+ OUString aStr(lcl_LeftB(GetString().getString(), n));
+ PushString( aStr );
+}
+void ScInterpreter::ScMidB()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ const sal_Int32 nCount = GetStringPositionArgument();
+ const sal_Int32 nStart = GetStringPositionArgument();
+ OUString aStr = GetString().getString();
+ if (nStart < 1 || nCount < 0)
+ PushIllegalArgument();
+ else
+ {
+
+ aStr = lcl_LeftB(aStr, nStart + nCount - 1);
+ sal_Int32 nCnt = getLengthB(aStr) - nStart + 1;
+ aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0));
+ PushString(aStr);
+ }
+}
+
+void ScInterpreter::ScReplaceB()
+{
+ if ( !MustHaveParamCount( GetByte(), 4 ) )
+ return;
+
+ OUString aNewStr = GetString().getString();
+ const sal_Int32 nCount = GetStringPositionArgument();
+ const sal_Int32 nPos = GetStringPositionArgument();
+ OUString aOldStr = GetString().getString();
+ int nLen = getLengthB( aOldStr );
+ if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen)
+ PushIllegalArgument();
+ else
+ {
+ // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
+ // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
+ OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 );
+ OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1);
+
+ PushString( aStr1 + aNewStr + aStr3 );
+ }
+}
+
+void ScInterpreter::ScFindB()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ sal_Int32 nStart;
+ if ( nParamCount == 3 )
+ nStart = GetStringPositionArgument();
+ else
+ nStart = 1;
+ OUString aStr = GetString().getString();
+ int nLen = getLengthB( aStr );
+ OUString asStr = GetString().getString();
+ int nsLen = getLengthB( asStr );
+ if ( nStart < 1 || nStart > nLen - nsLen + 1 )
+ PushIllegalArgument();
+ else
+ {
+ // create a string from sStr starting at nStart
+ OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 );
+ // search aBuf for asStr
+ sal_Int32 nPos = aBuf.indexOf( asStr, 0 );
+ if ( nPos == -1 )
+ PushNoValue();
+ else
+ {
+ // obtain byte value of nPos
+ int nBytePos = lcl_getLengthB( aBuf, nPos );
+ PushDouble( nBytePos + nStart );
+ }
+ }
+}
+
+void ScInterpreter::ScSearchB()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ sal_Int32 nStart;
+ if ( nParamCount == 3 )
+ {
+ nStart = GetStringPositionArgument();
+ if( nStart < 1 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ else
+ nStart = 1;
+ OUString aStr = GetString().getString();
+ sal_Int32 nLen = getLengthB( aStr );
+ OUString asStr = GetString().getString();
+ sal_Int32 nsLen = nStart - 1;
+ if( nsLen >= nLen )
+ PushNoValue();
+ else
+ {
+ // create a string from sStr starting at nStart
+ OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) );
+ // search aSubStr for asStr
+ sal_Int32 nPos = 0;
+ sal_Int32 nEndPos = aSubStr.getLength();
+ utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc );
+ utl::SearchParam sPar( asStr, eSearchType, false, '~', false );
+ utl::TextSearch sT( sPar, ScGlobal::getCharClass() );
+ if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) )
+ PushNoValue();
+ else
+ {
+ // obtain byte value of nPos
+ int nBytePos = lcl_getLengthB( aSubStr, nPos );
+ PushDouble( nBytePos + nStart );
+ }
+ }
+}
+
+void ScInterpreter::ScRight()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ sal_Int32 n;
+ if (nParamCount == 2)
+ {
+ n = GetStringPositionArgument();
+ if (n < 0)
+ {
+ PushIllegalArgument();
+ return ;
+ }
+ }
+ else
+ n = 1;
+ OUString aStr = GetString().getString();
+ sal_Int32 nLen = aStr.getLength();
+ if ( nLen <= n )
+ PushString( aStr );
+ else
+ {
+ sal_Int32 nIdx = nLen;
+ sal_Int32 nCnt = 0;
+ while ( nIdx > 0 && n > nCnt )
+ {
+ aStr.iterateCodePoints( &nIdx, -1 );
+ ++nCnt;
+ }
+ aStr = aStr.copy( nIdx, nLen - nIdx );
+ PushString( aStr );
+ }
+}
+
+void ScInterpreter::ScSearch()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ sal_Int32 nStart;
+ if (nParamCount == 3)
+ {
+ nStart = GetStringPositionArgument();
+ if( nStart < 1 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ else
+ nStart = 1;
+ OUString sStr = GetString().getString();
+ OUString SearchStr = GetString().getString();
+ sal_Int32 nPos = nStart - 1;
+ sal_Int32 nEndPos = sStr.getLength();
+ if( nPos >= nEndPos )
+ PushNoValue();
+ else
+ {
+ utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc );
+ utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false);
+ utl::TextSearch sT( sPar, ScGlobal::getCharClass() );
+ bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos);
+ if (!bBool)
+ PushNoValue();
+ else
+ {
+ sal_Int32 nIdx = 0;
+ sal_Int32 nCnt = 0;
+ while ( nIdx < nPos )
+ {
+ sStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ PushDouble( static_cast<double>(nCnt + 1) );
+ }
+ }
+}
+
+void ScInterpreter::ScRegex()
+{
+ const sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount( nParamCount, 2, 4))
+ return;
+
+ // Flags are supported only for replacement, search match flags can be
+ // individually and much more flexible set in the regular expression
+ // pattern using (?ismwx-ismwx)
+ bool bGlobalReplacement = false;
+ sal_Int32 nOccurrence = 1; // default first occurrence, if any
+ if (nParamCount == 4)
+ {
+ // Argument can be either string or double.
+ double fOccurrence;
+ svl::SharedString aFlagsString;
+ bool bDouble;
+ if (!IsMissing())
+ bDouble = GetDoubleOrString( fOccurrence, aFlagsString);
+ else
+ {
+ // For an omitted argument keep the default.
+ PopError();
+ bDouble = true;
+ fOccurrence = nOccurrence;
+ }
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ if (bDouble)
+ {
+ if (!CheckStringPositionArgument( fOccurrence))
+ {
+ PushError( FormulaError::IllegalArgument);
+ return;
+ }
+ nOccurrence = static_cast<sal_Int32>(fOccurrence);
+ }
+ else
+ {
+ const OUString aFlags( aFlagsString.getString());
+ // Empty flags string is valid => no flag set.
+ if (aFlags.getLength() > 1)
+ {
+ // Only one flag supported.
+ PushIllegalArgument();
+ return;
+ }
+ if (aFlags.getLength() == 1)
+ {
+ if (aFlags.indexOf('g') >= 0)
+ bGlobalReplacement = true;
+ else
+ {
+ // Unsupported flag.
+ PushIllegalArgument();
+ return;
+ }
+ }
+ }
+ }
+
+ bool bReplacement = false;
+ OUString aReplacement;
+ if (nParamCount >= 3)
+ {
+ // A missing argument is not an empty string to replace the match.
+ // nOccurrence==0 forces no replacement, so simply discard the
+ // argument.
+ if (IsMissing() || nOccurrence == 0)
+ PopError();
+ else
+ {
+ aReplacement = GetString().getString();
+ bReplacement = true;
+ }
+ }
+ // If bGlobalReplacement==true and bReplacement==false then
+ // bGlobalReplacement is silently ignored.
+
+ const OUString aExpression = GetString().getString();
+ const OUString aText = GetString().getString();
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ // 0-th match or replacement is none, return original string early.
+ if (nOccurrence == 0)
+ {
+ PushString( aText);
+ return;
+ }
+
+ const icu::UnicodeString aIcuExpression(
+ false, reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength());
+ UErrorCode status = U_ZERO_ERROR;
+ icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status);
+ if (U_FAILURE(status))
+ {
+ // Invalid regex.
+ PushIllegalArgument();
+ return;
+ }
+ // Guard against pathological patterns, limit steps of engine, see
+ // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
+ aRegexMatcher.setTimeLimit( 23*1000, status);
+
+ const icu::UnicodeString aIcuText(false, reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength());
+ aRegexMatcher.reset( aIcuText);
+
+ if (!bReplacement)
+ {
+ // Find n-th occurrence.
+ sal_Int32 nCount = 0;
+ while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence)
+ ;
+ if (U_FAILURE(status))
+ {
+ // Some error.
+ PushIllegalArgument();
+ return;
+ }
+ // n-th match found?
+ if (nCount != nOccurrence)
+ {
+ PushError( FormulaError::NotAvailable);
+ return;
+ }
+ // Extract matched text.
+ icu::UnicodeString aMatch( aRegexMatcher.group( status));
+ if (U_FAILURE(status))
+ {
+ // Some error.
+ PushIllegalArgument();
+ return;
+ }
+ OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length());
+ PushString( aResult);
+ return;
+ }
+
+ const icu::UnicodeString aIcuReplacement(
+ false, reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength());
+ icu::UnicodeString aReplaced;
+ if (bGlobalReplacement)
+ // Replace all occurrences of match with replacement.
+ aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status);
+ else if (nOccurrence == 1)
+ // Replace first occurrence of match with replacement.
+ aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status);
+ else
+ {
+ // Replace n-th occurrence of match with replacement.
+ sal_Int32 nCount = 0;
+ while (aRegexMatcher.find(status) && U_SUCCESS(status))
+ {
+ // XXX NOTE: After several RegexMatcher::find() the
+ // RegexMatcher::appendReplacement() still starts at the
+ // beginning (or after the last appendReplacement() position
+ // which is none here) and copies the original text up to the
+ // current found match and then replaces the found match.
+ if (++nCount == nOccurrence)
+ {
+ aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status);
+ break;
+ }
+ }
+ aRegexMatcher.appendTail( aReplaced);
+ }
+ if (U_FAILURE(status))
+ {
+ // Some error, e.g. extraneous $1 without group.
+ PushIllegalArgument();
+ return;
+ }
+ OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length());
+ PushString( aResult);
+}
+
+void ScInterpreter::ScMid()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ const sal_Int32 nSubLen = GetStringPositionArgument();
+ const sal_Int32 nStart = GetStringPositionArgument();
+ OUString aStr = GetString().getString();
+ if ( nStart < 1 || nSubLen < 0 )
+ PushIllegalArgument();
+ else if (nStart > kScInterpreterMaxStrLen || nSubLen > kScInterpreterMaxStrLen)
+ PushError(FormulaError::StringOverflow);
+ else
+ {
+ sal_Int32 nLen = aStr.getLength();
+ sal_Int32 nIdx = 0;
+ sal_Int32 nCnt = 0;
+ while ( nIdx < nLen && nStart - 1 > nCnt )
+ {
+ aStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ sal_Int32 nIdx0 = nIdx; //start position
+
+ while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt )
+ {
+ aStr.iterateCodePoints( &nIdx );
+ ++nCnt;
+ }
+ aStr = aStr.copy( nIdx0, nIdx - nIdx0 );
+ PushString( aStr );
+ }
+}
+
+void ScInterpreter::ScText()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ OUString sFormatString = GetString().getString();
+ svl::SharedString aStr;
+ bool bString = false;
+ double fVal = 0.0;
+ switch (GetStackType())
+ {
+ case svError:
+ PopError();
+ break;
+ case svDouble:
+ fVal = PopDouble();
+ break;
+ default:
+ {
+ FormulaConstTokenRef xTok( PopToken());
+ if (nGlobalError == FormulaError::NONE)
+ {
+ PushTokenRef( xTok);
+ // Temporarily override the ConvertStringToValue()
+ // error for GetCellValue() / GetCellValueOrZero()
+ FormulaError nSErr = mnStringNoValueError;
+ mnStringNoValueError = FormulaError::NotNumericString;
+ fVal = GetDouble();
+ mnStringNoValueError = nSErr;
+ if (nGlobalError == FormulaError::NotNumericString)
+ {
+ // Not numeric.
+ nGlobalError = FormulaError::NONE;
+ PushTokenRef( xTok);
+ aStr = GetString();
+ bString = true;
+ }
+ }
+ }
+ }
+ if (nGlobalError != FormulaError::NONE)
+ PushError( nGlobalError);
+ else if (sFormatString.isEmpty())
+ {
+ // Mimic the Excel behaviour that
+ // * anything numeric returns an empty string
+ // * text convertible to numeric returns an empty string
+ // * any other text returns that text
+ // Conversion was detected above.
+ if (bString)
+ PushString( aStr);
+ else
+ PushString( OUString());
+ }
+ else
+ {
+ OUString aResult;
+ const Color* pColor = nullptr;
+ LanguageType eCellLang;
+ const ScPatternAttr* pPattern = mrDoc.GetPattern(
+ aPos.Col(), aPos.Row(), aPos.Tab() );
+ if ( pPattern )
+ eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue();
+ else
+ eCellLang = ScGlobal::eLnge;
+ if (bString)
+ {
+ if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(),
+ aResult, &pColor, eCellLang))
+ PushIllegalArgument();
+ else
+ PushString( aResult);
+ }
+ else
+ {
+ if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal,
+ aResult, &pColor, eCellLang))
+ PushIllegalArgument();
+ else
+ PushString( aResult);
+ }
+ }
+}
+
+void ScInterpreter::ScSubstitute()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
+ return;
+
+ sal_Int32 nCnt;
+ if (nParamCount == 4)
+ {
+ nCnt = GetStringPositionArgument();
+ if (nCnt < 1)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ else
+ nCnt = 0;
+ OUString sNewStr = GetString().getString();
+ OUString sOldStr = GetString().getString();
+ OUString sStr = GetString().getString();
+ sal_Int32 nPos = 0;
+ sal_Int32 nCount = 0;
+ std::optional<OUStringBuffer> oResult;
+ for (sal_Int32 nEnd = sStr.indexOf(sOldStr); nEnd >= 0; nEnd = sStr.indexOf(sOldStr, nEnd))
+ {
+ if (nCnt == 0 || ++nCount == nCnt) // Found a replacement cite
+ {
+ if (!oResult) // Only allocate buffer when needed
+ oResult.emplace(sStr.getLength() + sNewStr.getLength() - sOldStr.getLength());
+
+ oResult->append(sStr.subView(nPos, nEnd - nPos)); // Copy leading unchanged text
+ if (!CheckStringResultLen(*oResult, sNewStr.getLength()))
+ return PushError(GetError());
+ oResult->append(sNewStr); // Copy the replacement
+ nPos = nEnd + sOldStr.getLength();
+ if (nCnt > 0) // Found the single replacement site - end the loop
+ break;
+ }
+ nEnd += sOldStr.getLength();
+ }
+ if (oResult) // If there were prior replacements, copy the rest, otherwise use original
+ oResult->append(sStr.subView(nPos, sStr.getLength() - nPos));
+ PushString(oResult ? oResult->makeStringAndClear() : sStr);
+}
+
+void ScInterpreter::ScRept()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ sal_Int32 nCnt = GetStringPositionArgument();
+ OUString aStr = GetString().getString();
+ if (nCnt < 0)
+ PushIllegalArgument();
+ else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen)
+ {
+ PushError( FormulaError::StringOverflow );
+ }
+ else if (nCnt == 0)
+ PushString( OUString() );
+ else
+ {
+ const sal_Int32 nLen = aStr.getLength();
+ OUStringBuffer aRes(nCnt*nLen);
+ while( nCnt-- )
+ aRes.append(aStr);
+ PushString( aRes.makeStringAndClear() );
+ }
+}
+
+void ScInterpreter::ScConcat()
+{
+ sal_uInt8 nParamCount = GetByte();
+
+ //reverse order of parameter stack to simplify processing
+ ReverseStack(nParamCount);
+
+ OUStringBuffer aRes;
+ while( nParamCount-- > 0)
+ {
+ OUString aStr = GetString().getString();
+ if (CheckStringResultLen(aRes, aStr.getLength()))
+ aRes.append(aStr);
+ else
+ break;
+ }
+ PushString( aRes.makeStringAndClear() );
+}
+
+FormulaError ScInterpreter::GetErrorType()
+{
+ FormulaError nErr;
+ FormulaError nOldError = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+ switch ( GetStackType() )
+ {
+ case svRefList :
+ {
+ FormulaConstTokenRef x = PopToken();
+ if (nGlobalError != FormulaError::NONE)
+ nErr = nGlobalError;
+ else
+ {
+ const ScRefList* pRefList = x->GetRefList();
+ size_t n = pRefList->size();
+ if (!n)
+ nErr = FormulaError::NoRef;
+ else if (n > 1)
+ nErr = FormulaError::NoValue;
+ else
+ {
+ ScRange aRange;
+ DoubleRefToRange( (*pRefList)[0], aRange);
+ if (nGlobalError != FormulaError::NONE)
+ nErr = nGlobalError;
+ else
+ {
+ ScAddress aAdr;
+ if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
+ nErr = mrDoc.GetErrCode( aAdr );
+ else
+ nErr = nGlobalError;
+ }
+ }
+ }
+ }
+ break;
+ case svDoubleRef :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if ( nGlobalError != FormulaError::NONE )
+ nErr = nGlobalError;
+ else
+ {
+ ScAddress aAdr;
+ if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
+ nErr = mrDoc.GetErrCode( aAdr );
+ else
+ nErr = nGlobalError;
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError != FormulaError::NONE )
+ nErr = nGlobalError;
+ else
+ nErr = mrDoc.GetErrCode( aAdr );
+ }
+ break;
+ default:
+ PopError();
+ nErr = nGlobalError;
+ }
+ nGlobalError = nOldError;
+ return nErr;
+}
+
+void ScInterpreter::ScErrorType()
+{
+ FormulaError nErr = GetErrorType();
+ if ( nErr != FormulaError::NONE )
+ {
+ nGlobalError = FormulaError::NONE;
+ PushDouble( static_cast<double>(nErr) );
+ }
+ else
+ {
+ PushNA();
+ }
+}
+
+void ScInterpreter::ScErrorType_ODF()
+{
+ FormulaError nErr = GetErrorType();
+ sal_uInt16 nErrType;
+
+ switch ( nErr )
+ {
+ case FormulaError::NoCode : // #NULL!
+ nErrType = 1;
+ break;
+ case FormulaError::DivisionByZero : // #DIV/0!
+ nErrType = 2;
+ break;
+ case FormulaError::NoValue : // #VALUE!
+ nErrType = 3;
+ break;
+ case FormulaError::NoRef : // #REF!
+ nErrType = 4;
+ break;
+ case FormulaError::NoName : // #NAME?
+ nErrType = 5;
+ break;
+ case FormulaError::IllegalFPOperation : // #NUM!
+ nErrType = 6;
+ break;
+ case FormulaError::NotAvailable : // #N/A
+ nErrType = 7;
+ break;
+ /*
+ #GETTING_DATA is a message that can appear in Excel when a large or
+ complex worksheet is being calculated. In Excel 2007 and newer,
+ operations are grouped so more complicated cells may finish after
+ earlier ones do. While the calculations are still processing, the
+ unfinished cells may display #GETTING_DATA.
+ Because the message is temporary and disappears when the calculations
+ complete, this isn’t a true error.
+ No calc error code known (yet).
+
+ case : // GETTING_DATA
+ nErrType = 8;
+ break;
+ */
+ default :
+ nErrType = 0;
+ break;
+ }
+
+ if ( nErrType )
+ {
+ nGlobalError =FormulaError::NONE;
+ PushDouble( nErrType );
+ }
+ else
+ PushNA();
+}
+
+static bool MayBeRegExp( std::u16string_view rStr )
+{
+ if ( rStr.empty() || (rStr.size() == 1 && rStr[0] != '.') )
+ return false; // single meta characters can not be a regexp
+ // First two characters are wildcard '?' and '*' characters.
+ std::u16string_view cre(u"?*+.[]^$\\<>()|");
+ return rStr.find_first_of(cre) != std::u16string_view::npos;
+}
+
+static bool MayBeWildcard( std::u16string_view rStr )
+{
+ // Wildcards with '~' escape, if there are no wildcards then an escaped
+ // character does not make sense, but it modifies the search pattern in an
+ // Excel compatible wildcard search...
+ std::u16string_view cw(u"*?~");
+ return rStr.find_first_of(cw) != std::u16string_view::npos;
+}
+
+utl::SearchParam::SearchType ScInterpreter::DetectSearchType( std::u16string_view rStr, const ScDocument& rDoc )
+{
+ const auto eType = rDoc.GetDocOptions().GetFormulaSearchType();
+ if ((eType == utl::SearchParam::SearchType::Wildcard && MayBeWildcard(rStr))
+ || (eType == utl::SearchParam::SearchType::Regexp && MayBeRegExp(rStr)))
+ return eType;
+ return utl::SearchParam::SearchType::Normal;
+}
+
+static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, ScInterpreterContext& rContext,
+ const ScQueryParam & rParam, const ScQueryEntry & rEntry, const ScFormulaCell* cell,
+ const ScComplexRefData* refData )
+{
+ if (rEntry.eOp != SC_EQUAL)
+ {
+ // range lookup <= or >=
+ SCCOL nCol;
+ SCROW nRow;
+ ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
+ if( aCellIter.FindEqualOrSortedLastInRange( nCol, nRow ))
+ {
+ o_rResultPos.SetCol( nCol);
+ o_rResultPos.SetRow( nRow);
+ return true;
+ }
+ }
+ else // EQUAL
+ {
+ if( ScQueryCellIteratorSortedCache::CanBeUsed( rDoc, rParam, rParam.nTab, cell, refData, rContext ))
+ {
+ ScQueryCellIteratorSortedCache aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
+ if (aCellIter.GetFirst())
+ {
+ o_rResultPos.SetCol( aCellIter.GetCol());
+ o_rResultPos.SetRow( aCellIter.GetRow());
+ return true;
+ }
+ }
+ else
+ {
+ ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
+ if (aCellIter.GetFirst())
+ {
+ o_rResultPos.SetCol( aCellIter.GetCol());
+ o_rResultPos.SetRow( aCellIter.GetRow());
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// tdf#121052:
+// =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
+// [SearchCriterion] is the value searched for in the first column of the array.
+// [RangeArray] is the reference, which is to comprise at least two columns.
+// [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
+//
+// Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
+// Value referenced by [SearchCriterion] is empty.
+// lcl_getPrevRowWithEmptyValueLookup() performs following checks:
+// - if we run query with "exact match" mode (i.e. VLOOKUP)
+// - and if we already have the same lookup done before but for another row
+// which is also had empty [SearchCriterion]
+//
+// then
+// we could say, that for current row we could reuse results of the cached call which was done for the row2
+// In this case we return row index, which is >= 0.
+//
+// Elsewhere
+// -1 is returned, which will lead to default behavior =>
+// complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
+//
+// This method was added only for speed up to avoid several useless complete
+// lookups inside [RangeArray] for searching empty strings.
+//
+static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache,
+ const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam)
+{
+ // is lookup value empty?
+ const ScQueryEntry& rEntry = rParam.GetEntry(0);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (! rItem.maString.getString().isEmpty())
+ return -1; // not found
+
+ // try to find the row index for which we have already performed lookup
+ // and have some result of it inside cache
+ return rCache.lookup( rCriteria );
+}
+
+bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
+ const ScQueryParam & rParam, const ScComplexRefData* refData ) const
+{
+ bool bFound = false;
+ const ScQueryEntry& rEntry = rParam.GetEntry(0);
+ bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
+ OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match");
+ // At least all volatile functions that generate indirect references have
+ // to force non-cached lookup.
+ /* TODO: We could further classify volatile functions into reference
+ * generating and not reference generating functions to have to force less
+ * direct lookups here. We could even further attribute volatility per
+ * parameter so it would affect only the lookup range parameter. */
+ if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE)
+ bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell, refData);
+ else
+ {
+ ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
+ rParam.nCol2, rParam.nRow2, rParam.nTab);
+ ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext );
+ ScLookupCache::QueryCriteria aCriteria( rEntry);
+ ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
+ aCriteria, aPos);
+
+ // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
+ // This check was added only for speed up to avoid several useless complete
+ // lookups inside [RangeArray] for searching empty strings.
+ if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery())
+ {
+ const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam);
+ if (nPrevRowWithEmptyValueLookup >= 0)
+ {
+ // make the same lookup using cache with different row index
+ // (this lookup was already cached)
+ ScAddress aPosPrev(aPos);
+ aPosPrev.SetRow(nPrevRowWithEmptyValueLookup);
+
+ eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev );
+ }
+ }
+
+ switch (eCacheResult)
+ {
+ case ScLookupCache::NOT_CACHED :
+ case ScLookupCache::CRITERIA_DIFFERENT :
+ bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell, refData);
+ if (eCacheResult == ScLookupCache::NOT_CACHED)
+ rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
+ break;
+ case ScLookupCache::FOUND :
+ bFound = true;
+ break;
+ case ScLookupCache::NOT_AVAILABLE :
+ ; // nothing, bFound remains FALSE
+ break;
+ }
+ }
+ return bFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx
new file mode 100644
index 0000000000..61f88d638a
--- /dev/null
+++ b/sc/source/core/tool/interpr2.cxx
@@ -0,0 +1,3727 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <interpre.hxx>
+
+#include <comphelper/string.hxx>
+#include <o3tl/float_int_conversion.hxx>
+#include <o3tl/string_view.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <tools/duration.hxx>
+#include <sal/macros.h>
+#include <osl/diagnose.h>
+
+#include <sc.hrc>
+#include <ddelink.hxx>
+#include <scmatrix.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <docsh.hxx>
+#include <unitconv.hxx>
+#include <hints.hxx>
+#include <dpobject.hxx>
+#include <tokenarray.hxx>
+#include <globalnames.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <dpcache.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+
+#include <string.h>
+
+using ::std::vector;
+using namespace com::sun::star;
+using namespace formula;
+
+#define SCdEpsilon 1.0E-7
+
+// Date and Time
+
+double ScInterpreter::GetDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
+ bool bStrict )
+{
+ if ( nYear < 100 && !bStrict )
+ nYear = pFormatter->ExpandTwoDigitYear( nYear );
+ // Do not use a default Date ctor here because it asks system time with a
+ // performance penalty.
+ sal_Int16 nY, nM, nD;
+ if (bStrict)
+ {
+ nY = nYear;
+ nM = nMonth;
+ nD = nDay;
+ }
+ else
+ {
+ if (nMonth > 0)
+ {
+ nY = nYear + (nMonth-1) / 12;
+ nM = ((nMonth-1) % 12) + 1;
+ }
+ else
+ {
+ nY = nYear + (nMonth-12) / 12;
+ nM = 12 - (-nMonth) % 12;
+ }
+ nD = 1;
+ }
+ Date aDate( nD, nM, nY);
+ if (!bStrict)
+ aDate.AddDays( nDay - 1 );
+ if (aDate.IsValidAndGregorian())
+ return static_cast<double>(aDate - pFormatter->GetNullDate());
+ else
+ {
+ SetError(FormulaError::NoValue);
+ return 0;
+ }
+}
+
+void ScInterpreter::ScGetActDate()
+{
+ nFuncFmtType = SvNumFormatType::DATE;
+ Date aActDate( Date::SYSTEM );
+ tools::Long nDiff = aActDate - pFormatter->GetNullDate();
+ PushDouble(static_cast<double>(nDiff));
+}
+
+void ScInterpreter::ScGetActTime()
+{
+ nFuncFmtType = SvNumFormatType::DATETIME;
+ DateTime aActTime( DateTime::SYSTEM );
+ tools::Long nDiff = aActTime - pFormatter->GetNullDate();
+ double fTime = aActTime.GetHour() / static_cast<double>(::tools::Time::hourPerDay) +
+ aActTime.GetMin() / static_cast<double>(::tools::Time::minutePerDay) +
+ aActTime.GetSec() / static_cast<double>(::tools::Time::secondPerDay) +
+ aActTime.GetNanoSec() / static_cast<double>(::tools::Time::nanoSecPerDay);
+ PushDouble( static_cast<double>(nDiff) + fTime );
+}
+
+void ScInterpreter::ScGetYear()
+{
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+ PushDouble( static_cast<double>(aDate.GetYear()) );
+}
+
+void ScInterpreter::ScGetMonth()
+{
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+ PushDouble( static_cast<double>(aDate.GetMonth()) );
+}
+
+void ScInterpreter::ScGetDay()
+{
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+ PushDouble(static_cast<double>(aDate.GetDay()));
+}
+
+void ScInterpreter::ScGetMin()
+{
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( GetDouble(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
+ PushDouble( nMinute);
+}
+
+void ScInterpreter::ScGetSec()
+{
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( GetDouble(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
+ if ( fFractionOfSecond >= 0.5 )
+ nSecond = ( nSecond + 1 ) % 60;
+ PushDouble( nSecond );
+
+}
+
+void ScInterpreter::ScGetHour()
+{
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( GetDouble(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
+ PushDouble( nHour);
+}
+
+void ScInterpreter::ScGetDateValue()
+{
+ OUString aInputString = GetString().getString();
+ sal_uInt32 nFIndex = 0; // for a default country/language
+ double fVal;
+ if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
+ {
+ SvNumFormatType eType = pFormatter->GetType(nFIndex);
+ if (eType == SvNumFormatType::DATE || eType == SvNumFormatType::DATETIME)
+ {
+ nFuncFmtType = SvNumFormatType::DATE;
+ PushDouble(::rtl::math::approxFloor(fVal));
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScGetDayOfWeek()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ sal_Int16 nFlag;
+ if (nParamCount == 2)
+ nFlag = GetInt16();
+ else
+ nFlag = 1;
+
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+ int nVal = static_cast<int>(aDate.GetDayOfWeek()); // MONDAY = 0
+ switch (nFlag)
+ {
+ case 1: // Sunday = 1
+ if (nVal == 6)
+ nVal = 1;
+ else
+ nVal += 2;
+ break;
+ case 2: // Monday = 1
+ nVal += 1;
+ break;
+ case 3: // Monday = 0
+ ; // nothing
+ break;
+ case 11: // Monday = 1
+ case 12: // Tuesday = 1
+ case 13: // Wednesday = 1
+ case 14: // Thursday = 1
+ case 15: // Friday = 1
+ case 16: // Saturday = 1
+ case 17: // Sunday = 1
+ if (nVal < nFlag - 11) // x = nFlag - 11 = 0,1,2,3,4,5,6
+ nVal += 19 - nFlag; // nVal += (8 - (nFlag - 11) = 8 - x = 8,7,6,5,4,3,2)
+ else
+ nVal -= nFlag - 12; // nVal -= ((nFlag - 11) - 1 = x - 1 = -1,0,1,2,3,4,5)
+ break;
+ default:
+ SetError( FormulaError::IllegalArgument);
+ }
+ PushInt( nVal );
+}
+
+void ScInterpreter::ScWeeknumOOo()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ sal_Int16 nFlag = GetInt16();
+
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+ PushInt( static_cast<int>(aDate.GetWeekOfYear( nFlag == 1 ? SUNDAY : MONDAY )));
+ }
+}
+
+void ScInterpreter::ScGetWeekOfYear()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ sal_Int16 nFlag = ( nParamCount == 1 ) ? 1 : GetInt16();
+
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+
+ sal_Int32 nMinimumNumberOfDaysInWeek;
+ DayOfWeek eFirstDayOfWeek;
+ switch ( nFlag )
+ {
+ case 1 :
+ eFirstDayOfWeek = SUNDAY;
+ nMinimumNumberOfDaysInWeek = 1;
+ break;
+ case 2 :
+ eFirstDayOfWeek = MONDAY;
+ nMinimumNumberOfDaysInWeek = 1;
+ break;
+ case 11 :
+ case 12 :
+ case 13 :
+ case 14 :
+ case 15 :
+ case 16 :
+ case 17 :
+ eFirstDayOfWeek = static_cast<DayOfWeek>( nFlag - 11 ); // MONDAY := 0
+ nMinimumNumberOfDaysInWeek = 1; //the week containing January 1 is week 1
+ break;
+ case 21 :
+ case 150 :
+ // ISO 8601
+ eFirstDayOfWeek = MONDAY;
+ nMinimumNumberOfDaysInWeek = 4;
+ break;
+ default :
+ PushIllegalArgument();
+ return;
+ }
+ PushInt( static_cast<int>(aDate.GetWeekOfYear( eFirstDayOfWeek, nMinimumNumberOfDaysInWeek )) );
+}
+
+void ScInterpreter::ScGetIsoWeekOfYear()
+{
+ if ( MustHaveParamCount( GetByte(), 1 ) )
+ {
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays( GetFloor32());
+ PushInt( static_cast<int>(aDate.GetWeekOfYear()) );
+ }
+}
+
+void ScInterpreter::ScEasterSunday()
+{
+ nFuncFmtType = SvNumFormatType::DATE;
+ if ( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ sal_Int16 nYear = GetInt16();
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ if ( nYear < 100 )
+ nYear = pFormatter->ExpandTwoDigitYear( nYear );
+ if (nYear < 1583 || nYear > 9956)
+ {
+ // Valid Gregorian and maximum year constraints not met.
+ PushIllegalArgument();
+ return;
+ }
+ // don't worry, be happy :)
+ int B,C,D,E,F,G,H,I,K,L,M,N,O;
+ N = nYear % 19;
+ B = int(nYear / 100);
+ C = nYear % 100;
+ D = int(B / 4);
+ E = B % 4;
+ F = int((B + 8) / 25);
+ G = int((B - F + 1) / 3);
+ H = (19 * N + B - D - G + 15) % 30;
+ I = int(C / 4);
+ K = C % 4;
+ L = (32 + 2 * E + 2 * I - H - K) % 7;
+ M = int((N + 11 * H + 22 * L) / 451);
+ O = H + L - 7 * M + 114;
+ sal_Int16 nDay = sal::static_int_cast<sal_Int16>( O % 31 + 1 );
+ sal_Int16 nMonth = sal::static_int_cast<sal_Int16>( int(O / 31) );
+ PushDouble( GetDateSerial( nYear, nMonth, nDay, true ) );
+}
+
+FormulaError ScInterpreter::GetWeekendAndHolidayMasks(
+ const sal_uInt8 nParamCount, const sal_uInt32 nNullDate, vector< double >& rSortArray,
+ bool bWeekendMask[ 7 ] )
+{
+ if ( nParamCount == 4 )
+ {
+ vector< double > nWeekendDays;
+ GetNumberSequenceArray( 1, nWeekendDays, false );
+ if ( nGlobalError != FormulaError::NONE )
+ return nGlobalError;
+ else
+ {
+ if ( nWeekendDays.size() != 7 )
+ return FormulaError::IllegalArgument;
+
+ // Weekend days defined by string, Sunday...Saturday
+ for ( int i = 0; i < 7; i++ )
+ bWeekendMask[ i ] = static_cast<bool>(nWeekendDays[ ( i == 6 ? 0 : i + 1 ) ]);
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < 7; i++ )
+ bWeekendMask[ i] = false;
+
+ bWeekendMask[ SATURDAY ] = true;
+ bWeekendMask[ SUNDAY ] = true;
+ }
+
+ if ( nParamCount >= 3 )
+ {
+ GetSortArray( 1, rSortArray, nullptr, true, true );
+ size_t nMax = rSortArray.size();
+ for ( size_t i = 0; i < nMax; i++ )
+ rSortArray.at( i ) = ::rtl::math::approxFloor( rSortArray.at( i ) ) + nNullDate;
+ }
+
+ return nGlobalError;
+}
+
+FormulaError ScInterpreter::GetWeekendAndHolidayMasks_MS(
+ const sal_uInt8 nParamCount, const sal_uInt32 nNullDate, vector< double >& rSortArray,
+ bool bWeekendMask[ 7 ], bool bWorkdayFunction )
+{
+ FormulaError nErr = FormulaError::NONE;
+ OUString aWeekendDays;
+ if ( nParamCount == 4 )
+ {
+ GetSortArray( 1, rSortArray, nullptr, true, true );
+ size_t nMax = rSortArray.size();
+ for ( size_t i = 0; i < nMax; i++ )
+ rSortArray.at( i ) = ::rtl::math::approxFloor( rSortArray.at( i ) ) + nNullDate;
+ }
+
+ if ( nParamCount >= 3 )
+ {
+ if ( IsMissing() )
+ Pop();
+ else
+ {
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ case svExternalDoubleRef :
+ return FormulaError::NoValue;
+
+ default :
+ {
+ double fDouble;
+ svl::SharedString aSharedString;
+ bool bDouble = GetDoubleOrString( fDouble, aSharedString);
+ if ( bDouble )
+ {
+ if ( fDouble >= 1.0 && fDouble <= 17 )
+ aWeekendDays = OUString::number( fDouble );
+ else
+ return FormulaError::NoValue;
+ }
+ else
+ {
+ if ( aSharedString.isEmpty() || aSharedString.getLength() != 7 ||
+ ( bWorkdayFunction && aSharedString.getString() == "1111111" ) )
+ return FormulaError::NoValue;
+ else
+ aWeekendDays = aSharedString.getString();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ for ( int i = 0; i < 7; i++ )
+ bWeekendMask[ i] = false;
+
+ if ( aWeekendDays.isEmpty() )
+ {
+ bWeekendMask[ SATURDAY ] = true;
+ bWeekendMask[ SUNDAY ] = true;
+ }
+ else
+ {
+ switch ( aWeekendDays.getLength() )
+ {
+ case 1 :
+ // Weekend days defined by code
+ switch ( aWeekendDays[ 0 ] )
+ {
+ case '1' : bWeekendMask[ SATURDAY ] = true; bWeekendMask[ SUNDAY ] = true; break;
+ case '2' : bWeekendMask[ SUNDAY ] = true; bWeekendMask[ MONDAY ] = true; break;
+ case '3' : bWeekendMask[ MONDAY ] = true; bWeekendMask[ TUESDAY ] = true; break;
+ case '4' : bWeekendMask[ TUESDAY ] = true; bWeekendMask[ WEDNESDAY ] = true; break;
+ case '5' : bWeekendMask[ WEDNESDAY ] = true; bWeekendMask[ THURSDAY ] = true; break;
+ case '6' : bWeekendMask[ THURSDAY ] = true; bWeekendMask[ FRIDAY ] = true; break;
+ case '7' : bWeekendMask[ FRIDAY ] = true; bWeekendMask[ SATURDAY ] = true; break;
+ default : nErr = FormulaError::IllegalArgument; break;
+ }
+ break;
+ case 2 :
+ // Weekend day defined by code
+ if ( aWeekendDays[ 0 ] == '1' )
+ {
+ switch ( aWeekendDays[ 1 ] )
+ {
+ case '1' : bWeekendMask[ SUNDAY ] = true; break;
+ case '2' : bWeekendMask[ MONDAY ] = true; break;
+ case '3' : bWeekendMask[ TUESDAY ] = true; break;
+ case '4' : bWeekendMask[ WEDNESDAY ] = true; break;
+ case '5' : bWeekendMask[ THURSDAY ] = true; break;
+ case '6' : bWeekendMask[ FRIDAY ] = true; break;
+ case '7' : bWeekendMask[ SATURDAY ] = true; break;
+ default : nErr = FormulaError::IllegalArgument; break;
+ }
+ }
+ else
+ nErr = FormulaError::IllegalArgument;
+ break;
+ case 7 :
+ // Weekend days defined by string
+ for ( int i = 0; i < 7 && nErr == FormulaError::NONE; i++ )
+ {
+ switch ( aWeekendDays[ i ] )
+ {
+ case '0' : bWeekendMask[ i ] = false; break;
+ case '1' : bWeekendMask[ i ] = true; break;
+ default : nErr = FormulaError::IllegalArgument; break;
+ }
+ }
+ break;
+ default :
+ nErr = FormulaError::IllegalArgument;
+ break;
+ }
+ }
+ return nErr;
+}
+
+void ScInterpreter::ScNetWorkdays( bool bOOXML_Version )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 4 ) )
+ return;
+
+ vector<double> nSortArray;
+ bool bWeekendMask[ 7 ];
+ const Date& rNullDate = pFormatter->GetNullDate();
+ sal_uInt32 nNullDate = Date::DateToDays( rNullDate.GetDay(), rNullDate.GetMonth(), rNullDate.GetYear() );
+ FormulaError nErr;
+ if ( bOOXML_Version )
+ {
+ nErr = GetWeekendAndHolidayMasks_MS( nParamCount, nNullDate,
+ nSortArray, bWeekendMask, false );
+ }
+ else
+ {
+ nErr = GetWeekendAndHolidayMasks( nParamCount, nNullDate,
+ nSortArray, bWeekendMask );
+ }
+ if ( nErr != FormulaError::NONE )
+ PushError( nErr );
+ else
+ {
+ sal_uInt32 nDate2 = GetUInt32();
+ sal_uInt32 nDate1 = GetUInt32();
+ if (nGlobalError != FormulaError::NONE || (nDate1 > SAL_MAX_UINT32 - nNullDate) || nDate2 > (SAL_MAX_UINT32 - nNullDate))
+ {
+ PushIllegalArgument();
+ return;
+ }
+ nDate2 += nNullDate;
+ nDate1 += nNullDate;
+
+ sal_Int32 nCnt = 0;
+ size_t nRef = 0;
+ bool bReverse = ( nDate1 > nDate2 );
+ if ( bReverse )
+ std::swap( nDate1, nDate2 );
+ size_t nMax = nSortArray.size();
+ while ( nDate1 <= nDate2 )
+ {
+ if ( !bWeekendMask[ GetDayOfWeek( nDate1 ) ] )
+ {
+ while ( nRef < nMax && nSortArray.at( nRef ) < nDate1 )
+ nRef++;
+ if ( nRef >= nMax || nSortArray.at( nRef ) != nDate1 )
+ nCnt++;
+ }
+ ++nDate1;
+ }
+ PushDouble( static_cast<double>( bReverse ? -nCnt : nCnt ) );
+ }
+}
+
+void ScInterpreter::ScWorkday_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 4 ) )
+ return;
+
+ nFuncFmtType = SvNumFormatType::DATE;
+ vector<double> nSortArray;
+ bool bWeekendMask[ 7 ];
+ const Date& rNullDate = pFormatter->GetNullDate();
+ sal_uInt32 nNullDate = Date::DateToDays( rNullDate.GetDay(), rNullDate.GetMonth(), rNullDate.GetYear() );
+ FormulaError nErr = GetWeekendAndHolidayMasks_MS( nParamCount, nNullDate,
+ nSortArray, bWeekendMask, true );
+ if ( nErr != FormulaError::NONE )
+ PushError( nErr );
+ else
+ {
+ sal_Int32 nDays = GetFloor32();
+ sal_uInt32 nDate = GetUInt32();
+ if (nGlobalError != FormulaError::NONE || (nDate > SAL_MAX_UINT32 - nNullDate))
+ {
+ PushIllegalArgument();
+ return;
+ }
+ nDate += nNullDate;
+
+ if ( !nDays )
+ PushDouble( static_cast<double>( nDate - nNullDate ) );
+ else
+ {
+ size_t nMax = nSortArray.size();
+ if ( nDays > 0 )
+ {
+ size_t nRef = 0;
+ while ( nDays )
+ {
+ do
+ {
+ ++nDate;
+ }
+ while ( bWeekendMask[ GetDayOfWeek( nDate ) ] ); //jump over weekend day(s)
+
+ while ( nRef < nMax && nSortArray.at( nRef ) < nDate )
+ nRef++;
+
+ if ( nRef >= nMax || nSortArray.at( nRef ) != nDate || nRef >= nMax )
+ nDays--;
+ }
+ }
+ else
+ {
+ sal_Int16 nRef = nMax - 1;
+ while ( nDays )
+ {
+ do
+ {
+ --nDate;
+ }
+ while ( bWeekendMask[ GetDayOfWeek( nDate ) ] ); //jump over weekend day(s)
+
+ while ( nRef >= 0 && nSortArray.at( nRef ) > nDate )
+ nRef--;
+
+ if (nRef < 0 || nSortArray.at(nRef) != nDate)
+ nDays++;
+ }
+ }
+ PushDouble( static_cast<double>( nDate - nNullDate ) );
+ }
+ }
+}
+
+void ScInterpreter::ScGetDate()
+{
+ nFuncFmtType = SvNumFormatType::DATE;
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ sal_Int16 nDay = GetInt16();
+ sal_Int16 nMonth = GetInt16();
+ if (IsMissing())
+ SetError( FormulaError::ParameterExpected); // Year must be given.
+ sal_Int16 nYear = GetInt16();
+ if (nGlobalError != FormulaError::NONE || nYear < 0)
+ PushIllegalArgument();
+ else
+ PushDouble(GetDateSerial(nYear, nMonth, nDay, false));
+}
+
+void ScInterpreter::ScGetTime()
+{
+ nFuncFmtType = SvNumFormatType::TIME;
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double fSec = GetDouble();
+ double fMin = GetDouble();
+ double fHour = GetDouble();
+ double fTime = fmod( (fHour * ::tools::Time::secondPerHour) + (fMin * ::tools::Time::secondPerMinute) + fSec, DATE_TIME_FACTOR) / DATE_TIME_FACTOR;
+ if (fTime < 0)
+ PushIllegalArgument();
+ else
+ PushDouble( fTime);
+ }
+}
+
+void ScInterpreter::ScGetDiffDate()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ double fDate2 = GetDouble();
+ double fDate1 = GetDouble();
+ PushDouble(fDate1 - fDate2);
+ }
+}
+
+void ScInterpreter::ScGetDiffDate360()
+{
+ /* Implementation follows
+ * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf
+ * Appendix B: Day-Count Bases, there are 7 different ways to calculate the
+ * 30-days count. That document also claims that Excel implements the "PSA
+ * 30" or "NASD 30" method (funny enough they also state that Excel is the
+ * only tool that does so).
+ *
+ * Note that the definition given in
+ * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp
+ * is _not_ the way how it is actually calculated by Excel (that would not
+ * even match any of the 7 methods mentioned above) and would result in the
+ * following test cases producing wrong results according to that appendix B:
+ *
+ * 28-Feb-95 31-Aug-95 181 instead of 180
+ * 29-Feb-96 31-Aug-96 181 instead of 180
+ * 30-Jan-96 31-Mar-96 61 instead of 60
+ * 31-Jan-96 31-Mar-96 61 instead of 60
+ *
+ * Still, there is a difference between OOoCalc and Excel:
+ * In Excel:
+ * 02-Feb-99 31-Mar-00 results in 419
+ * 31-Mar-00 02-Feb-99 results in -418
+ * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel.
+ */
+
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ bool bFlag = nParamCount == 3 && GetBool();
+ sal_Int32 nDate2 = GetFloor32();
+ sal_Int32 nDate1 = GetFloor32();
+ if (nGlobalError != FormulaError::NONE)
+ PushError( nGlobalError);
+ else
+ {
+ sal_Int32 nSign;
+ // #i84934# only for non-US European algorithm swap dates. Else
+ // follow Excel's meaningless extrapolation for "interoperability".
+ if (bFlag && (nDate2 < nDate1))
+ {
+ nSign = nDate1;
+ nDate1 = nDate2;
+ nDate2 = nSign;
+ nSign = -1;
+ }
+ else
+ nSign = 1;
+ Date aDate1 = pFormatter->GetNullDate();
+ aDate1.AddDays( nDate1);
+ Date aDate2 = pFormatter->GetNullDate();
+ aDate2.AddDays( nDate2);
+ if (aDate1.GetDay() == 31)
+ aDate1.AddDays( -1);
+ else if (!bFlag)
+ {
+ if (aDate1.GetMonth() == 2)
+ {
+ switch ( aDate1.GetDay() )
+ {
+ case 28 :
+ if ( !aDate1.IsLeapYear() )
+ aDate1.SetDay(30);
+ break;
+ case 29 :
+ aDate1.SetDay(30);
+ break;
+ }
+ }
+ }
+ if (aDate2.GetDay() == 31)
+ {
+ if (!bFlag )
+ {
+ if (aDate1.GetDay() == 30)
+ aDate2.AddDays( -1);
+ }
+ else
+ aDate2.SetDay(30);
+ }
+ PushDouble( static_cast<double>(nSign) *
+ ( static_cast<double>(aDate2.GetDay()) + static_cast<double>(aDate2.GetMonth()) * 30.0 +
+ static_cast<double>(aDate2.GetYear()) * 360.0
+ - static_cast<double>(aDate1.GetDay()) - static_cast<double>(aDate1.GetMonth()) * 30.0
+ - static_cast<double>(aDate1.GetYear()) * 360.0) );
+ }
+}
+
+// fdo#44456 function DATEDIF as defined in ODF1.2 (Par. 6.10.3)
+void ScInterpreter::ScGetDateDif()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ OUString aInterval = GetString().getString();
+ sal_Int32 nDate2 = GetFloor32();
+ sal_Int32 nDate1 = GetFloor32();
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ // Excel doesn't swap dates or return negative numbers, so don't we.
+ if (nDate1 > nDate2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ double dd = nDate2 - nDate1;
+ // Zero difference or number of days can be returned immediately.
+ if (dd == 0.0 || aInterval.equalsIgnoreAsciiCase( "d" ))
+ {
+ PushDouble( dd );
+ return;
+ }
+
+ // split dates in day, month, year for use with formats other than "d"
+ sal_uInt16 d1, m1, d2, m2;
+ sal_Int16 y1, y2;
+ Date aDate1( pFormatter->GetNullDate());
+ aDate1.AddDays( nDate1);
+ y1 = aDate1.GetYear();
+ m1 = aDate1.GetMonth();
+ d1 = aDate1.GetDay();
+ Date aDate2( pFormatter->GetNullDate());
+ aDate2.AddDays( nDate2);
+ y2 = aDate2.GetYear();
+ m2 = aDate2.GetMonth();
+ d2 = aDate2.GetDay();
+
+ // Close the year 0 gap to calculate year difference.
+ if (y1 < 0 && y2 > 0)
+ ++y1;
+ else if (y1 > 0 && y2 < 0)
+ ++y2;
+
+ if ( aInterval.equalsIgnoreAsciiCase( "m" ) )
+ {
+ // Return number of months.
+ int md = m2 - m1 + 12 * (y2 - y1);
+ if (d1 > d2)
+ --md;
+ PushInt( md );
+ }
+ else if ( aInterval.equalsIgnoreAsciiCase( "y" ) )
+ {
+ // Return number of years.
+ int yd;
+ if ( y2 > y1 )
+ {
+ if (m2 > m1 || (m2 == m1 && d2 >= d1))
+ yd = y2 - y1; // complete years between dates
+ else
+ yd = y2 - y1 - 1; // one incomplete year
+ }
+ else
+ {
+ // Year is equal as we don't allow reversed arguments, no
+ // complete year between dates.
+ yd = 0;
+ }
+ PushInt( yd );
+ }
+ else if ( aInterval.equalsIgnoreAsciiCase( "md" ) )
+ {
+ // Return number of days, excluding months and years.
+ // This is actually the remainder of days when subtracting years
+ // and months from the difference of dates. Birthday-like 23 years
+ // and 10 months and 19 days.
+
+ // Algorithm's roll-over behavior extracted from Excel by try and
+ // error...
+ // If day1 <= day2 then simply day2 - day1.
+ // If day1 > day2 then set month1 to month2-1 and year1 to
+ // year2(-1) and subtract dates, e.g. for 2012-01-28,2012-03-01 set
+ // 2012-02-28 and then (2012-03-01)-(2012-02-28) => 2 days (leap
+ // year).
+ // For 2011-01-29,2011-03-01 the non-existent 2011-02-29 rolls over
+ // to 2011-03-01 so the result is 0. Same for day 31 in months with
+ // only 30 days.
+
+ tools::Long nd;
+ if (d1 <= d2)
+ nd = d2 - d1;
+ else
+ {
+ if (m2 == 1)
+ {
+ aDate1.SetYear( y2 == 1 ? -1 : y2 - 1 );
+ aDate1.SetMonth( 12 );
+ }
+ else
+ {
+ aDate1.SetYear( y2 );
+ aDate1.SetMonth( m2 - 1 );
+ }
+ aDate1.Normalize();
+ nd = aDate2 - aDate1;
+ }
+ PushDouble( nd );
+ }
+ else if ( aInterval.equalsIgnoreAsciiCase( "ym" ) )
+ {
+ // Return number of months, excluding years.
+ int md = m2 - m1 + 12 * (y2 - y1);
+ if (d1 > d2)
+ --md;
+ md %= 12;
+ PushInt( md );
+ }
+ else if ( aInterval.equalsIgnoreAsciiCase( "yd" ) )
+ {
+ // Return number of days, excluding years.
+
+ // Condition corresponds with "y".
+ if (m2 > m1 || (m2 == m1 && d2 >= d1))
+ aDate1.SetYear( y2 );
+ else
+ aDate1.SetYear( y2 - 1 );
+ // XXX NOTE: Excel for the case 1988-06-22,2012-05-11 returns
+ // 323, whereas the result here is 324. Don't they use the leap
+ // year of 2012?
+ // http://www.cpearson.com/excel/datedif.aspx "DATEDIF And Leap
+ // Years" is not correct and Excel 2010 correctly returns 0 in
+ // both cases mentioned there. Also using year1 as mentioned
+ // produces incorrect results in other cases and different from
+ // Excel 2010. Apparently they fixed some calculations.
+ aDate1.Normalize();
+ double fd = aDate2 - aDate1;
+ PushDouble( fd );
+ }
+ else
+ PushIllegalArgument(); // unsupported format
+}
+
+void ScInterpreter::ScGetTimeValue()
+{
+ OUString aInputString = GetString().getString();
+ sal_uInt32 nFIndex = 0; // damit default Land/Spr.
+ double fVal;
+ if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal, SvNumInputOptions::LAX_TIME))
+ {
+ SvNumFormatType eType = pFormatter->GetType(nFIndex);
+ if (eType == SvNumFormatType::TIME || eType == SvNumFormatType::DATETIME)
+ {
+ nFuncFmtType = SvNumFormatType::TIME;
+ double fDateVal = rtl::math::approxFloor(fVal);
+ double fTimeVal = fVal - fDateVal;
+ fTimeVal = ::tools::Duration(fTimeVal).GetInDays(); // force corrected
+ PushDouble(fTimeVal);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScPlusMinus()
+{
+ double fVal = GetDouble();
+ short n = 0;
+ if (fVal < 0.0)
+ n = -1;
+ else if (fVal > 0.0)
+ n = 1;
+ PushInt( n );
+}
+
+void ScInterpreter::ScAbs()
+{
+ PushDouble(std::abs(GetDouble()));
+}
+
+void ScInterpreter::ScInt()
+{
+ PushDouble(::rtl::math::approxFloor(GetDouble()));
+}
+
+void ScInterpreter::RoundNumber( rtl_math_RoundingMode eMode )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ double fVal = 0.0;
+ if (nParamCount == 1)
+ fVal = ::rtl::math::round( GetDouble(), 0, eMode );
+ else
+ {
+ const sal_Int16 nDec = GetInt16();
+ const double fX = GetDouble();
+ if (nGlobalError == FormulaError::NONE)
+ {
+ // A quite aggressive approach with 12 significant digits.
+ // However, using 14 or some other doesn't work because other
+ // values may fail, like =ROUNDDOWN(2-5E-015;13) would produce
+ // 2 (another example in tdf#124286).
+ constexpr sal_Int16 kSigDig = 12;
+
+ if ( ( eMode == rtl_math_RoundingMode_Down ||
+ eMode == rtl_math_RoundingMode_Up ) &&
+ nDec < kSigDig && fmod( fX, 1.0 ) != 0.0 )
+
+ {
+ // tdf124286 : round to significant digits before rounding
+ // down or up to avoid unexpected rounding errors
+ // caused by decimal -> binary -> decimal conversion
+
+ double fRes = fX;
+ // Similar to RoundSignificant() but omitting the back-scaling
+ // and interim integer rounding before the final rounding,
+ // which would result in double rounding. Instead, adjust the
+ // decimals and round into integer part before scaling back.
+ const double fTemp = floor( log10( std::abs(fRes))) + 1.0 - kSigDig;
+ // Avoid inaccuracy of negative powers of 10.
+ if (fTemp < 0.0)
+ fRes *= pow(10.0, -fTemp);
+ else
+ fRes /= pow(10.0, fTemp);
+ if (std::isfinite(fRes))
+ {
+ // fRes is now at a decimal normalized scale.
+ // Truncate up-rounding to opposite direction for values
+ // like 0.0600000000000005 =ROUNDUP(8.06-8;2) that here now
+ // is 600000000000.005 and otherwise would yield 0.07
+ if (eMode == rtl_math_RoundingMode_Up)
+ fRes = ::rtl::math::approxFloor(fRes);
+ fVal = ::rtl::math::round( fRes, nDec + fTemp, eMode );
+ if (fTemp < 0.0)
+ fVal /= pow(10.0, -fTemp);
+ else
+ fVal *= pow(10.0, fTemp);
+ }
+ else
+ {
+ // Overflow. Let our round() decide if and how to round.
+ fVal = ::rtl::math::round( fX, nDec, eMode );
+ }
+ }
+ else
+ fVal = ::rtl::math::round( fX, nDec, eMode );
+ }
+ }
+ PushDouble(fVal);
+}
+
+void ScInterpreter::ScRound()
+{
+ RoundNumber( rtl_math_RoundingMode_Corrected );
+}
+
+void ScInterpreter::ScRoundDown()
+{
+ RoundNumber( rtl_math_RoundingMode_Down );
+}
+
+void ScInterpreter::ScRoundUp()
+{
+ RoundNumber( rtl_math_RoundingMode_Up );
+}
+
+void ScInterpreter::RoundSignificant( double fX, double fDigits, double &fRes )
+{
+ double fTemp = floor( log10( std::abs(fX) ) ) + 1.0 - fDigits;
+ double fIn = fX;
+ // Avoid inaccuracy of negative powers of 10.
+ if (fTemp < 0.0)
+ fIn *= pow(10.0, -fTemp);
+ else
+ fIn /= pow(10.0, fTemp);
+ // For very large fX there might be an overflow in fIn resulting in
+ // non-finite. rtl::math::round() handles that and it will be propagated as
+ // usual.
+ fRes = ::rtl::math::round(fIn);
+ if (fTemp < 0.0)
+ fRes /= pow(10.0, -fTemp);
+ else
+ fRes *= pow(10.0, fTemp);
+}
+
+// tdf#105931
+void ScInterpreter::ScRoundSignificant()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double fDigits = ::rtl::math::approxFloor( GetDouble() );
+ double fX = GetDouble();
+ if ( nGlobalError != FormulaError::NONE || fDigits < 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ if ( fX == 0.0 )
+ PushDouble( 0.0 );
+ else
+ {
+ double fRes;
+ RoundSignificant( fX, fDigits, fRes );
+ PushDouble( fRes );
+ }
+}
+
+/** tdf69552 ODFF1.2 function CEILING and Excel function CEILING.MATH
+ In essence, the difference between the two is that ODFF-CEILING needs to
+ have arguments value and significance of the same sign and with
+ CEILING.MATH the sign of argument significance is irrevelevant.
+ This is why ODFF-CEILING is exported to Excel as CEILING.MATH and
+ CEILING.MATH is imported in Calc as CEILING.MATH
+ */
+void ScInterpreter::ScCeil( bool bODFF )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
+ return;
+
+ bool bAbs = nParamCount == 3 && GetBool();
+ double fDec, fVal;
+ if ( nParamCount == 1 )
+ {
+ fVal = GetDouble();
+ fDec = ( fVal < 0 ? -1 : 1 );
+ }
+ else
+ {
+ bool bArgumentMissing = IsMissing();
+ fDec = GetDouble();
+ fVal = GetDouble();
+ if ( bArgumentMissing )
+ fDec = ( fVal < 0 ? -1 : 1 );
+ }
+ if ( fVal == 0 || fDec == 0.0 )
+ PushInt( 0 );
+ else
+ {
+ if ( bODFF && fVal * fDec < 0 )
+ PushIllegalArgument();
+ else
+ {
+ if ( fVal * fDec < 0.0 )
+ fDec = -fDec;
+
+ if ( !bAbs && fVal < 0.0 )
+ PushDouble(::rtl::math::approxFloor( fVal / fDec ) * fDec );
+ else
+ PushDouble(::rtl::math::approxCeil( fVal / fDec ) * fDec );
+ }
+ }
+}
+
+void ScInterpreter::ScCeil_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2 ) )
+ return;
+
+ double fDec = GetDouble();
+ double fVal = GetDouble();
+ if ( fVal == 0 || fDec == 0.0 )
+ PushInt(0);
+ else if ( fVal * fDec > 0 )
+ PushDouble(::rtl::math::approxCeil( fVal / fDec ) * fDec );
+ else if ( fVal < 0.0 )
+ PushDouble(::rtl::math::approxFloor( fVal / -fDec ) * -fDec );
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScCeil_Precise()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ double fDec, fVal;
+ if ( nParamCount == 1 )
+ {
+ fVal = GetDouble();
+ fDec = 1.0;
+ }
+ else
+ {
+ fDec = std::abs( GetDoubleWithDefault( 1.0 ));
+ fVal = GetDouble();
+ }
+ if ( fDec == 0.0 || fVal == 0.0 )
+ PushInt( 0 );
+ else
+ PushDouble(::rtl::math::approxCeil( fVal / fDec ) * fDec );
+}
+
+/** tdf69552 ODFF1.2 function FLOOR and Excel function FLOOR.MATH
+ In essence, the difference between the two is that ODFF-FLOOR needs to
+ have arguments value and significance of the same sign and with
+ FLOOR.MATH the sign of argument significance is irrevelevant.
+ This is why ODFF-FLOOR is exported to Excel as FLOOR.MATH and
+ FLOOR.MATH is imported in Calc as FLOOR.MATH
+ */
+void ScInterpreter::ScFloor( bool bODFF )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
+ return;
+
+ bool bAbs = ( nParamCount == 3 && GetBool() );
+ double fDec, fVal;
+ if ( nParamCount == 1 )
+ {
+ fVal = GetDouble();
+ fDec = ( fVal < 0 ? -1 : 1 );
+ }
+ else
+ {
+ bool bArgumentMissing = IsMissing();
+ fDec = GetDouble();
+ fVal = GetDouble();
+ if ( bArgumentMissing )
+ fDec = ( fVal < 0 ? -1 : 1 );
+ }
+ if ( fDec == 0.0 || fVal == 0.0 )
+ PushInt( 0 );
+ else
+ {
+ if ( bODFF && ( fVal * fDec < 0.0 ) )
+ PushIllegalArgument();
+ else
+ {
+ if ( fVal * fDec < 0.0 )
+ fDec = -fDec;
+
+ if ( !bAbs && fVal < 0.0 )
+ PushDouble(::rtl::math::approxCeil( fVal / fDec ) * fDec );
+ else
+ PushDouble(::rtl::math::approxFloor( fVal / fDec ) * fDec );
+ }
+ }
+}
+
+void ScInterpreter::ScFloor_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2 ) )
+ return;
+
+ double fDec = GetDouble();
+ double fVal = GetDouble();
+
+ if ( fVal == 0 )
+ PushInt( 0 );
+ else if ( fVal * fDec > 0 )
+ PushDouble(::rtl::math::approxFloor( fVal / fDec ) * fDec );
+ else if ( fDec == 0 )
+ PushIllegalArgument();
+ else if ( fVal < 0.0 )
+ PushDouble(::rtl::math::approxCeil( fVal / -fDec ) * -fDec );
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScFloor_Precise()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ double fDec = nParamCount == 1 ? 1.0 : std::abs( GetDoubleWithDefault( 1.0 ) );
+ double fVal = GetDouble();
+ if ( fDec == 0.0 || fVal == 0.0 )
+ PushInt( 0 );
+ else
+ PushDouble(::rtl::math::approxFloor( fVal / fDec ) * fDec );
+}
+
+void ScInterpreter::ScEven()
+{
+ double fVal = GetDouble();
+ if (fVal < 0.0)
+ PushDouble(::rtl::math::approxFloor(fVal/2.0) * 2.0);
+ else
+ PushDouble(::rtl::math::approxCeil(fVal/2.0) * 2.0);
+}
+
+void ScInterpreter::ScOdd()
+{
+ double fVal = GetDouble();
+ if (fVal >= 0.0)
+ {
+ fVal = ::rtl::math::approxCeil(fVal);
+ if (fmod(fVal, 2.0) == 0.0)
+ ++fVal;
+ }
+ else
+ {
+ fVal = ::rtl::math::approxFloor(fVal);
+ if (fmod(fVal, 2.0) == 0.0)
+ --fVal;
+ }
+ PushDouble(fVal);
+}
+
+void ScInterpreter::ScArcTan2()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ double fVal2 = GetDouble();
+ double fVal1 = GetDouble();
+ PushDouble(atan2(fVal2, fVal1));
+ }
+}
+
+void ScInterpreter::ScLog()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ double fBase = nParamCount == 2 ? GetDouble() : 10.0;
+ double fVal = GetDouble();
+ if (fVal > 0.0 && fBase > 0.0 && fBase != 1.0)
+ PushDouble(log(fVal) / log(fBase));
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScLn()
+{
+ double fVal = GetDouble();
+ if (fVal > 0.0)
+ PushDouble(log(fVal));
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScLog10()
+{
+ double fVal = GetDouble();
+ if (fVal > 0.0)
+ PushDouble(log10(fVal));
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScNPV()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 2) )
+ return;
+
+ KahanSum fVal = 0.0;
+ // We turn the stack upside down!
+ ReverseStack( nParamCount);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ double fCount = 1.0;
+ double fRate = GetDouble();
+ --nParamCount;
+ size_t nRefInList = 0;
+ ScRange aRange;
+ while (nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ fVal += GetDouble() / pow(1.0 + fRate, fCount);
+ fCount++;
+ }
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (!aCell.hasEmptyValue() && aCell.hasNumeric())
+ {
+ double fCellVal = GetCellValue(aAdr, aCell);
+ fVal += fCellVal / pow(1.0 + fRate, fCount);
+ fCount++;
+ }
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ double fCellVal;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ ScHorizontalValueIterator aValIter( mrDoc, aRange );
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(fCellVal, nErr))
+ {
+ fVal += fCellVal / pow(1.0 + fRate, fCount);
+ fCount++;
+ }
+ if ( nErr != FormulaError::NONE )
+ SetError(nErr);
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ else
+ {
+ double fx;
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ for (SCSIZE k = 0; k < nR; ++k)
+ {
+ if (!pMat->IsValue(j,k))
+ {
+ PushIllegalArgument();
+ return;
+ }
+ fx = pMat->GetDouble(j,k);
+ fVal += fx / pow(1.0 + fRate, fCount);
+ fCount++;
+ }
+ }
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ }
+ }
+ PushDouble(fVal.get());
+}
+
+void ScInterpreter::ScIRR()
+{
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+ double fEstimated = nParamCount == 2 ? GetDouble() : 0.1;
+ double fEps = 1.0;
+ // If it's -1 the default result for division by zero else startvalue
+ double x = fEstimated == -1.0 ? 0.1 : fEstimated;
+ double fValue;
+
+ ScRange aRange;
+ ScMatrixRef pMat;
+ SCSIZE nC = 0;
+ SCSIZE nR = 0;
+ bool bIsMatrix = false;
+ switch (GetStackType())
+ {
+ case svDoubleRef:
+ PopDoubleRef(aRange);
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ pMat = GetMatrix();
+ if (pMat)
+ {
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ bIsMatrix = true;
+ }
+ else
+ {
+ PushIllegalParameter();
+ return;
+ }
+ break;
+ default:
+ {
+ PushIllegalParameter();
+ return;
+ }
+ }
+ const sal_uInt16 nIterationsMax = 20;
+ sal_uInt16 nItCount = 0;
+ FormulaError nIterError = FormulaError::NONE;
+ while (fEps > SCdEpsilon && nItCount < nIterationsMax && nGlobalError == FormulaError::NONE)
+ { // Newtons method:
+ KahanSum fNom = 0.0;
+ KahanSum fDenom = 0.0;
+ double fCount = 0.0;
+ if (bIsMatrix)
+ {
+ for (SCSIZE j = 0; j < nC && nGlobalError == FormulaError::NONE; j++)
+ {
+ for (SCSIZE k = 0; k < nR; k++)
+ {
+ if (!pMat->IsValue(j, k))
+ continue;
+ fValue = pMat->GetDouble(j, k);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ fNom += fValue / pow(1.0+x,fCount);
+ fDenom += -fCount * fValue / pow(1.0+x,fCount+1.0);
+ fCount++;
+ }
+ }
+ }
+ else
+ {
+ ScValueIterator aValIter(mrContext, aRange, mnSubTotalFlags);
+ bool bLoop = aValIter.GetFirst(fValue, nIterError);
+ while (bLoop && nIterError == FormulaError::NONE)
+ {
+ fNom += fValue / pow(1.0+x,fCount);
+ fDenom += -fCount * fValue / pow(1.0+x,fCount+1.0);
+ fCount++;
+
+ bLoop = aValIter.GetNext(fValue, nIterError);
+ }
+ SetError(nIterError);
+ }
+ double xNew = x - fNom.get() / fDenom.get(); // x(i+1) = x(i)-f(x(i))/f'(x(i))
+ nItCount++;
+ fEps = std::abs(xNew - x);
+ x = xNew;
+ }
+ if (fEstimated == 0.0 && std::abs(x) < SCdEpsilon)
+ x = 0.0; // adjust to zero
+ if (fEps < SCdEpsilon)
+ PushDouble(x);
+ else
+ PushError( FormulaError::NoConvergence);
+}
+
+void ScInterpreter::ScMIRR()
+{ // range_of_values ; rate_invest ; rate_reinvest
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ double fRate1_reinvest = GetDouble() + 1;
+ double fRate1_invest = GetDouble() + 1;
+
+ ScRange aRange;
+ ScMatrixRef pMat;
+ SCSIZE nC = 0;
+ SCSIZE nR = 0;
+ bool bIsMatrix = false;
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ PopDoubleRef( aRange );
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ pMat = GetMatrix();
+ if ( pMat )
+ {
+ pMat->GetDimensions( nC, nR );
+ if ( nC == 0 || nR == 0 )
+ SetError( FormulaError::IllegalArgument );
+ bIsMatrix = true;
+ }
+ else
+ SetError( FormulaError::IllegalArgument );
+ }
+ break;
+ default :
+ SetError( FormulaError::IllegalParameter );
+ break;
+ }
+
+ if ( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError );
+ else
+ {
+ KahanSum fNPV_reinvest = 0.0;
+ double fPow_reinvest = 1.0;
+ KahanSum fNPV_invest = 0.0;
+ double fPow_invest = 1.0;
+ sal_uLong nCount = 0;
+ bool bHasPosValue = false;
+ bool bHasNegValue = false;
+
+ if ( bIsMatrix )
+ {
+ double fX;
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ for ( SCSIZE k = 0; k < nR; ++k )
+ {
+ if ( !pMat->IsValue( j, k ) )
+ continue;
+ fX = pMat->GetDouble( j, k );
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+
+ if ( fX > 0.0 )
+ { // reinvestments
+ bHasPosValue = true;
+ fNPV_reinvest += fX * fPow_reinvest;
+ }
+ else if ( fX < 0.0 )
+ { // investments
+ bHasNegValue = true;
+ fNPV_invest += fX * fPow_invest;
+ }
+ fPow_reinvest /= fRate1_reinvest;
+ fPow_invest /= fRate1_invest;
+ nCount++;
+ }
+ }
+ }
+ else
+ {
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ double fCellValue;
+ FormulaError nIterError = FormulaError::NONE;
+
+ bool bLoop = aValIter.GetFirst( fCellValue, nIterError );
+ while( bLoop )
+ {
+ if( fCellValue > 0.0 ) // reinvestments
+ { // reinvestments
+ bHasPosValue = true;
+ fNPV_reinvest += fCellValue * fPow_reinvest;
+ }
+ else if( fCellValue < 0.0 ) // investments
+ { // investments
+ bHasNegValue = true;
+ fNPV_invest += fCellValue * fPow_invest;
+ }
+ fPow_reinvest /= fRate1_reinvest;
+ fPow_invest /= fRate1_invest;
+ nCount++;
+
+ bLoop = aValIter.GetNext( fCellValue, nIterError );
+ }
+
+ if ( nIterError != FormulaError::NONE )
+ SetError( nIterError );
+ }
+ if ( !( bHasPosValue && bHasNegValue ) )
+ SetError( FormulaError::IllegalArgument );
+
+ if ( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError );
+ else
+ {
+ double fResult = -fNPV_reinvest.get() / fNPV_invest.get();
+ fResult *= pow( fRate1_reinvest, static_cast<double>( nCount - 1 ) );
+ fResult = pow( fResult, div( 1.0, (nCount - 1)) );
+ PushDouble( fResult - 1.0 );
+ }
+ }
+}
+
+void ScInterpreter::ScISPMT()
+{ // rate ; period ; total_periods ; invest
+ if( MustHaveParamCount( GetByte(), 4 ) )
+ {
+ double fInvest = GetDouble();
+ double fTotal = GetDouble();
+ double fPeriod = GetDouble();
+ double fRate = GetDouble();
+
+ if( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError);
+ else
+ PushDouble( fInvest * fRate * (fPeriod / fTotal - 1.0) );
+ }
+}
+
+// financial functions
+double ScInterpreter::ScGetPV(double fRate, double fNper, double fPmt,
+ double fFv, bool bPayInAdvance)
+{
+ double fPv;
+ if (fRate == 0.0)
+ fPv = fFv + fPmt * fNper;
+ else
+ {
+ if (bPayInAdvance)
+ fPv = (fFv * pow(1.0 + fRate, -fNper))
+ + (fPmt * (1.0 - pow(1.0 + fRate, -fNper + 1.0)) / fRate)
+ + fPmt;
+ else
+ fPv = (fFv * pow(1.0 + fRate, -fNper))
+ + (fPmt * (1.0 - pow(1.0 + fRate, -fNper)) / fRate);
+ }
+ return -fPv;
+}
+
+void ScInterpreter::ScPV()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+
+ bool bPayInAdvance = nParamCount == 5 && GetBool();
+ double fFv = nParamCount >= 4 ? GetDouble() : 0;
+ double fPmt = GetDouble();
+ double fNper = GetDouble();
+ double fRate = GetDouble();
+ PushDouble(ScGetPV(fRate, fNper, fPmt, fFv, bPayInAdvance));
+}
+
+void ScInterpreter::ScSYD()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ if ( MustHaveParamCount( GetByte(), 4 ) )
+ {
+ double fPer = GetDouble();
+ double fLife = GetDouble();
+ double fSalvage = GetDouble();
+ double fCost = GetDouble();
+ double fSyd = ((fCost - fSalvage) * (fLife - fPer + 1.0)) /
+ ((fLife * (fLife + 1.0)) / 2.0);
+ PushDouble(fSyd);
+ }
+}
+
+double ScInterpreter::ScGetDDB(double fCost, double fSalvage, double fLife,
+ double fPeriod, double fFactor)
+{
+ double fDdb, fRate, fOldValue, fNewValue;
+ fRate = fFactor / fLife;
+ if (fRate >= 1.0)
+ {
+ fRate = 1.0;
+ fOldValue = fPeriod == 1.0 ? fCost : 0;
+ }
+ else
+ fOldValue = fCost * pow(1.0 - fRate, fPeriod - 1.0);
+ fNewValue = fCost * pow(1.0 - fRate, fPeriod);
+
+ fDdb = fNewValue < fSalvage ? fOldValue - fSalvage : fOldValue - fNewValue;
+ return fDdb < 0 ? 0 : fDdb;
+}
+
+void ScInterpreter::ScDDB()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 4, 5 ) )
+ return;
+
+ double fFactor = nParamCount == 5 ? GetDouble() : 2.0;
+ double fPeriod = GetDouble();
+ double fLife = GetDouble();
+ double fSalvage = GetDouble();
+ double fCost = GetDouble();
+ if (fCost < 0.0 || fSalvage < 0.0 || fFactor <= 0.0 || fSalvage > fCost
+ || fPeriod < 1.0 || fPeriod > fLife)
+ PushIllegalArgument();
+ else
+ PushDouble(ScGetDDB(fCost, fSalvage, fLife, fPeriod, fFactor));
+}
+
+void ScInterpreter::ScDB()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 4, 5 ) )
+ return ;
+ double fMonths = nParamCount == 4 ? 12.0 : ::rtl::math::approxFloor(GetDouble());
+ double fPeriod = GetDouble();
+ double fLife = GetDouble();
+ double fSalvage = GetDouble();
+ double fCost = GetDouble();
+ if (fMonths < 1.0 || fMonths > 12.0 || fLife > 1200.0 || fSalvage < 0.0 ||
+ fPeriod > (fLife + 1.0) || fSalvage > fCost || fCost <= 0.0 ||
+ fLife <= 0 || fPeriod <= 0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ double fOffRate = 1.0 - pow(fSalvage / fCost, 1.0 / fLife);
+ fOffRate = ::rtl::math::approxFloor((fOffRate * 1000.0) + 0.5) / 1000.0;
+ double fFirstOffRate = fCost * fOffRate * fMonths / 12.0;
+ double fDb = 0.0;
+ if (::rtl::math::approxFloor(fPeriod) == 1)
+ fDb = fFirstOffRate;
+ else
+ {
+ KahanSum fSumOffRate = fFirstOffRate;
+ double fMin = fLife;
+ if (fMin > fPeriod) fMin = fPeriod;
+ sal_uInt16 iMax = static_cast<sal_uInt16>(::rtl::math::approxFloor(fMin));
+ for (sal_uInt16 i = 2; i <= iMax; i++)
+ {
+ fDb = -(fSumOffRate - fCost).get() * fOffRate;
+ fSumOffRate += fDb;
+ }
+ if (fPeriod > fLife)
+ fDb = -(fSumOffRate - fCost).get() * fOffRate * (12.0 - fMonths) / 12.0;
+ }
+ PushDouble(fDb);
+}
+
+double ScInterpreter::ScInterVDB(double fCost, double fSalvage, double fLife,
+ double fLife1, double fPeriod, double fFactor)
+{
+ KahanSum fVdb = 0.0;
+ double fIntEnd = ::rtl::math::approxCeil(fPeriod);
+ sal_uLong nLoopEnd = static_cast<sal_uLong>(fIntEnd);
+
+ double fTerm, fSln = 0; // SLN: Straight-Line Depreciation
+ double fSalvageValue = fCost - fSalvage;
+ bool bNowSln = false;
+
+ double fDdb;
+ sal_uLong i;
+ for ( i = 1; i <= nLoopEnd; i++)
+ {
+ if(!bNowSln)
+ {
+ fDdb = ScGetDDB(fCost, fSalvage, fLife, static_cast<double>(i), fFactor);
+ fSln = fSalvageValue/ (fLife1 - static_cast<double>(i-1));
+
+ if (fSln > fDdb)
+ {
+ fTerm = fSln;
+ bNowSln = true;
+ }
+ else
+ {
+ fTerm = fDdb;
+ fSalvageValue -= fDdb;
+ }
+ }
+ else
+ {
+ fTerm = fSln;
+ }
+
+ if ( i == nLoopEnd)
+ fTerm *= ( fPeriod + 1.0 - fIntEnd );
+
+ fVdb += fTerm;
+ }
+ return fVdb.get();
+}
+
+void ScInterpreter::ScVDB()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 5, 7 ) )
+ return;
+
+ KahanSum fVdb = 0.0;
+ bool bNoSwitch = nParamCount == 7 && GetBool();
+ double fFactor = nParamCount >= 6 ? GetDouble() : 2.0;
+ double fEnd = GetDouble();
+ double fStart = GetDouble();
+ double fLife = GetDouble();
+ double fSalvage = GetDouble();
+ double fCost = GetDouble();
+ if (fStart < 0.0 || fEnd < fStart || fEnd > fLife || fCost < 0.0
+ || fSalvage > fCost || fFactor <= 0.0)
+ PushIllegalArgument();
+ else
+ {
+ double fIntStart = ::rtl::math::approxFloor(fStart);
+ double fIntEnd = ::rtl::math::approxCeil(fEnd);
+ sal_uLong nLoopStart = static_cast<sal_uLong>(fIntStart);
+ sal_uLong nLoopEnd = static_cast<sal_uLong>(fIntEnd);
+
+ if (bNoSwitch)
+ {
+ for (sal_uLong i = nLoopStart + 1; i <= nLoopEnd; i++)
+ {
+ double fTerm = ScGetDDB(fCost, fSalvage, fLife, static_cast<double>(i), fFactor);
+
+ //respect partial period in the Beginning/ End:
+ if ( i == nLoopStart+1 )
+ fTerm *= ( std::min( fEnd, fIntStart + 1.0 ) - fStart );
+ else if ( i == nLoopEnd )
+ fTerm *= ( fEnd + 1.0 - fIntEnd );
+
+ fVdb += fTerm;
+ }
+ }
+ else
+ {
+ double fPart = 0.0;
+ // respect partial period in the Beginning / End:
+ if ( !::rtl::math::approxEqual( fStart, fIntStart ) ||
+ !::rtl::math::approxEqual( fEnd, fIntEnd ) )
+ {
+ if ( !::rtl::math::approxEqual( fStart, fIntStart ) )
+ {
+ // part to be subtracted at the beginning
+ double fTempIntEnd = fIntStart + 1.0;
+ double fTempValue = fCost -
+ ScInterVDB( fCost, fSalvage, fLife, fLife, fIntStart, fFactor );
+ fPart += ( fStart - fIntStart ) *
+ ScInterVDB( fTempValue, fSalvage, fLife, fLife - fIntStart,
+ fTempIntEnd - fIntStart, fFactor);
+ }
+ if ( !::rtl::math::approxEqual( fEnd, fIntEnd ) )
+ {
+ // part to be subtracted at the end
+ double fTempIntStart = fIntEnd - 1.0;
+ double fTempValue = fCost -
+ ScInterVDB( fCost, fSalvage, fLife, fLife, fTempIntStart, fFactor );
+ fPart += ( fIntEnd - fEnd ) *
+ ScInterVDB( fTempValue, fSalvage, fLife, fLife - fTempIntStart,
+ fIntEnd - fTempIntStart, fFactor);
+ }
+ }
+ // calculate depreciation for whole periods
+ fCost -= ScInterVDB( fCost, fSalvage, fLife, fLife, fIntStart, fFactor );
+ fVdb = ScInterVDB( fCost, fSalvage, fLife, fLife - fIntStart,
+ fIntEnd - fIntStart, fFactor);
+ fVdb -= fPart;
+ }
+ }
+ PushDouble(fVdb.get());
+}
+
+void ScInterpreter::ScPDuration()
+{
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double fFuture = GetDouble();
+ double fPresent = GetDouble();
+ double fRate = GetDouble();
+ if ( fFuture <= 0.0 || fPresent <= 0.0 || fRate <= 0.0 )
+ PushIllegalArgument();
+ else
+ PushDouble( std::log( fFuture / fPresent ) / std::log1p( fRate ) );
+ }
+}
+
+void ScInterpreter::ScSLN()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double fLife = GetDouble();
+ double fSalvage = GetDouble();
+ double fCost = GetDouble();
+ PushDouble( div( fCost - fSalvage, fLife ) );
+ }
+}
+
+double ScInterpreter::ScGetPMT(double fRate, double fNper, double fPv,
+ double fFv, bool bPayInAdvance)
+{
+ double fPayment;
+ if (fRate == 0.0)
+ fPayment = (fPv + fFv) / fNper;
+ else
+ {
+ if (bPayInAdvance) // payment in advance
+ fPayment = (fFv + fPv * exp( fNper * ::std::log1p(fRate) ) ) * fRate /
+ (std::expm1( (fNper + 1) * ::std::log1p(fRate) ) - fRate);
+ else // payment in arrear
+ fPayment = (fFv + fPv * exp(fNper * ::std::log1p(fRate) ) ) * fRate /
+ std::expm1( fNper * ::std::log1p(fRate) );
+ }
+ return -fPayment;
+}
+
+void ScInterpreter::ScPMT()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+ bool bPayInAdvance = nParamCount == 5 && GetBool();
+ double fFv = nParamCount >= 4 ? GetDouble() : 0;
+ double fPv = GetDouble();
+ double fNper = GetDouble();
+ double fRate = GetDouble();
+ PushDouble(ScGetPMT(fRate, fNper, fPv, fFv, bPayInAdvance));
+}
+
+void ScInterpreter::ScRRI()
+{
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double fFutureValue = GetDouble();
+ double fPresentValue = GetDouble();
+ double fNrOfPeriods = GetDouble();
+ if ( fNrOfPeriods <= 0.0 || fPresentValue == 0.0 )
+ PushIllegalArgument();
+ else
+ PushDouble(pow(fFutureValue / fPresentValue, 1.0 / fNrOfPeriods) - 1.0);
+ }
+}
+
+double ScInterpreter::ScGetFV(double fRate, double fNper, double fPmt,
+ double fPv, bool bPayInAdvance)
+{
+ double fFv;
+ if (fRate == 0.0)
+ fFv = fPv + fPmt * fNper;
+ else
+ {
+ double fTerm = pow(1.0 + fRate, fNper);
+ if (bPayInAdvance)
+ fFv = fPv * fTerm + fPmt*(1.0 + fRate)*(fTerm - 1.0)/fRate;
+ else
+ fFv = fPv * fTerm + fPmt*(fTerm - 1.0)/fRate;
+ }
+ return -fFv;
+}
+
+void ScInterpreter::ScFV()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+ bool bPayInAdvance = nParamCount == 5 && GetBool();
+ double fPv = nParamCount >= 4 ? GetDouble() : 0;
+ double fPmt = GetDouble();
+ double fNper = GetDouble();
+ double fRate = GetDouble();
+ PushDouble(ScGetFV(fRate, fNper, fPmt, fPv, bPayInAdvance));
+}
+
+void ScInterpreter::ScNper()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+ bool bPayInAdvance = nParamCount == 5 && GetBool();
+ double fFV = nParamCount >= 4 ? GetDouble() : 0;
+ double fPV = GetDouble(); // Present Value
+ double fPmt = GetDouble(); // Payment
+ double fRate = GetDouble();
+ // Note that due to the function specification in ODFF1.2 (and Excel) the
+ // amount to be paid to get from fPV to fFV is fFV_+_fPV.
+ if ( fPV + fFV == 0.0 )
+ PushDouble( 0.0 );
+ else if (fRate == 0.0)
+ PushDouble(-(fPV + fFV)/fPmt);
+ else if (bPayInAdvance)
+ PushDouble(log(-(fRate*fFV-fPmt*(1.0+fRate))/(fRate*fPV+fPmt*(1.0+fRate)))
+ / std::log1p(fRate));
+ else
+ PushDouble(log(-(fRate*fFV-fPmt)/(fRate*fPV+fPmt)) / std::log1p(fRate));
+}
+
+bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv,
+ double fFv, bool bPayType, double & fGuess )
+{
+ // See also #i15090#
+ // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i))
+ // This solution handles integer and non-integer values of Nper different.
+ // If ODFF will constraint Nper to integer, the distinction of cases can be
+ // removed; only the integer-part is needed then.
+ bool bValid = true, bFound = false;
+ double fX, fXnew, fTerm, fTermDerivation;
+ double fGeoSeries, fGeoSeriesDerivation;
+ const sal_uInt16 nIterationsMax = 150;
+ sal_uInt16 nCount = 0;
+ const double fEpsilonSmall = 1.0E-14;
+ if ( bPayType )
+ {
+ // payment at beginning of each period
+ fFv = fFv - fPayment;
+ fPv = fPv + fPayment;
+ }
+ if (fNper == ::rtl::math::round( fNper ))
+ { // Nper is an integer value
+ fX = fGuess;
+ while (!bFound && nCount < nIterationsMax)
+ {
+ double fPowN, fPowNminus1; // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1)
+ fPowNminus1 = pow( 1.0+fX, fNper-1.0);
+ fPowN = fPowNminus1 * (1.0+fX);
+ if (fX == 0.0)
+ {
+ fGeoSeries = fNper;
+ fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
+ }
+ else
+ {
+ fGeoSeries = (fPowN-1.0)/fX;
+ fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX;
+ }
+ fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries;
+ fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation;
+ if (std::abs(fTerm) < fEpsilonSmall)
+ bFound = true; // will catch root which is at an extreme
+ else
+ {
+ if (fTermDerivation == 0.0)
+ fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope
+ else
+ fXnew = fX - fTerm / fTermDerivation;
+ nCount++;
+ // more accuracy not possible in oscillating cases
+ bFound = (std::abs(fXnew - fX) < SCdEpsilon);
+ fX = fXnew;
+ }
+ }
+ // Gnumeric returns roots < -1, Excel gives an error in that cases,
+ // ODFF says nothing about it. Enable the statement, if you want Excel's
+ // behavior.
+ //bValid =(fX >=-1.0);
+ // Update 2013-06-17: Gnumeric (v1.12.2) doesn't return roots <= -1
+ // anymore.
+ bValid = (fX > -1.0);
+ }
+ else
+ { // Nper is not an integer value.
+ fX = (fGuess < -1.0) ? -1.0 : fGuess; // start with a valid fX
+ while (bValid && !bFound && nCount < nIterationsMax)
+ {
+ if (fX == 0.0)
+ {
+ fGeoSeries = fNper;
+ fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
+ }
+ else
+ {
+ fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX;
+ fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX;
+ }
+ fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries;
+ fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation;
+ if (std::abs(fTerm) < fEpsilonSmall)
+ bFound = true; // will catch root which is at an extreme
+ else
+ {
+ if (fTermDerivation == 0.0)
+ fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope
+ else
+ fXnew = fX - fTerm / fTermDerivation;
+ nCount++;
+ // more accuracy not possible in oscillating cases
+ bFound = (std::abs(fXnew - fX) < SCdEpsilon);
+ fX = fXnew;
+ bValid = (fX >= -1.0); // otherwise pow(1.0+fX,fNper) will fail
+ }
+ }
+ }
+ fGuess = fX; // return approximate root
+ return bValid && bFound;
+}
+
+// In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess)
+void ScInterpreter::ScRate()
+{
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
+ return;
+
+ // defaults for missing arguments, see ODFF spec
+ double fGuess = nParamCount == 6 ? GetDouble() : 0.1;
+ bool bDefaultGuess = nParamCount != 6;
+ bool bPayType = nParamCount >= 5 && GetBool();
+ double fFv = nParamCount >= 4 ? GetDouble() : 0;
+ double fPv = GetDouble();
+ double fPayment = GetDouble();
+ double fNper = GetDouble();
+ double fOrigGuess = fGuess;
+
+ if (fNper <= 0.0) // constraint from ODFF spec
+ {
+ PushIllegalArgument();
+ return;
+ }
+ bool bValid = RateIteration(fNper, fPayment, fPv, fFv, bPayType, fGuess);
+
+ if (!bValid)
+ {
+ /* TODO: try also for specified guess values, not only default? As is,
+ * a specified 0.1 guess may be error result but a default 0.1 guess
+ * may succeed. On the other hand, using a different guess value than
+ * the specified one may not be desired, even if that didn't match. */
+ if (bDefaultGuess)
+ {
+ /* TODO: this is rather ugly, instead of looping over different
+ * guess values and doing a Newton goal seek for each we could
+ * first insert the values into the RATE equation to obtain a set
+ * of y values and then do a bisecting goal seek, possibly using
+ * different algorithms. */
+ double fX = fOrigGuess;
+ for (int nStep = 2; nStep <= 10 && !bValid; ++nStep)
+ {
+ fGuess = fX * nStep;
+ bValid = RateIteration( fNper, fPayment, fPv, fFv, bPayType, fGuess);
+ if (!bValid)
+ {
+ fGuess = fX / nStep;
+ bValid = RateIteration( fNper, fPayment, fPv, fFv, bPayType, fGuess);
+ }
+ }
+ }
+ if (!bValid)
+ SetError(FormulaError::NoConvergence);
+ }
+ PushDouble(fGuess);
+}
+
+double ScInterpreter::ScGetIpmt(double fRate, double fPer, double fNper, double fPv,
+ double fFv, bool bPayInAdvance, double& fPmt)
+{
+ fPmt = ScGetPMT(fRate, fNper, fPv, fFv, bPayInAdvance); // for PPMT also if fPer == 1
+ double fIpmt;
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ if (fPer == 1.0)
+ fIpmt = bPayInAdvance ? 0.0 : -fPv;
+ else
+ {
+ if (bPayInAdvance)
+ fIpmt = ScGetFV(fRate, fPer-2.0, fPmt, fPv, true) - fPmt;
+ else
+ fIpmt = ScGetFV(fRate, fPer-1.0, fPmt, fPv, false);
+ }
+ return fIpmt * fRate;
+}
+
+void ScInterpreter::ScIpmt()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
+ return;
+ bool bPayInAdvance = nParamCount == 6 && GetBool();
+ double fFv = nParamCount >= 5 ? GetDouble() : 0;
+ double fPv = GetDouble();
+ double fNper = GetDouble();
+ double fPer = GetDouble();
+ double fRate = GetDouble();
+ if (fPer < 1.0 || fPer > fNper)
+ PushIllegalArgument();
+ else
+ {
+ double fPmt;
+ PushDouble(ScGetIpmt(fRate, fPer, fNper, fPv, fFv, bPayInAdvance, fPmt));
+ }
+}
+
+void ScInterpreter::ScPpmt()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
+ return;
+ bool bPayInAdvance = nParamCount == 6 && GetBool();
+ double fFv = nParamCount >= 5 ? GetDouble() : 0;
+ double fPv = GetDouble();
+ double fNper = GetDouble();
+ double fPer = GetDouble();
+ double fRate = GetDouble();
+ if (fPer < 1.0 || fPer > fNper)
+ PushIllegalArgument();
+ else
+ {
+ double fPmt;
+ double fInterestPer = ScGetIpmt(fRate, fPer, fNper, fPv, fFv, bPayInAdvance, fPmt);
+ PushDouble(fPmt - fInterestPer);
+ }
+}
+
+void ScInterpreter::ScCumIpmt()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ if ( !MustHaveParamCount( GetByte(), 6 ) )
+ return;
+
+ double fFlag = GetDoubleWithDefault( -1.0 );
+ double fEnd = ::rtl::math::approxFloor(GetDouble());
+ double fStart = ::rtl::math::approxFloor(GetDouble());
+ double fPv = GetDouble();
+ double fNper = GetDouble();
+ double fRate = GetDouble();
+ if (fStart < 1.0 || fEnd < fStart || fRate <= 0.0 ||
+ fEnd > fNper || fNper <= 0.0 || fPv <= 0.0 ||
+ ( fFlag != 0.0 && fFlag != 1.0 ))
+ PushIllegalArgument();
+ else
+ {
+ bool bPayInAdvance = static_cast<bool>(fFlag);
+ sal_uLong nStart = static_cast<sal_uLong>(fStart);
+ sal_uLong nEnd = static_cast<sal_uLong>(fEnd) ;
+ double fPmt = ScGetPMT(fRate, fNper, fPv, 0.0, bPayInAdvance);
+ KahanSum fIpmt = 0.0;
+ if (nStart == 1)
+ {
+ if (!bPayInAdvance)
+ fIpmt = -fPv;
+ nStart++;
+ }
+ for (sal_uLong i = nStart; i <= nEnd; i++)
+ {
+ if (bPayInAdvance)
+ fIpmt += ScGetFV(fRate, static_cast<double>(i-2), fPmt, fPv, true) - fPmt;
+ else
+ fIpmt += ScGetFV(fRate, static_cast<double>(i-1), fPmt, fPv, false);
+ }
+ fIpmt *= fRate;
+ PushDouble(fIpmt.get());
+ }
+}
+
+void ScInterpreter::ScCumPrinc()
+{
+ nFuncFmtType = SvNumFormatType::CURRENCY;
+ if ( !MustHaveParamCount( GetByte(), 6 ) )
+ return;
+
+ double fFlag = GetDoubleWithDefault( -1.0 );
+ double fEnd = ::rtl::math::approxFloor(GetDouble());
+ double fStart = ::rtl::math::approxFloor(GetDouble());
+ double fPv = GetDouble();
+ double fNper = GetDouble();
+ double fRate = GetDouble();
+ if (fStart < 1.0 || fEnd < fStart || fRate <= 0.0 ||
+ fEnd > fNper || fNper <= 0.0 || fPv <= 0.0 ||
+ ( fFlag != 0.0 && fFlag != 1.0 ))
+ PushIllegalArgument();
+ else
+ {
+ bool bPayInAdvance = static_cast<bool>(fFlag);
+ double fPmt = ScGetPMT(fRate, fNper, fPv, 0.0, bPayInAdvance);
+ KahanSum fPpmt = 0.0;
+ sal_uLong nStart = static_cast<sal_uLong>(fStart);
+ sal_uLong nEnd = static_cast<sal_uLong>(fEnd);
+ if (nStart == 1)
+ {
+ fPpmt = bPayInAdvance ? fPmt : fPmt + fPv * fRate;
+ nStart++;
+ }
+ for (sal_uLong i = nStart; i <= nEnd; i++)
+ {
+ if (bPayInAdvance)
+ fPpmt += fPmt - (ScGetFV(fRate, static_cast<double>(i-2), fPmt, fPv, true) - fPmt) * fRate;
+ else
+ fPpmt += fPmt - ScGetFV(fRate, static_cast<double>(i-1), fPmt, fPv, false) * fRate;
+ }
+ PushDouble(fPpmt.get());
+ }
+}
+
+void ScInterpreter::ScEffect()
+{
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double fPeriods = GetDouble();
+ double fNominal = GetDouble();
+ if (fPeriods < 1.0 || fNominal < 0.0)
+ PushIllegalArgument();
+ else if ( fNominal == 0.0 )
+ PushDouble( 0.0 );
+ else
+ {
+ fPeriods = ::rtl::math::approxFloor(fPeriods);
+ PushDouble(pow(1.0 + fNominal/fPeriods, fPeriods) - 1.0);
+ }
+}
+
+void ScInterpreter::ScNominal()
+{
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ double fPeriods = GetDouble();
+ double fEffective = GetDouble();
+ if (fPeriods < 1.0 || fEffective <= 0.0)
+ PushIllegalArgument();
+ else
+ {
+ fPeriods = ::rtl::math::approxFloor(fPeriods);
+ PushDouble( (pow(fEffective + 1.0, 1.0 / fPeriods) - 1.0) * fPeriods );
+ }
+ }
+}
+
+void ScInterpreter::ScMod()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double fDenom = GetDouble(); // Denominator
+ if ( fDenom == 0.0 )
+ {
+ PushError(FormulaError::DivisionByZero);
+ return;
+ }
+ double fNum = GetDouble(); // Numerator
+ double fRes = ::rtl::math::approxSub( fNum,
+ ::rtl::math::approxFloor( fNum / fDenom ) * fDenom );
+ if ( ( fDenom > 0 && fRes >= 0 && fRes < fDenom ) ||
+ ( fDenom < 0 && fRes <= 0 && fRes > fDenom ) )
+ PushDouble( fRes );
+ else
+ PushError( FormulaError::NoValue );
+}
+
+void ScInterpreter::ScIntersect()
+{
+ formula::FormulaConstTokenRef p2nd = PopToken();
+ formula::FormulaConstTokenRef p1st = PopToken();
+
+ if (nGlobalError != FormulaError::NONE || !p2nd || !p1st)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ StackVar sv1 = p1st->GetType();
+ StackVar sv2 = p2nd->GetType();
+ if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
+ (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ const formula::FormulaToken* x1 = p1st.get();
+ const formula::FormulaToken* x2 = p2nd.get();
+ if (sv1 == svRefList || sv2 == svRefList)
+ {
+ // Now this is a bit nasty but it simplifies things, and having
+ // intersections with lists isn't too common, if at all...
+ // Convert a reference to list.
+ const formula::FormulaToken* xt[2] = { x1, x2 };
+ StackVar sv[2] = { sv1, sv2 };
+ // There may only be one reference; the other is necessarily a list
+ // Ensure converted list proper destruction
+ std::unique_ptr<formula::FormulaToken> p;
+ for (size_t i=0; i<2; ++i)
+ {
+ if (sv[i] == svSingleRef)
+ {
+ ScComplexRefData aRef;
+ aRef.Ref1 = aRef.Ref2 = *xt[i]->GetSingleRef();
+ p.reset(new ScRefListToken);
+ p->GetRefList()->push_back( aRef);
+ xt[i] = p.get();
+ }
+ else if (sv[i] == svDoubleRef)
+ {
+ ScComplexRefData aRef = *xt[i]->GetDoubleRef();
+ p.reset(new ScRefListToken);
+ p->GetRefList()->push_back( aRef);
+ xt[i] = p.get();
+ }
+ }
+ x1 = xt[0];
+ x2 = xt[1];
+
+ ScTokenRef xRes = new ScRefListToken;
+ ScRefList* pRefList = xRes->GetRefList();
+ for (const auto& rRef1 : *x1->GetRefList())
+ {
+ const ScAddress& r11 = rRef1.Ref1.toAbs(mrDoc, aPos);
+ const ScAddress& r12 = rRef1.Ref2.toAbs(mrDoc, aPos);
+ for (const auto& rRef2 : *x2->GetRefList())
+ {
+ const ScAddress& r21 = rRef2.Ref1.toAbs(mrDoc, aPos);
+ const ScAddress& r22 = rRef2.Ref2.toAbs(mrDoc, aPos);
+ SCCOL nCol1 = ::std::max( r11.Col(), r21.Col());
+ SCROW nRow1 = ::std::max( r11.Row(), r21.Row());
+ SCTAB nTab1 = ::std::max( r11.Tab(), r21.Tab());
+ SCCOL nCol2 = ::std::min( r12.Col(), r22.Col());
+ SCROW nRow2 = ::std::min( r12.Row(), r22.Row());
+ SCTAB nTab2 = ::std::min( r12.Tab(), r22.Tab());
+ if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
+ ; // nothing
+ else
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pRefList->push_back( aRef);
+ }
+ }
+ }
+ size_t n = pRefList->size();
+ if (!n)
+ PushError( FormulaError::NoCode);
+ else if (n == 1)
+ {
+ const ScComplexRefData& rRef = (*pRefList)[0];
+ if (rRef.Ref1 == rRef.Ref2)
+ PushTempToken( new ScSingleRefToken(mrDoc.GetSheetLimits(), rRef.Ref1));
+ else
+ PushTempToken( new ScDoubleRefToken(mrDoc.GetSheetLimits(), rRef));
+ }
+ else
+ PushTokenRef( xRes);
+ }
+ else
+ {
+ const formula::FormulaToken* pt[2] = { x1, x2 };
+ StackVar sv[2] = { sv1, sv2 };
+ SCCOL nC1[2], nC2[2];
+ SCROW nR1[2], nR2[2];
+ SCTAB nT1[2], nT2[2];
+ for (size_t i=0; i<2; ++i)
+ {
+ switch (sv[i])
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ {
+ {
+ const ScAddress& r = pt[i]->GetSingleRef()->toAbs(mrDoc, aPos);
+ nC1[i] = r.Col();
+ nR1[i] = r.Row();
+ nT1[i] = r.Tab();
+ }
+ if (sv[i] == svDoubleRef)
+ {
+ const ScAddress& r = pt[i]->GetSingleRef2()->toAbs(mrDoc, aPos);
+ nC2[i] = r.Col();
+ nR2[i] = r.Row();
+ nT2[i] = r.Tab();
+ }
+ else
+ {
+ nC2[i] = nC1[i];
+ nR2[i] = nR1[i];
+ nT2[i] = nT1[i];
+ }
+ }
+ break;
+ default:
+ ; // nothing, prevent compiler warning
+ }
+ }
+ SCCOL nCol1 = ::std::max( nC1[0], nC1[1]);
+ SCROW nRow1 = ::std::max( nR1[0], nR1[1]);
+ SCTAB nTab1 = ::std::max( nT1[0], nT1[1]);
+ SCCOL nCol2 = ::std::min( nC2[0], nC2[1]);
+ SCROW nRow2 = ::std::min( nR2[0], nR2[1]);
+ SCTAB nTab2 = ::std::min( nT2[0], nT2[1]);
+ if (nCol2 < nCol1 || nRow2 < nRow1 || nTab2 < nTab1)
+ PushError( FormulaError::NoCode);
+ else if (nCol2 == nCol1 && nRow2 == nRow1 && nTab2 == nTab1)
+ PushSingleRef( nCol1, nRow1, nTab1);
+ else
+ PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ }
+}
+
+void ScInterpreter::ScRangeFunc()
+{
+ formula::FormulaConstTokenRef x2 = PopToken();
+ formula::FormulaConstTokenRef x1 = PopToken();
+
+ if (nGlobalError != FormulaError::NONE || !x2 || !x1)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ // We explicitly tell extendRangeReference() to not reuse the token,
+ // casting const away spares two clones.
+ FormulaTokenRef xRes = extendRangeReference(
+ mrDoc.GetSheetLimits(), const_cast<FormulaToken&>(*x1), const_cast<FormulaToken&>(*x2), aPos, false);
+ if (!xRes)
+ PushIllegalArgument();
+ else
+ PushTokenRef( xRes);
+}
+
+void ScInterpreter::ScUnionFunc()
+{
+ formula::FormulaConstTokenRef p2nd = PopToken();
+ formula::FormulaConstTokenRef p1st = PopToken();
+
+ if (nGlobalError != FormulaError::NONE || !p2nd || !p1st)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ StackVar sv1 = p1st->GetType();
+ StackVar sv2 = p2nd->GetType();
+ if ((sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList) ||
+ (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList))
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ const formula::FormulaToken* x1 = p1st.get();
+ const formula::FormulaToken* x2 = p2nd.get();
+
+ ScTokenRef xRes;
+ // Append to an existing RefList if there is one.
+ if (sv1 == svRefList)
+ {
+ xRes = x1->Clone();
+ sv1 = svUnknown; // mark as handled
+ }
+ else if (sv2 == svRefList)
+ {
+ xRes = x2->Clone();
+ sv2 = svUnknown; // mark as handled
+ }
+ else
+ xRes = new ScRefListToken;
+ ScRefList* pRes = xRes->GetRefList();
+ const formula::FormulaToken* pt[2] = { x1, x2 };
+ StackVar sv[2] = { sv1, sv2 };
+ for (size_t i=0; i<2; ++i)
+ {
+ if (pt[i] == xRes)
+ continue;
+ switch (sv[i])
+ {
+ case svSingleRef:
+ {
+ ScComplexRefData aRef;
+ aRef.Ref1 = aRef.Ref2 = *pt[i]->GetSingleRef();
+ pRes->push_back( aRef);
+ }
+ break;
+ case svDoubleRef:
+ pRes->push_back( *pt[i]->GetDoubleRef());
+ break;
+ case svRefList:
+ {
+ const ScRefList* p = pt[i]->GetRefList();
+ for (const auto& rRef : *p)
+ {
+ pRes->push_back( rRef);
+ }
+ }
+ break;
+ default:
+ ; // nothing, prevent compiler warning
+ }
+ }
+ ValidateRef( *pRes); // set #REF! if needed
+ PushTokenRef( xRes);
+}
+
+void ScInterpreter::ScCurrent()
+{
+ FormulaConstTokenRef xTok( PopToken());
+ if (xTok)
+ {
+ PushTokenRef( xTok);
+ PushTokenRef( xTok);
+ }
+ else
+ PushError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::ScStyle()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount(nParamCount, 1, 3))
+ return;
+
+ OUString aStyle2; // Style after timer
+ if (nParamCount >= 3)
+ aStyle2 = GetString().getString();
+ tools::Long nTimeOut = 0; // timeout
+ if (nParamCount >= 2)
+ nTimeOut = static_cast<tools::Long>(GetDouble()*1000.0);
+ OUString aStyle1 = GetString().getString(); // Style for immediate
+
+ if (nTimeOut < 0)
+ nTimeOut = 0;
+
+ // Execute request to apply style
+ if ( !mrDoc.IsClipOrUndo() )
+ {
+ ScDocShell* pShell = mrDoc.GetDocumentShell();
+ if (pShell)
+ {
+ // Normalize style names right here, making sure that character case is correct,
+ // and that we only apply anything when there's something to apply
+ auto pPool = mrDoc.GetStyleSheetPool();
+ if (!aStyle1.isEmpty())
+ {
+ if (auto pNewStyle = pPool->FindAutoStyle(aStyle1))
+ aStyle1 = pNewStyle->GetName();
+ else
+ aStyle1.clear();
+ }
+ if (!aStyle2.isEmpty())
+ {
+ if (auto pNewStyle = pPool->FindAutoStyle(aStyle2))
+ aStyle2 = pNewStyle->GetName();
+ else
+ aStyle2.clear();
+ }
+ // notify object shell directly!
+ if (!aStyle1.isEmpty() || !aStyle2.isEmpty())
+ {
+ const ScStyleSheet* pStyle = mrDoc.GetStyle(aPos.Col(), aPos.Row(), aPos.Tab());
+
+ const bool bNotify = !pStyle
+ || (!aStyle1.isEmpty() && pStyle->GetName() != aStyle1)
+ || (!aStyle2.isEmpty() && pStyle->GetName() != aStyle2);
+ if (bNotify)
+ {
+ ScRange aRange(aPos);
+ ScAutoStyleHint aHint(aRange, aStyle1, nTimeOut, aStyle2);
+ pShell->Broadcast(aHint);
+ }
+ }
+ }
+ }
+
+ PushDouble(0.0);
+}
+
+static ScDdeLink* lcl_GetDdeLink( const sfx2::LinkManager* pLinkMgr,
+ std::u16string_view rA, std::u16string_view rT, std::u16string_view rI, sal_uInt8 nM )
+{
+ size_t nCount = pLinkMgr->GetLinks().size();
+ for (size_t i=0; i<nCount; i++ )
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkMgr->GetLinks()[i].get();
+ if (ScDdeLink* pLink = dynamic_cast<ScDdeLink*>(pBase))
+ {
+ if ( pLink->GetAppl() == rA &&
+ pLink->GetTopic() == rT &&
+ pLink->GetItem() == rI &&
+ pLink->GetMode() == nM )
+ return pLink;
+ }
+ }
+
+ return nullptr;
+}
+
+void ScInterpreter::ScDde()
+{
+ // application, file, scope
+ // application, Topic, Item
+
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
+ return;
+
+ sal_uInt8 nMode = SC_DDE_DEFAULT;
+ if (nParamCount == 4)
+ {
+ sal_uInt32 nTmp = GetUInt32();
+ if (nGlobalError != FormulaError::NONE || nTmp > SAL_MAX_UINT8)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ nMode = static_cast<sal_uInt8>(nTmp);
+ }
+ OUString aItem = GetString().getString();
+ OUString aTopic = GetString().getString();
+ OUString aAppl = GetString().getString();
+
+ if (nMode > SC_DDE_TEXT)
+ nMode = SC_DDE_DEFAULT;
+
+ // temporary documents (ScFunctionAccess) have no DocShell
+ // and no LinkManager -> abort
+
+ //sfx2::LinkManager* pLinkMgr = mrDoc.GetLinkManager();
+ if (!mpLinkManager)
+ {
+ PushNoValue();
+ return;
+ }
+
+ // Need to reinterpret after loading (build links)
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+
+ // while the link is not evaluated, idle must be disabled (to avoid circular references)
+
+ bool bOldEnabled = mrDoc.IsIdleEnabled();
+ mrDoc.EnableIdle(false);
+
+ // Get/ Create link object
+
+ ScDdeLink* pLink = lcl_GetDdeLink( mpLinkManager, aAppl, aTopic, aItem, nMode );
+
+ //TODO: Save Dde-links (in addition) more efficient at document !!!!!
+ // ScDdeLink* pLink = mrDoc.GetDdeLink( aAppl, aTopic, aItem );
+
+ bool bWasError = ( pMyFormulaCell && pMyFormulaCell->GetRawError() != FormulaError::NONE );
+
+ if (!pLink)
+ {
+ pLink = new ScDdeLink( mrDoc, aAppl, aTopic, aItem, nMode );
+ mpLinkManager->InsertDDELink( pLink, aAppl, aTopic, aItem );
+ if ( mpLinkManager->GetLinks().size() == 1 ) // the first one?
+ {
+ SfxBindings* pBindings = mrDoc.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS ); // Link-Manager enabled
+ }
+
+ //if the document was just loaded, but the ScDdeLink entry was missing, then
+ //don't update this link until the links are updated in response to the users
+ //decision
+ if (!mrDoc.HasLinkFormulaNeedingCheck())
+ {
+ //TODO: evaluate asynchron ???
+ pLink->TryUpdate(); // TryUpdate doesn't call Update multiple times
+ }
+
+ if (pMyFormulaCell)
+ {
+ // StartListening after the Update to avoid circular references
+ pMyFormulaCell->StartListening( *pLink );
+ }
+ }
+ else
+ {
+ if (pMyFormulaCell)
+ pMyFormulaCell->StartListening( *pLink );
+ }
+
+ // If a new Error from Reschedule appears when the link is executed then reset the errorflag
+
+
+ if ( pMyFormulaCell && pMyFormulaCell->GetRawError() != FormulaError::NONE && !bWasError )
+ pMyFormulaCell->SetErrCode(FormulaError::NONE);
+
+ // check the value
+
+ const ScMatrix* pLinkMat = pLink->GetResult();
+ if (pLinkMat)
+ {
+ SCSIZE nC, nR;
+ pLinkMat->GetDimensions(nC, nR);
+ ScMatrixRef pNewMat = GetNewMat( nC, nR, /*bEmpty*/true);
+ if (pNewMat)
+ {
+ pLinkMat->MatCopy(*pNewMat); // copy
+ PushMatrix( pNewMat );
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ PushNA();
+
+ mrDoc.EnableIdle(bOldEnabled);
+ mpLinkManager->CloseCachedComps();
+}
+
+void ScInterpreter::ScBase()
+{ // Value, Base [, MinLen]
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+
+ static const sal_Unicode pDigits[] = {
+ '0','1','2','3','4','5','6','7','8','9',
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M',
+ 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
+ 0
+ };
+ static const int nDigits = SAL_N_ELEMENTS(pDigits) - 1;
+ sal_Int32 nMinLen;
+ if ( nParamCount == 3 )
+ {
+ double fLen = ::rtl::math::approxFloor( GetDouble() );
+ if ( 1.0 <= fLen && fLen < SAL_MAX_UINT16 )
+ nMinLen = static_cast<sal_Int32>(fLen);
+ else
+ nMinLen = fLen == 0.0 ? 1 : 0; // 0 means error
+ }
+ else
+ nMinLen = 1;
+ double fBase = ::rtl::math::approxFloor( GetDouble() );
+ double fVal = ::rtl::math::approxFloor( GetDouble() );
+ double fChars = ((fVal > 0.0 && fBase > 0.0) ?
+ (ceil( log( fVal ) / log( fBase ) ) + 2.0) :
+ 2.0);
+ if ( fChars >= SAL_MAX_UINT16 )
+ nMinLen = 0; // Error
+
+ if ( nGlobalError == FormulaError::NONE && nMinLen && 2 <= fBase && fBase <= nDigits && 0 <= fVal )
+ {
+ const sal_Int32 nConstBuf = 128;
+ sal_Unicode aBuf[nConstBuf];
+ sal_Int32 nBuf = std::max<sal_Int32>( fChars, nMinLen + 1 );
+ sal_Unicode* pBuf = (nBuf <= nConstBuf ? aBuf : new sal_Unicode[nBuf]);
+ for ( sal_Int32 j = 0; j < nBuf; ++j )
+ {
+ pBuf[j] = '0';
+ }
+ sal_Unicode* p = pBuf + nBuf - 1;
+ *p = 0;
+ if ( o3tl::convertsToAtMost(fVal, sal_uLong(~0)) )
+ {
+ sal_uLong nVal = static_cast<sal_uLong>(fVal);
+ sal_uLong nBase = static_cast<sal_uLong>(fBase);
+ while ( nVal && p > pBuf )
+ {
+ *--p = pDigits[ nVal % nBase ];
+ nVal /= nBase;
+ }
+ fVal = static_cast<double>(nVal);
+ }
+ else
+ {
+ bool bDirt = false;
+ while ( fVal && p > pBuf )
+ {
+//TODO: roundoff error starting with numbers greater than 2**48
+// double fDig = ::rtl::math::approxFloor( fmod( fVal, fBase ) );
+// a little bit better:
+ double fInt = ::rtl::math::approxFloor( fVal / fBase );
+ double fMult = fInt * fBase;
+#if 0
+ // =BASIS(1e308;36) => GPF with
+ // nDig = (size_t) ::rtl::math::approxFloor( fVal - fMult );
+ // in spite off previous test if fVal >= fMult
+ double fDebug1 = fVal - fMult;
+ // fVal := 7,5975311883090e+290
+ // fMult := 7,5975311883090e+290
+ // fDebug1 := 1,3848924157003e+275 <- RoundOff-Error
+ // fVal != fMult, aber: ::rtl::math::approxEqual( fVal, fMult ) == TRUE
+ double fDebug2 = ::rtl::math::approxSub( fVal, fMult );
+ // and ::rtl::math::approxSub( fVal, fMult ) == 0
+ double fDebug3 = ( fInt ? fVal / fInt : 0.0 );
+
+ // Actual after strange fDebug1 and fVal < fMult is fDebug2 == fBase, but
+ // anyway it can't be compared, then bDirt is executed an everything is good...
+
+ // prevent compiler warnings
+ (void)fDebug1; (void)fDebug2; (void)fDebug3;
+#endif
+ size_t nDig;
+ if ( fVal < fMult )
+ { // something is wrong there
+ bDirt = true;
+ nDig = 0;
+ }
+ else
+ {
+ double fDig = ::rtl::math::approxFloor( ::rtl::math::approxSub( fVal, fMult ) );
+ if ( bDirt )
+ {
+ bDirt = false;
+ --fDig;
+ }
+ if ( fDig <= 0.0 )
+ nDig = 0;
+ else if ( fDig >= fBase )
+ nDig = static_cast<size_t>(fBase) - 1;
+ else
+ nDig = static_cast<size_t>(fDig);
+ }
+ *--p = pDigits[ nDig ];
+ fVal = fInt;
+ }
+ }
+ if ( fVal )
+ PushError( FormulaError::StringOverflow );
+ else
+ {
+ if ( nBuf - (p - pBuf) <= nMinLen )
+ p = pBuf + nBuf - 1 - nMinLen;
+ PushStringBuffer( p );
+ }
+ if ( pBuf != aBuf )
+ delete [] pBuf;
+ }
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScDecimal()
+{ // Text, Base
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double fBase = ::rtl::math::approxFloor( GetDouble() );
+ OUString aStr = GetString().getString();
+ if ( nGlobalError == FormulaError::NONE && 2 <= fBase && fBase <= 36 )
+ {
+ double fVal = 0.0;
+ int nBase = static_cast<int>(fBase);
+ const sal_Unicode* p = aStr.getStr();
+ while ( *p == ' ' || *p == '\t' )
+ p++; // strip leading white space
+ if ( nBase == 16 )
+ { // evtl. hex-prefix stripped
+ if ( *p == 'x' || *p == 'X' )
+ p++;
+ else if ( *p == '0' && (*(p+1) == 'x' || *(p+1) == 'X') )
+ p += 2;
+ }
+ while ( *p )
+ {
+ int n;
+ if ( '0' <= *p && *p <= '9' )
+ n = *p - '0';
+ else if ( 'A' <= *p && *p <= 'Z' )
+ n = 10 + (*p - 'A');
+ else if ( 'a' <= *p && *p <= 'z' )
+ n = 10 + (*p - 'a');
+ else
+ n = nBase;
+ if ( nBase <= n )
+ {
+ if ( *(p+1) == 0 &&
+ ( (nBase == 2 && (*p == 'b' || *p == 'B'))
+ ||(nBase == 16 && (*p == 'h' || *p == 'H')) )
+ )
+ ; // 101b and F00Dh are ok
+ else
+ {
+ PushIllegalArgument();
+ return ;
+ }
+ }
+ else
+ fVal = fVal * fBase + n;
+ p++;
+
+ }
+ PushDouble( fVal );
+ }
+ else
+ PushIllegalArgument();
+}
+
+void ScInterpreter::ScConvertOOo()
+{ // Value, FromUnit, ToUnit
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ OUString aToUnit = GetString().getString();
+ OUString aFromUnit = GetString().getString();
+ double fVal = GetDouble();
+ if ( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError);
+ else
+ {
+ // first of all search for the given order; if it can't be found then search for the inverse
+ double fConv;
+ if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aFromUnit, aToUnit ) )
+ PushDouble( fVal * fConv );
+ else if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aToUnit, aFromUnit ) )
+ PushDouble( fVal / fConv );
+ else
+ PushNA();
+ }
+}
+
+void ScInterpreter::ScRoman()
+{ // Value [Mode]
+ sal_uInt8 nParamCount = GetByte();
+ if( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ double fMode = (nParamCount == 2) ? ::rtl::math::approxFloor( GetDouble() ) : 0.0;
+ double fVal = ::rtl::math::approxFloor( GetDouble() );
+ if( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError);
+ else if( (fMode >= 0.0) && (fMode < 5.0) && (fVal >= 0.0) && (fVal < 4000.0) )
+ {
+ static const sal_Unicode pChars[] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
+ static const sal_uInt16 pValues[] = { 1000, 500, 100, 50, 10, 5, 1 };
+ static const sal_uInt16 nMaxIndex = sal_uInt16(SAL_N_ELEMENTS(pValues) - 1);
+
+ OUStringBuffer aRoman;
+ sal_uInt16 nVal = static_cast<sal_uInt16>(fVal);
+ sal_uInt16 nMode = static_cast<sal_uInt16>(fMode);
+
+ for( sal_uInt16 i = 0; i <= nMaxIndex / 2; i++ )
+ {
+ sal_uInt16 nIndex = 2 * i;
+ sal_uInt16 nDigit = nVal / pValues[ nIndex ];
+
+ if( (nDigit % 5) == 4 )
+ {
+ // assert can't happen with nVal<4000 precondition
+ assert( ((nDigit == 4) ? (nIndex >= 1) : (nIndex >= 2)));
+
+ sal_uInt16 nIndex2 = (nDigit == 4) ? nIndex - 1 : nIndex - 2;
+ sal_uInt16 nSteps = 0;
+ while( (nSteps < nMode) && (nIndex < nMaxIndex) )
+ {
+ nSteps++;
+ if( pValues[ nIndex2 ] - pValues[ nIndex + 1 ] <= nVal )
+ nIndex++;
+ else
+ nSteps = nMode;
+ }
+ aRoman.append( OUStringChar(pChars[ nIndex ]) + OUStringChar(pChars[ nIndex2 ]) );
+ nVal = sal::static_int_cast<sal_uInt16>( nVal + pValues[ nIndex ] );
+ nVal = sal::static_int_cast<sal_uInt16>( nVal - pValues[ nIndex2 ] );
+ }
+ else
+ {
+ if( nDigit > 4 )
+ {
+ // assert can't happen with nVal<4000 precondition
+ assert( nIndex >= 1 );
+ aRoman.append( pChars[ nIndex - 1 ] );
+ }
+ sal_Int32 nPad = nDigit % 5;
+ if (nPad)
+ {
+ comphelper::string::padToLength(aRoman, aRoman.getLength() + nPad,
+ pChars[nIndex]);
+ }
+ nVal %= pValues[ nIndex ];
+ }
+ }
+
+ PushString( aRoman.makeStringAndClear() );
+ }
+ else
+ PushIllegalArgument();
+}
+
+static bool lcl_GetArabicValue( sal_Unicode cChar, sal_uInt16& rnValue, bool& rbIsDec )
+{
+ switch( cChar )
+ {
+ case 'M': rnValue = 1000; rbIsDec = true; break;
+ case 'D': rnValue = 500; rbIsDec = false; break;
+ case 'C': rnValue = 100; rbIsDec = true; break;
+ case 'L': rnValue = 50; rbIsDec = false; break;
+ case 'X': rnValue = 10; rbIsDec = true; break;
+ case 'V': rnValue = 5; rbIsDec = false; break;
+ case 'I': rnValue = 1; rbIsDec = true; break;
+ default: return false;
+ }
+ return true;
+}
+
+void ScInterpreter::ScArabic()
+{
+ OUString aRoman = GetString().getString();
+ if( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError);
+ else
+ {
+ aRoman = aRoman.toAsciiUpperCase();
+
+ sal_uInt16 nValue = 0;
+ sal_uInt16 nValidRest = 3999;
+ sal_Int32 nCharIndex = 0;
+ sal_Int32 nCharCount = aRoman.getLength();
+ bool bValid = true;
+
+ while( bValid && (nCharIndex < nCharCount) )
+ {
+ sal_uInt16 nDigit1 = 0;
+ sal_uInt16 nDigit2 = 0;
+ bool bIsDec1 = false;
+ bValid = lcl_GetArabicValue( aRoman[nCharIndex], nDigit1, bIsDec1 );
+ if( bValid && (nCharIndex + 1 < nCharCount) )
+ {
+ bool bIsDec2 = false;
+ bValid = lcl_GetArabicValue( aRoman[nCharIndex + 1], nDigit2, bIsDec2 );
+ }
+ if( bValid )
+ {
+ if( nDigit1 >= nDigit2 )
+ {
+ nValue = sal::static_int_cast<sal_uInt16>( nValue + nDigit1 );
+ nValidRest %= (nDigit1 * (bIsDec1 ? 5 : 2));
+ bValid = (nValidRest >= nDigit1);
+ if( bValid )
+ nValidRest = sal::static_int_cast<sal_uInt16>( nValidRest - nDigit1 );
+ nCharIndex++;
+ }
+ else if( nDigit1 * 2 != nDigit2 )
+ {
+ sal_uInt16 nDiff = nDigit2 - nDigit1;
+ nValue = sal::static_int_cast<sal_uInt16>( nValue + nDiff );
+ bValid = (nValidRest >= nDiff);
+ if( bValid )
+ nValidRest = nDigit1 - 1;
+ nCharIndex += 2;
+ }
+ else
+ bValid = false;
+ }
+ }
+ if( bValid )
+ PushInt( nValue );
+ else
+ PushIllegalArgument();
+ }
+}
+
+void ScInterpreter::ScHyperLink()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
+ return;
+
+ double fVal = 0.0;
+ svl::SharedString aStr;
+ ScMatValType nResultType = ScMatValType::String;
+
+ if ( nParamCount == 2 )
+ {
+ switch ( GetStackType() )
+ {
+ case svDouble:
+ fVal = GetDouble();
+ nResultType = ScMatValType::Value;
+ break;
+ case svString:
+ aStr = GetString();
+ break;
+ case svSingleRef:
+ case svDoubleRef:
+ {
+ ScAddress aAdr;
+ if ( !PopDoubleRefOrSingleRef( aAdr ) )
+ break;
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasEmptyValue())
+ nResultType = ScMatValType::Empty;
+ else
+ {
+ FormulaError nErr = GetCellErrCode(aCell);
+ if (nErr != FormulaError::NONE)
+ SetError( nErr);
+ else if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ nResultType = ScMatValType::Value;
+ }
+ else
+ GetCellString(aStr, aCell);
+ }
+ }
+ break;
+ case svMatrix:
+ nResultType = GetDoubleOrStringFromMatrix( fVal, aStr);
+ break;
+ case svMissing:
+ case svEmptyCell:
+ Pop();
+ // mimic xcl
+ fVal = 0.0;
+ nResultType = ScMatValType::Value;
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ svl::SharedString aUrl = GetString();
+ ScMatrixRef pResMat = GetNewMat( 1, 2);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ fVal = CreateDoubleError( nGlobalError);
+ nResultType = ScMatValType::Value;
+ }
+ if (nParamCount == 2 || nGlobalError != FormulaError::NONE)
+ {
+ if (ScMatrix::IsValueType( nResultType))
+ pResMat->PutDouble( fVal, 0);
+ else if (ScMatrix::IsRealStringType( nResultType))
+ pResMat->PutString(aStr, 0);
+ else // EmptyType, EmptyPathType, mimic xcl
+ pResMat->PutDouble( 0.0, 0 );
+ }
+ else
+ pResMat->PutString(aUrl, 0);
+ pResMat->PutString(aUrl, 1);
+ bMatrixFormula = true;
+ PushMatrix(pResMat);
+}
+
+/** Resources at the website of the European Commission:
+ http://ec.europa.eu/economy_finance/euro/adoption/conversion/
+ http://ec.europa.eu/economy_finance/euro/countries/
+ */
+static bool lclConvertMoney( std::u16string_view aSearchUnit, double& rfRate, int& rnDec )
+{
+ struct ConvertInfo
+ {
+ const char* pCurrText;
+ double fRate;
+ int nDec;
+ };
+ static const ConvertInfo aConvertTable[] = {
+ { "EUR", 1.0, 2 },
+ { "ATS", 13.7603, 2 },
+ { "BEF", 40.3399, 0 },
+ { "DEM", 1.95583, 2 },
+ { "ESP", 166.386, 0 },
+ { "FIM", 5.94573, 2 },
+ { "FRF", 6.55957, 2 },
+ { "IEP", 0.787564, 2 },
+ { "ITL", 1936.27, 0 },
+ { "LUF", 40.3399, 0 },
+ { "NLG", 2.20371, 2 },
+ { "PTE", 200.482, 2 },
+ { "GRD", 340.750, 2 },
+ { "SIT", 239.640, 2 },
+ { "MTL", 0.429300, 2 },
+ { "CYP", 0.585274, 2 },
+ { "SKK", 30.1260, 2 },
+ { "EEK", 15.6466, 2 },
+ { "LVL", 0.702804, 2 },
+ { "LTL", 3.45280, 2 },
+ { "HRK", 7.53450, 2 }
+ };
+
+ for (const auto & i : aConvertTable)
+ if ( o3tl::equalsIgnoreAsciiCase( aSearchUnit, i.pCurrText ) )
+ {
+ rfRate = i.fRate;
+ rnDec = i.nDec;
+ return true;
+ }
+ return false;
+}
+
+void ScInterpreter::ScEuroConvert()
+{ //Value, FromUnit, ToUnit[, FullPrecision, [TriangulationPrecision]]
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+
+ double fPrecision = 0.0;
+ if ( nParamCount == 5 )
+ {
+ fPrecision = ::rtl::math::approxFloor(GetDouble());
+ if ( fPrecision < 3 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+
+ bool bFullPrecision = nParamCount >= 4 && GetBool();
+ OUString aToUnit = GetString().getString();
+ OUString aFromUnit = GetString().getString();
+ double fVal = GetDouble();
+ if ( nGlobalError != FormulaError::NONE )
+ PushError( nGlobalError);
+ else
+ {
+ double fFromRate;
+ double fToRate;
+ int nFromDec;
+ int nToDec;
+ if ( lclConvertMoney( aFromUnit, fFromRate, nFromDec )
+ && lclConvertMoney( aToUnit, fToRate, nToDec ) )
+ {
+ double fRes;
+ if ( aFromUnit.equalsIgnoreAsciiCase( aToUnit ) )
+ fRes = fVal;
+ else
+ {
+ if ( aFromUnit.equalsIgnoreAsciiCase( "EUR" ) )
+ fRes = fVal * fToRate;
+ else
+ {
+ double fIntermediate = fVal / fFromRate;
+ if ( fPrecision )
+ fIntermediate = ::rtl::math::round( fIntermediate,
+ static_cast<int>(fPrecision) );
+ fRes = fIntermediate * fToRate;
+ }
+ if ( !bFullPrecision )
+ fRes = ::rtl::math::round( fRes, nToDec );
+ }
+ PushDouble( fRes );
+ }
+ else
+ PushIllegalArgument();
+ }
+}
+
+// BAHTTEXT
+#define UTF8_TH_0 "\340\270\250\340\270\271\340\270\231\340\270\242\340\271\214"
+#define UTF8_TH_1 "\340\270\253\340\270\231\340\270\266\340\271\210\340\270\207"
+#define UTF8_TH_2 "\340\270\252\340\270\255\340\270\207"
+#define UTF8_TH_3 "\340\270\252\340\270\262\340\270\241"
+#define UTF8_TH_4 "\340\270\252\340\270\265\340\271\210"
+#define UTF8_TH_5 "\340\270\253\340\271\211\340\270\262"
+#define UTF8_TH_6 "\340\270\253\340\270\201"
+#define UTF8_TH_7 "\340\271\200\340\270\210\340\271\207\340\270\224"
+#define UTF8_TH_8 "\340\271\201\340\270\233\340\270\224"
+#define UTF8_TH_9 "\340\271\200\340\270\201\340\271\211\340\270\262"
+#define UTF8_TH_10 "\340\270\252\340\270\264\340\270\232"
+#define UTF8_TH_11 "\340\271\200\340\270\255\340\271\207\340\270\224"
+#define UTF8_TH_20 "\340\270\242\340\270\265\340\271\210"
+#define UTF8_TH_1E2 "\340\270\243\340\271\211\340\270\255\340\270\242"
+#define UTF8_TH_1E3 "\340\270\236\340\270\261\340\270\231"
+#define UTF8_TH_1E4 "\340\270\253\340\270\241\340\270\267\340\271\210\340\270\231"
+#define UTF8_TH_1E5 "\340\271\201\340\270\252\340\270\231"
+#define UTF8_TH_1E6 "\340\270\245\340\271\211\340\270\262\340\270\231"
+#define UTF8_TH_DOT0 "\340\270\226\340\271\211\340\270\247\340\270\231"
+#define UTF8_TH_BAHT "\340\270\232\340\270\262\340\270\227"
+#define UTF8_TH_SATANG "\340\270\252\340\270\225\340\270\262\340\270\207\340\270\204\340\271\214"
+#define UTF8_TH_MINUS "\340\270\245\340\270\232"
+
+// local functions
+namespace {
+
+void lclSplitBlock( double& rfInt, sal_Int32& rnBlock, double fValue, double fSize )
+{
+ rnBlock = static_cast< sal_Int32 >( modf( (fValue + 0.1) / fSize, &rfInt ) * fSize + 0.1 );
+}
+
+/** Appends a digit (0 to 9) to the passed string. */
+void lclAppendDigit( OStringBuffer& rText, sal_Int32 nDigit )
+{
+ switch( nDigit )
+ {
+ case 0: rText.append( UTF8_TH_0 ); break;
+ case 1: rText.append( UTF8_TH_1 ); break;
+ case 2: rText.append( UTF8_TH_2 ); break;
+ case 3: rText.append( UTF8_TH_3 ); break;
+ case 4: rText.append( UTF8_TH_4 ); break;
+ case 5: rText.append( UTF8_TH_5 ); break;
+ case 6: rText.append( UTF8_TH_6 ); break;
+ case 7: rText.append( UTF8_TH_7 ); break;
+ case 8: rText.append( UTF8_TH_8 ); break;
+ case 9: rText.append( UTF8_TH_9 ); break;
+ default: OSL_FAIL( "lclAppendDigit - illegal digit" );
+ }
+}
+
+/** Appends a value raised to a power of 10: nDigit*10^nPow10.
+ @param nDigit A digit in the range from 1 to 9.
+ @param nPow10 A value in the range from 2 to 5.
+ */
+void lclAppendPow10( OStringBuffer& rText, sal_Int32 nDigit, sal_Int32 nPow10 )
+{
+ OSL_ENSURE( (1 <= nDigit) && (nDigit <= 9), "lclAppendPow10 - illegal digit" );
+ lclAppendDigit( rText, nDigit );
+ switch( nPow10 )
+ {
+ case 2: rText.append( UTF8_TH_1E2 ); break;
+ case 3: rText.append( UTF8_TH_1E3 ); break;
+ case 4: rText.append( UTF8_TH_1E4 ); break;
+ case 5: rText.append( UTF8_TH_1E5 ); break;
+ default: OSL_FAIL( "lclAppendPow10 - illegal power" );
+ }
+}
+
+/** Appends a block of 6 digits (value from 1 to 999,999) to the passed string. */
+void lclAppendBlock( OStringBuffer& rText, sal_Int32 nValue )
+{
+ OSL_ENSURE( (1 <= nValue) && (nValue <= 999999), "lclAppendBlock - illegal value" );
+ if( nValue >= 100000 )
+ {
+ lclAppendPow10( rText, nValue / 100000, 5 );
+ nValue %= 100000;
+ }
+ if( nValue >= 10000 )
+ {
+ lclAppendPow10( rText, nValue / 10000, 4 );
+ nValue %= 10000;
+ }
+ if( nValue >= 1000 )
+ {
+ lclAppendPow10( rText, nValue / 1000, 3 );
+ nValue %= 1000;
+ }
+ if( nValue >= 100 )
+ {
+ lclAppendPow10( rText, nValue / 100, 2 );
+ nValue %= 100;
+ }
+ if( nValue <= 0 )
+ return;
+
+ sal_Int32 nTen = nValue / 10;
+ sal_Int32 nOne = nValue % 10;
+ if( nTen >= 1 )
+ {
+ if( nTen >= 3 )
+ lclAppendDigit( rText, nTen );
+ else if( nTen == 2 )
+ rText.append( UTF8_TH_20 );
+ rText.append( UTF8_TH_10 );
+ }
+ if( (nTen > 0) && (nOne == 1) )
+ rText.append( UTF8_TH_11 );
+ else if( nOne > 0 )
+ lclAppendDigit( rText, nOne );
+}
+
+} // namespace
+
+void ScInterpreter::ScBahtText()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1 ) )
+ return;
+
+ double fValue = GetDouble();
+ if( nGlobalError != FormulaError::NONE )
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ // sign
+ bool bMinus = fValue < 0.0;
+ fValue = std::abs( fValue );
+
+ // round to 2 digits after decimal point, fValue contains Satang as integer
+ fValue = ::rtl::math::approxFloor( fValue * 100.0 + 0.5 );
+
+ // split Baht and Satang
+ double fBaht = 0.0;
+ sal_Int32 nSatang = 0;
+ lclSplitBlock( fBaht, nSatang, fValue, 100.0 );
+
+ OStringBuffer aText;
+
+ // generate text for Baht value
+ if( fBaht == 0.0 )
+ {
+ if( nSatang == 0 )
+ aText.append( UTF8_TH_0 );
+ }
+ else while( fBaht > 0.0 )
+ {
+ OStringBuffer aBlock;
+ sal_Int32 nBlock = 0;
+ lclSplitBlock( fBaht, nBlock, fBaht, 1.0e6 );
+ if( nBlock > 0 )
+ lclAppendBlock( aBlock, nBlock );
+ // add leading "million", if there will come more blocks
+ if( fBaht > 0.0 )
+ aBlock.insert( 0, UTF8_TH_1E6 );
+
+ aText.insert(0, aBlock);
+ }
+ if (!aText.isEmpty())
+ aText.append( UTF8_TH_BAHT );
+
+ // generate text for Satang value
+ if( nSatang == 0 )
+ {
+ aText.append( UTF8_TH_DOT0 );
+ }
+ else
+ {
+ lclAppendBlock( aText, nSatang );
+ aText.append( UTF8_TH_SATANG );
+ }
+
+ // add the minus sign
+ if( bMinus )
+ aText.insert( 0, UTF8_TH_MINUS );
+
+ PushString( OStringToOUString(aText, RTL_TEXTENCODING_UTF8) );
+}
+
+void ScInterpreter::ScGetPivotData()
+{
+ sal_uInt8 nParamCount = GetByte();
+
+ if (!MustHaveParamCountMin(nParamCount, 2) || (nParamCount % 2) == 1)
+ {
+ PushError(FormulaError::NoRef);
+ return;
+ }
+
+ bool bOldSyntax = false;
+ if (nParamCount == 2)
+ {
+ // if the first parameter is a ref, assume old syntax
+ StackVar eFirstType = GetStackType(2);
+ if (eFirstType == svSingleRef || eFirstType == svDoubleRef)
+ bOldSyntax = true;
+ }
+
+ std::vector<sheet::DataPilotFieldFilter> aFilters;
+ OUString aDataFieldName;
+ ScRange aBlock;
+
+ if (bOldSyntax)
+ {
+ aDataFieldName = GetString().getString();
+
+ switch (GetStackType())
+ {
+ case svDoubleRef :
+ PopDoubleRef(aBlock);
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAddr;
+ PopSingleRef(aAddr);
+ aBlock = aAddr;
+ }
+ break;
+ default:
+ PushError(FormulaError::NoRef);
+ return;
+ }
+ }
+ else
+ {
+ // Standard syntax: separate name/value pairs
+
+ sal_uInt16 nFilterCount = nParamCount / 2 - 1;
+ aFilters.resize(nFilterCount);
+
+ sal_uInt16 i = nFilterCount;
+ while (i-- > 0)
+ {
+ /* TODO: also, in case of numeric the entire filter match should
+ * not be on a (even if locale independent) formatted string down
+ * below in pDPObj->GetPivotData(). */
+
+ bool bEvaluateFormatIndex;
+ switch (GetRawStackType())
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ bEvaluateFormatIndex = true;
+ break;
+ default:
+ bEvaluateFormatIndex = false;
+ }
+
+ double fDouble;
+ svl::SharedString aSharedString;
+ bool bDouble = GetDoubleOrString( fDouble, aSharedString);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ if (bDouble)
+ {
+ sal_uInt32 nNumFormat;
+ if (bEvaluateFormatIndex && nCurFmtIndex)
+ nNumFormat = nCurFmtIndex;
+ else
+ {
+ if (nCurFmtType == SvNumFormatType::UNDEFINED)
+ nNumFormat = 0;
+ else
+ nNumFormat = pFormatter->GetStandardFormat( nCurFmtType, ScGlobal::eLnge);
+ }
+ const Color* pColor;
+ pFormatter->GetOutputString( fDouble, nNumFormat, aFilters[i].MatchValueName, &pColor);
+ aFilters[i].MatchValue = ScDPCache::GetLocaleIndependentFormattedString(
+ fDouble, *pFormatter, nNumFormat);
+ }
+ else
+ {
+ aFilters[i].MatchValueName = aSharedString.getString();
+
+ // Parse possible number from MatchValueName and format
+ // locale independent as MatchValue.
+ sal_uInt32 nNumFormat = 0;
+ double fValue;
+ if (pFormatter->IsNumberFormat( aFilters[i].MatchValueName, nNumFormat, fValue))
+ aFilters[i].MatchValue = ScDPCache::GetLocaleIndependentFormattedString(
+ fValue, *pFormatter, nNumFormat);
+ else
+ aFilters[i].MatchValue = aFilters[i].MatchValueName;
+ }
+
+ aFilters[i].FieldName = GetString().getString();
+ }
+
+ switch (GetStackType())
+ {
+ case svDoubleRef :
+ PopDoubleRef(aBlock);
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAddr;
+ PopSingleRef(aAddr);
+ aBlock = aAddr;
+ }
+ break;
+ default:
+ PushError(FormulaError::NoRef);
+ return;
+ }
+
+ aDataFieldName = GetString().getString(); // First parameter is data field name.
+ }
+
+ // Early bail-out, don't grind through data pilot cache and all.
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+
+ // NOTE : MS Excel docs claim to use the 'most recent' which is not
+ // exactly the same as what we do in ScDocument::GetDPAtBlock
+ // However we do need to use GetDPABlock
+ ScDPObject* pDPObj = mrDoc.GetDPAtBlock(aBlock);
+ if (!pDPObj)
+ {
+ PushError(FormulaError::NoRef);
+ return;
+ }
+
+ if (bOldSyntax)
+ {
+ OUString aFilterStr = aDataFieldName;
+ std::vector<sal_Int16> aFilterFuncs;
+ if (!pDPObj->ParseFilters(aDataFieldName, aFilters, aFilterFuncs, aFilterStr))
+ {
+ PushError(FormulaError::NoRef);
+ return;
+ }
+
+ // TODO : For now, we ignore filter functions since we couldn't find a
+ // live example of how they are supposed to be used. We'll support
+ // this again once we come across a real-world example.
+ }
+
+ double fVal = pDPObj->GetPivotData(aDataFieldName, aFilters);
+ if (std::isnan(fVal))
+ {
+ PushError(FormulaError::NoRef);
+ return;
+ }
+ PushDouble(fVal);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx
new file mode 100644
index 0000000000..88b32b44af
--- /dev/null
+++ b/sc/source/core/tool/interpr3.cxx
@@ -0,0 +1,5585 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/solar.h>
+#include <stdlib.h>
+
+#include <interpre.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <matrixoperators.hxx>
+#include <scmatrix.hxx>
+
+#include <cassert>
+#include <cmath>
+#include <memory>
+#include <set>
+#include <vector>
+#include <algorithm>
+#include <comphelper/random.hxx>
+#include <o3tl/float_int_conversion.hxx>
+#include <osl/diagnose.h>
+
+using ::std::vector;
+using namespace formula;
+
+/// Two columns of data should be sortable with GetSortArray() and QuickSort()
+// This is an arbitrary limit.
+static size_t MAX_COUNT_DOUBLE_FOR_SORT(const ScSheetLimits& rSheetLimits)
+{
+ return rSheetLimits.GetMaxRowCount() * 2;
+}
+
+const double ScInterpreter::fMaxGammaArgument = 171.624376956302; // found experimental
+const double fMachEps = ::std::numeric_limits<double>::epsilon();
+
+namespace {
+
+class ScDistFunc
+{
+public:
+ virtual double GetValue(double x) const = 0;
+
+protected:
+ ~ScDistFunc() {}
+};
+
+}
+
+// iteration for inverse distributions
+
+//template< class T > double lcl_IterateInverse( const T& rFunction, double x0, double x1, bool& rConvError )
+
+/** u*w<0.0 fails for values near zero */
+static bool lcl_HasChangeOfSign( double u, double w )
+{
+ return (u < 0.0 && w > 0.0) || (u > 0.0 && w < 0.0);
+}
+
+static double lcl_IterateInverse( const ScDistFunc& rFunction, double fAx, double fBx, bool& rConvError )
+{
+ rConvError = false;
+ const double fYEps = 1.0E-307;
+ const double fXEps = ::std::numeric_limits<double>::epsilon();
+
+ OSL_ENSURE(fAx<fBx, "IterateInverse: wrong interval");
+
+ // find enclosing interval
+
+ KahanSum fkAx = fAx;
+ KahanSum fkBx = fBx;
+ double fAy = rFunction.GetValue(fAx);
+ double fBy = rFunction.GetValue(fBx);
+ KahanSum fTemp;
+ unsigned short nCount;
+ for (nCount = 0; nCount < 1000 && !lcl_HasChangeOfSign(fAy,fBy); nCount++)
+ {
+ if (std::abs(fAy) <= std::abs(fBy))
+ {
+ fTemp = fkAx;
+ fkAx += (fkAx - fkBx) * 2.0;
+ if (fkAx < 0.0)
+ fkAx = 0.0;
+ fkBx = fTemp;
+ fBy = fAy;
+ fAy = rFunction.GetValue(fkAx.get());
+ }
+ else
+ {
+ fTemp = fkBx;
+ fkBx += (fkBx - fkAx) * 2.0;
+ fkAx = fTemp;
+ fAy = fBy;
+ fBy = rFunction.GetValue(fkBx.get());
+ }
+ }
+
+ fAx = fkAx.get();
+ fBx = fkBx.get();
+ if (fAy == 0.0)
+ return fAx;
+ if (fBy == 0.0)
+ return fBx;
+ if (!lcl_HasChangeOfSign( fAy, fBy))
+ {
+ rConvError = true;
+ return 0.0;
+ }
+ // inverse quadric interpolation with additional brackets
+ // set three points
+ double fPx = fAx;
+ double fPy = fAy;
+ double fQx = fBx;
+ double fQy = fBy;
+ double fRx = fAx;
+ double fRy = fAy;
+ double fSx = 0.5 * (fAx + fBx); // potential next point
+ bool bHasToInterpolate = true;
+ nCount = 0;
+ while ( nCount < 500 && std::abs(fRy) > fYEps &&
+ (fBx-fAx) > ::std::max( std::abs(fAx), std::abs(fBx)) * fXEps )
+ {
+ if (bHasToInterpolate)
+ {
+ if (fPy!=fQy && fQy!=fRy && fRy!=fPy)
+ {
+ fSx = fPx * fRy * fQy / (fRy-fPy) / (fQy-fPy)
+ + fRx * fQy * fPy / (fQy-fRy) / (fPy-fRy)
+ + fQx * fPy * fRy / (fPy-fQy) / (fRy-fQy);
+ bHasToInterpolate = (fAx < fSx) && (fSx < fBx); // inside the brackets?
+ }
+ else
+ bHasToInterpolate = false;
+ }
+ if(!bHasToInterpolate)
+ {
+ fSx = 0.5 * (fAx + fBx);
+ // reset points
+ fQx = fBx; fQy = fBy;
+ bHasToInterpolate = true;
+ }
+ // shift points for next interpolation
+ fPx = fQx; fQx = fRx; fRx = fSx;
+ fPy = fQy; fQy = fRy; fRy = rFunction.GetValue(fSx);
+ // update brackets
+ if (lcl_HasChangeOfSign( fAy, fRy))
+ {
+ fBx = fRx; fBy = fRy;
+ }
+ else
+ {
+ fAx = fRx; fAy = fRy;
+ }
+ // if last iteration brought too small advance, then do bisection next
+ // time, for safety
+ bHasToInterpolate = bHasToInterpolate && (std::abs(fRy) * 2.0 <= std::abs(fQy));
+ ++nCount;
+ }
+ return fRx;
+}
+
+// General functions
+
+void ScInterpreter::ScNoName()
+{
+ PushError(FormulaError::NoName);
+}
+
+void ScInterpreter::ScBadName()
+{
+ short nParamCount = GetByte();
+ while (nParamCount-- > 0)
+ {
+ PopError();
+ }
+ PushError( FormulaError::NoName);
+}
+
+double ScInterpreter::phi(double x)
+{
+ return 0.39894228040143268 * exp(-(x * x) / 2.0);
+}
+
+double ScInterpreter::integralPhi(double x)
+{ // Using gauss(x)+0.5 has severe cancellation errors for x<-4
+ return 0.5 * std::erfc(-x * M_SQRT1_2);
+}
+
+double ScInterpreter::taylor(const double* pPolynom, sal_uInt16 nMax, double x)
+{
+ KahanSum nVal = pPolynom[nMax];
+ for (short i = nMax-1; i >= 0; i--)
+ {
+ nVal = (nVal * x) + pPolynom[i];
+ }
+ return nVal.get();
+}
+
+double ScInterpreter::gauss(double x)
+{
+
+ double xAbs = std::abs(x);
+ sal_uInt16 xShort = static_cast<sal_uInt16>(::rtl::math::approxFloor(xAbs));
+ double nVal = 0.0;
+ if (xShort == 0)
+ {
+ static const double t0[] =
+ { 0.39894228040143268, -0.06649038006690545, 0.00997355701003582,
+ -0.00118732821548045, 0.00011543468761616, -0.00000944465625950,
+ 0.00000066596935163, -0.00000004122667415, 0.00000000227352982,
+ 0.00000000011301172, 0.00000000000511243, -0.00000000000021218 };
+ nVal = taylor(t0, 11, (xAbs * xAbs)) * xAbs;
+ }
+ else if (xShort <= 2)
+ {
+ static const double t2[] =
+ { 0.47724986805182079, 0.05399096651318805, -0.05399096651318805,
+ 0.02699548325659403, -0.00449924720943234, -0.00224962360471617,
+ 0.00134977416282970, -0.00011783742691370, -0.00011515930357476,
+ 0.00003704737285544, 0.00000282690796889, -0.00000354513195524,
+ 0.00000037669563126, 0.00000019202407921, -0.00000005226908590,
+ -0.00000000491799345, 0.00000000366377919, -0.00000000015981997,
+ -0.00000000017381238, 0.00000000002624031, 0.00000000000560919,
+ -0.00000000000172127, -0.00000000000008634, 0.00000000000007894 };
+ nVal = taylor(t2, 23, (xAbs - 2.0));
+ }
+ else if (xShort <= 4)
+ {
+ static const double t4[] =
+ { 0.49996832875816688, 0.00013383022576489, -0.00026766045152977,
+ 0.00033457556441221, -0.00028996548915725, 0.00018178605666397,
+ -0.00008252863922168, 0.00002551802519049, -0.00000391665839292,
+ -0.00000074018205222, 0.00000064422023359, -0.00000017370155340,
+ 0.00000000909595465, 0.00000000944943118, -0.00000000329957075,
+ 0.00000000029492075, 0.00000000011874477, -0.00000000004420396,
+ 0.00000000000361422, 0.00000000000143638, -0.00000000000045848 };
+ nVal = taylor(t4, 20, (xAbs - 4.0));
+ }
+ else
+ {
+ static const double asympt[] = { -1.0, 1.0, -3.0, 15.0, -105.0 };
+ nVal = 0.5 + phi(xAbs) * taylor(asympt, 4, 1.0 / (xAbs * xAbs)) / xAbs;
+ }
+ if (x < 0.0)
+ return -nVal;
+ else
+ return nVal;
+}
+
+// #i26836# new gaussinv implementation by Martin Eitzenberger <m.eitzenberger@unix.net>
+
+double ScInterpreter::gaussinv(double x)
+{
+ double q,t,z;
+
+ q=x-0.5;
+
+ if(std::abs(q)<=.425)
+ {
+ t=0.180625-q*q;
+
+ z=
+ q*
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ t*2509.0809287301226727+33430.575583588128105
+ )
+ *t+67265.770927008700853
+ )
+ *t+45921.953931549871457
+ )
+ *t+13731.693765509461125
+ )
+ *t+1971.5909503065514427
+ )
+ *t+133.14166789178437745
+ )
+ *t+3.387132872796366608
+ )
+ /
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ t*5226.495278852854561+28729.085735721942674
+ )
+ *t+39307.89580009271061
+ )
+ *t+21213.794301586595867
+ )
+ *t+5394.1960214247511077
+ )
+ *t+687.1870074920579083
+ )
+ *t+42.313330701600911252
+ )
+ *t+1.0
+ );
+
+ }
+ else
+ {
+ if(q>0) t=1-x;
+ else t=x;
+
+ t=sqrt(-log(t));
+
+ if(t<=5.0)
+ {
+ t+=-1.6;
+
+ z=
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ t*7.7454501427834140764e-4+0.0227238449892691845833
+ )
+ *t+0.24178072517745061177
+ )
+ *t+1.27045825245236838258
+ )
+ *t+3.64784832476320460504
+ )
+ *t+5.7694972214606914055
+ )
+ *t+4.6303378461565452959
+ )
+ *t+1.42343711074968357734
+ )
+ /
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ t*1.05075007164441684324e-9+5.475938084995344946e-4
+ )
+ *t+0.0151986665636164571966
+ )
+ *t+0.14810397642748007459
+ )
+ *t+0.68976733498510000455
+ )
+ *t+1.6763848301838038494
+ )
+ *t+2.05319162663775882187
+ )
+ *t+1.0
+ );
+
+ }
+ else
+ {
+ t+=-5.0;
+
+ z=
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ t*2.01033439929228813265e-7+2.71155556874348757815e-5
+ )
+ *t+0.0012426609473880784386
+ )
+ *t+0.026532189526576123093
+ )
+ *t+0.29656057182850489123
+ )
+ *t+1.7848265399172913358
+ )
+ *t+5.4637849111641143699
+ )
+ *t+6.6579046435011037772
+ )
+ /
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ t*2.04426310338993978564e-15+1.4215117583164458887e-7
+ )
+ *t+1.8463183175100546818e-5
+ )
+ *t+7.868691311456132591e-4
+ )
+ *t+0.0148753612908506148525
+ )
+ *t+0.13692988092273580531
+ )
+ *t+0.59983220655588793769
+ )
+ *t+1.0
+ );
+
+ }
+
+ if(q<0.0) z=-z;
+ }
+
+ return z;
+}
+
+double ScInterpreter::Fakultaet(double x)
+{
+ x = ::rtl::math::approxFloor(x);
+ if (x < 0.0)
+ return 0.0;
+ else if (x == 0.0)
+ return 1.0;
+ else if (x <= 170.0)
+ {
+ double fTemp = x;
+ while (fTemp > 2.0)
+ {
+ fTemp--;
+ x *= fTemp;
+ }
+ }
+ else
+ SetError(FormulaError::NoValue);
+ return x;
+}
+
+double ScInterpreter::BinomKoeff(double n, double k)
+{
+ // this method has been duplicated as BinomialCoefficient()
+ // in scaddins/source/analysis/analysishelper.cxx
+
+ double nVal = 0.0;
+ k = ::rtl::math::approxFloor(k);
+ if (n < k)
+ nVal = 0.0;
+ else if (k == 0.0)
+ nVal = 1.0;
+ else
+ {
+ nVal = n/k;
+ n--;
+ k--;
+ while (k > 0.0)
+ {
+ nVal *= n/k;
+ k--;
+ n--;
+ }
+
+ }
+ return nVal;
+}
+
+// The algorithm is based on lanczos13m53 in lanczos.hpp
+// in math library from http://www.boost.org
+/** you must ensure fZ>0
+ Uses a variant of the Lanczos sum with a rational function. */
+static double lcl_getLanczosSum(double fZ)
+{
+ static const double fNum[13] ={
+ 23531376880.41075968857200767445163675473,
+ 42919803642.64909876895789904700198885093,
+ 35711959237.35566804944018545154716670596,
+ 17921034426.03720969991975575445893111267,
+ 6039542586.35202800506429164430729792107,
+ 1439720407.311721673663223072794912393972,
+ 248874557.8620541565114603864132294232163,
+ 31426415.58540019438061423162831820536287,
+ 2876370.628935372441225409051620849613599,
+ 186056.2653952234950402949897160456992822,
+ 8071.672002365816210638002902272250613822,
+ 210.8242777515793458725097339207133627117,
+ 2.506628274631000270164908177133837338626
+ };
+ static const double fDenom[13] = {
+ 0,
+ 39916800,
+ 120543840,
+ 150917976,
+ 105258076,
+ 45995730,
+ 13339535,
+ 2637558,
+ 357423,
+ 32670,
+ 1925,
+ 66,
+ 1
+ };
+ // Horner scheme
+ double fSumNum;
+ double fSumDenom;
+ int nI;
+ if (fZ<=1.0)
+ {
+ fSumNum = fNum[12];
+ fSumDenom = fDenom[12];
+ for (nI = 11; nI >= 0; --nI)
+ {
+ fSumNum *= fZ;
+ fSumNum += fNum[nI];
+ fSumDenom *= fZ;
+ fSumDenom += fDenom[nI];
+ }
+ }
+ else
+ // Cancel down with fZ^12; Horner scheme with reverse coefficients
+ {
+ double fZInv = 1/fZ;
+ fSumNum = fNum[0];
+ fSumDenom = fDenom[0];
+ for (nI = 1; nI <=12; ++nI)
+ {
+ fSumNum *= fZInv;
+ fSumNum += fNum[nI];
+ fSumDenom *= fZInv;
+ fSumDenom += fDenom[nI];
+ }
+ }
+ return fSumNum/fSumDenom;
+}
+
+// The algorithm is based on tgamma in gamma.hpp
+// in math library from http://www.boost.org
+/** You must ensure fZ>0; fZ>171.624376956302 will overflow. */
+static double lcl_GetGammaHelper(double fZ)
+{
+ double fGamma = lcl_getLanczosSum(fZ);
+ const double fg = 6.024680040776729583740234375;
+ double fZgHelp = fZ + fg - 0.5;
+ // avoid intermediate overflow
+ double fHalfpower = pow( fZgHelp, fZ / 2 - 0.25);
+ fGamma *= fHalfpower;
+ fGamma /= exp(fZgHelp);
+ fGamma *= fHalfpower;
+ if (fZ <= 20.0 && fZ == ::rtl::math::approxFloor(fZ))
+ fGamma = ::rtl::math::round(fGamma);
+ return fGamma;
+}
+
+// The algorithm is based on tgamma in gamma.hpp
+// in math library from http://www.boost.org
+/** You must ensure fZ>0 */
+static double lcl_GetLogGammaHelper(double fZ)
+{
+ const double fg = 6.024680040776729583740234375;
+ double fZgHelp = fZ + fg - 0.5;
+ return log( lcl_getLanczosSum(fZ)) + (fZ-0.5) * log(fZgHelp) - fZgHelp;
+}
+
+/** You must ensure non integer arguments for fZ<1 */
+double ScInterpreter::GetGamma(double fZ)
+{
+ const double fLogPi = log(M_PI);
+ const double fLogDblMax = log( ::std::numeric_limits<double>::max());
+
+ if (fZ > fMaxGammaArgument)
+ {
+ SetError(FormulaError::IllegalFPOperation);
+ return HUGE_VAL;
+ }
+
+ if (fZ >= 1.0)
+ return lcl_GetGammaHelper(fZ);
+
+ if (fZ >= 0.5) // shift to x>=1 using Gamma(x)=Gamma(x+1)/x
+ return lcl_GetGammaHelper(fZ+1) / fZ;
+
+ if (fZ >= -0.5) // shift to x>=1, might overflow
+ {
+ double fLogTest = lcl_GetLogGammaHelper(fZ+2) - std::log1p(fZ) - log( std::abs(fZ));
+ if (fLogTest >= fLogDblMax)
+ {
+ SetError( FormulaError::IllegalFPOperation);
+ return HUGE_VAL;
+ }
+ return lcl_GetGammaHelper(fZ+2) / (fZ+1) / fZ;
+ }
+ // fZ<-0.5
+ // Use Euler's reflection formula: gamma(x)= pi/ ( gamma(1-x)*sin(pi*x) )
+ double fLogDivisor = lcl_GetLogGammaHelper(1-fZ) + log( std::abs( ::rtl::math::sin( M_PI*fZ)));
+ if (fLogDivisor - fLogPi >= fLogDblMax) // underflow
+ return 0.0;
+
+ if (fLogDivisor<0.0)
+ if (fLogPi - fLogDivisor > fLogDblMax) // overflow
+ {
+ SetError(FormulaError::IllegalFPOperation);
+ return HUGE_VAL;
+ }
+
+ return exp( fLogPi - fLogDivisor) * ((::rtl::math::sin( M_PI*fZ) < 0.0) ? -1.0 : 1.0);
+}
+
+/** You must ensure fZ>0 */
+double ScInterpreter::GetLogGamma(double fZ)
+{
+ if (fZ >= fMaxGammaArgument)
+ return lcl_GetLogGammaHelper(fZ);
+ if (fZ >= 1.0)
+ return log(lcl_GetGammaHelper(fZ));
+ if (fZ >= 0.5)
+ return log( lcl_GetGammaHelper(fZ+1) / fZ);
+ return lcl_GetLogGammaHelper(fZ+2) - std::log1p(fZ) - log(fZ);
+}
+
+double ScInterpreter::GetFDist(double x, double fF1, double fF2)
+{
+ double arg = fF2/(fF2+fF1*x);
+ double alpha = fF2/2.0;
+ double beta = fF1/2.0;
+ return GetBetaDist(arg, alpha, beta);
+}
+
+double ScInterpreter::GetTDist( double T, double fDF, int nType )
+{
+ switch ( nType )
+ {
+ case 1 : // 1-tailed T-distribution
+ return 0.5 * GetBetaDist( fDF / ( fDF + T * T ), fDF / 2.0, 0.5 );
+ case 2 : // 2-tailed T-distribution
+ return GetBetaDist( fDF / ( fDF + T * T ), fDF / 2.0, 0.5);
+ case 3 : // left-tailed T-distribution (probability density function)
+ return pow( 1 + ( T * T / fDF ), -( fDF + 1 ) / 2 ) / ( sqrt( fDF ) * GetBeta( 0.5, fDF / 2.0 ) );
+ case 4 : // left-tailed T-distribution (cumulative distribution function)
+ double X = fDF / ( T * T + fDF );
+ double R = 0.5 * GetBetaDist( X, 0.5 * fDF, 0.5 );
+ return ( T < 0 ? R : 1 - R );
+ }
+ SetError( FormulaError::IllegalArgument );
+ return HUGE_VAL;
+}
+
+// for LEGACY.CHIDIST, returns right tail, fDF=degrees of freedom
+/** You must ensure fDF>0.0 */
+double ScInterpreter::GetChiDist(double fX, double fDF)
+{
+ if (fX <= 0.0)
+ return 1.0; // see ODFF
+ else
+ return GetUpRegIGamma( fDF/2.0, fX/2.0);
+}
+
+// ready for ODF 1.2
+// for ODF CHISQDIST; cumulative distribution function, fDF=degrees of freedom
+// returns left tail
+/** You must ensure fDF>0.0 */
+double ScInterpreter::GetChiSqDistCDF(double fX, double fDF)
+{
+ if (fX <= 0.0)
+ return 0.0; // see ODFF
+ else
+ return GetLowRegIGamma( fDF/2.0, fX/2.0);
+}
+
+double ScInterpreter::GetChiSqDistPDF(double fX, double fDF)
+{
+ // you must ensure fDF is positive integer
+ double fValue;
+ if (fX <= 0.0)
+ return 0.0; // see ODFF
+ if (fDF*fX > 1391000.0)
+ {
+ // intermediate invalid values, use log
+ fValue = exp((0.5*fDF - 1) * log(fX*0.5) - 0.5 * fX - log(2.0) - GetLogGamma(0.5*fDF));
+ }
+ else // fDF is small in most cases, we can iterate
+ {
+ double fCount;
+ if (fmod(fDF,2.0)<0.5)
+ {
+ // even
+ fValue = 0.5;
+ fCount = 2.0;
+ }
+ else
+ {
+ fValue = 1/sqrt(fX*2*M_PI);
+ fCount = 1.0;
+ }
+ while ( fCount < fDF)
+ {
+ fValue *= (fX / fCount);
+ fCount += 2.0;
+ }
+ if (fX>=1425.0) // underflow in e^(-x/2)
+ fValue = exp(log(fValue)-fX/2);
+ else
+ fValue *= exp(-fX/2);
+ }
+ return fValue;
+}
+
+void ScInterpreter::ScChiSqDist()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+ bool bCumulative;
+ if (nParamCount == 3)
+ bCumulative = GetBool();
+ else
+ bCumulative = true;
+ double fDF = ::rtl::math::approxFloor(GetDouble());
+ if (fDF < 1.0)
+ PushIllegalArgument();
+ else
+ {
+ double fX = GetDouble();
+ if (bCumulative)
+ PushDouble(GetChiSqDistCDF(fX,fDF));
+ else
+ PushDouble(GetChiSqDistPDF(fX,fDF));
+ }
+}
+
+void ScInterpreter::ScChiSqDist_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 3 ) )
+ return;
+ bool bCumulative = GetBool();
+ double fDF = ::rtl::math::approxFloor( GetDouble() );
+ if ( fDF < 1.0 || fDF > 1E10 )
+ PushIllegalArgument();
+ else
+ {
+ double fX = GetDouble();
+ if ( fX < 0 )
+ PushIllegalArgument();
+ else
+ {
+ if ( bCumulative )
+ PushDouble( GetChiSqDistCDF( fX, fDF ) );
+ else
+ PushDouble( GetChiSqDistPDF( fX, fDF ) );
+ }
+ }
+}
+
+void ScInterpreter::ScGamma()
+{
+ double x = GetDouble();
+ if (x <= 0.0 && x == ::rtl::math::approxFloor(x))
+ PushIllegalArgument();
+ else
+ {
+ double fResult = GetGamma(x);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ PushDouble(fResult);
+ }
+}
+
+void ScInterpreter::ScLogGamma()
+{
+ double x = GetDouble();
+ if (x > 0.0) // constraint from ODFF
+ PushDouble( GetLogGamma(x));
+ else
+ PushIllegalArgument();
+}
+
+double ScInterpreter::GetBeta(double fAlpha, double fBeta)
+{
+ double fA;
+ double fB;
+ if (fAlpha > fBeta)
+ {
+ fA = fAlpha; fB = fBeta;
+ }
+ else
+ {
+ fA = fBeta; fB = fAlpha;
+ }
+ if (fA+fB < fMaxGammaArgument) // simple case
+ return GetGamma(fA)/GetGamma(fA+fB)*GetGamma(fB);
+ // need logarithm
+ // GetLogGamma is not accurate enough, back to Lanczos for all three
+ // GetGamma and arrange factors newly.
+ const double fg = 6.024680040776729583740234375; //see GetGamma
+ double fgm = fg - 0.5;
+ double fLanczos = lcl_getLanczosSum(fA);
+ fLanczos /= lcl_getLanczosSum(fA+fB);
+ fLanczos *= lcl_getLanczosSum(fB);
+ double fABgm = fA+fB+fgm;
+ fLanczos *= sqrt((fABgm/(fA+fgm))/(fB+fgm));
+ double fTempA = fB/(fA+fgm); // (fA+fgm)/fABgm = 1 / ( 1 + fB/(fA+fgm))
+ double fTempB = fA/(fB+fgm);
+ double fResult = exp(-fA * std::log1p(fTempA)
+ -fB * std::log1p(fTempB)-fgm);
+ fResult *= fLanczos;
+ return fResult;
+}
+
+// Same as GetBeta but with logarithm
+double ScInterpreter::GetLogBeta(double fAlpha, double fBeta)
+{
+ double fA;
+ double fB;
+ if (fAlpha > fBeta)
+ {
+ fA = fAlpha; fB = fBeta;
+ }
+ else
+ {
+ fA = fBeta; fB = fAlpha;
+ }
+ const double fg = 6.024680040776729583740234375; //see GetGamma
+ double fgm = fg - 0.5;
+ double fLanczos = lcl_getLanczosSum(fA);
+ fLanczos /= lcl_getLanczosSum(fA+fB);
+ fLanczos *= lcl_getLanczosSum(fB);
+ double fLogLanczos = log(fLanczos);
+ double fABgm = fA+fB+fgm;
+ fLogLanczos += 0.5*(log(fABgm)-log(fA+fgm)-log(fB+fgm));
+ double fTempA = fB/(fA+fgm); // (fA+fgm)/fABgm = 1 / ( 1 + fB/(fA+fgm))
+ double fTempB = fA/(fB+fgm);
+ double fResult = -fA * std::log1p(fTempA)
+ -fB * std::log1p(fTempB)-fgm;
+ fResult += fLogLanczos;
+ return fResult;
+}
+
+// beta distribution probability density function
+double ScInterpreter::GetBetaDistPDF(double fX, double fA, double fB)
+{
+ // special cases
+ if (fA == 1.0) // result b*(1-x)^(b-1)
+ {
+ if (fB == 1.0)
+ return 1.0;
+ if (fB == 2.0)
+ return -2.0*fX + 2.0;
+ if (fX == 1.0 && fB < 1.0)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return HUGE_VAL;
+ }
+ if (fX <= 0.01)
+ return fB + fB * std::expm1((fB-1.0) * std::log1p(-fX));
+ else
+ return fB * pow(0.5-fX+0.5,fB-1.0);
+ }
+ if (fB == 1.0) // result a*x^(a-1)
+ {
+ if (fA == 2.0)
+ return fA * fX;
+ if (fX == 0.0 && fA < 1.0)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return HUGE_VAL;
+ }
+ return fA * pow(fX,fA-1);
+ }
+ if (fX <= 0.0)
+ {
+ if (fA < 1.0 && fX == 0.0)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return HUGE_VAL;
+ }
+ else
+ return 0.0;
+ }
+ if (fX >= 1.0)
+ {
+ if (fB < 1.0 && fX == 1.0)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return HUGE_VAL;
+ }
+ else
+ return 0.0;
+ }
+
+ // normal cases; result x^(a-1)*(1-x)^(b-1)/Beta(a,b)
+ const double fLogDblMax = log( ::std::numeric_limits<double>::max());
+ const double fLogDblMin = log( ::std::numeric_limits<double>::min());
+ double fLogY = (fX < 0.1) ? std::log1p(-fX) : log(0.5-fX+0.5);
+ double fLogX = log(fX);
+ double fAm1LogX = (fA-1.0) * fLogX;
+ double fBm1LogY = (fB-1.0) * fLogY;
+ double fLogBeta = GetLogBeta(fA,fB);
+ // check whether parts over- or underflow
+ if ( fAm1LogX < fLogDblMax && fAm1LogX > fLogDblMin
+ && fBm1LogY < fLogDblMax && fBm1LogY > fLogDblMin
+ && fLogBeta < fLogDblMax && fLogBeta > fLogDblMin
+ && fAm1LogX + fBm1LogY < fLogDblMax && fAm1LogX + fBm1LogY > fLogDblMin)
+ return pow(fX,fA-1.0) * pow(0.5-fX+0.5,fB-1.0) / GetBeta(fA,fB);
+ else // need logarithm;
+ // might overflow as a whole, but seldom, not worth to pre-detect it
+ return exp( fAm1LogX + fBm1LogY - fLogBeta);
+}
+
+/*
+ x^a * (1-x)^b
+ I_x(a,b) = ---------------- * result of ContFrac
+ a * Beta(a,b)
+*/
+static double lcl_GetBetaHelperContFrac(double fX, double fA, double fB)
+{ // like old version
+ double a1, b1, a2, b2, fnorm, cfnew, cf;
+ a1 = 1.0; b1 = 1.0;
+ b2 = 1.0 - (fA+fB)/(fA+1.0)*fX;
+ if (b2 == 0.0)
+ {
+ a2 = 0.0;
+ fnorm = 1.0;
+ cf = 1.0;
+ }
+ else
+ {
+ a2 = 1.0;
+ fnorm = 1.0/b2;
+ cf = a2*fnorm;
+ }
+ cfnew = 1.0;
+ double rm = 1.0;
+
+ const double fMaxIter = 50000.0;
+ // loop security, normal cases converge in less than 100 iterations.
+ // FIXME: You will get so much iterations for fX near mean,
+ // I do not know a better algorithm.
+ bool bfinished = false;
+ do
+ {
+ const double apl2m = fA + 2.0*rm;
+ const double d2m = rm*(fB-rm)*fX/((apl2m-1.0)*apl2m);
+ const double d2m1 = -(fA+rm)*(fA+fB+rm)*fX/(apl2m*(apl2m+1.0));
+ a1 = (a2+d2m*a1)*fnorm;
+ b1 = (b2+d2m*b1)*fnorm;
+ a2 = a1 + d2m1*a2*fnorm;
+ b2 = b1 + d2m1*b2*fnorm;
+ if (b2 != 0.0)
+ {
+ fnorm = 1.0/b2;
+ cfnew = a2*fnorm;
+ bfinished = (std::abs(cf-cfnew) < std::abs(cf)*fMachEps);
+ }
+ cf = cfnew;
+ rm += 1.0;
+ }
+ while (rm < fMaxIter && !bfinished);
+ return cf;
+}
+
+// cumulative distribution function, normalized
+double ScInterpreter::GetBetaDist(double fXin, double fAlpha, double fBeta)
+{
+// special cases
+ if (fXin <= 0.0) // values are valid, see spec
+ return 0.0;
+ if (fXin >= 1.0) // values are valid, see spec
+ return 1.0;
+ if (fBeta == 1.0)
+ return pow(fXin, fAlpha);
+ if (fAlpha == 1.0)
+ // 1.0 - pow(1.0-fX,fBeta) is not accurate enough
+ return -std::expm1(fBeta * std::log1p(-fXin));
+ //FIXME: need special algorithm for fX near fP for large fA,fB
+ double fResult;
+ // I use always continued fraction, power series are neither
+ // faster nor more accurate.
+ double fY = (0.5-fXin)+0.5;
+ double flnY = std::log1p(-fXin);
+ double fX = fXin;
+ double flnX = log(fXin);
+ double fA = fAlpha;
+ double fB = fBeta;
+ bool bReflect = fXin > fAlpha/(fAlpha+fBeta);
+ if (bReflect)
+ {
+ fA = fBeta;
+ fB = fAlpha;
+ fX = fY;
+ fY = fXin;
+ flnX = flnY;
+ flnY = log(fXin);
+ }
+ fResult = lcl_GetBetaHelperContFrac(fX,fA,fB);
+ fResult = fResult/fA;
+ double fP = fA/(fA+fB);
+ double fQ = fB/(fA+fB);
+ double fTemp;
+ if (fA > 1.0 && fB > 1.0 && fP < 0.97 && fQ < 0.97) //found experimental
+ fTemp = GetBetaDistPDF(fX,fA,fB)*fX*fY;
+ else
+ fTemp = exp(fA*flnX + fB*flnY - GetLogBeta(fA,fB));
+ fResult *= fTemp;
+ if (bReflect)
+ fResult = 0.5 - fResult + 0.5;
+ if (fResult > 1.0) // ensure valid range
+ fResult = 1.0;
+ if (fResult < 0.0)
+ fResult = 0.0;
+ return fResult;
+}
+
+void ScInterpreter::ScBetaDist()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 6 ) ) // expanded, see #i91547#
+ return;
+ double fLowerBound, fUpperBound;
+ double alpha, beta, x;
+ bool bIsCumulative;
+ if (nParamCount == 6)
+ bIsCumulative = GetBool();
+ else
+ bIsCumulative = true;
+ if (nParamCount >= 5)
+ fUpperBound = GetDouble();
+ else
+ fUpperBound = 1.0;
+ if (nParamCount >= 4)
+ fLowerBound = GetDouble();
+ else
+ fLowerBound = 0.0;
+ beta = GetDouble();
+ alpha = GetDouble();
+ x = GetDouble();
+ double fScale = fUpperBound - fLowerBound;
+ if (fScale <= 0.0 || alpha <= 0.0 || beta <= 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if (bIsCumulative) // cumulative distribution function
+ {
+ // special cases
+ if (x < fLowerBound)
+ {
+ PushDouble(0.0); return; //see spec
+ }
+ if (x > fUpperBound)
+ {
+ PushDouble(1.0); return; //see spec
+ }
+ // normal cases
+ x = (x-fLowerBound)/fScale; // convert to standard form
+ PushDouble(GetBetaDist(x, alpha, beta));
+ return;
+ }
+ else // probability density function
+ {
+ if (x < fLowerBound || x > fUpperBound)
+ {
+ PushDouble(0.0);
+ return;
+ }
+ x = (x-fLowerBound)/fScale;
+ PushDouble(GetBetaDistPDF(x, alpha, beta)/fScale);
+ return;
+ }
+}
+
+/**
+ Microsoft version has parameters in different order
+ Also, upper and lowerbound are optional and have default values
+ and different constraints apply.
+ Basically, function is identical with ScInterpreter::ScBetaDist()
+*/
+void ScInterpreter::ScBetaDist_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 4, 6 ) )
+ return;
+ double fLowerBound, fUpperBound;
+ double alpha, beta, x;
+ bool bIsCumulative;
+ if (nParamCount == 6)
+ fUpperBound = GetDouble();
+ else
+ fUpperBound = 1.0;
+ if (nParamCount >= 5)
+ fLowerBound = GetDouble();
+ else
+ fLowerBound = 0.0;
+ bIsCumulative = GetBool();
+ beta = GetDouble();
+ alpha = GetDouble();
+ x = GetDouble();
+ if (alpha <= 0.0 || beta <= 0.0 || x < fLowerBound || x > fUpperBound)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ double fScale = fUpperBound - fLowerBound;
+ if (bIsCumulative) // cumulative distribution function
+ {
+ x = (x-fLowerBound)/fScale; // convert to standard form
+ PushDouble(GetBetaDist(x, alpha, beta));
+ return;
+ }
+ else // probability density function
+ {
+ x = (x-fLowerBound)/fScale;
+ PushDouble(GetBetaDistPDF(x, alpha, beta)/fScale);
+ return;
+ }
+}
+
+void ScInterpreter::ScPhi()
+{
+ PushDouble(phi(GetDouble()));
+}
+
+void ScInterpreter::ScGauss()
+{
+ PushDouble(gauss(GetDouble()));
+}
+
+void ScInterpreter::ScFisher()
+{
+ double fVal = GetDouble();
+ if (std::abs(fVal) >= 1.0)
+ PushIllegalArgument();
+ else
+ PushDouble(::atanh(fVal));
+}
+
+void ScInterpreter::ScFisherInv()
+{
+ PushDouble( tanh( GetDouble()));
+}
+
+void ScInterpreter::ScFact()
+{
+ double nVal = GetDouble();
+ if (nVal < 0.0)
+ PushIllegalArgument();
+ else
+ PushDouble(Fakultaet(nVal));
+}
+
+void ScInterpreter::ScCombin()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ double k = ::rtl::math::approxFloor(GetDouble());
+ double n = ::rtl::math::approxFloor(GetDouble());
+ if (k < 0.0 || n < 0.0 || k > n)
+ PushIllegalArgument();
+ else
+ PushDouble(BinomKoeff(n, k));
+ }
+}
+
+void ScInterpreter::ScCombinA()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ double k = ::rtl::math::approxFloor(GetDouble());
+ double n = ::rtl::math::approxFloor(GetDouble());
+ if (k < 0.0 || n < 0.0 || k > n)
+ PushIllegalArgument();
+ else
+ PushDouble(BinomKoeff(n + k - 1, k));
+ }
+}
+
+void ScInterpreter::ScPermut()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ double k = ::rtl::math::approxFloor(GetDouble());
+ double n = ::rtl::math::approxFloor(GetDouble());
+ if (n < 0.0 || k < 0.0 || k > n)
+ PushIllegalArgument();
+ else if (k == 0.0)
+ PushInt(1); // (n! / (n - 0)!) == 1
+ else
+ {
+ double nVal = n;
+ for (sal_uLong i = static_cast<sal_uLong>(k)-1; i >= 1; i--)
+ nVal *= n-static_cast<double>(i);
+ PushDouble(nVal);
+ }
+}
+
+void ScInterpreter::ScPermutationA()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ {
+ double k = ::rtl::math::approxFloor(GetDouble());
+ double n = ::rtl::math::approxFloor(GetDouble());
+ if (n < 0.0 || k < 0.0)
+ PushIllegalArgument();
+ else
+ PushDouble(pow(n,k));
+ }
+}
+
+double ScInterpreter::GetBinomDistPMF(double x, double n, double p)
+// used in ScB and ScBinomDist
+// preconditions: 0.0 <= x <= n, 0.0 < p < 1.0; x,n integral although double
+{
+ double q = (0.5 - p) + 0.5;
+ double fFactor = pow(q, n);
+ if (fFactor <=::std::numeric_limits<double>::min())
+ {
+ fFactor = pow(p, n);
+ if (fFactor <= ::std::numeric_limits<double>::min())
+ return GetBetaDistPDF(p, x+1.0, n-x+1.0)/(n+1.0);
+ else
+ {
+ sal_uInt32 max = static_cast<sal_uInt32>(n - x);
+ for (sal_uInt32 i = 0; i < max && fFactor > 0.0; i++)
+ fFactor *= (n-i)/(i+1)*q/p;
+ return fFactor;
+ }
+ }
+ else
+ {
+ sal_uInt32 max = static_cast<sal_uInt32>(x);
+ for (sal_uInt32 i = 0; i < max && fFactor > 0.0; i++)
+ fFactor *= (n-i)/(i+1)*p/q;
+ return fFactor;
+ }
+}
+
+static double lcl_GetBinomDistRange(double n, double xs,double xe,
+ double fFactor /* q^n */, double p, double q)
+//preconditions: 0.0 <= xs < xe <= n; xs,xe,n integral although double
+{
+ sal_uInt32 i;
+ // skip summands index 0 to xs-1, start sum with index xs
+ sal_uInt32 nXs = static_cast<sal_uInt32>( xs );
+ for (i = 1; i <= nXs && fFactor > 0.0; i++)
+ fFactor *= (n-i+1)/i * p/q;
+ KahanSum fSum = fFactor; // Summand xs
+ sal_uInt32 nXe = static_cast<sal_uInt32>(xe);
+ for (i = nXs+1; i <= nXe && fFactor > 0.0; i++)
+ {
+ fFactor *= (n-i+1)/i * p/q;
+ fSum += fFactor;
+ }
+ return std::min(fSum.get(), 1.0);
+}
+
+void ScInterpreter::ScB()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
+ return ;
+ if (nParamCount == 3) // mass function
+ {
+ double x = ::rtl::math::approxFloor(GetDouble());
+ double p = GetDouble();
+ double n = ::rtl::math::approxFloor(GetDouble());
+ if (n < 0.0 || x < 0.0 || x > n || p < 0.0 || p > 1.0)
+ PushIllegalArgument();
+ else if (p == 0.0)
+ PushDouble( (x == 0.0) ? 1.0 : 0.0 );
+ else if ( p == 1.0)
+ PushDouble( (x == n) ? 1.0 : 0.0);
+ else
+ PushDouble(GetBinomDistPMF(x,n,p));
+ }
+ else
+ { // nParamCount == 4
+ double xe = ::rtl::math::approxFloor(GetDouble());
+ double xs = ::rtl::math::approxFloor(GetDouble());
+ double p = GetDouble();
+ double n = ::rtl::math::approxFloor(GetDouble());
+ double q = (0.5 - p) + 0.5;
+ bool bIsValidX = ( 0.0 <= xs && xs <= xe && xe <= n);
+ if ( bIsValidX && 0.0 < p && p < 1.0)
+ {
+ if (xs == xe) // mass function
+ PushDouble(GetBinomDistPMF(xs,n,p));
+ else
+ {
+ double fFactor = pow(q, n);
+ if (fFactor > ::std::numeric_limits<double>::min())
+ PushDouble(lcl_GetBinomDistRange(n,xs,xe,fFactor,p,q));
+ else
+ {
+ fFactor = pow(p, n);
+ if (fFactor > ::std::numeric_limits<double>::min())
+ {
+ // sum from j=xs to xe {(n choose j) * p^j * q^(n-j)}
+ // = sum from i = n-xe to n-xs { (n choose i) * q^i * p^(n-i)}
+ PushDouble(lcl_GetBinomDistRange(n,n-xe,n-xs,fFactor,q,p));
+ }
+ else
+ PushDouble(GetBetaDist(q,n-xe,xe+1.0)-GetBetaDist(q,n-xs+1,xs) );
+ }
+ }
+ }
+ else
+ {
+ if ( bIsValidX ) // not(0<p<1)
+ {
+ if ( p == 0.0 )
+ PushDouble( (xs == 0.0) ? 1.0 : 0.0 );
+ else if ( p == 1.0 )
+ PushDouble( (xe == n) ? 1.0 : 0.0 );
+ else
+ PushIllegalArgument();
+ }
+ else
+ PushIllegalArgument();
+ }
+ }
+}
+
+void ScInterpreter::ScBinomDist()
+{
+ if ( !MustHaveParamCount( GetByte(), 4 ) )
+ return;
+
+ bool bIsCum = GetBool(); // false=mass function; true=cumulative
+ double p = GetDouble();
+ double n = ::rtl::math::approxFloor(GetDouble());
+ double x = ::rtl::math::approxFloor(GetDouble());
+ double q = (0.5 - p) + 0.5; // get one bit more for p near 1.0
+ if (n < 0.0 || x < 0.0 || x > n || p < 0.0 || p > 1.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if ( p == 0.0)
+ {
+ PushDouble( (x==0.0 || bIsCum) ? 1.0 : 0.0 );
+ return;
+ }
+ if ( p == 1.0)
+ {
+ PushDouble( (x==n) ? 1.0 : 0.0);
+ return;
+ }
+ if (!bIsCum)
+ PushDouble( GetBinomDistPMF(x,n,p));
+ else
+ {
+ if (x == n)
+ PushDouble(1.0);
+ else
+ {
+ double fFactor = pow(q, n);
+ if (x == 0.0)
+ PushDouble(fFactor);
+ else if (fFactor <= ::std::numeric_limits<double>::min())
+ {
+ fFactor = pow(p, n);
+ if (fFactor <= ::std::numeric_limits<double>::min())
+ PushDouble(GetBetaDist(q,n-x,x+1.0));
+ else
+ {
+ if (fFactor > fMachEps)
+ {
+ double fSum = 1.0 - fFactor;
+ sal_uInt32 max = static_cast<sal_uInt32> (n - x) - 1;
+ for (sal_uInt32 i = 0; i < max && fFactor > 0.0; i++)
+ {
+ fFactor *= (n-i)/(i+1)*q/p;
+ fSum -= fFactor;
+ }
+ PushDouble( (fSum < 0.0) ? 0.0 : fSum );
+ }
+ else
+ PushDouble(lcl_GetBinomDistRange(n,n-x,n,fFactor,q,p));
+ }
+ }
+ else
+ PushDouble( lcl_GetBinomDistRange(n,0.0,x,fFactor,p,q)) ;
+ }
+ }
+}
+
+void ScInterpreter::ScCritBinom()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ double alpha = GetDouble();
+ double p = GetDouble();
+ double n = ::rtl::math::approxFloor(GetDouble());
+ if (n < 0.0 || alpha < 0.0 || alpha > 1.0 || p < 0.0 || p > 1.0)
+ PushIllegalArgument();
+ else if ( alpha == 0.0 )
+ PushDouble( 0.0 );
+ else if ( alpha == 1.0 )
+ PushDouble( p == 0 ? 0.0 : n );
+ else
+ {
+ double fFactor;
+ double q = (0.5 - p) + 0.5; // get one bit more for p near 1.0
+ if ( q > p ) // work from the side where the cumulative curve is
+ {
+ // work from 0 upwards
+ fFactor = pow(q,n);
+ if (fFactor > ::std::numeric_limits<double>::min())
+ {
+ KahanSum fSum = fFactor;
+ sal_uInt32 max = static_cast<sal_uInt32> (n), i;
+ for (i = 0; i < max && fSum < alpha; i++)
+ {
+ fFactor *= (n-i)/(i+1)*p/q;
+ fSum += fFactor;
+ }
+ PushDouble(i);
+ }
+ else
+ {
+ // accumulate BinomDist until accumulated BinomDist reaches alpha
+ KahanSum fSum = 0.0;
+ sal_uInt32 max = static_cast<sal_uInt32> (n), i;
+ for (i = 0; i < max && fSum < alpha; i++)
+ {
+ const double x = GetBetaDistPDF( p, ( i + 1 ), ( n - i + 1 ) )/( n + 1 );
+ if ( nGlobalError == FormulaError::NONE )
+ fSum += x;
+ else
+ {
+ PushNoValue();
+ return;
+ }
+ }
+ PushDouble( i - 1 );
+ }
+ }
+ else
+ {
+ // work from n backwards
+ fFactor = pow(p, n);
+ if (fFactor > ::std::numeric_limits<double>::min())
+ {
+ KahanSum fSum = 1.0 - fFactor;
+ sal_uInt32 max = static_cast<sal_uInt32> (n), i;
+ for (i = 0; i < max && fSum >= alpha; i++)
+ {
+ fFactor *= (n-i)/(i+1)*q/p;
+ fSum -= fFactor;
+ }
+ PushDouble(n-i);
+ }
+ else
+ {
+ // accumulate BinomDist until accumulated BinomDist reaches alpha
+ KahanSum fSum = 0.0;
+ sal_uInt32 max = static_cast<sal_uInt32> (n), i;
+ alpha = 1 - alpha;
+ for (i = 0; i < max && fSum < alpha; i++)
+ {
+ const double x = GetBetaDistPDF( q, ( i + 1 ), ( n - i + 1 ) )/( n + 1 );
+ if ( nGlobalError == FormulaError::NONE )
+ fSum += x;
+ else
+ {
+ PushNoValue();
+ return;
+ }
+ }
+ PushDouble( n - i + 1 );
+ }
+ }
+ }
+}
+
+void ScInterpreter::ScNegBinomDist()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ double p = GetDouble(); // probability
+ double s = ::rtl::math::approxFloor(GetDouble()); // No of successes
+ double f = ::rtl::math::approxFloor(GetDouble()); // No of failures
+ if ((f + s) <= 1.0 || p < 0.0 || p > 1.0)
+ PushIllegalArgument();
+ else
+ {
+ double q = 1.0 - p;
+ double fFactor = pow(p,s);
+ for (double i = 0.0; i < f; i++)
+ fFactor *= (i+s)/(i+1.0)*q;
+ PushDouble(fFactor);
+ }
+}
+
+void ScInterpreter::ScNegBinomDist_MS()
+{
+ if ( !MustHaveParamCount( GetByte(), 4 ) )
+ return;
+
+ bool bCumulative = GetBool();
+ double p = GetDouble(); // probability
+ double s = ::rtl::math::approxFloor(GetDouble()); // No of successes
+ double f = ::rtl::math::approxFloor(GetDouble()); // No of failures
+ if ( s < 1.0 || f < 0.0 || p < 0.0 || p > 1.0 )
+ PushIllegalArgument();
+ else
+ {
+ double q = 1.0 - p;
+ if ( bCumulative )
+ PushDouble( 1.0 - GetBetaDist( q, f + 1, s ) );
+ else
+ {
+ double fFactor = pow( p, s );
+ for ( double i = 0.0; i < f; i++ )
+ fFactor *= ( i + s ) / ( i + 1.0 ) * q;
+ PushDouble( fFactor );
+ }
+ }
+}
+
+void ScInterpreter::ScNormDist( int nMinParamCount )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, nMinParamCount, 4 ) )
+ return;
+ bool bCumulative = nParamCount != 4 || GetBool();
+ double sigma = GetDouble(); // standard deviation
+ double mue = GetDouble(); // mean
+ double x = GetDouble(); // x
+ if (sigma <= 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if (bCumulative)
+ PushDouble(integralPhi((x-mue)/sigma));
+ else
+ PushDouble(phi((x-mue)/sigma)/sigma);
+}
+
+void ScInterpreter::ScLogNormDist( int nMinParamCount ) //expanded, see #i100119# and fdo72158
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, nMinParamCount, 4 ) )
+ return;
+ bool bCumulative = nParamCount != 4 || GetBool(); // cumulative
+ double sigma = nParamCount >= 3 ? GetDouble() : 1.0; // standard deviation
+ double mue = nParamCount >= 2 ? GetDouble() : 0.0; // mean
+ double x = GetDouble(); // x
+ if (sigma <= 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if (bCumulative)
+ { // cumulative
+ if (x <= 0.0)
+ PushDouble(0.0);
+ else
+ PushDouble(integralPhi((log(x)-mue)/sigma));
+ }
+ else
+ { // density
+ if (x <= 0.0)
+ PushIllegalArgument();
+ else
+ PushDouble(phi((log(x)-mue)/sigma)/sigma/x);
+ }
+}
+
+void ScInterpreter::ScStdNormDist()
+{
+ PushDouble(integralPhi(GetDouble()));
+}
+
+void ScInterpreter::ScStdNormDist_MS()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2 ) )
+ return;
+ bool bCumulative = GetBool(); // cumulative
+ double x = GetDouble(); // x
+
+ if ( bCumulative )
+ PushDouble( integralPhi( x ) );
+ else
+ PushDouble( exp( - pow( x, 2 ) / 2 ) / sqrt( 2 * M_PI ) );
+}
+
+void ScInterpreter::ScExpDist()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ double kum = GetDouble(); // 0 or 1
+ double lambda = GetDouble(); // lambda
+ double x = GetDouble(); // x
+ if (lambda <= 0.0)
+ PushIllegalArgument();
+ else if (kum == 0.0) // density
+ {
+ if (x >= 0.0)
+ PushDouble(lambda * exp(-lambda*x));
+ else
+ PushInt(0);
+ }
+ else // distribution
+ {
+ if (x > 0.0)
+ PushDouble(1.0 - exp(-lambda*x));
+ else
+ PushInt(0);
+ }
+}
+
+void ScInterpreter::ScTDist()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ double fFlag = ::rtl::math::approxFloor(GetDouble());
+ double fDF = ::rtl::math::approxFloor(GetDouble());
+ double T = GetDouble();
+ if (fDF < 1.0 || T < 0.0 || (fFlag != 1.0 && fFlag != 2.0) )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ PushDouble( GetTDist( T, fDF, static_cast<int>(fFlag) ) );
+}
+
+void ScInterpreter::ScTDist_T( int nTails )
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double fDF = ::rtl::math::approxFloor( GetDouble() );
+ double fT = GetDouble();
+ if ( fDF < 1.0 || ( nTails == 2 && fT < 0.0 ) )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ double fRes = GetTDist( fT, fDF, nTails );
+ if ( nTails == 1 && fT < 0.0 )
+ PushDouble( 1.0 - fRes ); // tdf#105937, right tail, negative X
+ else
+ PushDouble( fRes );
+}
+
+void ScInterpreter::ScTDist_MS()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ bool bCumulative = GetBool();
+ double fDF = ::rtl::math::approxFloor( GetDouble() );
+ double T = GetDouble();
+ if ( fDF < 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ PushDouble( GetTDist( T, fDF, ( bCumulative ? 4 : 3 ) ) );
+}
+
+void ScInterpreter::ScFDist()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ double fF2 = ::rtl::math::approxFloor(GetDouble());
+ double fF1 = ::rtl::math::approxFloor(GetDouble());
+ double fF = GetDouble();
+ if (fF < 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ PushDouble(GetFDist(fF, fF1, fF2));
+}
+
+void ScInterpreter::ScFDist_LT()
+{
+ int nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
+ return;
+ bool bCum;
+ if ( nParamCount == 3 )
+ bCum = true;
+ else if ( IsMissing() )
+ {
+ bCum = true;
+ Pop();
+ }
+ else
+ bCum = GetBool();
+ double fF2 = ::rtl::math::approxFloor( GetDouble() );
+ double fF1 = ::rtl::math::approxFloor( GetDouble() );
+ double fF = GetDouble();
+ if ( fF < 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if ( bCum )
+ {
+ // left tail cumulative distribution
+ PushDouble( 1.0 - GetFDist( fF, fF1, fF2 ) );
+ }
+ else
+ {
+ // probability density function
+ PushDouble( pow( fF1 / fF2, fF1 / 2 ) * pow( fF, ( fF1 / 2 ) - 1 ) /
+ ( pow( ( 1 + ( fF * fF1 / fF2 ) ), ( fF1 + fF2 ) / 2 ) *
+ GetBeta( fF1 / 2, fF2 / 2 ) ) );
+ }
+}
+
+void ScInterpreter::ScChiDist( bool bODFF )
+{
+ double fResult;
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double fDF = ::rtl::math::approxFloor(GetDouble());
+ double fChi = GetDouble();
+ if ( fDF < 1.0 // x<=0 returns 1, see ODFF1.2 6.18.11
+ || ( !bODFF && fChi < 0 ) ) // Excel does not accept negative fChi
+ {
+ PushIllegalArgument();
+ return;
+ }
+ fResult = GetChiDist( fChi, fDF);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ PushDouble(fResult);
+}
+
+void ScInterpreter::ScWeibull()
+{
+ if ( !MustHaveParamCount( GetByte(), 4 ) )
+ return;
+
+ double kum = GetDouble(); // 0 or 1
+ double beta = GetDouble(); // beta
+ double alpha = GetDouble(); // alpha
+ double x = GetDouble(); // x
+ if (alpha <= 0.0 || beta <= 0.0 || x < 0.0)
+ PushIllegalArgument();
+ else if (kum == 0.0) // Density
+ PushDouble(alpha/pow(beta,alpha)*pow(x,alpha-1.0)*
+ exp(-pow(x/beta,alpha)));
+ else // Distribution
+ PushDouble(1.0 - exp(-pow(x/beta,alpha)));
+}
+
+void ScInterpreter::ScPoissonDist( bool bODFF )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, ( bODFF ? 2 : 3 ), 3 ) )
+ return;
+
+ bool bCumulative = nParamCount != 3 || GetBool(); // default cumulative
+ double lambda = GetDouble(); // Mean
+ double x = ::rtl::math::approxFloor(GetDouble()); // discrete distribution
+ if (lambda <= 0.0 || x < 0.0)
+ PushIllegalArgument();
+ else if (!bCumulative) // Probability mass function
+ {
+ if (lambda >712.0) // underflow in exp(-lambda)
+ { // accuracy 11 Digits
+ PushDouble( exp(x*log(lambda)-lambda-GetLogGamma(x+1.0)));
+ }
+ else
+ {
+ double fPoissonVar = 1.0;
+ for ( double f = 0.0; f < x; ++f )
+ fPoissonVar *= lambda / ( f + 1.0 );
+ PushDouble( fPoissonVar * exp( -lambda ) );
+ }
+ }
+ else // Cumulative distribution function
+ {
+ if (lambda > 712.0) // underflow in exp(-lambda)
+ { // accuracy 12 Digits
+ PushDouble(GetUpRegIGamma(x+1.0,lambda));
+ }
+ else
+ {
+ if (x >= 936.0) // result is always indistinguishable from 1
+ PushDouble (1.0);
+ else
+ {
+ double fSummand = std::exp(-lambda);
+ KahanSum fSum = fSummand;
+ int nEnd = sal::static_int_cast<int>( x );
+ for (int i = 1; i <= nEnd; i++)
+ {
+ fSummand = (fSummand * lambda)/static_cast<double>(i);
+ fSum += fSummand;
+ }
+ PushDouble(fSum.get());
+ }
+ }
+ }
+}
+
+/** Local function used in the calculation of the hypergeometric distribution.
+ */
+static void lcl_PutFactorialElements( ::std::vector< double >& cn, double fLower, double fUpper, double fBase )
+{
+ for ( double i = fLower; i <= fUpper; ++i )
+ {
+ double fVal = fBase - i;
+ if ( fVal > 1.0 )
+ cn.push_back( fVal );
+ }
+}
+
+/** Calculates a value of the hypergeometric distribution.
+
+ @see #i47296#
+
+ This function has an extra argument bCumulative,
+ which only calculates the non-cumulative distribution and
+ which is optional in Calc and mandatory with Excel's HYPGEOM.DIST()
+
+ @see fdo#71722
+ @see tdf#102948, make Calc function ODFF1.2-compliant
+ @see tdf#117041, implement note at bottom of ODFF1.2 par.6.18.37
+ */
+void ScInterpreter::ScHypGeomDist( int nMinParamCount )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, nMinParamCount, 5 ) )
+ return;
+
+ bool bCumulative = ( nParamCount == 5 && GetBool() );
+ double N = ::rtl::math::approxFloor(GetDouble());
+ double M = ::rtl::math::approxFloor(GetDouble());
+ double n = ::rtl::math::approxFloor(GetDouble());
+ double x = ::rtl::math::approxFloor(GetDouble());
+
+ if ( (x < 0.0) || (n < x) || (N < n) || (N < M) || (M < 0.0) )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ KahanSum fVal = 0.0;
+
+ for ( int i = ( bCumulative ? 0 : x ); i <= x && nGlobalError == FormulaError::NONE; i++ )
+ {
+ if ( (n - i <= N - M) && (i <= M) )
+ fVal += GetHypGeomDist( i, n, M, N );
+ }
+
+ PushDouble( fVal.get() );
+}
+
+/** Calculates a value of the hypergeometric distribution.
+
+ The algorithm is designed to avoid unnecessary multiplications and division
+ by expanding all factorial elements (9 of them). It is done by excluding
+ those ranges that overlap in the numerator and the denominator. This allows
+ for a fast calculation for large values which would otherwise cause an overflow
+ in the intermediate values.
+
+ @see #i47296#
+ */
+double ScInterpreter::GetHypGeomDist( double x, double n, double M, double N )
+{
+ const size_t nMaxArraySize = 500000; // arbitrary max array size
+
+ std::vector<double> cnNumer, cnDenom;
+
+ size_t nEstContainerSize = static_cast<size_t>( x + ::std::min( n, M ) );
+ size_t nMaxSize = ::std::min( cnNumer.max_size(), nMaxArraySize );
+ if ( nEstContainerSize > nMaxSize )
+ {
+ PushNoValue();
+ return 0;
+ }
+ cnNumer.reserve( nEstContainerSize + 10 );
+ cnDenom.reserve( nEstContainerSize + 10 );
+
+ // Trim coefficient C first
+ double fCNumVarUpper = N - n - M + x - 1.0;
+ double fCDenomVarLower = 1.0;
+ if ( N - n - M + x >= M - x + 1.0 )
+ {
+ fCNumVarUpper = M - x - 1.0;
+ fCDenomVarLower = N - n - 2.0*(M - x) + 1.0;
+ }
+
+ double fCNumLower = N - n - fCNumVarUpper;
+ double fCDenomUpper = N - n - M + x + 1.0 - fCDenomVarLower;
+
+ double fDNumVarLower = n - M;
+
+ if ( n >= M + 1.0 )
+ {
+ if ( N - M < n + 1.0 )
+ {
+ // Case 1
+
+ if ( N - n < n + 1.0 )
+ {
+ // no overlap
+ lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, N - n - 1.0, N );
+ }
+ else
+ {
+ // overlap
+ OSL_ENSURE( fCNumLower < n + 1.0, "ScHypGeomDist: wrong assertion" );
+ lcl_PutFactorialElements( cnNumer, N - 2.0*n, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N );
+ }
+
+ OSL_ENSURE( fCDenomUpper <= N - M, "ScHypGeomDist: wrong assertion" );
+
+ if ( fCDenomUpper < n - x + 1.0 )
+ // no overlap
+ lcl_PutFactorialElements( cnNumer, 1.0, N - M - n + x, N - M + 1.0 );
+ else
+ {
+ // overlap
+ lcl_PutFactorialElements( cnNumer, 1.0, N - M - fCDenomUpper, N - M + 1.0 );
+
+ fCDenomUpper = n - x;
+ fCDenomVarLower = N - M - 2.0*(n - x) + 1.0;
+ }
+ }
+ else
+ {
+ // Case 2
+
+ if ( n > M - 1.0 )
+ {
+ // no overlap
+ lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, M - 1.0, N );
+ }
+ else
+ {
+ lcl_PutFactorialElements( cnNumer, M - n, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N );
+ }
+
+ OSL_ENSURE( fCDenomUpper <= n, "ScHypGeomDist: wrong assertion" );
+
+ if ( fCDenomUpper < n - x + 1.0 )
+ // no overlap
+ lcl_PutFactorialElements( cnNumer, N - M - n + 1.0, N - M - n + x, N - M + 1.0 );
+ else
+ {
+ lcl_PutFactorialElements( cnNumer, N - M - n + 1.0, N - M - fCDenomUpper, N - M + 1.0 );
+ fCDenomUpper = n - x;
+ fCDenomVarLower = N - M - 2.0*(n - x) + 1.0;
+ }
+ }
+
+ OSL_ENSURE( fCDenomUpper <= M, "ScHypGeomDist: wrong assertion" );
+ }
+ else
+ {
+ if ( N - M < M + 1.0 )
+ {
+ // Case 3
+
+ if ( N - n < M + 1.0 )
+ {
+ // No overlap
+ lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, N - M - 1.0, N );
+ }
+ else
+ {
+ lcl_PutFactorialElements( cnNumer, N - n - M, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N );
+ }
+
+ if ( n - x + 1.0 > fCDenomUpper )
+ // No overlap
+ lcl_PutFactorialElements( cnNumer, 1.0, N - M - n + x, N - M + 1.0 );
+ else
+ {
+ // Overlap
+ lcl_PutFactorialElements( cnNumer, 1.0, N - M - fCDenomUpper, N - M + 1.0 );
+
+ fCDenomVarLower = N - M - 2.0*(n - x) + 1.0;
+ fCDenomUpper = n - x;
+ }
+ }
+ else
+ {
+ // Case 4
+
+ OSL_ENSURE( M >= n - x, "ScHypGeomDist: wrong assertion" );
+ OSL_ENSURE( M - x <= N - M + 1.0, "ScHypGeomDist: wrong assertion" );
+
+ if ( N - n < N - M + 1.0 )
+ {
+ // No overlap
+ lcl_PutFactorialElements( cnNumer, 0.0, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, M - 1.0, N );
+ }
+ else
+ {
+ // Overlap
+ OSL_ENSURE( fCNumLower <= N - M + 1.0, "ScHypGeomDist: wrong assertion" );
+ lcl_PutFactorialElements( cnNumer, M - n, fCNumVarUpper, N - n );
+ lcl_PutFactorialElements( cnDenom, 0.0, n - 1.0, N );
+ }
+
+ if ( n - x + 1.0 > fCDenomUpper )
+ // No overlap
+ lcl_PutFactorialElements( cnNumer, N - 2.0*M + 1.0, N - M - n + x, N - M + 1.0 );
+ else if ( M >= fCDenomUpper )
+ {
+ lcl_PutFactorialElements( cnNumer, N - 2.0*M + 1.0, N - M - fCDenomUpper, N - M + 1.0 );
+
+ fCDenomUpper = n - x;
+ fCDenomVarLower = N - M - 2.0*(n - x) + 1.0;
+ }
+ else
+ {
+ OSL_ENSURE( M <= fCDenomUpper, "ScHypGeomDist: wrong assertion" );
+ lcl_PutFactorialElements( cnDenom, fCDenomVarLower, N - n - 2.0*M + x,
+ N - n - M + x + 1.0 );
+
+ fCDenomUpper = n - x;
+ fCDenomVarLower = N - M - 2.0*(n - x) + 1.0;
+ }
+ }
+
+ OSL_ENSURE( fCDenomUpper <= n, "ScHypGeomDist: wrong assertion" );
+
+ fDNumVarLower = 0.0;
+ }
+
+ double nDNumVarUpper = fCDenomUpper < x + 1.0 ? n - x - 1.0 : n - fCDenomUpper - 1.0;
+ double nDDenomVarLower = fCDenomUpper < x + 1.0 ? fCDenomVarLower : N - n - M + 1.0;
+ lcl_PutFactorialElements( cnNumer, fDNumVarLower, nDNumVarUpper, n );
+ lcl_PutFactorialElements( cnDenom, nDDenomVarLower, N - n - M + x, N - n - M + x + 1.0 );
+
+ ::std::sort( cnNumer.begin(), cnNumer.end() );
+ ::std::sort( cnDenom.begin(), cnDenom.end() );
+ auto it1 = cnNumer.rbegin(), it1End = cnNumer.rend();
+ auto it2 = cnDenom.rbegin(), it2End = cnDenom.rend();
+
+ double fFactor = 1.0;
+ for ( ; it1 != it1End || it2 != it2End; )
+ {
+ double fEnum = 1.0, fDenom = 1.0;
+ if ( it1 != it1End )
+ fEnum = *it1++;
+ if ( it2 != it2End )
+ fDenom = *it2++;
+ fFactor *= fEnum / fDenom;
+ }
+
+ return fFactor;
+}
+
+void ScInterpreter::ScGammaDist( bool bODFF )
+{
+ sal_uInt8 nMinParamCount = ( bODFF ? 3 : 4 );
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, nMinParamCount, 4 ) )
+ return;
+ bool bCumulative;
+ if (nParamCount == 4)
+ bCumulative = GetBool();
+ else
+ bCumulative = true;
+ double fBeta = GetDouble(); // scale
+ double fAlpha = GetDouble(); // shape
+ double fX = GetDouble(); // x
+ if ((!bODFF && fX < 0) || fAlpha <= 0.0 || fBeta <= 0.0)
+ PushIllegalArgument();
+ else
+ {
+ if (bCumulative) // distribution
+ PushDouble( GetGammaDist( fX, fAlpha, fBeta));
+ else // density
+ PushDouble( GetGammaDistPDF( fX, fAlpha, fBeta));
+ }
+}
+
+void ScInterpreter::ScNormInv()
+{
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double sigma = GetDouble();
+ double mue = GetDouble();
+ double x = GetDouble();
+ if (sigma <= 0.0 || x < 0.0 || x > 1.0)
+ PushIllegalArgument();
+ else if (x == 0.0 || x == 1.0)
+ PushNoValue();
+ else
+ PushDouble(gaussinv(x)*sigma + mue);
+ }
+}
+
+void ScInterpreter::ScSNormInv()
+{
+ double x = GetDouble();
+ if (x < 0.0 || x > 1.0)
+ PushIllegalArgument();
+ else if (x == 0.0 || x == 1.0)
+ PushNoValue();
+ else
+ PushDouble(gaussinv(x));
+}
+
+void ScInterpreter::ScLogNormInv()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( MustHaveParamCount( nParamCount, 1, 3 ) )
+ {
+ double fSigma = ( nParamCount == 3 ? GetDouble() : 1.0 ); // Stddev
+ double fMue = ( nParamCount >= 2 ? GetDouble() : 0.0 ); // Mean
+ double fP = GetDouble(); // p
+ if ( fSigma <= 0.0 || fP <= 0.0 || fP >= 1.0 )
+ PushIllegalArgument();
+ else
+ PushDouble( exp( fMue + fSigma * gaussinv( fP ) ) );
+ }
+}
+
+class ScGammaDistFunction : public ScDistFunc
+{
+ ScInterpreter& rInt;
+ double fp, fAlpha, fBeta;
+
+public:
+ ScGammaDistFunction( ScInterpreter& rI, double fpVal, double fAlphaVal, double fBetaVal ) :
+ rInt(rI), fp(fpVal), fAlpha(fAlphaVal), fBeta(fBetaVal) {}
+
+ virtual ~ScGammaDistFunction() {}
+
+ double GetValue( double x ) const override { return fp - rInt.GetGammaDist(x, fAlpha, fBeta); }
+};
+
+void ScInterpreter::ScGammaInv()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ double fBeta = GetDouble();
+ double fAlpha = GetDouble();
+ double fP = GetDouble();
+ if (fAlpha <= 0.0 || fBeta <= 0.0 || fP < 0.0 || fP >= 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if (fP == 0.0)
+ PushInt(0);
+ else
+ {
+ bool bConvError;
+ ScGammaDistFunction aFunc( *this, fP, fAlpha, fBeta );
+ double fStart = fAlpha * fBeta;
+ double fVal = lcl_IterateInverse( aFunc, fStart*0.5, fStart, bConvError );
+ if (bConvError)
+ SetError(FormulaError::NoConvergence);
+ PushDouble(fVal);
+ }
+}
+
+class ScBetaDistFunction : public ScDistFunc
+{
+ ScInterpreter& rInt;
+ double fp, fAlpha, fBeta;
+
+public:
+ ScBetaDistFunction( ScInterpreter& rI, double fpVal, double fAlphaVal, double fBetaVal ) :
+ rInt(rI), fp(fpVal), fAlpha(fAlphaVal), fBeta(fBetaVal) {}
+
+ virtual ~ScBetaDistFunction() {}
+
+ double GetValue( double x ) const override { return fp - rInt.GetBetaDist(x, fAlpha, fBeta); }
+};
+
+void ScInterpreter::ScBetaInv()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
+ return;
+ double fP, fA, fB, fAlpha, fBeta;
+ if (nParamCount == 5)
+ fB = GetDouble();
+ else
+ fB = 1.0;
+ if (nParamCount >= 4)
+ fA = GetDouble();
+ else
+ fA = 0.0;
+ fBeta = GetDouble();
+ fAlpha = GetDouble();
+ fP = GetDouble();
+ if (fP < 0.0 || fP > 1.0 || fA >= fB || fAlpha <= 0.0 || fBeta <= 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ bool bConvError;
+ ScBetaDistFunction aFunc( *this, fP, fAlpha, fBeta );
+ // 0..1 as range for iteration so it isn't extended beyond the valid range
+ double fVal = lcl_IterateInverse( aFunc, 0.0, 1.0, bConvError );
+ if (bConvError)
+ PushError( FormulaError::NoConvergence);
+ else
+ PushDouble(fA + fVal*(fB-fA)); // scale to (A,B)
+}
+
+// Note: T, F, and Chi are
+// monotonically decreasing,
+// therefore 1-Dist as function
+
+class ScTDistFunction : public ScDistFunc
+{
+ ScInterpreter& rInt;
+ double fp, fDF;
+ int nT;
+
+public:
+ ScTDistFunction( ScInterpreter& rI, double fpVal, double fDFVal, int nType ) :
+ rInt( rI ), fp( fpVal ), fDF( fDFVal ), nT( nType ) {}
+
+ virtual ~ScTDistFunction() {}
+
+ double GetValue( double x ) const override { return fp - rInt.GetTDist( x, fDF, nT ); }
+};
+
+void ScInterpreter::ScTInv( int nType )
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double fDF = ::rtl::math::approxFloor(GetDouble());
+ double fP = GetDouble();
+ if (fDF < 1.0 || fP <= 0.0 || fP > 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if ( nType == 4 ) // left-tailed cumulative t-distribution
+ {
+ if ( fP == 1.0 )
+ PushIllegalArgument();
+ else if ( fP < 0.5 )
+ PushDouble( -GetTInv( 1 - fP, fDF, nType ) );
+ else
+ PushDouble( GetTInv( fP, fDF, nType ) );
+ }
+ else
+ PushDouble( GetTInv( fP, fDF, nType ) );
+};
+
+double ScInterpreter::GetTInv( double fAlpha, double fSize, int nType )
+{
+ bool bConvError;
+ ScTDistFunction aFunc( *this, fAlpha, fSize, nType );
+ double fVal = lcl_IterateInverse( aFunc, fSize * 0.5, fSize, bConvError );
+ if (bConvError)
+ SetError(FormulaError::NoConvergence);
+ return fVal;
+}
+
+class ScFDistFunction : public ScDistFunc
+{
+ ScInterpreter& rInt;
+ double fp, fF1, fF2;
+
+public:
+ ScFDistFunction( ScInterpreter& rI, double fpVal, double fF1Val, double fF2Val ) :
+ rInt(rI), fp(fpVal), fF1(fF1Val), fF2(fF2Val) {}
+
+ virtual ~ScFDistFunction() {}
+
+ double GetValue( double x ) const override { return fp - rInt.GetFDist(x, fF1, fF2); }
+};
+
+void ScInterpreter::ScFInv()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ double fF2 = ::rtl::math::approxFloor(GetDouble());
+ double fF1 = ::rtl::math::approxFloor(GetDouble());
+ double fP = GetDouble();
+ if (fP <= 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10 || fP > 1.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ bool bConvError;
+ ScFDistFunction aFunc( *this, fP, fF1, fF2 );
+ double fVal = lcl_IterateInverse( aFunc, fF1*0.5, fF1, bConvError );
+ if (bConvError)
+ SetError(FormulaError::NoConvergence);
+ PushDouble(fVal);
+}
+
+void ScInterpreter::ScFInv_LT()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ double fF2 = ::rtl::math::approxFloor(GetDouble());
+ double fF1 = ::rtl::math::approxFloor(GetDouble());
+ double fP = GetDouble();
+ if (fP <= 0.0 || fF1 < 1.0 || fF2 < 1.0 || fF1 >= 1.0E10 || fF2 >= 1.0E10 || fP > 1.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ bool bConvError;
+ ScFDistFunction aFunc( *this, ( 1.0 - fP ), fF1, fF2 );
+ double fVal = lcl_IterateInverse( aFunc, fF1*0.5, fF1, bConvError );
+ if (bConvError)
+ SetError(FormulaError::NoConvergence);
+ PushDouble(fVal);
+}
+
+class ScChiDistFunction : public ScDistFunc
+{
+ ScInterpreter& rInt;
+ double fp, fDF;
+
+public:
+ ScChiDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) :
+ rInt(rI), fp(fpVal), fDF(fDFVal) {}
+
+ virtual ~ScChiDistFunction() {}
+
+ double GetValue( double x ) const override { return fp - rInt.GetChiDist(x, fDF); }
+};
+
+void ScInterpreter::ScChiInv()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double fDF = ::rtl::math::approxFloor(GetDouble());
+ double fP = GetDouble();
+ if (fDF < 1.0 || fP <= 0.0 || fP > 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ bool bConvError;
+ ScChiDistFunction aFunc( *this, fP, fDF );
+ double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError );
+ if (bConvError)
+ SetError(FormulaError::NoConvergence);
+ PushDouble(fVal);
+}
+
+/***********************************************/
+class ScChiSqDistFunction : public ScDistFunc
+{
+ ScInterpreter& rInt;
+ double fp, fDF;
+
+public:
+ ScChiSqDistFunction( ScInterpreter& rI, double fpVal, double fDFVal ) :
+ rInt(rI), fp(fpVal), fDF(fDFVal) {}
+
+ virtual ~ScChiSqDistFunction() {}
+
+ double GetValue( double x ) const override { return fp - rInt.GetChiSqDistCDF(x, fDF); }
+};
+
+void ScInterpreter::ScChiSqInv()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double fDF = ::rtl::math::approxFloor(GetDouble());
+ double fP = GetDouble();
+ if (fDF < 1.0 || fP < 0.0 || fP >= 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ bool bConvError;
+ ScChiSqDistFunction aFunc( *this, fP, fDF );
+ double fVal = lcl_IterateInverse( aFunc, fDF*0.5, fDF, bConvError );
+ if (bConvError)
+ SetError(FormulaError::NoConvergence);
+ PushDouble(fVal);
+}
+
+void ScInterpreter::ScConfidence()
+{
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double n = ::rtl::math::approxFloor(GetDouble());
+ double sigma = GetDouble();
+ double alpha = GetDouble();
+ if (sigma <= 0.0 || alpha <= 0.0 || alpha >= 1.0 || n < 1.0)
+ PushIllegalArgument();
+ else
+ PushDouble( gaussinv(1.0-alpha/2.0) * sigma/sqrt(n) );
+ }
+}
+
+void ScInterpreter::ScConfidenceT()
+{
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double n = ::rtl::math::approxFloor(GetDouble());
+ double sigma = GetDouble();
+ double alpha = GetDouble();
+ if (sigma <= 0.0 || alpha <= 0.0 || alpha >= 1.0 || n < 1.0)
+ PushIllegalArgument();
+ else if (n == 1.0) // for interoperability with Excel
+ PushError(FormulaError::DivisionByZero);
+ else
+ PushDouble( sigma * GetTInv( alpha, n - 1, 2 ) / sqrt( n ) );
+ }
+}
+
+void ScInterpreter::ScZTest()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+ double sigma = 0.0, x;
+ if (nParamCount == 3)
+ {
+ sigma = GetDouble();
+ if (sigma <= 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ x = GetDouble();
+
+ KahanSum fSum = 0.0;
+ KahanSum fSumSqr = 0.0;
+ double fVal;
+ double rValCount = 0.0;
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ fVal = GetDouble();
+ fSum += fVal;
+ fSumSqr += fVal*fVal;
+ rValCount++;
+ }
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ fSum += fVal;
+ fSumSqr += fVal*fVal;
+ rValCount++;
+ }
+ }
+ break;
+ case svRefList :
+ case svDoubleRef :
+ {
+ short nParam = 1;
+ size_t nRefInList = 0;
+ while (nParam-- > 0)
+ {
+ ScRange aRange;
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(fVal, nErr))
+ {
+ fSum += fVal;
+ fSumSqr += fVal*fVal;
+ rValCount++;
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr))
+ {
+ fSum += fVal;
+ fSumSqr += fVal*fVal;
+ rValCount++;
+ }
+ SetError(nErr);
+ }
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCount = pMat->GetElementCount();
+ if (pMat->IsNumeric())
+ {
+ for ( SCSIZE i = 0; i < nCount; i++ )
+ {
+ fVal= pMat->GetDouble(i);
+ fSum += fVal;
+ fSumSqr += fVal * fVal;
+ rValCount++;
+ }
+ }
+ else
+ {
+ for (SCSIZE i = 0; i < nCount; i++)
+ if (!pMat->IsStringOrEmpty(i))
+ {
+ fVal= pMat->GetDouble(i);
+ fSum += fVal;
+ fSumSqr += fVal * fVal;
+ rValCount++;
+ }
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ if (rValCount <= 1.0)
+ PushError( FormulaError::DivisionByZero);
+ else
+ {
+ double mue = fSum.get()/rValCount;
+
+ if (nParamCount != 3)
+ {
+ sigma = (fSumSqr - fSum*fSum/rValCount).get()/(rValCount-1.0);
+ if (sigma == 0.0)
+ {
+ PushError(FormulaError::DivisionByZero);
+ return;
+ }
+ PushDouble(0.5 - gauss((mue-x)/sqrt(sigma/rValCount)));
+ }
+ else
+ PushDouble(0.5 - gauss((mue-x)*sqrt(rValCount)/sigma));
+ }
+}
+
+bool ScInterpreter::CalculateTest(bool _bTemplin
+ ,const SCSIZE nC1, const SCSIZE nC2,const SCSIZE nR1,const SCSIZE nR2
+ ,const ScMatrixRef& pMat1,const ScMatrixRef& pMat2
+ ,double& fT,double& fF)
+{
+ double fCount1 = 0.0;
+ double fCount2 = 0.0;
+ KahanSum fSum1 = 0.0;
+ KahanSum fSumSqr1 = 0.0;
+ KahanSum fSum2 = 0.0;
+ KahanSum fSumSqr2 = 0.0;
+ double fVal;
+ SCSIZE i,j;
+ for (i = 0; i < nC1; i++)
+ for (j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j))
+ {
+ fVal = pMat1->GetDouble(i,j);
+ fSum1 += fVal;
+ fSumSqr1 += fVal * fVal;
+ fCount1++;
+ }
+ }
+ for (i = 0; i < nC2; i++)
+ for (j = 0; j < nR2; j++)
+ {
+ if (!pMat2->IsStringOrEmpty(i,j))
+ {
+ fVal = pMat2->GetDouble(i,j);
+ fSum2 += fVal;
+ fSumSqr2 += fVal * fVal;
+ fCount2++;
+ }
+ }
+ if (fCount1 < 2.0 || fCount2 < 2.0)
+ {
+ PushNoValue();
+ return false;
+ } // if (fCount1 < 2.0 || fCount2 < 2.0)
+ if ( _bTemplin )
+ {
+ double fS1 = (fSumSqr1-fSum1*fSum1/fCount1).get() / (fCount1-1.0) / fCount1;
+ double fS2 = (fSumSqr2-fSum2*fSum2/fCount2).get() / (fCount2-1.0) / fCount2;
+ if (fS1 + fS2 == 0.0)
+ {
+ PushNoValue();
+ return false;
+ }
+ fT = std::abs(( fSum1/fCount1 - fSum2/fCount2 ).get())/sqrt(fS1+fS2);
+ double c = fS1/(fS1+fS2);
+ // GetTDist is calculated via GetBetaDist and also works with non-integral
+ // degrees of freedom. The result matches Excel
+ fF = 1.0/(c*c/(fCount1-1.0)+(1.0-c)*(1.0-c)/(fCount2-1.0));
+ }
+ else
+ {
+ // according to Bronstein-Semendjajew
+ double fS1 = (fSumSqr1 - fSum1*fSum1/fCount1).get() / (fCount1 - 1.0); // Variance
+ double fS2 = (fSumSqr2 - fSum2*fSum2/fCount2).get() / (fCount2 - 1.0);
+ fT = std::abs( fSum1.get()/fCount1 - fSum2.get()/fCount2 ) /
+ sqrt( (fCount1-1.0)*fS1 + (fCount2-1.0)*fS2 ) *
+ sqrt( fCount1*fCount2*(fCount1+fCount2-2)/(fCount1+fCount2) );
+ fF = fCount1 + fCount2 - 2;
+ }
+ return true;
+}
+void ScInterpreter::ScTTest()
+{
+ if ( !MustHaveParamCount( GetByte(), 4 ) )
+ return;
+ double fTyp = ::rtl::math::approxFloor(GetDouble());
+ double fTails = ::rtl::math::approxFloor(GetDouble());
+ if (fTails != 1.0 && fTails != 2.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ ScMatrixRef pMat2 = GetMatrix();
+ ScMatrixRef pMat1 = GetMatrix();
+ if (!pMat1 || !pMat2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ double fT, fF;
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ SCSIZE i, j;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ if (fTyp == 1.0)
+ {
+ if (nC1 != nC2 || nR1 != nR2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ double fCount = 0.0;
+ KahanSum fSum1 = 0.0;
+ KahanSum fSum2 = 0.0;
+ KahanSum fSumSqrD = 0.0;
+ double fVal1, fVal2;
+ for (i = 0; i < nC1; i++)
+ for (j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ fVal1 = pMat1->GetDouble(i,j);
+ fVal2 = pMat2->GetDouble(i,j);
+ fSum1 += fVal1;
+ fSum2 += fVal2;
+ fSumSqrD += (fVal1 - fVal2)*(fVal1 - fVal2);
+ fCount++;
+ }
+ }
+ if (fCount < 1.0)
+ {
+ PushNoValue();
+ return;
+ }
+ KahanSum fSumD = fSum1 - fSum2;
+ double fDivider = ( fSumSqrD*fCount - fSumD*fSumD ).get();
+ if ( fDivider == 0.0 )
+ {
+ PushError(FormulaError::DivisionByZero);
+ return;
+ }
+ fT = std::abs(fSumD.get()) * sqrt((fCount-1.0) / fDivider);
+ fF = fCount - 1.0;
+ }
+ else if (fTyp == 2.0)
+ {
+ if (!CalculateTest(false,nC1, nC2,nR1, nR2,pMat1,pMat2,fT,fF))
+ return; // error was pushed
+ }
+ else if (fTyp == 3.0)
+ {
+ if (!CalculateTest(true,nC1, nC2,nR1, nR2,pMat1,pMat2,fT,fF))
+ return; // error was pushed
+ }
+ else
+ {
+ PushIllegalArgument();
+ return;
+ }
+ PushDouble( GetTDist( fT, fF, static_cast<int>(fTails) ) );
+}
+
+void ScInterpreter::ScFTest()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ ScMatrixRef pMat2 = GetMatrix();
+ ScMatrixRef pMat1 = GetMatrix();
+ if (!pMat1 || !pMat2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ auto aVal1 = pMat1->CollectKahan(sc::op::kOpSumAndSumSquare);
+ auto aVal2 = pMat2->CollectKahan(sc::op::kOpSumAndSumSquare);
+ double fCount1 = aVal1.mnCount;
+ double fCount2 = aVal2.mnCount;
+ KahanSum fSum1 = aVal1.maAccumulator[0];
+ KahanSum fSumSqr1 = aVal1.maAccumulator[1];
+ KahanSum fSum2 = aVal2.maAccumulator[0];
+ KahanSum fSumSqr2 = aVal2.maAccumulator[1];
+
+ if (fCount1 < 2.0 || fCount2 < 2.0)
+ {
+ PushNoValue();
+ return;
+ }
+ double fS1 = (fSumSqr1-fSum1*fSum1/fCount1).get() / (fCount1-1.0);
+ double fS2 = (fSumSqr2-fSum2*fSum2/fCount2).get() / (fCount2-1.0);
+ if (fS1 == 0.0 || fS2 == 0.0)
+ {
+ PushNoValue();
+ return;
+ }
+ double fF, fF1, fF2;
+ if (fS1 > fS2)
+ {
+ fF = fS1/fS2;
+ fF1 = fCount1-1.0;
+ fF2 = fCount2-1.0;
+ }
+ else
+ {
+ fF = fS2/fS1;
+ fF1 = fCount2-1.0;
+ fF2 = fCount1-1.0;
+ }
+ double fFcdf = GetFDist(fF, fF1, fF2);
+ PushDouble(2.0*std::min(fFcdf, 1.0 - fFcdf));
+}
+
+void ScInterpreter::ScChiTest()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ ScMatrixRef pMat2 = GetMatrix();
+ ScMatrixRef pMat1 = GetMatrix();
+ if (!pMat1 || !pMat2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ if (nR1 != nR2 || nC1 != nC2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ KahanSum fChi = 0.0;
+ bool bEmpty = true;
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!(pMat1->IsEmpty(i,j) || pMat2->IsEmpty(i,j)))
+ {
+ bEmpty = false;
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ double fValX = pMat1->GetDouble(i,j);
+ double fValE = pMat2->GetDouble(i,j);
+ if ( fValE == 0.0 )
+ {
+ PushError(FormulaError::DivisionByZero);
+ return;
+ }
+ // These fTemp values guard against a failure when compiled
+ // with optimization (using g++ 4.8.2 on tinderbox 71-TDF),
+ // where ((fValX - fValE) * (fValX - fValE)) with
+ // fValE==1e+308 should had produced Infinity but did
+ // not, instead the result of divide() then was 1e+308.
+ volatile double fTemp1 = (fValX - fValE) * (fValX - fValE);
+ double fTemp2 = fTemp1;
+ if (std::isinf(fTemp2))
+ {
+ PushError(FormulaError::NoConvergence);
+ return;
+ }
+ fChi += sc::divide( fTemp2, fValE);
+ }
+ else
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ }
+ }
+ if ( bEmpty )
+ {
+ // not in ODFF1.2, but for interoperability with Excel
+ PushIllegalArgument();
+ return;
+ }
+ double fDF;
+ if (nC1 == 1 || nR1 == 1)
+ {
+ fDF = static_cast<double>(nC1*nR1 - 1);
+ if (fDF == 0.0)
+ {
+ PushNoValue();
+ return;
+ }
+ }
+ else
+ fDF = static_cast<double>(nC1-1)*static_cast<double>(nR1-1);
+ PushDouble(GetChiDist(fChi.get(), fDF));
+}
+
+void ScInterpreter::ScKurt()
+{
+ KahanSum fSum;
+ double fCount;
+ std::vector<double> values;
+ if ( !CalculateSkew(fSum, fCount, values) )
+ return;
+
+ // ODF 1.2 constraints: # of numbers >= 4
+ if (fCount < 4.0)
+ {
+ // for interoperability with Excel
+ PushError( FormulaError::DivisionByZero);
+ return;
+ }
+
+ KahanSum vSum;
+ double fMean = fSum.get() / fCount;
+ for (double v : values)
+ vSum += (v - fMean) * (v - fMean);
+
+ double fStdDev = sqrt(vSum.get() / (fCount - 1.0));
+ if (fStdDev == 0.0)
+ {
+ PushError( FormulaError::DivisionByZero);
+ return;
+ }
+
+ KahanSum xpower4 = 0.0;
+ for (double v : values)
+ {
+ double dx = (v - fMean) / fStdDev;
+ xpower4 += (dx * dx) * (dx * dx);
+ }
+
+ double k_d = (fCount - 2.0) * (fCount - 3.0);
+ double k_l = fCount * (fCount + 1.0) / ((fCount - 1.0) * k_d);
+ double k_t = 3.0 * (fCount - 1.0) * (fCount - 1.0) / k_d;
+
+ PushDouble(xpower4.get() * k_l - k_t);
+}
+
+void ScInterpreter::ScHarMean()
+{
+ short nParamCount = GetByte();
+ KahanSum nVal = 0.0;
+ double nValCount = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while ((nGlobalError == FormulaError::NONE) && (nParamCount-- > 0))
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ double x = GetDouble();
+ if (x > 0.0)
+ {
+ nVal += 1.0/x;
+ nValCount++;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ double x = GetCellValue(aAdr, aCell);
+ if (x > 0.0)
+ {
+ nVal += 1.0/x;
+ nValCount++;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ break;
+ }
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ double nCellVal;
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(nCellVal, nErr))
+ {
+ if (nCellVal > 0.0)
+ {
+ nVal += 1.0/nCellVal;
+ nValCount++;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ SetError(nErr);
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(nCellVal, nErr))
+ {
+ if (nCellVal > 0.0)
+ {
+ nVal += 1.0/nCellVal;
+ nValCount++;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ SetError(nErr);
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCount = pMat->GetElementCount();
+ if (pMat->IsNumeric())
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ {
+ double x = pMat->GetDouble(nElem);
+ if (x > 0.0)
+ {
+ nVal += 1.0/x;
+ nValCount++;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ else
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ if (!pMat->IsStringOrEmpty(nElem))
+ {
+ double x = pMat->GetDouble(nElem);
+ if (x > 0.0)
+ {
+ nVal += 1.0/x;
+ nValCount++;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ }
+ if (nGlobalError == FormulaError::NONE)
+ PushDouble( nValCount / nVal.get() );
+ else
+ PushError( nGlobalError);
+}
+
+void ScInterpreter::ScGeoMean()
+{
+ short nParamCount = GetByte();
+ KahanSum nVal = 0.0;
+ double nValCount = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+
+ size_t nRefInList = 0;
+ while ((nGlobalError == FormulaError::NONE) && (nParamCount-- > 0))
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ double x = GetDouble();
+ if (x > 0.0)
+ {
+ nVal += log(x);
+ nValCount++;
+ }
+ else if ( x == 0.0 )
+ {
+ // value of 0 means that function result will be 0
+ while ( nParamCount-- > 0 )
+ PopError();
+ PushDouble( 0.0 );
+ return;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ double x = GetCellValue(aAdr, aCell);
+ if (x > 0.0)
+ {
+ nVal += log(x);
+ nValCount++;
+ }
+ else if ( x == 0.0 )
+ {
+ // value of 0 means that function result will be 0
+ while ( nParamCount-- > 0 )
+ PopError();
+ PushDouble( 0.0 );
+ return;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ break;
+ }
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ double nCellVal;
+ ScValueIterator aValIter(mrContext, aRange, mnSubTotalFlags);
+ if (aValIter.GetFirst(nCellVal, nErr))
+ {
+ if (nCellVal > 0.0)
+ {
+ nVal += log(nCellVal);
+ nValCount++;
+ }
+ else if ( nCellVal == 0.0 )
+ {
+ // value of 0 means that function result will be 0
+ while ( nParamCount-- > 0 )
+ PopError();
+ PushDouble( 0.0 );
+ return;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ SetError(nErr);
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(nCellVal, nErr))
+ {
+ if (nCellVal > 0.0)
+ {
+ nVal += log(nCellVal);
+ nValCount++;
+ }
+ else if ( nCellVal == 0.0 )
+ {
+ // value of 0 means that function result will be 0
+ while ( nParamCount-- > 0 )
+ PopError();
+ PushDouble( 0.0 );
+ return;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ SetError(nErr);
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCount = pMat->GetElementCount();
+ if (pMat->IsNumeric())
+ {
+ for (SCSIZE ui = 0; ui < nCount; ui++)
+ {
+ double x = pMat->GetDouble(ui);
+ if (x > 0.0)
+ {
+ nVal += log(x);
+ nValCount++;
+ }
+ else if ( x == 0.0 )
+ {
+ // value of 0 means that function result will be 0
+ while ( nParamCount-- > 0 )
+ PopError();
+ PushDouble( 0.0 );
+ return;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ else
+ {
+ for (SCSIZE ui = 0; ui < nCount; ui++)
+ {
+ if (!pMat->IsStringOrEmpty(ui))
+ {
+ double x = pMat->GetDouble(ui);
+ if (x > 0.0)
+ {
+ nVal += log(x);
+ nValCount++;
+ }
+ else if ( x == 0.0 )
+ {
+ // value of 0 means that function result will be 0
+ while ( nParamCount-- > 0 )
+ PopError();
+ PushDouble( 0.0 );
+ return;
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ }
+ if (nGlobalError == FormulaError::NONE)
+ PushDouble(exp(nVal.get() / nValCount));
+ else
+ PushError( nGlobalError);
+}
+
+void ScInterpreter::ScStandard()
+{
+ if ( MustHaveParamCount( GetByte(), 3 ) )
+ {
+ double sigma = GetDouble();
+ double mue = GetDouble();
+ double x = GetDouble();
+ if (sigma < 0.0)
+ PushError( FormulaError::IllegalArgument);
+ else if (sigma == 0.0)
+ PushError( FormulaError::DivisionByZero);
+ else
+ PushDouble((x-mue)/sigma);
+ }
+}
+bool ScInterpreter::CalculateSkew(KahanSum& fSum, double& fCount, std::vector<double>& values)
+{
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return false;
+
+ fSum = 0.0;
+ fCount = 0.0;
+ double fVal = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while (nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ {
+ fVal = GetDouble();
+ fSum += fVal;
+ values.push_back(fVal);
+ fCount++;
+ }
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ fSum += fVal;
+ values.push_back(fVal);
+ fCount++;
+ }
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ FormulaError nErr = FormulaError::NONE;
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(fVal, nErr))
+ {
+ fSum += fVal;
+ values.push_back(fVal);
+ fCount++;
+ SetError(nErr);
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr))
+ {
+ fSum += fVal;
+ values.push_back(fVal);
+ fCount++;
+ }
+ SetError(nErr);
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCount = pMat->GetElementCount();
+ if (pMat->IsNumeric())
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ {
+ fVal = pMat->GetDouble(nElem);
+ fSum += fVal;
+ values.push_back(fVal);
+ fCount++;
+ }
+ }
+ else
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ if (!pMat->IsStringOrEmpty(nElem))
+ {
+ fVal = pMat->GetDouble(nElem);
+ fSum += fVal;
+ values.push_back(fVal);
+ fCount++;
+ }
+ }
+ }
+ }
+ break;
+ default :
+ SetError(FormulaError::IllegalParameter);
+ break;
+ }
+ }
+
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return false;
+ } // if (nGlobalError != FormulaError::NONE)
+ return true;
+}
+
+void ScInterpreter::CalculateSkewOrSkewp( bool bSkewp )
+{
+ KahanSum fSum;
+ double fCount;
+ std::vector<double> values;
+ if (!CalculateSkew( fSum, fCount, values))
+ return;
+ // SKEW/SKEWP's constraints: they require at least three numbers
+ if (fCount < 3.0)
+ {
+ // for interoperability with Excel
+ PushError(FormulaError::DivisionByZero);
+ return;
+ }
+
+ KahanSum vSum;
+ double fMean = fSum.get() / fCount;
+ for (double v : values)
+ vSum += (v - fMean) * (v - fMean);
+
+ double fStdDev = sqrt( vSum.get() / (bSkewp ? fCount : (fCount - 1.0)));
+ if (fStdDev == 0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ KahanSum xcube = 0.0;
+ for (double v : values)
+ {
+ double dx = (v - fMean) / fStdDev;
+ xcube += dx * dx * dx;
+ }
+
+ if (bSkewp)
+ PushDouble( xcube.get() / fCount );
+ else
+ PushDouble( ((xcube.get() * fCount) / (fCount - 1.0)) / (fCount - 2.0) );
+}
+
+void ScInterpreter::ScSkew()
+{
+ CalculateSkewOrSkewp( false );
+}
+
+void ScInterpreter::ScSkewp()
+{
+ CalculateSkewOrSkewp( true );
+}
+
+double ScInterpreter::GetMedian( vector<double> & rArray )
+{
+ size_t nSize = rArray.size();
+ if (nSize == 0 || nGlobalError != FormulaError::NONE)
+ {
+ SetError( FormulaError::NoValue);
+ return 0.0;
+ }
+
+ // Upper median.
+ size_t nMid = nSize / 2;
+ vector<double>::iterator iMid = rArray.begin() + nMid;
+ ::std::nth_element( rArray.begin(), iMid, rArray.end());
+ if (nSize & 1)
+ return *iMid; // Lower and upper median are equal.
+ else
+ {
+ double fUp = *iMid;
+ // Lower median.
+ iMid = ::std::max_element( rArray.begin(), rArray.begin() + nMid);
+ return (fUp + *iMid) / 2;
+ }
+}
+
+void ScInterpreter::ScMedian()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+ vector<double> aArray;
+ GetNumberSequenceArray( nParamCount, aArray, false );
+ PushDouble( GetMedian( aArray));
+}
+
+double ScInterpreter::GetPercentile( vector<double> & rArray, double fPercentile )
+{
+ size_t nSize = rArray.size();
+ if (nSize == 1)
+ return rArray[0];
+ else
+ {
+ size_t nIndex = static_cast<size_t>(::rtl::math::approxFloor( fPercentile * (nSize-1)));
+ double fDiff = fPercentile * (nSize-1) - ::rtl::math::approxFloor( fPercentile * (nSize-1));
+ OSL_ENSURE(nIndex < nSize, "GetPercentile: wrong index(1)");
+ vector<double>::iterator iter = rArray.begin() + nIndex;
+ ::std::nth_element( rArray.begin(), iter, rArray.end());
+ if (fDiff <= 0.0)
+ {
+ // Note: neg fDiff seen with forum-mso-en4-719754.xlsx with
+ // fPercentile of near 1 where approxFloor gave nIndex of nSize-1
+ // resulting in a non-zero tiny negative fDiff.
+ return *iter;
+ }
+ else
+ {
+ OSL_ENSURE(nIndex < nSize-1, "GetPercentile: wrong index(2)");
+ double fVal = *iter;
+ iter = ::std::min_element( rArray.begin() + nIndex + 1, rArray.end());
+ return fVal + fDiff * (*iter - fVal);
+ }
+ }
+}
+
+double ScInterpreter::GetPercentileExclusive( vector<double> & rArray, double fPercentile )
+{
+ size_t nSize1 = rArray.size() + 1;
+ if ( rArray.empty() || nSize1 == 1 || nGlobalError != FormulaError::NONE)
+ {
+ SetError( FormulaError::NoValue );
+ return 0.0;
+ }
+ if ( fPercentile * nSize1 < 1.0 || fPercentile * nSize1 > static_cast<double>( nSize1 - 1 ) )
+ {
+ SetError( FormulaError::IllegalParameter );
+ return 0.0;
+ }
+
+ size_t nIndex = static_cast<size_t>(::rtl::math::approxFloor( fPercentile * nSize1 - 1 ));
+ double fDiff = fPercentile * nSize1 - 1 - ::rtl::math::approxFloor( fPercentile * nSize1 - 1 );
+ OSL_ENSURE(nIndex < ( nSize1 - 1 ), "GetPercentile: wrong index(1)");
+ vector<double>::iterator iter = rArray.begin() + nIndex;
+ ::std::nth_element( rArray.begin(), iter, rArray.end());
+ if (fDiff == 0.0)
+ return *iter;
+ else
+ {
+ OSL_ENSURE(nIndex < nSize1, "GetPercentile: wrong index(2)");
+ double fVal = *iter;
+ iter = ::std::min_element( rArray.begin() + nIndex + 1, rArray.end());
+ return fVal + fDiff * (*iter - fVal);
+ }
+}
+
+void ScInterpreter::ScPercentile( bool bInclusive )
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double alpha = GetDouble();
+ if ( bInclusive ? ( alpha < 0.0 || alpha > 1.0 ) : ( alpha <= 0.0 || alpha >= 1.0 ) )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ vector<double> aArray;
+ GetNumberSequenceArray( 1, aArray, false );
+ if ( aArray.empty() || nGlobalError != FormulaError::NONE )
+ {
+ PushNoValue();
+ return;
+ }
+ if ( bInclusive )
+ PushDouble( GetPercentile( aArray, alpha ));
+ else
+ PushDouble( GetPercentileExclusive( aArray, alpha ));
+}
+
+void ScInterpreter::ScQuartile( bool bInclusive )
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double fFlag = ::rtl::math::approxFloor(GetDouble());
+ if ( bInclusive ? ( fFlag < 0.0 || fFlag > 4.0 ) : ( fFlag <= 0.0 || fFlag >= 4.0 ) )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ vector<double> aArray;
+ GetNumberSequenceArray( 1, aArray, false );
+ if ( aArray.empty() || nGlobalError != FormulaError::NONE )
+ {
+ PushNoValue();
+ return;
+ }
+ if ( bInclusive )
+ PushDouble( fFlag == 2.0 ? GetMedian( aArray ) : GetPercentile( aArray, 0.25 * fFlag ) );
+ else
+ PushDouble( fFlag == 2.0 ? GetMedian( aArray ) : GetPercentileExclusive( aArray, 0.25 * fFlag ) );
+}
+
+void ScInterpreter::ScModalValue()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+ vector<double> aSortArray;
+ GetSortArray( nParamCount, aSortArray, nullptr, false, false );
+ SCSIZE nSize = aSortArray.size();
+ if (nSize == 0 || nGlobalError != FormulaError::NONE)
+ PushNoValue();
+ else
+ {
+ SCSIZE nMaxIndex = 0, nMax = 1, nCount = 1;
+ double nOldVal = aSortArray[0];
+ SCSIZE i;
+ for ( i = 1; i < nSize; i++)
+ {
+ if (aSortArray[i] == nOldVal)
+ nCount++;
+ else
+ {
+ nOldVal = aSortArray[i];
+ if (nCount > nMax)
+ {
+ nMax = nCount;
+ nMaxIndex = i-1;
+ }
+ nCount = 1;
+ }
+ }
+ if (nCount > nMax)
+ {
+ nMax = nCount;
+ nMaxIndex = i-1;
+ }
+ if (nMax == 1 && nCount == 1)
+ PushNoValue();
+ else if (nMax == 1)
+ PushDouble(nOldVal);
+ else
+ PushDouble(aSortArray[nMaxIndex]);
+ }
+}
+
+void ScInterpreter::ScModalValue_MS( bool bSingle )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+ vector<double> aArray;
+ GetNumberSequenceArray( nParamCount, aArray, false );
+ vector< double > aSortArray( aArray );
+ QuickSort( aSortArray, nullptr );
+ SCSIZE nSize = aSortArray.size();
+ if ( nSize == 0 || nGlobalError != FormulaError::NONE )
+ PushNoValue();
+ else
+ {
+ SCSIZE nMax = 1, nCount = 1;
+ double nOldVal = aSortArray[ 0 ];
+ vector< double > aResultArray( 1 );
+ SCSIZE i;
+ for ( i = 1; i < nSize; i++ )
+ {
+ if ( aSortArray[ i ] == nOldVal )
+ nCount++;
+ else
+ {
+ if ( nCount >= nMax && nCount > 1 )
+ {
+ if ( nCount > nMax )
+ {
+ nMax = nCount;
+ if ( aResultArray.size() != 1 )
+ vector< double >( 1 ).swap( aResultArray );
+ aResultArray[ 0 ] = nOldVal;
+ }
+ else
+ aResultArray.emplace_back( nOldVal );
+ }
+ nOldVal = aSortArray[ i ];
+ nCount = 1;
+ }
+ }
+ if ( nCount >= nMax && nCount > 1 )
+ {
+ if ( nCount > nMax )
+ vector< double >().swap( aResultArray );
+ aResultArray.emplace_back( nOldVal );
+ }
+ if ( nMax == 1 && nCount == 1 )
+ PushNoValue();
+ else if ( nMax == 1 )
+ PushDouble( nOldVal ); // there is only 1 result, no reordering needed
+ else
+ {
+ // sort resultArray according to ordering of aArray
+ vector< vector< double > > aOrder;
+ aOrder.resize( aResultArray.size(), vector< double >( 2 ) );
+ for ( i = 0; i < aResultArray.size(); i++ )
+ {
+ for ( SCSIZE j = 0; j < nSize; j++ )
+ {
+ if ( aArray[ j ] == aResultArray[ i ] )
+ {
+ aOrder[ i ][ 0 ] = aResultArray[ i ];
+ aOrder[ i ][ 1 ] = j;
+ break;
+ }
+ }
+ }
+ sort( aOrder.begin(), aOrder.end(), []( const std::vector< double >& lhs,
+ const std::vector< double >& rhs )
+ { return lhs[ 1 ] < rhs[ 1 ]; } );
+
+ if ( bSingle )
+ PushDouble( aOrder[ 0 ][ 0 ] );
+ else
+ {
+ // put result in correct order in aResultArray
+ for ( i = 0; i < aResultArray.size(); i++ )
+ aResultArray[ i ] = aOrder[ i ][ 0 ];
+ ScMatrixRef pResMatrix = GetNewMat( 1, aResultArray.size(), true );
+ pResMatrix->PutDoubleVector( aResultArray, 0, 0 );
+ PushMatrix( pResMatrix );
+ }
+ }
+ }
+}
+
+void ScInterpreter::CalculateSmallLarge(bool bSmall)
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ SCSIZE nCol = 0, nRow = 0;
+ const auto aArray = GetRankNumberArray(nCol, nRow);
+ const size_t nRankArraySize = aArray.size();
+ if (nRankArraySize == 0 || nGlobalError != FormulaError::NONE)
+ {
+ PushNoValue();
+ return;
+ }
+ assert(nRankArraySize == nCol * nRow);
+
+ std::vector<SCSIZE> aRankArray;
+ aRankArray.reserve(nRankArraySize);
+ std::transform(aArray.begin(), aArray.end(), std::back_inserter(aRankArray),
+ [bSmall](double f) {
+ f = (bSmall ? rtl::math::approxFloor(f) : rtl::math::approxCeil(f));
+ // Valid ranks are >= 1.
+ if (f < 1.0 || !o3tl::convertsToAtMost(f, std::numeric_limits<SCSIZE>::max()))
+ return static_cast<SCSIZE>(0);
+ return static_cast<SCSIZE>(f);
+ });
+
+ vector<double> aSortArray;
+ GetNumberSequenceArray(1, aSortArray, false );
+ const SCSIZE nSize = aSortArray.size();
+ if (nSize == 0 || nGlobalError != FormulaError::NONE)
+ PushNoValue();
+ else if (nRankArraySize == 1)
+ {
+ const SCSIZE k = aRankArray[0];
+ if (k < 1 || nSize < k)
+ {
+ if (!std::isfinite(aArray[0]))
+ PushDouble(aArray[0]); // propagates error
+ else
+ PushNoValue();
+ }
+ else
+ {
+ vector<double>::iterator iPos = aSortArray.begin() + (bSmall ? k-1 : nSize-k);
+ ::std::nth_element( aSortArray.begin(), iPos, aSortArray.end());
+ PushDouble( *iPos);
+ }
+ }
+ else
+ {
+ std::set<SCSIZE> aIndices;
+ for (SCSIZE n : aRankArray)
+ {
+ if (1 <= n && n <= nSize)
+ aIndices.insert(bSmall ? n-1 : nSize-n);
+ }
+ // We can spare sorting when the total number of ranks is small enough.
+ // Find only the elements at given indices if, arbitrarily, the index size is
+ // smaller than 1/3 of the haystack array's size; just sort it squarely, otherwise.
+ if (aIndices.size() < nSize/3)
+ {
+ auto itBegin = aSortArray.begin();
+ for (SCSIZE i : aIndices)
+ {
+ auto it = aSortArray.begin() + i;
+ std::nth_element(itBegin, it, aSortArray.end());
+ itBegin = ++it;
+ }
+ }
+ else
+ std::sort(aSortArray.begin(), aSortArray.end());
+
+ std::vector<double> aResultArray;
+ aResultArray.reserve(nRankArraySize);
+ for (size_t i = 0; i < nRankArraySize; ++i)
+ {
+ const SCSIZE n = aRankArray[i];
+ if (1 <= n && n <= nSize)
+ aResultArray.push_back( aSortArray[bSmall ? n-1 : nSize-n]);
+ else if (!std::isfinite( aArray[i]))
+ aResultArray.push_back( aArray[i]); // propagate error
+ else
+ aResultArray.push_back( CreateDoubleError( FormulaError::IllegalArgument));
+ }
+ ScMatrixRef pResult = GetNewMat(nCol, nRow, aResultArray);
+ PushMatrix(pResult);
+ }
+}
+
+void ScInterpreter::ScLarge()
+{
+ CalculateSmallLarge(false);
+}
+
+void ScInterpreter::ScSmall()
+{
+ CalculateSmallLarge(true);
+}
+
+void ScInterpreter::ScPercentrank( bool bInclusive )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+ double fSignificance = ( nParamCount == 3 ? ::rtl::math::approxFloor( GetDouble() ) : 3.0 );
+ if ( fSignificance < 1.0 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ double fNum = GetDouble();
+ vector<double> aSortArray;
+ GetSortArray( 1, aSortArray, nullptr, false, false );
+ SCSIZE nSize = aSortArray.size();
+ if ( nSize == 0 || nGlobalError != FormulaError::NONE )
+ PushNoValue();
+ else
+ {
+ if ( fNum < aSortArray[ 0 ] || fNum > aSortArray[ nSize - 1 ] )
+ PushNoValue();
+ else
+ {
+ double fRes;
+ if ( nSize == 1 )
+ fRes = 1.0; // fNum == aSortArray[ 0 ], see test above
+ else
+ fRes = GetPercentrank( aSortArray, fNum, bInclusive );
+ if ( fRes != 0.0 )
+ {
+ double fExp = ::rtl::math::approxFloor( log10( fRes ) ) + 1.0 - fSignificance;
+ fRes = ::rtl::math::round( fRes * pow( 10, -fExp ) ) / pow( 10, -fExp );
+ }
+ PushDouble( fRes );
+ }
+ }
+}
+
+double ScInterpreter::GetPercentrank( ::std::vector<double> & rArray, double fVal, bool bInclusive )
+{
+ SCSIZE nSize = rArray.size();
+ double fRes;
+ if ( fVal == rArray[ 0 ] )
+ {
+ if ( bInclusive )
+ fRes = 0.0;
+ else
+ fRes = 1.0 / static_cast<double>( nSize + 1 );
+ }
+ else
+ {
+ SCSIZE nOldCount = 0;
+ double fOldVal = rArray[ 0 ];
+ SCSIZE i;
+ for ( i = 1; i < nSize && rArray[ i ] < fVal; i++ )
+ {
+ if ( rArray[ i ] != fOldVal )
+ {
+ nOldCount = i;
+ fOldVal = rArray[ i ];
+ }
+ }
+ if ( rArray[ i ] != fOldVal )
+ nOldCount = i;
+ if ( fVal == rArray[ i ] )
+ {
+ if ( bInclusive )
+ fRes = div( nOldCount, nSize - 1 );
+ else
+ fRes = static_cast<double>( i + 1 ) / static_cast<double>( nSize + 1 );
+ }
+ else
+ {
+ // nOldCount is the count of smaller entries
+ // fVal is between rArray[ nOldCount - 1 ] and rArray[ nOldCount ]
+ // use linear interpolation to find a position between the entries
+ if ( nOldCount == 0 )
+ {
+ OSL_FAIL( "should not happen" );
+ fRes = 0.0;
+ }
+ else
+ {
+ double fFract = ( fVal - rArray[ nOldCount - 1 ] ) /
+ ( rArray[ nOldCount ] - rArray[ nOldCount - 1 ] );
+ if ( bInclusive )
+ fRes = div( static_cast<double>( nOldCount - 1 ) + fFract, nSize - 1 );
+ else
+ fRes = ( static_cast<double>(nOldCount) + fFract ) / static_cast<double>( nSize + 1 );
+ }
+ }
+ }
+ return fRes;
+}
+
+void ScInterpreter::ScTrimMean()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ double alpha = GetDouble();
+ if (alpha < 0.0 || alpha >= 1.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ vector<double> aSortArray;
+ GetSortArray( 1, aSortArray, nullptr, false, false );
+ SCSIZE nSize = aSortArray.size();
+ if (nSize == 0 || nGlobalError != FormulaError::NONE)
+ PushNoValue();
+ else
+ {
+ sal_uLong nIndex = static_cast<sal_uLong>(::rtl::math::approxFloor(alpha*static_cast<double>(nSize)));
+ if (nIndex % 2 != 0)
+ nIndex--;
+ nIndex /= 2;
+ OSL_ENSURE(nIndex < nSize, "ScTrimMean: wrong index");
+ KahanSum fSum = 0.0;
+ for (SCSIZE i = nIndex; i < nSize-nIndex; i++)
+ fSum += aSortArray[i];
+ PushDouble(fSum.get()/static_cast<double>(nSize-2*nIndex));
+ }
+}
+
+std::vector<double> ScInterpreter::GetRankNumberArray( SCSIZE& rCol, SCSIZE& rRow )
+{
+ std::vector<double> aArray;
+ switch (GetStackType())
+ {
+ case svDouble:
+ aArray.push_back(PopDouble());
+ rCol = rRow = 1;
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef(aAdr);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ aArray.push_back(GetCellValue(aAdr, aCell));
+ rCol = rRow = 1;
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef(aRange, true);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ // give up unless the start and end are in the same sheet
+ if (aRange.aStart.Tab() != aRange.aEnd.Tab())
+ {
+ SetError(FormulaError::IllegalParameter);
+ break;
+ }
+
+ // the range already is in order
+ assert(aRange.aStart.Col() <= aRange.aEnd.Col());
+ assert(aRange.aStart.Row() <= aRange.aEnd.Row());
+ rCol = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
+ rRow = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
+ aArray.reserve(rCol * rRow);
+
+ FormulaError nErr = FormulaError::NONE;
+ double fCellVal;
+ ScValueIterator aValIter(mrContext, aRange, mnSubTotalFlags);
+ if (aValIter.GetFirst(fCellVal, nErr))
+ {
+ do
+ aArray.push_back(fCellVal);
+ while (aValIter.GetNext(fCellVal, nErr) && nErr == FormulaError::NONE);
+ }
+ // Note that SMALL() and LARGE() rank parameters (2nd) have
+ // ParamClass::Value, so in array mode this is never hit and
+ // argument was converted to matrix instead, but for normal
+ // evaluation any non-numeric value including empty cell will
+ // result in error anyway, so just clear and propagate an existing
+ // error here already.
+ if (aArray.size() != rCol * rRow)
+ {
+ aArray.clear();
+ SetError(nErr);
+ }
+ }
+ break;
+ case svMatrix:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (!pMat)
+ break;
+
+ const SCSIZE nCount = pMat->GetElementCount();
+ aArray.reserve(nCount);
+ // Do not propagate errors from matrix elements as global error.
+ pMat->SetErrorInterpreter(nullptr);
+ if (pMat->IsNumeric())
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ aArray.push_back(pMat->GetDouble(i));
+ }
+ else
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ if (pMat->IsValue(i))
+ aArray.push_back( pMat->GetDouble(i));
+ else
+ aArray.push_back( CreateDoubleError( FormulaError::NoValue));
+ }
+ }
+ pMat->GetDimensions(rCol, rRow);
+ }
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ break;
+ }
+ return aArray;
+}
+
+void ScInterpreter::GetNumberSequenceArray( sal_uInt8 nParamCount, vector<double>& rArray, bool bConvertTextInArray )
+{
+ ScAddress aAdr;
+ ScRange aRange;
+ const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
+ short nParam = nParamCount;
+ size_t nRefInList = 0;
+ ReverseStack( nParamCount );
+ while (nParam-- > 0)
+ {
+ const StackVar eStackType = GetStackType();
+ switch (eStackType)
+ {
+ case svDouble :
+ rArray.push_back( PopDouble());
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (bIgnoreErrVal && aCell.hasError())
+ ; // nothing
+ else if (aCell.hasNumeric())
+ rArray.push_back(GetCellValue(aAdr, aCell));
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ PopDoubleRef( aRange, nParam, nRefInList);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ aRange.PutInOrder();
+ SCSIZE nCellCount = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
+ nCellCount *= aRange.aEnd.Row() - aRange.aStart.Row() + 1;
+ rArray.reserve( rArray.size() + nCellCount);
+
+ FormulaError nErr = FormulaError::NONE;
+ double fCellVal;
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst( fCellVal, nErr))
+ {
+ if (bIgnoreErrVal)
+ {
+ if (nErr == FormulaError::NONE)
+ rArray.push_back( fCellVal);
+ while (aValIter.GetNext( fCellVal, nErr))
+ {
+ if (nErr == FormulaError::NONE)
+ rArray.push_back( fCellVal);
+ }
+ }
+ else
+ {
+ rArray.push_back( fCellVal);
+ SetError(nErr);
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext( fCellVal, nErr))
+ rArray.push_back( fCellVal);
+ SetError(nErr);
+ }
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (!pMat)
+ break;
+
+ SCSIZE nCount = pMat->GetElementCount();
+ rArray.reserve( rArray.size() + nCount);
+ if (pMat->IsNumeric())
+ {
+ if (bIgnoreErrVal)
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ const double fVal = pMat->GetDouble(i);
+ if (nGlobalError == FormulaError::NONE)
+ rArray.push_back( fVal);
+ else
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ else
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ rArray.push_back( pMat->GetDouble(i));
+ }
+ }
+ else if (bConvertTextInArray && eStackType == svMatrix)
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ if ( pMat->IsValue( i ) )
+ {
+ if (bIgnoreErrVal)
+ {
+ const double fVal = pMat->GetDouble(i);
+ if (nGlobalError == FormulaError::NONE)
+ rArray.push_back( fVal);
+ else
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ rArray.push_back( pMat->GetDouble(i));
+ }
+ else
+ {
+ // tdf#88547 try to convert string to (date)value
+ OUString aStr = pMat->GetString( i ).getString();
+ if ( aStr.getLength() > 0 )
+ {
+ FormulaError nErr = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+ double fVal = ConvertStringToValue( aStr );
+ if ( nGlobalError == FormulaError::NONE )
+ {
+ rArray.push_back( fVal );
+ nGlobalError = nErr;
+ }
+ else
+ {
+ if (!bIgnoreErrVal)
+ rArray.push_back( CreateDoubleError( FormulaError::NoValue));
+ // Propagate previous error if any, else
+ // the current #VALUE! error, unless
+ // ignoring error values.
+ if (nErr != FormulaError::NONE)
+ nGlobalError = nErr;
+ else if (!bIgnoreErrVal)
+ nGlobalError = FormulaError::NoValue;
+ else
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (bIgnoreErrVal)
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ if (pMat->IsValue(i))
+ {
+ const double fVal = pMat->GetDouble(i);
+ if (nGlobalError == FormulaError::NONE)
+ rArray.push_back( fVal);
+ else
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ }
+ else
+ {
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ if (pMat->IsValue(i))
+ rArray.push_back( pMat->GetDouble(i));
+ }
+ }
+ }
+ }
+ break;
+ default :
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ if (nGlobalError != FormulaError::NONE)
+ break; // while
+ }
+ // nParam > 0 in case of error, clean stack environment and obtain earlier
+ // error if there was one.
+ while (nParam-- > 0)
+ PopError();
+}
+
+void ScInterpreter::GetSortArray( sal_uInt8 nParamCount, vector<double>& rSortArray, vector<tools::Long>* pIndexOrder, bool bConvertTextInArray, bool bAllowEmptyArray )
+{
+ GetNumberSequenceArray( nParamCount, rSortArray, bConvertTextInArray );
+ if (rSortArray.size() > MAX_COUNT_DOUBLE_FOR_SORT(mrDoc.GetSheetLimits()))
+ SetError( FormulaError::MatrixSize);
+ else if ( rSortArray.empty() )
+ {
+ if ( bAllowEmptyArray )
+ return;
+ SetError( FormulaError::NoValue);
+ }
+
+ if (nGlobalError == FormulaError::NONE)
+ QuickSort( rSortArray, pIndexOrder);
+}
+
+static void lcl_QuickSort( tools::Long nLo, tools::Long nHi, vector<double>& rSortArray, vector<tools::Long>* pIndexOrder )
+{
+ // If pIndexOrder is not NULL, we assume rSortArray.size() == pIndexOrder->size().
+
+ using ::std::swap;
+
+ if (nHi - nLo == 1)
+ {
+ if (rSortArray[nLo] > rSortArray[nHi])
+ {
+ swap(rSortArray[nLo], rSortArray[nHi]);
+ if (pIndexOrder)
+ swap(pIndexOrder->at(nLo), pIndexOrder->at(nHi));
+ }
+ return;
+ }
+
+ tools::Long ni = nLo;
+ tools::Long nj = nHi;
+ do
+ {
+ double fLo = rSortArray[nLo];
+ while (ni <= nHi && rSortArray[ni] < fLo) ni++;
+ while (nj >= nLo && fLo < rSortArray[nj]) nj--;
+ if (ni <= nj)
+ {
+ if (ni != nj)
+ {
+ swap(rSortArray[ni], rSortArray[nj]);
+ if (pIndexOrder)
+ swap(pIndexOrder->at(ni), pIndexOrder->at(nj));
+ }
+
+ ++ni;
+ --nj;
+ }
+ }
+ while (ni < nj);
+
+ if ((nj - nLo) < (nHi - ni))
+ {
+ if (nLo < nj) lcl_QuickSort(nLo, nj, rSortArray, pIndexOrder);
+ if (ni < nHi) lcl_QuickSort(ni, nHi, rSortArray, pIndexOrder);
+ }
+ else
+ {
+ if (ni < nHi) lcl_QuickSort(ni, nHi, rSortArray, pIndexOrder);
+ if (nLo < nj) lcl_QuickSort(nLo, nj, rSortArray, pIndexOrder);
+ }
+}
+
+void ScInterpreter::QuickSort( vector<double>& rSortArray, vector<tools::Long>* pIndexOrder )
+{
+ tools::Long n = static_cast<tools::Long>(rSortArray.size());
+
+ if (pIndexOrder)
+ {
+ pIndexOrder->clear();
+ pIndexOrder->reserve(n);
+ for (tools::Long i = 0; i < n; ++i)
+ pIndexOrder->push_back(i);
+ }
+
+ if (n < 2)
+ return;
+
+ size_t nValCount = rSortArray.size();
+ for (size_t i = 0; (i + 4) <= nValCount-1; i += 4)
+ {
+ size_t nInd = comphelper::rng::uniform_size_distribution(0, nValCount-2);
+ ::std::swap( rSortArray[i], rSortArray[nInd]);
+ if (pIndexOrder)
+ ::std::swap( pIndexOrder->at(i), pIndexOrder->at(nInd));
+ }
+
+ lcl_QuickSort(0, n-1, rSortArray, pIndexOrder);
+}
+
+void ScInterpreter::ScRank( bool bAverage )
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
+ return;
+ bool bAscending;
+ if ( nParamCount == 3 )
+ bAscending = GetBool();
+ else
+ bAscending = false;
+
+ vector<double> aSortArray;
+ GetSortArray( 1, aSortArray, nullptr, false, false );
+ double fVal = GetDouble();
+ SCSIZE nSize = aSortArray.size();
+ if ( nSize == 0 || nGlobalError != FormulaError::NONE )
+ PushNoValue();
+ else
+ {
+ if ( fVal < aSortArray[ 0 ] || fVal > aSortArray[ nSize - 1 ] )
+ PushError( FormulaError::NotAvailable);
+ else
+ {
+ double fLastPos = 0;
+ double fFirstPos = -1.0;
+ bool bFinished = false;
+ SCSIZE i;
+ for (i = 0; i < nSize && !bFinished; i++)
+ {
+ if ( aSortArray[ i ] == fVal )
+ {
+ if ( fFirstPos < 0 )
+ fFirstPos = i + 1.0;
+ }
+ else
+ {
+ if ( aSortArray[ i ] > fVal )
+ {
+ fLastPos = i;
+ bFinished = true;
+ }
+ }
+ }
+ if ( !bFinished )
+ fLastPos = i;
+ if (fFirstPos <= 0)
+ {
+ PushError( FormulaError::NotAvailable);
+ }
+ else if ( !bAverage )
+ {
+ if ( bAscending )
+ PushDouble( fFirstPos );
+ else
+ PushDouble( nSize + 1.0 - fLastPos );
+ }
+ else
+ {
+ if ( bAscending )
+ PushDouble( ( fFirstPos + fLastPos ) / 2.0 );
+ else
+ PushDouble( nSize + 1.0 - ( fFirstPos + fLastPos ) / 2.0 );
+ }
+ }
+ }
+}
+
+void ScInterpreter::ScAveDev()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+ sal_uInt16 SaveSP = sp;
+ double nMiddle = 0.0;
+ KahanSum rVal = 0.0;
+ double rValCount = 0.0;
+ ScAddress aAdr;
+ ScRange aRange;
+ short nParam = nParamCount;
+ size_t nRefInList = 0;
+ while (nParam-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ rVal += GetDouble();
+ rValCount++;
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ rVal += GetCellValue(aAdr, aCell);
+ rValCount++;
+ }
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ double nCellVal;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(nCellVal, nErr))
+ {
+ rVal += nCellVal;
+ rValCount++;
+ SetError(nErr);
+ while ((nErr == FormulaError::NONE) && aValIter.GetNext(nCellVal, nErr))
+ {
+ rVal += nCellVal;
+ rValCount++;
+ }
+ SetError(nErr);
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCount = pMat->GetElementCount();
+ if (pMat->IsNumeric())
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ {
+ rVal += pMat->GetDouble(nElem);
+ rValCount++;
+ }
+ }
+ else
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ if (!pMat->IsStringOrEmpty(nElem))
+ {
+ rVal += pMat->GetDouble(nElem);
+ rValCount++;
+ }
+ }
+ }
+ }
+ break;
+ default :
+ SetError(FormulaError::IllegalParameter);
+ break;
+ }
+ }
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ nMiddle = rVal.get() / rValCount;
+ sp = SaveSP;
+ rVal = 0.0;
+ nParam = nParamCount;
+ nRefInList = 0;
+ while (nParam-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ rVal += std::abs(GetDouble() - nMiddle);
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ rVal += std::abs(GetCellValue(aAdr, aCell) - nMiddle);
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ double nCellVal;
+ PopDoubleRef( aRange, nParam, nRefInList);
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(nCellVal, nErr))
+ {
+ rVal += std::abs(nCellVal - nMiddle);
+ while (aValIter.GetNext(nCellVal, nErr))
+ rVal += std::abs(nCellVal - nMiddle);
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCount = pMat->GetElementCount();
+ if (pMat->IsNumeric())
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ {
+ rVal += std::abs(pMat->GetDouble(nElem) - nMiddle);
+ }
+ }
+ else
+ {
+ for (SCSIZE nElem = 0; nElem < nCount; nElem++)
+ {
+ if (!pMat->IsStringOrEmpty(nElem))
+ rVal += std::abs(pMat->GetDouble(nElem) - nMiddle);
+ }
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ }
+ PushDouble(rVal.get() / rValCount);
+}
+
+void ScInterpreter::ScDevSq()
+{
+ auto VarResult = []( double fVal, size_t /*nValCount*/ )
+ {
+ return fVal;
+ };
+ GetStVarParams( false /*bTextAsZero*/, VarResult);
+}
+
+void ScInterpreter::ScProbability()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
+ return;
+ double fUp, fLo;
+ fUp = GetDouble();
+ if (nParamCount == 4)
+ fLo = GetDouble();
+ else
+ fLo = fUp;
+ if (fLo > fUp)
+ std::swap( fLo, fUp );
+ ScMatrixRef pMatP = GetMatrix();
+ ScMatrixRef pMatW = GetMatrix();
+ if (!pMatP || !pMatW)
+ PushIllegalParameter();
+ else
+ {
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMatP->GetDimensions(nC1, nR1);
+ pMatW->GetDimensions(nC2, nR2);
+ if (nC1 != nC2 || nR1 != nR2 || nC1 == 0 || nR1 == 0 ||
+ nC2 == 0 || nR2 == 0)
+ PushNA();
+ else
+ {
+ KahanSum fSum = 0.0;
+ KahanSum fRes = 0.0;
+ bool bStop = false;
+ double fP, fW;
+ for ( SCSIZE i = 0; i < nC1 && !bStop; i++ )
+ {
+ for (SCSIZE j = 0; j < nR1 && !bStop; ++j )
+ {
+ if (pMatP->IsValue(i,j) && pMatW->IsValue(i,j))
+ {
+ fP = pMatP->GetDouble(i,j);
+ fW = pMatW->GetDouble(i,j);
+ if (fP < 0.0 || fP > 1.0)
+ bStop = true;
+ else
+ {
+ fSum += fP;
+ if (fW >= fLo && fW <= fUp)
+ fRes += fP;
+ }
+ }
+ else
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ if (bStop || std::abs((fSum -1.0).get()) > 1.0E-7)
+ PushNoValue();
+ else
+ PushDouble(fRes.get());
+ }
+ }
+}
+
+void ScInterpreter::ScCorrel()
+{
+ // This is identical to ScPearson()
+ ScPearson();
+}
+
+void ScInterpreter::ScCovarianceP()
+{
+ CalculatePearsonCovar( false, false, false );
+}
+
+void ScInterpreter::ScCovarianceS()
+{
+ CalculatePearsonCovar( false, false, true );
+}
+
+void ScInterpreter::ScPearson()
+{
+ CalculatePearsonCovar( true, false, false );
+}
+
+void ScInterpreter::CalculatePearsonCovar( bool _bPearson, bool _bStexy, bool _bSample )
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ ScMatrixRef pMat1 = GetMatrix();
+ ScMatrixRef pMat2 = GetMatrix();
+ if (!pMat1 || !pMat2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ if (nR1 != nR2 || nC1 != nC2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ /* #i78250#
+ * (sum((X-MeanX)(Y-MeanY)))/N equals (SumXY)/N-MeanX*MeanY mathematically,
+ * but the latter produces wrong results if the absolute values are high,
+ * for example above 10^8
+ */
+ double fCount = 0.0;
+ KahanSum fSumX = 0.0;
+ KahanSum fSumY = 0.0;
+
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ fSumX += pMat1->GetDouble(i,j);
+ fSumY += pMat2->GetDouble(i,j);
+ fCount++;
+ }
+ }
+ }
+ if (fCount < (_bStexy ? 3.0 : (_bSample ? 2.0 : 1.0)))
+ PushNoValue();
+ else
+ {
+ KahanSum fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY)
+ KahanSum fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2
+ KahanSum fSumSqrDeltaY = 0.0; // sum of (ValY-MeanY)^2
+ const double fMeanX = fSumX.get() / fCount;
+ const double fMeanY = fSumY.get() / fCount;
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ const double fValX = pMat1->GetDouble(i,j);
+ const double fValY = pMat2->GetDouble(i,j);
+ fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY);
+ if ( _bPearson )
+ {
+ fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX);
+ fSumSqrDeltaY += (fValY - fMeanY) * (fValY - fMeanY);
+ }
+ }
+ }
+ }
+ if ( _bPearson )
+ {
+ // tdf#94962 - Values below the numerical limit lead to unexpected results
+ if (fSumSqrDeltaX < ::std::numeric_limits<double>::min()
+ || (!_bStexy && fSumSqrDeltaY < ::std::numeric_limits<double>::min()))
+ PushError( FormulaError::DivisionByZero);
+ else if ( _bStexy )
+ PushDouble( sqrt( ( fSumSqrDeltaY - fSumDeltaXDeltaY *
+ fSumDeltaXDeltaY / fSumSqrDeltaX ).get() / (fCount-2)));
+ else
+ PushDouble( fSumDeltaXDeltaY.get() / sqrt( fSumSqrDeltaX.get() * fSumSqrDeltaY.get() ));
+ }
+ else
+ {
+ if ( _bSample )
+ PushDouble( fSumDeltaXDeltaY.get() / ( fCount - 1 ) );
+ else
+ PushDouble( fSumDeltaXDeltaY.get() / fCount);
+ }
+ }
+}
+
+void ScInterpreter::ScRSQ()
+{
+ // Same as ScPearson()*ScPearson()
+ ScPearson();
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ switch (GetStackType())
+ {
+ case svDouble:
+ {
+ double fVal = PopDouble();
+ PushDouble( fVal * fVal);
+ }
+ break;
+ default:
+ PopError();
+ PushNoValue();
+ }
+}
+
+void ScInterpreter::ScSTEYX()
+{
+ CalculatePearsonCovar( true, true, false );
+}
+void ScInterpreter::CalculateSlopeIntercept(bool bSlope)
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+ ScMatrixRef pMat1 = GetMatrix();
+ ScMatrixRef pMat2 = GetMatrix();
+ if (!pMat1 || !pMat2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ if (nR1 != nR2 || nC1 != nC2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ // #i78250# numerical stability improved
+ double fCount = 0.0;
+ KahanSum fSumX = 0.0;
+ KahanSum fSumY = 0.0;
+
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ fSumX += pMat1->GetDouble(i,j);
+ fSumY += pMat2->GetDouble(i,j);
+ fCount++;
+ }
+ }
+ }
+ if (fCount < 1.0)
+ PushNoValue();
+ else
+ {
+ KahanSum fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY)
+ KahanSum fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2
+ double fMeanX = fSumX.get() / fCount;
+ double fMeanY = fSumY.get() / fCount;
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ double fValX = pMat1->GetDouble(i,j);
+ double fValY = pMat2->GetDouble(i,j);
+ fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY);
+ fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX);
+ }
+ }
+ }
+ if (fSumSqrDeltaX == 0.0)
+ PushError( FormulaError::DivisionByZero);
+ else
+ {
+ if ( bSlope )
+ PushDouble( fSumDeltaXDeltaY.get() / fSumSqrDeltaX.get());
+ else
+ PushDouble( fMeanY - fSumDeltaXDeltaY.get() / fSumSqrDeltaX.get() * fMeanX);
+ }
+ }
+}
+
+void ScInterpreter::ScSlope()
+{
+ CalculateSlopeIntercept(true);
+}
+
+void ScInterpreter::ScIntercept()
+{
+ CalculateSlopeIntercept(false);
+}
+
+void ScInterpreter::ScForecast()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+ ScMatrixRef pMat1 = GetMatrix();
+ ScMatrixRef pMat2 = GetMatrix();
+ if (!pMat1 || !pMat2)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ if (nR1 != nR2 || nC1 != nC2)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ double fVal = GetDouble();
+ // #i78250# numerical stability improved
+ double fCount = 0.0;
+ KahanSum fSumX = 0.0;
+ KahanSum fSumY = 0.0;
+
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ fSumX += pMat1->GetDouble(i,j);
+ fSumY += pMat2->GetDouble(i,j);
+ fCount++;
+ }
+ }
+ }
+ if (fCount < 1.0)
+ PushNoValue();
+ else
+ {
+ KahanSum fSumDeltaXDeltaY = 0.0; // sum of (ValX-MeanX)*(ValY-MeanY)
+ KahanSum fSumSqrDeltaX = 0.0; // sum of (ValX-MeanX)^2
+ double fMeanX = fSumX.get() / fCount;
+ double fMeanY = fSumY.get() / fCount;
+ for (SCSIZE i = 0; i < nC1; i++)
+ {
+ for (SCSIZE j = 0; j < nR1; j++)
+ {
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ double fValX = pMat1->GetDouble(i,j);
+ double fValY = pMat2->GetDouble(i,j);
+ fSumDeltaXDeltaY += (fValX - fMeanX) * (fValY - fMeanY);
+ fSumSqrDeltaX += (fValX - fMeanX) * (fValX - fMeanX);
+ }
+ }
+ }
+ if (fSumSqrDeltaX == 0.0)
+ PushError( FormulaError::DivisionByZero);
+ else
+ PushDouble( fMeanY + fSumDeltaXDeltaY.get() / fSumSqrDeltaX.get() * (fVal - fMeanX));
+ }
+}
+
+static void lcl_roundUpNearestPow2(SCSIZE& nNum, SCSIZE& nNumBits)
+{
+ // Find the least power of 2 that is less than or equal to nNum.
+ SCSIZE nPow2(1);
+ nNumBits = std::numeric_limits<SCSIZE>::digits;
+ nPow2 <<= (nNumBits - 1);
+ while (nPow2 >= 1)
+ {
+ if (nNum & nPow2)
+ break;
+
+ --nNumBits;
+ nPow2 >>= 1;
+ }
+
+ if (nPow2 != nNum)
+ nNum = nPow2 ? (nPow2 << 1) : 1;
+ else
+ --nNumBits;
+}
+
+static SCSIZE lcl_bitReverse(SCSIZE nIn, SCSIZE nBound)
+{
+ SCSIZE nOut = 0;
+ for (SCSIZE nMask = 1; nMask < nBound; nMask <<= 1)
+ {
+ nOut <<= 1;
+
+ if (nIn & nMask)
+ nOut |= 1;
+ }
+
+ return nOut;
+}
+
+namespace {
+
+// Computes and stores twiddle factors for computing DFT later.
+struct ScTwiddleFactors
+{
+ ScTwiddleFactors(SCSIZE nN, bool bInverse) :
+ mfWReal(nN),
+ mfWImag(nN),
+ mnN(nN),
+ mbInverse(bInverse)
+ {}
+
+ void Compute();
+
+ void Conjugate()
+ {
+ mbInverse = !mbInverse;
+ for (SCSIZE nIdx = 0; nIdx < mnN; ++nIdx)
+ mfWImag[nIdx] = -mfWImag[nIdx];
+ }
+
+ std::vector<double> mfWReal;
+ std::vector<double> mfWImag;
+ SCSIZE mnN;
+ bool mbInverse;
+};
+
+}
+
+void ScTwiddleFactors::Compute()
+{
+ mfWReal.resize(mnN);
+ mfWImag.resize(mnN);
+
+ double nW = (mbInverse ? 2 : -2)*M_PI/static_cast<double>(mnN);
+
+ if (mnN == 1)
+ {
+ mfWReal[0] = 1.0;
+ mfWImag[0] = 0.0;
+ }
+ else if (mnN == 2)
+ {
+ mfWReal[0] = 1;
+ mfWImag[0] = 0;
+
+ mfWReal[1] = -1;
+ mfWImag[1] = 0;
+ }
+ else if (mnN == 4)
+ {
+ mfWReal[0] = 1;
+ mfWImag[0] = 0;
+
+ mfWReal[1] = 0;
+ mfWImag[1] = (mbInverse ? 1.0 : -1.0);
+
+ mfWReal[2] = -1;
+ mfWImag[2] = 0;
+
+ mfWReal[3] = 0;
+ mfWImag[3] = (mbInverse ? -1.0 : 1.0);
+ }
+ else if ((mnN % 4) == 0)
+ {
+ const SCSIZE nQSize = mnN >> 2;
+ // Compute cos of the start quadrant.
+ // This is the first quadrant if mbInverse == true, else it is the fourth quadrant.
+ for (SCSIZE nIdx = 0; nIdx <= nQSize; ++nIdx)
+ mfWReal[nIdx] = cos(nW*static_cast<double>(nIdx));
+
+ if (mbInverse)
+ {
+ const SCSIZE nQ1End = nQSize;
+ // First quadrant
+ for (SCSIZE nIdx = 0; nIdx <= nQ1End; ++nIdx)
+ mfWImag[nIdx] = mfWReal[nQ1End-nIdx];
+
+ // Second quadrant
+ const SCSIZE nQ2End = nQ1End << 1;
+ for (SCSIZE nIdx = nQ1End+1; nIdx <= nQ2End; ++nIdx)
+ {
+ mfWReal[nIdx] = -mfWReal[nQ2End - nIdx];
+ mfWImag[nIdx] = mfWImag[nQ2End - nIdx];
+ }
+
+ // Third quadrant
+ const SCSIZE nQ3End = nQ2End + nQ1End;
+ for (SCSIZE nIdx = nQ2End+1; nIdx <= nQ3End; ++nIdx)
+ {
+ mfWReal[nIdx] = -mfWReal[nIdx - nQ2End];
+ mfWImag[nIdx] = -mfWImag[nIdx - nQ2End];
+ }
+
+ // Fourth Quadrant
+ for (SCSIZE nIdx = nQ3End+1; nIdx < mnN; ++nIdx)
+ {
+ mfWReal[nIdx] = mfWReal[mnN - nIdx];
+ mfWImag[nIdx] = -mfWImag[mnN - nIdx];
+ }
+ }
+ else
+ {
+ const SCSIZE nQ4End = nQSize;
+ const SCSIZE nQ3End = nQSize << 1;
+ const SCSIZE nQ2End = nQ3End + nQSize;
+
+ // Fourth quadrant.
+ for (SCSIZE nIdx = 0; nIdx <= nQ4End; ++nIdx)
+ mfWImag[nIdx] = -mfWReal[nQ4End - nIdx];
+
+ // Third quadrant.
+ for (SCSIZE nIdx = nQ4End+1; nIdx <= nQ3End; ++nIdx)
+ {
+ mfWReal[nIdx] = -mfWReal[nQ3End - nIdx];
+ mfWImag[nIdx] = mfWImag[nQ3End - nIdx];
+ }
+
+ // Second quadrant.
+ for (SCSIZE nIdx = nQ3End+1; nIdx <= nQ2End; ++nIdx)
+ {
+ mfWReal[nIdx] = -mfWReal[nIdx - nQ3End];
+ mfWImag[nIdx] = -mfWImag[nIdx - nQ3End];
+ }
+
+ // First quadrant.
+ for (SCSIZE nIdx = nQ2End+1; nIdx < mnN; ++nIdx)
+ {
+ mfWReal[nIdx] = mfWReal[mnN - nIdx];
+ mfWImag[nIdx] = -mfWImag[mnN - nIdx];
+ }
+ }
+ }
+ else
+ {
+ for (SCSIZE nIdx = 0; nIdx < mnN; ++nIdx)
+ {
+ double fAngle = nW*static_cast<double>(nIdx);
+ mfWReal[nIdx] = cos(fAngle);
+ mfWImag[nIdx] = sin(fAngle);
+ }
+ }
+}
+
+namespace {
+
+// A radix-2 decimation in time FFT algorithm for complex valued input.
+class ScComplexFFT2
+{
+public:
+ // rfArray.size() would always be even and a power of 2. (asserted in prepare())
+ // rfArray's first half contains the real parts and the later half contains the imaginary parts.
+ ScComplexFFT2(std::vector<double>& raArray, bool bInverse, bool bPolar, double fMinMag,
+ ScTwiddleFactors& rTF, bool bSubSampleTFs = false, bool bDisableNormalize = false) :
+ mrArray(raArray),
+ mfWReal(rTF.mfWReal),
+ mfWImag(rTF.mfWImag),
+ mnPoints(raArray.size()/2),
+ mnStages(0),
+ mfMinMag(fMinMag),
+ mbInverse(bInverse),
+ mbPolar(bPolar),
+ mbDisableNormalize(bDisableNormalize),
+ mbSubSampleTFs(bSubSampleTFs)
+ {}
+
+ void Compute();
+
+private:
+
+ void prepare();
+
+ double getReal(SCSIZE nIdx)
+ {
+ return mrArray[nIdx];
+ }
+
+ void setReal(double fVal, SCSIZE nIdx)
+ {
+ mrArray[nIdx] = fVal;
+ }
+
+ double getImag(SCSIZE nIdx)
+ {
+ return mrArray[mnPoints + nIdx];
+ }
+
+ void setImag(double fVal, SCSIZE nIdx)
+ {
+ mrArray[mnPoints + nIdx] = fVal;
+ }
+
+ SCSIZE getTFactorIndex(SCSIZE nPtIndex, SCSIZE nTfIdxScaleBits)
+ {
+ return ( ( nPtIndex << nTfIdxScaleBits ) & ( mnPoints - 1 ) ); // (x & (N-1)) is same as (x % N) but faster.
+ }
+
+ void computeFly(SCSIZE nTopIdx, SCSIZE nBottomIdx, SCSIZE nWIdx1, SCSIZE nWIdx2)
+ {
+ if (mbSubSampleTFs)
+ {
+ nWIdx1 <<= 1;
+ nWIdx2 <<= 1;
+ }
+
+ const double x1r = getReal(nTopIdx);
+ const double x2r = getReal(nBottomIdx);
+
+ const double& w1r = mfWReal[nWIdx1];
+ const double& w1i = mfWImag[nWIdx1];
+
+ const double& w2r = mfWReal[nWIdx2];
+ const double& w2i = mfWImag[nWIdx2];
+
+ const double x1i = getImag(nTopIdx);
+ const double x2i = getImag(nBottomIdx);
+
+ setReal(x1r + x2r*w1r - x2i*w1i, nTopIdx);
+ setImag(x1i + x2i*w1r + x2r*w1i, nTopIdx);
+
+ setReal(x1r + x2r*w2r - x2i*w2i, nBottomIdx);
+ setImag(x1i + x2i*w2r + x2r*w2i, nBottomIdx);
+ }
+
+ std::vector<double>& mrArray;
+ std::vector<double>& mfWReal;
+ std::vector<double>& mfWImag;
+ SCSIZE mnPoints;
+ SCSIZE mnStages;
+ double mfMinMag;
+ bool mbInverse:1;
+ bool mbPolar:1;
+ bool mbDisableNormalize:1;
+ bool mbSubSampleTFs:1;
+};
+
+}
+
+void ScComplexFFT2::prepare()
+{
+ SCSIZE nPoints = mnPoints;
+ lcl_roundUpNearestPow2(nPoints, mnStages);
+ assert(nPoints == mnPoints);
+
+ // Reorder array by bit-reversed indices.
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ SCSIZE nRevIdx = lcl_bitReverse(nIdx, mnPoints);
+ if (nIdx < nRevIdx)
+ {
+ double fTmp = getReal(nIdx);
+ setReal(getReal(nRevIdx), nIdx);
+ setReal(fTmp, nRevIdx);
+
+ fTmp = getImag(nIdx);
+ setImag(getImag(nRevIdx), nIdx);
+ setImag(fTmp, nRevIdx);
+ }
+ }
+}
+
+static void lcl_normalize(std::vector<double>& rCmplxArray, bool bScaleOnlyReal)
+{
+ const SCSIZE nPoints = rCmplxArray.size()/2;
+ const double fScale = 1.0/static_cast<double>(nPoints);
+
+ // Scale the real part
+ for (SCSIZE nIdx = 0; nIdx < nPoints; ++nIdx)
+ rCmplxArray[nIdx] *= fScale;
+
+ if (!bScaleOnlyReal)
+ {
+ const SCSIZE nLen = nPoints*2;
+ for (SCSIZE nIdx = nPoints; nIdx < nLen; ++nIdx)
+ rCmplxArray[nIdx] *= fScale;
+ }
+}
+
+static void lcl_convertToPolar(std::vector<double>& rCmplxArray, double fMinMag)
+{
+ const SCSIZE nPoints = rCmplxArray.size()/2;
+ double fMag, fPhase, fR, fI;
+ for (SCSIZE nIdx = 0; nIdx < nPoints; ++nIdx)
+ {
+ fR = rCmplxArray[nIdx];
+ fI = rCmplxArray[nPoints+nIdx];
+ fMag = std::hypot(fR, fI);
+ if (fMag < fMinMag)
+ {
+ fMag = 0.0;
+ fPhase = 0.0;
+ }
+ else
+ {
+ fPhase = atan2(fI, fR);
+ }
+
+ rCmplxArray[nIdx] = fMag;
+ rCmplxArray[nPoints+nIdx] = fPhase;
+ }
+}
+
+void ScComplexFFT2::Compute()
+{
+ prepare();
+
+ const SCSIZE nFliesInStage = mnPoints/2;
+ for (SCSIZE nStage = 0; nStage < mnStages; ++nStage)
+ {
+ const SCSIZE nTFIdxScaleBits = mnStages - nStage - 1; // Twiddle factor index's scale factor in bits.
+ const SCSIZE nFliesInGroup = SCSIZE(1) << nStage;
+ const SCSIZE nGroups = nFliesInStage/nFliesInGroup;
+ const SCSIZE nFlyWidth = nFliesInGroup;
+ for (SCSIZE nGroup = 0, nFlyTopIdx = 0; nGroup < nGroups; ++nGroup)
+ {
+ for (SCSIZE nFly = 0; nFly < nFliesInGroup; ++nFly, ++nFlyTopIdx)
+ {
+ SCSIZE nFlyBottomIdx = nFlyTopIdx + nFlyWidth;
+ SCSIZE nWIdx1 = getTFactorIndex(nFlyTopIdx, nTFIdxScaleBits);
+ SCSIZE nWIdx2 = getTFactorIndex(nFlyBottomIdx, nTFIdxScaleBits);
+
+ computeFly(nFlyTopIdx, nFlyBottomIdx, nWIdx1, nWIdx2);
+ }
+
+ nFlyTopIdx += nFlyWidth;
+ }
+ }
+
+ if (mbPolar)
+ lcl_convertToPolar(mrArray, mfMinMag);
+
+ // Normalize after converting to polar, so we have a chance to
+ // save O(mnPoints) flops.
+ if (mbInverse && !mbDisableNormalize)
+ lcl_normalize(mrArray, mbPolar);
+}
+
+namespace {
+
+// Bluestein's algorithm or chirp z-transform algorithm that can be used to
+// compute DFT of a complex valued input of any length N in O(N lgN) time.
+class ScComplexBluesteinFFT
+{
+public:
+
+ ScComplexBluesteinFFT(std::vector<double>& rArray, bool bReal, bool bInverse,
+ bool bPolar, double fMinMag, bool bDisableNormalize = false) :
+ mrArray(rArray),
+ mnPoints(rArray.size()/2), // rArray should have space for imaginary parts even if real input.
+ mfMinMag(fMinMag),
+ mbReal(bReal),
+ mbInverse(bInverse),
+ mbPolar(bPolar),
+ mbDisableNormalize(bDisableNormalize)
+ {}
+
+ void Compute();
+
+private:
+ std::vector<double>& mrArray;
+ const SCSIZE mnPoints;
+ double mfMinMag;
+ bool mbReal:1;
+ bool mbInverse:1;
+ bool mbPolar:1;
+ bool mbDisableNormalize:1;
+};
+
+}
+
+void ScComplexBluesteinFFT::Compute()
+{
+ std::vector<double> aRealScalars(mnPoints);
+ std::vector<double> aImagScalars(mnPoints);
+ double fW = (mbInverse ? 2 : -2)*M_PI/static_cast<double>(mnPoints);
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ double fAngle = 0.5*fW*static_cast<double>(nIdx*nIdx);
+ aRealScalars[nIdx] = cos(fAngle);
+ aImagScalars[nIdx] = sin(fAngle);
+ }
+
+ SCSIZE nMinSize = mnPoints*2 - 1;
+ SCSIZE nExtendedLength = nMinSize, nTmp = 0;
+ lcl_roundUpNearestPow2(nExtendedLength, nTmp);
+ std::vector<double> aASignal(nExtendedLength*2); // complex valued
+ std::vector<double> aBSignal(nExtendedLength*2); // complex valued
+
+ double fReal, fImag;
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ // Real part of A signal.
+ aASignal[nIdx] = mrArray[nIdx]*aRealScalars[nIdx] + (mbReal ? 0.0 : -mrArray[mnPoints+nIdx]*aImagScalars[nIdx]);
+ // Imaginary part of A signal.
+ aASignal[nExtendedLength + nIdx] = mrArray[nIdx]*aImagScalars[nIdx] + (mbReal ? 0.0 : mrArray[mnPoints+nIdx]*aRealScalars[nIdx]);
+
+ // Real part of B signal.
+ aBSignal[nIdx] = fReal = aRealScalars[nIdx];
+ // Imaginary part of B signal.
+ aBSignal[nExtendedLength + nIdx] = fImag = -aImagScalars[nIdx]; // negative sign because B signal is the conjugation of the scalars.
+
+ if (nIdx)
+ {
+ // B signal needs a mirror of its part in 0 < n < mnPoints at the tail end.
+ aBSignal[nExtendedLength - nIdx] = fReal;
+ aBSignal[(nExtendedLength<<1) - nIdx] = fImag;
+ }
+ }
+
+ {
+ ScTwiddleFactors aTF(nExtendedLength, false /*not inverse*/);
+ aTF.Compute();
+
+ // Do complex-FFT2 of both A and B signal.
+ ScComplexFFT2 aFFT2A(aASignal, false /*not inverse*/, false /*no polar*/, 0.0 /* no clipping */,
+ aTF, false /*no subsample*/, true /* disable normalize */);
+ aFFT2A.Compute();
+
+ ScComplexFFT2 aFFT2B(aBSignal, false /*not inverse*/, false /*no polar*/, 0.0 /* no clipping */,
+ aTF, false /*no subsample*/, true /* disable normalize */);
+ aFFT2B.Compute();
+
+ double fAR, fAI, fBR, fBI;
+ for (SCSIZE nIdx = 0; nIdx < nExtendedLength; ++nIdx)
+ {
+ fAR = aASignal[nIdx];
+ fAI = aASignal[nExtendedLength + nIdx];
+ fBR = aBSignal[nIdx];
+ fBI = aBSignal[nExtendedLength + nIdx];
+
+ // Do point-wise product inplace in A signal.
+ aASignal[nIdx] = fAR*fBR - fAI*fBI;
+ aASignal[nExtendedLength + nIdx] = fAR*fBI + fAI*fBR;
+ }
+
+ // Do complex-inverse-FFT2 of aASignal.
+ aTF.Conjugate();
+ ScComplexFFT2 aFFT2AI(aASignal, true /*inverse*/, false /*no polar*/, 0.0 /* no clipping */, aTF); // Need normalization here.
+ aFFT2AI.Compute();
+ }
+
+ // Point-wise multiply with scalars.
+ for (SCSIZE nIdx = 0; nIdx < mnPoints; ++nIdx)
+ {
+ fReal = aASignal[nIdx];
+ fImag = aASignal[nExtendedLength + nIdx];
+ mrArray[nIdx] = fReal*aRealScalars[nIdx] - fImag*aImagScalars[nIdx]; // no conjugation needed here.
+ mrArray[mnPoints + nIdx] = fReal*aImagScalars[nIdx] + fImag*aRealScalars[nIdx];
+ }
+
+ // Normalize/Polar operations
+ if (mbPolar)
+ lcl_convertToPolar(mrArray, mfMinMag);
+
+ // Normalize after converting to polar, so we have a chance to
+ // save O(mnPoints) flops.
+ if (mbInverse && !mbDisableNormalize)
+ lcl_normalize(mrArray, mbPolar);
+}
+
+namespace {
+
+// Computes DFT of an even length(N) real-valued input by using a
+// ScComplexFFT2 if N == 2^k for some k or else by using a ScComplexBluesteinFFT
+// with a complex valued input of length = N/2.
+class ScRealFFT
+{
+public:
+
+ ScRealFFT(std::vector<double>& rInArray, std::vector<double>& rOutArray, bool bInverse,
+ bool bPolar, double fMinMag) :
+ mrInArray(rInArray),
+ mrOutArray(rOutArray),
+ mfMinMag(fMinMag),
+ mbInverse(bInverse),
+ mbPolar(bPolar)
+ {}
+
+ void Compute();
+
+private:
+ std::vector<double>& mrInArray;
+ std::vector<double>& mrOutArray;
+ double mfMinMag;
+ bool mbInverse:1;
+ bool mbPolar:1;
+};
+
+}
+
+void ScRealFFT::Compute()
+{
+ // input length has to be even to do this optimization.
+ assert(mrInArray.size() % 2 == 0);
+ assert(mrInArray.size()*2 == mrOutArray.size());
+ // nN is the number of points in the complex-fft input
+ // which will be half of the number of points in real array.
+ const SCSIZE nN = mrInArray.size()/2;
+ if (nN == 0)
+ {
+ mrOutArray[0] = mrInArray[0];
+ mrOutArray[1] = 0.0;
+ return;
+ }
+
+ // work array should be the same length as mrInArray
+ std::vector<double> aWorkArray(nN*2);
+ for (SCSIZE nIdx = 0; nIdx < nN; ++nIdx)
+ {
+ SCSIZE nDoubleIdx = 2*nIdx;
+ // Use even elements as real part
+ aWorkArray[nIdx] = mrInArray[nDoubleIdx];
+ // and odd elements as imaginary part of the contrived complex sequence.
+ aWorkArray[nN+nIdx] = mrInArray[nDoubleIdx+1];
+ }
+
+ ScTwiddleFactors aTFs(nN*2, mbInverse);
+ aTFs.Compute();
+ SCSIZE nNextPow2 = nN, nTmp = 0;
+ lcl_roundUpNearestPow2(nNextPow2, nTmp);
+
+ if (nNextPow2 == nN)
+ {
+ ScComplexFFT2 aFFT2(aWorkArray, mbInverse, false /*disable polar*/, 0.0 /* no clipping */,
+ aTFs, true /*subsample tf*/, true /*disable normalize*/);
+ aFFT2.Compute();
+ }
+ else
+ {
+ ScComplexBluesteinFFT aFFT(aWorkArray, false /*complex input*/, mbInverse, false /*disable polar*/,
+ 0.0 /* no clipping */, true /*disable normalize*/);
+ aFFT.Compute();
+ }
+
+ // Post process aWorkArray to populate mrOutArray
+
+ const SCSIZE nTwoN = 2*nN, nThreeN = 3*nN;
+ double fY1R, fY2R, fY1I, fY2I, fResR, fResI, fWR, fWI;
+ for (SCSIZE nIdx = 0; nIdx < nN; ++nIdx)
+ {
+ const SCSIZE nIdxRev = nIdx ? (nN - nIdx) : 0;
+ fY1R = aWorkArray[nIdx];
+ fY2R = aWorkArray[nIdxRev];
+ fY1I = aWorkArray[nN + nIdx];
+ fY2I = aWorkArray[nN + nIdxRev];
+ fWR = aTFs.mfWReal[nIdx];
+ fWI = aTFs.mfWImag[nIdx];
+
+ // mrOutArray has length = 4*nN
+ // Real part of the final output (only half of the symmetry around Nyquist frequency)
+ // Fills the first quarter.
+ mrOutArray[nIdx] = fResR = 0.5*(
+ fY1R + fY2R +
+ fWR * (fY1I + fY2I) +
+ fWI * (fY1R - fY2R) );
+ // Imaginary part of the final output (only half of the symmetry around Nyquist frequency)
+ // Fills the third quarter.
+ mrOutArray[nTwoN + nIdx] = fResI = 0.5*(
+ fY1I - fY2I +
+ fWI * (fY1I + fY2I) -
+ fWR * (fY1R - fY2R) );
+
+ // Fill the missing 2 quarters using symmetry argument.
+ if (nIdx)
+ {
+ // Fills the 2nd quarter.
+ mrOutArray[nN + nIdxRev] = fResR;
+ // Fills the 4th quarter.
+ mrOutArray[nThreeN + nIdxRev] = -fResI;
+ }
+ else
+ {
+ mrOutArray[nN] = fY1R - fY1I;
+ mrOutArray[nThreeN] = 0.0;
+ }
+ }
+
+ // Normalize/Polar operations
+ if (mbPolar)
+ lcl_convertToPolar(mrOutArray, mfMinMag);
+
+ // Normalize after converting to polar, so we have a chance to
+ // save O(mnPoints) flops.
+ if (mbInverse)
+ lcl_normalize(mrOutArray, mbPolar);
+}
+
+using ScMatrixGenerator = ScMatrixRef(SCSIZE, SCSIZE, std::vector<double>&);
+
+namespace {
+
+// Generic FFT class that decides which FFT implementation to use.
+class ScFFT
+{
+public:
+
+ ScFFT(ScMatrixRef& pMat, bool bReal, bool bInverse, bool bPolar, double fMinMag) :
+ mpInputMat(pMat),
+ mfMinMag(fMinMag),
+ mbReal(bReal),
+ mbInverse(bInverse),
+ mbPolar(bPolar)
+ {}
+
+ ScMatrixRef Compute(const std::function<ScMatrixGenerator>& rMatGenFunc);
+
+private:
+ ScMatrixRef& mpInputMat;
+ double mfMinMag;
+ bool mbReal:1;
+ bool mbInverse:1;
+ bool mbPolar:1;
+};
+
+}
+
+ScMatrixRef ScFFT::Compute(const std::function<ScMatrixGenerator>& rMatGenFunc)
+{
+ std::vector<double> aArray;
+ mpInputMat->GetDoubleArray(aArray);
+ SCSIZE nPoints = mbReal ? aArray.size() : (aArray.size()/2);
+ if (nPoints == 1)
+ {
+ std::vector<double> aOutArray(2);
+ aOutArray[0] = aArray[0];
+ aOutArray[1] = mbReal ? 0.0 : aArray[1];
+ if (mbPolar)
+ lcl_convertToPolar(aOutArray, mfMinMag);
+ return rMatGenFunc(2, 1, aOutArray);
+ }
+
+ if (mbReal && (nPoints % 2) == 0)
+ {
+ std::vector<double> aOutArray(nPoints*2);
+ ScRealFFT aFFT(aArray, aOutArray, mbInverse, mbPolar, mfMinMag);
+ aFFT.Compute();
+ return rMatGenFunc(2, nPoints, aOutArray);
+ }
+
+ SCSIZE nNextPow2 = nPoints, nTmp = 0;
+ lcl_roundUpNearestPow2(nNextPow2, nTmp);
+ if (nNextPow2 == nPoints && !mbReal)
+ {
+ ScTwiddleFactors aTF(nPoints, mbInverse);
+ aTF.Compute();
+ ScComplexFFT2 aFFT2(aArray, mbInverse, mbPolar, mfMinMag, aTF);
+ aFFT2.Compute();
+ return rMatGenFunc(2, nPoints, aArray);
+ }
+
+ if (mbReal)
+ aArray.resize(nPoints*2, 0.0);
+ ScComplexBluesteinFFT aFFT(aArray, mbReal, mbInverse, mbPolar, mfMinMag);
+ aFFT.Compute();
+ return rMatGenFunc(2, nPoints, aArray);
+}
+
+void ScInterpreter::ScFourier()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 2, 5 ) )
+ return;
+
+ bool bInverse = false;
+ bool bPolar = false;
+ double fMinMag = 0.0;
+
+ if (nParamCount == 5)
+ {
+ if (IsMissing())
+ Pop();
+ else
+ fMinMag = GetDouble();
+ }
+
+ if (nParamCount >= 4)
+ {
+ if (IsMissing())
+ Pop();
+ else
+ bPolar = GetBool();
+ }
+
+ if (nParamCount >= 3)
+ {
+ if (IsMissing())
+ Pop();
+ else
+ bInverse = GetBool();
+ }
+
+ bool bGroupedByColumn = GetBool();
+
+ ScMatrixRef pInputMat = GetMatrix();
+ if (!pInputMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ SCSIZE nC, nR;
+ pInputMat->GetDimensions(nC, nR);
+
+ if ((bGroupedByColumn && nC > 2) || (!bGroupedByColumn && nR > 2))
+ {
+ // There can be no more than 2 columns (real, imaginary) if data grouped by columns.
+ // and no more than 2 rows if data is grouped by rows.
+ PushIllegalArgument();
+ return;
+ }
+
+ if (!pInputMat->IsNumeric())
+ {
+ PushNoValue();
+ return;
+ }
+
+ bool bRealInput = true;
+ if (!bGroupedByColumn)
+ {
+ pInputMat->MatTrans(*pInputMat);
+ bRealInput = (nR == 1);
+ }
+ else
+ {
+ bRealInput = (nC == 1);
+ }
+
+ ScFFT aFFT(pInputMat, bRealInput, bInverse, bPolar, fMinMag);
+ std::function<ScMatrixGenerator> aFunc = [this](SCSIZE nCol, SCSIZE nRow, std::vector<double>& rVec) -> ScMatrixRef
+ {
+ return this->GetNewMat(nCol, nRow, rVec);
+ };
+ ScMatrixRef pOut = aFFT.Compute(aFunc);
+ PushMatrix(pOut);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
new file mode 100644
index 0000000000..95dff9f1cc
--- /dev/null
+++ b/sc/source/core/tool/interpr4.cxx
@@ -0,0 +1,4839 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <interpre.hxx>
+
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/math.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbxobj.hxx>
+#include <basic/sbuno.hxx>
+#include <osl/thread.h>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <unotools/charclass.hxx>
+#include <stdlib.h>
+#include <string.h>
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/script/XInvocation.hpp>
+#include <com/sun/star/sheet/XSheetCellRange.hpp>
+
+#include <global.hxx>
+#include <dbdata.hxx>
+#include <formulacell.hxx>
+#include <callform.hxx>
+#include <addincol.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <docsh.hxx>
+#include <docoptio.hxx>
+#include <scmatrix.hxx>
+#include <adiasync.hxx>
+#include <cellsuno.hxx>
+#include <optuno.hxx>
+#include <rangeseq.hxx>
+#include <addinlis.hxx>
+#include <jumpmatrix.hxx>
+#include <parclass.hxx>
+#include <externalrefmgr.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <macromgr.hxx>
+#include <doubleref.hxx>
+#include <queryparam.hxx>
+#include <tokenarray.hxx>
+#include <compiler.hxx>
+
+#include <map>
+#include <algorithm>
+#include <basic/basmgr.hxx>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using namespace formula;
+using ::std::unique_ptr;
+
+#define ADDIN_MAXSTRLEN 256
+
+thread_local std::unique_ptr<ScTokenStack> ScInterpreter::pGlobalStack;
+thread_local bool ScInterpreter::bGlobalStackInUse = false;
+
+// document access functions
+
+void ScInterpreter::ReplaceCell( ScAddress& rPos )
+{
+ size_t ListSize = mrDoc.m_TableOpList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScInterpreterTableOpParams *const pTOp = mrDoc.m_TableOpList[ i ];
+ if ( rPos == pTOp->aOld1 )
+ {
+ rPos = pTOp->aNew1;
+ return ;
+ }
+ else if ( rPos == pTOp->aOld2 )
+ {
+ rPos = pTOp->aNew2;
+ return ;
+ }
+ }
+}
+
+bool ScInterpreter::IsTableOpInRange( const ScRange& rRange )
+{
+ if ( rRange.aStart == rRange.aEnd )
+ return false; // not considered to be a range in TableOp sense
+
+ // we can't replace a single cell in a range
+ size_t ListSize = mrDoc.m_TableOpList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScInterpreterTableOpParams *const pTOp = mrDoc.m_TableOpList[ i ];
+ if ( rRange.Contains( pTOp->aOld1 ) )
+ return true;
+ if ( rRange.Contains( pTOp->aOld2 ) )
+ return true;
+ }
+ return false;
+}
+
+sal_uInt32 ScInterpreter::GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell )
+{
+ sal_uInt32 nFormat;
+ FormulaError nErr;
+ if (rCell.isEmpty())
+ {
+ nFormat = mrDoc.GetNumberFormat( mrContext, rPos );
+ nErr = FormulaError::NONE;
+ }
+ else
+ {
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ nErr = rCell.getFormula()->GetErrCode();
+ else
+ nErr = FormulaError::NONE;
+ nFormat = mrDoc.GetNumberFormat( mrContext, rPos );
+ }
+
+ SetError(nErr);
+ return nFormat;
+}
+
+/// Only ValueCell, formula cells already store the result rounded.
+double ScInterpreter::GetValueCellValue( const ScAddress& rPos, double fOrig )
+{
+ if ( bCalcAsShown && fOrig != 0.0 )
+ {
+ sal_uInt32 nFormat = mrDoc.GetNumberFormat( mrContext, rPos );
+ fOrig = mrDoc.RoundValueAsShown( fOrig, nFormat, &mrContext );
+ }
+ return fOrig;
+}
+
+FormulaError ScInterpreter::GetCellErrCode( const ScRefCellValue& rCell )
+{
+ return rCell.getType() == CELLTYPE_FORMULA ? rCell.getFormula()->GetErrCode() : FormulaError::NONE;
+}
+
+double ScInterpreter::ConvertStringToValue( const OUString& rStr )
+{
+ FormulaError nError = FormulaError::NONE;
+ double fValue = ScGlobal::ConvertStringToValue( rStr, maCalcConfig, nError, mnStringNoValueError,
+ pFormatter, nCurFmtType);
+ if (nError != FormulaError::NONE)
+ SetError(nError);
+ return fValue;
+}
+
+double ScInterpreter::ConvertStringToValue( const OUString& rStr, FormulaError& rError, SvNumFormatType& rCurFmtType )
+{
+ return ScGlobal::ConvertStringToValue( rStr, maCalcConfig, rError, mnStringNoValueError, pFormatter, rCurFmtType);
+}
+
+double ScInterpreter::GetCellValue( const ScAddress& rPos, ScRefCellValue& rCell )
+{
+ FormulaError nErr = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+ double nVal = GetCellValueOrZero(rPos, rCell);
+ // Propagate previous error, if any; nGlobalError==CellNoValue is not an
+ // error here, preserve previous error or non-error.
+ if (nErr != FormulaError::NONE || nGlobalError == FormulaError::CellNoValue)
+ nGlobalError = nErr;
+ return nVal;
+}
+
+double ScInterpreter::GetCellValueOrZero( const ScAddress& rPos, ScRefCellValue& rCell )
+{
+ double fValue = 0.0;
+
+ CellType eType = rCell.getType();
+ switch (eType)
+ {
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ FormulaError nErr = pFCell->GetErrCode();
+ if( nErr == FormulaError::NONE )
+ {
+ if (pFCell->IsValue())
+ {
+ fValue = pFCell->GetValue();
+ mrDoc.GetNumberFormatInfo( mrContext, nCurFmtType, nCurFmtIndex,
+ rPos );
+ }
+ else
+ {
+ fValue = ConvertStringToValue(pFCell->GetString().getString());
+ }
+ }
+ else
+ {
+ fValue = 0.0;
+ SetError(nErr);
+ }
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ fValue = rCell.getDouble();
+ nCurFmtIndex = mrDoc.GetNumberFormat( mrContext, rPos );
+ nCurFmtType = mrContext.GetNumberFormatType( nCurFmtIndex );
+ if ( bCalcAsShown && fValue != 0.0 )
+ fValue = mrDoc.RoundValueAsShown( fValue, nCurFmtIndex, &mrContext );
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ // SUM(A1:A2) differs from A1+A2. No good. But people insist on
+ // it ... #i5658#
+ OUString aStr = rCell.getString(&mrDoc);
+ fValue = ConvertStringToValue( aStr );
+ }
+ break;
+ case CELLTYPE_NONE:
+ fValue = 0.0; // empty or broadcaster cell
+ break;
+ }
+
+ return fValue;
+}
+
+void ScInterpreter::GetCellString( svl::SharedString& rStr, ScRefCellValue& rCell )
+{
+ FormulaError nErr = FormulaError::NONE;
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ rStr = mrStrPool.intern(rCell.getString(&mrDoc));
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ nErr = pFCell->GetErrCode();
+ if (pFCell->IsValue())
+ {
+ rStr = GetStringFromDouble( pFCell->GetValue() );
+ }
+ else
+ rStr = pFCell->GetString();
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ rStr = GetStringFromDouble( rCell.getDouble() );
+ }
+ break;
+ default:
+ rStr = svl::SharedString::getEmptyString();
+ break;
+ }
+
+ SetError(nErr);
+}
+
+bool ScInterpreter::CreateDoubleArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr)
+{
+
+ // Old Add-Ins are hard limited to sal_uInt16 values.
+ static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16 && MAXCOLCOUNT_JUMBO <= SAL_MAX_UINT16,
+ "Add check for columns > SAL_MAX_UINT16!");
+ if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16)
+ return false;
+
+ sal_uInt16 nCount = 0;
+ sal_uInt16* p = reinterpret_cast<sal_uInt16*>(pCellArr);
+ *p++ = static_cast<sal_uInt16>(nCol1);
+ *p++ = static_cast<sal_uInt16>(nRow1);
+ *p++ = static_cast<sal_uInt16>(nTab1);
+ *p++ = static_cast<sal_uInt16>(nCol2);
+ *p++ = static_cast<sal_uInt16>(nRow2);
+ *p++ = static_cast<sal_uInt16>(nTab2);
+ sal_uInt16* pCount = p;
+ *p++ = 0;
+ sal_uInt16 nPos = 14;
+ SCTAB nTab = nTab1;
+ ScAddress aAdr;
+ while (nTab <= nTab2)
+ {
+ aAdr.SetTab( nTab );
+ SCROW nRow = nRow1;
+ while (nRow <= nRow2)
+ {
+ aAdr.SetRow( nRow );
+ SCCOL nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ aAdr.SetCol( nCol );
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (!aCell.isEmpty())
+ {
+ FormulaError nErr = FormulaError::NONE;
+ double nVal = 0.0;
+ bool bOk = true;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ nVal = GetValueCellValue(aAdr, aCell.getDouble());
+ break;
+ case CELLTYPE_FORMULA :
+ if (aCell.getFormula()->IsValue())
+ {
+ nErr = aCell.getFormula()->GetErrCode();
+ nVal = aCell.getFormula()->GetValue();
+ }
+ else
+ bOk = false;
+ break;
+ default :
+ bOk = false;
+ break;
+ }
+ if (bOk)
+ {
+ if ((nPos + (4 * sizeof(sal_uInt16)) + sizeof(double)) > MAXARRSIZE)
+ return false;
+ *p++ = static_cast<sal_uInt16>(nCol);
+ *p++ = static_cast<sal_uInt16>(nRow);
+ *p++ = static_cast<sal_uInt16>(nTab);
+ *p++ = static_cast<sal_uInt16>(nErr);
+ memcpy( p, &nVal, sizeof(double));
+ nPos += 8 + sizeof(double);
+ p = reinterpret_cast<sal_uInt16*>( pCellArr + nPos );
+ nCount++;
+ }
+ }
+ nCol++;
+ }
+ nRow++;
+ }
+ nTab++;
+ }
+ *pCount = nCount;
+ return true;
+}
+
+bool ScInterpreter::CreateStringArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ sal_uInt8* pCellArr)
+{
+
+ // Old Add-Ins are hard limited to sal_uInt16 values.
+ static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16 && MAXCOLCOUNT_JUMBO <= SAL_MAX_UINT16,
+ "Add check for columns > SAL_MAX_UINT16!");
+ if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16)
+ return false;
+
+ sal_uInt16 nCount = 0;
+ sal_uInt16* p = reinterpret_cast<sal_uInt16*>(pCellArr);
+ *p++ = static_cast<sal_uInt16>(nCol1);
+ *p++ = static_cast<sal_uInt16>(nRow1);
+ *p++ = static_cast<sal_uInt16>(nTab1);
+ *p++ = static_cast<sal_uInt16>(nCol2);
+ *p++ = static_cast<sal_uInt16>(nRow2);
+ *p++ = static_cast<sal_uInt16>(nTab2);
+ sal_uInt16* pCount = p;
+ *p++ = 0;
+ sal_uInt16 nPos = 14;
+ SCTAB nTab = nTab1;
+ while (nTab <= nTab2)
+ {
+ SCROW nRow = nRow1;
+ while (nRow <= nRow2)
+ {
+ SCCOL nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ ScRefCellValue aCell(mrDoc, ScAddress(nCol, nRow, nTab));
+ if (!aCell.isEmpty())
+ {
+ OUString aStr;
+ FormulaError nErr = FormulaError::NONE;
+ bool bOk = true;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ aStr = aCell.getString(&mrDoc);
+ break;
+ case CELLTYPE_FORMULA:
+ if (!aCell.getFormula()->IsValue())
+ {
+ nErr = aCell.getFormula()->GetErrCode();
+ aStr = aCell.getFormula()->GetString().getString();
+ }
+ else
+ bOk = false;
+ break;
+ default :
+ bOk = false;
+ break;
+ }
+ if (bOk)
+ {
+ OString aTmp(OUStringToOString(aStr,
+ osl_getThreadTextEncoding()));
+ // Old Add-Ins are limited to sal_uInt16 string
+ // lengths, and room for pad byte check.
+ if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 )
+ return false;
+ // Append a 0-pad-byte if string length is odd
+ // MUST be sal_uInt16
+ sal_uInt16 nStrLen = static_cast<sal_uInt16>(aTmp.getLength());
+ sal_uInt16 nLen = ( nStrLen + 2 ) & ~1;
+
+ if ((static_cast<sal_uLong>(nPos) + (5 * sizeof(sal_uInt16)) + nLen) > MAXARRSIZE)
+ return false;
+ *p++ = static_cast<sal_uInt16>(nCol);
+ *p++ = static_cast<sal_uInt16>(nRow);
+ *p++ = static_cast<sal_uInt16>(nTab);
+ *p++ = static_cast<sal_uInt16>(nErr);
+ *p++ = nLen;
+ memcpy( p, aTmp.getStr(), nStrLen + 1);
+ nPos += 10 + nStrLen + 1;
+ sal_uInt8* q = pCellArr + nPos;
+ if( (nStrLen & 1) == 0 )
+ {
+ *q++ = 0;
+ nPos++;
+ }
+ p = reinterpret_cast<sal_uInt16*>( pCellArr + nPos );
+ nCount++;
+ }
+ }
+ nCol++;
+ }
+ nRow++;
+ }
+ nTab++;
+ }
+ *pCount = nCount;
+ return true;
+}
+
+bool ScInterpreter::CreateCellArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ sal_uInt8* pCellArr)
+{
+
+ // Old Add-Ins are hard limited to sal_uInt16 values.
+ static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16 && MAXCOLCOUNT_JUMBO <= SAL_MAX_UINT16,
+ "Add check for columns > SAL_MAX_UINT16!");
+ if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16)
+ return false;
+
+ sal_uInt16 nCount = 0;
+ sal_uInt16* p = reinterpret_cast<sal_uInt16*>(pCellArr);
+ *p++ = static_cast<sal_uInt16>(nCol1);
+ *p++ = static_cast<sal_uInt16>(nRow1);
+ *p++ = static_cast<sal_uInt16>(nTab1);
+ *p++ = static_cast<sal_uInt16>(nCol2);
+ *p++ = static_cast<sal_uInt16>(nRow2);
+ *p++ = static_cast<sal_uInt16>(nTab2);
+ sal_uInt16* pCount = p;
+ *p++ = 0;
+ sal_uInt16 nPos = 14;
+ SCTAB nTab = nTab1;
+ ScAddress aAdr;
+ while (nTab <= nTab2)
+ {
+ aAdr.SetTab( nTab );
+ SCROW nRow = nRow1;
+ while (nRow <= nRow2)
+ {
+ aAdr.SetRow( nRow );
+ SCCOL nCol = nCol1;
+ while (nCol <= nCol2)
+ {
+ aAdr.SetCol( nCol );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (!aCell.isEmpty())
+ {
+ FormulaError nErr = FormulaError::NONE;
+ sal_uInt16 nType = 0; // 0 = number; 1 = string
+ double nVal = 0.0;
+ OUString aStr;
+ bool bOk = true;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ aStr = aCell.getString(&mrDoc);
+ nType = 1;
+ break;
+ case CELLTYPE_VALUE :
+ nVal = GetValueCellValue(aAdr, aCell.getDouble());
+ break;
+ case CELLTYPE_FORMULA :
+ nErr = aCell.getFormula()->GetErrCode();
+ if (aCell.getFormula()->IsValue())
+ nVal = aCell.getFormula()->GetValue();
+ else
+ aStr = aCell.getFormula()->GetString().getString();
+ break;
+ default :
+ bOk = false;
+ break;
+ }
+ if (bOk)
+ {
+ if ((nPos + (5 * sizeof(sal_uInt16))) > MAXARRSIZE)
+ return false;
+ *p++ = static_cast<sal_uInt16>(nCol);
+ *p++ = static_cast<sal_uInt16>(nRow);
+ *p++ = static_cast<sal_uInt16>(nTab);
+ *p++ = static_cast<sal_uInt16>(nErr);
+ *p++ = nType;
+ nPos += 10;
+ if (nType == 0)
+ {
+ if ((nPos + sizeof(double)) > MAXARRSIZE)
+ return false;
+ memcpy( p, &nVal, sizeof(double));
+ nPos += sizeof(double);
+ }
+ else
+ {
+ OString aTmp(OUStringToOString(aStr,
+ osl_getThreadTextEncoding()));
+ // Old Add-Ins are limited to sal_uInt16 string
+ // lengths, and room for pad byte check.
+ if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 )
+ return false;
+ // Append a 0-pad-byte if string length is odd
+ // MUST be sal_uInt16
+ sal_uInt16 nStrLen = static_cast<sal_uInt16>(aTmp.getLength());
+ sal_uInt16 nLen = ( nStrLen + 2 ) & ~1;
+ if ( (static_cast<sal_uLong>(nPos) + 2 + nLen) > MAXARRSIZE)
+ return false;
+ *p++ = nLen;
+ memcpy( p, aTmp.getStr(), nStrLen + 1);
+ nPos += 2 + nStrLen + 1;
+ sal_uInt8* q = pCellArr + nPos;
+ if( (nStrLen & 1) == 0 )
+ {
+ *q++ = 0;
+ nPos++;
+ }
+ }
+ nCount++;
+ p = reinterpret_cast<sal_uInt16*>( pCellArr + nPos );
+ }
+ }
+ nCol++;
+ }
+ nRow++;
+ }
+ nTab++;
+ }
+ *pCount = nCount;
+ return true;
+}
+
+// Stack operations
+
+// Also releases a TempToken if appropriate.
+
+void ScInterpreter::PushWithoutError( const FormulaToken& r )
+{
+ if ( sp >= MAXSTACK )
+ SetError( FormulaError::StackOverflow );
+ else
+ {
+ r.IncRef();
+ if( sp >= maxsp )
+ maxsp = sp + 1;
+ else
+ pStack[ sp ]->DecRef();
+ pStack[ sp ] = &r;
+ ++sp;
+ }
+}
+
+void ScInterpreter::Push( const FormulaToken& r )
+{
+ if ( sp >= MAXSTACK )
+ SetError( FormulaError::StackOverflow );
+ else
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (r.GetType() == svError)
+ PushWithoutError( r);
+ else
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+ }
+ else
+ PushWithoutError( r);
+ }
+}
+
+void ScInterpreter::PushTempToken( FormulaToken* p )
+{
+ if ( sp >= MAXSTACK )
+ {
+ SetError( FormulaError::StackOverflow );
+ // p may be a dangling pointer hereafter!
+ p->DeleteIfZeroRef();
+ }
+ else
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (p->GetType() == svError)
+ {
+ p->SetError( nGlobalError);
+ PushTempTokenWithoutError( p);
+ }
+ else
+ {
+ // p may be a dangling pointer hereafter!
+ p->DeleteIfZeroRef();
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+ }
+ }
+ else
+ PushTempTokenWithoutError( p);
+ }
+}
+
+void ScInterpreter::PushTempTokenWithoutError( const FormulaToken* p )
+{
+ p->IncRef();
+ if ( sp >= MAXSTACK )
+ {
+ SetError( FormulaError::StackOverflow );
+ // p may be a dangling pointer hereafter!
+ p->DecRef();
+ }
+ else
+ {
+ if( sp >= maxsp )
+ maxsp = sp + 1;
+ else
+ pStack[ sp ]->DecRef();
+ pStack[ sp ] = p;
+ ++sp;
+ }
+}
+
+void ScInterpreter::PushTokenRef( const formula::FormulaConstTokenRef& x )
+{
+ if ( sp >= MAXSTACK )
+ {
+ SetError( FormulaError::StackOverflow );
+ }
+ else
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (x->GetType() == svError && x->GetError() == nGlobalError)
+ PushTempTokenWithoutError( x.get());
+ else
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+ }
+ else
+ PushTempTokenWithoutError( x.get());
+ }
+}
+
+void ScInterpreter::PushCellResultToken( bool bDisplayEmptyAsString,
+ const ScAddress & rAddress, SvNumFormatType * pRetTypeExpr, sal_uInt32 * pRetIndexExpr, bool bFinalResult )
+{
+ ScRefCellValue aCell(mrDoc, rAddress);
+ if (aCell.hasEmptyValue())
+ {
+ bool bInherited = (aCell.getType() == CELLTYPE_FORMULA);
+ if (pRetTypeExpr && pRetIndexExpr)
+ mrDoc.GetNumberFormatInfo(mrContext, *pRetTypeExpr, *pRetIndexExpr, rAddress);
+ PushTempToken( new ScEmptyCellToken( bInherited, bDisplayEmptyAsString));
+ return;
+ }
+
+ FormulaError nErr = FormulaError::NONE;
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ nErr = aCell.getFormula()->GetErrCode();
+
+ if (nErr != FormulaError::NONE)
+ {
+ PushError( nErr);
+ if (pRetTypeExpr)
+ *pRetTypeExpr = SvNumFormatType::UNDEFINED;
+ if (pRetIndexExpr)
+ *pRetIndexExpr = 0;
+ }
+ else if (aCell.hasString())
+ {
+ svl::SharedString aRes;
+ GetCellString( aRes, aCell);
+ PushString( aRes);
+ if (pRetTypeExpr)
+ *pRetTypeExpr = SvNumFormatType::TEXT;
+ if (pRetIndexExpr)
+ *pRetIndexExpr = 0;
+ }
+ else
+ {
+ double fVal = GetCellValue(rAddress, aCell);
+ if (bFinalResult)
+ {
+ TreatDoubleError( fVal);
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( CreateFormulaDoubleToken( fVal));
+ }
+ else
+ {
+ PushDouble( fVal);
+ }
+ if (pRetTypeExpr)
+ *pRetTypeExpr = nCurFmtType;
+ if (pRetIndexExpr)
+ *pRetIndexExpr = nCurFmtIndex;
+ }
+}
+
+// Simply throw away TOS.
+
+void ScInterpreter::Pop()
+{
+ if( sp )
+ sp--;
+ else
+ SetError(FormulaError::UnknownStackVariable);
+}
+
+// Simply throw away TOS and set error code, used with ocIsError et al.
+
+void ScInterpreter::PopError()
+{
+ if( sp )
+ {
+ sp--;
+ if (pStack[sp]->GetType() == svError)
+ nGlobalError = pStack[sp]->GetError();
+ }
+ else
+ SetError(FormulaError::UnknownStackVariable);
+}
+
+FormulaConstTokenRef ScInterpreter::PopToken()
+{
+ if (sp)
+ {
+ sp--;
+ const FormulaToken* p = pStack[ sp ];
+ if (p->GetType() == svError)
+ nGlobalError = p->GetError();
+ return p;
+ }
+ else
+ SetError(FormulaError::UnknownStackVariable);
+ return nullptr;
+}
+
+double ScInterpreter::PopDouble()
+{
+ nCurFmtType = SvNumFormatType::NUMBER;
+ nCurFmtIndex = 0;
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svDouble:
+ {
+ SvNumFormatType nType = static_cast<SvNumFormatType>(p->GetDoubleType());
+ if (nType != SvNumFormatType::ALL && nType != SvNumFormatType::UNDEFINED)
+ nCurFmtType = nType;
+ return p->GetDouble();
+ }
+ case svEmptyCell:
+ case svMissing:
+ return 0.0;
+ default:
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+ return 0.0;
+}
+
+const svl::SharedString & ScInterpreter::PopString()
+{
+ nCurFmtType = SvNumFormatType::TEXT;
+ nCurFmtIndex = 0;
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svString:
+ return p->GetString();
+ case svEmptyCell:
+ case svMissing:
+ return svl::SharedString::getEmptyString();
+ default:
+ SetError( FormulaError::IllegalArgument);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+
+ return svl::SharedString::getEmptyString();
+}
+
+void ScInterpreter::ValidateRef( const ScSingleRefData & rRef )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SingleRefToVars( rRef, nCol, nRow, nTab);
+}
+
+void ScInterpreter::ValidateRef( const ScComplexRefData & rRef )
+{
+ ValidateRef( rRef.Ref1);
+ ValidateRef( rRef.Ref2);
+}
+
+void ScInterpreter::ValidateRef( const ScRefList & rRefList )
+{
+ for (const auto& rRef : rRefList)
+ {
+ ValidateRef( rRef);
+ }
+}
+
+void ScInterpreter::SingleRefToVars( const ScSingleRefData & rRef,
+ SCCOL & rCol, SCROW & rRow, SCTAB & rTab )
+{
+ if ( rRef.IsColRel() )
+ rCol = aPos.Col() + rRef.Col();
+ else
+ rCol = rRef.Col();
+
+ if ( rRef.IsRowRel() )
+ rRow = aPos.Row() + rRef.Row();
+ else
+ rRow = rRef.Row();
+
+ if ( rRef.IsTabRel() )
+ rTab = aPos.Tab() + rRef.Tab();
+ else
+ rTab = rRef.Tab();
+
+ if( !mrDoc.ValidCol( rCol) || rRef.IsColDeleted() )
+ {
+ SetError( FormulaError::NoRef );
+ rCol = 0;
+ }
+ if( !mrDoc.ValidRow( rRow) || rRef.IsRowDeleted() )
+ {
+ SetError( FormulaError::NoRef );
+ rRow = 0;
+ }
+ if( !ValidTab( rTab, mrDoc.GetTableCount() - 1) || rRef.IsTabDeleted() )
+ {
+ SetError( FormulaError::NoRef );
+ rTab = 0;
+ }
+}
+
+void ScInterpreter::PopSingleRef(SCCOL& rCol, SCROW &rRow, SCTAB& rTab)
+{
+ ScAddress aAddr(rCol, rRow, rTab);
+ PopSingleRef(aAddr);
+ rCol = aAddr.Col();
+ rRow = aAddr.Row();
+ rTab = aAddr.Tab();
+}
+
+void ScInterpreter::PopSingleRef( ScAddress& rAdr )
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svSingleRef:
+ {
+ const ScSingleRefData* pRefData = p->GetSingleRef();
+ if (pRefData->IsDeleted())
+ {
+ SetError( FormulaError::NoRef);
+ break;
+ }
+
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SingleRefToVars( *pRefData, nCol, nRow, nTab);
+ rAdr.Set( nCol, nRow, nTab );
+ if (!mrDoc.m_TableOpList.empty())
+ ReplaceCell( rAdr );
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::DoubleRefToVars( const formula::FormulaToken* p,
+ SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
+ SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2 )
+{
+ const ScComplexRefData& rCRef = *p->GetDoubleRef();
+ SingleRefToVars( rCRef.Ref1, rCol1, rRow1, rTab1);
+ SingleRefToVars( rCRef.Ref2, rCol2, rRow2, rTab2);
+ PutInOrder(rCol1, rCol2);
+ PutInOrder(rRow1, rRow2);
+ PutInOrder(rTab1, rTab2);
+ if (!mrDoc.m_TableOpList.empty())
+ {
+ ScRange aRange( rCol1, rRow1, rTab1, rCol2, rRow2, rTab2 );
+ if ( IsTableOpInRange( aRange ) )
+ SetError( FormulaError::IllegalParameter );
+ }
+}
+
+ScDBRangeBase* ScInterpreter::PopDBDoubleRef()
+{
+ StackVar eType = GetStackType();
+ switch (eType)
+ {
+ case svUnknown:
+ SetError(FormulaError::UnknownStackVariable);
+ break;
+ case svError:
+ PopError();
+ break;
+ case svDoubleRef:
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+ return new ScDBInternalRange(&mrDoc,
+ ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
+ }
+ case svMatrix:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ if (eType == svMatrix)
+ pMat = PopMatrix();
+ else
+ PopExternalDoubleRef(pMat);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+ return new ScDBExternalRange(&mrDoc, std::move(pMat));
+ }
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+
+ return nullptr;
+}
+
+void ScInterpreter::PopDoubleRef(SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
+ SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2)
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svDoubleRef:
+ DoubleRefToVars( p, rCol1, rRow1, rTab1, rCol2, rRow2, rTab2);
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::DoubleRefToRange( const ScComplexRefData & rCRef,
+ ScRange & rRange, bool bDontCheckForTableOp )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SingleRefToVars( rCRef.Ref1, nCol, nRow, nTab);
+ rRange.aStart.Set( nCol, nRow, nTab );
+ SingleRefToVars( rCRef.Ref2, nCol, nRow, nTab);
+ rRange.aEnd.Set( nCol, nRow, nTab );
+ rRange.PutInOrder();
+ if (!mrDoc.m_TableOpList.empty() && !bDontCheckForTableOp)
+ {
+ if ( IsTableOpInRange( rRange ) )
+ SetError( FormulaError::IllegalParameter );
+ }
+}
+
+void ScInterpreter::PopDoubleRef( ScRange & rRange, short & rParam, size_t & rRefInList )
+{
+ if (sp)
+ {
+ const formula::FormulaToken* pToken = pStack[ sp-1 ];
+ switch (pToken->GetType())
+ {
+ case svError:
+ nGlobalError = pToken->GetError();
+ break;
+ case svDoubleRef:
+ {
+ --sp;
+ const ScComplexRefData* pRefData = pToken->GetDoubleRef();
+ if (pRefData->IsDeleted())
+ {
+ SetError( FormulaError::NoRef);
+ break;
+ }
+ DoubleRefToRange( *pRefData, rRange);
+ break;
+ }
+ case svRefList:
+ {
+ const ScRefList* pList = pToken->GetRefList();
+ if (rRefInList < pList->size())
+ {
+ DoubleRefToRange( (*pList)[rRefInList], rRange);
+ if (++rRefInList < pList->size())
+ ++rParam;
+ else
+ {
+ --sp;
+ rRefInList = 0;
+ }
+ }
+ else
+ {
+ --sp;
+ rRefInList = 0;
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+void ScInterpreter::PopDoubleRef( ScRange& rRange, bool bDontCheckForTableOp )
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svDoubleRef:
+ DoubleRefToRange( *p->GetDoubleRef(), rRange, bDontCheckForTableOp);
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+const ScComplexRefData* ScInterpreter::GetStackDoubleRef(size_t rRefInList)
+{
+ if( sp )
+ {
+ const FormulaToken* p = pStack[ sp - 1 ];
+ switch (p->GetType())
+ {
+ case svDoubleRef:
+ return p->GetDoubleRef();
+ case svRefList:
+ {
+ const ScRefList* pList = p->GetRefList();
+ if (rRefInList < pList->size())
+ return &(*pList)[rRefInList];
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return nullptr;
+}
+
+void ScInterpreter::PopExternalSingleRef(sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef)
+{
+ if (!sp)
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ return;
+ }
+
+ --sp;
+ const FormulaToken* p = pStack[sp];
+ StackVar eType = p->GetType();
+
+ if (eType == svError)
+ {
+ nGlobalError = p->GetError();
+ return;
+ }
+
+ if (eType != svExternalSingleRef)
+ {
+ SetError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ rFileId = p->GetIndex();
+ rTabName = p->GetString().getString();
+ rRef = *p->GetSingleRef();
+}
+
+void ScInterpreter::PopExternalSingleRef(ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt)
+{
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScSingleRefData aData;
+ PopExternalSingleRef(nFileId, aTabName, aData, rToken, pFmt);
+}
+
+void ScInterpreter::PopExternalSingleRef(
+ sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef,
+ ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt)
+{
+ PopExternalSingleRef(rFileId, rTabName, rRef);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
+ const OUString* pFile = pRefMgr->getExternalFileName(rFileId);
+ if (!pFile)
+ {
+ SetError(FormulaError::NoName);
+ return;
+ }
+
+ if (rRef.IsTabRel())
+ {
+ OSL_FAIL("ScCompiler::GetToken: external single reference must have an absolute table reference!");
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ ScAddress aAddr = rRef.toAbs(mrDoc, aPos);
+ ScExternalRefCache::CellFormat aFmt;
+ ScExternalRefCache::TokenRef xNew = pRefMgr->getSingleRefToken(
+ rFileId, rTabName, aAddr, &aPos, nullptr, &aFmt);
+
+ if (!xNew)
+ {
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ if (xNew->GetType() == svError)
+ SetError( xNew->GetError());
+
+ rToken = xNew;
+ if (pFmt)
+ *pFmt = aFmt;
+}
+
+void ScInterpreter::PopExternalDoubleRef(sal_uInt16& rFileId, OUString& rTabName, ScComplexRefData& rRef)
+{
+ if (!sp)
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ return;
+ }
+
+ --sp;
+ const FormulaToken* p = pStack[sp];
+ StackVar eType = p->GetType();
+
+ if (eType == svError)
+ {
+ nGlobalError = p->GetError();
+ return;
+ }
+
+ if (eType != svExternalDoubleRef)
+ {
+ SetError( FormulaError::IllegalParameter);
+ return;
+ }
+
+ rFileId = p->GetIndex();
+ rTabName = p->GetString().getString();
+ rRef = *p->GetDoubleRef();
+}
+
+void ScInterpreter::PopExternalDoubleRef(ScExternalRefCache::TokenArrayRef& rArray)
+{
+ sal_uInt16 nFileId;
+ OUString aTabName;
+ ScComplexRefData aData;
+ PopExternalDoubleRef(nFileId, aTabName, aData);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ GetExternalDoubleRef(nFileId, aTabName, aData, rArray);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+}
+
+void ScInterpreter::PopExternalDoubleRef(ScMatrixRef& rMat)
+{
+ ScExternalRefCache::TokenArrayRef pArray;
+ PopExternalDoubleRef(pArray);
+ if (nGlobalError != FormulaError::NONE)
+ return;
+
+ // For now, we only support single range data for external
+ // references, which means the array should only contain a
+ // single matrix token.
+ formula::FormulaToken* p = pArray->FirstToken();
+ if (!p || p->GetType() != svMatrix)
+ SetError( FormulaError::IllegalParameter);
+ else
+ {
+ rMat = p->GetMatrix();
+ if (!rMat)
+ SetError( FormulaError::UnknownVariable);
+ }
+}
+
+void ScInterpreter::GetExternalDoubleRef(
+ sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rData, ScExternalRefCache::TokenArrayRef& rArray)
+{
+ ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
+ const OUString* pFile = pRefMgr->getExternalFileName(nFileId);
+ if (!pFile)
+ {
+ SetError(FormulaError::NoName);
+ return;
+ }
+ if (rData.Ref1.IsTabRel() || rData.Ref2.IsTabRel())
+ {
+ OSL_FAIL("ScCompiler::GetToken: external double reference must have an absolute table reference!");
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ ScComplexRefData aData(rData);
+ ScRange aRange = aData.toAbs(mrDoc, aPos);
+ if (!mrDoc.ValidColRow(aRange.aStart.Col(), aRange.aStart.Row()) || !mrDoc.ValidColRow(aRange.aEnd.Col(), aRange.aEnd.Row()))
+ {
+ SetError(FormulaError::NoRef);
+ return;
+ }
+
+ ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(
+ nFileId, rTabName, aRange, &aPos);
+
+ if (!pArray)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return;
+ }
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pArray);
+ formula::FormulaToken* pToken = aIter.First();
+ assert(pToken);
+ if (pToken->GetType() == svError)
+ {
+ SetError( pToken->GetError());
+ return;
+ }
+ if (pToken->GetType() != svMatrix)
+ {
+ SetError(FormulaError::IllegalArgument);
+ return;
+ }
+
+ if (aIter.Next())
+ {
+ // Can't handle more than one matrix per parameter.
+ SetError( FormulaError::IllegalArgument);
+ return;
+ }
+
+ rArray = pArray;
+}
+
+bool ScInterpreter::PopDoubleRefOrSingleRef( ScAddress& rAdr )
+{
+ switch ( GetStackType() )
+ {
+ case svDoubleRef :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, true );
+ return DoubleRefToPosSingleRef( aRange, rAdr );
+ }
+ case svSingleRef :
+ {
+ PopSingleRef( rAdr );
+ return true;
+ }
+ default:
+ PopError();
+ SetError( FormulaError::NoRef );
+ }
+ return false;
+}
+
+void ScInterpreter::PopDoubleRefPushMatrix()
+{
+ if ( GetStackType() == svDoubleRef )
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ PushMatrix( pMat );
+ else
+ PushIllegalParameter();
+ }
+ else
+ SetError( FormulaError::NoRef );
+}
+
+void ScInterpreter::PopRefListPushMatrixOrRef()
+{
+ if ( GetStackType() == svRefList )
+ {
+ FormulaConstTokenRef xTok = pStack[sp-1];
+ const std::vector<ScComplexRefData>* pv = xTok->GetRefList();
+ if (pv)
+ {
+ const size_t nEntries = pv->size();
+ if (nEntries == 1)
+ {
+ --sp;
+ PushTempTokenWithoutError( new ScDoubleRefToken( mrDoc.GetSheetLimits(), (*pv)[0] ));
+ }
+ else if (bMatrixFormula)
+ {
+ // Only single cells can be stuffed into a column vector.
+ // XXX NOTE: Excel doesn't do this but returns #VALUE! instead.
+ // Though there's no compelling reason not to...
+ for (const auto & rRef : *pv)
+ {
+ if (rRef.Ref1 != rRef.Ref2)
+ return;
+ }
+ ScMatrixRef xMat = GetNewMat( 1, nEntries, true); // init empty
+ if (!xMat)
+ return;
+ for (size_t i=0; i < nEntries; ++i)
+ {
+ SCCOL nCol; SCROW nRow; SCTAB nTab;
+ SingleRefToVars( (*pv)[i].Ref1, nCol, nRow, nTab);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ ScAddress aAdr( nCol, nRow, nTab);
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasError())
+ xMat->PutError( aCell.getFormula()->GetErrCode(), 0, i);
+ else if (aCell.hasEmptyValue())
+ xMat->PutEmpty( 0, i);
+ else if (aCell.hasString())
+ xMat->PutString( mrStrPool.intern( aCell.getString(&mrDoc)), 0, i);
+ else
+ xMat->PutDouble( aCell.getValue(), 0, i);
+ }
+ else
+ {
+ xMat->PutError( nGlobalError, 0, i);
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ --sp;
+ PushMatrix( xMat);
+ }
+ }
+ // else: keep token on stack, something will handle the error
+ }
+ else
+ SetError( FormulaError::NoRef );
+}
+
+void ScInterpreter::ConvertMatrixJumpConditionToMatrix()
+{
+ StackVar eStackType = GetStackType();
+ if (eStackType == svUnknown)
+ return; // can't do anything, some caller will catch that
+ if (eStackType == svMatrix)
+ return; // already matrix, nothing to do
+
+ if (eStackType != svDoubleRef && GetStackType(2) != svJumpMatrix)
+ return; // always convert svDoubleRef, others only in JumpMatrix context
+
+ GetTokenMatrixMap(); // make sure it exists, create if not.
+ ScMatrixRef pMat = GetMatrix();
+ if ( pMat )
+ PushMatrix( pMat );
+ else
+ PushIllegalParameter();
+}
+
+bool ScInterpreter::ConvertMatrixParameters()
+{
+ sal_uInt16 nParams = pCur->GetParamCount();
+ SAL_WARN_IF( nParams > sp, "sc.core", "ConvertMatrixParameters: stack/param count mismatch: eOp: "
+ << static_cast<int>(pCur->GetOpCode()) << " sp: " << sp << " nParams: " << nParams);
+ assert(nParams <= sp);
+ SCSIZE nJumpCols = 0, nJumpRows = 0;
+ for ( sal_uInt16 i=1; i <= nParams && i <= sp; ++i )
+ {
+ const FormulaToken* p = pStack[ sp - i ];
+ if ( p->GetOpCode() != ocPush && p->GetOpCode() != ocMissing)
+ {
+ assert(!"ConvertMatrixParameters: not a push");
+ }
+ else
+ {
+ switch ( p->GetType() )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ case svExternalSingleRef:
+ case svMissing:
+ case svError:
+ case svEmptyCell:
+ // nothing to do
+ break;
+ case svMatrix:
+ {
+ if ( ScParameterClassification::GetParameterType( pCur, nParams - i)
+ == formula::ParamClass::Value )
+ { // only if single value expected
+ ScConstMatrixRef pMat = p->GetMatrix();
+ if ( !pMat )
+ SetError( FormulaError::UnknownVariable);
+ else
+ {
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows);
+ if ( nJumpCols < nCols )
+ nJumpCols = nCols;
+ if ( nJumpRows < nRows )
+ nJumpRows = nRows;
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
+ if ( eType != formula::ParamClass::Reference &&
+ eType != formula::ParamClass::ReferenceOrRefArray &&
+ eType != formula::ParamClass::ReferenceOrForceArray &&
+ // For scalar Value: convert to Array/JumpMatrix
+ // only if in array formula context, else (function
+ // has ForceArray or ReferenceOrForceArray
+ // parameter *somewhere else*) pick a normal
+ // position dependent implicit intersection later.
+ (eType != formula::ParamClass::Value || IsInArrayContext()))
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ DoubleRefToVars( p, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ // Make sure the map exists, created if not.
+ GetTokenMatrixMap();
+ ScMatrixRef pMat = CreateMatrixFromDoubleRef( p,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (pMat)
+ {
+ if ( eType == formula::ParamClass::Value )
+ { // only if single value expected
+ if ( nJumpCols < o3tl::make_unsigned(nCol2 - nCol1 + 1) )
+ nJumpCols = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
+ if ( nJumpRows < o3tl::make_unsigned(nRow2 - nRow1 + 1) )
+ nJumpRows = static_cast<SCSIZE>(nRow2 - nRow1 + 1);
+ }
+ formula::FormulaToken* pNew = new ScMatrixToken( std::move(pMat) );
+ pNew->IncRef();
+ pStack[ sp - i ] = pNew;
+ p->DecRef(); // p may be dead now!
+ }
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
+ if (eType == formula::ParamClass::Value || eType == formula::ParamClass::Array)
+ {
+ sal_uInt16 nFileId = p->GetIndex();
+ OUString aTabName = p->GetString().getString();
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScExternalRefCache::TokenArrayRef pArray;
+ GetExternalDoubleRef(nFileId, aTabName, rRef, pArray);
+ if (nGlobalError != FormulaError::NONE || !pArray)
+ break;
+ formula::FormulaToken* pTemp = pArray->FirstToken();
+ if (!pTemp)
+ break;
+
+ ScMatrixRef pMat = pTemp->GetMatrix();
+ if (pMat)
+ {
+ if (eType == formula::ParamClass::Value)
+ { // only if single value expected
+ SCSIZE nC, nR;
+ pMat->GetDimensions( nC, nR);
+ if (nJumpCols < nC)
+ nJumpCols = nC;
+ if (nJumpRows < nR)
+ nJumpRows = nR;
+ }
+ formula::FormulaToken* pNew = new ScMatrixToken( std::move(pMat) );
+ pNew->IncRef();
+ pStack[ sp - i ] = pNew;
+ p->DecRef(); // p may be dead now!
+ }
+ }
+ }
+ break;
+ case svRefList:
+ {
+ formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
+ if ( eType != formula::ParamClass::Reference &&
+ eType != formula::ParamClass::ReferenceOrRefArray &&
+ eType != formula::ParamClass::ReferenceOrForceArray &&
+ eType != formula::ParamClass::ForceArray)
+ {
+ // can't convert to matrix
+ SetError( FormulaError::NoRef);
+ }
+ // else: the consuming function has to decide if and how to
+ // handle a reference list argument in array context.
+ }
+ break;
+ default:
+ assert(!"ConvertMatrixParameters: unknown parameter type");
+ }
+ }
+ }
+ if( nJumpCols && nJumpRows )
+ {
+ short nPC = aCode.GetPC();
+ short nStart = nPC - 1; // restart on current code (-1)
+ short nNext = nPC; // next instruction after subroutine
+ short nStop = nPC + 1; // stop subroutine before reaching that
+ FormulaConstTokenRef xNew;
+ ScTokenMatrixMap::const_iterator aMapIter;
+ if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end())
+ xNew = (*aMapIter).second;
+ else
+ {
+ std::shared_ptr<ScJumpMatrix> pJumpMat;
+ try
+ {
+ pJumpMat = std::make_shared<ScJumpMatrix>( pCur->GetOpCode(), nJumpCols, nJumpRows);
+ }
+ catch (const std::bad_alloc&)
+ {
+ SAL_WARN("sc.core", "std::bad_alloc in ScJumpMatrix ctor with " << nJumpCols << " columns and " << nJumpRows << " rows");
+ return false;
+ }
+ pJumpMat->SetAllJumps( 1.0, nStart, nNext, nStop);
+ // pop parameters and store in ScJumpMatrix, push in JumpMatrix()
+ ScTokenVec aParams(nParams);
+ for ( sal_uInt16 i=1; i <= nParams && sp > 0; ++i )
+ {
+ const FormulaToken* p = pStack[ --sp ];
+ p->IncRef();
+ // store in reverse order such that a push may simply iterate
+ aParams[ nParams - i ] = p;
+ }
+ pJumpMat->SetJumpParameters( std::move(aParams) );
+ xNew = new ScJumpMatrixToken( std::move(pJumpMat) );
+ GetTokenMatrixMap().emplace(pCur, xNew);
+ }
+ PushTempTokenWithoutError( xNew.get());
+ // set continuation point of path for main code line
+ aCode.Jump( nNext, nNext);
+ return true;
+ }
+ return false;
+}
+
+ScMatrixRef ScInterpreter::PopMatrix()
+{
+ if( sp )
+ {
+ --sp;
+ const FormulaToken* p = pStack[ sp ];
+ switch (p->GetType())
+ {
+ case svError:
+ nGlobalError = p->GetError();
+ break;
+ case svMatrix:
+ {
+ // ScMatrix itself maintains an im/mutable flag that should
+ // be obeyed where necessary... so we can return ScMatrixRef
+ // here instead of ScConstMatrixRef.
+ ScMatrix* pMat = const_cast<FormulaToken*>(p)->GetMatrix();
+ if ( pMat )
+ pMat->SetErrorInterpreter( this);
+ else
+ SetError( FormulaError::UnknownVariable);
+ return pMat;
+ }
+ default:
+ SetError( FormulaError::IllegalParameter);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+ return nullptr;
+}
+
+sc::RangeMatrix ScInterpreter::PopRangeMatrix()
+{
+ sc::RangeMatrix aRet;
+ if (sp)
+ {
+ switch (pStack[sp-1]->GetType())
+ {
+ case svMatrix:
+ {
+ --sp;
+ const FormulaToken* p = pStack[sp];
+ aRet.mpMat = const_cast<FormulaToken*>(p)->GetMatrix();
+ if (aRet.mpMat)
+ {
+ aRet.mpMat->SetErrorInterpreter(this);
+ if (p->GetByte() == MATRIX_TOKEN_HAS_RANGE)
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel())
+ {
+ aRet.mnCol1 = rRef.Ref1.Col();
+ aRet.mnRow1 = rRef.Ref1.Row();
+ aRet.mnTab1 = rRef.Ref1.Tab();
+ aRet.mnCol2 = rRef.Ref2.Col();
+ aRet.mnRow2 = rRef.Ref2.Row();
+ aRet.mnTab2 = rRef.Ref2.Tab();
+ }
+ }
+ }
+ else
+ SetError( FormulaError::UnknownVariable);
+ }
+ break;
+ default:
+ aRet.mpMat = PopMatrix();
+ }
+ }
+ return aRet;
+}
+
+void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, SvNumFormatType& rRetTypeExpr, sal_uInt32& rRetIndexExpr)
+{
+ if (xMat)
+ {
+ SCSIZE nCols, nRows;
+ xMat->GetDimensions(nCols, nRows);
+ ScMatrixValue nMatVal = xMat->Get(0, 0);
+ ScMatValType nMatValType = nMatVal.nType;
+ if (ScMatrix::IsNonValueType( nMatValType))
+ {
+ if ( xMat->IsEmptyPath( 0, 0))
+ { // result of empty FALSE jump path
+ FormulaTokenRef xRes = CreateFormulaDoubleToken( 0.0);
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ rRetTypeExpr = SvNumFormatType::LOGICAL;
+ }
+ else if ( xMat->IsEmptyResult( 0, 0))
+ { // empty formula result
+ FormulaTokenRef xRes = new ScEmptyCellToken( true, true); // inherited, display empty
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ }
+ else if ( xMat->IsEmpty( 0, 0))
+ { // empty or empty cell
+ FormulaTokenRef xRes = new ScEmptyCellToken( false, true); // not inherited, display empty
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ }
+ else
+ {
+ FormulaTokenRef xRes = new FormulaStringToken( nMatVal.GetString() );
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ rRetTypeExpr = SvNumFormatType::TEXT;
+ }
+ }
+ else
+ {
+ FormulaError nErr = GetDoubleErrorValue( nMatVal.fVal);
+ FormulaTokenRef xRes;
+ if (nErr != FormulaError::NONE)
+ xRes = new FormulaErrorToken( nErr);
+ else
+ xRes = CreateFormulaDoubleToken( nMatVal.fVal);
+ PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
+ if ( rRetTypeExpr != SvNumFormatType::LOGICAL )
+ rRetTypeExpr = SvNumFormatType::NUMBER;
+ }
+ rRetIndexExpr = 0;
+ xMat->SetErrorInterpreter( nullptr);
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+}
+
+formula::FormulaToken* ScInterpreter::CreateFormulaDoubleToken( double fVal, SvNumFormatType nFmt )
+{
+ assert( mrContext.maTokens.size() == TOKEN_CACHE_SIZE );
+
+ // Find a spare token
+ for ( auto p : mrContext.maTokens )
+ {
+ if (p && p->GetRef() == 1)
+ {
+ p->GetDoubleAsReference() = fVal;
+ p->SetDoubleType( static_cast<sal_Int16>(nFmt) );
+ return p;
+ }
+ }
+
+ // Allocate a new token
+ auto p = new FormulaTypedDoubleToken( fVal, static_cast<sal_Int16>(nFmt) );
+ if ( mrContext.maTokens[mrContext.mnTokenCachePos] )
+ mrContext.maTokens[mrContext.mnTokenCachePos]->DecRef();
+ mrContext.maTokens[mrContext.mnTokenCachePos] = p;
+ p->IncRef();
+ mrContext.mnTokenCachePos = (mrContext.mnTokenCachePos + 1) % TOKEN_CACHE_SIZE;
+ return p;
+}
+
+formula::FormulaToken* ScInterpreter::CreateDoubleOrTypedToken( double fVal )
+{
+ // NumberFormat::NUMBER is the default untyped double.
+ if (nFuncFmtType != SvNumFormatType::ALL && nFuncFmtType != SvNumFormatType::NUMBER &&
+ nFuncFmtType != SvNumFormatType::UNDEFINED)
+ return CreateFormulaDoubleToken( fVal, nFuncFmtType);
+ else
+ return CreateFormulaDoubleToken( fVal);
+}
+
+void ScInterpreter::PushDouble(double nVal)
+{
+ TreatDoubleError( nVal );
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( CreateDoubleOrTypedToken( nVal));
+}
+
+void ScInterpreter::PushInt(int nVal)
+{
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( CreateDoubleOrTypedToken( nVal));
+}
+
+void ScInterpreter::PushStringBuffer( const sal_Unicode* pString )
+{
+ if ( pString )
+ {
+ svl::SharedString aSS = mrDoc.GetSharedStringPool().intern(OUString(pString));
+ PushString(aSS);
+ }
+ else
+ PushString(svl::SharedString::getEmptyString());
+}
+
+void ScInterpreter::PushString( const OUString& rStr )
+{
+ PushString(mrDoc.GetSharedStringPool().intern(rStr));
+}
+
+void ScInterpreter::PushString( const svl::SharedString& rString )
+{
+ if (!IfErrorPushError())
+ PushTempTokenWithoutError( new FormulaStringToken( rString ) );
+}
+
+void ScInterpreter::PushSingleRef(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (!IfErrorPushError())
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(ScAddress(nCol,nRow,nTab));
+ PushTempTokenWithoutError( new ScSingleRefToken( mrDoc.GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushDoubleRef(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2)
+{
+ if (!IfErrorPushError())
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2));
+ PushTempTokenWithoutError( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushExternalSingleRef(
+ sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (!IfErrorPushError())
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(ScAddress(nCol,nRow,nTab));
+ PushTempTokenWithoutError( new ScExternalSingleRefToken(nFileId,
+ mrDoc.GetSharedStringPool().intern( rTabName), aRef)) ;
+ }
+}
+
+void ScInterpreter::PushExternalDoubleRef(
+ sal_uInt16 nFileId, const OUString& rTabName,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2)
+{
+ if (!IfErrorPushError())
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2));
+ PushTempTokenWithoutError( new ScExternalDoubleRefToken(nFileId,
+ mrDoc.GetSharedStringPool().intern( rTabName), aRef) );
+ }
+}
+
+void ScInterpreter::PushSingleRef( const ScRefAddress& rRef )
+{
+ if (!IfErrorPushError())
+ {
+ ScSingleRefData aRef;
+ aRef.InitFromRefAddress( mrDoc, rRef, aPos);
+ PushTempTokenWithoutError( new ScSingleRefToken( mrDoc.GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushDoubleRef( const ScRefAddress& rRef1, const ScRefAddress& rRef2 )
+{
+ if (!IfErrorPushError())
+ {
+ ScComplexRefData aRef;
+ aRef.InitFromRefAddresses( mrDoc, rRef1, rRef2, aPos);
+ PushTempTokenWithoutError( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRef ) );
+ }
+}
+
+void ScInterpreter::PushMatrix( const sc::RangeMatrix& rMat )
+{
+ if (!rMat.isRangeValid())
+ {
+ // Just push the matrix part only.
+ PushMatrix(rMat.mpMat);
+ return;
+ }
+
+ rMat.mpMat->SetErrorInterpreter(nullptr);
+ nGlobalError = FormulaError::NONE;
+ PushTempTokenWithoutError(new ScMatrixRangeToken(rMat));
+}
+
+void ScInterpreter::PushMatrix(const ScMatrixRef& pMat)
+{
+ pMat->SetErrorInterpreter( nullptr);
+ // No if (!IfErrorPushError()) because ScMatrix stores errors itself,
+ // but with notifying ScInterpreter via nGlobalError, substituting it would
+ // mean to inherit the error on all array elements in all following
+ // operations.
+ nGlobalError = FormulaError::NONE;
+ PushTempTokenWithoutError( new ScMatrixToken( pMat ) );
+}
+
+void ScInterpreter::PushError( FormulaError nError )
+{
+ SetError( nError ); // only sets error if not already set
+ PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
+}
+
+void ScInterpreter::PushParameterExpected()
+{
+ PushError( FormulaError::ParameterExpected);
+}
+
+void ScInterpreter::PushIllegalParameter()
+{
+ PushError( FormulaError::IllegalParameter);
+}
+
+void ScInterpreter::PushIllegalArgument()
+{
+ PushError( FormulaError::IllegalArgument);
+}
+
+void ScInterpreter::PushNA()
+{
+ PushError( FormulaError::NotAvailable);
+}
+
+void ScInterpreter::PushNoValue()
+{
+ PushError( FormulaError::NoValue);
+}
+
+bool ScInterpreter::IsMissing() const
+{
+ return sp && pStack[sp - 1]->GetType() == svMissing;
+}
+
+StackVar ScInterpreter::GetRawStackType()
+{
+ StackVar eRes;
+ if( sp )
+ {
+ eRes = pStack[sp - 1]->GetType();
+ }
+ else
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ eRes = svUnknown;
+ }
+ return eRes;
+}
+
+StackVar ScInterpreter::GetStackType()
+{
+ StackVar eRes;
+ if( sp )
+ {
+ eRes = pStack[sp - 1]->GetType();
+ if( eRes == svMissing || eRes == svEmptyCell )
+ eRes = svDouble; // default!
+ }
+ else
+ {
+ SetError(FormulaError::UnknownStackVariable);
+ eRes = svUnknown;
+ }
+ return eRes;
+}
+
+StackVar ScInterpreter::GetStackType( sal_uInt8 nParam )
+{
+ StackVar eRes;
+ if( sp > nParam-1 )
+ {
+ eRes = pStack[sp - nParam]->GetType();
+ if( eRes == svMissing || eRes == svEmptyCell )
+ eRes = svDouble; // default!
+ }
+ else
+ eRes = svUnknown;
+ return eRes;
+}
+
+void ScInterpreter::ReverseStack( sal_uInt8 nParamCount )
+{
+ //reverse order of parameter stack
+ assert( sp >= nParamCount && " less stack elements than parameters");
+ sal_uInt16 nStackParams = std::min<sal_uInt16>( sp, nParamCount);
+ std::reverse( pStack+(sp-nStackParams), pStack+sp );
+}
+
+bool ScInterpreter::DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& rAdr )
+{
+ // Check for a singleton first - no implicit intersection for them.
+ if( rRange.aStart == rRange.aEnd )
+ {
+ rAdr = rRange.aStart;
+ return true;
+ }
+
+ bool bOk = false;
+
+ if ( pJumpMatrix )
+ {
+ bOk = rRange.aStart.Tab() == rRange.aEnd.Tab();
+ if ( !bOk )
+ SetError( FormulaError::IllegalArgument);
+ else
+ {
+ SCSIZE nC, nR;
+ pJumpMatrix->GetPos( nC, nR);
+ rAdr.SetCol( sal::static_int_cast<SCCOL>( rRange.aStart.Col() + nC ) );
+ rAdr.SetRow( sal::static_int_cast<SCROW>( rRange.aStart.Row() + nR ) );
+ rAdr.SetTab( rRange.aStart.Tab());
+ bOk = rRange.aStart.Col() <= rAdr.Col() && rAdr.Col() <=
+ rRange.aEnd.Col() && rRange.aStart.Row() <= rAdr.Row() &&
+ rAdr.Row() <= rRange.aEnd.Row();
+ if ( !bOk )
+ SetError( FormulaError::NoValue);
+ }
+ return bOk;
+ }
+
+ bOk = ScCompiler::DoubleRefToPosSingleRefScalarCase(rRange, rAdr, aPos);
+
+ if ( !bOk )
+ SetError( FormulaError::NoValue );
+ return bOk;
+}
+
+double ScInterpreter::GetDoubleFromMatrix(const ScMatrixRef& pMat)
+{
+ if (!pMat)
+ return 0.0;
+
+ if ( !pJumpMatrix )
+ {
+ double fVal = pMat->GetDoubleWithStringConversion( 0, 0);
+ FormulaError nErr = GetDoubleErrorValue( fVal);
+ if (nErr != FormulaError::NONE)
+ {
+ // Do not propagate the coded double error, but set nGlobalError in
+ // case the matrix did not have an error interpreter set.
+ SetError( nErr);
+ fVal = 0.0;
+ }
+ return fVal;
+ }
+
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ // Use vector replication for single row/column arrays.
+ if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) )
+ {
+ double fVal = pMat->GetDoubleWithStringConversion( nC, nR);
+ FormulaError nErr = GetDoubleErrorValue( fVal);
+ if (nErr != FormulaError::NONE)
+ {
+ // Do not propagate the coded double error, but set nGlobalError in
+ // case the matrix did not have an error interpreter set.
+ SetError( nErr);
+ fVal = 0.0;
+ }
+ return fVal;
+ }
+
+ SetError( FormulaError::NoValue);
+ return 0.0;
+}
+
+double ScInterpreter::GetDouble()
+{
+ double nVal(0.0);
+ switch( GetRawStackType() )
+ {
+ case svDouble:
+ nVal = PopDouble();
+ break;
+ case svString:
+ nVal = ConvertStringToValue( PopString().getString());
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ nVal = GetCellValue(aAdr, aCell);
+ }
+ break;
+ case svDoubleRef:
+ { // generate position dependent SingleRef
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ ScAddress aAdr;
+ if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr ) )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ nVal = GetCellValue(aAdr, aCell);
+ }
+ else
+ nVal = 0.0;
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError == FormulaError::NONE)
+ {
+ if (pToken->GetType() == svDouble || pToken->GetType() == svEmptyCell)
+ nVal = pToken->GetDouble();
+ else
+ nVal = ConvertStringToValue( pToken->GetString().getString());
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ PopExternalDoubleRef(pMat);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ nVal = GetDoubleFromMatrix(pMat);
+ }
+ break;
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ nVal = GetDoubleFromMatrix(pMat);
+ }
+ break;
+ case svError:
+ PopError();
+ nVal = 0.0;
+ break;
+ case svEmptyCell:
+ case svMissing:
+ Pop();
+ nVal = 0.0;
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ nVal = 0.0;
+ }
+ if ( nFuncFmtType == nCurFmtType )
+ nFuncFmtIndex = nCurFmtIndex;
+ return nVal;
+}
+
+double ScInterpreter::GetDoubleWithDefault(double nDefault)
+{
+ bool bMissing = IsMissing();
+ double nResultVal = GetDouble();
+ if ( bMissing )
+ nResultVal = nDefault;
+ return nResultVal;
+}
+
+sal_Int32 ScInterpreter::double_to_int32(double fVal)
+{
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_INT32;
+ }
+ if (fVal > 0.0)
+ {
+ fVal = rtl::math::approxFloor( fVal);
+ if (fVal > SAL_MAX_INT32)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT32;
+ }
+ }
+ else if (fVal < 0.0)
+ {
+ fVal = rtl::math::approxCeil( fVal);
+ if (fVal < SAL_MIN_INT32)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT32;
+ }
+ }
+ return static_cast<sal_Int32>(fVal);
+}
+
+sal_Int32 ScInterpreter::GetInt32()
+{
+ return double_to_int32(GetDouble());
+}
+
+sal_Int32 ScInterpreter::GetInt32WithDefault( sal_Int32 nDefault )
+{
+ bool bMissing = IsMissing();
+ double fVal = GetDouble();
+ if ( bMissing )
+ return nDefault;
+ return double_to_int32(fVal);
+}
+
+sal_Int32 ScInterpreter::GetFloor32()
+{
+ double fVal = GetDouble();
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_INT32;
+ }
+ fVal = rtl::math::approxFloor( fVal);
+ if (fVal < SAL_MIN_INT32 || SAL_MAX_INT32 < fVal)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT32;
+ }
+ return static_cast<sal_Int32>(fVal);
+}
+
+sal_Int16 ScInterpreter::GetInt16()
+{
+ double fVal = GetDouble();
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_INT16;
+ }
+ if (fVal > 0.0)
+ {
+ fVal = rtl::math::approxFloor( fVal);
+ if (fVal > SAL_MAX_INT16)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT16;
+ }
+ }
+ else if (fVal < 0.0)
+ {
+ fVal = rtl::math::approxCeil( fVal);
+ if (fVal < SAL_MIN_INT16)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_INT16;
+ }
+ }
+ return static_cast<sal_Int16>(fVal);
+}
+
+sal_uInt32 ScInterpreter::GetUInt32()
+{
+ double fVal = rtl::math::approxFloor( GetDouble());
+ if (!std::isfinite(fVal))
+ {
+ SetError( GetDoubleErrorValue( fVal));
+ return SAL_MAX_UINT32;
+ }
+ if (fVal < 0.0 || fVal > SAL_MAX_UINT32)
+ {
+ SetError( FormulaError::IllegalArgument);
+ return SAL_MAX_UINT32;
+ }
+ return static_cast<sal_uInt32>(fVal);
+}
+
+bool ScInterpreter::GetDoubleOrString( double& rDouble, svl::SharedString& rString )
+{
+ bool bDouble = true;
+ switch( GetRawStackType() )
+ {
+ case svDouble:
+ rDouble = PopDouble();
+ break;
+ case svString:
+ rString = PopString();
+ bDouble = false;
+ break;
+ case svDoubleRef :
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ if (!PopDoubleRefOrSingleRef( aAdr))
+ {
+ rDouble = 0.0;
+ return true; // caller needs to check nGlobalError
+ }
+ ScRefCellValue aCell( mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ {
+ rDouble = GetCellValue( aAdr, aCell);
+ }
+ else
+ {
+ GetCellString( rString, aCell);
+ bDouble = false;
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatValType nType = GetDoubleOrStringFromMatrix( rDouble, rString);
+ bDouble = ScMatrix::IsValueType( nType);
+ }
+ break;
+ case svError:
+ PopError();
+ rDouble = 0.0;
+ break;
+ case svEmptyCell:
+ case svMissing:
+ Pop();
+ rDouble = 0.0;
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ rDouble = 0.0;
+ }
+ if ( nFuncFmtType == nCurFmtType )
+ nFuncFmtIndex = nCurFmtIndex;
+ return bDouble;
+}
+
+svl::SharedString ScInterpreter::GetString()
+{
+ switch (GetRawStackType())
+ {
+ case svError:
+ PopError();
+ return svl::SharedString::getEmptyString();
+ case svMissing:
+ case svEmptyCell:
+ Pop();
+ return svl::SharedString::getEmptyString();
+ case svDouble:
+ {
+ return GetStringFromDouble( PopDouble() );
+ }
+ case svString:
+ return PopString();
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if (nGlobalError == FormulaError::NONE)
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ svl::SharedString aSS;
+ GetCellString(aSS, aCell);
+ return aSS;
+ }
+ else
+ return svl::SharedString::getEmptyString();
+ }
+ case svDoubleRef:
+ { // generate position dependent SingleRef
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ ScAddress aAdr;
+ if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr ) )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ svl::SharedString aSS;
+ GetCellString(aSS, aCell);
+ return aSS;
+ }
+ else
+ return svl::SharedString::getEmptyString();
+ }
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ return svl::SharedString::getEmptyString();
+
+ if (pToken->GetType() == svDouble)
+ {
+ return GetStringFromDouble( pToken->GetDouble() );
+ }
+ else // svString or svEmpty
+ return pToken->GetString();
+ }
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ PopExternalDoubleRef(pMat);
+ return GetStringFromMatrix(pMat);
+ }
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ return GetStringFromMatrix(pMat);
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ }
+ return svl::SharedString::getEmptyString();
+}
+
+svl::SharedString ScInterpreter::GetStringFromMatrix(const ScMatrixRef& pMat)
+{
+ if ( !pMat )
+ ; // nothing
+ else if ( !pJumpMatrix )
+ {
+ return pMat->GetString( *pFormatter, 0, 0);
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ // Use vector replication for single row/column arrays.
+ if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) )
+ return pMat->GetString( *pFormatter, nC, nR);
+
+ SetError( FormulaError::NoValue);
+ }
+ return svl::SharedString::getEmptyString();
+}
+
+ScMatValType ScInterpreter::GetDoubleOrStringFromMatrix(
+ double& rDouble, svl::SharedString& rString )
+{
+
+ rDouble = 0.0;
+ rString = svl::SharedString::getEmptyString();
+ ScMatValType nMatValType = ScMatValType::Empty;
+
+ ScMatrixRef pMat;
+ StackVar eType = GetStackType();
+ if (eType == svExternalDoubleRef || eType == svExternalSingleRef || eType == svMatrix)
+ {
+ pMat = GetMatrix();
+ }
+ else
+ {
+ PopError();
+ SetError( FormulaError::IllegalParameter);
+ return nMatValType;
+ }
+
+ ScMatrixValue nMatVal;
+ if (!pMat)
+ {
+ // nothing
+ }
+ else if (!pJumpMatrix)
+ {
+ nMatVal = pMat->Get(0, 0);
+ nMatValType = nMatVal.nType;
+ }
+ else
+ {
+ SCSIZE nCols, nRows, nC, nR;
+ pMat->GetDimensions( nCols, nRows);
+ pJumpMatrix->GetPos( nC, nR);
+ // Use vector replication for single row/column arrays.
+ if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) )
+ {
+ nMatVal = pMat->Get( nC, nR);
+ nMatValType = nMatVal.nType;
+ }
+ else
+ SetError( FormulaError::NoValue);
+ }
+
+ if (ScMatrix::IsValueType( nMatValType))
+ {
+ rDouble = nMatVal.fVal;
+ FormulaError nError = nMatVal.GetError();
+ if (nError != FormulaError::NONE)
+ SetError( nError);
+ }
+ else
+ {
+ rString = nMatVal.GetString();
+ }
+
+ return nMatValType;
+}
+
+svl::SharedString ScInterpreter::GetStringFromDouble( double fVal )
+{
+ sal_uLong nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ OUString aStr;
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ return mrStrPool.intern(aStr);
+}
+
+void ScInterpreter::ScDBGet()
+{
+ bool bMissingField = false;
+ unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
+ if (!pQueryParam)
+ {
+ // Failed to create query param.
+ PushIllegalParameter();
+ return;
+ }
+
+ pQueryParam->mbSkipString = false;
+ ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
+ ScDBQueryDataIterator::Value aValue;
+ if (!aValIter.GetFirst(aValue) || aValue.mnError != FormulaError::NONE)
+ {
+ // No match found.
+ PushNoValue();
+ return;
+ }
+
+ ScDBQueryDataIterator::Value aValNext;
+ if (aValIter.GetNext(aValNext) && aValNext.mnError == FormulaError::NONE)
+ {
+ // There should be only one unique match.
+ PushIllegalArgument();
+ return;
+ }
+
+ if (aValue.mbIsNumber)
+ PushDouble(aValue.mfValue);
+ else
+ PushString(aValue.maString);
+}
+
+void ScInterpreter::ScExternal()
+{
+ sal_uInt8 nParamCount = GetByte();
+ OUString aUnoName;
+ OUString aFuncName( pCur->GetExternal().toAsciiUpperCase()); // programmatic name
+ LegacyFuncData* pLegacyFuncData = ScGlobal::GetLegacyFuncCollection()->findByName(aFuncName);
+ if (pLegacyFuncData)
+ {
+ // Old binary non-UNO add-in function.
+ // NOTE: parameter count is 1-based with the 0th "parameter" being the
+ // return value, included in pLegacyFuncDatat->GetParamCount()
+ if (nParamCount < MAXFUNCPARAM && nParamCount == pLegacyFuncData->GetParamCount() - 1)
+ {
+ ParamType eParamType[MAXFUNCPARAM];
+ void* ppParam[MAXFUNCPARAM];
+ double nVal[MAXFUNCPARAM];
+ char* pStr[MAXFUNCPARAM];
+ sal_uInt8* pCellArr[MAXFUNCPARAM];
+ short i;
+
+ for (i = 0; i < MAXFUNCPARAM; i++)
+ {
+ eParamType[i] = pLegacyFuncData->GetParamType(i);
+ ppParam[i] = nullptr;
+ nVal[i] = 0.0;
+ pStr[i] = nullptr;
+ pCellArr[i] = nullptr;
+ }
+
+ for (i = nParamCount; (i > 0) && (nGlobalError == FormulaError::NONE); i--)
+ {
+ if (IsMissing())
+ {
+ // Old binary Add-In can't distinguish between missing
+ // omitted argument and 0 (or any other value). Force
+ // error.
+ SetError( FormulaError::ParameterExpected);
+ break; // for
+ }
+ switch (eParamType[i])
+ {
+ case ParamType::PTR_DOUBLE :
+ {
+ nVal[i-1] = GetDouble();
+ ppParam[i] = &nVal[i-1];
+ }
+ break;
+ case ParamType::PTR_STRING :
+ {
+ OString aStr(OUStringToOString(GetString().getString(),
+ osl_getThreadTextEncoding()));
+ if ( aStr.getLength() >= ADDIN_MAXSTRLEN )
+ SetError( FormulaError::StringOverflow );
+ else
+ {
+ pStr[i-1] = new char[ADDIN_MAXSTRLEN];
+ strncpy( pStr[i-1], aStr.getStr(), ADDIN_MAXSTRLEN );
+ pStr[i-1][ADDIN_MAXSTRLEN-1] = 0;
+ ppParam[i] = pStr[i-1];
+ }
+ }
+ break;
+ case ParamType::PTR_DOUBLE_ARR :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
+ if (!CreateDoubleArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
+ SetError(FormulaError::CodeOverflow);
+ else
+ ppParam[i] = pCellArr[i-1];
+ }
+ break;
+ case ParamType::PTR_STRING_ARR :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
+ if (!CreateStringArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
+ SetError(FormulaError::CodeOverflow);
+ else
+ ppParam[i] = pCellArr[i-1];
+ }
+ break;
+ case ParamType::PTR_CELL_ARR :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
+ if (!CreateCellArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
+ SetError(FormulaError::CodeOverflow);
+ else
+ ppParam[i] = pCellArr[i-1];
+ }
+ break;
+ default :
+ SetError(FormulaError::IllegalParameter);
+ break;
+ }
+ }
+ while ( i-- )
+ Pop(); // In case of error (otherwise i==0) pop all parameters
+
+ if (nGlobalError == FormulaError::NONE)
+ {
+ if ( pLegacyFuncData->GetAsyncType() == ParamType::NONE )
+ {
+ switch ( eParamType[0] )
+ {
+ case ParamType::PTR_DOUBLE :
+ {
+ double nErg = 0.0;
+ ppParam[0] = &nErg;
+ pLegacyFuncData->Call(ppParam);
+ PushDouble(nErg);
+ }
+ break;
+ case ParamType::PTR_STRING :
+ {
+ std::unique_ptr<char[]> pcErg(new char[ADDIN_MAXSTRLEN]);
+ ppParam[0] = pcErg.get();
+ pLegacyFuncData->Call(ppParam);
+ OUString aUni( pcErg.get(), strlen(pcErg.get()), osl_getThreadTextEncoding() );
+ PushString( aUni );
+ }
+ break;
+ default:
+ PushError( FormulaError::UnknownState );
+ }
+ }
+ else
+ {
+ // enable asyncs after loading
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+ // assure identical handler with identical call?
+ double nErg = 0.0;
+ ppParam[0] = &nErg;
+ pLegacyFuncData->Call(ppParam);
+ sal_uLong nHandle = sal_uLong( nErg );
+ if ( nHandle >= 65536 )
+ {
+ ScAddInAsync* pAs = ScAddInAsync::Get( nHandle );
+ if ( !pAs )
+ {
+ pAs = new ScAddInAsync(nHandle, pLegacyFuncData, &mrDoc);
+ pMyFormulaCell->StartListening( *pAs );
+ }
+ else
+ {
+ pMyFormulaCell->StartListening( *pAs );
+ if ( !pAs->HasDocument( &mrDoc ) )
+ pAs->AddDocument( &mrDoc );
+ }
+ if ( pAs->IsValid() )
+ {
+ switch ( pAs->GetType() )
+ {
+ case ParamType::PTR_DOUBLE :
+ PushDouble( pAs->GetValue() );
+ break;
+ case ParamType::PTR_STRING :
+ PushString( pAs->GetString() );
+ break;
+ default:
+ PushError( FormulaError::UnknownState );
+ }
+ }
+ else
+ PushNA();
+ }
+ else
+ PushNoValue();
+ }
+ }
+
+ for (i = 0; i < MAXFUNCPARAM; i++)
+ {
+ delete[] pStr[i];
+ delete[] pCellArr[i];
+ }
+ }
+ else
+ {
+ while( nParamCount-- > 0)
+ PopError();
+ PushIllegalParameter();
+ }
+ }
+ else if ( !( aUnoName = ScGlobal::GetAddInCollection()->FindFunction(aFuncName, false) ).isEmpty() )
+ {
+ // bLocalFirst=false in FindFunction, cFunc should be the stored
+ // internal name
+
+ ScUnoAddInCall aCall( mrDoc, *ScGlobal::GetAddInCollection(), aUnoName, nParamCount );
+
+ if ( !aCall.ValidParamCount() )
+ SetError( FormulaError::IllegalParameter );
+
+ if ( aCall.NeedsCaller() && GetError() == FormulaError::NONE )
+ {
+ ScDocShell* pShell = mrDoc.GetDocumentShell();
+ if (pShell)
+ aCall.SetCallerFromObjectShell( pShell );
+ else
+ {
+ // use temporary model object (without document) to supply options
+ aCall.SetCaller( static_cast<beans::XPropertySet*>(
+ new ScDocOptionsObj( mrDoc.GetDocOptions() ) ) );
+ }
+ }
+
+ short nPar = nParamCount;
+ while ( nPar > 0 && GetError() == FormulaError::NONE )
+ {
+ --nPar; // 0 .. (nParamCount-1)
+
+ uno::Any aParam;
+ if (IsMissing())
+ {
+ // Add-In has to explicitly handle an omitted empty missing
+ // argument, do not default to anything like GetDouble() would
+ // do (e.g. 0).
+ Pop();
+ aCall.SetParam( nPar, aParam );
+ continue; // while
+ }
+
+ StackVar nStackType = GetStackType();
+ ScAddInArgumentType eType = aCall.GetArgType( nPar );
+ switch (eType)
+ {
+ case SC_ADDINARG_INTEGER:
+ {
+ sal_Int32 nVal = GetInt32();
+ if (nGlobalError == FormulaError::NONE)
+ aParam <<= nVal;
+ }
+ break;
+
+ case SC_ADDINARG_DOUBLE:
+ aParam <<= GetDouble();
+ break;
+
+ case SC_ADDINARG_STRING:
+ aParam <<= GetString().getString();
+ break;
+
+ case SC_ADDINARG_INTEGER_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ sal_Int32 nVal = GetInt32();
+ if (nGlobalError == FormulaError::NONE)
+ {
+ uno::Sequence<sal_Int32> aInner( &nVal, 1 );
+ uno::Sequence< uno::Sequence<sal_Int32> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillLongArray( aParam, mrDoc, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillLongArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_DOUBLE_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ double fVal = GetDouble();
+ uno::Sequence<double> aInner( &fVal, 1 );
+ uno::Sequence< uno::Sequence<double> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillDoubleArray( aParam, mrDoc, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillDoubleArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_STRING_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ OUString aString = GetString().getString();
+ uno::Sequence<OUString> aInner( &aString, 1 );
+ uno::Sequence< uno::Sequence<OUString> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillStringArray( aParam, mrDoc, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillStringArray( aParam, PopMatrix().get(), pFormatter ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_MIXED_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ case svString:
+ case svSingleRef:
+ {
+ uno::Any aElem;
+ if ( nStackType == svDouble )
+ aElem <<= GetDouble();
+ else if ( nStackType == svString )
+ aElem <<= GetString().getString();
+ else
+ {
+ ScAddress aAdr;
+ if ( PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ aElem <<= aStr.getString();
+ }
+ else
+ aElem <<= GetCellValue(aAdr, aCell);
+ }
+ }
+ uno::Sequence<uno::Any> aInner( &aElem, 1 );
+ uno::Sequence< uno::Sequence<uno::Any> > aOuter( &aInner, 1 );
+ aParam <<= aOuter;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillMixedArray( aParam, mrDoc, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_VALUE_OR_ARRAY:
+ switch( nStackType )
+ {
+ case svDouble:
+ aParam <<= GetDouble();
+ break;
+ case svString:
+ aParam <<= GetString().getString();
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ if ( PopDoubleRefOrSingleRef( aAdr ) )
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasString())
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ aParam <<= aStr.getString();
+ }
+ else
+ aParam <<= GetCellValue(aAdr, aCell);
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ if (!ScRangeToSequence::FillMixedArray( aParam, mrDoc, aRange ))
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() ))
+ SetError(FormulaError::IllegalParameter);
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ case SC_ADDINARG_CELLRANGE:
+ switch( nStackType )
+ {
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRange aRange( aAdr );
+ uno::Reference<table::XCellRange> xObj =
+ ScCellRangeObj::CreateRangeFromDoc( mrDoc, aRange );
+ if (xObj.is())
+ aParam <<= xObj;
+ else
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ uno::Reference<table::XCellRange> xObj =
+ ScCellRangeObj::CreateRangeFromDoc( mrDoc, aRange );
+ if (xObj.is())
+ {
+ aParam <<= xObj;
+ }
+ else
+ {
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+ break;
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ break;
+
+ default:
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ aCall.SetParam( nPar, aParam );
+ }
+
+ while (nPar-- > 0)
+ {
+ Pop(); // in case of error, remove remaining args
+ }
+ if ( GetError() == FormulaError::NONE )
+ {
+ aCall.ExecuteCall();
+
+ if ( aCall.HasVarRes() ) // handle async functions
+ {
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+ uno::Reference<sheet::XVolatileResult> xRes = aCall.GetVarRes();
+ ScAddInListener* pLis = ScAddInListener::Get( xRes );
+ // In case there is no pMyFormulaCell, i.e. while interpreting
+ // temporarily from within the Function Wizard, try to obtain a
+ // valid result from an existing listener for that volatile, or
+ // create a new and hope for an immediate result. If none
+ // available that should lead to a void result and thus #N/A.
+ bool bTemporaryListener = false;
+ if ( !pLis )
+ {
+ pLis = ScAddInListener::CreateListener( xRes, &mrDoc );
+ if (pMyFormulaCell)
+ pMyFormulaCell->StartListening( *pLis );
+ else
+ bTemporaryListener = true;
+ }
+ else if (pMyFormulaCell)
+ {
+ pMyFormulaCell->StartListening( *pLis );
+ if ( !pLis->HasDocument( &mrDoc ) )
+ {
+ pLis->AddDocument( &mrDoc );
+ }
+ }
+
+ aCall.SetResult( pLis->GetResult() ); // use result from async
+
+ if (bTemporaryListener)
+ {
+ try
+ {
+ // EventObject can be any, not evaluated by
+ // ScAddInListener::disposing()
+ css::lang::EventObject aEvent;
+ pLis->disposing(aEvent); // pLis is dead hereafter
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ if ( aCall.GetErrCode() != FormulaError::NONE )
+ {
+ PushError( aCall.GetErrCode() );
+ }
+ else if ( aCall.HasMatrix() )
+ {
+ PushMatrix( aCall.GetMatrix() );
+ }
+ else if ( aCall.HasString() )
+ {
+ PushString( aCall.GetString() );
+ }
+ else
+ {
+ PushDouble( aCall.GetValue() );
+ }
+ }
+ else // error...
+ PushError( GetError());
+ }
+ else
+ {
+ while( nParamCount-- > 0)
+ {
+ PopError();
+ }
+ PushError( FormulaError::NoAddin );
+ }
+}
+
+void ScInterpreter::ScMissing()
+{
+ if ( aCode.IsEndOfPath() )
+ PushTempToken( new ScEmptyCellToken( false, false ) );
+ else
+ PushTempToken( new FormulaMissingToken );
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+static uno::Any lcl_getSheetModule( const uno::Reference<table::XCellRange>& xCellRange, const ScDocument* pDok )
+{
+ uno::Reference< sheet::XSheetCellRange > xSheetRange( xCellRange, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xSheetRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
+ OUString sCodeName;
+ xProps->getPropertyValue("CodeName") >>= sCodeName;
+ // #TODO #FIXME ideally we should 'throw' here if we don't get a valid parent, but... it is possible
+ // to create a module ( and use 'Option VBASupport 1' ) for a calc document, in this scenario there
+ // are *NO* special document module objects ( of course being able to switch between vba/non vba mode at
+ // the document in the future could fix this, especially IF the switching of the vba mode takes care to
+ // create the special document module objects if they don't exist.
+ BasicManager* pBasMgr = pDok->GetDocumentShell()->GetBasicManager();
+
+ uno::Reference< uno::XInterface > xIf;
+ if ( pBasMgr && !pBasMgr->GetName().isEmpty() )
+ {
+ OUString sProj( "Standard" );
+ if ( !pDok->GetDocumentShell()->GetBasicManager()->GetName().isEmpty() )
+ {
+ sProj = pDok->GetDocumentShell()->GetBasicManager()->GetName();
+ }
+ StarBASIC* pBasic = pDok->GetDocumentShell()->GetBasicManager()->GetLib( sProj );
+ if ( pBasic )
+ {
+ SbModule* pMod = pBasic->FindModule( sCodeName );
+ if ( pMod )
+ {
+ xIf = pMod->GetUnoModule();
+ }
+ }
+ }
+ return uno::Any( xIf );
+}
+
+static bool lcl_setVBARange( const ScRange& aRange, const ScDocument& rDok, SbxVariable* pPar )
+{
+ bool bOk = false;
+ try
+ {
+ uno::Reference< uno::XInterface > xVBARange;
+ uno::Reference<table::XCellRange> xCellRange = ScCellRangeObj::CreateRangeFromDoc( rDok, aRange );
+ uno::Sequence< uno::Any > aArgs{ lcl_getSheetModule( xCellRange, &rDok ),
+ uno::Any(xCellRange) };
+ xVBARange = ooo::vba::createVBAUnoAPIServiceWithArgs( rDok.GetDocumentShell(), "ooo.vba.excel.Range", aArgs );
+ if ( xVBARange.is() )
+ {
+ SbxObjectRef aObj = GetSbUnoObject( "A-Range", uno::Any( xVBARange ) );
+ SetSbUnoObjectDfltPropName( aObj.get() );
+ bOk = pPar->PutObject( aObj.get() );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return bOk;
+}
+
+static bool lcl_isNumericResult( double& fVal, const SbxVariable* pVar )
+{
+ switch (pVar->GetType())
+ {
+ case SbxINTEGER:
+ case SbxLONG:
+ case SbxSINGLE:
+ case SbxDOUBLE:
+ case SbxCURRENCY:
+ case SbxDATE:
+ case SbxUSHORT:
+ case SbxULONG:
+ case SbxINT:
+ case SbxUINT:
+ case SbxSALINT64:
+ case SbxSALUINT64:
+ case SbxDECIMAL:
+ fVal = pVar->GetDouble();
+ return true;
+ case SbxBOOL:
+ fVal = (pVar->GetBool() ? 1.0 : 0.0);
+ return true;
+ default:
+ ; // nothing
+ }
+ return false;
+}
+
+#endif
+
+void ScInterpreter::ScMacro()
+{
+
+#if !HAVE_FEATURE_SCRIPTING
+ PushNoValue(); // without DocShell no CallBasic
+ return;
+#else
+ SbxBase::ResetError();
+
+ sal_uInt8 nParamCount = GetByte();
+ OUString aMacro( pCur->GetExternal() );
+
+ ScDocShell* pDocSh = mrDoc.GetDocumentShell();
+ if ( !pDocSh )
+ {
+ PushNoValue(); // without DocShell no CallBasic
+ return;
+ }
+
+ // no security queue beforehand (just CheckMacroWarn), moved to CallBasic
+
+ // If the Dok was loaded during a Basic-Calls,
+ // is the Sbx-object created(?)
+// pDocSh->GetSbxObject();
+
+ // search function with the name,
+ // then assemble SfxObjectShell::CallBasic from aBasicStr, aMacroStr
+
+ StarBASIC* pRoot;
+
+ try
+ {
+ pRoot = pDocSh->GetBasic();
+ }
+ catch (...)
+ {
+ pRoot = nullptr;
+ }
+
+ SbxVariable* pVar = pRoot ? pRoot->Find(aMacro, SbxClassType::Method) : nullptr;
+ if( !pVar || pVar->GetType() == SbxVOID )
+ {
+ PushError( FormulaError::NoMacro );
+ return;
+ }
+ SbMethod* pMethod = dynamic_cast<SbMethod*>(pVar);
+ if( !pMethod )
+ {
+ PushError( FormulaError::NoMacro );
+ return;
+ }
+
+ bool bVolatileMacro = false;
+
+ SbModule* pModule = pMethod->GetModule();
+ bool bUseVBAObjects = pModule->IsVBASupport();
+ SbxObject* pObject = pModule->GetParent();
+ OSL_ENSURE(dynamic_cast<const StarBASIC *>(pObject) != nullptr, "No Basic found!");
+ OUString aMacroStr = pObject->GetName() + "." + pModule->GetName() + "." + pMethod->GetName();
+ OUString aBasicStr;
+ if (pRoot && bUseVBAObjects)
+ {
+ // just here to make sure the VBA objects when we run the macro during ODF import
+ pRoot->getVBAGlobals();
+ }
+ if (pObject->GetParent())
+ {
+ aBasicStr = pObject->GetParent()->GetName(); // document BASIC
+ }
+ else
+ {
+ aBasicStr = SfxGetpApp()->GetName(); // application BASIC
+ }
+ // assemble a parameter array
+
+ SbxArrayRef refPar = new SbxArray;
+ bool bOk = true;
+ for( sal_uInt32 i = nParamCount; i && bOk ; i-- )
+ {
+ SbxVariable* pPar = refPar->Get(i);
+ switch( GetStackType() )
+ {
+ case svDouble:
+ pPar->PutDouble( GetDouble() );
+ break;
+ case svString:
+ pPar->PutString( GetString().getString() );
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ if (nGlobalError != FormulaError::NONE)
+ bOk = false;
+ else
+ {
+ if ( pToken->GetType() == svString )
+ pPar->PutString( pToken->GetString().getString() );
+ else if ( pToken->GetType() == svDouble )
+ pPar->PutDouble( pToken->GetDouble() );
+ else
+ {
+ SetError( FormulaError::IllegalArgument );
+ bOk = false;
+ }
+ }
+ }
+ break;
+ case svSingleRef:
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( bUseVBAObjects )
+ {
+ ScRange aRange( aAdr );
+ bOk = lcl_setVBARange( aRange, mrDoc, pPar );
+ }
+ else
+ {
+ bOk = SetSbxVariable( pPar, aAdr );
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if( nTab1 != nTab2 )
+ {
+ SetError( FormulaError::IllegalParameter );
+ bOk = false;
+ }
+ else
+ {
+ if ( bUseVBAObjects )
+ {
+ ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ bOk = lcl_setVBARange( aRange, mrDoc, pPar );
+ }
+ else
+ {
+ SbxDimArrayRef refArray = new SbxDimArray;
+ refArray->AddDim(1, nRow2 - nRow1 + 1);
+ refArray->AddDim(1, nCol2 - nCol1 + 1);
+ ScAddress aAdr( nCol1, nRow1, nTab1 );
+ for( SCROW nRow = nRow1; bOk && nRow <= nRow2; nRow++ )
+ {
+ aAdr.SetRow( nRow );
+ sal_Int32 nIdx[ 2 ];
+ nIdx[ 0 ] = nRow-nRow1+1;
+ for( SCCOL nCol = nCol1; bOk && nCol <= nCol2; nCol++ )
+ {
+ aAdr.SetCol( nCol );
+ nIdx[ 1 ] = nCol-nCol1+1;
+ SbxVariable* p = refArray->Get(nIdx);
+ bOk = SetSbxVariable( p, aAdr );
+ }
+ }
+ pPar->PutObject( refArray.get() );
+ }
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ case svMatrix:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ SCSIZE nC, nR;
+ if (pMat && nGlobalError == FormulaError::NONE)
+ {
+ pMat->GetDimensions(nC, nR);
+ SbxDimArrayRef refArray = new SbxDimArray;
+ refArray->AddDim(1, static_cast<sal_Int32>(nR));
+ refArray->AddDim(1, static_cast<sal_Int32>(nC));
+ for( SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++ )
+ {
+ sal_Int32 nIdx[ 2 ];
+ nIdx[ 0 ] = static_cast<sal_Int32>(nMatRow+1);
+ for( SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++ )
+ {
+ nIdx[ 1 ] = static_cast<sal_Int32>(nMatCol+1);
+ SbxVariable* p = refArray->Get(nIdx);
+ if (pMat->IsStringOrEmpty(nMatCol, nMatRow))
+ {
+ p->PutString( pMat->GetString(nMatCol, nMatRow).getString() );
+ }
+ else
+ {
+ p->PutDouble( pMat->GetDouble(nMatCol, nMatRow));
+ }
+ }
+ }
+ pPar->PutObject( refArray.get() );
+ }
+ else
+ {
+ SetError( FormulaError::IllegalParameter );
+ }
+ }
+ break;
+ default:
+ SetError( FormulaError::IllegalParameter );
+ bOk = false;
+ }
+ }
+ if( bOk )
+ {
+ mrDoc.LockTable( aPos.Tab() );
+ SbxVariableRef refRes = new SbxVariable;
+ mrDoc.IncMacroInterpretLevel();
+ ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar.get(), refRes.get() );
+ mrDoc.DecMacroInterpretLevel();
+ mrDoc.UnlockTable( aPos.Tab() );
+
+ ScMacroManager* pMacroMgr = mrDoc.GetMacroManager();
+ if (pMacroMgr)
+ {
+ bVolatileMacro = pMacroMgr->GetUserFuncVolatile( pMethod->GetName() );
+ pMacroMgr->AddDependentCell(pModule->GetName(), pMyFormulaCell);
+ }
+
+ double fVal;
+ SbxDataType eResType = refRes->GetType();
+ if( SbxBase::GetError() )
+ {
+ SetError( FormulaError::NoValue);
+ }
+ if ( eRet != ERRCODE_NONE )
+ {
+ PushNoValue();
+ }
+ else if (lcl_isNumericResult( fVal, refRes.get()))
+ {
+ switch (eResType)
+ {
+ case SbxDATE:
+ nFuncFmtType = SvNumFormatType::DATE;
+ break;
+ case SbxBOOL:
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ break;
+ // Do not add SbxCURRENCY, we don't know which currency.
+ default:
+ ; // nothing
+ }
+ PushDouble( fVal );
+ }
+ else if ( eResType & SbxARRAY )
+ {
+ SbxBase* pElemObj = refRes->GetObject();
+ SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>(pElemObj);
+ sal_Int32 nDim = pDimArray ? pDimArray->GetDims() : 0;
+ if ( 1 <= nDim && nDim <= 2 )
+ {
+ sal_Int32 nCs, nCe, nRs;
+ SCSIZE nC, nR;
+ SCCOL nColIdx;
+ SCROW nRowIdx;
+ if ( nDim == 1 )
+ { // array( cols ) one line, several columns
+ pDimArray->GetDim(1, nCs, nCe);
+ nC = static_cast<SCSIZE>(nCe - nCs + 1);
+ nRs = 0;
+ nR = 1;
+ nColIdx = 0;
+ nRowIdx = 1;
+ }
+ else
+ { // array( rows, cols )
+ sal_Int32 nRe;
+ pDimArray->GetDim(1, nRs, nRe);
+ nR = static_cast<SCSIZE>(nRe - nRs + 1);
+ pDimArray->GetDim(2, nCs, nCe);
+ nC = static_cast<SCSIZE>(nCe - nCs + 1);
+ nColIdx = 1;
+ nRowIdx = 0;
+ }
+ ScMatrixRef pMat = GetNewMat( nC, nR, /*bEmpty*/true);
+ if ( pMat )
+ {
+ SbxVariable* pV;
+ for ( SCSIZE j=0; j < nR; j++ )
+ {
+ sal_Int32 nIdx[ 2 ];
+ // in one-dimensional array( cols ) nIdx[1]
+ // from SbxDimArray::Get is ignored
+ nIdx[ nRowIdx ] = nRs + static_cast<sal_Int32>(j);
+ for ( SCSIZE i=0; i < nC; i++ )
+ {
+ nIdx[ nColIdx ] = nCs + static_cast<sal_Int32>(i);
+ pV = pDimArray->Get(nIdx);
+ if ( lcl_isNumericResult( fVal, pV) )
+ {
+ pMat->PutDouble( fVal, i, j );
+ }
+ else
+ {
+ pMat->PutString(mrStrPool.intern(pV->GetOUString()), i, j);
+ }
+ }
+ }
+ PushMatrix( pMat );
+ }
+ else
+ {
+ PushIllegalArgument();
+ }
+ }
+ else
+ {
+ PushNoValue();
+ }
+ }
+ else
+ {
+ PushString( refRes->GetOUString() );
+ }
+ }
+
+ if (bVolatileMacro && meVolatileType == NOT_VOLATILE)
+ meVolatileType = VOLATILE_MACRO;
+#endif
+}
+
+#if HAVE_FEATURE_SCRIPTING
+
+bool ScInterpreter::SetSbxVariable( SbxVariable* pVar, const ScAddress& rPos )
+{
+ bool bOk = true;
+ ScRefCellValue aCell(mrDoc, rPos);
+ if (!aCell.isEmpty())
+ {
+ FormulaError nErr;
+ double nVal;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE :
+ nVal = GetValueCellValue(rPos, aCell.getDouble());
+ pVar->PutDouble( nVal );
+ break;
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ pVar->PutString(aCell.getString(&mrDoc));
+ break;
+ case CELLTYPE_FORMULA :
+ nErr = aCell.getFormula()->GetErrCode();
+ if( nErr == FormulaError::NONE )
+ {
+ if (aCell.getFormula()->IsValue())
+ {
+ nVal = aCell.getFormula()->GetValue();
+ pVar->PutDouble( nVal );
+ }
+ else
+ pVar->PutString(aCell.getFormula()->GetString().getString());
+ }
+ else
+ {
+ SetError( nErr );
+ bOk = false;
+ }
+ break;
+ default :
+ pVar->PutEmpty();
+ }
+ }
+ else
+ pVar->PutEmpty();
+
+ return bOk;
+}
+
+#endif
+
+void ScInterpreter::ScTableOp()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (nParamCount != 3 && nParamCount != 5)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ ScInterpreterTableOpParams aTableOp;
+ if (nParamCount == 5)
+ {
+ PopSingleRef( aTableOp.aNew2 );
+ PopSingleRef( aTableOp.aOld2 );
+ }
+ PopSingleRef( aTableOp.aNew1 );
+ PopSingleRef( aTableOp.aOld1 );
+ PopSingleRef( aTableOp.aFormulaPos );
+
+ aTableOp.bValid = true;
+ mrDoc.m_TableOpList.push_back(&aTableOp);
+ mrDoc.IncInterpreterTableOpLevel();
+
+ bool bReuseLastParams = (mrDoc.aLastTableOpParams == aTableOp);
+ if ( bReuseLastParams )
+ {
+ aTableOp.aNotifiedFormulaPos = mrDoc.aLastTableOpParams.aNotifiedFormulaPos;
+ aTableOp.bRefresh = true;
+ for ( const auto& rPos : aTableOp.aNotifiedFormulaPos )
+ { // emulate broadcast and indirectly collect cell pointers
+ ScRefCellValue aCell(mrDoc, rPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ aCell.getFormula()->SetTableOpDirty();
+ }
+ }
+ else
+ { // broadcast and indirectly collect cell pointers and positions
+ mrDoc.SetTableOpDirty( aTableOp.aOld1 );
+ if ( nParamCount == 5 )
+ mrDoc.SetTableOpDirty( aTableOp.aOld2 );
+ }
+ aTableOp.bCollectNotifications = false;
+
+ ScRefCellValue aCell(mrDoc, aTableOp.aFormulaPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ aCell.getFormula()->SetDirtyVar();
+ if (aCell.hasNumeric())
+ {
+ PushDouble(GetCellValue(aTableOp.aFormulaPos, aCell));
+ }
+ else
+ {
+ svl::SharedString aCellString;
+ GetCellString(aCellString, aCell);
+ PushString( aCellString );
+ }
+
+ auto const itr =
+ ::std::find(mrDoc.m_TableOpList.begin(), mrDoc.m_TableOpList.end(), &aTableOp);
+ if (itr != mrDoc.m_TableOpList.end())
+ {
+ mrDoc.m_TableOpList.erase(itr);
+ }
+
+ // set dirty again once more to be able to recalculate original
+ for ( const auto& pCell : aTableOp.aNotifiedFormulaCells )
+ {
+ pCell->SetTableOpDirty();
+ }
+
+ // save these params for next incarnation
+ if ( !bReuseLastParams )
+ mrDoc.aLastTableOpParams = aTableOp;
+
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ aCell.getFormula()->SetDirtyVar();
+ aCell.getFormula()->GetErrCode(); // recalculate original
+ }
+
+ // Reset all dirty flags so next incarnation does really collect all cell
+ // pointers during notifications and not just non-dirty ones, which may
+ // happen if a formula cell is used by more than one TableOp block.
+ for ( const auto& pCell : aTableOp.aNotifiedFormulaCells )
+ {
+ pCell->ResetTableOpDirtyVar();
+ }
+
+ mrDoc.DecInterpreterTableOpLevel();
+}
+
+void ScInterpreter::ScDBArea()
+{
+ ScDBData* pDBData = mrDoc.GetDBCollection()->getNamedDBs().findByIndex(pCur->GetIndex());
+ if (pDBData)
+ {
+ ScComplexRefData aRefData;
+ aRefData.InitFlags();
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ aRange.aEnd.SetTab(aRange.aStart.Tab());
+ aRefData.SetRange(mrDoc.GetSheetLimits(), aRange, aPos);
+ PushTempToken( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRefData ) );
+ }
+ else
+ PushError( FormulaError::NoName);
+}
+
+void ScInterpreter::ScColRowNameAuto()
+{
+ ScComplexRefData aRefData( *pCur->GetDoubleRef() );
+ ScRange aAbs = aRefData.toAbs(mrDoc, aPos);
+ if (!mrDoc.ValidRange(aAbs))
+ {
+ PushError( FormulaError::NoRef );
+ return;
+ }
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+
+ // maybe remember limit by using defined ColRowNameRange
+ SCCOL nCol2 = aAbs.aEnd.Col();
+ SCROW nRow2 = aAbs.aEnd.Row();
+ // DataArea of the first cell
+ nStartCol = aAbs.aStart.Col();
+ nStartRow = aAbs.aStart.Row();
+ aAbs.aEnd = aAbs.aStart; // Shrink to the top-left cell.
+
+ {
+ // Expand to the data area. Only modify the end position.
+ SCCOL nDACol1 = aAbs.aStart.Col(), nDACol2 = aAbs.aEnd.Col();
+ SCROW nDARow1 = aAbs.aStart.Row(), nDARow2 = aAbs.aEnd.Row();
+ mrDoc.GetDataArea(aAbs.aStart.Tab(), nDACol1, nDARow1, nDACol2, nDARow2, true, false);
+ aAbs.aEnd.SetCol(nDACol2);
+ aAbs.aEnd.SetRow(nDARow2);
+ }
+
+ // corresponds with ScCompiler::GetToken
+ if ( aRefData.Ref1.IsColRel() )
+ { // ColName
+ aAbs.aEnd.SetCol(nStartCol);
+ // maybe get previous limit by using defined ColRowNameRange
+ if (aAbs.aEnd.Row() > nRow2)
+ aAbs.aEnd.SetRow(nRow2);
+ if ( aPos.Col() == nStartCol )
+ {
+ SCROW nMyRow = aPos.Row();
+ if ( nStartRow <= nMyRow && nMyRow <= aAbs.aEnd.Row())
+ { //Formula in the same column and within the range
+ if ( nMyRow == nStartRow )
+ { // take the rest under the name
+ nStartRow++;
+ if ( nStartRow > mrDoc.MaxRow() )
+ nStartRow = mrDoc.MaxRow();
+ aAbs.aStart.SetRow(nStartRow);
+ }
+ else
+ { // below the name to the formula cell
+ aAbs.aEnd.SetRow(nMyRow - 1);
+ }
+ }
+ }
+ }
+ else
+ { // RowName
+ aAbs.aEnd.SetRow(nStartRow);
+ // maybe get previous limit by using defined ColRowNameRange
+ if (aAbs.aEnd.Col() > nCol2)
+ aAbs.aEnd.SetCol(nCol2);
+ if ( aPos.Row() == nStartRow )
+ {
+ SCCOL nMyCol = aPos.Col();
+ if (nStartCol <= nMyCol && nMyCol <= aAbs.aEnd.Col())
+ { //Formula in the same column and within the range
+ if ( nMyCol == nStartCol )
+ { // take the rest under the name
+ nStartCol++;
+ if ( nStartCol > mrDoc.MaxCol() )
+ nStartCol = mrDoc.MaxCol();
+ aAbs.aStart.SetCol(nStartCol);
+ }
+ else
+ { // below the name to the formula cell
+ aAbs.aEnd.SetCol(nMyCol - 1);
+ }
+ }
+ }
+ }
+ aRefData.SetRange(mrDoc.GetSheetLimits(), aAbs, aPos);
+ PushTempToken( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRefData ) );
+}
+
+// --- internals ------------------------------------------------------------
+
+void ScInterpreter::ScTTT()
+{ // temporary test, testing functions etc.
+ sal_uInt8 nParamCount = GetByte();
+ // do something, count down nParamCount with Pops!
+
+ // clean up Stack
+ while ( nParamCount-- > 0)
+ Pop();
+ PushError(FormulaError::NoValue);
+}
+
+ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument& rDoc, ScInterpreterContext& rContext,
+ const ScAddress& rPos, ScTokenArray& r, bool bForGroupThreading )
+ : aCode(r)
+ , aPos(rPos)
+ , pArr(&r)
+ , mrContext(rContext)
+ , mrDoc(rDoc)
+ , mpLinkManager(rDoc.GetLinkManager())
+ , mrStrPool(rDoc.GetSharedStringPool())
+ , pJumpMatrix(nullptr)
+ , pMyFormulaCell(pCell)
+ , pFormatter(rContext.GetFormatTable())
+ , pCur(nullptr)
+ , nGlobalError(FormulaError::NONE)
+ , sp(0)
+ , maxsp(0)
+ , nFuncFmtIndex(0)
+ , nCurFmtIndex(0)
+ , nRetFmtIndex(0)
+ , nFuncFmtType(SvNumFormatType::ALL)
+ , nCurFmtType(SvNumFormatType::ALL)
+ , nRetFmtType(SvNumFormatType::ALL)
+ , mnStringNoValueError(FormulaError::NoValue)
+ , mnSubTotalFlags(SubtotalFlags::NONE)
+ , cPar(0)
+ , bCalcAsShown(rDoc.GetDocOptions().IsCalcAsShown())
+ , meVolatileType(r.IsRecalcModeAlways() ? VOLATILE : NOT_VOLATILE)
+{
+ MergeCalcConfig();
+
+ if(pMyFormulaCell)
+ {
+ ScMatrixMode cMatFlag = pMyFormulaCell->GetMatrixFlag();
+ bMatrixFormula = ( cMatFlag == ScMatrixMode::Formula );
+ }
+ else
+ bMatrixFormula = false;
+
+ // Lets not use the global stack while formula-group-threading.
+ // as it complicates its life-cycle mgmt since for threading formula-groups,
+ // ScInterpreter is preallocated (in main thread) for each worker thread.
+ if (!bGlobalStackInUse && !bForGroupThreading)
+ {
+ bGlobalStackInUse = true;
+ if (!pGlobalStack)
+ pGlobalStack.reset(new ScTokenStack);
+ pStackObj = pGlobalStack.get();
+ }
+ else
+ {
+ pStackObj = new ScTokenStack;
+ }
+ pStack = pStackObj->pPointer;
+}
+
+ScInterpreter::~ScInterpreter()
+{
+ if ( pStackObj == pGlobalStack.get() )
+ bGlobalStackInUse = false;
+ else
+ delete pStackObj;
+}
+
+void ScInterpreter::Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenArray& rTokArray )
+{
+ aCode.ReInit(rTokArray);
+ aPos = rPos;
+ pArr = &rTokArray;
+ xResult = nullptr;
+ pJumpMatrix = nullptr;
+ maTokenMatrixMap.clear();
+ pMyFormulaCell = pCell;
+ pCur = nullptr;
+ nGlobalError = FormulaError::NONE;
+ sp = 0;
+ maxsp = 0;
+ nFuncFmtIndex = 0;
+ nCurFmtIndex = 0;
+ nRetFmtIndex = 0;
+ nFuncFmtType = SvNumFormatType::ALL;
+ nCurFmtType = SvNumFormatType::ALL;
+ nRetFmtType = SvNumFormatType::ALL;
+ mnStringNoValueError = FormulaError::NoValue;
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ cPar = 0;
+}
+
+ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig()
+{
+ if (!mpGlobalConfig)
+ mpGlobalConfig = new ScCalcConfig();
+ return *mpGlobalConfig;
+}
+
+void ScInterpreter::SetGlobalConfig(const ScCalcConfig& rConfig)
+{
+ GetOrCreateGlobalConfig() = rConfig;
+}
+
+const ScCalcConfig& ScInterpreter::GetGlobalConfig()
+{
+ return GetOrCreateGlobalConfig();
+}
+
+void ScInterpreter::MergeCalcConfig()
+{
+ maCalcConfig = GetOrCreateGlobalConfig();
+ maCalcConfig.MergeDocumentSpecific( mrDoc.GetCalcConfig());
+}
+
+void ScInterpreter::GlobalExit()
+{
+ OSL_ENSURE(!bGlobalStackInUse, "who is still using the TokenStack?");
+ pGlobalStack.reset();
+}
+
+namespace {
+
+double applyImplicitIntersection(const sc::RangeMatrix& rMat, const ScAddress& rPos)
+{
+ if (rMat.mnRow1 <= rPos.Row() && rPos.Row() <= rMat.mnRow2 && rMat.mnCol1 == rMat.mnCol2)
+ {
+ SCROW nOffset = rPos.Row() - rMat.mnRow1;
+ return rMat.mpMat->GetDouble(0, nOffset);
+ }
+
+ if (rMat.mnCol1 <= rPos.Col() && rPos.Col() <= rMat.mnCol2 && rMat.mnRow1 == rMat.mnRow2)
+ {
+ SCROW nOffset = rPos.Col() - rMat.mnCol1;
+ return rMat.mpMat->GetDouble(nOffset, 0);
+ }
+
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+// Test for Functions that evaluate an error code and directly set nGlobalError to 0
+bool IsErrFunc(OpCode oc)
+{
+ switch (oc)
+ {
+ case ocCount :
+ case ocCount2 :
+ case ocErrorType :
+ case ocIsEmpty :
+ case ocIsErr :
+ case ocIsError :
+ case ocIsFormula :
+ case ocIsLogical :
+ case ocIsNA :
+ case ocIsNonString :
+ case ocIsRef :
+ case ocIsString :
+ case ocIsValue :
+ case ocN :
+ case ocType :
+ case ocIfError :
+ case ocIfNA :
+ case ocErrorType_ODF :
+ case ocAggregate: // may ignore errors depending on option
+ case ocIfs_MS:
+ case ocSwitch_MS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} //namespace
+
+StackVar ScInterpreter::Interpret()
+{
+ SvNumFormatType nRetTypeExpr = SvNumFormatType::UNDEFINED;
+ sal_uInt32 nRetIndexExpr = 0;
+ sal_uInt16 nErrorFunction = 0;
+ sal_uInt16 nErrorFunctionCount = 0;
+ std::vector<sal_uInt16> aErrorFunctionStack;
+ sal_uInt16 nStackBase;
+
+ nGlobalError = FormulaError::NONE;
+ nStackBase = sp = maxsp = 0;
+ nRetFmtType = SvNumFormatType::UNDEFINED;
+ nFuncFmtType = SvNumFormatType::UNDEFINED;
+ nFuncFmtIndex = nCurFmtIndex = nRetFmtIndex = 0;
+ xResult = nullptr;
+ pJumpMatrix = nullptr;
+ mnSubTotalFlags = SubtotalFlags::NONE;
+ ScTokenMatrixMap::const_iterator aTokenMatrixMapIter;
+
+ // Once upon a time we used to have FP exceptions on, and there was a
+ // Windows printer driver that kept switching off exceptions, so we had to
+ // switch them back on again every time. Who knows if there isn't a driver
+ // that keeps switching exceptions on, now that we run with exceptions off,
+ // so reassure exceptions are really off.
+ SAL_MATH_FPEXCEPTIONS_OFF();
+
+ OpCode eOp = ocNone;
+ aCode.Reset();
+ for (;;)
+ {
+ pCur = aCode.Next();
+ if (!pCur || (nGlobalError != FormulaError::NONE && nErrorFunction > nErrorFunctionCount) )
+ break;
+ eOp = pCur->GetOpCode();
+ cPar = pCur->GetByte();
+ if ( eOp == ocPush )
+ {
+ // RPN code push without error
+ PushWithoutError( *pCur );
+ nCurFmtType = SvNumFormatType::UNDEFINED;
+ }
+ else if (!FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
+ ((aTokenMatrixMapIter = maTokenMatrixMap.find( pCur)) !=
+ maTokenMatrixMap.end()) &&
+ (*aTokenMatrixMapIter).second->GetType() != svJumpMatrix)
+ {
+ // Path already calculated, reuse result.
+ if (sp >= pCur->GetParamCount())
+ nStackBase = sp - pCur->GetParamCount();
+ else
+ {
+ SAL_WARN("sc.core", "Stack anomaly with calculated path at "
+ << aPos.Tab() << "," << aPos.Col() << "," << aPos.Row()
+ << " " << aPos.Format(
+ ScRefFlags::VALID | ScRefFlags::FORCE_DOC | ScRefFlags::TAB_3D, &mrDoc)
+ << " eOp: " << static_cast<int>(eOp)
+ << " params: " << static_cast<int>(pCur->GetParamCount())
+ << " nStackBase: " << nStackBase << " sp: " << sp);
+ nStackBase = sp;
+ assert(!"underflow");
+ }
+ sp = nStackBase;
+ PushTokenRef( (*aTokenMatrixMapIter).second);
+ }
+ else
+ {
+ // previous expression determines the current number format
+ nCurFmtType = nRetTypeExpr;
+ nCurFmtIndex = nRetIndexExpr;
+ // default function's format, others are set if needed
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ nFuncFmtIndex = 0;
+
+ if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
+ nStackBase = sp; // don't mess around with the jumps
+ else
+ {
+ // Convert parameters to matrix if in array/matrix formula and
+ // parameters of function indicate doing so. Create JumpMatrix
+ // if necessary.
+ if ( MatrixParameterConversion() )
+ {
+ eOp = ocNone; // JumpMatrix created
+ nStackBase = sp;
+ }
+ else if (sp >= pCur->GetParamCount())
+ nStackBase = sp - pCur->GetParamCount();
+ else
+ {
+ SAL_WARN("sc.core", "Stack anomaly at " << aPos.Tab() << "," << aPos.Col() << "," << aPos.Row()
+ << " " << aPos.Format(
+ ScRefFlags::VALID | ScRefFlags::FORCE_DOC | ScRefFlags::TAB_3D, &mrDoc)
+ << " eOp: " << static_cast<int>(eOp)
+ << " params: " << static_cast<int>(pCur->GetParamCount())
+ << " nStackBase: " << nStackBase << " sp: " << sp);
+ nStackBase = sp;
+ assert(!"underflow");
+ }
+ }
+
+ switch( eOp )
+ {
+ case ocSep:
+ case ocClose: // pushed by the compiler
+ case ocMissing : ScMissing(); break;
+ case ocMacro : ScMacro(); break;
+ case ocDBArea : ScDBArea(); break;
+ case ocColRowNameAuto : ScColRowNameAuto(); break;
+ case ocIf : ScIfJump(); break;
+ case ocIfError : ScIfError( false ); break;
+ case ocIfNA : ScIfError( true ); break;
+ case ocChoose : ScChooseJump(); break;
+ case ocAdd : ScAdd(); break;
+ case ocSub : ScSub(); break;
+ case ocMul : ScMul(); break;
+ case ocDiv : ScDiv(); break;
+ case ocAmpersand : ScAmpersand(); break;
+ case ocPow : ScPow(); break;
+ case ocEqual : ScEqual(); break;
+ case ocNotEqual : ScNotEqual(); break;
+ case ocLess : ScLess(); break;
+ case ocGreater : ScGreater(); break;
+ case ocLessEqual : ScLessEqual(); break;
+ case ocGreaterEqual : ScGreaterEqual(); break;
+ case ocAnd : ScAnd(); break;
+ case ocOr : ScOr(); break;
+ case ocXor : ScXor(); break;
+ case ocIntersect : ScIntersect(); break;
+ case ocRange : ScRangeFunc(); break;
+ case ocUnion : ScUnionFunc(); break;
+ case ocNot : ScNot(); break;
+ case ocNegSub :
+ case ocNeg : ScNeg(); break;
+ case ocPercentSign : ScPercentSign(); break;
+ case ocPi : ScPi(); break;
+ case ocRandom : ScRandom(); break;
+ case ocRandomNV : ScRandom(); break;
+ case ocRandbetweenNV : ScRandbetween(); break;
+ case ocTrue : ScTrue(); break;
+ case ocFalse : ScFalse(); break;
+ case ocGetActDate : ScGetActDate(); break;
+ case ocGetActTime : ScGetActTime(); break;
+ case ocNotAvail : PushError( FormulaError::NotAvailable); break;
+ case ocDeg : ScDeg(); break;
+ case ocRad : ScRad(); break;
+ case ocSin : ScSin(); break;
+ case ocCos : ScCos(); break;
+ case ocTan : ScTan(); break;
+ case ocCot : ScCot(); break;
+ case ocArcSin : ScArcSin(); break;
+ case ocArcCos : ScArcCos(); break;
+ case ocArcTan : ScArcTan(); break;
+ case ocArcCot : ScArcCot(); break;
+ case ocSinHyp : ScSinHyp(); break;
+ case ocCosHyp : ScCosHyp(); break;
+ case ocTanHyp : ScTanHyp(); break;
+ case ocCotHyp : ScCotHyp(); break;
+ case ocArcSinHyp : ScArcSinHyp(); break;
+ case ocArcCosHyp : ScArcCosHyp(); break;
+ case ocArcTanHyp : ScArcTanHyp(); break;
+ case ocArcCotHyp : ScArcCotHyp(); break;
+ case ocCosecant : ScCosecant(); break;
+ case ocSecant : ScSecant(); break;
+ case ocCosecantHyp : ScCosecantHyp(); break;
+ case ocSecantHyp : ScSecantHyp(); break;
+ case ocExp : ScExp(); break;
+ case ocLn : ScLn(); break;
+ case ocLog10 : ScLog10(); break;
+ case ocSqrt : ScSqrt(); break;
+ case ocFact : ScFact(); break;
+ case ocGetYear : ScGetYear(); break;
+ case ocGetMonth : ScGetMonth(); break;
+ case ocGetDay : ScGetDay(); break;
+ case ocGetDayOfWeek : ScGetDayOfWeek(); break;
+ case ocWeek : ScGetWeekOfYear(); break;
+ case ocIsoWeeknum : ScGetIsoWeekOfYear(); break;
+ case ocWeeknumOOo : ScWeeknumOOo(); break;
+ case ocEasterSunday : ScEasterSunday(); break;
+ case ocNetWorkdays : ScNetWorkdays( false); break;
+ case ocNetWorkdays_MS : ScNetWorkdays( true ); break;
+ case ocWorkday_MS : ScWorkday_MS(); break;
+ case ocGetHour : ScGetHour(); break;
+ case ocGetMin : ScGetMin(); break;
+ case ocGetSec : ScGetSec(); break;
+ case ocPlusMinus : ScPlusMinus(); break;
+ case ocAbs : ScAbs(); break;
+ case ocInt : ScInt(); break;
+ case ocEven : ScEven(); break;
+ case ocOdd : ScOdd(); break;
+ case ocPhi : ScPhi(); break;
+ case ocGauss : ScGauss(); break;
+ case ocStdNormDist : ScStdNormDist(); break;
+ case ocStdNormDist_MS : ScStdNormDist_MS(); break;
+ case ocFisher : ScFisher(); break;
+ case ocFisherInv : ScFisherInv(); break;
+ case ocIsEmpty : ScIsEmpty(); break;
+ case ocIsString : ScIsString(); break;
+ case ocIsNonString : ScIsNonString(); break;
+ case ocIsLogical : ScIsLogical(); break;
+ case ocType : ScType(); break;
+ case ocCell : ScCell(); break;
+ case ocIsRef : ScIsRef(); break;
+ case ocIsValue : ScIsValue(); break;
+ case ocIsFormula : ScIsFormula(); break;
+ case ocFormula : ScFormula(); break;
+ case ocIsNA : ScIsNV(); break;
+ case ocIsErr : ScIsErr(); break;
+ case ocIsError : ScIsError(); break;
+ case ocIsEven : ScIsEven(); break;
+ case ocIsOdd : ScIsOdd(); break;
+ case ocN : ScN(); break;
+ case ocGetDateValue : ScGetDateValue(); break;
+ case ocGetTimeValue : ScGetTimeValue(); break;
+ case ocCode : ScCode(); break;
+ case ocTrim : ScTrim(); break;
+ case ocUpper : ScUpper(); break;
+ case ocProper : ScProper(); break;
+ case ocLower : ScLower(); break;
+ case ocLen : ScLen(); break;
+ case ocT : ScT(); break;
+ case ocClean : ScClean(); break;
+ case ocValue : ScValue(); break;
+ case ocNumberValue : ScNumberValue(); break;
+ case ocChar : ScChar(); break;
+ case ocArcTan2 : ScArcTan2(); break;
+ case ocMod : ScMod(); break;
+ case ocPower : ScPower(); break;
+ case ocRound : ScRound(); break;
+ case ocRoundSig : ScRoundSignificant(); break;
+ case ocRoundUp : ScRoundUp(); break;
+ case ocTrunc :
+ case ocRoundDown : ScRoundDown(); break;
+ case ocCeil : ScCeil( true ); break;
+ case ocCeil_MS : ScCeil_MS(); break;
+ case ocCeil_Precise :
+ case ocCeil_ISO : ScCeil_Precise(); break;
+ case ocCeil_Math : ScCeil( false ); break;
+ case ocFloor : ScFloor( true ); break;
+ case ocFloor_MS : ScFloor_MS(); break;
+ case ocFloor_Precise : ScFloor_Precise(); break;
+ case ocFloor_Math : ScFloor( false ); break;
+ case ocSumProduct : ScSumProduct(); break;
+ case ocSumSQ : ScSumSQ(); break;
+ case ocSumX2MY2 : ScSumX2MY2(); break;
+ case ocSumX2DY2 : ScSumX2DY2(); break;
+ case ocSumXMY2 : ScSumXMY2(); break;
+ case ocRawSubtract : ScRawSubtract(); break;
+ case ocLog : ScLog(); break;
+ case ocGCD : ScGCD(); break;
+ case ocLCM : ScLCM(); break;
+ case ocGetDate : ScGetDate(); break;
+ case ocGetTime : ScGetTime(); break;
+ case ocGetDiffDate : ScGetDiffDate(); break;
+ case ocGetDiffDate360 : ScGetDiffDate360(); break;
+ case ocGetDateDif : ScGetDateDif(); break;
+ case ocMin : ScMin() ; break;
+ case ocMinA : ScMin( true ); break;
+ case ocMax : ScMax(); break;
+ case ocMaxA : ScMax( true ); break;
+ case ocSum : ScSum(); break;
+ case ocProduct : ScProduct(); break;
+ case ocNPV : ScNPV(); break;
+ case ocIRR : ScIRR(); break;
+ case ocMIRR : ScMIRR(); break;
+ case ocISPMT : ScISPMT(); break;
+ case ocAverage : ScAverage() ; break;
+ case ocAverageA : ScAverage( true ); break;
+ case ocCount : ScCount(); break;
+ case ocCount2 : ScCount2(); break;
+ case ocVar :
+ case ocVarS : ScVar(); break;
+ case ocVarA : ScVar( true ); break;
+ case ocVarP :
+ case ocVarP_MS : ScVarP(); break;
+ case ocVarPA : ScVarP( true ); break;
+ case ocStDev :
+ case ocStDevS : ScStDev(); break;
+ case ocStDevA : ScStDev( true ); break;
+ case ocStDevP :
+ case ocStDevP_MS : ScStDevP(); break;
+ case ocStDevPA : ScStDevP( true ); break;
+ case ocPV : ScPV(); break;
+ case ocSYD : ScSYD(); break;
+ case ocDDB : ScDDB(); break;
+ case ocDB : ScDB(); break;
+ case ocVBD : ScVDB(); break;
+ case ocPDuration : ScPDuration(); break;
+ case ocSLN : ScSLN(); break;
+ case ocPMT : ScPMT(); break;
+ case ocColumns : ScColumns(); break;
+ case ocRows : ScRows(); break;
+ case ocSheets : ScSheets(); break;
+ case ocColumn : ScColumn(); break;
+ case ocRow : ScRow(); break;
+ case ocSheet : ScSheet(); break;
+ case ocRRI : ScRRI(); break;
+ case ocFV : ScFV(); break;
+ case ocNper : ScNper(); break;
+ case ocRate : ScRate(); break;
+ case ocFilterXML : ScFilterXML(); break;
+ case ocWebservice : ScWebservice(); break;
+ case ocEncodeURL : ScEncodeURL(); break;
+ case ocColor : ScColor(); break;
+ case ocErf_MS : ScErf(); break;
+ case ocErfc_MS : ScErfc(); break;
+ case ocIpmt : ScIpmt(); break;
+ case ocPpmt : ScPpmt(); break;
+ case ocCumIpmt : ScCumIpmt(); break;
+ case ocCumPrinc : ScCumPrinc(); break;
+ case ocEffect : ScEffect(); break;
+ case ocNominal : ScNominal(); break;
+ case ocSubTotal : ScSubTotal(); break;
+ case ocAggregate : ScAggregate(); break;
+ case ocDBSum : ScDBSum(); break;
+ case ocDBCount : ScDBCount(); break;
+ case ocDBCount2 : ScDBCount2(); break;
+ case ocDBAverage : ScDBAverage(); break;
+ case ocDBGet : ScDBGet(); break;
+ case ocDBMax : ScDBMax(); break;
+ case ocDBMin : ScDBMin(); break;
+ case ocDBProduct : ScDBProduct(); break;
+ case ocDBStdDev : ScDBStdDev(); break;
+ case ocDBStdDevP : ScDBStdDevP(); break;
+ case ocDBVar : ScDBVar(); break;
+ case ocDBVarP : ScDBVarP(); break;
+ case ocIndirect : ScIndirect(); break;
+ case ocAddress : ScAddressFunc(); break;
+ case ocMatch : ScMatch(); break;
+ case ocCountEmptyCells : ScCountEmptyCells(); break;
+ case ocCountIf : ScCountIf(); break;
+ case ocSumIf : ScSumIf(); break;
+ case ocAverageIf : ScAverageIf(); break;
+ case ocSumIfs : ScSumIfs(); break;
+ case ocAverageIfs : ScAverageIfs(); break;
+ case ocCountIfs : ScCountIfs(); break;
+ case ocLookup : ScLookup(); break;
+ case ocVLookup : ScVLookup(); break;
+ case ocHLookup : ScHLookup(); break;
+ case ocIndex : ScIndex(); break;
+ case ocMultiArea : ScMultiArea(); break;
+ case ocOffset : ScOffset(); break;
+ case ocAreas : ScAreas(); break;
+ case ocCurrency : ScCurrency(); break;
+ case ocReplace : ScReplace(); break;
+ case ocFixed : ScFixed(); break;
+ case ocFind : ScFind(); break;
+ case ocExact : ScExact(); break;
+ case ocLeft : ScLeft(); break;
+ case ocRight : ScRight(); break;
+ case ocSearch : ScSearch(); break;
+ case ocMid : ScMid(); break;
+ case ocText : ScText(); break;
+ case ocSubstitute : ScSubstitute(); break;
+ case ocRegex : ScRegex(); break;
+ case ocRept : ScRept(); break;
+ case ocConcat : ScConcat(); break;
+ case ocConcat_MS : ScConcat_MS(); break;
+ case ocTextJoin_MS : ScTextJoin_MS(); break;
+ case ocIfs_MS : ScIfs_MS(); break;
+ case ocSwitch_MS : ScSwitch_MS(); break;
+ case ocMinIfs_MS : ScMinIfs_MS(); break;
+ case ocMaxIfs_MS : ScMaxIfs_MS(); break;
+ case ocMatValue : ScMatValue(); break;
+ case ocMatrixUnit : ScEMat(); break;
+ case ocMatDet : ScMatDet(); break;
+ case ocMatInv : ScMatInv(); break;
+ case ocMatMult : ScMatMult(); break;
+ case ocMatTrans : ScMatTrans(); break;
+ case ocMatRef : ScMatRef(); break;
+ case ocB : ScB(); break;
+ case ocNormDist : ScNormDist( 3 ); break;
+ case ocNormDist_MS : ScNormDist( 4 ); break;
+ case ocExpDist :
+ case ocExpDist_MS : ScExpDist(); break;
+ case ocBinomDist :
+ case ocBinomDist_MS : ScBinomDist(); break;
+ case ocPoissonDist : ScPoissonDist( true ); break;
+ case ocPoissonDist_MS : ScPoissonDist( false ); break;
+ case ocCombin : ScCombin(); break;
+ case ocCombinA : ScCombinA(); break;
+ case ocPermut : ScPermut(); break;
+ case ocPermutationA : ScPermutationA(); break;
+ case ocHypGeomDist : ScHypGeomDist( 4 ); break;
+ case ocHypGeomDist_MS : ScHypGeomDist( 5 ); break;
+ case ocLogNormDist : ScLogNormDist( 1 ); break;
+ case ocLogNormDist_MS : ScLogNormDist( 4 ); break;
+ case ocTDist : ScTDist(); break;
+ case ocTDist_MS : ScTDist_MS(); break;
+ case ocTDist_RT : ScTDist_T( 1 ); break;
+ case ocTDist_2T : ScTDist_T( 2 ); break;
+ case ocFDist :
+ case ocFDist_RT : ScFDist(); break;
+ case ocFDist_LT : ScFDist_LT(); break;
+ case ocChiDist : ScChiDist( true ); break;
+ case ocChiDist_MS : ScChiDist( false ); break;
+ case ocChiSqDist : ScChiSqDist(); break;
+ case ocChiSqDist_MS : ScChiSqDist_MS(); break;
+ case ocStandard : ScStandard(); break;
+ case ocAveDev : ScAveDev(); break;
+ case ocDevSq : ScDevSq(); break;
+ case ocKurt : ScKurt(); break;
+ case ocSkew : ScSkew(); break;
+ case ocSkewp : ScSkewp(); break;
+ case ocModalValue : ScModalValue(); break;
+ case ocModalValue_MS : ScModalValue_MS( true ); break;
+ case ocModalValue_Multi : ScModalValue_MS( false ); break;
+ case ocMedian : ScMedian(); break;
+ case ocGeoMean : ScGeoMean(); break;
+ case ocHarMean : ScHarMean(); break;
+ case ocWeibull :
+ case ocWeibull_MS : ScWeibull(); break;
+ case ocBinomInv :
+ case ocCritBinom : ScCritBinom(); break;
+ case ocNegBinomVert : ScNegBinomDist(); break;
+ case ocNegBinomDist_MS : ScNegBinomDist_MS(); break;
+ case ocNoName : ScNoName(); break;
+ case ocBad : ScBadName(); break;
+ case ocZTest :
+ case ocZTest_MS : ScZTest(); break;
+ case ocTTest :
+ case ocTTest_MS : ScTTest(); break;
+ case ocFTest :
+ case ocFTest_MS : ScFTest(); break;
+ case ocRank :
+ case ocRank_Eq : ScRank( false ); break;
+ case ocRank_Avg : ScRank( true ); break;
+ case ocPercentile :
+ case ocPercentile_Inc : ScPercentile( true ); break;
+ case ocPercentile_Exc : ScPercentile( false ); break;
+ case ocPercentrank :
+ case ocPercentrank_Inc : ScPercentrank( true ); break;
+ case ocPercentrank_Exc : ScPercentrank( false ); break;
+ case ocLarge : ScLarge(); break;
+ case ocSmall : ScSmall(); break;
+ case ocFrequency : ScFrequency(); break;
+ case ocQuartile :
+ case ocQuartile_Inc : ScQuartile( true ); break;
+ case ocQuartile_Exc : ScQuartile( false ); break;
+ case ocNormInv :
+ case ocNormInv_MS : ScNormInv(); break;
+ case ocSNormInv :
+ case ocSNormInv_MS : ScSNormInv(); break;
+ case ocConfidence :
+ case ocConfidence_N : ScConfidence(); break;
+ case ocConfidence_T : ScConfidenceT(); break;
+ case ocTrimMean : ScTrimMean(); break;
+ case ocProb : ScProbability(); break;
+ case ocCorrel : ScCorrel(); break;
+ case ocCovar :
+ case ocCovarianceP : ScCovarianceP(); break;
+ case ocCovarianceS : ScCovarianceS(); break;
+ case ocPearson : ScPearson(); break;
+ case ocRSQ : ScRSQ(); break;
+ case ocSTEYX : ScSTEYX(); break;
+ case ocSlope : ScSlope(); break;
+ case ocIntercept : ScIntercept(); break;
+ case ocTrend : ScTrend(); break;
+ case ocGrowth : ScGrowth(); break;
+ case ocLinest : ScLinest(); break;
+ case ocLogest : ScLogest(); break;
+ case ocForecast_LIN :
+ case ocForecast : ScForecast(); break;
+ case ocForecast_ETS_ADD : ScForecast_Ets( etsAdd ); break;
+ case ocForecast_ETS_SEA : ScForecast_Ets( etsSeason ); break;
+ case ocForecast_ETS_MUL : ScForecast_Ets( etsMult ); break;
+ case ocForecast_ETS_PIA : ScForecast_Ets( etsPIAdd ); break;
+ case ocForecast_ETS_PIM : ScForecast_Ets( etsPIMult ); break;
+ case ocForecast_ETS_STA : ScForecast_Ets( etsStatAdd ); break;
+ case ocForecast_ETS_STM : ScForecast_Ets( etsStatMult ); break;
+ case ocGammaLn :
+ case ocGammaLn_MS : ScLogGamma(); break;
+ case ocGamma : ScGamma(); break;
+ case ocGammaDist : ScGammaDist( true ); break;
+ case ocGammaDist_MS : ScGammaDist( false ); break;
+ case ocGammaInv :
+ case ocGammaInv_MS : ScGammaInv(); break;
+ case ocChiTest :
+ case ocChiTest_MS : ScChiTest(); break;
+ case ocChiInv :
+ case ocChiInv_MS : ScChiInv(); break;
+ case ocChiSqInv :
+ case ocChiSqInv_MS : ScChiSqInv(); break;
+ case ocTInv :
+ case ocTInv_2T : ScTInv( 2 ); break;
+ case ocTInv_MS : ScTInv( 4 ); break;
+ case ocFInv :
+ case ocFInv_RT : ScFInv(); break;
+ case ocFInv_LT : ScFInv_LT(); break;
+ case ocLogInv :
+ case ocLogInv_MS : ScLogNormInv(); break;
+ case ocBetaDist : ScBetaDist(); break;
+ case ocBetaDist_MS : ScBetaDist_MS(); break;
+ case ocBetaInv :
+ case ocBetaInv_MS : ScBetaInv(); break;
+ case ocFourier : ScFourier(); break;
+ case ocExternal : ScExternal(); break;
+ case ocTableOp : ScTableOp(); break;
+ case ocStop : break;
+ case ocErrorType : ScErrorType(); break;
+ case ocErrorType_ODF : ScErrorType_ODF(); break;
+ case ocCurrent : ScCurrent(); break;
+ case ocStyle : ScStyle(); break;
+ case ocDde : ScDde(); break;
+ case ocBase : ScBase(); break;
+ case ocDecimal : ScDecimal(); break;
+ case ocConvertOOo : ScConvertOOo(); break;
+ case ocEuroConvert : ScEuroConvert(); break;
+ case ocRoman : ScRoman(); break;
+ case ocArabic : ScArabic(); break;
+ case ocInfo : ScInfo(); break;
+ case ocHyperLink : ScHyperLink(); break;
+ case ocBahtText : ScBahtText(); break;
+ case ocGetPivotData : ScGetPivotData(); break;
+ case ocJis : ScJis(); break;
+ case ocAsc : ScAsc(); break;
+ case ocLenB : ScLenB(); break;
+ case ocRightB : ScRightB(); break;
+ case ocLeftB : ScLeftB(); break;
+ case ocMidB : ScMidB(); break;
+ case ocReplaceB : ScReplaceB(); break;
+ case ocFindB : ScFindB(); break;
+ case ocSearchB : ScSearchB(); break;
+ case ocUnicode : ScUnicode(); break;
+ case ocUnichar : ScUnichar(); break;
+ case ocBitAnd : ScBitAnd(); break;
+ case ocBitOr : ScBitOr(); break;
+ case ocBitXor : ScBitXor(); break;
+ case ocBitRshift : ScBitRshift(); break;
+ case ocBitLshift : ScBitLshift(); break;
+ case ocTTT : ScTTT(); break;
+ case ocDebugVar : ScDebugVar(); break;
+ case ocNone : nFuncFmtType = SvNumFormatType::UNDEFINED; break;
+ default : PushError( FormulaError::UnknownOpCode); break;
+ }
+
+ // If the function pushed a subroutine as result, continue with
+ // execution of the subroutine.
+ if (sp > nStackBase && pStack[sp-1]->GetOpCode() == ocCall)
+ {
+ Pop(); continue;
+ }
+
+ if (FormulaCompiler::IsOpCodeVolatile(eOp))
+ meVolatileType = VOLATILE;
+
+ // Remember result matrix in case it could be reused.
+ if (sp && GetStackType() == svMatrix)
+ maTokenMatrixMap.emplace(pCur, pStack[sp-1]);
+
+ // outer function determines format of an expression
+ if ( nFuncFmtType != SvNumFormatType::UNDEFINED )
+ {
+ nRetTypeExpr = nFuncFmtType;
+ // Inherit the format index for currency, date or time formats.
+ switch (nFuncFmtType)
+ {
+ case SvNumFormatType::CURRENCY:
+ case SvNumFormatType::DATE:
+ case SvNumFormatType::TIME:
+ case SvNumFormatType::DATETIME:
+ case SvNumFormatType::DURATION:
+ nRetIndexExpr = nFuncFmtIndex;
+ break;
+ default:
+ nRetIndexExpr = 0;
+ }
+ }
+ }
+
+ // Need a clean stack environment for the JumpMatrix to work.
+ if (nGlobalError != FormulaError::NONE && eOp != ocPush && sp > nStackBase + 1)
+ {
+ // Not all functions pop all parameters in case an error is
+ // generated. Clean up stack. Assumes that every function pushes a
+ // result, may be arbitrary in case of error.
+ FormulaConstTokenRef xLocalResult = pStack[ sp - 1 ];
+ while (sp > nStackBase)
+ Pop();
+ PushTokenRef( xLocalResult );
+ }
+
+ bool bGotResult;
+ do
+ {
+ bGotResult = false;
+ sal_uInt8 nLevel = 0;
+ if ( GetStackType( ++nLevel ) == svJumpMatrix )
+ ; // nothing
+ else if ( GetStackType( ++nLevel ) == svJumpMatrix )
+ ; // nothing
+ else
+ nLevel = 0;
+ if ( nLevel == 1 || (nLevel == 2 && aCode.IsEndOfPath()) )
+ {
+ if (nLevel == 1)
+ aErrorFunctionStack.push_back( nErrorFunction);
+ bGotResult = JumpMatrix( nLevel );
+ if (aErrorFunctionStack.empty())
+ assert(!"ScInterpreter::Interpret - aErrorFunctionStack empty in JumpMatrix context");
+ else
+ {
+ nErrorFunction = aErrorFunctionStack.back();
+ if (bGotResult)
+ aErrorFunctionStack.pop_back();
+ }
+ }
+ else
+ pJumpMatrix = nullptr;
+ } while ( bGotResult );
+
+ if( IsErrFunc(eOp) )
+ ++nErrorFunction;
+
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ if ( !nErrorFunctionCount )
+ { // count of errorcode functions in formula
+ FormulaTokenArrayPlainIterator aIter(*pArr);
+ for ( FormulaToken* t = aIter.FirstRPN(); t; t = aIter.NextRPN() )
+ {
+ if ( IsErrFunc(t->GetOpCode()) )
+ ++nErrorFunctionCount;
+ }
+ }
+ if ( nErrorFunction >= nErrorFunctionCount )
+ ++nErrorFunction; // that's it, error => terminate
+ else if (nErrorFunctionCount && sp && GetStackType() == svError)
+ {
+ // Clear global error if we have an individual error result, so
+ // an error evaluating function can receive multiple arguments
+ // and not all evaluated arguments inheriting the error.
+ // This is important for at least IFS() and SWITCH() as long as
+ // they are classified as error evaluating functions and not
+ // implemented as short-cutting jump code paths, but also for
+ // more than one evaluated argument to AGGREGATE() or COUNT()
+ // that may ignore errors.
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ }
+
+ // End: obtain result
+
+ bool bForcedResultType;
+ switch (eOp)
+ {
+ case ocGetDateValue:
+ case ocGetTimeValue:
+ // Force final result of DATEVALUE and TIMEVALUE to number type,
+ // which so far was date or time for calculations.
+ nRetTypeExpr = nFuncFmtType = SvNumFormatType::NUMBER;
+ nRetIndexExpr = nFuncFmtIndex = 0;
+ bForcedResultType = true;
+ break;
+ default:
+ bForcedResultType = false;
+ }
+
+ if (sp == 1)
+ {
+ pCur = pStack[ sp-1 ];
+ if( pCur->GetOpCode() == ocPush )
+ {
+ // An svRefList can be resolved if it a) contains just one
+ // reference, or b) in array context contains an array of single
+ // cell references.
+ if (pCur->GetType() == svRefList)
+ {
+ PopRefListPushMatrixOrRef();
+ pCur = pStack[ sp-1 ];
+ }
+ switch( pCur->GetType() )
+ {
+ case svEmptyCell:
+ ; // nothing
+ break;
+ case svError:
+ nGlobalError = pCur->GetError();
+ break;
+ case svDouble :
+ {
+ // If typed, pop token to obtain type information and
+ // push a plain untyped double so the result token to
+ // be transferred to the formula cell result does not
+ // unnecessarily duplicate the information.
+ if (pCur->GetDoubleType() != 0)
+ {
+ double fVal = PopDouble();
+ if (!bForcedResultType)
+ {
+ if (nCurFmtType != nFuncFmtType)
+ nRetIndexExpr = 0; // carry format index only for matching type
+ nRetTypeExpr = nFuncFmtType = nCurFmtType;
+ }
+ if (nRetTypeExpr == SvNumFormatType::DURATION)
+ {
+ // Round the duration in case a wall clock time
+ // display format is used instead of a duration
+ // format. To micro seconds which then catches
+ // the converted hh:mm:ss.9999997 cases.
+ if (fVal != 0.0)
+ {
+ fVal *= 86400.0;
+ fVal = rtl::math::round( fVal, 6);
+ fVal /= 86400.0;
+ }
+ }
+ PushTempToken( CreateFormulaDoubleToken( fVal));
+ }
+ if ( nFuncFmtType == SvNumFormatType::UNDEFINED )
+ {
+ nRetTypeExpr = SvNumFormatType::NUMBER;
+ nRetIndexExpr = 0;
+ }
+ }
+ break;
+ case svString :
+ nRetTypeExpr = SvNumFormatType::TEXT;
+ nRetIndexExpr = 0;
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if( nGlobalError == FormulaError::NONE)
+ PushCellResultToken( false, aAdr, &nRetTypeExpr, &nRetIndexExpr, true);
+ }
+ break;
+ case svRefList :
+ PopError(); // maybe #REF! takes precedence over #VALUE!
+ PushError( FormulaError::NoValue);
+ break;
+ case svDoubleRef :
+ {
+ if ( bMatrixFormula )
+ { // create matrix for {=A1:A5}
+ PopDoubleRefPushMatrix();
+ ScMatrixRef xMat = PopMatrix();
+ QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr);
+ }
+ else
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange );
+ ScAddress aAdr;
+ if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr))
+ PushCellResultToken( false, aAdr, &nRetTypeExpr, &nRetIndexExpr, true);
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef xMat;
+ PopExternalDoubleRef(xMat);
+ QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr);
+ }
+ break;
+ case svMatrix :
+ {
+ sc::RangeMatrix aMat = PopRangeMatrix();
+ if (aMat.isRangeValid())
+ {
+ // This matrix represents a range reference. Apply implicit intersection.
+ double fVal = applyImplicitIntersection(aMat, aPos);
+ if (std::isnan(fVal))
+ PushNoValue();
+ else
+ PushInt(fVal);
+ }
+ else
+ // This is a normal matrix.
+ QueryMatrixType(aMat.mpMat, nRetTypeExpr, nRetIndexExpr);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ FormulaTokenRef xToken;
+ ScExternalRefCache::CellFormat aFmt;
+ PopExternalSingleRef(xToken, &aFmt);
+ if (nGlobalError != FormulaError::NONE)
+ break;
+
+ PushTokenRef(xToken);
+
+ if (aFmt.mbIsSet)
+ {
+ nFuncFmtType = aFmt.mnType;
+ nFuncFmtIndex = aFmt.mnIndex;
+ }
+ }
+ break;
+ default :
+ SetError( FormulaError::UnknownStackVariable);
+ }
+ }
+ else
+ SetError( FormulaError::UnknownStackVariable);
+ }
+ else if (sp > 1)
+ SetError( FormulaError::OperatorExpected);
+ else
+ SetError( FormulaError::NoCode);
+
+ if (bForcedResultType || nRetTypeExpr != SvNumFormatType::UNDEFINED)
+ {
+ nRetFmtType = nRetTypeExpr;
+ nRetFmtIndex = nRetIndexExpr;
+ }
+ else if( nFuncFmtType != SvNumFormatType::UNDEFINED )
+ {
+ nRetFmtType = nFuncFmtType;
+ nRetFmtIndex = nFuncFmtIndex;
+ }
+ else
+ nRetFmtType = SvNumFormatType::NUMBER;
+
+ if (nGlobalError != FormulaError::NONE && GetStackType() != svError )
+ PushError( nGlobalError);
+
+ // THE final result.
+ xResult = PopToken();
+ if (!xResult)
+ xResult = new FormulaErrorToken( FormulaError::UnknownStackVariable);
+
+ // release tokens in expression stack
+ const FormulaToken** p = pStack;
+ while( maxsp-- )
+ (*p++)->DecRef();
+
+ StackVar eType = xResult->GetType();
+ if (eType == svMatrix)
+ // Results are immutable in case they would be reused as input for new
+ // interpreters.
+ xResult->GetMatrix()->SetImmutable();
+ return eType;
+}
+
+void ScInterpreter::AssertFormulaMatrix()
+{
+ bMatrixFormula = true;
+}
+
+const svl::SharedString & ScInterpreter::GetStringResult() const
+{
+ return xResult->GetString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr5.cxx b/sc/source/core/tool/interpr5.cxx
new file mode 100644
index 0000000000..ae499ec492
--- /dev/null
+++ b/sc/source/core/tool/interpr5.cxx
@@ -0,0 +1,3303 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <string.h>
+#include <math.h>
+
+#ifdef DEBUG_SC_LUP_DECOMPOSITION
+#include <stdio.h>
+#endif
+
+#include <unotools/bootstrap.hxx>
+#include <svl/zforlist.hxx>
+#include <tools/duration.hxx>
+
+#include <interpre.hxx>
+#include <global.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <scmatrix.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <cellkeytranslator.hxx>
+#include <formulagroup.hxx>
+#include <vcl/svapp.hxx> //Application::
+
+#include <vector>
+
+using ::std::vector;
+using namespace formula;
+
+namespace {
+
+double MatrixAdd(const double& lhs, const double& rhs)
+{
+ return ::rtl::math::approxAdd( lhs,rhs);
+}
+
+double MatrixSub(const double& lhs, const double& rhs)
+{
+ return ::rtl::math::approxSub( lhs,rhs);
+}
+
+double MatrixMul(const double& lhs, const double& rhs)
+{
+ return lhs * rhs;
+}
+
+double MatrixDiv(const double& lhs, const double& rhs)
+{
+ return ScInterpreter::div( lhs,rhs);
+}
+
+double MatrixPow(const double& lhs, const double& rhs)
+{
+ return ::pow( lhs,rhs);
+}
+
+// Multiply n x m Mat A with m x l Mat B to n x l Mat R
+void lcl_MFastMult(const ScMatrixRef& pA, const ScMatrixRef& pB, const ScMatrixRef& pR,
+ SCSIZE n, SCSIZE m, SCSIZE l)
+{
+ for (SCSIZE row = 0; row < n; row++)
+ {
+ for (SCSIZE col = 0; col < l; col++)
+ { // result element(col, row) =sum[ (row of A) * (column of B)]
+ KahanSum fSum = 0.0;
+ for (SCSIZE k = 0; k < m; k++)
+ fSum += pA->GetDouble(k,row) * pB->GetDouble(col,k);
+ pR->PutDouble(fSum.get(), col, row);
+ }
+ }
+}
+
+}
+
+double ScInterpreter::ScGetGCD(double fx, double fy)
+{
+ // By ODFF definition GCD(0,a) => a. This is also vital for the code in
+ // ScGCD() to work correctly with a preset fy=0.0
+ if (fy == 0.0)
+ return fx;
+ else if (fx == 0.0)
+ return fy;
+ else
+ {
+ double fz = fmod(fx, fy);
+ while (fz > 0.0)
+ {
+ fx = fy;
+ fy = fz;
+ fz = fmod(fx, fy);
+ }
+ return fy;
+ }
+}
+
+void ScInterpreter::ScGCD()
+{
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+
+ double fx, fy = 0.0;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ case svString:
+ case svSingleRef:
+ {
+ fx = ::rtl::math::approxFloor( GetDouble());
+ if (fx < 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ fy = ScGetGCD(fx, fy);
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ double nCellVal;
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(nCellVal, nErr))
+ {
+ do
+ {
+ fx = ::rtl::math::approxFloor( nCellVal);
+ if (fx < 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ fy = ScGetGCD(fx, fy);
+ } while (nErr == FormulaError::NONE && aValIter.GetNext(nCellVal, nErr));
+ }
+ SetError(nErr);
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ SetError(FormulaError::IllegalArgument);
+ else
+ {
+ double nVal = pMat->GetGcd();
+ fy = ScGetGCD(nVal,fy);
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ }
+ PushDouble(fy);
+}
+
+void ScInterpreter:: ScLCM()
+{
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1 ) )
+ return;
+
+ double fx, fy = 1.0;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svDouble :
+ case svString:
+ case svSingleRef:
+ {
+ fx = ::rtl::math::approxFloor( GetDouble());
+ if (fx < 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if (fx == 0.0 || fy == 0.0)
+ fy = 0.0;
+ else
+ fy = fx * fy / ScGetGCD(fx, fy);
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ FormulaError nErr = FormulaError::NONE;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ double nCellVal;
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags );
+ if (aValIter.GetFirst(nCellVal, nErr))
+ {
+ do
+ {
+ fx = ::rtl::math::approxFloor( nCellVal);
+ if (fx < 0.0)
+ {
+ PushIllegalArgument();
+ return;
+ }
+ if (fx == 0.0 || fy == 0.0)
+ fy = 0.0;
+ else
+ fy = fx * fy / ScGetGCD(fx, fy);
+ } while (nErr == FormulaError::NONE && aValIter.GetNext(nCellVal, nErr));
+ }
+ SetError(nErr);
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ SetError(FormulaError::IllegalArgument);
+ else
+ {
+ double nVal = pMat->GetLcm();
+ fy = (nVal * fy ) / ScGetGCD(nVal, fy);
+ }
+ }
+ }
+ break;
+ default : SetError(FormulaError::IllegalParameter); break;
+ }
+ }
+ PushDouble(fy);
+}
+
+void ScInterpreter::MakeMatNew(ScMatrixRef& rMat, SCSIZE nC, SCSIZE nR)
+{
+ rMat->SetErrorInterpreter( this);
+ // A temporary matrix is mutable and ScMatrix::CloneIfConst() returns the
+ // very matrix.
+ rMat->SetMutable();
+ SCSIZE nCols, nRows;
+ rMat->GetDimensions( nCols, nRows);
+ if ( nCols != nC || nRows != nR )
+ { // arbitrary limit of elements exceeded
+ SetError( FormulaError::MatrixSize);
+ rMat.reset();
+ }
+}
+
+ScMatrixRef ScInterpreter::GetNewMat(SCSIZE nC, SCSIZE nR, bool bEmpty)
+{
+ ScMatrixRef pMat;
+ if (bEmpty)
+ pMat = new ScMatrix(nC, nR);
+ else
+ pMat = new ScMatrix(nC, nR, 0.0);
+ MakeMatNew(pMat, nC, nR);
+ return pMat;
+}
+
+ScMatrixRef ScInterpreter::GetNewMat(SCSIZE nC, SCSIZE nR, const std::vector<double>& rValues)
+{
+ ScMatrixRef pMat(new ScMatrix(nC, nR, rValues));
+ MakeMatNew(pMat, nC, nR);
+ return pMat;
+}
+
+ScMatrixRef ScInterpreter::CreateMatrixFromDoubleRef( const FormulaToken* pToken,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2 )
+{
+ if (nTab1 != nTab2 || nGlobalError != FormulaError::NONE)
+ {
+ // Not a 2D matrix.
+ SetError(FormulaError::IllegalParameter);
+ return nullptr;
+ }
+
+ if (nTab1 == nTab2 && pToken)
+ {
+ const ScComplexRefData& rCRef = *pToken->GetDoubleRef();
+ if (rCRef.IsTrimToData())
+ {
+ // Clamp the size of the matrix area to rows which actually contain data.
+ // For e.g. SUM(IF over an entire column, this can make a big difference,
+ // But lets not trim the empty space from the top or left as this matters
+ // at least in matrix formulas involving IF().
+ // Refer ScCompiler::AnnotateTrimOnDoubleRefs() where double-refs are
+ // flagged for trimming.
+ SCCOL nTempCol = nCol1;
+ SCROW nTempRow = nRow1;
+ mrDoc.ShrinkToDataArea(nTab1, nTempCol, nTempRow, nCol2, nRow2);
+ }
+ }
+
+ SCSIZE nMatCols = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
+ SCSIZE nMatRows = static_cast<SCSIZE>(nRow2 - nRow1 + 1);
+
+ if (!ScMatrix::IsSizeAllocatable( nMatCols, nMatRows))
+ {
+ SetError(FormulaError::MatrixSize);
+ return nullptr;
+ }
+
+ ScTokenMatrixMap::const_iterator aIter;
+ if (pToken && ((aIter = maTokenMatrixMap.find( pToken)) != maTokenMatrixMap.end()))
+ {
+ /* XXX casting const away here is ugly; ScMatrixToken (to which the
+ * result of this function usually is assigned) should not be forced to
+ * carry a ScConstMatrixRef though.
+ * TODO: a matrix already stored in pTokenMatrixMap should be
+ * read-only and have a copy-on-write mechanism. Previously all tokens
+ * were modifiable so we're already better than before ... */
+ return const_cast<FormulaToken*>((*aIter).second.get())->GetMatrix();
+ }
+
+ ScMatrixRef pMat = GetNewMat( nMatCols, nMatRows, true);
+ if (!pMat || nGlobalError != FormulaError::NONE)
+ return nullptr;
+
+ if (!bCalcAsShown)
+ {
+ // Use fast array fill.
+ mrDoc.FillMatrix(*pMat, nTab1, nCol1, nRow1, nCol2, nRow2);
+ }
+ else
+ {
+ // Use slower ScCellIterator to round values.
+
+ // TODO: this probably could use CellBucket for faster storage, see
+ // sc/source/core/data/column2.cxx and FillMatrixHandler, and then be
+ // moved to a function on its own, and/or squeeze the rounding into a
+ // similar FillMatrixHandler that would need to keep track of the cell
+ // position then.
+
+ // Set position where the next entry is expected.
+ SCROW nNextRow = nRow1;
+ SCCOL nNextCol = nCol1;
+ // Set last position as if there was a previous entry.
+ SCROW nThisRow = nRow2;
+ SCCOL nThisCol = nCol1 - 1;
+
+ ScCellIterator aCellIter( mrDoc, ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
+ for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
+ {
+ nThisCol = aCellIter.GetPos().Col();
+ nThisRow = aCellIter.GetPos().Row();
+ if (nThisCol != nNextCol || nThisRow != nNextRow)
+ {
+ // Fill empty between iterator's positions.
+ for ( ; nNextCol <= nThisCol; ++nNextCol)
+ {
+ const SCSIZE nC = nNextCol - nCol1;
+ const SCSIZE nMatStopRow = ((nNextCol < nThisCol) ? nMatRows : nThisRow - nRow1);
+ for (SCSIZE nR = nNextRow - nRow1; nR < nMatStopRow; ++nR)
+ {
+ pMat->PutEmpty( nC, nR);
+ }
+ nNextRow = nRow1;
+ }
+ }
+ if (nThisRow == nRow2)
+ {
+ nNextCol = nThisCol + 1;
+ nNextRow = nRow1;
+ }
+ else
+ {
+ nNextCol = nThisCol;
+ nNextRow = nThisRow + 1;
+ }
+
+ const SCSIZE nMatCol = static_cast<SCSIZE>(nThisCol - nCol1);
+ const SCSIZE nMatRow = static_cast<SCSIZE>(nThisRow - nRow1);
+ ScRefCellValue aCell( aCellIter.getRefCellValue());
+ if (aCellIter.isEmpty() || aCell.hasEmptyValue())
+ {
+ pMat->PutEmpty( nMatCol, nMatRow);
+ }
+ else if (aCell.hasError())
+ {
+ pMat->PutError( aCell.getFormula()->GetErrCode(), nMatCol, nMatRow);
+ }
+ else if (aCell.hasNumeric())
+ {
+ double fVal = aCell.getValue();
+ // CELLTYPE_FORMULA already stores the rounded value.
+ if (aCell.getType() == CELLTYPE_VALUE)
+ {
+ // TODO: this could be moved to ScCellIterator to take
+ // advantage of the faster ScAttrArray_IterGetNumberFormat.
+ const ScAddress aAdr( nThisCol, nThisRow, nTab1);
+ const sal_uInt32 nNumFormat = mrDoc.GetNumberFormat( mrContext, aAdr);
+ fVal = mrDoc.RoundValueAsShown( fVal, nNumFormat, &mrContext);
+ }
+ pMat->PutDouble( fVal, nMatCol, nMatRow);
+ }
+ else if (aCell.hasString())
+ {
+ pMat->PutString( mrStrPool.intern( aCell.getString(&mrDoc)), nMatCol, nMatRow);
+ }
+ else
+ {
+ assert(!"aCell.what?");
+ pMat->PutEmpty( nMatCol, nMatRow);
+ }
+ }
+
+ // Fill empty if iterator's last position wasn't the end.
+ if (nThisCol != nCol2 || nThisRow != nRow2)
+ {
+ for ( ; nNextCol <= nCol2; ++nNextCol)
+ {
+ SCSIZE nC = nNextCol - nCol1;
+ for (SCSIZE nR = nNextRow - nRow1; nR < nMatRows; ++nR)
+ {
+ pMat->PutEmpty( nC, nR);
+ }
+ nNextRow = nRow1;
+ }
+ }
+ }
+
+ if (pToken)
+ maTokenMatrixMap.emplace(pToken, new ScMatrixToken( pMat));
+
+ return pMat;
+}
+
+ScMatrixRef ScInterpreter::GetMatrix()
+{
+ ScMatrixRef pMat = nullptr;
+ switch (GetRawStackType())
+ {
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ pMat = GetNewMat(1, 1);
+ if (pMat)
+ {
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasEmptyValue())
+ pMat->PutEmpty(0, 0);
+ else if (aCell.hasNumeric())
+ pMat->PutDouble(GetCellValue(aAdr, aCell), 0);
+ else
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ pMat->PutString(aStr, 0);
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ const formula::FormulaToken* p = sp ? pStack[sp-1] : nullptr;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ pMat = CreateMatrixFromDoubleRef( p, nCol1, nRow1, nTab1,
+ nCol2, nRow2, nTab2);
+ }
+ break;
+ case svMatrix:
+ pMat = PopMatrix();
+ break;
+ case svError :
+ case svMissing :
+ case svDouble :
+ {
+ double fVal = GetDouble();
+ pMat = GetNewMat( 1, 1);
+ if ( pMat )
+ {
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ fVal = CreateDoubleError( nGlobalError);
+ nGlobalError = FormulaError::NONE;
+ }
+ pMat->PutDouble( fVal, 0);
+ }
+ }
+ break;
+ case svString :
+ {
+ svl::SharedString aStr = GetString();
+ pMat = GetNewMat( 1, 1);
+ if ( pMat )
+ {
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ double fVal = CreateDoubleError( nGlobalError);
+ pMat->PutDouble( fVal, 0);
+ nGlobalError = FormulaError::NONE;
+ }
+ else
+ pMat->PutString(aStr, 0);
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ PopExternalSingleRef(pToken);
+ pMat = GetNewMat( 1, 1, true);
+ if (!pMat)
+ break;
+ if (nGlobalError != FormulaError::NONE)
+ {
+ pMat->PutError( nGlobalError, 0, 0);
+ nGlobalError = FormulaError::NONE;
+ break;
+ }
+ switch (pToken->GetType())
+ {
+ case svError:
+ pMat->PutError( pToken->GetError(), 0, 0);
+ break;
+ case svDouble:
+ pMat->PutDouble( pToken->GetDouble(), 0, 0);
+ break;
+ case svString:
+ pMat->PutString( pToken->GetString(), 0, 0);
+ break;
+ default:
+ ; // nothing, empty element matrix
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ PopExternalDoubleRef(pMat);
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+ return pMat;
+}
+
+ScMatrixRef ScInterpreter::GetMatrix( short & rParam, size_t & rRefInList )
+{
+ switch (GetRawStackType())
+ {
+ case svRefList:
+ {
+ ScRange aRange( ScAddress::INITIALIZE_INVALID );
+ PopDoubleRef( aRange, rParam, rRefInList);
+ if (nGlobalError != FormulaError::NONE)
+ return nullptr;
+
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ return CreateMatrixFromDoubleRef( nullptr, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ }
+ break;
+ default:
+ return GetMatrix();
+ }
+}
+
+sc::RangeMatrix ScInterpreter::GetRangeMatrix()
+{
+ sc::RangeMatrix aRet;
+ switch (GetRawStackType())
+ {
+ case svMatrix:
+ aRet = PopRangeMatrix();
+ break;
+ default:
+ aRet.mpMat = GetMatrix();
+ }
+ return aRet;
+}
+
+void ScInterpreter::ScMatValue()
+{
+ if ( !MustHaveParamCount( GetByte(), 3 ) )
+ return;
+
+ // 0 to count-1
+ // Theoretically we could have GetSize() instead of GetUInt32(), but
+ // really, practically ...
+ SCSIZE nR = static_cast<SCSIZE>(GetUInt32());
+ SCSIZE nC = static_cast<SCSIZE>(GetUInt32());
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushError( nGlobalError);
+ return;
+ }
+ switch (GetStackType())
+ {
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ FormulaError nErrCode = aCell.getFormula()->GetErrCode();
+ if (nErrCode != FormulaError::NONE)
+ PushError( nErrCode);
+ else
+ {
+ const ScMatrix* pMat = aCell.getFormula()->GetMatrix();
+ CalculateMatrixValue(pMat,nC,nR);
+ }
+ }
+ else
+ PushIllegalParameter();
+ }
+ break;
+ case svDoubleRef :
+ {
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ if (nCol2 - nCol1 >= static_cast<SCCOL>(nR) &&
+ nRow2 - nRow1 >= static_cast<SCROW>(nC) &&
+ nTab1 == nTab2)
+ {
+ ScAddress aAdr( sal::static_int_cast<SCCOL>( nCol1 + nR ),
+ sal::static_int_cast<SCROW>( nRow1 + nC ), nTab1 );
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (aCell.hasNumeric())
+ PushDouble(GetCellValue(aAdr, aCell));
+ else
+ {
+ svl::SharedString aStr;
+ GetCellString(aStr, aCell);
+ PushString(aStr);
+ }
+ }
+ else
+ PushNoValue();
+ }
+ break;
+ case svMatrix:
+ {
+ ScMatrixRef pMat = PopMatrix();
+ CalculateMatrixValue(pMat.get(),nC,nR);
+ }
+ break;
+ default:
+ PopError();
+ PushIllegalParameter();
+ break;
+ }
+}
+void ScInterpreter::CalculateMatrixValue(const ScMatrix* pMat,SCSIZE nC,SCSIZE nR)
+{
+ if (pMat)
+ {
+ SCSIZE nCl, nRw;
+ pMat->GetDimensions(nCl, nRw);
+ if (nC < nCl && nR < nRw)
+ {
+ const ScMatrixValue nMatVal = pMat->Get( nC, nR);
+ ScMatValType nMatValType = nMatVal.nType;
+ if (ScMatrix::IsNonValueType( nMatValType))
+ PushString( nMatVal.GetString() );
+ else
+ PushDouble(nMatVal.fVal);
+ // also handles DoubleError
+ }
+ else
+ PushNoValue();
+ }
+ else
+ PushNoValue();
+}
+
+void ScInterpreter::ScEMat()
+{
+ if ( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ SCSIZE nDim = static_cast<SCSIZE>(GetUInt32());
+ if (nGlobalError != FormulaError::NONE || nDim == 0)
+ PushIllegalArgument();
+ else if (!ScMatrix::IsSizeAllocatable( nDim, nDim))
+ PushError( FormulaError::MatrixSize);
+ else
+ {
+ ScMatrixRef pRMat = GetNewMat(nDim, nDim, /*bEmpty*/true);
+ if (pRMat)
+ {
+ MEMat(pRMat, nDim);
+ PushMatrix(pRMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+}
+
+void ScInterpreter::MEMat(const ScMatrixRef& mM, SCSIZE n)
+{
+ mM->FillDouble(0.0, 0, 0, n-1, n-1);
+ for (SCSIZE i = 0; i < n; i++)
+ mM->PutDouble(1.0, i, i);
+}
+
+/* Matrix LUP decomposition according to the pseudocode of "Introduction to
+ * Algorithms" by Cormen, Leiserson, Rivest, Stein.
+ *
+ * Added scaling for numeric stability.
+ *
+ * Given an n x n nonsingular matrix A, find a permutation matrix P, a unit
+ * lower-triangular matrix L, and an upper-triangular matrix U such that PA=LU.
+ * Compute L and U "in place" in the matrix A, the original content is
+ * destroyed. Note that the diagonal elements of the U triangular matrix
+ * replace the diagonal elements of the L-unit matrix (that are each ==1). The
+ * permutation matrix P is an array, where P[i]=j means that the i-th row of P
+ * contains a 1 in column j. Additionally keep track of the number of
+ * permutations (row exchanges).
+ *
+ * Returns 0 if a singular matrix is encountered, else +1 if an even number of
+ * permutations occurred, or -1 if odd, which is the sign of the determinant.
+ * This may be used to calculate the determinant by multiplying the sign with
+ * the product of the diagonal elements of the LU matrix.
+ */
+static int lcl_LUP_decompose( ScMatrix* mA, const SCSIZE n,
+ ::std::vector< SCSIZE> & P )
+{
+ int nSign = 1;
+ // Find scale of each row.
+ ::std::vector< double> aScale(n);
+ for (SCSIZE i=0; i < n; ++i)
+ {
+ double fMax = 0.0;
+ for (SCSIZE j=0; j < n; ++j)
+ {
+ double fTmp = fabs( mA->GetDouble( j, i));
+ if (fMax < fTmp)
+ fMax = fTmp;
+ }
+ if (fMax == 0.0)
+ return 0; // singular matrix
+ aScale[i] = 1.0 / fMax;
+ }
+ // Represent identity permutation, P[i]=i
+ for (SCSIZE i=0; i < n; ++i)
+ P[i] = i;
+ // "Recursion" on the diagonal.
+ SCSIZE l = n - 1;
+ for (SCSIZE k=0; k < l; ++k)
+ {
+ // Implicit pivoting. With the scale found for a row, compare values of
+ // a column and pick largest.
+ double fMax = 0.0;
+ double fScale = aScale[k];
+ SCSIZE kp = k;
+ for (SCSIZE i = k; i < n; ++i)
+ {
+ double fTmp = fScale * fabs( mA->GetDouble( k, i));
+ if (fMax < fTmp)
+ {
+ fMax = fTmp;
+ kp = i;
+ }
+ }
+ if (fMax == 0.0)
+ return 0; // singular matrix
+ // Swap rows. The pivot element will be at mA[k,kp] (row,col notation)
+ if (k != kp)
+ {
+ // permutations
+ SCSIZE nTmp = P[k];
+ P[k] = P[kp];
+ P[kp] = nTmp;
+ nSign = -nSign;
+ // scales
+ double fTmp = aScale[k];
+ aScale[k] = aScale[kp];
+ aScale[kp] = fTmp;
+ // elements
+ for (SCSIZE i=0; i < n; ++i)
+ {
+ double fMatTmp = mA->GetDouble( i, k);
+ mA->PutDouble( mA->GetDouble( i, kp), i, k);
+ mA->PutDouble( fMatTmp, i, kp);
+ }
+ }
+ // Compute Schur complement.
+ for (SCSIZE i = k+1; i < n; ++i)
+ {
+ double fNum = mA->GetDouble( k, i);
+ double fDen = mA->GetDouble( k, k);
+ mA->PutDouble( fNum/fDen, k, i);
+ for (SCSIZE j = k+1; j < n; ++j)
+ mA->PutDouble( ( mA->GetDouble( j, i) * fDen -
+ fNum * mA->GetDouble( j, k) ) / fDen, j, i);
+ }
+ }
+#ifdef DEBUG_SC_LUP_DECOMPOSITION
+ fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): LU");
+ for (SCSIZE i=0; i < n; ++i)
+ {
+ for (SCSIZE j=0; j < n; ++j)
+ fprintf( stderr, "%8.2g ", mA->GetDouble( j, i));
+ fprintf( stderr, "\n%s\n", "");
+ }
+ fprintf( stderr, "\n%s\n", "lcl_LUP_decompose(): P");
+ for (SCSIZE j=0; j < n; ++j)
+ fprintf( stderr, "%5u ", (unsigned)P[j]);
+ fprintf( stderr, "\n%s\n", "");
+#endif
+
+ bool bSingular=false;
+ for (SCSIZE i=0; i<n && !bSingular; i++)
+ bSingular = (mA->GetDouble(i,i)) == 0.0;
+ if (bSingular)
+ nSign = 0;
+
+ return nSign;
+}
+
+/* Solve a LUP decomposed equation Ax=b. LU is a combined matrix of L and U
+ * triangulars and P the permutation vector as obtained from
+ * lcl_LUP_decompose(). B is the right-hand side input vector, X is used to
+ * return the solution vector.
+ */
+static void lcl_LUP_solve( const ScMatrix* mLU, const SCSIZE n,
+ const ::std::vector< SCSIZE> & P, const ::std::vector< double> & B,
+ ::std::vector< double> & X )
+{
+ SCSIZE nFirst = SCSIZE_MAX;
+ // Ax=b => PAx=Pb, with decomposition LUx=Pb.
+ // Define y=Ux and solve for y in Ly=Pb using forward substitution.
+ for (SCSIZE i=0; i < n; ++i)
+ {
+ KahanSum fSum = B[P[i]];
+ // Matrix inversion comes with a lot of zeros in the B vectors, we
+ // don't have to do all the computing with results multiplied by zero.
+ // Until then, simply lookout for the position of the first nonzero
+ // value.
+ if (nFirst != SCSIZE_MAX)
+ {
+ for (SCSIZE j = nFirst; j < i; ++j)
+ fSum -= mLU->GetDouble( j, i) * X[j]; // X[j] === y[j]
+ }
+ else if (fSum != 0)
+ nFirst = i;
+ X[i] = fSum.get(); // X[i] === y[i]
+ }
+ // Solve for x in Ux=y using back substitution.
+ for (SCSIZE i = n; i--; )
+ {
+ KahanSum fSum = X[i]; // X[i] === y[i]
+ for (SCSIZE j = i+1; j < n; ++j)
+ fSum -= mLU->GetDouble( j, i) * X[j]; // X[j] === x[j]
+ X[i] = fSum.get() / mLU->GetDouble( i, i); // X[i] === x[i]
+ }
+#ifdef DEBUG_SC_LUP_DECOMPOSITION
+ fprintf( stderr, "\n%s\n", "lcl_LUP_solve():");
+ for (SCSIZE i=0; i < n; ++i)
+ fprintf( stderr, "%8.2g ", X[i]);
+ fprintf( stderr, "%s\n", "");
+#endif
+}
+
+void ScInterpreter::ScMatDet()
+{
+ if ( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ ScMatrixRef pMat = GetMatrix();
+ if (!pMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ if ( !pMat->IsNumeric() )
+ {
+ PushNoValue();
+ return;
+ }
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if ( nC != nR || nC == 0 )
+ PushIllegalArgument();
+ else if (!ScMatrix::IsSizeAllocatable( nC, nR))
+ PushError( FormulaError::MatrixSize);
+ else
+ {
+ // LUP decomposition is done inplace, use copy.
+ ScMatrixRef xLU = pMat->Clone();
+ if (!xLU)
+ PushError( FormulaError::CodeOverflow);
+ else
+ {
+ ::std::vector< SCSIZE> P(nR);
+ int nDetSign = lcl_LUP_decompose( xLU.get(), nR, P);
+ if (!nDetSign)
+ PushInt(0); // singular matrix
+ else
+ {
+ // In an LU matrix the determinant is simply the product of
+ // all diagonal elements.
+ double fDet = nDetSign;
+ for (SCSIZE i=0; i < nR; ++i)
+ fDet *= xLU->GetDouble( i, i);
+ PushDouble( fDet);
+ }
+ }
+ }
+}
+
+void ScInterpreter::ScMatInv()
+{
+ if ( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ ScMatrixRef pMat = GetMatrix();
+ if (!pMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ if ( !pMat->IsNumeric() )
+ {
+ PushNoValue();
+ return;
+ }
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+
+ if (ScCalcConfig::isOpenCLEnabled())
+ {
+ sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic();
+ if (pInterpreter != nullptr)
+ {
+ ScMatrixRef xResMat = pInterpreter->inverseMatrix(*pMat);
+ if (xResMat)
+ {
+ PushMatrix(xResMat);
+ return;
+ }
+ }
+ }
+
+ if ( nC != nR || nC == 0 )
+ PushIllegalArgument();
+ else if (!ScMatrix::IsSizeAllocatable( nC, nR))
+ PushError( FormulaError::MatrixSize);
+ else
+ {
+ // LUP decomposition is done inplace, use copy.
+ ScMatrixRef xLU = pMat->Clone();
+ // The result matrix.
+ ScMatrixRef xY = GetNewMat( nR, nR, /*bEmpty*/true );
+ if (!xLU || !xY)
+ PushError( FormulaError::CodeOverflow);
+ else
+ {
+ ::std::vector< SCSIZE> P(nR);
+ int nDetSign = lcl_LUP_decompose( xLU.get(), nR, P);
+ if (!nDetSign)
+ PushIllegalArgument();
+ else
+ {
+ // Solve equation for each column.
+ ::std::vector< double> B(nR);
+ ::std::vector< double> X(nR);
+ for (SCSIZE j=0; j < nR; ++j)
+ {
+ for (SCSIZE i=0; i < nR; ++i)
+ B[i] = 0.0;
+ B[j] = 1.0;
+ lcl_LUP_solve( xLU.get(), nR, P, B, X);
+ for (SCSIZE i=0; i < nR; ++i)
+ xY->PutDouble( X[i], j, i);
+ }
+#ifdef DEBUG_SC_LUP_DECOMPOSITION
+ /* Possible checks for ill-condition:
+ * 1. Scale matrix, invert scaled matrix. If there are
+ * elements of the inverted matrix that are several
+ * orders of magnitude greater than 1 =>
+ * ill-conditioned.
+ * Just how much is "several orders"?
+ * 2. Invert the inverted matrix and assess whether the
+ * result is sufficiently close to the original matrix.
+ * If not => ill-conditioned.
+ * Just what is sufficient?
+ * 3. Multiplying the inverse by the original matrix should
+ * produce a result sufficiently close to the identity
+ * matrix.
+ * Just what is sufficient?
+ *
+ * The following is #3.
+ */
+ const double fInvEpsilon = 1.0E-7;
+ ScMatrixRef xR = GetNewMat( nR, nR);
+ if (xR)
+ {
+ ScMatrix* pR = xR.get();
+ lcl_MFastMult( pMat, xY.get(), pR, nR, nR, nR);
+ fprintf( stderr, "\n%s\n", "ScMatInv(): mult-identity");
+ for (SCSIZE i=0; i < nR; ++i)
+ {
+ for (SCSIZE j=0; j < nR; ++j)
+ {
+ double fTmp = pR->GetDouble( j, i);
+ fprintf( stderr, "%8.2g ", fTmp);
+ if (fabs( fTmp - (i == j)) > fInvEpsilon)
+ SetError( FormulaError::IllegalArgument);
+ }
+ fprintf( stderr, "\n%s\n", "");
+ }
+ }
+#endif
+ if (nGlobalError != FormulaError::NONE)
+ PushError( nGlobalError);
+ else
+ PushMatrix( xY);
+ }
+ }
+ }
+}
+
+void ScInterpreter::ScMatMult()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ ScMatrixRef pMat2 = GetMatrix();
+ ScMatrixRef pMat1 = GetMatrix();
+ ScMatrixRef pRMat;
+ if (pMat1 && pMat2)
+ {
+ if ( pMat1->IsNumeric() && pMat2->IsNumeric() )
+ {
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ if (nC1 != nR2)
+ PushIllegalArgument();
+ else
+ {
+ pRMat = GetNewMat(nC2, nR1, /*bEmpty*/true);
+ if (pRMat)
+ {
+ KahanSum fSum;
+ for (SCSIZE i = 0; i < nR1; i++)
+ {
+ for (SCSIZE j = 0; j < nC2; j++)
+ {
+ fSum = 0.0;
+ for (SCSIZE k = 0; k < nC1; k++)
+ {
+ fSum += pMat1->GetDouble(k,i)*pMat2->GetDouble(j,k);
+ }
+ pRMat->PutDouble(fSum.get(), j, i);
+ }
+ }
+ PushMatrix(pRMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ }
+ else
+ PushNoValue();
+ }
+ else
+ PushIllegalParameter();
+}
+
+void ScInterpreter::ScMatTrans()
+{
+ if ( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ ScMatrixRef pMat = GetMatrix();
+ ScMatrixRef pRMat;
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ pRMat = GetNewMat(nR, nC, /*bEmpty*/true);
+ if ( pRMat )
+ {
+ pMat->MatTrans(*pRMat);
+ PushMatrix(pRMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ PushIllegalParameter();
+}
+
+/** Minimum extent of one result matrix dimension.
+ For a row or column vector to be replicated the larger matrix dimension is
+ returned, else the smaller dimension.
+ */
+static SCSIZE lcl_GetMinExtent( SCSIZE n1, SCSIZE n2 )
+{
+ if (n1 == 1)
+ return n2;
+ else if (n2 == 1)
+ return n1;
+ else if (n1 < n2)
+ return n1;
+ else
+ return n2;
+}
+
+static ScMatrixRef lcl_MatrixCalculation(
+ const ScMatrix& rMat1, const ScMatrix& rMat2, ScInterpreter* pInterpreter, ScMatrix::CalculateOpFunction Op)
+{
+ SCSIZE nC1, nC2, nMinC;
+ SCSIZE nR1, nR2, nMinR;
+ rMat1.GetDimensions(nC1, nR1);
+ rMat2.GetDimensions(nC2, nR2);
+ nMinC = lcl_GetMinExtent( nC1, nC2);
+ nMinR = lcl_GetMinExtent( nR1, nR2);
+ ScMatrixRef xResMat = pInterpreter->GetNewMat(nMinC, nMinR, /*bEmpty*/true);
+ if (xResMat)
+ xResMat->ExecuteBinaryOp(nMinC, nMinR, rMat1, rMat2, pInterpreter, Op);
+ return xResMat;
+}
+
+ScMatrixRef ScInterpreter::MatConcat(const ScMatrixRef& pMat1, const ScMatrixRef& pMat2)
+{
+ SCSIZE nC1, nC2, nMinC;
+ SCSIZE nR1, nR2, nMinR;
+ pMat1->GetDimensions(nC1, nR1);
+ pMat2->GetDimensions(nC2, nR2);
+ nMinC = lcl_GetMinExtent( nC1, nC2);
+ nMinR = lcl_GetMinExtent( nR1, nR2);
+ ScMatrixRef xResMat = GetNewMat(nMinC, nMinR, /*bEmpty*/true);
+ if (xResMat)
+ {
+ xResMat->MatConcat(nMinC, nMinR, pMat1, pMat2, *pFormatter, mrDoc.GetSharedStringPool());
+ }
+ return xResMat;
+}
+
+// for DATE, TIME, DATETIME, DURATION
+static void lcl_GetDiffDateTimeFmtType( SvNumFormatType& nFuncFmt, SvNumFormatType nFmt1, SvNumFormatType nFmt2 )
+{
+ if ( nFmt1 == SvNumFormatType::UNDEFINED && nFmt2 == SvNumFormatType::UNDEFINED )
+ return;
+
+ if ( nFmt1 == nFmt2 )
+ {
+ if ( nFmt1 == SvNumFormatType::TIME || nFmt1 == SvNumFormatType::DATETIME
+ || nFmt1 == SvNumFormatType::DURATION )
+ nFuncFmt = SvNumFormatType::DURATION; // times result in time duration
+ // else: nothing special, number (date - date := days)
+ }
+ else if ( nFmt1 == SvNumFormatType::UNDEFINED )
+ nFuncFmt = nFmt2; // e.g. date + days := date
+ else if ( nFmt2 == SvNumFormatType::UNDEFINED )
+ nFuncFmt = nFmt1;
+ else
+ {
+ if ( nFmt1 == SvNumFormatType::DATE || nFmt2 == SvNumFormatType::DATE ||
+ nFmt1 == SvNumFormatType::DATETIME || nFmt2 == SvNumFormatType::DATETIME )
+ {
+ if ( nFmt1 == SvNumFormatType::TIME || nFmt2 == SvNumFormatType::TIME )
+ nFuncFmt = SvNumFormatType::DATETIME; // date + time
+ }
+ }
+}
+
+void ScInterpreter::ScAdd()
+{
+ CalculateAddSub(false);
+}
+
+void ScInterpreter::CalculateAddSub(bool _bSub)
+{
+ ScMatrixRef pMat1 = nullptr;
+ ScMatrixRef pMat2 = nullptr;
+ double fVal1 = 0.0, fVal2 = 0.0;
+ SvNumFormatType nFmt1, nFmt2;
+ nFmt1 = nFmt2 = SvNumFormatType::UNDEFINED;
+ bool bDuration = false;
+ SvNumFormatType nFmtCurrencyType = nCurFmtType;
+ sal_uLong nFmtCurrencyIndex = nCurFmtIndex;
+ SvNumFormatType nFmtPercentType = nCurFmtType;
+ if ( GetStackType() == svMatrix )
+ pMat2 = GetMatrix();
+ else
+ {
+ fVal2 = GetDouble();
+ switch ( nCurFmtType )
+ {
+ case SvNumFormatType::DATE :
+ case SvNumFormatType::TIME :
+ case SvNumFormatType::DATETIME :
+ case SvNumFormatType::DURATION :
+ nFmt2 = nCurFmtType;
+ bDuration = true;
+ break;
+ case SvNumFormatType::CURRENCY :
+ nFmtCurrencyType = nCurFmtType;
+ nFmtCurrencyIndex = nCurFmtIndex;
+ break;
+ case SvNumFormatType::PERCENT :
+ nFmtPercentType = SvNumFormatType::PERCENT;
+ break;
+ default: break;
+ }
+ }
+ if ( GetStackType() == svMatrix )
+ pMat1 = GetMatrix();
+ else
+ {
+ fVal1 = GetDouble();
+ switch ( nCurFmtType )
+ {
+ case SvNumFormatType::DATE :
+ case SvNumFormatType::TIME :
+ case SvNumFormatType::DATETIME :
+ case SvNumFormatType::DURATION :
+ nFmt1 = nCurFmtType;
+ bDuration = true;
+ break;
+ case SvNumFormatType::CURRENCY :
+ nFmtCurrencyType = nCurFmtType;
+ nFmtCurrencyIndex = nCurFmtIndex;
+ break;
+ case SvNumFormatType::PERCENT :
+ nFmtPercentType = SvNumFormatType::PERCENT;
+ break;
+ default: break;
+ }
+ }
+ if (pMat1 && pMat2)
+ {
+ ScMatrixRef pResMat;
+ if ( _bSub )
+ {
+ pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixSub);
+ }
+ else
+ {
+ pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixAdd);
+ }
+
+ if (!pResMat)
+ PushNoValue();
+ else
+ PushMatrix(pResMat);
+ }
+ else if (pMat1 || pMat2)
+ {
+ double fVal;
+ bool bFlag;
+ ScMatrixRef pMat = pMat1;
+ if (!pMat)
+ {
+ fVal = fVal1;
+ pMat = pMat2;
+ bFlag = true; // double - Matrix
+ }
+ else
+ {
+ fVal = fVal2;
+ bFlag = false; // Matrix - double
+ }
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ ScMatrixRef pResMat = GetNewMat(nC, nR, true);
+ if (pResMat)
+ {
+ if (_bSub)
+ {
+ pMat->SubOp( bFlag, fVal, *pResMat);
+ }
+ else
+ {
+ pMat->AddOp( fVal, *pResMat);
+ }
+ PushMatrix(pResMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ {
+ // Determine nFuncFmtType type before PushDouble().
+ if ( nFmtCurrencyType == SvNumFormatType::CURRENCY )
+ {
+ nFuncFmtType = nFmtCurrencyType;
+ nFuncFmtIndex = nFmtCurrencyIndex;
+ }
+ else
+ {
+ lcl_GetDiffDateTimeFmtType( nFuncFmtType, nFmt1, nFmt2 );
+ if (nFmtPercentType == SvNumFormatType::PERCENT && nFuncFmtType == SvNumFormatType::NUMBER)
+ nFuncFmtType = SvNumFormatType::PERCENT;
+ }
+ if ((nFuncFmtType == SvNumFormatType::DURATION || bDuration)
+ && ((_bSub && std::fabs(fVal1 - fVal2) <= SAL_MAX_INT32)
+ || (!_bSub && std::fabs(fVal1 + fVal2) <= SAL_MAX_INT32)))
+ {
+ // Limit to microseconds resolution on date inflicted or duration
+ // values of 24 hours or more.
+ const sal_uInt64 nEpsilon = ((std::fabs(fVal1) >= 1.0 || std::fabs(fVal2) >= 1.0) ?
+ ::tools::Duration::kAccuracyEpsilonNanosecondsMicroseconds :
+ ::tools::Duration::kAccuracyEpsilonNanoseconds);
+ if (_bSub)
+ PushDouble( ::tools::Duration( fVal1 - fVal2, nEpsilon).GetInDays());
+ else
+ PushDouble( ::tools::Duration( fVal1 + fVal2, nEpsilon).GetInDays());
+ }
+ else
+ {
+ if (_bSub)
+ PushDouble( ::rtl::math::approxSub( fVal1, fVal2 ) );
+ else
+ PushDouble( ::rtl::math::approxAdd( fVal1, fVal2 ) );
+ }
+ }
+}
+
+void ScInterpreter::ScAmpersand()
+{
+ ScMatrixRef pMat1 = nullptr;
+ ScMatrixRef pMat2 = nullptr;
+ OUString sStr1, sStr2;
+ if ( GetStackType() == svMatrix )
+ pMat2 = GetMatrix();
+ else
+ sStr2 = GetString().getString();
+ if ( GetStackType() == svMatrix )
+ pMat1 = GetMatrix();
+ else
+ sStr1 = GetString().getString();
+ if (pMat1 && pMat2)
+ {
+ ScMatrixRef pResMat = MatConcat(pMat1, pMat2);
+ if (!pResMat)
+ PushNoValue();
+ else
+ PushMatrix(pResMat);
+ }
+ else if (pMat1 || pMat2)
+ {
+ OUString sStr;
+ bool bFlag;
+ ScMatrixRef pMat = pMat1;
+ if (!pMat)
+ {
+ sStr = sStr1;
+ pMat = pMat2;
+ bFlag = true; // double - Matrix
+ }
+ else
+ {
+ sStr = sStr2;
+ bFlag = false; // Matrix - double
+ }
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
+ if (pResMat)
+ {
+ if (nGlobalError != FormulaError::NONE)
+ {
+ for (SCSIZE i = 0; i < nC; ++i)
+ for (SCSIZE j = 0; j < nR; ++j)
+ pResMat->PutError( nGlobalError, i, j);
+ }
+ else if (bFlag)
+ {
+ for (SCSIZE i = 0; i < nC; ++i)
+ for (SCSIZE j = 0; j < nR; ++j)
+ {
+ FormulaError nErr = pMat->GetErrorIfNotString( i, j);
+ if (nErr != FormulaError::NONE)
+ pResMat->PutError( nErr, i, j);
+ else
+ {
+ OUString aTmp = sStr + pMat->GetString(*pFormatter, i, j).getString();
+ pResMat->PutString(mrStrPool.intern(aTmp), i, j);
+ }
+ }
+ }
+ else
+ {
+ for (SCSIZE i = 0; i < nC; ++i)
+ for (SCSIZE j = 0; j < nR; ++j)
+ {
+ FormulaError nErr = pMat->GetErrorIfNotString( i, j);
+ if (nErr != FormulaError::NONE)
+ pResMat->PutError( nErr, i, j);
+ else
+ {
+ OUString aTmp = pMat->GetString(*pFormatter, i, j).getString() + sStr;
+ pResMat->PutString(mrStrPool.intern(aTmp), i, j);
+ }
+ }
+ }
+ PushMatrix(pResMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ {
+ if ( CheckStringResultLen( sStr1, sStr2.getLength() ) )
+ sStr1 += sStr2;
+ PushString(sStr1);
+ }
+}
+
+void ScInterpreter::ScSub()
+{
+ CalculateAddSub(true);
+}
+
+void ScInterpreter::ScMul()
+{
+ ScMatrixRef pMat1 = nullptr;
+ ScMatrixRef pMat2 = nullptr;
+ double fVal1 = 0.0, fVal2 = 0.0;
+ SvNumFormatType nFmtCurrencyType = nCurFmtType;
+ sal_uLong nFmtCurrencyIndex = nCurFmtIndex;
+ if ( GetStackType() == svMatrix )
+ pMat2 = GetMatrix();
+ else
+ {
+ fVal2 = GetDouble();
+ switch ( nCurFmtType )
+ {
+ case SvNumFormatType::CURRENCY :
+ nFmtCurrencyType = nCurFmtType;
+ nFmtCurrencyIndex = nCurFmtIndex;
+ break;
+ default: break;
+ }
+ }
+ if ( GetStackType() == svMatrix )
+ pMat1 = GetMatrix();
+ else
+ {
+ fVal1 = GetDouble();
+ switch ( nCurFmtType )
+ {
+ case SvNumFormatType::CURRENCY :
+ nFmtCurrencyType = nCurFmtType;
+ nFmtCurrencyIndex = nCurFmtIndex;
+ break;
+ default: break;
+ }
+ }
+ if (pMat1 && pMat2)
+ {
+ ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixMul);
+ if (!pResMat)
+ PushNoValue();
+ else
+ PushMatrix(pResMat);
+ }
+ else if (pMat1 || pMat2)
+ {
+ double fVal;
+ ScMatrixRef pMat = pMat1;
+ if (!pMat)
+ {
+ fVal = fVal1;
+ pMat = pMat2;
+ }
+ else
+ fVal = fVal2;
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
+ if (pResMat)
+ {
+ pMat->MulOp( fVal, *pResMat);
+ PushMatrix(pResMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ {
+ // Determine nFuncFmtType type before PushDouble().
+ if ( nFmtCurrencyType == SvNumFormatType::CURRENCY )
+ {
+ nFuncFmtType = nFmtCurrencyType;
+ nFuncFmtIndex = nFmtCurrencyIndex;
+ }
+ PushDouble(fVal1 * fVal2);
+ }
+}
+
+void ScInterpreter::ScDiv()
+{
+ ScMatrixRef pMat1 = nullptr;
+ ScMatrixRef pMat2 = nullptr;
+ double fVal1 = 0.0, fVal2 = 0.0;
+ SvNumFormatType nFmtCurrencyType = nCurFmtType;
+ sal_uLong nFmtCurrencyIndex = nCurFmtIndex;
+ SvNumFormatType nFmtCurrencyType2 = SvNumFormatType::UNDEFINED;
+ if ( GetStackType() == svMatrix )
+ pMat2 = GetMatrix();
+ else
+ {
+ fVal2 = GetDouble();
+ // do not take over currency, 123kg/456USD is not USD
+ nFmtCurrencyType2 = nCurFmtType;
+ }
+ if ( GetStackType() == svMatrix )
+ pMat1 = GetMatrix();
+ else
+ {
+ fVal1 = GetDouble();
+ switch ( nCurFmtType )
+ {
+ case SvNumFormatType::CURRENCY :
+ nFmtCurrencyType = nCurFmtType;
+ nFmtCurrencyIndex = nCurFmtIndex;
+ break;
+ default: break;
+ }
+ }
+ if (pMat1 && pMat2)
+ {
+ ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixDiv);
+ if (!pResMat)
+ PushNoValue();
+ else
+ PushMatrix(pResMat);
+ }
+ else if (pMat1 || pMat2)
+ {
+ double fVal;
+ bool bFlag;
+ ScMatrixRef pMat = pMat1;
+ if (!pMat)
+ {
+ fVal = fVal1;
+ pMat = pMat2;
+ bFlag = true; // double - Matrix
+ }
+ else
+ {
+ fVal = fVal2;
+ bFlag = false; // Matrix - double
+ }
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
+ if (pResMat)
+ {
+ pMat->DivOp( bFlag, fVal, *pResMat);
+ PushMatrix(pResMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ {
+ // Determine nFuncFmtType type before PushDouble().
+ if ( nFmtCurrencyType == SvNumFormatType::CURRENCY &&
+ nFmtCurrencyType2 != SvNumFormatType::CURRENCY)
+ { // even USD/USD is not USD
+ nFuncFmtType = nFmtCurrencyType;
+ nFuncFmtIndex = nFmtCurrencyIndex;
+ }
+ PushDouble( div( fVal1, fVal2) );
+ }
+}
+
+void ScInterpreter::ScPower()
+{
+ if ( MustHaveParamCount( GetByte(), 2 ) )
+ ScPow();
+}
+
+void ScInterpreter::ScPow()
+{
+ ScMatrixRef pMat1 = nullptr;
+ ScMatrixRef pMat2 = nullptr;
+ double fVal1 = 0.0, fVal2 = 0.0;
+ if ( GetStackType() == svMatrix )
+ pMat2 = GetMatrix();
+ else
+ fVal2 = GetDouble();
+ if ( GetStackType() == svMatrix )
+ pMat1 = GetMatrix();
+ else
+ fVal1 = GetDouble();
+ if (pMat1 && pMat2)
+ {
+ ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixPow);
+ if (!pResMat)
+ PushNoValue();
+ else
+ PushMatrix(pResMat);
+ }
+ else if (pMat1 || pMat2)
+ {
+ double fVal;
+ bool bFlag;
+ ScMatrixRef pMat = pMat1;
+ if (!pMat)
+ {
+ fVal = fVal1;
+ pMat = pMat2;
+ bFlag = true; // double - Matrix
+ }
+ else
+ {
+ fVal = fVal2;
+ bFlag = false; // Matrix - double
+ }
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ ScMatrixRef pResMat = GetNewMat(nC, nR, /*bEmpty*/true);
+ if (pResMat)
+ {
+ pMat->PowOp( bFlag, fVal, *pResMat);
+ PushMatrix(pResMat);
+ }
+ else
+ PushIllegalArgument();
+ }
+ else
+ {
+ PushDouble( sc::power( fVal1, fVal2));
+ }
+}
+
+void ScInterpreter::ScSumProduct()
+{
+ short nParamCount = GetByte();
+ if ( !MustHaveParamCountMin( nParamCount, 1) )
+ return;
+
+ // XXX NOTE: Excel returns #VALUE! for reference list and 0 (why?) for
+ // array of references. We calculate the proper individual arrays if sizes
+ // match.
+
+ size_t nInRefList = 0;
+ ScMatrixRef pMatLast;
+ ScMatrixRef pMat;
+
+ pMatLast = GetMatrix( --nParamCount, nInRefList);
+ if (!pMatLast)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ SCSIZE nC, nCLast, nR, nRLast;
+ pMatLast->GetDimensions(nCLast, nRLast);
+ std::vector<double> aResArray;
+ pMatLast->GetDoubleArray(aResArray);
+
+ while (nParamCount--)
+ {
+ pMat = GetMatrix( nParamCount, nInRefList);
+ if (!pMat)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ pMat->GetDimensions(nC, nR);
+ if (nC != nCLast || nR != nRLast)
+ {
+ PushNoValue();
+ return;
+ }
+
+ pMat->MergeDoubleArrayMultiply(aResArray);
+ }
+
+ KahanSum fSum = 0.0;
+ for( double fPosArray : aResArray )
+ {
+ FormulaError nErr = GetDoubleErrorValue(fPosArray);
+ if (nErr == FormulaError::NONE)
+ fSum += fPosArray;
+ else if (nErr != FormulaError::ElementNaN)
+ {
+ // Propagate the first error encountered, ignore "this is not a number" elements.
+ PushError(nErr);
+ return;
+ }
+ }
+
+ PushDouble(fSum.get());
+}
+
+void ScInterpreter::ScSumX2MY2()
+{
+ CalculateSumX2MY2SumX2DY2(false);
+}
+void ScInterpreter::CalculateSumX2MY2SumX2DY2(bool _bSumX2DY2)
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ ScMatrixRef pMat1 = nullptr;
+ ScMatrixRef pMat2 = nullptr;
+ SCSIZE i, j;
+ pMat2 = GetMatrix();
+ pMat1 = GetMatrix();
+ if (!pMat2 || !pMat1)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat2->GetDimensions(nC2, nR2);
+ pMat1->GetDimensions(nC1, nR1);
+ if (nC1 != nC2 || nR1 != nR2)
+ {
+ PushNoValue();
+ return;
+ }
+ double fVal;
+ KahanSum fSum = 0.0;
+ for (i = 0; i < nC1; i++)
+ for (j = 0; j < nR1; j++)
+ if (!pMat1->IsStringOrEmpty(i,j) && !pMat2->IsStringOrEmpty(i,j))
+ {
+ fVal = pMat1->GetDouble(i,j);
+ fSum += fVal * fVal;
+ fVal = pMat2->GetDouble(i,j);
+ if ( _bSumX2DY2 )
+ fSum += fVal * fVal;
+ else
+ fSum -= fVal * fVal;
+ }
+ PushDouble(fSum.get());
+}
+
+void ScInterpreter::ScSumX2DY2()
+{
+ CalculateSumX2MY2SumX2DY2(true);
+}
+
+void ScInterpreter::ScSumXMY2()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ ScMatrixRef pMat2 = GetMatrix();
+ ScMatrixRef pMat1 = GetMatrix();
+ if (!pMat2 || !pMat1)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ pMat2->GetDimensions(nC2, nR2);
+ pMat1->GetDimensions(nC1, nR1);
+ if (nC1 != nC2 || nR1 != nR2)
+ {
+ PushNoValue();
+ return;
+ } // if (nC1 != nC2 || nR1 != nR2)
+ ScMatrixRef pResMat = lcl_MatrixCalculation( *pMat1, *pMat2, this, MatrixSub);
+ if (!pResMat)
+ {
+ PushNoValue();
+ }
+ else
+ {
+ PushDouble(pResMat->SumSquare(false).maAccumulator.get());
+ }
+}
+
+void ScInterpreter::ScFrequency()
+{
+ if ( !MustHaveParamCount( GetByte(), 2 ) )
+ return;
+
+ vector<double> aBinArray;
+ vector<tools::Long> aBinIndexOrder;
+
+ GetSortArray( 1, aBinArray, &aBinIndexOrder, false, false );
+ SCSIZE nBinSize = aBinArray.size();
+ if (nGlobalError != FormulaError::NONE)
+ {
+ PushNoValue();
+ return;
+ }
+
+ vector<double> aDataArray;
+ GetSortArray( 1, aDataArray, nullptr, false, false );
+ SCSIZE nDataSize = aDataArray.size();
+
+ if (aDataArray.empty() || nGlobalError != FormulaError::NONE)
+ {
+ PushNoValue();
+ return;
+ }
+ ScMatrixRef pResMat = GetNewMat(1, nBinSize+1, /*bEmpty*/true);
+ if (!pResMat)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ if (nBinSize != aBinIndexOrder.size())
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ SCSIZE j;
+ SCSIZE i = 0;
+ for (j = 0; j < nBinSize; ++j)
+ {
+ SCSIZE nCount = 0;
+ while (i < nDataSize && aDataArray[i] <= aBinArray[j])
+ {
+ ++nCount;
+ ++i;
+ }
+ pResMat->PutDouble(static_cast<double>(nCount), aBinIndexOrder[j]);
+ }
+ pResMat->PutDouble(static_cast<double>(nDataSize-i), j);
+ PushMatrix(pResMat);
+}
+
+namespace {
+
+// Helper methods for LINEST/LOGEST and TREND/GROWTH
+// All matrices must already exist and have the needed size, no control tests
+// done. Those methods, which names start with lcl_T, are adapted to case 3,
+// where Y (=observed values) is given as row.
+// Remember, ScMatrix matrices are zero based, index access (column,row).
+
+// <A;B> over all elements; uses the matrices as vectors of length M
+double lcl_GetSumProduct(const ScMatrixRef& pMatA, const ScMatrixRef& pMatB, SCSIZE nM)
+{
+ KahanSum fSum = 0.0;
+ for (SCSIZE i=0; i<nM; i++)
+ fSum += pMatA->GetDouble(i) * pMatB->GetDouble(i);
+ return fSum.get();
+}
+
+// Special version for use within QR decomposition.
+// Euclidean norm of column index C starting in row index R;
+// matrix A has count N rows.
+double lcl_GetColumnEuclideanNorm(const ScMatrixRef& pMatA, SCSIZE nC, SCSIZE nR, SCSIZE nN)
+{
+ KahanSum fNorm = 0.0;
+ for (SCSIZE row=nR; row<nN; row++)
+ fNorm += (pMatA->GetDouble(nC,row)) * (pMatA->GetDouble(nC,row));
+ return sqrt(fNorm.get());
+}
+
+// Euclidean norm of row index R starting in column index C;
+// matrix A has count N columns.
+double lcl_TGetColumnEuclideanNorm(const ScMatrixRef& pMatA, SCSIZE nR, SCSIZE nC, SCSIZE nN)
+{
+ KahanSum fNorm = 0.0;
+ for (SCSIZE col=nC; col<nN; col++)
+ fNorm += (pMatA->GetDouble(col,nR)) * (pMatA->GetDouble(col,nR));
+ return sqrt(fNorm.get());
+}
+
+// Special version for use within QR decomposition.
+// Maximum norm of column index C starting in row index R;
+// matrix A has count N rows.
+double lcl_GetColumnMaximumNorm(const ScMatrixRef& pMatA, SCSIZE nC, SCSIZE nR, SCSIZE nN)
+{
+ double fNorm = 0.0;
+ for (SCSIZE row=nR; row<nN; row++)
+ {
+ double fVal = fabs(pMatA->GetDouble(nC,row));
+ if (fNorm < fVal)
+ fNorm = fVal;
+ }
+ return fNorm;
+}
+
+// Maximum norm of row index R starting in col index C;
+// matrix A has count N columns.
+double lcl_TGetColumnMaximumNorm(const ScMatrixRef& pMatA, SCSIZE nR, SCSIZE nC, SCSIZE nN)
+{
+ double fNorm = 0.0;
+ for (SCSIZE col=nC; col<nN; col++)
+ {
+ double fVal = fabs(pMatA->GetDouble(col,nR));
+ if (fNorm < fVal)
+ fNorm = fVal;
+ }
+ return fNorm;
+}
+
+// Special version for use within QR decomposition.
+// <A(Ca);B(Cb)> starting in row index R;
+// Ca and Cb are indices of columns, matrices A and B have count N rows.
+double lcl_GetColumnSumProduct(const ScMatrixRef& pMatA, SCSIZE nCa,
+ const ScMatrixRef& pMatB, SCSIZE nCb, SCSIZE nR, SCSIZE nN)
+{
+ KahanSum fResult = 0.0;
+ for (SCSIZE row=nR; row<nN; row++)
+ fResult += pMatA->GetDouble(nCa,row) * pMatB->GetDouble(nCb,row);
+ return fResult.get();
+}
+
+// <A(Ra);B(Rb)> starting in column index C;
+// Ra and Rb are indices of rows, matrices A and B have count N columns.
+double lcl_TGetColumnSumProduct(const ScMatrixRef& pMatA, SCSIZE nRa,
+ const ScMatrixRef& pMatB, SCSIZE nRb, SCSIZE nC, SCSIZE nN)
+{
+ KahanSum fResult = 0.0;
+ for (SCSIZE col=nC; col<nN; col++)
+ fResult += pMatA->GetDouble(col,nRa) * pMatB->GetDouble(col,nRb);
+ return fResult.get();
+}
+
+// no mathematical signum, but used to switch between adding and subtracting
+double lcl_GetSign(double fValue)
+{
+ return (fValue >= 0.0 ? 1.0 : -1.0 );
+}
+
+/* Calculates a QR decomposition with Householder reflection.
+ * For each NxK matrix A exists a decomposition A=Q*R with an orthogonal
+ * NxN matrix Q and a NxK matrix R.
+ * Q=H1*H2*...*Hk with Householder matrices H. Such a householder matrix can
+ * be build from a vector u by H=I-(2/u'u)*(u u'). This vectors u are returned
+ * in the columns of matrix A, overwriting the old content.
+ * The matrix R has a quadric upper part KxK with values in the upper right
+ * triangle and zeros in all other elements. Here the diagonal elements of R
+ * are stored in the vector R and the other upper right elements in the upper
+ * right of the matrix A.
+ * The function returns false, if calculation breaks. But because of round-off
+ * errors singularity is often not detected.
+ */
+bool lcl_CalculateQRdecomposition(const ScMatrixRef& pMatA,
+ ::std::vector< double>& pVecR, SCSIZE nK, SCSIZE nN)
+{
+ // ScMatrix matrices are zero based, index access (column,row)
+ for (SCSIZE col = 0; col <nK; col++)
+ {
+ // calculate vector u of the householder transformation
+ const double fScale = lcl_GetColumnMaximumNorm(pMatA, col, col, nN);
+ if (fScale == 0.0)
+ {
+ // A is singular
+ return false;
+ }
+ for (SCSIZE row = col; row <nN; row++)
+ pMatA->PutDouble( pMatA->GetDouble(col,row)/fScale, col, row);
+
+ const double fEuclid = lcl_GetColumnEuclideanNorm(pMatA, col, col, nN);
+ const double fFactor = 1.0/fEuclid/(fEuclid + fabs(pMatA->GetDouble(col,col)));
+ const double fSignum = lcl_GetSign(pMatA->GetDouble(col,col));
+ pMatA->PutDouble( pMatA->GetDouble(col,col) + fSignum*fEuclid, col,col);
+ pVecR[col] = -fSignum * fScale * fEuclid;
+
+ // apply Householder transformation to A
+ for (SCSIZE c=col+1; c<nK; c++)
+ {
+ const double fSum =lcl_GetColumnSumProduct(pMatA, col, pMatA, c, col, nN);
+ for (SCSIZE row = col; row <nN; row++)
+ pMatA->PutDouble( pMatA->GetDouble(c,row) - fSum * fFactor * pMatA->GetDouble(col,row), c, row);
+ }
+ }
+ return true;
+}
+
+// same with transposed matrix A, N is count of columns, K count of rows
+bool lcl_TCalculateQRdecomposition(const ScMatrixRef& pMatA,
+ ::std::vector< double>& pVecR, SCSIZE nK, SCSIZE nN)
+{
+ double fSum ;
+ // ScMatrix matrices are zero based, index access (column,row)
+ for (SCSIZE row = 0; row <nK; row++)
+ {
+ // calculate vector u of the householder transformation
+ const double fScale = lcl_TGetColumnMaximumNorm(pMatA, row, row, nN);
+ if (fScale == 0.0)
+ {
+ // A is singular
+ return false;
+ }
+ for (SCSIZE col = row; col <nN; col++)
+ pMatA->PutDouble( pMatA->GetDouble(col,row)/fScale, col, row);
+
+ const double fEuclid = lcl_TGetColumnEuclideanNorm(pMatA, row, row, nN);
+ const double fFactor = 1.0/fEuclid/(fEuclid + fabs(pMatA->GetDouble(row,row)));
+ const double fSignum = lcl_GetSign(pMatA->GetDouble(row,row));
+ pMatA->PutDouble( pMatA->GetDouble(row,row) + fSignum*fEuclid, row,row);
+ pVecR[row] = -fSignum * fScale * fEuclid;
+
+ // apply Householder transformation to A
+ for (SCSIZE r=row+1; r<nK; r++)
+ {
+ fSum =lcl_TGetColumnSumProduct(pMatA, row, pMatA, r, row, nN);
+ for (SCSIZE col = row; col <nN; col++)
+ pMatA->PutDouble(
+ pMatA->GetDouble(col,r) - fSum * fFactor * pMatA->GetDouble(col,row), col, r);
+ }
+ }
+ return true;
+}
+
+/* Applies a Householder transformation to a column vector Y with is given as
+ * Nx1 Matrix. The vector u, from which the Householder transformation is built,
+ * is the column part in matrix A, with column index C, starting with row
+ * index C. A is the result of the QR decomposition as obtained from
+ * lcl_CalculateQRdecomposition.
+ */
+void lcl_ApplyHouseholderTransformation(const ScMatrixRef& pMatA, SCSIZE nC,
+ const ScMatrixRef& pMatY, SCSIZE nN)
+{
+ // ScMatrix matrices are zero based, index access (column,row)
+ double fDenominator = lcl_GetColumnSumProduct(pMatA, nC, pMatA, nC, nC, nN);
+ double fNumerator = lcl_GetColumnSumProduct(pMatA, nC, pMatY, 0, nC, nN);
+ double fFactor = 2.0 * (fNumerator/fDenominator);
+ for (SCSIZE row = nC; row < nN; row++)
+ pMatY->PutDouble(
+ pMatY->GetDouble(row) - fFactor * pMatA->GetDouble(nC,row), row);
+}
+
+// Same with transposed matrices A and Y.
+void lcl_TApplyHouseholderTransformation(const ScMatrixRef& pMatA, SCSIZE nR,
+ const ScMatrixRef& pMatY, SCSIZE nN)
+{
+ // ScMatrix matrices are zero based, index access (column,row)
+ double fDenominator = lcl_TGetColumnSumProduct(pMatA, nR, pMatA, nR, nR, nN);
+ double fNumerator = lcl_TGetColumnSumProduct(pMatA, nR, pMatY, 0, nR, nN);
+ double fFactor = 2.0 * (fNumerator/fDenominator);
+ for (SCSIZE col = nR; col < nN; col++)
+ pMatY->PutDouble(
+ pMatY->GetDouble(col) - fFactor * pMatA->GetDouble(col,nR), col);
+}
+
+/* Solve for X in R*X=S using back substitution. The solution X overwrites S.
+ * Uses R from the result of the QR decomposition of a NxK matrix A.
+ * S is a column vector given as matrix, with at least elements on index
+ * 0 to K-1; elements on index>=K are ignored. Vector R must not have zero
+ * elements, no check is done.
+ */
+void lcl_SolveWithUpperRightTriangle(const ScMatrixRef& pMatA,
+ ::std::vector< double>& pVecR, const ScMatrixRef& pMatS,
+ SCSIZE nK, bool bIsTransposed)
+{
+ // ScMatrix matrices are zero based, index access (column,row)
+ SCSIZE row;
+ // SCSIZE is never negative, therefore test with rowp1=row+1
+ for (SCSIZE rowp1 = nK; rowp1>0; rowp1--)
+ {
+ row = rowp1-1;
+ KahanSum fSum = pMatS->GetDouble(row);
+ for (SCSIZE col = rowp1; col<nK ; col++)
+ if (bIsTransposed)
+ fSum -= pMatA->GetDouble(row,col) * pMatS->GetDouble(col);
+ else
+ fSum -= pMatA->GetDouble(col,row) * pMatS->GetDouble(col);
+ pMatS->PutDouble( fSum.get() / pVecR[row] , row);
+ }
+}
+
+/* Solve for X in R' * X= T using forward substitution. The solution X
+ * overwrites T. Uses R from the result of the QR decomposition of a NxK
+ * matrix A. T is a column vectors given as matrix, with at least elements on
+ * index 0 to K-1; elements on index>=K are ignored. Vector R must not have
+ * zero elements, no check is done.
+ */
+void lcl_SolveWithLowerLeftTriangle(const ScMatrixRef& pMatA,
+ ::std::vector< double>& pVecR, const ScMatrixRef& pMatT,
+ SCSIZE nK, bool bIsTransposed)
+{
+ // ScMatrix matrices are zero based, index access (column,row)
+ for (SCSIZE row = 0; row < nK; row++)
+ {
+ KahanSum fSum = pMatT -> GetDouble(row);
+ for (SCSIZE col=0; col < row; col++)
+ {
+ if (bIsTransposed)
+ fSum -= pMatA->GetDouble(col,row) * pMatT->GetDouble(col);
+ else
+ fSum -= pMatA->GetDouble(row,col) * pMatT->GetDouble(col);
+ }
+ pMatT->PutDouble( fSum.get() / pVecR[row] , row);
+ }
+}
+
+/* Calculates Z = R * B
+ * R is given in matrix A and vector VecR as obtained from the QR
+ * decomposition in lcl_CalculateQRdecomposition. B and Z are column vectors
+ * given as matrix with at least index 0 to K-1; elements on index>=K are
+ * not used.
+ */
+void lcl_ApplyUpperRightTriangle(const ScMatrixRef& pMatA,
+ ::std::vector< double>& pVecR, const ScMatrixRef& pMatB,
+ const ScMatrixRef& pMatZ, SCSIZE nK, bool bIsTransposed)
+{
+ // ScMatrix matrices are zero based, index access (column,row)
+ for (SCSIZE row = 0; row < nK; row++)
+ {
+ KahanSum fSum = pVecR[row] * pMatB->GetDouble(row);
+ for (SCSIZE col = row+1; col < nK; col++)
+ if (bIsTransposed)
+ fSum += pMatA->GetDouble(row,col) * pMatB->GetDouble(col);
+ else
+ fSum += pMatA->GetDouble(col,row) * pMatB->GetDouble(col);
+ pMatZ->PutDouble( fSum.get(), row);
+ }
+}
+
+double lcl_GetMeanOverAll(const ScMatrixRef& pMat, SCSIZE nN)
+{
+ KahanSum fSum = 0.0;
+ for (SCSIZE i=0 ; i<nN; i++)
+ fSum += pMat->GetDouble(i);
+ return fSum.get()/static_cast<double>(nN);
+}
+
+// Calculates means of the columns of matrix X. X is a RxC matrix;
+// ResMat is a 1xC matrix (=row).
+void lcl_CalculateColumnMeans(const ScMatrixRef& pX, const ScMatrixRef& pResMat,
+ SCSIZE nC, SCSIZE nR)
+{
+ for (SCSIZE i=0; i < nC; i++)
+ {
+ KahanSum fSum =0.0;
+ for (SCSIZE k=0; k < nR; k++)
+ fSum += pX->GetDouble(i,k); // GetDouble(Column,Row)
+ pResMat ->PutDouble( fSum.get()/static_cast<double>(nR),i);
+ }
+}
+
+// Calculates means of the rows of matrix X. X is a RxC matrix;
+// ResMat is a Rx1 matrix (=column).
+void lcl_CalculateRowMeans(const ScMatrixRef& pX, const ScMatrixRef& pResMat,
+ SCSIZE nC, SCSIZE nR)
+{
+ for (SCSIZE k=0; k < nR; k++)
+ {
+ KahanSum fSum = 0.0;
+ for (SCSIZE i=0; i < nC; i++)
+ fSum += pX->GetDouble(i,k); // GetDouble(Column,Row)
+ pResMat ->PutDouble( fSum.get()/static_cast<double>(nC),k);
+ }
+}
+
+void lcl_CalculateColumnsDelta(const ScMatrixRef& pMat, const ScMatrixRef& pColumnMeans,
+ SCSIZE nC, SCSIZE nR)
+{
+ for (SCSIZE i = 0; i < nC; i++)
+ for (SCSIZE k = 0; k < nR; k++)
+ pMat->PutDouble( ::rtl::math::approxSub
+ (pMat->GetDouble(i,k) , pColumnMeans->GetDouble(i) ) , i, k);
+}
+
+void lcl_CalculateRowsDelta(const ScMatrixRef& pMat, const ScMatrixRef& pRowMeans,
+ SCSIZE nC, SCSIZE nR)
+{
+ for (SCSIZE k = 0; k < nR; k++)
+ for (SCSIZE i = 0; i < nC; i++)
+ pMat->PutDouble( ::rtl::math::approxSub
+ ( pMat->GetDouble(i,k) , pRowMeans->GetDouble(k) ) , i, k);
+}
+
+// Case1 = simple regression
+// MatX = X - MeanX, MatY = Y - MeanY, y - haty = (y - MeanY) - (haty - MeanY)
+// = (y-MeanY)-((slope*x+a)-(slope*MeanX+a)) = (y-MeanY)-slope*(x-MeanX)
+double lcl_GetSSresid(const ScMatrixRef& pMatX, const ScMatrixRef& pMatY, double fSlope,
+ SCSIZE nN)
+{
+ KahanSum fSum = 0.0;
+ for (SCSIZE i=0; i<nN; i++)
+ {
+ const double fTemp = pMatY->GetDouble(i) - fSlope * pMatX->GetDouble(i);
+ fSum += fTemp * fTemp;
+ }
+ return fSum.get();
+}
+
+}
+
+// Fill default values in matrix X, transform Y to log(Y) in case LOGEST|GROWTH,
+// determine sizes of matrices X and Y, determine kind of regression, clone
+// Y in case LOGEST|GROWTH, if constant.
+bool ScInterpreter::CheckMatrix(bool _bLOG, sal_uInt8& nCase, SCSIZE& nCX,
+ SCSIZE& nCY, SCSIZE& nRX, SCSIZE& nRY, SCSIZE& M,
+ SCSIZE& N, ScMatrixRef& pMatX, ScMatrixRef& pMatY)
+{
+
+ nCX = 0;
+ nCY = 0;
+ nRX = 0;
+ nRY = 0;
+ M = 0;
+ N = 0;
+ pMatY->GetDimensions(nCY, nRY);
+ const SCSIZE nCountY = nCY * nRY;
+ for ( SCSIZE i = 0; i < nCountY; i++ )
+ {
+ if (!pMatY->IsValue(i))
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ }
+
+ if ( _bLOG )
+ {
+ ScMatrixRef pNewY = pMatY->CloneIfConst();
+ for (SCSIZE nElem = 0; nElem < nCountY; nElem++)
+ {
+ const double fVal = pNewY->GetDouble(nElem);
+ if (fVal <= 0.0)
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ else
+ pNewY->PutDouble(log(fVal), nElem);
+ }
+ pMatY = pNewY;
+ }
+
+ if (pMatX)
+ {
+ pMatX->GetDimensions(nCX, nRX);
+ const SCSIZE nCountX = nCX * nRX;
+ for ( SCSIZE i = 0; i < nCountX; i++ )
+ if (!pMatX->IsValue(i))
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ if (nCX == nCY && nRX == nRY)
+ {
+ nCase = 1; // simple regression
+ M = 1;
+ N = nCountY;
+ }
+ else if (nCY != 1 && nRY != 1)
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ else if (nCY == 1)
+ {
+ if (nRX != nRY)
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ else
+ {
+ nCase = 2; // Y is column
+ N = nRY;
+ M = nCX;
+ }
+ }
+ else if (nCX != nCY)
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ else
+ {
+ nCase = 3; // Y is row
+ N = nCY;
+ M = nRX;
+ }
+ }
+ else
+ {
+ pMatX = GetNewMat(nCY, nRY, /*bEmpty*/true);
+ nCX = nCY;
+ nRX = nRY;
+ if (!pMatX)
+ {
+ PushIllegalArgument();
+ return false;
+ }
+ for ( SCSIZE i = 1; i <= nCountY; i++ )
+ pMatX->PutDouble(static_cast<double>(i), i-1);
+ nCase = 1;
+ N = nCountY;
+ M = 1;
+ }
+ return true;
+}
+
+// LINEST
+void ScInterpreter::ScLinest()
+{
+ CalculateRGPRKP(false);
+}
+
+// LOGEST
+void ScInterpreter::ScLogest()
+{
+ CalculateRGPRKP(true);
+}
+
+void ScInterpreter::CalculateRGPRKP(bool _bRKP)
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount( nParamCount, 1, 4 ))
+ return;
+ bool bConstant, bStats;
+
+ // optional forth parameter
+ if (nParamCount == 4)
+ bStats = GetBool();
+ else
+ bStats = false;
+
+ // The third parameter may not be missing in ODF, if the forth parameter
+ // is present. But Excel allows it with default true, we too.
+ if (nParamCount >= 3)
+ {
+ if (IsMissing())
+ {
+ Pop();
+ bConstant = true;
+// PushIllegalParameter(); if ODF behavior is desired
+// return;
+ }
+ else
+ bConstant = GetBool();
+ }
+ else
+ bConstant = true;
+
+ ScMatrixRef pMatX;
+ if (nParamCount >= 2)
+ {
+ if (IsMissing())
+ { //In ODF1.2 empty second parameter (which is two ;; ) is allowed
+ Pop();
+ pMatX = nullptr;
+ }
+ else
+ {
+ pMatX = GetMatrix();
+ }
+ }
+ else
+ pMatX = nullptr;
+
+ ScMatrixRef pMatY = GetMatrix();
+ if (!pMatY)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ // 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
+ sal_uInt8 nCase;
+
+ SCSIZE nCX, nCY; // number of columns
+ SCSIZE nRX, nRY; //number of rows
+ SCSIZE K = 0, N = 0; // K=number of variables X, N=number of data samples
+ if (!CheckMatrix(_bRKP,nCase,nCX,nCY,nRX,nRY,K,N,pMatX,pMatY))
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ // Enough data samples?
+ if ((bConstant && (N<K+1)) || (!bConstant && (N<K)) || (N<1) || (K<1))
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ ScMatrixRef pResMat;
+ if (bStats)
+ pResMat = GetNewMat(K+1,5, /*bEmpty*/true);
+ else
+ pResMat = GetNewMat(K+1,1, /*bEmpty*/true);
+ if (!pResMat)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ // Fill unused cells in pResMat; order (column,row)
+ if (bStats)
+ {
+ for (SCSIZE i=2; i<K+1; i++)
+ {
+ pResMat->PutError( FormulaError::NotAvailable, i, 2);
+ pResMat->PutError( FormulaError::NotAvailable, i, 3);
+ pResMat->PutError( FormulaError::NotAvailable, i, 4);
+ }
+ }
+
+ // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant.
+ // Clone constant matrices, so that Mat = Mat - Mean is possible.
+ double fMeanY = 0.0;
+ if (bConstant)
+ {
+ ScMatrixRef pNewX = pMatX->CloneIfConst();
+ ScMatrixRef pNewY = pMatY->CloneIfConst();
+ if (!pNewX || !pNewY)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ pMatX = pNewX;
+ pMatY = pNewY;
+ // DeltaY is possible here; DeltaX depends on nCase, so later
+ fMeanY = lcl_GetMeanOverAll(pMatY, N);
+ for (SCSIZE i=0; i<N; i++)
+ {
+ pMatY->PutDouble( ::rtl::math::approxSub(pMatY->GetDouble(i),fMeanY), i );
+ }
+ }
+
+ if (nCase==1)
+ {
+ // calculate simple regression
+ double fMeanX = 0.0;
+ if (bConstant)
+ { // Mat = Mat - Mean
+ fMeanX = lcl_GetMeanOverAll(pMatX, N);
+ for (SCSIZE i=0; i<N; i++)
+ {
+ pMatX->PutDouble( ::rtl::math::approxSub(pMatX->GetDouble(i),fMeanX), i );
+ }
+ }
+ double fSumXY = lcl_GetSumProduct(pMatX,pMatY,N);
+ double fSumX2 = lcl_GetSumProduct(pMatX,pMatX,N);
+ if (fSumX2==0.0)
+ {
+ PushNoValue(); // all x-values are identical
+ return;
+ }
+ double fSlope = fSumXY / fSumX2;
+ double fIntercept = 0.0;
+ if (bConstant)
+ fIntercept = fMeanY - fSlope * fMeanX;
+ pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, 1, 0); //order (column,row)
+ pResMat->PutDouble(_bRKP ? exp(fSlope) : fSlope, 0, 0);
+
+ if (bStats)
+ {
+ double fSSreg = fSlope * fSlope * fSumX2;
+ pResMat->PutDouble(fSSreg, 0, 4);
+
+ double fDegreesFreedom =static_cast<double>( bConstant ? N-2 : N-1 );
+ pResMat->PutDouble(fDegreesFreedom, 1, 3);
+
+ double fSSresid = lcl_GetSSresid(pMatX,pMatY,fSlope,N);
+ pResMat->PutDouble(fSSresid, 1, 4);
+
+ if (fDegreesFreedom == 0.0 || fSSresid == 0.0 || fSSreg == 0.0)
+ { // exact fit; test SSreg too, because SSresid might be
+ // unequal zero due to round of errors
+ pResMat->PutDouble(0.0, 1, 4); // SSresid
+ pResMat->PutError( FormulaError::NotAvailable, 0, 3); // F
+ pResMat->PutDouble(0.0, 1, 2); // RMSE
+ pResMat->PutDouble(0.0, 0, 1); // SigmaSlope
+ if (bConstant)
+ pResMat->PutDouble(0.0, 1, 1); //SigmaIntercept
+ else
+ pResMat->PutError( FormulaError::NotAvailable, 1, 1);
+ pResMat->PutDouble(1.0, 0, 2); // R^2
+ }
+ else
+ {
+ double fFstatistic = (fSSreg / static_cast<double>(K))
+ / (fSSresid / fDegreesFreedom);
+ pResMat->PutDouble(fFstatistic, 0, 3);
+
+ // standard error of estimate
+ double fRMSE = sqrt(fSSresid / fDegreesFreedom);
+ pResMat->PutDouble(fRMSE, 1, 2);
+
+ double fSigmaSlope = fRMSE / sqrt(fSumX2);
+ pResMat->PutDouble(fSigmaSlope, 0, 1);
+
+ if (bConstant)
+ {
+ double fSigmaIntercept = fRMSE
+ * sqrt(fMeanX*fMeanX/fSumX2 + 1.0/static_cast<double>(N));
+ pResMat->PutDouble(fSigmaIntercept, 1, 1);
+ }
+ else
+ {
+ pResMat->PutError( FormulaError::NotAvailable, 1, 1);
+ }
+
+ double fR2 = fSSreg / (fSSreg + fSSresid);
+ pResMat->PutDouble(fR2, 0, 2);
+ }
+ }
+ PushMatrix(pResMat);
+ }
+ else // calculate multiple regression;
+ {
+ // Uses a QR decomposition X = QR. The solution B = (X'X)^(-1) * X' * Y
+ // becomes B = R^(-1) * Q' * Y
+ if (nCase ==2) // Y is column
+ {
+ ::std::vector< double> aVecR(N); // for QR decomposition
+ // Enough memory for needed matrices?
+ ScMatrixRef pMeans = GetNewMat(K, 1, /*bEmpty*/true); // mean of each column
+ ScMatrixRef pMatZ; // for Q' * Y , inter alia
+ if (bStats)
+ pMatZ = pMatY->Clone(); // Y is used in statistic, keep it
+ else
+ pMatZ = pMatY; // Y can be overwritten
+ ScMatrixRef pSlopes = GetNewMat(1,K, /*bEmpty*/true); // from b1 to bK
+ if (!pMeans || !pMatZ || !pSlopes)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ if (bConstant)
+ {
+ lcl_CalculateColumnMeans(pMatX, pMeans, K, N);
+ lcl_CalculateColumnsDelta(pMatX, pMeans, K, N);
+ }
+ if (!lcl_CalculateQRdecomposition(pMatX, aVecR, K, N))
+ {
+ PushNoValue();
+ return;
+ }
+ // Later on we will divide by elements of aVecR, so make sure
+ // that they aren't zero.
+ bool bIsSingular=false;
+ for (SCSIZE row=0; row < K && !bIsSingular; row++)
+ bIsSingular = aVecR[row] == 0.0;
+ if (bIsSingular)
+ {
+ PushNoValue();
+ return;
+ }
+ // Z = Q' Y;
+ for (SCSIZE col = 0; col < K; col++)
+ {
+ lcl_ApplyHouseholderTransformation(pMatX, col, pMatZ, N);
+ }
+ // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
+ // result Z should have zeros for index>=K; if not, ignore values
+ for (SCSIZE col = 0; col < K ; col++)
+ {
+ pSlopes->PutDouble( pMatZ->GetDouble(col), col);
+ }
+ lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, false);
+ double fIntercept = 0.0;
+ if (bConstant)
+ fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
+ // Fill first line in result matrix
+ pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, K, 0 );
+ for (SCSIZE i = 0; i < K; i++)
+ pResMat->PutDouble(_bRKP ? exp(pSlopes->GetDouble(i))
+ : pSlopes->GetDouble(i) , K-1-i, 0);
+
+ if (bStats)
+ {
+ double fSSreg = 0.0;
+ double fSSresid = 0.0;
+ // re-use memory of Z;
+ pMatZ->FillDouble(0.0, 0, 0, 0, N-1);
+ // Z = R * Slopes
+ lcl_ApplyUpperRightTriangle(pMatX, aVecR, pSlopes, pMatZ, K, false);
+ // Z = Q * Z, that is Q * R * Slopes = X * Slopes
+ for (SCSIZE colp1 = K; colp1 > 0; colp1--)
+ {
+ lcl_ApplyHouseholderTransformation(pMatX, colp1-1, pMatZ,N);
+ }
+ fSSreg =lcl_GetSumProduct(pMatZ, pMatZ, N);
+ // re-use Y for residuals, Y = Y-Z
+ for (SCSIZE row = 0; row < N; row++)
+ pMatY->PutDouble(pMatY->GetDouble(row) - pMatZ->GetDouble(row), row);
+ fSSresid = lcl_GetSumProduct(pMatY, pMatY, N);
+ pResMat->PutDouble(fSSreg, 0, 4);
+ pResMat->PutDouble(fSSresid, 1, 4);
+
+ double fDegreesFreedom =static_cast<double>( bConstant ? N-K-1 : N-K );
+ pResMat->PutDouble(fDegreesFreedom, 1, 3);
+
+ if (fDegreesFreedom == 0.0 || fSSresid == 0.0 || fSSreg == 0.0)
+ { // exact fit; incl. observed values Y are identical
+ pResMat->PutDouble(0.0, 1, 4); // SSresid
+ // F = (SSreg/K) / (SSresid/df) = #DIV/0!
+ pResMat->PutError( FormulaError::NotAvailable, 0, 3); // F
+ // RMSE = sqrt(SSresid / df) = sqrt(0 / df) = 0
+ pResMat->PutDouble(0.0, 1, 2); // RMSE
+ // SigmaSlope[i] = RMSE * sqrt(matrix[i,i]) = 0 * sqrt(...) = 0
+ for (SCSIZE i=0; i<K; i++)
+ pResMat->PutDouble(0.0, K-1-i, 1);
+
+ // SigmaIntercept = RMSE * sqrt(...) = 0
+ if (bConstant)
+ pResMat->PutDouble(0.0, K, 1); //SigmaIntercept
+ else
+ pResMat->PutError( FormulaError::NotAvailable, K, 1);
+
+ // R^2 = SSreg / (SSreg + SSresid) = 1.0
+ pResMat->PutDouble(1.0, 0, 2); // R^2
+ }
+ else
+ {
+ double fFstatistic = (fSSreg / static_cast<double>(K))
+ / (fSSresid / fDegreesFreedom);
+ pResMat->PutDouble(fFstatistic, 0, 3);
+
+ // standard error of estimate = root mean SSE
+ double fRMSE = sqrt(fSSresid / fDegreesFreedom);
+ pResMat->PutDouble(fRMSE, 1, 2);
+
+ // standard error of slopes
+ // = RMSE * sqrt(diagonal element of (R' R)^(-1) )
+ // standard error of intercept
+ // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N)
+ // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as
+ // a whole matrix, but iterate over unit vectors.
+ KahanSum aSigmaIntercept = 0.0;
+ double fPart; // for Xmean * single column of (R' R)^(-1)
+ for (SCSIZE col = 0; col < K; col++)
+ {
+ //re-use memory of MatZ
+ pMatZ->FillDouble(0.0,0,0,0,K-1); // Z = unit vector e
+ pMatZ->PutDouble(1.0, col);
+ //Solve R' * Z = e
+ lcl_SolveWithLowerLeftTriangle(pMatX, aVecR, pMatZ, K, false);
+ // Solve R * Znew = Zold
+ lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pMatZ, K, false);
+ // now Z is column col in (R' R)^(-1)
+ double fSigmaSlope = fRMSE * sqrt(pMatZ->GetDouble(col));
+ pResMat->PutDouble(fSigmaSlope, K-1-col, 1);
+ // (R' R) ^(-1) is symmetric
+ if (bConstant)
+ {
+ fPart = lcl_GetSumProduct(pMeans, pMatZ, K);
+ aSigmaIntercept += fPart * pMeans->GetDouble(col);
+ }
+ }
+ if (bConstant)
+ {
+ double fSigmaIntercept = fRMSE
+ * sqrt( (aSigmaIntercept + 1.0 / static_cast<double>(N) ).get() );
+ pResMat->PutDouble(fSigmaIntercept, K, 1);
+ }
+ else
+ {
+ pResMat->PutError( FormulaError::NotAvailable, K, 1);
+ }
+
+ double fR2 = fSSreg / (fSSreg + fSSresid);
+ pResMat->PutDouble(fR2, 0, 2);
+ }
+ }
+ PushMatrix(pResMat);
+ }
+ else // nCase == 3, Y is row, all matrices are transposed
+ {
+ ::std::vector< double> aVecR(N); // for QR decomposition
+ // Enough memory for needed matrices?
+ ScMatrixRef pMeans = GetNewMat(1, K, /*bEmpty*/true); // mean of each row
+ ScMatrixRef pMatZ; // for Q' * Y , inter alia
+ if (bStats)
+ pMatZ = pMatY->Clone(); // Y is used in statistic, keep it
+ else
+ pMatZ = pMatY; // Y can be overwritten
+ ScMatrixRef pSlopes = GetNewMat(K,1, /*bEmpty*/true); // from b1 to bK
+ if (!pMeans || !pMatZ || !pSlopes)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ if (bConstant)
+ {
+ lcl_CalculateRowMeans(pMatX, pMeans, N, K);
+ lcl_CalculateRowsDelta(pMatX, pMeans, N, K);
+ }
+
+ if (!lcl_TCalculateQRdecomposition(pMatX, aVecR, K, N))
+ {
+ PushNoValue();
+ return;
+ }
+
+ // Later on we will divide by elements of aVecR, so make sure
+ // that they aren't zero.
+ bool bIsSingular=false;
+ for (SCSIZE row=0; row < K && !bIsSingular; row++)
+ bIsSingular = aVecR[row] == 0.0;
+ if (bIsSingular)
+ {
+ PushNoValue();
+ return;
+ }
+ // Z = Q' Y
+ for (SCSIZE row = 0; row < K; row++)
+ {
+ lcl_TApplyHouseholderTransformation(pMatX, row, pMatZ, N);
+ }
+ // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
+ // result Z should have zeros for index>=K; if not, ignore values
+ for (SCSIZE col = 0; col < K ; col++)
+ {
+ pSlopes->PutDouble( pMatZ->GetDouble(col), col);
+ }
+ lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, true);
+ double fIntercept = 0.0;
+ if (bConstant)
+ fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
+ // Fill first line in result matrix
+ pResMat->PutDouble(_bRKP ? exp(fIntercept) : fIntercept, K, 0 );
+ for (SCSIZE i = 0; i < K; i++)
+ pResMat->PutDouble(_bRKP ? exp(pSlopes->GetDouble(i))
+ : pSlopes->GetDouble(i) , K-1-i, 0);
+
+ if (bStats)
+ {
+ double fSSreg = 0.0;
+ double fSSresid = 0.0;
+ // re-use memory of Z;
+ pMatZ->FillDouble(0.0, 0, 0, N-1, 0);
+ // Z = R * Slopes
+ lcl_ApplyUpperRightTriangle(pMatX, aVecR, pSlopes, pMatZ, K, true);
+ // Z = Q * Z, that is Q * R * Slopes = X * Slopes
+ for (SCSIZE rowp1 = K; rowp1 > 0; rowp1--)
+ {
+ lcl_TApplyHouseholderTransformation(pMatX, rowp1-1, pMatZ,N);
+ }
+ fSSreg =lcl_GetSumProduct(pMatZ, pMatZ, N);
+ // re-use Y for residuals, Y = Y-Z
+ for (SCSIZE col = 0; col < N; col++)
+ pMatY->PutDouble(pMatY->GetDouble(col) - pMatZ->GetDouble(col), col);
+ fSSresid = lcl_GetSumProduct(pMatY, pMatY, N);
+ pResMat->PutDouble(fSSreg, 0, 4);
+ pResMat->PutDouble(fSSresid, 1, 4);
+
+ double fDegreesFreedom =static_cast<double>( bConstant ? N-K-1 : N-K );
+ pResMat->PutDouble(fDegreesFreedom, 1, 3);
+
+ if (fDegreesFreedom == 0.0 || fSSresid == 0.0 || fSSreg == 0.0)
+ { // exact fit; incl. case observed values Y are identical
+ pResMat->PutDouble(0.0, 1, 4); // SSresid
+ // F = (SSreg/K) / (SSresid/df) = #DIV/0!
+ pResMat->PutError( FormulaError::NotAvailable, 0, 3); // F
+ // RMSE = sqrt(SSresid / df) = sqrt(0 / df) = 0
+ pResMat->PutDouble(0.0, 1, 2); // RMSE
+ // SigmaSlope[i] = RMSE * sqrt(matrix[i,i]) = 0 * sqrt(...) = 0
+ for (SCSIZE i=0; i<K; i++)
+ pResMat->PutDouble(0.0, K-1-i, 1);
+
+ // SigmaIntercept = RMSE * sqrt(...) = 0
+ if (bConstant)
+ pResMat->PutDouble(0.0, K, 1); //SigmaIntercept
+ else
+ pResMat->PutError( FormulaError::NotAvailable, K, 1);
+
+ // R^2 = SSreg / (SSreg + SSresid) = 1.0
+ pResMat->PutDouble(1.0, 0, 2); // R^2
+ }
+ else
+ {
+ double fFstatistic = (fSSreg / static_cast<double>(K))
+ / (fSSresid / fDegreesFreedom);
+ pResMat->PutDouble(fFstatistic, 0, 3);
+
+ // standard error of estimate = root mean SSE
+ double fRMSE = sqrt(fSSresid / fDegreesFreedom);
+ pResMat->PutDouble(fRMSE, 1, 2);
+
+ // standard error of slopes
+ // = RMSE * sqrt(diagonal element of (R' R)^(-1) )
+ // standard error of intercept
+ // = RMSE * sqrt( Xmean * (R' R)^(-1) * Xmean' + 1/N)
+ // (R' R)^(-1) = R^(-1) * (R')^(-1). Do not calculate it as
+ // a whole matrix, but iterate over unit vectors.
+ // (R' R) ^(-1) is symmetric
+ KahanSum aSigmaIntercept = 0.0;
+ double fPart; // for Xmean * single col of (R' R)^(-1)
+ for (SCSIZE row = 0; row < K; row++)
+ {
+ //re-use memory of MatZ
+ pMatZ->FillDouble(0.0,0,0,K-1,0); // Z = unit vector e
+ pMatZ->PutDouble(1.0, row);
+ //Solve R' * Z = e
+ lcl_SolveWithLowerLeftTriangle(pMatX, aVecR, pMatZ, K, true);
+ // Solve R * Znew = Zold
+ lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pMatZ, K, true);
+ // now Z is column col in (R' R)^(-1)
+ double fSigmaSlope = fRMSE * sqrt(pMatZ->GetDouble(row));
+ pResMat->PutDouble(fSigmaSlope, K-1-row, 1);
+ if (bConstant)
+ {
+ fPart = lcl_GetSumProduct(pMeans, pMatZ, K);
+ aSigmaIntercept += fPart * pMeans->GetDouble(row);
+ }
+ }
+ if (bConstant)
+ {
+ double fSigmaIntercept = fRMSE
+ * sqrt( (aSigmaIntercept + 1.0 / static_cast<double>(N) ).get() );
+ pResMat->PutDouble(fSigmaIntercept, K, 1);
+ }
+ else
+ {
+ pResMat->PutError( FormulaError::NotAvailable, K, 1);
+ }
+
+ double fR2 = fSSreg / (fSSreg + fSSresid);
+ pResMat->PutDouble(fR2, 0, 2);
+ }
+ }
+ PushMatrix(pResMat);
+ }
+ }
+}
+
+void ScInterpreter::ScTrend()
+{
+ CalculateTrendGrowth(false);
+}
+
+void ScInterpreter::ScGrowth()
+{
+ CalculateTrendGrowth(true);
+}
+
+void ScInterpreter::CalculateTrendGrowth(bool _bGrowth)
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount( nParamCount, 1, 4 ))
+ return;
+
+ // optional forth parameter
+ bool bConstant;
+ if (nParamCount == 4)
+ bConstant = GetBool();
+ else
+ bConstant = true;
+
+ // The third parameter may be missing in ODF, although the forth parameter
+ // is present. Default values depend on data not yet read.
+ ScMatrixRef pMatNewX;
+ if (nParamCount >= 3)
+ {
+ if (IsMissing())
+ {
+ Pop();
+ pMatNewX = nullptr;
+ }
+ else
+ pMatNewX = GetMatrix();
+ }
+ else
+ pMatNewX = nullptr;
+
+ //In ODF1.2 empty second parameter (which is two ;; ) is allowed
+ //Defaults will be set in CheckMatrix
+ ScMatrixRef pMatX;
+ if (nParamCount >= 2)
+ {
+ if (IsMissing())
+ {
+ Pop();
+ pMatX = nullptr;
+ }
+ else
+ {
+ pMatX = GetMatrix();
+ }
+ }
+ else
+ pMatX = nullptr;
+
+ ScMatrixRef pMatY = GetMatrix();
+ if (!pMatY)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ // 1 = simple; 2 = multiple with Y as column; 3 = multiple with Y as row
+ sal_uInt8 nCase;
+
+ SCSIZE nCX, nCY; // number of columns
+ SCSIZE nRX, nRY; //number of rows
+ SCSIZE K = 0, N = 0; // K=number of variables X, N=number of data samples
+ if (!CheckMatrix(_bGrowth,nCase,nCX,nCY,nRX,nRY,K,N,pMatX,pMatY))
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ // Enough data samples?
+ if ((bConstant && (N<K+1)) || (!bConstant && (N<K)) || (N<1) || (K<1))
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ // Set default pMatNewX if necessary
+ SCSIZE nCXN, nRXN;
+ SCSIZE nCountXN;
+ if (!pMatNewX)
+ {
+ nCXN = nCX;
+ nRXN = nRX;
+ nCountXN = nCXN * nRXN;
+ pMatNewX = pMatX->Clone(); // pMatX will be changed to X-meanX
+ }
+ else
+ {
+ pMatNewX->GetDimensions(nCXN, nRXN);
+ if ((nCase == 2 && K != nCXN) || (nCase == 3 && K != nRXN))
+ {
+ PushIllegalArgument();
+ return;
+ }
+ nCountXN = nCXN * nRXN;
+ for (SCSIZE i = 0; i < nCountXN; i++)
+ if (!pMatNewX->IsValue(i))
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ ScMatrixRef pResMat; // size depends on nCase
+ if (nCase == 1)
+ pResMat = GetNewMat(nCXN,nRXN, /*bEmpty*/true);
+ else
+ {
+ if (nCase==2)
+ pResMat = GetNewMat(1,nRXN, /*bEmpty*/true);
+ else
+ pResMat = GetNewMat(nCXN,1, /*bEmpty*/true);
+ }
+ if (!pResMat)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ // Uses sum(x-MeanX)^2 and not [sum x^2]-N * MeanX^2 in case bConstant.
+ // Clone constant matrices, so that Mat = Mat - Mean is possible.
+ double fMeanY = 0.0;
+ if (bConstant)
+ {
+ ScMatrixRef pCopyX = pMatX->CloneIfConst();
+ ScMatrixRef pCopyY = pMatY->CloneIfConst();
+ if (!pCopyX || !pCopyY)
+ {
+ PushError(FormulaError::MatrixSize);
+ return;
+ }
+ pMatX = pCopyX;
+ pMatY = pCopyY;
+ // DeltaY is possible here; DeltaX depends on nCase, so later
+ fMeanY = lcl_GetMeanOverAll(pMatY, N);
+ for (SCSIZE i=0; i<N; i++)
+ {
+ pMatY->PutDouble( ::rtl::math::approxSub(pMatY->GetDouble(i),fMeanY), i );
+ }
+ }
+
+ if (nCase==1)
+ {
+ // calculate simple regression
+ double fMeanX = 0.0;
+ if (bConstant)
+ { // Mat = Mat - Mean
+ fMeanX = lcl_GetMeanOverAll(pMatX, N);
+ for (SCSIZE i=0; i<N; i++)
+ {
+ pMatX->PutDouble( ::rtl::math::approxSub(pMatX->GetDouble(i),fMeanX), i );
+ }
+ }
+ double fSumXY = lcl_GetSumProduct(pMatX,pMatY,N);
+ double fSumX2 = lcl_GetSumProduct(pMatX,pMatX,N);
+ if (fSumX2==0.0)
+ {
+ PushNoValue(); // all x-values are identical
+ return;
+ }
+ double fSlope = fSumXY / fSumX2;
+ double fHelp;
+ if (bConstant)
+ {
+ double fIntercept = fMeanY - fSlope * fMeanX;
+ for (SCSIZE i = 0; i < nCountXN; i++)
+ {
+ fHelp = pMatNewX->GetDouble(i)*fSlope + fIntercept;
+ pResMat->PutDouble(_bGrowth ? exp(fHelp) : fHelp, i);
+ }
+ }
+ else
+ {
+ for (SCSIZE i = 0; i < nCountXN; i++)
+ {
+ fHelp = pMatNewX->GetDouble(i)*fSlope;
+ pResMat->PutDouble(_bGrowth ? exp(fHelp) : fHelp, i);
+ }
+ }
+ }
+ else // calculate multiple regression;
+ {
+ if (nCase ==2) // Y is column
+ {
+ ::std::vector< double> aVecR(N); // for QR decomposition
+ // Enough memory for needed matrices?
+ ScMatrixRef pMeans = GetNewMat(K, 1, /*bEmpty*/true); // mean of each column
+ ScMatrixRef pSlopes = GetNewMat(1,K, /*bEmpty*/true); // from b1 to bK
+ if (!pMeans || !pSlopes)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ if (bConstant)
+ {
+ lcl_CalculateColumnMeans(pMatX, pMeans, K, N);
+ lcl_CalculateColumnsDelta(pMatX, pMeans, K, N);
+ }
+ if (!lcl_CalculateQRdecomposition(pMatX, aVecR, K, N))
+ {
+ PushNoValue();
+ return;
+ }
+ // Later on we will divide by elements of aVecR, so make sure
+ // that they aren't zero.
+ bool bIsSingular=false;
+ for (SCSIZE row=0; row < K && !bIsSingular; row++)
+ bIsSingular = aVecR[row] == 0.0;
+ if (bIsSingular)
+ {
+ PushNoValue();
+ return;
+ }
+ // Z := Q' Y; Y is overwritten with result Z
+ for (SCSIZE col = 0; col < K; col++)
+ {
+ lcl_ApplyHouseholderTransformation(pMatX, col, pMatY, N);
+ }
+ // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
+ // result Z should have zeros for index>=K; if not, ignore values
+ for (SCSIZE col = 0; col < K ; col++)
+ {
+ pSlopes->PutDouble( pMatY->GetDouble(col), col);
+ }
+ lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, false);
+
+ // Fill result matrix
+ lcl_MFastMult(pMatNewX,pSlopes,pResMat,nRXN,K,1);
+ if (bConstant)
+ {
+ double fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
+ for (SCSIZE row = 0; row < nRXN; row++)
+ pResMat->PutDouble(pResMat->GetDouble(row)+fIntercept, row);
+ }
+ if (_bGrowth)
+ {
+ for (SCSIZE i = 0; i < nRXN; i++)
+ pResMat->PutDouble(exp(pResMat->GetDouble(i)), i);
+ }
+ }
+ else
+ { // nCase == 3, Y is row, all matrices are transposed
+
+ ::std::vector< double> aVecR(N); // for QR decomposition
+ // Enough memory for needed matrices?
+ ScMatrixRef pMeans = GetNewMat(1, K, /*bEmpty*/true); // mean of each row
+ ScMatrixRef pSlopes = GetNewMat(K,1, /*bEmpty*/true); // row from b1 to bK
+ if (!pMeans || !pSlopes)
+ {
+ PushError(FormulaError::CodeOverflow);
+ return;
+ }
+ if (bConstant)
+ {
+ lcl_CalculateRowMeans(pMatX, pMeans, N, K);
+ lcl_CalculateRowsDelta(pMatX, pMeans, N, K);
+ }
+ if (!lcl_TCalculateQRdecomposition(pMatX, aVecR, K, N))
+ {
+ PushNoValue();
+ return;
+ }
+ // Later on we will divide by elements of aVecR, so make sure
+ // that they aren't zero.
+ bool bIsSingular=false;
+ for (SCSIZE row=0; row < K && !bIsSingular; row++)
+ bIsSingular = aVecR[row] == 0.0;
+ if (bIsSingular)
+ {
+ PushNoValue();
+ return;
+ }
+ // Z := Q' Y; Y is overwritten with result Z
+ for (SCSIZE row = 0; row < K; row++)
+ {
+ lcl_TApplyHouseholderTransformation(pMatX, row, pMatY, N);
+ }
+ // B = R^(-1) * Q' * Y <=> B = R^(-1) * Z <=> R * B = Z
+ // result Z should have zeros for index>=K; if not, ignore values
+ for (SCSIZE col = 0; col < K ; col++)
+ {
+ pSlopes->PutDouble( pMatY->GetDouble(col), col);
+ }
+ lcl_SolveWithUpperRightTriangle(pMatX, aVecR, pSlopes, K, true);
+
+ // Fill result matrix
+ lcl_MFastMult(pSlopes,pMatNewX,pResMat,1,K,nCXN);
+ if (bConstant)
+ {
+ double fIntercept = fMeanY - lcl_GetSumProduct(pMeans,pSlopes,K);
+ for (SCSIZE col = 0; col < nCXN; col++)
+ pResMat->PutDouble(pResMat->GetDouble(col)+fIntercept, col);
+ }
+ if (_bGrowth)
+ {
+ for (SCSIZE i = 0; i < nCXN; i++)
+ pResMat->PutDouble(exp(pResMat->GetDouble(i)), i);
+ }
+ }
+ }
+ PushMatrix(pResMat);
+}
+
+void ScInterpreter::ScMatRef()
+{
+ // In case it contains relative references resolve them as usual.
+ Push( *pCur );
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+
+ ScRefCellValue aCell(mrDoc, aAdr);
+
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ {
+ PushError( FormulaError::NoRef );
+ return;
+ }
+
+ if (aCell.getFormula()->IsRunning())
+ {
+ // Twisted odd corner case where an array element's cell tries to
+ // access the top left matrix while it is still running, see tdf#88737
+ // This is a hackish workaround, not a general solution, the matrix
+ // isn't available anyway and FormulaError::CircularReference would be set.
+ PushError( FormulaError::RetryCircular );
+ return;
+ }
+
+ const ScMatrix* pMat = aCell.getFormula()->GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nCols, nRows;
+ pMat->GetDimensions( nCols, nRows );
+ SCSIZE nC = static_cast<SCSIZE>(aPos.Col() - aAdr.Col());
+ SCSIZE nR = static_cast<SCSIZE>(aPos.Row() - aAdr.Row());
+#if 0
+ // XXX: this could be an additional change for tdf#145085 to not
+ // display the URL in a voluntary entered 2-rows array context.
+ // However, that might as well be used on purpose to implement a check
+ // on the URL, which existing documents may have done, the more that
+ // before the accompanying change of
+ // ScFormulaCell::GetResultDimensions() the cell array was forced to
+ // two rows. Do not change without compelling reason. Note that this
+ // repeating top cell is what Excel implements, but it has no
+ // additional value so probably isn't used there. Exporting to and
+ // using in Excel though will lose this capability.
+ if (aCell.mpFormula->GetCode()->IsHyperLink())
+ {
+ // Row 2 element is the URL that is not to be displayed, fake a
+ // 1-row cell-text-only matrix that is repeated.
+ assert(nRows == 2);
+ nR = 0;
+ }
+#endif
+ if ((nCols <= nC && nCols != 1) || (nRows <= nR && nRows != 1))
+ PushNA();
+ else
+ {
+ const ScMatrixValue nMatVal = pMat->Get( nC, nR);
+ ScMatValType nMatValType = nMatVal.nType;
+
+ if (ScMatrix::IsNonValueType( nMatValType))
+ {
+ if (ScMatrix::IsEmptyPathType( nMatValType))
+ { // result of empty false jump path
+ nFuncFmtType = SvNumFormatType::LOGICAL;
+ PushInt(0);
+ }
+ else if (ScMatrix::IsEmptyType( nMatValType))
+ {
+ // Not inherited (really?) and display as empty string, not 0.
+ PushTempToken( new ScEmptyCellToken( false, true));
+ }
+ else
+ PushString( nMatVal.GetString() );
+ }
+ else
+ {
+ // Determine nFuncFmtType type before PushDouble().
+ mrDoc.GetNumberFormatInfo(mrContext, nCurFmtType, nCurFmtIndex, aAdr);
+ nFuncFmtType = nCurFmtType;
+ nFuncFmtIndex = nCurFmtIndex;
+ PushDouble(nMatVal.fVal); // handles DoubleError
+ }
+ }
+ }
+ else
+ {
+ // Determine nFuncFmtType type before PushDouble().
+ mrDoc.GetNumberFormatInfo(mrContext, nCurFmtType, nCurFmtIndex, aAdr);
+ nFuncFmtType = nCurFmtType;
+ nFuncFmtIndex = nCurFmtIndex;
+ // If not a result matrix, obtain the cell value.
+ FormulaError nErr = aCell.getFormula()->GetErrCode();
+ if (nErr != FormulaError::NONE)
+ PushError( nErr );
+ else if (aCell.getFormula()->IsValue())
+ PushDouble(aCell.getFormula()->GetValue());
+ else
+ {
+ svl::SharedString aVal = aCell.getFormula()->GetString();
+ PushString( aVal );
+ }
+ }
+}
+
+void ScInterpreter::ScInfo()
+{
+ if( !MustHaveParamCount( GetByte(), 1 ) )
+ return;
+
+ OUString aStr = GetString().getString();
+ ScCellKeywordTranslator::transKeyword(aStr, &ScGlobal::GetLocale(), ocInfo);
+ if( aStr == "SYSTEM" )
+ PushString( SC_INFO_OSVERSION );
+ else if( aStr == "OSVERSION" )
+#if (defined LINUX || defined __FreeBSD__)
+ PushString(Application::GetOSVersion());
+#elif defined MACOSX
+ // TODO tdf#140286 handle MACOSX version to get result compatible to Excel
+ PushString("Windows (32-bit) NT 5.01");
+#else // handle Windows (WNT, WIN_NT, WIN32, _WIN32)
+ // TODO tdf#140286 handle Windows version to get a result compatible to Excel
+ PushString( "Windows (32-bit) NT 5.01" );
+#endif
+ else if( aStr == "RELEASE" )
+ PushString( ::utl::Bootstrap::getBuildIdData( OUString() ) );
+ else if( aStr == "NUMFILE" )
+ PushDouble( 1 );
+ else if( aStr == "RECALC" )
+ PushString( ScResId( mrDoc.GetAutoCalc() ? STR_RECALC_AUTO : STR_RECALC_MANUAL ) );
+ else if (aStr == "DIRECTORY" || aStr == "MEMAVAIL" || aStr == "MEMUSED" || aStr == "ORIGIN" || aStr == "TOTMEM")
+ PushNA();
+ else
+ PushIllegalArgument();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr6.cxx b/sc/source/core/tool/interpr6.cxx
new file mode 100644
index 0000000000..8c6cfb449b
--- /dev/null
+++ b/sc/source/core/tool/interpr6.cxx
@@ -0,0 +1,1010 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <interpre.hxx>
+#include <columnspanset.hxx>
+#include <column.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <dociter.hxx>
+#include <mtvfunctions.hxx>
+#include <scmatrix.hxx>
+
+#include <arraysumfunctor.hxx>
+
+#include <formula/token.hxx>
+
+using namespace formula;
+
+double const fHalfMachEps = 0.5 * ::std::numeric_limits<double>::epsilon();
+
+// The idea how this group of gamma functions is calculated, is
+// based on the Cephes library
+// online http://www.moshier.net/#Cephes [called 2008-02]
+
+/** You must ensure fA>0.0 && fX>0.0
+ valid results only if fX > fA+1.0
+ uses continued fraction with odd items */
+double ScInterpreter::GetGammaContFraction( double fA, double fX )
+{
+
+ double const fBigInv = ::std::numeric_limits<double>::epsilon();
+ double const fBig = 1.0/fBigInv;
+ double fCount = 0.0;
+ double fY = 1.0 - fA;
+ double fDenom = fX + 2.0-fA;
+ double fPkm1 = fX + 1.0;
+ double fPkm2 = 1.0;
+ double fQkm1 = fDenom * fX;
+ double fQkm2 = fX;
+ double fApprox = fPkm1/fQkm1;
+ bool bFinished = false;
+ do
+ {
+ fCount = fCount +1.0;
+ fY = fY+ 1.0;
+ const double fNum = fY * fCount;
+ fDenom = fDenom +2.0;
+ double fPk = fPkm1 * fDenom - fPkm2 * fNum;
+ const double fQk = fQkm1 * fDenom - fQkm2 * fNum;
+ if (fQk != 0.0)
+ {
+ const double fR = fPk/fQk;
+ bFinished = (fabs( (fApprox - fR)/fR ) <= fHalfMachEps);
+ fApprox = fR;
+ }
+ fPkm2 = fPkm1;
+ fPkm1 = fPk;
+ fQkm2 = fQkm1;
+ fQkm1 = fQk;
+ if (fabs(fPk) > fBig)
+ {
+ // reduce a fraction does not change the value
+ fPkm2 = fPkm2 * fBigInv;
+ fPkm1 = fPkm1 * fBigInv;
+ fQkm2 = fQkm2 * fBigInv;
+ fQkm1 = fQkm1 * fBigInv;
+ }
+ } while (!bFinished && fCount<10000);
+ // most iterations, if fX==fAlpha+1.0; approx sqrt(fAlpha) iterations then
+ if (!bFinished)
+ {
+ SetError(FormulaError::NoConvergence);
+ }
+ return fApprox;
+}
+
+/** You must ensure fA>0.0 && fX>0.0
+ valid results only if fX <= fA+1.0
+ uses power series */
+double ScInterpreter::GetGammaSeries( double fA, double fX )
+{
+ double fDenomfactor = fA;
+ double fSummand = 1.0/fA;
+ double fSum = fSummand;
+ int nCount=1;
+ do
+ {
+ fDenomfactor = fDenomfactor + 1.0;
+ fSummand = fSummand * fX/fDenomfactor;
+ fSum = fSum + fSummand;
+ nCount = nCount+1;
+ } while ( fSummand/fSum > fHalfMachEps && nCount<=10000);
+ // large amount of iterations will be carried out for huge fAlpha, even
+ // if fX <= fAlpha+1.0
+ if (nCount>10000)
+ {
+ SetError(FormulaError::NoConvergence);
+ }
+ return fSum;
+}
+
+/** You must ensure fA>0.0 && fX>0.0) */
+double ScInterpreter::GetLowRegIGamma( double fA, double fX )
+{
+ double fLnFactor = fA * log(fX) - fX - GetLogGamma(fA);
+ double fFactor = exp(fLnFactor); // Do we need more accuracy than exp(ln()) has?
+ if (fX>fA+1.0) // includes fX>1.0; 1-GetUpRegIGamma, continued fraction
+ return 1.0 - fFactor * GetGammaContFraction(fA,fX);
+ else // fX<=1.0 || fX<=fA+1.0, series
+ return fFactor * GetGammaSeries(fA,fX);
+}
+
+/** You must ensure fA>0.0 && fX>0.0) */
+double ScInterpreter::GetUpRegIGamma( double fA, double fX )
+{
+
+ double fLnFactor= fA*log(fX)-fX-GetLogGamma(fA);
+ double fFactor = exp(fLnFactor); //Do I need more accuracy than exp(ln()) has?;
+ if (fX>fA+1.0) // includes fX>1.0
+ return fFactor * GetGammaContFraction(fA,fX);
+ else //fX<=1 || fX<=fA+1, 1-GetLowRegIGamma, series
+ return 1.0 -fFactor * GetGammaSeries(fA,fX);
+}
+
+/** Gamma distribution, probability density function.
+ fLambda is "scale" parameter
+ You must ensure fAlpha>0.0 and fLambda>0.0 */
+double ScInterpreter::GetGammaDistPDF( double fX, double fAlpha, double fLambda )
+{
+ if (fX < 0.0)
+ return 0.0; // see ODFF
+ else if (fX == 0)
+ // in this case 0^0 isn't zero
+ {
+ if (fAlpha < 1.0)
+ {
+ SetError(FormulaError::DivisionByZero); // should be #DIV/0
+ return HUGE_VAL;
+ }
+ else if (fAlpha == 1)
+ {
+ return (1.0 / fLambda);
+ }
+ else
+ {
+ return 0.0;
+ }
+ }
+ else
+ {
+ double fXr = fX / fLambda;
+ // use exp(ln()) only for large arguments because of less accuracy
+ if (fXr > 1.0)
+ {
+ const double fLogDblMax = log( ::std::numeric_limits<double>::max());
+ if (log(fXr) * (fAlpha-1.0) < fLogDblMax && fAlpha < fMaxGammaArgument)
+ {
+ return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha);
+ }
+ else
+ {
+ return exp( (fAlpha-1.0) * log(fXr) - fXr - log(fLambda) - GetLogGamma(fAlpha));
+ }
+ }
+ else // fXr near to zero
+ {
+ if (fAlpha<fMaxGammaArgument)
+ {
+ return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha);
+ }
+ else
+ {
+ return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / exp( GetLogGamma(fAlpha));
+ }
+ }
+ }
+}
+
+/** Gamma distribution, cumulative distribution function.
+ fLambda is "scale" parameter
+ You must ensure fAlpha>0.0 and fLambda>0.0 */
+double ScInterpreter::GetGammaDist( double fX, double fAlpha, double fLambda )
+{
+ if (fX <= 0.0)
+ return 0.0;
+ else
+ return GetLowRegIGamma( fAlpha, fX / fLambda);
+}
+
+namespace {
+
+class NumericCellAccumulator
+{
+ KahanSum maSum;
+ FormulaError mnError;
+
+public:
+ NumericCellAccumulator() : maSum(0.0), mnError(FormulaError::NONE) {}
+
+ void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize)
+ {
+ switch (rNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ if (nDataSize == 0)
+ return;
+
+ const double *p = &sc::numeric_block::at(*rNode.data, nOffset);
+ maSum += sc::op::sumArray(p, nDataSize);
+ break;
+ }
+
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (; it != itEnd; ++it)
+ {
+ double fVal = 0.0;
+ FormulaError nErr = FormulaError::NONE;
+ ScFormulaCell& rCell = *(*it);
+ if (!rCell.GetErrorOrValue(nErr, fVal))
+ // The cell has neither error nor value. Perhaps string result.
+ continue;
+
+ if (nErr != FormulaError::NONE)
+ {
+ // Cell has error - skip all the rest
+ mnError = nErr;
+ return;
+ }
+
+ maSum += fVal;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ FormulaError getError() const { return mnError; }
+ const KahanSum& getResult() const { return maSum; }
+};
+
+class NumericCellCounter
+{
+ size_t mnCount;
+public:
+ NumericCellCounter() : mnCount(0) {}
+
+ void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize)
+ {
+ switch (rNode.type)
+ {
+ case sc::element_type_numeric:
+ mnCount += nDataSize;
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ for (; it != itEnd; ++it)
+ {
+ ScFormulaCell& rCell = **it;
+ if (rCell.IsValueNoError())
+ ++mnCount;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ size_t getCount() const { return mnCount; }
+};
+
+class FuncCount : public sc::ColumnSpanSet::ColumnAction
+{
+ const ScInterpreterContext& mrContext;
+ sc::ColumnBlockConstPosition maPos;
+ ScColumn* mpCol;
+ size_t mnCount;
+ sal_uInt32 mnNumFmt;
+
+public:
+ FuncCount(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mnCount(0), mnNumFmt(0) {}
+
+ virtual void startColumn(ScColumn* pCol) override
+ {
+ mpCol = pCol;
+ mpCol->InitBlockPosition(maPos);
+ }
+
+ virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override
+ {
+ if (!bVal)
+ return;
+
+ NumericCellCounter aFunc;
+ maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2);
+ mnCount += aFunc.getCount();
+ mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2);
+ };
+
+ size_t getCount() const { return mnCount; }
+ sal_uInt32 getNumberFormat() const { return mnNumFmt; }
+};
+
+class FuncSum : public sc::ColumnSpanSet::ColumnAction
+{
+ const ScInterpreterContext& mrContext;
+ sc::ColumnBlockConstPosition maPos;
+ ScColumn* mpCol;
+ KahanSum mfSum;
+ FormulaError mnError;
+ sal_uInt32 mnNumFmt;
+
+public:
+ FuncSum(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mfSum(0.0), mnError(FormulaError::NONE), mnNumFmt(0) {}
+
+ virtual void startColumn(ScColumn* pCol) override
+ {
+ mpCol = pCol;
+ mpCol->InitBlockPosition(maPos);
+ }
+
+ virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override
+ {
+ if (!bVal)
+ return;
+
+ if (mnError != FormulaError::NONE)
+ return;
+
+ NumericCellAccumulator aFunc;
+ maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2);
+ mnError = aFunc.getError();
+ if (mnError != FormulaError::NONE)
+ return;
+
+
+ mfSum += aFunc.getResult();
+ mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2);
+ };
+
+ FormulaError getError() const { return mnError; }
+ const KahanSum& getSum() const { return mfSum; }
+ sal_uInt32 getNumberFormat() const { return mnNumFmt; }
+};
+
+}
+
+static void IterateMatrix(
+ const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero, SubtotalFlags nSubTotalFlags,
+ sal_uLong& rCount, SvNumFormatType& rFuncFmtType, KahanSum& fRes )
+{
+ if (!pMat)
+ return;
+
+ const bool bIgnoreErrVal = bool(nSubTotalFlags & SubtotalFlags::IgnoreErrVal);
+ rFuncFmtType = SvNumFormatType::NUMBER;
+ switch (eFunc)
+ {
+ case ifAVERAGE:
+ case ifSUM:
+ {
+ ScMatrix::KahanIterateResult aRes = pMat->Sum(bTextAsZero, bIgnoreErrVal);
+ fRes += aRes.maAccumulator;
+ rCount += aRes.mnCount;
+ }
+ break;
+ case ifCOUNT:
+ rCount += pMat->Count(bTextAsZero, false); // do not count error values
+ break;
+ case ifCOUNT2:
+ /* TODO: what is this supposed to be with bIgnoreErrVal? */
+ rCount += pMat->Count(true, true); // do count error values
+ break;
+ case ifPRODUCT:
+ {
+ ScMatrix::DoubleIterateResult aRes = pMat->Product(bTextAsZero, bIgnoreErrVal);
+ fRes *= aRes.maAccumulator;
+ rCount += aRes.mnCount;
+ }
+ break;
+ case ifSUMSQ:
+ {
+ ScMatrix::KahanIterateResult aRes = pMat->SumSquare(bTextAsZero, bIgnoreErrVal);
+ fRes += aRes.maAccumulator;
+ rCount += aRes.mnCount;
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+size_t ScInterpreter::GetRefListArrayMaxSize( short nParamCount )
+{
+ size_t nSize = 0;
+ if (IsInArrayContext())
+ {
+ for (short i=1; i <= nParamCount; ++i)
+ {
+ if (GetStackType(i) == svRefList)
+ {
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp - i]);
+ if (p && p->IsArrayResult() && p->GetRefList()->size() > nSize)
+ nSize = p->GetRefList()->size();
+ }
+ }
+ }
+ return nSize;
+}
+
+static double lcl_IterResult( ScIterFunc eFunc, double fRes, sal_uLong nCount )
+{
+ switch( eFunc )
+ {
+ case ifAVERAGE:
+ fRes = sc::div( fRes, nCount);
+ break;
+ case ifCOUNT2:
+ case ifCOUNT:
+ fRes = nCount;
+ break;
+ case ifPRODUCT:
+ if ( !nCount )
+ fRes = 0.0;
+ break;
+ default:
+ ; // nothing
+ }
+ return fRes;
+}
+
+void ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero )
+{
+ short nParamCount = GetByte();
+ const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
+ ScMatrixRef xResMat, xResCount;
+ const double ResInitVal = (eFunc == ifPRODUCT) ? 1.0 : 0.0;
+ KahanSum fRes = ResInitVal;
+ double fVal = 0.0;
+ sal_uLong nCount = 0;
+ ScAddress aAdr;
+ ScRange aRange;
+ size_t nRefInList = 0;
+ size_t nRefArrayPos = std::numeric_limits<size_t>::max();
+ if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
+ ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
+ nGlobalError = FormulaError::NONE;
+ while (nParamCount-- > 0)
+ {
+ switch (GetStackType())
+ {
+ case svString:
+ {
+ if( eFunc == ifCOUNT )
+ {
+ OUString aStr = PopString().getString();
+ if ( bTextAsZero )
+ nCount++;
+ else
+ {
+ // Only check if string can be converted to number, no
+ // error propagation.
+ FormulaError nErr = nGlobalError;
+ nGlobalError = FormulaError::NONE;
+ ConvertStringToValue( aStr );
+ if (nGlobalError == FormulaError::NONE)
+ ++nCount;
+ nGlobalError = nErr;
+ }
+ }
+ else
+ {
+ Pop();
+ switch ( eFunc )
+ {
+ case ifAVERAGE:
+ case ifSUM:
+ case ifSUMSQ:
+ case ifPRODUCT:
+ {
+ if ( bTextAsZero )
+ {
+ nCount++;
+ if ( eFunc == ifPRODUCT )
+ fRes = 0.0;
+ }
+ else
+ {
+ while (nParamCount-- > 0)
+ PopError();
+ SetError( FormulaError::NoValue );
+ }
+ }
+ break;
+ default:
+ nCount++;
+ }
+ }
+ }
+ break;
+ case svDouble :
+ fVal = GetDouble();
+ nCount++;
+ switch( eFunc )
+ {
+ case ifAVERAGE:
+ case ifSUM: fRes += fVal; break;
+ case ifSUMSQ: fRes += fVal * fVal; break;
+ case ifPRODUCT: fRes *= fVal; break;
+ default: ; // nothing
+ }
+ nFuncFmtType = SvNumFormatType::NUMBER;
+ break;
+ case svExternalSingleRef:
+ {
+ ScExternalRefCache::TokenRef pToken;
+ ScExternalRefCache::CellFormat aFmt;
+ PopExternalSingleRef(pToken, &aFmt);
+ if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
+ ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
+ {
+ nGlobalError = FormulaError::NONE;
+ if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ ++nCount;
+ break;
+ }
+
+ if (!pToken)
+ break;
+
+ StackVar eType = pToken->GetType();
+ if (eFunc == ifCOUNT2)
+ {
+ if ( eType != svEmptyCell &&
+ ( ( pToken->GetOpCode() != ocSubTotal &&
+ pToken->GetOpCode() != ocAggregate ) ||
+ ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) ) )
+ nCount++;
+ if (nGlobalError != FormulaError::NONE)
+ nGlobalError = FormulaError::NONE;
+ }
+ else if (eType == svDouble)
+ {
+ nCount++;
+ fVal = pToken->GetDouble();
+ if (aFmt.mbIsSet)
+ {
+ nFuncFmtType = aFmt.mnType;
+ nFuncFmtIndex = aFmt.mnIndex;
+ }
+ switch( eFunc )
+ {
+ case ifAVERAGE:
+ case ifSUM: fRes += fVal; break;
+ case ifSUMSQ: fRes += fVal * fVal; break;
+ case ifPRODUCT: fRes *= fVal; break;
+ case ifCOUNT:
+ if ( nGlobalError != FormulaError::NONE )
+ {
+ nGlobalError = FormulaError::NONE;
+ nCount--;
+ }
+ break;
+ default: ; // nothing
+ }
+ }
+ else if (bTextAsZero && eType == svString)
+ {
+ nCount++;
+ if ( eFunc == ifPRODUCT )
+ fRes = 0.0;
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ PopSingleRef( aAdr );
+ if (nGlobalError == FormulaError::NoRef)
+ {
+ PushError( FormulaError::NoRef);
+ return;
+ }
+
+ if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) &&
+ mrDoc.RowFiltered( aAdr.Row(), aAdr.Tab() ) ) ||
+ ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) &&
+ mrDoc.RowHidden( aAdr.Row(), aAdr.Tab() ) ) )
+ {
+ break;
+ }
+ if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
+ ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
+ {
+ nGlobalError = FormulaError::NONE;
+ if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ ++nCount;
+ break;
+ }
+ ScRefCellValue aCell(mrDoc, aAdr);
+ if (!aCell.isEmpty())
+ {
+ if( eFunc == ifCOUNT2 )
+ {
+ CellType eCellType = aCell.getType();
+ if ( eCellType != CELLTYPE_NONE )
+ nCount++;
+ if ( nGlobalError != FormulaError::NONE )
+ nGlobalError = FormulaError::NONE;
+ }
+ else if (aCell.hasNumeric())
+ {
+ fVal = GetCellValue(aAdr, aCell);
+ if (nGlobalError != FormulaError::NONE)
+ {
+ if (eFunc == ifCOUNT || (mnSubTotalFlags & SubtotalFlags::IgnoreErrVal))
+ nGlobalError = FormulaError::NONE;
+ break;
+ }
+ nCount++;
+ CurFmtToFuncFmt();
+ switch( eFunc )
+ {
+ case ifAVERAGE:
+ case ifSUM: fRes += fVal; break;
+ case ifSUMSQ: fRes += fVal * fVal; break;
+ case ifPRODUCT: fRes *= fVal; break;
+ default: ; // nothing
+ }
+ }
+ else if (bTextAsZero && aCell.hasString())
+ {
+ nCount++;
+ if ( eFunc == ifPRODUCT )
+ fRes = 0.0;
+ }
+ }
+ }
+ break;
+ case svRefList :
+ {
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
+ if (p && p->IsArrayResult())
+ {
+ nRefArrayPos = nRefInList;
+ // The "one value to all references of an array" seems to
+ // be what Excel does if there are other types than just
+ // arrays of references.
+ if (!xResMat)
+ {
+ // Create and init all elements with current value.
+ assert(nMatRows > 0);
+ xResMat = GetNewMat( 1, nMatRows, true);
+ xResMat->FillDouble( fRes.get(), 0,0, 0,nMatRows-1);
+ if (eFunc != ifSUM)
+ {
+ xResCount = GetNewMat( 1, nMatRows, true);
+ xResCount->FillDouble( nCount, 0,0, 0,nMatRows-1);
+ }
+ }
+ else
+ {
+ // Current value and values from vector are operands
+ // for each vector position.
+ if (nCount && xResCount)
+ {
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ xResCount->PutDouble( xResCount->GetDouble(0,i) + nCount, 0,i);
+ }
+ }
+ if (fRes != ResInitVal)
+ {
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ double fVecRes = xResMat->GetDouble(0,i);
+ if (eFunc == ifPRODUCT)
+ fVecRes *= fRes.get();
+ else
+ fVecRes += fRes.get();
+ xResMat->PutDouble( fVecRes, 0,i);
+ }
+ }
+ }
+ fRes = ResInitVal;
+ nCount = 0;
+ }
+ }
+ [[fallthrough]];
+ case svDoubleRef :
+ {
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if (nGlobalError == FormulaError::NoRef)
+ {
+ PushError( FormulaError::NoRef);
+ return;
+ }
+
+ if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ||
+ ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
+ {
+ nGlobalError = FormulaError::NONE;
+ if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ ++nCount;
+ if ( eFunc == ifCOUNT2 || eFunc == ifCOUNT )
+ break;
+ }
+ if( eFunc == ifCOUNT2 )
+ {
+ ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if ( !aIter.isEmpty() )
+ {
+ ++nCount;
+ }
+ }
+
+ if ( nGlobalError != FormulaError::NONE )
+ nGlobalError = FormulaError::NONE;
+ }
+ else if (((eFunc == ifSUM && !bCalcAsShown) || eFunc == ifCOUNT )
+ && mnSubTotalFlags == SubtotalFlags::NONE)
+ {
+ // Use fast span set array method.
+ // ifSUM with bCalcAsShown has to use the slow bells and
+ // whistles ScValueIterator below.
+ sc::RangeColumnSpanSet aSet( aRange );
+
+ if ( eFunc == ifSUM )
+ {
+ FuncSum aAction(mrContext);
+ aSet.executeColumnAction( mrDoc, aAction );
+ FormulaError nErr = aAction.getError();
+ if ( nErr != FormulaError::NONE )
+ {
+ PushError( nErr );
+ return;
+ }
+ fRes += aAction.getSum();
+
+ // Get the number format of the last iterated cell.
+ nFuncFmtIndex = aAction.getNumberFormat();
+ }
+ else
+ {
+ FuncCount aAction(mrContext);
+ aSet.executeColumnAction(mrDoc, aAction);
+ nCount += aAction.getCount();
+
+ // Get the number format of the last iterated cell.
+ nFuncFmtIndex = aAction.getNumberFormat();
+ }
+
+ nFuncFmtType = mrContext.GetNumberFormatType( nFuncFmtIndex );
+ }
+ else
+ {
+ ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero );
+ FormulaError nErr = FormulaError::NONE;
+ if (aValIter.GetFirst(fVal, nErr))
+ {
+ // placed the loop on the inside for performance reasons:
+ aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
+ switch( eFunc )
+ {
+ case ifAVERAGE:
+ case ifSUM:
+ if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal )
+ {
+ do
+ {
+ if ( nErr == FormulaError::NONE )
+ {
+ SetError(nErr);
+ fRes += fVal;
+ nCount++;
+ }
+ }
+ while (aValIter.GetNext(fVal, nErr));
+ }
+ else
+ {
+ do
+ {
+ SetError(nErr);
+ fRes += fVal;
+ nCount++;
+ }
+ while (aValIter.GetNext(fVal, nErr));
+ }
+ break;
+ case ifSUMSQ:
+ if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal )
+ {
+ do
+ {
+ if ( nErr == FormulaError::NONE )
+ {
+ SetError(nErr);
+ fRes += fVal * fVal;
+ nCount++;
+ }
+ }
+ while (aValIter.GetNext(fVal, nErr));
+ }
+ else
+ {
+ do
+ {
+ SetError(nErr);
+ fRes += fVal * fVal;
+ nCount++;
+ }
+ while (aValIter.GetNext(fVal, nErr));
+ }
+ break;
+ case ifPRODUCT:
+ do
+ {
+ if ( !( nErr != FormulaError::NONE && ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) )
+ {
+ SetError(nErr);
+ fRes *= fVal;
+ nCount++;
+ }
+ }
+ while (aValIter.GetNext(fVal, nErr));
+ break;
+ case ifCOUNT:
+ do
+ {
+ if ( nErr == FormulaError::NONE )
+ nCount++;
+ }
+ while (aValIter.GetNext(fVal, nErr));
+ break;
+ default: ; // nothing
+ }
+ SetError( nErr );
+ }
+ }
+ if (nRefArrayPos != std::numeric_limits<size_t>::max())
+ {
+ // Update vector element with current value.
+ if (xResCount)
+ xResCount->PutDouble( xResCount->GetDouble(0,nRefArrayPos) + nCount, 0,nRefArrayPos);
+ double fVecRes = xResMat->GetDouble(0,nRefArrayPos);
+ if (eFunc == ifPRODUCT)
+ fVecRes *= fRes.get();
+ else
+ fVecRes += fRes.get();
+ xResMat->PutDouble( fVecRes, 0,nRefArrayPos);
+ // Reset.
+ fRes = ResInitVal;
+ nCount = 0;
+ nRefArrayPos = std::numeric_limits<size_t>::max();
+ }
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat;
+ PopExternalDoubleRef(pMat);
+ if ( nGlobalError != FormulaError::NONE && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ break;
+
+ IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes );
+ }
+ break;
+ case svMatrix :
+ {
+ ScMatrixRef pMat = PopMatrix();
+
+ IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes );
+ }
+ break;
+ case svError:
+ {
+ PopError();
+ if ( eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ {
+ nGlobalError = FormulaError::NONE;
+ }
+ else if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) )
+ {
+ nCount++;
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+ break;
+ default :
+ while (nParamCount-- > 0)
+ PopError();
+ SetError(FormulaError::IllegalParameter);
+ }
+ }
+
+ // A boolean return type makes no sense on sums et al.
+ // Counts are always numbers.
+ if( nFuncFmtType == SvNumFormatType::LOGICAL || eFunc == ifCOUNT || eFunc == ifCOUNT2 )
+ nFuncFmtType = SvNumFormatType::NUMBER;
+
+ if (xResMat)
+ {
+ // Include value of last non-references-array type and calculate final result.
+ for (SCSIZE i=0; i < nMatRows; ++i)
+ {
+ sal_uLong nVecCount = (xResCount ? nCount + xResCount->GetDouble(0,i) : nCount);
+ double fVecRes = xResMat->GetDouble(0,i);
+ if (eFunc == ifPRODUCT)
+ fVecRes *= fRes.get();
+ else
+ fVecRes += fRes.get();
+ fVecRes = lcl_IterResult( eFunc, fVecRes, nVecCount);
+ xResMat->PutDouble( fVecRes, 0,i);
+ }
+ PushMatrix( xResMat);
+ }
+ else
+ {
+ PushDouble( lcl_IterResult( eFunc, fRes.get(), nCount));
+ }
+}
+
+void ScInterpreter::ScSumSQ()
+{
+ IterateParameters( ifSUMSQ );
+}
+
+void ScInterpreter::ScSum()
+{
+ IterateParameters( ifSUM );
+}
+
+void ScInterpreter::ScProduct()
+{
+ IterateParameters( ifPRODUCT );
+}
+
+void ScInterpreter::ScAverage( bool bTextAsZero )
+{
+ IterateParameters( ifAVERAGE, bTextAsZero );
+}
+
+void ScInterpreter::ScCount()
+{
+ IterateParameters( ifCOUNT );
+}
+
+void ScInterpreter::ScCount2()
+{
+ IterateParameters( ifCOUNT2 );
+}
+
+/**
+ * The purpose of RAWSUBTRACT() is exactly to not apply any error correction, approximation etc.
+ * But use the "raw" IEEE 754 double subtraction.
+ * So no Kahan summation
+ */
+void ScInterpreter::ScRawSubtract()
+{
+ short nParamCount = GetByte();
+ if (!MustHaveParamCountMin( nParamCount, 2))
+ return;
+
+ // Reverse stack to process arguments from left to right.
+ ReverseStack( nParamCount);
+ // Obtain the minuend.
+ double fRes = GetDouble();
+
+ while (nGlobalError == FormulaError::NONE && --nParamCount > 0)
+ {
+ // Simple single values without matrix support.
+ fRes -= GetDouble();
+ }
+ while (nParamCount-- > 0)
+ PopError();
+
+ PushDouble( fRes);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr7.cxx b/sc/source/core/tool/interpr7.cxx
new file mode 100644
index 0000000000..ecb4ea3463
--- /dev/null
+++ b/sc/source/core/tool/interpr7.cxx
@@ -0,0 +1,557 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <interpre.hxx>
+#include <jumpmatrix.hxx>
+#include <formulacell.hxx>
+#include <scmatrix.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/character.hxx>
+#include <formula/errorcodes.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <tools/urlobj.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <libxml/xpath.h>
+#include <datastreamgettime.hxx>
+#include <dpobject.hxx>
+#include <document.hxx>
+#include <tokenarray.hxx>
+#include <webservicelink.hxx>
+
+#include <sc.hrc>
+
+#include <cstring>
+#include <memory>
+#include <string_view>
+#include <libxml/parser.h>
+
+using namespace com::sun::star;
+
+// TODO: Add new methods for ScInterpreter here.
+
+void ScInterpreter::ScFilterXML()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount( nParamCount, 2 ) )
+ return;
+
+ SCSIZE nMatCols = 1, nMatRows = 1, nNode = 0;
+ // In array/matrix context node elements' results are to be
+ // subsequently stored. Check this before obtaining any argument from
+ // the stack so the stack type can be used.
+ if (pJumpMatrix || IsInArrayContext())
+ {
+ if (pJumpMatrix)
+ {
+ // Single result, GetString() will retrieve the corresponding
+ // argument and JumpMatrix() will store it at the proper
+ // position. Note that nMatCols and nMatRows are still 1.
+ SCSIZE nCurCol = 0, nCurRow = 0;
+ pJumpMatrix->GetPos( nCurCol, nCurRow);
+ nNode = nCurRow;
+ }
+ else if (bMatrixFormula)
+ {
+ // If there is no formula cell then continue with a single
+ // result.
+ if (pMyFormulaCell)
+ {
+ SCCOL nCols;
+ SCROW nRows;
+ pMyFormulaCell->GetMatColsRows( nCols, nRows);
+ nMatCols = nCols;
+ nMatRows = nRows;
+ }
+ }
+ else if (GetStackType() == formula::svMatrix)
+ {
+ const ScMatrix* pPathMatrix = pStack[sp-1]->GetMatrix();
+ if (!pPathMatrix)
+ {
+ PushIllegalParameter();
+ return;
+ }
+ pPathMatrix->GetDimensions( nMatCols, nMatRows);
+
+ /* TODO: it is unclear what should happen if there are
+ * different path arguments in matrix elements. We may have to
+ * evaluate each, and for repeated identical paths use
+ * subsequent nodes. As is, the path at 0,0 is used as obtained
+ * by GetString(). */
+
+ }
+ }
+ if (!nMatCols || !nMatRows)
+ {
+ PushNoValue();
+ return;
+ }
+
+ OUString aXPathExpression = GetString().getString();
+ OUString aString = GetString().getString();
+ if(aString.isEmpty() || aXPathExpression.isEmpty())
+ {
+ PushError( FormulaError::NoValue );
+ return;
+ }
+
+ OString aOXPathExpression = OUStringToOString( aXPathExpression, RTL_TEXTENCODING_UTF8 );
+ const char* pXPathExpr = aOXPathExpression.getStr();
+ OString aOString = OUStringToOString( aString, RTL_TEXTENCODING_UTF8 );
+ const char* pXML = aOString.getStr();
+
+ std::shared_ptr<xmlParserCtxt> pContext(
+ xmlNewParserCtxt(), xmlFreeParserCtxt );
+
+ std::shared_ptr<xmlDoc> pDoc( xmlParseMemory( pXML, aOString.getLength() ),
+ xmlFreeDoc );
+
+ if(!pDoc)
+ {
+ PushError( FormulaError::NoValue );
+ return;
+ }
+
+ std::shared_ptr<xmlXPathContext> pXPathCtx( xmlXPathNewContext(pDoc.get()),
+ xmlXPathFreeContext );
+
+ std::shared_ptr<xmlXPathObject> pXPathObj( xmlXPathEvalExpression(BAD_CAST(pXPathExpr), pXPathCtx.get()),
+ xmlXPathFreeObject );
+
+ if(!pXPathObj)
+ {
+ PushError( FormulaError::NoValue );
+ return;
+ }
+
+ switch(pXPathObj->type)
+ {
+ case XPATH_UNDEFINED:
+ PushNoValue();
+ break;
+ case XPATH_NODESET:
+ {
+ xmlNodeSetPtr pNodeSet = pXPathObj->nodesetval;
+ if(!pNodeSet)
+ {
+ PushError( FormulaError::NoValue );
+ return;
+ }
+
+ const size_t nSize = pNodeSet->nodeNr;
+ if (nNode >= nSize)
+ {
+ // For pJumpMatrix
+ PushError( FormulaError::NotAvailable);
+ return;
+ }
+
+ /* TODO: for nMatCols>1 IF stack type is svMatrix, i.e.
+ * pPathMatrix!=nullptr, we may want a result matrix with
+ * nMatCols columns as well, but clarify first how to treat
+ * differing path elements. */
+
+ ScMatrixRef xResMat;
+ if (nMatRows > 1)
+ {
+ xResMat = GetNewMat( 1, nMatRows, true);
+ if (!xResMat)
+ {
+ PushError( FormulaError::CodeOverflow);
+ return;
+ }
+ }
+
+ for ( ; nNode < nMatRows; ++nNode)
+ {
+ if( nSize > nNode )
+ {
+ OUString aResult;
+ if(pNodeSet->nodeTab[nNode]->type == XML_NAMESPACE_DECL)
+ {
+ xmlNsPtr ns = reinterpret_cast<xmlNsPtr>(pNodeSet->nodeTab[nNode]);
+ xmlNodePtr cur = reinterpret_cast<xmlNodePtr>(ns->next);
+ std::shared_ptr<xmlChar> pChar2(xmlNodeGetContent(cur), xmlFree);
+ aResult = OStringToOUString(std::string_view(reinterpret_cast<char*>(pChar2.get())), RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ xmlNodePtr cur = pNodeSet->nodeTab[nNode];
+ std::shared_ptr<xmlChar> pChar2(xmlNodeGetContent(cur), xmlFree);
+ aResult = OStringToOUString(std::string_view(reinterpret_cast<char*>(pChar2.get())), RTL_TEXTENCODING_UTF8);
+ }
+ if (xResMat)
+ xResMat->PutString( mrStrPool.intern( aResult), 0, nNode);
+ else
+ PushString(aResult);
+ }
+ else
+ {
+ if (xResMat)
+ xResMat->PutError( FormulaError::NotAvailable, 0, nNode);
+ else
+ PushError( FormulaError::NotAvailable );
+ }
+ }
+ if (xResMat)
+ PushMatrix( xResMat);
+ }
+ break;
+ case XPATH_BOOLEAN:
+ {
+ bool bVal = pXPathObj->boolval != 0;
+ PushDouble(double(bVal));
+ }
+ break;
+ case XPATH_NUMBER:
+ {
+ double fVal = pXPathObj->floatval;
+ PushDouble(fVal);
+ }
+ break;
+ case XPATH_STRING:
+ PushString(OUString::createFromAscii(reinterpret_cast<char*>(pXPathObj->stringval)));
+ break;
+#if LIBXML_VERSION < 21000 || defined(LIBXML_XPTR_LOCS_ENABLED)
+ case XPATH_POINT:
+ PushNoValue();
+ break;
+ case XPATH_RANGE:
+ PushNoValue();
+ break;
+ case XPATH_LOCATIONSET:
+ PushNoValue();
+ break;
+#endif
+ case XPATH_USERS:
+ PushNoValue();
+ break;
+ case XPATH_XSLT_TREE:
+ PushNoValue();
+ break;
+
+ }
+}
+
+static ScWebServiceLink* lcl_GetWebServiceLink(const sfx2::LinkManager* pLinkMgr, std::u16string_view rURL)
+{
+ size_t nCount = pLinkMgr->GetLinks().size();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkMgr->GetLinks()[i].get();
+ if (ScWebServiceLink* pLink = dynamic_cast<ScWebServiceLink*>(pBase))
+ {
+ if (pLink->GetURL() == rURL)
+ return pLink;
+ }
+ }
+
+ return nullptr;
+}
+
+static bool lcl_FunctionAccessLoadWebServiceLink( OUString& rResult, ScDocument* pDoc, const OUString& rURI )
+{
+ // For FunctionAccess service always force a changed data update.
+ ScWebServiceLink aLink( pDoc, rURI);
+ if (aLink.DataChanged( OUString(), css::uno::Any()) != sfx2::SvBaseLink::UpdateResult::SUCCESS)
+ return false;
+
+ if (!aLink.HasResult())
+ return false;
+
+ rResult = aLink.GetResult();
+
+ return true;
+}
+
+void ScInterpreter::ScWebservice()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (!MustHaveParamCount( nParamCount, 1 ) )
+ return;
+
+ OUString aURI = GetString().getString();
+
+ if (aURI.isEmpty())
+ {
+ PushError( FormulaError::NoValue );
+ return;
+ }
+
+ INetURLObject aObj(aURI, INetProtocol::File);
+ INetProtocol eProtocol = aObj.GetProtocol();
+ if (eProtocol != INetProtocol::Http && eProtocol != INetProtocol::Https)
+ {
+ PushError(FormulaError::NoValue);
+ return;
+ }
+
+ if (!mpLinkManager)
+ {
+ if (!mrDoc.IsFunctionAccess() || mrDoc.HasLinkFormulaNeedingCheck())
+ {
+ PushError( FormulaError::NoValue);
+ }
+ else
+ {
+ OUString aResult;
+ if (lcl_FunctionAccessLoadWebServiceLink( aResult, &mrDoc, aURI))
+ PushString( aResult);
+ else
+ PushError( FormulaError::NoValue);
+ }
+ return;
+ }
+
+ // Need to reinterpret after loading (build links)
+ pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT );
+
+ // while the link is not evaluated, idle must be disabled (to avoid circular references)
+ bool bOldEnabled = mrDoc.IsIdleEnabled();
+ mrDoc.EnableIdle(false);
+
+ // Get/ Create link object
+ ScWebServiceLink* pLink = lcl_GetWebServiceLink(mpLinkManager, aURI);
+
+ bool bWasError = (pMyFormulaCell && pMyFormulaCell->GetRawError() != FormulaError::NONE);
+
+ if (!pLink)
+ {
+ pLink = new ScWebServiceLink(&mrDoc, aURI);
+ mpLinkManager->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile, aURI);
+ if ( mpLinkManager->GetLinks().size() == 1 ) // the first one?
+ {
+ SfxBindings* pBindings = mrDoc.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS ); // Link-Manager enabled
+ }
+
+ //if the document was just loaded, but the ScDdeLink entry was missing, then
+ //don't update this link until the links are updated in response to the users
+ //decision
+ if (!mrDoc.HasLinkFormulaNeedingCheck())
+ {
+ pLink->Update();
+ }
+
+ if (pMyFormulaCell)
+ {
+ // StartListening after the Update to avoid circular references
+ pMyFormulaCell->StartListening(*pLink);
+ }
+ }
+ else
+ {
+ if (pMyFormulaCell)
+ pMyFormulaCell->StartListening(*pLink);
+ }
+
+ // If a new Error from Reschedule appears when the link is executed then reset the errorflag
+ if (pMyFormulaCell && pMyFormulaCell->GetRawError() != FormulaError::NONE && !bWasError)
+ pMyFormulaCell->SetErrCode(FormulaError::NONE);
+
+ // check the value
+ if (pLink->HasResult())
+ PushString(pLink->GetResult());
+ else if (mrDoc.HasLinkFormulaNeedingCheck())
+ {
+ // If this formula cell is recalculated just after load and the
+ // expression is exactly WEBSERVICE("literal_URI") (i.e. no other
+ // calculation involved, not even a cell reference) and a cached
+ // result is set as hybrid string then use that as result value to
+ // prevent a #VALUE! result due to the "Automatic update of
+ // external links has been disabled."
+ // This will work only once, as the new formula cell result won't
+ // be a hybrid anymore.
+ /* TODO: the FormulaError::LinkFormulaNeedingCheck could be used as
+ * a signal for the formula cell to keep the hybrid string as
+ * result of the overall formula *iff* no higher prioritized
+ * ScRecalcMode than ONLOAD_LENIENT is present in the entire
+ * document (i.e. the formula result could not be influenced by an
+ * ONLOAD_MUST or ALWAYS recalc, necessary as we don't track
+ * interim results of subexpressions that could be compared), which
+ * also means to track setting ScRecalcMode somehow... note this is
+ * just a vague idea so far and might or might not work. */
+ if (pMyFormulaCell && pMyFormulaCell->HasHybridStringResult())
+ {
+ if (pMyFormulaCell->GetCode()->GetCodeLen() == 2)
+ {
+ formula::FormulaToken const * const * pRPN = pMyFormulaCell->GetCode()->GetCode();
+ if (pRPN[0]->GetType() == formula::svString && pRPN[1]->GetOpCode() == ocWebservice)
+ PushString( pMyFormulaCell->GetResultString());
+ else
+ PushError(FormulaError::LinkFormulaNeedingCheck);
+ }
+ else
+ PushError(FormulaError::LinkFormulaNeedingCheck);
+ }
+ else
+ PushError(FormulaError::LinkFormulaNeedingCheck);
+ }
+ else
+ PushError(FormulaError::NoValue);
+
+ mrDoc.EnableIdle(bOldEnabled);
+ mpLinkManager->CloseCachedComps();
+}
+
+/**
+ Returns a string in which all non-alphanumeric characters except stroke and
+ underscore (-_) have been replaced with a percent (%) sign
+ followed by hex digits.
+ It is encoded the same way that the posted data from a WWW form is encoded,
+ that is the same way as in application/x-www-form-urlencoded media type and
+ as per RFC 3986.
+
+ @see fdo#76870
+*/
+void ScInterpreter::ScEncodeURL()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if ( !MustHaveParamCount( nParamCount, 1 ) )
+ return;
+
+ OUString aStr = GetString().getString();
+ if ( aStr.isEmpty() )
+ {
+ PushError( FormulaError::NoValue );
+ return;
+ }
+
+ OString aUtf8Str( aStr.toUtf8());
+ const sal_Int32 nLen = aUtf8Str.getLength();
+ OStringBuffer aUrlBuf( nLen );
+ for ( int i = 0; i < nLen; i++ )
+ {
+ char c = aUtf8Str[ i ];
+ if ( rtl::isAsciiAlphanumeric( static_cast<unsigned char>( c ) ) || c == '-' || c == '_' )
+ aUrlBuf.append( c );
+ else
+ {
+ aUrlBuf.append( '%' );
+ OString convertedChar = OString::number( static_cast<unsigned char>( c ), 16 ).toAsciiUpperCase();
+ // RFC 3986 indicates:
+ // "A percent-encoded octet is encoded as a character triplet,
+ // consisting of the percent character "%" followed by the two hexadecimal digits
+ // representing that octet's numeric value"
+ if (convertedChar.getLength() == 1)
+ aUrlBuf.append("0");
+ aUrlBuf.append(convertedChar);
+ }
+ }
+ PushString( OUString::fromUtf8( aUrlBuf ) );
+}
+
+void ScInterpreter::ScDebugVar()
+{
+ // This is to be used by developers only! Never document this for end
+ // users. This is a convenient way to extract arbitrary internal state to
+ // a cell for easier debugging.
+
+ if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
+ {
+ PushError(FormulaError::NoName);
+ return;
+ }
+
+ if (!MustHaveParamCount(GetByte(), 1))
+ return;
+
+ rtl_uString* p = GetString().getDataIgnoreCase();
+ if (!p)
+ {
+ PushIllegalParameter();
+ return;
+ }
+
+ OUString aStrUpper(p);
+
+ if (aStrUpper == "PIVOTCOUNT")
+ {
+ // Set the number of pivot tables in the document.
+
+ double fVal = 0.0;
+ if (mrDoc.HasPivotTable())
+ {
+ const ScDPCollection* pDPs = mrDoc.GetDPCollection();
+ fVal = pDPs->GetCount();
+ }
+ PushDouble(fVal);
+ }
+ else if (aStrUpper == "DATASTREAM_IMPORT")
+ PushDouble( sc::datastream_get_time( sc::DebugTime::Import ) );
+ else if (aStrUpper == "DATASTREAM_RECALC")
+ PushDouble( sc::datastream_get_time( sc::DebugTime::Recalc ) );
+ else if (aStrUpper == "DATASTREAM_RENDER")
+ PushDouble( sc::datastream_get_time( sc::DebugTime::Render ) );
+ else
+ PushIllegalParameter();
+}
+
+void ScInterpreter::ScErf()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (MustHaveParamCount( nParamCount, 1 ) )
+ PushDouble( std::erf( GetDouble() ) );
+}
+
+void ScInterpreter::ScErfc()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if (MustHaveParamCount( nParamCount, 1 ) )
+ PushDouble( std::erfc( GetDouble() ) );
+}
+
+void ScInterpreter::ScColor()
+{
+ sal_uInt8 nParamCount = GetByte();
+ if(!MustHaveParamCount(nParamCount, 3, 4))
+ return;
+
+ double nAlpha = 0;
+ if(nParamCount == 4)
+ nAlpha = rtl::math::approxFloor(GetDouble());
+
+ if(nAlpha < 0 || nAlpha > 255)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ double nBlue = rtl::math::approxFloor(GetDouble());
+
+ if(nBlue < 0 || nBlue > 255)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ double nGreen = rtl::math::approxFloor(GetDouble());
+
+ if(nGreen < 0 || nGreen > 255)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ double nRed = rtl::math::approxFloor(GetDouble());
+
+ if(nRed < 0 || nRed > 255)
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ double nVal = 256*256*256*nAlpha + 256*256*nRed + 256*nGreen + nBlue;
+ PushDouble(nVal);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr8.cxx b/sc/source/core/tool/interpr8.cxx
new file mode 100644
index 0000000000..8d3f7c25f4
--- /dev/null
+++ b/sc/source/core/tool/interpr8.cxx
@@ -0,0 +1,2008 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <interpre.hxx>
+#include <cellvalue.hxx>
+#include <scmatrix.hxx>
+#include <comphelper/random.hxx>
+#include <formula/token.hxx>
+#include <sal/log.hxx>
+#include <svl/numformat.hxx>
+
+#include <cmath>
+#include <memory>
+#include <vector>
+
+using namespace formula;
+
+namespace {
+
+struct DataPoint
+{
+ double X, Y;
+
+ DataPoint( double rX, double rY ) : X( rX ), Y( rY ) {};
+};
+
+}
+
+static bool lcl_SortByX( const DataPoint &lhs, const DataPoint &rhs ) { return lhs.X < rhs.X; }
+
+/*
+ * ScETSForecastCalculation
+ *
+ * Class is set up to be used with Calc's FORECAST.ETS
+ * functions and with chart extrapolations (not yet implemented).
+ *
+ * Triple Exponential Smoothing (Holt-Winters method)
+ *
+ * Forecasting of a linear change in data over time (y=a+b*x) with
+ * superimposed absolute or relative seasonal deviations, using additive
+ * respectively multiplicative Holt-Winters method.
+ *
+ * Initialisation and forecasting calculations are based on
+ * Engineering Statistics Handbook, 6.4.3.5 Triple Exponential Smoothing
+ * see "http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc435.htm"
+ * Further to the above is that initial calculation of Seasonal effect
+ * is corrected for trend.
+ *
+ * Prediction Interval calculations are based on
+ * Yar & Chatfield, Prediction Intervals for the Holt-Winters forecasting
+ * procedure, International Journal of Forecasting, 1990, Vol.6, pp127-137
+ * The calculation here is a simplified numerical approximation of the above,
+ * using random distributions.
+ *
+ * Double Exponential Smoothing (Holt-Winters method)
+ *
+ * Forecasting of a linear change in data over time (y=a+b*x), using
+ * the Holt-Winters method.
+ *
+ * Initialisation and forecasting calculations are based on
+ * Engineering Statistics Handbook, 6.4.3.3 Double Exponential Smoothing
+ * see "http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc433.htm"
+ *
+ * Prediction Interval calculations are based on
+ * Statistical Methods for Forecasting, Bovas & Ledolter, 2009, 3.8 Prediction
+ * Intervals for Future Values
+ *
+ */
+
+namespace {
+
+class ScETSForecastCalculation
+{
+private:
+ SvNumberFormatter* mpFormatter;
+ std::vector< DataPoint > maRange; // data (X, Y)
+ std::unique_ptr<double[]> mpBase; // calculated base value array
+ std::unique_ptr<double[]> mpTrend; // calculated trend factor array
+ std::unique_ptr<double[]> mpPerIdx; // calculated periodical deviation array, not used with eds
+ std::unique_ptr<double[]> mpForecast; // forecasted value array
+ SCSIZE mnSmplInPrd; // samples per period
+ double mfStepSize; // increment of X in maRange
+ double mfAlpha, mfBeta, mfGamma; // constants to minimize the RMSE in the ES-equations
+ SCSIZE mnCount; // No of data points
+ bool mbInitialised;
+ int mnMonthDay; // n-month X-interval, value is day of month
+ // accuracy indicators
+ double mfMAE; // mean absolute error
+ double mfMASE; // mean absolute scaled error
+ double mfMSE; // mean squared error (variation)
+ double mfRMSE; // root mean squared error (standard deviation)
+ double mfSMAPE; // symmetric mean absolute error
+ FormulaError mnErrorValue;
+ bool bAdditive; // true: additive method, false: multiplicative method
+ bool bEDS; // true: EDS, false: ETS
+
+ // constants used in determining best fit for alpha, beta, gamma
+ static constexpr double cfMinABCResolution = 0.001; // minimum change of alpha, beta, gamma
+ static const SCSIZE cnScenarios = 1000; // No. of scenarios to calculate for PI calculations
+
+ bool initData();
+ void prefillBaseData();
+ bool prefillTrendData();
+ bool prefillPerIdx();
+ void initCalc();
+ void refill();
+ SCSIZE CalcPeriodLen();
+ void CalcAlphaBetaGamma();
+ void CalcBetaGamma();
+ void CalcGamma();
+ void calcAccuracyIndicators();
+ void GetForecast( double fTarget, double& rForecast );
+ double RandDev();
+ double convertXtoMonths( double x );
+
+public:
+ ScETSForecastCalculation( SCSIZE nSize, SvNumberFormatter* pFormatter );
+
+ bool PreprocessDataRange( const ScMatrixRef& rMatX, const ScMatrixRef& rMatY, int nSmplInPrd,
+ bool bDataCompletion, int nAggregation, const ScMatrixRef& rTMat,
+ ScETSType eETSType );
+ FormulaError GetError() const { return mnErrorValue; };
+ void GetForecastRange( const ScMatrixRef& rTMat, const ScMatrixRef& rFcMat );
+ void GetStatisticValue( const ScMatrixRef& rTypeMat, const ScMatrixRef& rStatMat );
+ void GetSamplesInPeriod( double& rVal );
+ void GetEDSPredictionIntervals( const ScMatrixRef& rTMat, const ScMatrixRef& rPIMat, double fPILevel );
+ void GetETSPredictionIntervals( const ScMatrixRef& rTMat, const ScMatrixRef& rPIMat, double fPILevel );
+};
+
+}
+
+ScETSForecastCalculation::ScETSForecastCalculation( SCSIZE nSize, SvNumberFormatter* pFormatter )
+ : mpFormatter(pFormatter)
+ , mnSmplInPrd(0)
+ , mfStepSize(0.0)
+ , mfAlpha(0.0)
+ , mfBeta(0.0)
+ , mfGamma(0.0)
+ , mnCount(nSize)
+ , mbInitialised(false)
+ , mnMonthDay(0)
+ , mfMAE(0.0)
+ , mfMASE(0.0)
+ , mfMSE(0.0)
+ , mfRMSE(0.0)
+ , mfSMAPE(0.0)
+ , mnErrorValue(FormulaError::NONE)
+ , bAdditive(false)
+ , bEDS(false)
+{
+ maRange.reserve( mnCount );
+}
+
+bool ScETSForecastCalculation::PreprocessDataRange( const ScMatrixRef& rMatX, const ScMatrixRef& rMatY, int nSmplInPrd,
+ bool bDataCompletion, int nAggregation, const ScMatrixRef& rTMat,
+ ScETSType eETSType )
+{
+ bEDS = ( nSmplInPrd == 0 );
+ bAdditive = ( eETSType == etsAdd || eETSType == etsPIAdd || eETSType == etsStatAdd );
+
+ // maRange needs to be sorted by X
+ for ( SCSIZE i = 0; i < mnCount; i++ )
+ maRange.emplace_back( rMatX->GetDouble( i ), rMatY->GetDouble( i ) );
+ sort( maRange.begin(), maRange.end(), lcl_SortByX );
+
+ if ( rTMat )
+ {
+ if ( eETSType != etsPIAdd && eETSType != etsPIMult )
+ {
+ if ( rTMat->GetDouble( 0 ) < maRange[ 0 ].X )
+ {
+ // target cannot be less than start of X-range
+ mnErrorValue = FormulaError::IllegalFPOperation;
+ return false;
+ }
+ }
+ else
+ {
+ if ( rTMat->GetDouble( 0 ) < maRange[ mnCount - 1 ].X )
+ {
+ // target cannot be before end of X-range
+ mnErrorValue = FormulaError::IllegalFPOperation;
+ return false;
+ }
+ }
+ }
+
+ // Month intervals don't have exact stepsize, so first
+ // detect if month interval is used.
+ // Method: assume there is an month interval and verify.
+ // If month interval is used, replace maRange.X with month values
+ // for ease of calculations.
+ Date aNullDate = mpFormatter->GetNullDate();
+ Date aDate = aNullDate + static_cast< sal_Int32 >( maRange[ 0 ].X );
+ mnMonthDay = aDate.GetDay();
+ for ( SCSIZE i = 1; i < mnCount && mnMonthDay; i++ )
+ {
+ Date aDate1 = aNullDate + static_cast< sal_Int32 >( maRange[ i ].X );
+ if ( aDate != aDate1 )
+ {
+ if ( aDate1.GetDay() != mnMonthDay )
+ mnMonthDay = 0;
+ }
+ }
+
+ mfStepSize = ::std::numeric_limits<double>::max();
+ if ( mnMonthDay )
+ {
+ for ( SCSIZE i = 0; i < mnCount; i++ )
+ {
+ aDate = aNullDate + static_cast< sal_Int32 >( maRange[ i ].X );
+ maRange[ i ].X = aDate.GetYear() * 12 + aDate.GetMonth();
+ }
+ }
+ for ( SCSIZE i = 1; i < mnCount; i++ )
+ {
+ double fStep = maRange[ i ].X - maRange[ i - 1 ].X;
+ if ( fStep == 0.0 )
+ {
+ if ( nAggregation == 0 )
+ {
+ // identical X-values are not allowed
+ mnErrorValue = FormulaError::NoValue;
+ return false;
+ }
+ double fTmp = maRange[ i - 1 ].Y;
+ SCSIZE nCounter = 1;
+ switch ( nAggregation )
+ {
+ case 1 : // AVERAGE (default)
+ while ( i < mnCount && maRange[ i ].X == maRange[ i - 1 ].X )
+ {
+ maRange.erase( maRange.begin() + i );
+ --mnCount;
+ }
+ break;
+ case 7 : // SUM
+ while ( i < mnCount && maRange[ i ].X == maRange[ i - 1 ].X )
+ {
+ fTmp += maRange[ i ].Y;
+ maRange.erase( maRange.begin() + i );
+ --mnCount;
+ }
+ maRange[ i - 1 ].Y = fTmp;
+ break;
+
+ case 2 : // COUNT
+ case 3 : // COUNTA (same as COUNT as there are no non-numeric Y-values)
+ while ( i < mnCount && maRange[ i ].X == maRange[ i - 1 ].X )
+ {
+ nCounter++;
+ maRange.erase( maRange.begin() + i );
+ --mnCount;
+ }
+ maRange[ i - 1 ].Y = nCounter;
+ break;
+
+ case 4 : // MAX
+ while ( i < mnCount && maRange[ i ].X == maRange[ i - 1 ].X )
+ {
+ if ( maRange[ i ].Y > fTmp )
+ fTmp = maRange[ i ].Y;
+ maRange.erase( maRange.begin() + i );
+ --mnCount;
+ }
+ maRange[ i - 1 ].Y = fTmp;
+ break;
+
+ case 5 : // MEDIAN
+ {
+ std::vector< double > aTmp { maRange[ i - 1 ].Y };
+ while ( i < mnCount && maRange[ i ].X == maRange[ i - 1 ].X )
+ {
+ aTmp.push_back( maRange[ i ].Y );
+ nCounter++;
+ maRange.erase( maRange.begin() + i );
+ --mnCount;
+ }
+ sort( aTmp.begin(), aTmp.end() );
+
+ if ( nCounter % 2 )
+ maRange[ i - 1 ].Y = aTmp[ nCounter / 2 ];
+ else
+ maRange[ i - 1 ].Y = ( aTmp[ nCounter / 2 ] + aTmp[ nCounter / 2 - 1 ] ) / 2.0;
+ }
+ break;
+
+ case 6 : // MIN
+ while ( i < mnCount && maRange[ i ].X == maRange[ i - 1 ].X )
+ {
+ if ( maRange[ i ].Y < fTmp )
+ fTmp = maRange[ i ].Y;
+ maRange.erase( maRange.begin() + i );
+ --mnCount;
+ }
+ maRange[ i - 1 ].Y = fTmp;
+ break;
+ }
+ if ( i < mnCount - 1 )
+ fStep = maRange[ i ].X - maRange[ i - 1 ].X;
+ else
+ fStep = mfStepSize;
+ }
+ if ( fStep > 0 && fStep < mfStepSize )
+ mfStepSize = fStep;
+ }
+
+ // step must be constant (or gap multiple of step)
+ bool bHasGap = false;
+ for ( SCSIZE i = 1; i < mnCount && !bHasGap; i++ )
+ {
+ double fStep = maRange[ i ].X - maRange[ i - 1 ].X;
+
+ if ( fStep != mfStepSize )
+ {
+ if ( fmod( fStep, mfStepSize ) != 0.0 )
+ {
+ // step not constant nor multiple of mfStepSize in case of gaps
+ mnErrorValue = FormulaError::NoValue;
+ return false;
+ }
+ bHasGap = true;
+ }
+ }
+
+ // fill gaps with values depending on bDataCompletion
+ if ( bHasGap )
+ {
+ SCSIZE nMissingXCount = 0;
+ double fOriginalCount = static_cast< double >( mnCount );
+ if ( mnMonthDay )
+ aDate = aNullDate + static_cast< sal_Int32 >( maRange[ 0 ].X );
+ for ( SCSIZE i = 1; i < mnCount; i++ )
+ {
+ double fDist;
+ if ( mnMonthDay )
+ {
+ Date aDate1 = aNullDate + static_cast< sal_Int32 >( maRange[ i ].X );
+ fDist = 12 * ( aDate1.GetYear() - aDate.GetYear() ) +
+ ( aDate1.GetMonth() - aDate.GetMonth() );
+ aDate = aDate1;
+ }
+ else
+ fDist = maRange[ i ].X - maRange[ i - 1 ].X;
+ if ( fDist > mfStepSize )
+ {
+ // gap, insert missing data points
+ double fYGap = ( maRange[ i ].Y + maRange[ i - 1 ].Y ) / 2.0;
+ for ( KahanSum fXGap = maRange[ i - 1].X + mfStepSize; fXGap < maRange[ i ].X; fXGap += mfStepSize )
+ {
+ maRange.insert( maRange.begin() + i, DataPoint( fXGap.get(), ( bDataCompletion ? fYGap : 0.0 ) ) );
+ i++;
+ mnCount++;
+ nMissingXCount++;
+ if ( static_cast< double >( nMissingXCount ) / fOriginalCount > 0.3 )
+ {
+ // maximum of 30% missing points exceeded
+ mnErrorValue = FormulaError::NoValue;
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if ( nSmplInPrd != 1 )
+ mnSmplInPrd = nSmplInPrd;
+ else
+ {
+ mnSmplInPrd = CalcPeriodLen();
+ if ( mnSmplInPrd == 1 )
+ bEDS = true; // period length 1 means no periodic data: EDS suffices
+ }
+
+ if ( !initData() )
+ return false; // note: mnErrorValue is set in called function(s)
+
+ return true;
+}
+
+bool ScETSForecastCalculation::initData( )
+{
+ // give various vectors size and initial value
+ mpBase.reset( new double[ mnCount ] );
+ mpTrend.reset( new double[ mnCount ] );
+ if ( !bEDS )
+ mpPerIdx.reset( new double[ mnCount ] );
+ mpForecast.reset( new double[ mnCount ] );
+ mpForecast[ 0 ] = maRange[ 0 ].Y;
+
+ if ( prefillTrendData() )
+ {
+ if ( prefillPerIdx() )
+ {
+ prefillBaseData();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScETSForecastCalculation::prefillTrendData()
+{
+ if ( bEDS )
+ mpTrend[ 0 ] = ( maRange[ mnCount - 1 ].Y - maRange[ 0 ].Y ) / static_cast< double >( mnCount - 1 );
+ else
+ {
+ // we need at least 2 periods in the data range
+ if ( mnCount < 2 * mnSmplInPrd )
+ {
+ mnErrorValue = FormulaError::NoValue;
+ return false;
+ }
+
+ KahanSum fSum = 0.0;
+ for ( SCSIZE i = 0; i < mnSmplInPrd; i++ )
+ {
+ fSum += maRange[ i + mnSmplInPrd ].Y;
+ fSum -= maRange[ i ].Y;
+ }
+ double fTrend = fSum.get() / static_cast< double >( mnSmplInPrd * mnSmplInPrd );
+
+ mpTrend[ 0 ] = fTrend;
+ }
+
+ return true;
+}
+
+bool ScETSForecastCalculation::prefillPerIdx()
+{
+ if ( !bEDS )
+ {
+ // use as many complete periods as available
+ if ( mnSmplInPrd == 0 )
+ {
+ // should never happen; if mnSmplInPrd equals 0, bEDS is true
+ mnErrorValue = FormulaError::UnknownState;
+ return false;
+ }
+ SCSIZE nPeriods = mnCount / mnSmplInPrd;
+ std::vector< KahanSum > aPeriodAverage( nPeriods, 0.0 );
+ for ( SCSIZE i = 0; i < nPeriods ; i++ )
+ {
+ for ( SCSIZE j = 0; j < mnSmplInPrd; j++ )
+ aPeriodAverage[ i ] += maRange[ i * mnSmplInPrd + j ].Y;
+ aPeriodAverage[ i ] /= static_cast< double >( mnSmplInPrd );
+ if ( aPeriodAverage[ i ] == 0.0 )
+ {
+ SAL_WARN( "sc.core", "prefillPerIdx(), average of 0 will cause divide by zero error, quitting calculation" );
+ mnErrorValue = FormulaError::DivisionByZero;
+ return false;
+ }
+ }
+
+ for ( SCSIZE j = 0; j < mnSmplInPrd; j++ )
+ {
+ KahanSum fI = 0.0;
+ for ( SCSIZE i = 0; i < nPeriods ; i++ )
+ {
+ // adjust average value for position within period
+ if ( bAdditive )
+ fI += maRange[ i * mnSmplInPrd + j ].Y -
+ ( aPeriodAverage[ i ].get() + ( static_cast< double >( j ) - 0.5 * ( mnSmplInPrd - 1 ) ) *
+ mpTrend[ 0 ] );
+ else
+ fI += maRange[ i * mnSmplInPrd + j ].Y /
+ ( aPeriodAverage[ i ].get() + ( static_cast< double >( j ) - 0.5 * ( mnSmplInPrd - 1 ) ) *
+ mpTrend[ 0 ] );
+ }
+ mpPerIdx[ j ] = fI.get() / nPeriods;
+ }
+ if (mnSmplInPrd < mnCount)
+ mpPerIdx[mnSmplInPrd] = 0.0;
+ }
+ return true;
+}
+
+void ScETSForecastCalculation::prefillBaseData()
+{
+ if ( bEDS )
+ mpBase[ 0 ] = maRange[ 0 ].Y;
+ else
+ mpBase[ 0 ] = maRange[ 0 ].Y / mpPerIdx[ 0 ];
+}
+
+void ScETSForecastCalculation::initCalc()
+{
+ if ( !mbInitialised )
+ {
+ CalcAlphaBetaGamma();
+
+ mbInitialised = true;
+ calcAccuracyIndicators();
+ }
+}
+
+void ScETSForecastCalculation::calcAccuracyIndicators()
+{
+ KahanSum fSumAbsErr = 0.0;
+ KahanSum fSumDivisor = 0.0;
+ KahanSum fSumErrSq = 0.0;
+ KahanSum fSumAbsPercErr = 0.0;
+
+ for ( SCSIZE i = 1; i < mnCount; i++ )
+ {
+ double fError = mpForecast[ i ] - maRange[ i ].Y;
+ fSumAbsErr += fabs( fError );
+ fSumErrSq += fError * fError;
+ fSumAbsPercErr += fabs( fError ) / ( fabs( mpForecast[ i ] ) + fabs( maRange[ i ].Y ) );
+ }
+
+ for ( SCSIZE i = 2; i < mnCount; i++ )
+ fSumDivisor += fabs( maRange[ i ].Y - maRange[ i - 1 ].Y );
+
+ int nCalcCount = mnCount - 1;
+ mfMAE = fSumAbsErr.get() / nCalcCount;
+ mfMASE = fSumAbsErr.get() / ( nCalcCount * fSumDivisor.get() / ( nCalcCount - 1 ) );
+ mfMSE = fSumErrSq.get() / nCalcCount;
+ mfRMSE = sqrt( mfMSE );
+ mfSMAPE = fSumAbsPercErr.get() * 2.0 / nCalcCount;
+}
+
+/*
+ * CalcPeriodLen() calculates the most likely length of a period.
+ *
+ * Method used: for all possible values (between mnCount/2 and 2) compare for
+ * each (sample-previous sample) with next period and calculate mean error.
+ * Use as much samples as possible for each period length and the most recent samples
+ * Return the period length with the lowest mean error.
+ */
+SCSIZE ScETSForecastCalculation::CalcPeriodLen()
+{
+ SCSIZE nBestVal = mnCount;
+ double fBestME = ::std::numeric_limits<double>::max();
+
+ for ( SCSIZE nPeriodLen = mnCount / 2; nPeriodLen >= 1; nPeriodLen-- )
+ {
+ KahanSum fMeanError = 0.0;
+ SCSIZE nPeriods = mnCount / nPeriodLen;
+ SCSIZE nStart = mnCount - ( nPeriods * nPeriodLen ) + 1;
+ for ( SCSIZE i = nStart; i < ( mnCount - nPeriodLen ); i++ )
+ {
+ fMeanError += fabs( ( maRange[ i ].Y - maRange[ i - 1 ].Y ) -
+ ( maRange[ nPeriodLen + i ].Y - maRange[ nPeriodLen + i - 1 ].Y ) );
+ }
+ double fMeanErrorGet = fMeanError.get();
+ fMeanErrorGet /= static_cast< double >( ( nPeriods - 1 ) * nPeriodLen - 1 );
+
+ if ( fMeanErrorGet <= fBestME || fMeanErrorGet == 0.0 )
+ {
+ nBestVal = nPeriodLen;
+ fBestME = fMeanErrorGet;
+ }
+ }
+ return nBestVal;
+}
+
+void ScETSForecastCalculation::CalcAlphaBetaGamma()
+{
+ double f0 = 0.0;
+ mfAlpha = f0;
+ if ( bEDS )
+ {
+ mfBeta = 0.0; // beta is not used with EDS
+ CalcGamma();
+ }
+ else
+ CalcBetaGamma();
+ refill();
+ double fE0 = mfMSE;
+
+ double f2 = 1.0;
+ mfAlpha = f2;
+ if ( bEDS )
+ CalcGamma();
+ else
+ CalcBetaGamma();
+ refill();
+ double fE2 = mfMSE;
+
+ double f1 = 0.5;
+ mfAlpha = f1;
+ if ( bEDS )
+ CalcGamma();
+ else
+ CalcBetaGamma();
+ refill();
+
+ if ( fE0 == mfMSE && mfMSE == fE2 )
+ {
+ mfAlpha = 0;
+ if ( bEDS )
+ CalcGamma();
+ else
+ CalcBetaGamma();
+ refill();
+ return;
+ }
+ while ( ( f2 - f1 ) > cfMinABCResolution )
+ {
+ if ( fE2 > fE0 )
+ {
+ f2 = f1;
+ fE2 = mfMSE;
+ f1 = ( f0 + f1 ) / 2;
+ }
+ else
+ {
+ f0 = f1;
+ fE0 = mfMSE;
+ f1 = ( f1 + f2 ) / 2;
+ }
+ mfAlpha = f1;
+ if ( bEDS )
+ CalcGamma();
+ else
+ CalcBetaGamma();
+ refill();
+ }
+ if ( fE2 > fE0 )
+ {
+ if ( fE0 < mfMSE )
+ {
+ mfAlpha = f0;
+ if ( bEDS )
+ CalcGamma();
+ else
+ CalcBetaGamma();
+ refill();
+ }
+ }
+ else
+ {
+ if ( fE2 < mfMSE )
+ {
+ mfAlpha = f2;
+ if ( bEDS )
+ CalcGamma();
+ else
+ CalcBetaGamma();
+ refill();
+ }
+ }
+ calcAccuracyIndicators();
+}
+
+void ScETSForecastCalculation::CalcBetaGamma()
+{
+ double f0 = 0.0;
+ mfBeta = f0;
+ CalcGamma();
+ refill();
+ double fE0 = mfMSE;
+
+ double f2 = 1.0;
+ mfBeta = f2;
+ CalcGamma();
+ refill();
+ double fE2 = mfMSE;
+
+ double f1 = 0.5;
+ mfBeta = f1;
+ CalcGamma();
+ refill();
+
+ if ( fE0 == mfMSE && mfMSE == fE2 )
+ {
+ mfBeta = 0;
+ CalcGamma();
+ refill();
+ return;
+ }
+ while ( ( f2 - f1 ) > cfMinABCResolution )
+ {
+ if ( fE2 > fE0 )
+ {
+ f2 = f1;
+ fE2 = mfMSE;
+ f1 = ( f0 + f1 ) / 2;
+ }
+ else
+ {
+ f0 = f1;
+ fE0 = mfMSE;
+ f1 = ( f1 + f2 ) / 2;
+ }
+ mfBeta = f1;
+ CalcGamma();
+ refill();
+ }
+ if ( fE2 > fE0 )
+ {
+ if ( fE0 < mfMSE )
+ {
+ mfBeta = f0;
+ CalcGamma();
+ refill();
+ }
+ }
+ else
+ {
+ if ( fE2 < mfMSE )
+ {
+ mfBeta = f2;
+ CalcGamma();
+ refill();
+ }
+ }
+}
+
+void ScETSForecastCalculation::CalcGamma()
+{
+ double f0 = 0.0;
+ mfGamma = f0;
+ refill();
+ double fE0 = mfMSE;
+
+ double f2 = 1.0;
+ mfGamma = f2;
+ refill();
+ double fE2 = mfMSE;
+
+ double f1 = 0.5;
+ mfGamma = f1;
+ refill();
+
+ if ( fE0 == mfMSE && mfMSE == fE2 )
+ {
+ mfGamma = 0;
+ refill();
+ return;
+ }
+ while ( ( f2 - f1 ) > cfMinABCResolution )
+ {
+ if ( fE2 > fE0 )
+ {
+ f2 = f1;
+ fE2 = mfMSE;
+ f1 = ( f0 + f1 ) / 2;
+ }
+ else
+ {
+ f0 = f1;
+ fE0 = mfMSE;
+ f1 = ( f1 + f2 ) / 2;
+ }
+ mfGamma = f1;
+ refill();
+ }
+ if ( fE2 > fE0 )
+ {
+ if ( fE0 < mfMSE )
+ {
+ mfGamma = f0;
+ refill();
+ }
+ }
+ else
+ {
+ if ( fE2 < mfMSE )
+ {
+ mfGamma = f2;
+ refill();
+ }
+ }
+}
+
+void ScETSForecastCalculation::refill()
+{
+ // refill mpBase, mpTrend, mpPerIdx and mpForecast with values
+ // using the calculated mfAlpha, (mfBeta), mfGamma
+ // forecast 1 step ahead
+ for ( SCSIZE i = 1; i < mnCount; i++ )
+ {
+ if ( bEDS )
+ {
+ mpBase[ i ] = mfAlpha * maRange[ i ].Y +
+ ( 1 - mfAlpha ) * ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] );
+ mpTrend[ i ] = mfGamma * ( mpBase[ i ] - mpBase[ i - 1 ] ) +
+ ( 1 - mfGamma ) * mpTrend[ i - 1 ];
+ mpForecast[ i ] = mpBase[ i - 1 ] + mpTrend[ i - 1 ];
+ }
+ else
+ {
+ SCSIZE nIdx;
+ if ( bAdditive )
+ {
+ nIdx = ( i > mnSmplInPrd ? i - mnSmplInPrd : i );
+ mpBase[ i ] = mfAlpha * ( maRange[ i ].Y - mpPerIdx[ nIdx ] ) +
+ ( 1 - mfAlpha ) * ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] );
+ mpPerIdx[ i ] = mfBeta * ( maRange[ i ].Y - mpBase[ i ] ) +
+ ( 1 - mfBeta ) * mpPerIdx[ nIdx ];
+ }
+ else
+ {
+ nIdx = ( i >= mnSmplInPrd ? i - mnSmplInPrd : i );
+ mpBase[ i ] = mfAlpha * ( maRange[ i ].Y / mpPerIdx[ nIdx ] ) +
+ ( 1 - mfAlpha ) * ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] );
+ mpPerIdx[ i ] = mfBeta * ( maRange[ i ].Y / mpBase[ i ] ) +
+ ( 1 - mfBeta ) * mpPerIdx[ nIdx ];
+ }
+ mpTrend[ i ] = mfGamma * ( mpBase[ i ] - mpBase[ i - 1 ] ) +
+ ( 1 - mfGamma ) * mpTrend[ i - 1 ];
+
+ if ( bAdditive )
+ mpForecast[ i ] = mpBase[ i - 1 ] + mpTrend[ i - 1 ] + mpPerIdx[ nIdx ];
+ else
+ mpForecast[ i ] = ( mpBase[ i - 1 ] + mpTrend[ i - 1 ] ) * mpPerIdx[ nIdx ];
+ }
+ }
+ calcAccuracyIndicators();
+}
+
+double ScETSForecastCalculation::convertXtoMonths( double x )
+{
+ Date aDate = mpFormatter->GetNullDate() + static_cast< sal_Int32 >( x );
+ int nYear = aDate.GetYear();
+ int nMonth = aDate.GetMonth();
+ double fMonthLength;
+ switch ( nMonth )
+ {
+ case 1 :
+ case 3 :
+ case 5 :
+ case 7 :
+ case 8 :
+ case 10 :
+ case 12 :
+ fMonthLength = 31.0;
+ break;
+ case 2 :
+ fMonthLength = ( aDate.IsLeapYear() ? 29.0 : 28.0 );
+ break;
+ default :
+ fMonthLength = 30.0;
+ }
+ return ( 12.0 * nYear + nMonth + ( aDate.GetDay() - mnMonthDay ) / fMonthLength );
+}
+
+void ScETSForecastCalculation::GetForecast( double fTarget, double& rForecast )
+{
+ initCalc();
+
+ if ( fTarget <= maRange[ mnCount - 1 ].X )
+ {
+ SCSIZE n = ( fTarget - maRange[ 0 ].X ) / mfStepSize;
+ double fInterpolate = fmod( fTarget - maRange[ 0 ].X, mfStepSize );
+ rForecast = maRange[ n ].Y;
+
+ if ( fInterpolate >= cfMinABCResolution )
+ {
+ double fInterpolateFactor = fInterpolate / mfStepSize;
+ double fFc_1 = mpForecast[ n + 1 ];
+ rForecast = rForecast + fInterpolateFactor * ( fFc_1 - rForecast );
+ }
+ }
+ else
+ {
+ SCSIZE n = ( fTarget - maRange[ mnCount - 1 ].X ) / mfStepSize;
+ double fInterpolate = fmod( fTarget - maRange[ mnCount - 1 ].X, mfStepSize );
+
+ if ( bEDS )
+ rForecast = mpBase[ mnCount - 1 ] + n * mpTrend[ mnCount - 1 ];
+ else if ( bAdditive )
+ rForecast = mpBase[ mnCount - 1 ] + n * mpTrend[ mnCount - 1 ] +
+ mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( n % mnSmplInPrd ) ];
+ else
+ rForecast = ( mpBase[ mnCount - 1 ] + n * mpTrend[ mnCount - 1 ] ) *
+ mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( n % mnSmplInPrd ) ];
+
+ if ( fInterpolate >= cfMinABCResolution )
+ {
+ double fInterpolateFactor = fInterpolate / mfStepSize;
+ double fFc_1;
+ if ( bEDS )
+ fFc_1 = mpBase[ mnCount - 1 ] + ( n + 1 ) * mpTrend[ mnCount - 1 ];
+ else if ( bAdditive )
+ fFc_1 = mpBase[ mnCount - 1 ] + ( n + 1 ) * mpTrend[ mnCount - 1 ] +
+ mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( ( n + 1 ) % mnSmplInPrd ) ];
+ else
+ fFc_1 = ( mpBase[ mnCount - 1 ] + ( n + 1 ) * mpTrend[ mnCount - 1 ] ) *
+ mpPerIdx[ mnCount - 1 - mnSmplInPrd + ( ( n + 1 ) % mnSmplInPrd ) ];
+ rForecast = rForecast + fInterpolateFactor * ( fFc_1 - rForecast );
+ }
+ }
+}
+
+void ScETSForecastCalculation::GetForecastRange( const ScMatrixRef& rTMat, const ScMatrixRef& rFcMat )
+{
+ SCSIZE nC, nR;
+ rTMat->GetDimensions( nC, nR );
+
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ double fTarget;
+ if ( mnMonthDay )
+ fTarget = convertXtoMonths( rTMat->GetDouble( j, i ) );
+ else
+ fTarget = rTMat->GetDouble( j, i );
+ double fForecast;
+ GetForecast( fTarget, fForecast );
+ rFcMat->PutDouble( fForecast, j, i );
+ }
+ }
+}
+
+void ScETSForecastCalculation::GetStatisticValue( const ScMatrixRef& rTypeMat, const ScMatrixRef& rStatMat )
+{
+ initCalc();
+
+ SCSIZE nC, nR;
+ rTypeMat->GetDimensions( nC, nR );
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ switch ( static_cast< int >( rTypeMat->GetDouble( j, i ) ) )
+ {
+ case 1 : // alpha
+ rStatMat->PutDouble( mfAlpha, j, i );
+ break;
+ case 2 : // gamma
+ rStatMat->PutDouble( mfGamma, j, i );
+ break;
+ case 3 : // beta
+ rStatMat->PutDouble( mfBeta, j, i );
+ break;
+ case 4 : // MASE
+ rStatMat->PutDouble( mfMASE, j, i );
+ break;
+ case 5 : // SMAPE
+ rStatMat->PutDouble( mfSMAPE, j, i );
+ break;
+ case 6 : // MAE
+ rStatMat->PutDouble( mfMAE, j, i );
+ break;
+ case 7 : // RMSE
+ rStatMat->PutDouble( mfRMSE, j, i );
+ break;
+ case 8 : // step size
+ rStatMat->PutDouble( mfStepSize, j, i );
+ break;
+ case 9 : // samples in period
+ rStatMat->PutDouble( mnSmplInPrd, j, i );
+ break;
+ }
+ }
+ }
+}
+
+void ScETSForecastCalculation::GetSamplesInPeriod( double& rVal )
+{
+ rVal = mnSmplInPrd;
+}
+
+double ScETSForecastCalculation::RandDev()
+{
+ // return a random deviation given the standard deviation
+ return ( mfRMSE * ScInterpreter::gaussinv(
+ ::comphelper::rng::uniform_real_distribution( 0.5, 1.0 ) ) );
+}
+
+void ScETSForecastCalculation::GetETSPredictionIntervals( const ScMatrixRef& rTMat, const ScMatrixRef& rPIMat, double fPILevel )
+{
+ initCalc();
+
+ SCSIZE nC, nR;
+ rTMat->GetDimensions( nC, nR );
+
+ // find maximum target value and calculate size of scenario-arrays
+ double fMaxTarget = rTMat->GetDouble( 0, 0 );
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ if ( fMaxTarget < rTMat->GetDouble( j, i ) )
+ fMaxTarget = rTMat->GetDouble( j, i );
+ }
+ }
+ if ( mnMonthDay )
+ fMaxTarget = convertXtoMonths( fMaxTarget ) - maRange[ mnCount - 1 ].X;
+ else
+ fMaxTarget -= maRange[ mnCount - 1 ].X;
+ SCSIZE nSize = fMaxTarget / mfStepSize;
+ if ( fmod( fMaxTarget, mfStepSize ) != 0.0 )
+ nSize++;
+
+ if (nSize == 0)
+ {
+ mnErrorValue = FormulaError::IllegalArgument;
+ return;
+ }
+
+ std::unique_ptr< double[] > xScenRange( new double[nSize]);
+ std::unique_ptr< double[] > xScenBase( new double[nSize]);
+ std::unique_ptr< double[] > xScenTrend( new double[nSize]);
+ std::unique_ptr< double[] > xScenPerIdx( new double[nSize]);
+ std::vector< std::vector< double > > aPredictions( nSize, std::vector< double >( cnScenarios ) );
+
+ // fill scenarios
+ for ( SCSIZE k = 0; k < cnScenarios; k++ )
+ {
+ // fill array with forecasts, with RandDev() added to xScenRange
+ if ( bAdditive )
+ {
+ double nPIdx = !bEDS ? mpPerIdx[mnCount - mnSmplInPrd] : 0.0;
+ // calculation based on additive model
+ xScenRange[ 0 ] = mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] +
+ nPIdx +
+ RandDev();
+ aPredictions[ 0 ][ k ] = xScenRange[ 0 ];
+ xScenBase[ 0 ] = mfAlpha * ( xScenRange[ 0 ] - nPIdx ) +
+ ( 1 - mfAlpha ) * ( mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] );
+ xScenTrend[ 0 ] = mfGamma * ( xScenBase[ 0 ] - mpBase[ mnCount - 1 ] ) +
+ ( 1 - mfGamma ) * mpTrend[ mnCount - 1 ];
+ xScenPerIdx[ 0 ] = mfBeta * ( xScenRange[ 0 ] - xScenBase[ 0 ] ) +
+ ( 1 - mfBeta ) * nPIdx;
+ for ( SCSIZE i = 1; i < nSize; i++ )
+ {
+ double fPerIdx;
+ if ( i < mnSmplInPrd )
+ fPerIdx = mpPerIdx[ mnCount + i - mnSmplInPrd ];
+ else
+ fPerIdx = xScenPerIdx[ i - mnSmplInPrd ];
+ xScenRange[ i ] = xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] + fPerIdx + RandDev();
+ aPredictions[ i ][ k ] = xScenRange[ i ];
+ xScenBase[ i ] = mfAlpha * ( xScenRange[ i ] - fPerIdx ) +
+ ( 1 - mfAlpha ) * ( xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] );
+ xScenTrend[ i ] = mfGamma * ( xScenBase[ i ] - xScenBase[ i - 1 ] ) +
+ ( 1 - mfGamma ) * xScenTrend[ i - 1 ];
+ xScenPerIdx[ i ] = mfBeta * ( xScenRange[ i ] - xScenBase[ i ] ) +
+ ( 1 - mfBeta ) * fPerIdx;
+ }
+ }
+ else
+ {
+ // calculation based on multiplicative model
+ xScenRange[ 0 ] = ( mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] ) *
+ mpPerIdx[ mnCount - mnSmplInPrd ] +
+ RandDev();
+ aPredictions[ 0 ][ k ] = xScenRange[ 0 ];
+ xScenBase[ 0 ] = mfAlpha * ( xScenRange[ 0 ] / mpPerIdx[ mnCount - mnSmplInPrd ] ) +
+ ( 1 - mfAlpha ) * ( mpBase[ mnCount - 1 ] + mpTrend[ mnCount - 1 ] );
+ xScenTrend[ 0 ] = mfGamma * ( xScenBase[ 0 ] - mpBase[ mnCount - 1 ] ) +
+ ( 1 - mfGamma ) * mpTrend[ mnCount - 1 ];
+ xScenPerIdx[ 0 ] = mfBeta * ( xScenRange[ 0 ] / xScenBase[ 0 ] ) +
+ ( 1 - mfBeta ) * mpPerIdx[ mnCount - mnSmplInPrd ];
+ for ( SCSIZE i = 1; i < nSize; i++ )
+ {
+ double fPerIdx;
+ if ( i < mnSmplInPrd )
+ fPerIdx = mpPerIdx[ mnCount + i - mnSmplInPrd ];
+ else
+ fPerIdx = xScenPerIdx[ i - mnSmplInPrd ];
+ xScenRange[ i ] = ( xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] ) * fPerIdx + RandDev();
+ aPredictions[ i ][ k ] = xScenRange[ i ];
+ xScenBase[ i ] = mfAlpha * ( xScenRange[ i ] / fPerIdx ) +
+ ( 1 - mfAlpha ) * ( xScenBase[ i - 1 ] + xScenTrend[ i - 1 ] );
+ xScenTrend[ i ] = mfGamma * ( xScenBase[ i ] - xScenBase[ i - 1 ] ) +
+ ( 1 - mfGamma ) * xScenTrend[ i - 1 ];
+ xScenPerIdx[ i ] = mfBeta * ( xScenRange[ i ] / xScenBase[ i ] ) +
+ ( 1 - mfBeta ) * fPerIdx;
+ }
+ }
+ }
+
+ // create array of Percentile values;
+ std::unique_ptr< double[] > xPercentile( new double[nSize]);
+ for ( SCSIZE i = 0; i < nSize; i++ )
+ {
+ xPercentile[ i ] = ScInterpreter::GetPercentile( aPredictions[ i ], ( 1 + fPILevel ) / 2 ) -
+ ScInterpreter::GetPercentile( aPredictions[ i ], 0.5 );
+ }
+
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ double fTarget;
+ if ( mnMonthDay )
+ fTarget = convertXtoMonths( rTMat->GetDouble( j, i ) ) - maRange[ mnCount - 1 ].X;
+ else
+ fTarget = rTMat->GetDouble( j, i ) - maRange[ mnCount - 1 ].X;
+ SCSIZE nSteps = ( fTarget / mfStepSize ) - 1;
+ double fFactor = fmod( fTarget, mfStepSize );
+ double fPI = xPercentile[ nSteps ];
+ if ( fFactor != 0.0 )
+ {
+ // interpolate
+ double fPI1 = xPercentile[ nSteps + 1 ];
+ fPI = fPI + fFactor * ( fPI1 - fPI );
+ }
+ rPIMat->PutDouble( fPI, j, i );
+ }
+ }
+}
+
+
+void ScETSForecastCalculation::GetEDSPredictionIntervals( const ScMatrixRef& rTMat, const ScMatrixRef& rPIMat, double fPILevel )
+{
+ initCalc();
+
+ SCSIZE nC, nR;
+ rTMat->GetDimensions( nC, nR );
+
+ // find maximum target value and calculate size of coefficient- array c
+ double fMaxTarget = rTMat->GetDouble( 0, 0 );
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ if ( fMaxTarget < rTMat->GetDouble( j, i ) )
+ fMaxTarget = rTMat->GetDouble( j, i );
+ }
+ }
+ if ( mnMonthDay )
+ fMaxTarget = convertXtoMonths( fMaxTarget ) - maRange[ mnCount - 1 ].X;
+ else
+ fMaxTarget -= maRange[ mnCount - 1 ].X;
+ SCSIZE nSize = fMaxTarget / mfStepSize;
+ if ( fmod( fMaxTarget, mfStepSize ) != 0.0 )
+ nSize++;
+
+ if (nSize == 0)
+ {
+ mnErrorValue = FormulaError::IllegalArgument;
+ return;
+ }
+
+ double z = ScInterpreter::gaussinv( ( 1.0 + fPILevel ) / 2.0 );
+ double o = 1 - fPILevel;
+ std::vector< double > c( nSize );
+ for ( SCSIZE i = 0; i < nSize; i++ )
+ {
+ c[ i ] = sqrt( 1 + ( fPILevel / pow( 1 + o, 3.0 ) ) *
+ ( ( 1 + 4 * o + 5 * o * o ) +
+ 2 * static_cast< double >( i ) * fPILevel * ( 1 + 3 * o ) +
+ 2 * static_cast< double >( i * i ) * fPILevel * fPILevel ) );
+ }
+
+
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ double fTarget;
+ if ( mnMonthDay )
+ fTarget = convertXtoMonths( rTMat->GetDouble( j, i ) ) - maRange[ mnCount - 1 ].X;
+ else
+ fTarget = rTMat->GetDouble( j, i ) - maRange[ mnCount - 1 ].X;
+ SCSIZE nSteps = ( fTarget / mfStepSize ) - 1;
+ double fFactor = fmod( fTarget, mfStepSize );
+ double fPI = z * mfRMSE * c[ nSteps ] / c[ 0 ];
+ if ( fFactor != 0.0 )
+ {
+ // interpolate
+ double fPI1 = z * mfRMSE * c[ nSteps + 1 ] / c[ 0 ];
+ fPI = fPI + fFactor * ( fPI1 - fPI );
+ }
+ rPIMat->PutDouble( fPI, j, i );
+ }
+ }
+}
+
+
+void ScInterpreter::ScForecast_Ets( ScETSType eETSType )
+{
+ sal_uInt8 nParamCount = GetByte();
+ switch ( eETSType )
+ {
+ case etsAdd :
+ case etsMult :
+ case etsStatAdd :
+ case etsStatMult :
+ if ( !MustHaveParamCount( nParamCount, 3, 6 ) )
+ return;
+ break;
+ case etsPIAdd :
+ case etsPIMult :
+ if ( !MustHaveParamCount( nParamCount, 3, 7 ) )
+ {
+ return;
+ }
+ break;
+ case etsSeason :
+ if ( !MustHaveParamCount( nParamCount, 2, 4 ) )
+ return;
+ break;
+ }
+
+ int nAggregation;
+ if ( ( nParamCount == 6 && eETSType != etsPIAdd && eETSType != etsPIMult ) ||
+ ( nParamCount == 4 && eETSType == etsSeason ) ||
+ nParamCount == 7 )
+ nAggregation = static_cast< int >( GetDoubleWithDefault( 1.0 ) );
+ else
+ nAggregation = 1;
+ if ( nAggregation < 1 || nAggregation > 7 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ bool bDataCompletion;
+ if ( ( nParamCount >= 5 && eETSType != etsPIAdd && eETSType != etsPIMult ) ||
+ ( nParamCount >= 3 && eETSType == etsSeason ) ||
+ ( nParamCount >= 6 && ( eETSType == etsPIAdd || eETSType == etsPIMult ) ) )
+ {
+ int nTemp = static_cast< int >( GetDoubleWithDefault( 1.0 ) );
+ if ( nTemp == 0 || nTemp == 1 )
+ bDataCompletion = nTemp;
+ else
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ else
+ bDataCompletion = true;
+
+ int nSmplInPrd;
+ if ( ( ( nParamCount >= 4 && eETSType != etsPIAdd && eETSType != etsPIMult ) ||
+ ( nParamCount >= 5 && ( eETSType == etsPIAdd || eETSType == etsPIMult ) ) ) &&
+ eETSType != etsSeason )
+ {
+ double fVal = GetDoubleWithDefault( 1.0 );
+ if ( fmod( fVal, 1.0 ) != 0 || fVal < 0.0 )
+ {
+ PushError( FormulaError::IllegalFPOperation );
+ return;
+ }
+ nSmplInPrd = static_cast< int >( fVal );
+ }
+ else
+ nSmplInPrd = 1;
+
+ // required arguments
+ double fPILevel = 0.0;
+ if ( nParamCount < 3 && ( nParamCount != 2 || eETSType != etsSeason ) )
+ {
+ PushParameterExpected();
+ return;
+ }
+
+ if ( eETSType == etsPIAdd || eETSType == etsPIMult )
+ {
+ fPILevel = (nParamCount < 4 ? 0.95 : GetDoubleWithDefault( 0.95 ));
+ if ( fPILevel < 0 || fPILevel > 1 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+
+ ScMatrixRef pTypeMat;
+ if ( eETSType == etsStatAdd || eETSType == etsStatMult )
+ {
+ pTypeMat = GetMatrix();
+ SCSIZE nC, nR;
+ pTypeMat->GetDimensions( nC, nR );
+ for ( SCSIZE i = 0; i < nR; i++ )
+ {
+ for ( SCSIZE j = 0; j < nC; j++ )
+ {
+ if ( static_cast< int >( pTypeMat->GetDouble( j, i ) ) < 1 ||
+ static_cast< int >( pTypeMat->GetDouble( j, i ) ) > 9 )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+ }
+ }
+
+ ScMatrixRef pMatX = GetMatrix();
+ ScMatrixRef pMatY = GetMatrix();
+ if ( !pMatX || !pMatY )
+ {
+ PushIllegalParameter();
+ return;
+ }
+ SCSIZE nCX, nCY;
+ SCSIZE nRX, nRY;
+ pMatX->GetDimensions( nCX, nRX );
+ pMatY->GetDimensions( nCY, nRY );
+ if ( nRX != nRY || nCX != nCY ||
+ !pMatX->IsNumeric() || !pMatY->IsNumeric() )
+ {
+ PushIllegalArgument();
+ return;
+ }
+
+ ScMatrixRef pTMat;
+ if ( eETSType != etsStatAdd && eETSType != etsStatMult && eETSType != etsSeason )
+ {
+ pTMat = GetMatrix();
+ if ( !pTMat )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ }
+
+ ScETSForecastCalculation aETSCalc( pMatX->GetElementCount(), pFormatter );
+ if ( !aETSCalc.PreprocessDataRange( pMatX, pMatY, nSmplInPrd, bDataCompletion,
+ nAggregation,
+ ( eETSType != etsStatAdd && eETSType != etsStatMult ? pTMat : nullptr ),
+ eETSType ) )
+ {
+ PushError( aETSCalc.GetError() );
+ return;
+ }
+
+ switch ( eETSType )
+ {
+ case etsAdd :
+ case etsMult :
+ {
+ SCSIZE nC, nR;
+ pTMat->GetDimensions( nC, nR );
+ ScMatrixRef pFcMat = GetNewMat( nC, nR, /*bEmpty*/true );
+ aETSCalc.GetForecastRange( pTMat, pFcMat );
+ if (aETSCalc.GetError() != FormulaError::NONE)
+ PushError( aETSCalc.GetError()); // explicitly push error, PushMatrix() does not
+ else
+ PushMatrix( pFcMat );
+ }
+ break;
+ case etsPIAdd :
+ case etsPIMult :
+ {
+ SCSIZE nC, nR;
+ pTMat->GetDimensions( nC, nR );
+ ScMatrixRef pPIMat = GetNewMat( nC, nR, /*bEmpty*/true );
+ if ( nSmplInPrd == 0 )
+ {
+ aETSCalc.GetEDSPredictionIntervals( pTMat, pPIMat, fPILevel );
+ }
+ else
+ {
+ aETSCalc.GetETSPredictionIntervals( pTMat, pPIMat, fPILevel );
+ }
+ if (aETSCalc.GetError() != FormulaError::NONE)
+ PushError( aETSCalc.GetError()); // explicitly push error, PushMatrix() does not
+ else
+ PushMatrix( pPIMat );
+ }
+ break;
+ case etsStatAdd :
+ case etsStatMult :
+ {
+ SCSIZE nC, nR;
+ pTypeMat->GetDimensions( nC, nR );
+ ScMatrixRef pStatMat = GetNewMat( nC, nR, /*bEmpty*/true );
+ aETSCalc.GetStatisticValue( pTypeMat, pStatMat );
+ if (aETSCalc.GetError() != FormulaError::NONE)
+ PushError( aETSCalc.GetError()); // explicitly push error, PushMatrix() does not
+ else
+ PushMatrix( pStatMat );
+ }
+ break;
+ case etsSeason :
+ {
+ double rVal;
+ aETSCalc.GetSamplesInPeriod( rVal );
+ SetError( aETSCalc.GetError() );
+ PushDouble( rVal );
+ }
+ break;
+ }
+}
+
+void ScInterpreter::ScConcat_MS()
+{
+ OUStringBuffer aResBuf;
+ short nParamCount = GetByte();
+
+ //reverse order of parameter stack to simplify concatenation:
+ ReverseStack( nParamCount );
+
+ size_t nRefInList = 0;
+ while ( nParamCount-- > 0 && nGlobalError == FormulaError::NONE )
+ {
+ switch ( GetStackType() )
+ {
+ case svString:
+ case svDouble:
+ {
+ OUString aStr = GetString().getString();
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append(aStr);
+ }
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+ ScRefCellValue aCell( mrDoc, aAdr );
+ if (!aCell.hasEmptyValue())
+ {
+ svl::SharedString aSS;
+ GetCellString( aSS, aCell);
+ const OUString& rStr = aSS.getString();
+ if (CheckStringResultLen(aResBuf, rStr.getLength()))
+ aResBuf.append( rStr);
+ }
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+ // we need to read row for row, so we can't use ScCellIter
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( nTab1 != nTab2 )
+ {
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ PutInOrder( nRow1, nRow2 );
+ PutInOrder( nCol1, nCol2 );
+ ScAddress aAdr;
+ aAdr.SetTab( nTab1 );
+ for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
+ {
+ for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
+ {
+ aAdr.SetRow( nRow );
+ aAdr.SetCol( nCol );
+ ScRefCellValue aCell( mrDoc, aAdr );
+ if (!aCell.hasEmptyValue() )
+ {
+ svl::SharedString aSS;
+ GetCellString( aSS, aCell);
+ const OUString& rStr = aSS.getString();
+ if (CheckStringResultLen(aResBuf, rStr.getLength()))
+ aResBuf.append( rStr);
+ }
+ }
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ SetError(FormulaError::IllegalArgument);
+ else
+ {
+ for (SCSIZE k = 0; k < nR; ++k)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ if ( pMat->IsStringOrEmpty( j, k ) )
+ {
+ OUString aStr = pMat->GetString( j, k ).getString();
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append(aStr);
+ }
+ else
+ {
+ if ( pMat->IsValue( j, k ) )
+ {
+ OUString aStr = pMat->GetString( *pFormatter, j, k ).getString();
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append(aStr);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+ }
+ PushString( aResBuf.makeStringAndClear() );
+}
+
+void ScInterpreter::ScTextJoin_MS()
+{
+ short nParamCount = GetByte();
+
+ if ( !MustHaveParamCountMin( nParamCount, 3 ) )
+ return;
+
+ //reverse order of parameter stack to simplify processing
+ ReverseStack( nParamCount );
+
+ // get aDelimiters and bSkipEmpty
+ std::vector< OUString > aDelimiters;
+ size_t nRefInList = 0;
+ switch ( GetStackType() )
+ {
+ case svString:
+ case svDouble:
+ aDelimiters.push_back( GetString().getString() );
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+ ScRefCellValue aCell( mrDoc, aAdr );
+ if (aCell.hasEmptyValue())
+ aDelimiters.emplace_back("");
+ else
+ {
+ svl::SharedString aSS;
+ GetCellString( aSS, aCell);
+ aDelimiters.push_back( aSS.getString());
+ }
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+ // we need to read row for row, so we can't use ScCellIterator
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( nTab1 != nTab2 )
+ {
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ PutInOrder( nRow1, nRow2 );
+ PutInOrder( nCol1, nCol2 );
+ ScAddress aAdr;
+ aAdr.SetTab( nTab1 );
+ for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
+ {
+ for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
+ {
+ aAdr.SetRow( nRow );
+ aAdr.SetCol( nCol );
+ ScRefCellValue aCell( mrDoc, aAdr );
+ if (aCell.hasEmptyValue())
+ aDelimiters.emplace_back("");
+ else
+ {
+ svl::SharedString aSS;
+ GetCellString( aSS, aCell);
+ aDelimiters.push_back( aSS.getString());
+ }
+ }
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ SetError(FormulaError::IllegalArgument);
+ else
+ {
+ for (SCSIZE k = 0; k < nR; ++k)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ if (pMat->IsEmpty( j, k ))
+ aDelimiters.emplace_back("");
+ else if (pMat->IsStringOrEmpty( j, k ))
+ aDelimiters.push_back( pMat->GetString( j, k ).getString() );
+ else if (pMat->IsValue( j, k ))
+ aDelimiters.push_back( pMat->GetString( *pFormatter, j, k ).getString() );
+ else
+ {
+ assert(!"should this really happen?");
+ aDelimiters.emplace_back("");
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+ if ( aDelimiters.empty() )
+ {
+ PushIllegalArgument();
+ return;
+ }
+ SCSIZE nSize = aDelimiters.size();
+ bool bSkipEmpty = static_cast< bool >( GetDouble() );
+ nParamCount -= 2;
+
+ OUStringBuffer aResBuf;
+ bool bFirst = true;
+ SCSIZE nIdx = 0;
+ nRefInList = 0;
+ // get the strings to be joined
+ while ( nParamCount-- > 0 && nGlobalError == FormulaError::NONE )
+ {
+ switch ( GetStackType() )
+ {
+ case svString:
+ case svDouble:
+ {
+ OUString aStr = GetString().getString();
+ if ( !aStr.isEmpty() || !bSkipEmpty )
+ {
+ if ( !bFirst )
+ {
+ aResBuf.append( aDelimiters[ nIdx ] );
+ if ( nSize > 1 )
+ {
+ if ( ++nIdx >= nSize )
+ nIdx = 0;
+ }
+ }
+ else
+ bFirst = false;
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append( aStr );
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ ScAddress aAdr;
+ PopSingleRef( aAdr );
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+ ScRefCellValue aCell( mrDoc, aAdr );
+ OUString aStr;
+ if (!aCell.hasEmptyValue())
+ {
+ svl::SharedString aSS;
+ GetCellString( aSS, aCell);
+ aStr = aSS.getString();
+ }
+ if ( !aStr.isEmpty() || !bSkipEmpty )
+ {
+ if ( !bFirst )
+ {
+ aResBuf.append( aDelimiters[ nIdx ] );
+ if ( nSize > 1 )
+ {
+ if ( ++nIdx >= nSize )
+ nIdx = 0;
+ }
+ }
+ else
+ bFirst = false;
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append( aStr );
+ }
+ }
+ break;
+ case svDoubleRef :
+ case svRefList :
+ {
+ ScRange aRange;
+ PopDoubleRef( aRange, nParamCount, nRefInList);
+ if ( nGlobalError != FormulaError::NONE )
+ break;
+ // we need to read row for row, so we can't use ScCellIterator
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( nTab1 != nTab2 )
+ {
+ SetError( FormulaError::IllegalParameter);
+ break;
+ }
+ PutInOrder( nRow1, nRow2 );
+ PutInOrder( nCol1, nCol2 );
+ ScAddress aAdr;
+ aAdr.SetTab( nTab1 );
+ OUString aStr;
+ for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
+ {
+ for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
+ {
+ aAdr.SetRow( nRow );
+ aAdr.SetCol( nCol );
+ ScRefCellValue aCell( mrDoc, aAdr );
+ if (aCell.hasEmptyValue())
+ aStr.clear();
+ else
+ {
+ svl::SharedString aSS;
+ GetCellString( aSS, aCell);
+ aStr = aSS.getString();
+ }
+ if ( !aStr.isEmpty() || !bSkipEmpty )
+ {
+ if ( !bFirst )
+ {
+ aResBuf.append( aDelimiters[ nIdx ] );
+ if ( nSize > 1 )
+ {
+ if ( ++nIdx >= nSize )
+ nIdx = 0;
+ }
+ }
+ else
+ bFirst = false;
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append( aStr );
+ }
+ }
+ }
+ }
+ break;
+ case svMatrix :
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScMatrixRef pMat = GetMatrix();
+ if (pMat)
+ {
+ SCSIZE nC, nR;
+ pMat->GetDimensions(nC, nR);
+ if (nC == 0 || nR == 0)
+ SetError(FormulaError::IllegalArgument);
+ else
+ {
+ OUString aStr;
+ for (SCSIZE k = 0; k < nR; ++k)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ if (pMat->IsEmpty( j, k ) )
+ aStr.clear();
+ else if (pMat->IsStringOrEmpty( j, k ))
+ aStr = pMat->GetString( j, k ).getString();
+ else if (pMat->IsValue( j, k ))
+ aStr = pMat->GetString( *pFormatter, j, k ).getString();
+ else
+ {
+ assert(!"should this really happen?");
+ aStr.clear();
+ }
+ if ( !aStr.isEmpty() || !bSkipEmpty )
+ {
+ if ( !bFirst )
+ {
+ aResBuf.append( aDelimiters[ nIdx ] );
+ if ( nSize > 1 )
+ {
+ if ( ++nIdx >= nSize )
+ nIdx = 0;
+ }
+ }
+ else
+ bFirst = false;
+ if (CheckStringResultLen(aResBuf, aStr.getLength()))
+ aResBuf.append( aStr );
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case svMissing :
+ {
+ if ( !bSkipEmpty )
+ {
+ if ( !bFirst )
+ {
+ aResBuf.append( aDelimiters[ nIdx ] );
+ if ( nSize > 1 )
+ {
+ if ( ++nIdx >= nSize )
+ nIdx = 0;
+ }
+ }
+ else
+ bFirst = false;
+ }
+ }
+ break;
+ default:
+ PopError();
+ SetError( FormulaError::IllegalArgument);
+ break;
+ }
+ }
+ PushString( aResBuf.makeStringAndClear() );
+}
+
+
+void ScInterpreter::ScIfs_MS()
+{
+ short nParamCount = GetByte();
+
+ ReverseStack( nParamCount );
+
+ nGlobalError = FormulaError::NONE; // propagate only for condition or active result path
+ bool bFinished = false;
+ while ( nParamCount > 0 && !bFinished && nGlobalError == FormulaError::NONE )
+ {
+ bool bVal = GetBool();
+ nParamCount--;
+ if ( bVal )
+ {
+ // TRUE
+ if ( nParamCount < 1 )
+ {
+ // no parameter given for THEN
+ PushParameterExpected();
+ return;
+ }
+ bFinished = true;
+ }
+ else
+ {
+ // FALSE
+ if ( nParamCount >= 3 )
+ {
+ // ELSEIF path
+ Pop();
+ nParamCount--;
+ }
+ else
+ {
+ // no parameter given for ELSE
+ PushNA();
+ return;
+ }
+ }
+ }
+
+ if ( nGlobalError != FormulaError::NONE || !bFinished )
+ {
+ if ( !bFinished )
+ PushNA(); // no true expression found
+ if ( nGlobalError != FormulaError::NONE )
+ PushNoValue(); // expression returned something other than true or false
+ return;
+ }
+
+ //push result :
+ FormulaConstTokenRef xToken( PopToken() );
+ if ( xToken )
+ {
+ // Remove unused arguments of IFS from the stack before pushing the result.
+ while ( nParamCount > 1 )
+ {
+ Pop();
+ nParamCount--;
+ }
+ PushTokenRef( xToken );
+ }
+ else
+ PushError( FormulaError::UnknownStackVariable );
+}
+
+
+void ScInterpreter::ScSwitch_MS()
+{
+ short nParamCount = GetByte();
+
+ if (!MustHaveParamCountMin( nParamCount, 3))
+ return;
+
+ ReverseStack( nParamCount );
+
+ nGlobalError = FormulaError::NONE; // propagate only for match or active result path
+ bool isValue = false;
+ double fRefVal = 0;
+ svl::SharedString aRefStr;
+ switch ( GetStackType() )
+ {
+ case svDouble:
+ isValue = true;
+ fRefVal = GetDouble();
+ break;
+ case svString:
+ isValue = false;
+ aRefStr = GetString();
+ break;
+ case svSingleRef :
+ case svDoubleRef :
+ {
+ ScAddress aAdr;
+ if (!PopDoubleRefOrSingleRef( aAdr ))
+ break;
+ ScRefCellValue aCell( mrDoc, aAdr );
+ isValue = !( aCell.hasString() || aCell.hasEmptyValue() || aCell.isEmpty() );
+ if ( isValue )
+ fRefVal = GetCellValue( aAdr, aCell);
+ else
+ GetCellString( aRefStr, aCell);
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svMatrix:
+ isValue = ScMatrix::IsValueType( GetDoubleOrStringFromMatrix( fRefVal, aRefStr ) );
+ break;
+ default :
+ PopError();
+ PushIllegalArgument();
+ return;
+ }
+ nParamCount--;
+ bool bFinished = false;
+ while ( nParamCount > 1 && !bFinished && nGlobalError == FormulaError::NONE )
+ {
+ double fVal = 0;
+ svl::SharedString aStr;
+ if ( isValue )
+ fVal = GetDouble();
+ else
+ aStr = GetString();
+ nParamCount--;
+ if ((nGlobalError != FormulaError::NONE && nParamCount < 2)
+ || (isValue && rtl::math::approxEqual( fRefVal, fVal))
+ || (!isValue && aRefStr.getDataIgnoreCase() == aStr.getDataIgnoreCase()))
+ {
+ // TRUE
+ bFinished = true;
+ }
+ else
+ {
+ // FALSE
+ if ( nParamCount >= 2 )
+ {
+ // ELSEIF path
+ Pop();
+ nParamCount--;
+ // if nParamCount equals 1: default value to be returned
+ bFinished = ( nParamCount == 1 );
+ }
+ else
+ {
+ // no parameter given for ELSE
+ PushNA();
+ return;
+ }
+ nGlobalError = FormulaError::NONE;
+ }
+ }
+
+ if ( nGlobalError != FormulaError::NONE || !bFinished )
+ {
+ if ( !bFinished )
+ PushNA(); // no true expression found
+ else
+ PushError( nGlobalError );
+ return;
+ }
+
+ // push result
+ FormulaConstTokenRef xToken( PopToken() );
+ if ( xToken )
+ {
+ // Remove unused arguments of SWITCH from the stack before pushing the result.
+ while ( nParamCount > 1 )
+ {
+ Pop();
+ nParamCount--;
+ }
+ PushTokenRef( xToken );
+ }
+ else
+ PushError( FormulaError::UnknownStackVariable );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpretercontext.cxx b/sc/source/core/tool/interpretercontext.cxx
new file mode 100644
index 0000000000..deb6f6d0ed
--- /dev/null
+++ b/sc/source/core/tool/interpretercontext.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 <interpretercontext.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+
+#include <document.hxx>
+#include <formula/token.hxx>
+#include <lookupcache.hxx>
+#include <rangecache.hxx>
+#include <algorithm>
+
+ScInterpreterContextPool ScInterpreterContextPool::aThreadedInterpreterPool(true);
+ScInterpreterContextPool ScInterpreterContextPool::aNonThreadedInterpreterPool(false);
+
+ScInterpreterContext::ScInterpreterContext(const ScDocument& rDoc, SvNumberFormatter* pFormatter)
+ : mpDoc(&rDoc)
+ , mnTokenCachePos(0)
+ , maTokens(TOKEN_CACHE_SIZE, nullptr)
+ , pInterpreter(nullptr)
+ , mpFormatter(pFormatter)
+{
+}
+
+ScInterpreterContext::~ScInterpreterContext() { ResetTokens(); }
+
+void ScInterpreterContext::ResetTokens()
+{
+ for (auto p : maTokens)
+ if (p)
+ p->DecRef();
+
+ mnTokenCachePos = 0;
+ std::fill(maTokens.begin(), maTokens.end(), nullptr);
+}
+
+void ScInterpreterContext::SetDocAndFormatter(const ScDocument& rDoc, SvNumberFormatter* pFormatter)
+{
+ if (mpDoc != &rDoc)
+ {
+ mxScLookupCache.reset();
+ mpDoc = &rDoc;
+ }
+ mpFormatter = pFormatter;
+}
+
+void ScInterpreterContext::initFormatTable()
+{
+ mpFormatter = mpDoc->GetFormatTable(); // will assert if not main thread
+}
+
+void ScInterpreterContext::Cleanup()
+{
+ // Do not disturb mxScLookupCache.
+ maConditions.clear();
+ maDelayedSetNumberFormat.clear();
+ ResetTokens();
+}
+
+void ScInterpreterContext::ClearLookupCache(const ScDocument* pDoc)
+{
+ if (pDoc == mpDoc)
+ mxScLookupCache.reset();
+}
+
+SvNumFormatType ScInterpreterContext::GetNumberFormatType(sal_uInt32 nFIndex) const
+{
+ if (!mpDoc->IsThreadedGroupCalcInProgress())
+ {
+ return mpFormatter->GetType(nFIndex);
+ }
+
+ if (maNFTypeCache.bIsValid && maNFTypeCache.nIndex == nFIndex)
+ {
+ return maNFTypeCache.eType;
+ }
+
+ maNFTypeCache.nIndex = nFIndex;
+ maNFTypeCache.eType = mpFormatter->GetType(nFIndex);
+ maNFTypeCache.bIsValid = true;
+ return maNFTypeCache.eType;
+}
+
+/* ScInterpreterContextPool */
+
+// Threaded version
+void ScInterpreterContextPool::Init(size_t nNumThreads, const ScDocument& rDoc,
+ SvNumberFormatter* pFormatter)
+{
+ assert(mbThreaded);
+ size_t nOldSize = maPool.size();
+ maPool.resize(nNumThreads);
+ for (size_t nIdx = 0; nIdx < nNumThreads; ++nIdx)
+ {
+ if (nIdx >= nOldSize)
+ maPool[nIdx].reset(new ScInterpreterContext(rDoc, pFormatter));
+ else
+ maPool[nIdx]->SetDocAndFormatter(rDoc, pFormatter);
+ }
+}
+
+ScInterpreterContext*
+ScInterpreterContextPool::GetInterpreterContextForThreadIdx(size_t nThreadIdx) const
+{
+ assert(mbThreaded);
+ assert(nThreadIdx < maPool.size());
+ return maPool[nThreadIdx].get();
+}
+
+// Non-Threaded version
+void ScInterpreterContextPool::Init(const ScDocument& rDoc, SvNumberFormatter* pFormatter)
+{
+ assert(!mbThreaded);
+ assert(mnNextFree <= maPool.size());
+ bool bCreateNew = (maPool.size() == mnNextFree);
+ size_t nCurrIdx = mnNextFree;
+ if (bCreateNew)
+ {
+ maPool.resize(maPool.size() + 1);
+ maPool[nCurrIdx].reset(new ScInterpreterContext(rDoc, pFormatter));
+ }
+ else
+ maPool[nCurrIdx]->SetDocAndFormatter(rDoc, pFormatter);
+
+ ++mnNextFree;
+}
+
+ScInterpreterContext* ScInterpreterContextPool::GetInterpreterContext() const
+{
+ assert(!mbThreaded);
+ assert(mnNextFree && (mnNextFree <= maPool.size()));
+ return maPool[mnNextFree - 1].get();
+}
+
+void ScInterpreterContextPool::ReturnToPool()
+{
+ if (mbThreaded)
+ {
+ for (size_t nIdx = 0; nIdx < maPool.size(); ++nIdx)
+ maPool[nIdx]->Cleanup();
+ }
+ else
+ {
+ assert(mnNextFree && (mnNextFree <= maPool.size()));
+ --mnNextFree;
+ maPool[mnNextFree]->Cleanup();
+ }
+}
+
+// static
+void ScInterpreterContextPool::ClearLookupCaches(const ScDocument* pDoc)
+{
+ for (auto& rPtr : aThreadedInterpreterPool.maPool)
+ rPtr->ClearLookupCache(pDoc);
+ for (auto& rPtr : aNonThreadedInterpreterPool.maPool)
+ rPtr->ClearLookupCache(pDoc);
+}
+
+/* ScThreadedInterpreterContextGetterGuard */
+
+ScThreadedInterpreterContextGetterGuard::ScThreadedInterpreterContextGetterGuard(
+ size_t nNumThreads, const ScDocument& rDoc, SvNumberFormatter* pFormatter)
+ : rPool(ScInterpreterContextPool::aThreadedInterpreterPool)
+{
+ rPool.Init(nNumThreads, rDoc, pFormatter);
+}
+
+ScThreadedInterpreterContextGetterGuard::~ScThreadedInterpreterContextGetterGuard()
+{
+ rPool.ReturnToPool();
+}
+
+ScInterpreterContext*
+ScThreadedInterpreterContextGetterGuard::GetInterpreterContextForThreadIdx(size_t nThreadIdx) const
+{
+ return rPool.GetInterpreterContextForThreadIdx(nThreadIdx);
+}
+
+/* ScInterpreterContextGetterGuard */
+
+ScInterpreterContextGetterGuard::ScInterpreterContextGetterGuard(const ScDocument& rDoc,
+ SvNumberFormatter* pFormatter)
+ : rPool(ScInterpreterContextPool::aNonThreadedInterpreterPool)
+#if !defined NDEBUG
+ , nContextIdx(rPool.mnNextFree)
+#endif
+{
+ rPool.Init(rDoc, pFormatter);
+}
+
+ScInterpreterContextGetterGuard::~ScInterpreterContextGetterGuard()
+{
+ assert(nContextIdx == (rPool.mnNextFree - 1));
+ rPool.ReturnToPool();
+}
+
+ScInterpreterContext* ScInterpreterContextGetterGuard::GetInterpreterContext() const
+{
+ return rPool.GetInterpreterContext();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/jumpmatrix.cxx b/sc/source/core/tool/jumpmatrix.cxx
new file mode 100644
index 0000000000..868d5da655
--- /dev/null
+++ b/sc/source/core/tool/jumpmatrix.cxx
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <jumpmatrix.hxx>
+#include <scmatrix.hxx>
+#include <osl/diagnose.h>
+
+namespace {
+// Don't bother with buffer overhead for less than y rows.
+const SCSIZE kBufferThreshold = 128;
+}
+
+ScJumpMatrix::ScJumpMatrix( OpCode eOp, SCSIZE nColsP, SCSIZE nRowsP )
+ : mvJump(nColsP * nRowsP)
+ // Initialize result matrix in case of
+ // a premature end of the interpreter
+ // due to errors.
+ , pMat(new ScMatrix(nColsP, nRowsP, CreateDoubleError(FormulaError::NotAvailable)))
+ , nCols(nColsP)
+ , nRows(nRowsP)
+ , nCurCol(0)
+ , nCurRow(0)
+ , nResMatCols(nColsP)
+ , nResMatRows(nRowsP)
+ , meOp(eOp)
+ , bStarted(false)
+ , mnBufferCol(0)
+ , mnBufferRowStart(0)
+ , mnBufferEmptyCount(0)
+ , mnBufferEmptyPathCount(0)
+{
+ /*! pJump not initialized */
+}
+
+ScJumpMatrix::~ScJumpMatrix()
+{
+ for (const auto & i : mvParams)
+ i->DecRef();
+}
+
+void ScJumpMatrix::GetDimensions(SCSIZE& rCols, SCSIZE& rRows) const
+{
+ rCols = nCols;
+ rRows = nRows;
+}
+
+void ScJumpMatrix::SetJump(SCSIZE nCol, SCSIZE nRow, double fBool,
+ short nStart, short nNext)
+{
+ mvJump[static_cast<sal_uInt64>(nCol) * nRows + nRow].SetJump(fBool, nStart, nNext, SHRT_MAX);
+}
+
+void ScJumpMatrix::GetJump(
+ SCSIZE nCol, SCSIZE nRow, double& rBool, short& rStart, short& rNext, short& rStop) const
+{
+ if (nCols == 1 && nRows == 1)
+ {
+ nCol = 0;
+ nRow = 0;
+ }
+ else if (nCols == 1 && nRow < nRows) nCol = 0;
+ else if (nRows == 1 && nCol < nCols) nRow = 0;
+ else if (nCols <= nCol || nRows <= nRow)
+ {
+ OSL_FAIL("ScJumpMatrix::GetJump: dimension error");
+ nCol = 0;
+ nRow = 0;
+ }
+ mvJump[static_cast<sal_uInt64>(nCol) * nRows + nRow].
+ GetJump(rBool, rStart, rNext, rStop);
+}
+
+void ScJumpMatrix::SetAllJumps(double fBool, short nStart, short nNext, short nStop)
+{
+ sal_uInt64 n = static_cast<sal_uInt64>(nCols) * nRows;
+ for (sal_uInt64 j = 0; j < n; ++j)
+ {
+ mvJump[j].SetJump(fBool, nStart,
+ nNext, nStop);
+ }
+}
+
+void ScJumpMatrix::SetJumpParameters(ScTokenVec&& p)
+{
+ mvParams = std::move(p);
+}
+
+void ScJumpMatrix::GetPos(SCSIZE& rCol, SCSIZE& rRow) const
+{
+ rCol = nCurCol;
+ rRow = nCurRow;
+}
+
+bool ScJumpMatrix::Next(SCSIZE& rCol, SCSIZE& rRow)
+{
+ if (!bStarted)
+ {
+ bStarted = true;
+ nCurCol = nCurRow = 0;
+ }
+ else
+ {
+ if (++nCurRow >= nResMatRows)
+ {
+ nCurRow = 0;
+ ++nCurCol;
+ }
+ }
+ GetPos(rCol, rRow);
+ return nCurCol < nResMatCols;
+}
+
+void ScJumpMatrix::GetResMatDimensions(SCSIZE& rCols, SCSIZE& rRows)
+{
+ rCols = nResMatCols;
+ rRows = nResMatRows;
+}
+
+void ScJumpMatrix::SetNewResMat(SCSIZE nNewCols, SCSIZE nNewRows)
+{
+ if (nNewCols <= nResMatCols && nNewRows <= nResMatRows)
+ return;
+
+ FlushBufferOtherThan( BUFFER_NONE, 0, 0);
+ pMat = pMat->CloneAndExtend(nNewCols, nNewRows);
+ if (nResMatCols < nNewCols)
+ {
+ pMat->FillDouble(
+ CreateDoubleError(FormulaError::NotAvailable),
+ nResMatCols, 0, nNewCols - 1, nResMatRows - 1);
+ }
+ if (nResMatRows < nNewRows)
+ {
+ pMat->FillDouble(
+ CreateDoubleError(FormulaError::NotAvailable),
+ 0, nResMatRows, nNewCols - 1, nNewRows - 1);
+ }
+ if (nRows == 1 && nCurCol != 0)
+ {
+ nCurCol = 0;
+ nCurRow = nResMatRows - 1;
+ }
+ nResMatCols = nNewCols;
+ nResMatRows = nNewRows;
+}
+
+bool ScJumpMatrix::HasResultMatrix() const
+{
+ // We now always have a matrix but caller logic may still want to check it.
+ return bool(pMat);
+}
+
+ScRefList& ScJumpMatrix::GetRefList()
+{
+ return mvRefList;
+}
+
+void ScJumpMatrix::FlushBufferOtherThan( ScJumpMatrix::BufferType eType, SCSIZE nC, SCSIZE nR )
+{
+ if (!mvBufferDoubles.empty() &&
+ (eType != BUFFER_DOUBLE || nC != mnBufferCol || nR != mnBufferRowStart + mvBufferDoubles.size()))
+ {
+ pMat->PutDoubleVector( mvBufferDoubles, mnBufferCol, mnBufferRowStart);
+ mvBufferDoubles.clear();
+ }
+ if (!mvBufferStrings.empty() &&
+ (eType != BUFFER_STRING || nC != mnBufferCol || nR != mnBufferRowStart + mvBufferStrings.size()))
+ {
+ pMat->PutStringVector( mvBufferStrings, mnBufferCol, mnBufferRowStart);
+ mvBufferStrings.clear();
+ }
+ if (mnBufferEmptyCount &&
+ (eType != BUFFER_EMPTY || nC != mnBufferCol || nR != mnBufferRowStart + mnBufferEmptyCount))
+ {
+ pMat->PutEmptyVector( mnBufferEmptyCount, mnBufferCol, mnBufferRowStart);
+ mnBufferEmptyCount = 0;
+ }
+ if (mnBufferEmptyPathCount &&
+ (eType != BUFFER_EMPTYPATH || nC != mnBufferCol || nR != mnBufferRowStart + mnBufferEmptyPathCount))
+ {
+ pMat->PutEmptyPathVector( mnBufferEmptyPathCount, mnBufferCol, mnBufferRowStart);
+ mnBufferEmptyPathCount = 0;
+ }
+}
+
+ScMatrix* ScJumpMatrix::GetResultMatrix()
+{
+ if (nResMatRows >= kBufferThreshold)
+ FlushBufferOtherThan( BUFFER_NONE, 0, 0);
+ return pMat.get();
+}
+
+void ScJumpMatrix::PutResultDouble( double fVal, SCSIZE nC, SCSIZE nR )
+{
+ if (nResMatRows < kBufferThreshold)
+ pMat->PutDouble( fVal, nC, nR);
+ else
+ {
+ FlushBufferOtherThan( BUFFER_DOUBLE, nC, nR);
+ if (mvBufferDoubles.empty())
+ {
+ mnBufferCol = nC;
+ mnBufferRowStart = nR;
+ }
+ mvBufferDoubles.push_back( fVal);
+ }
+}
+
+void ScJumpMatrix::PutResultString( const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR )
+{
+ if (nResMatRows < kBufferThreshold)
+ pMat->PutString( rStr, nC, nR);
+ else
+ {
+ FlushBufferOtherThan( BUFFER_STRING, nC, nR);
+ if (mvBufferStrings.empty())
+ {
+ mnBufferCol = nC;
+ mnBufferRowStart = nR;
+ }
+ mvBufferStrings.push_back( rStr);
+ }
+}
+
+void ScJumpMatrix::PutResultEmpty( SCSIZE nC, SCSIZE nR )
+{
+ if (nResMatRows < kBufferThreshold)
+ pMat->PutEmpty( nC, nR);
+ else
+ {
+ FlushBufferOtherThan( BUFFER_EMPTY, nC, nR);
+ if (!mnBufferEmptyCount)
+ {
+ mnBufferCol = nC;
+ mnBufferRowStart = nR;
+ }
+ ++mnBufferEmptyCount;
+ }
+}
+
+void ScJumpMatrix::PutResultEmptyPath( SCSIZE nC, SCSIZE nR )
+{
+ if (nResMatRows < kBufferThreshold)
+ pMat->PutEmptyPath( nC, nR);
+ else
+ {
+ FlushBufferOtherThan( BUFFER_EMPTYPATH, nC, nR);
+ if (!mnBufferEmptyPathCount)
+ {
+ mnBufferCol = nC;
+ mnBufferRowStart = nR;
+ }
+ ++mnBufferEmptyPathCount;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/listenerquery.cxx b/sc/source/core/tool/listenerquery.cxx
new file mode 100644
index 0000000000..10cebdd421
--- /dev/null
+++ b/sc/source/core/tool/listenerquery.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <listenerquery.hxx>
+#include <listenerqueryids.hxx>
+#include <address.hxx>
+
+namespace sc {
+
+RefQueryFormulaGroup::RefQueryFormulaGroup() :
+ SvtListener::QueryBase(SC_LISTENER_QUERY_FORMULA_GROUP_POS),
+ maSkipRange(ScAddress::INITIALIZE_INVALID) {}
+
+RefQueryFormulaGroup::~RefQueryFormulaGroup() {}
+
+void RefQueryFormulaGroup::setSkipRange( const ScRange& rRange )
+{
+ maSkipRange = rRange;
+}
+
+void RefQueryFormulaGroup::add( const ScAddress& rPos )
+{
+ if (!rPos.IsValid())
+ return;
+
+ if (maSkipRange.IsValid() && maSkipRange.Contains(rPos))
+ // This is within the skip range. Skip it.
+ return;
+
+ TabsType::iterator itTab = maTabs.find(rPos.Tab());
+ if (itTab == maTabs.end())
+ {
+ std::pair<TabsType::iterator,bool> r =
+ maTabs.emplace(rPos.Tab(), ColsType());
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itTab = r.first;
+ }
+
+ ColsType& rCols = itTab->second;
+ ColsType::iterator itCol = rCols.find(rPos.Col());
+ if (itCol == rCols.end())
+ {
+ std::pair<ColsType::iterator,bool> r =
+ rCols.emplace(rPos.Col(), ColType());
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itCol = r.first;
+ }
+
+ ColType& rCol = itCol->second;
+ rCol.push_back(rPos.Row());
+}
+
+const RefQueryFormulaGroup::TabsType& RefQueryFormulaGroup::getAllPositions() const
+{
+ return maTabs;
+}
+
+QueryRange::QueryRange() :
+ SvtListener::QueryBase(SC_LISTENER_QUERY_FORMULA_GROUP_RANGE)
+{}
+
+QueryRange::~QueryRange()
+{
+}
+
+void QueryRange::add( const ScRange& rRange )
+{
+ maRanges.Join(rRange);
+}
+
+void QueryRange::swapRanges( ScRangeList& rRanges )
+{
+ maRanges.swap(rRanges);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/lookupcache.cxx b/sc/source/core/tool/lookupcache.cxx
new file mode 100644
index 0000000000..70e19ec20d
--- /dev/null
+++ b/sc/source/core/tool/lookupcache.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 <lookupcache.hxx>
+#include <document.hxx>
+#include <queryentry.hxx>
+#include <brdcst.hxx>
+
+#include <sal/log.hxx>
+
+ScLookupCache::QueryCriteria::QueryCriteria( const ScQueryEntry& rEntry ) :
+ mfVal(0.0), mbAlloc(false), mbString(false)
+{
+ switch (rEntry.eOp)
+ {
+ case SC_EQUAL :
+ meOp = EQUAL;
+ break;
+ case SC_LESS_EQUAL :
+ meOp = LESS_EQUAL;
+ break;
+ case SC_GREATER_EQUAL :
+ meOp = GREATER_EQUAL;
+ break;
+ default:
+ meOp = UNKNOWN;
+ SAL_WARN( "sc.core", "ScLookupCache::QueryCriteria not prepared for this ScQueryOp");
+ }
+
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (rItem.meType == ScQueryEntry::ByString)
+ setString(rItem.maString.getString());
+ else
+ setDouble(rItem.mfVal);
+}
+
+ScLookupCache::QueryCriteria::QueryCriteria( const ScLookupCache::QueryCriteria & r ) :
+ mfVal( r.mfVal),
+ mbAlloc( false),
+ mbString( false),
+ meOp( r.meOp)
+{
+ if (r.mbString && r.mpStr)
+ {
+ mpStr = new OUString( *r.mpStr);
+ mbAlloc = mbString = true;
+ }
+}
+
+ScLookupCache::QueryCriteria::~QueryCriteria()
+{
+ deleteString();
+}
+
+ScLookupCache::Result ScLookupCache::lookup( ScAddress & o_rResultAddress,
+ const QueryCriteria & rCriteria, const ScAddress & rQueryAddress ) const
+{
+ auto it( maQueryMap.find( QueryKey( rQueryAddress,
+ rCriteria.getQueryOp())));
+ if (it == maQueryMap.end())
+ return NOT_CACHED;
+ const QueryCriteriaAndResult& rResult = (*it).second;
+ if (!(rResult.maCriteria == rCriteria))
+ return CRITERIA_DIFFERENT;
+ if (rResult.maAddress.Row() < 0 )
+ return NOT_AVAILABLE;
+ o_rResultAddress = rResult.maAddress;
+ return FOUND;
+}
+
+SCROW ScLookupCache::lookup( const QueryCriteria & rCriteria ) const
+{
+ // try to find the row index for which we have already performed lookup
+ auto it = std::find_if(maQueryMap.begin(), maQueryMap.end(),
+ [&rCriteria](const std::pair<QueryKey, QueryCriteriaAndResult>& rEntry) {
+ return rEntry.second.maCriteria == rCriteria;
+ });
+ if (it != maQueryMap.end())
+ return it->first.mnRow;
+
+ // not found
+ return -1;
+}
+
+bool ScLookupCache::insert( const ScAddress & rResultAddress,
+ const QueryCriteria & rCriteria, const ScAddress & rQueryAddress,
+ const bool bAvailable )
+{
+ QueryKey aKey( rQueryAddress, rCriteria.getQueryOp());
+ QueryCriteriaAndResult aResult( rCriteria, rResultAddress);
+ if (!bAvailable)
+ aResult.maAddress.SetRow(-1);
+ bool bInserted = maQueryMap.insert( ::std::pair< const QueryKey,
+ QueryCriteriaAndResult>( aKey, aResult)).second;
+
+ return bInserted;
+}
+
+void ScLookupCache::Notify( const SfxHint& rHint )
+{
+ if (!mpDoc->IsInDtorClear())
+ {
+ if (rHint.GetId() == SfxHintId::ScDataChanged || rHint.GetId() == SfxHintId::ScAreaChanged)
+ {
+ mpDoc->RemoveLookupCache( *this);
+ // this ScLookupCache is deleted by RemoveLookupCache
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/math.cxx b/sc/source/core/tool/math.cxx
new file mode 100644
index 0000000000..e61d39386e
--- /dev/null
+++ b/sc/source/core/tool/math.cxx
@@ -0,0 +1,72 @@
+/* -*- 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 <math.hxx>
+#include <cmath>
+#include <cerrno>
+#include <cfenv>
+
+#include <o3tl/float_int_conversion.hxx>
+#include <rtl/math.hxx>
+
+namespace sc
+{
+static double err_pow(const double& fVal1, const double& fVal2)
+{
+ // pow() is expected to set domain error or pole error or range error (or
+ // flag them via exceptions) or return NaN or Inf.
+ assert((math_errhandling & (MATH_ERRNO | MATH_ERREXCEPT)) != 0);
+ std::feclearexcept(FE_ALL_EXCEPT);
+ errno = 0;
+ return pow(fVal1, fVal2);
+}
+
+double power(const double& fVal1, const double& fVal2)
+{
+ double fPow;
+ if (fVal1 < 0 && fVal2 != 0.0)
+ {
+ const double f = 1.0 / fVal2 + ((fVal2 < 0.0) ? -0.5 : 0.5);
+ if (!(o3tl::convertsToAtLeast(f, SAL_MIN_INT64)
+ && o3tl::convertsToAtMost(f, SAL_MAX_INT64)))
+ {
+ // Casting to int would be undefined behaviour.
+ fPow = err_pow(fVal1, fVal2);
+ }
+ else
+ {
+ const sal_Int64 i = static_cast<sal_Int64>(f);
+ if (i % 2 != 0 && rtl::math::approxEqual(1 / static_cast<double>(i), fVal2))
+ fPow = -err_pow(-fVal1, fVal2);
+ else
+ fPow = err_pow(fVal1, fVal2);
+ }
+ }
+ else
+ {
+ fPow = err_pow(fVal1, fVal2);
+ }
+ // The pow() call must had been the most recent call to check errno or exception.
+ if ((((math_errhandling & MATH_ERRNO) != 0) && (errno == EDOM || errno == ERANGE))
+// emscripten is currently broken by https://github.com/emscripten-core/emscripten/pull/11087
+// While the removal is correct for C99, it's not for C++11 (see http://www.cplusplus.com/reference/cfenv/FE_INEXACT/)
+// But since emscripten currently doesn't support any math exceptions, we simply ignore them
+#ifndef __EMSCRIPTEN__
+ || (((math_errhandling & MATH_ERREXCEPT) != 0)
+ && std::fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW))
+#endif
+ || !std::isfinite(fPow))
+ {
+ fPow = CreateDoubleError(FormulaError::IllegalFPOperation);
+ }
+ return fPow;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/source/core/tool/matrixoperators.cxx b/sc/source/core/tool/matrixoperators.cxx
new file mode 100644
index 0000000000..9406d09255
--- /dev/null
+++ b/sc/source/core/tool/matrixoperators.cxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <matrixoperators.hxx>
+
+namespace sc::op
+{
+/* Simple operators */
+
+void Sum::operator()(KahanSum& rAccum, double fVal) const { rAccum += fVal; }
+
+const double Sum::InitVal = 0.0;
+
+void SumSquare::operator()(KahanSum& rAccum, double fVal) const { rAccum += fVal * fVal; }
+
+const double SumSquare::InitVal = 0.0;
+
+void Product::operator()(double& rAccum, double fVal) const { rAccum *= fVal; }
+
+const double Product::InitVal = 1.0;
+
+/* Op operators */
+
+void fkOpSum(KahanSum& rAccum, double fVal) { rAccum += fVal; }
+
+kOp kOpSum(0.0, fkOpSum);
+
+void fkOpSumSquare(KahanSum& rAccum, double fVal) { rAccum += fVal * fVal; }
+
+kOp kOpSumSquare(0.0, fkOpSumSquare);
+
+std::vector<kOp> kOpSumAndSumSquare = { kOpSum, kOpSumSquare };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/navicfg.cxx b/sc/source/core/tool/navicfg.cxx
new file mode 100644
index 0000000000..ad8e295800
--- /dev/null
+++ b/sc/source/core/tool/navicfg.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 <navicfg.hxx>
+#include <content.hxx>
+
+//TODO: #define CFGPATH_NAVIPI "Office.Calc/Navigator"
+
+ScNavipiCfg::ScNavipiCfg() :
+//TODO: ConfigItem( OUString( CFGPATH_NAVIPI ) ),
+ nListMode(0),
+ nDragMode(0),
+ nRootType(ScContentId::ROOT)
+{
+}
+
+void ScNavipiCfg::SetListMode(sal_uInt16 nNew)
+{
+ if ( nListMode != nNew )
+ {
+ nListMode = nNew;
+//TODO: SetModified();
+ }
+}
+
+void ScNavipiCfg::SetDragMode(sal_uInt16 nNew)
+{
+ if ( nDragMode != nNew )
+ {
+ nDragMode = nNew;
+//TODO: SetModified();
+ }
+}
+
+void ScNavipiCfg::SetRootType(ScContentId nNew)
+{
+ if ( nRootType != nNew )
+ {
+ nRootType = nNew;
+//TODO: SetModified();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/numformat.cxx b/sc/source/core/tool/numformat.cxx
new file mode 100644
index 0000000000..c69da6de4e
--- /dev/null
+++ b/sc/source/core/tool/numformat.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 <numformat.hxx>
+#include <patattr.hxx>
+#include <document.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/languageoptions.hxx>
+#include <optional>
+
+namespace
+{
+ const OUString& getNumDecimalSep(const SvNumberformat& rFormat)
+ {
+ LanguageType nFormatLang = rFormat.GetLanguage();
+ if (nFormatLang == LANGUAGE_SYSTEM)
+ return ScGlobal::getLocaleData().getNumDecimalSep();
+ // LocaleDataWrapper can be expensive to construct, so cache the result for
+ // repeated calls
+ static std::optional<LocaleDataWrapper> localeCache;
+ if (!localeCache || localeCache->getLanguageTag().getLanguageType() != nFormatLang)
+ localeCache.emplace(
+ comphelper::getProcessComponentContext(), LanguageTag(nFormatLang));
+ return localeCache->getNumDecimalSep();
+ }
+}
+
+namespace sc {
+
+bool NumFmtUtil::isLatinScript( const ScPatternAttr& rPat, ScDocument& rDoc )
+{
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nKey = rPat.GetNumberFormat(pFormatter);
+ return isLatinScript(nKey, rDoc);
+}
+
+bool NumFmtUtil::isLatinScript( sal_uLong nFormat, ScDocument& rDoc )
+{
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SvNumberformat* pFormat = pFormatter->GetEntry(nFormat);
+ if (!pFormat || !pFormat->IsStandard())
+ return false;
+
+ // The standard format is all-latin if the decimal separator doesn't
+ // have a different script type
+ SvtScriptType nScript = rDoc.GetStringScriptType(getNumDecimalSep(*pFormat));
+ return (nScript == SvtScriptType::NONE || nScript == SvtScriptType::LATIN);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/odffmap.cxx b/sc/source/core/tool/odffmap.cxx
new file mode 100644
index 0000000000..7fc4ac5141
--- /dev/null
+++ b/sc/source/core/tool/odffmap.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <compiler.hxx>
+
+// ODFF, English, Programmatical, ODF_11
+// Functions duplicated to internal when writing ODFF are listed in static const XclFunctionInfo saFuncTable_4[]
+const ScCompiler::AddInMap ScCompiler::g_aAddInMap[] =
+{
+ { "ORG.OPENOFFICE.WEEKS", "WEEKS", "com.sun.star.sheet.addin.DateFunctions.getDiffWeeks", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFWEEKS" },
+ { "ORG.OPENOFFICE.MONTHS", "MONTHS", "com.sun.star.sheet.addin.DateFunctions.getDiffMonths", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFMONTHS" },
+ { "ORG.OPENOFFICE.YEARS", "YEARS", "com.sun.star.sheet.addin.DateFunctions.getDiffYears", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFYEARS" },
+ { "ORG.OPENOFFICE.ISLEAPYEAR", "ISLEAPYEAR", "com.sun.star.sheet.addin.DateFunctions.getIsLeapYear", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETISLEAPYEAR" },
+ { "ORG.OPENOFFICE.DAYSINMONTH", "DAYSINMONTH", "com.sun.star.sheet.addin.DateFunctions.getDaysInMonth", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINMONTH" },
+ { "ORG.OPENOFFICE.DAYSINYEAR", "DAYSINYEAR", "com.sun.star.sheet.addin.DateFunctions.getDaysInYear", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINYEAR" },
+ { "ORG.OPENOFFICE.WEEKSINYEAR", "WEEKSINYEAR", "com.sun.star.sheet.addin.DateFunctions.getWeeksInYear", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETWEEKSINYEAR" },
+ { "ORG.OPENOFFICE.ROT13", "ROT13", "com.sun.star.sheet.addin.DateFunctions.getRot13", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13" },
+ { "WORKDAY", "WORKDAY", "com.sun.star.sheet.addin.Analysis.getWorkday", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETWORKDAY" },
+ { "YEARFRAC", "YEARFRAC", "com.sun.star.sheet.addin.Analysis.getYearfrac", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYEARFRAC" },
+ { "EDATE", "EDATE", "com.sun.star.sheet.addin.Analysis.getEdate", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEDATE" },
+ { "WEEKNUM", "WEEKNUM_EXCEL2003", "com.sun.star.sheet.addin.Analysis.getWeeknum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETWEEKNUM" },
+ { "EOMONTH", "EOMONTH", "com.sun.star.sheet.addin.Analysis.getEomonth", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEOMONTH" },
+ { "NETWORKDAYS", "NETWORKDAYS_EXCEL2003", "com.sun.star.sheet.addin.Analysis.getNetworkdays", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETNETWORKDAYS" },
+ { "ISEVEN", "ISEVEN_ADD", "com.sun.star.sheet.addin.Analysis.getIseven", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETISEVEN" },
+ { "ISODD", "ISODD_ADD", "com.sun.star.sheet.addin.Analysis.getIsodd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETISODD" },
+ { "MULTINOMIAL", "MULTINOMIAL", "com.sun.star.sheet.addin.Analysis.getMultinomial", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMULTINOMIAL" },
+ { "SERIESSUM", "SERIESSUM", "com.sun.star.sheet.addin.Analysis.getSeriessum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETSERIESSUM" },
+ { "QUOTIENT", "QUOTIENT", "com.sun.star.sheet.addin.Analysis.getQuotient", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETQUOTIENT" },
+ { "MROUND", "MROUND", "com.sun.star.sheet.addin.Analysis.getMround", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMROUND" },
+ { "SQRTPI", "SQRTPI", "com.sun.star.sheet.addin.Analysis.getSqrtpi", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETSQRTPI" },
+ { "RANDBETWEEN", "RANDBETWEEN", "com.sun.star.sheet.addin.Analysis.getRandbetween", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRANDBETWEEN" },
+ { "GCD", "GCD_EXCEL2003", "com.sun.star.sheet.addin.Analysis.getGcd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGCD" },
+ { "LCM", "LCM_EXCEL2003", "com.sun.star.sheet.addin.Analysis.getLcm", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETLCM" },
+ { "BESSELI", "BESSELI", "com.sun.star.sheet.addin.Analysis.getBesseli", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELI" },
+ { "BESSELJ", "BESSELJ", "com.sun.star.sheet.addin.Analysis.getBesselj", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELJ" },
+ { "BESSELK", "BESSELK", "com.sun.star.sheet.addin.Analysis.getBesselk", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELK" },
+ { "BESSELY", "BESSELY", "com.sun.star.sheet.addin.Analysis.getBessely", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBESSELY" },
+ { "BIN2OCT", "BIN2OCT", "com.sun.star.sheet.addin.Analysis.getBin2Oct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2OCT" },
+ { "BIN2DEC", "BIN2DEC", "com.sun.star.sheet.addin.Analysis.getBin2Dec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2DEC" },
+ { "BIN2HEX", "BIN2HEX", "com.sun.star.sheet.addin.Analysis.getBin2Hex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETBIN2HEX" },
+ { "OCT2BIN", "OCT2BIN", "com.sun.star.sheet.addin.Analysis.getOct2Bin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2BIN" },
+ { "OCT2DEC", "OCT2DEC", "com.sun.star.sheet.addin.Analysis.getOct2Dec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2DEC" },
+ { "OCT2HEX", "OCT2HEX", "com.sun.star.sheet.addin.Analysis.getOct2Hex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETOCT2HEX" },
+ { "DEC2BIN", "DEC2BIN", "com.sun.star.sheet.addin.Analysis.getDec2Bin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2BIN" },
+ { "DEC2OCT", "DEC2OCT", "com.sun.star.sheet.addin.Analysis.getDec2Oct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2OCT" },
+ { "DEC2HEX", "DEC2HEX", "com.sun.star.sheet.addin.Analysis.getDec2Hex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDEC2HEX" },
+ { "HEX2BIN", "HEX2BIN", "com.sun.star.sheet.addin.Analysis.getHex2Bin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2BIN" },
+ { "HEX2DEC", "HEX2DEC", "com.sun.star.sheet.addin.Analysis.getHex2Dec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2DEC" },
+ { "HEX2OCT", "HEX2OCT", "com.sun.star.sheet.addin.Analysis.getHex2Oct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETHEX2OCT" },
+ { "DELTA", "DELTA", "com.sun.star.sheet.addin.Analysis.getDelta", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDELTA" },
+ { "ERF", "ERF", "com.sun.star.sheet.addin.Analysis.getErf", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETERF" },
+ { "ERFC", "ERFC", "com.sun.star.sheet.addin.Analysis.getErfc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETERFC" },
+ { "GESTEP", "GESTEP", "com.sun.star.sheet.addin.Analysis.getGestep", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETGESTEP" },
+ { "FACTDOUBLE", "FACTDOUBLE", "com.sun.star.sheet.addin.Analysis.getFactdouble", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFACTDOUBLE" },
+ { "IMABS", "IMABS", "com.sun.star.sheet.addin.Analysis.getImabs", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMABS" },
+ { "IMAGINARY", "IMAGINARY", "com.sun.star.sheet.addin.Analysis.getImaginary", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMAGINARY" },
+ { "IMPOWER", "IMPOWER", "com.sun.star.sheet.addin.Analysis.getImpower", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMPOWER" },
+ { "IMARGUMENT", "IMARGUMENT", "com.sun.star.sheet.addin.Analysis.getImargument", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMARGUMENT" },
+ { "IMCOS", "IMCOS", "com.sun.star.sheet.addin.Analysis.getImcos", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCOS" },
+ { "IMDIV", "IMDIV", "com.sun.star.sheet.addin.Analysis.getImdiv", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMDIV" },
+ { "IMEXP", "IMEXP", "com.sun.star.sheet.addin.Analysis.getImexp", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMEXP" },
+ { "IMCONJUGATE", "IMCONJUGATE", "com.sun.star.sheet.addin.Analysis.getImconjugate", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCONJUGATE" },
+ { "IMLN", "IMLN", "com.sun.star.sheet.addin.Analysis.getImln", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLN" },
+ { "IMLOG10", "IMLOG10", "com.sun.star.sheet.addin.Analysis.getImlog10", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLOG10" },
+ { "IMLOG2", "IMLOG2", "com.sun.star.sheet.addin.Analysis.getImlog2", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMLOG2" },
+ { "IMPRODUCT", "IMPRODUCT", "com.sun.star.sheet.addin.Analysis.getImproduct", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMPRODUCT" },
+ { "IMREAL", "IMREAL", "com.sun.star.sheet.addin.Analysis.getImreal", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMREAL" },
+ { "IMSIN", "IMSIN", "com.sun.star.sheet.addin.Analysis.getImsin", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSIN" },
+ { "IMSUB", "IMSUB", "com.sun.star.sheet.addin.Analysis.getImsub", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSUB" },
+ { "IMSUM", "IMSUM", "com.sun.star.sheet.addin.Analysis.getImsum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSUM" },
+ { "IMSQRT", "IMSQRT", "com.sun.star.sheet.addin.Analysis.getImsqrt", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSQRT" },
+ { "IMTAN", "IMTAN", "com.sun.star.sheet.addin.Analysis.getImtan", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMTAN" },
+ { "IMSEC", "IMSEC", "com.sun.star.sheet.addin.Analysis.getImsec", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSEC" },
+ { "IMCSC", "IMCSC", "com.sun.star.sheet.addin.Analysis.getImcsc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCSC" },
+ { "IMCOT", "IMCOT", "com.sun.star.sheet.addin.Analysis.getImcot", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCOT" },
+ { "IMSINH", "IMSINH", "com.sun.star.sheet.addin.Analysis.getImsinh", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSINH" },
+ { "IMCOSH", "IMCOSH", "com.sun.star.sheet.addin.Analysis.getImcosh", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCOSH" },
+ { "IMSECH", "IMSECH", "com.sun.star.sheet.addin.Analysis.getImsech", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMSECH" },
+ { "IMCSCH", "IMCSCH", "com.sun.star.sheet.addin.Analysis.getImcsch", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETIMCSCH" },
+ { "COMPLEX", "COMPLEX", "com.sun.star.sheet.addin.Analysis.getComplex", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOMPLEX" },
+ { "CONVERT", "CONVERT", "com.sun.star.sheet.addin.Analysis.getConvert", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCONVERT" },
+ { "AMORDEGRC", "AMORDEGRC", "com.sun.star.sheet.addin.Analysis.getAmordegrc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETAMORDEGRC" },
+ { "AMORLINC", "AMORLINC", "com.sun.star.sheet.addin.Analysis.getAmorlinc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETAMORLINC" },
+ { "ACCRINT", "ACCRINT", "com.sun.star.sheet.addin.Analysis.getAccrint", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETACCRINT" },
+ { "ACCRINTM", "ACCRINTM", "com.sun.star.sheet.addin.Analysis.getAccrintm", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETACCRINTM" },
+ { "RECEIVED", "RECEIVED", "com.sun.star.sheet.addin.Analysis.getReceived", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETRECEIVED" },
+ { "DISC", "DISC", "com.sun.star.sheet.addin.Analysis.getDisc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDISC" },
+ { "DURATION", "DURATION", "com.sun.star.sheet.addin.Analysis.getDuration", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDURATION" },
+ { "EFFECT", "EFFECT_ADD", "com.sun.star.sheet.addin.Analysis.getEffect", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETEFFECT" },
+ { "CUMPRINC", "CUMPRINC_ADD", "com.sun.star.sheet.addin.Analysis.getCumprinc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCUMPRINC" },
+ { "CUMIPMT", "CUMIPMT_ADD", "com.sun.star.sheet.addin.Analysis.getCumipmt", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCUMIPMT" },
+ { "PRICE", "PRICE", "com.sun.star.sheet.addin.Analysis.getPrice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICE" },
+ { "PRICEDISC", "PRICEDISC", "com.sun.star.sheet.addin.Analysis.getPricedisc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICEDISC" },
+ { "PRICEMAT", "PRICEMAT", "com.sun.star.sheet.addin.Analysis.getPricemat", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETPRICEMAT" },
+ { "MDURATION", "MDURATION", "com.sun.star.sheet.addin.Analysis.getMduration", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETMDURATION" },
+ { "NOMINAL", "NOMINAL_ADD", "com.sun.star.sheet.addin.Analysis.getNominal", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETNOMINAL" },
+ { "DOLLARFR", "DOLLARFR", "com.sun.star.sheet.addin.Analysis.getDollarfr", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDOLLARFR" },
+ { "DOLLARDE", "DOLLARDE", "com.sun.star.sheet.addin.Analysis.getDollarde", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETDOLLARDE" },
+ { "YIELD", "YIELD", "com.sun.star.sheet.addin.Analysis.getYield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELD" },
+ { "YIELDDISC", "YIELDDISC", "com.sun.star.sheet.addin.Analysis.getYielddisc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELDDISC" },
+ { "YIELDMAT", "YIELDMAT", "com.sun.star.sheet.addin.Analysis.getYieldmat", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETYIELDMAT" },
+ { "TBILLEQ", "TBILLEQ", "com.sun.star.sheet.addin.Analysis.getTbilleq", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLEQ" },
+ { "TBILLPRICE", "TBILLPRICE", "com.sun.star.sheet.addin.Analysis.getTbillprice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLPRICE" },
+ { "TBILLYIELD", "TBILLYIELD", "com.sun.star.sheet.addin.Analysis.getTbillyield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETTBILLYIELD" },
+ { "ODDFPRICE", "ODDFPRICE", "com.sun.star.sheet.addin.Analysis.getOddfprice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDFPRICE" },
+ { "ODDFYIELD", "ODDFYIELD", "com.sun.star.sheet.addin.Analysis.getOddfyield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDFYIELD" },
+ { "ODDLPRICE", "ODDLPRICE", "com.sun.star.sheet.addin.Analysis.getOddlprice", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDLPRICE" },
+ { "ODDLYIELD", "ODDLYIELD", "com.sun.star.sheet.addin.Analysis.getOddlyield", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETODDLYIELD" },
+ { "XIRR", "XIRR", "com.sun.star.sheet.addin.Analysis.getXirr", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETXIRR" },
+ { "XNPV", "XNPV", "com.sun.star.sheet.addin.Analysis.getXnpv", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETXNPV" },
+ { "INTRATE", "INTRATE", "com.sun.star.sheet.addin.Analysis.getIntrate", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETINTRATE" },
+ { "COUPNCD", "COUPNCD", "com.sun.star.sheet.addin.Analysis.getCoupncd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPNCD" },
+ { "COUPDAYS", "COUPDAYS", "com.sun.star.sheet.addin.Analysis.getCoupdays", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYS" },
+ { "COUPDAYSNC", "COUPDAYSNC", "com.sun.star.sheet.addin.Analysis.getCoupdaysnc", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYSNC" },
+ { "COUPDAYBS", "COUPDAYBS", "com.sun.star.sheet.addin.Analysis.getCoupdaybs", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPDAYBS" },
+ { "COUPPCD", "COUPPCD", "com.sun.star.sheet.addin.Analysis.getCouppcd", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPPCD" },
+ { "COUPNUM", "COUPNUM", "com.sun.star.sheet.addin.Analysis.getCoupnum", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETCOUPNUM" },
+ { "FVSCHEDULE", "FVSCHEDULE", "com.sun.star.sheet.addin.Analysis.getFvschedule", "COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.GETFVSCHEDULE" },
+};
+
+size_t ScCompiler::GetAddInMapCount()
+{
+ return std::size(g_aAddInMap);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/optutil.cxx b/sc/source/core/tool/optutil.cxx
new file mode 100644
index 0000000000..8f3e1fec9d
--- /dev/null
+++ b/sc/source/core/tool/optutil.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 <optutil.hxx>
+#include <global.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+bool ScOptionsUtil::IsMetricSystem()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return true;
+
+ //TODO: which language should be used here - system language or installed office language?
+
+ MeasurementSystem eSys = ScGlobal::getLocaleData().getMeasurementSystemEnum();
+
+ return ( eSys == MeasurementSystem::Metric );
+}
+
+ScLinkConfigItem::ScLinkConfigItem( const OUString& rSubTree ) :
+ ConfigItem( rSubTree )
+{
+}
+
+ScLinkConfigItem::ScLinkConfigItem( const OUString& rSubTree, ConfigItemMode nMode ) :
+ ConfigItem( rSubTree, nMode )
+{
+}
+
+void ScLinkConfigItem::SetCommitLink( const Link<ScLinkConfigItem&,void>& rLink )
+{
+ aCommitLink = rLink;
+}
+
+void ScLinkConfigItem::SetNotifyLink( const Link<ScLinkConfigItem&,void>& rLink )
+{
+ aNotifyLink = rLink;
+}
+
+void ScLinkConfigItem::Notify( const css::uno::Sequence<OUString>& /* aPropertyNames */ )
+{
+ aNotifyLink.Call(*this);
+}
+
+void ScLinkConfigItem::ImplCommit()
+{
+ aCommitLink.Call( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/orcusxml.cxx b/sc/source/core/tool/orcusxml.cxx
new file mode 100644
index 0000000000..eddeb977b9
--- /dev/null
+++ b/sc/source/core/tool/orcusxml.cxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <orcusxml.hxx>
+
+#include <utility>
+#include <vcl/weld.hxx>
+
+ScOrcusXMLTreeParam::EntryData::EntryData(EntryType eType)
+ : mnNamespaceID(0)
+ , meType(eType)
+ , maLinkedPos(ScAddress::INITIALIZE_INVALID)
+ , mbRangeParent(false)
+ , mbLeafNode(true)
+{}
+
+ScOrcusXMLTreeParam::EntryData* ScOrcusXMLTreeParam::getUserData(const weld::TreeView& rControl, const weld::TreeIter& rEntry)
+{
+ return weld::fromId<ScOrcusXMLTreeParam::EntryData*>(rControl.get_id(rEntry));
+}
+
+ScOrcusImportXMLParam::CellLink::CellLink(const ScAddress& rPos, OString aPath) :
+ maPos(rPos), maPath(std::move(aPath)) {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
new file mode 100644
index 0000000000..473177c8fc
--- /dev/null
+++ b/sc/source/core/tool/parclass.cxx
@@ -0,0 +1,710 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <parclass.hxx>
+#include <global.hxx>
+#include <callform.hxx>
+#include <addincol.hxx>
+#include <formula/token.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <string.h>
+
+#if DEBUG_SC_PARCLASSDOC
+// the documentation thingy
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+#include <rtl/strbuf.hxx>
+#include <formula/funcvarargs.h>
+#include "compiler.hxx"
+#endif
+
+using namespace formula;
+
+/* Following assumptions are made:
+ * - OpCodes not specified at all will have at least one and only parameters of
+ * type Value, no check is done on the count of parameters => no Bounds type
+ * is returned.
+ * - For OpCodes with a variable number of parameters the type(s) of the last
+ * repeated parameter(s) specified determine(s) the type(s) of all following
+ * parameters.
+ */
+
+const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
+{
+ // { OpCode, {{ ParamClass, ... }, nRepeatLast, ReturnClass }},
+
+ // IF() and CHOOSE() are somewhat special, since the ScJumpMatrix is
+ // created inside those functions and ConvertMatrixParameters() is not
+ // called for them.
+ { ocIf, {{ Array, Reference, Reference }, 0, Value }},
+ { ocIfError, {{ Array, Reference }, 0, Value }},
+ { ocIfNA, {{ Array, Reference }, 0, Value }},
+ { ocChoose, {{ Array, Reference }, 1, Value }},
+ // Other specials.
+ { ocArrayClose, {{ Bounds }, 0, Bounds }},
+ { ocArrayColSep, {{ Bounds }, 0, Bounds }},
+ { ocArrayOpen, {{ Bounds }, 0, Bounds }},
+ { ocArrayRowSep, {{ Bounds }, 0, Bounds }},
+ { ocBad, {{ Bounds }, 0, Bounds }},
+ { ocClose, {{ Bounds }, 0, Bounds }},
+ { ocColRowName, {{ Bounds }, 0, Value }}, // or Reference?
+ { ocColRowNameAuto, {{ Bounds }, 0, Value }}, // or Reference?
+ { ocDBArea, {{ Bounds }, 0, Value }}, // or Reference?
+ { ocMatRef, {{ Bounds }, 0, Value }},
+ { ocMissing, {{ Bounds }, 0, Value }},
+ { ocNoName, {{ Bounds }, 0, Bounds }},
+ { ocOpen, {{ Bounds }, 0, Bounds }},
+ { ocSep, {{ Bounds }, 0, Bounds }},
+ { ocSkip, {{ Bounds }, 0, Bounds }},
+ { ocSpaces, {{ Bounds }, 0, Bounds }},
+ { ocStop, {{ Bounds }, 0, Bounds }},
+ { ocStringXML, {{ Bounds }, 0, Bounds }},
+ { ocTableRef, {{ Bounds }, 0, Value }}, // or Reference?
+ { ocTableRefClose, {{ Bounds }, 0, Bounds }},
+ { ocTableRefItemAll, {{ Bounds }, 0, Bounds }},
+ { ocTableRefItemData, {{ Bounds }, 0, Bounds }},
+ { ocTableRefItemHeaders, {{ Bounds }, 0, Bounds }},
+ { ocTableRefItemThisRow, {{ Bounds }, 0, Bounds }},
+ { ocTableRefItemTotals, {{ Bounds }, 0, Bounds }},
+ { ocTableRefOpen, {{ Bounds }, 0, Bounds }},
+ // Error constants.
+ { ocErrDivZero, {{ Bounds }, 0, Bounds }},
+ { ocErrNA, {{ Bounds }, 0, Bounds }},
+ { ocErrName, {{ Bounds }, 0, Bounds }},
+ { ocErrNull, {{ Bounds }, 0, Bounds }},
+ { ocErrNum, {{ Bounds }, 0, Bounds }},
+ { ocErrRef, {{ Bounds }, 0, Bounds }},
+ { ocErrValue, {{ Bounds }, 0, Bounds }},
+ // Functions with Value parameters only but not in resource.
+ { ocBackSolver, {{ Value, Value, Value }, 0, Value }},
+ { ocTableOp, {{ Value, Value, Value, Value, Value }, 0, Value }},
+ // Operators and functions.
+ { ocAdd, {{ Array, Array }, 0, Value }},
+ { ocAggregate, {{ Value, Value, ReferenceOrForceArray }, 1, Value }},
+ { ocAmpersand, {{ Array, Array }, 0, Value }},
+ { ocAnd, {{ Reference }, 1, Value }},
+ { ocAreas, {{ Reference }, 0, Value }},
+ { ocAveDev, {{ Reference }, 1, Value }},
+ { ocAverage, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocAverageA, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocAverageIf, {{ ReferenceOrRefArray, Value, Reference }, 0, Value }},
+ { ocAverageIfs, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
+ { ocCell, {{ Value, Reference }, 0, Value }},
+ { ocColumn, {{ Reference }, 0, Value }},
+ { ocColumns, {{ Reference }, 1, Value }},
+ { ocConcat_MS, {{ Reference }, 1, Value }},
+ { ocCorrel, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocCount, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocCount2, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocCountEmptyCells, {{ ReferenceOrRefArray }, 0, Value }},
+ { ocCountIf, {{ ReferenceOrRefArray, Value }, 0, Value }},
+ { ocCountIfs, {{ ReferenceOrRefArray, Value }, 2, Value }},
+ { ocCovar, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocCovarianceP, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocCovarianceS, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocCurrent, {{ Bounds }, 0, Value }},
+ { ocDBAverage, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBCount, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBCount2, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBGet, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBMax, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBMin, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBProduct, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBStdDev, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBStdDevP, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBSum, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBVar, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDBVarP, {{ Reference, Reference, Reference }, 0, Value }},
+ { ocDevSq, {{ Reference }, 1, Value }},
+ { ocDiv, {{ Array, Array }, 0, Value }},
+ { ocEqual, {{ Array, Array }, 0, Value }},
+ { ocFTest, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocFalse, {{ Bounds }, 0, Value }},
+ { ocForecast, {{ Value, ForceArray, ForceArray }, 0, Value }},
+ { ocForecast_ETS_ADD, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value }, 0, Value }},
+ { ocForecast_ETS_MUL, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value }, 0, Value }},
+ { ocForecast_ETS_PIA, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value, Value }, 0, Value }},
+ { ocForecast_ETS_PIM, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value, Value }, 0, Value }},
+ { ocForecast_ETS_SEA, {{ ForceArray, ForceArray, Value, Value }, 0, Value }},
+ { ocForecast_ETS_STA, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value }, 0, Value }},
+ { ocForecast_ETS_STM, {{ ForceArray, ForceArray, ForceArray, Value, Value, Value }, 0, Value }},
+ { ocFormula, {{ Reference }, 0, Value }},
+ { ocFourier, {{ ForceArray, Value, Value, Value, Value }, 0, Value }},
+ { ocFrequency, {{ ReferenceOrForceArray, ReferenceOrForceArray }, 0, ForceArrayReturn }},
+ { ocGCD, {{ Reference }, 1, Value }},
+ { ocGeoMean, {{ Reference }, 1, Value }},
+ { ocGetActDate, {{ Bounds }, 0, Value }},
+ { ocGetActTime, {{ Bounds }, 0, Value }},
+ { ocGreater, {{ Array, Array }, 0, Value }},
+ { ocGreaterEqual, {{ Array, Array }, 0, Value }},
+ { ocGrowth, {{ Reference, Reference, Reference, Value }, 0, Value }},
+ { ocHLookup, {{ Value, ReferenceOrForceArray, Value, Value }, 0, Value }},
+ { ocHarMean, {{ Reference }, 1, Value }},
+ { ocIRR, {{ Reference, Value }, 0, Value }},
+ { ocIndex, {{ Reference, Value, Value, Value }, 0, Value }},
+ { ocIndirect, {{ Value, Value }, 0, Reference }},
+ { ocIntercept, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocIntersect, {{ Reference, Reference }, 0, Reference }},
+ { ocIsFormula, {{ Reference }, 0, Value }},
+ { ocIsRef, {{ Reference }, 0, Value }},
+ { ocKurt, {{ Reference }, 1, Value }},
+ { ocLCM, {{ Reference }, 1, Value }},
+ { ocLarge, {{ Reference, Value }, 0, Value }},
+ { ocLess, {{ Array, Array }, 0, Value }},
+ { ocLessEqual, {{ Array, Array }, 0, Value }},
+ { ocLinest, {{ ForceArray, ForceArray, Value, Value }, 0, Value }},
+ { ocLogest, {{ ForceArray, ForceArray, Value, Value }, 0, Value }},
+ { ocLookup, {{ Value, ReferenceOrForceArray, ReferenceOrForceArray }, 0, Value }},
+ { ocMIRR, {{ Reference, Value, Value }, 0, Value }},
+ { ocMatDet, {{ ForceArray }, 0, Value }},
+ { ocMatInv, {{ ForceArray }, 0, Value }},
+ { ocMatMult, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocMatTrans, {{ ForceArray }, 0, ForceArrayReturn }},
+ { ocMatValue, {{ Reference, Value, Value }, 0, Value }},
+ { ocMatch, {{ Value, ReferenceOrForceArray, Value }, 0, Value }},
+ { ocMax, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocMaxA, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocMaxIfs_MS, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
+ { ocMedian, {{ Reference }, 1, Value }},
+ { ocMin, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocMinA, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocMinIfs_MS, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
+ { ocModalValue, {{ ForceArray }, 1, Value }},
+ { ocModalValue_MS, {{ ForceArray }, 1, Value }},
+ { ocModalValue_Multi,{{ ForceArray }, 1, Value }},
+ { ocMul, {{ Array, Array }, 0, Value }},
+ { ocMultiArea, {{ Reference }, 1, Reference }},
+ { ocNPV, {{ Value, Reference }, 1, Value }},
+ { ocNeg, {{ Array }, 0, Value }},
+ { ocNegSub, {{ Array }, 0, Value }},
+ { ocNetWorkdays, {{ Value, Value, Reference, Reference }, 0, Value }},
+ { ocNetWorkdays_MS, {{ Value, Value, Value, Reference }, 0, Value }},
+ { ocNot, {{ Array }, 0, Value }},
+ { ocNotAvail, {{ Bounds }, 0, Value }},
+ { ocNotEqual, {{ Array, Array }, 0, Value }},
+ { ocOffset, {{ Reference, Value, Value, Value, Value }, 0, Reference }},
+ { ocOr, {{ Reference }, 1, Value }},
+ { ocPearson, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocPercentSign, {{ Array }, 0, Value }},
+ { ocPercentile, {{ Reference, Value }, 0, Value }},
+ { ocPercentile_Exc, {{ Reference, Value }, 0, Value }},
+ { ocPercentile_Inc, {{ Reference, Value }, 0, Value }},
+ { ocPercentrank, {{ Reference, Value, Value }, 0, Value }},
+ { ocPercentrank_Exc, {{ Reference, Value, Value }, 0, Value }},
+ { ocPercentrank_Inc, {{ Reference, Value, Value }, 0, Value }},
+ { ocPi, {{ Bounds }, 0, Value }},
+ { ocPow, {{ Array, Array }, 0, Value }},
+ { ocPower, {{ Array, Array }, 0, Value }},
+ { ocProb, {{ ForceArray, ForceArray, Value, Value }, 0, Value }},
+ { ocProduct, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocQuartile, {{ Reference, Value }, 0, Value }},
+ { ocQuartile_Exc, {{ Reference, Value }, 0, Value }},
+ { ocQuartile_Inc, {{ Reference, Value }, 0, Value }},
+ { ocRSQ, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocRandom, {{ Bounds }, 0, Value }},
+ { ocRandomNV, {{ Bounds }, 0, Value }},
+ { ocRange, {{ Reference, Reference }, 0, Reference }},
+ { ocRank, {{ Value, Reference, Value }, 0, Value }},
+ { ocRank_Avg, {{ Value, Reference, Value }, 0, Value }},
+ { ocRank_Eq, {{ Value, Reference, Value }, 0, Value }},
+ { ocRow, {{ Reference }, 0, Value }},
+ { ocRows, {{ Reference }, 1, Value }},
+ { ocSTEYX, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocSheet, {{ Reference }, 0, Value }},
+ { ocSheets, {{ Reference }, 1, Value }},
+ { ocSkew, {{ Reference }, 1, Value }},
+ { ocSkewp, {{ Reference }, 1, Value }},
+ { ocSlope, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocSmall, {{ Reference, Value }, 0, Value }},
+ { ocStDev, {{ Reference }, 1, Value }},
+ { ocStDevA, {{ Reference }, 1, Value }},
+ { ocStDevP, {{ Reference }, 1, Value }},
+ { ocStDevPA, {{ Reference }, 1, Value }},
+ { ocStDevP_MS, {{ Reference }, 1, Value }},
+ { ocStDevS, {{ Reference }, 1, Value }},
+ { ocSub, {{ Array, Array }, 0, Value }},
+ { ocSubTotal, {{ Value, ReferenceOrRefArray }, 1, Value }},
+ { ocSum, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocSumIf, {{ ReferenceOrRefArray, Value, Reference }, 0, Value }},
+ { ocSumIfs, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
+ { ocSumProduct, {{ ForceArray }, 1, Value }},
+ { ocSumSQ, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocSumX2DY2, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocSumX2MY2, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocSumXMY2, {{ ForceArray, ForceArray }, 0, Value }},
+ { ocTTest, {{ ForceArray, ForceArray, Value, Value }, 0, Value }},
+ { ocTextJoin_MS, {{ Reference, Value, Reference }, 1, Value }},
+ { ocTrend, {{ Reference, Reference, Reference, Value }, 0, Value }},
+ { ocTrimMean, {{ Reference, Value }, 0, Value }},
+ { ocTrue, {{ Bounds }, 0, Value }},
+ { ocUnion, {{ Reference, Reference }, 0, Reference }},
+ { ocVLookup, {{ Value, ReferenceOrForceArray, Value, Value }, 0, Value }},
+ { ocVar, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocVarA, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocVarP, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocVarPA, {{ ReferenceOrRefArray }, 1, Value }},
+ { ocVarP_MS, {{ Reference }, 1, Value }},
+ { ocVarS, {{ Reference }, 1, Value }},
+ { ocWhitespace, {{ Bounds }, 0, Bounds }},
+ { ocWorkday_MS, {{ Value, Value, Value, Reference }, 0, Value }},
+ { ocXor, {{ Reference }, 1, Value }},
+ { ocZTest, {{ Reference, Value, Value }, 0, Value }},
+ { ocZTest_MS, {{ Reference, Value, Value }, 0, Value }},
+ // Excel doubts:
+ // ocN, ocT: Excel says (and handles) Reference, error? This means no
+ // position dependent SingleRef if DoubleRef, and no array calculation,
+ // just the upper left corner. We never did that for ocT and now also not
+ // for ocN (position dependent intersection worked before but array
+ // didn't). No specifics in ODFF, so the general rule applies. Gnumeric
+ // does the same.
+ { ocN, {{ Value }, 0, Value }},
+ { ocT, {{ Value }, 0, Value }},
+ // The stopper.
+ { ocNone, {{ Bounds }, 0, Value }}
+};
+
+ScParameterClassification::RunData * ScParameterClassification::pData = nullptr;
+
+void ScParameterClassification::Init()
+{
+ if ( pData )
+ return;
+ pData = new RunData[ SC_OPCODE_LAST_OPCODE_ID + 1 ];
+ memset( pData, 0, sizeof(RunData) * (SC_OPCODE_LAST_OPCODE_ID + 1));
+
+ // init from specified static data above
+ for (const auto & i : pRawData)
+ {
+ const RawData* pRaw = &i;
+ if ( pRaw->eOp > SC_OPCODE_LAST_OPCODE_ID )
+ {
+ OSL_ENSURE( pRaw->eOp == ocNone, "RawData OpCode error");
+ }
+ else
+ {
+ RunData* pRun = &pData[ pRaw->eOp ];
+ SAL_WARN_IF(pRun->aData.nParam[0] != Unknown, "sc.core", "already assigned: " << static_cast<int>(pRaw->eOp));
+ memcpy( &(pRun->aData), &(pRaw->aData), sizeof(CommonData));
+ // fill 0-initialized fields with real values
+ if ( pRun->aData.nRepeatLast )
+ {
+ for ( sal_Int32 j=0; j < CommonData::nMaxParams; ++j )
+ {
+ if ( pRun->aData.nParam[j] )
+ pRun->nMinParams = sal::static_int_cast<sal_uInt8>( j+1 );
+ else if (j >= pRun->aData.nRepeatLast)
+ pRun->aData.nParam[j] = pRun->aData.nParam[j - pRun->aData.nRepeatLast];
+ else
+ {
+ SAL_INFO(
+ "sc.core",
+ "bad classification: eOp " << +pRaw->eOp
+ << ", repeated param " << j
+ << " negative offset");
+ pRun->aData.nParam[j] = Unknown;
+ }
+ }
+ }
+ else
+ {
+ for ( sal_Int32 j=0; j < CommonData::nMaxParams; ++j )
+ {
+ if ( !pRun->aData.nParam[j] )
+ {
+ if ( j == 0 || pRun->aData.nParam[j-1] != Bounds )
+ pRun->nMinParams = sal::static_int_cast<sal_uInt8>( j );
+ pRun->aData.nParam[j] = Bounds;
+ }
+ }
+ if ( !pRun->nMinParams &&
+ pRun->aData.nParam[CommonData::nMaxParams-1] != Bounds)
+ pRun->nMinParams = CommonData::nMaxParams;
+ }
+ for (const formula::ParamClass & j : pRun->aData.nParam)
+ {
+ if ( j == ForceArray || j == ReferenceOrForceArray )
+ {
+ pRun->bHasForceArray = true;
+ break; // for
+ }
+ }
+ }
+ }
+
+#if DEBUG_SC_PARCLASSDOC
+ GenerateDocumentation();
+#endif
+}
+
+void ScParameterClassification::Exit()
+{
+ delete [] pData;
+ pData = nullptr;
+}
+
+formula::ParamClass ScParameterClassification::GetParameterType(
+ const formula::FormulaToken* pToken, sal_uInt16 nParameter)
+{
+ OpCode eOp = pToken->GetOpCode();
+ switch ( eOp )
+ {
+ case ocExternal:
+ return GetExternalParameterType( pToken, nParameter);
+ case ocMacro:
+ return (nParameter == SAL_MAX_UINT16 ? Value : Reference);
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ if ( 0 <= static_cast<short>(eOp) && eOp <= SC_OPCODE_LAST_OPCODE_ID )
+ {
+ sal_uInt8 nRepeat;
+ formula::ParamClass eType;
+ if (nParameter == SAL_MAX_UINT16)
+ eType = pData[eOp].aData.eReturn;
+ else if ( nParameter < CommonData::nMaxParams )
+ eType = pData[eOp].aData.nParam[nParameter];
+ else if ( (nRepeat = pData[eOp].aData.nRepeatLast) > 0 )
+ {
+ // The usual case is 1 repeated parameter, we don't need to
+ // calculate that on each call.
+ sal_uInt16 nParam = (nRepeat > 1 ?
+ (pData[eOp].nMinParams -
+ ((nParameter - pData[eOp].nMinParams) % nRepeat)) :
+ pData[eOp].nMinParams);
+ return pData[eOp].aData.nParam[nParam];
+ }
+ else
+ eType = Bounds;
+ return eType == Unknown ? Value : eType;
+ }
+ return Unknown;
+}
+
+formula::ParamClass ScParameterClassification::GetExternalParameterType( const formula::FormulaToken* pToken,
+ sal_uInt16 nParameter)
+{
+ formula::ParamClass eRet = Unknown;
+ if (nParameter == SAL_MAX_UINT16)
+ return eRet;
+
+ // similar to ScInterpreter::ScExternal()
+ OUString aFuncName = pToken->GetExternal().toAsciiUpperCase(); // programmatic name
+ {
+ const LegacyFuncData* pLegacyFuncData = ScGlobal::GetLegacyFuncCollection()->findByName(aFuncName);
+ if (pLegacyFuncData)
+ {
+ if ( nParameter >= pLegacyFuncData->GetParamCount() )
+ eRet = Bounds;
+ else
+ {
+ switch ( pLegacyFuncData->GetParamType( nParameter) )
+ {
+ case ParamType::PTR_DOUBLE:
+ case ParamType::PTR_STRING:
+ eRet = Value;
+ break;
+ default:
+ eRet = Reference;
+ // also array types are created using an area reference
+ }
+ }
+ return eRet;
+ }
+ }
+
+ OUString aUnoName =
+ ScGlobal::GetAddInCollection()->FindFunction(aFuncName, false);
+
+ if (!aUnoName.isEmpty())
+ {
+ // the relevant parts of ScUnoAddInCall without having to create one
+ const ScUnoAddInFuncData* pFuncData =
+ ScGlobal::GetAddInCollection()->GetFuncData( aUnoName, true ); // need fully initialized data
+ if ( pFuncData )
+ {
+ tools::Long nCount = pFuncData->GetArgumentCount();
+ if ( nCount <= 0 )
+ eRet = Bounds;
+ else
+ {
+ const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
+ if ( nParameter >= nCount &&
+ pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
+ eRet = Value;
+ // last arg is sequence, optional "any"s, we simply can't
+ // determine the type
+ if ( eRet == Unknown )
+ {
+ if ( nParameter >= nCount )
+ eRet = Bounds;
+ else
+ {
+ switch ( pArgs[nParameter].eType )
+ {
+ case SC_ADDINARG_INTEGER:
+ case SC_ADDINARG_DOUBLE:
+ case SC_ADDINARG_STRING:
+ eRet = Value;
+ break;
+ default:
+ eRet = Reference;
+ }
+ }
+ }
+ }
+ }
+ }
+ return eRet;
+}
+
+#if DEBUG_SC_PARCLASSDOC
+
+// add remaining functions, all Value parameters
+void ScParameterClassification::MergeArgumentsFromFunctionResource()
+{
+ ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ for ( const ScFuncDesc* pDesc = pFuncList->First(); pDesc;
+ pDesc = pFuncList->Next() )
+ {
+ if ( pDesc->nFIndex > SC_OPCODE_LAST_OPCODE_ID ||
+ pData[pDesc->nFIndex].aData.nParam[0] != Unknown )
+ continue; // not an internal opcode or already done
+
+ RunData* pRun = &pData[ pDesc->nFIndex ];
+ sal_uInt16 nArgs = pDesc->GetSuppressedArgCount();
+ if ( nArgs >= PAIRED_VAR_ARGS )
+ {
+ nArgs -= PAIRED_VAR_ARGS - 2;
+ pRun->aData.nRepeatLast = 2;
+ }
+ else if ( nArgs >= VAR_ARGS )
+ {
+ nArgs -= VAR_ARGS - 1;
+ pRun->aData.nRepeatLast = 1;
+ }
+ if ( nArgs > CommonData::nMaxParams )
+ {
+ SAL_WARN( "sc", "ScParameterClassification::Init: too many arguments in listed function: "
+ << *(pDesc->pFuncName)
+ << ": " << nArgs );
+ nArgs = CommonData::nMaxParams - 1;
+ pRun->aData.nRepeatLast = 1;
+ }
+ pRun->nMinParams = static_cast< sal_uInt8 >( nArgs );
+ for ( sal_Int32 j=0; j < nArgs; ++j )
+ {
+ pRun->aData.nParam[j] = Value;
+ }
+ if ( pRun->aData.nRepeatLast )
+ {
+ for ( sal_Int32 j = nArgs; j < CommonData::nMaxParams; ++j )
+ {
+ pRun->aData.nParam[j] = Value;
+ }
+ }
+ else
+ {
+ for ( sal_Int32 j = nArgs; j < CommonData::nMaxParams; ++j )
+ {
+ pRun->aData.nParam[j] = Bounds;
+ }
+ }
+ }
+}
+
+void ScParameterClassification::GenerateDocumentation()
+{
+ static const char aEnvVarName[] = "OOO_CALC_GENPARCLASSDOC";
+ if ( !getenv( aEnvVarName) )
+ return;
+ MergeArgumentsFromFunctionResource();
+ ScAddress aAddress;
+ ScCompiler aComp(NULL,aAddress);
+ ScCompiler::OpCodeMapPtr xMap( aComp.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));
+ if (!xMap)
+ return;
+ fflush( stderr);
+ size_t nCount = xMap->getSymbolCount();
+ for ( size_t i=0; i<nCount; ++i )
+ {
+ OpCode eOp = OpCode(i);
+ if ( !xMap->getSymbol(eOp).isEmpty() )
+ {
+ OUStringBuffer aStr(xMap->getSymbol(eOp));
+ formula::FormulaByteToken aToken( eOp);
+ sal_uInt8 nParams = GetMinimumParameters( eOp);
+ // preset parameter count according to opcode value, with some
+ // special handling
+ bool bAddParentheses = true;
+ if ( eOp < SC_OPCODE_STOP_DIV )
+ {
+ bAddParentheses = false; // will be overridden below if parameters
+ switch ( eOp )
+ {
+ case ocIf:
+ aToken.SetByte(3);
+ break;
+ case ocIfError:
+ case ocIfNA:
+ case ocChoose:
+ aToken.SetByte(2);
+ break;
+ case ocPercentSign:
+ aToken.SetByte(1);
+ break;
+ default:;
+ }
+ }
+ else if ( eOp < SC_OPCODE_STOP_ERRORS )
+ {
+ bAddParentheses = false;
+ aToken.SetByte(0);
+ }
+ else if ( eOp < SC_OPCODE_STOP_BIN_OP )
+ {
+ switch ( eOp )
+ {
+ case ocAnd:
+ case ocOr:
+ aToken.SetByte(1); // (r1)AND(r2) --> AND( r1, ...)
+ break;
+ default:
+ aToken.SetByte(2);
+ }
+ }
+ else if ( eOp < SC_OPCODE_STOP_UN_OP )
+ aToken.SetByte(1);
+ else if ( eOp < SC_OPCODE_STOP_NO_PAR )
+ aToken.SetByte(0);
+ else if ( eOp < SC_OPCODE_STOP_1_PAR )
+ aToken.SetByte(1);
+ else
+ aToken.SetByte( nParams);
+ // compare (this is a mere test for opcode order Div, BinOp, UnOp,
+ // NoPar, 1Par, ...) and override parameter count with
+ // classification
+ if ( nParams != aToken.GetByte() )
+ SAL_WARN("sc.core", "(parameter count differs, token Byte: " << (int)aToken.GetByte() << " classification: " << (int)nParams << ") ");
+ aToken.SetByte( nParams);
+ if ( nParams != aToken.GetParamCount() )
+ SAL_WARN("sc.core", "(parameter count differs, token ParamCount: " << (int)aToken.GetParamCount() << " classification: " << (int)nParams << ") ");
+ if (aToken.GetByte())
+ bAddParentheses = true;
+ if (bAddParentheses)
+ aStr.append('(');
+ for ( sal_uInt16 j=0; j < nParams; ++j )
+ {
+ if ( j > 0 )
+ aStr.append(',');
+ formula::ParamClass eType = GetParameterType( &aToken, j);
+ switch ( eType )
+ {
+ case Value :
+ aStr.append(" Value");
+ break;
+ case Reference :
+ aStr.append(" Reference");
+ break;
+ case ReferenceOrRefArray :
+ aStr.append(" ReferenceOrRefArray");
+ break;
+ case Array :
+ aStr.append(" Array");
+ break;
+ case ForceArray :
+ aStr.append(" ForceArray");
+ break;
+ case ReferenceOrForceArray :
+ aStr.append(" ReferenceOrForceArray");
+ break;
+ case Bounds :
+ aStr.append(" (Bounds, classification error?)");
+ break;
+ default:
+ aStr.append(" (???, classification error?)");
+ }
+ }
+ if ( HasRepeatParameters( eOp) )
+ aStr.append(", ...");
+ if ( nParams )
+ aStr.append(' ');
+ if (bAddParentheses)
+ aStr.append(')');
+ switch ( eOp )
+ {
+ case ocRRI:
+ aStr.append(" // RRI in English resource, but ZGZ in English-only section");
+ break;
+ case ocMultiArea:
+ aStr.append(" // e.g. combined first parameter of INDEX() function, not a real function");
+ break;
+ case ocBackSolver:
+ aStr.append(" // goal seek via menu, not a real function");
+ break;
+ case ocTableOp:
+ aStr.append(" // MULTIPLE.OPERATIONS in English resource, but TABLE in English-only section");
+ break;
+ case ocNoName:
+ aStr.append(" // error function, not a real function");
+ break;
+ default:;
+ }
+ // Return type.
+ formula::ParamClass eType = GetParameterType( &aToken, SAL_MAX_UINT16);
+ switch ( eType )
+ {
+ case Value :
+ aStr.append(" -> Value");
+ break;
+ case Reference :
+ aStr.append(" -> Reference");
+ break;
+ case ReferenceOrRefArray :
+ aStr.append(" -> ReferenceOrRefArray");
+ break;
+ case Array :
+ aStr.append(" -> Array");
+ break;
+ case ForceArray :
+ aStr.append(" -> ForceArray");
+ break;
+ case ReferenceOrForceArray :
+ aStr.append(" -> ReferenceOrForceArray");
+ break;
+ case Bounds :
+ ; // nothing
+ break;
+ default:
+ aStr.append(" (-> ???, classification error?)");
+ }
+ /* We could add yet another log domain for this, if we wanted... but
+ * as it more seldom than rarely used it's not actually necessary,
+ * just grep output. */
+ SAL_INFO( "sc.core", "CALC_GENPARCLASSDOC: " << aStr.makeStringAndClear());
+ }
+ }
+ fflush( stdout);
+}
+
+#endif // OSL_DEBUG_LEVEL
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/printopt.cxx b/sc/source/core/tool/printopt.cxx
new file mode 100644
index 0000000000..e9b3e15161
--- /dev/null
+++ b/sc/source/core/tool/printopt.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 <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/diagnose.h>
+
+#include <printopt.hxx>
+#include <sc.hrc>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+
+ScPrintOptions::ScPrintOptions()
+{
+ SetDefaults();
+}
+
+void ScPrintOptions::SetDefaults()
+{
+ bSkipEmpty = true;
+ bAllSheets = false;
+ bForceBreaks = false;
+}
+
+bool ScPrintOptions::operator==( const ScPrintOptions& rOpt ) const
+{
+ return bSkipEmpty == rOpt.bSkipEmpty
+ && bAllSheets == rOpt.bAllSheets
+ && bForceBreaks == rOpt.bForceBreaks;
+}
+
+ScTpPrintItem::ScTpPrintItem( const ScPrintOptions& rOpt ) :
+ SfxPoolItem ( SID_SCPRINTOPTIONS ),
+ theOptions ( rOpt )
+{
+}
+
+ScTpPrintItem::~ScTpPrintItem()
+{
+}
+
+bool ScTpPrintItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScTpPrintItem& rPItem = static_cast<const ScTpPrintItem&>(rItem);
+ return ( theOptions == rPItem.theOptions );
+}
+
+ScTpPrintItem* ScTpPrintItem::Clone( SfxItemPool * ) const
+{
+ return new ScTpPrintItem( *this );
+}
+
+constexpr OUStringLiteral CFGPATH_PRINT = u"Office.Calc/Print";
+
+#define SCPRINTOPT_EMPTYPAGES 0
+#define SCPRINTOPT_ALLSHEETS 1
+#define SCPRINTOPT_FORCEBREAKS 2
+
+Sequence<OUString> ScPrintCfg::GetPropertyNames()
+{
+ return {"Page/EmptyPages", // SCPRINTOPT_EMPTYPAGES
+ "Other/AllSheets", // SCPRINTOPT_ALLSHEETS
+ "Page/ForceBreaks"}; // SCPRINTOPT_FORCEBREAKS;
+}
+
+ScPrintCfg::ScPrintCfg() :
+ ConfigItem( CFGPATH_PRINT )
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ EnableNotification(aNames);
+ ReadCfg();
+}
+
+void ScPrintCfg::ReadCfg()
+{
+ const Sequence<OUString> aNames = GetPropertyNames();
+ const Sequence<Any> aValues = GetProperties(aNames);
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() != aNames.getLength())
+ return;
+
+ if (bool bVal; aValues[SCPRINTOPT_EMPTYPAGES] >>= bVal)
+ SetSkipEmpty(!bVal); // reversed
+ if (bool bVal; aValues[SCPRINTOPT_ALLSHEETS] >>= bVal)
+ SetAllSheets(bVal);
+ if (bool bVal; aValues[SCPRINTOPT_FORCEBREAKS] >>= bVal)
+ SetForceBreaks(bVal);
+}
+
+void ScPrintCfg::ImplCommit()
+{
+ Sequence<OUString> aNames = GetPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ pValues[SCPRINTOPT_EMPTYPAGES] <<= !GetSkipEmpty(); // reversed
+ pValues[SCPRINTOPT_ALLSHEETS] <<= GetAllSheets();
+ pValues[SCPRINTOPT_FORCEBREAKS] <<= GetForceBreaks();
+ PutProperties(aNames, aValues);
+}
+
+void ScPrintCfg::SetOptions( const ScPrintOptions& rNew )
+{
+ *static_cast<ScPrintOptions*>(this) = rNew;
+ SetModified();
+ Commit();
+}
+
+void ScPrintCfg::Notify( const css::uno::Sequence< OUString >& ) { ReadCfg(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/prnsave.cxx b/sc/source/core/tool/prnsave.cxx
new file mode 100644
index 0000000000..abf4926001
--- /dev/null
+++ b/sc/source/core/tool/prnsave.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 <prnsave.hxx>
+#include <address.hxx>
+
+#include <osl/diagnose.h>
+#include <tools/json_writer.hxx>
+
+// Data per table
+
+ScPrintSaverTab::ScPrintSaverTab() :
+ mbEntireSheet(false)
+{
+}
+
+ScPrintSaverTab::~ScPrintSaverTab()
+{
+}
+
+void ScPrintSaverTab::SetAreas( ScRangeVec&& rRanges, bool bEntireSheet )
+{
+ maPrintRanges = std::move(rRanges);
+ mbEntireSheet = bEntireSheet;
+}
+
+void ScPrintSaverTab::SetRepeat( std::optional<ScRange> oCol, std::optional<ScRange> oRow )
+{
+ moRepeatCol = std::move(oCol);
+ moRepeatRow = std::move(oRow);
+}
+
+bool ScPrintSaverTab::operator==( const ScPrintSaverTab& rCmp ) const
+{
+ return
+ (moRepeatCol == rCmp.moRepeatCol) &&
+ (moRepeatRow == rCmp.moRepeatRow) &&
+ (mbEntireSheet == rCmp.mbEntireSheet) &&
+ (maPrintRanges == rCmp.maPrintRanges);
+}
+
+// Data for the whole document
+
+ScPrintRangeSaver::ScPrintRangeSaver( SCTAB nCount ) :
+ nTabCount( nCount )
+{
+ if (nCount > 0)
+ pData.reset( new ScPrintSaverTab[nCount] );
+}
+
+ScPrintRangeSaver::~ScPrintRangeSaver()
+{
+}
+
+ScPrintSaverTab& ScPrintRangeSaver::GetTabData(SCTAB nTab)
+{
+ OSL_ENSURE(nTab<nTabCount,"ScPrintRangeSaver Tab too big");
+ return pData[nTab];
+}
+
+const ScPrintSaverTab& ScPrintRangeSaver::GetTabData(SCTAB nTab) const
+{
+ OSL_ENSURE(nTab<nTabCount,"ScPrintRangeSaver Tab too big");
+ return pData[nTab];
+}
+
+void ScPrintRangeSaver::GetPrintRangesInfo(tools::JsonWriter& rPrintRanges) const
+{
+ // Array for sheets in the document.
+ auto printRanges = rPrintRanges.startArray("printranges");
+ for (SCTAB nTab = 0; nTab < nTabCount; nTab++)
+ {
+ auto sheetNode = rPrintRanges.startStruct();
+ const ScPrintSaverTab& rPsTab = pData[nTab];
+ const std::vector<ScRange>& rRangeVec = rPsTab.GetPrintRanges();
+
+ rPrintRanges.put("sheet", static_cast<sal_Int32>(nTab));
+
+ // Array for ranges within each sheet.
+ auto sheetRanges = rPrintRanges.startArray("ranges");
+ OStringBuffer aRanges;
+ sal_Int32 nLast = rRangeVec.size() - 1;
+ for (sal_Int32 nIdx = 0; nIdx <= nLast; ++nIdx)
+ {
+ const ScRange& rRange = rRangeVec[nIdx];
+ aRanges.append("[ " +
+ OString::number(rRange.aStart.Col()) + ", " +
+ OString::number(rRange.aStart.Row()) + ", " +
+ OString::number(rRange.aEnd.Col()) + ", " +
+ OString::number(rRange.aEnd.Row()) +
+ (nLast == nIdx ? std::string_view("]") : std::string_view("], ")));
+ }
+
+ rPrintRanges.putRaw(aRanges);
+ }
+}
+
+bool ScPrintRangeSaver::operator==( const ScPrintRangeSaver& rCmp ) const
+{
+ bool bEqual = ( nTabCount == rCmp.nTabCount );
+ if (bEqual)
+ for (SCTAB i=0; i<nTabCount; i++)
+ if (!(pData[i]==rCmp.pData[i]))
+ {
+ bEqual = false;
+ break;
+ }
+ return bEqual;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/progress.cxx b/sc/source/core/tool/progress.cxx
new file mode 100644
index 0000000000..ae0b99b32f
--- /dev/null
+++ b/sc/source/core/tool/progress.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 <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/eitem.hxx>
+#include <svl/itemset.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/frame/XModel.hpp>
+
+#define SC_PROGRESS_CXX
+#include <progress.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+using namespace com::sun::star;
+
+static ScProgress theDummyInterpretProgress;
+SfxProgress* ScProgress::pGlobalProgress = nullptr;
+sal_uInt64 ScProgress::nGlobalRange = 0;
+sal_uInt64 ScProgress::nGlobalPercent = 0;
+ScProgress* ScProgress::pInterpretProgress = &theDummyInterpretProgress;
+sal_uInt64 ScProgress::nInterpretProgress = 0;
+ScDocument* ScProgress::pInterpretDoc;
+bool ScProgress::bIdleWasEnabled = false;
+
+static bool lcl_IsHiddenDocument( const SfxObjectShell* pObjSh )
+{
+ if (pObjSh)
+ {
+ SfxMedium* pMed = pObjSh->GetMedium();
+ if (pMed)
+ {
+ if (const SfxBoolItem* pItem = pMed->GetItemSet().GetItemIfSet(SID_HIDDEN);
+ pItem && pItem->GetValue())
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool lcl_HasControllersLocked( const SfxObjectShell& rObjSh )
+{
+ uno::Reference<frame::XModel> xModel( rObjSh.GetBaseModel() );
+ if (xModel.is())
+ return xModel->hasControllersLocked();
+ return false;
+}
+
+ScProgress::ScProgress(SfxObjectShell* pObjSh, const OUString& rText,
+ sal_uInt64 nRange, bool bWait)
+ : bEnabled(true)
+{
+
+ if ( pGlobalProgress || SfxProgress::GetActiveProgress() )
+ {
+ if ( lcl_IsHiddenDocument(pObjSh) )
+ {
+ // loading a hidden document while a progress is active is possible - no error
+ pProgress = nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "ScProgress: there can be only one!" );
+ pProgress = nullptr;
+ }
+ }
+ else if ( SfxGetpApp()->IsDowning() )
+ {
+ // This happens. E.g. when saving the clipboard-content as OLE when closing the app.
+ // In this case a SfxProgress would produce dirt in memory.
+ //TODO: Should that be this way ???
+
+ pProgress = nullptr;
+ }
+ else if ( pObjSh && ( pObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ||
+ pObjSh->GetProgress() ||
+ lcl_HasControllersLocked(*pObjSh) ) )
+ {
+ // no own progress for embedded objects,
+ // no second progress if the document already has one
+
+ pProgress = nullptr;
+ }
+ else
+ {
+ pProgress.reset(new SfxProgress( pObjSh, rText, nRange, bWait ));
+ pGlobalProgress = pProgress.get();
+ nGlobalRange = nRange;
+ nGlobalPercent = 0;
+ }
+}
+
+ScProgress::ScProgress()
+ : bEnabled(true)
+{
+ // DummyInterpret
+}
+
+ScProgress::~ScProgress()
+{
+ if ( pProgress )
+ {
+ pProgress.reset();
+ pGlobalProgress = nullptr;
+ nGlobalRange = 0;
+ nGlobalPercent = 0;
+ }
+}
+
+void ScProgress::CreateInterpretProgress( ScDocument* pDoc, bool bWait )
+{
+ if ( nInterpretProgress )
+ nInterpretProgress++;
+ else if ( pDoc->GetAutoCalc() )
+ {
+ nInterpretProgress = 1;
+ bIdleWasEnabled = pDoc->IsIdleEnabled();
+ pDoc->EnableIdle(false);
+ // Interpreter may be called in many circumstances, also if another
+ // progress bar is active, for example while adapting row heights.
+ // Keep the dummy interpret progress.
+ if ( !pGlobalProgress )
+ pInterpretProgress = new ScProgress( pDoc->GetDocumentShell(),
+ ScResId( STR_PROGRESS_CALCULATING ),
+ pDoc->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE, bWait );
+ pInterpretDoc = pDoc;
+ }
+}
+
+void ScProgress::DeleteInterpretProgress()
+{
+ if ( !nInterpretProgress )
+ return;
+
+ /* Do not decrement 'nInterpretProgress', before 'pInterpretProgress'
+ is deleted. In rare cases, deletion of 'pInterpretProgress' causes
+ a refresh of the sheet window which may call CreateInterpretProgress
+ and DeleteInterpretProgress again (from Output::DrawStrings),
+ resulting in double deletion of 'pInterpretProgress'. */
+ if ( nInterpretProgress == 1 )
+ {
+ if ( pInterpretProgress != &theDummyInterpretProgress )
+ {
+ // move pointer to local temporary to avoid double deletion
+ ScProgress* pTmpProgress = pInterpretProgress;
+ pInterpretProgress = &theDummyInterpretProgress;
+ delete pTmpProgress;
+ }
+ if ( pInterpretDoc )
+ pInterpretDoc->EnableIdle(bIdleWasEnabled);
+ }
+ --nInterpretProgress;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/queryentry.cxx b/sc/source/core/tool/queryentry.cxx
new file mode 100644
index 0000000000..d66382d2e2
--- /dev/null
+++ b/sc/source/core/tool/queryentry.cxx
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <queryentry.hxx>
+
+#include <unotools/textsearch.hxx>
+
+/*
+ * dialog returns the special field values "empty"/"not empty"
+ * as constants SC_EMPTYFIELDS and SC_NONEMPTYFIELDS respectively in nVal in
+ * conjunctions with the flag bQueryByString = FALSE.
+ */
+
+#define SC_EMPTYFIELDS (double(0x0042))
+#define SC_NONEMPTYFIELDS (double(0x0043))
+#define SC_TEXTCOLOR (double(0x0044))
+#define SC_BACKGROUNDCOLOR (double(0x0045))
+
+bool ScQueryEntry::Item::operator== (const Item& r) const
+{
+ return meType == r.meType && mfVal == r.mfVal && maString == r.maString && mbMatchEmpty == r.mbMatchEmpty
+ && mbRoundForFilter == r.mbRoundForFilter;
+}
+
+ScQueryEntry::ScQueryEntry() :
+ bDoQuery(false),
+ nField(0),
+ eOp(SC_EQUAL),
+ eConnect(SC_AND),
+ maQueryItems(1)
+{
+}
+
+ScQueryEntry::ScQueryEntry(const ScQueryEntry& r) :
+ bDoQuery(r.bDoQuery),
+ nField(r.nField),
+ eOp(r.eOp),
+ eConnect(r.eConnect),
+ maQueryItems(r.maQueryItems)
+{
+}
+
+ScQueryEntry::~ScQueryEntry()
+{
+}
+
+ScQueryEntry& ScQueryEntry::operator=( const ScQueryEntry& r )
+{
+ bDoQuery = r.bDoQuery;
+ eOp = r.eOp;
+ eConnect = r.eConnect;
+ nField = r.nField;
+ maQueryItems = r.maQueryItems;
+
+ pSearchParam.reset();
+ pSearchText.reset();
+
+ return *this;
+}
+
+void ScQueryEntry::SetQueryByEmpty()
+{
+ eOp = SC_EQUAL;
+ maQueryItems.resize(1);
+ Item& rItem = maQueryItems[0];
+ rItem.meType = ByEmpty;
+ rItem.maString = svl::SharedString();
+ rItem.mfVal = SC_EMPTYFIELDS;
+}
+
+bool ScQueryEntry::IsQueryByEmpty() const
+{
+ if (maQueryItems.size() != 1)
+ return false;
+
+ const Item& rItem = maQueryItems[0];
+ return eOp == SC_EQUAL &&
+ rItem.meType == ByEmpty &&
+ rItem.maString.isEmpty() &&
+ rItem.mfVal == SC_EMPTYFIELDS;
+}
+
+void ScQueryEntry::SetQueryByNonEmpty()
+{
+ eOp = SC_EQUAL;
+ maQueryItems.resize(1);
+ Item& rItem = maQueryItems[0];
+ rItem.meType = ByEmpty;
+ rItem.maString = svl::SharedString();
+ rItem.mfVal = SC_NONEMPTYFIELDS;
+}
+
+bool ScQueryEntry::IsQueryByNonEmpty() const
+{
+ if (maQueryItems.size() != 1)
+ return false;
+
+ const Item& rItem = maQueryItems[0];
+ return eOp == SC_EQUAL &&
+ rItem.meType == ByEmpty &&
+ rItem.maString.isEmpty() &&
+ rItem.mfVal == SC_NONEMPTYFIELDS;
+}
+
+void ScQueryEntry::SetQueryByTextColor(Color color)
+{
+ eOp = SC_EQUAL;
+ maQueryItems.resize(1);
+ Item& rItem = maQueryItems[0];
+ rItem.meType = ByTextColor;
+ rItem.maString = svl::SharedString();
+ rItem.mfVal = SC_TEXTCOLOR;
+ rItem.maColor = color;
+}
+
+bool ScQueryEntry::IsQueryByTextColor() const
+{
+ if (maQueryItems.size() != 1)
+ return false;
+
+ const Item& rItem = maQueryItems[0];
+ return eOp == SC_EQUAL &&
+ rItem.meType == ByTextColor;
+}
+
+void ScQueryEntry::SetQueryByBackgroundColor(Color color)
+{
+ eOp = SC_EQUAL;
+ maQueryItems.resize(1);
+ Item& rItem = maQueryItems[0];
+ rItem.meType = ByBackgroundColor;
+ rItem.maString = svl::SharedString();
+ rItem.mfVal = SC_BACKGROUNDCOLOR;
+ rItem.maColor = color;
+}
+
+bool ScQueryEntry::IsQueryByBackgroundColor() const
+{
+ if (maQueryItems.size() != 1)
+ return false;
+
+ const Item& rItem = maQueryItems[0];
+ return eOp == SC_EQUAL &&
+ rItem.meType == ByBackgroundColor;
+}
+
+ScQueryEntry::Item& ScQueryEntry::GetQueryItemImpl() const
+{
+ if (maQueryItems.size() != 1)
+ // Reset to a single query mode.
+ maQueryItems.resize(1);
+ return maQueryItems[0];
+}
+
+void ScQueryEntry::Clear()
+{
+ bDoQuery = false;
+ eOp = SC_EQUAL;
+ eConnect = SC_AND;
+ nField = 0;
+ maQueryItems.clear();
+ maQueryItems.emplace_back();
+
+ pSearchParam.reset();
+ pSearchText.reset();
+}
+
+bool ScQueryEntry::operator==( const ScQueryEntry& r ) const
+{
+ return bDoQuery == r.bDoQuery
+ && eOp == r.eOp
+ && eConnect == r.eConnect
+ && nField == r.nField
+ && maQueryItems == r.maQueryItems;
+ // do not compare pSearchParam and pSearchText!
+}
+
+utl::TextSearch* ScQueryEntry::GetSearchTextPtr( utl::SearchParam::SearchType eSearchType, bool bCaseSens,
+ bool bWildMatchSel ) const
+{
+ if ( !pSearchParam )
+ {
+ OUString aStr = maQueryItems[0].maString.getString();
+ pSearchParam.reset(new utl::SearchParam(
+ aStr, eSearchType, bCaseSens, '~', bWildMatchSel));
+ pSearchText.reset(new utl::TextSearch( *pSearchParam, ScGlobal::getCharClass() ));
+ }
+ return pSearchText.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/queryparam.cxx b/sc/source/core/tool/queryparam.cxx
new file mode 100644
index 0000000000..286a1ef4ff
--- /dev/null
+++ b/sc/source/core/tool/queryparam.cxx
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <queryparam.hxx>
+#include <queryentry.hxx>
+#include <scmatrix.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <svl/numformat.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+namespace {
+
+const size_t MAXQUERY = 8;
+
+class FindByField
+{
+ SCCOLROW mnField;
+public:
+ explicit FindByField(SCCOLROW nField) : mnField(nField) {}
+ bool operator() (const ScQueryEntry& rpEntry) const
+ {
+ return rpEntry.bDoQuery && rpEntry.nField == mnField;
+ }
+};
+
+struct FindUnused
+{
+ bool operator() (const ScQueryEntry& rpEntry) const
+ {
+ return !rpEntry.bDoQuery;
+ }
+};
+
+}
+
+ScQueryParamBase::const_iterator ScQueryParamBase::begin() const
+{
+ return m_Entries.begin();
+}
+
+ScQueryParamBase::const_iterator ScQueryParamBase::end() const
+{
+ return m_Entries.end();
+}
+
+ScQueryParamBase::ScQueryParamBase() :
+ eSearchType(utl::SearchParam::SearchType::Normal),
+ bHasHeader(true),
+ bByRow(true),
+ bInplace(true),
+ bCaseSens(false),
+ bDuplicate(false),
+ mbRangeLookup(false)
+{
+ m_Entries.resize(MAXQUERY);
+}
+
+ScQueryParamBase::ScQueryParamBase(const ScQueryParamBase& r) :
+ eSearchType(r.eSearchType), bHasHeader(r.bHasHeader), bByRow(r.bByRow), bInplace(r.bInplace),
+ bCaseSens(r.bCaseSens), bDuplicate(r.bDuplicate), mbRangeLookup(r.mbRangeLookup),
+ m_Entries(r.m_Entries)
+{
+}
+
+ScQueryParamBase& ScQueryParamBase::operator=(const ScQueryParamBase& r)
+{
+ if (this != &r)
+ {
+ eSearchType = r.eSearchType;
+ bHasHeader = r.bHasHeader;
+ bByRow = r.bByRow;
+ bInplace = r.bInplace;
+ bCaseSens = r.bCaseSens;
+ bDuplicate = r.bDuplicate;
+ mbRangeLookup = r.mbRangeLookup;
+ m_Entries = r.m_Entries;
+ }
+ return *this;
+}
+
+ScQueryParamBase::~ScQueryParamBase()
+{
+}
+
+bool ScQueryParamBase::IsValidFieldIndex() const
+{
+ return true;
+}
+
+SCSIZE ScQueryParamBase::GetEntryCount() const
+{
+ return m_Entries.size();
+}
+
+const ScQueryEntry& ScQueryParamBase::GetEntry(SCSIZE n) const
+{
+ return m_Entries[n];
+}
+
+ScQueryEntry& ScQueryParamBase::GetEntry(SCSIZE n)
+{
+ return m_Entries[n];
+}
+
+ScQueryEntry& ScQueryParamBase::AppendEntry()
+{
+ // Find the first unused entry.
+ EntriesType::iterator itr = std::find_if(
+ m_Entries.begin(), m_Entries.end(), FindUnused());
+
+ if (itr != m_Entries.end())
+ // Found!
+ return *itr;
+
+ // Add a new entry to the end.
+ m_Entries.push_back(ScQueryEntry());
+ return m_Entries.back();
+}
+
+ScQueryEntry* ScQueryParamBase::FindEntryByField(SCCOLROW nField, bool bNew)
+{
+ EntriesType::iterator itr = std::find_if(
+ m_Entries.begin(), m_Entries.end(), FindByField(nField));
+
+ if (itr != m_Entries.end())
+ {
+ // existing entry found!
+ return &*itr;
+ }
+
+ if (!bNew)
+ // no existing entry found, and we are not creating a new one.
+ return nullptr;
+
+ return &AppendEntry();
+}
+
+std::vector<ScQueryEntry*> ScQueryParamBase::FindAllEntriesByField(SCCOLROW nField)
+{
+ std::vector<ScQueryEntry*> aEntries;
+
+ auto fFind = FindByField(nField);
+
+ for (auto& rxEntry : m_Entries)
+ if (fFind(rxEntry))
+ aEntries.push_back(&rxEntry);
+
+ return aEntries;
+}
+
+bool ScQueryParamBase::RemoveEntryByField(SCCOLROW nField)
+{
+ EntriesType::iterator itr = std::find_if(
+ m_Entries.begin(), m_Entries.end(), FindByField(nField));
+ bool bRet = false;
+
+ if (itr != m_Entries.end())
+ {
+ m_Entries.erase(itr);
+ if (m_Entries.size() < MAXQUERY)
+ // Make sure that we have at least MAXQUERY number of entries at
+ // all times.
+ m_Entries.resize(MAXQUERY);
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void ScQueryParamBase::RemoveAllEntriesByField(SCCOLROW nField)
+{
+ while( RemoveEntryByField( nField ) ) {}
+}
+
+void ScQueryParamBase::Resize(size_t nNew)
+{
+ if (nNew < MAXQUERY)
+ nNew = MAXQUERY; // never less than MAXQUERY
+
+ m_Entries.resize(nNew);
+}
+
+void ScQueryParamBase::FillInExcelSyntax(
+ svl::SharedStringPool& rPool, const OUString& rCellStr, SCSIZE nIndex, SvNumberFormatter* pFormatter )
+{
+ if (nIndex >= m_Entries.size())
+ Resize(nIndex+1);
+
+ ScQueryEntry& rEntry = GetEntry(nIndex);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bByEmpty = false;
+ bool bByNonEmpty = false;
+
+ if (rCellStr.isEmpty())
+ rItem.maString = svl::SharedString::getEmptyString();
+ else
+ {
+ rEntry.bDoQuery = true;
+ // Operatoren herausfiltern
+ if (rCellStr[0] == '<')
+ {
+ if (rCellStr.getLength() > 1 && rCellStr[1] == '>')
+ {
+ rItem.maString = rPool.intern(rCellStr.copy(2));
+ rEntry.eOp = SC_NOT_EQUAL;
+ if (rCellStr.getLength() == 2)
+ bByNonEmpty = true;
+ }
+ else if (rCellStr.getLength() > 1 && rCellStr[1] == '=')
+ {
+ rItem.maString = rPool.intern(rCellStr.copy(2));
+ rEntry.eOp = SC_LESS_EQUAL;
+ }
+ else
+ {
+ rItem.maString = rPool.intern(rCellStr.copy(1));
+ rEntry.eOp = SC_LESS;
+ }
+ }
+ else if (rCellStr[0]== '>')
+ {
+ if (rCellStr.getLength() > 1 && rCellStr[1] == '=')
+ {
+ rItem.maString = rPool.intern(rCellStr.copy(2));
+ rEntry.eOp = SC_GREATER_EQUAL;
+ }
+ else
+ {
+ rItem.maString = rPool.intern(rCellStr.copy(1));
+ rEntry.eOp = SC_GREATER;
+ }
+ }
+ else
+ {
+ if (rCellStr[0] == '=')
+ {
+ rItem.maString = rPool.intern(rCellStr.copy(1));
+ if (rCellStr.getLength() == 1)
+ bByEmpty = true;
+ }
+ else
+ rItem.maString = rPool.intern(rCellStr);
+ rEntry.eOp = SC_EQUAL;
+ }
+ }
+
+ if (!pFormatter)
+ return;
+
+ /* TODO: pFormatter currently is also used as a flag whether matching
+ * empty cells with an empty string is triggered from the interpreter.
+ * This could be handled independently if all queries should support
+ * it, needs to be evaluated if that actually is desired. */
+
+ // Interpreter queries have only one query, also QueryByEmpty and
+ // QueryByNonEmpty rely on that.
+ if (nIndex != 0)
+ return;
+
+ // (empty = empty) is a match, and (empty <> not-empty) also is a
+ // match. (empty = 0) is not a match.
+ rItem.mbMatchEmpty = ((rEntry.eOp == SC_EQUAL && rItem.maString.isEmpty())
+ || (rEntry.eOp == SC_NOT_EQUAL && !rItem.maString.isEmpty()));
+
+ // SetQueryBy override item members with special values, so do this last.
+ if (bByEmpty)
+ rEntry.SetQueryByEmpty();
+ else if (bByNonEmpty)
+ rEntry.SetQueryByNonEmpty();
+ else
+ {
+ sal_uInt32 nFormat = 0;
+ bool bNumber = pFormatter->IsNumberFormat( rItem.maString.getString(), nFormat, rItem.mfVal);
+ rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ }
+}
+
+ScQueryParamTable::ScQueryParamTable() :
+ nCol1(0),nRow1(0),nCol2(0),nRow2(0),nTab(0)
+{
+}
+
+ScQueryParamTable::~ScQueryParamTable()
+{
+}
+
+ScQueryParam::ScQueryParam() :
+ bDestPers(true),
+ nDestTab(0),
+ nDestCol(0),
+ nDestRow(0)
+{
+ Clear();
+}
+
+ScQueryParam::ScQueryParam( const ScQueryParam& ) = default;
+
+ScQueryParam::ScQueryParam( const ScDBQueryParamInternal& r ) :
+ ScQueryParamBase(r),
+ ScQueryParamTable(r),
+ bDestPers(true),
+ nDestTab(0),
+ nDestCol(0),
+ nDestRow(0)
+{
+}
+
+ScQueryParam::~ScQueryParam()
+{
+}
+
+void ScQueryParam::Clear()
+{
+ nCol1=nCol2 = 0;
+ nRow1=nRow2 = 0;
+ nTab = SCTAB_MAX;
+ eSearchType = utl::SearchParam::SearchType::Normal;
+ bHasHeader = bCaseSens = false;
+ bInplace = bByRow = bDuplicate = true;
+
+ for (auto & itr : m_Entries)
+ {
+ itr.Clear();
+ }
+
+ ClearDestParams();
+}
+
+void ScQueryParam::ClearDestParams()
+{
+ bDestPers = true;
+ nDestTab = 0;
+ nDestCol = 0;
+ nDestRow = 0;
+}
+
+ScQueryParam& ScQueryParam::operator=( const ScQueryParam& ) = default;
+
+bool ScQueryParam::operator==( const ScQueryParam& rOther ) const
+{
+ bool bEqual = false;
+
+ // Are the number of queries equal?
+ SCSIZE nUsed = 0;
+ SCSIZE nOtherUsed = 0;
+ SCSIZE nEntryCount = GetEntryCount();
+ SCSIZE nOtherEntryCount = rOther.GetEntryCount();
+
+ while (nUsed<nEntryCount && m_Entries[nUsed].bDoQuery) ++nUsed;
+ while (nOtherUsed<nOtherEntryCount && rOther.m_Entries[nOtherUsed].bDoQuery)
+ ++nOtherUsed;
+
+ if ( (nUsed == nOtherUsed)
+ && (nCol1 == rOther.nCol1)
+ && (nRow1 == rOther.nRow1)
+ && (nCol2 == rOther.nCol2)
+ && (nRow2 == rOther.nRow2)
+ && (nTab == rOther.nTab)
+ && (bHasHeader == rOther.bHasHeader)
+ && (bByRow == rOther.bByRow)
+ && (bInplace == rOther.bInplace)
+ && (bCaseSens == rOther.bCaseSens)
+ && (eSearchType == rOther.eSearchType)
+ && (bDuplicate == rOther.bDuplicate)
+ && (bDestPers == rOther.bDestPers)
+ && (nDestTab == rOther.nDestTab)
+ && (nDestCol == rOther.nDestCol)
+ && (nDestRow == rOther.nDestRow) )
+ {
+ bEqual = true;
+ for ( SCSIZE i=0; i<nUsed && bEqual; i++ )
+ bEqual = m_Entries[i] == rOther.m_Entries[i];
+ }
+ return bEqual;
+}
+
+void ScQueryParam::MoveToDest()
+{
+ if (!bInplace)
+ {
+ SCCOL nDifX = nDestCol - nCol1;
+ SCROW nDifY = nDestRow - nRow1;
+ SCTAB nDifZ = nDestTab - nTab;
+
+ nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nDifX );
+ nRow1 = sal::static_int_cast<SCROW>( nRow1 + nDifY );
+ nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nDifX );
+ nRow2 = sal::static_int_cast<SCROW>( nRow2 + nDifY );
+ nTab = sal::static_int_cast<SCTAB>( nTab + nDifZ );
+ size_t n = m_Entries.size();
+ for (size_t i=0; i<n; i++)
+ m_Entries[i].nField += nDifX;
+
+ bInplace = true;
+ }
+ else
+ {
+ OSL_FAIL("MoveToDest, bInplace == TRUE");
+ }
+}
+
+ScDBQueryParamBase::ScDBQueryParamBase(DataType eType) :
+ mnField(-1),
+ mbSkipString(true),
+ meType(eType)
+{
+}
+
+ScDBQueryParamBase::~ScDBQueryParamBase()
+{
+}
+
+ScDBQueryParamInternal::ScDBQueryParamInternal() :
+ ScDBQueryParamBase(ScDBQueryParamBase::INTERNAL)
+{
+}
+
+ScDBQueryParamInternal::~ScDBQueryParamInternal()
+{
+}
+
+bool ScDBQueryParamInternal::IsValidFieldIndex() const
+{
+ return nCol1 <= mnField && mnField <= nCol2;
+}
+
+ScDBQueryParamMatrix::ScDBQueryParamMatrix() :
+ ScDBQueryParamBase(ScDBQueryParamBase::MATRIX)
+{
+}
+
+bool ScDBQueryParamMatrix::IsValidFieldIndex() const
+{
+ SCSIZE nC, nR;
+ mpMatrix->GetDimensions(nC, nR);
+ return 0 <= mnField && o3tl::make_unsigned(mnField) <= nC;
+}
+
+ScDBQueryParamMatrix::~ScDBQueryParamMatrix()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/rangecache.cxx b/sc/source/core/tool/rangecache.cxx
new file mode 100644
index 0000000000..f42d368436
--- /dev/null
+++ b/sc/source/core/tool/rangecache.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 <rangecache.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <brdcst.hxx>
+#include <queryevaluator.hxx>
+#include <queryparam.hxx>
+
+#include <sal/log.hxx>
+#include <svl/numformat.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+static bool needsDescending(ScQueryOp op)
+{
+ assert(op == SC_GREATER || op == SC_GREATER_EQUAL || op == SC_LESS || op == SC_LESS_EQUAL
+ || op == SC_EQUAL);
+ // We want all matching values to start in the sort order,
+ // since the data is searched from start until the last matching one.
+ return op == SC_GREATER || op == SC_GREATER_EQUAL;
+}
+
+static ScSortedRangeCache::ValueType toValueType(const ScQueryParam& param)
+{
+ assert(param.GetEntry(0).bDoQuery && !param.GetEntry(1).bDoQuery
+ && param.GetEntry(0).GetQueryItems().size() == 1);
+ assert(param.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString
+ || param.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue);
+ if (param.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByValue)
+ return ScSortedRangeCache::ValueType::Values;
+ return param.bCaseSens ? ScSortedRangeCache::ValueType::StringsCaseSensitive
+ : ScSortedRangeCache::ValueType::StringsCaseInsensitive;
+}
+
+ScSortedRangeCache::ScSortedRangeCache(ScDocument* pDoc, const ScRange& rRange,
+ const ScQueryParam& param, ScInterpreterContext* context,
+ bool invalid)
+ : maRange(rRange)
+ , mpDoc(pDoc)
+ , mValid(false)
+ , mValueType(toValueType(param))
+{
+ assert(maRange.aStart.Col() == maRange.aEnd.Col());
+ assert(maRange.aStart.Tab() == maRange.aEnd.Tab());
+ SCTAB nTab = maRange.aStart.Tab();
+ SCTAB nCol = maRange.aStart.Col();
+ assert(param.GetEntry(0).bDoQuery && !param.GetEntry(1).bDoQuery
+ && param.GetEntry(0).GetQueryItems().size() == 1);
+ const ScQueryEntry& entry = param.GetEntry(0);
+ const ScQueryEntry::Item& item = entry.GetQueryItem();
+ mQueryOp = entry.eOp;
+ mQueryType = item.meType;
+
+ if (invalid)
+ return; // leave empty
+
+ SCROW startRow = maRange.aStart.Row();
+ SCROW endRow = maRange.aEnd.Row();
+ SCCOL startCol = maRange.aStart.Col();
+ SCCOL endCol = maRange.aEnd.Col();
+ if (!item.mbMatchEmpty)
+ if (!pDoc->ShrinkToDataArea(nTab, startCol, startRow, endCol, endRow))
+ return; // no data cells, no need for a cache
+
+ if (mValueType == ValueType::Values)
+ {
+ struct RowData
+ {
+ SCROW row;
+ double value;
+ };
+ std::vector<RowData> rowData;
+ for (SCROW nRow = startRow; nRow <= endRow; ++nRow)
+ {
+ ScRefCellValue cell(pDoc->GetRefCellValue(ScAddress(nCol, nRow, nTab)));
+ if (ScQueryEvaluator::isQueryByValue(mQueryOp, mQueryType, cell))
+ rowData.push_back(RowData{ nRow, cell.getValue() });
+ else if (ScQueryEvaluator::isQueryByString(mQueryOp, mQueryType, cell))
+ {
+ // Make sure that other possibilities in the generic handling
+ // in ScQueryEvaluator::processEntry() do not alter the results.
+ // (ByTextColor/ByBackgroundColor are blocked by CanBeUsedForSorterCache(),
+ // but isQueryByString() is possible if the cell content is a string.
+ // And including strings here would be tricky, as the string comparison
+ // may possibly(?) be different than a numeric one. So check if the string
+ // may possibly match a number, by converting it to one. If it can't match,
+ // then it's fine to ignore it (and it can happen e.g. if the query uses
+ // the whole column which includes a textual header). But if it can possibly
+ // match, then bail out and leave it to the unoptimized case.
+ // TODO Maybe it would actually work to use the numeric value obtained here?
+ if (!ScQueryEvaluator::isMatchWholeCell(*pDoc, mQueryOp))
+ return; // substring matching cannot be sorted
+ sal_uInt32 format = 0;
+ double value;
+ if (context->GetFormatTable()->IsNumberFormat(cell.getString(pDoc), format, value))
+ return;
+ }
+ }
+ std::stable_sort(rowData.begin(), rowData.end(),
+ [](const RowData& d1, const RowData& d2) { return d1.value < d2.value; });
+ if (needsDescending(entry.eOp))
+ for (auto it = rowData.rbegin(); it != rowData.rend(); ++it)
+ mSortedRows.emplace_back(it->row);
+ else
+ for (const RowData& d : rowData)
+ mSortedRows.emplace_back(d.row);
+ }
+ else
+ {
+ struct RowData
+ {
+ SCROW row;
+ OUString string;
+ };
+ std::vector<RowData> rowData;
+ // Try to reuse as much ScQueryEvaluator code as possible, this should
+ // basically do the same comparisons.
+ assert(pDoc->FetchTable(nTab) != nullptr);
+ ScQueryEvaluator evaluator(*pDoc, *pDoc->FetchTable(nTab), param, context);
+ for (SCROW nRow = startRow; nRow <= endRow; ++nRow)
+ {
+ ScRefCellValue cell(pDoc->GetRefCellValue(ScAddress(nCol, nRow, nTab)));
+ // This should be used only with ScQueryEntry::ByString, and that
+ // means that ScQueryEvaluator::isQueryByString() should be the only
+ // possibility in the generic handling in ScQueryEvaluator::processEntry()
+ // (ByTextColor/ByBackgroundColor are blocked by CanBeUsedForSorterCache(),
+ // and isQueryByValue() is blocked by ScQueryEntry::ByString).
+ assert(mQueryType == ScQueryEntry::ByString);
+ assert(!ScQueryEvaluator::isQueryByValue(mQueryOp, mQueryType, cell));
+ if (ScQueryEvaluator::isQueryByString(mQueryOp, mQueryType, cell))
+ {
+ const svl::SharedString* sharedString = nullptr;
+ OUString string = evaluator.getCellString(cell, nRow, nCol, &sharedString);
+ if (sharedString)
+ string = sharedString->getString();
+ rowData.push_back(RowData{ nRow, string });
+ }
+ }
+ CollatorWrapper& collator
+ = ScGlobal::GetCollator(mValueType == ValueType::StringsCaseSensitive);
+ std::stable_sort(rowData.begin(), rowData.end(),
+ [&collator](const RowData& d1, const RowData& d2) {
+ return collator.compareString(d1.string, d2.string) < 0;
+ });
+ if (needsDescending(entry.eOp))
+ for (auto it = rowData.rbegin(); it != rowData.rend(); ++it)
+ mSortedRows.emplace_back(it->row);
+ else
+ for (const RowData& d : rowData)
+ mSortedRows.emplace_back(d.row);
+ }
+
+ mRowToIndex.resize(maRange.aEnd.Row() - maRange.aStart.Row() + 1, mSortedRows.max_size());
+ for (size_t i = 0; i < mSortedRows.size(); ++i)
+ mRowToIndex[mSortedRows[i] - maRange.aStart.Row()] = i;
+ mValid = true;
+}
+
+void ScSortedRangeCache::Notify(const SfxHint& rHint)
+{
+ if (!mpDoc->IsInDtorClear())
+ {
+ if (rHint.GetId() == SfxHintId::ScDataChanged || rHint.GetId() == SfxHintId::ScAreaChanged)
+ {
+ mpDoc->RemoveSortedRangeCache(*this);
+ // this ScSortedRangeCache is deleted by RemoveSortedRangeCache
+ }
+ }
+}
+
+ScSortedRangeCache::HashKey ScSortedRangeCache::makeHashKey(const ScRange& range,
+ const ScQueryParam& param)
+{
+ assert(param.GetEntry(0).bDoQuery && !param.GetEntry(1).bDoQuery
+ && param.GetEntry(0).GetQueryItems().size() == 1);
+ const ScQueryEntry& entry = param.GetEntry(0);
+ const ScQueryEntry::Item& item = entry.GetQueryItem();
+ return { range, toValueType(param), entry.eOp, item.meType };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/rangelst.cxx b/sc/source/core/tool/rangelst.cxx
new file mode 100644
index 0000000000..f84c92c7a7
--- /dev/null
+++ b/sc/source/core/tool/rangelst.cxx
@@ -0,0 +1,1531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <unotools/collatorwrapper.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <rangelst.hxx>
+#include <document.hxx>
+#include <refupdat.hxx>
+#include <compiler.hxx>
+#include <algorithm>
+#include <memory>
+
+using ::std::vector;
+using ::std::find_if;
+using ::std::for_each;
+using ::formula::FormulaGrammar;
+
+namespace {
+
+template<typename T>
+class FindEnclosingRange
+{
+public:
+ explicit FindEnclosingRange(const T& rTest) : mrTest(rTest) {}
+ bool operator() (const ScRange & rRange) const
+ {
+ return rRange.Contains(mrTest);
+ }
+private:
+ const T& mrTest;
+};
+
+template<typename T>
+class FindIntersectingRange
+{
+public:
+ explicit FindIntersectingRange(const T& rTest) : mrTest(rTest) {}
+ bool operator() (const ScRange & rRange) const
+ {
+ return rRange.Intersects(mrTest);
+ }
+private:
+ const T& mrTest;
+};
+
+class CountCells
+{
+public:
+ CountCells() : mnCellCount(0) {}
+
+ void operator() (const ScRange & r)
+ {
+ mnCellCount +=
+ sal_uInt64(r.aEnd.Col() - r.aStart.Col() + 1)
+ * sal_uInt64(r.aEnd.Row() - r.aStart.Row() + 1)
+ * sal_uInt64(r.aEnd.Tab() - r.aStart.Tab() + 1);
+ }
+
+ sal_uInt64 getCellCount() const { return mnCellCount; }
+
+private:
+ sal_uInt64 mnCellCount;
+};
+
+
+}
+
+// ScRangeList
+ScRangeList::~ScRangeList()
+{
+}
+
+ScRefFlags ScRangeList::Parse( std::u16string_view rStr, const ScDocument& rDoc,
+ formula::FormulaGrammar::AddressConvention eConv,
+ SCTAB nDefaultTab, sal_Unicode cDelimiter )
+{
+ if ( !rStr.empty() )
+ {
+ if (!cDelimiter)
+ cDelimiter = ScCompiler::GetNativeSymbolChar(ocSep);
+
+ ScRefFlags nResult = ~ScRefFlags::ZERO; // set all bits
+ ScRange aRange;
+ const SCTAB nTab = nDefaultTab;
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ const OUString aOne( o3tl::getToken(rStr, 0, cDelimiter, nPos ) );
+ aRange.aStart.SetTab( nTab ); // default tab if not specified
+ ScRefFlags nRes = aRange.ParseAny( aOne, rDoc, eConv );
+ ScRefFlags nEndRangeBits = ScRefFlags::COL2_VALID | ScRefFlags::ROW2_VALID | ScRefFlags::TAB2_VALID;
+ ScRefFlags nTmp1 = nRes & ScRefFlags::BITS;
+ ScRefFlags nTmp2 = nRes & nEndRangeBits;
+ // If we have a valid single range with
+ // any of the address bits we are interested in
+ // set - set the equiv end range bits
+ if ( (nRes & ScRefFlags::VALID ) && (nTmp1 != ScRefFlags::ZERO) && ( nTmp2 != nEndRangeBits ) )
+ applyStartToEndFlags(nRes, nTmp1);
+
+ if ( nRes & ScRefFlags::VALID )
+ push_back( aRange );
+ nResult &= nRes; // all common bits are preserved
+ }
+ while (nPos >= 0);
+
+ return nResult; // ScRefFlags::VALID set when all are OK
+ }
+ else
+ return ScRefFlags::ZERO;
+}
+
+void ScRangeList::Format( OUString& rStr, ScRefFlags nFlags, const ScDocument& rDoc,
+ formula::FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cDelimiter, bool bFullAddressNotation ) const
+{
+ if (!cDelimiter)
+ cDelimiter = ScCompiler::GetNativeSymbolChar(ocSep);
+
+ OUStringBuffer aBuf;
+ bool bFirst = true;
+ for( auto const & r : maRanges)
+ {
+ if (bFirst)
+ bFirst = false;
+ else
+ aBuf.append(OUStringChar(cDelimiter));
+ aBuf.append(r.Format(rDoc, nFlags, eConv, bFullAddressNotation));
+ }
+ rStr = aBuf.makeStringAndClear();
+}
+
+void ScRangeList::Join( const ScRange& rNewRange, bool bIsInList )
+{
+ if ( maRanges.empty() )
+ {
+ push_back( rNewRange );
+ return ;
+ }
+
+ // One common usage is to join ranges that actually are top to bottom
+ // appends but the caller doesn't exactly know about it, e.g. when invoked
+ // by ScMarkData::FillRangeListWithMarks(), check for this special case
+ // first and speed up things by not looping over all ranges for each range
+ // to be joined. We don't remember the exact encompassing range that would
+ // have to be updated on refupdates and insertions and deletions, instead
+ // remember just the maximum row used, even independently of the sheet.
+ // This satisfies most use cases.
+
+ if (!bIsInList)
+ {
+ const SCROW nRow1 = rNewRange.aStart.Row();
+ if (nRow1 > mnMaxRowUsed + 1)
+ {
+ push_back( rNewRange );
+ return;
+ }
+ else if (nRow1 == mnMaxRowUsed + 1)
+ {
+ // Check if we can simply enlarge the last range.
+ ScRange & rLast = maRanges.back();
+ if (rLast.aEnd.Row() + 1 == nRow1 &&
+ rLast.aStart.Col() == rNewRange.aStart.Col() && rLast.aEnd.Col() == rNewRange.aEnd.Col() &&
+ rLast.aStart.Tab() == rNewRange.aStart.Tab() && rLast.aEnd.Tab() == rNewRange.aEnd.Tab())
+ {
+ const SCROW nRow2 = rNewRange.aEnd.Row();
+ rLast.aEnd.SetRow( nRow2 );
+ mnMaxRowUsed = nRow2;
+ return;
+ }
+ }
+ }
+
+ bool bJoinedInput = false;
+ const ScRange* pOver = &rNewRange;
+
+Label_Range_Join:
+
+ assert(pOver);
+ const SCCOL nCol1 = pOver->aStart.Col();
+ const SCROW nRow1 = pOver->aStart.Row();
+ const SCCOL nTab1 = pOver->aStart.Tab();
+ const SCCOL nCol2 = pOver->aEnd.Col();
+ const SCROW nRow2 = pOver->aEnd.Row();
+ const SCCOL nTab2 = pOver->aEnd.Tab();
+
+ size_t nOverPos = std::numeric_limits<size_t>::max();
+ for (size_t i = 0; i < maRanges.size(); ++i)
+ {
+ ScRange & rRange = maRanges[i];
+ if ( &rRange == pOver )
+ {
+ nOverPos = i;
+ continue; // the same one, continue with the next
+ }
+ bool bJoined = false;
+ if ( rRange.Contains( *pOver ) )
+ { // range pOver included in or identical to range p
+ // XXX if we never used Append() before Join() we could remove
+ // pOver and end processing, but it is not guaranteed and there can
+ // be duplicates.
+ if ( bIsInList )
+ bJoined = true; // do away with range pOver
+ else
+ { // that was all then
+ bJoinedInput = true; // don't append
+ break; // for
+ }
+ }
+ else if ( pOver->Contains( rRange ) )
+ { // range rRange included in range pOver, make pOver the new range
+ rRange = *pOver;
+ bJoined = true;
+ }
+ if ( !bJoined && rRange.aStart.Tab() == nTab1 && rRange.aEnd.Tab() == nTab2 )
+ { // 2D
+ if ( rRange.aStart.Col() == nCol1 && rRange.aEnd.Col() == nCol2 )
+ {
+ if ( rRange.aStart.Row() <= nRow2+1 &&
+ rRange.aStart.Row() >= nRow1 )
+ { // top
+ rRange.aStart.SetRow( nRow1 );
+ bJoined = true;
+ }
+ else if ( rRange.aEnd.Row() >= nRow1-1 &&
+ rRange.aEnd.Row() <= nRow2 )
+ { // bottom
+ rRange.aEnd.SetRow( nRow2 );
+ bJoined = true;
+ }
+ }
+ else if ( rRange.aStart.Row() == nRow1 && rRange.aEnd.Row() == nRow2 )
+ {
+ if ( rRange.aStart.Col() <= nCol2+1 &&
+ rRange.aStart.Col() >= nCol1 )
+ { // left
+ rRange.aStart.SetCol( nCol1 );
+ bJoined = true;
+ }
+ else if ( rRange.aEnd.Col() >= nCol1-1 &&
+ rRange.aEnd.Col() <= nCol2 )
+ { // right
+ rRange.aEnd.SetCol( nCol2 );
+ bJoined = true;
+ }
+ }
+ }
+ if ( bJoined )
+ {
+ if ( bIsInList )
+ { // delete range pOver within the list
+ if (nOverPos != std::numeric_limits<size_t>::max())
+ {
+ Remove(nOverPos);
+ if (nOverPos < i)
+ --i;
+ }
+ else
+ {
+ for (size_t nOver = 0, nRanges = maRanges.size(); nOver < nRanges; ++nOver)
+ {
+ if (&maRanges[nOver] == pOver)
+ {
+ Remove(nOver);
+ break;
+ }
+ }
+ }
+ }
+ bJoinedInput = true;
+ pOver = &maRanges[i];
+ bIsInList = true;
+ goto Label_Range_Join;
+ }
+ }
+ if ( !bIsInList && !bJoinedInput )
+ push_back( rNewRange );
+}
+
+void ScRangeList::AddAndPartialCombine( const ScRange& rNewRange )
+{
+ if ( maRanges.empty() )
+ {
+ push_back( rNewRange );
+ return ;
+ }
+
+ // One common usage is to join ranges that actually are top to bottom
+ // appends but the caller doesn't exactly know about it, e.g. when invoked
+ // by ScMarkData::FillRangeListWithMarks(), check for this special case
+ // first and speed up things by not looping over all ranges for each range
+ // to be joined. We don't remember the exact encompassing range that would
+ // have to be updated on refupdates and insertions and deletions, instead
+ // remember just the maximum row used, even independently of the sheet.
+ // This satisfies most use cases.
+
+ const SCROW nRow1 = rNewRange.aStart.Row();
+ if (nRow1 > mnMaxRowUsed + 1)
+ {
+ push_back( rNewRange );
+ return;
+ }
+
+ // scan backwards 2 rows to see if we can merge with anything
+ auto it = maRanges.rbegin();
+ while (it != maRanges.rend() && it->aStart.Row() >= (rNewRange.aStart.Row() - 2))
+ {
+ // Check if we can simply enlarge this range.
+ ScRange & rLast = *it;
+ if (rLast.aEnd.Row() + 1 == nRow1 &&
+ rLast.aStart.Col() == rNewRange.aStart.Col() && rLast.aEnd.Col() == rNewRange.aEnd.Col() &&
+ rLast.aStart.Tab() == rNewRange.aStart.Tab() && rLast.aEnd.Tab() == rNewRange.aEnd.Tab())
+ {
+ const SCROW nRow2 = rNewRange.aEnd.Row();
+ rLast.aEnd.SetRow( nRow2 );
+ mnMaxRowUsed = std::max(mnMaxRowUsed, nRow2);
+ return;
+ }
+ ++it;
+ }
+
+ push_back( rNewRange );
+}
+
+bool ScRangeList::operator==( const ScRangeList& r ) const
+{
+ if ( this == &r )
+ return true;
+
+ return maRanges == r.maRanges;
+}
+
+bool ScRangeList::operator!=( const ScRangeList& r ) const
+{
+ return !operator==( r );
+}
+
+bool ScRangeList::UpdateReference(
+ UpdateRefMode eUpdateRefMode,
+ const ScDocument* pDoc,
+ const ScRange& rWhere,
+ SCCOL nDx,
+ SCROW nDy,
+ SCTAB nDz
+)
+{
+ if (maRanges.empty())
+ // No ranges to update. Bail out.
+ return false;
+
+ bool bChanged = false;
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+
+ if(eUpdateRefMode == URM_INSDEL)
+ {
+ // right now this only works for nTab1 == nTab2
+ if(nTab1 == nTab2)
+ {
+ if(nDx < 0)
+ {
+ bChanged = DeleteArea(nCol1+nDx, nRow1, nTab1, nCol1-1, nRow2, nTab2);
+ }
+ if(nDy < 0)
+ {
+ bChanged = DeleteArea(nCol1, nRow1+nDy, nTab1, nCol2, nRow1-1, nTab2);
+ }
+ SAL_WARN_IF(nDx < 0 && nDy < 0, "sc", "nDx and nDy are negative, check why");
+ }
+ }
+
+ if(maRanges.empty())
+ return true;
+
+ for (auto& rR : maRanges)
+ {
+ SCCOL theCol1;
+ SCROW theRow1;
+ SCTAB theTab1;
+ SCCOL theCol2;
+ SCROW theRow2;
+ SCTAB theTab2;
+ rR.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2,
+ nDx, nDy, nDz,
+ theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 )
+ != UR_NOTHING )
+ {
+ bChanged = true;
+ rR.aStart.Set( theCol1, theRow1, theTab1 );
+ rR.aEnd.Set( theCol2, theRow2, theTab2 );
+ if (mnMaxRowUsed < theRow2)
+ mnMaxRowUsed = theRow2;
+ }
+ }
+
+ if(eUpdateRefMode == URM_INSDEL)
+ {
+ if( nDx < 0 || nDy < 0 )
+ {
+ size_t n = maRanges.size();
+ for(size_t i = n-1; i > 0;)
+ {
+ Join(maRanges[i], true);
+ // Join() may merge and remove even more than one item, protect against it.
+ if(i >= maRanges.size())
+ i = maRanges.size()-1;
+ else
+ --i;
+ }
+ }
+ }
+
+ return bChanged;
+}
+
+void ScRangeList::InsertRow( SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, SCROW nRowPos, SCSIZE nSize )
+{
+ std::vector<ScRange> aNewRanges;
+ for(const auto & rRange : maRanges)
+ {
+ if(rRange.aStart.Tab() <= nTab && rRange.aEnd.Tab() >= nTab)
+ {
+ if(rRange.aEnd.Row() == nRowPos - 1 && (nColStart <= rRange.aEnd.Col() || nColEnd >= rRange.aStart.Col()))
+ {
+ SCCOL nNewRangeStartCol = std::max<SCCOL>(nColStart, rRange.aStart.Col());
+ SCCOL nNewRangeEndCol = std::min<SCCOL>(nColEnd, rRange.aEnd.Col());
+ SCROW nNewRangeStartRow = rRange.aEnd.Row() + 1;
+ SCROW nNewRangeEndRow = nRowPos + nSize - 1;
+ aNewRanges.emplace_back(nNewRangeStartCol, nNewRangeStartRow, nTab, nNewRangeEndCol,
+ nNewRangeEndRow, nTab);
+ if (mnMaxRowUsed < nNewRangeEndRow)
+ mnMaxRowUsed = nNewRangeEndRow;
+ }
+ }
+ }
+
+ for(const auto & rRange : aNewRanges)
+ {
+ if(!rRange.IsValid())
+ continue;
+
+ Join(rRange);
+ }
+}
+
+void ScRangeList::InsertCol( SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, SCCOL nColPos, SCSIZE nSize )
+{
+ std::vector<ScRange> aNewRanges;
+ for(const auto & rRange : maRanges)
+ {
+ if(rRange.aStart.Tab() <= nTab && rRange.aEnd.Tab() >= nTab)
+ {
+ if(rRange.aEnd.Col() == nColPos - 1 && (nRowStart <= rRange.aEnd.Row() || nRowEnd >= rRange.aStart.Row()))
+ {
+ SCROW nNewRangeStartRow = std::max<SCROW>(nRowStart, rRange.aStart.Row());
+ SCROW nNewRangeEndRow = std::min<SCROW>(nRowEnd, rRange.aEnd.Row());
+ SCCOL nNewRangeStartCol = rRange.aEnd.Col() + 1;
+ SCCOL nNewRangeEndCol = nColPos + nSize - 1;
+ aNewRanges.emplace_back(nNewRangeStartCol, nNewRangeStartRow, nTab, nNewRangeEndCol,
+ nNewRangeEndRow, nTab);
+ }
+ }
+ }
+
+ for(const auto & rRange : aNewRanges)
+ {
+ if(!rRange.IsValid())
+ continue;
+
+ Join(rRange);
+ }
+}
+
+void ScRangeList::InsertCol( SCTAB nTab, SCCOL nCol )
+{
+ std::vector<ScRange> aNewRanges;
+ for(const auto & rRange : maRanges)
+ {
+ if(rRange.aStart.Tab() <= nTab && rRange.aEnd.Tab() >= nTab)
+ {
+ if(rRange.aEnd.Col() == nCol - 1)
+ {
+ SCCOL nNewRangeStartCol = rRange.aEnd.Col() + 1;
+ SCCOL nNewRangeEndCol = nCol;
+ aNewRanges.emplace_back(nNewRangeStartCol, rRange.aStart.Row(), nTab, nNewRangeEndCol,
+ rRange.aEnd.Row(), nTab);
+ }
+ }
+ }
+
+ for(const auto & rRange : aNewRanges)
+ {
+ if(!rRange.IsValid())
+ continue;
+
+ Join(rRange);
+ }
+}
+
+namespace {
+
+/**
+ * Check if the deleting range cuts the test range exactly into a single
+ * piece.
+ *
+ * X = column ; Y = row
+ * +------+ +------+
+ * |xxxxxx| | |
+ * +------+ or +------+
+ * | | |xxxxxx|
+ * +------+ +------+
+ *
+ * X = row; Y = column
+ * +--+--+ +--+--+
+ * |xx| | | |xx|
+ * |xx| | or | |xx|
+ * |xx| | | |xx|
+ * +--+--+ +--+--+
+ * where xxx is the deleted region.
+ */
+template<typename X, typename Y>
+bool checkForOneRange(
+ X nDeleteX1, X nDeleteX2, Y nDeleteY1, Y nDeleteY2, X nX1, X nX2, Y nY1, Y nY2)
+{
+ return nDeleteX1 <= nX1 && nX2 <= nDeleteX2 && (nDeleteY1 <= nY1 || nY2 <= nDeleteY2);
+}
+
+bool handleOneRange( const ScRange& rDeleteRange, ScRange& r )
+{
+ const ScAddress& rDelStart = rDeleteRange.aStart;
+ const ScAddress& rDelEnd = rDeleteRange.aEnd;
+ ScAddress aPStart = r.aStart;
+ ScAddress aPEnd = r.aEnd;
+ SCCOL nDeleteCol1 = rDelStart.Col();
+ SCCOL nDeleteCol2 = rDelEnd.Col();
+ SCROW nDeleteRow1 = rDelStart.Row();
+ SCROW nDeleteRow2 = rDelEnd.Row();
+ SCCOL nCol1 = aPStart.Col();
+ SCCOL nCol2 = aPEnd.Col();
+ SCROW nRow1 = aPStart.Row();
+ SCROW nRow2 = aPEnd.Row();
+
+ if (checkForOneRange(nDeleteCol1, nDeleteCol2, nDeleteRow1, nDeleteRow2, nCol1, nCol2, nRow1, nRow2))
+ {
+ // Deleting range fully overlaps the column range. Adjust the row span.
+ if (nDeleteRow1 <= nRow1)
+ {
+ // +------+
+ // |xxxxxx|
+ // +------+
+ // | |
+ // +------+ (xxx) = deleted region
+
+ r.aStart.SetRow(nDeleteRow1+1);
+ return true;
+ }
+ else if (nRow2 <= nDeleteRow2)
+ {
+ // +------+
+ // | |
+ // +------+
+ // |xxxxxx|
+ // +------+ (xxx) = deleted region
+
+ r.aEnd.SetRow(nDeleteRow1-1);
+ return true;
+ }
+ }
+ else if (checkForOneRange(nDeleteRow1, nDeleteRow2, nDeleteCol1, nDeleteCol2, nRow1, nRow2, nCol1, nCol2))
+ {
+ // Deleting range fully overlaps the row range. Adjust the column span.
+ if (nDeleteCol1 <= nCol1)
+ {
+ // +--+--+
+ // |xx| |
+ // |xx| |
+ // |xx| |
+ // +--+--+ (xxx) = deleted region
+
+ r.aStart.SetCol(nDeleteCol2+1);
+ return true;
+ }
+ else if (nCol2 <= nDeleteCol2)
+ {
+ // +--+--+
+ // | |xx|
+ // | |xx|
+ // | |xx|
+ // +--+--+ (xxx) = deleted region
+
+ r.aEnd.SetCol(nDeleteCol1-1);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool handleTwoRanges( const ScRange& rDeleteRange, ScRange& r, std::vector<ScRange>& rNewRanges )
+{
+ const ScAddress& rDelStart = rDeleteRange.aStart;
+ const ScAddress& rDelEnd = rDeleteRange.aEnd;
+ ScAddress aPStart = r.aStart;
+ ScAddress aPEnd = r.aEnd;
+ SCCOL nDeleteCol1 = rDelStart.Col();
+ SCCOL nDeleteCol2 = rDelEnd.Col();
+ SCROW nDeleteRow1 = rDelStart.Row();
+ SCROW nDeleteRow2 = rDelEnd.Row();
+ SCCOL nCol1 = aPStart.Col();
+ SCCOL nCol2 = aPEnd.Col();
+ SCROW nRow1 = aPStart.Row();
+ SCROW nRow2 = aPEnd.Row();
+ SCTAB nTab = aPStart.Tab();
+
+ if (nCol1 < nDeleteCol1 && nDeleteCol1 <= nCol2 && nCol2 <= nDeleteCol2)
+ {
+ // column deleted : |-------|
+ // column original: |-------|
+ if (nRow1 < nDeleteRow1 && nDeleteRow1 <= nRow2 && nRow2 <= nDeleteRow2)
+ {
+ // row deleted: |------|
+ // row original: |------|
+ //
+ // +-------+
+ // | 1 |
+ // +---+---+---+
+ // | 2 |xxxxxxx|
+ // +---+xxxxxxx|
+ // |xxxxxxx|
+ // +-------+ (xxx) deleted region
+
+ ScRange aNewRange( nCol1, nDeleteRow1, nTab, nDeleteCol1-1, nRow2, nTab ); // 2
+ rNewRanges.push_back(aNewRange);
+
+ r.aEnd.SetRow(nDeleteRow1-1); // 1
+ return true;
+ }
+ else if (nRow1 <= nDeleteRow2 && nDeleteRow2 < nRow2 && nDeleteRow1 <= nRow1)
+ {
+ // row deleted: |------|
+ // row original: |------|
+ //
+ // +-------+
+ // |xxxxxxx|
+ // +---+xxxxxxx|
+ // | 1 |xxxxxxx|
+ // +---+---+---+
+ // | 2 | (xxx) deleted region
+ // +-------+
+
+ ScRange aNewRange( aPStart, ScAddress(nDeleteCol1-1, nRow2, nTab) ); // 1
+ rNewRanges.push_back(aNewRange);
+
+ r.aStart.SetRow(nDeleteRow2+1); // 2
+ return true;
+ }
+ }
+ else if (nCol1 <= nDeleteCol2 && nDeleteCol2 < nCol2 && nDeleteCol1 <= nCol1)
+ {
+ // column deleted : |-------|
+ // column original: |-------|
+ if (nRow1 < nDeleteRow1 && nDeleteRow1 <= nRow2 && nRow2 <= nDeleteRow2)
+ {
+ // row deleted: |------|
+ // row original: |------|
+ //
+ // +-------+
+ // | 1 |
+ // +-------+---+
+ // |xxxxxxx| 2 |
+ // |xxxxxxx+---+
+ // |xxxxxxx|
+ // +-------+
+ // (xxx) deleted region
+
+ ScRange aNewRange( ScAddress( nDeleteCol2+1, nDeleteRow1, nTab ), aPEnd ); // 2
+ rNewRanges.push_back(aNewRange);
+
+ r.aEnd.SetRow(nDeleteRow1-1); // 1
+ return true;
+ }
+ else if (nRow1 <= nDeleteRow2 && nDeleteRow2 < nRow2 && nDeleteRow1 <= nRow1)
+ {
+ // row deleted: |-------|
+ // row original: |--------|
+ //
+ // +-------+
+ // |xxxxxxx|
+ // |xxxxxxx+---+
+ // |xxxxxxx| 1 |
+ // +-------+---+
+ // | 2 |
+ // +-------+ (xxx) deleted region
+
+ ScRange aNewRange(nDeleteCol2+1, nRow1, nTab, nCol2, nDeleteRow2, nTab); // 1
+ rNewRanges.push_back(aNewRange);
+
+ r.aStart.SetRow(nDeleteRow2+1); // 2
+ return true;
+ }
+ }
+ else if (nRow1 < nDeleteRow1 && nDeleteRow2 < nRow2 && nDeleteCol1 <= nCol1 && nCol2 <= nDeleteCol2)
+ {
+ // +--------+
+ // | 1 |
+ // +--------+
+ // |xxxxxxxx| (xxx) deleted region
+ // +--------+
+ // | 2 |
+ // +--------+
+
+ ScRange aNewRange( aPStart, ScAddress(nCol2, nDeleteRow1-1, nTab) ); // 1
+ rNewRanges.push_back(aNewRange);
+
+ r.aStart.SetRow(nDeleteRow2+1); // 2
+ return true;
+ }
+ else if (nCol1 < nDeleteCol1 && nDeleteCol2 < nCol2 && nDeleteRow1 <= nRow1 && nRow2 <= nDeleteRow2)
+ {
+ // +---+-+---+
+ // | |x| |
+ // | |x| |
+ // | 1 |x| 2 | (xxx) deleted region
+ // | |x| |
+ // | |x| |
+ // +---+-+---+
+
+ ScRange aNewRange( aPStart, ScAddress(nDeleteCol1-1, nRow2, nTab) ); // 1
+ rNewRanges.push_back(aNewRange);
+
+ r.aStart.SetCol(nDeleteCol2+1); // 2
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Check if any of the following applies:
+ *
+ * X = column; Y = row
+ * +----------+ +----------+
+ * | | | |
+ * | +-------+---+ +--+-------+ |
+ * | |xxxxxxxxxxx| or |xxxxxxxxxx| |
+ * | +-------+---+ +--+-------+ |
+ * | | | |
+ * +----------+ +----------+
+ *
+ * X = row; Y = column
+ * +--+
+ * |xx|
+ * +---+xx+---+ +----------+
+ * | |xx| | | |
+ * | |xx| | or | +--+ |
+ * | +--+ | | |xx| |
+ * | | | |xx| |
+ * +----------+ +---+xx+---+
+ * |xx|
+ * +--+ (xxx) deleted region
+ */
+template<typename X, typename Y>
+bool checkForThreeRanges(
+ X nDeleteX1, X nDeleteX2, Y nDeleteY1, Y nDeleteY2, X nX1, X nX2, Y nY1, Y nY2)
+{
+ if (nX1 <= nDeleteX1 && nX2 <= nDeleteX2 && nY1 < nDeleteY1 && nDeleteY2 < nY2)
+ return true;
+
+ if (nDeleteX1 <= nX1 && nDeleteX2 <= nX2 && nY1 < nDeleteY1 && nDeleteY2 < nY2)
+ return true;
+
+ return false;
+}
+
+bool handleThreeRanges( const ScRange& rDeleteRange, ScRange& r, std::vector<ScRange>& rNewRanges )
+{
+ const ScAddress& rDelStart = rDeleteRange.aStart;
+ const ScAddress& rDelEnd = rDeleteRange.aEnd;
+ ScAddress aPStart = r.aStart;
+ ScAddress aPEnd = r.aEnd;
+ SCCOL nDeleteCol1 = rDelStart.Col();
+ SCCOL nDeleteCol2 = rDelEnd.Col();
+ SCROW nDeleteRow1 = rDelStart.Row();
+ SCROW nDeleteRow2 = rDelEnd.Row();
+ SCCOL nCol1 = aPStart.Col();
+ SCCOL nCol2 = aPEnd.Col();
+ SCROW nRow1 = aPStart.Row();
+ SCROW nRow2 = aPEnd.Row();
+ SCTAB nTab = aPStart.Tab();
+
+ if (checkForThreeRanges(nDeleteCol1, nDeleteCol2, nDeleteRow1, nDeleteRow2, nCol1, nCol2, nRow1, nRow2))
+ {
+ if (nCol1 < nDeleteCol1)
+ {
+ // +---+------+
+ // | | 2 |
+ // | +------+---+
+ // | 1 |xxxxxxxxxx|
+ // | +------+---+
+ // | | 3 |
+ // +---+------+
+
+ ScRange aNewRange(nDeleteCol1, nRow1, nTab, nCol2, nDeleteRow1-1, nTab); // 2
+ rNewRanges.push_back(aNewRange);
+
+ aNewRange = ScRange(ScAddress(nDeleteCol1, nDeleteRow2+1, nTab), aPEnd); // 3
+ rNewRanges.push_back(aNewRange);
+
+ r.aEnd.SetCol(nDeleteCol1-1); // 1
+ }
+ else
+ {
+ // +------+---+
+ // | 1 | |
+ // +---+------+ |
+ // |xxxxxxxxxx| 2 |
+ // +---+------+ |
+ // | 3 | |
+ // +------+---+
+
+ ScRange aNewRange(aPStart, ScAddress(nDeleteCol2, nDeleteRow1-1, nTab)); // 1
+ rNewRanges.push_back(aNewRange);
+
+ aNewRange = ScRange(nCol1, nDeleteRow2+1, nTab, nDeleteCol2, nRow2, nTab); // 3
+ rNewRanges.push_back(aNewRange);
+
+ r.aStart.SetCol(nDeleteCol2+1); // 2
+ }
+ return true;
+ }
+ else if (checkForThreeRanges(nDeleteRow1, nDeleteRow2, nDeleteCol1, nDeleteCol2, nRow1, nRow2, nCol1, nCol2))
+ {
+ if (nRow1 < nDeleteRow1)
+ {
+ // +----------+
+ // | 1 |
+ // +---+--+---+
+ // | |xx| |
+ // | 2 |xx| 3 |
+ // | |xx| |
+ // +---+xx+---+
+ // |xx|
+ // +--+
+
+ ScRange aNewRange(nCol1, nDeleteRow1, nTab, nDeleteCol1-1, nRow2, nTab); // 2
+ rNewRanges.push_back( aNewRange );
+
+ aNewRange = ScRange(ScAddress(nDeleteCol2+1, nDeleteRow1, nTab), aPEnd); // 3
+ rNewRanges.push_back( aNewRange );
+
+ r.aEnd.SetRow(nDeleteRow1-1); // 1
+ }
+ else
+ {
+ // +--+
+ // |xx|
+ // +---+xx+---+
+ // | 1 |xx| 2 |
+ // | |xx| |
+ // +---+--+---+
+ // | 3 |
+ // +----------+
+
+ ScRange aNewRange(aPStart, ScAddress(nDeleteCol1-1, nDeleteRow2, nTab)); // 1
+ rNewRanges.push_back(aNewRange);
+
+ aNewRange = ScRange(nDeleteCol2+1, nRow1, nTab, nCol2, nDeleteRow2, nTab); // 2
+ rNewRanges.push_back( aNewRange );
+
+ r.aStart.SetRow(nDeleteRow2+1); // 3
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool handleFourRanges( const ScRange& rDelRange, ScRange& r, std::vector<ScRange>& rNewRanges )
+{
+ const ScAddress& rDelStart = rDelRange.aStart;
+ const ScAddress& rDelEnd = rDelRange.aEnd;
+ ScAddress aPStart = r.aStart;
+ ScAddress aPEnd = r.aEnd;
+ SCCOL nDeleteCol1 = rDelStart.Col();
+ SCCOL nDeleteCol2 = rDelEnd.Col();
+ SCROW nDeleteRow1 = rDelStart.Row();
+ SCROW nDeleteRow2 = rDelEnd.Row();
+ SCCOL nCol1 = aPStart.Col();
+ SCCOL nCol2 = aPEnd.Col();
+ SCROW nRow1 = aPStart.Row();
+ SCROW nRow2 = aPEnd.Row();
+ SCTAB nTab = aPStart.Tab();
+
+ if (nCol1 < nDeleteCol1 && nDeleteCol2 < nCol2 && nRow1 < nDeleteRow1 && nDeleteRow2 < nRow2)
+ {
+
+ // +---------------+
+ // | 1 |
+ // +---+-------+---+
+ // | |xxxxxxx| |
+ // | 2 |xxxxxxx| 3 |
+ // | |xxxxxxx| |
+ // +---+-------+---+
+ // | 4 |
+ // +---------------+
+
+ ScRange aNewRange(ScAddress(nCol1, nDeleteRow2+1, nTab), aPEnd); // 4
+ rNewRanges.push_back( aNewRange );
+
+ aNewRange = ScRange(nCol1, nDeleteRow1, nTab, nDeleteCol1-1, nDeleteRow2, nTab); // 2
+ rNewRanges.push_back( aNewRange );
+
+ aNewRange = ScRange(nDeleteCol2+1, nDeleteRow1, nTab, nCol2, nDeleteRow2, nTab); // 3
+ rNewRanges.push_back( aNewRange );
+
+ r.aEnd.SetRow(nDeleteRow1-1); // 1
+
+ return true;
+ }
+
+ return false;
+}
+
+}
+
+bool ScRangeList::DeleteArea( SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2 )
+{
+ bool bChanged = false;
+ ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ for(size_t i = 0; i < maRanges.size();)
+ {
+ if(aRange.Contains(maRanges[i]))
+ {
+ Remove(i);
+ bChanged = true;
+ }
+ else
+ ++i;
+ }
+
+ std::vector<ScRange> aNewRanges;
+
+ for(auto & rRange : maRanges)
+ {
+ // we have two basic cases here:
+ // 1. Delete area and pRange intersect
+ // 2. Delete area and pRange are not intersecting
+ // checking for 2 and if true skip this range
+ if(!rRange.Intersects(aRange))
+ continue;
+
+ // We get between 1 and 4 ranges from the difference of the first with the second
+
+ // X either Col or Row and Y then the opposite
+ // r = deleteRange, p = entry from ScRangeList
+
+ // getting exactly one range is the simple case
+ // r.aStart.X() <= p.aStart.X() && r.aEnd.X() >= p.aEnd.X()
+ // && ( r.aStart.Y() <= p.aStart.Y() || r.aEnd.Y() >= r.aEnd.Y() )
+ if(handleOneRange( aRange, rRange ))
+ {
+ bChanged = true;
+ continue;
+ }
+
+ // getting two ranges
+ // r.aStart.X()
+ else if(handleTwoRanges( aRange, rRange, aNewRanges ))
+ {
+ bChanged = true;
+ continue;
+ }
+
+ // getting 3 ranges
+ // r.aStart.X() > p.aStart.X() && r.aEnd.X() >= p.aEnd.X()
+ // && r.aStart.Y() > p.aStart.Y() && r.aEnd.Y() < p.aEnd.Y()
+ // or
+ // r.aStart.X() <= p.aStart.X() && r.aEnd.X() < p.aEnd.X()
+ // && r.aStart.Y() > p.aStart.Y() && r.aEnd.Y() < p.aEnd.Y()
+ else if(handleThreeRanges( aRange, rRange, aNewRanges ))
+ {
+ bChanged = true;
+ continue;
+ }
+
+ // getting 4 ranges
+ // r.aStart.X() > p.aStart.X() && r.aEnd().X() < p.aEnd.X()
+ // && r.aStart.Y() > p.aStart.Y() && r.aEnd().Y() < p.aEnd.Y()
+ else if(handleFourRanges( aRange, rRange, aNewRanges ))
+ {
+ bChanged = true;
+ continue;
+ }
+ }
+ for(const auto & rRange : aNewRanges)
+ Join(rRange);
+
+ return bChanged;
+}
+
+const ScRange* ScRangeList::Find( const ScAddress& rAdr ) const
+{
+ auto itr = find_if(
+ maRanges.cbegin(), maRanges.cend(), FindEnclosingRange<ScAddress>(rAdr));
+ return itr == maRanges.end() ? nullptr : &*itr;
+}
+
+ScRange* ScRangeList::Find( const ScAddress& rAdr )
+{
+ auto itr = find_if(
+ maRanges.begin(), maRanges.end(), FindEnclosingRange<ScAddress>(rAdr));
+ return itr == maRanges.end() ? nullptr : &*itr;
+}
+
+ScRangeList::ScRangeList() : mnMaxRowUsed(-1) {}
+
+ScRangeList::ScRangeList( const ScRangeList& rList ) :
+ SvRefBase(rList),
+ maRanges(rList.maRanges),
+ mnMaxRowUsed(rList.mnMaxRowUsed)
+{
+}
+
+ScRangeList::ScRangeList(ScRangeList&& rList) noexcept :
+ maRanges(std::move(rList.maRanges)),
+ mnMaxRowUsed(rList.mnMaxRowUsed)
+{
+}
+
+ScRangeList::ScRangeList( const ScRange& rRange ) :
+ mnMaxRowUsed(-1)
+{
+ maRanges.reserve(1);
+ push_back(rRange);
+}
+
+ScRangeList& ScRangeList::operator=(const ScRangeList& rList)
+{
+ maRanges = rList.maRanges;
+ mnMaxRowUsed = rList.mnMaxRowUsed;
+ return *this;
+}
+
+ScRangeList& ScRangeList::operator=(ScRangeList&& rList) noexcept
+{
+ maRanges = std::move(rList.maRanges);
+ mnMaxRowUsed = rList.mnMaxRowUsed;
+ return *this;
+}
+
+bool ScRangeList::Intersects( const ScRange& rRange ) const
+{
+ return std::any_of(maRanges.begin(), maRanges.end(), FindIntersectingRange<ScRange>(rRange));
+}
+
+bool ScRangeList::Contains( const ScRange& rRange ) const
+{
+ return std::any_of(maRanges.begin(), maRanges.end(), FindEnclosingRange<ScRange>(rRange));
+}
+
+sal_uInt64 ScRangeList::GetCellCount() const
+{
+ CountCells func;
+ return for_each(maRanges.begin(), maRanges.end(), func).getCellCount();
+}
+
+void ScRangeList::Remove(size_t nPos)
+{
+ if (maRanges.size() <= nPos)
+ // Out-of-bound condition. Bail out.
+ return;
+ maRanges.erase(maRanges.begin() + nPos);
+}
+
+void ScRangeList::RemoveAll()
+{
+ maRanges.clear();
+ mnMaxRowUsed = -1;
+}
+
+ScRange ScRangeList::Combine() const
+{
+ if (maRanges.empty())
+ return ScRange();
+
+ auto itr = maRanges.cbegin(), itrEnd = maRanges.cend();
+ ScRange aRet = *itr;
+ ++itr;
+ for (; itr != itrEnd; ++itr)
+ {
+ const ScRange& r = *itr;
+ SCROW nRow1 = r.aStart.Row(), nRow2 = r.aEnd.Row();
+ SCCOL nCol1 = r.aStart.Col(), nCol2 = r.aEnd.Col();
+ SCTAB nTab1 = r.aStart.Tab(), nTab2 = r.aEnd.Tab();
+ if (aRet.aStart.Row() > nRow1)
+ aRet.aStart.SetRow(nRow1);
+ if (aRet.aStart.Col() > nCol1)
+ aRet.aStart.SetCol(nCol1);
+ if (aRet.aStart.Tab() > nTab1)
+ aRet.aStart.SetTab(nTab1);
+ if (aRet.aEnd.Row() < nRow2)
+ aRet.aEnd.SetRow(nRow2);
+ if (aRet.aEnd.Col() < nCol2)
+ aRet.aEnd.SetCol(nCol2);
+ if (aRet.aEnd.Tab() < nTab2)
+ aRet.aEnd.SetTab(nTab2);
+ }
+ return aRet;
+}
+
+void ScRangeList::push_back(const ScRange & r)
+{
+ maRanges.push_back(r);
+ if (mnMaxRowUsed < r.aEnd.Row())
+ mnMaxRowUsed = r.aEnd.Row();
+}
+
+void ScRangeList::swap( ScRangeList& r )
+{
+ maRanges.swap(r.maRanges);
+ std::swap(mnMaxRowUsed, r.mnMaxRowUsed);
+}
+
+ScAddress ScRangeList::GetTopLeftCorner() const
+{
+ if(empty())
+ return ScAddress();
+
+ ScAddress const * pAddr = &maRanges[0].aStart;
+ for(size_t i = 1, n = size(); i < n; ++i)
+ {
+ if(maRanges[i].aStart < *pAddr)
+ pAddr = &maRanges[i].aStart;
+ }
+
+ return *pAddr;
+}
+
+ScRangeList ScRangeList::GetIntersectedRange(const ScRange& rRange) const
+{
+ ScRangeList aReturn;
+ for(auto& rR : maRanges)
+ {
+ if(rR.Intersects(rRange))
+ {
+ SCCOL nColStart1, nColEnd1, nColStart2, nColEnd2;
+ SCROW nRowStart1, nRowEnd1, nRowStart2, nRowEnd2;
+ SCTAB nTabStart1, nTabEnd1, nTabStart2, nTabEnd2;
+ rR.GetVars(nColStart1, nRowStart1, nTabStart1,
+ nColEnd1, nRowEnd1, nTabEnd1);
+ rRange.GetVars(nColStart2, nRowStart2, nTabStart2,
+ nColEnd2, nRowEnd2, nTabEnd2);
+
+ ScRange aNewRange(std::max<SCCOL>(nColStart1, nColStart2), std::max<SCROW>(nRowStart1, nRowStart2),
+ std::max<SCTAB>(nTabStart1, nTabStart2), std::min<SCCOL>(nColEnd1, nColEnd2),
+ std::min<SCROW>(nRowEnd1, nRowEnd2), std::min<SCTAB>(nTabEnd1, nTabEnd2));
+ aReturn.Join(aNewRange);
+ }
+ }
+
+ return aReturn;
+}
+
+// ScRangePairList
+ScRangePairList::~ScRangePairList()
+{
+}
+
+void ScRangePairList::Remove(size_t nPos)
+{
+ if (maPairs.size() <= nPos)
+ // Out-of-bound condition. Bail out.
+ return;
+ maPairs.erase(maPairs.begin() + nPos);
+}
+
+void ScRangePairList::Remove( const ScRangePair & rAdr)
+{
+ auto itr = std::find_if(maPairs.begin(), maPairs.end(), [&rAdr](const ScRangePair& rPair) { return &rAdr == &rPair; });
+ if (itr != maPairs.end())
+ {
+ maPairs.erase( itr );
+ return;
+ }
+ assert(false);
+}
+
+ScRangePair & ScRangePairList::operator [](size_t idx)
+{
+ return maPairs[idx];
+}
+
+const ScRangePair & ScRangePairList::operator [](size_t idx) const
+{
+ return maPairs[idx];
+}
+
+size_t ScRangePairList::size() const
+{
+ return maPairs.size();
+}
+
+void ScRangePairList::UpdateReference( UpdateRefMode eUpdateRefMode,
+ const ScDocument* pDoc, const ScRange& rWhere,
+ SCCOL nDx, SCROW nDy, SCTAB nDz )
+{
+ if ( maPairs.empty() )
+ return;
+
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ for (ScRangePair & rR : maPairs)
+ {
+ for ( sal_uInt16 j=0; j<2; j++ )
+ {
+ ScRange& rRange = rR.GetRange(j);
+ SCCOL theCol1;
+ SCROW theRow1;
+ SCTAB theTab1;
+ SCCOL theCol2;
+ SCROW theRow2;
+ SCTAB theTab2;
+ rRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
+ nCol1, nRow1, nTab1, nCol2, nRow2, nTab2,
+ nDx, nDy, nDz,
+ theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 )
+ != UR_NOTHING )
+ {
+ rRange.aStart.Set( theCol1, theRow1, theTab1 );
+ rRange.aEnd.Set( theCol2, theRow2, theTab2 );
+ }
+ }
+ }
+}
+
+// Delete entries that have the labels (first range) on nTab
+void ScRangePairList::DeleteOnTab( SCTAB nTab )
+{
+ std::erase_if(maPairs,
+ [&nTab](const ScRangePair& rR) {
+ const ScRange & rRange = rR.GetRange(0);
+ return (rRange.aStart.Tab() == nTab) && (rRange.aEnd.Tab() == nTab);
+ });
+}
+
+ScRangePair* ScRangePairList::Find( const ScAddress& rAdr )
+{
+ for (ScRangePair & rR : maPairs)
+ {
+ if ( rR.GetRange(0).Contains( rAdr ) )
+ return &rR;
+ }
+ return nullptr;
+}
+
+ScRangePair* ScRangePairList::Find( const ScRange& rRange )
+{
+ for (ScRangePair & rR : maPairs)
+ {
+ if ( rR.GetRange(0) == rRange )
+ return &rR;
+ }
+ return nullptr;
+}
+
+ScRangePairList* ScRangePairList::Clone() const
+{
+ ScRangePairList* pNew = new ScRangePairList;
+ for (const ScRangePair & rR : maPairs)
+ {
+ pNew->Append( rR );
+ }
+ return pNew;
+}
+
+namespace {
+
+class ScRangePairList_sortNameCompare
+{
+public:
+ ScRangePairList_sortNameCompare(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+ bool operator()( const ScRangePair *ps1, const ScRangePair* ps2 ) const
+ {
+ const ScAddress& rStartPos1 = ps1->GetRange(0).aStart;
+ const ScAddress& rStartPos2 = ps2->GetRange(0).aStart;
+ OUString aStr1, aStr2;
+ sal_Int32 nComp;
+ if ( rStartPos1.Tab() == rStartPos2.Tab() )
+ nComp = 0;
+ else
+ {
+ mrDoc.GetName( rStartPos1.Tab(), aStr1 );
+ mrDoc.GetName( rStartPos2.Tab(), aStr2 );
+ nComp = ScGlobal::GetCollator().compareString( aStr1, aStr2 );
+ }
+ if (nComp < 0)
+ {
+ return true; // -1;
+ }
+ else if (nComp > 0)
+ {
+ return false; // 1;
+ }
+
+ // equal tabs
+ if ( rStartPos1.Col() < rStartPos2.Col() )
+ return true; // -1;
+ if ( rStartPos1.Col() > rStartPos2.Col() )
+ return false; // 1;
+ // equal cols
+ if ( rStartPos1.Row() < rStartPos2.Row() )
+ return true; // -1;
+ if ( rStartPos1.Row() > rStartPos2.Row() )
+ return false; // 1;
+
+ // first corner equal, second corner
+ const ScAddress& rEndPos1 = ps1->GetRange(0).aEnd;
+ const ScAddress& rEndPos2 = ps2->GetRange(0).aEnd;
+ if ( rEndPos1.Tab() == rEndPos2.Tab() )
+ nComp = 0;
+ else
+ {
+ mrDoc.GetName( rEndPos1.Tab(), aStr1 );
+ mrDoc.GetName( rEndPos2.Tab(), aStr2 );
+ nComp = ScGlobal::GetCollator().compareString( aStr1, aStr2 );
+ }
+ if (nComp < 0)
+ {
+ return true; // -1;
+ }
+ else if (nComp > 0)
+ {
+ return false; // 1;
+ }
+
+ // equal tabs
+ if ( rEndPos1.Col() < rEndPos2.Col() )
+ return true; // -1;
+ if ( rEndPos1.Col() > rEndPos2.Col() )
+ return false; // 1;
+ // equal cols
+ if ( rEndPos1.Row() < rEndPos2.Row() )
+ return true; // -1;
+ if ( rEndPos1.Row() > rEndPos2.Row() )
+ return false; // 1;
+
+ return false;
+ }
+private:
+ ScDocument& mrDoc;
+};
+
+}
+
+void ScRangePairList::Join( const ScRangePair& r, bool bIsInList )
+{
+ if ( maPairs.empty() )
+ {
+ Append( r );
+ return ;
+ }
+
+ bool bJoinedInput = false;
+ const ScRangePair* pOver = &r;
+
+Label_RangePair_Join:
+
+ assert(pOver);
+ const ScRange& r1 = pOver->GetRange(0);
+ const ScRange& r2 = pOver->GetRange(1);
+ const SCCOL nCol1 = r1.aStart.Col();
+ const SCROW nRow1 = r1.aStart.Row();
+ const SCTAB nTab1 = r1.aStart.Tab();
+ const SCCOL nCol2 = r1.aEnd.Col();
+ const SCROW nRow2 = r1.aEnd.Row();
+ const SCTAB nTab2 = r1.aEnd.Tab();
+
+ size_t nOverPos = std::numeric_limits<size_t>::max();
+ for (size_t i = 0; i < maPairs.size(); ++i)
+ {
+ ScRangePair & rPair = maPairs[ i ];
+ if ( &rPair == pOver )
+ {
+ nOverPos = i;
+ continue; // the same one, continue with the next
+ }
+ bool bJoined = false;
+ ScRange& rp1 = rPair.GetRange(0);
+ ScRange& rp2 = rPair.GetRange(1);
+ if ( rp2 == r2 )
+ { // only if Range2 is equal
+ if ( rp1.Contains( r1 ) )
+ { // RangePair pOver included in or identical to RangePair p
+ if ( bIsInList )
+ bJoined = true; // do away with RangePair pOver
+ else
+ { // that was all then
+ bJoinedInput = true; // don't append
+ break; // for
+ }
+ }
+ else if ( r1.Contains( rp1 ) )
+ { // RangePair p included in RangePair pOver, make pOver the new RangePair
+ rPair = *pOver;
+ bJoined = true;
+ }
+ }
+ if ( !bJoined && rp1.aStart.Tab() == nTab1 && rp1.aEnd.Tab() == nTab2
+ && rp2.aStart.Tab() == r2.aStart.Tab()
+ && rp2.aEnd.Tab() == r2.aEnd.Tab() )
+ { // 2D, Range2 must be located side-by-side just like Range1
+ if ( rp1.aStart.Col() == nCol1 && rp1.aEnd.Col() == nCol2
+ && rp2.aStart.Col() == r2.aStart.Col()
+ && rp2.aEnd.Col() == r2.aEnd.Col() )
+ {
+ if ( rp1.aStart.Row() == nRow2+1
+ && rp2.aStart.Row() == r2.aEnd.Row()+1 )
+ { // top
+ rp1.aStart.SetRow( nRow1 );
+ rp2.aStart.SetRow( r2.aStart.Row() );
+ bJoined = true;
+ }
+ else if ( rp1.aEnd.Row() == nRow1-1
+ && rp2.aEnd.Row() == r2.aStart.Row()-1 )
+ { // bottom
+ rp1.aEnd.SetRow( nRow2 );
+ rp2.aEnd.SetRow( r2.aEnd.Row() );
+ bJoined = true;
+ }
+ }
+ else if ( rp1.aStart.Row() == nRow1 && rp1.aEnd.Row() == nRow2
+ && rp2.aStart.Row() == r2.aStart.Row()
+ && rp2.aEnd.Row() == r2.aEnd.Row() )
+ {
+ if ( rp1.aStart.Col() == nCol2+1
+ && rp2.aStart.Col() == r2.aEnd.Col()+1 )
+ { // left
+ rp1.aStart.SetCol( nCol1 );
+ rp2.aStart.SetCol( r2.aStart.Col() );
+ bJoined = true;
+ }
+ else if ( rp1.aEnd.Col() == nCol1-1
+ && rp2.aEnd.Col() == r2.aEnd.Col()-1 )
+ { // right
+ rp1.aEnd.SetCol( nCol2 );
+ rp2.aEnd.SetCol( r2.aEnd.Col() );
+ bJoined = true;
+ }
+ }
+ }
+ if ( bJoined )
+ {
+ if ( bIsInList )
+ { // delete RangePair pOver within the list
+ if (nOverPos != std::numeric_limits<size_t>::max())
+ {
+ Remove(nOverPos);
+ if (nOverPos < i)
+ --i;
+ }
+ else
+ {
+ for (size_t nOver = 0, nRangePairs = maPairs.size(); nOver < nRangePairs; ++nOver)
+ {
+ if (&maPairs[nOver] == pOver)
+ {
+ maPairs.erase(maPairs.begin() + nOver);
+ break;
+ }
+ }
+ assert(false);
+ }
+ }
+ bJoinedInput = true;
+ pOver = &maPairs[i];
+ bIsInList = true;
+ goto Label_RangePair_Join;
+ }
+ }
+ if ( !bIsInList && !bJoinedInput )
+ Append( r );
+}
+
+std::vector<const ScRangePair*> ScRangePairList::CreateNameSortedArray( ScDocument& rDoc ) const
+{
+ std::vector<const ScRangePair*> aSortedVec(maPairs.size());
+ size_t i = 0;
+ for ( auto const & rPair : maPairs)
+ {
+ aSortedVec[i++] = &rPair;
+ }
+
+ std::sort( aSortedVec.begin(), aSortedVec.end(), ScRangePairList_sortNameCompare(rDoc) );
+
+ return aSortedVec;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/rangenam.cxx b/sc/source/core/tool/rangenam.cxx
new file mode 100644
index 0000000000..051b0a5e77
--- /dev/null
+++ b/sc/source/core/tool/rangenam.cxx
@@ -0,0 +1,900 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <memory>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <osl/diagnose.h>
+
+#include <token.hxx>
+#include <tokenarray.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <global.hxx>
+#include <compiler.hxx>
+#include <refupdat.hxx>
+#include <document.hxx>
+#include <refupdatecontext.hxx>
+#include <tokenstringcontext.hxx>
+
+#include <formula/errorcodes.hxx>
+
+using namespace formula;
+using ::std::pair;
+
+// ScRangeData
+
+ScRangeData::ScRangeData( ScDocument& rDok,
+ const OUString& rName,
+ const OUString& rSymbol,
+ const ScAddress& rAddress,
+ Type nType,
+ const FormulaGrammar::Grammar eGrammar ) :
+ aName ( rName ),
+ aUpperName ( ScGlobal::getCharClass().uppercase( rName ) ),
+ aPos ( rAddress ),
+ eType ( nType ),
+ rDoc ( rDok ),
+ eTempGrammar( eGrammar ),
+ nIndex ( 0 ),
+ bModified ( false )
+{
+ if (!rSymbol.isEmpty())
+ {
+ // Let the compiler set an error on unknown names for a subsequent
+ // CompileUnresolvedXML().
+ const bool bImporting = rDoc.IsImportingXML();
+ CompileRangeData( rSymbol, bImporting);
+ if (bImporting)
+ rDoc.CheckLinkFormulaNeedingCheck( *pCode);
+ }
+ else
+ {
+ // #i63513#/#i65690# don't leave pCode as NULL.
+ // Copy ctor default-constructs pCode if it was NULL, so it's initialized here, too,
+ // to ensure same behavior if unnecessary copying is left out.
+
+ pCode.reset( new ScTokenArray(rDoc) );
+ pCode->SetFromRangeName(true);
+ }
+}
+
+ScRangeData::ScRangeData( ScDocument& rDok,
+ const OUString& rName,
+ const ScTokenArray& rArr,
+ const ScAddress& rAddress,
+ Type nType ) :
+ aName ( rName ),
+ aUpperName ( ScGlobal::getCharClass().uppercase( rName ) ),
+ pCode ( new ScTokenArray( rArr ) ),
+ aPos ( rAddress ),
+ eType ( nType ),
+ rDoc ( rDok ),
+ eTempGrammar( FormulaGrammar::GRAM_UNSPECIFIED ),
+ nIndex ( 0 ),
+ bModified ( false )
+{
+ pCode->SetFromRangeName(true);
+ InitCode();
+}
+
+ScRangeData::ScRangeData( ScDocument& rDok,
+ const OUString& rName,
+ const ScAddress& rTarget ) :
+ aName ( rName ),
+ aUpperName ( ScGlobal::getCharClass().uppercase( rName ) ),
+ pCode ( new ScTokenArray(rDok) ),
+ aPos ( rTarget ),
+ eType ( Type::Name ),
+ rDoc ( rDok ),
+ eTempGrammar( FormulaGrammar::GRAM_UNSPECIFIED ),
+ nIndex ( 0 ),
+ bModified ( false )
+{
+ ScSingleRefData aRefData;
+ aRefData.InitAddress( rTarget );
+ aRefData.SetFlag3D( true );
+ pCode->AddSingleReference( aRefData );
+ pCode->SetFromRangeName(true);
+ ScCompiler aComp( rDoc, aPos, *pCode, rDoc.GetGrammar() );
+ aComp.CompileTokenArray();
+ if ( pCode->GetCodeError() == FormulaError::NONE )
+ eType |= Type::AbsPos;
+}
+
+ScRangeData::ScRangeData(const ScRangeData& rScRangeData, ScDocument* pDocument, const ScAddress* pPos) :
+ aName (rScRangeData.aName),
+ aUpperName (rScRangeData.aUpperName),
+ pCode (rScRangeData.pCode ? rScRangeData.pCode->Clone().release() : new ScTokenArray(*pDocument)), // make real copy (not copy-ctor)
+ aPos (pPos ? *pPos : rScRangeData.aPos),
+ eType (rScRangeData.eType),
+ rDoc (pDocument ? *pDocument : rScRangeData.rDoc),
+ eTempGrammar(rScRangeData.eTempGrammar),
+ nIndex (rScRangeData.nIndex),
+ bModified (rScRangeData.bModified)
+{
+ pCode->SetFromRangeName(true);
+}
+
+ScRangeData::~ScRangeData()
+{
+}
+
+void ScRangeData::CompileRangeData( const OUString& rSymbol, bool bSetError )
+{
+ if (eTempGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
+ {
+ OSL_FAIL( "ScRangeData::CompileRangeData: unspecified grammar");
+ // Anything is almost as bad as this, but we might have the best choice
+ // if not loading documents.
+ eTempGrammar = FormulaGrammar::GRAM_NATIVE;
+ }
+
+ ScCompiler aComp( rDoc, aPos, eTempGrammar );
+ if (bSetError)
+ aComp.SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_NO_BREAK);
+ pCode = aComp.CompileString( rSymbol );
+ pCode->SetFromRangeName(true);
+ if( pCode->GetCodeError() != FormulaError::NONE )
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ FormulaToken* p = aIter.GetNextReference();
+ if( p )
+ {
+ // first token is a reference
+ /* FIXME: wouldn't that need a check if it's exactly one reference? */
+ if( p->GetType() == svSingleRef )
+ eType = eType | Type::AbsPos;
+ else
+ eType = eType | Type::AbsArea;
+ }
+ // For manual input set an error for an incomplete formula.
+ if (!rDoc.IsImportingXML())
+ {
+ aComp.CompileTokenArray();
+ pCode->DelRPN();
+ }
+}
+
+void ScRangeData::CompileUnresolvedXML( sc::CompileFormulaContext& rCxt )
+{
+ if (pCode->GetCodeError() == FormulaError::NoName)
+ {
+ // Reconstruct the symbol/formula and then recompile.
+ OUString aSymbol;
+ rCxt.setGrammar(eTempGrammar);
+ ScCompiler aComp(rCxt, aPos, *pCode);
+ aComp.CreateStringFromTokenArray( aSymbol);
+ // Don't let the compiler set an error for unknown names on final
+ // compile, errors are handled by the interpreter thereafter.
+ CompileRangeData( aSymbol, false);
+ rCxt.getDoc().CheckLinkFormulaNeedingCheck( *pCode);
+ }
+}
+
+#if DEBUG_FORMULA_COMPILER
+void ScRangeData::Dump() const
+{
+ cout << "-- ScRangeData" << endl;
+ cout << " name: " << aName << endl;
+ cout << " ref position: (col=" << aPos.Col() << ", row=" << aPos.Row() << ", sheet=" << aPos.Tab() << ")" << endl;
+
+ if (pCode)
+ pCode->Dump();
+}
+#endif
+
+void ScRangeData::GuessPosition()
+{
+ // set a position that allows "absoluting" of all relative references
+ // in CalcAbsIfRel without errors
+
+ OSL_ENSURE(aPos == ScAddress(), "position will go lost now");
+
+ SCCOL nMinCol = 0;
+ SCROW nMinRow = 0;
+ SCTAB nMinTab = 0;
+
+ formula::FormulaToken* t;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() && rRef1.Col() < nMinCol )
+ nMinCol = rRef1.Col();
+ if ( rRef1.IsRowRel() && rRef1.Row() < nMinRow )
+ nMinRow = rRef1.Row();
+ if ( rRef1.IsTabRel() && rRef1.Tab() < nMinTab )
+ nMinTab = rRef1.Tab();
+
+ if ( t->GetType() == svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ if ( rRef2.IsColRel() && rRef2.Col() < nMinCol )
+ nMinCol = rRef2.Col();
+ if ( rRef2.IsRowRel() && rRef2.Row() < nMinRow )
+ nMinRow = rRef2.Row();
+ if ( rRef2.IsTabRel() && rRef2.Tab() < nMinTab )
+ nMinTab = rRef2.Tab();
+ }
+ }
+
+ aPos = ScAddress( static_cast<SCCOL>(-nMinCol), static_cast<SCROW>(-nMinRow), static_cast<SCTAB>(-nMinTab) );
+}
+
+OUString ScRangeData::GetSymbol( const FormulaGrammar::Grammar eGrammar ) const
+{
+ ScCompiler aComp(rDoc, aPos, *pCode, eGrammar);
+ OUString symbol;
+ aComp.CreateStringFromTokenArray( symbol );
+ return symbol;
+}
+
+OUString ScRangeData::GetSymbol( const ScAddress& rPos, const FormulaGrammar::Grammar eGrammar ) const
+{
+ OUString aStr;
+ ScCompiler aComp(rDoc, rPos, *pCode, eGrammar);
+ aComp.CreateStringFromTokenArray( aStr );
+ return aStr;
+}
+
+void ScRangeData::UpdateSymbol( OUStringBuffer& rBuffer, const ScAddress& rPos )
+{
+ ScTokenArray aTemp( pCode->CloneValue() );
+ ScCompiler aComp(rDoc, rPos, aTemp, formula::FormulaGrammar::GRAM_DEFAULT);
+ aComp.MoveRelWrap();
+ aComp.CreateStringFromTokenArray( rBuffer );
+}
+
+void ScRangeData::UpdateReference( sc::RefUpdateContext& rCxt, SCTAB nLocalTab )
+{
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceInName(rCxt, aPos);
+ bModified = aRes.mbReferenceModified;
+ if (aRes.mbReferenceModified)
+ rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex);
+}
+
+void ScRangeData::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest )
+{
+ bool bChanged = false;
+
+ formula::FormulaToken* t;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ {
+ if( t->GetType() != svIndex )
+ {
+ SingleDoubleRefModifier aMod( *t );
+ ScComplexRefData& rRef = aMod.Ref();
+ // Update only absolute references
+ if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() &&
+ (!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) &&
+ ( t->GetType() == svSingleRef ||
+ (!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() &&
+ (!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel()))))
+ {
+ ScRange aAbs = rRef.toAbs(rDoc, aPos);
+ // Check if the absolute reference of this range is pointing to the transposed source
+ if (ScRefUpdate::UpdateTranspose(rDoc, rSource, rDest, aAbs) != UR_NOTHING)
+ {
+ rRef.SetRange(rDoc.GetSheetLimits(), aAbs, aPos);
+ bChanged = true;
+ }
+ }
+ }
+ }
+
+ bModified = bChanged;
+}
+
+void ScRangeData::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ bool bChanged = false;
+
+ formula::FormulaToken* t;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ {
+ if( t->GetType() != svIndex )
+ {
+ SingleDoubleRefModifier aMod( *t );
+ ScComplexRefData& rRef = aMod.Ref();
+ if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() &&
+ (!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) &&
+ ( t->GetType() == svSingleRef ||
+ (!rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel() &&
+ (!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel()))))
+ {
+ ScRange aAbs = rRef.toAbs(rDoc, aPos);
+ if (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING)
+ {
+ rRef.SetRange(rDoc.GetSheetLimits(), aAbs, aPos);
+ bChanged = true;
+ }
+ }
+ }
+ }
+
+ bModified = bChanged; // has to be evaluated immediately afterwards
+}
+
+bool ScRangeData::operator== (const ScRangeData& rData) const // for Undo
+{
+ if ( nIndex != rData.nIndex ||
+ aName != rData.aName ||
+ aPos != rData.aPos ||
+ eType != rData.eType ) return false;
+
+ sal_uInt16 nLen = pCode->GetLen();
+ if ( nLen != rData.pCode->GetLen() ) return false;
+
+ FormulaToken** ppThis = pCode->GetArray();
+ FormulaToken** ppOther = rData.pCode->GetArray();
+
+ for ( sal_uInt16 i=0; i<nLen; i++ )
+ if ( ppThis[i] != ppOther[i] && !(*ppThis[i] == *ppOther[i]) )
+ return false;
+
+ return true;
+}
+
+bool ScRangeData::IsRangeAtBlock( const ScRange& rBlock ) const
+{
+ bool bRet = false;
+ ScRange aRange;
+ if ( IsReference(aRange) )
+ bRet = ( rBlock == aRange );
+ return bRet;
+}
+
+bool ScRangeData::IsReference( ScRange& rRange ) const
+{
+ if ( (eType & ( Type::AbsArea | Type::RefArea | Type::AbsPos )) && pCode )
+ return pCode->IsReference(rRange, aPos);
+
+ return false;
+}
+
+bool ScRangeData::IsReference( ScRange& rRange, const ScAddress& rPos ) const
+{
+ if ( (eType & ( Type::AbsArea | Type::RefArea | Type::AbsPos ) ) && pCode )
+ return pCode->IsReference(rRange, rPos);
+
+ return false;
+}
+
+bool ScRangeData::IsValidReference( ScRange& rRange ) const
+{
+ if ( (eType & ( Type::AbsArea | Type::RefArea | Type::AbsPos ) ) && pCode )
+ return pCode->IsValidReference(rRange, aPos);
+
+ return false;
+}
+
+void ScRangeData::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt, SCTAB nLocalTab )
+{
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aPos);
+ if (aRes.mbReferenceModified)
+ rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex);
+
+ if (rCxt.mnInsertPos <= aPos.Tab())
+ aPos.IncTab(rCxt.mnSheets);
+}
+
+void ScRangeData::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt, SCTAB nLocalTab )
+{
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aPos);
+ if (aRes.mbReferenceModified)
+ rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex);
+
+ ScRangeUpdater::UpdateDeleteTab( aPos, rCxt);
+}
+
+void ScRangeData::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nLocalTab )
+{
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aPos);
+ if (aRes.mbReferenceModified)
+ rCxt.maUpdatedNames.setUpdatedName(nLocalTab, nIndex);
+
+ aPos.SetTab(rCxt.getNewTab(aPos.Tab()));
+}
+
+void ScRangeData::MakeValidName( const ScDocument& rDoc, OUString& rName )
+{
+
+ // strip leading invalid characters
+ sal_Int32 nPos = 0;
+ sal_Int32 nLen = rName.getLength();
+ while ( nPos < nLen && !ScCompiler::IsCharFlagAllConventions( rName, nPos, ScCharFlags::Name) )
+ ++nPos;
+ if ( nPos>0 )
+ rName = rName.copy(nPos);
+
+ // if the first character is an invalid start character, precede with '_'
+ if ( !rName.isEmpty() && !ScCompiler::IsCharFlagAllConventions( rName, 0, ScCharFlags::CharName ) )
+ rName = "_" + rName;
+
+ // replace invalid with '_'
+ nLen = rName.getLength();
+ for (nPos=0; nPos<nLen; nPos++)
+ {
+ if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos, ScCharFlags::Name) )
+ rName = rName.replaceAt( nPos, 1, u"_" );
+ }
+
+ // Ensure that the proposed name is not a reference under any convention,
+ // same as in IsNameValid()
+ ScAddress aAddr;
+ ScRange aRange;
+ for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
+ {
+ ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) );
+ // Don't check Parse on VALID, any partial only VALID may result in
+ // #REF! during compile later!
+ while (aRange.Parse(rName, rDoc, details) != ScRefFlags::ZERO ||
+ aAddr.Parse(rName, rDoc, details) != ScRefFlags::ZERO)
+ {
+ // Range Parse is partially valid also with invalid sheet name,
+ // Address Parse ditto, during compile name would generate a #REF!
+ if ( rName.indexOf( '.' ) != -1 )
+ rName = rName.replaceFirst( ".", "_" );
+ else
+ rName = "_" + rName;
+ }
+ }
+}
+
+ScRangeData::IsNameValidType ScRangeData::IsNameValid( const OUString& rName, const ScDocument& rDoc )
+{
+ /* XXX If changed, sc/source/filter/ftools/ftools.cxx
+ * ScfTools::ConvertToScDefinedName needs to be changed too. */
+ char const a('.');
+ if (rName.indexOf(a) != -1)
+ return IsNameValidType::NAME_INVALID_BAD_STRING;
+ sal_Int32 nPos = 0;
+ sal_Int32 nLen = rName.getLength();
+ if ( !nLen || !ScCompiler::IsCharFlagAllConventions( rName, nPos++, ScCharFlags::CharName ) )
+ return IsNameValidType::NAME_INVALID_BAD_STRING;
+ while ( nPos < nLen )
+ {
+ if ( !ScCompiler::IsCharFlagAllConventions( rName, nPos++, ScCharFlags::Name ) )
+ return IsNameValidType::NAME_INVALID_BAD_STRING;
+ }
+ ScAddress aAddr;
+ ScRange aRange;
+ for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
+ {
+ ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) );
+ // Don't check Parse on VALID, any partial only VALID may result in
+ // #REF! during compile later!
+ if (aRange.Parse(rName, rDoc, details) != ScRefFlags::ZERO ||
+ aAddr.Parse(rName, rDoc, details) != ScRefFlags::ZERO )
+ {
+ return IsNameValidType::NAME_INVALID_CELL_REF;
+ }
+ }
+ return IsNameValidType::NAME_VALID;
+}
+
+bool ScRangeData::HasPossibleAddressConflict() const
+{
+ // Similar to part of IsNameValid(), but only check if the name is a valid address.
+ ScAddress aAddr;
+ for (int nConv = FormulaGrammar::CONV_UNSPECIFIED; ++nConv < FormulaGrammar::CONV_LAST; )
+ {
+ ScAddress::Details details( static_cast<FormulaGrammar::AddressConvention>( nConv ) );
+ // Don't check Parse on VALID, any partial only VALID may result in
+ // #REF! during compile later!
+ if(aAddr.Parse(aUpperName, rDoc, details) != ScRefFlags::ZERO)
+ return true;
+ }
+ return false;
+}
+
+FormulaError ScRangeData::GetErrCode() const
+{
+ return pCode ? pCode->GetCodeError() : FormulaError::NONE;
+}
+
+bool ScRangeData::HasReferences() const
+{
+ return pCode->HasReferences();
+}
+
+sal_uInt32 ScRangeData::GetUnoType() const
+{
+ sal_uInt32 nUnoType = 0;
+ if ( HasType(Type::Criteria) ) nUnoType |= css::sheet::NamedRangeFlag::FILTER_CRITERIA;
+ if ( HasType(Type::PrintArea) ) nUnoType |= css::sheet::NamedRangeFlag::PRINT_AREA;
+ if ( HasType(Type::ColHeader) ) nUnoType |= css::sheet::NamedRangeFlag::COLUMN_HEADER;
+ if ( HasType(Type::RowHeader) ) nUnoType |= css::sheet::NamedRangeFlag::ROW_HEADER;
+ if ( HasType(Type::Hidden) ) nUnoType |= css::sheet::NamedRangeFlag::HIDDEN;
+ return nUnoType;
+}
+
+void ScRangeData::ValidateTabRefs()
+{
+ // try to make sure all relative references and the reference position
+ // are within existing tables, so they can be represented as text
+ // (if the range of used tables is more than the existing tables,
+ // the result may still contain invalid tables, because the relative
+ // references aren't changed so formulas stay the same)
+
+ // find range of used tables
+
+ SCTAB nMinTab = aPos.Tab();
+ SCTAB nMaxTab = nMinTab;
+ formula::FormulaToken* t;
+ formula::FormulaTokenArrayPlainIterator aIter(*pCode);
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ ScAddress aAbs = rRef1.toAbs(rDoc, aPos);
+ if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() )
+ {
+ if (aAbs.Tab() < nMinTab)
+ nMinTab = aAbs.Tab();
+ if (aAbs.Tab() > nMaxTab)
+ nMaxTab = aAbs.Tab();
+ }
+ if ( t->GetType() == svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ aAbs = rRef2.toAbs(rDoc, aPos);
+ if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() )
+ {
+ if (aAbs.Tab() < nMinTab)
+ nMinTab = aAbs.Tab();
+ if (aAbs.Tab() > nMaxTab)
+ nMaxTab = aAbs.Tab();
+ }
+ }
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nMaxTab < nTabCount || nMinTab <= 0 )
+ return;
+
+ // move position and relative tab refs
+ // The formulas that use the name are not changed by this
+
+ SCTAB nMove = nMinTab;
+ ScAddress aOldPos = aPos;
+ aPos.SetTab( aPos.Tab() - nMove );
+
+ aIter.Reset();
+ while ( ( t = aIter.GetNextReference() ) != nullptr )
+ {
+ switch (t->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *t->GetSingleRef();
+ if (!rRef.IsTabDeleted())
+ {
+ ScAddress aAbs = rRef.toAbs(rDoc, aOldPos);
+ rRef.SetAddress(rDoc.GetSheetLimits(), aAbs, aPos);
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *t->GetDoubleRef();
+ if (!rRef.Ref1.IsTabDeleted())
+ {
+ ScAddress aAbs = rRef.Ref1.toAbs(rDoc, aOldPos);
+ rRef.Ref1.SetAddress(rDoc.GetSheetLimits(), aAbs, aPos);
+ }
+ if (!rRef.Ref2.IsTabDeleted())
+ {
+ ScAddress aAbs = rRef.Ref2.toAbs(rDoc, aOldPos);
+ rRef.Ref2.SetAddress(rDoc.GetSheetLimits(), aAbs, aPos);
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void ScRangeData::SetCode( const ScTokenArray& rArr )
+{
+ pCode.reset(new ScTokenArray( rArr ));
+ pCode->SetFromRangeName(true);
+ InitCode();
+}
+
+void ScRangeData::InitCode()
+{
+ if( pCode->GetCodeError() == FormulaError::NONE )
+ {
+ FormulaToken* p = FormulaTokenArrayPlainIterator(*pCode).GetNextReference();
+ if( p ) // exact one reference at first
+ {
+ if( p->GetType() == svSingleRef )
+ eType = eType | Type::AbsPos;
+ else
+ eType = eType | Type::AbsArea;
+ }
+ }
+}
+
+extern "C"
+int ScRangeData_QsortNameCompare( const void* p1, const void* p2 )
+{
+ return static_cast<int>(ScGlobal::GetCollator().compareString(
+ (*static_cast<const ScRangeData* const *>(p1))->GetName(),
+ (*static_cast<const ScRangeData* const *>(p2))->GetName() ));
+}
+
+namespace {
+
+/**
+ * Predicate to check if the name references the specified range.
+ */
+class MatchByRange
+{
+ const ScRange& mrRange;
+public:
+ explicit MatchByRange(const ScRange& rRange) : mrRange(rRange) {}
+ bool operator() (std::pair<OUString const, std::unique_ptr<ScRangeData>> const& r) const
+ {
+ return r.second->IsRangeAtBlock(mrRange);
+ }
+};
+
+}
+
+ScRangeName::ScRangeName()
+ : mHasPossibleAddressConflict(false)
+ , mHasPossibleAddressConflictDirty(false)
+{
+}
+
+ScRangeName::ScRangeName(const ScRangeName& r)
+ : mHasPossibleAddressConflict( r.mHasPossibleAddressConflict )
+ , mHasPossibleAddressConflictDirty( r.mHasPossibleAddressConflictDirty )
+{
+ for (auto const& it : r.m_Data)
+ {
+ m_Data.insert(std::make_pair(it.first, std::make_unique<ScRangeData>(*it.second)));
+ }
+ // std::map was cloned, so each collection needs its own index to data.
+ maIndexToData.resize( r.maIndexToData.size(), nullptr);
+ for (auto const& itr : m_Data)
+ {
+ size_t nPos = itr.second->GetIndex() - 1;
+ if (nPos >= maIndexToData.size())
+ {
+ OSL_FAIL( "ScRangeName copy-ctor: maIndexToData size doesn't fit");
+ maIndexToData.resize(nPos+1, nullptr);
+ }
+ maIndexToData[nPos] = itr.second.get();
+ }
+}
+
+const ScRangeData* ScRangeName::findByRange(const ScRange& rRange) const
+{
+ DataType::const_iterator itr = std::find_if(
+ m_Data.begin(), m_Data.end(), MatchByRange(rRange));
+ return itr == m_Data.end() ? nullptr : itr->second.get();
+}
+
+ScRangeData* ScRangeName::findByUpperName(const OUString& rName)
+{
+ DataType::iterator itr = m_Data.find(rName);
+ return itr == m_Data.end() ? nullptr : itr->second.get();
+}
+
+const ScRangeData* ScRangeName::findByUpperName(const OUString& rName) const
+{
+ DataType::const_iterator itr = m_Data.find(rName);
+ return itr == m_Data.end() ? nullptr : itr->second.get();
+}
+
+ScRangeData* ScRangeName::findByIndex(sal_uInt16 i) const
+{
+ if (!i)
+ // index should never be zero.
+ return nullptr;
+
+ size_t nPos = i - 1;
+ return nPos < maIndexToData.size() ? maIndexToData[nPos] : nullptr;
+}
+
+void ScRangeName::UpdateReference(sc::RefUpdateContext& rCxt, SCTAB nLocalTab )
+{
+ if (rCxt.meMode == URM_COPY)
+ // Copying cells does not modify named expressions.
+ return;
+
+ for (auto const& itr : m_Data)
+ {
+ itr.second->UpdateReference(rCxt, nLocalTab);
+ }
+}
+
+void ScRangeName::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt, SCTAB nLocalTab )
+{
+ for (auto const& itr : m_Data)
+ {
+ itr.second->UpdateInsertTab(rCxt, nLocalTab);
+ }
+}
+
+void ScRangeName::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt, SCTAB nLocalTab )
+{
+ for (auto const& itr : m_Data)
+ {
+ itr.second->UpdateDeleteTab(rCxt, nLocalTab);
+ }
+}
+
+void ScRangeName::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nLocalTab )
+{
+ for (auto const& itr : m_Data)
+ {
+ itr.second->UpdateMoveTab(rCxt, nLocalTab);
+ }
+}
+
+void ScRangeName::UpdateTranspose(const ScRange& rSource, const ScAddress& rDest)
+{
+ for (auto const& itr : m_Data)
+ {
+ itr.second->UpdateTranspose(rSource, rDest);
+ }
+}
+
+void ScRangeName::UpdateGrow(const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY)
+{
+ for (auto const& itr : m_Data)
+ {
+ itr.second->UpdateGrow(rArea, nGrowX, nGrowY);
+ }
+}
+
+void ScRangeName::CompileUnresolvedXML( sc::CompileFormulaContext& rCxt )
+{
+ for (auto const& itr : m_Data)
+ {
+ itr.second->CompileUnresolvedXML(rCxt);
+ }
+}
+
+void ScRangeName::CopyUsedNames( const SCTAB nLocalTab, const SCTAB nOldTab, const SCTAB nNewTab,
+ const ScDocument& rOldDoc, ScDocument& rNewDoc, const bool bGlobalNamesToLocal ) const
+{
+ for (auto const& itr : m_Data)
+ {
+ SCTAB nSheet = (nLocalTab < 0) ? nLocalTab : nOldTab;
+ sal_uInt16 nIndex = itr.second->GetIndex();
+ ScAddress aOldPos( itr.second->GetPos());
+ aOldPos.SetTab( nOldTab);
+ ScAddress aNewPos( aOldPos);
+ aNewPos.SetTab( nNewTab);
+ ScRangeData* pRangeData = nullptr;
+ rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, aNewPos, aOldPos, bGlobalNamesToLocal, false);
+ }
+}
+
+bool ScRangeName::insert( ScRangeData* p, bool bReuseFreeIndex )
+{
+ if (!p)
+ return false;
+
+ if (!p->GetIndex())
+ {
+ // Assign a new index. An index must be unique and is never 0.
+ if (bReuseFreeIndex)
+ {
+ IndexDataType::iterator itr = std::find(
+ maIndexToData.begin(), maIndexToData.end(), static_cast<ScRangeData*>(nullptr));
+ if (itr != maIndexToData.end())
+ {
+ // Empty slot exists. Re-use it.
+ size_t nPos = std::distance(maIndexToData.begin(), itr);
+ p->SetIndex(nPos + 1);
+ }
+ else
+ // No empty slot. Append it to the end.
+ p->SetIndex(maIndexToData.size() + 1);
+ }
+ else
+ {
+ p->SetIndex(maIndexToData.size() + 1);
+ }
+ }
+
+ OUString aName(p->GetUpperName());
+ erase(aName); // ptr_map won't insert it if a duplicate name exists.
+ pair<DataType::iterator, bool> r =
+ m_Data.insert(std::make_pair(aName, std::unique_ptr<ScRangeData>(p)));
+ if (r.second)
+ {
+ // Data inserted. Store its index for mapping.
+ size_t nPos = p->GetIndex() - 1;
+ if (nPos >= maIndexToData.size())
+ maIndexToData.resize(nPos+1, nullptr);
+ maIndexToData[nPos] = p;
+ mHasPossibleAddressConflictDirty = true;
+ }
+ return r.second;
+}
+
+void ScRangeName::erase(const ScRangeData& r)
+{
+ erase(r.GetUpperName());
+}
+
+void ScRangeName::erase(const OUString& rName)
+{
+ DataType::const_iterator itr = m_Data.find(rName);
+ if (itr != m_Data.end())
+ erase(itr);
+}
+
+void ScRangeName::erase(const_iterator itr)
+{
+ sal_uInt16 nIndex = itr->second->GetIndex();
+ m_Data.erase(itr);
+ OSL_ENSURE( 0 < nIndex && nIndex <= maIndexToData.size(), "ScRangeName::erase: bad index");
+ if (0 < nIndex && nIndex <= maIndexToData.size())
+ maIndexToData[nIndex-1] = nullptr;
+ if(mHasPossibleAddressConflict)
+ mHasPossibleAddressConflictDirty = true;
+}
+
+void ScRangeName::clear()
+{
+ m_Data.clear();
+ maIndexToData.clear();
+ mHasPossibleAddressConflict = false;
+ mHasPossibleAddressConflictDirty = false;
+}
+
+void ScRangeName::checkHasPossibleAddressConflict() const
+{
+ mHasPossibleAddressConflict = false;
+ mHasPossibleAddressConflictDirty = false;
+ for (auto const& itr : m_Data)
+ {
+ if( itr.second->HasPossibleAddressConflict())
+ {
+ mHasPossibleAddressConflict = true;
+ return;
+ }
+ }
+}
+
+bool ScRangeName::operator== (const ScRangeName& r) const
+{
+ return std::equal(m_Data.begin(), m_Data.end(), r.m_Data.begin(), r.m_Data.end(),
+ [](const DataType::value_type& lhs, const DataType::value_type& rhs) {
+ return (lhs.first == rhs.first) && (*lhs.second == *rhs.second);
+ });
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/rangeseq.cxx b/sc/source/core/tool/rangeseq.cxx
new file mode 100644
index 0000000000..75652b720a
--- /dev/null
+++ b/sc/source/core/tool/rangeseq.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 <svl/numformat.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/float_int_conversion.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/string.hxx>
+#include <rangeseq.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <scmatrix.hxx>
+#include <formulacell.hxx>
+
+using namespace com::sun::star;
+
+static bool lcl_HasErrors( ScDocument& rDoc, const ScRange& rRange )
+{
+ // no need to look at empty cells - just use ScCellIterator
+ ScCellIterator aIter( rDoc, rRange );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pCell = aIter.getFormulaCell();
+ if (pCell->GetErrCode() != FormulaError::NONE)
+ return true;
+ }
+ return false; // no error found
+}
+
+static tools::Long lcl_DoubleToLong( double fVal )
+{
+ double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) :
+ ::rtl::math::approxCeil( fVal );
+ if ( o3tl::convertsToAtLeast(fInt, LONG_MIN) && o3tl::convertsToAtMost(fInt, LONG_MAX) )
+ return static_cast<tools::Long>(fInt);
+ else
+ return 0; // out of range
+}
+
+bool ScRangeToSequence::FillLongArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
+ sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
+
+ uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq( nRowCount );
+ uno::Sequence<sal_Int32>* pRowAry = aRowSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<sal_Int32> aColSeq( nColCount );
+ sal_Int32* pColAry = aColSeq.getArray();
+ for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
+ pColAry[nCol] = lcl_DoubleToLong( rDoc.GetValue(
+ ScAddress( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ) ) );
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return !lcl_HasErrors( rDoc, rRange );
+}
+
+bool ScRangeToSequence::FillLongArray( uno::Any& rAny, const ScMatrix* pMatrix )
+{
+ if (!pMatrix)
+ return false;
+
+ SCSIZE nColCount;
+ SCSIZE nRowCount;
+ pMatrix->GetDimensions( nColCount, nRowCount );
+
+ uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
+ uno::Sequence<sal_Int32>* pRowAry = aRowSeq.getArray();
+ for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<sal_Int32> aColSeq( static_cast<sal_Int32>(nColCount) );
+ sal_Int32* pColAry = aColSeq.getArray();
+ for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
+ if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
+ pColAry[nCol] = 0;
+ else
+ pColAry[nCol] = lcl_DoubleToLong( pMatrix->GetDouble( nCol, nRow ) );
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return true;
+}
+
+bool ScRangeToSequence::FillDoubleArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
+ sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
+
+ uno::Sequence< uno::Sequence<double> > aRowSeq( nRowCount );
+ uno::Sequence<double>* pRowAry = aRowSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<double> aColSeq( nColCount );
+ double* pColAry = aColSeq.getArray();
+ for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
+ pColAry[nCol] = rDoc.GetValue(
+ ScAddress( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab ) );
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return !lcl_HasErrors( rDoc, rRange );
+}
+
+bool ScRangeToSequence::FillDoubleArray( uno::Any& rAny, const ScMatrix* pMatrix )
+{
+ if (!pMatrix)
+ return false;
+
+ SCSIZE nColCount;
+ SCSIZE nRowCount;
+ pMatrix->GetDimensions( nColCount, nRowCount );
+
+ uno::Sequence< uno::Sequence<double> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
+ uno::Sequence<double>* pRowAry = aRowSeq.getArray();
+ for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<double> aColSeq( static_cast<sal_Int32>(nColCount) );
+ double* pColAry = aColSeq.getArray();
+ for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
+ if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
+ pColAry[nCol] = 0.0;
+ else
+ pColAry[nCol] = pMatrix->GetDouble( nCol, nRow );
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return true;
+}
+
+bool ScRangeToSequence::FillStringArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
+ sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
+
+ bool bHasErrors = false;
+
+ uno::Sequence< uno::Sequence<OUString> > aRowSeq( nRowCount );
+ uno::Sequence<OUString>* pRowAry = aRowSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<OUString> aColSeq( nColCount );
+ OUString* pColAry = aColSeq.getArray();
+ for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
+ {
+ FormulaError nErrCode = rDoc.GetStringForFormula(
+ ScAddress(static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab),
+ pColAry[nCol] );
+ if ( nErrCode != FormulaError::NONE )
+ bHasErrors = true;
+ }
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return !bHasErrors;
+}
+
+bool ScRangeToSequence::FillStringArray( uno::Any& rAny, const ScMatrix* pMatrix,
+ SvNumberFormatter* pFormatter )
+{
+ if (!pMatrix)
+ return false;
+
+ SCSIZE nColCount;
+ SCSIZE nRowCount;
+ pMatrix->GetDimensions( nColCount, nRowCount );
+
+ uno::Sequence< uno::Sequence<OUString> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
+ uno::Sequence<OUString>* pRowAry = aRowSeq.getArray();
+ for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<OUString> aColSeq( static_cast<sal_Int32>(nColCount) );
+ OUString* pColAry = aColSeq.getArray();
+ for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
+ {
+ OUString aStr;
+ if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
+ {
+ if ( !pMatrix->IsEmpty( nCol, nRow ) )
+ aStr = pMatrix->GetString(nCol, nRow).getString();
+ }
+ else if ( pFormatter )
+ {
+ double fVal = pMatrix->GetDouble( nCol, nRow );
+ const Color* pColor;
+ pFormatter->GetOutputString( fVal, 0, aStr, &pColor );
+ }
+ pColAry[nCol] = aStr;
+ }
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return true;
+}
+
+bool ScRangeToSequence::FillMixedArray( uno::Any& rAny, ScDocument& rDoc, const ScRange& rRange,
+ bool bAllowNV )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ sal_Int32 nColCount = rRange.aEnd.Col() + 1 - rRange.aStart.Col();
+ sal_Int32 nRowCount = rRange.aEnd.Row() + 1 - rRange.aStart.Row();
+
+ bool bHasErrors = false;
+
+ uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( nRowCount );
+ uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<uno::Any> aColSeq( nColCount );
+ uno::Any* pColAry = aColSeq.getArray();
+ for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
+ {
+ uno::Any& rElement = pColAry[nCol];
+
+ ScAddress aPos( static_cast<SCCOL>(nStartCol+nCol), static_cast<SCROW>(nStartRow+nRow), nTab );
+ ScRefCellValue aCell(rDoc, aPos);
+
+ if (aCell.isEmpty())
+ {
+ rElement <<= OUString();
+ continue;
+ }
+
+ if (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetErrCode() != FormulaError::NONE)
+ {
+ // if NV is allowed, leave empty for errors
+ bHasErrors = true;
+ }
+ else if (aCell.hasNumeric())
+ rElement <<= aCell.getValue();
+ else
+ rElement <<= aCell.getString(&rDoc);
+ }
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return bAllowNV || !bHasErrors;
+}
+
+bool ScRangeToSequence::FillMixedArray( uno::Any& rAny, const ScMatrix* pMatrix, bool bDataTypes )
+{
+ if (!pMatrix)
+ return false;
+
+ SCSIZE nColCount;
+ SCSIZE nRowCount;
+ pMatrix->GetDimensions( nColCount, nRowCount );
+
+ uno::Sequence< uno::Sequence<uno::Any> > aRowSeq( static_cast<sal_Int32>(nRowCount) );
+ uno::Sequence<uno::Any>* pRowAry = aRowSeq.getArray();
+ for (SCSIZE nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<uno::Any> aColSeq( static_cast<sal_Int32>(nColCount) );
+ uno::Any* pColAry = aColSeq.getArray();
+ for (SCSIZE nCol = 0; nCol < nColCount; nCol++)
+ {
+ if ( pMatrix->IsStringOrEmpty( nCol, nRow ) )
+ {
+ OUString aStr;
+ if ( !pMatrix->IsEmpty( nCol, nRow ) )
+ aStr = pMatrix->GetString(nCol, nRow).getString();
+ pColAry[nCol] <<= aStr;
+ }
+ else
+ {
+ double fVal = pMatrix->GetDouble( nCol, nRow );
+ if (bDataTypes && pMatrix->IsBoolean( nCol, nRow ))
+ pColAry[nCol] <<= fVal != 0.0;
+ else
+ pColAry[nCol] <<= fVal;
+ }
+ }
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ rAny <<= aRowSeq;
+ return true;
+}
+
+bool ScApiTypeConversion::ConvertAnyToDouble( double & o_fVal,
+ css::uno::TypeClass & o_eClass,
+ const css::uno::Any & rAny )
+{
+ bool bRet = false;
+ o_eClass = rAny.getValueTypeClass();
+ switch (o_eClass)
+ {
+ //TODO: extract integer values
+ case uno::TypeClass_ENUM:
+ case uno::TypeClass_BOOLEAN:
+ case uno::TypeClass_CHAR:
+ case uno::TypeClass_BYTE:
+ case uno::TypeClass_SHORT:
+ case uno::TypeClass_UNSIGNED_SHORT:
+ case uno::TypeClass_LONG:
+ case uno::TypeClass_UNSIGNED_LONG:
+ case uno::TypeClass_FLOAT:
+ case uno::TypeClass_DOUBLE:
+ rAny >>= o_fVal;
+ bRet = true;
+ break;
+ default:
+ ; // nothing, avoid warning
+ }
+ if (!bRet)
+ o_fVal = 0.0;
+ return bRet;
+}
+
+ScMatrixRef ScSequenceToMatrix::CreateMixedMatrix( const css::uno::Any & rAny )
+{
+ ScMatrixRef xMatrix;
+ uno::Sequence< uno::Sequence< uno::Any > > aSequence;
+ if ( rAny >>= aSequence )
+ {
+ sal_Int32 nRowCount = aSequence.getLength();
+ sal_Int32 nMaxColCount = 0;
+ if (nRowCount)
+ {
+ auto pRow = std::max_element(std::cbegin(aSequence), std::cend(aSequence),
+ [](const uno::Sequence<uno::Any>& a, const uno::Sequence<uno::Any>& b) {
+ return a.getLength() < b.getLength(); });
+ nMaxColCount = pRow->getLength();
+ }
+ if ( nMaxColCount && nRowCount )
+ {
+ const uno::Sequence<uno::Any>* pRowArr = aSequence.getConstArray();
+ OUString aUStr;
+ xMatrix = new ScMatrix(
+ static_cast<SCSIZE>(nMaxColCount),
+ static_cast<SCSIZE>(nRowCount), 0.0);
+ SCSIZE nCols, nRows;
+ xMatrix->GetDimensions( nCols, nRows);
+ if (nCols != static_cast<SCSIZE>(nMaxColCount) || nRows != static_cast<SCSIZE>(nRowCount))
+ {
+ OSL_FAIL( "ScSequenceToMatrix::CreateMixedMatrix: matrix exceeded max size, returning NULL matrix");
+ return nullptr;
+ }
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ sal_Int32 nColCount = pRowArr[nRow].getLength();
+ const uno::Any* pColArr = pRowArr[nRow].getConstArray();
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ {
+ double fVal;
+ uno::TypeClass eClass;
+ if (ScApiTypeConversion::ConvertAnyToDouble( fVal, eClass, pColArr[nCol]))
+ {
+ if (eClass == uno::TypeClass_BOOLEAN)
+ xMatrix->PutBoolean( fVal != 0.0,
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ else
+ xMatrix->PutDouble( fVal,
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ }
+ else
+ {
+ // Try string, else use empty as last resort.
+
+ if ( pColArr[nCol] >>= aUStr )
+ {
+ xMatrix->PutString(
+ svl::SharedString(aUStr), static_cast<SCSIZE>(nCol), static_cast<SCSIZE>(nRow));
+ }
+ else
+ xMatrix->PutEmpty(
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ }
+ }
+ for (sal_Int32 nCol=nColCount; nCol<nMaxColCount; nCol++)
+ {
+ xMatrix->PutEmpty(
+ static_cast<SCSIZE>(nCol),
+ static_cast<SCSIZE>(nRow) );
+ }
+ }
+ }
+ }
+ return xMatrix;
+}
+
+bool ScByteSequenceToString::GetString( OUString& rString, const uno::Any& rAny )
+{
+ bool bResult = false;
+ if (rAny >>= rString)
+ {
+ bResult = true;
+ }
+ else if (uno::Sequence<sal_Int8> aSeq; rAny >>= aSeq)
+ {
+ rString = OUString( reinterpret_cast<const char*>(aSeq.getConstArray()),
+ aSeq.getLength(), osl_getThreadTextEncoding() );
+ bResult = true;
+ }
+ if (bResult)
+ rString = comphelper::string::stripEnd(rString, 0);
+ return bResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/rangeutl.cxx b/sc/source/core/tool/rangeutl.cxx
new file mode 100644
index 0000000000..e7314645c2
--- /dev/null
+++ b/sc/source/core/tool/rangeutl.cxx
@@ -0,0 +1,1067 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#include <rangeutl.hxx>
+#include <document.hxx>
+#include <global.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <convuno.hxx>
+#include <externalrefmgr.hxx>
+#include <compiler.hxx>
+#include <refupdatecontext.hxx>
+
+using ::formula::FormulaGrammar;
+using namespace ::com::sun::star;
+
+bool ScRangeUtil::MakeArea( const OUString& rAreaStr,
+ ScArea& rArea,
+ const ScDocument& rDoc,
+ SCTAB nTab,
+ ScAddress::Details const & rDetails )
+{
+ // Input in rAreaStr: "$Tabelle1.$A1:$D17"
+
+ // BROKEN BROKEN BROKEN
+ // but it is only used in the consolidate dialog. Ignore for now.
+
+ bool bSuccess = false;
+ sal_Int32 nPointPos = rAreaStr.indexOf('.');
+ sal_Int32 nColonPos = rAreaStr.indexOf(':');
+ OUString aStrArea( rAreaStr );
+ ScRefAddress startPos;
+ ScRefAddress endPos;
+
+ if ( nColonPos == -1 && nPointPos != -1 )
+ {
+ aStrArea += OUString::Concat(":") + rAreaStr.subView( nPointPos+1 ); // do not include '.' in copy
+ }
+
+ bSuccess = ConvertDoubleRef( rDoc, aStrArea, nTab, startPos, endPos, rDetails );
+
+ if ( bSuccess )
+ rArea = ScArea( startPos.Tab(),
+ startPos.Col(), startPos.Row(),
+ endPos.Col(), endPos.Row() );
+
+ return bSuccess;
+}
+
+void ScRangeUtil::CutPosString( const OUString& theAreaStr,
+ OUString& thePosStr )
+{
+ OUString aPosStr;
+ // BROKEN BROKEN BROKEN
+ // but it is only used in the consolidate dialog. Ignore for now.
+
+ sal_Int32 nColonPos = theAreaStr.indexOf(':');
+
+ if ( nColonPos != -1 )
+ aPosStr = theAreaStr.copy( 0, nColonPos ); // do not include ':' in copy
+ else
+ aPosStr = theAreaStr;
+
+ thePosStr = aPosStr;
+}
+
+bool ScRangeUtil::IsAbsTabArea( const OUString& rAreaStr,
+ const ScDocument* pDoc,
+ std::unique_ptr<ScArea[]>* ppAreas,
+ sal_uInt16* pAreaCount,
+ bool /* bAcceptCellRef */,
+ ScAddress::Details const & rDetails )
+{
+ OSL_ENSURE( pDoc, "No document given!" );
+ if ( !pDoc )
+ return false;
+
+ // BROKEN BROKEN BROKEN
+ // but it is only used in the consolidate dialog. Ignore for now.
+
+ /*
+ * Expects strings like:
+ * "$Tabelle1.$A$1:$Tabelle3.$D$17"
+ * If bAcceptCellRef == sal_True then also accept strings like:
+ * "$Tabelle1.$A$1"
+ *
+ * as result a ScArea-Array is created,
+ * which is published via ppAreas and also has to be deleted this route.
+ */
+
+ bool bStrOk = false;
+ OUString aTempAreaStr(rAreaStr);
+
+ if ( -1 == aTempAreaStr.indexOf(':') )
+ {
+ aTempAreaStr += ":" + rAreaStr;
+ }
+
+ sal_Int32 nColonPos = aTempAreaStr.indexOf(':');
+
+ if ( -1 != nColonPos
+ && -1 != aTempAreaStr.indexOf('.') )
+ {
+ ScRefAddress aStartPos;
+
+ OUString aStartPosStr = aTempAreaStr.copy( 0, nColonPos );
+ OUString aEndPosStr = aTempAreaStr.copy( nColonPos+1 );
+
+ if ( ConvertSingleRef( *pDoc, aStartPosStr, 0, aStartPos, rDetails ) )
+ {
+ ScRefAddress aEndPos;
+ if ( ConvertSingleRef( *pDoc, aEndPosStr, aStartPos.Tab(), aEndPos, rDetails ) )
+ {
+ aStartPos.SetRelCol( false );
+ aStartPos.SetRelRow( false );
+ aStartPos.SetRelTab( false );
+ aEndPos.SetRelCol( false );
+ aEndPos.SetRelRow( false );
+ aEndPos.SetRelTab( false );
+
+ bStrOk = true;
+
+ if ( ppAreas && pAreaCount ) // Array returned ?
+ {
+ SCTAB nStartTab = aStartPos.Tab();
+ SCTAB nEndTab = aEndPos.Tab();
+ sal_uInt16 nTabCount = static_cast<sal_uInt16>(nEndTab-nStartTab+1);
+ ppAreas->reset(new ScArea[nTabCount]);
+ SCTAB nTab = 0;
+ sal_uInt16 i = 0;
+ ScArea theArea( 0, aStartPos.Col(), aStartPos.Row(),
+ aEndPos.Col(), aEndPos.Row() );
+
+ nTab = nStartTab;
+ for ( i=0; i<nTabCount; i++ )
+ {
+ (*ppAreas)[i] = theArea;
+ (*ppAreas)[i].nTab = nTab;
+ nTab++;
+ }
+ *pAreaCount = nTabCount;
+ }
+ }
+ }
+ }
+
+ return bStrOk;
+}
+
+bool ScRangeUtil::IsAbsArea( const OUString& rAreaStr,
+ const ScDocument& rDoc,
+ SCTAB nTab,
+ OUString* pCompleteStr,
+ ScRefAddress* pStartPos,
+ ScRefAddress* pEndPos,
+ ScAddress::Details const & rDetails )
+{
+ ScRefAddress startPos;
+ ScRefAddress endPos;
+
+ bool bIsAbsArea = ConvertDoubleRef( rDoc, rAreaStr, nTab, startPos, endPos, rDetails );
+
+ if ( bIsAbsArea )
+ {
+ startPos.SetRelCol( false );
+ startPos.SetRelRow( false );
+ startPos.SetRelTab( false );
+ endPos .SetRelCol( false );
+ endPos .SetRelRow( false );
+ endPos .SetRelTab( false );
+
+ if ( pCompleteStr )
+ {
+ *pCompleteStr = startPos.GetRefString( rDoc, MAXTAB+1, rDetails );
+ *pCompleteStr += ":";
+ *pCompleteStr += endPos.GetRefString( rDoc, nTab, rDetails );
+ }
+
+ if ( pStartPos && pEndPos )
+ {
+ *pStartPos = startPos;
+ *pEndPos = endPos;
+ }
+ }
+
+ return bIsAbsArea;
+}
+
+bool ScRangeUtil::IsAbsPos( const OUString& rPosStr,
+ const ScDocument& rDoc,
+ SCTAB nTab,
+ OUString* pCompleteStr,
+ ScRefAddress* pPosTripel,
+ ScAddress::Details const & rDetails )
+{
+ ScRefAddress thePos;
+
+ bool bIsAbsPos = ConvertSingleRef( rDoc, rPosStr, nTab, thePos, rDetails );
+ thePos.SetRelCol( false );
+ thePos.SetRelRow( false );
+ thePos.SetRelTab( false );
+
+ if ( bIsAbsPos )
+ {
+ if ( pPosTripel )
+ *pPosTripel = thePos;
+ if ( pCompleteStr )
+ *pCompleteStr = thePos.GetRefString( rDoc, MAXTAB+1, rDetails );
+ }
+
+ return bIsAbsPos;
+}
+
+bool ScRangeUtil::MakeRangeFromName (
+ const OUString& rName,
+ const ScDocument& rDoc,
+ SCTAB nCurTab,
+ ScRange& rRange,
+ RutlNameScope eScope,
+ ScAddress::Details const & rDetails,
+ bool bUseDetailsPos )
+{
+ bool bResult = false;
+ if (rName.isEmpty())
+ return bResult;
+
+ SCTAB nTab = 0;
+ SCCOL nColStart = 0;
+ SCCOL nColEnd = 0;
+ SCROW nRowStart = 0;
+ SCROW nRowEnd = 0;
+
+ if (eScope == RUTL_NAMES || eScope == RUTL_NAMES_LOCAL || eScope == RUTL_NAMES_GLOBAL)
+ {
+ OUString aName(rName);
+ SCTAB nTable = nCurTab;
+
+ if (eScope != RUTL_NAMES_GLOBAL)
+ {
+ // First handle UI names like "local1 (Sheet1)", which point to a
+ // local range name.
+ const sal_Int32 nEndPos = aName.getLength() - 1;
+ if (rName[nEndPos] == ')')
+ {
+ const sal_Int32 nStartPos = aName.indexOf(" (");
+ if (nStartPos != -1)
+ {
+ OUString aSheetName = aName.copy(nStartPos+2, nEndPos-nStartPos-2);
+ if (rDoc.GetTable(aSheetName, nTable))
+ {
+ aName = aName.copy(0, nStartPos);
+ eScope = RUTL_NAMES_LOCAL;
+ }
+ else
+ nTable = nCurTab;
+ }
+ }
+ }
+
+ aName = ScGlobal::getCharClass().uppercase(aName);
+ ScRangeData* pData = nullptr;
+ if (eScope != RUTL_NAMES_GLOBAL)
+ {
+ // Check for local range names.
+ ScRangeName* pRangeNames = rDoc.GetRangeName( nTable );
+ if ( pRangeNames )
+ pData = pRangeNames->findByUpperName(aName);
+ }
+ if (!pData && eScope != RUTL_NAMES_LOCAL)
+ pData = rDoc.GetRangeName()->findByUpperName(aName);
+ if (pData)
+ {
+ OUString aStrArea;
+ ScRefAddress aStartPos;
+ ScRefAddress aEndPos;
+
+ // tdf#138646: use the current grammar of the document and passed
+ // address convention.
+ // tdf#145077: create range string according to current cell cursor
+ // position if expression has relative references and details say so.
+ if (bUseDetailsPos)
+ aStrArea = pData->GetSymbol( ScAddress( rDetails.nCol, rDetails.nRow, nCurTab),
+ FormulaGrammar::mergeToGrammar(rDoc.GetGrammar(), rDetails.eConv));
+ else
+ aStrArea = pData->GetSymbol(
+ FormulaGrammar::mergeToGrammar(rDoc.GetGrammar(), rDetails.eConv));
+
+ if ( IsAbsArea( aStrArea, rDoc, nTable,
+ nullptr, &aStartPos, &aEndPos, rDetails ) )
+ {
+ nTab = aStartPos.Tab();
+ nColStart = aStartPos.Col();
+ nRowStart = aStartPos.Row();
+ nColEnd = aEndPos.Col();
+ nRowEnd = aEndPos.Row();
+ bResult = true;
+ }
+ else
+ {
+ CutPosString( aStrArea, aStrArea );
+
+ if ( IsAbsPos( aStrArea, rDoc, nTable,
+ nullptr, &aStartPos, rDetails ) )
+ {
+ nTab = aStartPos.Tab();
+ nColStart = nColEnd = aStartPos.Col();
+ nRowStart = nRowEnd = aStartPos.Row();
+ bResult = true;
+ }
+ }
+ }
+ }
+ else if( eScope==RUTL_DBASE )
+ {
+ ScDBCollection::NamedDBs& rDbNames = rDoc.GetDBCollection()->getNamedDBs();
+ ScDBData* pData = rDbNames.findByUpperName(ScGlobal::getCharClass().uppercase(rName));
+ if (pData)
+ {
+ pData->GetArea(nTab, nColStart, nRowStart, nColEnd, nRowEnd);
+ bResult = true;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ScRangeUtil::MakeRangeFromName" );
+ }
+
+ if( bResult )
+ {
+ rRange = ScRange( nColStart, nRowStart, nTab, nColEnd, nRowEnd, nTab );
+ }
+
+ return bResult;
+}
+
+void ScRangeStringConverter::AssignString(
+ OUString& rString,
+ const OUString& rNewStr,
+ bool bAppendStr,
+ sal_Unicode cSeparator)
+{
+ if( bAppendStr )
+ {
+ if( !rNewStr.isEmpty() )
+ {
+ if( !rString.isEmpty() )
+ rString += OUStringChar(cSeparator);
+ rString += rNewStr;
+ }
+ }
+ else
+ rString = rNewStr;
+}
+
+sal_Int32 ScRangeStringConverter::IndexOf(
+ std::u16string_view rString,
+ sal_Unicode cSearchChar,
+ sal_Int32 nOffset,
+ sal_Unicode cQuote )
+{
+ sal_Int32 nLength = rString.size();
+ sal_Int32 nIndex = nOffset;
+ bool bQuoted = false;
+ bool bExitLoop = false;
+
+ while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) )
+ {
+ sal_Unicode cCode = rString[ nIndex ];
+ bExitLoop = (cCode == cSearchChar) && !bQuoted;
+ bQuoted = (bQuoted != (cCode == cQuote));
+ if( !bExitLoop )
+ nIndex++;
+ }
+ return (nIndex < nLength) ? nIndex : -1;
+}
+
+sal_Int32 ScRangeStringConverter::IndexOfDifferent(
+ std::u16string_view rString,
+ sal_Unicode cSearchChar,
+ sal_Int32 nOffset )
+{
+ sal_Int32 nLength = rString.size();
+ sal_Int32 nIndex = nOffset;
+ bool bExitLoop = false;
+
+ while( !bExitLoop && (nIndex >= 0 && nIndex < nLength) )
+ {
+ bExitLoop = (rString[ nIndex ] != cSearchChar);
+ if( !bExitLoop )
+ nIndex++;
+ }
+ return (nIndex < nLength) ? nIndex : -1;
+}
+
+void ScRangeStringConverter::GetTokenByOffset(
+ OUString& rToken,
+ std::u16string_view rString,
+ sal_Int32& nOffset,
+ sal_Unicode cSeparator,
+ sal_Unicode cQuote)
+{
+ sal_Int32 nLength = rString.size();
+ if( nOffset == -1 || nOffset >= nLength )
+ {
+ rToken.clear();
+ nOffset = -1;
+ }
+ else
+ {
+ sal_Int32 nTokenEnd = IndexOf( rString, cSeparator, nOffset, cQuote );
+ if( nTokenEnd < 0 )
+ nTokenEnd = nLength;
+ rToken = rString.substr( nOffset, nTokenEnd - nOffset );
+
+ sal_Int32 nNextBegin = IndexOfDifferent( rString, cSeparator, nTokenEnd );
+ nOffset = (nNextBegin < 0) ? nLength : nNextBegin;
+ }
+}
+
+void ScRangeStringConverter::AppendTableName(OUStringBuffer& rBuf, const OUString& rTabName)
+{
+ // quote character is always "'"
+ OUString aQuotedTab(rTabName);
+ ScCompiler::CheckTabQuotes(aQuotedTab);
+ rBuf.append(aQuotedTab);
+}
+
+sal_Int32 ScRangeStringConverter::GetTokenCount( std::u16string_view rString, sal_Unicode cSeparator )
+{
+ OUString sToken;
+ sal_Int32 nCount = 0;
+ sal_Int32 nOffset = 0;
+ while( nOffset >= 0 )
+ {
+ GetTokenByOffset( sToken, rString, nOffset, '\'', cSeparator );
+ if( nOffset >= 0 )
+ nCount++;
+ }
+ return nCount;
+}
+
+bool ScRangeStringConverter::GetAddressFromString(
+ ScAddress& rAddress,
+ std::u16string_view rAddressStr,
+ const ScDocument& rDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Int32& nOffset,
+ sal_Unicode cSeparator,
+ sal_Unicode cQuote )
+{
+ OUString sToken;
+ GetTokenByOffset( sToken, rAddressStr, nOffset, cSeparator, cQuote );
+ if( nOffset >= 0 )
+ {
+ if ((rAddress.Parse( sToken, rDocument, eConv ) & ScRefFlags::VALID) == ScRefFlags::VALID)
+ return true;
+ ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
+ if (eConv != eConvUI)
+ return ((rAddress.Parse(sToken, rDocument, eConvUI) & ScRefFlags::VALID) == ScRefFlags::VALID);
+ }
+ return false;
+}
+
+bool ScRangeStringConverter::GetRangeFromString(
+ ScRange& rRange,
+ std::u16string_view rRangeStr,
+ const ScDocument& rDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Int32& nOffset,
+ sal_Unicode cSeparator,
+ sal_Unicode cQuote )
+{
+ OUString sToken;
+ bool bResult(false);
+ GetTokenByOffset( sToken, rRangeStr, nOffset, cSeparator, cQuote );
+ if( nOffset >= 0 )
+ {
+ sal_Int32 nIndex = IndexOf( sToken, ':', 0, cQuote );
+ OUString aUIString(sToken);
+
+ if( nIndex < 0 )
+ {
+ if ( aUIString[0] == '.' )
+ aUIString = aUIString.copy( 1 );
+ bResult = (rRange.aStart.Parse( aUIString, rDocument, eConv) & ScRefFlags::VALID) ==
+ ScRefFlags::VALID;
+ ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
+ if (!bResult && eConv != eConvUI)
+ bResult = (rRange.aStart.Parse(aUIString, rDocument, eConvUI) & ScRefFlags::VALID) ==
+ ScRefFlags::VALID;
+ rRange.aEnd = rRange.aStart;
+ }
+ else
+ {
+ if ( aUIString[0] == '.' )
+ {
+ aUIString = aUIString.copy( 1 );
+ --nIndex;
+ }
+
+ if ( nIndex < aUIString.getLength() - 1 &&
+ aUIString[ nIndex + 1 ] == '.' )
+ aUIString = aUIString.replaceAt( nIndex + 1, 1, u"" );
+
+ bResult = ((rRange.Parse(aUIString, rDocument, eConv) & ScRefFlags::VALID) ==
+ ScRefFlags::VALID);
+
+ // #i77703# chart ranges in the file format contain both sheet names, even for an external reference sheet.
+ // This isn't parsed by ScRange, so try to parse the two Addresses then.
+ if (!bResult)
+ {
+ bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConv)
+ & ScRefFlags::VALID) == ScRefFlags::VALID)
+ &&
+ ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConv)
+ & ScRefFlags::VALID) == ScRefFlags::VALID);
+
+ ::formula::FormulaGrammar::AddressConvention eConvUI = rDocument.GetAddressConvention();
+ if (!bResult && eConv != eConvUI)
+ {
+ bResult = ((rRange.aStart.Parse( aUIString.copy(0, nIndex), rDocument, eConvUI)
+ & ScRefFlags::VALID) == ScRefFlags::VALID)
+ &&
+ ((rRange.aEnd.Parse( aUIString.copy(nIndex+1), rDocument, eConvUI)
+ & ScRefFlags::VALID) == ScRefFlags::VALID);
+ }
+ }
+ }
+ }
+ return bResult;
+}
+
+bool ScRangeStringConverter::GetRangeListFromString(
+ ScRangeList& rRangeList,
+ std::u16string_view rRangeListStr,
+ const ScDocument& rDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator,
+ sal_Unicode cQuote )
+{
+ bool bRet = true;
+ OSL_ENSURE( !rRangeListStr.empty(), "ScXMLConverter::GetRangeListFromString - empty string!" );
+ sal_Int32 nOffset = 0;
+ while( nOffset >= 0 )
+ {
+ ScRange aRange;
+ if (
+ GetRangeFromString( aRange, rRangeListStr, rDocument, eConv, nOffset, cSeparator, cQuote ) &&
+ (nOffset >= 0)
+ )
+ {
+ rRangeList.push_back( aRange );
+ }
+ else if (nOffset > -1)
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool ScRangeStringConverter::GetAreaFromString(
+ ScArea& rArea,
+ std::u16string_view rRangeStr,
+ const ScDocument& rDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Int32& nOffset,
+ sal_Unicode cSeparator )
+{
+ ScRange aScRange;
+ bool bResult(false);
+ if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) )
+ {
+ rArea.nTab = aScRange.aStart.Tab();
+ rArea.nColStart = aScRange.aStart.Col();
+ rArea.nRowStart = aScRange.aStart.Row();
+ rArea.nColEnd = aScRange.aEnd.Col();
+ rArea.nRowEnd = aScRange.aEnd.Row();
+ bResult = true;
+ }
+ return bResult;
+}
+
+bool ScRangeStringConverter::GetRangeFromString(
+ table::CellRangeAddress& rRange,
+ std::u16string_view rRangeStr,
+ const ScDocument& rDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Int32& nOffset,
+ sal_Unicode cSeparator )
+{
+ ScRange aScRange;
+ bool bResult(false);
+ if( GetRangeFromString( aScRange, rRangeStr, rDocument, eConv, nOffset, cSeparator ) && (nOffset >= 0) )
+ {
+ ScUnoConversion::FillApiRange( rRange, aScRange );
+ bResult = true;
+ }
+ return bResult;
+}
+
+void ScRangeStringConverter::GetStringFromAddress(
+ OUString& rString,
+ const ScAddress& rAddress,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator,
+ bool bAppendStr,
+ ScRefFlags nFormatFlags )
+{
+ if (pDocument && pDocument->HasTable(rAddress.Tab()))
+ {
+ OUString sAddress(rAddress.Format(nFormatFlags, pDocument, eConv));
+ AssignString( rString, sAddress, bAppendStr, cSeparator );
+ }
+}
+
+void ScRangeStringConverter::GetStringFromRange(
+ OUString& rString,
+ const ScRange& rRange,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator,
+ bool bAppendStr,
+ ScRefFlags nFormatFlags )
+{
+ if (pDocument && pDocument->HasTable(rRange.aStart.Tab()))
+ {
+ ScAddress aStartAddress( rRange.aStart );
+ ScAddress aEndAddress( rRange.aEnd );
+ OUString sStartAddress(aStartAddress.Format(nFormatFlags, pDocument, eConv));
+ OUString sEndAddress(aEndAddress.Format(nFormatFlags, pDocument, eConv));
+ AssignString(
+ rString, sStartAddress + ":" + sEndAddress, bAppendStr, cSeparator);
+ }
+}
+
+void ScRangeStringConverter::GetStringFromRangeList(
+ OUString& rString,
+ const ScRangeList* pRangeList,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator )
+{
+ OUString sRangeListStr;
+ if( pRangeList )
+ {
+ for( size_t nIndex = 0, nCount = pRangeList->size(); nIndex < nCount; nIndex++ )
+ {
+ const ScRange & rRange = (*pRangeList)[nIndex];
+ GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true );
+ }
+ }
+ rString = sRangeListStr;
+}
+
+void ScRangeStringConverter::GetStringFromArea(
+ OUString& rString,
+ const ScArea& rArea,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator,
+ bool bAppendStr,
+ ScRefFlags nFormatFlags )
+{
+ ScRange aRange( rArea.nColStart, rArea.nRowStart, rArea.nTab, rArea.nColEnd, rArea.nRowEnd, rArea.nTab );
+ GetStringFromRange( rString, aRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags );
+}
+
+void ScRangeStringConverter::GetStringFromAddress(
+ OUString& rString,
+ const table::CellAddress& rAddress,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator,
+ bool bAppendStr )
+{
+ ScAddress aScAddress( static_cast<SCCOL>(rAddress.Column), static_cast<SCROW>(rAddress.Row), rAddress.Sheet );
+ GetStringFromAddress( rString, aScAddress, pDocument, eConv, cSeparator, bAppendStr );
+}
+
+void ScRangeStringConverter::GetStringFromRange(
+ OUString& rString,
+ const table::CellRangeAddress& rRange,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator,
+ bool bAppendStr,
+ ScRefFlags nFormatFlags )
+{
+ ScRange aScRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), rRange.Sheet,
+ static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), rRange.Sheet );
+ GetStringFromRange( rString, aScRange, pDocument, eConv, cSeparator, bAppendStr, nFormatFlags );
+}
+
+void ScRangeStringConverter::GetStringFromRangeList(
+ OUString& rString,
+ const uno::Sequence< table::CellRangeAddress >& rRangeSeq,
+ const ScDocument* pDocument,
+ FormulaGrammar::AddressConvention eConv,
+ sal_Unicode cSeparator )
+{
+ OUString sRangeListStr;
+ for( const table::CellRangeAddress& rRange : rRangeSeq )
+ {
+ GetStringFromRange( sRangeListStr, rRange, pDocument, eConv, cSeparator, true );
+ }
+ rString = sRangeListStr;
+}
+
+static void lcl_appendCellAddress(
+ OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell,
+ const ScAddress::ExternalInfo& rExtInfo)
+{
+ if (rExtInfo.mbExternal)
+ {
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo.mnFileId, true);
+ if (!pFilePath)
+ return;
+
+ sal_Unicode cQuote = '\'';
+ rBuf.append(cQuote);
+ rBuf.append(*pFilePath);
+ rBuf.append(cQuote);
+ rBuf.append('#');
+ rBuf.append('$');
+ ScRangeStringConverter::AppendTableName(rBuf, rExtInfo.maTabName);
+ rBuf.append('.');
+
+ OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
+ rBuf.append(aAddr);
+ }
+ else
+ {
+ OUString aAddr(rCell.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()));
+ rBuf.append(aAddr);
+ }
+}
+
+static void lcl_appendCellRangeAddress(
+ OUStringBuffer& rBuf, const ScDocument& rDoc, const ScAddress& rCell1, const ScAddress& rCell2,
+ const ScAddress::ExternalInfo& rExtInfo1, const ScAddress::ExternalInfo& rExtInfo2)
+{
+ if (rExtInfo1.mbExternal)
+ {
+ OSL_ENSURE(rExtInfo2.mbExternal, "2nd address is not external!?");
+ OSL_ENSURE(rExtInfo1.mnFileId == rExtInfo2.mnFileId, "File IDs do not match between 1st and 2nd addresses.");
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pFilePath = pRefMgr->getExternalFileName(rExtInfo1.mnFileId, true);
+ if (!pFilePath)
+ return;
+
+ sal_Unicode cQuote = '\'';
+ rBuf.append(cQuote);
+ rBuf.append(*pFilePath);
+ rBuf.append(cQuote);
+ rBuf.append('#');
+ rBuf.append('$');
+ ScRangeStringConverter::AppendTableName(rBuf, rExtInfo1.maTabName);
+ rBuf.append('.');
+
+ OUString aAddr(rCell1.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
+ rBuf.append(aAddr);
+
+ rBuf.append(":");
+
+ if (rExtInfo1.maTabName != rExtInfo2.maTabName)
+ {
+ rBuf.append('$');
+ ScRangeStringConverter::AppendTableName(rBuf, rExtInfo2.maTabName);
+ rBuf.append('.');
+ }
+
+ aAddr = rCell2.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention());
+ rBuf.append(aAddr);
+ }
+ else
+ {
+ ScRange aRange;
+ aRange.aStart = rCell1;
+ aRange.aEnd = rCell2;
+ OUString aAddr(aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ rBuf.append(aAddr);
+ }
+}
+
+void ScRangeStringConverter::GetStringFromXMLRangeString( OUString& rString, std::u16string_view rXMLRange, const ScDocument& rDoc )
+{
+ FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ const sal_Unicode cSepNew = ScCompiler::GetNativeSymbolChar(ocSep);
+
+ OUStringBuffer aRetStr;
+ sal_Int32 nOffset = 0;
+ bool bFirst = true;
+
+ while (nOffset >= 0)
+ {
+ OUString aToken;
+ GetTokenByOffset(aToken, rXMLRange, nOffset);
+ if (nOffset < 0)
+ break;
+
+ sal_Int32 nSepPos = IndexOf(aToken, ':', 0);
+ if (nSepPos >= 0)
+ {
+ // Cell range
+ OUString aBeginCell = aToken.copy(0, nSepPos);
+ OUString aEndCell = aToken.copy(nSepPos+1);
+
+ if (aBeginCell.isEmpty() || aEndCell.isEmpty())
+ // both cell addresses must exist for this to work.
+ continue;
+
+ sal_Int32 nEndCellDotPos = aEndCell.indexOf('.');
+ if (nEndCellDotPos <= 0)
+ {
+ // initialize buffer with table name...
+ sal_Int32 nDotPos = IndexOf(aBeginCell, '.', 0);
+ OUStringBuffer aBuf(aBeginCell.subView(0, nDotPos));
+
+ if (nEndCellDotPos == 0)
+ {
+ // workaround for old syntax (probably pre-chart2 age?)
+ // e.g. Sheet1.A1:.B2
+ aBuf.append(aEndCell);
+ }
+ else if (nEndCellDotPos < 0)
+ {
+ // sheet name in the end cell is omitted (e.g. Sheet2.A1:B2).
+ aBuf.append("." + aEndCell);
+ }
+ aEndCell = aBuf.makeStringAndClear();
+ }
+
+ ScAddress::ExternalInfo aExtInfo1, aExtInfo2;
+ ScAddress aCell1, aCell2;
+ ScRefFlags nRet = aCell1.Parse(aBeginCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo1);
+ if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ {
+ // first cell is invalid.
+ if (eConv == FormulaGrammar::CONV_OOO)
+ continue;
+
+ nRet = aCell1.Parse(aBeginCell, rDoc, eConv, &aExtInfo1);
+ if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ // first cell is really invalid.
+ continue;
+ }
+
+ nRet = aCell2.Parse(aEndCell, rDoc, FormulaGrammar::CONV_OOO, &aExtInfo2);
+ if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ {
+ // second cell is invalid.
+ if (eConv == FormulaGrammar::CONV_OOO)
+ continue;
+
+ nRet = aCell2.Parse(aEndCell, rDoc, eConv, &aExtInfo2);
+ if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ // second cell is really invalid.
+ continue;
+ }
+
+ if (aExtInfo1.mnFileId != aExtInfo2.mnFileId || aExtInfo1.mbExternal != aExtInfo2.mbExternal)
+ // external info inconsistency.
+ continue;
+
+ // All looks good!
+
+ if (bFirst)
+ bFirst = false;
+ else
+ aRetStr.append(cSepNew);
+
+ lcl_appendCellRangeAddress(aRetStr, rDoc, aCell1, aCell2, aExtInfo1, aExtInfo2);
+ }
+ else
+ {
+ // Chart always saves ranges using CONV_OOO convention.
+ ScAddress::ExternalInfo aExtInfo;
+ ScAddress aCell;
+ ScRefFlags nRet = aCell.Parse(aToken, rDoc, ::formula::FormulaGrammar::CONV_OOO, &aExtInfo);
+ if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO )
+ {
+ nRet = aCell.Parse(aToken, rDoc, eConv, &aExtInfo);
+ if ((nRet & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ continue;
+ }
+
+ // Looks good!
+
+ if (bFirst)
+ bFirst = false;
+ else
+ aRetStr.append(cSepNew);
+
+ lcl_appendCellAddress(aRetStr, rDoc, aCell, aExtInfo);
+ }
+ }
+
+ rString = aRetStr.makeStringAndClear();
+}
+
+ScRangeData* ScRangeStringConverter::GetRangeDataFromString( const OUString& rString, const SCTAB nTab,
+ const ScDocument& rDoc, formula::FormulaGrammar::AddressConvention eConv )
+{
+ // This may be called with an external 'doc'#name but wouldn't find any.
+
+ // Dot '.' is not allowed in range names, if present only lookup if it's a
+ // sheet-local name. Same for '!' Excel syntax.
+ // If eConv == FormulaGrammar::CONV_A1_XL_A1 then try both, first our own.
+ sal_Int32 nIndex = -1;
+ if (eConv == FormulaGrammar::CONV_OOO || eConv == FormulaGrammar::CONV_A1_XL_A1)
+ nIndex = ScGlobal::FindUnquoted( rString, '.');
+ if (nIndex < 0 && (eConv == FormulaGrammar::CONV_A1_XL_A1
+ || eConv == FormulaGrammar::CONV_XL_A1
+ || eConv == FormulaGrammar::CONV_XL_R1C1
+ || eConv == FormulaGrammar::CONV_XL_OOX))
+ nIndex = ScGlobal::FindUnquoted( rString, '!');
+
+ if (nIndex >= 0)
+ {
+ if (nIndex == 0)
+ return nullptr; // Can't be a name.
+
+ OUString aTab( rString.copy( 0, nIndex));
+ ScGlobal::EraseQuotes( aTab, '\'');
+ SCTAB nLocalTab;
+ if (!rDoc.GetTable( aTab, nLocalTab))
+ return nullptr;
+
+ ScRangeName* pLocalRangeName = rDoc.GetRangeName(nLocalTab);
+ if (!pLocalRangeName)
+ return nullptr;
+
+ const OUString aName( rString.copy( nIndex+1));
+ return pLocalRangeName->findByUpperName( ScGlobal::getCharClass().uppercase( aName));
+ }
+
+ ScRangeName* pLocalRangeName = rDoc.GetRangeName(nTab);
+ ScRangeData* pData = nullptr;
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rString);
+ if(pLocalRangeName)
+ {
+ pData = pLocalRangeName->findByUpperName(aUpperName);
+ }
+ if (!pData)
+ {
+ ScRangeName* pGlobalRangeName = rDoc.GetRangeName();
+ if (pGlobalRangeName)
+ {
+ pData = pGlobalRangeName->findByUpperName(aUpperName);
+ }
+ }
+ return pData;
+}
+
+ScArea::ScArea( SCTAB tab,
+ SCCOL colStart, SCROW rowStart,
+ SCCOL colEnd, SCROW rowEnd ) :
+ nTab ( tab ),
+ nColStart( colStart ), nRowStart( rowStart ),
+ nColEnd ( colEnd ), nRowEnd ( rowEnd )
+{
+}
+
+bool ScArea::operator==( const ScArea& r ) const
+{
+ return ( (nTab == r.nTab)
+ && (nColStart == r.nColStart)
+ && (nRowStart == r.nRowStart)
+ && (nColEnd == r.nColEnd)
+ && (nRowEnd == r.nRowEnd) );
+}
+
+ScAreaNameIterator::ScAreaNameIterator( const ScDocument& rDoc ) :
+ pRangeName(rDoc.GetRangeName()),
+ pDBCollection(rDoc.GetDBCollection()),
+ bFirstPass(true)
+{
+ if (pRangeName)
+ {
+ maRNPos = pRangeName->begin();
+ maRNEnd = pRangeName->end();
+ }
+}
+
+bool ScAreaNameIterator::Next( OUString& rName, ScRange& rRange )
+{
+ for (;;)
+ {
+ if ( bFirstPass ) // first the area names
+ {
+ if ( pRangeName && maRNPos != maRNEnd )
+ {
+ const ScRangeData& rData = *maRNPos->second;
+ ++maRNPos;
+ bool bValid = rData.IsValidReference(rRange);
+ if (bValid)
+ {
+ rName = rData.GetName();
+ return true; // found
+ }
+ }
+ else
+ {
+ bFirstPass = false;
+ if (pDBCollection)
+ {
+ const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
+ maDBPos = rDBs.begin();
+ maDBEnd = rDBs.end();
+ }
+ }
+ }
+
+ if ( !bFirstPass ) // then the DB areas
+ {
+ if (pDBCollection && maDBPos != maDBEnd)
+ {
+ const ScDBData& rData = **maDBPos;
+ ++maDBPos;
+ rData.GetArea(rRange);
+ rName = rData.GetName();
+ return true; // found
+ }
+ else
+ return false; // nothing left
+ }
+ }
+}
+
+void ScRangeUpdater::UpdateInsertTab(ScAddress& rAddr, const sc::RefUpdateInsertTabContext& rCxt)
+{
+ if (rCxt.mnInsertPos <= rAddr.Tab())
+ {
+ rAddr.IncTab(rCxt.mnSheets);
+ }
+}
+
+void ScRangeUpdater::UpdateDeleteTab(ScAddress& rAddr, const sc::RefUpdateDeleteTabContext& rCxt)
+{
+ if (rCxt.mnDeletePos <= rAddr.Tab())
+ {
+ rAddr.SetTab( std::max<SCTAB>(0, rAddr.Tab() - rCxt.mnSheets));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/rechead.cxx b/sc/source/core/tool/rechead.cxx
new file mode 100644
index 0000000000..abd44d0419
--- /dev/null
+++ b/sc/source/core/tool/rechead.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 <rechead.hxx>
+#include <scerrors.hxx>
+
+#include <osl/diagnose.h>
+
+ScMultipleReadHeader::ScMultipleReadHeader(SvStream& rNewStream) :
+ rStream( rNewStream )
+{
+ sal_uInt32 nDataSize;
+ rStream.ReadUInt32( nDataSize );
+ sal_uInt64 nDataPos = rStream.Tell();
+ nTotalEnd = nDataPos + nDataSize;
+ nEntryEnd = nTotalEnd;
+
+ rStream.SeekRel(nDataSize);
+ sal_uInt16 nID;
+ rStream.ReadUInt16( nID );
+ if (nID != SCID_SIZES)
+ {
+ OSL_FAIL("SCID_SIZES not found");
+ if ( rStream.GetError() == ERRCODE_NONE )
+ rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
+
+ // everything to 0, so BytesLeft() aborts at least
+ pBuf = nullptr; pMemStream.reset();
+ nEntryEnd = nDataPos;
+ }
+ else
+ {
+ sal_uInt32 nSizeTableLen;
+ rStream.ReadUInt32( nSizeTableLen );
+ pBuf.reset( new sal_uInt8[nSizeTableLen] );
+ rStream.ReadBytes( pBuf.get(), nSizeTableLen );
+ pMemStream.reset(new SvMemoryStream( pBuf.get(), nSizeTableLen, StreamMode::READ ));
+ }
+
+ nEndPos = rStream.Tell();
+ rStream.Seek( nDataPos );
+}
+
+ScMultipleReadHeader::~ScMultipleReadHeader()
+{
+ if ( pMemStream && pMemStream->Tell() != pMemStream->GetEndOfData() )
+ {
+ OSL_FAIL( "Sizes not fully read" );
+ if ( rStream.GetError() == ERRCODE_NONE )
+ rStream.SetError( SCWARN_IMPORT_INFOLOST );
+ }
+ pMemStream.reset();
+
+ rStream.Seek(nEndPos);
+}
+
+void ScMultipleReadHeader::EndEntry()
+{
+ sal_uInt64 nPos = rStream.Tell();
+ OSL_ENSURE( nPos <= nEntryEnd, "read too much" );
+ if ( nPos != nEntryEnd )
+ {
+ if ( rStream.GetError() == ERRCODE_NONE )
+ rStream.SetError( SCWARN_IMPORT_INFOLOST );
+ rStream.Seek( nEntryEnd ); // ignore the rest
+ }
+
+ nEntryEnd = nTotalEnd; // all remaining, if no StartEntry follows
+}
+
+void ScMultipleReadHeader::StartEntry()
+{
+ sal_uInt64 nPos = rStream.Tell();
+ sal_uInt32 nEntrySize;
+ (*pMemStream).ReadUInt32( nEntrySize );
+
+ nEntryEnd = nPos + nEntrySize;
+ OSL_ENSURE( nEntryEnd <= nTotalEnd, "read too many entries" );
+}
+
+sal_uInt64 ScMultipleReadHeader::BytesLeft() const
+{
+ sal_uInt64 nReadEnd = rStream.Tell();
+ if (nReadEnd <= nEntryEnd)
+ return nEntryEnd-nReadEnd;
+
+ OSL_FAIL("ScMultipleReadHeader::BytesLeft: Error");
+ return 0;
+}
+
+ScMultipleWriteHeader::ScMultipleWriteHeader(SvStream& rNewStream) :
+ rStream( rNewStream ),
+ aMemStream( 4096, 4096 )
+{
+ nDataSize = 0;
+ rStream.WriteUInt32( nDataSize );
+
+ nDataPos = rStream.Tell();
+ nEntryStart = nDataPos;
+}
+
+ScMultipleWriteHeader::~ScMultipleWriteHeader()
+{
+ sal_uInt64 nDataEnd = rStream.Tell();
+
+ rStream.WriteUInt16( SCID_SIZES );
+ rStream.WriteUInt32( aMemStream.Tell() );
+ rStream.WriteBytes( aMemStream.GetData(), aMemStream.Tell() );
+
+ if ( nDataEnd - nDataPos != nDataSize ) // matched default ?
+ {
+ nDataSize = nDataEnd - nDataPos;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.Seek(nDataPos-sizeof(sal_uInt32));
+ rStream.WriteUInt32( nDataSize ); // record size at the beginning
+ rStream.Seek(nPos);
+ }
+}
+
+void ScMultipleWriteHeader::EndEntry()
+{
+ sal_uInt64 nPos = rStream.Tell();
+ aMemStream.WriteUInt32( nPos - nEntryStart );
+}
+
+void ScMultipleWriteHeader::StartEntry()
+{
+ sal_uInt64 nPos = rStream.Tell();
+ nEntryStart = nPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/recursionhelper.cxx b/sc/source/core/tool/recursionhelper.cxx
new file mode 100644
index 0000000000..59601f37a0
--- /dev/null
+++ b/sc/source/core/tool/recursionhelper.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/.
+ */
+
+#include <recursionhelper.hxx>
+#include <formulacell.hxx>
+
+void ScRecursionHelper::Init()
+{
+ nRecursionCount = 0;
+ nDependencyComputationLevel = 0;
+ bInRecursionReturn = bDoingRecursion = bInIterationReturn = false;
+ bAbortingDependencyComputation = false;
+ aInsertPos = GetIterationEnd();
+ ResetIteration();
+ // Must not force clear aFGList ever.
+}
+
+void ScRecursionHelper::ResetIteration()
+{
+ aLastIterationStart = GetIterationEnd();
+ nIteration = 0;
+ bConverging = false;
+}
+
+ScRecursionHelper::ScRecursionHelper()
+{
+ pFGSet = nullptr;
+ bGroupsIndependent = true;
+ Init();
+}
+
+void ScRecursionHelper::SetInRecursionReturn( bool b )
+{
+ // Do not use IsInRecursionReturn() here, it decouples iteration.
+ if (b && !bInRecursionReturn)
+ aInsertPos = aRecursionFormulas.begin();
+ bInRecursionReturn = b;
+}
+
+void ScRecursionHelper::Insert(
+ ScFormulaCell* p, bool bOldRunning, const ScFormulaResult & rRes )
+{
+ aRecursionFormulas.insert( aInsertPos, ScFormulaRecursionEntry( p,
+ bOldRunning, rRes));
+}
+
+void ScRecursionHelper::SetInIterationReturn( bool b )
+{
+ // An iteration return is always coupled to a recursion return.
+ SetInRecursionReturn( b);
+ bInIterationReturn = b;
+}
+
+void ScRecursionHelper::StartIteration()
+{
+ SetInIterationReturn( false);
+ nIteration = 1;
+ bConverging = false;
+ aLastIterationStart = GetIterationStart();
+}
+
+void ScRecursionHelper::ResumeIteration()
+{
+ SetInIterationReturn( false);
+ aLastIterationStart = GetIterationStart();
+}
+
+void ScRecursionHelper::IncIteration()
+{
+ ++nIteration;
+}
+
+void ScRecursionHelper::EndIteration()
+{
+ aRecursionFormulas.erase( GetIterationStart(), GetIterationEnd());
+ ResetIteration();
+}
+
+ScFormulaRecursionList::iterator ScRecursionHelper::GetIterationStart()
+{
+ return aRecursionFormulas.begin();
+}
+
+ScFormulaRecursionList::iterator ScRecursionHelper::GetIterationEnd()
+{
+ return aRecursionFormulas.end();
+}
+
+void ScRecursionHelper::Clear()
+{
+ aRecursionFormulas.clear();
+ while (!aRecursionInIterationStack.empty())
+ aRecursionInIterationStack.pop();
+ Init();
+}
+
+static ScFormulaCell* lcl_GetTopCell(ScFormulaCell* pCell)
+{
+ if (!pCell)
+ return nullptr;
+
+ const ScFormulaCellGroupRef& mxGroup = pCell->GetCellGroup();
+ if (!mxGroup)
+ return pCell;
+ return mxGroup->mpTopCell;
+}
+
+bool ScRecursionHelper::PushFormulaGroup(ScFormulaCell* pCell)
+{
+ assert(pCell);
+
+ if (pCell->GetSeenInPath())
+ {
+ // Found a simple cycle of formula-groups.
+ // Disable group calc for all elements of this cycle.
+ sal_Int32 nIdx = aFGList.size();
+ assert(nIdx > 0);
+ do
+ {
+ --nIdx;
+ assert(nIdx >= 0);
+ const ScFormulaCellGroupRef& mxGroup = aFGList[nIdx]->GetCellGroup();
+ if (mxGroup)
+ mxGroup->mbPartOfCycle = true;
+ } while (aFGList[nIdx] != pCell);
+
+ return false;
+ }
+
+ pCell->SetSeenInPath(true);
+ aFGList.push_back(pCell);
+ aInDependencyEvalMode.push_back(false);
+ return true;
+}
+
+void ScRecursionHelper::PopFormulaGroup()
+{
+ assert(aFGList.size() == aInDependencyEvalMode.size());
+ if (aFGList.empty())
+ return;
+ ScFormulaCell* pCell = aFGList.back();
+ pCell->SetSeenInPath(false);
+ aFGList.pop_back();
+ aInDependencyEvalMode.pop_back();
+}
+
+bool ScRecursionHelper::AnyCycleMemberInDependencyEvalMode(const ScFormulaCell* pCell)
+{
+ assert(pCell);
+
+ if (pCell->GetSeenInPath())
+ {
+ // Found a simple cycle of formula-groups.
+ sal_Int32 nIdx = aFGList.size();
+ assert(nIdx > 0);
+ do
+ {
+ --nIdx;
+ assert(nIdx >= 0);
+ const ScFormulaCellGroupRef& mxGroup = aFGList[nIdx]->GetCellGroup();
+ // Found a cycle member FG that is in dependency evaluation mode.
+ if (mxGroup && aInDependencyEvalMode[nIdx])
+ return true;
+ } while (aFGList[nIdx] != pCell);
+
+ return false;
+ }
+
+ return false;
+}
+
+bool ScRecursionHelper::AnyParentFGInCycle()
+{
+ sal_Int32 nIdx = aFGList.size() - 1;
+ while (nIdx >= 0)
+ {
+ const ScFormulaCellGroupRef& mxGroup = aFGList[nIdx]->GetCellGroup();
+ if (mxGroup)
+ return mxGroup->mbPartOfCycle;
+ --nIdx;
+ };
+ return false;
+}
+
+void ScRecursionHelper::SetFormulaGroupDepEvalMode(bool bSet)
+{
+ assert(aFGList.size());
+ assert(aFGList.size() == aInDependencyEvalMode.size());
+ assert(aFGList.back()->GetCellGroup());
+ aInDependencyEvalMode.back() = bSet;
+}
+
+void ScRecursionHelper::AbortDependencyComputation()
+{
+ assert( nDependencyComputationLevel > 0 );
+ bAbortingDependencyComputation = true;
+}
+
+void ScRecursionHelper::IncDepComputeLevel()
+{
+ ++nDependencyComputationLevel;
+}
+
+void ScRecursionHelper::DecDepComputeLevel()
+{
+ --nDependencyComputationLevel;
+ bAbortingDependencyComputation = false;
+}
+
+void ScRecursionHelper::AddTemporaryGroupCell(ScFormulaCell* cell)
+{
+ aTemporaryGroupCells.push_back( cell );
+}
+
+void ScRecursionHelper::CleanTemporaryGroupCells()
+{
+ if( GetRecursionCount() == 0 )
+ {
+ for( ScFormulaCell* cell : aTemporaryGroupCells )
+ cell->SetCellGroup( nullptr );
+ aTemporaryGroupCells.clear();
+ }
+}
+
+bool ScRecursionHelper::CheckFGIndependence(ScFormulaCellGroup* pFG)
+{
+ if (pFGSet && pFGSet->count(pFG))
+ {
+ bGroupsIndependent = false;
+ return false;
+ }
+
+ return true;
+}
+
+ScFormulaGroupCycleCheckGuard::ScFormulaGroupCycleCheckGuard(ScRecursionHelper& rRecursionHelper, ScFormulaCell* pCell) :
+ mrRecHelper(rRecursionHelper)
+{
+ if (pCell)
+ {
+ pCell = lcl_GetTopCell(pCell);
+ mbShouldPop = mrRecHelper.PushFormulaGroup(pCell);
+ }
+ else
+ mbShouldPop = false;
+}
+
+ScFormulaGroupCycleCheckGuard::~ScFormulaGroupCycleCheckGuard()
+{
+ if (mbShouldPop)
+ mrRecHelper.PopFormulaGroup();
+}
+
+ScFormulaGroupDependencyComputeGuard::ScFormulaGroupDependencyComputeGuard(ScRecursionHelper& rRecursionHelper) :
+ mrRecHelper(rRecursionHelper)
+{
+ mrRecHelper.IncDepComputeLevel();
+ mrRecHelper.SetFormulaGroupDepEvalMode(true);
+}
+
+ScFormulaGroupDependencyComputeGuard::~ScFormulaGroupDependencyComputeGuard()
+{
+ mrRecHelper.SetFormulaGroupDepEvalMode(false);
+ mrRecHelper.DecDepComputeLevel();
+}
+
+ScCheckIndependentFGGuard::ScCheckIndependentFGGuard(ScRecursionHelper& rRecursionHelper,
+ o3tl::sorted_vector<ScFormulaCellGroup*>* pSet) :
+ mrRecHelper(rRecursionHelper),
+ mbUsedFGSet(false)
+{
+ if (!mrRecHelper.HasFormulaGroupSet())
+ {
+ mrRecHelper.SetFormulaGroupSet(pSet);
+ mrRecHelper.SetGroupsIndependent(true);
+ mbUsedFGSet = true;
+ }
+}
+
+ScCheckIndependentFGGuard::~ScCheckIndependentFGGuard()
+{
+ if (mbUsedFGSet)
+ {
+ // Reset to defaults.
+ mrRecHelper.SetFormulaGroupSet(nullptr);
+ mrRecHelper.SetGroupsIndependent(true);
+ }
+}
+
+bool ScCheckIndependentFGGuard::AreGroupsIndependent()
+{
+ if (!mbUsedFGSet)
+ return false;
+
+ return mrRecHelper.AreGroupsIndependent();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/refdata.cxx b/sc/source/core/tool/refdata.cxx
new file mode 100644
index 0000000000..3e1b9b1af8
--- /dev/null
+++ b/sc/source/core/tool/refdata.cxx
@@ -0,0 +1,599 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <refdata.hxx>
+#include <document.hxx>
+
+void ScSingleRefData::InitAddress( const ScAddress& rAdr )
+{
+ InitAddress( rAdr.Col(), rAdr.Row(), rAdr.Tab());
+}
+
+void ScSingleRefData::InitAddress( SCCOL nColP, SCROW nRowP, SCTAB nTabP )
+{
+ InitFlags();
+ mnCol = nColP;
+ mnRow = nRowP;
+ mnTab = nTabP;
+}
+
+void ScSingleRefData::InitAddressRel( const ScDocument& rDoc, const ScAddress& rAdr, const ScAddress& rPos )
+{
+ InitFlags();
+ SetColRel(true);
+ SetRowRel(true);
+ SetTabRel(true);
+ SetAddress(rDoc.GetSheetLimits(), rAdr, rPos);
+}
+
+void ScSingleRefData::InitFromRefAddress( const ScDocument& rDoc, const ScRefAddress& rRef, const ScAddress& rPos )
+{
+ InitFlags();
+ SetColRel( rRef.IsRelCol());
+ SetRowRel( rRef.IsRelRow());
+ SetTabRel( rRef.IsRelTab());
+ SetFlag3D( rRef.Tab() != rPos.Tab());
+ SetAddress( rDoc.GetSheetLimits(), rRef.GetAddress(), rPos);
+}
+
+void ScSingleRefData::SetAbsCol( SCCOL nVal )
+{
+ Flags.bColRel = false;
+ mnCol = nVal;
+}
+
+void ScSingleRefData::SetRelCol( SCCOL nVal )
+{
+ Flags.bColRel = true;
+ mnCol = nVal;
+}
+
+void ScSingleRefData::IncCol( SCCOL nInc )
+{
+ mnCol += nInc;
+}
+
+void ScSingleRefData::SetAbsRow( SCROW nVal )
+{
+ Flags.bRowRel = false;
+ mnRow = nVal;
+}
+
+void ScSingleRefData::SetRelRow( SCROW nVal )
+{
+ Flags.bRowRel = true;
+ mnRow = nVal;
+}
+
+void ScSingleRefData::IncRow( SCROW nInc )
+{
+ mnRow += nInc;
+}
+
+void ScSingleRefData::SetAbsTab( SCTAB nVal )
+{
+ Flags.bTabRel = false;
+ mnTab = nVal;
+}
+
+void ScSingleRefData::SetRelTab( SCTAB nVal )
+{
+ Flags.bTabRel = true;
+ mnTab = nVal;
+}
+
+void ScSingleRefData::IncTab( SCTAB nInc )
+{
+ mnTab += nInc;
+}
+
+void ScSingleRefData::SetColDeleted( bool bVal )
+{
+ Flags.bColDeleted = bVal;
+}
+
+void ScSingleRefData::SetRowDeleted( bool bVal )
+{
+ Flags.bRowDeleted = bVal;
+}
+
+void ScSingleRefData::SetTabDeleted( bool bVal )
+{
+ Flags.bTabDeleted = bVal;
+}
+
+bool ScSingleRefData::IsDeleted() const
+{
+ return IsColDeleted() || IsRowDeleted() || IsTabDeleted();
+}
+
+bool ScSingleRefData::Valid(const ScDocument& rDoc) const
+{
+ return !IsDeleted() && ColValid(rDoc) && RowValid(rDoc) && TabValid(rDoc);
+}
+
+bool ScSingleRefData::ColValid(const ScDocument& rDoc) const
+{
+ if (Flags.bColRel)
+ {
+ if (mnCol < -rDoc.MaxCol() || rDoc.MaxCol() < mnCol)
+ return false;
+ }
+ else
+ {
+ if (mnCol < 0 || rDoc.MaxCol() < mnCol)
+ return false;
+ }
+
+ return true;
+}
+
+bool ScSingleRefData::RowValid(const ScDocument& rDoc) const
+{
+ if (Flags.bRowRel)
+ {
+ if (mnRow < -rDoc.MaxRow() || rDoc.MaxRow() < mnRow)
+ return false;
+ }
+ else
+ {
+ if (mnRow < 0 || rDoc.MaxRow() < mnRow)
+ return false;
+ }
+
+ return true;
+}
+
+bool ScSingleRefData::TabValid(const ScDocument& rDoc) const
+{
+ if (Flags.bTabRel)
+ {
+ if (mnTab < -MAXTAB || MAXTAB < mnTab)
+ return false;
+ }
+ else
+ {
+ if (mnTab < 0 || rDoc.GetTableCount() <= mnTab)
+ return false;
+ }
+
+ return true;
+}
+
+bool ScSingleRefData::ValidExternal(const ScDocument& rDoc) const
+{
+ return ColValid(rDoc) && RowValid(rDoc) && mnTab >= -1;
+}
+
+ScAddress ScSingleRefData::toAbs( const ScDocument& rDoc, const ScAddress& rPos ) const
+{
+ return toAbs(rDoc.GetSheetLimits(), rPos);
+}
+
+ScAddress ScSingleRefData::toAbs( const ScSheetLimits& rLimits, const ScAddress& rPos ) const
+{
+ SCCOL nRetCol = Flags.bColRel ? mnCol + rPos.Col() : mnCol;
+ SCROW nRetRow = Flags.bRowRel ? mnRow + rPos.Row() : mnRow;
+ SCTAB nRetTab = Flags.bTabRel ? mnTab + rPos.Tab() : mnTab;
+
+ ScAddress aAbs(ScAddress::INITIALIZE_INVALID);
+
+ if (rLimits.ValidCol(nRetCol))
+ aAbs.SetCol(nRetCol);
+
+ if (rLimits.ValidRow(nRetRow))
+ aAbs.SetRow(nRetRow);
+
+ if (ValidTab(nRetTab))
+ aAbs.SetTab(nRetTab);
+
+ return aAbs;
+}
+
+void ScSingleRefData::SetAddress( const ScSheetLimits& rLimits, const ScAddress& rAddr, const ScAddress& rPos )
+{
+ if (Flags.bColRel)
+ mnCol = rAddr.Col() - rPos.Col();
+ else
+ mnCol = rAddr.Col();
+
+ if (!rLimits.ValidCol(rAddr.Col()))
+ SetColDeleted(true);
+
+ if (Flags.bRowRel)
+ mnRow = rAddr.Row() - rPos.Row();
+ else
+ mnRow = rAddr.Row();
+
+ if (!rLimits.ValidRow(rAddr.Row()))
+ SetRowDeleted(true);
+
+ if (Flags.bTabRel)
+ mnTab = rAddr.Tab() - rPos.Tab();
+ else
+ mnTab = rAddr.Tab();
+
+ if (!ValidTab( rAddr.Tab(), MAXTAB))
+ SetTabDeleted(true);
+}
+
+SCROW ScSingleRefData::Row() const
+{
+ if (Flags.bRowDeleted)
+ return -1;
+ return mnRow;
+}
+
+SCCOL ScSingleRefData::Col() const
+{
+ if (Flags.bColDeleted)
+ return -1;
+ return mnCol;
+}
+
+SCTAB ScSingleRefData::Tab() const
+{
+ if (Flags.bTabDeleted)
+ return -1;
+ return mnTab;
+}
+
+// static
+void ScSingleRefData::PutInOrder( ScSingleRefData& rRef1, ScSingleRefData& rRef2, const ScAddress& rPos )
+{
+ const sal_uInt8 kCOL = 1;
+ const sal_uInt8 kROW = 2;
+ const sal_uInt8 kTAB = 4;
+
+ sal_uInt8 nRelState1 = rRef1.Flags.bRelName ?
+ ((rRef1.Flags.bTabRel ? kTAB : 0) |
+ (rRef1.Flags.bRowRel ? kROW : 0) |
+ (rRef1.Flags.bColRel ? kCOL : 0)) :
+ 0;
+
+ sal_uInt8 nRelState2 = rRef2.Flags.bRelName ?
+ ((rRef2.Flags.bTabRel ? kTAB : 0) |
+ (rRef2.Flags.bRowRel ? kROW : 0) |
+ (rRef2.Flags.bColRel ? kCOL : 0)) :
+ 0;
+
+ SCCOL nCol1 = rRef1.Flags.bColRel ? rPos.Col() + rRef1.mnCol : rRef1.mnCol;
+ SCCOL nCol2 = rRef2.Flags.bColRel ? rPos.Col() + rRef2.mnCol : rRef2.mnCol;
+ if (nCol2 < nCol1)
+ {
+ rRef1.mnCol = rRef2.Flags.bColRel ? nCol2 - rPos.Col() : nCol2;
+ rRef2.mnCol = rRef1.Flags.bColRel ? nCol1 - rPos.Col() : nCol1;
+ if (rRef1.Flags.bRelName && rRef1.Flags.bColRel)
+ nRelState2 |= kCOL;
+ else
+ nRelState2 &= ~kCOL;
+ if (rRef2.Flags.bRelName && rRef2.Flags.bColRel)
+ nRelState1 |= kCOL;
+ else
+ nRelState1 &= ~kCOL;
+ bool bTmp = rRef1.Flags.bColRel;
+ rRef1.Flags.bColRel = rRef2.Flags.bColRel;
+ rRef2.Flags.bColRel = bTmp;
+ bTmp = rRef1.Flags.bColDeleted;
+ rRef1.Flags.bColDeleted = rRef2.Flags.bColDeleted;
+ rRef2.Flags.bColDeleted = bTmp;
+ }
+
+ SCROW nRow1 = rRef1.Flags.bRowRel ? rPos.Row() + rRef1.mnRow : rRef1.mnRow;
+ SCROW nRow2 = rRef2.Flags.bRowRel ? rPos.Row() + rRef2.mnRow : rRef2.mnRow;
+ if (nRow2 < nRow1)
+ {
+ rRef1.mnRow = rRef2.Flags.bRowRel ? nRow2 - rPos.Row() : nRow2;
+ rRef2.mnRow = rRef1.Flags.bRowRel ? nRow1 - rPos.Row() : nRow1;
+ if (rRef1.Flags.bRelName && rRef1.Flags.bRowRel)
+ nRelState2 |= kROW;
+ else
+ nRelState2 &= ~kROW;
+ if (rRef2.Flags.bRelName && rRef2.Flags.bRowRel)
+ nRelState1 |= kROW;
+ else
+ nRelState1 &= ~kROW;
+ bool bTmp = rRef1.Flags.bRowRel;
+ rRef1.Flags.bRowRel = rRef2.Flags.bRowRel;
+ rRef2.Flags.bRowRel = bTmp;
+ bTmp = rRef1.Flags.bRowDeleted;
+ rRef1.Flags.bRowDeleted = rRef2.Flags.bRowDeleted;
+ rRef2.Flags.bRowDeleted = bTmp;
+ }
+
+ SCTAB nTab1 = rRef1.Flags.bTabRel ? rPos.Tab() + rRef1.mnTab : rRef1.mnTab;
+ SCTAB nTab2 = rRef2.Flags.bTabRel ? rPos.Tab() + rRef2.mnTab : rRef2.mnTab;
+ if (nTab2 < nTab1)
+ {
+ rRef1.mnTab = rRef2.Flags.bTabRel ? nTab2 - rPos.Tab() : nTab2;
+ rRef2.mnTab = rRef1.Flags.bTabRel ? nTab1 - rPos.Tab() : nTab1;
+ if (rRef1.Flags.bRelName && rRef1.Flags.bTabRel)
+ nRelState2 |= kTAB;
+ else
+ nRelState2 &= ~kTAB;
+ if (rRef2.Flags.bRelName && rRef2.Flags.bTabRel)
+ nRelState1 |= kTAB;
+ else
+ nRelState1 &= ~kTAB;
+ bool bTmp = rRef1.Flags.bTabRel;
+ rRef1.Flags.bTabRel = rRef2.Flags.bTabRel;
+ rRef2.Flags.bTabRel = bTmp;
+ bTmp = rRef1.Flags.bTabDeleted;
+ rRef1.Flags.bTabDeleted = rRef2.Flags.bTabDeleted;
+ rRef2.Flags.bTabDeleted = bTmp;
+ }
+
+ // bFlag3D stays the same on both references.
+
+ rRef1.Flags.bRelName = (nRelState1 != 0);
+ rRef2.Flags.bRelName = (nRelState2 != 0);
+}
+
+bool ScSingleRefData::operator==( const ScSingleRefData& r ) const
+{
+ return mnFlagValue == r.mnFlagValue && mnCol == r.mnCol && mnRow == r.mnRow && mnTab == r.mnTab;
+}
+
+bool ScSingleRefData::operator!=( const ScSingleRefData& r ) const
+{
+ return !operator==(r);
+}
+
+#if DEBUG_FORMULA_COMPILER
+void ScSingleRefData::Dump( int nIndent ) const
+{
+ std::string aIndent;
+ for (int i = 0; i < nIndent; ++i)
+ aIndent += " ";
+
+ cout << aIndent << "address type column: " << (IsColRel()?"relative":"absolute")
+ << " row : " << (IsRowRel()?"relative":"absolute") << " sheet: "
+ << (IsTabRel()?"relative":"absolute") << endl;
+ cout << aIndent << "deleted column: " << (IsColDeleted()?"yes":"no")
+ << " row : " << (IsRowDeleted()?"yes":"no") << " sheet: "
+ << (IsTabDeleted()?"yes":"no") << endl;
+ cout << aIndent << "column: " << mnCol << " row: " << mnRow << " sheet: " << mnTab << endl;
+ cout << aIndent << "3d ref: " << (IsFlag3D()?"yes":"no") << endl;
+}
+#endif
+
+void ScComplexRefData::InitFromRefAddresses( const ScDocument& rDoc, const ScRefAddress& rRef1, const ScRefAddress& rRef2, const ScAddress& rPos )
+{
+ InitFlags();
+ Ref1.SetColRel( rRef1.IsRelCol());
+ Ref1.SetRowRel( rRef1.IsRelRow());
+ Ref1.SetTabRel( rRef1.IsRelTab());
+ Ref1.SetFlag3D( rRef1.Tab() != rPos.Tab() || rRef1.Tab() != rRef2.Tab());
+ Ref2.SetColRel( rRef2.IsRelCol());
+ Ref2.SetRowRel( rRef2.IsRelRow());
+ Ref2.SetTabRel( rRef2.IsRelTab());
+ Ref2.SetFlag3D( rRef1.Tab() != rRef2.Tab());
+ SetRange( rDoc.GetSheetLimits(), ScRange( rRef1.GetAddress(), rRef2.GetAddress()), rPos);
+}
+
+ScComplexRefData& ScComplexRefData::Extend( const ScSheetLimits& rLimits, const ScSingleRefData & rRef, const ScAddress & rPos )
+{
+ bool bInherit3D = (Ref1.IsFlag3D() && !Ref2.IsFlag3D() && !rRef.IsFlag3D());
+ ScRange aAbsRange = toAbs(rLimits, rPos);
+
+ ScSingleRefData aRef = rRef;
+ // If no sheet was given in the extending part, let it point to the same
+ // sheet as this reference's end point, inheriting the absolute/relative
+ // mode.
+ // [$]Sheet1.A5:A6:A7 on Sheet2 do still reference only Sheet1.
+ if (!rRef.IsFlag3D())
+ {
+ if (Ref2.IsTabRel())
+ aRef.SetRelTab( Ref2.Tab());
+ else
+ aRef.SetAbsTab( Ref2.Tab());
+ }
+ ScAddress aAbs = aRef.toAbs(rLimits, rPos);
+
+ if (aAbs.Col() < aAbsRange.aStart.Col())
+ aAbsRange.aStart.SetCol(aAbs.Col());
+
+ if (aAbs.Row() < aAbsRange.aStart.Row())
+ aAbsRange.aStart.SetRow(aAbs.Row());
+
+ if (aAbs.Tab() < aAbsRange.aStart.Tab())
+ aAbsRange.aStart.SetTab(aAbs.Tab());
+
+ if (aAbsRange.aEnd.Col() < aAbs.Col())
+ aAbsRange.aEnd.SetCol(aAbs.Col());
+
+ if (aAbsRange.aEnd.Row() < aAbs.Row())
+ aAbsRange.aEnd.SetRow(aAbs.Row());
+
+ if (aAbsRange.aEnd.Tab() < aAbs.Tab())
+ aAbsRange.aEnd.SetTab(aAbs.Tab());
+
+ // In Ref2 inherit absolute/relative addressing from the extending part.
+ // A$5:A5 => A$5:A$5:A5 => A$5:A5, and not A$5:A$5
+ // A$6:$A5 => A$6:A$6:$A5 => A5:$A$6
+ if (aAbsRange.aEnd.Col() == aAbs.Col())
+ Ref2.SetColRel( rRef.IsColRel());
+ if (aAbsRange.aEnd.Row() == aAbs.Row())
+ Ref2.SetRowRel( rRef.IsRowRel());
+
+ // In Ref1 inherit relative sheet from extending part if given.
+ if (aAbsRange.aStart.Tab() == aAbs.Tab() && rRef.IsFlag3D())
+ Ref1.SetTabRel( rRef.IsTabRel());
+
+ // In Ref2 inherit relative sheet from either Ref1 or extending part.
+ // Use the original 3D flags to determine which.
+ // $Sheet1.$A$5:$A$6 => $Sheet1.$A$5:$A$5:$A$6 => $Sheet1.$A$5:$A$6, and
+ // not $Sheet1.$A$5:Sheet1.$A$6 (with invisible second 3D, but relative).
+ if (aAbsRange.aEnd.Tab() == aAbs.Tab())
+ Ref2.SetTabRel( bInherit3D ? Ref1.IsTabRel() : rRef.IsTabRel());
+
+ // Force 3D flag in Ref1 if different sheet or more than one sheet
+ // referenced.
+ if (aAbsRange.aStart.Tab() != rPos.Tab() || aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab())
+ Ref1.SetFlag3D(true);
+
+ // Force 3D flag in Ref2 if more than one sheet referenced.
+ if (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab())
+ Ref2.SetFlag3D(true);
+
+ // Inherit 3D flag in Ref1 from extending part in case range wasn't
+ // extended as in A5:A5:Sheet1.A5 if on Sheet1.
+ if (rRef.IsFlag3D())
+ Ref1.SetFlag3D( true);
+
+ // Inherit RelNameRef from extending part.
+ if (rRef.IsRelName())
+ Ref2.SetRelName(true);
+
+ SetRange(rLimits, aAbsRange, rPos);
+
+ return *this;
+}
+
+ScComplexRefData& ScComplexRefData::Extend( const ScSheetLimits& rLimits, const ScComplexRefData & rRef, const ScAddress & rPos )
+{
+ return Extend( rLimits, rRef.Ref1, rPos).Extend( rLimits, rRef.Ref2, rPos);
+}
+
+bool ScComplexRefData::Valid(const ScDocument& rDoc) const
+{
+ return Ref1.Valid(rDoc) && Ref2.Valid(rDoc);
+}
+
+bool ScComplexRefData::ValidExternal(const ScDocument& rDoc) const
+{
+ return Ref1.ValidExternal(rDoc) && Ref2.ColValid(rDoc) && Ref2.RowValid(rDoc) && Ref1.Tab() <= Ref2.Tab();
+}
+
+ScRange ScComplexRefData::toAbs( const ScDocument& rDoc, const ScAddress& rPos ) const
+{
+ return toAbs(rDoc.GetSheetLimits(), rPos);
+}
+
+ScRange ScComplexRefData::toAbs( const ScSheetLimits& rLimits, const ScAddress& rPos ) const
+{
+ return ScRange(Ref1.toAbs(rLimits, rPos), Ref2.toAbs(rLimits, rPos));
+}
+
+void ScComplexRefData::SetRange( const ScSheetLimits& rLimits, const ScRange& rRange, const ScAddress& rPos )
+{
+ Ref1.SetAddress(rLimits, rRange.aStart, rPos);
+ Ref2.SetAddress(rLimits, rRange.aEnd, rPos);
+}
+
+void ScComplexRefData::PutInOrder( const ScAddress& rPos )
+{
+ ScSingleRefData::PutInOrder( Ref1, Ref2, rPos);
+}
+
+bool ScComplexRefData::IsEntireCol( const ScSheetLimits& rLimits ) const
+{
+ // Both row anchors must be absolute.
+ return Ref1.Row() == 0 && Ref2.Row() == rLimits.MaxRow() && !Ref1.IsRowRel() && !Ref2.IsRowRel();
+}
+
+/** Whether this references entire rows, 1:1 */
+bool ScComplexRefData::IsEntireRow( const ScSheetLimits& rLimits ) const
+{
+ // Both column anchors must be absolute.
+ return Ref1.Col() == 0 && Ref2.Col() == rLimits.MaxCol() && !Ref1.IsColRel() && !Ref2.IsColRel();
+}
+
+bool ScComplexRefData::IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta, const ScAddress& rPos )
+{
+ SCCOL nCol1 = Ref1.IsColRel() ? Ref1.Col() + rPos.Col() : Ref1.Col();
+ SCCOL nCol2 = Ref2.IsColRel() ? Ref2.Col() + rPos.Col() : Ref2.Col();
+ if (nCol1 >= nCol2)
+ {
+ // Less than two columns => not sticky.
+ Ref2.IncCol( nDelta);
+ return true;
+ }
+
+ if (nCol2 == rDoc.MaxCol())
+ // already sticky
+ return false;
+
+ if (nCol2 < rDoc.MaxCol())
+ {
+ SCCOL nCol = ::std::min( static_cast<SCCOL>(nCol2 + nDelta), rDoc.MaxCol());
+ if (Ref2.IsColRel())
+ Ref2.SetRelCol( nCol - rPos.Col());
+ else
+ Ref2.SetAbsCol( nCol);
+ }
+ else
+ Ref2.IncCol( nDelta); // was greater than rDoc.MaxCol(), caller should know...
+
+ return true;
+}
+
+bool ScComplexRefData::IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta, const ScAddress& rPos )
+{
+ SCROW nRow1 = Ref1.IsRowRel() ? Ref1.Row() + rPos.Row() : Ref1.Row();
+ SCROW nRow2 = Ref2.IsRowRel() ? Ref2.Row() + rPos.Row() : Ref2.Row();
+ if (nRow1 >= nRow2)
+ {
+ // Less than two rows => not sticky.
+ Ref2.IncRow( nDelta);
+ return true;
+ }
+
+ if (nRow2 == rDoc.MaxRow())
+ // already sticky
+ return false;
+
+ if (nRow2 < rDoc.MaxRow())
+ {
+ SCROW nRow = ::std::min( static_cast<SCROW>(nRow2 + nDelta), rDoc.MaxRow());
+ if (Ref2.IsRowRel())
+ Ref2.SetRelRow( nRow - rPos.Row());
+ else
+ Ref2.SetAbsRow( nRow);
+ }
+ else
+ Ref2.IncRow( nDelta); // was greater than rDoc.MaxRow(), caller should know...
+
+ return true;
+}
+
+bool ScComplexRefData::IsDeleted() const
+{
+ return Ref1.IsDeleted() || Ref2.IsDeleted();
+}
+
+#if DEBUG_FORMULA_COMPILER
+void ScComplexRefData::Dump( int nIndent ) const
+{
+ std::string aIndent;
+ for (int i = 0; i < nIndent; ++i)
+ aIndent += " ";
+
+ cout << aIndent << "ref 1" << endl;
+ Ref1.Dump(nIndent+1);
+ cout << aIndent << "ref 2" << endl;
+ Ref2.Dump(nIndent+1);
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/reffind.cxx b/sc/source/core/tool/reffind.cxx
new file mode 100644
index 0000000000..10522310f8
--- /dev/null
+++ b/sc/source/core/tool/reffind.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/underlyingenumvalue.hxx>
+
+#include <reffind.hxx>
+#include <global.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <utility>
+
+namespace {
+
+// Include colon; addresses in range reference are handled individually.
+const sal_Unicode pDelimiters[] = {
+ '=','(',')','+','-','*','/','^','&',' ','{','}','<','>',':', 0
+};
+
+bool IsText( sal_Unicode c )
+{
+ bool bFound = ScGlobal::UnicodeStrChr( pDelimiters, c );
+ if (bFound)
+ // This is one of delimiters, therefore not text.
+ return false;
+
+ // argument separator is configurable.
+ const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
+ return c != sep;
+}
+
+bool IsText( bool& bQuote, sal_Unicode c )
+{
+ if (c == '\'')
+ {
+ bQuote = !bQuote;
+ return true;
+ }
+ if (bQuote)
+ return true;
+
+ return IsText(c);
+}
+
+/**
+ * Find first character position that is considered text. A character is
+ * considered a text when it's within the ascii range and when it's not a
+ * delimiter.
+ */
+sal_Int32 FindStartPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
+{
+ while (nStartPos <= nEndPos && !IsText(p[nStartPos]))
+ ++nStartPos;
+
+ return nStartPos;
+}
+
+sal_Int32 FindEndPosA1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
+{
+ bool bQuote = false;
+ sal_Int32 nNewEnd = nStartPos;
+ while (nNewEnd <= nEndPos && IsText(bQuote, p[nNewEnd]))
+ ++nNewEnd;
+
+ return nNewEnd;
+}
+
+sal_Int32 FindEndPosR1C1(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos)
+{
+ sal_Int32 nNewEnd = nStartPos;
+ p = &p[nStartPos];
+ for (; nNewEnd <= nEndPos; ++p, ++nNewEnd)
+ {
+ if (*p == '\'')
+ {
+ // Skip until the closing quote.
+ for (++p, ++nNewEnd; nNewEnd <= nEndPos; ++p, ++nNewEnd)
+ if (*p == '\'')
+ break;
+ if (nNewEnd > nEndPos)
+ break;
+ }
+ else if (*p == '[')
+ {
+ // Skip until the closing bracket.
+ for (++p, ++nNewEnd; nNewEnd <= nEndPos; ++p, ++nNewEnd)
+ if (*p == ']')
+ break;
+ if (nNewEnd > nEndPos)
+ break;
+ }
+ else if (!IsText(*p))
+ break;
+ }
+
+ return nNewEnd;
+}
+
+/**
+ * Find last character position that is considered text, from the specified
+ * start position.
+ */
+sal_Int32 FindEndPos(const sal_Unicode* p, sal_Int32 nStartPos, sal_Int32 nEndPos,
+ formula::FormulaGrammar::AddressConvention eConv)
+{
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ return FindEndPosR1C1(p, nStartPos, nEndPos);
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ default:
+ return FindEndPosA1(p, nStartPos, nEndPos);
+ }
+}
+
+void ExpandToTextA1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
+{
+ bool bQuote = false; // skip quoted text
+ while (rStartPos > 0 && IsText(bQuote, p[rStartPos - 1]) )
+ --rStartPos;
+ if (rEndPos)
+ --rEndPos;
+ while (rEndPos+1 < nLen && IsText(p[rEndPos + 1]) )
+ ++rEndPos;
+}
+
+void ExpandToTextR1C1(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos)
+{
+ // move back the start position to the first text character.
+ if (rStartPos > 0)
+ {
+ for (--rStartPos; rStartPos > 0; --rStartPos)
+ {
+ sal_Unicode c = p[rStartPos];
+ if (c == '\'')
+ {
+ // Skip until the opening quote.
+ for (--rStartPos; rStartPos > 0; --rStartPos)
+ {
+ c = p[rStartPos];
+ if (c == '\'')
+ break;
+ }
+ if (rStartPos == 0)
+ break;
+ }
+ else if (c == ']')
+ {
+ // Skip until the opening bracket.
+ for (--rStartPos; rStartPos > 0; --rStartPos)
+ {
+ c = p[rStartPos];
+ if (c == '[')
+ break;
+ }
+ if (rStartPos == 0)
+ break;
+ }
+ else if (!IsText(c))
+ {
+ ++rStartPos;
+ break;
+ }
+ }
+ }
+
+ // move forward the end position to the last text character.
+ rEndPos = FindEndPosR1C1(p, rEndPos, nLen-1);
+}
+
+void ExpandToText(const sal_Unicode* p, sal_Int32 nLen, sal_Int32& rStartPos, sal_Int32& rEndPos,
+ formula::FormulaGrammar::AddressConvention eConv)
+{
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ ExpandToTextR1C1(p, nLen, rStartPos, rEndPos);
+ break;
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ default:
+ ExpandToTextA1(p, nLen, rStartPos, rEndPos);
+ }
+}
+
+}
+
+ScRefFinder::ScRefFinder(
+ OUString aFormula, const ScAddress& rPos,
+ ScDocument& rDoc, formula::FormulaGrammar::AddressConvention eConvP) :
+ maFormula(std::move(aFormula)),
+ meConv(eConvP),
+ mrDoc(rDoc),
+ maPos(rPos),
+ mnFound(0),
+ mnSelStart(0),
+ mnSelEnd(0)
+{
+}
+
+ScRefFinder::~ScRefFinder()
+{
+}
+
+static ScRefFlags lcl_NextFlags( ScRefFlags nOld )
+{
+ const ScRefFlags Mask_ABS = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS | ScRefFlags::TAB_ABS;
+ ScRefFlags nNew = nOld & Mask_ABS;
+ nNew = ScRefFlags( o3tl::to_underlying(nNew) - 1 ) & Mask_ABS; // weiterzaehlen
+
+ if (!(nOld & ScRefFlags::TAB_3D))
+ nNew &= ~ScRefFlags::TAB_ABS; // not 3D -> never absolute!
+
+ return (nOld & ~Mask_ABS) | nNew;
+}
+
+void ScRefFinder::ToggleRel( sal_Int32 nStartPos, sal_Int32 nEndPos )
+{
+ sal_Int32 nLen = maFormula.getLength();
+ if (nLen <= 0)
+ return;
+ const sal_Unicode* pSource = maFormula.getStr(); // for quick access
+
+ // expand selection, and instead of selection start- and end-index
+
+ if ( nEndPos < nStartPos )
+ ::std::swap(nEndPos, nStartPos);
+
+ ExpandToText(pSource, nLen, nStartPos, nEndPos, meConv);
+
+ OUStringBuffer aResult;
+ OUString aExpr;
+ OUString aSep;
+ ScAddress aAddr;
+ mnFound = 0;
+
+ sal_Int32 nLoopStart = nStartPos;
+ while ( nLoopStart <= nEndPos )
+ {
+ // Determine the start and end positions of a text segment. Note that
+ // the end position returned from FindEndPos may be one position after
+ // the last character position in case of the last segment.
+ sal_Int32 nEStart = FindStartPos(pSource, nLoopStart, nEndPos);
+ sal_Int32 nEEnd = FindEndPos(pSource, nEStart, nEndPos, meConv);
+
+ aSep = maFormula.copy(nLoopStart, nEStart-nLoopStart);
+ if (nEEnd < maFormula.getLength())
+ aExpr = maFormula.copy(nEStart, nEEnd-nEStart);
+ else
+ aExpr = maFormula.copy(nEStart);
+
+ // Check the validity of the expression, and toggle the relative flag.
+ ScAddress::Details aDetails(meConv, maPos.Row(), maPos.Col());
+ ScAddress::ExternalInfo aExtInfo;
+ ScRefFlags nResult = aAddr.Parse(aExpr, mrDoc, aDetails, &aExtInfo);
+ if ( nResult & ScRefFlags::VALID )
+ {
+ ScRefFlags nFlags;
+ if( aExtInfo.mbExternal )
+ { // retain external doc name and tab name before toggle relative flag
+ sal_Int32 nSep;
+ switch(meConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_A1 :
+ case formula::FormulaGrammar::CONV_XL_OOX :
+ case formula::FormulaGrammar::CONV_XL_R1C1 :
+ nSep = aExpr.lastIndexOf('!');
+ break;
+ case formula::FormulaGrammar::CONV_OOO :
+ default:
+ nSep = aExpr.lastIndexOf('.');
+ break;
+ }
+ if (nSep >= 0)
+ {
+ OUString aRef = aExpr.copy(nSep+1);
+ std::u16string_view aExtDocNameTabName = aExpr.subView(0, nSep+1);
+ nResult = aAddr.Parse(aRef, mrDoc, aDetails);
+ aAddr.SetTab(0); // force to first tab to avoid error on checking
+ nFlags = lcl_NextFlags( nResult );
+ aExpr = aExtDocNameTabName + aAddr.Format(nFlags, &mrDoc, aDetails);
+ }
+ else
+ {
+ assert(!"Invalid syntax according to address convention.");
+ }
+ }
+ else
+ {
+ nFlags = lcl_NextFlags( nResult );
+ aExpr = aAddr.Format(nFlags, &mrDoc, aDetails);
+ }
+
+ sal_Int32 nAbsStart = nStartPos+aResult.getLength()+aSep.getLength();
+
+ if (!mnFound) // first reference ?
+ mnSelStart = nAbsStart;
+ mnSelEnd = nAbsStart + aExpr.getLength(); // selection, no indices
+ ++mnFound;
+ }
+
+ // assemble
+
+ aResult.append(aSep + aExpr);
+
+ nLoopStart = nEEnd;
+ }
+
+ OUString aTotal = maFormula.subView(0, nStartPos) + aResult;
+ if (nEndPos < maFormula.getLength()-1)
+ aTotal += maFormula.subView(nEndPos+1);
+
+ maFormula = aTotal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/refhint.cxx b/sc/source/core/tool/refhint.cxx
new file mode 100644
index 0000000000..a7245fd281
--- /dev/null
+++ b/sc/source/core/tool/refhint.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/.
+ */
+
+#include <refhint.hxx>
+
+namespace sc {
+
+RefHint::RefHint( Type eType ) : SfxHint(SfxHintId::ScReference), meType(eType) {}
+RefHint::~RefHint() {}
+
+RefHint::Type RefHint::getType() const
+{
+ return meType;
+}
+
+RefColReorderHint::RefColReorderHint( const sc::ColRowReorderMapType& rColMap, SCTAB nTab, SCROW nRow1, SCROW nRow2 ) :
+ RefHint(ColumnReordered), mrColMap(rColMap), mnTab(nTab), mnRow1(nRow1), mnRow2(nRow2) {}
+
+RefColReorderHint::~RefColReorderHint() {}
+
+const sc::ColRowReorderMapType& RefColReorderHint::getColMap() const
+{
+ return mrColMap;
+}
+
+SCTAB RefColReorderHint::getTab() const
+{
+ return mnTab;
+}
+
+SCROW RefColReorderHint::getStartRow() const
+{
+ return mnRow1;
+}
+
+SCROW RefColReorderHint::getEndRow() const
+{
+ return mnRow2;
+}
+
+RefRowReorderHint::RefRowReorderHint( const sc::ColRowReorderMapType& rRowMap, SCTAB nTab, SCCOL nCol1, SCCOL nCol2 ) :
+ RefHint(RowReordered), mrRowMap(rRowMap), mnTab(nTab), mnCol1(nCol1), mnCol2(nCol2) {}
+
+RefRowReorderHint::~RefRowReorderHint() {}
+
+const sc::ColRowReorderMapType& RefRowReorderHint::getRowMap() const
+{
+ return mrRowMap;
+}
+
+SCTAB RefRowReorderHint::getTab() const
+{
+ return mnTab;
+}
+
+SCCOL RefRowReorderHint::getStartColumn() const
+{
+ return mnCol1;
+}
+
+SCCOL RefRowReorderHint::getEndColumn() const
+{
+ return mnCol2;
+}
+
+RefStartListeningHint::RefStartListeningHint() : RefHint(StartListening) {}
+RefStartListeningHint::~RefStartListeningHint() {}
+
+RefStopListeningHint::RefStopListeningHint() : RefHint(StopListening) {}
+RefStopListeningHint::~RefStopListeningHint() {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/refreshtimer.cxx b/sc/source/core/tool/refreshtimer.cxx
new file mode 100644
index 0000000000..1ad6734d9c
--- /dev/null
+++ b/sc/source/core/tool/refreshtimer.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 <refreshtimer.hxx>
+#include <refreshtimerprotector.hxx>
+
+void ScRefreshTimerControl::SetAllowRefresh( bool b )
+{
+ if ( b && nBlockRefresh )
+ --nBlockRefresh;
+ else if ( !b && nBlockRefresh < sal_uInt16(~0) )
+ ++nBlockRefresh;
+}
+
+ScRefreshTimerProtector::ScRefreshTimerProtector( std::unique_ptr<ScRefreshTimerControl> const & rp )
+ :
+ m_rpControl( rp )
+{
+ if ( m_rpControl )
+ {
+ m_rpControl->SetAllowRefresh( false );
+ // wait for any running refresh in another thread to finish
+ std::scoped_lock aGuard( m_rpControl->GetMutex() );
+ }
+}
+
+ScRefreshTimerProtector::~ScRefreshTimerProtector()
+{
+ if ( m_rpControl )
+ m_rpControl->SetAllowRefresh( true );
+}
+
+ScRefreshTimer::ScRefreshTimer() : AutoTimer("ScRefreshTimer"), ppControl(nullptr)
+{
+ SetTimeout( 0 );
+}
+
+ScRefreshTimer::ScRefreshTimer( sal_Int32 nRefreshDelaySeconds ) : AutoTimer("ScRefreshTimer"), ppControl(nullptr)
+{
+ SetTimeout( nRefreshDelaySeconds * 1000 );
+ Launch();
+}
+
+ScRefreshTimer::ScRefreshTimer( const ScRefreshTimer& r ) : AutoTimer( r ), ppControl(nullptr)
+{
+}
+
+ScRefreshTimer::~ScRefreshTimer()
+{
+ if ( IsActive() )
+ Stop();
+}
+
+ScRefreshTimer& ScRefreshTimer::operator=( const ScRefreshTimer& r )
+{
+ if(this == &r)
+ return *this;
+
+ SetRefreshControl(nullptr);
+ AutoTimer::operator=( r );
+ return *this;
+}
+
+bool ScRefreshTimer::operator==( const ScRefreshTimer& r ) const
+{
+ return GetTimeout() == r.GetTimeout();
+}
+
+bool ScRefreshTimer::operator!=( const ScRefreshTimer& r ) const
+{
+ return !ScRefreshTimer::operator==( r );
+}
+
+void ScRefreshTimer::SetRefreshControl( std::unique_ptr<ScRefreshTimerControl> const * pp )
+{
+ ppControl = pp;
+}
+
+void ScRefreshTimer::SetRefreshHandler( const Link<Timer *, void>& rLink )
+{
+ SetInvokeHandler( rLink );
+}
+
+sal_Int32 ScRefreshTimer::GetRefreshDelaySeconds() const
+{
+ return GetTimeout() / 1000;
+}
+
+void ScRefreshTimer::StopRefreshTimer()
+{
+ Stop();
+}
+
+void ScRefreshTimer::SetRefreshDelay( sal_Int32 nSeconds )
+{
+ bool bActive = IsActive();
+ if ( bActive && !nSeconds )
+ Stop();
+ SetTimeout( nSeconds * 1000 );
+ if ( !bActive && nSeconds )
+ Launch();
+}
+
+void ScRefreshTimer::Invoke()
+{
+ if ( ppControl && *ppControl && (*ppControl)->IsRefreshAllowed() )
+ {
+ // now we COULD make the call in another thread ...
+ std::scoped_lock aGuard( (*ppControl)->GetMutex() );
+ Timer::Invoke();
+ // restart from now on, don't execute immediately again if timed out
+ // a second time during refresh
+ if ( IsActive() )
+ Launch();
+ }
+}
+
+void ScRefreshTimer::Launch()
+{
+ if ( GetTimeout() )
+ AutoTimer::Start();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/reftokenhelper.cxx b/sc/source/core/tool/reftokenhelper.cxx
new file mode 100644
index 0000000000..a4992485ee
--- /dev/null
+++ b/sc/source/core/tool/reftokenhelper.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 <reftokenhelper.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+
+#include <rtl/ustring.hxx>
+#include <formula/grammar.hxx>
+#include <formula/token.hxx>
+
+#include <memory>
+
+using namespace formula;
+
+using ::std::vector;
+
+void ScRefTokenHelper::compileRangeRepresentation(
+ vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument& rDoc,
+ const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
+{
+ // #i107275# ignore parentheses
+ OUString aRangeStr = rRangeStr;
+ while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
+ aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
+
+ bool bFailure = false;
+ sal_Int32 nOffset = 0;
+ while (nOffset >= 0 && !bFailure)
+ {
+ OUString aToken;
+ ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep);
+ if (nOffset < 0)
+ break;
+
+ ScCompiler aCompiler(rDoc, ScAddress(0,0,0), eGrammar);
+ std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
+
+ // There MUST be exactly one reference per range token and nothing
+ // else, and it MUST be a valid reference, not some #REF!
+ sal_uInt16 nLen = pArray->GetLen();
+ if (!nLen)
+ continue; // Should a missing range really be allowed?
+ if (nLen != 1)
+ {
+ bFailure = true;
+ break;
+ }
+
+ const FormulaToken* p = pArray->FirstToken();
+ if (!p)
+ {
+ bFailure = true;
+ break;
+ }
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ if (!rRef.Valid(rDoc))
+ bFailure = true;
+ else if (bOnly3DRef && !rRef.IsFlag3D())
+ bFailure = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (!rRef.Valid(rDoc))
+ bFailure = true;
+ else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
+ bFailure = true;
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ if (!p->GetSingleRef()->ValidExternal(rDoc))
+ bFailure = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ if (!p->GetDoubleRef()->ValidExternal(rDoc))
+ bFailure = true;
+ }
+ break;
+ case svString:
+ if (p->GetString().isEmpty())
+ bFailure = true;
+ break;
+ case svIndex:
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ ScRangeData* pNameRange = rDoc.FindRangeNameBySheetAndIndex(p->GetSheet(), p->GetIndex());
+ if (!pNameRange->HasReferences())
+ bFailure = true;
+ }
+ }
+ break;
+ default:
+ bFailure = true;
+ break;
+ }
+ if (!bFailure)
+ rRefTokens.emplace_back(p->Clone());
+
+ }
+ if (bFailure)
+ rRefTokens.clear();
+}
+
+bool ScRefTokenHelper::getRangeFromToken(
+ const ScDocument* pDoc,
+ ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
+{
+ StackVar eType = pToken->GetType();
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ if ((eType == svExternalSingleRef && !bExternal) ||
+ (eType == svSingleRef && bExternal))
+ return false;
+
+ const ScSingleRefData& rRefData = *pToken->GetSingleRef();
+ rRange.aStart = rRefData.toAbs(*pDoc, rPos);
+ rRange.aEnd = rRange.aStart;
+ return true;
+ }
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ {
+ if ((eType == svExternalDoubleRef && !bExternal) ||
+ (eType == svDoubleRef && bExternal))
+ return false;
+
+ const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
+ rRange = rRefData.toAbs(*pDoc, rPos);
+ return true;
+ }
+ case svIndex:
+ {
+ if (pToken->GetOpCode() == ocName)
+ {
+ ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(pToken->GetSheet(), pToken->GetIndex());
+ if (pNameRange->IsReference(rRange, rPos))
+ return true;
+ }
+ return false;
+ }
+ default:
+ ; // do nothing
+ }
+ return false;
+}
+
+void ScRefTokenHelper::getRangeListFromTokens(
+ const ScDocument* pDoc, ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
+{
+ for (const auto& rToken : rTokens)
+ {
+ ScRange aRange;
+ getRangeFromToken(pDoc, aRange, rToken, rPos);
+ rRangeList.push_back(aRange);
+ }
+}
+
+void ScRefTokenHelper::getTokenFromRange(const ScDocument* pDoc, ScTokenRef& pToken, const ScRange& rRange)
+{
+ ScComplexRefData aData;
+ aData.InitRange(rRange);
+ aData.Ref1.SetFlag3D(true);
+
+ // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
+ // different sheets.
+ aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
+
+ pToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aData));
+}
+
+void ScRefTokenHelper::getTokensFromRangeList(const ScDocument* pDoc, vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
+{
+ vector<ScTokenRef> aTokens;
+ size_t nCount = rRanges.size();
+ aTokens.reserve(nCount);
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const ScRange & rRange = rRanges[i];
+ ScTokenRef pToken;
+ ScRefTokenHelper::getTokenFromRange(pDoc, pToken, rRange);
+ aTokens.push_back(pToken);
+ }
+ pTokens.swap(aTokens);
+}
+
+bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
+{
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
+{
+ switch (pToken->GetType())
+ {
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+bool ScRefTokenHelper::intersects(
+ const ScDocument* pDoc,
+ const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
+{
+ if (!isRef(pToken))
+ return false;
+
+ bool bExternal = isExternalRef(pToken);
+ sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
+
+ ScRange aRange;
+ getRangeFromToken(pDoc, aRange, pToken, rPos, bExternal);
+
+ for (const ScTokenRef& p : rTokens)
+ {
+ if (!isRef(p))
+ continue;
+
+ if (bExternal != isExternalRef(p))
+ continue;
+
+ ScRange aRange2;
+ getRangeFromToken(pDoc, aRange2, p, rPos, bExternal);
+
+ if (bExternal && nFileId != p->GetIndex())
+ // different external file
+ continue;
+
+ if (aRange.Intersects(aRange2))
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+class JoinRefTokenRanges
+{
+public:
+ /**
+ * Insert a new reference token into the existing list of reference tokens,
+ * but in that process, try to join as many adjacent ranges as possible.
+ *
+ * @param rTokens existing list of reference tokens
+ * @param rToken new token
+ */
+ void operator() (const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
+ {
+ join(pDoc, rTokens, pToken, rPos);
+ }
+
+private:
+
+ /**
+ * Check two 1-dimensional ranges to see if they overlap each other.
+ *
+ * @param nMin1 min value of range 1
+ * @param nMax1 max value of range 1
+ * @param nMin2 min value of range 2
+ * @param nMax2 max value of range 2
+ * @param rNewMin min value of new range in case they overlap
+ * @param rNewMax max value of new range in case they overlap
+ */
+ template<typename T>
+ static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
+ {
+ bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
+ bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
+ if (bDisjoint1 || bDisjoint2)
+ // These two ranges cannot be joined. Move on.
+ return false;
+
+ T nMin = std::min(nMin1, nMin2);
+ T nMax = std::max(nMax1, nMax2);
+
+ rNewMin = nMin;
+ rNewMax = nMax;
+
+ return true;
+ }
+
+ void join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
+ {
+ // Normalize the token to a double reference.
+ ScComplexRefData aData;
+ if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
+ return;
+
+ // Get the information of the new token.
+ bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
+ sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
+ svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
+
+ bool bJoined = false;
+ for (ScTokenRef& pOldToken : rTokens)
+ {
+ if (!ScRefTokenHelper::isRef(pOldToken))
+ // A non-ref token should not have been added here in the first
+ // place!
+ continue;
+
+ if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
+ // External and internal refs don't mix.
+ continue;
+
+ if (bExternal)
+ {
+ if (nFileId != pOldToken->GetIndex())
+ // Different external files.
+ continue;
+
+ if (aTabName != pOldToken->GetString())
+ // Different table names.
+ continue;
+ }
+
+ ScComplexRefData aOldData;
+ if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
+ continue;
+
+ ScRange aOld = aOldData.toAbs(*pDoc, rPos), aNew = aData.toAbs(*pDoc, rPos);
+
+ if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
+ // Sheet ranges differ.
+ continue;
+
+ if (aOld.Contains(aNew))
+ // This new range is part of an existing range. Skip it.
+ return;
+
+ bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
+ bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
+ ScComplexRefData aNewData = aOldData;
+ bool bJoinRanges = false;
+ if (bSameRows)
+ {
+ SCCOL nNewMin, nNewMax;
+ bJoinRanges = overlaps(
+ aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
+ nNewMin, nNewMax);
+
+ if (bJoinRanges)
+ {
+ aNew.aStart.SetCol(nNewMin);
+ aNew.aEnd.SetCol(nNewMax);
+ aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
+ }
+ }
+ else if (bSameCols)
+ {
+ SCROW nNewMin, nNewMax;
+ bJoinRanges = overlaps(
+ aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
+ nNewMin, nNewMax);
+
+ if (bJoinRanges)
+ {
+ aNew.aStart.SetRow(nNewMin);
+ aNew.aEnd.SetRow(nNewMax);
+ aNewData.SetRange(pDoc->GetSheetLimits(), aNew, rPos);
+ }
+ }
+
+ if (bJoinRanges)
+ {
+ if (bExternal)
+ pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
+ else
+ pOldToken.reset(new ScDoubleRefToken(pDoc->GetSheetLimits(), aNewData));
+
+ bJoined = true;
+ break;
+ }
+ }
+
+ if (bJoined)
+ {
+ if (rTokens.size() == 1)
+ // There is only one left. No need to do more joining.
+ return;
+
+ // Pop the last token from the list, and keep joining recursively.
+ ScTokenRef p = rTokens.back();
+ rTokens.pop_back();
+ join(pDoc, rTokens, p, rPos);
+ }
+ else
+ rTokens.push_back(pToken);
+ }
+};
+
+}
+
+void ScRefTokenHelper::join(const ScDocument* pDoc, vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
+{
+ JoinRefTokenRanges join;
+ join(pDoc, rTokens, pToken, rPos);
+}
+
+bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
+{
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& r = *pToken->GetSingleRef();
+ rData.Ref1 = r;
+ rData.Ref1.SetFlag3D(true);
+ rData.Ref2 = r;
+ rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
+ }
+ break;
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ rData = *pToken->GetDoubleRef();
+ break;
+ default:
+ // Not a reference token. Bail out.
+ return false;
+ }
+ return true;
+}
+
+ScTokenRef ScRefTokenHelper::createRefToken(const ScDocument& rDoc, const ScAddress& rAddr)
+{
+ ScSingleRefData aRefData;
+ aRefData.InitAddress(rAddr);
+ ScTokenRef pRef(new ScSingleRefToken(rDoc.GetSheetLimits(), aRefData));
+ return pRef;
+}
+
+ScTokenRef ScRefTokenHelper::createRefToken(const ScDocument& rDoc, const ScRange& rRange)
+{
+ ScComplexRefData aRefData;
+ aRefData.InitRange(rRange);
+ ScTokenRef pRef(new ScDoubleRefToken(rDoc.GetSheetLimits(), aRefData));
+ return pRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/refupdat.cxx b/sc/source/core/tool/refupdat.cxx
new file mode 100644
index 0000000000..95f738c4ed
--- /dev/null
+++ b/sc/source/core/tool/refupdat.cxx
@@ -0,0 +1,592 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <refupdat.hxx>
+#include <document.hxx>
+#include <bigrange.hxx>
+#include <refdata.hxx>
+
+#include <osl/diagnose.h>
+
+template< typename R, typename S, typename U >
+static bool lcl_MoveStart( R& rRef, U nStart, S nDelta, U nMask, bool bShrink = true )
+{
+ bool bCut = false;
+ if ( rRef >= nStart )
+ rRef = sal::static_int_cast<R>( rRef + nDelta );
+ else if ( nDelta < 0 && bShrink && rRef >= nStart + nDelta )
+ rRef = nStart + nDelta; //TODO: limit ???
+ if ( rRef < 0 )
+ {
+ rRef = 0;
+ bCut = true;
+ }
+ else if ( rRef > nMask )
+ {
+ rRef = nMask;
+ bCut = true;
+ }
+ return bCut;
+}
+
+template< typename R, typename S, typename U >
+static bool lcl_MoveEnd( R& rRef, U nStart, S nDelta, U nMask, bool bShrink = true )
+{
+ bool bCut = false;
+ if ( rRef >= nStart )
+ rRef = sal::static_int_cast<R>( rRef + nDelta );
+ else if ( nDelta < 0 && bShrink && rRef >= nStart + nDelta )
+ rRef = nStart + nDelta - 1; //TODO: limit ???
+ if (rRef < 0)
+ {
+ rRef = 0;
+ bCut = true;
+ }
+ else if(rRef > nMask)
+ {
+ rRef = nMask;
+ bCut = true;
+ }
+ return bCut;
+}
+
+template< typename R, typename S, typename U >
+static bool lcl_MoveReorder( R& rRef, U nStart, U nEnd, S nDelta )
+{
+ if ( rRef >= nStart && rRef <= nEnd )
+ {
+ rRef = sal::static_int_cast<R>( rRef + nDelta );
+ return true;
+ }
+
+ if ( nDelta > 0 ) // move backward
+ {
+ if ( rRef >= nStart && rRef <= nEnd + nDelta )
+ {
+ if ( rRef <= nEnd )
+ rRef = sal::static_int_cast<R>( rRef + nDelta ); // in the moved range
+ else
+ rRef -= nEnd - nStart + 1; // move up
+ return true;
+ }
+ }
+ else // move forward
+ {
+ if ( rRef >= nStart + nDelta && rRef <= nEnd )
+ {
+ if ( rRef >= nStart )
+ rRef = sal::static_int_cast<R>( rRef + nDelta ); // in the moved range
+ else
+ rRef += nEnd - nStart + 1; // move up
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template< typename R, typename S, typename U >
+static bool lcl_MoveItCut( R& rRef, S nDelta, U nMask )
+{
+ bool bCut = false;
+ rRef = sal::static_int_cast<R>( rRef + nDelta );
+ if ( rRef < 0 )
+ {
+ rRef = 0;
+ bCut = true;
+ }
+ else if ( rRef > nMask )
+ {
+ rRef = nMask;
+ bCut = true;
+ }
+ return bCut;
+}
+
+template< typename R, typename U >
+static void lcl_MoveItWrap( R& rRef, U nMask )
+{
+ rRef = sal::static_int_cast<R>( rRef );
+ if ( rRef < 0 )
+ rRef += nMask+1;
+ else if ( rRef > nMask )
+ rRef -= nMask+1;
+}
+
+template< typename R, typename S, typename U >
+static bool IsExpand( R n1, R n2, U nStart, S nD )
+{ // before normal Move...
+ return
+ nD > 0 // Insert
+ && n1 < n2 // at least two Cols/Rows/Tabs in Ref
+ && (
+ (nStart <= n1 && n1 < nStart + nD) // n1 within the Insert
+ || (n2 + 1 == nStart) // n2 directly before Insert
+ ); // n1 < nStart <= n2 is expanded anyway!
+}
+
+template< typename R, typename S, typename U >
+static void Expand( R& n1, R& n2, U nStart, S nD )
+{ // after normal Move..., only if IsExpand was true before!
+ // first the End
+ if ( n2 + 1 == nStart )
+ { // at End
+ n2 = sal::static_int_cast<R>( n2 + nD );
+ return;
+ }
+ // at the beginning
+ n1 = sal::static_int_cast<R>( n1 - nD );
+}
+
+static bool lcl_IsWrapBig( sal_Int64 nRef, sal_Int32 nDelta )
+{
+ if ( nRef > 0 && nDelta > 0 )
+ return nRef + nDelta <= 0;
+ else if ( nRef < 0 && nDelta < 0 )
+ return nRef + nDelta >= 0;
+ return false;
+}
+
+static bool lcl_MoveBig( sal_Int64& rRef, sal_Int64 nStart, sal_Int32 nDelta )
+{
+ bool bCut = false;
+ if ( rRef >= nStart )
+ {
+ if ( nDelta > 0 )
+ bCut = lcl_IsWrapBig( rRef, nDelta );
+ if ( bCut )
+ rRef = ScBigRange::nRangeMax;
+ else
+ rRef += nDelta;
+ }
+ return bCut;
+}
+
+static bool lcl_MoveItCutBig( sal_Int64& rRef, sal_Int32 nDelta )
+{
+ bool bCut = lcl_IsWrapBig( rRef, nDelta );
+ rRef += nDelta;
+ return bCut;
+}
+
+ScRefUpdateRes ScRefUpdate::Update( const ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
+ SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ SCCOL nDx, SCROW nDy, SCTAB nDz,
+ SCCOL& theCol1, SCROW& theRow1, SCTAB& theTab1,
+ SCCOL& theCol2, SCROW& theRow2, SCTAB& theTab2 )
+{
+ ScRefUpdateRes eRet = UR_NOTHING;
+
+ SCCOL oldCol1 = theCol1;
+ SCROW oldRow1 = theRow1;
+ SCTAB oldTab1 = theTab1;
+ SCCOL oldCol2 = theCol2;
+ SCROW oldRow2 = theRow2;
+ SCTAB oldTab2 = theTab2;
+
+ bool bCut1, bCut2;
+
+ if (eUpdateRefMode == URM_INSDEL)
+ {
+ bool bExpand = pDoc->IsExpandRefs();
+ if ( nDx && (theRow1 >= nRow1) && (theRow2 <= nRow2) &&
+ (theTab1 >= nTab1) && (theTab2 <= nTab2))
+ {
+ bool bExp = (bExpand && IsExpand( theCol1, theCol2, nCol1, nDx ));
+ bCut1 = lcl_MoveStart( theCol1, nCol1, nDx, pDoc->MaxCol() );
+ bCut2 = lcl_MoveEnd( theCol2, nCol1, nDx, pDoc->MaxCol() );
+ if ( theCol2 < theCol1 )
+ {
+ eRet = UR_INVALID;
+ theCol2 = theCol1;
+ }
+ else if (bCut2 && theCol2 == 0)
+ eRet = UR_INVALID;
+ else if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ if ( bExp )
+ {
+ Expand( theCol1, theCol2, nCol1, nDx );
+ eRet = UR_UPDATED;
+ }
+ if (eRet != UR_NOTHING && oldCol1 == 0 && oldCol2 == pDoc->MaxCol())
+ {
+ eRet = UR_STICKY;
+ theCol1 = oldCol1;
+ theCol2 = oldCol2;
+ }
+ else if (oldCol2 == pDoc->MaxCol() && oldCol1 < pDoc->MaxCol())
+ {
+ // End was sticky, but start may have been moved. Only on range.
+ theCol2 = oldCol2;
+ if (eRet == UR_NOTHING)
+ eRet = UR_STICKY;
+ }
+ // Else, if (bCut2 && theCol2 == pDoc->MaxCol()) then end becomes sticky,
+ // but currently there's nothing to do.
+ }
+ if ( nDy && (theCol1 >= nCol1) && (theCol2 <= nCol2) &&
+ (theTab1 >= nTab1) && (theTab2 <= nTab2))
+ {
+ bool bExp = (bExpand && IsExpand( theRow1, theRow2, nRow1, nDy ));
+ bCut1 = lcl_MoveStart( theRow1, nRow1, nDy, pDoc->MaxRow() );
+ bCut2 = lcl_MoveEnd( theRow2, nRow1, nDy, pDoc->MaxRow() );
+ if ( theRow2 < theRow1 )
+ {
+ eRet = UR_INVALID;
+ theRow2 = theRow1;
+ }
+ else if (bCut2 && theRow2 == 0)
+ eRet = UR_INVALID;
+ else if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ if ( bExp )
+ {
+ Expand( theRow1, theRow2, nRow1, nDy );
+ eRet = UR_UPDATED;
+ }
+ if (eRet != UR_NOTHING && oldRow1 == 0 && oldRow2 == pDoc->MaxRow())
+ {
+ eRet = UR_STICKY;
+ theRow1 = oldRow1;
+ theRow2 = oldRow2;
+ }
+ else if (oldRow2 == pDoc->MaxRow() && oldRow1 < pDoc->MaxRow())
+ {
+ // End was sticky, but start may have been moved. Only on range.
+ theRow2 = oldRow2;
+ if (eRet == UR_NOTHING)
+ eRet = UR_STICKY;
+ }
+ // Else, if (bCut2 && theRow2 == pDoc->MaxRow()) then end becomes sticky,
+ // but currently there's nothing to do.
+ }
+ if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) &&
+ (theRow1 >= nRow1) && (theRow2 <= nRow2) )
+ {
+ SCTAB nMaxTab = pDoc->GetTableCount() - 1;
+ nMaxTab = sal::static_int_cast<SCTAB>(nMaxTab + nDz); // adjust to new count
+ bool bExp = (bExpand && IsExpand( theTab1, theTab2, nTab1, nDz ));
+ bCut1 = lcl_MoveStart( theTab1, nTab1, nDz, nMaxTab, false /*bShrink*/);
+ bCut2 = lcl_MoveEnd( theTab2, nTab1, nDz, nMaxTab, false /*bShrink*/);
+ if ( theTab2 < theTab1 )
+ {
+ eRet = UR_INVALID;
+ theTab2 = theTab1;
+ }
+ else if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ if ( bExp )
+ {
+ Expand( theTab1, theTab2, nTab1, nDz );
+ eRet = UR_UPDATED;
+ }
+ }
+ }
+ else if (eUpdateRefMode == URM_MOVE)
+ {
+ if ((theCol1 >= nCol1-nDx) && (theRow1 >= nRow1-nDy) && (theTab1 >= nTab1-nDz) &&
+ (theCol2 <= nCol2-nDx) && (theRow2 <= nRow2-nDy) && (theTab2 <= nTab2-nDz))
+ {
+ if ( nDx )
+ {
+ bCut1 = lcl_MoveItCut( theCol1, nDx, pDoc->MaxCol() );
+ bCut2 = lcl_MoveItCut( theCol2, nDx, pDoc->MaxCol() );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ if (eRet != UR_NOTHING && oldCol1 == 0 && oldCol2 == pDoc->MaxCol())
+ {
+ eRet = UR_STICKY;
+ theCol1 = oldCol1;
+ theCol2 = oldCol2;
+ }
+ }
+ if ( nDy )
+ {
+ bCut1 = lcl_MoveItCut( theRow1, nDy, pDoc->MaxRow() );
+ bCut2 = lcl_MoveItCut( theRow2, nDy, pDoc->MaxRow() );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ if (eRet != UR_NOTHING && oldRow1 == 0 && oldRow2 == pDoc->MaxRow())
+ {
+ eRet = UR_STICKY;
+ theRow1 = oldRow1;
+ theRow2 = oldRow2;
+ }
+ }
+ if ( nDz )
+ {
+ SCTAB nMaxTab = pDoc->GetTableCount() - 1;
+ bCut1 = lcl_MoveItCut( theTab1, nDz, nMaxTab );
+ bCut2 = lcl_MoveItCut( theTab2, nDz, nMaxTab );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ }
+ }
+ }
+ else if (eUpdateRefMode == URM_REORDER)
+ {
+ // so far only for nDz (MoveTab)
+ OSL_ENSURE ( !nDx && !nDy, "URM_REORDER for x and y not yet implemented" );
+
+ if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) &&
+ (theRow1 >= nRow1) && (theRow2 <= nRow2) )
+ {
+ bCut1 = lcl_MoveReorder( theTab1, nTab1, nTab2, nDz );
+ bCut2 = lcl_MoveReorder( theTab2, nTab1, nTab2, nDz );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ }
+ }
+
+ if ( eRet == UR_NOTHING )
+ {
+ if (oldCol1 != theCol1
+ || oldRow1 != theRow1
+ || oldTab1 != theTab1
+ || oldCol2 != theCol2
+ || oldRow2 != theRow2
+ || oldTab2 != theTab2
+ )
+ eRet = UR_UPDATED;
+ }
+ return eRet;
+}
+
+// simple UpdateReference for ScBigRange (ScChangeAction/ScChangeTrack)
+// References can also be located outside of the document!
+// Whole columns/rows (ScBigRange::nRangeMin..ScBigRange::nRangeMax) stay as such!
+ScRefUpdateRes ScRefUpdate::Update( UpdateRefMode eUpdateRefMode,
+ const ScBigRange& rWhere, sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz,
+ ScBigRange& rWhat )
+{
+ ScRefUpdateRes eRet = UR_NOTHING;
+ const ScBigRange aOldRange( rWhat );
+
+ sal_Int64 nCol1, nRow1, nTab1, nCol2, nRow2, nTab2;
+ sal_Int64 theCol1, theRow1, theTab1, theCol2, theRow2, theTab2;
+ rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ rWhat.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
+
+ bool bCut1, bCut2;
+
+ if (eUpdateRefMode == URM_INSDEL)
+ {
+ if ( nDx && (theRow1 >= nRow1) && (theRow2 <= nRow2) &&
+ (theTab1 >= nTab1) && (theTab2 <= nTab2) &&
+ (theCol1 != ScBigRange::nRangeMin || theCol2 != ScBigRange::nRangeMax) )
+ {
+ bCut1 = lcl_MoveBig( theCol1, nCol1, nDx );
+ bCut2 = lcl_MoveBig( theCol2, nCol1, nDx );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ rWhat.aStart.SetCol( theCol1 );
+ rWhat.aEnd.SetCol( theCol2 );
+ }
+ if ( nDy && (theCol1 >= nCol1) && (theCol2 <= nCol2) &&
+ (theTab1 >= nTab1) && (theTab2 <= nTab2) &&
+ (theRow1 != ScBigRange::nRangeMin || theRow2 != ScBigRange::nRangeMax) )
+ {
+ bCut1 = lcl_MoveBig( theRow1, nRow1, nDy );
+ bCut2 = lcl_MoveBig( theRow2, nRow1, nDy );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ rWhat.aStart.SetRow( theRow1 );
+ rWhat.aEnd.SetRow( theRow2 );
+ }
+ if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) &&
+ (theRow1 >= nRow1) && (theRow2 <= nRow2) &&
+ (theTab1 != ScBigRange::nRangeMin || theTab2 != ScBigRange::nRangeMax) )
+ {
+ bCut1 = lcl_MoveBig( theTab1, nTab1, nDz );
+ bCut2 = lcl_MoveBig( theTab2, nTab1, nDz );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ rWhat.aStart.SetTab( theTab1 );
+ rWhat.aEnd.SetTab( theTab2 );
+ }
+ }
+ else if (eUpdateRefMode == URM_MOVE)
+ {
+ if ( rWhere.Contains( rWhat ) )
+ {
+ if ( nDx && (theCol1 != ScBigRange::nRangeMin || theCol2 != ScBigRange::nRangeMax) )
+ {
+ bCut1 = lcl_MoveItCutBig( theCol1, nDx );
+ bCut2 = lcl_MoveItCutBig( theCol2, nDx );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ rWhat.aStart.SetCol( theCol1 );
+ rWhat.aEnd.SetCol( theCol2 );
+ }
+ if ( nDy && (theRow1 != ScBigRange::nRangeMin || theRow2 != ScBigRange::nRangeMax) )
+ {
+ bCut1 = lcl_MoveItCutBig( theRow1, nDy );
+ bCut2 = lcl_MoveItCutBig( theRow2, nDy );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ rWhat.aStart.SetRow( theRow1 );
+ rWhat.aEnd.SetRow( theRow2 );
+ }
+ if ( nDz && (theTab1 != ScBigRange::nRangeMin || theTab2 != ScBigRange::nRangeMax) )
+ {
+ bCut1 = lcl_MoveItCutBig( theTab1, nDz );
+ bCut2 = lcl_MoveItCutBig( theTab2, nDz );
+ if ( bCut1 || bCut2 )
+ eRet = UR_UPDATED;
+ rWhat.aStart.SetTab( theTab1 );
+ rWhat.aEnd.SetTab( theTab2 );
+ }
+ }
+ }
+
+ if ( eRet == UR_NOTHING && rWhat != aOldRange )
+ eRet = UR_UPDATED;
+
+ return eRet;
+}
+
+void ScRefUpdate::MoveRelWrap( const ScDocument& rDoc, const ScAddress& rPos,
+ SCCOL nMaxCol, SCROW nMaxRow, ScComplexRefData& rRef )
+{
+ ScRange aAbsRange = rRef.toAbs(rDoc, rPos);
+ if( rRef.Ref1.IsColRel() )
+ {
+ SCCOL nCol = aAbsRange.aStart.Col();
+ lcl_MoveItWrap(nCol, nMaxCol);
+ aAbsRange.aStart.SetCol(nCol);
+ }
+ if( rRef.Ref2.IsColRel() )
+ {
+ SCCOL nCol = aAbsRange.aEnd.Col();
+ lcl_MoveItWrap(nCol, nMaxCol);
+ aAbsRange.aEnd.SetCol(nCol);
+ }
+ if( rRef.Ref1.IsRowRel() )
+ {
+ SCROW nRow = aAbsRange.aStart.Row();
+ lcl_MoveItWrap(nRow, nMaxRow);
+ aAbsRange.aStart.SetRow(nRow);
+ }
+ if( rRef.Ref2.IsRowRel() )
+ {
+ SCROW nRow = aAbsRange.aEnd.Row();
+ lcl_MoveItWrap(nRow, nMaxRow);
+ aAbsRange.aEnd.SetRow(nRow);
+ }
+ SCTAB nMaxTab = rDoc.GetTableCount() - 1;
+ if( rRef.Ref1.IsTabRel() )
+ {
+ SCTAB nTab = aAbsRange.aStart.Tab();
+ lcl_MoveItWrap(nTab, nMaxTab);
+ aAbsRange.aStart.SetTab(nTab);
+ }
+ if( rRef.Ref2.IsTabRel() )
+ {
+ SCTAB nTab = aAbsRange.aEnd.Tab();
+ lcl_MoveItWrap(nTab, nMaxTab);
+ aAbsRange.aEnd.SetTab(nTab);
+ }
+
+ aAbsRange.PutInOrder();
+ rRef.SetRange(rDoc.GetSheetLimits(), aAbsRange, rPos);
+}
+
+void ScRefUpdate::DoTranspose( SCCOL& rCol, SCROW& rRow, SCTAB& rTab,
+ const ScDocument& rDoc, const ScRange& rSource, const ScAddress& rDest )
+{
+ SCTAB nDz = rDest.Tab() - rSource.aStart.Tab();
+ if (nDz)
+ {
+ SCTAB nNewTab = rTab+nDz;
+ SCTAB nCount = rDoc.GetTableCount();
+ while (nNewTab<0) nNewTab = sal::static_int_cast<SCTAB>( nNewTab + nCount );
+ while (nNewTab>=nCount) nNewTab = sal::static_int_cast<SCTAB>( nNewTab - nCount );
+ rTab = nNewTab;
+ }
+ OSL_ENSURE( rCol>=rSource.aStart.Col() && rRow>=rSource.aStart.Row(),
+ "UpdateTranspose: pos. wrong" );
+
+ SCCOL nRelX = rCol - rSource.aStart.Col();
+ SCROW nRelY = rRow - rSource.aStart.Row();
+
+ rCol = static_cast<SCCOL>(static_cast<SCCOLROW>(rDest.Col()) +
+ static_cast<SCCOLROW>(nRelY));
+ rRow = static_cast<SCROW>(static_cast<SCCOLROW>(rDest.Row()) +
+ static_cast<SCCOLROW>(nRelX));
+}
+
+ScRefUpdateRes ScRefUpdate::UpdateTranspose(
+ const ScDocument& rDoc, const ScRange& rSource, const ScAddress& rDest, ScRange& rRef )
+{
+ ScRefUpdateRes eRet = UR_NOTHING;
+ // Only references in source range must be updated, i.e. no references in destination area.
+ // Otherwise existing references pointing to destination area will be wrongly transposed.
+ if (rSource.Contains(rRef))
+ {
+ // Source range contains the reference range.
+ SCCOL nCol1 = rRef.aStart.Col(), nCol2 = rRef.aEnd.Col();
+ SCROW nRow1 = rRef.aStart.Row(), nRow2 = rRef.aEnd.Row();
+ SCTAB nTab1 = rRef.aStart.Tab(), nTab2 = rRef.aEnd.Tab();
+ DoTranspose(nCol1, nRow1, nTab1, rDoc, rSource, rDest);
+ DoTranspose(nCol2, nRow2, nTab2, rDoc, rSource, rDest);
+ rRef.aStart = ScAddress(nCol1, nRow1, nTab1);
+ rRef.aEnd = ScAddress(nCol2, nRow2, nTab2);
+ eRet = UR_UPDATED;
+ }
+ return eRet;
+}
+
+// UpdateGrow - expands references which point exactly to the area
+// gets by without document
+
+ScRefUpdateRes ScRefUpdate::UpdateGrow(
+ const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY, ScRange& rRef )
+{
+ ScRefUpdateRes eRet = UR_NOTHING;
+
+ // in y-direction the Ref may also start one row further below,
+ // if an area contains column heads
+
+ bool bUpdateX = ( nGrowX &&
+ rRef.aStart.Col() == rArea.aStart.Col() && rRef.aEnd.Col() == rArea.aEnd.Col() &&
+ rRef.aStart.Row() >= rArea.aStart.Row() && rRef.aEnd.Row() <= rArea.aEnd.Row() &&
+ rRef.aStart.Tab() >= rArea.aStart.Tab() && rRef.aEnd.Tab() <= rArea.aEnd.Tab() );
+ bool bUpdateY = ( nGrowY &&
+ rRef.aStart.Col() >= rArea.aStart.Col() && rRef.aEnd.Col() <= rArea.aEnd.Col() &&
+ (rRef.aStart.Row() == rArea.aStart.Row() || rRef.aStart.Row() == rArea.aStart.Row()+1) &&
+ rRef.aEnd.Row() == rArea.aEnd.Row() &&
+ rRef.aStart.Tab() >= rArea.aStart.Tab() && rRef.aEnd.Tab() <= rArea.aEnd.Tab() );
+
+ if ( bUpdateX )
+ {
+ rRef.aEnd.SetCol(sal::static_int_cast<SCCOL>(rRef.aEnd.Col() + nGrowX));
+ eRet = UR_UPDATED;
+ }
+ if ( bUpdateY )
+ {
+ rRef.aEnd.SetRow(sal::static_int_cast<SCROW>(rRef.aEnd.Row() + nGrowY));
+ eRet = UR_UPDATED;
+ }
+
+ return eRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/scmatrix.cxx b/sc/source/core/tool/scmatrix.cxx
new file mode 100644
index 0000000000..f62990b76b
--- /dev/null
+++ b/sc/source/core/tool/scmatrix.cxx
@@ -0,0 +1,3645 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scmatrix.hxx>
+#include <global.hxx>
+#include <address.hxx>
+#include <formula/errorcodes.hxx>
+#include <interpre.hxx>
+#include <mtvelements.hxx>
+#include <compare.hxx>
+#include <matrixoperators.hxx>
+#include <math.hxx>
+
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstring.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+#include <limits>
+
+#include <mdds/multi_type_matrix.hpp>
+#include <mdds/multi_type_vector/types.hpp>
+
+#if DEBUG_MATRIX
+#include <iostream>
+using std::cout;
+using std::endl;
+#endif
+
+using ::std::pair;
+using ::std::advance;
+
+namespace {
+
+/**
+ * Custom string trait struct to tell mdds::multi_type_matrix about the
+ * custom string type and how to handle blocks storing them.
+ */
+struct matrix_traits
+{
+ typedef sc::string_block string_element_block;
+ typedef sc::uint16_block integer_element_block;
+};
+
+struct matrix_flag_traits
+{
+ typedef sc::string_block string_element_block;
+ typedef mdds::mtv::uint8_element_block integer_element_block;
+};
+
+}
+
+typedef mdds::multi_type_matrix<matrix_traits> MatrixImplType;
+typedef mdds::multi_type_matrix<matrix_flag_traits> MatrixFlagImplType;
+
+namespace {
+
+double convertStringToValue( ScInterpreter* pErrorInterpreter, const OUString& rStr )
+{
+ if (pErrorInterpreter)
+ {
+ FormulaError nError = FormulaError::NONE;
+ SvNumFormatType nCurFmtType = SvNumFormatType::ALL;
+ double fValue = pErrorInterpreter->ConvertStringToValue( rStr, nError, nCurFmtType);
+ if (nError != FormulaError::NONE)
+ {
+ pErrorInterpreter->SetError( nError);
+ return CreateDoubleError( nError);
+ }
+ return fValue;
+ }
+ return CreateDoubleError( FormulaError::NoValue);
+}
+
+struct ElemEqualZero
+{
+ double operator() (double val) const
+ {
+ if (!std::isfinite(val))
+ return val;
+ return val == 0.0 ? 1.0 : 0.0;
+ }
+};
+
+struct ElemNotEqualZero
+{
+ double operator() (double val) const
+ {
+ if (!std::isfinite(val))
+ return val;
+ return val != 0.0 ? 1.0 : 0.0;
+ }
+};
+
+struct ElemGreaterZero
+{
+ double operator() (double val) const
+ {
+ if (!std::isfinite(val))
+ return val;
+ return val > 0.0 ? 1.0 : 0.0;
+ }
+};
+
+struct ElemLessZero
+{
+ double operator() (double val) const
+ {
+ if (!std::isfinite(val))
+ return val;
+ return val < 0.0 ? 1.0 : 0.0;
+ }
+};
+
+struct ElemGreaterEqualZero
+{
+ double operator() (double val) const
+ {
+ if (!std::isfinite(val))
+ return val;
+ return val >= 0.0 ? 1.0 : 0.0;
+ }
+};
+
+struct ElemLessEqualZero
+{
+ double operator() (double val) const
+ {
+ if (!std::isfinite(val))
+ return val;
+ return val <= 0.0 ? 1.0 : 0.0;
+ }
+};
+
+template<typename Comp>
+class CompareMatrixElemFunc
+{
+ static Comp maComp;
+
+ std::vector<double> maNewMatValues; // double instead of bool to transport error values
+ size_t mnRow;
+ size_t mnCol;
+public:
+ CompareMatrixElemFunc( size_t nRow, size_t nCol ) : mnRow(nRow), mnCol(nCol)
+ {
+ maNewMatValues.reserve(nRow*nCol);
+ }
+
+ CompareMatrixElemFunc( const CompareMatrixElemFunc& ) = delete;
+ CompareMatrixElemFunc& operator= ( const CompareMatrixElemFunc& ) = delete;
+
+ CompareMatrixElemFunc( CompareMatrixElemFunc&& ) = default;
+ CompareMatrixElemFunc& operator= ( CompareMatrixElemFunc&& ) = default;
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ double fVal = *it;
+ maNewMatValues.push_back(maComp(fVal));
+ }
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ double fVal = *it ? 1.0 : 0.0;
+ maNewMatValues.push_back(maComp(fVal));
+ }
+ }
+ break;
+ case mdds::mtm::element_string:
+ case mdds::mtm::element_empty:
+ default:
+ // Fill it with false.
+ maNewMatValues.resize(maNewMatValues.size() + node.size, 0.0);
+ }
+ }
+
+ void swap( MatrixImplType& rMat )
+ {
+ MatrixImplType aNewMat(mnRow, mnCol, maNewMatValues.begin(), maNewMatValues.end());
+ rMat.swap(aNewMat);
+ }
+};
+
+template<typename Comp>
+Comp CompareMatrixElemFunc<Comp>::maComp;
+
+}
+
+typedef uint8_t TMatFlag;
+const TMatFlag SC_MATFLAG_EMPTYRESULT = 1;
+const TMatFlag SC_MATFLAG_EMPTYPATH = 2;
+
+class ScMatrixImpl
+{
+ MatrixImplType maMat;
+ MatrixFlagImplType maMatFlag;
+ ScInterpreter* pErrorInterpreter;
+
+public:
+ ScMatrixImpl(const ScMatrixImpl&) = delete;
+ const ScMatrixImpl& operator=(const ScMatrixImpl&) = delete;
+
+ ScMatrixImpl(SCSIZE nC, SCSIZE nR);
+ ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal);
+
+ ScMatrixImpl( size_t nC, size_t nR, const std::vector<double>& rInitVals );
+
+ ~ScMatrixImpl();
+
+ void Clear();
+ void Resize(SCSIZE nC, SCSIZE nR);
+ void Resize(SCSIZE nC, SCSIZE nR, double fVal);
+ void SetErrorInterpreter( ScInterpreter* p);
+ ScInterpreter* GetErrorInterpreter() const { return pErrorInterpreter; }
+
+ void GetDimensions( SCSIZE& rC, SCSIZE& rR) const;
+ SCSIZE GetElementCount() const;
+ bool ValidColRow( SCSIZE nC, SCSIZE nR) const;
+ bool ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const;
+ bool ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const;
+ void SetErrorAtInterpreter( FormulaError nError ) const;
+
+ void PutDouble(double fVal, SCSIZE nC, SCSIZE nR);
+ void PutDouble( double fVal, SCSIZE nIndex);
+ void PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR);
+
+ void PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR);
+ void PutString(const svl::SharedString& rStr, SCSIZE nIndex);
+ void PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR);
+
+ void PutEmpty(SCSIZE nC, SCSIZE nR);
+ void PutEmptyPath(SCSIZE nC, SCSIZE nR);
+ void PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR );
+ void PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR);
+ FormulaError GetError( SCSIZE nC, SCSIZE nR) const;
+ double GetDouble(SCSIZE nC, SCSIZE nR) const;
+ double GetDouble( SCSIZE nIndex) const;
+ double GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const;
+ svl::SharedString GetString(SCSIZE nC, SCSIZE nR) const;
+ svl::SharedString GetString( SCSIZE nIndex) const;
+ svl::SharedString GetString( SvNumberFormatter& rFormatter, SCSIZE nC, SCSIZE nR) const;
+ ScMatrixValue Get(SCSIZE nC, SCSIZE nR) const;
+ bool IsStringOrEmpty( SCSIZE nIndex ) const;
+ bool IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const;
+ bool IsEmpty( SCSIZE nC, SCSIZE nR ) const;
+ bool IsEmptyCell( SCSIZE nC, SCSIZE nR ) const;
+ bool IsEmptyResult( SCSIZE nC, SCSIZE nR ) const;
+ bool IsEmptyPath( SCSIZE nC, SCSIZE nR ) const;
+ bool IsValue( SCSIZE nIndex ) const;
+ bool IsValue( SCSIZE nC, SCSIZE nR ) const;
+ bool IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const;
+ bool IsBoolean( SCSIZE nC, SCSIZE nR ) const;
+ bool IsNumeric() const;
+
+ void MatCopy(ScMatrixImpl& mRes) const;
+ void MatTrans(ScMatrixImpl& mRes) const;
+ void FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 );
+ void PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR );
+ void PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR );
+ void PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
+ void PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
+ void PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR );
+ void CompareEqual();
+ void CompareNotEqual();
+ void CompareLess();
+ void CompareGreater();
+ void CompareLessEqual();
+ void CompareGreaterEqual();
+ double And() const;
+ double Or() const;
+ double Xor() const;
+
+ ScMatrix::KahanIterateResult Sum( bool bTextAsZero, bool bIgnoreErrorValues ) const;
+ ScMatrix::KahanIterateResult SumSquare( bool bTextAsZero, bool bIgnoreErrorValues ) const;
+ ScMatrix::DoubleIterateResult Product( bool bTextAsZero, bool bIgnoreErrorValues ) const;
+ size_t Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) const;
+ size_t MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const;
+ size_t MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const;
+
+ double GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const;
+ double GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const;
+ double GetGcd() const;
+ double GetLcm() const;
+
+ ScMatrixRef CompareMatrix( sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const;
+
+ void GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero ) const;
+ void MergeDoubleArrayMultiply( std::vector<double>& rArray ) const;
+
+ template<typename T>
+ void ApplyOperation(T aOp, ScMatrixImpl& rMat);
+
+ void ExecuteOperation(const std::pair<size_t, size_t>& rStartPos,
+ const std::pair<size_t, size_t>& rEndPos, const ScMatrix::DoubleOpFunction& aDoubleFunc,
+ const ScMatrix::BoolOpFunction& aBoolFunc, const ScMatrix::StringOpFunction& aStringFunc,
+ const ScMatrix::EmptyOpFunction& aEmptyFunc) const;
+
+ template<typename T, typename tRes>
+ ScMatrix::IterateResultMultiple<tRes> ApplyCollectOperation(const std::vector<T>& aOp);
+
+ void MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2,
+ SvNumberFormatter& rFormatter, svl::SharedStringPool& rPool);
+
+ void ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2,
+ ScInterpreter* pInterpreter, ScMatrix::CalculateOpFunction op);
+ bool IsValueOrEmpty( const MatrixImplType::const_position_type & rPos ) const;
+ double GetDouble( const MatrixImplType::const_position_type & rPos) const;
+ FormulaError GetErrorIfNotString( const MatrixImplType::const_position_type & rPos ) const;
+ bool IsValue( const MatrixImplType::const_position_type & rPos ) const;
+ FormulaError GetError(const MatrixImplType::const_position_type & rPos) const;
+ bool IsStringOrEmpty(const MatrixImplType::const_position_type & rPos) const;
+ svl::SharedString GetString(const MatrixImplType::const_position_type& rPos) const;
+
+#if DEBUG_MATRIX
+ void Dump() const;
+#endif
+
+private:
+ void CalcPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const;
+};
+
+static std::once_flag bElementsMaxFetched;
+static std::atomic<size_t> nElementsMax;
+
+/** The maximum number of elements a matrix or the pool may have at runtime.
+
+ @param nMemory
+ If 0, the arbitrary limit of one matrix is returned.
+ If >0, the given memory pool divided by the average size of a
+ matrix element is returned, which is used to initialize
+ nElementsMax.
+ */
+static size_t GetElementsMax( size_t nMemory )
+{
+ // Arbitrarily assuming 12 bytes per element, 8 bytes double plus
+ // overhead. Stored as an array in an mdds container it's less, but for
+ // strings or mixed matrix it can be much more...
+ constexpr size_t nPerElem = 12;
+ if (nMemory)
+ return nMemory / nPerElem;
+
+ // Arbitrarily assuming 1GB memory. Could be dynamic at some point.
+ constexpr size_t nMemMax = 0x40000000;
+ // With 1GB that's ~85M elements, or 85 whole columns.
+ constexpr size_t nElemMax = nMemMax / nPerElem;
+ // With MAXROWCOUNT==1048576 and 128 columns => 128M elements, 1.5GB
+ constexpr size_t nArbitraryLimit = size_t(MAXROWCOUNT) * 128;
+ // With the constant 1GB from above that's the actual value.
+ return std::min(nElemMax, nArbitraryLimit);
+}
+
+ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR) :
+ maMat(nR, nC), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
+{
+ nElementsMax -= GetElementCount();
+}
+
+ScMatrixImpl::ScMatrixImpl(SCSIZE nC, SCSIZE nR, double fInitVal) :
+ maMat(nR, nC, fInitVal), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
+{
+ nElementsMax -= GetElementCount();
+}
+
+ScMatrixImpl::ScMatrixImpl( size_t nC, size_t nR, const std::vector<double>& rInitVals ) :
+ maMat(nR, nC, rInitVals.begin(), rInitVals.end()), maMatFlag(nR, nC), pErrorInterpreter(nullptr)
+{
+ nElementsMax -= GetElementCount();
+}
+
+ScMatrixImpl::~ScMatrixImpl()
+{
+ nElementsMax += GetElementCount();
+ suppress_fun_call_w_exception(Clear());
+}
+
+void ScMatrixImpl::Clear()
+{
+ suppress_fun_call_w_exception(maMat.clear());
+ maMatFlag.clear();
+}
+
+void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR)
+{
+ nElementsMax += GetElementCount();
+ if (ScMatrix::IsSizeAllocatable( nC, nR))
+ {
+ maMat.resize(nR, nC);
+ maMatFlag.resize(nR, nC);
+ }
+ else
+ {
+ // Invalid matrix size, allocate 1x1 matrix with error value.
+ maMat.resize(1, 1, CreateDoubleError( FormulaError::MatrixSize));
+ maMatFlag.resize(1, 1);
+ }
+ nElementsMax -= GetElementCount();
+}
+
+void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR, double fVal)
+{
+ nElementsMax += GetElementCount();
+ if (ScMatrix::IsSizeAllocatable( nC, nR))
+ {
+ maMat.resize(nR, nC, fVal);
+ maMatFlag.resize(nR, nC);
+ }
+ else
+ {
+ // Invalid matrix size, allocate 1x1 matrix with error value.
+ maMat.resize(1, 1, CreateDoubleError( FormulaError::StackOverflow));
+ maMatFlag.resize(1, 1);
+ }
+ nElementsMax -= GetElementCount();
+}
+
+void ScMatrixImpl::SetErrorInterpreter( ScInterpreter* p)
+{
+ pErrorInterpreter = p;
+}
+
+void ScMatrixImpl::GetDimensions( SCSIZE& rC, SCSIZE& rR) const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ rR = aSize.row;
+ rC = aSize.column;
+}
+
+SCSIZE ScMatrixImpl::GetElementCount() const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ return aSize.row * aSize.column;
+}
+
+bool ScMatrixImpl::ValidColRow( SCSIZE nC, SCSIZE nR) const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ return nR < aSize.row && nC < aSize.column;
+}
+
+bool ScMatrixImpl::ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ if (aSize.column == 1 && aSize.row == 1)
+ {
+ rC = 0;
+ rR = 0;
+ return true;
+ }
+ else if (aSize.column == 1 && rR < aSize.row)
+ {
+ // single column matrix.
+ rC = 0;
+ return true;
+ }
+ else if (aSize.row == 1 && rC < aSize.column)
+ {
+ // single row matrix.
+ rR = 0;
+ return true;
+ }
+ return false;
+}
+
+bool ScMatrixImpl::ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const
+{
+ return ValidColRow( rC, rR) || ValidColRowReplicated( rC, rR);
+}
+
+void ScMatrixImpl::SetErrorAtInterpreter( FormulaError nError ) const
+{
+ if ( pErrorInterpreter )
+ pErrorInterpreter->SetError( nError);
+}
+
+void ScMatrixImpl::PutDouble(double fVal, SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ maMat.set(nR, nC, fVal);
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutDouble: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ maMat.set(nR, nC, pArray, pArray + nLen);
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutDouble: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutDouble( double fVal, SCSIZE nIndex)
+{
+ SCSIZE nC, nR;
+ CalcPosition(nIndex, nC, nR);
+ PutDouble(fVal, nC, nR);
+}
+
+void ScMatrixImpl::PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ maMat.set(nR, nC, rStr);
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutString: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ maMat.set(nR, nC, pArray, pArray + nLen);
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutString: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutString(const svl::SharedString& rStr, SCSIZE nIndex)
+{
+ SCSIZE nC, nR;
+ CalcPosition(nIndex, nC, nR);
+ PutString(rStr, nC, nR);
+}
+
+void ScMatrixImpl::PutEmpty(SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ {
+ maMat.set_empty(nR, nC);
+ maMatFlag.set_empty(nR, nC);
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutEmpty: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutEmptyPath(SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ {
+ maMat.set_empty(nR, nC);
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+ maMatFlag.set(nR, nC, SC_MATFLAG_EMPTYPATH);
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 && __cplusplus == 202002L
+#pragma GCC diagnostic pop
+#endif
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutEmptyPath: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR )
+{
+ maMat.set(nR, nC, CreateDoubleError(nErrorCode));
+}
+
+void ScMatrixImpl::PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR)
+{
+ if (ValidColRow( nC, nR))
+ maMat.set(nR, nC, bVal);
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutBoolean: dimension error");
+ }
+}
+
+FormulaError ScMatrixImpl::GetError( SCSIZE nC, SCSIZE nR) const
+{
+ if (ValidColRowOrReplicated( nC, nR ))
+ {
+ double fVal = maMat.get_numeric(nR, nC);
+ return GetDoubleErrorValue(fVal);
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::GetError: dimension error");
+ return FormulaError::NoValue;
+ }
+}
+
+double ScMatrixImpl::GetDouble(SCSIZE nC, SCSIZE nR) const
+{
+ if (ValidColRowOrReplicated( nC, nR ))
+ {
+ double fVal = maMat.get_numeric(nR, nC);
+ if ( pErrorInterpreter )
+ {
+ FormulaError nError = GetDoubleErrorValue(fVal);
+ if ( nError != FormulaError::NONE )
+ SetErrorAtInterpreter( nError);
+ }
+ return fVal;
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::GetDouble: dimension error");
+ return CreateDoubleError( FormulaError::NoValue);
+ }
+}
+
+double ScMatrixImpl::GetDouble( SCSIZE nIndex) const
+{
+ SCSIZE nC, nR;
+ CalcPosition(nIndex, nC, nR);
+ return GetDouble(nC, nR);
+}
+
+double ScMatrixImpl::GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const
+{
+ ScMatrixValue aMatVal = Get(nC, nR);
+ if (aMatVal.nType == ScMatValType::String)
+ return convertStringToValue( pErrorInterpreter, aMatVal.aStr.getString());
+ return aMatVal.fVal;
+}
+
+svl::SharedString ScMatrixImpl::GetString(SCSIZE nC, SCSIZE nR) const
+{
+ if (ValidColRowOrReplicated( nC, nR ))
+ {
+ return GetString(maMat.position(nR, nC));
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::GetString: dimension error");
+ }
+ return svl::SharedString::getEmptyString();
+}
+
+svl::SharedString ScMatrixImpl::GetString(const MatrixImplType::const_position_type& rPos) const
+{
+ double fErr = 0.0;
+ switch (maMat.get_type(rPos))
+ {
+ case mdds::mtm::element_string:
+ return maMat.get_string(rPos);
+ case mdds::mtm::element_empty:
+ return svl::SharedString::getEmptyString();
+ case mdds::mtm::element_numeric:
+ case mdds::mtm::element_boolean:
+ fErr = maMat.get_numeric(rPos);
+ [[fallthrough]];
+ default:
+ OSL_FAIL("ScMatrixImpl::GetString: access error, no string");
+ }
+ SetErrorAtInterpreter(GetDoubleErrorValue(fErr));
+ return svl::SharedString::getEmptyString();
+}
+
+svl::SharedString ScMatrixImpl::GetString( SCSIZE nIndex) const
+{
+ SCSIZE nC, nR;
+ CalcPosition(nIndex, nC, nR);
+ return GetString(nC, nR);
+}
+
+svl::SharedString ScMatrixImpl::GetString( SvNumberFormatter& rFormatter, SCSIZE nC, SCSIZE nR) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ {
+ OSL_FAIL("ScMatrixImpl::GetString: dimension error");
+ return svl::SharedString::getEmptyString();
+ }
+
+ double fVal = 0.0;
+ MatrixImplType::const_position_type aPos = maMat.position(nR, nC);
+ switch (maMat.get_type(aPos))
+ {
+ case mdds::mtm::element_string:
+ return maMat.get_string(aPos);
+ case mdds::mtm::element_empty:
+ {
+ if (maMatFlag.get<uint8_t>(nR, nC) != SC_MATFLAG_EMPTYPATH)
+ // not an empty path.
+ return svl::SharedString::getEmptyString();
+
+ // result of empty FALSE jump path
+ sal_uInt32 nKey = rFormatter.GetStandardFormat( SvNumFormatType::LOGICAL,
+ ScGlobal::eLnge);
+ OUString aStr;
+ const Color* pColor = nullptr;
+ rFormatter.GetOutputString( 0.0, nKey, aStr, &pColor);
+ return svl::SharedString( aStr); // string not interned
+ }
+ case mdds::mtm::element_numeric:
+ case mdds::mtm::element_boolean:
+ fVal = maMat.get_numeric(aPos);
+ break;
+ default:
+ ;
+ }
+
+ FormulaError nError = GetDoubleErrorValue(fVal);
+ if (nError != FormulaError::NONE)
+ {
+ SetErrorAtInterpreter( nError);
+ return svl::SharedString( ScGlobal::GetErrorString( nError)); // string not interned
+ }
+
+ sal_uInt32 nKey = rFormatter.GetStandardFormat( SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ OUString aStr;
+ rFormatter.GetInputLineString( fVal, nKey, aStr);
+ return svl::SharedString( aStr); // string not interned
+}
+
+ScMatrixValue ScMatrixImpl::Get(SCSIZE nC, SCSIZE nR) const
+{
+ ScMatrixValue aVal;
+ if (ValidColRowOrReplicated(nC, nR))
+ {
+ MatrixImplType::const_position_type aPos = maMat.position(nR, nC);
+ mdds::mtm::element_t eType = maMat.get_type(aPos);
+ switch (eType)
+ {
+ case mdds::mtm::element_boolean:
+ aVal.nType = ScMatValType::Boolean;
+ aVal.fVal = double(maMat.get_boolean(aPos));
+ break;
+ case mdds::mtm::element_numeric:
+ aVal.nType = ScMatValType::Value;
+ aVal.fVal = maMat.get_numeric(aPos);
+ break;
+ case mdds::mtm::element_string:
+ aVal.nType = ScMatValType::String;
+ aVal.aStr = maMat.get_string(aPos);
+ break;
+ case mdds::mtm::element_empty:
+ /* TODO: do we need to pass the differentiation of 'empty' and
+ * 'empty result' to the outer world anywhere? */
+ switch (maMatFlag.get_type(nR, nC))
+ {
+ case mdds::mtm::element_empty:
+ aVal.nType = ScMatValType::Empty;
+ break;
+ case mdds::mtm::element_integer:
+ aVal.nType = maMatFlag.get<uint8_t>(nR, nC)
+ == SC_MATFLAG_EMPTYPATH ? ScMatValType::EmptyPath : ScMatValType::Empty;
+ break;
+ default:
+ assert(false);
+ }
+ aVal.fVal = 0.0;
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::Get: dimension error");
+ }
+ return aVal;
+}
+
+bool ScMatrixImpl::IsStringOrEmpty( SCSIZE nIndex ) const
+{
+ SCSIZE nC, nR;
+ CalcPosition(nIndex, nC, nR);
+ return IsStringOrEmpty(nC, nR);
+}
+
+bool ScMatrixImpl::IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ switch (maMat.get_type(nR, nC))
+ {
+ case mdds::mtm::element_empty:
+ case mdds::mtm::element_string:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+bool ScMatrixImpl::IsEmpty( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ // Flag must indicate an 'empty' or 'empty cell' or 'empty result' element,
+ // but not an 'empty path' element.
+ return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
+ maMatFlag.get_integer(nR, nC) != SC_MATFLAG_EMPTYPATH;
+}
+
+bool ScMatrixImpl::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ // Flag must indicate an 'empty cell' element instead of an
+ // 'empty' or 'empty result' or 'empty path' element.
+ return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
+ maMatFlag.get_type(nR, nC) == mdds::mtm::element_empty;
+}
+
+bool ScMatrixImpl::IsEmptyResult( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ // Flag must indicate an 'empty result' element instead of an
+ // 'empty' or 'empty cell' or 'empty path' element.
+ return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
+ maMatFlag.get_integer(nR, nC) == SC_MATFLAG_EMPTYRESULT;
+}
+
+bool ScMatrixImpl::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const
+{
+ // Flag must indicate an 'empty path' element.
+ if (ValidColRowOrReplicated( nC, nR ))
+ return maMat.get_type(nR, nC) == mdds::mtm::element_empty &&
+ maMatFlag.get_integer(nR, nC) == SC_MATFLAG_EMPTYPATH;
+ else
+ return true;
+}
+
+bool ScMatrixImpl::IsValue( SCSIZE nIndex ) const
+{
+ SCSIZE nC, nR;
+ CalcPosition(nIndex, nC, nR);
+ return IsValue(nC, nR);
+}
+
+bool ScMatrixImpl::IsValue( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ switch (maMat.get_type(nR, nC))
+ {
+ case mdds::mtm::element_boolean:
+ case mdds::mtm::element_numeric:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+bool ScMatrixImpl::IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ switch (maMat.get_type(nR, nC))
+ {
+ case mdds::mtm::element_boolean:
+ case mdds::mtm::element_numeric:
+ case mdds::mtm::element_empty:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+bool ScMatrixImpl::IsBoolean( SCSIZE nC, SCSIZE nR ) const
+{
+ if (!ValidColRowOrReplicated( nC, nR ))
+ return false;
+
+ return maMat.get_type(nR, nC) == mdds::mtm::element_boolean;
+}
+
+bool ScMatrixImpl::IsNumeric() const
+{
+ return maMat.numeric();
+}
+
+void ScMatrixImpl::MatCopy(ScMatrixImpl& mRes) const
+{
+ if (maMat.size().row > mRes.maMat.size().row || maMat.size().column > mRes.maMat.size().column)
+ {
+ // destination matrix is not large enough.
+ OSL_FAIL("ScMatrixImpl::MatCopy: dimension error");
+ return;
+ }
+
+ mRes.maMat.copy(maMat);
+}
+
+void ScMatrixImpl::MatTrans(ScMatrixImpl& mRes) const
+{
+ mRes.maMat = maMat;
+ mRes.maMat.transpose();
+}
+
+void ScMatrixImpl::FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 )
+{
+ if (ValidColRow( nC1, nR1) && ValidColRow( nC2, nR2))
+ {
+ for (SCSIZE j = nC1; j <= nC2; ++j)
+ {
+ // Passing value array is much faster.
+ std::vector<double> aVals(nR2-nR1+1, fVal);
+ maMat.set(nR1, j, aVals.begin(), aVals.end());
+ }
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::FillDouble: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR )
+{
+ if (!rVec.empty() && ValidColRow( nC, nR) && ValidColRow( nC, nR + rVec.size() - 1))
+ {
+ maMat.set(nR, nC, rVec.begin(), rVec.end());
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutDoubleVector: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR )
+{
+ if (!rVec.empty() && ValidColRow( nC, nR) && ValidColRow( nC, nR + rVec.size() - 1))
+ {
+ maMat.set(nR, nC, rVec.begin(), rVec.end());
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutStringVector: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
+{
+ if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
+ {
+ maMat.set_empty(nR, nC, nCount);
+ // Flag to indicate that this is 'empty', not 'empty result' or 'empty path'.
+ maMatFlag.set_empty(nR, nC, nCount);
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutEmptyVector: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
+{
+ if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
+ {
+ maMat.set_empty(nR, nC, nCount);
+ // Flag to indicate that this is 'empty result', not 'empty' or 'empty path'.
+ std::vector<uint8_t> aVals(nCount, SC_MATFLAG_EMPTYRESULT);
+ maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutEmptyResultVector: dimension error");
+ }
+}
+
+void ScMatrixImpl::PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
+{
+ if (nCount && ValidColRow( nC, nR) && ValidColRow( nC, nR + nCount - 1))
+ {
+ maMat.set_empty(nR, nC, nCount);
+ // Flag to indicate 'empty path'.
+ std::vector<uint8_t> aVals(nCount, SC_MATFLAG_EMPTYPATH);
+ maMatFlag.set(nR, nC, aVals.begin(), aVals.end());
+ }
+ else
+ {
+ OSL_FAIL("ScMatrixImpl::PutEmptyPathVector: dimension error");
+ }
+}
+
+void ScMatrixImpl::CompareEqual()
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ CompareMatrixElemFunc<ElemEqualZero> aFunc(aSize.row, aSize.column);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(maMat);
+}
+
+void ScMatrixImpl::CompareNotEqual()
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ CompareMatrixElemFunc<ElemNotEqualZero> aFunc(aSize.row, aSize.column);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(maMat);
+}
+
+void ScMatrixImpl::CompareLess()
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ CompareMatrixElemFunc<ElemLessZero> aFunc(aSize.row, aSize.column);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(maMat);
+}
+
+void ScMatrixImpl::CompareGreater()
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ CompareMatrixElemFunc<ElemGreaterZero> aFunc(aSize.row, aSize.column);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(maMat);
+}
+
+void ScMatrixImpl::CompareLessEqual()
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ CompareMatrixElemFunc<ElemLessEqualZero> aFunc(aSize.row, aSize.column);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(maMat);
+}
+
+void ScMatrixImpl::CompareGreaterEqual()
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ CompareMatrixElemFunc<ElemGreaterEqualZero> aFunc(aSize.row, aSize.column);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(maMat);
+}
+
+namespace {
+
+struct AndEvaluator
+{
+ bool mbResult;
+ void operate(double fVal) { mbResult &= (fVal != 0.0); }
+ bool result() const { return mbResult; }
+ AndEvaluator() : mbResult(true) {}
+};
+
+struct OrEvaluator
+{
+ bool mbResult;
+ void operate(double fVal) { mbResult |= (fVal != 0.0); }
+ bool result() const { return mbResult; }
+ OrEvaluator() : mbResult(false) {}
+};
+
+struct XorEvaluator
+{
+ bool mbResult;
+ void operate(double fVal) { mbResult ^= (fVal != 0.0); }
+ bool result() const { return mbResult; }
+ XorEvaluator() : mbResult(false) {}
+};
+
+// Do not short circuit logical operations, in case there are error values
+// these need to be propagated even if the result was determined earlier.
+template <typename Evaluator>
+double EvalMatrix(const MatrixImplType& rMat)
+{
+ Evaluator aEval;
+ size_t nRows = rMat.size().row, nCols = rMat.size().column;
+ for (size_t i = 0; i < nRows; ++i)
+ {
+ for (size_t j = 0; j < nCols; ++j)
+ {
+ MatrixImplType::const_position_type aPos = rMat.position(i, j);
+ mdds::mtm::element_t eType = rMat.get_type(aPos);
+ if (eType != mdds::mtm::element_numeric && eType != mdds::mtm::element_boolean)
+ // assuming a CompareMat this is an error
+ return CreateDoubleError(FormulaError::IllegalArgument);
+
+ double fVal = rMat.get_numeric(aPos);
+ if (!std::isfinite(fVal))
+ // DoubleError
+ return fVal;
+
+ aEval.operate(fVal);
+ }
+ }
+ return aEval.result();
+}
+
+}
+
+double ScMatrixImpl::And() const
+{
+ // All elements must be of value type.
+ // True only if all the elements have non-zero values.
+ return EvalMatrix<AndEvaluator>(maMat);
+}
+
+double ScMatrixImpl::Or() const
+{
+ // All elements must be of value type.
+ // True if at least one element has a non-zero value.
+ return EvalMatrix<OrEvaluator>(maMat);
+}
+
+double ScMatrixImpl::Xor() const
+{
+ // All elements must be of value type.
+ // True if an odd number of elements have a non-zero value.
+ return EvalMatrix<XorEvaluator>(maMat);
+}
+
+namespace {
+
+template<typename Op, typename tRes>
+class WalkElementBlocks
+{
+ Op maOp;
+ ScMatrix::IterateResult<tRes> maRes;
+ bool mbTextAsZero:1;
+ bool mbIgnoreErrorValues:1;
+public:
+ WalkElementBlocks(bool bTextAsZero, bool bIgnoreErrorValues) :
+ maRes(Op::InitVal, 0),
+ mbTextAsZero(bTextAsZero), mbIgnoreErrorValues(bIgnoreErrorValues)
+ {}
+
+ const ScMatrix::IterateResult<tRes>& getResult() const { return maRes; }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ size_t nIgnored = 0;
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ if (mbIgnoreErrorValues && !std::isfinite(*it))
+ {
+ ++nIgnored;
+ continue;
+ }
+ maOp(maRes.maAccumulator, *it);
+ }
+ maRes.mnCount += node.size - nIgnored;
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ maOp(maRes.maAccumulator, *it);
+ }
+ maRes.mnCount += node.size;
+ }
+ break;
+ case mdds::mtm::element_string:
+ if (mbTextAsZero)
+ maRes.mnCount += node.size;
+ break;
+ case mdds::mtm::element_empty:
+ default:
+ ;
+ }
+ }
+};
+
+template<typename Op, typename tRes>
+class WalkElementBlocksMultipleValues
+{
+ const std::vector<Op>* mpOp;
+ ScMatrix::IterateResultMultiple<tRes> maRes;
+public:
+ WalkElementBlocksMultipleValues(const std::vector<Op>& aOp) :
+ mpOp(&aOp), maRes(0)
+ {
+ for (const auto& rpOp : *mpOp)
+ maRes.maAccumulator.emplace_back(rpOp.mInitVal);
+ }
+
+ WalkElementBlocksMultipleValues( const WalkElementBlocksMultipleValues& ) = delete;
+ WalkElementBlocksMultipleValues& operator= ( const WalkElementBlocksMultipleValues& ) = delete;
+
+ WalkElementBlocksMultipleValues(WalkElementBlocksMultipleValues&& r) noexcept
+ : mpOp(r.mpOp), maRes(r.maRes.mnCount)
+ {
+ maRes.maAccumulator = std::move(r.maRes.maAccumulator);
+ }
+
+ WalkElementBlocksMultipleValues& operator=(WalkElementBlocksMultipleValues&& r) noexcept
+ {
+ mpOp = r.mpOp;
+ maRes.maAccumulator = std::move(r.maRes.maAccumulator);
+ maRes.mnCount = r.maRes.mnCount;
+ return *this;
+ }
+
+ const ScMatrix::IterateResultMultiple<tRes>& getResult() const { return maRes; }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ for (size_t i = 0u; i < mpOp->size(); ++i)
+ (*mpOp)[i](maRes.maAccumulator[i], *it);
+ }
+ maRes.mnCount += node.size;
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ for (size_t i = 0u; i < mpOp->size(); ++i)
+ (*mpOp)[i](maRes.maAccumulator[i], *it);
+ }
+ maRes.mnCount += node.size;
+ }
+ break;
+ case mdds::mtm::element_string:
+ case mdds::mtm::element_empty:
+ default:
+ ;
+ }
+ }
+};
+
+class CountElements
+{
+ size_t mnCount;
+ bool mbCountString;
+ bool mbCountErrors;
+ bool mbIgnoreEmptyStrings;
+public:
+ explicit CountElements(bool bCountString, bool bCountErrors, bool bIgnoreEmptyStrings) :
+ mnCount(0), mbCountString(bCountString), mbCountErrors(bCountErrors),
+ mbIgnoreEmptyStrings(bIgnoreEmptyStrings) {}
+
+ size_t getCount() const { return mnCount; }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ mnCount += node.size;
+ if (!mbCountErrors)
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ if (!std::isfinite(*it))
+ --mnCount;
+ }
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ mnCount += node.size;
+ break;
+ case mdds::mtm::element_string:
+ if (mbCountString)
+ {
+ mnCount += node.size;
+ if (mbIgnoreEmptyStrings)
+ {
+ typedef MatrixImplType::string_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ if (it->isEmpty())
+ --mnCount;
+ }
+ }
+ }
+ break;
+ case mdds::mtm::element_empty:
+ default:
+ ;
+ }
+ }
+};
+
+const size_t ResultNotSet = std::numeric_limits<size_t>::max();
+
+template<typename Type>
+class WalkAndMatchElements
+{
+ Type maMatchValue;
+ size_t mnStartIndex;
+ size_t mnStopIndex;
+ size_t mnResult;
+ size_t mnIndex;
+
+public:
+ WalkAndMatchElements(Type aMatchValue, const MatrixImplType::size_pair_type& aSize, size_t nCol1, size_t nCol2) :
+ maMatchValue(std::move(aMatchValue)),
+ mnStartIndex( nCol1 * aSize.row ),
+ mnStopIndex( (nCol2 + 1) * aSize.row ),
+ mnResult(ResultNotSet),
+ mnIndex(0)
+ {
+ assert( nCol1 < aSize.column && nCol2 < aSize.column);
+ }
+
+ size_t getMatching() const { return mnResult; }
+
+ size_t getRemainingCount() const
+ {
+ return mnIndex < mnStopIndex ? mnStopIndex - mnIndex : 0;
+ }
+
+ size_t compare(const MatrixImplType::element_block_node_type& node) const;
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ // early exit if match already found
+ if (mnResult != ResultNotSet)
+ return;
+
+ // limit lookup to the requested columns
+ if (mnStartIndex <= mnIndex && getRemainingCount() > 0)
+ {
+ mnResult = compare(node);
+ }
+
+ mnIndex += node.size;
+ }
+};
+
+template<>
+size_t WalkAndMatchElements<double>::compare(const MatrixImplType::element_block_node_type& node) const
+{
+ size_t nCount = 0;
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ const size_t nRemaining = getRemainingCount();
+ for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
+ {
+ if (*it == maMatchValue)
+ {
+ return mnIndex + nCount;
+ }
+ }
+ break;
+ }
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ const size_t nRemaining = getRemainingCount();
+ for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
+ {
+ if (int(*it) == maMatchValue)
+ {
+ return mnIndex + nCount;
+ }
+ }
+ break;
+ }
+ break;
+ case mdds::mtm::element_string:
+ case mdds::mtm::element_empty:
+ default:
+ ;
+ }
+ return ResultNotSet;
+}
+
+template<>
+size_t WalkAndMatchElements<svl::SharedString>::compare(const MatrixImplType::element_block_node_type& node) const
+{
+ switch (node.type)
+ {
+ case mdds::mtm::element_string:
+ {
+ size_t nCount = 0;
+ typedef MatrixImplType::string_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ const size_t nRemaining = getRemainingCount();
+ for (; it != itEnd && nCount < nRemaining; ++it, ++nCount)
+ {
+ if (it->getDataIgnoreCase() == maMatchValue.getDataIgnoreCase())
+ {
+ return mnIndex + nCount;
+ }
+ }
+ break;
+ }
+ case mdds::mtm::element_boolean:
+ case mdds::mtm::element_numeric:
+ case mdds::mtm::element_empty:
+ default:
+ ;
+ }
+ return ResultNotSet;
+}
+
+struct MaxOp
+{
+ static double init() { return -std::numeric_limits<double>::max(); }
+ static double compare(double left, double right)
+ {
+ if (!std::isfinite(left))
+ return left;
+ if (!std::isfinite(right))
+ return right;
+ return std::max(left, right);
+ }
+
+ static double boolValue(
+ MatrixImplType::boolean_block_type::const_iterator it,
+ const MatrixImplType::boolean_block_type::const_iterator& itEnd)
+ {
+ // If the array has at least one true value, the maximum value is 1.
+ it = std::find(it, itEnd, true);
+ return it == itEnd ? 0.0 : 1.0;
+ }
+};
+
+struct MinOp
+{
+ static double init() { return std::numeric_limits<double>::max(); }
+ static double compare(double left, double right)
+ {
+ if (!std::isfinite(left))
+ return left;
+ if (!std::isfinite(right))
+ return right;
+ return std::min(left, right);
+ }
+
+ static double boolValue(
+ MatrixImplType::boolean_block_type::const_iterator it,
+ const MatrixImplType::boolean_block_type::const_iterator& itEnd)
+ {
+ // If the array has at least one false value, the minimum value is 0.
+ it = std::find(it, itEnd, false);
+ return it == itEnd ? 1.0 : 0.0;
+ }
+};
+
+struct Lcm
+{
+ static double init() { return 1.0; }
+ static double calculate(double fx,double fy)
+ {
+ return (fx*fy)/ScInterpreter::ScGetGCD(fx,fy);
+ }
+
+ static double boolValue(
+ MatrixImplType::boolean_block_type::const_iterator it,
+ const MatrixImplType::boolean_block_type::const_iterator& itEnd)
+ {
+ // If the array has at least one false value, the minimum value is 0.
+ it = std::find(it, itEnd, false);
+ return it == itEnd ? 1.0 : 0.0;
+ }
+};
+
+struct Gcd
+{
+ static double init() { return 0.0; }
+ static double calculate(double fx,double fy)
+ {
+ return ScInterpreter::ScGetGCD(fx,fy);
+ }
+
+ static double boolValue(
+ MatrixImplType::boolean_block_type::const_iterator it,
+ const MatrixImplType::boolean_block_type::const_iterator& itEnd)
+ {
+ // If the array has at least one true value, the gcdResult is 1.
+ it = std::find(it, itEnd, true);
+ return it == itEnd ? 0.0 : 1.0;
+ }
+};
+
+template<typename Op>
+class CalcMaxMinValue
+{
+ double mfVal;
+ bool mbTextAsZero;
+ bool mbIgnoreErrorValues;
+ bool mbHasValue;
+public:
+ CalcMaxMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) :
+ mfVal(Op::init()),
+ mbTextAsZero(bTextAsZero),
+ mbIgnoreErrorValues(bIgnoreErrorValues),
+ mbHasValue(false) {}
+
+ double getValue() const { return mbHasValue ? mfVal : 0.0; }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ if (mbIgnoreErrorValues)
+ {
+ for (; it != itEnd; ++it)
+ {
+ if (std::isfinite(*it))
+ mfVal = Op::compare(mfVal, *it);
+ }
+ }
+ else
+ {
+ for (; it != itEnd; ++it)
+ mfVal = Op::compare(mfVal, *it);
+ }
+
+ mbHasValue = true;
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ double fVal = Op::boolValue(it, itEnd);
+ mfVal = Op::compare(mfVal, fVal);
+ mbHasValue = true;
+ }
+ break;
+ case mdds::mtm::element_string:
+ case mdds::mtm::element_empty:
+ {
+ // empty elements are treated as empty strings.
+ if (mbTextAsZero)
+ {
+ mfVal = Op::compare(mfVal, 0.0);
+ mbHasValue = true;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+template<typename Op>
+class CalcGcdLcm
+{
+ double mfval;
+
+public:
+ CalcGcdLcm() : mfval(Op::init()) {}
+
+ double getResult() const { return mfval; }
+
+ void operator() ( const MatrixImplType::element_block_node_type& node )
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+
+ for ( ; it != itEnd; ++it)
+ {
+ if (*it < 0.0)
+ mfval = CreateDoubleError(FormulaError::IllegalArgument);
+ else
+ mfval = ::rtl::math::approxFloor( Op::calculate(*it,mfval));
+ }
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+
+ mfval = Op::boolValue(it, itEnd);
+ }
+ break;
+ case mdds::mtm::element_empty:
+ case mdds::mtm::element_string:
+ {
+ mfval = CreateDoubleError(FormulaError::IllegalArgument);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+double evaluate( double fVal, ScQueryOp eOp )
+{
+ if (!std::isfinite(fVal))
+ return fVal;
+
+ switch (eOp)
+ {
+ case SC_EQUAL:
+ return fVal == 0.0 ? 1.0 : 0.0;
+ case SC_LESS:
+ return fVal < 0.0 ? 1.0 : 0.0;
+ case SC_GREATER:
+ return fVal > 0.0 ? 1.0 : 0.0;
+ case SC_LESS_EQUAL:
+ return fVal <= 0.0 ? 1.0 : 0.0;
+ case SC_GREATER_EQUAL:
+ return fVal >= 0.0 ? 1.0 : 0.0;
+ case SC_NOT_EQUAL:
+ return fVal != 0.0 ? 1.0 : 0.0;
+ default:
+ ;
+ }
+
+ SAL_WARN("sc.core", "evaluate: unhandled comparison operator: " << static_cast<int>(eOp));
+ return CreateDoubleError( FormulaError::UnknownState);
+}
+
+class CompareMatrixFunc
+{
+ sc::Compare& mrComp;
+ size_t mnMatPos;
+ sc::CompareOptions* mpOptions;
+ std::vector<double> maResValues; // double instead of bool to transport error values
+
+ void compare()
+ {
+ double fVal = sc::CompareFunc( mrComp, mpOptions);
+ maResValues.push_back(evaluate(fVal, mrComp.meOp));
+ }
+
+public:
+ CompareMatrixFunc( size_t nResSize, sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) :
+ mrComp(rComp), mnMatPos(nMatPos), mpOptions(pOptions)
+ {
+ maResValues.reserve(nResSize);
+ }
+
+ CompareMatrixFunc( const CompareMatrixFunc& ) = delete;
+ CompareMatrixFunc& operator= ( const CompareMatrixFunc& ) = delete;
+
+ CompareMatrixFunc(CompareMatrixFunc&& r) noexcept :
+ mrComp(r.mrComp),
+ mnMatPos(r.mnMatPos),
+ mpOptions(r.mpOptions),
+ maResValues(std::move(r.maResValues)) {}
+
+ CompareMatrixFunc& operator=(CompareMatrixFunc&& r) noexcept
+ {
+ mrComp = r.mrComp;
+ mnMatPos = r.mnMatPos;
+ mpOptions = r.mpOptions;
+ maResValues = std::move(r.maResValues);
+ return *this;
+ }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ sc::Compare::Cell& rCell = mrComp.maCells[mnMatPos];
+
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ rCell.mbValue = true;
+ rCell.mbEmpty = false;
+ rCell.mfValue = *it;
+ compare();
+ }
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ rCell.mbValue = true;
+ rCell.mbEmpty = false;
+ rCell.mfValue = double(*it);
+ compare();
+ }
+ }
+ break;
+ case mdds::mtm::element_string:
+ {
+ typedef MatrixImplType::string_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ const svl::SharedString& rStr = *it;
+ rCell.mbValue = false;
+ rCell.mbEmpty = false;
+ rCell.maStr = rStr;
+ compare();
+ }
+ }
+ break;
+ case mdds::mtm::element_empty:
+ {
+ rCell.mbValue = false;
+ rCell.mbEmpty = true;
+ rCell.maStr = svl::SharedString::getEmptyString();
+ for (size_t i = 0; i < node.size; ++i)
+ compare();
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ const std::vector<double>& getValues() const
+ {
+ return maResValues;
+ }
+};
+
+/**
+ * Left-hand side is a matrix while the right-hand side is a numeric value.
+ */
+class CompareMatrixToNumericFunc
+{
+ sc::Compare& mrComp;
+ double mfRightValue;
+ sc::CompareOptions* mpOptions;
+ std::vector<double> maResValues; // double instead of bool to transport error values
+
+ void compare()
+ {
+ double fVal = sc::CompareFunc(mrComp.maCells[0], mfRightValue, mpOptions);
+ maResValues.push_back(evaluate(fVal, mrComp.meOp));
+ }
+
+ void compareLeftNumeric( double fLeftVal )
+ {
+ double fVal = sc::CompareFunc(fLeftVal, mfRightValue);
+ maResValues.push_back(evaluate(fVal, mrComp.meOp));
+ }
+
+ void compareLeftEmpty( size_t nSize )
+ {
+ double fVal = sc::CompareEmptyToNumericFunc(mfRightValue);
+ bool bRes = evaluate(fVal, mrComp.meOp);
+ maResValues.resize(maResValues.size() + nSize, bRes ? 1.0 : 0.0);
+ }
+
+public:
+ CompareMatrixToNumericFunc( size_t nResSize, sc::Compare& rComp, double fRightValue, sc::CompareOptions* pOptions ) :
+ mrComp(rComp), mfRightValue(fRightValue), mpOptions(pOptions)
+ {
+ maResValues.reserve(nResSize);
+ }
+
+ CompareMatrixToNumericFunc( const CompareMatrixToNumericFunc& ) = delete;
+ CompareMatrixToNumericFunc& operator= ( const CompareMatrixToNumericFunc& ) = delete;
+
+ CompareMatrixToNumericFunc(CompareMatrixToNumericFunc&& r) noexcept :
+ mrComp(r.mrComp),
+ mfRightValue(r.mfRightValue),
+ mpOptions(r.mpOptions),
+ maResValues(std::move(r.maResValues)) {}
+
+ CompareMatrixToNumericFunc& operator=(CompareMatrixToNumericFunc&& r) noexcept
+ {
+ mrComp = r.mrComp;
+ mfRightValue = r.mfRightValue;
+ mpOptions = r.mpOptions;
+ maResValues = std::move(r.maResValues);
+ return *this;
+ }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ compareLeftNumeric(*it);
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ compareLeftNumeric(double(*it));
+ }
+ break;
+ case mdds::mtm::element_string:
+ {
+ typedef MatrixImplType::string_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ for (; it != itEnd; ++it)
+ {
+ const svl::SharedString& rStr = *it;
+ sc::Compare::Cell& rCell = mrComp.maCells[0];
+ rCell.mbValue = false;
+ rCell.mbEmpty = false;
+ rCell.maStr = rStr;
+ compare();
+ }
+ }
+ break;
+ case mdds::mtm::element_empty:
+ compareLeftEmpty(node.size);
+ break;
+ default:
+ ;
+ }
+ }
+
+ const std::vector<double>& getValues() const
+ {
+ return maResValues;
+ }
+};
+
+class ToDoubleArray
+{
+ std::vector<double> maArray;
+ std::vector<double>::iterator miPos;
+ double mfNaN;
+ bool mbEmptyAsZero;
+
+ void moveArray( ToDoubleArray& r )
+ {
+ // Re-create the iterator from the new array after the array has been
+ // moved, to ensure that the iterator points to a valid array
+ // position.
+ size_t n = std::distance(r.maArray.begin(), r.miPos);
+ maArray = std::move(r.maArray);
+ miPos = maArray.begin();
+ std::advance(miPos, n);
+ }
+
+public:
+ ToDoubleArray( size_t nSize, bool bEmptyAsZero ) :
+ maArray(nSize, 0.0), miPos(maArray.begin()), mbEmptyAsZero(bEmptyAsZero)
+ {
+ mfNaN = CreateDoubleError( FormulaError::ElementNaN);
+ }
+
+ ToDoubleArray( const ToDoubleArray& ) = delete;
+ ToDoubleArray& operator= ( const ToDoubleArray& ) = delete;
+
+ ToDoubleArray(ToDoubleArray&& r) noexcept :
+ mfNaN(r.mfNaN), mbEmptyAsZero(r.mbEmptyAsZero)
+ {
+ moveArray(r);
+ }
+
+ ToDoubleArray& operator=(ToDoubleArray&& r) noexcept
+ {
+ mfNaN = r.mfNaN;
+ mbEmptyAsZero = r.mbEmptyAsZero;
+ moveArray(r);
+ return *this;
+ }
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ using namespace mdds::mtv;
+
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ double_element_block::const_iterator it = double_element_block::begin(*node.data);
+ double_element_block::const_iterator itEnd = double_element_block::end(*node.data);
+ for (; it != itEnd; ++it, ++miPos)
+ *miPos = *it;
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ boolean_element_block::const_iterator it = boolean_element_block::begin(*node.data);
+ boolean_element_block::const_iterator itEnd = boolean_element_block::end(*node.data);
+ for (; it != itEnd; ++it, ++miPos)
+ *miPos = *it ? 1.0 : 0.0;
+ }
+ break;
+ case mdds::mtm::element_string:
+ {
+ for (size_t i = 0; i < node.size; ++i, ++miPos)
+ *miPos = mfNaN;
+ }
+ break;
+ case mdds::mtm::element_empty:
+ {
+ if (mbEmptyAsZero)
+ {
+ std::advance(miPos, node.size);
+ return;
+ }
+
+ for (size_t i = 0; i < node.size; ++i, ++miPos)
+ *miPos = mfNaN;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ void swap(std::vector<double>& rOther)
+ {
+ maArray.swap(rOther);
+ }
+};
+
+struct ArrayMul
+{
+ double operator() (const double& lhs, const double& rhs) const
+ {
+ return lhs * rhs;
+ }
+};
+
+template<typename Op>
+class MergeDoubleArrayFunc
+{
+ std::vector<double>::iterator miPos;
+ double mfNaN;
+public:
+ MergeDoubleArrayFunc(std::vector<double>& rArray) : miPos(rArray.begin())
+ {
+ mfNaN = CreateDoubleError( FormulaError::ElementNaN);
+ }
+
+ MergeDoubleArrayFunc( const MergeDoubleArrayFunc& ) = delete;
+ MergeDoubleArrayFunc& operator= ( const MergeDoubleArrayFunc& ) = delete;
+
+ MergeDoubleArrayFunc( MergeDoubleArrayFunc&& ) = default;
+ MergeDoubleArrayFunc& operator= ( MergeDoubleArrayFunc&& ) = default;
+
+ void operator() (const MatrixImplType::element_block_node_type& node)
+ {
+ using namespace mdds::mtv;
+ static const Op op;
+
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ double_element_block::const_iterator it = double_element_block::begin(*node.data);
+ double_element_block::const_iterator itEnd = double_element_block::end(*node.data);
+ for (; it != itEnd; ++it, ++miPos)
+ {
+ if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
+ continue;
+
+ *miPos = op(*miPos, *it);
+ }
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ boolean_element_block::const_iterator it = boolean_element_block::begin(*node.data);
+ boolean_element_block::const_iterator itEnd = boolean_element_block::end(*node.data);
+ for (; it != itEnd; ++it, ++miPos)
+ {
+ if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
+ continue;
+
+ *miPos = op(*miPos, *it ? 1.0 : 0.0);
+ }
+ }
+ break;
+ case mdds::mtm::element_string:
+ {
+ for (size_t i = 0; i < node.size; ++i, ++miPos)
+ *miPos = mfNaN;
+ }
+ break;
+ case mdds::mtm::element_empty:
+ {
+ // Empty element is equivalent of having a numeric value of 0.0.
+ for (size_t i = 0; i < node.size; ++i, ++miPos)
+ {
+ if (GetDoubleErrorValue(*miPos) == FormulaError::ElementNaN)
+ continue;
+
+ *miPos = op(*miPos, 0.0);
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+}
+
+namespace {
+
+template<typename TOp, typename tRes>
+ScMatrix::IterateResult<tRes> GetValueWithCount(bool bTextAsZero, bool bIgnoreErrorValues, const MatrixImplType& maMat)
+{
+ WalkElementBlocks<TOp, tRes> aFunc(bTextAsZero, bIgnoreErrorValues);
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getResult();
+}
+
+}
+
+ScMatrix::KahanIterateResult ScMatrixImpl::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const
+{
+ return GetValueWithCount<sc::op::Sum, KahanSum>(bTextAsZero, bIgnoreErrorValues, maMat);
+}
+
+ScMatrix::KahanIterateResult ScMatrixImpl::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const
+{
+ return GetValueWithCount<sc::op::SumSquare, KahanSum>(bTextAsZero, bIgnoreErrorValues, maMat);
+}
+
+ScMatrix::DoubleIterateResult ScMatrixImpl::Product(bool bTextAsZero, bool bIgnoreErrorValues) const
+{
+ return GetValueWithCount<sc::op::Product, double>(bTextAsZero, bIgnoreErrorValues, maMat);
+}
+
+size_t ScMatrixImpl::Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) const
+{
+ CountElements aFunc(bCountStrings, bCountErrors, bIgnoreEmptyStrings);
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getCount();
+}
+
+size_t ScMatrixImpl::MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const
+{
+ WalkAndMatchElements<double> aFunc(fValue, maMat.size(), nCol1, nCol2);
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getMatching();
+}
+
+size_t ScMatrixImpl::MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const
+{
+ WalkAndMatchElements<svl::SharedString> aFunc(rStr, maMat.size(), nCol1, nCol2);
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getMatching();
+}
+
+double ScMatrixImpl::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
+{
+ CalcMaxMinValue<MaxOp> aFunc(bTextAsZero, bIgnoreErrorValues);
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getValue();
+}
+
+double ScMatrixImpl::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
+{
+ CalcMaxMinValue<MinOp> aFunc(bTextAsZero, bIgnoreErrorValues);
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getValue();
+}
+
+double ScMatrixImpl::GetGcd() const
+{
+ CalcGcdLcm<Gcd> aFunc;
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getResult();
+}
+
+double ScMatrixImpl::GetLcm() const
+{
+ CalcGcdLcm<Lcm> aFunc;
+ aFunc = maMat.walk(aFunc);
+ return aFunc.getResult();
+}
+
+ScMatrixRef ScMatrixImpl::CompareMatrix(
+ sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ size_t nSize = aSize.column * aSize.row;
+ if (nMatPos == 0)
+ {
+ if (rComp.maCells[1].mbValue && !rComp.maCells[1].mbEmpty)
+ {
+ // Matrix on the left, and a numeric value on the right. Use a
+ // function object that has much less branching for much better
+ // performance.
+ CompareMatrixToNumericFunc aFunc(nSize, rComp, rComp.maCells[1].mfValue, pOptions);
+ aFunc = maMat.walk(std::move(aFunc));
+
+ // We assume the result matrix has the same dimension as this matrix.
+ const std::vector<double>& rResVal = aFunc.getValues();
+ assert (nSize == rResVal.size());
+ if (nSize != rResVal.size())
+ return ScMatrixRef();
+
+ return ScMatrixRef(new ScMatrix(aSize.column, aSize.row, rResVal));
+ }
+ }
+
+ CompareMatrixFunc aFunc(nSize, rComp, nMatPos, pOptions);
+ aFunc = maMat.walk(std::move(aFunc));
+
+ // We assume the result matrix has the same dimension as this matrix.
+ const std::vector<double>& rResVal = aFunc.getValues();
+ assert (nSize == rResVal.size());
+ if (nSize != rResVal.size())
+ return ScMatrixRef();
+
+ return ScMatrixRef(new ScMatrix(aSize.column, aSize.row, rResVal));
+}
+
+void ScMatrixImpl::GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero ) const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ ToDoubleArray aFunc(aSize.row*aSize.column, bEmptyAsZero);
+ aFunc = maMat.walk(std::move(aFunc));
+ aFunc.swap(rArray);
+}
+
+void ScMatrixImpl::MergeDoubleArrayMultiply( std::vector<double>& rArray ) const
+{
+ MatrixImplType::size_pair_type aSize = maMat.size();
+ size_t nSize = aSize.row*aSize.column;
+ if (nSize != rArray.size())
+ return;
+
+ MergeDoubleArrayFunc<ArrayMul> aFunc(rArray);
+ maMat.walk(std::move(aFunc));
+}
+
+namespace {
+
+template<typename T, typename U, typename return_type>
+struct wrapped_iterator
+{
+ typedef ::std::bidirectional_iterator_tag iterator_category;
+ typedef typename T::const_iterator::value_type old_value_type;
+ typedef return_type value_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef typename T::const_iterator::difference_type difference_type;
+
+ typename T::const_iterator it;
+ mutable value_type val;
+ U maOp;
+
+private:
+
+ value_type calcVal() const
+ {
+ return maOp(*it);
+ }
+
+public:
+
+ wrapped_iterator(typename T::const_iterator it_, U const & aOp):
+ it(std::move(it_)),
+ val(value_type()),
+ maOp(aOp)
+ {
+ }
+
+ wrapped_iterator(const wrapped_iterator& r):
+ it(r.it),
+ val(r.val),
+ maOp(r.maOp)
+ {
+ }
+
+ wrapped_iterator& operator=(const wrapped_iterator& r)
+ {
+ it = r.it;
+ return *this;
+ }
+
+ bool operator==(const wrapped_iterator& r) const
+ {
+ return it == r.it;
+ }
+
+ bool operator!=(const wrapped_iterator& r) const
+ {
+ return !operator==(r);
+ }
+
+ wrapped_iterator& operator++()
+ {
+ ++it;
+
+ return *this;
+ }
+
+ wrapped_iterator& operator--()
+ {
+ --it;
+
+ return *this;
+ }
+
+ value_type& operator*() const
+ {
+ val = calcVal();
+ return val;
+ }
+
+ pointer operator->() const
+ {
+ val = calcVal();
+ return &val;
+ }
+};
+
+template<typename T, typename U, typename return_type>
+struct MatrixIteratorWrapper
+{
+private:
+ typename T::const_iterator m_itBegin;
+ typename T::const_iterator m_itEnd;
+ U maOp;
+public:
+ MatrixIteratorWrapper(typename T::const_iterator itBegin, typename T::const_iterator itEnd, U const & aOp):
+ m_itBegin(std::move(itBegin)),
+ m_itEnd(std::move(itEnd)),
+ maOp(aOp)
+ {
+ }
+
+ wrapped_iterator<T, U, return_type> begin()
+ {
+ return wrapped_iterator<T, U, return_type>(m_itBegin, maOp);
+ }
+
+ wrapped_iterator<T, U, return_type> end()
+ {
+ return wrapped_iterator<T, U, return_type>(m_itEnd, maOp);
+ }
+};
+
+MatrixImplType::position_type increment_position(const MatrixImplType::position_type& pos, size_t n)
+{
+ MatrixImplType::position_type ret = pos;
+ do
+ {
+ if (ret.second + n < ret.first->size)
+ {
+ ret.second += n;
+ break;
+ }
+ else
+ {
+ n -= (ret.first->size - ret.second);
+ ++ret.first;
+ ret.second = 0;
+ }
+ }
+ while (n > 0);
+ return ret;
+}
+
+template<typename T>
+struct MatrixOpWrapper
+{
+private:
+ MatrixImplType& mrMat;
+ MatrixImplType::position_type pos;
+ const T* mpOp;
+
+public:
+ MatrixOpWrapper(MatrixImplType& rMat, const T& aOp):
+ mrMat(rMat),
+ pos(rMat.position(0,0)),
+ mpOp(&aOp)
+ {
+ }
+
+ MatrixOpWrapper( const MatrixOpWrapper& r ) : mrMat(r.mrMat), pos(r.pos), mpOp(r.mpOp) {}
+
+ MatrixOpWrapper& operator= ( const MatrixOpWrapper& r ) = default;
+
+ void operator()(const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+ MatrixIteratorWrapper<block_type, T, typename T::number_value_type> aFunc(it, itEnd, *mpOp);
+ pos = mrMat.set(pos,aFunc.begin(), aFunc.end());
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+
+ MatrixIteratorWrapper<block_type, T, typename T::number_value_type> aFunc(it, itEnd, *mpOp);
+ pos = mrMat.set(pos, aFunc.begin(), aFunc.end());
+ }
+ break;
+ case mdds::mtm::element_string:
+ {
+ typedef MatrixImplType::string_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ block_type::const_iterator itEnd = block_type::end(*node.data);
+
+ MatrixIteratorWrapper<block_type, T, typename T::number_value_type> aFunc(it, itEnd, *mpOp);
+ pos = mrMat.set(pos, aFunc.begin(), aFunc.end());
+ }
+ break;
+ case mdds::mtm::element_empty:
+ {
+ if (mpOp->useFunctionForEmpty())
+ {
+ std::vector<char> aVec(node.size);
+ MatrixIteratorWrapper<std::vector<char>, T, typename T::number_value_type>
+ aFunc(aVec.begin(), aVec.end(), *mpOp);
+ pos = mrMat.set(pos, aFunc.begin(), aFunc.end());
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ pos = increment_position(pos, node.size);
+ }
+};
+
+}
+
+template<typename T>
+void ScMatrixImpl::ApplyOperation(T aOp, ScMatrixImpl& rMat)
+{
+ MatrixOpWrapper<T> aFunc(rMat.maMat, aOp);
+ maMat.walk(aFunc);
+}
+
+template<typename T, typename tRes>
+ScMatrix::IterateResultMultiple<tRes> ScMatrixImpl::ApplyCollectOperation(const std::vector<T>& aOp)
+{
+ WalkElementBlocksMultipleValues<T, tRes> aFunc(aOp);
+ aFunc = maMat.walk(std::move(aFunc));
+ return aFunc.getResult();
+}
+
+namespace {
+
+struct ElementBlock
+{
+ ElementBlock(size_t nRowSize,
+ ScMatrix::DoubleOpFunction aDoubleFunc,
+ ScMatrix::BoolOpFunction aBoolFunc,
+ ScMatrix::StringOpFunction aStringFunc,
+ ScMatrix::EmptyOpFunction aEmptyFunc):
+ mnRowSize(nRowSize),
+ mnRowPos(0),
+ mnColPos(0),
+ maDoubleFunc(std::move(aDoubleFunc)),
+ maBoolFunc(std::move(aBoolFunc)),
+ maStringFunc(std::move(aStringFunc)),
+ maEmptyFunc(std::move(aEmptyFunc))
+ {
+ }
+
+ size_t mnRowSize;
+ size_t mnRowPos;
+ size_t mnColPos;
+
+ ScMatrix::DoubleOpFunction maDoubleFunc;
+ ScMatrix::BoolOpFunction maBoolFunc;
+ ScMatrix::StringOpFunction maStringFunc;
+ ScMatrix::EmptyOpFunction maEmptyFunc;
+};
+
+class WalkElementBlockOperation
+{
+public:
+
+ WalkElementBlockOperation(ElementBlock& rElementBlock)
+ : mrElementBlock(rElementBlock)
+ {
+ }
+
+ void operator()(const MatrixImplType::element_block_node_type& node)
+ {
+ switch (node.type)
+ {
+ case mdds::mtm::element_numeric:
+ {
+ typedef MatrixImplType::numeric_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ std::advance(it, node.offset);
+ block_type::const_iterator itEnd = it;
+ std::advance(itEnd, node.size);
+ for (auto itr = it; itr != itEnd; ++itr)
+ {
+ mrElementBlock.maDoubleFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos, *itr);
+ ++mrElementBlock.mnRowPos;
+ if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize)
+ {
+ mrElementBlock.mnRowPos = 0;
+ ++mrElementBlock.mnColPos;
+ }
+ }
+ }
+ break;
+ case mdds::mtm::element_string:
+ {
+ typedef MatrixImplType::string_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ std::advance(it, node.offset);
+ block_type::const_iterator itEnd = it;
+ std::advance(itEnd, node.size);
+ for (auto itr = it; itr != itEnd; ++itr)
+ {
+ mrElementBlock.maStringFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos, *itr);
+ ++mrElementBlock.mnRowPos;
+ if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize)
+ {
+ mrElementBlock.mnRowPos = 0;
+ ++mrElementBlock.mnColPos;
+ }
+ }
+ }
+ break;
+ case mdds::mtm::element_boolean:
+ {
+ typedef MatrixImplType::boolean_block_type block_type;
+
+ block_type::const_iterator it = block_type::begin(*node.data);
+ std::advance(it, node.offset);
+ block_type::const_iterator itEnd = it;
+ std::advance(itEnd, node.size);
+ for (auto itr = it; itr != itEnd; ++itr)
+ {
+ mrElementBlock.maBoolFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos, *itr);
+ ++mrElementBlock.mnRowPos;
+ if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize)
+ {
+ mrElementBlock.mnRowPos = 0;
+ ++mrElementBlock.mnColPos;
+ }
+ }
+ }
+ break;
+ case mdds::mtm::element_empty:
+ {
+ for (size_t i=0; i < node.size; ++i)
+ {
+ mrElementBlock.maEmptyFunc(mrElementBlock.mnRowPos, mrElementBlock.mnColPos);
+ ++mrElementBlock.mnRowPos;
+ if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize)
+ {
+ mrElementBlock.mnRowPos = 0;
+ ++mrElementBlock.mnColPos;
+ }
+ }
+ }
+ break;
+ case mdds::mtm::element_integer:
+ {
+ SAL_WARN("sc.core","WalkElementBlockOperation - unhandled element_integer");
+ // No function (yet?), but advance row and column count.
+ mrElementBlock.mnColPos += node.size / mrElementBlock.mnRowSize;
+ mrElementBlock.mnRowPos += node.size % mrElementBlock.mnRowSize;
+ if (mrElementBlock.mnRowPos >= mrElementBlock.mnRowSize)
+ {
+ mrElementBlock.mnRowPos = 0;
+ ++mrElementBlock.mnColPos;
+ }
+ }
+ break;
+ }
+ }
+
+private:
+
+ ElementBlock& mrElementBlock;
+};
+
+}
+
+void ScMatrixImpl::ExecuteOperation(const std::pair<size_t, size_t>& rStartPos,
+ const std::pair<size_t, size_t>& rEndPos, const ScMatrix::DoubleOpFunction& aDoubleFunc,
+ const ScMatrix::BoolOpFunction& aBoolFunc, const ScMatrix::StringOpFunction& aStringFunc,
+ const ScMatrix::EmptyOpFunction& aEmptyFunc) const
+{
+ ElementBlock aPayload(maMat.size().row, aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc);
+ WalkElementBlockOperation aFunc(aPayload);
+ maMat.walk(
+ aFunc,
+ MatrixImplType::size_pair_type(rStartPos.first, rStartPos.second),
+ MatrixImplType::size_pair_type(rEndPos.first, rEndPos.second));
+}
+
+#if DEBUG_MATRIX
+
+void ScMatrixImpl::Dump() const
+{
+ cout << "-- matrix content" << endl;
+ SCSIZE nCols, nRows;
+ GetDimensions(nCols, nRows);
+ for (SCSIZE nRow = 0; nRow < nRows; ++nRow)
+ {
+ for (SCSIZE nCol = 0; nCol < nCols; ++nCol)
+ {
+ cout << " row=" << nRow << ", col=" << nCol << " : ";
+ switch (maMat.get_type(nRow, nCol))
+ {
+ case mdds::mtm::element_string:
+ cout << "string (" << maMat.get_string(nRow, nCol).getString() << ")";
+ break;
+ case mdds::mtm::element_numeric:
+ cout << "numeric (" << maMat.get_numeric(nRow, nCol) << ")";
+ break;
+ case mdds::mtm::element_boolean:
+ cout << "boolean (" << maMat.get_boolean(nRow, nCol) << ")";
+ break;
+ case mdds::mtm::element_empty:
+ cout << "empty";
+ break;
+ default:
+ ;
+ }
+
+ cout << endl;
+ }
+ }
+}
+#endif
+
+void ScMatrixImpl::CalcPosition(SCSIZE nIndex, SCSIZE& rC, SCSIZE& rR) const
+{
+ SCSIZE nRowSize = maMat.size().row;
+ SAL_WARN_IF( !nRowSize, "sc.core", "ScMatrixImpl::CalcPosition: 0 rows!");
+ rC = nRowSize > 1 ? nIndex / nRowSize : nIndex;
+ rR = nIndex - rC*nRowSize;
+}
+
+namespace {
+
+size_t get_index(SCSIZE nMaxRow, size_t nRow, size_t nCol, size_t nRowOffset, size_t nColOffset)
+{
+ return nMaxRow * (nCol + nColOffset) + nRow + nRowOffset;
+}
+
+}
+
+void ScMatrixImpl::MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrixRef& xMat1, const ScMatrixRef& xMat2,
+ SvNumberFormatter& rFormatter, svl::SharedStringPool& rStringPool)
+{
+ SCSIZE nC1, nC2;
+ SCSIZE nR1, nR2;
+ xMat1->GetDimensions(nC1, nR1);
+ xMat2->GetDimensions(nC2, nR2);
+
+ sal_uInt32 nKey = rFormatter.GetStandardFormat( SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+
+ std::vector<OUString> aString(nMaxCol * nMaxRow);
+ std::vector<bool> aValid(nMaxCol * nMaxRow, true);
+ std::vector<FormulaError> nErrors(nMaxCol * nMaxRow,FormulaError::NONE);
+
+ size_t nRowOffset = 0;
+ size_t nColOffset = 0;
+ std::function<void(size_t, size_t, double)> aDoubleFunc =
+ [&](size_t nRow, size_t nCol, double nVal)
+ {
+ FormulaError nErr = GetDoubleErrorValue(nVal);
+ if (nErr != FormulaError::NONE)
+ {
+ aValid[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = false;
+ nErrors[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = nErr;
+ return;
+ }
+ OUString aStr;
+ rFormatter.GetInputLineString( nVal, nKey, aStr);
+ aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr;
+ };
+
+ std::function<void(size_t, size_t, bool)> aBoolFunc =
+ [&](size_t nRow, size_t nCol, bool nVal)
+ {
+ OUString aStr;
+ rFormatter.GetInputLineString( nVal ? 1.0 : 0.0, nKey, aStr);
+ aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr;
+ };
+
+ std::function<void(size_t, size_t, const svl::SharedString&)> aStringFunc =
+ [&](size_t nRow, size_t nCol, const svl::SharedString& aStr)
+ {
+ aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr.getString();
+ };
+
+ std::function<void(size_t, size_t)> aEmptyFunc =
+ [](size_t /*nRow*/, size_t /*nCol*/)
+ {
+ // Nothing. Concatenating an empty string to an existing string.
+ };
+
+
+ if (nC1 == 1 || nR1 == 1)
+ {
+ size_t nRowRep = nR1 == 1 ? nMaxRow : 1;
+ size_t nColRep = nC1 == 1 ? nMaxCol : 1;
+
+ for (size_t i = 0; i < nRowRep; ++i)
+ {
+ nRowOffset = i;
+ for (size_t j = 0; j < nColRep; ++j)
+ {
+ nColOffset = j;
+ xMat1->ExecuteOperation(
+ std::pair<size_t, size_t>(0, 0),
+ std::pair<size_t, size_t>(std::min(nR1, nMaxRow) - 1, std::min(nC1, nMaxCol) - 1),
+ aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc);
+ }
+ }
+ }
+ else
+ xMat1->ExecuteOperation(
+ std::pair<size_t, size_t>(0, 0),
+ std::pair<size_t, size_t>(nMaxRow - 1, nMaxCol - 1),
+ std::move(aDoubleFunc), std::move(aBoolFunc), std::move(aStringFunc), std::move(aEmptyFunc));
+
+ std::vector<svl::SharedString> aSharedString(nMaxCol*nMaxRow);
+
+ std::function<void(size_t, size_t, double)> aDoubleFunc2 =
+ [&](size_t nRow, size_t nCol, double nVal)
+ {
+ FormulaError nErr = GetDoubleErrorValue(nVal);
+ if (nErr != FormulaError::NONE)
+ {
+ aValid[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = false;
+ nErrors[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = nErr;
+ return;
+ }
+ OUString aStr;
+ rFormatter.GetInputLineString( nVal, nKey, aStr);
+ aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr);
+ };
+
+ std::function<void(size_t, size_t, bool)> aBoolFunc2 =
+ [&](size_t nRow, size_t nCol, bool nVal)
+ {
+ OUString aStr;
+ rFormatter.GetInputLineString( nVal ? 1.0 : 0.0, nKey, aStr);
+ aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] = rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr);
+ };
+
+ std::function<void(size_t, size_t, const svl::SharedString&)> aStringFunc2 =
+ [&](size_t nRow, size_t nCol, const svl::SharedString& aStr)
+ {
+ aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] =
+ rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] + aStr.getString());
+ };
+
+ std::function<void(size_t, size_t)> aEmptyFunc2 =
+ [&](size_t nRow, size_t nCol)
+ {
+ aSharedString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)] =
+ rStringPool.intern(aString[get_index(nMaxRow, nRow, nCol, nRowOffset, nColOffset)]);
+ };
+
+ nRowOffset = 0;
+ nColOffset = 0;
+ if (nC2 == 1 || nR2 == 1)
+ {
+ size_t nRowRep = nR2 == 1 ? nMaxRow : 1;
+ size_t nColRep = nC2 == 1 ? nMaxCol : 1;
+
+ for (size_t i = 0; i < nRowRep; ++i)
+ {
+ nRowOffset = i;
+ for (size_t j = 0; j < nColRep; ++j)
+ {
+ nColOffset = j;
+ xMat2->ExecuteOperation(
+ std::pair<size_t, size_t>(0, 0),
+ std::pair<size_t, size_t>(std::min(nR2, nMaxRow) - 1, std::min(nC2, nMaxCol) - 1),
+ aDoubleFunc2, aBoolFunc2, aStringFunc2, aEmptyFunc2);
+ }
+ }
+ }
+ else
+ xMat2->ExecuteOperation(
+ std::pair<size_t, size_t>(0, 0),
+ std::pair<size_t, size_t>(nMaxRow - 1, nMaxCol - 1),
+ std::move(aDoubleFunc2), std::move(aBoolFunc2), std::move(aStringFunc2), std::move(aEmptyFunc2));
+
+ aString.clear();
+
+ MatrixImplType::position_type pos = maMat.position(0, 0);
+ for (SCSIZE i = 0; i < nMaxCol; ++i)
+ {
+ for (SCSIZE j = 0; j < nMaxRow && i < nMaxCol; ++j)
+ {
+ if (aValid[nMaxRow * i + j])
+ {
+ auto itr = aValid.begin();
+ std::advance(itr, nMaxRow * i + j);
+ auto itrEnd = std::find(itr, aValid.end(), false);
+ size_t nSteps = std::distance(itr, itrEnd);
+ auto itrStr = aSharedString.begin();
+ std::advance(itrStr, nMaxRow * i + j);
+ auto itrEndStr = itrStr;
+ std::advance(itrEndStr, nSteps);
+ pos = maMat.set(pos, itrStr, itrEndStr);
+ size_t nColSteps = nSteps / nMaxRow;
+ i += nColSteps;
+ j += nSteps % nMaxRow;
+ if (j >= nMaxRow)
+ {
+ j -= nMaxRow;
+ ++i;
+ }
+ }
+ else
+ {
+ pos = maMat.set(pos, CreateDoubleError(nErrors[nMaxRow * i + j]));
+ }
+ pos = MatrixImplType::next_position(pos);
+ }
+ }
+}
+
+bool ScMatrixImpl::IsValueOrEmpty( const MatrixImplType::const_position_type & rPos ) const
+{
+ switch (maMat.get_type(rPos))
+ {
+ case mdds::mtm::element_boolean:
+ case mdds::mtm::element_numeric:
+ case mdds::mtm::element_empty:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+double ScMatrixImpl::GetDouble(const MatrixImplType::const_position_type & rPos) const
+{
+ double fVal = maMat.get_numeric(rPos);
+ if ( pErrorInterpreter )
+ {
+ FormulaError nError = GetDoubleErrorValue(fVal);
+ if ( nError != FormulaError::NONE )
+ SetErrorAtInterpreter( nError);
+ }
+ return fVal;
+}
+
+FormulaError ScMatrixImpl::GetErrorIfNotString( const MatrixImplType::const_position_type & rPos ) const
+{ return IsValue(rPos) ? GetError(rPos) : FormulaError::NONE; }
+
+bool ScMatrixImpl::IsValue( const MatrixImplType::const_position_type & rPos ) const
+{
+ switch (maMat.get_type(rPos))
+ {
+ case mdds::mtm::element_boolean:
+ case mdds::mtm::element_numeric:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+FormulaError ScMatrixImpl::GetError(const MatrixImplType::const_position_type & rPos) const
+{
+ double fVal = maMat.get_numeric(rPos);
+ return GetDoubleErrorValue(fVal);
+}
+
+bool ScMatrixImpl::IsStringOrEmpty(const MatrixImplType::const_position_type & rPos) const
+{
+ switch (maMat.get_type(rPos))
+ {
+ case mdds::mtm::element_empty:
+ case mdds::mtm::element_string:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+void ScMatrixImpl::ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2,
+ ScInterpreter* pInterpreter, ScMatrix::CalculateOpFunction Op)
+{
+ // Check output matrix size, otherwise output iterator logic will be wrong.
+ assert(maMat.size().row == nMaxRow && maMat.size().column == nMaxCol
+ && "the caller code should have sized the output matrix to the passed dimensions");
+ auto & rMatImpl1 = *rInputMat1.pImpl;
+ auto & rMatImpl2 = *rInputMat2.pImpl;
+ // Check if we can do fast-path, where we have no replication or mis-matched matrix sizes.
+ if (rMatImpl1.maMat.size() == rMatImpl2.maMat.size()
+ && rMatImpl1.maMat.size() == maMat.size())
+ {
+ MatrixImplType::position_type aOutPos = maMat.position(0, 0);
+ MatrixImplType::const_position_type aPos1 = rMatImpl1.maMat.position(0, 0);
+ MatrixImplType::const_position_type aPos2 = rMatImpl2.maMat.position(0, 0);
+ for (SCSIZE i = 0; i < nMaxCol; i++)
+ {
+ for (SCSIZE j = 0; j < nMaxRow; j++)
+ {
+ bool bVal1 = rMatImpl1.IsValueOrEmpty(aPos1);
+ bool bVal2 = rMatImpl2.IsValueOrEmpty(aPos2);
+ FormulaError nErr;
+ if (bVal1 && bVal2)
+ {
+ double d = Op(rMatImpl1.GetDouble(aPos1), rMatImpl2.GetDouble(aPos2));
+ aOutPos = maMat.set(aOutPos, d);
+ }
+ else if (((nErr = rMatImpl1.GetErrorIfNotString(aPos1)) != FormulaError::NONE) ||
+ ((nErr = rMatImpl2.GetErrorIfNotString(aPos2)) != FormulaError::NONE))
+ {
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(nErr));
+ }
+ else if ((!bVal1 && rMatImpl1.IsStringOrEmpty(aPos1)) ||
+ (!bVal2 && rMatImpl2.IsStringOrEmpty(aPos2)))
+ {
+ FormulaError nError1 = FormulaError::NONE;
+ SvNumFormatType nFmt1 = SvNumFormatType::ALL;
+ double fVal1 = (bVal1 ? rMatImpl1.GetDouble(aPos1) :
+ pInterpreter->ConvertStringToValue( rMatImpl1.GetString(aPos1).getString(), nError1, nFmt1));
+
+ FormulaError nError2 = FormulaError::NONE;
+ SvNumFormatType nFmt2 = SvNumFormatType::ALL;
+ double fVal2 = (bVal2 ? rMatImpl2.GetDouble(aPos2) :
+ pInterpreter->ConvertStringToValue( rMatImpl2.GetString(aPos2).getString(), nError2, nFmt2));
+
+ if (nError1 != FormulaError::NONE)
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(nError1));
+ else if (nError2 != FormulaError::NONE)
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(nError2));
+ else
+ {
+ double d = Op( fVal1, fVal2);
+ aOutPos = maMat.set(aOutPos, d);
+ }
+ }
+ else
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(FormulaError::NoValue));
+ aPos1 = MatrixImplType::next_position(aPos1);
+ aPos2 = MatrixImplType::next_position(aPos2);
+ aOutPos = MatrixImplType::next_position(aOutPos);
+ }
+ }
+ }
+ else
+ {
+ // Noting that this block is very hard to optimise to use iterators, because various dodgy
+ // array function usage relies on the semantics of some of the methods we call here.
+ // (see unit test testDubiousArrayFormulasFODS).
+ // These methods are inconsistent in their usage of ValidColRowReplicated() vs. ValidColRowOrReplicated()
+ // which leads to some very odd results.
+ MatrixImplType::position_type aOutPos = maMat.position(0, 0);
+ for (SCSIZE i = 0; i < nMaxCol; i++)
+ {
+ for (SCSIZE j = 0; j < nMaxRow; j++)
+ {
+ bool bVal1 = rInputMat1.IsValueOrEmpty(i,j);
+ bool bVal2 = rInputMat2.IsValueOrEmpty(i,j);
+ FormulaError nErr;
+ if (bVal1 && bVal2)
+ {
+ double d = Op(rInputMat1.GetDouble(i,j), rInputMat2.GetDouble(i,j));
+ aOutPos = maMat.set(aOutPos, d);
+ }
+ else if (((nErr = rInputMat1.GetErrorIfNotString(i,j)) != FormulaError::NONE) ||
+ ((nErr = rInputMat2.GetErrorIfNotString(i,j)) != FormulaError::NONE))
+ {
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(nErr));
+ }
+ else if ((!bVal1 && rInputMat1.IsStringOrEmpty(i,j)) || (!bVal2 && rInputMat2.IsStringOrEmpty(i,j)))
+ {
+ FormulaError nError1 = FormulaError::NONE;
+ SvNumFormatType nFmt1 = SvNumFormatType::ALL;
+ double fVal1 = (bVal1 ? rInputMat1.GetDouble(i,j) :
+ pInterpreter->ConvertStringToValue( rInputMat1.GetString(i,j).getString(), nError1, nFmt1));
+
+ FormulaError nError2 = FormulaError::NONE;
+ SvNumFormatType nFmt2 = SvNumFormatType::ALL;
+ double fVal2 = (bVal2 ? rInputMat2.GetDouble(i,j) :
+ pInterpreter->ConvertStringToValue( rInputMat2.GetString(i,j).getString(), nError2, nFmt2));
+
+ if (nError1 != FormulaError::NONE)
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(nError1));
+ else if (nError2 != FormulaError::NONE)
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(nError2));
+ else
+ {
+ double d = Op( fVal1, fVal2);
+ aOutPos = maMat.set(aOutPos, d);
+ }
+ }
+ else
+ aOutPos = maMat.set(aOutPos, CreateDoubleError(FormulaError::NoValue));
+ aOutPos = MatrixImplType::next_position(aOutPos);
+ }
+ }
+ }
+}
+
+void ScMatrix::IncRef() const
+{
+ ++nRefCnt;
+}
+
+void ScMatrix::DecRef() const
+{
+ --nRefCnt;
+ if (nRefCnt == 0)
+ delete this;
+}
+
+bool ScMatrix::IsSizeAllocatable( SCSIZE nC, SCSIZE nR )
+{
+ SAL_WARN_IF( !nC, "sc.core", "ScMatrix with 0 columns!");
+ SAL_WARN_IF( !nR, "sc.core", "ScMatrix with 0 rows!");
+ // 0-size matrix is valid, it could be resized later.
+ if ((nC && !nR) || (!nC && nR))
+ {
+ SAL_WARN( "sc.core", "ScMatrix one-dimensional zero: " << nC << " columns * " << nR << " rows");
+ return false;
+ }
+ if (!nC || !nR)
+ return true;
+
+ std::call_once(bElementsMaxFetched,
+ []()
+ {
+ const char* pEnv = std::getenv("SC_MAX_MATRIX_ELEMENTS");
+ if (pEnv)
+ {
+ // Environment specifies the overall elements pool.
+ nElementsMax = std::atoi(pEnv);
+ }
+ else
+ {
+ // GetElementsMax() uses an (~arbitrary) elements limit.
+ // The actual allocation depends on the types of individual matrix
+ // elements and is averaged for type double.
+#if SAL_TYPES_SIZEOFPOINTER < 8
+ // Assume 1GB memory could be consumed by matrices.
+ constexpr size_t nMemMax = 0x40000000;
+#else
+ // Assume 6GB memory could be consumed by matrices.
+ constexpr size_t nMemMax = 0x180000000;
+#endif
+ nElementsMax = GetElementsMax( nMemMax);
+ }
+ });
+
+ if (nC > (nElementsMax / nR))
+ {
+ SAL_WARN( "sc.core", "ScMatrix overflow: " << nC << " columns * " << nR << " rows");
+ return false;
+ }
+ return true;
+}
+
+ScMatrix::ScMatrix( SCSIZE nC, SCSIZE nR) :
+ nRefCnt(0), mbCloneIfConst(true)
+{
+ if (ScMatrix::IsSizeAllocatable( nC, nR))
+ pImpl.reset( new ScMatrixImpl( nC, nR));
+ else
+ // Invalid matrix size, allocate 1x1 matrix with error value.
+ pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( FormulaError::MatrixSize)));
+}
+
+ScMatrix::ScMatrix(SCSIZE nC, SCSIZE nR, double fInitVal) :
+ nRefCnt(0), mbCloneIfConst(true)
+{
+ if (ScMatrix::IsSizeAllocatable( nC, nR))
+ pImpl.reset( new ScMatrixImpl( nC, nR, fInitVal));
+ else
+ // Invalid matrix size, allocate 1x1 matrix with error value.
+ pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( FormulaError::MatrixSize)));
+}
+
+ScMatrix::ScMatrix( size_t nC, size_t nR, const std::vector<double>& rInitVals ) :
+ nRefCnt(0), mbCloneIfConst(true)
+{
+ if (ScMatrix::IsSizeAllocatable( nC, nR))
+ pImpl.reset( new ScMatrixImpl( nC, nR, rInitVals));
+ else
+ // Invalid matrix size, allocate 1x1 matrix with error value.
+ pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( FormulaError::MatrixSize)));
+}
+
+ScMatrix::~ScMatrix()
+{
+}
+
+ScMatrix* ScMatrix::Clone() const
+{
+ SCSIZE nC, nR;
+ pImpl->GetDimensions(nC, nR);
+ ScMatrix* pScMat = new ScMatrix(nC, nR);
+ MatCopy(*pScMat);
+ pScMat->SetErrorInterpreter(pImpl->GetErrorInterpreter()); // TODO: really?
+ return pScMat;
+}
+
+ScMatrix* ScMatrix::CloneIfConst()
+{
+ return mbCloneIfConst ? Clone() : this;
+}
+
+void ScMatrix::SetMutable()
+{
+ mbCloneIfConst = false;
+}
+
+void ScMatrix::SetImmutable() const
+{
+ mbCloneIfConst = true;
+}
+
+void ScMatrix::Resize( SCSIZE nC, SCSIZE nR)
+{
+ pImpl->Resize(nC, nR);
+}
+
+void ScMatrix::Resize(SCSIZE nC, SCSIZE nR, double fVal)
+{
+ pImpl->Resize(nC, nR, fVal);
+}
+
+ScMatrix* ScMatrix::CloneAndExtend(SCSIZE nNewCols, SCSIZE nNewRows) const
+{
+ ScMatrix* pScMat = new ScMatrix(nNewCols, nNewRows);
+ MatCopy(*pScMat);
+ pScMat->SetErrorInterpreter(pImpl->GetErrorInterpreter());
+ return pScMat;
+}
+
+void ScMatrix::SetErrorInterpreter( ScInterpreter* p)
+{
+ pImpl->SetErrorInterpreter(p);
+}
+
+void ScMatrix::GetDimensions( SCSIZE& rC, SCSIZE& rR) const
+{
+ pImpl->GetDimensions(rC, rR);
+}
+
+SCSIZE ScMatrix::GetElementCount() const
+{
+ return pImpl->GetElementCount();
+}
+
+bool ScMatrix::ValidColRow( SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->ValidColRow(nC, nR);
+}
+
+bool ScMatrix::ValidColRowReplicated( SCSIZE & rC, SCSIZE & rR ) const
+{
+ return pImpl->ValidColRowReplicated(rC, rR);
+}
+
+bool ScMatrix::ValidColRowOrReplicated( SCSIZE & rC, SCSIZE & rR ) const
+{
+ return ValidColRow( rC, rR) || ValidColRowReplicated( rC, rR);
+}
+
+void ScMatrix::PutDouble(double fVal, SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutDouble(fVal, nC, nR);
+}
+
+void ScMatrix::PutDouble( double fVal, SCSIZE nIndex)
+{
+ pImpl->PutDouble(fVal, nIndex);
+}
+
+void ScMatrix::PutDouble(const double* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutDouble(pArray, nLen, nC, nR);
+}
+
+void ScMatrix::PutString(const svl::SharedString& rStr, SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutString(rStr, nC, nR);
+}
+
+void ScMatrix::PutString(const svl::SharedString& rStr, SCSIZE nIndex)
+{
+ pImpl->PutString(rStr, nIndex);
+}
+
+void ScMatrix::PutString(const svl::SharedString* pArray, size_t nLen, SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutString(pArray, nLen, nC, nR);
+}
+
+void ScMatrix::PutEmpty(SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutEmpty(nC, nR);
+}
+
+void ScMatrix::PutEmptyPath(SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutEmptyPath(nC, nR);
+}
+
+void ScMatrix::PutError( FormulaError nErrorCode, SCSIZE nC, SCSIZE nR )
+{
+ pImpl->PutError(nErrorCode, nC, nR);
+}
+
+void ScMatrix::PutBoolean(bool bVal, SCSIZE nC, SCSIZE nR)
+{
+ pImpl->PutBoolean(bVal, nC, nR);
+}
+
+FormulaError ScMatrix::GetError( SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->GetError(nC, nR);
+}
+
+double ScMatrix::GetDouble(SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->GetDouble(nC, nR);
+}
+
+double ScMatrix::GetDouble( SCSIZE nIndex) const
+{
+ return pImpl->GetDouble(nIndex);
+}
+
+double ScMatrix::GetDoubleWithStringConversion(SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->GetDoubleWithStringConversion(nC, nR);
+}
+
+svl::SharedString ScMatrix::GetString(SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->GetString(nC, nR);
+}
+
+svl::SharedString ScMatrix::GetString( SCSIZE nIndex) const
+{
+ return pImpl->GetString(nIndex);
+}
+
+svl::SharedString ScMatrix::GetString( SvNumberFormatter& rFormatter, SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->GetString(rFormatter, nC, nR);
+}
+
+ScMatrixValue ScMatrix::Get(SCSIZE nC, SCSIZE nR) const
+{
+ return pImpl->Get(nC, nR);
+}
+
+bool ScMatrix::IsStringOrEmpty( SCSIZE nIndex ) const
+{
+ return pImpl->IsStringOrEmpty(nIndex);
+}
+
+bool ScMatrix::IsStringOrEmpty( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsStringOrEmpty(nC, nR);
+}
+
+bool ScMatrix::IsEmpty( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsEmpty(nC, nR);
+}
+
+bool ScMatrix::IsEmptyCell( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsEmptyCell(nC, nR);
+}
+
+bool ScMatrix::IsEmptyResult( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsEmptyResult(nC, nR);
+}
+
+bool ScMatrix::IsEmptyPath( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsEmptyPath(nC, nR);
+}
+
+bool ScMatrix::IsValue( SCSIZE nIndex ) const
+{
+ return pImpl->IsValue(nIndex);
+}
+
+bool ScMatrix::IsValue( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsValue(nC, nR);
+}
+
+bool ScMatrix::IsValueOrEmpty( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsValueOrEmpty(nC, nR);
+}
+
+bool ScMatrix::IsBoolean( SCSIZE nC, SCSIZE nR ) const
+{
+ return pImpl->IsBoolean(nC, nR);
+}
+
+bool ScMatrix::IsNumeric() const
+{
+ return pImpl->IsNumeric();
+}
+
+void ScMatrix::MatCopy(const ScMatrix& mRes) const
+{
+ pImpl->MatCopy(*mRes.pImpl);
+}
+
+void ScMatrix::MatTrans(const ScMatrix& mRes) const
+{
+ pImpl->MatTrans(*mRes.pImpl);
+}
+
+void ScMatrix::FillDouble( double fVal, SCSIZE nC1, SCSIZE nR1, SCSIZE nC2, SCSIZE nR2 )
+{
+ pImpl->FillDouble(fVal, nC1, nR1, nC2, nR2);
+}
+
+void ScMatrix::PutDoubleVector( const ::std::vector< double > & rVec, SCSIZE nC, SCSIZE nR )
+{
+ pImpl->PutDoubleVector(rVec, nC, nR);
+}
+
+void ScMatrix::PutStringVector( const ::std::vector< svl::SharedString > & rVec, SCSIZE nC, SCSIZE nR )
+{
+ pImpl->PutStringVector(rVec, nC, nR);
+}
+
+void ScMatrix::PutEmptyVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
+{
+ pImpl->PutEmptyVector(nCount, nC, nR);
+}
+
+void ScMatrix::PutEmptyResultVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
+{
+ pImpl->PutEmptyResultVector(nCount, nC, nR);
+}
+
+void ScMatrix::PutEmptyPathVector( SCSIZE nCount, SCSIZE nC, SCSIZE nR )
+{
+ pImpl->PutEmptyPathVector(nCount, nC, nR);
+}
+
+void ScMatrix::CompareEqual()
+{
+ pImpl->CompareEqual();
+}
+
+void ScMatrix::CompareNotEqual()
+{
+ pImpl->CompareNotEqual();
+}
+
+void ScMatrix::CompareLess()
+{
+ pImpl->CompareLess();
+}
+
+void ScMatrix::CompareGreater()
+{
+ pImpl->CompareGreater();
+}
+
+void ScMatrix::CompareLessEqual()
+{
+ pImpl->CompareLessEqual();
+}
+
+void ScMatrix::CompareGreaterEqual()
+{
+ pImpl->CompareGreaterEqual();
+}
+
+double ScMatrix::And() const
+{
+ return pImpl->And();
+}
+
+double ScMatrix::Or() const
+{
+ return pImpl->Or();
+}
+
+double ScMatrix::Xor() const
+{
+ return pImpl->Xor();
+}
+
+ScMatrix::KahanIterateResult ScMatrix::Sum(bool bTextAsZero, bool bIgnoreErrorValues) const
+{
+ return pImpl->Sum(bTextAsZero, bIgnoreErrorValues);
+}
+
+ScMatrix::KahanIterateResult ScMatrix::SumSquare(bool bTextAsZero, bool bIgnoreErrorValues) const
+{
+ return pImpl->SumSquare(bTextAsZero, bIgnoreErrorValues);
+}
+
+ScMatrix::DoubleIterateResult ScMatrix::Product(bool bTextAsZero, bool bIgnoreErrorValues) const
+{
+ return pImpl->Product(bTextAsZero, bIgnoreErrorValues);
+}
+
+size_t ScMatrix::Count(bool bCountStrings, bool bCountErrors, bool bIgnoreEmptyStrings) const
+{
+ return pImpl->Count(bCountStrings, bCountErrors, bIgnoreEmptyStrings);
+}
+
+size_t ScMatrix::MatchDoubleInColumns(double fValue, size_t nCol1, size_t nCol2) const
+{
+ return pImpl->MatchDoubleInColumns(fValue, nCol1, nCol2);
+}
+
+size_t ScMatrix::MatchStringInColumns(const svl::SharedString& rStr, size_t nCol1, size_t nCol2) const
+{
+ return pImpl->MatchStringInColumns(rStr, nCol1, nCol2);
+}
+
+double ScMatrix::GetMaxValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
+{
+ return pImpl->GetMaxValue(bTextAsZero, bIgnoreErrorValues);
+}
+
+double ScMatrix::GetMinValue( bool bTextAsZero, bool bIgnoreErrorValues ) const
+{
+ return pImpl->GetMinValue(bTextAsZero, bIgnoreErrorValues);
+}
+
+double ScMatrix::GetGcd() const
+{
+ return pImpl->GetGcd();
+}
+
+double ScMatrix::GetLcm() const
+{
+ return pImpl->GetLcm();
+}
+
+
+ScMatrixRef ScMatrix::CompareMatrix(
+ sc::Compare& rComp, size_t nMatPos, sc::CompareOptions* pOptions ) const
+{
+ return pImpl->CompareMatrix(rComp, nMatPos, pOptions);
+}
+
+void ScMatrix::GetDoubleArray( std::vector<double>& rArray, bool bEmptyAsZero ) const
+{
+ pImpl->GetDoubleArray(rArray, bEmptyAsZero);
+}
+
+void ScMatrix::MergeDoubleArrayMultiply( std::vector<double>& rArray ) const
+{
+ pImpl->MergeDoubleArrayMultiply(rArray);
+}
+
+namespace matop {
+
+namespace {
+
+/** A template for operations where operands are supposed to be numeric.
+ A non-numeric (string) operand leads to the configured conversion to number
+ method being called if in interpreter context and a FormulaError::NoValue DoubleError
+ if conversion was not possible, else to an unconditional FormulaError::NoValue
+ DoubleError.
+ An empty operand evaluates to 0.
+ */
+template<typename TOp>
+struct MatOp
+{
+private:
+ TOp maOp;
+ ScInterpreter* mpErrorInterpreter;
+ double mfVal;
+
+public:
+ typedef double number_value_type;
+
+ MatOp( TOp aOp, ScInterpreter* pErrorInterpreter,
+ double fVal = 0.0 ):
+ maOp(aOp),
+ mpErrorInterpreter(pErrorInterpreter),
+ mfVal(fVal)
+ {
+ if (mpErrorInterpreter)
+ {
+ FormulaError nErr = mpErrorInterpreter->GetError();
+ if (nErr != FormulaError::NONE)
+ mfVal = CreateDoubleError( nErr);
+ }
+ }
+
+ double operator()(double fVal) const
+ {
+ return maOp(fVal, mfVal);
+ }
+
+ double operator()(bool bVal) const
+ {
+ return maOp(static_cast<double>(bVal), mfVal);
+ }
+
+ double operator()(const svl::SharedString& rStr) const
+ {
+ return maOp( convertStringToValue( mpErrorInterpreter, rStr.getString()), mfVal);
+ }
+
+ /// the action for empty entries in a matrix
+ double operator()(char) const
+ {
+ return maOp(0, mfVal);
+ }
+
+ static bool useFunctionForEmpty()
+ {
+ return true;
+ }
+};
+
+}
+
+}
+
+void ScMatrix::NotOp( const ScMatrix& rMat)
+{
+ auto not_ = [](double a, double){return double(a == 0.0);};
+ matop::MatOp<decltype(not_)> aOp(not_, pImpl->GetErrorInterpreter());
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+}
+
+void ScMatrix::NegOp( const ScMatrix& rMat)
+{
+ auto neg_ = [](double a, double){return -a;};
+ matop::MatOp<decltype(neg_)> aOp(neg_, pImpl->GetErrorInterpreter());
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+}
+
+void ScMatrix::AddOp( double fVal, const ScMatrix& rMat)
+{
+ auto add_ = [](double a, double b){return a + b;};
+ matop::MatOp<decltype(add_)> aOp(add_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+}
+
+void ScMatrix::SubOp( bool bFlag, double fVal, const ScMatrix& rMat)
+{
+ if (bFlag)
+ {
+ auto sub_ = [](double a, double b){return b - a;};
+ matop::MatOp<decltype(sub_)> aOp(sub_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+ }
+ else
+ {
+ auto sub_ = [](double a, double b){return a - b;};
+ matop::MatOp<decltype(sub_)> aOp(sub_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+ }
+}
+
+void ScMatrix::MulOp( double fVal, const ScMatrix& rMat)
+{
+ auto mul_ = [](double a, double b){return a * b;};
+ matop::MatOp<decltype(mul_)> aOp(mul_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+}
+
+void ScMatrix::DivOp( bool bFlag, double fVal, const ScMatrix& rMat)
+{
+ if (bFlag)
+ {
+ auto div_ = [](double a, double b){return sc::div(b, a);};
+ matop::MatOp<decltype(div_)> aOp(div_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+ }
+ else
+ {
+ auto div_ = [](double a, double b){return sc::div(a, b);};
+ matop::MatOp<decltype(div_)> aOp(div_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+ }
+}
+
+void ScMatrix::PowOp( bool bFlag, double fVal, const ScMatrix& rMat)
+{
+ if (bFlag)
+ {
+ auto pow_ = [](double a, double b){return sc::power(b, a);};
+ matop::MatOp<decltype(pow_)> aOp(pow_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+ }
+ else
+ {
+ auto pow_ = [](double a, double b){return sc::power(a, b);};
+ matop::MatOp<decltype(pow_)> aOp(pow_, pImpl->GetErrorInterpreter(), fVal);
+ pImpl->ApplyOperation(aOp, *rMat.pImpl);
+ }
+}
+
+void ScMatrix::ExecuteOperation(const std::pair<size_t, size_t>& rStartPos,
+ const std::pair<size_t, size_t>& rEndPos, DoubleOpFunction aDoubleFunc,
+ BoolOpFunction aBoolFunc, StringOpFunction aStringFunc, EmptyOpFunction aEmptyFunc) const
+{
+ pImpl->ExecuteOperation(rStartPos, rEndPos, aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc);
+}
+
+ScMatrix::KahanIterateResultMultiple ScMatrix::CollectKahan(const std::vector<sc::op::kOp>& aOp)
+{
+ return pImpl->ApplyCollectOperation<sc::op::kOp, KahanSum>(aOp);
+}
+
+#if DEBUG_MATRIX
+void ScMatrix::Dump() const
+{
+ pImpl->Dump();
+}
+#endif
+
+void ScMatrix::MatConcat(SCSIZE nMaxCol, SCSIZE nMaxRow,
+ const ScMatrixRef& xMat1, const ScMatrixRef& xMat2, SvNumberFormatter& rFormatter, svl::SharedStringPool& rPool)
+{
+ pImpl->MatConcat(nMaxCol, nMaxRow, xMat1, xMat2, rFormatter, rPool);
+}
+
+void ScMatrix::ExecuteBinaryOp(SCSIZE nMaxCol, SCSIZE nMaxRow, const ScMatrix& rInputMat1, const ScMatrix& rInputMat2,
+ ScInterpreter* pInterpreter, CalculateOpFunction op)
+{
+ pImpl->ExecuteBinaryOp(nMaxCol, nMaxRow, rInputMat1, rInputMat2, pInterpreter, op);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/scopetools.cxx b/sc/source/core/tool/scopetools.cxx
new file mode 100644
index 0000000000..38ca8c2527
--- /dev/null
+++ b/sc/source/core/tool/scopetools.cxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <scopetools.hxx>
+#include <document.hxx>
+#include <column.hxx>
+
+namespace sc {
+
+AutoCalcSwitch::AutoCalcSwitch(ScDocument& rDoc, bool bAutoCalc) :
+ mrDoc(rDoc), mbOldValue(rDoc.GetAutoCalc())
+{
+ mrDoc.SetAutoCalc(bAutoCalc);
+}
+
+AutoCalcSwitch::~AutoCalcSwitch()
+{
+ mrDoc.SetAutoCalc(mbOldValue);
+}
+
+ExpandRefsSwitch::ExpandRefsSwitch(ScDocument& rDoc, bool bExpandRefs) :
+ mrDoc(rDoc), mbOldValue(rDoc.IsExpandRefs())
+{
+ mrDoc.SetExpandRefs(bExpandRefs);
+}
+
+ExpandRefsSwitch::~ExpandRefsSwitch()
+{
+ mrDoc.SetExpandRefs(mbOldValue);
+}
+
+UndoSwitch::UndoSwitch(ScDocument& rDoc, bool bUndo) :
+ mrDoc(rDoc), mbOldValue(rDoc.IsUndoEnabled())
+{
+ mrDoc.EnableUndo(bUndo);
+}
+
+UndoSwitch::~UndoSwitch()
+{
+ mrDoc.EnableUndo(mbOldValue);
+}
+
+IdleSwitch::IdleSwitch(ScDocument& rDoc, bool bEnableIdle) :
+ mrDoc(rDoc), mbOldValue(rDoc.IsIdleEnabled())
+{
+ mrDoc.EnableIdle(bEnableIdle);
+}
+
+IdleSwitch::~IdleSwitch()
+{
+ mrDoc.EnableIdle(mbOldValue);
+}
+
+DelayFormulaGroupingSwitch::DelayFormulaGroupingSwitch(ScDocument& rDoc, bool delay) :
+ mrDoc(rDoc), mbOldValue(rDoc.IsDelayedFormulaGrouping())
+{
+ mrDoc.DelayFormulaGrouping(delay);
+}
+
+DelayFormulaGroupingSwitch::~DelayFormulaGroupingSwitch() COVERITY_NOEXCEPT_FALSE
+{
+ mrDoc.DelayFormulaGrouping(mbOldValue);
+}
+
+void DelayFormulaGroupingSwitch::reset()
+{
+ mrDoc.DelayFormulaGrouping(mbOldValue);
+}
+
+DelayStartListeningFormulaCells::DelayStartListeningFormulaCells(ScColumn& column, bool delay)
+ : mColumn(column), mbOldValue(column.GetDoc().IsEnabledDelayStartListeningFormulaCells(&column))
+{
+ column.GetDoc().EnableDelayStartListeningFormulaCells(&column, delay);
+}
+
+DelayStartListeningFormulaCells::DelayStartListeningFormulaCells(ScColumn& column)
+ : mColumn(column), mbOldValue(column.GetDoc().IsEnabledDelayStartListeningFormulaCells(&column))
+{
+}
+
+DelayStartListeningFormulaCells::~DelayStartListeningFormulaCells()
+{
+#if defined(__COVERITY__)
+ try
+ {
+ mColumn.GetDoc().EnableDelayStartListeningFormulaCells(&mColumn, mbOldValue);
+ }
+ catch (...)
+ {
+ std::abort();
+ }
+#else
+ mColumn.GetDoc().EnableDelayStartListeningFormulaCells(&mColumn, mbOldValue);
+#endif
+}
+
+void DelayStartListeningFormulaCells::set()
+{
+ mColumn.GetDoc().EnableDelayStartListeningFormulaCells(&mColumn, true);
+}
+
+DelayDeletingBroadcasters::DelayDeletingBroadcasters(ScDocument& doc)
+ : mDoc( doc )
+ , mOldValue( mDoc.IsDelayedDeletingBroadcasters())
+{
+ mDoc.EnableDelayDeletingBroadcasters( true );
+}
+
+DelayDeletingBroadcasters::~DelayDeletingBroadcasters()
+{
+ suppress_fun_call_w_exception(mDoc.EnableDelayDeletingBroadcasters(mOldValue));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/sharedformula.cxx b/sc/source/core/tool/sharedformula.cxx
new file mode 100644
index 0000000000..7680aac405
--- /dev/null
+++ b/sc/source/core/tool/sharedformula.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/.
+ */
+
+#include <sharedformula.hxx>
+#include <calcmacros.hxx>
+#include <tokenarray.hxx>
+#include <listenercontext.hxx>
+#include <document.hxx>
+#include <grouparealistener.hxx>
+#include <refdata.hxx>
+
+namespace sc {
+
+const ScFormulaCell* SharedFormulaUtil::getSharedTopFormulaCell(const CellStoreType::position_type& aPos)
+{
+ if (aPos.first->type != sc::element_type_formula)
+ // Not a formula cell block.
+ return nullptr;
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
+ std::advance(it, aPos.second);
+ const ScFormulaCell* pCell = *it;
+ if (!pCell->IsShared())
+ // Not a shared formula.
+ return nullptr;
+
+ return pCell->GetCellGroup()->mpTopCell;
+}
+
+bool SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt)
+{
+ SCROW nRow = aPos.first->position + aPos.second;
+
+ if (aPos.first->type != sc::element_type_formula)
+ // Not a formula cell block.
+ return false;
+
+ if (aPos.second == 0)
+ // Split position coincides with the block border. Nothing to do.
+ return false;
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
+ std::advance(it, aPos.second);
+ ScFormulaCell& rTop = **it;
+ if (!rTop.IsShared())
+ // Not a shared formula.
+ return false;
+
+ if (nRow == rTop.GetSharedTopRow())
+ // Already the top cell of a shared group.
+ return false;
+
+ ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
+
+ SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
+ ScFormulaCellGroupRef xGroup2;
+ if (nLength2 > 1)
+ {
+ xGroup2.reset(new ScFormulaCellGroup);
+ xGroup2->mbInvariant = xGroup->mbInvariant;
+ xGroup2->mpTopCell = &rTop;
+ xGroup2->mnLength = nLength2;
+ xGroup2->mpCode = xGroup->mpCode->CloneValue();
+ }
+
+ xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
+ ScFormulaCell& rPrevTop = *sc::formula_block::at(*aPos.first->data, aPos.second - xGroup->mnLength);
+
+#if USE_FORMULA_GROUP_LISTENER
+ // At least group area listeners will have to be adapted. As long as
+ // there's no update mechanism and no separated handling of group area and
+ // other listeners, all listeners of this group's top cell are to be reset.
+ if (nLength2)
+ {
+ // If a context exists it has to be used to not interfere with
+ // ScColumn::maBroadcasters iterators, which the EndListeningTo()
+ // without context would do when removing a broadcaster that had its
+ // last listener removed.
+ if (pCxt)
+ rPrevTop.EndListeningTo(*pCxt);
+ else
+ rPrevTop.EndListeningTo( rPrevTop.GetDocument(), nullptr, ScAddress( ScAddress::UNINITIALIZED));
+ rPrevTop.SetNeedsListening(true);
+
+ // The new group or remaining single cell needs a new listening.
+ rTop.SetNeedsListening(true);
+ }
+#endif
+
+ if (xGroup->mnLength == 1)
+ {
+ // The top group consists of only one cell. Ungroup this.
+ ScFormulaCellGroupRef xNone;
+ rPrevTop.SetCellGroup(xNone);
+ }
+
+ // Apply the lower group object to the lower cells.
+ assert ((xGroup2 == nullptr || xGroup2->mpTopCell->aPos.Row() + size_t(xGroup2->mnLength) <= aPos.first->position + aPos.first->size)
+ && "Shared formula region goes beyond the formula block.");
+ sc::formula_block::iterator itEnd = it;
+ std::advance(itEnd, nLength2);
+ for (; it != itEnd; ++it)
+ {
+ ScFormulaCell& rCell = **it;
+ rCell.SetCellGroup(xGroup2);
+ }
+
+ return true;
+}
+
+bool SharedFormulaUtil::splitFormulaCellGroups(const ScDocument& rDoc, CellStoreType& rCells, std::vector<SCROW>& rBounds)
+{
+ if (rBounds.empty())
+ return false;
+
+ // Sort and remove duplicates.
+ std::sort(rBounds.begin(), rBounds.end());
+ std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
+ rBounds.erase(it, rBounds.end());
+
+ it = rBounds.begin();
+ SCROW nRow = *it;
+ CellStoreType::position_type aPos = rCells.position(nRow);
+ if (aPos.first == rCells.end())
+ return false;
+
+ bool bSplit = splitFormulaCellGroup(aPos, nullptr);
+ std::vector<SCROW>::iterator itEnd = rBounds.end();
+ for (++it; it != itEnd; ++it)
+ {
+ nRow = *it;
+ if (rDoc.ValidRow(nRow))
+ {
+ aPos = rCells.position(aPos.first, nRow);
+ if (aPos.first == rCells.end())
+ return bSplit;
+ bSplit |= splitFormulaCellGroup(aPos, nullptr);
+ }
+ }
+ return bSplit;
+}
+
+bool SharedFormulaUtil::joinFormulaCells(
+ const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
+{
+ if( rCell1.GetDocument().IsDelayedFormulaGrouping())
+ {
+ rCell1.GetDocument().AddDelayedFormulaGroupingCell( &rCell1 );
+ rCell1.GetDocument().AddDelayedFormulaGroupingCell( &rCell2 );
+ return false;
+ }
+
+ ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
+ if (eState == ScFormulaCell::NotEqual)
+ return false;
+
+ // Formula tokens equal those of the previous formula cell.
+ ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
+ ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
+ if (xGroup1)
+ {
+ if (xGroup2)
+ {
+ // Both cell 1 and cell 2 are shared. Merge them together.
+ if (xGroup1.get() == xGroup2.get())
+ // They belong to the same group.
+ return false;
+
+ // Set the group object from cell 1 to all cells in group 2.
+ xGroup1->mnLength += xGroup2->mnLength;
+ size_t nOffset = rPos.second + 1; // position of cell 2
+ for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
+ {
+ ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
+ rCell.SetCellGroup(xGroup1);
+ }
+ }
+ else
+ {
+ // cell 1 is shared but cell 2 is not.
+ rCell2.SetCellGroup(xGroup1);
+ ++xGroup1->mnLength;
+ }
+ }
+ else
+ {
+ if (xGroup2)
+ {
+ // cell 1 is not shared, but cell 2 is already shared.
+ rCell1.SetCellGroup(xGroup2);
+ xGroup2->mpTopCell = &rCell1;
+ ++xGroup2->mnLength;
+ }
+ else
+ {
+ // neither cells are shared.
+ assert(rCell1.aPos.Row() == static_cast<SCROW>(rPos.first->position + rPos.second));
+ xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
+ rCell2.SetCellGroup(xGroup1);
+ }
+ }
+
+ return true;
+}
+
+bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
+{
+ if (aPos.first->type != sc::element_type_formula)
+ // This is not a formula cell.
+ return false;
+
+ if (aPos.second == 0)
+ // This cell is already the top cell in a formula block; the previous
+ // cell is not a formula cell.
+ return false;
+
+ ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
+ ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
+ sc::CellStoreType::position_type aPosPrev = aPos;
+ --aPosPrev.second;
+ return joinFormulaCells(aPosPrev, rPrev, rCell);
+}
+
+void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
+{
+ if (!rCell.IsShared())
+ return;
+
+ ScFormulaCellGroupRef xNone;
+ sc::CellStoreType::iterator it = aPos.first;
+
+ // This formula cell is shared. Adjust the shared group.
+ if (rCell.aPos.Row() == rCell.GetSharedTopRow())
+ {
+ // Top of the shared range.
+ const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
+ if (xGroup->mnLength == 2)
+ {
+ // Group consists of only two cells. Mark the second one non-shared.
+ assert (aPos.second+1 < aPos.first->size
+ && "There is no next formula cell but there should be!");
+ ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
+ rNext.SetCellGroup(xNone);
+ }
+ else
+ {
+ // Move the top cell to the next formula cell down.
+ ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
+ xGroup->mpTopCell = &rNext;
+ }
+ --xGroup->mnLength;
+ }
+ else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
+ {
+ // Bottom of the shared range.
+ const ScFormulaCellGroupRef& xGroup = rCell.GetCellGroup();
+ if (xGroup->mnLength == 2)
+ {
+ // Mark the top cell non-shared.
+ assert(aPos.second != 0 && "There is no previous formula cell but there should be!");
+ ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
+ rPrev.SetCellGroup(xNone);
+ }
+ else
+ {
+ // Just shorten the shared range length by one.
+ --xGroup->mnLength;
+ }
+ }
+ else
+ {
+ // In the middle of the shared range. Split it into two groups.
+ ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
+ SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
+ xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
+ if (xGroup->mnLength == 1)
+ {
+ // Make the top cell non-shared.
+ assert(aPos.second != 0 && "There is no previous formula cell but there should be!");
+ ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
+ rPrev.SetCellGroup(xNone);
+ }
+
+ SCROW nLength2 = nEndRow - rCell.aPos.Row();
+ if (nLength2 >= 2)
+ {
+ ScFormulaCellGroupRef xGroup2;
+ xGroup2.reset(new ScFormulaCellGroup);
+ ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
+ xGroup2->mpTopCell = &rNext;
+ xGroup2->mnLength = nLength2;
+ xGroup2->mbInvariant = xGroup->mbInvariant;
+ xGroup2->mpCode = xGroup->mpCode->CloneValue();
+ assert(xGroup2->mpTopCell->aPos.Row() + size_t(xGroup2->mnLength) <= it->position + it->size
+ && "Shared formula region goes beyond the formula block.");
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, aPos.second+1);
+ sc::formula_block::iterator itCellEnd = itCell;
+ std::advance(itCellEnd, xGroup2->mnLength);
+ for (; itCell != itCellEnd; ++itCell)
+ {
+ ScFormulaCell& rCell2 = **itCell;
+ rCell2.SetCellGroup(xGroup2);
+ }
+ }
+ else
+ {
+ // Make the next cell non-shared.
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, aPos.second+1);
+ ScFormulaCell& rCell2 = **itCell;
+ rCell2.SetCellGroup(xNone);
+ }
+ }
+
+ rCell.SetCellGroup(xNone);
+}
+
+void SharedFormulaUtil::unshareFormulaCells(const ScDocument& rDoc, CellStoreType& rCells, std::vector<SCROW>& rRows)
+{
+ if (rRows.empty())
+ return;
+
+ // Sort and remove duplicates.
+ std::sort(rRows.begin(), rRows.end());
+ rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
+
+ // Add next cell positions to the list (to ensure that each position becomes a single cell).
+ std::vector<SCROW> aRows2;
+ for (const auto& rRow : rRows)
+ {
+ if (rRow > rDoc.MaxRow())
+ break;
+
+ aRows2.push_back(rRow);
+
+ if (rRow < rDoc.MaxRow())
+ aRows2.push_back(rRow+1);
+ }
+
+ // Remove duplicates again (the vector should still be sorted).
+ aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
+
+ splitFormulaCellGroups(rDoc, rCells, aRows2);
+}
+
+void SharedFormulaUtil::startListeningAsGroup( sc::StartListeningContext& rCxt, ScFormulaCell** ppSharedTop )
+{
+ ScFormulaCell& rTopCell = **ppSharedTop;
+ assert(rTopCell.IsSharedTop());
+
+#if USE_FORMULA_GROUP_LISTENER
+ ScDocument& rDoc = rCxt.getDoc();
+ rDoc.SetDetectiveDirty(true);
+
+ ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
+ const ScTokenArray& rCode = *xGroup->mpCode;
+ assert(&rCode == rTopCell.GetCode());
+ if (rCode.IsRecalcModeAlways())
+ {
+ rDoc.StartListeningArea(
+ BCA_LISTEN_ALWAYS, false,
+ xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
+ }
+
+ formula::FormulaToken** p = rCode.GetCode();
+ formula::FormulaToken** pEnd = p + rCode.GetCodeLen();
+ for (; p != pEnd; ++p)
+ {
+ const formula::FormulaToken* t = *p;
+ switch (t->GetType())
+ {
+ case formula::svSingleRef:
+ {
+ const ScSingleRefData* pRef = t->GetSingleRef();
+ ScAddress aPos = pRef->toAbs(rDoc, rTopCell.aPos);
+ ScFormulaCell** pp = ppSharedTop;
+ ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
+ for (; pp != ppEnd; ++pp)
+ {
+ if (!aPos.IsValid())
+ break;
+
+ rDoc.StartListeningCell(rCxt, aPos, **pp);
+ if (pRef->IsRowRel())
+ aPos.IncRow();
+ }
+ }
+ break;
+ case formula::svDoubleRef:
+ {
+ const ScSingleRefData& rRef1 = *t->GetSingleRef();
+ const ScSingleRefData& rRef2 = *t->GetSingleRef2();
+ ScAddress aPos1 = rRef1.toAbs(rDoc, rTopCell.aPos);
+ ScAddress aPos2 = rRef2.toAbs(rDoc, rTopCell.aPos);
+
+ ScRange aOrigRange(aPos1, aPos2);
+ ScRange aListenedRange = aOrigRange;
+ if (rRef2.IsRowRel())
+ aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
+
+ if (aPos1.IsValid() && aPos2.IsValid())
+ {
+ rDoc.StartListeningArea(
+ aListenedRange, true,
+ xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ ScFormulaCell** pp = ppSharedTop;
+ ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell& rCell = **pp;
+ rCell.SetNeedsListening(false);
+ }
+
+#else
+ ScFormulaCell** pp = ppSharedTop;
+ ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell& rFC = **pp;
+ rFC.StartListeningTo(rCxt);
+ }
+#endif
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/sharedstringpoolpurge.cxx b/sc/source/core/tool/sharedstringpoolpurge.cxx
new file mode 100644
index 0000000000..7b8749006e
--- /dev/null
+++ b/sc/source/core/tool/sharedstringpoolpurge.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <sharedstringpoolpurge.hxx>
+
+#include <algorithm>
+
+#include <vcl/svapp.hxx>
+
+namespace sc
+{
+SharedStringPoolPurge::SharedStringPoolPurge()
+ : mTimer("SharedStringPoolPurge")
+{
+ mTimer.SetPriority(TaskPriority::LOWEST);
+ mTimer.SetTimeout(10000); // 10 sec
+ mTimer.SetInvokeHandler(LINK(this, SharedStringPoolPurge, timerHandler));
+}
+
+SharedStringPoolPurge::~SharedStringPoolPurge() { cleanup(); }
+
+void SharedStringPoolPurge::delayedPurge(const std::shared_ptr<svl::SharedStringPool>& pool)
+{
+ if (std::find(mPoolsToPurge.begin(), mPoolsToPurge.end(), pool) == mPoolsToPurge.end())
+ {
+ mPoolsToPurge.push_back(pool);
+ SolarMutexGuard guard;
+ mTimer.Start();
+ }
+}
+
+void SharedStringPoolPurge::cleanup()
+{
+ for (std::shared_ptr<svl::SharedStringPool>& pool : mPoolsToPurge)
+ {
+ if (pool.use_count() > 1)
+ pool->purge();
+ }
+ mPoolsToPurge.clear();
+}
+
+IMPL_LINK_NOARG(SharedStringPoolPurge, timerHandler, Timer*, void)
+{
+ SolarMutexGuard guard;
+ mTimer.Stop();
+ cleanup();
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/stringutil.cxx b/sc/source/core/tool/stringutil.cxx
new file mode 100644
index 0000000000..8a436386ae
--- /dev/null
+++ b/sc/source/core/tool/stringutil.cxx
@@ -0,0 +1,469 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stringutil.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/math.hxx>
+
+ScSetStringParam::ScSetStringParam() :
+ mpNumFormatter(nullptr),
+ mbDetectNumberFormat(true),
+ mbDetectScientificNumberFormat(true),
+ meSetTextNumFormat(Never),
+ mbHandleApostrophe(true),
+ meStartListening(sc::SingleCellListening),
+ mbCheckLinkFormula(false)
+{
+}
+
+void ScSetStringParam::setTextInput()
+{
+ mbDetectNumberFormat = false;
+ mbDetectScientificNumberFormat = false;
+ mbHandleApostrophe = false;
+ meSetTextNumFormat = Always;
+}
+
+void ScSetStringParam::setNumericInput()
+{
+ mbDetectNumberFormat = true;
+ mbDetectScientificNumberFormat = true;
+ mbHandleApostrophe = true;
+ meSetTextNumFormat = Never;
+}
+
+bool ScStringUtil::parseSimpleNumber(
+ const OUString& rStr, sal_Unicode dsep, sal_Unicode gsep, sal_Unicode dsepa, double& rVal, bool bDetectScientificNumber)
+{
+ // Actually almost the entire pre-check is unnecessary and we could call
+ // rtl::math::stringToDouble() just after having exchanged ascii space with
+ // non-breaking space, if it wasn't for check of grouped digits. The NaN
+ // and Inf cases that are accepted by stringToDouble() could be detected
+ // using std::isfinite() on the result.
+
+ /* TODO: The grouped digits check isn't even valid for locales that do not
+ * group in thousands ... e.g. Indian locales. But that's something also
+ * the number scanner doesn't implement yet, only the formatter. */
+
+ OUStringBuffer aBuf;
+
+ sal_Int32 i = 0;
+ sal_Int32 n = rStr.getLength();
+ const sal_Unicode* p = rStr.getStr();
+ const sal_Unicode* pLast = p + (n-1);
+ sal_Int32 nPosDSep = -1, nPosGSep = -1;
+ sal_uInt32 nDigitCount = 0;
+ bool haveSeenDigit = false;
+ sal_Int32 nPosExponent = -1;
+
+ // Skip preceding spaces.
+ for (i = 0; i < n; ++i, ++p)
+ {
+ sal_Unicode c = *p;
+ if (c != 0x0020 && c != 0x00A0)
+ // first non-space character. Exit.
+ break;
+ }
+
+ if (i == n)
+ // the whole string is space. Fail.
+ return false;
+
+ n -= i; // Subtract the length of the preceding spaces.
+
+ // Determine the last non-space character.
+ for (; p != pLast; --pLast, --n)
+ {
+ sal_Unicode c = *pLast;
+ if (c != 0x0020 && c != 0x00A0)
+ // Non space character. Exit.
+ break;
+ }
+
+ for (i = 0; i < n; ++i, ++p)
+ {
+ sal_Unicode c = *p;
+ if (c == 0x0020 && gsep == 0x00A0)
+ // ascii space to unicode space if that is group separator
+ c = 0x00A0;
+
+ if ('0' <= c && c <= '9')
+ {
+ // this is a digit.
+ aBuf.append(c);
+ haveSeenDigit = true;
+ ++nDigitCount;
+ }
+ else if (c == dsep || (dsepa && c == dsepa))
+ {
+ // this is a decimal separator.
+
+ if (nPosDSep >= 0)
+ // a second decimal separator -> not a valid number.
+ return false;
+
+ if (nPosGSep >= 0 && i - nPosGSep != 4)
+ // the number has a group separator and the decimal sep is not
+ // positioned correctly.
+ return false;
+
+ nPosDSep = i;
+ nPosGSep = -1;
+ aBuf.append(dsep); // append the separator that is parsed in stringToDouble() below
+ nDigitCount = 0;
+ }
+ else if (c == gsep)
+ {
+ // this is a group (thousand) separator.
+
+ if (!haveSeenDigit)
+ // not allowed before digits.
+ return false;
+
+ if (nPosDSep >= 0)
+ // not allowed after the decimal separator.
+ return false;
+
+ if (nPosGSep >= 0 && nDigitCount != 3)
+ // must be exactly 3 digits since the last group separator.
+ return false;
+
+ if (nPosExponent >= 0)
+ // not allowed in exponent.
+ return false;
+
+ nPosGSep = i;
+ nDigitCount = 0;
+ }
+ else if (c == '-' || c == '+')
+ {
+ // A sign must be the first character if it's given, or immediately
+ // follow the exponent character if present.
+ if (i == 0 || (nPosExponent >= 0 && i == nPosExponent + 1))
+ aBuf.append(c);
+ else
+ return false;
+ }
+ else if (c == 'E' || c == 'e')
+ {
+ // this is an exponent designator.
+
+ if (nPosExponent >= 0 || !bDetectScientificNumber)
+ // Only one exponent allowed.
+ return false;
+
+ if (nPosGSep >= 0 && nDigitCount != 3)
+ // must be exactly 3 digits since the last group separator.
+ return false;
+
+ aBuf.append(c);
+ nPosExponent = i;
+ nPosDSep = -1;
+ nPosGSep = -1;
+ nDigitCount = 0;
+ }
+ else
+ return false;
+ }
+
+ // finished parsing the number.
+
+ if (nPosGSep >= 0 && nDigitCount != 3)
+ // must be exactly 3 digits since the last group separator.
+ return false;
+
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ sal_Int32 nParseEnd = 0;
+ rVal = ::rtl::math::stringToDouble( aBuf, dsep, gsep, &eStatus, &nParseEnd);
+ if (eStatus != rtl_math_ConversionStatus_Ok || nParseEnd < aBuf.getLength())
+ // Not a valid number or not entire string consumed.
+ return false;
+
+ return true;
+}
+
+bool ScStringUtil::parseSimpleNumber(
+ const char* p, size_t n, char dsep, char gsep, double& rVal)
+{
+ // Actually almost the entire pre-check is unnecessary and we could call
+ // rtl::math::stringToDouble() just after having exchanged ascii space with
+ // non-breaking space, if it wasn't for check of grouped digits. The NaN
+ // and Inf cases that are accepted by stringToDouble() could be detected
+ // using std::isfinite() on the result.
+
+ /* TODO: The grouped digits check isn't even valid for locales that do not
+ * group in thousands ... e.g. Indian locales. But that's something also
+ * the number scanner doesn't implement yet, only the formatter. */
+
+ OStringBuffer aBuf;
+
+ size_t i = 0;
+ const char* pLast = p + (n-1);
+ sal_Int32 nPosDSep = -1, nPosGSep = -1;
+ sal_uInt32 nDigitCount = 0;
+ bool haveSeenDigit = false;
+ sal_Int32 nPosExponent = -1;
+
+ // Skip preceding spaces.
+ for (i = 0; i < n; ++i, ++p)
+ {
+ char c = *p;
+ if (c != ' ')
+ // first non-space character. Exit.
+ break;
+ }
+
+ if (i == n)
+ // the whole string is space. Fail.
+ return false;
+
+ n -= i; // Subtract the length of the preceding spaces.
+
+ // Determine the last non-space character.
+ for (; p != pLast; --pLast, --n)
+ {
+ char c = *pLast;
+ if (c != ' ')
+ // Non space character. Exit.
+ break;
+ }
+
+ for (i = 0; i < n; ++i, ++p)
+ {
+ char c = *p;
+
+ if ('0' <= c && c <= '9')
+ {
+ // this is a digit.
+ aBuf.append(c);
+ haveSeenDigit = true;
+ ++nDigitCount;
+ }
+ else if (c == dsep)
+ {
+ // this is a decimal separator.
+
+ if (nPosDSep >= 0)
+ // a second decimal separator -> not a valid number.
+ return false;
+
+ if (nPosGSep >= 0 && i - nPosGSep != 4)
+ // the number has a group separator and the decimal sep is not
+ // positioned correctly.
+ return false;
+
+ nPosDSep = i;
+ nPosGSep = -1;
+ aBuf.append(c);
+ nDigitCount = 0;
+ }
+ else if (c == gsep)
+ {
+ // this is a group (thousand) separator.
+
+ if (!haveSeenDigit)
+ // not allowed before digits.
+ return false;
+
+ if (nPosDSep >= 0)
+ // not allowed after the decimal separator.
+ return false;
+
+ if (nPosGSep >= 0 && nDigitCount != 3)
+ // must be exactly 3 digits since the last group separator.
+ return false;
+
+ if (nPosExponent >= 0)
+ // not allowed in exponent.
+ return false;
+
+ nPosGSep = i;
+ nDigitCount = 0;
+ }
+ else if (c == '-' || c == '+')
+ {
+ // A sign must be the first character if it's given, or immediately
+ // follow the exponent character if present.
+ if (i == 0 || (nPosExponent >= 0 && i == static_cast<size_t>(nPosExponent+1)))
+ aBuf.append(c);
+ else
+ return false;
+ }
+ else if (c == 'E' || c == 'e')
+ {
+ // this is an exponent designator.
+
+ if (nPosExponent >= 0)
+ // Only one exponent allowed.
+ return false;
+
+ if (nPosGSep >= 0 && nDigitCount != 3)
+ // must be exactly 3 digits since the last group separator.
+ return false;
+
+ aBuf.append(c);
+ nPosExponent = i;
+ nPosDSep = -1;
+ nPosGSep = -1;
+ nDigitCount = 0;
+ }
+ else
+ return false;
+ }
+
+ // finished parsing the number.
+
+ if (nPosGSep >= 0 && nDigitCount != 3)
+ // must be exactly 3 digits since the last group separator.
+ return false;
+
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ sal_Int32 nParseEnd = 0;
+ rVal = ::rtl::math::stringToDouble( aBuf, dsep, gsep, &eStatus, &nParseEnd);
+ if (eStatus != rtl_math_ConversionStatus_Ok || nParseEnd < aBuf.getLength())
+ // Not a valid number or not entire string consumed.
+ return false;
+
+ return true;
+}
+
+OUString ScStringUtil::GetQuotedToken(const OUString &rIn, sal_Int32 nToken, const OUString& rQuotedPairs,
+ sal_Unicode cTok, sal_Int32& rIndex )
+{
+ assert( !(rQuotedPairs.getLength()%2) );
+ assert( rQuotedPairs.indexOf(cTok) == -1 );
+
+ const sal_Unicode* pStr = rIn.getStr();
+ const sal_Unicode* pQuotedStr = rQuotedPairs.getStr();
+ sal_Unicode cQuotedEndChar = 0;
+ sal_Int32 nQuotedLen = rQuotedPairs.getLength();
+ sal_Int32 nLen = rIn.getLength();
+ sal_Int32 nTok = 0;
+ sal_Int32 nFirstChar = rIndex;
+ sal_Int32 i = nFirstChar;
+
+ // detect token position and length
+ pStr += i;
+ while ( i < nLen )
+ {
+ sal_Unicode c = *pStr;
+ if ( cQuotedEndChar )
+ {
+ // end of the quote reached ?
+ if ( c == cQuotedEndChar )
+ cQuotedEndChar = 0;
+ }
+ else
+ {
+ // Is the char a quote-begin char ?
+ sal_Int32 nQuoteIndex = 0;
+ while ( nQuoteIndex < nQuotedLen )
+ {
+ if ( pQuotedStr[nQuoteIndex] == c )
+ {
+ cQuotedEndChar = pQuotedStr[nQuoteIndex+1];
+ break;
+ }
+ else
+ nQuoteIndex += 2;
+ }
+
+ // If the token-char matches then increase TokCount
+ if ( c == cTok )
+ {
+ ++nTok;
+
+ if ( nTok == nToken )
+ nFirstChar = i+1;
+ else
+ {
+ if ( nTok > nToken )
+ break;
+ }
+ }
+ }
+
+ ++pStr;
+ ++i;
+ }
+
+ if ( nTok >= nToken )
+ {
+ if ( i < nLen )
+ rIndex = i+1;
+ else
+ rIndex = -1;
+ return rIn.copy( nFirstChar, i-nFirstChar );
+ }
+ else
+ {
+ rIndex = -1;
+ return OUString();
+ }
+}
+
+bool ScStringUtil::isMultiline( std::u16string_view rStr )
+{
+ return rStr.find_first_of(u"\n\r") != std::u16string_view::npos;
+}
+
+ScInputStringType ScStringUtil::parseInputString(
+ SvNumberFormatter& rFormatter, const OUString& rStr, LanguageType eLang )
+{
+ ScInputStringType aRet;
+ aRet.mnFormatType = SvNumFormatType::ALL;
+ aRet.meType = ScInputStringType::Unknown;
+ aRet.maText = rStr;
+ aRet.mfValue = 0.0;
+
+ if (rStr.getLength() > 1 && rStr[0] == '=')
+ {
+ aRet.meType = ScInputStringType::Formula;
+ }
+ else if (rStr.getLength() > 1 && rStr[0] == '\'')
+ {
+ // for bEnglish, "'" at the beginning is always interpreted as text
+ // marker and stripped
+ aRet.maText = rStr.copy(1);
+ aRet.meType = ScInputStringType::Text;
+ }
+ else // test for English number format (only)
+ {
+ sal_uInt32 nNumFormat = rFormatter.GetStandardIndex(eLang);
+
+ if (rFormatter.IsNumberFormat(rStr, nNumFormat, aRet.mfValue))
+ {
+ aRet.meType = ScInputStringType::Number;
+ aRet.mnFormatType = rFormatter.GetType(nNumFormat);
+ }
+ else if (!rStr.isEmpty())
+ aRet.meType = ScInputStringType::Text;
+
+ // the (English) number format is not set
+ //TODO: find and replace with matching local format???
+ }
+
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/stylehelper.cxx b/sc/source/core/tool/stylehelper.cxx
new file mode 100644
index 0000000000..bdd580ee32
--- /dev/null
+++ b/sc/source/core/tool/stylehelper.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 <sal/config.h>
+
+#include <string_view>
+
+#include <svl/style.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include <stylehelper.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+namespace {
+
+struct ScDisplayNameMap
+{
+ OUString aDispName;
+ OUString aProgName;
+};
+
+}
+
+static const ScDisplayNameMap* lcl_GetStyleNameMap( SfxStyleFamily nType )
+{
+ if ( nType == SfxStyleFamily::Para )
+ {
+ static ScDisplayNameMap const aCellMap[]
+ {
+ // Standard builtin styles from configuration.
+ // Defined in sc/res/xml/styles.xml
+ // Installed to "$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/calc/styles.xml"
+ // e.g. /usr/lib64/libreoffice/share/calc/styles.xml
+ // or instdir/share/calc/styles.xml
+ { ScResId( STR_STYLENAME_HEADING ), "Heading" },
+ { ScResId( STR_STYLENAME_HEADING_1 ), "Heading 1" },
+ { ScResId( STR_STYLENAME_HEADING_2 ), "Heading 2" },
+ { ScResId( STR_STYLENAME_TEXT ), "Text" },
+ { ScResId( STR_STYLENAME_NOTE ), "Note" },
+ { ScResId( STR_STYLENAME_FOOTNOTE ), "Footnote" },
+ { ScResId( STR_STYLENAME_HYPERLINK ), "Hyperlink" },
+ { ScResId( STR_STYLENAME_STATUS ), "Status" },
+ { ScResId( STR_STYLENAME_GOOD ), "Good" },
+ { ScResId( STR_STYLENAME_NEUTRAL ), "Neutral" },
+ { ScResId( STR_STYLENAME_BAD ), "Bad" },
+ { ScResId( STR_STYLENAME_WARNING ), "Warning" },
+ { ScResId( STR_STYLENAME_ERROR ), "Error" },
+ { ScResId( STR_STYLENAME_ACCENT ), "Accent" },
+ { ScResId( STR_STYLENAME_ACCENT_1 ), "Accent 1" },
+ { ScResId( STR_STYLENAME_ACCENT_2 ), "Accent 2" },
+ { ScResId( STR_STYLENAME_ACCENT_3 ), "Accent 3" },
+ { ScResId( STR_STYLENAME_RESULT ), "Result" },
+ // API compatibility programmatic names after.
+ { ScResId( STR_STYLENAME_STANDARD ), SC_STYLE_PROG_STANDARD },
+ { ScResId( STR_STYLENAME_RESULT ), SC_STYLE_PROG_RESULT },
+ { ScResId( STR_STYLENAME_RESULT1 ), SC_STYLE_PROG_RESULT1 },
+ { ScResId( STR_STYLENAME_HEADING ), SC_STYLE_PROG_HEADING },
+ { ScResId( STR_STYLENAME_HEADING_1 ), SC_STYLE_PROG_HEADING1 },
+ // Pivot table styles.
+ { ScResId( STR_PIVOT_STYLENAME_INNER ), SC_PIVOT_STYLE_PROG_INNER },
+ { ScResId( STR_PIVOT_STYLENAME_RESULT ), SC_PIVOT_STYLE_PROG_RESULT },
+ { ScResId( STR_PIVOT_STYLENAME_CATEGORY ), SC_PIVOT_STYLE_PROG_CATEGORY },
+ { ScResId( STR_PIVOT_STYLENAME_TITLE ), SC_PIVOT_STYLE_PROG_TITLE },
+ { ScResId( STR_PIVOT_STYLENAME_FIELDNAME ), SC_PIVOT_STYLE_PROG_FIELDNAME },
+ { ScResId( STR_PIVOT_STYLENAME_TOP ), SC_PIVOT_STYLE_PROG_TOP },
+ // last entry remains empty
+ { OUString(), OUString() },
+ };
+ return aCellMap;
+ }
+ else if ( nType == SfxStyleFamily::Page )
+ {
+ static ScDisplayNameMap const aPageMap[]
+ {
+ { ScResId( STR_STYLENAME_STANDARD ), SC_STYLE_PROG_STANDARD },
+ { ScResId( STR_STYLENAME_REPORT ), SC_STYLE_PROG_REPORT },
+ // last entry remains empty
+ { OUString(), OUString() },
+ };
+ return aPageMap;
+ }
+ else if ( nType == SfxStyleFamily::Frame )
+ {
+ static ScDisplayNameMap const aGraphicMap[]
+ {
+ { ScResId( STR_STYLENAME_STANDARD ), SC_STYLE_PROG_STANDARD },
+ { ScResId( STR_STYLENAME_NOTE ), "Note" },
+ // last entry remains empty
+ { OUString(), OUString() },
+ };
+ return aGraphicMap;
+ }
+ OSL_FAIL("invalid family");
+ return nullptr;
+}
+
+// programmatic name suffix for display names that match other programmatic names
+// is " (user)" including a space
+
+constexpr OUString SC_SUFFIX_USER = u" (user)"_ustr;
+
+static bool lcl_EndsWithUser( std::u16string_view rString )
+{
+ return o3tl::ends_with(rString, SC_SUFFIX_USER);
+}
+
+OUString ScStyleNameConversion::DisplayToProgrammaticName( const OUString& rDispName, SfxStyleFamily nType )
+{
+ bool bDisplayIsProgrammatic = false;
+
+ const ScDisplayNameMap* pNames = lcl_GetStyleNameMap( nType );
+ if (pNames)
+ {
+ do
+ {
+ if (pNames->aDispName == rDispName)
+ return pNames->aProgName;
+ else if (pNames->aProgName == rDispName)
+ bDisplayIsProgrammatic = true; // display name matches any programmatic name
+ }
+ while( !(++pNames)->aDispName.isEmpty() );
+ }
+
+ if ( bDisplayIsProgrammatic || lcl_EndsWithUser( rDispName ) )
+ {
+ // add the (user) suffix if the display name matches any style's programmatic name
+ // or if it already contains the suffix
+ return rDispName + SC_SUFFIX_USER;
+ }
+
+ return rDispName;
+}
+
+OUString ScStyleNameConversion::ProgrammaticToDisplayName( const OUString& rProgName, SfxStyleFamily nType )
+{
+ if ( lcl_EndsWithUser( rProgName ) )
+ {
+ // remove the (user) suffix, don't compare to map entries
+ return rProgName.copy( 0, rProgName.getLength() - SC_SUFFIX_USER.getLength() );
+ }
+
+ const ScDisplayNameMap* pNames = lcl_GetStyleNameMap( nType );
+ if (pNames)
+ {
+ do
+ {
+ if (pNames->aProgName == rProgName)
+ return pNames->aDispName;
+ }
+ while( !(++pNames)->aDispName.isEmpty() );
+ }
+ return rProgName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/subtotal.cxx b/sc/source/core/tool/subtotal.cxx
new file mode 100644
index 0000000000..4c213893b1
--- /dev/null
+++ b/sc/source/core/tool/subtotal.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <subtotal.hxx>
+#include <sal/mathconf.h>
+#include <cfloat>
+
+bool SubTotal::SafePlus(double& fVal1, double fVal2)
+{
+ bool bOk = true;
+ SAL_MATH_FPEXCEPTIONS_OFF();
+ fVal1 += fVal2;
+ if (!std::isfinite(fVal1))
+ {
+ bOk = false;
+ if (fVal2 > 0.0)
+ fVal1 = DBL_MAX;
+ else
+ fVal1 = -DBL_MAX;
+ }
+ return bOk;
+}
+
+bool SubTotal::SafeMult(double& fVal1, double fVal2)
+{
+ bool bOk = true;
+ SAL_MATH_FPEXCEPTIONS_OFF();
+ fVal1 *= fVal2;
+ if (!std::isfinite(fVal1))
+ {
+ bOk = false;
+ fVal1 = DBL_MAX;
+ }
+ return bOk;
+}
+
+bool SubTotal::SafeDiv(double& fVal1, double fVal2)
+{
+ bool bOk = true;
+ SAL_MATH_FPEXCEPTIONS_OFF();
+ fVal1 /= fVal2;
+ if (!std::isfinite(fVal1))
+ {
+ bOk = false;
+ fVal1 = DBL_MAX;
+ }
+ return bOk;
+}
+
+void ScFunctionData::update(double fNewVal)
+{
+ if (mbError)
+ return;
+
+ switch (meFunc)
+ {
+ case SUBTOTAL_FUNC_SUM:
+ if (!SubTotal::SafePlus(getValueRef(), fNewVal))
+ mbError = true;
+ break;
+ case SUBTOTAL_FUNC_PROD:
+ if (getCountRef() == 0) // copy first value (nVal is initialized to 0)
+ {
+ getValueRef() = fNewVal;
+ getCountRef() = 1; // don't care about further count
+ }
+ else if (!SubTotal::SafeMult(getValueRef(), fNewVal))
+ mbError = true;
+ break;
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2:
+ ++getCountRef();
+ break;
+ case SUBTOTAL_FUNC_SELECTION_COUNT:
+ getCountRef() += fNewVal;
+ break;
+ case SUBTOTAL_FUNC_AVE:
+ if (!SubTotal::SafePlus(getValueRef(), fNewVal))
+ mbError = true;
+ else
+ ++getCountRef();
+ break;
+ case SUBTOTAL_FUNC_MAX:
+ if (getCountRef() == 0) // copy first value (nVal is initialized to 0)
+ {
+ getValueRef() = fNewVal;
+ getCountRef() = 1; // don't care about further count
+ }
+ else if (fNewVal > getValueRef())
+ getValueRef() = fNewVal;
+ break;
+ case SUBTOTAL_FUNC_MIN:
+ if (getCountRef() == 0) // copy first value (nVal is initialized to 0)
+ {
+ getValueRef() = fNewVal;
+ getCountRef() = 1; // don't care about further count
+ }
+ else if (fNewVal < getValueRef())
+ getValueRef() = fNewVal;
+ break;
+ case SUBTOTAL_FUNC_VAR:
+ case SUBTOTAL_FUNC_STD:
+ case SUBTOTAL_FUNC_VARP:
+ case SUBTOTAL_FUNC_STDP:
+ maWelford.update(fNewVal);
+ break;
+ default:
+ // unhandled unknown
+ mbError = true;
+ }
+}
+
+double ScFunctionData::getResult()
+{
+ if (mbError)
+ return 0.0;
+
+ double fRet = 0.0;
+ switch (meFunc)
+ {
+ case SUBTOTAL_FUNC_CNT:
+ case SUBTOTAL_FUNC_CNT2:
+ case SUBTOTAL_FUNC_SELECTION_COUNT:
+ fRet = getCountRef();
+ break;
+ case SUBTOTAL_FUNC_SUM:
+ case SUBTOTAL_FUNC_MAX:
+ case SUBTOTAL_FUNC_MIN:
+ // Note that nVal is 0.0 for MAX and MIN if nCount==0, that's also
+ // how it is defined in ODFF.
+ fRet = getValueRef();
+ break;
+ case SUBTOTAL_FUNC_PROD:
+ fRet = (getCountRef() > 0) ? getValueRef() : 0.0;
+ break;
+ case SUBTOTAL_FUNC_AVE:
+ if (getCountRef() == 0)
+ mbError = true;
+ else
+ fRet = getValueRef() / getCountRef();
+ break;
+ case SUBTOTAL_FUNC_VAR:
+ case SUBTOTAL_FUNC_STD:
+ if (maWelford.getCount() < 2)
+ mbError = true;
+ else
+ {
+ fRet = maWelford.getVarianceSample();
+ if (fRet < 0.0)
+ mbError = true;
+ else if (meFunc == SUBTOTAL_FUNC_STD)
+ fRet = sqrt(fRet);
+ }
+ break;
+ case SUBTOTAL_FUNC_VARP:
+ case SUBTOTAL_FUNC_STDP:
+ if (maWelford.getCount() < 1)
+ mbError = true;
+ else if (maWelford.getCount() == 1)
+ fRet = 0.0;
+ else
+ {
+ fRet = maWelford.getVariancePopulation();
+ if (fRet < 0.0)
+ mbError = true;
+ else if (meFunc == SUBTOTAL_FUNC_STDP)
+ fRet = sqrt(fRet);
+ }
+ break;
+ default:
+ assert(!"unhandled unknown");
+ mbError = true;
+ break;
+ }
+ if (mbError)
+ fRet = 0.0;
+ return fRet;
+}
+
+void WelfordRunner::update(double fVal)
+{
+ ++mnCount;
+ const double fDelta = fVal - mfMean;
+ mfMean += fDelta / mnCount;
+ mfM2 += fDelta * (fVal - mfMean);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
new file mode 100644
index 0000000000..25c5b6f05f
--- /dev/null
+++ b/sc/source/core/tool/token.cxx
@@ -0,0 +1,5413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <functional>
+
+#include <string.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <token.hxx>
+#include <tokenarray.hxx>
+#include <reftokenhelper.hxx>
+#include <clipparam.hxx>
+#include <compiler.hxx>
+#include <interpre.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <formula/compiler.hxx>
+#include <formula/opcode.hxx>
+#include <jumpmatrix.hxx>
+#include <rangeseq.hxx>
+#include <rangeutl.hxx>
+#include <externalrefmgr.hxx>
+#include <document.hxx>
+#include <refupdatecontext.hxx>
+#include <tokenstringcontext.hxx>
+#include <types.hxx>
+#include <addincol.hxx>
+#include <dbdata.hxx>
+#include <reordermap.hxx>
+#include <svl/sharedstring.hxx>
+#include <scmatrix.hxx>
+
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/ExternalReference.hpp>
+#include <com/sun/star/sheet/FormulaToken.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/NameToken.hpp>
+#include <utility>
+#include <o3tl/safeint.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+using ::std::vector;
+using namespace formula;
+using namespace com::sun::star;
+
+namespace
+{
+ void lcl_SingleRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
+ {
+ rRef.InitFlags();
+
+ rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
+ rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 );
+ rRef.SetTabRel( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_RELATIVE ) != 0 );
+ rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 );
+ rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 );
+ rRef.SetTabDeleted( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_DELETED ) != 0 );
+ rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 );
+ rRef.SetRelName( ( rAPI.Flags & sheet::ReferenceFlags::RELATIVE_NAME ) != 0 );
+
+ if (rRef.IsColRel())
+ rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
+ else
+ rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
+
+ if (rRef.IsRowRel())
+ rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
+ else
+ rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
+
+ if (rRef.IsTabRel())
+ rRef.SetRelTab(static_cast<SCTAB>(rAPI.RelativeSheet));
+ else
+ rRef.SetAbsTab(static_cast<SCTAB>(rAPI.Sheet));
+ }
+
+ void lcl_ExternalRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
+ {
+ rRef.InitFlags();
+
+ rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
+ rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 );
+ rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 );
+ rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 );
+ rRef.SetTabDeleted( false ); // sheet must not be deleted for external refs
+ rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 );
+ rRef.SetRelName( false );
+
+ if (rRef.IsColRel())
+ rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
+ else
+ rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
+
+ if (rRef.IsRowRel())
+ rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
+ else
+ rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
+
+ // sheet index must be absolute for external refs
+ rRef.SetAbsTab(0);
+ }
+
+ struct TokenPointerRange
+ {
+ FormulaToken** mpStart;
+ FormulaToken** mpStop;
+
+ TokenPointerRange() : mpStart(nullptr), mpStop(nullptr) {}
+ TokenPointerRange( FormulaToken** p, sal_uInt16 n ) :
+ mpStart(p), mpStop( p + static_cast<size_t>(n)) {}
+ };
+ struct TokenPointers
+ {
+ TokenPointerRange maPointerRange[2];
+ bool mbSkipRelName;
+
+ TokenPointers( FormulaToken** pCode, sal_uInt16 nLen, FormulaToken** pRPN, sal_uInt16 nRPN,
+ bool bSkipRelName = true ) :
+ mbSkipRelName(bSkipRelName)
+ {
+ maPointerRange[0] = TokenPointerRange( pCode, nLen);
+ maPointerRange[1] = TokenPointerRange( pRPN, nRPN);
+ }
+
+ bool skipToken( size_t i, const FormulaToken* const * pp )
+ {
+ // Handle all code tokens, and tokens in RPN only if they have a
+ // reference count of 1, which means they are not referenced in the
+ // code array. Doing it the other way would skip code tokens that
+ // are held by flat copied token arrays and thus are shared. For
+ // flat copy arrays the caller has to know what it does and should
+ // discard all RPN, update only one array and regenerate all RPN.
+ if (i == 1)
+ {
+ if ((*pp)->GetRef() > 1)
+ return true;
+
+ if (mbSkipRelName)
+ {
+ // Skip (do not adjust) relative references resulting from
+ // named expressions. Resolved expressions are only in RPN.
+ switch ((*pp)->GetType())
+ {
+ case svSingleRef:
+ return (*pp)->GetSingleRef()->IsRelName();
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *(*pp)->GetDoubleRef();
+ return rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName();
+ }
+ default:
+ ; // nothing
+ }
+ }
+ }
+
+ return false;
+ }
+
+ FormulaToken* getHandledToken( size_t i, FormulaToken* const * pp )
+ {
+ if (skipToken( i, pp))
+ return nullptr;
+
+ FormulaToken* p = *pp;
+ if (p->GetOpCode() == ocTableRef)
+ {
+ // Return the inner reference token if it is not in RPN.
+ ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(p);
+ if (!pTR)
+ return p;
+ p = pTR->GetAreaRefRPN();
+ if (!p)
+ return pTR;
+ if (p->GetRef() > 1)
+ // Reference handled in RPN, but do not return nullptr so
+ // loops will process ocTableRef via pp instead of issuing
+ // a continue.
+ return pTR;
+ }
+ return p;
+ }
+ };
+
+} // namespace
+
+
+// --- class ScRawToken -----------------------------------------------------
+
+void ScRawToken::SetOpCode( OpCode e )
+{
+ eOp = e;
+ switch (eOp)
+ {
+ case ocIf:
+ eType = svJump;
+ nJump[ 0 ] = 3; // If, Else, Behind
+ break;
+ case ocIfError:
+ case ocIfNA:
+ eType = svJump;
+ nJump[ 0 ] = 2; // If, Behind
+ break;
+ case ocChoose:
+ eType = svJump;
+ nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
+ break;
+ case ocMissing:
+ eType = svMissing;
+ break;
+ case ocSep:
+ case ocOpen:
+ case ocClose:
+ case ocArrayRowSep:
+ case ocArrayColSep:
+ case ocArrayOpen:
+ case ocArrayClose:
+ case ocTableRefOpen:
+ case ocTableRefClose:
+ eType = svSep;
+ break;
+ case ocWhitespace:
+ eType = svByte;
+ whitespace.nCount = 1;
+ whitespace.cChar = 0x20;
+ break;
+ default:
+ eType = svByte;
+ sbyte.cByte = 0;
+ sbyte.eInForceArray = ParamClass::Unknown;
+ }
+}
+
+void ScRawToken::SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
+{
+ eOp = ocPush;
+ eType = svString;
+
+ sharedstring.mpData = pData;
+ sharedstring.mpDataIgnoreCase = pDataIgnoreCase;
+}
+
+void ScRawToken::SetSingleReference( const ScSingleRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svSingleRef;
+ aRef.Ref1 =
+ aRef.Ref2 = rRef;
+}
+
+void ScRawToken::SetDoubleReference( const ScComplexRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svDoubleRef;
+ aRef = rRef;
+}
+
+void ScRawToken::SetDouble(double rVal)
+{
+ eOp = ocPush;
+ eType = svDouble;
+ nValue = rVal;
+}
+
+void ScRawToken::SetErrorConstant( FormulaError nErr )
+{
+ eOp = ocPush;
+ eType = svError;
+ nError = nErr;
+}
+
+void ScRawToken::SetName(sal_Int16 nSheet, sal_uInt16 nIndex)
+{
+ eOp = ocName;
+ eType = svIndex;
+
+ name.nSheet = nSheet;
+ name.nIndex = nIndex;
+}
+
+void ScRawToken::SetExternalSingleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svExternalSingleRef;
+
+ extref.nFileId = nFileId;
+ extref.aRef.Ref1 =
+ extref.aRef.Ref2 = rRef;
+ maExternalName = rTabName;
+}
+
+void ScRawToken::SetExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
+{
+ eOp = ocPush;
+ eType = svExternalDoubleRef;
+
+ extref.nFileId = nFileId;
+ extref.aRef = rRef;
+ maExternalName = rTabName;
+}
+
+void ScRawToken::SetExternalName( sal_uInt16 nFileId, const OUString& rName )
+{
+ eOp = ocPush;
+ eType = svExternalName;
+
+ extname.nFileId = nFileId;
+ maExternalName = rName;
+}
+
+void ScRawToken::SetExternal( const OUString& rStr )
+{
+ eOp = ocExternal;
+ eType = svExternal;
+ maExternalName = rStr;
+}
+
+bool ScRawToken::IsValidReference(const ScDocument& rDoc) const
+{
+ switch (eType)
+ {
+ case svSingleRef:
+ return aRef.Ref1.Valid(rDoc);
+ case svDoubleRef:
+ return aRef.Valid(rDoc);
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ return true;
+ default:
+ ; // nothing
+ }
+ return false;
+}
+
+FormulaToken* ScRawToken::CreateToken(ScSheetLimits& rLimits) const
+{
+#define IF_NOT_OPCODE_ERROR(o,c) SAL_WARN_IF((eOp!=o), "sc.core", #c "::ctor: OpCode " << static_cast<int>(eOp) << " lost, converted to " #o "; maybe inherit from FormulaToken instead!")
+ switch ( GetType() )
+ {
+ case svByte :
+ if (eOp == ocWhitespace)
+ return new FormulaSpaceToken( whitespace.nCount, whitespace.cChar );
+ else
+ return new FormulaByteToken( eOp, sbyte.cByte, sbyte.eInForceArray );
+ case svDouble :
+ IF_NOT_OPCODE_ERROR( ocPush, FormulaDoubleToken);
+ return new FormulaDoubleToken( nValue );
+ case svString :
+ {
+ svl::SharedString aSS(sharedstring.mpData, sharedstring.mpDataIgnoreCase);
+ if (eOp == ocPush)
+ return new FormulaStringToken(std::move(aSS));
+ else
+ return new FormulaStringOpToken(eOp, std::move(aSS));
+ }
+ case svSingleRef :
+ if (eOp == ocPush)
+ return new ScSingleRefToken(rLimits, aRef.Ref1 );
+ else
+ return new ScSingleRefToken(rLimits, aRef.Ref1, eOp );
+ case svDoubleRef :
+ if (eOp == ocPush)
+ return new ScDoubleRefToken(rLimits, aRef );
+ else
+ return new ScDoubleRefToken(rLimits, aRef, eOp );
+ case svMatrix :
+ IF_NOT_OPCODE_ERROR( ocPush, ScMatrixToken);
+ return new ScMatrixToken( pMat );
+ case svIndex :
+ if (eOp == ocTableRef)
+ return new ScTableRefToken( table.nIndex, table.eItem);
+ else
+ return new FormulaIndexToken( eOp, name.nIndex, name.nSheet);
+ case svExternalSingleRef:
+ {
+ svl::SharedString aTabName(maExternalName); // string not interned
+ return new ScExternalSingleRefToken(extref.nFileId, std::move(aTabName), extref.aRef.Ref1);
+ }
+ case svExternalDoubleRef:
+ {
+ svl::SharedString aTabName(maExternalName); // string not interned
+ return new ScExternalDoubleRefToken(extref.nFileId, std::move(aTabName), extref.aRef);
+ }
+ case svExternalName:
+ {
+ svl::SharedString aName(maExternalName); // string not interned
+ return new ScExternalNameToken( extname.nFileId, std::move(aName) );
+ }
+ case svJump :
+ return new FormulaJumpToken( eOp, nJump );
+ case svExternal :
+ return new FormulaExternalToken( eOp, sbyte.cByte, maExternalName );
+ case svFAP :
+ return new FormulaFAPToken( eOp, sbyte.cByte, nullptr );
+ case svMissing :
+ IF_NOT_OPCODE_ERROR( ocMissing, FormulaMissingToken);
+ return new FormulaMissingToken;
+ case svSep :
+ return new FormulaToken( svSep,eOp );
+ case svError :
+ return new FormulaErrorToken( nError );
+ case svUnknown :
+ return new FormulaUnknownToken( eOp );
+ default:
+ {
+ SAL_WARN("sc.core", "unknown ScRawToken::CreateToken() type " << int(GetType()));
+ return new FormulaUnknownToken( ocBad );
+ }
+ }
+#undef IF_NOT_OPCODE_ERROR
+}
+
+namespace {
+
+// TextEqual: if same formula entered (for optimization in sort)
+bool checkTextEqual( const ScSheetLimits& rLimits, const FormulaToken& _rToken1, const FormulaToken& _rToken2 )
+{
+ assert(
+ (_rToken1.GetType() == svSingleRef || _rToken1.GetType() == svDoubleRef)
+ && _rToken1.FormulaToken::operator ==(_rToken2));
+
+ // in relative Refs only compare relative parts
+
+ ScComplexRefData aTemp1;
+ if ( _rToken1.GetType() == svSingleRef )
+ {
+ aTemp1.Ref1 = *_rToken1.GetSingleRef();
+ aTemp1.Ref2 = aTemp1.Ref1;
+ }
+ else
+ aTemp1 = *_rToken1.GetDoubleRef();
+
+ ScComplexRefData aTemp2;
+ if ( _rToken2.GetType() == svSingleRef )
+ {
+ aTemp2.Ref1 = *_rToken2.GetSingleRef();
+ aTemp2.Ref2 = aTemp2.Ref1;
+ }
+ else
+ aTemp2 = *_rToken2.GetDoubleRef();
+
+ ScAddress aPos;
+ ScRange aRange1 = aTemp1.toAbs(rLimits, aPos), aRange2 = aTemp2.toAbs(rLimits, aPos);
+
+ // memcmp doesn't work because of the alignment byte after bFlags.
+ // After SmartRelAbs only absolute parts have to be compared.
+ return aRange1 == aRange2 && aTemp1.Ref1.FlagValue() == aTemp2.Ref1.FlagValue() && aTemp1.Ref2.FlagValue() == aTemp2.Ref2.FlagValue();
+}
+
+}
+
+#if DEBUG_FORMULA_COMPILER
+void DumpToken(formula::FormulaToken const & rToken)
+{
+ switch (rToken.GetType()) {
+ case svSingleRef:
+ cout << "-- ScSingleRefToken" << endl;
+ rToken.GetSingleRef()->Dump(1);
+ break;
+ case svDoubleRef:
+ cout << "-- ScDoubleRefToken" << endl;
+ rToken.GetDoubleRef()->Dump(1);
+ break;
+ default:
+ cout << "-- FormulaToken" << endl;
+ cout << " opcode: " << int(rToken.GetOpCode()) << " " <<
+ formula::FormulaCompiler::GetNativeSymbol( rToken.GetOpCode()).toUtf8().getStr() << endl;
+ cout << " type: " << static_cast<int>(rToken.GetType()) << endl;
+ switch (rToken.GetType())
+ {
+ case svDouble:
+ cout << " value: " << rToken.GetDouble() << endl;
+ break;
+ case svString:
+ cout << " string: "
+ << OUStringToOString(rToken.GetString().getString(), RTL_TEXTENCODING_UTF8).getStr()
+ << endl;
+ break;
+ default:
+ ;
+ }
+ break;
+ }
+}
+#endif
+
+FormulaTokenRef extendRangeReference( ScSheetLimits& rLimits, FormulaToken & rTok1, FormulaToken & rTok2,
+ const ScAddress & rPos, bool bReuseDoubleRef )
+{
+
+ StackVar sv1 = rTok1.GetType();
+ // Doing a RangeOp with RefList is probably utter nonsense, but Xcl
+ // supports it, so do we.
+ if (sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList
+ && sv1 != svExternalSingleRef && sv1 != svExternalDoubleRef)
+ return nullptr;
+ StackVar sv2 = rTok2.GetType();
+ if (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)
+ return nullptr;
+
+ ScTokenRef xRes;
+ bool bExternal = (sv1 == svExternalSingleRef);
+ if ((sv1 == svSingleRef || bExternal) && sv2 == svSingleRef)
+ {
+ // Range references like Sheet1.A1:A2 are generalized and built by
+ // first creating a DoubleRef from the first SingleRef, effectively
+ // generating Sheet1.A1:A1, and then extending that with A2 as if
+ // Sheet1.A1:A1:A2 was encountered, so the mechanisms to adjust the
+ // references apply as well.
+
+ /* Given the current structure of external references an external
+ * reference can only be extended if the second reference does not
+ * point to a different sheet. 'file'#Sheet1.A1:A2 is ok,
+ * 'file'#Sheet1.A1:Sheet2.A2 is not. Since we can't determine from a
+ * svSingleRef whether the sheet would be different from the one given
+ * in the external reference, we have to bail out if there is any sheet
+ * specified. NOTE: Xcl does handle external 3D references as in
+ * '[file]Sheet1:Sheet2'!A1:A2
+ *
+ * FIXME: For OOo syntax be smart and remember an external singleref
+ * encountered and if followed by ocRange and singleref, create an
+ * external singleref for the second singleref. Both could then be
+ * merged here. For Xcl syntax already parse an external range
+ * reference entirely, cumbersome. */
+
+ const ScSingleRefData& rRef2 = *rTok2.GetSingleRef();
+ if (bExternal && rRef2.IsFlag3D())
+ return nullptr;
+
+ ScComplexRefData aRef;
+ aRef.Ref1 = aRef.Ref2 = *rTok1.GetSingleRef();
+ aRef.Ref2.SetFlag3D( false);
+ aRef.Extend(rLimits, rRef2, rPos);
+ if (bExternal)
+ xRes = new ScExternalDoubleRefToken( rTok1.GetIndex(), rTok1.GetString(), aRef);
+ else
+ xRes = new ScDoubleRefToken(rLimits, aRef);
+ }
+ else
+ {
+ bExternal |= (sv1 == svExternalDoubleRef);
+ const ScRefList* pRefList = nullptr;
+ if (sv1 == svDoubleRef)
+ {
+ xRes = (bReuseDoubleRef && rTok1.GetRef() == 1 ? &rTok1 : rTok1.Clone());
+ sv1 = svUnknown; // mark as handled
+ }
+ else if (sv2 == svDoubleRef)
+ {
+ xRes = (bReuseDoubleRef && rTok2.GetRef() == 1 ? &rTok2 : rTok2.Clone());
+ sv2 = svUnknown; // mark as handled
+ }
+ else if (sv1 == svRefList)
+ pRefList = rTok1.GetRefList();
+ else if (sv2 == svRefList)
+ pRefList = rTok2.GetRefList();
+ if (pRefList)
+ {
+ if (pRefList->empty())
+ return nullptr;
+ if (bExternal)
+ return nullptr; // external reference list not possible
+ xRes = new ScDoubleRefToken(rLimits, (*pRefList)[0] );
+ }
+ if (!xRes)
+ return nullptr; // shouldn't happen...
+ StackVar sv[2] = { sv1, sv2 };
+ formula::FormulaToken* pt[2] = { &rTok1, &rTok2 };
+ ScComplexRefData& rRef = *xRes->GetDoubleRef();
+ for (size_t i=0; i<2; ++i)
+ {
+ switch (sv[i])
+ {
+ case svSingleRef:
+ rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
+ break;
+ case svDoubleRef:
+ rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
+ break;
+ case svRefList:
+ {
+ const ScRefList* p = pt[i]->GetRefList();
+ if (p->empty())
+ return nullptr;
+ for (const auto& rRefData : *p)
+ {
+ rRef.Extend(rLimits, rRefData, rPos);
+ }
+ }
+ break;
+ case svExternalSingleRef:
+ if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
+ return nullptr; // no other sheets with external refs
+ else
+ rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
+ break;
+ case svExternalDoubleRef:
+ if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
+ return nullptr; // no other sheets with external refs
+ else
+ rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
+ break;
+ default:
+ ; // nothing, prevent compiler warning
+ }
+ }
+ }
+ return FormulaTokenRef(xRes.get());
+}
+
+// real implementations of virtual functions
+
+const ScSingleRefData* ScSingleRefToken::GetSingleRef() const { return &aSingleRef; }
+ScSingleRefData* ScSingleRefToken::GetSingleRef() { return &aSingleRef; }
+bool ScSingleRefToken::TextEqual( const FormulaToken& _rToken ) const
+{
+ return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
+}
+bool ScSingleRefToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && aSingleRef == *r.GetSingleRef();
+}
+
+const ScSingleRefData* ScDoubleRefToken::GetSingleRef() const { return &aDoubleRef.Ref1; }
+ScSingleRefData* ScDoubleRefToken::GetSingleRef() { return &aDoubleRef.Ref1; }
+const ScComplexRefData* ScDoubleRefToken::GetDoubleRef() const { return &aDoubleRef; }
+ScComplexRefData* ScDoubleRefToken::GetDoubleRef() { return &aDoubleRef; }
+const ScSingleRefData* ScDoubleRefToken::GetSingleRef2() const { return &aDoubleRef.Ref2; }
+ScSingleRefData* ScDoubleRefToken::GetSingleRef2() { return &aDoubleRef.Ref2; }
+bool ScDoubleRefToken::TextEqual( const FormulaToken& _rToken ) const
+{
+ return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
+}
+bool ScDoubleRefToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && aDoubleRef == *r.GetDoubleRef();
+}
+
+const ScRefList* ScRefListToken::GetRefList() const { return &aRefList; }
+ ScRefList* ScRefListToken::GetRefList() { return &aRefList; }
+ bool ScRefListToken::IsArrayResult() const { return mbArrayResult; }
+bool ScRefListToken::operator==( const FormulaToken& r ) const
+{
+ if (!FormulaToken::operator==( r ) || &aRefList != r.GetRefList())
+ return false;
+ const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(&r);
+ return p && mbArrayResult == p->IsArrayResult();
+}
+
+ScMatrixToken::ScMatrixToken( ScMatrixRef p ) :
+ FormulaToken(formula::svMatrix), pMatrix(std::move(p)) {}
+
+ScMatrixToken::ScMatrixToken( const ScMatrixToken& ) = default;
+
+const ScMatrix* ScMatrixToken::GetMatrix() const { return pMatrix.get(); }
+ScMatrix* ScMatrixToken::GetMatrix() { return pMatrix.get(); }
+bool ScMatrixToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && pMatrix == r.GetMatrix();
+}
+
+ScMatrixRangeToken::ScMatrixRangeToken( const sc::RangeMatrix& rMat ) :
+ FormulaToken(formula::svMatrix), mpMatrix(rMat.mpMat)
+{
+ maRef.InitRange(rMat.mnCol1, rMat.mnRow1, rMat.mnTab1, rMat.mnCol2, rMat.mnRow2, rMat.mnTab2);
+}
+
+ScMatrixRangeToken::ScMatrixRangeToken( const ScMatrixRangeToken& ) = default;
+
+sal_uInt8 ScMatrixRangeToken::GetByte() const
+{
+ return MATRIX_TOKEN_HAS_RANGE;
+}
+
+const ScMatrix* ScMatrixRangeToken::GetMatrix() const
+{
+ return mpMatrix.get();
+}
+
+ScMatrix* ScMatrixRangeToken::GetMatrix()
+{
+ return mpMatrix.get();
+}
+
+const ScComplexRefData* ScMatrixRangeToken::GetDoubleRef() const
+{
+ return &maRef;
+}
+
+ScComplexRefData* ScMatrixRangeToken::GetDoubleRef()
+{
+ return &maRef;
+}
+
+bool ScMatrixRangeToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==(r) && mpMatrix == r.GetMatrix();
+}
+
+FormulaToken* ScMatrixRangeToken::Clone() const
+{
+ return new ScMatrixRangeToken(*this);
+}
+
+ScExternalSingleRefToken::ScExternalSingleRefToken( sal_uInt16 nFileId, svl::SharedString aTabName, const ScSingleRefData& r ) :
+ FormulaToken( svExternalSingleRef, ocPush),
+ mnFileId(nFileId),
+ maTabName(std::move(aTabName)),
+ maSingleRef(r)
+{
+}
+
+ScExternalSingleRefToken::~ScExternalSingleRefToken()
+{
+}
+
+sal_uInt16 ScExternalSingleRefToken::GetIndex() const
+{
+ return mnFileId;
+}
+
+const svl::SharedString & ScExternalSingleRefToken::GetString() const
+{
+ return maTabName;
+}
+
+const ScSingleRefData* ScExternalSingleRefToken::GetSingleRef() const
+{
+ return &maSingleRef;
+}
+
+ScSingleRefData* ScExternalSingleRefToken::GetSingleRef()
+{
+ return &maSingleRef;
+}
+
+bool ScExternalSingleRefToken::operator ==( const FormulaToken& r ) const
+{
+ if (!FormulaToken::operator==(r))
+ return false;
+
+ if (mnFileId != r.GetIndex())
+ return false;
+
+ if (maTabName != r.GetString())
+ return false;
+
+ return maSingleRef == *r.GetSingleRef();
+}
+
+ScExternalDoubleRefToken::ScExternalDoubleRefToken( sal_uInt16 nFileId, svl::SharedString aTabName, const ScComplexRefData& r ) :
+ FormulaToken( svExternalDoubleRef, ocPush),
+ mnFileId(nFileId),
+ maTabName(std::move(aTabName)),
+ maDoubleRef(r)
+{
+}
+
+ScExternalDoubleRefToken::~ScExternalDoubleRefToken()
+{
+}
+
+sal_uInt16 ScExternalDoubleRefToken::GetIndex() const
+{
+ return mnFileId;
+}
+
+const svl::SharedString & ScExternalDoubleRefToken::GetString() const
+{
+ return maTabName;
+}
+
+const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef() const
+{
+ return &maDoubleRef.Ref1;
+}
+
+ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef()
+{
+ return &maDoubleRef.Ref1;
+}
+
+const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2() const
+{
+ return &maDoubleRef.Ref2;
+}
+
+ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2()
+{
+ return &maDoubleRef.Ref2;
+}
+
+const ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef() const
+{
+ return &maDoubleRef;
+}
+
+ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef()
+{
+ return &maDoubleRef;
+}
+
+bool ScExternalDoubleRefToken::operator ==( const FormulaToken& r ) const
+{
+ if (!FormulaToken::operator==(r))
+ return false;
+
+ if (mnFileId != r.GetIndex())
+ return false;
+
+ if (maTabName != r.GetString())
+ return false;
+
+ return maDoubleRef == *r.GetDoubleRef();
+}
+
+ScExternalNameToken::ScExternalNameToken( sal_uInt16 nFileId, svl::SharedString aName ) :
+ FormulaToken( svExternalName, ocPush),
+ mnFileId(nFileId),
+ maName(std::move(aName))
+{
+}
+
+ScExternalNameToken::~ScExternalNameToken() {}
+
+sal_uInt16 ScExternalNameToken::GetIndex() const
+{
+ return mnFileId;
+}
+
+const svl::SharedString & ScExternalNameToken::GetString() const
+{
+ return maName;
+}
+
+bool ScExternalNameToken::operator==( const FormulaToken& r ) const
+{
+ if ( !FormulaToken::operator==(r) )
+ return false;
+
+ if (mnFileId != r.GetIndex())
+ return false;
+
+ return maName.getData() == r.GetString().getData();
+}
+
+ScTableRefToken::ScTableRefToken( sal_uInt16 nIndex, ScTableRefToken::Item eItem ) :
+ FormulaToken( svIndex, ocTableRef),
+ mnIndex(nIndex),
+ meItem(eItem)
+{
+}
+
+ScTableRefToken::ScTableRefToken( const ScTableRefToken& r ) :
+ FormulaToken(r),
+ mxAreaRefRPN( r.mxAreaRefRPN ? r.mxAreaRefRPN->Clone() : nullptr),
+ mnIndex(r.mnIndex),
+ meItem(r.meItem)
+{
+}
+
+ScTableRefToken::~ScTableRefToken() {}
+
+sal_uInt16 ScTableRefToken::GetIndex() const
+{
+ return mnIndex;
+}
+
+void ScTableRefToken::SetIndex( sal_uInt16 n )
+{
+ mnIndex = n;
+}
+
+sal_Int16 ScTableRefToken::GetSheet() const
+{
+ // Code asking for this may have to be adapted as it might assume an
+ // svIndex token would always be ocName or ocDBArea.
+ SAL_WARN("sc.core","ScTableRefToken::GetSheet - maybe adapt caller to know about TableRef?");
+ // Database range is always global.
+ return -1;
+}
+
+ScTableRefToken::Item ScTableRefToken::GetItem() const
+{
+ return meItem;
+}
+
+void ScTableRefToken::AddItem( ScTableRefToken::Item eItem )
+{
+ meItem = static_cast<ScTableRefToken::Item>(meItem | eItem);
+}
+
+void ScTableRefToken::SetAreaRefRPN( formula::FormulaToken* pToken )
+{
+ mxAreaRefRPN = pToken;
+}
+
+formula::FormulaToken* ScTableRefToken::GetAreaRefRPN() const
+{
+ return mxAreaRefRPN.get();
+}
+
+bool ScTableRefToken::operator==( const FormulaToken& r ) const
+{
+ if ( !FormulaToken::operator==(r) )
+ return false;
+
+ if (mnIndex != r.GetIndex())
+ return false;
+
+ const ScTableRefToken* p = dynamic_cast<const ScTableRefToken*>(&r);
+ if (!p)
+ return false;
+
+ if (meItem != p->GetItem())
+ return false;
+
+ if (!mxAreaRefRPN && !p->mxAreaRefRPN)
+ ; // nothing
+ else if (!mxAreaRefRPN || !p->mxAreaRefRPN)
+ return false;
+ else if (!(*mxAreaRefRPN == *(p->mxAreaRefRPN)))
+ return false;
+
+ return true;
+}
+
+ScJumpMatrixToken::ScJumpMatrixToken(std::shared_ptr<ScJumpMatrix> p)
+ : FormulaToken(formula::svJumpMatrix)
+ , mpJumpMatrix(std::move(p))
+{}
+
+ScJumpMatrixToken::ScJumpMatrixToken( const ScJumpMatrixToken & ) = default;
+
+ScJumpMatrix* ScJumpMatrixToken::GetJumpMatrix() const
+{
+ return mpJumpMatrix.get();
+}
+
+bool ScJumpMatrixToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) && mpJumpMatrix.get() == r.GetJumpMatrix();
+}
+
+ScJumpMatrixToken::~ScJumpMatrixToken()
+{
+}
+
+double ScEmptyCellToken::GetDouble() const { return 0.0; }
+
+const svl::SharedString & ScEmptyCellToken::GetString() const
+{
+ return svl::SharedString::getEmptyString();
+}
+
+bool ScEmptyCellToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ bInherited == static_cast< const ScEmptyCellToken & >(r).IsInherited() &&
+ bDisplayedAsString == static_cast< const ScEmptyCellToken & >(r).IsDisplayedAsString();
+}
+
+ScMatrixCellResultToken::ScMatrixCellResultToken( ScConstMatrixRef pMat, const formula::FormulaToken* pUL ) :
+ FormulaToken(formula::svMatrixCell), xMatrix(std::move(pMat)), xUpperLeft(pUL) {}
+
+ScMatrixCellResultToken::ScMatrixCellResultToken( const ScMatrixCellResultToken& ) = default;
+
+double ScMatrixCellResultToken::GetDouble() const { return xUpperLeft->GetDouble(); }
+
+ScMatrixCellResultToken::~ScMatrixCellResultToken() {}
+
+const svl::SharedString & ScMatrixCellResultToken::GetString() const
+{
+ return xUpperLeft->GetString();
+}
+
+const ScMatrix* ScMatrixCellResultToken::GetMatrix() const { return xMatrix.get(); }
+// Non-const GetMatrix() is private and unused but must be implemented to
+// satisfy vtable linkage.
+ScMatrix* ScMatrixCellResultToken::GetMatrix()
+{
+ return const_cast<ScMatrix*>(xMatrix.get());
+}
+
+bool ScMatrixCellResultToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ xUpperLeft == static_cast<const ScMatrixCellResultToken &>(r).xUpperLeft &&
+ xMatrix == static_cast<const ScMatrixCellResultToken &>(r).xMatrix;
+}
+
+FormulaToken* ScMatrixCellResultToken::Clone() const
+{
+ return new ScMatrixCellResultToken(*this);
+}
+
+void ScMatrixCellResultToken::Assign( const ScMatrixCellResultToken & r )
+{
+ xMatrix = r.xMatrix;
+ xUpperLeft = r.xUpperLeft;
+}
+
+ScMatrixFormulaCellToken::ScMatrixFormulaCellToken(
+ SCCOL nC, SCROW nR, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) :
+ ScMatrixCellResultToken(pMat, pUL), nRows(nR), nCols(nC)
+{
+ CloneUpperLeftIfNecessary();
+}
+
+ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( SCCOL nC, SCROW nR ) :
+ ScMatrixCellResultToken(nullptr, nullptr), nRows(nR), nCols(nC) {}
+
+ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( const ScMatrixFormulaCellToken& r ) :
+ ScMatrixCellResultToken(r), nRows(r.nRows), nCols(r.nCols)
+{
+ CloneUpperLeftIfNecessary();
+}
+
+ScMatrixFormulaCellToken::~ScMatrixFormulaCellToken() {}
+
+bool ScMatrixFormulaCellToken::operator==( const FormulaToken& r ) const
+{
+ const ScMatrixFormulaCellToken* p = dynamic_cast<const ScMatrixFormulaCellToken*>(&r);
+ return p && ScMatrixCellResultToken::operator==( r ) &&
+ nCols == p->nCols && nRows == p->nRows;
+}
+
+void ScMatrixFormulaCellToken::CloneUpperLeftIfNecessary()
+{
+ if (xUpperLeft && xUpperLeft->GetType() == svDouble)
+ xUpperLeft = xUpperLeft->Clone();
+}
+
+void ScMatrixFormulaCellToken::Assign( const ScMatrixCellResultToken & r )
+{
+ ScMatrixCellResultToken::Assign( r);
+
+ CloneUpperLeftIfNecessary();
+}
+
+void ScMatrixFormulaCellToken::Assign( const formula::FormulaToken& r )
+{
+ if (this == &r)
+ return;
+ const ScMatrixCellResultToken* p = dynamic_cast<const ScMatrixCellResultToken*>(&r);
+ if (p)
+ ScMatrixCellResultToken::Assign( *p);
+ else
+ {
+ OSL_ENSURE( r.GetType() != svMatrix, "ScMatrixFormulaCellToken::operator=: assigning ScMatrixToken to ScMatrixFormulaCellToken is not proper, use ScMatrixCellResultToken instead");
+ if (r.GetType() == svMatrix)
+ {
+ xUpperLeft = nullptr;
+ xMatrix = r.GetMatrix();
+ }
+ else
+ {
+ xUpperLeft = &r;
+ xMatrix = nullptr;
+ CloneUpperLeftIfNecessary();
+ }
+ }
+}
+
+void ScMatrixFormulaCellToken::SetUpperLeftDouble( double f )
+{
+ switch (GetUpperLeftType())
+ {
+ case svDouble:
+ const_cast<FormulaToken*>(xUpperLeft.get())->GetDoubleAsReference() = f;
+ break;
+ case svString:
+ xUpperLeft = new FormulaDoubleToken( f);
+ break;
+ case svUnknown:
+ if (!xUpperLeft)
+ {
+ xUpperLeft = new FormulaDoubleToken( f);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ {
+ OSL_FAIL("ScMatrixFormulaCellToken::SetUpperLeftDouble: not modifying unhandled token type");
+ }
+ }
+}
+
+void ScMatrixFormulaCellToken::ResetResult()
+{
+ xMatrix = nullptr;
+ xUpperLeft = nullptr;
+}
+
+ScHybridCellToken::ScHybridCellToken(
+ double f, const svl::SharedString & rStr, OUString aFormula, bool bEmptyDisplayedAsString ) :
+ FormulaToken( formula::svHybridCell ),
+ mfDouble( f ), maString( rStr ),
+ maFormula(std::move( aFormula )),
+ mbEmptyDisplayedAsString( bEmptyDisplayedAsString)
+{
+ // caller, make up your mind...
+ assert( !bEmptyDisplayedAsString || (f == 0.0 && rStr.getString().isEmpty()));
+}
+
+double ScHybridCellToken::GetDouble() const { return mfDouble; }
+
+const svl::SharedString & ScHybridCellToken::GetString() const
+{
+ return maString;
+}
+
+bool ScHybridCellToken::operator==( const FormulaToken& r ) const
+{
+ return FormulaToken::operator==( r ) &&
+ mfDouble == r.GetDouble() && maString == r.GetString() &&
+ maFormula == static_cast<const ScHybridCellToken &>(r).GetFormula();
+}
+
+bool ScTokenArray::AddFormulaToken(
+ const css::sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, formula::ExternalReferenceHelper* pExtRef)
+{
+ bool bError = FormulaTokenArray::AddFormulaToken(rToken, rSPool, pExtRef);
+ if ( bError )
+ {
+ bError = false;
+ const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode); // assuming equal values for the moment
+
+ const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
+ switch ( eClass )
+ {
+ case uno::TypeClass_STRUCT:
+ {
+ uno::Type aType = rToken.Data.getValueType();
+ if ( aType.equals( cppu::UnoType<sheet::SingleReference>::get() ) )
+ {
+ ScSingleRefData aSingleRef;
+ sheet::SingleReference aApiRef;
+ rToken.Data >>= aApiRef;
+ lcl_SingleRefToCalc( aSingleRef, aApiRef );
+ if ( eOpCode == ocPush )
+ AddSingleReference( aSingleRef );
+ else if ( eOpCode == ocColRowName )
+ AddColRowName( aSingleRef );
+ else
+ bError = true;
+ }
+ else if ( aType.equals( cppu::UnoType<sheet::ComplexReference>::get() ) )
+ {
+ ScComplexRefData aComplRef;
+ sheet::ComplexReference aApiRef;
+ rToken.Data >>= aApiRef;
+ lcl_SingleRefToCalc( aComplRef.Ref1, aApiRef.Reference1 );
+ lcl_SingleRefToCalc( aComplRef.Ref2, aApiRef.Reference2 );
+
+ if ( eOpCode == ocPush )
+ AddDoubleReference( aComplRef );
+ else
+ bError = true;
+ }
+ else if ( aType.equals( cppu::UnoType<sheet::NameToken>::get() ) )
+ {
+ sheet::NameToken aTokenData;
+ rToken.Data >>= aTokenData;
+ if ( eOpCode == ocName )
+ {
+ SAL_WARN_IF( aTokenData.Sheet < -1 || std::numeric_limits<sal_Int16>::max() < aTokenData.Sheet,
+ "sc.core",
+ "ScTokenArray::AddFormulaToken - NameToken.Sheet out of limits: " << aTokenData.Sheet);
+ sal_Int16 nSheet = static_cast<sal_Int16>(aTokenData.Sheet);
+ AddRangeName(aTokenData.Index, nSheet);
+ }
+ else if (eOpCode == ocDBArea)
+ AddDBRange(aTokenData.Index);
+ else if (eOpCode == ocTableRef)
+ bError = true; /* TODO: implementation */
+ else
+ bError = true;
+ }
+ else if ( aType.equals( cppu::UnoType<sheet::ExternalReference>::get() ) )
+ {
+ sheet::ExternalReference aApiExtRef;
+ if( (eOpCode == ocPush) && (rToken.Data >>= aApiExtRef) && (0 <= aApiExtRef.Index) && (aApiExtRef.Index <= SAL_MAX_UINT16) )
+ {
+ sal_uInt16 nFileId = static_cast< sal_uInt16 >( aApiExtRef.Index );
+ sheet::SingleReference aApiSRef;
+ sheet::ComplexReference aApiCRef;
+ OUString aName;
+ if( aApiExtRef.Reference >>= aApiSRef )
+ {
+ // try to resolve cache index to sheet name
+ size_t nCacheId = static_cast< size_t >( aApiSRef.Sheet );
+ OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
+ if( !aTabName.isEmpty() )
+ {
+ ScSingleRefData aSingleRef;
+ // convert column/row settings, set sheet index to absolute
+ lcl_ExternalRefToCalc( aSingleRef, aApiSRef );
+ AddExternalSingleReference( nFileId, rSPool.intern( aTabName), aSingleRef );
+ }
+ else
+ bError = true;
+ }
+ else if( aApiExtRef.Reference >>= aApiCRef )
+ {
+ // try to resolve cache index to sheet name.
+ size_t nCacheId = static_cast< size_t >( aApiCRef.Reference1.Sheet );
+ OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
+ if( !aTabName.isEmpty() )
+ {
+ ScComplexRefData aComplRef;
+ // convert column/row settings, set sheet index to absolute
+ lcl_ExternalRefToCalc( aComplRef.Ref1, aApiCRef.Reference1 );
+ lcl_ExternalRefToCalc( aComplRef.Ref2, aApiCRef.Reference2 );
+ // NOTE: This assumes that cached sheets are in consecutive order!
+ aComplRef.Ref2.SetAbsTab(
+ aComplRef.Ref1.Tab() + static_cast<SCTAB>(aApiCRef.Reference2.Sheet - aApiCRef.Reference1.Sheet));
+ AddExternalDoubleReference( nFileId, rSPool.intern( aTabName), aComplRef );
+ }
+ else
+ bError = true;
+ }
+ else if( aApiExtRef.Reference >>= aName )
+ {
+ if( !aName.isEmpty() )
+ AddExternalName( nFileId, rSPool.intern( aName) );
+ else
+ bError = true;
+ }
+ else
+ bError = true;
+ }
+ else
+ bError = true;
+ }
+ else
+ bError = true; // unknown struct
+ }
+ break;
+ case uno::TypeClass_SEQUENCE:
+ {
+ if ( eOpCode != ocPush )
+ bError = true; // not an inline array
+ else if (!rToken.Data.getValueType().equals( cppu::UnoType<
+ uno::Sequence< uno::Sequence< uno::Any >>>::get()))
+ bError = true; // unexpected sequence type
+ else
+ {
+ ScMatrixRef xMat = ScSequenceToMatrix::CreateMixedMatrix( rToken.Data);
+ if (xMat)
+ AddMatrix( xMat);
+ else
+ bError = true;
+ }
+ }
+ break;
+ default:
+ bError = true;
+ }
+ }
+ return bError;
+}
+
+void ScTokenArray::CheckForThreading( const FormulaToken& r )
+{
+#if HAVE_CPP_CONSTINIT_SORTED_VECTOR
+ constinit
+#endif
+ static const o3tl::sorted_vector<OpCode> aThreadedCalcDenyList({
+ ocIndirect,
+ ocMacro,
+ ocOffset,
+ ocTableOp,
+ ocCell,
+ ocMatch,
+ ocInfo,
+ ocStyle,
+ ocDBAverage,
+ ocDBCount,
+ ocDBCount2,
+ ocDBGet,
+ ocDBMax,
+ ocDBMin,
+ ocDBProduct,
+ ocDBStdDev,
+ ocDBStdDevP,
+ ocDBSum,
+ ocDBVar,
+ ocDBVarP,
+ ocText,
+ ocSheet,
+ ocExternal,
+ ocDde,
+ ocWebservice,
+ ocGetPivotData
+ });
+
+ // Don't enable threading once we decided to disable it.
+ if (!mbThreadingEnabled)
+ return;
+
+ static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
+
+ if (bThreadingProhibited)
+ {
+ mbThreadingEnabled = false;
+ return;
+ }
+
+ OpCode eOp = r.GetOpCode();
+
+ if (aThreadedCalcDenyList.find(eOp) != aThreadedCalcDenyList.end())
+ {
+ SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables threaded calculation of formula group");
+ mbThreadingEnabled = false;
+ return;
+ }
+
+ if (eOp != ocPush)
+ return;
+
+ switch (r.GetType())
+ {
+ case svExternalDoubleRef:
+ case svExternalSingleRef:
+ case svExternalName:
+ case svMatrix:
+ SAL_INFO("sc.core.formulagroup", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType())
+ << " disables threaded calculation of formula group");
+ mbThreadingEnabled = false;
+ return;
+ default:
+ break;
+ }
+}
+
+void ScTokenArray::CheckToken( const FormulaToken& r )
+{
+ if (mbThreadingEnabled)
+ CheckForThreading(r);
+
+ if (IsFormulaVectorDisabled())
+ return; // It's already disabled. No more checking needed.
+
+ OpCode eOp = r.GetOpCode();
+
+ if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION)
+ {
+ if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
+ ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
+ {
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledNotInSubSet;
+ mbOpenCLEnabled = false;
+ return;
+ }
+
+ // We support vectorization for the following opcodes.
+ switch (eOp)
+ {
+ case ocAverage:
+ case ocMin:
+ case ocMinA:
+ case ocMax:
+ case ocMaxA:
+ case ocSum:
+ case ocSumIfs:
+ case ocSumProduct:
+ case ocCount:
+ case ocCount2:
+ case ocVLookup:
+ case ocSLN:
+ case ocIRR:
+ case ocMIRR:
+ case ocPMT:
+ case ocRate:
+ case ocRRI:
+ case ocPpmt:
+ case ocFisher:
+ case ocFisherInv:
+ case ocGamma:
+ case ocGammaLn:
+ case ocNotAvail:
+ case ocGauss:
+ case ocGeoMean:
+ case ocHarMean:
+ case ocSYD:
+ case ocCorrel:
+ case ocNegBinomVert:
+ case ocPearson:
+ case ocRSQ:
+ case ocCos:
+ case ocCosecant:
+ case ocCosecantHyp:
+ case ocISPMT:
+ case ocPDuration:
+ case ocSinHyp:
+ case ocAbs:
+ case ocPV:
+ case ocSin:
+ case ocTan:
+ case ocTanHyp:
+ case ocStandard:
+ case ocWeibull:
+ case ocMedian:
+ case ocDDB:
+ case ocFV:
+ case ocVBD:
+ case ocKurt:
+ case ocNper:
+ case ocNormDist:
+ case ocArcCos:
+ case ocSqrt:
+ case ocArcCosHyp:
+ case ocNPV:
+ case ocStdNormDist:
+ case ocNormInv:
+ case ocSNormInv:
+ case ocPermut:
+ case ocPermutationA:
+ case ocPhi:
+ case ocIpmt:
+ case ocConfidence:
+ case ocIntercept:
+ case ocDB:
+ case ocLogInv:
+ case ocArcCot:
+ case ocCosHyp:
+ case ocCritBinom:
+ case ocArcCotHyp:
+ case ocArcSin:
+ case ocArcSinHyp:
+ case ocArcTan:
+ case ocArcTanHyp:
+ case ocBitAnd:
+ case ocForecast:
+ case ocLogNormDist:
+ case ocGammaDist:
+ case ocLn:
+ case ocRound:
+ case ocCot:
+ case ocCotHyp:
+ case ocFDist:
+ case ocVar:
+ case ocChiDist:
+ case ocPower:
+ case ocOdd:
+ case ocChiSqDist:
+ case ocChiSqInv:
+ case ocGammaInv:
+ case ocFloor:
+ case ocFInv:
+ case ocFTest:
+ case ocB:
+ case ocBetaDist:
+ case ocExp:
+ case ocLog10:
+ case ocExpDist:
+ case ocAverageIfs:
+ case ocCountIfs:
+ case ocCombinA:
+ case ocEven:
+ case ocLog:
+ case ocMod:
+ case ocTrunc:
+ case ocSkew:
+ case ocArcTan2:
+ case ocBitOr:
+ case ocBitLshift:
+ case ocBitRshift:
+ case ocBitXor:
+ case ocChiInv:
+ case ocPoissonDist:
+ case ocSumSQ:
+ case ocSkewp:
+ case ocBinomDist:
+ case ocVarP:
+ case ocCeil:
+ case ocCombin:
+ case ocDevSq:
+ case ocStDev:
+ case ocSlope:
+ case ocSTEYX:
+ case ocZTest:
+ case ocPi:
+ case ocRandom:
+ case ocProduct:
+ case ocHypGeomDist:
+ case ocSumX2MY2:
+ case ocSumX2DY2:
+ case ocBetaInv:
+ case ocTTest:
+ case ocTDist:
+ case ocTInv:
+ case ocSumXMY2:
+ case ocStDevP:
+ case ocCovar:
+ case ocAnd:
+ case ocOr:
+ case ocNot:
+ case ocXor:
+ case ocDBMax:
+ case ocDBMin:
+ case ocDBProduct:
+ case ocDBAverage:
+ case ocDBStdDev:
+ case ocDBStdDevP:
+ case ocDBSum:
+ case ocDBVar:
+ case ocDBVarP:
+ case ocAverageIf:
+ case ocDBCount:
+ case ocDBCount2:
+ case ocDeg:
+ case ocRoundUp:
+ case ocRoundDown:
+ case ocInt:
+ case ocRad:
+ case ocCountIf:
+ case ocIsEven:
+ case ocIsOdd:
+ case ocFact:
+ case ocAverageA:
+ case ocVarA:
+ case ocVarPA:
+ case ocStDevA:
+ case ocStDevPA:
+ case ocSecant:
+ case ocSecantHyp:
+ case ocSumIf:
+ case ocNegSub:
+ case ocAveDev:
+ // Don't change the state.
+ break;
+ default:
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledByOpCode;
+ mbOpenCLEnabled = false;
+ return;
+ }
+ }
+ else if (eOp == ocPush)
+ {
+ // This is a stack variable. See if this is a reference.
+
+ switch (r.GetType())
+ {
+ case svByte:
+ case svDouble:
+ case svString:
+ // Don't change the state.
+ break;
+ case svSingleRef:
+ case svDoubleRef:
+ // Depends on the reference state.
+ meVectorState = FormulaVectorCheckReference;
+ break;
+ case svError:
+ case svEmptyCell:
+ case svExternal:
+ case svExternalDoubleRef:
+ case svExternalName:
+ case svExternalSingleRef:
+ case svFAP:
+ case svHybridCell:
+ case svIndex:
+ case svJump:
+ case svJumpMatrix:
+ case svMatrix:
+ case svMatrixCell:
+ case svMissing:
+ case svRefList:
+ case svSep:
+ case svUnknown:
+ // We don't support vectorization on these.
+ SAL_INFO("sc.opencl", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType()) << " disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledByStackVariable;
+ mbOpenCLEnabled = false;
+ return;
+ default:
+ ;
+ }
+ }
+ else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
+ {
+ if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
+ ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
+ {
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledNotInSubSet;
+ mbOpenCLEnabled = false;
+ return;
+ }
+ }
+ else
+ {
+ // All the rest, special commands, separators, error codes, ...
+ switch (eOp)
+ {
+ default:
+ // Default is off, no vectorization.
+ // Mentioning some specific values below to indicate why.
+
+ case ocName:
+ // Named expression would need "recursive" handling of its
+ // token array for vector state in
+ // ScFormulaCell::InterpretFormulaGroup() and below.
+
+ case ocDBArea:
+ // Certainly not a vectorization of the entire area...
+
+ case ocTableRef:
+ // May result in a single cell or range reference, depending on
+ // context.
+
+ case ocColRowName:
+ // The associated reference is the name cell with which to
+ // create the implicit intersection.
+
+ case ocColRowNameAuto:
+ // Auto column/row names lead to references computed in
+ // interpreter.
+
+ SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
+ << "(" << int(eOp) << ") disables vectorisation for formula group");
+ meVectorState = FormulaVectorDisabledByOpCode;
+ mbOpenCLEnabled = false;
+ return;
+
+ // Known good, don't change state.
+ case ocStop:
+ case ocExternal:
+ case ocOpen:
+ case ocClose:
+ case ocSep:
+ case ocArrayOpen:
+ case ocArrayRowSep:
+ case ocArrayColSep:
+ case ocArrayClose:
+ case ocMissing:
+ case ocBad:
+ case ocSpaces:
+ case ocWhitespace:
+ case ocSkip:
+ case ocPercentSign:
+ case ocErrNull:
+ case ocErrDivZero:
+ case ocErrValue:
+ case ocErrRef:
+ case ocErrName:
+ case ocErrNum:
+ case ocErrNA:
+ break;
+ case ocIf:
+ case ocIfError:
+ case ocIfNA:
+ case ocChoose:
+ // Jump commands are now supported.
+ break;
+ }
+ }
+}
+
+bool ScTokenArray::ImplGetReference( ScRange& rRange, const ScAddress& rPos, bool bValidOnly ) const
+{
+ bool bIs = false;
+ if ( pCode && nLen == 1 )
+ {
+ const FormulaToken* pToken = pCode[0];
+ if ( pToken )
+ {
+ if ( pToken->GetType() == svSingleRef )
+ {
+ const ScSingleRefData& rRef = *static_cast<const ScSingleRefToken*>(pToken)->GetSingleRef();
+ rRange.aStart = rRange.aEnd = rRef.toAbs(*mxSheetLimits, rPos);
+ bIs = !bValidOnly || mxSheetLimits->ValidAddress(rRange.aStart);
+ }
+ else if ( pToken->GetType() == svDoubleRef )
+ {
+ const ScComplexRefData& rCompl = *static_cast<const ScDoubleRefToken*>(pToken)->GetDoubleRef();
+ const ScSingleRefData& rRef1 = rCompl.Ref1;
+ const ScSingleRefData& rRef2 = rCompl.Ref2;
+ rRange.aStart = rRef1.toAbs(*mxSheetLimits, rPos);
+ rRange.aEnd = rRef2.toAbs(*mxSheetLimits, rPos);
+ bIs = !bValidOnly || mxSheetLimits->ValidRange(rRange);
+ }
+ }
+ }
+ return bIs;
+}
+
+namespace {
+
+// we want to compare for similar not identical formulae
+// so we can't use actual row & column indices.
+size_t HashSingleRef( const ScSingleRefData& rRef )
+{
+ size_t nVal = 0;
+
+ nVal += size_t(rRef.IsColRel());
+ nVal += (size_t(rRef.IsRowRel()) << 1);
+ nVal += (size_t(rRef.IsTabRel()) << 2);
+
+ return nVal;
+}
+
+}
+
+void ScTokenArray::GenHash()
+{
+ static const OUStringHash aHasher;
+
+ size_t nHash = 1;
+ OpCode eOp;
+ StackVar eType;
+ const formula::FormulaToken* p;
+ sal_uInt16 n = std::min<sal_uInt16>(nLen, 20);
+ for (sal_uInt16 i = 0; i < n; ++i)
+ {
+ p = pCode[i];
+ eOp = p->GetOpCode();
+ if (eOp == ocPush)
+ {
+ // This is stack variable. Do additional differentiation.
+ eType = p->GetType();
+ switch (eType)
+ {
+ case svByte:
+ {
+ // Constant value.
+ sal_uInt8 nVal = p->GetByte();
+ nHash += static_cast<size_t>(nVal);
+ }
+ break;
+ case svDouble:
+ {
+ // Constant value.
+ double fVal = p->GetDouble();
+ nHash += std::hash<double>()(fVal);
+ }
+ break;
+ case svString:
+ {
+ // Constant string.
+ OUString aStr = p->GetString().getString();
+ nHash += aHasher(aStr);
+ }
+ break;
+ case svSingleRef:
+ {
+ size_t nVal = HashSingleRef(*p->GetSingleRef());
+ nHash += nVal;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ size_t nVal1 = HashSingleRef(rRef.Ref1);
+ size_t nVal2 = HashSingleRef(rRef.Ref2);
+ nHash += nVal1;
+ nHash += nVal2;
+ }
+ break;
+ default:
+ // Use the opcode value in all the other cases.
+ nHash += static_cast<size_t>(eOp);
+ }
+ }
+ else
+ // Use the opcode value in all the other cases.
+ nHash += static_cast<size_t>(eOp);
+
+ nHash = (nHash << 4) - nHash;
+ }
+
+ mnHashValue = nHash;
+}
+
+void ScTokenArray::ResetVectorState()
+{
+ mbOpenCLEnabled = ScCalcConfig::isOpenCLEnabled();
+ meVectorState = mbOpenCLEnabled ? FormulaVectorEnabled : FormulaVectorDisabled;
+ mbThreadingEnabled = ScCalcConfig::isThreadingEnabled();
+}
+
+bool ScTokenArray::IsFormulaVectorDisabled() const
+{
+ switch (meVectorState)
+ {
+ case FormulaVectorDisabled:
+ case FormulaVectorDisabledByOpCode:
+ case FormulaVectorDisabledByStackVariable:
+ case FormulaVectorDisabledNotInSubSet:
+ return true;
+ default:
+ ;
+ }
+
+ return false;
+}
+
+bool ScTokenArray::IsInvariant() const
+{
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ switch ((*p)->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rRef = *(*p)->GetSingleRef();
+ if (rRef.IsRowRel())
+ return false;
+ }
+ break;
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = *(*p)->GetDoubleRef();
+ if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
+ return false;
+ }
+ break;
+ case svIndex:
+ return false;
+ default:
+ ;
+ }
+ }
+
+ return true;
+}
+
+bool ScTokenArray::IsReference( ScRange& rRange, const ScAddress& rPos ) const
+{
+ return ImplGetReference(rRange, rPos, false);
+}
+
+bool ScTokenArray::IsValidReference( ScRange& rRange, const ScAddress& rPos ) const
+{
+ return ImplGetReference(rRange, rPos, true);
+}
+
+ScTokenArray::ScTokenArray(const ScDocument& rDoc) :
+ mxSheetLimits(&rDoc.GetSheetLimits()),
+ mnHashValue(0)
+{
+ ResetVectorState();
+}
+
+ScTokenArray::ScTokenArray(ScSheetLimits& rLimits) :
+ mxSheetLimits(&rLimits),
+ mnHashValue(0)
+{
+ ResetVectorState();
+}
+
+ScTokenArray::~ScTokenArray()
+{
+}
+
+ScTokenArray& ScTokenArray::operator=( const ScTokenArray& rArr )
+{
+ Clear();
+ Assign( rArr );
+ mnHashValue = rArr.mnHashValue;
+ meVectorState = rArr.meVectorState;
+ mbOpenCLEnabled = rArr.mbOpenCLEnabled;
+ mbThreadingEnabled = rArr.mbThreadingEnabled;
+ return *this;
+}
+
+ScTokenArray& ScTokenArray::operator=( ScTokenArray&& rArr )
+{
+ mxSheetLimits = std::move(rArr.mxSheetLimits);
+ mnHashValue = rArr.mnHashValue;
+ meVectorState = rArr.meVectorState;
+ mbOpenCLEnabled = rArr.mbOpenCLEnabled;
+ mbThreadingEnabled = rArr.mbThreadingEnabled;
+ Move(std::move(rArr));
+ return *this;
+}
+
+bool ScTokenArray::EqualTokens( const ScTokenArray* pArr2) const
+{
+ // We only compare the non-RPN array
+ if ( pArr2->nLen != nLen )
+ return false;
+
+ FormulaToken** ppToken1 = GetArray();
+ FormulaToken** ppToken2 = pArr2->GetArray();
+ for (sal_uInt16 i=0; i<nLen; i++)
+ {
+ if ( ppToken1[i] != ppToken2[i] &&
+ !(*ppToken1[i] == *ppToken2[i]) )
+ return false; // Difference
+ }
+ return true; // All entries are the same
+}
+
+void ScTokenArray::Clear()
+{
+ mnHashValue = 0;
+ ResetVectorState();
+ FormulaTokenArray::Clear();
+}
+
+std::unique_ptr<ScTokenArray> ScTokenArray::Clone() const
+{
+ std::unique_ptr<ScTokenArray> p(new ScTokenArray(*mxSheetLimits));
+ p->nLen = nLen;
+ p->nRPN = nRPN;
+ p->nMode = nMode;
+ p->nError = nError;
+ p->bHyperLink = bHyperLink;
+ p->mnHashValue = mnHashValue;
+ p->meVectorState = meVectorState;
+ p->mbOpenCLEnabled = mbOpenCLEnabled;
+ p->mbThreadingEnabled = mbThreadingEnabled;
+ p->mbFromRangeName = mbFromRangeName;
+ p->mbShareable = mbShareable;
+
+ FormulaToken** pp;
+ if( nLen )
+ {
+ p->pCode.reset(new FormulaToken*[ nLen ]);
+ pp = p->pCode.get();
+ memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
+ {
+ *pp = (*pp)->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ if( nRPN )
+ {
+ pp = p->pRPN = new FormulaToken*[ nRPN ];
+ memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
+ {
+ FormulaToken* t = *pp;
+ if( t->GetRef() > 1 )
+ {
+ FormulaToken** p2 = pCode.get();
+ sal_uInt16 nIdx = 0xFFFF;
+ for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
+ {
+ if( *p2 == t )
+ {
+ nIdx = j; break;
+ }
+ }
+ if( nIdx == 0xFFFF )
+ *pp = t->Clone();
+ else
+ *pp = p->pCode[ nIdx ];
+ }
+ else
+ *pp = t->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ return p;
+}
+
+ScTokenArray ScTokenArray::CloneValue() const
+{
+ ScTokenArray aNew(*mxSheetLimits);
+ aNew.nLen = nLen;
+ aNew.nRPN = nRPN;
+ aNew.nMode = nMode;
+ aNew.nError = nError;
+ aNew.bHyperLink = bHyperLink;
+ aNew.mnHashValue = mnHashValue;
+ aNew.meVectorState = meVectorState;
+ aNew.mbOpenCLEnabled = mbOpenCLEnabled;
+ aNew.mbThreadingEnabled = mbThreadingEnabled;
+ aNew.mbFromRangeName = mbFromRangeName;
+ aNew.mbShareable = mbShareable;
+
+ FormulaToken** pp;
+ if( nLen )
+ {
+ aNew.pCode.reset(new FormulaToken*[ nLen ]);
+ pp = aNew.pCode.get();
+ memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
+ {
+ *pp = (*pp)->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ if( nRPN )
+ {
+ pp = aNew.pRPN = new FormulaToken*[ nRPN ];
+ memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
+ for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
+ {
+ FormulaToken* t = *pp;
+ if( t->GetRef() > 1 )
+ {
+ FormulaToken** p2 = pCode.get();
+ sal_uInt16 nIdx = 0xFFFF;
+ for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
+ {
+ if( *p2 == t )
+ {
+ nIdx = j; break;
+ }
+ }
+ if( nIdx == 0xFFFF )
+ *pp = t->Clone();
+ else
+ *pp = aNew.pCode[ nIdx ];
+ }
+ else
+ *pp = t->Clone();
+ (*pp)->IncRef();
+ }
+ }
+ return aNew;
+}
+
+FormulaToken* ScTokenArray::AddRawToken( const ScRawToken& r )
+{
+ return Add( r.CreateToken(*mxSheetLimits) );
+}
+
+// Utility function to ensure that there is strict alternation of values and
+// separators.
+static bool
+checkArraySep( bool & bPrevWasSep, bool bNewVal )
+{
+ bool bResult = (bPrevWasSep == bNewVal);
+ bPrevWasSep = bNewVal;
+ return bResult;
+}
+
+FormulaToken* ScTokenArray::MergeArray( )
+{
+ int nCol = -1, nRow = 0;
+ int i, nPrevRowSep = -1, nStart = 0;
+ bool bPrevWasSep = false; // top of stack is ocArrayClose
+ FormulaToken* t;
+ bool bNumeric = false; // numeric value encountered in current element
+
+ // (1) Iterate from the end to the start to find matrix dims
+ // and do basic validation.
+ for ( i = nLen ; i-- > nStart ; )
+ {
+ t = pCode[i];
+ switch ( t->GetOpCode() )
+ {
+ case ocPush :
+ if( checkArraySep( bPrevWasSep, false ) )
+ {
+ return nullptr;
+ }
+
+ // no references or nested arrays
+ if ( t->GetType() != svDouble && t->GetType() != svString )
+ {
+ return nullptr;
+ }
+ bNumeric = (t->GetType() == svDouble);
+ break;
+
+ case ocMissing :
+ case ocTrue :
+ case ocFalse :
+ if( checkArraySep( bPrevWasSep, false ) )
+ {
+ return nullptr;
+ }
+ bNumeric = false;
+ break;
+
+ case ocArrayColSep :
+ case ocSep :
+ if( checkArraySep( bPrevWasSep, true ) )
+ {
+ return nullptr;
+ }
+ bNumeric = false;
+ break;
+
+ case ocArrayClose :
+ // not possible with the , but check just in case
+ // something changes in the future
+ if( i != (nLen-1))
+ {
+ return nullptr;
+ }
+
+ if( checkArraySep( bPrevWasSep, true ) )
+ {
+ return nullptr;
+ }
+
+ nPrevRowSep = i;
+ bNumeric = false;
+ break;
+
+ case ocArrayOpen :
+ nStart = i; // stop iteration
+ [[fallthrough]]; // to ArrayRowSep
+
+ case ocArrayRowSep :
+ if( checkArraySep( bPrevWasSep, true ) )
+ {
+ return nullptr;
+ }
+
+ if( nPrevRowSep < 0 || // missing ocArrayClose
+ ((nPrevRowSep - i) % 2) == 1) // no complex elements
+ {
+ return nullptr;
+ }
+
+ if( nCol < 0 )
+ {
+ nCol = (nPrevRowSep - i) / 2;
+ }
+ else if( (nPrevRowSep - i)/2 != nCol) // irregular array
+ {
+ return nullptr;
+ }
+
+ nPrevRowSep = i;
+ nRow++;
+ bNumeric = false;
+ break;
+
+ case ocNegSub :
+ case ocAdd :
+ // negation or unary plus must precede numeric value
+ if( !bNumeric )
+ {
+ return nullptr;
+ }
+ --nPrevRowSep; // shorten this row by 1
+ bNumeric = false; // one level only, no --42
+ break;
+
+ case ocSpaces :
+ case ocWhitespace :
+ // ignore spaces
+ --nPrevRowSep; // shorten this row by 1
+ break;
+
+ default :
+ // no functions or operators
+ return nullptr;
+ }
+ }
+ if( nCol <= 0 || nRow <= 0 )
+ return nullptr;
+
+ int nSign = 1;
+ ScMatrix* pArray = new ScMatrix(nCol, nRow, 0.0);
+ for ( i = nStart, nCol = 0, nRow = 0 ; i < nLen ; i++ )
+ {
+ t = pCode[i];
+
+ switch ( t->GetOpCode() )
+ {
+ case ocPush :
+ if ( t->GetType() == svDouble )
+ {
+ pArray->PutDouble( t->GetDouble() * nSign, nCol, nRow );
+ nSign = 1;
+ }
+ else if ( t->GetType() == svString )
+ {
+ pArray->PutString(t->GetString(), nCol, nRow);
+ }
+ break;
+
+ case ocMissing :
+ pArray->PutEmpty( nCol, nRow );
+ break;
+
+ case ocTrue :
+ pArray->PutBoolean( true, nCol, nRow );
+ break;
+
+ case ocFalse :
+ pArray->PutBoolean( false, nCol, nRow );
+ break;
+
+ case ocArrayColSep :
+ case ocSep :
+ nCol++;
+ break;
+
+ case ocArrayRowSep :
+ nRow++; nCol = 0;
+ break;
+
+ case ocNegSub :
+ nSign = -nSign;
+ break;
+
+ default :
+ break;
+ }
+ pCode[i] = nullptr;
+ t->DecRef();
+ }
+ nLen = sal_uInt16( nStart );
+ return AddMatrix( pArray );
+}
+
+void ScTokenArray::MergeRangeReference( const ScAddress & rPos )
+{
+ if (!pCode || !nLen)
+ return;
+ sal_uInt16 nIdx = nLen;
+
+ // The actual types are checked in extendRangeReference().
+ FormulaToken *p3 = PeekPrev(nIdx); // ref
+ if (!p3)
+ return;
+ FormulaToken *p2 = PeekPrev(nIdx); // ocRange
+ if (!p2 || p2->GetOpCode() != ocRange)
+ return;
+ FormulaToken *p1 = PeekPrev(nIdx); // ref
+ if (!p1)
+ return;
+ FormulaTokenRef p = extendRangeReference( *mxSheetLimits, *p1, *p3, rPos, true);
+ if (p)
+ {
+ p->IncRef();
+ p1->DecRef();
+ p2->DecRef();
+ p3->DecRef();
+ nLen -= 2;
+ pCode[ nLen-1 ] = p.get();
+ }
+}
+
+FormulaToken* ScTokenArray::AddOpCode( OpCode e )
+{
+ ScRawToken t;
+ t.SetOpCode( e );
+ return AddRawToken( t );
+}
+
+FormulaToken* ScTokenArray::AddSingleReference( const ScSingleRefData& rRef )
+{
+ return Add( new ScSingleRefToken( *mxSheetLimits, rRef ) );
+}
+
+FormulaToken* ScTokenArray::AddMatrixSingleReference( const ScSingleRefData& rRef )
+{
+ return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocMatRef ) );
+}
+
+FormulaToken* ScTokenArray::AddDoubleReference( const ScComplexRefData& rRef )
+{
+ return Add( new ScDoubleRefToken(*mxSheetLimits, rRef ) );
+}
+
+FormulaToken* ScTokenArray::AddMatrix( const ScMatrixRef& p )
+{
+ return Add( new ScMatrixToken( p ) );
+}
+
+void ScTokenArray::AddRangeName( sal_uInt16 n, sal_Int16 nSheet )
+{
+ Add( new FormulaIndexToken( ocName, n, nSheet));
+}
+
+FormulaToken* ScTokenArray::AddDBRange( sal_uInt16 n )
+{
+ return Add( new FormulaIndexToken( ocDBArea, n));
+}
+
+FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const svl::SharedString& rName )
+{
+ return Add( new ScExternalNameToken(nFileId, rName) );
+}
+
+void ScTokenArray::AddExternalSingleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
+ const ScSingleRefData& rRef )
+{
+ Add( new ScExternalSingleRefToken(nFileId, rTabName, rRef) );
+}
+
+FormulaToken* ScTokenArray::AddExternalDoubleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
+ const ScComplexRefData& rRef )
+{
+ return Add( new ScExternalDoubleRefToken(nFileId, rTabName, rRef) );
+}
+
+FormulaToken* ScTokenArray::AddColRowName( const ScSingleRefData& rRef )
+{
+ return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocColRowName ) );
+}
+
+void ScTokenArray::AssignXMLString( const OUString &rText, const OUString &rFormulaNmsp )
+{
+ sal_uInt16 nTokens = 1;
+ FormulaToken *aTokens[2];
+
+ aTokens[0] = new FormulaStringOpToken( ocStringXML, svl::SharedString( rText) ); // string not interned
+ if( !rFormulaNmsp.isEmpty() )
+ aTokens[ nTokens++ ] = new FormulaStringOpToken( ocStringXML,
+ svl::SharedString( rFormulaNmsp) ); // string not interned
+
+ Assign( nTokens, aTokens );
+}
+
+bool ScTokenArray::GetAdjacentExtendOfOuterFuncRefs( SCCOLROW& nExtend,
+ const ScAddress& rPos, ScDirection eDir )
+{
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ switch ( eDir )
+ {
+ case DIR_BOTTOM :
+ if ( rPos.Row() >= mxSheetLimits->mnMaxRow )
+ return false;
+ nExtend = rPos.Row();
+ nRow = nExtend + 1;
+ break;
+ case DIR_RIGHT :
+ if ( rPos.Col() >= mxSheetLimits->mnMaxCol )
+ return false;
+ nExtend = rPos.Col();
+ nCol = static_cast<SCCOL>(nExtend) + 1;
+ break;
+ case DIR_TOP :
+ if ( rPos.Row() <= 0 )
+ return false;
+ nExtend = rPos.Row();
+ nRow = nExtend - 1;
+ break;
+ case DIR_LEFT :
+ if ( rPos.Col() <= 0 )
+ return false;
+ nExtend = rPos.Col();
+ nCol = static_cast<SCCOL>(nExtend) - 1;
+ break;
+ default:
+ OSL_FAIL( "unknown Direction" );
+ return false;
+ }
+ if ( pRPN && nRPN )
+ {
+ FormulaToken* t = pRPN[nRPN-1];
+ if ( t->GetType() == svByte )
+ {
+ sal_uInt8 nParamCount = t->GetByte();
+ if ( nParamCount && nRPN > nParamCount )
+ {
+ bool bRet = false;
+ sal_uInt16 nParam = nRPN - nParamCount - 1;
+ for ( ; nParam < nRPN-1; nParam++ )
+ {
+ FormulaToken* p = pRPN[nParam];
+ switch ( p->GetType() )
+ {
+ case svSingleRef :
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ switch ( eDir )
+ {
+ case DIR_BOTTOM :
+ if (aAbs.Row() == nRow && aAbs.Row() > nExtend)
+ {
+ nExtend = aAbs.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_RIGHT :
+ if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) > nExtend)
+ {
+ nExtend = aAbs.Col();
+ bRet = true;
+ }
+ break;
+ case DIR_TOP :
+ if (aAbs.Row() == nRow && aAbs.Row() < nExtend)
+ {
+ nExtend = aAbs.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_LEFT :
+ if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) < nExtend)
+ {
+ nExtend = aAbs.Col();
+ bRet = true;
+ }
+ break;
+ }
+ }
+ break;
+ case svDoubleRef :
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ switch ( eDir )
+ {
+ case DIR_BOTTOM :
+ if (aAbs.aStart.Row() == nRow && aAbs.aEnd.Row() > nExtend)
+ {
+ nExtend = aAbs.aEnd.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_RIGHT :
+ if (aAbs.aStart.Col() == nCol && static_cast<SCCOLROW>(aAbs.aEnd.Col()) > nExtend)
+ {
+ nExtend = aAbs.aEnd.Col();
+ bRet = true;
+ }
+ break;
+ case DIR_TOP :
+ if (aAbs.aEnd.Row() == nRow && aAbs.aStart.Row() < nExtend)
+ {
+ nExtend = aAbs.aStart.Row();
+ bRet = true;
+ }
+ break;
+ case DIR_LEFT :
+ if (aAbs.aEnd.Col() == nCol && static_cast<SCCOLROW>(aAbs.aStart.Col()) < nExtend)
+ {
+ nExtend = aAbs.aStart.Col();
+ bRet = true;
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ } // switch
+ } // for
+ return bRet;
+ }
+ }
+ }
+ return false;
+}
+
+namespace {
+
+void GetExternalTableData(const ScDocument* pOldDoc, const ScDocument* pNewDoc, const SCTAB nTab, OUString& rTabName, sal_uInt16& rFileId)
+{
+ const OUString& aFileName = pOldDoc->GetFileURL();
+ rFileId = pNewDoc->GetExternalRefManager()->getExternalFileId(aFileName);
+ rTabName = pOldDoc->GetCopyTabName(nTab);
+ if (rTabName.isEmpty())
+ pOldDoc->GetName(nTab, rTabName);
+}
+
+bool IsInCopyRange( const ScRange& rRange, const ScDocument* pClipDoc )
+{
+ ScClipParam& rClipParam = const_cast<ScDocument*>(pClipDoc)->GetClipParam();
+ return rClipParam.maRanges.Contains(rRange);
+}
+
+bool SkipReference(formula::FormulaToken* pToken, const ScAddress& rPos, const ScDocument& rOldDoc, bool bRangeName, bool bCheckCopyArea)
+{
+ ScRange aRange;
+
+ if (!ScRefTokenHelper::getRangeFromToken(&rOldDoc, aRange, pToken, rPos))
+ return true;
+
+ if (bRangeName && aRange.aStart.Tab() == rPos.Tab())
+ {
+ switch (pToken->GetType())
+ {
+ case svDoubleRef:
+ {
+ ScSingleRefData& rRef = *pToken->GetSingleRef2();
+ if (rRef.IsColRel() || rRef.IsRowRel())
+ return true;
+ }
+ [[fallthrough]];
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *pToken->GetSingleRef();
+ if (rRef.IsColRel() || rRef.IsRowRel())
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bCheckCopyArea && IsInCopyRange(aRange, &rOldDoc))
+ return true;
+
+ return false;
+}
+
+void AdjustSingleRefData( ScSingleRefData& rRef, const ScAddress& rOldPos, const ScAddress& rNewPos)
+{
+ SCCOL nCols = rNewPos.Col() - rOldPos.Col();
+ SCROW nRows = rNewPos.Row() - rOldPos.Row();
+ SCTAB nTabs = rNewPos.Tab() - rOldPos.Tab();
+
+ if (!rRef.IsColRel())
+ rRef.IncCol(nCols);
+
+ if (!rRef.IsRowRel())
+ rRef.IncRow(nRows);
+
+ if (!rRef.IsTabRel())
+ rRef.IncTab(nTabs);
+}
+
+}
+
+void ScTokenArray::ReadjustAbsolute3DReferences( const ScDocument& rOldDoc, ScDocument& rNewDoc, const ScAddress& rPos, bool bRangeName )
+{
+ for ( sal_uInt16 j=0; j<nLen; ++j )
+ {
+ switch ( pCode[j]->GetType() )
+ {
+ case svDoubleRef :
+ {
+ if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
+ continue;
+
+ ScComplexRefData& rRef = *pCode[j]->GetDoubleRef();
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScSingleRefData& rRef1 = rRef.Ref1;
+
+ if ( (rRef2.IsFlag3D() && !rRef2.IsTabRel()) || (rRef1.IsFlag3D() && !rRef1.IsTabRel()) )
+ {
+ OUString aTabName;
+ sal_uInt16 nFileId;
+ GetExternalTableData(&rOldDoc, &rNewDoc, rRef1.Tab(), aTabName, nFileId);
+ ReplaceToken( j, new ScExternalDoubleRefToken( nFileId,
+ rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
+ // ATTENTION: rRef can't be used after this point
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
+ continue;
+
+ ScSingleRefData& rRef = *pCode[j]->GetSingleRef();
+
+ if ( rRef.IsFlag3D() && !rRef.IsTabRel() )
+ {
+ OUString aTabName;
+ sal_uInt16 nFileId;
+ GetExternalTableData(&rOldDoc, &rNewDoc, rRef.Tab(), aTabName, nFileId);
+ ReplaceToken( j, new ScExternalSingleRefToken( nFileId,
+ rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
+ // ATTENTION: rRef can't be used after this point
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustAbsoluteRefs( const ScDocument& rOldDoc, const ScAddress& rOldPos, const ScAddress& rNewPos,
+ bool bCheckCopyRange)
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, true);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch ( p->GetType() )
+ {
+ case svDoubleRef :
+ {
+ if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
+ continue;
+
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScSingleRefData& rRef1 = rRef.Ref1;
+
+ AdjustSingleRefData( rRef1, rOldPos, rNewPos );
+ AdjustSingleRefData( rRef2, rOldPos, rNewPos );
+ }
+ break;
+ case svSingleRef :
+ {
+ if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
+ continue;
+
+ ScSingleRefData& rRef = *p->GetSingleRef();
+
+ AdjustSingleRefData( rRef, rOldPos, rNewPos );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustSheetLocalNameReferences( SCTAB nOldTab, SCTAB nNewTab )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch ( p->GetType() )
+ {
+ case svDoubleRef :
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScSingleRefData& rRef1 = rRef.Ref1;
+
+ if (!rRef1.IsTabRel() && rRef1.Tab() == nOldTab)
+ rRef1.SetAbsTab( nNewTab);
+ if (!rRef2.IsTabRel() && rRef2.Tab() == nOldTab)
+ rRef2.SetAbsTab( nNewTab);
+ if (!rRef1.IsTabRel() && !rRef2.IsTabRel() && rRef1.Tab() > rRef2.Tab())
+ {
+ SCTAB nTab = rRef1.Tab();
+ rRef1.SetAbsTab( rRef2.Tab());
+ rRef2.SetAbsTab( nTab);
+ }
+ }
+ break;
+ case svSingleRef :
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+
+ if (!rRef.IsTabRel() && rRef.Tab() == nOldTab)
+ rRef.SetAbsTab( nNewTab);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+bool ScTokenArray::ReferencesSheet( SCTAB nTab, SCTAB nPosTab ) const
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken* const * const pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ const FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch ( p->GetType() )
+ {
+ case svDoubleRef :
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ const ScSingleRefData& rRef2 = rRef.Ref2;
+ const ScSingleRefData& rRef1 = rRef.Ref1;
+
+ SCTAB nTab1 = (rRef1.IsTabRel() ? rRef1.Tab() + nPosTab : rRef1.Tab());
+ SCTAB nTab2 = (rRef2.IsTabRel() ? rRef2.Tab() + nPosTab : rRef2.Tab());
+ if (nTab1 <= nTab && nTab <= nTab2)
+ return true;
+ }
+ break;
+ case svSingleRef :
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsTabRel())
+ {
+ if (rRef.Tab() + nPosTab == nTab)
+ return true;
+ }
+ else
+ {
+ if (rRef.Tab() == nTab)
+ return true;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ return false;
+}
+
+namespace {
+
+ScRange getSelectedRange( const sc::RefUpdateContext& rCxt )
+{
+ ScRange aSelectedRange(ScAddress::INITIALIZE_INVALID);
+ if (rCxt.mnColDelta < 0)
+ {
+ // Delete and shift to left.
+ aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta, rCxt.maRange.aStart.Row(), rCxt.maRange.aStart.Tab());
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ // Delete and shift up.
+ aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta, rCxt.maRange.aStart.Tab());
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()-1, rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnTabDelta < 0)
+ {
+ // Deleting sheets.
+ // TODO : Figure out what to do here.
+ }
+ else if (rCxt.mnColDelta > 0)
+ {
+ // Insert and shift to the right.
+ aSelectedRange.aStart = rCxt.maRange.aStart;
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnRowDelta > 0)
+ {
+ // Insert and shift down.
+ aSelectedRange.aStart = rCxt.maRange.aStart;
+ aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta-1, rCxt.maRange.aEnd.Tab());
+ }
+ else if (rCxt.mnTabDelta > 0)
+ {
+ // Inserting sheets.
+ // TODO : Figure out what to do here.
+ }
+
+ return aSelectedRange;
+}
+
+void setRefDeleted( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.mnColDelta < 0)
+ rRef.SetColDeleted(true);
+ else if (rCxt.mnRowDelta < 0)
+ rRef.SetRowDeleted(true);
+ else if (rCxt.mnTabDelta < 0)
+ rRef.SetTabDeleted(true);
+}
+
+void restoreDeletedRef( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.mnColDelta)
+ {
+ if (rRef.IsColDeleted())
+ rRef.SetColDeleted(false);
+ }
+ else if (rCxt.mnRowDelta)
+ {
+ if (rRef.IsRowDeleted())
+ rRef.SetRowDeleted(false);
+ }
+ else if (rCxt.mnTabDelta)
+ {
+ if (rRef.IsTabDeleted())
+ rRef.SetTabDeleted(false);
+ }
+}
+
+void setRefDeleted( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ if (rCxt.mnColDelta < 0)
+ {
+ rRef.Ref1.SetColDeleted(true);
+ rRef.Ref2.SetColDeleted(true);
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ rRef.Ref1.SetRowDeleted(true);
+ rRef.Ref2.SetRowDeleted(true);
+ }
+ else if (rCxt.mnTabDelta < 0)
+ {
+ rRef.Ref1.SetTabDeleted(true);
+ rRef.Ref2.SetTabDeleted(true);
+ }
+}
+
+void restoreDeletedRef( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
+{
+ restoreDeletedRef(rRef.Ref1, rCxt);
+ restoreDeletedRef(rRef.Ref2, rCxt);
+}
+
+enum ShrinkResult
+{
+ UNMODIFIED,
+ SHRUNK,
+ STICKY
+};
+
+ShrinkResult shrinkRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rDeletedRange,
+ const ScComplexRefData& rRef )
+{
+ if (!rDeletedRange.Intersects(rRefRange))
+ return UNMODIFIED;
+
+ if (rCxt.mnColDelta < 0)
+ {
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Entire rows are not affected, columns are anchored.
+ return STICKY;
+
+ // Shifting left.
+ if (rRefRange.aStart.Row() < rDeletedRange.aStart.Row() || rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ // Deleted range is only partially overlapping in vertical direction. Bail out.
+ return UNMODIFIED;
+
+ if (rDeletedRange.aStart.Col() <= rRefRange.aStart.Col())
+ {
+ if (rRefRange.aEnd.Col() <= rDeletedRange.aEnd.Col())
+ {
+ // Reference is entirely deleted.
+ rRefRange.SetInvalid();
+ }
+ else
+ {
+ // The reference range is truncated on the left.
+ SCCOL nOffset = rDeletedRange.aStart.Col() - rRefRange.aStart.Col();
+ SCCOL nDelta = rRefRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta+nOffset);
+ rRefRange.aStart.IncCol(nOffset);
+ }
+ }
+ else if (rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ {
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // Reference is deleted in the middle. Move the last column
+ // position to the left.
+ SCCOL nDelta = rDeletedRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ }
+ else
+ {
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // The reference range is truncated on the right.
+ SCCOL nDelta = rDeletedRange.aStart.Col() - rRefRange.aEnd.Col() - 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ }
+ return SHRUNK;
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Entire columns are not affected, rows are anchored.
+ return STICKY;
+
+ // Shifting up.
+
+ if (rRefRange.aStart.Col() < rDeletedRange.aStart.Col() || rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ // Deleted range is only partially overlapping in horizontal direction. Bail out.
+ return UNMODIFIED;
+
+ if (rDeletedRange.aStart.Row() <= rRefRange.aStart.Row())
+ {
+ if (rRefRange.aEnd.Row() <= rDeletedRange.aEnd.Row())
+ {
+ // Reference is entirely deleted.
+ rRefRange.SetInvalid();
+ }
+ else
+ {
+ // The reference range is truncated on the top.
+ SCROW nOffset = rDeletedRange.aStart.Row() - rRefRange.aStart.Row();
+ SCROW nDelta = rRefRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta+nOffset);
+ rRefRange.aStart.IncRow(nOffset);
+ }
+ }
+ else if (rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ {
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // Reference is deleted in the middle. Move the last row
+ // position upward.
+ SCROW nDelta = rDeletedRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ }
+ else
+ {
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return STICKY;
+
+ // The reference range is truncated on the bottom.
+ SCROW nDelta = rDeletedRange.aStart.Row() - rRefRange.aEnd.Row() - 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ }
+ return SHRUNK;
+ }
+
+ return UNMODIFIED;
+}
+
+bool expandRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
+ const ScComplexRefData& rRef )
+{
+ if (!rSelectedRange.Intersects(rRefRange))
+ return false;
+
+ if (rCxt.mnColDelta > 0)
+ {
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Entire rows are not affected, columns are anchored.
+ return false;
+
+ // Insert and shifting right.
+ if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ // Selected range is only partially overlapping in vertical direction. Bail out.
+ return false;
+
+ if (rCxt.mrDoc.IsExpandRefs())
+ {
+ if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
+ // Reference must be at least two columns wide.
+ return false;
+ }
+ else
+ {
+ if (rSelectedRange.aStart.Col() <= rRefRange.aStart.Col())
+ // Selected range is at the left end and the edge expansion is turned off. No expansion.
+ return false;
+ }
+
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last column position to the right.
+ SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+ else if (rCxt.mnRowDelta > 0)
+ {
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Entire columns are not affected, rows are anchored.
+ return false;
+
+ // Insert and shifting down.
+ if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ // Selected range is only partially overlapping in horizontal direction. Bail out.
+ return false;
+
+ if (rCxt.mrDoc.IsExpandRefs())
+ {
+ if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
+ // Reference must be at least two rows tall.
+ return false;
+ }
+ else
+ {
+ if (rSelectedRange.aStart.Row() <= rRefRange.aStart.Row())
+ // Selected range is at the top end and the edge expansion is turned off. No expansion.
+ return false;
+ }
+
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last row position down.
+ SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check if the referenced range is expandable when the selected range is
+ * not overlapping the referenced range.
+ */
+bool expandRangeByEdge( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
+ const ScComplexRefData& rRef )
+{
+ if (!rCxt.mrDoc.IsExpandRefs())
+ // Edge-expansion is turned off.
+ return false;
+
+ if (rSelectedRange.aStart.Tab() > rRefRange.aStart.Tab() || rRefRange.aEnd.Tab() > rSelectedRange.aEnd.Tab())
+ // Sheet references not within selected range.
+ return false;
+
+ if (rCxt.mnColDelta > 0)
+ {
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Entire rows are not affected, columns are anchored.
+ return false;
+
+ // Insert and shift right.
+
+ if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
+ // Reference must be at least two columns wide.
+ return false;
+
+ if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
+ // Selected range is only partially overlapping in vertical direction. Bail out.
+ return false;
+
+ if (rSelectedRange.aStart.Col() - rRefRange.aEnd.Col() != 1)
+ // Selected range is not immediately adjacent. Bail out.
+ return false;
+
+ if (rRefRange.IsEndColSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last column position to the right.
+ SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
+ rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+ else if (rCxt.mnRowDelta > 0)
+ {
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Entire columns are not affected, rows are anchored.
+ return false;
+
+ if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
+ // Reference must be at least two rows tall.
+ return false;
+
+ if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
+ // Selected range is only partially overlapping in horizontal direction. Bail out.
+ return false;
+
+ if (rSelectedRange.aStart.Row() - rRefRange.aEnd.Row() != 1)
+ // Selected range is not immediately adjacent. Bail out.
+ return false;
+
+ if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
+ // Sticky end not affected.
+ return false;
+
+ // Move the last row position down.
+ SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
+ rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
+ return true;
+ }
+
+ return false;
+}
+
+bool isNameModified( const sc::UpdatedRangeNames& rUpdatedNames, SCTAB nOldTab, const formula::FormulaToken& rToken )
+{
+ SCTAB nTab = -1;
+ if (rToken.GetSheet() >= 0)
+ nTab = nOldTab;
+
+ // Check if this named expression has been modified.
+ return rUpdatedNames.isNameUpdated(nTab, rToken.GetIndex());
+}
+
+bool isDBDataModified( const ScDocument& rDoc, const formula::FormulaToken& rToken )
+{
+ // Check if this DBData has been modified.
+ const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( rToken.GetIndex());
+ if (!pDBData)
+ return true;
+
+ return pDBData->IsModified();
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnShift( const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos )
+{
+ ScRange aSelectedRange = getSelectedRange(rCxt);
+
+ sc::RefUpdateResult aRes;
+ ScAddress aNewPos = rOldPos;
+ bool bCellShifted = rCxt.maRange.Contains(rOldPos);
+ if (bCellShifted)
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aNewPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+ }
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ if (rCxt.isDeleted() && aSelectedRange.Contains(aAbs))
+ {
+ // This reference is in the deleted region.
+ setRefDeleted(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+
+ if (!rCxt.isDeleted() && rRef.IsDeleted())
+ {
+ // Check if the token has reference to previously deleted region.
+ ScAddress aCheckPos = rRef.toAbs(*mxSheetLimits, aNewPos);
+ if (rCxt.maRange.Contains(aCheckPos))
+ {
+ restoreDeletedRef(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+ }
+
+ if (rCxt.maRange.Contains(aAbs))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ aAbs = aErrorPos;
+ aRes.mbReferenceModified = true;
+ }
+
+ rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ if (rCxt.isDeleted())
+ {
+ if (aSelectedRange.Contains(aAbs))
+ {
+ // This reference is in the deleted region.
+ setRefDeleted(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+ else if (aSelectedRange.Intersects(aAbs))
+ {
+ const ShrinkResult eSR = shrinkRange(rCxt, aAbs, aSelectedRange, rRef);
+ if (eSR == SHRUNK)
+ {
+ // The reference range has been shrunk.
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ aRes.mbValueChanged = true;
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ else if (eSR == STICKY)
+ {
+ // The reference range stays the same but a
+ // new (empty) cell range is shifted in and
+ // may change the calculation result.
+ aRes.mbValueChanged = true;
+ // Sticky when intersecting the selected
+ // range means also that the other
+ // conditions below are not met,
+ // specifically not the
+ // if (rCxt.maRange.Contains(aAbs))
+ // that is able to update the reference,
+ // but aSelectedRange does not intersect
+ // with rCxt.maRange so that can't happen
+ // and we can bail out early without
+ // updating the reference.
+ break;
+ }
+ }
+ }
+
+ if (!rCxt.isDeleted() && rRef.IsDeleted())
+ {
+ // Check if the token has reference to previously deleted region.
+ ScRange aCheckRange = rRef.toAbs(*mxSheetLimits, aNewPos);
+ if (aSelectedRange.Contains(aCheckRange))
+ {
+ // This reference was previously in the deleted region. Restore it.
+ restoreDeletedRef(rRef, rCxt);
+ aRes.mbValueChanged = true;
+ break;
+ }
+ }
+
+ if (rCxt.isInserted())
+ {
+ if (expandRange(rCxt, aAbs, aSelectedRange, rRef))
+ {
+ // The reference range has been expanded.
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ aRes.mbValueChanged = true;
+ aRes.mbReferenceModified = true;
+ break;
+ }
+
+ if (expandRangeByEdge(rCxt, aAbs, aSelectedRange, rRef))
+ {
+ // The reference range has been expanded on the edge.
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ aRes.mbValueChanged = true;
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ }
+
+ if (rCxt.maRange.Contains(aAbs))
+ {
+ // We shift either by column or by row, not both,
+ // so moving the reference has only to be done in
+ // the non-sticky case.
+ if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ || (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
+ {
+ // In entire col/row, values are shifted within
+ // the reference, which affects all positional
+ // results like in MATCH or matrix positions.
+ aRes.mbValueChanged = true;
+ }
+ else
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aAbs.MoveSticky(rCxt.mrDoc, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange))
+ aAbs = aErrorRange;
+ aRes.mbReferenceModified = true;
+ }
+ }
+ else if (rCxt.maRange.Intersects(aAbs))
+ {
+ // Part of the referenced range is being shifted. This
+ // will change the values of the range.
+ aRes.mbValueChanged = true;
+ }
+
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ // For external reference, just reset the reference with
+ // respect to the new cell position.
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ // Same as above.
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ if (rCxt.mnTabDelta &&
+ rCxt.maRange.aStart.Tab() <= nOldTab && nOldTab <= rCxt.maRange.aEnd.Tab())
+ {
+ aRes.mbNameModified = true;
+ (*pp)->SetSheet( nOldTab + rCxt.mnTabDelta);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+
+ return aRes;
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ sc::RefUpdateResult aRes;
+
+ if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
+ // The cell hasn't moved at all.
+ return aRes;
+
+ // When moving, the range in the context is the destination range. We need
+ // to use the old range prior to the move for hit analysis.
+ ScRange aOldRange = rCxt.maRange;
+ ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
+ if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ // Additionally, do not update the references from cells within the moved
+ // range as they lead to #REF! errors here. These #REF! cannot by fixed
+ // later in UpdateTranspose().
+ if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ aAbs = aErrorPos;
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.maRange.Contains(aAbs))
+ {
+ // Referenced cell has been overwritten.
+ aRes.mbValueChanged = true;
+ }
+
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ rRef.SetFlag3D(rRef.IsFlag3D() || !rRef.IsTabRel() || aAbs.Tab() != rNewPos.Tab());
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ // Additionally, do not update the references from cells within the moved
+ // range as they lead to #REF! errors here. These #REF! cannot by fixed
+ // later in UpdateTranspose().
+ if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
+ aAbs = aErrorRange;
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.maRange.Contains(aAbs))
+ {
+ // Referenced range has been entirely overwritten.
+ aRes.mbValueChanged = true;
+ }
+
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ bool b1, b2;
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ {
+ // More than one sheet referenced => has to have
+ // both 3D flags.
+ b1 = b2 = true;
+ }
+ else
+ {
+ // Keep given 3D flag even for relative sheet
+ // reference to same sheet.
+ // Absolute sheet reference => set 3D flag.
+ // Reference to another sheet => set 3D flag.
+ b1 = rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel() || rNewPos.Tab() != aAbs.aStart.Tab();
+ b2 = rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel() || rNewPos.Tab() != aAbs.aEnd.Tab();
+ // End part has 3D flag => start part must have it too.
+ if (b2)
+ b1 = true;
+ // End part sheet reference is identical to start
+ // part sheet reference and end part sheet
+ // reference was not explicitly given => clear end
+ // part 3D flag.
+ if (b1 && b2 && rRef.Ref1.IsTabRel() == rRef.Ref2.IsTabRel() && !rRef.Ref2.IsFlag3D())
+ b2 = false;
+ }
+ rRef.Ref1.SetFlag3D(b1);
+ rRef.Ref2.SetFlag3D(b2);
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+
+ return aRes;
+}
+
+void ScTokenArray::MoveReferenceColReorder(
+ const ScAddress& rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColRowReorderMapType& rColMap )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.Tab() == nTab && nRow1 <= aAbs.Row() && aAbs.Row() <= nRow2)
+ {
+ // Inside reordered row range.
+ sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.Col());
+ if (it != rColMap.end())
+ {
+ // This column is reordered.
+ SCCOL nNewCol = it->second;
+ aAbs.SetCol(nNewCol);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ // Must be a single-sheet reference.
+ break;
+
+ if (aAbs.aStart.Col() != aAbs.aEnd.Col())
+ // Whole range must fit in a single column.
+ break;
+
+ if (aAbs.aStart.Tab() == nTab && nRow1 <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= nRow2)
+ {
+ // Inside reordered row range.
+ sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.aStart.Col());
+ if (it != rColMap.end())
+ {
+ // This column is reordered.
+ SCCOL nNewCol = it->second;
+ aAbs.aStart.SetCol(nNewCol);
+ aAbs.aEnd.SetCol(nNewCol);
+ rRef.SetRange(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::MoveReferenceRowReorder( const ScAddress& rPos, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, const sc::ColRowReorderMapType& rRowMap )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.Tab() == nTab && nCol1 <= aAbs.Col() && aAbs.Col() <= nCol2)
+ {
+ // Inside reordered column range.
+ sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.Row());
+ if (it != rRowMap.end())
+ {
+ // This column is reordered.
+ SCROW nNewRow = it->second;
+ aAbs.SetRow(nNewRow);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+
+ if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+ // Must be a single-sheet reference.
+ break;
+
+ if (aAbs.aStart.Row() != aAbs.aEnd.Row())
+ // Whole range must fit in a single row.
+ break;
+
+ if (aAbs.aStart.Tab() == nTab && nCol1 <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= nCol2)
+ {
+ // Inside reordered column range.
+ sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.aStart.Row());
+ if (it != rRowMap.end())
+ {
+ // This row is reordered.
+ SCROW nNewRow = it->second;
+ aAbs.aStart.SetRow(nNewRow);
+ aAbs.aEnd.SetRow(nNewRow);
+ rRef.SetRange(*mxSheetLimits, aAbs, rPos);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+namespace {
+
+bool adjustSingleRefInName(
+ ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos,
+ ScComplexRefData* pEndOfComplex )
+{
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.Tab() < rCxt.maRange.aStart.Tab() || rCxt.maRange.aEnd.Tab() < aAbs.Tab())
+ {
+ // This references a sheet that has not shifted. Don't change it.
+ return false;
+ }
+
+ if (!rCxt.maRange.Contains(rRef.toAbs(rCxt.mrDoc, rPos)))
+ return false;
+
+ bool bChanged = false;
+
+ if (rCxt.mnColDelta && !rRef.IsColRel())
+ {
+ // Adjust absolute column reference.
+ if (rCxt.maRange.aStart.Col() <= rRef.Col() && rRef.Col() <= rCxt.maRange.aEnd.Col())
+ {
+ if (pEndOfComplex)
+ {
+ if (pEndOfComplex->IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos))
+ bChanged = true;
+ }
+ else
+ {
+ rRef.IncCol(rCxt.mnColDelta);
+ bChanged = true;
+ }
+ }
+ }
+
+ if (rCxt.mnRowDelta && !rRef.IsRowRel())
+ {
+ // Adjust absolute row reference.
+ if (rCxt.maRange.aStart.Row() <= rRef.Row() && rRef.Row() <= rCxt.maRange.aEnd.Row())
+ {
+ if (pEndOfComplex)
+ {
+ if (pEndOfComplex->IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos))
+ bChanged = true;
+ }
+ else
+ {
+ rRef.IncRow(rCxt.mnRowDelta);
+ bChanged = true;
+ }
+ }
+ }
+
+ if (!rRef.IsTabRel() && rCxt.mnTabDelta)
+ {
+ // Sheet range has already been checked above.
+ rRef.IncTab(rCxt.mnTabDelta);
+ bChanged = true;
+ }
+
+ return bChanged;
+}
+
+bool adjustDoubleRefInName(
+ ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
+{
+ bool bRefChanged = false;
+ if (rCxt.mrDoc.IsExpandRefs())
+ {
+ if (rCxt.mnRowDelta > 0 && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel())
+ {
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+ // Expand only if at least two rows tall.
+ if (aAbs.aStart.Row() < aAbs.aEnd.Row())
+ {
+ // Check and see if we should expand the range at the top.
+ ScRange aSelectedRange = getSelectedRange(rCxt);
+ if (aSelectedRange.Intersects(aAbs))
+ {
+ // Selection intersects the referenced range. Only expand the
+ // bottom position.
+ rRef.IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos);
+ return true;
+ }
+ }
+ }
+ if (rCxt.mnColDelta > 0 && !rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel())
+ {
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+ // Expand only if at least two columns wide.
+ if (aAbs.aStart.Col() < aAbs.aEnd.Col())
+ {
+ // Check and see if we should expand the range at the left.
+ ScRange aSelectedRange = getSelectedRange(rCxt);
+ if (aSelectedRange.Intersects(aAbs))
+ {
+ // Selection intersects the referenced range. Only expand the
+ // right position.
+ rRef.IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos);
+ return true;
+ }
+ }
+ }
+ }
+
+ if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ || (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
+ {
+ sc::RefUpdateContext aCxt( rCxt.mrDoc);
+ // We only need a few parameters of RefUpdateContext.
+ aCxt.maRange = rCxt.maRange;
+ aCxt.mnColDelta = rCxt.mnColDelta;
+ aCxt.mnRowDelta = rCxt.mnRowDelta;
+ aCxt.mnTabDelta = rCxt.mnTabDelta;
+
+ // References to entire col/row are not to be adjusted in the other axis.
+ if (aCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ aCxt.mnRowDelta = 0;
+ if (aCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ aCxt.mnColDelta = 0;
+ if (!aCxt.mnColDelta && !aCxt.mnRowDelta && !aCxt.mnTabDelta)
+ // early bailout
+ return bRefChanged;
+
+ // Ref2 before Ref1 for sticky ends.
+ if (adjustSingleRefInName(rRef.Ref2, aCxt, rPos, &rRef))
+ bRefChanged = true;
+
+ if (adjustSingleRefInName(rRef.Ref1, aCxt, rPos, nullptr))
+ bRefChanged = true;
+ }
+ else
+ {
+ // Ref2 before Ref1 for sticky ends.
+ if (adjustSingleRefInName(rRef.Ref2, rCxt, rPos, &rRef))
+ bRefChanged = true;
+
+ if (adjustSingleRefInName(rRef.Ref1, rCxt, rPos, nullptr))
+ bRefChanged = true;
+ }
+
+ return bRefChanged;
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceInName(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
+{
+ if (rCxt.meMode == URM_MOVE)
+ return AdjustReferenceInMovedName(rCxt, rPos);
+
+ sc::RefUpdateResult aRes;
+
+ if (rCxt.meMode == URM_COPY)
+ // Copying cells does not modify named expressions.
+ return aRes;
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rCxt.mnRowDelta < 0)
+ {
+ // row(s) deleted.
+
+ if (rRef.IsRowRel())
+ // Don't modify relative references in names.
+ break;
+
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.Col())
+ // column of the reference is not in the deleted column range.
+ break;
+
+ if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
+ // wrong tables
+ break;
+
+ const SCROW nDelStartRow = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
+ const SCROW nDelEndRow = nDelStartRow - rCxt.mnRowDelta - 1;
+
+ if (nDelStartRow <= aAbs.Row() && aAbs.Row() <= nDelEndRow)
+ {
+ // This reference is deleted.
+ rRef.SetRowDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ }
+ else if (rCxt.mnColDelta < 0)
+ {
+ // column(s) deleted.
+
+ if (rRef.IsColRel())
+ // Don't modify relative references in names.
+ break;
+
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.Row())
+ // row of the reference is not in the deleted row range.
+ break;
+
+ if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
+ // wrong tables
+ break;
+
+ const SCCOL nDelStartCol = rCxt.maRange.aStart.Col() + rCxt.mnColDelta;
+ const SCCOL nDelEndCol = nDelStartCol - rCxt.mnColDelta - 1;
+
+ if (nDelStartCol <= aAbs.Col() && aAbs.Col() <= nDelEndCol)
+ {
+ // This reference is deleted.
+ rRef.SetColDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+ }
+
+ if (adjustSingleRefInName(rRef, rCxt, rPos, nullptr))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ if (aAbs.aStart.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.aEnd.Tab() < rCxt.maRange.aStart.Tab())
+ // Sheet references not affected.
+ break;
+
+ if (rCxt.maRange.Contains(aAbs))
+ {
+ // This range is entirely within the shifted region.
+ if (adjustDoubleRefInName(rRef, rCxt, rPos))
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.mnRowDelta < 0)
+ {
+ // row(s) deleted.
+
+ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
+ // Rows of entire columns are not affected.
+ break;
+
+ if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
+ // Don't modify relative references in names.
+ break;
+
+ if (aAbs.aStart.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.aEnd.Col())
+ // column range of the reference is not entirely in the deleted column range.
+ break;
+
+ ScRange aDeleted = rCxt.maRange;
+ aDeleted.aStart.IncRow(rCxt.mnRowDelta);
+ aDeleted.aEnd.SetRow(aDeleted.aStart.Row()-rCxt.mnRowDelta-1);
+
+ if (aAbs.aEnd.Row() < aDeleted.aStart.Row() || aDeleted.aEnd.Row() < aAbs.aStart.Row())
+ // reference range doesn't intersect with the deleted range.
+ break;
+
+ if (aDeleted.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= aDeleted.aEnd.Row())
+ {
+ // This reference is entirely deleted.
+ rRef.Ref1.SetRowDeleted(true);
+ rRef.Ref2.SetRowDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+
+ if (aAbs.aStart.Row() < aDeleted.aStart.Row())
+ {
+ if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
+ {
+ if (aDeleted.aEnd.Row() < aAbs.aEnd.Row())
+ // Deleted in the middle. Make the reference shorter.
+ rRef.Ref2.IncRow(rCxt.mnRowDelta);
+ else
+ // Deleted at tail end. Cut off the lower part.
+ rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1);
+ }
+ }
+ else
+ {
+ // Deleted at the top. Cut the top off and shift up.
+ rRef.Ref1.SetAbsRow(aDeleted.aEnd.Row()+1);
+ rRef.Ref1.IncRow(rCxt.mnRowDelta);
+ if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
+ rRef.Ref2.IncRow(rCxt.mnRowDelta);
+ }
+
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.mnColDelta < 0)
+ {
+ // column(s) deleted.
+
+ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
+ // Rows of entire rows are not affected.
+ break;
+
+ if (rRef.Ref1.IsColRel() || rRef.Ref2.IsColRel())
+ // Don't modify relative references in names.
+ break;
+
+ if (aAbs.aStart.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.aEnd.Row())
+ // row range of the reference is not entirely in the deleted row range.
+ break;
+
+ ScRange aDeleted = rCxt.maRange;
+ aDeleted.aStart.IncCol(rCxt.mnColDelta);
+ aDeleted.aEnd.SetCol(aDeleted.aStart.Col()-rCxt.mnColDelta-1);
+
+ if (aAbs.aEnd.Col() < aDeleted.aStart.Col() || aDeleted.aEnd.Col() < aAbs.aStart.Col())
+ // reference range doesn't intersect with the deleted range.
+ break;
+
+ if (aDeleted.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= aDeleted.aEnd.Col())
+ {
+ // This reference is entirely deleted.
+ rRef.Ref1.SetColDeleted(true);
+ rRef.Ref2.SetColDeleted(true);
+ aRes.mbReferenceModified = true;
+ break;
+ }
+
+ if (aAbs.aStart.Col() < aDeleted.aStart.Col())
+ {
+ if (!aAbs.IsEndColSticky(rCxt.mrDoc))
+ {
+ if (aDeleted.aEnd.Col() < aAbs.aEnd.Col())
+ // Deleted in the middle. Make the reference shorter.
+ rRef.Ref2.IncCol(rCxt.mnColDelta);
+ else
+ // Deleted at tail end. Cut off the right part.
+ rRef.Ref2.SetAbsCol(aDeleted.aStart.Col()-1);
+ }
+ }
+ else
+ {
+ // Deleted at the left. Cut the left off and shift left.
+ rRef.Ref1.SetAbsCol(aDeleted.aEnd.Col()+1);
+ rRef.Ref1.IncCol(rCxt.mnColDelta);
+ if (!aAbs.IsEndColSticky(rCxt.mrDoc))
+ rRef.Ref2.IncCol(rCxt.mnColDelta);
+ }
+
+ aRes.mbReferenceModified = true;
+ }
+ else if (rCxt.maRange.Intersects(aAbs))
+ {
+ if (rCxt.mnColDelta && rCxt.maRange.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= rCxt.maRange.aEnd.Row())
+ {
+ if (adjustDoubleRefInName(rRef, rCxt, rPos))
+ aRes.mbReferenceModified = true;
+ }
+ if (rCxt.mnRowDelta && rCxt.maRange.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= rCxt.maRange.aEnd.Col())
+ {
+ if (adjustDoubleRefInName(rRef, rCxt, rPos))
+ aRes.mbReferenceModified = true;
+ }
+ }
+ else if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
+ {
+ // Check if we could expand range reference by the bottom
+ // edge. For named expressions, we only expand absolute
+ // references. Reference must be at least two rows
+ // tall.
+ if (!rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel() &&
+ aAbs.aStart.Row() < aAbs.aEnd.Row() &&
+ aAbs.aEnd.Row()+1 == rCxt.maRange.aStart.Row())
+ {
+ // Expand by the bottom edge.
+ rRef.Ref2.IncRow(rCxt.mnRowDelta);
+ aRes.mbReferenceModified = true;
+ }
+ }
+ else if (rCxt.mnColDelta > 0 && rCxt.mrDoc.IsExpandRefs())
+ {
+ // Check if we could expand range reference by the right
+ // edge. For named expressions, we only expand absolute
+ // references. Reference must be at least two
+ // columns wide.
+ if (!rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel() &&
+ aAbs.aStart.Col() < aAbs.aEnd.Col() &&
+ aAbs.aEnd.Col()+1 == rCxt.maRange.aStart.Col())
+ {
+ // Expand by the right edge.
+ rRef.Ref2.IncCol(rCxt.mnColDelta);
+ aRes.mbReferenceModified = true;
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ return aRes;
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceInMovedName( const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
+{
+ // When moving, the range is the destination range.
+ ScRange aOldRange = rCxt.maRange;
+ ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
+ if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+
+ // In a named expression, we'll move the reference only when the reference
+ // is entirely absolute.
+
+ sc::RefUpdateResult aRes;
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsColRel() || rRef.IsRowRel() || rRef.IsTabRel())
+ continue;
+
+ ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
+ aAbs = aErrorPos;
+ aRes.mbReferenceModified = true;
+ }
+
+ rRef.SetAddress(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() || rRef.Ref1.IsTabRel() ||
+ rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() || rRef.Ref2.IsTabRel())
+ continue;
+
+ ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
+ break;
+
+ if (aOldRange.Contains(aAbs))
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
+ aAbs = aErrorRange;
+ aRes.mbReferenceModified = true;
+ }
+
+ rRef.SetRange(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ return aRes;
+}
+
+namespace {
+
+bool adjustSingleRefOnDeletedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
+ if (nDelPos <= aAbs.Tab() && aAbs.Tab() < nDelPos + nSheets)
+ {
+ rRef.SetTabDeleted(true);
+ return true;
+ }
+
+ if (nDelPos < aAbs.Tab())
+ {
+ // Reference sheet needs to be adjusted.
+ aAbs.IncTab(-1*nSheets);
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+ else if (rOldPos.Tab() != rNewPos.Tab())
+ {
+ // Cell itself has moved.
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+
+ return false;
+}
+
+bool adjustSingleRefOnInsertedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nInsPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
+ if (nInsPos <= aAbs.Tab())
+ {
+ // Reference sheet needs to be adjusted.
+ aAbs.IncTab(nSheets);
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+ else if (rOldPos.Tab() != rNewPos.Tab())
+ {
+ // Cell itself has moved.
+ rRef.SetAddress(rLimits, aAbs, rNewPos);
+ return true;
+ }
+
+ return false;
+}
+
+bool adjustDoubleRefOnDeleteTab(const ScSheetLimits& rLimits, ScComplexRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos)
+{
+ ScSingleRefData& rRef1 = rRef.Ref1;
+ ScSingleRefData& rRef2 = rRef.Ref2;
+ ScAddress aStartPos = rRef1.toAbs(rLimits, rOldPos);
+ ScAddress aEndPos = rRef2.toAbs(rLimits, rOldPos);
+ bool bMoreThanOneTab = aStartPos.Tab() != aEndPos.Tab();
+ bool bModified = false;
+ if (bMoreThanOneTab && aStartPos.Tab() == nDelPos && nDelPos + nSheets <= aEndPos.Tab())
+ {
+ if (rRef1.IsTabRel() && aStartPos.Tab() < rOldPos.Tab())
+ {
+ rRef1.IncTab(nSheets);
+ bModified = true;
+ }
+ }
+ else
+ {
+ bModified = adjustSingleRefOnDeletedTab(rLimits, rRef1, nDelPos, nSheets, rOldPos, rNewPos);
+ }
+
+ if (bMoreThanOneTab && aEndPos.Tab() == nDelPos && aStartPos.Tab() <= nDelPos - nSheets)
+ {
+ if (!rRef2.IsTabRel() || rOldPos.Tab() < aEndPos.Tab())
+ {
+ rRef2.IncTab(-nSheets);
+ bModified = true;
+ }
+ }
+ else
+ {
+ bModified |= adjustSingleRefOnDeletedTab(rLimits, rRef2, nDelPos, nSheets, rOldPos, rNewPos);
+ }
+ return bModified;
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnDeletedTab( const sc::RefUpdateDeleteTabContext& rCxt, const ScAddress& rOldPos )
+{
+ sc::RefUpdateResult aRes;
+ ScAddress aNewPos = rOldPos;
+ ScRangeUpdater::UpdateDeleteTab( aNewPos, rCxt);
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (adjustSingleRefOnDeletedTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ aRes.mbReferenceModified |= adjustDoubleRefOnDeleteTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ if (rCxt.mnDeletePos <= nOldTab)
+ {
+ aRes.mbNameModified = true;
+ if (rCxt.mnDeletePos + rCxt.mnSheets <= nOldTab)
+ (*pp)->SetSheet( nOldTab - rCxt.mnSheets);
+ else
+ // Would point to a deleted sheet. Invalidate.
+ (*pp)->SetSheet( SCTAB_MAX);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+ return aRes;
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnInsertedTab( const sc::RefUpdateInsertTabContext& rCxt, const ScAddress& rOldPos )
+{
+ sc::RefUpdateResult aRes;
+ ScAddress aNewPos = rOldPos;
+ if (rCxt.mnInsertPos <= rOldPos.Tab())
+ aNewPos.IncTab(rCxt.mnSheets);
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref1, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref2, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
+ aRes.mbReferenceModified = true;
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ if (rCxt.mnInsertPos <= nOldTab)
+ {
+ aRes.mbNameModified = true;
+ (*pp)->SetSheet( nOldTab + rCxt.mnSheets);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+ return aRes;
+}
+
+namespace {
+
+bool adjustTabOnMove( ScAddress& rPos, const sc::RefUpdateMoveTabContext& rCxt )
+{
+ SCTAB nNewTab = rCxt.getNewTab(rPos.Tab());
+ if (nNewTab == rPos.Tab())
+ return false;
+
+ rPos.SetTab(nNewTab);
+ return true;
+}
+
+}
+
+sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMovedTab( const sc::RefUpdateMoveTabContext& rCxt, const ScAddress& rOldPos )
+{
+ sc::RefUpdateResult aRes;
+ if (rCxt.mnOldPos == rCxt.mnNewPos)
+ return aRes;
+
+ ScAddress aNewPos = rOldPos;
+ if (adjustTabOnMove(aNewPos, rCxt))
+ {
+ aRes.mbReferenceModified = true;
+ aRes.mbValueChanged = true;
+ aRes.mnTab = aNewPos.Tab(); // this sets the new tab position used when deleting
+ }
+
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (adjustTabOnMove(aAbs, rCxt))
+ aRes.mbReferenceModified = true;
+ rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (adjustTabOnMove(aAbs.aStart, rCxt))
+ aRes.mbReferenceModified = true;
+ if (adjustTabOnMove(aAbs.aEnd, rCxt))
+ aRes.mbReferenceModified = true;
+ rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+
+ // For ocTableRef p is the inner token of *pp, so have a separate
+ // condition here.
+ if ((*pp)->GetType() == svIndex)
+ {
+ switch ((*pp)->GetOpCode())
+ {
+ case ocName:
+ {
+ SCTAB nOldTab = (*pp)->GetSheet();
+ if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
+ aRes.mbNameModified = true;
+ SCTAB nNewTab = rCxt.getNewTab( nOldTab);
+ if (nNewTab != nOldTab)
+ {
+ aRes.mbNameModified = true;
+ (*pp)->SetSheet( nNewTab);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ if (isDBDataModified(rCxt.mrDoc, **pp))
+ aRes.mbNameModified = true;
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+
+ return aRes;
+}
+
+void ScTokenArray::AdjustReferenceOnMovedOrigin( const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustReferenceOnMovedOriginIfOtherSheet( const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ bool bAdjust = false;
+ switch (p->GetType())
+ {
+ case svExternalSingleRef:
+ bAdjust = true; // always
+ [[fallthrough]];
+ case svSingleRef:
+ {
+ ScSingleRefData& rRef = *p->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (!bAdjust)
+ bAdjust = (aAbs.Tab() != rOldPos.Tab());
+ if (bAdjust)
+ rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ case svExternalDoubleRef:
+ bAdjust = true; // always
+ [[fallthrough]];
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+ if (!bAdjust)
+ bAdjust = (rOldPos.Tab() < aAbs.aStart.Tab() || aAbs.aEnd.Tab() < rOldPos.Tab());
+ if (bAdjust)
+ rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::AdjustReferenceOnCopy( const ScAddress& rNewPos )
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svDoubleRef:
+ {
+ ScComplexRefData& rRef = *p->GetDoubleRef();
+ rRef.PutInOrder( rNewPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+namespace {
+
+void clearTabDeletedFlag( const ScSheetLimits& rLimits, ScSingleRefData& rRef, const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
+{
+ if (!rRef.IsTabDeleted())
+ return;
+
+ ScAddress aAbs = rRef.toAbs(rLimits, rPos);
+ if (nStartTab <= aAbs.Tab() && aAbs.Tab() <= nEndTab)
+ rRef.SetTabDeleted(false);
+}
+
+}
+
+void ScTokenArray::ClearTabDeleted( const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
+{
+ if (nEndTab < nStartTab)
+ return;
+
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ switch ((*p)->GetType())
+ {
+ case svSingleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScSingleRefData& rRef = *pToken->GetSingleRef();
+ clearTabDeletedFlag(*mxSheetLimits, rRef, rPos, nStartTab, nEndTab);
+ }
+ break;
+ case svDoubleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScComplexRefData& rRef = *pToken->GetDoubleRef();
+ clearTabDeletedFlag(*mxSheetLimits, rRef.Ref1, rPos, nStartTab, nEndTab);
+ clearTabDeletedFlag(*mxSheetLimits, rRef.Ref2, rPos, nStartTab, nEndTab);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+namespace {
+
+void checkBounds(
+ const ScSheetLimits& rLimits,
+ const ScAddress& rPos, SCROW nGroupLen, const ScRange& rCheckRange,
+ const ScSingleRefData& rRef, std::vector<SCROW>& rBounds, const ScRange* pDeletedRange )
+{
+ if (!rRef.IsRowRel())
+ return;
+
+ ScRange aAbs(rRef.toAbs(rLimits, rPos));
+ aAbs.aEnd.IncRow(nGroupLen-1);
+ if (!rCheckRange.Intersects(aAbs) && (!pDeletedRange || !pDeletedRange->Intersects(aAbs)))
+ return;
+
+ // Get the boundary row positions.
+ if (aAbs.aEnd.Row() < rCheckRange.aStart.Row() && (!pDeletedRange || aAbs.aEnd.Row() < pDeletedRange->aStart.Row()))
+ // No intersections.
+ return;
+
+ // rCheckRange may be a virtual non-existent row being shifted in.
+ if (aAbs.aStart.Row() <= rCheckRange.aStart.Row() && rCheckRange.aStart.Row() < rLimits.GetMaxRowCount())
+ {
+ // +-+ <---- top
+ // | |
+ // +--+-+--+ <---- boundary row position
+ // | | | |
+ // | |
+ // +-------+
+
+ // Add offset from the reference top to the cell position.
+ SCROW nOffset = rCheckRange.aStart.Row() - aAbs.aStart.Row();
+ rBounds.push_back(rPos.Row()+nOffset);
+ }
+ // Same for deleted range.
+ if (pDeletedRange && aAbs.aStart.Row() <= pDeletedRange->aStart.Row())
+ {
+ SCROW nOffset = pDeletedRange->aStart.Row() - aAbs.aStart.Row();
+ SCROW nRow = rPos.Row() + nOffset;
+ // Unlike for rCheckRange, for pDeletedRange nRow can be anywhere>=0.
+ if (rLimits.ValidRow(nRow))
+ rBounds.push_back(nRow);
+ }
+
+ if (aAbs.aEnd.Row() >= rCheckRange.aEnd.Row())
+ {
+ // only check for end range
+
+ // +-------+
+ // | |
+ // | | | |
+ // +--+-+--+ <---- boundary row position
+ // | |
+ // +-+
+
+ // Ditto.
+ SCROW nOffset = rCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
+ rBounds.push_back(rPos.Row()+nOffset);
+ }
+ // Same for deleted range.
+ if (pDeletedRange && aAbs.aEnd.Row() >= pDeletedRange->aEnd.Row())
+ {
+ SCROW nOffset = pDeletedRange->aEnd.Row() + 1 - aAbs.aStart.Row();
+ SCROW nRow = rPos.Row() + nOffset;
+ // Unlike for rCheckRange, for pDeletedRange nRow can be ~anywhere.
+ if (rLimits.ValidRow(nRow))
+ rBounds.push_back(nRow);
+ }
+}
+
+void checkBounds(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen,
+ const ScSingleRefData& rRef, std::vector<SCROW>& rBounds)
+{
+ if (!rRef.IsRowRel())
+ return;
+
+ ScRange aDeletedRange( ScAddress::UNINITIALIZED );
+ const ScRange* pDeletedRange = nullptr;
+
+ ScRange aCheckRange = rCxt.maRange;
+ if (rCxt.meMode == URM_MOVE)
+ {
+ // Check bounds against the old range prior to the move.
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
+ {
+ assert(!"can't move");
+ }
+
+ // Check bounds also against the range moved into.
+ pDeletedRange = &rCxt.maRange;
+ }
+ else if (rCxt.meMode == URM_INSDEL &&
+ ((rCxt.mnColDelta < 0 && rCxt.maRange.aStart.Col() > 0) ||
+ (rCxt.mnRowDelta < 0 && rCxt.maRange.aStart.Row() > 0)))
+ {
+ // Check bounds also against deleted range where cells are shifted
+ // into and references need to be invalidated.
+ aDeletedRange = getSelectedRange( rCxt);
+ pDeletedRange = &aDeletedRange;
+ }
+
+ checkBounds(rCxt.mrDoc.GetSheetLimits(), rPos, nGroupLen, aCheckRange, rRef, rBounds, pDeletedRange);
+}
+
+}
+
+void ScTokenArray::CheckRelativeReferenceBounds(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ checkBounds(rCxt, rPos, nGroupLen, *p->GetSingleRef(), rBounds);
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ checkBounds(rCxt, rPos, nGroupLen, rRef.Ref1, rBounds);
+ checkBounds(rCxt, rPos, nGroupLen, rRef.Ref2, rBounds);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::CheckRelativeReferenceBounds(
+ const ScAddress& rPos, SCROW nGroupLen, const ScRange& rRange, std::vector<SCROW>& rBounds ) const
+{
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
+ FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef, rBounds, nullptr);
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref1, rBounds, nullptr);
+ checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref2, rBounds, nullptr);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+void ScTokenArray::CheckExpandReferenceBounds(
+ const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
+{
+ const SCROW nInsRow = rCxt.maRange.aStart.Row();
+ TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
+ for (size_t j=0; j<2; ++j)
+ {
+ FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
+ const FormulaToken* const * pEnd = aPtrs.maPointerRange[j].mpStop;
+ for (; pp != pEnd; ++pp)
+ {
+ const FormulaToken* p = aPtrs.getHandledToken(j,pp);
+ if (!p)
+ continue;
+
+ switch (p->GetType())
+ {
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ bool bStartRowRelative = rRef.Ref1.IsRowRel();
+ bool bEndRowRelative = rRef.Ref2.IsRowRel();
+
+ // For absolute references nothing needs to be done, they stay
+ // the same for all and if to be expanded the group will be
+ // adjusted later.
+ if (!bStartRowRelative && !bEndRowRelative)
+ break; // switch
+
+ ScRange aAbsStart(rRef.toAbs(*mxSheetLimits, rPos));
+ ScAddress aPos(rPos);
+ aPos.IncRow(nGroupLen);
+ ScRange aAbsEnd(rRef.toAbs(*mxSheetLimits, aPos));
+ // References must be at least two rows to be expandable.
+ if ((aAbsStart.aEnd.Row() - aAbsStart.aStart.Row() < 1) &&
+ (aAbsEnd.aEnd.Row() - aAbsEnd.aStart.Row() < 1))
+ break; // switch
+
+ // Only need to process if an edge may be touching the
+ // insertion row anywhere within the run of the group.
+ if (!((aAbsStart.aStart.Row() <= nInsRow && nInsRow <= aAbsEnd.aStart.Row()) ||
+ (aAbsStart.aEnd.Row() <= nInsRow && nInsRow <= aAbsEnd.aEnd.Row())))
+ break; // switch
+
+ SCROW nStartRow = aAbsStart.aStart.Row();
+ SCROW nEndRow = aAbsStart.aEnd.Row();
+ // Position on first relevant range.
+ SCROW nOffset = 0;
+ if (nEndRow + 1 < nInsRow)
+ {
+ if (bEndRowRelative)
+ {
+ nOffset = nInsRow - nEndRow - 1;
+ nEndRow += nOffset;
+ if (bStartRowRelative)
+ nStartRow += nOffset;
+ }
+ else // bStartRowRelative==true
+ {
+ nOffset = nInsRow - nStartRow;
+ nStartRow += nOffset;
+ // Start is overtaking End, swap.
+ bStartRowRelative = false;
+ bEndRowRelative = true;
+ }
+ }
+ for (SCROW i = nOffset; i < nGroupLen; ++i)
+ {
+ bool bSplit = (nStartRow == nInsRow || nEndRow + 1 == nInsRow);
+ if (bSplit)
+ rBounds.push_back( rPos.Row() + i);
+
+ if (bEndRowRelative)
+ ++nEndRow;
+ if (bStartRowRelative)
+ {
+ ++nStartRow;
+ if (!bEndRowRelative && nStartRow == nEndRow)
+ {
+ // Start is overtaking End, swap.
+ bStartRowRelative = false;
+ bEndRowRelative = true;
+ }
+ }
+ if (nInsRow < nStartRow || (!bStartRowRelative && nInsRow <= nEndRow))
+ {
+ if (bSplit && (++i < nGroupLen))
+ rBounds.push_back( rPos.Row() + i);
+ break; // for, out of range now
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+}
+
+namespace {
+
+void appendDouble( const sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, double fVal )
+{
+ if (rCxt.mxOpCodeMap->isEnglish())
+ {
+ rtl::math::doubleToUStringBuffer(
+ rBuf, fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
+ }
+ else
+ {
+ SvtSysLocale aSysLocale;
+ rtl::math::doubleToUStringBuffer(
+ rBuf, fVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ aSysLocale.GetLocaleData().getNumDecimalSep()[0], true);
+ }
+}
+
+void appendString( OUStringBuffer& rBuf, const OUString& rStr )
+{
+ rBuf.append('"');
+ rBuf.append(rStr.replaceAll("\"", "\"\""));
+ rBuf.append('"');
+}
+
+void appendTokenByType( ScSheetLimits& rLimits, sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, const FormulaToken& rToken,
+ const ScAddress& rPos, bool bFromRangeName )
+{
+ if (rToken.IsExternalRef())
+ {
+ size_t nFileId = rToken.GetIndex();
+ OUString aTabName = rToken.GetString().getString();
+ if (nFileId >= rCxt.maExternalFileNames.size())
+ // out of bound
+ return;
+
+ OUString aFileName = rCxt.maExternalFileNames[nFileId];
+
+ switch (rToken.GetType())
+ {
+ case svExternalName:
+ rBuf.append(rCxt.mpRefConv->makeExternalNameStr(nFileId, aFileName, aTabName));
+ break;
+ case svExternalSingleRef:
+ rCxt.mpRefConv->makeExternalRefStr(
+ rLimits, rBuf, rPos, nFileId, aFileName, aTabName, *rToken.GetSingleRef());
+ break;
+ case svExternalDoubleRef:
+ {
+ sc::TokenStringContext::IndexNamesMapType::const_iterator it =
+ rCxt.maExternalCachedTabNames.find(nFileId);
+
+ if (it == rCxt.maExternalCachedTabNames.end())
+ return;
+
+ rCxt.mpRefConv->makeExternalRefStr(
+ rLimits, rBuf, rPos, nFileId, aFileName, it->second, aTabName,
+ *rToken.GetDoubleRef());
+ }
+ break;
+ default:
+ // warning, not error, otherwise we may end up with a never
+ // ending message box loop if this was the cursor cell to be redrawn.
+ OSL_FAIL("appendTokenByType: unknown type of ocExternalRef");
+ }
+ return;
+ }
+
+ OpCode eOp = rToken.GetOpCode();
+ switch (rToken.GetType())
+ {
+ case svDouble:
+ appendDouble(rCxt, rBuf, rToken.GetDouble());
+ break;
+ case svString:
+ {
+ OUString aStr = rToken.GetString().getString();
+ if (eOp == ocBad || eOp == ocStringXML)
+ {
+ rBuf.append(aStr);
+ return;
+ }
+
+ appendString(rBuf, aStr);
+ }
+ break;
+ case svSingleRef:
+ {
+ if (rCxt.mpRefConv)
+ {
+ const ScSingleRefData& rRef = *rToken.GetSingleRef();
+ ScComplexRefData aRef;
+ aRef.Ref1 = rRef;
+ aRef.Ref2 = rRef;
+ rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, aRef, true,
+ bFromRangeName);
+ }
+ else
+ rBuf.append(rCxt.maErrRef);
+ }
+ break;
+ case svDoubleRef:
+ {
+ if (rCxt.mpRefConv)
+ {
+ const ScComplexRefData& rRef = *rToken.GetDoubleRef();
+ rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, rRef, false,
+ bFromRangeName);
+ }
+ else
+ rBuf.append(rCxt.maErrRef);
+ }
+ break;
+ case svMatrix:
+ {
+ const ScMatrix* pMat = rToken.GetMatrix();
+ if (!pMat)
+ return;
+
+ size_t nC, nMaxC, nR, nMaxR;
+ pMat->GetDimensions(nMaxC, nMaxR);
+
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayOpen));
+ for (nR = 0 ; nR < nMaxR ; ++nR)
+ {
+ if (nR > 0)
+ {
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayRowSep));
+ }
+
+ for (nC = 0 ; nC < nMaxC ; ++nC)
+ {
+ if (nC > 0)
+ {
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayColSep));
+ }
+
+ if (pMat->IsValue(nC, nR))
+ {
+ if (pMat->IsBoolean(nC, nR))
+ {
+ bool bVal = pMat->GetDouble(nC, nR) != 0.0;
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(bVal ? ocTrue : ocFalse));
+ }
+ else
+ {
+ FormulaError nErr = pMat->GetError(nC, nR);
+ if (nErr != FormulaError::NONE)
+ rBuf.append(ScGlobal::GetErrorString(nErr));
+ else
+ appendDouble(rCxt, rBuf, pMat->GetDouble(nC, nR));
+ }
+ }
+ else if (pMat->IsEmpty(nC, nR))
+ {
+ // Skip it.
+ }
+ else if (pMat->IsStringOrEmpty(nC, nR))
+ appendString(rBuf, pMat->GetString(nC, nR).getString());
+ }
+ }
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayClose));
+ }
+ break;
+ case svIndex:
+ {
+ typedef sc::TokenStringContext::IndexNameMapType NameType;
+
+ sal_uInt16 nIndex = rToken.GetIndex();
+ switch (eOp)
+ {
+ case ocName:
+ {
+ SCTAB nTab = rToken.GetSheet();
+ if (nTab < 0)
+ {
+ // global named range
+ NameType::const_iterator it = rCxt.maGlobalRangeNames.find(nIndex);
+ if (it == rCxt.maGlobalRangeNames.end())
+ {
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ break;
+ }
+
+ rBuf.append(it->second);
+ }
+ else
+ {
+ // sheet-local named range
+ if (nTab != rPos.Tab())
+ {
+ // On other sheet.
+ OUString aName;
+ if (o3tl::make_unsigned(nTab) < rCxt.maTabNames.size())
+ aName = rCxt.maTabNames[nTab];
+ if (!aName.isEmpty())
+ {
+ ScCompiler::CheckTabQuotes( aName, rCxt.mpRefConv->meConv);
+ rBuf.append( aName);
+ }
+ else
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ rBuf.append( rCxt.mpRefConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR));
+ }
+
+ sc::TokenStringContext::TabIndexMapType::const_iterator itTab = rCxt.maSheetRangeNames.find(nTab);
+ if (itTab == rCxt.maSheetRangeNames.end())
+ {
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ break;
+ }
+
+ const NameType& rNames = itTab->second;
+ NameType::const_iterator it = rNames.find(nIndex);
+ if (it == rNames.end())
+ {
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ break;
+ }
+
+ rBuf.append(it->second);
+ }
+ }
+ break;
+ case ocDBArea:
+ case ocTableRef:
+ {
+ NameType::const_iterator it = rCxt.maNamedDBs.find(nIndex);
+ if (it != rCxt.maNamedDBs.end())
+ rBuf.append(it->second);
+ }
+ break;
+ default:
+ rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
+ }
+ }
+ break;
+ case svExternal:
+ {
+ // mapped or translated name of AddIns
+ OUString aAddIn = rToken.GetExternal();
+ bool bMapped = rCxt.mxOpCodeMap->isPODF(); // ODF 1.1 directly uses programmatical name
+ if (!bMapped && rCxt.mxOpCodeMap->hasExternals())
+ {
+ const ExternalHashMap& rExtMap = rCxt.mxOpCodeMap->getReverseExternalHashMap();
+ ExternalHashMap::const_iterator it = rExtMap.find(aAddIn);
+ if (it != rExtMap.end())
+ {
+ aAddIn = it->second;
+ bMapped = true;
+ }
+ }
+
+ if (!bMapped && !rCxt.mxOpCodeMap->isEnglish())
+ ScGlobal::GetAddInCollection()->LocalizeString(aAddIn);
+
+ rBuf.append(aAddIn);
+ }
+ break;
+ case svError:
+ {
+ FormulaError nErr = rToken.GetError();
+ OpCode eOpErr;
+ switch (nErr)
+ {
+ break;
+ case FormulaError::DivisionByZero:
+ eOpErr = ocErrDivZero;
+ break;
+ case FormulaError::NoValue:
+ eOpErr = ocErrValue;
+ break;
+ case FormulaError::NoRef:
+ eOpErr = ocErrRef;
+ break;
+ case FormulaError::NoName:
+ eOpErr = ocErrName;
+ break;
+ case FormulaError::IllegalFPOperation:
+ eOpErr = ocErrNum;
+ break;
+ case FormulaError::NotAvailable:
+ eOpErr = ocErrNA;
+ break;
+ case FormulaError::NoCode:
+ default:
+ eOpErr = ocErrNull;
+ }
+ rBuf.append(rCxt.mxOpCodeMap->getSymbol(eOpErr));
+ }
+ break;
+ case svByte:
+ case svJump:
+ case svFAP:
+ case svMissing:
+ case svSep:
+ default:
+ ;
+ }
+}
+
+}
+
+OUString ScTokenArray::CreateString( sc::TokenStringContext& rCxt, const ScAddress& rPos ) const
+{
+ if (!nLen)
+ return OUString();
+
+ OUStringBuffer aBuf;
+
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ const FormulaToken* pToken = *p;
+ OpCode eOp = pToken->GetOpCode();
+ /* FIXME: why does this ignore the count of spaces? */
+ if (eOp == ocSpaces)
+ {
+ // TODO : Handle intersection operator '!!'.
+ aBuf.append(' ');
+ continue;
+ }
+ else if (eOp == ocWhitespace)
+ {
+ aBuf.append( pToken->GetChar());
+ continue;
+ }
+
+ if (eOp < rCxt.mxOpCodeMap->getSymbolCount())
+ aBuf.append(rCxt.mxOpCodeMap->getSymbol(eOp));
+
+ appendTokenByType(*mxSheetLimits, rCxt, aBuf, *pToken, rPos, IsFromRangeName());
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+namespace {
+
+void wrapAddress( ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
+{
+ if (rPos.Col() > nMaxCol)
+ rPos.SetCol(rPos.Col() % (nMaxCol+1));
+ if (rPos.Row() > nMaxRow)
+ rPos.SetRow(rPos.Row() % (nMaxRow+1));
+}
+
+template<typename T> void wrapRange( T& n1, T& n2, T nMax )
+{
+ if (n2 > nMax)
+ {
+ if (n1 == 0)
+ n2 = nMax; // Truncate to full range instead of wrapping to a weird range.
+ else
+ n2 = n2 % (nMax+1);
+ }
+ if (n1 > nMax)
+ n1 = n1 % (nMax+1);
+}
+
+void wrapColRange( ScRange& rRange, SCCOL nMaxCol )
+{
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ wrapRange( nCol1, nCol2, nMaxCol);
+ rRange.aStart.SetCol( nCol1);
+ rRange.aEnd.SetCol( nCol2);
+}
+
+void wrapRowRange( ScRange& rRange, SCROW nMaxRow )
+{
+ SCROW nRow1 = rRange.aStart.Row();
+ SCROW nRow2 = rRange.aEnd.Row();
+ wrapRange( nRow1, nRow2, nMaxRow);
+ rRange.aStart.SetRow( nRow1);
+ rRange.aEnd.SetRow( nRow2);
+}
+
+}
+
+void ScTokenArray::WrapReference( const ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
+{
+ FormulaToken** p = pCode.get();
+ FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+ for (; p != pEnd; ++p)
+ {
+ switch ((*p)->GetType())
+ {
+ case svSingleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScSingleRefData& rRef = *pToken->GetSingleRef();
+ ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ wrapAddress(aAbs, nMaxCol, nMaxRow);
+ rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
+ }
+ break;
+ case svDoubleRef:
+ {
+ formula::FormulaToken* pToken = *p;
+ ScComplexRefData& rRef = *pToken->GetDoubleRef();
+ ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
+ // Entire columns/rows are sticky.
+ if (!rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
+ {
+ wrapColRange( aAbs, nMaxCol);
+ wrapRowRange( aAbs, nMaxRow);
+ }
+ else if (rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
+ wrapColRange( aAbs, nMaxCol);
+ else if (!rRef.IsEntireCol(*mxSheetLimits) && rRef.IsEntireRow(*mxSheetLimits))
+ wrapRowRange( aAbs, nMaxRow);
+ // else nothing if both, column and row, are entire.
+ aAbs.PutInOrder();
+ rRef.SetRange(*mxSheetLimits, aAbs, rPos);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+sal_Int32 ScTokenArray::GetWeight() const
+{
+ sal_Int32 nResult = 0;
+ for (auto i = 0; i < nRPN; ++i)
+ {
+ switch ((*pRPN[i]).GetType())
+ {
+ case svDoubleRef:
+ {
+ const auto pComplexRef = (*pRPN[i]).GetDoubleRef();
+
+ // Number of cells referenced divided by 10.
+ const double nRows = 1 + (pComplexRef->Ref2.Row() - pComplexRef->Ref1.Row());
+ const double nCols = 1 + (pComplexRef->Ref2.Col() - pComplexRef->Ref1.Col());
+ const double nNumCellsTerm = nRows * nCols / 10.0;
+
+ if (nNumCellsTerm + nResult < SAL_MAX_INT32)
+ nResult += nNumCellsTerm;
+ else
+ nResult = SAL_MAX_INT32;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ if (nResult == 0)
+ nResult = 1;
+
+ return nResult;
+}
+
+#if DEBUG_FORMULA_COMPILER
+
+void ScTokenArray::Dump() const
+{
+ cout << "+++ Normal Tokens +++" << endl;
+ for (sal_uInt16 i = 0; i < nLen; ++i)
+ {
+ DumpToken(*pCode[i]);
+ }
+
+ cout << "+++ RPN Tokens +++" << endl;
+ for (sal_uInt16 i = 0; i < nRPN; ++i)
+ {
+ DumpToken(*pRPN[i]);
+ }
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/tokenstringcontext.cxx b/sc/source/core/tool/tokenstringcontext.cxx
new file mode 100644
index 0000000000..5a4430c155
--- /dev/null
+++ b/sc/source/core/tool/tokenstringcontext.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <tokenstringcontext.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <externalrefmgr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+using namespace com::sun::star;
+
+namespace sc {
+
+namespace {
+
+void insertAllNames( TokenStringContext::IndexNameMapType& rMap, const ScRangeName& rNames )
+{
+ for (auto const& it : rNames)
+ {
+ const ScRangeData *const pData = it.second.get();
+ rMap.emplace(pData->GetIndex(), pData->GetName());
+ }
+}
+
+}
+
+TokenStringContext::TokenStringContext( const ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ) :
+ meGram(eGram),
+ mpRefConv(ScCompiler::GetRefConvention(formula::FormulaGrammar::extractRefConvention(eGram)))
+{
+ formula::FormulaCompiler aComp;
+ mxOpCodeMap = aComp.GetOpCodeMap(formula::FormulaGrammar::extractFormulaLanguage(eGram));
+ if (mxOpCodeMap)
+ maErrRef = mxOpCodeMap->getSymbol(ocErrRef);
+ else
+ {
+ assert(!"TokenStringContext - no OpCodeMap?!?");
+ maErrRef = ScResId(STR_NO_REF_TABLE);
+ }
+
+ // Fetch all sheet names.
+ maTabNames = rDoc.GetAllTableNames();
+ {
+ for (auto& rTabName : maTabNames)
+ ScCompiler::CheckTabQuotes(rTabName, formula::FormulaGrammar::extractRefConvention(eGram));
+ }
+
+ // Fetch all named range names.
+ const ScRangeName* pNames = rDoc.GetRangeName();
+ if (pNames)
+ // global names
+ insertAllNames(maGlobalRangeNames, *pNames);
+
+ {
+ ScRangeName::TabNameCopyMap aTabRangeNames;
+ rDoc.GetAllTabRangeNames(aTabRangeNames);
+ for (const auto& [nTab, pSheetNames] : aTabRangeNames)
+ {
+ if (!pSheetNames)
+ continue;
+
+ IndexNameMapType aNames;
+ insertAllNames(aNames, *pSheetNames);
+ maSheetRangeNames.emplace(nTab, aNames);
+ }
+ }
+
+ // Fetch all named database ranges names.
+ const ScDBCollection* pDBs = rDoc.GetDBCollection();
+ if (pDBs)
+ {
+ const ScDBCollection::NamedDBs& rNamedDBs = pDBs->getNamedDBs();
+ for (const auto& rxNamedDB : rNamedDBs)
+ {
+ const ScDBData& rData = *rxNamedDB;
+ maNamedDBs.emplace(rData.GetIndex(), rData.GetName());
+ }
+ }
+
+ // Fetch all relevant bits for external references.
+ if (!rDoc.HasExternalRefManager())
+ return;
+
+ const ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ maExternalFileNames = pRefMgr->getAllCachedExternalFileNames();
+ for (size_t i = 0, n = maExternalFileNames.size(); i < n; ++i)
+ {
+ sal_uInt16 nFileId = static_cast<sal_uInt16>(i);
+ std::vector<OUString> aTabNames;
+ pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
+ if (!aTabNames.empty())
+ maExternalCachedTabNames.emplace(nFileId, aTabNames);
+ }
+}
+
+CompileFormulaContext::CompileFormulaContext( ScDocument& rDoc ) :
+ mrDoc(rDoc), meGram(rDoc.GetGrammar())
+{
+ updateTabNames();
+}
+
+CompileFormulaContext::CompileFormulaContext( ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ) :
+ mrDoc(rDoc), meGram(eGram)
+{
+ updateTabNames();
+}
+
+void CompileFormulaContext::updateTabNames()
+{
+ // Fetch all sheet names.
+ maTabNames = mrDoc.GetAllTableNames();
+ {
+ for (auto& rTabName : maTabNames)
+ ScCompiler::CheckTabQuotes(rTabName, formula::FormulaGrammar::extractRefConvention(meGram));
+ }
+}
+
+void CompileFormulaContext::setGrammar( formula::FormulaGrammar::Grammar eGram )
+{
+ bool bUpdate = (meGram != eGram);
+ meGram = eGram;
+ if (bUpdate)
+ updateTabNames();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/typedstrdata.cxx b/sc/source/core/tool/typedstrdata.cxx
new file mode 100644
index 0000000000..e00c1bc18d
--- /dev/null
+++ b/sc/source/core/tool/typedstrdata.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <typedstrdata.hxx>
+#include <global.hxx>
+
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <utility>
+
+bool ScTypedStrData::LessHiddenRows::operator() (const ScTypedStrData& left, const ScTypedStrData& right) const
+{
+ return left.mbIsHiddenByFilter < right.mbIsHiddenByFilter;
+}
+
+bool ScTypedStrData::LessCaseSensitive::operator() (const ScTypedStrData& left, const ScTypedStrData& right) const
+{
+ if (left.meStrType != right.meStrType)
+ return left.meStrType < right.meStrType;
+
+ if (left.meStrType == Value)
+ {
+ if (left.mfValue == right.mfValue)
+ return left.mbIsHiddenByFilter < right.mbIsHiddenByFilter;
+ return left.mfValue < right.mfValue;
+ }
+
+ if (left.mbIsDate != right.mbIsDate)
+ return left.mbIsDate < right.mbIsDate;
+
+ sal_Int32 nEqual = ScGlobal::GetCaseCollator().compareString(
+ left.maStrValue, right.maStrValue);
+
+ if (!nEqual)
+ return left.mbIsHiddenByFilter < right.mbIsHiddenByFilter;
+
+ return nEqual < 0;
+}
+
+bool ScTypedStrData::LessCaseInsensitive::operator() (const ScTypedStrData& left, const ScTypedStrData& right) const
+{
+ if (left.meStrType != right.meStrType)
+ return left.meStrType < right.meStrType;
+
+ if (left.meStrType == Value)
+ {
+ if (left.mfValue == right.mfValue)
+ return left.mbIsHiddenByFilter < right.mbIsHiddenByFilter;
+ return left.mfValue < right.mfValue;
+ }
+
+ if (left.mbIsDate != right.mbIsDate)
+ return left.mbIsDate < right.mbIsDate;
+
+ sal_Int32 nEqual = ScGlobal::GetCollator().compareString(
+ left.maStrValue, right.maStrValue);
+
+ if (!nEqual)
+ return left.mbIsHiddenByFilter < right.mbIsHiddenByFilter;
+
+ return nEqual < 0;
+}
+
+bool ScTypedStrData::EqualCaseSensitive::operator() (const ScTypedStrData& left, const ScTypedStrData& right) const
+{
+ if (left.meStrType != right.meStrType)
+ return false;
+
+ if (left.meStrType == Value && left.mfRoundedValue != right.mfRoundedValue)
+ return false;
+
+ if (left.mbIsDate != right.mbIsDate )
+ return false;
+
+ return ScGlobal::GetCaseTransliteration().isEqual(left.maStrValue, right.maStrValue);
+}
+
+bool ScTypedStrData::EqualCaseInsensitive::operator() (const ScTypedStrData& left, const ScTypedStrData& right) const
+{
+ if (left.meStrType != right.meStrType)
+ return false;
+
+ if (left.meStrType == Value && left.mfRoundedValue != right.mfRoundedValue)
+ return false;
+
+ if (left.mbIsDate != right.mbIsDate )
+ return false;
+
+ return ScGlobal::GetTransliteration().isEqual(left.maStrValue, right.maStrValue);
+}
+
+bool ScTypedStrData::operator< (const ScTypedStrData& r) const
+{
+ // Case insensitive comparison by default.
+ return LessCaseInsensitive()(*this, r);
+}
+
+ScTypedStrData::ScTypedStrData(
+ const OUString& rStr, double fVal, double fRVal, StringType nType, bool bDate, bool bIsHiddenByFilter ) :
+ maStrValue(rStr),
+ mfValue(fVal),
+ mfRoundedValue(fRVal),
+ meStrType(nType),
+ mbIsDate( bDate ),
+ mbIsHiddenByFilter(bIsHiddenByFilter) {}
+
+FindTypedStrData::FindTypedStrData(ScTypedStrData aVal, bool bCaseSens) :
+ maVal(std::move(aVal)), mbCaseSens(bCaseSens) {}
+
+bool FindTypedStrData::operator() (const ScTypedStrData& r) const
+{
+ if (mbCaseSens)
+ {
+ return ScTypedStrData::EqualCaseSensitive()(maVal, r);
+ }
+ else
+ {
+ return ScTypedStrData::EqualCaseInsensitive()(maVal, r);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/unitconv.cxx b/sc/source/core/tool/unitconv.cxx
new file mode 100644
index 0000000000..1f5337cd81
--- /dev/null
+++ b/sc/source/core/tool/unitconv.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 <com/sun/star/uno/Sequence.hxx>
+
+#include <unitconv.hxx>
+#include <global.hxx>
+#include <optutil.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+const sal_Unicode cDelim = 0x01; // delimiter between From and To
+
+// ScUnitConverterData
+ScUnitConverterData::ScUnitConverterData(
+ std::u16string_view rFromUnit, std::u16string_view rToUnit, double fValue ) :
+ maIndexString(BuildIndexString(rFromUnit, rToUnit)),
+ mfValue(fValue) {}
+
+OUString ScUnitConverterData::BuildIndexString(
+ std::u16string_view rFromUnit, std::u16string_view rToUnit )
+{
+ return rFromUnit + OUStringChar(cDelim) + rToUnit;
+}
+
+// ScUnitConverter
+constexpr OUStringLiteral CFGPATH_UNIT = u"Office.Calc/UnitConversion";
+constexpr OUStringLiteral CFGSTR_UNIT_FROM = u"FromUnit";
+constexpr OUStringLiteral CFGSTR_UNIT_TO = u"ToUnit";
+constexpr OUStringLiteral CFGSTR_UNIT_FACTOR = u"Factor";
+
+ScUnitConverter::ScUnitConverter()
+{
+ // read from configuration - "convert.ini" is no longer used
+ //TODO: config item as member to allow change of values
+
+ ScLinkConfigItem aConfigItem( CFGPATH_UNIT );
+
+ // empty node name -> use the config item's path itself
+ const Sequence<OUString> aNodeNames = aConfigItem.GetNodeNames( "" );
+
+ tools::Long nNodeCount = aNodeNames.getLength();
+ if ( !nNodeCount )
+ return;
+
+ Sequence<OUString> aValNames( nNodeCount * 3 );
+ OUString* pValNameArray = aValNames.getArray();
+ const OUString sSlash('/');
+
+ tools::Long nIndex = 0;
+ for (const OUString& rNode : aNodeNames)
+ {
+ OUString sPrefix = rNode + sSlash;
+
+ pValNameArray[nIndex++] = sPrefix + CFGSTR_UNIT_FROM;
+ pValNameArray[nIndex++] = sPrefix + CFGSTR_UNIT_TO;
+ pValNameArray[nIndex++] = sPrefix + CFGSTR_UNIT_FACTOR;
+ }
+
+ Sequence<Any> aProperties = aConfigItem.GetProperties(aValNames);
+
+ if (aProperties.getLength() != aValNames.getLength())
+ return;
+
+ const Any* pProperties = aProperties.getConstArray();
+
+ OUString sFromUnit;
+ OUString sToUnit;
+ double fFactor = 0;
+
+ nIndex = 0;
+ for (tools::Long i=0; i<nNodeCount; i++)
+ {
+ pProperties[nIndex++] >>= sFromUnit;
+ pProperties[nIndex++] >>= sToUnit;
+ pProperties[nIndex++] >>= fFactor;
+
+ ScUnitConverterData aNew(sFromUnit, sToUnit, fFactor);
+ OUString const aIndex = aNew.GetIndexString();
+ maData.insert(std::make_pair(aIndex, aNew));
+ }
+}
+
+ScUnitConverter::~ScUnitConverter() {}
+
+bool ScUnitConverter::GetValue(
+ double& fValue, std::u16string_view rFromUnit, std::u16string_view rToUnit ) const
+{
+ OUString aIndex = ScUnitConverterData::BuildIndexString(rFromUnit, rToUnit);
+ MapType::const_iterator it = maData.find(aIndex);
+ if (it == maData.end())
+ {
+ fValue = 1.0;
+ return false;
+ }
+
+ fValue = it->second.GetValue();
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/userlist.cxx b/sc/source/core/tool/userlist.cxx
new file mode 100644
index 0000000000..b1030eca62
--- /dev/null
+++ b/sc/source/core/tool/userlist.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 <sal/config.h>
+
+#include <unotools/charclass.hxx>
+
+#include <global.hxx>
+#include <userlist.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <com/sun/star/i18n/Calendar2.hpp>
+
+#include <algorithm>
+#include <utility>
+
+ScUserListData::SubStr::SubStr(OUString&& aReal) :
+ maReal(std::move(aReal)), maUpper(ScGlobal::getCharClass().uppercase(maReal)) {}
+
+void ScUserListData::InitTokens()
+{
+ maSubStrings.clear();
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aSub = aStr.getToken(0, ScGlobal::cListDelimiter, nIndex);
+ if (!aSub.isEmpty())
+ maSubStrings.emplace_back(std::move(aSub));
+ } while (nIndex >= 0);
+}
+
+ScUserListData::ScUserListData(OUString _aStr) :
+ aStr(std::move(_aStr))
+{
+ InitTokens();
+}
+
+void ScUserListData::SetString( const OUString& rStr )
+{
+ aStr = rStr;
+ InitTokens();
+}
+
+bool ScUserListData::GetSubIndex(const OUString& rSubStr, sal_uInt16& rIndex, bool& bMatchCase) const
+{
+ // First, case sensitive search.
+ auto itr = ::std::find_if(maSubStrings.begin(), maSubStrings.end(),
+ [&rSubStr](const SubStr& item) { return item.maReal == rSubStr; });
+ if (itr != maSubStrings.end())
+ {
+ rIndex = ::std::distance(maSubStrings.begin(), itr);
+ bMatchCase = true;
+ return true;
+ }
+
+ // When that fails, do a case insensitive search.
+ bMatchCase = false;
+ OUString aUpStr = ScGlobal::getCharClass().uppercase(rSubStr);
+ itr = ::std::find_if(maSubStrings.begin(), maSubStrings.end(),
+ [&aUpStr](const SubStr& item) { return item.maUpper == aUpStr; });
+ if (itr != maSubStrings.end())
+ {
+ rIndex = ::std::distance(maSubStrings.begin(), itr);
+ return true;
+ }
+ return false;
+}
+
+OUString ScUserListData::GetSubStr(sal_uInt16 nIndex) const
+{
+ if (nIndex < maSubStrings.size())
+ return maSubStrings[nIndex].maReal;
+ else
+ return OUString();
+}
+
+sal_Int32 ScUserListData::Compare(const OUString& rSubStr1, const OUString& rSubStr2) const
+{
+ sal_uInt16 nIndex1, nIndex2;
+ bool bMatchCase;
+ bool bFound1 = GetSubIndex(rSubStr1, nIndex1, bMatchCase);
+ bool bFound2 = GetSubIndex(rSubStr2, nIndex2, bMatchCase);
+ if (bFound1)
+ {
+ if (bFound2)
+ {
+ if (nIndex1 < nIndex2)
+ return -1;
+ else if (nIndex1 > nIndex2)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return -1;
+ }
+ else if (bFound2)
+ return 1;
+ else
+ return ScGlobal::GetCaseTransliteration().compareString( rSubStr1, rSubStr2 );
+}
+
+sal_Int32 ScUserListData::ICompare(const OUString& rSubStr1, const OUString& rSubStr2) const
+{
+ sal_uInt16 nIndex1, nIndex2;
+ bool bMatchCase;
+ bool bFound1 = GetSubIndex(rSubStr1, nIndex1, bMatchCase);
+ bool bFound2 = GetSubIndex(rSubStr2, nIndex2, bMatchCase);
+ if (bFound1)
+ {
+ if (bFound2)
+ {
+ if (nIndex1 < nIndex2)
+ return -1;
+ else if (nIndex1 > nIndex2)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return -1;
+ }
+ else if (bFound2)
+ return 1;
+ else
+ return ScGlobal::GetTransliteration().compareString( rSubStr1, rSubStr2 );
+}
+
+ScUserList::ScUserList(bool initDefault)
+{
+ if (initDefault)
+ AddDefaults();
+}
+
+void ScUserList::AddDefaults()
+{
+ sal_Unicode cDelimiter = ScGlobal::cListDelimiter;
+ for (const auto& rCalendar : ScGlobal::getLocaleData().getAllCalendars())
+ {
+ if (const auto& xCal = rCalendar.Days; xCal.hasElements())
+ {
+ OUStringBuffer aDayShortBuf(32), aDayLongBuf(64);
+ sal_Int32 i;
+ sal_Int32 nLen = xCal.getLength();
+ sal_Int16 nStart = sal::static_int_cast<sal_Int16>(nLen);
+ while (nStart > 0)
+ {
+ if (xCal[--nStart].ID == rCalendar.StartOfWeek)
+ break;
+ }
+ sal_Int16 nLast = sal::static_int_cast<sal_Int16>( (nStart + nLen - 1) % nLen );
+ for (i = nStart; i != nLast; i = (i+1) % nLen)
+ {
+ aDayShortBuf.append(xCal[i].AbbrevName);
+ aDayShortBuf.append(cDelimiter);
+ aDayLongBuf.append(xCal[i].FullName);
+ aDayLongBuf.append(cDelimiter);
+ }
+ aDayShortBuf.append(xCal[i].AbbrevName);
+ aDayLongBuf.append(xCal[i].FullName);
+
+ OUString aDayShort = aDayShortBuf.makeStringAndClear();
+ OUString aDayLong = aDayLongBuf.makeStringAndClear();
+
+ if ( !HasEntry( aDayShort ) )
+ emplace_back(aDayShort);
+ if ( !HasEntry( aDayLong ) )
+ emplace_back(aDayLong);
+ }
+
+ if (const auto& xCal = rCalendar.Months; xCal.hasElements())
+ {
+ OUStringBuffer aMonthShortBuf(128), aMonthLongBuf(128);
+ sal_Int32 i;
+ sal_Int32 nLen = xCal.getLength() - 1;
+ for (i = 0; i < nLen; i++)
+ {
+ aMonthShortBuf.append(xCal[i].AbbrevName);
+ aMonthShortBuf.append(cDelimiter);
+ aMonthLongBuf.append(xCal[i].FullName);
+ aMonthLongBuf.append(cDelimiter);
+ }
+ aMonthShortBuf.append(xCal[i].AbbrevName);
+ aMonthLongBuf.append(xCal[i].FullName);
+
+ OUString aMonthShort = aMonthShortBuf.makeStringAndClear();
+ OUString aMonthLong = aMonthLongBuf.makeStringAndClear();
+
+ if ( !HasEntry( aMonthShort ) )
+ emplace_back(aMonthShort);
+ if ( !HasEntry( aMonthLong ) )
+ emplace_back(aMonthLong);
+ }
+ }
+}
+
+const ScUserListData* ScUserList::GetData(const OUString& rSubStr) const
+{
+ const ScUserListData* pFirstCaseInsensitive = nullptr;
+ sal_uInt16 nIndex;
+ bool bMatchCase = false;
+
+ for (const auto& rxItem : maData)
+ {
+ if (rxItem.GetSubIndex(rSubStr, nIndex, bMatchCase))
+ {
+ if (bMatchCase)
+ return &rxItem;
+ if (!pFirstCaseInsensitive)
+ pFirstCaseInsensitive = &rxItem;
+ }
+ }
+
+ return pFirstCaseInsensitive;
+}
+
+bool ScUserList::operator==( const ScUserList& r ) const
+{
+ return std::equal(maData.begin(), maData.end(), r.maData.begin(), r.maData.end(),
+ [](const ScUserListData& lhs, const ScUserListData& rhs) {
+ return lhs.GetString() == rhs.GetString();
+ });
+}
+
+bool ScUserList::HasEntry( std::u16string_view rStr ) const
+{
+ return ::std::any_of(maData.begin(), maData.end(),
+ [&] (ScUserListData const& pData)
+ { return pData.GetString() == rStr; } );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/viewopti.cxx b/sc/source/core/tool/viewopti.cxx
new file mode 100644
index 0000000000..39e2e6e0e7
--- /dev/null
+++ b/sc/source/core/tool/viewopti.cxx
@@ -0,0 +1,593 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <svtools/colorcfg.hxx>
+
+#include <global.hxx>
+#include <viewopti.hxx>
+#include <sc.hrc>
+#include <scmod.hxx>
+#include <miscuno.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+
+
+void ScGridOptions::SetDefaults()
+{
+ *this = ScGridOptions();
+
+ // grid defaults differ now between the apps
+ // therefore, enter here in its own right (all in 1/100mm)
+
+ if ( ScOptionsUtil::IsMetricSystem() )
+ {
+ nFldDrawX = 1000; // 1cm
+ nFldDrawY = 1000;
+ }
+ else
+ {
+ nFldDrawX = 1270; // 0,5"
+ nFldDrawY = 1270;
+ }
+ nFldDivisionX = 1;
+ nFldDivisionY = 1;
+}
+
+bool ScGridOptions::operator==( const ScGridOptions& rCpy ) const
+{
+ return ( nFldDrawX == rCpy.nFldDrawX
+ && nFldDivisionX == rCpy.nFldDivisionX
+ && nFldDrawY == rCpy.nFldDrawY
+ && nFldDivisionY == rCpy.nFldDivisionY
+ && bUseGridsnap == rCpy.bUseGridsnap
+ && bSynchronize == rCpy.bSynchronize
+ && bGridVisible == rCpy.bGridVisible
+ && bEqualGrid == rCpy.bEqualGrid );
+}
+
+
+ScViewOptions::ScViewOptions()
+{
+ SetDefaults();
+}
+
+ScViewOptions::ScViewOptions( const ScViewOptions& rCpy )
+{
+ *this = rCpy;
+}
+
+ScViewOptions::~ScViewOptions()
+{
+}
+
+void ScViewOptions::SetDefaults()
+{
+ aOptArr[ VOPT_FORMULAS ] = false;
+ aOptArr[ VOPT_SYNTAX ] = false;
+ aOptArr[ VOPT_HELPLINES ] = false;
+ aOptArr[ VOPT_GRID_ONTOP ] = false;
+ aOptArr[ VOPT_NOTES ] = true;
+ aOptArr[ VOPT_FORMULAS_MARKS ] = false;
+ aOptArr[ VOPT_NULLVALS ] = true;
+ aOptArr[ VOPT_VSCROLL ] = true;
+ aOptArr[ VOPT_HSCROLL ] = true;
+ aOptArr[ VOPT_TABCONTROLS ] = true;
+ aOptArr[ VOPT_OUTLINER ] = true;
+ aOptArr[ VOPT_HEADER ] = true;
+ aOptArr[ VOPT_GRID ] = true;
+ aOptArr[ VOPT_ANCHOR ] = true;
+ aOptArr[ VOPT_PAGEBREAKS ] = true;
+ aOptArr[ VOPT_SUMMARY ] = true;
+ aOptArr[ VOPT_COPY_SHEET ] = false;
+ aOptArr[ VOPT_THEMEDCURSOR ] = false;
+
+ aModeArr[VOBJ_TYPE_OLE ] = VOBJ_MODE_SHOW;
+ aModeArr[VOBJ_TYPE_CHART] = VOBJ_MODE_SHOW;
+ aModeArr[VOBJ_TYPE_DRAW ] = VOBJ_MODE_SHOW;
+
+ aGridCol = svtools::ColorConfig().GetColorValue( svtools::CALCGRID ).nColor;
+
+ aDocCol = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+
+ aGridOpt.SetDefaults();
+}
+
+Color const & ScViewOptions::GetGridColor( OUString* pStrName ) const
+{
+ if ( pStrName )
+ *pStrName = aGridColName;
+
+ return aGridCol;
+}
+
+ScViewOptions& ScViewOptions::operator=(const ScViewOptions& rCpy) = default;
+
+bool ScViewOptions::operator==( const ScViewOptions& rOpt ) const
+{
+ bool bEqual = true;
+ sal_uInt16 i;
+
+ for ( i=0; i<MAX_OPT && bEqual; i++ ) bEqual = (aOptArr [i] == rOpt.aOptArr[i]);
+ for ( i=0; i<MAX_TYPE && bEqual; i++ ) bEqual = (aModeArr[i] == rOpt.aModeArr[i]);
+
+ bEqual = bEqual && (aGridCol == rOpt.aGridCol);
+ bEqual = bEqual && (aGridColName == rOpt.aGridColName);
+ bEqual = bEqual && (aGridOpt == rOpt.aGridOpt);
+ bEqual = bEqual && (sColorSchemeName == rOpt.sColorSchemeName);
+ bEqual = bEqual && (aDocCol == rOpt.aDocCol);
+
+ return bEqual;
+}
+
+std::unique_ptr<SvxGridItem> ScViewOptions::CreateGridItem() const
+{
+ std::unique_ptr<SvxGridItem> pItem(new SvxGridItem( SID_ATTR_GRID_OPTIONS ));
+
+ pItem->SetFieldDrawX ( aGridOpt.GetFieldDrawX() );
+ pItem->SetFieldDivisionX ( aGridOpt.GetFieldDivisionX() );
+ pItem->SetFieldDrawY ( aGridOpt.GetFieldDrawY() );
+ pItem->SetFieldDivisionY ( aGridOpt.GetFieldDivisionY() );
+ pItem->SetUseGridSnap ( aGridOpt.GetUseGridSnap() );
+ pItem->SetSynchronize ( aGridOpt.GetSynchronize() );
+ pItem->SetGridVisible ( aGridOpt.GetGridVisible() );
+ pItem->SetEqualGrid ( aGridOpt.GetEqualGrid() );
+
+ return pItem;
+}
+
+// ScTpViewItem - data for the ViewOptions TabPage
+
+ScTpViewItem::ScTpViewItem( const ScViewOptions& rOpt )
+ : SfxPoolItem ( SID_SCVIEWOPTIONS ),
+ theOptions ( rOpt )
+{
+}
+
+ScTpViewItem::~ScTpViewItem()
+{
+}
+
+bool ScTpViewItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScTpViewItem& rPItem = static_cast<const ScTpViewItem&>(rItem);
+
+ return ( theOptions == rPItem.theOptions );
+}
+
+ScTpViewItem* ScTpViewItem::Clone( SfxItemPool * ) const
+{
+ return new ScTpViewItem( *this );
+}
+
+// Config Item containing view options
+
+constexpr OUStringLiteral CFGPATH_LAYOUT = u"Office.Calc/Layout";
+
+#define SCLAYOUTOPT_GRIDLINES 0
+#define SCLAYOUTOPT_GRIDCOLOR 1
+#define SCLAYOUTOPT_PAGEBREAK 2
+#define SCLAYOUTOPT_GUIDE 3
+#define SCLAYOUTOPT_COLROWHDR 4
+#define SCLAYOUTOPT_HORISCROLL 5
+#define SCLAYOUTOPT_VERTSCROLL 6
+#define SCLAYOUTOPT_SHEETTAB 7
+#define SCLAYOUTOPT_OUTLINE 8
+#define SCLAYOUTOPT_GRID_ONCOLOR 9
+#define SCLAYOUTOPT_SUMMARY 10
+#define SCLAYOUTOPT_THEMEDCURSOR 11
+
+constexpr OUStringLiteral CFGPATH_DISPLAY = u"Office.Calc/Content/Display";
+
+#define SCDISPLAYOPT_FORMULA 0
+#define SCDISPLAYOPT_ZEROVALUE 1
+#define SCDISPLAYOPT_NOTETAG 2
+#define SCDISPLAYOPT_FORMULAMARK 3
+#define SCDISPLAYOPT_VALUEHI 4
+#define SCDISPLAYOPT_ANCHOR 5
+#define SCDISPLAYOPT_OBJECTGRA 6
+#define SCDISPLAYOPT_CHART 7
+#define SCDISPLAYOPT_DRAWING 8
+
+constexpr OUStringLiteral CFGPATH_GRID = u"Office.Calc/Grid";
+
+#define SCGRIDOPT_RESOLU_X 0
+#define SCGRIDOPT_RESOLU_Y 1
+#define SCGRIDOPT_SUBDIV_X 2
+#define SCGRIDOPT_SUBDIV_Y 3
+#define SCGRIDOPT_SNAPTOGRID 4
+#define SCGRIDOPT_SYNCHRON 5
+#define SCGRIDOPT_VISIBLE 6
+#define SCGRIDOPT_SIZETOGRID 7
+
+Sequence<OUString> ScViewCfg::GetLayoutPropertyNames()
+{
+ return {"Line/GridLine", // SCLAYOUTOPT_GRIDLINES
+ "Line/GridLineColor", // SCLAYOUTOPT_GRIDCOLOR
+ "Line/PageBreak", // SCLAYOUTOPT_PAGEBREAK
+ "Line/Guide", // SCLAYOUTOPT_GUIDE
+ "Window/ColumnRowHeader", // SCLAYOUTOPT_COLROWHDR
+ "Window/HorizontalScroll", // SCLAYOUTOPT_HORISCROLL
+ "Window/VerticalScroll", // SCLAYOUTOPT_VERTSCROLL
+ "Window/SheetTab", // SCLAYOUTOPT_SHEETTAB
+ "Window/OutlineSymbol", // SCLAYOUTOPT_OUTLINE
+ "Line/GridOnColoredCells", // SCLAYOUTOPT_GRID_ONCOLOR;
+ "Window/SearchSummary", // SCLAYOUTOPT_SUMMARY
+ "Window/ThemedCursor"}; // SCLAYOUTOPT_THEMEDCURSOR
+}
+
+Sequence<OUString> ScViewCfg::GetDisplayPropertyNames()
+{
+ return {"Formula", // SCDISPLAYOPT_FORMULA
+ "ZeroValue", // SCDISPLAYOPT_ZEROVALUE
+ "NoteTag", // SCDISPLAYOPT_NOTETAG
+ "FormulaMark", // SCDISPLAYOPT_FORMULAMARK
+ "ValueHighlighting", // SCDISPLAYOPT_VALUEHI
+ "Anchor", // SCDISPLAYOPT_ANCHOR
+ "ObjectGraphic", // SCDISPLAYOPT_OBJECTGRA
+ "Chart", // SCDISPLAYOPT_CHART
+ "DrawingObject"}; // SCDISPLAYOPT_DRAWING;
+}
+
+Sequence<OUString> ScViewCfg::GetGridPropertyNames()
+{
+ const bool bIsMetric = ScOptionsUtil::IsMetricSystem();
+
+ return {(bIsMetric ? OUString("Resolution/XAxis/Metric")
+ : OUString("Resolution/XAxis/NonMetric")), // SCGRIDOPT_RESOLU_X
+ (bIsMetric ? OUString("Resolution/YAxis/Metric")
+ : OUString("Resolution/YAxis/NonMetric")), // SCGRIDOPT_RESOLU_Y
+ "Subdivision/XAxis", // SCGRIDOPT_SUBDIV_X
+ "Subdivision/YAxis", // SCGRIDOPT_SUBDIV_Y
+ "Option/SnapToGrid", // SCGRIDOPT_SNAPTOGRID
+ "Option/Synchronize", // SCGRIDOPT_SYNCHRON
+ "Option/VisibleGrid", // SCGRIDOPT_VISIBLE
+ "Option/SizeToGrid"}; // SCGRIDOPT_SIZETOGRID;
+}
+
+ScViewCfg::ScViewCfg() :
+ aLayoutItem( CFGPATH_LAYOUT ),
+ aDisplayItem( CFGPATH_DISPLAY ),
+ aGridItem( CFGPATH_GRID )
+{
+ sal_Int32 nIntVal = 0;
+
+ Sequence<OUString> aNames = GetLayoutPropertyNames();
+ Sequence<Any> aValues = aLayoutItem.GetProperties(aNames);
+ aLayoutItem.EnableNotification(aNames);
+ const Any* pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() == aNames.getLength())
+ {
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ OSL_ENSURE(pValues[nProp].hasValue(), "property value missing");
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case SCLAYOUTOPT_GRIDCOLOR:
+ {
+ Color aColor;
+ if ( pValues[nProp] >>= aColor )
+ SetGridColor( aColor, OUString() );
+ break;
+ }
+ case SCLAYOUTOPT_GRIDLINES:
+ SetOption( VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_GRID_ONCOLOR:
+ SetOption( VOPT_GRID_ONTOP, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_PAGEBREAK:
+ SetOption( VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_GUIDE:
+ SetOption( VOPT_HELPLINES, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_COLROWHDR:
+ SetOption( VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_HORISCROLL:
+ SetOption( VOPT_HSCROLL, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_VERTSCROLL:
+ SetOption( VOPT_VSCROLL, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_SHEETTAB:
+ SetOption( VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_OUTLINE:
+ SetOption( VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_SUMMARY:
+ SetOption( VOPT_SUMMARY, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCLAYOUTOPT_THEMEDCURSOR:
+ SetOption( VOPT_THEMEDCURSOR, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ }
+ }
+ }
+ }
+ aLayoutItem.SetCommitLink( LINK( this, ScViewCfg, LayoutCommitHdl ) );
+
+ aNames = GetDisplayPropertyNames();
+ aValues = aDisplayItem.GetProperties(aNames);
+ aDisplayItem.EnableNotification(aNames);
+ pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() == aNames.getLength())
+ {
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ OSL_ENSURE(pValues[nProp].hasValue(), "property value missing");
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case SCDISPLAYOPT_FORMULA:
+ SetOption( VOPT_FORMULAS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCDISPLAYOPT_ZEROVALUE:
+ SetOption( VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCDISPLAYOPT_NOTETAG:
+ SetOption( VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCDISPLAYOPT_FORMULAMARK:
+ SetOption( VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCDISPLAYOPT_VALUEHI:
+ SetOption( VOPT_SYNTAX, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCDISPLAYOPT_ANCHOR:
+ SetOption( VOPT_ANCHOR, ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCDISPLAYOPT_OBJECTGRA:
+ if ( pValues[nProp] >>= nIntVal )
+ {
+ //#i80528# adapt to new range eventually
+ if(sal_Int32(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int32(VOBJ_MODE_SHOW);
+
+ SetObjMode( VOBJ_TYPE_OLE, static_cast<ScVObjMode>(nIntVal));
+ }
+ break;
+ case SCDISPLAYOPT_CHART:
+ if ( pValues[nProp] >>= nIntVal )
+ {
+ //#i80528# adapt to new range eventually
+ if(sal_Int32(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int32(VOBJ_MODE_SHOW);
+
+ SetObjMode( VOBJ_TYPE_CHART, static_cast<ScVObjMode>(nIntVal));
+ }
+ break;
+ case SCDISPLAYOPT_DRAWING:
+ if ( pValues[nProp] >>= nIntVal )
+ {
+ //#i80528# adapt to new range eventually
+ if(sal_Int32(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int32(VOBJ_MODE_SHOW);
+
+ SetObjMode( VOBJ_TYPE_DRAW, static_cast<ScVObjMode>(nIntVal));
+ }
+ break;
+ }
+ }
+ }
+ }
+ aDisplayItem.SetCommitLink( LINK( this, ScViewCfg, DisplayCommitHdl ) );
+
+ ScGridOptions aGrid = GetGridOptions(); //TODO: initialization necessary?
+ aNames = GetGridPropertyNames();
+ aValues = aGridItem.GetProperties(aNames);
+ aGridItem.EnableNotification(aNames);
+ pValues = aValues.getConstArray();
+ OSL_ENSURE(aValues.getLength() == aNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() == aNames.getLength())
+ {
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ OSL_ENSURE(pValues[nProp].hasValue(), "property value missing");
+ if(pValues[nProp].hasValue())
+ {
+ switch(nProp)
+ {
+ case SCGRIDOPT_RESOLU_X:
+ if (pValues[nProp] >>= nIntVal) aGrid.SetFieldDrawX( nIntVal );
+ break;
+ case SCGRIDOPT_RESOLU_Y:
+ if (pValues[nProp] >>= nIntVal) aGrid.SetFieldDrawY( nIntVal );
+ break;
+ case SCGRIDOPT_SUBDIV_X:
+ if (pValues[nProp] >>= nIntVal) aGrid.SetFieldDivisionX( nIntVal );
+ break;
+ case SCGRIDOPT_SUBDIV_Y:
+ if (pValues[nProp] >>= nIntVal) aGrid.SetFieldDivisionY( nIntVal );
+ break;
+ case SCGRIDOPT_SNAPTOGRID:
+ aGrid.SetUseGridSnap( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCGRIDOPT_SYNCHRON:
+ aGrid.SetSynchronize( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCGRIDOPT_VISIBLE:
+ aGrid.SetGridVisible( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ case SCGRIDOPT_SIZETOGRID:
+ aGrid.SetEqualGrid( ScUnoHelpFunctions::GetBoolFromAny( pValues[nProp] ) );
+ break;
+ }
+ }
+ }
+ }
+ SetGridOptions( aGrid );
+ aGridItem.SetCommitLink( LINK( this, ScViewCfg, GridCommitHdl ) );
+}
+
+IMPL_LINK_NOARG(ScViewCfg, LayoutCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetLayoutPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCLAYOUTOPT_GRIDCOLOR:
+ pValues[nProp] <<= GetGridColor();
+ break;
+ case SCLAYOUTOPT_GRIDLINES:
+ pValues[nProp] <<= GetOption( VOPT_GRID );
+ break;
+ case SCLAYOUTOPT_GRID_ONCOLOR:
+ pValues[nProp] <<= GetOption( VOPT_GRID_ONTOP );
+ break;
+ case SCLAYOUTOPT_PAGEBREAK:
+ pValues[nProp] <<= GetOption( VOPT_PAGEBREAKS );
+ break;
+ case SCLAYOUTOPT_GUIDE:
+ pValues[nProp] <<= GetOption( VOPT_HELPLINES );
+ break;
+ case SCLAYOUTOPT_COLROWHDR:
+ pValues[nProp] <<= GetOption( VOPT_HEADER );
+ break;
+ case SCLAYOUTOPT_HORISCROLL:
+ pValues[nProp] <<= GetOption( VOPT_HSCROLL );
+ break;
+ case SCLAYOUTOPT_VERTSCROLL:
+ pValues[nProp] <<= GetOption( VOPT_VSCROLL );
+ break;
+ case SCLAYOUTOPT_SHEETTAB:
+ pValues[nProp] <<= GetOption( VOPT_TABCONTROLS );
+ break;
+ case SCLAYOUTOPT_OUTLINE:
+ pValues[nProp] <<= GetOption( VOPT_OUTLINER );
+ break;
+ case SCLAYOUTOPT_SUMMARY:
+ pValues[nProp] <<= GetOption( VOPT_SUMMARY );
+ break;
+ case SCLAYOUTOPT_THEMEDCURSOR:
+ pValues[nProp] <<= GetOption( VOPT_THEMEDCURSOR );
+ break;
+ }
+ }
+ aLayoutItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScViewCfg, DisplayCommitHdl, ScLinkConfigItem&, void)
+{
+ Sequence<OUString> aNames = GetDisplayPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCDISPLAYOPT_FORMULA:
+ pValues[nProp] <<= GetOption( VOPT_FORMULAS );
+ break;
+ case SCDISPLAYOPT_ZEROVALUE:
+ pValues[nProp] <<= GetOption( VOPT_NULLVALS );
+ break;
+ case SCDISPLAYOPT_NOTETAG:
+ pValues[nProp] <<= GetOption( VOPT_NOTES );
+ break;
+ case SCDISPLAYOPT_FORMULAMARK:
+ pValues[nProp] <<= GetOption( VOPT_FORMULAS_MARKS );
+ break;
+ case SCDISPLAYOPT_VALUEHI:
+ pValues[nProp] <<= GetOption( VOPT_SYNTAX );
+ break;
+ case SCDISPLAYOPT_ANCHOR:
+ pValues[nProp] <<= GetOption( VOPT_ANCHOR );
+ break;
+ case SCDISPLAYOPT_OBJECTGRA:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetObjMode( VOBJ_TYPE_OLE ));
+ break;
+ case SCDISPLAYOPT_CHART:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetObjMode( VOBJ_TYPE_CHART ));
+ break;
+ case SCDISPLAYOPT_DRAWING:
+ pValues[nProp] <<= static_cast<sal_Int32>(GetObjMode( VOBJ_TYPE_DRAW ));
+ break;
+ }
+ }
+ aDisplayItem.PutProperties(aNames, aValues);
+}
+
+IMPL_LINK_NOARG(ScViewCfg, GridCommitHdl, ScLinkConfigItem&, void)
+{
+ const ScGridOptions& rGrid = GetGridOptions();
+
+ Sequence<OUString> aNames = GetGridPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ switch(nProp)
+ {
+ case SCGRIDOPT_RESOLU_X:
+ pValues[nProp] <<= static_cast<sal_Int32>(rGrid.GetFieldDrawX());
+ break;
+ case SCGRIDOPT_RESOLU_Y:
+ pValues[nProp] <<= static_cast<sal_Int32>(rGrid.GetFieldDrawY());
+ break;
+ case SCGRIDOPT_SUBDIV_X:
+ pValues[nProp] <<= static_cast<sal_Int32>(rGrid.GetFieldDivisionX());
+ break;
+ case SCGRIDOPT_SUBDIV_Y:
+ pValues[nProp] <<= static_cast<sal_Int32>(rGrid.GetFieldDivisionY());
+ break;
+ case SCGRIDOPT_SNAPTOGRID:
+ pValues[nProp] <<= rGrid.GetUseGridSnap();
+ break;
+ case SCGRIDOPT_SYNCHRON:
+ pValues[nProp] <<= rGrid.GetSynchronize();
+ break;
+ case SCGRIDOPT_VISIBLE:
+ pValues[nProp] <<= rGrid.GetGridVisible();
+ break;
+ case SCGRIDOPT_SIZETOGRID:
+ pValues[nProp] <<= rGrid.GetEqualGrid();
+ break;
+ }
+ }
+ aGridItem.PutProperties(aNames, aValues);
+}
+
+void ScViewCfg::SetOptions( const ScViewOptions& rNew )
+{
+ *static_cast<ScViewOptions*>(this) = rNew;
+ aLayoutItem.SetModified();
+ aDisplayItem.SetModified();
+ aGridItem.SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/webservicelink.cxx b/sc/source/core/tool/webservicelink.cxx
new file mode 100644
index 0000000000..b61907471e
--- /dev/null
+++ b/sc/source/core/tool/webservicelink.cxx
@@ -0,0 +1,99 @@
+/* -*- 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 <comphelper/processfactory.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <utility>
+#include <webservicelink.hxx>
+#include <brdcst.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+
+ScWebServiceLink::ScWebServiceLink(ScDocument* pD, OUString _aURL)
+ : ::sfx2::SvBaseLink(SfxLinkUpdateMode::ALWAYS, SotClipboardFormatId::STRING)
+ , pDoc(pD)
+ , aURL(std::move(_aURL))
+ , bHasResult(false)
+{
+}
+
+ScWebServiceLink::~ScWebServiceLink() {}
+
+sfx2::SvBaseLink::UpdateResult ScWebServiceLink::DataChanged(const OUString&, const css::uno::Any&)
+{
+ aResult.clear();
+ bHasResult = false;
+
+ css::uno::Reference<css::ucb::XSimpleFileAccess3> xFileAccess
+ = css::ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext());
+ if (!xFileAccess.is())
+ return ERROR_GENERAL;
+
+ css::uno::Reference<css::io::XInputStream> xStream;
+ try
+ {
+ xStream = xFileAccess->openFileRead(aURL);
+ }
+ catch (...)
+ {
+ // don't let any exceptions pass
+ return ERROR_GENERAL;
+ }
+ if (!xStream.is())
+ return ERROR_GENERAL;
+
+ const sal_Int32 BUF_LEN = 8000;
+ css::uno::Sequence<sal_Int8> buffer(BUF_LEN);
+ OStringBuffer aBuffer(64000);
+
+ sal_Int32 nRead = 0;
+ while ((nRead = xStream->readBytes(buffer, BUF_LEN)) == BUF_LEN)
+ aBuffer.append(reinterpret_cast<const char*>(buffer.getConstArray()), nRead);
+
+ if (nRead > 0)
+ aBuffer.append(reinterpret_cast<const char*>(buffer.getConstArray()), nRead);
+
+ xStream->closeInput();
+
+ aResult = OStringToOUString(aBuffer, RTL_TEXTENCODING_UTF8);
+ bHasResult = true;
+
+ // Something happened...
+ if (HasListeners())
+ {
+ Broadcast(ScHint(SfxHintId::ScDataChanged, ScAddress()));
+ pDoc->TrackFormulas(); // must happen immediately
+ pDoc->StartTrackTimer();
+ }
+
+ return SUCCESS;
+}
+
+void ScWebServiceLink::ListenersGone()
+{
+ ScDocument* pStackDoc = pDoc; // member pDoc can't be used after removing the link
+
+ sfx2::LinkManager* pLinkMgr = pDoc->GetLinkManager();
+ pLinkMgr->Remove(this); // deletes this
+
+ if (pLinkMgr->GetLinks().empty()) // deleted the last one ?
+ {
+ SfxBindings* pBindings = pStackDoc->GetViewBindings(); // don't use member pDoc!
+ if (pBindings)
+ pBindings->Invalidate(SID_LINKS);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/source/core/tool/zforauto.cxx b/sc/source/core/tool/zforauto.cxx
new file mode 100644
index 0000000000..f6fb26cba1
--- /dev/null
+++ b/sc/source/core/tool/zforauto.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 <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+
+#include <zforauto.hxx>
+#include <tools/stream.hxx>
+
+ScNumFormatAbbrev::ScNumFormatAbbrev() :
+ sFormatstring ( "Standard" ),
+ eLanguage (LANGUAGE_SYSTEM),
+ eSysLanguage (LANGUAGE_GERMAN) // otherwise "Standard" does not fit
+{
+}
+
+ScNumFormatAbbrev::ScNumFormatAbbrev(sal_uInt32 nFormat,
+ const SvNumberFormatter& rFormatter)
+{
+ PutFormatIndex(nFormat, rFormatter);
+}
+
+void ScNumFormatAbbrev::Load( SvStream& rStream, rtl_TextEncoding eByteStrSet )
+{
+ sal_uInt16 nSysLang, nLang;
+ sFormatstring = rStream.ReadUniOrByteString( eByteStrSet );
+ rStream.ReadUInt16( nSysLang ).ReadUInt16( nLang );
+ eLanguage = LanguageType(nLang);
+ eSysLanguage = LanguageType(nSysLang);
+ if ( eSysLanguage == LANGUAGE_SYSTEM ) // old versions did write it
+ eSysLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+}
+
+void ScNumFormatAbbrev::Save( SvStream& rStream, rtl_TextEncoding eByteStrSet ) const
+{
+ rStream.WriteUniOrByteString( sFormatstring, eByteStrSet );
+ rStream.WriteUInt16( static_cast<sal_uInt16>(eSysLanguage) ).WriteUInt16( static_cast<sal_uInt16>(eLanguage) );
+}
+
+void ScNumFormatAbbrev::PutFormatIndex(sal_uInt32 nFormat,
+ const SvNumberFormatter& rFormatter)
+{
+ const SvNumberformat* pFormat = rFormatter.GetEntry(nFormat);
+ if (pFormat)
+ {
+ eSysLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+ eLanguage = pFormat->GetLanguage();
+ sFormatstring = pFormat->GetFormatstring();
+ }
+ else
+ {
+ OSL_FAIL("SCNumFormatAbbrev:: unknown number format");
+ eLanguage = LANGUAGE_SYSTEM;
+ eSysLanguage = LANGUAGE_GERMAN; // otherwise "Standard" does not fit
+ sFormatstring = "Standard";
+ }
+}
+
+sal_uInt32 ScNumFormatAbbrev::GetFormatIndex( SvNumberFormatter& rFormatter)
+{
+ SvNumFormatType nType;
+ bool bNewInserted;
+ sal_Int32 nCheckPos;
+ return rFormatter.GetIndexPuttingAndConverting( sFormatstring, eLanguage,
+ eSysLanguage, nType, bNewInserted, nCheckPos);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/dif/difexp.cxx b/sc/source/filter/dif/difexp.cxx
new file mode 100644
index 0000000000..ca2821fac9
--- /dev/null
+++ b/sc/source/filter/dif/difexp.cxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dif.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <formulacell.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <progress.hxx>
+#include <rtl/tencinfo.h>
+#include <ftools.hxx>
+#include <cellvalue.hxx>
+#include <rtl/strbuf.hxx>
+#include <osl/diagnose.h>
+#include <formula/errorcodes.hxx>
+#include <tools/stream.hxx>
+
+void ScFormatFilterPluginImpl::ScExportDif( SvStream& rStream, ScDocument* pDoc,
+ const ScAddress& rOutPos, const rtl_TextEncoding eNach )
+{
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDoc->GetTableArea( rOutPos.Tab(), nEndCol, nEndRow );
+ ScAddress aEnd( nEndCol, nEndRow, rOutPos.Tab() );
+ ScAddress aStart( rOutPos );
+
+ aStart.PutInOrder( aEnd );
+
+ ScExportDif( rStream, pDoc, ScRange( aStart, aEnd ), eNach );
+}
+
+void ScFormatFilterPluginImpl::ScExportDif( SvStream& rOut, ScDocument* pDoc,
+ const ScRange&rRange, const rtl_TextEncoding eCharSet )
+{
+ OSL_ENSURE( rRange.aStart <= rRange.aEnd, "*ScExportDif(): Range not sorted!" );
+ OSL_ENSURE( rRange.aStart.Tab() == rRange.aEnd.Tab(),
+ "ScExportDif(): only one table please!" );
+
+ const rtl_TextEncoding eStreamCharSet = rOut.GetStreamCharSet();
+ if ( eStreamCharSet != eCharSet )
+ rOut.SetStreamCharSet( eCharSet );
+
+ sal_Unicode cStrDelim('"');
+ OString aStrDelimEncoded; // only used if not Unicode
+ OUString aStrDelimDecoded; // only used if context encoding
+ bool bContextOrNotAsciiEncoding;
+ if ( eCharSet == RTL_TEXTENCODING_UNICODE )
+ {
+ rOut.StartWritingUnicodeText();
+ bContextOrNotAsciiEncoding = false;
+ }
+ else
+ {
+ aStrDelimEncoded = OString(&cStrDelim, 1, eCharSet);
+ rtl_TextEncodingInfo aInfo;
+ aInfo.StructSize = sizeof(aInfo);
+ if ( rtl_getTextEncodingInfo( eCharSet, &aInfo ) )
+ {
+ bContextOrNotAsciiEncoding =
+ (((aInfo.Flags & RTL_TEXTENCODING_INFO_CONTEXT) != 0) ||
+ ((aInfo.Flags & RTL_TEXTENCODING_INFO_ASCII) == 0));
+ if ( bContextOrNotAsciiEncoding )
+ aStrDelimDecoded = OStringToOUString(aStrDelimEncoded, eCharSet);
+ }
+ else
+ bContextOrNotAsciiEncoding = false;
+ }
+
+ const char p2DoubleQuotes_LF[] = "\"\"\n";
+ const char pSpecDataType_LF[] = "-1,0\n";
+ const char pEmptyData[] = "1,0\n\"\"\n";
+ const char pStringData[] = "1,0\n";
+ const char pNumData[] = "0,";
+ const char pNumDataERROR[] = "0,0\nERROR\n";
+
+ OUStringBuffer aOS;
+ OUString aString;
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCCOL nNumCols = nEndCol - rRange.aStart.Col() + 1;
+ SCROW nNumRows = nEndRow - rRange.aStart.Row() + 1;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScProgress aPrgrsBar( pDoc->GetDocumentShell(), ScResId( STR_LOAD_DOC ), nNumRows, true );
+
+ aPrgrsBar.SetState( 0 );
+
+ // TABLE
+ OSL_ENSURE( pDoc->HasTable( nTab ), "*ScExportDif(): Table not existent!" );
+
+ pDoc->GetName( nTab, aString );
+ aOS.append(OUString::Concat(pKeyTABLE)
+ + "\n0,1\n\""
+ + aString
+ + "\"\n");
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // VECTORS
+ aOS.append(OUString::Concat(pKeyVECTORS)
+ + "\n0,"
+ + OUString::number(static_cast<sal_Int32>(nNumCols))
+ + "\n"
+ + p2DoubleQuotes_LF);
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // TUPLES
+ aOS.append(OUString::Concat(pKeyTUPLES)
+ + "\n0,"
+ + OUString::number(static_cast<sal_Int32>(nNumRows))
+ + "\n"
+ + p2DoubleQuotes_LF);
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // DATA
+ aOS.append(OUString::Concat(pKeyDATA)
+ + "\n0,0\n"
+ + p2DoubleQuotes_LF);
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ SCCOL nColCnt;
+ SCROW nRowCnt;
+
+ for( nRowCnt = rRange.aStart.Row() ; nRowCnt <= nEndRow ; nRowCnt++ )
+ {
+ assert( aOS.isEmpty() && "aOS should be empty");
+ aOS.append(OUString::Concat(pSpecDataType_LF)
+ + pKeyBOT
+ + "\n");
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+ for( nColCnt = rRange.aStart.Col() ; nColCnt <= nEndCol ; nColCnt++ )
+ {
+ assert( aOS.isEmpty() && "aOS should be empty");
+ bool bWriteStringData = false;
+ ScRefCellValue aCell(*pDoc, ScAddress(nColCnt, nRowCnt, nTab));
+
+ switch (aCell.getType())
+ {
+ case CELLTYPE_NONE:
+ aOS.append(pEmptyData);
+ break;
+ case CELLTYPE_VALUE:
+ aString = pDoc->GetInputString( nColCnt, nRowCnt, nTab );
+ aOS.append(pNumData + aString + "\nV\n");
+ break;
+ case CELLTYPE_EDIT:
+ case CELLTYPE_STRING:
+ aString = aCell.getString(pDoc);
+ bWriteStringData = true;
+ break;
+ case CELLTYPE_FORMULA:
+ if (aCell.getFormula()->GetErrCode() != FormulaError::NONE)
+ aOS.append(pNumDataERROR);
+ else if (aCell.getFormula()->IsValue())
+ {
+ aString = pDoc->GetInputString( nColCnt, nRowCnt, nTab );
+ aOS.append(pNumData + aString + "\nV\n");
+ }
+ else
+ {
+ aString = aCell.getFormula()->GetString().getString();
+ bWriteStringData = true;
+ }
+
+ break;
+ default:;
+ }
+
+ if ( !bWriteStringData )
+ {
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+ }
+ else
+ {
+ // for an explanation why this complicated, see
+ // sc/source/ui/docshell/docsh.cxx:ScDocShell::AsciiSave()
+ // In fact we should create a common method if this would be
+ // needed just one more time...
+ assert( aOS.isEmpty() && "aOS should be empty");
+ OUString aTmpStr = aString;
+ aOS.append(pStringData);
+ rOut.WriteUnicodeOrByteText(aOS, eCharSet);
+ aOS.setLength(0);
+ if ( eCharSet == RTL_TEXTENCODING_UNICODE )
+ {
+ // the goal is to replace cStrDelim by cStrDelim+cStrDelim
+ OUString strFrom(cStrDelim);
+ OUString strTo = strFrom + strFrom;
+ aTmpStr = aTmpStr.replaceAll(strFrom, strTo);
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ write_uInt16s_FromOUString(rOut, aTmpStr);
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ }
+ else if ( bContextOrNotAsciiEncoding )
+ {
+ // to byte encoding
+ OString aStrEnc = OUStringToOString(aTmpStr, eCharSet);
+ // back to Unicode
+ OUString aStrDec = OStringToOUString(aStrEnc, eCharSet);
+ // search on re-decoded string
+ OUString aStrTo = aStrDelimDecoded + aStrDelimDecoded;
+ aStrDec = aStrDec.replaceAll(aStrDelimDecoded, aStrTo);
+ // write byte re-encoded
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ rOut.WriteUnicodeOrByteText( aStrDec, eCharSet );
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ }
+ else
+ {
+ OString aStrEnc = OUStringToOString(aTmpStr, eCharSet);
+ // search on encoded string
+ OString aStrTo = aStrDelimEncoded + aStrDelimEncoded;
+ aStrEnc = aStrEnc.replaceAll(aStrDelimEncoded, aStrTo);
+ // write byte encoded
+ rOut.WriteBytes(aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
+ rOut.WriteBytes(aStrEnc.getStr(), aStrEnc.getLength());
+ rOut.WriteBytes(aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
+ }
+ rOut.WriteUniOrByteChar( '\n', eCharSet );
+ }
+ }
+ aPrgrsBar.SetState( nRowCnt );
+ }
+
+ assert( aOS.isEmpty() && "aOS should be empty");
+ aOS.append(OUString::Concat(pSpecDataType_LF)
+ + pKeyEOD
+ + "\n");
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // restore original value
+ rOut.SetStreamCharSet( eStreamCharSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/dif/difimp.cxx b/sc/source/filter/dif/difimp.cxx
new file mode 100644
index 0000000000..224cb998fb
--- /dev/null
+++ b/sc/source/filter/dif/difimp.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 <svl/numformat.hxx>
+#include <tools/stream.hxx>
+#include <osl/diagnose.h>
+#include <dif.hxx>
+#include <docpool.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <fprogressbar.hxx>
+#include <ftools.hxx>
+#include <patattr.hxx>
+#include <scerrors.hxx>
+#include <scitems.hxx>
+#include <stringutil.hxx>
+#include <table.hxx>
+#include <memory>
+
+const std::u16string_view pKeyTABLE = u"TABLE";
+const std::u16string_view pKeyVECTORS = u"VECTORS";
+const std::u16string_view pKeyTUPLES = u"TUPLES";
+const std::u16string_view pKeyDATA = u"DATA";
+const std::u16string_view pKeyBOT = u"BOT";
+const std::u16string_view pKeyEOD = u"EOD";
+
+ErrCode ScFormatFilterPluginImpl::ScImportDif(SvStream& rIn, ScDocument* pDoc, const ScAddress& rInsPos,
+ const rtl_TextEncoding eVon )
+{
+ DifParser aDifParser( rIn, *pDoc, eVon );
+
+ SCTAB nBaseTab = rInsPos.Tab();
+
+ TOPIC eTopic = T_UNKNOWN;
+ bool bSyntErrWarn = false;
+ bool bOverflowWarn = false;
+
+ OUStringBuffer& rData = aDifParser.m_aData;
+
+ rIn.Seek( 0 );
+
+ ScfStreamProgressBar aPrgrsBar( rIn, pDoc->GetDocumentShell() );
+
+ while( eTopic != T_DATA && eTopic != T_END )
+ {
+ eTopic = aDifParser.GetNextTopic();
+
+ aPrgrsBar.Progress();
+
+ const bool bData = !rData.isEmpty();
+
+ switch( eTopic )
+ {
+ case T_TABLE:
+ {
+ if( aDifParser.nVector != 0 || aDifParser.nVal != 1 )
+ bSyntErrWarn = true;
+ if( bData )
+ pDoc->RenameTab(nBaseTab, rData.toString());
+ }
+ break;
+ case T_VECTORS:
+ {
+ if( aDifParser.nVector != 0 )
+ bSyntErrWarn = true;
+ }
+ break;
+ case T_TUPLES:
+ {
+ if( aDifParser.nVector != 0 )
+ bSyntErrWarn = true;
+ }
+ break;
+ case T_DATA:
+ {
+ if( aDifParser.nVector != 0 || aDifParser.nVal != 0 )
+ bSyntErrWarn = true;
+ }
+ break;
+ case T_LABEL:
+ case T_COMMENT:
+ case T_SIZE:
+ case T_PERIODICITY:
+ case T_MAJORSTART:
+ case T_MINORSTART:
+ case T_TRUELENGTH:
+ case T_UINITS:
+ case T_DISPLAYUNITS:
+ case T_END:
+ case T_UNKNOWN:
+ break;
+ default:
+ OSL_FAIL( "ScImportDif - missing enum" );
+ }
+
+ }
+
+ if( eTopic == T_DATA )
+ { // data starts here
+ SCCOL nBaseCol = rInsPos.Col();
+
+ SCCOL nColCnt = SCCOL_MAX;
+ SCROW nRowCnt = rInsPos.Row();
+ DifAttrCache aAttrCache;
+
+ DATASET eCurrent = D_UNKNOWN;
+
+ ScSetStringParam aStrParam; // used to set string value without number detection.
+ aStrParam.setTextInput();
+
+ while( eCurrent != D_EOD )
+ {
+ eCurrent = aDifParser.GetNextDataset();
+
+ aPrgrsBar.Progress();
+ ScAddress aPos(nColCnt, nRowCnt, nBaseTab);
+ const OUString aData = rData.makeStringAndClear();
+
+ switch( eCurrent )
+ {
+ case D_BOT:
+ if( nColCnt < SCCOL_MAX )
+ nRowCnt++;
+ nColCnt = nBaseCol;
+ break;
+ case D_EOD:
+ break;
+ case D_NUMERIC: // Number cell
+ if( nColCnt == SCCOL_MAX )
+ nColCnt = nBaseCol;
+
+ if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
+ {
+ pDoc->EnsureTable(nBaseTab);
+
+ if( DifParser::IsV( aData.getStr() ) )
+ {
+ pDoc->SetValue(aPos, aDifParser.fVal);
+ aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
+ aDifParser.nNumFormat );
+ }
+ else if( aData == "TRUE" || aData == "FALSE" )
+ {
+ pDoc->SetValue(aPos, aDifParser.fVal);
+ aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
+ aDifParser.nNumFormat );
+ }
+ else if( aData == "NA" || aData == "ERROR" )
+ {
+ pDoc->SetString(aPos, aData, &aStrParam);
+ }
+ else
+ {
+ OUString aTmp = "#IND:" + aData + "?";
+ pDoc->SetString(aPos, aTmp, &aStrParam);
+ }
+ }
+ else
+ bOverflowWarn = true;
+
+ nColCnt++;
+ break;
+ case D_STRING: // Text cell
+ if( nColCnt == SCCOL_MAX )
+ nColCnt = nBaseCol;
+
+ if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
+ {
+ if (!aData.isEmpty())
+ {
+ pDoc->EnsureTable(nBaseTab);
+ pDoc->SetTextCell(aPos, aData);
+ }
+ }
+ else
+ bOverflowWarn = true;
+
+ nColCnt++;
+ break;
+ case D_UNKNOWN:
+ break;
+ case D_SYNT_ERROR:
+ break;
+ default:
+ OSL_FAIL( "ScImportDif - missing enum" );
+ }
+ }
+
+ aAttrCache.Apply( *pDoc, nBaseTab );
+ }
+ else
+ return SCERR_IMPORT_FORMAT;
+
+ if( bSyntErrWarn )
+
+ // FIXME: Add proper warning!
+ return SCWARN_IMPORT_RANGE_OVERFLOW;
+
+ else if( bOverflowWarn )
+ return SCWARN_IMPORT_RANGE_OVERFLOW;
+ else
+ return ERRCODE_NONE;
+}
+
+DifParser::DifParser( SvStream& rNewIn, const ScDocument& rDoc, rtl_TextEncoding eCharSet )
+ : fVal(0.0)
+ , nVector(0)
+ , nVal(0)
+ , nNumFormat(0)
+ , pNumFormatter(rDoc.GetFormatTable())
+ , rIn(rNewIn)
+{
+ if ( rIn.GetStreamCharSet() != eCharSet )
+ {
+ OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
+ rIn.SetStreamCharSet( eCharSet );
+ }
+ rIn.StartReadingUnicodeText( eCharSet );
+}
+
+TOPIC DifParser::GetNextTopic()
+{
+ enum STATE { S_VectorVal, S_Data, S_END, S_START, S_UNKNOWN, S_ERROR_L2 };
+
+ static const std::u16string_view ppKeys[] =
+ {
+ pKeyTABLE, // 0
+ pKeyVECTORS,
+ pKeyTUPLES,
+ pKeyDATA,
+ u"LABEL",
+ u"COMMENT", // 5
+ u"SIZE",
+ u"PERIODICITY",
+ u"MAJORSTART",
+ u"MINORSTART",
+ u"TRUELENGTH", // 10
+ u"UINITS",
+ u"DISPLAYUNITS",
+ u"" // 13
+ };
+
+ static const TOPIC pTopics[] =
+ {
+ T_TABLE, // 0
+ T_VECTORS,
+ T_TUPLES,
+ T_DATA,
+ T_LABEL,
+ T_COMMENT, // 5
+ T_SIZE,
+ T_PERIODICITY,
+ T_MAJORSTART,
+ T_MINORSTART,
+ T_TRUELENGTH, // 10
+ T_UINITS,
+ T_DISPLAYUNITS,
+ T_UNKNOWN // 13
+ };
+
+ STATE eS = S_START;
+ OUString aLine;
+
+ nVector = 0;
+ nVal = 0;
+ TOPIC eRet = T_UNKNOWN;
+
+ while( eS != S_END )
+ {
+ if( !ReadNextLine( aLine ) )
+ {
+ eS = S_END;
+ eRet = T_END;
+ }
+
+ switch( eS )
+ {
+ case S_START:
+ {
+ const std::u16string_view* pRef;
+ sal_uInt16 nCnt = 0;
+ bool bSearch = true;
+
+ pRef = &ppKeys[ nCnt ];
+
+ while( bSearch )
+ {
+ if( aLine == *pRef )
+ {
+ eRet = pTopics[ nCnt ];
+ bSearch = false;
+ }
+ else
+ {
+ nCnt++;
+ pRef = &ppKeys[ nCnt ];
+ if( pRef->empty() )
+ bSearch = false;
+ }
+ }
+
+ if( !pRef->empty() )
+ eS = S_VectorVal;
+ else
+ eS = S_UNKNOWN;
+ }
+ break;
+ case S_VectorVal:
+ {
+ const sal_Unicode* pCur = aLine.getStr();
+
+ pCur = ScanIntVal( pCur, nVector );
+
+ if( pCur && *pCur == ',' )
+ {
+ pCur++;
+ ScanIntVal( pCur, nVal );
+ eS = S_Data;
+ }
+ else
+ eS = S_ERROR_L2;
+ }
+ break;
+ case S_Data:
+ OSL_ENSURE( aLine.getLength() >= 2,
+ "+GetNextTopic(): <String> is too short!" );
+ if( aLine.getLength() > 2 )
+ m_aData.append(aLine.subView(1, aLine.getLength() - 2));
+ else
+ m_aData.truncate();
+ eS = S_END;
+ break;
+ case S_END:
+ OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
+ break;
+ case S_UNKNOWN:
+ // skip 2 lines
+ ReadNextLine( aLine );
+ [[fallthrough]];
+ case S_ERROR_L2: // error happened in line 2
+ // skip 1 line
+ ReadNextLine( aLine );
+ eS = S_END;
+ break;
+ default:
+ OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
+ }
+ }
+
+ return eRet;
+}
+
+static void lcl_DeEscapeQuotesDif(OUStringBuffer& rString)
+{
+ // Special handling for DIF import: Escaped (duplicated) quotes are resolved.
+ // Single quote characters are left in place because older versions didn't
+ // escape quotes in strings (and Excel doesn't when using the clipboard).
+ // The quotes around the string are removed before this function is called.
+
+ rString = rString.makeStringAndClear().replaceAll("\"\"", "\"");
+}
+
+// Determine if passed in string is numeric data and set fVal/nNumFormat if so
+DATASET DifParser::GetNumberDataset( const sal_Unicode* pPossibleNumericData )
+{
+ DATASET eRet = D_SYNT_ERROR;
+
+ OSL_ENSURE( pNumFormatter, "-DifParser::GetNumberDataset(): No Formatter, more fun!" );
+ OUString aTestVal( pPossibleNumericData );
+ sal_uInt32 nFormat = 0;
+ double fTmpVal;
+ if( pNumFormatter->IsNumberFormat( aTestVal, nFormat, fTmpVal ) )
+ {
+ fVal = fTmpVal;
+ nNumFormat = nFormat;
+ eRet = D_NUMERIC;
+ }
+ else
+ eRet = D_SYNT_ERROR;
+
+ return eRet;
+}
+
+bool DifParser::ReadNextLine( OUString& rStr )
+{
+ if( aLookAheadLine.isEmpty() )
+ {
+ return rIn.ReadUniOrByteStringLine( rStr, rIn.GetStreamCharSet() );
+ }
+ else
+ {
+ rStr = aLookAheadLine;
+ aLookAheadLine.clear();
+ return true;
+ }
+}
+
+// Look ahead in the stream to determine if the next line is the first line of
+// a valid data record structure
+bool DifParser::LookAhead()
+{
+ const sal_Unicode* pCurrentBuffer;
+ bool bValidStructure = false;
+
+ OSL_ENSURE( aLookAheadLine.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
+ rIn.ReadUniOrByteStringLine( aLookAheadLine, rIn.GetStreamCharSet() );
+
+ pCurrentBuffer = aLookAheadLine.getStr();
+
+ switch( *pCurrentBuffer )
+ {
+ case '-': // Special Datatype
+ pCurrentBuffer++;
+
+ if( Is1_0( pCurrentBuffer ) )
+ {
+ bValidStructure = true;
+ }
+ break;
+ case '0': // Numeric Data
+ pCurrentBuffer++;
+ if( *pCurrentBuffer == ',' )
+ {
+ pCurrentBuffer++;
+ bValidStructure = ( GetNumberDataset(pCurrentBuffer) != D_SYNT_ERROR );
+ }
+ break;
+ case '1': // String Data
+ if( Is1_0( aLookAheadLine.getStr() ) )
+ {
+ bValidStructure = true;
+ }
+ break;
+ }
+ return bValidStructure;
+}
+
+DATASET DifParser::GetNextDataset()
+{
+ DATASET eRet = D_UNKNOWN;
+ OUString aLine;
+ const sal_Unicode* pCurrentBuffer;
+
+ ReadNextLine( aLine );
+
+ pCurrentBuffer = aLine.getStr();
+
+ switch( *pCurrentBuffer )
+ {
+ case '-': // Special Datatype
+ pCurrentBuffer++;
+
+ if( Is1_0( pCurrentBuffer ) )
+ {
+ ReadNextLine( aLine );
+ if( IsBOT( aLine.getStr() ) )
+ eRet = D_BOT;
+ else if( IsEOD( aLine.getStr() ) )
+ eRet = D_EOD;
+ }
+ break;
+ case '0': // Numeric Data
+ pCurrentBuffer++; // value in fVal, 2. line in m_aData
+ if( *pCurrentBuffer == ',' )
+ {
+ pCurrentBuffer++;
+ eRet = GetNumberDataset(pCurrentBuffer);
+ OUString aTmpLine;
+ ReadNextLine( aTmpLine );
+ if ( eRet == D_SYNT_ERROR )
+ { // for broken records write "#ERR: data" to cell
+ m_aData = OUString::Concat("#ERR: ") + pCurrentBuffer + " (" + aTmpLine + ")";
+ eRet = D_STRING;
+ }
+ else
+ {
+ m_aData = aTmpLine;
+ }
+ }
+ break;
+ case '1': // String Data
+ if( Is1_0( aLine.getStr() ) )
+ {
+ ReadNextLine( aLine );
+ sal_Int32 nLineLength = aLine.getLength();
+ const sal_Unicode* pLine = aLine.getStr();
+
+ if( nLineLength >= 1 && *pLine == '"' )
+ {
+ // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
+ // A look ahead into the next line is needed in order to deal with
+ // multiline strings containing quotes
+ if( LookAhead() )
+ {
+ // Single line string
+ if( nLineLength >= 2 && pLine[nLineLength - 1] == '"' )
+ {
+ m_aData = aLine.subView( 1, nLineLength - 2 );
+ lcl_DeEscapeQuotesDif(m_aData);
+ eRet = D_STRING;
+ }
+ }
+ else
+ {
+ // Multiline string
+ m_aData = aLine.subView( 1 );
+ bool bContinue = true;
+ while ( bContinue )
+ {
+ m_aData.append("\n");
+ bContinue = !rIn.eof() && ReadNextLine( aLine );
+ if( bContinue )
+ {
+ nLineLength = aLine.getLength();
+ if( nLineLength >= 1 )
+ {
+ pLine = aLine.getStr();
+ bContinue = !LookAhead();
+ if( bContinue )
+ {
+ m_aData.append(aLine);
+ }
+ else if( pLine[nLineLength - 1] == '"' )
+ {
+ m_aData.append(aLine.subView(0, nLineLength -1));
+ lcl_DeEscapeQuotesDif(m_aData);
+ eRet = D_STRING;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ if( eRet == D_UNKNOWN )
+ ReadNextLine( aLine );
+
+ if( rIn.eof() )
+ eRet = D_EOD;
+
+ return eRet;
+}
+
+const sal_Unicode* DifParser::ScanIntVal( const sal_Unicode* pStart, sal_uInt32& rRet )
+{
+ // eat leading whitespace, not specified, but seen in the wild
+ while (*pStart == ' ' || *pStart == '\t')
+ ++pStart;
+
+ sal_Unicode cCurrent = *pStart;
+
+ if( IsNumber( cCurrent ) )
+ rRet = static_cast<sal_uInt32>( cCurrent - '0' );
+ else
+ return nullptr;
+
+ pStart++;
+ cCurrent = *pStart;
+
+ while( IsNumber( cCurrent ) && rRet < ( 0xFFFFFFFF / 10 ) )
+ {
+ rRet *= 10;
+ rRet += static_cast<sal_uInt32>( cCurrent - '0' );
+
+ pStart++;
+ cCurrent = *pStart;
+ }
+
+ return pStart;
+}
+
+DifColumn::DifColumn ()
+ : mpCurrent(nullptr)
+{
+}
+
+void DifColumn::SetNumFormat( const ScDocument* pDoc, SCROW nRow, const sal_uInt32 nNumFormat )
+{
+ OSL_ENSURE( pDoc->ValidRow(nRow), "*DifColumn::SetNumFormat(): Row too big!" );
+
+ if( nNumFormat > 0 )
+ {
+ if(mpCurrent)
+ {
+ OSL_ENSURE( nRow > 0,
+ "*DifColumn::SetNumFormat(): more cannot be zero!" );
+ OSL_ENSURE( nRow > mpCurrent->nEnd,
+ "*DifColumn::SetNumFormat(): start from scratch?" );
+
+ if( mpCurrent->nNumFormat == nNumFormat && mpCurrent->nEnd == nRow - 1 )
+ mpCurrent->nEnd = nRow;
+ else
+ NewEntry( nRow, nNumFormat );
+ }
+ else
+ NewEntry(nRow,nNumFormat );
+ }
+ else
+ mpCurrent = nullptr;
+}
+
+void DifColumn::NewEntry( const SCROW nPos, const sal_uInt32 nNumFormat )
+{
+ maEntries.emplace_back();
+ mpCurrent = &maEntries.back();
+ mpCurrent->nStart = mpCurrent->nEnd = nPos;
+ mpCurrent->nNumFormat = nNumFormat;
+
+}
+
+void DifColumn::Apply( ScDocument& rDoc, const SCCOL nCol, const SCTAB nTab )
+{
+ ScPatternAttr aAttr( rDoc.GetPool() );
+ SfxItemSet &rItemSet = aAttr.GetItemSet();
+
+ for (const auto& rEntry : maEntries)
+ {
+ OSL_ENSURE( rEntry.nNumFormat > 0,
+ "+DifColumn::Apply(): Number format must not be 0!" );
+
+ rItemSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, rEntry.nNumFormat ) );
+
+ rDoc.ApplyPatternAreaTab( nCol, rEntry.nStart, nCol, rEntry.nEnd, nTab, aAttr );
+
+ rItemSet.ClearItem();
+ }
+}
+
+DifAttrCache::DifAttrCache()
+{
+}
+
+DifAttrCache::~DifAttrCache()
+{
+}
+
+void DifAttrCache::SetNumFormat( const ScDocument* pDoc, const SCCOL nCol, const SCROW nRow, const sal_uInt32 nNumFormat )
+{
+ OSL_ENSURE( pDoc->ValidCol(nCol), "-DifAttrCache::SetNumFormat(): Col too big!" );
+
+ if( !maColMap.count(nCol) )
+ maColMap[ nCol ].reset( new DifColumn );
+
+ maColMap[ nCol ]->SetNumFormat( pDoc, nRow, nNumFormat );
+}
+
+void DifAttrCache::Apply( ScDocument& rDoc, SCTAB nTab )
+{
+ for( SCCOL nCol : rDoc.GetWritableColumnsRange(nTab, 0, rDoc.MaxCol()) )
+ {
+ if( maColMap.count(nCol) )
+ maColMap[ nCol ]->Apply( rDoc, nCol, nTab );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/colrowst.cxx b/sc/source/filter/excel/colrowst.cxx
new file mode 100644
index 0000000000..e194b7309d
--- /dev/null
+++ b/sc/source/filter/excel/colrowst.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <colrowst.hxx>
+
+#include <document.hxx>
+#include <ftools.hxx>
+#include <xltable.hxx>
+#include <xistyle.hxx>
+#include <excimp8.hxx>
+#include <table.hxx>
+
+XclImpColRowSettings::XclImpColRowSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maColWidths(0, rRoot.GetDoc().GetSheetLimits().GetMaxColCount(), 0),
+ maColFlags(0, rRoot.GetDoc().GetSheetLimits().GetMaxColCount(), ExcColRowFlags::NONE),
+ maRowHeights(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), 0),
+ maRowFlags(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), ExcColRowFlags::NONE),
+ maHiddenRows(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), false),
+ mnLastScRow( -1 ),
+ mnDefWidth( STD_COL_WIDTH ),
+ mnDefHeight( ScGlobal::nStdRowHeight ),
+ mnDefRowFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mbHasStdWidthRec( false ),
+ mbHasDefHeight( false ),
+ mbDirty( true )
+{
+}
+
+XclImpColRowSettings::~XclImpColRowSettings()
+{
+}
+
+void XclImpColRowSettings::SetDefWidth( sal_uInt16 nDefWidth, bool bStdWidthRec )
+{
+ if( bStdWidthRec )
+ {
+ // STANDARDWIDTH record overrides DEFCOLWIDTH record
+ mnDefWidth = nDefWidth;
+ mbHasStdWidthRec = true;
+ }
+ else if( !mbHasStdWidthRec )
+ {
+ // use DEFCOLWIDTH record only, if no STANDARDWIDTH record exists
+ mnDefWidth = nDefWidth;
+ }
+}
+
+void XclImpColRowSettings::SetWidthRange( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nWidth )
+{
+ ScDocument& rDoc = GetDoc();
+ nCol2 = ::std::min( nCol2, rDoc.MaxCol() );
+ if (nCol2 == 256)
+ // In BIFF8, the column range is 0-255, and the use of 256 probably
+ // means the range should extend to the max column if the loading app
+ // support columns beyond 255.
+ nCol2 = rDoc.MaxCol();
+
+ nCol1 = ::std::min( nCol1, nCol2 );
+ maColWidths.insert_back(nCol1, nCol2+1, nWidth);
+
+ // We need to apply flag values individually since all flag values are aggregated for each column.
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ ApplyColFlag(nCol, ExcColRowFlags::Used);
+}
+
+void XclImpColRowSettings::HideCol( SCCOL nCol )
+{
+ if (!GetDoc().ValidCol(nCol))
+ return;
+
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+}
+
+void XclImpColRowSettings::HideColRange( SCCOL nCol1, SCCOL nCol2 )
+{
+ nCol2 = ::std::min( nCol2, GetDoc().MaxCol() );
+ nCol1 = ::std::min( nCol1, nCol2 );
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+}
+
+void XclImpColRowSettings::SetDefHeight( sal_uInt16 nDefHeight, sal_uInt16 nFlags )
+{
+ mnDefHeight = nDefHeight;
+ mnDefRowFlags = nFlags;
+ if( mnDefHeight == 0 )
+ {
+ mnDefHeight = ScGlobal::nStdRowHeight;
+ ::set_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN );
+ }
+ mbHasDefHeight = true;
+}
+
+void XclImpColRowSettings::SetHeight( SCROW nScRow, sal_uInt16 nHeight )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ sal_uInt16 nRawHeight = nHeight & EXC_ROW_HEIGHTMASK;
+ bool bDefHeight = ::get_flag( nHeight, EXC_ROW_FLAGDEFHEIGHT ) || (nRawHeight == 0);
+ maRowHeights.insert_back(nScRow, nScRow+1, nRawHeight);
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ ::set_flag(nFlagVal, ExcColRowFlags::Used);
+ ::set_flag(nFlagVal, ExcColRowFlags::Default, bDefHeight);
+
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+
+ if (nScRow > mnLastScRow)
+ mnLastScRow = nScRow;
+}
+
+void XclImpColRowSettings::SetRowSettings( SCROW nScRow, sal_uInt16 nHeight, sal_uInt16 nFlags )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ SetHeight(nScRow, nHeight);
+
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ if (::get_flag(nFlags, EXC_ROW_UNSYNCED))
+ ::set_flag(nFlagVal, ExcColRowFlags::Man);
+
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+
+ if (::get_flag(nFlags, EXC_ROW_HIDDEN))
+ maHiddenRows.insert_back(nScRow, nScRow+1, true);
+}
+
+void XclImpColRowSettings::SetManualRowHeight( SCROW nScRow )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ nFlagVal |= ExcColRowFlags::Man;
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+}
+
+void XclImpColRowSettings::SetDefaultXF( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nXFIndex )
+{
+ /* assign the default column formatting here to ensure that
+ explicit cell formatting is not overwritten. */
+ OSL_ENSURE( (nCol1 <= nCol2) && GetDoc().ValidCol( nCol2 ), "XclImpColRowSettings::SetDefaultXF - invalid column index" );
+ nCol2 = ::std::min( nCol2, GetDoc().MaxCol() );
+ nCol1 = ::std::min( nCol1, nCol2 );
+ XclImpXFRangeBuffer& rXFRangeBuffer = GetXFRangeBuffer();
+ for( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
+ rXFRangeBuffer.SetColumnDefXF( nCol, nXFIndex );
+}
+
+void XclImpColRowSettings::Convert( SCTAB nScTab )
+{
+ if( !mbDirty )
+ return;
+
+ ScDocument& rDoc = GetDoc();
+
+ // column widths ----------------------------------------------------------
+
+ maColWidths.build_tree();
+ for (SCCOL nCol = 0; nCol <= rDoc.MaxCol(); ++nCol)
+ {
+ sal_uInt16 nWidth = mnDefWidth;
+ if (GetColFlag(nCol, ExcColRowFlags::Used))
+ {
+ sal_uInt16 nTmp;
+ if (maColWidths.search_tree(nCol, nTmp).second)
+ nWidth = nTmp;
+ }
+
+ /* Hidden columns: remember hidden state, but do not set hidden state
+ in document here. Needed for #i11776#, no HIDDEN flags in the
+ document, until filters and outlines are inserted. */
+ if( nWidth == 0 )
+ {
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+ nWidth = mnDefWidth;
+ }
+ rDoc.SetColWidthOnly( nCol, nScTab, nWidth );
+ }
+
+ // row heights ------------------------------------------------------------
+
+ // #i54252# set default row height
+ rDoc.SetRowHeightOnly( 0, rDoc.MaxRow(), nScTab, mnDefHeight );
+ if( ::get_flag( mnDefRowFlags, EXC_DEFROW_UNSYNCED ) )
+ // first access to row flags, do not ask for old flags
+ rDoc.SetRowFlags( 0, rDoc.MaxRow(), nScTab, CRFlags::ManualSize );
+
+ maRowHeights.build_tree();
+ if (!maRowHeights.is_tree_valid())
+ return;
+
+ SCROW nPrevRow = -1;
+ ExcColRowFlags nPrevFlags = ExcColRowFlags::NONE;
+ for (const auto& [nRow, nFlags] : maRowFlags)
+ {
+ if (nPrevRow >= 0)
+ {
+ sal_uInt16 nHeight = 0;
+
+ if (nPrevFlags & ExcColRowFlags::Used)
+ {
+ if (nPrevFlags & ExcColRowFlags::Default)
+ {
+ nHeight = mnDefHeight;
+ rDoc.SetRowHeightOnly(nPrevRow, nRow-1, nScTab, nHeight);
+ }
+ else
+ {
+ for (SCROW i = nPrevRow; i <= nRow - 1; ++i)
+ {
+ SCROW nLast;
+ if (!maRowHeights.search_tree(i, nHeight, nullptr, &nLast).second)
+ {
+ // search failed for some reason
+ return;
+ }
+
+ if (nLast > nRow)
+ nLast = nRow;
+
+ rDoc.SetRowHeightOnly(i, nLast-1, nScTab, nHeight);
+ i = nLast-1;
+ }
+ }
+
+ if (nPrevFlags & ExcColRowFlags::Man)
+ rDoc.SetManualHeight(nPrevRow, nRow-1, nScTab, true);
+ }
+ else
+ {
+ nHeight = mnDefHeight;
+ rDoc.SetRowHeightOnly(nPrevRow, nRow-1, nScTab, nHeight);
+ }
+ }
+
+ nPrevRow = nRow;
+ nPrevFlags = nFlags;
+ }
+
+ mbDirty = false;
+}
+
+void XclImpColRowSettings::ConvertHiddenFlags( SCTAB nScTab )
+{
+ ScDocument& rDoc = GetDoc();
+
+ // hide the columns
+ for( SCCOL nCol : rDoc.GetColumnsRange(nScTab, 0, rDoc.MaxCol()) )
+ if (GetColFlag(nCol, ExcColRowFlags::Hidden))
+ rDoc.ShowCol( nCol, nScTab, false );
+
+ // #i38093# rows hidden by filter need extra flag
+ SCROW nFirstFilterScRow = SCROW_MAX;
+ SCROW nLastFilterScRow = SCROW_MAX;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ const XclImpAutoFilterData* pFilter = GetFilterManager().GetByTab( nScTab );
+ // #i70026# use IsFiltered() to set the CRFlags::Filtered flag for active filters only
+ if( pFilter && pFilter->IsActive() && pFilter->IsFiltered() )
+ {
+ nFirstFilterScRow = pFilter->StartRow();
+ nLastFilterScRow = pFilter->EndRow();
+ }
+ }
+
+ // In case the excel row limit is lower than calc's, use the visibility of
+ // the last row and extend it to calc's last row.
+ SCROW nLastXLRow = GetRoot().GetXclMaxPos().Row();
+ if (nLastXLRow < rDoc.MaxRow())
+ {
+ bool bHidden = false;
+ if (!maHiddenRows.search(nLastXLRow, bHidden).second)
+ return;
+
+ maHiddenRows.insert_back(nLastXLRow, GetDoc().GetSheetLimits().GetMaxRowCount(), bHidden);
+ }
+
+ SCROW nPrevRow = -1;
+ bool bPrevHidden = false;
+ for (const auto& [nRow, bHidden] : maHiddenRows)
+ {
+ if (nPrevRow >= 0)
+ {
+ if (bPrevHidden)
+ {
+ rDoc.SetRowHidden(nPrevRow, nRow-1, nScTab, true);
+ // #i38093# rows hidden by filter need extra flag
+ if (nFirstFilterScRow <= nPrevRow && nPrevRow <= nLastFilterScRow)
+ {
+ SCROW nLast = ::std::min(nRow-1, nLastFilterScRow);
+ rDoc.SetRowFiltered(nPrevRow, nLast, nScTab, true);
+ }
+ }
+ }
+
+ nPrevRow = nRow;
+ bPrevHidden = bHidden;
+ }
+
+ // #i47438# if default row format is hidden, hide remaining rows
+ if( ::get_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN ) && (mnLastScRow < rDoc.MaxRow()) )
+ rDoc.ShowRows( mnLastScRow + 1, rDoc.MaxRow(), nScTab, false );
+}
+
+void XclImpColRowSettings::ApplyColFlag(SCCOL nCol, ExcColRowFlags nNewVal)
+{
+ // Get the original flag value.
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ std::pair<ColRowFlagsType::const_iterator,bool> r = maColFlags.search(nCol, nFlagVal);
+ if (!r.second)
+ // Search failed.
+ return;
+
+ ::set_flag(nFlagVal, nNewVal);
+
+ // Re-insert the flag value.
+ maColFlags.insert(r.first, nCol, nCol+1, nFlagVal);
+}
+
+bool XclImpColRowSettings::GetColFlag(SCCOL nCol, ExcColRowFlags nMask) const
+{
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maColFlags.search(nCol, nFlagVal).second)
+ return false;
+ // Search failed.
+
+ return bool(nFlagVal & nMask);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx
new file mode 100644
index 0000000000..00b1ab0a3d
--- /dev/null
+++ b/sc/source/filter/excel/excdoc.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 <sfx2/objsh.hxx>
+#include <rtl/ustring.hxx>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scextopt.hxx>
+#include <docoptio.hxx>
+#include <tabprotection.hxx>
+#include <postit.hxx>
+#include <root.hxx>
+
+#include <excdoc.hxx>
+#include <xeextlst.hxx>
+#include <biffhelper.hxx>
+
+#include <xcl97rec.hxx>
+#include <xetable.hxx>
+#include <xelink.hxx>
+#include <xepage.hxx>
+#include <xeview.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xepivot.hxx>
+#include <export/SparklineExt.hxx>
+#include <XclExpChangeTrack.hxx>
+#include <xepivotxml.hxx>
+#include <xedbdata.hxx>
+#include <xlcontent.hxx>
+#include <xlname.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <o3tl/safeint.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/ThemeExport.hxx>
+#include <docmodel/theme/Theme.hxx>
+#include <svx/svdpage.hxx>
+#include <memory>
+
+using namespace oox;
+
+static OUString lcl_GetVbaTabName( SCTAB n )
+{
+ OUString aRet = "__VBA__" + OUString::number( static_cast<sal_uInt16>(n) );
+ return aRet;
+}
+
+static void lcl_AddBookviews( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_bookViews ) );
+ aRecList.AppendNewRecord( new XclExpWindow1( self.GetRoot() ) );
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_bookViews ) );
+}
+
+static void lcl_AddCalcPr( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ ScDocument& rDoc = self.GetDoc();
+
+ aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_calcPr ) );
+ // OOXTODO: calcCompleted, calcId, calcMode, calcOnSave,
+ // concurrentCalc, concurrentManualCount,
+ // forceFullCalc, fullCalcOnLoad, fullPrecision
+ aRecList.AppendNewRecord( new XclCalccount( rDoc ) );
+ aRecList.AppendNewRecord( new XclRefmode( rDoc ) );
+ aRecList.AppendNewRecord( new XclIteration( rDoc ) );
+ aRecList.AppendNewRecord( new XclDelta( rDoc ) );
+ aRecList.AppendNewRecord( new XclExpBoolRecord(oox::xls::BIFF_ID_SAVERECALC, true) );
+ aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_calcPr
+}
+
+static void lcl_AddWorkbookProtection( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_workbookProtection ) );
+
+ const ScDocProtection* pProtect = self.GetDoc().GetDocProtection();
+ if (pProtect && pProtect->isProtected())
+ {
+ aRecList.AppendNewRecord( new XclExpWindowProtection(pProtect->isOptionEnabled(ScDocProtection::WINDOWS)) );
+ aRecList.AppendNewRecord( new XclExpProtection(pProtect->isOptionEnabled(ScDocProtection::STRUCTURE)) );
+ aRecList.AppendNewRecord( new XclExpPassHash(pProtect->getPasswordHash(PASSHASH_XL)) );
+ }
+
+ aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_workbookProtection
+}
+
+static void lcl_AddScenariosAndFilters( XclExpRecordList<>& aRecList, const XclExpRoot& rRoot, SCTAB nScTab )
+{
+ // Scenarios
+ aRecList.AppendNewRecord( new ExcEScenarioManager( rRoot, nScTab ) );
+ // filter
+ aRecList.AppendRecord( rRoot.GetFilterManager().CreateRecord( nScTab ) );
+}
+
+ExcTable::ExcTable( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnScTab( SCTAB_GLOBAL ),
+ nExcTab( EXC_NOTAB ),
+ mxNoteList( new XclExpNoteList )
+{
+}
+
+ExcTable::ExcTable( const XclExpRoot& rRoot, SCTAB nScTab ) :
+ XclExpRoot( rRoot ),
+ mnScTab( nScTab ),
+ nExcTab( rRoot.GetTabInfo().GetXclTab( nScTab ) ),
+ mxNoteList( new XclExpNoteList )
+{
+}
+
+ExcTable::~ExcTable()
+{
+}
+
+void ExcTable::Add( XclExpRecordBase* pRec )
+{
+ OSL_ENSURE( pRec, "-ExcTable::Add(): pRec is NULL!" );
+ aRecList.AppendNewRecord( pRec );
+}
+
+void ExcTable::FillAsHeaderBinary( ExcBoundsheetList& rBoundsheetList )
+{
+ InitializeGlobals();
+
+ RootData& rR = GetOldRoot();
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ if ( GetBiff() <= EXC_BIFF5 )
+ Add( new ExcBofW );
+ else
+ Add( new ExcBofW8 );
+
+ sal_uInt16 nExcTabCount = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodenames = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+
+ ScDocShell* pShell = GetDocShell();
+ sal_uInt16 nWriteProtHash = pShell ? pShell->GetModifyPasswordHash() : 0;
+ bool bRecommendReadOnly = pShell && pShell->IsLoadReadonly();
+
+ if( (nWriteProtHash > 0) || bRecommendReadOnly )
+ Add( new XclExpEmptyRecord( EXC_ID_WRITEPROT ) );
+
+ // TODO: correct codepage for BIFF5?
+ sal_uInt16 nCodePage = XclTools::GetXclCodePage( (GetBiff() <= EXC_BIFF5) ? RTL_TEXTENCODING_MS_1252 : RTL_TEXTENCODING_UNICODE );
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new XclExpEmptyRecord( EXC_ID_INTERFACEHDR ) );
+ Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) );
+ Add( new XclExpEmptyRecord( EXC_ID_TOOLBARHDR ) );
+ Add( new XclExpEmptyRecord( EXC_ID_TOOLBAREND ) );
+ Add( new XclExpEmptyRecord( EXC_ID_INTERFACEEND ) );
+ Add( new ExcDummy_00 );
+ }
+ else
+ {
+ if( IsDocumentEncrypted() )
+ Add( new XclExpFileEncryption( GetRoot() ) );
+ Add( new XclExpInterfaceHdr( nCodePage ) );
+ Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) );
+ Add( new XclExpInterfaceEnd );
+ Add( new XclExpWriteAccess );
+ }
+
+ Add( new XclExpFileSharing( GetRoot(), nWriteProtHash, bRecommendReadOnly ) );
+ Add( new XclExpUInt16Record( EXC_ID_CODEPAGE, nCodePage ) );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpBoolRecord( EXC_ID_DSF, false ) );
+ Add( new XclExpEmptyRecord( EXC_ID_XL9FILE ) );
+ rR.pTabId = new XclExpChTrTabId( std::max( nExcTabCount, nCodenames ) );
+ Add( rR.pTabId );
+ if( HasVbaStorage() )
+ {
+ Add( new XclObproj );
+ const OUString& rCodeName = GetExtDocOptions().GetDocSettings().maGlobCodeName;
+ if( !rCodeName.isEmpty() )
+ Add( new XclCodename( rCodeName ) );
+ }
+ }
+
+ Add( new XclExpUInt16Record( EXC_ID_FNGROUPCOUNT, 14 ) );
+
+ if ( GetBiff() <= EXC_BIFF5 )
+ {
+ // global link table: EXTERNCOUNT, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+ }
+
+ // document protection options
+ lcl_AddWorkbookProtection( aRecList, *this );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpProt4Rev );
+ Add( new XclExpProt4RevPass );
+ }
+
+ lcl_AddBookviews( aRecList, *this );
+
+ Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) );
+ if ( GetBiff() == EXC_BIFF8 && GetOutput() != EXC_OUTPUT_BINARY )
+ {
+ Add( new XclExpBoolRecord(0x0040, false, XML_backupFile ) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false, XML_showObjects ) ); // HIDEOBJ
+ }
+
+ if ( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpBoolRecord(0x0040, false) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false) ); // HIDEOBJ
+ }
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new ExcDummy_040 );
+ Add( new Exc1904( rDoc ) );
+ Add( new ExcDummy_041 );
+ }
+ else
+ {
+ // BIFF8
+ Add( new Exc1904( rDoc ) );
+ Add( new XclExpBoolRecord( 0x000E, !rDoc.GetDocOptions().IsCalcAsShown() ) );
+ Add( new XclExpBoolRecord(0x01B7, false) ); // REFRESHALL
+ Add( new XclExpBoolRecord(0x00DA, false) ); // BOOKBOOL
+ }
+
+ // Formatting: FONT, FORMAT, XF, STYLE, PALETTE
+ aRecList.AppendRecord( CreateRecord( EXC_ID_FONTLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_FORMATLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_XFLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_PALETTE ) );
+
+ SCTAB nC;
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ // Bundlesheet
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+ }
+ else
+ {
+ // Pivot Cache
+ GetPivotTableManager().CreatePivotTables();
+ aRecList.AppendRecord( GetPivotTableManager().CreatePivotCachesRecord() );
+
+ // Change tracking
+ if( rDoc.GetChangeTrack() )
+ {
+ rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() );
+ Add( rR.pUserBViewList );
+ }
+
+ // Natural Language Formulas Flag
+ aRecList.AppendNewRecord( new XclExpBoolRecord( EXC_ID_USESELFS, GetDoc().GetDocOptions().IsLookUpColRowNames() ) );
+
+ // Bundlesheet
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ OUString aTmpString;
+ for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ )
+ {
+ aTmpString = lcl_GetVbaTabName( nAdd );
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( aTmpString );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ // COUNTRY - in BIFF8 in workbook globals
+ Add( new XclExpCountry( GetRoot() ) );
+
+ // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+
+ Add( new XclExpRecalcId );
+
+ // MSODRAWINGGROUP per-document data
+ aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() );
+ // Shared string table: SST, EXTSST
+ aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) );
+
+ Add( new XclExpBookExt );
+ }
+
+ Add( new ExcEof );
+}
+
+void ExcTable::FillAsHeaderXml( ExcBoundsheetList& rBoundsheetList )
+{
+ InitializeGlobals();
+
+ RootData& rR = GetOldRoot();
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ sal_uInt16 nExcTabCount = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodenames = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+
+ rR.pTabId = new XclExpChTrTabId( std::max( nExcTabCount, nCodenames ) );
+ Add( rR.pTabId );
+
+ Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) );
+ Add( new XclExpBoolRecord(0x0040, false, XML_backupFile ) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false, XML_showObjects ) ); // HIDEOBJ
+
+ Add( new Exc1904( rDoc ) );
+ // OOXTODO: The following /workbook/workbookPr attributes are mapped
+ // to various BIFF records that are not currently supported:
+ //
+ // XML_allowRefreshQuery: QSISTAG 802h: fEnableRefresh
+ // XML_autoCompressPictures: COMPRESSPICTURES 89Bh: fAutoCompressPictures
+ // XML_checkCompatibility: COMPAT12 88Ch: fNoCompatChk
+ // XML_codeName: "Calc"
+ // XML_defaultThemeVersion: ???
+ // XML_filterPrivacy: BOOKEXT 863h: fFilterPrivacy
+ // XML_hidePivotFieldList: BOOKBOOL DAh: fHidePivotTableFList
+ // XML_promptedSolutions: BOOKEXT 863h: fBuggedUserAboutSolution
+ // XML_publishItems: NAMEPUBLISH 893h: fPublished
+ // XML_saveExternalLinkValues: BOOKBOOL DAh: fNoSavSupp
+ // XML_showBorderUnselectedTables: BOOKBOOL DAh: fHideBorderUnsels
+ // XML_showInkAnnotation: BOOKEXT 863h: fShowInkAnnotation
+ // XML_showPivotChart: PIVOTCHARTBITS 859h: fGXHide??
+ // XML_updateLinks: BOOKBOOL DAh: grbitUpdateLinks
+ Add( new XclExpXmlEndSingleElementRecord() ); // XML_workbookPr
+
+ // Formatting: FONT, FORMAT, XF, STYLE, PALETTE
+ aRecList.AppendNewRecord( new XclExpXmlStyleSheet( *this ) );
+
+ // Change tracking
+ if( rDoc.GetChangeTrack() )
+ {
+ rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() );
+ Add( rR.pUserBViewList );
+ }
+
+ lcl_AddWorkbookProtection( aRecList, *this );
+ lcl_AddBookviews( aRecList, *this );
+
+ // Bundlesheet
+ SCTAB nC;
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_sheets ) );
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_sheets ) );
+
+ OUString aTmpString;
+ for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ )
+ {
+ aTmpString = lcl_GetVbaTabName( nAdd );
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( aTmpString );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+
+ lcl_AddCalcPr( aRecList, *this );
+
+ // MSODRAWINGGROUP per-document data
+ aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() );
+ // Shared string table: SST, EXTSST
+ aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) );
+}
+
+void ExcTable::FillAsTableBinary( SCTAB nCodeNameIdx )
+{
+ InitializeTable( mnScTab );
+
+ RootData& rR = GetOldRoot();
+ XclBiff eBiff = GetBiff();
+ ScDocument& rDoc = GetDoc();
+
+ OSL_ENSURE( (mnScTab >= 0) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" );
+ OSL_ENSURE( nExcTab <= o3tl::make_unsigned(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" );
+
+ // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation)
+ if( eBiff == EXC_BIFF8 )
+ GetObjectManager().StartSheet();
+
+ // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ mxCellTable = new XclExpCellTable( GetRoot() );
+
+ //export cell notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+ for (const auto& rNote : aNotes)
+ {
+ if (rNote.maPos.Tab() != mnScTab)
+ continue;
+
+ mxNoteList->AppendNewRecord(new XclExpNote(GetRoot(), rNote.maPos, rNote.mpNote, u""));
+ }
+
+ // WSBOOL needs data from page settings, create it here, add it later
+ rtl::Reference<XclExpPageSettings> xPageSett = new XclExpPageSettings( GetRoot() );
+ bool bFitToPages = xPageSett->GetPageData().mbFitToPages;
+
+ if( eBiff <= EXC_BIFF5 )
+ {
+ Add( new ExcBof );
+ Add( new ExcDummy_02a );
+ }
+ else
+ {
+ Add( new ExcBof8 );
+ lcl_AddCalcPr( aRecList, *this );
+ }
+
+ // GUTS (count & size of outline icons)
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) );
+ // DEFROWHEIGHT, created by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) );
+
+ // COUNTRY - in BIFF5/7 in every worksheet
+ if( eBiff <= EXC_BIFF5 )
+ Add( new XclExpCountry( GetRoot() ) );
+
+ Add( new XclExpWsbool( bFitToPages ) );
+
+ // page settings (SETUP and various other records)
+ aRecList.AppendRecord( xPageSett );
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab);
+ if (pTabProtect && pTabProtect->isProtected())
+ {
+ Add( new XclExpProtection(true) );
+ Add( new XclExpBoolRecord(oox::xls::BIFF_ID_SCENPROTECT, pTabProtect->isOptionEnabled(ScTableProtection::SCENARIOS)) );
+ if (pTabProtect->isOptionEnabled(ScTableProtection::OBJECTS))
+ Add( new XclExpBoolRecord(oox::xls::BIFF_ID_OBJECTPROTECT, true ));
+ Add( new XclExpPassHash(pTabProtect->getPasswordHash(PASSHASH_XL)) );
+ }
+
+ // local link table: EXTERNCOUNT, EXTERNSHEET
+ if( eBiff <= EXC_BIFF5 )
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+
+ if ( eBiff == EXC_BIFF8 )
+ lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab );
+
+ // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ aRecList.AppendRecord( mxCellTable );
+
+ // MERGEDCELLS record, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) );
+ // label ranges
+ if( eBiff == EXC_BIFF8 )
+ Add( new XclExpLabelranges( GetRoot() ) );
+ // data validation (DVAL and list of DV records), generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
+
+ if( eBiff == EXC_BIFF8 )
+ {
+ // all MSODRAWING and OBJ stuff of this sheet goes here
+ aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) );
+ // pivot tables
+ aRecList.AppendRecord( GetPivotTableManager().CreatePivotTablesRecord( mnScTab ) );
+ }
+
+ // list of NOTE records, generated by the cell table
+ aRecList.AppendRecord( mxNoteList );
+
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+
+ if( eBiff == EXC_BIFF8 )
+ {
+ // sheet protection options
+ Add( new XclExpSheetProtectOptions( GetRoot(), mnScTab ) );
+
+ // enhanced protections if there are
+ if (pTabProtect)
+ {
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ for (const auto& rProt : rProts)
+ {
+ Add( new XclExpSheetEnhancedProtection( GetRoot(), rProt));
+ }
+ }
+
+ // web queries
+ Add( new XclExpWebQueryBuffer( GetRoot() ) );
+
+ // conditional formats
+ Add( new XclExpCondFormatBuffer( GetRoot(), XclExtLstRef() ) );
+
+ if( HasVbaStorage() )
+ if( nCodeNameIdx < GetExtDocOptions().GetCodeNameCount() )
+ Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) );
+ }
+
+ // list of HLINK records, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_HLINK ) );
+
+ // change tracking
+ if( rR.pUserBViewList )
+ {
+ XclExpUserBViewList::const_iterator iter;
+ for ( iter = rR.pUserBViewList->cbegin(); iter != rR.pUserBViewList->cend(); ++iter)
+ {
+ Add( new XclExpUsersViewBegin( (*iter).GetGUID(), nExcTab ) );
+ Add( new XclExpUsersViewEnd );
+ }
+ }
+
+ // EOF
+ Add( new ExcEof );
+}
+
+void ExcTable::FillAsTableXml()
+{
+ InitializeTable( mnScTab );
+
+ ScDocument& rDoc = GetDoc();
+
+ OSL_ENSURE( (mnScTab >= 0) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" );
+ OSL_ENSURE( nExcTab <= o3tl::make_unsigned(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" );
+
+ // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation)
+ GetObjectManager().StartSheet();
+
+ // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ mxCellTable = new XclExpCellTable( GetRoot() );
+
+ //export cell notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+ for (const auto& rNote : aNotes)
+ {
+ if (rNote.maPos.Tab() != mnScTab)
+ continue;
+
+ mxNoteList->AppendNewRecord(new XclExpNote(GetRoot(), rNote.maPos, rNote.mpNote, u""));
+ }
+
+ // WSBOOL needs data from page settings, create it here, add it later
+ rtl::Reference<XclExpPageSettings> xPageSett = new XclExpPageSettings( GetRoot() );
+ XclExtLstRef xExtLst = new XclExtLst( GetRoot() );
+ bool bFitToPages = xPageSett->GetPageData().mbFitToPages;
+
+ Color aTabColor = GetRoot().GetDoc().GetTabBgColor(mnScTab);
+ Add(new XclExpXmlSheetPr(bFitToPages, mnScTab, aTabColor, &GetFilterManager()));
+
+ // GUTS (count & size of outline icons)
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) );
+ // DEFROWHEIGHT, created by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) );
+
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID3_DIMENSIONS ) );
+
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+
+ // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ aRecList.AppendRecord( mxCellTable );
+
+ // list of NOTE records, generated by the cell table
+ // not in the worksheet file
+ if( mxNoteList != nullptr && !mxNoteList->IsEmpty() )
+ aRecList.AppendNewRecord( new XclExpComments( mnScTab, *mxNoteList ) );
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab);
+ if (pTabProtect && pTabProtect->isProtected())
+ Add( new XclExpSheetProtection(true, mnScTab) );
+
+ lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab );
+
+ // MERGEDCELLS record, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) );
+
+ // conditional formats
+ Add( new XclExpCondFormatBuffer( GetRoot(), xExtLst ) );
+
+ Add(new xcl::exp::SparklineBuffer(GetRoot(), xExtLst));
+
+ // data validation (DVAL and list of DV records), generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
+
+ // list of HLINK records, generated by the cell table
+ XclExpRecordRef xHyperlinks = mxCellTable->CreateRecord( EXC_ID_HLINK );
+ XclExpHyperlinkList* pHyperlinkList = dynamic_cast<XclExpHyperlinkList*>(xHyperlinks.get());
+ if( pHyperlinkList != nullptr && !pHyperlinkList->IsEmpty() )
+ {
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_hyperlinks ) );
+ aRecList.AppendRecord( xHyperlinks );
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_hyperlinks ) );
+ }
+
+ aRecList.AppendRecord( xPageSett );
+
+ // all MSODRAWING and OBJ stuff of this sheet goes here
+ aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) );
+
+ XclExpImgData* pImgData = xPageSett->getGraphicExport();
+ if (pImgData)
+ aRecList.AppendRecord(pImgData);
+
+ // <tableParts> after <drawing> and before <extLst>
+ aRecList.AppendRecord( GetTablesManager().GetTablesBySheet( mnScTab));
+
+ aRecList.AppendRecord( xExtLst );
+}
+
+void ExcTable::FillAsEmptyTable( SCTAB nCodeNameIdx )
+{
+ InitializeTable( mnScTab );
+
+ if( !(HasVbaStorage() && (nCodeNameIdx < GetExtDocOptions().GetCodeNameCount())) )
+ return;
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new ExcBof );
+ }
+ else
+ {
+ Add( new ExcBof8 );
+ Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) );
+ }
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+ Add( new ExcEof );
+}
+
+void ExcTable::Write( XclExpStream& rStrm )
+{
+ SetCurrScTab( mnScTab );
+ if( mxCellTable )
+ mxCellTable->Finalize(true);
+ aRecList.Save( rStrm );
+}
+
+void ExcTable::WriteXml( XclExpXmlStream& rStrm )
+{
+ if (!GetTabInfo().IsExportTab(mnScTab))
+ {
+ // header export.
+ SetCurrScTab(mnScTab);
+ if (mxCellTable)
+ mxCellTable->Finalize(false);
+ aRecList.SaveXml(rStrm);
+
+ return;
+ }
+
+ // worksheet export
+ OUString sSheetName = XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", mnScTab+1 );
+
+ sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetStreamForPath( sSheetName );
+
+ rStrm.PushStream( pWorksheet );
+
+ pWorksheet->startElement( XML_worksheet,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)),
+ FSNS(XML_xmlns, XML_xdr), "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", // rStrm.getNamespaceURL(OOX_NS(xm)) -> "http://schemas.microsoft.com/office/excel/2006/main",
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ FSNS(XML_xmlns, XML_xr2), rStrm.getNamespaceURL(OOX_NS(xr2)),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+
+ SetCurrScTab( mnScTab );
+ if (mxCellTable)
+ mxCellTable->Finalize(false);
+ aRecList.SaveXml( rStrm );
+
+ XclExpXmlPivotTables* pPT = GetXmlPivotTableManager().GetTablesBySheet(mnScTab);
+ if (pPT)
+ pPT->SaveXml(rStrm);
+
+ rStrm.GetCurrentStream()->endElement( XML_worksheet );
+ rStrm.PopStream();
+}
+
+ExcDocument::ExcDocument( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ aHeader( rRoot )
+{
+}
+
+ExcDocument::~ExcDocument()
+{
+ maTableList.RemoveAllRecords(); // for the following assertion!
+}
+
+void ExcDocument::ReadDoc()
+{
+ InitializeConvert();
+
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ aHeader.FillAsHeaderBinary(maBoundsheetList);
+ else
+ {
+ aHeader.FillAsHeaderXml(maBoundsheetList);
+ GetXmlPivotTableManager().Initialize();
+ GetTablesManager().Initialize(); // Move outside conditions if we wanted to support BIFF.
+ }
+
+ SCTAB nScTab = 0, nScTabCount = GetTabInfo().GetScTabCount();
+ SCTAB nCodeNameIdx = 0, nCodeNameCount = GetExtDocOptions().GetCodeNameCount();
+
+ for( ; nScTab < nScTabCount; ++nScTab )
+ {
+ if( GetTabInfo().IsExportTab( nScTab ) )
+ {
+ ExcTableList::RecordRefType xTab = new ExcTable( GetRoot(), nScTab );
+ maTableList.AppendRecord( xTab );
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ xTab->FillAsTableBinary(nCodeNameIdx);
+ else
+ xTab->FillAsTableXml();
+
+ ++nCodeNameIdx;
+ }
+ }
+ for( ; nCodeNameIdx < nCodeNameCount; ++nScTab, ++nCodeNameIdx )
+ {
+ ExcTableList::RecordRefType xTab = new ExcTable( GetRoot(), nScTab );
+ maTableList.AppendRecord( xTab );
+ xTab->FillAsEmptyTable( nCodeNameIdx );
+ }
+
+ if ( GetBiff() == EXC_BIFF8 )
+ {
+ // complete temporary Escher stream
+ GetObjectManager().EndDocument();
+
+ // change tracking
+ if ( GetDoc().GetChangeTrack() )
+ m_xExpChangeTrack.reset(new XclExpChangeTrack( GetRoot() ));
+ }
+}
+
+void ExcDocument::Write( SvStream& rSvStrm )
+{
+ if( !maTableList.IsEmpty() )
+ {
+ InitializeSave();
+
+ XclExpStream aXclStrm( rSvStrm, GetRoot() );
+
+ aHeader.Write( aXclStrm );
+
+ OSL_ENSURE( maTableList.GetSize() == maBoundsheetList.GetSize(),
+ "ExcDocument::Write - different number of sheets and BOUNDSHEET records" );
+
+ for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab )
+ {
+ // set current stream position in BOUNDSHEET record
+ ExcBoundsheetRef xBoundsheet = maBoundsheetList.GetRecord( nTab );
+ if( xBoundsheet )
+ xBoundsheet->SetStreamPos( aXclStrm.GetSvStreamPos() );
+ // write the table
+ maTableList.GetRecord( nTab )->Write( aXclStrm );
+ }
+
+ // write the table stream positions into the BOUNDSHEET records
+ for( size_t nBSheet = 0, nBSheetCount = maBoundsheetList.GetSize(); nBSheet < nBSheetCount; ++nBSheet )
+ maBoundsheetList.GetRecord( nBSheet )->UpdateStreamPos( aXclStrm );
+ }
+ if( m_xExpChangeTrack )
+ m_xExpChangeTrack->Write();
+}
+
+void ExcDocument::WriteXml( XclExpXmlStream& rStrm )
+{
+ ScDocShell* pDocShell = GetDocShell();
+
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS( static_cast<cppu::OWeakObject*>(pDocShell->GetModel()), uno::UNO_QUERY_THROW );
+ uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
+
+ OUString sUserName = GetUserName();
+ sal_uInt32 nWriteProtHash = pDocShell->GetModifyPasswordHash();
+ bool bHasPasswordHash = nWriteProtHash && !sUserName.isEmpty();
+ const uno::Sequence<beans::PropertyValue> aInfo = pDocShell->GetModifyPasswordInfo();
+ OUString sAlgorithm, sSalt, sHash;
+ sal_Int32 nCount = 0;
+ for (const auto& prop : aInfo)
+ {
+ if (prop.Name == "algorithm-name")
+ prop.Value >>= sAlgorithm;
+ else if (prop.Name == "salt")
+ prop.Value >>= sSalt;
+ else if (prop.Name == "iteration-count")
+ prop.Value >>= nCount;
+ else if (prop.Name == "hash")
+ prop.Value >>= sHash;
+ }
+ bool bHasPasswordInfo
+ = sAlgorithm != "PBKDF2" && !sSalt.isEmpty() && !sHash.isEmpty() && !sUserName.isEmpty();
+ rStrm.exportDocumentProperties(xDocProps, pDocShell->IsSecurityOptOpenReadOnly()
+ && !bHasPasswordHash && !bHasPasswordInfo);
+ rStrm.exportCustomFragments();
+
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_workbook,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)) );
+ rWorkbook->singleElement( XML_fileVersion,
+ XML_appName, "Calc"
+ // OOXTODO: XML_codeName
+ // OOXTODO: XML_lastEdited
+ // OOXTODO: XML_lowestEdited
+ // OOXTODO: XML_rupBuild
+ );
+
+ if (bHasPasswordHash)
+ rWorkbook->singleElement(XML_fileSharing,
+ XML_userName, sUserName,
+ XML_reservationPassword, OString::number(nWriteProtHash, 16));
+ else if (bHasPasswordInfo)
+ rWorkbook->singleElement(XML_fileSharing,
+ XML_userName, sUserName,
+ XML_algorithmName, sAlgorithm,
+ XML_hashValue, sHash,
+ XML_saltValue, sSalt,
+ XML_spinCount, OString::number(nCount));
+
+ if( !maTableList.IsEmpty() )
+ {
+ InitializeSave();
+
+ auto* pDrawLayer = GetDoc().GetDrawLayer();
+ if (pDrawLayer)
+ {
+ std::shared_ptr<model::Theme> pTheme = pDrawLayer->getTheme();
+ if (pTheme)
+ {
+ OUString sThemeRelationshipPath = "theme/theme1.xml";
+ OUString sThemeDocumentPath = "xl/" + sThemeRelationshipPath;
+
+ oox::ThemeExport aThemeExport(&rStrm, oox::drawingml::DOCUMENT_XLSX);
+ aThemeExport.write(sThemeDocumentPath, *pTheme);
+
+ rStrm.addRelation(rStrm.GetCurrentStream()->getOutputStream(),
+ oox::getRelationship(Relationship::THEME),
+ sThemeRelationshipPath);
+ }
+ }
+
+ aHeader.WriteXml( rStrm );
+
+ for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab )
+ {
+ // write the table
+ maTableList.GetRecord( nTab )->WriteXml( rStrm );
+ }
+ }
+
+ if( m_xExpChangeTrack )
+ m_xExpChangeTrack->WriteXml( rStrm );
+
+ XclExpXmlPivotCaches& rCaches = GetXmlPivotTableManager().GetCaches();
+ if (rCaches.HasCaches())
+ rCaches.SaveXml(rStrm);
+
+ const ScCalcConfig& rCalcConfig = GetDoc().GetCalcConfig();
+ formula::FormulaGrammar::AddressConvention eConv = rCalcConfig.meStringRefAddressSyntax;
+
+ // don't save "unspecified" string ref syntax ... query formula grammar
+ // and save that instead
+ if( eConv == formula::FormulaGrammar::CONV_UNSPECIFIED)
+ {
+ eConv = GetDoc().GetAddressConvention();
+ }
+
+ // write if it has been read|imported or explicitly changed
+ // or if ref syntax isn't what would be native for our file format
+ // i.e. ExcelA1 in this case
+ if ( rCalcConfig.mbHasStringRefSyntax ||
+ (eConv != formula::FormulaGrammar::CONV_XL_A1) )
+ {
+ XclExtLstRef xExtLst = new XclExtLst( GetRoot() );
+ xExtLst->AddRecord( new XclExpExtCalcPr( GetRoot(), eConv ) );
+ xExtLst->SaveXml(rStrm);
+ }
+
+ rWorkbook->endElement( XML_workbook );
+ rWorkbook.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excel.cxx b/sc/source/filter/excel/excel.cxx
new file mode 100644
index 0000000000..8420cc696a
--- /dev/null
+++ b/sc/source/filter/excel/excel.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 <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sot/storage.hxx>
+#include <sot/exchange.hxx>
+#include <svl/intitem.hxx>
+#include <filter/msfilter/classids.hxx>
+#include <tools/globname.hxx>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/packages/XPackageEncryption.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <unotools/streamwrap.hxx>
+#include <unotools/defaultencoding.hxx>
+#include <unotools/wincodepage.hxx>
+#include <osl/diagnose.h>
+#include <filter.hxx>
+#include <document.hxx>
+#include <xistream.hxx>
+#include <xltools.hxx>
+#include <docoptio.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <docsh.hxx>
+#include <scerrors.hxx>
+#include <imp_op.hxx>
+#include <excimp8.hxx>
+#include <exp_op.hxx>
+#include <scdll.hxx>
+
+#include <memory>
+
+using namespace css;
+
+static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, std::u16string_view sPrefix)
+{
+ SvStorageInfoList aElements;
+ pStorage->FillInfoList(&aElements);
+ for (const auto & aElement : aElements)
+ {
+ OUString sStreamFullName = sPrefix.size() ? OUString::Concat(sPrefix) + "/" + aElement.GetName() : aElement.GetName();
+ if (aElement.IsStorage())
+ {
+ tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
+ lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
+ }
+ else
+ {
+ // Read stream
+ tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (rStream.is())
+ {
+ sal_Int32 nStreamSize = rStream->GetSize();
+ uno::Sequence< sal_Int8 > oData;
+ oData.realloc(nStreamSize);
+ sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
+ if (nStreamSize == nReadBytes)
+ aStreamsData[sStreamFullName] <<= oData;
+ }
+ }
+ }
+}
+
+static tools::SvRef<SotStorage> lcl_DRMDecrypt(const SfxMedium& rMedium, const tools::SvRef<SotStorage>& rStorage, std::shared_ptr<SvStream>& rNewStorageStrm)
+{
+ tools::SvRef<SotStorage> aNewStorage;
+
+ // We have DRM encrypted storage. We should try to decrypt it first, if we can
+ uno::Sequence< uno::Any > aArguments;
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (!xPackageEncryption.is())
+ {
+ // We do not know how to decrypt this
+ return aNewStorage;
+ }
+
+ comphelper::SequenceAsHashMap aStreamsData;
+ lcl_getListOfStreams(rStorage.get(), aStreamsData, u"");
+
+ try {
+ uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
+ if (!xPackageEncryption->readEncryptionInfo(aStreams))
+ {
+ // We failed with decryption
+ return aNewStorage;
+ }
+
+ tools::SvRef<SotStorageStream> rContentStream = rStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (!rContentStream.is())
+ {
+ return aNewStorage;
+ }
+
+ rNewStorageStrm = std::make_shared<SvMemoryStream>();
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
+ uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*rNewStorageStrm));
+
+ if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
+ {
+ // We failed with decryption
+ return aNewStorage;
+ }
+
+ rNewStorageStrm->Seek(0);
+
+ // Further reading is done from new document
+ aNewStorage = new SotStorage(*rNewStorageStrm);
+
+ // Set the media descriptor data
+ uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData("");
+ rMedium.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+ catch (const std::exception&)
+ {
+ return aNewStorage;
+ }
+
+ return aNewStorage;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScImportExcel( SfxMedium& rMedium, ScDocument* pDocument, const EXCIMPFORMAT eFormat )
+{
+ // check the passed Calc document
+ OSL_ENSURE( pDocument, "::ScImportExcel - no document" );
+ if( !pDocument ) return SCERR_IMPORT_INTERNAL; // should not happen
+
+ /* Import all BIFF versions regardless on eFormat, needed for import of
+ external cells (file type detection returns Excel4.0). */
+ if( (eFormat != EIF_AUTO) && (eFormat != EIF_BIFF_LE4) && (eFormat != EIF_BIFF5) && (eFormat != EIF_BIFF8) )
+ {
+ OSL_FAIL( "::ScImportExcel - wrong file format specification" );
+ return SCERR_IMPORT_FORMAT;
+ }
+
+ // check the input stream from medium
+ SvStream* pMedStrm = rMedium.GetInStream();
+ OSL_ENSURE( pMedStrm, "::ScImportExcel - medium without input stream" );
+ if( !pMedStrm ) return SCERR_IMPORT_OPEN; // should not happen
+
+ SvStream* pBookStrm = nullptr; // The "Book"/"Workbook" stream containing main data.
+ XclBiff eBiff = EXC_BIFF_UNKNOWN; // The BIFF version of the main stream.
+
+ // try to open an OLE storage
+ tools::SvRef<SotStorage> xRootStrg;
+ tools::SvRef<SotStorageStream> xStrgStrm;
+ std::shared_ptr<SvStream> aNewStorageStrm;
+ if( SotStorage::IsStorageFile( pMedStrm ) )
+ {
+ xRootStrg = new SotStorage( pMedStrm, false );
+ if( xRootStrg->GetError() )
+ xRootStrg = nullptr;
+ }
+
+ // try to open "Book" or "Workbook" stream in OLE storage
+ if( xRootStrg.is() )
+ {
+ // Check if there is DRM encryption in storage
+ tools::SvRef<SotStorageStream> xDRMStrm = ScfTools::OpenStorageStreamRead(xRootStrg, "\011DRMContent");
+ if (xDRMStrm.is())
+ {
+ xRootStrg = lcl_DRMDecrypt(rMedium, xRootStrg, aNewStorageStrm);
+ }
+
+ // try to open the "Book" stream
+ tools::SvRef<SotStorageStream> xBookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_BOOK );
+ XclBiff eBookBiff = xBookStrm.is() ? XclImpStream::DetectBiffVersion( *xBookStrm ) : EXC_BIFF_UNKNOWN;
+
+ // try to open the "Workbook" stream
+ tools::SvRef<SotStorageStream> xWorkbookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_WORKBOOK );
+ XclBiff eWorkbookBiff = xWorkbookStrm.is() ? XclImpStream::DetectBiffVersion( *xWorkbookStrm ) : EXC_BIFF_UNKNOWN;
+
+ // decide which stream to use
+ if( (eWorkbookBiff != EXC_BIFF_UNKNOWN) && ((eBookBiff == EXC_BIFF_UNKNOWN) || (eWorkbookBiff > eBookBiff)) )
+ {
+ /* Only "Workbook" stream exists; or both streams exist,
+ and "Workbook" has higher BIFF version than "Book" stream. */
+ xStrgStrm = xWorkbookStrm;
+ eBiff = eWorkbookBiff;
+ }
+ else if( eBookBiff != EXC_BIFF_UNKNOWN )
+ {
+ /* Only "Book" stream exists; or both streams exist,
+ and "Book" has higher BIFF version than "Workbook" stream. */
+ xStrgStrm = xBookStrm;
+ eBiff = eBookBiff;
+ }
+
+ pBookStrm = xStrgStrm.get();
+ }
+
+ // no "Book" or "Workbook" stream found, try plain input stream from medium (even for BIFF5+)
+ if( !pBookStrm )
+ {
+ eBiff = XclImpStream::DetectBiffVersion( *pMedStrm );
+ if( eBiff != EXC_BIFF_UNKNOWN )
+ pBookStrm = pMedStrm;
+ }
+
+ // try to import the file
+ ErrCode eRet = SCERR_IMPORT_UNKNOWN_BIFF;
+ if( pBookStrm )
+ {
+ pBookStrm->SetBufferSize( 0x8000 ); // still needed?
+
+ XclImpRootData aImpData(
+ eBiff, rMedium, xRootStrg, *pDocument,
+ utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding()));
+ std::unique_ptr< ImportExcel > xFilter;
+ switch( eBiff )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ xFilter.reset( new ImportExcel( aImpData, *pBookStrm ) );
+ break;
+ case EXC_BIFF8:
+ xFilter.reset( new ImportExcel8( aImpData, *pBookStrm ) );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ eRet = xFilter ? xFilter->Read() : SCERR_IMPORT_INTERNAL;
+ }
+
+ return eRet;
+}
+
+static ErrCode lcl_ExportExcelBiff( SfxMedium& rMedium, ScDocument *pDocument,
+ SvStream* pMedStrm, bool bBiff8, rtl_TextEncoding eNach )
+{
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption;
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ SvStream* pOriginalMediaStrm = pMedStrm;
+ std::shared_ptr<SvStream> pMediaStrm;
+ if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
+ {
+ ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
+ OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString());
+
+ if (sCryptoType.getLength())
+ {
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Sequence<uno::Any> aArguments{
+ uno::Any(beans::NamedValue("Binary", uno::Any(true))) };
+ xPackageEncryption.set(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (xPackageEncryption.is())
+ {
+ // We have an encryptor. Export document into memory stream and encrypt it later
+ pMediaStrm = std::make_shared<SvMemoryStream>();
+ pMedStrm = pMediaStrm.get();
+
+ // Temp removal of EncryptionData to avoid password protection triggering
+ rMedium.GetItemSet().ClearItem(SID_ENCRYPTIONDATA);
+ }
+ }
+ }
+
+ // try to open an OLE storage
+ tools::SvRef<SotStorage> xRootStrg = new SotStorage( pMedStrm, false );
+ if( xRootStrg->GetError() ) return SCERR_IMPORT_OPEN;
+
+ // create BIFF dependent strings
+ OUString aStrmName, aClipName, aClassName;
+ if( bBiff8 )
+ {
+ aStrmName = EXC_STREAM_WORKBOOK;
+ aClipName = "Biff8";
+ aClassName = "Microsoft Excel 97-Tabelle";
+ }
+ else
+ {
+ aStrmName = EXC_STREAM_BOOK;
+ aClipName = "Biff5";
+ aClassName = "Microsoft Excel 5.0-Tabelle";
+ }
+
+ // open the "Book"/"Workbook" stream
+ tools::SvRef<SotStorageStream> xStrgStrm = ScfTools::OpenStorageStreamWrite( xRootStrg, aStrmName );
+ if( !xStrgStrm.is() || xStrgStrm->GetError() ) return SCERR_IMPORT_OPEN;
+
+ xStrgStrm->SetBufferSize( 0x8000 ); // still needed?
+
+ ErrCode eRet = SCERR_IMPORT_UNKNOWN_BIFF;
+ XclExpRootData aExpData( bBiff8 ? EXC_BIFF8 : EXC_BIFF5, rMedium, xRootStrg, *pDocument, eNach );
+ if ( bBiff8 )
+ {
+ ExportBiff8 aFilter( aExpData, *xStrgStrm );
+ eRet = aFilter.Write();
+ }
+ else
+ {
+ ExportBiff5 aFilter( aExpData, *xStrgStrm );
+ eRet = aFilter.Write();
+ }
+
+ if( eRet == SCWARN_IMPORT_RANGE_OVERFLOW )
+ eRet = SCWARN_EXPORT_MAXROW;
+
+ SvGlobalName aGlobName(MSO_EXCEL5_CLASSID);
+ SotClipboardFormatId nClip = SotExchange::RegisterFormatName( aClipName );
+ xRootStrg->SetClass( aGlobName, nClip, aClassName );
+
+ xStrgStrm->Commit();
+ xRootStrg->Commit();
+
+ if (xPackageEncryption.is())
+ {
+ // Perform DRM encryption
+ pMedStrm->Seek(0);
+
+ xPackageEncryption->setupEncryption(aEncryptionData);
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pMedStrm, false));
+ uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
+
+ tools::SvRef<SotStorage> xEncryptedRootStrg = new SotStorage(pOriginalMediaStrm, false);
+ for (const beans::NamedValue & aStreamData : std::as_const(aStreams))
+ {
+ // To avoid long paths split and open substorages recursively
+ // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
+ tools::SvRef<SotStorage> pStorage = xEncryptedRootStrg.get();
+ OUString sFileName;
+ sal_Int32 idx = 0;
+ do
+ {
+ OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
+ if (!sPathElem.isEmpty())
+ {
+ if (idx < 0)
+ {
+ sFileName = sPathElem;
+ }
+ else
+ {
+ pStorage = pStorage->OpenSotStorage(sPathElem);
+ }
+ }
+ } while (pStorage && idx >= 0);
+
+ if (!pStorage)
+ {
+ eRet = ERRCODE_IO_GENERAL;
+ break;
+ }
+
+ tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
+ if (!pStream)
+ {
+ eRet = ERRCODE_IO_GENERAL;
+ break;
+ }
+ uno::Sequence<sal_Int8> aStreamContent;
+ aStreamData.Value >>= aStreamContent;
+ size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getConstArray(), aStreamContent.getLength());
+ if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
+ {
+ eRet = ERRCODE_IO_CANTWRITE;
+ break;
+ }
+ }
+ xEncryptedRootStrg->Commit();
+
+ // Restore encryption data
+ rMedium.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+
+ return eRet;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScExportExcel5( SfxMedium& rMedium, ScDocument *pDocument,
+ ExportFormatExcel eFormat, rtl_TextEncoding eNach )
+{
+ if( eFormat != ExpBiff5 && eFormat != ExpBiff8 )
+ return SCERR_IMPORT_NI;
+
+ // check the passed Calc document
+ OSL_ENSURE( pDocument, "::ScExportExcel5 - no document" );
+ if( !pDocument ) return SCERR_IMPORT_INTERNAL; // should not happen
+
+ // check the output stream from medium
+ SvStream* pMedStrm = rMedium.GetOutStream();
+ OSL_ENSURE( pMedStrm, "::ScExportExcel5 - medium without output stream" );
+ if( !pMedStrm ) return SCERR_IMPORT_OPEN; // should not happen
+
+ ErrCode eRet = lcl_ExportExcelBiff(rMedium, pDocument, pMedStrm, eFormat == ExpBiff8, eNach);
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportCalcRTF(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ ScRange aRange;
+
+ bool bRet;
+
+ try
+ {
+ bRet = ScFormatFilter::Get().ScImportRTF(rStream, OUString(), &aDocument, aRange) == ERRCODE_NONE;
+ }
+ catch (const std::range_error&)
+ {
+ return false;
+ }
+
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportXLS(SvStream& rStream)
+{
+ ScDLL::Init();
+ SfxMedium aMedium;
+ css::uno::Reference<css::io::XInputStream> xStm(new utl::OInputStreamWrapper(rStream));
+ aMedium.GetItemSet().Put(SfxUnoAnyItem(SID_INPUTSTREAM, css::uno::Any(xStm)));
+ aMedium.GetItemSet().Put(SfxUInt16Item(SID_UPDATEDOCMODE, css::document::UpdateDocMode::NO_UPDATE));
+
+ ScDocShellRef xDocShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT |
+ SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
+ SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
+
+ xDocShell->DoInitNew();
+ xDocShell->SetInitialLinkUpdate(&aMedium);
+
+ ScDocument& rDoc = xDocShell->GetDocument();
+
+ ScDocOptions aDocOpt = rDoc.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ rDoc.SetDocOptions(aDocOpt);
+ rDoc.MakeTable(0);
+ rDoc.EnableExecuteLink(false);
+ rDoc.SetInsertingFromOtherDoc(true);
+ rDoc.InitDrawLayer(xDocShell.get());
+ bool bRet(false);
+ try
+ {
+ bRet = ScFormatFilter::Get().ScImportExcel(aMedium, &rDoc, EIF_AUTO) == ERRCODE_NONE;
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ }
+ catch (const std::out_of_range&)
+ {
+ }
+ xDocShell->DoClose();
+ xDocShell.clear();
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDIF(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ return ScFormatFilter::Get().ScImportDif(rStream, &aDocument, ScAddress(0, 0, 0), RTL_TEXTENCODING_IBM_850) == ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx
new file mode 100644
index 0000000000..d217c9622e
--- /dev/null
+++ b/sc/source/filter/excel/excform.cxx
@@ -0,0 +1,1911 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excform.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <scmatrix.hxx>
+
+#include <formula/errorcodes.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+#include <imp_op.hxx>
+#include <namebuff.hxx>
+#include <root.hxx>
+#include <xltracer.hxx>
+#include <xihelper.hxx>
+#include <xiname.hxx>
+#include <xistyle.hxx>
+#include <documentimport.hxx>
+
+using ::std::vector;
+
+const sal_uInt16 ExcelToSc::nRowMask = 0x3FFF;
+
+void ImportExcel::Formula25()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXF = 0, nFormLen;
+ double fCurVal;
+ bool bShrFmla;
+
+ aIn >> aXclPos;
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// BIFF2
+ aIn.Ignore( 3 );
+
+ fCurVal = aIn.ReadDouble();
+ aIn.Ignore( 1 );
+ nFormLen = aIn.ReaduInt8();
+ bShrFmla = false;
+ }
+ else
+ {// BIFF5
+ sal_uInt8 nFlag0;
+ nXF = aIn.ReaduInt16();
+ fCurVal = aIn.ReadDouble();
+ nFlag0 = aIn.ReaduInt8();
+ aIn.Ignore( 5 );
+
+ nFormLen = aIn.ReaduInt16();
+
+ bShrFmla = nFlag0 & 0x08; // shared or not shared
+ }
+
+ Formula( aXclPos, nXF, nFormLen, fCurVal, bShrFmla );
+}
+
+void ImportExcel::Formula3()
+{
+ Formula4();
+}
+
+void ImportExcel::Formula4()
+{
+ XclAddress aXclPos;
+
+ aIn >> aXclPos;
+ sal_uInt16 nXF = aIn.ReaduInt16();
+ double fCurVal = aIn.ReadDouble();
+ aIn.Ignore( 2 );
+ sal_uInt16 nFormLen = aIn.ReaduInt16();
+
+ Formula( aXclPos, nXF, nFormLen, fCurVal, false );
+}
+
+void ImportExcel::Formula(
+ const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla)
+{
+ if (!nFormLen)
+ return;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if (!GetAddressConverter().ConvertAddress(aScPos, rXclPos, GetCurrScTab(), true))
+ // Conversion failed.
+ return;
+
+ // Formula will be read next, length in nFormLen
+ std::unique_ptr<ScTokenArray> pResult;
+
+ pFormConv->Reset( aScPos );
+ ScDocumentImport& rDoc = GetDocImport();
+
+ if (bShrFmla)
+ {
+ // This is a shared formula. Get the token array from the shared formula pool.
+ SCCOL nSharedCol;
+ SCROW nSharedRow;
+ if (ExcelToSc::ReadSharedFormulaPosition(maStrm, nSharedCol, nSharedRow))
+ {
+ ScAddress aRefPos(nSharedCol, nSharedRow, GetCurrScTab());
+ const ScTokenArray* pSharedCode = pFormConv->GetSharedFormula(aRefPos);
+ if (pSharedCode)
+ {
+ ScFormulaCell* pCell;
+ pCell = new ScFormulaCell(rD, aScPos, pSharedCode->Clone());
+ pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().EnsureTable(aScPos.Tab());
+ rDoc.setFormulaCell(aScPos, pCell);
+ pCell->SetNeedNumberFormat(false);
+ if (std::isfinite(fCurVal))
+ pCell->SetResultDouble(fCurVal);
+
+ GetXFRangeBuffer().SetXF(aScPos, nXF);
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell);
+ }
+ else
+ {
+ // Shared formula not found even though it's clearly a shared formula.
+ // The cell will be created in the following shared formula
+ // record.
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, nullptr);
+ }
+ return;
+ }
+ }
+
+ ConvErr eErr = pFormConv->Convert( pResult, maStrm, nFormLen, true );
+
+ ScFormulaCell* pCell = nullptr;
+
+ if (pResult)
+ {
+ pCell = new ScFormulaCell(rDoc.getDoc(), aScPos, std::move(pResult));
+ pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+ rDoc.getDoc().EnsureTable(aScPos.Tab());
+ rDoc.setFormulaCell(aScPos, pCell);
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell);
+ }
+ else
+ {
+ pCell = rDoc.getDoc().GetFormulaCell(aScPos);
+ if (pCell)
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ }
+
+ if (pCell)
+ {
+ pCell->SetNeedNumberFormat(false);
+ if( eErr != ConvErr::OK )
+ ExcelToSc::SetError( *pCell, eErr );
+
+ if (std::isfinite(fCurVal))
+ pCell->SetResultDouble(fCurVal);
+ }
+
+ GetXFRangeBuffer().SetXF(aScPos, nXF);
+}
+
+ExcelToSc::ExcelToSc( XclImpRoot& rRoot ) :
+ ExcelConverterBase(rRoot.GetDocImport().getDoc().GetSharedStringPool()),
+ XclImpRoot( rRoot ),
+ maFuncProv( rRoot ),
+ meBiff( rRoot.GetBiff() )
+{
+}
+
+ExcelToSc::~ExcelToSc()
+{
+}
+
+std::unique_ptr<ScTokenArray> ExcelToSc::GetDummy()
+{
+ aPool.Store( "Dummy()" );
+ aPool >> aStack;
+ return aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+}
+
+// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
+// otherwise it will seek to the first byte after the additional content (eg
+// inline arrays) following <nFormulaLen>
+ConvErr ExcelToSc::Convert( std::unique_ptr<ScTokenArray>& pResult, XclImpStream& aIn, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT )
+{
+ RootData& rR = GetOldRoot();
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ bool bArrayFormula = false;
+ TokenId nBuf0;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bConditional = eFT == FT_CondFormat;
+ const bool bRNorSF = bRangeName || bSharedFormula || bConditional;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ ExtensionTypeVec aExtensions;
+
+ if( nFormulaLen == 0 )
+ {
+ aPool.Store( "-/-" );
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return ConvErr::OK;
+ }
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ {
+ sal_uInt16 nUINT16 = 3;
+
+ if( meBiff != EXC_BIFF2 )
+ nUINT16++;
+
+ aIn.Ignore( nUINT16 );
+
+ bArrayFormula = true;
+ break;
+ }
+ case 0x03: // Addition [312 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocAdd << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x04: // Subtraction [313 264]
+ // SECOND-TOP minus TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocSub << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x05: // Multiplication [313 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocMul << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x06: // Division [313 264]
+ // divide TOP by SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocDiv << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x07: // Exponentiation [313 265]
+ // raise SECOND-TOP to power of TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocPow << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x08: // Concatenation [313 265]
+ // append TOP to SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocAmpersand << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x09: // Less Than [313 265]
+ // SECOND-TOP < TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLess << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0A: // Less Than or Equal [313 265]
+ // SECOND-TOP <= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLessEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0B: // Equal [313 265]
+ // SECOND-TOP == TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0C: // Greater Than or Equal [313 265]
+ // SECOND-TOP >= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreaterEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0D: // Greater Than [313 265]
+ // SECOND-TOP > TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreater << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0E: // Not Equal [313 265]
+ // SECOND-TOP != TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocNotEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0F: // Intersection [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocIntersect << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x10: // Union [314 265]
+ // ocSep instead of 'ocUnion'
+ aStack >> nBuf0;
+ aPool << aStack << ocSep << nBuf0;
+ // doesn't fit exactly, but is more Excel-like
+ aPool >> aStack;
+ break;
+ case 0x11: // Range [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocRange << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x12: // Unary Plus [312 264]
+ aPool << ocAdd << aStack;
+ aPool >> aStack;
+ break;
+ case 0x13: // Unary Minus [312 264]
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+ case 0x14: // Percent Sign [312 264]
+ aPool << aStack << ocPercentSign;
+ aPool >> aStack;
+ break;
+ case 0x15: // Parenthesis [326 278]
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case 0x16: // Missing Argument [314 266]
+ aPool << ocMissing;
+ aPool >> aStack;
+ GetTracer().TraceFormulaMissingArg();
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ nLen = aIn.ReaduInt8();
+ OUString aString = aIn.ReadRawByteString( nLen );
+
+ aStack << aPool.Store( aString );
+ break;
+ }
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+
+ if( meBiff == EXC_BIFF2 )
+ {
+ nData = aIn.ReaduInt8();
+ nFactor = 1;
+ }
+ else
+ {
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+ }
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ else if( nOpt & 0x10 ) // AttrSum
+ DoMulArgs( ocSum, 1 );
+ }
+ break;
+ case 0x1A: // External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 7 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 10 ); break;
+ case EXC_BIFF5:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1A does not exist in Biff5!" );
+ [[fallthrough]];
+ default:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1B: // End External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 3 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 4 ); break;
+ case EXC_BIFF5:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1B does not exist in Biff5!" );
+ [[fallthrough]];
+ default:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1D: // Boolean [315 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ if( nByte == 0 )
+ aPool << ocFalse << ocOpen << ocClose;
+ else
+ aPool << ocTrue << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1E: // Integer [315 266]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aStack << aPool.Store( static_cast<double>(nUINT16) );
+ break;
+ }
+ case 0x1F: // Number [315 266]
+ {
+ double fDouble = aIn.ReadDouble();
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ {
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 6 : 7 );
+ if( bAllowArrays )
+ {
+ aStack << aPool.StoreMatrix();
+ aExtensions.push_back( EXTENSION_ARRAY );
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ {
+ sal_uInt16 nXclFunc;
+ if( meBiff <= EXC_BIFF3 )
+ nXclFunc = aIn.ReaduInt8();
+ else
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ }
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ {
+ sal_uInt16 nXclFunc;
+ sal_uInt8 nParamCount;
+ nParamCount = aIn.ReaduInt8();
+ nParamCount &= 0x7F;
+ if( meBiff <= EXC_BIFF3 )
+ nXclFunc = aIn.ReaduInt8();
+ else
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, nParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ }
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 5 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 8 ); break;
+ case EXC_BIFF5: aIn.Ignore( 12 ); break;
+ default:
+ OSL_FAIL(
+ "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ const XclImpName* pName = GetNameManager().GetName( nUINT16 );
+ if(pName && !pName->GetScRangeData())
+ aStack << aPool.Store( ocMacro, pName->GetXclName() );
+ else
+ aStack << aPool.StoreName(nUINT16, -1);
+ }
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aSRD.SetAbsCol(static_cast<SCCOL>(nByte));
+ aSRD.SetAbsRow(nUINT16 & 0x3FFF);
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName );
+
+ switch ( nOp )
+ {
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+ ScSingleRefData& rSRef1 = aCRD.Ref1;
+ ScSingleRefData& rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ // no information which part is deleted, set all
+ rSRef1.SetColDeleted( true );
+ rSRef1.SetRowDeleted( true );
+ rSRef2.SetColDeleted( true );
+ rSRef2.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ aExtensions.push_back( EXTENSION_MEMAREA );
+ [[fallthrough]];
+
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 4 : 6 );
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8(); // >> Attribute, Row >> Col
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF );
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8( );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 1 : 2 );
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ {
+ OUString aString = "COMM_EQU_FUNC";
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aString += OUString::number( nByte );
+ nByte = aIn.ReaduInt8();
+ aStack << aPool.Store( aString );
+ DoMulArgs( ocPush, nByte + 1 );
+ break;
+ }
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ {
+ sal_Int16 nINT16 = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ if( nINT16 >= 0 )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ else
+ aStack << aPool.StoreName( nUINT16, -1 );
+ aIn.Ignore( 12 );
+ break;
+ }
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRow;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nCol;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ { // from external
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a SingleRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ { // in current Workbook
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel( nRow, nCol, aSRD, bRangeName );
+
+ switch ( nOp )
+ {
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+ if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) )
+ aSRD.SetTabDeleted( true );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aCRD.Ref2.SetTabDeleted( !ValidTab(static_cast<SCTAB>(nTabLast)) );
+ aStack << aPool.Store( aCRD );
+ }
+ else
+ aStack << aPool.Store( aSRD );
+ }
+ }
+
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRowFirst, nRowLast;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nColFirst, nColLast;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a CompleteRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ // first part of range
+ ScSingleRefData& rR1 = aCRD.Ref1;
+ ScSingleRefData& rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nTabFirst != nTabLast );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ // no information which part is deleted, set all
+ rR1.SetColDeleted( true );
+ rR1.SetRowDeleted( true );
+ rR2.SetColDeleted( true );
+ rR2.SetRowDeleted( true );
+ }
+ if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) )
+ rR1.SetTabDeleted( true );
+ if ( !ValidTab(static_cast<SCTAB>(nTabLast)) )
+ rR2.SetTabDeleted( true );
+
+ aStack << aPool.Store( aCRD );
+ }//END in current Workbook
+ }
+ break;
+ default: bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Ni;
+ }
+ else if( aIn.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Count;
+ }
+ else if( bArrayFormula )
+ {
+ pResult = nullptr;
+ eRet = ConvErr::OK;
+ }
+ else
+ {
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::OK;
+ }
+
+ aIn.Seek( nEndPos );
+
+ if( eRet == ConvErr::OK )
+ ReadExtensions( aExtensions, aIn );
+
+ return eRet;
+}
+
+// stream seeks to first byte after <nFormulaLen>
+ConvErr ExcelToSc::Convert( ScRangeListTabs& rRangeList, XclImpStream& aIn, std::size_t nFormulaLen,
+ SCTAB nTab, const FORMULA_TYPE eFT )
+{
+ RootData& rR = GetOldRoot();
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeName || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ aCRD.Ref1.SetAbsTab(aEingPos.Tab());
+ aCRD.Ref2.SetAbsTab(aEingPos.Tab());
+
+ if( nFormulaLen == 0 )
+ return ConvErr::OK;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+ std::size_t nIgnore = 0;
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4;
+ break;
+ case 0x02: // Data Table [325 277]
+ nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4;
+ break;
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x17: // String Constant [314 266]
+ nLen = aIn.ReaduInt8();
+ nIgnore = nLen;
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+
+ if( meBiff == EXC_BIFF2 )
+ {
+ nData = aIn.ReaduInt8();
+ nFactor = 1;
+ }
+ else
+ {
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+ }
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ }
+ break;
+ case 0x1A: // External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 7; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 10; break;
+ case EXC_BIFF5: SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1A does not exist in Biff5!" );
+ [[fallthrough]];
+ default: SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1B: // End External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 3; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 4; break;
+ case EXC_BIFF5: SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1B does not exist in Biff5!" );
+ [[fallthrough]];
+ default: SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nIgnore = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ nIgnore = 2;
+ break;
+ case 0x1F: // Number [315 266]
+ nIgnore = 8;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nIgnore = (meBiff == EXC_BIFF2) ? 6 : 7;
+ break;
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ nIgnore = (meBiff <= EXC_BIFF3) ? 1 : 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ nIgnore = (meBiff <= EXC_BIFF3) ? 2 : 3;
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 7; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 10; break;
+ case EXC_BIFF5: nIgnore = 14; break;
+ default: OSL_FAIL( "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aSRD.SetAbsCol(static_cast<SCCOL>(nByte));
+ aSRD.SetAbsRow(nUINT16 & 0x3FFF);
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ nIgnore = (meBiff == EXC_BIFF2) ? 4 : 6;
+ break;
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nIgnore = 3;
+ break;
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nIgnore = 6;
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8(); // >> Attribute, Row >> Col
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ nIgnore = (meBiff == EXC_BIFF2) ? 1 : 2;
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nIgnore = 2;
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nIgnore = 24;
+ break;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRow;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nCol;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a SingleRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ bool b3D = ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName;
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D( b3D );
+
+ ExcRelToScRel( nRow, nCol, aSRD, bRangeName );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aSRD;
+ aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(static_cast<SCTAB>(nTabLast));
+ b3D = ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() );
+ aCRD.Ref2.SetFlag3D( b3D );
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ else
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRowFirst, nRowLast;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nColFirst, nColLast;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a CompleteRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ // first part of range
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D( ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName );
+ rR2.SetFlag3D( ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() ) || bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }//END in current Workbook
+ }
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ nIgnore = 17;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nIgnore = 20;
+ break;
+ default: bError = true;
+ }
+ bError |= !aIn.IsValid();
+
+ aIn.Ignore( nIgnore );
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ eRet = ConvErr::Ni;
+ else if( aIn.GetRecPos() != nEndPos )
+ eRet = ConvErr::Count;
+ else
+ eRet = ConvErr::OK;
+
+ aIn.Seek( nEndPos );
+ return eRet;
+}
+
+void ExcelToSc::ConvertExternName( std::unique_ptr<ScTokenArray>& /*rpArray*/, XclImpStream& /*rStrm*/, std::size_t /*nFormulaLen*/,
+ const OUString& /*rUrl*/, const vector<OUString>& /*rTabNames*/ )
+{
+}
+
+void ExcelToSc::GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF5 );
+ if( GetBiff() != EXC_BIFF5 )
+ return;
+
+ sal_uInt8 nOp;
+ sal_uInt16 nRow1, nRow2;
+ sal_uInt8 nCol1, nCol2;
+ SCTAB nTab1, nTab2;
+ sal_uInt16 nTabFirst, nTabLast;
+ sal_Int16 nRefIdx;
+
+ std::size_t nSeek;
+ std::size_t nEndPos = rStrm.GetRecPos() + nLen;
+
+ while( rStrm.IsValid() && (rStrm.GetRecPos() < nEndPos) )
+ {
+ nOp = rStrm.ReaduInt8();
+ nSeek = 0;
+
+ switch( nOp )
+ {
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ nRow1 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ // Area Reference Within a Shared Formula[ 274]
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+ nCol2 = rStrm.ReaduInt8();
+
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nTabFirst = rStrm.ReaduInt16();
+ nTabLast = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+
+ goto _3d_common;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nTabFirst = rStrm.ReaduInt16();
+ nTabLast = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+ nCol2 = rStrm.ReaduInt8();
+
+ _3d_common:
+ nTab1 = static_cast< SCTAB >( nTabFirst );
+ nTab2 = static_cast< SCTAB >( nTabLast );
+
+ // skip references to deleted sheets
+ if( (nRefIdx >= 0) || !ValidTab( nTab1 ) || (nTab1 != nTab2) )
+ break;
+
+ goto _common;
+ _common:
+ // do not check abs/rel flags, linked controls have set them!
+ {
+ ScRange aScRange;
+ nRow1 &= 0x3FFF;
+ nRow2 &= 0x3FFF;
+ if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) )
+ rRangeList.push_back( aScRange );
+ }
+ break;
+
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nSeek = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nSeek = 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nSeek = 3;
+ break;
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ nSeek = 4;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nSeek = 6;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nSeek = 7;
+ break;
+ case 0x1F: // Number [315 266]
+ nSeek = 8;
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ nSeek = 14;
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ nSeek = 17;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nSeek = 20;
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nSeek = 24;
+ break;
+ case 0x17: // String Constant [314 266]
+ nSeek = rStrm.ReaduInt8();
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt8 nOpt;
+ sal_uInt16 nData;
+ nOpt = rStrm.ReaduInt8();
+ nData = rStrm.ReaduInt16();
+ if( nOpt & 0x04 )
+ nSeek = nData * 2 + 2;
+ }
+ break;
+ }
+
+ rStrm.Ignore( nSeek );
+ }
+ rStrm.Seek( nEndPos );
+}
+
+void ExcelToSc::DoMulArgs( DefTokenId eId, sal_uInt8 nCnt )
+{
+ TokenId eParam[ 256 ];
+ sal_Int32 nPass;
+
+ if( eId == ocCeil || eId == ocFloor )
+ {
+ aStack << aPool.Store( 1.0 ); // default, because not present in Excel
+ nCnt++;
+ }
+
+ for( nPass = 0; aStack.HasMoreTokens() && (nPass < nCnt); nPass++ )
+ aStack >> eParam[ nPass ];
+ // #i70925# reduce parameter count, if no more tokens available on token stack
+ if( nPass < nCnt )
+ nCnt = static_cast< sal_uInt8 >( nPass );
+
+ if( nCnt > 0 && eId == ocExternal )
+ {
+ TokenId n = eParam[ nCnt - 1 ];
+//##### ADJUST STUPIDITY FOR BASIC-FUNCS!
+ if( const OUString* pExt = aPool.GetExternal( n ) )
+ {
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclMacroName( *pExt ) )
+ aPool << pFuncInfo->meOpCode;
+ else
+ aPool << n;
+ nCnt--;
+ }
+ else
+ aPool << eId;
+ }
+ else
+ aPool << eId;
+
+ aPool << ocOpen;
+
+ if( nCnt > 0 )
+ {
+ // attention: 0 = last parameter, nCnt-1 = first parameter
+ sal_Int16 nSkipEnd = -1; // skip all parameters <= nSkipEnd
+
+ sal_Int16 nLast = nCnt - 1;
+
+ // functions for which parameters have to be skipped
+ if( eId == ocPercentrank && nCnt == 3 )
+ nSkipEnd = 0; // skip last parameter if necessary
+
+ // Joost special cases
+ else if( eId == ocIf )
+ {
+ sal_uInt16 nNullParam = 0;
+ for( nPass = 0 ; nPass < nCnt ; nPass++ )
+ {
+ if( aPool.IsSingleOp( eParam[ nPass ], ocMissing ) )
+ {
+ if( !nNullParam )
+ nNullParam = static_cast<sal_uInt16>(aPool.Store( 0.0 ));
+ eParam[ nPass ] = nNullParam;
+ }
+ }
+ }
+
+ // [Parameter{;Parameter}]
+ if( nLast > nSkipEnd )
+ {
+ // nSkipEnd is either 0 or -1 => nLast >= 0
+ aPool << eParam[ nLast ];
+ for( nPass = nLast - 1 ; nPass > nSkipEnd ; nPass-- )
+ {
+ // nPass > nSkipEnd => nPass >= 0
+ aPool << ocSep << eParam[nPass];
+ }
+ }
+ }
+ aPool << ocClose;
+
+ aPool >> aStack;
+}
+
+void ExcelToSc::ExcRelToScRel( sal_uInt16 nRow, sal_uInt8 nCol, ScSingleRefData &rSRD, const bool bName )
+{
+ if( bName )
+ {
+ // C O L
+ if( nRow & 0x4000 )
+ rSRD.SetRelCol(nCol);
+ else
+ rSRD.SetAbsCol(nCol);
+
+ // R O W
+ if( nRow & 0x8000 )
+ {// rel Row
+ if( nRow & 0x2000 ) // Bit 13 set?
+ // Row negative
+ rSRD.SetRelRow(nRow | 0xC000);
+ else
+ // Row positive
+ rSRD.SetRelRow(nRow & nRowMask);
+ }
+ else
+ {// abs Row
+ rSRD.SetAbsRow(nRow & nRowMask);
+ }
+
+ // T A B
+ // abs needed if rel in shared formula for ScCompiler UpdateNameReference
+ if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() )
+ rSRD.SetAbsTab(GetCurrScTab());
+ }
+ else
+ {
+ bool bColRel = (nRow & 0x4000) > 0;
+ bool bRowRel = (nRow & 0x8000) > 0;
+
+ if (bColRel)
+ rSRD.SetRelCol(nCol - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(nCol);
+
+ rSRD.SetAbsRow(nRow & nRowMask);
+ if (bRowRel)
+ rSRD.SetRelRow(rSRD.Row() - aEingPos.Row());
+
+ // T A B
+ // #i10184# abs needed if rel in shared formula for ScCompiler UpdateNameReference
+ if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() )
+ rSRD.SetAbsTab(GetCurrScTab() + rSRD.Tab());
+ }
+}
+
+std::unique_ptr<ScTokenArray> ExcelToSc::GetBoolErr( XclBoolError eType )
+{
+ FormulaError nError;
+ aPool.Reset();
+ aStack.Reset();
+
+ DefTokenId eOc;
+
+ switch( eType )
+ {
+ case xlErrNull: eOc = ocStop; nError = FormulaError::NoCode; break;
+ case xlErrDiv0: eOc = ocStop; nError = FormulaError::DivisionByZero; break;
+ case xlErrValue: eOc = ocStop; nError = FormulaError::NoValue; break;
+ case xlErrRef: eOc = ocStop; nError = FormulaError::NoRef; break;
+ case xlErrName: eOc = ocStop; nError = FormulaError::NoName; break;
+ case xlErrNum: eOc = ocStop; nError = FormulaError::IllegalFPOperation; break;
+ case xlErrNA: eOc = ocNotAvail; nError = FormulaError::NotAvailable; break;
+ case xlErrTrue: eOc = ocTrue; nError = FormulaError::NONE; break;
+ case xlErrFalse: eOc = ocFalse; nError = FormulaError::NONE; break;
+ case xlErrUnknown: eOc = ocStop; nError = FormulaError::UnknownState; break;
+ default:
+ OSL_FAIL( "ExcelToSc::GetBoolErr - wrong enum!" );
+ eOc = ocNoName;
+ nError = FormulaError::UnknownState;
+ }
+
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+
+ aPool >> aStack;
+
+ std::unique_ptr<ScTokenArray> pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ if( nError != FormulaError::NONE )
+ pResult->SetCodeError( nError );
+
+ pResult->SetExclusiveRecalcModeNormal();
+
+ return pResult;
+}
+
+bool ExcelToSc::ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow )
+{
+ rStrm.PushPosition();
+
+ sal_uInt8 nOp;
+ nOp = rStrm.ReaduInt8();
+
+ if (nOp != 0x01) // must be PtgExp token.
+ {
+ rStrm.PopPosition();
+ return false;
+ }
+
+ sal_uInt16 nRow, nCol;
+ nRow = rStrm.ReaduInt16();
+ nCol = rStrm.ReaduInt16();
+ rStrm.PopPosition();
+ rCol = nCol;
+ rRow = nRow;
+ return true;
+}
+
+const ScTokenArray* ExcelToSc::GetSharedFormula( const ScAddress& rRefPos ) const
+{
+ return GetOldRoot().pShrfmlaBuff->Find(rRefPos);
+}
+
+void ExcelToSc::SetError( ScFormulaCell &rCell, const ConvErr eErr )
+{
+ FormulaError nInd;
+
+ switch( eErr )
+ {
+ case ConvErr::Ni: nInd = FormulaError::UnknownToken; break;
+ case ConvErr::Count: nInd = FormulaError::CodeOverflow; break;
+ default: nInd = FormulaError::NoCode; // I had no better idea
+ }
+
+ rCell.SetErrCode( nInd );
+}
+
+void ExcelToSc::SetComplCol( ScComplexRefData &rCRD )
+{
+ ScSingleRefData &rSRD = rCRD.Ref2;
+ ScDocument& rDoc = GetDocImport().getDoc();
+ if( rSRD.IsColRel() )
+ rSRD.SetRelCol(rDoc.MaxCol() - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(rDoc.MaxCol());
+}
+
+void ExcelToSc::SetComplRow( ScComplexRefData &rCRD )
+{
+ ScSingleRefData &rSRD = rCRD.Ref2;
+ ScDocument& rDoc = GetDocImport().getDoc();
+ if( rSRD.IsRowRel() )
+ rSRD.SetRelRow(rDoc.MaxRow() - aEingPos.Row());
+ else
+ rSRD.SetAbsRow(rDoc.MaxRow());
+}
+
+void ExcelToSc::ReadExtensionArray( unsigned int n, XclImpStream& aIn )
+{
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+
+ SCSIZE nC, nCols;
+ SCSIZE nR, nRows;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ nCols = nByte + 1;
+ nRows = nUINT16 + 1;
+ }
+ else
+ {
+ nCols = nByte ? nByte : 256;
+ nRows = nUINT16;
+ }
+
+ ScMatrix* pMatrix = aPool.GetMatrix( n );
+
+ if( nullptr != pMatrix )
+ {
+ pMatrix->Resize(nCols, nRows);
+ pMatrix->GetDimensions( nC, nR);
+ if( nC != nCols || nR != nRows )
+ {
+ OSL_FAIL( "ExcelToSc::ReadExtensionArray - matrix size mismatch" );
+ pMatrix = nullptr;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ExcelToSc::ReadExtensionArray - missing matrix" );
+ }
+
+ //assuming worst case scenario of unknown types
+ const size_t nMinRecordSize = 1;
+ const size_t nMaxRows = aIn.GetRecLeft() / (nMinRecordSize * nCols);
+ if (nRows > nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << nRows << " claimed, truncating");
+ nRows = nMaxRows;
+ }
+
+ svl::SharedStringPool& rPool = GetDoc().GetSharedStringPool();
+ for( nR = 0 ; nR < nRows; nR++ )
+ {
+ for( nC = 0 ; nC < nCols; nC++ )
+ {
+ nByte = aIn.ReaduInt8();
+ switch( nByte )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ aIn.Ignore( 8 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutEmpty( nC, nR );
+ }
+ break;
+
+ case EXC_CACHEDVAL_DOUBLE:
+ {
+ double fDouble = aIn.ReadDouble();
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutDouble( fDouble, nC, nR );
+ }
+ break;
+ }
+ case EXC_CACHEDVAL_STRING:
+ {
+ OUString aString;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ nUINT16 = aIn.ReaduInt16();
+ aString = aIn.ReadUniString( nUINT16 );
+ }
+ else
+ {
+ nByte = aIn.ReaduInt8();
+ aString = aIn.ReadRawByteString( nByte );
+ }
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutString(rPool.intern(aString), nC, nR);
+ }
+ break;
+ }
+ case EXC_CACHEDVAL_BOOL:
+ nByte = aIn.ReaduInt8();
+ aIn.Ignore( 7 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutBoolean( nByte != 0, nC, nR );
+ }
+ break;
+
+ case EXC_CACHEDVAL_ERROR:
+ nByte = aIn.ReaduInt8();
+ aIn.Ignore( 7 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutError( XclTools::GetScErrorCode( nByte ), nC, nR );
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ExcelToSc::ReadExtensionNlr( XclImpStream& aIn )
+{
+ sal_uInt32 nFlags;
+ nFlags = aIn.ReaduInt32();
+
+ sal_uInt32 nCount = nFlags & EXC_TOK_NLR_ADDMASK;
+ aIn.Ignore( nCount * 4 ); // Drop the cell positions
+}
+
+void ExcelToSc::ReadExtensionMemArea( XclImpStream& aIn )
+{
+ sal_uInt16 nCount = aIn.ReaduInt16();
+
+ aIn.Ignore( static_cast<std::size_t>(nCount) * ((GetBiff() == EXC_BIFF8) ? 8 : 6) ); // drop the ranges
+}
+
+void ExcelToSc::ReadExtensions( const ExtensionTypeVec& rExtensions,
+ XclImpStream& aIn )
+{
+ unsigned int nArray = 0;
+
+ for(int eType : rExtensions)
+ {
+ switch( eType )
+ {
+ case EXTENSION_ARRAY:
+ ReadExtensionArray( nArray++, aIn );
+ break;
+
+ case EXTENSION_NLR:
+ ReadExtensionNlr( aIn );
+ break;
+
+ case EXTENSION_MEMAREA:
+ ReadExtensionMemArea( aIn );
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excform8.cxx b/sc/source/filter/excel/excform8.cxx
new file mode 100644
index 0000000000..00c01923e2
--- /dev/null
+++ b/sc/source/filter/excel/excform8.cxx
@@ -0,0 +1,1672 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excform.hxx>
+
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <docsh.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+
+#include <externalrefmgr.hxx>
+
+#include <cstring>
+
+#include <o3tl/safeint.hxx>
+
+using ::std::vector;
+
+namespace {
+
+/**
+ * Extract a file path from OLE link path. An OLE link path is expected to
+ * be in the following format:
+ *
+ * Excel.Sheet.8 \3 [file path]
+ */
+bool extractFilePath(const OUString& rUrl, OUString& rPath)
+{
+ const char* prefix = "Excel.Sheet.8\3";
+ size_t nPrefixLen = ::std::strlen(prefix);
+
+ sal_Int32 n = rUrl.getLength();
+ if (n <= static_cast<sal_Int32>(nPrefixLen))
+ // needs to have the specified prefix.
+ return false;
+
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rUrl.getStr();
+ for (size_t i = 0; i < o3tl::make_unsigned(n); ++i, ++p)
+ {
+ if (i < nPrefixLen)
+ {
+ sal_Unicode pc = static_cast<sal_Unicode>(*prefix++);
+ if (pc != *p)
+ return false;
+
+ continue;
+ }
+ aBuf.append(*p);
+ }
+
+ rPath = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+ExcelToSc8::ExternalTabInfo::ExternalTabInfo() :
+ mnFileId(0), mbExternal(false)
+{
+}
+
+ExcelToSc8::ExcelToSc8( XclImpRoot& rRoot ) :
+ ExcelToSc( rRoot ),
+ rLinkMan( rRoot.GetLinkManager() )
+{
+}
+
+ExcelToSc8::~ExcelToSc8()
+{
+}
+
+bool ExcelToSc8::GetExternalFileIdFromXti( sal_uInt16 nIxti, sal_uInt16& rFileId ) const
+{
+ const OUString* pFileUrl = rLinkMan.GetSupbookUrl(nIxti);
+ if (!pFileUrl || pFileUrl->isEmpty() || !GetDocShell())
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(*pFileUrl, GetDocShell());
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ rFileId = pRefMgr->getExternalFileId(aFileUrl);
+
+ return true;
+}
+
+bool ExcelToSc8::Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo )
+{
+ rFirstTab = rLastTab = 0;
+ rExtInfo.mbExternal = !rLinkMan.IsSelfRef(nIxti);
+ bool bSuccess = rLinkMan.GetScTabRange(rFirstTab, rLastTab, nIxti);
+ if (!bSuccess)
+ return false;
+
+ if (!rExtInfo.mbExternal)
+ // This is internal reference. Stop here.
+ return true;
+
+ rExtInfo.maTabName = rLinkMan.GetSupbookTabName(nIxti, rFirstTab);
+ return GetExternalFileIdFromXti(nIxti, rExtInfo.mnFileId);
+}
+
+bool ExcelToSc8::HandleOleLink(sal_uInt16 nXtiIndex, const XclImpExtName& rExtName, ExternalTabInfo& rExtInfo)
+{
+ const OUString* pUrl = rLinkMan.GetSupbookUrl(nXtiIndex);
+ if (!pUrl)
+ return false;
+
+ OUString aPath;
+ if (!extractFilePath(*pUrl, aPath))
+ // file path extraction failed.
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(aPath, GetDocShell());
+ return rExtName.CreateOleData(GetDoc(), aFileUrl, rExtInfo.mnFileId, rExtInfo.maTabName, rExtInfo.maRange);
+}
+
+// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
+// otherwise it will seek to the first byte past additional content after <nFormulaLen>
+ConvErr ExcelToSc8::Convert( std::unique_ptr<ScTokenArray>& rpTokArray, XclImpStream& aIn, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT )
+{
+ bool bError = false;
+ bool bArrayFormula = false;
+ TokenId nBuf0;
+ const bool bCondFormat = eFT == FT_CondFormat;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bRangeNameOrCond = bRangeName || bCondFormat;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeNameOrCond || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ ExtensionTypeVec aExtensions;
+
+ if( nFormulaLen == 0 )
+ {
+ aPool.Store( "-/-" );
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return ConvErr::OK;
+ }
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ sal_uInt8 nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ aIn.Ignore( 4 );
+
+ bArrayFormula = true;
+ break;
+ case 0x03: // Addition [312 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocAdd << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x04: // Subtraction [313 264]
+ // SECOND-TOP minus TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocSub << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x05: // Multiplication [313 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocMul << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x06: // Division [313 264]
+ // divide TOP by SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocDiv << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x07: // Exponentiation [313 265]
+ // raise SECOND-TOP to power of TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocPow << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x08: // Concatenation [313 265]
+ // append TOP to SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocAmpersand << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x09: // Less Than [313 265]
+ // SECOND-TOP < TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLess << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0A: // Less Than or Equal [313 265]
+ // SECOND-TOP <= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLessEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0B: // Equal [313 265]
+ // SECOND-TOP == TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0C: // Greater Than or Equal [313 265]
+ // SECOND-TOP >= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreaterEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0D: // Greater Than [313 265]
+ // SECOND-TOP > TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreater << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0E: // Not Equal [313 265]
+ // SECOND-TOP != TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocNotEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0F: // Intersection [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocIntersect << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x10: // Union [314 265]
+ // ocSep instead of 'ocUnion'
+ aStack >> nBuf0;
+ aPool << aStack << ocSep << nBuf0;
+ // doesn't fit exactly, but is more Excel-like
+ aPool >> aStack;
+ break;
+ case 0x11: // Range [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocRange << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x12: // Unary Plus [312 264]
+ aPool << ocAdd << aStack;
+ aPool >> aStack;
+ break;
+ case 0x13: // Unary Minus [312 264]
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+ case 0x14: // Percent Sign [312 264]
+ aPool << aStack << ocPercentSign;
+ aPool >> aStack;
+ break;
+ case 0x15: // Parenthesis [326 278]
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case 0x16: // Missing Argument [314 266]
+ aPool << ocMissing;
+ aPool >> aStack;
+ GetTracer().TraceFormulaMissingArg();
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ sal_uInt8 nLen = aIn.ReaduInt8(); // Why?
+ OUString aString = aIn.ReadUniString( nLen ); // reads Grbit even if nLen==0
+
+ aStack << aPool.Store( aString );
+ break;
+ }
+ case 0x18: // natural language formula
+ {
+ sal_uInt8 nEptg;
+ sal_uInt16 nCol, nRow;
+ nEptg = aIn.ReaduInt8();
+ switch( nEptg )
+ { // name size ext type
+ case 0x01: // Lel 4 - err
+ aIn.Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x02: // Rw 4 - ref
+ case 0x03: // Col 4 - ref
+ case 0x06: // RwV 4 - val
+ case 0x07: // ColV 4 - val
+ {
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+ ScAddress aAddr(static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab());
+ aSRD.InitAddress(aAddr);
+
+ if( nEptg == 0x02 || nEptg == 0x06 )
+ aSRD.SetRowRel(true);
+ else
+ aSRD.SetColRel(true);
+
+ aSRD.SetAddress(GetDocImport().getDoc().GetSheetLimits(), aAddr, aEingPos);
+
+ aStack << aPool.StoreNlf( aSRD );
+
+ break;
+ }
+ case 0x0A: // Radical 13 - ref
+ {
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+ aIn.Ignore( 9 );
+ ScAddress aAddr(static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab());
+ aSRD.InitAddress(aAddr);
+ aSRD.SetColRel(true);
+ aSRD.SetAddress(GetDocImport().getDoc().GetSheetLimits(), aAddr, aEingPos);
+
+ aStack << aPool.StoreNlf( aSRD );
+
+ break;
+ }
+ case 0x0B: // RadicalS 13 x ref
+ aIn.Ignore( 13 );
+ aExtensions.push_back( EXTENSION_NLR );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x0C: // RwS 4 x ref
+ case 0x0D: // ColS 4 x ref
+ case 0x0E: // RwSV 4 x val
+ case 0x0F: // ColSV 4 x val
+ aIn.Ignore( 4 );
+ aExtensions.push_back( EXTENSION_NLR );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x10: // RadicalLel 4 - err
+ case 0x1D: // SxName 4 - val
+ aIn.Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ default:
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ }
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ nData++;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ else if( nOpt & 0x10 ) // AttrSum
+ DoMulArgs( ocSum, 1 );
+ break;
+ }
+ case 0x1C: // Error Value [314 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+
+ break;
+ }
+ case 0x1D: // Boolean [315 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ if( nByte == 0 )
+ aPool << ocFalse << ocOpen << ocClose;
+ else
+ aPool << ocTrue << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1E: // Integer [315 266]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aStack << aPool.Store( static_cast<double>(nUINT16) );
+ break;
+ }
+ case 0x1F: // Number [315 266]
+ {
+ double fDouble = aIn.ReadDouble();
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ {
+ aIn.Ignore( 7 );
+ if( bAllowArrays )
+ {
+ aStack << aPool.StoreMatrix();
+ aExtensions.push_back( EXTENSION_ARRAY );
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ {
+ sal_uInt16 nXclFunc;
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ break;
+ }
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ {
+ sal_uInt16 nXclFunc;
+ sal_uInt8 nParamCount;
+ nParamCount = aIn.ReaduInt8();
+ nXclFunc = aIn.ReaduInt16();
+ nParamCount &= 0x7F;
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, nParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ break;
+ }
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aIn.Ignore( 2 );
+ const XclImpName* pName = GetNameManager().GetName( nUINT16 );
+ if (pName)
+ {
+ if (pName->IsMacro())
+ // user-defined macro name.
+ aStack << aPool.Store(ocMacro, pName->GetXclName());
+ else
+ aStack << aPool.StoreName(nUINT16, pName->IsGlobal() ? -1 : pName->GetScTab());
+ }
+ break;
+ }
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ {
+ sal_uInt16 nCol, nRow;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRangeNameOrCond );
+
+ switch ( nOp )
+ {
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeNameOrCond );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeNameOrCond );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ // no information which part is deleted, set all
+ rSRef1.SetColDeleted( true );
+ rSRef1.SetRowDeleted( true );
+ rSRef2.SetColDeleted( true );
+ rSRef2.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ aExtensions.push_back( EXTENSION_MEMAREA );
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nRow, nCol;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF );
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ bool bColRel = aCRD.Ref1.IsColRel() || aCRD.Ref2.IsColRel();
+ bool bRowRel = aCRD.Ref1.IsRowRel() || aCRD.Ref2.IsRowRel();
+
+ if( !bColRel && IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( !bRowRel && IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ {
+ OUString aString = "COMM_EQU_FUNC";
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aString += OUString::number( nByte );
+ nByte = aIn.ReaduInt8();
+ aStack << aPool.Store( aString );
+ DoMulArgs( ocPush, nByte + 1 );
+ break;
+ }
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ {
+ sal_uInt16 nXtiIndex, nNameIdx;
+ nXtiIndex = aIn.ReaduInt16();
+ nNameIdx = aIn.ReaduInt16();
+ aIn.Ignore( 2 );
+
+ if( rLinkMan.IsSelfRef( nXtiIndex ) )
+ {
+ // internal defined name with explicit sheet, i.e.: =Sheet1!AnyName
+ const XclImpName* pName = GetNameManager().GetName( nNameIdx );
+ if (pName)
+ {
+ if (pName->GetScRangeData())
+ aStack << aPool.StoreName( nNameIdx, pName->IsGlobal() ? -1 : pName->GetScTab());
+ else
+ aStack << aPool.Store(ocMacro, pName->GetXclName());
+ }
+ }
+ else if( const XclImpExtName* pExtName = rLinkMan.GetExternName( nXtiIndex, nNameIdx ) )
+ {
+ switch( pExtName->GetType() )
+ {
+ case xlExtName:
+ {
+ /* FIXME: enable this code for #i4385# once
+ * external name reference can be stored in ODF,
+ * which remains to be done for #i3740#. Until then
+ * create a #NAME? token. */
+#if 1
+ sal_uInt16 nFileId;
+ if (!GetExternalFileIdFromXti(nXtiIndex, nFileId) || !pExtName->HasFormulaTokens())
+ {
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ break;
+ }
+
+ aStack << aPool.StoreExtName(nFileId, pExtName->GetName());
+ pExtName->CreateExtNameData(GetDoc(), nFileId);
+#else
+ aStack << aPool.Store( ocNoName, pExtName->GetName() );
+#endif
+ }
+ break;
+
+ case xlExtAddIn:
+ {
+ aStack << aPool.Store( ocExternal, pExtName->GetName() );
+ }
+ break;
+
+ case xlExtDDE:
+ {
+ OUString aApplic, aTopic;
+ if( rLinkMan.GetLinkData( aApplic, aTopic, nXtiIndex ) )
+ {
+ TokenId nPar1 = aPool.Store( aApplic );
+ TokenId nPar2 = aPool.Store( aTopic );
+ nBuf0 = aPool.Store( pExtName->GetName() );
+ aPool << ocDde << ocOpen << nPar1 << ocSep << nPar2 << ocSep
+ << nBuf0 << ocClose;
+ aPool >> aStack;
+ pExtName->CreateDdeData( GetDoc(), aApplic, aTopic );
+ GetDoc().SetLinkFormulaNeedingCheck(true);
+ }
+ }
+ break;
+
+ case xlExtEuroConvert:
+ {
+ aStack << aPool.Store( ocEuroConvert, OUString() );
+ }
+ break;
+ case xlExtOLE:
+ {
+ ExternalTabInfo aExtInfo;
+ if (HandleOleLink(nXtiIndex, *pExtName, aExtInfo))
+ {
+ if (aExtInfo.maRange.aStart == aExtInfo.maRange.aEnd)
+ {
+ // single cell
+ aSRD.InitAddress(aExtInfo.maRange.aStart);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ // range
+ aCRD.InitRange(aExtInfo.maRange);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ }
+ else
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ }
+ break;
+ default:
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ }
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ {
+ sal_uInt16 nIxti, nRw, nGrbitCol;
+ SCTAB nTabFirst, nTabLast;
+
+ nIxti = aIn.ReaduInt16();
+ nRw = aIn.ReaduInt16();
+ nGrbitCol = aIn.ReaduInt16();
+
+ ExternalTabInfo aExtInfo;
+ if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo))
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ }
+
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeNameOrCond );
+
+ switch ( nOp )
+ {
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ if (aExtInfo.mbExternal)
+ {
+ // nTabFirst and nTabLast are the indices of the referenced
+ // sheets in the SUPBOOK record, hence do not represent
+ // the actual indices of the original sheets since the
+ // SUPBOOK record only stores referenced sheets and skips
+ // the ones that are not referenced.
+
+ if (nTabLast != nTabFirst)
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ else
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ if ( !ValidTab(nTabFirst))
+ aSRD.SetTabDeleted( true );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aCRD.Ref2.SetTabDeleted( !ValidTab(nTabLast) );
+ aStack << aPool.Store( aCRD );
+ }
+ else
+ aStack << aPool.Store( aSRD );
+ }
+ break;
+ }
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ {
+ sal_uInt16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2;
+ SCTAB nTabFirst, nTabLast;
+ nIxti = aIn.ReaduInt16();
+ nRw1 = aIn.ReaduInt16();
+ nRw2 = aIn.ReaduInt16();
+ nGrbitCol1 = aIn.ReaduInt16();
+ nGrbitCol2 = aIn.ReaduInt16();
+
+ ExternalTabInfo aExtInfo;
+ if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo))
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ }
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nTabFirst != nTabLast );
+
+ ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeNameOrCond );
+ ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeNameOrCond );
+
+ if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRw1, nRw2 ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ // no information which part is deleted, set all
+ rR1.SetColDeleted( true );
+ rR1.SetRowDeleted( true );
+ rR2.SetColDeleted( true );
+ rR2.SetRowDeleted( true );
+ }
+
+ if (aExtInfo.mbExternal)
+ {
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ else
+ {
+ if ( !ValidTab(nTabFirst) )
+ rR1.SetTabDeleted( true );
+ if ( !ValidTab(nTabLast) )
+ rR2.SetTabDeleted( true );
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ }
+ default:
+ bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Ni;
+ }
+ else if( aIn.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Count;
+ }
+ else if( bArrayFormula )
+ {
+ rpTokArray = nullptr;
+ eRet = ConvErr::OK;
+ }
+ else
+ {
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::OK;
+ }
+
+ aIn.Seek( nEndPos );
+
+ if( eRet == ConvErr::OK)
+ ReadExtensions( aExtensions, aIn );
+
+ return eRet;
+}
+
+// stream seeks to first byte after <nFormulaLen>
+ConvErr ExcelToSc8::Convert( ScRangeListTabs& rRangeList, XclImpStream& aIn, std::size_t nFormulaLen,
+ SCTAB nTab, const FORMULA_TYPE eFT )
+{
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ const bool bCondFormat = eFT == FT_CondFormat;
+ const bool bRangeName = eFT == FT_RangeName || bCondFormat;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeName || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+
+ if( nFormulaLen == 0 )
+ return ConvErr::OK;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ aIn.Ignore( 4 );
+ break;
+ case 0x02: // Data Table [325 277]
+ aIn.Ignore( 4 );
+ break;
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x17: // String Constant [314 266]
+ nLen = aIn.ReaduInt8(); // Why?
+
+ aIn.IgnoreUniString( nLen ); // reads Grbit even if nLen==0
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ aIn.Ignore( 1 );
+ break;
+ case 0x1E: // Integer [315 266]
+ aIn.Ignore( 2 );
+ break;
+ case 0x1F: // Number [315 266]
+ aIn.Ignore( 8 );
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ aIn.Ignore( 7 );
+ break;
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ aIn.Ignore( 2 );
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ aIn.Ignore( 3 );
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ aIn.Ignore( 4 );
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ {
+ sal_uInt16 nCol, nRow;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName && !bCondFormat );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRangeName );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName && !bCondFormat );
+ rSRef2.SetFlag3D( bRangeName && !bCondFormat );
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ aIn.Ignore( 3 );
+ break;
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ aIn.Ignore( 6 );
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nRow, nCol;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16( );
+ nColLast = aIn.ReaduInt16();
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ aIn.Ignore( 2 );
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ aIn.Ignore( 24 );
+ break;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ {
+ sal_uInt16 nIxti, nRw, nGrbitCol;
+
+ nIxti = aIn.ReaduInt16();
+ nRw = aIn.ReaduInt16();
+ nGrbitCol = aIn.ReaduInt16();
+
+ SCTAB nFirstScTab, nLastScTab;
+ if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) )
+ {
+ aSRD.SetAbsTab(nFirstScTab);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeName );
+
+ if( nFirstScTab != nLastScTab )
+ {
+ aCRD.Ref1 = aSRD;
+ aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nLastScTab);
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ else
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ {
+ sal_uInt16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2;
+
+ nIxti = aIn.ReaduInt16();
+ nRw1 = aIn.ReaduInt16();
+ nRw2 = aIn.ReaduInt16();
+ nGrbitCol1 = aIn.ReaduInt16();
+ nGrbitCol2 = aIn.ReaduInt16();
+
+ SCTAB nFirstScTab, nLastScTab;
+ if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) )
+ {
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nFirstScTab);
+ rR2.SetAbsTab(nLastScTab);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nFirstScTab != nLastScTab );
+
+ ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeName );
+ ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRw1, nRw2 ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ aIn.Ignore( 6 );
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ aIn.Ignore( 10 );
+ break;
+ default:
+ bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ eRet = ConvErr::Ni;
+ else if( aIn.GetRecPos() != nEndPos )
+ eRet = ConvErr::Count;
+ else
+ eRet = ConvErr::OK;
+
+ aIn.Seek( nEndPos );
+ return eRet;
+}
+
+void ExcelToSc8::ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const vector<OUString>& rTabNames )
+{
+ if( !GetDocShell() )
+ return;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(rUrl, GetDocShell());
+
+ sal_uInt8 nOp, nByte;
+ bool bError = false;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+
+ if (nFormulaLen == 0)
+ {
+ aPool.Store("-/-");
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return;
+ }
+
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFileUrl);
+ sal_uInt16 nTabCount = static_cast< sal_uInt16 >( rTabNames.size() );
+
+ std::size_t nEndPos = rStrm.GetRecPos() + nFormulaLen;
+
+ while( (rStrm.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = rStrm.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp )
+ {
+ case 0x1C: // Error Value
+ {
+ nByte = rStrm.ReaduInt8();
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+ }
+ break;
+ case 0x3A:
+ {
+ // cell reference in external range name
+ sal_uInt16 nExtTab1, nExtTab2, nRow, nGrbitCol;
+ nExtTab1 = rStrm.ReaduInt16();
+ nExtTab2 = rStrm.ReaduInt16();
+ nRow = rStrm.ReaduInt16();
+ nGrbitCol = rStrm.ReaduInt16();
+ if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount)
+ {
+ bError = true;
+ break;
+ }
+
+ aSRD.SetAbsTab(nExtTab1);
+ aSRD.SetFlag3D(true);
+ ExcRelToScRel8(nRow, nGrbitCol, aSRD, true);
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ OUString aTabName = rTabNames[nExtTab1];
+
+ if (nExtTab1 == nExtTab2)
+ {
+ // single cell reference
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aSRD);
+ }
+ else
+ {
+ // area reference
+ aCRD.Ref2.SetAbsTab(nExtTab2);
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD);
+ }
+ }
+ break;
+ case 0x3B:
+ {
+ // area reference
+ sal_uInt16 nExtTab1, nExtTab2, nRow1, nRow2, nGrbitCol1, nGrbitCol2;
+ nExtTab1 = rStrm.ReaduInt16();
+ nExtTab2 = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nGrbitCol1 = rStrm.ReaduInt16();
+ nGrbitCol2 = rStrm.ReaduInt16();
+
+ if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount)
+ {
+ bError = true;
+ break;
+ }
+
+ ScSingleRefData& rR1 = aCRD.Ref1;
+ ScSingleRefData& rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nExtTab1);
+ rR1.SetFlag3D(true);
+ ExcRelToScRel8(nRow1, nGrbitCol1, rR1, true);
+
+ rR2.SetAbsTab(nExtTab2);
+ rR2.SetFlag3D(true);
+ ExcRelToScRel8(nRow2, nGrbitCol2, rR2, true);
+
+ OUString aTabName = rTabNames[nExtTab1];
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD);
+ }
+ break;
+ default:
+ bError = true;
+ }
+ bError |= !rStrm.IsValid();
+ }
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+ else if( rStrm.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+ else
+ {
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+
+ rStrm.Seek(nEndPos);
+}
+
+void ExcelToSc8::ExcRelToScRel8( sal_uInt16 nRow, sal_uInt16 nC, ScSingleRefData &rSRD, const bool bName )
+{
+ const bool bColRel = ( nC & 0x4000 ) != 0;
+ const bool bRowRel = ( nC & 0x8000 ) != 0;
+ const sal_uInt8 nCol = static_cast<sal_uInt8>(nC);
+
+ if( bName )
+ {
+ // C O L
+ if( bColRel )
+ {
+ SCCOL nRelCol = static_cast<sal_Int8>(nC);
+ sal_Int16 nDiff = aEingPos.Col() + nRelCol;
+ if ( nDiff < 0)
+ {
+ // relative column references wrap around
+ nRelCol = static_cast<sal_Int16>(256 + static_cast<int>(nRelCol));
+ }
+ rSRD.SetRelCol(nRelCol);
+ }
+ else
+ rSRD.SetAbsCol(static_cast<SCCOL>(nCol));
+
+ // R O W
+ if( bRowRel )
+ {
+ SCROW nRelRow = static_cast<sal_Int16>(nRow);
+ sal_Int32 nDiff = aEingPos.Row() + nRelRow;
+ if (nDiff < 0)
+ {
+ // relative row references wrap around
+ nRelRow = 65536 + nRelRow;
+ }
+ rSRD.SetRelRow(nRelRow);
+ }
+ else
+ rSRD.SetAbsRow(std::min( static_cast<SCROW>(nRow), GetDoc().MaxRow()));
+ }
+ else
+ {
+ // C O L
+ if ( bColRel )
+ rSRD.SetRelCol(static_cast<SCCOL>(nCol) - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(nCol);
+
+ // R O W
+ if ( bRowRel )
+ rSRD.SetRelRow(static_cast<SCROW>(nRow) - aEingPos.Row());
+ else
+ rSRD.SetAbsRow(nRow);
+ }
+}
+
+// stream seeks to first byte after <nLen>
+void ExcelToSc8::GetAbsRefs( ScRangeList& r, XclImpStream& aIn, std::size_t nLen )
+{
+ sal_uInt8 nOp;
+ sal_uInt16 nRow1, nRow2, nCol1, nCol2;
+ SCTAB nTab1, nTab2;
+ sal_uInt16 nIxti;
+
+ std::size_t nSeek;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nLen;
+
+ while( aIn.IsValid() && (aIn.GetRecPos() < nEndPos) )
+ {
+ nOp = aIn.ReaduInt8();
+ nSeek = 0;
+
+ switch( nOp )
+ {
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ nRow1 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ // Area Reference Within a Shared Formula[ 274]
+ nRow1 = aIn.ReaduInt16();
+ nRow2 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+ nCol2 = aIn.ReaduInt16();
+
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ nIxti = aIn.ReaduInt16();
+ nRow1 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+
+ goto _3d_common;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ nIxti = aIn.ReaduInt16();
+ nRow1 = aIn.ReaduInt16();
+ nRow2 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+ nCol2 = aIn.ReaduInt16();
+
+ _3d_common:
+ // skip references to deleted sheets
+ if( !rLinkMan.GetScTabRange( nTab1, nTab2, nIxti ) || !ValidTab( nTab1 ) || !ValidTab( nTab2 ) )
+ break;
+
+ goto _common;
+ _common:
+ // do not check abs/rel flags, linked controls have set them!
+ {
+ ScRange aScRange;
+ nCol1 &= 0x3FFF;
+ nCol2 &= 0x3FFF;
+ if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) )
+ r.push_back( aScRange );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nSeek = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nSeek = 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ nSeek = 3;
+ break;
+ case 0x01: // Array Formula [325 ]
+ case 0x02: // Data Table [325 277]
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nSeek = 4;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nSeek = 6;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nSeek = 7;
+ break;
+ case 0x1F: // Number [315 266]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nSeek = 8;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nSeek = 10;
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ sal_uInt8 nStrLen;
+ nStrLen = aIn.ReaduInt8();
+ aIn.IgnoreUniString( nStrLen ); // reads Grbit even if nLen==0
+ nSeek = 0;
+ }
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData;
+ sal_uInt8 nOpt;
+ nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ if( nOpt & 0x04 )
+ {// nFactor -> skip bytes or words AttrChoose
+ nData++;
+ nSeek = nData * 2;
+ }
+ }
+ break;
+ }
+
+ aIn.Ignore( nSeek );
+ }
+ aIn.Seek( nEndPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excimp8.cxx b/sc/source/filter/excel/excimp8.cxx
new file mode 100644
index 0000000000..28a819f9d6
--- /dev/null
+++ b/sc/source/filter/excel/excimp8.cxx
@@ -0,0 +1,821 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <excimp8.hxx>
+
+#include <scitems.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <unotools/fltrcfg.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docinf.hxx>
+#include <sot/storage.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <dbdata.hxx>
+#include <globalnames.hxx>
+#include <docoptio.hxx>
+#include <xihelper.hxx>
+#include <xicontent.hxx>
+#include <xilink.hxx>
+#include <xiescher.hxx>
+#include <xistyle.hxx>
+#include <excdefs.hxx>
+
+#include <excform.hxx>
+#include <queryentry.hxx>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <cppuhelper/implbase.hxx>
+#include "xltoolbar.hxx"
+#include <oox/ole/vbaproject.hxx>
+#include <oox/ole/olestorage.hxx>
+
+using namespace com::sun::star;
+using namespace ::comphelper;
+
+//OleNameOverrideContainer
+
+namespace {
+
+class OleNameOverrideContainer : public ::cppu::WeakImplHelper< container::XNameContainer >
+{
+private:
+ typedef std::unordered_map< OUString, uno::Reference< container::XIndexContainer > > NamedIndexToOleName;
+ NamedIndexToOleName IdToOleNameHash;
+ std::mutex m_aMutex;
+public:
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<container::XIndexContainer>::get(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ return ( !IdToOleNameHash.empty() );
+ }
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ auto it = IdToOleNameHash.find( aName );
+ if ( it == IdToOleNameHash.end() )
+ throw container::NoSuchElementException();
+ return uno::Any( it->second );
+ }
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ return comphelper::mapKeysToSequence( IdToOleNameHash);
+ }
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ return ( IdToOleNameHash.find( aName ) != IdToOleNameHash.end() );
+ }
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ auto it = IdToOleNameHash.find( aName );
+ if ( it != IdToOleNameHash.end() )
+ throw container::ElementExistException();
+ uno::Reference< container::XIndexContainer > xElement;
+ if ( ! ( aElement >>= xElement ) )
+ throw lang::IllegalArgumentException();
+ IdToOleNameHash[ aName ] = xElement;
+ }
+ virtual void SAL_CALL removeByName( const OUString& aName ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ if ( IdToOleNameHash.erase( aName ) == 0 )
+ throw container::NoSuchElementException();
+ }
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ std::unique_lock aGuard( m_aMutex );
+ auto it = IdToOleNameHash.find( aName );
+ if ( it == IdToOleNameHash.end() )
+ throw container::NoSuchElementException();
+ uno::Reference< container::XIndexContainer > xElement;
+ if ( ! ( aElement >>= xElement ) )
+ throw lang::IllegalArgumentException();
+ it->second = xElement;
+ }
+};
+
+/** Future Record Type header.
+ @return whether read rt matches nRecordID
+ */
+bool readFrtHeader( XclImpStream& rStrm, sal_uInt16 nRecordID )
+{
+ sal_uInt16 nRt = rStrm.ReaduInt16();
+ rStrm.Ignore(10); // grbitFrt (2 bytes) and reserved (8 bytes)
+ return nRt == nRecordID;
+}
+
+}
+
+ImportExcel8::ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm ) :
+ ImportExcel( rImpData, rStrm )
+{
+ // replace BIFF2-BIFF5 formula importer with BIFF8 formula importer
+ pFormConv.reset(new ExcelToSc8( GetRoot() ));
+ pExcRoot->pFmlaConverter = pFormConv.get();
+}
+
+ImportExcel8::~ImportExcel8()
+{
+}
+
+void ImportExcel8::Calccount()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIterCount( aIn.ReaduInt16() );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Precision()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetCalcAsShown( aIn.ReaduInt16() == 0 );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Delta()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIterEps( aIn.ReadDouble() );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Iteration()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIter( aIn.ReaduInt16() == 1 );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Boundsheet()
+{
+ sal_uInt8 nLen;
+ sal_uInt16 nGrbit;
+
+ aIn.DisableDecryption();
+ maSheetOffsets.push_back( aIn.ReaduInt32() );
+ aIn.EnableDecryption();
+ nGrbit = aIn.ReaduInt16();
+ nLen = aIn.ReaduInt8();
+
+ OUString aName( aIn.ReadUniString( nLen ) );
+ GetTabInfo().AppendXclTabName( aName, nBdshtTab );
+
+ SCTAB nScTab = nBdshtTab;
+ if( nScTab > 0 )
+ {
+ OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel8::Boundsheet - sheet exists already" );
+ rD.MakeTable( nScTab );
+ }
+
+ if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
+ rD.SetVisible( nScTab, false );
+
+ if( !rD.RenameTab( nScTab, aName ) )
+ {
+ rD.CreateValidTabName( aName );
+ rD.RenameTab( nScTab, aName );
+ }
+
+ nBdshtTab++;
+}
+
+void ImportExcel8::Scenman()
+{
+ sal_uInt16 nLastDispl;
+
+ aIn.Ignore( 4 );
+ nLastDispl = aIn.ReaduInt16();
+
+ maScenList.nLastScenario = nLastDispl;
+}
+
+void ImportExcel8::Scenario()
+{
+ maScenList.aEntries.push_back( std::make_unique<ExcScenario>( aIn, *pExcRoot ) );
+}
+
+void ImportExcel8::Labelsst()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXF;
+ sal_uInt32 nSst;
+
+ aIn >> aXclPos;
+ nXF = aIn.ReaduInt16();
+ nSst = aIn.ReaduInt32( );
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ GetXFRangeBuffer().SetXF( aScPos, nXF );
+ const XclImpString* pXclStr = GetSst().GetString(nSst);
+ if (pXclStr)
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, *pXclStr, nXF);
+ }
+}
+
+void ImportExcel8::FeatHdr()
+{
+ if (!readFrtHeader( aIn, 0x0867))
+ return;
+
+ // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
+ // EXC_ISFFACTOID.
+ sal_uInt16 nFeatureType = aIn.ReaduInt16();
+ if (nFeatureType != EXC_ISFPROTECTION)
+ // We currently only support import of enhanced protection data.
+ return;
+
+ aIn.Ignore(1); // always 1
+
+ GetSheetProtectBuffer().ReadOptions( aIn, GetCurrScTab() );
+}
+
+void ImportExcel8::Feat()
+{
+ if (!readFrtHeader( aIn, 0x0868))
+ return;
+
+ // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
+ // EXC_ISFFACTOID.
+ sal_uInt16 nFeatureType = aIn.ReaduInt16();
+ if (nFeatureType != EXC_ISFPROTECTION)
+ // We currently only support import of enhanced protection data.
+ return;
+
+ aIn.Ignore(5); // reserved1 (1 byte) and reserved2 (4 bytes)
+
+ sal_uInt16 nCref = aIn.ReaduInt16(); // number of ref elements
+ aIn.Ignore(4); // size if EXC_ISFFEC2, else 0 and to be ignored
+ aIn.Ignore(2); // reserved3 (2 bytes)
+
+ ScEnhancedProtection aProt;
+ if (nCref)
+ {
+ XclRangeList aRefs;
+ aRefs.Read( aIn, true, nCref);
+ if (!aRefs.empty())
+ {
+ aProt.maRangeList = new ScRangeList;
+ GetAddressConverter().ConvertRangeList( *aProt.maRangeList, aRefs, GetCurrScTab(), false);
+ }
+ }
+
+ // FeatProtection structure follows in record.
+
+ aProt.mnAreserved = aIn.ReaduInt32();
+ aProt.mnPasswordVerifier = aIn.ReaduInt32();
+ aProt.maTitle = aIn.ReadUniString();
+ if ((aProt.mnAreserved & 0x00000001) == 0x00000001)
+ {
+ sal_uInt32 nCbSD = aIn.ReaduInt32();
+ // TODO: could here be some sanity check applied to not allocate 4GB?
+ aProt.maSecurityDescriptor.resize( nCbSD);
+ std::size_t nRead = aIn.Read(aProt.maSecurityDescriptor.data(), nCbSD);
+ if (nRead < nCbSD)
+ aProt.maSecurityDescriptor.resize( nRead);
+ }
+
+ GetSheetProtectBuffer().AppendEnhancedProtection( aProt, GetCurrScTab() );
+}
+
+void ImportExcel8::ReadBasic()
+{
+ ScDocShell* pShell = GetDocShell();
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if( !pShell || !xRootStrg.is() )
+ return;
+
+ try
+ {
+ // #FIXME need to get rid of this, we can also do this from within oox
+ // via the "ooo.vba.VBAGlobals" service
+ if( ( rFilterOpt.IsLoadExcelBasicCode() ||
+ rFilterOpt.IsLoadExcelBasicStorage() ) &&
+ rFilterOpt.IsLoadExcelBasicExecutable() )
+ {
+ // see if we have the XCB stream
+ tools::SvRef<SotStorageStream> xXCB = xRootStrg->OpenSotStream( "XCB", StreamMode::STD_READ );
+ if ( xXCB.is()|| ERRCODE_NONE == xXCB->GetError() )
+ {
+ ScCTBWrapper wrapper;
+ if ( wrapper.Read( *xXCB ) )
+ {
+#ifdef DEBUG_SC_EXCEL
+ wrapper.Print( stderr );
+#endif
+ wrapper.ImportCustomToolBar( *pShell );
+ }
+ }
+ }
+ try
+ {
+ uno::Reference< uno::XComponentContext > aCtx( ::comphelper::getProcessComponentContext() );
+ SfxMedium& rMedium = GetMedium();
+ uno::Reference< io::XInputStream > xIn = rMedium.GetInputStream();
+ oox::ole::OleStorage root( aCtx, xIn, false );
+ oox::StorageRef vbaStg = root.openSubStorage( "_VBA_PROJECT_CUR", false );
+ if ( vbaStg )
+ {
+ oox::ole::VbaProject aVbaPrj( aCtx, pShell->GetModel(), u"Calc" );
+ // collect names of embedded form controls, as specified in the VBA project
+ uno::Reference< container::XNameContainer > xOleNameOverrideSink( new OleNameOverrideContainer );
+ aVbaPrj.setOleOverridesSink( xOleNameOverrideSink );
+ aVbaPrj.importVbaProject( *vbaStg );
+ GetObjectManager().SetOleNameOverrideInfo( xOleNameOverrideSink );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ImportExcel8::EndSheet()
+{
+ ImportExcel::EndSheet();
+ GetCondFormatManager().Apply();
+ GetValidationManager().Apply();
+}
+
+void ImportExcel8::PostDocLoad()
+{
+#if HAVE_FEATURE_SCRIPTING
+ // reading basic has been delayed until sheet objects (codenames etc.) are read
+ if( HasBasic() )
+ ReadBasic();
+#endif
+ // #i11776# filtered ranges before outlines and hidden rows
+ if( pExcRoot->pAutoFilterBuffer )
+ pExcRoot->pAutoFilterBuffer->Apply();
+
+ GetWebQueryBuffer().Apply(); //TODO: test if extant
+ GetSheetProtectBuffer().Apply();
+ GetDocProtectBuffer().Apply();
+
+ ImportExcel::PostDocLoad();
+
+ // check scenarios; Attention: This increases the table count of the document!!
+ if( !rD.IsClipboard() && !maScenList.aEntries.empty() )
+ {
+ rD.UpdateChartListenerCollection(); // references in charts must be updated
+
+ maScenList.Apply( GetRoot() );
+ }
+
+ // read doc info (no docshell while pasting from clipboard)
+ ScDocShell* pShell = GetDocShell();
+ if(!pShell)
+ return;
+
+ // BIFF5+ without storage is possible
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ if( xRootStrg.is() ) try
+ {
+ uno::Reference< document::XDocumentPropertiesSupplier > xDPS( static_cast<cppu::OWeakObject*>(pShell->GetModel()), uno::UNO_QUERY_THROW );
+ uno::Reference< document::XDocumentProperties > xDocProps( xDPS->getDocumentProperties(), uno::UNO_SET_THROW );
+ sfx2::LoadOlePropertySet( xDocProps, xRootStrg.get() );
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ // #i45843# Pivot tables are now handled outside of PostDocLoad, so they are available
+ // when formula cells are calculated, for the GETPIVOTDATA function.
+}
+
+// autofilter
+
+void ImportExcel8::FilterMode()
+{
+ // The FilterMode record exists: if either the AutoFilter
+ // record exists or an Advanced Filter is saved and stored
+ // in the sheet. Thus if the FilterMode records only exists
+ // then the latter is true...
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ pData->SetAutoOrAdvanced();
+}
+
+void ImportExcel8::AutoFilterInfo()
+{
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ {
+ pData->SetAdvancedRange( nullptr );
+ pData->Activate();
+ }
+}
+
+void ImportExcel8::AutoFilter()
+{
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ pData->ReadAutoFilter(aIn, GetDoc().GetSharedStringPool());
+}
+
+XclImpAutoFilterData::XclImpAutoFilterData( RootData* pRoot, const ScRange& rRange ) :
+ ExcRoot( pRoot ),
+ pCurrDBData(nullptr),
+ bActive( false ),
+ bCriteria( false ),
+ bAutoOrAdvanced(false)
+{
+ aParam.nCol1 = rRange.aStart.Col();
+ aParam.nRow1 = rRange.aStart.Row();
+ aParam.nTab = rRange.aStart.Tab();
+ aParam.nCol2 = rRange.aEnd.Col();
+ aParam.nRow2 = rRange.aEnd.Row();
+
+ aParam.bInplace = true;
+
+}
+
+namespace {
+
+OUString CreateFromDouble( double fVal )
+{
+ return rtl::math::doubleToUString(fVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+}
+
+}
+
+void XclImpAutoFilterData::SetCellAttribs()
+{
+ ScDocument& rDoc = pExcRoot->pIR->GetDoc();
+ for ( SCCOL nCol = StartCol(); nCol <= EndCol(); nCol++ )
+ {
+ ScMF nFlag = rDoc.GetAttr( nCol, StartRow(), Tab(), ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, StartRow(), Tab(), ScMergeFlagAttr( nFlag | ScMF::Auto) );
+ }
+}
+
+void XclImpAutoFilterData::InsertQueryParam()
+{
+ if (!pCurrDBData)
+ return;
+
+ ScRange aAdvRange;
+ bool bHasAdv = pCurrDBData->GetAdvancedQuerySource( aAdvRange );
+ if( bHasAdv )
+ pExcRoot->pIR->GetDoc().CreateQueryParam(aAdvRange, aParam);
+
+ pCurrDBData->SetQueryParam( aParam );
+ if( bHasAdv )
+ pCurrDBData->SetAdvancedQuerySource( &aAdvRange );
+ else
+ {
+ pCurrDBData->SetAutoFilter( true );
+ SetCellAttribs();
+ }
+}
+
+static void ExcelQueryToOooQuery( OUString& aStr, ScQueryEntry& rEntry )
+{
+ if (rEntry.eOp != SC_EQUAL && rEntry.eOp != SC_NOT_EQUAL)
+ return;
+
+ sal_Int32 nLen = aStr.getLength();
+ sal_Unicode nStart = aStr[0];
+ sal_Unicode nEnd = aStr[ nLen-1 ];
+ if( nLen > 2 && nStart == '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 1, nLen-2 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_CONTAINS : SC_DOES_NOT_CONTAIN;
+ }
+ else if( nLen > 1 && nStart == '*' && nEnd != '*' )
+ {
+ aStr = aStr.copy( 1 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_ENDS_WITH : SC_DOES_NOT_END_WITH;
+ }
+ else if( nLen > 1 && nStart != '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 0, nLen-1 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_BEGINS_WITH : SC_DOES_NOT_BEGIN_WITH;
+ }
+ else if( nLen == 2 && nStart == '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 1 );
+ }
+}
+
+void XclImpAutoFilterData::ReadAutoFilter(
+ XclImpStream& rStrm, svl::SharedStringPool& rPool )
+{
+ sal_uInt16 nCol, nFlags;
+ nCol = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ ScQueryConnect eConn = ::get_flagvalue( nFlags, EXC_AFFLAG_ANDORMASK, SC_OR, SC_AND );
+ bool bSimple1 = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE1);
+ bool bSimple2 = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE2);
+ bool bTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10);
+ bool bTopOfTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10TOP);
+ bool bPercent = ::get_flag(nFlags, EXC_AFFLAG_TOP10PERC);
+ sal_uInt16 nCntOfTop10 = nFlags >> 7;
+
+ if( bTop10 )
+ {
+ ScQueryEntry& aEntry = aParam.AppendEntry();
+ ScQueryEntry::Item& rItem = aEntry.GetQueryItem();
+ aEntry.bDoQuery = true;
+ aEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
+ aEntry.eOp = bTopOfTop10 ?
+ (bPercent ? SC_TOPPERC : SC_TOPVAL) : (bPercent ? SC_BOTPERC : SC_BOTVAL);
+ aEntry.eConnect = SC_AND;
+
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.maString = rPool.intern(OUString::number(nCntOfTop10));
+
+ rStrm.Ignore(20);
+ return;
+ }
+
+ sal_uInt8 nType, nOper, nBoolErr, nVal;
+ sal_Int32 nRK;
+ double fVal;
+
+ sal_uInt8 nStrLen[2] = { 0, 0 };
+ ScQueryEntry aEntries[2];
+
+ for (size_t nE = 0; nE < 2; ++nE)
+ {
+ ScQueryEntry& rEntry = aEntries[nE];
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bIgnore = false;
+
+ nType = rStrm.ReaduInt8();
+ nOper = rStrm.ReaduInt8();
+ switch( nOper )
+ {
+ case EXC_AFOPER_LESS:
+ rEntry.eOp = SC_LESS;
+ break;
+ case EXC_AFOPER_EQUAL:
+ rEntry.eOp = SC_EQUAL;
+ break;
+ case EXC_AFOPER_LESSEQUAL:
+ rEntry.eOp = SC_LESS_EQUAL;
+ break;
+ case EXC_AFOPER_GREATER:
+ rEntry.eOp = SC_GREATER;
+ break;
+ case EXC_AFOPER_NOTEQUAL:
+ rEntry.eOp = SC_NOT_EQUAL;
+ break;
+ case EXC_AFOPER_GREATEREQUAL:
+ rEntry.eOp = SC_GREATER_EQUAL;
+ break;
+ default:
+ rEntry.eOp = SC_EQUAL;
+ }
+
+ switch( nType )
+ {
+ case EXC_AFTYPE_RK:
+ nRK = rStrm.ReadInt32();
+ rStrm.Ignore( 4 );
+ rItem.maString = rPool.intern(
+ CreateFromDouble(XclTools::GetDoubleFromRK(nRK)));
+ break;
+ case EXC_AFTYPE_DOUBLE:
+ fVal = rStrm.ReadDouble();
+ rItem.maString = rPool.intern(CreateFromDouble(fVal));
+ break;
+ case EXC_AFTYPE_STRING:
+ rStrm.Ignore( 4 );
+ nStrLen[ nE ] = rStrm.ReaduInt8();
+ rStrm.Ignore( 3 );
+ rItem.maString = svl::SharedString();
+ break;
+ case EXC_AFTYPE_BOOLERR:
+ nBoolErr = rStrm.ReaduInt8();
+ nVal = rStrm.ReaduInt8();
+ rStrm.Ignore( 6 );
+ rItem.maString = rPool.intern(OUString::number(nVal));
+ bIgnore = (nBoolErr != 0);
+ break;
+ case EXC_AFTYPE_EMPTY:
+ rEntry.SetQueryByEmpty();
+ break;
+ case EXC_AFTYPE_NOTEMPTY:
+ rEntry.SetQueryByNonEmpty();
+ break;
+ default:
+ rStrm.Ignore( 8 );
+ bIgnore = true;
+ }
+
+ if (!bIgnore)
+ {
+ rEntry.bDoQuery = true;
+ rItem.meType = ScQueryEntry::ByString;
+ rEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
+ rEntry.eConnect = nE ? eConn : SC_AND;
+ }
+ }
+
+ if (eConn == SC_AND)
+ {
+ for (size_t nE = 0; nE < 2; ++nE)
+ {
+ if (nStrLen[nE] && aEntries[nE].bDoQuery)
+ {
+ OUString aStr = rStrm.ReadUniString(nStrLen[nE]);
+ ExcelQueryToOooQuery(aStr, aEntries[nE]);
+ aEntries[nE].GetQueryItem().maString = rPool.intern(aStr);
+ aParam.AppendEntry() = aEntries[nE];
+ }
+ }
+ }
+ else
+ {
+ assert( eConn == SC_OR && "eConn should be SC_AND or SC_OR");
+ // Import only when both conditions are for simple equality, else
+ // import only the 1st condition due to conflict with the ordering of
+ // conditions. #i39464#.
+ //
+ // Example: Let A1 be a condition of column A, and B1 and B2
+ // conditions of column B, connected with OR. Excel performs 'A1 AND
+ // (B1 OR B2)' in this case, but Calc would do '(A1 AND B1) OR B2'
+ // instead.
+
+ if (bSimple1 && bSimple2 && nStrLen[0] && nStrLen[1])
+ {
+ // Two simple OR'ed equal conditions. We can import this correctly.
+ ScQueryEntry& rEntry = aParam.AppendEntry();
+ rEntry.bDoQuery = true;
+ rEntry.eOp = SC_EQUAL;
+ rEntry.eConnect = SC_AND;
+ ScQueryEntry::QueryItemsType aItems;
+ aItems.reserve(2);
+ ScQueryEntry::Item aItem1, aItem2;
+ aItem1.maString = rPool.intern(rStrm.ReadUniString(nStrLen[0]));
+ aItem1.meType = ScQueryEntry::ByString;
+ aItem2.maString = rPool.intern(rStrm.ReadUniString(nStrLen[1]));
+ aItem2.meType = ScQueryEntry::ByString;
+ aItems.push_back(aItem1);
+ aItems.push_back(aItem2);
+ rEntry.GetQueryItems().swap(aItems);
+ }
+ else if (nStrLen[0] && aEntries[0].bDoQuery)
+ {
+ // Due to conflict, we can import only the first condition.
+ OUString aStr = rStrm.ReadUniString(nStrLen[0]);
+ ExcelQueryToOooQuery(aStr, aEntries[0]);
+ aEntries[0].GetQueryItem().maString = rPool.intern(aStr);
+ aParam.AppendEntry() = aEntries[0];
+ }
+ }
+}
+
+void XclImpAutoFilterData::SetAdvancedRange( const ScRange* pRange )
+{
+ if (pRange)
+ {
+ aCriteriaRange = *pRange;
+ bCriteria = true;
+ }
+ else
+ bCriteria = false;
+}
+
+void XclImpAutoFilterData::SetExtractPos( const ScAddress& rAddr )
+{
+ aParam.nDestCol = rAddr.Col();
+ aParam.nDestRow = rAddr.Row();
+ aParam.nDestTab = rAddr.Tab();
+ aParam.bInplace = false;
+ aParam.bDestPers = true;
+}
+
+void XclImpAutoFilterData::Apply()
+{
+ // Create the ScDBData() object if the AutoFilter is activated
+ // or if we need to create the Advanced Filter.
+ if( bActive || bCriteria)
+ {
+ ScDocument& rDoc = pExcRoot->pIR->GetDoc();
+ pCurrDBData = new ScDBData(STR_DB_LOCAL_NONAME, Tab(),
+ StartCol(),StartRow(), EndCol(),EndRow() );
+ if(bCriteria)
+ {
+ EnableRemoveFilter();
+
+ pCurrDBData->SetQueryParam( aParam );
+ pCurrDBData->SetAdvancedQuerySource(&aCriteriaRange);
+ }
+ else
+ pCurrDBData->SetAdvancedQuerySource(nullptr);
+ rDoc.SetAnonymousDBData(Tab(), std::unique_ptr<ScDBData>(pCurrDBData));
+ }
+
+ if( bActive )
+ {
+ InsertQueryParam();
+ }
+}
+
+void XclImpAutoFilterData::EnableRemoveFilter()
+{
+ // only if this is a saved Advanced filter
+ if( !bActive && bAutoOrAdvanced )
+ {
+ ScQueryEntry& aEntry = aParam.AppendEntry();
+ aEntry.bDoQuery = true;
+ }
+
+ // TBD: force the automatic activation of the
+ // "Remove Filter" by setting a virtual mouse click
+ // inside the advanced range
+}
+
+void XclImpAutoFilterBuffer::Insert( RootData* pRoot, const ScRange& rRange)
+{
+ if( !GetByTab( rRange.aStart.Tab() ) )
+ maFilters.push_back( std::make_shared<XclImpAutoFilterData>( pRoot, rRange ));
+}
+
+void XclImpAutoFilterBuffer::AddAdvancedRange( const ScRange& rRange )
+{
+ XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
+ if( pData )
+ pData->SetAdvancedRange( &rRange );
+}
+
+void XclImpAutoFilterBuffer::AddExtractPos( const ScRange& rRange )
+{
+ XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
+ if( pData )
+ pData->SetExtractPos( rRange.aStart );
+}
+
+void XclImpAutoFilterBuffer::Apply()
+{
+ for( const auto& rFilterPtr : maFilters )
+ rFilterPtr->Apply();
+}
+
+XclImpAutoFilterData* XclImpAutoFilterBuffer::GetByTab( SCTAB nTab )
+{
+ for( const auto& rFilterPtr : maFilters )
+ {
+ if( rFilterPtr->Tab() == nTab )
+ return rFilterPtr.get();
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx
new file mode 100644
index 0000000000..86afa5a6c1
--- /dev/null
+++ b/sc/source/filter/excel/excrecds.cxx
@@ -0,0 +1,1219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excrecds.hxx>
+
+#include <map>
+#include <filter/msfilter/countryid.hxx>
+
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <string.h>
+
+#include <global.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+#include <sortparam.hxx>
+#include <userlist.hxx>
+#include <root.hxx>
+
+#include <xeescher.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xlname.hxx>
+#include <xestyle.hxx>
+
+#include <xcl97rec.hxx>
+#include <tabprotection.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Sequence;
+
+//--------------------------------------------------------- class ExcDummy_00 -
+const sal_uInt8 ExcDummy_00::pMyData[] = {
+ 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
+};
+const std::size_t ExcDummy_00::nMyLen = sizeof( ExcDummy_00::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_04x -
+const sal_uInt8 ExcDummy_040::pMyData[] = {
+ 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
+ 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
+};
+const std::size_t ExcDummy_040::nMyLen = sizeof( ExcDummy_040::pMyData );
+
+const sal_uInt8 ExcDummy_041::pMyData[] = {
+ 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
+ 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
+};
+const std::size_t ExcDummy_041::nMyLen = sizeof( ExcDummy_041::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_02a -
+const sal_uInt8 ExcDummy_02a::pMyData[] = {
+ 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
+ 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
+ 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
+ 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
+ 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
+ 0x62, 0x50, 0x3f,
+ 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
+};
+const std::size_t ExcDummy_02a::nMyLen = sizeof( ExcDummy_02a::pMyData );
+
+//----------------------------------------------------------- class ExcRecord -
+
+void ExcRecord::Save( XclExpStream& rStrm )
+{
+ SetRecHeader( GetNum(), GetLen() );
+ XclExpRecord::Save( rStrm );
+}
+
+void ExcRecord::SaveCont( XclExpStream& /*rStrm*/ )
+{
+}
+
+void ExcRecord::WriteBody( XclExpStream& rStrm )
+{
+ SaveCont( rStrm );
+}
+
+void ExcRecord::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+//--------------------------------------------------------- class ExcEmptyRec -
+
+void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+sal_uInt16 ExcEmptyRec::GetNum() const
+{
+ return 0;
+}
+
+std::size_t ExcEmptyRec::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummyRec -
+
+void ExcDummyRec::Save( XclExpStream& rStrm )
+{
+ rStrm.Write( GetData(), GetLen() ); // raw write mode
+}
+
+sal_uInt16 ExcDummyRec::GetNum() const
+{
+ return 0x0000;
+}
+
+//------------------------------------------------------- class ExcBoolRecord -
+
+void ExcBoolRecord::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(bVal ? 0x0001 : 0x0000);
+}
+
+std::size_t ExcBoolRecord::GetLen() const
+{
+ return 2;
+}
+
+//--------------------------------------------------------- class ExcBof_Base -
+
+ExcBof_Base::ExcBof_Base()
+ : nDocType(0)
+ , nVers(0)
+ , nRupBuild(0x096C) // copied from Excel
+ , nRupYear(0x07C9) // copied from Excel
+{
+}
+
+//-------------------------------------------------------------- class ExcBof -
+
+ExcBof::ExcBof()
+{
+ nDocType = 0x0010;
+ nVers = 0x0500;
+}
+
+void ExcBof::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBof::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBof::GetLen() const
+{
+ return 8;
+}
+
+//------------------------------------------------------------- class ExcBofW -
+
+ExcBofW::ExcBofW()
+{
+ nDocType = 0x0005;
+ nVers = 0x0500;
+}
+
+void ExcBofW::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBofW::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBofW::GetLen() const
+{
+ return 8;
+}
+
+//-------------------------------------------------------------- class ExcEof -
+
+sal_uInt16 ExcEof::GetNum() const
+{
+ return 0x000A;
+}
+
+std::size_t ExcEof::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummy_00 -
+
+std::size_t ExcDummy_00::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_00::GetData() const
+{
+ return pMyData;
+}
+
+//-------------------------------------------------------- class ExcDummy_04x -
+
+std::size_t ExcDummy_040::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_040::GetData() const
+{
+ return pMyData;
+}
+
+std::size_t ExcDummy_041::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_041::GetData() const
+{
+ return pMyData;
+}
+
+//------------------------------------------------------------- class Exc1904 -
+
+Exc1904::Exc1904( const ScDocument& rDoc )
+{
+ const Date& rDate = rDoc.GetFormatTable()->GetNullDate();
+ bVal = (rDate == Date( 1, 1, 1904 ));
+ bDateCompatibility = (rDate != Date( 30, 12, 1899 ));
+}
+
+sal_uInt16 Exc1904::GetNum() const
+{
+ return 0x0022;
+}
+
+void Exc1904::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bISOIEC = ( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 );
+
+ if( bISOIEC )
+ {
+ rStrm.WriteAttributes(XML_dateCompatibility, ToPsz(bDateCompatibility));
+ }
+
+ if( !bISOIEC || bDateCompatibility )
+ {
+ rStrm.WriteAttributes(XML_date1904, ToPsz(bVal));
+ }
+}
+
+//------------------------------------------------------ class ExcBundlesheet -
+
+ExcBundlesheetBase::ExcBundlesheetBase( const RootData& rRootData, SCTAB nTabNum ) :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( rRootData.pER->GetTabInfo().IsVisibleTab( nTabNum ) ? 0x0000 : 0x0001 ),
+ nTab( nTabNum )
+{
+}
+
+ExcBundlesheetBase::ExcBundlesheetBase() :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( 0x0000 ),
+ nTab( SCTAB_GLOBAL )
+{
+}
+
+void ExcBundlesheetBase::UpdateStreamPos( XclExpStream& rStrm )
+{
+ rStrm.SetSvStreamPos( m_nOwnPos );
+ rStrm.DisableEncryption();
+ rStrm << static_cast<sal_uInt32>(m_nStrPos);
+ rStrm.EnableEncryption();
+}
+
+sal_uInt16 ExcBundlesheetBase::GetNum() const
+{
+ return 0x0085;
+}
+
+ExcBundlesheet::ExcBundlesheet( const RootData& rRootData, SCTAB _nTab ) :
+ ExcBundlesheetBase( rRootData, _nTab )
+{
+ OUString sTabName = rRootData.pER->GetTabInfo().GetScTabName( _nTab );
+ OSL_ENSURE( sTabName.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
+ aName = OUStringToOString(sTabName, rRootData.pER->GetTextEncoding());
+}
+
+void ExcBundlesheet::SaveCont( XclExpStream& rStrm )
+{
+ m_nOwnPos = rStrm.GetSvStreamPos();
+ rStrm << sal_uInt32(0x00000000) // dummy (stream position of the sheet)
+ << nGrbit;
+ rStrm.WriteByteString(aName); // 8 bit length, max 255 chars
+}
+
+std::size_t ExcBundlesheet::GetLen() const
+{
+ return 7 + std::min( aName.getLength(), sal_Int32(255) );
+}
+
+//--------------------------------------------------------- class ExcDummy_02 -
+
+std::size_t ExcDummy_02a::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_02a::GetData() const
+{
+ return pMyData;
+}
+//--------------------------------------------------------- class ExcDummy_02 -
+
+XclExpCountry::XclExpCountry( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_COUNTRY, 4 )
+{
+ /* #i31530# set document country as UI country too -
+ needed for correct behaviour of number formats. */
+ mnUICountry = mnDocCountry = static_cast< sal_uInt16 >(
+ ::msfilter::ConvertLanguageToCountry( rRoot.GetDocLanguage() ) );
+}
+
+void XclExpCountry::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnUICountry << mnDocCountry;
+}
+
+// XclExpWsbool ===============================================================
+
+XclExpWsbool::XclExpWsbool( bool bFitToPages )
+ : XclExpUInt16Record( EXC_ID_WSBOOL, EXC_WSBOOL_DEFAULTFLAGS )
+{
+ if( bFitToPages )
+ SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE );
+}
+
+XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager ) :
+ mnScTab(nScTab), mpManager(pManager), mbFitToPage(bFitToPages), maTabColor(rTabColor) {}
+
+void XclExpXmlSheetPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetPr,
+ // OOXTODO: XML_syncHorizontal,
+ // OOXTODO: XML_syncVertical,
+ // OOXTODO: XML_syncRef,
+ // OOXTODO: XML_transitionEvaluation,
+ // OOXTODO: XML_transitionEntry,
+ // OOXTODO: XML_published,
+ // OOXTODO: XML_codeName,
+ XML_filterMode, mpManager ? ToPsz(mpManager->HasFilterMode(mnScTab)) : nullptr
+ // OOXTODO: XML_enableFormatConditionsCalculation
+ );
+
+ // Note : the order of child elements is significant. Don't change the order.
+
+ // OOXTODO: XML_outlinePr
+
+ if (maTabColor != COL_AUTO)
+ rWorksheet->singleElement(XML_tabColor, XML_rgb, XclXmlUtils::ToOString(maTabColor));
+
+ rWorksheet->singleElement(XML_pageSetUpPr,
+ // OOXTODO: XML_autoPageBreaks,
+ XML_fitToPage, ToPsz(mbFitToPage));
+
+ rWorksheet->endElement( XML_sheetPr );
+}
+
+// XclExpWindowProtection ===============================================================
+
+XclExpWindowProtection::XclExpWindowProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_WINDOWPROTECT, bValue)
+{
+}
+
+void XclExpWindowProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_lockWindows, ToPsz(GetBool()));
+}
+
+// XclExpDocProtection ===============================================================
+
+XclExpProtection::XclExpProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_PROTECT, bValue)
+{
+}
+
+XclExpSheetProtection::XclExpSheetProtection(bool bValue, SCTAB nTab ) :
+ XclExpProtection( bValue),
+ mnTab(nTab)
+{
+}
+
+void XclExpSheetProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScDocument& rDoc = rStrm.GetRoot().GetDoc();
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnTab);
+ if ( !pTabProtect )
+ return;
+
+ const ScOoxPasswordHash& rPH = pTabProtect->getPasswordHash();
+ // Do not write any hash attributes if there is no password.
+ ScOoxPasswordHash aPH;
+ if (rPH.hasPassword())
+ aPH = rPH;
+
+ Sequence<sal_Int8> aHash = pTabProtect->getPasswordHash(PASSHASH_XL);
+ std::optional<OString> sHash;
+ if (aHash.getLength() >= 2)
+ {
+ sHash = OString::number(
+ ( static_cast<sal_uInt8>(aHash[0]) << 8
+ | static_cast<sal_uInt8>(aHash[1]) ),
+ 16 );
+ }
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_sheetProtection,
+ XML_algorithmName, sax_fastparser::UseIf(aPH.maAlgorithmName, !aPH.maAlgorithmName.isEmpty()),
+ XML_hashValue, sax_fastparser::UseIf(aPH.maHashValue, !aPH.maHashValue.isEmpty()),
+ XML_saltValue, sax_fastparser::UseIf(aPH.maSaltValue, !aPH.maSaltValue.isEmpty()),
+ XML_spinCount, sax_fastparser::UseIf(OString::number(aPH.mnSpinCount), aPH.mnSpinCount != 0),
+ XML_sheet, ToPsz( true ),
+ XML_password, sHash,
+ XML_objects, pTabProtect->isOptionEnabled( ScTableProtection::OBJECTS ) ? nullptr : ToPsz( true ),
+ XML_scenarios, pTabProtect->isOptionEnabled( ScTableProtection::SCENARIOS ) ? nullptr : ToPsz( true ),
+ XML_formatCells, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_CELLS ) ? ToPsz( false ) : nullptr,
+ XML_formatColumns, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_formatRows, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertColumns, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_insertRows, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertHyperlinks, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS ) ? ToPsz( false ) : nullptr,
+ XML_deleteColumns, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_deleteRows, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_selectLockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS ) ? nullptr : ToPsz( true ),
+ XML_sort, pTabProtect->isOptionEnabled( ScTableProtection::SORT ) ? ToPsz( false ) : nullptr,
+ XML_autoFilter, pTabProtect->isOptionEnabled( ScTableProtection::AUTOFILTER ) ? ToPsz( false ) : nullptr,
+ XML_pivotTables, pTabProtect->isOptionEnabled( ScTableProtection::PIVOT_TABLES ) ? ToPsz( false ) : nullptr,
+ XML_selectUnlockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS ) ? nullptr : ToPsz( true ) );
+
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ if (rProts.empty())
+ return;
+
+ rWorksheet->startElement(XML_protectedRanges);
+ for (const auto& rProt : rProts)
+ {
+ SAL_WARN_IF( rProt.maSecurityDescriptorXML.isEmpty() && !rProt.maSecurityDescriptor.empty(),
+ "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
+ rWorksheet->singleElement( XML_protectedRange,
+ XML_name, sax_fastparser::UseIf(rProt.maTitle, !rProt.maTitle.isEmpty()),
+ XML_securityDescriptor, sax_fastparser::UseIf(rProt.maSecurityDescriptorXML, !rProt.maSecurityDescriptorXML.isEmpty()),
+ /* XXX 'password' is not part of OOXML, but Excel2013
+ * writes it if loaded from BIFF, in which case
+ * 'algorithmName', 'hashValue', 'saltValue' and
+ * 'spinCount' are absent; so do we if it was present. */
+ XML_password, sax_fastparser::UseIf(OString::number(rProt.mnPasswordVerifier, 16), rProt.mnPasswordVerifier != 0),
+ XML_algorithmName, sax_fastparser::UseIf(rProt.maPasswordHash.maAlgorithmName, !rProt.maPasswordHash.maAlgorithmName.isEmpty()),
+ XML_hashValue, sax_fastparser::UseIf(rProt.maPasswordHash.maHashValue, !rProt.maPasswordHash.maHashValue.isEmpty()),
+ XML_saltValue, sax_fastparser::UseIf(rProt.maPasswordHash.maSaltValue, !rProt.maPasswordHash.maSaltValue.isEmpty()),
+ XML_spinCount, sax_fastparser::UseIf(OString::number(rProt.maPasswordHash.mnSpinCount), rProt.maPasswordHash.mnSpinCount != 0),
+ XML_sqref, rProt.maRangeList.is() ? XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), *rProt.maRangeList).getStr() : nullptr);
+ }
+ rWorksheet->endElement( XML_protectedRanges);
+}
+
+XclExpPassHash::XclExpPassHash(const Sequence<sal_Int8>& aHash) :
+ XclExpRecord(EXC_ID_PASSWORD, 2),
+ mnHash(0x0000)
+{
+ if (aHash.getLength() >= 2)
+ {
+ mnHash = ((aHash[0] << 8) & 0xFFFF);
+ mnHash |= (aHash[1] & 0xFF);
+ }
+}
+
+XclExpPassHash::~XclExpPassHash()
+{
+}
+
+void XclExpPassHash::WriteBody(XclExpStream& rStrm)
+{
+ rStrm << mnHash;
+}
+
+XclExpFiltermode::XclExpFiltermode() :
+ XclExpEmptyRecord( EXC_ID_FILTERMODE )
+{
+}
+
+XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol ) :
+ XclExpUInt16Record( EXC_ID_AUTOFILTERINFO, static_cast< sal_uInt16 >( nScCol ) ),
+ maStartPos( rStartPos )
+{
+}
+
+ExcFilterCondition::ExcFilterCondition() :
+ nType( EXC_AFTYPE_NOTUSED ),
+ nOper( EXC_AFOPER_EQUAL )
+{
+}
+
+ExcFilterCondition::~ExcFilterCondition()
+{
+}
+
+std::size_t ExcFilterCondition::GetTextBytes() const
+{
+ return pText ? (1 + pText->GetBufferSize()) : 0;
+}
+
+void ExcFilterCondition::SetCondition( sal_uInt8 nTp, sal_uInt8 nOp, const OUString* pT )
+{
+ nType = nTp;
+ nOper = nOp;
+ pText.reset( pT ? new XclExpString( *pT, XclStrFlags::EightBitLength ) : nullptr);
+}
+
+void ExcFilterCondition::Save( XclExpStream& rStrm )
+{
+ rStrm << nType << nOper;
+ if (nType == EXC_AFTYPE_STRING)
+ {
+ OSL_ENSURE(pText, "ExcFilterCondition::Save() -- pText is NULL!");
+ rStrm << sal_uInt32(0) << static_cast<sal_uInt8>(pText->Len()) << sal_uInt16(0) << sal_uInt8(0);
+ }
+ else
+ rStrm << sal_uInt32(0) << sal_uInt32(0);
+}
+
+static const char* lcl_GetOperator( sal_uInt8 nOper )
+{
+ switch( nOper )
+ {
+ case EXC_AFOPER_EQUAL: return "equal";
+ case EXC_AFOPER_GREATER: return "greaterThan";
+ case EXC_AFOPER_GREATEREQUAL: return "greaterThanOrEqual";
+ case EXC_AFOPER_LESS: return "lessThan";
+ case EXC_AFOPER_LESSEQUAL: return "lessThanOrEqual";
+ case EXC_AFOPER_NOTEQUAL: return "notEqual";
+ case EXC_AFOPER_NONE:
+ default: return "**none**";
+ }
+}
+
+static OString lcl_GetValue( sal_uInt8 nType, const XclExpString* pStr )
+{
+ if (nType == EXC_AFTYPE_STRING)
+ return XclXmlUtils::ToOString(*pStr);
+ else
+ return OString();
+}
+
+void ExcFilterCondition::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( IsEmpty() )
+ return;
+
+ rStrm.GetCurrentStream()->singleElement( XML_customFilter,
+ XML_operator, lcl_GetOperator( nOper ),
+ XML_val, lcl_GetValue(nType, pText.get()) );
+}
+
+void ExcFilterCondition::SaveText( XclExpStream& rStrm )
+{
+ if( nType == EXC_AFTYPE_STRING )
+ {
+ OSL_ENSURE( pText, "ExcFilterCondition::SaveText() -- pText is NULL!" );
+ pText->WriteFlagField( rStrm );
+ pText->WriteBuffer( rStrm );
+ }
+}
+
+XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC, bool bIsEmpty ) :
+ XclExpRecord( EXC_ID_AUTOFILTER, 24 ),
+ XclExpRoot( rRoot ),
+ meType(bIsEmpty ? Empty : FilterCondition),
+ nCol( nC ),
+ bIsButtonHidden( false ),
+ nFlags( 0 ),
+ bHasBlankValue( false )
+{
+}
+
+bool XclExpAutofilter::AddCondition( ScQueryConnect eConn, sal_uInt8 nType, sal_uInt8 nOp,
+ const OUString* pText, bool bSimple )
+{
+ if( !aCond[ 1 ].IsEmpty() )
+ return false;
+
+ sal_uInt16 nInd = aCond[ 0 ].IsEmpty() ? 0 : 1;
+
+ if( nInd == 1 )
+ nFlags |= (eConn == SC_OR) ? EXC_AFFLAG_OR : EXC_AFFLAG_AND;
+ if( bSimple )
+ nFlags |= (nInd == 0) ? EXC_AFFLAG_SIMPLE1 : EXC_AFFLAG_SIMPLE2;
+
+ aCond[ nInd ].SetCondition( nType, nOp, pText );
+
+ AddRecSize( aCond[ nInd ].GetTextBytes() );
+
+ return true;
+}
+
+bool XclExpAutofilter::HasCondition() const
+{
+ return !aCond[0].IsEmpty();
+}
+
+bool XclExpAutofilter::AddEntry( const ScQueryEntry& rEntry )
+{
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+
+ if (rItems.empty())
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY)
+ {
+ // tdf#123353 XLSX export
+ meType = BlankValue;
+ return false;
+ }
+ // XLS export
+ return true;
+ }
+
+ if (GetOutput() != EXC_OUTPUT_BINARY && rItems.size() > 1)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ bool bConflict = false;
+ OUString sText;
+ const ScQueryEntry::Item& rItem = rItems[0];
+ if (!rItem.maString.isEmpty())
+ {
+ sText = rItem.maString.getString();
+ switch( rEntry.eOp )
+ {
+ case SC_CONTAINS:
+ case SC_DOES_NOT_CONTAIN:
+ {
+ sText = "*" + sText + "*";
+ }
+ break;
+ case SC_BEGINS_WITH:
+ case SC_DOES_NOT_BEGIN_WITH:
+ sText += "*";
+ break;
+ case SC_ENDS_WITH:
+ case SC_DOES_NOT_END_WITH:
+ sText = "*" + sText;
+ break;
+ default:
+ {
+ //nothing
+ }
+ }
+ }
+
+ // empty/nonempty fields
+ if (rEntry.IsQueryByEmpty())
+ {
+ bConflict = !AddCondition(rEntry.eConnect, EXC_AFTYPE_EMPTY, EXC_AFOPER_NONE, nullptr, true);
+ bHasBlankValue = true;
+ }
+ else if(rEntry.IsQueryByNonEmpty())
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_NOTEMPTY, EXC_AFOPER_NONE, nullptr, true );
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ AddColorEntry(rEntry);
+ }
+ // other conditions
+ else
+ {
+ // top10 flags
+ sal_uInt16 nNewFlags = 0x0000;
+ switch( rEntry.eOp )
+ {
+ case SC_TOPVAL:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP);
+ break;
+ case SC_BOTVAL:
+ nNewFlags = EXC_AFFLAG_TOP10;
+ break;
+ case SC_TOPPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC);
+ break;
+ case SC_BOTPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC);
+ break;
+ default:;
+ }
+ bool bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 );
+
+ bConflict = HasTop10() && bNewTop10;
+ if( !bConflict )
+ {
+ if( bNewTop10 )
+ {
+ sal_uInt32 nIndex = 0;
+ double fVal = 0.0;
+ if (GetFormatter().IsNumberFormat(sText, nIndex, fVal))
+ {
+ if (fVal < 0) fVal = 0;
+ if (fVal >= 501) fVal = 500;
+ }
+ nFlags |= (nNewFlags | static_cast<sal_uInt16>(fVal) << 7);
+ }
+ // normal condition
+ else
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY && rEntry.eOp == SC_EQUAL)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ sal_uInt8 nOper = EXC_AFOPER_NONE;
+
+ switch( rEntry.eOp )
+ {
+ case SC_EQUAL: nOper = EXC_AFOPER_EQUAL; break;
+ case SC_LESS: nOper = EXC_AFOPER_LESS; break;
+ case SC_GREATER: nOper = EXC_AFOPER_GREATER; break;
+ case SC_LESS_EQUAL: nOper = EXC_AFOPER_LESSEQUAL; break;
+ case SC_GREATER_EQUAL: nOper = EXC_AFOPER_GREATEREQUAL; break;
+ case SC_NOT_EQUAL: nOper = EXC_AFOPER_NOTEQUAL; break;
+ case SC_CONTAINS:
+ case SC_BEGINS_WITH:
+ case SC_ENDS_WITH:
+ nOper = EXC_AFOPER_EQUAL; break;
+ case SC_DOES_NOT_CONTAIN:
+ case SC_DOES_NOT_BEGIN_WITH:
+ case SC_DOES_NOT_END_WITH:
+ nOper = EXC_AFOPER_NOTEQUAL; break;
+ default:;
+ }
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_STRING, nOper, &sText);
+ }
+ }
+ }
+ return bConflict;
+}
+
+void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
+{
+ meType = MultiValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ if( rItem.maString.isEmpty() )
+ bHasBlankValue = true;
+ else
+ maMultiValues.push_back(std::make_pair(rItem.maString.getString(), rItem.meType == ScQueryEntry::ByDate));
+ }
+}
+
+void XclExpAutofilter::AddColorEntry(const ScQueryEntry& rEntry)
+{
+ meType = ColorValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ maColorValues.push_back(
+ std::make_pair(rItem.maColor, rItem.meType == ScQueryEntry::ByBackgroundColor));
+ // Ensure that selected color(s) will be added to dxf: selection can be not in list
+ // of already added to dfx colors taken from filter range
+ if (GetDxfs().GetDxfByColor(rItem.maColor) == -1)
+ GetDxfs().addColor(rItem.maColor);
+ }
+}
+
+void XclExpAutofilter::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << nCol << nFlags;
+ aCond[ 0 ].Save( rStrm );
+ aCond[ 1 ].Save( rStrm );
+ aCond[ 0 ].SaveText( rStrm );
+ aCond[ 1 ].SaveText( rStrm );
+}
+
+void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (meType == FilterCondition && !HasCondition() && !HasTop10())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ std::optional<OString> sHiddenButtonValue;
+ if (bIsButtonHidden)
+ sHiddenButtonValue = "1";
+
+ rWorksheet->startElement( XML_filterColumn,
+ XML_colId, OString::number(nCol),
+ XML_hiddenButton, sHiddenButtonValue
+ );
+
+ switch (meType)
+ {
+ case FilterCondition:
+ {
+ if( HasTop10() )
+ {
+ rWorksheet->singleElement( XML_top10,
+ XML_top, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10TOP ) ),
+ XML_percent, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10PERC ) ),
+ XML_val, OString::number(nFlags >> 7)
+ // OOXTODO: XML_filterVal
+ );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_customFilters, XML_and,
+ ToPsz((nFlags & EXC_AFFLAG_ANDORMASK) == EXC_AFFLAG_AND));
+ aCond[0].SaveXml(rStrm);
+ aCond[1].SaveXml(rStrm);
+ rWorksheet->endElement(XML_customFilters);
+ }
+ // OOXTODO: XML_dynamicFilter, XML_extLst, XML_filters, XML_iconFilter
+ }
+ break;
+ case ColorValue:
+ {
+ if (!maColorValues.empty())
+ {
+ Color color = maColorValues[0].first;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ if (maColorValues[0].second) // is background color
+ {
+ pAttrList->add(XML_cellColor, OString::number(1));
+ }
+ else
+ {
+ pAttrList->add(XML_cellColor, OString::number(0));
+ }
+ pAttrList->add(XML_dxfId, OString::number(GetDxfs().GetDxfByColor(color)));
+ rWorksheet->singleElement(XML_colorFilter, pAttrList);
+ }
+ }
+ break;
+ case BlankValue:
+ {
+ rWorksheet->singleElement(XML_filters, XML_blank, "1");
+ }
+ break;
+ case MultiValue:
+ {
+ if( bHasBlankValue )
+ rWorksheet->startElement(XML_filters, XML_blank, "1");
+ else
+ rWorksheet->startElement(XML_filters);
+
+ for (const auto& rMultiValue : maMultiValues)
+ {
+ if( !rMultiValue.second )
+ {
+ rWorksheet->singleElement(XML_filter, XML_val, rMultiValue.first);
+ }
+ else
+ {
+ OString aStr = OUStringToOString(rMultiValue.first, RTL_TEXTENCODING_UTF8);
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ sal_Int32 aDateGroup[3] = { XML_year, XML_month, XML_day };
+ sal_Int32 idx = 0;
+ for (size_t i = 0; idx >= 0 && i < 3; i++)
+ {
+ OString kw = aStr.getToken(0, '-', idx);
+ kw = kw.trim();
+ if (!kw.isEmpty())
+ {
+ pAttrList->add(aDateGroup[i], kw);
+ }
+ }
+ // TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
+ // will be "day" as default, until date filter cannot handle HH:MM:SS.
+ pAttrList->add(XML_dateTimeGrouping, "day");
+ rWorksheet->singleElement(XML_dateGroupItem, pAttrList);
+ }
+ }
+ rWorksheet->endElement(XML_filters);
+ }
+ break;
+ // Used for constructing an empty filterColumn element for exporting the XML_hiddenButton attribute
+ case Empty: break;
+ }
+ rWorksheet->endElement( XML_filterColumn );
+}
+
+ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData ) :
+ XclExpRoot( rRoot ),
+ mbAutoFilter (false)
+{
+ XclExpNameManager& rNameMgr = GetNameManager();
+
+ bool bFound = false;
+ bool bAdvanced = false;
+ const ScDBData* pData = (pDefinedData ? pDefinedData : rRoot.GetDoc().GetAnonymousDBData(nTab));
+ ScRange aAdvRange;
+ if (pData)
+ {
+ bAdvanced = pData->GetAdvancedQuerySource( aAdvRange );
+ bFound = (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced);
+ }
+ if( !bFound )
+ return;
+
+ ScQueryParam aParam;
+ pData->GetQueryParam( aParam );
+
+ ScRange aRange( aParam.nCol1, aParam.nRow1, aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+ aRange.PutInOrder();
+ SCCOL nColCnt = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
+
+ maRef = aRange;
+
+ // #i2394# built-in defined names must be sorted by containing sheet name
+ if (!pDefinedData)
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange );
+
+ // advanced filter
+ if( bAdvanced )
+ {
+ // filter criteria, excel allows only same table
+ if( !pDefinedData && aAdvRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_CRITERIA, aAdvRange );
+
+ // filter destination range, excel allows only same table
+ if( !aParam.bInplace )
+ {
+ ScRange aDestRange( aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
+ aDestRange.aEnd.IncCol( nColCnt - 1 );
+ if( !pDefinedData && aDestRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_EXTRACT, aDestRange );
+ }
+
+ m_pFilterMode = new XclExpFiltermode;
+ }
+ // AutoFilter
+ else
+ {
+ bool bConflict = false;
+ bool bContLoop = true;
+ bool bHasOr = false;
+ SCCOLROW nFirstField = aParam.GetEntry( 0 ).nField;
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCROW nRow = aRange.aStart.Row();
+
+ // create AUTOFILTER records for filtered columns
+ for( SCSIZE nEntry = 0; !bConflict && bContLoop && (nEntry < aParam.GetEntryCount()); nEntry++ )
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry( nEntry );
+
+ bContLoop = rEntry.bDoQuery;
+ if( bContLoop )
+ {
+ SCCOL nCol = static_cast<SCCOL>( rEntry.nField ) - aRange.aStart.Col();
+ auto nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
+ bool bIsButtonHidden = !( nFlag & ScMF::Auto );
+ XclExpAutofilter* pFilter = GetByCol( nCol );
+ pFilter->SetButtonHidden( bIsButtonHidden );
+
+ if( nEntry > 0 )
+ bHasOr |= (rEntry.eConnect == SC_OR);
+
+ bConflict = (nEntry > 1) && bHasOr;
+ if( !bConflict )
+ bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) &&
+ (nFirstField != rEntry.nField);
+ if( !bConflict )
+ bConflict = pFilter->AddEntry( rEntry );
+ }
+ }
+
+ sal_uInt16 nColId = 0;
+ for ( auto nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); nCol++, nColId++ )
+ {
+ auto nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
+ bool bIsButtonHidden = !( nFlag & ScMF::Auto );
+ if ( bIsButtonHidden )
+ {
+ // Create filter column with hiddenButton=1 attribute if it doesn't exist
+ XclExpAutofilterRef xFilter;
+ bool bFilterFound = false;
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ {
+ xFilter = maFilterList.GetRecord( nPos );
+ if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) )
+ {
+ bFilterFound = true;
+ break;
+ }
+ }
+ if ( !bFilterFound )
+ {
+ xFilter = new XclExpAutofilter( GetRoot(), nColId, /*bIsEmpty*/true );
+ xFilter->SetButtonHidden( true );
+ maFilterList.AppendRecord( xFilter );
+ }
+ }
+ }
+
+ // additional tests for conflicts
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); !bConflict && (nPos < nSize); ++nPos )
+ {
+ XclExpAutofilterRef xFilter = maFilterList.GetRecord( nPos );
+ bConflict = xFilter->HasCondition() && xFilter->HasTop10();
+ }
+
+ if( bConflict )
+ maFilterList.RemoveAllRecords();
+
+ if( !maFilterList.IsEmpty() )
+ m_pFilterMode = new XclExpFiltermode;
+ m_pFilterInfo = new XclExpAutofilterinfo( aRange.aStart, nColCnt );
+
+ if (maFilterList.IsEmpty () && !bConflict)
+ mbAutoFilter = true;
+
+ // get sort criteria
+ {
+ ScSortParam aSortParam;
+ pData->GetSortParam( aSortParam );
+
+ ScUserList* pList = ScGlobal::GetUserList();
+ if (aSortParam.bUserDef && pList && pList->size() > aSortParam.nUserIndex)
+ {
+ // get sorted area without headers
+ maSortRef = ScRange(
+ aParam.nCol1, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+
+ // get sorted columns with custom lists
+ const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
+
+ // get column index and sorting direction
+ SCCOLROW nField = 0;
+ bool bSortAscending=true;
+ for (const auto & rKey : aSortParam.maKeyState)
+ {
+ if (rKey.bDoSort)
+ {
+ nField = rKey.nField;
+ bSortAscending = rKey.bAscending;
+ break;
+ }
+ }
+
+ // remember sort criteria
+ const ScRange aSortedColumn(
+ nField, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ nField, aParam.nRow2, aParam.nTab );
+ const OUString aItemList = rData.GetString();
+
+ maSortCustomList.emplace_back(aSortedColumn, aItemList, !bSortAscending);
+ }
+ }
+ }
+}
+
+ExcAutoFilterRecs::~ExcAutoFilterRecs()
+{
+}
+
+XclExpAutofilter* ExcAutoFilterRecs::GetByCol( SCCOL nCol )
+{
+ XclExpAutofilterRef xFilter;
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ {
+ xFilter = maFilterList.GetRecord( nPos );
+ if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return xFilter.get();
+ }
+ xFilter = new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16>(nCol) );
+ maFilterList.AppendRecord( xFilter );
+ return xFilter.get();
+}
+
+bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol )
+{
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ if( maFilterList.GetRecord( nPos )->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return true;
+ return false;
+}
+
+void ExcAutoFilterRecs::AddObjRecs()
+{
+ if( m_pFilterInfo )
+ {
+ ScAddress aAddr( m_pFilterInfo->GetStartPos() );
+ for( SCCOL nObj = 0, nCount = m_pFilterInfo->GetColCount(); nObj < nCount; nObj++ )
+ {
+ std::unique_ptr<XclObj> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr, IsFiltered( nObj ) ));
+ GetObjectManager().AddObj( std::move(pObjRec) );
+ aAddr.IncCol();
+ }
+ }
+}
+
+void ExcAutoFilterRecs::Save( XclExpStream& rStrm )
+{
+ if( m_pFilterMode )
+ m_pFilterMode->Save( rStrm );
+ if( m_pFilterInfo )
+ m_pFilterInfo->Save( rStrm );
+ maFilterList.Save( rStrm );
+}
+
+void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFilterList.IsEmpty() && !mbAutoFilter )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_autoFilter, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRef));
+ // OOXTODO: XML_extLst, XML_sortState
+ if( !maFilterList.IsEmpty() )
+ maFilterList.SaveXml( rStrm );
+
+ if (!maSortCustomList.empty())
+ {
+ rWorksheet->startElement(XML_sortState, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSortRef));
+
+ for (const auto & rSortCriteria : maSortCustomList)
+ {
+ if (std::get<2>(rSortCriteria))
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_descending, "1",
+ XML_customList, std::get<1>(rSortCriteria));
+ else
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_customList, std::get<1>(rSortCriteria));
+ }
+
+ rWorksheet->endElement(XML_sortState);
+ }
+
+ rWorksheet->endElement( XML_autoFilter );
+}
+
+bool ExcAutoFilterRecs::HasFilterMode() const
+{
+ return m_pFilterMode != nullptr;
+}
+
+XclExpFilterManager::XclExpFilterManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpFilterManager::InitTabFilter( SCTAB nScTab )
+{
+ maFilterMap[ nScTab ] = new ExcAutoFilterRecs( GetRoot(), nScTab, nullptr );
+}
+
+XclExpRecordRef XclExpFilterManager::CreateRecord( SCTAB nScTab )
+{
+ XclExpTabFilterRef xRec;
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ xRec = aIt->second;
+ xRec->AddObjRecs();
+ }
+ return xRec;
+}
+
+bool XclExpFilterManager::HasFilterMode( SCTAB nScTab )
+{
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ return aIt->second->HasFilterMode();
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/exctools.cxx b/sc/source/filter/excel/exctools.cxx
new file mode 100644
index 0000000000..6e9d917778
--- /dev/null
+++ b/sc/source/filter/excel/exctools.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <scextopt.hxx>
+#include <olinetab.hxx>
+
+#include <root.hxx>
+#include <excimp8.hxx>
+#include <namebuff.hxx>
+#include <otlnbuff.hxx>
+#include <formel.hxx>
+#include <xilink.hxx>
+
+#include <memory>
+#include <vector>
+
+RootData::RootData()
+{
+ eDateiTyp = BiffX;
+ pFmlaConverter = nullptr;
+
+ pTabId = nullptr;
+ pUserBViewList = nullptr;
+
+ pIR = nullptr;
+ pER = nullptr;
+ pColRowBuff = nullptr;
+}
+
+RootData::~RootData()
+{
+ pExtSheetBuff.reset();
+ pShrfmlaBuff.reset();
+ pExtNameBuff.reset();
+ pAutoFilterBuffer.reset();
+}
+
+XclImpOutlineBuffer::XclImpOutlineBuffer( SCSIZE nNewSize ) :
+ maLevels(0, nNewSize, 0),
+ mpOutlineArray(nullptr),
+ mnEndPos(nNewSize),
+ mnMaxLevel(0),
+ mbButtonAfter(true)
+{
+}
+
+XclImpOutlineBuffer::~XclImpOutlineBuffer()
+{
+}
+
+void XclImpOutlineBuffer::SetLevel( SCSIZE nIndex, sal_uInt8 nVal, bool bCollapsed )
+{
+ maLevels.insert_back(nIndex, nIndex+1, nVal);
+ if (nVal > mnMaxLevel)
+ mnMaxLevel = nVal;
+ if (bCollapsed)
+ maCollapsedPosSet.insert(nIndex);
+}
+
+void XclImpOutlineBuffer::SetOutlineArray( ScOutlineArray* pOArray )
+{
+ mpOutlineArray = pOArray;
+}
+
+void XclImpOutlineBuffer::MakeScOutline()
+{
+ if (!mpOutlineArray)
+ return;
+
+ ::std::vector<SCSIZE> aOutlineStack;
+ aOutlineStack.reserve(mnMaxLevel);
+ for (const auto& [nPos, nLevel] : maLevels)
+ {
+ if (nPos >= mnEndPos)
+ {
+ // Don't go beyond the max allowed position.
+ OSL_ENSURE(aOutlineStack.empty(), "XclImpOutlineBuffer::MakeScOutline: outline stack not empty but expected to be.");
+ break;
+ }
+ sal_uInt8 nCurLevel = static_cast<sal_uInt8>(aOutlineStack.size());
+ if (nLevel > nCurLevel)
+ {
+ for (sal_uInt8 i = 0; i < nLevel - nCurLevel; ++i)
+ aOutlineStack.push_back(nPos);
+ }
+ else
+ {
+ OSL_ENSURE(nLevel <= nCurLevel, "XclImpOutlineBuffer::MakeScOutline: unexpected level!");
+ for (sal_uInt8 i = 0; i < nCurLevel - nLevel; ++i)
+ {
+ if (aOutlineStack.empty())
+ {
+ // Something is wrong.
+ return;
+ }
+ SCSIZE nFirstPos = aOutlineStack.back();
+ aOutlineStack.pop_back();
+ bool bCollapsed = false;
+ if (mbButtonAfter)
+ bCollapsed = maCollapsedPosSet.count(nPos) > 0;
+ else if (nFirstPos > 0)
+ bCollapsed = maCollapsedPosSet.count(nFirstPos-1) > 0;
+
+ bool bDummy;
+ mpOutlineArray->Insert(nFirstPos, nPos-1, bDummy, bCollapsed);
+ }
+ }
+ }
+}
+
+void XclImpOutlineBuffer::SetLevelRange( SCSIZE nF, SCSIZE nL, sal_uInt8 nVal, bool bCollapsed )
+{
+ if (nF > nL)
+ // invalid range
+ return;
+
+ maLevels.insert_back(nF, nL+1, nVal);
+
+ if (bCollapsed)
+ maCollapsedPosSet.insert(nF);
+}
+
+void XclImpOutlineBuffer::SetButtonMode( bool bRightOrUnder )
+{
+ mbButtonAfter = bRightOrUnder;
+}
+
+ExcScenarioCell::ExcScenarioCell( const sal_uInt16 nC, const sal_uInt16 nR )
+ : nCol( nC ), nRow( nR )
+{
+}
+
+ExcScenario::ExcScenario( XclImpStream& rIn, const RootData& rR )
+ : nTab( rR.pIR->GetCurrScTab() )
+{
+ sal_uInt16 nCref;
+ sal_uInt8 nName, nComment;
+
+ nCref = rIn.ReaduInt16();
+ nProtected = rIn.ReaduInt8();
+ rIn.Ignore( 1 ); // Hide
+ nName = rIn.ReaduInt8();
+ nComment = rIn.ReaduInt8();
+ rIn.Ignore( 1 ); // instead of nUser!
+
+ if( nName )
+ aName = rIn.ReadUniString( nName );
+ else
+ {
+ aName = "Scenery";
+ rIn.Ignore( 1 );
+ }
+
+ rIn.ReadUniString(); // username
+
+ if( nComment )
+ aComment = rIn.ReadUniString();
+
+ sal_uInt16 n = nCref;
+ sal_uInt16 nC, nR;
+ aEntries.reserve(n);
+ while( n )
+ {
+ nR = rIn.ReaduInt16();
+ nC = rIn.ReaduInt16();
+
+ aEntries.emplace_back( nC, nR );
+
+ n--;
+ }
+
+ for (auto& rEntry : aEntries)
+ rEntry.SetValue(rIn.ReadUniString());
+}
+
+void ExcScenario::Apply( const XclImpRoot& rRoot, const bool bLast )
+{
+ ScDocument& r = rRoot.GetDoc();
+ OUString aSzenName( aName );
+ sal_uInt16 nNewTab = nTab + 1;
+
+ if( !r.InsertTab( nNewTab, aSzenName ) )
+ return;
+
+ r.SetScenario( nNewTab, true );
+ // do not show scenario frames
+ const ScScenarioFlags nFlags = ScScenarioFlags::CopyAll
+ | (nProtected ? ScScenarioFlags::Protected : ScScenarioFlags::NONE);
+ /* | ScScenarioFlags::ShowFrame*/
+ r.SetScenarioData( nNewTab, aComment, COL_LIGHTGRAY, nFlags);
+
+ for (const auto& rEntry : aEntries)
+ {
+ sal_uInt16 nCol = rEntry.nCol;
+ sal_uInt16 nRow = rEntry.nRow;
+ OUString aVal = rEntry.GetValue();
+
+ r.ApplyFlagsTab( nCol, nRow, nCol, nRow, nNewTab, ScMF::Scenario );
+
+ r.SetString( nCol, nRow, nNewTab, aVal );
+ }
+
+ if( bLast )
+ r.SetActiveScenario( nNewTab, true );
+
+ // modify what the Active tab is set to if the new
+ // scenario tab occurs before the active tab.
+ ScExtDocSettings& rDocSett = rRoot.GetExtDocOptions().GetDocSettings();
+ if( (static_cast< SCCOL >( nTab ) < rDocSett.mnDisplTab) && (rDocSett.mnDisplTab < MAXTAB) )
+ ++rDocSett.mnDisplTab;
+ rRoot.GetTabInfo().InsertScTab( nNewTab );
+}
+
+void ExcScenarioList::Apply( const XclImpRoot& rRoot )
+{
+ sal_uInt16 n = static_cast<sal_uInt16>(aEntries.size());
+
+ std::vector< std::unique_ptr<ExcScenario> >::reverse_iterator iter;
+ for (iter = aEntries.rbegin(); iter != aEntries.rend(); ++iter)
+ {
+ n--;
+ (*iter)->Apply(rRoot, n == nLastScenario);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/expop2.cxx b/sc/source/filter/excel/expop2.cxx
new file mode 100644
index 0000000000..3ad57b358f
--- /dev/null
+++ b/sc/source/filter/excel/expop2.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 <unotools/fltrcfg.hxx>
+
+#include <osl/diagnose.h>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docinf.hxx>
+#include <filter/msfilter/svxmsbas.hxx>
+#include <docsh.hxx>
+#include <oox/ole/vbaexport.hxx>
+
+#include <scerrors.hxx>
+
+#include <root.hxx>
+#include <excdoc.hxx>
+#include <exp_op.hxx>
+
+#include <xehelper.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+namespace com::sun::star::document { class XDocumentProperties; }
+
+namespace {
+
+enum class VBAExportMode
+{
+ NONE,
+ REEXPORT_STREAM,
+ FULL_EXPORT
+};
+
+}
+
+ExportBiff5::ExportBiff5( XclExpRootData& rExpData, SvStream& rStrm ):
+ ExportTyp( rStrm ),
+ XclExpRoot( rExpData )
+{
+ // only need part of the Root data
+ pExcRoot = &GetOldRoot();
+ pExcRoot->pER = this; // ExcRoot -> XclExpRoot
+ pExcRoot->eDateiTyp = Biff5;
+ pExcDoc.reset( new ExcDocument( *this ) );
+}
+
+ExportBiff5::~ExportBiff5()
+{
+}
+
+ErrCode ExportBiff5::Write()
+{
+ ScDocShell* pDocShell = GetDocShell();
+ OSL_ENSURE( pDocShell, "ExportBiff5::Write - no document shell" );
+
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ OSL_ENSURE( xRootStrg.is(), "ExportBiff5::Write - no root storage" );
+
+ VBAExportMode eVbaExportMode = VBAExportMode::NONE;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ if (officecfg::Office::Calc::Filter::Import::VBA::UseExport::get())
+ eVbaExportMode = VBAExportMode::FULL_EXPORT;
+ else
+ {
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if (rFilterOpt.IsLoadExcelBasicStorage())
+ eVbaExportMode = VBAExportMode::REEXPORT_STREAM;
+ }
+ }
+
+ if ( pDocShell && xRootStrg.is() && eVbaExportMode == VBAExportMode::FULL_EXPORT)
+ {
+ VbaExport aExport(pDocShell->GetModel());
+ if (aExport.containsVBAProject())
+ {
+ tools::SvRef<SotStorage> xVBARoot = xRootStrg->OpenSotStorage("_VBA_PROJECT_CUR");
+ aExport.exportVBA( xVBARoot.get() );
+ }
+ }
+ else if( pDocShell && xRootStrg.is() && eVbaExportMode == VBAExportMode::REEXPORT_STREAM )
+ {
+ SvxImportMSVBasic aBasicImport( *pDocShell, *xRootStrg );
+ const ErrCode nErr = aBasicImport.SaveOrDelMSVBAStorage( true, EXC_STORAGE_VBA_PROJECT );
+ if( nErr != ERRCODE_NONE )
+ pDocShell->SetError(nErr);
+ }
+
+ pExcDoc->ReadDoc(); // ScDoc -> ExcDoc
+ pExcDoc->Write( aOut ); // wechstreamen
+
+ if( pDocShell && xRootStrg.is() )
+ {
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<cppu::OWeakObject*>(pDocShell->GetModel()), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+ if ( SvtFilterOptions::Get().IsEnableCalcPreview() )
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ pDocShell->GetPreviewMetaFile();
+ uno::Sequence<sal_Int8> metaFile(
+ sfx2::convertMetaFile(xMetaFile.get()));
+ sfx2::SaveOlePropertySet( xDocProps, xRootStrg.get(), &metaFile );
+ }
+ else
+ sfx2::SaveOlePropertySet( xDocProps, xRootStrg.get() );
+ }
+
+ const XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsRowTruncated() )
+ return SCWARN_EXPORT_MAXROW;
+ if( rAddrConv.IsColTruncated() )
+ return SCWARN_EXPORT_MAXCOL;
+ if( rAddrConv.IsTabTruncated() )
+ return SCWARN_EXPORT_MAXTAB;
+
+ return ERRCODE_NONE;
+}
+
+ExportBiff8::ExportBiff8( XclExpRootData& rExpData, SvStream& rStrm ) :
+ ExportBiff5( rExpData, rStrm )
+{
+ pExcRoot->eDateiTyp = Biff8;
+}
+
+ExportBiff8::~ExportBiff8()
+{
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/export/ExportTools.cxx b/sc/source/filter/excel/export/ExportTools.cxx
new file mode 100644
index 0000000000..b093da0125
--- /dev/null
+++ b/sc/source/filter/excel/export/ExportTools.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/.
+ */
+
+#include <export/ExportTools.hxx>
+#include <oox/export/ColorExportUtils.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <xestream.hxx>
+
+namespace oox::xls
+{
+void writeComplexColor(sax_fastparser::FSHelperPtr& pFS, sal_Int32 nElement,
+ model::ComplexColor const& rComplexColor, Color const& rColor)
+{
+ if (rComplexColor.isValidThemeType())
+ {
+ sal_Int32 nTheme
+ = oox::convertThemeColorTypeToExcelThemeNumber(rComplexColor.getThemeColorType());
+ double fTintShade = oox::convertColorTransformsToTintOrShade(rComplexColor);
+ pFS->singleElement(nElement, XML_theme, OString::number(nTheme), XML_tint,
+ sax_fastparser::UseIf(OString::number(fTintShade), fTintShade != 0.0));
+ }
+ else if (rColor != COL_TRANSPARENT)
+ {
+ pFS->singleElement(nElement, XML_rgb, XclXmlUtils::ToOString(rColor));
+ }
+}
+
+void writeComplexColor(sax_fastparser::FSHelperPtr& pFS, sal_Int32 nElement,
+ model::ComplexColor const& rComplexColor)
+{
+ if (rComplexColor.isValidThemeType() || rComplexColor.getType() == model::ColorType::RGB)
+ {
+ writeComplexColor(pFS, nElement, rComplexColor, rComplexColor.getFinalColor());
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/export/SparklineExt.cxx b/sc/source/filter/excel/export/SparklineExt.cxx
new file mode 100644
index 0000000000..684e358124
--- /dev/null
+++ b/sc/source/filter/excel/export/SparklineExt.cxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <export/SparklineExt.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+#include <export/ExportTools.hxx>
+
+using namespace oox;
+
+namespace xcl::exp
+{
+SparklineExt::SparklineExt(const XclExpRoot& rRoot)
+ : XclExpExt(rRoot)
+{
+ maURI = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"_ostr;
+}
+
+void SparklineExt::SaveXml(XclExpXmlStream& rStream)
+{
+ auto& rDocument = GetDoc();
+
+ auto* pSparklineList = rDocument.GetSparklineList(GetCurrScTab());
+ if (!pSparklineList)
+ return;
+
+ auto const& rSparklineGroups = pSparklineList->getSparklineGroups();
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+ rWorksheet->startElement(XML_ext, FSNS(XML_xmlns, XML_x14),
+ rStream.getNamespaceURL(OOX_NS(xls14Lst)), XML_uri, maURI);
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroups, FSNS(XML_xmlns, XML_xm),
+ rStream.getNamespaceURL(OOX_NS(xm)));
+
+ for (auto const& pSparklineGroup : rSparklineGroups)
+ {
+ auto const& rSparklineVector = pSparklineList->getSparklinesFor(pSparklineGroup);
+ addSparklineGroup(rStream, *pSparklineGroup, rSparklineVector);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroups);
+ rWorksheet->endElement(XML_ext);
+}
+
+void SparklineExt::addSparklineGroupAttributes(
+ rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineAttributes& rAttributes)
+{
+ if (rAttributes.getLineWeight() != 0.75)
+ pAttrList->add(XML_lineWeight, OString::number(rAttributes.getLineWeight()));
+
+ if (rAttributes.getType() != sc::SparklineType::Line)
+ {
+ if (rAttributes.getType() == sc::SparklineType::Column)
+ pAttrList->add(XML_type, "column");
+ else if (rAttributes.getType() == sc::SparklineType::Stacked)
+ pAttrList->add(XML_type, "stacked");
+ }
+
+ if (rAttributes.isDateAxis())
+ pAttrList->add(XML_dateAxis, "1");
+
+ if (rAttributes.getDisplayEmptyCellsAs() != sc::DisplayEmptyCellsAs::Zero)
+ {
+ if (rAttributes.getDisplayEmptyCellsAs() == sc::DisplayEmptyCellsAs::Gap)
+ pAttrList->add(XML_displayEmptyCellsAs, "gap");
+ else if (rAttributes.getDisplayEmptyCellsAs() == sc::DisplayEmptyCellsAs::Span)
+ pAttrList->add(XML_displayEmptyCellsAs, "span");
+ }
+
+ if (rAttributes.isMarkers())
+ pAttrList->add(XML_markers, "1");
+ if (rAttributes.isHigh())
+ pAttrList->add(XML_high, "1");
+ if (rAttributes.isLow())
+ pAttrList->add(XML_low, "1");
+ if (rAttributes.isFirst())
+ pAttrList->add(XML_first, "1");
+ if (rAttributes.isLast())
+ pAttrList->add(XML_last, "1");
+ if (rAttributes.isNegative())
+ pAttrList->add(XML_negative, "1");
+ if (rAttributes.shouldDisplayXAxis())
+ pAttrList->add(XML_displayXAxis, "1");
+ if (rAttributes.shouldDisplayHidden())
+ pAttrList->add(XML_displayHidden, "1");
+
+ if (rAttributes.getMinAxisType() != sc::AxisType::Individual)
+ {
+ if (rAttributes.getMinAxisType() == sc::AxisType::Group)
+ pAttrList->add(XML_minAxisType, "group");
+ else if (rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_minAxisType, "custom");
+ }
+
+ if (rAttributes.getMaxAxisType() != sc::AxisType::Individual)
+ {
+ if (rAttributes.getMaxAxisType() == sc::AxisType::Group)
+ pAttrList->add(XML_maxAxisType, "group");
+ else if (rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_maxAxisType, "custom");
+ }
+
+ if (rAttributes.isRightToLeft())
+ pAttrList->add(XML_rightToLeft, "1");
+
+ if (rAttributes.getManualMax() && rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMax, OString::number(*rAttributes.getManualMax()));
+
+ if (rAttributes.getManualMin() && rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMin, OString::number(*rAttributes.getManualMin()));
+}
+
+void SparklineExt::addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineAttributes& rAttributes)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorSeries),
+ rAttributes.getColorSeries());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorNegative),
+ rAttributes.getColorNegative());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorAxis),
+ rAttributes.getColorAxis());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorMarkers),
+ rAttributes.getColorMarkers());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorFirst),
+ rAttributes.getColorFirst());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorLast),
+ rAttributes.getColorLast());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorHigh),
+ rAttributes.getColorHigh());
+ oox::xls::writeComplexColor(rWorksheet, FSNS(XML_x14, XML_colorLow), rAttributes.getColorLow());
+}
+
+void SparklineExt::addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ // Sparkline Group Attributes
+ auto pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ // Write ID
+ OString sUID = rSparklineGroup.getID().getString();
+ pAttrList->addNS(XML_xr2, XML_uid, sUID);
+
+ // Write attributes
+ addSparklineGroupAttributes(pAttrList, rSparklineGroup.getAttributes());
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroup, pAttrList);
+
+ addSparklineGroupColors(rStream, rSparklineGroup.getAttributes());
+
+ // Sparklines
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklines);
+ for (auto const& rSparkline : rSparklines)
+ {
+ rWorksheet->startElementNS(XML_x14, XML_sparkline);
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+
+ OUString sRangeFormula;
+ ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ rSparkline->getInputRange().Format(sRangeFormula, eFlags, GetDoc(),
+ formula::FormulaGrammar::CONV_XL_OOX, ' ', true);
+
+ rWorksheet->writeEscaped(sRangeFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+
+ ScAddress::Details detailsXL(formula::FormulaGrammar::CONV_XL_OOX);
+ ScAddress aAddress(rSparkline->getColumn(), rSparkline->getRow(), GetCurrScTab());
+ OUString sLocation = aAddress.Format(ScRefFlags::VALID, &GetDoc(), detailsXL);
+
+ rWorksheet->writeEscaped(sLocation);
+ rWorksheet->endElementNS(XML_xm, XML_sqref);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparkline);
+ }
+ rWorksheet->endElementNS(XML_x14, XML_sparklines);
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroup);
+}
+
+SparklineBuffer::SparklineBuffer(const XclExpRoot& rRoot, XclExtLstRef const& xExtLst)
+ : XclExpRoot(rRoot)
+{
+ auto& rDocument = GetDoc();
+ auto* pSparklineList = rDocument.GetSparklineList(GetCurrScTab());
+ if (pSparklineList && !pSparklineList->getSparklineGroups().empty())
+ {
+ xExtLst->AddRecord(new xcl::exp::SparklineExt(GetRoot()));
+ }
+}
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/fontbuff.cxx b/sc/source/filter/excel/fontbuff.cxx
new file mode 100644
index 0000000000..40e04fcb06
--- /dev/null
+++ b/sc/source/filter/excel/fontbuff.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 <lotfntbf.hxx>
+
+#include <scitems.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemset.hxx>
+
+void LotusFontBuffer::Fill( const sal_uInt8 nIndex, SfxItemSet& rItemSet )
+{
+ sal_uInt8 nIntIndex = nIndex & 0x07;
+
+ ENTRY* pCurrent = pData + nIntIndex;
+
+ if( pCurrent->pFont )
+ rItemSet.Put( *pCurrent->pFont );
+
+ if( pCurrent->pHeight )
+ rItemSet.Put( *pCurrent->pHeight );
+
+ if( nIndex & 0x08 )
+ {
+ SvxWeightItem aWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT );
+ rItemSet.Put( aWeightItem );
+ }
+
+ if( nIndex & 0x10 )
+ {
+ SvxPostureItem aAttr( ITALIC_NORMAL, ATTR_FONT_POSTURE );
+ rItemSet.Put( aAttr );
+ }
+
+ FontLineStyle eUnderline;
+ switch( nIndex & 0x60 ) // Bit 5+6
+ {
+ case 0x60:
+ case 0x20: eUnderline = LINESTYLE_SINGLE; break;
+ case 0x40: eUnderline = LINESTYLE_DOUBLE; break;
+ default: eUnderline = LINESTYLE_NONE;
+ }
+ if( eUnderline != LINESTYLE_NONE )
+ {
+ SvxUnderlineItem aUndItem( eUnderline, ATTR_FONT_UNDERLINE );
+ rItemSet.Put( aUndItem );
+ }
+}
+
+void LotusFontBuffer::SetName( const sal_uInt16 nIndex, const OUString& rName )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetName(): Array too small!" );
+ if( nIndex < nSize )
+ {
+ ENTRY* pEntry = pData + nIndex;
+ pEntry->TmpName( rName );
+
+ if( pEntry->nType >= 0 )
+ MakeFont( pEntry );
+ }
+}
+
+void LotusFontBuffer::SetHeight( const sal_uInt16 nIndex, const sal_uInt16 nHeight )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetHeight(): Array too small!" );
+ if( nIndex < nSize )
+ pData[ nIndex ].Height( std::make_unique<SvxFontHeightItem>( static_cast<sal_uInt32>(nHeight) * 20, 100, ATTR_FONT_HEIGHT ) );
+}
+
+void LotusFontBuffer::SetType( const sal_uInt16 nIndex, const sal_uInt16 nType )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetType(): Array too small!" );
+ if( nIndex < nSize )
+ {
+ ENTRY* pEntry = pData + nIndex;
+ pEntry->Type( nType );
+
+ if( pEntry->xTmpName )
+ MakeFont( pEntry );
+ }
+}
+
+void LotusFontBuffer::MakeFont( ENTRY* pEntry )
+{
+ FontFamily eFamily = FAMILY_DONTKNOW;
+ FontPitch ePitch = PITCH_DONTKNOW;
+ rtl_TextEncoding eCharSet = RTL_TEXTENCODING_DONTKNOW;
+
+ switch( pEntry->nType )
+ {
+ case 0x00: // Helvetica
+ eFamily = FAMILY_SWISS;
+ ePitch = PITCH_VARIABLE;
+ break;
+ case 0x01: // Times Roman
+ eFamily = FAMILY_ROMAN;
+ ePitch = PITCH_VARIABLE;
+ break;
+ case 0x02: // Courier
+ ePitch = PITCH_FIXED;
+ break;
+ case 0x03: // Symbol
+ eCharSet = RTL_TEXTENCODING_SYMBOL;
+ break;
+ }
+
+ pEntry->pFont.reset( new SvxFontItem( eFamily, *pEntry->xTmpName, OUString(), ePitch, eCharSet, ATTR_FONT ) );
+
+ pEntry->xTmpName.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/frmbase.cxx b/sc/source/filter/excel/frmbase.cxx
new file mode 100644
index 0000000000..73ef59dadc
--- /dev/null
+++ b/sc/source/filter/excel/frmbase.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 <formel.hxx>
+
+#include <osl/diagnose.h>
+
+ScRangeListTabs::ScRangeListTabs( const XclImpRoot& rRoot )
+: XclImpRoot( rRoot )
+{
+}
+
+ScRangeListTabs::~ScRangeListTabs()
+{
+}
+
+void ScRangeListTabs::Append( const ScAddress& aSRD, SCTAB nTab )
+{
+ ScAddress a = aSRD;
+ ScDocument& rDoc = GetRoot().GetDoc();
+
+ if (a.Tab() > MAXTAB)
+ a.SetTab(MAXTAB);
+
+ if (a.Col() > rDoc.MaxCol())
+ a.SetCol(rDoc.MaxCol());
+
+ if (a.Row() > rDoc.MaxRow())
+ a.SetRow(rDoc.MaxRow());
+
+ if( nTab == SCTAB_MAX)
+ return;
+ if( nTab < 0)
+ nTab = a.Tab();
+
+ if (nTab < 0 || MAXTAB < nTab)
+ return;
+
+ TabRangeType::iterator itr = m_TabRanges.find(nTab);
+ if (itr == m_TabRanges.end())
+ {
+ // No entry for this table yet. Insert a new one.
+ std::pair<TabRangeType::iterator, bool> r =
+ m_TabRanges.insert(std::make_pair(nTab, RangeListType()));
+
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(ScRange(a.Col(),a.Row(),a.Tab()));
+}
+
+void ScRangeListTabs::Append( const ScRange& aCRD, SCTAB nTab )
+{
+ ScRange a = aCRD;
+ ScDocument& rDoc = GetRoot().GetDoc();
+
+ // ignore 3D ranges
+ if (a.aStart.Tab() != a.aEnd.Tab())
+ return;
+
+ if (a.aStart.Tab() > MAXTAB)
+ a.aStart.SetTab(MAXTAB);
+ else if (a.aStart.Tab() < 0)
+ a.aStart.SetTab(0);
+
+ if (a.aStart.Col() > rDoc.MaxCol())
+ a.aStart.SetCol(rDoc.MaxCol());
+ else if (a.aStart.Col() < 0)
+ a.aStart.SetCol(0);
+
+ if (a.aStart.Row() > rDoc.MaxRow())
+ a.aStart.SetRow(rDoc.MaxRow());
+ else if (a.aStart.Row() < 0)
+ a.aStart.SetRow(0);
+
+ if (a.aEnd.Col() > rDoc.MaxCol())
+ a.aEnd.SetCol(rDoc.MaxCol());
+ else if (a.aEnd.Col() < 0)
+ a.aEnd.SetCol(0);
+
+ if (a.aEnd.Row() > rDoc.MaxRow())
+ a.aEnd.SetRow(rDoc.MaxRow());
+ else if (a.aEnd.Row() < 0)
+ a.aEnd.SetRow(0);
+
+ if( nTab == SCTAB_MAX)
+ return;
+
+ if( nTab < -1)
+ nTab = a.aStart.Tab();
+
+ if (nTab < 0 || MAXTAB < nTab)
+ return;
+
+ TabRangeType::iterator itr = m_TabRanges.find(nTab);
+ if (itr == m_TabRanges.end())
+ {
+ // No entry for this table yet. Insert a new one.
+ std::pair<TabRangeType::iterator, bool> r =
+ m_TabRanges.insert(std::make_pair(nTab, RangeListType()));
+
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(a);
+}
+
+const ScRange* ScRangeListTabs::First( SCTAB n )
+{
+ OSL_ENSURE( ValidTab(n), "-ScRangeListTabs::First(): Good bye!" );
+
+ TabRangeType::iterator itr = m_TabRanges.find(n);
+ if (itr == m_TabRanges.end())
+ // No range list exists for this table.
+ return nullptr;
+
+ const RangeListType& rList = itr->second;
+ maItrCur = rList.begin();
+ maItrCurEnd = rList.end();
+ return rList.empty() ? nullptr : &(*maItrCur);
+}
+
+const ScRange* ScRangeListTabs::Next ()
+{
+ ++maItrCur;
+ if (maItrCur == maItrCurEnd)
+ return nullptr;
+
+ return &(*maItrCur);
+}
+
+ConverterBase::ConverterBase( svl::SharedStringPool& rSPool ) :
+ aPool(rSPool),
+ aEingPos( 0, 0, 0 )
+{
+}
+
+ConverterBase::~ConverterBase()
+{
+}
+
+void ConverterBase::Reset()
+{
+ aPool.Reset();
+ aStack.Reset();
+}
+
+ExcelConverterBase::ExcelConverterBase( svl::SharedStringPool& rSPool ) :
+ ConverterBase(rSPool)
+{
+}
+
+ExcelConverterBase::~ExcelConverterBase()
+{
+}
+
+void ExcelConverterBase::Reset( const ScAddress& rEingPos )
+{
+ ConverterBase::Reset();
+ aEingPos = rEingPos;
+}
+
+void ExcelConverterBase::Reset()
+{
+ ConverterBase::Reset();
+ aEingPos.Set( 0, 0, 0 );
+}
+
+LotusConverterBase::LotusConverterBase( SvStream &rStr, svl::SharedStringPool& rSPool ) :
+ ConverterBase(rSPool),
+ aIn( rStr ),
+ nBytesLeft( 0 )
+{
+}
+
+LotusConverterBase::~LotusConverterBase()
+{
+}
+
+void LotusConverterBase::Reset( const ScAddress& rEingPos )
+{
+ ConverterBase::Reset();
+ nBytesLeft = 0;
+ aEingPos = rEingPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx
new file mode 100644
index 0000000000..3262586c14
--- /dev/null
+++ b/sc/source/filter/excel/impop.cxx
@@ -0,0 +1,1419 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <imp_op.hxx>
+
+#include <filter/msfilter/countryid.hxx>
+
+#include <scitems.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <sfx2/docfile.hxx>
+#include <svx/svxids.hrc>
+#include <svl/numformat.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <olinetab.hxx>
+#include <stlpool.hxx>
+#include <viewopti.hxx>
+#include <docoptio.hxx>
+#include <scextopt.hxx>
+#include <unonames.hxx>
+#include <paramisc.hxx>
+#include <colrowst.hxx>
+#include <otlnbuff.hxx>
+#include <xistyle.hxx>
+
+#include <namebuff.hxx>
+#include <xltools.hxx>
+#include <xltable.hxx>
+#include <xltracer.hxx>
+#include <xihelper.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+#include <xiescher.hxx>
+#include <xicontent.hxx>
+
+#include <excform.hxx>
+#include <documentimport.hxx>
+
+#if defined(_WIN32)
+#include <math.h>
+#endif
+
+using namespace ::com::sun::star;
+
+ImportTyp::ImportTyp(ScDocument& rDoc, rtl_TextEncoding eQ)
+ : eQuellChar(eQ)
+ , rD(rDoc)
+
+{
+}
+
+ImportTyp::~ImportTyp()
+{
+}
+
+ImportExcel::ImportExcel( XclImpRootData& rImpData, SvStream& rStrm ):
+ ImportTyp( rImpData.mrDoc, rImpData.meTextEnc ),
+ XclImpRoot( rImpData ),
+ maStrm( rStrm, GetRoot() ),
+ aIn( maStrm ),
+ maScOleSize( ScAddress::INITIALIZE_INVALID ),
+ pColOutlineBuff(nullptr),
+ pRowOutlineBuff(nullptr),
+ pColRowBuff(nullptr),
+ mpLastFormula(nullptr),
+ mnLastRefIdx( 0 ),
+ mnIxfeIndex( 0 ),
+ mnLastRecId(0),
+ mbBiff2HasXfs(false),
+ mbBiff2HasXfsValid(false),
+ mbFuzzing(utl::ConfigManager::IsFuzzing())
+{
+ nBdshtTab = 0;
+
+ // fill in root data - after new's without root as parameter
+ pExcRoot = &GetOldRoot();
+ pExcRoot->pIR = this; // ExcRoot -> XclImpRoot
+ pExcRoot->eDateiTyp = BiffX;
+ pExcRoot->pExtSheetBuff.reset( new ExtSheetBuffer( pExcRoot ) ); //&aExtSheetBuff;
+ pExcRoot->pShrfmlaBuff.reset( new SharedFormulaBuffer( pExcRoot ) ); //&aShrfrmlaBuff;
+ pExcRoot->pExtNameBuff.reset( new ExtNameBuff ( *this ) );
+
+ pOutlineListBuffer.reset(new XclImpOutlineListBuffer);
+
+ // from Biff8 on
+ pFormConv.reset(new ExcelToSc( GetRoot() ));
+ pExcRoot->pFmlaConverter = pFormConv.get();
+
+ bTabTruncated = false;
+
+ // Excel document per Default on 31.12.1899, accords to Excel settings with 1.1.1900
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetDate( 30, 12, 1899 );
+ rD.SetDocOptions( aOpt );
+ rD.GetFormatTable()->ChangeNullDate( 30, 12, 1899 );
+
+ ScDocOptions aDocOpt( rD.GetDocOptions() );
+ aDocOpt.SetIgnoreCase( true ); // always in Excel
+ aDocOpt.SetFormulaRegexEnabled( false ); // regular expressions? what's that?
+ aDocOpt.SetFormulaWildcardsEnabled( true ); // Excel uses wildcard expressions
+ aDocOpt.SetLookUpColRowNames( false ); // default: no natural language refs
+ rD.SetDocOptions( aDocOpt );
+}
+
+ImportExcel::~ImportExcel()
+{
+ GetDoc().SetSrcCharSet( GetTextEncoding() );
+
+ pOutlineListBuffer.reset();
+
+ pFormConv.reset();
+}
+
+void ImportExcel::SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell )
+{
+ LastFormulaMapType::iterator it = maLastFormulaCells.find(nCol);
+ if (it == maLastFormulaCells.end())
+ {
+ std::pair<LastFormulaMapType::iterator, bool> r =
+ maLastFormulaCells.emplace(nCol, LastFormula());
+ it = r.first;
+ }
+
+ it->second.mnCol = nCol;
+ it->second.mnRow = nRow;
+ it->second.mpCell = pCell;
+ it->second.mfValue = fVal;
+ it->second.mnXF = nXF;
+
+ mpLastFormula = &it->second;
+}
+
+void ImportExcel::ReadFileSharing()
+{
+ sal_uInt16 nRecommendReadOnly, nPasswordHash;
+ nRecommendReadOnly = maStrm.ReaduInt16();
+ nPasswordHash = maStrm.ReaduInt16();
+
+ if((nRecommendReadOnly == 0) && (nPasswordHash == 0))
+ return;
+
+ GetMedium().GetItemSet().Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+
+ if( ScDocShell* pShell = GetDocShell() )
+ {
+ if( nRecommendReadOnly != 0 )
+ pShell->SetLoadReadonly( true );
+ if( nPasswordHash != 0 )
+ pShell->SetModifyPasswordHash( nPasswordHash );
+ }
+}
+
+sal_uInt16 ImportExcel::ReadXFIndex( const ScAddress& rScPos, bool bBiff2 )
+{
+ sal_uInt16 nXFIdx = 0;
+ if( bBiff2 )
+ {
+ /* #i71453# On first call, check if the file contains XF records (by
+ trying to access the first XF with index 0). If there are no XFs,
+ the explicit formatting information contained in each cell record
+ will be used instead. */
+ if( !mbBiff2HasXfsValid )
+ {
+ mbBiff2HasXfsValid = true;
+ mbBiff2HasXfs = GetXFBuffer().GetXF( 0 ) != nullptr;
+ }
+ // read formatting information (includes the XF identifier)
+ sal_uInt8 nFlags1, nFlags2, nFlags3;
+ nFlags1 = maStrm.ReaduInt8();
+ nFlags2 = maStrm.ReaduInt8();
+ nFlags3 = maStrm.ReaduInt8();
+ /* If the file contains XFs, extract and set the XF identifier,
+ otherwise get the explicit formatting. */
+ if( mbBiff2HasXfs )
+ {
+ nXFIdx = ::extract_value< sal_uInt16 >( nFlags1, 0, 6 );
+ /* If the identifier is equal to 63, then the real identifier is
+ contained in the preceding IXFE record (stored in mnBiff2XfId). */
+ if( nXFIdx == 63 )
+ nXFIdx = mnIxfeIndex;
+ }
+ else
+ {
+ /* Let the XclImpXF class do the conversion of the imported
+ formatting. The XF buffer is empty, therefore will not do any
+ conversion based on the XF index later on. */
+ XclImpXF::ApplyPatternForBiff2CellFormat( GetRoot(), rScPos, nFlags1, nFlags2, nFlags3 );
+ }
+ }
+ else
+ nXFIdx = aIn.ReaduInt16();
+ return nXFIdx;
+}
+
+void ImportExcel::ReadDimensions()
+{
+ XclRange aXclUsedArea;
+ if( (maStrm.GetRecId() == EXC_ID2_DIMENSIONS) || (GetBiff() <= EXC_BIFF5) )
+ {
+ maStrm >> aXclUsedArea;
+ if( (aXclUsedArea.GetColCount() > 1) && (aXclUsedArea.GetRowCount() > 1) )
+ {
+ // Excel stores first unused row/column index
+ --aXclUsedArea.maLast.mnCol;
+ --aXclUsedArea.maLast.mnRow;
+ // create the Calc range
+ SCTAB nScTab = GetCurrScTab();
+ ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea;
+ GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false );
+ // if any error occurs in ConvertRange(), rScUsedArea keeps untouched
+ }
+ }
+ else
+ {
+ sal_uInt32 nXclRow1 = 0, nXclRow2 = 0;
+ nXclRow1 = maStrm.ReaduInt32();
+ nXclRow2 = maStrm.ReaduInt32();
+ aXclUsedArea.maFirst.mnCol = maStrm.ReaduInt16();
+ aXclUsedArea.maLast.mnCol = maStrm.ReaduInt16();
+ if( (nXclRow1 < nXclRow2) && (aXclUsedArea.GetColCount() > 1) &&
+ (nXclRow1 <= o3tl::make_unsigned( GetScMaxPos().Row() )) )
+ {
+ // Excel stores first unused row/column index
+ --nXclRow2;
+ --aXclUsedArea.maLast.mnCol;
+ // convert row indexes to 16-bit values
+ aXclUsedArea.maFirst.mnRow = static_cast< sal_uInt16 >( nXclRow1 );
+ aXclUsedArea.maLast.mnRow = limit_cast< sal_uInt16 >( nXclRow2, aXclUsedArea.maFirst.mnRow, SAL_MAX_UINT16 );
+ // create the Calc range
+ SCTAB nScTab = GetCurrScTab();
+ ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea;
+ GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false );
+ // if any error occurs in ConvertRange(), rScUsedArea keeps untouched
+ }
+ }
+}
+
+void ImportExcel::ReadBlank()
+{
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_BLANK );
+
+ GetXFRangeBuffer().SetBlankXF( aScPos, nXFIdx );
+ }
+}
+
+void ImportExcel::ReadInteger()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, true );
+ sal_uInt16 nValue;
+ nValue = maStrm.ReaduInt16();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, nValue);
+ }
+}
+
+void ImportExcel::ReadNumber()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_NUMBER );
+ double fValue;
+ fValue = maStrm.ReadDouble();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, fValue);
+ }
+}
+
+void ImportExcel::ReadLabel()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ /* Record ID BIFF XF type String type
+ 0x0004 2-7 3 byte 8-bit length, byte string
+ 0x0004 8 3 byte 16-bit length, unicode string
+ 0x0204 2-7 2 byte 16-bit length, byte string
+ 0x0204 8 2 byte 16-bit length, unicode string */
+ bool bBiff2 = maStrm.GetRecId() == EXC_ID2_LABEL;
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, bBiff2 );
+ XclStrFlags nFlags = (bBiff2 && (GetBiff() <= EXC_BIFF5)) ? XclStrFlags::EightBitLength : XclStrFlags::NONE;
+ XclImpString aString;
+
+ // #i63105# use text encoding from FONT record
+ rtl_TextEncoding eOldTextEnc = GetTextEncoding();
+ if( const XclImpFont* pFont = GetXFBuffer().GetFont( nXFIdx ) )
+ SetTextEncoding( pFont->GetFontEncoding() );
+ aString.Read( maStrm, nFlags );
+ SetTextEncoding( eOldTextEnc );
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, GetRoot(), aString, nXFIdx);
+}
+
+void ImportExcel::ReadBoolErr()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_BOOLERR );
+ sal_uInt8 nValue, nType;
+ nValue = maStrm.ReaduInt8();
+ nType = maStrm.ReaduInt8();
+
+ if( nType == EXC_BOOLERR_BOOL )
+ GetXFRangeBuffer().SetBoolXF( aScPos, nXFIdx );
+ else
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+
+ double fValue;
+ std::unique_ptr<ScTokenArray> pScTokArr = ErrorToFormula( nType != EXC_BOOLERR_BOOL, nValue, fValue );
+ ScFormulaCell* pCell = pScTokArr
+ ? new ScFormulaCell(rD, aScPos, std::move(pScTokArr))
+ : new ScFormulaCell(rD, aScPos);
+ pCell->SetHybridDouble( fValue );
+ GetDocImport().setFormulaCell(aScPos, pCell);
+}
+
+void ImportExcel::ReadRk()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, false );
+ sal_Int32 nRk;
+ nRk = maStrm.ReadInt32();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, XclTools::GetDoubleFromRK(nRk));
+ }
+}
+
+void ImportExcel::Window1()
+{
+ GetDocViewSettings().ReadWindow1( maStrm );
+}
+
+void ImportExcel::Row25()
+{
+ sal_uInt16 nRow, nRowHeight;
+
+ nRow = aIn.ReaduInt16();
+ aIn.Ignore( 4 );
+
+ if( !GetRoot().GetDoc().ValidRow( nRow ) )
+ return;
+
+ nRowHeight = aIn.ReaduInt16(); // specify direct in Twips
+ aIn.Ignore( 2 );
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// -------------------- BIFF2
+ pColRowBuff->SetHeight( nRow, nRowHeight );
+ }
+ else
+ {// -------------------- BIFF5
+ sal_uInt16 nGrbit;
+
+ aIn.Ignore( 2 ); // reserved
+ nGrbit = aIn.ReaduInt16();
+
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 );
+ pRowOutlineBuff->SetLevel( nRow, nLevel, ::get_flag( nGrbit, EXC_ROW_COLLAPSED ) );
+ pColRowBuff->SetRowSettings( nRow, nRowHeight, nGrbit );
+ }
+}
+
+void ImportExcel::Bof2()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff2C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff2M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff2;
+}
+
+void ImportExcel::Eof()
+{
+ // POST: cannot be called after an invalid table!
+ EndSheet();
+ IncCurrScTab();
+}
+
+void ImportExcel::SheetPassword()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetSheetProtectBuffer().ReadPasswordHash( aIn, GetCurrScTab() );
+}
+
+void ImportExcel::Externsheet()
+{
+ OUString aUrl, aTabName;
+ bool bSameWorkBook;
+ OUString aEncodedUrl( aIn.ReadByteString( false ) );
+ XclImpUrlHelper::DecodeUrl( aUrl, aTabName, bSameWorkBook, *pExcRoot->pIR, aEncodedUrl );
+ mnLastRefIdx = pExcRoot->pExtSheetBuff->Add( aUrl, aTabName, bSameWorkBook );
+}
+
+void ImportExcel:: WinProtection()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadWinProtect( aIn );
+}
+
+void ImportExcel::Columndefault()
+{// Default Cell Attributes
+ sal_uInt16 nColMic, nColMac;
+ sal_uInt8 nOpt0;
+
+ nColMic = aIn.ReaduInt16();
+ nColMac = aIn.ReaduInt16();
+
+ OSL_ENSURE( aIn.GetRecLeft() == static_cast<std::size_t>(nColMac - nColMic) * 3 + 2,
+ "ImportExcel::Columndefault - wrong record size" );
+
+ nColMac--;
+
+ if( nColMac > rD.MaxCol() )
+ nColMac = static_cast<sal_uInt16>(rD.MaxCol());
+
+ for( sal_uInt16 nCol = nColMic ; nCol <= nColMac ; nCol++ )
+ {
+ nOpt0 = aIn.ReaduInt8();
+ aIn.Ignore( 2 ); // only 0. Attribute-Byte used
+
+ if( nOpt0 & 0x80 ) // Col hidden?
+ pColRowBuff->HideCol( nCol );
+ }
+}
+
+void ImportExcel::Array25()
+{
+ sal_uInt16 nFormLen;
+ sal_uInt16 nFirstRow = aIn.ReaduInt16();
+ sal_uInt16 nLastRow = aIn.ReaduInt16();
+ sal_uInt8 nFirstCol = aIn.ReaduInt8();
+ sal_uInt8 nLastCol = aIn.ReaduInt8();
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// BIFF2
+ aIn.Ignore( 1 );
+ nFormLen = aIn.ReaduInt8();
+ }
+ else
+ {// BIFF5
+ aIn.Ignore( 6 );
+ nFormLen = aIn.ReaduInt16();
+ }
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ if (GetRoot().GetDoc().ValidColRow(nLastCol, nLastRow))
+ {
+ // the read mark is now on the formula, length in nFormLen
+
+ pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow), GetCurrScTab() ) );
+ pFormConv->Convert(pResult, maStrm, nFormLen, true);
+
+ SAL_WARN_IF(!pResult, "sc", "*ImportExcel::Array25(): ScTokenArray is NULL!");
+ }
+
+ if (pResult)
+ {
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aArrayRange(nFirstCol, nFirstRow, GetCurrScTab(), nLastCol, nLastRow, GetCurrScTab());
+ rDoc.setMatrixCells(aArrayRange, *pResult, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+}
+
+void ImportExcel::Rec1904()
+{
+ sal_uInt16 n1904;
+
+ n1904 = aIn.ReaduInt16();
+
+ if( n1904 )
+ {// 1904 date system
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetDate( 1, 1, 1904 );
+ rD.SetDocOptions( aOpt );
+ rD.GetFormatTable()->ChangeNullDate( 1, 1, 1904 );
+ }
+}
+
+void ImportExcel::Externname25()
+{
+ sal_uInt32 nRes;
+ sal_uInt16 nOpt;
+
+ nOpt = aIn.ReaduInt16();
+ nRes = aIn.ReaduInt32();
+
+ aIn.ReadByteString( false ); // name
+
+ if( ( nOpt & 0x0001 ) || ( ( nOpt & 0xFFFE ) == 0x0000 ) )
+ {// external name
+ pExcRoot->pExtNameBuff->AddName( mnLastRefIdx );
+ }
+ else if( nOpt & 0x0010 )
+ {// ole link
+ pExcRoot->pExtNameBuff->AddOLE( mnLastRefIdx, nRes ); // nRes is storage ID
+ }
+ else
+ {// dde link
+ pExcRoot->pExtNameBuff->AddDDE( mnLastRefIdx );
+ }
+}
+
+void ImportExcel::Colwidth()
+{// Column Width
+ sal_uInt8 nColFirst, nColLast;
+ sal_uInt16 nColWidth;
+
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+ nColWidth = aIn.ReaduInt16();
+
+//TODO: add a check for the unlikely case of changed MAXCOL (-> XclImpAddressConverter)
+// if( nColLast > rD.MaxCol() )
+// nColLast = static_cast<sal_uInt16>(rD.MaxCol());
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() );
+ pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth );
+}
+
+void ImportExcel::Defrowheight2()
+{
+ sal_uInt16 nDefHeight;
+ nDefHeight = maStrm.ReaduInt16();
+ nDefHeight &= 0x7FFF;
+ pColRowBuff->SetDefHeight( nDefHeight, EXC_DEFROW_UNSYNCED );
+}
+
+void ImportExcel::SheetProtect()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetSheetProtectBuffer().ReadProtect( aIn, GetCurrScTab() );
+}
+
+void ImportExcel::DocProtect()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadDocProtect( aIn );
+}
+
+void ImportExcel::DocPassword()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadPasswordHash( aIn );
+}
+
+void ImportExcel::Codepage()
+{
+ SetCodePage( maStrm.ReaduInt16() );
+}
+
+void ImportExcel::Ixfe()
+{
+ mnIxfeIndex = maStrm.ReaduInt16();
+}
+
+void ImportExcel::DefColWidth()
+{
+ // stored as entire characters -> convert to 1/256 of characters (as in COLINFO)
+ double fDefWidth = 256.0 * maStrm.ReaduInt16();
+
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::DefColWidth(): pColRowBuff is NULL!");
+ return;
+ }
+
+ // #i3006# additional space for default width - Excel adds space depending on font size
+ tools::Long nFontHt = GetFontBuffer().GetAppFontData().mnHeight;
+ fDefWidth += XclTools::GetXclDefColWidthCorrection( nFontHt );
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( limit_cast< sal_uInt16 >( fDefWidth ), GetCharWidth() );
+ pColRowBuff->SetDefWidth( nScWidth );
+}
+
+void ImportExcel::Colinfo()
+{// Column Formatting Information
+ sal_uInt16 nColFirst, nColLast, nColWidth, nXF;
+ sal_uInt16 nOpt;
+
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+ nColWidth = aIn.ReaduInt16();
+ nXF = aIn.ReaduInt16();
+ nOpt = aIn.ReaduInt16();
+
+ if( nColFirst > rD.MaxCol() )
+ return;
+
+ if( nColLast > rD.MaxCol() )
+ nColLast = static_cast<sal_uInt16>(rD.MaxCol());
+
+ bool bHidden = ::get_flag( nOpt, EXC_COLINFO_HIDDEN );
+ bool bCollapsed = ::get_flag( nOpt, EXC_COLINFO_COLLAPSED );
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nOpt, 8, 3 );
+ pColOutlineBuff->SetLevelRange( nColFirst, nColLast, nLevel, bCollapsed );
+
+ if( bHidden )
+ pColRowBuff->HideColRange( nColFirst, nColLast );
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() );
+ pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth );
+ pColRowBuff->SetDefaultXF( nColFirst, nColLast, nXF );
+}
+
+void ImportExcel::Wsbool()
+{
+ sal_uInt16 nFlags;
+ nFlags = aIn.ReaduInt16();
+
+ pRowOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_ROWBELOW ) );
+ pColOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_COLBELOW ) );
+
+ GetPageSettings().SetFitToPages( ::get_flag( nFlags, EXC_WSBOOL_FITTOPAGE ) );
+}
+
+void ImportExcel::Boundsheet()
+{
+ sal_uInt16 nGrbit = 0;
+
+ if( GetBiff() == EXC_BIFF5 )
+ {
+ aIn.DisableDecryption();
+ maSheetOffsets.push_back( aIn.ReaduInt32() );
+ aIn.EnableDecryption();
+ nGrbit = aIn.ReaduInt16();
+ }
+
+ OUString aName( aIn.ReadByteString( false ) );
+
+ SCTAB nScTab = nBdshtTab;
+ if( nScTab > 0 )
+ {
+ OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel::Boundsheet - sheet exists already" );
+ rD.MakeTable( nScTab );
+ }
+
+ if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
+ rD.SetVisible( nScTab, false );
+
+ if( !rD.RenameTab( nScTab, aName ) )
+ {
+ rD.CreateValidTabName( aName );
+ rD.RenameTab( nScTab, aName );
+ }
+
+ nBdshtTab++;
+}
+
+void ImportExcel::Country()
+{
+ sal_uInt16 nUICountry, nDocCountry;
+ nUICountry = maStrm.ReaduInt16();
+ nDocCountry = maStrm.ReaduInt16();
+
+ // Store system language in XclRoot
+ LanguageType eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nDocCountry ) );
+ if( eLanguage != LANGUAGE_DONTKNOW )
+ SetDocLanguage( eLanguage );
+
+ // Set Excel UI language in add-in name translator
+ eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nUICountry ) );
+ if( eLanguage != LANGUAGE_DONTKNOW )
+ SetUILanguage( eLanguage );
+}
+
+void ImportExcel::ReadUsesElfs()
+{
+ if( maStrm.ReaduInt16() != 0 )
+ {
+ ScDocOptions aDocOpt = GetDoc().GetDocOptions();
+ aDocOpt.SetLookUpColRowNames( true );
+ GetDoc().SetDocOptions( aDocOpt );
+ }
+}
+
+void ImportExcel::Hideobj()
+{
+ sal_uInt16 nHide;
+ ScVObjMode eOle, eChart, eDraw;
+
+ nHide = aIn.ReaduInt16();
+
+ ScViewOptions aOpts( rD.GetViewOptions() );
+
+ switch( nHide )
+ {
+ case 1: // Placeholders
+ eOle = VOBJ_MODE_SHOW; // in Excel 97 only charts as place holder are displayed
+ eChart = VOBJ_MODE_SHOW; //#i80528# VOBJ_MODE_DUMMY replaced by VOBJ_MODE_SHOW now
+ eDraw = VOBJ_MODE_SHOW;
+ break;
+ case 2: // Hide all
+ eOle = VOBJ_MODE_HIDE;
+ eChart = VOBJ_MODE_HIDE;
+ eDraw = VOBJ_MODE_HIDE;
+ break;
+ default: // Show all
+ eOle = VOBJ_MODE_SHOW;
+ eChart = VOBJ_MODE_SHOW;
+ eDraw = VOBJ_MODE_SHOW;
+ break;
+ }
+
+ aOpts.SetObjMode( VOBJ_TYPE_OLE, eOle );
+ aOpts.SetObjMode( VOBJ_TYPE_CHART, eChart );
+ aOpts.SetObjMode( VOBJ_TYPE_DRAW, eDraw );
+
+ rD.SetViewOptions( aOpts );
+}
+
+void ImportExcel::Standardwidth()
+{
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( maStrm.ReaduInt16(), GetCharWidth() );
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::Standardwidth(): pColRowBuff is NULL!");
+ return;
+ }
+ pColRowBuff->SetDefWidth( nScWidth, true );
+}
+
+void ImportExcel::Shrfmla()
+{
+ switch (mnLastRecId)
+ {
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA:
+ // This record MUST immediately follow a FORMULA record.
+ break;
+ default:
+ return;
+ }
+
+ if (!mpLastFormula)
+ // The last FORMULA record should have left this data.
+ return;
+
+ aIn.Ignore( 8 );
+ sal_uInt16 nLenExpr = aIn.ReaduInt16();
+
+ // read mark is now on the formula
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ // The shared range in this record is erroneous more than half the time.
+ // Don't ever rely on it. Use the one from the formula cell above.
+ SCCOL nCol1 = mpLastFormula->mnCol;
+ SCROW nRow1 = mpLastFormula->mnRow;
+
+ ScAddress aPos(nCol1, nRow1, GetCurrScTab());
+ pFormConv->Reset(aPos);
+ pFormConv->Convert( pResult, maStrm, nLenExpr, true, FT_SharedFormula );
+
+ if (!pResult)
+ {
+ SAL_WARN("sc", "+ImportExcel::Shrfmla(): ScTokenArray is NULL!");
+ return;
+ }
+
+ pExcRoot->pShrfmlaBuff->Store(aPos, *pResult);
+
+ // Create formula cell for the last formula record.
+
+ ScDocumentImport& rDoc = GetDocImport();
+
+ ScFormulaCell* pCell = new ScFormulaCell(rD, aPos, std::move(pResult));
+ pCell->GetCode()->WrapReference(aPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+ rDoc.getDoc().EnsureTable(aPos.Tab());
+ rDoc.setFormulaCell(aPos, pCell);
+ pCell->SetNeedNumberFormat(false);
+ if (std::isfinite(mpLastFormula->mfValue))
+ pCell->SetResultDouble(mpLastFormula->mfValue);
+
+ GetXFRangeBuffer().SetXF(aPos, mpLastFormula->mnXF);
+ mpLastFormula->mpCell = pCell;
+}
+
+void ImportExcel::Mulrk()
+{
+ /* rw (2 bytes): An Rw structure that specifies the row containing the
+ cells with numeric data.
+
+ colFirst (2 bytes): A Col structure that specifies the first column in
+ the series of numeric cells within the sheet. The value of colFirst.col
+ MUST be less than or equal to 254.
+
+ rgrkrec (variable): An array of RkRec structures. Each element in the
+ array specifies an RkRec in the row. The number of entries in the array
+ MUST be equal to the value given by the following formula:
+
+ Number of entries in rgrkrec = (colLast.col – colFirst.col +1)
+
+ colLast (2 bytes): A Col structure that specifies the last column in
+ the set of numeric cells within the sheet. This colLast.col value MUST
+ be greater than the colFirst.col value. */
+
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ XclAddress aCurrXclPos(aXclPos);
+ while (true)
+ {
+ if (aXclPos.mnCol > aCurrXclPos.mnCol)
+ break;
+ if (aIn.GetRecLeft() <= 2)
+ break;
+
+ sal_uInt16 nXF = aIn.ReaduInt16();
+ sal_Int32 nRkNum = aIn.ReadInt32();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) )
+ {
+ GetXFRangeBuffer().SetXF( aScPos, nXF );
+ GetDocImport().setNumericCell(aScPos, XclTools::GetDoubleFromRK(nRkNum));
+ }
+ ++aCurrXclPos.mnCol;
+ }
+}
+
+void ImportExcel::Mulblank()
+{
+ /* rw (2 bytes): An Rw structure that specifies a row containing the blank
+ cells.
+
+ colFirst (2 bytes): A Col structure that specifies the first column in
+ the series of blank cells within the sheet. The value of colFirst.col
+ MUST be less than or equal to 254.
+
+ rgixfe (variable): An array of IXFCell structures. Each element of this
+ array contains an IXFCell structure corresponding to a blank cell in the
+ series. The number of entries in the array MUST be equal to the value
+ given by the following formula:
+
+ Number of entries in rgixfe = (colLast.col – colFirst.col +1)
+
+ colLast (2 bytes): A Col structure that specifies the last column in
+ the series of blank cells within the sheet. This colLast.col value MUST
+ be greater than colFirst.col value. */
+
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ XclAddress aCurrXclPos(aXclPos);
+ while (true)
+ {
+ if (aXclPos.mnCol > aCurrXclPos.mnCol)
+ break;
+ if (aIn.GetRecLeft() <= 2)
+ break;
+
+ sal_uInt16 nXF = aIn.ReaduInt16();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) )
+ GetXFRangeBuffer().SetBlankXF( aScPos, nXF );
+ ++aCurrXclPos.mnCol;
+ }
+}
+
+void ImportExcel::Rstring()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXFIdx;
+ aIn >> aXclPos;
+ nXFIdx = aIn.ReaduInt16();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ // unformatted Unicode string with separate formatting information
+ XclImpString aString;
+ aString.Read( maStrm );
+
+ // character formatting runs
+ if( !aString.IsRich() )
+ aString.ReadFormats( maStrm );
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, aString, nXFIdx);
+}
+
+void ImportExcel::Cellmerging()
+{
+ XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ SCTAB nScTab = GetCurrScTab();
+
+ sal_uInt16 nCount = maStrm.ReaduInt16();
+ sal_uInt16 nIdx = 0;
+ while (true)
+ {
+ if (maStrm.GetRecLeft() < 8)
+ break;
+ if (nIdx >= nCount)
+ break;
+ XclRange aXclRange;
+ maStrm >> aXclRange; // 16-bit rows and columns
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( rAddrConv.ConvertRange( aScRange, aXclRange, nScTab, nScTab, true ) )
+ {
+ const bool bTooSlowForFuzzing = mbFuzzing && (aScRange.aEnd.Col() > 512 || aScRange.aEnd.Row() > 512);
+ if (!bTooSlowForFuzzing)
+ GetXFRangeBuffer().SetMerge( aScRange.aStart.Col(), aScRange.aStart.Row(), aScRange.aEnd.Col(), aScRange.aEnd.Row() );
+ }
+ ++nIdx;
+ }
+}
+
+void ImportExcel::Olesize()
+{
+ XclRange aXclOleSize( ScAddress::UNINITIALIZED );
+ maStrm.Ignore( 2 );
+ aXclOleSize.Read( maStrm, false );
+
+ SCTAB nScTab = GetCurrScTab();
+ GetAddressConverter().ConvertRange( maScOleSize, aXclOleSize, nScTab, nScTab, false );
+}
+
+void ImportExcel::Row34()
+{
+ sal_uInt16 nRow, nRowHeight, nGrbit, nXF;
+
+ nRow = aIn.ReaduInt16();
+ aIn.Ignore( 4 );
+
+ SCROW nScRow = static_cast< SCROW >( nRow );
+
+ if( !GetRoot().GetDoc().ValidRow( nScRow ) )
+ return;
+
+ nRowHeight = aIn.ReaduInt16(); // specify direct in Twips
+ aIn.Ignore( 4 );
+
+ nRowHeight = nRowHeight & 0x7FFF; // Bit 15: Row Height not changed manually
+ if( !nRowHeight )
+ nRowHeight = (GetBiff() == EXC_BIFF2) ? 0x25 : 0x225;
+
+ nGrbit = aIn.ReaduInt16();
+ nXF = aIn.ReaduInt16();
+
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 );
+ pRowOutlineBuff->SetLevel( nScRow, nLevel, ::get_flag( nGrbit, EXC_ROW_COLLAPSED ) );
+ pColRowBuff->SetRowSettings( nScRow, nRowHeight, nGrbit );
+
+ if( nGrbit & EXC_ROW_USEDEFXF )
+ GetXFRangeBuffer().SetRowDefXF( nScRow, nXF & EXC_ROW_XFMASK );
+}
+
+void ImportExcel::Bof3()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ OSL_ENSURE( nSubType != 0x0100, "*ImportExcel::Bof3(): Biff3 as Workbook?!" );
+ if( nSubType == 0x0100 ) // Book
+ pExcRoot->eDateiTyp = Biff3W;
+ else if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff3C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff3M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff3;
+}
+
+void ImportExcel::Array34()
+{
+ sal_uInt16 nFirstRow, nLastRow, nFormLen;
+ sal_uInt8 nFirstCol, nLastCol;
+
+ nFirstRow = aIn.ReaduInt16();
+ nLastRow = aIn.ReaduInt16();
+ nFirstCol = aIn.ReaduInt8();
+ nLastCol = aIn.ReaduInt8();
+ aIn.Ignore( (GetBiff() >= EXC_BIFF5) ? 6 : 2 );
+ nFormLen = aIn.ReaduInt16();
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ if( GetRoot().GetDoc().ValidColRow( nLastCol, nLastRow ) )
+ {
+ // the read mark is now on the formula, length in nFormLen
+
+ pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow), GetCurrScTab() ) );
+ pFormConv->Convert( pResult, maStrm, nFormLen, true );
+
+ SAL_WARN_IF(!pResult, "sc", "+ImportExcel::Array34(): ScTokenArray is NULL!");
+ }
+
+ if (pResult)
+ {
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aArrayRange(nFirstCol, nFirstRow, GetCurrScTab(), nLastCol, nLastRow, GetCurrScTab());
+ rDoc.setMatrixCells(aArrayRange, *pResult, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+}
+
+void ImportExcel::Defrowheight345()
+{
+ sal_uInt16 nFlags, nDefHeight;
+ nFlags = maStrm.ReaduInt16();
+ nDefHeight = maStrm.ReaduInt16();
+
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::Defrowheight345(): pColRowBuff is NULL!");
+ return;
+ }
+
+ pColRowBuff->SetDefHeight( nDefHeight, nFlags );
+}
+
+void ImportExcel::TableOp()
+{
+ sal_uInt16 nFirstRow = aIn.ReaduInt16();
+ sal_uInt16 nLastRow = aIn.ReaduInt16();
+ sal_uInt8 nFirstCol = aIn.ReaduInt8();
+ sal_uInt8 nLastCol = aIn.ReaduInt8();
+ sal_uInt16 nGrbit = aIn.ReaduInt16();
+ sal_uInt16 nInpRow = aIn.ReaduInt16();
+ sal_uInt16 nInpCol = aIn.ReaduInt16();
+ sal_uInt16 nInpRow2 = aIn.ReaduInt16();
+ sal_uInt16 nInpCol2 = aIn.ReaduInt16();
+
+ if (mbFuzzing)
+ {
+ //shrink to smallish arbitrary value to not timeout
+ nLastRow = std::min<sal_uInt16>(nLastRow, MAXROW_30 / 2);
+ }
+
+ if( GetRoot().GetDoc().ValidColRow( nLastCol, nLastRow ) )
+ {
+ if( nFirstCol && nFirstRow )
+ {
+ ScTabOpParam aTabOpParam;
+ aTabOpParam.meMode = (nGrbit & EXC_TABLEOP_BOTH) ? ScTabOpParam::Both : ((nGrbit & EXC_TABLEOP_ROW) ? ScTabOpParam::Row : ScTabOpParam::Column);
+ sal_uInt16 nCol = nFirstCol - 1;
+ sal_uInt16 nRow = nFirstRow - 1;
+ SCTAB nTab = GetCurrScTab();
+ switch (aTabOpParam.meMode)
+ {
+ case ScTabOpParam::Column:
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefFormulaEnd.Set(
+ static_cast<SCCOL>(nLastCol),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ nRow++;
+ break;
+ case ScTabOpParam::Row:
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nFirstRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefFormulaEnd.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nLastRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ nCol++;
+ break;
+ case ScTabOpParam::Both: // TWO-INPUT
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol2),
+ static_cast<SCROW>(nInpRow2), nTab, false, false,
+ false );
+ break;
+ }
+
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aTabOpRange(nCol, nRow, nTab, nLastCol, nLastRow, nTab);
+ rDoc.setTableOpCells(aTabOpRange, aTabOpParam);
+ }
+ }
+ else
+ {
+ bTabTruncated = true;
+ GetTracer().TraceInvalidRow(nLastRow, rD.MaxRow());
+ }
+}
+
+void ImportExcel::Bof4()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ if( nSubType == 0x0100 ) // Book
+ pExcRoot->eDateiTyp = Biff4W;
+ else if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff4C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff4M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff4;
+}
+
+void ImportExcel::Bof5()
+{
+ //POST: eDateiTyp = Type of the file to be read
+ sal_uInt16 nSubType, nVers;
+ BiffTyp eDatei;
+
+ maStrm.DisableDecryption();
+ nVers = maStrm.ReaduInt16();
+ nSubType = maStrm.ReaduInt16( );
+
+ switch( nSubType )
+ {
+ case 0x0005: eDatei = Biff5W; break; // workbook globals
+ case 0x0006: eDatei = Biff5V; break; // VB module
+ case 0x0020: eDatei = Biff5C; break; // chart
+ case 0x0040: eDatei = Biff5M4; break; // macro sheet
+ case 0x0010: // worksheet
+ default: eDatei = Biff5; break; // tdf#144732 Excel interprets invalid indexes as worksheet
+ }
+
+ if( nVers == 0x0600 && (GetBiff() == EXC_BIFF8) )
+ eDatei = static_cast<BiffTyp>( eDatei - Biff5 + Biff8 );
+
+ pExcRoot->eDateiTyp = eDatei;
+}
+
+void ImportExcel::EndSheet()
+{
+ pExcRoot->pExtSheetBuff->Reset();
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ pExcRoot->pExtNameBuff->Reset();
+ mnLastRefIdx = 0;
+ }
+
+ FinalizeTable();
+}
+
+void ImportExcel::NewTable()
+{
+ SCTAB nTab = GetCurrScTab();
+ if( nTab > 0 && !rD.HasTable( nTab ) )
+ rD.MakeTable( nTab );
+
+ if (nTab == 0 && GetBiff() == EXC_BIFF2)
+ {
+ // For Excel 2.1 Worksheet file, we need to set the file name as the
+ // sheet name.
+ INetURLObject aURL(GetDocUrl());
+ rD.RenameTab(0, aURL.getBase());
+ }
+
+ pExcRoot->pShrfmlaBuff->Clear();
+ maLastFormulaCells.clear();
+ mpLastFormula = nullptr;
+
+ InitializeTable( nTab );
+
+ XclImpOutlineDataBuffer* pNewItem = new XclImpOutlineDataBuffer( GetRoot(), nTab );
+ pOutlineListBuffer->push_back( std::unique_ptr<XclImpOutlineDataBuffer>(pNewItem) );
+ pExcRoot->pColRowBuff = pColRowBuff = pNewItem->GetColRowBuff();
+ pColOutlineBuff = pNewItem->GetColOutline();
+ pRowOutlineBuff = pNewItem->GetRowOutline();
+}
+
+std::unique_ptr<ScTokenArray> ImportExcel::ErrorToFormula( bool bErrOrVal, sal_uInt8 nError, double& rVal )
+{
+ return pFormConv->GetBoolErr( XclTools::ErrorToEnum( rVal, bErrOrVal, nError ) );
+}
+
+void ImportExcel::AdjustRowHeight()
+{
+ /* Speed up chart import: import all sheets without charts, then
+ update row heights (here), last load all charts -> do not any longer
+ update inside of ScDocShell::ConvertFrom() (causes update of existing
+ charts during each and every change of row height). */
+ if( ScModelObj* pDocObj = GetDocModelObj() )
+ pDocObj->UpdateAllRowHeights();
+}
+
+void ImportExcel::PostDocLoad()
+{
+ /* Set automatic page numbering in Default page style (default is "page number = 1").
+ Otherwise hidden tables (i.e. for scenarios) which have Default page style will
+ break automatic page numbering. */
+ if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Page ) )
+ pStyleSheet->GetItemSet().Put( SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, 0 ) );
+
+ // outlines for all sheets, sets hidden rows and columns (#i11776# after filtered ranges)
+ for (auto& rxBuffer : *pOutlineListBuffer)
+ rxBuffer->Convert();
+
+ // document view settings (before visible OLE area)
+ GetDocViewSettings().Finalize();
+
+ // process all drawing objects (including OLE, charts, controls; after hiding rows/columns; before visible OLE area)
+ GetObjectManager().ConvertObjects();
+
+ // visible area (used if this document is an embedded OLE object)
+ if( ScDocShell* pDocShell = GetDocShell() )
+ {
+ // visible area if embedded
+ const ScExtDocSettings& rDocSett = GetExtDocOptions().GetDocSettings();
+ SCTAB nDisplScTab = rDocSett.mnDisplTab;
+
+ /* #i44077# If a new OLE object is inserted from file, there is no
+ OLESIZE record in the Excel file. Calculate used area from file
+ contents (used cells and drawing objects). */
+ if( !maScOleSize.IsValid() )
+ {
+ // used area of displayed sheet (cell contents)
+ if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nDisplScTab ) )
+ maScOleSize = pTabSett->maUsedArea;
+ // add all valid drawing objects
+ ScRange aScObjArea = GetObjectManager().GetUsedArea( nDisplScTab );
+ if( aScObjArea.IsValid() )
+ maScOleSize.ExtendTo( aScObjArea );
+ }
+
+ // valid size found - set it at the document
+ if( maScOleSize.IsValid() )
+ {
+ pDocShell->SetVisArea( GetDoc().GetMMRect(
+ maScOleSize.aStart.Col(), maScOleSize.aStart.Row(),
+ maScOleSize.aEnd.Col(), maScOleSize.aEnd.Row(), nDisplScTab ) );
+ GetDoc().SetVisibleTab( nDisplScTab );
+ }
+ }
+
+ // open forms in alive mode (has no effect, if no controls in document)
+ if( ScModelObj* pDocObj = GetDocModelObj() )
+ pDocObj->setPropertyValue( SC_UNO_APPLYFMDES, uno::Any( false ) );
+
+ // enables extended options to be set to the view after import
+ GetExtDocOptions().SetChanged( true );
+
+ // root data owns the extended document options -> create a new object
+ GetDoc().SetExtDocOptions( std::make_unique<ScExtDocOptions>( GetExtDocOptions() ) );
+
+ const SCTAB nLast = rD.GetTableCount();
+ const ScRange* p;
+
+ if( GetRoot().GetPrintAreaBuffer().HasRanges() )
+ {
+ for( SCTAB n = 0 ; n < nLast ; n++ )
+ {
+ p = GetRoot().GetPrintAreaBuffer().First(n);
+ if( p )
+ {
+ rD.ClearPrintRanges( n );
+ while( p )
+ {
+ rD.AddPrintRange( n, *p );
+ p = GetRoot().GetPrintAreaBuffer().Next();
+ }
+ }
+ else
+ {
+ // #i4063# no print ranges -> print entire sheet
+ rD.SetPrintEntireSheet( n );
+ }
+ }
+ GetTracer().TracePrintRange();
+ }
+
+ if( !GetRoot().GetTitleAreaBuffer().HasRanges() )
+ return;
+
+ for( SCTAB n = 0 ; n < nLast ; n++ )
+ {
+ p = GetRoot().GetTitleAreaBuffer().First(n);
+ if( p )
+ {
+ bool bRowVirgin = true;
+ bool bColVirgin = true;
+
+ while( p )
+ {
+ if( p->aStart.Col() == 0 && p->aEnd.Col() == rD.MaxCol() && bRowVirgin )
+ {
+ rD.SetRepeatRowRange( n, *p );
+ bRowVirgin = false;
+ }
+
+ if( p->aStart.Row() == 0 && p->aEnd.Row() == rD.MaxRow() && bColVirgin )
+ {
+ rD.SetRepeatColRange( n, *p );
+ bColVirgin = false;
+ }
+
+ p = GetRoot().GetTitleAreaBuffer().Next();
+ }
+ }
+ }
+}
+
+XclImpOutlineDataBuffer::XclImpOutlineDataBuffer( const XclImpRoot& rRoot, SCTAB nScTab ) :
+ XclImpRoot( rRoot ),
+ mxColOutlineBuff( std::make_shared<XclImpOutlineBuffer>( rRoot.GetXclMaxPos().Col() + 1 ) ),
+ mxRowOutlineBuff( std::make_shared<XclImpOutlineBuffer>( rRoot.GetXclMaxPos().Row() + 1 ) ),
+ mxColRowBuff( std::make_shared<XclImpColRowSettings>( rRoot ) ),
+ mnScTab( nScTab )
+{
+}
+
+XclImpOutlineDataBuffer::~XclImpOutlineDataBuffer()
+{
+}
+
+void XclImpOutlineDataBuffer::Convert()
+{
+ mxColOutlineBuff->SetOutlineArray( &GetDoc().GetOutlineTable( mnScTab, true )->GetColArray() );
+ mxColOutlineBuff->MakeScOutline();
+
+ mxRowOutlineBuff->SetOutlineArray( &GetDoc().GetOutlineTable( mnScTab, true )->GetRowArray() );
+ mxRowOutlineBuff->MakeScOutline();
+
+ mxColRowBuff->ConvertHiddenFlags( mnScTab );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx
new file mode 100644
index 0000000000..d64e77ccd8
--- /dev/null
+++ b/sc/source/filter/excel/namebuff.cxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <namebuff.hxx>
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scextopt.hxx>
+#include <tokenarray.hxx>
+
+#include <root.hxx>
+#include <xiroot.hxx>
+
+#include <osl/diagnose.h>
+
+sal_uInt32 StringHashEntry::MakeHashCode( const OUString& r )
+{
+ sal_uInt32 n = 0;
+ const sal_Unicode* pCurrent = r.getStr();
+ sal_Unicode cCurrent = *pCurrent;
+
+ while( cCurrent )
+ {
+ n *= 70;
+ n += static_cast<sal_uInt32>(cCurrent);
+ pCurrent++;
+ cCurrent = *pCurrent;
+ }
+
+ return n;
+}
+
+SharedFormulaBuffer::SharedFormulaBuffer(RootData* pRD)
+ : ExcRoot(pRD)
+{
+}
+
+SharedFormulaBuffer::~SharedFormulaBuffer()
+{
+ Clear();
+}
+
+void SharedFormulaBuffer::Clear()
+{
+ maTokenArrays.clear();
+}
+
+void SharedFormulaBuffer::Store( const ScAddress& rPos, const ScTokenArray& rArray )
+{
+ ScTokenArray aCode(rArray.CloneValue());
+ aCode.GenHash();
+ maTokenArrays.emplace(rPos, std::move(aCode));
+}
+
+const ScTokenArray* SharedFormulaBuffer::Find( const ScAddress& rRefPos ) const
+{
+ TokenArraysType::const_iterator it = maTokenArrays.find(rRefPos);
+ if (it == maTokenArrays.end())
+ return nullptr;
+
+ return &it->second;
+}
+
+sal_Int16 ExtSheetBuffer::Add( const OUString& rFPAN, const OUString& rTN, const bool bSWB )
+{
+ maEntries.emplace_back( rFPAN, rTN, bSWB );
+ // return 1-based index of EXTERNSHEET
+ return static_cast< sal_Int16 >( maEntries.size() );
+}
+
+bool ExtSheetBuffer::GetScTabIndex( sal_uInt16 nExcIndex, sal_uInt16& rScIndex )
+{
+ OSL_ENSURE( nExcIndex,
+ "*ExtSheetBuffer::GetScTabIndex(): Sheet-Index == 0!" );
+
+ if ( !nExcIndex || nExcIndex > maEntries.size() )
+ return false;
+
+ Cont* pCur = &maEntries[ nExcIndex - 1 ];
+ sal_uInt16& rTabNum = pCur->nTabNum;
+
+ if( rTabNum < 0xFFFD )
+ {
+ rScIndex = rTabNum;
+ return true;
+ }
+
+ if( rTabNum == 0xFFFF )
+ {// create new table
+ SCTAB nNewTabNum;
+ if( pCur->bSWB )
+ {// table is in the same workbook!
+ if( pExcRoot->pIR->GetDoc().GetTable( pCur->aTab, nNewTabNum ) )
+ {
+ rScIndex = rTabNum = static_cast<sal_uInt16>(nNewTabNum);
+ return true;
+ }
+ else
+ rTabNum = 0xFFFD;
+ }
+ else if( pExcRoot->pIR->GetDocShell() )
+ {// table is 'really' external
+ if( pExcRoot->pIR->GetExtDocOptions().GetDocSettings().mnLinkCnt == 0 )
+ {
+ OUString aURL( ScGlobal::GetAbsDocName( pCur->aFile,
+ pExcRoot->pIR->GetDocShell() ) );
+ OUString aTabName( ScGlobal::GetDocTabName( aURL, pCur->aTab ) );
+ if( pExcRoot->pIR->GetDoc().LinkExternalTab( nNewTabNum, aTabName, aURL, pCur->aTab ) )
+ {
+ rScIndex = rTabNum = static_cast<sal_uInt16>(nNewTabNum);
+ return true;
+ }
+ else
+ rTabNum = 0xFFFE; // no table is created for now -> and likely
+ // will not be created later...
+ }
+ else
+ rTabNum = 0xFFFE;
+
+ }
+ }
+
+ return false;
+}
+
+void ExtSheetBuffer::Reset()
+{
+ maEntries.clear();
+}
+
+bool ExtName::IsOLE() const
+{
+ return ( nFlags & 0x0002 ) != 0;
+}
+
+ExtNameBuff::ExtNameBuff( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void ExtNameBuff::AddDDE( sal_Int16 nRefIdx )
+{
+ ExtName aNew( 0x0001 );
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+void ExtNameBuff::AddOLE( sal_Int16 nRefIdx, sal_uInt32 nStorageId )
+{
+ ExtName aNew( 0x0002 );
+ aNew.nStorageId = nStorageId;
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+void ExtNameBuff::AddName( sal_Int16 nRefIdx )
+{
+ ExtName aNew( 0x0004 );
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+const ExtName* ExtNameBuff::GetNameByIndex( sal_Int16 nRefIdx, sal_uInt16 nNameIdx ) const
+{
+ OSL_ENSURE( nNameIdx > 0, "ExtNameBuff::GetNameByIndex() - invalid name index" );
+ ExtNameMap::const_iterator aIt = maExtNames.find( nRefIdx );
+ return ((aIt != maExtNames.end()) && (0 < nNameIdx) && (nNameIdx <= aIt->second.size())) ? &aIt->second[ nNameIdx - 1 ] : nullptr;
+}
+
+void ExtNameBuff::Reset()
+{
+ maExtNames.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/ooxml-export-TODO.txt b/sc/source/filter/excel/ooxml-export-TODO.txt
new file mode 100644
index 0000000000..d995598d2f
--- /dev/null
+++ b/sc/source/filter/excel/ooxml-export-TODO.txt
@@ -0,0 +1,164 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+TODO/Unimplemented Calc OOXML Export Features:
+=============================================
+
+Partially implemented features are not mentioned here; grep for OOXTODO within
+sc/source/filter/*.
+
+In updated OfficeFileFormatsProtocols.zip [MS-XLS].pdf,
+Section §2.3.1 (p.154) provides the record name :: record number mapping, and
+Section §2.3.2 (p.165) provides the record number :: record name mapping.
+
+Elements:
+ - Workbook (§3.2):
+ - customWorkbookViews (§3.2.3)
+ - ext (§3.2.7)
+ - extLst (§3.2.10)
+ - fileRecoveryPr (§3.2.11) [ CRASHRECERR? 865h ]
+ - fileSharing (§3.2.12) [ FILESHARING 5Bh ]
+ - functionGroup (§3.2.14) [ FNGRP12 898h; FNGROUPNAME 9Ah ]
+ - functionGroups (§3.2.15) [ FNGROUPCOUNT: 9Ch ]
+ - oleSize (§3.2.16) [ OLESIZE DEh ]
+ - smartTagPr (§3.2.21) [ BOOKEXT 863h ]
+ - smartTagType (§3.2.22) [ unknown record ]
+ - smartTagTypes (§3.2.23) [ unknown record ]
+ - webPublishing (§3.2.24) [ WOPT 80Bh ]
+ - webPublishObject (§3.2.25) [ WEBPUB 801h ]
+ - webPublishObjects (§3.2.26) [ unsupported ]
+ - Worksheets (§3.3.1):
+ - autoFilter (§3.3.1.1) [ AutoFilter 9Eh ]
+ - cellSmartTag (§3.3.1.4) [ FEAT 868h ]
+ - cellSmartTagPr (§3.3.1.5) [ FEAT? 868h ]
+ - cellSmartTags (§3.3.1.6) [ FEAT 868h ]
+ - cellWatch (§3.3.1.7) [ CELLWATCH 86Ch ]
+ - cellWatches (§3.3.1.8) [ CELLWATCH 86Ch ]
+ - cfRule (§3.3.1.9) [ CF 1B1h ]
+ - cfvo (§3.3.1.10) [ CF12 87Ah ]
+ - chartsheet (§3.3.1.11) [ CHARTFRTINFO 850h, FRTWRAPPER 851h...]
+ - color (§3.3.1.14) [ DXF 88Dh xfpropBorder?
+ XFEXT 87Dh xclrType? ]
+ - colorScale (§3.3.1.15) [ DXF 88Dh? ]
+ - control (§3.3.1.18) [ ??? ]
+ - controls (§3.3.1.19) [ ??? ]
+ - customPr (§3.3.1.20) [ ??? ]
+ - customProperties (§3.3.1.21) [ ??? ]
+ - customSheetView (§3.3.1.22) [ ???; for charts; see chartsheet? ]
+ - customSheetView (§3.3.1.23) [ ??? ]
+ - customSheetViews (§3.3.1.24) [ ???; for charts; see chartsheet? ]
+ - customSheetViews (§3.3.1.25) [ ??? ]
+ - dataBar (§3.3.1.26) [ CF12 87Ah ct=Databar ]
+ - dataConsolidate (§3.3.1.27) [ DCON 50h ]
+ - dataRef (§3.3.1.28) [ DCONBIN 1B5h ]
+ - dataRefs (§3.3.1.29) [ ??? ]
+ - dialogsheet (§3.3.1.32) [ ??? ]
+ - drawing (§3.3.1.34) [ ??? ]
+ - evenFooter (§3.3.1.35) [ HeaderFooter 89Ch ]
+ - evenHeader (§3.3.1.36) [ HeaderFooter 89Ch ]
+ - firstFooter (§3.3.1.38) [ HeaderFooter 89Ch ]
+ - firstHeader (§3.3.1.39) [ HeaderFooter 89Ch ]
+ - formula (§3.3.1.40) [ CF 1B1h ]
+ - iconSet (§3.3.1.46) [ CF12 87Ah ct=CFMultistate ]
+ - ignoredError (§3.3.1.47) [ Feat/FeatFormulaErr2/FFErrorCheck 868h ]
+ - ignoredErrors (§3.3.1.48) [ Feat 868h ]
+ - legacyDrawing (§3.3.1.51) [ MsoDrawing ECh ]
+ - legacyDrawingHF (§3.3.1.52) [ ??? ]
+ - oleObject (§3.3.1.57) [ ??? ]
+ - oleObjects (§3.3.1.58) [ ??? ]
+ - outlinePr (§3.3.1.59) [ ??? ]
+ - pageSetup (§3.3.1.62) [ ???; for charts; see chartsheet? ]
+ - picture (§3.3.1.65) [ BkHim E9h; see XclExpBitmap ]
+ - pivotArea (§3.3.1.66) [ ??? ]
+ - pivotSelection (§3.3.1.67) [ ??? ]
+ - protectedRange (§3.3.1.69) [ ??? ]
+ - protectedRanges (§3.3.1.70) [ ??? ]
+ - sheetCalcPr (§3.3.1.76) [ REFRESHALL?? ]
+ - sheetFormatPr (§3.3.1.78) [ lots of records? ]
+ @defaultColWidth: DefColWidth
+ @defaultRowHeight: DEFROWHEIGHT
+ @baseColWidth: ColInfo/coldx?
+ @customHeight: ColInfo/fUserSet?
+ @zeroHeight: ColInfo/fHidden?
+ @thickTop: ?
+ @thickBottom: ?
+ @outlineLevelRow: ?
+ @outlineLevelCol: ColInfo/iOutLevel?
+ - sheetPr (§3.3.1.80) [ ??? ; for charts ]
+ - sheetView (§3.3.1.84) [ ??? ; for charts ]
+ - sheetViews (§3.3.1.86) [ ??? ; for charts ]
+ - smartTags (§3.3.1.87) [ FEAT 868h; isf=ISFFACTOID ]
+ - sortCondition (§3.3.1.88) [ SortData 895h? ]
+ - sortState (§3.3.1.89) [ Sort 90h ]
+ - tabColor (§3.3.1.90) [ SheetExt 862h ]
+ - tablePart (§3.3.1.91) [ ??? ]
+ - tableParts (§3.3.1.92) [ ??? ]
+ - webPublishItem (§3.3.1.94) [ WebPub 801h ]
+ - webPublishItems (§3.3.1.95)
+ - AutoFilter Settings (§3.3.2):
+ - colorFilter (§3.3.2.1) [ AutoFilter12 87Eh,
+ DXFN12NoCB struct ]
+ - dateGroupItem (§3.3.2.4) [ AutoFilter12 87Eh,
+ AF12DateInfo struct ]
+ - dynamicFilter (§3.3.2.5) [ AutoFilter12 87Eh, cft field ]
+ - filter (§3.3.2.6) [ AutoFilter12 87Eh, rgCriteria? ]
+ - filters (§3.3.2.9) [ AutoFilter12 87Eh, rgCriteria? ]
+ - iconFilter (§3.3.2.9) [ AutoFilter12 87Eh,
+ AF12CellIcon struct ]
+ - Shared String Table (§3.4):
+ - phoneticPr (§3.4.3)
+ - rPh (§3.4.6)
+ - Tables (§3.5.1):
+ - calculatedColumnFormula (§3.5.1.1)
+ [ ??? ]
+ - table (§3.5.1.2) [ ??? ]
+ - tableColumn (§3.5.1.3) [ ??? ]
+ - tableColumns (§3.5.1.4) [ ??? ]
+ - tableStyleInfo (§3.5.1.5) [ ??? ]
+ - totalRowFormula (§3.5.1.6) [ ??? ]
+ - xmlColumnPr (§3.5.1.7) [ ??? ]
+ - Single Cell Tables (§3.5.2):
+ - singleXmlCell (§3.5.2.1) [ ??? ]
+ - singleXmlCells (§3.5.2.2) [ ??? ]
+ - xmlCellPr (§3.5.2.3) [ ??? ]
+ - xmlPr (§3.5.2.4) [ ??? ]
+ - Calculation Chain (§3.6):
+ - c (§3.6.1) [ ??? ]
+ - calcChain (§3.6.2) [ ??? ]
+ - Comments (§3.7):
+ - Note: Excel *requires* that there be a drawing object associated
+ with the comment before it will show it. If you _just_ generate the
+ <comments/> XML part and create a <Relationship/> for it, Excel
+ will NOT display the comment.
+ - As drawing is not currently implemented, comments support is
+ incomplete.
+ - TODO: text formatting. Currently we only write unformatted text
+ into comments?.xml, as I'm not sure how formatted text is handled.
+ - Styles (§3.8):
+ - dxf (§3.8.14): [ DXF 88Dh; unsupported ]
+ - dxfs (§3.8.15): [ DXF 88Dh ]
+ - gradientFill (§3.8.23): [ ??? ]
+ - horizontal (§3.8.24): [ DXF 88Dh fNewBorder, xfprops ]
+ - mruColors (§3.8.28): [ ??? ]
+ - scheme (§3.8.36): [ ??? ]
+ - stop (§3.8.38): [ ??? ]
+ - tableStyle (§3.8.40): [ TableStyle 88Fh; unsupported ]
+ - tableStyleElement (§3.8.41): [ TableStyleElement 890h; unsupported ]
+ - tableStyles (§3.8.42): [ TableStyles 88Eh; unsupported ]
+ - vertical (§3.8.44): [ DXF 88Dh fNewBorder, xfprops ]
+
diff --git a/sc/source/filter/excel/read.cxx b/sc/source/filter/excel/read.cxx
new file mode 100644
index 0000000000..ad58040729
--- /dev/null
+++ b/sc/source/filter/excel/read.cxx
@@ -0,0 +1,1312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scerrors.hxx>
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <xlcontent.hxx>
+#include <xltracer.hxx>
+#include <xltable.hxx>
+#include <xihelper.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+#include <xlname.hxx>
+#include <xicontent.hxx>
+#include <xiescher.hxx>
+#include <xipivot.hxx>
+#include <xistyle.hxx>
+#include <XclImpChangeTrack.hxx>
+#include <documentimport.hxx>
+
+#include <root.hxx>
+#include <imp_op.hxx>
+#include <excimp8.hxx>
+
+#include <unotools/configmgr.hxx>
+
+#include <memory>
+
+namespace
+{
+ bool TryStartNextRecord(XclImpStream& rIn, std::size_t nProgressBasePos)
+ {
+ bool bValid = true;
+ // i#115255 fdo#40304 BOUNDSHEET doesn't point to a valid
+ // BOF record position. Scan the records manually (from
+ // the BOUNDSHEET position) until we find a BOF. Some 3rd
+ // party Russian programs generate invalid xls docs with
+ // this kind of silliness.
+ if (rIn.PeekRecId(nProgressBasePos) == EXC_ID5_BOF)
+ // BOUNDSHEET points to a valid BOF record. Good.
+ rIn.StartNextRecord(nProgressBasePos);
+ else
+ {
+ while (bValid && rIn.GetRecId() != EXC_ID5_BOF)
+ bValid = rIn.StartNextRecord();
+ }
+ return bValid;
+ }
+}
+
+ErrCode ImportExcel::Read()
+{
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+ XclImpPalette& rPal = GetPalette();
+ XclImpFontBuffer& rFontBfr = GetFontBuffer();
+ XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer();
+ XclImpXFBuffer& rXFBfr = GetXFBuffer();
+ XclImpNameManager& rNameMgr = GetNameManager();
+ // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets)
+
+ enum STATE {
+ Z_BiffNull, // not a valid Biff-Format
+ Z_Biff2, // Biff2: only one table
+
+ Z_Biff3, // Biff3: only one table
+
+ Z_Biff4, // Biff4: only one table
+ Z_Biff4W, // Biff4 Workbook: Globals
+ Z_Biff4T, // Biff4 Workbook: a table itself
+ Z_Biff4E, // Biff4 Workbook: between tables
+
+ Z_Biff5WPre,// Biff5: Prefetch Workbook
+ Z_Biff5W, // Biff5: Globals
+ Z_Biff5TPre,// Biff5: Prefetch for Shrfmla/Array Formula
+ Z_Biff5T, // Biff5: a table itself
+ Z_Biff5E, // Biff5: between tables
+ Z_Biffn0, // all Biffs: skip table till next EOF
+ Z_End };
+
+ STATE eCurrent = Z_BiffNull, ePrev = Z_BiffNull;
+
+ ErrCode eLastErr = ERRCODE_NONE;
+ sal_uInt16 nOpcode;
+ sal_uInt16 nBofLevel = 0;
+
+ std::unique_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar(
+ aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) );
+
+ /* #i104057# Need to track a base position for progress bar calculation,
+ because sheet substreams may not be in order of sheets. */
+ std::size_t nProgressBasePos = 0;
+ std::size_t nProgressBaseSize = 0;
+
+ for (; eCurrent != Z_End; mnLastRecId = nOpcode)
+ {
+ if( eCurrent == Z_Biff5E )
+ {
+ sal_uInt16 nScTab = GetCurrScTab();
+ if( nScTab < maSheetOffsets.size() )
+ {
+ nProgressBaseSize += (aIn.GetSvStreamPos() - nProgressBasePos);
+ nProgressBasePos = maSheetOffsets[ nScTab ];
+
+ bool bValid = TryStartNextRecord(aIn, nProgressBasePos);
+ if (!bValid)
+ {
+ // Safeguard ourselves from potential infinite loop.
+ eCurrent = Z_End;
+ }
+ }
+ else
+ eCurrent = Z_End;
+ }
+ else
+ aIn.StartNextRecord();
+
+ nOpcode = aIn.GetRecId();
+
+ if( !aIn.IsValid() )
+ {
+ // finalize table if EOF is missing
+ switch( eCurrent )
+ {
+ case Z_Biff2:
+ case Z_Biff3:
+ case Z_Biff4:
+ case Z_Biff4T:
+ case Z_Biff5TPre:
+ case Z_Biff5T:
+ rNumFmtBfr.CreateScFormats();
+ Eof();
+ break;
+ default:;
+ }
+ break;
+ }
+
+ if( eCurrent == Z_End )
+ break;
+
+ if( eCurrent != Z_Biff5TPre && eCurrent != Z_Biff5WPre )
+ pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos );
+
+ switch( eCurrent )
+ {
+
+ case Z_BiffNull: // ------------------------------- Z_BiffNull -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF:
+ {
+ // #i23425# don't rely on the record ID, but on the detected BIFF version
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ Bof2();
+ if( pExcRoot->eDateiTyp == Biff2 )
+ {
+ eCurrent = Z_Biff2;
+ NewTable();
+ }
+ break;
+ case EXC_BIFF3:
+ Bof3();
+ if( pExcRoot->eDateiTyp == Biff3 )
+ {
+ eCurrent = Z_Biff3;
+ NewTable();
+ }
+ break;
+ case EXC_BIFF4:
+ Bof4();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4;
+ NewTable();
+ }
+ else if( pExcRoot->eDateiTyp == Biff4W )
+ eCurrent = Z_Biff4W;
+ break;
+ case EXC_BIFF5:
+ Bof5();
+ if( pExcRoot->eDateiTyp == Biff5W )
+ {
+ eCurrent = Z_Biff5WPre;
+
+ nBdshtTab = 0;
+
+ aIn.StoreGlobalPosition(); // store position
+ }
+ else if( pExcRoot->eDateiTyp == Biff5 )
+ {
+ // #i62752# possible to have BIFF5 sheet without globals
+ NewTable();
+ eCurrent = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch
+ nBofLevel = 0;
+ aIn.StoreGlobalPosition(); // store position
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case Z_Biff2: // ---------------------------------- Z_Biff2 -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x06: Formula25(); break; // FORMULA [ 2 5]
+ case 0x08: Row25(); break; // ROW [ 2 5]
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x18: rNameMgr.ReadName( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x20: Columndefault(); break; // COLUMNDEFAULT[ 2 ]
+ case 0x21: Array25(); break; // ARRAY [ 2 5]
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x24: Colwidth(); break; // COLWIDTH [ 2 ]
+ case 0x25: Defrowheight2(); break; // DEFAULTROWHEI[ 2 ]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID_EFONT: rFontBfr.ReadEfont( maStrm ); break;
+ case 0x3E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x43: rXFBfr.ReadXF( maStrm ); break;
+ case 0x44: Ixfe(); break; // IXFE [ 2 ]
+ }
+ }
+ break;
+
+ case Z_Biff3: // ---------------------------------- Z_Biff3 -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x0206: Formula3(); break; // FORMULA [ 3 ]
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0243: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+ }
+ break;
+
+ case Z_Biff4: // ---------------------------------- Z_Biff4 -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0406: Formula4(); break; // FORMULA [ 4 ]
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+ }
+ break;
+
+ case Z_Biff4W: // --------------------------------- Z_Biff4W -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ rNameMgr.ConvertAllTokens();
+ eCurrent = Z_End;
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5]
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID4_BOF: // BOF [ 4 ]
+ Bof4();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4T;
+ NewTable();
+ }
+ else
+ eCurrent = Z_End;
+ break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff4T: // --------------------------------- Z_Biff4T -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_Biff4E;
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break;
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0406: Formula4(); break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff4E: // --------------------------------- Z_Biff4E -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_End;
+ break;
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case EXC_ID4_BOF: // BOF [ 4 ]
+ Bof4();
+ NewTable();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4T;
+ }
+ else
+ {
+ ePrev = eCurrent;
+ eCurrent = Z_Biffn0;
+ }
+ break;
+ }
+
+ }
+ break;
+ case Z_Biff5WPre: // ------------------------------ Z_Biff5WPre -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_Biff5W;
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5]
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x3D: Window1(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ // PALETTE follows XFs, but already needed while reading the XFs
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ }
+ }
+ break;
+ case Z_Biff5W: // --------------------------------- Z_Biff5W -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rXFBfr.CreateUserStyles();
+ rNameMgr.ConvertAllTokens();
+ eCurrent = Z_Biff5E;
+ break;
+ case 0x18: rNameMgr.ReadName( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x31: rFontBfr.ReadFont( maStrm ); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8D: Hideobj(); break; // HIDEOBJ [ 345]
+ case 0xDE: Olesize(); break;
+ case 0xE0: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff5TPre: // ------------------------------- Z_Biff5Pre -
+ {
+ if (nOpcode == EXC_ID5_BOF)
+ nBofLevel++;
+ else if( (nOpcode == 0x000A) && nBofLevel )
+ nBofLevel--;
+ else if( !nBofLevel ) // don't read chart records
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case 0x08: Row25(); break; // ROW [ 2 5]
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_Biff5T;
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x21: Array25(); break; // ARRAY [ 2 5]
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x81: Wsbool(); break; // WSBOOL [ 2345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ }
+ }
+ }
+ break;
+
+ case Z_Biff5T: // --------------------------------- Z_Biff5T -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA: Formula25(); break;
+ case EXC_ID_SHRFMLA: Shrfmla(); break;
+ case 0x0A: Eof(); eCurrent = Z_Biff5E; break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x83:
+ case 0x84: rPageSett.ReadCenter( maStrm ); break;
+ case 0xA0: rTabViewSett.ReadScl( maStrm ); break;
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0xBD: Mulrk(); break; // MULRK [ 5]
+ case 0xBE: Mulblank(); break; // MULBLANK [ 5]
+ case 0xD6: Rstring(); break; // RSTRING [ 5]
+ case 0x00E5: Cellmerging(); break; // #i62300#
+ case 0x0236: TableOp(); break; // TABLE [ 5]
+ case EXC_ID5_BOF: // BOF [ 5]
+ XclTools::SkipSubStream( maStrm );
+ break;
+ }
+
+ }
+ break;
+
+ case Z_Biff5E: // --------------------------------- Z_Biff5E -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID5_BOF: // BOF [ 5]
+ Bof5();
+ NewTable();
+ switch( pExcRoot->eDateiTyp )
+ {
+ case Biff5:
+ case Biff5M4:
+ eCurrent = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch
+ nBofLevel = 0;
+ aIn.StoreGlobalPosition(); // store position
+ break;
+ case Biff5C: // chart sheet
+ GetCurrSheetDrawing().ReadTabChart( maStrm );
+ Eof();
+ GetTracer().TraceChartOnlySheet();
+ break;
+ case Biff5V:
+ default:
+ rD.SetVisible( GetCurrScTab(), false );
+ ePrev = eCurrent;
+ eCurrent = Z_Biffn0;
+ }
+ OSL_ENSURE( pExcRoot->eDateiTyp != Biff5W,
+ "+ImportExcel::Read(): Doppel-Whopper-Workbook!" );
+
+ break;
+ }
+
+ }
+ break;
+ case Z_Biffn0: // --------------------------------- Z_Biffn0 -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = ePrev;
+ IncCurrScTab();
+ break;
+ }
+
+ }
+ break;
+
+ case Z_End: // ----------------------------------- Z_End -
+ OSL_FAIL( "*ImportExcel::Read(): Not possible state!" );
+ break;
+ default: OSL_FAIL( "-ImportExcel::Read(): state forgotten!" );
+ }
+ }
+
+ if( eLastErr == ERRCODE_NONE )
+ {
+ pProgress.reset();
+
+ GetDocImport().finalize();
+ if (!utl::ConfigManager::IsFuzzing())
+ AdjustRowHeight();
+ PostDocLoad();
+
+ rD.CalcAfterLoad(false);
+
+ const XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsTabTruncated() )
+ eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW;
+ else if( bTabTruncated || rAddrConv.IsRowTruncated() )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
+ }
+
+ return eLastErr;
+}
+
+ErrCode ImportExcel8::Read()
+{
+#ifdef EXC_INCL_DUMPER
+ {
+ Biff8RecDumper aDumper( GetRoot(), sal_True );
+ if( aDumper.Dump( aIn ) )
+ return ERRCODE_ABORT;
+ }
+#endif
+ // read the entire BIFF8 stream
+ // don't look too close - this stuff seriously needs to be reworked
+
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+ XclImpPalette& rPal = GetPalette();
+ XclImpFontBuffer& rFontBfr = GetFontBuffer();
+ XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer();
+ XclImpXFBuffer& rXFBfr = GetXFBuffer();
+ XclImpSst& rSst = GetSst();
+ XclImpTabInfo& rTabInfo = GetTabInfo();
+ XclImpNameManager& rNameMgr = GetNameManager();
+ XclImpLinkManager& rLinkMgr = GetLinkManager();
+ XclImpObjectManager& rObjMgr = GetObjectManager();
+ // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets)
+ XclImpCondFormatManager& rCondFmtMgr = GetCondFormatManager();
+ XclImpValidationManager& rValidMgr = GetValidationManager();
+ XclImpPivotTableManager& rPTableMgr = GetPivotTableManager();
+ XclImpWebQueryBuffer& rWQBfr = GetWebQueryBuffer();
+
+ bool bInUserView = false; // true = In USERSVIEW(BEGIN|END) record block.
+
+ enum XclImpReadState
+ {
+ EXC_STATE_BEFORE_GLOBALS, /// Before workbook globals (wait for initial BOF).
+ EXC_STATE_GLOBALS_PRE, /// Prefetch for workbook globals.
+ EXC_STATE_GLOBALS, /// Workbook globals.
+ EXC_STATE_BEFORE_SHEET, /// Before worksheet (wait for new worksheet BOF).
+ EXC_STATE_SHEET_PRE, /// Prefetch for worksheet.
+ EXC_STATE_SHEET, /// Worksheet.
+ EXC_STATE_END /// Stop reading.
+ };
+
+ XclImpReadState eCurrent = EXC_STATE_BEFORE_GLOBALS;
+
+ ErrCode eLastErr = ERRCODE_NONE;
+
+ std::unique_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar(
+ aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) );
+
+ /* #i104057# Need to track a base position for progress bar calculation,
+ because sheet substreams may not be in order of sheets. */
+ std::size_t nProgressBasePos = 0;
+ std::size_t nProgressBaseSize = 0;
+
+ bool bSheetHasCodeName = false;
+
+ std::vector<OUString> aCodeNames;
+ std::vector < SCTAB > nTabsWithNoCodeName;
+
+ sal_uInt16 nRecId = 0;
+
+ for (; eCurrent != EXC_STATE_END; mnLastRecId = nRecId)
+ {
+ if( eCurrent == EXC_STATE_BEFORE_SHEET )
+ {
+ sal_uInt16 nScTab = GetCurrScTab();
+ if( nScTab < maSheetOffsets.size() )
+ {
+ nProgressBaseSize += (maStrm.GetSvStreamPos() - nProgressBasePos);
+ nProgressBasePos = maSheetOffsets[ nScTab ];
+
+ bool bValid = TryStartNextRecord(aIn, nProgressBasePos);
+ if (!bValid)
+ {
+ // Safeguard ourselves from potential infinite loop.
+ eCurrent = EXC_STATE_END;
+ }
+
+ // import only 256 sheets
+ if( nScTab > GetScMaxPos().Tab() )
+ {
+ if( maStrm.GetRecId() != EXC_ID_EOF )
+ XclTools::SkipSubStream( maStrm );
+ // #i29930# show warning box
+ GetAddressConverter().CheckScTab( nScTab );
+ eCurrent = EXC_STATE_END;
+ }
+ else
+ {
+ // #i109800# SHEET record may point to any record inside the
+ // sheet substream
+ bool bIsBof = maStrm.GetRecId() == EXC_ID5_BOF;
+ if( bIsBof )
+ Bof5(); // read the BOF record
+ else
+ pExcRoot->eDateiTyp = Biff8; // on missing BOF, assume a standard worksheet
+ NewTable();
+ switch( pExcRoot->eDateiTyp )
+ {
+ case Biff8: // worksheet
+ case Biff8M4: // macro sheet
+ eCurrent = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch
+ // go to next record
+ if( bIsBof ) maStrm.StartNextRecord();
+ maStrm.StoreGlobalPosition();
+ break;
+ case Biff8C: // chart sheet
+ GetCurrSheetDrawing().ReadTabChart( maStrm );
+ Eof();
+ GetTracer().TraceChartOnlySheet();
+ break;
+ case Biff8W: // workbook
+ OSL_FAIL( "ImportExcel8::Read - double workbook globals" );
+ [[fallthrough]];
+ case Biff8V: // VB module
+ default:
+ // TODO: do not create a sheet in the Calc document
+ rD.SetVisible( nScTab, false );
+ XclTools::SkipSubStream( maStrm );
+ IncCurrScTab();
+ }
+ }
+ }
+ else
+ eCurrent = EXC_STATE_END;
+ }
+ else
+ aIn.StartNextRecord();
+
+ if( !aIn.IsValid() )
+ {
+ // #i63591# finalize table if EOF is missing
+ switch( eCurrent )
+ {
+ case EXC_STATE_SHEET_PRE:
+ eCurrent = EXC_STATE_SHEET;
+ aIn.SeekGlobalPosition();
+ continue; // next iteration in while loop
+ case EXC_STATE_SHEET:
+ Eof();
+ eCurrent = EXC_STATE_END;
+ break;
+ default:
+ eCurrent = EXC_STATE_END;
+ }
+ }
+
+ if( eCurrent == EXC_STATE_END )
+ break;
+
+ if( eCurrent != EXC_STATE_SHEET_PRE && eCurrent != EXC_STATE_GLOBALS_PRE )
+ pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos );
+
+ nRecId = aIn.GetRecId();
+
+ /* #i39464# Ignore records between USERSVIEWBEGIN and USERSVIEWEND
+ completely (user specific view settings). Otherwise view settings
+ and filters are loaded multiple times, which at least causes
+ problems in auto-filters. */
+ switch( nRecId )
+ {
+ case EXC_ID_USERSVIEWBEGIN:
+ OSL_ENSURE( !bInUserView, "ImportExcel8::Read - nested user view settings" );
+ bInUserView = true;
+ break;
+ case EXC_ID_USERSVIEWEND:
+ OSL_ENSURE( bInUserView, "ImportExcel8::Read - not in user view settings" );
+ bInUserView = false;
+ break;
+ }
+
+ if( !bInUserView ) switch( eCurrent )
+ {
+
+ // before workbook globals: wait for initial workbook globals BOF
+ case EXC_STATE_BEFORE_GLOBALS:
+ {
+ if( nRecId == EXC_ID5_BOF )
+ {
+ OSL_ENSURE( GetBiff() == EXC_BIFF8, "ImportExcel8::Read - wrong BIFF version" );
+ Bof5();
+ if( pExcRoot->eDateiTyp == Biff8W )
+ {
+ eCurrent = EXC_STATE_GLOBALS_PRE;
+ maStrm.StoreGlobalPosition();
+ nBdshtTab = 0;
+ }
+ else if( pExcRoot->eDateiTyp == Biff8 )
+ {
+ // #i62752# possible to have BIFF8 sheet without globals
+ NewTable();
+ eCurrent = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch
+ bSheetHasCodeName = false; // reset
+ aIn.StoreGlobalPosition();
+ }
+ }
+ }
+ break;
+
+ // prefetch for workbook globals
+ case EXC_STATE_GLOBALS_PRE:
+ {
+ switch( nRecId )
+ {
+ case EXC_ID_EOF:
+ case EXC_ID_EXTSST:
+ /* #i56376# evil hack: if EOF for globals is missing,
+ simulate it. This hack works only for the bugdoc
+ given in the issue, where the sheet substreams
+ start directly after the EXTSST record. A future
+ implementation should be more robust against
+ missing EOFs. */
+ if( (nRecId == EXC_ID_EOF) ||
+ ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) )
+ {
+ eCurrent = EXC_STATE_GLOBALS;
+ aIn.SeekGlobalPosition();
+ }
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5678]
+ case 0x13: DocPassword(); break;
+ case 0x19: WinProtection(); break;
+ case 0x2F: // FILEPASS [ 2345 ]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = EXC_STATE_END;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x3D: Window1(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345 ]
+ case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345 ]
+
+ // PALETTE follows XFs, but already needed while reading the XFs
+ case EXC_ID_PALETTE: rPal.ReadPalette( maStrm ); break;
+ }
+ }
+ break;
+
+ // workbook globals
+ case EXC_STATE_GLOBALS:
+ {
+ switch( nRecId )
+ {
+ case EXC_ID_EOF:
+ case EXC_ID_EXTSST:
+ /* #i56376# evil hack: if EOF for globals is missing,
+ simulate it. This hack works only for the bugdoc
+ given in the issue, where the sheet substreams
+ start directly after the EXTSST record. A future
+ implementation should be more robust against
+ missing EOFs. */
+ if( (nRecId == EXC_ID_EOF) ||
+ ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) )
+ {
+ rNumFmtBfr.CreateScFormats();
+ rXFBfr.CreateUserStyles();
+ rPTableMgr.ReadPivotCaches( maStrm );
+ rNameMgr.ConvertAllTokens();
+ eCurrent = EXC_STATE_BEFORE_SHEET;
+ }
+ break;
+ case 0x0E: Precision(); break; // PRECISION
+ case 0x22: Rec1904(); break; // 1904 [ 2345 ]
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8D: Hideobj(); break; // HIDEOBJ [ 345 ]
+ case 0xD3: SetHasBasic(); break;
+ case 0xDE: Olesize(); break;
+
+ case EXC_ID_CODENAME: ReadCodeName( aIn, true ); break;
+ case EXC_ID_USESELFS: ReadUsesElfs(); break;
+
+ case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID4_FORMAT: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case EXC_ID5_XF: rXFBfr.ReadXF( maStrm ); break;
+ case EXC_ID_STYLE: rXFBfr.ReadStyle( maStrm ); break;
+
+ case EXC_ID_SST: rSst.ReadSst( maStrm ); break;
+ case EXC_ID_TABID: rTabInfo.ReadTabid( maStrm ); break;
+ case EXC_ID_NAME: rNameMgr.ReadName( maStrm ); break;
+
+ case EXC_ID_EXTERNSHEET: rLinkMgr.ReadExternsheet( maStrm ); break;
+ case EXC_ID_SUPBOOK: rLinkMgr.ReadSupbook( maStrm ); break;
+ case EXC_ID_XCT: rLinkMgr.ReadXct( maStrm ); break;
+ case EXC_ID_CRN: rLinkMgr.ReadCrn( maStrm ); break;
+ case EXC_ID_EXTERNNAME: rLinkMgr.ReadExternname( maStrm, pFormConv.get() ); break;
+
+ case EXC_ID_MSODRAWINGGROUP:rObjMgr.ReadMsoDrawingGroup( maStrm ); break;
+
+ case EXC_ID_SXIDSTM: rPTableMgr.ReadSxidstm( maStrm ); break;
+ case EXC_ID_SXVS: rPTableMgr.ReadSxvs( maStrm ); break;
+ case EXC_ID_DCONREF: rPTableMgr.ReadDconref( maStrm ); break;
+ case EXC_ID_DCONNAME: rPTableMgr.ReadDConName( maStrm ); break;
+ }
+
+ }
+ break;
+
+ // prefetch for worksheet
+ case EXC_STATE_SHEET_PRE:
+ {
+ switch( nRecId )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case EXC_ID_SCL: rTabViewSett.ReadScl( maStrm ); break;
+ case EXC_ID_PANE: rTabViewSett.ReadPane( maStrm ); break;
+ case EXC_ID_SELECTION: rTabViewSett.ReadSelection( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+
+ case EXC_ID_CODENAME: ReadCodeName( aIn, false ); bSheetHasCodeName = true; break;
+
+ case 0x0A: // EOF [ 2345 ]
+ {
+ eCurrent = EXC_STATE_SHEET;
+ OUString sName;
+ GetDoc().GetName( GetCurrScTab(), sName );
+ if ( !bSheetHasCodeName )
+ {
+ nTabsWithNoCodeName.push_back( GetCurrScTab() );
+ }
+ else
+ {
+ OUString sCodeName;
+ GetDoc().GetCodeName( GetCurrScTab(), sCodeName );
+ aCodeNames.push_back( sCodeName );
+ }
+
+ bSheetHasCodeName = false; // reset
+
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ }
+ case 0x12: SheetProtect(); break;
+ case 0x13: SheetPassword(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345 ]
+ case 0x55: DefColWidth(); break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345 ]
+ case 0x81: Wsbool(); break; // WSBOOL [ 2345 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345 ]
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45 ]
+ case 0x9B: FilterMode(); break; // FILTERMODE
+ case EXC_ID_AUTOFILTERINFO: AutoFilterInfo(); break;// AUTOFILTERINFO
+ case EXC_ID_AUTOFILTER: AutoFilter(); break; // AUTOFILTER
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case EXC_ID2_ARRAY:
+ case EXC_ID3_ARRAY: Array34(); break; // ARRAY [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345 ]
+ case 0x0867: FeatHdr(); break; // FEATHDR
+ case 0x0868: Feat(); break; // FEAT
+ }
+ }
+ break;
+
+ // worksheet
+ case EXC_STATE_SHEET:
+ {
+ switch( nRecId )
+ {
+ // skip unknown substreams
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID_EOF: Eof(); eCurrent = EXC_STATE_BEFORE_SHEET; break;
+
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA: Formula25(); break;
+ case EXC_ID_SHRFMLA: Shrfmla(); break;
+ case 0x000C: Calccount(); break; // CALCCOUNT
+ case 0x0010: Delta(); break; // DELTA
+ case 0x0011: Iteration(); break; // ITERATION
+ case 0x007E:
+ case 0x00AE: Scenman(); break; // SCENMAN
+ case 0x00AF: Scenario(); break; // SCENARIO
+ case 0x00BD: Mulrk(); break; // MULRK [ 5 ]
+ case 0x00BE: Mulblank(); break; // MULBLANK [ 5 ]
+ case 0x00D6: Rstring(); break; // RSTRING [ 5 ]
+ case 0x00E5: Cellmerging(); break; // CELLMERGING
+ case 0x00FD: Labelsst(); break; // LABELSST [ 8 ]
+ case 0x0236: TableOp(); break; // TABLE
+
+ case EXC_ID_HORPAGEBREAKS:
+ case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( maStrm ); break;
+ case EXC_ID_HEADER:
+ case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( maStrm ); break;
+ case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case EXC_ID_HCENTER:
+ case EXC_ID_VCENTER: rPageSett.ReadCenter( maStrm ); break;
+ case EXC_ID_SETUP: rPageSett.ReadSetup( maStrm ); break;
+ case EXC_ID8_IMGDATA: rPageSett.ReadImgData( maStrm ); break;
+
+ case EXC_ID_MSODRAWING: GetCurrSheetDrawing().ReadMsoDrawing( maStrm ); break;
+ // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
+ case EXC_ID_OBJ: GetCurrSheetDrawing().ReadObj( maStrm ); break;
+ case EXC_ID_NOTE: GetCurrSheetDrawing().ReadNote( maStrm ); break;
+
+ case EXC_ID_HLINK: XclImpHyperlink::ReadHlink( maStrm ); break;
+ case EXC_ID_LABELRANGES: XclImpLabelranges::ReadLabelranges( maStrm ); break;
+
+ case EXC_ID_CONDFMT: rCondFmtMgr.ReadCondfmt( maStrm ); break;
+ case EXC_ID_CF: rCondFmtMgr.ReadCF( maStrm ); break;
+
+ case EXC_ID_DVAL: XclImpValidationManager::ReadDval( maStrm ); break;
+ case EXC_ID_DV: rValidMgr.ReadDV( maStrm ); break;
+
+ case EXC_ID_QSI: rWQBfr.ReadQsi( maStrm ); break;
+ case EXC_ID_WQSTRING: rWQBfr.ReadWqstring( maStrm ); break;
+ case EXC_ID_PQRY: rWQBfr.ReadParamqry( maStrm ); break;
+ case EXC_ID_WQSETT: rWQBfr.ReadWqsettings( maStrm ); break;
+ case EXC_ID_WQTABLES: rWQBfr.ReadWqtables( maStrm ); break;
+
+ case EXC_ID_SXVIEW: rPTableMgr.ReadSxview( maStrm ); break;
+ case EXC_ID_SXVD: rPTableMgr.ReadSxvd( maStrm ); break;
+ case EXC_ID_SXVI: rPTableMgr.ReadSxvi( maStrm ); break;
+ case EXC_ID_SXIVD: rPTableMgr.ReadSxivd( maStrm ); break;
+ case EXC_ID_SXPI: rPTableMgr.ReadSxpi( maStrm ); break;
+ case EXC_ID_SXDI: rPTableMgr.ReadSxdi( maStrm ); break;
+ case EXC_ID_SXVDEX: rPTableMgr.ReadSxvdex( maStrm ); break;
+ case EXC_ID_SXEX: rPTableMgr.ReadSxex( maStrm ); break;
+ case EXC_ID_SHEETEXT: rTabViewSett.ReadTabBgColor( maStrm, rPal ); break;
+ case EXC_ID_SXVIEWEX9: rPTableMgr.ReadSxViewEx9( maStrm ); break;
+ case EXC_ID_SXADDL: rPTableMgr.ReadSxAddl( maStrm ); break;
+ }
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ if( eLastErr == ERRCODE_NONE )
+ {
+ // In some strange circumstances the codename might be missing
+ // # Create any missing Sheet CodeNames
+ for ( const auto& rTab : nTabsWithNoCodeName )
+ {
+ SCTAB nTab = 1;
+ while ( true )
+ {
+ OUString sTmpName = "Sheet" + OUString::number(static_cast<sal_Int32>(nTab++));
+
+ if ( std::find(aCodeNames.begin(), aCodeNames.end(), sTmpName) == aCodeNames.end() ) // generated codename not found
+ {
+ // Set new codename
+ GetDoc().SetCodeName( rTab, sTmpName );
+ // Record newly used codename
+ aCodeNames.push_back(sTmpName);
+ break;
+ }
+ }
+ }
+ // #i45843# Convert pivot tables before calculation, so they are available
+ // for the GETPIVOTDATA function.
+ if( GetBiff() == EXC_BIFF8 )
+ GetPivotTableManager().ConvertPivotTables();
+
+ ScDocumentImport& rDoc = GetDocImport();
+ rDoc.finalize();
+ pProgress.reset();
+#if 0
+ // Excel documents look much better without this call; better in the
+ // sense that the row heights are identical to the original heights in
+ // Excel.
+ if ( !rD.IsAdjustHeightLocked())
+ AdjustRowHeight();
+#endif
+ PostDocLoad();
+
+ rD.CalcAfterLoad(false);
+
+ // import change tracking data
+ XclImpChangeTrack aImpChTr( GetRoot(), maStrm );
+ aImpChTr.Apply();
+
+ const XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsTabTruncated() )
+ eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW;
+ else if( bTabTruncated || rAddrConv.IsRowTruncated() )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
+
+ if( GetBiff() == EXC_BIFF8 )
+ GetPivotTableManager().MaybeRefreshPivotTables();
+ }
+
+ return eLastErr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/tokstack.cxx b/sc/source/filter/excel/tokstack.cxx
new file mode 100644
index 0000000000..a0e3974caa
--- /dev/null
+++ b/sc/source/filter/excel/tokstack.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tokstack.hxx>
+#include <scmatrix.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <string.h>
+
+const sal_uInt16 TokenPool::nScTokenOff = 8192;
+
+TokenStack::TokenStack( )
+ : pStack( new TokenId[ nSize ] )
+{
+ Reset();
+}
+
+TokenStack::~TokenStack()
+{
+}
+
+// !ATTENTION!": to the outside the numbering starts with 1!
+// !ATTENTION!": SC-Token are stored with an offset nScTokenOff
+// -> to distinguish from other tokens
+
+TokenPool::TokenPool( svl::SharedStringPool& rSPool ) :
+ mrStringPool(rSPool)
+{
+ // pool for Id-sequences
+ nP_Id = 256;
+ pP_Id.reset( new sal_uInt16[ nP_Id ] );
+
+ // pool for Ids
+ nElement = 32;
+ pElement.reset( new sal_uInt16[ nElement ] );
+ pType.reset( new E_TYPE[ nElement ] );
+ pSize.reset( new sal_uInt16[ nElement ] );
+ nP_IdLast = 0;
+
+ nP_Matrix = 16;
+ ppP_Matrix.reset( new ScMatrix*[ nP_Matrix ] );
+ memset( ppP_Matrix.get(), 0, sizeof( ScMatrix* ) * nP_Matrix );
+
+ Reset();
+}
+
+TokenPool::~TokenPool()
+{
+ ClearMatrix();
+}
+
+/** Returns the new number of elements, or 0 if overflow. */
+static sal_uInt16 lcl_canGrow( sal_uInt16 nOld )
+{
+ if (!nOld)
+ return 1;
+ if (nOld == SAL_MAX_UINT16)
+ return 0;
+ sal_uInt32 nNew = ::std::max( static_cast<sal_uInt32>(nOld) * 2,
+ static_cast<sal_uInt32>(nOld) + 1);
+ if (nNew > SAL_MAX_UINT16)
+ nNew = SAL_MAX_UINT16;
+ if (nNew - 1 < nOld)
+ nNew = 0;
+ return static_cast<sal_uInt16>(nNew);
+}
+
+bool TokenPool::GrowId()
+{
+ sal_uInt16 nP_IdNew = lcl_canGrow( nP_Id);
+ if (!nP_IdNew)
+ return false;
+
+ sal_uInt16* pP_IdNew = new (::std::nothrow) sal_uInt16[ nP_IdNew ];
+ if (!pP_IdNew)
+ return false;
+
+ for( sal_uInt16 nL = 0 ; nL < nP_Id ; nL++ )
+ pP_IdNew[ nL ] = pP_Id[ nL ];
+
+ nP_Id = nP_IdNew;
+
+ pP_Id.reset( pP_IdNew );
+ return true;
+}
+
+bool TokenPool::CheckElementOrGrow()
+{
+ // Last possible ID to be assigned somewhere is nElementCurrent+1
+ if (nElementCurrent + 1 == nScTokenOff - 1)
+ {
+ SAL_WARN("sc.filter","TokenPool::CheckElementOrGrow - last possible ID " << nElementCurrent+1);
+ return false;
+ }
+
+ if (nElementCurrent >= nElement)
+ return GrowElement();
+
+ return true;
+}
+
+bool TokenPool::GrowElement()
+{
+ sal_uInt16 nElementNew = lcl_canGrow( nElement);
+ if (!nElementNew)
+ return false;
+
+ std::unique_ptr<sal_uInt16[]> pElementNew(new (::std::nothrow) sal_uInt16[ nElementNew ]);
+ std::unique_ptr<E_TYPE[]> pTypeNew(new (::std::nothrow) E_TYPE[ nElementNew ]);
+ std::unique_ptr<sal_uInt16[]> pSizeNew(new (::std::nothrow) sal_uInt16[ nElementNew ]);
+ if (!pElementNew || !pTypeNew || !pSizeNew)
+ {
+ return false;
+ }
+
+ for( sal_uInt16 nL = 0 ; nL < nElement ; nL++ )
+ {
+ pElementNew[ nL ] = pElement[ nL ];
+ pTypeNew[ nL ] = pType[ nL ];
+ pSizeNew[ nL ] = pSize[ nL ];
+ }
+
+ nElement = nElementNew;
+
+ pElement = std::move( pElementNew );
+ pType = std::move( pTypeNew );
+ pSize = std::move( pSizeNew );
+ return true;
+}
+
+bool TokenPool::GrowMatrix()
+{
+ sal_uInt16 nNewSize = lcl_canGrow( nP_Matrix);
+ if (!nNewSize)
+ return false;
+
+ ScMatrix** ppNew = new (::std::nothrow) ScMatrix*[ nNewSize ];
+ if (!ppNew)
+ return false;
+
+ memset( ppNew, 0, sizeof( ScMatrix* ) * nNewSize );
+ for( sal_uInt16 nL = 0 ; nL < nP_Matrix ; nL++ )
+ ppNew[ nL ] = ppP_Matrix[ nL ];
+
+ ppP_Matrix.reset( ppNew );
+ nP_Matrix = nNewSize;
+ return true;
+}
+
+bool TokenPool::GetElement( const sal_uInt16 nId, ScTokenArray* pScToken )
+{
+ if (nId >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter","TokenPool::GetElement - Id too large, " << nId << " >= " << nElementCurrent);
+ return false;
+ }
+
+ bool bRet = true;
+ if( pType[ nId ] == T_Id )
+ bRet = GetElementRek( nId, pScToken );
+ else
+ {
+ switch( pType[ nId ] )
+ {
+ case T_Str:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto* p = ppP_Str.getIfInRange( n );
+ if (p)
+ pScToken->AddString(mrStringPool.intern(**p));
+ else
+ bRet = false;
+ }
+ break;
+ case T_D:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < pP_Dbl.m_writemark)
+ pScToken->AddDouble( pP_Dbl[ n ] );
+ else
+ bRet = false;
+ }
+ break;
+ case T_Err:
+ break;
+/* TODO: in case we had FormulaTokenArray::AddError() */
+#if 0
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < nP_Err)
+ pScToken->AddError( pP_Err[ n ] );
+ else
+ bRet = false;
+ }
+#endif
+ case T_RefC:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_RefTr.getIfInRange(n);
+ if (p)
+ pScToken->AddSingleReference( **p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_RefA:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < ppP_RefTr.m_writemark && ppP_RefTr[ n ] && n+1 < ppP_RefTr.m_writemark && ppP_RefTr[ n + 1 ])
+ {
+ ScComplexRefData aScComplexRefData;
+ aScComplexRefData.Ref1 = *ppP_RefTr[ n ];
+ aScComplexRefData.Ref2 = *ppP_RefTr[ n + 1 ];
+ pScToken->AddDoubleReference( aScComplexRefData );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_RN:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maRangeNames.size())
+ {
+ const RangeName& r = maRangeNames[n];
+ pScToken->AddRangeName(r.mnIndex, r.mnSheet);
+ }
+ }
+ break;
+ case T_Ext:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_Ext.getIfInRange(n);
+
+ if( p )
+ {
+ if( (*p)->eId == ocEuroConvert )
+ pScToken->AddOpCode( (*p)->eId );
+ else
+ pScToken->AddExternal( (*p)->aText, (*p)->eId );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_Nlf:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_Nlf.getIfInRange(n);
+
+ if( p )
+ pScToken->AddColRowName( **p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_Matrix:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ ScMatrix* p = ( n < nP_Matrix )? ppP_Matrix[ n ] : nullptr;
+
+ if( p )
+ pScToken->AddMatrix( p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtName:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtNames.size())
+ {
+ const ExtName& r = maExtNames[n];
+ pScToken->AddExternalName(r.mnFileId, mrStringPool.intern( r.maName));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtRefC:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtCellRefs.size())
+ {
+ const ExtCellRef& r = maExtCellRefs[n];
+ pScToken->AddExternalSingleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtRefA:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtAreaRefs.size())
+ {
+ const ExtAreaRef& r = maExtAreaRefs[n];
+ pScToken->AddExternalDoubleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ OSL_FAIL("-TokenPool::GetElement(): undefined state!?");
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+bool TokenPool::GetElementRek( const sal_uInt16 nId, ScTokenArray* pScToken )
+{
+#ifdef DBG_UTIL
+ m_nRek++;
+ OSL_ENSURE(m_nRek <= nP_Id, "*TokenPool::GetElement(): recursion loops!?");
+#endif
+
+ OSL_ENSURE( nId < nElementCurrent, "*TokenPool::GetElementRek(): nId >= nElementCurrent" );
+
+ if (nId >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter", "*TokenPool::GetElementRek(): nId >= nElementCurrent");
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return false;
+ }
+
+ if (pType[ nId ] != T_Id)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::GetElementRek(): pType[ nId ] != T_Id");
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return false;
+ }
+
+ bool bRet = true;
+ sal_uInt16 nCnt = pSize[ nId ];
+ sal_uInt16 nFirstId = pElement[ nId ];
+ if (nFirstId >= nP_Id)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: nFirstId >= nP_Id");
+ nCnt = 0;
+ bRet = false;
+ }
+ sal_uInt16* pCurrent = nCnt ? &pP_Id[ nFirstId ] : nullptr;
+ if (nCnt > nP_Id - nFirstId)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: nCnt > nP_Id - nFirstId");
+ nCnt = nP_Id - nFirstId;
+ bRet = false;
+ }
+ for( ; nCnt > 0 ; nCnt--, pCurrent++ )
+ {
+ assert(pCurrent);
+ if( *pCurrent < nScTokenOff )
+ {// recursion or not?
+ if (*pCurrent >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: *pCurrent >= nElementCurrent");
+ bRet = false;
+ }
+ else
+ {
+ if (pType[ *pCurrent ] == T_Id)
+ bRet = GetElementRek( *pCurrent, pScToken );
+ else
+ bRet = GetElement( *pCurrent, pScToken );
+ }
+ }
+ else // elementary SC_Token
+ pScToken->AddOpCode( static_cast<DefTokenId>( *pCurrent - nScTokenOff ) );
+ }
+
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return bRet;
+}
+
+void TokenPool::operator >>( TokenId& rId )
+{
+ rId = static_cast<TokenId>( nElementCurrent + 1 );
+
+ if (!CheckElementOrGrow())
+ return;
+
+ pElement[ nElementCurrent ] = nP_IdLast; // Start of Token-sequence
+ pType[ nElementCurrent ] = T_Id; // set Typeinfo
+ pSize[ nElementCurrent ] = nP_IdCurrent - nP_IdLast;
+ // write from nP_IdLast to nP_IdCurrent-1 -> length of the sequence
+
+ nElementCurrent++; // start value for next sequence
+ nP_IdLast = nP_IdCurrent;
+}
+
+TokenId TokenPool::Store( const double& rDouble )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( pP_Dbl.m_writemark >= pP_Dbl.m_capacity )
+ if (!pP_Dbl.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = pP_Dbl.m_writemark; // Index in Double-Array
+ pType[ nElementCurrent ] = T_D; // set Typeinfo Double
+
+ pP_Dbl[ pP_Dbl.m_writemark ] = rDouble;
+
+ pSize[ nElementCurrent ] = 1; // does not matter
+
+ nElementCurrent++;
+ pP_Dbl.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const sal_uInt16 nIndex )
+{
+ return StoreName(nIndex, -1);
+}
+
+TokenId TokenPool::Store( const OUString& rString )
+{
+ // mostly copied to Store( const char* ), to avoid a temporary string
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Str.m_writemark >= ppP_Str.m_capacity )
+ if (!ppP_Str.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Str.m_writemark; // Index in String-Array
+ pType[ nElementCurrent ] = T_Str; // set Typeinfo String
+
+ // create String
+ if( !ppP_Str[ ppP_Str.m_writemark ] )
+ //...but only, if it does not exist already
+ ppP_Str[ ppP_Str.m_writemark ].reset( new OUString( rString ) );
+ else
+ //...copy otherwise
+ *ppP_Str[ ppP_Str.m_writemark ] = rString;
+
+ /* attention truncate to 16 bits */
+ pSize[ nElementCurrent ] = static_cast<sal_uInt16>(ppP_Str[ ppP_Str.m_writemark ]->getLength());
+
+ nElementCurrent++;
+ ppP_Str.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const ScSingleRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_RefTr.m_writemark >= ppP_RefTr.m_capacity )
+ if (!ppP_RefTr.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_RefTr.m_writemark;
+ pType[ nElementCurrent ] = T_RefC; // set Typeinfo Cell-Ref
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr;
+
+ nElementCurrent++;
+ ppP_RefTr.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const ScComplexRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_RefTr.m_writemark + 1 >= ppP_RefTr.m_capacity )
+ if (!ppP_RefTr.Grow(2))
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_RefTr.m_writemark;
+ pType[ nElementCurrent ] = T_RefA; // setTypeinfo Area-Ref
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref1 ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref1;
+ ppP_RefTr.m_writemark++;
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref2 ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref2;
+ ppP_RefTr.m_writemark++;
+
+ nElementCurrent++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const DefTokenId e, const OUString& r )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Ext.m_writemark >= ppP_Ext.m_capacity )
+ if (!ppP_Ext.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Ext.m_writemark;
+ pType[ nElementCurrent ] = T_Ext; // set Typeinfo String
+
+ if( ppP_Ext[ ppP_Ext.m_writemark ] )
+ {
+ ppP_Ext[ ppP_Ext.m_writemark ]->eId = e;
+ ppP_Ext[ ppP_Ext.m_writemark ]->aText = r;
+ }
+ else
+ ppP_Ext[ ppP_Ext.m_writemark ].reset( new EXTCONT( e, r ) );
+
+ nElementCurrent++;
+ ppP_Ext.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::StoreNlf( const ScSingleRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Nlf.m_writemark >= ppP_Nlf.m_capacity )
+ if (!ppP_Nlf.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Nlf.m_writemark;
+ pType[ nElementCurrent ] = T_Nlf;
+
+ if( ppP_Nlf[ ppP_Nlf.m_writemark ] )
+ {
+ *ppP_Nlf[ ppP_Nlf.m_writemark ] = rTr;
+ }
+ else
+ ppP_Nlf[ ppP_Nlf.m_writemark ].reset( new ScSingleRefData( rTr ) );
+
+ nElementCurrent++;
+ ppP_Nlf.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreMatrix()
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( nP_MatrixCurrent >= nP_Matrix )
+ if (!GrowMatrix())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = nP_MatrixCurrent;
+ pType[ nElementCurrent ] = T_Matrix;
+
+ ScMatrix* pM = new ScMatrix( 0, 0 );
+ pM->IncRef( );
+ ppP_Matrix[ nP_MatrixCurrent ] = pM;
+
+ nElementCurrent++;
+ nP_MatrixCurrent++;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreName( sal_uInt16 nIndex, sal_Int16 nSheet )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maRangeNames.size());
+ pType[nElementCurrent] = T_RN;
+
+ maRangeNames.emplace_back();
+ RangeName& r = maRangeNames.back();
+ r.mnIndex = nIndex;
+ r.mnSheet = nSheet;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtName( sal_uInt16 nFileId, const OUString& rName )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtNames.size());
+ pType[nElementCurrent] = T_ExtName;
+
+ maExtNames.emplace_back();
+ ExtName& r = maExtNames.back();
+ r.mnFileId = nFileId;
+ r.maName = rName;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtCellRefs.size());
+ pType[nElementCurrent] = T_ExtRefC;
+
+ maExtCellRefs.emplace_back();
+ ExtCellRef& r = maExtCellRefs.back();
+ r.mnFileId = nFileId;
+ r.maTabName = rTabName;
+ r.maRef = rRef;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtAreaRefs.size());
+ pType[nElementCurrent] = T_ExtRefA;
+
+ maExtAreaRefs.emplace_back();
+ ExtAreaRef& r = maExtAreaRefs.back();
+ r.mnFileId = nFileId;
+ r.maTabName = rTabName;
+ r.maRef = rRef;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+void TokenPool::Reset()
+{
+ nP_IdCurrent = nP_IdLast = nElementCurrent
+ = ppP_Str.m_writemark = pP_Dbl.m_writemark = pP_Err.m_writemark
+ = ppP_RefTr.m_writemark = ppP_Ext.m_writemark = ppP_Nlf.m_writemark = nP_MatrixCurrent = 0;
+ maRangeNames.clear();
+ maExtNames.clear();
+ maExtCellRefs.clear();
+ maExtAreaRefs.clear();
+ ClearMatrix();
+}
+
+bool TokenPool::IsSingleOp( const TokenId& rId, const DefTokenId eId ) const
+{
+ sal_uInt16 nId = static_cast<sal_uInt16>(rId);
+ if( nId && nId <= nElementCurrent )
+ {// existent?
+ nId--;
+ if( T_Id == pType[ nId ] )
+ {// Token-Sequence?
+ if( pSize[ nId ] == 1 )
+ {// EXACTLY 1 Token
+ sal_uInt16 nPid = pElement[ nId ];
+ if (nPid < nP_Id)
+ {
+ sal_uInt16 nSecId = pP_Id[ nPid ];
+ if( nSecId >= nScTokenOff )
+ {// Default-Token?
+ return static_cast<DefTokenId>( nSecId - nScTokenOff ) == eId; // wanted?
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+const OUString* TokenPool::GetExternal( const TokenId& rId ) const
+{
+ const OUString* p = nullptr;
+ sal_uInt16 n = static_cast<sal_uInt16>(rId);
+ if( n && n <= nElementCurrent )
+ {
+ n--;
+ if( pType[ n ] == T_Ext )
+ {
+ sal_uInt16 nExt = pElement[ n ];
+ if ( nExt < ppP_Ext.m_writemark && ppP_Ext[ nExt ] )
+ p = &ppP_Ext[ nExt ]->aText;
+ }
+ }
+
+ return p;
+}
+
+ScMatrix* TokenPool::GetMatrix( unsigned int n ) const
+{
+ if( n < nP_MatrixCurrent )
+ return ppP_Matrix[ n ];
+ else
+ SAL_WARN("sc.filter", "GetMatrix: " << n << " >= " << nP_MatrixCurrent);
+ return nullptr;
+}
+
+void TokenPool::ClearMatrix()
+{
+ for(sal_uInt16 n = 0 ; n < nP_Matrix ; n++ )
+ {
+ if( ppP_Matrix[ n ] )
+ {
+ ppP_Matrix[ n ]->DecRef( );
+ ppP_Matrix[n] = nullptr;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xechart.cxx b/sc/source/filter/excel/xechart.cxx
new file mode 100644
index 0000000000..b09f370dd2
--- /dev/null
+++ b/sc/source/filter/excel/xechart.cxx
@@ -0,0 +1,3475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xechart.hxx>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart/TimeInterval.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart/XAxisSupplier.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XTitled.hpp>
+#include <com/sun/star/chart2/XColorScheme.hpp>
+#include <com/sun/star/chart2/data/XDataSource.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/CurveStyle.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/TickmarkStyle.hpp>
+
+#include <tools/gen.hxx>
+#include <filter/msfilter/escherex.hxx>
+
+#include <document.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <xeescher.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xepage.hxx>
+#include <xestyle.hxx>
+#include <xltools.hxx>
+
+#include <memory>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+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::Exception;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::i18n::XBreakIterator;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XShapes;
+
+using ::com::sun::star::chart2::IncrementData;
+using ::com::sun::star::chart2::RelativePosition;
+using ::com::sun::star::chart2::RelativeSize;
+using ::com::sun::star::chart2::ScaleData;
+using ::com::sun::star::chart2::SubIncrement;
+using ::com::sun::star::chart2::XAxis;
+using ::com::sun::star::chart2::XChartDocument;
+using ::com::sun::star::chart2::XChartTypeContainer;
+using ::com::sun::star::chart2::XColorScheme;
+using ::com::sun::star::chart2::XCoordinateSystem;
+using ::com::sun::star::chart2::XCoordinateSystemContainer;
+using ::com::sun::star::chart2::XChartType;
+using ::com::sun::star::chart2::XDataSeries;
+using ::com::sun::star::chart2::XDataSeriesContainer;
+using ::com::sun::star::chart2::XDiagram;
+using ::com::sun::star::chart2::XFormattedString;
+using ::com::sun::star::chart2::XLegend;
+using ::com::sun::star::chart2::XRegressionCurve;
+using ::com::sun::star::chart2::XRegressionCurveContainer;
+using ::com::sun::star::chart2::XTitle;
+using ::com::sun::star::chart2::XTitled;
+
+using ::com::sun::star::chart2::data::XDataSequence;
+using ::com::sun::star::chart2::data::XDataSource;
+using ::com::sun::star::chart2::data::XLabeledDataSequence;
+
+using ::formula::FormulaToken;
+using ::formula::FormulaTokenArrayPlainIterator;
+
+namespace cssc = ::com::sun::star::chart;
+namespace cssc2 = ::com::sun::star::chart2;
+
+// Helpers ====================================================================
+
+namespace {
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclChRectangle& rRect )
+{
+ return rStrm << rRect.mnX << rRect.mnY << rRect.mnWidth << rRect.mnHeight;
+}
+
+void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec )
+{
+ if( xRec )
+ xRec->Save( rStrm );
+}
+
+/** Saves the passed record (group) together with a leading value record. */
+template< typename Type >
+void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec, sal_uInt16 nRecId, Type nValue )
+{
+ if( xRec )
+ {
+ XclExpValueRecord< Type >( nRecId, nValue ).Save( rStrm );
+ xRec->Save( rStrm );
+ }
+}
+
+template<typename ValueType, typename KeyType>
+void lclSaveRecord(XclExpStream& rStrm, ValueType* pRec, sal_uInt16 nRecId, KeyType nValue)
+{
+ if (pRec)
+ {
+ XclExpValueRecord<KeyType>(nRecId, nValue).Save(rStrm);
+ pRec->Save(rStrm);
+ }
+}
+
+void lclWriteChFrBlockRecord( XclExpStream& rStrm, const XclChFrBlock& rFrBlock, bool bBegin )
+{
+ sal_uInt16 nRecId = bBegin ? EXC_ID_CHFRBLOCKBEGIN : EXC_ID_CHFRBLOCKEND;
+ rStrm.StartRecord( nRecId, 12 );
+ rStrm << nRecId << EXC_FUTUREREC_EMPTYFLAGS << rFrBlock.mnType << rFrBlock.mnContext << rFrBlock.mnValue1 << rFrBlock.mnValue2;
+ rStrm.EndRecord();
+}
+
+template< typename Type >
+bool lclIsAutoAnyOrGetValue( Type& rValue, const Any& rAny )
+{
+ return !rAny.hasValue() || !(rAny >>= rValue);
+}
+
+bool lclIsAutoAnyOrGetScaledValue( double& rfValue, const Any& rAny, bool bLogScale )
+{
+ bool bIsAuto = lclIsAutoAnyOrGetValue( rfValue, rAny );
+ if( !bIsAuto && bLogScale )
+ rfValue = log( rfValue ) / log( 10.0 );
+ return bIsAuto;
+}
+
+sal_uInt16 lclGetTimeValue( const XclExpRoot& rRoot, double fSerialDate, sal_uInt16 nTimeUnit )
+{
+ DateTime aDateTime = rRoot.GetDateTimeFromDouble( fSerialDate );
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS:
+ return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
+ case EXC_CHDATERANGE_MONTHS:
+ return ::limit_cast< sal_uInt16, sal_uInt16 >( 12 * (aDateTime.GetYear() - rRoot.GetBaseYear()) + aDateTime.GetMonth() - 1, 0, SAL_MAX_INT16 );
+ case EXC_CHDATERANGE_YEARS:
+ return ::limit_cast< sal_uInt16, sal_uInt16 >( aDateTime.GetYear() - rRoot.GetBaseYear(), 0, SAL_MAX_INT16 );
+ default:
+ OSL_ENSURE( false, "lclGetTimeValue - unexpected time unit" );
+ }
+ return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
+}
+
+bool lclConvertTimeValue( const XclExpRoot& rRoot, sal_uInt16& rnValue, const Any& rAny, sal_uInt16 nTimeUnit )
+{
+ double fSerialDate = 0;
+ bool bAuto = lclIsAutoAnyOrGetValue( fSerialDate, rAny );
+ if( !bAuto )
+ rnValue = lclGetTimeValue( rRoot, fSerialDate, nTimeUnit );
+ return bAuto;
+}
+
+sal_uInt16 lclGetTimeUnit( sal_Int32 nApiTimeUnit )
+{
+ switch( nApiTimeUnit )
+ {
+ case cssc::TimeUnit::DAY: return EXC_CHDATERANGE_DAYS;
+ case cssc::TimeUnit::MONTH: return EXC_CHDATERANGE_MONTHS;
+ case cssc::TimeUnit::YEAR: return EXC_CHDATERANGE_YEARS;
+ default: OSL_ENSURE( false, "lclGetTimeUnit - unexpected time unit" );
+ }
+ return EXC_CHDATERANGE_DAYS;
+}
+
+bool lclConvertTimeInterval( sal_uInt16& rnValue, sal_uInt16& rnTimeUnit, const Any& rAny )
+{
+ cssc::TimeInterval aInterval;
+ bool bAuto = lclIsAutoAnyOrGetValue( aInterval, rAny );
+ if( !bAuto )
+ {
+ rnValue = ::limit_cast< sal_uInt16, sal_Int32 >( aInterval.Number, 1, SAL_MAX_UINT16 );
+ rnTimeUnit = lclGetTimeUnit( aInterval.TimeUnit );
+ }
+ return bAuto;
+}
+
+} // namespace
+
+// Common =====================================================================
+
+/** Stores global data needed in various classes of the Chart export filter. */
+struct XclExpChRootData : public XclChRootData
+{
+ typedef ::std::vector< XclChFrBlock > XclChFrBlockVector;
+
+ XclExpChChart& mrChartData; /// The chart data object.
+ XclChFrBlockVector maWrittenFrBlocks; /// Stack of future record levels already written out.
+ XclChFrBlockVector maUnwrittenFrBlocks; /// Stack of future record levels not yet written out.
+
+ explicit XclExpChRootData( XclExpChChart& rChartData ) : mrChartData( rChartData ) {}
+
+ /** Registers a new future record level. */
+ void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock );
+ /** Initializes the current future record level (writes all unwritten CHFRBLOCKBEGIN records). */
+ void InitializeFutureRecBlock( XclExpStream& rStrm );
+ /** Finalizes the current future record level (writes CHFRBLOCKEND record if needed). */
+ void FinalizeFutureRecBlock( XclExpStream& rStrm );
+};
+
+void XclExpChRootData::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
+{
+ maUnwrittenFrBlocks.push_back( rFrBlock );
+}
+
+void XclExpChRootData::InitializeFutureRecBlock( XclExpStream& rStrm )
+{
+ // first call from a future record writes all missing CHFRBLOCKBEGIN records
+ if( maUnwrittenFrBlocks.empty() )
+ return;
+
+ // write the leading CHFRINFO record
+ if( maWrittenFrBlocks.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_CHFRINFO, 20 );
+ rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 );
+ rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B );
+ rStrm.EndRecord();
+ }
+ // write all unwritten CHFRBLOCKBEGIN records
+ for( const auto& rUnwrittenFrBlock : maUnwrittenFrBlocks )
+ {
+ OSL_ENSURE( rUnwrittenFrBlock.mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" );
+ lclWriteChFrBlockRecord( rStrm, rUnwrittenFrBlock, true );
+ }
+ // move all record infos to vector of written blocks
+ maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() );
+ maUnwrittenFrBlocks.clear();
+}
+
+void XclExpChRootData::FinalizeFutureRecBlock( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !maUnwrittenFrBlocks.empty() || !maWrittenFrBlocks.empty(), "XclExpChRootData::FinalizeFutureRecBlock - no future record level found" );
+ if( !maUnwrittenFrBlocks.empty() )
+ {
+ // no future record has been written, just forget the topmost level
+ maUnwrittenFrBlocks.pop_back();
+ }
+ else if( !maWrittenFrBlocks.empty() )
+ {
+ // write the CHFRBLOCKEND record for the topmost block and delete it
+ lclWriteChFrBlockRecord( rStrm, maWrittenFrBlocks.back(), false );
+ maWrittenFrBlocks.pop_back();
+ }
+}
+
+XclExpChRoot::XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData ) :
+ XclExpRoot( rRoot ),
+ mxChData( std::make_shared<XclExpChRootData>( rChartData ) )
+{
+}
+
+XclExpChRoot::~XclExpChRoot()
+{
+}
+
+Reference< XChartDocument > const & XclExpChRoot::GetChartDocument() const
+{
+ return mxChData->mxChartDoc;
+}
+
+XclExpChChart& XclExpChRoot::GetChartData() const
+{
+ return mxChData->mrChartData;
+}
+
+const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
+}
+
+const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( std::u16string_view rServiceName ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfoFromService( rServiceName );
+}
+
+const XclChFormatInfo& XclExpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
+}
+
+void XclExpChRoot::InitConversion( css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) const
+{
+ mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
+}
+
+void XclExpChRoot::FinishConversion() const
+{
+ mxChData->FinishConversion();
+}
+
+bool XclExpChRoot::IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const
+{
+ XclExpPalette& rPal = GetPalette();
+ return rPal.IsSystemColor( nSysColorIdx ) && (rColor == rPal.GetDefColor( nSysColorIdx ));
+}
+
+void XclExpChRoot::SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const
+{
+ OSL_ENSURE( GetPalette().IsSystemColor( nSysColorIdx ), "XclExpChRoot::SetSystemColor - invalid color index" );
+ rColor = GetPalette().GetDefColor( nSysColorIdx );
+ rnColorId = XclExpPalette::GetColorIdFromIndex( nSysColorIdx );
+}
+
+sal_Int32 XclExpChRoot::CalcChartXFromHmm( sal_Int32 nPosX ) const
+{
+ return ::limit_cast< sal_Int32, double >( (nPosX - mxChData->mnBorderGapX) / mxChData->mfUnitSizeX, 0, EXC_CHART_TOTALUNITS );
+}
+
+sal_Int32 XclExpChRoot::CalcChartYFromHmm( sal_Int32 nPosY ) const
+{
+ return ::limit_cast< sal_Int32, double >( (nPosY - mxChData->mnBorderGapY) / mxChData->mfUnitSizeY, 0, EXC_CHART_TOTALUNITS );
+}
+
+XclChRectangle XclExpChRoot::CalcChartRectFromHmm( const css::awt::Rectangle& rRect ) const
+{
+ XclChRectangle aRect;
+ aRect.mnX = CalcChartXFromHmm( rRect.X );
+ aRect.mnY = CalcChartYFromHmm( rRect.Y );
+ aRect.mnWidth = CalcChartXFromHmm( rRect.Width );
+ aRect.mnHeight = CalcChartYFromHmm( rRect.Height );
+ return aRect;
+}
+
+void XclExpChRoot::ConvertLineFormat( XclChLineFormat& rLineFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().ReadLineProperties(
+ rLineFmt, *mxChData->mxLineDashTable, rPropSet, ePropMode );
+}
+
+bool XclExpChRoot::ConvertAreaFormat( XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ return GetChartPropSetHelper().ReadAreaProperties( rAreaFmt, rPropSet, ePropMode );
+}
+
+void XclExpChRoot::ConvertEscherFormat(
+ XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().ReadEscherProperties( rEscherFmt, rPicFmt,
+ *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, rPropSet, ePropMode );
+}
+
+sal_uInt16 XclExpChRoot::ConvertFont( const ScfPropertySet& rPropSet, sal_Int16 nScript ) const
+{
+ XclFontData aFontData;
+ GetFontPropSetHelper().ReadFontProperties( aFontData, rPropSet, EXC_FONTPROPSET_CHART, nScript );
+ return GetFontBuffer().Insert( aFontData, EXC_COLOR_CHARTTEXT );
+}
+
+sal_uInt16 XclExpChRoot::ConvertPieRotation( const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nApiRot = 0;
+ rPropSet.GetProperty( nApiRot, EXC_CHPROP_STARTINGANGLE );
+ return static_cast< sal_uInt16 >( (450 - (nApiRot % 360)) % 360 );
+}
+
+void XclExpChRoot::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
+{
+ mxChData->RegisterFutureRecBlock( rFrBlock );
+}
+
+void XclExpChRoot::InitializeFutureRecBlock( XclExpStream& rStrm )
+{
+ mxChData->InitializeFutureRecBlock( rStrm );
+}
+
+void XclExpChRoot::FinalizeFutureRecBlock( XclExpStream& rStrm )
+{
+ mxChData->FinalizeFutureRecBlock( rStrm );
+}
+
+XclExpChGroupBase::XclExpChGroupBase( const XclExpChRoot& rRoot,
+ sal_uInt16 nFrType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ XclExpChRoot( rRoot ),
+ maFrBlock( nFrType )
+{
+}
+
+XclExpChGroupBase::~XclExpChGroupBase()
+{
+}
+
+void XclExpChGroupBase::Save( XclExpStream& rStrm )
+{
+ // header record
+ XclExpRecord::Save( rStrm );
+ // group records
+ if( !HasSubRecords() )
+ return;
+
+ // register the future record context corresponding to this record group
+ RegisterFutureRecBlock( maFrBlock );
+ // CHBEGIN record
+ XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm );
+ // embedded records
+ WriteSubRecords( rStrm );
+ // finalize the future records, must be done before the closing CHEND
+ FinalizeFutureRecBlock( rStrm );
+ // CHEND record
+ XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm );
+}
+
+bool XclExpChGroupBase::HasSubRecords() const
+{
+ return true;
+}
+
+void XclExpChGroupBase::SetFutureRecordContext( sal_uInt16 nFrContext, sal_uInt16 nFrValue1, sal_uInt16 nFrValue2 )
+{
+ maFrBlock.mnContext = nFrContext;
+ maFrBlock.mnValue1 = nFrValue1;
+ maFrBlock.mnValue2 = nFrValue2;
+}
+
+XclExpChFutureRecordBase::XclExpChFutureRecordBase( const XclExpChRoot& rRoot,
+ XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpFutureRecord( eRecType, nRecId, nRecSize ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChFutureRecordBase::Save( XclExpStream& rStrm )
+{
+ InitializeFutureRecBlock( rStrm );
+ XclExpFutureRecord::Save( rStrm );
+}
+
+// Frame formatting ===========================================================
+
+XclExpChFramePos::XclExpChFramePos( sal_uInt16 nTLMode ) :
+ XclExpRecord( EXC_ID_CHFRAMEPOS, 20 )
+{
+ maData.mnTLMode = nTLMode;
+ maData.mnBRMode = EXC_CHFRAMEPOS_PARENT;
+}
+
+void XclExpChFramePos::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnTLMode << maData.mnBRMode << maData.maRect;
+}
+
+XclExpChLineFormat::XclExpChLineFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHLINEFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 10 ),
+ mnColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChLineFormat::SetDefault( XclChFrameType eDefFrameType )
+{
+ switch( eDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ SetAuto( true );
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ SetAuto( false );
+ maData.mnPattern = EXC_CHLINEFORMAT_NONE;
+ break;
+ default:
+ OSL_FAIL( "XclExpChLineFormat::SetDefault - unknown frame type" );
+ }
+}
+
+void XclExpChLineFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ rRoot.ConvertLineFormat( maData, rPropSet, rFmtInfo.mePropMode );
+ if( HasLine() )
+ {
+ // detect system color, set color identifier (TODO: detect automatic series line)
+ if( (eObjType != EXC_CHOBJTYPE_LINEARSERIES) && rRoot.IsSystemColor( maData.maColor, rFmtInfo.mnAutoLineColorIdx ) )
+ {
+ // store color index from automatic format data
+ mnColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoLineColorIdx );
+ // try to set automatic mode
+ bool bAuto = (maData.mnPattern == EXC_CHLINEFORMAT_SOLID) && (maData.mnWeight == rFmtInfo.mnAutoLineWeight);
+ ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto );
+ }
+ else
+ {
+ // user defined color - register in palette
+ mnColorId = rRoot.GetPalette().InsertColor( maData.maColor, EXC_COLOR_CHARTLINE );
+ }
+ }
+ else
+ {
+ // no line - set default system color
+ rRoot.SetSystemColor( maData.maColor, mnColorId, EXC_COLOR_CHWINDOWTEXT );
+ }
+}
+
+bool XclExpChLineFormat::IsDefault( XclChFrameType eDefFrameType ) const
+{
+ return
+ ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasLine()) ||
+ ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
+}
+
+void XclExpChLineFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maColor << maData.mnPattern << maData.mnWeight << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ rStrm << rStrm.GetRoot().GetPalette().GetColorIndex( mnColorId );
+}
+
+namespace {
+
+/** Creates a CHLINEFORMAT record from the passed property set. */
+XclExpChLineFormatRef lclCreateLineFormat( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( rRoot );
+ xLineFmt->Convert( rRoot, rPropSet, eObjType );
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( rFmtInfo.mbDeleteDefFrame && xLineFmt->IsDefault( rFmtInfo.meDefFrameType ) )
+ xLineFmt.clear();
+ return xLineFmt;
+}
+
+} // namespace
+
+XclExpChAreaFormat::XclExpChAreaFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHAREAFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 16 : 12 ),
+ mnPattColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
+ mnBackColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+bool XclExpChAreaFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ bool bComplexFill = rRoot.ConvertAreaFormat( maData, rPropSet, rFmtInfo.mePropMode );
+ if( HasArea() )
+ {
+ bool bSolid = maData.mnPattern == EXC_PATT_SOLID;
+ // detect system color, set color identifier (TODO: detect automatic series area)
+ if( (eObjType != EXC_CHOBJTYPE_FILLEDSERIES) && rRoot.IsSystemColor( maData.maPattColor, rFmtInfo.mnAutoPattColorIdx ) )
+ {
+ // store color index from automatic format data
+ mnPattColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoPattColorIdx );
+ // set automatic mode
+ ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bSolid );
+ }
+ else
+ {
+ // user defined color - register color in palette
+ mnPattColorId = rRoot.GetPalette().InsertColor( maData.maPattColor, EXC_COLOR_CHARTAREA );
+ }
+ // background color (default system color for solid fills)
+ if( bSolid )
+ rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
+ else
+ mnBackColorId = rRoot.GetPalette().InsertColor( maData.maBackColor, EXC_COLOR_CHARTAREA );
+ }
+ else
+ {
+ // no area - set default system colors
+ rRoot.SetSystemColor( maData.maPattColor, mnPattColorId, EXC_COLOR_CHWINDOWBACK );
+ rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
+ }
+ return bComplexFill;
+}
+
+void XclExpChAreaFormat::SetDefault( XclChFrameType eDefFrameType )
+{
+ switch( eDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ SetAuto( true );
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ SetAuto( false );
+ maData.mnPattern = EXC_PATT_NONE;
+ break;
+ default:
+ OSL_FAIL( "XclExpChAreaFormat::SetDefault - unknown frame type" );
+ }
+}
+
+bool XclExpChAreaFormat::IsDefault( XclChFrameType eDefFrameType ) const
+{
+ return
+ ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasArea()) ||
+ ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
+}
+
+void XclExpChAreaFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maPattColor << maData.maBackColor << maData.mnPattern << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ rStrm << rPal.GetColorIndex( mnPattColorId ) << rPal.GetColorIndex( mnBackColorId );
+ }
+}
+
+XclExpChEscherFormat::XclExpChEscherFormat( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_UNKNOWN, EXC_ID_CHESCHERFORMAT ),
+ mnColor1Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
+ mnColor2Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+}
+
+void XclExpChEscherFormat::Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = GetFormatInfo( eObjType );
+ ConvertEscherFormat( maData, maPicFmt, rPropSet, rFmtInfo.mePropMode );
+ // register colors in palette
+ mnColor1Id = RegisterColor( ESCHER_Prop_fillColor );
+ mnColor2Id = RegisterColor( ESCHER_Prop_fillBackColor );
+}
+
+bool XclExpChEscherFormat::IsValid() const
+{
+ return static_cast< bool >(maData.mxEscherSet);
+}
+
+void XclExpChEscherFormat::Save( XclExpStream& rStrm )
+{
+ if( maData.mxEscherSet )
+ {
+ // replace RGB colors with palette indexes in the Escher container
+ const XclExpPalette& rPal = GetPalette();
+ maData.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, 0x08000000 | rPal.GetColorIndex( mnColor1Id ) );
+ maData.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x08000000 | rPal.GetColorIndex( mnColor2Id ) );
+
+ // save the record group
+ XclExpChGroupBase::Save( rStrm );
+ }
+}
+
+bool XclExpChEscherFormat::HasSubRecords() const
+{
+ // no subrecords for gradients
+ return maPicFmt.mnBmpMode != EXC_CHPICFORMAT_NONE;
+}
+
+void XclExpChEscherFormat::WriteSubRecords( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( EXC_ID_CHPICFORMAT, 14 );
+ rStrm << maPicFmt.mnBmpMode << sal_uInt16( 0 ) << maPicFmt.mnFlags << maPicFmt.mfScale;
+ rStrm.EndRecord();
+}
+
+sal_uInt32 XclExpChEscherFormat::RegisterColor( sal_uInt16 nPropId )
+{
+ sal_uInt32 nBGRValue;
+ if( maData.mxEscherSet && maData.mxEscherSet->GetOpt( nPropId, nBGRValue ) )
+ {
+ // swap red and blue
+ Color aColor( nBGRValue & 0xff, (nBGRValue >> 8) & 0xff, (nBGRValue >> 16) & 0xff );
+ return GetPalette().InsertColor( aColor, EXC_COLOR_CHARTAREA );
+ }
+ return XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK );
+}
+
+void XclExpChEscherFormat::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( maData.mxEscherSet, "XclExpChEscherFormat::WriteBody - missing property container" );
+ // write Escher property container via temporary memory stream
+ SvMemoryStream aMemStrm;
+ maData.mxEscherSet->Commit( aMemStrm );
+ aMemStrm.FlushBuffer();
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( aMemStrm );
+}
+
+XclExpChFrameBase::XclExpChFrameBase()
+{
+}
+
+XclExpChFrameBase::~XclExpChFrameBase()
+{
+}
+
+void XclExpChFrameBase::ConvertFrameBase( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ // line format
+ mxLineFmt = new XclExpChLineFormat( rRoot );
+ mxLineFmt->Convert( rRoot, rPropSet, eObjType );
+ // area format (only for frame objects)
+ if( !rRoot.GetFormatInfo( eObjType ).mbIsFrame )
+ return;
+
+ mxAreaFmt = new XclExpChAreaFormat( rRoot );
+ bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType );
+ if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill )
+ {
+ mxEscherFmt = new XclExpChEscherFormat( rRoot );
+ mxEscherFmt->Convert( rPropSet, eObjType );
+ if( mxEscherFmt->IsValid() )
+ mxAreaFmt->SetAuto( false );
+ else
+ mxEscherFmt.clear();
+ }
+}
+
+void XclExpChFrameBase::SetDefaultFrameBase( const XclExpChRoot& rRoot,
+ XclChFrameType eDefFrameType, bool bIsFrame )
+{
+ // line format
+ mxLineFmt = new XclExpChLineFormat( rRoot );
+ mxLineFmt->SetDefault( eDefFrameType );
+ // area format (only for frame objects)
+ if( bIsFrame )
+ {
+ mxAreaFmt = new XclExpChAreaFormat( rRoot );
+ mxAreaFmt->SetDefault( eDefFrameType );
+ mxEscherFmt.clear();
+ }
+}
+
+bool XclExpChFrameBase::IsDefaultFrameBase( XclChFrameType eDefFrameType ) const
+{
+ return
+ (!mxLineFmt || mxLineFmt->IsDefault( eDefFrameType )) &&
+ (!mxAreaFmt || mxAreaFmt->IsDefault( eDefFrameType ));
+}
+
+void XclExpChFrameBase::WriteFrameRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxLineFmt );
+ lclSaveRecord( rStrm, mxAreaFmt );
+ lclSaveRecord( rStrm, mxEscherFmt );
+}
+
+XclExpChFrame::XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_FRAME, EXC_ID_CHFRAME, 4 ),
+ meObjType( eObjType )
+{
+}
+
+void XclExpChFrame::Convert( const ScfPropertySet& rPropSet )
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
+}
+
+void XclExpChFrame::SetAutoFlags( bool bAutoPos, bool bAutoSize )
+{
+ ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOPOS, bAutoPos );
+ ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOSIZE, bAutoSize );
+}
+
+bool XclExpChFrame::IsDefault() const
+{
+ return IsDefaultFrameBase( GetFormatInfo( meObjType ).meDefFrameType );
+}
+
+bool XclExpChFrame::IsDeleteable() const
+{
+ return IsDefault() && GetFormatInfo( meObjType ).mbDeleteDefFrame;
+}
+
+void XclExpChFrame::Save( XclExpStream& rStrm )
+{
+ switch( meObjType )
+ {
+ // wall/floor frame without CHFRAME header record
+ case EXC_CHOBJTYPE_WALL3D:
+ case EXC_CHOBJTYPE_FLOOR3D:
+ WriteFrameRecords( rStrm );
+ break;
+ default:
+ XclExpChGroupBase::Save( rStrm );
+ }
+}
+
+void XclExpChFrame::WriteSubRecords( XclExpStream& rStrm )
+{
+ WriteFrameRecords( rStrm );
+}
+
+void XclExpChFrame::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnFormat << maData.mnFlags;
+}
+
+namespace {
+
+/** Creates a CHFRAME record from the passed property set. */
+XclExpChFrameRef lclCreateFrame( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ XclExpChFrameRef xFrame = new XclExpChFrame( rRoot, eObjType );
+ xFrame->Convert( rPropSet );
+ if( xFrame->IsDeleteable() )
+ xFrame.clear();
+ return xFrame;
+}
+
+} // namespace
+
+// Source links ===============================================================
+
+namespace {
+
+void lclAddDoubleRefData(
+ ScTokenArray& orArray, const FormulaToken& rToken,
+ SCTAB nScTab1, SCCOL nScCol1, SCROW nScRow1,
+ SCTAB nScTab2, SCCOL nScCol2, SCROW nScRow2 )
+{
+ ScComplexRefData aComplexRef;
+ aComplexRef.InitRange(ScRange(nScCol1,nScRow1,nScTab1,nScCol2,nScRow2,nScTab2));
+ aComplexRef.Ref1.SetFlag3D( true );
+
+ if( orArray.GetLen() > 0 )
+ orArray.AddOpCode( ocUnion );
+
+ OSL_ENSURE( (rToken.GetType() == ::formula::svDoubleRef) || (rToken.GetType() == ::formula::svExternalDoubleRef),
+ "lclAddDoubleRefData - double reference token expected");
+ if( rToken.GetType() == ::formula::svExternalDoubleRef )
+ orArray.AddExternalDoubleReference(
+ rToken.GetIndex(), rToken.GetString(), aComplexRef);
+ else
+ orArray.AddDoubleReference( aComplexRef );
+}
+
+} // namespace
+
+XclExpChSourceLink::XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType ) :
+ XclExpRecord( EXC_ID_CHSOURCELINK ),
+ XclExpChRoot( rRoot )
+{
+ maData.mnDestType = nDestType;
+ maData.mnLinkType = EXC_CHSRCLINK_DIRECTLY;
+}
+
+sal_uInt16 XclExpChSourceLink::ConvertDataSequence( Reference< XDataSequence > const & xDataSeq, bool bSplitToColumns, sal_uInt16 nDefCount )
+{
+ mxLinkFmla.reset();
+ maData.mnLinkType = EXC_CHSRCLINK_DEFAULT;
+
+ if( !xDataSeq.is() )
+ return nDefCount;
+
+ // Compile the range representation string into token array. Note that the
+ // source range text depends on the current grammar.
+ OUString aRangeRepr = xDataSeq->getSourceRangeRepresentation();
+ ScCompiler aComp( GetDoc(), ScAddress(), GetDoc().GetGrammar() );
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aRangeRepr));
+ if( !pArray )
+ return nDefCount;
+
+ ScTokenArray aArray(GetRoot().GetDoc());
+ sal_uInt32 nValueCount = 0;
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
+ {
+ switch( pToken->GetType() )
+ {
+ case ::formula::svSingleRef:
+ case ::formula::svExternalSingleRef:
+ // for a single ref token, just add it to the new token array as is
+ if( aArray.GetLen() > 0 )
+ aArray.AddOpCode( ocUnion );
+ aArray.AddToken( *pToken );
+ ++nValueCount;
+ break;
+
+ case ::formula::svDoubleRef:
+ case ::formula::svExternalDoubleRef:
+ {
+ // split 3-dimensional ranges into single sheets
+ const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
+ ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
+ ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
+ for (SCTAB nScTab = aAbs1.Tab(); nScTab <= aAbs2.Tab(); ++nScTab)
+ {
+ // split 2-dimensional ranges into single columns
+ if (bSplitToColumns && (aAbs1.Col() < aAbs2.Col()) && (aAbs1.Row() < aAbs2.Row()))
+ for (SCCOL nScCol = aAbs1.Col(); nScCol <= aAbs2.Col(); ++nScCol)
+ lclAddDoubleRefData(aArray, *pToken, nScTab, nScCol, aAbs1.Row(), nScTab, nScCol, aAbs2.Row());
+ else
+ lclAddDoubleRefData(aArray, *pToken, nScTab, aAbs1.Col(), aAbs1.Row(), nScTab, aAbs2.Col(), aAbs2.Row());
+ }
+ sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
+ sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
+ sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
+ nValueCount += nCols * nRows * nTabs;
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ const ScAddress aBaseCell;
+ mxLinkFmla = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aArray, &aBaseCell );
+ maData.mnLinkType = EXC_CHSRCLINK_WORKSHEET;
+ return ulimit_cast< sal_uInt16 >( nValueCount, EXC_CHDATAFORMAT_MAXPOINTCOUNT );
+}
+
+void XclExpChSourceLink::ConvertString( const OUString& aString )
+{
+ mxString = XclExpStringHelper::CreateString( GetRoot(), aString, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+}
+
+sal_uInt16 XclExpChSourceLink::ConvertStringSequence( const Sequence< Reference< XFormattedString > >& rStringSeq )
+{
+ mxString.reset();
+ sal_uInt16 nFontIdx = EXC_FONT_APP;
+ if( rStringSeq.hasElements() )
+ {
+ mxString = XclExpStringHelper::CreateString( GetRoot(), OUString(), XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+ Reference< XBreakIterator > xBreakIt = GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+ // convert all formatted string entries from the sequence
+ for( const Reference< XFormattedString >& rString : rStringSeq )
+ {
+ if( rString.is() )
+ {
+ sal_uInt16 nWstrnFontIdx = EXC_FONT_NOTFOUND;
+ sal_uInt16 nAsianFontIdx = EXC_FONT_NOTFOUND;
+ sal_uInt16 nCmplxFontIdx = EXC_FONT_NOTFOUND;
+ OUString aText = rString->getString();
+ ScfPropertySet aStrProp( rString );
+
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( GetRoot(), aText );
+
+ // process all script portions
+ sal_Int32 nPortionPos = 0;
+ sal_Int32 nTextLen = aText.getLength();
+ while( nPortionPos < nTextLen )
+ {
+ // get script type and end position of next script portion
+ sal_Int16 nScript = xBreakIt->getScriptType( aText, nPortionPos );
+ sal_Int32 nPortionEnd = xBreakIt->endOfScript( aText, nPortionPos, nScript );
+
+ // reuse previous script for following weak portions
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+
+ // Excel start position of this portion
+ sal_uInt16 nXclPortionStart = mxString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *mxString, GetRoot(), aText.subView( nPortionPos, nPortionEnd - nPortionPos ) );
+ if( nXclPortionStart < mxString->Len() )
+ {
+ // find font index variable dependent on script type
+ sal_uInt16& rnFontIdx = (nScript == ApiScriptType::COMPLEX) ? nCmplxFontIdx :
+ ((nScript == ApiScriptType::ASIAN) ? nAsianFontIdx : nWstrnFontIdx);
+
+ // insert font into buffer (if not yet done)
+ if( rnFontIdx == EXC_FONT_NOTFOUND )
+ rnFontIdx = ConvertFont( aStrProp, nScript );
+
+ // insert font index into format run vector
+ mxString->AppendFormat( nXclPortionStart, rnFontIdx );
+ }
+
+ // go to next script portion
+ nLastScript = nScript;
+ nPortionPos = nPortionEnd;
+ }
+ }
+ }
+ if( !mxString->IsEmpty() )
+ {
+ // get leading font index
+ const XclFormatRunVec& rFormats = mxString->GetFormats();
+ OSL_ENSURE( !rFormats.empty() && (rFormats.front().mnChar == 0),
+ "XclExpChSourceLink::ConvertStringSequenc - missing leading format" );
+ // remove leading format run, if entire string is equally formatted
+ if( rFormats.size() == 1 )
+ nFontIdx = mxString->RemoveLeadingFont();
+ else if( !rFormats.empty() )
+ nFontIdx = rFormats.front().mnFontIdx;
+ // add trailing format run, if string is rich-formatted
+ if( mxString->IsRich() )
+ mxString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ }
+ return nFontIdx;
+}
+
+void XclExpChSourceLink::ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent )
+{
+ sal_Int32 nApiNumFmt = 0;
+ if( bPercent ? rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_PERCENTAGENUMFMT ) : rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
+ {
+ ::set_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
+ maData.mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
+ }
+}
+
+void XclExpChSourceLink::AppendString( std::u16string_view rStr )
+{
+ if (!mxString)
+ return;
+ XclExpStringHelper::AppendString( *mxString, GetRoot(), rStr );
+}
+
+void XclExpChSourceLink::Save( XclExpStream& rStrm )
+{
+ // CHFORMATRUNS record
+ if( mxString && mxString->IsRich() )
+ {
+ std::size_t nRecSize = (1 + mxString->GetFormatsCount()) * ((GetBiff() == EXC_BIFF8) ? 2 : 1);
+ rStrm.StartRecord( EXC_ID_CHFORMATRUNS, nRecSize );
+ mxString->WriteFormats( rStrm, true );
+ rStrm.EndRecord();
+ }
+ // CHSOURCELINK record
+ XclExpRecord::Save( rStrm );
+ // CHSTRING record
+ if( mxString && !mxString->IsEmpty() )
+ {
+ rStrm.StartRecord( EXC_ID_CHSTRING, 2 + mxString->GetSize() );
+ rStrm << sal_uInt16( 0 ) << *mxString;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpChSourceLink::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnDestType
+ << maData.mnLinkType
+ << maData.mnFlags
+ << maData.mnNumFmtIdx
+ << mxLinkFmla;
+}
+
+// Text =======================================================================
+
+XclExpChFont::XclExpChFont( sal_uInt16 nFontIdx ) :
+ XclExpUInt16Record( EXC_ID_CHFONT, nFontIdx )
+{
+}
+
+XclExpChObjectLink::XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos ) :
+ XclExpRecord( EXC_ID_CHOBJECTLINK, 6 )
+{
+ maData.mnTarget = nLinkTarget;
+ maData.maPointPos = rPointPos;
+}
+
+void XclExpChObjectLink::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnTarget << maData.maPointPos.mnSeriesIdx << maData.maPointPos.mnPointIdx;
+}
+
+XclExpChFrLabelProps::XclExpChFrLabelProps( const XclExpChRoot& rRoot ) :
+ XclExpChFutureRecordBase( rRoot, EXC_FUTUREREC_UNUSEDREF, EXC_ID_CHFRLABELPROPS, 4 )
+{
+}
+
+void XclExpChFrLabelProps::Convert( const ScfPropertySet& rPropSet,
+ bool bShowCateg, bool bShowValue, bool bShowPercent, bool bShowBubble )
+{
+ // label value flags
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWSERIES, false );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWCATEG, bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWVALUE, bShowValue );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWPERCENT, bShowPercent );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWBUBBLE, bShowBubble );
+
+ // label value separator
+ maData.maSeparator = rPropSet.GetStringProperty( EXC_CHPROP_LABELSEPARATOR );
+ if( maData.maSeparator.isEmpty() )
+ maData.maSeparator = " ";
+}
+
+void XclExpChFrLabelProps::WriteBody( XclExpStream& rStrm )
+{
+ XclExpString aXclSep( maData.maSeparator, XclStrFlags::ForceUnicode | XclStrFlags::SmartFlags );
+ rStrm << maData.mnFlags << aXclSep;
+}
+
+XclExpChFontBase::~XclExpChFontBase()
+{
+}
+
+void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx )
+{
+ if( const XclExpFont* pFont = rRoot.GetFontBuffer().GetFont( nFontIdx ) )
+ {
+ XclExpChFontRef xFont = new XclExpChFont(nFontIdx);
+ SetFont(xFont, pFont->GetFontData().maComplexColor, pFont->GetFontColorId());
+ }
+}
+
+void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet )
+{
+ ConvertFontBase( rRoot, rRoot.ConvertFont( rPropSet, rRoot.GetDefApiScript() ) );
+}
+
+void XclExpChFontBase::ConvertRotationBase(const ScfPropertySet& rPropSet, bool bSupportsStacked )
+{
+ sal_uInt16 nRotation = XclChPropSetHelper::ReadRotationProperties( rPropSet, bSupportsStacked );
+ SetRotation( nRotation );
+}
+
+XclExpChText::XclExpChText( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TEXT, EXC_ID_CHTEXT, (rRoot.GetBiff() == EXC_BIFF8) ? 32 : 26 ),
+ mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChText::SetFont( XclExpChFontRef xFont, model::ComplexColor const& rComplexColor, sal_uInt32 nColorId )
+{
+ mxFont = xFont;
+ maData.maTextComplexColor = rComplexColor;
+ ::set_flag(maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, rComplexColor.getFinalColor() == COL_AUTO);
+ mnTextColorId = nColorId;
+}
+
+void XclExpChText::SetRotation( sal_uInt16 nRotation )
+{
+ maData.mnRotation = nRotation;
+ ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 8, 3 );
+}
+
+void XclExpChText::ConvertTitle( Reference< XTitle > const & xTitle, sal_uInt16 nTarget, const OUString* pSubTitle )
+{
+ switch( nTarget )
+ {
+ case EXC_CHOBJLINK_TITLE: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_TITLE ); break;
+ case EXC_CHOBJLINK_YAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 1 ); break;
+ case EXC_CHOBJLINK_XAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE ); break;
+ case EXC_CHOBJLINK_ZAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 2 ); break;
+ }
+
+ mxSrcLink.clear();
+ mxObjLink = new XclExpChObjectLink( nTarget, XclChDataPointPos( 0, 0 ) );
+
+ if( xTitle.is() )
+ {
+ // title frame formatting
+ ScfPropertySet aTitleProp( xTitle );
+ mxFrame = lclCreateFrame( GetChRoot(), aTitleProp, EXC_CHOBJTYPE_TEXT );
+
+ // string sequence
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ sal_uInt16 nFontIdx = mxSrcLink->ConvertStringSequence( xTitle->getText() );
+ if (pSubTitle)
+ {
+ // append subtitle as the 2nd line of the title.
+ OUString aSubTitle = "\n" + *pSubTitle;
+ mxSrcLink->AppendString(aSubTitle);
+ }
+
+ ConvertFontBase( GetChRoot(), nFontIdx );
+
+ // rotation
+ ConvertRotationBase( aTitleProp, true );
+
+ // manual text position - only for main title
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
+ if( nTarget == EXC_CHOBJLINK_TITLE )
+ {
+ Any aRelPos;
+ if( aTitleProp.GetAnyProperty( aRelPos, EXC_CHPROP_RELATIVEPOSITION ) && aRelPos.has< RelativePosition >() ) try
+ {
+ // calculate absolute position for CHTEXT record
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< XShape > xTitleShape( xChart1Doc->getTitle(), UNO_SET_THROW );
+ css::awt::Point aPos = xTitleShape->getPosition();
+ css::awt::Size aSize = xTitleShape->getSize();
+ css::awt::Rectangle aRect( aPos.X, aPos.Y, aSize.Width, aSize.Height );
+ maData.maRect = CalcChartRectFromHmm( aRect );
+ ::insert_value( maData.mnFlags2, EXC_CHTEXT_POS_MOVED, 0, 4 );
+ // manual title position implies manual plot area
+ GetChartData().SetManualPlotArea();
+ // calculate the default title position in chart units
+ sal_Int32 nDefPosX = ::std::max< sal_Int32 >( (EXC_CHART_TOTALUNITS - maData.maRect.mnWidth) / 2, 0 );
+ sal_Int32 nDefPosY = 85;
+ // set the position relative to the standard position
+ XclChRectangle& rFrameRect = mxFramePos->GetFramePosData().maRect;
+ rFrameRect.mnX = maData.maRect.mnX - nDefPosX;
+ rFrameRect.mnY = maData.maRect.mnY - nDefPosY;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ }
+ else
+ {
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED );
+ }
+}
+
+void XclExpChText::ConvertLegend( const ScfPropertySet& rPropSet )
+{
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOGEN );
+ ConvertFontBase( GetChRoot(), rPropSet );
+}
+
+bool XclExpChText::ConvertDataLabel( const ScfPropertySet& rPropSet,
+ const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos )
+{
+ SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_DATALABEL, rPointPos.mnPointIdx, rPointPos.mnSeriesIdx );
+
+ cssc2::DataPointLabel aPointLabel;
+ if( !rPropSet.GetProperty( aPointLabel, EXC_CHPROP_LABEL ) )
+ return false;
+
+ // percentage only allowed in pie and donut charts
+ bool bIsPie = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE;
+ // bubble sizes only allowed in bubble charts
+ bool bIsBubble = rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES;
+ OSL_ENSURE( (GetBiff() == EXC_BIFF8) || !bIsBubble, "XclExpChText::ConvertDataLabel - bubble charts only in BIFF8" );
+
+ // raw show flags
+ bool bShowValue = !bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size
+ bool bShowPercent = bIsPie && aPointLabel.ShowNumberInPercent; // percentage only in pie/donut charts
+ bool bShowCateg = aPointLabel.ShowCategoryName;
+ bool bShowBubble = bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size
+ bool bShowAny = bShowValue || bShowPercent || bShowCateg || bShowBubble;
+
+ // create the CHFRLABELPROPS record for extended settings in BIFF8
+ if( bShowAny && (GetBiff() == EXC_BIFF8) )
+ {
+ mxLabelProps = new XclExpChFrLabelProps( GetChRoot() );
+ mxLabelProps->Convert( rPropSet, bShowCateg, bShowValue, bShowPercent, bShowBubble );
+ }
+
+ // restrict to combinations allowed in CHTEXT
+ if( bShowPercent ) bShowValue = false; // percent wins over value
+ if( bShowValue ) bShowCateg = false; // value wins over category
+ if( bShowValue || bShowCateg ) bShowBubble = false; // value or category wins over bubble size
+
+ // set all flags
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bShowValue );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bShowPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bShowPercent && bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWBUBBLE, bShowBubble );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL, bShowAny && aPointLabel.ShowLegendSymbol );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bShowAny );
+
+ if( bShowAny )
+ {
+ // font settings
+ ConvertFontBase( GetChRoot(), rPropSet );
+ ConvertRotationBase( rPropSet, false );
+ // label placement
+ sal_Int32 nPlacement = 0;
+ sal_uInt16 nLabelPos = EXC_CHTEXT_POS_AUTO;
+ if( rPropSet.GetProperty( nPlacement, EXC_CHPROP_LABELPLACEMENT ) )
+ {
+ using namespace cssc::DataLabelPlacement;
+ if( nPlacement == rTypeInfo.mnDefaultLabelPos )
+ {
+ nLabelPos = EXC_CHTEXT_POS_DEFAULT;
+ }
+ else switch( nPlacement )
+ {
+ case AVOID_OVERLAP: nLabelPos = EXC_CHTEXT_POS_AUTO; break;
+ case CENTER: nLabelPos = EXC_CHTEXT_POS_CENTER; break;
+ case TOP: nLabelPos = EXC_CHTEXT_POS_ABOVE; break;
+ case TOP_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case BOTTOM_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case BOTTOM: nLabelPos = EXC_CHTEXT_POS_BELOW; break;
+ case BOTTOM_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case TOP_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case INSIDE: nLabelPos = EXC_CHTEXT_POS_INSIDE; break;
+ case OUTSIDE: nLabelPos = EXC_CHTEXT_POS_OUTSIDE; break;
+ case NEAR_ORIGIN: nLabelPos = EXC_CHTEXT_POS_AXIS; break;
+ default: OSL_FAIL( "XclExpChText::ConvertDataLabel - unknown label placement type" );
+ }
+ }
+ ::insert_value( maData.mnFlags2, nLabelPos, 0, 4 );
+ // source link (contains number format)
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ if( bShowValue || bShowPercent )
+ // percentage format wins over value format
+ mxSrcLink->ConvertNumFmt( rPropSet, bShowPercent );
+ // object link
+ mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
+ }
+
+ /* Return true to indicate valid label settings:
+ - for existing labels at entire series
+ - for any settings at single data point (to be able to delete a point label) */
+ return bShowAny || (rPointPos.mnPointIdx != EXC_CHDATAFORMAT_ALLPOINTS);
+}
+
+void XclExpChText::ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos )
+{
+ // required flags
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ); // must set this to make equation visible in Excel
+ // frame formatting
+ mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_TEXT );
+ // font settings
+ maData.mnHAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
+ maData.mnVAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
+ ConvertFontBase( GetChRoot(), rPropSet );
+ // source link (contains number format)
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ mxSrcLink->ConvertNumFmt( rPropSet, false );
+ // object link
+ mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
+}
+
+sal_uInt16 XclExpChText::GetAttLabelFlags() const
+{
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWVALUE, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWPERCENT, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEGPERC, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEG, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ) );
+ return nFlags;
+}
+
+void XclExpChText::WriteSubRecords( XclExpStream& rStrm )
+{
+ // CHFRAMEPOS record
+ lclSaveRecord( rStrm, mxFramePos );
+ // CHFONT record
+ lclSaveRecord( rStrm, mxFont );
+ // CHSOURCELINK group
+ lclSaveRecord( rStrm, mxSrcLink );
+ // CHFRAME group
+ lclSaveRecord( rStrm, mxFrame );
+ // CHOBJECTLINK record
+ lclSaveRecord( rStrm, mxObjLink );
+ // CHFRLABELPROPS record
+ lclSaveRecord( rStrm, mxLabelProps );
+}
+
+void XclExpChText::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnHAlign
+ << maData.mnVAlign
+ << maData.mnBackMode
+ << maData.maTextComplexColor.getFinalColor()
+ << maData.maRect
+ << maData.mnFlags;
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ rStrm << GetPalette().GetColorIndex( mnTextColorId )
+ << maData.mnFlags2
+ << maData.mnRotation;
+ }
+}
+
+namespace {
+
+/** Creates and returns an Excel text object from the passed title. */
+XclExpChTextRef lclCreateTitle( const XclExpChRoot& rRoot, Reference< XTitled > const & xTitled, sal_uInt16 nTarget,
+ const OUString* pSubTitle = nullptr )
+{
+ Reference< XTitle > xTitle;
+ if( xTitled.is() )
+ xTitle = xTitled->getTitleObject();
+
+ XclExpChTextRef xText = new XclExpChText( rRoot );
+ xText->ConvertTitle( xTitle, nTarget, pSubTitle );
+ /* Do not delete the CHTEXT group for the main title. A missing CHTEXT
+ will be interpreted as auto-generated title showing the series title in
+ charts that contain exactly one data series. */
+ if( (nTarget != EXC_CHOBJLINK_TITLE) && !xText->HasString() )
+ xText.clear();
+
+ return xText;
+}
+
+}
+
+// Data series ================================================================
+
+XclExpChMarkerFormat::XclExpChMarkerFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHMARKERFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 20 : 12 ),
+ mnLineColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ),
+ mnFillColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
+{
+}
+
+void XclExpChMarkerFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
+{
+ XclChPropSetHelper::ReadMarkerProperties( maData, rPropSet, nFormatIdx );
+ /* Set marker line/fill color to series line color.
+ TODO: remove this if OOChart supports own colors in markers. */
+ Color aLineColor;
+ if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
+ maData.maLineColor = maData.maFillColor = aLineColor;
+ // register colors in palette
+ RegisterColors( rRoot );
+}
+
+void XclExpChMarkerFormat::ConvertStockSymbol( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, bool bCloseSymbol )
+{
+ // clear the automatic flag
+ ::set_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
+ // symbol type and color
+ if( bCloseSymbol )
+ {
+ // set symbol type for the 'close' data series
+ maData.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ;
+ maData.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE;
+ // set symbol line/fill color to series line color
+ Color aLineColor;
+ if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
+ {
+ maData.maLineColor = maData.maFillColor = aLineColor;
+ RegisterColors( rRoot );
+ }
+ }
+ else
+ {
+ // set invisible symbol
+ maData.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
+ }
+}
+
+void XclExpChMarkerFormat::RegisterColors( const XclExpChRoot& rRoot )
+{
+ if( HasMarker() )
+ {
+ if( HasLineColor() )
+ mnLineColorId = rRoot.GetPalette().InsertColor( maData.maLineColor, EXC_COLOR_CHARTLINE );
+ if( HasFillColor() )
+ mnFillColorId = rRoot.GetPalette().InsertColor( maData.maFillColor, EXC_COLOR_CHARTAREA );
+ }
+}
+
+void XclExpChMarkerFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maLineColor << maData.maFillColor << maData.mnMarkerType << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ rStrm << rPal.GetColorIndex( mnLineColorId ) << rPal.GetColorIndex( mnFillColorId ) << maData.mnMarkerSize;
+ }
+}
+
+XclExpChPieFormat::XclExpChPieFormat() :
+ XclExpUInt16Record( EXC_ID_CHPIEFORMAT, 0 )
+{
+}
+
+void XclExpChPieFormat::Convert( const ScfPropertySet& rPropSet )
+{
+ double fApiDist(0.0);
+ if( rPropSet.GetProperty( fApiDist, EXC_CHPROP_OFFSET ) )
+ SetValue( limit_cast< sal_uInt16 >( fApiDist * 100.0, 0, 100 ) );
+}
+
+XclExpCh3dDataFormat::XclExpCh3dDataFormat() :
+ XclExpRecord( EXC_ID_CH3DDATAFORMAT, 2 )
+{
+}
+
+void XclExpCh3dDataFormat::Convert( const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nApiType(0);
+ if( !rPropSet.GetProperty( nApiType, EXC_CHPROP_GEOMETRY3D ) )
+ return;
+
+ using namespace cssc2::DataPointGeometry3D;
+ switch( nApiType )
+ {
+ case CUBOID:
+ maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
+ maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
+ break;
+ case PYRAMID:
+ maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
+ maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
+ break;
+ case CYLINDER:
+ maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
+ maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
+ break;
+ case CONE:
+ maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
+ maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
+ break;
+ default:
+ OSL_FAIL( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" );
+ }
+}
+
+void XclExpCh3dDataFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnBase << maData.mnTop;
+}
+
+XclExpChAttachedLabel::XclExpChAttachedLabel( sal_uInt16 nFlags ) :
+ XclExpUInt16Record( EXC_ID_CHATTACHEDLABEL, nFlags )
+{
+}
+
+XclExpChDataFormat::XclExpChDataFormat( const XclExpChRoot& rRoot,
+ const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DATAFORMAT, EXC_ID_CHDATAFORMAT, 8 )
+{
+ maData.maPointPos = rPointPos;
+ maData.mnFormatIdx = nFormatIdx;
+}
+
+void XclExpChDataFormat::ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo )
+{
+ // line and area formatting
+ ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType() );
+
+ // data point symbols
+ bool bIsFrame = rTypeInfo.IsSeriesFrameFormat();
+ if( !bIsFrame )
+ {
+ mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
+ mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx );
+ }
+
+ // pie segments
+ if( rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE )
+ {
+ mxPieFmt = new XclExpChPieFormat();
+ mxPieFmt->Convert( rPropSet );
+ }
+
+ // 3D bars (only allowed for entire series in BIFF8)
+ if( IsSeriesFormat() && (GetBiff() == EXC_BIFF8) && rTypeInfo.mb3dChart && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) )
+ {
+ mx3dDataFmt = new XclExpCh3dDataFormat();
+ mx3dDataFmt->Convert( rPropSet );
+ }
+
+ // spline
+ if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame )
+ mxSeriesFmt = new XclExpUInt16Record( EXC_ID_CHSERIESFORMAT, EXC_CHSERIESFORMAT_SMOOTHED );
+
+ // data point labels
+ XclExpChTextRef xLabel = new XclExpChText( GetChRoot() );
+ if( xLabel->ConvertDataLabel( rPropSet, rTypeInfo, maData.maPointPos ) )
+ {
+ // CHTEXT groups for data labels are stored in global CHCHART group
+ GetChartData().SetDataLabel( xLabel );
+ mxAttLabel = new XclExpChAttachedLabel( xLabel->GetAttLabelFlags() );
+ }
+}
+
+void XclExpChDataFormat::ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol )
+{
+ // set line format to invisible
+ SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, false );
+ // set symbols to invisible or to 'close' series symbol
+ mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
+ mxMarkerFmt->ConvertStockSymbol( GetChRoot(), rPropSet, bCloseSymbol );
+}
+
+void XclExpChDataFormat::ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, eObjType );
+}
+
+void XclExpChDataFormat::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mx3dDataFmt );
+ WriteFrameRecords( rStrm );
+ lclSaveRecord( rStrm, mxPieFmt );
+ lclSaveRecord( rStrm, mxMarkerFmt );
+ lclSaveRecord( rStrm, mxSeriesFmt );
+ lclSaveRecord( rStrm, mxAttLabel );
+}
+
+void XclExpChDataFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maPointPos.mnPointIdx
+ << maData.maPointPos.mnSeriesIdx
+ << maData.mnFormatIdx
+ << maData.mnFlags;
+}
+
+XclExpChSerTrendLine::XclExpChSerTrendLine( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHSERTRENDLINE, 28 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+bool XclExpChSerTrendLine::Convert( Reference< XRegressionCurve > const & xRegCurve, sal_uInt16 nSeriesIdx )
+{
+ if( !xRegCurve.is() )
+ return false;
+
+ // trend line type
+ ScfPropertySet aCurveProp( xRegCurve );
+
+ OUString aService = aCurveProp.GetServiceName();
+ if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
+ maData.mnOrder = 1;
+ }
+ else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_EXPONENTIAL;
+ }
+ else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_LOGARITHMIC;
+ }
+ else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POWER;
+ }
+ else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
+ sal_Int32 aDegree;
+ aCurveProp.GetProperty(aDegree, EXC_CHPROP_POLYNOMIAL_DEGREE);
+ maData.mnOrder = static_cast<sal_uInt8> (aDegree);
+ }
+ else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_MOVING_AVG;
+ sal_Int32 aPeriod;
+ aCurveProp.GetProperty(aPeriod, EXC_CHPROP_MOVING_AVERAGE_PERIOD);
+ maData.mnOrder = static_cast<sal_uInt8> (aPeriod);
+ }
+ else
+ {
+ return false;
+ }
+
+ aCurveProp.GetProperty(maData.mfForecastFor, EXC_CHPROP_EXTRAPOLATE_FORWARD);
+ aCurveProp.GetProperty(maData.mfForecastBack, EXC_CHPROP_EXTRAPOLATE_BACKWARD);
+ bool bIsForceIntercept = false;
+ aCurveProp.GetProperty(bIsForceIntercept, EXC_CHPROP_FORCE_INTERCEPT);
+ if (bIsForceIntercept)
+ aCurveProp.GetProperty(maData.mfIntercept, EXC_CHPROP_INTERCEPT_VALUE);
+
+ // line formatting
+ XclChDataPointPos aPointPos( nSeriesIdx );
+ mxDataFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, 0 );
+ mxDataFmt->ConvertLine( aCurveProp, EXC_CHOBJTYPE_TRENDLINE );
+
+ // #i83100# show equation and correlation coefficient
+ ScfPropertySet aEquationProp( xRegCurve->getEquationProperties() );
+ maData.mnShowEquation = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWEQUATION ) ? 1 : 0;
+ maData.mnShowRSquared = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWCORRELATION ) ? 1 : 0;
+
+ // #i83100# formatting of the equation text box
+ if( (maData.mnShowEquation != 0) || (maData.mnShowRSquared != 0) )
+ {
+ mxLabel = new XclExpChText( GetChRoot() );
+ mxLabel->ConvertTrendLineEquation( aEquationProp, aPointPos );
+ }
+
+ // missing features
+ // #i5085# manual trend line size
+ // #i34093# manual crossing point
+ return true;
+}
+
+void XclExpChSerTrendLine::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnLineType
+ << maData.mnOrder
+ << maData.mfIntercept
+ << maData.mnShowEquation
+ << maData.mnShowRSquared
+ << maData.mfForecastFor
+ << maData.mfForecastBack;
+}
+
+XclExpChSerErrorBar::XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType ) :
+ XclExpRecord( EXC_ID_CHSERERRORBAR, 14 ),
+ XclExpChRoot( rRoot )
+{
+ maData.mnBarType = nBarType;
+}
+
+bool XclExpChSerErrorBar::Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nBarStyle = 0;
+ bool bOk = rPropSet.GetProperty( nBarStyle, EXC_CHPROP_ERRORBARSTYLE );
+ if( bOk )
+ {
+ switch( nBarStyle )
+ {
+ case cssc::ErrorBarStyle::ABSOLUTE:
+ maData.mnSourceType = EXC_CHSERERR_FIXED;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
+ break;
+ case cssc::ErrorBarStyle::RELATIVE:
+ maData.mnSourceType = EXC_CHSERERR_PERCENT;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
+ break;
+ case cssc::ErrorBarStyle::STANDARD_DEVIATION:
+ maData.mnSourceType = EXC_CHSERERR_STDDEV;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_WEIGHT );
+ break;
+ case cssc::ErrorBarStyle::STANDARD_ERROR:
+ maData.mnSourceType = EXC_CHSERERR_STDERR;
+ break;
+ case cssc::ErrorBarStyle::FROM_DATA:
+ {
+ bOk = false;
+ maData.mnSourceType = EXC_CHSERERR_CUSTOM;
+ Reference< XDataSource > xDataSource( rPropSet.GetApiPropertySet(), UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ // find first sequence with current role
+ OUString aRole = XclChartHelper::GetErrorBarValuesRole( maData.mnBarType );
+ Reference< XDataSequence > xValueSeq;
+
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aCurrRole;
+ if( aValueProp.GetProperty( aCurrRole, EXC_CHPROP_ROLE ) && (aCurrRole == aRole) )
+ {
+ xValueSeq = xTmpValueSeq;
+ break;
+ }
+ }
+ if( xValueSeq.is() )
+ {
+ // #i86465# pass value count back to series
+ rnValueCount = maData.mnValueCount = rValueLink.ConvertDataSequence( xValueSeq, true );
+ bOk = maData.mnValueCount > 0;
+ }
+ }
+ }
+ break;
+ default:
+ bOk = false;
+ }
+ }
+ return bOk;
+}
+
+void XclExpChSerErrorBar::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnBarType
+ << maData.mnSourceType
+ << maData.mnLineEnd
+ << sal_uInt8( 1 ) // must be 1 to make line visible
+ << maData.mfValue
+ << maData.mnValueCount;
+}
+
+namespace {
+
+/** Returns the property set of the specified data point. */
+ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_Int32 nPointIdx )
+{
+ ScfPropertySet aPropSet;
+ try
+ {
+ aPropSet.Set( xDataSeries->getDataPointByIndex( nPointIdx ) );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "lclGetPointPropSet - no data point property set" );
+ }
+ return aPropSet;
+}
+
+} // namespace
+
+XclExpChSeries::XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_SERIES, EXC_ID_CHSERIES, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 8 ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE ),
+ mnSeriesIdx( nSeriesIdx ),
+ mnParentIdx( EXC_CHSERIES_INVALID )
+{
+ // CHSOURCELINK records are always required, even if unused
+ mxTitleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ mxValueLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES );
+ mxCategLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY );
+ if( GetBiff() == EXC_BIFF8 )
+ mxBubbleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_BUBBLES );
+}
+
+bool XclExpChSeries::ConvertDataSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries,
+ const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx )
+{
+ bool bOk = false;
+ Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ Reference< XDataSequence > xYValueSeq, xTitleSeq, xXValueSeq, xBubbleSeq;
+
+ // find first sequence with role 'values-y'
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aRole;
+ if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) )
+ {
+ if( !xYValueSeq.is() && (aRole == EXC_CHPROP_ROLE_YVALUES) )
+ {
+ xYValueSeq = xTmpValueSeq;
+ if( !xTitleSeq.is() )
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ }
+ else if( !xXValueSeq.is() && !rTypeInfo.mbCategoryAxis && (aRole == EXC_CHPROP_ROLE_XVALUES) )
+ {
+ xXValueSeq = xTmpValueSeq;
+ }
+ else if( !xBubbleSeq.is() && (rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES) && (aRole == EXC_CHPROP_ROLE_SIZEVALUES) )
+ {
+ xBubbleSeq = xTmpValueSeq;
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ }
+ }
+ }
+
+ bOk = xYValueSeq.is();
+ if( bOk )
+ {
+ // chart type group index
+ mnGroupIdx = nGroupIdx;
+
+ // convert source links
+ maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
+ mxTitleLink->ConvertDataSequence( xTitleSeq, true );
+
+ // X values of XY charts
+ maData.mnCategCount = mxCategLink->ConvertDataSequence( xXValueSeq, false, maData.mnValueCount );
+
+ // size values of bubble charts
+ if( mxBubbleLink )
+ mxBubbleLink->ConvertDataSequence( xBubbleSeq, false, maData.mnValueCount );
+
+ // series formatting
+ XclChDataPointPos aPointPos( mnSeriesIdx );
+ ScfPropertySet aSeriesProp( xDataSeries );
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
+ mxSeriesFmt->ConvertDataSeries( aSeriesProp, rTypeInfo );
+
+ // trend lines
+ CreateTrendLines( xDataSeries );
+
+ // error bars
+ CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARX, EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
+ CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARY, EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
+
+ if( maData.mnValueCount > 0 )
+ {
+ const sal_Int32 nMaxPointCount = maData.mnValueCount;
+
+ /* #i91063# Create missing fill properties in pie/doughnut charts.
+ If freshly created (never saved to ODF), these charts show
+ varying point colors but do not return these points via API. */
+ if( xDiagram.is() && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE) )
+ {
+ Reference< XColorScheme > xColorScheme = xDiagram->getDefaultColorScheme();
+ if( xColorScheme.is() )
+ {
+ static constexpr OUStringLiteral aFillStyleName = u"FillStyle";
+ static constexpr OUString aColorName = u"Color"_ustr;
+ namespace cssd = ::com::sun::star::drawing;
+ for( sal_Int32 nPointIdx = 0; nPointIdx < nMaxPointCount; ++nPointIdx )
+ {
+ aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIdx );
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
+ // test that the point fill style is solid, but no color is set
+ cssd::FillStyle eFillStyle = cssd::FillStyle_NONE;
+ if( aPointProp.GetProperty( eFillStyle, aFillStyleName ) &&
+ (eFillStyle == cssd::FillStyle_SOLID) &&
+ !aPointProp.HasProperty( aColorName ) )
+ {
+ aPointProp.SetProperty( aColorName, xColorScheme->getColorByIndex( nPointIdx ) );
+ }
+ }
+ }
+ }
+
+ // data point formatting
+ Sequence< sal_Int32 > aPointIndexes;
+ if( aSeriesProp.GetProperty( aPointIndexes, EXC_CHPROP_ATTRIBDATAPOINTS ) && aPointIndexes.hasElements() )
+ {
+ for( const sal_Int32 nPointIndex : std::as_const(aPointIndexes) )
+ {
+ if (nPointIndex >= nMaxPointCount)
+ break;
+ aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIndex );
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIndex );
+ XclExpChDataFormatRef xPointFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
+ xPointFmt->ConvertDataSeries( aPointProp, rTypeInfo );
+ maPointFmts.AppendRecord( xPointFmt );
+ }
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol )
+{
+ bool bOk = false;
+ Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ Reference< XDataSequence > xYValueSeq, xTitleSeq;
+
+ // find first sequence with passed role
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aRole;
+ if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) && (aRole == rValueRole) )
+ {
+ xYValueSeq = xTmpValueSeq;
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ break;
+ }
+ }
+
+ bOk = xYValueSeq.is();
+ if( bOk )
+ {
+ // chart type group index
+ mnGroupIdx = nGroupIdx;
+ // convert source links
+ maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
+ mxTitleLink->ConvertDataSequence( xTitleSeq, true );
+ // series formatting
+ ScfPropertySet aSeriesProp( xDataSeries );
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), nFormatIdx );
+ mxSeriesFmt->ConvertStockSeries( aSeriesProp, bCloseSymbol );
+ }
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertTrendLine( const XclExpChSeries& rParent, Reference< XRegressionCurve > const & xRegCurve )
+{
+ InitFromParent( rParent );
+
+ mxTrendLine = new XclExpChSerTrendLine( GetChRoot() );
+ bool bOk = mxTrendLine->Convert( xRegCurve, mnSeriesIdx );
+ if( bOk )
+ {
+ OUString aName;
+ ScfPropertySet aProperties( xRegCurve );
+ aProperties.GetProperty(aName, EXC_CHPROP_CURVENAME);
+ mxTitleLink->ConvertString(aName);
+
+ mxSeriesFmt = mxTrendLine->GetDataFormat();
+ GetChartData().SetDataLabel( mxTrendLine->GetDataLabel() );
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId )
+{
+ InitFromParent( rParent );
+ // error bar settings
+ mxErrorBar = new XclExpChSerErrorBar( GetChRoot(), nBarId );
+ bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet );
+ if( bOk )
+ {
+ // error bar formatting
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), 0 );
+ mxSeriesFmt->ConvertLine( rPropSet, EXC_CHOBJTYPE_ERRORBAR );
+ }
+ return bOk;
+}
+
+void XclExpChSeries::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
+{
+ if( xCategSeq.is() )
+ maData.mnCategCount = mxCategLink->ConvertDataSequence( xCategSeq->getValues(), false );
+}
+
+void XclExpChSeries::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxTitleLink );
+ lclSaveRecord( rStrm, mxValueLink );
+ lclSaveRecord( rStrm, mxCategLink );
+ lclSaveRecord( rStrm, mxBubbleLink );
+ lclSaveRecord( rStrm, mxSeriesFmt );
+ maPointFmts.Save( rStrm );
+ if( mnGroupIdx != EXC_CHSERGROUP_NONE )
+ XclExpUInt16Record( EXC_ID_CHSERGROUP, mnGroupIdx ).Save( rStrm );
+ if( mnParentIdx != EXC_CHSERIES_INVALID )
+ XclExpUInt16Record( EXC_ID_CHSERPARENT, mnParentIdx ).Save( rStrm );
+ lclSaveRecord( rStrm, mxTrendLine );
+ lclSaveRecord( rStrm, mxErrorBar );
+}
+
+void XclExpChSeries::InitFromParent( const XclExpChSeries& rParent )
+{
+ // index to parent series is stored 1-based
+ mnParentIdx = rParent.mnSeriesIdx + 1;
+ /* #i86465# MSO2007 SP1 expects correct point counts in child series
+ (there was no problem in Excel2003 or Excel2007 without SP1...) */
+ maData.mnCategCount = rParent.maData.mnCategCount;
+ maData.mnValueCount = rParent.maData.mnValueCount;
+}
+
+void XclExpChSeries::CreateTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries )
+{
+ Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
+ if( xRegCurveCont.is() )
+ {
+ const Sequence< Reference< XRegressionCurve > > aRegCurveSeq = xRegCurveCont->getRegressionCurves();
+ for( const Reference< XRegressionCurve >& rRegCurve : aRegCurveSeq )
+ {
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries && !xSeries->ConvertTrendLine( *this, rRegCurve ) )
+ GetChartData().RemoveLastSeries();
+ }
+ }
+}
+
+void XclExpChSeries::CreateErrorBars( const ScfPropertySet& rPropSet,
+ const OUString& rBarPropName, sal_uInt8 nPosBarId, sal_uInt8 nNegBarId )
+{
+ Reference< XPropertySet > xErrorBar;
+ if( rPropSet.GetProperty( xErrorBar, rBarPropName ) && xErrorBar.is() )
+ {
+ ScfPropertySet aErrorProp( xErrorBar );
+ CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWPOSITIVEERROR, nPosBarId );
+ CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWNEGATIVEERROR, nNegBarId );
+ }
+}
+
+void XclExpChSeries::CreateErrorBar( const ScfPropertySet& rPropSet,
+ const OUString& rShowPropName, sal_uInt8 nBarId )
+{
+ if( rPropSet.GetBoolProperty( rShowPropName ) )
+ {
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries && !xSeries->ConvertErrorBar( *this, rPropSet, nBarId ) )
+ GetChartData().RemoveLastSeries();
+ }
+}
+
+void XclExpChSeries::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnCategType << maData.mnValueType << maData.mnCategCount << maData.mnValueCount;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnBubbleType << maData.mnBubbleCount;
+}
+
+// Chart type groups ==========================================================
+
+XclExpChType::XclExpChType( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHUNKNOWN ),
+ XclExpChRoot( rRoot ),
+ maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
+{
+}
+
+void XclExpChType::Convert( Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels )
+{
+ if( !xChartType.is() )
+ return;
+
+ maTypeInfo = GetChartTypeInfo( xChartType->getChartType() );
+ // special handling for some chart types
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_BAR:
+ {
+ maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR );
+ ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet );
+ ScfPropertySet aTypeProp( xChartType );
+ Sequence< sal_Int32 > aInt32Seq;
+ maData.mnOverlap = 0;
+ if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
+ maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 );
+ maData.mnGap = 150;
+ if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
+ maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 );
+ }
+ break;
+ case EXC_CHTYPECATEG_RADAR:
+ ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels );
+ break;
+ case EXC_CHTYPECATEG_PIE:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS );
+ maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
+ maData.mnPieHole = bDonut ? 50 : 0;
+ // #i85166# starting angle of first pie slice
+ ScfPropertySet aDiaProp( xDiagram );
+ maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp );
+ }
+ break;
+ case EXC_CHTYPECATEG_SCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES );
+ break;
+ default:;
+ }
+ SetRecId( maTypeInfo.mnRecId );
+}
+
+void XclExpChType::SetStacked( bool bPercent )
+{
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ ::set_flag( maData.mnFlags, EXC_CHLINE_STACKED );
+ ::set_flag( maData.mnFlags, EXC_CHLINE_PERCENT, bPercent );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ ::set_flag( maData.mnFlags, EXC_CHBAR_STACKED );
+ ::set_flag( maData.mnFlags, EXC_CHBAR_PERCENT, bPercent );
+ maData.mnOverlap = -100;
+ break;
+ default:;
+ }
+}
+
+void XclExpChType::WriteBody( XclExpStream& rStrm )
+{
+ switch( GetRecId() )
+ {
+ case EXC_ID_CHBAR:
+ rStrm << maData.mnOverlap << maData.mnGap << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHLINE:
+ case EXC_ID_CHAREA:
+ case EXC_ID_CHRADARLINE:
+ case EXC_ID_CHRADARAREA:
+ rStrm << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHPIE:
+ rStrm << maData.mnRotation << maData.mnPieHole;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHSCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnBubbleSize << maData.mnBubbleType << maData.mnFlags;
+ break;
+
+ default:
+ OSL_FAIL( "XclExpChType::WriteBody - unknown chart type" );
+ }
+}
+
+XclExpChChart3d::XclExpChChart3d() :
+ XclExpRecord( EXC_ID_CHCHART3D, 14 )
+{
+}
+
+void XclExpChChart3d::Convert( const ScfPropertySet& rPropSet, bool b3dWallChart )
+{
+ sal_Int32 nRotationY = 0;
+ rPropSet.GetProperty( nRotationY, EXC_CHPROP_ROTATIONVERTICAL );
+ sal_Int32 nRotationX = 0;
+ rPropSet.GetProperty( nRotationX, EXC_CHPROP_ROTATIONHORIZONTAL );
+ sal_Int32 nPerspective = 15;
+ rPropSet.GetProperty( nPerspective, EXC_CHPROP_PERSPECTIVE );
+
+ if( b3dWallChart )
+ {
+ // Y rotation (Excel [0..359], Chart2 [-179,180])
+ if( nRotationY < 0 ) nRotationY += 360;
+ maData.mnRotation = static_cast< sal_uInt16 >( nRotationY );
+ // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
+ maData.mnElevation = limit_cast< sal_Int16 >( nRotationX, -90, 90 );
+ // perspective (Excel and Chart2 [0,100])
+ maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
+ // flags
+ maData.mnFlags = 0;
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D, !rPropSet.GetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES ) );
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_AUTOHEIGHT );
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS );
+ }
+ else
+ {
+ // Y rotation not used in pie charts, but 'first pie slice angle'
+ maData.mnRotation = XclExpChRoot::ConvertPieRotation( rPropSet );
+ // X rotation a.k.a. elevation (map Chart2 [-80,-10] to Excel [10..80])
+ maData.mnElevation = limit_cast< sal_Int16 >( (nRotationX + 270) % 180, 10, 80 );
+ // perspective (Excel and Chart2 [0,100])
+ maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
+ // flags
+ maData.mnFlags = 0;
+ }
+}
+
+void XclExpChChart3d::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnRotation
+ << maData.mnElevation
+ << maData.mnEyeDist
+ << maData.mnRelHeight
+ << maData.mnRelDepth
+ << maData.mnDepthGap
+ << maData.mnFlags;
+}
+
+XclExpChLegend::XclExpChLegend( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_LEGEND, EXC_ID_CHLEGEND, 20 )
+{
+}
+
+void XclExpChLegend::Convert( const ScfPropertySet& rPropSet )
+{
+ // frame properties
+ mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_LEGEND );
+ // text properties
+ mxText = new XclExpChText( GetChRoot() );
+ mxText->ConvertLegend( rPropSet );
+
+ // legend position and size
+ Any aRelPosAny, aRelSizeAny;
+ rPropSet.GetAnyProperty( aRelPosAny, EXC_CHPROP_RELATIVEPOSITION );
+ rPropSet.GetAnyProperty( aRelSizeAny, EXC_CHPROP_RELATIVESIZE );
+ cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ rPropSet.GetProperty( eApiExpand, EXC_CHPROP_EXPANSION );
+ if( aRelPosAny.has< RelativePosition >() || ((eApiExpand == cssc::ChartLegendExpansion_CUSTOM) && aRelSizeAny.has< RelativeSize >()) )
+ {
+ try
+ {
+ /* The 'RelativePosition' or 'RelativeSize' properties are used as
+ indicator of manually changed legend position/size, but due to
+ the different anchor modes used by this property (in the
+ RelativePosition.Anchor member) it cannot be used to calculate
+ the position easily. For this, the Chart1 API will be used
+ instead. */
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< XShape > xChart1Legend( xChart1Doc->getLegend(), UNO_SET_THROW );
+ // coordinates in CHLEGEND record written but not used by Excel
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_CHARTSIZE );
+ XclChFramePos& rFramePos = mxFramePos->GetFramePosData();
+ rFramePos.mnTLMode = EXC_CHFRAMEPOS_CHARTSIZE;
+ css::awt::Point aLegendPos = xChart1Legend->getPosition();
+ rFramePos.maRect.mnX = maData.maRect.mnX = CalcChartXFromHmm( aLegendPos.X );
+ rFramePos.maRect.mnY = maData.maRect.mnY = CalcChartYFromHmm( aLegendPos.Y );
+ // legend size, Excel expects points in CHFRAMEPOS record
+ rFramePos.mnBRMode = EXC_CHFRAMEPOS_ABSSIZE_POINTS;
+ css::awt::Size aLegendSize = xChart1Legend->getSize();
+ rFramePos.maRect.mnWidth = o3tl::convert(aLegendSize.Width, o3tl::Length::mm100, o3tl::Length::pt);
+ rFramePos.maRect.mnHeight = o3tl::convert(aLegendSize.Height, o3tl::Length::mm100, o3tl::Length::pt);
+ maData.maRect.mnWidth = CalcChartXFromHmm( aLegendSize.Width );
+ maData.maRect.mnHeight = CalcChartYFromHmm( aLegendSize.Height );
+ eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ // manual legend position implies manual plot area
+ GetChartData().SetManualPlotArea();
+ maData.mnDockMode = EXC_CHLEGEND_NOTDOCKED;
+ // a CHFRAME record with cleared auto flags is needed
+ if( !mxFrame )
+ mxFrame = new XclExpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ mxFrame->SetAutoFlags( false, false );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclExpChLegend::Convert - cannot get legend shape" );
+ maData.mnDockMode = EXC_CHLEGEND_RIGHT;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ }
+ }
+ else
+ {
+ cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
+ rPropSet.GetProperty( eApiPos, EXC_CHPROP_ANCHORPOSITION );
+ switch( eApiPos )
+ {
+ case cssc2::LegendPosition_LINE_START: maData.mnDockMode = EXC_CHLEGEND_LEFT; break;
+ case cssc2::LegendPosition_LINE_END: maData.mnDockMode = EXC_CHLEGEND_RIGHT; break;
+ case cssc2::LegendPosition_PAGE_START: maData.mnDockMode = EXC_CHLEGEND_TOP; break;
+ case cssc2::LegendPosition_PAGE_END: maData.mnDockMode = EXC_CHLEGEND_BOTTOM; break;
+ default:
+ OSL_FAIL( "XclExpChLegend::Convert - unrecognized legend position" );
+ maData.mnDockMode = EXC_CHLEGEND_RIGHT;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ }
+ }
+ ::set_flag( maData.mnFlags, EXC_CHLEGEND_STACKED, eApiExpand == cssc::ChartLegendExpansion_HIGH );
+
+ // other flags
+ ::set_flag( maData.mnFlags, EXC_CHLEGEND_AUTOSERIES );
+ const sal_uInt16 nAutoFlags = EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY;
+ ::set_flag( maData.mnFlags, nAutoFlags, maData.mnDockMode != EXC_CHLEGEND_NOTDOCKED );
+}
+
+void XclExpChLegend::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxFramePos );
+ lclSaveRecord( rStrm, mxText );
+ lclSaveRecord( rStrm, mxFrame );
+}
+
+void XclExpChLegend::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maRect << maData.mnDockMode << maData.mnSpacing << maData.mnFlags;
+}
+
+XclExpChDropBar::XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DROPBAR, EXC_ID_CHDROPBAR, 2 ),
+ meObjType( eObjType )
+{
+}
+
+void XclExpChDropBar::Convert( const ScfPropertySet& rPropSet )
+{
+ if( rPropSet.Is() )
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
+ else
+ SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, true );
+}
+
+void XclExpChDropBar::WriteSubRecords( XclExpStream& rStrm )
+{
+ WriteFrameRecords( rStrm );
+}
+
+void XclExpChDropBar::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(100); // Distance between bars (CHDROPBAR record).
+}
+
+XclExpChTypeGroup::XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TYPEGROUP, EXC_ID_CHTYPEGROUP, 20 ),
+ maType( rRoot ),
+ maTypeInfo( maType.GetTypeInfo() )
+{
+ maData.mnGroupIdx = nGroupIdx;
+}
+
+void XclExpChTypeGroup::ConvertType(
+ Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels )
+{
+ // chart type settings
+ maType.Convert( xDiagram, xChartType, nApiAxesSetIdx, bSwappedAxesSet, bHasXLabels );
+
+ // spline - TODO: get from single series (#i66858#)
+ ScfPropertySet aTypeProp( xChartType );
+ cssc2::CurveStyle eCurveStyle;
+ bool bSpline = aTypeProp.GetProperty( eCurveStyle, EXC_CHPROP_CURVESTYLE ) &&
+ (eCurveStyle != cssc2::CurveStyle_LINES);
+
+ // extended type info
+ maTypeInfo.Set( maType.GetTypeInfo(), b3dChart, bSpline );
+
+ // 3d chart settings
+ if( maTypeInfo.mb3dChart ) // only true, if Excel chart supports 3d mode
+ {
+ mxChart3d = new XclExpChChart3d();
+ ScfPropertySet aDiaProp( xDiagram );
+ mxChart3d->Convert( aDiaProp, Is3dWallChart() );
+ }
+}
+
+void XclExpChTypeGroup::ConvertSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectBars )
+{
+ Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
+ if( !xSeriesCont.is() )
+ return;
+
+ std::vector< Reference< XDataSeries > > aSeriesVec;
+
+ // copy data series attached to the current axes set to the vector
+ const Sequence< Reference< XDataSeries > > aSeriesSeq = xSeriesCont->getDataSeries();
+ for( const Reference< XDataSeries >& rSeries : aSeriesSeq )
+ {
+ ScfPropertySet aSeriesProp( rSeries );
+ sal_Int32 nSeriesAxesSetIdx(0);
+ if( aSeriesProp.GetProperty( nSeriesAxesSetIdx, EXC_CHPROP_ATTAXISINDEX ) && (nSeriesAxesSetIdx == nGroupAxesSetIdx) )
+ aSeriesVec.push_back( rSeries );
+ }
+
+ // Are there any series in the current axes set?
+ if( aSeriesVec.empty() )
+ return;
+
+ // stacking direction (stacked/percent/deep 3d) from first series
+ ScfPropertySet aSeriesProp( aSeriesVec.front() );
+ cssc2::StackingDirection eStacking;
+ if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) )
+ eStacking = cssc2::StackingDirection_NO_STACKING;
+
+ // stacked or percent chart
+ if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) )
+ {
+ // percent overrides simple stacking
+ maType.SetStacked( bPercent );
+
+ // connected data points (only in stacked bar charts)
+ if (bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR))
+ {
+ sal_uInt16 nKey = EXC_CHCHARTLINE_CONNECT;
+ m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
+ }
+ }
+ else
+ {
+ // reverse series order for some unstacked 2D chart types
+ if( maTypeInfo.mbReverseSeries && !Is3dChart() )
+ ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() );
+ }
+
+ // deep 3d chart or clustered 3d chart (stacked is not clustered)
+ if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() )
+ mxChart3d->SetClustered();
+
+ // varied point colors
+ ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) );
+
+ // process all series
+ for( const auto& rxSeries : aSeriesVec )
+ {
+ // create Excel series object, stock charts need special processing
+ if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
+ CreateAllStockSeries( xChartType, rxSeries );
+ else
+ CreateDataSeries( xDiagram, rxSeries );
+ }
+}
+
+void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
+{
+ for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx )
+ maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq );
+}
+
+void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet )
+{
+ if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ {
+ mxLegend = new XclExpChLegend( GetChRoot() );
+ mxLegend->Convert( rPropSet );
+ }
+}
+
+void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm )
+{
+ maType.Save( rStrm );
+ lclSaveRecord( rStrm, mxChart3d );
+ lclSaveRecord( rStrm, mxLegend );
+ lclSaveRecord( rStrm, mxUpBar );
+ lclSaveRecord( rStrm, mxDownBar );
+ for (auto const& it : m_ChartLines)
+ {
+ lclSaveRecord( rStrm, it.second.get(), EXC_ID_CHCHARTLINE, it.first );
+ }
+}
+
+sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const
+{
+ return static_cast< sal_uInt16 >( maSeries.GetSize() );
+}
+
+void XclExpChTypeGroup::CreateDataSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries )
+{
+ // let chart create series object with correct series index
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries )
+ {
+ if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) )
+ maSeries.AppendRecord( xSeries );
+ else
+ GetChartData().RemoveLastSeries();
+ }
+}
+
+void XclExpChTypeGroup::CreateAllStockSeries(
+ Reference< XChartType > const & xChartType, Reference< XDataSeries > const & xDataSeries )
+{
+ // create existing series objects
+ bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false );
+ bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false );
+ bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false );
+ bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen );
+
+ // formatting of special stock chart elements
+ ScfPropertySet aTypeProp( xChartType );
+ // hi-lo lines
+ if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) )
+ {
+ ScfPropertySet aSeriesProp( xDataSeries );
+ XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( GetChRoot() );
+ xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
+ sal_uInt16 nKey = EXC_CHCHARTLINE_HILO;
+ m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
+ }
+ // dropbars
+ if( !(bHasOpen && bHasClose) )
+ return;
+
+ // dropbar type is dependent on position in the file - always create both
+ Reference< XPropertySet > xWhitePropSet, xBlackPropSet;
+ // white dropbar format
+ aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY );
+ ScfPropertySet aWhiteProp( xWhitePropSet );
+ mxUpBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_WHITEDROPBAR );
+ mxUpBar->Convert( aWhiteProp );
+ // black dropbar format
+ aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY );
+ ScfPropertySet aBlackProp( xBlackPropSet );
+ mxDownBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR );
+ mxDownBar->Convert( aBlackProp );
+}
+
+bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, bool bCloseSymbol )
+{
+ bool bOk = false;
+ // let chart create series object with correct series index
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries )
+ {
+ bOk = xSeries->ConvertStockSeries( xDataSeries,
+ rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol );
+ if( bOk )
+ maSeries.AppendRecord( xSeries );
+ else
+ GetChartData().RemoveLastSeries();
+ }
+ return bOk;
+}
+
+void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 16 );
+ rStrm << maData.mnFlags << maData.mnGroupIdx;
+}
+
+// Axes =======================================================================
+
+XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHLABELRANGE, 8 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChLabelRange::Convert( const ScaleData& rScaleData, const ScfPropertySet& rChart1Axis, bool bMirrorOrient )
+{
+ /* Base time unit (using the property 'ExplicitTimeIncrement' from the old
+ chart API allows to detect axis type (date axis, if property exists),
+ and to receive the base time unit currently used in case the base time
+ unit is set to 'automatic'. */
+ cssc::TimeIncrement aTimeIncrement;
+ if( rChart1Axis.GetProperty( aTimeIncrement, EXC_CHPROP_EXPTIMEINCREMENT ) )
+ {
+ // property exists -> this is a date axis currently
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
+
+ // automatic base time unit, if the UNO Any 'rScaleData.TimeIncrement.TimeResolution' does not contain a valid value...
+ bool bAutoBase = !rScaleData.TimeIncrement.TimeResolution.has< cssc::TimeIncrement >();
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE, bAutoBase );
+
+ // ...but get the current base time unit from the property of the old chart API
+ sal_Int32 nApiTimeUnit = 0;
+ bool bValidBaseUnit = aTimeIncrement.TimeResolution >>= nApiTimeUnit;
+ OSL_ENSURE( bValidBaseUnit, "XclExpChLabelRange::Convert - cannot get base time unit" );
+ maDateData.mnBaseUnit = bValidBaseUnit ? lclGetTimeUnit( nApiTimeUnit ) : EXC_CHDATERANGE_DAYS;
+
+ /* Min/max values depend on base time unit, they specify the number of
+ days, months, or years starting from null date. */
+ bool bAutoMin = lclConvertTimeValue( GetRoot(), maDateData.mnMinDate, rScaleData.Minimum, maDateData.mnBaseUnit );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN, bAutoMin );
+ bool bAutoMax = lclConvertTimeValue( GetRoot(), maDateData.mnMaxDate, rScaleData.Maximum, maDateData.mnBaseUnit );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX, bAutoMax );
+ }
+
+ // automatic axis type detection
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE, rScaleData.AutoDateAxis );
+
+ // increment
+ bool bAutoMajor = lclConvertTimeInterval( maDateData.mnMajorStep, maDateData.mnMajorUnit, rScaleData.TimeIncrement.MajorTimeInterval );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR, bAutoMajor );
+ bool bAutoMinor = lclConvertTimeInterval( maDateData.mnMinorStep, maDateData.mnMinorUnit, rScaleData.TimeIncrement.MinorTimeInterval );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR, bAutoMinor );
+
+ // origin
+ double fOrigin = 0.0;
+ if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) )
+ maLabelData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 );
+
+ // reverse order
+ if( (rScaleData.Orientation == cssc2::AxisOrientation_REVERSE) != bMirrorOrient )
+ ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE );
+}
+
+void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
+{
+ cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
+ rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION );
+ double fCrossingPos = 1.0;
+ rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE );
+
+ bool bDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
+ switch( eAxisPos )
+ {
+ case cssc::ChartAxisPosition_ZERO:
+ case cssc::ChartAxisPosition_START:
+ maLabelData.mnCross = 1;
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ break;
+ case cssc::ChartAxisPosition_END:
+ ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_MAXCROSS );
+ break;
+ case cssc::ChartAxisPosition_VALUE:
+ maLabelData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS, false );
+ if( bDateAxis )
+ maDateData.mnCross = lclGetTimeValue( GetRoot(), fCrossingPos, maDateData.mnBaseUnit );
+ break;
+ default:
+ maLabelData.mnCross = 1;
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ }
+}
+
+void XclExpChLabelRange::Save( XclExpStream& rStrm )
+{
+ // the CHLABELRANGE record
+ XclExpRecord::Save( rStrm );
+
+ // the CHDATERANGE record with date axis settings (BIFF8 only)
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ rStrm.StartRecord( EXC_ID_CHDATERANGE, 18 );
+ rStrm << maDateData.mnMinDate
+ << maDateData.mnMaxDate
+ << maDateData.mnMajorStep
+ << maDateData.mnMajorUnit
+ << maDateData.mnMinorStep
+ << maDateData.mnMinorUnit
+ << maDateData.mnBaseUnit
+ << maDateData.mnCross
+ << maDateData.mnFlags;
+ rStrm.EndRecord();
+}
+
+void XclExpChLabelRange::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maLabelData.mnCross << maLabelData.mnLabelFreq << maLabelData.mnTickFreq << maLabelData.mnFlags;
+}
+
+XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHVALUERANGE, 42 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChValueRange::Convert( const ScaleData& rScaleData )
+{
+ // scaling algorithm
+ bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == "com.sun.star.chart2.LogarithmicScaling";
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale );
+
+ // min/max
+ bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin );
+ bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax );
+
+ // origin
+ bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross );
+
+ // major increment
+ const IncrementData& rIncrementData = rScaleData.IncrementData;
+ const bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0);
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor );
+ // minor increment
+ const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
+ sal_Int32 nCount = 0;
+
+ // tdf#114168 If IntervalCount is 5, then enable automatic minor calculation.
+ // During import, if minorUnit is set and majorUnit not, then it is impossible
+ // to calculate IntervalCount.
+ const bool bAutoMinor = bLogScale || bAutoMajor || !rSubIncrementSeq.hasElements() ||
+ lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1) || (nCount == 5);
+
+ if( maData.mfMajorStep && !bAutoMinor )
+ maData.mfMinorStep = maData.mfMajorStep / nCount;
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor );
+
+ // reverse order
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE );
+}
+
+void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
+{
+ cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
+ double fCrossingPos = 0.0;
+ if( !(rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE )) )
+ return;
+
+ switch( eAxisPos )
+ {
+ case cssc::ChartAxisPosition_ZERO:
+ case cssc::ChartAxisPosition_START:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ break;
+ case cssc::ChartAxisPosition_END:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
+ break;
+ case cssc::ChartAxisPosition_VALUE:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, false );
+ maData.mfCross = ::get_flagvalue< double >( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, log( fCrossingPos ) / log( 10.0 ), fCrossingPos );
+ break;
+ default:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ }
+}
+
+void XclExpChValueRange::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mfMin
+ << maData.mfMax
+ << maData.mfMajorStep
+ << maData.mfMinorStep
+ << maData.mfCross
+ << maData.mnFlags;
+}
+
+namespace {
+
+sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks )
+{
+ using namespace cssc2::TickmarkStyle;
+ sal_uInt8 nXclTickPos = 0;
+ ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE, ::get_flag( nApiTickmarks, INNER ) );
+ ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) );
+ return nXclTickPos;
+}
+
+} // namespace
+
+XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ),
+ XclExpChRoot( rRoot ),
+ mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType )
+{
+ // tick mark style
+ sal_Int32 nApiTickmarks = 0;
+ if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) )
+ maData.mnMajor = lclGetXclTickPos( nApiTickmarks );
+ if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) )
+ maData.mnMinor = lclGetXclTickPos( nApiTickmarks );
+
+ // axis labels
+ if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) )
+ {
+ /* Radar charts disable their category labels via chart type, not via
+ axis, and axis labels are always 'near axis'. */
+ maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) )
+ {
+ // no labels
+ maData.mnLabelPos = EXC_CHTICK_NOLABEL;
+ }
+ else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) )
+ {
+ // Excel expects 'near axis' at Y axes in 3D charts
+ maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ else
+ {
+ cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS;
+ rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION );
+ switch( eApiLabelPos )
+ {
+ case cssc::ChartAxisLabelPosition_NEAR_AXIS:
+ case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT; break;
+ case cssc::ChartAxisLabelPosition_OUTSIDE_START: maData.mnLabelPos = EXC_CHTICK_LOW; break;
+ case cssc::ChartAxisLabelPosition_OUTSIDE_END: maData.mnLabelPos = EXC_CHTICK_HIGH; break;
+ default: maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ }
+}
+
+void XclExpChTick::SetFontColor(model::ComplexColor const& rComplexColor, sal_uInt32 nColorId )
+{
+ maData.maTextComplexColor = rComplexColor;
+ ::set_flag(maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rComplexColor.getFinalColor() == COL_AUTO);
+ mnTextColorId = nColorId;
+}
+
+void XclExpChTick::SetRotation( sal_uInt16 nRotation )
+{
+ maData.mnRotation = nRotation;
+ ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false );
+ ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 );
+}
+
+void XclExpChTick::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnMajor
+ << maData.mnMinor
+ << maData.mnLabelPos
+ << maData.mnBackMode;
+ rStrm.WriteZeroBytes( 16 );
+ rStrm << maData.maTextComplexColor.getFinalColor()
+ << maData.mnFlags;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation;
+}
+
+namespace {
+
+/** Returns an API axis object from the passed coordinate system. */
+Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > const & xCoordSystem,
+ sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
+{
+ Reference< XAxis > xAxis;
+ if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try
+ {
+ xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx );
+ }
+ catch( Exception& )
+ {
+ }
+ return xAxis;
+}
+
+Reference< cssc::XAxis > lclGetApiChart1Axis( Reference< XChartDocument > const & xChartDoc,
+ sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
+{
+ Reference< cssc::XAxis > xChart1Axis;
+ try
+ {
+ Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY_THROW );
+ Reference< cssc::XAxisSupplier > xChart1AxisSupp( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
+ switch( nApiAxesSetIdx )
+ {
+ case EXC_CHART_AXESSET_PRIMARY:
+ xChart1Axis = xChart1AxisSupp->getAxis( nApiAxisDim );
+ break;
+ case EXC_CHART_AXESSET_SECONDARY:
+ xChart1Axis = xChart1AxisSupp->getSecondaryAxis( nApiAxisDim );
+ break;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return xChart1Axis;
+}
+
+} // namespace
+
+XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ),
+ mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
+{
+ maData.mnType = nAxisType;
+}
+
+void XclExpChAxis::SetFont( XclExpChFontRef xFont, model::ComplexColor const& rComplexColor, sal_uInt32 nColorId )
+{
+ mxFont = xFont;
+ if( mxTick )
+ mxTick->SetFontColor(rComplexColor, nColorId);
+}
+
+void XclExpChAxis::SetRotation( sal_uInt16 nRotation )
+{
+ if( mxTick )
+ mxTick->SetRotation( nRotation );
+}
+
+void XclExpChAxis::Convert( Reference< XAxis > const & xAxis, Reference< XAxis > const & xCrossingAxis,
+ Reference< cssc::XAxis > const & xChart1Axis, const XclChExtTypeInfo& rTypeInfo )
+{
+ ScfPropertySet aAxisProp( xAxis );
+ bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z);
+
+ // axis line format -------------------------------------------------------
+
+ mxAxisLine = new XclExpChLineFormat( GetChRoot() );
+ mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
+ // #i58688# axis enabled
+ mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) );
+
+ // axis scaling and increment ---------------------------------------------
+
+ ScfPropertySet aCrossingProp( xCrossingAxis );
+ if( bCategoryAxis )
+ {
+ mxLabelRange = new XclExpChLabelRange( GetChRoot() );
+ mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg );
+ if( xAxis.is() )
+ {
+ ScfPropertySet aChart1AxisProp( xChart1Axis );
+ // #i71684# radar charts have reversed rotation direction
+ mxLabelRange->Convert( xAxis->getScaleData(), aChart1AxisProp, (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) );
+ }
+ // get position of crossing axis on this axis from passed axis object
+ if( aCrossingProp.Is() )
+ mxLabelRange->ConvertAxisPosition( aCrossingProp );
+ }
+ else
+ {
+ mxValueRange = new XclExpChValueRange( GetChRoot() );
+ if( xAxis.is() )
+ mxValueRange->Convert( xAxis->getScaleData() );
+ // get position of crossing axis on this axis from passed axis object
+ if( aCrossingProp.Is() )
+ mxValueRange->ConvertAxisPosition( aCrossingProp );
+ }
+
+ // axis caption text ------------------------------------------------------
+
+ // axis ticks properties
+ mxTick = new XclExpChTick( GetChRoot() );
+ mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() );
+
+ // axis label formatting and rotation
+ ConvertFontBase( GetChRoot(), aAxisProp );
+ ConvertRotationBase( aAxisProp, true );
+
+ // axis number format
+ sal_Int32 nApiNumFmt = 0;
+ if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
+ {
+ bool bLinkNumberFmtToSource = false;
+ if ( !aAxisProp.GetProperty( bLinkNumberFmtToSource, EXC_CHPROP_NUMBERFORMAT_LINKSRC ) || !bLinkNumberFmtToSource )
+ mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
+ }
+
+ // grid -------------------------------------------------------------------
+
+ if( !xAxis.is() )
+ return;
+
+ // main grid
+ ScfPropertySet aGridProp( xAxis->getGridProperties() );
+ if( aGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ mxMajorGrid = lclCreateLineFormat( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ // sub grid
+ Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
+ if( aSubGridPropSeq.hasElements() )
+ {
+ ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
+ if( aSubGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ mxMinorGrid = lclCreateLineFormat( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ }
+}
+
+void XclExpChAxis::ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram )
+{
+ if( !xDiagram.is() )
+ return;
+
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ {
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D );
+ }
+ break;
+ case EXC_CHAXIS_Y:
+ {
+ ScfPropertySet aFloorProp( xDiagram->getFloor() );
+ mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D );
+ }
+ break;
+ default:
+ mxWallFrame.clear();
+ }
+}
+
+void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxLabelRange );
+ lclSaveRecord( rStrm, mxValueRange );
+ if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
+ XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm );
+ lclSaveRecord( rStrm, mxTick );
+ lclSaveRecord( rStrm, mxFont );
+ lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE );
+ lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID );
+ lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID );
+ lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS );
+}
+
+void XclExpChAxis::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnType;
+ rStrm.WriteZeroBytes( 16 );
+}
+
+XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 )
+{
+ maData.mnAxesSetId = nAxesSetId;
+ SetFutureRecordContext( 0, nAxesSetId );
+
+ /* Need to set a reasonable size for the plot area, otherwise Excel will
+ move away embedded shapes while auto-sizing the plot area. This is just
+ a wild guess, but will be fixed with implementing manual positioning of
+ chart elements. */
+ maData.maRect.mnX = 262;
+ maData.maRect.mnY = 626;
+ maData.maRect.mnWidth = 3187;
+ maData.maRect.mnHeight = 2633;
+}
+
+sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > const & xDiagram, sal_uInt16 nFirstGroupIdx )
+{
+ /* First unused chart type group index is passed to be able to continue
+ counting of chart type groups for secondary axes set. */
+ sal_uInt16 nGroupIdx = nFirstGroupIdx;
+ Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY );
+ if( xCoordSysCont.is() )
+ {
+ Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems();
+ if( aCoordSysSeq.hasElements() )
+ {
+ /* Process first coordinate system only. Import filter puts all
+ chart types into one coordinate system. */
+ Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ];
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+
+ // 3d mode
+ bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3);
+
+ // percent charts
+ namespace ApiAxisType = cssc2::AxisType;
+ Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx );
+ bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT);
+
+ // connector lines in bar charts
+ ScfPropertySet aDiaProp( xDiagram );
+ bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS );
+
+ // swapped axes sets
+ ScfPropertySet aCoordSysProp( xCoordSystem );
+ bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS );
+
+ // X axis for later use
+ Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx );
+ // X axis labels
+ ScfPropertySet aXAxisProp( xApiXAxis );
+ bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS );
+
+ // process chart types
+ Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
+ if( xChartTypeCont.is() )
+ {
+ const Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes();
+ for( const Reference< XChartType >& rChartType : aChartTypeSeq )
+ {
+ XclExpChTypeGroupRef xTypeGroup = new XclExpChTypeGroup( GetChRoot(), nGroupIdx );
+ xTypeGroup->ConvertType( xDiagram, rChartType, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels );
+ /* If new chart type group cannot be inserted into a combination
+ chart with existing type groups, insert all series into last
+ contained chart type group instead of creating a new group. */
+ XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup();
+ if( xLastGroup && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) )
+ {
+ xLastGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
+ }
+ else
+ {
+ xTypeGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
+ if( xTypeGroup->IsValidGroup() )
+ {
+ maTypeGroups.AppendRecord( xTypeGroup );
+ ++nGroupIdx;
+ }
+ }
+ }
+ }
+
+ if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() )
+ {
+ const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo();
+
+ // create axes according to chart type (no axes for pie and donut charts)
+ if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
+ {
+ ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y );
+ ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X );
+ if( pGroup->Is3dDeepChart() )
+ ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE );
+ }
+
+ // X axis category ranges
+ if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() )
+ {
+ const ScaleData aScaleData = xApiXAxis->getScaleData();
+ for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx )
+ maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories );
+ }
+
+ // legend
+ if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
+ {
+ Reference< XLegend > xLegend = xDiagram->getLegend();
+ if( xLegend.is() )
+ {
+ ScfPropertySet aLegendProp( xLegend );
+ pGroup->ConvertLegend( aLegendProp );
+ }
+ }
+ }
+ }
+ }
+
+ // wall/floor/diagram frame formatting
+ if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
+ {
+ XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup && xTypeGroup->Is3dWallChart() )
+ {
+ // wall/floor formatting (3D charts)
+ if( mxXAxis )
+ mxXAxis->ConvertWall( xDiagram );
+ if( mxYAxis )
+ mxYAxis->ConvertWall( xDiagram );
+ }
+ else
+ {
+ // diagram background formatting
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME );
+ }
+ }
+
+ // inner and outer plot area position and size
+ try
+ {
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
+ // set manual flag in chart data
+ if( !xPositioning->isAutomaticDiagramPositioning() )
+ GetChartData().SetManualPlotArea();
+ // the CHAXESSET record contains the inner plot area
+ maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() );
+ // the embedded CHFRAMEPOS record contains the outer plot area
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
+ // for pie charts, always use inner plot area size to exclude the data labels as Excel does
+ const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get();
+ bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE);
+ mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect :
+ CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // return first unused chart type group index for next axes set
+ return nGroupIdx;
+}
+
+bool XclExpChAxesSet::Is3dChart() const
+{
+ XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ return xTypeGroup && xTypeGroup->Is3dChart();
+}
+
+void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxFramePos );
+ lclSaveRecord( rStrm, mxXAxis );
+ lclSaveRecord( rStrm, mxYAxis );
+ lclSaveRecord( rStrm, mxZAxis );
+ lclSaveRecord( rStrm, mxXAxisTitle );
+ lclSaveRecord( rStrm, mxYAxisTitle );
+ lclSaveRecord( rStrm, mxZAxisTitle );
+ if( mxPlotFrame )
+ {
+ XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm );
+ mxPlotFrame->Save( rStrm );
+ }
+ maTypeGroups.Save( rStrm );
+}
+
+XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const
+{
+ return maTypeGroups.GetFirstRecord();
+}
+
+XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const
+{
+ return maTypeGroups.GetLastRecord();
+}
+
+void XclExpChAxesSet::ConvertAxis(
+ XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
+ XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
+ Reference< XCoordinateSystem > const & xCoordSystem, const XclChExtTypeInfo& rTypeInfo,
+ sal_Int32 nCrossingAxisDim )
+{
+ // create and convert axis object
+ rxChAxis = new XclExpChAxis( GetChRoot(), nAxisType );
+ sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension();
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx );
+ Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx );
+ Reference< cssc::XAxis > xChart1Axis = lclGetApiChart1Axis( GetChartDocument(), nApiAxisDim, nApiAxesSetIdx );
+ rxChAxis->Convert( xAxis, xCrossingAxis, xChart1Axis, rTypeInfo );
+
+ // create and convert axis title
+ Reference< XTitled > xTitled( xAxis, UNO_QUERY );
+ rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget );
+}
+
+void XclExpChAxesSet::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnAxesSetId << maData.maRect;
+}
+
+// The chart object ===========================================================
+
+static void lcl_getChartSubTitle(const Reference<XChartDocument>& xChartDoc,
+ OUString& rSubTitle)
+{
+ Reference< css::chart::XChartDocument > xChartDoc1(xChartDoc, UNO_QUERY);
+ if (!xChartDoc1.is())
+ return;
+
+ Reference< XPropertySet > xProp(xChartDoc1->getSubTitle(), UNO_QUERY);
+ if (!xProp.is())
+ return;
+
+ OUString aTitle;
+ Any any = xProp->getPropertyValue("String");
+ if (any >>= aTitle)
+ rSubTitle = aTitle;
+}
+
+XclExpChChart::XclExpChChart( const XclExpRoot& rRoot,
+ Reference< XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) :
+ XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 )
+{
+ Size aPtSize = o3tl::convert( rChartRect.GetSize(), o3tl::Length::mm100, o3tl::Length::pt );
+ // rectangle is stored in 16.16 fixed-point format
+ maRect.mnX = maRect.mnY = 0;
+ maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 );
+ maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 );
+
+ // global chart properties (default values)
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false );
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA );
+ maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;
+
+ // always create both axes set objects
+ mxPrimAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
+ mxSecnAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
+
+ if( !xChartDoc.is() )
+ return;
+
+ Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram();
+
+ // global chart properties (only 'include hidden cells' attribute for now)
+ ScfPropertySet aDiagramProp( xDiagram );
+ bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS );
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden );
+
+ // initialize API conversion (remembers xChartDoc and rChartRect internally)
+ InitConversion( xChartDoc, rChartRect );
+
+ // chart frame
+ ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
+ mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND );
+
+ // chart title
+ Reference< XTitled > xTitled( xChartDoc, UNO_QUERY );
+ OUString aSubTitle;
+ lcl_getChartSubTitle(xChartDoc, aSubTitle);
+ mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE,
+ !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
+
+ // diagrams (axes sets)
+ sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 );
+ if( !mxPrimAxesSet->Is3dChart() )
+ mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx );
+
+ // treatment of missing values
+ ScfPropertySet aDiaProp( xDiagram );
+ sal_Int32 nMissingValues = 0;
+ if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) )
+ {
+ using namespace cssc::MissingValueTreatment;
+ switch( nMissingValues )
+ {
+ case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; break;
+ case USE_ZERO: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO; break;
+ case CONTINUE: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE; break;
+ }
+ }
+
+ // finish API conversion
+ FinishConversion();
+}
+
+XclExpChSeriesRef XclExpChChart::CreateSeries()
+{
+ XclExpChSeriesRef xSeries;
+ sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() );
+ if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES )
+ {
+ xSeries = new XclExpChSeries( GetChRoot(), nSeriesIdx );
+ maSeries.AppendRecord( xSeries );
+ }
+ return xSeries;
+}
+
+void XclExpChChart::RemoveLastSeries()
+{
+ if( !maSeries.IsEmpty() )
+ maSeries.RemoveRecord( maSeries.GetSize() - 1 );
+}
+
+void XclExpChChart::SetDataLabel( XclExpChTextRef const & xText )
+{
+ if( xText )
+ maLabels.AppendRecord( xText );
+}
+
+void XclExpChChart::SetManualPlotArea()
+{
+ // this flag does not exist in BIFF5
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
+}
+
+void XclExpChChart::WriteSubRecords( XclExpStream& rStrm )
+{
+ // background format
+ lclSaveRecord( rStrm, mxFrame );
+
+ // data series
+ maSeries.Save( rStrm );
+
+ // CHPROPERTIES record
+ rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 );
+ rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 );
+ rStrm.EndRecord();
+
+ // axes sets (always save primary axes set)
+ sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1;
+ XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm );
+ mxPrimAxesSet->Save( rStrm );
+ if( mxSecnAxesSet->IsValidAxesSet() )
+ mxSecnAxesSet->Save( rStrm );
+
+ // chart title and data labels
+ lclSaveRecord( rStrm, mxTitle );
+ maLabels.Save( rStrm );
+}
+
+void XclExpChChart::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maRect;
+}
+
+XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot,
+ const Reference< XModel >& rxModel, const Size& rChartSize ) :
+ XclExpRoot( rRoot )
+{
+ if( rChartSize.IsEmpty() )
+ return;
+
+ ScfPropertySet aPropSet( rxModel );
+ Reference< XShapes > xShapes;
+ if( !(aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0)) )
+ return;
+
+ /* Create a new independent object manager with own DFF stream for the
+ DGCONTAINER, pass global manager as parent for shared usage of
+ global DFF data (picture container etc.). */
+ mxObjMgr = std::make_shared<XclExpEmbeddedObjectManager>( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS );
+ // initialize the drawing object list
+ mxObjMgr->StartSheet();
+ // process the draw page (convert all shapes)
+ mxObjRecs = mxObjMgr->ProcessDrawing( xShapes );
+ // finalize the DFF stream
+ mxObjMgr->EndDocument();
+}
+
+XclExpChartDrawing::~XclExpChartDrawing()
+{
+}
+
+void XclExpChartDrawing::Save( XclExpStream& rStrm )
+{
+ if( mxObjRecs )
+ mxObjRecs->Save( rStrm );
+}
+
+XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > const & xModel, const tools::Rectangle& rChartRect ) :
+ XclExpSubStream( EXC_BOF_CHART ),
+ XclExpRoot( rRoot )
+{
+ AppendNewRecord( new XclExpChartPageSettings( rRoot ) );
+ AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) );
+ AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) );
+ AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) );
+
+ Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
+ AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xecontent.cxx b/sc/source/filter/excel/xecontent.cxx
new file mode 100644
index 0000000000..082845651d
--- /dev/null
+++ b/sc/source/filter/excel/xecontent.cxx
@@ -0,0 +1,2226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+#include <xecontent.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <string_view>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/XAreaLinks.hpp>
+#include <com/sun/star/sheet/XAreaLink.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/grammar.hxx>
+#include <scitems.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <validat.hxx>
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <stlpool.hxx>
+#include <patattr.hxx>
+#include <fapihelper.hxx>
+#include <xehelper.hxx>
+#include <xestyle.hxx>
+#include <xename.hxx>
+#include <xlcontent.hxx>
+#include <xltools.hxx>
+#include <xeformula.hxx>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::table::CellRangeAddress;
+using ::com::sun::star::sheet::XAreaLinks;
+using ::com::sun::star::sheet::XAreaLink;
+
+// Shared string table ========================================================
+
+namespace {
+
+/** A single string entry in the hash table. */
+struct XclExpHashEntry
+{
+ const XclExpString* mpString; /// Pointer to the string (no ownership).
+ sal_uInt32 mnSstIndex; /// The SST index of this string.
+ explicit XclExpHashEntry( const XclExpString* pString, sal_uInt32 nSstIndex ) :
+ mpString( pString ), mnSstIndex( nSstIndex ) {}
+};
+
+/** Function object for strict weak ordering. */
+struct XclExpHashEntrySWO
+{
+ bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
+ { return *rLeft.mpString < *rRight.mpString; }
+};
+
+}
+
+/** Implementation of the SST export.
+ @descr Stores all passed strings in a hash table and prevents repeated
+ insertion of equal strings. */
+class XclExpSstImpl
+{
+public:
+ explicit XclExpSstImpl();
+
+ /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
+ sal_uInt32 Insert( XclExpStringRef xString );
+
+ /** Writes the complete SST and EXTSST records. */
+ void Save( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
+
+ std::vector< XclExpStringRef > maStringVector; /// List of unique strings (in SST ID order).
+ std::vector< XclExpHashVec >
+ maHashTab; /// Hashed table that manages string pointers.
+ sal_uInt32 mnTotal; /// Total count of strings (including doubles).
+ sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
+};
+
+const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
+
+XclExpSstImpl::XclExpSstImpl() :
+ maHashTab( EXC_SST_HASHTABLE_SIZE ),
+ mnTotal( 0 ),
+ mnSize( 0 )
+{
+}
+
+sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
+{
+ OSL_ENSURE( xString, "XclExpSstImpl::Insert - empty pointer not allowed" );
+ if( !xString )
+ xString.reset( new XclExpString );
+
+ ++mnTotal;
+ sal_uInt32 nSstIndex = 0;
+
+ // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
+ sal_uInt16 nHash = xString->GetHash();
+ nHash = (nHash ^ (nHash / EXC_SST_HASHTABLE_SIZE)) % EXC_SST_HASHTABLE_SIZE;
+
+ XclExpHashVec& rVec = maHashTab[ nHash ];
+ XclExpHashEntry aEntry( xString.get(), mnSize );
+ XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
+ if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
+ {
+ nSstIndex = mnSize;
+ maStringVector.push_back( xString );
+ rVec.insert( aIt, aEntry );
+ ++mnSize;
+ }
+ else
+ {
+ nSstIndex = aIt->mnSstIndex;
+ }
+
+ return nSstIndex;
+}
+
+void XclExpSstImpl::Save( XclExpStream& rStrm )
+{
+ if( maStringVector.empty() )
+ return;
+
+ SvMemoryStream aExtSst( 8192 );
+
+ sal_uInt32 nBucket = mnSize;
+ while( nBucket > 0x0100 )
+ nBucket /= 2;
+
+ sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
+ sal_uInt16 nBucketIndex = 0;
+
+ // *** write the SST record ***
+
+ rStrm.StartRecord( EXC_ID_SST, 8 );
+
+ rStrm << mnTotal << mnSize;
+ for (auto const& elem : maStringVector)
+ {
+ if( !nBucketIndex )
+ {
+ // write bucket info before string to get correct record position
+ sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
+ sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
+ aExtSst.WriteUInt32( nStrmPos ) // stream position
+ .WriteUInt16( nRecPos ) // position from start of SST or CONTINUE
+ .WriteUInt16( 0 ); // reserved
+ }
+
+ rStrm << *elem;
+
+ if( ++nBucketIndex == nPerBucket )
+ nBucketIndex = 0;
+ }
+
+ rStrm.EndRecord();
+
+ // *** write the EXTSST record ***
+
+ rStrm.StartRecord( EXC_ID_EXTSST, 0 );
+
+ rStrm << nPerBucket;
+ rStrm.SetSliceSize( 8 ); // size of one bucket info
+ aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( aExtSst );
+
+ rStrm.EndRecord();
+}
+
+void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maStringVector.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
+ "xl/sharedStrings.xml",
+ u"sharedStrings.xml",
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
+ oox::getRelationship(Relationship::SHAREDSTRINGS));
+ rStrm.PushStream( pSst );
+
+ pSst->startElement( XML_sst,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ XML_count, OString::number(mnTotal),
+ XML_uniqueCount, OString::number(mnSize) );
+
+ for (auto const& elem : maStringVector)
+ {
+ pSst->startElement(XML_si);
+ elem->WriteXml( rStrm );
+ pSst->endElement( XML_si );
+ }
+
+ pSst->endElement( XML_sst );
+
+ rStrm.PopStream();
+}
+
+XclExpSst::XclExpSst() :
+ mxImpl( new XclExpSstImpl )
+{
+}
+
+XclExpSst::~XclExpSst()
+{
+}
+
+sal_uInt32 XclExpSst::Insert( const XclExpStringRef& xString )
+{
+ return mxImpl->Insert( xString );
+}
+
+void XclExpSst::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+// Merged cells ===============================================================
+
+XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maMergedRanges.push_back( rRange );
+ maBaseXFIds.push_back( nBaseXFId );
+ }
+}
+
+sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
+{
+ OSL_ENSURE( maBaseXFIds.size() == maMergedRanges.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
+ ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
+ ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
+ for ( size_t i = 0, nRanges = rNCRanges.size(); i < nRanges; ++i, ++aIt )
+ {
+ const ScRange & rScRange = rNCRanges[ i ];
+ if( rScRange.Contains( rPos ) )
+ return *aIt;
+ }
+ return EXC_XFID_NOTFOUND;
+}
+
+void XclExpMergedcells::Save( XclExpStream& rStrm )
+{
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ XclRangeList aXclRanges;
+ GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
+ size_t nFirstRange = 0;
+ size_t nRemainingRanges = aXclRanges.size();
+ while( nRemainingRanges > 0 )
+ {
+ size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
+ rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
+ aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
+ rStrm.EndRecord();
+ nFirstRange += nRangeCount;
+ nRemainingRanges -= nRangeCount;
+ }
+}
+
+void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
+{
+ size_t nCount = maMergedRanges.size();
+ if( !nCount )
+ return;
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_mergeCells, XML_count, OString::number(nCount));
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange = maMergedRanges[ i ];
+ rWorksheet->singleElement(XML_mergeCell, XML_ref,
+ XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rRange));
+ }
+ rWorksheet->endElement( XML_mergeCells );
+}
+
+// Hyperlinks =================================================================
+
+XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
+ XclExpRecord( EXC_ID_HLINK ),
+ maScPos( rScPos ),
+ mxVarData( new SvMemoryStream ),
+ mnFlags( 0 )
+{
+ const OUString& rUrl = rUrlField.GetURL();
+ const OUString& rRepr = rUrlField.GetRepresentation();
+ INetURLObject aUrlObj( rUrl );
+ const INetProtocol eProtocol = aUrlObj.GetProtocol();
+ bool bWithRepr = !rRepr.isEmpty();
+ XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
+
+ // description
+ if( bWithRepr )
+ {
+ XclExpString aDescr( rRepr, XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
+ aDescr.WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_DESCR;
+ m_Repr = rRepr;
+ }
+
+ // file link or URL
+ if( eProtocol == INetProtocol::File || eProtocol == INetProtocol::Smb )
+ {
+ sal_uInt16 nLevel;
+ bool bRel;
+ OUString aFileName(
+ BuildFileName(nLevel, bRel, rUrl, rRoot, rRoot.GetOutput() == EXC_OUTPUT_XML_2007));
+
+ if( eProtocol == INetProtocol::Smb )
+ {
+ // #n382718# (and #n261623#) Convert smb notation to '\\'
+ aFileName = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ aFileName = aFileName.copy(4); // skip the 'smb:' part
+ aFileName = aFileName.replace('/', '\\');
+ }
+
+ if( !bRel )
+ mnFlags |= EXC_HLINK_ABS;
+ mnFlags |= EXC_HLINK_BODY;
+
+ OString aAsciiLink(OUStringToOString(aFileName,
+ rRoot.GetTextEncoding()));
+ XclExpString aLink( aFileName, XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << XclTools::maGuidFileMoniker
+ << nLevel
+ << sal_uInt32( aAsciiLink.getLength() + 1 ); // string length + 1 trailing zero byte
+ aXclStrm.Write( aAsciiLink.getStr(), aAsciiLink.getLength() );
+ aXclStrm << sal_uInt8( 0 )
+ << sal_uInt32( 0xDEADFFFF );
+ aXclStrm.WriteZeroBytes( 20 );
+ aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
+ << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
+ << sal_uInt16( 0x0003 );
+ aLink.WriteBuffer( aXclStrm ); // NO flags
+
+ if (m_Repr.isEmpty())
+ m_Repr = aFileName;
+
+ msTarget = XclXmlUtils::ToOUString( aLink );
+
+ if( bRel )
+ {
+ for( int i = 0; i < nLevel; ++i )
+ msTarget = "../" + msTarget;
+ }
+ else if (rRoot.GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ // xls expects the file:/// part appended ( or at least
+ // ms2007 does, ms2010 is more tolerant )
+ msTarget = "file:///" + msTarget;
+ }
+ }
+ else if( eProtocol != INetProtocol::NotValid )
+ {
+ XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << XclTools::maGuidUrlMoniker
+ << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
+ aUrl.WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
+ if (m_Repr.isEmpty())
+ m_Repr = rUrl;
+
+ msTarget = XclXmlUtils::ToOUString( aUrl );
+ }
+ else if( !rUrl.isEmpty() && rUrl[0] == '#' ) // hack for #89066#
+ {
+ OUString aTextMark( rUrl.copy( 1 ) );
+
+ sal_Int32 nSepPos = aTextMark.lastIndexOf( '!' );
+ sal_Int32 nPointPos = aTextMark.lastIndexOf( '.' );
+ // last dot is the separator, if there is no ! after it
+ if(nSepPos < nPointPos)
+ {
+ nSepPos = nPointPos;
+ aTextMark = aTextMark.replaceAt( nSepPos, 1, u"!" );
+ }
+
+ if (nSepPos != -1)
+ {
+ std::u16string_view aSheetName(aTextMark.subView(0, nSepPos));
+
+ if (aSheetName.find(' ') != std::u16string_view::npos && aSheetName[0] != '\'')
+ {
+ aTextMark = "'" + aTextMark.replaceAt(nSepPos, 0, u"'");
+ }
+ }
+ else
+ {
+ SCTAB nTab;
+ if (rRoot.GetDoc().GetTable(aTextMark, nTab))
+ aTextMark += "!A1"; // tdf#143220 link to sheet not valid without cell reference
+ }
+
+ mxTextMark.reset( new XclExpString( aTextMark, XclStrFlags::ForceUnicode, 255 ) );
+ }
+
+ // text mark
+ if( !mxTextMark && aUrlObj.HasMark() )
+ mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), XclStrFlags::ForceUnicode, 255 ) );
+
+ if( mxTextMark )
+ {
+ aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
+ mxTextMark->WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_MARK;
+
+ OUString location = XclXmlUtils::ToOUString(*mxTextMark);
+ if (!location.isEmpty() && msTarget.endsWith(Concat2View("#" + location)))
+ msTarget = msTarget.copy(0, msTarget.getLength() - location.getLength() - 1);
+ }
+
+ SetRecSize( 32 + mxVarData->Tell() );
+}
+
+XclExpHyperlink::~XclExpHyperlink()
+{
+}
+
+OUString XclExpHyperlink::BuildFileName(
+ sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded )
+{
+ INetURLObject aURLObject( rUrl );
+ OUString aDosName(bEncoded ? aURLObject.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)
+ : aURLObject.getFSysPath(FSysStyle::Dos));
+ rnLevel = 0;
+ rbRel = rRoot.IsRelUrl();
+
+ if( rbRel )
+ {
+ // try to convert to relative file name
+ OUString aTmpName( aDosName );
+ aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
+ INetURLObject::EncodeMechanism::WasEncoded,
+ (bEncoded ? INetURLObject::DecodeMechanism::ToIUri : INetURLObject::DecodeMechanism::WithCharset));
+
+ if (aDosName.startsWith(INET_FILE_SCHEME))
+ {
+ // not converted to rel -> back to old, return absolute flag
+ aDosName = aTmpName;
+ rbRel = false;
+ }
+ else if (aDosName.startsWith("./"))
+ {
+ aDosName = aDosName.copy(2);
+ }
+ else
+ {
+ while (aDosName.startsWith("../"))
+ {
+ aDosName = aDosName.copy(3);
+ ++rnLevel;
+ }
+ }
+ }
+ return aDosName;
+}
+
+void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
+ sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
+ rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
+ WriteEmbeddedData( rStrm );
+}
+
+void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
+{
+ rStrm << XclTools::maGuidStdLink
+ << sal_uInt32( 2 )
+ << mnFlags;
+
+ mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( *mxVarData );
+}
+
+void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString sId = !msTarget.isEmpty() ? rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
+ oox::getRelationship(Relationship::HYPERLINK),
+ msTarget, true ) : OUString();
+ std::optional<OString> sTextMark;
+ if (mxTextMark)
+ sTextMark = XclXmlUtils::ToOString(*mxTextMark);
+ rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScPos),
+ FSNS( XML_r, XML_id ), sax_fastparser::UseIf(sId, !sId.isEmpty()),
+ XML_location, sTextMark,
+ // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
+ XML_display, m_Repr );
+}
+
+// Label ranges ===============================================================
+
+XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ SCTAB nScTab = GetCurrScTab();
+ // row label ranges
+ FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
+ // row labels only over 1 column (restriction of Excel97/2000/XP)
+ for ( size_t i = 0, nRanges = maRowRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rScRange = maRowRanges[ i ];
+ if( rScRange.aStart.Col() != rScRange.aEnd.Col() )
+ rScRange.aEnd.SetCol( rScRange.aStart.Col() );
+ }
+ // col label ranges
+ FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
+}
+
+void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
+ const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab )
+{
+ for ( size_t i = 0, nPairs = xLabelRangesRef->size(); i < nPairs; ++i )
+ {
+ const ScRangePair & rRangePair = (*xLabelRangesRef)[i];
+ const ScRange& rScRange = rRangePair.GetRange( 0 );
+ if( rScRange.aStart.Tab() == nScTab )
+ rScRanges.push_back( rScRange );
+ }
+}
+
+void XclExpLabelranges::Save( XclExpStream& rStrm )
+{
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ XclRangeList aRowXclRanges, aColXclRanges;
+ rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
+ rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
+ if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
+ rStrm << aRowXclRanges << aColXclRanges;
+ rStrm.EndRecord();
+ }
+}
+
+// Conditional formatting ====================================================
+
+/** Represents a CF record that contains one condition of a conditional format. */
+class XclExpCFImpl : protected XclExpRoot
+{
+public:
+ explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
+
+ /** Writes the body of the CF record. */
+ void WriteBody( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
+ ScAddress maOrigin; /// Top left cell of the combined range
+ XclFontData maFontData; /// Font formatting attributes.
+ XclExpCellBorder maBorder; /// Border formatting attributes.
+ XclExpCellArea maArea; /// Pattern formatting attributes.
+ XclTokenArrayRef mxTokArr1; /// Formula for first condition.
+ XclTokenArrayRef mxTokArr2; /// Formula for second condition.
+ sal_uInt32 mnFontColorId; /// Font color ID.
+ sal_uInt8 mnType; /// Type of the condition (cell/formula).
+ sal_uInt8 mnOperator; /// Comparison operator for cell type.
+ sal_Int32 mnPriority; /// Priority of this entry; needed for oox export
+ bool mbFontUsed; /// true = Any font attribute used.
+ bool mbHeightUsed; /// true = Font height used.
+ bool mbWeightUsed; /// true = Font weight used.
+ bool mbColorUsed; /// true = Font color used.
+ bool mbUnderlUsed; /// true = Font underline type used.
+ bool mbItalicUsed; /// true = Font posture used.
+ bool mbStrikeUsed; /// true = Font strikeout used.
+ bool mbBorderUsed; /// true = Border attribute used.
+ bool mbPattUsed; /// true = Pattern attribute used.
+ bool mbFormula2;
+};
+
+XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
+ XclExpRoot( rRoot ),
+ mrFormatEntry( rFormatEntry ),
+ maOrigin( aOrigin ),
+ mnFontColorId( 0 ),
+ mnType( EXC_CF_TYPE_CELL ),
+ mnOperator( EXC_CF_CMP_NONE ),
+ mnPriority( nPriority ),
+ mbFontUsed( false ),
+ mbHeightUsed( false ),
+ mbWeightUsed( false ),
+ mbColorUsed( false ),
+ mbUnderlUsed( false ),
+ mbItalicUsed( false ),
+ mbStrikeUsed( false ),
+ mbBorderUsed( false ),
+ mbPattUsed( false ),
+ mbFormula2(false)
+{
+ // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
+ ScAddress aValidSrcPos = mrFormatEntry.GetValidSrcPos();
+ maOrigin.SetTab(aValidSrcPos.Tab());
+
+ /* Get formatting attributes here, and not in WriteBody(). This is needed to
+ correctly insert all colors into the palette. */
+
+ if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SfxStyleFamily::Para ) )
+ {
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+
+ // font
+ mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
+ mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
+ mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
+ mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
+ mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
+ mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
+ mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
+ if( mbFontUsed )
+ {
+ vcl::Font aFont;
+ model::ComplexColor aComplexColor;
+ ScPatternAttr::fillFontOnly(aFont, rItemSet);
+ ScPatternAttr::fillColor(aComplexColor, rItemSet, ScAutoFontColorMode::Raw);
+ maFontData.FillFromVclFont(aFont, aComplexColor);
+ mnFontColorId = GetPalette().InsertColor(maFontData.maComplexColor.getFinalColor(), EXC_COLOR_CELLTEXT);
+ }
+
+ // border
+ mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
+ if( mbBorderUsed )
+ maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
+
+ // pattern
+ mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
+ if( mbPattUsed )
+ maArea.FillFromItemSet( rItemSet, GetPalette(), true );
+ }
+
+ // *** mode and comparison operator ***
+
+ switch( rFormatEntry.GetOperation() )
+ {
+ case ScConditionMode::NONE:
+ mnType = EXC_CF_TYPE_NONE;
+ break;
+ case ScConditionMode::Between:
+ mnOperator = EXC_CF_CMP_BETWEEN;
+ mbFormula2 = true;
+ break;
+ case ScConditionMode::NotBetween:
+ mnOperator = EXC_CF_CMP_NOT_BETWEEN;
+ mbFormula2 = true;
+ break;
+ case ScConditionMode::Equal:
+ mnOperator = EXC_CF_CMP_EQUAL;
+ break;
+ case ScConditionMode::NotEqual:
+ mnOperator = EXC_CF_CMP_NOT_EQUAL;
+ break;
+ case ScConditionMode::Greater:
+ mnOperator = EXC_CF_CMP_GREATER;
+ break;
+ case ScConditionMode::Less:
+ mnOperator = EXC_CF_CMP_LESS;
+ break;
+ case ScConditionMode::EqGreater:
+ mnOperator = EXC_CF_CMP_GREATER_EQUAL;
+ break;
+ case ScConditionMode::EqLess:
+ mnOperator = EXC_CF_CMP_LESS_EQUAL;
+ break;
+ case ScConditionMode::Direct:
+ mnType = EXC_CF_TYPE_FMLA;
+ break;
+ default:
+ mnType = EXC_CF_TYPE_NONE;
+ OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
+ }
+}
+
+void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
+{
+
+ // *** formulas ***
+
+ XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
+
+ std::unique_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateFlatCopiedTokenArray( 0 ) );
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
+
+ if (mbFormula2)
+ {
+ xScTokArr = mrFormatEntry.CreateFlatCopiedTokenArray( 1 );
+ mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
+ }
+
+ // *** mode and comparison operator ***
+
+ rStrm << mnType << mnOperator;
+
+ // *** formula sizes ***
+
+ sal_uInt16 nFmlaSize1 = mxTokArr1 ? mxTokArr1->GetSize() : 0;
+ sal_uInt16 nFmlaSize2 = mxTokArr2 ? mxTokArr2->GetSize() : 0;
+ rStrm << nFmlaSize1 << nFmlaSize2;
+
+ // *** formatting blocks ***
+
+ if( mbFontUsed || mbBorderUsed || mbPattUsed )
+ {
+ sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
+
+ ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
+ ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
+ ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
+
+ // attributes used -> set flags to 0.
+ ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
+ ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
+
+ rStrm << nFlags << sal_uInt16( 0 );
+
+ if( mbFontUsed )
+ {
+ // font height, 0xFFFFFFFF indicates unused
+ sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
+ // font style: italic and strikeout
+ sal_uInt32 nStyle = 0;
+ ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
+ ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
+ // font color, 0xFFFFFFFF indicates unused
+ sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
+ // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
+ sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
+ ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
+ ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
+ // font used flag for underline -> 0 = used, 1 = default
+ sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
+
+ rStrm.WriteZeroBytesToRecord( 64 );
+ rStrm << nHeight
+ << nStyle
+ << maFontData.mnWeight
+ << EXC_FONTESC_NONE
+ << maFontData.mnUnderline;
+ rStrm.WriteZeroBytesToRecord( 3 );
+ rStrm << nColor
+ << sal_uInt32( 0 )
+ << nFontFlags1
+ << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
+ << nFontFlags3;
+ rStrm.WriteZeroBytesToRecord( 16 );
+ rStrm << sal_uInt16( 1 ); // must be 1
+ }
+
+ if( mbBorderUsed )
+ {
+ sal_uInt16 nLineStyle = 0;
+ sal_uInt32 nLineColor = 0;
+ maBorder.SetFinalColors( GetPalette() );
+ maBorder.FillToCF8( nLineStyle, nLineColor );
+ rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
+ }
+
+ if( mbPattUsed )
+ {
+ sal_uInt16 nPattern = 0, nColor = 0;
+ maArea.SetFinalColors( GetPalette() );
+ maArea.FillToCF8( nPattern, nColor );
+ rStrm << nPattern << nColor;
+ }
+ }
+ else
+ {
+ // no data blocks at all
+ rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
+ }
+
+ // *** formulas ***
+
+ if( mxTokArr1 )
+ mxTokArr1->WriteArray( rStrm );
+ if( mxTokArr2 )
+ mxTokArr2->WriteArray( rStrm );
+}
+
+namespace {
+
+const char* GetOperatorString(ScConditionMode eMode, bool& bFrmla2)
+{
+ const char *pRet = nullptr;
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ pRet = "equal";
+ break;
+ case ScConditionMode::Less:
+ pRet = "lessThan";
+ break;
+ case ScConditionMode::Greater:
+ pRet = "greaterThan";
+ break;
+ case ScConditionMode::EqLess:
+ pRet = "lessThanOrEqual";
+ break;
+ case ScConditionMode::EqGreater:
+ pRet = "greaterThanOrEqual";
+ break;
+ case ScConditionMode::NotEqual:
+ pRet = "notEqual";
+ break;
+ case ScConditionMode::Between:
+ bFrmla2 = true;
+ pRet = "between";
+ break;
+ case ScConditionMode::NotBetween:
+ bFrmla2 = true;
+ pRet = "notBetween";
+ break;
+ case ScConditionMode::Duplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::NotDuplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::BeginsWith:
+ pRet = "beginsWith";
+ break;
+ case ScConditionMode::EndsWith:
+ pRet = "endsWith";
+ break;
+ case ScConditionMode::ContainsText:
+ pRet = "containsText";
+ break;
+ case ScConditionMode::NotContainsText:
+ pRet = "notContains";
+ break;
+ case ScConditionMode::Direct:
+ break;
+ case ScConditionMode::NONE:
+ default:
+ break;
+ }
+ return pRet;
+}
+
+const char* GetTypeString(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Direct:
+ return "expression";
+ case ScConditionMode::Top10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::BottomPercent:
+ return "top10";
+ case ScConditionMode::AboveAverage:
+ case ScConditionMode::BelowAverage:
+ case ScConditionMode::AboveEqualAverage:
+ case ScConditionMode::BelowEqualAverage:
+ return "aboveAverage";
+ case ScConditionMode::NotDuplicate:
+ return "uniqueValues";
+ case ScConditionMode::Duplicate:
+ return "duplicateValues";
+ case ScConditionMode::Error:
+ return "containsErrors";
+ case ScConditionMode::NoError:
+ return "notContainsErrors";
+ case ScConditionMode::BeginsWith:
+ return "beginsWith";
+ case ScConditionMode::EndsWith:
+ return "endsWith";
+ case ScConditionMode::ContainsText:
+ return "containsText";
+ case ScConditionMode::NotContainsText:
+ return "notContainsText";
+ default:
+ return "cellIs";
+ }
+}
+
+bool IsTopBottomRule(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Top10:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::BottomPercent:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool IsTextRule(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool RequiresFormula(ScConditionMode eMode)
+{
+ if (IsTopBottomRule(eMode))
+ return false;
+ else if (IsTextRule(eMode))
+ return false;
+
+ switch (eMode)
+ {
+ case ScConditionMode::NoError:
+ case ScConditionMode::Error:
+ case ScConditionMode::Duplicate:
+ case ScConditionMode::NotDuplicate:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool RequiresFixedFormula(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::NoError:
+ case ScConditionMode::Error:
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
+{
+ OStringBuffer aBuffer;
+ XclXmlUtils::ToOString(aBuffer, rAddress);
+ OString aPos = aBuffer.makeStringAndClear();
+ switch (eMode)
+ {
+ case ScConditionMode::Error:
+ return OString("ISERROR(" + aPos + ")") ;
+ case ScConditionMode::NoError:
+ return OString("NOT(ISERROR(" + aPos + "))") ;
+ case ScConditionMode::BeginsWith:
+ return OString("LEFT(" + aPos + ",LEN(\"" + rText + "\"))=\"" + rText + "\"");
+ case ScConditionMode::EndsWith:
+ return OString("RIGHT(" + aPos +",LEN(\"" + rText + "\"))=\"" + rText + "\"");
+ case ScConditionMode::ContainsText:
+ return OString(OString::Concat("NOT(ISERROR(SEARCH(\"") + rText + "\"," + aPos + ")))");
+ case ScConditionMode::NotContainsText:
+ return OString(OString::Concat("ISERROR(SEARCH(\"") + rText + "\"," + aPos + "))");
+ default:
+ break;
+ }
+
+ return ""_ostr;
+}
+
+}
+
+void XclExpCFImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bFmla2 = false;
+ ScConditionMode eOperation = mrFormatEntry.GetOperation();
+ bool bAboveAverage = eOperation == ScConditionMode::AboveAverage ||
+ eOperation == ScConditionMode::AboveEqualAverage;
+ bool bEqualAverage = eOperation == ScConditionMode::AboveEqualAverage ||
+ eOperation == ScConditionMode::BelowEqualAverage;
+ bool bBottom = eOperation == ScConditionMode::Bottom10
+ || eOperation == ScConditionMode::BottomPercent;
+ bool bPercent = eOperation == ScConditionMode::TopPercent ||
+ eOperation == ScConditionMode::BottomPercent;
+ OUString aRank("0");
+ if(IsTopBottomRule(eOperation))
+ {
+ // position and formula grammar are not important
+ // we only store a number there
+ aRank = mrFormatEntry.GetExpression(ScAddress(0,0,0), 0);
+ }
+ OString aText;
+ if(IsTextRule(eOperation))
+ {
+ // we need to write the text without quotes
+ // we have to actually get the string from
+ // the token array for that
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
+ if(pTokenArray->GetLen())
+ {
+ formula::StackVar eType = pTokenArray->FirstToken()->GetType();
+ switch (eType)
+ {
+ case formula::svDouble:
+ {
+ aText = OString::number(pTokenArray->FirstToken()->GetDouble());
+ break;
+ }
+ default:
+ {
+ aText = pTokenArray->FirstToken()->GetString().getString().toUtf8();
+ break;
+ }
+ }
+ }
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, GetTypeString( mrFormatEntry.GetOperation() ),
+ XML_priority, OString::number(mnPriority + 1),
+ XML_operator, GetOperatorString( mrFormatEntry.GetOperation(), bFmla2 ),
+ XML_aboveAverage, ToPsz10(bAboveAverage),
+ XML_equalAverage, ToPsz10(bEqualAverage),
+ XML_bottom, ToPsz10(bBottom),
+ XML_percent, ToPsz10(bPercent),
+ XML_rank, aRank,
+ XML_text, aText,
+ XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyle())) );
+
+ if (RequiresFixedFormula(eOperation))
+ {
+ rWorksheet->startElement(XML_formula);
+ OString aFormula = GetFixedFormula(eOperation, maOrigin, aText);
+ rWorksheet->writeEscaped(aFormula.getStr());
+ rWorksheet->endElement( XML_formula );
+ }
+ else if(RequiresFormula(eOperation))
+ {
+ rWorksheet->startElement(XML_formula);
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
+ rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
+ pTokenArray.get()));
+ rWorksheet->endElement( XML_formula );
+ if (bFmla2)
+ {
+ rWorksheet->startElement(XML_formula);
+ std::unique_ptr<ScTokenArray> pTokenArray2(mrFormatEntry.CreateFlatCopiedTokenArray(1));
+ rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
+ pTokenArray2.get()));
+ rWorksheet->endElement( XML_formula );
+ }
+ }
+ // OOXTODO: XML_extLst
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
+ XclExpRecord( EXC_ID_CF ),
+ XclExpRoot( rRoot ),
+ mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, nPriority, aOrigin ) )
+{
+}
+
+XclExpCF::~XclExpCF()
+{
+}
+
+void XclExpCF::WriteBody( XclExpStream& rStrm )
+{
+ mxImpl->WriteBody( rStrm );
+}
+
+void XclExpCF::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+XclExpDateFormat::XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority ):
+ XclExpRecord( EXC_ID_CF ),
+ XclExpRoot( rRoot ),
+ mrFormatEntry(rFormatEntry),
+ mnPriority(nPriority)
+{
+}
+
+XclExpDateFormat::~XclExpDateFormat()
+{
+}
+
+namespace {
+
+const char* getTimePeriodString( condformat::ScCondFormatDateType eType )
+{
+ switch(eType)
+ {
+ case condformat::TODAY:
+ return "today";
+ case condformat::YESTERDAY:
+ return "yesterday";
+ case condformat::TOMORROW:
+ return "yesterday";
+ case condformat::THISWEEK:
+ return "thisWeek";
+ case condformat::LASTWEEK:
+ return "lastWeek";
+ case condformat::NEXTWEEK:
+ return "nextWeek";
+ case condformat::THISMONTH:
+ return "thisMonth";
+ case condformat::LASTMONTH:
+ return "lastMonth";
+ case condformat::NEXTMONTH:
+ return "nextMonth";
+ case condformat::LAST7DAYS:
+ return "last7Days";
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+}
+
+void XclExpDateFormat::SaveXml( XclExpXmlStream& rStrm )
+{
+ // only write the supported entries into OOXML
+ const char* sTimePeriod = getTimePeriodString(mrFormatEntry.GetDateType());
+ if(!sTimePeriod)
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "timePeriod",
+ XML_priority, OString::number(mnPriority + 1),
+ XML_timePeriod, sTimePeriod,
+ XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyleName())) );
+ rWorksheet->endElement( XML_cfRule);
+}
+
+XclExpCfvo::XclExpCfvo(const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rAddr, bool bFirst):
+ XclExpRoot( rRoot ),
+ mrEntry(rEntry),
+ maSrcPos(rAddr),
+ mbFirst(bFirst)
+{
+}
+
+namespace {
+
+OString getColorScaleType( const ScColorScaleEntry& rEntry, bool bFirst )
+{
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_MIN:
+ return "min"_ostr;
+ case COLORSCALE_MAX:
+ return "max"_ostr;
+ case COLORSCALE_PERCENT:
+ return "percent"_ostr;
+ case COLORSCALE_FORMULA:
+ return "formula"_ostr;
+ case COLORSCALE_AUTO:
+ if(bFirst)
+ return "min"_ostr;
+ else
+ return "max"_ostr;
+ case COLORSCALE_PERCENTILE:
+ return "percentile"_ostr;
+ default:
+ break;
+ }
+
+ return "num"_ostr;
+}
+
+}
+
+void XclExpCfvo::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ OString aValue;
+ if(mrEntry.GetType() == COLORSCALE_FORMULA)
+ {
+ OUString aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos,
+ mrEntry.GetFormula());
+ aValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ {
+ aValue = OString::number( mrEntry.GetValue() );
+ }
+
+ rWorksheet->startElement( XML_cfvo,
+ XML_type, getColorScaleType(mrEntry, mbFirst),
+ XML_val, aValue );
+
+ rWorksheet->endElement( XML_cfvo );
+}
+
+XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor ):
+ XclExpRoot( rRoot ),
+ mrColor( rColor )
+{
+}
+
+XclExpColScaleCol::~XclExpColScaleCol()
+{
+}
+
+void XclExpColScaleCol::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(XML_color, XML_rgb, XclXmlUtils::ToOString(mrColor));
+
+ rWorksheet->endElement( XML_color );
+}
+
+namespace {
+
+OString createHexStringFromDigit(sal_uInt8 nDigit)
+{
+ OString aString = OString::number( nDigit, 16 );
+ if(aString.getLength() == 1)
+ aString += OString::number(0);
+ return aString;
+}
+
+OString createGuidStringFromInt(sal_uInt8 nGuid[16])
+{
+ OStringBuffer aBuffer("{");
+ for(size_t i = 0; i < 16; ++i)
+ {
+ aBuffer.append(createHexStringFromDigit(nGuid[i]));
+ if(i == 3|| i == 5 || i == 7 || i == 9 )
+ aBuffer.append('-');
+ }
+ aBuffer.append('}');
+ OString aString = aBuffer.makeStringAndClear();
+ return aString.toAsciiUpperCase();
+}
+
+OString generateGUIDString()
+{
+ sal_uInt8 nGuid[16];
+ rtl_createUuid(nGuid, nullptr, true);
+ return createGuidStringFromInt(nGuid);
+}
+
+}
+
+XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex ) :
+ XclExpRecord( EXC_ID_CONDFMT ),
+ XclExpRoot( rRoot )
+{
+ const ScRangeList& aScRanges = rCondFormat.GetRange();
+ GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
+ if( maXclRanges.empty() )
+ return;
+
+ std::vector<XclExpExtCondFormatData> aExtEntries;
+ ScAddress aOrigin = aScRanges.Combine().aStart;
+ for( size_t nIndex = 0, nCount = rCondFormat.size(); nIndex < nCount; ++nIndex )
+ if( const ScFormatEntry* pFormatEntry = rCondFormat.GetEntry( nIndex ) )
+ {
+ if(pFormatEntry->GetType() == ScFormatEntry::Type::Condition)
+ maCFList.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry&>(*pFormatEntry), ++rIndex, aOrigin ) );
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*pFormatEntry);
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = ++rIndex;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rFormat;
+ aExtEntries.push_back(aExtEntry);
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ maCFList.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat&>(*pFormatEntry), ++rIndex ) );
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
+ {
+ const ScDataBarFormat& rFormat = static_cast<const ScDataBarFormat&>(*pFormatEntry);
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = -1;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rFormat;
+ aExtEntries.push_back(aExtEntry);
+
+ maCFList.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat, ++rIndex, aExtEntry.aGUID));
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
+ {
+ // don't export iconSet entries that are not in OOXML
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
+ bool bNeedsExt = false;
+ switch (rIconSet.GetIconSetData()->eIconSetType)
+ {
+ case IconSet_3Smilies:
+ case IconSet_3ColorSmilies:
+ case IconSet_3Stars:
+ case IconSet_3Triangles:
+ case IconSet_5Boxes:
+ {
+ bNeedsExt = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ bNeedsExt |= rIconSet.GetIconSetData()->mbCustom;
+
+ if (bNeedsExt)
+ {
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = ++rIndex;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rIconSet;
+ aExtEntries.push_back(aExtEntry);
+ }
+ else
+ maCFList.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet, ++rIndex ) );
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
+ maCFList.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry&>(*pFormatEntry), ++rIndex ) );
+ }
+ aScRanges.Format( msSeqRef, ScRefFlags::VALID, GetDoc(), formula::FormulaGrammar::CONV_XL_OOX, ' ', true );
+
+ if(!aExtEntries.empty() && xExtLst)
+ {
+ XclExpExt* pParent = xExtLst->GetItem( XclExpExtDataBarType );
+ if( !pParent )
+ {
+ xExtLst->AddRecord( new XclExpExtCondFormat( *xExtLst ) );
+ pParent = xExtLst->GetItem( XclExpExtDataBarType );
+ }
+ static_cast<XclExpExtCondFormat*>(xExtLst->GetItem( XclExpExtDataBarType ))->AddRecord(
+ new XclExpExtConditionalFormatting( *pParent, aExtEntries, aScRanges));
+ }
+}
+
+XclExpCondfmt::~XclExpCondfmt()
+{
+}
+
+bool XclExpCondfmt::IsValidForBinary() const
+{
+ // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
+ // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
+
+ SAL_WARN_IF( maCFList.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
+
+ return !maCFList.IsEmpty() && maCFList.GetSize() <= 3 && !maXclRanges.empty();
+}
+
+bool XclExpCondfmt::IsValidForXml() const
+{
+ return !maCFList.IsEmpty() && !maXclRanges.empty();
+}
+
+void XclExpCondfmt::Save( XclExpStream& rStrm )
+{
+ if( IsValidForBinary() )
+ {
+ XclExpRecord::Save( rStrm );
+ maCFList.Save( rStrm );
+ }
+}
+
+void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
+ OSL_ENSURE( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
+
+ rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
+ << sal_uInt16( 1 )
+ << maXclRanges.GetEnclosingRange()
+ << maXclRanges;
+}
+
+void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !IsValidForXml() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_conditionalFormatting,
+ XML_sqref, msSeqRef
+ // OOXTODO: XML_pivot
+ );
+
+ maCFList.SaveXml( rStrm );
+
+ rWorksheet->endElement( XML_conditionalFormatting );
+}
+
+XclExpColorScale::XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority ):
+ XclExpRoot( rRoot ),
+ mnPriority( nPriority )
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ for(const auto& rxColorScaleEntry : rFormat)
+ {
+ // exact position is not important, we allow only absolute refs
+
+ XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry, aAddr ) );
+ maCfvoList.AppendRecord( xCfvo );
+ XclExpColScaleColList::RecordRefType xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry->GetColor() ) );
+ maColList.AppendRecord( xClo );
+ }
+}
+
+void XclExpColorScale::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "colorScale",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ rWorksheet->startElement(XML_colorScale);
+
+ maCfvoList.SaveXml(rStrm);
+ maColList.SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_colorScale );
+
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpDataBar::XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, OString aGUID):
+ XclExpRoot( rRoot ),
+ mrFormat( rFormat ),
+ mnPriority( nPriority ),
+ maGUID(std::move(aGUID))
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ // exact position is not important, we allow only absolute refs
+ mpCfvoLowerLimit.reset(
+ new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpLowerLimit, aAddr, true));
+ mpCfvoUpperLimit.reset(
+ new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpUpperLimit, aAddr, false));
+
+ mpCol.reset( new XclExpColScaleCol( GetRoot(), mrFormat.GetDataBarData()->maPositiveColor ) );
+}
+
+void XclExpDataBar::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "dataBar",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ rWorksheet->startElement( XML_dataBar,
+ XML_showValue, ToPsz10(!mrFormat.GetDataBarData()->mbOnlyBar),
+ XML_minLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMinLength)),
+ XML_maxLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMaxLength)) );
+
+ mpCfvoLowerLimit->SaveXml(rStrm);
+ mpCfvoUpperLimit->SaveXml(rStrm);
+ mpCol->SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_dataBar );
+
+ // extLst entries for Excel 2010 and 2013
+ rWorksheet->startElement(XML_extLst);
+ rWorksheet->startElement(XML_ext,
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ XML_uri, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
+
+ rWorksheet->startElementNS( XML_x14, XML_id );
+ rWorksheet->write(maGUID);
+ rWorksheet->endElementNS( XML_x14, XML_id );
+
+ rWorksheet->endElement( XML_ext );
+ rWorksheet->endElement( XML_extLst );
+
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpIconSet::XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority ):
+ XclExpRoot( rRoot ),
+ mrFormat( rFormat ),
+ mnPriority( nPriority )
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ for (auto const& itr : rFormat)
+ {
+ // exact position is not important, we allow only absolute refs
+
+ XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *itr, aAddr ) );
+ maCfvoList.AppendRecord( xCfvo );
+ }
+}
+
+void XclExpIconSet::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "iconSet",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ const char* pIconSetName = ScIconSetFormat::getIconSetName(mrFormat.GetIconSetData()->eIconSetType);
+ rWorksheet->startElement( XML_iconSet,
+ XML_iconSet, pIconSetName,
+ XML_showValue, sax_fastparser::UseIf("0", !mrFormat.GetIconSetData()->mbShowValue),
+ XML_reverse, sax_fastparser::UseIf("1", mrFormat.GetIconSetData()->mbReverse));
+
+ maCfvoList.SaveXml( rStrm );
+
+ rWorksheet->endElement( XML_iconSet );
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst ) :
+ XclExpRoot( rRoot )
+{
+ if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList(GetCurrScTab()) )
+ {
+ sal_Int32 nIndex = 0;
+ for( const auto& rxCondFmt : *pCondFmtList)
+ {
+ XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt, xExtLst, nIndex ));
+ if( xCondfmtRec->IsValidForXml() )
+ maCondfmtList.AppendRecord( xCondfmtRec );
+ }
+ }
+}
+
+void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
+{
+ maCondfmtList.Save( rStrm );
+}
+
+void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ maCondfmtList.SaveXml( rStrm );
+}
+
+// Validation =================================================================
+
+namespace {
+
+/** Writes a formula for the DV record. */
+void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
+{
+ sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
+ rStrm << nFmlaSize << sal_uInt16( 0 );
+ if( pXclTokArr )
+ pXclTokArr->WriteArray( rStrm );
+}
+
+/** Writes a formula for the DV record, based on a single string. */
+void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
+{
+ // fake a formula with a single tStr token
+ rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
+ << sal_uInt16( 0 )
+ << EXC_TOKID_STR
+ << rString;
+}
+
+const char* lcl_GetValidationType( sal_uInt32 nFlags )
+{
+ switch( nFlags & EXC_DV_MODE_MASK )
+ {
+ case EXC_DV_MODE_ANY: return "none";
+ case EXC_DV_MODE_WHOLE: return "whole";
+ case EXC_DV_MODE_DECIMAL: return "decimal";
+ case EXC_DV_MODE_LIST: return "list";
+ case EXC_DV_MODE_DATE: return "date";
+ case EXC_DV_MODE_TIME: return "time";
+ case EXC_DV_MODE_TEXTLEN: return "textLength";
+ case EXC_DV_MODE_CUSTOM: return "custom";
+ }
+ return nullptr;
+}
+
+const char* lcl_GetOperatorType( sal_uInt32 nFlags )
+{
+ switch( nFlags & EXC_DV_COND_MASK )
+ {
+ case EXC_DV_COND_BETWEEN: return "between";
+ case EXC_DV_COND_NOTBETWEEN: return "notBetween";
+ case EXC_DV_COND_EQUAL: return "equal";
+ case EXC_DV_COND_NOTEQUAL: return "notEqual";
+ case EXC_DV_COND_GREATER: return "greaterThan";
+ case EXC_DV_COND_LESS: return "lessThan";
+ case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
+ case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
+ }
+ return nullptr;
+}
+
+const char* lcl_GetErrorType( sal_uInt32 nFlags )
+{
+ switch (nFlags & EXC_DV_ERROR_MASK)
+ {
+ case EXC_DV_ERROR_STOP: return "stop";
+ case EXC_DV_ERROR_WARNING: return "warning";
+ case EXC_DV_ERROR_INFO: return "information";
+ }
+ return nullptr;
+}
+
+void lcl_SetValidationText(const OUString& rText, XclExpString& rValidationText)
+{
+ if ( !rText.isEmpty() )
+ {
+ // maximum length allowed in Excel is 255 characters
+ if ( rText.getLength() > 255 )
+ {
+ OUStringBuffer aBuf( rText );
+ rValidationText.Assign(
+ comphelper::string::truncateToLength(aBuf, 255).makeStringAndClear() );
+ }
+ else
+ rValidationText.Assign( rText );
+ }
+ else
+ rValidationText.Assign( '\0' );
+}
+
+} // namespace
+
+XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uInt32 nScHandle ) :
+ XclExpRecord( EXC_ID_DV ),
+ XclExpRoot( rRoot ),
+ mnFlags( 0 ),
+ mnScHandle( nScHandle )
+{
+ if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
+ {
+ // prompt box - empty string represented by single NUL character
+ OUString aTitle, aText;
+ bool bShowPrompt = pValData->GetInput( aTitle, aText );
+ lcl_SetValidationText(aTitle, maPromptTitle);
+ lcl_SetValidationText(aText, maPromptText);
+
+ // error box - empty string represented by single NUL character
+ ScValidErrorStyle eScErrorStyle;
+ bool bShowError = pValData->GetErrMsg( aTitle, aText, eScErrorStyle );
+ lcl_SetValidationText(aTitle, maErrorTitle);
+ lcl_SetValidationText(aText, maErrorText);
+
+ // flags
+ switch( pValData->GetDataMode() )
+ {
+ case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
+ case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
+ case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
+ case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
+ case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
+ case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
+ case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
+ case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
+ }
+
+ switch( pValData->GetOperation() )
+ {
+ case ScConditionMode::NONE:
+ case ScConditionMode::Equal: mnFlags |= EXC_DV_COND_EQUAL; break;
+ case ScConditionMode::Less: mnFlags |= EXC_DV_COND_LESS; break;
+ case ScConditionMode::Greater: mnFlags |= EXC_DV_COND_GREATER; break;
+ case ScConditionMode::EqLess: mnFlags |= EXC_DV_COND_EQLESS; break;
+ case ScConditionMode::EqGreater: mnFlags |= EXC_DV_COND_EQGREATER; break;
+ case ScConditionMode::NotEqual: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
+ case ScConditionMode::Between: mnFlags |= EXC_DV_COND_BETWEEN; break;
+ case ScConditionMode::NotBetween: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
+ }
+ switch( eScErrorStyle )
+ {
+ case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
+ case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
+ case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
+ case SC_VALERR_MACRO:
+ // set INFO for validity with macro call, delete title
+ mnFlags |= EXC_DV_ERROR_INFO;
+ maErrorTitle.Assign( '\0' ); // contains macro name
+ break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
+ }
+ ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
+ ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE );
+ ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
+ ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
+
+ // formulas
+ XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
+
+ // first formula
+ std::unique_ptr< ScTokenArray > xScTokArr = pValData->CreateFlatCopiedTokenArray( 0 );
+ if (xScTokArr)
+ {
+ if( pValData->GetDataMode() == SC_VALID_LIST )
+ {
+ OUString aString;
+ if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
+ {
+ bool bList = false;
+ OUStringBuffer sListBuf;
+ OUStringBuffer sFormulaBuf("\"");
+ /* Formula is a list of string tokens -> build the Excel string.
+ Data validity is BIFF8 only (important for the XclExpString object).
+ Excel uses the NUL character as string list separator. */
+ mxString1.reset( new XclExpString( XclStrFlags::EightBitLength ) );
+ if (!aString.isEmpty())
+ {
+ sal_Int32 nStringIx = 0;
+ for(;;)
+ {
+ const std::u16string_view aToken( o3tl::getToken(aString, 0, '\n', nStringIx ) );
+ if (aToken.find(',') != std::u16string_view::npos)
+ {
+ sListBuf.append(OUString::Concat("\"") + aToken + "\"");
+ bList = true;
+ }
+ else
+ sListBuf.append(aToken);
+ mxString1->Append( aToken );
+ sFormulaBuf.append( aToken );
+ if (nStringIx<0)
+ break;
+ sal_Unicode cUnicodeChar = 0;
+ mxString1->Append( std::u16string_view(&cUnicodeChar, 1) );
+ sFormulaBuf.append( ',' );
+ sListBuf.append( ',' );
+ }
+ }
+ ::set_flag( mnFlags, EXC_DV_STRINGLIST );
+
+ // maximum length allowed in Excel is 255 characters, and don't end with an empty token
+ // It should be 8192 but Excel doesn't accept it for unknown reason
+ // See also https://bugs.documentfoundation.org/show_bug.cgi?id=137167#c2 for more details
+ sal_uInt32 nLen = sFormulaBuf.getLength();
+ if( nLen > 256 ) // 255 + beginning quote mark
+ {
+ nLen = 256;
+ if( sFormulaBuf[nLen - 1] == ',' )
+ --nLen;
+ sFormulaBuf.truncate(nLen);
+ }
+
+ sFormulaBuf.append( '"' );
+ msFormula1 = sFormulaBuf.makeStringAndClear();
+ if (bList)
+ msList = sListBuf.makeStringAndClear();
+ else
+ sListBuf.remove(0, sListBuf.getLength());
+ }
+ else
+ {
+ /* All other formulas in validation are stored like conditional
+ formatting formulas (with tRefN/tAreaN tokens as value or
+ array class). But NOT the cell references and defined names
+ in list validation - they are stored as reference class
+ tokens... Example:
+ 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
+ 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
+ Formula compiler supports this by offering two different functions
+ CreateDataValFormula() and CreateListValFormula(). */
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
+ else
+ msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+ else
+ {
+ // no list validation -> convert the formula
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
+ else
+ msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+
+ // second formula
+ xScTokArr = pValData->CreateFlatCopiedTokenArray( 1 );
+ if (xScTokArr)
+ {
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
+ else
+ msFormula2 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
+ mnScHandle = SAL_MAX_UINT32;
+ }
+}
+
+XclExpDV::~XclExpDV()
+{
+}
+
+void XclExpDV::InsertCellRange( const ScRange& rRange )
+{
+ maScRanges.Join( rRange );
+}
+
+bool XclExpDV::Finalize()
+{
+ GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
+ return (mnScHandle != SAL_MAX_UINT32) && !maXclRanges.empty();
+}
+
+void XclExpDV::WriteBody( XclExpStream& rStrm )
+{
+ // flags and strings
+ rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
+ // condition formulas
+ if( mxString1 )
+ lclWriteDvFormula( rStrm, *mxString1 );
+ else
+ lclWriteDvFormula( rStrm, mxTokArr1.get() );
+ lclWriteDvFormula( rStrm, mxTokArr2.get() );
+ // cell ranges
+ rStrm << maXclRanges;
+}
+
+void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_dataValidation,
+ XML_allowBlank, ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
+ XML_error, XESTRING_TO_PSZ( maErrorText ),
+ XML_errorStyle, lcl_GetErrorType(mnFlags),
+ XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
+ // OOXTODO: XML_imeMode,
+ XML_operator, lcl_GetOperatorType( mnFlags ),
+ XML_prompt, XESTRING_TO_PSZ( maPromptText ),
+ XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
+ // showDropDown should have been showNoDropDown - check oox/xlsx import for details
+ XML_showDropDown, ToPsz( ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
+ XML_showErrorMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
+ XML_showInputMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScRanges),
+ XML_type, lcl_GetValidationType(mnFlags) );
+ if (!msList.isEmpty())
+ {
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_x12ac),rStrm.getNamespaceURL(OOX_NS(x12ac)),
+ FSNS(XML_xmlns, XML_mc),rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x12ac");
+ rWorksheet->startElement(FSNS(XML_x12ac, XML_list));
+ rWorksheet->writeEscaped(msList);
+ rWorksheet->endElement(FSNS(XML_x12ac, XML_list));
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Fallback));
+ rWorksheet->startElement(XML_formula1);
+ rWorksheet->writeEscaped(msFormula1);
+ rWorksheet->endElement(XML_formula1);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Fallback));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+ }
+ if (msList.isEmpty() && !msFormula1.isEmpty())
+ {
+ rWorksheet->startElement(XML_formula1);
+ rWorksheet->writeEscaped( msFormula1 );
+ rWorksheet->endElement( XML_formula1 );
+ }
+ if( !msFormula2.isEmpty() )
+ {
+ rWorksheet->startElement(XML_formula2);
+ rWorksheet->writeEscaped( msFormula2 );
+ rWorksheet->endElement( XML_formula2 );
+ }
+ rWorksheet->endElement( XML_dataValidation );
+}
+
+XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_DVAL, 18 ),
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpDval::~XclExpDval()
+{
+}
+
+void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uInt32 nScHandle )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
+ rDVRec.InsertCellRange( rRange );
+ }
+}
+
+void XclExpDval::Save( XclExpStream& rStrm )
+{
+ // check all records
+ size_t nPos = maDVList.GetSize();
+ while( nPos )
+ {
+ --nPos; // backwards to keep nPos valid
+ XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
+ if( !xDVRec->Finalize() )
+ maDVList.RemoveRecord( nPos );
+ }
+
+ // write the DVAL and the DV's
+ if( !maDVList.IsEmpty() )
+ {
+ XclExpRecord::Save( rStrm );
+ maDVList.Save( rStrm );
+ }
+}
+
+void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maDVList.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_dataValidations,
+ XML_count, OString::number(maDVList.GetSize())
+ // OOXTODO: XML_disablePrompts,
+ // OOXTODO: XML_xWindow,
+ // OOXTODO: XML_yWindow
+ );
+ maDVList.SaveXml( rStrm );
+ rWorksheet->endElement( XML_dataValidations );
+}
+
+XclExpDV& XclExpDval::SearchOrCreateDv( sal_uInt32 nScHandle )
+{
+ // test last found record
+ if( mxLastFoundDV && (mxLastFoundDV->GetScHandle() == nScHandle) )
+ return *mxLastFoundDV;
+
+ // binary search
+ size_t nCurrPos = 0;
+ if( !maDVList.IsEmpty() )
+ {
+ size_t nFirstPos = 0;
+ size_t nLastPos = maDVList.GetSize() - 1;
+ bool bLoop = true;
+ sal_uInt32 nCurrScHandle = ::std::numeric_limits< sal_uInt32 >::max();
+ while( (nFirstPos <= nLastPos) && bLoop )
+ {
+ nCurrPos = (nFirstPos + nLastPos) / 2;
+ mxLastFoundDV = maDVList.GetRecord( nCurrPos );
+ nCurrScHandle = mxLastFoundDV->GetScHandle();
+ if( nCurrScHandle == nScHandle )
+ bLoop = false;
+ else if( nCurrScHandle < nScHandle )
+ nFirstPos = nCurrPos + 1;
+ else if( nCurrPos )
+ nLastPos = nCurrPos - 1;
+ else // special case for nLastPos = -1
+ bLoop = false;
+ }
+ if( nCurrScHandle == nScHandle )
+ return *mxLastFoundDV;
+ else if( nCurrScHandle < nScHandle )
+ ++nCurrPos;
+ }
+
+ // create new DV record
+ mxLastFoundDV = new XclExpDV( *this, nScHandle );
+ maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
+ return *mxLastFoundDV;
+}
+
+void XclExpDval::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 10 );
+ rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
+}
+
+// Web Queries ================================================================
+
+XclExpWebQuery::XclExpWebQuery(
+ const OUString& rRangeName,
+ const OUString& rUrl,
+ std::u16string_view rSource,
+ sal_Int32 nRefrSecs ) :
+ maDestRange( rRangeName ),
+ maUrl( rUrl ),
+ // refresh delay time: seconds -> minutes
+ mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59) / 60 ) ),
+ mbEntireDoc( false )
+{
+ // comma separated list of HTML table names or indexes
+ OUString aNewTables;
+ OUString aAppendTable;
+ bool bExitLoop = false;
+ if (!rSource.empty())
+ {
+ sal_Int32 nStringIx = 0;
+ do
+ {
+ OUString aToken( o3tl::getToken(rSource, 0, ';', nStringIx ) );
+ mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
+ bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
+ if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
+ aNewTables = ScGlobal::addToken( aNewTables, aAppendTable, ',' );
+ }
+ while (nStringIx>0 && !bExitLoop);
+ }
+
+ if( !bExitLoop ) // neither HTML_all nor HTML_tables found
+ {
+ if( !aNewTables.isEmpty() )
+ mxQryTables.reset( new XclExpString( aNewTables ) );
+ else
+ mbEntireDoc = true;
+ }
+}
+
+XclExpWebQuery::~XclExpWebQuery()
+{
+}
+
+void XclExpWebQuery::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !mbEntireDoc || !mxQryTables, "XclExpWebQuery::Save - illegal mode" );
+ sal_uInt16 nFlags;
+
+ // QSI record
+ rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
+ rStrm << EXC_QSI_DEFAULTFLAGS
+ << sal_uInt16( 0x0010 )
+ << sal_uInt16( 0x0012 )
+ << sal_uInt32( 0x00000000 )
+ << maDestRange;
+ rStrm.EndRecord();
+
+ // PARAMQRY record
+ nFlags = 0;
+ ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
+ ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
+ ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
+ rStrm.StartRecord( EXC_ID_PQRY, 12 );
+ rStrm << nFlags
+ << sal_uInt16( 0x0000 )
+ << sal_uInt16( 0x0001 );
+ rStrm.WriteZeroBytes( 6 );
+ rStrm.EndRecord();
+
+ // WQSTRING record
+ rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
+ rStrm << maUrl;
+ rStrm.EndRecord();
+
+ // unknown record 0x0802
+ rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
+ rStrm << EXC_ID_0802; // repeated record id ?!?
+ rStrm.WriteZeroBytes( 6 );
+ rStrm << sal_uInt16( 0x0003 )
+ << sal_uInt32( 0x00000000 )
+ << sal_uInt16( 0x0010 )
+ << maDestRange;
+ rStrm.EndRecord();
+
+ // WEBQRYSETTINGS record
+ nFlags = mxQryTables ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
+ rStrm.StartRecord( EXC_ID_WQSETT, 28 );
+ rStrm << EXC_ID_WQSETT // repeated record id ?!?
+ << sal_uInt16( 0x0000 )
+ << sal_uInt16( 0x0004 )
+ << sal_uInt16( 0x0000 )
+ << EXC_WQSETT_DEFAULTFLAGS
+ << nFlags;
+ rStrm.WriteZeroBytes( 10 );
+ rStrm << mnRefresh // refresh delay in minutes
+ << EXC_WQSETT_FORMATFULL
+ << sal_uInt16( 0x0000 );
+ rStrm.EndRecord();
+
+ // WEBQRYTABLES record
+ if( mxQryTables )
+ {
+ rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
+ rStrm << EXC_ID_WQTABLES // repeated record id ?!?
+ << sal_uInt16( 0x0000 )
+ << *mxQryTables; // comma separated list of source tables
+ rStrm.EndRecord();
+ }
+}
+
+XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
+{
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ScDocShell* pShell = rRoot.GetDocShell();
+ if( !pShell ) return;
+ ScfPropertySet aModelProp( pShell->GetModel() );
+ if( !aModelProp.Is() ) return;
+
+ Reference< XAreaLinks > xAreaLinks;
+ aModelProp.GetProperty( xAreaLinks, SC_UNO_AREALINKS );
+ if( !xAreaLinks.is() ) return;
+
+ for( sal_Int32 nIndex = 0, nCount = xAreaLinks->getCount(); nIndex < nCount; ++nIndex )
+ {
+ Reference< XAreaLink > xAreaLink( xAreaLinks->getByIndex( nIndex ), UNO_QUERY );
+ if( xAreaLink.is() )
+ {
+ CellRangeAddress aDestRange( xAreaLink->getDestArea() );
+ if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
+ {
+ ScfPropertySet aLinkProp( xAreaLink );
+ OUString aFilter;
+ if( aLinkProp.GetProperty( aFilter, SC_UNONAME_FILTER ) &&
+ (aFilter == EXC_WEBQRY_FILTER) )
+ {
+ // get properties
+ OUString /*aFilterOpt,*/ aUrl;
+ sal_Int32 nRefresh = 0;
+
+// aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
+ aLinkProp.GetProperty( aUrl, SC_UNONAME_LINKURL );
+ aLinkProp.GetProperty( nRefresh, SC_UNONAME_REFDELAY );
+
+ OUString aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
+ INetURLObject aUrlObj( aAbsDoc );
+ OUString aWebQueryUrl( aUrlObj.getFSysPath( FSysStyle::Dos ) );
+ if( aWebQueryUrl.isEmpty() )
+ aWebQueryUrl = aAbsDoc;
+
+ // find range or create a new range
+ OUString aRangeName;
+ ScRange aScDestRange;
+ ScUnoConversion::FillScRange( aScDestRange, aDestRange );
+ if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().findByRange( aScDestRange ) )
+ {
+ aRangeName = pRangeData->GetName();
+ }
+ else
+ {
+ XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
+ XclExpNameManager& rNameMgr = rRoot.GetNameManager();
+
+ // create a new unique defined name containing the range
+ XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
+ sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
+ aRangeName = rNameMgr.GetOrigName( nNameIdx );
+ }
+
+ // create and store the web query record
+ if( !aRangeName.isEmpty() )
+ AppendNewRecord( new XclExpWebQuery(
+ aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xedbdata.cxx b/sc/source/filter/excel/xedbdata.cxx
new file mode 100644
index 0000000000..5bea87e72b
--- /dev/null
+++ b/sc/source/filter/excel/xedbdata.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <xedbdata.hxx>
+#include <excrecds.hxx>
+#include <dbdata.hxx>
+#include <document.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace oox;
+
+namespace {
+
+/** (So far) dummy implementation of table export for BIFF5/BIFF7. */
+class XclExpTablesImpl5 : public XclExpTables
+{
+public:
+ explicit XclExpTablesImpl5( const XclExpRoot& rRoot );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/** Implementation of table export for OOXML, so far dummy for BIFF8. */
+class XclExpTablesImpl8 : public XclExpTables
+{
+public:
+ explicit XclExpTablesImpl8( const XclExpRoot& rRoot );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+}
+
+XclExpTablesImpl5::XclExpTablesImpl5( const XclExpRoot& rRoot ) :
+ XclExpTables( rRoot )
+{
+}
+
+void XclExpTablesImpl5::Save( XclExpStream& /*rStrm*/ )
+{
+ // not implemented
+}
+
+void XclExpTablesImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+ // not applicable
+}
+
+
+XclExpTablesImpl8::XclExpTablesImpl8( const XclExpRoot& rRoot ) :
+ XclExpTables( rRoot )
+{
+}
+
+void XclExpTablesImpl8::Save( XclExpStream& /*rStrm*/ )
+{
+ // not implemented
+}
+
+void XclExpTablesImpl8::SaveXml( XclExpXmlStream& rStrm )
+{
+
+ sax_fastparser::FSHelperPtr& pWorksheetStrm = rStrm.GetCurrentStream();
+ pWorksheetStrm->startElement(XML_tableParts);
+ for (auto const& it : maTables)
+ {
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pTableStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/tables/", "table", it.mnTableId),
+ XclXmlUtils::GetStreamName("../tables/", "table", it.mnTableId),
+ pWorksheetStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("table"),
+ CREATE_OFFICEDOC_RELATION_TYPE("table"),
+ &aRelId);
+
+ pWorksheetStrm->singleElement(XML_tablePart, FSNS(XML_r, XML_id), aRelId.toUtf8());
+
+ rStrm.PushStream( pTableStrm);
+ SaveTableXml( rStrm, it);
+ rStrm.PopStream();
+ }
+ pWorksheetStrm->endElement( XML_tableParts);
+}
+
+
+XclExpTablesManager::XclExpTablesManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpTablesManager::~XclExpTablesManager()
+{
+}
+
+void XclExpTablesManager::Initialize()
+{
+ // All non-const to be able to call RefreshTableColumnNames().
+ ScDocument& rDoc = GetDoc();
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+ if (!pDBColl)
+ return;
+
+ ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
+ if (rDBs.empty())
+ return;
+
+ sal_Int32 nTableId = 0;
+ for (const auto& rxDB : rDBs)
+ {
+ ScDBData* pDBData = rxDB.get();
+ pDBData->RefreshTableColumnNames( &rDoc); // currently not in sync, so refresh
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ pDBData->GetArea( aRange);
+ SCTAB nTab = aRange.aStart.Tab();
+ TablesMapType::iterator it = maTablesMap.find( nTab);
+ if (it == maTablesMap.end())
+ {
+ rtl::Reference< XclExpTables > pNew;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ pNew = new XclExpTablesImpl5( GetRoot());
+ break;
+ case EXC_BIFF8:
+ pNew = new XclExpTablesImpl8( GetRoot());
+ break;
+ default:
+ assert(!"Unknown BIFF type!");
+ continue; // for
+ }
+ ::std::pair< TablesMapType::iterator, bool > ins( maTablesMap.insert( ::std::make_pair( nTab, pNew)));
+ if (!ins.second)
+ {
+ assert(!"XclExpTablesManager::Initialize - XclExpTables insert failed");
+ continue; // for
+ }
+ it = ins.first;
+ }
+ it->second->AppendTable( pDBData, ++nTableId);
+ }
+}
+
+rtl::Reference< XclExpTables > XclExpTablesManager::GetTablesBySheet( SCTAB nTab )
+{
+ TablesMapType::iterator it = maTablesMap.find(nTab);
+ return it == maTablesMap.end() ? nullptr : it->second;
+}
+
+XclExpTables::Entry::Entry( const ScDBData* pData, sal_Int32 nTableId ) :
+ mpData(pData), mnTableId(nTableId)
+{
+}
+
+XclExpTables::XclExpTables( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot)
+{
+}
+
+XclExpTables::~XclExpTables()
+{
+}
+
+void XclExpTables::AppendTable( const ScDBData* pData, sal_Int32 nTableId )
+{
+ maTables.emplace_back( pData, nTableId);
+}
+
+void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry )
+{
+ const ScDBData& rData = *rEntry.mpData;
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ rData.GetArea( aRange);
+ sax_fastparser::FSHelperPtr& pTableStrm = rStrm.GetCurrentStream();
+ pTableStrm->startElement( XML_table,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ XML_id, OString::number( rEntry.mnTableId),
+ XML_name, rData.GetName().toUtf8(),
+ XML_displayName, rData.GetName().toUtf8(),
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange),
+ XML_headerRowCount, ToPsz10(rData.HasHeader()),
+ XML_totalsRowCount, ToPsz10(rData.HasTotals()),
+ XML_totalsRowShown, ToPsz10(rData.HasTotals()) // we don't support that but if there are totals they are shown
+ // OOXTODO: XML_comment, ...,
+ // OOXTODO: XML_connectionId, ...,
+ // OOXTODO: XML_dataCellStyle, ...,
+ // OOXTODO: XML_dataDxfId, ...,
+ // OOXTODO: XML_headerRowBorderDxfId, ...,
+ // OOXTODO: XML_headerRowCellStyle, ...,
+ // OOXTODO: XML_headerRowDxfId, ...,
+ // OOXTODO: XML_insertRow, ...,
+ // OOXTODO: XML_insertRowShift, ...,
+ // OOXTODO: XML_published, ...,
+ // OOXTODO: XML_tableBorderDxfId, ...,
+ // OOXTODO: XML_tableType, ...,
+ // OOXTODO: XML_totalsRowBorderDxfId, ...,
+ // OOXTODO: XML_totalsRowCellStyle, ...,
+ // OOXTODO: XML_totalsRowDxfId, ...
+ );
+
+ if (rData.HasAutoFilter())
+ {
+ /* TODO: does this need to exclude totals row? */
+
+ /* TODO: in OOXML 12.3.21 Table Definition Part has information
+ * that an applied autoFilter has child elements
+ * <af:filterColumn><af:filters><af:filter>.
+ * When not applied but buttons hidden, Excel writes, for example,
+ * <filterColumn colId="0" hiddenButton="1"/> */
+
+ ExcAutoFilterRecs aAutoFilter( rStrm.GetRoot(), aRange.aStart.Tab(), &rData);
+ aAutoFilter.SaveXml( rStrm);
+ }
+
+ const std::vector< OUString >& rColNames = rData.GetTableColumnNames();
+ const std::vector< TableColumnAttributes >& rColAttributes = rData.GetTableColumnAttributes();
+ if (!rColNames.empty())
+ {
+ pTableStrm->startElement(XML_tableColumns,
+ XML_count, OString::number(aRange.aEnd.Col() - aRange.aStart.Col() + 1));
+
+ for (size_t i=0, n=rColNames.size(); i < n; ++i)
+ {
+ // OOXTODO: write <calculatedColumnFormula> once we support it, in
+ // which case we'd need start/endElement XML_tableColumn for such
+ // column.
+
+ // OOXTODO: write <totalsRowFormula> once we support it.
+
+ pTableStrm->singleElement( XML_tableColumn,
+ XML_id, OString::number(i+1),
+ XML_name, rColNames[i].toUtf8(),
+ XML_totalsRowFunction, (i < rColAttributes.size() ? rColAttributes[i].maTotalsFunction : std::nullopt)
+ // OOXTODO: XML_dataCellStyle, ...,
+ // OOXTODO: XML_dataDxfId, ...,
+ // OOXTODO: XML_headerRowCellStyle, ...,
+ // OOXTODO: XML_headerRowDxfId, ...,
+ // OOXTODO: XML_queryTableFieldId, ...,
+ // OOXTODO: XML_totalsRowCellStyle, ...,
+ // OOXTODO: XML_totalsRowDxfId, ...,
+ // OOXTODO: XML_totalsRowLabel, ...,
+ // OOXTODO: XML_uniqueName, ...
+ );
+ }
+
+ pTableStrm->endElement( XML_tableColumns);
+ }
+
+ // OOXTODO: write <tableStyleInfo> once we have table styles.
+
+ pTableStrm->endElement( XML_table);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeescher.cxx b/sc/source/filter/excel/xeescher.cxx
new file mode 100644
index 0000000000..8827b78afe
--- /dev/null
+++ b/sc/source/filter/excel/xeescher.cxx
@@ -0,0 +1,2066 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xeescher.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+
+#include <set>
+#include <vcl/BitmapReadAccess.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/outlobj.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svtools/embedhlp.hxx>
+
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <postit.hxx>
+
+#include <fapihelper.hxx>
+#include <xcl97esc.hxx>
+#include <xechart.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xestyle.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+#include <userdat.hxx>
+#include <drwlayer.hxx>
+#include <svl/itemset.hxx>
+#include <svx/sdtaitm.hxx>
+#include <document.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/chartexport.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::lang::XServiceInfo;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XShapes;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::form::binding::XBindableValue;
+using ::com::sun::star::form::binding::XListEntrySink;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+using ::oox::drawingml::DrawingML;
+using ::oox::drawingml::ChartExport;
+using namespace oox;
+
+namespace
+{
+
+const char *ToHorizAlign( SdrTextHorzAdjust eAdjust )
+{
+ switch( eAdjust )
+ {
+ case SDRTEXTHORZADJUST_CENTER:
+ return "center";
+ case SDRTEXTHORZADJUST_RIGHT:
+ return "right";
+ case SDRTEXTHORZADJUST_BLOCK:
+ return "justify";
+ case SDRTEXTHORZADJUST_LEFT:
+ default:
+ return "left";
+ }
+}
+
+const char *ToVertAlign( SdrTextVertAdjust eAdjust )
+{
+ switch( eAdjust )
+ {
+ case SDRTEXTVERTADJUST_CENTER:
+ return "center";
+ case SDRTEXTVERTADJUST_BOTTOM:
+ return "bottom";
+ case SDRTEXTVERTADJUST_BLOCK:
+ return "justify";
+ case SDRTEXTVERTADJUST_TOP:
+ default:
+ return "top";
+ }
+}
+
+void lcl_WriteAnchorVertex( sax_fastparser::FSHelperPtr const & rComments, const tools::Rectangle &aRect )
+{
+ rComments->startElement(FSNS(XML_xdr, XML_col));
+ rComments->writeEscaped( OUString::number( aRect.Left() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_col ) );
+ rComments->startElement(FSNS(XML_xdr, XML_colOff));
+ rComments->writeEscaped( OUString::number( aRect.Top() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_colOff ) );
+ rComments->startElement(FSNS(XML_xdr, XML_row));
+ rComments->writeEscaped( OUString::number( aRect.Right() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_row ) );
+ rComments->startElement(FSNS(XML_xdr, XML_rowOff));
+ rComments->writeEscaped( OUString::number( aRect.Bottom() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_rowOff ) );
+}
+
+tools::Long lcl_hmm2output(tools::Long value, bool bInEMU)
+{
+ return o3tl::convert(value, o3tl::Length::mm100, bInEMU ? o3tl::Length::emu : o3tl::Length::px);
+}
+
+void lcl_GetFromTo( const XclExpRoot& rRoot, const tools::Rectangle &aRect, sal_Int32 nTab, tools::Rectangle &aFrom, tools::Rectangle &aTo, bool bInEMU = false )
+{
+ sal_Int32 nCol = 0, nRow = 0;
+ sal_Int32 nColOff = 0, nRowOff= 0;
+
+ const bool bRTL = rRoot.GetDoc().IsNegativePage( nTab );
+ if (!bRTL)
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Left() <= aRect.Left() )
+ {
+ nCol++;
+ nColOff = aRect.Left() - r.Left();
+ }
+ if( r.Top() <= aRect.Top() )
+ {
+ nRow++;
+ nRowOff = aRect.Top() - r.Top();
+ }
+ if( r.Left() > aRect.Left() && r.Top() > aRect.Top() )
+ {
+ aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ),
+ nRow-1, lcl_hmm2output( nRowOff, bInEMU ) );
+ break;
+ }
+ }
+ }
+ else
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Left() >= aRect.Left() )
+ {
+ nCol++;
+ nColOff = r.Left() - aRect.Left();
+ }
+ if( r.Top() <= aRect.Top() )
+ {
+ nRow++;
+ nRowOff = aRect.Top() - r.Top();
+ }
+ if( r.Left() < aRect.Left() && r.Top() > aRect.Top() )
+ {
+ aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ),
+ nRow-1, lcl_hmm2output( nRowOff, bInEMU ) );
+ break;
+ }
+ }
+ }
+ if (!bRTL)
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Right() < aRect.Right() )
+ nCol++;
+ if( r.Bottom() < aRect.Bottom() )
+ nRow++;
+ if( r.Right() >= aRect.Right() && r.Bottom() >= aRect.Bottom() )
+ {
+ aTo = tools::Rectangle( nCol, lcl_hmm2output( aRect.Right() - r.Left(), bInEMU ),
+ nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU ));
+ break;
+ }
+ }
+ }
+ else
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Right() >= aRect.Right() )
+ nCol++;
+ if( r.Bottom() < aRect.Bottom() )
+ nRow++;
+ if( r.Right() < aRect.Right() && r.Bottom() >= aRect.Bottom() )
+ {
+ aTo = tools::Rectangle( nCol, lcl_hmm2output( r.Left() - aRect.Right(), bInEMU ),
+ nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU ));
+ break;
+ }
+ }
+ }
+}
+
+} // namespace
+
+// Escher client anchor =======================================================
+
+XclExpDffAnchorBase::XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags ) :
+ XclExpRoot( rRoot ),
+ mnFlags( nFlags )
+{
+}
+
+void XclExpDffAnchorBase::SetFlags( const SdrObject& rSdrObj )
+{
+ ImplSetFlags( rSdrObj );
+}
+
+void XclExpDffAnchorBase::SetSdrObject( const SdrObject& rSdrObj )
+{
+ ImplSetFlags( rSdrObj );
+ ImplCalcAnchorRect( rSdrObj.GetCurrentBoundRect(), MapUnit::Map100thMM );
+}
+
+void XclExpDffAnchorBase::WriteDffData( EscherEx& rEscherEx ) const
+{
+ rEscherEx.AddAtom( 18, ESCHER_ClientAnchor );
+ rEscherEx.GetStream().WriteUInt16( mnFlags );
+ WriteXclObjAnchor( rEscherEx.GetStream(), maAnchor );
+}
+
+void XclExpDffAnchorBase::WriteData( EscherEx& rEscherEx, const tools::Rectangle& rRect )
+{
+ // the passed rectangle is in twips
+ ImplCalcAnchorRect( rRect, MapUnit::MapTwip );
+ WriteDffData( rEscherEx );
+}
+
+void XclExpDffAnchorBase::ImplSetFlags( const SdrObject& )
+{
+ OSL_FAIL( "XclExpDffAnchorBase::ImplSetFlags - not implemented" );
+}
+
+void XclExpDffAnchorBase::ImplCalcAnchorRect( const tools::Rectangle&, MapUnit )
+{
+ OSL_FAIL( "XclExpDffAnchorBase::ImplCalcAnchorRect - not implemented" );
+}
+
+XclExpDffSheetAnchor::XclExpDffSheetAnchor( const XclExpRoot& rRoot ) :
+ XclExpDffAnchorBase( rRoot ),
+ mnScTab( rRoot.GetCurrScTab() )
+{
+}
+
+void XclExpDffSheetAnchor::ImplSetFlags( const SdrObject& rSdrObj )
+{
+ // set flags for cell/page anchoring
+ if ( ScDrawLayer::GetAnchorType( rSdrObj ) == SCA_CELL )
+ mnFlags = 0;
+ else
+ mnFlags = EXC_ESC_ANCHOR_LOCKED;
+}
+
+void XclExpDffSheetAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ maAnchor.SetRect( GetRoot(), mnScTab, rRect, eMapUnit );
+}
+
+XclExpDffEmbeddedAnchor::XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot,
+ const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) :
+ XclExpDffAnchorBase( rRoot ),
+ maPageSize( rPageSize ),
+ mnScaleX( nScaleX ),
+ mnScaleY( nScaleY )
+{
+}
+
+void XclExpDffEmbeddedAnchor::ImplSetFlags( const SdrObject& /*rSdrObj*/ )
+{
+ // TODO (unsupported feature): fixed size
+}
+
+void XclExpDffEmbeddedAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ maAnchor.SetRect( maPageSize, mnScaleX, mnScaleY, rRect, eMapUnit );
+}
+
+XclExpDffNoteAnchor::XclExpDffNoteAnchor( const XclExpRoot& rRoot, const tools::Rectangle& rRect ) :
+ XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_SIZELOCKED )
+{
+ maAnchor.SetRect( rRoot, rRoot.GetCurrScTab(), rRect, MapUnit::Map100thMM );
+}
+
+XclExpDffDropDownAnchor::XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
+ XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_POSLOCKED )
+{
+ GetAddressConverter().ConvertAddress( maAnchor.maFirst, rScPos, true );
+ maAnchor.maLast.mnCol = maAnchor.maFirst.mnCol + 1;
+ maAnchor.maLast.mnRow = maAnchor.maFirst.mnRow + 1;
+ maAnchor.mnLX = maAnchor.mnTY = maAnchor.mnRX = maAnchor.mnBY = 0;
+}
+
+// MSODRAWING* records ========================================================
+
+XclExpMsoDrawingBase::XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId ) :
+ XclExpRecord( nRecId ),
+ mrEscherEx( rEscherEx ),
+ mnFragmentKey( rEscherEx.InitNextDffFragment() )
+{
+}
+
+void XclExpMsoDrawingBase::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mrEscherEx.GetStreamPos() == mrEscherEx.GetDffFragmentPos( mnFragmentKey ),
+ "XclExpMsoDrawingBase::WriteBody - DFF stream position mismatch" );
+ rStrm.CopyFromStream( mrEscherEx.GetStream(), mrEscherEx.GetDffFragmentSize( mnFragmentKey ) );
+}
+
+XclExpMsoDrawingGroup::XclExpMsoDrawingGroup( XclEscherEx& rEscherEx ) :
+ XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWINGGROUP )
+{
+ SvStream& rDffStrm = mrEscherEx.GetStream();
+
+ // write the DGGCONTAINER with some default settings
+ mrEscherEx.OpenContainer( ESCHER_DggContainer );
+
+ // TODO: stuff the OPT atom with our own document defaults?
+ static const sal_uInt8 spnDffOpt[] = {
+ 0xBF, 0x00, 0x08, 0x00, 0x08, 0x00, 0x81, 0x01,
+ 0x09, 0x00, 0x00, 0x08, 0xC0, 0x01, 0x40, 0x00,
+ 0x00, 0x08
+ };
+ mrEscherEx.AddAtom( sizeof( spnDffOpt ), ESCHER_OPT, 3, 3 );
+ rDffStrm.WriteBytes(spnDffOpt, sizeof(spnDffOpt));
+
+ // SPLITMENUCOLORS contains colors in toolbar
+ static const sal_uInt8 spnDffSplitMenuColors[] = {
+ 0x0D, 0x00, 0x00, 0x08, 0x0C, 0x00, 0x00, 0x08,
+ 0x17, 0x00, 0x00, 0x08, 0xF7, 0x00, 0x00, 0x10
+ };
+ mrEscherEx.AddAtom( sizeof( spnDffSplitMenuColors ), ESCHER_SplitMenuColors, 0, 4 );
+ rDffStrm.WriteBytes(spnDffSplitMenuColors, sizeof(spnDffSplitMenuColors));
+
+ // close the DGGCONTAINER
+ mrEscherEx.CloseContainer();
+ mrEscherEx.UpdateDffFragmentEnd();
+}
+
+XclExpMsoDrawing::XclExpMsoDrawing( XclEscherEx& rEscherEx ) :
+ XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWING )
+{
+}
+
+XclExpImgData::XclExpImgData( Graphic aGraphic, sal_uInt16 nRecId ) :
+ maGraphic(std::move( aGraphic )),
+ mnRecId( nRecId )
+{
+}
+
+void XclExpImgData::Save( XclExpStream& rStrm )
+{
+ Bitmap aBmp = maGraphic.GetBitmapEx().GetBitmap();
+ if (aBmp.getPixelFormat() != vcl::PixelFormat::N24_BPP)
+ aBmp.Convert( BmpConversion::N24Bit );
+
+ BitmapScopedReadAccess pAccess(aBmp);
+ if( !pAccess )
+ return;
+
+ sal_Int32 nWidth = ::std::min< sal_Int32 >( pAccess->Width(), 0xFFFF );
+ sal_Int32 nHeight = ::std::min< sal_Int32 >( pAccess->Height(), 0xFFFF );
+ if( (nWidth <= 0) || (nHeight <= 0) )
+ return;
+
+ sal_uInt8 nPadding = static_cast< sal_uInt8 >( nWidth & 0x03 );
+ sal_uInt32 nTmpSize = static_cast< sal_uInt32 >( (nWidth * 3 + nPadding) * nHeight + 12 );
+
+ rStrm.StartRecord( mnRecId, nTmpSize + 4 );
+
+ rStrm << EXC_IMGDATA_BMP // BMP format
+ << EXC_IMGDATA_WIN // Windows
+ << nTmpSize // size after _this_ field
+ << sal_uInt32( 12 ) // BITMAPCOREHEADER size
+ << static_cast< sal_uInt16 >( nWidth ) // width
+ << static_cast< sal_uInt16 >( nHeight ) // height
+ << sal_uInt16( 1 ) // planes
+ << sal_uInt16( 24 ); // bits per pixel
+
+ for( sal_Int32 nY = nHeight - 1; nY >= 0; --nY )
+ {
+ Scanline pScanline = pAccess->GetScanline( nY );
+ for( sal_Int32 nX = 0; nX < nWidth; ++nX )
+ {
+ const BitmapColor& rBmpColor = pAccess->GetPixelFromData( pScanline, nX );
+ rStrm << rBmpColor.GetBlue() << rBmpColor.GetGreen() << rBmpColor.GetRed();
+ }
+ rStrm.WriteZeroBytes( nPadding );
+ }
+
+ rStrm.EndRecord();
+}
+
+void XclExpImgData::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetCurrentStream();
+
+ DrawingML aDML(pWorksheet, &rStrm, drawingml::DOCUMENT_XLSX);
+ OUString rId = aDML.writeGraphicToStorage(maGraphic);
+ pWorksheet->singleElement(XML_picture, FSNS(XML_r, XML_id), rId);
+}
+
+XclExpControlHelper::XclExpControlHelper( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnEntryCount( 0 )
+{
+}
+
+XclExpControlHelper::~XclExpControlHelper()
+{
+}
+
+void XclExpControlHelper::ConvertSheetLinks( Reference< XShape > const & xShape )
+{
+ mxCellLink.reset();
+ mxCellLinkAddress.SetInvalid();
+ mxSrcRange.reset();
+ mnEntryCount = 0;
+
+ // get control model
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ // *** cell link *** ------------------------------------------------------
+
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY );
+ if( xBindable.is() )
+ {
+ Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY );
+ if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) )
+ {
+ ScfPropertySet aBindProp( xServInfo );
+ CellAddress aApiAddress;
+ if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) )
+ {
+ ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress );
+ if( GetTabInfo().IsExportTab( mxCellLinkAddress.Tab() ) )
+ mxCellLink = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, mxCellLinkAddress );
+ }
+ }
+ }
+
+ // *** source range *** ---------------------------------------------------
+
+ Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY );
+ if( !xEntrySink.is() )
+ return;
+
+ Reference< XServiceInfo > xServInfo( xEntrySink->getListEntrySource(), UNO_QUERY );
+ if( !(xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_LISTSOURCE )) )
+ return;
+
+ ScfPropertySet aSinkProp( xServInfo );
+ CellRangeAddress aApiRange;
+ if( aSinkProp.GetProperty( aApiRange, SC_UNONAME_CELLRANGE ) )
+ {
+ ScRange aSrcRange;
+ ScUnoConversion::FillScRange( aSrcRange, aApiRange );
+ if( (aSrcRange.aStart.Tab() == aSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( aSrcRange.aStart.Tab() ) )
+ mxSrcRange = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, aSrcRange );
+ mnEntryCount = static_cast< sal_uInt16 >( aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1 );
+ }
+}
+
+void XclExpControlHelper::WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr )
+{
+ sal_uInt16 nFmlaSize = rTokArr.GetSize();
+ rStrm << nFmlaSize << sal_uInt32( 0 );
+ rTokArr.WriteArray( rStrm );
+ if( nFmlaSize & 1 ) // pad to 16-bit
+ rStrm << sal_uInt8( 0 );
+}
+
+void XclExpControlHelper::WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr )
+{
+ rStrm.StartRecord( nSubRecId, (rTokArr.GetSize() + 5) & ~1 );
+ WriteFormula( rStrm, rTokArr );
+ rStrm.EndRecord();
+}
+
+//delete for exporting OCX
+//#if EXC_EXP_OCX_CTRL
+
+XclExpOcxControlObj::XclExpOcxControlObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor, OUString aClassName, sal_uInt32 nStrmStart, sal_uInt32 nStrmSize ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_PICTURE, true ),
+ XclExpControlHelper( rObjMgr.GetRoot() ),
+ maClassName(std::move( aClassName )),
+ mnStrmStart( nStrmStart ),
+ mnStrmSize( nStrmSize )
+{
+ ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) );
+
+ // OBJ record flags
+ SetLocked( true );
+ SetPrintable( aCtrlProp.GetBoolProperty( "Printable" ) );
+ SetAutoFill( false );
+ SetAutoLine( false );
+
+ // fill DFF property set
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl,
+ ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | ShapeFlag::OLEShape );
+ tools::Rectangle aDummyRect;
+ EscherPropertyContainer aPropOpt( mrEscherEx.GetGraphicProvider(), mrEscherEx.QueryPictureStream(), aDummyRect );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+
+ // #i51348# name of the control, may overwrite shape name
+ OUString aCtrlName;
+ if( aCtrlProp.GetProperty( aCtrlName, "Name" ) && !aCtrlName.isEmpty() )
+ aPropOpt.AddOpt( ESCHER_Prop_wzName, aCtrlName );
+
+ // meta file
+ //TODO - needs check
+ Reference< XPropertySet > xShapePS( xShape, UNO_QUERY );
+ if( xShapePS.is() && aPropOpt.CreateGraphicProperties( xShapePS, "MetaFile", false ) )
+ {
+ sal_uInt32 nBlipId;
+ if( aPropOpt.GetOpt( ESCHER_Prop_pib, nBlipId ) )
+ aPropOpt.AddOpt( ESCHER_Prop_pictureId, nBlipId );
+ }
+
+ // write DFF property set to stream
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // spreadsheet links
+ ConvertSheetLinks( xShape );
+}
+
+void XclExpOcxControlObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ // OBJCF - clipboard format
+ rStrm.StartRecord( EXC_ID_OBJCF, 2 );
+ rStrm << sal_uInt16( 2 );
+ rStrm.EndRecord();
+
+ // OBJFLAGS
+ rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 );
+ rStrm << sal_uInt16( 0x0031 );
+ rStrm.EndRecord();
+
+ // OBJPICTFMLA
+ XclExpString aClass( maClassName );
+ sal_uInt16 nClassNameSize = static_cast< sal_uInt16 >( aClass.GetSize() );
+ sal_uInt16 nClassNamePad = nClassNameSize & 1;
+ sal_uInt16 nFirstPartSize = 12 + nClassNameSize + nClassNamePad;
+
+ const XclTokenArray* pCellLink = GetCellLinkTokArr();
+ sal_uInt16 nCellLinkSize = pCellLink ? ((pCellLink->GetSize() + 7) & 0xFFFE) : 0;
+
+ const XclTokenArray* pSrcRange = GetSourceRangeTokArr();
+ sal_uInt16 nSrcRangeSize = pSrcRange ? ((pSrcRange->GetSize() + 7) & 0xFFFE) : 0;
+
+ sal_uInt16 nPictFmlaSize = nFirstPartSize + nCellLinkSize + nSrcRangeSize + 18;
+ rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nPictFmlaSize );
+
+ rStrm << nFirstPartSize // size of first part
+ << sal_uInt16( 5 ) // formula size
+ << sal_uInt32( 0 ) // unknown ID
+ << sal_uInt8( 0x02 ) << sal_uInt32( 0 ) // tTbl token with unknown ID
+ << sal_uInt8( 3 ) // pad to word
+ << aClass; // "Forms.***.1"
+ rStrm.WriteZeroBytes( nClassNamePad ); // pad to word
+ rStrm << mnStrmStart // start in 'Ctls' stream
+ << mnStrmSize // size in 'Ctls' stream
+ << sal_uInt32( 0 ); // class ID size
+ // cell link
+ rStrm << nCellLinkSize;
+ if( pCellLink )
+ WriteFormula( rStrm, *pCellLink );
+ // list source range
+ rStrm << nSrcRangeSize;
+ if( pSrcRange )
+ WriteFormula( rStrm, *pSrcRange );
+
+ rStrm.EndRecord();
+}
+
+//#else
+
+XclExpTbxControlObj::XclExpTbxControlObj( XclExpObjectManager& rRoot, Reference< XShape > const & xShape , const tools::Rectangle* pChildAnchor ) :
+ XclObj( rRoot, EXC_OBJTYPE_UNKNOWN, true ),
+ XclMacroHelper( rRoot ),
+ mxShape( xShape ),
+ meEventType( EXC_TBX_EVENT_ACTION ),
+ mnHeight( 0 ),
+ mnState( 0 ),
+ mnLineCount( 0 ),
+ mnSelEntry( 0 ),
+ mnScrollValue( 0 ),
+ mnScrollMin( 0 ),
+ mnScrollMax( 100 ),
+ mnScrollStep( 1 ),
+ mnScrollPage( 10 ),
+ mbFlatButton( false ),
+ mbFlatBorder( false ),
+ mbMultiSel( false ),
+ mbScrollHor( false ),
+ mbPrint( false ),
+ mbVisible( false ),
+ mnShapeId( 0 ),
+ mrRoot(rRoot)
+{
+ namespace FormCompType = css::form::FormComponentType;
+ namespace AwtVisualEffect = css::awt::VisualEffect;
+ namespace AwtScrollOrient = css::awt::ScrollBarOrientation;
+
+ ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) );
+ if( !xShape.is() || !aCtrlProp.Is() )
+ return;
+
+ mnHeight = xShape->getSize().Height;
+ if( mnHeight <= 0 )
+ return;
+
+ // control type
+ sal_Int16 nClassId = 0;
+ if( aCtrlProp.GetProperty( nClassId, "ClassId" ) )
+ {
+ switch( nClassId )
+ {
+ case FormCompType::COMMANDBUTTON: mnObjType = EXC_OBJTYPE_BUTTON; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::RADIOBUTTON: mnObjType = EXC_OBJTYPE_OPTIONBUTTON; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::CHECKBOX: mnObjType = EXC_OBJTYPE_CHECKBOX; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::LISTBOX: mnObjType = EXC_OBJTYPE_LISTBOX; meEventType = EXC_TBX_EVENT_CHANGE; break;
+ case FormCompType::COMBOBOX: mnObjType = EXC_OBJTYPE_DROPDOWN; meEventType = EXC_TBX_EVENT_CHANGE; break;
+ case FormCompType::GROUPBOX: mnObjType = EXC_OBJTYPE_GROUPBOX; meEventType = EXC_TBX_EVENT_MOUSE; break;
+ case FormCompType::FIXEDTEXT: mnObjType = EXC_OBJTYPE_LABEL; meEventType = EXC_TBX_EVENT_MOUSE; break;
+ case FormCompType::SCROLLBAR: mnObjType = EXC_OBJTYPE_SCROLLBAR; meEventType = EXC_TBX_EVENT_VALUE; break;
+ case FormCompType::SPINBUTTON: mnObjType = EXC_OBJTYPE_SPIN; meEventType = EXC_TBX_EVENT_VALUE; break;
+ }
+ }
+ if( mnObjType == EXC_OBJTYPE_UNKNOWN )
+ return;
+
+ // OBJ record flags
+ SetLocked( true );
+ mbPrint = aCtrlProp.GetBoolProperty( "Printable" );
+ SetPrintable( mbPrint );
+ SetAutoFill( false );
+ SetAutoLine( false );
+
+ // fill DFF property set
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ mbVisible = aCtrlProp.GetBoolProperty( "EnableVisible" );
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, mbVisible ? 0x00080000 : 0x00080002 ); // visible flag
+
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01000100 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // Text ID
+ aPropOpt.AddOpt( ESCHER_Prop_WrapText, 0x00000001 );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x001A0008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00100000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+
+ // #i51348# name of the control, may overwrite shape name
+ if( aCtrlProp.GetProperty( msCtrlName, "Name" ) && !msCtrlName.isEmpty() )
+ aPropOpt.AddOpt( ESCHER_Prop_wzName, msCtrlName );
+
+ //Export description as alt text
+ if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) )
+ {
+ OUString aAltTxt;
+ OUString aDescrText = pSdrObj->GetDescription();
+ if(!aDescrText.isEmpty())
+ aAltTxt = aDescrText.copy( 0, std::min<sal_Int32>(MSPROP_DESCRIPTION_MAX_LEN, aDescrText.getLength()) );
+ aPropOpt.AddOpt( ESCHER_Prop_wzDescription, aAltTxt );
+ }
+
+ // write DFF property set to stream
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // control label
+ if( aCtrlProp.GetProperty( msLabel, "Label" ) )
+ {
+ /* Be sure to construct the MSODRAWING record containing the
+ ClientTextbox atom after the base OBJ's MSODRAWING record data is
+ completed. */
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ sal_uInt16 nXclFont = EXC_FONT_APP;
+ if( !msLabel.isEmpty() )
+ {
+ XclFontData aFontData;
+ GetFontPropSetHelper().ReadFontProperties( aFontData, aCtrlProp, EXC_FONTPROPSET_CONTROL );
+ if( (!aFontData.maName.isEmpty() ) && (aFontData.mnHeight > 0) )
+ nXclFont = GetFontBuffer().Insert( aFontData, EXC_COLOR_CTRLTEXT );
+ }
+
+ pTxo.reset( new XclTxo( msLabel, nXclFont ) );
+ pTxo->SetHorAlign( (mnObjType == EXC_OBJTYPE_BUTTON) ? EXC_OBJ_HOR_CENTER : EXC_OBJ_HOR_LEFT );
+ pTxo->SetVerAlign( EXC_OBJ_VER_CENTER );
+ }
+
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+
+ // other properties
+ aCtrlProp.GetProperty( mnLineCount, "LineCount" );
+
+ // border style
+ sal_Int16 nApiButton = AwtVisualEffect::LOOK3D;
+ sal_Int16 nApiBorder = AwtVisualEffect::LOOK3D;
+ switch( nClassId )
+ {
+ case FormCompType::LISTBOX:
+ case FormCompType::COMBOBOX:
+ aCtrlProp.GetProperty( nApiBorder, "Border" );
+ break;
+ case FormCompType::CHECKBOX:
+ case FormCompType::RADIOBUTTON:
+ aCtrlProp.GetProperty( nApiButton, "VisualEffect" );
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ // Push button cannot be set to flat in Excel
+ case FormCompType::COMMANDBUTTON:
+ nApiBorder = AwtVisualEffect::LOOK3D;
+ break;
+ // Label does not support a border in Excel
+ case FormCompType::FIXEDTEXT:
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ /* Scroll bar and spin button have a "Border" property, but it is
+ really used for a border, and not for own 3D/flat look (#i34712#). */
+ case FormCompType::SCROLLBAR:
+ case FormCompType::SPINBUTTON:
+ nApiButton = AwtVisualEffect::LOOK3D;
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ // Group box does not support flat style (#i34712#)
+ case FormCompType::GROUPBOX:
+ nApiBorder = AwtVisualEffect::LOOK3D;
+ break;
+ }
+ mbFlatButton = nApiButton != AwtVisualEffect::LOOK3D;
+ mbFlatBorder = nApiBorder != AwtVisualEffect::LOOK3D;
+
+ // control state
+ sal_Int16 nApiState = 0;
+ if( aCtrlProp.GetProperty( nApiState, "State" ) )
+ {
+ switch( nApiState )
+ {
+ case 0: mnState = EXC_OBJ_CHECKBOX_UNCHECKED; break;
+ case 1: mnState = EXC_OBJ_CHECKBOX_CHECKED; break;
+ case 2: mnState = EXC_OBJ_CHECKBOX_TRISTATE; break;
+ }
+ }
+
+ // special control contents
+ switch( nClassId )
+ {
+ case FormCompType::LISTBOX:
+ {
+ mbMultiSel = aCtrlProp.GetBoolProperty( "MultiSelection" );
+ Sequence< sal_Int16 > aSelection;
+ if( aCtrlProp.GetProperty( aSelection, "SelectedItems" ) )
+ {
+ if( aSelection.hasElements() )
+ {
+ mnSelEntry = aSelection[ 0 ] + 1;
+ comphelper::sequenceToContainer(maMultiSel, aSelection);
+ }
+ }
+
+ // convert listbox with dropdown button to Excel dropdown
+ if( aCtrlProp.GetBoolProperty( "Dropdown" ) )
+ mnObjType = EXC_OBJTYPE_DROPDOWN;
+ }
+ break;
+
+ case FormCompType::COMBOBOX:
+ {
+ Sequence< OUString > aStringList;
+ OUString aDefText;
+ if( aCtrlProp.GetProperty( aStringList, "StringItemList" ) &&
+ aCtrlProp.GetProperty( aDefText, "Text" ) &&
+ aStringList.hasElements() && !aDefText.isEmpty() )
+ {
+ auto nIndex = comphelper::findValue(aStringList, aDefText);
+ if( nIndex != -1 )
+ mnSelEntry = static_cast< sal_Int16 >( nIndex + 1 ); // 1-based
+ if( mnSelEntry > 0 )
+ maMultiSel.resize( 1, mnSelEntry - 1 );
+ }
+
+ // convert combobox without dropdown button to Excel listbox
+ if( !aCtrlProp.GetBoolProperty( "Dropdown" ) )
+ mnObjType = EXC_OBJTYPE_LISTBOX;
+ }
+ break;
+
+ case FormCompType::SCROLLBAR:
+ {
+ sal_Int32 nApiValue = 0;
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMin" ) )
+ mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMax" ) )
+ mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValue" ) )
+ mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax );
+ if( aCtrlProp.GetProperty( nApiValue, "LineIncrement" ) )
+ mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "BlockIncrement" ) )
+ mnScrollPage = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) )
+ mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL;
+ }
+ break;
+
+ case FormCompType::SPINBUTTON:
+ {
+ sal_Int32 nApiValue = 0;
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValueMin" ) )
+ mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValueMax" ) )
+ mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValue" ) )
+ mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinIncrement" ) )
+ mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) )
+ mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL;
+ }
+ break;
+ }
+
+ {
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( xCtrlModel.is() )
+ {
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY );
+ if( xBindable.is() )
+ {
+ Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY );
+ if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) )
+ {
+ ScfPropertySet aBindProp( xServInfo );
+ CellAddress aApiAddress;
+ if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) )
+ {
+ ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress );
+ if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) )
+ lcl_GetFromTo( rRoot, pSdrObj->GetLogicRect(), mxCellLinkAddress.Tab(), maAreaFrom, maAreaTo, true );
+ }
+ }
+ }
+ }
+ }
+
+ // spreadsheet links
+ ConvertSheetLinks( xShape );
+}
+
+bool XclExpTbxControlObj::SetMacroLink( const ScriptEventDescriptor& rEvent )
+{
+ return XclMacroHelper::SetMacroLink( rEvent, meEventType );
+}
+
+void XclExpTbxControlObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ switch( mnObjType )
+ {
+ // *** Push buttons, labels ***
+
+ case EXC_OBJTYPE_BUTTON:
+ case EXC_OBJTYPE_LABEL:
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ break;
+
+ // *** Check boxes, option buttons ***
+
+ case EXC_OBJTYPE_CHECKBOX:
+ case EXC_OBJTYPE_OPTIONBUTTON:
+ {
+ // ftCbls - box properties
+ sal_uInt16 nStyle = 0;
+ ::set_flag( nStyle, EXC_OBJ_CHECKBOX_FLAT, mbFlatButton );
+
+ rStrm.StartRecord( EXC_ID_OBJCBLS, 12 );
+ rStrm << mnState;
+ rStrm.WriteZeroBytes( 8 );
+ rStrm << nStyle;
+ rStrm.EndRecord();
+
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftCblsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJCBLSFMLA );
+
+ // ftCblsData subrecord - box properties, again
+ rStrm.StartRecord( EXC_ID_OBJCBLS, 8 );
+ rStrm << mnState;
+ rStrm.WriteZeroBytes( 4 );
+ rStrm << nStyle;
+ rStrm.EndRecord();
+ }
+ break;
+
+ // *** List boxes, combo boxes ***
+
+ case EXC_OBJTYPE_LISTBOX:
+ case EXC_OBJTYPE_DROPDOWN:
+ {
+ sal_uInt16 nEntryCount = GetSourceEntryCount();
+
+ // ftSbs subrecord - Scroll bars
+ sal_Int32 nLineHeight = XclTools::GetHmmFromTwips( 200 ); // always 10pt
+ if( mnObjType == EXC_OBJTYPE_LISTBOX )
+ mnLineCount = static_cast< sal_uInt16 >( mnHeight / nLineHeight );
+ mnScrollValue = 0;
+ mnScrollMin = 0;
+ sal_uInt16 nInvisLines = (nEntryCount >= mnLineCount) ? (nEntryCount - mnLineCount) : 0;
+ mnScrollMax = limit_cast< sal_uInt16 >( nInvisLines, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ mnScrollStep = 1;
+ mnScrollPage = limit_cast< sal_uInt16 >( mnLineCount, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ mbScrollHor = false;
+ WriteSbs( rStrm );
+
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftSbsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA );
+
+ // ftLbsData - source data range and box properties
+ sal_uInt16 nStyle = 0;
+ ::insert_value( nStyle, mbMultiSel ? EXC_OBJ_LISTBOX_MULTI : EXC_OBJ_LISTBOX_SINGLE, 4, 2 );
+ ::set_flag( nStyle, EXC_OBJ_LISTBOX_FLAT, mbFlatBorder );
+
+ rStrm.StartRecord( EXC_ID_OBJLBSDATA, 0 );
+
+ if( const XclTokenArray* pSrcRange = GetSourceRangeTokArr() )
+ {
+ rStrm << static_cast< sal_uInt16 >( (pSrcRange->GetSize() + 7) & 0xFFFE );
+ WriteFormula( rStrm, *pSrcRange );
+ }
+ else
+ rStrm << sal_uInt16( 0 );
+
+ rStrm << nEntryCount << mnSelEntry << nStyle << sal_uInt16( 0 );
+ if( mnObjType == EXC_OBJTYPE_LISTBOX )
+ {
+ if( nEntryCount )
+ {
+ ScfUInt8Vec aSelEx( nEntryCount, 0 );
+ for( const auto& rItem : maMultiSel )
+ if( rItem < nEntryCount )
+ aSelEx[ rItem ] = 1;
+ rStrm.Write( aSelEx.data(), aSelEx.size() );
+ }
+ }
+ else if( mnObjType == EXC_OBJTYPE_DROPDOWN )
+ {
+ rStrm << sal_uInt16( 0 ) << mnLineCount << sal_uInt16( 0 ) << sal_uInt16( 0 );
+ }
+
+ rStrm.EndRecord();
+ }
+ break;
+
+ // *** Spin buttons, scrollbars ***
+
+ case EXC_OBJTYPE_SPIN:
+ case EXC_OBJTYPE_SCROLLBAR:
+ {
+ // ftSbs subrecord - scroll bars
+ WriteSbs( rStrm );
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftSbsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA );
+ }
+ break;
+
+ // *** Group boxes ***
+
+ case EXC_OBJTYPE_GROUPBOX:
+ {
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+
+ // ftGboData subrecord - group box properties
+ sal_uInt16 nStyle = 0;
+ ::set_flag( nStyle, EXC_OBJ_GROUPBOX_FLAT, mbFlatBorder );
+
+ rStrm.StartRecord( EXC_ID_OBJGBODATA, 6 );
+ rStrm << sal_uInt32( 0 )
+ << nStyle;
+ rStrm.EndRecord();
+ }
+ break;
+ }
+}
+
+void XclExpTbxControlObj::WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId )
+{
+ if( const XclTokenArray* pCellLink = GetCellLinkTokArr() )
+ WriteFormulaSubRec( rStrm, nSubRecId, *pCellLink );
+}
+
+void XclExpTbxControlObj::WriteSbs( XclExpStream& rStrm )
+{
+ sal_uInt16 nOrient = 0;
+ ::set_flag( nOrient, EXC_OBJ_SCROLLBAR_HOR, mbScrollHor );
+ sal_uInt16 nStyle = EXC_OBJ_SCROLLBAR_DEFFLAGS;
+ ::set_flag( nStyle, EXC_OBJ_SCROLLBAR_FLAT, mbFlatButton );
+
+ rStrm.StartRecord( EXC_ID_OBJSBS, 20 );
+ rStrm << sal_uInt32( 0 ) // reserved
+ << mnScrollValue // thumb position
+ << mnScrollMin // thumb min pos
+ << mnScrollMax // thumb max pos
+ << mnScrollStep // line increment
+ << mnScrollPage // page increment
+ << nOrient // 0 = vertical, 1 = horizontal
+ << sal_uInt16( 15 ) // thumb width
+ << nStyle; // flags/style
+ rStrm.EndRecord();
+}
+
+void XclExpTbxControlObj::setShapeId(sal_Int32 aShapeId)
+{
+ mnShapeId = aShapeId;
+}
+
+namespace
+{
+/// Handles the VML export of form controls (e.g. checkboxes).
+class VmlFormControlExporter : public oox::vml::VMLExport
+{
+ sal_uInt16 m_nObjType;
+ tools::Rectangle m_aAreaFrom;
+ tools::Rectangle m_aAreaTo;
+ OUString m_sControlName;
+ OUString m_sFmlaLink;
+ OUString m_aLabel;
+ OUString m_aMacroName;
+
+public:
+ VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p, sal_uInt16 nObjType,
+ const tools::Rectangle& rAreaFrom, const tools::Rectangle& rAreaTo,
+ const OUString& sControlName, const OUString& sFmlaLink,
+ OUString aLabel, OUString aMacroName);
+
+protected:
+ using VMLExport::StartShape;
+ sal_Int32 StartShape() override;
+ using VMLExport::EndShape;
+ void EndShape(sal_Int32 nShapeElement) override;
+};
+
+VmlFormControlExporter::VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p,
+ sal_uInt16 nObjType,
+ const tools::Rectangle& rAreaFrom,
+ const tools::Rectangle& rAreaTo,
+ const OUString& sControlName,
+ const OUString& sFmlaLink,
+ OUString aLabel, OUString aMacroName)
+ : VMLExport(p)
+ , m_nObjType(nObjType)
+ , m_aAreaFrom(rAreaFrom)
+ , m_aAreaTo(rAreaTo)
+ , m_sControlName(sControlName)
+ , m_sFmlaLink(sFmlaLink)
+ , m_aLabel(std::move(aLabel))
+ , m_aMacroName(std::move(aMacroName))
+{
+}
+
+sal_Int32 VmlFormControlExporter::StartShape()
+{
+ // Host control.
+ AddShapeAttribute(XML_type, "#_x0000_t201");
+ if (!m_sControlName.isEmpty())
+ AddShapeAttribute(XML_id, m_sControlName.toUtf8());
+
+ return VMLExport::StartShape();
+}
+
+void VmlFormControlExporter::EndShape(sal_Int32 nShapeElement)
+{
+ sax_fastparser::FSHelperPtr pVmlDrawing = GetFS();
+
+ pVmlDrawing->startElement(FSNS(XML_v, XML_textbox));
+ pVmlDrawing->startElement(XML_div);
+ pVmlDrawing->startElement(XML_font);
+ pVmlDrawing->write(m_aLabel);
+ pVmlDrawing->endElement(XML_font);
+ pVmlDrawing->endElement(XML_div);
+ pVmlDrawing->endElement(FSNS(XML_v, XML_textbox));
+
+ OString aObjectType;
+ switch (m_nObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ aObjectType = "Checkbox"_ostr;
+ break;
+ case EXC_OBJTYPE_BUTTON:
+ aObjectType = "Button"_ostr;
+ break;
+ }
+ pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, aObjectType);
+ OString aAnchor
+ = OString::number(m_aAreaFrom.Left()) + ", " + OString::number(m_aAreaFrom.Top()) + ", "
+ + OString::number(m_aAreaFrom.Right()) + ", " + OString::number(m_aAreaFrom.Bottom()) + ", "
+ + OString::number(m_aAreaTo.Left()) + ", " + OString::number(m_aAreaTo.Top()) + ", "
+ + OString::number(m_aAreaTo.Right()) + ", " + OString::number(m_aAreaTo.Bottom());
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_Anchor), aAnchor);
+
+ if (!m_aMacroName.isEmpty())
+ {
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_FmlaMacro), m_aMacroName);
+ }
+
+ // XclExpOcxControlObj::WriteSubRecs() has the same fixed values.
+ if (m_nObjType == EXC_OBJTYPE_BUTTON)
+ {
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextHAlign), "Center");
+ }
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextVAlign), "Center");
+
+ if (!m_sFmlaLink.isEmpty())
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_FmlaLink), m_sFmlaLink);
+
+ pVmlDrawing->endElement(FSNS(XML_x, XML_ClientData));
+ VMLExport::EndShape(nShapeElement);
+}
+
+}
+
+/// Save into xl/drawings/vmlDrawing1.vml.
+void XclExpTbxControlObj::SaveVml(XclExpXmlStream& rStrm)
+{
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ tools::Rectangle aAreaFrom;
+ tools::Rectangle aAreaTo;
+ // Unlike XclExpTbxControlObj::SaveXml(), this is not calculated in EMUs.
+ lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo);
+
+ const OUString sCellLink
+ = mxCellLinkAddress.IsValid()
+ ? mxCellLinkAddress.Format(ScRefFlags::ADDR_ABS, &GetDoc(),
+ ScAddress::Details(formula::FormulaGrammar::CONV_XL_A1))
+ : OUString();
+
+ VmlFormControlExporter aFormControlExporter(rStrm.GetCurrentStream(), GetObjType(), aAreaFrom,
+ aAreaTo, msCtrlName, sCellLink, msLabel, GetMacroName());
+ aFormControlExporter.SetSkipwzName(true); // use XML_id for legacyid, not XML_ID
+ aFormControlExporter.OverrideShapeIDGen(true, "_x0000_s"_ostr);
+ aFormControlExporter.AddSdrObject(*pObj, /*bIsFollowingTextFlow=*/false, /*eHOri=*/-1,
+ /*eVOri=*/-1, /*eHRel=*/-1, /*eVRel=*/-1,
+ /*pWrapAttrList=*/nullptr, /*bOOxmlExport=*/true, mnShapeId);
+}
+
+// save into xl\drawings\drawing1.xml
+void XclExpTbxControlObj::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pDrawing = rStrm.GetCurrentStream();
+
+ pDrawing->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+ pDrawing->startElement(FSNS(XML_mc, XML_Choice),
+ FSNS(XML_xmlns, XML_a14), rStrm.getNamespaceURL(OOX_NS(a14)),
+ XML_Requires, "a14");
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_twoCellAnchor), XML_editAs, "oneCell");
+ {
+ pDrawing->startElement(FSNS(XML_xdr, XML_from));
+ lcl_WriteAnchorVertex(pDrawing, maAreaFrom);
+ pDrawing->endElement(FSNS(XML_xdr, XML_from));
+ pDrawing->startElement(FSNS(XML_xdr, XML_to));
+ lcl_WriteAnchorVertex(pDrawing, maAreaTo);
+ pDrawing->endElement(FSNS(XML_xdr, XML_to));
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_sp));
+ {
+ // xdr:nvSpPr
+ pDrawing->startElement(FSNS(XML_xdr, XML_nvSpPr));
+ {
+ pDrawing->singleElement(FSNS(XML_xdr, XML_cNvPr),
+ XML_id, OString::number(mnShapeId),
+ XML_name, msCtrlName, // control name
+ XML_descr, msLabel, // description as alt text
+ XML_hidden, mbVisible ? "0" : "1");
+ pDrawing->singleElement(FSNS(XML_xdr, XML_cNvSpPr));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_nvSpPr));
+
+ // xdr:spPr
+ pDrawing->startElement(FSNS(XML_xdr, XML_spPr));
+ {
+ // a:xfrm
+ pDrawing->startElement(FSNS(XML_a, XML_xfrm));
+ {
+ pDrawing->singleElement(FSNS(XML_a, XML_off),
+ XML_x, "0",
+ XML_y, "0");
+ pDrawing->singleElement(FSNS(XML_a, XML_ext),
+ XML_cx, "0",
+ XML_cy, "0");
+ }
+ pDrawing->endElement(FSNS(XML_a, XML_xfrm));
+
+ // a:prstGeom
+ pDrawing->startElement(FSNS(XML_a, XML_prstGeom), XML_prst, "rect");
+ {
+ pDrawing->singleElement(FSNS(XML_a, XML_avLst));
+ }
+ pDrawing->endElement(FSNS(XML_a, XML_prstGeom));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_spPr));
+
+ // xdr:txBody
+ {
+ pDrawing->startElement(FSNS(XML_xdr, XML_txBody));
+
+#define DEFLRINS 254
+#define DEFTBINS 127
+ sal_Int32 nLeft, nRight, nTop, nBottom;
+ nLeft = nRight = DEFLRINS;
+ nTop = nBottom = DEFTBINS;
+
+ // top inset looks a bit different compared to ppt export
+ // check if something related doesn't work as expected
+ Reference< XPropertySet > rXPropSet(mxShape, UNO_QUERY);
+
+ try
+ {
+ css::uno::Any mAny;
+
+ mAny = rXPropSet->getPropertyValue("TextLeftDistance");
+ if (mAny.hasValue())
+ mAny >>= nLeft;
+
+ mAny = rXPropSet->getPropertyValue("TextRightDistance");
+ if (mAny.hasValue())
+ mAny >>= nRight;
+
+ mAny = rXPropSet->getPropertyValue("TextUpperDistance");
+ if (mAny.hasValue())
+ mAny >>= nTop;
+
+ mAny = rXPropSet->getPropertyValue("TextLowerDistance");
+ if (mAny.hasValue())
+ mAny >>= nBottom;
+ }
+ catch (...)
+ {
+ }
+
+ // Specifies the inset of the bounding rectangle.
+ // Insets are used just as internal margins for text boxes within shapes.
+ // If this attribute is omitted, then a value of 45720 or 0.05 inches is implied.
+ pDrawing->startElementNS(XML_a, XML_bodyPr,
+ XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != DEFLRINS),
+ XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != DEFLRINS),
+ XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != DEFTBINS),
+ XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != DEFTBINS),
+ XML_anchor, "ctr");
+
+ {
+ bool bTextAutoGrowHeight = false;
+
+ try
+ {
+ css::uno::Any mAny;
+
+ mAny = rXPropSet->getPropertyValue("TextAutoGrowHeight");
+ if (mAny.hasValue())
+ mAny >>= bTextAutoGrowHeight;
+ }
+ catch (...)
+ {
+ }
+
+ pDrawing->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
+ }
+
+ pDrawing->endElementNS(XML_a, XML_bodyPr);
+
+ {
+ pDrawing->startElementNS(XML_a, XML_p);
+ pDrawing->startElementNS(XML_a, XML_r);
+ pDrawing->startElementNS(XML_a, XML_t);
+ pDrawing->write(msLabel);
+ pDrawing->endElementNS(XML_a, XML_t);
+ pDrawing->endElementNS(XML_a, XML_r);
+ pDrawing->endElementNS(XML_a, XML_p);
+ }
+
+ pDrawing->endElement(FSNS(XML_xdr, XML_txBody));
+ }
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_sp));
+ pDrawing->singleElement(FSNS(XML_xdr, XML_clientData));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_twoCellAnchor));
+ pDrawing->endElement( FSNS( XML_mc, XML_Choice ) );
+ pDrawing->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+}
+
+// output into ctrlProp1.xml
+OUString XclExpTbxControlObj::SaveControlPropertiesXml(XclExpXmlStream& rStrm) const
+{
+ OUString sIdFormControlPr;
+
+ switch (mnObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ {
+ const sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId();
+ sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "ctrlProps/ctrlProps", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "ctrlProps/ctrlProps", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.ms-excel.controlproperties+xml",
+ oox::getRelationship(Relationship::CTRLPROP),
+ &sIdFormControlPr );
+
+ rStrm.PushStream( pFormControl );
+ // checkbox
+ // <formControlPr
+ // xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
+ // objectType="CheckBox" checked="Checked" lockText="1" noThreeD="1"/>
+ //
+ pFormControl->write("<formControlPr xmlns=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" objectType=\"CheckBox\"");
+ if (mnState == EXC_OBJ_CHECKBOX_CHECKED)
+ pFormControl->write(" checked=\"Checked\"");
+
+ pFormControl->write(" autoLine=\"false\"");
+
+ if (mbPrint)
+ pFormControl->write(" print=\"true\"");
+ else
+ pFormControl->write(" print=\"false\"");
+
+ if (mxCellLinkAddress.IsValid())
+ {
+ OUString aCellLink = mxCellLinkAddress.Format(ScRefFlags::ADDR_ABS,
+ &GetDoc(),
+ ScAddress::Details(::formula::FormulaGrammar::CONV_XL_A1));
+
+ // "Sheet1!$C$5"
+ pFormControl->write(" fmlaLink=\"");
+ if (aCellLink.indexOf('!') < 0)
+ {
+ pFormControl->write(GetTabInfo().GetScTabName(mxCellLinkAddress.Tab()));
+ pFormControl->write("!");
+ }
+ pFormControl->write(aCellLink);
+ pFormControl->write("\"");
+ }
+
+ pFormControl->write(" lockText=\"1\" noThreeD=\"1\"/>");
+ rStrm.PopStream();
+
+ break;
+ }
+ case EXC_OBJTYPE_BUTTON:
+ {
+ sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId();
+ sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/", "ctrlProps/ctrlProps", nDrawing),
+ XclXmlUtils::GetStreamName("../", "ctrlProps/ctrlProps", nDrawing),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.ms-excel.controlproperties+xml",
+ oox::getRelationship(Relationship::CTRLPROP), &sIdFormControlPr);
+
+ pFormControl->singleElement(XML_formControlPr, XML_xmlns,
+ rStrm.getNamespaceURL(OOX_NS(xls14Lst)), XML_objectType,
+ "Button", XML_lockText, "1");
+ break;
+ }
+ }
+
+ return sIdFormControlPr;
+}
+
+// output into sheet1.xml
+void XclExpTbxControlObj::SaveSheetXml(XclExpXmlStream& rStrm, const OUString& aIdFormControlPr) const
+{
+ switch (mnObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ {
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+
+ rWorksheet->startElement(
+ XML_control,
+ XML_shapeId, OString::number(mnShapeId),
+ FSNS(XML_r, XML_id), aIdFormControlPr,
+ XML_name, msLabel); // text to display with checkbox button
+
+ rWorksheet->write("<controlPr defaultSize=\"0\" locked=\"1\" autoFill=\"0\" autoLine=\"0\" autoPict=\"0\"");
+
+ if (mbPrint)
+ rWorksheet->write(" print=\"true\"");
+ else
+ rWorksheet->write(" print=\"false\"");
+
+ if (!msCtrlName.isEmpty())
+ {
+ rWorksheet->write(" altText=\"");
+ rWorksheet->write(msCtrlName); // alt text
+ rWorksheet->write("\"");
+ }
+
+ rWorksheet->write(">");
+
+ rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells, "false");
+ rWorksheet->startElement(XML_from);
+ lcl_WriteAnchorVertex(rWorksheet, maAreaFrom);
+ rWorksheet->endElement(XML_from);
+ rWorksheet->startElement(XML_to);
+ lcl_WriteAnchorVertex(rWorksheet, maAreaTo);
+ rWorksheet->endElement(XML_to);
+ rWorksheet->endElement( XML_anchor );
+
+ rWorksheet->write("</controlPr>");
+
+ rWorksheet->endElement(XML_control);
+ rWorksheet->endElement( FSNS( XML_mc, XML_Choice ) );
+ rWorksheet->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+
+ break;
+ }
+ case EXC_OBJTYPE_BUTTON:
+ {
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent), FSNS(XML_xmlns, XML_mc),
+ rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+
+ rWorksheet->startElement(XML_control, XML_shapeId, OString::number(mnShapeId),
+ FSNS(XML_r, XML_id), aIdFormControlPr, XML_name, msCtrlName);
+
+ OUString aMacroName = GetMacroName();
+ // Omit the macro attribute if it would be empty.
+ rWorksheet->startElement(XML_controlPr, XML_defaultSize, "0", XML_print,
+ mbPrint ? "true" : "false", XML_autoFill, "0", XML_autoPict,
+ "0", XML_macro, sax_fastparser::UseIf(aMacroName, !aMacroName.isEmpty()));
+
+ rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells,
+ "false");
+
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ tools::Rectangle aAreaFrom;
+ tools::Rectangle aAreaTo;
+ lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo,
+ /*bInEMU=*/true);
+
+ rWorksheet->startElement(XML_from);
+ lcl_WriteAnchorVertex(rWorksheet, aAreaFrom);
+ rWorksheet->endElement(XML_from);
+ rWorksheet->startElement(XML_to);
+ lcl_WriteAnchorVertex(rWorksheet, aAreaTo);
+ rWorksheet->endElement(XML_to);
+ rWorksheet->endElement(XML_anchor);
+
+ rWorksheet->endElement(XML_controlPr);
+
+ rWorksheet->endElement(XML_control);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+ break;
+ }
+ }
+}
+
+//#endif
+
+XclExpChartObj::XclExpChartObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor, ScDocument* pDoc ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_CHART ),
+ XclExpRoot( rObjMgr.GetRoot() ), mxShape( xShape ),
+ mpDoc(pDoc)
+{
+ // create the MSODRAWING record contents for the chart object
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x0800004E );
+ aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x0800004D );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 );
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x0800004D );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00020000 );
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 );
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape );
+ ImplWriteAnchor( pSdrObj, pChildAnchor );
+
+ // client data (the following OBJ record)
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData );
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // load the chart OLE object
+ if( SdrOle2Obj* pSdrOleObj = dynamic_cast< SdrOle2Obj* >( pSdrObj ) )
+ (void)svt::EmbeddedObjectRef::TryRunningState(pSdrOleObj->GetObjRef());
+
+ // create the chart substream object
+ ScfPropertySet aShapeProp( xShape );
+ css::awt::Rectangle aBoundRect;
+ aShapeProp.GetProperty( aBoundRect, "BoundRect" );
+ tools::Rectangle aChartRect( Point( aBoundRect.X, aBoundRect.Y ), Size( aBoundRect.Width, aBoundRect.Height ) );
+ mxChart = std::make_shared<XclExpChart>(GetRoot(), GetChartDoc(), aChartRect);
+}
+
+XclExpChartObj::~XclExpChartObj()
+{
+}
+
+void XclExpChartObj::Save( XclExpStream& rStrm )
+{
+ // content of OBJ record
+ XclObj::Save( rStrm );
+ // chart substream
+ mxChart->Save( rStrm );
+}
+
+void XclExpChartObj::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ // FIXME: two cell? it seems the two cell anchor is incorrect.
+ pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor
+ XML_editAs, "oneCell" );
+ Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY );
+ if (xPropSet.is())
+ {
+ XclObjAny::WriteFromTo( rStrm, mxShape, GetTab() );
+ ChartExport aChartExport(XML_xdr, pDrawing, GetChartDoc(), &rStrm, drawingml::DOCUMENT_XLSX);
+ auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc);
+ aChartExport.SetURLTranslator(pURLTransformer);
+ sal_Int32 nChartCount = oox::drawingml::DrawingML::getNewChartUniqueId();
+ sal_Int32 nID = rStrm.GetUniqueId();
+ aChartExport.WriteChartObj( mxShape, nID, nChartCount );
+ // TODO: get the correcto chart number
+ }
+
+ pDrawing->singleElement( FSNS( XML_xdr, XML_clientData)
+ // OOXTODO: XML_fLocksWithSheet
+ // OOXTODO: XML_fPrintsWithSheet
+ );
+ pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) );
+}
+
+css::uno::Reference<css::chart::XChartDocument> XclExpChartObj::GetChartDoc() const
+{
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if (!pObj || pObj->GetObjIdentifier() != SdrObjKind::OLE2)
+ return {};
+ // May load here - makes sure that we are working with actually loaded OLE object
+ return css::uno::Reference<css::chart::XChartDocument>(
+ static_cast<SdrOle2Obj*>(pObj)->getXModel(), css::uno::UNO_QUERY);
+}
+
+XclExpNote::XclExpNote(const XclExpRoot& rRoot, const ScAddress& rScPos,
+ const ScPostIt* pScNote, std::u16string_view rAddText)
+ : XclExpRecord(EXC_ID_NOTE)
+ , mrRoot(rRoot)
+ , maScPos(rScPos)
+ , mnObjId(EXC_OBJ_INVALID_ID)
+ , mbVisible(pScNote && pScNote->IsCaptionShown())
+ , meTHA(SDRTEXTHORZADJUST_LEFT)
+ , meTVA(SDRTEXTVERTADJUST_TOP)
+ , mbAutoScale(false)
+ , mbLocked(false)
+ , mbAutoFill(false)
+ , mbColHidden(false)
+ , mbRowHidden(false)
+{
+ // get the main note text
+ OUString aNoteText;
+ if( pScNote )
+ aNoteText = pScNote->GetText();
+
+ // append additional text
+ aNoteText = ScGlobal::addToken( aNoteText, rAddText, '\n', 2 );
+
+ // initialize record dependent on BIFF type
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF5:
+ maNoteText = OUStringToOString(aNoteText, rRoot.GetTextEncoding());
+ break;
+
+ case EXC_BIFF8:
+ {
+ // TODO: additional text
+ if( pScNote )
+ {
+ if( SdrCaptionObj* pCaption = pScNote->GetOrCreateCaption( maScPos ) )
+ {
+ lcl_GetFromTo( rRoot, pCaption->GetLogicRect(), maScPos.Tab(), maCommentFrom, maCommentTo );
+ if( const OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() )
+ mnObjId = rRoot.GetObjectManager().AddObj( std::make_unique<XclObjComment>( rRoot.GetObjectManager(), pCaption->GetLogicRect(), pOPO->GetTextObject(), pCaption, mbVisible, maScPos, maCommentFrom, maCommentTo ) );
+
+ SfxItemSet aItemSet = pCaption->GetMergedItemSet();
+ meTVA = pCaption->GetTextVerticalAdjust();
+ meTHA = pCaption->GetTextHorizontalAdjust();
+ mbAutoScale = pCaption->GetFitToSize() != drawing::TextFitToSizeType_NONE;
+ mbLocked = pCaption->IsMoveProtect() || pCaption->IsResizeProtect();
+
+ // AutoFill style would change if Postit.cxx object creation values are changed
+ OUString aCol(aItemSet.Get(XATTR_FILLCOLOR).GetValue());
+ mbAutoFill = aCol.isEmpty() && (aItemSet.Get(XATTR_FILLSTYLE).GetValue() == drawing::FillStyle_SOLID);
+ mbRowHidden = (rRoot.GetDoc().RowHidden(maScPos.Row(),maScPos.Tab()));
+ mbColHidden = (rRoot.GetDoc().ColHidden(maScPos.Col(),maScPos.Tab()));
+ }
+ // stAuthor (variable): An XLUnicodeString that specifies the name of the comment
+ // author. String length MUST be greater than or equal to 1 and less than or equal
+ // to 54.
+ if( pScNote->GetAuthor().isEmpty() )
+ maAuthor = XclExpString( " " );
+ else
+ maAuthor = XclExpString( pScNote->GetAuthor(), XclStrFlags::NONE, 54 );
+
+ if (const EditTextObject *pEditObj = pScNote->GetEditTextObject())
+ mpNoteContents = XclExpStringHelper::CreateString( rRoot, *pEditObj );
+ }
+
+ SetRecSize( 9 + maAuthor.GetSize() );
+ }
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpNote::Save( XclExpStream& rStrm )
+{
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ // write the NOTE record directly, there may be the need to create more than one
+ const char* pcBuffer = maNoteText.getStr();
+ sal_uInt16 nCharsLeft = static_cast< sal_uInt16 >( maNoteText.getLength() );
+
+ while( nCharsLeft )
+ {
+ sal_uInt16 nWriteChars = ::std::min( nCharsLeft, EXC_NOTE5_MAXLEN );
+
+ rStrm.StartRecord( EXC_ID_NOTE, 6 + nWriteChars );
+ if( pcBuffer == maNoteText.getStr() )
+ {
+ // first record: row, col, length of complete text
+ rStrm << static_cast< sal_uInt16 >( maScPos.Row() )
+ << static_cast< sal_uInt16 >( maScPos.Col() )
+ << nCharsLeft; // still contains full length
+ }
+ else
+ {
+ // next records: -1, 0, length of current text segment
+ rStrm << sal_uInt16( 0xFFFF )
+ << sal_uInt16( 0 )
+ << nWriteChars;
+ }
+ rStrm.Write( pcBuffer, nWriteChars );
+ rStrm.EndRecord();
+
+ pcBuffer += nWriteChars;
+ nCharsLeft = nCharsLeft - nWriteChars;
+ }
+ }
+ break;
+
+ case EXC_BIFF8:
+ if( mnObjId != EXC_OBJ_INVALID_ID )
+ XclExpRecord::Save( rStrm );
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpNote::WriteBody( XclExpStream& rStrm )
+{
+ // BIFF5/BIFF7 is written separately
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
+
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_NOTE_VISIBLE, mbVisible );
+
+ rStrm << static_cast< sal_uInt16 >( maScPos.Row() )
+ << static_cast< sal_uInt16 >( maScPos.Col() )
+ << nFlags
+ << mnObjId
+ << maAuthor
+ << sal_uInt8( 0 );
+}
+
+void XclExpNote::WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr rComments = rStrm.GetCurrentStream();
+
+ rComments->startElement( XML_comment,
+ XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), maScPos),
+ XML_authorId, OString::number(nAuthorId)
+ // OOXTODO: XML_guid
+ );
+ rComments->startElement(XML_text);
+ // OOXTODO: phoneticPr, rPh, r
+ if( mpNoteContents )
+ mpNoteContents->WriteXml( rStrm );
+ rComments->endElement( XML_text );
+
+/*
+ Export of commentPr is disabled, since the current (Oct 2010)
+ version of MSO 2010 doesn't yet support commentPr
+ */
+#if 1//def XLSX_OOXML_FUTURE
+ if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 )
+ {
+ rComments->startElement(FSNS(XML_mc, XML_AlternateContent));
+ rComments->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "v2");
+ rComments->startElement( XML_commentPr,
+ XML_autoFill, ToPsz( mbAutoFill ),
+ XML_autoScale, ToPsz( mbAutoScale ),
+ XML_colHidden, ToPsz( mbColHidden ),
+ XML_locked, ToPsz( mbLocked ),
+ XML_rowHidden, ToPsz( mbRowHidden ),
+ XML_textHAlign, ToHorizAlign( meTHA ),
+ XML_textVAlign, ToVertAlign( meTVA ) );
+ rComments->startElement(XML_anchor, XML_moveWithCells, "false", XML_sizeWithCells, "false");
+ rComments->startElement(FSNS(XML_xdr, XML_from));
+ lcl_WriteAnchorVertex( rComments, maCommentFrom );
+ rComments->endElement( FSNS( XML_xdr, XML_from ) );
+ rComments->startElement(FSNS(XML_xdr, XML_to));
+ lcl_WriteAnchorVertex( rComments, maCommentTo );
+ rComments->endElement( FSNS( XML_xdr, XML_to ) );
+ rComments->endElement( XML_anchor );
+ rComments->endElement( XML_commentPr );
+
+ rComments->endElement( FSNS( XML_mc, XML_Choice ) );
+ rComments->startElement(FSNS(XML_mc, XML_Fallback));
+ // Any fallback code ?
+ rComments->endElement( FSNS( XML_mc, XML_Fallback ) );
+ rComments->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+ }
+#endif
+ rComments->endElement( XML_comment );
+}
+
+XclMacroHelper::XclMacroHelper( const XclExpRoot& rRoot ) :
+ XclExpControlHelper( rRoot )
+{
+}
+
+XclMacroHelper::~XclMacroHelper()
+{
+}
+
+void XclMacroHelper::WriteMacroSubRec( XclExpStream& rStrm )
+{
+ if( mxMacroLink )
+ WriteFormulaSubRec( rStrm, EXC_ID_OBJMACRO, *mxMacroLink );
+}
+
+const OUString& XclMacroHelper::GetMacroName() const { return maMacroName; }
+
+bool
+XclMacroHelper::SetMacroLink( const ScriptEventDescriptor& rEvent, const XclTbxEventType& nEventType )
+{
+ maMacroName = XclControlHelper::ExtractFromMacroDescriptor(rEvent, nEventType);
+ if (!maMacroName.isEmpty())
+ {
+ return SetMacroLink(maMacroName);
+ }
+ return false;
+}
+
+bool
+XclMacroHelper::SetMacroLink( const OUString& rMacroName )
+{
+ // OOXML documents do not store any defined name for VBA macros (while BIFF documents do).
+ bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007;
+ if (!rMacroName.isEmpty() && !bOOXML)
+ {
+ sal_uInt16 nExtSheet = GetLocalLinkManager().FindExtSheet( EXC_EXTSH_OWNDOC );
+ sal_uInt16 nNameIdx
+ = GetNameManager().InsertMacroCall(rMacroName, /*bVBasic=*/true, /*bFunc=*/false);
+ mxMacroLink = GetFormulaCompiler().CreateNameXFormula( nExtSheet, nNameIdx );
+ return true;
+ }
+ return false;
+}
+
+XclExpShapeObj::XclExpShapeObj( XclExpObjectManager& rRoot, css::uno::Reference< css::drawing::XShape > const & xShape, ScDocument* pDoc ) :
+ XclObjAny( rRoot, xShape, pDoc ),
+ XclMacroHelper( rRoot )
+{
+ if (SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape))
+ {
+ ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pSdrObj );
+ if ( pInfo && !pInfo->GetMacro().isEmpty() )
+// FIXME ooo330-m2: XclControlHelper::GetXclMacroName was removed in upstream sources; they started to call XclTools::GetXclMacroName instead; is this enough? it has only one parameter
+// SetMacroLink( XclControlHelper::GetXclMacroName( pInfo->GetMacro(), rRoot.GetDocShell() ) );
+ SetMacroLink( XclTools::GetXclMacroName( pInfo->GetMacro() ) );
+ }
+}
+
+XclExpShapeObj::~XclExpShapeObj()
+{
+}
+
+void XclExpShapeObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ XclObjAny::WriteSubRecs( rStrm );
+ WriteMacroSubRec( rStrm );
+}
+
+XclExpComments::XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes )
+ : mnTab( nTab ), mrNotes( rNotes )
+{
+}
+
+void XclExpComments::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mrNotes.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr rComments = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "comments", mnTab + 1 ),
+ XclXmlUtils::GetStreamName( "../", "comments", mnTab + 1 ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
+ oox::getRelationship(Relationship::COMMENTS));
+ rStrm.PushStream( rComments );
+
+ if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 )
+ rComments->startElement( XML_comments,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)),
+ FSNS(XML_xmlns, XML_v2), rStrm.getNamespaceURL(OOX_NS(mceTest)),
+ FSNS( XML_mc, XML_Ignorable ), "v2" );
+ else
+ rComments->startElement( XML_comments,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)) );
+
+ rComments->startElement(XML_authors);
+
+ typedef std::set<OUString> Authors;
+ Authors aAuthors;
+
+ size_t nNotes = mrNotes.GetSize();
+ for( size_t i = 0; i < nNotes; ++i )
+ {
+ aAuthors.insert( XclXmlUtils::ToOUString( mrNotes.GetRecord( i )->GetAuthor() ) );
+ }
+
+ for( const auto& rAuthor : aAuthors )
+ {
+ rComments->startElement(XML_author);
+ rComments->writeEscaped( rAuthor );
+ rComments->endElement( XML_author );
+ }
+
+ rComments->endElement( XML_authors );
+ rComments->startElement(XML_commentList);
+
+ Authors::const_iterator aAuthorsBegin = aAuthors.begin();
+ for( size_t i = 0; i < nNotes; ++i )
+ {
+ XclExpRecordList< XclExpNote >::RecordRefType xNote = mrNotes.GetRecord( i );
+ Authors::const_iterator aAuthor = aAuthors.find(
+ XclXmlUtils::ToOUString( xNote->GetAuthor() ) );
+ sal_Int32 nAuthorId = distance( aAuthorsBegin, aAuthor );
+ xNote->WriteXml( nAuthorId, rStrm );
+ }
+
+ rComments->endElement( XML_commentList );
+ rComments->endElement( XML_comments );
+
+ rStrm.PopStream();
+}
+
+// object manager =============================================================
+
+XclExpObjectManager::XclExpObjectManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ InitStream( true );
+ mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mpDffStrm );
+}
+
+XclExpObjectManager::XclExpObjectManager( const XclExpObjectManager& rParent ) :
+ XclExpRoot( rParent.GetRoot() )
+{
+ InitStream( false );
+ mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mpDffStrm, rParent.mxEscherEx.get() );
+}
+
+XclExpObjectManager::~XclExpObjectManager()
+{
+}
+
+XclExpDffAnchorBase* XclExpObjectManager::CreateDffAnchor() const
+{
+ return new XclExpDffSheetAnchor( GetRoot() );
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::CreateDrawingGroup()
+{
+ return new XclExpMsoDrawingGroup( *mxEscherEx );
+}
+
+void XclExpObjectManager::StartSheet()
+{
+ mxObjList = new XclExpObjList( GetRoot(), *mxEscherEx );
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const SdrPage* pSdrPage )
+{
+ if( pSdrPage )
+ mxEscherEx->AddSdrPage( *pSdrPage, GetOutput() != EXC_OUTPUT_BINARY );
+ // the first dummy object may still be open
+ OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" );
+ while( mxEscherEx->GetGroupLevel() )
+ mxEscherEx->LeaveGroup();
+ mxObjList->EndSheet();
+ return mxObjList;
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const Reference< XShapes >& rxShapes )
+{
+ if( rxShapes.is() )
+ mxEscherEx->AddUnoShapes( rxShapes, GetOutput() != EXC_OUTPUT_BINARY );
+ // the first dummy object may still be open
+ OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" );
+ while( mxEscherEx->GetGroupLevel() )
+ mxEscherEx->LeaveGroup();
+ mxObjList->EndSheet();
+ return mxObjList;
+}
+
+void XclExpObjectManager::EndDocument()
+{
+ mxEscherEx->EndDocument();
+}
+
+XclExpMsoDrawing* XclExpObjectManager::GetMsodrawingPerSheet()
+{
+ return mxObjList->GetMsodrawingPerSheet();
+}
+
+bool XclExpObjectManager::HasObj() const
+{
+ return !mxObjList->empty();
+}
+
+sal_uInt16 XclExpObjectManager::AddObj( std::unique_ptr<XclObj> pObjRec )
+{
+ return mxObjList->Add( std::move(pObjRec) );
+}
+
+std::unique_ptr<XclObj> XclExpObjectManager::RemoveLastObj()
+{
+ return mxObjList->pop_back();
+}
+
+void XclExpObjectManager::InitStream( bool bTempFile )
+{
+ if( bTempFile )
+ {
+ moTempFile.emplace();
+ mpDffStrm = moTempFile->GetStream( StreamMode::STD_READWRITE );
+ }
+
+ if( !mpDffStrm )
+ {
+ mpBackupStrm = std::make_unique<SvMemoryStream>();
+ mpDffStrm = mpBackupStrm.get();
+ }
+
+ mpDffStrm->SetEndian( SvStreamEndian::LITTLE );
+}
+
+XclExpEmbeddedObjectManager::XclExpEmbeddedObjectManager(
+ const XclExpObjectManager& rParent, const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) :
+ XclExpObjectManager( rParent ),
+ maPageSize( rPageSize ),
+ mnScaleX( nScaleX ),
+ mnScaleY( nScaleY )
+{
+}
+
+XclExpDffAnchorBase* XclExpEmbeddedObjectManager::CreateDffAnchor() const
+{
+ return new XclExpDffEmbeddedAnchor( GetRoot(), maPageSize, mnScaleX, mnScaleY );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeextlst.cxx b/sc/source/filter/excel/xeextlst.cxx
new file mode 100644
index 0000000000..daff4bd07e
--- /dev/null
+++ b/sc/source/filter/excel/xeextlst.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/.
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <utility>
+#include <xeextlst.hxx>
+#include <xeroot.hxx>
+#include <xestyle.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace ::oox;
+
+XclExpExt::XclExpExt( const XclExpRoot& rRoot ):
+ XclExpRoot(rRoot)
+{
+}
+
+XclExtLst::XclExtLst( const XclExpRoot& rRoot ):
+ XclExpRoot(rRoot)
+{
+}
+
+XclExpExtNegativeColor::XclExpExtNegativeColor( const Color& rColor ):
+ maColor(rColor)
+{
+}
+
+void XclExpExtNegativeColor::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElementNS( XML_x14, XML_negativeFillColor,
+ XML_rgb, XclXmlUtils::ToOString(maColor) );
+}
+
+XclExpExtAxisColor::XclExpExtAxisColor( const Color& rColor ):
+ maAxisColor(rColor)
+{
+}
+
+void XclExpExtAxisColor::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElementNS( XML_x14, XML_axisColor,
+ XML_rgb, XclXmlUtils::ToOString(maAxisColor) );
+}
+
+XclExpExtIcon::XclExpExtIcon(const XclExpRoot& rRoot, const std::pair<ScIconSetType, sal_Int32>& rCustomEntry):
+ XclExpRoot(rRoot),
+ nIndex(rCustomEntry.second)
+{
+ pIconSetName = ScIconSetFormat::getIconSetName(rCustomEntry.first);
+}
+
+void XclExpExtIcon::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ if (nIndex == -1)
+ {
+ nIndex = 0;
+ pIconSetName = "NoIcons";
+ }
+
+ rWorksheet->singleElementNS(XML_x14, XML_cfIcon,
+ XML_iconSet, pIconSetName,
+ XML_iconId, OString::number(nIndex));
+}
+
+XclExpExtCfvo::XclExpExtCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rSrcPos, bool bFirst ):
+ XclExpRoot(rRoot),
+ meType(rEntry.GetType()),
+ mbFirst(bFirst)
+{
+ if( rEntry.GetType() == COLORSCALE_FORMULA )
+ {
+ const ScTokenArray* pArr = rEntry.GetFormula();
+ OUString aFormula;
+ if(pArr)
+ {
+ aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), rSrcPos, pArr);
+ }
+ maValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ maValue = OString::number(rEntry.GetValue());
+}
+
+namespace {
+
+const char* getColorScaleType( ScColorScaleEntryType eType, bool bFirst )
+{
+ switch(eType)
+ {
+ case COLORSCALE_MIN:
+ return "min";
+ case COLORSCALE_MAX:
+ return "max";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_AUTO:
+ if(bFirst)
+ return "autoMin";
+ else
+ return "autoMax";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ default:
+ break;
+ }
+ return "num";
+}
+
+}
+
+void XclExpExtCfvo::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS(XML_x14, XML_cfvo, XML_type, getColorScaleType(meType, mbFirst));
+
+ if (meType == COLORSCALE_FORMULA ||
+ meType == COLORSCALE_PERCENT ||
+ meType == COLORSCALE_PERCENTILE ||
+ meType == COLORSCALE_VALUE)
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+ rWorksheet->writeEscaped(maValue.getStr());
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_cfvo);
+}
+
+XclExpExtCF::XclExpExtCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormat ):
+ XclExpRoot(rRoot),
+ mrFormat(rFormat)
+{
+}
+
+namespace {
+
+bool RequiresFixedFormula(ScConditionMode eMode)
+{
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
+{
+ OStringBuffer aBuffer;
+ XclXmlUtils::ToOString(aBuffer, rAddress);
+ OString aPos = aBuffer.makeStringAndClear();
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ return OString("LEFT(" + aPos + ",LEN(" + rText + "))=" + rText);
+ case ScConditionMode::EndsWith:
+ return OString("RIGHT(" + aPos + ",LEN(" + rText + "))=" + rText);
+ case ScConditionMode::ContainsText:
+ return OString(OString::Concat("NOT(ISERROR(SEARCH(") + rText + "," + aPos + ")))");
+ case ScConditionMode::NotContainsText:
+ return OString(OString::Concat("ISERROR(SEARCH(") + rText + "," + aPos + "))");
+ default:
+ break;
+ }
+
+ return ""_ostr;
+}
+
+}
+
+void XclExpExtCF::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString aStyleName = mrFormat.GetStyle();
+ SfxStyleSheetBasePool* pPool = GetDoc().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyle = pPool->Find(aStyleName, SfxStyleFamily::Para);
+ SfxItemSet& rSet = pStyle->GetItemSet();
+
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormat.CreateFlatCopiedTokenArray(0));
+ aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormat.GetValidSrcPos(), pTokenArray.get());
+
+ std::unique_ptr<XclExpColor> pColor(new XclExpColor);
+ if(!pColor->FillFromItemSet( rSet ))
+ pColor.reset();
+
+ std::unique_ptr<XclExpCellBorder> pBorder(new XclExpCellBorder);
+ if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
+ pBorder.reset();
+
+ std::unique_ptr<XclExpCellAlign> pAlign(new XclExpCellAlign);
+ if (!pAlign->FillFromItemSet(*this, rSet, false, GetBiff()))
+ pAlign.reset();
+
+ std::unique_ptr<XclExpCellProt> pCellProt(new XclExpCellProt);
+ if (!pCellProt->FillFromItemSet( rSet ))
+ pCellProt.reset();
+
+ std::unique_ptr<XclExpDxfFont> pFont(new XclExpDxfFont(GetRoot(), rSet));
+
+ std::unique_ptr<XclExpNumFmt> pNumFormat;
+ if( const SfxUInt32Item* pPoolItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nScNumFmt = pPoolItem->GetValue();
+ XclExpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
+ sal_uInt32 nXclNumFmt = rNumFmtBuffer.Insert(nScNumFmt);
+ pNumFormat.reset(new XclExpNumFmt(nScNumFmt, nXclNumFmt, rNumFmtBuffer.GetFormatCode(nScNumFmt)));
+ }
+
+ XclExpDxf rDxf( GetRoot(),
+ std::move(pAlign),
+ std::move(pBorder),
+ std::move(pFont),
+ std::move(pNumFormat),
+ std::move(pCellProt),
+ std::move(pColor) );
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ ScConditionMode eOperation = mrFormat.GetOperation();
+ if (RequiresFixedFormula(eOperation))
+ {
+ ScAddress aFixedFormulaPos = mrFormat.GetValidSrcPos();
+ OString aFixedFormulaText = aFormula.toUtf8();
+ OString aFixedFormula = GetFixedFormula(eOperation, aFixedFormulaPos, aFixedFormulaText);
+ rWorksheet->startElementNS( XML_xm, XML_f );
+ rWorksheet->writeEscaped(aFixedFormula.getStr());
+ rWorksheet->endElementNS( XML_xm, XML_f );
+
+ rWorksheet->startElementNS( XML_xm, XML_f );
+ rWorksheet->writeEscaped( aFormula );
+ rWorksheet->endElementNS( XML_xm, XML_f );
+ rDxf.SaveXmlExt(rStrm);
+ }
+ else
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+ rWorksheet->writeEscaped(aFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ rDxf.SaveXmlExt(rStrm);
+ }
+}
+
+XclExpExtDataBar::XclExpExtDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, const ScAddress& rPos ):
+ XclExpRoot(rRoot)
+{
+ const ScDataBarFormatData& rFormatData = *rFormat.GetDataBarData();
+ mpLowerLimit.reset(new XclExpExtCfvo(*this, *rFormatData.mpLowerLimit, rPos, true));
+ mpUpperLimit.reset(new XclExpExtCfvo(*this, *rFormatData.mpUpperLimit, rPos, false));
+ if (rFormatData.mxNegativeColor)
+ mpNegativeColor.reset(new XclExpExtNegativeColor(*rFormatData.mxNegativeColor));
+ else
+ mpNegativeColor.reset( new XclExpExtNegativeColor( rFormatData.maPositiveColor ) );
+ mpAxisColor.reset( new XclExpExtAxisColor( rFormatData.maAxisColor ) );
+
+ meAxisPosition = rFormatData.meAxisPosition;
+ mbGradient = rFormatData.mbGradient;
+ mnMinLength = rFormatData.mnMinLength;
+ mnMaxLength = rFormatData.mnMaxLength;
+}
+
+namespace {
+
+const char* getAxisPosition(databar::ScAxisPosition eAxisPosition)
+{
+ switch(eAxisPosition)
+ {
+ case databar::NONE:
+ return "none";
+ case databar::AUTOMATIC:
+ return "automatic";
+ case databar::MIDDLE:
+ return "middle";
+ }
+ return "";
+}
+
+const char* GetOperatorString(ScConditionMode eMode)
+{
+ const char* pRet = nullptr;
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ pRet = "equal";
+ break;
+ case ScConditionMode::Less:
+ pRet = "lessThan";
+ break;
+ case ScConditionMode::Greater:
+ pRet = "greaterThan";
+ break;
+ case ScConditionMode::EqLess:
+ pRet = "lessThanOrEqual";
+ break;
+ case ScConditionMode::EqGreater:
+ pRet = "greaterThanOrEqual";
+ break;
+ case ScConditionMode::NotEqual:
+ pRet = "notEqual";
+ break;
+ case ScConditionMode::Between:
+ pRet = "between";
+ break;
+ case ScConditionMode::NotBetween:
+ pRet = "notBetween";
+ break;
+ case ScConditionMode::Duplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::NotDuplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::BeginsWith:
+ pRet = "beginsWith";
+ break;
+ case ScConditionMode::EndsWith:
+ pRet = "endsWith";
+ break;
+ case ScConditionMode::ContainsText:
+ pRet = "containsText";
+ break;
+ case ScConditionMode::NotContainsText:
+ pRet = "notContains";
+ break;
+ case ScConditionMode::Direct:
+ break;
+ case ScConditionMode::NONE:
+ default:
+ break;
+ }
+ return pRet;
+}
+
+const char* GetTypeString(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Direct:
+ return "expression";
+ case ScConditionMode::BeginsWith:
+ return "beginsWith";
+ case ScConditionMode::EndsWith:
+ return "endsWith";
+ case ScConditionMode::ContainsText:
+ return "containsText";
+ case ScConditionMode::NotContainsText:
+ return "notContainsText";
+ default:
+ return "cellIs";
+ }
+}
+
+}
+
+void XclExpExtDataBar::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_dataBar,
+ XML_minLength, OString::number(mnMinLength),
+ XML_maxLength, OString::number(mnMaxLength),
+ XML_axisPosition, getAxisPosition(meAxisPosition),
+ XML_gradient, ToPsz(mbGradient) );
+
+ mpLowerLimit->SaveXml( rStrm );
+ mpUpperLimit->SaveXml( rStrm );
+ mpNegativeColor->SaveXml( rStrm );
+ mpAxisColor->SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_dataBar );
+}
+
+XclExpExtIconSet::XclExpExtIconSet(const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, const ScAddress& rPos):
+ XclExpRoot(rRoot)
+{
+ const ScIconSetFormatData& rData = *rFormat.GetIconSetData();
+ for (auto const& itr : rData.m_Entries)
+ {
+ maCfvos.AppendNewRecord(new XclExpExtCfvo(*this, *itr, rPos, false));
+ }
+ mbCustom = rData.mbCustom;
+ mbReverse = rData.mbReverse;
+ mbShowValue = rData.mbShowValue;
+ mpIconSetName = ScIconSetFormat::getIconSetName(rData.eIconSetType);
+
+ if (mbCustom)
+ {
+ for (const auto& rItem : rData.maCustomVector)
+ {
+ maCustom.AppendNewRecord(new XclExpExtIcon(*this, rItem));
+ }
+ }
+}
+
+void XclExpExtIconSet::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElementNS(XML_x14, XML_iconSet,
+ XML_iconSet, mpIconSetName,
+ XML_custom, sax_fastparser::UseIf(ToPsz10(mbCustom), mbCustom),
+ XML_reverse, ToPsz10(mbReverse),
+ XML_showValue, ToPsz10(mbShowValue));
+
+ maCfvos.SaveXml(rStrm);
+
+ if (mbCustom)
+ {
+ maCustom.SaveXml(rStrm);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_iconSet);
+}
+
+XclExpExtCfRule::XclExpExtCfRule( const XclExpRoot& rRoot, const ScFormatEntry& rFormat, const ScAddress& rPos, OString aId, sal_Int32 nPriority ):
+ XclExpRoot(rRoot),
+ maId(std::move(aId)),
+ pType(nullptr),
+ mnPriority(nPriority),
+ mOperator(nullptr)
+{
+ switch (rFormat.GetType())
+ {
+ case ScFormatEntry::Type::Databar:
+ {
+ const ScDataBarFormat& rDataBar = static_cast<const ScDataBarFormat&>(rFormat);
+ mxEntry = new XclExpExtDataBar( *this, rDataBar, rPos );
+ pType = "dataBar";
+ }
+ break;
+ case ScFormatEntry::Type::Iconset:
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(rFormat);
+ mxEntry = new XclExpExtIconSet(*this, rIconSet, rPos);
+ pType = "iconSet";
+ }
+ break;
+ case ScFormatEntry::Type::ExtCondition:
+ {
+ const ScCondFormatEntry& rCondFormat = static_cast<const ScCondFormatEntry&>(rFormat);
+ mxEntry = new XclExpExtCF(*this, rCondFormat);
+ pType = GetTypeString(rCondFormat.GetOperation());
+ mOperator = GetOperatorString( rCondFormat.GetOperation() );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void XclExpExtCfRule::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (!mxEntry)
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_cfRule,
+ XML_type, pType,
+ XML_priority, sax_fastparser::UseIf(OString::number(mnPriority + 1), mnPriority != -1),
+ XML_operator, mOperator,
+ XML_id, maId );
+
+ mxEntry->SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_cfRule );
+
+}
+
+XclExpExtConditionalFormatting::XclExpExtConditionalFormatting( const XclExpRoot& rRoot,
+ std::vector<XclExpExtCondFormatData>& rData, ScRangeList aRange):
+ XclExpRoot(rRoot),
+ maRange(std::move(aRange))
+{
+ ScAddress aAddr = maRange.front().aStart;
+ for (const auto& rItem : rData)
+ {
+ const ScFormatEntry* pEntry = rItem.pEntry;
+ switch (pEntry->GetType())
+ {
+ case ScFormatEntry::Type::Iconset:
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pEntry);
+ bool bNeedsExt = false;
+ switch (rIconSet.GetIconSetData()->eIconSetType)
+ {
+ case IconSet_3Triangles:
+ case IconSet_3Smilies:
+ case IconSet_3ColorSmilies:
+ case IconSet_5Boxes:
+ case IconSet_3Stars:
+ bNeedsExt = true;
+ break;
+ default:
+ break;
+ }
+
+ if (rIconSet.GetIconSetData()->mbCustom)
+ bNeedsExt = true;
+
+ if (bNeedsExt)
+ {
+ maCfRules.AppendNewRecord(new XclExpExtCfRule(*this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ }
+ }
+ break;
+ case ScFormatEntry::Type::Databar:
+ maCfRules.AppendNewRecord(new XclExpExtCfRule( *this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ break;
+ case ScFormatEntry::Type::ExtCondition:
+ maCfRules.AppendNewRecord(new XclExpExtCfRule( *this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void XclExpExtConditionalFormatting::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_conditionalFormatting,
+ FSNS( XML_xmlns, XML_xm ), rStrm.getNamespaceURL(OOX_NS(xm)) );
+
+ maCfRules.SaveXml( rStrm );
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+ rWorksheet->write(XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRange));
+
+ rWorksheet->endElementNS( XML_xm, XML_sqref );
+
+ rWorksheet->endElementNS( XML_x14, XML_conditionalFormatting );
+}
+
+XclExpExtCalcPr::XclExpExtCalcPr( const XclExpRoot& rRoot, formula::FormulaGrammar::AddressConvention eConv ):
+ XclExpExt( rRoot )
+{
+ maURI = "{7626C862-2A13-11E5-B345-FEFF819CDC9F}"_ostr;
+
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_OOO:
+ maSyntax = "CalcA1"_ostr;
+ break;
+ case formula::FormulaGrammar::CONV_XL_A1:
+ maSyntax = "ExcelA1"_ostr;
+ break;
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ maSyntax = "ExcelR1C1"_ostr;
+ break;
+ case formula::FormulaGrammar::CONV_A1_XL_A1:
+ maSyntax = "CalcA1ExcelA1"_ostr;
+ break;
+ case formula::FormulaGrammar::CONV_UNSPECIFIED:
+ case formula::FormulaGrammar::CONV_ODF:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ case formula::FormulaGrammar::CONV_LOTUS_A1:
+ case formula::FormulaGrammar::CONV_LAST:
+ maSyntax = "Unspecified"_ostr;
+ break;
+ }
+}
+
+void XclExpExtCalcPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_ext,
+ FSNS(XML_xmlns, XML_loext), rStrm.getNamespaceURL(OOX_NS(loext)),
+ XML_uri, maURI );
+
+ rWorksheet->singleElementNS(XML_loext, XML_extCalcPr, XML_stringRefSyntax, maSyntax);
+
+ rWorksheet->endElement( XML_ext );
+}
+
+XclExpExtCondFormat::XclExpExtCondFormat( const XclExpRoot& rRoot ):
+ XclExpExt( rRoot )
+{
+ maURI = "{78C0D931-6437-407d-A8EE-F0AAD7539E65}"_ostr;
+}
+
+void XclExpExtCondFormat::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_ext,
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ XML_uri, maURI );
+
+ rWorksheet->startElementNS(XML_x14, XML_conditionalFormattings);
+
+ maCF.SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_conditionalFormattings );
+ rWorksheet->endElement( XML_ext );
+}
+
+void XclExpExtCondFormat::AddRecord( XclExpExtConditionalFormatting* pEntry )
+{
+ maCF.AppendRecord( pEntry );
+}
+
+void XclExtLst::SaveXml( XclExpXmlStream& rStrm )
+{
+ if(maExtEntries.IsEmpty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_extLst);
+
+ maExtEntries.SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_extLst );
+}
+
+void XclExtLst::AddRecord( XclExpExt* pEntry )
+{
+ maExtEntries.AppendRecord( pEntry );
+}
+
+XclExpExt* XclExtLst::GetItem( XclExpExtType eType )
+{
+ size_t n = maExtEntries.GetSize();
+ for( size_t i = 0; i < n; ++i )
+ {
+ if (maExtEntries.GetRecord( i )->GetType() == eType)
+ return maExtEntries.GetRecord( i );
+ }
+
+ return nullptr;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeformula.cxx b/sc/source/filter/excel/xeformula.cxx
new file mode 100644
index 0000000000..71f5056beb
--- /dev/null
+++ b/sc/source/filter/excel/xeformula.cxx
@@ -0,0 +1,2722 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <map>
+#include <addincol.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <externalrefmgr.hxx>
+#include <rangelst.hxx>
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <utility>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xestring.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+using namespace ::formula;
+
+// External reference log =====================================================
+
+XclExpRefLogEntry::XclExpRefLogEntry() :
+ mpUrl( nullptr ),
+ mpFirstTab( nullptr ),
+ mpLastTab( nullptr ),
+ mnFirstXclTab( EXC_TAB_DELETED ),
+ mnLastXclTab( EXC_TAB_DELETED )
+{
+}
+
+// Formula compiler ===========================================================
+
+namespace {
+
+/** Wrapper structure for a processed Calc formula token with additional
+ settings (whitespaces). */
+struct XclExpScToken
+{
+ const FormulaToken* mpScToken; /// Currently processed Calc token.
+ sal_uInt8 mnSpaces; /// Number of spaces before the Calc token.
+
+ explicit XclExpScToken() : mpScToken( nullptr ), mnSpaces( 0 ) {}
+ bool Is() const { return mpScToken != nullptr; }
+ StackVar GetType() const { return mpScToken ? mpScToken->GetType() : svUnknown; }
+ OpCode GetOpCode() const { return mpScToken ? mpScToken->GetOpCode() : ocNone; }
+};
+
+/** Effective token class conversion types. */
+enum XclExpClassConv
+{
+ EXC_CLASSCONV_ORG, /// Keep original class of the token.
+ EXC_CLASSCONV_VAL, /// Convert ARR tokens to VAL class (REF remains unchanged).
+ EXC_CLASSCONV_ARR /// Convert VAL tokens to ARR class (REF remains unchanged).
+};
+
+/** Token class conversion and position of a token in the token array. */
+struct XclExpTokenConvInfo
+{
+ sal_uInt16 mnTokPos; /// Position of the token in the token array.
+ XclFuncParamConv meConv; /// Token class conversion type.
+ bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE).
+};
+
+/** Vector of token position and conversion for all operands of an operator,
+ or for all parameters of a function. */
+struct XclExpOperandList : public std::vector< XclExpTokenConvInfo >
+{
+ explicit XclExpOperandList() { reserve( 2 ); }
+ void AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType );
+};
+
+void XclExpOperandList::AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType )
+{
+ resize( size() + 1 );
+ XclExpTokenConvInfo& rConvInfo = back();
+ rConvInfo.mnTokPos = nTokPos;
+ rConvInfo.meConv = eConv;
+ rConvInfo.mbValType = bValType;
+}
+
+typedef std::shared_ptr< XclExpOperandList > XclExpOperandListRef;
+
+/** Encapsulates all data needed for a call to an external function (macro, add-in). */
+struct XclExpExtFuncData
+{
+ OUString maFuncName; /// Name of the function.
+ bool mbVBasic; /// True = Visual Basic macro call.
+ bool mbHidden; /// True = Create hidden defined name.
+
+ explicit XclExpExtFuncData() : mbVBasic( false ), mbHidden( false ) {}
+ void Set( const OUString& rFuncName, bool bVBasic, bool bHidden );
+};
+
+void XclExpExtFuncData::Set( const OUString& rFuncName, bool bVBasic, bool bHidden )
+{
+ maFuncName = rFuncName;
+ mbVBasic = bVBasic;
+ mbHidden = bHidden;
+}
+
+/** Encapsulates all data needed to process an entire function. */
+class XclExpFuncData
+{
+public:
+ explicit XclExpFuncData(
+ const XclExpScToken& rTokData,
+ const XclFunctionInfo& rFuncInfo,
+ XclExpExtFuncData aExtFuncData );
+
+ const FormulaToken& GetScToken() const { return *mrTokData.mpScToken; }
+ OpCode GetOpCode() const { return mrFuncInfo.meOpCode; }
+ sal_uInt16 GetXclFuncIdx() const { return mrFuncInfo.mnXclFunc; }
+ bool IsVolatile() const { return mrFuncInfo.IsVolatile(); }
+ bool IsFixedParamCount() const { return mrFuncInfo.IsFixedParamCount(); }
+ bool IsAddInEquivalent() const { return mrFuncInfo.IsAddInEquivalent(); }
+ bool IsMacroFunc() const { return mrFuncInfo.IsMacroFunc(); }
+ sal_uInt8 GetSpaces() const { return mrTokData.mnSpaces; }
+ const XclExpExtFuncData& GetExtFuncData() const { return maExtFuncData; }
+ sal_uInt8 GetReturnClass() const { return mrFuncInfo.mnRetClass; }
+
+ const XclFuncParamInfo& GetParamInfo() const;
+ bool IsCalcOnlyParam() const;
+ bool IsExcelOnlyParam() const;
+ void IncParamInfoIdx();
+
+ sal_uInt8 GetMinParamCount() const { return mrFuncInfo.mnMinParamCount; }
+ sal_uInt8 GetMaxParamCount() const { return mrFuncInfo.mnMaxParamCount; }
+ sal_uInt8 GetParamCount() const { return static_cast< sal_uInt8 >( mxOperands->size() ); }
+ void FinishParam( sal_uInt16 nTokPos );
+ const XclExpOperandListRef& GetOperandList() const { return mxOperands; }
+
+ ScfUInt16Vec& GetAttrPosVec() { return maAttrPosVec; }
+ void AppendAttrPos( sal_uInt16 nPos ) { maAttrPosVec.push_back( nPos ); }
+
+private:
+ ScfUInt16Vec maAttrPosVec; /// Token array positions of tAttr tokens.
+ const XclExpScToken& mrTokData; /// Data about processed function name token.
+ const XclFunctionInfo& mrFuncInfo; /// Constant data about processed function.
+ XclExpExtFuncData maExtFuncData; /// Data for external functions (macro, add-in).
+ XclExpOperandListRef mxOperands; /// Class conversion and position of all parameters.
+ const XclFuncParamInfo* mpParamInfo; /// Information for current parameter.
+};
+
+XclExpFuncData::XclExpFuncData( const XclExpScToken& rTokData,
+ const XclFunctionInfo& rFuncInfo, XclExpExtFuncData aExtFuncData ) :
+ mrTokData( rTokData ),
+ mrFuncInfo( rFuncInfo ),
+ maExtFuncData(std::move( aExtFuncData )),
+ mxOperands( std::make_shared<XclExpOperandList>() ),
+ mpParamInfo( rFuncInfo.mpParamInfos )
+{
+ OSL_ENSURE( mrTokData.mpScToken, "XclExpFuncData::XclExpFuncData - missing core token" );
+ // set name of an add-in function
+ if( (maExtFuncData.maFuncName.isEmpty()) && dynamic_cast< const FormulaExternalToken* >( mrTokData.mpScToken ) )
+ maExtFuncData.Set( GetScToken().GetExternal(), true, false );
+}
+
+const XclFuncParamInfo& XclExpFuncData::GetParamInfo() const
+{
+ static const XclFuncParamInfo saInvalidInfo = { EXC_PARAM_NONE, EXC_PARAMCONV_ORG, false };
+ return mpParamInfo ? *mpParamInfo : saInvalidInfo;
+}
+
+bool XclExpFuncData::IsCalcOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_CALCONLY);
+}
+
+bool XclExpFuncData::IsExcelOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_EXCELONLY);
+}
+
+void XclExpFuncData::IncParamInfoIdx()
+{
+ if( !mpParamInfo )
+ return;
+
+ // move pointer to next entry, if something explicit follows
+ if( (o3tl::make_unsigned( mpParamInfo - mrFuncInfo.mpParamInfos + 1 ) < EXC_FUNCINFO_PARAMINFO_COUNT) && (mpParamInfo[ 1 ].meValid != EXC_PARAM_NONE) )
+ ++mpParamInfo;
+ // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it
+ else if( IsExcelOnlyParam() || IsCalcOnlyParam() )
+ mpParamInfo = nullptr;
+ // points to last info, but parameter pairs expected, move to previous info
+ else if( mrFuncInfo.IsParamPairs() )
+ --mpParamInfo;
+ // otherwise: repeat last parameter class
+}
+
+void XclExpFuncData::FinishParam( sal_uInt16 nTokPos )
+{
+ // write token class conversion info for this parameter
+ const XclFuncParamInfo& rParamInfo = GetParamInfo();
+ mxOperands->AppendOperand( nTokPos, rParamInfo.meConv, rParamInfo.mbValType );
+ // move to next parameter info structure
+ IncParamInfoIdx();
+}
+
+// compiler configuration -----------------------------------------------------
+
+/** Type of token class handling. */
+enum XclExpFmlaClassType
+{
+ EXC_CLASSTYPE_CELL, /// Cell formula, shared formula.
+ EXC_CLASSTYPE_ARRAY, /// Array formula, conditional formatting, data validation.
+ EXC_CLASSTYPE_NAME /// Defined name, range list.
+};
+
+/** Configuration data of the formula compiler. */
+struct XclExpCompConfig
+{
+ XclFormulaType meType; /// Type of the formula to be created.
+ XclExpFmlaClassType meClassType; /// Token class handling type.
+ bool mbLocalLinkMgr; /// True = local (per-sheet) link manager, false = global.
+ bool mbFromCell; /// True = Any kind of cell formula (cell, array, shared).
+ bool mb3DRefOnly; /// True = Only 3D references allowed (e.g. names).
+ bool mbAllowArrays; /// True = Allow inline arrays.
+};
+
+/** The table containing configuration data for all formula types. */
+const XclExpCompConfig spConfigTable[] =
+{
+ // formula type token class type lclLM inCell 3dOnly allowArray
+ { EXC_FMLATYPE_CELL, EXC_CLASSTYPE_CELL, true, true, false, true },
+ { EXC_FMLATYPE_SHARED, EXC_CLASSTYPE_CELL, true, true, false, true },
+ { EXC_FMLATYPE_MATRIX, EXC_CLASSTYPE_ARRAY, true, true, false, true },
+ { EXC_FMLATYPE_CONDFMT, EXC_CLASSTYPE_ARRAY, true, false, false, false },
+ { EXC_FMLATYPE_DATAVAL, EXC_CLASSTYPE_ARRAY, true, false, false, false },
+ { EXC_FMLATYPE_NAME, EXC_CLASSTYPE_NAME, false, false, true, true },
+ { EXC_FMLATYPE_CHART, EXC_CLASSTYPE_NAME, true, false, true, true },
+ { EXC_FMLATYPE_CONTROL, EXC_CLASSTYPE_NAME, true, false, false, false },
+ { EXC_FMLATYPE_WQUERY, EXC_CLASSTYPE_NAME, true, false, true, false },
+ { EXC_FMLATYPE_LISTVAL, EXC_CLASSTYPE_NAME, true, false, false, false }
+};
+
+/** Working data of the formula compiler. Used to push onto a stack for recursive calls. */
+struct XclExpCompData
+{
+ typedef std::shared_ptr< ScTokenArray > ScTokenArrayRef;
+
+ const XclExpCompConfig& mrCfg; /// Configuration for current formula type.
+ ScTokenArrayRef mxOwnScTokArr; /// Own clone of a Calc token array.
+ XclTokenArrayIterator maTokArrIt; /// Iterator in Calc token array.
+ XclExpLinkManager* mpLinkMgr; /// Link manager for current context (local/global).
+ XclExpRefLog* mpRefLog; /// Log for external references.
+ const ScAddress* mpScBasePos; /// Current cell position of the formula.
+
+ ScfUInt8Vec maTokVec; /// Byte vector containing token data.
+ ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs).
+ std::vector< XclExpOperandListRef >
+ maOpListVec; /// Formula structure, maps operators to their operands.
+ ScfUInt16Vec maOpPosStack; /// Stack with positions of operand tokens waiting for an operator.
+ bool mbStopAtSep; /// True = Stop subexpression creation at an ocSep token.
+ bool mbVolatile; /// True = Formula contains volatile function.
+ bool mbOk; /// Current state of the compiler.
+
+ explicit XclExpCompData( const XclExpCompConfig* pCfg );
+};
+
+XclExpCompData::XclExpCompData( const XclExpCompConfig* pCfg ) :
+ mrCfg( pCfg ? *pCfg : spConfigTable[ 0 ] ),
+ mpLinkMgr( nullptr ),
+ mpRefLog( nullptr ),
+ mpScBasePos( nullptr ),
+ mbStopAtSep( false ),
+ mbVolatile( false ),
+ mbOk( pCfg != nullptr )
+{
+ OSL_ENSURE( pCfg, "XclExpFmlaCompImpl::Init - unknown formula type" );
+}
+
+} // namespace
+
+/** Implementation class of the export formula compiler. */
+class XclExpFmlaCompImpl : protected XclExpRoot, protected XclTokenArrayHelper
+{
+public:
+ explicit XclExpFmlaCompImpl( const XclExpRoot& rRoot );
+
+ /** Creates an Excel token array from the passed Calc token array. */
+ XclTokenArrayRef CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos = nullptr, XclExpRefLog* pRefLog = nullptr );
+ /** Creates a single error token containing the passed error code. */
+ XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode );
+ /** Creates a single token for a special cell reference. */
+ XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos );
+ /** Creates a single tNameXR token for a reference to an external name. */
+ XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
+
+ /** Returns true, if the passed formula type allows 3D references only. */
+ bool Is3DRefOnly( XclFormulaType eType ) const;
+
+ bool IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const;
+
+private:
+ const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const;
+ sal_uInt16 GetSize() const { return static_cast< sal_uInt16 >( mxData->maTokVec.size() ); }
+
+ void Init( XclFormulaType eType );
+ void Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog );
+
+ void RecalcTokenClasses();
+ void RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass,
+ o3tl::sorted_vector<const XclExpTokenConvInfo*>& rSeenTokens );
+
+ void FinalizeFormula();
+ XclTokenArrayRef CreateTokenArray();
+
+ // compiler ---------------------------------------------------------------
+ // XclExpScToken: pass-by-value and return-by-value is intended
+
+ const FormulaToken* GetNextRawToken();
+ const FormulaToken* PeekNextRawToken() const;
+
+ bool GetNextToken( XclExpScToken& rTokData );
+ XclExpScToken GetNextToken();
+
+ XclExpScToken Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep );
+ XclExpScToken SkipExpression( XclExpScToken aTokData, bool bStopAtSep );
+
+ XclExpScToken OrTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken AndTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken CompareTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken ConcatTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken AddSubTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken MulDivTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken PowTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken ListTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp );
+ XclExpScToken RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp );
+ XclExpScToken Factor( XclExpScToken aTokData );
+
+ // formula structure ------------------------------------------------------
+
+ void ProcessDouble( const XclExpScToken& rTokData );
+ void ProcessString( const XclExpScToken& rTokData );
+ void ProcessMissing( const XclExpScToken& rTokData );
+ void ProcessBad( const XclExpScToken& rTokData );
+ void ProcessParentheses( const XclExpScToken& rTokData );
+ void ProcessBoolean( const XclExpScToken& rTokData );
+ void ProcessDdeLink( const XclExpScToken& rTokData );
+ void ProcessExternal( const XclExpScToken& rTokData );
+ void ProcessMatrix( const XclExpScToken& rTokData );
+
+ void ProcessFunction( const XclExpScToken& rTokData );
+ void PrepareFunction( const XclExpFuncData& rFuncData );
+ void FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces );
+ void FinishIfFunction( XclExpFuncData& rFuncData );
+ void FinishChooseFunction( XclExpFuncData& rFuncData );
+
+ XclExpScToken ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData );
+ void PrepareParam( XclExpFuncData& rFuncData );
+ void FinishParam( XclExpFuncData& rFuncData );
+ void AppendDefaultParam( XclExpFuncData& rFuncData );
+ void AppendTrailingParam( XclExpFuncData& rFuncData );
+
+ // reference handling -----------------------------------------------------
+
+ SCTAB GetScTab( const ScSingleRefData& rRefData ) const;
+
+ void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos,
+ bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const;
+ void ConvertRefData( ScComplexRefData& rRefData, XclRange& rXclRange,
+ bool bNatLangRef ) const;
+
+ XclExpRefLogEntry* GetNewRefLogEntry();
+ void ProcessCellRef( const XclExpScToken& rTokData );
+ void ProcessRangeRef( const XclExpScToken& rTokData );
+ void ProcessExternalCellRef( const XclExpScToken& rTokData );
+ void ProcessExternalRangeRef( const XclExpScToken& rTokData );
+ void ProcessDefinedName( const XclExpScToken& rTokData );
+ void ProcessExternalName( const XclExpScToken& rTokData );
+
+ // token vector -----------------------------------------------------------
+
+ void PushOperandPos( sal_uInt16 nTokPos );
+ void PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands );
+ sal_uInt16 PopOperandPos();
+
+ void Append( sal_uInt8 nData );
+ void Append( sal_uInt8 nData, size_t nCount );
+ void Append( sal_uInt16 nData );
+ void Append( sal_uInt32 nData );
+ void Append( double fData );
+ void Append( const OUString& rString );
+
+ void AppendAddress( const XclAddress& rXclPos );
+ void AppendRange( const XclRange& rXclRange );
+
+ void AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount );
+
+ void AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
+ void AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces = 0 );
+ void AppendNumToken( double fValue, sal_uInt8 nSpaces = 0 );
+ void AppendBoolToken( bool bValue, sal_uInt8 nSpaces = 0 );
+ void AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces = 0 );
+ void AppendMissingToken( sal_uInt8 nSpaces = 0 );
+ void AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces = 0 );
+ void AppendMissingNameToken( const OUString& rName, sal_uInt8 nSpaces = 0 );
+ void AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces = 0 );
+ void AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData );
+ void AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData );
+ void AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData );
+
+ void AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces = 0 );
+ void AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
+ void AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces = 0 );
+ void AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount );
+ void AppendFuncToken( const XclExpFuncData& rFuncData );
+
+ void AppendParenToken( sal_uInt8 nOpenSpaces = 0, sal_uInt8 nCloseSpaces = 0 );
+ void AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType );
+
+ void InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize );
+ void Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset );
+
+ void UpdateAttrGoto( sal_uInt16 nAttrPos );
+
+ bool IsSpaceToken( sal_uInt16 nPos ) const;
+ void RemoveTrailingParen();
+
+ void AppendExt( sal_uInt8 nData );
+ void AppendExt( sal_uInt8 nData, size_t nCount );
+ void AppendExt( sal_uInt16 nData );
+ void AppendExt( double fData );
+ void AppendExt( const OUString& rString );
+
+private:
+ typedef std::map< XclFormulaType, XclExpCompConfig > XclExpCompConfigMap;
+ typedef std::shared_ptr< XclExpCompData > XclExpCompDataRef;
+
+ XclExpCompConfigMap maCfgMap; /// Compiler configuration map for all formula types.
+ XclFunctionProvider maFuncProv; /// Excel function data provider.
+ XclExpCompDataRef mxData; /// Working data for current formula.
+ std::vector< XclExpCompDataRef >
+ maDataStack; /// Stack for working data, when compiler is called recursively.
+ const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls.
+ const SCCOL mnMaxAbsCol; /// Maximum column index.
+ const SCROW mnMaxAbsRow; /// Maximum row index.
+ const SCCOL mnMaxScCol; /// Maximum column index in Calc itself.
+ const SCROW mnMaxScRow; /// Maximum row index in Calc itself.
+ const sal_uInt16 mnMaxColMask; /// Mask to delete invalid bits in column fields.
+ const sal_uInt32 mnMaxRowMask; /// Mask to delete invalid bits in row fields.
+};
+
+XclExpFmlaCompImpl::XclExpFmlaCompImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maFuncProv( rRoot ),
+ meBiff( rRoot.GetBiff() ),
+ mnMaxAbsCol( rRoot.GetXclMaxPos().Col() ),
+ mnMaxAbsRow( rRoot.GetXclMaxPos().Row() ),
+ mnMaxScCol( rRoot.GetScMaxPos().Col() ),
+ mnMaxScRow( rRoot.GetScMaxPos().Row() ),
+ mnMaxColMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Col() ) ),
+ mnMaxRowMask( static_cast< sal_uInt32 >( rRoot.GetXclMaxPos().Row() ) )
+{
+ // build the configuration map
+ for(auto const &rEntry : spConfigTable)
+ maCfgMap[ rEntry.meType ] = rEntry;
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateFormula( XclFormulaType eType,
+ const ScTokenArray& rScTokArr, const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ // initialize the compiler
+ Init( eType, rScTokArr, pScBasePos, pRefLog );
+
+ // start compilation, if initialization didn't fail
+ if( mxData->mbOk )
+ {
+ XclExpScToken aTokData( GetNextToken() );
+ FormulaError nScError = rScTokArr.GetCodeError();
+ if( (nScError != FormulaError::NONE) && (!aTokData.Is() || (aTokData.GetOpCode() == ocStop)) )
+ {
+ // #i50253# convert simple ocStop token to error code formula (e.g. =#VALUE!)
+ AppendErrorToken( XclTools::GetXclErrorCode( nScError ), aTokData.mnSpaces );
+ }
+ else if( aTokData.Is() )
+ {
+ aTokData = Expression( aTokData, false, false );
+ }
+ else
+ {
+ OSL_FAIL( "XclExpFmlaCompImpl::CreateFormula - empty token array" );
+ mxData->mbOk = false;
+ }
+
+ if( mxData->mbOk )
+ {
+ // #i44907# auto-generated SUBTOTAL formula cells have trailing ocStop token
+ mxData->mbOk = !aTokData.Is() || (aTokData.GetOpCode() == ocStop);
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::CreateFormula - unknown garbage behind formula" );
+ }
+ }
+
+ // finalize (add tAttrVolatile token, calculate all token classes)
+ RecalcTokenClasses();
+ FinalizeFormula();
+
+ // leave recursive call, create and return the final token array
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateErrorFormula( sal_uInt8 nErrCode )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendErrorToken( nErrCode );
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendOperandTokenId( nTokenId );
+ Append( static_cast<sal_uInt16>(rXclPos.mnRow) );
+ Append( rXclPos.mnCol ); // do not use AppendAddress(), we always need 16-bit column here
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendNameXToken( nExtSheet, nExtName );
+ return CreateTokenArray();
+}
+
+bool XclExpFmlaCompImpl::Is3DRefOnly( XclFormulaType eType ) const
+{
+ const XclExpCompConfig* pCfg = GetConfigForType( eType );
+ return pCfg && pCfg->mb3DRefOnly;
+}
+
+// private --------------------------------------------------------------------
+
+const XclExpCompConfig* XclExpFmlaCompImpl::GetConfigForType( XclFormulaType eType ) const
+{
+ XclExpCompConfigMap::const_iterator aIt = maCfgMap.find( eType );
+ OSL_ENSURE( aIt != maCfgMap.end(), "XclExpFmlaCompImpl::GetConfigForType - unknown formula type" );
+ return (aIt == maCfgMap.end()) ? nullptr : &aIt->second;
+}
+
+void XclExpFmlaCompImpl::Init( XclFormulaType eType )
+{
+ // compiler invoked recursively? - store old working data
+ if( mxData )
+ maDataStack.push_back( mxData );
+ // new compiler working data structure
+ mxData = std::make_shared<XclExpCompData>( GetConfigForType( eType ) );
+}
+
+void XclExpFmlaCompImpl::Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ // common initialization
+ Init( eType );
+
+ // special initialization
+ if( mxData->mbOk ) switch( mxData->mrCfg.meType )
+ {
+ case EXC_FMLATYPE_CELL:
+ case EXC_FMLATYPE_MATRIX:
+ case EXC_FMLATYPE_CHART:
+ mxData->mbOk = pScBasePos != nullptr;
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" );
+ mxData->mpScBasePos = pScBasePos;
+ break;
+ case EXC_FMLATYPE_SHARED:
+ mxData->mbOk = pScBasePos != nullptr;
+ assert(mxData->mbOk && "XclExpFmlaCompImpl::Init - missing cell address");
+ if (mxData->mbOk)
+ {
+ // clone the passed token array, convert references relative to current cell position
+ mxData->mxOwnScTokArr = rScTokArr.Clone();
+ ScCompiler::MoveRelWrap( *mxData->mxOwnScTokArr, GetDoc(), *pScBasePos, GetDoc().MaxCol(), GetDoc().MaxRow() );
+ // don't remember pScBasePos in mxData->mpScBasePos, shared formulas use real relative refs
+ }
+ break;
+ default:;
+ }
+
+ if( mxData->mbOk )
+ {
+ // link manager to be used
+ mxData->mpLinkMgr = mxData->mrCfg.mbLocalLinkMgr ? &GetLocalLinkManager() : &GetGlobalLinkManager();
+
+ // token array iterator (use cloned token array if present)
+ mxData->maTokArrIt.Init( mxData->mxOwnScTokArr ? *mxData->mxOwnScTokArr : rScTokArr, false );
+ mxData->mpRefLog = pRefLog;
+ // Only for OOXML
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ mxData->mpScBasePos = pScBasePos;
+ }
+}
+
+void XclExpFmlaCompImpl::RecalcTokenClasses()
+{
+ if( !mxData->mbOk )
+ return;
+
+ mxData->mbOk = mxData->maOpPosStack.size() == 1;
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::RecalcTokenClasses - position of root token expected on stack" );
+ if( mxData->mbOk )
+ {
+ /* Cell and array formulas start with VAL conversion and VALTYPE
+ parameter type, defined names start with ARR conversion and
+ REFTYPE parameter type for the root token. */
+ bool bNameFmla = mxData->mrCfg.meClassType == EXC_CLASSTYPE_NAME;
+ XclFuncParamConv eParamConv = bNameFmla ? EXC_PARAMCONV_ARR : EXC_PARAMCONV_VAL;
+ XclExpClassConv eClassConv = bNameFmla ? EXC_CLASSCONV_ARR : EXC_CLASSCONV_VAL;
+ XclExpTokenConvInfo aConvInfo = { PopOperandPos(), eParamConv, !bNameFmla };
+ o3tl::sorted_vector<const XclExpTokenConvInfo*> aSeenTokens;
+ RecalcTokenClass(aConvInfo, eParamConv, eClassConv, bNameFmla, aSeenTokens);
+ }
+
+ // clear operand vectors (calls to the expensive InsertZeros() may follow)
+ mxData->maOpListVec.clear();
+ mxData->maOpPosStack.clear();
+}
+
+void XclExpFmlaCompImpl::RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo,
+ XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass,
+ o3tl::sorted_vector<const XclExpTokenConvInfo*>& rSeenTokens )
+{
+ OSL_ENSURE( rConvInfo.mnTokPos < GetSize(), "XclExpFmlaCompImpl::RecalcTokenClass - invalid token position" );
+
+ const bool bAlreadySeen = !rSeenTokens.insert(&rConvInfo).second;
+ if (bAlreadySeen)
+ {
+ SAL_WARN("sc.filter", "XclExpFmlaCompImpl::RecalcTokenClass: loop in nested operands");
+ return;
+ }
+ rSeenTokens.insert(&rConvInfo);
+
+ sal_uInt8& rnTokenId = mxData->maTokVec[ rConvInfo.mnTokPos ];
+ sal_uInt8 nTokClass = GetTokenClass( rnTokenId );
+
+ // REF tokens in VALTYPE parameters behave like VAL tokens
+ if( rConvInfo.mbValType && (nTokClass == EXC_TOKCLASS_REF) )
+ {
+ nTokClass = EXC_TOKCLASS_VAL;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+
+ // replace RPO conversion of operator with parent conversion
+ XclFuncParamConv eConv = (rConvInfo.meConv == EXC_PARAMCONV_RPO) ? ePrevConv : rConvInfo.meConv;
+
+ // find the effective token class conversion to be performed for this token
+ XclExpClassConv eClassConv = EXC_CLASSCONV_ORG;
+ switch( eConv )
+ {
+ case EXC_PARAMCONV_ORG:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_VAL:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_VAL;
+ break;
+ case EXC_PARAMCONV_ARR:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_ARR;
+ break;
+ case EXC_PARAMCONV_RPT:
+ switch( ePrevConv )
+ {
+ case EXC_PARAMCONV_ORG:
+ case EXC_PARAMCONV_VAL:
+ case EXC_PARAMCONV_ARR:
+ /* If parent token has REF class (REF token in REFTYPE
+ function parameter), then RPT does not repeat the
+ previous explicit ORG or ARR conversion, but always
+ falls back to VAL conversion. */
+ eClassConv = bWasRefClass ? EXC_CLASSCONV_VAL : ePrevClassConv;
+ break;
+ case EXC_PARAMCONV_RPT:
+ // nested RPT repeats the previous effective conversion
+ eClassConv = ePrevClassConv;
+ break;
+ case EXC_PARAMCONV_RPX:
+ /* If parent token has REF class (REF token in REFTYPE
+ function parameter), then RPX repeats the previous
+ effective conversion (which will be either ORG or ARR,
+ but never VAL), otherwise falls back to ORG conversion. */
+ eClassConv = bWasRefClass ? ePrevClassConv : EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_RPO: // does not occur
+ break;
+ }
+ break;
+ case EXC_PARAMCONV_RPX:
+ /* If current token still has REF class, set previous effective
+ conversion as current conversion. This will not have an effect
+ on the REF token but is needed for RPT parameters of this
+ function that want to repeat this conversion type. If current
+ token is VAL or ARR class, the previous ARR conversion will be
+ repeated on the token, but VAL conversion will not. */
+ eClassConv = ((nTokClass == EXC_TOKCLASS_REF) || (ePrevClassConv == EXC_CLASSCONV_ARR)) ?
+ ePrevClassConv : EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_RPO: // does not occur (see above)
+ break;
+ }
+
+ // do the token class conversion
+ switch( eClassConv )
+ {
+ case EXC_CLASSCONV_ORG:
+ /* Cell formulas: leave the current token class. Cell formulas
+ are the only type of formulas where all tokens can keep
+ their original token class.
+ Array and defined name formulas: convert VAL to ARR. */
+ if( (mxData->mrCfg.meClassType != EXC_CLASSTYPE_CELL) && (nTokClass == EXC_TOKCLASS_VAL) )
+ {
+ nTokClass = EXC_TOKCLASS_ARR;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ case EXC_CLASSCONV_VAL:
+ // convert ARR to VAL
+ if( nTokClass == EXC_TOKCLASS_ARR )
+ {
+ nTokClass = EXC_TOKCLASS_VAL;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ case EXC_CLASSCONV_ARR:
+ // convert VAL to ARR
+ if( nTokClass == EXC_TOKCLASS_VAL )
+ {
+ nTokClass = EXC_TOKCLASS_ARR;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ }
+
+ // do conversion for nested operands, if token is an operator or function
+ if( rConvInfo.mnTokPos < mxData->maOpListVec.size() )
+ if( const XclExpOperandList* pOperands = mxData->maOpListVec[ rConvInfo.mnTokPos ].get() )
+ for( const auto& rOperand : *pOperands )
+ RecalcTokenClass( rOperand, eConv, eClassConv, nTokClass == EXC_TOKCLASS_REF, rSeenTokens );
+}
+
+void XclExpFmlaCompImpl::FinalizeFormula()
+{
+ if( mxData->mbOk )
+ {
+ // Volatile? Add a tAttrVolatile token at the beginning of the token array.
+ if( mxData->mbVolatile )
+ {
+ // tAttrSpace token can be extended with volatile flag
+ if( !IsSpaceToken( 0 ) )
+ {
+ InsertZeros( 0, 4 );
+ mxData->maTokVec[ 0 ] = EXC_TOKID_ATTR;
+ }
+ mxData->maTokVec[ 1 ] |= EXC_TOK_ATTR_VOLATILE;
+ }
+
+ // Token array too long? -> error
+ mxData->mbOk = mxData->maTokVec.size() <= EXC_TOKARR_MAXLEN;
+ }
+
+ if( !mxData->mbOk )
+ {
+ // Any unrecoverable error? -> Create a =#NA formula.
+ mxData->maTokVec.clear();
+ mxData->maExtDataVec.clear();
+ mxData->mbVolatile = false;
+ AppendErrorToken( EXC_ERR_NA );
+ }
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateTokenArray()
+{
+ // create the Excel token array from working data before resetting mxData
+ OSL_ENSURE( mxData->mrCfg.mbAllowArrays || mxData->maExtDataVec.empty(), "XclExpFmlaCompImpl::CreateTokenArray - unexpected extended data" );
+ if( !mxData->mrCfg.mbAllowArrays )
+ mxData->maExtDataVec.clear();
+ XclTokenArrayRef xTokArr = std::make_shared<XclTokenArray>( mxData->maTokVec, mxData->maExtDataVec, mxData->mbVolatile );
+ mxData.reset();
+
+ // compiler invoked recursively? - restore old working data
+ if( !maDataStack.empty() )
+ {
+ mxData = maDataStack.back();
+ maDataStack.pop_back();
+ }
+
+ return xTokArr;
+}
+
+// compiler -------------------------------------------------------------------
+
+const FormulaToken* XclExpFmlaCompImpl::GetNextRawToken()
+{
+ const FormulaToken* pScToken = mxData->maTokArrIt.Get();
+ ++mxData->maTokArrIt;
+ return pScToken;
+}
+
+const FormulaToken* XclExpFmlaCompImpl::PeekNextRawToken() const
+{
+ /* Returns pointer to next raw token in the token array. The token array
+ iterator already points to the next token (A call to GetNextToken()
+ always increases the iterator), so this function just returns the token
+ the iterator points to. To skip space tokens, a copy of the iterator is
+ created and set to the passed skip-spaces mode. If spaces have to be
+ skipped, and the iterator currently points to a space token, the
+ constructor will move it to the next non-space token. */
+ XclTokenArrayIterator aTempIt( mxData->maTokArrIt, true/*bSkipSpaces*/ );
+ return aTempIt.Get();
+}
+
+bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData )
+{
+ rTokData.mpScToken = GetNextRawToken();
+ rTokData.mnSpaces = 0;
+ /* TODO: handle ocWhitespace characters? */
+ while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace)
+ {
+ rTokData.mnSpaces += rTokData.mpScToken->GetByte();
+ rTokData.mpScToken = GetNextRawToken();
+ }
+ return rTokData.Is();
+}
+
+XclExpScToken XclExpFmlaCompImpl::GetNextToken()
+{
+ XclExpScToken aTokData;
+ GetNextToken( aTokData );
+ return aTokData;
+}
+
+namespace {
+
+/** Returns the Excel token ID of a comparison operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetCompareTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocLess: return EXC_TOKID_LT;
+ case ocLessEqual: return EXC_TOKID_LE;
+ case ocEqual: return EXC_TOKID_EQ;
+ case ocGreaterEqual: return EXC_TOKID_GE;
+ case ocGreater: return EXC_TOKID_GT;
+ case ocNotEqual: return EXC_TOKID_NE;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a string concatenation operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetConcatTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocAmpersand) ? EXC_TOKID_CONCAT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of an addition/subtraction operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetAddSubTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocAdd: return EXC_TOKID_ADD;
+ case ocSub: return EXC_TOKID_SUB;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a multiplication/division operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetMulDivTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocMul: return EXC_TOKID_MUL;
+ case ocDiv: return EXC_TOKID_DIV;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a power operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetPowTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocPow) ? EXC_TOKID_POWER : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a trailing unary operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetUnaryPostTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocPercentSign) ? EXC_TOKID_PERCENT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a leading unary operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetUnaryPreTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocAdd: return EXC_TOKID_UPLUS; // +(1)
+ case ocNeg: return EXC_TOKID_UMINUS; // NEG(1)
+ case ocNegSub: return EXC_TOKID_UMINUS; // -(1)
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference list operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetListTokenId( OpCode eOpCode, bool bStopAtSep )
+{
+ return ((eOpCode == ocUnion) || (!bStopAtSep && (eOpCode == ocSep))) ? EXC_TOKID_LIST : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference intersection operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetIntersectTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocIntersect) ? EXC_TOKID_ISECT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference range operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetRangeTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocRange) ? EXC_TOKID_RANGE : EXC_TOKID_NONE;
+}
+
+} // namespace
+
+XclExpScToken XclExpFmlaCompImpl::Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep )
+{
+ if( mxData->mbOk && aTokData.Is() )
+ {
+ // remember old stop-at-ocSep mode, restored below
+ bool bOldStopAtSep = mxData->mbStopAtSep;
+ mxData->mbStopAtSep = bStopAtSep;
+ // start compilation of the subexpression
+ aTokData = OrTerm( aTokData, bInParentheses );
+ // restore old stop-at-ocSep mode
+ mxData->mbStopAtSep = bOldStopAtSep;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::SkipExpression( XclExpScToken aTokData, bool bStopAtSep )
+{
+ while( mxData->mbOk && aTokData.Is() && (aTokData.GetOpCode() != ocClose) && (!bStopAtSep || (aTokData.GetOpCode() != ocSep)) )
+ {
+ if( aTokData.GetOpCode() == ocOpen )
+ {
+ aTokData = SkipExpression( GetNextToken(), false );
+ if( mxData->mbOk ) mxData->mbOk = aTokData.GetOpCode() == ocClose;
+ }
+ aTokData = GetNextToken();
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::OrTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = AndTerm( aTokData, bInParentheses );
+ sal_uInt8 nParamCount = 1;
+ while( mxData->mbOk && (aTokData.GetOpCode() == ocOr) )
+ {
+ RemoveTrailingParen();
+ aTokData = AndTerm( GetNextToken(), bInParentheses );
+ RemoveTrailingParen();
+ ++nParamCount;
+ if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
+ }
+ if( mxData->mbOk && (nParamCount > 1) )
+ AppendLogicalOperatorToken( EXC_FUNCID_OR, nParamCount );
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::AndTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = CompareTerm( aTokData, bInParentheses );
+ sal_uInt8 nParamCount = 1;
+ while( mxData->mbOk && (aTokData.GetOpCode() == ocAnd) )
+ {
+ RemoveTrailingParen();
+ aTokData = CompareTerm( GetNextToken(), bInParentheses );
+ RemoveTrailingParen();
+ ++nParamCount;
+ if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
+ }
+ if( mxData->mbOk && (nParamCount > 1) )
+ AppendLogicalOperatorToken( EXC_FUNCID_AND, nParamCount );
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::CompareTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = ConcatTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetConcatTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = ConcatTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::ConcatTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = AddSubTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetCompareTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = AddSubTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::AddSubTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = MulDivTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetAddSubTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = MulDivTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::MulDivTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = PowTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetMulDivTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = PowTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::PowTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = UnaryPostTerm( aTokData, bInParentheses );
+ sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
+ while( mxData->mbOk )
+ {
+ nOpTokenId = lclGetPowTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = UnaryPostTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = UnaryPreTerm( aTokData, bInParentheses );
+ sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
+ while( mxData->mbOk )
+ {
+ nOpTokenId = lclGetUnaryPostTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ AppendUnaryOperatorToken( nOpTokenId, aTokData.mnSpaces );
+ GetNextToken( aTokData );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ sal_uInt8 nOpTokenId = mxData->mbOk ? lclGetUnaryPreTokenId( aTokData.GetOpCode() ) : EXC_TOKID_NONE;
+ if( nOpTokenId != EXC_TOKID_NONE )
+ {
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = UnaryPreTerm( GetNextToken(), bInParentheses );
+ AppendUnaryOperatorToken( nOpTokenId, nSpaces );
+ }
+ else
+ {
+ aTokData = ListTerm( aTokData, bInParentheses );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::ListTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ sal_uInt16 nSubExprPos = GetSize();
+ bool bHasAnyRefOp = false;
+ bool bHasListOp = false;
+ aTokData = IntersectTerm( aTokData, bHasAnyRefOp );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetListTokenId( aTokData.GetOpCode(), mxData->mbStopAtSep );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = IntersectTerm( GetNextToken(), bHasAnyRefOp );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ bHasAnyRefOp = bHasListOp = true;
+ }
+ if( bHasAnyRefOp )
+ {
+ // add a tMemFunc token enclosing the entire reference subexpression
+ sal_uInt16 nSubExprSize = GetSize() - nSubExprPos;
+ InsertZeros( nSubExprPos, 3 );
+ mxData->maTokVec[ nSubExprPos ] = GetTokenId( EXC_TOKID_MEMFUNC, EXC_TOKCLASS_REF );
+ Overwrite( nSubExprPos + 1, nSubExprSize );
+ // update the operand/operator stack (set the list expression as operand of the tMemFunc)
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_VAL, false );
+ PushOperatorPos( nSubExprPos, xOperands );
+ }
+ // #i86439# enclose list operator into parentheses, e.g. Calc's =AREAS(A1~A2) to Excel's =AREAS((A1;A2))
+ if( bHasListOp && !bInParentheses )
+ AppendParenToken();
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp )
+{
+ aTokData = RangeTerm( aTokData, rbHasRefOp );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetIntersectTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId ==EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = RangeTerm( GetNextToken(), rbHasRefOp );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ rbHasRefOp = true;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp )
+{
+ aTokData = Factor( aTokData );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetRangeTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = Factor( GetNextToken() );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ rbHasRefOp = true;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::Factor( XclExpScToken aTokData )
+{
+ if( !mxData->mbOk || !aTokData.Is() ) return XclExpScToken();
+
+ switch( aTokData.GetType() )
+ {
+ case svUnknown: mxData->mbOk = false; break;
+ case svDouble: ProcessDouble( aTokData ); break;
+ case svString: ProcessString( aTokData ); break;
+ case svSingleRef: ProcessCellRef( aTokData ); break;
+ case svDoubleRef: ProcessRangeRef( aTokData ); break;
+ case svExternalSingleRef: ProcessExternalCellRef( aTokData ); break;
+ case svExternalDoubleRef: ProcessExternalRangeRef( aTokData ); break;
+ case svExternalName: ProcessExternalName( aTokData ); break;
+ case svMatrix: ProcessMatrix( aTokData ); break;
+ case svExternal: ProcessExternal( aTokData ); break;
+
+ default: switch( aTokData.GetOpCode() )
+ {
+ case ocNone: /* do nothing */ break;
+ case ocMissing: ProcessMissing( aTokData ); break;
+ case ocBad: ProcessBad( aTokData ); break;
+ case ocOpen: ProcessParentheses( aTokData ); break;
+ case ocName: ProcessDefinedName( aTokData ); break;
+ case ocFalse:
+ case ocTrue: ProcessBoolean( aTokData ); break;
+ case ocDde: ProcessDdeLink( aTokData ); break;
+ default: ProcessFunction( aTokData );
+ }
+ }
+
+ return GetNextToken();
+}
+
+// formula structure ----------------------------------------------------------
+
+void XclExpFmlaCompImpl::ProcessDouble( const XclExpScToken& rTokData )
+{
+ double fValue = rTokData.mpScToken->GetDouble();
+ double fInt;
+ double fFrac = modf( fValue, &fInt );
+ if( (fFrac == 0.0) && (0.0 <= fInt) && (fInt <= 65535.0) )
+ AppendIntToken( static_cast< sal_uInt16 >( fInt ), rTokData.mnSpaces );
+ else
+ AppendNumToken( fValue, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessString( const XclExpScToken& rTokData )
+{
+ AppendOperandTokenId( EXC_TOKID_STR, rTokData.mnSpaces );
+ Append( rTokData.mpScToken->GetString().getString() );
+}
+
+void XclExpFmlaCompImpl::ProcessMissing( const XclExpScToken& rTokData )
+{
+ AppendMissingToken( rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessBad( const XclExpScToken& rTokData )
+{
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessParentheses( const XclExpScToken& rTokData )
+{
+ XclExpScToken aTokData = Expression( GetNextToken(), true, false );
+ mxData->mbOk = aTokData.GetOpCode() == ocClose;
+ AppendParenToken( rTokData.mnSpaces, aTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessBoolean( const XclExpScToken& rTokData )
+{
+ mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
+ if( mxData->mbOk )
+ AppendBoolToken( rTokData.GetOpCode() == ocTrue, rTokData.mnSpaces );
+}
+
+namespace {
+
+bool lclGetTokenString( OUString& rString, const XclExpScToken& rTokData )
+{
+ bool bIsStr = (rTokData.GetType() == svString) && (rTokData.GetOpCode() == ocPush);
+ if( bIsStr )
+ rString = rTokData.mpScToken->GetString().getString();
+ return bIsStr;
+}
+
+} // namespace
+
+void XclExpFmlaCompImpl::ProcessDdeLink( const XclExpScToken& rTokData )
+{
+ OUString aApplic, aTopic, aItem;
+
+ mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aApplic, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aTopic, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aItem, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
+ if( mxData->mbOk ) mxData->mbOk = !aApplic.isEmpty() && !aTopic.isEmpty() && !aItem.isEmpty();
+ if( mxData->mbOk )
+ {
+ sal_uInt16 nExtSheet(0), nExtName(0);
+ if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertDde( nExtSheet, nExtName, aApplic, aTopic, aItem ) )
+ AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
+ else
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternal( const XclExpScToken& rTokData )
+{
+ /* #i47228# Excel import generates svExternal/ocMacro tokens for invalid
+ names and for external/invalid function calls. This function looks for
+ the next token in the token array. If it is an opening parenthesis, the
+ token is processed as external function call, otherwise as undefined name. */
+ const FormulaToken* pNextScToken = PeekNextRawToken();
+ if( !pNextScToken || (pNextScToken->GetOpCode() != ocOpen) )
+ AppendMissingNameToken( rTokData.mpScToken->GetExternal(), rTokData.mnSpaces );
+ else
+ ProcessFunction( rTokData );
+}
+
+void XclExpFmlaCompImpl::ProcessMatrix( const XclExpScToken& rTokData )
+{
+ const ScMatrix* pMatrix = rTokData.mpScToken->GetMatrix();
+ if( pMatrix && mxData->mrCfg.mbAllowArrays )
+ {
+ SCSIZE nScCols, nScRows;
+ pMatrix->GetDimensions( nScCols, nScRows );
+ OSL_ENSURE( (nScCols > 0) && (nScRows > 0), "XclExpFmlaCompImpl::ProcessMatrix - invalid matrix size" );
+ sal_uInt16 nCols = ::limit_cast< sal_uInt16 >( nScCols, 0, 256 );
+ sal_uInt16 nRows = ::limit_cast< sal_uInt16 >( nScRows, 0, 1024 );
+
+ // create the tArray token
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_ARRAY, EXC_TOKCLASS_ARR ), rTokData.mnSpaces );
+ Append( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
+ Append( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
+ Append( static_cast< sal_uInt32 >( 0 ) );
+
+ // create the extended data containing the array values
+ AppendExt( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
+ AppendExt( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
+ for( SCSIZE nScRow = 0; nScRow < nScRows; ++nScRow )
+ {
+ for( SCSIZE nScCol = 0; nScCol < nScCols; ++nScCol )
+ {
+ ScMatrixValue nMatVal = pMatrix->Get( nScCol, nScRow );
+ if( ScMatrix::IsValueType( nMatVal.nType ) ) // value, boolean, or error
+ {
+ FormulaError nErr;
+ if( ScMatrix::IsBooleanType( nMatVal.nType ) )
+ {
+ AppendExt( EXC_CACHEDVAL_BOOL );
+ AppendExt( static_cast< sal_uInt8 >( nMatVal.GetBoolean() ? 1 : 0 ) );
+ AppendExt( 0, 7 );
+ }
+ else if( (nErr = nMatVal.GetError()) != FormulaError::NONE )
+ {
+ AppendExt( EXC_CACHEDVAL_ERROR );
+ AppendExt( XclTools::GetXclErrorCode( nErr ) );
+ AppendExt( 0, 7 );
+ }
+ else
+ {
+ AppendExt( EXC_CACHEDVAL_DOUBLE );
+ AppendExt( nMatVal.fVal );
+ }
+ }
+ else // string or empty
+ {
+ const OUString aStr( nMatVal.GetString().getString());
+ if( aStr.isEmpty() )
+ {
+ AppendExt( EXC_CACHEDVAL_EMPTY );
+ AppendExt( 0, 8 );
+ }
+ else
+ {
+ AppendExt( EXC_CACHEDVAL_STRING );
+ AppendExt( aStr );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // array in places that do not allow it (cond fmts, data validation)
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessFunction( const XclExpScToken& rTokData )
+{
+ OpCode eOpCode = rTokData.GetOpCode();
+ const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( eOpCode );
+
+ XclExpExtFuncData aExtFuncData;
+
+ // no exportable function found - try to create an external macro call
+ if( !pFuncInfo && (eOpCode >= SC_OPCODE_START_NO_PAR) )
+ {
+ const OUString& rFuncName = ScCompiler::GetNativeSymbol( eOpCode );
+ if( !rFuncName.isEmpty() )
+ {
+ aExtFuncData.Set( rFuncName, true, false );
+ pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( ocMacro );
+ }
+ }
+
+ mxData->mbOk = pFuncInfo != nullptr;
+ if( !mxData->mbOk ) return;
+
+ // internal functions equivalent to an existing add-in
+ if( pFuncInfo->IsAddInEquivalent() )
+ aExtFuncData.Set( pFuncInfo->GetAddInEquivalentFuncName(), true, false );
+ // functions simulated by a macro call in file format
+ else if( pFuncInfo->IsMacroFunc() )
+ aExtFuncData.Set( pFuncInfo->GetMacroFuncName(), false, true );
+
+ XclExpFuncData aFuncData( rTokData, *pFuncInfo, std::move(aExtFuncData) );
+ XclExpScToken aTokData;
+
+ // preparations for special functions, before function processing starts
+ PrepareFunction( aFuncData );
+
+ enum { STATE_START, STATE_OPEN, STATE_PARAM, STATE_SEP, STATE_CLOSE, STATE_END }
+ eState = STATE_START;
+ while( eState != STATE_END ) switch( eState )
+ {
+ case STATE_START:
+ mxData->mbOk = GetNextToken( aTokData ) && (aTokData.GetOpCode() == ocOpen);
+ eState = mxData->mbOk ? STATE_OPEN : STATE_END;
+ break;
+ case STATE_OPEN:
+ mxData->mbOk = GetNextToken( aTokData );
+ eState = mxData->mbOk ? ((aTokData.GetOpCode() == ocClose) ? STATE_CLOSE : STATE_PARAM) : STATE_END;
+ break;
+ case STATE_PARAM:
+ aTokData = ProcessParam( aTokData, aFuncData );
+ switch( aTokData.GetOpCode() )
+ {
+ case ocSep: eState = STATE_SEP; break;
+ case ocClose: eState = STATE_CLOSE; break;
+ default: mxData->mbOk = false;
+ }
+ if( !mxData->mbOk ) eState = STATE_END;
+ break;
+ case STATE_SEP:
+ mxData->mbOk = (aFuncData.GetParamCount() < EXC_FUNC_MAXPARAM) && GetNextToken( aTokData );
+ eState = mxData->mbOk ? STATE_PARAM : STATE_END;
+ break;
+ case STATE_CLOSE:
+ FinishFunction( aFuncData, aTokData.mnSpaces );
+ eState = STATE_END;
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::PrepareFunction( const XclExpFuncData& rFuncData )
+{
+ // For OOXML these are not rewritten anymore.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ return;
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocCosecant: // simulate CSC(x) by (1/SIN(x))
+ case ocSecant: // simulate SEC(x) by (1/COS(x))
+ case ocCot: // simulate COT(x) by (1/TAN(x))
+ case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
+ case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
+ case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
+ AppendIntToken( 1 );
+ break;
+ case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
+ AppendNumToken( M_PI_2 );
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces )
+{
+ // append missing parameters required in Excel, may modify param count
+ AppendTrailingParam( rFuncData );
+
+ // check if parameter count fits into the limits of the function
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ if( (rFuncData.GetMinParamCount() <= nParamCount) && (nParamCount <= rFuncData.GetMaxParamCount()) )
+ {
+ // first put the tAttrSpace tokens, they must not be included in tAttrGoto handling
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, rFuncData.GetSpaces() );
+
+ // add tAttrGoto tokens for IF or CHOOSE functions
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ case ocChoose:
+ AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
+ break;
+ default:;
+ }
+
+ // put the tFunc or tFuncVar token (or another special token, e.g. tAttrSum)
+ AppendFuncToken( rFuncData );
+
+ // update volatile flag - is set if at least one used function is volatile
+ mxData->mbVolatile |= rFuncData.IsVolatile();
+
+ // update jump tokens for specific functions, add additional tokens
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ FinishIfFunction( rFuncData );
+ break;
+ case ocChoose:
+ FinishChooseFunction( rFuncData );
+ break;
+
+ case ocCosecant: // simulate CSC(x) by (1/SIN(x))
+ case ocSecant: // simulate SEC(x) by (1/COS(x))
+ case ocCot: // simulate COT(x) by (1/TAN(x))
+ case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
+ case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
+ case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
+ // For OOXML not rewritten anymore.
+ if (GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
+ AppendParenToken();
+ }
+ break;
+ case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
+ // For OOXML not rewritten anymore.
+ if (GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ AppendBinaryOperatorToken( EXC_TOKID_SUB, true );
+ AppendParenToken();
+ }
+ break;
+
+ default:;
+ }
+ }
+ else
+ mxData->mbOk = false;
+}
+
+void XclExpFmlaCompImpl::FinishIfFunction( XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nParamCount = rFuncData.GetParamCount();
+ OSL_ENSURE( (nParamCount == 2) || (nParamCount == 3), "XclExpFmlaCompImpl::FinishIfFunction - wrong parameter count" );
+ const ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
+ OSL_ENSURE( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishIfFunction - wrong number of tAttr tokens" );
+ // update tAttrIf token following the condition parameter
+ Overwrite( rAttrPos[ 0 ] + 2, static_cast< sal_uInt16 >( rAttrPos[ 1 ] - rAttrPos[ 0 ] ) );
+ // update the tAttrGoto tokens following true and false parameters
+ UpdateAttrGoto( rAttrPos[ 1 ] );
+ if( nParamCount == 3 )
+ UpdateAttrGoto( rAttrPos[ 2 ] );
+}
+
+void XclExpFmlaCompImpl::FinishChooseFunction( XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nParamCount = rFuncData.GetParamCount();
+ ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
+ OSL_ENSURE( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishChooseFunction - wrong number of tAttr tokens" );
+ // number of choices is parameter count minus 1
+ sal_uInt16 nChoices = nParamCount - 1;
+ // tAttrChoose token contains number of choices
+ Overwrite( rAttrPos[ 0 ] + 2, nChoices );
+ // cache position of the jump table (follows number of choices in tAttrChoose token)
+ sal_uInt16 nJumpArrPos = rAttrPos[ 0 ] + 4;
+ // size of jump table: number of choices, plus 1 for error position
+ sal_uInt16 nJumpArrSize = 2 * (nChoices + 1);
+ // insert the jump table into the tAttrChoose token
+ InsertZeros( nJumpArrPos, nJumpArrSize );
+ // update positions of tAttrGoto tokens after jump table insertion
+ sal_uInt16 nIdx;
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ rAttrPos[ nIdx ] = rAttrPos[ nIdx ] + nJumpArrSize;
+ // update the tAttrGoto tokens (they contain a value one-less to real distance)
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ UpdateAttrGoto( rAttrPos[ nIdx ] );
+ // update the distances in the jump table
+ Overwrite( nJumpArrPos, nJumpArrSize );
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ Overwrite( nJumpArrPos + 2 * nIdx, static_cast< sal_uInt16 >( rAttrPos[ nIdx ] + 4 - nJumpArrPos ) );
+}
+
+XclExpScToken XclExpFmlaCompImpl::ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData )
+{
+ if( rFuncData.IsCalcOnlyParam() )
+ {
+ // skip Calc-only parameter, stop at next ocClose or ocSep
+ aTokData = SkipExpression( aTokData, true );
+ rFuncData.IncParamInfoIdx();
+ }
+ else
+ {
+ // insert Excel-only parameters, modifies param count and class in rFuncData
+ while( rFuncData.IsExcelOnlyParam() )
+ AppendDefaultParam( rFuncData );
+
+ // process the parameter, stop at next ocClose or ocSep
+ PrepareParam( rFuncData );
+ /* #i37355# insert tMissArg token for missing parameters --
+ Excel import filter adds ocMissing token (handled in Factor()),
+ but Calc itself does not do this if a new formula is entered. */
+ switch( aTokData.GetOpCode() )
+ {
+ case ocSep:
+ case ocClose: AppendMissingToken(); break; // empty parameter
+ default: aTokData = Expression( aTokData, false, true );
+ }
+ // finalize the parameter and add special tokens, e.g. for IF or CHOOSE parameters
+ if( mxData->mbOk ) FinishParam( rFuncData );
+ }
+ return aTokData;
+}
+
+void XclExpFmlaCompImpl::PrepareParam( XclExpFuncData& rFuncData )
+{
+ // index of this parameter is equal to number of already finished parameters
+ sal_uInt8 nParamIdx = rFuncData.GetParamCount();
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ switch( nParamIdx )
+ {
+ // add a tAttrIf token before true-parameter (second parameter)
+ case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_IF ); break;
+ // add a tAttrGoto token before false-parameter (third parameter)
+ case 2: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); break;
+ }
+ break;
+
+ case ocChoose:
+ switch( nParamIdx )
+ {
+ // do nothing for first parameter
+ case 0: break;
+ // add a tAttrChoose token before first value parameter (second parameter)
+ case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_CHOOSE ); break;
+ // add a tAttrGoto token before other value parameters
+ default: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
+ }
+ break;
+
+ case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
+ if( nParamIdx == 0 )
+ AppendIntToken( 1 );
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::FinishParam( XclExpFuncData& rFuncData )
+{
+ // increase parameter count, update operand stack
+ rFuncData.FinishParam( PopOperandPos() );
+
+ // append more tokens for parameters of some special functions
+ sal_uInt8 nParamIdx = rFuncData.GetParamCount() - 1;
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
+ if( nParamIdx == 0 )
+ {
+ AppendParenToken();
+ AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
+ }
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::AppendDefaultParam( XclExpFuncData& rFuncData )
+{
+ // prepare parameters of some special functions
+ PrepareParam( rFuncData );
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocExternal:
+ AppendAddInCallToken( rFuncData.GetExtFuncData() );
+ break;
+ case ocEuroConvert:
+ AppendEuroToolCallToken( rFuncData.GetExtFuncData() );
+ break;
+ case ocMacro:
+ // Do not write the OOXML <definedName> element.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ AppendNameToken( 0 ); // dummy to keep parameter count valid
+ else
+ AppendMacroCallToken( rFuncData.GetExtFuncData() );
+ break;
+ default:
+ {
+ if( rFuncData.IsAddInEquivalent() )
+ {
+ AppendAddInCallToken( rFuncData.GetExtFuncData() );
+ }
+ else if( rFuncData.IsMacroFunc() )
+ {
+ // Do not write the OOXML <definedName> element for new _xlfn.
+ // prefixed functions.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ AppendNameToken( 0 ); // dummy to keep parameter count valid
+ else
+ AppendMacroCallToken( rFuncData.GetExtFuncData() );
+ }
+ else
+ {
+ SAL_WARN( "sc.filter", "XclExpFmlaCompImpl::AppendDefaultParam - unknown opcode" );
+ AppendMissingToken(); // to keep parameter count valid
+ }
+ }
+ }
+
+ // update parameter count, add special parameter tokens
+ FinishParam( rFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendTrailingParam( XclExpFuncData& rFuncData )
+{
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ if( nParamCount == 1 )
+ {
+ // Excel needs at least two parameters in IF function
+ PrepareParam( rFuncData );
+ AppendBoolToken( true );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocRound:
+ case ocRoundUp:
+ case ocRoundDown:
+ if( nParamCount == 1 )
+ {
+ // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 0 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocIndex:
+ if( nParamCount == 1 )
+ {
+ // INDEX function needs at least 2 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendMissingToken();
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocExternal:
+ case ocMacro:
+ // external or macro call without parameters needs the external name reference
+ if( nParamCount == 0 )
+ AppendDefaultParam( rFuncData );
+ break;
+
+ case ocGammaDist:
+ if( nParamCount == 3 )
+ {
+ // GAMMADIST function needs 4 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocPoissonDist:
+ if( nParamCount == 2 )
+ {
+ // POISSON function needs 3 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocNormDist:
+ if( nParamCount == 3 )
+ {
+ // NORMDIST function needs 4 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendBoolToken( true );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocLogNormDist:
+ case ocLogInv:
+ switch( nParamCount )
+ {
+ // LOGNORMDIST function needs 3 parameters in Excel
+ case 1:
+ PrepareParam( rFuncData );
+ AppendIntToken( 0 );
+ FinishParam( rFuncData );
+ [[fallthrough]]; // add next default parameter
+ case 2:
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ break;
+ default:;
+ }
+
+ break;
+
+ default:
+ // #i108420# function without parameters stored as macro call needs the external name reference
+ if( (nParamCount == 0) && rFuncData.IsMacroFunc() )
+ AppendDefaultParam( rFuncData );
+
+ }
+}
+
+// reference handling ---------------------------------------------------------
+
+namespace {
+
+bool lclIsRefRel2D( const ScSingleRefData& rRefData )
+{
+ return rRefData.IsColRel() || rRefData.IsRowRel();
+}
+
+bool lclIsRefDel2D( const ScSingleRefData& rRefData )
+{
+ return rRefData.IsColDeleted() || rRefData.IsRowDeleted();
+}
+
+bool lclIsRefRel2D( const ScComplexRefData& rRefData )
+{
+ return lclIsRefRel2D( rRefData.Ref1 ) || lclIsRefRel2D( rRefData.Ref2 );
+}
+
+bool lclIsRefDel2D( const ScComplexRefData& rRefData )
+{
+ return lclIsRefDel2D( rRefData.Ref1 ) || lclIsRefDel2D( rRefData.Ref2 );
+}
+
+} // namespace
+
+SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const
+{
+ if (rRefData.IsTabDeleted())
+ return SCTAB_INVALID;
+
+ if (!rRefData.IsTabRel())
+ // absolute address
+ return rRefData.Tab();
+
+ if (!mxData->mpScBasePos)
+ return SCTAB_INVALID;
+
+ return rRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos).Tab();
+}
+
+bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const
+{
+ /* rRefData.IsFlag3D() determines if sheet name is always visible, even on
+ the own sheet. If 3D references are allowed, the passed reference does
+ not count as 2D reference. */
+
+ // conditional formatting does not allow 3D refs in xls
+ if (mxData && mxData->mrCfg.meType == EXC_FMLATYPE_CONDFMT)
+ return true;
+
+ if (bCheck3DFlag && rRefData.IsFlag3D())
+ return false;
+
+ if (rRefData.IsTabDeleted())
+ return false;
+
+ if (rRefData.IsTabRel())
+ return rRefData.Tab() == 0;
+ else
+ return rRefData.Tab() == GetCurrScTab();
+}
+
+bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const
+{
+ return IsRef2D(rRefData.Ref1, bCheck3DFlag) && IsRef2D(rRefData.Ref2, bCheck3DFlag);
+}
+
+void XclExpFmlaCompImpl::ConvertRefData(
+ ScSingleRefData& rRefData, XclAddress& rXclPos,
+ bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const
+{
+ if( mxData->mpScBasePos )
+ {
+ // *** reference position exists (cell, matrix) - convert to absolute ***
+ ScAddress aAbs = rRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos);
+
+ // convert column index
+ if (bTruncMaxCol && (aAbs.Col() == mnMaxScCol))
+ aAbs.SetCol(mnMaxAbsCol);
+ else if ((aAbs.Col() < 0) || (aAbs.Col() > mnMaxAbsCol))
+ rRefData.SetColDeleted(true);
+ rXclPos.mnCol = static_cast<sal_uInt16>(aAbs.Col()) & mnMaxColMask;
+
+ // convert row index
+ if (bTruncMaxRow && (aAbs.Row() == mnMaxScRow))
+ aAbs.SetRow(mnMaxAbsRow);
+ else if ((aAbs.Row() < 0) || (aAbs.Row() > mnMaxAbsRow))
+ rRefData.SetRowDeleted(true);
+ rXclPos.mnRow = static_cast<sal_uInt32>(aAbs.Row()) & mnMaxRowMask;
+
+ // Update the reference.
+ rRefData.SetAddress(GetRoot().GetDoc().GetSheetLimits(), aAbs, *mxData->mpScBasePos);
+ }
+ else
+ {
+ // *** no reference position (shared, names, condfmt) - use relative values ***
+
+ // convert column index (2-step-cast ScsCOL->sal_Int16->sal_uInt16 to get all bits correctly)
+ sal_Int16 nXclRelCol = static_cast<sal_Int16>(rRefData.Col());
+ rXclPos.mnCol = static_cast< sal_uInt16 >( nXclRelCol ) & mnMaxColMask;
+
+ // convert row index (2-step-cast ScsROW->sal_Int32->sal_uInt32 to get all bits correctly)
+ sal_Int32 nXclRelRow = static_cast<sal_Int32>(rRefData.Row());
+ rXclPos.mnRow = static_cast< sal_uInt32 >( nXclRelRow ) & mnMaxRowMask;
+ }
+
+ // flags for relative column and row
+ if( bNatLangRef )
+ {
+ OSL_ENSURE( meBiff == EXC_BIFF8, "XclExpFmlaCompImpl::ConvertRefData - NLRs only for BIFF8" );
+ // Calc does not support absolute reference mode in natural language references
+ ::set_flag( rXclPos.mnCol, EXC_TOK_NLR_REL );
+ }
+ else
+ {
+ sal_uInt16 rnRelRow = rXclPos.mnRow;
+ sal_uInt16& rnRelField = (meBiff <= EXC_BIFF5) ? rnRelRow : rXclPos.mnCol;
+ ::set_flag( rnRelField, EXC_TOK_REF_COLREL, rRefData.IsColRel() );
+ ::set_flag( rnRelField, EXC_TOK_REF_ROWREL, rRefData.IsRowRel() );
+ }
+}
+
+void XclExpFmlaCompImpl::ConvertRefData(
+ ScComplexRefData& rRefData, XclRange& rXclRange, bool bNatLangRef ) const
+{
+ // convert start and end of the range
+ ConvertRefData( rRefData.Ref1, rXclRange.maFirst, bNatLangRef, false, false );
+ bool bTruncMaxCol = !rRefData.Ref1.IsColDeleted() && (rXclRange.maFirst.mnCol == 0);
+ bool bTruncMaxRow = !rRefData.Ref1.IsRowDeleted() && (rXclRange.maFirst.mnRow == 0);
+ ConvertRefData( rRefData.Ref2, rXclRange.maLast, bNatLangRef, bTruncMaxCol, bTruncMaxRow );
+}
+
+XclExpRefLogEntry* XclExpFmlaCompImpl::GetNewRefLogEntry()
+{
+ if( mxData->mpRefLog )
+ {
+ mxData->mpRefLog->emplace_back();
+ return &mxData->mpRefLog->back();
+ }
+ return nullptr;
+}
+
+void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData )
+{
+ // get the Excel address components, adjust internal data in aRefData
+ bool bNatLangRef = (meBiff == EXC_BIFF8) && mxData->mpScBasePos && (rTokData.GetOpCode() == ocColRowName);
+ ScSingleRefData aRefData = *rTokData.mpScToken->GetSingleRef();
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclPos, bNatLangRef, false, false );
+
+ if( bNatLangRef )
+ {
+ OSL_ENSURE( aRefData.IsColRel() != aRefData.IsRowRel(),
+ "XclExpFmlaCompImpl::ProcessCellRef - broken natural language reference" );
+ // create tNlr token for natural language reference
+ sal_uInt8 nSubId = aRefData.IsColRel() ? EXC_TOK_NLR_COLV : EXC_TOK_NLR_ROWV;
+ AppendOperandTokenId( EXC_TOKID_NLR, rTokData.mnSpaces );
+ Append( nSubId );
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ // store external cell contents in CRN records
+ if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCell(aRefData, *mxData->mpScBasePos);
+
+ // create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token
+ if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr != nullptr))
+ {
+ // 2D reference (not in defined names, but allowed in range lists)
+ sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN :
+ (lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR : EXC_TOKID_REF);
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ AppendAddress( aXclPos );
+ }
+ else if( mxData->mpLinkMgr ) // 3D reference
+ {
+ // 1-based EXTERNSHEET index and 0-based Excel sheet index
+ sal_uInt16 nExtSheet, nXclTab;
+ mxData->mpLinkMgr->FindExtSheet( nExtSheet, nXclTab, GetScTab( aRefData ), GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nXclTab );
+ Append( nXclTab );
+ }
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ // 3D ref in cond. format, or 2D ref in name
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData )
+{
+ // get the Excel address components, adjust internal data in aRefData
+ ScComplexRefData aRefData = *rTokData.mpScToken->GetDoubleRef();
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclRange, false );
+
+ // store external cell contents in CRN records
+ if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCellRange(aRefData, *mxData->mpScBasePos);
+
+ // create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token
+ if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr != nullptr))
+ {
+ // 2D reference (not in name formulas, but allowed in range lists)
+ sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN :
+ (lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR : EXC_TOKID_AREA);
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ AppendRange( aXclRange );
+ }
+ else if( mxData->mpLinkMgr ) // 3D reference
+ {
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstXclTab, nLastXclTab;
+ mxData->mpLinkMgr->FindExtSheet( nExtSheet, nFirstXclTab, nLastXclTab,
+ GetScTab( aRefData.Ref1 ), GetScTab( aRefData.Ref2 ), GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstXclTab );
+ Append( nLastXclTab );
+ }
+ AppendRange( aXclRange );
+ }
+ else
+ {
+ // 3D ref in cond. format, or 2D ref in name
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternalCellRef( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ // get the Excel address components, adjust internal data in aRefData
+ ScSingleRefData aRefData = *rTokData.mpScToken->GetSingleRef();
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclPos, false, false, false );
+
+ // store external cell contents in CRN records
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aTabName = rTokData.mpScToken->GetString().getString();
+ if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCell(nFileId, aTabName, aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
+ mxData->mpLinkMgr->FindExtSheet( nFileId, aTabName, 1, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstSBTab );
+ Append( nLastSBTab );
+ }
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternalRangeRef( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ // get the Excel address components, adjust internal data in aRefData
+ ScComplexRefData aRefData = *rTokData.mpScToken->GetDoubleRef();
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclRange, false );
+
+ // store external cell contents in CRN records
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aTabName = rTokData.mpScToken->GetString().getString();
+ if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCellRange(nFileId, aTabName, aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
+ sal_uInt16 nTabSpan = static_cast<sal_uInt16>(aRefData.Ref2.Tab() - aRefData.Ref1.Tab() + 1);
+ mxData->mpLinkMgr->FindExtSheet(
+ nFileId, aTabName, nTabSpan, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry());
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstSBTab );
+ Append( nLastSBTab );
+ }
+ AppendRange( aXclRange );
+ }
+ else
+ {
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessDefinedName( const XclExpScToken& rTokData )
+{
+ sal_Int16 nSheet = rTokData.mpScToken->GetSheet();
+ SCTAB nTab = (nSheet < 0 ? SCTAB_GLOBAL : nSheet);
+
+ XclExpNameManager& rNameMgr = GetNameManager();
+ sal_uInt16 nNameIdx = rNameMgr.InsertName(nTab, rTokData.mpScToken->GetIndex(), GetCurrScTab());
+ if( nNameIdx != 0 )
+ {
+ // global names always with tName token, local names dependent on config
+ SCTAB nScTab = rNameMgr.GetScTab( nNameIdx );
+ if( (nScTab == SCTAB_GLOBAL) || (!mxData->mrCfg.mb3DRefOnly && (nScTab == GetCurrScTab())) )
+ {
+ AppendNameToken( nNameIdx, rTokData.mnSpaces );
+ }
+ else if( mxData->mpLinkMgr )
+ {
+ // use the same special EXTERNNAME to refer to any local name
+ sal_uInt16 nExtSheet = mxData->mpLinkMgr->FindExtSheet( EXC_EXTSH_OWNDOC );
+ AppendNameXToken( nExtSheet, nNameIdx, rTokData.mnSpaces );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+ // volatile names (containing volatile functions)
+ mxData->mbVolatile |= rNameMgr.IsVolatile( nNameIdx );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessExternalName( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ ScExternalRefManager& rExtRefMgr = *GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aName = rTokData.mpScToken->GetString().getString();
+ ScExternalRefCache::TokenArrayRef xArray = rExtRefMgr.getRangeNameTokens( nFileId, aName );
+ if( xArray )
+ {
+ // store external cell contents in CRN records
+ if( mxData->mpScBasePos )
+ {
+ FormulaTokenArrayPlainIterator aIter(*xArray);
+ for( FormulaToken* pScToken = aIter.First(); pScToken; pScToken = aIter.Next() )
+ {
+ if( pScToken->IsExternalRef() )
+ {
+ switch( pScToken->GetType() )
+ {
+ case svExternalSingleRef:
+ {
+ ScSingleRefData aRefData = *pScToken->GetSingleRef();
+ mxData->mpLinkMgr->StoreCell(
+ nFileId, pScToken->GetString().getString(), aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData aRefData = *pScToken->GetDoubleRef();
+ mxData->mpLinkMgr->StoreCellRange(
+ nFileId, pScToken->GetString().getString(), aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+ }
+ break;
+ default:
+ ; // nothing, avoid compiler warning
+ }
+ }
+ }
+ }
+
+ // insert the new external name and create the tNameX token
+ sal_uInt16 nExtSheet = 0, nExtName = 0;
+ const OUString* pFile = rExtRefMgr.getExternalFileName( nFileId );
+ if( pFile && mxData->mpLinkMgr->InsertExtName( nExtSheet, nExtName, *pFile, aName, xArray ) )
+ {
+ AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
+ return;
+ }
+ }
+ }
+
+ // on any error: create a #NAME? error
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+}
+
+// token vector ---------------------------------------------------------------
+
+void XclExpFmlaCompImpl::PushOperandPos( sal_uInt16 nTokPos )
+{
+ mxData->maOpPosStack.push_back( nTokPos );
+}
+
+void XclExpFmlaCompImpl::PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands )
+{
+ PushOperandPos( nTokPos );
+ OSL_ENSURE( rxOperands, "XclExpFmlaCompImpl::AppendOperatorTokenId - missing operand list" );
+ if( mxData->maOpListVec.size() <= nTokPos )
+ mxData->maOpListVec.resize( nTokPos + 1, XclExpOperandListRef() );
+ mxData->maOpListVec[ nTokPos ] = rxOperands;
+}
+
+sal_uInt16 XclExpFmlaCompImpl::PopOperandPos()
+{
+ OSL_ENSURE( !mxData->mbOk || !mxData->maOpPosStack.empty(), "XclExpFmlaCompImpl::PopOperandPos - token stack broken" );
+ mxData->mbOk &= !mxData->maOpPosStack.empty();
+ if( mxData->mbOk )
+ {
+ sal_uInt16 nTokPos = mxData->maOpPosStack.back();
+ mxData->maOpPosStack.pop_back();
+ return nTokPos;
+ }
+ return 0;
+}
+
+namespace {
+
+void lclAppend( ScfUInt8Vec& orVector, sal_uInt16 nData )
+{
+ orVector.resize( orVector.size() + 2 );
+ ShortToSVBT16( nData, &*(orVector.end() - 2) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, sal_uInt32 nData )
+{
+ orVector.resize( orVector.size() + 4 );
+ UInt32ToSVBT32( nData, &*(orVector.end() - 4) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, double fData )
+{
+ orVector.resize( orVector.size() + 8 );
+ DoubleToSVBT64( fData, &*(orVector.end() - 8) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nStrFlags )
+{
+ XclExpStringRef xXclStr = XclExpStringHelper::CreateString( rRoot, rString, nStrFlags, EXC_TOK_STR_MAXLEN );
+ size_t nSize = orVector.size();
+ orVector.resize( nSize + xXclStr->GetSize() );
+ xXclStr->WriteToMem( &orVector[ nSize ] );
+}
+
+} // namespace
+
+void XclExpFmlaCompImpl::Append( sal_uInt8 nData )
+{
+ mxData->maTokVec.push_back( nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt8 nData, size_t nCount )
+{
+ mxData->maTokVec.resize( mxData->maTokVec.size() + nCount, nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt16 nData )
+{
+ lclAppend( mxData->maTokVec, nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt32 nData )
+{
+ lclAppend( mxData->maTokVec, nData );
+}
+
+void XclExpFmlaCompImpl::Append( double fData )
+{
+ lclAppend( mxData->maTokVec, fData );
+}
+
+void XclExpFmlaCompImpl::Append( const OUString& rString )
+{
+ lclAppend( mxData->maTokVec, GetRoot(), rString, XclStrFlags::EightBitLength );
+}
+
+void XclExpFmlaCompImpl::AppendAddress( const XclAddress& rXclPos )
+{
+ Append( static_cast<sal_uInt16>(rXclPos.mnRow) );
+ if( meBiff <= EXC_BIFF5 )
+ Append( static_cast< sal_uInt8 >( rXclPos.mnCol ) );
+ else
+ Append( rXclPos.mnCol );
+}
+
+void XclExpFmlaCompImpl::AppendRange( const XclRange& rXclRange )
+{
+ Append( static_cast<sal_uInt16>(rXclRange.maFirst.mnRow) );
+ Append( static_cast<sal_uInt16>(rXclRange.maLast.mnRow) );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( static_cast< sal_uInt8 >( rXclRange.maFirst.mnCol ) );
+ Append( static_cast< sal_uInt8 >( rXclRange.maLast.mnCol ) );
+ }
+ else
+ {
+ Append( rXclRange.maFirst.mnCol );
+ Append( rXclRange.maLast.mnCol );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount )
+{
+ if( nCount > 0 )
+ {
+ Append( EXC_TOKID_ATTR );
+ Append( EXC_TOK_ATTR_SPACE );
+ Append( nType );
+ Append( nCount );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
+ PushOperandPos( GetSize() );
+ Append( nTokenId );
+}
+
+void XclExpFmlaCompImpl::AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_INT, nSpaces );
+ Append( nValue );
+}
+
+void XclExpFmlaCompImpl::AppendNumToken( double fValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_NUM, nSpaces );
+ Append( fValue );
+}
+
+void XclExpFmlaCompImpl::AppendBoolToken( bool bValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_BOOL, nSpaces );
+ Append( bValue ? EXC_TOK_BOOL_TRUE : EXC_TOK_BOOL_FALSE );
+}
+
+void XclExpFmlaCompImpl::AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_ERR, nSpaces );
+ Append( nErrCode );
+}
+
+void XclExpFmlaCompImpl::AppendMissingToken( sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_MISSARG, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces )
+{
+ if( nNameIdx > 0 )
+ {
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_NAME, EXC_TOKCLASS_REF ), nSpaces );
+ Append( nNameIdx );
+ Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME );
+}
+
+void XclExpFmlaCompImpl::AppendMissingNameToken( const OUString& rName, sal_uInt8 nSpaces )
+{
+ sal_uInt16 nNameIdx = GetNameManager().InsertRawName( rName );
+ AppendNameToken( nNameIdx, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), nSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ Append( 0, 8 );
+ Append( nExtName );
+ Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
+}
+
+void XclExpFmlaCompImpl::AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( rExtFuncData.maFuncName, rExtFuncData.mbVBasic, true, rExtFuncData.mbHidden );
+ AppendNameToken( nNameIdx );
+}
+
+void XclExpFmlaCompImpl::AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ OUString aXclFuncName;
+ if( mxData->mpLinkMgr && ScGlobal::GetAddInCollection()->GetExcelName( rExtFuncData.maFuncName, GetUILanguage(), aXclFuncName ) )
+ {
+ sal_uInt16 nExtSheet, nExtName;
+ if( mxData->mpLinkMgr->InsertAddIn( nExtSheet, nExtName, aXclFuncName ) )
+ {
+ AppendNameXToken( nExtSheet, nExtName );
+ return;
+ }
+ }
+ AppendMacroCallToken( rExtFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ sal_uInt16 nExtSheet(0), nExtName(0);
+ if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertEuroTool( nExtSheet, nExtName, rExtFuncData.maFuncName ) )
+ AppendNameXToken( nExtSheet, nExtName );
+ else
+ AppendMacroCallToken( rExtFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
+ PushOperatorPos( GetSize(), rxOperands );
+ Append( nTokenId );
+}
+
+void XclExpFmlaCompImpl::AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, true );
+ AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
+ AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ for( sal_uInt8 nOpIdx = 0; nOpIdx < nOpCount; ++nOpIdx )
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPX, false );
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, EXC_TOKCLASS_VAL ), xOperands );
+ Append( nOpCount );
+ Append( nXclFuncIdx );
+}
+
+void XclExpFmlaCompImpl::AppendFuncToken( const XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nXclFuncIdx = rFuncData.GetXclFuncIdx();
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ sal_uInt8 nRetClass = rFuncData.GetReturnClass();
+
+ if( (nXclFuncIdx == EXC_FUNCID_SUM) && (nParamCount == 1) )
+ {
+ // SUM with only one parameter
+ AppendOperatorTokenId( EXC_TOKID_ATTR, rFuncData.GetOperandList() );
+ Append( EXC_TOK_ATTR_SUM );
+ Append( sal_uInt16( 0 ) );
+ }
+ else if( rFuncData.IsFixedParamCount() )
+ {
+ // fixed number of parameters
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNC, nRetClass ), rFuncData.GetOperandList() );
+ Append( nXclFuncIdx );
+ }
+ else
+ {
+ // variable number of parameters
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, nRetClass ), rFuncData.GetOperandList() );
+ Append( nParamCount );
+ Append( nXclFuncIdx );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendParenToken( sal_uInt8 nOpenSpaces, sal_uInt8 nCloseSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_OPEN, nOpenSpaces );
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
+ Append( EXC_TOKID_PAREN );
+}
+
+void XclExpFmlaCompImpl::AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType )
+{
+ // store the start position of the token
+ rFuncData.AppendAttrPos( GetSize() );
+ // create the tAttr token
+ Append( EXC_TOKID_ATTR );
+ Append( nAttrType );
+ Append( sal_uInt16( 0 ) ); // placeholder that will be updated later
+}
+
+void XclExpFmlaCompImpl::InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize )
+{
+ // insert zeros into the token array
+ OSL_ENSURE( nInsertPos < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Insert - invalid position" );
+ mxData->maTokVec.insert( mxData->maTokVec.begin() + nInsertPos, nInsertSize, 0 );
+
+ // update positions of operands waiting for an operator
+ for( auto& rOpPos : mxData->maOpPosStack )
+ if( nInsertPos <= rOpPos )
+ rOpPos += nInsertSize;
+
+ // update operand lists of all operator tokens
+ if( nInsertPos < mxData->maOpListVec.size() )
+ mxData->maOpListVec.insert( mxData->maOpListVec.begin() + nInsertPos, nInsertSize, XclExpOperandListRef() );
+ for( auto& rxOpList : mxData->maOpListVec )
+ if( rxOpList )
+ for( auto& rOp : *rxOpList )
+ if( nInsertPos <= rOp.mnTokPos )
+ rOp.mnTokPos += nInsertSize;
+}
+
+void XclExpFmlaCompImpl::Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset )
+{
+ OSL_ENSURE( o3tl::make_unsigned( nWriteToPos + 1 ) < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Overwrite - invalid position" );
+ ShortToSVBT16( nOffset, &mxData->maTokVec[ nWriteToPos ] );
+}
+
+void XclExpFmlaCompImpl::UpdateAttrGoto( sal_uInt16 nAttrPos )
+{
+ /* tAttrGoto contains distance from end of tAttr token to position behind
+ the function token (for IF or CHOOSE function), which is currently at
+ the end of the token array. Additionally this distance is decreased by
+ one, for whatever reason. So we have to subtract 4 and 1 from the
+ distance between the tAttr token start and the end of the token array. */
+ Overwrite( nAttrPos + 2, static_cast< sal_uInt16 >( GetSize() - nAttrPos - 5 ) );
+}
+
+bool XclExpFmlaCompImpl::IsSpaceToken( sal_uInt16 nPos ) const
+{
+ return
+ (o3tl::make_unsigned( nPos + 4 ) <= mxData->maTokVec.size()) &&
+ (mxData->maTokVec[ nPos ] == EXC_TOKID_ATTR) &&
+ (mxData->maTokVec[ nPos + 1 ] == EXC_TOK_ATTR_SPACE);
+}
+
+void XclExpFmlaCompImpl::RemoveTrailingParen()
+{
+ // remove trailing tParen token
+ if( !mxData->maTokVec.empty() && (mxData->maTokVec.back() == EXC_TOKID_PAREN) )
+ mxData->maTokVec.pop_back();
+ // remove remaining tAttrSpace tokens
+ while( (mxData->maTokVec.size() >= 4) && IsSpaceToken( GetSize() - 4 ) )
+ mxData->maTokVec.erase( mxData->maTokVec.end() - 4, mxData->maTokVec.end() );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData )
+{
+ mxData->maExtDataVec.push_back( nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData, size_t nCount )
+{
+ mxData->maExtDataVec.resize( mxData->maExtDataVec.size() + nCount, nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt16 nData )
+{
+ lclAppend( mxData->maExtDataVec, nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( double fData )
+{
+ lclAppend( mxData->maExtDataVec, fData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( const OUString& rString )
+{
+ lclAppend( mxData->maExtDataVec, GetRoot(), rString, (meBiff == EXC_BIFF8) ? XclStrFlags::NONE : XclStrFlags::EightBitLength );
+}
+
+namespace {
+
+void lclInitOwnTab( ScSingleRefData& rRef, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ if( b3DRefOnly )
+ {
+ // no reduction to 2D reference, if global link manager is used
+ rRef.SetFlag3D(true);
+ }
+ else if( rScPos.Tab() == nCurrScTab )
+ {
+ rRef.SetRelTab(0);
+ }
+}
+
+void lclPutCellToTokenArray( ScTokenArray& rScTokArr, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ ScSingleRefData aRef;
+ aRef.InitAddress( rScPos );
+ lclInitOwnTab( aRef, rScPos, nCurrScTab, b3DRefOnly );
+ rScTokArr.AddSingleReference( aRef );
+}
+
+void lclPutRangeToTokenArray( ScTokenArray& rScTokArr, const ScRange& rScRange, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ if( rScRange.aStart == rScRange.aEnd )
+ {
+ lclPutCellToTokenArray( rScTokArr, rScRange.aStart, nCurrScTab, b3DRefOnly );
+ }
+ else
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange( rScRange );
+ lclInitOwnTab( aRef.Ref1, rScRange.aStart, nCurrScTab, b3DRefOnly );
+ lclInitOwnTab( aRef.Ref2, rScRange.aEnd, nCurrScTab, b3DRefOnly );
+ rScTokArr.AddDoubleReference( aRef );
+ }
+}
+
+} // namespace
+
+XclExpFormulaCompiler::XclExpFormulaCompiler( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxImpl( std::make_shared<XclExpFmlaCompImpl>( rRoot ) )
+{
+}
+
+XclExpFormulaCompiler::~XclExpFormulaCompiler()
+{
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ return mxImpl->CreateFormula( eType, rScTokArr, pScBasePos, pRefLog );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScAddress& rScPos )
+{
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ lclPutCellToTokenArray( aScTokArr, rScPos, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRange& rScRange )
+{
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ lclPutRangeToTokenArray( aScTokArr, rScRange, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges )
+{
+ size_t nCount = rScRanges.size();
+ if( nCount == 0 )
+ return XclTokenArrayRef();
+
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ SCTAB nCurrScTab = GetCurrScTab();
+ bool b3DRefOnly = mxImpl->Is3DRefOnly( eType );
+ for( size_t nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ if( nIdx > 0 )
+ aScTokArr.AddOpCode( ocUnion );
+ lclPutRangeToTokenArray( aScTokArr, rScRanges[ nIdx ], nCurrScTab, b3DRefOnly );
+ }
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateErrorFormula( sal_uInt8 nErrCode )
+{
+ return mxImpl->CreateErrorFormula( nErrCode );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateSpecialRefFormula(
+ sal_uInt8 nTokenId, const XclAddress& rXclPos )
+{
+ return mxImpl->CreateSpecialRefFormula( nTokenId, rXclPos );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula(
+ sal_uInt16 nExtSheet, sal_uInt16 nExtName )
+{
+ return mxImpl->CreateNameXFormula( nExtSheet, nExtName );
+}
+
+bool XclExpFormulaCompiler::IsRef2D( const ScSingleRefData& rRefData ) const
+{
+ return mxImpl->IsRef2D(rRefData, true);
+}
+
+bool XclExpFormulaCompiler::IsRef2D( const ScComplexRefData& rRefData ) const
+{
+ return mxImpl->IsRef2D(rRefData, true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx
new file mode 100644
index 0000000000..aa21902dfb
--- /dev/null
+++ b/sc/source/filter/excel/xehelper.cxx
@@ -0,0 +1,1110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/XUriReferenceFactory.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/string_view.hxx>
+#include <sfx2/objsh.hxx>
+#include <vcl/font.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/itemset.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/outlobj.hxx>
+#include <scitems.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/editids.hrc>
+#include <osl/file.hxx>
+
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <editutil.hxx>
+#include <patattr.hxx>
+#include <scmatrix.hxx>
+#include <xestyle.hxx>
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xecontent.hxx>
+#include <xelink.hxx>
+#include <xehelper.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::i18n::XBreakIterator;
+
+// Export progress bar ========================================================
+
+XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
+ mpSubProgress( nullptr ),
+ mpSubRowCreate( nullptr ),
+ mpSubRowFinal( nullptr ),
+ mnSegRowFinal( SCF_INV_SEGMENT ),
+ mnRowCount( 0 )
+{
+}
+
+XclExpProgressBar::~XclExpProgressBar()
+{
+}
+
+void XclExpProgressBar::Initialize()
+{
+ const ScDocument& rDoc = GetDoc();
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+
+ // *** segment: creation of ROW records *** -------------------------------
+
+ sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
+ mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
+ maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
+
+ for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
+ {
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ SCCOL nLastUsedScCol;
+ SCROW nLastUsedScRow;
+ rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
+ std::size_t nSegSize = static_cast< std::size_t >( nLastUsedScRow + 1 );
+ maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
+ }
+ }
+
+ // *** segment: writing all ROW records *** -------------------------------
+
+ mnSegRowFinal = mxProgress->AddSegment( 1000 );
+ // sub progress bar and segment are created later in ActivateFinalRowsSegment()
+}
+
+void XclExpProgressBar::IncRowRecordCount()
+{
+ ++mnRowCount;
+}
+
+void XclExpProgressBar::ActivateCreateRowsSegment()
+{
+ OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
+ "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
+ sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
+ OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
+ if( nSeg != SCF_INV_SEGMENT )
+ {
+ mpSubProgress = mpSubRowCreate;
+ mpSubProgress->ActivateSegment( nSeg );
+ }
+ else
+ mpSubProgress = nullptr;
+}
+
+void XclExpProgressBar::ActivateFinalRowsSegment()
+{
+ if( !mpSubRowFinal && (mnRowCount > 0) )
+ {
+ mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
+ mpSubRowFinal->AddSegment( mnRowCount );
+ }
+ mpSubProgress = mpSubRowFinal;
+ if( mpSubProgress )
+ mpSubProgress->Activate();
+}
+
+void XclExpProgressBar::Progress()
+{
+ if( mpSubProgress && !mpSubProgress->IsFull() )
+ mpSubProgress->Progress();
+}
+
+// Calc->Excel cell address/range conversion ==================================
+
+namespace {
+
+/** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
+void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
+{
+ rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
+ rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
+}
+
+} // namespace
+
+XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
+ XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
+{
+}
+
+// cell address ---------------------------------------------------------------
+
+bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
+{
+ // ScAddress::operator<=() doesn't do what we want here
+ bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
+ bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
+ bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
+
+ bool bValid = bValidCol && bValidRow && bValidTab;
+ if( !bValid )
+ {
+ mbColTrunc |= !bValidCol;
+ mbRowTrunc |= !bValidRow;
+ }
+ if( !bValid && bWarn )
+ {
+ mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
+ mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
+ }
+ return bValid;
+}
+
+bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
+ const ScAddress& rScPos, bool bWarn )
+{
+ bool bValid = CheckAddress( rScPos, bWarn );
+ if( bValid )
+ lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
+ return bValid;
+}
+
+XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
+{
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
+ lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
+ return aXclPos;
+}
+
+// cell range -----------------------------------------------------------------
+
+bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
+{
+ return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
+}
+
+bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
+{
+ rScRange.PutInOrder();
+
+ // check start position
+ bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
+ if( bValidStart )
+ {
+ // check & correct end position
+ ScAddress& rScEnd = rScRange.aEnd;
+ if( !CheckAddress( rScEnd, bWarn ) )
+ {
+ rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
+ rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
+ rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
+ }
+ }
+
+ return bValidStart;
+}
+
+bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
+ const ScRange& rScRange, bool bWarn )
+{
+ // check start position
+ bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
+ if( bValidStart )
+ {
+ lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
+
+ // check & correct end position
+ SCCOL nScCol2 = rScRange.aEnd.Col();
+ SCROW nScRow2 = rScRange.aEnd.Row();
+ if( !CheckAddress( rScRange.aEnd, bWarn ) )
+ {
+ nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
+ nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
+ }
+ lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
+ }
+ return bValidStart;
+}
+
+// cell range list ------------------------------------------------------------
+
+void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
+{
+ for ( size_t nRange = rScRanges.size(); nRange > 0; )
+ {
+ ScRange & rScRange = rScRanges[ --nRange ];
+ if( !CheckRange( rScRange, bWarn ) )
+ rScRanges.Remove(nRange);
+ }
+}
+
+void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
+ const ScRangeList& rScRanges, bool bWarn )
+{
+ rXclRanges.clear();
+ for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
+ {
+ const ScRange & rScRange = rScRanges[ nPos ];
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ if( ConvertRange( aXclRange, rScRange, bWarn ) )
+ rXclRanges.push_back( aXclRange );
+ }
+}
+
+// EditEngine->String conversion ==============================================
+
+namespace {
+
+OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
+{
+ const OUString& aRepr = rUrlField.GetRepresentation();
+ // no representation -> use URL
+ return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
+}
+
+} // namespace
+
+XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
+ XclExpRoot( rRoot ),
+ maScPos( rScPos ),
+ mbMultipleUrls( false )
+{
+}
+
+XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
+{
+}
+
+OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
+{
+ OUString aUrlRepr;
+
+ if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
+ {
+ // there was/is already a HLINK record
+ mbMultipleUrls = static_cast< bool >(mxLinkRec);
+
+ mxLinkRec = new XclExpHyperlink( GetRoot(), rUrlField, maScPos );
+
+ if( const OUString* pRepr = mxLinkRec->GetRepr() )
+ aUrlRepr = *pRepr;
+
+ // add URL to note text
+ maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
+ }
+
+ // no hyperlink representation from Excel HLINK record -> use it from text field
+ return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
+}
+
+bool XclExpHyperlinkHelper::HasLinkRecord() const
+{
+ return !mbMultipleUrls && mxLinkRec;
+}
+
+XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord() const
+{
+ if( HasLinkRecord() )
+ return mxLinkRec;
+ return XclExpHyperlinkRef();
+}
+
+namespace {
+
+/** Creates a new formatted string from the passed unformatted string.
+
+ Creates a Unicode string or a byte string, depending on the current BIFF
+ version contained in the passed XclExpRoot object. May create a formatted
+ string object, if the text contains different script types.
+
+ @param pCellAttr
+ Cell attributes used for font formatting.
+ @param nFlags
+ Modifiers for string export.
+ @param nMaxLen
+ The maximum number of characters to store in this string.
+ @return
+ The new string object.
+ */
+XclExpStringRef lclCreateFormattedString(
+ const XclExpRoot& rRoot, const OUString& rText, const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ /* Create an empty Excel string object with correctly initialized BIFF mode,
+ because this function only uses Append() functions that require this. */
+ XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, OUString(), nFlags, nMaxLen );
+
+ // script type handling
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
+
+ // font buffer and cell item set
+ XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
+
+ // process all script portions
+ sal_Int32 nPortionPos = 0;
+ sal_Int32 nTextLen = rText.getLength();
+ while( nPortionPos < nTextLen )
+ {
+ // get script type and end position of next script portion
+ sal_Int16 nScript = xBreakIt->getScriptType( rText, nPortionPos );
+ sal_Int32 nPortionEnd = xBreakIt->endOfScript( rText, nPortionPos, nScript );
+
+ // reuse previous script for following weak portions
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+
+ // construct font from current text portion
+ SvxFont aFont(XclExpFontHelper::GetFontFromItemSet(rRoot, rItemSet, nScript));
+ model::ComplexColor aComplexColor;
+ ScPatternAttr::fillColor(aComplexColor, rItemSet, ScAutoFontColorMode::Raw);
+
+ // Excel start position of this portion
+ sal_Int32 nXclPortionStart = xString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *xString, rRoot, rText.subView( nPortionPos, nPortionEnd - nPortionPos ) );
+ if( nXclPortionStart < xString->Len() )
+ {
+ // insert font into buffer
+ sal_uInt16 nFontIdx = rFontBuffer.Insert(aFont, aComplexColor, EXC_COLOR_CELLTEXT);
+ // insert font index into format run vector
+ xString->AppendFormat( nXclPortionStart, nFontIdx );
+ }
+
+ // go to next script portion
+ nLastScript = nScript;
+ nPortionPos = nPortionEnd;
+ }
+
+ return xString;
+}
+
+/** Creates a new formatted string from an edit engine text object.
+
+ Creates a Unicode string or a byte string, depending on the current BIFF
+ version contained in the passed XclExpRoot object.
+
+ @param rEE
+ The edit engine in use. The text object must already be set.
+ @param nFlags
+ Modifiers for string export.
+ @param nMaxLen
+ The maximum number of characters to store in this string.
+ @return
+ The new string object.
+ */
+XclExpStringRef lclCreateFormattedString(
+ const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ /* Create an empty Excel string object with correctly initialized BIFF mode,
+ because this function only uses Append() functions that require this. */
+ XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, OUString(), nFlags, nMaxLen );
+
+ // font buffer and helper item set for edit engine -> Calc item conversion
+ XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *rRoot.GetDoc().GetPool() );
+
+ // script type handling
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
+
+ // process all paragraphs
+ sal_Int32 nParaCount = rEE.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ ESelection aSel( nPara, 0 );
+ OUString aParaText( rEE.GetText( nPara ) );
+
+ std::vector<sal_Int32> aPosList;
+ rEE.GetPortions( nPara, aPosList );
+
+ // process all portions in the paragraph
+ for( const auto& rPos : aPosList )
+ {
+ aSel.nEndPos = rPos;
+ OUString aXclPortionText = aParaText.copy( aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
+
+ aItemSet.ClearItem();
+ SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
+ ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
+
+ // get escapement value
+ short nEsc = aEditSet.Get( EE_CHAR_ESCAPEMENT ).GetEsc();
+
+ // process text fields
+ bool bIsHyperlink = false;
+ if( aSel.nStartPos + 1 == aSel.nEndPos )
+ {
+ // test if the character is a text field
+ if( const SvxFieldItem* pItem = aEditSet.GetItemIfSet( EE_FEATURE_FIELD, false ) )
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ if( const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>( pField ) )
+ {
+ // convert URL field to string representation
+ aXclPortionText = pLinkHelper ?
+ pLinkHelper->ProcessUrlField( *pUrlField ) :
+ lclGetUrlRepresentation( *pUrlField );
+ bIsHyperlink = true;
+ }
+ else
+ {
+ OSL_FAIL( "lclCreateFormattedString - unknown text field" );
+ aXclPortionText.clear();
+ }
+ }
+ }
+
+ // Excel start position of this portion
+ sal_Int32 nXclPortionStart = xString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
+ if( (nXclPortionStart < xString->Len()) || (aParaText.isEmpty()) )
+ {
+ /* Construct font from current edit engine text portion. Edit engine
+ creates different portions for different script types, no need to loop. */
+ sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+ SvxFont aFont( XclExpFontHelper::GetFontFromItemSet(rRoot, aItemSet, nScript));
+ model::ComplexColor aComplexColor;
+ ScPatternAttr::fillColor(aComplexColor, aItemSet, ScAutoFontColorMode::Raw);
+
+ nLastScript = nScript;
+
+ // add escapement
+ aFont.SetEscapement( nEsc );
+ // modify automatic font color for hyperlinks
+ if (bIsHyperlink && aItemSet.Get(ATTR_FONT_COLOR).GetValue() == COL_AUTO)
+ aComplexColor.setFinalColor(COL_LIGHTBLUE);
+
+ // insert font into buffer
+ sal_uInt16 nFontIdx = rFontBuffer.Insert(aFont, aComplexColor, EXC_COLOR_CELLTEXT);
+ // insert font index into format run vector
+ xString->AppendFormat( nXclPortionStart, nFontIdx );
+ }
+
+ aSel.nStartPos = aSel.nEndPos;
+ }
+
+ // add trailing newline (important for correct character index calculation)
+ if( nPara + 1 < nParaCount )
+ XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
+ }
+
+ return xString;
+}
+
+} // namespace
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString = std::make_shared<XclExpString>();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ xString->Assign( rString, nFlags, nMaxLen );
+ else
+ xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString = CreateString( rRoot, OUString(), nFlags, nMaxLen );
+ AppendChar( *xString, rRoot, cChar );
+ return xString;
+}
+
+void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, std::u16string_view rString )
+{
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ rXclString.Append( rString );
+ else
+ rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
+}
+
+void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
+{
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ rXclString.Append( rtl::OUStringChar(cChar) );
+ else
+ rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
+}
+
+XclExpStringRef XclExpStringHelper::CreateCellString(
+ const XclExpRoot& rRoot, const OUString& rString, const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ return lclCreateFormattedString(rRoot, rString, pCellAttr, nFlags, nMaxLen);
+}
+
+XclExpStringRef XclExpStringHelper::CreateCellString(
+ const XclExpRoot& rRoot, const EditTextObject& rEditText, const ScPatternAttr* pCellAttr,
+ XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString;
+
+ // formatted cell
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+
+ // default items
+ const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
+ auto pEEItemSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
+ ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
+ rEE.SetDefaults( std::move(pEEItemSet) ); // edit engine takes ownership
+
+ // create the string
+ rEE.SetTextCurrentDefaults(rEditText);
+ xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
+ XclStrFlags nFlags )
+{
+ XclExpStringRef xString;
+ if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
+ {
+ EditEngine& rEE = rRoot.GetDrawEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+ // create the string
+ rEE.SetText( pParaObj->GetTextObject() );
+ xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+ // limit formats - TODO: BIFF dependent
+ if( !xString->IsEmpty() )
+ {
+ xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
+ xString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
+ // create BIFF dependent empty Excel string
+ xString = CreateString( rRoot, OUString(), nFlags );
+ }
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const EditTextObject& rEditObj,
+ XclStrFlags nFlags )
+{
+ XclExpStringRef xString;
+ EditEngine& rEE = rRoot.GetDrawEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+ rEE.SetText( rEditObj );
+ xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+ // limit formats - TODO: BIFF dependent
+ if( !xString->IsEmpty() )
+ {
+ xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
+ xString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ return xString;
+}
+
+sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ sal_Int32 nStrPos = 0;
+ sal_Int32 nStrLen = rString.getLength();
+ sal_Int16 nScript = ApiScriptType::WEAK;
+ while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
+ {
+ nScript = xBreakIt->getScriptType( rString, nStrPos );
+ nStrPos = xBreakIt->endOfScript( rString, nStrPos, nScript );
+ }
+ return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
+}
+
+// Header/footer conversion ===================================================
+
+XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mrEE( rRoot.GetHFEditEngine() ),
+ mnTotalHeight( 0 )
+{
+}
+
+void XclExpHFConverter::GenerateString(
+ const EditTextObject* pLeftObj,
+ const EditTextObject* pCenterObj,
+ const EditTextObject* pRightObj )
+{
+ maHFString.clear();
+ mnTotalHeight = 0;
+ AppendPortion( pLeftObj, 'L' );
+ AppendPortion( pCenterObj, 'C' );
+ AppendPortion( pRightObj, 'R' );
+}
+
+void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
+{
+ if( !pTextObj ) return;
+
+ OUString aText;
+ sal_Int32 nHeight = 0;
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *GetDoc().GetPool() );
+
+ // edit engine
+ bool bOldUpdateMode = mrEE.SetUpdateLayout( true );
+ mrEE.SetText( *pTextObj );
+
+ // font information
+ XclFontData aFontData, aNewData;
+ if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
+ {
+ aFontData = pFirstFont->GetFontData();
+ aFontData.mnHeight = (aFontData.mnHeight + 10) / 20; // using pt here, not twips
+ }
+ else
+ aFontData.mnHeight = 10;
+
+ const FontList* pFontList = nullptr;
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
+ pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
+ pFontList = pInfoItem->GetFontList();
+ }
+
+ sal_Int32 nParaCount = mrEE.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ ESelection aSel( nPara, 0 );
+ OUStringBuffer aParaText;
+ sal_Int32 nParaHeight = 0;
+ std::vector<sal_Int32> aPosList;
+ mrEE.GetPortions( nPara, aPosList );
+
+ for( const auto& rPos : aPosList )
+ {
+ aSel.nEndPos = rPos;
+ if( aSel.nStartPos < aSel.nEndPos )
+ {
+
+// --- font attributes ---
+
+ vcl::Font aFont;
+ model::ComplexColor aComplexColor;
+ aItemSet.ClearItem();
+ SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
+ ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
+ ScPatternAttr::fillFontOnly(aFont, aItemSet);
+ ScPatternAttr::fillColor(aComplexColor, aItemSet, ScAutoFontColorMode::Raw);
+
+ // font name and style
+ aNewData.maName = XclTools::GetXclFontName( aFont.GetFamilyName() );
+ aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
+ aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
+ bool bNewFont = (aFontData.maName != aNewData.maName);
+ bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
+ (aFontData.mbItalic != aNewData.mbItalic);
+ if( bNewFont || (bNewStyle && pFontList) )
+ {
+ aParaText.append("&\"" + aNewData.maName);
+ if( pFontList )
+ {
+ FontMetric aFontMetric( pFontList->Get(
+ aNewData.maName,
+ (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
+ aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
+ aNewData.maStyle = pFontList->GetStyleName( aFontMetric );
+ if( !aNewData.maStyle.isEmpty() )
+ aParaText.append("," + aNewData.maStyle);
+ }
+ aParaText.append("\"");
+ }
+
+ // height
+ // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
+ // -> get it directly from edit engine item set
+ aNewData.mnHeight = ulimit_cast< sal_uInt16 >( aEditSet.Get( EE_CHAR_FONTHEIGHT ).GetHeight() );
+ aNewData.mnHeight = (aNewData.mnHeight + 10) / 20;
+ bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
+ if( bFontHtChanged )
+ aParaText.append("&" + OUString::number(aNewData.mnHeight));
+ // update maximum paragraph height, convert to twips
+ nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
+
+ // underline
+ aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
+ switch( aFont.GetUnderline() )
+ {
+ case LINESTYLE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case LINESTYLE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
+ case LINESTYLE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+ if( aFontData.mnUnderline != aNewData.mnUnderline )
+ {
+ sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
+ aFontData.mnUnderline : aNewData.mnUnderline;
+ (nTmpUnderl == EXC_FONTUNDERL_SINGLE)? aParaText.append("&U") : aParaText.append("&E");
+ }
+
+ // font color
+ aNewData.maComplexColor = aComplexColor;
+ Color aNewColor = aNewData.maComplexColor.getFinalColor();
+
+ if (!aFontData.maComplexColor.getFinalColor().IsRGBEqual(aNewColor))
+ {
+ aParaText.append("&K" + aNewColor.AsRGBHexString());
+ }
+
+ // strikeout
+ aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
+ if( aFontData.mbStrikeout != aNewData.mbStrikeout )
+ aParaText.append("&S");
+
+ // super/sub script
+ const SvxEscapementItem& rEscapeItem = aEditSet.Get( EE_CHAR_ESCAPEMENT );
+ aNewData.SetScEscapement( rEscapeItem.GetEsc() );
+ if( aFontData.mnEscapem != aNewData.mnEscapem )
+ {
+ switch(aNewData.mnEscapem)
+ {
+ // close the previous super/sub script.
+ case EXC_FONTESC_NONE: (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? aParaText.append("&X") : aParaText.append("&Y"); break;
+ case EXC_FONTESC_SUPER: aParaText.append("&X"); break;
+ case EXC_FONTESC_SUB: aParaText.append("&Y"); break;
+ default: break;
+ }
+ }
+
+ aFontData = aNewData;
+
+// --- text content or text fields ---
+
+ const SvxFieldItem* pItem;
+ if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
+ (pItem = aEditSet.GetItemIfSet( EE_FEATURE_FIELD, false )) )
+ {
+ if( const SvxFieldData* pFieldData = pItem->GetField() )
+ {
+ if( dynamic_cast<const SvxPageField*>( pFieldData) != nullptr )
+ aParaText.append("&P");
+ else if( dynamic_cast<const SvxPagesField*>( pFieldData) != nullptr )
+ aParaText.append("&N");
+ else if( dynamic_cast<const SvxDateField*>( pFieldData) != nullptr )
+ aParaText.append("&D");
+ else if( dynamic_cast<const SvxTimeField*>( pFieldData) != nullptr || dynamic_cast<const SvxExtTimeField*>( pFieldData) != nullptr )
+ aParaText.append("&T");
+ else if( dynamic_cast<const SvxTableField*>( pFieldData) != nullptr )
+ aParaText.append("&A");
+ else if( dynamic_cast<const SvxFileField*>( pFieldData) != nullptr ) // title -> file name
+ aParaText.append("&F");
+ else if( const SvxExtFileField* pFileField = dynamic_cast<const SvxExtFileField*>( pFieldData ) )
+ {
+ switch( pFileField->GetFormat() )
+ {
+ case SvxFileFormat::NameAndExt:
+ case SvxFileFormat::NameOnly:
+ aParaText.append("&F");
+ break;
+ case SvxFileFormat::PathOnly:
+ aParaText.append("&Z");
+ break;
+ case SvxFileFormat::PathFull:
+ aParaText.append("&Z&F");
+ break;
+ default:
+ OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
+ }
+ }
+ }
+ }
+ else
+ {
+ OUString aPortionText( mrEE.GetText( aSel ) );
+ aPortionText = aPortionText.replaceAll( "&", "&&" );
+ // #i17440# space between font height and numbers in text
+ if( bFontHtChanged && aParaText.getLength() && !aPortionText.isEmpty() )
+ {
+ sal_Unicode cLast = aParaText[ aParaText.getLength() - 1 ];
+ sal_Unicode cFirst = aPortionText[0];
+ if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
+ aParaText.append(" ");
+ }
+ aParaText.append(aPortionText);
+ }
+ }
+
+ aSel.nStartPos = aSel.nEndPos;
+ }
+
+ aText = ScGlobal::addToken( aText, aParaText, '\n' );
+ aParaText.setLength(0);
+ if( nParaHeight == 0 )
+ nParaHeight = aFontData.mnHeight * 20; // points -> twips
+ nHeight += nParaHeight;
+ }
+
+ mrEE.SetUpdateLayout( bOldUpdateMode );
+
+ if( !aText.isEmpty() )
+ {
+ maHFString += "&" + OUStringChar(cPortionCode) + aText;
+ mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
+ }
+}
+
+// URL conversion =============================================================
+
+namespace {
+
+/** Encodes special parts of the path, i.e. directory separators and volume names.
+ @param pTableName Pointer to a table name to be encoded in this path, or 0. */
+OUString lclEncodeDosPath(
+ XclBiff eBiff, std::u16string_view path, bool bIsRel, const OUString* pTableName)
+{
+ OUStringBuffer aBuf;
+
+ if (!path.empty())
+ {
+ aBuf.append(EXC_URLSTART_ENCODED);
+
+ if ( path.length() > 2 && o3tl::starts_with(path, u"\\\\") )
+ {
+ // UNC
+ aBuf.append(OUStringChar(EXC_URL_DOSDRIVE) + "@");
+ path = path.substr(2);
+ }
+ else if ( path.length() > 2 && o3tl::starts_with(path.substr(1), u":\\") )
+ {
+ aBuf.append(OUStringChar(EXC_URL_DOSDRIVE) + OUStringChar(path[0]));
+ path = path.substr(3);
+ }
+ else if ( !bIsRel )
+ {
+ // URL probably points to a document on a Unix-like file system
+ aBuf.append(EXC_URL_DRIVEROOT);
+ }
+
+ // directories
+ auto nPos = std::u16string_view::npos;
+ while((nPos = path.find('\\')) != std::u16string_view::npos)
+ {
+ if ( o3tl::starts_with(path, u"..") )
+ // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
+ // Excel seems confused by this token).
+ aBuf.append(EXC_URL_PARENTDIR);
+ else
+ aBuf.append(path.substr(0,nPos) + OUStringChar(EXC_URL_SUBDIR));
+
+ path = path.substr(nPos + 1);
+ }
+
+ // file name
+ if (pTableName) // enclose file name in brackets if table name follows
+ aBuf.append(OUString::Concat("[") + path + "]");
+ else
+ aBuf.append(path);
+ }
+ else // empty URL -> self reference
+ {
+ switch( eBiff )
+ {
+ case EXC_BIFF5:
+ aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
+ break;
+ case EXC_BIFF8:
+ DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
+ aBuf.append(EXC_URLSTART_SELF);
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+
+ // table name
+ if (pTableName)
+ aBuf.append(*pTableName);
+
+ // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
+ // It's better to truncate, than generate invalid file that Excel cannot open.
+ if (aBuf.getLength() > 255)
+ aBuf.setLength(255);
+
+ return aBuf.makeStringAndClear();
+}
+
+bool isUrlRelative(const OUString& aUrl)
+{
+ css::uno::Reference<css::uri::XUriReferenceFactory> xUriFactory(
+ css::uri::UriReferenceFactory::create(
+ comphelper::getProcessComponentContext()));
+ css::uno::Reference<css::uri::XUriReference> xUri(xUriFactory->parse(aUrl));
+
+ return !xUri->isAbsolute();
+}
+
+} // namespace
+
+OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, std::u16string_view rAbsUrl, const OUString* pTableName )
+{
+ OUString aDosPath;
+ bool bIsRel = false;
+
+ if (rRoot.IsRelUrl())
+ {
+ OUString aUrlPath = INetURLObject::GetRelURL(
+ rRoot.GetBasePath(), OUString(rAbsUrl),
+ INetURLObject::EncodeMechanism::All,
+ INetURLObject::DecodeMechanism::NONE,
+ RTL_TEXTENCODING_UTF8, FSysStyle::Detect
+ );
+
+ if (isUrlRelative(aUrlPath))
+ {
+ bIsRel = true;
+ osl::FileBase::getSystemPathFromFileURL(aUrlPath, aDosPath);
+ aDosPath = aDosPath.replaceAll(u"/", u"\\");
+ }
+ }
+
+ if (!bIsRel)
+ aDosPath = INetURLObject(rAbsUrl).getFSysPath(FSysStyle::Dos);
+
+ return lclEncodeDosPath(rRoot.GetBiff(), aDosPath, bIsRel, pTableName);
+}
+
+OUString XclExpUrlHelper::EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic )
+{
+ return rApplic + OUStringChar(EXC_DDE_DELIM) + rTopic;
+}
+
+// Cached Value Lists =========================================================
+
+XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
+ : mrMatrix( rMatrix )
+{
+ mrMatrix.IncRef();
+}
+XclExpCachedMatrix::~XclExpCachedMatrix()
+{
+ mrMatrix.DecRef();
+}
+
+void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
+{
+ mrMatrix.GetDimensions( nCols, nRows );
+
+ OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
+ OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
+}
+
+std::size_t XclExpCachedMatrix::GetSize() const
+{
+ SCSIZE nCols, nRows;
+
+ GetDimensions( nCols, nRows );
+
+ /* The returned size may be wrong if the matrix contains strings. The only
+ effect is that the export stream has to update a wrong record size which is
+ faster than to iterate through all cached values and calculate their sizes. */
+ return 3 + 9 * (nCols * nRows);
+}
+
+void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
+{
+ SCSIZE nCols, nRows;
+
+ GetDimensions( nCols, nRows );
+
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ // in BIFF2-BIFF7: 256 columns represented by 0 columns
+ rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
+ else
+ // in BIFF8: columns and rows decreased by 1
+ rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
+
+ for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
+ {
+ ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
+
+ FormulaError nScError;
+ if( ScMatValType::Empty == nMatVal.nType )
+ {
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_EMPTY;
+ rStrm.WriteZeroBytes( 8 );
+ }
+ else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
+ {
+ XclExpString aStr( nMatVal.GetString().getString(), XclStrFlags::NONE );
+ rStrm.SetSliceSize( 6 );
+ rStrm << EXC_CACHEDVAL_STRING << aStr;
+ }
+ else if( ScMatValType::Boolean == nMatVal.nType )
+ {
+ sal_Int8 nBool = sal_Int8(nMatVal.GetBoolean());
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_BOOL << nBool;
+ rStrm.WriteZeroBytes( 7 );
+ }
+ else if( (nScError = nMatVal.GetError()) != FormulaError::NONE )
+ {
+ sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_ERROR << nError;
+ rStrm.WriteZeroBytes( 7 );
+ }
+ else
+ {
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xelink.cxx b/sc/source/filter/excel/xelink.cxx
new file mode 100644
index 0000000000..528286ea49
--- /dev/null
+++ b/sc/source/filter/excel/xelink.cxx
@@ -0,0 +1,2661 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xelink.hxx>
+
+#include <algorithm>
+#include <formula/errorcodes.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sal/log.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <externalrefmgr.hxx>
+#include <tokenarray.hxx>
+#include <xecontent.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+
+#include <vector>
+#include <memory>
+#include <string_view>
+
+using ::std::unique_ptr;
+using ::std::vector;
+using ::com::sun::star::uno::Any;
+
+using namespace oox;
+
+// *** Helper classes ***
+
+// External names =============================================================
+
+namespace {
+
+/** This is a base class for any external name (i.e. add-in names or DDE links).
+ @descr Derived classes implement creation and export of the external names. */
+class XclExpExtNameBase : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** @param nFlags The flags to export. */
+ explicit XclExpExtNameBase( const XclExpRoot& rRoot,
+ const OUString& rName, sal_uInt16 nFlags = 0 );
+
+ /** Returns the name string of the external name. */
+ const OUString& GetName() const { return maName; }
+
+private:
+ /** Writes the start of the record that is equal in all EXTERNNAME records and calls WriteAddData(). */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Called to write additional data following the common record contents.
+ @descr Derived classes should overwrite this function to write their data. */
+ virtual void WriteAddData( XclExpStream& rStrm );
+
+protected:
+ OUString maName; /// Calc name (title) of the external name.
+ XclExpStringRef mxName; /// Excel name (title) of the external name.
+ sal_uInt16 mnFlags; /// Flags for record export.
+};
+
+/** Represents an EXTERNNAME record for an add-in function name. */
+class XclExpExtNameAddIn : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName );
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+};
+
+/** Represents an EXTERNNAME record for a DDE link. */
+class XclExpExtNameDde : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtNameDde( const XclExpRoot& rRoot, const OUString& rName,
+ sal_uInt16 nFlags, const ScMatrix* pResults = nullptr );
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpCachedMatrix > XclExpCachedMatRef;
+ XclExpCachedMatRef mxMatrix; /// Cached results of the DDE link.
+};
+
+class XclExpSupbook;
+
+class XclExpExtName : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook, const OUString& rName,
+ const ScExternalRefCache::TokenArrayRef& rArray );
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpSupbook& mrSupbook;
+ unique_ptr<ScTokenArray> mpArray;
+};
+
+// List of external names =====================================================
+
+/** List of all external names of a sheet. */
+class XclExpExtNameBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtNameBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts an add-in function name
+ @return The 1-based (Excel-like) list index of the name. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+ /** InsertEuroTool */
+ sal_uInt16 InsertEuroTool( const OUString& rName );
+ /** Inserts a DDE link.
+ @return The 1-based (Excel-like) list index of the DDE link. */
+ sal_uInt16 InsertDde( std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem );
+
+ sal_uInt16 InsertExtName( const XclExpSupbook& rSupbook, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Writes the EXTERNNAME record list. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+private:
+ /** Returns the 1-based (Excel-like) list index of the external name or 0, if not found. */
+ sal_uInt16 GetIndex( std::u16string_view rName ) const;
+ /** Appends the passed newly crested external name.
+ @return The 1-based (Excel-like) list index of the appended name. */
+ sal_uInt16 AppendNew( XclExpExtNameBase* pExtName );
+
+ XclExpRecordList< XclExpExtNameBase > maNameList; /// The list with all EXTERNNAME records.
+};
+
+// Cached external cells ======================================================
+
+/** Stores the contents of a consecutive row of external cells (record CRN). */
+class XclExpCrn : public XclExpRecord
+{
+public:
+ explicit XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+
+ /** Returns true, if the passed value could be appended to this record. */
+ bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+
+ /** Writes the row and child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+ static void WriteBool( XclExpStream& rStrm, bool bValue );
+ static void WriteDouble( XclExpStream& rStrm, double fValue );
+ static void WriteString( XclExpStream& rStrm, const OUString& rValue );
+ static void WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode );
+ static void WriteEmpty( XclExpStream& rStrm );
+
+private:
+ typedef ::std::vector< Any > CachedValues;
+
+ CachedValues maValues; /// All cached values.
+ SCCOL mnScCol; /// Column index of the first external cell.
+ SCROW mnScRow; /// Row index of the external cells.
+};
+
+class XclExpCrnList;
+
+/** Represents the record XCT which is the header record of a CRN record list.
+ */
+class XclExpXct : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXct( const XclExpRoot& rRoot,
+ const OUString& rTabName, sal_uInt16 nSBTab,
+ ScExternalRefCache::TableTypeRef xCacheTable );
+
+ /** Returns the external sheet name. */
+ const XclExpString& GetTabName() const { return maTabName; }
+
+ /** Stores all cells in the given range in the CRN list. */
+ void StoreCellRange( const ScRange& rRange );
+
+ void StoreCell_( const ScAddress& rCell );
+ void StoreCellRange_( const ScRange& rRange );
+
+ /** Writes the XCT and all CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the sheetDataSet and child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScExternalRefCache::TableTypeRef mxCacheTable;
+ ScMarkData maUsedCells; /// Contains addresses of all stored cells.
+ ScRange maBoundRange; /// Bounding box of maUsedCells.
+ XclExpString maTabName; /// Sheet name of the external sheet.
+ sal_uInt16 mnSBTab; /// Referred sheet index in SUPBOOK record.
+
+ /** Build the internal representation of records to be saved as BIFF or OOXML. */
+ bool BuildCrnList( XclExpCrnList& rCrnRecs );
+};
+
+// External documents (EXTERNSHEET/SUPBOOK), base class =======================
+
+/** Base class for records representing external sheets/documents.
+
+ In BIFF5/BIFF7, this record is the EXTERNSHEET record containing one sheet
+ of the own or an external document. In BIFF8, this record is the SUPBOOK
+ record representing the entire own or external document with all referenced
+ sheets.
+ */
+class XclExpExternSheetBase : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpExternSheetBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, sal_uInt32 nRecSize = 0 );
+
+protected:
+ /** Creates and returns the list of EXTERNNAME records. */
+ XclExpExtNameBuffer& GetExtNameBuffer();
+ /** Writes the list of EXTERNNAME records. */
+ void WriteExtNameBuffer( XclExpStream& rStrm );
+ /** Writes the list of externalName elements. */
+ void WriteExtNameBufferXml( XclExpXmlStream& rStrm );
+
+protected:
+ typedef std::shared_ptr< XclExpExtNameBuffer > XclExpExtNameBfrRef;
+ XclExpExtNameBfrRef mxExtNameBfr; /// List of EXTERNNAME records.
+};
+
+// External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
+
+/** Represents an EXTERNSHEET record containing the URL and sheet name of a sheet.
+ @descr This class is used up to BIFF7 only, writing a BIFF8 EXTERNSHEET
+ record is implemented directly in the link manager. */
+class XclExpExternSheet : public XclExpExternSheetBase
+{
+public:
+ /** Creates an EXTERNSHEET record containing a special code (i.e. own document or sheet). */
+ explicit XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode );
+ /** Creates an EXTERNSHEET record referring to an internal sheet. */
+ explicit XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName );
+
+ /** Finds or inserts an EXTERNNAME record for add-ins.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+
+ /** Writes the EXTERNSHEET and all EXTERNNAME, XCT and CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Initializes the record data with the passed encoded URL. */
+ void Init( std::u16string_view rEncUrl );
+ /** Writes the contents of the EXTERNSHEET record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maTabName; /// The name of the sheet.
+};
+
+// External documents (SUPBOOK, BIFF8) ========================================
+
+/** The SUPBOOK record contains data for an external document (URL, sheet names, external values). */
+class XclExpSupbook : public XclExpExternSheetBase
+{
+public:
+ /** Creates a SUPBOOK record for internal references. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount );
+ /** Creates a SUPBOOK record for add-in functions. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot );
+ /** EUROTOOL SUPBOOK */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType );
+ /** Creates a SUPBOOK record for an external document. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl );
+ /** Creates a SUPBOOK record for a DDE link. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic );
+
+ /** Returns true, if this SUPBOOK contains the passed URL of an external document. */
+ bool IsUrlLink( std::u16string_view rUrl ) const;
+ /** Returns true, if this SUPBOOK contains the passed DDE link. */
+ bool IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const;
+ /** Fills the passed reference log entry with the URL and sheet names. */
+ void FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
+ sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const;
+
+ /** Stores all cells in the given range in the CRN list of the specified SUPBOOK sheet. */
+ void StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab );
+
+ void StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell );
+ void StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange );
+
+ sal_uInt16 GetTabIndex( const OUString& rTabName ) const;
+ sal_uInt16 GetTabCount() const;
+
+ /** Inserts a new sheet name into the SUPBOOK and returns the SUPBOOK internal sheet index. */
+ sal_uInt16 InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable );
+ /** Finds or inserts an EXTERNNAME record for add-ins.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+ /** InsertEuroTool */
+ sal_uInt16 InsertEuroTool( const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertDde( const OUString& rItem );
+
+ sal_uInt16 InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Get the type of record. */
+ XclSupbookType GetType() const;
+
+ /** For references to an external document, 1-based OOXML file ID. */
+ sal_uInt16 GetFileId() const;
+
+ /** For references to an external document. */
+ const OUString& GetUrl() const;
+
+ /** Writes the SUPBOOK and all EXTERNNAME, XCT and CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the externalBook and all child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Returns the sheet name inside of this SUPBOOK. */
+ const XclExpString* GetTabName( sal_uInt16 nSBTab ) const;
+
+ /** Writes the SUPBOOK record contents. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpXct > XclExpXctList;
+ typedef XclExpXctList::RecordRefType XclExpXctRef;
+
+ XclExpXctList maXctList; /// List of XCT records (which contain CRN records).
+ OUString maUrl; /// URL of the external document or application name for DDE.
+ OUString maDdeTopic; /// Topic of a DDE link.
+ XclExpString maUrlEncoded; /// Document name encoded for Excel.
+ XclSupbookType meType; /// Type of this SUPBOOK record.
+ sal_uInt16 mnXclTabCount; /// Number of internal sheets.
+ sal_uInt16 mnFileId; /// 1-based external reference file ID for OOXML
+};
+
+// All SUPBOOKS in a document =================================================
+
+/** This struct contains a sheet index range for 3D references.
+ @descr This reference consists of an index to a SUPBOOK record and indexes
+ to SUPBOOK sheet names. */
+struct XclExpXti
+{
+ sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
+ sal_uInt16 mnFirstSBTab; /// Index to the first sheet of the range in the SUPBOOK.
+ sal_uInt16 mnLastSBTab; /// Index to the last sheet of the range in the SUPBOOK.
+
+ explicit XclExpXti() : mnSupbook( 0 ), mnFirstSBTab( 0 ), mnLastSBTab( 0 ) {}
+ explicit XclExpXti( sal_uInt16 nSupbook, sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) :
+ mnSupbook( nSupbook ), mnFirstSBTab( nFirstSBTab ), mnLastSBTab( nLastSBTab ) {}
+
+ /** Writes this XTI structure (inside of the EXTERNSHEET record). */
+ void Save( XclExpStream& rStrm ) const
+ { rStrm << mnSupbook << mnFirstSBTab << mnLastSBTab; }
+};
+
+bool operator==( const XclExpXti& rLeft, const XclExpXti& rRight )
+{
+ return
+ (rLeft.mnSupbook == rRight.mnSupbook) &&
+ (rLeft.mnFirstSBTab == rRight.mnFirstSBTab) &&
+ (rLeft.mnLastSBTab == rRight.mnLastSBTab);
+}
+
+/** Contains a list of all SUPBOOK records and index arrays of external sheets. */
+class XclExpSupbookBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpSupbookBuffer( const XclExpRoot& rRoot );
+
+ /** Finds SUPBOOK index and SUPBOOK sheet range from given Excel sheet range.
+ @return An XTI structure containing SUPBOOK and sheet indexes. */
+ XclExpXti GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
+ XclExpRefLogEntry* pRefLogEntry = nullptr ) const;
+
+ /** Stores all cells in the given range in a CRN record list. */
+ void StoreCellRange( const ScRange& rRange );
+
+ void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell );
+ void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange );
+
+ /** Finds or inserts an EXTERNNAME record for an add-in function name.
+ @param rnSupbook Returns the index of the SUPBOOK record which contains the add-in function name.
+ @param rnExtName Returns the 1-based EXTERNNAME record index. */
+ bool InsertAddIn(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** InsertEuroTool */
+ bool InsertEuroTool(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @param rnSupbook Returns the index of the SUPBOOK record which contains the DDE link.
+ @param rnExtName Returns the 1-based EXTERNNAME record index. */
+ bool InsertDde(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem );
+
+ bool InsertExtName(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ XclExpXti GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ XclExpRefLogEntry* pRefLogEntry );
+
+ /** Writes all SUPBOOK records with their sub records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes all externalBook elements with their child elements to OOXML. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ /** Whether we need to write externalReferences or not. */
+ bool HasExternalReferences() const;
+
+ struct XclExpSBIndex
+ {
+ sal_uInt16 mnSupbook; /// SUPBOOK index for an Excel sheet.
+ sal_uInt16 mnSBTab; /// Sheet name index in SUPBOOK for an Excel sheet.
+ void Set( sal_uInt16 nSupbook, sal_uInt16 nSBTab )
+ { mnSupbook = nSupbook; mnSBTab = nSBTab; }
+ };
+
+private:
+ typedef XclExpRecordList< XclExpSupbook > XclExpSupbookList;
+ typedef XclExpSupbookList::RecordRefType XclExpSupbookRef;
+
+private:
+ /** Searches for the SUPBOOK record containing the passed document URL.
+ @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
+ @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists.
+ @return True, if the SUPBOOK record exists (out-parameters are valid). */
+ bool GetSupbookUrl( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex,
+ std::u16string_view rUrl ) const;
+ /** Searches for the SUPBOOK record containing the passed DDE link.
+ @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
+ @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists.
+ @return True, if the SUPBOOK record exists (out-parameters are valid). */
+ bool GetSupbookDde( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex,
+ std::u16string_view rApplic, std::u16string_view rTopic ) const;
+
+ /** Appends a new SUPBOOK to the list.
+ @return The list index of the SUPBOOK record. */
+ sal_uInt16 Append( XclExpSupbookRef const & xSupbook );
+
+private:
+ XclExpSupbookList maSupbookList; /// List of all SUPBOOK records.
+ std::vector< XclExpSBIndex >
+ maSBIndexVec; /// SUPBOOK and sheet name index for each Excel sheet.
+ sal_uInt16 mnOwnDocSB; /// Index to SUPBOOK for own document.
+ sal_uInt16 mnAddInSB; /// Index to add-in SUPBOOK.
+};
+
+}
+
+// Export link manager ========================================================
+
+/** Abstract base class for implementation classes of the link manager. */
+class XclExpLinkManagerImpl : protected XclExpRoot
+{
+public:
+ /** Derived classes search for an EXTSHEET structure for the given Calc sheet range. */
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) = 0;
+ /** Derived classes search for a special EXTERNSHEET index for the own document. */
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) = 0;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) = 0;
+
+ /** Derived classes store all cells in the given range in a CRN record list. */
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) = 0;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) = 0;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) = 0;
+
+ /** Derived classes find or insert an EXTERNNAME record for an add-in function name. */
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) = 0;
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) = 0;
+
+ /** Derived classes find or insert an EXTERNNAME record for DDE links. */
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) = 0;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) = 0;
+
+ /** Derived classes write the entire link table to the passed stream. */
+ virtual void Save( XclExpStream& rStrm ) = 0;
+
+ /** Derived classes write the entire link table to the passed OOXML stream. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) = 0;
+
+protected:
+ explicit XclExpLinkManagerImpl( const XclExpRoot& rRoot );
+};
+
+namespace {
+
+/** Implementation of the link manager for BIFF5/BIFF7. */
+class XclExpLinkManagerImpl5 : public XclExpLinkManagerImpl
+{
+public:
+ explicit XclExpLinkManagerImpl5( const XclExpRoot& rRoot );
+
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
+
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) override;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpExternSheet > XclExpExtSheetList;
+ typedef XclExpExtSheetList::RecordRefType XclExpExtSheetRef;
+ typedef ::std::map< SCTAB, sal_uInt16 > XclExpIntTabMap;
+ typedef ::std::map< sal_Unicode, sal_uInt16 > XclExpCodeMap;
+
+private:
+ /** Returns the number of EXTERNSHEET records. */
+ sal_uInt16 GetExtSheetCount() const;
+
+ /** Appends an internal EXTERNSHEET record and returns the one-based index. */
+ sal_uInt16 AppendInternal( XclExpExtSheetRef const & xExtSheet );
+ /** Creates all EXTERNSHEET records for internal sheets on first call. */
+ void CreateInternal();
+
+ /** Returns the specified internal EXTERNSHEET record. */
+ XclExpExtSheetRef GetInternal( sal_uInt16 nExtSheet );
+ /** Returns the EXTERNSHEET index of an internal Calc sheet, or a deleted reference. */
+ XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab );
+ /** Finds or creates the EXTERNSHEET index of an internal special EXTERNSHEET. */
+ XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_Unicode cCode );
+
+private:
+ XclExpExtSheetList maExtSheetList; /// List with EXTERNSHEET records.
+ XclExpIntTabMap maIntTabMap; /// Maps internal Calc sheets to EXTERNSHEET records.
+ XclExpCodeMap maCodeMap; /// Maps special external codes to EXTERNSHEET records.
+};
+
+/** Implementation of the link manager for BIFF8 and OOXML. */
+class XclExpLinkManagerImpl8 : public XclExpLinkManagerImpl
+{
+public:
+ explicit XclExpLinkManagerImpl8( const XclExpRoot& rRoot );
+
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
+
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) override;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Searches for or inserts a new XTI structure.
+ @return The 0-based list index of the XTI structure. */
+ sal_uInt16 InsertXti( const XclExpXti& rXti );
+
+private:
+
+ XclExpSupbookBuffer maSBBuffer; /// List of all SUPBOOK records.
+ std::vector< XclExpXti > maXtiVec; /// List of XTI structures for the EXTERNSHEET record.
+};
+
+}
+
+// *** Implementation ***
+
+// Excel sheet indexes ========================================================
+
+
+XclExpTabInfo::XclExpTabInfo( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnScCnt( 0 ),
+ mnXclCnt( 0 ),
+ mnXclExtCnt( 0 ),
+ mnXclSelCnt( 0 ),
+ mnDisplXclTab( 0 ),
+ mnFirstVisXclTab( 0 )
+{
+ ScDocument& rDoc = GetDoc();
+ ScExtDocOptions& rDocOpt = GetExtDocOptions();
+
+ mnScCnt = rDoc.GetTableCount();
+
+ SCTAB nScTab;
+ SCTAB nFirstVisScTab = SCTAB_INVALID; // first visible sheet
+ SCTAB nFirstExpScTab = SCTAB_INVALID; // first exported sheet
+
+ // --- initialize the flags in the index buffer ---
+
+ maTabInfoVec.resize( mnScCnt );
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ // ignored sheets (skipped by export, with invalid Excel sheet index)
+ if( rDoc.IsScenario( nScTab ) )
+ {
+ SetFlag( nScTab, ExcTabBufFlags::Ignore );
+ }
+
+ // external sheets (skipped, but with valid Excel sheet index for ref's)
+ else if( rDoc.GetLinkMode( nScTab ) == ScLinkMode::VALUE )
+ {
+ SetFlag( nScTab, ExcTabBufFlags::Extern );
+ }
+
+ // exported sheets
+ else
+ {
+ // sheet name
+ rDoc.GetName( nScTab, maTabInfoVec[ nScTab ].maScName );
+
+ // remember first exported sheet
+ if( nFirstExpScTab == SCTAB_INVALID )
+ nFirstExpScTab = nScTab;
+ // remember first visible exported sheet
+ if( (nFirstVisScTab == SCTAB_INVALID) && rDoc.IsVisible( nScTab ) )
+ nFirstVisScTab = nScTab;
+
+ // sheet visible (only exported sheets)
+ SetFlag( nScTab, ExcTabBufFlags::Visible, rDoc.IsVisible( nScTab ) );
+
+ // sheet selected (only exported sheets)
+ if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nScTab ) )
+ SetFlag( nScTab, ExcTabBufFlags::Selected, pTabSett->mbSelected );
+
+ // sheet mirrored (only exported sheets)
+ SetFlag( nScTab, ExcTabBufFlags::Mirrored, rDoc.IsLayoutRTL( nScTab ) );
+ }
+ }
+
+ // --- visible/selected sheets ---
+
+ SCTAB nDisplScTab = rDocOpt.GetDocSettings().mnDisplTab;
+
+ // missing viewdata at embedded XLSX OLE objects
+ if (nDisplScTab == -1 )
+ nDisplScTab = rDoc.GetVisibleTab();
+
+ // find first visible exported sheet
+ if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
+ {
+ // no exportable visible sheet -> use first exportable sheet
+ nFirstVisScTab = nFirstExpScTab;
+ if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
+ {
+ // no exportable sheet at all -> use active sheet and export it
+ nFirstVisScTab = nDisplScTab;
+ SetFlag( nFirstVisScTab, ExcTabBufFlags::SkipMask, false ); // clear skip flags
+ }
+ SetFlag( nFirstVisScTab, ExcTabBufFlags::Visible ); // must be visible, even if originally hidden
+ }
+
+ // find currently displayed sheet
+ if( !IsExportTab( nDisplScTab ) ) // selected sheet not exported (i.e. scenario) -> use first visible
+ nDisplScTab = nFirstVisScTab;
+ SetFlag( nDisplScTab, ExcTabBufFlags::Visible | ExcTabBufFlags::Selected );
+
+ // number of selected sheets
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ if( IsSelectedTab( nScTab ) )
+ ++mnXclSelCnt;
+
+ // --- calculate resulting Excel sheet indexes ---
+
+ CalcXclIndexes();
+ mnFirstVisXclTab = GetXclTab( nFirstVisScTab );
+ mnDisplXclTab = GetXclTab( nDisplScTab );
+
+ // --- sorted vectors for index lookup ---
+
+ CalcSortedIndexes();
+}
+
+bool XclExpTabInfo::IsExportTab( SCTAB nScTab ) const
+{
+ /* Check sheet index before to avoid assertion in GetFlag(). */
+ return (nScTab < mnScCnt && nScTab >= 0) && !GetFlag( nScTab, ExcTabBufFlags::SkipMask );
+}
+
+bool XclExpTabInfo::IsExternalTab( SCTAB nScTab ) const
+{
+ /* Check sheet index before to avoid assertion (called from formula
+ compiler also for deleted references). */
+ return (nScTab < mnScCnt && nScTab >= 0) && GetFlag( nScTab, ExcTabBufFlags::Extern );
+}
+
+bool XclExpTabInfo::IsVisibleTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Visible );
+}
+
+bool XclExpTabInfo::IsSelectedTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Selected );
+}
+
+bool XclExpTabInfo::IsDisplayedTab( SCTAB nScTab ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
+ return GetXclTab( nScTab ) == mnDisplXclTab;
+}
+
+bool XclExpTabInfo::IsMirroredTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Mirrored );
+}
+
+OUString XclExpTabInfo::GetScTabName( SCTAB nScTab ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
+ return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].maScName : OUString();
+}
+
+sal_uInt16 XclExpTabInfo::GetXclTab( SCTAB nScTab ) const
+{
+ return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].mnXclTab : EXC_TAB_DELETED;
+}
+
+SCTAB XclExpTabInfo::GetRealScTab( SCTAB nSortedScTab ) const
+{
+ OSL_ENSURE( nSortedScTab < mnScCnt && nSortedScTab >= 0, "XclExpTabInfo::GetRealScTab - sheet out of range" );
+ return (nSortedScTab < mnScCnt && nSortedScTab >= 0) ? maFromSortedVec[ nSortedScTab ] : SCTAB_INVALID;
+}
+
+bool XclExpTabInfo::GetFlag( SCTAB nScTab, ExcTabBufFlags nFlags ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::GetFlag - sheet out of range" );
+ return (nScTab < mnScCnt && nScTab >= 0) && (maTabInfoVec[ nScTab ].mnFlags & nFlags);
+}
+
+void XclExpTabInfo::SetFlag( SCTAB nScTab, ExcTabBufFlags nFlags, bool bSet )
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::SetFlag - sheet out of range" );
+ if( nScTab < mnScCnt && nScTab >= 0 )
+ {
+ if (bSet)
+ maTabInfoVec[ nScTab ].mnFlags |= nFlags;
+ else
+ maTabInfoVec[ nScTab ].mnFlags &= ~nFlags;
+ }
+}
+
+void XclExpTabInfo::CalcXclIndexes()
+{
+ sal_uInt16 nXclTab = 0;
+ SCTAB nScTab = 0;
+
+ // --- pass 1: process regular sheets ---
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ if( IsExportTab( nScTab ) )
+ {
+ maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
+ ++nXclTab;
+ }
+ else
+ maTabInfoVec[ nScTab ].mnXclTab = EXC_TAB_DELETED;
+ }
+ mnXclCnt = nXclTab;
+
+ // --- pass 2: process external sheets (nXclTab continues) ---
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ if( IsExternalTab( nScTab ) )
+ {
+ maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
+ ++nXclTab;
+ ++mnXclExtCnt;
+ }
+ }
+
+ // result: first occur all exported sheets, followed by all external sheets
+}
+
+typedef ::std::pair< OUString, SCTAB > XclExpTabName;
+
+namespace {
+
+struct XclExpTabNameSort {
+ bool operator ()( const XclExpTabName& rArg1, const XclExpTabName& rArg2 )
+ {
+ // compare the sheet names only
+ return ScGlobal::GetCollator().compareString( rArg1.first, rArg2.first ) < 0;
+ }
+};
+
+}
+
+void XclExpTabInfo::CalcSortedIndexes()
+{
+ ScDocument& rDoc = GetDoc();
+ ::std::vector< XclExpTabName > aVec( mnScCnt );
+ SCTAB nScTab;
+
+ // fill with sheet names
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ rDoc.GetName( nScTab, aVec[ nScTab ].first );
+ aVec[ nScTab ].second = nScTab;
+ }
+ ::std::sort( aVec.begin(), aVec.end(), XclExpTabNameSort() );
+
+ // fill index vectors from sorted sheet name vector
+ maFromSortedVec.resize( mnScCnt );
+ maToSortedVec.resize( mnScCnt );
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ maFromSortedVec[ nScTab ] = aVec[ nScTab ].second;
+ maToSortedVec[ aVec[ nScTab ].second ] = nScTab;
+ }
+}
+
+// External names =============================================================
+
+XclExpExtNameBase::XclExpExtNameBase(
+ const XclExpRoot& rRoot, const OUString& rName, sal_uInt16 nFlags ) :
+ XclExpRecord( EXC_ID_EXTERNNAME ),
+ XclExpRoot( rRoot ),
+ maName( rName ),
+ mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
+ mnFlags( nFlags )
+{
+ OSL_ENSURE( maName.getLength() <= 255, "XclExpExtNameBase::XclExpExtNameBase - string too long" );
+ SetRecSize( 6 + mxName->GetSize() );
+}
+
+void XclExpExtNameBase::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnFlags
+ << sal_uInt32( 0 )
+ << *mxName;
+ WriteAddData( rStrm );
+}
+
+void XclExpExtNameBase::WriteAddData( XclExpStream& /*rStrm*/ )
+{
+}
+
+XclExpExtNameAddIn::XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName ) :
+ XclExpExtNameBase( rRoot, rName )
+{
+ AddRecSize( 4 );
+}
+
+void XclExpExtNameAddIn::WriteAddData( XclExpStream& rStrm )
+{
+ // write a #REF! error formula
+ rStrm << sal_uInt16( 2 ) << EXC_TOKID_ERR << EXC_ERR_REF;
+}
+
+XclExpExtNameDde::XclExpExtNameDde( const XclExpRoot& rRoot,
+ const OUString& rName, sal_uInt16 nFlags, const ScMatrix* pResults ) :
+ XclExpExtNameBase( rRoot, rName, nFlags )
+{
+ if( pResults )
+ {
+ mxMatrix = std::make_shared<XclExpCachedMatrix>( *pResults );
+ AddRecSize( mxMatrix->GetSize() );
+ }
+}
+
+void XclExpExtNameDde::WriteAddData( XclExpStream& rStrm )
+{
+ if( mxMatrix )
+ mxMatrix->Save( rStrm );
+}
+
+XclExpExtName::XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) :
+ XclExpExtNameBase( rRoot, rName ),
+ mrSupbook(rSupbook),
+ mpArray(rArray->Clone())
+{
+}
+
+void XclExpExtName::WriteAddData( XclExpStream& rStrm )
+{
+ // Write only if it only has a single token that is either a cell or cell
+ // range address. Excel just writes '02 00 1C 17' for all the other types
+ // of external names.
+
+ using namespace ::formula;
+ do
+ {
+ if (mpArray->GetLen() != 1)
+ break;
+
+ const formula::FormulaToken* p = mpArray->FirstToken();
+ if (!p->IsExternalRef())
+ break;
+
+ switch (p->GetType())
+ {
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsTabRel())
+ break;
+
+ bool bColRel = rRef.IsColRel();
+ bool bRowRel = rRef.IsRowRel();
+ sal_uInt16 nCol = static_cast<sal_uInt16>(rRef.Col());
+ sal_uInt16 nRow = static_cast<sal_uInt16>(rRef.Row());
+ if (bColRel) nCol |= 0x4000;
+ if (bRowRel) nCol |= 0x8000;
+
+ OUString aTabName = p->GetString().getString();
+ sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
+
+ // size is always 9
+ rStrm << static_cast<sal_uInt16>(9);
+ // operator token (3A for cell reference)
+ rStrm << static_cast<sal_uInt8>(0x3A);
+ // cell address (Excel's address has 2 sheet IDs.)
+ rStrm << nSBTab << nSBTab << nRow << nCol;
+ return;
+ }
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ const ScSingleRefData& r1 = rRef.Ref1;
+ const ScSingleRefData& r2 = rRef.Ref2;
+ if (r1.IsTabRel() || r2.IsTabRel())
+ break;
+
+ sal_uInt16 nTab1 = r1.Tab();
+ sal_uInt16 nTab2 = r2.Tab();
+ bool bCol1Rel = r1.IsColRel();
+ bool bRow1Rel = r1.IsRowRel();
+ bool bCol2Rel = r2.IsColRel();
+ bool bRow2Rel = r2.IsRowRel();
+
+ sal_uInt16 nCol1 = static_cast<sal_uInt16>(r1.Col());
+ sal_uInt16 nCol2 = static_cast<sal_uInt16>(r2.Col());
+ sal_uInt16 nRow1 = static_cast<sal_uInt16>(r1.Row());
+ sal_uInt16 nRow2 = static_cast<sal_uInt16>(r2.Row());
+ if (bCol1Rel) nCol1 |= 0x4000;
+ if (bRow1Rel) nCol1 |= 0x8000;
+ if (bCol2Rel) nCol2 |= 0x4000;
+ if (bRow2Rel) nCol2 |= 0x8000;
+
+ OUString aTabName = p->GetString().getString();
+ sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
+
+ // size is always 13 (0x0D)
+ rStrm << static_cast<sal_uInt16>(13);
+ // operator token (3B for area reference)
+ rStrm << static_cast<sal_uInt8>(0x3B);
+ // range (area) address
+ sal_uInt16 nSBTab2 = nSBTab + nTab2 - nTab1;
+ rStrm << nSBTab << nSBTab2 << nRow1 << nRow2 << nCol1 << nCol2;
+ return;
+ }
+ default:
+ ; // nothing
+ }
+ }
+ while (false);
+
+ // special value for #REF! (02 00 1C 17)
+ rStrm << static_cast<sal_uInt16>(2) << EXC_TOKID_ERR << EXC_ERR_REF;
+}
+
+void XclExpExtName::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
+
+ /* TODO: mpArray contains external references. It doesn't cause any problems, but it's enough
+ to export it without the external document identifier. */
+ if (mpArray->GetLen())
+ {
+ const OUString aFormula = XclXmlUtils::ToOUString(GetCompileFormulaContext(), ScAddress(0, 0, 0), mpArray.get());
+ pExternalLink->startElement(XML_definedName,
+ XML_name, maName.toUtf8(),
+ XML_refersTo, aFormula.toUtf8(),
+ XML_sheetId, nullptr);
+ }
+ else
+ {
+ pExternalLink->startElement(XML_definedName,
+ XML_name, maName.toUtf8(),
+ XML_refersTo, nullptr,
+ XML_sheetId, nullptr);
+ }
+
+ pExternalLink->endElement(XML_definedName);
+}
+
+// List of external names =====================================================
+
+XclExpExtNameBuffer::XclExpExtNameBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertAddIn( const OUString& rName )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtNameAddIn( GetRoot(), rName ) );
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertEuroTool( const OUString& rName )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtNameBase( GetRoot(), rName ) );
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertDde(
+ std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem )
+{
+ sal_uInt16 nIndex = GetIndex( rItem );
+ if( nIndex == 0 )
+ {
+ size_t nPos;
+ if( GetDoc().FindDdeLink( rApplic, rTopic, rItem, SC_DDE_IGNOREMODE, nPos ) )
+ {
+ // create the leading 'StdDocumentName' EXTERNNAME record
+ if( maNameList.IsEmpty() )
+ AppendNew( new XclExpExtNameDde(
+ GetRoot(), "StdDocumentName", EXC_EXTN_EXPDDE_STDDOC ) );
+
+ // try to find DDE result array, but create EXTERNNAME record without them too
+ const ScMatrix* pScMatrix = GetDoc().GetDdeLinkResultMatrix( nPos );
+ nIndex = AppendNew( new XclExpExtNameDde( GetRoot(), rItem, EXC_EXTN_EXPDDE, pScMatrix ) );
+ }
+ }
+ return nIndex;
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertExtName( const XclExpSupbook& rSupbook,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtName( GetRoot(), rSupbook, rName, rArray ) );
+}
+
+void XclExpExtNameBuffer::Save( XclExpStream& rStrm )
+{
+ maNameList.Save( rStrm );
+}
+
+void XclExpExtNameBuffer::SaveXml(XclExpXmlStream& rStrm)
+{
+ maNameList.SaveXml(rStrm);
+}
+
+sal_uInt16 XclExpExtNameBuffer::GetIndex( std::u16string_view rName ) const
+{
+ for( size_t nPos = 0, nSize = maNameList.GetSize(); nPos < nSize; ++nPos )
+ if( maNameList.GetRecord( nPos )->GetName() == rName )
+ return static_cast< sal_uInt16 >( nPos + 1 );
+ return 0;
+}
+
+sal_uInt16 XclExpExtNameBuffer::AppendNew( XclExpExtNameBase* pExtName )
+{
+ size_t nSize = maNameList.GetSize();
+ if( nSize < 0x7FFF )
+ {
+ maNameList.AppendRecord( pExtName );
+ return static_cast< sal_uInt16 >( nSize + 1 );
+ }
+ return 0;
+}
+
+// Cached external cells ======================================================
+
+XclExpCrn::XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue ) :
+ XclExpRecord( EXC_ID_CRN, 4 ),
+ mnScCol( nScCol ),
+ mnScRow( nScRow )
+{
+ maValues.push_back( rValue );
+}
+
+bool XclExpCrn::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
+{
+ if( (nScRow != mnScRow) || (nScCol != static_cast< SCCOL >( mnScCol + maValues.size() )) )
+ return false;
+ maValues.push_back( rValue );
+ return true;
+}
+
+void XclExpCrn::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt8 >( mnScCol + maValues.size() - 1 )
+ << static_cast< sal_uInt8 >( mnScCol )
+ << static_cast< sal_uInt16 >( mnScRow );
+ for( const auto& rValue : maValues )
+ {
+ if( rValue.has< bool >() )
+ WriteBool( rStrm, rValue.get< bool >() );
+ else if( rValue.has< double >() )
+ WriteDouble( rStrm, rValue.get< double >() );
+ else if( rValue.has< OUString >() )
+ WriteString( rStrm, rValue.get< OUString >() );
+ else
+ WriteEmpty( rStrm );
+ }
+}
+
+void XclExpCrn::WriteBool( XclExpStream& rStrm, bool bValue )
+{
+ rStrm << EXC_CACHEDVAL_BOOL << sal_uInt8( bValue ? 1 : 0);
+ rStrm.WriteZeroBytes( 7 );
+}
+
+void XclExpCrn::WriteDouble( XclExpStream& rStrm, double fValue )
+{
+ if( !std::isfinite( fValue ) )
+ {
+ FormulaError nScError = GetDoubleErrorValue(fValue);
+ WriteError( rStrm, XclTools::GetXclErrorCode( nScError ) );
+ }
+ else
+ {
+ rStrm << EXC_CACHEDVAL_DOUBLE << fValue;
+ }
+}
+
+void XclExpCrn::WriteString( XclExpStream& rStrm, const OUString& rValue )
+{
+ rStrm << EXC_CACHEDVAL_STRING << XclExpString( rValue );
+}
+
+void XclExpCrn::WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode )
+{
+ rStrm << EXC_CACHEDVAL_ERROR << nErrCode;
+ rStrm.WriteZeroBytes( 7 );
+}
+
+void XclExpCrn::WriteEmpty( XclExpStream& rStrm )
+{
+ rStrm << EXC_CACHEDVAL_EMPTY;
+ rStrm.WriteZeroBytes( 8 );
+}
+
+void XclExpCrn::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
+
+ pFS->startElement(XML_row, XML_r, OString::number(mnScRow + 1));
+
+ ScAddress aAdr( mnScCol, mnScRow, 0); // Tab number doesn't matter
+ for( const auto& rValue : maValues )
+ {
+ bool bCloseCell = true;
+ if( rValue.has< double >() )
+ {
+ double fVal = rValue.get< double >();
+ if (std::isfinite( fVal))
+ {
+ // t='n' is omitted
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr));
+ pFS->startElement(XML_v);
+ pFS->write( fVal );
+ }
+ else
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "e");
+ pFS->startElement(XML_v);
+ pFS->write( "#VALUE!" ); // OOXTODO: support other error values
+ }
+ }
+ else if( rValue.has< OUString >() )
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "str");
+ pFS->startElement(XML_v);
+ pFS->write( rValue.get< OUString >() );
+ }
+ else if( rValue.has< bool >() )
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "b");
+ pFS->startElement(XML_v);
+ pFS->write( rValue.get< bool >() ? "1" : "0" );
+ }
+ // OOXTODO: error type cell t='e'
+ else
+ {
+ // Empty/blank cell not stored, only aAdr is incremented.
+ bCloseCell = false;
+ }
+ if (bCloseCell)
+ {
+ pFS->endElement(XML_v);
+ pFS->endElement(XML_cell);
+ }
+ aAdr.IncCol();
+ }
+
+ pFS->endElement( XML_row);
+}
+
+// Cached cells of a sheet ====================================================
+
+XclExpXct::XclExpXct( const XclExpRoot& rRoot, const OUString& rTabName,
+ sal_uInt16 nSBTab, ScExternalRefCache::TableTypeRef xCacheTable ) :
+ XclExpRoot( rRoot ),
+ mxCacheTable(std::move( xCacheTable )),
+ maUsedCells( rRoot.GetDoc().GetSheetLimits() ),
+ maBoundRange( ScAddress::INITIALIZE_INVALID ),
+ maTabName( rTabName ),
+ mnSBTab( nSBTab )
+{
+}
+
+void XclExpXct::StoreCellRange( const ScRange& rRange )
+{
+ // #i70418# restrict size of external range to prevent memory overflow
+ if( (rRange.aEnd.Col() - rRange.aStart.Col()) * (rRange.aEnd.Row() - rRange.aStart.Row()) > 1024 )
+ return;
+
+ maUsedCells.SetMultiMarkArea( rRange );
+ maBoundRange.ExtendTo( rRange );
+}
+
+void XclExpXct::StoreCell_( const ScAddress& rCell )
+{
+ maUsedCells.SetMultiMarkArea( ScRange( rCell ) );
+ maBoundRange.ExtendTo( ScRange( rCell ) );
+}
+
+void XclExpXct::StoreCellRange_( const ScRange& rRange )
+{
+ maUsedCells.SetMultiMarkArea( rRange );
+ maBoundRange.ExtendTo( rRange );
+}
+
+namespace {
+
+class XclExpCrnList : public XclExpRecordList< XclExpCrn >
+{
+public:
+ /** Inserts the passed value into an existing or new CRN record.
+ @return True = value inserted successfully, false = CRN list is full. */
+ bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+};
+
+bool XclExpCrnList::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
+{
+ RecordRefType xLastRec = GetLastRecord();
+ if( xLastRec && xLastRec->InsertValue( nScCol, nScRow, rValue ) )
+ return true;
+ if( GetSize() == SAL_MAX_UINT16 )
+ return false;
+ AppendNewRecord( new XclExpCrn( nScCol, nScRow, rValue ) );
+ return true;
+}
+
+} // namespace
+
+bool XclExpXct::BuildCrnList( XclExpCrnList& rCrnRecs )
+{
+ if( !mxCacheTable )
+ return false;
+
+ /* Get the range of used rows in the cache table. This may help to
+ optimize building the CRN record list if the cache table does not
+ contain all referred cells, e.g. if big empty ranges are used in the
+ formulas. */
+ ::std::pair< SCROW, SCROW > aRowRange = mxCacheTable->getRowRange();
+ if( aRowRange.first >= aRowRange.second )
+ return false;
+
+ /* Crop the bounding range of used cells in this table to Excel limits.
+ Return if there is no external cell inside these limits. */
+ if( !GetAddressConverter().ValidateRange( maBoundRange, false ) )
+ return false;
+
+ /* Find the resulting row range that needs to be processed. */
+ SCROW nScRow1 = ::std::max( aRowRange.first, maBoundRange.aStart.Row() );
+ SCROW nScRow2 = ::std::min( aRowRange.second - 1, maBoundRange.aEnd.Row() );
+ if( nScRow1 > nScRow2 )
+ return false;
+
+ /* Build and collect all CRN records before writing the XCT record. This
+ is needed to determine the total number of CRN records which must be
+ known when writing the XCT record (possibly encrypted, so seeking the
+ output stream back after writing the CRN records is not an option). */
+ SvNumberFormatter& rFormatter = GetFormatter();
+ bool bValid = true;
+ for( SCROW nScRow = nScRow1; bValid && (nScRow <= nScRow2); ++nScRow )
+ {
+ ::std::pair< SCCOL, SCCOL > aColRange = mxCacheTable->getColRange( nScRow );
+ const SCCOL nScEnd = ::std::min( aColRange.second, GetDoc().GetSheetLimits().GetMaxColCount() );
+ for( SCCOL nScCol = aColRange.first; bValid && (nScCol < nScEnd); ++nScCol )
+ {
+ if( maUsedCells.IsCellMarked( nScCol, nScRow, true ) )
+ {
+ sal_uInt32 nScNumFmt = 0;
+ ScExternalRefCache::TokenRef xToken = mxCacheTable->getCell( nScCol, nScRow, &nScNumFmt );
+ using namespace ::formula;
+ if( xToken )
+ switch( xToken->GetType() )
+ {
+ case svDouble:
+ bValid = (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) ?
+ rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() != 0 ) ) :
+ rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() ) );
+ break;
+ case svString:
+ // do not save empty strings (empty cells) to cache
+ if( !xToken->GetString().isEmpty() )
+ bValid = rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetString().getString() ) );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void XclExpXct::Save( XclExpStream& rStrm )
+{
+ XclExpCrnList aCrnRecs;
+ if (!BuildCrnList( aCrnRecs))
+ return;
+
+ // write the XCT record and the list of CRN records
+ rStrm.StartRecord( EXC_ID_XCT, 4 );
+ rStrm << static_cast< sal_uInt16 >( aCrnRecs.GetSize() ) << mnSBTab;
+ rStrm.EndRecord();
+ aCrnRecs.Save( rStrm );
+}
+
+void XclExpXct::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpCrnList aCrnRecs;
+
+ sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
+
+ bool bValid = BuildCrnList( aCrnRecs);
+ pFS->startElement(XML_sheetData, XML_sheetId, OString::number(mnSBTab));
+ if (bValid)
+ {
+ // row elements
+ aCrnRecs.SaveXml( rStrm );
+ }
+ pFS->endElement( XML_sheetData);
+}
+
+// External documents (EXTERNSHEET/SUPBOOK), base class =======================
+
+XclExpExternSheetBase::XclExpExternSheetBase( const XclExpRoot& rRoot, sal_uInt16 nRecId, sal_uInt32 nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpExtNameBuffer& XclExpExternSheetBase::GetExtNameBuffer()
+{
+ if( !mxExtNameBfr )
+ mxExtNameBfr = std::make_shared<XclExpExtNameBuffer>( GetRoot() );
+ return *mxExtNameBfr;
+}
+
+void XclExpExternSheetBase::WriteExtNameBuffer( XclExpStream& rStrm )
+{
+ if( mxExtNameBfr )
+ mxExtNameBfr->Save( rStrm );
+}
+
+void XclExpExternSheetBase::WriteExtNameBufferXml( XclExpXmlStream& rStrm )
+{
+ if( mxExtNameBfr )
+ mxExtNameBfr->SaveXml( rStrm );
+}
+
+// External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
+
+XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
+{
+ Init( OUStringChar(cCode) );
+}
+
+XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
+{
+ // reference to own sheet: \03<sheetname>
+ Init(Concat2View(OUStringChar(EXC_EXTSH_TABNAME) + rTabName));
+}
+
+void XclExpExternSheet::Save( XclExpStream& rStrm )
+{
+ // EXTERNSHEET record
+ XclExpRecord::Save( rStrm );
+ // EXTERNNAME records
+ WriteExtNameBuffer( rStrm );
+}
+
+void XclExpExternSheet::Init( std::u16string_view rEncUrl )
+{
+ OSL_ENSURE_BIFF( GetBiff() <= EXC_BIFF5 );
+ maTabName.AssignByte( rEncUrl, GetTextEncoding(), XclStrFlags::EightBitLength );
+ SetRecSize( maTabName.GetSize() );
+}
+
+sal_uInt16 XclExpExternSheet::InsertAddIn( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertAddIn( rName );
+}
+
+void XclExpExternSheet::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt8 nNameSize = static_cast< sal_uInt8 >( maTabName.Len() );
+ // special case: reference to own sheet (starting with '\03') needs wrong string length
+ if( maTabName.GetChar( 0 ) == EXC_EXTSH_TABNAME )
+ --nNameSize;
+ rStrm << nNameSize;
+ maTabName.WriteBuffer( rStrm );
+}
+
+// External document (SUPBOOK, BIFF8) =========================================
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ meType( XclSupbookType::Self ),
+ mnXclTabCount( nXclTabCount ),
+ mnFileId( 0 )
+{
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ meType( XclSupbookType::Addin ),
+ mnXclTabCount( 1 ),
+ mnFileId( 0 )
+{
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
+ maUrl( rUrl ),
+ maUrlEncoded( rUrl ),
+ meType( XclSupbookType::Eurotool ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
+ maUrl( rUrl ),
+ maUrlEncoded( XclExpUrlHelper::EncodeUrl( rRoot, rUrl ) ),
+ meType( XclSupbookType::Extern ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+
+ // We need to create all tables up front to ensure the correct table order.
+ ScExternalRefManager* pRefMgr = rRoot.GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId( rUrl );
+ mnFileId = nFileId + 1;
+ ScfStringVec aTabNames;
+ pRefMgr->getAllCachedTableNames( nFileId, aTabNames );
+ size_t nTabIndex = 0;
+ for( const auto& rTabName : aTabNames )
+ {
+ InsertTabName( rTabName, pRefMgr->getCacheTable( nFileId, nTabIndex ) );
+ ++nTabIndex;
+ }
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ maUrl( rApplic ),
+ maDdeTopic( rTopic ),
+ maUrlEncoded( XclExpUrlHelper::EncodeDde( rApplic, rTopic ) ),
+ meType( XclSupbookType::Special ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+}
+
+bool XclExpSupbook::IsUrlLink( std::u16string_view rUrl ) const
+{
+ return (meType == XclSupbookType::Extern || meType == XclSupbookType::Eurotool) && (maUrl == rUrl);
+}
+
+bool XclExpSupbook::IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const
+{
+ return (meType == XclSupbookType::Special) && (maUrl == rApplic) && (maDdeTopic == rTopic);
+}
+
+void XclExpSupbook::FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
+ sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const
+{
+ rRefLogEntry.mpUrl = maUrlEncoded.IsEmpty() ? nullptr : &maUrlEncoded;
+ rRefLogEntry.mpFirstTab = GetTabName( nFirstSBTab );
+ rRefLogEntry.mpLastTab = GetTabName( nLastSBTab );
+}
+
+void XclExpSupbook::StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab )
+{
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCellRange( rRange );
+}
+
+void XclExpSupbook::StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell )
+{
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCell_( rCell );
+}
+
+void XclExpSupbook::StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange )
+{
+ // multi-table range is not allowed!
+ if( rRange.aStart.Tab() == rRange.aEnd.Tab() )
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCellRange_( rRange );
+}
+
+sal_uInt16 XclExpSupbook::GetTabIndex( const OUString& rTabName ) const
+{
+ XclExpString aXclName(rTabName);
+ size_t nSize = maXctList.GetSize();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ XclExpXctRef aRec = maXctList.GetRecord(i);
+ if (aXclName == aRec->GetTabName())
+ return ulimit_cast<sal_uInt16>(i);
+ }
+ return EXC_NOTAB;
+}
+
+sal_uInt16 XclExpSupbook::GetTabCount() const
+{
+ return ulimit_cast<sal_uInt16>(maXctList.GetSize());
+}
+
+sal_uInt16 XclExpSupbook::InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable )
+{
+ SAL_WARN_IF( meType != XclSupbookType::Extern, "sc.filter", "Don't insert sheet names here" );
+ sal_uInt16 nSBTab = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
+ XclExpXctRef xXct = new XclExpXct( GetRoot(), rTabName, nSBTab, xCacheTable );
+ AddRecSize( xXct->GetTabName().GetSize() );
+ maXctList.AppendRecord( xXct );
+ return nSBTab;
+}
+
+sal_uInt16 XclExpSupbook::InsertAddIn( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertAddIn( rName );
+}
+
+sal_uInt16 XclExpSupbook::InsertEuroTool( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertEuroTool( rName );
+}
+
+sal_uInt16 XclExpSupbook::InsertDde( const OUString& rItem )
+{
+ return GetExtNameBuffer().InsertDde( maUrl, maDdeTopic, rItem );
+}
+
+sal_uInt16 XclExpSupbook::InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ return GetExtNameBuffer().InsertExtName(*this, rName, rArray);
+}
+
+XclSupbookType XclExpSupbook::GetType() const
+{
+ return meType;
+}
+
+sal_uInt16 XclExpSupbook::GetFileId() const
+{
+ return mnFileId;
+}
+
+const OUString& XclExpSupbook::GetUrl() const
+{
+ return maUrl;
+}
+
+void XclExpSupbook::Save( XclExpStream& rStrm )
+{
+ // SUPBOOK record
+ XclExpRecord::Save( rStrm );
+ // XCT record, CRN records
+ maXctList.Save( rStrm );
+ // EXTERNNAME records
+ WriteExtNameBuffer( rStrm );
+}
+
+void XclExpSupbook::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
+
+ // Add relation for this stream, e.g. xl/externalLinks/_rels/externalLink1.xml.rels
+ sal_uInt16 nLevel = 0;
+ bool bRel = true;
+
+ // BuildFileName delete ../ and convert them to nLevel
+ // but addrelation needs ../ instead of nLevel, so we have to convert it back
+ OUString sFile = XclExpHyperlink::BuildFileName(nLevel, bRel, maUrl, GetRoot(), true);
+ while (nLevel-- > 0)
+ sFile = "../" + sFile;
+
+ OUString sId = rStrm.addRelation( pExternalLink->getOutputStream(),
+ oox::getRelationship(Relationship::EXTERNALLINKPATH), sFile, true );
+
+ pExternalLink->startElement( XML_externalLink,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8());
+
+ pExternalLink->startElement( XML_externalBook,
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_r, XML_id), sId.toUtf8());
+
+ if (!maXctList.IsEmpty())
+ {
+ pExternalLink->startElement(XML_sheetNames);
+ for (size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos)
+ {
+ pExternalLink->singleElement(XML_sheetName,
+ XML_val, XclXmlUtils::ToOString(maXctList.GetRecord(nPos)->GetTabName()));
+ }
+ pExternalLink->endElement( XML_sheetNames);
+
+ }
+
+ if (mxExtNameBfr)
+ {
+ pExternalLink->startElement(XML_definedNames);
+ // externalName elements
+ WriteExtNameBufferXml( rStrm );
+ pExternalLink->endElement(XML_definedNames);
+ }
+
+ if (!maXctList.IsEmpty())
+ {
+ pExternalLink->startElement(XML_sheetDataSet);
+
+ // sheetData elements
+ maXctList.SaveXml( rStrm );
+
+ pExternalLink->endElement( XML_sheetDataSet);
+
+ }
+ pExternalLink->endElement( XML_externalBook);
+ pExternalLink->endElement( XML_externalLink);
+}
+
+const XclExpString* XclExpSupbook::GetTabName( sal_uInt16 nSBTab ) const
+{
+ XclExpXctRef xXct = maXctList.GetRecord( nSBTab );
+ return xXct ? &xXct->GetTabName() : nullptr;
+}
+
+void XclExpSupbook::WriteBody( XclExpStream& rStrm )
+{
+ switch( meType )
+ {
+ case XclSupbookType::Self:
+ rStrm << mnXclTabCount << EXC_SUPB_SELF;
+ break;
+ case XclSupbookType::Extern:
+ case XclSupbookType::Special:
+ case XclSupbookType::Eurotool:
+ {
+ sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
+ rStrm << nCount << maUrlEncoded;
+
+ for( size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos )
+ rStrm << maXctList.GetRecord( nPos )->GetTabName();
+ }
+ break;
+ case XclSupbookType::Addin:
+ rStrm << mnXclTabCount << EXC_SUPB_ADDIN;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "Unhandled SUPBOOK type " << meType);
+ }
+}
+
+// All SUPBOOKS in a document =================================================
+
+XclExpSupbookBuffer::XclExpSupbookBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnOwnDocSB( SAL_MAX_UINT16 ),
+ mnAddInSB( SAL_MAX_UINT16 )
+{
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ sal_uInt16 nXclCnt = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodeCnt = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+ size_t nCount = nXclCnt + rTabInfo.GetXclExtTabCount();
+
+ OSL_ENSURE( nCount > 0, "XclExpSupbookBuffer::XclExpSupbookBuffer - no sheets to export" );
+ if( nCount )
+ {
+ maSBIndexVec.resize( nCount );
+
+ // self-ref SUPBOOK first of list
+ XclExpSupbookRef xSupbook = new XclExpSupbook( GetRoot(), ::std::max( nXclCnt, nCodeCnt ) );
+ mnOwnDocSB = Append( xSupbook );
+ for( sal_uInt16 nXclTab = 0; nXclTab < nXclCnt; ++nXclTab )
+ maSBIndexVec[ nXclTab ].Set( mnOwnDocSB, nXclTab );
+ }
+}
+
+XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
+ XclExpRefLogEntry* pRefLogEntry ) const
+{
+ XclExpXti aXti;
+ size_t nSize = maSBIndexVec.size();
+ if( (nFirstXclTab < nSize) && (nLastXclTab < nSize) )
+ {
+ // index of the SUPBOOK record
+ aXti.mnSupbook = maSBIndexVec[ nFirstXclTab ].mnSupbook;
+
+ // all sheets in the same supbook?
+ bool bSameSB = true;
+ for( sal_uInt16 nXclTab = nFirstXclTab + 1; bSameSB && (nXclTab <= nLastXclTab); ++nXclTab )
+ {
+ bSameSB = maSBIndexVec[ nXclTab ].mnSupbook == aXti.mnSupbook;
+ if( !bSameSB )
+ nLastXclTab = nXclTab - 1;
+ }
+ aXti.mnFirstSBTab = maSBIndexVec[ nFirstXclTab ].mnSBTab;
+ aXti.mnLastSBTab = maSBIndexVec[ nLastXclTab ].mnSBTab;
+
+ // fill external reference log entry (for change tracking)
+ if( pRefLogEntry )
+ {
+ pRefLogEntry->mnFirstXclTab = nFirstXclTab;
+ pRefLogEntry->mnLastXclTab = nLastXclTab;
+ XclExpSupbookRef xSupbook = maSupbookList.GetRecord( aXti.mnSupbook );
+ if( xSupbook )
+ xSupbook->FillRefLogEntry( *pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab );
+ }
+ }
+ else
+ {
+ // special range, i.e. for deleted sheets or add-ins
+ aXti.mnSupbook = mnOwnDocSB;
+ aXti.mnFirstSBTab = nFirstXclTab;
+ aXti.mnLastSBTab = nLastXclTab;
+ }
+
+ return aXti;
+}
+
+void XclExpSupbookBuffer::StoreCellRange( const ScRange& rRange )
+{
+ sal_uInt16 nXclTab = GetTabInfo().GetXclTab( rRange.aStart.Tab() );
+ if( nXclTab < maSBIndexVec.size() )
+ {
+ const XclExpSBIndex& rSBIndex = maSBIndexVec[ nXclTab ];
+ XclExpSupbookRef xSupbook = maSupbookList.GetRecord( rSBIndex.mnSupbook );
+ OSL_ENSURE( xSupbook , "XclExpSupbookBuffer::StoreCellRange - missing SUPBOOK record" );
+ if( xSupbook )
+ xSupbook->StoreCellRange( rRange, rSBIndex.mnSBTab );
+ }
+}
+
+namespace {
+
+class FindSBIndexEntry
+{
+public:
+ explicit FindSBIndexEntry(sal_uInt16 nSupbookId, sal_uInt16 nTabId) :
+ mnSupbookId(nSupbookId), mnTabId(nTabId) {}
+
+ bool operator()(const XclExpSupbookBuffer::XclExpSBIndex& r) const
+ {
+ return mnSupbookId == r.mnSupbook && mnTabId == r.mnSBTab;
+ }
+
+private:
+ sal_uInt16 mnSupbookId;
+ sal_uInt16 mnTabId;
+};
+
+}
+
+void XclExpSupbookBuffer::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell )
+{
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+
+ sal_uInt16 nSheetId = xSupbook->GetTabIndex(rTabName);
+ if (nSheetId == EXC_NOTAB)
+ // specified table name not found in this SUPBOOK.
+ return;
+
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+
+ xSupbook->StoreCell_(nSheetId, rCell);
+}
+
+void XclExpSupbookBuffer::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+
+ SCTAB nTabCount = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+
+ // If this is a multi-table range, get token for each table.
+ using namespace ::formula;
+ SCTAB aMatrixListSize = 0;
+
+ // This is a new'ed instance, so we must manage its life cycle here.
+ ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, rTabName, rRange, nullptr);
+ if (!pArray)
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetType() == svMatrix)
+ ++aMatrixListSize;
+ else if (p->GetOpCode() != ocSep)
+ {
+ // This is supposed to be ocSep!!!
+ return;
+ }
+ }
+
+ if (aMatrixListSize != nTabCount)
+ {
+ // matrix size mismatch!
+ return;
+ }
+
+ sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
+
+ ScRange aRange(rRange);
+ aRange.aStart.SetTab(0);
+ aRange.aEnd.SetTab(0);
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ sal_uInt16 nSheetId = nFirstSheetId + static_cast<sal_uInt16>(nTab);
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+
+ xSupbook->StoreCellRange_(nSheetId, aRange);
+ }
+}
+
+bool XclExpSupbookBuffer::InsertAddIn(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpSupbookRef xSupbook;
+ if( mnAddInSB == SAL_MAX_UINT16 )
+ {
+ xSupbook = new XclExpSupbook( GetRoot() );
+ mnAddInSB = Append( xSupbook );
+ }
+ else
+ xSupbook = maSupbookList.GetRecord( mnAddInSB );
+ OSL_ENSURE( xSupbook, "XclExpSupbookBuffer::InsertAddin - missing add-in supbook" );
+ rnSupbook = mnAddInSB;
+ rnExtName = xSupbook->InsertAddIn( rName );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertEuroTool(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpSupbookRef xSupbook;
+ OUString aUrl( "\001\010EUROTOOL.XLA" );
+ if( !GetSupbookUrl( xSupbook, rnSupbook, aUrl ) )
+ {
+ xSupbook = new XclExpSupbook( GetRoot(), aUrl, XclSupbookType::Eurotool );
+ rnSupbook = Append( xSupbook );
+ }
+ rnExtName = xSupbook->InsertEuroTool( rName );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertDde(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ XclExpSupbookRef xSupbook;
+ if( !GetSupbookDde( xSupbook, rnSupbook, rApplic, rTopic ) )
+ {
+ xSupbook = new XclExpSupbook( GetRoot(), rApplic, rTopic );
+ rnSupbook = Append( xSupbook );
+ }
+ rnExtName = xSupbook->InsertDde( rItem );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertExtName(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ XclExpSupbookRef xSupbook;
+ if (!GetSupbookUrl(xSupbook, rnSupbook, rUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), rUrl);
+ rnSupbook = Append(xSupbook);
+ }
+ rnExtName = xSupbook->InsertExtName(rName, rArray);
+ return rnExtName > 0;
+}
+
+XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpXti aXti(0, EXC_NOTAB, EXC_NOTAB);
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return aXti;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+ aXti.mnSupbook = nSupbookId;
+
+ sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
+ if (nFirstSheetId == EXC_NOTAB)
+ {
+ // first sheet not found in SUPBOOK.
+ return aXti;
+ }
+ sal_uInt16 nSheetCount = xSupbook->GetTabCount();
+ for (sal_uInt16 i = 0; i < nXclTabSpan; ++i)
+ {
+ sal_uInt16 nSheetId = nFirstSheetId + i;
+ if (nSheetId >= nSheetCount)
+ return aXti;
+
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+ if (i == 0)
+ aXti.mnFirstSBTab = nSheetId;
+ if (i == nXclTabSpan - 1)
+ aXti.mnLastSBTab = nSheetId;
+ }
+
+ if (pRefLogEntry)
+ {
+ pRefLogEntry->mnFirstXclTab = 0;
+ pRefLogEntry->mnLastXclTab = 0;
+ if (xSupbook)
+ xSupbook->FillRefLogEntry(*pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab);
+ }
+
+ return aXti;
+}
+
+void XclExpSupbookBuffer::Save( XclExpStream& rStrm )
+{
+ maSupbookList.Save( rStrm );
+}
+
+void XclExpSupbookBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ // Unused external references are not saved, only kept in memory.
+ // Those that are saved must be indexed from 1, so indexes must be reordered
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ vector<sal_uInt16> aExternFileIds;
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ XclExpSupbookRef xRef(maSupbookList.GetRecord(nPos));
+ // fileIDs are indexed from 1 in xlsx, and from 0 in ScExternalRefManager
+ // converting between them require a -1 or +1
+ if (xRef->GetType() == XclSupbookType::Extern)
+ aExternFileIds.push_back(xRef->GetFileId() - 1);
+ }
+ if (aExternFileIds.size() > 0)
+ pRefMgr->setSkipUnusedFileIds(aExternFileIds);
+
+ ::std::map< sal_uInt16, OUString > aMap;
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ XclExpSupbookRef xRef( maSupbookList.GetRecord( nPos));
+ if (xRef->GetType() != XclSupbookType::Extern)
+ continue; // handle only external reference (for now?)
+
+ sal_uInt16 nId = xRef->GetFileId();
+ sal_uInt16 nUsedId = pRefMgr->convertFileIdToUsedFileId(nId - 1) + 1;
+ const OUString& rUrl = xRef->GetUrl();
+ ::std::pair< ::std::map< sal_uInt16, OUString >::iterator, bool > aInsert(
+ aMap.insert( ::std::make_pair( nId, rUrl)));
+ if (!aInsert.second)
+ {
+ SAL_WARN( "sc.filter", "XclExpSupbookBuffer::SaveXml: file ID already used: " << nId <<
+ " wanted for " << rUrl << " and is " << (*aInsert.first).second <<
+ (rUrl == (*aInsert.first).second ? " multiple Supbook not supported" : ""));
+ continue;
+ }
+ OUString sId;
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "externalLinks/externalLink", nUsedId),
+ XclXmlUtils::GetStreamName( nullptr, "externalLinks/externalLink", nUsedId),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("externalLink"),
+ &sId );
+
+ // externalReference entry in workbook externalReferences
+ rStrm.GetCurrentStream()->singleElement( XML_externalReference,
+ FSNS(XML_r, XML_id), sId.toUtf8() );
+
+ // Each externalBook in a separate stream.
+ rStrm.PushStream( pExternalLink );
+ xRef->SaveXml( rStrm );
+ rStrm.PopStream();
+ }
+}
+
+bool XclExpSupbookBuffer::HasExternalReferences() const
+{
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ if (maSupbookList.GetRecord( nPos)->GetType() == XclSupbookType::Extern)
+ return true;
+ }
+ return false;
+}
+
+bool XclExpSupbookBuffer::GetSupbookUrl(
+ XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, std::u16string_view rUrl ) const
+{
+ for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
+ {
+ rxSupbook = maSupbookList.GetRecord( nPos );
+ if( rxSupbook->IsUrlLink( rUrl ) )
+ {
+ rnIndex = ulimit_cast< sal_uInt16 >( nPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool XclExpSupbookBuffer::GetSupbookDde( XclExpSupbookRef& rxSupbook,
+ sal_uInt16& rnIndex, std::u16string_view rApplic, std::u16string_view rTopic ) const
+{
+ for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
+ {
+ rxSupbook = maSupbookList.GetRecord( nPos );
+ if( rxSupbook->IsDdeLink( rApplic, rTopic ) )
+ {
+ rnIndex = ulimit_cast< sal_uInt16 >( nPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+sal_uInt16 XclExpSupbookBuffer::Append( XclExpSupbookRef const & xSupbook )
+{
+ maSupbookList.AppendRecord( xSupbook );
+ return ulimit_cast< sal_uInt16 >( maSupbookList.GetSize() - 1 );
+}
+
+// Export link manager ========================================================
+
+XclExpLinkManagerImpl::XclExpLinkManagerImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpLinkManagerImpl5::XclExpLinkManagerImpl5( const XclExpRoot& rRoot ) :
+ XclExpLinkManagerImpl( rRoot )
+{
+}
+
+void XclExpLinkManagerImpl5::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ FindInternal( rnExtSheet, rnFirstXclTab, nFirstScTab );
+ if( (rnFirstXclTab == EXC_TAB_DELETED) || (nFirstScTab == nLastScTab) )
+ {
+ rnLastXclTab = rnFirstXclTab;
+ }
+ else
+ {
+ sal_uInt16 nDummyExtSheet;
+ FindInternal( nDummyExtSheet, rnLastXclTab, nLastScTab );
+ }
+
+ OSL_ENSURE( !pRefLogEntry, "XclExpLinkManagerImpl5::FindExtSheet - fill reflog entry not implemented" );
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::FindExtSheet( sal_Unicode cCode )
+{
+ sal_uInt16 nExtSheet;
+ FindInternal( nExtSheet, cCode );
+ return nExtSheet;
+}
+
+void XclExpLinkManagerImpl5::FindExtSheet(
+ sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, sal_uInt16 /*nXclTabSpan*/,
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnFirstSBTab*/, sal_uInt16& /*rnLastSBTab*/,
+ XclExpRefLogEntry* /*pRefLogEntry*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCellRange( const ScSingleRefData& /*rRef1*/, const ScSingleRefData& /*rRef2*/, const ScAddress& /*rPos*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCell( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScAddress& /*rPos*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCellRange( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScRange& /*rRange*/ )
+{
+ // not implemented
+}
+
+bool XclExpLinkManagerImpl5::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpExtSheetRef xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_ADDIN );
+ if( xExtSheet )
+ {
+ rnExtName = xExtSheet->InsertAddIn( rName );
+ return rnExtName > 0;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertEuroTool(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const OUString& /*rName*/ )
+{
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertDde(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/,
+ const OUString& /*rApplic*/, const OUString& /*rTopic*/, const OUString& /*rItem*/ )
+{
+ // not implemented
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertExtName(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const OUString& /*rUrl*/,
+ const OUString& /*rName*/, const ScExternalRefCache::TokenArrayRef& /*rArray*/ )
+{
+ // not implemented
+ return false;
+}
+
+void XclExpLinkManagerImpl5::Save( XclExpStream& rStrm )
+{
+ if( sal_uInt16 nExtSheetCount = GetExtSheetCount() )
+ {
+ // EXTERNCOUNT record
+ XclExpUInt16Record( EXC_ID_EXTERNCOUNT, nExtSheetCount ).Save( rStrm );
+ // list of EXTERNSHEET records with EXTERNNAME, XCT, CRN records
+ maExtSheetList.Save( rStrm );
+ }
+}
+
+void XclExpLinkManagerImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+ // not applicable
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::GetExtSheetCount() const
+{
+ return static_cast< sal_uInt16 >( maExtSheetList.GetSize() );
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::AppendInternal( XclExpExtSheetRef const & xExtSheet )
+{
+ if( GetExtSheetCount() < 0x7FFF )
+ {
+ maExtSheetList.AppendRecord( xExtSheet );
+ // return negated one-based EXTERNSHEET index (i.e. 0xFFFD for 3rd record)
+ return static_cast< sal_uInt16 >( -GetExtSheetCount() );
+ }
+ return 0;
+}
+
+void XclExpLinkManagerImpl5::CreateInternal()
+{
+ if( !maIntTabMap.empty() )
+ return;
+
+ // create EXTERNSHEET records for all internal exported sheets
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ for( SCTAB nScTab = 0, nScCnt = rTabInfo.GetScTabCount(); nScTab < nScCnt; ++nScTab )
+ {
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ XclExpExtSheetRef xRec;
+ if( nScTab == GetCurrScTab() )
+ xRec = new XclExpExternSheet( GetRoot(), EXC_EXTSH_OWNTAB );
+ else
+ xRec = new XclExpExternSheet( GetRoot(), rTabInfo.GetScTabName( nScTab ) );
+ maIntTabMap[ nScTab ] = AppendInternal( xRec );
+ }
+ }
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::GetInternal( sal_uInt16 nExtSheet )
+{
+ return maExtSheetList.GetRecord( static_cast< sal_uInt16 >( -nExtSheet - 1 ) );
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab )
+{
+ // create internal EXTERNSHEET records on demand
+ CreateInternal();
+
+ // try to find an EXTERNSHEET record - if not, return a "deleted sheet" reference
+ XclExpExtSheetRef xExtSheet;
+ XclExpIntTabMap::const_iterator aIt = maIntTabMap.find( nScTab );
+ if( aIt == maIntTabMap.end() )
+ {
+ xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_OWNDOC );
+ rnXclTab = EXC_TAB_DELETED;
+ }
+ else
+ {
+ rnExtSheet = aIt->second;
+ xExtSheet = GetInternal( rnExtSheet );
+ rnXclTab = GetTabInfo().GetXclTab( nScTab );
+ }
+ return xExtSheet;
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
+ sal_uInt16& rnExtSheet, sal_Unicode cCode )
+{
+ XclExpExtSheetRef xExtSheet;
+ XclExpCodeMap::const_iterator aIt = maCodeMap.find( cCode );
+ if( aIt == maCodeMap.end() )
+ {
+ xExtSheet = new XclExpExternSheet( GetRoot(), cCode );
+ rnExtSheet = maCodeMap[ cCode ] = AppendInternal( xExtSheet );
+ }
+ else
+ {
+ rnExtSheet = aIt->second;
+ xExtSheet = GetInternal( rnExtSheet );
+ }
+ return xExtSheet;
+}
+
+XclExpLinkManagerImpl8::XclExpLinkManagerImpl8( const XclExpRoot& rRoot ) :
+ XclExpLinkManagerImpl( rRoot ),
+ maSBBuffer( rRoot )
+{
+}
+
+void XclExpLinkManagerImpl8::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ rnFirstXclTab = rTabInfo.GetXclTab( nFirstScTab );
+ rnLastXclTab = rTabInfo.GetXclTab( nLastScTab );
+ rnExtSheet = InsertXti( maSBBuffer.GetXti( rnFirstXclTab, rnLastXclTab, pRefLogEntry ) );
+}
+
+sal_uInt16 XclExpLinkManagerImpl8::FindExtSheet( sal_Unicode cCode )
+{
+ OSL_ENSURE( (cCode == EXC_EXTSH_OWNDOC) || (cCode == EXC_EXTSH_ADDIN),
+ "XclExpLinkManagerImpl8::FindExtSheet - unknown externsheet code" );
+ return InsertXti( maSBBuffer.GetXti( EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+}
+
+void XclExpLinkManagerImpl8::FindExtSheet(
+ sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpXti aXti = maSBBuffer.GetXti(nFileId, rTabName, nXclTabSpan, pRefLogEntry);
+ rnExtSheet = InsertXti(aXti);
+ rnFirstSBTab = aXti.mnFirstSBTab;
+ rnLastSBTab = aXti.mnLastSBTab;
+}
+
+void XclExpLinkManagerImpl8::StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos )
+{
+ ScAddress aAbs1 = rRef1.toAbs(GetRoot().GetDoc(), rPos);
+ ScAddress aAbs2 = rRef2.toAbs(GetRoot().GetDoc(), rPos);
+ if (!(!rRef1.IsDeleted() && !rRef2.IsDeleted() && (aAbs1.Tab() >= 0) && (aAbs2.Tab() >= 0)))
+ return;
+
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ SCTAB nFirstScTab = aAbs1.Tab();
+ SCTAB nLastScTab = aAbs2.Tab();
+ ScRange aRange(aAbs1.Col(), aAbs1.Row(), 0, aAbs2.Col(), aAbs2.Row(), 0);
+ for (SCTAB nScTab = nFirstScTab; nScTab <= nLastScTab; ++nScTab)
+ {
+ if( rTabInfo.IsExternalTab( nScTab ) )
+ {
+ aRange.aStart.SetTab( nScTab );
+ aRange.aEnd.SetTab( nScTab );
+ maSBBuffer.StoreCellRange( aRange );
+ }
+ }
+}
+
+void XclExpLinkManagerImpl8::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
+{
+ maSBBuffer.StoreCell(nFileId, rTabName, rPos);
+}
+
+void XclExpLinkManagerImpl8::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ maSBBuffer.StoreCellRange(nFileId, rTabName, rRange);
+}
+
+bool XclExpLinkManagerImpl8::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertAddIn( nSupbook, rnExtName, rName ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertEuroTool( nSupbook, rnExtName, rName ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertDde( nSupbook, rnExtName, rApplic, rTopic, rItem ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertExtName( sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rUrl, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertExtName( nSupbook, rnExtName, rUrl, rName, rArray ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+void XclExpLinkManagerImpl8::Save( XclExpStream& rStrm )
+{
+ if( maXtiVec.empty() )
+ return;
+
+ // SUPBOOKs, XCTs, CRNs, EXTERNNAMEs
+ maSBBuffer.Save( rStrm );
+
+ // EXTERNSHEET
+ sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXtiVec.size() );
+ rStrm.StartRecord( EXC_ID_EXTERNSHEET, 2 + 6 * nCount );
+ rStrm << nCount;
+ rStrm.SetSliceSize( 6 );
+ for( const auto& rXti : maXtiVec )
+ rXti.Save( rStrm );
+ rStrm.EndRecord();
+}
+
+void XclExpLinkManagerImpl8::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (maSBBuffer.HasExternalReferences())
+ {
+ sax_fastparser::FSHelperPtr pWorkbook = rStrm.GetCurrentStream();
+ pWorkbook->startElement(XML_externalReferences);
+
+ // externalLink, externalBook, sheetNames, sheetDataSet, externalName
+ maSBBuffer.SaveXml( rStrm );
+
+ pWorkbook->endElement( XML_externalReferences);
+ }
+
+ // TODO: equivalent for EXTERNSHEET in OOXML?
+#if 0
+ if( !maXtiVec.empty() )
+ {
+ for( const auto& rXti : maXtiVec )
+ rXti.SaveXml( rStrm );
+ }
+#endif
+}
+
+sal_uInt16 XclExpLinkManagerImpl8::InsertXti( const XclExpXti& rXti )
+{
+ auto aIt = std::find(maXtiVec.begin(), maXtiVec.end(), rXti);
+ if (aIt != maXtiVec.end())
+ return ulimit_cast< sal_uInt16 >( std::distance(maXtiVec.begin(), aIt) );
+ maXtiVec.push_back( rXti );
+ return ulimit_cast< sal_uInt16 >( maXtiVec.size() - 1 );
+}
+
+XclExpLinkManager::XclExpLinkManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ mxImpl = std::make_shared<XclExpLinkManagerImpl5>( rRoot );
+ break;
+ case EXC_BIFF8:
+ mxImpl = std::make_shared<XclExpLinkManagerImpl8>( rRoot );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+XclExpLinkManager::~XclExpLinkManager()
+{
+}
+
+void XclExpLinkManager::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab,
+ SCTAB nScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( rnExtSheet, rnXclTab, rnXclTab, nScTab, nScTab, pRefLogEntry );
+}
+
+void XclExpLinkManager::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( rnExtSheet, rnFirstXclTab, rnLastXclTab, nFirstScTab, nLastScTab, pRefLogEntry );
+}
+
+sal_uInt16 XclExpLinkManager::FindExtSheet( sal_Unicode cCode )
+{
+ return mxImpl->FindExtSheet( cCode );
+}
+
+void XclExpLinkManager::FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( nFileId, rTabName, nXclTabSpan, rnExtSheet, rnFirstSBTab, rnLastSBTab, pRefLogEntry );
+}
+
+void XclExpLinkManager::StoreCell( const ScSingleRefData& rRef, const ScAddress& rPos )
+{
+ mxImpl->StoreCellRange(rRef, rRef, rPos);
+}
+
+void XclExpLinkManager::StoreCellRange( const ScComplexRefData& rRef, const ScAddress& rPos )
+{
+ mxImpl->StoreCellRange(rRef.Ref1, rRef.Ref2, rPos);
+}
+
+void XclExpLinkManager::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
+{
+ mxImpl->StoreCell(nFileId, rTabName, rPos);
+}
+
+void XclExpLinkManager::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ mxImpl->StoreCellRange(nFileId, rTabName, rRange);
+}
+
+bool XclExpLinkManager::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ return mxImpl->InsertAddIn( rnExtSheet, rnExtName, rName );
+}
+
+bool XclExpLinkManager::InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ return mxImpl->InsertEuroTool( rnExtSheet, rnExtName, rName );
+}
+
+bool XclExpLinkManager::InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ return mxImpl->InsertDde( rnExtSheet, rnExtName, rApplic, rTopic, rItem );
+}
+
+bool XclExpLinkManager::InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl, const OUString& rName,
+ const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ return mxImpl->InsertExtName(rnExtSheet, rnExtName, rUrl, rName, rArray);
+}
+
+void XclExpLinkManager::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpLinkManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xename.cxx b/sc/source/filter/excel/xename.cxx
new file mode 100644
index 0000000000..84073da8ff
--- /dev/null
+++ b/sc/source/filter/excel/xename.cxx
@@ -0,0 +1,876 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xename.hxx>
+
+#include <map>
+
+#include <document.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <excrecds.hxx>
+#include <xlname.hxx>
+#include <xeformula.hxx>
+#include <xestring.hxx>
+#include <xltools.hxx>
+
+#include <formula/grammar.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <com/sun/star/sheet/NamedRangeFlag.hdl>
+
+using namespace ::oox;
+using namespace ::com::sun::star;
+
+// *** Helper classes ***
+
+namespace {
+
+/** Represents an internal defined name, supports writing it to a NAME record. */
+class XclExpName : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Creates a standard defined name. */
+ explicit XclExpName( const XclExpRoot& rRoot, const OUString& rName );
+ /** Creates a built-in defined name. */
+ explicit XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn );
+
+ /** Sets a token array containing the definition of this name. */
+ void SetTokenArray( const XclTokenArrayRef& xTokArr );
+ /** Changes this defined name to be local on the specified Calc sheet. */
+ void SetLocalTab( SCTAB nScTab );
+ /** Hides or unhides the defined name. */
+ void SetHidden( bool bHidden = true );
+ /** Changes this name to be the call to a VB macro function or procedure.
+ @param bVBasic true = Visual Basic macro, false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ void SetMacroCall( bool bVBasic, bool bFunc );
+
+ /** Sets the name's symbol value
+ @param sValue the name's symbolic value */
+ void SetSymbol( const OUString& rValue );
+
+ /** Returns the original name (title) of this defined name. */
+ const OUString& GetOrigName() const { return maOrigName; }
+ /** Returns the Excel built-in name index of this defined name.
+ @return The built-in name index or EXC_BUILTIN_UNKNOWN for user-defined names. */
+ sal_Unicode GetBuiltInName() const { return mcBuiltIn; }
+
+ /** Returns the symbol value for this defined name. */
+ const OUString& GetSymbol() const { return msSymbol; }
+
+ /** Returns true, if this is a document-global defined name. */
+ bool IsGlobal() const { return mnXclTab == EXC_NAME_GLOBAL; }
+ /** Returns the Calc sheet of a local defined name. */
+ SCTAB GetScTab() const { return mnScTab; }
+
+ /** Returns true, if this defined name is volatile. */
+ bool IsVolatile() const;
+ /** Returns true, if this defined name describes a macro call.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ bool IsMacroCall( bool bVBasic, bool bFunc ) const;
+
+ /** Writes the entire NAME record to the passed stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the NAME record to the passed stream. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Convert localized range separators */
+ OUString GetWithDefaultRangeSeparator( const OUString& rSymbol ) const;
+
+private:
+ OUString maOrigName; /// The original user-defined name.
+ OUString msSymbol; /// The value of the symbol
+ XclExpStringRef mxName; /// The name as Excel string object.
+ XclTokenArrayRef mxTokArr; /// The definition of the defined name.
+ sal_Unicode mcBuiltIn; /// The built-in index for built-in names.
+ SCTAB mnScTab; /// The Calc sheet index for local names.
+ sal_uInt16 mnFlags; /// Additional flags for this defined name.
+ sal_uInt16 mnExtSheet; /// The 1-based index to a global EXTERNSHEET record.
+ sal_uInt16 mnXclTab; /// The 1-based Excel sheet index for local names.
+};
+
+}
+
+/** Implementation class of the name manager. */
+class XclExpNameManagerImpl : protected XclExpRoot
+{
+public:
+ explicit XclExpNameManagerImpl( const XclExpRoot& rRoot );
+
+ /** Creates NAME records for built-in and user defined names. */
+ void Initialize();
+
+ /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
+ sal_uInt16 InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab );
+
+ /** Inserts a new built-in defined name. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& aRangeList );
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange );
+ /** Inserts a new defined name. Sets another unused name, if rName already exists. */
+ sal_uInt16 InsertUniqueName( const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab );
+ /** Returns index of an existing name, or creates a name without definition. */
+ sal_uInt16 InsertRawName( const OUString& rName );
+ /** Searches or inserts a defined name describing a macro name.
+ @param bVBasic true = Visual Basic macro; false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ sal_uInt16 InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden );
+
+ /** Returns the NAME record at the specified position or 0 on error. */
+ const XclExpName* GetName( sal_uInt16 nNameIdx ) const;
+
+ /** Writes the entire list of NAME records.
+ @descr In BIFF7 and lower, writes the entire global link table, which
+ consists of an EXTERNCOUNT record, several EXTERNSHEET records, and
+ the list of NAME records. */
+ void Save( XclExpStream& rStrm );
+
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ typedef XclExpRecordList< XclExpName > XclExpNameList;
+ typedef XclExpNameList::RecordRefType XclExpNameRef;
+
+ typedef ::std::map< ::std::pair<SCTAB, OUString>, sal_uInt16> NamedExpMap;
+
+private:
+ /**
+ * @param nTab 0-based table index, or SCTAB_GLOBAL for global names.
+ * @param nScIdx calc's name index.
+ *
+ * @return excel's name index.
+ */
+ sal_uInt16 FindNamedExp( SCTAB nTab, OUString sName );
+
+ /** Returns the index of an existing built-in NAME record with the passed definition, otherwise 0. */
+ sal_uInt16 FindBuiltInNameIdx( const OUString& rName,
+ const OUString& sSymbol ) const;
+ /** Returns an unused name for the passed name. */
+ OUString GetUnusedName( const OUString& rName ) const;
+
+ /** Appends a new NAME record to the record list.
+ @return The 1-based NAME record index used elsewhere in the Excel file. */
+ sal_uInt16 Append( XclExpName* pName );
+ sal_uInt16 Append( XclExpNameRef const & rxName ) { return Append(rxName.get()); }
+ /** Creates a new NAME record for the passed user-defined name.
+ @return The 1-based NAME record index used elsewhere in the Excel file. */
+ sal_uInt16 CreateName( SCTAB nTab, const ScRangeData& rRangeData );
+
+ /** Creates NAME records for all built-in names in the document. */
+ void CreateBuiltInNames();
+ /** Creates NAME records for all user-defined names in the document. */
+ void CreateUserNames();
+
+private:
+ /**
+ * Maps Calc's named range to Excel's NAME records. Global names use
+ * -1 as their table index, whereas sheet-local names have 0-based table
+ * index.
+ */
+ NamedExpMap maNamedExpMap;
+ XclExpNameList maNameList; /// List of NAME records.
+ size_t mnFirstUserIdx; /// List index of first user-defined NAME record.
+};
+
+// *** Implementation ***
+
+XclExpName::XclExpName( const XclExpRoot& rRoot, const OUString& rName ) :
+ XclExpRecord( EXC_ID_NAME ),
+ XclExpRoot( rRoot ),
+ maOrigName( rName ),
+ mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
+ mcBuiltIn( EXC_BUILTIN_UNKNOWN ),
+ mnScTab( SCTAB_GLOBAL ),
+ mnFlags( EXC_NAME_DEFAULT ),
+ mnExtSheet( EXC_NAME_GLOBAL ),
+ mnXclTab( EXC_NAME_GLOBAL )
+{
+}
+
+XclExpName::XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn ) :
+ XclExpRecord( EXC_ID_NAME ),
+ XclExpRoot( rRoot ),
+ mcBuiltIn( cBuiltIn ),
+ mnScTab( SCTAB_GLOBAL ),
+ mnFlags( EXC_NAME_DEFAULT ),
+ mnExtSheet( EXC_NAME_GLOBAL ),
+ mnXclTab( EXC_NAME_GLOBAL )
+{
+ // filter source range is hidden in Excel
+ if( cBuiltIn == EXC_BUILTIN_FILTERDATABASE )
+ SetHidden();
+
+ // special case for BIFF5/7 filter source range - name appears as plain text without built-in flag
+ if( (GetBiff() <= EXC_BIFF5) && (cBuiltIn == EXC_BUILTIN_FILTERDATABASE) )
+ {
+ OUString aName( XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE ) );
+ mxName = XclExpStringHelper::CreateString( rRoot, aName, XclStrFlags::EightBitLength );
+ maOrigName = XclTools::GetXclBuiltInDefName( cBuiltIn );
+ }
+ else
+ {
+ maOrigName = XclTools::GetBuiltInDefNameXml( cBuiltIn ) ;
+ mxName = XclExpStringHelper::CreateString( rRoot, cBuiltIn, XclStrFlags::EightBitLength );
+ ::set_flag( mnFlags, EXC_NAME_BUILTIN );
+ }
+}
+
+void XclExpName::SetTokenArray( const XclTokenArrayRef& xTokArr )
+{
+ mxTokArr = xTokArr;
+}
+
+void XclExpName::SetLocalTab( SCTAB nScTab )
+{
+ OSL_ENSURE( GetTabInfo().IsExportTab( nScTab ), "XclExpName::SetLocalTab - invalid sheet index" );
+ if( !GetTabInfo().IsExportTab( nScTab ) )
+ return;
+
+ mnScTab = nScTab;
+ GetGlobalLinkManager().FindExtSheet( mnExtSheet, mnXclTab, nScTab );
+
+ // special handling for NAME record
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: // EXTERNSHEET index is positive in NAME record
+ mnExtSheet = ~mnExtSheet + 1;
+ break;
+ case EXC_BIFF8: // EXTERNSHEET index not used, but must be created in link table
+ mnExtSheet = 0;
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ // Excel sheet index is 1-based
+ ++mnXclTab;
+}
+
+void XclExpName::SetHidden( bool bHidden )
+{
+ ::set_flag( mnFlags, EXC_NAME_HIDDEN, bHidden );
+}
+
+void XclExpName::SetMacroCall( bool bVBasic, bool bFunc )
+{
+ ::set_flag( mnFlags, EXC_NAME_PROC );
+ ::set_flag( mnFlags, EXC_NAME_VB, bVBasic );
+ ::set_flag( mnFlags, EXC_NAME_FUNC, bFunc );
+}
+
+void XclExpName::SetSymbol( const OUString& rSymbol )
+{
+ msSymbol = rSymbol;
+}
+
+bool XclExpName::IsVolatile() const
+{
+ return mxTokArr && mxTokArr->IsVolatile();
+}
+
+bool XclExpName::IsMacroCall( bool bVBasic, bool bFunc ) const
+{
+ return
+ (::get_flag( mnFlags, EXC_NAME_VB ) == bVBasic) &&
+ (::get_flag( mnFlags, EXC_NAME_FUNC ) == bFunc);
+}
+
+void XclExpName::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mxName && (mxName->Len() > 0), "XclExpName::Save - missing name" );
+ OSL_ENSURE( !(IsGlobal() && ::get_flag( mnFlags, EXC_NAME_BUILTIN )), "XclExpName::Save - global built-in name" );
+ SetRecSize( 11 + mxName->GetSize() + (mxTokArr ? mxTokArr->GetSize() : 2) );
+ XclExpRecord::Save( rStrm );
+}
+
+OUString XclExpName::GetWithDefaultRangeSeparator( const OUString& rSymbol ) const
+{
+ sal_Int32 nPos = rSymbol.indexOf(';');
+ if ( nPos > -1 )
+ {
+ // convert with validation
+ ScRange aRange;
+ ScAddress::Details detailsXL( ::formula::FormulaGrammar::CONV_XL_A1 );
+ ScRefFlags nRes = aRange.Parse( rSymbol.copy(0, nPos), GetDoc(), detailsXL );
+ if ( nRes & ScRefFlags::VALID )
+ {
+ nRes = aRange.Parse( rSymbol.copy(nPos+1), GetDoc(), detailsXL );
+ if ( nRes & ScRefFlags::VALID )
+ {
+ return rSymbol.replaceFirst(";", ",");
+ }
+ }
+ }
+ return rSymbol;
+}
+
+void XclExpName::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_definedName,
+ // OOXTODO: XML_comment, "",
+ // OOXTODO: XML_customMenu, "",
+ // OOXTODO: XML_description, "",
+ XML_function, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) ),
+ // OOXTODO: XML_functionGroupId, "",
+ // OOXTODO: XML_help, "",
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_NAME_HIDDEN ) ),
+ XML_localSheetId, sax_fastparser::UseIf(OString::number( mnScTab ), mnScTab != SCTAB_GLOBAL),
+ XML_name, maOrigName.toUtf8(),
+ // OOXTODO: XML_publishToServer, "",
+ // OOXTODO: XML_shortcutKey, "",
+ // OOXTODO: XML_statusBar, "",
+ XML_vbProcedure, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) )
+ // OOXTODO: XML_workbookParameter, "",
+ // OOXTODO: XML_xlm, ""
+ );
+ rWorkbook->writeEscaped( GetWithDefaultRangeSeparator( msSymbol ) );
+ rWorkbook->endElement( XML_definedName );
+}
+
+void XclExpName::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nFmlaSize = mxTokArr ? mxTokArr->GetSize() : 0;
+
+ rStrm << mnFlags // flags
+ << sal_uInt8( 0 ); // keyboard shortcut
+ mxName->WriteLenField( rStrm ); // length of name
+ rStrm << nFmlaSize // size of token array
+ << mnExtSheet // BIFF5/7: EXTSHEET index, BIFF8: not used
+ << mnXclTab // 1-based sheet index for local names
+ << sal_uInt32( 0 ); // length of menu/descr/help/status text
+ mxName->WriteFlagField( rStrm ); // BIFF8 flag field (no-op in <=BIFF7)
+ mxName->WriteBuffer( rStrm ); // character array of the name
+ if( mxTokArr )
+ mxTokArr->WriteArray( rStrm ); // token array without size
+}
+
+/** Returns true (needed fixing) if FormulaToken was not absolute and 3D.
+ So, regardless of whether the fix was successful or not, true is still returned since a fix was required.*/
+static bool lcl_EnsureAbs3DToken( const SCTAB nTab, formula::FormulaToken* pTok, const bool bFix = true )
+{
+ bool bFixRequired = false;
+ if ( !pTok || ( pTok->GetType() != formula::svSingleRef && pTok->GetType() != formula::svDoubleRef ) )
+ return bFixRequired;
+
+ ScSingleRefData* pRef1 = pTok->GetSingleRef();
+ if ( !pRef1 )
+ return bFixRequired;
+
+ ScSingleRefData* pRef2 = nullptr;
+ if ( pTok->GetType() == formula::svDoubleRef )
+ pRef2 = pTok->GetSingleRef2();
+
+ if ( pRef1->IsTabRel() || !pRef1->IsFlag3D() )
+ {
+ bFixRequired = true;
+ if ( bFix )
+ {
+ if ( pRef1->IsTabRel() && nTab != SCTAB_GLOBAL )
+ pRef1->SetAbsTab( nTab + pRef1->Tab() ); //XLS requirement
+ if ( !pRef1->IsTabRel() )
+ {
+ pRef1->SetFlag3D( true ); //XLSX requirement
+ if ( pRef2 && !pRef2->IsTabRel() )
+ pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
+ }
+ }
+ }
+
+ if ( pRef2 && pRef2->IsTabRel() && !pRef1->IsTabRel() )
+ {
+ bFixRequired = true;
+ if ( bFix && nTab != SCTAB_GLOBAL )
+ {
+ pRef2->SetAbsTab( nTab + pRef2->Tab() );
+ pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
+ }
+ }
+ return bFixRequired;
+}
+
+XclExpNameManagerImpl::XclExpNameManagerImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnFirstUserIdx( 0 )
+{
+}
+
+void XclExpNameManagerImpl::Initialize()
+{
+ CreateBuiltInNames();
+ mnFirstUserIdx = maNameList.GetSize();
+ CreateUserNames();
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
+{
+ sal_uInt16 nNameIdx = 0;
+ const ScRangeData* pData = nullptr;
+ ScRangeName* pRN = (nTab == SCTAB_GLOBAL) ? GetDoc().GetRangeName() : GetDoc().GetRangeName(nTab);
+ if (pRN)
+ pData = pRN->findByIndex(nScNameIdx);
+
+ if (pData)
+ {
+ bool bEmulateGlobalRelativeTable = false;
+ const ScTokenArray* pCode = pData->GetCode();
+ if ( pCode
+ && nTab == SCTAB_GLOBAL
+ && (pData->HasType( ScRangeData::Type::AbsPos ) || pData->HasType( ScRangeData::Type::AbsArea )) )
+ {
+ bEmulateGlobalRelativeTable = lcl_EnsureAbs3DToken( nTab, pCode->FirstToken(), /*bFix=*/false );
+ }
+ nNameIdx = FindNamedExp( bEmulateGlobalRelativeTable ? nCurrTab : nTab, pData->GetName() );
+ if (!nNameIdx)
+ nNameIdx = CreateName(nTab, *pData);
+ }
+
+ return nNameIdx;
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange )
+{
+ XclExpNameRef xName = new XclExpName( GetRoot(), cBuiltIn );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( aRange.aStart.Tab() );
+ OUString sSymbol(aRange.Format(GetDoc(), ScRefFlags::RANGE_ABS_3D, ScAddress::Details( ::formula::FormulaGrammar::CONV_XL_A1)));
+ xName->SetSymbol( sSymbol );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& rRangeList )
+{
+ XclExpNameRef xName = new XclExpName( GetRoot(), cBuiltIn );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( nScTab );
+ OUString sSymbol;
+ rRangeList.Format( sSymbol, ScRefFlags::RANGE_ABS_3D, GetDoc(), ::formula::FormulaGrammar::CONV_XL_A1 );
+ xName->SetSymbol( sSymbol );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertUniqueName(
+ const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
+{
+ OSL_ENSURE( !rName.isEmpty(), "XclExpNameManagerImpl::InsertUniqueName - empty name" );
+ XclExpNameRef xName = new XclExpName( GetRoot(), GetUnusedName( rName ) );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( nScTab );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertRawName( const OUString& rName )
+{
+ // empty name? may occur in broken external Calc tokens
+ if( rName.isEmpty() )
+ return 0;
+
+ // try to find an existing NAME record, regardless of its type
+ for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nListIdx );
+ if( xName->IsGlobal() && (xName->GetOrigName() == rName) )
+ return static_cast< sal_uInt16 >( nListIdx + 1 );
+ }
+
+ // create a new NAME record
+ XclExpNameRef xName = new XclExpName( GetRoot(), rName );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
+{
+ // empty name? may occur in broken external Calc tokens
+ if( rMacroName.isEmpty() )
+ return 0;
+
+ // try to find an existing NAME record
+ for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nListIdx );
+ if( xName->IsMacroCall( bVBasic, bFunc ) && (xName->GetOrigName() == rMacroName) )
+ return static_cast< sal_uInt16 >( nListIdx + 1 );
+ }
+
+ // create a new NAME record
+ XclExpNameRef xName = new XclExpName( GetRoot(), rMacroName );
+ xName->SetMacroCall( bVBasic, bFunc );
+ xName->SetHidden( bHidden );
+
+ // for sheet macros, add a #NAME! error
+ if( !bVBasic )
+ xName->SetTokenArray( GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NAME ) );
+
+ return Append( xName );
+}
+
+const XclExpName* XclExpNameManagerImpl::GetName( sal_uInt16 nNameIdx ) const
+{
+ OSL_ENSURE( maNameList.HasRecord( nNameIdx - 1 ), "XclExpNameManagerImpl::GetName - wrong record index" );
+ return maNameList.GetRecord( nNameIdx - 1 );
+}
+
+void XclExpNameManagerImpl::Save( XclExpStream& rStrm )
+{
+ maNameList.Save( rStrm );
+}
+
+void XclExpNameManagerImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maNameList.IsEmpty() )
+ return;
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement(XML_definedNames);
+ maNameList.SaveXml( rStrm );
+ rWorkbook->endElement( XML_definedNames );
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt16 XclExpNameManagerImpl::FindNamedExp( SCTAB nTab, OUString sName )
+{
+ NamedExpMap::key_type key(nTab, sName);
+ NamedExpMap::const_iterator itr = maNamedExpMap.find(key);
+ return (itr == maNamedExpMap.end()) ? 0 : itr->second;
+}
+
+sal_uInt16 XclExpNameManagerImpl::FindBuiltInNameIdx(
+ const OUString& rName, const OUString& sSymbol ) const
+{
+ /* Get built-in index from the name. Special case: the database range
+ 'unnamed' will be mapped to Excel's built-in '_FilterDatabase' name. */
+ sal_Unicode cBuiltIn = XclTools::GetBuiltInDefNameIndex( rName );
+
+ if( cBuiltIn < EXC_BUILTIN_UNKNOWN )
+ {
+ // try to find the record in existing built-in NAME record list
+ for( size_t nPos = 0; nPos < mnFirstUserIdx; ++nPos )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nPos );
+ if( xName->GetBuiltInName() == cBuiltIn && xName->GetSymbol().replace(';', ',') == sSymbol.replace(';', ',') )
+ {
+ // tdf#112567 restore the original built-in names with non-localized separators
+ // TODO: support more localizations, if needed
+ if ( xName->GetSymbol() != sSymbol )
+ {
+ xName->SetSymbol(xName->GetSymbol().replace(';', ','));
+ }
+ return static_cast< sal_uInt16 >( nPos + 1 );
+ }
+ }
+ }
+ return 0;
+}
+
+OUString XclExpNameManagerImpl::GetUnusedName( const OUString& rName ) const
+{
+ OUString aNewName( rName );
+ sal_Int32 nAppIdx = 0;
+ bool bExist = true;
+ while( bExist )
+ {
+ // search the list of user-defined names
+ bExist = false;
+ for( size_t nPos = mnFirstUserIdx, nSize = maNameList.GetSize(); !bExist && (nPos < nSize); ++nPos )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nPos );
+ bExist = xName->GetOrigName() == aNewName;
+ // name exists -> create a new name "<originalname>_<counter>"
+ if( bExist )
+ aNewName = rName + "_" + OUString::number( ++nAppIdx );
+ }
+ }
+ return aNewName;
+}
+
+sal_uInt16 XclExpNameManagerImpl::Append( XclExpName* pName )
+{
+ if( maNameList.GetSize() == 0xFFFF )
+ return 0;
+ maNameList.AppendRecord( pName );
+ return static_cast< sal_uInt16 >( maNameList.GetSize() ); // 1-based
+}
+
+sal_uInt16 XclExpNameManagerImpl::CreateName( SCTAB nTab, const ScRangeData& rRangeData )
+{
+ const OUString& rName = rRangeData.GetName();
+
+ /* #i38821# recursive names: first insert the (empty) name object,
+ otherwise a recursive call of this function from the formula compiler
+ with the same defined name will not find it and will create it again. */
+ size_t nOldListSize = maNameList.GetSize();
+ XclExpNameRef xName = new XclExpName( GetRoot(), rName );
+ if (nTab != SCTAB_GLOBAL)
+ xName->SetLocalTab(nTab);
+ sal_uInt16 nNameIdx = Append( xName );
+ // store the index of the NAME record in the lookup map
+ NamedExpMap::key_type key(nTab, rRangeData.GetName());
+ maNamedExpMap[key] = nNameIdx;
+
+ // Check if it is a hidden named range
+ if ((rRangeData.GetUnoType() & sheet::NamedRangeFlag::HIDDEN) == sheet::NamedRangeFlag::HIDDEN)
+ xName->SetHidden(true);
+
+ /* Create the definition formula.
+ This may cause recursive creation of other defined names. */
+ if( const ScTokenArray* pScTokArr = const_cast< ScRangeData& >( rRangeData ).GetCode() )
+ {
+ XclTokenArrayRef xTokArr;
+ OUString sSymbol;
+ // MSO requires named ranges to have absolute sheet references
+ if ( rRangeData.HasType( ScRangeData::Type::AbsPos ) || rRangeData.HasType( ScRangeData::Type::AbsArea ) )
+ {
+ // Don't modify the actual document; use a temporary copy to create the export formulas.
+ ScTokenArray aTokenCopy( pScTokArr->CloneValue() );
+ lcl_EnsureAbs3DToken(nTab, aTokenCopy.FirstToken());
+
+ xTokArr = GetFormulaCompiler().CreateFormula(EXC_FMLATYPE_NAME, aTokenCopy);
+ if ( GetOutput() != EXC_OUTPUT_BINARY )
+ {
+ ScCompiler aComp(GetDoc(), rRangeData.GetPos(), aTokenCopy,
+ formula::FormulaGrammar::GRAM_OOXML);
+ aComp.CreateStringFromTokenArray( sSymbol );
+ }
+ }
+ else
+ {
+ bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007;
+ xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, *pScTokArr, bOOXML ?
+ &rRangeData.GetPos() : nullptr );
+ sSymbol = rRangeData.GetSymbol( (GetOutput() == EXC_OUTPUT_BINARY) ?
+ formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 : formula::FormulaGrammar::GRAM_OOXML);
+ }
+ xName->SetTokenArray( xTokArr );
+ xName->SetSymbol( sSymbol );
+
+ /* Try to replace by existing built-in name - complete token array is
+ needed for comparison, and due to the recursion problem above this
+ cannot be done earlier. If a built-in name is found, the created NAME
+ record for this name and all following records in the list must be
+ deleted, otherwise they may contain wrong name list indexes. */
+ sal_uInt16 nBuiltInIdx = FindBuiltInNameIdx( rName, sSymbol );
+ if( nBuiltInIdx != 0 )
+ {
+ // delete the new NAME records
+ while( maNameList.GetSize() > nOldListSize )
+ maNameList.RemoveRecord( maNameList.GetSize() - 1 );
+ // use index of the found built-in NAME record
+ key = NamedExpMap::key_type(nTab, rRangeData.GetName());
+ maNamedExpMap[key] = nNameIdx = nBuiltInIdx;
+ }
+ }
+
+ return nNameIdx;
+}
+
+void XclExpNameManagerImpl::CreateBuiltInNames()
+{
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ /* #i2394# built-in defined names must be sorted by the name of the
+ containing sheet. Example: SheetA!Print_Range must be stored *before*
+ SheetB!Print_Range, regardless of the position of SheetA in the document! */
+ for( SCTAB nScTabIdx = 0, nScTabCount = rTabInfo.GetScTabCount(); nScTabIdx < nScTabCount; ++nScTabIdx )
+ {
+ // find real sheet index from the nScTabIdx counter
+ SCTAB nScTab = rTabInfo.GetRealScTab( nScTabIdx );
+ // create NAME records for all built-in names of this sheet
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ // *** 1) print ranges *** ----------------------------------------
+
+ if( rDoc.HasPrintRange() )
+ {
+ ScRangeList aRangeList;
+ for( sal_uInt16 nIdx = 0, nCount = rDoc.GetPrintRangeCount( nScTab ); nIdx < nCount; ++nIdx )
+ {
+ const ScRange* pPrintRange = rDoc.GetPrintRange( nScTab, nIdx );
+ if (!pPrintRange)
+ continue;
+ ScRange aRange( *pPrintRange );
+ // Calc document does not care about sheet index in print ranges
+ aRange.aStart.SetTab( nScTab );
+ aRange.aEnd.SetTab( nScTab );
+ aRange.PutInOrder();
+ aRangeList.push_back( aRange );
+ }
+ // create the NAME record (do not warn if ranges are shrunken)
+ GetAddressConverter().ValidateRangeList( aRangeList, false );
+ if( !aRangeList.empty() )
+ GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTAREA, aRangeList );
+ }
+
+ // *** 2) print titles *** ----------------------------------------
+
+ ScRangeList aTitleList;
+ // repeated columns
+ if( std::optional<ScRange> oColRange = rDoc.GetRepeatColRange( nScTab ) )
+ aTitleList.push_back( ScRange(
+ oColRange->aStart.Col(), 0, nScTab,
+ oColRange->aEnd.Col(), GetXclMaxPos().Row(), nScTab ) );
+ // repeated rows
+ if( std::optional<ScRange> oRowRange = rDoc.GetRepeatRowRange( nScTab ) )
+ aTitleList.push_back( ScRange(
+ 0, oRowRange->aStart.Row(), nScTab,
+ GetXclMaxPos().Col(), oRowRange->aEnd.Row(), nScTab ) );
+ // create the NAME record
+ GetAddressConverter().ValidateRangeList( aTitleList, false );
+ if( !aTitleList.empty() )
+ GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTTITLES, aTitleList );
+
+ // *** 3) filter ranges *** ---------------------------------------
+
+ if( GetBiff() == EXC_BIFF8 )
+ GetFilterManager().InitTabFilter( nScTab );
+ }
+ }
+}
+
+void XclExpNameManagerImpl::CreateUserNames()
+{
+ std::vector<ScRangeData*> vEmulateAsLocalRange;
+ const ScRangeName& rNamedRanges = GetNamedRanges();
+ for (const auto& rEntry : rNamedRanges)
+ {
+ // skip definitions of shared formulas
+ if (!FindNamedExp(SCTAB_GLOBAL, rEntry.second->GetName()))
+ {
+ const ScTokenArray* pCode = rEntry.second->GetCode();
+ if ( pCode
+ && (rEntry.second->HasType( ScRangeData::Type::AbsPos ) || rEntry.second->HasType( ScRangeData::Type::AbsArea ))
+ && lcl_EnsureAbs3DToken( SCTAB_GLOBAL, pCode->FirstToken(), /*bFix=*/false ) )
+ {
+ vEmulateAsLocalRange.emplace_back(rEntry.second.get());
+ }
+ else
+ CreateName(SCTAB_GLOBAL, *rEntry.second);
+ }
+ }
+ //look at sheets containing local range names
+ ScRangeName::TabNameCopyMap rLocalNames;
+ GetDoc().GetAllTabRangeNames(rLocalNames);
+ for (const auto& [rTab, pRangeName] : rLocalNames)
+ {
+ for (const auto& rEntry : *pRangeName)
+ {
+ // skip definitions of shared formulas
+ if (!FindNamedExp(rTab, rEntry.second->GetName()))
+ CreateName(rTab, *rEntry.second);
+ }
+ }
+
+ // Emulate relative global variables by creating a copy in each local range.
+ // Creating AFTER true local range names so that conflicting global names will be ignored.
+ for ( SCTAB nTab = 0; nTab < GetDoc().GetTableCount(); ++nTab )
+ {
+ for ( auto rangeDataItr : vEmulateAsLocalRange )
+ {
+ if ( !FindNamedExp(nTab, rangeDataItr->GetName()) )
+ CreateName(nTab, *rangeDataItr );
+ }
+ }
+}
+
+XclExpNameManager::XclExpNameManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxImpl( std::make_shared<XclExpNameManagerImpl>( rRoot ) )
+{
+}
+
+XclExpNameManager::~XclExpNameManager()
+{
+}
+
+void XclExpNameManager::Initialize()
+{
+ mxImpl->Initialize();
+}
+
+sal_uInt16 XclExpNameManager::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
+{
+ return mxImpl->InsertName( nTab, nScNameIdx, nCurrTab );
+}
+
+sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange )
+{
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRange );
+ return mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRange );
+}
+
+sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList )
+{
+ sal_uInt16 nNameIdx = 0;
+ if( !rRangeList.empty() )
+ {
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRangeList );
+ nNameIdx = mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRangeList.front().aStart.Tab(), rRangeList );
+ }
+ return nNameIdx;
+}
+
+sal_uInt16 XclExpNameManager::InsertUniqueName(
+ const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
+{
+ return mxImpl->InsertUniqueName( rName, xTokArr, nScTab );
+}
+
+sal_uInt16 XclExpNameManager::InsertRawName( const OUString& rName )
+{
+ return mxImpl->InsertRawName( rName );
+}
+
+sal_uInt16 XclExpNameManager::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
+{
+ return mxImpl->InsertMacroCall( rMacroName, bVBasic, bFunc, bHidden );
+}
+
+OUString XclExpNameManager::GetOrigName( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName ? pName->GetOrigName() : OUString();
+}
+
+SCTAB XclExpNameManager::GetScTab( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName ? pName->GetScTab() : SCTAB_GLOBAL;
+}
+
+bool XclExpNameManager::IsVolatile( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName && pName->IsVolatile();
+}
+
+void XclExpNameManager::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpNameManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepage.cxx b/sc/source/filter/excel/xepage.cxx
new file mode 100644
index 0000000000..144ab51c07
--- /dev/null
+++ b/sc/source/filter/excel/xepage.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 <utility>
+#include <xepage.hxx>
+#include <svl/itemset.hxx>
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <sax/fastattribs.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <attrib.hxx>
+#include <xehelper.hxx>
+#include <xeescher.hxx>
+#include <xltools.hxx>
+
+#include <set>
+#include <limits>
+
+using namespace ::oox;
+
+using ::std::set;
+using ::std::numeric_limits;
+
+// Page settings records ======================================================
+
+// Header/footer --------------------------------------------------------------
+
+XclExpHeaderFooter::XclExpHeaderFooter( sal_uInt16 nRecId, OUString aHdrString ) :
+ XclExpRecord( nRecId ),
+ maHdrString(std::move( aHdrString ))
+{
+}
+
+void XclExpHeaderFooter::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ sal_Int32 nElement;
+ switch(GetRecId()) {
+ case EXC_ID_HEADER_FIRST: nElement = XML_firstHeader; break;
+ case EXC_ID_FOOTER_FIRST: nElement = XML_firstFooter; break;
+ case EXC_ID_HEADER_EVEN: nElement = XML_evenHeader; break;
+ case EXC_ID_FOOTER_EVEN: nElement = XML_evenFooter; break;
+ case EXC_ID_HEADER: nElement = XML_oddHeader; break;
+ case EXC_ID_FOOTER:
+ default: nElement = XML_oddFooter;
+ }
+ rWorksheet->startElement(nElement);
+ rWorksheet->writeEscaped( maHdrString );
+ rWorksheet->endElement( nElement );
+}
+
+void XclExpHeaderFooter::WriteBody( XclExpStream& rStrm )
+{
+ if( !maHdrString.isEmpty() )
+ {
+ XclExpString aExString;
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ aExString.AssignByte( maHdrString, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aExString.Assign( maHdrString, XclStrFlags::NONE, 255 ); // 16-bit length, but max 255 chars
+ rStrm << aExString;
+ }
+}
+
+// General page settings ------------------------------------------------------
+
+XclExpSetup::XclExpSetup( const XclPageData& rPageData ) :
+ XclExpRecord( EXC_ID_SETUP, 34 ),
+ mrData( rPageData )
+{
+}
+
+void XclExpSetup::SaveXml( XclExpXmlStream& rStrm )
+{
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ if( rStrm.getVersion() != oox::core::ISOIEC_29500_2008 ||
+ mrData.mnStrictPaperSize != EXC_PAPERSIZE_USER )
+ {
+ pAttrList->add( XML_paperSize, OString::number( mrData.mnPaperSize ) );
+ }
+ else
+ {
+ pAttrList->add( XML_paperWidth, OString::number( mrData.mnPaperWidth ) + "mm" );
+ pAttrList->add( XML_paperHeight, OString::number( mrData.mnPaperHeight ) + "mm" );
+ // pAttrList->add( XML_paperUnits, "mm" );
+ }
+ pAttrList->add( XML_scale, OString::number( mrData.mnScaling ) );
+ pAttrList->add( XML_fitToWidth, OString::number( mrData.mnFitToWidth ) );
+ pAttrList->add( XML_fitToHeight, OString::number( mrData.mnFitToHeight ) );
+ pAttrList->add( XML_pageOrder, mrData.mbPrintInRows ? "overThenDown" : "downThenOver" );
+ pAttrList->add( XML_orientation, mrData.mbPortrait ? "portrait" : "landscape" ); // OOXTODO: "default"?
+ // tdf#48767 if XML_usePrinterDefaults field is exist, then XML_orientation is always "portrait" in MS Excel
+ // To resolve that import issue, if XML_usePrinterDefaults has default value (false) then XML_usePrinterDefaults is not added.
+ if ( !mrData.mbValid )
+ pAttrList->add( XML_usePrinterDefaults, ToPsz( !mrData.mbValid ) );
+ pAttrList->add( XML_blackAndWhite, ToPsz( mrData.mbBlackWhite ) );
+ pAttrList->add( XML_draft, ToPsz( mrData.mbDraftQuality ) );
+ pAttrList->add( XML_cellComments, mrData.mbPrintNotes ? "atEnd" : "none" ); // OOXTODO: "asDisplayed"?
+
+ if ( mrData.mbManualStart )
+ {
+ pAttrList->add( XML_firstPageNumber, OString::number( mrData.mnStartPage ) );
+ pAttrList->add( XML_useFirstPageNumber, ToPsz( mrData.mbManualStart ) );
+ }
+ // OOXTODO: XML_errors, // == displayed|blank|dash|NA
+ pAttrList->add( XML_horizontalDpi, OString::number( mrData.mnHorPrintRes ) );
+ pAttrList->add( XML_verticalDpi, OString::number( mrData.mnVerPrintRes ) );
+ pAttrList->add( XML_copies, OString::number( mrData.mnCopies ) );
+ // OOXTODO: devMode settings part RelationshipId: FSNS( XML_r, XML_id ),
+
+ rStrm.GetCurrentStream()->singleElement( XML_pageSetup, pAttrList );
+}
+
+void XclExpSetup::WriteBody( XclExpStream& rStrm )
+{
+ XclBiff eBiff = rStrm.GetRoot().GetBiff();
+
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_SETUP_INROWS, mrData.mbPrintInRows );
+ ::set_flag( nFlags, EXC_SETUP_PORTRAIT, mrData.mbPortrait );
+ ::set_flag( nFlags, EXC_SETUP_INVALID, !mrData.mbValid );
+ ::set_flag( nFlags, EXC_SETUP_BLACKWHITE, mrData.mbBlackWhite );
+ if( eBiff >= EXC_BIFF5 )
+ {
+ ::set_flag( nFlags, EXC_SETUP_DRAFT, mrData.mbDraftQuality );
+ /* Set the Comments/Notes to "At end of sheet" if Print Notes is true.
+ We don't currently support "as displayed on sheet". Thus this value
+ will be re-interpreted to "At end of sheet". */
+ const sal_uInt16 nNotes = EXC_SETUP_PRINTNOTES | EXC_SETUP_NOTES_END;
+ ::set_flag( nFlags, nNotes, mrData.mbPrintNotes );
+ ::set_flag( nFlags, EXC_SETUP_STARTPAGE, mrData.mbManualStart );
+ }
+
+ rStrm << mrData.mnPaperSize << mrData.mnScaling << mrData.mnStartPage
+ << mrData.mnFitToWidth << mrData.mnFitToHeight << nFlags;
+ if( eBiff >= EXC_BIFF5 )
+ {
+ rStrm << mrData.mnHorPrintRes << mrData.mnVerPrintRes
+ << mrData.mfHeaderMargin << mrData.mfFooterMargin << mrData.mnCopies;
+ }
+}
+
+// Manual page breaks ---------------------------------------------------------
+
+XclExpPageBreaks::XclExpPageBreaks( sal_uInt16 nRecId, const ScfUInt16Vec& rPageBreaks, sal_uInt16 nMaxPos ) :
+ XclExpRecord( nRecId ),
+ mrPageBreaks( rPageBreaks ),
+ mnMaxPos( nMaxPos )
+{
+}
+
+void XclExpPageBreaks::Save( XclExpStream& rStrm )
+{
+ if( !mrPageBreaks.empty() )
+ {
+ SetRecSize( 2 + ((rStrm.GetRoot().GetBiff() <= EXC_BIFF5) ? 2 : 6) * mrPageBreaks.size() );
+ XclExpRecord::Save( rStrm );
+ }
+}
+
+void XclExpPageBreaks::WriteBody( XclExpStream& rStrm )
+{
+ bool bWriteRange = (rStrm.GetRoot().GetBiff() == EXC_BIFF8);
+
+ rStrm << static_cast< sal_uInt16 >( mrPageBreaks.size() );
+ for( const auto& rPageBreak : mrPageBreaks )
+ {
+ rStrm << rPageBreak;
+ if( bWriteRange )
+ rStrm << sal_uInt16( 0 ) << mnMaxPos;
+ }
+}
+
+void XclExpPageBreaks::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mrPageBreaks.empty() )
+ return;
+
+ sal_Int32 nElement = GetRecId() == EXC_ID_HORPAGEBREAKS ? XML_rowBreaks : XML_colBreaks;
+ sax_fastparser::FSHelperPtr& pWorksheet = rStrm.GetCurrentStream();
+ OString sNumPageBreaks = OString::number( mrPageBreaks.size() );
+ pWorksheet->startElement( nElement,
+ XML_count, sNumPageBreaks,
+ XML_manualBreakCount, sNumPageBreaks );
+ for( const auto& rPageBreak : mrPageBreaks )
+ {
+ pWorksheet->singleElement( XML_brk,
+ XML_id, OString::number(rPageBreak),
+ XML_man, "true",
+ XML_max, OString::number(mnMaxPos),
+ XML_min, "0"
+ // OOXTODO: XML_pt, ""
+ );
+ }
+ pWorksheet->endElement( nElement );
+}
+
+// Page settings ==============================================================
+
+XclExpPageSettings::XclExpPageSettings( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( rDoc.GetPageStyle( nScTab ), SfxStyleFamily::Page ) )
+ {
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ maData.mbValid = true;
+
+ // *** page settings ***
+
+ maData.mbPrintInRows = ! rItemSet.Get( ATTR_PAGE_TOPDOWN ).GetValue();
+ maData.mbHorCenter = rItemSet.Get( ATTR_PAGE_HORCENTER ).GetValue();
+ maData.mbVerCenter = rItemSet.Get( ATTR_PAGE_VERCENTER ).GetValue();
+ maData.mbPrintHeadings = rItemSet.Get( ATTR_PAGE_HEADERS ).GetValue();
+ maData.mbPrintGrid = rItemSet.Get( ATTR_PAGE_GRID ).GetValue();
+ maData.mbPrintNotes = rItemSet.Get( ATTR_PAGE_NOTES ).GetValue();
+
+ maData.mnStartPage = rItemSet.Get( ATTR_PAGE_FIRSTPAGENO ).GetValue();
+ maData.mbManualStart = maData.mnStartPage && (!nScTab || rDoc.NeedPageResetAfterTab( nScTab - 1 ));
+
+ const SvxLRSpaceItem& rLRItem = rItemSet.Get( ATTR_LRSPACE );
+ maData.mfLeftMargin = XclTools::GetInchFromTwips( rLRItem.GetLeft() );
+ maData.mfRightMargin = XclTools::GetInchFromTwips( rLRItem.GetRight() );
+ const SvxULSpaceItem& rULItem = rItemSet.Get( ATTR_ULSPACE );
+ maData.mfTopMargin = XclTools::GetInchFromTwips( rULItem.GetUpper() );
+ maData.mfBottomMargin = XclTools::GetInchFromTwips( rULItem.GetLower() );
+
+ const SvxPageItem& rPageItem = rItemSet.Get( ATTR_PAGE );
+ const SvxSizeItem& rSizeItem = rItemSet.Get( ATTR_PAGE_SIZE );
+ maData.SetScPaperSize( rSizeItem.GetSize(), !rPageItem.IsLandscape() );
+
+ const ScPageScaleToItem& rScaleToItem = rItemSet.Get( ATTR_PAGE_SCALETO );
+ sal_uInt16 nPages = rItemSet.Get( ATTR_PAGE_SCALETOPAGES ).GetValue();
+ sal_uInt16 nScale = rItemSet.Get( ATTR_PAGE_SCALE ).GetValue();
+
+ if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETO, false ) && rScaleToItem.IsValid() )
+ {
+ maData.mnFitToWidth = rScaleToItem.GetWidth();
+ maData.mnFitToHeight = rScaleToItem.GetHeight();
+ maData.mbFitToPages = true;
+ }
+ else if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETOPAGES, false ) && nPages )
+ {
+ maData.mnFitToWidth = 1;
+ maData.mnFitToHeight = nPages;
+ maData.mbFitToPages = true;
+ }
+ else if( nScale )
+ {
+ maData.mnScaling = nScale;
+ maData.mbFitToPages = false;
+ }
+
+ maData.mxBrushItem.reset( new SvxBrushItem( rItemSet.Get( ATTR_BACKGROUND ) ) );
+ maData.mbUseEvenHF = false;
+ maData.mbUseFirstHF = false;
+
+ // *** header and footer ***
+
+ XclExpHFConverter aHFConv( GetRoot() );
+
+ // header
+ const SfxItemSet& rHdrItemSet = rItemSet.Get( ATTR_PAGE_HEADERSET ).GetItemSet();
+ if( rHdrItemSet.Get( ATTR_PAGE_ON ).GetValue() )
+ {
+ const ScPageHFItem& rHFItem = rItemSet.Get( ATTR_PAGE_HEADERRIGHT );
+ aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() );
+ maData.maHeader = aHFConv.GetHFString();
+ if ( rHdrItemSet.HasItem(ATTR_PAGE_SHARED) && !rHdrItemSet.Get(ATTR_PAGE_SHARED).GetValue())
+ {
+ const ScPageHFItem& rHFItemLeft = rItemSet.Get( ATTR_PAGE_HEADERLEFT );
+ aHFConv.GenerateString( rHFItemLeft.GetLeftArea(), rHFItemLeft.GetCenterArea(), rHFItemLeft.GetRightArea() );
+ maData.maHeaderEven = aHFConv.GetHFString();
+ maData.mbUseEvenHF = true;
+ }
+ else
+ {
+ // If maData.mbUseEvenHF become true, then we will need a copy of maHeader in maHeaderEven.
+ maData.maHeaderEven = maData.maHeader;
+ }
+ if (rHdrItemSet.HasItem(ATTR_PAGE_SHARED_FIRST) && !rHdrItemSet.Get(ATTR_PAGE_SHARED_FIRST).GetValue())
+ {
+ const ScPageHFItem& rHFItemFirst = rItemSet.Get( ATTR_PAGE_HEADERFIRST );
+ aHFConv.GenerateString( rHFItemFirst.GetLeftArea(), rHFItemFirst.GetCenterArea(), rHFItemFirst.GetRightArea() );
+ maData.maHeaderFirst = aHFConv.GetHFString();
+ maData.mbUseFirstHF = true;
+ }
+ else
+ {
+ maData.maHeaderFirst = maData.maHeader;
+ }
+ // header height (Excel excludes header from top margin)
+ sal_Int32 nHdrHeight = rHdrItemSet.Get( ATTR_PAGE_DYNAMIC ).GetValue() ?
+ // dynamic height: calculate header height, add header <-> sheet area distance
+ (aHFConv.GetTotalHeight() + rHdrItemSet.Get( ATTR_ULSPACE ).GetLower()) :
+ // static height: ATTR_PAGE_SIZE already includes header <-> sheet area distance
+ static_cast< sal_Int32 >( rHdrItemSet.Get( ATTR_PAGE_SIZE ).GetSize().Height() );
+ maData.mfHeaderMargin = maData.mfTopMargin;
+ maData.mfTopMargin += XclTools::GetInchFromTwips( nHdrHeight );
+ }
+
+ // footer
+ const SfxItemSet& rFtrItemSet = rItemSet.Get( ATTR_PAGE_FOOTERSET ).GetItemSet();
+ if( rFtrItemSet.Get( ATTR_PAGE_ON ).GetValue() )
+ {
+ const ScPageHFItem& rHFItem = rItemSet.Get( ATTR_PAGE_FOOTERRIGHT );
+ aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() );
+ maData.maFooter = aHFConv.GetHFString();
+ if (rFtrItemSet.HasItem(ATTR_PAGE_SHARED) && !rFtrItemSet.Get(ATTR_PAGE_SHARED).GetValue())
+ {
+ const ScPageHFItem& rHFItemLeft = rItemSet.Get( ATTR_PAGE_FOOTERLEFT );
+ aHFConv.GenerateString( rHFItemLeft.GetLeftArea(), rHFItemLeft.GetCenterArea(), rHFItemLeft.GetRightArea() );
+ maData.maFooterEven = aHFConv.GetHFString();
+ maData.mbUseEvenHF = true;
+ }
+ else
+ {
+ maData.maFooterEven = maData.maFooter;
+ }
+ if (rFtrItemSet.HasItem(ATTR_PAGE_SHARED_FIRST) && !rFtrItemSet.Get(ATTR_PAGE_SHARED_FIRST).GetValue())
+ {
+ const ScPageHFItem& rHFItemFirst = rItemSet.Get( ATTR_PAGE_FOOTERFIRST );
+ aHFConv.GenerateString( rHFItemFirst.GetLeftArea(), rHFItemFirst.GetCenterArea(), rHFItemFirst.GetRightArea() );
+ maData.maFooterFirst = aHFConv.GetHFString();
+ maData.mbUseFirstHF = true;
+ }
+ else
+ {
+ maData.maFooterFirst = maData.maFooter;
+ }
+ // footer height (Excel excludes footer from bottom margin)
+ sal_Int32 nFtrHeight = rFtrItemSet.Get( ATTR_PAGE_DYNAMIC ).GetValue() ?
+ // dynamic height: calculate footer height, add sheet area <-> footer distance
+ (aHFConv.GetTotalHeight() + rFtrItemSet.Get( ATTR_ULSPACE ).GetUpper()) :
+ // static height: ATTR_PAGE_SIZE already includes sheet area <-> footer distance
+ static_cast< sal_Int32 >( rFtrItemSet.Get( ATTR_PAGE_SIZE ).GetSize().Height() );
+ maData.mfFooterMargin = maData.mfBottomMargin;
+ maData.mfBottomMargin += XclTools::GetInchFromTwips( nFtrHeight );
+ }
+ }
+
+ // *** page breaks ***
+
+ set<SCROW> aRowBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nScTab, false, true);
+
+ SCROW const nMaxRow = numeric_limits<sal_uInt16>::max();
+ for (const SCROW nRow : aRowBreaks)
+ {
+ if (nRow > nMaxRow)
+ break;
+
+ maData.maHorPageBreaks.push_back(nRow);
+ }
+
+ if (maData.maHorPageBreaks.size() > 1026)
+ {
+ // Excel allows only up to 1026 page breaks. Trim any excess page breaks.
+ ScfUInt16Vec::iterator itr = maData.maHorPageBreaks.begin();
+ ::std::advance(itr, 1026);
+ maData.maHorPageBreaks.erase(itr, maData.maHorPageBreaks.end());
+ }
+
+ set<SCCOL> aColBreaks;
+ rDoc.GetAllColBreaks(aColBreaks, nScTab, false, true);
+ for (const auto& rColBreak : aColBreaks)
+ maData.maVerPageBreaks.push_back(rColBreak);
+}
+
+namespace {
+
+class XclExpXmlStartHeaderFooterElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartHeaderFooterElementRecord(sal_Int32 const nElement, bool const bDifferentOddEven = false, bool const bDifferentFirst = false)
+ : XclExpXmlElementRecord(nElement), mbDifferentOddEven(bDifferentOddEven), mbDifferentFirst(bDifferentFirst) {}
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ bool mbDifferentOddEven;
+ bool mbDifferentFirst;
+};
+
+}
+
+void XclExpXmlStartHeaderFooterElementRecord::SaveXml(XclExpXmlStream& rStrm)
+{
+ // OOXTODO: we currently only emit oddHeader/oddFooter elements, and
+ // do not support the first/even/odd page distinction.
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ rStream->startElement( mnElement,
+ // OOXTODO: XML_alignWithMargins,
+ XML_differentFirst, mbDifferentFirst ? "true" : "false",
+ XML_differentOddEven, mbDifferentOddEven ? "true" : "false"
+ // OOXTODO: XML_scaleWithDoc
+ );
+}
+
+void XclExpPageSettings::Save( XclExpStream& rStrm )
+{
+ XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_GRIDSET, true ).Save( rStrm );
+ XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).Save( rStrm );
+ XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).Save( rStrm );
+ XclExpSetup( maData ).Save( rStrm );
+
+ if( (GetBiff() == EXC_BIFF8) && maData.mxBrushItem )
+ if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() )
+ XclExpImgData( *pGraphic, EXC_ID8_IMGDATA ).Save( rStrm );
+}
+
+void XclExpPageSettings::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpXmlStartSingleElementRecord( XML_printOptions ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings, XML_headings ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid, XML_gridLines ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_GRIDSET, true, XML_gridLinesSet ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter, XML_horizontalCentered ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter, XML_verticalCentered ).SaveXml( rStrm );
+ XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_printOptions
+
+ XclExpXmlStartSingleElementRecord( XML_pageMargins ).SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).SetAttribute( XML_left )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).SetAttribute( XML_right )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).SetAttribute( XML_top )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).SetAttribute( XML_bottom )->SaveXml( rStrm );
+ XclExpDoubleRecord( 0, maData.mfHeaderMargin).SetAttribute( XML_header )->SaveXml( rStrm );
+ XclExpDoubleRecord( 0, maData.mfFooterMargin).SetAttribute( XML_footer )->SaveXml( rStrm );
+ XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_pageMargins
+
+ XclExpSetup( maData ).SaveXml( rStrm );
+
+ XclExpXmlStartHeaderFooterElementRecord(XML_headerFooter, maData.mbUseEvenHF, maData.mbUseFirstHF).SaveXml(rStrm);
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).SaveXml( rStrm );
+ if (maData.mbUseEvenHF)
+ {
+ XclExpHeaderFooter( EXC_ID_HEADER_EVEN, maData.maHeaderEven ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER_EVEN, maData.maFooterEven ).SaveXml( rStrm );
+ }
+ if (maData.mbUseFirstHF)
+ {
+ XclExpHeaderFooter( EXC_ID_HEADER_FIRST, maData.maHeaderFirst ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER_FIRST, maData.maFooterFirst ).SaveXml( rStrm );
+ }
+ XclExpXmlEndElementRecord( XML_headerFooter ).SaveXml( rStrm );
+
+ XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks,
+ static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).SaveXml( rStrm );
+ XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks,
+ static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).SaveXml( rStrm );
+}
+
+XclExpImgData* XclExpPageSettings::getGraphicExport()
+{
+ if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() )
+ return new XclExpImgData( *pGraphic, EXC_ID8_IMGDATA );
+
+ return nullptr;
+}
+
+XclExpChartPageSettings::XclExpChartPageSettings( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpChartPageSettings::Save( XclExpStream& rStrm )
+{
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm );
+ XclExpSetup( maData ).Save( rStrm );
+ XclExpUInt16Record( EXC_ID_PRINTSIZE, EXC_PRINTSIZE_FULL ).Save( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepivot.cxx b/sc/source/filter/excel/xepivot.cxx
new file mode 100644
index 0000000000..a8aab5b694
--- /dev/null
+++ b/sc/source/filter/excel/xepivot.cxx
@@ -0,0 +1,1702 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xepivot.hxx>
+#include <xehelper.hxx>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+
+#include <algorithm>
+#include <math.h>
+#include <string_view>
+
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <document.hxx>
+#include <dpcache.hxx>
+#include <dpgroup.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpshttab.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xestring.hxx>
+#include <xelink.hxx>
+#include <dputil.hxx>
+#include <generalfunction.hxx>
+#include <svl/numformat.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
+using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+using ::com::sun::star::sheet::DataPilotFieldReference;
+
+// Pivot cache
+
+namespace {
+
+// constants to track occurrence of specific data types
+const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
+const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
+const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
+const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
+
+/** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
+const sal_uInt16 spnPCItemFlags[] =
+{ // STR DBL INT DAT
+ EXC_SXFIELD_DATA_NONE,
+ EXC_SXFIELD_DATA_STR, // x
+ EXC_SXFIELD_DATA_INT, // x
+ EXC_SXFIELD_DATA_STR_INT, // x x
+ EXC_SXFIELD_DATA_DBL, // x
+ EXC_SXFIELD_DATA_STR_DBL, // x x
+ EXC_SXFIELD_DATA_INT, // x x
+ EXC_SXFIELD_DATA_STR_INT, // x x x
+ EXC_SXFIELD_DATA_DATE, // x
+ EXC_SXFIELD_DATA_DATE_STR, // x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x
+ EXC_SXFIELD_DATA_DATE_STR, // x x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x
+ EXC_SXFIELD_DATA_DATE_STR, // x x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x x
+ EXC_SXFIELD_DATA_DATE_STR // x x x x
+};
+
+} // namespace
+
+XclExpPCItem::XclExpPCItem( const OUString& rText ) :
+ XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
+ mnTypeFlag( EXC_PCITEM_DATA_STRING )
+{
+ if( !rText.isEmpty() )
+ SetText( rText );
+ else
+ SetEmpty();
+}
+
+XclExpPCItem::XclExpPCItem( double fValue, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXDOUBLE, 8 )
+{
+ SetDouble( fValue, rText );
+ mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
+ EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
+}
+
+XclExpPCItem::XclExpPCItem( const DateTime& rDateTime, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXDATETIME, 8 )
+{
+ SetDateTime( rDateTime, rText );
+ mnTypeFlag = EXC_PCITEM_DATA_DATE;
+}
+
+XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
+ XclExpRecord( EXC_ID_SXINTEGER, 2 ),
+ mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
+{
+ SetInteger( nValue );
+}
+
+XclExpPCItem::XclExpPCItem( bool bValue, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
+ mnTypeFlag( EXC_PCITEM_DATA_STRING )
+{
+ SetBool( bValue, rText );
+}
+
+bool XclExpPCItem::EqualsText( std::u16string_view rText ) const
+{
+ return rText.empty() ? IsEmpty() : (GetText() && (*GetText() == rText));
+}
+
+bool XclExpPCItem::EqualsDouble( double fValue ) const
+{
+ return GetDouble() && (*GetDouble() == fValue);
+}
+
+bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
+{
+ return GetDateTime() && (*GetDateTime() == rDateTime);
+}
+
+bool XclExpPCItem::EqualsBool( bool bValue ) const
+{
+ return GetBool() && (*GetBool() == bValue);
+}
+
+void XclExpPCItem::WriteBody( XclExpStream& rStrm )
+{
+ if( const OUString* pText = GetText() )
+ {
+ rStrm << XclExpString( *pText );
+ }
+ else if( const double* pfValue = GetDouble() )
+ {
+ rStrm << *pfValue;
+ }
+ else if( const sal_Int16* pnValue = GetInteger() )
+ {
+ rStrm << *pnValue;
+ }
+ else if( const DateTime* pDateTime = GetDateTime() )
+ {
+ sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
+ sal_uInt16 nMonth = pDateTime->GetMonth();
+ sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
+ sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
+ sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
+ sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
+ if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
+ rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
+ }
+ else if( const bool* pbValue = GetBool() )
+ {
+ rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
+ }
+ else
+ {
+ // nothing to do for SXEMPTY
+ OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
+ }
+}
+
+XclExpPCField::XclExpPCField(
+ const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScRange& rRange ) :
+ XclExpRecord( EXC_ID_SXFIELD ),
+ XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
+ XclExpRoot( rRoot ),
+ mnTypeFlags( 0 )
+{
+ // general settings for the standard field, insert all items from source range
+ InitStandardField( rRange );
+
+ // add special settings for inplace numeric grouping
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+ if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
+ {
+ if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
+ {
+ const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
+ const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
+ OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
+ "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
+
+ if( rNumInfo.mbEnable )
+ InitNumGroupField( rDPObj, rNumInfo );
+ else if( rDateInfo.mbEnable )
+ InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
+ }
+ }
+ }
+
+ // final settings (flags, item numbers)
+ Finalize();
+}
+
+XclExpPCField::XclExpPCField(
+ const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
+ XclExpRecord( EXC_ID_SXFIELD ),
+ XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
+ XclExpRoot( rRoot ),
+ mnTypeFlags( 0 )
+{
+ // add base field info (always using first base field, not predecessor of this field) ***
+ OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
+ "XclExpPCField::FillFromGroup - wrong base cache field" );
+ maFieldInfo.maName = rGroupDim.GetGroupDimName();
+ maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
+
+ // add standard group info or date group info
+ const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
+ if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
+ InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
+ else
+ InitStdGroupField( rBaseField, rGroupDim );
+
+ // final settings (flags, item numbers)
+ Finalize();
+}
+
+XclExpPCField::~XclExpPCField()
+{
+}
+
+void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
+{
+ OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
+ "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+ maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
+}
+
+sal_uInt16 XclExpPCField::GetItemCount() const
+{
+ return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
+}
+
+const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return GetVisItemList().GetRecord( nItemIdx );
+}
+
+sal_uInt16 XclExpPCField::GetItemIndex( std::u16string_view rItemName ) const
+{
+ const XclExpPCItemList& rItemList = GetVisItemList();
+ for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
+ if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
+ return static_cast< sal_uInt16 >( nPos );
+ return EXC_PC_NOITEM;
+}
+
+std::size_t XclExpPCField::GetIndexSize() const
+{
+ return Has16BitIndexes() ? 2 : 1;
+}
+
+void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
+{
+ // only standard fields write item indexes
+ if( nSrcRow < maIndexVec.size() )
+ {
+ sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
+ if( Has16BitIndexes() )
+ rStrm << nIndex;
+ else
+ rStrm << static_cast< sal_uInt8 >( nIndex );
+ }
+}
+
+void XclExpPCField::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
+ // SXFIELD
+ XclExpRecord::Save( rStrm );
+ // SXFDBTYPE
+ XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
+ // list of grouping items
+ maGroupItemList.Save( rStrm );
+ // SXGROUPINFO
+ WriteSxgroupinfo( rStrm );
+ // SXNUMGROUP and additional grouping items (grouping limit settings)
+ WriteSxnumgroup( rStrm );
+ // list of original items
+ maOrigItemList.Save( rStrm );
+}
+
+// private --------------------------------------------------------------------
+
+const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
+{
+ OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
+ "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
+ return IsStandardField() ? maOrigItemList : maGroupItemList;
+}
+
+void XclExpPCField::InitStandardField( const ScRange& rRange )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
+ OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
+
+ ScDocument& rDoc = GetDoc();
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // field name is in top cell of the range
+ ScAddress aPos( rRange.aStart );
+ maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
+ // #i76047# maximum field name length in pivot cache is 255
+ if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
+ maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
+
+ // loop over all cells, create pivot cache items
+ for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
+ {
+ OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
+ if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
+ {
+ double fValue = rDoc.GetValue( aPos );
+ SvNumFormatType nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( rDoc.GetNonThreadedContext(), aPos ) );
+ if( nFmtType == SvNumFormatType::LOGICAL )
+ InsertOrigBoolItem( fValue != 0, aText );
+ else if( nFmtType & SvNumFormatType::DATETIME )
+ InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ), aText );
+ else
+ InsertOrigDoubleItem( fValue, aText );
+ }
+ else
+ {
+ InsertOrigTextItem( aText );
+ }
+ }
+}
+
+void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
+{
+ OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
+
+ maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
+ maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
+
+ // loop over all groups of this field
+ for( tools::Long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
+ {
+ const ScDPSaveGroupItem& rGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx );
+ // the index of the new item containing the grouping name
+ sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
+ // loop over all elements of one group
+ for( size_t nElemIdx = 0, nElemCount = rGroupItem.GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
+ {
+ if (const OUString* pElemName = rGroupItem.GetElementByIndex(nElemIdx))
+ {
+ // try to find the item that is part of the group in the base field
+ sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
+ if( nBaseItemIdx < maFieldInfo.mnBaseItems )
+ {
+ // add group name item only if there are any valid base items
+ if( nGroupItemIdx == EXC_PC_NOITEM )
+ nGroupItemIdx = InsertGroupItem( new XclExpPCItem( rGroupItem.GetGroupName() ) );
+ maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
+ }
+ }
+ }
+ }
+
+ // add items and base item indexes of all ungrouped elements
+ for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
+ // items that are not part of a group still have the EXC_PC_NOITEM entry
+ if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
+ // try to find the base item
+ if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
+ // create a clone of the base item, insert its index into item order list
+ maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
+}
+
+void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
+ OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
+
+ // new field type, date type, limit settings (min/max/step/auto)
+ if( rNumInfo.mbDateValues )
+ {
+ // special case: group by days with step count
+ meFieldType = EXC_PCFIELD_DATEGROUP;
+ maNumGroupInfo.SetScDateType( css::sheet::DataPilotFieldGroupBy::DAYS );
+ SetDateGroupLimit( rNumInfo, true );
+ }
+ else
+ {
+ meFieldType = EXC_PCFIELD_NUMGROUP;
+ maNumGroupInfo.SetNumType();
+ SetNumGroupLimit( rNumInfo );
+ }
+
+ // generate visible items
+ InsertNumDateGroupItems( rDPObj, rNumInfo );
+}
+
+void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
+{
+ OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
+ OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
+
+ // new field type
+ meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
+
+ // date type, limit settings (min/max/step/auto)
+ maNumGroupInfo.SetScDateType( nDatePart );
+ SetDateGroupLimit( rDateInfo, false );
+
+ // generate visible items
+ InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
+}
+
+void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
+ maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
+}
+
+void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
+{
+ size_t nItemIdx = maOrigItemList.GetSize();
+ maOrigItemList.AppendNewRecord( pNewItem );
+ InsertItemArrayIndex( nItemIdx );
+ mnTypeFlags |= pNewItem->GetTypeFlag();
+}
+
+void XclExpPCField::InsertOrigTextItem( const OUString& aText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ // #i76047# maximum item text length in pivot cache is 255
+ OUString aShortText = aText.copy( 0, ::std::min(aText.getLength(), EXC_PC_MAXSTRLEN ) );
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( aShortText ) );
+}
+
+void XclExpPCField::InsertOrigDoubleItem( double fValue, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( fValue, rText ) );
+}
+
+void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( rDateTime, rText ) );
+}
+
+void XclExpPCField::InsertOrigBoolItem( bool bValue, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( bValue, rText ) );
+}
+
+sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
+{
+ maGroupItemList.AppendNewRecord( pNewItem );
+ return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
+}
+
+void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
+{
+ OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
+ const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
+ if(!pSrcDesc)
+ return;
+
+ // get the string collection with original source elements
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ const ScDPDimensionSaveData* pDimData = nullptr;
+ if (pSaveData)
+ pDimData = pSaveData->GetExistingDimensionData();
+
+ const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
+ if (!pCache)
+ return;
+
+ ScSheetDPData aDPData(&GetDoc(), *pSrcDesc, *pCache);
+ tools::Long nDim = GetFieldIndex();
+ // get the string collection with generated grouping elements
+ ScDPNumGroupDimension aTmpDim( rNumInfo );
+ if( nDatePart != 0 )
+ aTmpDim.SetDateDimension();
+ const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
+ static_cast<SCCOL>(nDim), pCache);
+ for (SCROW nMemberId : aMemberIds)
+ {
+ const ScDPItemData* pData = aDPData.GetMemberById(nDim, nMemberId);
+ if ( pData )
+ {
+ OUString aStr = pCache->GetFormattedString(nDim, *pData, false);
+ InsertGroupItem(new XclExpPCItem(aStr));
+ }
+ }
+}
+
+void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
+{
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.mbAutoStart );
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.mbAutoEnd );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStart ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfEnd ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStep ) );
+}
+
+void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
+{
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.mbAutoStart );
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.mbAutoEnd );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfStart ) ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfEnd ) ) );
+ sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.mfStep, 1, SAL_MAX_INT16 ) : 1;
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
+}
+
+void XclExpPCField::Finalize()
+{
+ // flags
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
+ // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
+ /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
+ for the current combination of item types is added to the flags. */
+ ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
+
+ // item count fields
+ maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
+ maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
+ // maFieldInfo.mnBaseItems set in InitStdGroupField()
+ maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
+}
+
+void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
+{
+ if( IsNumGroupField() || IsDateGroupField() )
+ {
+ // SXNUMGROUP record
+ rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
+ rStrm << maNumGroupInfo;
+ rStrm.EndRecord();
+
+ // limits (min/max/step) for numeric grouping
+ OSL_ENSURE( maNumGroupLimits.GetSize() == 3,
+ "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
+ maNumGroupLimits.Save( rStrm );
+ }
+}
+
+void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
+{
+ OSL_ENSURE( IsStdGroupField() != maGroupOrder.empty(),
+ "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
+ if( IsStdGroupField() && !maGroupOrder.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
+ for( const auto& rItem : maGroupOrder )
+ rStrm << rItem;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPCField::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maFieldInfo;
+}
+
+XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
+ XclExpRoot( rRoot ),
+ mnListIdx( nListIdx ),
+ mbValid( false )
+{
+ // source from sheet only
+ const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
+ if(!pSrcDesc)
+ return;
+
+ /* maOrigSrcRange: Range received from the DataPilot object.
+ maExpSrcRange: Range written to the DCONREF record.
+ maDocSrcRange: Range used to get source data from Calc document.
+ This range may be shorter than maExpSrcRange to improve export
+ performance (#i22541#). */
+ maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
+ maSrcRangeName = pSrcDesc->GetRangeName();
+
+ // internal sheet data only
+ SCTAB nScTab = maExpSrcRange.aStart.Tab();
+ if( !((nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab )) )
+ return;
+
+ // ValidateRange() restricts source range to valid Excel limits
+ if( !GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
+ return;
+
+ // #i22541# skip empty cell areas (performance)
+ SCCOL nDocCol1, nDocCol2;
+ SCROW nDocRow1, nDocRow2;
+ GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
+ GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
+ SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
+ SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
+ SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
+ SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
+
+ // #i22541# do not store index list for too big ranges
+ if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
+ ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
+
+ // adjust row indexes, keep one row of empty area to surely have the empty cache item
+ if( nSrcRow1 < nDocRow1 )
+ nSrcRow1 = nDocRow1 - 1;
+ if( nSrcRow2 > nDocRow2 )
+ nSrcRow2 = nDocRow2 + 1;
+
+ maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
+ maDocSrcRange.aStart.SetRow( nSrcRow1 );
+ maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
+ maDocSrcRange.aEnd.SetRow( nSrcRow2 );
+
+ GetDoc().GetName( nScTab, maTabName );
+ maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
+ maPCInfo.mnStrmId = nListIdx + 1;
+ maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
+
+ AddFields( rDPObj );
+
+ mbValid = true;
+}
+
+bool XclExpPivotCache::HasItemIndexList() const
+{
+ return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
+}
+
+sal_uInt16 XclExpPivotCache::GetFieldCount() const
+{
+ return static_cast< sal_uInt16 >( maFieldList.GetSize() );
+}
+
+const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return maFieldList.GetRecord( nFieldIdx );
+}
+
+bool XclExpPivotCache::HasAddFields() const
+{
+ // pivot cache can be shared, if there are no additional cache fields
+ return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
+}
+
+bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
+{
+ /* For now, only sheet sources are supported, therefore it is enough to
+ compare the ScSheetSourceDesc. Later, there should be done more complicated
+ comparisons regarding the source type of rDPObj and this cache. */
+ if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
+ return pSrcDesc->GetSourceRange() == maOrigSrcRange;
+ return false;
+}
+
+void XclExpPivotCache::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
+ // SXIDSTM
+ XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
+ // SXVS
+ XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
+
+ if (!maSrcRangeName.isEmpty())
+ // DCONNAME
+ WriteDConName(rStrm);
+ else
+ // DCONREF
+ WriteDconref(rStrm);
+
+ // create the pivot cache storage stream
+ WriteCacheStream();
+}
+
+void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
+{
+ AddStdFields( rDPObj );
+ maPCInfo.mnStdFields = GetFieldCount();
+ AddGroupFields( rDPObj );
+ maPCInfo.mnTotalFields = GetFieldCount();
+};
+
+void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
+{
+ // if item index list is not written, used shortened source range (maDocSrcRange) for performance
+ const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
+ // create a standard pivot cache field for each source column
+ for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
+ {
+ ScRange aColRange( rRange );
+ aColRange.aStart.SetCol( nScCol );
+ aColRange.aEnd.SetCol( nScCol );
+ maFieldList.AppendNewRecord( new XclExpPCField(
+ GetRoot(), GetFieldCount(), rDPObj, aColRange ) );
+ }
+}
+
+void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
+{
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ if(!pSaveData)
+ return;
+ const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData();
+ if( !pSaveDimData )
+ return;
+
+ // loop over all existing standard fields to find their group fields
+ for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
+ {
+ if( XclExpPCField* pCurrStdField = maFieldList.GetRecord( nFieldIdx ) )
+ {
+ const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
+ XclExpPCField* pLastGroupField = pCurrStdField;
+ while( pGroupDim )
+ {
+ // insert the new grouping field
+ XclExpPCFieldRef xNewGroupField = new XclExpPCField(
+ GetRoot(), GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField );
+ maFieldList.AppendRecord( xNewGroupField );
+
+ // register new grouping field at current grouping field, building a chain
+ pLastGroupField->SetGroupChildField( *xNewGroupField );
+
+ // next grouping dimension
+ pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
+ pLastGroupField = xNewGroupField.get();
+ }
+ }
+ }
+}
+
+void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
+{
+ XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), u"", &maTabName ) );
+ rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
+ rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
+ << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
+ << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
+ << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
+ << aRef
+ << sal_uInt8( 0 );
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
+{
+ XclExpString aName(maSrcRangeName);
+ rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
+ rStrm << aName << sal_uInt16(0);
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteCacheStream()
+{
+ tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
+ if( !xSvStrm.is() )
+ return;
+
+ XclExpStream aStrm( *xSvStrm, GetRoot() );
+ // SXDB
+ WriteSxdb( aStrm );
+ // SXDBEX
+ WriteSxdbex( aStrm );
+ // field list (SXFIELD and items)
+ maFieldList.Save( aStrm );
+ // index table (list of SXINDEXLIST)
+ WriteSxindexlistList( aStrm );
+ // EOF
+ XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
+}
+
+void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXDB, 21 );
+ rStrm << maPCInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
+ rStrm << EXC_SXDBEX_CREATION_DATE
+ << sal_uInt32( 0 ); // number of SXFORMULA records
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
+{
+ if( !HasItemIndexList() )
+ return;
+
+ std::size_t nRecSize = 0;
+ size_t nPos, nSize = maFieldList.GetSize();
+ for( nPos = 0; nPos < nSize; ++nPos )
+ nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
+
+ for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
+ {
+ rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
+ for( nPos = 0; nPos < nSize; ++nPos )
+ maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
+ rStrm.EndRecord();
+ }
+}
+
+// Pivot table
+
+namespace {
+
+/** Returns a display string for a data field containing the field name and aggregation function. */
+OUString lclGetDataFieldCaption( std::u16string_view rFieldName, ScGeneralFunction eFunc )
+{
+ OUString aCaption;
+
+ TranslateId pResIdx;
+ switch( eFunc )
+ {
+ case ScGeneralFunction::SUM: pResIdx = STR_FUN_TEXT_SUM; break;
+ case ScGeneralFunction::COUNT: pResIdx = STR_FUN_TEXT_COUNT; break;
+ case ScGeneralFunction::AVERAGE: pResIdx = STR_FUN_TEXT_AVG; break;
+ case ScGeneralFunction::MAX: pResIdx = STR_FUN_TEXT_MAX; break;
+ case ScGeneralFunction::MIN: pResIdx = STR_FUN_TEXT_MIN; break;
+ case ScGeneralFunction::PRODUCT: pResIdx = STR_FUN_TEXT_PRODUCT; break;
+ case ScGeneralFunction::COUNTNUMS: pResIdx = STR_FUN_TEXT_COUNT; break;
+ case ScGeneralFunction::STDEV: pResIdx = STR_FUN_TEXT_STDDEV; break;
+ case ScGeneralFunction::STDEVP: pResIdx = STR_FUN_TEXT_STDDEV; break;
+ case ScGeneralFunction::VAR: pResIdx = STR_FUN_TEXT_VAR; break;
+ case ScGeneralFunction::VARP: pResIdx = STR_FUN_TEXT_VAR; break;
+ default:;
+ }
+ if (pResIdx)
+ aCaption = ScResId(pResIdx) + " - ";
+ aCaption += rFieldName;
+ return aCaption;
+}
+
+} // namespace
+
+XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
+ XclExpRecord( EXC_ID_SXVI, 8 ),
+ mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
+{
+ maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
+ maItemInfo.mnCacheIdx = nCacheIdx;
+ maItemInfo.maVisName.mbUseCache = mpCacheItem != nullptr;
+}
+
+XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx ) :
+ XclExpRecord( EXC_ID_SXVI, 8 ),
+ mpCacheItem( nullptr )
+{
+ maItemInfo.mnType = nItemType;
+ maItemInfo.mnCacheIdx = nCacheIdx;
+ maItemInfo.maVisName.mbUseCache = true;
+}
+
+OUString XclExpPTItem::GetItemName() const
+{
+ return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
+}
+
+void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
+{
+ // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
+ // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
+
+ // visible name
+ const std::optional<OUString> & pVisName = rSaveMem.GetLayoutName();
+ if (pVisName && *pVisName != GetItemName())
+ maItemInfo.SetVisName(*pVisName);
+}
+
+void XclExpPTItem::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maItemInfo;
+}
+
+XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
+ mrPTable( rPTable ),
+ mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
+{
+ maFieldInfo.mnCacheIdx = nCacheIdx;
+
+ // create field items
+ if( mpCacheField )
+ for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
+ maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
+ maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
+}
+
+// data access ----------------------------------------------------------------
+
+OUString XclExpPTField::GetFieldName() const
+{
+ return mpCacheField ? mpCacheField->GetFieldName() : OUString();
+}
+
+sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
+{
+ OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
+ // will return 0xFFFF for empty vector -> ok
+ return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
+}
+
+sal_uInt16 XclExpPTField::GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const
+{
+ for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
+ if( maItemList.GetRecord( nPos )->GetItemName() == rName )
+ return static_cast< sal_uInt16 >( nPos );
+ return nDefaultIdx;
+}
+
+// fill data --------------------------------------------------------------
+
+/**
+ * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
+ * are not escaped at all.
+ */
+static OUString lcl_convertCalcSubtotalName(const OUString& rName)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rName.getStr();
+ sal_Int32 n = rName.getLength();
+ bool bEscaped = false;
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (!bEscaped && c == '\\')
+ {
+ bEscaped = true;
+ continue;
+ }
+
+ aBuf.append(c);
+ bEscaped = false;
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ // orientation
+ DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
+ OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
+ maFieldInfo.AddApiOrient( eOrient );
+
+ // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
+
+ // visible name
+ const std::optional<OUString> & pLayoutName = rSaveDim.GetLayoutName();
+ if (pLayoutName && *pLayoutName != GetFieldName())
+ maFieldInfo.SetVisName(*pLayoutName);
+
+ const std::optional<OUString> & pSubtotalName = rSaveDim.GetSubtotalName();
+ if (pSubtotalName)
+ {
+ OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
+ maFieldExtInfo.mpFieldTotalName = aSubName;
+ }
+
+ // subtotals
+ XclPTSubtotalVec aSubtotals;
+ aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
+ for( tools::Long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
+ aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
+ maFieldInfo.SetSubtotals( aSubtotals );
+
+ // sorting
+ if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
+ {
+ maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
+ if( pSortInfo->Mode == css::sheet::DataPilotFieldSortMode::DATA )
+ maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
+ }
+
+ // auto show
+ if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
+ {
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
+ maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
+ maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
+ maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
+ }
+
+ // layout
+ if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
+ {
+ maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
+ }
+
+ // special page field properties
+ if( eOrient == DataPilotFieldOrientation_PAGE )
+ {
+ maPageInfo.mnField = GetFieldIndex();
+ maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
+ }
+
+ // item properties
+ const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
+ for (const auto& pMember : rMembers)
+ if( XclExpPTItem* pItem = GetItemAcc( pMember->GetName() ) )
+ pItem->SetPropertiesFromMember( *pMember );
+}
+
+void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ maDataInfoVec.emplace_back( );
+ XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
+ rDataInfo.mnField = GetFieldIndex();
+
+ // orientation
+ maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
+
+ // aggregation function
+ ScGeneralFunction eFunc = rSaveDim.GetFunction();
+ rDataInfo.SetApiAggFunc( eFunc );
+
+ // visible name
+ const std::optional<OUString> & pVisName = rSaveDim.GetLayoutName();
+ if (pVisName)
+ rDataInfo.SetVisName(*pVisName);
+ else
+ rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
+
+ // result field reference
+ if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
+ {
+ rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
+ rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
+ if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
+ {
+ rDataInfo.mnRefField = pRefField->GetFieldIndex();
+ if( pFieldRef->ReferenceItemType == css::sheet::DataPilotFieldReferenceItemType::NAMED )
+ rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
+ }
+ }
+}
+
+void XclExpPTField::AppendSubtotalItems()
+{
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
+}
+
+// records --------------------------------------------------------------------
+
+void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
+{
+ rStrm << maPageInfo;
+}
+
+void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
+{
+ OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
+ if( nDataInfoIdx < maDataInfoVec.size() )
+ {
+ rStrm.StartRecord( EXC_ID_SXDI, 12 );
+ rStrm << maDataInfoVec[ nDataInfoIdx ];
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPTField::Save( XclExpStream& rStrm )
+{
+ // SXVD
+ WriteSxvd( rStrm );
+ // list of SXVI records
+ maItemList.Save( rStrm );
+ // SXVDEX
+ WriteSxvdex( rStrm );
+}
+
+// private --------------------------------------------------------------------
+
+XclExpPTItem* XclExpPTField::GetItemAcc( std::u16string_view rName )
+{
+ XclExpPTItem* pItem = nullptr;
+ for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
+ if( maItemList.GetRecord( nPos )->GetItemName() == rName )
+ pItem = maItemList.GetRecord( nPos );
+ return pItem;
+}
+
+void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
+{
+ maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE ) );
+ ++maFieldInfo.mnItemCount;
+}
+
+void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVD, 10 );
+ rStrm << maFieldInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
+ rStrm << maFieldExtInfo;
+ rStrm.EndRecord();
+}
+
+XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
+ XclExpRoot( rRoot ),
+ mrPCache( rPCache ),
+ maDataOrientField( *this, EXC_SXIVD_DATA ),
+ mnOutScTab( 0 ),
+ mbValid( false ),
+ mbFilterBtn( false )
+{
+ const ScRange& rOutScRange = rDPObj.GetOutRange();
+ if( !GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
+ return;
+
+ // DataPilot properties -----------------------------------------------
+
+ // pivot table properties from DP object
+ mnOutScTab = rOutScRange.aStart.Tab();
+ maPTInfo.maTableName = rDPObj.GetName();
+ maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
+
+ maPTViewEx9Info.Init( rDPObj );
+
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ if( !pSaveData )
+ return;
+
+ // additional properties from ScDPSaveData
+ SetPropertiesFromDP( *pSaveData );
+
+ // loop over all dimensions ---------------------------------------
+
+ /* 1) Default-construct all pivot table fields for all pivot cache fields. */
+ for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
+ maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
+
+ const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
+
+ /* 2) First process all data dimensions, they are needed for extended
+ settings of row/column/page fields (sorting/auto show). */
+ for (auto const& iter : rDimList)
+ {
+ if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
+ SetDataFieldPropertiesFromDim(*iter);
+ }
+
+ /* 3) Row/column/page/hidden fields. */
+ for (auto const& iter : rDimList)
+ {
+ if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
+ SetFieldPropertiesFromDim(*iter);
+ }
+
+ // Finalize -------------------------------------------------------
+
+ Finalize();
+ mbValid = true;
+}
+
+const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
+{
+ return mrPCache.GetField( nCacheIdx );
+}
+
+const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx );
+}
+
+const XclExpPTField* XclExpPivotTable::GetField( std::u16string_view rName ) const
+{
+ return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
+}
+
+sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
+{
+ auto aIt = std::find_if(maDataFields.begin(), maDataFields.end(),
+ [this, &rName](const XclPTDataFieldPos& rDataField) {
+ const XclExpPTField* pField = GetField( rDataField.first );
+ return pField && pField->GetFieldName() == rName;
+ });
+ if (aIt != maDataFields.end())
+ return static_cast< sal_uInt16 >( std::distance(maDataFields.begin(), aIt) );
+ return nDefaultIdx;
+}
+
+void XclExpPivotTable::Save( XclExpStream& rStrm )
+{
+ if( !mbValid )
+ return;
+
+ // SXVIEW
+ WriteSxview( rStrm );
+ // pivot table fields (SXVD, SXVDEX, and item records)
+ maFieldList.Save( rStrm );
+ // SXIVD records for row and column fields
+ WriteSxivd( rStrm, maRowFields );
+ WriteSxivd( rStrm, maColFields );
+ // SXPI
+ WriteSxpi( rStrm );
+ // list of SXDI records containing data field info
+ WriteSxdiList( rStrm );
+ // SXLI records
+ WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
+ WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
+ // SXEX
+ WriteSxex( rStrm );
+ // QSISXTAG
+ WriteQsiSxTag( rStrm );
+ // SXVIEWEX9
+ WriteSxViewEx9( rStrm );
+}
+
+XclExpPTField* XclExpPivotTable::GetFieldAcc( std::u16string_view rName )
+{
+ XclExpPTField* pField = nullptr;
+ for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
+ if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
+ pField = maFieldList.GetRecord( nPos );
+ return pField;
+}
+
+XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
+{
+ // data field orientation field?
+ if( rSaveDim.IsDataLayout() )
+ return &maDataOrientField;
+
+ // a real dimension
+ OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
+ return aFieldName.isEmpty() ? nullptr : GetFieldAcc(aFieldName);
+}
+
+// fill data --------------------------------------------------------------
+
+void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
+{
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
+ ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
+ mbFilterBtn = rSaveData.GetFilterButton();
+ const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
+
+ if (pDim && pDim->GetLayoutName())
+ maPTInfo.maDataName = *pDim->GetLayoutName();
+ else
+ maPTInfo.maDataName = ScResId(STR_PIVOT_DATA);
+}
+
+void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ XclExpPTField* pField = GetFieldAcc( rSaveDim );
+ if(!pField)
+ return;
+
+ // field properties
+ pField->SetPropertiesFromDim( rSaveDim );
+
+ // update the corresponding field position list
+ DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
+ sal_uInt16 nFieldIdx = pField->GetFieldIndex();
+ bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
+ bool bMultiData = maDataFields.size() > 1;
+
+ if( bDataLayout && !bMultiData )
+ return;
+
+ switch( eOrient )
+ {
+ case DataPilotFieldOrientation_ROW:
+ maRowFields.push_back( nFieldIdx );
+ if( bDataLayout )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
+ break;
+ case DataPilotFieldOrientation_COLUMN:
+ maColFields.push_back( nFieldIdx );
+ if( bDataLayout )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
+ break;
+ case DataPilotFieldOrientation_PAGE:
+ maPageFields.push_back( nFieldIdx );
+ OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
+ break;
+ case DataPilotFieldOrientation_DATA:
+ OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
+ break;
+ default:;
+ }
+}
+
+void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
+ {
+ // field properties
+ pField->SetDataPropertiesFromDim( rSaveDim );
+ // update the data field position list
+ maDataFields.emplace_back( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() );
+ }
+}
+
+void XclExpPivotTable::Finalize()
+{
+ // field numbers
+ maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
+ maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
+ maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
+ maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
+ maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
+
+ maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
+ maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
+
+ // subtotal items
+ for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
+ maFieldList.GetRecord( nPos )->AppendSubtotalItems();
+
+ // find data field orientation field
+ maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
+ const ScfUInt16Vec* pFieldVec = nullptr;
+ switch( maPTInfo.mnDataAxis )
+ {
+ case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
+ case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
+ }
+
+ if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
+ {
+ ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
+ if( aIt != pFieldVec->end() )
+ maPTInfo.mnDataPos = static_cast< sal_uInt16 >( std::distance(pFieldVec->begin(), aIt) );
+ }
+
+ // single data field is always row oriented
+ if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
+
+ // update output range (initialized in ctor)
+ sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
+ sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
+ sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
+ sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
+ // exclude page fields from output range
+ rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
+ // exclude filter button from output range
+ if( mbFilterBtn )
+ ++rnXclRow1;
+ // exclude empty row between (filter button and/or page fields) and table
+ if( mbFilterBtn || maPTInfo.mnPageFields )
+ ++rnXclRow1;
+
+ // data area
+ sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
+ sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
+ rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
+ rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
+ if( maDataFields.empty() )
+ ++rnDataXclRow;
+
+ bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
+ if (bExtraHeaderRow)
+ // Insert an extra row only when there is no column field.
+ ++rnDataXclRow;
+
+ rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
+ rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
+ maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
+ maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
+
+ // first heading
+ maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
+ if (bExtraHeaderRow)
+ maPTInfo.mnFirstHeadRow += 1;
+}
+
+// records ----------------------------------------------------------------
+
+void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
+ rStrm << maPTInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
+{
+ if( !rFields.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
+ for( const auto& rField : rFields )
+ rStrm << rField;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
+{
+ if( !maPageFields.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
+ rStrm.SetSliceSize( 6 );
+ for( const auto& rPageField : maPageFields )
+ {
+ XclExpPTFieldRef xField = maFieldList.GetRecord( rPageField );
+ if( xField )
+ xField->WriteSxpiEntry( rStrm );
+ }
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
+{
+ for( const auto& [rFieldIdx, rDataInfoIdx] : maDataFields )
+ {
+ XclExpPTFieldRef xField = maFieldList.GetRecord( rFieldIdx );
+ if( xField )
+ xField->WriteSxdi( rStrm, rDataInfoIdx );
+ }
+}
+
+void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
+{
+ if( nLineCount <= 0 )
+ return;
+
+ std::size_t nLineSize = 8 + 2 * nIndexCount;
+ rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
+
+ /* Excel expects the records to be filled completely, do not
+ set a segment size... */
+// rStrm.SetSliceSize( nLineSize );
+
+ for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ // Excel XP needs a partly initialized SXLI record
+ rStrm << sal_uInt16( 0 ) // number of equal index entries
+ << EXC_SXVI_TYPE_DATA
+ << nIndexCount
+ << EXC_SXLI_DEFAULTFLAGS;
+ rStrm.WriteZeroBytes( 2 * nIndexCount );
+ }
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXEX, 24 );
+ rStrm << maPTExtInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( 0x0802, 32 );
+
+ sal_uInt16 const nRecordType = 0x0802;
+ sal_uInt16 const nDummyFlags = 0x0000;
+ sal_uInt16 const nTableType = 1; // 0 = query table : 1 = pivot table
+
+ rStrm << nRecordType << nDummyFlags << nTableType;
+
+ // General flags
+ sal_uInt16 const nFlags = 0x0001;
+#if 0
+ // for doc purpose
+ sal_uInt16 nFlags = 0x0000;
+ bool bEnableRefresh = true;
+ bool bPCacheInvalid = false;
+ bool bOlapPTReport = false;
+
+ if (bEnableRefresh) nFlags |= 0x0001;
+ if (bPCacheInvalid) nFlags |= 0x0002;
+ if (bOlapPTReport) nFlags |= 0x0004;
+#endif
+ rStrm << nFlags;
+
+ // Feature-specific options. The value differs depending on the table
+ // type, but we assume the table type is always pivot table.
+ sal_uInt32 const nOptions = 0x00000000;
+#if 0
+ // documentation for which bit is for what
+ bool bNoStencil = false;
+ bool bHideTotal = false;
+ bool bEmptyRows = false;
+ bool bEmptyCols = false;
+ if (bNoStencil) nOptions |= 0x00000001;
+ if (bHideTotal) nOptions |= 0x00000002;
+ if (bEmptyRows) nOptions |= 0x00000008;
+ if (bEmptyCols) nOptions |= 0x00000010;
+#endif
+ rStrm << nOptions;
+
+ sal_uInt8 eXclVer = 0; // Excel2000
+ sal_uInt8 const nOffsetBytes = 16;
+ rStrm << eXclVer // version table last refreshed
+ << eXclVer // minimum version to refresh
+ << nOffsetBytes
+ << eXclVer; // first version created
+
+ rStrm << XclExpString(maPTInfo.maTableName);
+ rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
+
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
+{
+ // Until we sync the autoformat ids only export if using grid header layout
+ // That could only have been set via xls import so far.
+ if ( 0 == maPTViewEx9Info.mnGridLayout )
+ {
+ rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
+ rStrm << maPTViewEx9Info;
+ rStrm.EndRecord();
+ }
+}
+
+namespace {
+
+const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
+
+/** Record wrapper class to write the pivot caches or pivot tables. */
+class XclExpPivotRecWrapper : public XclExpRecordBase
+{
+public:
+ explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
+ virtual void Save( XclExpStream& rStrm ) override;
+private:
+ XclExpPivotTableManager& mrPTMgr;
+ SCTAB mnScTab;
+};
+
+XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
+ mrPTMgr( rPTMgr ),
+ mnScTab( nScTab )
+{
+}
+
+void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
+{
+ if( mnScTab == EXC_PTMGR_PIVOTCACHES )
+ mrPTMgr.WritePivotCaches( rStrm );
+ else
+ mrPTMgr.WritePivotTables( rStrm, mnScTab );
+}
+
+} // namespace
+
+XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpPivotTableManager::CreatePivotTables()
+{
+ if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
+ for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
+ {
+ ScDPObject& rDPObj = (*pDPColl)[ nDPObj ];
+ if( const XclExpPivotCache* pPCache = CreatePivotCache( rDPObj ) )
+ maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), rDPObj, *pPCache ) );
+ }
+}
+
+XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
+{
+ return new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES );
+}
+
+XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
+{
+ return new XclExpPivotRecWrapper( *this, nScTab );
+}
+
+void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
+{
+ maPCacheList.Save( rStrm );
+}
+
+void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
+{
+ for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
+ if( xPTable->GetScTab() == nScTab )
+ xPTable->Save( rStrm );
+ }
+}
+
+const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
+{
+ // try to find a pivot cache with the same data source
+ /* #i25110# In Excel, the pivot cache contains additional fields
+ (i.e. grouping info, calculated fields). If the passed DataPilot object
+ or the found cache contains this data, do not share the cache with
+ multiple pivot tables. */
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+ const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
+ // no dimension save data at all or save data does not contain grouping info
+ if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
+ {
+ // check all existing pivot caches
+ for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpPivotCache* pPCache = maPCacheList.GetRecord( nPos );
+ // pivot cache does not have grouping info and source data is equal
+ if( !pPCache->HasAddFields() && pPCache->HasEqualDataSource( rDPObj ) )
+ return pPCache;
+ }
+ }
+ }
+
+ // create a new pivot cache
+ sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
+ XclExpPivotCacheRef xNewPCache = new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx );
+ if( xNewPCache->IsValid() )
+ {
+ maPCacheList.AppendRecord( xNewPCache.get() );
+ return xNewPCache.get();
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepivotxml.cxx b/sc/source/filter/excel/xepivotxml.cxx
new file mode 100644
index 0000000000..fc8b118eae
--- /dev/null
+++ b/sc/source/filter/excel/xepivotxml.cxx
@@ -0,0 +1,1210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <xepivotxml.hxx>
+#include <dpcache.hxx>
+#include <dpdimsave.hxx>
+#include <dpitemdata.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dputil.hxx>
+#include <document.hxx>
+#include <generalfunction.hxx>
+#include <unonames.hxx>
+#include <xestyle.hxx>
+#include <xeroot.hxx>
+
+#include <o3tl/temporary.hxx>
+#include <o3tl/safeint.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <sal/log.hxx>
+#include <sax/tools/converter.hxx>
+#include <sax/fastattribs.hxx>
+#include <svl/numformat.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+
+#include <vector>
+
+using namespace oox;
+using namespace com::sun::star;
+
+namespace {
+
+void savePivotCacheRecordsXml( XclExpXmlStream& rStrm, const ScDPCache& rCache )
+{
+ SCROW nCount = rCache.GetDataSize();
+ size_t nFieldCount = rCache.GetFieldCount();
+
+ sax_fastparser::FSHelperPtr& pRecStrm = rStrm.GetCurrentStream();
+ pRecStrm->startElement(XML_pivotCacheRecords,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ XML_count, OString::number(static_cast<tools::Long>(nCount)));
+
+ for (SCROW i = 0; i < nCount; ++i)
+ {
+ pRecStrm->startElement(XML_r);
+ for (size_t nField = 0; nField < nFieldCount; ++nField)
+ {
+ const ScDPCache::IndexArrayType* pArray = rCache.GetFieldIndexArray(nField);
+ assert(pArray);
+ assert(o3tl::make_unsigned(i) < pArray->size());
+
+ // We are using XML_x reference (like: <x v="0"/>), instead of values here (eg: <s v="No Discount"/>).
+ // That's why in SavePivotCacheXml method, we need to list all items.
+ pRecStrm->singleElement(XML_x, XML_v, OString::number((*pArray)[i]));
+ }
+ pRecStrm->endElement(XML_r);
+ }
+
+ pRecStrm->endElement(XML_pivotCacheRecords);
+}
+
+const char* toOOXMLAxisType( sheet::DataPilotFieldOrientation eOrient )
+{
+ switch (eOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ return "axisCol";
+ case sheet::DataPilotFieldOrientation_ROW:
+ return "axisRow";
+ case sheet::DataPilotFieldOrientation_PAGE:
+ return "axisPage";
+ case sheet::DataPilotFieldOrientation_DATA:
+ return "axisValues";
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ default:
+ ;
+ }
+
+ return "";
+}
+
+const char* toOOXMLSubtotalType(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM:
+ return "sum";
+ case ScGeneralFunction::COUNT:
+ return "count";
+ case ScGeneralFunction::AVERAGE:
+ return "average";
+ case ScGeneralFunction::MAX:
+ return "max";
+ case ScGeneralFunction::MIN:
+ return "min";
+ case ScGeneralFunction::PRODUCT:
+ return "product";
+ case ScGeneralFunction::COUNTNUMS:
+ return "countNums";
+ case ScGeneralFunction::STDEV:
+ return "stdDev";
+ case ScGeneralFunction::STDEVP:
+ return "stdDevp";
+ case ScGeneralFunction::VAR:
+ return "var";
+ case ScGeneralFunction::VARP:
+ return "varp";
+ default:
+ ;
+ }
+ return nullptr;
+}
+
+}
+
+XclExpXmlPivotCaches::XclExpXmlPivotCaches( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot) {}
+
+void XclExpXmlPivotCaches::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pWorkbookStrm = rStrm.GetCurrentStream();
+ pWorkbookStrm->startElement(XML_pivotCaches);
+
+ for (size_t i = 0, n = maCaches.size(); i < n; ++i)
+ {
+ const Entry& rEntry = maCaches[i];
+
+ sal_Int32 nCacheId = i + 1;
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pPCStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheDefinition", nCacheId),
+ XclXmlUtils::GetStreamName(nullptr, "pivotCache/pivotCacheDefinition", nCacheId),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotCacheDefinition"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
+ &aRelId);
+
+ pWorkbookStrm->singleElement(XML_pivotCache,
+ XML_cacheId, OString::number(nCacheId),
+ FSNS(XML_r, XML_id), aRelId.toUtf8());
+
+ rStrm.PushStream(pPCStrm);
+ SavePivotCacheXml(rStrm, rEntry, nCacheId);
+ rStrm.PopStream();
+ }
+
+ pWorkbookStrm->endElement(XML_pivotCaches);
+}
+
+void XclExpXmlPivotCaches::SetCaches( std::vector<Entry>&& rCaches )
+{
+ maCaches = std::move(rCaches);
+}
+
+bool XclExpXmlPivotCaches::HasCaches() const
+{
+ return !maCaches.empty();
+}
+
+const XclExpXmlPivotCaches::Entry* XclExpXmlPivotCaches::GetCache( sal_Int32 nCacheId ) const
+{
+ if (nCacheId <= 0)
+ // cache ID is 1-based.
+ return nullptr;
+
+ size_t nPos = nCacheId - 1;
+ if (nPos >= maCaches.size())
+ return nullptr;
+
+ return &maCaches[nPos];
+}
+
+namespace {
+/**
+ * Create combined date and time string according the requirements of Excel.
+ * A single point in time can be represented by concatenating a complete date expression,
+ * the letter T as a delimiter, and a valid time expression. For example, "2007-04-05T14:30".
+ *
+ * fSerialDateTime - a number representing the number of days since 1900-Jan-0 (integer portion of the number),
+ * plus a fractional portion of a 24 hour day (fractional portion of the number).
+ */
+OUString GetExcelFormattedDate( double fSerialDateTime, const SvNumberFormatter& rFormatter )
+{
+ // tdf#125055: properly round the value to seconds when truncating nanoseconds below
+ constexpr double fHalfSecond = 1 / 86400.0 * 0.5;
+ css::util::DateTime aUDateTime
+ = (DateTime(rFormatter.GetNullDate()) + fSerialDateTime + fHalfSecond).GetUNODateTime();
+ // We need to reset nanoseconds, to avoid string like: "1982-02-18T16:04:47.999999849"
+ aUDateTime.NanoSeconds = 0;
+ OUStringBuffer sBuf;
+ ::sax::Converter::convertDateTime(sBuf, aUDateTime, nullptr, true);
+ return sBuf.makeStringAndClear();
+}
+
+// Excel seems to expect different order of group item values; we need to rearrange elements
+// to output "<date1" first, then elements, then ">date2" last.
+// Since ScDPItemData::DateFirst is -1, ScDPItemData::DateLast is 10000, and other date group
+// items would fit between those in order (like 0 = Jan, 1 = Feb, etc.), we can simply sort
+// the items by value.
+std::vector<OUString> SortGroupItems(const ScDPCache& rCache, tools::Long nDim)
+{
+ struct ItemData
+ {
+ sal_Int32 nVal;
+ const ScDPItemData* pData;
+ };
+ std::vector<ItemData> aDataToSort;
+ ScfInt32Vec aGIIds;
+ rCache.GetGroupDimMemberIds(nDim, aGIIds);
+ for (sal_Int32 id : aGIIds)
+ {
+ const ScDPItemData* pGIData = rCache.GetItemDataById(nDim, id);
+ if (pGIData->GetType() == ScDPItemData::GroupValue)
+ {
+ auto aGroupVal = pGIData->GetGroupValue();
+ aDataToSort.push_back({ aGroupVal.mnValue, pGIData });
+ }
+ }
+ std::sort(aDataToSort.begin(), aDataToSort.end(),
+ [](const ItemData& a, const ItemData& b) { return a.nVal < b.nVal; });
+
+ std::vector<OUString> aSortedResult;
+ for (const auto& el : aDataToSort)
+ {
+ aSortedResult.push_back(rCache.GetFormattedString(nDim, *el.pData, false));
+ }
+ return aSortedResult;
+}
+} // namespace
+
+void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCounter )
+{
+ assert(rEntry.mpCache);
+ const ScDPCache& rCache = *rEntry.mpCache;
+
+ sax_fastparser::FSHelperPtr& pDefStrm = rStrm.GetCurrentStream();
+
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pRecStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheRecords", nCounter),
+ XclXmlUtils::GetStreamName(nullptr, "pivotCacheRecords", nCounter),
+ pDefStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotCacheRecords"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheRecords"),
+ &aRelId);
+
+ rStrm.PushStream(pRecStrm);
+ savePivotCacheRecordsXml(rStrm, rCache);
+ rStrm.PopStream();
+
+ pDefStrm->startElement(XML_pivotCacheDefinition,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_r, XML_id), aRelId.toUtf8(),
+ XML_recordCount, OString::number(rEntry.mpCache->GetDataSize()),
+ XML_createdVersion, "3"); // MS Excel 2007, tdf#112936: setting version number makes MSO to handle the pivot table differently
+
+ pDefStrm->startElement(XML_cacheSource, XML_type, "worksheet");
+
+ OUString aSheetName;
+ GetDoc().GetName(rEntry.maSrcRange.aStart.Tab(), aSheetName);
+ pDefStrm->singleElement(XML_worksheetSource,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rEntry.maSrcRange),
+ XML_sheet, aSheetName.toUtf8());
+
+ pDefStrm->endElement(XML_cacheSource);
+
+ size_t nCount = rCache.GetFieldCount();
+ const size_t nGroupFieldCount = rCache.GetGroupFieldCount();
+ pDefStrm->startElement(XML_cacheFields,
+ XML_count, OString::number(static_cast<tools::Long>(nCount + nGroupFieldCount)));
+
+ auto WriteFieldGroup = [this, &rCache, pDefStrm](size_t i, size_t base) {
+ const sal_Int32 nDatePart = rCache.GetGroupType(i);
+ if (!nDatePart)
+ return;
+ OString sGroupBy;
+ switch (nDatePart)
+ {
+ case sheet::DataPilotFieldGroupBy::SECONDS:
+ sGroupBy = "seconds"_ostr;
+ break;
+ case sheet::DataPilotFieldGroupBy::MINUTES:
+ sGroupBy = "minutes"_ostr;
+ break;
+ case sheet::DataPilotFieldGroupBy::HOURS:
+ sGroupBy = "hours"_ostr;
+ break;
+ case sheet::DataPilotFieldGroupBy::DAYS:
+ sGroupBy = "days"_ostr;
+ break;
+ case sheet::DataPilotFieldGroupBy::MONTHS:
+ sGroupBy = "months"_ostr;
+ break;
+ case sheet::DataPilotFieldGroupBy::QUARTERS:
+ sGroupBy = "quarters"_ostr;
+ break;
+ case sheet::DataPilotFieldGroupBy::YEARS:
+ sGroupBy = "years"_ostr;
+ break;
+ }
+
+ // fieldGroup element
+ pDefStrm->startElement(XML_fieldGroup, XML_base, OString::number(base));
+
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // rangePr element
+ const ScDPNumGroupInfo* pGI = rCache.GetNumGroupInfo(i);
+ auto pGroupAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pGroupAttList->add(XML_groupBy, sGroupBy);
+ // Possible TODO: find out when to write autoStart attribute for years grouping
+ pGroupAttList->add(XML_startDate, GetExcelFormattedDate(pGI->mfStart, rFormatter).toUtf8());
+ pGroupAttList->add(XML_endDate, GetExcelFormattedDate(pGI->mfEnd, rFormatter).toUtf8());
+ if (pGI->mfStep)
+ pGroupAttList->add(XML_groupInterval, OString::number(pGI->mfStep));
+ pDefStrm->singleElement(XML_rangePr, pGroupAttList);
+
+ // groupItems element
+ auto aElemVec = SortGroupItems(rCache, i);
+ pDefStrm->startElement(XML_groupItems, XML_count, OString::number(aElemVec.size()));
+ for (const auto& sElem : aElemVec)
+ {
+ pDefStrm->singleElement(XML_s, XML_v, sElem.toUtf8());
+ }
+ pDefStrm->endElement(XML_groupItems);
+ pDefStrm->endElement(XML_fieldGroup);
+ };
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ OUString aName = rCache.GetDimensionName(i);
+
+ pDefStrm->startElement(XML_cacheField,
+ XML_name, aName.toUtf8(),
+ XML_numFmtId, OString::number(0));
+
+ const ScDPCache::ScDPItemDataVec& rFieldItems = rCache.GetDimMemberValues(i);
+
+ std::set<ScDPItemData::Type> aDPTypes;
+ double fMin = std::numeric_limits<double>::infinity(), fMax = -std::numeric_limits<double>::infinity();
+ bool isValueInteger = true;
+ bool isContainsDate = rCache.IsDateDimension(i);
+ bool isLongText = false;
+ for (const auto& rFieldItem : rFieldItems)
+ {
+ ScDPItemData::Type eType = rFieldItem.GetType();
+ // tdf#123939 : error and string are same for cache; if both are present, keep only one
+ if (eType == ScDPItemData::Error)
+ eType = ScDPItemData::String;
+ aDPTypes.insert(eType);
+ if (eType == ScDPItemData::Value)
+ {
+ double fVal = rFieldItem.GetValue();
+ fMin = std::min(fMin, fVal);
+ fMax = std::max(fMax, fVal);
+
+ // Check if all values are integers
+ if (isValueInteger && (modf(fVal, &o3tl::temporary(double())) != 0.0))
+ {
+ isValueInteger = false;
+ }
+ }
+ else if (eType == ScDPItemData::String && !isLongText)
+ {
+ isLongText = rFieldItem.GetString().getLength() > 255;
+ }
+ }
+
+ auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ // TODO In same cases, disable listing of items, as it is done in MS Excel.
+ // Exporting savePivotCacheRecordsXml method needs to be updated accordingly
+ //bool bListItems = true;
+
+ std::set<ScDPItemData::Type> aDPTypesWithoutBlank = aDPTypes;
+ aDPTypesWithoutBlank.erase(ScDPItemData::Empty);
+
+ const bool isContainsString = aDPTypesWithoutBlank.count(ScDPItemData::String) > 0;
+ const bool isContainsBlank = aDPTypes.count(ScDPItemData::Empty) > 0;
+ const bool isContainsNumber
+ = !isContainsDate && aDPTypesWithoutBlank.count(ScDPItemData::Value) > 0;
+ bool isContainsNonDate = !(isContainsDate && aDPTypesWithoutBlank.size() <= 1);
+
+ // XML_containsSemiMixedTypes possible values:
+ // 1 - (Default) at least one text value, or can also contain a mix of other data types and blank values,
+ // or blank values only
+ // 0 - the field does not have a mix of text and other values
+ if (!(isContainsString || (aDPTypes.size() > 1) || (isContainsBlank && aDPTypesWithoutBlank.empty())))
+ pAttList->add(XML_containsSemiMixedTypes, ToPsz10(false));
+
+ if (!isContainsNonDate)
+ pAttList->add(XML_containsNonDate, ToPsz10(false));
+
+ if (isContainsDate)
+ pAttList->add(XML_containsDate, ToPsz10(true));
+
+ // default for containsString field is true, so we are writing only when is false
+ if (!isContainsString)
+ pAttList->add(XML_containsString, ToPsz10(false));
+
+ if (isContainsBlank)
+ pAttList->add(XML_containsBlank, ToPsz10(true));
+
+ // XML_containsMixedType possible values:
+ // 1 - field contains more than one data type
+ // 0 - (Default) only one data type. The field can still contain blank values (that's why we are using aDPTypesWithoutBlank)
+ if (aDPTypesWithoutBlank.size() > 1)
+ pAttList->add(XML_containsMixedTypes, ToPsz10(true));
+
+ // If field contain mixed types (Date and Numbers), MS Excel is saving only "minDate" and "maxDate" and not "minValue" and "maxValue"
+ // Example how Excel is saving mixed Date and Numbers:
+ // <sharedItems containsSemiMixedTypes="0" containsDate="1" containsString="0" containsMixedTypes="1" minDate="1900-01-03T22:26:04" maxDate="1900-01-07T14:02:04" />
+ // Example how Excel is saving Dates only:
+ // <sharedItems containsSemiMixedTypes="0" containsNonDate="0" containsDate="1" containsString="0" minDate="1903-08-24T07:40:48" maxDate="2024-05-23T07:12:00"/>
+ if (isContainsNumber)
+ pAttList->add(XML_containsNumber, ToPsz10(true));
+
+ if (isValueInteger && isContainsNumber)
+ pAttList->add(XML_containsInteger, ToPsz10(true));
+
+
+ // Number type fields could be mixed with blank types, and it shouldn't be treated as listed items.
+ // Example:
+ // <cacheField name="employeeID" numFmtId="0">
+ // <sharedItems containsString="0" containsBlank="1" containsNumber="1" containsInteger="1" minValue="35" maxValue="89"/>
+ // </cacheField>
+ if (isContainsNumber)
+ {
+ pAttList->add(XML_minValue, OString::number(fMin));
+ pAttList->add(XML_maxValue, OString::number(fMax));
+ }
+
+ if (isContainsDate)
+ {
+ pAttList->add(XML_minDate, GetExcelFormattedDate(fMin, GetFormatter()).toUtf8());
+ pAttList->add(XML_maxDate, GetExcelFormattedDate(fMax, GetFormatter()).toUtf8());
+ }
+
+ //if (bListItems) // see TODO above
+ {
+ pAttList->add(XML_count, OString::number(static_cast<tools::Long>(rFieldItems.size())));
+ }
+
+ if (isLongText)
+ {
+ pAttList->add(XML_longText, ToPsz10(true));
+ }
+
+ pDefStrm->startElement(XML_sharedItems, pAttList);
+
+ //if (bListItems) // see TODO above
+ {
+ for (const ScDPItemData& rItem : rFieldItems)
+ {
+ switch (rItem.GetType())
+ {
+ case ScDPItemData::String:
+ pDefStrm->singleElement(XML_s, XML_v, rItem.GetString().toUtf8());
+ break;
+ case ScDPItemData::Value:
+ if (isContainsDate)
+ {
+ pDefStrm->singleElement(XML_d,
+ XML_v, GetExcelFormattedDate(rItem.GetValue(), GetFormatter()).toUtf8());
+ }
+ else
+ pDefStrm->singleElement(XML_n,
+ XML_v, OString::number(rItem.GetValue()));
+ break;
+ case ScDPItemData::Empty:
+ pDefStrm->singleElement(XML_m);
+ break;
+ case ScDPItemData::Error:
+ pDefStrm->singleElement(XML_e,
+ XML_v, rItem.GetString().toUtf8());
+ break;
+ case ScDPItemData::GroupValue: // Should not happen here!
+ case ScDPItemData::RangeStart:
+ // TODO : What do we do with these types?
+ pDefStrm->singleElement(XML_m);
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ pDefStrm->endElement(XML_sharedItems);
+
+ WriteFieldGroup(i, i);
+
+ pDefStrm->endElement(XML_cacheField);
+ }
+
+ ScDPObject* pDPObject
+ = rCache.GetAllReferences().empty() ? nullptr : *rCache.GetAllReferences().begin();
+
+ for (size_t i = nCount; pDPObject && i < nCount + nGroupFieldCount; ++i)
+ {
+ const OUString aName = pDPObject->GetDimName(i, o3tl::temporary(bool()));
+ // tdf#126748: DPObject might not reference all group fields, when there are several
+ // DPObjects referencing this cache. Trying to get a dimension data for a field not used
+ // in a given DPObject will give nullptr, and dereferencing it then will crash. To avoid
+ // the crash, until there's a correct method to find the names of group fields in cache,
+ // just skip the fields, creating bad cache data, which is of course a temporary hack.
+ // TODO: reimplement the whole block to get the names from another source, not from first
+ // cache reference.
+ if (aName.isEmpty())
+ break;
+
+ ScDPSaveData* pSaveData = pDPObject->GetSaveData();
+ assert(pSaveData);
+
+ const ScDPSaveGroupDimension* pDim = pSaveData->GetDimensionData()->GetNamedGroupDim(aName);
+ assert(pDim);
+
+ const SCCOL nBase = rCache.GetDimensionIndex(pDim->GetSourceDimName());
+ assert(nBase >= 0);
+
+ pDefStrm->startElement(XML_cacheField, XML_name, aName.toUtf8(), XML_numFmtId,
+ OString::number(0), XML_databaseField, ToPsz10(false));
+ WriteFieldGroup(i, nBase);
+ pDefStrm->endElement(XML_cacheField);
+ }
+
+ pDefStrm->endElement(XML_cacheFields);
+
+ pDefStrm->endElement(XML_pivotCacheDefinition);
+}
+
+XclExpXmlPivotTableManager::XclExpXmlPivotTableManager( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot), maCaches(rRoot) {}
+
+void XclExpXmlPivotTableManager::Initialize()
+{
+ ScDocument& rDoc = GetDoc();
+ if (!rDoc.HasPivotTable())
+ // No pivot table to export.
+ return;
+
+ ScDPCollection* pDPColl = rDoc.GetDPCollection();
+ if (!pDPColl)
+ return;
+
+ // Update caches from DPObject
+ for (size_t i = 0; i < pDPColl->GetCount(); ++i)
+ {
+ ScDPObject& rDPObj = (*pDPColl)[i];
+ rDPObj.SyncAllDimensionMembers();
+ (void)rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
+ }
+
+ // Go through the caches first.
+
+ std::vector<XclExpXmlPivotCaches::Entry> aCaches;
+ const ScDPCollection::SheetCaches& rSheetCaches = pDPColl->GetSheetCaches();
+ const std::vector<ScRange>& rRanges = rSheetCaches.getAllRanges();
+ for (const auto & rRange : rRanges)
+ {
+ const ScDPCache* pCache = rSheetCaches.getExistingCache(rRange);
+ if (!pCache)
+ continue;
+
+ // Get all pivot objects that reference this cache, and set up an
+ // object to cache ID mapping.
+ const ScDPCache::ScDPObjectSet& rRefs = pCache->GetAllReferences();
+ for (const auto& rRef : rRefs)
+ maCacheIdMap.emplace(rRef, aCaches.size()+1);
+
+ XclExpXmlPivotCaches::Entry aEntry;
+ aEntry.mpCache = pCache;
+ aEntry.maSrcRange = rRange;
+ aCaches.push_back(aEntry); // Cache ID equals position + 1.
+ }
+
+ // TODO : Handle name and database caches as well.
+
+ for (size_t i = 0, n = pDPColl->GetCount(); i < n; ++i)
+ {
+ const ScDPObject& rDPObj = (*pDPColl)[i];
+
+ // Get the cache ID for this pivot table.
+ CacheIdMapType::iterator itCache = maCacheIdMap.find(&rDPObj);
+ if (itCache == maCacheIdMap.end())
+ // No cache ID found. Something is wrong here...
+ continue;
+
+ sal_Int32 nCacheId = itCache->second;
+ SCTAB nTab = rDPObj.GetOutRange().aStart.Tab();
+
+ TablesType::iterator it = m_Tables.find(nTab);
+ if (it == m_Tables.end())
+ {
+ // Insert a new instance for this sheet index.
+ std::pair<TablesType::iterator, bool> r =
+ m_Tables.insert(std::make_pair(nTab, std::make_unique<XclExpXmlPivotTables>(GetRoot(), maCaches)));
+ it = r.first;
+ }
+
+ XclExpXmlPivotTables *const p = it->second.get();
+ p->AppendTable(&rDPObj, nCacheId, i+1);
+ }
+
+ maCaches.SetCaches(std::move(aCaches));
+}
+
+XclExpXmlPivotCaches& XclExpXmlPivotTableManager::GetCaches()
+{
+ return maCaches;
+}
+
+XclExpXmlPivotTables* XclExpXmlPivotTableManager::GetTablesBySheet( SCTAB nTab )
+{
+ TablesType::iterator const it = m_Tables.find(nTab);
+ return it == m_Tables.end() ? nullptr : it->second.get();
+}
+
+XclExpXmlPivotTables::Entry::Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) :
+ mpTable(pTable), mnCacheId(nCacheId), mnPivotId(nPivotId) {}
+
+XclExpXmlPivotTables::XclExpXmlPivotTables( const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches ) :
+ XclExpRoot(rRoot), mrCaches(rCaches) {}
+
+void XclExpXmlPivotTables::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pWSStrm = rStrm.GetCurrentStream(); // worksheet stream
+
+ for (const auto& rTable : maTables)
+ {
+ const ScDPObject& rObj = *rTable.mpTable;
+ sal_Int32 nCacheId = rTable.mnCacheId;
+ sal_Int32 nPivotId = rTable.mnPivotId;
+
+ sax_fastparser::FSHelperPtr pPivotStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotTables/", "pivotTable", nPivotId),
+ XclXmlUtils::GetStreamName(nullptr, "../pivotTables/pivotTable", nPivotId),
+ pWSStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotTable"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotTable"));
+
+ rStrm.PushStream(pPivotStrm);
+ SavePivotTableXml(rStrm, rObj, nCacheId);
+ rStrm.PopStream();
+ }
+}
+
+namespace {
+
+struct DataField
+{
+ tools::Long mnPos; // field index in pivot cache.
+ const ScDPSaveDimension* mpDim;
+
+ DataField( tools::Long nPos, const ScDPSaveDimension* pDim ) : mnPos(nPos), mpDim(pDim) {}
+};
+
+/** Returns an OOXML subtotal function name string. See ECMA-376-1:2016 18.18.43 */
+OString GetSubtotalFuncName(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM: return "sum"_ostr;
+ case ScGeneralFunction::COUNT: return "count"_ostr;
+ case ScGeneralFunction::AVERAGE: return "avg"_ostr;
+ case ScGeneralFunction::MAX: return "max"_ostr;
+ case ScGeneralFunction::MIN: return "min"_ostr;
+ case ScGeneralFunction::PRODUCT: return "product"_ostr;
+ case ScGeneralFunction::COUNTNUMS: return "countA"_ostr;
+ case ScGeneralFunction::STDEV: return "stdDev"_ostr;
+ case ScGeneralFunction::STDEVP: return "stdDevP"_ostr;
+ case ScGeneralFunction::VAR: return "var"_ostr;
+ case ScGeneralFunction::VARP: return "varP"_ostr;
+ default:;
+ }
+ return "default"_ostr;
+}
+
+sal_Int32 GetSubtotalAttrToken(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM: return XML_sumSubtotal;
+ case ScGeneralFunction::COUNT: return XML_countSubtotal;
+ case ScGeneralFunction::AVERAGE: return XML_avgSubtotal;
+ case ScGeneralFunction::MAX: return XML_maxSubtotal;
+ case ScGeneralFunction::MIN: return XML_minSubtotal;
+ case ScGeneralFunction::PRODUCT: return XML_productSubtotal;
+ case ScGeneralFunction::COUNTNUMS: return XML_countASubtotal;
+ case ScGeneralFunction::STDEV: return XML_stdDevSubtotal;
+ case ScGeneralFunction::STDEVP: return XML_stdDevPSubtotal;
+ case ScGeneralFunction::VAR: return XML_varSubtotal;
+ case ScGeneralFunction::VARP: return XML_varPSubtotal;
+ default:;
+ }
+ return XML_defaultSubtotal;
+}
+
+// An item is expected to contain sequences of css::xml::FastAttribute and css::xml::Attribute
+void WriteGrabBagItemToStream(XclExpXmlStream& rStrm, sal_Int32 tokenId, const css::uno::Any& rItem)
+{
+ css::uno::Sequence<css::uno::Any> aSeqs;
+ if(!(rItem >>= aSeqs))
+ return;
+
+ auto& pStrm = rStrm.GetCurrentStream();
+ pStrm->write("<")->writeId(tokenId);
+
+ css::uno::Sequence<css::xml::FastAttribute> aFastSeq;
+ css::uno::Sequence<css::xml::Attribute> aUnkSeq;
+ for (const auto& a : std::as_const(aSeqs))
+ {
+ if (a >>= aFastSeq)
+ {
+ for (const auto& rAttr : std::as_const(aFastSeq))
+ rStrm.WriteAttributes(rAttr.Token, rAttr.Value);
+ }
+ else if (a >>= aUnkSeq)
+ {
+ for (const auto& rAttr : std::as_const(aUnkSeq))
+ pStrm->write(" ")
+ ->write(rAttr.Name)
+ ->write("=\"")
+ ->writeEscaped(rAttr.Value)
+ ->write("\"");
+ }
+ }
+
+ pStrm->write("/>");
+}
+}
+
+void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rDPObj, sal_Int32 nCacheId )
+{
+ typedef std::unordered_map<OUString, long> NameToIdMapType;
+
+ const XclExpXmlPivotCaches::Entry* pCacheEntry = mrCaches.GetCache(nCacheId);
+ if (!pCacheEntry)
+ // Something is horribly wrong. Check your logic.
+ return;
+
+ const ScDPCache& rCache = *pCacheEntry->mpCache;
+
+ const ScDPSaveData& rSaveData = *rDPObj.GetSaveData();
+
+ size_t nFieldCount = rCache.GetFieldCount() + rCache.GetGroupFieldCount();
+ std::vector<const ScDPSaveDimension*> aCachedDims;
+ NameToIdMapType aNameToIdMap;
+
+ aCachedDims.reserve(nFieldCount);
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ OUString aName = const_cast<ScDPObject&>(rDPObj).GetDimName(i, o3tl::temporary(bool()));
+ aNameToIdMap.emplace(aName, aCachedDims.size());
+ const ScDPSaveDimension* pDim = rSaveData.GetExistingDimensionByName(aName);
+ aCachedDims.push_back(pDim);
+ }
+
+ std::vector<tools::Long> aRowFields;
+ std::vector<tools::Long> aColFields;
+ std::vector<tools::Long> aPageFields;
+ std::vector<DataField> aDataFields;
+
+ tools::Long nDataDimCount = rSaveData.GetDataDimensionCount();
+ // Use dimensions in the save data to get their correct ordering.
+ // Dimension order here is significant as they specify the order of
+ // appearance in each axis.
+ const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
+ bool bTabularMode = false;
+ bool bCompactMode = true;
+ for (const auto & i : rDims)
+ {
+ const ScDPSaveDimension& rDim = *i;
+
+ tools::Long nPos = -1; // position in cache
+ if (rDim.IsDataLayout())
+ nPos = -2; // Excel uses an index of -2 to indicate a data layout field.
+ else
+ {
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(rDim.GetName());
+ NameToIdMapType::iterator it = aNameToIdMap.find(aSrcName);
+ if (it != aNameToIdMap.end())
+ nPos = it->second;
+
+ if (nPos == -1)
+ continue;
+
+ if (!aCachedDims[nPos])
+ continue;
+ }
+
+ sheet::DataPilotFieldOrientation eOrient = rDim.GetOrientation();
+
+ switch (eOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ if (nPos == -2 && nDataDimCount <= 1)
+ break;
+ aColFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ aRowFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ aPageFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ aDataFields.emplace_back(nPos, &rDim);
+ break;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ default:
+ ;
+ }
+ if(rDim.GetLayoutInfo())
+ {
+ const auto eLayoutMode = rDim.GetLayoutInfo()->LayoutMode;
+ bTabularMode |= (eLayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ bCompactMode &= (eLayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
+ }
+ }
+
+ sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream();
+ pPivotStrm->startElement(XML_pivotTableDefinition,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ XML_name, rDPObj.GetName().toUtf8(),
+ XML_cacheId, OString::number(nCacheId),
+ XML_applyNumberFormats, ToPsz10(false),
+ XML_applyBorderFormats, ToPsz10(false),
+ XML_applyFontFormats, ToPsz10(false),
+ XML_applyPatternFormats, ToPsz10(false),
+ XML_applyAlignmentFormats, ToPsz10(false),
+ XML_applyWidthHeightFormats, ToPsz10(false),
+ XML_dataCaption, "Values",
+ XML_showDrill, ToPsz10(rSaveData.GetExpandCollapse()),
+ XML_useAutoFormatting, ToPsz10(false),
+ XML_itemPrintTitles, ToPsz10(true),
+ XML_indent, ToPsz10(false),
+ XML_outline, ToPsz10(!bTabularMode),
+ XML_outlineData, ToPsz10(!bTabularMode),
+ XML_compact, ToPsz10(bCompactMode),
+ XML_compactData, ToPsz10(bCompactMode));
+
+ // NB: Excel's range does not include page field area (if any).
+ ScRange aOutRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
+
+ sal_Int32 nFirstHeaderRow = rDPObj.GetHeaderLayout() ? 2 : 1;
+ sal_Int32 nFirstDataRow = 2;
+ sal_Int32 nFirstDataCol = 1;
+ ScRange aResRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::RESULT);
+
+ if (!aOutRange.IsValid())
+ aOutRange = rDPObj.GetOutRange();
+
+ if (aOutRange.IsValid() && aResRange.IsValid())
+ {
+ nFirstDataRow = aResRange.aStart.Row() - aOutRange.aStart.Row();
+ nFirstDataCol = aResRange.aStart.Col() - aOutRange.aStart.Col();
+ }
+
+ pPivotStrm->write("<")->writeId(XML_location);
+ rStrm.WriteAttributes(XML_ref,
+ XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aOutRange),
+ XML_firstHeaderRow, OUString::number(nFirstHeaderRow),
+ XML_firstDataRow, OUString::number(nFirstDataRow),
+ XML_firstDataCol, OUString::number(nFirstDataCol));
+
+ if (!aPageFields.empty())
+ {
+ rStrm.WriteAttributes(XML_rowPageCount, OUString::number(static_cast<tools::Long>(aPageFields.size())));
+ rStrm.WriteAttributes(XML_colPageCount, OUString::number(1));
+ }
+
+ pPivotStrm->write("/>");
+
+ // <pivotFields> - It must contain all fields in the pivot cache even if
+ // only some of them are used in the pivot table. The order must be as
+ // they appear in the cache.
+
+ pPivotStrm->startElement(XML_pivotFields,
+ XML_count, OString::number(static_cast<tools::Long>(aCachedDims.size())));
+
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ const ScDPSaveDimension* pDim = aCachedDims[i];
+ if (!pDim)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ continue;
+ }
+
+ bool bDimInTabularMode = false;
+ bool bDimInCompactMode = false;
+ if(pDim->GetLayoutInfo())
+ {
+ const auto eLayoutMode = pDim->GetLayoutInfo()->LayoutMode;
+ bDimInTabularMode = (eLayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ bDimInCompactMode = (eLayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
+ }
+
+ sheet::DataPilotFieldOrientation eOrient = pDim->GetOrientation();
+
+ if (eOrient == sheet::DataPilotFieldOrientation_HIDDEN)
+ {
+ if(bDimInTabularMode)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false),
+ XML_outline, ToPsz10(false));
+ }
+ else
+ {
+ if (bDimInCompactMode)
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_showAll, ToPsz10(false));
+ else
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ }
+ continue;
+ }
+
+ if (eOrient == sheet::DataPilotFieldOrientation_DATA)
+ {
+ if(bDimInTabularMode)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false),
+ XML_outline, ToPsz10(false));
+ }
+ else
+ {
+ if (bDimInCompactMode)
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_showAll, ToPsz10(false));
+ else
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ }
+ continue;
+ }
+
+ // Dump field items.
+ std::vector<ScDPLabelData::Member> aMembers;
+ {
+ // We need to get the members in actual order, getting which requires non-const reference here
+ auto& dpo = const_cast<ScDPObject&>(rDPObj);
+ dpo.GetMembers(i, dpo.GetUsedHierarchy(i), aMembers);
+ }
+
+ std::vector<OUString> aCacheFieldItems;
+ if (i < rCache.GetFieldCount() && !rCache.GetGroupType(i))
+ {
+ for (const auto& it : rCache.GetDimMemberValues(i))
+ {
+ OUString sFormattedName;
+ if (it.HasStringData() || it.IsEmpty())
+ sFormattedName = it.GetString();
+ else
+ sFormattedName = const_cast<ScDPObject&>(rDPObj).GetFormattedString(
+ pDim->GetName(), it.GetValue());
+ aCacheFieldItems.push_back(sFormattedName);
+ }
+ }
+ else
+ {
+ aCacheFieldItems = SortGroupItems(rCache, i);
+ }
+ // The pair contains the member index in cache and if it is hidden
+ std::vector< std::pair<size_t, bool> > aMemberSequence;
+ std::set<size_t> aUsedCachePositions;
+ for (const auto & rMember : aMembers)
+ {
+ auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
+ if (it != aCacheFieldItems.end())
+ {
+ size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
+ auto aInserted = aUsedCachePositions.insert(nCachePos);
+ if (aInserted.second)
+ aMemberSequence.emplace_back(std::make_pair(nCachePos, !rMember.mbVisible));
+ }
+ }
+ // Now add all remaining cache items as hidden
+ for (size_t nItem = 0; nItem < aCacheFieldItems.size(); ++nItem)
+ {
+ if (aUsedCachePositions.find(nItem) == aUsedCachePositions.end())
+ aMemberSequence.emplace_back(nItem, true);
+ }
+
+ // tdf#125086: check if this field *also* appears in Data region
+ bool bAppearsInData = false;
+ {
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
+ const auto it = std::find_if(
+ aDataFields.begin(), aDataFields.end(), [&aSrcName](const DataField& rDataField) {
+ OUString aThisName
+ = ScDPUtil::getSourceDimensionName(rDataField.mpDim->GetName());
+ return aThisName == aSrcName;
+ });
+ if (it != aDataFields.end())
+ bAppearsInData = true;
+ }
+
+ auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pAttList->add(XML_axis, toOOXMLAxisType(eOrient));
+ if (bAppearsInData)
+ pAttList->add(XML_dataField, ToPsz10(true));
+
+ if (!bDimInCompactMode)
+ pAttList->add(XML_compact, ToPsz10(false));
+
+ pAttList->add(XML_showAll, ToPsz10(false));
+
+ tools::Long nSubTotalCount = pDim->GetSubTotalsCount();
+ std::vector<OString> aSubtotalSequence;
+ bool bHasDefaultSubtotal = false;
+ for (tools::Long nSubTotal = 0; nSubTotal < nSubTotalCount; ++nSubTotal)
+ {
+ ScGeneralFunction eFunc = pDim->GetSubTotalFunc(nSubTotal);
+ aSubtotalSequence.push_back(GetSubtotalFuncName(eFunc));
+ sal_Int32 nAttToken = GetSubtotalAttrToken(eFunc);
+ if (nAttToken == XML_defaultSubtotal)
+ bHasDefaultSubtotal = true;
+ else if (!pAttList->hasAttribute(nAttToken))
+ pAttList->add(nAttToken, ToPsz10(true));
+ }
+ // XML_defaultSubtotal is true by default; only write it if it's false
+ if (!bHasDefaultSubtotal)
+ pAttList->add(XML_defaultSubtotal, ToPsz10(false));
+
+ if(bDimInTabularMode)
+ pAttList->add( XML_outline, ToPsz10(false));
+ pPivotStrm->startElement(XML_pivotField, pAttList);
+
+ pPivotStrm->startElement(XML_items,
+ XML_count, OString::number(static_cast<tools::Long>(aMemberSequence.size() + aSubtotalSequence.size())));
+
+ for (const auto & nMember : aMemberSequence)
+ {
+ auto pItemAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ if (nMember.second)
+ pItemAttList->add(XML_h, ToPsz10(true));
+ pItemAttList->add(XML_x, OString::number(static_cast<tools::Long>(nMember.first)));
+ pPivotStrm->singleElement(XML_item, pItemAttList);
+ }
+
+ for (const OString& sSubtotal : aSubtotalSequence)
+ {
+ pPivotStrm->singleElement(XML_item, XML_t, sSubtotal);
+ }
+
+ pPivotStrm->endElement(XML_items);
+ pPivotStrm->endElement(XML_pivotField);
+ }
+
+ pPivotStrm->endElement(XML_pivotFields);
+
+ // <rowFields>
+
+ if (!aRowFields.empty())
+ {
+ pPivotStrm->startElement(XML_rowFields,
+ XML_count, OString::number(static_cast<tools::Long>(aRowFields.size())));
+
+ for (const auto& rRowField : aRowFields)
+ {
+ pPivotStrm->singleElement(XML_field, XML_x, OString::number(rRowField));
+ }
+
+ pPivotStrm->endElement(XML_rowFields);
+ }
+
+ // <rowItems>
+
+ // <colFields>
+
+ if (!aColFields.empty())
+ {
+ pPivotStrm->startElement(XML_colFields,
+ XML_count, OString::number(static_cast<tools::Long>(aColFields.size())));
+
+ for (const auto& rColField : aColFields)
+ {
+ pPivotStrm->singleElement(XML_field, XML_x, OString::number(rColField));
+ }
+
+ pPivotStrm->endElement(XML_colFields);
+ }
+
+ // <colItems>
+
+ // <pageFields>
+
+ if (!aPageFields.empty())
+ {
+ pPivotStrm->startElement(XML_pageFields,
+ XML_count, OString::number(static_cast<tools::Long>(aPageFields.size())));
+
+ for (const auto& rPageField : aPageFields)
+ {
+ pPivotStrm->singleElement(XML_pageField,
+ XML_fld, OString::number(rPageField),
+ XML_hier, OString::number(-1)); // TODO : handle this correctly.
+ }
+
+ pPivotStrm->endElement(XML_pageFields);
+ }
+
+ // <dataFields>
+
+ if (!aDataFields.empty())
+ {
+ css::uno::Reference<css::container::XNameAccess> xDimsByName;
+ if (auto xDimSupplier = const_cast<ScDPObject&>(rDPObj).GetSource())
+ xDimsByName = xDimSupplier->getDimensions();
+
+ pPivotStrm->startElement(XML_dataFields,
+ XML_count, OString::number(static_cast<tools::Long>(aDataFields.size())));
+
+ for (const auto& rDataField : aDataFields)
+ {
+ tools::Long nDimIdx = rDataField.mnPos;
+ assert(nDimIdx == -2 || aCachedDims[nDimIdx]); // the loop above should have screened for NULL's, skip check for -2 "data field"
+ const ScDPSaveDimension& rDim = *rDataField.mpDim;
+ std::optional<OUString> pName = rDim.GetLayoutName();
+ // tdf#124651: despite being optional in CT_DataField according to ECMA-376 Part 1,
+ // Excel (at least 2016) seems to insist on the presence of "name" attribute in
+ // dataField element.
+ // tdf#124881: try to create a meaningful name; don't use empty string.
+ if (!pName)
+ pName = ScDPUtil::getDisplayedMeasureName(
+ rDim.GetName(), ScDPUtil::toSubTotalFunc(rDim.GetFunction()));
+ auto pItemAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pItemAttList->add(XML_name, pName->toUtf8());
+ pItemAttList->add(XML_fld, OString::number(nDimIdx));
+ const char* pSubtotal = toOOXMLSubtotalType(rDim.GetFunction());
+ if (pSubtotal)
+ pItemAttList->add(XML_subtotal, pSubtotal);
+ if (xDimsByName)
+ {
+ try
+ {
+ css::uno::Reference<css::beans::XPropertySet> xDimProps(
+ xDimsByName->getByName(rDim.GetName()), uno::UNO_QUERY_THROW);
+ css::uno::Any aVal = xDimProps->getPropertyValue(SC_UNONAME_NUMFMT);
+ sal_uInt32 nScNumFmt = aVal.get<sal_uInt32>();
+ sal_uInt16 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
+ pItemAttList->add(XML_numFmtId, OString::number(nXclNumFmt));
+ }
+ catch (uno::Exception&)
+ {
+ SAL_WARN("sc.filter",
+ "Couldn't get number format for data field " << rDim.GetName());
+ // Just skip exporting number format
+ }
+ }
+ pPivotStrm->singleElement(XML_dataField, pItemAttList);
+ }
+
+ pPivotStrm->endElement(XML_dataFields);
+ }
+
+ // Now add style info (use grab bag, or just a set which is default on Excel 2007 through 2016)
+ if (const auto [bHas, aVal] = rDPObj.GetInteropGrabBagValue("pivotTableStyleInfo"); bHas)
+ WriteGrabBagItemToStream(rStrm, XML_pivotTableStyleInfo, aVal);
+ else
+ pPivotStrm->singleElement(XML_pivotTableStyleInfo, XML_name, "PivotStyleLight16",
+ XML_showRowHeaders, "1", XML_showColHeaders, "1",
+ XML_showRowStripes, "0", XML_showColStripes, "0",
+ XML_showLastColumn, "1");
+
+ OUString aBuf = "../pivotCache/pivotCacheDefinition" +
+ OUString::number(nCacheId) +
+ ".xml";
+
+ rStrm.addRelation(
+ pPivotStrm->getOutputStream(),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
+ aBuf);
+
+ pPivotStrm->endElement(XML_pivotTableDefinition);
+}
+
+void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId )
+{
+ maTables.emplace_back(pTable, nCacheId, nPivotId);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xerecord.cxx b/sc/source/filter/excel/xerecord.cxx
new file mode 100644
index 0000000000..8778d75b5c
--- /dev/null
+++ b/sc/source/filter/excel/xerecord.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xerecord.hxx>
+#include <xeroot.hxx>
+#include <xltools.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::oox;
+
+// Base classes to export Excel records =======================================
+
+XclExpRecordBase::~XclExpRecordBase()
+{
+}
+
+void XclExpRecordBase::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+void XclExpRecordBase::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+XclExpDelegatingRecord::XclExpDelegatingRecord( XclExpRecordBase* pRecord ) :
+ mpRecord( pRecord )
+{
+}
+
+XclExpDelegatingRecord::~XclExpDelegatingRecord()
+{
+ // Do Nothing; we use Delegating Record for other objects we "know" will
+ // survive...
+}
+
+void XclExpDelegatingRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mpRecord )
+ mpRecord->SaveXml( rStrm );
+}
+
+XclExpXmlElementRecord::XclExpXmlElementRecord(sal_Int32 const nElement)
+ : mnElement( nElement )
+{
+}
+
+XclExpXmlElementRecord::~XclExpXmlElementRecord()
+{
+}
+
+XclExpXmlStartElementRecord::XclExpXmlStartElementRecord(sal_Int32 const nElement)
+ : XclExpXmlElementRecord(nElement)
+{
+}
+
+XclExpXmlStartElementRecord::~XclExpXmlStartElementRecord()
+{
+}
+
+void XclExpXmlStartElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ // TODO: no generic way to add attributes here, but it appears to
+ // not be needed yet
+ rStream->startElement(mnElement);
+}
+
+XclExpXmlEndElementRecord::XclExpXmlEndElementRecord( sal_Int32 nElement )
+ : XclExpXmlElementRecord( nElement )
+{
+}
+
+XclExpXmlEndElementRecord::~XclExpXmlEndElementRecord()
+{
+}
+
+void XclExpXmlEndElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->endElement( mnElement );
+}
+
+XclExpXmlStartSingleElementRecord::XclExpXmlStartSingleElementRecord(
+ sal_Int32 const nElement)
+ : XclExpXmlElementRecord( nElement )
+{
+}
+
+XclExpXmlStartSingleElementRecord::~XclExpXmlStartSingleElementRecord()
+{
+}
+
+void XclExpXmlStartSingleElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ rStream->write( "<" )->writeId( mnElement );
+}
+
+XclExpXmlEndSingleElementRecord::XclExpXmlEndSingleElementRecord()
+{
+}
+
+XclExpXmlEndSingleElementRecord::~XclExpXmlEndSingleElementRecord()
+{
+}
+
+void XclExpXmlEndSingleElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->write( "/>" );
+}
+
+XclExpRecord::XclExpRecord( sal_uInt16 nRecId, std::size_t nRecSize ) :
+ mnRecSize( nRecSize ),
+ mnRecId( nRecId )
+{
+}
+
+XclExpRecord::~XclExpRecord()
+{
+}
+
+void XclExpRecord::SetRecHeader( sal_uInt16 nRecId, std::size_t nRecSize )
+{
+ SetRecId( nRecId );
+ SetRecSize( nRecSize );
+}
+
+void XclExpRecord::WriteBody( XclExpStream& /*rStrm*/ )
+{
+}
+
+void XclExpRecord::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mnRecId != EXC_ID_UNKNOWN, "XclExpRecord::Save - record ID uninitialized" );
+ rStrm.StartRecord( mnRecId, mnRecSize );
+ WriteBody( rStrm );
+ rStrm.EndRecord();
+}
+
+template<>
+void XclExpValueRecord<double>::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+ rStrm.WriteAttributes(mnAttribute, OUString::number(maValue));
+}
+
+void XclExpBoolRecord::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >( mbValue ? 1 : 0 );
+}
+
+void XclExpBoolRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+
+ rStrm.WriteAttributes(
+ // HACK: HIDEOBJ (excdoc.cxx) should be its own object to handle XML_showObjects
+ mnAttribute, mnAttribute == XML_showObjects ? "all" : ToPsz( mbValue ));
+}
+
+XclExpDummyRecord::XclExpDummyRecord( sal_uInt16 nRecId, const void* pRecData, std::size_t nRecSize ) :
+ XclExpRecord( nRecId )
+{
+ SetData( pRecData, nRecSize );
+}
+
+void XclExpDummyRecord::SetData( const void* pRecData, std::size_t nRecSize )
+{
+ mpData = pRecData;
+ SetRecSize( pRecData ? nRecSize : 0 );
+}
+
+void XclExpDummyRecord::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.Write( mpData, GetRecSize() );
+}
+
+// Future records =============================================================
+
+XclExpFutureRecord::XclExpFutureRecord( XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ meRecType( eRecType )
+{
+}
+
+void XclExpFutureRecord::Save( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( GetRecId(), GetRecSize() + ((meRecType == EXC_FUTUREREC_UNUSEDREF) ? 12 : 4) );
+ rStrm << GetRecId() << sal_uInt16( 0 );
+ if( meRecType == EXC_FUTUREREC_UNUSEDREF )
+ rStrm.WriteZeroBytes( 8 );
+ WriteBody( rStrm );
+ rStrm.EndRecord();
+}
+
+XclExpSubStream::XclExpSubStream( sal_uInt16 nSubStrmType ) :
+ mnSubStrmType( nSubStrmType )
+{
+}
+
+void XclExpSubStream::Save( XclExpStream& rStrm )
+{
+ // BOF record
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ rStrm.StartRecord( EXC_ID2_BOF, 4 );
+ rStrm << sal_uInt16( 7 ) << mnSubStrmType;
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF3:
+ rStrm.StartRecord( EXC_ID3_BOF, 6 );
+ rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 2104 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF4:
+ rStrm.StartRecord( EXC_ID4_BOF, 6 );
+ rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 1705 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF5:
+ rStrm.StartRecord( EXC_ID5_BOF, 8 );
+ rStrm << EXC_BOF_BIFF5 << mnSubStrmType << sal_uInt16( 4915 ) << sal_uInt16( 1994 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF8:
+ rStrm.StartRecord( EXC_ID5_BOF, 16 );
+ rStrm << EXC_BOF_BIFF8 << mnSubStrmType << sal_uInt16( 3612 ) << sal_uInt16( 1996 );
+ rStrm << sal_uInt32( 1 ) << sal_uInt32( 6 );
+ rStrm.EndRecord();
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+
+ // substream records
+ XclExpRecordList<>::Save( rStrm );
+
+ // EOF record
+ rStrm.StartRecord( EXC_ID_EOF, 0 );
+ rStrm.EndRecord();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeroot.cxx b/sc/source/filter/excel/xeroot.cxx
new file mode 100644
index 0000000000..ce281890f8
--- /dev/null
+++ b/sc/source/filter/excel/xeroot.cxx
@@ -0,0 +1,363 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <rtl/random.h>
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sot/storage.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xepivot.hxx>
+#include <xestyle.hxx>
+#include <xeroot.hxx>
+#include <xepivotxml.hxx>
+#include <xedbdata.hxx>
+#include <xlcontent.hxx>
+#include <xlname.hxx>
+#include <xllink.hxx>
+
+#include <excrecds.hxx>
+#include <tabprotection.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+
+#include <formulabase.hxx>
+#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+using namespace ::com::sun::star;
+
+// Global data ================================================================
+
+XclExpRootData::XclExpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) :
+ XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, true )
+{
+ mbRelUrl = mrMedium.IsRemote()
+ ? officecfg::Office::Common::Save::URL::Internet::get()
+ : officecfg::Office::Common::Save::URL::FileSystem::get();
+ maStringBuf.setLength(0);
+}
+
+XclExpRootData::~XclExpRootData()
+{
+}
+
+XclExpRoot::XclExpRoot( XclExpRootData& rExpRootData ) :
+ XclRoot( rExpRootData ),
+ mrExpData( rExpRootData )
+{
+}
+
+XclExpTabInfo& XclExpRoot::GetTabInfo() const
+{
+ OSL_ENSURE( mrExpData.mxTabInfo, "XclExpRoot::GetTabInfo - missing object (wrong BIFF?)" );
+ return *mrExpData.mxTabInfo;
+}
+
+XclExpAddressConverter& XclExpRoot::GetAddressConverter() const
+{
+ OSL_ENSURE( mrExpData.mxAddrConv, "XclExpRoot::GetAddressConverter - missing object (wrong BIFF?)" );
+ return *mrExpData.mxAddrConv;
+}
+
+XclExpFormulaCompiler& XclExpRoot::GetFormulaCompiler() const
+{
+ OSL_ENSURE( mrExpData.mxFmlaComp, "XclExpRoot::GetFormulaCompiler - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFmlaComp;
+}
+
+XclExpProgressBar& XclExpRoot::GetProgressBar() const
+{
+ OSL_ENSURE( mrExpData.mxProgress, "XclExpRoot::GetProgressBar - missing object (wrong BIFF?)" );
+ return *mrExpData.mxProgress;
+}
+
+XclExpSst& XclExpRoot::GetSst() const
+{
+ OSL_ENSURE( mrExpData.mxSst, "XclExpRoot::GetSst - missing object (wrong BIFF?)" );
+ return *mrExpData.mxSst;
+}
+
+XclExpPalette& XclExpRoot::GetPalette() const
+{
+ OSL_ENSURE( mrExpData.mxPalette, "XclExpRoot::GetPalette - missing object (wrong BIFF?)" );
+ return *mrExpData.mxPalette;
+}
+
+XclExpFontBuffer& XclExpRoot::GetFontBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxFontBfr, "XclExpRoot::GetFontBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFontBfr;
+}
+
+XclExpNumFmtBuffer& XclExpRoot::GetNumFmtBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxNumFmtBfr, "XclExpRoot::GetNumFmtBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxNumFmtBfr;
+}
+
+XclExpXFBuffer& XclExpRoot::GetXFBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxXFBfr, "XclExpRoot::GetXFBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxXFBfr;
+}
+
+XclExpLinkManager& XclExpRoot::GetGlobalLinkManager() const
+{
+ OSL_ENSURE( mrExpData.mxGlobLinkMgr, "XclExpRoot::GetGlobalLinkManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxGlobLinkMgr;
+}
+
+XclExpLinkManager& XclExpRoot::GetLocalLinkManager() const
+{
+ OSL_ENSURE( GetLocalLinkMgrRef(), "XclExpRoot::GetLocalLinkManager - missing object (wrong BIFF?)" );
+ return *GetLocalLinkMgrRef();
+}
+
+XclExpNameManager& XclExpRoot::GetNameManager() const
+{
+ OSL_ENSURE( mrExpData.mxNameMgr, "XclExpRoot::GetNameManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxNameMgr;
+}
+
+XclExpObjectManager& XclExpRoot::GetObjectManager() const
+{
+ OSL_ENSURE( mrExpData.mxObjMgr, "XclExpRoot::GetObjectManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxObjMgr;
+}
+
+XclExpFilterManager& XclExpRoot::GetFilterManager() const
+{
+ OSL_ENSURE( mrExpData.mxFilterMgr, "XclExpRoot::GetFilterManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFilterMgr;
+}
+
+XclExpDxfs& XclExpRoot::GetDxfs() const
+{
+ OSL_ENSURE( mrExpData.mxDxfs, "XclExpRoot::GetDxfs - missing object ( wrong BIFF?)" );
+ return *mrExpData.mxDxfs;
+}
+
+XclExpPivotTableManager& XclExpRoot::GetPivotTableManager() const
+{
+ OSL_ENSURE( mrExpData.mxPTableMgr, "XclExpRoot::GetPivotTableManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxPTableMgr;
+}
+
+XclExpXmlPivotTableManager& XclExpRoot::GetXmlPivotTableManager()
+{
+ assert(mrExpData.mxXmlPTableMgr);
+ return *mrExpData.mxXmlPTableMgr;
+}
+
+XclExpTablesManager& XclExpRoot::GetTablesManager()
+{
+ assert(mrExpData.mxTablesMgr);
+ return *mrExpData.mxTablesMgr;
+}
+
+void XclExpRoot::InitializeConvert()
+{
+ mrExpData.mxTabInfo = std::make_shared<XclExpTabInfo>( GetRoot() );
+ mrExpData.mxAddrConv = std::make_shared<XclExpAddressConverter>( GetRoot() );
+ mrExpData.mxFmlaComp = std::make_shared<XclExpFormulaCompiler>( GetRoot() );
+ mrExpData.mxProgress = std::make_shared<XclExpProgressBar>( GetRoot() );
+
+ GetProgressBar().Initialize();
+}
+
+void XclExpRoot::InitializeGlobals()
+{
+ SetCurrScTab( SCTAB_GLOBAL );
+
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ mrExpData.mxPalette = new XclExpPalette( GetRoot() );
+ mrExpData.mxFontBfr = new XclExpFontBuffer( GetRoot() );
+ mrExpData.mxNumFmtBfr = new XclExpNumFmtBuffer( GetRoot() );
+ mrExpData.mxXFBfr = new XclExpXFBuffer( GetRoot() );
+ mrExpData.mxGlobLinkMgr = new XclExpLinkManager( GetRoot() );
+ mrExpData.mxNameMgr = new XclExpNameManager( GetRoot() );
+ }
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mrExpData.mxSst = new XclExpSst();
+ mrExpData.mxObjMgr = std::make_shared<XclExpObjectManager>( GetRoot() );
+ mrExpData.mxFilterMgr = std::make_shared<XclExpFilterManager>( GetRoot() );
+ mrExpData.mxPTableMgr = std::make_shared<XclExpPivotTableManager>( GetRoot() );
+ // BIFF8: only one link manager for all sheets
+ mrExpData.mxLocLinkMgr = mrExpData.mxGlobLinkMgr;
+ mrExpData.mxDxfs = new XclExpDxfs( GetRoot() );
+ }
+
+ if( GetOutput() == EXC_OUTPUT_XML_2007 )
+ {
+ mrExpData.mxXmlPTableMgr = std::make_shared<XclExpXmlPivotTableManager>(GetRoot());
+ mrExpData.mxTablesMgr = std::make_shared<XclExpTablesManager>(GetRoot());
+
+ do
+ {
+ ScDocument& rDoc = GetDoc();
+ // Pass the model factory to OpCodeProvider, not the process
+ // service factory, otherwise a FormulaOpCodeMapperObj would be
+ // instantiated instead of a ScFormulaOpCodeMapperObj and the
+ // ScCompiler virtuals not be called! Which would be the case with
+ // the current (2013-01-24) rDoc.GetServiceManager()
+ const ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (!pShell)
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no object shell");
+ break;
+ }
+ uno::Reference< lang::XComponent > xComponent = pShell->GetModel();
+ if (!xComponent.is())
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no component");
+ break;
+ }
+ uno::Reference< lang::XMultiServiceFactory > xModelFactory( xComponent, uno::UNO_QUERY);
+ oox::xls::OpCodeProvider aOpCodeProvider(xModelFactory, false);
+ // Compiler mocks about non-matching ctor or conversion from
+ // Sequence<...> to Sequence<const ...> if directly created or passed,
+ // conversion through Any works around.
+ uno::Any aAny( aOpCodeProvider.getOoxParserMap());
+ uno::Sequence< const sheet::FormulaOpCodeMapEntry > aOpCodeMapping;
+ if (!(aAny >>= aOpCodeMapping))
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no OpCodeMap");
+ break;
+ }
+ ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
+ mrExpData.mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( aOpCodeMapping, true);
+ } while(false);
+ }
+
+ GetXFBuffer().Initialize();
+ GetNameManager().Initialize();
+}
+
+void XclExpRoot::InitializeTable( SCTAB nScTab )
+{
+ SetCurrScTab( nScTab );
+ if( GetBiff() == EXC_BIFF5 )
+ {
+ // local link manager per sheet
+ mrExpData.mxLocLinkMgr = new XclExpLinkManager( GetRoot() );
+ }
+}
+
+void XclExpRoot::InitializeSave()
+{
+ GetPalette().Finalize();
+ GetXFBuffer().Finalize();
+ GetDxfs().Finalize();
+}
+
+XclExpRecordRef XclExpRoot::CreateRecord( sal_uInt16 nRecId ) const
+{
+ XclExpRecordRef xRec;
+ switch( nRecId )
+ {
+ case EXC_ID_PALETTE: xRec = mrExpData.mxPalette; break;
+ case EXC_ID_FONTLIST: xRec = mrExpData.mxFontBfr; break;
+ case EXC_ID_FORMATLIST: xRec = mrExpData.mxNumFmtBfr; break;
+ case EXC_ID_XFLIST: xRec = mrExpData.mxXFBfr; break;
+ case EXC_ID_SST: xRec = mrExpData.mxSst; break;
+ case EXC_ID_EXTERNSHEET: xRec = GetLocalLinkMgrRef(); break;
+ case EXC_ID_NAME: xRec = mrExpData.mxNameMgr; break;
+ case EXC_ID_DXFS: xRec = mrExpData.mxDxfs; break;
+ }
+ OSL_ENSURE( xRec, "XclExpRoot::CreateRecord - unknown record ID or missing object" );
+ return xRec;
+}
+
+bool XclExpRoot::IsDocumentEncrypted() const
+{
+ // We need to encrypt the content when the document structure is protected.
+ const ScDocProtection* pDocProt = GetDoc().GetDocProtection();
+ if (pDocProt && pDocProt->isProtected() && pDocProt->isOptionEnabled(ScDocProtection::STRUCTURE))
+ return true;
+
+ // Whether password is entered directly into the save dialog.
+ return GetEncryptionData().hasElements();
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GenerateEncryptionData( std::u16string_view aPass )
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+
+ if ( !aPass.empty() && aPass.size() < 16 )
+ {
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ sal_uInt8 pnDocId[16];
+ rtl_random_getBytes( aRandomPool, pnDocId, 16 );
+
+ rtl_random_destroyPool( aRandomPool );
+
+ sal_uInt16 pnPasswd[16] = {};
+ for( size_t nChar = 0; nChar < aPass.size(); ++nChar )
+ pnPasswd[nChar] = aPass[nChar];
+
+ ::msfilter::MSCodec_Std97 aCodec;
+ aCodec.InitKey( pnPasswd, pnDocId );
+ aEncryptionData = aCodec.GetEncryptionData();
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GetEncryptionData() const
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = GetMedium().GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+ else
+ {
+ // try to get the encryption data from the password
+ const SfxStringItem* pPasswordItem = GetMedium().GetItemSet().GetItem(SID_PASSWORD, false);
+ if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() )
+ aEncryptionData = GenerateEncryptionData( pPasswordItem->GetValue() );
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GenerateDefaultEncryptionData()
+{
+ return GenerateEncryptionData( GetDefaultPassword() );
+}
+
+XclExpRootData::XclExpLinkMgrRef const & XclExpRoot::GetLocalLinkMgrRef() const
+{
+ return IsInGlobals() ? mrExpData.mxGlobLinkMgr : mrExpData.mxLocLinkMgr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestream.cxx b/sc/source/filter/excel/xestream.cxx
new file mode 100644
index 0000000000..4158fa2c15
--- /dev/null
+++ b/sc/source/filter/excel/xestream.cxx
@@ -0,0 +1,1289 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <utility>
+
+#include <filter/msfilter/util.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/sprintf.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/random.h>
+#include <sax/fshelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sot/storage.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+#include <docuno.hxx>
+#include <xestream.hxx>
+#include <xladdress.hxx>
+#include <xlstring.hxx>
+#include <xltools.hxx>
+#include <xeroot.hxx>
+#include <xestring.hxx>
+#include <xlstyle.hxx>
+#include <rangelst.hxx>
+#include <compiler.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <tokenstringcontext.hxx>
+#include <refreshtimerprotector.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <root.hxx>
+#include <sfx2/app.hxx>
+
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <excdoc.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/export/ColorExportUtils.hxx>
+#include <formula/grammar.hxx>
+#include <oox/ole/vbaexport.hxx>
+#include <excelvbaproject.hxx>
+
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <memory>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+
+#include <externalrefmgr.hxx>
+
+#define DEBUG_XL_ENCRYPTION 0
+
+using ::com::sun::star::uno::XInterface;
+using ::std::vector;
+
+using namespace com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::formula;
+using namespace ::oox;
+
+XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) :
+ mrStrm( rOutStrm ),
+ mrRoot( rRoot ),
+ mbUseEncrypter( false ),
+ mnMaxRecSize( nMaxRecSize ),
+ mnCurrMaxSize( 0 ),
+ mnMaxSliceSize( 0 ),
+ mnHeaderSize( 0 ),
+ mnCurrSize( 0 ),
+ mnSliceSize( 0 ),
+ mnPredictSize( 0 ),
+ mnLastSizePos( 0 ),
+ mbInRec( false )
+{
+ if( mnMaxRecSize == 0 )
+ mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8;
+ mnMaxContSize = mnMaxRecSize;
+}
+
+XclExpStream::~XclExpStream()
+{
+ mrStrm.FlushBuffer();
+}
+
+void XclExpStream::StartRecord( sal_uInt16 nRecId, std::size_t nRecSize )
+{
+ OSL_ENSURE( !mbInRec, "XclExpStream::StartRecord - another record still open" );
+ DisableEncryption();
+ mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
+ mnPredictSize = nRecSize;
+ mbInRec = true;
+ InitRecord( nRecId );
+ SetSliceSize( 0 );
+ EnableEncryption();
+}
+
+void XclExpStream::EndRecord()
+{
+ OSL_ENSURE( mbInRec, "XclExpStream::EndRecord - no record open" );
+ DisableEncryption();
+ UpdateRecSize();
+ mrStrm.Seek( STREAM_SEEK_TO_END );
+ mbInRec = false;
+}
+
+void XclExpStream::SetSliceSize( sal_uInt16 nSize )
+{
+ mnMaxSliceSize = nSize;
+ mnSliceSize = 0;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
+{
+ PrepareWrite( 1 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteSChar( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
+{
+ PrepareWrite( 1 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUChar( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
+{
+ PrepareWrite( 2 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteInt16( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
+{
+ PrepareWrite( 2 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUInt16( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteInt32( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUInt32( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( float fValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, fValue);
+ else
+ mrStrm.WriteFloat( fValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( double fValue )
+{
+ PrepareWrite( 8 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, fValue);
+ else
+ mrStrm.WriteDouble( fValue );
+ return *this;
+}
+
+std::size_t XclExpStream::Write( const void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( pData && (nBytes > 0) )
+ {
+ if( mbInRec )
+ {
+ const sal_uInt8* pBuffer = static_cast< const sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+ bool bValid = true;
+
+ while( bValid && (nBytesLeft > 0) )
+ {
+ std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
+ std::size_t nWriteRet = nWriteLen;
+ if (mbUseEncrypter && HasValidEncrypter())
+ {
+ OSL_ENSURE(nWriteLen > 0, "XclExpStream::Write: write length is 0!");
+ vector<sal_uInt8> aBytes(nWriteLen);
+ memcpy(aBytes.data(), pBuffer, nWriteLen);
+ mxEncrypter->EncryptBytes(mrStrm, aBytes);
+ // TODO: How do I check if all the bytes have been successfully written ?
+ }
+ else
+ {
+ nWriteRet = mrStrm.WriteBytes(pBuffer, nWriteLen);
+ bValid = (nWriteLen == nWriteRet);
+ OSL_ENSURE( bValid, "XclExpStream::Write - stream write error" );
+ }
+ pBuffer += nWriteRet;
+ nRet += nWriteRet;
+ nBytesLeft -= nWriteRet;
+ UpdateSizeVars( nWriteRet );
+ }
+ }
+ else
+ nRet = mrStrm.WriteBytes(pData, nBytes);
+ }
+ return nRet;
+}
+
+void XclExpStream::WriteZeroBytes( std::size_t nBytes )
+{
+ if( mbInRec )
+ {
+ std::size_t nBytesLeft = nBytes;
+ while( nBytesLeft > 0 )
+ {
+ std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
+ WriteRawZeroBytes( nWriteLen );
+ nBytesLeft -= nWriteLen;
+ UpdateSizeVars( nWriteLen );
+ }
+ }
+ else
+ WriteRawZeroBytes( nBytes );
+}
+
+void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes )
+{
+ if (!mbInRec)
+ // not in record.
+ return;
+
+ for (std::size_t i = 0; i < nBytes; ++i)
+ *this << sal_uInt8(0)/*nZero*/;
+}
+
+void XclExpStream::CopyFromStream(SvStream& rInStrm, sal_uInt64 const nBytes)
+{
+ sal_uInt64 const nRemaining(rInStrm.remainingSize());
+ sal_uInt64 nBytesLeft = ::std::min(nBytes, nRemaining);
+ if( nBytesLeft <= 0 )
+ return;
+
+ const std::size_t nMaxBuffer = 4096;
+ std::unique_ptr<sal_uInt8[]> pBuffer(
+ new sal_uInt8[ ::std::min<std::size_t>(nBytesLeft, nMaxBuffer) ]);
+ bool bValid = true;
+
+ while( bValid && (nBytesLeft > 0) )
+ {
+ std::size_t nWriteLen = ::std::min<std::size_t>(nBytesLeft, nMaxBuffer);
+ rInStrm.ReadBytes(pBuffer.get(), nWriteLen);
+ std::size_t nWriteRet = Write( pBuffer.get(), nWriteLen );
+ bValid = (nWriteLen == nWriteRet);
+ nBytesLeft -= nWriteRet;
+ }
+}
+
+void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags )
+{
+ SetSliceSize( 0 );
+ nFlags &= EXC_STRF_16BIT; // repeat only 16bit flag
+ sal_uInt16 nCharLen = nFlags ? 2 : 1;
+
+ for( const auto& rItem : rBuffer )
+ {
+ if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
+ {
+ StartContinue();
+ operator<<( nFlags );
+ }
+ if( nCharLen == 2 )
+ operator<<( rItem );
+ else
+ operator<<( static_cast< sal_uInt8 >( rItem ) );
+ }
+}
+
+// Xcl has an obscure sense of whether starting a new record or not,
+// and crashes if it encounters the string header at the very end of a record.
+// Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
+void XclExpStream::WriteByteString( const OString& rString )
+{
+ SetSliceSize( 0 );
+ std::size_t nLen = ::std::min< std::size_t >( rString.getLength(), 0x00FF );
+ nLen = ::std::min< std::size_t >( nLen, 0xFF );
+
+ sal_uInt16 nLeft = PrepareWrite();
+ if( mbInRec && (nLeft <= 1) )
+ StartContinue();
+
+ operator<<( static_cast< sal_uInt8 >( nLen ) );
+ Write( rString.getStr(), nLen );
+}
+
+void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
+{
+ SetSliceSize( 0 );
+ Write( rBuffer.data(), rBuffer.size() );
+}
+
+void XclExpStream::SetEncrypter( XclExpEncrypterRef const & xEncrypter )
+{
+ mxEncrypter = xEncrypter;
+}
+
+bool XclExpStream::HasValidEncrypter() const
+{
+ return mxEncrypter && mxEncrypter->IsValid();
+}
+
+void XclExpStream::EnableEncryption( bool bEnable )
+{
+ mbUseEncrypter = bEnable && HasValidEncrypter();
+}
+
+void XclExpStream::DisableEncryption()
+{
+ EnableEncryption(false);
+}
+
+void XclExpStream::SetSvStreamPos(sal_uInt64 const nPos)
+{
+ OSL_ENSURE( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
+ mbInRec ? 0 : mrStrm.Seek( nPos );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpStream::InitRecord( sal_uInt16 nRecId )
+{
+ mrStrm.Seek( STREAM_SEEK_TO_END );
+ mrStrm.WriteUInt16( nRecId );
+
+ mnLastSizePos = mrStrm.Tell();
+ mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( mnPredictSize, mnCurrMaxSize ) );
+ mrStrm.WriteUInt16( mnHeaderSize );
+ mnCurrSize = mnSliceSize = 0;
+}
+
+void XclExpStream::UpdateRecSize()
+{
+ if( mnCurrSize != mnHeaderSize )
+ {
+ mrStrm.Seek( mnLastSizePos );
+ mrStrm.WriteUInt16( mnCurrSize );
+ }
+}
+
+void XclExpStream::UpdateSizeVars( std::size_t nSize )
+{
+ OSL_ENSURE( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" );
+ mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize );
+
+ if( mnMaxSliceSize > 0 )
+ {
+ OSL_ENSURE( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" );
+ mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize );
+ if( mnSliceSize >= mnMaxSliceSize )
+ mnSliceSize = 0;
+ }
+}
+
+void XclExpStream::StartContinue()
+{
+ UpdateRecSize();
+ mnCurrMaxSize = mnMaxContSize;
+ mnPredictSize -= mnCurrSize;
+ InitRecord( EXC_ID_CONT );
+}
+
+void XclExpStream::PrepareWrite( sal_uInt16 nSize )
+{
+ if( mbInRec )
+ {
+ if( (mnCurrSize + nSize > mnCurrMaxSize) ||
+ ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
+ StartContinue();
+ UpdateSizeVars( nSize );
+ }
+}
+
+sal_uInt16 XclExpStream::PrepareWrite()
+{
+ sal_uInt16 nRet = 0;
+ if( mbInRec )
+ {
+ if( (mnCurrSize >= mnCurrMaxSize) ||
+ ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
+ StartContinue();
+ UpdateSizeVars( 0 );
+
+ nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
+ }
+ return nRet;
+}
+
+void XclExpStream::WriteRawZeroBytes( std::size_t nBytes )
+{
+ const sal_uInt32 nData = 0;
+ std::size_t nBytesLeft = nBytes;
+ while( nBytesLeft >= sizeof( nData ) )
+ {
+ mrStrm.WriteUInt32( nData );
+ nBytesLeft -= sizeof( nData );
+ }
+ if( nBytesLeft )
+ mrStrm.WriteBytes(&nData, nBytesLeft);
+}
+
+XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
+ mnOldPos(STREAM_SEEK_TO_END),
+ mbValid(false)
+{
+ Sequence< NamedValue > aEncryptionData = rRoot.GetEncryptionData();
+ if( !aEncryptionData.hasElements() )
+ // Empty password. Get the default biff8 password.
+ aEncryptionData = XclExpRoot::GenerateDefaultEncryptionData();
+ Init( aEncryptionData );
+}
+
+XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
+{
+}
+
+void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const
+{
+ if ( sizeof( mpnSaltDigest ) == 16 )
+ memcpy( pnSaltDigest, mpnSaltDigest, 16 );
+}
+
+void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt[16] ) const
+{
+ if ( sizeof( mpnSalt ) == 16 )
+ memcpy( pnSalt, mpnSalt, 16 );
+}
+
+void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId[16] ) const
+{
+ if ( sizeof( mpnDocId ) == 16 )
+ memcpy( pnDocId, mpnDocId, 16 );
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData )
+{
+ vector<sal_uInt8> aByte { nData };
+ EncryptBytes(rStrm, aByte);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData )
+{
+ ::std::vector<sal_uInt8> pnBytes
+ {
+ o3tl::narrowing<sal_uInt8>(nData & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF)
+ };
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData )
+{
+ ::std::vector<sal_uInt8> pnBytes
+ {
+ o3tl::narrowing<sal_uInt8>(nData & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 16) & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 24) & 0xFF)
+ };
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue )
+{
+ ::std::vector<sal_uInt8> pnBytes(4);
+ memcpy(pnBytes.data(), &fValue, 4);
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue )
+{
+ ::std::vector<sal_uInt8> pnBytes(8);
+ memcpy(pnBytes.data(), &fValue, 8);
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt8>(nData));
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt16>(nData));
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt32>(nData));
+}
+
+void XclExpBiff8Encrypter::Init( const Sequence< NamedValue >& rEncryptionData )
+{
+ mbValid = false;
+
+ if( !maCodec.InitCodec( rEncryptionData ) )
+ return;
+
+ maCodec.GetDocId( mpnDocId );
+
+ // generate the salt here
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ rtl_random_getBytes( aRandomPool, mpnSalt, 16 );
+ rtl_random_destroyPool( aRandomPool );
+
+ memset( mpnSaltDigest, 0, sizeof( mpnSaltDigest ) );
+
+ // generate salt hash.
+ ::msfilter::MSCodec_Std97 aCodec;
+ aCodec.InitCodec( rEncryptionData );
+ aCodec.CreateSaltDigest( mpnSalt, mpnSaltDigest );
+
+ // verify to make sure it's in good shape.
+ mbValid = maCodec.VerifyKey( mpnSalt, mpnSaltDigest );
+}
+
+sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes )
+{
+ sal_uInt64 nStrmPos = rStrm.Tell();
+ sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos);
+ sal_uInt32 nBlockPos = GetBlockPos(nStrmPos);
+
+ SAL_INFO("sc.filter", "XclExpBiff8Encrypter::EncryptBytes: stream pos = "
+ << nStrmPos << " offset in block = " << nBlockOffset
+ << " block pos = " << nBlockPos);
+
+ sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() );
+ if (nSize == 0)
+ return;
+
+#if DEBUG_XL_ENCRYPTION
+ fprintf(stdout, "RAW: ");
+ for (sal_uInt16 i = 0; i < nSize; ++i)
+ fprintf(stdout, "%2.2X ", aBytes[i]);
+ fprintf(stdout, "\n");
+#endif
+
+ if (mnOldPos != nStrmPos)
+ {
+ sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos);
+ sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos);
+
+ if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) )
+ {
+ maCodec.InitCipher(nBlockPos);
+ nOldOffset = 0;
+ }
+
+ if (nBlockOffset > nOldOffset)
+ maCodec.Skip(nBlockOffset - nOldOffset);
+ }
+
+ sal_uInt16 nBytesLeft = nSize;
+ sal_uInt16 nPos = 0;
+ while (nBytesLeft > 0)
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset;
+ sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft);
+
+ bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes);
+ OSL_ENSURE(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
+
+ std::size_t nRet = rStrm.WriteBytes(&aBytes[nPos], nEncBytes);
+ OSL_ENSURE(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
+
+ nStrmPos = rStrm.Tell();
+ nBlockOffset = GetOffsetInBlock(nStrmPos);
+ nBlockPos = GetBlockPos(nStrmPos);
+ if (nBlockOffset == 0)
+ maCodec.InitCipher(nBlockPos);
+
+ nBytesLeft -= nEncBytes;
+ nPos += nEncBytes;
+ }
+ mnOldPos = nStrmPos;
+}
+
+static const char* lcl_GetErrorString( FormulaError nScErrCode )
+{
+ sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
+ switch( nXclErrCode )
+ {
+ case EXC_ERR_NULL: return "#NULL!";
+ case EXC_ERR_DIV0: return "#DIV/0!";
+ case EXC_ERR_VALUE: return "#VALUE!";
+ case EXC_ERR_REF: return "#REF!";
+ case EXC_ERR_NAME: return "#NAME?";
+ case EXC_ERR_NUM: return "#NUM!";
+ case EXC_ERR_NA:
+ default: return "#N/A";
+ }
+}
+
+void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& rsType, OUString& rsValue )
+{
+ sc::FormulaResultValue aResValue = rCell.GetResult();
+
+ switch (aResValue.meType)
+ {
+ case sc::FormulaResultValue::Error:
+ rsType = "e";
+ rsValue = ToOUString(lcl_GetErrorString(aResValue.mnError));
+ break;
+ case sc::FormulaResultValue::Value:
+ rsType = rCell.GetFormatType() == SvNumFormatType::LOGICAL
+ && (aResValue.mfValue == 0.0 || aResValue.mfValue == 1.0)
+ ? "b"
+ : "n";
+ rsValue = OUString::number(aResValue.mfValue);
+ break;
+ case sc::FormulaResultValue::String:
+ rsType = "str";
+ rsValue = rCell.GetString().getString();
+ break;
+ case sc::FormulaResultValue::Invalid:
+ default:
+ // TODO : double-check this to see if this is correct.
+ rsType = "inlineStr";
+ rsValue = rCell.GetString().getString();
+ }
+}
+
+OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId )
+{
+ OUStringBuffer sBuf;
+ if( sStreamDir )
+ sBuf.appendAscii( sStreamDir );
+ sBuf.appendAscii( sStream );
+ if( nId )
+ sBuf.append( nId );
+ if( strstr(sStream, "vml") )
+ sBuf.append( ".vml" );
+ else
+ sBuf.append( ".xml" );
+ return sBuf.makeStringAndClear();
+}
+
+OString XclXmlUtils::ToOString( const Color& rColor )
+{
+ char buf[9];
+ o3tl::sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetAlpha(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
+ buf[8] = '\0';
+ return buf;
+}
+
+OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const ScAddress& rAddress )
+{
+ rAddress.Format(s, ScRefFlags::VALID, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1));
+ return s;
+}
+
+OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
+{
+ if(rBuffer.empty())
+ return OString();
+
+ const sal_uInt16* pBuffer = rBuffer.data();
+ return OString(
+ reinterpret_cast<sal_Unicode const *>(pBuffer), rBuffer.size(),
+ RTL_TEXTENCODING_UTF8);
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRange& rRange, bool bFullAddressNotation )
+{
+ OUString sRange(rRange.Format( rDoc, ScRefFlags::VALID,
+ ScAddress::Details( FormulaGrammar::CONV_XL_A1 ),
+ bFullAddressNotation ) );
+ return sRange.toUtf8();
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList )
+{
+ OUString s;
+ rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' ');
+ return s.toUtf8();
+}
+
+static ScAddress lcl_ToAddress( const XclAddress& rAddress )
+{
+ return ScAddress( rAddress.mnCol, rAddress.mnRow, 0 );
+}
+
+OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const XclAddress& rAddress )
+{
+ return ToOString( s, lcl_ToAddress( rAddress ));
+}
+
+OString XclXmlUtils::ToOString( const XclExpString& s )
+{
+ OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
+ return ToOString( s.GetUnicodeBuffer() );
+}
+
+static ScRange lcl_ToRange( const XclRange& rRange )
+{
+ ScRange aRange;
+
+ aRange.aStart = lcl_ToAddress( rRange.maFirst );
+ aRange.aEnd = lcl_ToAddress( rRange.maLast );
+
+ return aRange;
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const XclRangeList& rRanges )
+{
+ ScRangeList aRanges;
+ for( const auto& rRange : rRanges )
+ {
+ aRanges.push_back( lcl_ToRange( rRange ) );
+ }
+ return ToOString( rDoc, aRanges );
+}
+
+OUString XclXmlUtils::ToOUString( const char* s )
+{
+ return OUString( s, static_cast<sal_Int32>(strlen( s )), RTL_TEXTENCODING_ASCII_US );
+}
+
+OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength )
+{
+ if( nLength == -1 || ( nLength > (static_cast<sal_Int32>(rBuf.size()) - nStart) ) )
+ nLength = (rBuf.size() - nStart);
+
+ return nLength > 0
+ ? OUString(
+ reinterpret_cast<sal_Unicode const *>(&rBuf[nStart]), nLength)
+ : OUString();
+}
+
+OUString XclXmlUtils::ToOUString(
+ sc::CompileFormulaContext& rCtx, const ScAddress& rAddress, const ScTokenArray* pTokenArray,
+ FormulaError nErrCode )
+{
+ ScCompiler aCompiler( rCtx, rAddress, const_cast<ScTokenArray&>(*pTokenArray));
+
+ /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
+ aCompiler.SetGrammar(FormulaGrammar::GRAM_OOXML);
+
+ sal_Int32 nLen = pTokenArray->GetLen();
+ OUStringBuffer aBuffer( nLen ? (nLen * 5) : 8 );
+ if (nLen)
+ aCompiler.CreateStringFromTokenArray( aBuffer );
+ else
+ {
+ if (nErrCode != FormulaError::NONE)
+ aCompiler.AppendErrorConstant( aBuffer, nErrCode);
+ else
+ {
+ // No code SHOULD be an "error cell", assert caller thought of that
+ // and it really is.
+ assert(!"No code and no error.");
+ }
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString XclXmlUtils::ToOUString( const XclExpString& s )
+{
+ OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
+ return ToOUString( s.GetUnicodeBuffer() );
+}
+
+static const char* lcl_GetUnderlineStyle( FontLineStyle eUnderline, bool& bHaveUnderline )
+{
+ bHaveUnderline = true;
+ switch( eUnderline )
+ {
+ // OOXTODO: doubleAccounting, singleAccounting
+ // OOXTODO: what should be done with the other FontLineStyle values?
+ case LINESTYLE_SINGLE: return "single";
+ case LINESTYLE_DOUBLE: return "double";
+ case LINESTYLE_NONE:
+ default: bHaveUnderline = false; return "none";
+ }
+}
+
+static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment )
+{
+ bHaveAlignment = true;
+ switch( eEscapement )
+ {
+ case SvxEscapement::Superscript: return "superscript";
+ case SvxEscapement::Subscript: return "subscript";
+ case SvxEscapement::Off:
+ default: bHaveAlignment = false; return "baseline";
+ }
+}
+
+sax_fastparser::FSHelperPtr XclXmlUtils::WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nFontId )
+{
+ bool bHaveUnderline, bHaveVertAlign;
+ const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline );
+ const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign );
+
+ if (rFontData.mnWeight > 400)
+ pStream->singleElement(XML_b, XML_val, ToPsz( true ));
+ if (rFontData.mbItalic)
+ pStream->singleElement(XML_i, XML_val, ToPsz( true ));
+ if (rFontData.mbStrikeout)
+ pStream->singleElement(XML_strike, XML_val, ToPsz( true ));
+ // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting
+ // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting
+ if (rFontData.mbOutline)
+ pStream->singleElement(XML_outline, XML_val, ToPsz( true ));
+ if (rFontData.mbShadow)
+ pStream->singleElement(XML_shadow, XML_val, ToPsz( true ));
+ if (bHaveUnderline)
+ pStream->singleElement(XML_u, XML_val, pUnderline);
+ if (bHaveVertAlign)
+ pStream->singleElement(XML_vertAlign, XML_val, pVertAlign);
+ pStream->singleElement(XML_sz, XML_val, OString::number( rFontData.mnHeight / 20.0 )); // Twips->Pt
+
+ auto& rComplexColor = rFontData.maComplexColor;
+ if (rComplexColor.isValidThemeType())
+ {
+ sal_Int32 nTheme = oox::convertThemeColorTypeToExcelThemeNumber(rComplexColor.getThemeColorType());
+ double fTintShade = oox::convertColorTransformsToTintOrShade(rComplexColor);
+ pStream->singleElement(XML_color,
+ XML_theme, OString::number(nTheme),
+ XML_tint, sax_fastparser::UseIf(OString::number(fTintShade), fTintShade != 0.0));
+ }
+ else if (rComplexColor.getFinalColor() != Color( ColorAlpha, 0, 0xFF, 0xFF, 0xFF))
+ {
+ pStream->singleElement(XML_color,
+ XML_rgb, XclXmlUtils::ToOString(rComplexColor.getFinalColor()));
+ }
+ pStream->singleElement(nFontId, XML_val, rFontData.maName);
+ pStream->singleElement(XML_family, XML_val, OString::number( rFontData.mnFamily ));
+ if (rFontData.mnCharSet != 0)
+ pStream->singleElement(XML_charset, XML_val, OString::number(rFontData.mnCharSet));
+
+ return pStream;
+}
+
+XclExpXmlStream::XclExpXmlStream( const uno::Reference< XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate )
+ : XmlFilterBase( rCC ),
+ mpRoot( nullptr ),
+ mbExportVBA(bExportVBA),
+ mbExportTemplate(bExportTemplate)
+{
+}
+
+XclExpXmlStream::~XclExpXmlStream()
+{
+ assert(maStreams.empty() && "Forgotten PopStream()?");
+}
+
+sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream()
+{
+ OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
+ return maStreams.top();
+}
+
+void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr const & aStream )
+{
+ maStreams.push( aStream );
+}
+
+void XclExpXmlStream::PopStream()
+{
+ OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
+ maStreams.pop();
+}
+
+sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath )
+{
+ if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
+ return sax_fastparser::FSHelperPtr();
+ return maOpenedStreamMap[ sPath ].second;
+}
+
+void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr, std::u16string_view sVal)
+{
+ GetCurrentStream()->write(" ")->writeId(nAttr)->write("=\"")->writeEscaped(sVal)->write("\"");
+}
+
+sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream (
+ const OUString& sFullStream,
+ std::u16string_view sRelativeStream,
+ const uno::Reference< XOutputStream >& xParentRelation,
+ const char* sContentType,
+ std::u16string_view sRelationshipType,
+ OUString* pRelationshipId )
+{
+ OUString sRelationshipId;
+ if (xParentRelation.is())
+ sRelationshipId = addRelation( xParentRelation, OUString(sRelationshipType), sRelativeStream );
+ else
+ sRelationshipId = addRelation( OUString(sRelationshipType), sRelativeStream );
+
+ if( pRelationshipId )
+ *pRelationshipId = sRelationshipId;
+
+ sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
+
+ maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p );
+
+ return p;
+}
+
+bool XclExpXmlStream::importDocument() noexcept
+{
+ return false;
+}
+
+oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
+{
+ return nullptr;
+}
+
+const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
+{
+ return nullptr;
+}
+
+oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
+{
+ return oox::drawingml::table::TableStyleListPtr();
+}
+
+oox::drawingml::chart::ChartConverter* XclExpXmlStream::getChartConverter()
+{
+ // DO NOT CALL
+ return nullptr;
+}
+
+ScDocShell* XclExpXmlStream::getDocShell()
+{
+ uno::Reference< XInterface > xModel( getModel(), UNO_QUERY );
+
+ ScModelObj *pObj = comphelper::getFromUnoTunnel < ScModelObj >( xModel );
+
+ if ( pObj )
+ return static_cast < ScDocShell* >( pObj->GetEmbeddedObject() );
+
+ return nullptr;
+}
+
+bool XclExpXmlStream::exportDocument()
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+ ScRefreshTimerProtector aProt(rDoc.GetRefreshTimerControlAddress());
+
+ const bool bValidateTabNames = officecfg::Office::Calc::Filter::Export::MS_Excel::TruncateLongSheetNames::get();
+ std::vector<OUString> aOriginalTabNames;
+ if (bValidateTabNames)
+ {
+ validateTabNames(aOriginalTabNames);
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator = getStatusIndicator();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->start(ScResId(STR_SAVE_DOC), 100);
+
+ // NOTE: Don't use SotStorage or SvStream any more, and never call
+ // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
+ // Instead, write via XOutputStream instance.
+ tools::SvRef<SotStorage> rStorage = static_cast<SotStorage*>(nullptr);
+ drawingml::DrawingML::ResetMlCounters();
+
+ auto& rGraphicExportCache = drawingml::GraphicExportCache::get();
+
+ rGraphicExportCache.push();
+
+ XclExpRootData aData(
+ EXC_BIFF8, *pShell->GetMedium (), rStorage, rDoc,
+ msfilter::util::getBestTextEncodingFromLocale(
+ Application::GetSettings().GetLanguageTag().getLocale()));
+ aData.meOutput = EXC_OUTPUT_XML_2007;
+ aData.maXclMaxPos.Set( EXC_MAXCOL_XML_2007, EXC_MAXROW_XML_2007, EXC_MAXTAB_XML_2007 );
+ aData.maMaxPos.SetCol( ::std::min( aData.maScMaxPos.Col(), aData.maXclMaxPos.Col() ) );
+ aData.maMaxPos.SetRow( ::std::min( aData.maScMaxPos.Row(), aData.maXclMaxPos.Row() ) );
+ aData.maMaxPos.SetTab( ::std::min( aData.maScMaxPos.Tab(), aData.maXclMaxPos.Tab() ) );
+ aData.mpCompileFormulaCxt = std::make_shared<sc::CompileFormulaContext>(rDoc);
+ // set target path to get correct relative links to target document, not source
+ INetURLObject aPath(getFileUrl());
+ aData.maBasePath = OUString("file:///" + aPath.GetPath() + "\\").replace('\\', '/')
+ // fix for Linux
+ .replaceFirst("file:////", "file:///");
+
+ XclExpRoot aRoot( aData );
+
+ mpRoot = &aRoot;
+ aRoot.GetOldRoot().pER = &aRoot;
+ aRoot.GetOldRoot().eDateiTyp = Biff8;
+ // Get the viewsettings before processing
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ pViewData->WriteExtOptions( mpRoot->GetExtDocOptions() );
+ else
+ {
+ // Try to get ScViewData through the current ScDocShell
+ ScTabViewShell* pTabViewShell = pShell->GetBestViewShell( false );
+ if ( pTabViewShell )
+ {
+ pViewData = &pTabViewShell->GetViewData();
+ pViewData->WriteExtOptions( mpRoot->GetExtDocOptions() );
+ }
+ }
+
+ static constexpr OUString workbook = u"xl/workbook.xml"_ustr;
+ const char* pWorkbookContentType = nullptr;
+ if (mbExportVBA)
+ {
+ if (mbExportTemplate)
+ {
+ pWorkbookContentType = "application/vnd.ms-excel.template.macroEnabled.main+xml";
+ }
+ else
+ {
+ pWorkbookContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
+ }
+ }
+ else
+ {
+ if (mbExportTemplate)
+ {
+ pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
+ }
+ else
+ {
+ pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
+ }
+ }
+
+ PushStream( CreateOutputStream( workbook, workbook,
+ uno::Reference <XOutputStream>(),
+ pWorkbookContentType,
+ oox::getRelationship(Relationship::OFFICEDOCUMENT) ) );
+
+ if (mbExportVBA)
+ {
+ VbaExport aExport(getModel());
+ if (aExport.containsVBAProject())
+ {
+ SvMemoryStream aVbaStream(4096, 4096);
+ tools::SvRef<SotStorage> pVBAStorage(new SotStorage(aVbaStream));
+ aExport.exportVBA( pVBAStorage.get() );
+ aVbaStream.Seek(0);
+ css::uno::Reference<css::io::XInputStream> xVBAStream(
+ new utl::OInputStreamWrapper(aVbaStream));
+ css::uno::Reference<css::io::XOutputStream> xVBAOutput =
+ openFragmentStream("xl/vbaProject.bin", "application/vnd.ms-office.vbaProject");
+ comphelper::OStorageHelper::CopyInputToOutput(xVBAStream, xVBAOutput);
+
+ addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin");
+ }
+ }
+
+ // destruct at the end of the block
+ {
+ ExcDocument aDocRoot( aRoot );
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(10);
+ aDocRoot.ReadDoc();
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(40);
+ aDocRoot.WriteXml( *this );
+ rDoc.GetExternalRefManager()->disableSkipUnusedFileIds();
+ }
+
+ PopStream();
+ // Free all FSHelperPtr, to flush data before committing storage
+ for (auto& entry : maOpenedStreamMap)
+ {
+ if (!entry.second.second)
+ continue;
+ entry.second.second->endDocument();
+ }
+ maOpenedStreamMap.clear();
+
+ commitStorage();
+
+ if (bValidateTabNames)
+ {
+ restoreTabNames(aOriginalTabNames);
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ mpRoot = nullptr;
+
+ rGraphicExportCache.pop();
+
+ return true;
+}
+
+::oox::ole::VbaProject* XclExpXmlStream::implCreateVbaProject() const
+{
+ return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
+}
+
+OUString XclExpXmlStream::getImplementationName()
+{
+ return "TODO";
+}
+
+void XclExpXmlStream::validateTabNames(std::vector<OUString>& aOriginalTabNames)
+{
+ const int MAX_TAB_NAME_LENGTH = 31;
+
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ // get original names
+ aOriginalTabNames.resize(rDoc.GetTableCount());
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ rDoc.GetName(nTab, aOriginalTabNames[nTab]);
+ }
+
+ // new tab names
+ std::vector<OUString> aNewTabNames;
+ aNewTabNames.reserve(rDoc.GetTableCount());
+
+ // check and rename
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ const OUString& rOriginalName = aOriginalTabNames[nTab];
+ if (rOriginalName.getLength() > MAX_TAB_NAME_LENGTH)
+ {
+ OUString aNewName;
+
+ // let's try just truncate "<first 31 chars>"
+ if (aNewName.isEmpty())
+ {
+ aNewName = rOriginalName.copy(0, MAX_TAB_NAME_LENGTH);
+ if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
+ aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
+ {
+ // was found => let's use another tab name
+ aNewName.clear();
+ }
+ }
+
+ // let's try "<first N chars>-XXX" template
+ for (int digits=1; digits<10 && aNewName.isEmpty(); digits++)
+ {
+ const int rangeStart = pow(10, digits - 1);
+ const int rangeEnd = pow(10, digits);
+
+ for (int i=rangeStart; i<rangeEnd && aNewName.isEmpty(); i++)
+ {
+ aNewName = OUString::Concat(rOriginalName.subView(0, MAX_TAB_NAME_LENGTH - 1 - digits)) + "-" + OUString::number(i);
+ if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
+ aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
+ {
+ // was found => let's use another tab name
+ aNewName.clear();
+ }
+ }
+ }
+
+ if (!aNewName.isEmpty())
+ {
+ // new name was created => rename
+ renameTab(nTab, aNewName);
+ aNewTabNames.push_back(aNewName);
+ }
+ else
+ {
+ // default: do not rename
+ aNewTabNames.push_back(rOriginalName);
+ }
+ }
+ else
+ {
+ // default: do not rename
+ aNewTabNames.push_back(rOriginalName);
+ }
+ }
+}
+
+void XclExpXmlStream::restoreTabNames(const std::vector<OUString>& aOriginalTabNames)
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ const OUString& rOriginalName = aOriginalTabNames[nTab];
+
+ OUString rModifiedName;
+ rDoc.GetName(nTab, rModifiedName);
+
+ if (rOriginalName != rModifiedName)
+ {
+ renameTab(nTab, rOriginalName);
+ }
+ }
+}
+
+void XclExpXmlStream::renameTab(SCTAB aTab, OUString aNewName)
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ bool bAutoCalcShellDisabled = rDoc.IsAutoCalcShellDisabled();
+ bool bIdleEnabled = rDoc.IsIdleEnabled();
+
+ rDoc.SetAutoCalcShellDisabled( true );
+ rDoc.EnableIdle(false);
+
+ if (rDoc.RenameTab(aTab, aNewName))
+ {
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScTablesChanged));
+ }
+
+ rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
+ rDoc.EnableIdle(bIdleEnabled);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestring.cxx b/sc/source/filter/excel/xestring.cxx
new file mode 100644
index 0000000000..295f377095
--- /dev/null
+++ b/sc/source/filter/excel/xestring.cxx
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <cassert>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <tools/solar.h>
+#include <xlstyle.hxx>
+#include <xestyle.hxx>
+#include <xestream.hxx>
+#include <xestring.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox;
+
+namespace {
+
+// compare vectors
+
+/** Compares two vectors.
+ @return A negative value, if rLeft<rRight; or a positive value, if rLeft>rRight;
+ or 0, if rLeft==rRight. */
+template< typename Type >
+int lclCompareVectors( const ::std::vector< Type >& rLeft, const ::std::vector< Type >& rRight )
+{
+ int nResult = 0;
+
+ // 1st: compare all elements of the vectors
+ auto [aItL, aItR] = std::mismatch(rLeft.begin(), rLeft.end(), rRight.begin(), rRight.end());
+ if ((aItL != rLeft.end()) && (aItR != rRight.end()))
+ nResult = static_cast< int >( *aItL ) - static_cast< int >( *aItR );
+ else
+ // 2nd: compare the vector sizes. Shorter vector is less
+ nResult = static_cast< int >( rLeft.size() ) - static_cast< int >( rRight.size() );
+
+ return nResult;
+}
+
+// hashing helpers
+
+/** Base class for value hashers.
+ @descr These function objects are used to hash any value to a sal_uInt32 value. */
+template< typename Type >
+struct XclHasher {};
+
+template< typename Type >
+struct XclDirectHasher : public XclHasher< Type >
+{
+ sal_uInt32 operator()( Type nVal ) const { return nVal; }
+};
+
+struct XclFormatRunHasher : public XclHasher< const XclFormatRun& >
+{
+ sal_uInt32 operator()( const XclFormatRun& rRun ) const
+ { return (rRun.mnChar << 8) ^ rRun.mnFontIdx; }
+};
+
+/** Calculates a hash value from a vector.
+ @descr Uses the passed hasher function object to calculate hash values from
+ all vector elements. */
+template< typename Type, typename ValueHasher >
+sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec, const ValueHasher& rHasher )
+{
+ sal_uInt32 nHash = rVec.size();
+ for( const auto& rItem : rVec )
+ nHash = (nHash * 31) + rHasher( rItem );
+ return static_cast< sal_uInt16 >( nHash ^ (nHash >> 16) );
+}
+
+/** Calculates a hash value from a vector. Uses XclDirectHasher to hash the vector elements. */
+template< typename Type >
+sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec )
+{
+ return lclHashVector( rVec, XclDirectHasher< Type >() );
+}
+
+} // namespace
+
+// constructors ---------------------------------------------------------------
+
+XclExpString::XclExpString( XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( 0, nFlags, nMaxLen, true );
+}
+
+XclExpString::XclExpString( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Assign( rString, nFlags, nMaxLen );
+}
+
+// assign ---------------------------------------------------------------------
+
+void XclExpString::Assign( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Build( rString.getStr(), rString.getLength(), nFlags, nMaxLen );
+}
+
+void XclExpString::Assign( sal_Unicode cChar )
+{
+ Build( &cChar, 1, XclStrFlags::NONE, EXC_STR_MAXLEN );
+}
+
+void XclExpString::AssignByte(
+ std::u16string_view rString, rtl_TextEncoding eTextEnc, XclStrFlags nFlags,
+ sal_uInt16 nMaxLen )
+{
+ // length may differ from length of rString
+ OString aByteStr(OUStringToOString(rString, eTextEnc));
+ Build(aByteStr.getStr(), aByteStr.getLength(), nFlags, nMaxLen);
+}
+
+// append ---------------------------------------------------------------------
+
+void XclExpString::Append( std::u16string_view rString )
+{
+ BuildAppend( rString );
+}
+
+void XclExpString::AppendByte( std::u16string_view rString, rtl_TextEncoding eTextEnc )
+{
+ if (!rString.empty())
+ {
+ // length may differ from length of rString
+ OString aByteStr(OUStringToOString(rString, eTextEnc));
+ BuildAppend(aByteStr);
+ }
+}
+
+void XclExpString::AppendByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc )
+{
+ if( !cChar )
+ {
+ char cByteChar = 0;
+ BuildAppend( std::string_view(&cByteChar, 1) );
+ }
+ else
+ {
+ OString aByteStr( &cChar, 1, eTextEnc ); // length may be >1
+ BuildAppend( aByteStr );
+ }
+}
+
+// formatting runs ------------------------------------------------------------
+
+void XclExpString::AppendFormat( sal_uInt16 nChar, sal_uInt16 nFontIdx, bool bDropDuplicate )
+{
+ OSL_ENSURE( maFormats.empty() || (maFormats.back().mnChar < nChar), "XclExpString::AppendFormat - invalid char index" );
+ size_t nMaxSize = static_cast< size_t >( mbIsBiff8 ? EXC_STR_MAXLEN : EXC_STR_MAXLEN_8BIT );
+ if( maFormats.empty() || ((maFormats.size() < nMaxSize) && (!bDropDuplicate || (maFormats.back().mnFontIdx != nFontIdx))) )
+ maFormats.emplace_back( nChar, nFontIdx );
+}
+
+void XclExpString::AppendTrailingFormat( sal_uInt16 nFontIdx )
+{
+ AppendFormat( mnLen, nFontIdx, false );
+}
+
+void XclExpString::LimitFormatCount( sal_uInt16 nMaxCount )
+{
+ if( maFormats.size() > nMaxCount )
+ maFormats.erase( maFormats.begin() + nMaxCount, maFormats.end() );
+}
+
+sal_uInt16 XclExpString::GetLeadingFont()
+{
+ sal_uInt16 nFontIdx = EXC_FONT_NOTFOUND;
+ if( !maFormats.empty() && (maFormats.front().mnChar == 0) )
+ {
+ nFontIdx = maFormats.front().mnFontIdx;
+ }
+ return nFontIdx;
+}
+
+sal_uInt16 XclExpString::RemoveLeadingFont()
+{
+ sal_uInt16 nFontIdx = GetLeadingFont();
+ if( nFontIdx != EXC_FONT_NOTFOUND )
+ {
+ maFormats.erase( maFormats.begin() );
+ }
+ return nFontIdx;
+}
+
+bool XclExpString::IsEqual( const XclExpString& rCmp ) const
+{
+ return
+ (mnLen == rCmp.mnLen) &&
+ (mbIsBiff8 == rCmp.mbIsBiff8) &&
+ (mbIsUnicode == rCmp.mbIsUnicode) &&
+ (mbWrapped == rCmp.mbWrapped) &&
+ (
+ ( mbIsBiff8 && (maUniBuffer == rCmp.maUniBuffer)) ||
+ (!mbIsBiff8 && (maCharBuffer == rCmp.maCharBuffer))
+ ) &&
+ (maFormats == rCmp.maFormats);
+}
+
+bool XclExpString::IsLessThan( const XclExpString& rCmp ) const
+{
+ int nResult = mbIsBiff8 ?
+ lclCompareVectors( maUniBuffer, rCmp.maUniBuffer ) :
+ lclCompareVectors( maCharBuffer, rCmp.maCharBuffer );
+ return (nResult != 0) ? (nResult < 0) : (maFormats < rCmp.maFormats);
+}
+
+// get data -------------------------------------------------------------------
+
+sal_uInt16 XclExpString::GetFormatsCount() const
+{
+ return static_cast< sal_uInt16 >( maFormats.size() );
+}
+
+sal_uInt8 XclExpString::GetFlagField() const
+{
+ return (mbIsUnicode ? EXC_STRF_16BIT : 0) | (IsWriteFormats() ? EXC_STRF_RICH : 0);
+}
+
+sal_uInt16 XclExpString::GetHeaderSize() const
+{
+ return
+ (mb8BitLen ? 1 : 2) + // length field
+ (IsWriteFlags() ? 1 : 0) + // flag field
+ (IsWriteFormats() ? 2 : 0); // richtext formatting count
+}
+
+std::size_t XclExpString::GetBufferSize() const
+{
+ return static_cast<std::size_t>(mnLen) * (mbIsUnicode ? 2 : 1);
+}
+
+std::size_t XclExpString::GetSize() const
+{
+ return
+ GetHeaderSize() + // header
+ GetBufferSize() + // character buffer
+ (IsWriteFormats() ? (4 * GetFormatsCount()) : 0); // richtext formatting
+}
+
+sal_uInt16 XclExpString::GetChar( sal_uInt16 nCharIdx ) const
+{
+ OSL_ENSURE( nCharIdx < Len(), "XclExpString::GetChar - invalid character index" );
+ return static_cast< sal_uInt16 >( mbIsBiff8 ? maUniBuffer[ nCharIdx ] : maCharBuffer[ nCharIdx ] );
+}
+
+sal_uInt16 XclExpString::GetHash() const
+{
+ return
+ (mbIsBiff8 ? lclHashVector( maUniBuffer ) : lclHashVector( maCharBuffer )) ^
+ lclHashVector( maFormats, XclFormatRunHasher() );
+}
+
+// streaming ------------------------------------------------------------------
+
+void XclExpString::WriteLenField( XclExpStream& rStrm ) const
+{
+ if( mb8BitLen )
+ rStrm << static_cast< sal_uInt8 >( mnLen );
+ else
+ rStrm << mnLen;
+}
+
+void XclExpString::WriteFlagField( XclExpStream& rStrm ) const
+{
+ if( mbIsBiff8 )
+ {
+ PrepareWrite( rStrm, 1 );
+ rStrm << GetFlagField();
+ rStrm.SetSliceSize( 0 );
+ }
+}
+
+void XclExpString::WriteHeader( XclExpStream& rStrm ) const
+{
+ OSL_ENSURE( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeader - string too long" );
+ PrepareWrite( rStrm, GetHeaderSize() );
+ // length
+ WriteLenField( rStrm );
+ // flag field
+ if( IsWriteFlags() )
+ rStrm << GetFlagField();
+ // format run count
+ if( IsWriteFormats() )
+ rStrm << GetFormatsCount();
+ rStrm.SetSliceSize( 0 );
+}
+
+void XclExpString::WriteBuffer( XclExpStream& rStrm ) const
+{
+ if( mbIsBiff8 )
+ rStrm.WriteUnicodeBuffer( maUniBuffer, GetFlagField() );
+ else
+ rStrm.WriteCharBuffer( maCharBuffer );
+}
+
+void XclExpString::WriteFormats( XclExpStream& rStrm, bool bWriteSize ) const
+{
+ if( !IsRich() )
+ return;
+
+ if( mbIsBiff8 )
+ {
+ if( bWriteSize )
+ rStrm << GetFormatsCount();
+ rStrm.SetSliceSize( 4 );
+ for( const auto& rFormat : maFormats )
+ rStrm << rFormat.mnChar << rFormat.mnFontIdx;
+ }
+ else
+ {
+ if( bWriteSize )
+ rStrm << static_cast< sal_uInt8 >( GetFormatsCount() );
+ rStrm.SetSliceSize( 2 );
+ for( const auto& rFormat : maFormats )
+ rStrm << static_cast< sal_uInt8 >( rFormat.mnChar ) << static_cast< sal_uInt8 >( rFormat.mnFontIdx );
+ }
+ rStrm.SetSliceSize( 0 );
+}
+
+void XclExpString::Write( XclExpStream& rStrm ) const
+{
+ if (!mbSkipHeader)
+ WriteHeader( rStrm );
+ WriteBuffer( rStrm );
+ if( IsWriteFormats() ) // only in BIFF8 included in string
+ WriteFormats( rStrm );
+}
+
+void XclExpString::WriteHeaderToMem( sal_uInt8* pnMem ) const
+{
+ assert(pnMem);
+ OSL_ENSURE( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeaderToMem - string too long" );
+ OSL_ENSURE( !IsWriteFormats(), "XclExpString::WriteHeaderToMem - formatted strings not supported" );
+ // length
+ if( mb8BitLen )
+ {
+ *pnMem = static_cast< sal_uInt8 >( mnLen );
+ ++pnMem;
+ }
+ else
+ {
+ ShortToSVBT16( mnLen, pnMem );
+ pnMem += 2;
+ }
+ // flag field
+ if( IsWriteFlags() )
+ *pnMem = GetFlagField();
+}
+
+void XclExpString::WriteBufferToMem( sal_uInt8* pnMem ) const
+{
+ assert(pnMem);
+ if( IsEmpty() )
+ return;
+
+ if( mbIsBiff8 )
+ {
+ for( const sal_uInt16 nChar : maUniBuffer )
+ {
+ *pnMem = static_cast< sal_uInt8 >( nChar );
+ ++pnMem;
+ if( mbIsUnicode )
+ {
+ *pnMem = static_cast< sal_uInt8 >( nChar >> 8 );
+ ++pnMem;
+ }
+ }
+ }
+ else
+ memcpy( pnMem, maCharBuffer.data(), mnLen );
+}
+
+void XclExpString::WriteToMem( sal_uInt8* pnMem ) const
+{
+ WriteHeaderToMem( pnMem );
+ WriteBufferToMem( pnMem + GetHeaderSize() );
+}
+
+static sal_uInt16 lcl_WriteRun( XclExpXmlStream& rStrm, const ScfUInt16Vec& rBuffer, sal_uInt16 nStart, sal_Int32 nLength, const XclExpFont* pFont )
+{
+ if( nLength == 0 )
+ return nStart;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(XML_r);
+ if( pFont )
+ {
+ const XclFontData& rFontData = pFont->GetFontData();
+ rWorksheet->startElement(XML_rPr);
+ XclXmlUtils::WriteFontData( rWorksheet, rFontData, XML_rFont );
+ rWorksheet->endElement( XML_rPr );
+ }
+ rWorksheet->startElement(XML_t, FSNS(XML_xml, XML_space), "preserve");
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString( rBuffer, nStart, nLength ) );
+ rWorksheet->endElement( XML_t );
+ rWorksheet->endElement( XML_r );
+ return nStart + nLength;
+}
+
+void XclExpString::WriteXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr rWorksheet = rStrm.GetCurrentStream();
+
+ if( !IsWriteFormats() )
+ {
+ rWorksheet->startElement(XML_t, FSNS(XML_xml, XML_space), "preserve");
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *this ) );
+ rWorksheet->endElement( XML_t );
+ }
+ else
+ {
+ XclExpFontBuffer& rFonts = rStrm.GetRoot().GetFontBuffer();
+
+ sal_uInt16 nStart = 0;
+ const XclExpFont* pFont = nullptr;
+ for ( const auto& rFormat : maFormats )
+ {
+ nStart = lcl_WriteRun( rStrm, GetUnicodeBuffer(),
+ nStart, rFormat.mnChar-nStart, pFont );
+ pFont = rFonts.GetFont( rFormat.mnFontIdx );
+ }
+ lcl_WriteRun( rStrm, GetUnicodeBuffer(),
+ nStart, GetUnicodeBuffer().size() - nStart, pFont );
+ }
+}
+
+bool XclExpString::IsWriteFlags() const
+{
+ return mbIsBiff8 && (!IsEmpty() || !mbSmartFlags);
+}
+
+bool XclExpString::IsWriteFormats() const
+{
+ return mbIsBiff8 && !mbSkipFormats && IsRich();
+}
+
+void XclExpString::SetStrLen( sal_Int32 nNewLen )
+{
+ sal_uInt16 nAllowedLen = (mb8BitLen && (mnMaxLen > 255)) ? 255 : mnMaxLen;
+ mnLen = limit_cast< sal_uInt16 >( nNewLen, 0, nAllowedLen );
+}
+
+void XclExpString::CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ OSL_ENSURE( maUniBuffer.size() >= o3tl::make_unsigned( nBegin + nLen ),
+ "XclExpString::CharsToBuffer - char buffer invalid" );
+ ScfUInt16Vec::iterator aBeg = maUniBuffer.begin() + nBegin;
+ ScfUInt16Vec::iterator aEnd = aBeg + nLen;
+ const sal_Unicode* pcSrcChar = pcSource;
+ for( ScfUInt16Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar )
+ {
+ *aIt = static_cast< sal_uInt16 >( *pcSrcChar );
+ if( *aIt & 0xFF00 )
+ mbIsUnicode = true;
+ }
+ if( !mbWrapped )
+ mbWrapped = ::std::find( aBeg, aEnd, EXC_LF ) != aEnd;
+}
+
+void XclExpString::CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ OSL_ENSURE( maCharBuffer.size() >= o3tl::make_unsigned( nBegin + nLen ),
+ "XclExpString::CharsToBuffer - char buffer invalid" );
+ ScfUInt8Vec::iterator aBeg = maCharBuffer.begin() + nBegin;
+ ScfUInt8Vec::iterator aEnd = aBeg + nLen;
+ const char* pcSrcChar = pcSource;
+ for( ScfUInt8Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar )
+ *aIt = static_cast< sal_uInt8 >( *pcSrcChar );
+ mbIsUnicode = false;
+ if( !mbWrapped )
+ mbWrapped = ::std::find( aBeg, aEnd, EXC_LF_C ) != aEnd;
+}
+
+void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 )
+{
+ mbIsBiff8 = bBiff8;
+ mbIsUnicode = bBiff8 && ( nFlags & XclStrFlags::ForceUnicode );
+ mb8BitLen = bool( nFlags & XclStrFlags::EightBitLength );
+ mbSmartFlags = bBiff8 && ( nFlags & XclStrFlags::SmartFlags );
+ mbSkipFormats = bool( nFlags & XclStrFlags::SeparateFormats );
+ mbWrapped = false;
+ mbSkipHeader = bool( nFlags & XclStrFlags::NoHeader );
+ mnMaxLen = nMaxLen;
+ SetStrLen( nCurrLen );
+
+ maFormats.clear();
+ if( mbIsBiff8 )
+ {
+ maCharBuffer.clear();
+ maUniBuffer.resize( mnLen );
+ }
+ else
+ {
+ maUniBuffer.clear();
+ maCharBuffer.resize( mnLen );
+ }
+}
+
+void XclExpString::Build( const sal_Unicode* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( nCurrLen, nFlags, nMaxLen, true );
+ CharsToBuffer( pcSource, 0, mnLen );
+}
+
+void XclExpString::Build( const char* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( nCurrLen, nFlags, nMaxLen, false );
+ CharsToBuffer( pcSource, 0, mnLen );
+}
+
+void XclExpString::InitAppend( sal_Int32 nAddLen )
+{
+ SetStrLen( static_cast< sal_Int32 >( mnLen ) + nAddLen );
+ if( mbIsBiff8 )
+ maUniBuffer.resize( mnLen );
+ else
+ maCharBuffer.resize( mnLen );
+}
+
+void XclExpString::BuildAppend( std::u16string_view rSource )
+{
+ OSL_ENSURE( mbIsBiff8, "XclExpString::BuildAppend - must not be called at byte strings" );
+ if( mbIsBiff8 )
+ {
+ sal_uInt16 nOldLen = mnLen;
+ InitAppend( rSource.size() );
+ CharsToBuffer( rSource.data(), nOldLen, mnLen - nOldLen );
+ }
+}
+
+void XclExpString::BuildAppend( std::string_view rSource )
+{
+ OSL_ENSURE( !mbIsBiff8, "XclExpString::BuildAppend - must not be called at unicode strings" );
+ if( !mbIsBiff8 )
+ {
+ sal_uInt16 nOldLen = mnLen;
+ InitAppend( rSource.size() );
+ CharsToBuffer( rSource.data(), nOldLen, mnLen - nOldLen );
+ }
+}
+
+void XclExpString::PrepareWrite( XclExpStream& rStrm, sal_uInt16 nBytes ) const
+{
+ rStrm.SetSliceSize( nBytes + (mbIsUnicode ? 2 : 1) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestyle.cxx b/sc/source/filter/excel/xestyle.cxx
new file mode 100644
index 0000000000..678327bd1a
--- /dev/null
+++ b/sc/source/filter/excel/xestyle.cxx
@@ -0,0 +1,3344 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+#include <xestyle.hxx>
+
+#include <algorithm>
+#include <iterator>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+#include <rtl/tencinfo.h>
+#include <vcl/font.hxx>
+#include <svl/languageoptions.hxx>
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/langitem.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xestring.hxx>
+#include <xltools.hxx>
+#include <conditio.hxx>
+#include <dbdata.hxx>
+#include <filterentries.hxx>
+#include <export/ExportTools.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <svl/numformat.hxx>
+
+using namespace ::com::sun::star;
+using namespace oox;
+
+// PALETTE record - color information =========================================
+
+namespace {
+
+sal_uInt32 lclGetWeighting( XclExpColorType eType )
+{
+ switch( eType )
+ {
+ case EXC_COLOR_CHARTLINE: return 1;
+ case EXC_COLOR_CELLBORDER:
+ case EXC_COLOR_CHARTAREA: return 2;
+ case EXC_COLOR_CELLTEXT:
+ case EXC_COLOR_CHARTTEXT:
+ case EXC_COLOR_CTRLTEXT: return 10;
+ case EXC_COLOR_TABBG:
+ case EXC_COLOR_CELLAREA: return 20;
+ case EXC_COLOR_GRID: return 50;
+ default: OSL_FAIL( "lclGetWeighting - unknown color type" );
+ }
+ return 1;
+}
+
+sal_Int32 lclGetColorDistance( const Color& rColor1, const Color& rColor2 )
+{
+ sal_Int32 nDist = rColor1.GetRed() - rColor2.GetRed();
+ nDist *= nDist * 77;
+ sal_Int32 nDummy = rColor1.GetGreen() - rColor2.GetGreen();
+ nDist += nDummy * nDummy * 151;
+ nDummy = rColor1.GetBlue() - rColor2.GetBlue();
+ nDist += nDummy * nDummy * 28;
+ return nDist;
+}
+
+sal_uInt8 lclGetMergedColorComp( sal_uInt8 nComp1, sal_uInt32 nWeight1, sal_uInt8 nComp2, sal_uInt32 nWeight2 )
+{
+ sal_uInt8 nComp1Dist = ::std::min< sal_uInt8 >( nComp1, 0xFF - nComp1 );
+ sal_uInt8 nComp2Dist = ::std::min< sal_uInt8 >( nComp2, 0xFF - nComp2 );
+ if( nComp1Dist != nComp2Dist )
+ {
+ /* #i36945# One of the passed RGB components is nearer at the limits (0x00 or 0xFF).
+ Increase its weighting to prevent fading of the colors during reduction. */
+ const sal_uInt8& rnCompNearer = (nComp1Dist < nComp2Dist) ? nComp1 : nComp2;
+ sal_uInt32& rnWeight = (nComp1Dist < nComp2Dist) ? nWeight1 : nWeight2;
+ rnWeight *= ((rnCompNearer - 0x80L) * (rnCompNearer - 0x7FL) / 0x1000L + 1);
+ }
+ sal_uInt32 nWSum = nWeight1 + nWeight2;
+ return static_cast< sal_uInt8 >( (nComp1 * nWeight1 + nComp2 * nWeight2 + nWSum / 2) / nWSum );
+}
+
+void lclSetMixedColor( Color& rDest, const Color& rSrc1, const Color& rSrc2 )
+{
+ rDest.SetRed( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetRed() ) + rSrc2.GetRed()) / 2 ) );
+ rDest.SetGreen( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetGreen() ) + rSrc2.GetGreen()) / 2 ) );
+ rDest.SetBlue( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetBlue() ) + rSrc2.GetBlue()) / 2 ) );
+}
+
+} // namespace
+
+// additional classes for color reduction -------------------------------------
+
+namespace {
+
+/** Represents an entry in a color list.
+
+ The color stores a weighting value, which increases the more the color is
+ used in the document. Heavy-weighted colors will change less than others on
+ color reduction.
+ */
+class XclListColor
+{
+private:
+ Color maColor; /// The color value of this palette entry.
+ sal_uInt32 mnColorId; /// Unique color ID for color reduction.
+ sal_uInt32 mnWeight; /// Weighting for color reduction.
+ bool mbBaseColor; /// true = Handle as base color, (don't remove/merge).
+
+public:
+ explicit XclListColor( const Color& rColor, sal_uInt32 nColorId );
+
+ /** Returns the RGB color value of the color. */
+ const Color& GetColor() const { return maColor; }
+ /** Returns the unique ID of the color. */
+ sal_uInt32 GetColorId() const { return mnColorId; }
+ /** Returns the current weighting of the color. */
+ sal_uInt32 GetWeighting() const { return mnWeight; }
+ /** Returns true, if this color is a base color, i.e. it will not be removed or merged. */
+ bool IsBaseColor() const { return mbBaseColor; }
+
+ /** Adds the passed weighting to this color. */
+ void AddWeighting( sal_uInt32 nWeight ) { mnWeight += nWeight; }
+ /** Merges this color with rColor, regarding weighting settings. */
+ void Merge( const XclListColor& rColor );
+};
+
+XclListColor::XclListColor( const Color& rColor, sal_uInt32 nColorId ) :
+ maColor( rColor ),
+ mnColorId( nColorId ),
+ mnWeight( 0 )
+{
+ mbBaseColor =
+ ((rColor.GetRed() == 0x00) || (rColor.GetRed() == 0xFF)) &&
+ ((rColor.GetGreen() == 0x00) || (rColor.GetGreen() == 0xFF)) &&
+ ((rColor.GetBlue() == 0x00) || (rColor.GetBlue() == 0xFF));
+}
+
+void XclListColor::Merge( const XclListColor& rColor )
+{
+ sal_uInt32 nWeight2 = rColor.GetWeighting();
+ // do not change RGB value of base colors
+ if( !mbBaseColor )
+ {
+ maColor.SetRed( lclGetMergedColorComp( maColor.GetRed(), mnWeight, rColor.maColor.GetRed(), nWeight2 ) );
+ maColor.SetGreen( lclGetMergedColorComp( maColor.GetGreen(), mnWeight, rColor.maColor.GetGreen(), nWeight2 ) );
+ maColor.SetBlue( lclGetMergedColorComp( maColor.GetBlue(), mnWeight, rColor.maColor.GetBlue(), nWeight2 ) );
+ }
+ AddWeighting( nWeight2 );
+}
+
+/** Data for each inserted original color, represented by a color ID. */
+struct XclColorIdData
+{
+ Color maColor; /// The original inserted color.
+ sal_uInt32 mnIndex; /// Maps current color ID to color list or export color vector.
+ /** Sets the contents of this struct. */
+ void Set( const Color& rColor, sal_uInt32 nIndex ) { maColor = rColor; mnIndex = nIndex; }
+};
+
+/** A color that will be written to the Excel file. */
+struct XclPaletteColor
+{
+ Color maColor; /// Resulting color to export.
+ bool mbUsed; /// true = Entry is used in the document.
+
+ explicit XclPaletteColor( const Color& rColor ) : maColor( rColor ), mbUsed( false ) {}
+ void SetColor( const Color& rColor ) { maColor = rColor; mbUsed = true; }
+};
+
+/** Maps a color list index to a palette index.
+ @descr Used to remap the color ID data vector from list indexes to palette indexes. */
+struct XclRemap
+{
+ sal_uInt32 mnPalIndex; /// Index to palette.
+ bool mbProcessed; /// true = List color already processed.
+
+ explicit XclRemap() : mnPalIndex( 0 ), mbProcessed( false ) {}
+ void SetIndex( sal_uInt32 nPalIndex )
+ { mnPalIndex = nPalIndex; mbProcessed = true; }
+};
+
+/** Stores the nearest palette color index of a list color. */
+struct XclNearest
+{
+ sal_uInt32 mnPalIndex; /// Index to nearest palette color.
+ sal_Int32 mnDist; /// Distance to palette color.
+
+ explicit XclNearest() : mnPalIndex( 0 ), mnDist( 0 ) {}
+};
+
+} // namespace
+
+class XclExpPaletteImpl
+{
+public:
+ explicit XclExpPaletteImpl( const XclDefaultPalette& rDefPal );
+
+ /** Inserts the color into the list and updates weighting.
+ @param nAutoDefault The Excel palette index for automatic color.
+ @return A unique ID for this color. */
+ sal_uInt32 InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 );
+ /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */
+ static sal_uInt32 GetColorIdFromIndex( sal_uInt16 nIndex );
+
+ /** Reduces the color list to the maximum count of the current BIFF version. */
+ void Finalize();
+
+ /** Returns the Excel palette index of the color with passed color ID. */
+ sal_uInt16 GetColorIndex( sal_uInt32 nColorId ) const;
+
+ /** Returns a foreground and background color for the two passed color IDs.
+ @descr If rnXclPattern contains a solid pattern, this function tries to find
+ the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId.
+ This will result in a better approximation to the passed foreground color. */
+ void GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const;
+
+ /** Returns the RGB color for a (non-zero-based) Excel palette entry.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Returns true, if all colors of the palette are equal to default palette colors. */
+ bool IsDefaultPalette() const;
+ /** Writes the color list (contents of the palette record) to the passed stream. */
+ void WriteBody( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ /** Returns the Excel index of a 0-based color index. */
+ static sal_uInt16 GetXclIndex( sal_uInt32 nIndex )
+ { return static_cast< sal_uInt16 >( nIndex + EXC_COLOR_USEROFFSET ); }
+
+ /** Returns the original inserted color represented by the color ID nColorId. */
+ const Color& GetOriginalColor( sal_uInt32 nColorId ) const;
+
+ /** Searches for rColor, returns the ordered insertion index for rColor in rnIndex. */
+ XclListColor* SearchListEntry( const Color& rColor, sal_uInt32& rnIndex );
+ /** Creates and inserts a new color list entry at the specified list position. */
+ XclListColor* CreateListEntry( const Color& rColor, sal_uInt32 nIndex );
+
+ /** Raw and fast reduction of the palette. */
+ void RawReducePalette( sal_uInt32 nPass );
+ /** Reduction of one color using advanced color merging based on color weighting. */
+ void ReduceLeastUsedColor();
+
+ /** Finds the least used color and returns its current list index. */
+ sal_uInt32 GetLeastUsedListColor() const;
+ /** Returns the list index of the color nearest to rColor.
+ @param nIgnore List index of a color which will be ignored.
+ @return The list index of the found color. */
+ sal_uInt32 GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const;
+ /** Returns the list index of the color nearest to the color with list index nIndex. */
+ sal_uInt32 GetNearestListColor( sal_uInt32 nIndex ) const;
+
+ /** Returns in rnIndex the palette index of the color nearest to rColor.
+ Searches for default colors only (colors never replaced).
+ @return The distance from passed color to found color. */
+ sal_Int32 GetNearestPaletteColor(
+ sal_uInt32& rnIndex,
+ const Color& rColor ) const;
+ /** Returns in rnFirst and rnSecond the palette indexes of the two colors nearest to rColor.
+ @return The minimum distance from passed color to found colors. */
+ sal_Int32 GetNearPaletteColors(
+ sal_uInt32& rnFirst, sal_uInt32& rnSecond,
+ const Color& rColor ) const;
+
+private:
+ typedef std::vector< std::unique_ptr<XclListColor> > XclListColorList;
+ typedef std::shared_ptr< XclListColorList > XclListColorListRef;
+
+ const XclDefaultPalette& mrDefPal; /// The default palette for the current BIFF version.
+ XclListColorListRef mxColorList; /// Working color list.
+ std::vector< XclColorIdData >
+ maColorIdDataVec; /// Data of all CIDs.
+ std::vector< XclPaletteColor >
+ maPalette; /// Contains resulting colors to export.
+ sal_uInt32 mnLastIdx; /// Last insertion index for search opt.
+};
+
+const sal_uInt32 EXC_PAL_INDEXBASE = 0xFFFF0000;
+const sal_uInt32 EXC_PAL_MAXRAWSIZE = 1024;
+
+XclExpPaletteImpl::XclExpPaletteImpl( const XclDefaultPalette& rDefPal ) :
+ mrDefPal( rDefPal ),
+ mxColorList( std::make_shared<XclListColorList>() ),
+ mnLastIdx( 0 )
+{
+ // initialize maPalette with default colors
+ sal_uInt16 nCount = static_cast< sal_uInt16 >( mrDefPal.GetColorCount() );
+ maPalette.reserve( nCount );
+ for( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
+ maPalette.emplace_back( mrDefPal.GetDefColor( GetXclIndex( nIdx ) ) );
+
+ InsertColor( COL_BLACK, EXC_COLOR_CELLTEXT );
+}
+
+sal_uInt32 XclExpPaletteImpl::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
+{
+ if( rColor == COL_AUTO )
+ return GetColorIdFromIndex( nAutoDefault );
+
+ sal_uInt32 nFoundIdx = 0;
+ XclListColor* pEntry = SearchListEntry( rColor, nFoundIdx );
+ if( !pEntry || (pEntry->GetColor() != rColor) )
+ pEntry = CreateListEntry( rColor, nFoundIdx );
+ pEntry->AddWeighting( lclGetWeighting( eType ) );
+
+ return pEntry->GetColorId();
+}
+
+sal_uInt32 XclExpPaletteImpl::GetColorIdFromIndex( sal_uInt16 nIndex )
+{
+ return EXC_PAL_INDEXBASE | nIndex;
+}
+
+void XclExpPaletteImpl::Finalize()
+{
+// --- build initial color ID data vector (maColorIdDataVec) ---
+
+ sal_uInt32 nCount = mxColorList->size();
+ maColorIdDataVec.resize( nCount );
+ for( sal_uInt32 nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ const XclListColor& listColor = *mxColorList->at( nIdx );
+ maColorIdDataVec[ listColor.GetColorId() ].Set( listColor.GetColor(), nIdx );
+ }
+
+// --- loop as long as current color count does not fit into palette of current BIFF ---
+
+ // phase 1: raw reduction (performance reasons, #i36945#)
+ sal_uInt32 nPass = 0;
+ while( mxColorList->size() > EXC_PAL_MAXRAWSIZE )
+ RawReducePalette( nPass++ );
+
+ // phase 2: precise reduction using advanced color merging based on color weighting
+ while( mxColorList->size() > mrDefPal.GetColorCount() )
+ ReduceLeastUsedColor();
+
+// --- use default palette and replace colors with nearest used colors ---
+
+ nCount = mxColorList->size();
+ std::vector< XclRemap > aRemapVec( nCount );
+ std::vector< XclNearest > aNearestVec( nCount );
+
+ // in each run: search the best fitting color and replace a default color with it
+ for( sal_uInt32 nRun = 0; nRun < nCount; ++nRun )
+ {
+ sal_uInt32 nIndex;
+ // find nearest unused default color for each unprocessed list color
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ aNearestVec[ nIndex ].mnDist = aRemapVec[ nIndex ].mbProcessed ? SAL_MAX_INT32 :
+ GetNearestPaletteColor( aNearestVec[ nIndex ].mnPalIndex, mxColorList->at( nIndex )->GetColor() );
+ // find the list color which is nearest to a default color
+ sal_uInt32 nFound = 0;
+ for( nIndex = 1; nIndex < nCount; ++nIndex )
+ if( aNearestVec[ nIndex ].mnDist < aNearestVec[ nFound ].mnDist )
+ nFound = nIndex;
+ // replace default color with list color
+ sal_uInt32 nNearest = aNearestVec[ nFound ].mnPalIndex;
+ OSL_ENSURE( nNearest < maPalette.size(), "XclExpPaletteImpl::Finalize - algorithm error" );
+ maPalette[ nNearest ].SetColor( mxColorList->at( nFound )->GetColor() );
+ aRemapVec[ nFound ].SetIndex( nNearest );
+ }
+
+ // remap color ID data map (maColorIdDataVec) from list indexes to palette indexes
+ for( auto& rColorIdData : maColorIdDataVec )
+ rColorIdData.mnIndex = aRemapVec[ rColorIdData.mnIndex ].mnPalIndex;
+}
+
+sal_uInt16 XclExpPaletteImpl::GetColorIndex( sal_uInt32 nColorId ) const
+{
+ sal_uInt16 nRet = 0;
+ if( nColorId >= EXC_PAL_INDEXBASE )
+ nRet = static_cast< sal_uInt16 >( nColorId & ~EXC_PAL_INDEXBASE );
+ else if( nColorId < maColorIdDataVec.size() )
+ nRet = GetXclIndex( maColorIdDataVec[ nColorId ].mnIndex );
+ return nRet;
+}
+
+void XclExpPaletteImpl::GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
+{
+ rnXclForeIx = GetColorIndex( nForeColorId );
+ rnXclBackIx = GetColorIndex( nBackColorId );
+ if( (rnXclPattern != EXC_PATT_SOLID) || (nForeColorId >= maColorIdDataVec.size()) )
+ return;
+
+ // now we have solid pattern, and a defined foreground (background doesn't care for solid pattern)
+
+ sal_uInt32 nIndex1, nIndex2;
+ Color aForeColor( GetOriginalColor( nForeColorId ) );
+ sal_Int32 nFirstDist = GetNearPaletteColors( nIndex1, nIndex2, aForeColor );
+ if( (nIndex1 >= maPalette.size()) || (nIndex2 >= maPalette.size()) )
+ return;
+
+ Color aColorArr[ 5 ];
+ aColorArr[ 0 ] = maPalette[ nIndex1 ].maColor;
+ aColorArr[ 4 ] = maPalette[ nIndex2 ].maColor;
+ lclSetMixedColor( aColorArr[ 2 ], aColorArr[ 0 ], aColorArr[ 4 ] );
+ lclSetMixedColor( aColorArr[ 1 ], aColorArr[ 0 ], aColorArr[ 2 ] );
+ lclSetMixedColor( aColorArr[ 3 ], aColorArr[ 2 ], aColorArr[ 4 ] );
+
+ sal_Int32 nMinDist = nFirstDist;
+ sal_uInt32 nMinIndex = 0;
+ for( sal_uInt32 nCnt = 1; nCnt < 4; ++nCnt )
+ {
+ sal_Int32 nDist = lclGetColorDistance( aForeColor, aColorArr[ nCnt ] );
+ if( nDist < nMinDist )
+ {
+ nMinDist = nDist;
+ nMinIndex = nCnt;
+ }
+ }
+ rnXclForeIx = GetXclIndex( nIndex1 );
+ rnXclBackIx = GetXclIndex( nIndex2 );
+ if( nMinDist < nFirstDist )
+ {
+ switch( nMinIndex )
+ {
+ case 1: rnXclPattern = EXC_PATT_75_PERC; break;
+ case 2: rnXclPattern = EXC_PATT_50_PERC; break;
+ case 3: rnXclPattern = EXC_PATT_25_PERC; break;
+ }
+ }
+}
+
+Color XclExpPaletteImpl::GetColor( sal_uInt16 nXclIndex ) const
+{
+ if( nXclIndex >= EXC_COLOR_USEROFFSET )
+ {
+ sal_uInt32 nIdx = nXclIndex - EXC_COLOR_USEROFFSET;
+ if( nIdx < maPalette.size() )
+ return maPalette[ nIdx ].maColor;
+ }
+ return mrDefPal.GetDefColor( nXclIndex );
+}
+
+bool XclExpPaletteImpl::IsDefaultPalette() const
+{
+ bool bDefault = true;
+ for( sal_uInt32 nIdx = 0, nSize = static_cast< sal_uInt32 >( maPalette.size() ); bDefault && (nIdx < nSize); ++nIdx )
+ bDefault = maPalette[ nIdx ].maColor == mrDefPal.GetDefColor( GetXclIndex( nIdx ) );
+ return bDefault;
+}
+
+void XclExpPaletteImpl::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >( maPalette.size() );
+ for( const auto& rColor : maPalette )
+ rStrm << rColor.maColor;
+}
+
+void XclExpPaletteImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maPalette.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_colors);
+ rStyleSheet->startElement(XML_indexedColors);
+ for( const auto& rColor : maPalette )
+ rStyleSheet->singleElement(XML_rgbColor, XML_rgb, XclXmlUtils::ToOString(rColor.maColor));
+ rStyleSheet->endElement( XML_indexedColors );
+ rStyleSheet->endElement( XML_colors );
+}
+
+const Color& XclExpPaletteImpl::GetOriginalColor( sal_uInt32 nColorId ) const
+{
+ if( nColorId < maColorIdDataVec.size() )
+ return maColorIdDataVec[ nColorId ].maColor;
+ return maPalette[ 0 ].maColor;
+}
+
+XclListColor* XclExpPaletteImpl::SearchListEntry( const Color& rColor, sal_uInt32& rnIndex )
+{
+ rnIndex = 0;
+
+ if (mxColorList->empty())
+ return nullptr;
+
+ XclListColor* pEntry = nullptr;
+
+ // search optimization for equal-colored objects occurring repeatedly
+ if (mnLastIdx < mxColorList->size())
+ {
+ pEntry = (*mxColorList)[mnLastIdx].get();
+ if( pEntry->GetColor() == rColor )
+ {
+ rnIndex = mnLastIdx;
+ return pEntry;
+ }
+ }
+
+ // binary search for color
+ sal_uInt32 nBegIdx = 0;
+ sal_uInt32 nEndIdx = mxColorList->size();
+ bool bFound = false;
+ while( !bFound && (nBegIdx < nEndIdx) )
+ {
+ rnIndex = (nBegIdx + nEndIdx) / 2;
+ pEntry = (*mxColorList)[rnIndex].get();
+ bFound = pEntry->GetColor() == rColor;
+ if( !bFound )
+ {
+ if( pEntry->GetColor() < rColor )
+ nBegIdx = rnIndex + 1;
+ else
+ nEndIdx = rnIndex;
+ }
+ }
+
+ // not found - use end of range as new insertion position
+ if( !bFound )
+ rnIndex = nEndIdx;
+
+ mnLastIdx = rnIndex;
+ return pEntry;
+}
+
+XclListColor* XclExpPaletteImpl::CreateListEntry( const Color& rColor, sal_uInt32 nIndex )
+{
+ XclListColor* pEntry = new XclListColor( rColor, mxColorList->size() );
+ mxColorList->insert(mxColorList->begin() + nIndex, std::unique_ptr<XclListColor>(pEntry));
+ return pEntry;
+}
+
+void XclExpPaletteImpl::RawReducePalette( sal_uInt32 nPass )
+{
+ /* Fast palette reduction - in each call of this function one RGB component
+ of each color is reduced to a lower number of distinct values.
+ Pass 0: Blue is reduced to 128 distinct values.
+ Pass 1: Red is reduced to 128 distinct values.
+ Pass 2: Green is reduced to 128 distinct values.
+ Pass 3: Blue is reduced to 64 distinct values.
+ Pass 4: Red is reduced to 64 distinct values.
+ Pass 5: Green is reduced to 64 distinct values.
+ And so on...
+ */
+
+ XclListColorListRef xOldList = mxColorList;
+ mxColorList = std::make_shared<XclListColorList>();
+
+ // maps old list indexes to new list indexes, used to update maColorIdDataVec
+ ScfUInt32Vec aListIndexMap;
+ aListIndexMap.reserve( xOldList->size() );
+
+ // preparations
+ sal_uInt8 nR, nG, nB;
+ sal_uInt8& rnComp = ((nPass % 3 == 0) ? nB : ((nPass % 3 == 1) ? nR : nG));
+ nPass /= 3;
+ OSL_ENSURE( nPass < 7, "XclExpPaletteImpl::RawReducePalette - reduction not terminated" );
+
+ static const sal_uInt8 spnFactor2[] = { 0x81, 0x82, 0x84, 0x88, 0x92, 0xAA, 0xFF };
+ sal_uInt8 nFactor1 = static_cast< sal_uInt8 >( 0x02 << nPass );
+ sal_uInt8 nFactor2 = spnFactor2[ nPass ];
+ sal_uInt8 nFactor3 = static_cast< sal_uInt8 >( 0x40 >> nPass );
+
+ // process each color in the old color list
+ for(const std::unique_ptr<XclListColor> & pOldColor : *xOldList)
+ {
+ // get the old list entry
+ const XclListColor* pOldEntry = pOldColor.get();
+ nR = pOldEntry->GetColor().GetRed();
+ nG = pOldEntry->GetColor().GetGreen();
+ nB = pOldEntry->GetColor().GetBlue();
+
+ /* Calculate the new RGB component (rnComp points to one of nR, nG, nB).
+ Using integer arithmetic with its rounding errors, the results of
+ this calculation are always exactly in the range 0x00 to 0xFF
+ (simply cutting the lower bits would darken the colors slightly). */
+ sal_uInt32 nNewComp = rnComp;
+ nNewComp /= nFactor1;
+ nNewComp *= nFactor2;
+ nNewComp /= nFactor3;
+ rnComp = static_cast< sal_uInt8 >( nNewComp );
+ Color aNewColor( nR, nG, nB );
+
+ // find or insert the new color
+ sal_uInt32 nFoundIdx = 0;
+ XclListColor* pNewEntry = SearchListEntry( aNewColor, nFoundIdx );
+ if( !pNewEntry || (pNewEntry->GetColor() != aNewColor) )
+ pNewEntry = CreateListEntry( aNewColor, nFoundIdx );
+ pNewEntry->AddWeighting( pOldEntry->GetWeighting() );
+ aListIndexMap.push_back( nFoundIdx );
+ }
+
+ // update color ID data map (maps color IDs to color list indexes), replace old by new list indexes
+ for( auto& rColorIdData : maColorIdDataVec )
+ rColorIdData.mnIndex = aListIndexMap[ rColorIdData.mnIndex ];
+}
+
+void XclExpPaletteImpl::ReduceLeastUsedColor()
+{
+ // find a list color to remove
+ sal_uInt32 nRemove = GetLeastUsedListColor();
+ // find its nearest neighbor
+ sal_uInt32 nKeep = GetNearestListColor( nRemove );
+
+ // merge both colors to one color, remove one color from list
+ XclListColor* pKeepEntry = mxColorList->at(nKeep).get();
+ XclListColor* pRemoveEntry = mxColorList->at(nRemove).get();
+ if( !(pKeepEntry && pRemoveEntry) )
+ return;
+
+ // merge both colors (if pKeepEntry is a base color, it will not change)
+ pKeepEntry->Merge( *pRemoveEntry );
+ // remove the less used color, adjust nKeep index if kept color follows removed color
+ XclListColorList::iterator itr = mxColorList->begin();
+ ::std::advance(itr, nRemove);
+ mxColorList->erase(itr);
+ if( nKeep > nRemove ) --nKeep;
+
+ // recalculate color ID data map (maps color IDs to color list indexes)
+ for( auto& rColorIdData : maColorIdDataVec )
+ {
+ if( rColorIdData.mnIndex > nRemove )
+ --rColorIdData.mnIndex;
+ else if( rColorIdData.mnIndex == nRemove )
+ rColorIdData.mnIndex = nKeep;
+ }
+}
+
+sal_uInt32 XclExpPaletteImpl::GetLeastUsedListColor() const
+{
+ sal_uInt32 nFound = 0;
+ sal_uInt32 nMinW = SAL_MAX_UINT32;
+
+ for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
+ {
+ XclListColor& rEntry = *mxColorList->at( nIdx );
+ // ignore the base colors
+ if( !rEntry.IsBaseColor() && (rEntry.GetWeighting() < nMinW) )
+ {
+ nFound = nIdx;
+ nMinW = rEntry.GetWeighting();
+ }
+ }
+ return nFound;
+}
+
+sal_uInt32 XclExpPaletteImpl::GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const
+{
+ sal_uInt32 nFound = 0;
+ sal_Int32 nMinD = SAL_MAX_INT32;
+
+ for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
+ {
+ if( nIdx != nIgnore )
+ {
+ if( XclListColor* pEntry = mxColorList->at(nIdx).get() )
+ {
+ sal_Int32 nDist = lclGetColorDistance( rColor, pEntry->GetColor() );
+ if( nDist < nMinD )
+ {
+ nFound = nIdx;
+ nMinD = nDist;
+ }
+ }
+ }
+ }
+ return nFound;
+}
+
+sal_uInt32 XclExpPaletteImpl::GetNearestListColor( sal_uInt32 nIndex ) const
+{
+ if (nIndex >= mxColorList->size())
+ return 0;
+ XclListColor* pEntry = mxColorList->at(nIndex).get();
+ return GetNearestListColor( pEntry->GetColor(), nIndex );
+}
+
+sal_Int32 XclExpPaletteImpl::GetNearestPaletteColor(
+ sal_uInt32& rnIndex, const Color& rColor ) const
+{
+ rnIndex = 0;
+ sal_Int32 nDist = SAL_MAX_INT32;
+
+ sal_uInt32 nPaletteIndex = 0;
+ for( const auto& rPaletteColor : maPalette )
+ {
+ if( !rPaletteColor.mbUsed )
+ {
+ sal_Int32 nCurrDist = lclGetColorDistance( rColor, rPaletteColor.maColor );
+ if( nCurrDist < nDist )
+ {
+ rnIndex = nPaletteIndex;
+ nDist = nCurrDist;
+ }
+ }
+ ++nPaletteIndex;
+ }
+ return nDist;
+}
+
+sal_Int32 XclExpPaletteImpl::GetNearPaletteColors(
+ sal_uInt32& rnFirst, sal_uInt32& rnSecond, const Color& rColor ) const
+{
+ rnFirst = rnSecond = 0;
+ sal_Int32 nDist1 = SAL_MAX_INT32;
+ sal_Int32 nDist2 = SAL_MAX_INT32;
+
+ sal_uInt32 nPaletteIndex = 0;
+ for( const auto& rPaletteColor : maPalette )
+ {
+ sal_Int32 nCurrDist = lclGetColorDistance( rColor, rPaletteColor.maColor );
+ if( nCurrDist < nDist1 )
+ {
+ rnSecond = rnFirst;
+ nDist2 = nDist1;
+ rnFirst = nPaletteIndex;
+ nDist1 = nCurrDist;
+ }
+ else if( nCurrDist < nDist2 )
+ {
+ rnSecond = nPaletteIndex;
+ nDist2 = nCurrDist;
+ }
+ ++nPaletteIndex;
+ }
+ return nDist1;
+}
+
+XclExpPalette::XclExpPalette( const XclExpRoot& rRoot ) :
+ XclDefaultPalette( rRoot ),
+ XclExpRecord( EXC_ID_PALETTE )
+{
+ mxImpl = std::make_shared<XclExpPaletteImpl>( *this );
+ SetRecSize( GetColorCount() * 4 + 2 );
+}
+
+XclExpPalette::~XclExpPalette()
+{
+}
+
+sal_uInt32 XclExpPalette::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
+{
+ return mxImpl->InsertColor( rColor, eType, nAutoDefault );
+}
+
+sal_uInt32 XclExpPalette::GetColorIdFromIndex( sal_uInt16 nIndex )
+{
+ return XclExpPaletteImpl::GetColorIdFromIndex( nIndex );
+}
+
+void XclExpPalette::Finalize()
+{
+ mxImpl->Finalize();
+}
+
+sal_uInt16 XclExpPalette::GetColorIndex( sal_uInt32 nColorId ) const
+{
+ return mxImpl->GetColorIndex( nColorId );
+}
+
+void XclExpPalette::GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
+{
+ return mxImpl->GetMixedColors( rnXclForeIx, rnXclBackIx, rnXclPattern, nForeColorId, nBackColorId );
+}
+
+Color XclExpPalette::GetColor( sal_uInt16 nXclIndex ) const
+{
+ return mxImpl->GetColor( nXclIndex );
+}
+
+void XclExpPalette::Save( XclExpStream& rStrm )
+{
+ if( !mxImpl->IsDefaultPalette() )
+ XclExpRecord::Save( rStrm );
+}
+
+void XclExpPalette::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !mxImpl->IsDefaultPalette() )
+ mxImpl->SaveXml( rStrm );
+}
+
+void XclExpPalette::WriteBody( XclExpStream& rStrm )
+{
+ mxImpl->WriteBody( rStrm );
+}
+
+// FONT record - font information =============================================
+
+namespace {
+
+typedef ::std::pair< sal_uInt16, sal_Int16 > WhichAndScript;
+
+sal_Int16 lclCheckFontItems( const SfxItemSet& rItemSet,
+ const WhichAndScript& rWAS1, const WhichAndScript& rWAS2, const WhichAndScript& rWAS3 )
+{
+ if( ScfTools::CheckItem( rItemSet, rWAS1.first, false ) ) return rWAS1.second;
+ if( ScfTools::CheckItem( rItemSet, rWAS2.first, false ) ) return rWAS2.second;
+ if( ScfTools::CheckItem( rItemSet, rWAS3.first, false ) ) return rWAS3.second;
+ return 0;
+};
+
+} // namespace
+
+sal_Int16 XclExpFontHelper::GetFirstUsedScript( const XclExpRoot& rRoot, const SfxItemSet& rItemSet )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+ /* #i17050# #i107170# We need to determine which font items are set in the
+ item set, and which script type we should prefer according to the
+ current languages and locales. */
+
+ static const WhichAndScript WAS_LATIN( ATTR_FONT, css::i18n::ScriptType::LATIN );
+ static const WhichAndScript WAS_ASIAN( ATTR_CJK_FONT, css::i18n::ScriptType::ASIAN );
+ static const WhichAndScript WAS_CMPLX( ATTR_CTL_FONT, css::i18n::ScriptType::COMPLEX );
+
+ /* do not let a font from a parent style override an explicit
+ cell font. */
+
+ sal_Int16 nDefScript = rRoot.GetDefApiScript();
+ sal_Int16 nScript = 0;
+ const SfxItemSet* pCurrSet = &rItemSet;
+
+ while( (nScript == 0) && pCurrSet )
+ {
+ switch( nDefScript )
+ {
+ case ApiScriptType::LATIN:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_LATIN, WAS_CMPLX, WAS_ASIAN );
+ break;
+ case ApiScriptType::ASIAN:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_ASIAN, WAS_CMPLX, WAS_LATIN );
+ break;
+ case ApiScriptType::COMPLEX:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_CMPLX, WAS_ASIAN, WAS_LATIN );
+ break;
+ default:
+ OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
+ nScript = ApiScriptType::LATIN;
+ };
+ pCurrSet = pCurrSet->GetParent();
+ }
+
+ if (nScript == 0)
+ nScript = nDefScript;
+
+ if (nScript == 0)
+ {
+ OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
+ nScript = ApiScriptType::LATIN;
+ }
+
+ return nScript;
+}
+
+vcl::Font XclExpFontHelper::GetFontFromItemSet( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript )
+{
+ // if WEAK is passed, guess script type from existing items in the item set
+ if( nScript == css::i18n::ScriptType::WEAK )
+ nScript = GetFirstUsedScript( rRoot, rItemSet );
+
+ // convert to core script type constants
+ SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
+
+ // fill the font object
+ vcl::Font aFont;
+ ScPatternAttr::fillFontOnly(aFont, rItemSet, nullptr, nullptr, nullptr, nScScript);
+ return aFont;
+}
+
+ScDxfFont XclExpFontHelper::GetDxfFontFromItemSet(const XclExpRoot& rRoot, const SfxItemSet& rItemSet)
+{
+ sal_Int16 nScript = GetFirstUsedScript(rRoot, rItemSet);
+
+ // convert to core script type constants
+ SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
+ return ScPatternAttr::GetDxfFont(rItemSet, nScScript);
+}
+
+bool XclExpFontHelper::CheckItems( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript, bool bDeep )
+{
+ static const sal_uInt16 pnCommonIds[] = {
+ ATTR_FONT_UNDERLINE, ATTR_FONT_CROSSEDOUT, ATTR_FONT_CONTOUR,
+ ATTR_FONT_SHADOWED, ATTR_FONT_COLOR, ATTR_FONT_LANGUAGE, 0 };
+ static const sal_uInt16 pnLatinIds[] = {
+ ATTR_FONT, ATTR_FONT_HEIGHT, ATTR_FONT_WEIGHT, ATTR_FONT_POSTURE, 0 };
+ static const sal_uInt16 pnAsianIds[] = {
+ ATTR_CJK_FONT, ATTR_CJK_FONT_HEIGHT, ATTR_CJK_FONT_WEIGHT, ATTR_CJK_FONT_POSTURE, 0 };
+ static const sal_uInt16 pnComplexIds[] = {
+ ATTR_CTL_FONT, ATTR_CTL_FONT_HEIGHT, ATTR_CTL_FONT_WEIGHT, ATTR_CTL_FONT_POSTURE, 0 };
+
+ bool bUsed = ScfTools::CheckItems( rItemSet, pnCommonIds, bDeep );
+ if( !bUsed )
+ {
+ namespace ApiScriptType = css::i18n::ScriptType;
+ // if WEAK is passed, guess script type from existing items in the item set
+ if( nScript == ApiScriptType::WEAK )
+ nScript = GetFirstUsedScript( rRoot, rItemSet );
+ // check the correct items
+ switch( nScript )
+ {
+ case ApiScriptType::LATIN: bUsed = ScfTools::CheckItems( rItemSet, pnLatinIds, bDeep ); break;
+ case ApiScriptType::ASIAN: bUsed = ScfTools::CheckItems( rItemSet, pnAsianIds, bDeep ); break;
+ case ApiScriptType::COMPLEX: bUsed = ScfTools::CheckItems( rItemSet, pnComplexIds, bDeep ); break;
+ default: OSL_FAIL( "XclExpFontHelper::CheckItems - unknown script type" );
+ }
+ }
+ return bUsed;
+}
+
+namespace {
+
+std::size_t lclCalcHash( const XclFontData& rFontData )
+{
+ std::size_t seed = 0;
+ o3tl::hash_combine(seed, rFontData.maName);
+ o3tl::hash_combine(seed, rFontData.maComplexColor);
+ o3tl::hash_combine(seed, rFontData.mnWeight);
+ o3tl::hash_combine(seed, rFontData.mnCharSet);
+ o3tl::hash_combine(seed, rFontData.mnFamily);
+ o3tl::hash_combine(seed, rFontData.mnHeight);
+ o3tl::hash_combine(seed, rFontData.mnUnderline);
+ o3tl::hash_combine(seed, rFontData.mnEscapem);
+ o3tl::hash_combine(seed, rFontData.mbItalic);
+ o3tl::hash_combine(seed, rFontData.mbStrikeout);
+ o3tl::hash_combine(seed, rFontData.mbOutline);
+ o3tl::hash_combine(seed, rFontData.mbShadow);
+ return seed;
+}
+
+} // namespace
+
+XclExpFont::XclExpFont( const XclExpRoot& rRoot,
+ const XclFontData& rFontData, XclExpColorType eColorType ) :
+ XclExpRecord( EXC_ID2_FONT, 14 ),
+ XclExpRoot( rRoot ),
+ maData( rFontData )
+{
+ // insert font color into palette
+ mnColorId = rRoot.GetPalette().InsertColor(rFontData.maComplexColor.getFinalColor(), eColorType, EXC_COLOR_FONTAUTO);
+ // hash value for faster comparison
+ mnHash = lclCalcHash( maData );
+ // record size
+ sal_Int32 nStrLen = maData.maName.getLength();
+ SetRecSize( ((GetBiff() == EXC_BIFF8) ? (nStrLen * 2 + 1) : nStrLen) + 15 );
+}
+
+bool XclExpFont::Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const
+{
+ return (mnHash == nHash) && (maData == rFontData);
+}
+
+void XclExpFont::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_font);
+ XclXmlUtils::WriteFontData( rStyleSheet, maData, XML_name );
+ // OOXTODO: XML_scheme; //scheme/@val values: "major", "minor", "none"
+ rStyleSheet->endElement( XML_font );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpFont::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nAttr = EXC_FONTATTR_NONE;
+ ::set_flag( nAttr, EXC_FONTATTR_ITALIC, maData.mbItalic );
+ if( maData.mnUnderline > 0 )
+ ::set_flag( nAttr, EXC_FONTATTR_UNDERLINE, true );
+ ::set_flag( nAttr, EXC_FONTATTR_STRIKEOUT, maData.mbStrikeout );
+ ::set_flag( nAttr, EXC_FONTATTR_OUTLINE, maData.mbOutline );
+ ::set_flag( nAttr, EXC_FONTATTR_SHADOW, maData.mbShadow );
+
+ OSL_ENSURE( maData.maName.getLength() < 256, "XclExpFont::WriteBody - font name too long" );
+ XclExpString aFontName;
+ if( GetBiff() <= EXC_BIFF5 )
+ aFontName.AssignByte( maData.maName, GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aFontName.Assign( maData.maName, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength );
+
+ rStrm << maData.mnHeight
+ << nAttr
+ << GetPalette().GetColorIndex( mnColorId )
+ << maData.mnWeight
+ << maData.mnEscapem
+ << maData.mnUnderline
+ << maData.mnFamily
+ << maData.mnCharSet
+ << sal_uInt8( 0 )
+ << aFontName;
+}
+
+XclExpDxfFont::XclExpDxfFont(const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet):
+ XclExpRoot(rRoot)
+{
+ maDxfData = XclExpFontHelper::GetDxfFontFromItemSet(rRoot, rItemSet);
+}
+
+namespace {
+
+const char* getUnderlineOOXValue(FontLineStyle eUnderline)
+{
+ switch (eUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_DONTKNOW:
+ return "none";
+ case LINESTYLE_DOUBLE:
+ case LINESTYLE_DOUBLEWAVE:
+ return "double";
+ default:
+ return "single";
+ }
+}
+
+const char* getFontFamilyOOXValue(FontFamily eValue)
+{
+ switch (eValue)
+ {
+ case FAMILY_DONTKNOW:
+ return "0";
+ case FAMILY_SWISS:
+ case FAMILY_SYSTEM:
+ return "2";
+ case FAMILY_ROMAN:
+ return "1";
+ case FAMILY_SCRIPT:
+ return "4";
+ case FAMILY_MODERN:
+ return "3";
+ case FAMILY_DECORATIVE:
+ return "5";
+ default:
+ return "0";
+ }
+}
+
+}
+
+void XclExpDxfFont::SaveXml(XclExpXmlStream& rStrm)
+{
+ if (maDxfData.isEmpty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_font);
+
+ if (maDxfData.pFontAttr)
+ {
+ OUString aFontName = (*maDxfData.pFontAttr)->GetFamilyName();
+
+ aFontName = XclTools::GetXclFontName(aFontName);
+ if (!aFontName.isEmpty())
+ {
+ rStyleSheet->singleElement(XML_name, XML_val, aFontName);
+ }
+
+ rtl_TextEncoding eTextEnc = (*maDxfData.pFontAttr)->GetCharSet();
+ sal_uInt8 nExcelCharSet = rtl_getBestWindowsCharsetFromTextEncoding(eTextEnc);
+ if (nExcelCharSet)
+ {
+ rStyleSheet->singleElement(XML_charset, XML_val, OString::number(nExcelCharSet));
+ }
+
+ FontFamily eFamily = (*maDxfData.pFontAttr)->GetFamily();
+ const char* pVal = getFontFamilyOOXValue(eFamily);
+ if (pVal)
+ {
+ rStyleSheet->singleElement(XML_family, XML_val, pVal);
+ }
+ }
+
+ if (maDxfData.eWeight)
+ {
+ rStyleSheet->singleElement(XML_b,
+ XML_val, ToPsz10(*maDxfData.eWeight != WEIGHT_NORMAL));
+ }
+
+ if (maDxfData.eItalic)
+ {
+ bool bItalic = (*maDxfData.eItalic == ITALIC_OBLIQUE) || (*maDxfData.eItalic == ITALIC_NORMAL);
+ rStyleSheet->singleElement(XML_i, XML_val, ToPsz10(bItalic));
+ }
+
+ if (maDxfData.eStrike)
+ {
+ bool bStrikeout =
+ (*maDxfData.eStrike == STRIKEOUT_SINGLE) || (*maDxfData.eStrike == STRIKEOUT_DOUBLE) ||
+ (*maDxfData.eStrike == STRIKEOUT_BOLD) || (*maDxfData.eStrike == STRIKEOUT_SLASH) ||
+ (*maDxfData.eStrike == STRIKEOUT_X);
+
+ rStyleSheet->singleElement(XML_strike, XML_val, ToPsz10(bStrikeout));
+ }
+
+ if (maDxfData.bOutline)
+ {
+ rStyleSheet->singleElement(XML_outline, XML_val, ToPsz10(*maDxfData.bOutline));
+ }
+
+ if (maDxfData.bShadow)
+ {
+ rStyleSheet->singleElement(XML_shadow, XML_val, ToPsz10(*maDxfData.bShadow));
+ }
+
+ if (maDxfData.aColor)
+ {
+ rStyleSheet->singleElement(XML_color,
+ XML_rgb, XclXmlUtils::ToOString(*maDxfData.aColor));
+ }
+
+ if (maDxfData.nFontHeight)
+ {
+ rStyleSheet->singleElement(XML_sz,
+ XML_val, OString::number(*maDxfData.nFontHeight/20));
+ }
+
+ if (maDxfData.eUnder)
+ {
+ const char* pVal = getUnderlineOOXValue(*maDxfData.eUnder);
+ rStyleSheet->singleElement(XML_u, XML_val, pVal);
+ }
+
+ rStyleSheet->endElement(XML_font);
+}
+
+XclExpBlindFont::XclExpBlindFont( const XclExpRoot& rRoot ) :
+ XclExpFont( rRoot, XclFontData(), EXC_COLOR_CELLTEXT )
+{
+}
+
+bool XclExpBlindFont::Equals( const XclFontData& /*rFontData*/, sal_uInt32 /*nHash*/ ) const
+{
+ return false;
+}
+
+void XclExpBlindFont::Save( XclExpStream& /*rStrm*/ )
+{
+ // do nothing
+}
+
+XclExpFontBuffer::XclExpFontBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnXclMaxSize( 0 )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF4: mnXclMaxSize = EXC_FONT_MAXCOUNT4; break;
+ case EXC_BIFF5: mnXclMaxSize = EXC_FONT_MAXCOUNT5; break;
+ case EXC_BIFF8: mnXclMaxSize = EXC_FONT_MAXCOUNT8; break;
+ default: DBG_ERROR_BIFF();
+ }
+ InitDefaultFonts();
+}
+
+const XclExpFont* XclExpFontBuffer::GetFont( sal_uInt16 nXclFont ) const
+{
+ return maFontList.GetRecord( nXclFont );
+}
+
+const XclFontData& XclExpFontBuffer::GetAppFontData() const
+{
+ return maFontList.GetRecord( EXC_FONT_APP )->GetFontData(); // exists always
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(
+ const XclFontData& rFontData, XclExpColorType eColorType, bool bAppFont )
+{
+ if( bAppFont )
+ {
+ XclExpFontRef xFont = new XclExpFont( GetRoot(), rFontData, eColorType );
+ maFontList.ReplaceRecord( xFont, EXC_FONT_APP );
+ // set width of '0' character for column width export
+ SetCharWidth( xFont->GetFontData() );
+ return EXC_FONT_APP;
+ }
+
+ size_t nPos = Find( rFontData );
+ if( nPos == EXC_FONTLIST_NOTFOUND )
+ {
+ // not found in buffer - create new font
+ size_t nSize = maFontList.GetSize();
+ if( nSize < mnXclMaxSize )
+ {
+ // possible to insert
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), rFontData, eColorType ) );
+ nPos = nSize; // old size is last position now
+ }
+ else
+ {
+ // buffer is full - ignore new font, use default font
+ nPos = EXC_FONT_APP;
+ }
+ }
+ return static_cast< sal_uInt16 >( nPos );
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(const SvxFont& rFont, model::ComplexColor const& rComplexColor, XclExpColorType eColorType )
+{
+ return Insert(XclFontData(rFont, rComplexColor), eColorType);
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(const SfxItemSet& rItemSet, sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont )
+{
+ // #i17050# script type now provided by caller
+ vcl::Font aFont = XclExpFontHelper::GetFontFromItemSet(GetRoot(), rItemSet, nScript);
+ model::ComplexColor aComplexColor;
+ ScPatternAttr::fillColor(aComplexColor, rItemSet, ScAutoFontColorMode::Raw);
+ return Insert(XclFontData(aFont, aComplexColor), eColorType, bAppFont );
+}
+
+void XclExpFontBuffer::Save( XclExpStream& rStrm )
+{
+ maFontList.Save( rStrm );
+}
+
+void XclExpFontBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFontList.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fonts, XML_count, OString::number(maFontList.GetSize()));
+
+ maFontList.SaveXml( rStrm );
+
+ rStyleSheet->endElement( XML_fonts );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpFontBuffer::InitDefaultFonts()
+{
+ XclFontData aFontData;
+ aFontData.maName = "Arial";
+ aFontData.SetScFamily( FAMILY_DONTKNOW );
+ aFontData.SetFontEncoding( ScfTools::GetSystemTextEncoding() );
+ aFontData.SetScHeight( 200 ); // 200 twips = 10 pt
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_BOLD );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+ aFontData.SetScPosture( ITALIC_NORMAL );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_BOLD );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ // the blind font with index 4
+ maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
+ // already add the first user defined font (Excel does it too)
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+ aFontData.SetScPosture( ITALIC_NONE );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ }
+ break;
+ case EXC_BIFF8:
+ {
+ XclExpFontRef xFont = new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ if( GetOutput() == EXC_OUTPUT_BINARY )
+ // the blind font with index 4
+ maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+size_t XclExpFontBuffer::Find( const XclFontData& rFontData )
+{
+ sal_uInt32 nHash = lclCalcHash( rFontData );
+ for( size_t nPos = 0, nSize = maFontList.GetSize(); nPos < nSize; ++nPos )
+ if( maFontList.GetRecord( nPos )->Equals( rFontData, nHash ) )
+ return nPos;
+ return EXC_FONTLIST_NOTFOUND;
+}
+
+// FORMAT record - number formats =============================================
+
+namespace {
+
+/** Predicate for search algorithm. */
+struct XclExpNumFmtPred
+{
+ sal_uInt32 mnScNumFmt;
+ explicit XclExpNumFmtPred( sal_uInt32 nScNumFmt ) : mnScNumFmt( nScNumFmt ) {}
+ bool operator()( const XclExpNumFmt& rFormat ) const
+ { return rFormat.mnScNumFmt == mnScNumFmt; }
+};
+
+}
+
+void XclExpNumFmt::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->singleElement( XML_numFmt,
+ XML_numFmtId, OString::number(mnXclNumFmt),
+ XML_formatCode, maNumFmtString );
+}
+
+XclExpNumFmtBuffer::XclExpNumFmtBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxFormatter( new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) ),
+ mpKeywordTable( new NfKeywordTable ),
+ mnStdFmt( GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: mnXclOffset = EXC_FORMAT_OFFSET5; break;
+ case EXC_BIFF8: mnXclOffset = EXC_FORMAT_OFFSET8; break;
+ default: mnXclOffset = 0; DBG_ERROR_BIFF();
+ }
+
+ mxFormatter->FillKeywordTableForExcel( *mpKeywordTable );
+}
+
+XclExpNumFmtBuffer::~XclExpNumFmtBuffer()
+{
+}
+
+sal_uInt16 XclExpNumFmtBuffer::Insert( sal_uInt32 nScNumFmt )
+{
+ XclExpNumFmtVec::const_iterator aIt =
+ ::std::find_if( maFormatMap.begin(), maFormatMap.end(), XclExpNumFmtPred( nScNumFmt ) );
+ if( aIt != maFormatMap.end() )
+ return aIt->mnXclNumFmt;
+
+ size_t nSize = maFormatMap.size();
+ if( nSize < o3tl::make_unsigned( 0xFFFF - mnXclOffset ) )
+ {
+ sal_uInt16 nXclNumFmt = static_cast< sal_uInt16 >( nSize + mnXclOffset );
+ maFormatMap.emplace_back( nScNumFmt, nXclNumFmt, GetFormatCode( nScNumFmt ) );
+ return nXclNumFmt;
+ }
+
+ return 0;
+}
+
+void XclExpNumFmtBuffer::Save( XclExpStream& rStrm )
+{
+ for( const auto& rEntry : maFormatMap )
+ WriteFormatRecord( rStrm, rEntry );
+}
+
+void XclExpNumFmtBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFormatMap.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_numFmts, XML_count, OString::number(maFormatMap.size()));
+ for( auto& rEntry : maFormatMap )
+ {
+ rEntry.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_numFmts );
+}
+
+void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const OUString& rFormatStr )
+{
+ XclExpString aExpStr;
+ if( GetBiff() <= EXC_BIFF5 )
+ aExpStr.AssignByte( rFormatStr, GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aExpStr.Assign( rFormatStr );
+
+ rStrm.StartRecord( EXC_ID4_FORMAT, 2 + aExpStr.GetSize() );
+ rStrm << nXclNumFmt << aExpStr;
+ rStrm.EndRecord();
+}
+
+void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat )
+{
+ WriteFormatRecord( rStrm, rFormat.mnXclNumFmt, GetFormatCode( rFormat.mnScNumFmt ) );
+}
+
+namespace {
+
+OUString GetNumberFormatCode(const XclRoot& rRoot, const sal_uInt32 nScNumFmt, SvNumberFormatter* pFormatter, const NfKeywordTable* pKeywordTable)
+{
+ return rRoot.GetFormatter().GetFormatStringForExcel( nScNumFmt, *pKeywordTable, *pFormatter);
+}
+
+}
+
+OUString XclExpNumFmtBuffer::GetFormatCode( sal_uInt32 nScNumFmt )
+{
+ return GetNumberFormatCode( *this, nScNumFmt, mxFormatter.get(), mpKeywordTable.get() );
+}
+
+// XF, STYLE record - Cell formatting =========================================
+
+bool XclExpCellProt::FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle )
+{
+ const ScProtectionAttr& rProtItem = rItemSet.Get( ATTR_PROTECTION );
+ mbLocked = rProtItem.GetProtection();
+ mbHidden = rProtItem.GetHideFormula() || rProtItem.GetHideCell();
+ return ScfTools::CheckItem( rItemSet, ATTR_PROTECTION, bStyle );
+}
+
+void XclExpCellProt::FillToXF3( sal_uInt16& rnProt ) const
+{
+ ::set_flag( rnProt, EXC_XF_LOCKED, mbLocked );
+ ::set_flag( rnProt, EXC_XF_HIDDEN, mbHidden );
+}
+
+void XclExpCellProt::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_protection,
+ XML_locked, ToPsz( mbLocked ),
+ XML_hidden, ToPsz( mbHidden ) );
+}
+
+bool XclExpCellAlign::FillFromItemSet(const XclRoot& rRoot, const SfxItemSet& rItemSet,
+ bool bForceLineBreak, XclBiff eBiff, bool bStyle)
+{
+ bool bUsed = false;
+ SvxCellHorJustify eHorAlign = rItemSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
+ SvxCellVerJustify eVerAlign = rItemSet.Get( ATTR_VER_JUSTIFY ).GetValue();
+
+ switch( eBiff )
+ {
+ case EXC_BIFF8: // attributes new in BIFF8
+ {
+ // text indent
+ tools::Long nTmpIndent = rItemSet.Get( ATTR_INDENT ).GetValue(); // already in twips
+ tools::Long nSpaceWidth = rRoot.GetSpaceWidth();
+ sal_Int32 nIndent = static_cast<double>(nTmpIndent) / (3.0 * nSpaceWidth) + 0.5;
+ mnIndent = limit_cast< sal_uInt8 >( nIndent, 0, 15 );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_INDENT, bStyle );
+
+ // shrink to fit
+ mbShrink = rItemSet.Get( ATTR_SHRINKTOFIT ).GetValue();
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_SHRINKTOFIT, bStyle );
+
+ // CTL text direction
+ SetScFrameDir( rItemSet.Get( ATTR_WRITINGDIR ).GetValue() );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_WRITINGDIR, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF5: // attributes new in BIFF5
+ case EXC_BIFF4: // attributes new in BIFF4
+ {
+ // vertical alignment
+ SetScVerAlign( eVerAlign );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_VER_JUSTIFY, bStyle );
+
+ // stacked/rotation
+ bool bStacked = rItemSet.Get( ATTR_STACKED ).GetValue();
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_STACKED, bStyle );
+ if( bStacked )
+ {
+ mnRotation = EXC_ROT_STACKED;
+ }
+ else
+ {
+ // rotation
+ Degree100 nScRot = rItemSet.Get( ATTR_ROTATE_VALUE ).GetValue();
+ mnRotation = XclTools::GetXclRotation( nScRot );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_ROTATE_VALUE, bStyle );
+ }
+ mnOrient = XclTools::GetXclOrientFromRot( mnRotation );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF3: // attributes new in BIFF3
+ {
+ // text wrap
+ mbLineBreak = bForceLineBreak;
+ bUsed |= bForceLineBreak || ScfTools::CheckItem( rItemSet, ATTR_LINEBREAK, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF2: // attributes new in BIFF2
+ {
+ // horizontal alignment
+ SetScHorAlign( eHorAlign );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_HOR_JUSTIFY, bStyle );
+ }
+
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ if (eBiff == EXC_BIFF8)
+ {
+ // Adjust for distributed alignments.
+ if (eHorAlign == SvxCellHorJustify::Block)
+ {
+ SvxCellJustifyMethod eHorJustMethod =
+ rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_HOR_JUSTIFY_METHOD)->GetValue();
+ if (eHorJustMethod == SvxCellJustifyMethod::Distribute)
+ mnHorAlign = EXC_XF_HOR_DISTRIB;
+ }
+
+ if (eVerAlign == SvxCellVerJustify::Block)
+ {
+ SvxCellJustifyMethod eVerJustMethod =
+ rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_VER_JUSTIFY_METHOD)->GetValue();
+ if (eVerJustMethod == SvxCellJustifyMethod::Distribute)
+ mnVerAlign = EXC_XF_VER_DISTRIB;
+ }
+ }
+
+ return bUsed;
+}
+
+void XclExpCellAlign::FillToXF5( sal_uInt16& rnAlign ) const
+{
+ ::insert_value( rnAlign, mnHorAlign, 0, 3 );
+ ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
+ ::insert_value( rnAlign, mnVerAlign, 4, 3 );
+ ::insert_value( rnAlign, mnOrient, 8, 2 );
+}
+
+void XclExpCellAlign::FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const
+{
+ ::insert_value( rnAlign, mnHorAlign, 0, 3 );
+ ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
+ ::insert_value( rnAlign, mnVerAlign, 4, 3 );
+ ::insert_value( rnAlign, mnRotation, 8, 8 );
+ ::insert_value( rnMiscAttrib, mnIndent, 0, 4 );
+ ::set_flag( rnMiscAttrib, EXC_XF8_SHRINK, mbShrink );
+ ::insert_value( rnMiscAttrib, mnTextDir, 6, 2 );
+}
+
+static const char* ToHorizontalAlignment( sal_uInt8 nHorAlign )
+{
+ switch( nHorAlign )
+ {
+ case EXC_XF_HOR_GENERAL: return "general";
+ case EXC_XF_HOR_LEFT: return "left";
+ case EXC_XF_HOR_CENTER: return "center";
+ case EXC_XF_HOR_RIGHT: return "right";
+ case EXC_XF_HOR_FILL: return "fill";
+ case EXC_XF_HOR_JUSTIFY: return "justify";
+ case EXC_XF_HOR_CENTER_AS: return "centerContinuous";
+ case EXC_XF_HOR_DISTRIB: return "distributed";
+ }
+ return "*unknown*";
+}
+
+static const char* ToVerticalAlignment( sal_uInt8 nVerAlign )
+{
+ switch( nVerAlign )
+ {
+ case EXC_XF_VER_TOP: return "top";
+ case EXC_XF_VER_CENTER: return "center";
+ case EXC_XF_VER_BOTTOM: return "bottom";
+ case EXC_XF_VER_JUSTIFY: return "justify";
+ case EXC_XF_VER_DISTRIB: return "distributed";
+ }
+ return "*unknown*";
+}
+
+void XclExpCellAlign::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_alignment,
+ XML_horizontal, ToHorizontalAlignment( mnHorAlign ),
+ XML_vertical, ToVerticalAlignment( mnVerAlign ),
+ XML_textRotation, OString::number(mnRotation),
+ XML_wrapText, ToPsz( mbLineBreak ),
+ XML_indent, OString::number(mnIndent),
+ // OOXTODO: XML_relativeIndent, mnIndent?
+ // OOXTODO: XML_justifyLastLine,
+ XML_shrinkToFit, ToPsz( mbShrink ),
+ XML_readingOrder, sax_fastparser::UseIf(OString::number(mnTextDir), mnTextDir != EXC_XF_TEXTDIR_CONTEXT) );
+}
+
+namespace {
+
+void lclGetBorderLine(
+ sal_uInt8& rnXclLine, sal_uInt32& rnColorId, model::ComplexColor& rComplexColor,
+ const ::editeng::SvxBorderLine* pLine, XclExpPalette& rPalette, XclBiff eBiff )
+{
+ // Document: sc/qa/unit/data/README.cellborders
+
+ enum CalcLineIndex{Idx_None, Idx_Solid, Idx_Dotted, Idx_Dashed, Idx_FineDashed, Idx_DashDot, Idx_DashDotDot, Idx_DoubleThin, Idx_Last};
+ enum ExcelWidthIndex{Width_Hair, Width_Thin, Width_Medium, Width_Thick, Width_Last};
+ static sal_uInt8 Map_LineLO_toMS[Idx_Last][Width_Last] =
+ {
+ // 0,05 - 0,74 0,75 - 1,49 1,50 - 2,49 2,50 - 9,00 Width Range [pt]
+ // EXC_BORDER_HAIR EXC_BORDER_THIN EXC_BORDER_MEDIUM EXC_BORDER_THICK MS Width
+ {EXC_LINE_NONE , EXC_LINE_NONE , EXC_LINE_NONE , EXC_LINE_NONE }, // 0 BorderLineStyle::NONE
+ {EXC_LINE_HAIR , EXC_LINE_THIN , EXC_LINE_MEDIUM , EXC_LINE_THICK }, // 1 BorderLineStyle::SOLID
+ {EXC_LINE_DOTTED , EXC_LINE_DOTTED , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, // 2 BorderLineStyle::DOTTED
+ {EXC_LINE_DOTTED , EXC_LINE_DASHED , EXC_LINE_MEDIUM_DASHED , EXC_LINE_MEDIUM_DASHED }, // 3 BorderLineStyle::DASHED
+ {EXC_LINE_DASHED , EXC_LINE_DASHED , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, // 4 BorderLineStyle::FINE_DASHED
+ {EXC_LINE_DASHED , EXC_LINE_THIN_DASHDOT , EXC_LINE_MEDIUM_DASHDOT , EXC_LINE_MEDIUM_DASHDOT }, // 5 BorderLineStyle::DASH_DOT
+ {EXC_LINE_DASHED , EXC_LINE_THIN_DASHDOTDOT , EXC_LINE_MEDIUM_DASHDOTDOT , EXC_LINE_MEDIUM_DASHDOTDOT }, // 6 BorderLineStyle::DASH_DOT_DOT
+ {EXC_LINE_DOUBLE , EXC_LINE_DOUBLE , EXC_LINE_DOUBLE , EXC_LINE_DOUBLE } // 7 BorderLineStyle::DOUBLE_THIN
+ }; // Line Name
+
+ rnXclLine = EXC_LINE_NONE;
+ if( pLine )
+ {
+ sal_uInt16 nOuterWidth = pLine->GetOutWidth();
+ ExcelWidthIndex nOuterWidthIndx;
+ CalcLineIndex nStyleIndex;
+
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::NONE:
+ nStyleIndex = Idx_None;
+ break;
+ case SvxBorderLineStyle::SOLID:
+ nStyleIndex = Idx_Solid;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ nStyleIndex = Idx_Dotted;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ nStyleIndex = Idx_Dashed;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ nStyleIndex = Idx_FineDashed;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ nStyleIndex = Idx_DashDot;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ nStyleIndex = Idx_DashDotDot;
+ break;
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ // the "nOuterWidth" is not right for this line type
+ // but at the moment width it not important for that
+ // the right function is nOuterWidth = (sal_uInt16) pLine->GetWidth();
+ nStyleIndex = Idx_DoubleThin;
+ break;
+ default:
+ nStyleIndex = Idx_Solid;
+ }
+
+ if( nOuterWidth >= EXC_BORDER_THICK )
+ nOuterWidthIndx = Width_Thick;
+ else if( nOuterWidth >= EXC_BORDER_MEDIUM )
+ nOuterWidthIndx = Width_Medium;
+ else if( nOuterWidth >= EXC_BORDER_THIN )
+ nOuterWidthIndx = Width_Thin;
+ else if ( nOuterWidth >= EXC_BORDER_HAIR )
+ nOuterWidthIndx = Width_Hair;
+ else
+ nOuterWidthIndx = Width_Thin;
+
+ rnXclLine = Map_LineLO_toMS[nStyleIndex][nOuterWidthIndx];
+ }
+
+ if( (eBiff == EXC_BIFF2) && (rnXclLine != EXC_LINE_NONE) )
+ rnXclLine = EXC_LINE_THIN;
+
+ if (pLine && (rnXclLine != EXC_LINE_NONE))
+ {
+ rnColorId = rPalette.InsertColor(pLine->GetColor(), EXC_COLOR_CELLBORDER);
+ rComplexColor = pLine->getComplexColor();
+ }
+ else
+ {
+ rnColorId = XclExpPalette::GetColorIdFromIndex(0);
+ }
+}
+
+} // namespace
+
+XclExpCellBorder::XclExpCellBorder() :
+ mnLeftColorId( XclExpPalette::GetColorIdFromIndex( mnLeftColor ) ),
+ mnRightColorId( XclExpPalette::GetColorIdFromIndex( mnRightColor ) ),
+ mnTopColorId( XclExpPalette::GetColorIdFromIndex( mnTopColor ) ),
+ mnBottomColorId( XclExpPalette::GetColorIdFromIndex( mnBottomColor ) ),
+ mnDiagColorId( XclExpPalette::GetColorIdFromIndex( mnDiagColor ) )
+{
+}
+
+bool XclExpCellBorder::FillFromItemSet(
+ const SfxItemSet& rItemSet, XclExpPalette& rPalette, XclBiff eBiff, bool bStyle )
+{
+ bool bUsed = false;
+
+ switch( eBiff )
+ {
+ case EXC_BIFF8: // attributes new in BIFF8
+ {
+ const SvxLineItem& rTLBRItem = rItemSet.Get( ATTR_BORDER_TLBR );
+ sal_uInt8 nTLBRLine;
+ sal_uInt32 nTLBRColorId;
+ model::ComplexColor aTLBRComplexColor;
+ lclGetBorderLine( nTLBRLine, nTLBRColorId, aTLBRComplexColor, rTLBRItem.GetLine(), rPalette, eBiff );
+ mbDiagTLtoBR = (nTLBRLine != EXC_LINE_NONE);
+
+ const SvxLineItem& rBLTRItem = rItemSet.Get( ATTR_BORDER_BLTR );
+ sal_uInt8 nBLTRLine;
+ sal_uInt32 nBLTRColorId;
+ model::ComplexColor aBLTRComplexColor;
+ lclGetBorderLine( nBLTRLine, nBLTRColorId, aBLTRComplexColor, rBLTRItem.GetLine(), rPalette, eBiff );
+ mbDiagBLtoTR = (nBLTRLine != EXC_LINE_NONE);
+
+ if( ::ScHasPriority( rTLBRItem.GetLine(), rBLTRItem.GetLine() ) )
+ {
+ mnDiagLine = nTLBRLine;
+ mnDiagColorId = nTLBRColorId;
+ }
+ else
+ {
+ mnDiagLine = nBLTRLine;
+ mnDiagColorId = nBLTRColorId;
+ }
+
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER_TLBR, bStyle ) ||
+ ScfTools::CheckItem( rItemSet, ATTR_BORDER_BLTR, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF5:
+ case EXC_BIFF4:
+ case EXC_BIFF3:
+ case EXC_BIFF2:
+ {
+ const SvxBoxItem& rBoxItem = rItemSet.Get( ATTR_BORDER );
+
+ lclGetBorderLine(mnLeftLine, mnLeftColorId, maComplexColorLeft, rBoxItem.GetLeft(), rPalette, eBiff);
+ lclGetBorderLine(mnRightLine, mnRightColorId, maComplexColorRight, rBoxItem.GetRight(), rPalette, eBiff);
+ lclGetBorderLine(mnTopLine, mnTopColorId, maComplexColorTop, rBoxItem.GetTop(), rPalette, eBiff);
+ lclGetBorderLine(mnBottomLine, mnBottomColorId, maComplexColorBottom, rBoxItem.GetBottom(), rPalette, eBiff);
+
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER, bStyle );
+ }
+
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ return bUsed;
+}
+
+void XclExpCellBorder::SetFinalColors( const XclExpPalette& rPalette )
+{
+ mnLeftColor = rPalette.GetColorIndex( mnLeftColorId );
+ mnRightColor = rPalette.GetColorIndex( mnRightColorId );
+ mnTopColor = rPalette.GetColorIndex( mnTopColorId );
+ mnBottomColor = rPalette.GetColorIndex( mnBottomColorId );
+ mnDiagColor = rPalette.GetColorIndex( mnDiagColorId );
+}
+
+void XclExpCellBorder::FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const
+{
+ ::insert_value( rnBorder, mnTopLine, 0, 3 );
+ ::insert_value( rnBorder, mnLeftLine, 3, 3 );
+ ::insert_value( rnArea, mnBottomLine, 22, 3 );
+ ::insert_value( rnBorder, mnRightLine, 6, 3 );
+ ::insert_value( rnBorder, mnTopColor, 9, 7 );
+ ::insert_value( rnBorder, mnLeftColor, 16, 7 );
+ ::insert_value( rnArea, mnBottomColor, 25, 7 );
+ ::insert_value( rnBorder, mnRightColor, 23, 7 );
+}
+
+void XclExpCellBorder::FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const
+{
+ ::insert_value( rnBorder1, mnLeftLine, 0, 4 );
+ ::insert_value( rnBorder1, mnRightLine, 4, 4 );
+ ::insert_value( rnBorder1, mnTopLine, 8, 4 );
+ ::insert_value( rnBorder1, mnBottomLine, 12, 4 );
+ ::insert_value( rnBorder1, mnLeftColor, 16, 7 );
+ ::insert_value( rnBorder1, mnRightColor, 23, 7 );
+ ::insert_value( rnBorder2, mnTopColor, 0, 7 );
+ ::insert_value( rnBorder2, mnBottomColor, 7, 7 );
+ ::insert_value( rnBorder2, mnDiagColor, 14, 7 );
+ ::insert_value( rnBorder2, mnDiagLine, 21, 4 );
+ ::set_flag( rnBorder1, EXC_XF_DIAGONAL_TL_TO_BR, mbDiagTLtoBR );
+ ::set_flag( rnBorder1, EXC_XF_DIAGONAL_BL_TO_TR, mbDiagBLtoTR );
+}
+
+void XclExpCellBorder::FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const
+{
+ ::insert_value( rnLine, mnLeftLine, 0, 4 );
+ ::insert_value( rnLine, mnRightLine, 4, 4 );
+ ::insert_value( rnLine, mnTopLine, 8, 4 );
+ ::insert_value( rnLine, mnBottomLine, 12, 4 );
+ ::insert_value( rnColor, mnLeftColor, 0, 7 );
+ ::insert_value( rnColor, mnRightColor, 7, 7 );
+ ::insert_value( rnColor, mnTopColor, 16, 7 );
+ ::insert_value( rnColor, mnBottomColor, 23, 7 );
+}
+
+static const char* ToLineStyle( sal_uInt8 nLineStyle )
+{
+ switch( nLineStyle )
+ {
+ case EXC_LINE_NONE: return "none";
+ case EXC_LINE_THIN: return "thin";
+ case EXC_LINE_MEDIUM: return "medium";
+ case EXC_LINE_THICK: return "thick";
+ case EXC_LINE_DOUBLE: return "double";
+ case EXC_LINE_HAIR: return "hair";
+ case EXC_LINE_DOTTED: return "dotted";
+ case EXC_LINE_DASHED: return "dashed";
+ case EXC_LINE_MEDIUM_DASHED: return "mediumDashed";
+ case EXC_LINE_THIN_DASHDOT: return "dashDot";
+ case EXC_LINE_THIN_DASHDOTDOT: return "dashDotDot";
+ case EXC_LINE_MEDIUM_DASHDOT: return "mediumDashDot";
+ case EXC_LINE_MEDIUM_DASHDOTDOT: return "mediumDashDotDot";
+ case EXC_LINE_MEDIUM_SLANT_DASHDOT: return "slantDashDot";
+ }
+ return "*unknown*";
+}
+
+namespace
+{
+void lcl_WriteBorder(XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor, model::ComplexColor const& rComplexColor)
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ if( nLineStyle == EXC_LINE_NONE )
+ {
+ rStyleSheet->singleElement(nElement);
+ return;
+ }
+ else if (rColor == Color(0, 0, 0) && !rComplexColor.isValidThemeType())
+ {
+ rStyleSheet->singleElement(nElement, XML_style, ToLineStyle(nLineStyle));
+ return;
+ }
+
+ rStyleSheet->startElement(nElement, XML_style, ToLineStyle(nLineStyle));
+ oox::xls::writeComplexColor(rStyleSheet, XML_color, rComplexColor, rColor);
+ rStyleSheet->endElement(nElement);
+}
+} // end anonymous namespace
+
+void XclExpCellBorder::SaveXml(XclExpXmlStream& rStream) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStream.GetCurrentStream();
+
+ XclExpPalette& rPalette = rStream.GetRoot().GetPalette();
+
+ rStyleSheet->startElement( XML_border,
+ XML_diagonalUp, ToPsz( mbDiagBLtoTR ),
+ XML_diagonalDown, ToPsz( mbDiagTLtoBR )
+ // OOXTODO: XML_outline
+ );
+
+ lcl_WriteBorder(rStream, XML_left, mnLeftLine, rPalette.GetColor(mnLeftColor), maComplexColorLeft);
+ lcl_WriteBorder(rStream, XML_right, mnRightLine, rPalette.GetColor(mnRightColor), maComplexColorRight);
+ lcl_WriteBorder(rStream, XML_top, mnTopLine, rPalette.GetColor(mnTopColor), maComplexColorTop);
+ lcl_WriteBorder(rStream, XML_bottom, mnBottomLine, rPalette.GetColor(mnBottomColor), maComplexColorBottom);
+ lcl_WriteBorder(rStream, XML_diagonal, mnDiagLine, rPalette.GetColor(mnDiagColor), maComplexColorDiagonal);
+
+ // OOXTODO: XML_vertical, XML_horizontal
+ rStyleSheet->endElement( XML_border );
+}
+
+XclExpCellArea::XclExpCellArea() :
+ mnForeColorId(XclExpPalette::GetColorIdFromIndex(mnForeColor)),
+ mnBackColorId(XclExpPalette::GetColorIdFromIndex(mnBackColor)),
+ maForeColor(COL_TRANSPARENT),
+ maBackColor(COL_TRANSPARENT)
+{
+}
+
+XclExpCellArea::XclExpCellArea(Color aForeColor, Color aBackColor)
+ : XclCellArea(EXC_PATT_SOLID)
+ , mnForeColorId(0)
+ , mnBackColorId(0)
+ , maForeColor(aForeColor)
+ , maBackColor(aBackColor)
+{
+}
+
+bool XclExpCellArea::FillFromItemSet( const SfxItemSet& rItemSet, XclExpPalette& rPalette, bool bStyle )
+{
+ const SvxBrushItem& rBrushItem = rItemSet.Get( ATTR_BACKGROUND );
+
+ if (rBrushItem.getComplexColor().getType() != model::ColorType::Unused)
+ maForegroundComplexColor = rBrushItem.getComplexColor();
+
+ if( rBrushItem.GetColor().IsTransparent() )
+ {
+ mnPattern = EXC_PATT_NONE;
+ mnForeColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
+ mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWBACK );
+ }
+ else
+ {
+ mnPattern = EXC_PATT_SOLID;
+ mnForeColorId = rPalette.InsertColor( rBrushItem.GetColor(), EXC_COLOR_CELLAREA );
+ mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
+ }
+ return ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, bStyle );
+}
+
+void XclExpCellArea::SetFinalColors( const XclExpPalette& rPalette )
+{
+ rPalette.GetMixedColors( mnForeColor, mnBackColor, mnPattern, mnForeColorId, mnBackColorId );
+}
+
+void XclExpCellArea::FillToXF5( sal_uInt32& rnArea ) const
+{
+ ::insert_value( rnArea, mnPattern, 16, 6 );
+ ::insert_value( rnArea, mnForeColor, 0, 7 );
+ ::insert_value( rnArea, mnBackColor, 7, 7 );
+}
+
+void XclExpCellArea::FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const
+{
+ ::insert_value( rnBorder2, mnPattern, 26, 6 );
+ ::insert_value( rnArea, mnForeColor, 0, 7 );
+ ::insert_value( rnArea, mnBackColor, 7, 7 );
+}
+
+void XclExpCellArea::FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const
+{
+ XclCellArea aTmp( *this );
+ if( !aTmp.IsTransparent() && (aTmp.mnBackColor == EXC_COLOR_WINDOWTEXT) )
+ aTmp.mnBackColor = 0;
+ if( aTmp.mnPattern == EXC_PATT_SOLID )
+ ::std::swap( aTmp.mnForeColor, aTmp.mnBackColor );
+ ::insert_value( rnColor, aTmp.mnForeColor, 0, 7 );
+ ::insert_value( rnColor, aTmp.mnBackColor, 7, 7 );
+ ::insert_value( rnPattern, aTmp.mnPattern, 10, 6 );
+}
+
+static const char* ToPatternType( sal_uInt8 nPattern )
+{
+ switch( nPattern )
+ {
+ case EXC_PATT_NONE: return "none";
+ case EXC_PATT_SOLID: return "solid";
+ case EXC_PATT_50_PERC: return "mediumGray";
+ case EXC_PATT_75_PERC: return "darkGray";
+ case EXC_PATT_25_PERC: return "lightGray";
+ case EXC_PATT_12_5_PERC: return "gray125";
+ case EXC_PATT_6_25_PERC: return "gray0625";
+ }
+ return "*unknown*";
+}
+
+void XclExpCellArea::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fill);
+
+ // OOXTODO: XML_gradientFill
+
+ XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
+
+ if (mnPattern == EXC_PATT_NONE ||
+ (mnForeColor == 0 && mnBackColor == 0 && maForeColor == COL_TRANSPARENT && maBackColor == COL_TRANSPARENT))
+ {
+ rStyleSheet->singleElement(XML_patternFill, XML_patternType, ToPatternType(mnPattern));
+ }
+ else
+ {
+ rStyleSheet->startElement(XML_patternFill, XML_patternType, ToPatternType(mnPattern));
+
+ if (maForeColor != COL_TRANSPARENT || maBackColor != COL_TRANSPARENT)
+ {
+ oox::xls::writeComplexColor(rStyleSheet, XML_fgColor, maForegroundComplexColor, maForeColor);
+ oox::xls::writeComplexColor(rStyleSheet, XML_bgColor, maBackgroundComplexColor, maBackColor);
+ }
+ else
+ {
+ {
+ Color aColor = rPalette.GetColor(mnForeColor);
+ if (maForegroundComplexColor.isValidThemeType() || mnForeColor != 0)
+ oox::xls::writeComplexColor(rStyleSheet, XML_fgColor, maForegroundComplexColor, aColor);
+ else if (mnForeColor != 0)
+ oox::xls::writeComplexColor(rStyleSheet, XML_fgColor, maForegroundComplexColor, aColor);
+ }
+
+ {
+ Color aColor = rPalette.GetColor(mnBackColor);
+ if (maBackgroundComplexColor.isValidThemeType() || mnBackColor != 0)
+ oox::xls::writeComplexColor(rStyleSheet, XML_bgColor, maBackgroundComplexColor, aColor);
+ }
+ }
+ rStyleSheet->endElement( XML_patternFill );
+ }
+
+ rStyleSheet->endElement( XML_fill );
+}
+
+bool XclExpColor::FillFromItemSet( const SfxItemSet& rItemSet )
+{
+ if( !ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true ) )
+ return false;
+
+ const SvxBrushItem& rBrushItem = rItemSet.Get( ATTR_BACKGROUND );
+ maColor = rBrushItem.GetColor();
+ maComplexColor = rBrushItem.getComplexColor();
+
+ return true;
+}
+
+void XclExpColor::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fill);
+ rStyleSheet->startElement(XML_patternFill);
+
+ oox::xls::writeComplexColor(rStyleSheet, XML_bgColor, maComplexColor, maColor);
+
+ rStyleSheet->endElement( XML_patternFill );
+ rStyleSheet->endElement( XML_fill );
+}
+
+XclExpXFId::XclExpXFId() :
+ mnXFId( XclExpXFBuffer::GetDefCellXFId() ),
+ mnXFIndex( EXC_XF_DEFAULTCELL )
+{
+}
+
+void XclExpXFId::ConvertXFIndex( const XclExpRoot& rRoot )
+{
+ mnXFIndex = rRoot.GetXFBuffer().GetXFIndex( mnXFId );
+}
+
+XclExpXF::XclExpXF(
+ const XclExpRoot& rRoot, const ScPatternAttr& rPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) :
+ XclXFBase( true ),
+ XclExpRoot( rRoot )
+{
+ mnParentXFId = GetXFBuffer().InsertStyle( rPattern.GetStyleSheet() );
+ Init( rPattern.GetItemSet(), nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak, false );
+}
+
+XclExpXF::XclExpXF( const XclExpRoot& rRoot, const SfxStyleSheetBase& rStyleSheet ) :
+ XclXFBase( false ),
+ XclExpRoot( rRoot ),
+ mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
+{
+ bool bDefStyle = (rStyleSheet.GetName() == ScResId( STR_STYLENAME_STANDARD ));
+ sal_Int16 nScript = bDefStyle ? GetDefApiScript() : css::i18n::ScriptType::WEAK;
+ Init( const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet(), nScript,
+ NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false, bDefStyle );
+}
+
+XclExpXF::XclExpXF( const XclExpRoot& rRoot, bool bCellXF ) :
+ XclXFBase( bCellXF ),
+ XclExpRoot( rRoot ),
+ mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
+{
+ InitDefault();
+}
+
+bool XclExpXF::Equals( const ScPatternAttr& rPattern,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
+{
+ return IsCellXF() && (mpItemSet == &rPattern.GetItemSet()) &&
+ (!bForceLineBreak || maAlignment.mbLineBreak) &&
+ ((nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) || (mnScNumFmt == nForceScNumFmt)) &&
+ ((nForceXclFont == EXC_FONT_NOTFOUND) || (mnXclFont == nForceXclFont));
+}
+
+bool XclExpXF::Equals( const SfxStyleSheetBase& rStyleSheet ) const
+{
+ return IsStyleXF() && (mpItemSet == &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet());
+}
+
+void XclExpXF::SetFinalColors()
+{
+ maBorder.SetFinalColors( GetPalette() );
+ maArea.SetFinalColors( GetPalette() );
+}
+
+bool XclExpXF::Equals( const XclExpXF& rCmpXF ) const
+{
+ return XclXFBase::Equals( rCmpXF ) &&
+ (maProtection == rCmpXF.maProtection) && (maAlignment == rCmpXF.maAlignment) &&
+ (maBorder == rCmpXF.maBorder) && (maArea == rCmpXF.maArea) &&
+ (mnXclFont == rCmpXF.mnXclFont) && (mnXclNumFmt == rCmpXF.mnXclNumFmt) &&
+ (mnParentXFId == rCmpXF.mnParentXFId);
+}
+
+void XclExpXF::InitDefault()
+{
+ SetRecHeader( EXC_ID5_XF, (GetBiff() == EXC_BIFF8) ? 20 : 16 );
+ mpItemSet = nullptr;
+ mnScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ mnXclFont = mnXclNumFmt = 0;
+ SetXmlIds(0, 0);
+}
+
+void XclExpXF::Init( const SfxItemSet& rItemSet, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak, bool bDefStyle )
+{
+ InitDefault();
+ mpItemSet = &rItemSet;
+
+ // cell protection
+ mbProtUsed = maProtection.FillFromItemSet( rItemSet, IsStyleXF() );
+
+ // font
+ if( nForceXclFont == EXC_FONT_NOTFOUND )
+ {
+ mnXclFont = GetFontBuffer().Insert( rItemSet, nScript, EXC_COLOR_CELLTEXT, bDefStyle );
+ mbFontUsed = XclExpFontHelper::CheckItems( GetRoot(), rItemSet, nScript, IsStyleXF() );
+ }
+ else
+ {
+ mnXclFont = nForceXclFont;
+ mbFontUsed = true;
+ }
+
+ // number format
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ mnXclNumFmt = nForceScNumFmt;
+ else
+ {
+ // Built-in formats of dedicated languages may be attributed using the
+ // system language (or even other?) format with a language attribute,
+ // obtain the "real" format key.
+ mnScNumFmt = rItemSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType nLang = rItemSet.Get( ATTR_LANGUAGE_FORMAT).GetLanguage();
+ if (mnScNumFmt >= SV_COUNTRY_LANGUAGE_OFFSET || nLang != LANGUAGE_SYSTEM)
+ mnScNumFmt = GetFormatter().GetFormatForLanguageIfBuiltIn( mnScNumFmt, nLang);
+ }
+ mnXclNumFmt = GetNumFmtBuffer().Insert( mnScNumFmt );
+ mbFmtUsed = ScfTools::CheckItem( rItemSet, ATTR_VALUE_FORMAT, IsStyleXF() );
+
+ // alignment
+ mbAlignUsed = maAlignment.FillFromItemSet(*this, rItemSet, bForceLineBreak, GetBiff(), IsStyleXF());
+
+ // cell border
+ mbBorderUsed = maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff(), IsStyleXF() );
+
+ // background area
+ mbAreaUsed = maArea.FillFromItemSet( rItemSet, GetPalette(), IsStyleXF() );
+
+ // set all b***Used flags to true in "Default"/"Normal" style
+ if( bDefStyle )
+ SetAllUsedFlags( true );
+}
+
+sal_uInt8 XclExpXF::GetUsedFlags() const
+{
+ sal_uInt8 nUsedFlags = 0;
+ /* In cell XFs a set bit means a used attribute, in style XFs a cleared bit.
+ "mbCellXF == mb***Used" evaluates to correct value in cell and style XFs. */
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_PROT, mbCellXF == mbProtUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_FONT, mbCellXF == mbFontUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_VALFMT, mbCellXF == mbFmtUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_ALIGN, mbCellXF == mbAlignUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_BORDER, mbCellXF == mbBorderUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_AREA, mbCellXF == mbAreaUsed );
+ return nUsedFlags;
+}
+
+void XclExpXF::WriteBody5( XclExpStream& rStrm )
+{
+ sal_uInt16 nTypeProt = 0, nAlign = 0;
+ sal_uInt32 nArea = 0, nBorder = 0;
+
+ ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
+ ::insert_value( nTypeProt, mnParent, 4, 12 );
+ ::insert_value( nAlign, GetUsedFlags(), 10, 6 );
+
+ maProtection.FillToXF3( nTypeProt );
+ maAlignment.FillToXF5( nAlign );
+ maBorder.FillToXF5( nBorder, nArea );
+ maArea.FillToXF5( nArea );
+
+ rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nArea << nBorder;
+}
+
+void XclExpXF::WriteBody8( XclExpStream& rStrm )
+{
+ sal_uInt16 nTypeProt = 0, nAlign = 0, nMiscAttrib = 0, nArea = 0;
+ sal_uInt32 nBorder1 = 0, nBorder2 = 0;
+
+ ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
+ ::insert_value( nTypeProt, mnParent, 4, 12 );
+ ::insert_value( nMiscAttrib, GetUsedFlags(), 10, 6 );
+
+ maProtection.FillToXF3( nTypeProt );
+ maAlignment.FillToXF8( nAlign, nMiscAttrib );
+ maBorder.FillToXF8( nBorder1, nBorder2 );
+ maArea.FillToXF8( nBorder2, nArea );
+
+ rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nMiscAttrib << nBorder1 << nBorder2 << nArea;
+}
+
+void XclExpXF::WriteBody( XclExpStream& rStrm )
+{
+ XclExpXFId aParentId( mnParentXFId );
+ aParentId.ConvertXFIndex( GetRoot() );
+ mnParent = aParentId.mnXFIndex;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: WriteBody5( rStrm ); break;
+ case EXC_BIFF8: WriteBody8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpXF::SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId )
+{
+ mnBorderId = nBorderId;
+ mnFillId = nFillId;
+}
+
+void XclExpXF::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ sal_Int32 nXfId = 0;
+ const XclExpXF* pStyleXF = nullptr;
+ if( IsCellXF() )
+ {
+ sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( mnParentXFId );
+ nXfId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFIndex );
+ pStyleXF = rStrm.GetRoot().GetXFBuffer().GetXFById( mnParentXFId );
+ }
+
+ rStyleSheet->startElement( XML_xf,
+ XML_numFmtId, OString::number(mnXclNumFmt),
+ XML_fontId, OString::number(mnXclFont),
+ XML_fillId, OString::number(mnFillId),
+ XML_borderId, OString::number(mnBorderId),
+ XML_xfId, sax_fastparser::UseIf(OString::number(nXfId), !IsStyleXF()),
+ // OOXTODO: XML_quotePrefix,
+ // OOXTODO: XML_pivotButton,
+ // OOXTODO: XML_applyNumberFormat, ;
+ XML_applyFont, ToPsz( mbFontUsed ),
+ // OOXTODO: XML_applyFill,
+ XML_applyBorder, ToPsz( mbBorderUsed ),
+ XML_applyAlignment, ToPsz( mbAlignUsed ),
+ XML_applyProtection, ToPsz( mbProtUsed ) );
+ if( mbAlignUsed )
+ maAlignment.SaveXml( rStrm );
+ else if ( pStyleXF )
+ pStyleXF->GetAlignmentData().SaveXml( rStrm );
+ if( mbProtUsed )
+ maProtection.SaveXml( rStrm );
+ else if ( pStyleXF )
+ pStyleXF->GetProtectionData().SaveXml( rStrm );
+
+ // OOXTODO: XML_extLst
+ rStyleSheet->endElement( XML_xf );
+}
+
+XclExpDefaultXF::XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF ) :
+ XclExpXF( rRoot, bCellXF )
+{
+}
+
+void XclExpDefaultXF::SetFont( sal_uInt16 nXclFont )
+{
+ mnXclFont = nXclFont;
+ mbFontUsed = true;
+}
+
+void XclExpDefaultXF::SetNumFmt( sal_uInt16 nXclNumFmt )
+{
+ mnXclNumFmt = nXclNumFmt;
+ mbFmtUsed = true;
+}
+
+XclExpStyle::XclExpStyle( sal_uInt32 nXFId, OUString aStyleName ) :
+ XclExpRecord( EXC_ID_STYLE, 4 ),
+ maName(std::move( aStyleName )),
+ maXFId( nXFId ),
+ mnStyleId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL )
+{
+ OSL_ENSURE( !maName.isEmpty(), "XclExpStyle::XclExpStyle - empty style name" );
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt8 nStyleId, nLevel; // do not use members for debug tests
+ OSL_ENSURE( !XclTools::GetBuiltInStyleId( nStyleId, nLevel, maName ),
+ "XclExpStyle::XclExpStyle - this is a built-in style" );
+#endif
+}
+
+XclExpStyle::XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel ) :
+ XclExpRecord( EXC_ID_STYLE, 4 ),
+ maXFId( nXFId ),
+ mnStyleId( nStyleId ),
+ mnLevel( nLevel )
+{
+}
+
+void XclExpStyle::WriteBody( XclExpStream& rStrm )
+{
+ maXFId.ConvertXFIndex( rStrm.GetRoot() );
+ ::set_flag( maXFId.mnXFIndex, EXC_STYLE_BUILTIN, IsBuiltIn() );
+ rStrm << maXFId.mnXFIndex;
+
+ if( IsBuiltIn() )
+ {
+ rStrm << mnStyleId << mnLevel;
+ }
+ else
+ {
+ XclExpString aNameEx;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ aNameEx.Assign( maName );
+ else
+ aNameEx.AssignByte( maName, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
+ rStrm << aNameEx;
+ }
+}
+
+static const char* lcl_StyleNameFromId( sal_Int32 nStyleId )
+{
+ switch( nStyleId )
+ {
+ case 0: return "Normal";
+ case 3: return "Comma";
+ case 4: return "Currency";
+ case 5: return "Percent";
+ case 6: return "Comma [0]";
+ case 7: return "Currency [0]";
+ }
+ return "*unknown*";
+}
+
+void XclExpStyle::SaveXml( XclExpXmlStream& rStrm )
+{
+ constexpr sal_Int32 CELL_STYLE_MAX_BUILTIN_ID = 54;
+ OString sName;
+ OString sBuiltinId;
+ const char* pBuiltinId = nullptr;
+ if( IsBuiltIn() )
+ {
+ sName = OString( lcl_StyleNameFromId( mnStyleId ) );
+ sBuiltinId = OString::number( std::min( static_cast<sal_Int32>( CELL_STYLE_MAX_BUILTIN_ID - 1 ), static_cast <sal_Int32>( mnStyleId ) ) );
+ pBuiltinId = sBuiltinId.getStr();
+ }
+ else
+ sName = maName.toUtf8();
+
+ // get the index in sortedlist associated with the mnXId
+ sal_Int32 nXFId = rStrm.GetRoot().GetXFBuffer().GetXFIndex( maXFId.mnXFId );
+ // get the style index associated with index into sortedlist
+ nXFId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFId );
+ rStrm.GetCurrentStream()->singleElement( XML_cellStyle,
+ XML_name, sName,
+ XML_xfId, OString::number(nXFId),
+// builtinId of 54 or above is invalid according to OpenXML SDK validator.
+ XML_builtinId, pBuiltinId
+ // OOXTODO: XML_iLevel,
+ // OOXTODO: XML_hidden,
+ // XML_customBuiltin, ToPsz( ! IsBuiltIn() )
+ );
+ // OOXTODO: XML_extLst
+}
+
+namespace {
+
+const sal_uInt32 EXC_XFLIST_INDEXBASE = 0xFFFE0000;
+/** Maximum count of XF records to store in the XF list (performance). */
+const sal_uInt32 EXC_XFLIST_HARDLIMIT = 256 * 1024;
+
+bool lclIsBuiltInStyle( const OUString& rStyleName )
+{
+ return
+ XclTools::IsBuiltInStyleName( rStyleName ) ||
+ XclTools::IsCondFormatStyleName( rStyleName );
+}
+
+} // namespace
+
+XclExpXFBuffer::XclExpBuiltInInfo::XclExpBuiltInInfo() :
+ mnStyleId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL ),
+ mbPredefined( true ),
+ mbHasStyleRec( false )
+{
+}
+
+namespace {
+
+/** Predicate for search algorithm. */
+struct XclExpBorderPred
+{
+ const XclExpCellBorder&
+ mrBorder;
+ explicit XclExpBorderPred( const XclExpCellBorder& rBorder ) : mrBorder( rBorder ) {}
+ bool operator()( const XclExpCellBorder& rBorder ) const;
+};
+
+}
+
+bool XclExpBorderPred::operator()( const XclExpCellBorder& rBorder ) const
+{
+ return
+ mrBorder.mnLeftColor == rBorder.mnLeftColor &&
+ mrBorder.mnRightColor == rBorder.mnRightColor &&
+ mrBorder.mnTopColor == rBorder.mnTopColor &&
+ mrBorder.mnBottomColor == rBorder.mnBottomColor &&
+ mrBorder.mnDiagColor == rBorder.mnDiagColor &&
+ mrBorder.mnLeftLine == rBorder.mnLeftLine &&
+ mrBorder.mnRightLine == rBorder.mnRightLine &&
+ mrBorder.mnTopLine == rBorder.mnTopLine &&
+ mrBorder.mnBottomLine == rBorder.mnBottomLine &&
+ mrBorder.mnDiagLine == rBorder.mnDiagLine &&
+ mrBorder.mbDiagTLtoBR == rBorder.mbDiagTLtoBR &&
+ mrBorder.mbDiagBLtoTR == rBorder.mbDiagBLtoTR &&
+ mrBorder.mnLeftColorId == rBorder.mnLeftColorId &&
+ mrBorder.mnRightColorId == rBorder.mnRightColorId &&
+ mrBorder.mnTopColorId == rBorder.mnTopColorId &&
+ mrBorder.mnBottomColorId == rBorder.mnBottomColorId &&
+ mrBorder.mnDiagColorId == rBorder.mnDiagColorId;
+}
+
+namespace {
+
+struct XclExpFillPred
+{
+ const XclExpCellArea&
+ mrFill;
+ explicit XclExpFillPred( const XclExpCellArea& rFill ) : mrFill( rFill ) {}
+ bool operator()( const XclExpCellArea& rFill ) const;
+};
+
+}
+
+bool XclExpFillPred::operator()( const XclExpCellArea& rFill ) const
+{
+ return
+ mrFill.mnForeColor == rFill.mnForeColor &&
+ mrFill.mnBackColor == rFill.mnBackColor &&
+ mrFill.mnPattern == rFill.mnPattern &&
+ mrFill.mnForeColorId == rFill.mnForeColorId &&
+ mrFill.mnBackColorId == rFill.mnBackColorId;
+}
+
+XclExpXFBuffer::XclExpXFBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpXFBuffer::Initialize()
+{
+ InsertDefaultRecords();
+ InsertUserStyles();
+}
+
+sal_uInt32 XclExpXFBuffer::Insert( const ScPatternAttr* pPattern, sal_Int16 nScript )
+{
+ return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertWithFont( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak )
+{
+ return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, nForceXclFont, bForceLineBreak );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForceScNumFmt, bool bForceLineBreak )
+{
+ return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, bForceLineBreak );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertStyle( const SfxStyleSheetBase* pStyleSheet )
+{
+ return pStyleSheet ? InsertStyleXF( *pStyleSheet ) : GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
+}
+
+sal_uInt32 XclExpXFBuffer::GetXFIdFromIndex( sal_uInt16 nXFIndex )
+{
+ return EXC_XFLIST_INDEXBASE | nXFIndex;
+}
+
+sal_uInt32 XclExpXFBuffer::GetDefCellXFId()
+{
+ return GetXFIdFromIndex( EXC_XF_DEFAULTCELL );
+}
+
+const XclExpXF* XclExpXFBuffer::GetXFById( sal_uInt32 nXFId ) const
+{
+ return maXFList.GetRecord( nXFId );
+}
+
+void XclExpXFBuffer::Finalize()
+{
+ for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos )
+ maXFList.GetRecord( nPos )->SetFinalColors();
+
+ sal_uInt32 nTotalCount = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ sal_uInt32 nId;
+ maXFIndexVec.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+ maStyleIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+ maCellIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+
+ XclExpBuiltInMap::const_iterator aBuiltInEnd = maBuiltInMap.end();
+ /* nMaxBuiltInXFId used to decide faster whether an XF record is
+ user-defined. If the current XF ID is greater than this value,
+ maBuiltInMap doesn't need to be searched. */
+ sal_uInt32 nMaxBuiltInXFId = maBuiltInMap.empty() ? 0 : maBuiltInMap.rbegin()->first;
+
+ // *** map all built-in XF records (cell and style) *** -------------------
+
+ // do not change XF order -> std::map<> iterates elements in ascending order
+ for( const auto& rEntry : maBuiltInMap )
+ AppendXFIndex( rEntry.first );
+
+ // *** insert all user-defined style XF records, without reduce *** -------
+
+ sal_uInt32 nStyleXFCount = 0; // counts up to EXC_XF_MAXSTYLECOUNT limit
+
+ for( nId = 0; nId < nTotalCount; ++nId )
+ {
+ XclExpXFRef xXF = maXFList.GetRecord( nId );
+ if( xXF->IsStyleXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
+ {
+ if( nStyleXFCount < EXC_XF_MAXSTYLECOUNT )
+ {
+ // maximum count of styles not reached
+ AppendXFIndex( nId );
+ ++nStyleXFCount;
+ }
+ else
+ {
+ /* Maximum count of styles reached - do not append more
+ pointers to XFs; use default style XF instead; do not break
+ the loop to initialize all maXFIndexVec elements. */
+ maXFIndexVec[ nId ] = EXC_XF_DEFAULTSTYLE;
+ }
+ }
+ }
+
+ // *** insert all cell XF records *** -------------------------------------
+
+ // start position to search for equal inserted XF records
+ size_t nSearchStart = maSortedXFList.GetSize();
+
+ // break the loop if XF limit reached - maXFIndexVec is already initialized with default index
+ XclExpXFRef xDefCellXF = maXFList.GetRecord( EXC_XF_DEFAULTCELL );
+ for( nId = 0; (nId < nTotalCount) && (maSortedXFList.GetSize() < EXC_XF_MAXCOUNT); ++nId )
+ {
+ XclExpXFRef xXF = maXFList.GetRecord( nId );
+ if( xXF->IsCellXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
+ {
+ // try to find an XF record equal to *xXF, which is already inserted
+ sal_uInt16 nFoundIndex = EXC_XF_NOTFOUND;
+
+ // first try if it is equal to the default cell XF
+ if( xDefCellXF->Equals( *xXF ) )
+ {
+ nFoundIndex = EXC_XF_DEFAULTCELL;
+ }
+ else for( size_t nSearchPos = nSearchStart, nSearchEnd = maSortedXFList.GetSize();
+ (nSearchPos < nSearchEnd) && (nFoundIndex == EXC_XF_NOTFOUND); ++nSearchPos )
+ {
+ if( maSortedXFList.GetRecord( nSearchPos )->Equals( *xXF ) )
+ nFoundIndex = static_cast< sal_uInt16 >( nSearchPos );
+ }
+
+ if( nFoundIndex != EXC_XF_NOTFOUND )
+ // equal XF already in the list, use its resulting XF index
+ maXFIndexVec[ nId ] = nFoundIndex;
+ else
+ AppendXFIndex( nId );
+ }
+ }
+
+ sal_uInt16 nXmlStyleIndex = 0;
+ sal_uInt16 nXmlCellIndex = 0;
+
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( xXF->IsStyleXF() )
+ maStyleIndexes[ i ] = nXmlStyleIndex++;
+ else
+ maCellIndexes[ i ] = nXmlCellIndex++;
+ }
+}
+
+sal_uInt16 XclExpXFBuffer::GetXFIndex( sal_uInt32 nXFId ) const
+{
+ sal_uInt16 nXFIndex = EXC_XF_DEFAULTSTYLE;
+ if( nXFId >= EXC_XFLIST_INDEXBASE )
+ nXFIndex = static_cast< sal_uInt16 >( nXFId & ~EXC_XFLIST_INDEXBASE );
+ else if( nXFId < maXFIndexVec.size() )
+ nXFIndex = maXFIndexVec[ nXFId ];
+ return nXFIndex;
+}
+
+sal_Int32 XclExpXFBuffer::GetXmlStyleIndex( sal_uInt32 nXFIndex ) const
+{
+ OSL_ENSURE( nXFIndex < maStyleIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
+ if( nXFIndex >= maStyleIndexes.size() )
+ return 0; // should be caught/debugged via above assert; return "valid" index.
+ return maStyleIndexes[ nXFIndex ];
+}
+
+sal_Int32 XclExpXFBuffer::GetXmlCellIndex( sal_uInt32 nXFIndex ) const
+{
+ OSL_ENSURE( nXFIndex < maCellIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
+ if( nXFIndex >= maCellIndexes.size() )
+ return 0; // should be caught/debugged via above assert; return "valid" index.
+ return maCellIndexes[ nXFIndex ];
+}
+
+void XclExpXFBuffer::Save( XclExpStream& rStrm )
+{
+ // save all XF records contained in the maSortedXFList vector (sorted by XF index)
+ maSortedXFList.Save( rStrm );
+ // save all STYLE records
+ maStyleList.Save( rStrm );
+}
+
+static void lcl_GetCellCounts( const XclExpRecordList< XclExpXF >& rXFList, sal_Int32& rCells, sal_Int32& rStyles )
+{
+ rCells = 0;
+ rStyles = 0;
+ size_t nXFCount = rXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpRecordList< XclExpXF >::RecordRefType xXF = rXFList.GetRecord( i );
+ if( xXF->IsCellXF() )
+ ++rCells;
+ else if( xXF->IsStyleXF() )
+ ++rStyles;
+ }
+}
+
+void XclExpXFBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ rStyleSheet->startElement(XML_fills, XML_count, OString::number(maFills.size()));
+ for( const auto& rFill : maFills )
+ {
+ rFill.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_fills );
+
+ rStyleSheet->startElement(XML_borders, XML_count, OString::number(maBorders.size()));
+ for( const auto& rBorder : maBorders )
+ {
+ rBorder.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_borders );
+
+ // save all XF records contained in the maSortedXFList vector (sorted by XF index)
+ sal_Int32 nCells, nStyles;
+ lcl_GetCellCounts( maSortedXFList, nCells, nStyles );
+
+ if( nStyles > 0 )
+ {
+ rStyleSheet->startElement(XML_cellStyleXfs, XML_count, OString::number(nStyles));
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( ! xXF->IsStyleXF() )
+ continue;
+ SaveXFXml( rStrm, *xXF );
+ }
+ rStyleSheet->endElement( XML_cellStyleXfs );
+ }
+
+ if( nCells > 0 )
+ {
+ rStyleSheet->startElement(XML_cellXfs, XML_count, OString::number(nCells));
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( ! xXF->IsCellXF() )
+ continue;
+ SaveXFXml( rStrm, *xXF );
+ }
+ rStyleSheet->endElement( XML_cellXfs );
+ }
+
+ // save all STYLE records
+ rStyleSheet->startElement(XML_cellStyles, XML_count, OString::number(maStyleList.GetSize()));
+ maStyleList.SaveXml( rStrm );
+ rStyleSheet->endElement( XML_cellStyles );
+}
+
+void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF )
+{
+ XclExpBorderList::iterator aBorderPos =
+ std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) );
+ OSL_ENSURE( aBorderPos != maBorders.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" );
+ XclExpFillList::iterator aFillPos =
+ std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) );
+ OSL_ENSURE( aFillPos != maFills.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" );
+
+ sal_Int32 nBorderId = 0, nFillId = 0;
+ if( aBorderPos != maBorders.end() )
+ nBorderId = std::distance( maBorders.begin(), aBorderPos );
+ if( aFillPos != maFills.end() )
+ nFillId = std::distance( maFills.begin(), aFillPos );
+
+ rXF.SetXmlIds( nBorderId, nFillId );
+ rXF.SaveXml( rStrm );
+}
+
+sal_uInt32 XclExpXFBuffer::FindXF( const ScPatternAttr& rPattern,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
+{
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND && nForceXclFont == EXC_FONT_NOTFOUND)
+ {
+ FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, 0 };
+ FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ if (it1 != maXFFindMap.end())
+ {
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ }
+ else if (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND || nForceXclFont == EXC_FONT_NOTFOUND)
+ {
+ FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), 0, 0 };
+ FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ if (it1 != maXFFindMap.end())
+ {
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ }
+ else
+ {
+ FindKey key { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, nForceXclFont };
+ auto it = maXFFindMap.find(key);
+ if (it == maXFFindMap.end())
+ return EXC_XFID_NOTFOUND;
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ return EXC_XFID_NOTFOUND;
+}
+
+sal_uInt32 XclExpXFBuffer::FindXF( const SfxStyleSheetBase& rStyleSheet ) const
+{
+ const SfxItemSet* pItemSet = &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet();
+ FindKey key1 { /*mbCellXF*/false, pItemSet, 0, 0 };
+ FindKey key2 { /*mbCellXF*/false, pItemSet, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) )
+ return nPos;
+ return EXC_XFID_NOTFOUND;
+}
+
+sal_uInt32 XclExpXFBuffer::FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const
+{
+ auto aIt = std::find_if(maBuiltInMap.begin(), maBuiltInMap.end(),
+ [&nStyleId, nLevel](const XclExpBuiltInMap::value_type& rEntry) {
+ return (rEntry.second.mnStyleId == nStyleId) && (rEntry.second.mnLevel == nLevel);
+ });
+ if (aIt != maBuiltInMap.end())
+ return aIt->first;
+ return EXC_XFID_NOTFOUND;
+}
+
+XclExpXFBuffer::FindKey XclExpXFBuffer::ToFindKey(XclExpXF const & rRec)
+{
+ return { rRec.IsCellXF(), rRec.GetItemSet(), rRec.GetScNumFmt(), rRec.GetXclFont() };
+}
+
+sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak )
+{
+ const ScPatternAttr* pDefPattern = GetDoc().GetDefPattern();
+ if( !pPattern )
+ pPattern = pDefPattern;
+
+ // special handling for default cell formatting
+ if( SfxPoolItem::areSame(pPattern, pDefPattern) && !bForceLineBreak &&
+ (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) &&
+ (nForceXclFont == EXC_FONT_NOTFOUND) )
+ {
+ // Is it the first try to insert the default cell format?
+ bool& rbPredefined = maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined;
+ if( rbPredefined )
+ {
+ // remove old entry in find-map
+ auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(EXC_XF_DEFAULTCELL))];
+ auto it = std::find(rPositions.begin(), rPositions.end(), EXC_XF_DEFAULTCELL);
+ rPositions.erase(it);
+ // replace default cell pattern
+ XclExpXFRef xNewXF = new XclExpXF( GetRoot(), *pPattern, nScript );
+ maXFList.ReplaceRecord( xNewXF, EXC_XF_DEFAULTCELL );
+ // and add new entry in find-map
+ maXFFindMap[ToFindKey(*xNewXF)].push_back(EXC_XF_DEFAULTCELL);
+ rbPredefined = false;
+ }
+ return GetDefCellXFId();
+ }
+
+ sal_uInt32 nXFId = FindXF( *pPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // not found - insert new cell XF
+ if( maXFList.GetSize() < EXC_XFLIST_HARDLIMIT )
+ {
+ auto pNewExp = new XclExpXF(
+ GetRoot(), *pPattern, nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak );
+ maXFList.AppendNewRecord( pNewExp );
+ // do not set nXFId before the AppendNewRecord() call - it may insert 2 XFs (style+cell)
+ nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() - 1 );
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ }
+ else
+ {
+ // list full - fall back to default cell XF
+ nXFId = GetDefCellXFId();
+ }
+ }
+ return nXFId;
+}
+
+sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet )
+{
+ // *** try, if it is a built-in style - create new XF or replace existing predefined XF ***
+
+ sal_uInt8 nStyleId, nLevel;
+ if( XclTools::GetBuiltInStyleId( nStyleId, nLevel, rStyleSheet.GetName() ) )
+ {
+ // try to find the built-in XF record (if already created in InsertDefaultRecords())
+ sal_uInt32 nXFId = FindBuiltInXF( nStyleId, nLevel );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // built-in style XF not yet created - do it now
+ XclExpXFRef xXF = new XclExpXF( GetRoot(), rStyleSheet );
+ nXFId = AppendBuiltInXFWithStyle( xXF, nStyleId, nLevel );
+ // this new XF record is not predefined
+ maBuiltInMap[ nXFId ].mbPredefined = false;
+ }
+ else
+ {
+ OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::InsertStyleXF - built-in XF not found" );
+ // XF record still predefined? -> Replace with real XF
+ bool& rbPredefined = maBuiltInMap[ nXFId ].mbPredefined;
+ if( rbPredefined )
+ {
+ // remove old entry in find-map
+ auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(nXFId))];
+ auto it = std::find(rPositions.begin(), rPositions.end(), nXFId);
+ rPositions.erase(it);
+ // replace predefined built-in style (ReplaceRecord() deletes old record)
+ XclExpXFRef pNewExp = new XclExpXF( GetRoot(), rStyleSheet );
+ maXFList.ReplaceRecord( pNewExp, nXFId );
+ // and add new entry in find-map
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ rbPredefined = false;
+ }
+ }
+
+ // STYLE already inserted? (may be not, i.e. for RowLevel/ColLevel or Hyperlink styles)
+ bool& rbHasStyleRec = maBuiltInMap[ nXFId ].mbHasStyleRec;
+ if( !rbHasStyleRec )
+ {
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
+ rbHasStyleRec = true;
+ }
+
+ return nXFId;
+ }
+
+ // *** try to find the XF record of a user-defined style ***
+
+ sal_uInt32 nXFId = FindXF( rStyleSheet );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // not found - insert new style XF and STYLE
+ nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ if( nXFId < EXC_XFLIST_HARDLIMIT )
+ {
+ auto pNewExp = new XclExpXF( GetRoot(), rStyleSheet );
+ maXFList.AppendNewRecord( pNewExp );
+ // create the STYLE record
+ if( !rStyleSheet.GetName().isEmpty() )
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, rStyleSheet.GetName() ) );
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ }
+ else
+ // list full - fall back to default style XF
+ nXFId = GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
+ }
+ return nXFId;
+}
+
+void XclExpXFBuffer::InsertUserStyles()
+{
+ SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
+ for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
+ if( pStyleSheet->IsUserDefined() && !lclIsBuiltInStyle( pStyleSheet->GetName() ) )
+ InsertStyleXF( *pStyleSheet );
+}
+
+sal_uInt32 XclExpXFBuffer::AppendBuiltInXF( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
+{
+ sal_uInt32 nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ maXFList.AppendRecord( xXF );
+ maXFFindMap[ToFindKey(*xXF)].push_back(nXFId);
+ XclExpBuiltInInfo& rInfo = maBuiltInMap[ nXFId ];
+ rInfo.mnStyleId = nStyleId;
+ rInfo.mnLevel = nLevel;
+ rInfo.mbPredefined = true;
+ return nXFId;
+}
+
+sal_uInt32 XclExpXFBuffer::AppendBuiltInXFWithStyle( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
+{
+ sal_uInt32 nXFId = AppendBuiltInXF( xXF, nStyleId, nLevel );
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
+ maBuiltInMap[ nXFId ].mbHasStyleRec = true; // mark existing STYLE record
+ return nXFId;
+}
+
+static XclExpCellArea lcl_GetPatternFill_None()
+{
+ XclExpCellArea aFill;
+ aFill.mnPattern = EXC_PATT_NONE;
+ return aFill;
+}
+
+static XclExpCellArea lcl_GetPatternFill_Gray125()
+{
+ XclExpCellArea aFill;
+ aFill.mnPattern = EXC_PATT_12_5_PERC;
+ aFill.mnForeColor = 0;
+ aFill.mnBackColor = 0;
+ return aFill;
+}
+
+void XclExpXFBuffer::InsertDefaultRecords()
+{
+ maFills.push_back( lcl_GetPatternFill_None() );
+ maFills.push_back( lcl_GetPatternFill_Gray125() );
+
+ // index 0: default style
+ if( SfxStyleSheetBase* pDefStyleSheet = GetStyleSheetPool().Find( ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) )
+ {
+ XclExpXFRef xDefStyle = new XclExpXF( GetRoot(), *pDefStyleSheet );
+ sal_uInt32 nXFId = AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
+ // mark this XF as not predefined, prevents overwriting
+ maBuiltInMap[ nXFId ].mbPredefined = false;
+ }
+ else
+ {
+ OSL_FAIL( "XclExpXFBuffer::InsertDefaultRecords - default style not found" );
+ XclExpXFRef xDefStyle = new XclExpDefaultXF( GetRoot(), false );
+ xDefStyle->SetAllUsedFlags( true );
+ AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
+ }
+
+ // index 1-14: RowLevel and ColLevel styles (without STYLE records)
+ XclExpDefaultXF aLevelStyle( GetRoot(), false );
+ // RowLevel_1, ColLevel_1
+ aLevelStyle.SetFont( 1 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, 0 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, 0 );
+ // RowLevel_2, ColLevel_2
+ aLevelStyle.SetFont( 2 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, 1 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, 1 );
+ // RowLevel_3, ColLevel_3 ... RowLevel_7, ColLevel_7
+ aLevelStyle.SetFont( 0 );
+ for( sal_uInt8 nLevel = 2; nLevel < EXC_STYLE_LEVELCOUNT; ++nLevel )
+ {
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, nLevel );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, nLevel );
+ }
+
+ // index 15: default hard cell format, placeholder to be able to add more built-in styles
+ maXFList.AppendNewRecord( new XclExpDefaultXF( GetRoot(), true ) );
+ maXFFindMap[ToFindKey(*maXFList.GetRecord(maXFList.GetSize()-1))].push_back(maXFList.GetSize()-1);
+ maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined = true;
+
+ // index 16-20: other built-in styles
+ XclExpDefaultXF aFormatStyle( GetRoot(), false );
+ aFormatStyle.SetFont( 1 );
+ aFormatStyle.SetNumFmt( 43 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_COMMA );
+ aFormatStyle.SetNumFmt( 41 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_COMMA_0 );
+ aFormatStyle.SetNumFmt( 44 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_CURRENCY );
+ aFormatStyle.SetNumFmt( 42 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_CURRENCY_0 );
+ aFormatStyle.SetNumFmt( 9 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_PERCENT );
+
+ // other built-in style XF records (i.e. Hyperlink styles) are created on demand
+
+ /* Insert the real default hard cell format -> 0 is document default pattern.
+ Do it here (and not already above) to really have all built-in styles. */
+ Insert( nullptr, GetDefApiScript() );
+}
+
+void XclExpXFBuffer::AppendXFIndex( sal_uInt32 nXFId )
+{
+ OSL_ENSURE( nXFId < maXFIndexVec.size(), "XclExpXFBuffer::AppendXFIndex - XF ID out of range" );
+ maXFIndexVec[ nXFId ] = static_cast< sal_uInt16 >( maSortedXFList.GetSize() );
+ XclExpXFRef xXF = maXFList.GetRecord( nXFId );
+ AddBorderAndFill( *xXF );
+ maSortedXFList.AppendRecord( xXF );
+ OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::AppendXFIndex - XF not found" );
+}
+
+void XclExpXFBuffer::AddBorderAndFill( const XclExpXF& rXF )
+{
+ if( std::none_of( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ) )
+ {
+ maBorders.push_back( rXF.GetBorderData() );
+ }
+
+ if( std::none_of( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ) )
+ {
+ maFills.push_back( rXF.GetAreaData() );
+ }
+}
+
+XclExpDxfs::XclExpDxfs( const XclExpRoot& rRoot )
+ : XclExpRoot( rRoot ),
+ mpKeywordTable( new NfKeywordTable )
+{
+ // Special number formatter for conversion.
+ SvNumberFormatterPtr xFormatter(new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ));
+ xFormatter->FillKeywordTableForExcel( *mpKeywordTable );
+
+ SCTAB nTables = rRoot.GetDoc().GetTableCount();
+ sal_Int32 nDxfId = 0;
+ for(SCTAB nTab = 0; nTab < nTables; ++nTab)
+ {
+ // Color filters
+ std::vector<ScDBData*> pDBData = rRoot.GetDoc().GetDBCollection()->GetAllDBsFromTab(nTab);
+ for (auto& pData : pDBData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+ for (auto nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); nCol++)
+ {
+ ScFilterEntries aFilterEntries;
+ rRoot.GetDoc().GetFilterEntriesArea(nCol, aRange.aStart.Row(),
+ aRange.aEnd.Row(), nTab, true, aFilterEntries);
+
+ // Excel has all filter values stored as foreground colors
+ // Does not matter it is text color or cell background color
+ for (auto& rColor : aFilterEntries.getBackgroundColors())
+ {
+ if (!maColorToDxfId.emplace(rColor, nDxfId).second)
+ continue;
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(rColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(rRoot, std::move(pExpCellArea)));
+ nDxfId++;
+ }
+ for (auto& rColor : aFilterEntries.getTextColors())
+ {
+ if (!maColorToDxfId.emplace(rColor, nDxfId).second)
+ continue;
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(rColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(rRoot, std::move(pExpCellArea)));
+ nDxfId++;
+ }
+ }
+ }
+
+ // Conditional formatting
+ ScConditionalFormatList* pList = rRoot.GetDoc().GetCondFormList(nTab);
+ if (pList)
+ {
+ for (const auto& rxItem : *pList)
+ {
+ size_t nEntryCount = rxItem->size();
+ for (size_t nFormatEntry = 0; nFormatEntry < nEntryCount; ++nFormatEntry)
+ {
+ const ScFormatEntry* pFormatEntry = rxItem->GetEntry(nFormatEntry);
+ if (!pFormatEntry
+ || (pFormatEntry->GetType() != ScFormatEntry::Type::Condition
+ && pFormatEntry->GetType() != ScFormatEntry::Type::Date
+ && pFormatEntry->GetType() != ScFormatEntry::Type::ExtCondition))
+ continue;
+
+ OUString aStyleName;
+ if (pFormatEntry->GetType() == ScFormatEntry::Type::Condition
+ || pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+ aStyleName= pEntry->GetStyle();
+ }
+ else
+ {
+ const ScCondDateFormatEntry* pEntry = static_cast<const ScCondDateFormatEntry*>(pFormatEntry);
+ aStyleName = pEntry->GetStyleName();
+ }
+
+ if (maStyleNameToDxfId.emplace(aStyleName, nDxfId).second)
+ {
+ SfxStyleSheetBase* pStyle = rRoot.GetDoc().GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para);
+ if(!pStyle)
+ continue;
+
+ SfxItemSet& rSet = pStyle->GetItemSet();
+
+ std::unique_ptr<XclExpCellBorder> pBorder(new XclExpCellBorder);
+ if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
+ {
+ pBorder.reset();
+ }
+
+ std::unique_ptr<XclExpCellAlign> pAlign(new XclExpCellAlign);
+ if (!pAlign->FillFromItemSet(rRoot, rSet, false, GetBiff()))
+ {
+ pAlign.reset();
+ }
+
+ std::unique_ptr<XclExpCellProt> pCellProt(new XclExpCellProt);
+ if (!pCellProt->FillFromItemSet( rSet ))
+ {
+ pCellProt.reset();
+ }
+
+ std::unique_ptr<XclExpColor> pColor(new XclExpColor);
+ if(!pColor->FillFromItemSet( rSet ))
+ {
+ pColor.reset();
+ }
+
+ std::unique_ptr<XclExpDxfFont> pFont(new XclExpDxfFont(rRoot, rSet));
+
+ std::unique_ptr<XclExpNumFmt> pNumFormat;
+ if( const SfxUInt32Item *pPoolItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nScNumFmt = pPoolItem->GetValue();
+ sal_Int32 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
+ pNumFormat.reset(new XclExpNumFmt( nScNumFmt, nXclNumFmt, GetNumberFormatCode( *this, nScNumFmt, xFormatter.get(), mpKeywordTable.get() )));
+ }
+
+ maDxf.push_back(std::make_unique<XclExpDxf>( rRoot, std::move(pAlign), std::move(pBorder),
+ std::move(pFont), std::move(pNumFormat), std::move(pCellProt), std::move(pColor) ));
+ ++nDxfId;
+ }
+
+ }
+ }
+ }
+ }
+}
+
+sal_Int32 XclExpDxfs::GetDxfId( const OUString& rStyleName ) const
+{
+ std::map<OUString, sal_Int32>::const_iterator itr = maStyleNameToDxfId.find(rStyleName);
+ if(itr!= maStyleNameToDxfId.end())
+ return itr->second;
+ return -1;
+}
+
+sal_Int32 XclExpDxfs::GetDxfByColor(Color aColor) const
+{
+ std::map<Color, sal_Int32>::const_iterator itr = maColorToDxfId.find(aColor);
+ if (itr != maColorToDxfId.end())
+ return itr->second;
+ return -1;
+}
+
+void XclExpDxfs::addColor(Color aColor)
+{
+ maColorToDxfId.emplace(aColor, maDxf.size());
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(aColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(GetRoot(), std::move(pExpCellArea)));
+}
+
+void XclExpDxfs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if(maDxf.empty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_dxfs, XML_count, OString::number(maDxf.size()));
+
+ for ( auto& rxDxf : maDxf )
+ {
+ rxDxf->SaveXml( rStrm );
+ }
+
+ rStyleSheet->endElement( XML_dxfs );
+}
+
+void XclExpDxfs::Finalize()
+{
+ for (auto& rxDxf : maDxf)
+ {
+ rxDxf->SetFinalColors();
+ }
+}
+
+XclExpDxf::XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellAlign> pAlign, std::unique_ptr<XclExpCellBorder> pBorder,
+ std::unique_ptr<XclExpDxfFont> pFont, std::unique_ptr<XclExpNumFmt> pNumberFmt, std::unique_ptr<XclExpCellProt> pProt,
+ std::unique_ptr<XclExpColor> pColor)
+ : XclExpRoot( rRoot ),
+ mpAlign(std::move(pAlign)),
+ mpBorder(std::move(pBorder)),
+ mpFont(std::move(pFont)),
+ mpNumberFmt(std::move(pNumberFmt)),
+ mpProt(std::move(pProt)),
+ mpColor(std::move(pColor))
+{
+}
+
+XclExpDxf::XclExpDxf(const XclExpRoot& rRoot, std::unique_ptr<XclExpCellArea> pCellArea)
+ : XclExpRoot(rRoot)
+ , mpCellArea(std::move(pCellArea))
+{
+}
+
+XclExpDxf::~XclExpDxf()
+{
+}
+
+void XclExpDxf::SetFinalColors()
+{
+ if (mpBorder)
+ {
+ mpBorder->SetFinalColors(GetPalette());
+ }
+}
+
+void XclExpDxf::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_dxf);
+
+ if (mpFont)
+ mpFont->SaveXml(rStrm);
+ if (mpNumberFmt)
+ mpNumberFmt->SaveXml(rStrm);
+ if (mpColor)
+ mpColor->SaveXml(rStrm);
+ if (mpAlign)
+ mpAlign->SaveXml(rStrm);
+ if (mpBorder)
+ mpBorder->SaveXml(rStrm);
+ if (mpProt)
+ mpProt->SaveXml(rStrm);
+ if (mpCellArea)
+ mpCellArea->SaveXml(rStrm);
+ rStyleSheet->endElement( XML_dxf );
+}
+
+void XclExpDxf::SaveXmlExt( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElementNS( XML_x14, XML_dxf );
+
+ if (mpFont)
+ mpFont->SaveXml(rStrm);
+ if (mpNumberFmt)
+ mpNumberFmt->SaveXml(rStrm);
+ if (mpColor)
+ mpColor->SaveXml(rStrm);
+ if (mpAlign)
+ mpAlign->SaveXml(rStrm);
+ if (mpBorder)
+ mpBorder->SaveXml(rStrm);
+ if (mpProt)
+ mpProt->SaveXml(rStrm);
+ rStyleSheet->endElementNS( XML_x14, XML_dxf );
+}
+
+
+XclExpXmlStyleSheet::XclExpXmlStyleSheet( const XclExpRoot& rRoot )
+ : XclExpRoot( rRoot )
+{
+}
+
+void XclExpXmlStyleSheet::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr aStyleSheet = rStrm.CreateOutputStream(
+ "xl/styles.xml",
+ u"styles.xml",
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
+ oox::getRelationship(Relationship::STYLES));
+ rStrm.PushStream( aStyleSheet );
+
+ aStyleSheet->startElement(XML_styleSheet, XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)));
+
+ CreateRecord( EXC_ID_FORMATLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_FONTLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_XFLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_DXFS )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_PALETTE )->SaveXml( rStrm );
+
+ aStyleSheet->endElement( XML_styleSheet );
+
+ rStrm.PopStream();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx
new file mode 100644
index 0000000000..f7b9a81be9
--- /dev/null
+++ b/sc/source/filter/excel/xetable.cxx
@@ -0,0 +1,2841 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xetable.hxx>
+
+#include <map>
+#include <numeric>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <scitems.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <olinetab.hxx>
+#include <formulacell.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <xehelper.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xeextlst.hxx>
+#include <xeformula.hxx>
+#include <xlcontent.hxx>
+#include <xltools.hxx>
+#include <tokenarray.hxx>
+#include <formula/errorcodes.hxx>
+#include <comphelper/threadpool.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+
+using namespace ::oox;
+
+namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+// Helper records for cell records
+
+XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult ) :
+ XclExpRecord( EXC_ID3_STRING ),
+ mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
+{
+ OSL_ENSURE( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
+ "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
+ SetRecSize( mxResult->GetSize() );
+}
+
+void XclExpStringRec::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << *mxResult;
+}
+
+// Additional records for special formula ranges ==============================
+
+XclExpRangeFmlaBase::XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
+ XclExpRecord( nRecId, nRecSize ),
+ maXclRange( ScAddress::UNINITIALIZED ),
+ maBaseXclPos( ScAddress::UNINITIALIZED )
+{
+ maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
+ maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
+}
+
+XclExpRangeFmlaBase::XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
+ XclExpRecord( nRecId, nRecSize ),
+ maXclRange( ScAddress::UNINITIALIZED ),
+ maBaseXclPos( ScAddress::UNINITIALIZED )
+{
+ maXclRange.Set(
+ static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
+ static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
+ static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
+ static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
+ maBaseXclPos = maXclRange.maFirst;
+}
+
+bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
+{
+ return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
+}
+
+void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
+ sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
+ maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
+ maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
+ maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
+ maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
+}
+
+void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
+{
+ maXclRange.Write( rStrm, false );
+}
+
+// Array formulas =============================================================
+
+XclExpArray::XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange ) :
+ XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
+ mxTokArr( xTokArr )
+{
+}
+
+XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
+}
+
+bool XclExpArray::IsVolatile() const
+{
+ return mxTokArr->IsVolatile();
+}
+
+void XclExpArray::WriteBody( XclExpStream& rStrm )
+{
+ WriteRangeAddress( rStrm );
+ sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
+ rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
+}
+
+XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
+{
+ const ScAddress& rScPos = rScRange.aStart;
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
+
+ OSL_ENSURE( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
+ XclExpArrayRef& rxRec = maRecMap[ rScPos ];
+ rxRec = new XclExpArray( xTokArr, rScRange );
+ return rxRec;
+}
+
+XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const
+{
+ XclExpArrayRef xRec;
+ // try to extract a matrix reference token
+ if (rScTokArr.GetLen() != 1)
+ // Must consist of a single reference token.
+ return xRec;
+
+ const formula::FormulaToken* pToken = rScTokArr.GetArray()[0];
+ if (!pToken || pToken->GetOpCode() != ocMatRef)
+ // not a matrix reference token.
+ return xRec;
+
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ ScAddress aAbsPos = rRef.toAbs(GetRoot().GetDoc(), rBasePos);
+ XclExpArrayMap::const_iterator it = maRecMap.find(aAbsPos);
+
+ if (it != maRecMap.end())
+ xRec = it->second;
+ return xRec;
+}
+
+// Shared formulas ============================================================
+
+XclExpShrfmla::XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos ) :
+ XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
+ mxTokArr( xTokArr ),
+ mnUsedCount( 1 )
+{
+}
+
+void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
+{
+ Extend( rScPos );
+ ++mnUsedCount;
+}
+
+XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
+}
+
+bool XclExpShrfmla::IsVolatile() const
+{
+ return mxTokArr->IsVolatile();
+}
+
+void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
+{
+ WriteRangeAddress( rStrm );
+ rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
+}
+
+XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
+{
+ using namespace formula;
+
+ FormulaToken** pTokens = rArray.GetArray();
+ sal_uInt16 nLen = rArray.GetLen();
+ for (sal_uInt16 i = 0; i < nLen; ++i)
+ {
+ const FormulaToken* p = pTokens[i];
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRefData = *p->GetSingleRef();
+ if (!GetFormulaCompiler().IsRef2D(rRefData))
+ // Excel's shared formula cannot include 3D reference.
+ return false;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRefData = *p->GetDoubleRef();
+ if (!GetFormulaCompiler().IsRef2D(rRefData))
+ // Excel's shared formula cannot include 3D reference.
+ return false;
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName:
+ // External references aren't allowed.
+ return false;
+ default:
+ ;
+ }
+ }
+ return true;
+}
+
+XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
+ const ScFormulaCell& rScCell, const ScAddress& rScPos )
+{
+ XclExpShrfmlaRef xRec;
+ const ScTokenArray* pShrdScTokArr = rScCell.GetSharedCode();
+ if (!pShrdScTokArr)
+ // This formula cell is not shared formula cell.
+ return xRec;
+
+ // Check to see if this shared formula contains any tokens that Excel's shared formula cannot handle.
+ if (maBadTokens.count(pShrdScTokArr) > 0)
+ // Already on the black list. Skip it.
+ return xRec;
+
+ if (!IsValidTokenArray(*pShrdScTokArr))
+ {
+ // We can't export this as shared formula.
+ maBadTokens.insert(pShrdScTokArr);
+ return xRec;
+ }
+
+ TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
+ if( aIt == maRecMap.end() )
+ {
+ // create a new record
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
+ xRec = new XclExpShrfmla( xTokArr, rScPos );
+ maRecMap[ pShrdScTokArr ] = xRec;
+ }
+ else
+ {
+ // extend existing record
+ OSL_ENSURE( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
+ xRec = aIt->second;
+ xRec->ExtendRange( rScPos );
+ }
+
+ return xRec;
+}
+
+// Multiple operations ========================================================
+
+XclExpTableop::XclExpTableop( const ScAddress& rScPos,
+ const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
+ XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
+ mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
+ mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
+ mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
+ mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
+ mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
+ mnScMode( nScMode ),
+ mbValid( false )
+{
+}
+
+bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
+ sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
+
+ bool bOk = IsAppendable( nXclCol, nXclRow );
+ if( bOk )
+ {
+ SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
+ SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
+ SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
+ SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
+ SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
+ SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
+
+ bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
+ (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
+ (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
+ (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColRelScPos.Tab());
+
+ if( bOk ) switch( mnScMode )
+ {
+ case 0:
+ bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
+ (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
+ (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row());
+ break;
+ case 1:
+ bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
+ (nFirstScRow == rRefs.maColRelScPos.Row() + 1);
+ break;
+ case 2:
+ bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
+ (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
+ (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
+ (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
+ (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
+ (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
+ (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
+ (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
+ break;
+ default:
+ bOk = false;
+ }
+
+ if( bOk )
+ {
+ // extend the cell range
+ OSL_ENSURE( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
+ Extend( rScPos );
+ mnLastAppXclCol = nXclCol;
+ }
+ }
+
+ return bOk;
+}
+
+void XclExpTableop::Finalize()
+{
+ // is the range complete? (last appended cell is in last column)
+ mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
+ // if last row is incomplete, try to shorten the used range
+ if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
+ {
+ --maXclRange.maLast.mnRow;
+ mbValid = true;
+ }
+
+ // check if referred cells are outside of own range
+ if( !mbValid )
+ return;
+
+ switch( mnScMode )
+ {
+ case 0:
+ mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
+ break;
+ case 1:
+ mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
+ break;
+ case 2:
+ mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
+ ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
+ break;
+ }
+}
+
+XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
+ return mbValid ?
+ rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
+ rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
+}
+
+bool XclExpTableop::IsVolatile() const
+{
+ return true;
+}
+
+void XclExpTableop::Save( XclExpStream& rStrm )
+{
+ if( mbValid )
+ XclExpRangeFmlaBase::Save( rStrm );
+}
+
+bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
+{
+ return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
+ ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
+ ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
+}
+
+void XclExpTableop::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
+ switch( mnScMode )
+ {
+ case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
+ case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
+ }
+
+ WriteRangeAddress( rStrm );
+ rStrm << nFlags;
+ if( mnScMode == 2 )
+ rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
+ else
+ rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
+}
+
+XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
+ const ScTokenArray& rScTokArr, const ScAddress& rScPos )
+{
+ XclExpTableopRef xRec;
+
+ // try to extract cell references of a multiple operations formula
+ XclMultipleOpRefs aRefs;
+ if (XclTokenArrayHelper::GetMultipleOpRefs(GetDoc(), aRefs, rScTokArr, rScPos))
+ {
+ // try to find an existing TABLEOP record for this cell position
+ for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
+ {
+ XclExpTableop* xTempRec = maTableopList.GetRecord( nPos );
+ if( xTempRec->TryExtend( rScPos, aRefs ) )
+ xRec = xTempRec;
+ }
+
+ // no record found, or found record not extensible
+ if( !xRec )
+ xRec = TryCreate( rScPos, aRefs );
+ }
+
+ return xRec;
+}
+
+void XclExpTableopBuffer::Finalize()
+{
+ for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
+ maTableopList.GetRecord( nPos )->Finalize();
+}
+
+XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
+{
+ sal_uInt8 nScMode = 0;
+ bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColRelScPos.Tab());
+
+ if( bOk )
+ {
+ if( rRefs.mbDblRefMode )
+ {
+ nScMode = 2;
+ bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
+ (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
+ (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
+ (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
+ }
+ else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) )
+ {
+ nScMode = 0;
+ }
+ else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
+ {
+ nScMode = 1;
+ }
+ else
+ {
+ bOk = false;
+ }
+ }
+
+ XclExpTableopRef xRec;
+ if( bOk )
+ {
+ xRec = new XclExpTableop( rScPos, rRefs, nScMode );
+ maTableopList.AppendRecord( xRec );
+ }
+
+ return xRec;
+}
+
+// Cell records
+
+XclExpCellBase::XclExpCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
+ XclExpRecord( nRecId, nContSize + 4 ),
+ maXclPos( rXclPos )
+{
+}
+
+bool XclExpCellBase::IsMultiLineText() const
+{
+ return false;
+}
+
+bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
+{
+ return false;
+}
+
+void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
+{
+ // default: do nothing
+}
+
+void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/, size_t /*nStartAllNotFound*/ )
+{
+ // default: do nothing
+}
+
+// Single cell records ========================================================
+
+XclExpSingleCellBase::XclExpSingleCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
+ XclExpCellBase( nRecId, 2, rXclPos ),
+ maXFId( nXFId ),
+ mnContSize( nContSize )
+{
+}
+
+XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
+ XclExpCellBase( nRecId, 2, rXclPos ),
+ maXFId( nForcedXFId ),
+ mnContSize( nContSize )
+{
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
+}
+
+sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
+{
+ return GetXclCol();
+}
+
+sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
+{
+ return GetXFId();
+}
+
+bool XclExpSingleCellBase::IsEmpty() const
+{
+ return false;
+}
+
+void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
+{
+ maXFId.ConvertXFIndex( rRoot );
+}
+
+void XclExpSingleCellBase::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+ AddRecSize( mnContSize );
+ XclExpCellBase::Save( rStrm );
+}
+
+void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16> (GetXclRow()) << GetXclCol() << maXFId.mnXFIndex;
+ WriteContents( rStrm );
+}
+
+XclExpNumberCell::XclExpNumberCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
+ // #i41210# always use latin script for number cells - may look wrong for special number formats...
+ XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
+ mfValue( fValue )
+{
+}
+
+static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
+{
+ return OString::number( rStrm.GetRoot().GetXFBuffer()
+ .GetXmlCellIndex( nXFIndex ) );
+}
+
+static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
+{
+ sal_uInt32 nXFId = rCell.GetFirstXFId();
+ sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
+ return lcl_GetStyleId( rStrm, nXFIndex );
+}
+
+void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "n"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement(XML_v);
+ rWorksheet->write( mfValue );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
+{
+ rStrm << mfValue;
+}
+
+XclExpBooleanCell::XclExpBooleanCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
+ // #i41210# always use latin script for boolean cells
+ XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
+ mbValue( bValue )
+{
+}
+
+void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "b"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( mbValue ? "1" : "0" );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
+}
+
+XclExpLabelCell::XclExpLabelCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const OUString& rStr ) :
+ XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
+{
+ sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
+ XclExpStringRef xText = XclExpStringHelper::CreateCellString(
+ rRoot, rStr, pPattern, XclStrFlags::NONE, nMaxLen);
+ Init( rRoot, pPattern, xText );
+}
+
+XclExpLabelCell::XclExpLabelCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const EditTextObject* pEditText, XclExpHyperlinkHelper& rLinkHelper ) :
+ XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
+{
+ sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
+
+ XclExpStringRef xText;
+ if (pEditText)
+ xText = XclExpStringHelper::CreateCellString(
+ rRoot, *pEditText, pPattern, rLinkHelper, XclStrFlags::NONE, nMaxLen);
+ else
+ xText = XclExpStringHelper::CreateCellString(
+ rRoot, OUString(), pPattern, XclStrFlags::NONE, nMaxLen);
+
+ Init( rRoot, pPattern, xText );
+}
+
+bool XclExpLabelCell::IsMultiLineText() const
+{
+ return mbLineBreak || mxText->IsWrapped();
+}
+
+void XclExpLabelCell::Init( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, XclExpStringRef const & xText )
+{
+ OSL_ENSURE( xText && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
+ mxText = xText;
+ mnSstIndex = 0;
+
+ const XclFormatRunVec& rFormats = mxText->GetFormats();
+ // remove formatting of the leading run if the entire string
+ // is equally formatted
+ sal_uInt16 nXclFont = EXC_FONT_NOTFOUND;
+ if( rFormats.size() == 1 )
+ nXclFont = mxText->RemoveLeadingFont();
+ else
+ nXclFont = mxText->GetLeadingFont();
+
+ // create cell format
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ {
+ OSL_ENSURE( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
+ bool bForceLineBreak = pPattern->GetItemSet().Get(ATTR_LINEBREAK ).GetValue();
+ SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
+ }
+
+ // get auto-wrap attribute from cell format
+ const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
+ mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
+
+ // initialize the record contents
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF5:
+ // BIFF5-BIFF7: create a LABEL or RSTRING record
+ OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
+ SetContSize( mxText->GetSize() );
+ // formatted string is exported in an RSTRING record
+ if( mxText->IsRich() )
+ {
+ OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
+ mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
+ SetRecId( EXC_ID_RSTRING );
+ SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
+ }
+ break;
+ case EXC_BIFF8:
+ // BIFF8+: create a LABELSST record
+ mnSstIndex = rRoot.GetSst().Insert( xText );
+ SetRecId( EXC_ID_LABELSST );
+ SetContSize( 4 );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "s"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( static_cast<sal_Int32>(mnSstIndex) );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
+{
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF5:
+ rStrm << *mxText;
+ if( mxText->IsRich() )
+ {
+ rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
+ mxText->WriteFormats( rStrm );
+ }
+ break;
+ case EXC_BIFF8:
+ rStrm << mnSstIndex;
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+XclExpFormulaCell::XclExpFormulaCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const ScFormulaCell& rScFmlaCell,
+ XclExpArrayBuffer& rArrayBfr,
+ XclExpShrfmlaBuffer& rShrfmlaBfr,
+ XclExpTableopBuffer& rTableopBfr ) :
+ XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
+ mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
+{
+ // *** Find result number format overwriting cell number format *** -------
+
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ {
+ SvNumberFormatter& rFormatter = rRoot.GetFormatter();
+ XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
+
+ // current cell number format
+ sal_uInt32 nScNumFmt = pPattern ?
+ pPattern->GetItemSet().Get( ATTR_VALUE_FORMAT ).GetValue() :
+ rNumFmtBfr.GetStandardFormat();
+
+ // alternative number format passed to XF buffer
+ sal_uInt32 nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ /* Xcl doesn't know Boolean number formats, we write
+ "TRUE";"FALSE" (language dependent). Don't do it for automatic
+ formula formats, because Excel gets them right. */
+ /* #i8640# Don't set text format, if we have string results. */
+ SvNumFormatType nFormatType = mrScFmlaCell.GetFormatType();
+ if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
+ (nFormatType != SvNumFormatType::LOGICAL) &&
+ (nFormatType != SvNumFormatType::TEXT) )
+ nAltScNumFmt = nScNumFmt;
+ /* If cell number format is Boolean and automatic formula
+ format is Boolean don't write that ugly special format. */
+ else if( (nFormatType == SvNumFormatType::LOGICAL) &&
+ (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) )
+ nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
+
+ // #i41420# find script type according to result type (always latin for numeric results)
+ sal_Int16 nScript = ApiScriptType::LATIN;
+ bool bForceLineBreak = false;
+ if( nFormatType == SvNumFormatType::TEXT )
+ {
+ OUString aResult = mrScFmlaCell.GetString().getString();
+ bForceLineBreak = mrScFmlaCell.IsMultilineResult();
+ nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
+ }
+ SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
+ }
+
+ // *** Convert the formula token array *** --------------------------------
+
+ ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
+ const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
+
+ // first try to create multiple operations
+ mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
+
+ // no multiple operation found - try to create matrix formula
+ if( !mxAddRec )
+ switch( mrScFmlaCell.GetMatrixFlag() )
+ {
+ case ScMatrixMode::Formula:
+ {
+ // origin of the matrix - find the used matrix range
+ SCCOL nMatWidth;
+ SCROW nMatHeight;
+ mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
+ OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
+ ScRange aMatScRange( aScPos );
+ ScAddress& rMatEnd = aMatScRange.aEnd;
+ rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
+ rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
+ // reduce to valid range (range keeps valid, because start position IS valid)
+ rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
+ // create the ARRAY record
+ mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
+ }
+ break;
+ case ScMatrixMode::Reference:
+ {
+ // other formula cell covered by a matrix - find the ARRAY record
+ mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
+ // should always be found, if Calc document is not broken
+ OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
+ }
+ break;
+ default:;
+ }
+
+ // no matrix found - try to create shared formula
+ if( !mxAddRec )
+ mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
+
+ // no shared formula found - create a simple cell formula
+ if( !mxAddRec )
+ mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
+}
+
+void XclExpFormulaCell::Save( XclExpStream& rStrm )
+{
+ // create token array for FORMULA cells with additional record
+ if( mxAddRec )
+ mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
+
+ // FORMULA record itself
+ OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
+ if( !mxTokArr )
+ mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
+ SetContSize( 16 + mxTokArr->GetSize() );
+ XclExpSingleCellBase::Save( rStrm );
+
+ // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
+ if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
+ mxAddRec->Save( rStrm );
+
+ // STRING record for string result
+ if( mxStringRec )
+ mxStringRec->Save( rStrm );
+}
+
+void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ const char* sType = nullptr;
+ OUString sValue;
+ XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, sType
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+
+ bool bWriteFormula = true;
+ bool bTagStarted = false;
+ ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
+ static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
+
+ switch (mrScFmlaCell.GetMatrixFlag())
+ {
+ case ScMatrixMode::NONE:
+ break;
+ case ScMatrixMode::Reference:
+ bWriteFormula = false;
+ break;
+ case ScMatrixMode::Formula:
+ {
+ // origin of the matrix - find the used matrix range
+ SCCOL nMatWidth;
+ SCROW nMatHeight;
+ mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
+ OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
+ ScRange aMatScRange( aScPos );
+ ScAddress& rMatEnd = aMatScRange.aEnd;
+ rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
+ rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
+ // reduce to valid range (range keeps valid, because start position IS valid
+ rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
+
+ OStringBuffer sFmlaCellRange;
+ if (rStrm.GetRoot().GetDoc().ValidRange(aMatScRange))
+ {
+ // calculate the cell range.
+ sFmlaCellRange.append( XclXmlUtils::ToOString(
+ rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart )
+ + OString::Concat(":"));
+ sFmlaCellRange.append( XclXmlUtils::ToOString(
+ rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ));
+ }
+
+ if ( aMatScRange.aStart.Col() == GetXclPos().mnCol &&
+ aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
+ {
+ rWorksheet->startElement( XML_f,
+ XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
+ (mxAddRec && mxAddRec->IsVolatile())),
+ XML_t, mxAddRec ? "array" : nullptr,
+ XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : nullptr
+ // OOXTODO: XML_dt2D, bool
+ // OOXTODO: XML_dtr, bool
+ // OOXTODO: XML_del1, bool
+ // OOXTODO: XML_del2, bool
+ // OOXTODO: XML_r1, ST_CellRef
+ // OOXTODO: XML_r2, ST_CellRef
+ // OOXTODO: XML_ca, bool
+ // OOXTODO: XML_si, uint
+ // OOXTODO: XML_bx bool
+ );
+ bTagStarted = true;
+ }
+ }
+ break;
+ }
+
+ if (bWriteFormula)
+ {
+ if (!bTagStarted)
+ {
+ rWorksheet->startElement( XML_f,
+ XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
+ (mxAddRec && mxAddRec->IsVolatile()) ) );
+ }
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
+ rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode(),
+ mrScFmlaCell.GetErrCode()));
+ rWorksheet->endElement( XML_f );
+ }
+
+ if( strcmp( sType, "inlineStr" ) == 0 )
+ {
+ rWorksheet->startElement(XML_is);
+ rWorksheet->startElement(XML_t);
+ rWorksheet->writeEscaped( sValue );
+ rWorksheet->endElement( XML_t );
+ rWorksheet->endElement( XML_is );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_v);
+ rWorksheet->writeEscaped( sValue );
+ rWorksheet->endElement( XML_v );
+ }
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
+{
+ FormulaError nScErrCode = mrScFmlaCell.GetErrCode();
+ if( nScErrCode != FormulaError::NONE )
+ {
+ rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
+ << XclTools::GetXclErrorCode( nScErrCode )
+ << sal_uInt8( 0 ) << sal_uInt16( 0 )
+ << sal_uInt16( 0xFFFF );
+ }
+ else
+ {
+ // result of the formula
+ switch( mrScFmlaCell.GetFormatType() )
+ {
+ case SvNumFormatType::NUMBER:
+ {
+ // either value or error code
+ rStrm << mrScFmlaCell.GetValue();
+ }
+ break;
+
+ case SvNumFormatType::TEXT:
+ {
+ OUString aResult = mrScFmlaCell.GetString().getString();
+ if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
+ {
+ rStrm << EXC_FORMULA_RES_STRING;
+ mxStringRec = new XclExpStringRec( rStrm.GetRoot(), aResult );
+ }
+ else
+ rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
+ rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
+ }
+ break;
+
+ case SvNumFormatType::LOGICAL:
+ {
+ sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
+ rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
+ << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
+ << sal_uInt16( 0xFFFF );
+ }
+ break;
+
+ default:
+ rStrm << mrScFmlaCell.GetValue();
+ }
+ }
+
+ // flags and formula token array
+ sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
+ ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
+ rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
+}
+
+// Multiple cell records ======================================================
+
+XclExpMultiCellBase::XclExpMultiCellBase(
+ sal_uInt16 nRecId, sal_uInt16 nMulRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
+ XclExpCellBase( nRecId, 0, rXclPos ),
+ mnMulRecId( nMulRecId ),
+ mnContSize( nContSize )
+{
+}
+
+sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
+{
+ return GetXclCol() + GetCellCount() - 1;
+}
+
+sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
+{
+ return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
+}
+
+bool XclExpMultiCellBase::IsEmpty() const
+{
+ return maXFIds.empty();
+}
+
+void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
+{
+ for( auto& rXFId : maXFIds )
+ rXFId.ConvertXFIndex( rRoot );
+}
+
+void XclExpMultiCellBase::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+
+ XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
+ XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
+ XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
+ sal_uInt16 nBegXclCol = GetXclCol();
+ sal_uInt16 nEndXclCol = nBegXclCol;
+
+ while( aRangeEnd != aEnd )
+ {
+ // find begin of next used XF range
+ aRangeBeg = aRangeEnd;
+ nBegXclCol = nEndXclCol;
+ while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
+ ++aRangeBeg;
+ }
+ // find end of next used XF range
+ aRangeEnd = aRangeBeg;
+ nEndXclCol = nBegXclCol;
+ while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
+ {
+ nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
+ ++aRangeEnd;
+ }
+
+ // export this range as a record
+ if( aRangeBeg != aRangeEnd )
+ {
+ sal_uInt16 nCount = nEndXclCol - nBegXclCol;
+ bool bIsMulti = nCount > 1;
+ std::size_t nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
+ if( bIsMulti ) nTotalSize += 2;
+
+ rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
+ rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
+
+ sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
+ for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
+ {
+ rStrm << aIt->mnXFIndex;
+ WriteContents( rStrm, nRelCol );
+ ++nRelCol;
+ }
+ }
+ if( bIsMulti )
+ rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
+ rStrm.EndRecord();
+ }
+ }
+}
+
+void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
+ XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
+ XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
+ sal_uInt16 nBegXclCol = GetXclCol();
+ sal_uInt16 nEndXclCol = nBegXclCol;
+
+ while( aRangeEnd != aEnd )
+ {
+ // find begin of next used XF range
+ aRangeBeg = aRangeEnd;
+ nBegXclCol = nEndXclCol;
+ while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
+ ++aRangeBeg;
+ }
+ // find end of next used XF range
+ aRangeEnd = aRangeBeg;
+ nEndXclCol = nBegXclCol;
+ while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
+ {
+ nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
+ ++aRangeEnd;
+ }
+
+ // export this range as a record
+ if( aRangeBeg != aRangeEnd )
+ {
+ sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
+ sal_Int32 nRelCol = 0;
+ for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
+ {
+ WriteXmlContents(
+ rStrm,
+ XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
+ aIt->mnXFIndex,
+ nRelColIdx );
+ ++nRelCol;
+ ++nRelColIdx;
+ }
+ }
+ }
+ }
+}
+
+sal_uInt16 XclExpMultiCellBase::GetCellCount() const
+{
+ return std::accumulate(maXFIds.begin(), maXFIds.end(), sal_uInt16(0),
+ [](const sal_uInt16& rSum, const XclExpMultiXFId& rXFId) { return rSum + rXFId.mnCount; });
+}
+
+void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
+{
+ if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
+ maXFIds.push_back( rXFId );
+ else
+ maXFIds.back().mnCount += rXFId.mnCount;
+}
+
+void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
+{
+ sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
+ rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
+ AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
+}
+
+bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
+{
+ if( GetLastXclCol() + 1 == rCell.GetXclCol() )
+ {
+ maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
+ return true;
+ }
+ return false;
+}
+
+void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
+{
+ OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
+ ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
+ for( const auto& rXFId : maXFIds )
+ {
+ ::std::fill( aDestIt, aDestIt + rXFId.mnCount, rXFId.mnXFIndex );
+ aDestIt += rXFId.mnCount;
+ }
+}
+
+void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
+{
+ // save last column before calling maXFIds.clear()
+ sal_uInt16 nLastXclCol = GetLastXclCol();
+ OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
+
+ // build new XF index vector, containing passed XF indexes
+ maXFIds.clear();
+ // Process only all that possibly are not EXC_XF_NOTFOUND.
+ size_t nEnd = std::min<size_t>(nLastXclCol + 1, nStartAllNotFound);
+ for( size_t i = GetXclCol(); i < nEnd; ++i )
+ {
+ XclExpMultiXFId aXFId( 0 );
+ // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
+ aXFId.mnXFId = aXFId.mnXFIndex = rXFIndexes[ i ];
+ AppendXFId( aXFId );
+ }
+
+ // remove leading and trailing unused XF indexes
+ if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ SetXclCol( GetXclCol() + maXFIds.front().mnCount );
+ maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
+ }
+ if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
+ maXFIds.pop_back();
+
+ // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
+}
+
+sal_uInt16 XclExpMultiCellBase::GetStartColAllDefaultCell() const
+{
+ sal_uInt16 col = GetXclCol();
+ sal_uInt16 nMaxNonDefCol = col;
+ for( const auto& rXFId : maXFIds )
+ {
+ col += rXFId.mnCount;
+ if (rXFId.mnXFIndex != EXC_XF_DEFAULTCELL)
+ nMaxNonDefCol = col;
+ }
+ return nMaxNonDefCol;
+}
+
+XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
+ XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
+{
+ OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
+ AppendXFId( rXFId );
+}
+
+XclExpBlankCell::XclExpBlankCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
+ XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
+{
+ OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
+ // #i46627# use default script type instead of ApiScriptType::WEAK
+ AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
+}
+
+bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
+{
+ const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
+ return pBlankCell && TryMergeXFIds( *pBlankCell );
+}
+
+void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
+{
+ GetXFIndexes( rXFIndexes );
+}
+
+void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
+{
+ RemoveUnusedXFIndexes( rXFIndexes, nStartAllNotFound );
+}
+
+void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
+{
+}
+
+void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, nXFId) );
+}
+
+XclExpRkCell::XclExpRkCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
+ XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
+{
+ // #i41210# always use latin script for number cells - may look wrong for special number formats...
+ AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
+ maRkValues.push_back( nRkValue );
+}
+
+bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
+{
+ const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
+ if( pRkCell && TryMergeXFIds( *pRkCell ) )
+ {
+ maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
+ return true;
+ }
+ return false;
+}
+
+void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, nXFId),
+ XML_t, "n"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
+{
+ OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
+ rStrm << maRkValues[ nRelCol ];
+}
+
+// Rows and Columns
+
+XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
+ mpScOLArray( nullptr ),
+ maLevelInfos( SC_OL_MAXDEPTH ),
+ mnCurrLevel( 0 ),
+ mbCurrCollapse( false )
+{
+ if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
+ mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
+
+ if( mpScOLArray )
+ for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
+ if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
+ maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
+}
+
+void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
+{
+ if( !mpScOLArray )
+ return;
+
+ // find open level index for passed position
+ size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
+ sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
+
+ if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
+ nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
+ // else nNewLevel keeps 0 to show that there are no groups
+
+ mbCurrCollapse = false;
+ if( nNewLevel >= mnCurrLevel )
+ {
+ // new level(s) opened, or no level closed - update all level infos
+ for( size_t nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
+ {
+ /* In each level: check if a new group is started (there may be
+ neighbored groups without gap - therefore check ALL levels). */
+ if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
+ {
+ if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
+ {
+ maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
+ maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
+ }
+ }
+ }
+ }
+ else
+ {
+ // level(s) closed - check if any of the closed levels are collapsed
+ // Calc uses 0-based level indexes
+ sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
+ for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
+ mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
+ }
+
+ // cache new opened level
+ mnCurrLevel = nNewLevel;
+}
+
+XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_GUTS, 8 ),
+ mnColLevels( 0 ),
+ mnColWidth( 0 ),
+ mnRowLevels( 0 ),
+ mnRowWidth( 0 )
+{
+ const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() );
+ if(!pOutlineTable)
+ return;
+
+ // column outline groups
+ const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
+ mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
+ if( mnColLevels )
+ {
+ ++mnColLevels;
+ mnColWidth = 12 * mnColLevels + 5;
+ }
+
+ // row outline groups
+ const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
+ if( mnRowLevels )
+ {
+ ++mnRowLevels;
+ mnRowWidth = 12 * mnRowLevels + 5;
+ }
+}
+
+void XclExpGuts::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
+}
+
+XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
+ mrRoot(rRoot),
+ mnFirstUsedXclRow( 0 ),
+ mnFirstFreeXclRow( 0 ),
+ mnFirstUsedXclCol( 0 ),
+ mnFirstFreeXclCol( 0 )
+{
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
+ case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpDimensions::SetDimensions(
+ sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
+ sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
+{
+ mnFirstUsedXclRow = nFirstUsedXclRow;
+ mnFirstFreeXclRow = nFirstFreeXclRow;
+ mnFirstUsedXclCol = nFirstUsedXclCol;
+ mnFirstFreeXclCol = nFirstFreeXclCol;
+}
+
+void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScRange aRange;
+ aRange.aStart.SetRow( static_cast<SCROW>(mnFirstUsedXclRow) );
+ aRange.aStart.SetCol( static_cast<SCCOL>(mnFirstUsedXclCol) );
+
+ if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
+ {
+ aRange.aEnd.SetRow( static_cast<SCROW>(mnFirstFreeXclRow-1) );
+ aRange.aEnd.SetCol( static_cast<SCCOL>(mnFirstFreeXclCol-1) );
+ }
+
+ aRange.PutInOrder();
+ rStrm.GetCurrentStream()->singleElement( XML_dimension,
+ // To be compatible with MS Office 2007,
+ // we need full address notation format
+ // e.g. "A1:AMJ177" and not partial like: "1:177".
+ XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), aRange, true) );
+}
+
+void XclExpDimensions::WriteBody( XclExpStream& rStrm )
+{
+ XclBiff eBiff = rStrm.GetRoot().GetBiff();
+ if( eBiff == EXC_BIFF8 )
+ rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
+ else
+ rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
+ rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
+ if( eBiff >= EXC_BIFF3 )
+ rStrm << sal_uInt16( 0 );
+}
+
+namespace {
+
+double lclGetCChCorrection(const XclExpRoot& rRoot)
+{
+ // Convert the correction from 1/256ths of a character size to count of chars
+ // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
+ // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
+ // So this should depend on rRoot.GetCharWidth(), not on font height
+
+ tools::Long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
+ return XclTools::GetXclDefColWidthCorrection(nFontHt) / 256.0;
+}
+
+} // namespace
+
+XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
+ XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF + lclGetCChCorrection(rRoot)),
+ XclExpRoot( rRoot )
+{
+}
+
+bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
+{
+ // This formula is taking number of characters with GetValue()
+ // and it is translating it into default column width.
+ // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
+ double defaultColumnWidth = 256.0 * GetValue();
+
+ // exactly matched, if difference is less than 1/16 of a character to the left or to the right
+ return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
+}
+
+void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
+{
+ double fCCh = nXclColWidth / 256.0;
+ if (bXLS)
+ {
+ const double fCorrection = lclGetCChCorrection(GetRoot());
+ const double fCorrectedCCh = fCCh - fCorrection;
+ // Now get the value which would be stored in XLS DefColWidth struct
+ double fCChRound = std::round(fCorrectedCCh);
+ // If default width was set to a value that is not representable as integral CCh between 0
+ // and 255, then just ignore that value, and use an arbitrary default. That way, the stored
+ // default might not represent the most used column width (or any used column width), but
+ // that's OK, and it just means that those columns will explicitly store their width in
+ // 1/256ths of char, and have fUserSet in their ColInfo records.
+ if (fCChRound < 0.0 || fCChRound > 255.0 || std::abs(fCChRound - fCorrectedCCh) > 1.0 / 512)
+ fCChRound = 8.0;
+ fCCh = fCChRound + fCorrection;
+ }
+
+ SetValue(fCCh);
+}
+
+void XclExpDefcolwidth::Save(XclExpStream& rStrm)
+{
+ double fCorrectedCCh = GetValue() - lclGetCChCorrection(GetRoot());
+ // Convert double to sal_uInt16
+ XclExpUInt16Record aUInt16Rec(GetRecId(),
+ static_cast<sal_uInt16>(std::round(fCorrectedCCh)));
+ aUInt16Rec.Save(rStrm);
+}
+
+XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
+ SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
+ XclExpRecord( EXC_ID_COLINFO, 12 ),
+ XclExpRoot( rRoot ),
+ mbCustomWidth( false ),
+ mnWidth( 0 ),
+ mnScWidth( 0 ),
+ mnFlags( 0 ),
+ mnOutlineLevel( 0 ),
+ mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
+ mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // column default format
+ maXFId.mnXFId = GetXFBuffer().Insert(
+ rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
+
+ // column width. If column is hidden then we should return real value (not zero)
+ sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab, false );
+ mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
+ mnScWidth = convertTwipToMm100(nScWidth);
+
+ // column flags
+ ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
+
+ // outline data
+ rOutlineBfr.Update( nScCol );
+ ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
+ ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
+ mnOutlineLevel = rOutlineBfr.GetLevel();
+}
+
+void XclExpColinfo::ConvertXFIndexes()
+{
+ maXFId.ConvertXFIndex( GetRoot() );
+}
+
+bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth )
+{
+ mbCustomWidth = !rDefColWidth.IsDefWidth(mnWidth);
+ return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) &&
+ (mnFlags == 0) &&
+ (mnOutlineLevel == 0) &&
+ !mbCustomWidth;
+}
+
+bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
+{
+ if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
+ (mnWidth == rColInfo.mnWidth) &&
+ (mnFlags == rColInfo.mnFlags) &&
+ (mnOutlineLevel == rColInfo.mnOutlineLevel) &&
+ (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
+ {
+ mnLastXclCol = rColInfo.mnLastXclCol;
+ return true;
+ }
+ return false;
+}
+
+void XclExpColinfo::WriteBody( XclExpStream& rStrm )
+{
+ // if last column is equal to last possible column, Excel adds one more
+ sal_uInt16 nLastXclCol = mnLastXclCol;
+ if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
+ ++nLastXclCol;
+
+ rStrm << mnFirstXclCol
+ << nLastXclCol
+ << mnWidth
+ << maXFId.mnXFIndex
+ << mnFlags
+ << sal_uInt16( 0 );
+}
+
+void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
+{
+ const double nExcelColumnWidth = mnScWidth / convertTwipToMm100<double>(GetCharWidth());
+
+ // tdf#101363 In MS specification the output value is set with double precision after delimiter:
+ // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
+ // Explanation of magic numbers:
+ // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
+ // It is unknown if it should be applied during LibreOffice export
+ // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
+ // 0.5 number (0.005 to output value) - used to increase value before truncating,
+ // to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
+ const double nTruncatedExcelColumnWidth = std::trunc( nExcelColumnWidth * 100.0 + 0.5 ) / 100.0;
+ rStrm.GetCurrentStream()->singleElement( XML_col,
+ // OOXTODO: XML_bestFit,
+ XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
+ XML_customWidth, ToPsz( mbCustomWidth ),
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
+ XML_outlineLevel, OString::number(mnOutlineLevel),
+ XML_max, OString::number(mnLastXclCol + 1),
+ XML_min, OString::number(mnFirstXclCol + 1),
+ // OOXTODO: XML_phonetic,
+ XML_style, lcl_GetStyleId(rStrm, maXFId.mnXFIndex),
+ XML_width, OString::number(nTruncatedExcelColumnWidth) );
+}
+
+XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maDefcolwidth( rRoot ),
+ maOutlineBfr( rRoot ),
+ mnHighestOutlineLevel( 0 )
+{
+}
+
+void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
+{
+
+ for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
+ {
+ maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
+ if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
+ {
+ mnHighestOutlineLevel = maOutlineBfr.GetLevel();
+ }
+ }
+}
+
+void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
+{
+ rXFIndexes.clear();
+ rXFIndexes.reserve( maColInfos.GetSize() );
+
+ if( !maColInfos.IsEmpty())
+ {
+ XclExpColinfo* xPrevRec = maColInfos.GetRecord( 0 );
+ xPrevRec->ConvertXFIndexes();
+ for( size_t nPos = 1; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ xRec->ConvertXFIndexes();
+
+ // try to merge with previous record
+ if( xPrevRec->TryMerge( *xRec ) )
+ maColInfos.InvalidateRecord( nPos );
+ else
+ xPrevRec = xRec;
+ }
+ maColInfos.RemoveInvalidatedRecords();
+ }
+
+ // put XF indexes into passed vector, collect use count of all different widths
+ std::unordered_map< sal_uInt16, sal_uInt16 > aWidthMap;
+ sal_uInt16 nMaxColCount = 0;
+ sal_uInt16 nMaxUsedWidth = 0;
+ for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ const XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ sal_uInt16 nColCount = xRec->GetColCount();
+
+ // add XF index to passed vector
+ rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
+
+ // collect use count of column width
+ sal_uInt16 nWidth = xRec->GetColWidth();
+ sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
+ rnMapCount = rnMapCount + nColCount;
+ if( rnMapCount > nMaxColCount )
+ {
+ nMaxColCount = rnMapCount;
+ nMaxUsedWidth = nWidth;
+ }
+ }
+ maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
+
+ // remove all default COLINFO records
+ for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ if( xRec->IsDefault( maDefcolwidth ) )
+ maColInfos.InvalidateRecord( nPos );
+ }
+ maColInfos.RemoveInvalidatedRecords();
+}
+
+void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
+{
+ // DEFCOLWIDTH
+ maDefcolwidth.Save( rStrm );
+ // COLINFO records
+ maColInfos.Save( rStrm );
+}
+
+void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maColInfos.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_cols);
+ maColInfos.SaveXml( rStrm );
+ rWorksheet->endElement( XML_cols );
+}
+
+XclExpDefaultRowData::XclExpDefaultRowData() :
+ mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
+{
+}
+
+XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
+ mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mnHeight( rRow.GetHeight() )
+{
+ ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
+ ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
+}
+
+static bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
+{
+ return (rLeft.mnHeight < rRight.mnHeight) ||
+ ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
+}
+
+XclExpDefrowheight::XclExpDefrowheight() :
+ XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
+{
+}
+
+void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
+{
+ maDefData = rDefData;
+}
+
+void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+ rStrm << maDefData.mnFlags << maDefData.mnHeight;
+}
+
+XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
+ XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight ) :
+ XclExpRecord( EXC_ID3_ROW, 16 ),
+ XclExpRoot( rRoot ),
+ mnXclRow( nXclRow ),
+ mnHeight( nHeight ),
+ mnFlags( EXC_ROW_DEFAULTFLAGS ),
+ mnXFIndex( EXC_XF_DEFAULTCELL ),
+ mnOutlineLevel( 0 ),
+ mnXclRowRpt( 1 ),
+ mnCurrentRow( nXclRow ),
+ mbAlwaysEmpty( bAlwaysEmpty ),
+ mbEnabled( true )
+{
+ SCTAB nScTab = GetCurrScTab();
+ SCROW nScRow = static_cast< SCROW >( mnXclRow );
+
+ // *** Row flags *** ------------------------------------------------------
+
+ CRFlags nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
+ bool bUserHeight( nRowFlags & CRFlags::ManualSize );
+ ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
+ ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
+
+ // *** Outline data *** ---------------------------------------------------
+
+ rOutlineBfr.Update( nScRow );
+ ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
+ ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
+ mnOutlineLevel = rOutlineBfr.GetLevel();
+
+ // *** Progress bar *** ---------------------------------------------------
+
+ XclExpProgressBar& rProgress = GetProgressBar();
+ rProgress.IncRowRecordCount();
+ rProgress.Progress();
+}
+
+static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec& rIndexes, sal_uInt16 value,
+ size_t searchStart = std::numeric_limits<size_t>::max())
+{
+ for( size_t i = std::min(rIndexes.size(), searchStart); i >= 1; --i )
+ {
+ if( rIndexes[ i - 1 ] != value )
+ return i;
+ }
+ return 0;
+}
+
+void XclExpRow::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
+{
+ OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
+ // try to merge with last existing cell
+ InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
+}
+
+void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, ScfUInt16Vec& aXFIndexes, size_t nStartColAllDefault, bool bProgress )
+{
+ size_t nPos, nSize;
+
+ // *** Convert XF identifiers *** -----------------------------------------
+
+ // additionally collect the blank XF indexes
+ size_t nColCount = GetMaxPos().Col() + 1;
+ OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
+
+ // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
+ // and clearing.
+ assert( aXFIndexes.size() == nColCount );
+ assert( aXFIndexes.front() == EXC_XF_NOTFOUND );
+ assert( aXFIndexes.back() == EXC_XF_NOTFOUND );
+ for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpCellBase* pCell = maCellList.GetRecord( nPos );
+ pCell->ConvertXFIndexes( GetRoot() );
+ pCell->GetBlankXFIndexes( aXFIndexes );
+ }
+
+ // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
+
+ /* This is needed because nonexistent cells in Calc are not formatted at all,
+ but in Excel they would have the column default format. Blank cells that
+ are equal to the respective column default are removed later in this function. */
+ if( !mbAlwaysEmpty )
+ {
+ // XF identifier representing default cell XF
+ XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
+ aXFId.ConvertXFIndex( GetRoot() );
+
+ nPos = 0;
+ while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
+ {
+ // get column index that follows previous cell
+ sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
+ // get own column index
+ sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
+
+ // is there a gap?
+ if( nFirstFreeXclCol < nNextUsedXclCol )
+ {
+ aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
+ XclExpCellRef xNewCell = new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId );
+ // insert the cell, InsertCell() may merge it with existing BLANK records
+ InsertCell( xNewCell, nPos, false );
+ // insert default XF indexes into aXFIndexes
+ for( size_t i = nFirstFreeXclCol; i < nNextUsedXclCol; ++i )
+ aXFIndexes[ i ] = aXFId.mnXFIndex;
+ // don't step forward with nPos, InsertCell() may remove records
+ }
+ else
+ ++nPos;
+ }
+ }
+
+ // *** Find default row format *** ----------------------------------------
+
+ // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
+ size_t nStartSearchAllDefault = aXFIndexes.size();
+ if( !maCellList.IsEmpty() && dynamic_cast< const XclExpBlankCell* >( maCellList.GetLastRecord()))
+ {
+ const XclExpBlankCell* pLastBlank = static_cast< const XclExpBlankCell* >( maCellList.GetLastRecord());
+ assert(pLastBlank->GetLastXclCol() == aXFIndexes.size() - 1);
+ nStartSearchAllDefault = pLastBlank->GetStartColAllDefaultCell();
+ }
+ size_t nStartAllDefault = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_DEFAULTCELL, nStartSearchAllDefault);
+
+ // find most used XF index in the row
+ sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
+ const size_t nHalfIndexes = aXFIndexes.size() / 2;
+ if( nStartAllDefault > nHalfIndexes ) // Otherwise most are EXC_XF_DEFAULTCELL.
+ {
+ // Very likely the most common one is going to be the last one.
+ nRowXFIndex = aXFIndexes.back();
+ size_t nStartLastSame = findFirstAllSameUntilEnd( aXFIndexes, nRowXFIndex );
+ if( nStartLastSame > nHalfIndexes ) // No, find out the most used one by counting.
+ {
+ std::unordered_map< sal_uInt16, size_t > aIndexMap;
+ size_t nMaxXFCount = 0;
+ for( const auto& rXFIndex : aXFIndexes )
+ {
+ if( rXFIndex != EXC_XF_NOTFOUND )
+ {
+ size_t& rnCount = aIndexMap[ rXFIndex ];
+ ++rnCount;
+ if( rnCount > nMaxXFCount )
+ {
+ nRowXFIndex = rXFIndex;
+ nMaxXFCount = rnCount;
+ if (nMaxXFCount > nHalfIndexes)
+ {
+ // No other XF index can have a greater usage count, we
+ // don't need to loop through the remaining cells.
+ // Specifically for the tail of unused default
+ // cells/columns this makes a difference.
+ break; // for
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // decide whether to use the row default XF index or column default XF indexes
+ bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
+ if( !bUseColDefXFs )
+ {
+ // count needed XF indexes for blank cells with and without row default XF index
+ size_t nXFCountWithRowDefXF = 0;
+ size_t nXFCountWithoutRowDefXF = 0;
+ ScfUInt16Vec::const_iterator aColIt = rColXFIndexes.begin();
+ for( const auto& rXFIndex : aXFIndexes )
+ {
+ sal_uInt16 nXFIndex = rXFIndex;
+ if( nXFIndex != EXC_XF_NOTFOUND )
+ {
+ if( nXFIndex != nRowXFIndex )
+ ++nXFCountWithRowDefXF; // with row default XF index
+ if( nXFIndex != *aColIt )
+ ++nXFCountWithoutRowDefXF; // without row default XF index
+ }
+ ++aColIt;
+ }
+
+ // use column XF indexes if this would cause less or equal number of BLANK records
+ bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
+ }
+
+ // *** Remove unused BLANK cell records *** -------------------------------
+
+ size_t maxStartAllNotFound;
+ if( bUseColDefXFs )
+ {
+ size_t maxStartAllDefault = std::max( nStartAllDefault, nStartColAllDefault );
+ // use column default XF indexes
+ // #i194#: remove cell XF indexes equal to column default XF indexes
+ for( size_t i = 0; i < maxStartAllDefault; ++i )
+ {
+ if( aXFIndexes[ i ] == rColXFIndexes[ i ] )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+ }
+ // They can differ only up to maxNonDefault, in the rest they are the same.
+ for( size_t i = maxStartAllDefault; i < aXFIndexes.size(); ++i )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+ maxStartAllNotFound = maxStartAllDefault;
+ }
+ else
+ {
+ // use row default XF index
+ mnXFIndex = nRowXFIndex;
+ ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
+ // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
+ for( auto& rXFIndex : aXFIndexes )
+ if( rXFIndex == nRowXFIndex )
+ rXFIndex = EXC_XF_NOTFOUND;
+ maxStartAllNotFound = aXFIndexes.size();
+ }
+
+ // remove unused parts of BLANK/MULBLANK cell records
+ size_t nStartAllNotFound = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_NOTFOUND, maxStartAllNotFound );
+ nPos = 0;
+ while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
+ {
+ XclExpCellBase* xCell = maCellList.GetRecord( nPos );
+ xCell->RemoveUnusedBlankCells( aXFIndexes, nStartAllNotFound );
+ if( xCell->IsEmpty() )
+ maCellList.RemoveRecord( nPos );
+ else
+ ++nPos;
+ }
+ // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
+ for( size_t i = 0; i < nStartAllNotFound; ++i )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+
+ // progress bar includes disabled rows; only update it in the lead thread.
+ if (bProgress)
+ GetProgressBar().Progress();
+}
+sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
+{
+ return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
+}
+
+sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
+{
+ return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
+}
+
+bool XclExpRow::IsDefaultable() const
+{
+ const sal_uInt16 nFlagsAlwaysMarkedAsDefault = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
+ return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nFlagsAlwaysMarkedAsDefault ) ) &&
+ IsEmpty();
+}
+
+void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
+{
+ mbEnabled = !IsDefaultable() ||
+ (mnHeight != rDefRowData.mnHeight) ||
+ (IsHidden() != rDefRowData.IsHidden()) ||
+ (IsUnsynced() != rDefRowData.IsUnsynced());
+}
+
+void XclExpRow::WriteCellList( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
+ maCellList.Save( rStrm );
+}
+
+void XclExpRow::Save( XclExpStream& rStrm )
+{
+ if( mbEnabled )
+ {
+ mnCurrentRow = mnXclRow;
+ for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
+ XclExpRecord::Save( rStrm );
+ }
+}
+
+void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
+{
+ OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
+
+ /* If we have a multi-line text in a merged cell, and the resulting
+ row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
+ flag to be true to ensure Excel works correctly. */
+ if( bIsMergedBase && xCell->IsMultiLineText() )
+ ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
+
+ // try to merge with previous cell, insert the new cell if not successful
+ XclExpCellBase* xPrevCell = maCellList.GetRecord( nPos - 1 );
+ if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
+ xCell = xPrevCell;
+ else
+ maCellList.InsertRecord( xCell, nPos++ );
+ // nPos points now to following cell
+
+ // try to merge with following cell, remove it if successful
+ XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
+ if( xNextCell && xCell->TryMerge( *xNextCell ) )
+ maCellList.RemoveRecord( nPos );
+}
+
+void XclExpRow::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >(mnCurrentRow)
+ << GetFirstUsedXclCol()
+ << GetFirstFreeXclCol()
+ << mnHeight
+ << sal_uInt32( 0 )
+ << mnFlags
+ << mnXFIndex;
+}
+
+void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !mbEnabled )
+ return;
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
+ mnCurrentRow = mnXclRow + 1;
+ for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
+ {
+ rWorksheet->startElement( XML_row,
+ XML_r, OString::number(mnCurrentRow++),
+ // OOXTODO: XML_spans, optional
+ XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : nullptr,
+ XML_customFormat, ToPsz( haveFormat ),
+ XML_ht, OString::number(static_cast<double>(mnHeight) / 20.0),
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
+ XML_customHeight, ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
+ XML_outlineLevel, OString::number(mnOutlineLevel),
+ XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) )
+ // OOXTODO: XML_thickTop, bool
+ // OOXTODO: XML_thickBot, bool
+ // OOXTODO: XML_ph, bool
+ );
+ // OOXTODO: XML_extLst
+ maCellList.SaveXml( rStrm );
+ rWorksheet->endElement( XML_row );
+ }
+}
+
+XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maOutlineBfr( rRoot ),
+ maDimensions( rRoot ),
+ mnHighestOutlineLevel( 0 )
+{
+}
+
+void XclExpRowBuffer::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
+{
+ OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
+ GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
+}
+
+void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
+{
+ if( nFirstFreeScRow > 0 )
+ GetOrCreateRow( ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
+}
+
+namespace {
+
+class RowFinalizeTask : public comphelper::ThreadTask
+{
+ bool mbProgress;
+ const ScfUInt16Vec& mrColXFIndexes;
+ size_t mnStartColAllDefault;
+ std::vector< XclExpRow * > maRows;
+public:
+ RowFinalizeTask( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
+ const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault,
+ bool bProgress ) :
+ comphelper::ThreadTask( pTag ),
+ mbProgress( bProgress ),
+ mrColXFIndexes( rColXFIndexes ),
+ mnStartColAllDefault( nStartColAllDefault )
+ {}
+
+ void push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
+ virtual void doWork() override
+ {
+ ScfUInt16Vec aXFIndexes( mrColXFIndexes.size(), EXC_XF_NOTFOUND );
+ for (XclExpRow* p : maRows)
+ p->Finalize( mrColXFIndexes, aXFIndexes, mnStartColAllDefault, mbProgress );
+ }
+};
+
+}
+
+void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData,
+ const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault )
+{
+ // *** Finalize all rows *** ----------------------------------------------
+
+ GetProgressBar().ActivateFinalRowsSegment();
+
+#if 1
+ // This is staggeringly slow, and each element operates only
+ // on its own data.
+ const size_t nRows = maRowMap.size();
+ const size_t nThreads = nRows < 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
+#else
+ const size_t nThreads = 1; // globally disable multi-threading for now.
+#endif
+ if (nThreads == 1)
+ {
+ ScfUInt16Vec aXFIndexes( rColXFIndexes.size(), EXC_XF_NOTFOUND );
+ for (auto& rEntry : maRowMap)
+ rEntry.second->Finalize( rColXFIndexes, aXFIndexes, nStartColAllDefault, true );
+ }
+ else
+ {
+ comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ std::vector<std::unique_ptr<RowFinalizeTask>> aTasks(nThreads);
+ for ( size_t i = 0; i < nThreads; i++ )
+ aTasks[ i ].reset( new RowFinalizeTask( pTag, rColXFIndexes, nStartColAllDefault, i == 0 ) );
+
+ size_t nIdx = 0;
+ for ( const auto& rEntry : maRowMap )
+ {
+ aTasks[ nIdx % nThreads ]->push_back( rEntry.second.get() );
+ ++nIdx;
+ }
+
+ for ( size_t i = 1; i < nThreads; i++ )
+ rPool.pushTask( std::move(aTasks[ i ]) );
+
+ // Progress bar updates must be synchronous to avoid deadlock
+ aTasks[0]->doWork();
+
+ rPool.waitUntilDone(pTag);
+ }
+
+ // *** Default row format *** ---------------------------------------------
+
+ std::map< XclExpDefaultRowData, size_t > aDefRowMap;
+
+ XclExpDefaultRowData aMaxDefData;
+ size_t nMaxDefCount = 0;
+ // only look for default format in existing rows, if there are more than unused
+ // if the row is hidden, then row xml must be created even if it not contain cells
+ XclExpRow* pPrev = nullptr;
+ std::vector< XclExpRow* > aRepeated;
+ for (const auto& rEntry : maRowMap)
+ {
+ const RowRef& rRow = rEntry.second;
+ if ( rRow->IsDefaultable() )
+ {
+ XclExpDefaultRowData aDefData( *rRow );
+ size_t& rnDefCount = aDefRowMap[ aDefData ];
+ ++rnDefCount;
+ if( rnDefCount > nMaxDefCount )
+ {
+ nMaxDefCount = rnDefCount;
+ aMaxDefData = aDefData;
+ }
+ }
+ if ( pPrev )
+ {
+ if ( pPrev->IsDefaultable() )
+ {
+ // if the previous row we processed is not
+ // defaultable then afaict the rows in between are
+ // not used ( and not repeatable )
+ sal_uInt32 nRpt = rRow->GetXclRow() - pPrev->GetXclRow();
+ if ( nRpt > 1 )
+ aRepeated.push_back( pPrev );
+ pPrev->SetXclRowRpt( nRpt );
+ XclExpDefaultRowData aDefData( *pPrev );
+ size_t& rnDefCount = aDefRowMap[ aDefData ];
+ rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
+ if( rnDefCount > nMaxDefCount )
+ {
+ nMaxDefCount = rnDefCount;
+ aMaxDefData = aDefData;
+ }
+ }
+ }
+ pPrev = rRow.get();
+ }
+ // return the default row format to caller
+ rDefRowData = aMaxDefData;
+
+ // now disable repeating extra (empty) rows that are equal to the default row
+ for (auto& rpRow : aRepeated)
+ {
+ if ( rpRow->GetXclRowRpt() > 1
+ && rpRow->GetHeight() == rDefRowData.mnHeight
+ && rpRow->IsHidden() == rDefRowData.IsHidden() )
+ {
+ rpRow->SetXclRowRpt( 1 );
+ }
+ }
+
+ // *** Disable unused ROW records, find used area *** ---------------------
+
+ sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
+ sal_uInt16 nFirstFreeXclCol = 0;
+ sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
+ sal_uInt32 nFirstFreeXclRow = 0;
+
+ for (const auto& rEntry : maRowMap)
+ {
+ const RowRef& rRow = rEntry.second;
+ // disable unused rows
+ rRow->DisableIfDefault( aMaxDefData );
+
+ // find used column range
+ if( !rRow->IsEmpty() ) // empty rows return (0...0) as used range
+ {
+ nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
+ nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
+ }
+
+ // find used row range
+ if( rRow->IsEnabled() )
+ {
+ sal_uInt32 nXclRow = rRow->GetXclRow();
+ nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
+ nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
+ }
+ }
+
+ // adjust start position, if there are no or only empty/disabled ROW records
+ nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
+ nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
+
+ // initialize the DIMENSIONS record
+ maDimensions.SetDimensions(
+ nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
+}
+
+void XclExpRowBuffer::Save( XclExpStream& rStrm )
+{
+ // DIMENSIONS record
+ maDimensions.Save( rStrm );
+
+ // save in blocks of 32 rows, each block contains first all ROWs, then all cells
+ size_t nSize = maRowMap.size();
+ RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
+ RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
+ sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itr->second->GetXclRow();
+
+ for (; itr != itrEnd; ++itr)
+ {
+ // find end of row block
+ itrBlkEnd = std::find_if_not(itrBlkEnd, itrEnd,
+ [&nStartXclRow](const RowMap::value_type& rRow) { return rRow.second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE; });
+
+ // write the ROW records
+ std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->Save( rStrm ); });
+
+ // write the cell records
+ std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->WriteCellList( rStrm ); });
+
+ itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
+ nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
+ }
+}
+
+void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (std::none_of(maRowMap.begin(), maRowMap.end(), [](const RowMap::value_type& rRow) { return rRow.second->IsEnabled(); }))
+ {
+ rStrm.GetCurrentStream()->singleElement(XML_sheetData);
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_sheetData);
+ for (const auto& rEntry : maRowMap)
+ rEntry.second->SaveXml(rStrm);
+ rWorksheet->endElement( XML_sheetData );
+}
+
+XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
+{
+ // This is called rather often, so optimize for the most common case of saving row by row
+ // (so the requested row is often the last one in the map or belongs after the last one).
+ RowMap::iterator itr;
+ if(maRowMap.empty())
+ itr = maRowMap.end();
+ else
+ {
+ RowMap::reverse_iterator last = maRowMap.rbegin();
+ if( last->first == nXclRow )
+ return *last->second;
+ if( nXclRow > last->first )
+ itr = maRowMap.end();
+ else
+ itr = maRowMap.lower_bound( nXclRow );
+ }
+ const bool bFound = itr != maRowMap.end();
+ // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
+ // coverity[deref_iterator : FALSE] - clearly itr if only derefed if bFound which checks for valid itr
+ const bool bFoundHigher = bFound && itr->first != nXclRow;
+ if( bFound && !bFoundHigher )
+ return *itr->second;
+
+ size_t nFrom = 0;
+ RowRef pPrevEntry;
+ if( itr != maRowMap.begin() )
+ {
+ --itr;
+ pPrevEntry = itr->second;
+ if( bFoundHigher )
+ nFrom = nXclRow;
+ else
+ nFrom = itr->first + 1;
+ }
+
+ const ScDocument& rDoc = GetRoot().GetDoc();
+ const SCTAB nScTab = GetRoot().GetCurrScTab();
+ // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
+ bool bHidden = false;
+ SCROW lastSameHiddenRow = -1;
+ sal_uInt16 nHeight = 0;
+ SCROW lastSameHeightRow = -1;
+ // create the missing rows first
+ while( nFrom <= nXclRow )
+ {
+ // only create RowMap entries if it is first row in spreadsheet,
+ // if it is the desired row, or for rows that differ from previous.
+ if( static_cast<SCROW>(nFrom) > lastSameHiddenRow )
+ bHidden = rDoc.RowHidden(nFrom, nScTab, nullptr, &lastSameHiddenRow);
+ // Always get the actual row height even if the manual size flag is
+ // not set, to correctly export the heights of rows with wrapped
+ // texts.
+ if( static_cast<SCROW>(nFrom) > lastSameHeightRow )
+ nHeight = rDoc.GetRowHeight(nFrom, nScTab, nullptr, &lastSameHeightRow, false);
+ if ( !pPrevEntry || ( nFrom == nXclRow ) ||
+ ( maOutlineBfr.IsCollapsed() ) ||
+ ( maOutlineBfr.GetLevel() != 0 ) ||
+ ( bRowAlwaysEmpty && !pPrevEntry->IsEmpty() ) ||
+ ( bHidden != pPrevEntry->IsHidden() ) ||
+ ( nHeight != pPrevEntry->GetHeight() ) )
+ {
+ if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
+ {
+ mnHighestOutlineLevel = maOutlineBfr.GetLevel();
+ }
+ RowRef p = std::make_shared<XclExpRow>(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty, bHidden, nHeight);
+ maRowMap.emplace(nFrom, p);
+ pPrevEntry = p;
+ }
+ ++nFrom;
+ }
+ itr = maRowMap.find(nXclRow);
+ return *itr->second;
+}
+
+// Cell Table
+
+XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maColInfoBfr( rRoot ),
+ maRowBfr( rRoot ),
+ maArrayBfr( rRoot ),
+ maShrfmlaBfr( rRoot ),
+ maTableopBfr( rRoot ),
+ mxDefrowheight( new XclExpDefrowheight() ),
+ mxGuts( new XclExpGuts( rRoot ) ),
+ mxNoteList( new XclExpNoteList ),
+ mxMergedcells( new XclExpMergedcells( rRoot ) ),
+ mxHyperlinkList( new XclExpHyperlinkList ),
+ mxDval( new XclExpDval( rRoot ) ),
+ mxExtLst( new XclExtLst( rRoot ) )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // maximum sheet limits
+ SCCOL nMaxScCol = GetMaxPos().Col();
+ SCROW nMaxScRow = GetMaxPos().Row();
+
+ // find used area (non-empty cells)
+ SCCOL nLastUsedScCol;
+ SCROW nLastUsedScRow;
+ rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
+
+ if(nLastUsedScCol > nMaxScCol)
+ nLastUsedScCol = nMaxScCol;
+
+ // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
+ nLastUsedScRow += 1000;
+
+ if(nLastUsedScRow > nMaxScRow)
+ nLastUsedScRow = nMaxScRow;
+
+ ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
+ GetAddressConverter().ValidateRange( aUsedRange, true );
+ nLastUsedScRow = aUsedRange.aEnd.Row();
+
+ // first row without any set attributes (height/hidden/...)
+ SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
+
+ // find range of outlines
+ SCROW nFirstUngroupedScRow = 0;
+ if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
+ {
+ SCCOLROW nScStartPos, nScEndPos;
+ const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ rRowArray.GetRange( nScStartPos, nScEndPos );
+ // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
+ nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
+ }
+
+ // column settings
+ /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
+ formatting cause big Excel files, because all rows from row 1 to row
+ 32000 are exported. Now, if the used area goes exactly to row 32000,
+ use this row as default and ignore all rows >32000.
+ #i59220# Tolerance of +-128 rows for inserted/removed rows. */
+ if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
+ nMaxScRow = nLastUsedScRow;
+ maColInfoBfr.Initialize( nMaxScRow );
+
+ // range for cell iterator
+ SCCOL nLastIterScCol = nMaxScCol;
+ SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
+ ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
+
+ // activate the correct segment and sub segment at the progress bar
+ GetProgressBar().ActivateCreateRowsSegment();
+
+ for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
+ {
+ SCCOL nScCol = aIt.GetStartCol();
+ SCROW nScRow = aIt.GetRow();
+ SCCOL nLastScCol = aIt.GetEndCol();
+ ScAddress aScPos( nScCol, nScRow, nScTab );
+
+ XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
+ sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
+
+ const ScRefCellValue& rScCell = aIt.GetCell();
+ XclExpCellRef xCell;
+
+ const ScPatternAttr* pPattern = aIt.GetPattern();
+
+ // handle overlapped merged cells before creating the cell record
+ sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
+ bool bIsMergedBase = false;
+ if( pPattern )
+ {
+ const SfxItemSet& rItemSet = pPattern->GetItemSet();
+ // base cell in a merged range
+ const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
+ bIsMergedBase = rMergeItem.IsMerged();
+ /* overlapped cell in a merged range; in Excel all merged cells
+ must contain same XF index, for correct border */
+ const ScMergeFlagAttr& rMergeFlagItem = rItemSet.Get( ATTR_MERGE_FLAG );
+ if( rMergeFlagItem.IsOverlapped() )
+ nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
+ }
+
+ OUString aAddNoteText; // additional text to be appended to a note
+
+ switch (rScCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ double fValue = rScCell.getDouble();
+
+ if (pPattern)
+ {
+ OUString aUrl = pPattern->GetItemSet().Get(ATTR_HYPERLINK).GetValue();
+ if (!aUrl.isEmpty())
+ {
+ rtl::Reference<XclExpHyperlink> aLink =
+ new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
+ mxHyperlinkList->AppendRecord(aLink);
+ }
+ }
+
+ // try to create a Boolean cell
+ if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
+ {
+ sal_uInt32 nScNumFmt = pPattern->GetItemSet().Get( ATTR_VALUE_FORMAT ).GetValue();
+ if( rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL )
+ xCell = new XclExpBooleanCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 );
+ }
+
+ // try to create an RK value (compressed floating-point number)
+ sal_Int32 nRkValue;
+ if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
+ xCell = new XclExpRkCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue );
+
+ // else: simple floating-point number cell
+ if( !xCell )
+ xCell = new XclExpNumberCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue );
+ }
+ break;
+
+ case CELLTYPE_STRING:
+ {
+ xCell = new XclExpLabelCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getSharedString()->getString());
+ }
+ break;
+
+ case CELLTYPE_EDIT:
+ {
+ XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
+ xCell = new XclExpLabelCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getEditText(), aLinkHelper);
+
+ // add a single created HLINK record to the record list
+ if( aLinkHelper.HasLinkRecord() )
+ mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
+ // add list of multiple URLs to the additional cell note text
+ if( aLinkHelper.HasMultipleUrls() )
+ aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
+ }
+ break;
+
+ case CELLTYPE_FORMULA:
+ {
+ if (pPattern)
+ {
+ OUString aUrl = pPattern->GetItemSet().Get(ATTR_HYPERLINK).GetValue();
+ if (!aUrl.isEmpty())
+ {
+ rtl::Reference<XclExpHyperlink> aLink =
+ new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
+ mxHyperlinkList->AppendRecord(aLink);
+ }
+ }
+
+ xCell = new XclExpFormulaCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
+ *rScCell.getFormula(), maArrayBfr, maShrfmlaBfr, maTableopBfr);
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
+ [[fallthrough]];
+ case CELLTYPE_NONE:
+ {
+ xCell = new XclExpBlankCell(
+ GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId );
+ }
+ break;
+ }
+
+ assert(xCell && "can only reach here with xCell set");
+
+ // insert the cell into the current row
+ maRowBfr.AppendCell( xCell, bIsMergedBase );
+
+ if ( !aAddNoteText.isEmpty() )
+ mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, nullptr, aAddNoteText ) );
+
+ // other sheet contents
+ if( pPattern )
+ {
+ const SfxItemSet& rItemSet = pPattern->GetItemSet();
+
+ // base cell in a merged range
+ if( bIsMergedBase )
+ {
+ const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
+ ScRange aScRange( aScPos );
+ aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
+ aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
+ sal_uInt32 nXFId = xCell->GetFirstXFId();
+ // blank cells merged vertically may occur repeatedly
+ OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
+ "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
+ for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
+ {
+ mxMergedcells->AppendRange( aScRange, nXFId );
+ aScRange.aStart.IncCol();
+ aScRange.aEnd.IncCol();
+ }
+ }
+
+ // data validation
+ if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
+ {
+ sal_uInt32 nScHandle = rItemSet.Get( ATTR_VALIDDATA ).GetValue();
+ ScRange aScRange( aScPos );
+ aScRange.aEnd.SetCol( nLastScCol );
+ mxDval->InsertCellRange( aScRange, nScHandle );
+ }
+ }
+ }
+
+ // create missing row settings for rows anyhow flagged or with outlines
+ maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
+}
+
+void XclExpCellTable::Finalize(bool bXLS)
+{
+ // Finalize multiple operations.
+ maTableopBfr.Finalize();
+
+ /* Finalize column buffer. This calculates column default XF indexes from
+ the XF identifiers and fills a vector with these XF indexes. */
+ ScfUInt16Vec aColXFIndexes;
+ maColInfoBfr.Finalize( aColXFIndexes, bXLS );
+
+ // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
+ // the index that starts all EXC_XF_DEFAULTCELL until the end.
+ size_t nStartColAllDefault = findFirstAllSameUntilEnd( aColXFIndexes, EXC_XF_DEFAULTCELL );
+
+ /* Finalize row buffer. This calculates all cell XF indexes from the XF
+ identifiers. Then the XF index vector aColXFIndexes (filled above) is
+ used to calculate the row default formats. With this, all unneeded blank
+ cell records (equal to row default or column default) will be removed.
+ The function returns the (most used) default row format in aDefRowData. */
+ XclExpDefaultRowData aDefRowData;
+ maRowBfr.Finalize( aDefRowData, aColXFIndexes, nStartColAllDefault );
+
+ // Initialize the DEFROWHEIGHT record.
+ mxDefrowheight->SetDefaultData( aDefRowData );
+}
+
+XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
+{
+ XclExpRecordRef xRec;
+ switch( nRecId )
+ {
+ case EXC_ID3_DIMENSIONS: xRec = new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ); break;
+ case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
+ case EXC_ID_GUTS: xRec = mxGuts; break;
+ case EXC_ID_NOTE: xRec = mxNoteList; break;
+ case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
+ case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
+ case EXC_ID_DVAL: xRec = mxDval; break;
+ case EXC_ID_EXTLST: xRec = mxExtLst; break;
+ default: OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
+ }
+ return xRec;
+}
+
+void XclExpCellTable::Save( XclExpStream& rStrm )
+{
+ // DEFCOLWIDTH and COLINFOs
+ maColInfoBfr.Save( rStrm );
+ // ROWs and cell records
+ maRowBfr.Save( rStrm );
+}
+
+void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
+{
+ // DEFAULT row height
+ XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetFormatPr,
+ // OOXTODO: XML_baseColWidth
+ XML_defaultColWidth, OString::number(maColInfoBfr.GetDefColWidth()),
+ // OOXTODO: XML_customHeight
+ // OOXTODO: XML_thickTop
+ // OOXTODO: XML_thickBottom
+ XML_defaultRowHeight, OString::number(static_cast<double> (rDefData.mnHeight) / 20.0),
+ XML_zeroHeight, ToPsz( rDefData.IsHidden() ),
+ XML_outlineLevelRow, OString::number(maRowBfr.GetHighestOutlineLevel()),
+ XML_outlineLevelCol, OString::number(maColInfoBfr.GetHighestOutlineLevel()) );
+ rWorksheet->endElement( XML_sheetFormatPr );
+
+ maColInfoBfr.SaveXml( rStrm );
+ maRowBfr.SaveXml( rStrm );
+ mxExtLst->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeview.cxx b/sc/source/filter/excel/xeview.cxx
new file mode 100644
index 0000000000..5db6de1c2f
--- /dev/null
+++ b/sc/source/filter/excel/xeview.cxx
@@ -0,0 +1,537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xeview.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <viewopti.hxx>
+#include <xelink.hxx>
+#include <xestyle.hxx>
+#include <xehelper.hxx>
+#include <xltools.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+
+using namespace ::oox;
+
+// Workbook view settings records =============================================
+
+XclExpWindow1::XclExpWindow1( const XclExpRoot& rRoot )
+ : XclExpRecord(EXC_ID_WINDOW1, 18)
+ , mnFlags(0)
+ , mnTabBarSize(600)
+{
+ const ScViewOptions& rViewOpt = rRoot.GetDoc().GetViewOptions();
+ ::set_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR, rViewOpt.GetOption( VOPT_HSCROLL ) );
+ ::set_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR, rViewOpt.GetOption( VOPT_VSCROLL ) );
+ ::set_flag( mnFlags, EXC_WIN1_TABBAR, rViewOpt.GetOption( VOPT_TABCONTROLS ) );
+
+ double fTabBarWidth = rRoot.GetExtDocOptions().GetDocSettings().mfTabBarWidth;
+ if( (0.0 <= fTabBarWidth) && (fTabBarWidth <= 1.0) )
+ mnTabBarSize = static_cast< sal_uInt16 >( fTabBarWidth * 1000.0 + 0.5 );
+}
+
+void XclExpWindow1::SaveXml( XclExpXmlStream& rStrm )
+{
+ const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo();
+
+ rStrm.GetCurrentStream()->singleElement( XML_workbookView,
+ // OOXTODO: XML_visibility, // ST_visibility
+ // OOXTODO: XML_minimized, // bool
+ XML_showHorizontalScroll, ToPsz( ::get_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR ) ),
+ XML_showVerticalScroll, ToPsz( ::get_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR ) ),
+ XML_showSheetTabs, ToPsz( ::get_flag( mnFlags, EXC_WIN1_TABBAR ) ),
+ XML_xWindow, "0",
+ XML_yWindow, "0",
+ XML_windowWidth, OString::number(0x4000),
+ XML_windowHeight, OString::number(0x2000),
+ XML_tabRatio, OString::number(mnTabBarSize),
+ XML_firstSheet, OString::number(rTabInfo.GetFirstVisXclTab()),
+ XML_activeTab, OString::number(rTabInfo.GetDisplayedXclTab())
+ // OOXTODO: XML_autoFilterDateGrouping, // bool; AUTOFILTER12? 87Eh
+ );
+}
+
+void XclExpWindow1::WriteBody( XclExpStream& rStrm )
+{
+ const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo();
+
+ rStrm << sal_uInt16( 0 ) // X position of the window
+ << sal_uInt16( 0 ) // Y position of the window
+ << sal_uInt16( 0x4000 ) // width of the window
+ << sal_uInt16( 0x2000 ) // height of the window
+ << mnFlags
+ << rTabInfo.GetDisplayedXclTab()
+ << rTabInfo.GetFirstVisXclTab()
+ << rTabInfo.GetXclSelectedCount()
+ << mnTabBarSize;
+}
+
+// Sheet view settings records ================================================
+
+XclExpWindow2::XclExpWindow2( const XclExpRoot& rRoot,
+ const XclTabViewData& rData, sal_uInt32 nGridColorId ) :
+ XclExpRecord( EXC_ID_WINDOW2, (rRoot.GetBiff() == EXC_BIFF8) ? 18 : 10 ),
+ maGridColor( rData.maGridColor ),
+ mnGridColorId( nGridColorId ),
+ mnFlags( 0 ),
+ maFirstXclPos( rData.maFirstXclPos ),
+ mnNormalZoom( rData.mnNormalZoom ),
+ mnPageZoom( rData.mnPageZoom )
+{
+ ::set_flag( mnFlags, EXC_WIN2_SHOWFORMULAS, rData.mbShowFormulas );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWGRID, rData.mbShowGrid );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWHEADINGS, rData.mbShowHeadings );
+ ::set_flag( mnFlags, EXC_WIN2_FROZEN, rData.mbFrozenPanes );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWZEROS, rData.mbShowZeros );
+ ::set_flag( mnFlags, EXC_WIN2_DEFGRIDCOLOR, rData.mbDefGridColor );
+ ::set_flag( mnFlags, EXC_WIN2_MIRRORED, rData.mbMirrored );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWOUTLINE, rData.mbShowOutline );
+ ::set_flag( mnFlags, EXC_WIN2_FROZENNOSPLIT, rData.mbFrozenPanes );
+ ::set_flag( mnFlags, EXC_WIN2_SELECTED, rData.mbSelected );
+ ::set_flag( mnFlags, EXC_WIN2_DISPLAYED, rData.mbDisplayed );
+ ::set_flag( mnFlags, EXC_WIN2_PAGEBREAKMODE, rData.mbPageMode );
+}
+
+void XclExpWindow2::WriteBody( XclExpStream& rStrm )
+{
+ const XclExpRoot& rRoot = rStrm.GetRoot();
+
+ rStrm << mnFlags
+ << maFirstXclPos;
+
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ rStrm << maGridColor;
+ break;
+ case EXC_BIFF8:
+ rStrm << rRoot.GetPalette().GetColorIndex( mnGridColorId )
+ << sal_uInt16( 0 )
+ << mnPageZoom
+ << mnNormalZoom
+ << sal_uInt32( 0 );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+XclExpScl::XclExpScl( sal_uInt16 nZoom ) :
+ XclExpRecord( EXC_ID_SCL, 4 ),
+ mnNum( nZoom ),
+ mnDenom( 100 )
+{
+ Shorten( 2 );
+ Shorten( 5 );
+}
+
+void XclExpScl::Shorten( sal_uInt16 nFactor )
+{
+ while( (mnNum % nFactor == 0) && (mnDenom % nFactor == 0) )
+ {
+ mnNum = mnNum / nFactor;
+ mnDenom = mnDenom / nFactor;
+ }
+}
+
+void XclExpScl::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF4 );
+ rStrm << mnNum << mnDenom;
+}
+
+XclExpPane::XclExpPane( const XclTabViewData& rData ) :
+ XclExpRecord( EXC_ID_PANE, 10 ),
+ mnSplitX( rData.mnSplitX ),
+ mnSplitY( rData.mnSplitY ),
+ maSecondXclPos( rData.maSecondXclPos ),
+ mnActivePane( rData.mnActivePane ),
+ mbFrozenPanes( rData.mbFrozenPanes )
+{
+ OSL_ENSURE( rData.IsSplit(), "XclExpPane::XclExpPane - no PANE record for unsplit view" );
+}
+
+static const char* lcl_GetActivePane( sal_uInt8 nActivePane )
+{
+ switch( nActivePane )
+ {
+ case EXC_PANE_TOPLEFT: return "topLeft";
+ case EXC_PANE_TOPRIGHT: return "topRight";
+ case EXC_PANE_BOTTOMLEFT: return "bottomLeft";
+ case EXC_PANE_BOTTOMRIGHT: return "bottomRight";
+ }
+ return "**error: lcl_GetActivePane";
+}
+
+void XclExpPane::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElement( XML_pane,
+ XML_xSplit, OString::number(mnSplitX),
+ XML_ySplit, OString::number(mnSplitY),
+ XML_topLeftCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maSecondXclPos ).getStr(),
+ XML_activePane, lcl_GetActivePane( mnActivePane ),
+ XML_state, mbFrozenPanes ? "frozen" : "split" );
+}
+
+void XclExpPane::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnSplitX
+ << static_cast<sal_uInt16>( mnSplitY )
+ << maSecondXclPos
+ << mnActivePane;
+ if( rStrm.GetRoot().GetBiff() >= EXC_BIFF5 )
+ rStrm << sal_uInt8( 0 );
+}
+
+XclExpSelection::XclExpSelection( const XclTabViewData& rData, sal_uInt8 nPane ) :
+ XclExpRecord( EXC_ID_SELECTION, 15 ),
+ mnPane( nPane )
+{
+ if( const XclSelectionData* pSelData = rData.GetSelectionData( nPane ) )
+ maSelData = *pSelData;
+
+ // find the cursor position in the selection list (or add it)
+ XclRangeList& rXclSel = maSelData.maXclSelection;
+ auto aIt = std::find_if(rXclSel.begin(), rXclSel.end(),
+ [this](const XclRange& rRange) { return rRange.Contains(maSelData.maXclCursor); });
+ if (aIt != rXclSel.end())
+ {
+ maSelData.mnCursorIdx = static_cast< sal_uInt16 >( std::distance(rXclSel.begin(), aIt) );
+ }
+ else
+ {
+ /* Cursor cell not found in list? (e.g. inactive pane, or removed in
+ ConvertRangeList(), because Calc cursor on invalid pos)
+ -> insert the valid Excel cursor. */
+ maSelData.mnCursorIdx = static_cast< sal_uInt16 >( rXclSel.size() );
+ rXclSel.push_back( XclRange( maSelData.maXclCursor ) );
+ }
+}
+
+void XclExpSelection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElement( XML_selection,
+ XML_pane, lcl_GetActivePane( mnPane ),
+ XML_activeCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maSelData.maXclCursor ).getStr(),
+ XML_activeCellId, OString::number(maSelData.mnCursorIdx),
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSelData.maXclSelection) );
+}
+
+void XclExpSelection::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnPane // pane for this selection
+ << maSelData.maXclCursor // cell cursor
+ << maSelData.mnCursorIdx; // index to range containing cursor
+ maSelData.maXclSelection.Write( rStrm, false );
+}
+
+XclExpTabBgColor::XclExpTabBgColor( const XclTabViewData& rTabViewData ) :
+ XclExpRecord( EXC_ID_SHEETEXT, 18 ),
+ mrTabViewData( rTabViewData )
+{
+}
+//TODO Fix savexml...
+/*void XclExpTabBgColor::SaveXml( XclExpXmlStream& rStrm )
+{
+}*/
+
+void XclExpTabBgColor::WriteBody( XclExpStream& rStrm )
+{
+ if ( mrTabViewData.IsDefaultTabBgColor() )
+ return;
+ sal_uInt16 const rt = 0x0862; //rt
+ sal_uInt16 const grbitFrt = 0x0000; //grbit must be set to 0
+ sal_uInt32 unused = 0x00000000; //Use twice...
+ sal_uInt32 const cb = 0x00000014; // Record Size, may be larger in future...
+ sal_uInt16 const reserved = 0x0000; //trailing bits are 0
+ sal_uInt16 TabBgColorIndex;
+ XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ TabBgColorIndex = rPal.GetColorIndex(mrTabViewData.mnTabBgColorId);
+ if (TabBgColorIndex < 8 || TabBgColorIndex > 63 ) // only numbers 8 - 63 are valid numbers
+ TabBgColorIndex = 127; //Excel specs: 127 makes excel ignore tab color information.
+ rStrm << rt << grbitFrt << unused << unused << cb << TabBgColorIndex << reserved;
+}
+
+// Sheet view settings ========================================================
+
+namespace {
+
+/** Converts a Calc zoom factor into an Excel zoom factor. Returns 0 for a default zoom value. */
+sal_uInt16 lclGetXclZoom( tools::Long nScZoom, sal_uInt16 nDefXclZoom )
+{
+ sal_uInt16 nXclZoom = limit_cast< sal_uInt16 >( nScZoom, EXC_ZOOM_MIN, EXC_ZOOM_MAX );
+ return (nXclZoom == nDefXclZoom) ? 0 : nXclZoom;
+}
+
+} // namespace
+
+XclExpTabViewSettings::XclExpTabViewSettings( const XclExpRoot& rRoot, SCTAB nScTab ) :
+ XclExpRoot( rRoot ),
+ mnGridColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ),
+ mbHasTabSettings(false)
+{
+ // *** sheet flags ***
+
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ maData.mbSelected = rTabInfo.IsSelectedTab( nScTab );
+ maData.mbDisplayed = rTabInfo.IsDisplayedTab( nScTab );
+ maData.mbMirrored = rTabInfo.IsMirroredTab( nScTab );
+
+ const ScViewOptions& rViewOpt = GetDoc().GetViewOptions();
+ maData.mbShowFormulas = rViewOpt.GetOption( VOPT_FORMULAS );
+ maData.mbShowHeadings = rViewOpt.GetOption( VOPT_HEADER );
+ maData.mbShowZeros = rViewOpt.GetOption( VOPT_NULLVALS );
+ maData.mbShowOutline = rViewOpt.GetOption( VOPT_OUTLINER );
+
+ // *** sheet options: cursor, selection, splits, grid color, zoom ***
+
+ if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nScTab ) )
+ {
+ mbHasTabSettings = true;
+ const ScExtTabSettings& rTabSett = *pTabSett;
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+
+ // first visible cell in top-left pane
+ if( (rTabSett.maFirstVis.Col() >= 0) && (rTabSett.maFirstVis.Row() >= 0) )
+ maData.maFirstXclPos = rAddrConv.CreateValidAddress( rTabSett.maFirstVis, false );
+
+ // first visible cell in additional pane(s)
+ if( (rTabSett.maSecondVis.Col() >= 0) && (rTabSett.maSecondVis.Row() >= 0) )
+ maData.maSecondXclPos = rAddrConv.CreateValidAddress( rTabSett.maSecondVis, false );
+
+ // active pane
+ switch( rTabSett.meActivePane )
+ {
+ case SCEXT_PANE_TOPLEFT: maData.mnActivePane = EXC_PANE_TOPLEFT; break;
+ case SCEXT_PANE_TOPRIGHT: maData.mnActivePane = EXC_PANE_TOPRIGHT; break;
+ case SCEXT_PANE_BOTTOMLEFT: maData.mnActivePane = EXC_PANE_BOTTOMLEFT; break;
+ case SCEXT_PANE_BOTTOMRIGHT: maData.mnActivePane = EXC_PANE_BOTTOMRIGHT; break;
+ }
+
+ // freeze/split position
+ maData.mbFrozenPanes = rTabSett.mbFrozenPanes;
+ if( maData.mbFrozenPanes )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */
+ SCCOL nFreezeScCol = rTabSett.maFreezePos.Col();
+ if( (0 < nFreezeScCol) && (nFreezeScCol <= GetXclMaxPos().Col()) )
+ maData.mnSplitX = static_cast< sal_uInt16 >( nFreezeScCol ) - maData.maFirstXclPos.mnCol;
+ SCROW nFreezeScRow = rTabSett.maFreezePos.Row();
+ if( (0 < nFreezeScRow) && (nFreezeScRow <= GetXclMaxPos().Row()) )
+ maData.mnSplitY = static_cast< sal_uInt32 >( nFreezeScRow ) - maData.maFirstXclPos.mnRow;
+ // if both splits are left out (address overflow), remove the frozen flag
+ maData.mbFrozenPanes = maData.IsSplit();
+
+ // #i20671# frozen panes: mostright/mostbottom pane is active regardless of cursor position
+ if( maData.HasPane( EXC_PANE_BOTTOMRIGHT ) )
+ maData.mnActivePane = EXC_PANE_BOTTOMRIGHT;
+ else if( maData.HasPane( EXC_PANE_TOPRIGHT ) )
+ maData.mnActivePane = EXC_PANE_TOPRIGHT;
+ else if( maData.HasPane( EXC_PANE_BOTTOMLEFT ) )
+ maData.mnActivePane = EXC_PANE_BOTTOMLEFT;
+ }
+ else
+ {
+ // split window: position is in twips
+ maData.mnSplitX = static_cast<sal_uInt16>(rTabSett.maSplitPos.X());
+ maData.mnSplitY = static_cast<sal_uInt32>(rTabSett.maSplitPos.Y());
+ }
+
+ // selection
+ CreateSelectionData( EXC_PANE_TOPLEFT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_TOPRIGHT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_BOTTOMLEFT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_BOTTOMRIGHT, rTabSett.maCursor, rTabSett.maSelection );
+
+ // grid color
+ const Color& rGridColor = rTabSett.maGridColor;
+ maData.mbDefGridColor = rGridColor == COL_AUTO;
+ if( !maData.mbDefGridColor )
+ {
+ if( GetBiff() == EXC_BIFF8 )
+ mnGridColorId = GetPalette().InsertColor( rGridColor, EXC_COLOR_GRID );
+ else
+ maData.maGridColor = rGridColor;
+ }
+ maData.mbShowGrid = rTabSett.mbShowGrid;
+
+ // view mode and zoom
+ maData.mbPageMode = (GetBiff() == EXC_BIFF8) && rTabSett.mbPageMode;
+ maData.mnNormalZoom = lclGetXclZoom( rTabSett.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF );
+ maData.mnPageZoom = lclGetXclZoom( rTabSett.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF );
+ maData.mnCurrentZoom = maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom;
+ }
+
+ // Tab Bg Color
+ if ( GetBiff() == EXC_BIFF8 && !GetDoc().IsDefaultTabBgColor(nScTab) )
+ {
+ XclExpPalette& rPal = GetPalette();
+ maData.maTabBgColor = GetDoc().GetTabBgColor(nScTab);
+ maData.mnTabBgColorId = rPal.InsertColor(maData.maTabBgColor, EXC_COLOR_TABBG, EXC_COLOR_NOTABBG );
+ }
+}
+
+void XclExpTabViewSettings::Save( XclExpStream& rStrm )
+{
+ WriteWindow2( rStrm );
+ WriteScl( rStrm );
+ WritePane( rStrm );
+ WriteSelection( rStrm, EXC_PANE_TOPLEFT );
+ WriteSelection( rStrm, EXC_PANE_TOPRIGHT );
+ WriteSelection( rStrm, EXC_PANE_BOTTOMLEFT );
+ WriteSelection( rStrm, EXC_PANE_BOTTOMRIGHT );
+ WriteTabBgColor( rStrm );
+}
+
+static void lcl_WriteSelection( XclExpXmlStream& rStrm, const XclTabViewData& rData, sal_uInt8 nPane )
+{
+ if( rData.HasPane( nPane ) )
+ XclExpSelection( rData, nPane ).SaveXml( rStrm );
+}
+
+static OString lcl_GetZoom( sal_uInt16 nZoom )
+{
+ if( nZoom )
+ return OString::number( nZoom );
+ return "100"_ostr;
+}
+
+void XclExpTabViewSettings::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_sheetViews);
+
+ // handle missing viewdata at embedded XLSX OLE objects
+ if( !mbHasTabSettings && maData.mbSelected )
+ {
+ SCCOL nPosLeft = rStrm.GetRoot().GetDoc().GetPosLeft();
+ SCROW nPosTop = rStrm.GetRoot().GetDoc().GetPosTop();
+ if (nPosLeft > 0 || nPosTop > 0)
+ {
+ ScAddress aLeftTop(nPosLeft, nPosTop, 0);
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ maData.maFirstXclPos = rAddrConv.CreateValidAddress( aLeftTop, false );
+ }
+ }
+
+ rWorksheet->startElement( XML_sheetView,
+ // OOXTODO: XML_windowProtection,
+ XML_showFormulas, ToPsz( maData.mbShowFormulas ),
+ XML_showGridLines, ToPsz( maData.mbShowGrid ),
+ XML_showRowColHeaders, ToPsz( maData.mbShowHeadings ),
+ XML_showZeros, ToPsz( maData.mbShowZeros ),
+ XML_rightToLeft, ToPsz( maData.mbMirrored ),
+ XML_tabSelected, ToPsz( maData.mbSelected ),
+ // OOXTODO: XML_showRuler,
+ XML_showOutlineSymbols, ToPsz( maData.mbShowOutline ),
+ XML_defaultGridColor, mnGridColorId == XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ? "true" : "false",
+ // OOXTODO: XML_showWhiteSpace,
+ XML_view, maData.mbPageMode ? "pageBreakPreview" : "normal", // OOXTODO: pageLayout
+ XML_topLeftCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maData.maFirstXclPos ).getStr(),
+ XML_colorId, OString::number(rStrm.GetRoot().GetPalette().GetColorIndex(mnGridColorId)),
+ XML_zoomScale, lcl_GetZoom(maData.mnCurrentZoom),
+ XML_zoomScaleNormal, lcl_GetZoom(maData.mnNormalZoom),
+ // OOXTODO: XML_zoomScaleSheetLayoutView,
+ XML_zoomScalePageLayoutView, lcl_GetZoom(maData.mnPageZoom),
+ XML_workbookViewId, "0" // OOXTODO? 0-based index of document(xl/workbook.xml)/workbook/bookviews/workbookView
+ // should always be 0, as we only generate 1 such element.
+ );
+ if( maData.IsSplit() )
+ {
+ XclExpPane aPane( maData );
+ aPane.SaveXml( rStrm );
+ }
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPLEFT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPRIGHT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMLEFT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMRIGHT );
+ rWorksheet->endElement( XML_sheetView );
+ // OOXTODO: XML_extLst
+ rWorksheet->endElement( XML_sheetViews );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpTabViewSettings::CreateSelectionData( sal_uInt8 nPane,
+ const ScAddress& rCursor, const ScRangeList& rSelection )
+{
+ if( !maData.HasPane( nPane ) )
+ return;
+
+ XclSelectionData& rSelData = maData.CreateSelectionData( nPane );
+
+ // first step: use top-left visible cell as cursor
+ rSelData.maXclCursor.mnCol = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_BOTTOMLEFT)) ?
+ maData.maFirstXclPos.mnCol : maData.maSecondXclPos.mnCol;
+ rSelData.maXclCursor.mnRow = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_TOPRIGHT)) ?
+ maData.maFirstXclPos.mnRow : maData.maSecondXclPos.mnRow;
+
+ // second step, active pane: create actual selection data with current cursor position
+ if( nPane == maData.mnActivePane )
+ {
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ // cursor position (keep top-left pane position from above, if rCursor is invalid)
+ if( (rCursor.Col() >= 0) && (rCursor.Row() >= 0) )
+ rSelData.maXclCursor = rAddrConv.CreateValidAddress( rCursor, false );
+ // selection
+ rAddrConv.ConvertRangeList( rSelData.maXclSelection, rSelection, false );
+ }
+}
+
+void XclExpTabViewSettings::WriteWindow2( XclExpStream& rStrm ) const
+{
+// #i43553# GCC 3.3 parse error
+// XclExpWindow2( GetRoot(), maData, mnGridColorId ).Save( rStrm );
+ XclExpWindow2 aWindow2( GetRoot(), maData, mnGridColorId );
+ aWindow2.Save( rStrm );
+}
+
+void XclExpTabViewSettings::WriteScl( XclExpStream& rStrm ) const
+{
+ if( maData.mnCurrentZoom != 0 )
+ XclExpScl( maData.mnCurrentZoom ).Save( rStrm );
+}
+
+void XclExpTabViewSettings::WritePane( XclExpStream& rStrm ) const
+{
+ if( maData.IsSplit() )
+// #i43553# GCC 3.3 parse error
+// XclExpPane( GetRoot(), maData ).Save( rStrm );
+ {
+ XclExpPane aPane( maData );
+ aPane.Save( rStrm );
+ }
+}
+
+void XclExpTabViewSettings::WriteSelection( XclExpStream& rStrm, sal_uInt8 nPane ) const
+{
+ if( maData.HasPane( nPane ) )
+ XclExpSelection( maData, nPane ).Save( rStrm );
+}
+
+void XclExpTabViewSettings::WriteTabBgColor( XclExpStream& rStrm ) const
+{
+ if ( !maData.IsDefaultTabBgColor() )
+ XclExpTabBgColor( maData ).Save( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xichart.cxx b/sc/source/filter/excel/xichart.cxx
new file mode 100644
index 0000000000..534e252b74
--- /dev/null
+++ b/sc/source/filter/excel/xichart.cxx
@@ -0,0 +1,4423 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xichart.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
+#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
+#include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart/TimeInterval.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart2/LinearRegressionCurve.hpp>
+#include <com/sun/star/chart2/ExponentialRegressionCurve.hpp>
+#include <com/sun/star/chart2/LogarithmicRegressionCurve.hpp>
+#include <com/sun/star/chart2/PotentialRegressionCurve.hpp>
+#include <com/sun/star/chart2/PolynomialRegressionCurve.hpp>
+#include <com/sun/star/chart2/MovingAverageRegressionCurve.hpp>
+#include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
+#include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
+#include <com/sun/star/chart2/FormattedString.hpp>
+#include <com/sun/star/chart2/LogarithmicScaling.hpp>
+#include <com/sun/star/chart2/LinearScaling.hpp>
+#include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
+#include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XTitled.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/CurveStyle.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/TickmarkStyle.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart2/data/XDataSink.hpp>
+#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoapi.hxx>
+#include <sal/log.hxx>
+#include <tools/helpers.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <tokenarray.hxx>
+#include <compiler.hxx>
+#include <reftokenhelper.hxx>
+#include <chartlis.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xistream.hxx>
+#include <xiformula.hxx>
+#include <xistyle.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+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::Exception;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::util::XNumberFormatsSupplier;
+using ::com::sun::star::drawing::XDrawPage;
+using ::com::sun::star::drawing::XDrawPageSupplier;
+using ::com::sun::star::drawing::XShape;
+
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::chart2::data::XDataProvider;
+using ::com::sun::star::chart2::data::XDataReceiver;
+using ::com::sun::star::chart2::data::XDataSequence;
+using ::com::sun::star::chart2::data::XDataSink;
+using ::com::sun::star::chart2::data::XLabeledDataSequence;
+using ::com::sun::star::chart2::data::LabeledDataSequence;
+
+using ::formula::FormulaToken;
+using ::formula::FormulaTokenArrayPlainIterator;
+using ::std::unique_ptr;
+
+namespace cssc = ::com::sun::star::chart;
+namespace cssc2 = ::com::sun::star::chart2;
+
+// Helpers ====================================================================
+
+namespace {
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclChRectangle& rRect )
+{
+ rRect.mnX = rStrm.ReadInt32();
+ rRect.mnY = rStrm.ReadInt32();
+ rRect.mnWidth = rStrm.ReadInt32();
+ rRect.mnHeight = rStrm.ReadInt32();
+ return rStrm;
+}
+
+void lclSetValueOrClearAny( Any& rAny, double fValue, bool bClear )
+{
+ if( bClear )
+ rAny.clear();
+ else
+ rAny <<= fValue;
+}
+
+void lclSetExpValueOrClearAny( Any& rAny, double fValue, bool bLogScale, bool bClear )
+{
+ if( !bClear && bLogScale )
+ fValue = pow( 10.0, fValue );
+ lclSetValueOrClearAny( rAny, fValue, bClear );
+}
+
+double lclGetSerialDay( const XclImpRoot& rRoot, sal_uInt16 nValue, sal_uInt16 nTimeUnit )
+{
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS:
+ return nValue;
+ case EXC_CHDATERANGE_MONTHS:
+ return rRoot.GetDoubleFromDateTime( Date( 1, static_cast< sal_uInt16 >( 1 + nValue % 12 ), static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue / 12 ) ) );
+ case EXC_CHDATERANGE_YEARS:
+ return rRoot.GetDoubleFromDateTime( Date( 1, 1, static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue ) ) );
+ default:
+ OSL_ENSURE( false, "lclGetSerialDay - unexpected time unit" );
+ }
+ return nValue;
+}
+
+void lclConvertTimeValue( const XclImpRoot& rRoot, Any& rAny, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
+{
+ if( bAuto )
+ rAny.clear();
+ else
+ rAny <<= lclGetSerialDay( rRoot, nValue, nTimeUnit );
+}
+
+sal_Int32 lclGetApiTimeUnit( sal_uInt16 nTimeUnit )
+{
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS: return cssc::TimeUnit::DAY;
+ case EXC_CHDATERANGE_MONTHS: return cssc::TimeUnit::MONTH;
+ case EXC_CHDATERANGE_YEARS: return cssc::TimeUnit::YEAR;
+ default: OSL_ENSURE( false, "lclGetApiTimeUnit - unexpected time unit" );
+ }
+ return cssc::TimeUnit::DAY;
+}
+
+void lclConvertTimeInterval( Any& rInterval, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
+{
+ if( bAuto || (nValue == 0) )
+ rInterval.clear();
+ else
+ rInterval <<= cssc::TimeInterval( nValue, lclGetApiTimeUnit( nTimeUnit ) );
+}
+
+} // namespace
+
+// Common =====================================================================
+
+/** Stores global data needed in various classes of the Chart import filter. */
+struct XclImpChRootData : public XclChRootData
+{
+ XclImpChChart& mrChartData; /// The chart data object.
+
+ explicit XclImpChRootData( XclImpChChart& rChartData ) : mrChartData( rChartData ) {}
+};
+
+XclImpChRoot::XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData ) :
+ XclImpRoot( rRoot ),
+ mxChData( std::make_shared<XclImpChRootData>( rChartData ) )
+{
+}
+
+XclImpChRoot::~XclImpChRoot()
+{
+}
+
+XclImpChChart& XclImpChRoot::GetChartData() const
+{
+ return mxChData->mrChartData;
+}
+
+const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
+}
+
+const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( sal_uInt16 nRecId ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfoFromRecId( nRecId );
+}
+
+const XclChFormatInfo& XclImpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
+}
+
+Color XclImpChRoot::GetFontAutoColor() const
+{
+ return GetPalette().GetColor( EXC_COLOR_CHWINDOWTEXT );
+}
+
+Color XclImpChRoot::GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const
+{
+ return GetPalette().GetColor( XclChartHelper::GetSeriesLineAutoColorIdx( nFormatIdx ) );
+}
+
+Color XclImpChRoot::GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const
+{
+ const XclImpPalette& rPal = GetPalette();
+ Color aColor = rPal.GetColor( XclChartHelper::GetSeriesFillAutoColorIdx( nFormatIdx ) );
+ sal_uInt8 nTrans = XclChartHelper::GetSeriesFillAutoTransp( nFormatIdx );
+ return ScfTools::GetMixedColor( aColor, rPal.GetColor( EXC_COLOR_CHWINDOWBACK ), nTrans );
+}
+
+void XclImpChRoot::InitConversion( const Reference<XChartDocument>& xChartDoc, const tools::Rectangle& rChartRect ) const
+{
+ // create formatting object tables
+ mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
+
+ // lock the model to suppress any internal updates
+ if( xChartDoc.is() )
+ xChartDoc->lockControllers();
+
+ ScDocShell* pDocShell = GetDocShell();
+ Reference< XDataReceiver > xDataRec( xChartDoc, UNO_QUERY );
+ if( pDocShell && xDataRec.is() )
+ {
+ // create and register a data provider
+ Reference< XDataProvider > xDataProv(
+ ScfApiHelper::CreateInstance( pDocShell, SERVICE_CHART2_DATAPROVIDER ), UNO_QUERY );
+ if( xDataProv.is() )
+ xDataRec->attachDataProvider( xDataProv );
+ // attach the number formatter
+ Reference< XNumberFormatsSupplier > xNumFmtSupp( static_cast<cppu::OWeakObject*>(pDocShell->GetModel()), UNO_QUERY );
+ if( xNumFmtSupp.is() )
+ xDataRec->attachNumberFormatsSupplier( xNumFmtSupp );
+ }
+}
+
+void XclImpChRoot::FinishConversion( XclImpDffConverter& rDffConv ) const
+{
+ rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
+ // unlock the model
+ Reference< XModel > xModel = mxChData->mxChartDoc;
+ if( xModel.is() )
+ xModel->unlockControllers();
+ rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
+
+ mxChData->FinishConversion();
+}
+
+Reference< XDataProvider > XclImpChRoot::GetDataProvider() const
+{
+ return mxChData->mxChartDoc->getDataProvider();
+}
+
+Reference< XShape > XclImpChRoot::GetTitleShape( const XclChTextKey& rTitleKey ) const
+{
+ return mxChData->GetTitleShape( rTitleKey );
+}
+
+sal_Int32 XclImpChRoot::CalcHmmFromChartX( sal_Int32 nPosX ) const
+{
+ return static_cast< sal_Int32 >( mxChData->mfUnitSizeX * nPosX + mxChData->mnBorderGapX + 0.5 );
+}
+
+sal_Int32 XclImpChRoot::CalcHmmFromChartY( sal_Int32 nPosY ) const
+{
+ return static_cast< sal_Int32 >( mxChData->mfUnitSizeY * nPosY + mxChData->mnBorderGapY + 0.5 );
+}
+
+css::awt::Rectangle XclImpChRoot::CalcHmmFromChartRect( const XclChRectangle& rRect ) const
+{
+ return css::awt::Rectangle(
+ CalcHmmFromChartX( rRect.mnX ),
+ CalcHmmFromChartY( rRect.mnY ),
+ CalcHmmFromChartX( rRect.mnWidth ),
+ CalcHmmFromChartY( rRect.mnHeight ) );
+}
+
+double XclImpChRoot::CalcRelativeFromHmmX( sal_Int32 nPosX ) const
+{
+ const tools::Long nWidth = mxChData->maChartRect.GetWidth();
+ if (!nWidth)
+ throw o3tl::divide_by_zero();
+ return static_cast<double>(nPosX) / nWidth;
+}
+
+double XclImpChRoot::CalcRelativeFromHmmY( sal_Int32 nPosY ) const
+{
+ const tools::Long nHeight = mxChData->maChartRect.GetHeight();
+ if (!nHeight)
+ throw o3tl::divide_by_zero();
+ return static_cast<double >(nPosY) / nHeight;
+}
+
+double XclImpChRoot::CalcRelativeFromChartX( sal_Int32 nPosX ) const
+{
+ return CalcRelativeFromHmmX( CalcHmmFromChartX( nPosX ) );
+}
+
+double XclImpChRoot::CalcRelativeFromChartY( sal_Int32 nPosY ) const
+{
+ return CalcRelativeFromHmmY( CalcHmmFromChartY( nPosY ) );
+}
+
+void XclImpChRoot::ConvertLineFormat( ScfPropertySet& rPropSet,
+ const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteLineProperties(
+ rPropSet, *mxChData->mxLineDashTable, rLineFmt, ePropMode );
+}
+
+void XclImpChRoot::ConvertAreaFormat( ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteAreaProperties( rPropSet, rAreaFmt, ePropMode );
+}
+
+void XclImpChRoot::ConvertEscherFormat( ScfPropertySet& rPropSet,
+ const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteEscherProperties( rPropSet,
+ *mxChData->mxGradientTable, *mxChData->mxBitmapTable,
+ rEscherFmt, pPicFmt, nDffFillType, ePropMode );
+}
+
+void XclImpChRoot::ConvertFont( ScfPropertySet& rPropSet,
+ sal_uInt16 nFontIdx, const Color* pFontColor ) const
+{
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CHART, nFontIdx, pFontColor );
+}
+
+void XclImpChRoot::ConvertPieRotation( ScfPropertySet& rPropSet, sal_uInt16 nAngle )
+{
+ sal_Int32 nApiRot = (450 - (nAngle % 360)) % 360;
+ rPropSet.SetProperty( EXC_CHPROP_STARTINGANGLE, nApiRot );
+}
+
+XclImpChGroupBase::~XclImpChGroupBase()
+{
+}
+
+void XclImpChGroupBase::ReadRecordGroup( XclImpStream& rStrm )
+{
+ // read contents of the header record
+ ReadHeaderRecord( rStrm );
+
+ // only read sub records, if the next record is a CHBEGIN
+ if( rStrm.GetNextRecId() != EXC_ID_CHBEGIN )
+ return;
+
+ // read the CHBEGIN record, may be used for special initial processing
+ rStrm.StartNextRecord();
+ ReadSubRecord( rStrm );
+
+ // read the nested records
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_CHEND;
+ // skip unsupported nested blocks
+ if( nRecId == EXC_ID_CHBEGIN )
+ SkipBlock( rStrm );
+ else
+ ReadSubRecord( rStrm );
+ }
+ /* Returns with current CHEND record or unchanged stream, if no record
+ group present. In every case another call to StartNextRecord() will go
+ to next record of interest. */
+}
+
+void XclImpChGroupBase::SkipBlock( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecId() == EXC_ID_CHBEGIN, "XclImpChGroupBase::SkipBlock - no CHBEGIN record" );
+ // do nothing if current record is not CHBEGIN
+ bool bLoop = rStrm.GetRecId() == EXC_ID_CHBEGIN;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_CHEND;
+ // skip nested record groups
+ if( nRecId == EXC_ID_CHBEGIN )
+ SkipBlock( rStrm );
+ }
+}
+
+// Frame formatting ===========================================================
+
+void XclImpChFramePos::ReadChFramePos( XclImpStream& rStrm )
+{
+ maData.mnTLMode = rStrm.ReaduInt16();
+ maData.mnBRMode = rStrm.ReaduInt16();
+ /* According to the spec, the upper 16 bits of all members in the
+ rectangle are unused and may contain garbage. */
+ maData.maRect.mnX = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnY = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnWidth = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnHeight = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+}
+
+void XclImpChLineFormat::ReadChLineFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maColor;
+ maData.mnPattern = rStrm.ReaduInt16();
+ maData.mnWeight = rStrm.ReadInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ // BIFF8: index into palette used instead of RGB data
+ maData.maColor = rRoot.GetPalette().GetColor( rStrm.ReaduInt16() );
+}
+
+void XclImpChLineFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( IsAuto() )
+ {
+ XclChLineFormat aLineFmt;
+ aLineFmt.maColor = (eObjType == EXC_CHOBJTYPE_LINEARSERIES) ?
+ rRoot.GetSeriesLineAutoColor( nFormatIdx ) :
+ rRoot.GetPalette().GetColor( rFmtInfo.mnAutoLineColorIdx );
+ aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ aLineFmt.mnWeight = rFmtInfo.mnAutoLineWeight;
+ rRoot.ConvertLineFormat( rPropSet, aLineFmt, rFmtInfo.mePropMode );
+ }
+ else
+ {
+ rRoot.ConvertLineFormat( rPropSet, maData, rFmtInfo.mePropMode );
+ }
+}
+
+void XclImpChAreaFormat::ReadChAreaFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maPattColor >> maData.maBackColor;
+ maData.mnPattern = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ const XclImpPalette& rPal = rRoot.GetPalette();
+ maData.maPattColor = rPal.GetColor( rStrm.ReaduInt16() );
+ maData.maBackColor = rPal.GetColor( rStrm.ReaduInt16());
+ }
+}
+
+void XclImpChAreaFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( IsAuto() )
+ {
+ XclChAreaFormat aAreaFmt;
+ aAreaFmt.maPattColor = (eObjType == EXC_CHOBJTYPE_FILLEDSERIES) ?
+ rRoot.GetSeriesFillAutoColor( nFormatIdx ) :
+ rRoot.GetPalette().GetColor( rFmtInfo.mnAutoPattColorIdx );
+ aAreaFmt.mnPattern = EXC_PATT_SOLID;
+ rRoot.ConvertAreaFormat( rPropSet, aAreaFmt, rFmtInfo.mePropMode );
+ }
+ else
+ {
+ rRoot.ConvertAreaFormat( rPropSet, maData, rFmtInfo.mePropMode );
+ }
+}
+
+XclImpChEscherFormat::XclImpChEscherFormat( const XclImpRoot& rRoot ) :
+ mnDffFillType( mso_fillSolid )
+{
+ maData.mxItemSet =
+ std::make_shared<SfxItemSet>( rRoot.GetDoc().GetDrawLayer()->GetItemPool() );
+}
+
+void XclImpChEscherFormat::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ // read from stream - CHESCHERFORMAT uses own ID for record continuation
+ XclImpDffPropSet aPropSet( rStrm.GetRoot() );
+ rStrm.ResetRecord( true, rStrm.GetRecId() );
+ rStrm >> aPropSet;
+ // get the data
+ aPropSet.FillToItemSet( *maData.mxItemSet );
+ // get fill type from DFF property set
+ mnDffFillType = aPropSet.GetPropertyValue( DFF_Prop_fillType );
+}
+
+void XclImpChEscherFormat::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHPICFORMAT:
+ maPicFmt.mnBmpMode = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ maPicFmt.mnFlags = rStrm.ReaduInt16();
+ maPicFmt.mfScale = rStrm.ReadDouble();
+ break;
+ }
+}
+
+void XclImpChEscherFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, bool bUsePicFmt ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ rRoot.ConvertEscherFormat( rPropSet, maData, bUsePicFmt ? &maPicFmt : nullptr, mnDffFillType, rFmtInfo.mePropMode );
+}
+
+XclImpChFrameBase::XclImpChFrameBase( const XclChFormatInfo& rFmtInfo )
+{
+ if( !rFmtInfo.mbCreateDefFrame )
+ return;
+
+ switch( rFmtInfo.meDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ mxLineFmt = new XclImpChLineFormat();
+ if( rFmtInfo.mbIsFrame )
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ {
+ XclChLineFormat aLineFmt;
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
+ aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ mxLineFmt = new XclImpChLineFormat( aLineFmt );
+ if( rFmtInfo.mbIsFrame )
+ {
+ XclChAreaFormat aAreaFmt;
+ ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
+ aAreaFmt.mnPattern = EXC_PATT_NONE;
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpChFrameBase::XclImpChFrameBase - unknown frame type" );
+ }
+}
+
+void XclImpChFrameBase::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHLINEFORMAT:
+ mxLineFmt = new XclImpChLineFormat();
+ mxLineFmt->ReadChLineFormat( rStrm );
+ break;
+ case EXC_ID_CHAREAFORMAT:
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ mxAreaFmt->ReadChAreaFormat( rStrm );
+ break;
+ case EXC_ID_CHESCHERFORMAT:
+ mxEscherFmt = std::make_shared<XclImpChEscherFormat>( rStrm.GetRoot() );
+ mxEscherFmt->ReadRecordGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChFrameBase::ConvertLineBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ if( mxLineFmt )
+ mxLineFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
+}
+
+void XclImpChFrameBase::ConvertAreaBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
+{
+ if( rRoot.GetFormatInfo( eObjType ).mbIsFrame )
+ {
+ // CHESCHERFORMAT overrides CHAREAFORMAT (even if it is auto)
+ if( mxEscherFmt )
+ mxEscherFmt->Convert( rRoot, rPropSet, eObjType, bUsePicFmt );
+ else if( mxAreaFmt )
+ mxAreaFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
+ }
+}
+
+void XclImpChFrameBase::ConvertFrameBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
+{
+ ConvertLineBase( rRoot, rPropSet, eObjType, nFormatIdx );
+ ConvertAreaBase( rRoot, rPropSet, eObjType, nFormatIdx, bUsePicFmt );
+}
+
+XclImpChFrame::XclImpChFrame( const XclImpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclImpChFrameBase( rRoot.GetFormatInfo( eObjType ) ),
+ XclImpChRoot( rRoot ),
+ meObjType( eObjType )
+{
+}
+
+void XclImpChFrame::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnFormat = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChFrame::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ const XclImpPalette& rPal = GetPalette();
+
+ if( rLineData.IsVisible() && (!mxLineFmt || !mxLineFmt->HasLine()) )
+ {
+ // line formatting
+ XclChLineFormat aLineFmt;
+ aLineFmt.maColor = rPal.GetColor( rLineData.mnColorIdx );
+ switch( rLineData.mnStyle )
+ {
+ case EXC_OBJ_LINE_SOLID: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; break;
+ case EXC_OBJ_LINE_DASH: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASH; break;
+ case EXC_OBJ_LINE_DOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DOT; break;
+ case EXC_OBJ_LINE_DASHDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOT; break;
+ case EXC_OBJ_LINE_DASHDOTDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOTDOT; break;
+ case EXC_OBJ_LINE_MEDTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS; break;
+ case EXC_OBJ_LINE_DARKTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS; break;
+ case EXC_OBJ_LINE_LIGHTTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS; break;
+ case EXC_OBJ_LINE_NONE: aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; break;
+ default: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ }
+ switch( rLineData.mnWidth )
+ {
+ case EXC_OBJ_LINE_HAIR: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; break;
+ case EXC_OBJ_LINE_THIN: aLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; break;
+ case EXC_OBJ_LINE_MEDIUM: aLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE; break;
+ case EXC_OBJ_LINE_THICK: aLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE; break;
+ default: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
+ }
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, rLineData.IsAuto() );
+ mxLineFmt = new XclImpChLineFormat( aLineFmt );
+ }
+
+ if( rFillData.IsFilled() && (!mxAreaFmt || !mxAreaFmt->HasArea()) && !mxEscherFmt )
+ {
+ // area formatting
+ XclChAreaFormat aAreaFmt;
+ aAreaFmt.maPattColor = rPal.GetColor( rFillData.mnPattColorIdx );
+ aAreaFmt.maBackColor = rPal.GetColor( rFillData.mnBackColorIdx );
+ aAreaFmt.mnPattern = rFillData.mnPattern;
+ ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, rFillData.IsAuto() );
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
+ }
+}
+
+void XclImpChFrame::Convert( ScfPropertySet& rPropSet, bool bUsePicFmt ) const
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType, EXC_CHDATAFORMAT_UNKNOWN, bUsePicFmt );
+}
+
+// Source links ===============================================================
+
+namespace {
+
+/** Creates a labeled data sequence object, adds link for series title if present. */
+Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
+ const XclImpChSourceLinkRef& xValueLink, const OUString& rValueRole,
+ const XclImpChSourceLink* pTitleLink = nullptr )
+{
+ // create data sequence for values and title
+ Reference< XDataSequence > xValueSeq;
+ if( xValueLink )
+ xValueSeq = xValueLink->CreateDataSequence( rValueRole );
+ Reference< XDataSequence > xTitleSeq;
+ if( pTitleLink )
+ xTitleSeq = pTitleLink->CreateDataSequence( EXC_CHPROP_ROLE_LABEL );
+
+ // create the labeled data sequence, if values or title are present
+ Reference< XLabeledDataSequence > xLabeledSeq;
+ if( xValueSeq.is() || xTitleSeq.is() )
+ xLabeledSeq = LabeledDataSequence::create(comphelper::getProcessComponentContext());
+ if( xLabeledSeq.is() )
+ {
+ if( xValueSeq.is() )
+ xLabeledSeq->setValues( xValueSeq );
+ if( xTitleSeq.is() )
+ xLabeledSeq->setLabel( xTitleSeq );
+ }
+ return xLabeledSeq;
+}
+
+} // namespace
+
+XclImpChSourceLink::XclImpChSourceLink( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+XclImpChSourceLink::~XclImpChSourceLink()
+{
+}
+
+void XclImpChSourceLink::ReadChSourceLink( XclImpStream& rStrm )
+{
+ maData.mnDestType = rStrm.ReaduInt8();
+ maData.mnLinkType = rStrm.ReaduInt8();
+ maData.mnFlags = rStrm.ReaduInt16();
+ maData.mnNumFmtIdx = rStrm.ReaduInt16();
+
+ mxTokenArray.reset();
+ if( GetLinkType() == EXC_CHSRCLINK_WORKSHEET )
+ {
+ // read token array
+ XclTokenArray aXclTokArr;
+ rStrm >> aXclTokArr;
+
+ // convert BIFF formula tokens to Calc token array
+ if( std::unique_ptr<ScTokenArray> pTokens = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aXclTokArr ) )
+ mxTokenArray = std::move( pTokens );
+ }
+
+ // try to read a following CHSTRING record
+ if( (rStrm.GetNextRecId() == EXC_ID_CHSTRING) && rStrm.StartNextRecord() )
+ {
+ mxString = std::make_shared<XclImpString>();
+ rStrm.Ignore( 2 );
+ mxString->Read( rStrm, XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+ }
+}
+
+void XclImpChSourceLink::SetString( const OUString& rString )
+{
+ if( !mxString )
+ mxString = std::make_shared<XclImpString>();
+ mxString->SetText( rString );
+}
+
+void XclImpChSourceLink::SetTextFormats( XclFormatRunVec&& rFormats )
+{
+ if( mxString )
+ mxString->SetFormats( std::move(rFormats) );
+}
+
+sal_uInt16 XclImpChSourceLink::GetCellCount() const
+{
+ sal_uInt32 nCellCount = 0;
+ if( mxTokenArray )
+ {
+ FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
+ for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
+ {
+ switch( pToken->GetType() )
+ {
+ case ::formula::svSingleRef:
+ case ::formula::svExternalSingleRef:
+ // single cell
+ ++nCellCount;
+ break;
+ case ::formula::svDoubleRef:
+ case ::formula::svExternalDoubleRef:
+ {
+ // cell range
+ const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
+ ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
+ ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
+ sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
+ sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
+ sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
+ nCellCount += nCols * nRows * nTabs;
+ }
+ break;
+ default: ;
+ }
+ }
+ }
+ return limit_cast< sal_uInt16 >( nCellCount );
+}
+
+void XclImpChSourceLink::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
+{
+ bool bLinkToSource = ::get_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
+ sal_uInt32 nScNumFmt = bLinkToSource ? GetNumFmtBuffer().GetScFormat( maData.mnNumFmtIdx ) : NUMBERFORMAT_ENTRY_NOT_FOUND;
+ OUString aPropName = bPercent ? EXC_CHPROP_PERCENTAGENUMFMT : EXC_CHPROP_NUMBERFORMAT;
+ if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ rPropSet.SetProperty( aPropName, static_cast< sal_Int32 >( nScNumFmt ) );
+ else
+ // restore 'link to source' at data point (series may contain manual number format)
+ rPropSet.SetAnyProperty( aPropName, Any() );
+}
+
+Reference< XDataSequence > XclImpChSourceLink::CreateDataSequence( const OUString& rRole ) const
+{
+ Reference< XDataSequence > xDataSeq;
+ Reference< XDataProvider > xDataProv = GetDataProvider();
+ if( xDataProv.is() )
+ {
+ if ( mxTokenArray )
+ {
+ ScCompiler aComp( GetDoc(), ScAddress(), *mxTokenArray, GetDoc().GetGrammar() );
+ OUStringBuffer aRangeRep;
+ aComp.CreateStringFromTokenArray( aRangeRep );
+ try
+ {
+ xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aRangeRep.makeStringAndClear() );
+ // set sequence role
+ ScfPropertySet aSeqProp( xDataSeq );
+ aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
+ }
+ catch( Exception& )
+ {
+ // OSL_FAIL( "XclImpChSourceLink::CreateDataSequence - cannot create data sequence" );
+ }
+ }
+ else if( rRole == EXC_CHPROP_ROLE_LABEL && mxString && !mxString->GetText().isEmpty() )
+ {
+ try
+ {
+ OUString aString("\"");
+ xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aString + mxString->GetText() + aString );
+ // set sequence role
+ ScfPropertySet aSeqProp( xDataSeq );
+ aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
+ }
+ catch( Exception& ) { }
+ }
+ }
+ return xDataSeq;
+}
+
+Sequence< Reference< XFormattedString > > XclImpChSourceLink::CreateStringSequence(
+ const XclImpChRoot& rRoot, sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const
+{
+ ::std::vector< Reference< XFormattedString > > aStringVec;
+ if( mxString )
+ {
+ for( XclImpStringIterator aIt( *mxString ); aIt.Is(); ++aIt )
+ {
+ Reference< css::chart2::XFormattedString2 > xFmtStr = css::chart2::FormattedString::create( comphelper::getProcessComponentContext() );
+ // set text data
+ xFmtStr->setString( aIt.GetPortionText() );
+
+ // set font formatting and font color
+ ScfPropertySet aStringProp( xFmtStr );
+ sal_uInt16 nFontIdx = aIt.GetPortionFont();
+ if( (nFontIdx == EXC_FONT_NOTFOUND) && (aIt.GetPortionIndex() == 0) )
+ // leading unformatted portion - use passed font settings
+ rRoot.ConvertFont( aStringProp, nLeadFontIdx, &rLeadFontColor );
+ else
+ rRoot.ConvertFont( aStringProp, nFontIdx );
+
+ // add string to vector of strings
+ aStringVec.emplace_back(xFmtStr );
+ }
+ }
+ return ScfApiHelper::VectorToSequence( aStringVec );
+}
+
+void XclImpChSourceLink::FillSourceLink( ::std::vector< ScTokenRef >& rTokens ) const
+{
+ if( !mxTokenArray )
+ // no links to fill.
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ ScTokenRef pToken(p->Clone());
+ if (ScRefTokenHelper::isRef(pToken))
+ // This is a reference token. Store it.
+ ScRefTokenHelper::join(&GetRoot().GetDoc(), rTokens, pToken, ScAddress());
+ }
+}
+
+// Text =======================================================================
+
+XclImpChFontBase::~XclImpChFontBase()
+{
+}
+
+void XclImpChFontBase::ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
+{
+ Color aFontColor = GetFontColor();
+ rRoot.ConvertFont( rPropSet, GetFontIndex(), &aFontColor );
+}
+
+void XclImpChFontBase::ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
+{
+ XclChPropSetHelper::WriteRotationProperties( rPropSet, GetRotation(), bSupportsStacked );
+}
+
+XclImpChFont::XclImpChFont() :
+ mnFontIdx( EXC_FONT_NOTFOUND )
+{
+}
+
+void XclImpChFont::ReadChFont( XclImpStream& rStrm )
+{
+ mnFontIdx = rStrm.ReaduInt16();
+}
+
+XclImpChText::XclImpChText( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChText::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnHAlign = rStrm.ReaduInt8();
+ maData.mnVAlign = rStrm.ReaduInt8();
+ maData.mnBackMode = rStrm.ReaduInt16();
+
+ Color aColor;
+ rStrm >> aColor;
+ maData.maTextComplexColor.setColor(aColor);
+
+ rStrm >> maData.maRect;
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ aColor = GetPalette().GetColor(rStrm.ReaduInt16());
+ maData.maTextComplexColor.setColor(aColor);
+ // placement and rotation
+ maData.mnFlags2 = rStrm.ReaduInt16();
+ maData.mnRotation = rStrm.ReaduInt16();
+ }
+ else
+ {
+ // BIFF2-BIFF7: get rotation from text orientation
+ sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 );
+ maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
+ }
+}
+
+void XclImpChText::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHFONT:
+ mxFont = std::make_shared<XclImpChFont>();
+ mxFont->ReadChFont( rStrm );
+ break;
+ case EXC_ID_CHFORMATRUNS:
+ if( GetBiff() == EXC_BIFF8 )
+ XclImpString::ReadFormats( rStrm, maFormats );
+ break;
+ case EXC_ID_CHSOURCELINK:
+ mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ mxSrcLink->ReadChSourceLink( rStrm );
+ break;
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_TEXT );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHOBJECTLINK:
+ maObjLink.mnTarget = rStrm.ReaduInt16();
+ maObjLink.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
+ maObjLink.maPointPos.mnPointIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHFRLABELPROPS:
+ ReadChFrLabelProps( rStrm );
+ break;
+ case EXC_ID_CHEND:
+ if( mxSrcLink && !maFormats.empty() )
+ mxSrcLink->SetTextFormats( std::vector(maFormats) );
+ break;
+ }
+}
+
+sal_uInt16 XclImpChText::GetFontIndex() const
+{
+ return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+Color XclImpChText::GetFontColor() const
+{
+ return ::get_flag(maData.mnFlags, EXC_CHTEXT_AUTOCOLOR) ? GetFontAutoColor() : maData.maTextComplexColor.getFinalColor();
+}
+
+sal_uInt16 XclImpChText::GetRotation() const
+{
+ return maData.mnRotation;
+}
+
+void XclImpChText::SetString( const OUString& rString )
+{
+ if( !mxSrcLink )
+ mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ mxSrcLink->SetString( rString );
+}
+
+void XclImpChText::UpdateText( const XclImpChText* pParentText )
+{
+ if( !pParentText )
+ return;
+
+ // update missing members
+ if( !mxFrame )
+ mxFrame = pParentText->mxFrame;
+ if( !mxFont )
+ {
+ mxFont = pParentText->mxFont;
+ // text color is taken from CHTEXT record, not from font in CHFONT
+ ::set_flag(maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, ::get_flag(pParentText->maData.mnFlags, EXC_CHTEXT_AUTOCOLOR));
+ maData.maTextComplexColor = pParentText->maData.maTextComplexColor;
+ }
+}
+
+void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent )
+{
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bValue );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent );
+}
+
+void XclImpChText::ConvertFont( ScfPropertySet& rPropSet ) const
+{
+ ConvertFontBase( GetChRoot(), rPropSet );
+}
+
+void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
+{
+ ConvertRotationBase( rPropSet, bSupportsStacked );
+}
+
+void XclImpChText::ConvertFrame( ScfPropertySet& rPropSet ) const
+{
+ if( mxFrame )
+ mxFrame->Convert( rPropSet );
+}
+
+void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
+{
+ if( mxSrcLink )
+ mxSrcLink->ConvertNumFmt( rPropSet, bPercent );
+}
+
+void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
+{
+ // existing CHFRLABELPROPS record wins over flags from CHTEXT
+ sal_uInt16 nShowFlags = mxLabelProps ? mxLabelProps->mnFlags : maData.mnFlags;
+ sal_uInt16 SHOWANYCATEG = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWCATEG : (EXC_CHTEXT_SHOWCATEGPERC | EXC_CHTEXT_SHOWCATEG);
+ sal_uInt16 SHOWANYVALUE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWVALUE : EXC_CHTEXT_SHOWVALUE;
+ sal_uInt16 SHOWANYPERCENT = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWPERCENT : (EXC_CHTEXT_SHOWPERCENT | EXC_CHTEXT_SHOWCATEGPERC);
+ sal_uInt16 SHOWANYBUBBLE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWBUBBLE : EXC_CHTEXT_SHOWBUBBLE;
+
+ // get raw flags for label values
+ bool bShowNone = IsDeleted();
+ bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG );
+ bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT );
+ bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE );
+ bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE );
+
+ // adjust to Chart2 behaviour
+ if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
+ bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set
+
+ // other flags
+ bool bShowAny = bShowValue || bShowPercent || bShowCateg;
+ bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL );
+
+ // create API struct for label values, set API label separator
+ cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, false, false );
+ rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel );
+ OUString aSep = mxLabelProps ? mxLabelProps->maSeparator : OUString('\n');
+ if( aSep.isEmpty() )
+ aSep = "; ";
+ rPropSet.SetStringProperty( EXC_CHPROP_LABELSEPARATOR, aSep );
+
+ // text properties of attached label
+ if( !bShowAny )
+ return;
+
+ ConvertFont( rPropSet );
+ ConvertRotation( rPropSet, false );
+ // label placement
+ using namespace cssc::DataLabelPlacement;
+ sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos;
+ switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) )
+ {
+ case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break;
+ case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break;
+ case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break;
+ case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break;
+ case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break;
+ case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break;
+ case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break;
+ case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break;
+ case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break;
+ case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break;
+ }
+ sal_Int32 nGlobalPlacement = 0;
+ if ( ( nPlacement == rTypeInfo.mnDefaultLabelPos ) && pGlobalPropSet &&
+ pGlobalPropSet->GetProperty( nGlobalPlacement, EXC_CHPROP_LABELPLACEMENT ) )
+ nPlacement = nGlobalPlacement;
+
+ rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement );
+ // label number format (percentage format wins over value format)
+ if( bShowPercent || bShowValue )
+ ConvertNumFmt( rPropSet, bShowPercent );
+}
+
+Reference< XTitle > XclImpChText::CreateTitle() const
+{
+ Reference< XTitle > xTitle;
+ if( mxSrcLink && mxSrcLink->HasString() )
+ {
+ // create the formatted strings
+ Sequence< Reference< XFormattedString > > aStringSeq(
+ mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) );
+ if( aStringSeq.hasElements() )
+ {
+ // create the title object
+ xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY );
+ if( xTitle.is() )
+ {
+ // set the formatted strings
+ xTitle->setText( aStringSeq );
+ // more title formatting properties
+ ScfPropertySet aTitleProp( xTitle );
+ ConvertFrame( aTitleProp );
+ ConvertRotation( aTitleProp, true );
+ }
+ }
+ }
+ return xTitle;
+}
+
+void XclImpChText::ConvertTitlePosition( const XclChTextKey& rTitleKey ) const
+{
+ if( !mxFramePos ) return;
+
+ const XclChFramePos& rPosData = mxFramePos->GetFramePosData();
+ OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT),
+ "XclImpChText::ConvertTitlePosition - unexpected frame position mode" );
+
+ /* Check if title is moved manually. To get the actual position of the
+ title, we do some kind of hack and use the values from the CHTEXT
+ record, effectively ignoring the contents of the CHFRAMEPOS record
+ which contains the position relative to the default title position
+ (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record).
+ Especially when it comes to axis titles, things would become very
+ complicated here, because the relative title position is stored in a
+ measurement unit that is dependent on the size of the inner plot area,
+ the interpretation of the X and Y coordinate is dependent on the
+ direction of the axis, and in 3D charts, and the title default
+ positions are dependent on the 3D view settings (rotation, elevation,
+ and perspective). Thus, it is easier to assume that the creator has
+ written out the correct absolute position and size of the title in the
+ CHTEXT record. This is assured by checking that the shape size stored
+ in the CHTEXT record is non-zero. */
+ if( !((rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) &&
+ ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) &&
+ (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0)) )
+ return;
+
+ try
+ {
+ Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW );
+ // the call to XShape.getSize() may recalc the chart view
+ css::awt::Size aTitleSize = xTitleShape->getSize();
+ // rotated titles need special handling...
+ Degree100 nScRot = XclTools::GetScRotation( GetRotation(), 0_deg100 );
+ double fRad = toRadians(nScRot);
+ double fSin = fabs( sin( fRad ) );
+ // calculate the title position from the values in the CHTEXT record
+ css::awt::Point aTitlePos(
+ CalcHmmFromChartX( maData.maRect.mnX ),
+ CalcHmmFromChartY( maData.maRect.mnY ) );
+ // add part of height to X direction, if title is rotated down (clockwise)
+ if( nScRot > 18000_deg100 )
+ aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 );
+ // add part of width to Y direction, if title is rotated up (counterclockwise)
+ else if( nScRot > 0_deg100 )
+ aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 );
+ // set the resulting position at the title shape
+ xTitleShape->setPosition( aTitlePos );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void XclImpChText::ReadChFrLabelProps( XclImpStream& rStrm )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mxLabelProps = std::make_shared<XclChFrLabelProps>();
+ sal_uInt16 nSepLen;
+ rStrm.Ignore( 12 );
+ mxLabelProps->mnFlags = rStrm.ReaduInt16();
+ nSepLen = rStrm.ReaduInt16();
+ if( nSepLen > 0 )
+ mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen );
+ }
+}
+
+namespace {
+
+void lclUpdateText( XclImpChTextRef& rxText, const XclImpChText* xDefText )
+{
+ if (rxText)
+ rxText->UpdateText( xDefText );
+ else if (xDefText)
+ {
+ rxText = std::make_shared<XclImpChText>(*xDefText);
+ }
+}
+
+void lclFinalizeTitle( XclImpChTextRef& rxTitle, const XclImpChText* pDefText, const OUString& rAutoTitle )
+{
+ /* Do not update a title, if it is not visible (if rxTitle is null).
+ Existing reference indicates enabled title. */
+ if( rxTitle )
+ {
+ if( !rxTitle->HasString() )
+ rxTitle->SetString( rAutoTitle );
+ if( rxTitle->HasString() )
+ rxTitle->UpdateText(pDefText);
+ else
+ rxTitle.reset();
+ }
+}
+
+} // namespace
+
+// Data series ================================================================
+
+void XclImpChMarkerFormat::ReadChMarkerFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maLineColor >> maData.maFillColor;
+ maData.mnMarkerType = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ const XclImpPalette& rPal = rRoot.GetPalette();
+ maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() );
+ maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() );
+ // marker size
+ maData.mnMarkerSize = rStrm.ReaduInt32();
+ }
+}
+
+void XclImpChMarkerFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const
+{
+ if( IsAuto() )
+ {
+ XclChMarkerFormat aMarkerFmt;
+ // line and fill color of the symbol are equal to series line color
+ //TODO: Excel sets no fill color for specific symbols (e.g. cross)
+ aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx );
+ switch( nLineWeight )
+ {
+ case EXC_CHLINEFORMAT_HAIR: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_HAIRSIZE; break;
+ case EXC_CHLINEFORMAT_SINGLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE; break;
+ case EXC_CHLINEFORMAT_DOUBLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; break;
+ case EXC_CHLINEFORMAT_TRIPLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_TRIPLESIZE; break;
+ default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE;
+ }
+ aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ XclChPropSetHelper::WriteMarkerProperties( rPropSet, aMarkerFmt );
+ }
+ else
+ {
+ XclChPropSetHelper::WriteMarkerProperties( rPropSet, maData );
+ }
+}
+
+void XclImpChMarkerFormat::ConvertColor( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
+{
+ Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor;
+ rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor );
+}
+
+XclImpChPieFormat::XclImpChPieFormat() :
+ mnPieDist( 0 )
+{
+}
+
+void XclImpChPieFormat::ReadChPieFormat( XclImpStream& rStrm )
+{
+ mnPieDist = rStrm.ReaduInt16();
+}
+
+void XclImpChPieFormat::Convert( ScfPropertySet& rPropSet ) const
+{
+ double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 );
+ rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist );
+}
+
+XclImpChSeriesFormat::XclImpChSeriesFormat() :
+ mnFlags( 0 )
+{
+}
+
+void XclImpChSeriesFormat::ReadChSeriesFormat( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpCh3dDataFormat::ReadCh3dDataFormat( XclImpStream& rStrm )
+{
+ maData.mnBase = rStrm.ReaduInt8();
+ maData.mnTop = rStrm.ReaduInt8();
+}
+
+void XclImpCh3dDataFormat::Convert( ScfPropertySet& rPropSet ) const
+{
+ using namespace ::com::sun::star::chart2::DataPointGeometry3D;
+ sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ?
+ ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) :
+ ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE);
+ rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType );
+}
+
+XclImpChAttachedLabel::XclImpChAttachedLabel( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ mnFlags( 0 )
+{
+}
+
+void XclImpChAttachedLabel::ReadChAttachedLabel( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+}
+
+XclImpChTextRef XclImpChAttachedLabel::CreateDataLabel( const XclImpChText* pParent ) const
+{
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE;
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC;
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC;
+
+ XclImpChTextRef xLabel;
+ if ( pParent )
+ xLabel = std::make_shared<XclImpChText>( *pParent );
+ else
+ xLabel = std::make_shared<XclImpChText>( GetChRoot() );
+ xLabel->UpdateDataLabel(
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ),
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ),
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) );
+ return xLabel;
+}
+
+XclImpChDataFormat::XclImpChDataFormat( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChDataFormat::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.maPointPos.mnPointIdx = rStrm.ReaduInt16();
+ maData.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
+ maData.mnFormatIdx = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChDataFormat::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHMARKERFORMAT:
+ mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
+ mxMarkerFmt->ReadChMarkerFormat( rStrm );
+ break;
+ case EXC_ID_CHPIEFORMAT:
+ mxPieFmt = std::make_shared<XclImpChPieFormat>();
+ mxPieFmt->ReadChPieFormat( rStrm );
+ break;
+ case EXC_ID_CHSERIESFORMAT:
+ mxSeriesFmt = std::make_shared<XclImpChSeriesFormat>();
+ mxSeriesFmt->ReadChSeriesFormat( rStrm );
+ break;
+ case EXC_ID_CH3DDATAFORMAT:
+ mx3dDataFmt = std::make_shared<XclImpCh3dDataFormat>();
+ mx3dDataFmt->ReadCh3dDataFormat( rStrm );
+ break;
+ case EXC_ID_CHATTACHEDLABEL:
+ mxAttLabel = std::make_shared<XclImpChAttachedLabel>( GetChRoot() );
+ mxAttLabel->ReadChAttachedLabel( rStrm );
+ break;
+ default:
+ XclImpChFrameBase::ReadSubRecord( rStrm );
+ }
+}
+
+void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx )
+{
+ maData.maPointPos = rPointPos;
+ maData.mnFormatIdx = nFormatIdx;
+}
+
+void XclImpChDataFormat::UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo )
+{
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+}
+
+void XclImpChDataFormat::UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt )
+{
+ // update missing formats from passed chart type group format
+ if( pGroupFmt )
+ {
+ if( !mxLineFmt )
+ mxLineFmt = pGroupFmt->mxLineFmt;
+ if( !mxAreaFmt && !mxEscherFmt )
+ {
+ mxAreaFmt = pGroupFmt->mxAreaFmt;
+ mxEscherFmt = pGroupFmt->mxEscherFmt;
+ }
+ if( !mxMarkerFmt )
+ mxMarkerFmt = pGroupFmt->mxMarkerFmt;
+ if( !mxPieFmt )
+ mxPieFmt = pGroupFmt->mxPieFmt;
+ if( !mxSeriesFmt )
+ mxSeriesFmt = pGroupFmt->mxSeriesFmt;
+ if( !mx3dDataFmt )
+ mx3dDataFmt = pGroupFmt->mx3dDataFmt;
+ if( !mxAttLabel )
+ mxAttLabel = pGroupFmt->mxAttLabel;
+ }
+
+ /* Create missing but required formats. Existing line, area, and marker
+ format objects are needed to create automatic series formatting. */
+ if( !mxLineFmt )
+ mxLineFmt = new XclImpChLineFormat();
+ if( !mxAreaFmt && !mxEscherFmt )
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ if( !mxMarkerFmt )
+ mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
+
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+ // update data label
+ UpdateDataLabel( pGroupFmt );
+}
+
+void XclImpChDataFormat::UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt )
+{
+ // remove formats if they are automatic in this and in the passed series format
+ if( pSeriesFmt )
+ {
+ if( IsAutoLine() && pSeriesFmt->IsAutoLine() )
+ mxLineFmt.clear();
+ if( IsAutoArea() && pSeriesFmt->IsAutoArea() )
+ mxAreaFmt.reset();
+ if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() )
+ mxMarkerFmt.reset();
+ mxSeriesFmt.reset();
+ }
+
+ // Excel ignores 3D bar format for single data points
+ mx3dDataFmt.reset();
+ // remove point line formats for linear chart types, TODO: implement in OOChart
+ if( !rTypeInfo.IsSeriesFrameFormat() )
+ mxLineFmt.clear();
+
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+ // update data label
+ UpdateDataLabel( pSeriesFmt );
+}
+
+void XclImpChDataFormat::UpdateTrendLineFormat()
+{
+ if( !mxLineFmt )
+ mxLineFmt = new XclImpChLineFormat();
+ mxAreaFmt.reset();
+ mxEscherFmt.reset();
+ mxMarkerFmt.reset();
+ mxPieFmt.reset();
+ mxSeriesFmt.reset();
+ mx3dDataFmt.reset();
+ mxAttLabel.reset();
+ // update data label
+ UpdateDataLabel( nullptr );
+}
+
+void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
+{
+ /* Line and area format.
+ #i71810# If the data points are filled with bitmaps, textures, or
+ patterns, then only bar charts will use the CHPICFORMAT record to
+ determine stacking/stretching mode. All other chart types ignore this
+ record and always use the property 'fill-type' from the DFF property
+ set (stretched for bitmaps, and stacked for textures and patterns). */
+ bool bUsePicFmt = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR;
+ ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx, bUsePicFmt );
+
+ // #i83151# only hair lines in 3D charts with filled data points
+ if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt && mxLineFmt->HasLine() )
+ rPropSet.SetProperty< sal_Int32 >( "BorderWidth", 0 );
+
+ // other formatting
+ if( mxMarkerFmt )
+ mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() );
+ if( mxPieFmt )
+ mxPieFmt->Convert( rPropSet );
+ if( mx3dDataFmt )
+ mx3dDataFmt->Convert( rPropSet );
+ if( mxLabel )
+ mxLabel->ConvertDataLabel( rPropSet, rTypeInfo, pGlobalPropSet );
+
+ // 3D settings
+ rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 );
+
+ /* Special case: set marker color as line color, if series line is not
+ visible. This makes the color visible in the marker area.
+ TODO: remove this if OOChart supports own colors in markers. */
+ if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt )
+ mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx );
+}
+
+void XclImpChDataFormat::ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const
+{
+ ConvertLineBase( GetChRoot(), rPropSet, eObjType );
+}
+
+void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
+{
+ ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx );
+}
+
+void XclImpChDataFormat::RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo )
+{
+ // data point marker only in linear 2D charts
+ if( rTypeInfo.IsSeriesFrameFormat() )
+ mxMarkerFmt.reset();
+ // pie format only in pie/donut charts
+ if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
+ mxPieFmt.reset();
+ // 3D format only in 3D bar charts
+ if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
+ mx3dDataFmt.reset();
+}
+
+void XclImpChDataFormat::UpdateDataLabel( const XclImpChDataFormat* pParentFmt )
+{
+ /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL
+ records. Only if there is a CHATTACHEDLABEL record without a CHTEXT
+ group, the contents of the CHATTACHEDLABEL record are used. In this
+ case a new CHTEXT group is created and filled with the settings from
+ the CHATTACHEDLABEL record. */
+ const XclImpChText* pDefText = nullptr;
+ if (pParentFmt)
+ pDefText = pParentFmt->GetDataLabel();
+ if (!pDefText)
+ pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_DATALABEL );
+ if (mxLabel)
+ mxLabel->UpdateText(pDefText);
+ else if (mxAttLabel)
+ mxLabel = mxAttLabel->CreateDataLabel( pDefText );
+}
+
+XclImpChSerTrendLine::XclImpChSerTrendLine( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChSerTrendLine::ReadChSerTrendLine( XclImpStream& rStrm )
+{
+ maData.mnLineType = rStrm.ReaduInt8();
+ maData.mnOrder = rStrm.ReaduInt8();
+ maData.mfIntercept = rStrm.ReadDouble();
+ maData.mnShowEquation = rStrm.ReaduInt8();
+ maData.mnShowRSquared = rStrm.ReaduInt8();
+ maData.mfForecastFor = rStrm.ReadDouble();
+ maData.mfForecastBack = rStrm.ReadDouble();
+}
+
+Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const
+{
+ // trend line type
+ Reference< XRegressionCurve > xRegCurve;
+ switch( maData.mnLineType )
+ {
+ case EXC_CHSERTREND_POLYNOMIAL:
+ if( maData.mnOrder == 1 )
+ {
+ xRegCurve = LinearRegressionCurve::create( comphelper::getProcessComponentContext() );
+ } else {
+ xRegCurve = PolynomialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ }
+ break;
+ case EXC_CHSERTREND_EXPONENTIAL:
+ xRegCurve = ExponentialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_LOGARITHMIC:
+ xRegCurve = LogarithmicRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_POWER:
+ xRegCurve = PotentialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_MOVING_AVG:
+ xRegCurve = MovingAverageRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ }
+
+ // trend line formatting
+ if( xRegCurve.is() && mxDataFmt )
+ {
+ ScfPropertySet aPropSet( xRegCurve );
+ mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE );
+
+ aPropSet.SetProperty(EXC_CHPROP_CURVENAME, maTrendLineName);
+ aPropSet.SetProperty(EXC_CHPROP_POLYNOMIAL_DEGREE, static_cast<sal_Int32> (maData.mnOrder) );
+ aPropSet.SetProperty(EXC_CHPROP_MOVING_AVERAGE_PERIOD, static_cast<sal_Int32> (maData.mnOrder) );
+ aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_FORWARD, maData.mfForecastFor);
+ aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_BACKWARD, maData.mfForecastBack);
+
+ bool bForceIntercept = std::isfinite(maData.mfIntercept);
+ aPropSet.SetProperty(EXC_CHPROP_FORCE_INTERCEPT, bForceIntercept);
+ if (bForceIntercept)
+ {
+ aPropSet.SetProperty(EXC_CHPROP_INTERCEPT_VALUE, maData.mfIntercept);
+ }
+
+ // #i83100# show equation and correlation coefficient
+ ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() );
+ aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWEQUATION, maData.mnShowEquation != 0 );
+ aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWCORRELATION, maData.mnShowRSquared != 0 );
+
+ // #i83100# formatting of the equation text box
+ if (const XclImpChText* pLabel = mxDataFmt->GetDataLabel())
+ {
+ pLabel->ConvertFont( aLabelProp );
+ pLabel->ConvertFrame( aLabelProp );
+ pLabel->ConvertNumFmt( aLabelProp, false );
+ }
+ }
+
+ return xRegCurve;
+}
+
+XclImpChSerErrorBar::XclImpChSerErrorBar( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChSerErrorBar::ReadChSerErrorBar( XclImpStream& rStrm )
+{
+ maData.mnBarType = rStrm.ReaduInt8();
+ maData.mnSourceType = rStrm.ReaduInt8();
+ maData.mnLineEnd = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ maData.mfValue = rStrm.ReadDouble();
+ maData.mnValueCount = rStrm.ReaduInt16();
+}
+
+void XclImpChSerErrorBar::SetSeriesData( XclImpChSourceLinkRef const & xValueLink, XclImpChDataFormatRef const & xDataFmt )
+{
+ mxValueLink = xValueLink;
+ mxDataFmt = xDataFmt;
+}
+
+Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const
+{
+ return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) );
+}
+
+Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar )
+{
+ Reference< XPropertySet > xErrorBar;
+
+ if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar )
+ {
+ xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY );
+ ScfPropertySet aBarProp( xErrorBar );
+
+ // plus/minus bars visible?
+ aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != nullptr );
+ aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != nullptr );
+
+ // type of displayed error
+ switch( pPrimaryBar->maData.mnSourceType )
+ {
+ case EXC_CHSERERR_PERCENT:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE );
+ aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
+ aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_FIXED:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE );
+ aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
+ aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_STDDEV:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION );
+ aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_STDERR:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR );
+ break;
+ case EXC_CHSERERR_CUSTOM:
+ {
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA );
+ // attach data sequences to error bar
+ Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
+ if( xDataSink.is() )
+ {
+ // create vector of all value sequences
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ // add positive values
+ if( pPosBar )
+ {
+ Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence();
+ if( xValueSeq.is() )
+ aLabeledSeqVec.push_back( xValueSeq );
+ }
+ // add negative values
+ if( pNegBar )
+ {
+ Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence();
+ if( xValueSeq.is() )
+ aLabeledSeqVec.push_back( xValueSeq );
+ }
+ // attach labeled data sequences to series
+ if( aLabeledSeqVec.empty() )
+ xErrorBar.clear();
+ else
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+ }
+ }
+ break;
+ default:
+ xErrorBar.clear();
+ }
+
+ // error bar formatting
+ if( pPrimaryBar->mxDataFmt && xErrorBar.is() )
+ pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR );
+ }
+
+ return xErrorBar;
+}
+
+XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
+ XclImpChRoot( rRoot ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE ),
+ mnSeriesIdx( nSeriesIdx ),
+ mnParentIdx( EXC_CHSERIES_INVALID ),
+ mbLabelDeleted( false )
+{
+}
+
+void XclImpChSeries::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnCategType = rStrm.ReaduInt16();
+ maData.mnValueType = rStrm.ReaduInt16();
+ maData.mnCategCount = rStrm.ReaduInt16();
+ maData.mnValueCount = rStrm.ReaduInt16();
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maData.mnBubbleType = rStrm.ReaduInt16();
+ maData.mnBubbleCount = rStrm.ReaduInt16();
+ }
+}
+
+void XclImpChSeries::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHSOURCELINK:
+ ReadChSourceLink( rStrm );
+ break;
+ case EXC_ID_CHDATAFORMAT:
+ ReadChDataFormat( rStrm );
+ break;
+ case EXC_ID_CHSERGROUP:
+ mnGroupIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHSERPARENT:
+ ReadChSerParent( rStrm );
+ break;
+ case EXC_ID_CHSERTRENDLINE:
+ ReadChSerTrendLine( rStrm );
+ break;
+ case EXC_ID_CHSERERRORBAR:
+ ReadChSerErrorBar( rStrm );
+ break;
+ case EXC_ID_CHLEGENDEXCEPTION:
+ ReadChLegendException( rStrm );
+ break;
+ }
+}
+
+void XclImpChSeries::SetDataFormat( const XclImpChDataFormatRef& xDataFmt )
+{
+ if (!xDataFmt)
+ return;
+
+ sal_uInt16 nPointIdx = xDataFmt->GetPointPos().mnPointIdx;
+ if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
+ {
+ if (mxSeriesFmt)
+ // Don't overwrite the existing format.
+ return;
+
+ mxSeriesFmt = xDataFmt;
+ if (HasParentSeries())
+ return;
+
+ XclImpChTypeGroupRef pTypeGroup = GetChartData().GetTypeGroup(mnGroupIdx);
+ if (pTypeGroup)
+ pTypeGroup->SetUsedFormatIndex(xDataFmt->GetFormatIdx());
+
+ return;
+ }
+
+ if (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT)
+ // Above the max point count. Bail out.
+ return;
+
+ XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
+ if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert it.
+ itr = maPointFmts.insert(itr, XclImpChDataFormatMap::value_type(nPointIdx, xDataFmt));
+ }
+}
+
+void XclImpChSeries::SetDataLabel( const XclImpChTextRef& xLabel )
+{
+ if (!xLabel)
+ return;
+
+ sal_uInt16 nPointIdx = xLabel->GetPointPos().mnPointIdx;
+ if ((nPointIdx != EXC_CHDATAFORMAT_ALLPOINTS) && (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT))
+ // Above the maximum allowed data points. Bail out.
+ return;
+
+ XclImpChTextMap::iterator itr = maLabels.lower_bound(nPointIdx);
+ if (itr == maLabels.end() || maLabels.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert it.
+ itr = maLabels.insert(itr, XclImpChTextMap::value_type(nPointIdx, xLabel));
+ }
+}
+
+void XclImpChSeries::AddChildSeries( const XclImpChSeries& rSeries )
+{
+ OSL_ENSURE( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" );
+ if (&rSeries == this)
+ {
+ SAL_WARN("sc.filter", "self add attempt");
+ return;
+ }
+
+ /* In Excel, trend lines and error bars are stored as own series. In Calc,
+ these are properties of the parent series. This function adds the
+ settings of the passed series to this series. */
+ maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() );
+ for (auto const& it : rSeries.m_ErrorBars)
+ {
+ m_ErrorBars.insert(std::make_pair(it.first, std::make_unique<XclImpChSerErrorBar>(*it.second)));
+ }
+}
+
+void XclImpChSeries::FinalizeDataFormats()
+{
+ if( HasParentSeries() )
+ {
+ // *** series is a child series, e.g. trend line or error bar ***
+
+ // create missing series format
+ if( !mxSeriesFmt )
+ mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, 0 );
+
+ if( mxSeriesFmt )
+ {
+ // #i83100# set text label format, e.g. for trend line equations
+ XclImpChTextRef xLabel;
+ XclImpChTextMap::iterator itr = maLabels.find(EXC_CHDATAFORMAT_ALLPOINTS);
+ if (itr != maLabels.end())
+ xLabel = itr->second;
+ mxSeriesFmt->SetDataLabel(xLabel);
+ // create missing automatic formats
+ mxSeriesFmt->UpdateTrendLineFormat();
+ }
+
+ // copy series formatting to child objects
+ for (auto const& trendLine : maTrendLines)
+ {
+ trendLine->SetDataFormat(mxSeriesFmt);
+ if (mxTitleLink && mxTitleLink->HasString())
+ {
+ trendLine->SetTrendlineName(mxTitleLink->GetString());
+ }
+ }
+ for (auto const& it : m_ErrorBars)
+ {
+ it.second->SetSeriesData( mxValueLink, mxSeriesFmt );
+ }
+ }
+ else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
+ {
+ // *** series is a regular data series ***
+
+ // create missing series format
+ if( !mxSeriesFmt )
+ {
+ // #i51639# use a new unused format index to create series default format
+ sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex();
+ mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, nFormatIdx );
+ }
+
+ // set text labels to data formats
+ for (auto const& label : maLabels)
+ {
+ sal_uInt16 nPointIdx = label.first;
+ if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
+ {
+ if (!mxSeriesFmt)
+ mxSeriesFmt = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
+ mxSeriesFmt->SetDataLabel(label.second);
+ }
+ else if (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT)
+ {
+ XclImpChDataFormatRef p;
+ XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
+ if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert
+ // a new one.
+ p = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
+ itr = maPointFmts.insert(
+ itr, XclImpChDataFormatMap::value_type(nPointIdx, p));
+ }
+ else
+ p = itr->second;
+ p->SetDataLabel(label.second);
+ }
+ }
+
+ // update series format (copy missing formatting from group default format)
+ if( mxSeriesFmt )
+ mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() );
+
+ // update data point formats (removes unchanged automatic formatting)
+ for (auto const& pointFormat : maPointFmts)
+ pointFormat.second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() );
+ }
+}
+
+namespace {
+
+/** Returns the property set of the specified data point. */
+ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_uInt16 nPointIdx )
+{
+ ScfPropertySet aPropSet;
+ try
+ {
+ aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "lclGetPointPropSet - no data point property set" );
+ }
+ return aPropSet;
+}
+
+} // namespace
+
+Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const
+{
+ return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() );
+}
+
+Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const
+{
+ return lclCreateLabeledDataSequence( mxCategLink, rCategRole );
+}
+
+Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const
+{
+ Reference< XDataSeries > xDataSeries;
+ if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
+ {
+ const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo();
+
+ // create the data series object
+ xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
+
+ // attach data and title sequences to series
+ Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
+ if( xDataSink.is() )
+ {
+ // create vector of all value sequences
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ // add Y values
+ Reference< XLabeledDataSequence > xYValueSeq =
+ CreateValueSequence( EXC_CHPROP_ROLE_YVALUES );
+ if( xYValueSeq.is() )
+ aLabeledSeqVec.push_back( xYValueSeq );
+ // add X values
+ if( !rTypeInfo.mbCategoryAxis )
+ {
+ Reference< XLabeledDataSequence > xXValueSeq =
+ CreateCategSequence( EXC_CHPROP_ROLE_XVALUES );
+ if( xXValueSeq.is() )
+ aLabeledSeqVec.push_back( xXValueSeq );
+ // add size values of bubble charts
+ if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
+ {
+ Reference< XLabeledDataSequence > xSizeValueSeq =
+ lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() );
+ if( xSizeValueSeq.is() )
+ aLabeledSeqVec.push_back( xSizeValueSeq );
+ }
+ }
+ // attach labeled data sequences to series
+ if( !aLabeledSeqVec.empty() )
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+ }
+
+ // series formatting
+ ScfPropertySet aSeriesProp( xDataSeries );
+ if( mxSeriesFmt )
+ mxSeriesFmt->Convert( aSeriesProp, rTypeInfo );
+
+ if (mbLabelDeleted)
+ aSeriesProp.SetProperty(EXC_CHPROP_SHOWLEGENDENTRY, false);
+
+ // trend lines
+ ConvertTrendLines( xDataSeries );
+
+ // error bars
+ Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
+ if( xErrorBarX.is() )
+ aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX );
+ Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
+ if( xErrorBarY.is() )
+ aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY );
+
+ // own area formatting for every data point (TODO: varying line color not supported)
+ bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat();
+ aSeriesProp.SetBoolProperty( EXC_CHPROP_VARYCOLORSBY, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
+ // #i91271# always set area formatting for every point in pie/doughnut charts
+ if (mxSeriesFmt && mxValueLink && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)))
+ {
+ for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx )
+ {
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
+ mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx );
+ }
+ }
+
+ // data point formatting
+ for (auto const& pointFormat : maPointFmts)
+ {
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, pointFormat.first );
+ pointFormat.second->Convert( aPointProp, rTypeInfo, &aSeriesProp );
+ }
+ }
+ return xDataSeries;
+}
+
+void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScTokenRef >& rTokens ) const
+{
+ if( mxValueLink )
+ mxValueLink->FillSourceLink( rTokens );
+ if( mxCategLink )
+ mxCategLink->FillSourceLink( rTokens );
+ if( mxTitleLink )
+ mxTitleLink->FillSourceLink( rTokens );
+ if( mxBubbleLink )
+ mxBubbleLink->FillSourceLink( rTokens );
+}
+
+void XclImpChSeries::ReadChSourceLink( XclImpStream& rStrm )
+{
+ XclImpChSourceLinkRef xSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ xSrcLink->ReadChSourceLink( rStrm );
+ switch( xSrcLink->GetDestType() )
+ {
+ case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break;
+ case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break;
+ case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break;
+ case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break;
+ }
+}
+
+void XclImpChSeries::ReadChDataFormat( XclImpStream& rStrm )
+{
+ // #i51639# chart stores all data formats and assigns them later to the series
+ GetChartData().ReadChDataFormat( rStrm );
+}
+
+void XclImpChSeries::ReadChSerParent( XclImpStream& rStrm )
+{
+ mnParentIdx = rStrm.ReaduInt16();
+ // index to parent series is 1-based, convert it to 0-based
+ if( mnParentIdx > 0 )
+ --mnParentIdx;
+ else
+ mnParentIdx = EXC_CHSERIES_INVALID;
+}
+
+void XclImpChSeries::ReadChSerTrendLine( XclImpStream& rStrm )
+{
+ XclImpChSerTrendLineRef xTrendLine = std::make_shared<XclImpChSerTrendLine>( GetChRoot() );
+ xTrendLine->ReadChSerTrendLine( rStrm );
+ maTrendLines.push_back( xTrendLine );
+}
+
+void XclImpChSeries::ReadChSerErrorBar( XclImpStream& rStrm )
+{
+ unique_ptr<XclImpChSerErrorBar> pErrorBar(new XclImpChSerErrorBar(GetChRoot()));
+ pErrorBar->ReadChSerErrorBar(rStrm);
+ sal_uInt8 nBarType = pErrorBar->GetBarType();
+ m_ErrorBars.insert(std::make_pair(nBarType, std::move(pErrorBar)));
+}
+
+XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx )
+{
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx );
+ return xDataFmt;
+}
+
+void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > const & xDataSeries ) const
+{
+ Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
+ if( !xRegCurveCont.is() )
+ return;
+
+ for (auto const& trendLine : maTrendLines)
+ {
+ try
+ {
+ Reference< XRegressionCurve > xRegCurve = trendLine->CreateRegressionCurve();
+ if( xRegCurve.is() )
+ {
+ xRegCurveCont->addRegressionCurve( xRegCurve );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" );
+ }
+ }
+}
+
+Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const
+{
+ XclImpChSerErrorBarMap::const_iterator itrPosBar = m_ErrorBars.find(nPosBarId);
+ XclImpChSerErrorBarMap::const_iterator itrNegBar = m_ErrorBars.find(nNegBarId);
+ XclImpChSerErrorBarMap::const_iterator itrEnd = m_ErrorBars.end();
+ if (itrPosBar == itrEnd || itrNegBar == itrEnd)
+ return Reference<XPropertySet>();
+
+ return XclImpChSerErrorBar::CreateErrorBar(itrPosBar->second.get(), itrNegBar->second.get());
+}
+
+void XclImpChSeries::ReadChLegendException(XclImpStream& rStrm)
+{
+ rStrm.Ignore(2);
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ mbLabelDeleted = (nFlags & EXC_CHLEGENDEXCEPTION_DELETED);
+}
+
+// Chart type groups ==========================================================
+
+XclImpChType::XclImpChType( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ mnRecId( EXC_ID_CHUNKNOWN ),
+ maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
+{
+}
+
+void XclImpChType::ReadChType( XclImpStream& rStrm )
+{
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bool bKnownType = true;
+
+ switch( nRecId )
+ {
+ case EXC_ID_CHBAR:
+ maData.mnOverlap = rStrm.ReadInt16();
+ maData.mnGap = rStrm.ReadInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ case EXC_ID_CHLINE:
+ case EXC_ID_CHAREA:
+ case EXC_ID_CHRADARLINE:
+ case EXC_ID_CHRADARAREA:
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ case EXC_ID_CHPIE:
+ maData.mnRotation = rStrm.ReaduInt16();
+ maData.mnPieHole = rStrm.ReaduInt16();
+ if( GetBiff() == EXC_BIFF8 )
+ maData.mnFlags = rStrm.ReaduInt16();
+ else
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHPIEEXT:
+ maData.mnRotation = 0;
+ maData.mnPieHole = 0;
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHSCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maData.mnBubbleSize = rStrm.ReaduInt16();
+ maData.mnBubbleType = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ }
+ else
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHSURFACE:
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ default:
+ bKnownType = false;
+ }
+
+ if( bKnownType )
+ mnRecId = nRecId;
+}
+
+void XclImpChType::Finalize( bool bStockChart )
+{
+ switch( mnRecId )
+ {
+ case EXC_ID_CHLINE:
+ maTypeInfo = GetChartTypeInfo( bStockChart ?
+ EXC_CHTYPEID_STOCK : EXC_CHTYPEID_LINE );
+ break;
+ case EXC_ID_CHBAR:
+ maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
+ maData.mnFlags, EXC_CHBAR_HORIZONTAL,
+ EXC_CHTYPEID_HORBAR, EXC_CHTYPEID_BAR ) );
+ break;
+ case EXC_ID_CHPIE:
+ maTypeInfo = GetChartTypeInfo( (maData.mnPieHole > 0) ?
+ EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
+ break;
+ case EXC_ID_CHSCATTER:
+ maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
+ maData.mnFlags, EXC_CHSCATTER_BUBBLES,
+ EXC_CHTYPEID_BUBBLES, EXC_CHTYPEID_SCATTER ) );
+ break;
+ default:
+ maTypeInfo = GetChartTypeInfo( mnRecId );
+ }
+
+ switch( maTypeInfo.meTypeId )
+ {
+ case EXC_CHTYPEID_PIEEXT:
+ case EXC_CHTYPEID_BUBBLES:
+ case EXC_CHTYPEID_SURFACE:
+ case EXC_CHTYPEID_UNKNOWN:
+ GetTracer().TraceChartUnKnownType();
+ break;
+ default:;
+ }
+}
+
+bool XclImpChType::IsStacked() const
+{
+ bool bStacked = false;
+ if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ bStacked =
+ ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
+ !::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ bStacked =
+ ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
+ !::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
+ break;
+ default:;
+ }
+ return bStacked;
+}
+
+bool XclImpChType::IsPercent() const
+{
+ bool bPercent = false;
+ if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ bPercent =
+ ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
+ ::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ bPercent =
+ ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
+ ::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
+ break;
+ default:;
+ }
+ return bPercent;
+}
+
+bool XclImpChType::HasCategoryLabels() const
+{
+ // radar charts disable category labels in chart type, not in CHTICK of X axis
+ return (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) || ::get_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS );
+}
+
+Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const
+{
+ // create the coordinate system object
+ Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XCoordinateSystem > xCoordSystem;
+ if( maTypeInfo.mbPolarCoordSystem )
+ {
+ if( b3dChart )
+ xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
+ else
+ xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
+ }
+ else
+ {
+ if( b3dChart )
+ xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
+ else
+ xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
+ }
+
+ // swap X and Y axis
+ if( maTypeInfo.mbSwappedAxesSet )
+ {
+ ScfPropertySet aCoordSysProp( xCoordSystem );
+ aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true );
+ }
+
+ return xCoordSystem;
+}
+
+Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > const & xDiagram, bool b3dChart ) const
+{
+ OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
+ Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY );
+
+ // additional properties
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_BAR:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ Sequence< sal_Int32 > aInt32Seq{ -maData.mnOverlap, -maData.mnOverlap };
+ aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq );
+ aInt32Seq = { maData.mnGap, maData.mnGap };
+ aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq );
+ }
+ break;
+ case EXC_CHTYPECATEG_PIE:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_USERINGS, maTypeInfo.meTypeId == EXC_CHTYPEID_DONUT );
+ /* #i85166# starting angle of first pie slice. 3D pie charts use Y
+ rotation setting in view3D element. Of-pie charts do not
+ support pie rotation. */
+ if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) )
+ {
+ ScfPropertySet aDiaProp( xDiagram );
+ XclImpChRoot::ConvertPieRotation( aDiaProp, maData.mnRotation );
+ }
+ }
+ break;
+ default:;
+ }
+
+ return xChartType;
+}
+
+void XclImpChChart3d::ReadChChart3d( XclImpStream& rStrm )
+{
+ maData.mnRotation = rStrm.ReaduInt16();
+ maData.mnElevation = rStrm.ReadInt16();
+ maData.mnEyeDist = rStrm.ReaduInt16();
+ maData.mnRelHeight = rStrm.ReaduInt16();
+ maData.mnRelDepth = rStrm.ReaduInt16();
+ maData.mnDepthGap = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const
+{
+ namespace cssd = ::com::sun::star::drawing;
+
+// #i104057# do not assert this, written by broken external generators
+// OSL_ENSURE( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" );
+
+ sal_Int32 nRotationY = 0;
+ sal_Int32 nRotationX = 0;
+ sal_Int32 nPerspective = 15;
+ bool bRightAngled = false;
+ cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE;
+ Color aAmbientColor, aLightColor;
+
+ if( b3dWallChart )
+ {
+ // Y rotation (Excel [0..359], Chart2 [-179,180])
+ nRotationY = NormAngle180<sal_Int32>(maData.mnRotation);
+ // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
+ nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 );
+ // perspective (Excel and Chart2 [0,100])
+ nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
+ // right-angled axes
+ bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D );
+ // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
+ bool bParallel = bRightAngled || (nPerspective == 0);
+ eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
+ // ambient color (Gray 20%)
+ aAmbientColor = Color( 204, 204, 204 );
+ // light color (Gray 60%)
+ aLightColor = Color( 102, 102, 102 );
+ }
+ else
+ {
+ // Y rotation not used in pie charts, but 'first pie slice angle'
+ nRotationY = 0;
+ XclImpChRoot::ConvertPieRotation( rPropSet, maData.mnRotation );
+ // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10])
+ nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90;
+ // perspective (Excel and Chart2 [0,100])
+ nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
+ // no right-angled axes in pie charts, but parallel projection
+ bRightAngled = false;
+ eProjMode = cssd::ProjectionMode_PARALLEL;
+ // ambient color (Gray 30%)
+ aAmbientColor = Color( 179, 179, 179 );
+ // light color (Gray 70%)
+ aLightColor = Color( 76, 76, 76 );
+ }
+
+ // properties
+ rPropSet.SetProperty( EXC_CHPROP_3DRELATIVEHEIGHT, static_cast<sal_Int32>(maData.mnRelHeight / 2)); // seems to be 200%, change to 100%
+ rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY );
+ rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX );
+ rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective );
+ rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled );
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode );
+
+ // light settings
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT );
+ rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor );
+ rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON1, false );
+ rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON2, true );
+ rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor );
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
+}
+
+XclImpChLegend::XclImpChLegend( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChLegend::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ rStrm >> maData.maRect;
+ maData.mnDockMode = rStrm.ReaduInt8();
+ maData.mnSpacing = rStrm.ReaduInt8();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ // trace unsupported features
+ if( GetTracer().IsEnabled() )
+ {
+ if( maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
+ GetTracer().TraceChartLegendPosition();
+ if( ::get_flag( maData.mnFlags, EXC_CHLEGEND_DATATABLE ) )
+ GetTracer().TraceChartDataTable();
+ }
+}
+
+void XclImpChLegend::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ mxText = std::make_shared<XclImpChText>( GetChRoot() );
+ mxText->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChLegend::Finalize()
+{
+ // legend default formatting differs in OOChart and Excel, missing frame means automatic
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ // Update text formatting. If mxText is empty, the passed default text is used.
+ lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) );
+}
+
+Reference< XLegend > XclImpChLegend::CreateLegend() const
+{
+ Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY );
+ if( xLegend.is() )
+ {
+ ScfPropertySet aLegendProp( xLegend );
+ aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true );
+
+ // frame properties
+ if( mxFrame )
+ mxFrame->Convert( aLegendProp );
+ // text properties
+ if( mxText )
+ mxText->ConvertFont( aLegendProp );
+
+ /* Legend position and size. Default positions are used only if the
+ plot area is positioned automatically (Excel sets the plot area to
+ manual mode, if the legend is moved or resized). With manual plot
+ areas, Excel ignores the value in maData.mnDockMode completely. */
+ cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
+ cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode )
+ {
+ case EXC_CHLEGEND_LEFT:
+ eApiPos = cssc2::LegendPosition_LINE_START;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ break;
+ case EXC_CHLEGEND_RIGHT:
+ // top-right not supported
+ case EXC_CHLEGEND_CORNER:
+ eApiPos = cssc2::LegendPosition_LINE_END;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ break;
+ case EXC_CHLEGEND_TOP:
+ eApiPos = cssc2::LegendPosition_PAGE_START;
+ eApiExpand = cssc::ChartLegendExpansion_WIDE;
+ break;
+ case EXC_CHLEGEND_BOTTOM:
+ eApiPos = cssc2::LegendPosition_PAGE_END;
+ eApiExpand = cssc::ChartLegendExpansion_WIDE;
+ break;
+ }
+
+ // no automatic position/size: try to find the correct position and size
+ if( GetChartData().IsManualPlotArea() || maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
+ {
+ const XclChFramePos* pFramePos = mxFramePos ? &mxFramePos->GetFramePosData() : nullptr;
+
+ /* Legend position. Only the settings from the CHFRAMEPOS record
+ are used by Excel, the position in the CHLEGEND record will be
+ ignored. */
+ if( pFramePos )
+ {
+ RelativePosition aRelPos(
+ CalcRelativeFromChartX( pFramePos->maRect.mnX ),
+ CalcRelativeFromChartY( pFramePos->maRect.mnY ),
+ css::drawing::Alignment_TOP_LEFT );
+ aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos );
+ }
+ else
+ {
+ // no manual position/size found, just go for the default
+ eApiPos = cssc2::LegendPosition_LINE_END;
+ }
+
+ /* Legend size. The member mnBRMode specifies whether size is
+ automatic or changes manually. Manual size is given in points,
+ not in chart units. */
+ if( pFramePos && (pFramePos->mnBRMode == EXC_CHFRAMEPOS_ABSSIZE_POINTS) &&
+ (pFramePos->maRect.mnWidth > 0) && (pFramePos->maRect.mnHeight > 0) )
+ {
+ eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ sal_Int32 nWidthHmm = o3tl::convert(pFramePos->maRect.mnWidth, o3tl::Length::pt, o3tl::Length::mm100);
+ sal_Int32 nHeightHmm = o3tl::convert(pFramePos->maRect.mnHeight, o3tl::Length::pt, o3tl::Length::mm100);
+ RelativeSize aRelSize( CalcRelativeFromHmmX( nWidthHmm ), CalcRelativeFromHmmY( nHeightHmm ) );
+ aLegendProp.SetProperty( EXC_CHPROP_RELATIVESIZE, aRelSize );
+ }
+ else
+ {
+ // automatic size: determine entry direction from flags
+ eApiExpand = ::get_flagvalue( maData.mnFlags, EXC_CHLEGEND_STACKED,
+ cssc::ChartLegendExpansion_HIGH, cssc::ChartLegendExpansion_WIDE );
+ }
+ }
+ aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos );
+ aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand );
+ }
+ return xLegend;
+}
+
+XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) :
+ mnDropBar( nDropBar ),
+ mnBarDist( 0 )
+{
+}
+
+void XclImpChDropBar::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ mnBarDist = rStrm.ReaduInt16();
+}
+
+void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
+{
+ XclChObjectType eObjType = EXC_CHOBJTYPE_BACKGROUND;
+ switch( mnDropBar )
+ {
+ case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break;
+ case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break;
+ }
+ ConvertFrameBase( rRoot, rPropSet, eObjType );
+}
+
+XclImpChTypeGroup::XclImpChTypeGroup( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ maType( rRoot ),
+ maTypeInfo( maType.GetTypeInfo() )
+{
+ // Initialize unused format indexes set. At this time, all formats are unused.
+ for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx )
+ maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx );
+}
+
+void XclImpChTypeGroup::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 16 );
+ maData.mnFlags = rStrm.ReaduInt16();
+ maData.mnGroupIdx = rStrm.ReaduInt16();
+}
+
+void XclImpChTypeGroup::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHCHART3D:
+ mxChart3d = std::make_shared<XclImpChChart3d>();
+ mxChart3d->ReadChChart3d( rStrm );
+ break;
+ case EXC_ID_CHLEGEND:
+ mxLegend = std::make_shared<XclImpChLegend>( GetChRoot() );
+ mxLegend->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHDEFAULTTEXT:
+ GetChartData().ReadChDefaultText( rStrm );
+ break;
+ case EXC_ID_CHDROPBAR:
+ ReadChDropBar( rStrm );
+ break;
+ case EXC_ID_CHCHARTLINE:
+ ReadChChartLine( rStrm );
+ break;
+ case EXC_ID_CHDATAFORMAT:
+ ReadChDataFormat( rStrm );
+ break;
+ default:
+ maType.ReadChType( rStrm );
+ }
+}
+
+void XclImpChTypeGroup::Finalize()
+{
+ // check and set valid chart type
+ bool bStockChart =
+ (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart
+ !mxChart3d && // must be a 2d chart
+ m_ChartLines.find(EXC_CHCHARTLINE_HILO) != m_ChartLines.end() && // must contain hi-lo lines
+ (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count
+ maType.Finalize( bStockChart );
+
+ // extended type info
+ maTypeInfo.Set( maType.GetTypeInfo(), static_cast< bool >(mxChart3d), false );
+
+ // reverse series order for some unstacked 2D chart types
+ if( maTypeInfo.mbReverseSeries && !Is3dChart() && !maType.IsStacked() && !maType.IsPercent() )
+ ::std::reverse( maSeries.begin(), maSeries.end() );
+
+ // update chart type group format, may depend on chart type finalized above
+ if( mxGroupFmt )
+ mxGroupFmt->UpdateGroupFormat( maTypeInfo );
+}
+
+void XclImpChTypeGroup::AddSeries( XclImpChSeriesRef const & xSeries )
+{
+ if( xSeries )
+ maSeries.push_back( xSeries );
+ // store first inserted series separately, series order may be reversed later
+ if( !mxFirstSeries )
+ mxFirstSeries = xSeries;
+}
+
+void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx )
+{
+ maUnusedFormats.erase( nFormatIdx );
+}
+
+sal_uInt16 XclImpChTypeGroup::PopUnusedFormatIndex()
+{
+ OSL_ENSURE( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" );
+ sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin();
+ SetUsedFormatIndex( nFormatIdx );
+ return nFormatIdx;
+}
+
+bool XclImpChTypeGroup::HasVarPointFormat() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS ) &&
+ ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed
+ ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series?
+ (maSeries.size() == 1)));
+}
+
+bool XclImpChTypeGroup::HasConnectorLines() const
+{
+ // existence of connector lines (only in stacked bar charts)
+ if ( !(maType.IsStacked() || maType.IsPercent()) || (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
+ return false;
+ XclImpChLineFormatMap::const_iterator aConLine = m_ChartLines.find(EXC_CHCHARTLINE_CONNECT);
+ return (aConLine != m_ChartLines.end() && aConLine->second.HasLine());
+}
+
+OUString XclImpChTypeGroup::GetSingleSeriesTitle() const
+{
+ // no automatic title for series with trendlines or error bars
+ // pie charts always show an automatic title, even if more series exist
+ return (mxFirstSeries && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ?
+ mxFirstSeries->GetTitle() : OUString();
+}
+
+void XclImpChTypeGroup::ConvertChart3d( ScfPropertySet& rPropSet ) const
+{
+ if( mxChart3d )
+ mxChart3d->Convert( rPropSet, Is3dWallChart() );
+}
+
+Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const
+{
+ return maType.CreateCoordSystem( Is3dChart() );
+}
+
+Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const
+{
+ OSL_ENSURE( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" );
+
+ // create the chart type object
+ Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() );
+
+ // bar chart connector lines
+ if( HasConnectorLines() )
+ {
+ ScfPropertySet aDiaProp( xDiagram );
+ aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true );
+ }
+
+ /* Stock chart needs special processing. Create one 'big' series with
+ data sequences of different roles. */
+ if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
+ CreateStockSeries( xChartType, nApiAxesSetIdx );
+ else
+ CreateDataSeries( xChartType, nApiAxesSetIdx );
+
+ return xChartType;
+}
+
+Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const
+{
+ Reference< XLabeledDataSequence > xLabeledSeq;
+ // create category sequence from first visible series
+ if( mxFirstSeries )
+ xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG );
+ return xLabeledSeq;
+}
+
+void XclImpChTypeGroup::ReadChDropBar( XclImpStream& rStrm )
+{
+ if (m_DropBars.find(EXC_CHDROPBAR_UP) == m_DropBars.end())
+ {
+ unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_UP));
+ p->ReadRecordGroup(rStrm);
+ m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_UP, std::move(p)));
+ }
+ else if (m_DropBars.find(EXC_CHDROPBAR_DOWN) == m_DropBars.end())
+ {
+ unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_DOWN));
+ p->ReadRecordGroup(rStrm);
+ m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_DOWN, std::move(p)));
+ }
+}
+
+void XclImpChTypeGroup::ReadChChartLine( XclImpStream& rStrm )
+{
+ sal_uInt16 nLineId = rStrm.ReaduInt16();
+ if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() )
+ {
+ XclImpChLineFormat aLineFmt;
+ aLineFmt.ReadChLineFormat( rStrm );
+ m_ChartLines[ nLineId ] = aLineFmt;
+ }
+}
+
+void XclImpChTypeGroup::ReadChDataFormat( XclImpStream& rStrm )
+{
+ // global series and data point format
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->ReadRecordGroup( rStrm );
+ const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
+ if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) &&
+ (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) )
+ mxGroupFmt = xDataFmt;
+}
+
+void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > const & xChartType,
+ Reference< XDataSeries > const & xSeries, sal_Int32 nApiAxesSetIdx ) const
+{
+ Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
+ if( !(xSeriesCont.is() && xSeries.is()) )
+ return;
+
+ // series stacking mode
+ cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING;
+ // stacked overrides deep-3d
+ if( maType.IsStacked() || maType.IsPercent() )
+ eStacking = cssc2::StackingDirection_Y_STACKING;
+ else if( Is3dDeepChart() )
+ eStacking = cssc2::StackingDirection_Z_STACKING;
+
+ // additional series properties
+ ScfPropertySet aSeriesProp( xSeries );
+ aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking );
+ aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx );
+
+ // insert series into container
+ try
+ {
+ xSeriesCont->addDataSeries( xSeries );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" );
+ }
+}
+
+void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
+{
+ bool bSpline = false;
+ for (auto const& elem : maSeries)
+ {
+ Reference< XDataSeries > xDataSeries = elem->CreateDataSeries();
+ InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
+ bSpline |= elem->HasSpline();
+ }
+ // spline - TODO: set at single series (#i66858#)
+ if( bSpline && !maTypeInfo.IsSeriesFrameFormat() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) )
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, css::chart2::CurveStyle_CUBIC_SPLINES );
+ }
+}
+
+void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
+{
+ // create the data series object
+ Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
+ Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
+ if( !xDataSink.is() )
+ return;
+
+ // create a list of data sequences from all series
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ OSL_ENSURE( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" );
+ int nRoleIdx = (maSeries.size() == 3) ? 1 : 0;
+ for( const auto& rxSeries : maSeries )
+ {
+ // create a data sequence with a specific role
+ OUString aRole;
+ switch( nRoleIdx )
+ {
+ case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break;
+ case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break;
+ case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break;
+ case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break;
+ }
+ Reference< XLabeledDataSequence > xDataSeq = rxSeries->CreateValueSequence( aRole );
+ if( xDataSeq.is() )
+ aLabeledSeqVec.push_back( xDataSeq );
+ ++nRoleIdx;
+ if (nRoleIdx >= 4)
+ break;
+ }
+
+ // attach labeled data sequences to series and insert series into chart type
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+
+ // formatting of special stock chart elements
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_JAPANESE, HasDropBars() );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWFIRST, HasDropBars() );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true );
+ // hi-lo line format
+ XclImpChLineFormatMap::const_iterator aHiLoLine = m_ChartLines.find( EXC_CHCHARTLINE_HILO );
+ if (aHiLoLine != m_ChartLines.end())
+ {
+ ScfPropertySet aSeriesProp( xDataSeries );
+ aHiLoLine->second.Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
+ }
+ // white dropbar format
+ XclImpChDropBarMap::const_iterator itr = m_DropBars.find(EXC_CHDROPBAR_UP);
+ Reference<XPropertySet> xWhitePropSet;
+ if (itr != m_DropBars.end() && aTypeProp.GetProperty(xWhitePropSet, EXC_CHPROP_WHITEDAY))
+ {
+ ScfPropertySet aBarProp( xWhitePropSet );
+ itr->second->Convert(GetChRoot(), aBarProp);
+ }
+ // black dropbar format
+ itr = m_DropBars.find(EXC_CHDROPBAR_DOWN);
+ Reference<XPropertySet> xBlackPropSet;
+ if (itr != m_DropBars.end() && aTypeProp.GetProperty(xBlackPropSet, EXC_CHPROP_BLACKDAY))
+ {
+ ScfPropertySet aBarProp( xBlackPropSet );
+ itr->second->Convert(GetChRoot(), aBarProp);
+ }
+
+ // insert the series into the chart type object
+ InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
+}
+
+// Axes =======================================================================
+
+XclImpChLabelRange::XclImpChLabelRange( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChLabelRange::ReadChLabelRange( XclImpStream& rStrm )
+{
+ maLabelData.mnCross = rStrm.ReaduInt16();
+ maLabelData.mnLabelFreq = rStrm.ReaduInt16();
+ maLabelData.mnTickFreq = rStrm.ReaduInt16();
+ maLabelData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChLabelRange::ReadChDateRange( XclImpStream& rStrm )
+{
+ maDateData.mnMinDate = rStrm.ReaduInt16();
+ maDateData.mnMaxDate = rStrm.ReaduInt16();
+ maDateData.mnMajorStep = rStrm.ReaduInt16();
+ maDateData.mnMajorUnit = rStrm.ReaduInt16();
+ maDateData.mnMinorStep = rStrm.ReaduInt16();
+ maDateData.mnMinorUnit = rStrm.ReaduInt16();
+ maDateData.mnBaseUnit = rStrm.ReaduInt16();
+ maDateData.mnCross = rStrm.ReaduInt16();
+ maDateData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const
+{
+ // automatic axis type detection
+ rScaleData.AutoDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE );
+
+ // the flag EXC_CHDATERANGE_DATEAXIS specifies whether this is a date axis
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
+ {
+ /* Chart2 requires axis type CATEGORY for automatic category/date axis
+ (even if it is a date axis currently). */
+ rScaleData.AxisType = rScaleData.AutoDateAxis ? cssc2::AxisType::CATEGORY : cssc2::AxisType::DATE;
+ rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
+ /* Min/max values depend on base time unit, they specify the number of
+ days, months, or years starting from null date. */
+ lclConvertTimeValue( GetRoot(), rScaleData.Minimum, maDateData.mnMinDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN ), maDateData.mnBaseUnit );
+ lclConvertTimeValue( GetRoot(), rScaleData.Maximum, maDateData.mnMaxDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX ), maDateData.mnBaseUnit );
+ // increment
+ cssc::TimeIncrement& rTimeIncrement = rScaleData.TimeIncrement;
+ lclConvertTimeInterval( rTimeIncrement.MajorTimeInterval, maDateData.mnMajorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR ), maDateData.mnMajorUnit );
+ lclConvertTimeInterval( rTimeIncrement.MinorTimeInterval, maDateData.mnMinorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR ), maDateData.mnMinorUnit );
+ // base unit
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE ) )
+ rTimeIncrement.TimeResolution.clear();
+ else
+ rTimeIncrement.TimeResolution <<= lclGetApiTimeUnit( maDateData.mnBaseUnit );
+ }
+ else
+ {
+ // do not overlap text unless all labels are visible
+ rPropSet.SetBoolProperty( EXC_CHPROP_TEXTOVERLAP, maLabelData.mnLabelFreq == 1 );
+ // do not break text into several lines unless all labels are visible
+ rPropSet.SetBoolProperty( EXC_CHPROP_TEXTBREAK, maLabelData.mnLabelFreq == 1 );
+ // do not stagger labels in two lines
+ rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE );
+ }
+
+ // reverse order
+ bool bReverse = ::get_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient;
+ rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
+
+ //TODO #i58731# show n-th category
+}
+
+void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const
+{
+ /* Crossing mode (max-cross flag overrides other crossing settings). Excel
+ does not move the Y axis in 3D charts, regardless of actual settings.
+ But: the Y axis has to be moved to "end", if the X axis is mirrored,
+ to keep it at the left end of the chart. */
+ bool bMaxCross = ::get_flag( maLabelData.mnFlags, b3dChart ? EXC_CHLABELRANGE_REVERSE : EXC_CHLABELRANGE_MAXCROSS );
+ cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
+
+ // crossing position (depending on axis type text/date)
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
+ {
+ bool bAutoCross = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ /* Crossing position value depends on base time unit, it specifies the
+ number of days, months, or years from null date. Note that Excel
+ 2007/2010 write broken BIFF8 files, they always stores the number
+ of days regardless of the base time unit (and they are reading it
+ the same way, thus wrongly displaying files written by Excel
+ 97-2003). This filter sticks to the correct behaviour of Excel
+ 97-2003. */
+ double fCrossingPos = bAutoCross ? 1.0 : lclGetSerialDay( GetRoot(), maDateData.mnCross, maDateData.mnBaseUnit );
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+ }
+ else
+ {
+ double fCrossingPos = b3dChart ? 1.0 : maLabelData.mnCross;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+ }
+}
+
+XclImpChValueRange::XclImpChValueRange( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChValueRange::ReadChValueRange( XclImpStream& rStrm )
+{
+ maData.mfMin = rStrm.ReadDouble();
+ maData.mfMax = rStrm.ReadDouble();
+ maData.mfMajorStep = rStrm.ReadDouble();
+ maData.mfMinorStep = rStrm.ReadDouble();
+ maData.mfCross = rStrm.ReadDouble();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const
+{
+ // scaling algorithm
+ const bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
+ if( bLogScale )
+ rScaleData.Scaling = css::chart2::LogarithmicScaling::create( comphelper::getProcessComponentContext() );
+ else
+ rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
+
+ // min/max
+ lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) );
+ lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) );
+
+ // increment
+ bool bAutoMajor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR );
+ bool bAutoMinor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR );
+ // major increment
+ IncrementData& rIncrementData = rScaleData.IncrementData;
+ lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor );
+ // minor increment
+ Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
+ rSubIncrementSeq.realloc( 1 );
+ Any& rIntervalCount = rSubIncrementSeq.getArray()[ 0 ].IntervalCount;
+ rIntervalCount.clear();
+ if( bLogScale )
+ {
+ if( !bAutoMinor )
+ rIntervalCount <<= sal_Int32( 9 );
+ }
+ else if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) )
+ {
+ double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5;
+ if( (1.0 <= fCount) && (fCount < 1001.0) )
+ rIntervalCount <<= static_cast< sal_Int32 >( fCount );
+ }
+ else if( bAutoMinor )
+ {
+ // tdf#114168 If minor unit is not set then set interval to 5, as MS Excel do.
+ rIntervalCount <<= static_cast< sal_Int32 >( 5 );
+ }
+
+ // reverse order
+ bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient;
+ rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
+}
+
+void XclImpChValueRange::ConvertAxisPosition( ScfPropertySet& rPropSet ) const
+{
+ bool bMaxCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
+ bool bAutoCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
+
+ // crossing mode (max-cross flag overrides other crossing settings)
+ cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
+
+ // crossing position
+ double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross;
+ if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos );
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+}
+
+namespace {
+
+sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos )
+{
+ using namespace ::com::sun::star::chart2::TickmarkStyle;
+ sal_Int32 nApiTickmarks = css::chart2::TickmarkStyle::NONE;
+ ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) );
+ ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) );
+ return nApiTickmarks;
+}
+
+cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos )
+{
+ using namespace ::com::sun::star::chart;
+ switch( nXclLabelPos )
+ {
+ case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START;
+ case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END;
+ case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS;
+ }
+ return ChartAxisLabelPosition_NEAR_AXIS;
+}
+
+} // namespace
+
+XclImpChTick::XclImpChTick( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChTick::ReadChTick( XclImpStream& rStrm )
+{
+ maData.mnMajor = rStrm.ReaduInt8();
+ maData.mnMinor = rStrm.ReaduInt8();
+ maData.mnLabelPos = rStrm.ReaduInt8();
+ maData.mnBackMode = rStrm.ReaduInt8();
+ rStrm.Ignore( 16 );
+ Color aColor;
+ rStrm >> aColor;
+ maData.maTextComplexColor.setColor(aColor);
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ maData.maTextComplexColor.setColor(GetPalette().GetColor(rStrm.ReaduInt16()));
+ // rotation
+ maData.mnRotation = rStrm.ReaduInt16();
+ }
+ else
+ {
+ // BIFF2-BIFF7: get rotation from text orientation
+ sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 );
+ maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
+ }
+}
+
+Color XclImpChTick::GetFontColor() const
+{
+ return ::get_flag(maData.mnFlags, EXC_CHTICK_AUTOCOLOR) ? GetFontAutoColor() : maData.maTextComplexColor.getFinalColor();
+}
+
+sal_uInt16 XclImpChTick::GetRotation() const
+{
+ /* n#720443: Ignore auto-rotation if there is a suggested rotation.
+ * Better fix would be to improve our axis auto rotation algorithm.
+ */
+ if( maData.mnRotation != EXC_ROT_NONE )
+ return maData.mnRotation;
+ return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOROT ) ? EXC_CHART_AUTOROTATION : maData.mnRotation;
+}
+
+void XclImpChTick::Convert( ScfPropertySet& rPropSet ) const
+{
+ rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) );
+ rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) );
+ rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) );
+ rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS );
+}
+
+XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) :
+ XclImpChRoot( rRoot ),
+ mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
+{
+ maData.mnType = nAxisType;
+}
+
+void XclImpChAxis::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnType = rStrm.ReaduInt16();
+}
+
+void XclImpChAxis::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHLABELRANGE:
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ mxLabelRange->ReadChLabelRange( rStrm );
+ break;
+ case EXC_ID_CHDATERANGE:
+ if( !mxLabelRange )
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ mxLabelRange->ReadChDateRange( rStrm );
+ break;
+ case EXC_ID_CHVALUERANGE:
+ mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
+ mxValueRange->ReadChValueRange( rStrm );
+ break;
+ case EXC_ID_CHFORMAT:
+ mnNumFmtIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHTICK:
+ mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
+ mxTick->ReadChTick( rStrm );
+ break;
+ case EXC_ID_CHFONT:
+ mxFont = std::make_shared<XclImpChFont>();
+ mxFont->ReadChFont( rStrm );
+ break;
+ case EXC_ID_CHAXISLINE:
+ ReadChAxisLine( rStrm );
+ break;
+ }
+}
+
+void XclImpChAxis::Finalize()
+{
+ // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts
+ if( !mxLabelRange )
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ if( !mxValueRange )
+ mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
+ // remove invisible grid lines completely
+ if( mxMajorGrid && !mxMajorGrid->HasLine() )
+ mxMajorGrid.clear();
+ if( mxMinorGrid && !mxMinorGrid->HasLine() )
+ mxMinorGrid.clear();
+ // default tick settings different in OOChart and Excel
+ if( !mxTick )
+ mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
+ // #i4140# different default axis line color
+ if( !mxAxisLine )
+ {
+ XclChLineFormat aLineFmt;
+ // set "show axis" flag, default if line format record is missing
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS );
+ mxAxisLine = new XclImpChLineFormat( aLineFmt );
+ }
+ // add wall/floor frame for 3d charts
+ if( !mxWallFrame )
+ CreateWallFrame();
+}
+
+sal_uInt16 XclImpChAxis::GetFontIndex() const
+{
+ return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+Color XclImpChAxis::GetFontColor() const
+{
+ return mxTick ? mxTick->GetFontColor() : GetFontAutoColor();
+}
+
+sal_uInt16 XclImpChAxis::GetRotation() const
+{
+ return mxTick ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION;
+}
+
+Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const
+{
+ // create the axis object (always)
+ Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY );
+ if( xAxis.is() )
+ {
+ ScfPropertySet aAxisProp( xAxis );
+ // #i58688# axis enabled
+ aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, !mxAxisLine || mxAxisLine->IsShowAxis() );
+
+ // axis line properties
+ if( mxAxisLine )
+ mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
+ // axis ticks properties
+ if( mxTick )
+ mxTick->Convert( aAxisProp );
+
+ // axis caption text --------------------------------------------------
+
+ // radar charts disable their category labels via chart type, not via axis
+ bool bHasLabels = (!mxTick || mxTick->HasLabels()) &&
+ ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels());
+ aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels );
+ if( bHasLabels )
+ {
+ // font settings from CHFONT record or from default text
+ if( mxFont )
+ ConvertFontBase( GetChRoot(), aAxisProp );
+ else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ) )
+ pDefText->ConvertFont( aAxisProp );
+ // label text rotation
+ ConvertRotationBase( aAxisProp, true );
+ // number format
+ bool bLinkNumberFmtToSource = true;
+ if ( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
+ {
+ sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx );
+ if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) );
+ bLinkNumberFmtToSource = false;
+ }
+ }
+
+ aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT_LINKSRC, bLinkNumberFmtToSource );
+ }
+
+ // axis scaling and increment -----------------------------------------
+
+ const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo();
+ ScaleData aScaleData = xAxis->getScaleData();
+ // set axis type
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ if( rTypeInfo.mbCategoryAxis )
+ {
+ aScaleData.AxisType = cssc2::AxisType::CATEGORY;
+ aScaleData.Categories = rTypeGroup.CreateCategSequence();
+ }
+ else
+ aScaleData.AxisType = cssc2::AxisType::REALNUMBER;
+ break;
+ case EXC_CHAXIS_Y:
+ aScaleData.AxisType = rTypeGroup.IsPercent() ?
+ cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER;
+ break;
+ case EXC_CHAXIS_Z:
+ aScaleData.AxisType = cssc2::AxisType::SERIES;
+ break;
+ }
+ // axis scaling settings, dependent on axis type
+ switch( aScaleData.AxisType )
+ {
+ case cssc2::AxisType::CATEGORY:
+ case cssc2::AxisType::SERIES:
+ // #i71684# radar charts have reversed rotation direction
+ if (mxLabelRange)
+ mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR );
+ else
+ SAL_WARN("sc.filter", "missing LabelRange");
+ break;
+ case cssc2::AxisType::REALNUMBER:
+ case cssc2::AxisType::PERCENT:
+ // #i85167# pie/donut charts have reversed rotation direction (at Y axis!)
+ if (mxValueRange)
+ mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
+ else
+ SAL_WARN("sc.filter", "missing ValueRange");
+ break;
+ default:
+ OSL_FAIL( "XclImpChAxis::CreateAxis - unknown axis type" );
+ }
+
+ /* Do not set a value to the Origin member anymore (will be done via
+ new axis properties 'CrossoverPosition' and 'CrossoverValue'). */
+ aScaleData.Origin.clear();
+
+ // write back
+ xAxis->setScaleData( aScaleData );
+
+ // grid ---------------------------------------------------------------
+
+ // main grid
+ ScfPropertySet aGridProp( xAxis->getGridProperties() );
+ aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMajorGrid) );
+ if( mxMajorGrid )
+ mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ // sub grid
+ Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
+ if( aSubGridPropSeq.hasElements() )
+ {
+ ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
+ aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMinorGrid) );
+ if( mxMinorGrid )
+ mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ }
+
+ // position of crossing axis ------------------------------------------
+
+ if( pCrossingAxis )
+ pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup );
+ }
+ return xAxis;
+}
+
+void XclImpChAxis::ConvertWall( ScfPropertySet& rPropSet ) const
+{
+ // #i71810# walls and floor in 3D charts use the CHPICFORMAT record for bitmap mode
+ if( mxWallFrame )
+ mxWallFrame->Convert( rPropSet, true );
+}
+
+void XclImpChAxis::ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const
+{
+ if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) )
+ {
+ if (mxLabelRange)
+ mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() );
+ else
+ SAL_WARN("sc.filter", "missing LabelRange");
+ }
+ else
+ {
+ if (mxValueRange)
+ mxValueRange->ConvertAxisPosition( rPropSet );
+ else
+ SAL_WARN("sc.filter", "missing ValueRange");
+ }
+}
+
+void XclImpChAxis::ReadChAxisLine( XclImpStream& rStrm )
+{
+ XclImpChLineFormatRef* pxLineFmt = nullptr;
+ bool bWallFrame = false;
+ switch( rStrm.ReaduInt16() )
+ {
+ case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break;
+ case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break;
+ case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break;
+ case EXC_CHAXISLINE_WALLS: bWallFrame = true; break;
+ }
+ if( bWallFrame )
+ CreateWallFrame();
+
+ bool bLoop = pxLineFmt || bWallFrame;
+ while( bLoop )
+ {
+ sal_uInt16 nRecId = rStrm.GetNextRecId();
+ bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) ||
+ (nRecId == EXC_ID_CHAREAFORMAT) ||
+ (nRecId == EXC_ID_CHESCHERFORMAT))
+ && rStrm.StartNextRecord();
+ if( bLoop )
+ {
+ if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) )
+ {
+ (*pxLineFmt) = new XclImpChLineFormat();
+ (*pxLineFmt)->ReadChLineFormat( rStrm );
+ }
+ else if( bWallFrame && mxWallFrame )
+ {
+ mxWallFrame->ReadSubRecord( rStrm );
+ }
+ }
+ }
+}
+
+void XclImpChAxis::CreateWallFrame()
+{
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_WALL3D );
+ break;
+ case EXC_CHAXIS_Y:
+ mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D );
+ break;
+ default:
+ mxWallFrame.reset();
+ }
+}
+
+XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
+ XclImpChRoot( rRoot )
+{
+ maData.mnAxesSetId = nAxesSetId;
+}
+
+void XclImpChAxesSet::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnAxesSetId = rStrm.ReaduInt16();
+ rStrm >> maData.maRect;
+}
+
+void XclImpChAxesSet::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHAXIS:
+ ReadChAxis( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ ReadChText( rStrm );
+ break;
+ case EXC_ID_CHPLOTFRAME:
+ ReadChPlotFrame( rStrm );
+ break;
+ case EXC_ID_CHTYPEGROUP:
+ ReadChTypeGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChAxesSet::Finalize()
+{
+ if( IsValidAxesSet() )
+ {
+ // finalize chart type groups, erase empty groups without series
+ XclImpChTypeGroupMap aValidGroups;
+ for (auto const& typeGroup : maTypeGroups)
+ {
+ XclImpChTypeGroupRef xTypeGroup = typeGroup.second;
+ xTypeGroup->Finalize();
+ if( xTypeGroup->IsValidGroup() )
+ aValidGroups.emplace(typeGroup.first, xTypeGroup);
+ }
+ maTypeGroups.swap( aValidGroups );
+ }
+
+ // invalid chart type groups are deleted now, check again with IsValidAxesSet()
+ if( !IsValidAxesSet() )
+ return;
+
+ // always create missing axis objects
+ if( !mxXAxis )
+ mxXAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_X );
+ if( !mxYAxis )
+ mxYAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Y );
+ if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() )
+ mxZAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Z );
+
+ // finalize axes
+ if( mxXAxis ) mxXAxis->Finalize();
+ if( mxYAxis ) mxYAxis->Finalize();
+ if( mxZAxis ) mxZAxis->Finalize();
+
+ // finalize axis titles
+ const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISTITLE );
+ OUString aAutoTitle(ScResId(STR_AXISTITLE));
+ lclFinalizeTitle( mxXAxisTitle, pDefText, aAutoTitle );
+ lclFinalizeTitle( mxYAxisTitle, pDefText, aAutoTitle );
+ lclFinalizeTitle( mxZAxisTitle, pDefText, aAutoTitle );
+
+ // #i47745# missing plot frame -> invisible border and area
+ if( !mxPlotFrame )
+ mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
+}
+
+XclImpChTypeGroupRef XclImpChAxesSet::GetTypeGroup( sal_uInt16 nGroupIdx ) const
+{
+ XclImpChTypeGroupMap::const_iterator itr = maTypeGroups.find(nGroupIdx);
+ return itr == maTypeGroups.end() ? XclImpChTypeGroupRef() : itr->second;
+}
+
+XclImpChTypeGroupRef XclImpChAxesSet::GetFirstTypeGroup() const
+{
+ XclImpChTypeGroupRef xTypeGroup;
+ if( !maTypeGroups.empty() )
+ xTypeGroup = maTypeGroups.begin()->second;
+ return xTypeGroup;
+}
+
+XclImpChLegendRef XclImpChAxesSet::GetLegend() const
+{
+ XclImpChLegendRef xLegend;
+ for( const auto& rEntry : maTypeGroups )
+ {
+ xLegend = rEntry.second->GetLegend();
+ if (xLegend)
+ break;
+ }
+ return xLegend;
+}
+
+OUString XclImpChAxesSet::GetSingleSeriesTitle() const
+{
+ return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : OUString();
+}
+
+void XclImpChAxesSet::Convert( Reference< XDiagram > const & xDiagram ) const
+{
+ if( !(IsValidAxesSet() && xDiagram.is()) )
+ return;
+
+ // diagram background formatting
+ if( GetAxesSetId() == EXC_CHAXESSET_PRIMARY )
+ ConvertBackground( xDiagram );
+
+ // create the coordinate system, this inserts all chart types and series
+ Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram );
+ if( !xCoordSystem.is() )
+ return;
+
+ // insert coordinate system, if not already done
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW );
+ Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
+ if( !aCoordSystems.hasElements() )
+ xCoordSystemCont->addCoordinateSystem( xCoordSystem );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::Convert - cannot insert coordinate system" );
+ }
+
+ // create the axes with grids and axis titles and insert them into the diagram
+ ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() );
+ ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() );
+ ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, nullptr );
+}
+
+void XclImpChAxesSet::ConvertTitlePositions() const
+{
+ if( mxXAxisTitle )
+ mxXAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_X ) );
+ if( mxYAxisTitle )
+ mxYAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Y ) );
+ if( mxZAxisTitle )
+ mxZAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Z ) );
+}
+
+void XclImpChAxesSet::ReadChAxis( XclImpStream& rStrm )
+{
+ XclImpChAxisRef xAxis = std::make_shared<XclImpChAxis>( GetChRoot() );
+ xAxis->ReadRecordGroup( rStrm );
+
+ switch( xAxis->GetAxisType() )
+ {
+ case EXC_CHAXIS_X: mxXAxis = xAxis; break;
+ case EXC_CHAXIS_Y: mxYAxis = xAxis; break;
+ case EXC_CHAXIS_Z: mxZAxis = xAxis; break;
+ }
+}
+
+void XclImpChAxesSet::ReadChText( XclImpStream& rStrm )
+{
+ XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
+ xText->ReadRecordGroup( rStrm );
+
+ switch( xText->GetLinkTarget() )
+ {
+ case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break;
+ case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break;
+ case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break;
+ }
+}
+
+void XclImpChAxesSet::ReadChPlotFrame( XclImpStream& rStrm )
+{
+ if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() )
+ {
+ mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
+ mxPlotFrame->ReadRecordGroup( rStrm );
+ }
+}
+
+void XclImpChAxesSet::ReadChTypeGroup( XclImpStream& rStrm )
+{
+ XclImpChTypeGroupRef xTypeGroup = std::make_shared<XclImpChTypeGroup>( GetChRoot() );
+ xTypeGroup->ReadRecordGroup( rStrm );
+ sal_uInt16 nGroupIdx = xTypeGroup->GetGroupIdx();
+ XclImpChTypeGroupMap::iterator itr = maTypeGroups.lower_bound(nGroupIdx);
+ if (itr != maTypeGroups.end() && !maTypeGroups.key_comp()(nGroupIdx, itr->first))
+ // Overwrite the existing element.
+ itr->second = xTypeGroup;
+ else
+ maTypeGroups.insert(
+ itr, XclImpChTypeGroupMap::value_type(nGroupIdx, xTypeGroup));
+}
+
+Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > const & xDiagram ) const
+{
+ Reference< XCoordinateSystem > xCoordSystem;
+
+ /* Try to get existing coordinate system. For now, all series from primary
+ and secondary axes sets are inserted into one coordinate system. Later,
+ this should be changed to use one coordinate system for each axes set. */
+ Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY );
+ if( xCoordSystemCont.is() )
+ {
+ Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
+ OSL_ENSURE( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" );
+ if( aCoordSystems.hasElements() )
+ xCoordSystem = aCoordSystems[ 0 ];
+ }
+
+ // create the coordinate system according to the first chart type
+ if( !xCoordSystem.is() )
+ {
+ XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup )
+ {
+ xCoordSystem = xTypeGroup->CreateCoordSystem();
+ // convert 3d chart settings
+ ScfPropertySet aDiaProp( xDiagram );
+ xTypeGroup->ConvertChart3d( aDiaProp );
+ }
+ }
+
+ /* Create XChartType objects for all chart type groups. Each group will
+ add its series to the data provider attached to the chart document. */
+ Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
+ if( xChartTypeCont.is() )
+ {
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ for( const auto& rEntry : maTypeGroups )
+ {
+ try
+ {
+ Reference< XChartType > xChartType = rEntry.second->CreateChartType( xDiagram, nApiAxesSetIdx );
+ if( xChartType.is() )
+ xChartTypeCont->addChartType( xChartType );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" );
+ }
+ }
+ }
+
+ return xCoordSystem;
+}
+
+void XclImpChAxesSet::ConvertAxis(
+ XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
+ Reference< XCoordinateSystem > const & xCoordSystem, const XclImpChAxis* pCrossingAxis ) const
+{
+ if( !xChAxis )
+ return;
+
+ // create and attach the axis object
+ Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis );
+ if( !xAxis.is() )
+ return;
+
+ // create and attach the axis title
+ if( xChAxisTitle ) try
+ {
+ Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW );
+ Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW );
+ xTitled->setTitleObject( xTitle );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis title" );
+ }
+
+ // insert axis into coordinate system
+ try
+ {
+ sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension();
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis" );
+ }
+}
+
+Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const
+{
+ Reference< XAxis > xAxis;
+ if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() )
+ xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis );
+ return xAxis;
+}
+
+void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > const & xDiagram ) const
+{
+ XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup && xTypeGroup->Is3dWallChart() )
+ {
+ // wall/floor formatting (3D charts)
+ if( mxXAxis )
+ {
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxXAxis->ConvertWall( aWallProp );
+ }
+ if( mxYAxis )
+ {
+ ScfPropertySet aFloorProp( xDiagram->getFloor() );
+ mxYAxis->ConvertWall( aFloorProp );
+ }
+ }
+ else if( mxPlotFrame )
+ {
+ // diagram background formatting
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxPlotFrame->Convert( aWallProp );
+ }
+}
+
+// The chart object ===========================================================
+
+XclImpChChart::XclImpChChart( const XclImpRoot& rRoot ) :
+ XclImpChRoot( rRoot, *this )
+{
+ mxPrimAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
+ mxSecnAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
+}
+
+XclImpChChart::~XclImpChChart()
+{
+}
+
+void XclImpChChart::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ // coordinates are stored as 16.16 fixed point
+ rStrm >> maRect;
+}
+
+void XclImpChChart::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHSERIES:
+ ReadChSeries( rStrm );
+ break;
+ case EXC_ID_CHPROPERTIES:
+ ReadChProperties( rStrm );
+ break;
+ case EXC_ID_CHDEFAULTTEXT:
+ ReadChDefaultText( rStrm );
+ break;
+ case EXC_ID_CHAXESSET:
+ ReadChAxesSet( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ ReadChText( rStrm );
+ break;
+ case EXC_ID_CHEND:
+ Finalize(); // finalize the entire chart object
+ break;
+ }
+}
+
+void XclImpChChart::ReadChDefaultText( XclImpStream& rStrm )
+{
+ sal_uInt16 nTextId = rStrm.ReaduInt16();
+ if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() )
+ {
+ unique_ptr<XclImpChText> pText(new XclImpChText(GetChRoot()));
+ pText->ReadRecordGroup(rStrm);
+ m_DefTexts.insert(std::make_pair(nTextId, std::move(pText)));
+ }
+}
+
+void XclImpChChart::ReadChDataFormat( XclImpStream& rStrm )
+{
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->ReadRecordGroup( rStrm );
+ if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES )
+ {
+ const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
+ XclImpChDataFormatMap::iterator itr = maDataFmts.lower_bound(rPos);
+ if (itr == maDataFmts.end() || maDataFmts.key_comp()(rPos, itr->first))
+ // No element exists for this data point. Insert it.
+ maDataFmts.insert(
+ itr, XclImpChDataFormatMap::value_type(rPos, xDataFmt));
+
+ /* Do not overwrite existing data format group, Excel always uses the
+ first data format group occurring in any CHSERIES group. */
+ }
+}
+
+void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ mxFrame->UpdateObjFrame( rLineData, rFillData );
+}
+
+XclImpChTypeGroupRef XclImpChChart::GetTypeGroup( sal_uInt16 nGroupIdx ) const
+{
+ XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx );
+ if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx );
+ if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup();
+ return xTypeGroup;
+}
+
+const XclImpChText* XclImpChChart::GetDefaultText( XclChTextType eTextType ) const
+{
+ sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL;
+ bool bBiff8 = GetBiff() == EXC_BIFF8;
+ switch( eTextType )
+ {
+ case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ }
+
+ XclImpChTextMap::const_iterator const itr = m_DefTexts.find(nDefTextId);
+ return itr == m_DefTexts.end() ? nullptr : itr->second.get();
+}
+
+bool XclImpChChart::IsManualPlotArea() const
+{
+ // there is no real automatic mode in BIFF5 charts
+ return (GetBiff() <= EXC_BIFF5) || ::get_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
+}
+
+void XclImpChChart::Convert( const Reference<XChartDocument>& xChartDoc,
+ XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
+{
+ // initialize conversion (locks the model to suppress any internal updates)
+ InitConversion( xChartDoc, rChartRect );
+
+ // chart frame formatting
+ if( mxFrame )
+ {
+ ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
+ mxFrame->Convert( aFrameProp );
+ }
+
+ // chart title
+ if( mxTitle ) try
+ {
+ Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW );
+ Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW );
+ xTitled->setTitleObject( xTitle );
+ }
+ catch( Exception& )
+ {
+ }
+
+ /* Create the diagram object and attach it to the chart document. Currently,
+ one diagram is used to carry all coordinate systems and data series. */
+ Reference< XDiagram > xDiagram = CreateDiagram();
+ xChartDoc->setFirstDiagram( xDiagram );
+
+ // coordinate systems and chart types, convert axis settings
+ mxPrimAxesSet->Convert( xDiagram );
+ mxSecnAxesSet->Convert( xDiagram );
+
+ // legend
+ if( xDiagram.is() && mxLegend )
+ xDiagram->setLegend( mxLegend->CreateLegend() );
+
+ /* Following all conversions needing the old Chart1 API that involves full
+ initialization of the chart view. */
+ Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY );
+ if( xChart1Doc.is() )
+ {
+ Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram();
+
+ /* Set the 'IncludeHiddenCells' property via the old API as only this
+ ensures that the data provider and all created sequences get this
+ flag correctly. */
+ ScfPropertySet aDiaProp( xDiagram1 );
+ bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY );
+ aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells );
+
+ // plot area position and size (there is no real automatic mode in BIFF5 charts)
+ XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos();
+ if( IsManualPlotArea() && xPlotAreaPos ) try
+ {
+ const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData();
+ if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) )
+ {
+ Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW );
+ css::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect );
+ // for pie charts, always set inner plot area size to exclude the data labels as Excel does
+ const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get();
+ if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) )
+ xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
+ else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() )
+ xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect );
+ else
+ xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // positions of all title objects
+ if( mxTitle )
+ mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) );
+ mxPrimAxesSet->ConvertTitlePositions();
+ mxSecnAxesSet->ConvertTitlePositions();
+ }
+
+ // unlock the model
+ FinishConversion( rDffConv );
+
+ // start listening to this chart
+ ScDocument& rDoc = GetRoot().GetDoc();
+ ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection();
+ if(!pChartCollection)
+ return;
+
+ std::vector< ScTokenRef > aRefTokens;
+ for( const auto& rxSeries : maSeries )
+ rxSeries->FillAllSourceLinks( aRefTokens );
+ if( !aRefTokens.empty() )
+ {
+ ::std::unique_ptr< ScChartListener > xListener( new ScChartListener( rObjName, rDoc, std::move(aRefTokens) ) );
+ xListener->SetUsed( true );
+ xListener->StartListeningTo();
+ pChartCollection->insert( xListener.release() );
+ }
+}
+
+void XclImpChChart::ReadChSeries( XclImpStream& rStrm )
+{
+ sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() );
+ XclImpChSeriesRef xSeries = std::make_shared<XclImpChSeries>( GetChRoot(), nNewSeriesIdx );
+ xSeries->ReadRecordGroup( rStrm );
+ maSeries.push_back( xSeries );
+}
+
+void XclImpChChart::ReadChProperties( XclImpStream& rStrm )
+{
+ maProps.mnFlags = rStrm.ReaduInt16();
+ maProps.mnEmptyMode = rStrm.ReaduInt8();
+}
+
+void XclImpChChart::ReadChAxesSet( XclImpStream& rStrm )
+{
+ XclImpChAxesSetRef xAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_NONE );
+ xAxesSet->ReadRecordGroup( rStrm );
+ switch( xAxesSet->GetAxesSetId() )
+ {
+ case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break;
+ case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break;
+ }
+}
+
+void XclImpChChart::ReadChText( XclImpStream& rStrm )
+{
+ XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
+ xText->ReadRecordGroup( rStrm );
+ switch( xText->GetLinkTarget() )
+ {
+ case EXC_CHOBJLINK_TITLE:
+ mxTitle = xText;
+ break;
+ case EXC_CHOBJLINK_DATA:
+ {
+ sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx;
+ if( nSeriesIdx < maSeries.size() )
+ maSeries[ nSeriesIdx ]->SetDataLabel( xText );
+ }
+ break;
+ }
+}
+
+void XclImpChChart::Finalize()
+{
+ // finalize series (must be done first)
+ FinalizeSeries();
+ // #i49218# legend may be attached to primary or secondary axes set
+ mxLegend = mxPrimAxesSet->GetLegend();
+ if( !mxLegend )
+ mxLegend = mxSecnAxesSet->GetLegend();
+ if( mxLegend )
+ mxLegend->Finalize();
+ // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats()
+ mxPrimAxesSet->Finalize();
+ mxSecnAxesSet->Finalize();
+ // formatting of all series
+ FinalizeDataFormats();
+ // #i47745# missing frame -> invisible border and area
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ // chart title
+ FinalizeTitle();
+}
+
+void XclImpChChart::FinalizeSeries()
+{
+ for( const XclImpChSeriesRef& xSeries : maSeries )
+ {
+ if( xSeries->HasParentSeries() )
+ {
+ /* Process child series (trend lines and error bars). Data of
+ child series will be set at the connected parent series. */
+ if( xSeries->GetParentIdx() < maSeries.size() )
+ maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries );
+ }
+ else
+ {
+ // insert the series into the related chart type group
+ if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() )
+ pTypeGroup->AddSeries( xSeries );
+ }
+ }
+}
+
+void XclImpChChart::FinalizeDataFormats()
+{
+ /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups.
+ Each CHDATAFORMAT group specifies the series and data point it is
+ assigned to. This makes it possible to have a data format that is
+ related to another series, e.g. a CHDATAFORMAT group for series 2 is
+ part of a CHSERIES group that describes series 1. Therefore the chart
+ itself has collected all CHDATAFORMAT groups to be able to store data
+ format groups for series that have not been imported at that time. This
+ loop finally assigns these groups to the related series. */
+ for( const auto& [rPos, rDataFmt] : maDataFmts )
+ {
+ sal_uInt16 nSeriesIdx = rPos.mnSeriesIdx;
+ if( nSeriesIdx < maSeries.size() )
+ maSeries[ nSeriesIdx ]->SetDataFormat( rDataFmt );
+ }
+
+ /* #i51639# (part 2): Finalize data formats of all series. This adds for
+ example missing CHDATAFORMAT groups for entire series that are needed
+ for automatic colors of lines and areas. */
+ for( auto& rxSeries : maSeries )
+ rxSeries->FinalizeDataFormats();
+}
+
+void XclImpChChart::FinalizeTitle()
+{
+ // special handling for auto-generated title
+ OUString aAutoTitle;
+ if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) )
+ {
+ // automatic title from first series name (if there are no series on secondary axes set)
+ if( !mxSecnAxesSet->IsValidAxesSet() )
+ aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle();
+ if( mxTitle || (!aAutoTitle.isEmpty()) )
+ {
+ if( !mxTitle )
+ mxTitle = std::make_shared<XclImpChText>( GetChRoot() );
+ if( aAutoTitle.isEmpty() )
+ aAutoTitle = ScResId(STR_CHARTTITLE);
+ }
+ }
+
+ // will reset mxTitle, if it does not contain a string and no auto title exists
+ lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle );
+}
+
+Reference< XDiagram > XclImpChChart::CreateDiagram() const
+{
+ // create a diagram object
+ Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY );
+
+ // convert global chart settings
+ ScfPropertySet aDiaProp( xDiagram );
+
+ // treatment of missing values
+ using namespace cssc::MissingValueTreatment;
+ sal_Int32 nMissingValues = LEAVE_GAP;
+ switch( maProps.mnEmptyMode )
+ {
+ case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break;
+ case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break;
+ case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break;
+ }
+ aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues );
+
+ return xDiagram;
+}
+
+XclImpChartDrawing::XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects
+ mnScTab( rRoot.GetCurrScTab() ),
+ mbOwnTab( bOwnTab )
+{
+}
+
+void XclImpChartDrawing::ConvertObjects( XclImpDffConverter& rDffConv,
+ const Reference< XModel >& rxModel, const tools::Rectangle& rChartRect )
+{
+ maChartRect = rChartRect; // needed in CalcAnchorRect() callback
+
+ SdrModel* pSdrModel = nullptr;
+ SdrPage* pSdrPage = nullptr;
+ if( mbOwnTab )
+ {
+ // chart sheet: insert all shapes into the sheet, not into the chart object
+ pSdrModel = GetDoc().GetDrawLayer();
+ pSdrPage = GetSdrPage( mnScTab );
+ }
+ else
+ {
+ // embedded chart object: insert all shapes into the chart
+ try
+ {
+ Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW );
+ Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW );
+ pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage );
+ pSdrModel = pSdrPage ? &pSdrPage->getSdrModelFromSdrPage() : nullptr;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ if( pSdrModel && pSdrPage )
+ ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
+}
+
+tools::Rectangle XclImpChartDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const
+{
+ /* In objects with DFF client anchor, the position of the shape is stored
+ in the cell address components of the client anchor. In old BIFF3-BIFF5
+ objects, the position is stored in the offset components of the anchor. */
+ tools::Rectangle aRect(
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) );
+ aRect.Normalize();
+ // move shapes into chart area for sheet charts
+ if( mbOwnTab )
+ aRect.Move( maChartRect.Left(), maChartRect.Top() );
+ return aRect;
+}
+
+void XclImpChartDrawing::OnObjectInserted( const XclImpDrawObjBase& )
+{
+}
+
+XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpRoot( rRoot ),
+ mbOwnTab( bOwnTab ),
+ mbIsPivotChart( false )
+{
+}
+
+XclImpChart::~XclImpChart()
+{
+}
+
+void XclImpChart::ReadChartSubStream( XclImpStream& rStrm )
+{
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ // page settings - only for charts in entire sheet
+ if( mbOwnTab ) switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HORPAGEBREAKS:
+ case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break;
+ case EXC_ID_HEADER:
+ case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break;
+ case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break;
+ case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break;
+ case EXC_ID_HCENTER:
+ case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break;
+ case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break;
+ case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break;
+
+ case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break;
+ case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break;
+
+ case EXC_ID_SHEETEXT: //0x0862
+ {
+ // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root
+ XclImpPalette& rPal = GetPalette();
+ rTabViewSett.ReadTabBgColor( rStrm, rPal);
+ }
+ break;
+
+ case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break;
+ }
+
+ // common records
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_EOF: bLoop = false; break;
+
+ // #i31882# ignore embedded chart objects
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( rStrm ); break;
+
+ case EXC_ID_CHCHART: ReadChChart( rStrm ); break;
+
+ case EXC_ID8_CHPIVOTREF:
+ GetTracer().TracePivotChartExists();
+ mbIsPivotChart = true;
+ break;
+
+ // BIFF specific records
+ default: switch( GetBiff() )
+ {
+ case EXC_BIFF5: switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
+ }
+ break;
+ case EXC_BIFF8: switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_MSODRAWING: GetChartDrawing().ReadMsoDrawing( rStrm ); break;
+ // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
+ case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
+ }
+ break;
+ default:;
+ }
+ }
+ }
+}
+
+void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ if( !mxChartData )
+ mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
+ mxChartData->UpdateObjFrame( rLineData, rFillData );
+}
+
+std::size_t XclImpChart::GetProgressSize() const
+{
+ return
+ (mxChartData ? XclImpChChart::GetProgressSize() : 0) +
+ (mxChartDrawing ? mxChartDrawing->GetProgressSize() : 0);
+}
+
+void XclImpChart::Convert( Reference< XModel > const & xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
+{
+ Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
+ if( xChartDoc.is() )
+ {
+ if( mxChartData )
+ mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect );
+ if( mxChartDrawing )
+ mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect );
+ }
+}
+
+XclImpChartDrawing& XclImpChart::GetChartDrawing()
+{
+ if( !mxChartDrawing )
+ mxChartDrawing = std::make_shared<XclImpChartDrawing>( GetRoot(), mbOwnTab );
+ return *mxChartDrawing;
+}
+
+void XclImpChart::ReadChChart( XclImpStream& rStrm )
+{
+ mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
+ mxChartData->ReadRecordGroup( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xicontent.cxx b/sc/source/filter/excel/xicontent.cxx
new file mode 100644
index 0000000000..32a6ec04a4
--- /dev/null
+++ b/sc/source/filter/excel/xicontent.cxx
@@ -0,0 +1,1451 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/sheet/TableValidationVisibility.hpp>
+#include <utility>
+#include <xicontent.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <svl/itemset.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editobj.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <stringutil.hxx>
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <validat.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <rangenam.hxx>
+#include <arealink.hxx>
+#include <stlsheet.hxx>
+#include <xlcontent.hxx>
+#include <xlformula.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xistyle.hxx>
+#include <xiescher.hxx>
+#include <xiname.hxx>
+
+#include <excform.hxx>
+#include <tabprotection.hxx>
+#include <documentimport.hxx>
+
+#include <memory>
+#include <oox/helper/helper.hxx>
+#include <sal/log.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::std::unique_ptr;
+
+// Shared string table ========================================================
+
+XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpSst::ReadSst( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 4 );
+ sal_uInt32 nStrCount = rStrm.ReaduInt32();
+ auto nBytesAvailable = rStrm.GetRecLeft();
+ if (nStrCount > nBytesAvailable)
+ {
+ SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
+ nStrCount = nBytesAvailable;
+ }
+ maStrings.clear();
+ maStrings.reserve(nStrCount);
+ while( (nStrCount > 0) && rStrm.IsValid() )
+ {
+ XclImpString aString;
+ aString.Read( rStrm );
+ maStrings.push_back( aString );
+ --nStrCount;
+ }
+}
+
+const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
+{
+ return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
+}
+
+// Hyperlinks =================================================================
+
+namespace {
+
+/** Reads character array and stores it into rString.
+ @param nChars Number of following characters (not byte count!).
+ @param b16Bit true = 16-bit characters, false = 8-bit characters. */
+void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
+{
+ sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
+ rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
+ // ignore remaining chars
+ std::size_t nIgnore = nChars - nReadChars;
+ if( b16Bit )
+ nIgnore *= 2;
+ rStrm.Ignore( nIgnore );
+}
+
+/** Reads 32-bit string length and the character array and stores it into rString.
+ @param b16Bit true = 16-bit characters, false = 8-bit characters. */
+void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
+{
+ lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
+}
+
+/** Reads 32-bit string length and ignores following 16-bit character array. */
+void lclIgnoreString32( XclImpStream& rStrm )
+{
+ sal_uInt32 nChars = rStrm.ReaduInt32();
+ nChars *= 2;
+ rStrm.Ignore( nChars );
+}
+
+/** Converts a path to an absolute path.
+ @param rPath The source path. The resulting path is returned here.
+ @param nLevel Number of parent directories to add in front of the path. */
+void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, const SfxObjectShell* pDocShell )
+{
+ OUStringBuffer aTmpStr;
+ while( nLevel )
+ {
+ aTmpStr.append( "../" );
+ --nLevel;
+ }
+ aTmpStr.append( rPath );
+
+ if( pDocShell )
+ {
+ bool bWasAbs = false;
+ rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ // full path as stored in SvxURLField must be encoded
+ }
+ else
+ rPath = aTmpStr.makeStringAndClear();
+}
+
+/** Inserts the URL into a text cell. Does not modify value or formula cells. */
+void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ ScAddress aScPos( nScCol, nScRow, nScTab );
+ ScRefCellValue aCell(rDoc.getDoc(), aScPos);
+ switch( aCell.getType() )
+ {
+ // #i54261# hyperlinks in string cells
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ sal_uInt32 nNumFmt = rDoc.getDoc().GetNumberFormat(rDoc.getDoc().GetNonThreadedContext(), aScPos);
+ SvNumberFormatter* pFormatter = rDoc.getDoc().GetFormatTable();
+ const Color* pColor;
+ OUString aDisplText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, rDoc.getDoc());
+ if (aDisplText.isEmpty())
+ aDisplText = rUrl;
+
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ SvxURLField aUrlField( rUrl, aDisplText, SvxURLFormat::AppDefault );
+
+ if( aCell.getType() == CELLTYPE_EDIT )
+ {
+ const EditTextObject* pEditObj = aCell.getEditText();
+ rEE.SetTextCurrentDefaults( *pEditObj );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, EE_PARA_ALL, 0 ) );
+ }
+ else
+ {
+ rEE.SetTextCurrentDefaults( OUString() );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() );
+ if( const ScPatternAttr* pPattern = rDoc.getDoc().GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) )
+ {
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+ pPattern->FillEditItemSet( &aItemSet );
+ rEE.QuickSetAttribs( aItemSet, ESelection( 0, 0, EE_PARA_ALL, 0 ) );
+ }
+ }
+
+ // The cell will own the text object instance.
+ rDoc.setEditCell(aScPos, rEE.CreateTextObject());
+ }
+ break;
+
+ default:
+ // Handle other cell types e.g. formulas ( and ? ) that have associated
+ // hyperlinks.
+ // Ideally all hyperlinks should be treated as below. For the moment,
+ // given the current absence of ods support lets just handle what we
+ // previously didn't handle the new way.
+ // Unfortunately we won't be able to preserve such hyperlinks when
+ // saving to ods. Note: when we are able to save such hyperlinks to ods
+ // we should handle *all* imported hyperlinks as below ( e.g. as cell
+ // attribute ) for better interoperability.
+ {
+ SfxStringItem aItem( ATTR_HYPERLINK, rUrl );
+ rDoc.getDoc().ApplyAttr(nScCol, nScRow, nScTab, aItem);
+ break;
+ }
+ }
+}
+
+} // namespace
+
+void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
+{
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ rStrm >> aXclRange;
+ // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
+ aXclRange.maFirst.mnCol &= 0xFF;
+ aXclRange.maLast.mnCol &= 0xFF;
+ OUString aString = ReadEmbeddedData( rStrm );
+ if ( !aString.isEmpty() )
+ rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString );
+}
+
+OUString XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ SfxObjectShell* pDocShell = rRoot.GetDocShell();
+
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ XclGuid aGuid;
+ rStrm >> aGuid;
+ rStrm.Ignore( 4 );
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+
+ OSL_ENSURE( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
+
+ ::std::unique_ptr< OUString > xLongName; // link / file name
+ ::std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
+ ::std::unique_ptr< OUString > xTextMark; // text mark
+
+ // description (ignore)
+ if( ::get_flag( nFlags, EXC_HLINK_DESCR ) )
+ lclIgnoreString32( rStrm );
+ // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
+ if( ::get_flag( nFlags, EXC_HLINK_FRAME ) )
+ lclIgnoreString32( rStrm );
+
+ // URL fields are zero-terminated - do not let the stream replace them
+ // in the lclAppendString32() with the '?' character.
+ rStrm.SetNulSubstChar( '\0' );
+
+ // UNC path
+ if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
+ {
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, true );
+ lclGetAbsPath( *xLongName, 0, pDocShell );
+ }
+ // file link or URL
+ else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
+ {
+ rStrm >> aGuid;
+
+ if( aGuid == XclTools::maGuidFileMoniker )
+ {
+ sal_uInt16 nLevel = rStrm.ReaduInt16(); // counter for level to climb down in path
+ xShortName.reset( new OUString );
+ lclAppendString32( *xShortName, rStrm, false );
+ rStrm.Ignore( 24 );
+
+ sal_uInt32 nStrLen = rStrm.ReaduInt32();
+ if( nStrLen )
+ {
+ nStrLen = rStrm.ReaduInt32();
+ nStrLen /= 2; // it's byte count here...
+ rStrm.Ignore( 2 );
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, nStrLen, true );
+ lclGetAbsPath( *xLongName, nLevel, pDocShell );
+ }
+ else
+ lclGetAbsPath( *xShortName, nLevel, pDocShell );
+ }
+ else if( aGuid == XclTools::maGuidUrlMoniker )
+ {
+ sal_uInt32 nStrLen = rStrm.ReaduInt32();
+ nStrLen /= 2; // it's byte count here...
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, nStrLen, true );
+ if( !::get_flag( nFlags, EXC_HLINK_ABS ) )
+ lclGetAbsPath( *xLongName, 0, pDocShell );
+ }
+ else
+ {
+ OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
+ }
+ }
+
+ // text mark
+ if( ::get_flag( nFlags, EXC_HLINK_MARK ) )
+ {
+ xTextMark.reset( new OUString );
+ lclAppendString32( *xTextMark, rStrm, true );
+ }
+
+ rStrm.SetNulSubstChar(); // back to default
+
+ OSL_ENSURE( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
+
+ if (!xLongName && xShortName)
+ xLongName = std::move(xShortName);
+ else if (!xLongName && xTextMark)
+ xLongName.reset( new OUString );
+
+ if (xLongName)
+ {
+ if (xTextMark)
+ {
+ if( xLongName->isEmpty() )
+ {
+ sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
+ if( nSepPos > 0 )
+ {
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well.
+ // Hyperlink handler has to handle all, but prefer
+ // '#SheetName.A1' if possible.
+ if (nSepPos < xTextMark->getLength() - 1)
+ {
+ ScDocument& rDoc = rRoot.GetDoc();
+ ScRange aRange;
+ if ((aRange.ParseAny( xTextMark->copy( nSepPos + 1 ), rDoc, formula::FormulaGrammar::CONV_XL_R1C1)
+ & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ xTextMark.reset( new OUString( xTextMark->replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ))));
+ }
+ }
+ }
+ xLongName.reset( new OUString( *xLongName + "#" + *xTextMark ) );
+ }
+ return( *xLongName );
+ }
+ return( OUString() );
+}
+
+void XclImpHyperlink::ConvertToValidTabName(OUString& rUrl)
+{
+ sal_Int32 n = rUrl.getLength();
+ if (n < 4)
+ // Needs at least 4 characters.
+ return;
+
+ if (rUrl[0] != '#')
+ // the 1st character must be '#'.
+ return;
+
+ OUStringBuffer aNewUrl("#");
+ OUStringBuffer aTabName;
+
+ bool bInQuote = false;
+ bool bQuoteTabName = false;
+ for( sal_Int32 i = 1; i < n; ++i )
+ {
+ sal_Unicode c = rUrl[i];
+ if (c == '\'')
+ {
+ if (bInQuote && i+1 < n && rUrl[i+1] == '\'')
+ {
+ // Two consecutive single quotes ('') signify a single literal
+ // quite. When this occurs, the whole table name needs to be
+ // quoted.
+ bQuoteTabName = true;
+ aTabName.append(OUStringChar(c) + OUStringChar(c));
+ ++i;
+ continue;
+ }
+
+ bInQuote = !bInQuote;
+ if (!bInQuote && !aTabName.isEmpty())
+ {
+ if (bQuoteTabName)
+ aNewUrl.append("'");
+ aNewUrl.append(aTabName);
+ if (bQuoteTabName)
+ aNewUrl.append("'");
+ }
+ }
+ else if (bInQuote)
+ aTabName.append(c);
+ else
+ aNewUrl.append(c);
+ }
+
+ if (bInQuote)
+ // It should be outside the quotes!
+ return;
+
+ // All is good. Pass the new URL.
+ rUrl = aNewUrl.makeStringAndClear();
+}
+
+void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
+{
+ OUString aUrl(rUrl);
+ ConvertToValidTabName(aUrl);
+
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) )
+ {
+ SCCOL nScCol1, nScCol2;
+ SCROW nScRow1, nScRow2;
+ aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab );
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ SCROW nRows = nScRow2 - nScRow1;
+ if (nRows > 1024)
+ {
+ SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2 << " to " << nScRow1 + 1024);
+ nScRow2 = nScRow1 + 1024;
+ }
+ }
+
+ for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol )
+ for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow )
+ lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab );
+ }
+}
+
+// Label ranges ===============================================================
+
+void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter();
+ ScRangePairListRef xLabelRangesRef;
+
+ XclRangeList aRowXclRanges, aColXclRanges;
+ rStrm >> aRowXclRanges >> aColXclRanges;
+
+ // row label ranges
+ ScRangeList aRowScRanges;
+ rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
+ xLabelRangesRef = rDoc.GetRowNameRangesRef();
+ for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = aRowScRanges[ i ];
+ ScRange aDataRange( rScRange );
+ if( aDataRange.aEnd.Col() < rDoc.MaxCol() )
+ {
+ aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
+ aDataRange.aEnd.SetCol( rDoc.MaxCol() );
+ }
+ else if( aDataRange.aStart.Col() > 0 )
+ {
+ aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
+ aDataRange.aStart.SetCol( 0 );
+ }
+ xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
+ }
+
+ // column label ranges
+ ScRangeList aColScRanges;
+ rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
+ xLabelRangesRef = rDoc.GetColNameRangesRef();
+
+ for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = aColScRanges[ i ];
+ ScRange aDataRange( rScRange );
+ if( aDataRange.aEnd.Row() < rDoc.MaxRow() )
+ {
+ aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
+ aDataRange.aEnd.SetRow( rDoc.MaxRow() );
+ }
+ else if( aDataRange.aStart.Row() > 0 )
+ {
+ aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
+ aDataRange.aStart.SetRow( 0 );
+ }
+ xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
+ }
+}
+
+// Conditional formatting =====================================================
+
+XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
+ XclImpRoot( rRoot ),
+ mnFormatIndex( nFormatIndex ),
+ mnCondCount( 0 ),
+ mnCondIndex( 0 )
+{
+}
+
+XclImpCondFormat::~XclImpCondFormat()
+{
+}
+
+void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
+{
+ OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
+ XclRangeList aXclRanges;
+ mnCondCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ rStrm >> aXclRanges;
+ GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
+}
+
+void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
+{
+ if( mnCondIndex >= mnCondCount )
+ {
+ OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
+ return;
+ }
+
+ // entire conditional format outside of valid range?
+ if( maRanges.empty() )
+ return;
+
+ sal_uInt8 nType = rStrm.ReaduInt8();
+ sal_uInt8 nOperator = rStrm.ReaduInt8();
+ sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
+ sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+ rStrm.Ignore( 2 ); //nFlagsExtended
+
+ // *** mode and comparison operator ***
+
+ ScConditionMode eMode = ScConditionMode::NONE;
+ switch( nType )
+ {
+ case EXC_CF_TYPE_CELL:
+ {
+ switch( nOperator )
+ {
+ case EXC_CF_CMP_BETWEEN: eMode = ScConditionMode::Between; break;
+ case EXC_CF_CMP_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break;
+ case EXC_CF_CMP_EQUAL: eMode = ScConditionMode::Equal; break;
+ case EXC_CF_CMP_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break;
+ case EXC_CF_CMP_GREATER: eMode = ScConditionMode::Greater; break;
+ case EXC_CF_CMP_LESS: eMode = ScConditionMode::Less; break;
+ case EXC_CF_CMP_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break;
+ case EXC_CF_CMP_LESS_EQUAL: eMode = ScConditionMode::EqLess; break;
+ default:
+ SAL_INFO(
+ "sc.filter", "unknown CF comparison " << nOperator);
+ }
+ }
+ break;
+
+ case EXC_CF_TYPE_FMLA:
+ eMode = ScConditionMode::Direct;
+ break;
+
+ default:
+ SAL_INFO("sc.filter", "unknown CF mode " << nType);
+ return;
+ }
+
+ // *** create style sheet ***
+
+ OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
+ SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
+
+ const XclImpPalette& rPalette = GetPalette();
+
+ // number format
+
+ if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
+ {
+ XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
+ bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
+ sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
+ rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
+ }
+
+ // *** font block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
+ {
+ XclImpFont aFont( GetRoot() );
+ aFont.ReadCFFontBlock( rStrm );
+ aFont.FillToItemSet( rStyleItemSet, XclFontItemType::Cell );
+ }
+
+ // alignment
+ if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
+ {
+ XclImpCellAlign aAlign;
+ sal_uInt16 nAlign(0);
+ sal_uInt16 nAlignMisc(0);
+ nAlign = rStrm.ReaduInt16();
+ nAlignMisc = rStrm.ReaduInt16();
+ aAlign.FillFromCF( nAlign, nAlignMisc );
+ aAlign.FillToItemSet( rStyleItemSet, nullptr );
+ rStrm.Ignore(4);
+ }
+
+ // *** border block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
+ {
+ sal_uInt16 nLineStyle(0);
+ sal_uInt32 nLineColor(0);
+ nLineStyle = rStrm.ReaduInt16();
+ nLineColor = rStrm.ReaduInt32();
+ rStrm.Ignore( 2 );
+
+ XclImpCellBorder aBorder;
+ aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
+ aBorder.FillToItemSet( rStyleItemSet, rPalette );
+ }
+
+ // *** pattern block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
+ {
+ sal_uInt16 nPattern(0), nColor(0);
+ nPattern = rStrm.ReaduInt16();
+ nColor = rStrm.ReaduInt16();
+
+ XclImpCellArea aArea;
+ aArea.FillFromCF8( nPattern, nColor, nFlags );
+ aArea.FillToItemSet( rStyleItemSet, rPalette );
+ }
+
+ if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
+ {
+ sal_uInt16 nCellProt;
+ nCellProt = rStrm.ReaduInt16();
+ XclImpCellProt aCellProt;
+ aCellProt.FillFromXF3(nCellProt);
+ aCellProt.FillToItemSet( rStyleItemSet );
+ }
+
+ // *** formulas ***
+
+ const ScAddress& rPos = maRanges.front().aStart; // assured above that maRanges is not empty
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+
+ ::std::unique_ptr< ScTokenArray > xTokArr1;
+ if( nFmlaSize1 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset( rPos );
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ {
+ xTokArr1 = std::move( pTokArr );
+ GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1);
+ }
+ }
+
+ ::std::unique_ptr< ScTokenArray > xTokArr2;
+ if( nFmlaSize2 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset( rPos );
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ {
+ xTokArr2 = std::move( pTokArr );
+ GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2);
+ }
+ }
+
+ // *** create the Calc conditional formatting ***
+
+ const ScAddress aPos(rPos); //in case maRanges.Join invalidates it
+
+ if( !mxScCondFmt )
+ {
+ mxScCondFmt.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
+ if(maRanges.size() > 1)
+ maRanges.Join(maRanges[0], true);
+ mxScCondFmt->SetRange(maRanges);
+ }
+
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, xTokArr1.get(), xTokArr2.get(), GetDoc(), aPos, aStyleName);
+ mxScCondFmt->AddEntry( pEntry );
+ ++mnCondIndex;
+}
+
+void XclImpCondFormat::Apply()
+{
+ if( mxScCondFmt )
+ {
+ ScDocument& rDoc = GetDoc();
+
+ SCTAB nTab = maRanges.front().aStart.Tab();
+ sal_uLong nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
+
+ rDoc.AddCondFormatData( maRanges, nTab, nKey );
+ }
+}
+
+XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
+{
+ XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
+ pFmt->ReadCondfmt( rStrm );
+ maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
+}
+
+void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
+{
+ OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
+ if( !maCondFmtList.empty() )
+ maCondFmtList.back()->ReadCF( rStrm );
+}
+
+void XclImpCondFormatManager::Apply()
+{
+ for( auto& rxFmt : maCondFmtList )
+ rxFmt->Apply();
+ maCondFmtList.clear();
+}
+
+// Data Validation ============================================================
+
+XclImpValidationManager::DVItem::DVItem( ScRangeList aRanges, const ScValidationData& rValidData ) :
+ maRanges(std::move(aRanges)), maValidData(rValidData) {}
+
+XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ sal_uInt32 nObjId(0);
+ rStrm.Ignore( 10 );
+ nObjId = rStrm.ReaduInt32();
+ if( nObjId != EXC_DVAL_NOOBJ )
+ {
+ OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
+ rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
+ }
+}
+
+void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
+
+ // flags
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+
+ // message strings
+ /* Empty strings are single NUL characters in Excel (string length is 1).
+ -> Do not let the stream replace them with '?' characters. */
+ rStrm.SetNulSubstChar( '\0' );
+ OUString aPromptTitle( rStrm.ReadUniString() );
+ OUString aErrorTitle( rStrm.ReadUniString() );
+ OUString aPromptMessage( rStrm.ReadUniString() );
+ OUString aErrorMessage( rStrm.ReadUniString() );
+ rStrm.SetNulSubstChar(); // back to default
+
+ // formula(s)
+ if ( rStrm.GetRecLeft() <= 8 )
+ // Not enough bytes left in the record. Bail out.
+ return;
+
+ // first formula
+ // string list is single tStr token with NUL separators -> replace them with LF
+ rStrm.SetNulSubstChar( '\n' );
+ ::std::unique_ptr< ScTokenArray > xTokArr1;
+
+ // We can't import the formula directly because we need the range
+ sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ XclImpStreamPos aPosFormula1;
+ rStrm.StorePosition(aPosFormula1);
+ rStrm.Ignore(nLenFormula1);
+
+ // second formula
+ ::std::unique_ptr< ScTokenArray > xTokArr2;
+
+ sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ XclImpStreamPos aPosFormula2;
+ rStrm.StorePosition(aPosFormula2);
+ rStrm.Ignore(nLenFormula2);
+
+ // read all cell ranges
+ XclRangeList aXclRanges;
+ rStrm >> aXclRanges;
+
+ // convert to Calc range list
+ ScRangeList aScRanges;
+ rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
+
+ // only continue if there are valid ranges
+ if ( aScRanges.empty() )
+ return;
+
+ ScRange aCombinedRange = aScRanges.Combine();
+
+ XclImpStreamPos aCurrentPos;
+ rStrm.StorePosition(aCurrentPos);
+ rStrm.RestorePosition(aPosFormula1);
+ if( nLenFormula1 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset(aCombinedRange.aStart);
+ rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ xTokArr1 = std::move( pTokArr );
+ }
+ rStrm.SetNulSubstChar(); // back to default
+ if (nLenFormula2 > 0)
+ {
+ rStrm.RestorePosition(aPosFormula2);
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset(aCombinedRange.aStart);
+ rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ xTokArr2 = std::move( pTokArr );
+ }
+
+ rStrm.RestorePosition(aCurrentPos);
+
+ bool bIsValid = true; // valid settings in flags field
+
+ ScValidationMode eValMode = SC_VALID_ANY;
+ switch( nFlags & EXC_DV_MODE_MASK )
+ {
+ case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break;
+ case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break;
+ case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
+ case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break;
+ case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break;
+ case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break;
+ case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
+ case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
+ default: bIsValid = false;
+ }
+ rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
+
+ ScConditionMode eCondMode = ScConditionMode::Between;
+ switch( nFlags & EXC_DV_COND_MASK )
+ {
+ case EXC_DV_COND_BETWEEN: eCondMode = ScConditionMode::Between; break;
+ case EXC_DV_COND_NOTBETWEEN:eCondMode = ScConditionMode::NotBetween; break;
+ case EXC_DV_COND_EQUAL: eCondMode = ScConditionMode::Equal; break;
+ case EXC_DV_COND_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
+ case EXC_DV_COND_GREATER: eCondMode = ScConditionMode::Greater; break;
+ case EXC_DV_COND_LESS: eCondMode = ScConditionMode::Less; break;
+ case EXC_DV_COND_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
+ case EXC_DV_COND_EQLESS: eCondMode = ScConditionMode::EqLess; break;
+ default: bIsValid = false;
+ }
+
+ if ( !bIsValid )
+ // No valid validation found. Bail out.
+ return;
+
+ // The default value for comparison is _BETWEEN. However, custom
+ // rules are a formula, and thus the comparator should be ignored
+ // and only a true or false from the formula is evaluated. In Calc,
+ // formulas use comparison SC_COND_DIRECT.
+ if( eValMode == SC_VALID_CUSTOM )
+ {
+ eCondMode = ScConditionMode::Direct;
+ }
+
+ // first range for base address for relative references
+ const ScRange& rScRange = aScRanges.front(); // aScRanges is not empty
+
+ // process string list of a list validity (convert to list of string tokens)
+ if( xTokArr1 && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
+ XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n');
+
+ maDVItems.push_back(
+ std::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), rDoc, rScRange.aStart)));
+ DVItem& rItem = *maDVItems.back();
+
+ rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
+ rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
+
+ // *** prompt box ***
+ if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
+ {
+ // set any text stored in the record
+ rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
+ if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
+ rItem.maValidData.ResetInput();
+ }
+
+ // *** error box ***
+ ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
+ switch( nFlags & EXC_DV_ERROR_MASK )
+ {
+ case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break;
+ case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break;
+ }
+ // set texts and error style
+ rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
+ if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
+ rItem.maValidData.ResetError();
+}
+
+void XclImpValidationManager::Apply()
+{
+ const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ size_t nPatterns = 0;
+
+ ScDocument& rDoc = GetRoot().GetDoc();
+ for (const auto& rxDVItem : maDVItems)
+ {
+ DVItem& rItem = *rxDVItem;
+ // set the handle ID
+ sal_uInt32 nHandle = rDoc.AddValidationEntry( rItem.maValidData );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) );
+
+ // apply all ranges
+ for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i, ++nPatterns )
+ {
+ const ScRange & rScRange = rItem.maRanges[ i ];
+ rDoc.ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aEnd.Col(), rScRange.aEnd.Row(), rScRange.aStart.Tab(), aPattern );
+ if (bFuzzing && nPatterns >= 128)
+ {
+ SAL_WARN("sc.filter", "for fuzzing performance, abandoned pattern after " << nPatterns << " insertions");
+ break;
+ }
+ }
+ }
+ maDVItems.clear();
+}
+
+// Web queries ================================================================
+
+XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
+ maDestRange( rDestRange ),
+ meMode( xlWQUnknown ),
+ mnRefresh( 0 )
+{
+}
+
+void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
+ if( !((nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY )) )
+ return;
+
+ if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
+ {
+ meMode = xlWQAllTables;
+ maTables = ScfTools::GetHTMLTablesName();
+ }
+ else
+ {
+ meMode = xlWQDocument;
+ maTables = ScfTools::GetHTMLDocName();
+ }
+}
+
+void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
+{
+ maURL = rStrm.ReadUniString();
+}
+
+void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 10 );
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnRefresh = rStrm.ReaduInt16();
+
+ if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) )
+ meMode = xlWQSpecTables;
+}
+
+void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm )
+{
+ if( meMode != xlWQSpecTables )
+ return;
+
+ rStrm.Ignore( 4 );
+ OUString aTables( rStrm.ReadUniString() );
+
+ const sal_Unicode cSep = ';';
+ static constexpr OUStringLiteral aQuotedPairs( u"\"\"" );
+ maTables.clear();
+ for ( sal_Int32 nStringIx {aTables.isEmpty() ? -1 : 0}; nStringIx>=0; )
+ {
+ OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
+ sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
+ if( nTabNum > 0 )
+ maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
+ else
+ {
+ ScGlobal::EraseQuotes( aToken, '"', false );
+ if( !aToken.isEmpty() )
+ maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
+ }
+ }
+}
+
+void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
+{
+ if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
+ {
+ ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(),
+ maURL, rFilterName, OUString(), maTables, maDestRange, mnRefresh * 60UL );
+ rDoc.GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile,
+ maURL, &rFilterName, &maTables );
+ }
+}
+
+XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ rStrm.Ignore( 10 );
+ OUString aXclName( rStrm.ReadUniString() );
+
+ // #i64794# Excel replaces spaces with underscores
+ aXclName = aXclName.replaceAll( " ", "_" );
+
+ // find the defined name used in Calc
+ if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
+ {
+ if( const ScRangeData* pRangeData = pName->GetScRangeData() )
+ {
+ ScRange aRange;
+ if( pRangeData->IsReference( aRange ) )
+ maWQList.emplace_back( aRange );
+ }
+ }
+ }
+ else
+ {
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadParamqry( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqstring( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqsettings( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqtables( rStrm );
+}
+
+void XclImpWebQueryBuffer::Apply()
+{
+ ScDocument& rDoc = GetDoc();
+ for( auto& rQuery : maWQList )
+ rQuery.Apply( rDoc, EXC_WEBQRY_FILTER );
+}
+
+// Decryption =================================================================
+
+namespace {
+
+XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
+ if( rStrm.GetRecLeft() == 4 )
+ {
+ sal_uInt16 nKey(0), nHash(0);
+ nKey = rStrm.ReaduInt16();
+ nHash = rStrm.ReaduInt16();
+ xDecr = std::make_shared<XclImpBiff5Decrypter>( nKey, nHash );
+ }
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
+ if( rStrm.GetRecLeft() == 48 )
+ {
+ std::vector<sal_uInt8> aSalt(16);
+ std::vector<sal_uInt8> aVerifier(16);
+ std::vector<sal_uInt8> aVerifierHash(16);
+ rStrm.Read(aSalt.data(), 16);
+ rStrm.Read(aVerifier.data(), 16);
+ rStrm.Read(aVerifierHash.data(), 16);
+ xDecr = std::make_shared<XclImpBiff8StdDecrypter>(std::move(aSalt), std::move(aVerifier), std::move(aVerifierHash));
+ }
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
+{
+ //It is possible there are other variants in existence but these
+ //are the defaults I get with Excel 2013
+ XclImpDecrypterRef xDecr;
+
+ msfilter::RC4EncryptionInfo info;
+
+ info.header.flags = rStream.ReaduInt32();
+ if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
+ return xDecr;
+
+ sal_uInt32 nHeaderSize = rStream.ReaduInt32();
+ sal_uInt32 actualHeaderSize = sizeof(info.header);
+
+ if( nHeaderSize < actualHeaderSize )
+ return xDecr;
+
+ info.header.flags = rStream.ReaduInt32();
+ info.header.sizeExtra = rStream.ReaduInt32();
+ info.header.algId = rStream.ReaduInt32();
+ info.header.algIdHash = rStream.ReaduInt32();
+ info.header.keyBits = rStream.ReaduInt32();
+ info.header.providedType = rStream.ReaduInt32();
+ info.header.reserved1 = rStream.ReaduInt32();
+ info.header.reserved2 = rStream.ReaduInt32();
+
+ rStream.Ignore(nHeaderSize - actualHeaderSize);
+
+ info.verifier.saltSize = rStream.ReaduInt32();
+ if (info.verifier.saltSize != msfilter::SALT_LENGTH)
+ return xDecr;
+ rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
+ rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
+
+ info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
+ if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
+ return xDecr;
+ rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
+
+ // check flags and algorithm IDs, required are AES128 and SHA-1
+ if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
+ return xDecr;
+
+ if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
+ return xDecr;
+
+ if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
+ return xDecr;
+
+ // hash algorithm ID 0 defaults to SHA-1 too
+ if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
+ return xDecr;
+
+ xDecr = std::make_shared<XclImpBiff8CryptoAPIDecrypter>(
+ std::vector<sal_uInt8>(info.verifier.salt,
+ info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
+ std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
+ info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
+ std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
+ info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash)));
+
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+
+ sal_uInt16 nMode = rStrm.ReaduInt16();
+ switch( nMode )
+ {
+ case EXC_FILEPASS_BIFF5:
+ xDecr = lclReadFilepass5( rStrm );
+ break;
+
+ case EXC_FILEPASS_BIFF8:
+ {
+ sal_uInt32 nVersion = rStrm.ReaduInt32();
+ if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
+ {
+ //A Version structure where Version.vMajor MUST be 0x0001,
+ //and Version.vMinor MUST be 0x0001.
+ xDecr = lclReadFilepass8_Standard(rStrm);
+ }
+ else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
+ nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
+ {
+ //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
+ //Version.vMinor MUST be 0x0002.
+ xDecr = lclReadFilepass8_Strong(rStrm);
+ }
+ else
+ OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
+ }
+ break;
+
+ default:
+ OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
+ }
+
+ return xDecr;
+}
+
+} // namespace
+
+const ErrCode& XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ rStrm.DisableDecryption();
+
+ // read the FILEPASS record and create a new decrypter object
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break;
+ case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ };
+
+ // set decrypter at import stream
+ rStrm.SetDecrypter( xDecr );
+
+ // request and verify a password (decrypter implements IDocPasswordVerifier)
+ if( xDecr )
+ rStrm.GetRoot().RequestEncryptionData( *xDecr );
+
+ // return error code (success, wrong password, etc.)
+ return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
+}
+
+// Document protection ========================================================
+
+XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnPassHash(0x0000),
+ mbDocProtect(false),
+ mbWinProtect(false)
+{
+}
+
+void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
+{
+ mbDocProtect = rStrm.ReaduInt16() != 0;
+}
+
+void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
+{
+ mbWinProtect = rStrm.ReaduInt16() != 0;
+}
+
+void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
+{
+ rStrm.EnableDecryption();
+ mnPassHash = rStrm.ReaduInt16();
+}
+
+void XclImpDocProtectBuffer::Apply() const
+{
+ if (!mbDocProtect && !mbWinProtect)
+ // Excel requires either the structure or windows protection is set.
+ // If neither is set then the document is not protected at all.
+ return;
+
+ unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
+ pProtect->setProtected(true);
+
+ if (mnPassHash)
+ {
+ // 16-bit password hash.
+ Sequence<sal_Int8> aPass{sal_Int8(mnPassHash >> 8), sal_Int8(mnPassHash & 0xFF)};
+ pProtect->setPasswordHash(aPass, PASSHASH_XL);
+ }
+
+ // document protection options
+ pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
+ pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect);
+
+ GetDoc().SetDocProtection(pProtect.get());
+}
+
+// Sheet Protection ===========================================================
+
+XclImpSheetProtectBuffer::Sheet::Sheet() :
+ mbProtected(false),
+ mnPasswordHash(0x0000),
+ mnOptions(0x4400)
+{
+}
+
+XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
+ mbProtected(r.mbProtected),
+ mnPasswordHash(r.mnPasswordHash),
+ mnOptions(r.mnOptions)
+{
+}
+
+XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
+{
+ if ( rStrm.ReaduInt16() )
+ {
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mbProtected = true;
+ }
+}
+
+void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
+{
+ // The flag size specifies the size of bytes that follows that stores
+ // feature data. If -1 it depends on the feature type imported earlier.
+ // For enhanced protection data, the size is always 4. For the most xls
+ // documents out there this value is almost always -1.
+ sal_Int32 nFlagSize = rStrm.ReadInt32();
+ if (nFlagSize != -1)
+ return;
+
+ // There are actually 4 bytes to read, but the upper 2 bytes currently
+ // don't store any bits.
+ sal_uInt16 nOptions = rStrm.ReaduInt16();
+
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mnOptions = nOptions;
+}
+
+void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
+{
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->maEnhancedProtections.push_back( rProt);
+}
+
+void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
+{
+ sal_uInt16 nHash = rStrm.ReaduInt16();
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mnPasswordHash = nHash;
+}
+
+void XclImpSheetProtectBuffer::Apply() const
+{
+ for (const auto& [rTab, rSheet] : maProtectedSheets)
+ {
+ if (!rSheet.mbProtected)
+ // This sheet is (for whatever reason) not protected.
+ continue;
+
+ unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
+ pProtect->setProtected(true);
+
+ // 16-bit hash password
+ const sal_uInt16 nHash = rSheet.mnPasswordHash;
+ if (nHash)
+ {
+ Sequence<sal_Int8> aPass{sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
+ pProtect->setPasswordHash(aPass, PASSHASH_XL);
+ }
+
+ // sheet protection options
+ const sal_uInt16 nOptions = rSheet.mnOptions;
+ pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) );
+ pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) );
+ pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) );
+ pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) );
+ pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) );
+ pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) );
+ pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) );
+ pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) );
+ pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) );
+ pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) );
+ pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) );
+ pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) );
+ pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) );
+ pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) );
+ pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
+
+ // Enhanced protection containing editable ranges and permissions.
+ pProtect->setEnhancedProtection( std::vector(rSheet.maEnhancedProtections) );
+
+ // all done. now commit.
+ GetDoc().SetTabProtection(rTab, pProtect.get());
+ }
+}
+
+XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
+{
+ ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
+ if (itr == maProtectedSheets.end())
+ {
+ // new sheet
+ if ( !maProtectedSheets.emplace( nTab, Sheet() ).second )
+ return nullptr;
+
+ itr = maProtectedSheets.find(nTab);
+ }
+
+ return &itr->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiescher.cxx b/sc/source/filter/excel/xiescher.cxx
new file mode 100644
index 0000000000..62e35213a0
--- /dev/null
+++ b/sc/source/filter/excel/xiescher.cxx
@@ -0,0 +1,4514 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiescher.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/awt/PushButtonType.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/wmf.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <o3tl/safeint.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <sal/log.hxx>
+
+#include <svx/svdopath.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svditer.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlndsit.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/sdasitm.hxx>
+#include <svx/sdshcitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtditm.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <svx/xflclit.hxx>
+#include <sal/macros.h>
+#include <editeng/adjustitem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xbitmap.hxx>
+#include <svtools/embedhlp.hxx>
+#include <sot/storage.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <userdat.hxx>
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <postit.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <fprogressbar.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiformula.hxx>
+#include <xilink.hxx>
+#include <xistyle.hxx>
+#include <xipage.hxx>
+#include <xichart.hxx>
+#include <xicontent.hxx>
+#include <scextopt.hxx>
+
+#include <namebuff.hxx>
+#include <sfx2/docfile.hxx>
+#include <memory>
+#include <numeric>
+#include <string_view>
+#include <utility>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+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::beans::NamedValue;
+using ::com::sun::star::lang::XMultiServiceFactory;
+using ::com::sun::star::container::XIndexContainer;
+using ::com::sun::star::container::XNameContainer;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::embed::XEmbeddedObject;
+using ::com::sun::star::embed::XEmbedPersist;
+using ::com::sun::star::drawing::XControlShape;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::form::XFormComponent;
+using ::com::sun::star::form::XFormsSupplier;
+using ::com::sun::star::form::binding::XBindableValue;
+using ::com::sun::star::form::binding::XValueBinding;
+using ::com::sun::star::form::binding::XListEntrySink;
+using ::com::sun::star::form::binding::XListEntrySource;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::script::XEventAttacherManager;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+
+// Drawing objects ============================================================
+
+XclImpDrawObjBase::XclImpDrawObjBase( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnObjId( EXC_OBJ_INVALID_ID ),
+ mnTab( 0 ),
+ mnObjType( EXC_OBJTYPE_UNKNOWN ),
+ mnDffShapeId( 0 ),
+ mnDffFlags( ShapeFlag::NONE ),
+ mbHasAnchor( false ),
+ mbHidden( false ),
+ mbVisible( true ),
+ mbPrintable( true ),
+ mbAreaObj( false ),
+ mbAutoMargin( true ),
+ mbSimpleMacro( true ),
+ mbProcessSdr( true ),
+ mbInsertSdr( true ),
+ mbCustomDff( false ),
+ mbNotifyMacroEventRead( false )
+{
+}
+
+XclImpDrawObjBase::~XclImpDrawObjBase()
+{
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 30 )
+ {
+ sal_uInt16 nObjType;
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj= std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj= std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj= std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj= std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj= std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj= std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj= std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj= std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj= std::make_shared<XclImpPictureObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj3 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+
+ if (!xDrawObj)
+ {
+ xDrawObj = std::make_shared<XclImpPhObj>(rRoot);
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj3( rStrm );
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 30 )
+ {
+ sal_uInt16 nObjType;
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj4 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+
+ if (!xDrawObj)
+ {
+ xDrawObj = std::make_shared<XclImpPhObj>(rRoot);
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj4( rStrm );
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 34 )
+ {
+ sal_uInt16 nObjType(EXC_OBJTYPE_UNKNOWN);
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break;
+ case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break;
+ case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break;
+ case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break;
+ case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj5 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ xDrawObj = std::make_shared<XclImpPhObj>( rRoot );
+ }
+ }
+
+ OSL_ENSURE(xDrawObj, "object import failed");
+
+ if (xDrawObj)
+ {
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj5( rStrm );
+ }
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 10 )
+ {
+ sal_uInt16 nSubRecId(0), nSubRecSize(0), nObjType(0);
+ nSubRecId = rStrm.ReaduInt16();
+ nSubRecSize = rStrm.ReaduInt16();
+ nObjType = rStrm.ReaduInt16();
+ OSL_ENSURE( nSubRecId == EXC_ID_OBJCMO, "XclImpDrawObjBase::ReadObj8 - OBJCMO subrecord expected" );
+ if( (nSubRecId == EXC_ID_OBJCMO) && (nSubRecSize >= 6) )
+ {
+ switch( nObjType )
+ {
+ // in BIFF8, all simple objects support text
+ case EXC_OBJTYPE_LINE:
+ case EXC_OBJTYPE_ARC:
+ xDrawObj = std::make_shared<XclImpTextObj>( rRoot );
+ // lines and arcs may be 2-dimensional
+ xDrawObj->SetAreaObj( false );
+ break;
+
+ // in BIFF8, all simple objects support text
+ case EXC_OBJTYPE_RECTANGLE:
+ case EXC_OBJTYPE_OVAL:
+ case EXC_OBJTYPE_POLYGON:
+ case EXC_OBJTYPE_DRAWING:
+ case EXC_OBJTYPE_TEXT:
+ xDrawObj = std::make_shared<XclImpTextObj>( rRoot );
+ break;
+
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break;
+ case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break;
+ case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break;
+ case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break;
+ case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break;
+ case EXC_OBJTYPE_NOTE: xDrawObj = std::make_shared<XclImpNoteObj>( rRoot ); break;
+
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj8 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+ }
+
+ if (!xDrawObj) //ensure placeholder for unknown or broken records
+ {
+ SAL_WARN( "sc.filter", "XclImpDrawObjBase::ReadObj8 import failed, substituting placeholder");
+ xDrawObj = std::make_shared<XclImpPhObj>( rRoot );
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj8( rStrm );
+ return xDrawObj;
+}
+
+void XclImpDrawObjBase::SetAnchor( const XclObjAnchor& rAnchor )
+{
+ maAnchor = rAnchor;
+ mbHasAnchor = true;
+}
+
+const tools::Rectangle& XclImpDrawObjBase::GetDffRect() const
+{
+ return maDffRect;
+}
+
+void XclImpDrawObjBase::SetDffData(
+ const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink,
+ bool bVisible, bool bAutoMargin )
+{
+ mnDffShapeId = rDffObjData.nShapeId;
+ mnDffFlags = rDffObjData.nSpFlags;
+ maObjName = rObjName;
+ maHyperlink = rHyperlink;
+ mbVisible = bVisible;
+ mbAutoMargin = bAutoMargin;
+ maDffRect = rDffObjData.aChildAnchor;
+}
+
+OUString XclImpDrawObjBase::GetObjName() const
+{
+ /* #i51348# Always return a non-empty name. Create English
+ default names depending on the object type. This is not implemented as
+ virtual functions in derived classes, as class type and object type may
+ not match. */
+ return maObjName.isEmpty() ? GetObjectManager().GetDefaultObjName(*this) : maObjName;
+}
+
+const XclObjAnchor* XclImpDrawObjBase::GetAnchor() const
+{
+ return mbHasAnchor ? &maAnchor : nullptr;
+}
+
+bool XclImpDrawObjBase::IsValidSize( const tools::Rectangle& rAnchorRect ) const
+{
+ // XclObjAnchor rounds up the width, width of 3 is the result of an Excel width of 0
+ return mbAreaObj ?
+ ((rAnchorRect.GetWidth() > 3) && (rAnchorRect.GetHeight() > 1)) :
+ ((rAnchorRect.GetWidth() > 3) || (rAnchorRect.GetHeight() > 1));
+}
+
+ScRange XclImpDrawObjBase::GetUsedArea( SCTAB nScTab ) const
+{
+ ScRange aScUsedArea( ScAddress::INITIALIZE_INVALID );
+ // #i44077# object inserted -> update used area for OLE object import
+ if( mbHasAnchor && GetAddressConverter().ConvertRange( aScUsedArea, maAnchor, nScTab, nScTab, false ) )
+ {
+ // reduce range, if object ends directly on borders between two columns or rows
+ if( (maAnchor.mnRX == 0) && (aScUsedArea.aStart.Col() < aScUsedArea.aEnd.Col()) )
+ aScUsedArea.aEnd.IncCol( -1 );
+ if( (maAnchor.mnBY == 0) && (aScUsedArea.aStart.Row() < aScUsedArea.aEnd.Row()) )
+ aScUsedArea.aEnd.IncRow( -1 );
+ }
+ return aScUsedArea;
+}
+
+std::size_t XclImpDrawObjBase::GetProgressSize() const
+{
+ return DoGetProgressSize();
+}
+
+rtl::Reference<SdrObject> XclImpDrawObjBase::CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const
+{
+ rtl::Reference<SdrObject> xSdrObj;
+ if( bIsDff && !mbCustomDff )
+ {
+ rDffConv.Progress( GetProgressSize() );
+ }
+ else
+ {
+ xSdrObj = DoCreateSdrObj( rDffConv, rAnchorRect );
+
+ //added for exporting OCX control
+ /* mnObjType value set should be as below table:
+ 0x0000 Group 0x0001 Line
+ 0x0002 Rectangle 0x0003 Oval
+ 0x0004 Arc 0x0005 Chart
+ 0x0006 Text 0x0009 Polygon
+ +-----------------------------------------------------+
+ OCX ==>| 0x0008 Picture |
+ +-----------------------------------------------------+
+ | 0x0007 Button |
+ | 0x000B Checkbox 0x000C Radio button |
+ | 0x000D Edit box 0x000E Label |
+ TBX ==> | 0x000F Dialog box 0x0010 Spin control |
+ | 0x0011 Scrollbar 0x0012 List |
+ | 0x0013 Group box 0x0014 Dropdown list |
+ +-----------------------------------------------------+
+ 0x0019 Note 0x001E OfficeArt object
+ */
+ if( xSdrObj && xSdrObj->IsUnoObj() &&
+ ( (mnObjType < 25 && mnObjType > 10) || mnObjType == 7 || mnObjType == 8 ) )
+ {
+ SdrUnoObj* pSdrUnoObj = dynamic_cast< SdrUnoObj* >( xSdrObj.get() );
+ if( pSdrUnoObj != nullptr )
+ {
+ const Reference< XControlModel >& xCtrlModel = pSdrUnoObj->GetUnoControlModel();
+ Reference< XPropertySet > xPropSet(xCtrlModel,UNO_QUERY);
+ static constexpr OUString sPropertyName(u"ControlTypeinMSO"_ustr);
+
+ enum { eCreateFromOffice = 0, eCreateFromMSTBXControl, eCreateFromMSOCXControl };
+
+ if( mnObjType == 7 || (mnObjType < 25 && mnObjType > 10) )//TBX
+ {
+ try
+ {
+ //Need summary type for export. Detail type(checkbox, button ...) has been contained by mnObjType
+ const sal_Int16 nTBXControlType = eCreateFromMSTBXControl ;
+ xPropSet->setPropertyValue(sPropertyName, Any(nTBXControlType));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ControlTypeinMSO!");
+ }
+ }
+ if( mnObjType == 8 )//OCX
+ {
+ //Need summary type for export
+ static constexpr OUStringLiteral sObjIdPropertyName(u"ObjIDinMSO");
+ const XclImpPictureObj* const pObj = dynamic_cast< const XclImpPictureObj* const >(this);
+ if( pObj != nullptr && pObj->IsOcxControl() )
+ {
+ try
+ {
+ const sal_Int16 nOCXControlType = eCreateFromMSOCXControl;
+ xPropSet->setPropertyValue(sPropertyName, Any(nOCXControlType));
+ //Detail type(checkbox, button ...)
+ xPropSet->setPropertyValue(sObjIdPropertyName, Any(sal_uInt16(mnObjId)));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ObjIDinMSO!");
+ }
+ }
+ }
+
+ }
+ }
+ }
+ return xSdrObj;
+}
+
+void XclImpDrawObjBase::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ ScDocShell* pDocShell = GetDocShell();
+ if (!pDocShell)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(pDocShell->GetModel());
+ mbNotifyMacroEventRead = true;
+}
+
+void XclImpDrawObjBase::PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj )
+{
+ // default: front layer, derived classes may have to set other layer in DoPreProcessSdrObj()
+ rSdrObj.NbcSetLayer( SC_LAYER_FRONT );
+
+ // set object name (GetObjName() will always return a non-empty name)
+ rSdrObj.SetName( GetObjName() );
+
+ // #i39167# full width for all objects regardless of horizontal alignment
+ rSdrObj.SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) );
+
+ // automatic text margin
+ if( mbAutoMargin )
+ {
+ sal_Int32 nMargin = rDffConv.GetDefaultTextMargin();
+ rSdrObj.SetMergedItem( makeSdrTextLeftDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextRightDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextUpperDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextLowerDistItem( nMargin ) );
+ }
+
+ // macro and hyperlink
+ // removed oracle/sun check for mbSimpleMacro ( no idea what its for )
+ if (!maMacroName.isEmpty())
+ {
+ if( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( &rSdrObj, true ) )
+ {
+ OUString sMacro = XclTools::GetSbMacroUrl(maMacroName, GetDocShell());
+ if (!sMacro.isEmpty())
+ NotifyMacroEventRead();
+ pInfo->SetMacro(sMacro);
+ }
+ }
+ if (!maHyperlink.isEmpty())
+ rSdrObj.setHyperlink(maHyperlink);
+
+ // call virtual function for object type specific processing
+ DoPreProcessSdrObj( rDffConv, rSdrObj );
+}
+
+void XclImpDrawObjBase::PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // call virtual function for object type specific processing
+ DoPostProcessSdrObj( rDffConv, rSdrObj );
+}
+
+// protected ------------------------------------------------------------------
+
+void XclImpDrawObjBase::ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen )
+{
+ maObjName.clear();
+ if( nNameLen > 0 )
+ {
+ // name length field is repeated before the name
+ maObjName = rStrm.ReadByteString( false );
+ // skip padding byte for word boundaries
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+ }
+}
+
+void XclImpDrawObjBase::ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+ // skip padding byte for word boundaries, not contained in nMacroSize
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+}
+
+void XclImpDrawObjBase::ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+}
+
+void XclImpDrawObjBase::ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+}
+
+void XclImpDrawObjBase::ReadMacro8( XclImpStream& rStrm )
+{
+ maMacroName.clear();
+ if( rStrm.GetRecLeft() <= 6 )
+ return;
+
+ // macro is stored in a tNameXR token containing a link to a defined name
+ sal_uInt16 nFmlaSize;
+ nFmlaSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ OSL_ENSURE( nFmlaSize == 7, "XclImpDrawObjBase::ReadMacro - unexpected formula size" );
+ if( nFmlaSize == 7 )
+ {
+ sal_uInt8 nTokenId;
+ sal_uInt16 nExtSheet, nExtName;
+ nTokenId = rStrm.ReaduInt8();
+ nExtSheet = rStrm.ReaduInt16();
+ nExtName = rStrm.ReaduInt16();
+ OSL_ENSURE( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ),
+ "XclImpDrawObjBase::ReadMacro - tNameXR token expected" );
+ if( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) )
+ maMacroName = GetLinkManager().GetMacroName( nExtSheet, nExtName );
+ }
+}
+
+void XclImpDrawObjBase::ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const
+{
+ if( rLineData.IsAuto() )
+ {
+ XclObjLineData aAutoData;
+ aAutoData.mnAuto = 0;
+ ConvertLineStyle( rSdrObj, aAutoData );
+ }
+ else
+ {
+ tools::Long nLineWidth = 35 * ::std::min( rLineData.mnWidth, EXC_OBJ_LINE_THICK );
+ rSdrObj.SetMergedItem( XLineWidthItem( nLineWidth ) );
+ rSdrObj.SetMergedItem( XLineColorItem( OUString(), GetPalette().GetColor( rLineData.mnColorIdx ) ) );
+ rSdrObj.SetMergedItem( XLineJointItem( css::drawing::LineJoint_MITER ) );
+
+ sal_uLong nDotLen = ::std::max< sal_uLong >( 70 * rLineData.mnWidth, 35 );
+ sal_uLong nDashLen = 3 * nDotLen;
+ sal_uLong nDist = 2 * nDotLen;
+
+ switch( rLineData.mnStyle )
+ {
+ default:
+ case EXC_OBJ_LINE_SOLID:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ break;
+ case EXC_OBJ_LINE_DASH:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 0, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 0, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DASHDOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DASHDOTDOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 2, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_MEDTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 50 ) );
+ break;
+ case EXC_OBJ_LINE_DARKTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 25 ) );
+ break;
+ case EXC_OBJ_LINE_LIGHTTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 75 ) );
+ break;
+ case EXC_OBJ_LINE_NONE:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_NONE ) );
+ break;
+ }
+ }
+}
+
+void XclImpDrawObjBase::ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const
+{
+ if( rFillData.IsAuto() )
+ {
+ XclObjFillData aAutoData;
+ aAutoData.mnAuto = 0;
+ ConvertFillStyle( rSdrObj, aAutoData );
+ }
+ else if( rFillData.mnPattern == EXC_PATT_NONE )
+ {
+ rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) );
+ }
+ else
+ {
+ Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx );
+ Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx );
+ if( (rFillData.mnPattern == EXC_PATT_SOLID) || (aPattColor == aBackColor) )
+ {
+ rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XFillColorItem( OUString(), aPattColor ) );
+ }
+ else
+ {
+ static const sal_uInt8 sppnPatterns[][ 8 ] =
+ {
+ { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 },
+ { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD },
+ { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 },
+ { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 },
+ { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC },
+ { 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99 },
+ { 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99 },
+ { 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 },
+ { 0xCC, 0xFF, 0x33, 0xFF, 0xCC, 0xFF, 0x33, 0xFF },
+ { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 },
+ { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 },
+ { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 },
+ { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 },
+ { 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x11, 0x11, 0x11 },
+ { 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11 },
+ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
+ { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00 }
+ };
+ const sal_uInt8* const pnPattern = sppnPatterns[std::min<size_t>(rFillData.mnPattern - 2, SAL_N_ELEMENTS(sppnPatterns) - 1)];
+ // create 2-colored 8x8 DIB
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt32( 12 ).WriteInt16( 8 ).WriteInt16( 8 ).WriteUInt16( 1 ).WriteUInt16( 1 );
+ aMemStrm.WriteUChar( 0xFF ).WriteUChar( 0xFF ).WriteUChar( 0xFF );
+ aMemStrm.WriteUChar( 0x00 ).WriteUChar( 0x00 ).WriteUChar( 0x00 );
+ for( size_t nIdx = 0; nIdx < 8; ++nIdx )
+ aMemStrm.WriteUInt32( pnPattern[ nIdx ] ); // 32-bit little-endian
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ Bitmap aBitmap;
+ (void)ReadDIB(aBitmap, aMemStrm, false);
+
+ XOBitmap aXOBitmap(( BitmapEx(aBitmap) ));
+ aXOBitmap.Bitmap2Array();
+ if( aXOBitmap.GetBackgroundColor() == COL_BLACK )
+ ::std::swap( aPattColor, aBackColor );
+ aXOBitmap.SetPixelColor( aPattColor );
+ aXOBitmap.SetBackgroundColor( aBackColor );
+ aXOBitmap.Array2Bitmap();
+ aBitmap = aXOBitmap.GetBitmap().GetBitmap();
+
+ rSdrObj.SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ rSdrObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(BitmapEx(aBitmap))));
+ }
+ }
+}
+
+void XclImpDrawObjBase::ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const
+{
+ if( ::get_flag( nFrameFlags, EXC_OBJ_FRAME_SHADOW ) )
+ {
+ rSdrObj.SetMergedItem( makeSdrShadowItem( true ) );
+ rSdrObj.SetMergedItem( makeSdrShadowXDistItem( 35 ) );
+ rSdrObj.SetMergedItem( makeSdrShadowYDistItem( 35 ) );
+ rSdrObj.SetMergedItem( makeSdrShadowColorItem( GetPalette().GetColor( EXC_COLOR_WINDOWTEXT ) ) );
+ }
+}
+
+Color XclImpDrawObjBase::GetSolidLineColor( const XclObjLineData& rLineData ) const
+{
+ Color aColor( COL_TRANSPARENT );
+ if( rLineData.IsAuto() )
+ {
+ XclObjLineData aAutoData;
+ aAutoData.mnAuto = 0;
+ aColor = GetSolidLineColor( aAutoData );
+ }
+ else if( rLineData.mnStyle != EXC_OBJ_LINE_NONE )
+ {
+ aColor = GetPalette().GetColor( rLineData.mnColorIdx );
+ }
+ return aColor;
+}
+
+Color XclImpDrawObjBase::GetSolidFillColor( const XclObjFillData& rFillData ) const
+{
+ Color aColor( COL_TRANSPARENT );
+ if( rFillData.IsAuto() )
+ {
+ XclObjFillData aAutoData;
+ aAutoData.mnAuto = 0;
+ aColor = GetSolidFillColor( aAutoData );
+ }
+ else if( rFillData.mnPattern != EXC_PATT_NONE )
+ {
+ Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx );
+ Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx );
+ aColor = XclTools::GetPatternColor( aPattColor, aBackColor, rFillData.mnPattern );
+ }
+ return aColor;
+}
+
+void XclImpDrawObjBase::DoReadObj3( XclImpStream&, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj4( XclImpStream&, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj5( XclImpStream&, sal_uInt16, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj8SubRec( XclImpStream&, sal_uInt16, sal_uInt16 )
+{
+}
+
+std::size_t XclImpDrawObjBase::DoGetProgressSize() const
+{
+ return 1;
+}
+
+rtl::Reference<SdrObject> XclImpDrawObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& ) const
+{
+ rDffConv.Progress( GetProgressSize() );
+ return nullptr;
+}
+
+void XclImpDrawObjBase::DoPreProcessSdrObj( XclImpDffConverter&, SdrObject& ) const
+{
+ // trace if object is not printable
+ if( !IsPrintable() )
+ GetTracer().TraceObjectNotPrintable();
+}
+
+void XclImpDrawObjBase::DoPostProcessSdrObj( XclImpDffConverter&, SdrObject& ) const
+{
+}
+
+void XclImpDrawObjBase::ImplReadObj3( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ DoReadObj3( rStrm, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj4( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE );
+ DoReadObj4( rStrm, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj5( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize, nNameLen;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ nNameLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE );
+ DoReadObj5( rStrm, nNameLen, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj8( XclImpStream& rStrm )
+{
+ // back to beginning
+ rStrm.Seek( EXC_REC_SEEK_TO_BEGIN );
+
+ bool bLoop = true;
+ while (bLoop)
+ {
+ if (rStrm.GetRecLeft() < 4)
+ break;
+
+ sal_uInt16 nSubRecId = rStrm.ReaduInt16();
+ sal_uInt16 nSubRecSize = rStrm.ReaduInt16();
+ rStrm.PushPosition();
+ // sometimes the last subrecord has an invalid length (OBJLBSDATA) -> min()
+ nSubRecSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nSubRecSize, rStrm.GetRecLeft() ) );
+
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJCMO:
+ OSL_ENSURE( rStrm.GetRecPos() == 4, "XclImpDrawObjBase::ImplReadObj8 - unexpected OBJCMO subrecord" );
+ if( (rStrm.GetRecPos() == 4) && (nSubRecSize >= 6) )
+ {
+ sal_uInt16 nObjFlags;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16( );
+ nObjFlags = rStrm.ReaduInt16( );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJCMO_PRINTABLE );
+ }
+ break;
+ case EXC_ID_OBJMACRO:
+ ReadMacro8( rStrm );
+ break;
+ case EXC_ID_OBJEND:
+ bLoop = false;
+ break;
+ default:
+ DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+
+ rStrm.PopPosition();
+ rStrm.Ignore( nSubRecSize );
+ }
+
+ /* Call DoReadObj8SubRec() with EXC_ID_OBJEND for further stream
+ processing (e.g. charts), even if the OBJEND subrecord is missing. */
+ DoReadObj8SubRec( rStrm, EXC_ID_OBJEND, 0 );
+
+ /* Pictures that Excel reads from BIFF5 and writes to BIFF8 still have the
+ IMGDATA record following the OBJ record (but they use the image data
+ stored in DFF). The IMGDATA record may be continued by several CONTINUE
+ records. But the last CONTINUE record may be in fact an MSODRAWING
+ record that contains the DFF data of the next drawing object! So we
+ have to skip just enough CONTINUE records to look at the next
+ MSODRAWING/CONTINUE record. */
+ if( !((rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord()) )
+ return;
+
+ rStrm.Ignore( 4 );
+ sal_uInt32 nDataSize = rStrm.ReaduInt32();
+ nDataSize -= rStrm.GetRecLeft();
+ // skip following CONTINUE records until IMGDATA ends
+ while (true)
+ {
+ if (!nDataSize)
+ break;
+ if (rStrm.GetNextRecId() != EXC_ID_CONT)
+ break;
+ if (!rStrm.StartNextRecord())
+ break;
+ OSL_ENSURE( nDataSize >= rStrm.GetRecLeft(), "XclImpDrawObjBase::ImplReadObj8 - CONTINUE too long" );
+ nDataSize -= ::std::min< sal_uInt32 >( rStrm.GetRecLeft(), nDataSize );
+ }
+ OSL_ENSURE( nDataSize == 0, "XclImpDrawObjBase::ImplReadObj8 - missing CONTINUE records" );
+ // next record may be MSODRAWING or CONTINUE or anything else
+}
+
+void XclImpDrawObjVector::InsertGrouped( XclImpDrawObjRef const & xDrawObj )
+{
+ if( !mObjs.empty() )
+ if( XclImpGroupObj* pGroupObj = dynamic_cast< XclImpGroupObj* >( mObjs.back().get() ) )
+ if( pGroupObj->TryInsert( xDrawObj ) )
+ return;
+ mObjs.push_back( xDrawObj );
+}
+
+std::size_t XclImpDrawObjVector::GetProgressSize() const
+{
+ return std::accumulate(mObjs.begin(), mObjs.end(), std::size_t(0),
+ [](const std::size_t& rSum, const XclImpDrawObjRef& rxObj) { return rSum + rxObj->GetProgressSize(); });
+}
+
+XclImpPhObj::XclImpPhObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot )
+{
+ SetProcessSdrObj( false );
+}
+
+XclImpGroupObj::XclImpGroupObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnFirstUngrouped( 0 )
+{
+}
+
+bool XclImpGroupObj::TryInsert( XclImpDrawObjRef const & xDrawObj )
+{
+ if( xDrawObj->GetObjId() == mnFirstUngrouped )
+ return false;
+ // insert into own list or into nested group
+ maChildren.InsertGrouped( xDrawObj );
+ return true;
+}
+
+void XclImpGroupObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpGroupObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpGroupObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+std::size_t XclImpGroupObj::DoGetProgressSize() const
+{
+ return XclImpDrawObjBase::DoGetProgressSize() + maChildren.GetProgressSize();
+}
+
+rtl::Reference<SdrObject> XclImpGroupObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& /*rAnchorRect*/ ) const
+{
+ rtl::Reference<SdrObjGroup> xSdrObj(
+ new SdrObjGroup(
+ *GetDoc().GetDrawLayer()));
+ // child objects in BIFF2-BIFF5 have absolute size, not needed to pass own anchor rectangle
+ SdrObjList& rObjList = *xSdrObj->GetSubList(); // SdrObjGroup always returns existing sublist
+ for( const auto& rxChild : maChildren )
+ rDffConv.ProcessObject( rObjList, *rxChild );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpLineObj::XclImpLineObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnArrows( 0 ),
+ mnStartPoint( EXC_OBJ_LINE_TL )
+{
+ SetAreaObj( false );
+}
+
+void XclImpLineObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpLineObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpLineObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+rtl::Reference<SdrObject> XclImpLineObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ ::basegfx::B2DPolygon aB2DPolygon;
+ switch( mnStartPoint )
+ {
+ default:
+ case EXC_OBJ_LINE_TL:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) );
+ break;
+ case EXC_OBJ_LINE_TR:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) );
+ break;
+ case EXC_OBJ_LINE_BR:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) );
+ break;
+ case EXC_OBJ_LINE_BL:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) );
+ break;
+ }
+ rtl::Reference<SdrObject> xSdrObj(
+ new SdrPathObj(
+ *GetDoc().GetDrawLayer(),
+ SdrObjKind::Line,
+ ::basegfx::B2DPolyPolygon(aB2DPolygon)));
+ ConvertLineStyle( *xSdrObj, maLineData );
+
+ // line ends
+ sal_uInt8 nArrowType = ::extract_value< sal_uInt8 >( mnArrows, 0, 4 );
+ bool bLineStart = false;
+ bool bLineEnd = false;
+ bool bFilled = false;
+ switch( nArrowType )
+ {
+ case EXC_OBJ_ARROW_OPEN: bLineStart = false; bLineEnd = true; bFilled = false; break;
+ case EXC_OBJ_ARROW_OPENBOTH: bLineStart = true; bLineEnd = true; bFilled = false; break;
+ case EXC_OBJ_ARROW_FILLED: bLineStart = false; bLineEnd = true; bFilled = true; break;
+ case EXC_OBJ_ARROW_FILLEDBOTH: bLineStart = true; bLineEnd = true; bFilled = true; break;
+ }
+ if( bLineStart || bLineEnd )
+ {
+ sal_uInt8 nArrowWidth = ::extract_value< sal_uInt8 >( mnArrows, 4, 4 );
+ double fArrowWidth = 3.0;
+ switch( nArrowWidth )
+ {
+ case EXC_OBJ_ARROW_NARROW: fArrowWidth = 2.0; break;
+ case EXC_OBJ_ARROW_MEDIUM: fArrowWidth = 3.0; break;
+ case EXC_OBJ_ARROW_WIDE: fArrowWidth = 5.0; break;
+ }
+
+ sal_uInt8 nArrowLength = ::extract_value< sal_uInt8 >( mnArrows, 8, 4 );
+ double fArrowLength = 3.0;
+ switch( nArrowLength )
+ {
+ case EXC_OBJ_ARROW_NARROW: fArrowLength = 2.5; break;
+ case EXC_OBJ_ARROW_MEDIUM: fArrowLength = 3.5; break;
+ case EXC_OBJ_ARROW_WIDE: fArrowLength = 6.0; break;
+ }
+
+ ::basegfx::B2DPolygon aArrowPoly;
+#define EXC_ARROW_POINT( x, y ) ::basegfx::B2DPoint( fArrowWidth * (x), fArrowLength * (y) )
+ if( bFilled )
+ {
+ aArrowPoly.append( EXC_ARROW_POINT( 0, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100, 100 ) );
+ }
+ else
+ {
+ sal_uInt8 nLineWidth = ::limit_cast< sal_uInt8 >( maLineData.mnWidth, EXC_OBJ_LINE_THIN, EXC_OBJ_LINE_THICK );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100, 100 - 3 * nLineWidth ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100 - 5 * nLineWidth, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 12 * nLineWidth ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 5 * nLineWidth, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 0, 100 - 3 * nLineWidth ) );
+ }
+#undef EXC_ARROW_POINT
+
+ ::basegfx::B2DPolyPolygon aArrowPolyPoly( aArrowPoly );
+ tools::Long nWidth = static_cast< tools::Long >( 125 * fArrowWidth );
+ if( bLineStart )
+ {
+ xSdrObj->SetMergedItem( XLineStartItem( OUString(), aArrowPolyPoly ) );
+ xSdrObj->SetMergedItem( XLineStartWidthItem( nWidth ) );
+ xSdrObj->SetMergedItem( XLineStartCenterItem( false ) );
+ }
+ if( bLineEnd )
+ {
+ xSdrObj->SetMergedItem( XLineEndItem( OUString(), aArrowPolyPoly ) );
+ xSdrObj->SetMergedItem( XLineEndWidthItem( nWidth ) );
+ xSdrObj->SetMergedItem( XLineEndCenterItem( false ) );
+ }
+ }
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpRectObj::XclImpRectObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnFrameFlags( 0 )
+{
+ SetAreaObj( true );
+}
+
+void XclImpRectObj::ReadFrameData( XclImpStream& rStrm )
+{
+ rStrm >> maFillData >> maLineData;
+ mnFrameFlags = rStrm.ReaduInt16();
+}
+
+void XclImpRectObj::ConvertRectStyle( SdrObject& rSdrObj ) const
+{
+ ConvertLineStyle( rSdrObj, maLineData );
+ ConvertFillStyle( rSdrObj, maFillData );
+ ConvertFrameStyle( rSdrObj, mnFrameFlags );
+}
+
+void XclImpRectObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpRectObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpRectObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+rtl::Reference<SdrObject> XclImpRectObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ rtl::Reference<SdrObject> xSdrObj(
+ new SdrRectObj(
+ *GetDoc().GetDrawLayer(),
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpOvalObj::XclImpOvalObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot )
+{
+}
+
+rtl::Reference<SdrObject> XclImpOvalObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ rtl::Reference<SdrObject> xSdrObj(
+ new SdrCircObj(
+ *GetDoc().GetDrawLayer(),
+ SdrCircKind::Full,
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpArcObj::XclImpArcObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnQuadrant( EXC_OBJ_ARC_TR )
+{
+ SetAreaObj( false ); // arc may be 2-dimensional
+}
+
+void XclImpArcObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpArcObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpArcObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+rtl::Reference<SdrObject> XclImpArcObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ tools::Rectangle aNewRect = rAnchorRect;
+ Degree100 nStartAngle;
+ Degree100 nEndAngle;
+ switch( mnQuadrant )
+ {
+ default:
+ case EXC_OBJ_ARC_TR:
+ nStartAngle = 0_deg100;
+ nEndAngle = 9000_deg100;
+ aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) );
+ aNewRect.AdjustBottom(rAnchorRect.GetHeight() );
+ break;
+ case EXC_OBJ_ARC_TL:
+ nStartAngle = 9000_deg100;
+ nEndAngle = 18000_deg100;
+ aNewRect.AdjustRight(rAnchorRect.GetWidth() );
+ aNewRect.AdjustBottom(rAnchorRect.GetHeight() );
+ break;
+ case EXC_OBJ_ARC_BL:
+ nStartAngle = 18000_deg100;
+ nEndAngle = 27000_deg100;
+ aNewRect.AdjustRight(rAnchorRect.GetWidth() );
+ aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) );
+ break;
+ case EXC_OBJ_ARC_BR:
+ nStartAngle = 27000_deg100;
+ nEndAngle = 0_deg100;
+ aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) );
+ aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) );
+ break;
+ }
+ SdrCircKind eObjKind = maFillData.IsFilled() ? SdrCircKind::Section : SdrCircKind::Arc;
+ rtl::Reference<SdrObject> xSdrObj(
+ new SdrCircObj(
+ *GetDoc().GetDrawLayer(),
+ eObjKind,
+ aNewRect,
+ nStartAngle,
+ nEndAngle));
+ ConvertFillStyle( *xSdrObj, maFillData );
+ ConvertLineStyle( *xSdrObj, maLineData );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpPolygonObj::XclImpPolygonObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot ),
+ mnPolyFlags( 0 ),
+ mnPointCount( 0 )
+{
+ SetAreaObj( false ); // polygon may be 2-dimensional
+}
+
+void XclImpPolygonObj::ReadCoordList( XclImpStream& rStrm )
+{
+ if( (rStrm.GetNextRecId() == EXC_ID_COORDLIST) && rStrm.StartNextRecord() )
+ {
+ OSL_ENSURE( rStrm.GetRecLeft() / 4 == mnPointCount, "XclImpPolygonObj::ReadCoordList - wrong polygon point count" );
+ while (true)
+ {
+ if (rStrm.GetRecLeft() < 4)
+ break;
+ sal_uInt16 nX = rStrm.ReaduInt16();
+ sal_uInt16 nY = rStrm.ReaduInt16();
+ maCoords.emplace_back( nX, nY );
+ }
+ }
+}
+
+void XclImpPolygonObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ mnPolyFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnPointCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+ ReadMacro4( rStrm, nMacroSize );
+ ReadCoordList( rStrm );
+}
+
+void XclImpPolygonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ mnPolyFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnPointCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadCoordList( rStrm );
+}
+
+namespace {
+
+::basegfx::B2DPoint lclGetPolyPoint( const tools::Rectangle& rAnchorRect, const Point& rPoint )
+{
+ return ::basegfx::B2DPoint(
+ rAnchorRect.Left() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.X(), 16384.0 ) / 16384.0 * rAnchorRect.GetWidth() + 0.5 ),
+ rAnchorRect.Top() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.Y(), 16384.0 ) / 16384.0 * rAnchorRect.GetHeight() + 0.5 ) );
+}
+
+} // namespace
+
+rtl::Reference<SdrObject> XclImpPolygonObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ rtl::Reference<SdrObject> xSdrObj;
+ if( maCoords.size() >= 2 )
+ {
+ // create the polygon
+ ::basegfx::B2DPolygon aB2DPolygon;
+ for( const auto& rCoord : maCoords )
+ aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, rCoord ) );
+ // close polygon if specified
+ if( ::get_flag( mnPolyFlags, EXC_OBJ_POLY_CLOSED ) && (maCoords.front() != maCoords.back()) )
+ aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, maCoords.front() ) );
+ // create the SdrObject
+ SdrObjKind eObjKind = maFillData.IsFilled() ? SdrObjKind::PathPoly : SdrObjKind::PathPolyLine;
+ xSdrObj =
+ new SdrPathObj(
+ *GetDoc().GetDrawLayer(),
+ eObjKind,
+ ::basegfx::B2DPolyPolygon(aB2DPolygon));
+ ConvertRectStyle( *xSdrObj );
+ }
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpObjTextData::ReadByteString( XclImpStream& rStrm )
+{
+ mxString.reset();
+ if( maData.mnTextLen > 0 )
+ {
+ mxString = std::make_shared<XclImpString>( rStrm.ReadRawByteString( maData.mnTextLen ) );
+ // skip padding byte for word boundaries
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+ }
+}
+
+void XclImpObjTextData::ReadFormats( XclImpStream& rStrm )
+{
+ if( mxString )
+ mxString->ReadObjFormats( rStrm, maData.mnFormatSize );
+ else
+ rStrm.Ignore( maData.mnFormatSize );
+}
+
+XclImpTextObj::XclImpTextObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot )
+{
+}
+
+void XclImpTextObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj3( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ maTextData.ReadFormats( rStrm );
+}
+
+void XclImpTextObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj3( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ maTextData.ReadFormats( rStrm );
+}
+
+void XclImpTextObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj5( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ rStrm.Ignore( maTextData.maData.mnLinkSize ); // ignore text link formula
+ maTextData.ReadFormats( rStrm );
+}
+
+rtl::Reference<SdrObject> XclImpTextObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ rtl::Reference<SdrObjCustomShape> xSdrObj(
+ new SdrObjCustomShape(
+ *GetDoc().GetDrawLayer()));
+ xSdrObj->NbcSetSnapRect( rAnchorRect );
+ OUString aRectType = "rectangle";
+ xSdrObj->MergeDefaultAttributes( &aRectType );
+ ConvertRectStyle( *xSdrObj );
+ bool bAutoSize = ::get_flag( maTextData.maData.mnFlags, EXC_OBJ_TEXT_AUTOSIZE );
+ xSdrObj->SetMergedItem( makeSdrTextAutoGrowWidthItem( bAutoSize ) );
+ xSdrObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( bAutoSize ) );
+ xSdrObj->SetMergedItem( makeSdrTextWordWrapItem( true ) );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpTextObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // set text data
+ if( SdrTextObj* pTextObj = DynCastSdrTextObj( &rSdrObj ) )
+ {
+ if( maTextData.mxString )
+ {
+ if( maTextData.mxString->IsRich() )
+ {
+ if (maTextData.mxString->GetText().getLength() > 1024 && utl::ConfigManager::IsFuzzing())
+ {
+ SAL_WARN("sc.filter", "truncating slow long rich text for fuzzing performance");
+ maTextData.mxString->SetText(maTextData.mxString->GetText().copy(0, 1024));
+ }
+
+ // rich text
+ std::unique_ptr< EditTextObject > xEditObj(
+ XclImpStringHelper::CreateTextObject( GetRoot(), *maTextData.mxString ) );
+ OutlinerParaObject aOutlineObj(std::move(xEditObj));
+ aOutlineObj.SetOutlinerMode( OutlinerMode::TextObject );
+ pTextObj->NbcSetOutlinerParaObject( std::move(aOutlineObj) );
+ }
+ else
+ {
+ // plain text
+ pTextObj->NbcSetText( maTextData.mxString->GetText() );
+ }
+
+ /* #i96858# Do not apply any formatting if there is no text.
+ SdrObjCustomShape::SetVerticalWriting (initiated from
+ SetMergedItem) calls SdrTextObj::ForceOutlinerParaObject which
+ ensures that we can erroneously write a ClientTextbox record
+ (with no content) while exporting to XLS, which can cause a
+ corrupted exported document. */
+
+ SvxAdjust eHorAlign = SvxAdjust::Left;
+ SdrTextVertAdjust eVerAlign = SDRTEXTVERTADJUST_TOP;
+
+ // orientation (this is only a fake, drawing does not support real text orientation)
+ namespace csst = ::com::sun::star::text;
+ csst::WritingMode eWriteMode = csst::WritingMode_LR_TB;
+ switch( maTextData.maData.mnOrient )
+ {
+ default:
+ case EXC_OBJ_ORIENT_NONE:
+ {
+ eWriteMode = csst::WritingMode_LR_TB;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_HOR_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_HOR_RIGHT: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_HOR_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_VER_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_VER_BOTTOM: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_VER_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ }
+ break;
+
+ case EXC_OBJ_ORIENT_90CCW:
+ {
+ if( SdrObjCustomShape* pObjCustomShape = dynamic_cast< SdrObjCustomShape* >( &rSdrObj ) )
+ {
+ css::beans::PropertyValue aTextRotateAngle;
+ aTextRotateAngle.Name = "TextRotateAngle";
+ aTextRotateAngle.Value <<= 180.0;
+ SdrCustomShapeGeometryItem aGeometryItem(pObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ aGeometryItem.SetPropertyValue( aTextRotateAngle );
+ pObjCustomShape->SetMergedItem( aGeometryItem );
+ }
+ eWriteMode = csst::WritingMode_TB_RL;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ));
+ switch( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ {
+ eHorAlign = SvxAdjust::Center;
+ }
+ break;
+
+ default:
+ {
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ }
+ }
+ }
+ break;
+
+ case EXC_OBJ_ORIENT_STACKED:
+ {
+ // sj: STACKED is not supported, maybe it can be optimized here a bit
+ [[fallthrough]];
+ }
+ case EXC_OBJ_ORIENT_90CW:
+ {
+ eWriteMode = csst::WritingMode_TB_RL;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ));
+ switch ( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ {
+ eHorAlign = SvxAdjust::Center;
+ }
+ break;
+
+ default:
+ {
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ rSdrObj.SetMergedItem( SvxAdjustItem( eHorAlign, EE_PARA_JUST ) );
+ rSdrObj.SetMergedItem( SdrTextVertAdjustItem( eVerAlign ) );
+ rSdrObj.SetMergedItem( SvxWritingModeItem( eWriteMode, SDRATTR_TEXTDIRECTION ) );
+ }
+ }
+ // base class processing
+ XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+}
+
+XclImpChartObj::XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpRectObj( rRoot ),
+ mbOwnTab( bOwnTab )
+{
+ SetSimpleMacro( false );
+ SetCustomDffObj( true );
+}
+
+void XclImpChartObj::ReadChartSubStream( XclImpStream& rStrm )
+{
+ /* If chart is read from a chartsheet (mbOwnTab == true), the BOF record
+ has already been read. If chart is embedded as object, the next record
+ has to be the BOF record. */
+ if( mbOwnTab )
+ {
+ /* #i109800# The input stream may point somewhere inside the chart
+ substream and not exactly to the leading BOF record. To read this
+ record correctly in the following, the stream has to rewind it, so
+ that the next call to StartNextRecord() will find it correctly. */
+ if( rStrm.GetRecId() != EXC_ID5_BOF )
+ rStrm.RewindRecord();
+ }
+ else
+ {
+ if( (rStrm.GetNextRecId() == EXC_ID5_BOF) && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nBofType;
+ rStrm.Seek( 2 );
+ nBofType = rStrm.ReaduInt16();
+ SAL_WARN_IF( nBofType != EXC_BOF_CHART, "sc.filter", "XclImpChartObj::ReadChartSubStream - no chart BOF record" );
+ }
+ else
+ {
+ SAL_INFO("sc.filter", "XclImpChartObj::ReadChartSubStream - missing chart substream");
+ return;
+ }
+ }
+
+ // read chart, even if BOF record contains wrong substream identifier
+ mxChart = std::make_shared<XclImpChart>( GetRoot(), mbOwnTab );
+ mxChart->ReadChartSubStream( rStrm );
+ if( mbOwnTab )
+ FinalizeTabChart();
+}
+
+void XclImpChartObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadMacro3( rStrm, nMacroSize );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadMacro4( rStrm, nMacroSize );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadChartSubStream( rStrm );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 /*nSubRecSize*/ )
+{
+ // read the following chart substream
+ if( nSubRecId == EXC_ID_OBJEND )
+ {
+ // enable CONTINUE handling for the entire chart substream
+ rStrm.ResetRecord( true );
+ ReadChartSubStream( rStrm );
+ /* disable CONTINUE handling again to be able to read
+ following CONTINUE records as MSODRAWING records. */
+ rStrm.ResetRecord( false );
+ }
+}
+
+std::size_t XclImpChartObj::DoGetProgressSize() const
+{
+ return mxChart ? mxChart->GetProgressSize() : 1;
+}
+
+rtl::Reference<SdrObject> XclImpChartObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ rtl::Reference<SdrObject> xSdrObj;
+ ScDocShell* pDocShell = GetDocShell();
+ if( rDffConv.SupportsOleObjects() && SvtModuleOptions().IsChart() && pDocShell && mxChart && !mxChart->IsPivotChart() )
+ {
+ // create embedded chart object
+ OUString aEmbObjName;
+ OUString sBaseURL(GetRoot().GetMedium().GetBaseURL());
+ Reference< XEmbeddedObject > xEmbObj = pDocShell->GetEmbeddedObjectContainer().
+ CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aEmbObjName, &sBaseURL );
+
+ if (!xEmbObj)
+ return xSdrObj;
+
+ /* Set the size to the embedded object, this prevents that font sizes
+ of text objects are changed in the chart when the object is
+ inserted into the draw page. */
+ sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT;
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xEmbObj->getMapUnit( nAspect ) );
+ Size aSize( OutputDevice::LogicToLogic( rAnchorRect.GetSize(), MapMode( MapUnit::Map100thMM ), MapMode( aUnit ) ) );
+ css::awt::Size aAwtSize( aSize.Width(), aSize.Height() );
+ xEmbObj->setVisualAreaSize( nAspect, aAwtSize );
+
+ // #i121334# This call will change the chart's default background fill from white to transparent.
+ // Add here again if this is wanted (see task description for details)
+ // ChartHelper::AdaptDefaultsForChart( xEmbObj );
+
+ // create the container OLE object
+ xSdrObj =
+ new SdrOle2Obj(
+ *GetDoc().GetDrawLayer(),
+ svt::EmbeddedObjectRef(xEmbObj, nAspect),
+ aEmbObjName,
+ rAnchorRect);
+ }
+
+ return xSdrObj;
+}
+
+void XclImpChartObj::DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ const SdrOle2Obj* pSdrOleObj = dynamic_cast< const SdrOle2Obj* >( &rSdrObj );
+ if( !(mxChart && pSdrOleObj) )
+ return;
+
+ const Reference< XEmbeddedObject >& xEmbObj = pSdrOleObj->GetObjRef();
+ if( xEmbObj.is() && ::svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) try
+ {
+ Reference< XEmbedPersist > xPersist( xEmbObj, UNO_QUERY_THROW );
+ Reference< XModel > xModel( xEmbObj->getComponent(), UNO_QUERY_THROW );
+ mxChart->Convert( xModel, rDffConv, xPersist->getEntryName(), rSdrObj.GetLogicRect() );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void XclImpChartObj::FinalizeTabChart()
+{
+ /* #i44077# Calculate and store DFF anchor for sheet charts.
+ Needed to get used area if this chart is inserted as OLE object. */
+ OSL_ENSURE( mbOwnTab, "XclImpChartObj::FinalizeTabChart - not allowed for embedded chart objects" );
+
+ // set uninitialized page to landscape
+ if( !GetPageSettings().GetPageData().mbValid )
+ GetPageSettings().SetPaperSize( EXC_PAPERSIZE_DEFAULT, false );
+
+ // calculate size of the chart object
+ const XclPageData& rPageData = GetPageSettings().GetPageData();
+ Size aPaperSize = rPageData.GetScPaperSize();
+
+ tools::Long nWidth = XclTools::GetHmmFromTwips( aPaperSize.Width() );
+ tools::Long nHeight = XclTools::GetHmmFromTwips( aPaperSize.Height() );
+
+ // subtract page margins, give some more extra space
+ nWidth -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfLeftMargin + rPageData.mfRightMargin), static_cast<sal_Int32>(2000));
+ nHeight -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfTopMargin + rPageData.mfBottomMargin), static_cast<sal_Int32>(1000));
+
+ // print column/row headers?
+ if( rPageData.mbPrintHeadings )
+ {
+ nWidth -= 2000;
+ nHeight -= 1000;
+ }
+
+ // create the object anchor
+ XclObjAnchor aAnchor;
+ aAnchor.SetRect( GetRoot(), GetCurrScTab(), tools::Rectangle( 1000, 500, nWidth, nHeight ), MapUnit::Map100thMM );
+ SetAnchor( aAnchor );
+}
+
+XclImpNoteObj::XclImpNoteObj( const XclImpRoot& rRoot ) :
+ XclImpTextObj( rRoot ),
+ maScPos( ScAddress::INITIALIZE_INVALID ),
+ mnNoteFlags( 0 )
+{
+ SetSimpleMacro( false );
+ // caption object will be created manually
+ SetInsertSdrObj( false );
+}
+
+void XclImpNoteObj::SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags )
+{
+ maScPos = rScPos;
+ mnNoteFlags = nNoteFlags;
+}
+
+void XclImpNoteObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // create formatted text
+ XclImpTextObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+ OutlinerParaObject* pOutlinerObj = rSdrObj.GetOutlinerParaObject();
+ if( maScPos.IsValid() && pOutlinerObj )
+ {
+ // create cell note with all data from drawing object
+ ScNoteUtil::CreateNoteFromObjectData(
+ GetDoc(), maScPos,
+ rSdrObj.GetMergedItemSet(),
+ OUString(), *pOutlinerObj,
+ rSdrObj.GetLogicRect(),
+ ::get_flag( mnNoteFlags, EXC_NOTE_VISIBLE ) );
+ }
+}
+
+XclImpControlHelper::XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode ) :
+ mrRoot( rRoot ),
+ meBindMode( eBindMode )
+{
+}
+
+XclImpControlHelper::~XclImpControlHelper()
+{
+}
+
+rtl::Reference<SdrObject> XclImpControlHelper::CreateSdrObjectFromShape(
+ const Reference< XShape >& rxShape, const tools::Rectangle& rAnchorRect ) const
+{
+ mxShape = rxShape;
+ rtl::Reference<SdrObject> xSdrObj( SdrObject::getSdrObjectFromXShape( rxShape ) );
+ if( xSdrObj )
+ {
+ xSdrObj->NbcSetSnapRect( rAnchorRect );
+ // #i30543# insert into control layer
+ xSdrObj->NbcSetLayer( SC_LAYER_CONTROLS );
+ }
+ return xSdrObj;
+}
+
+void XclImpControlHelper::ApplySheetLinkProps() const
+{
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ // sheet links
+ ScDocShell* pDocShell = mrRoot.GetDocShell();
+ if(!pDocShell)
+ return;
+
+ ScModelObj* pModelObj = pDocShell->GetModel();
+ if( !pModelObj )
+ return;
+
+ // cell link
+ if( mxCellLink ) try
+ {
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY_THROW );
+
+ // create argument sequence for createInstanceWithArguments()
+ CellAddress aApiAddress;
+ ScUnoConversion::FillApiAddress( aApiAddress, *mxCellLink );
+
+ NamedValue aValue;
+ aValue.Name = SC_UNONAME_BOUNDCELL;
+ aValue.Value <<= aApiAddress;
+
+ Sequence< Any > aArgs{ Any(aValue) };
+
+ // create the CellValueBinding instance and set at the control model
+ OUString aServiceName;
+ switch( meBindMode )
+ {
+ case EXC_CTRL_BINDCONTENT: aServiceName = SC_SERVICENAME_VALBIND; break;
+ case EXC_CTRL_BINDPOSITION: aServiceName = SC_SERVICENAME_LISTCELLBIND; break;
+ }
+ Reference< XValueBinding > xBinding(
+ pModelObj->createInstanceWithArguments( aServiceName, aArgs ), UNO_QUERY_THROW );
+ xBindable->setValueBinding( xBinding );
+ }
+ catch( const Exception& )
+ {
+ }
+
+ // source range
+ if( !mxSrcRange )
+ return;
+
+ try
+ {
+ Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY_THROW );
+
+ // create argument sequence for createInstanceWithArguments()
+ CellRangeAddress aApiRange;
+ ScUnoConversion::FillApiRange( aApiRange, *mxSrcRange );
+
+ NamedValue aValue;
+ aValue.Name = SC_UNONAME_CELLRANGE;
+ aValue.Value <<= aApiRange;
+
+ Sequence< Any > aArgs{ Any(aValue) };
+
+ // create the EntrySource instance and set at the control model
+ Reference< XListEntrySource > xEntrySource( pModelObj->createInstanceWithArguments(
+ SC_SERVICENAME_LISTSOURCE, aArgs ), UNO_QUERY_THROW );
+ xEntrySink->setListEntrySource( xEntrySource );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void XclImpControlHelper::ProcessControl( const XclImpDrawObjBase& rDrawObj ) const
+{
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ ApplySheetLinkProps();
+
+ ScfPropertySet aPropSet( xCtrlModel );
+
+ // #i51348# set object name at control model
+ aPropSet.SetStringProperty( "Name", rDrawObj.GetObjName() );
+
+ // control visible and printable?
+ aPropSet.SetBoolProperty( "EnableVisible", rDrawObj.IsVisible() );
+ aPropSet.SetBoolProperty( "Printable", rDrawObj.IsPrintable() );
+
+ // virtual call for type specific processing
+ DoProcessControl( aPropSet );
+}
+
+void XclImpControlHelper::ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize )
+{
+ ScRangeList aScRanges;
+ ReadRangeList( aScRanges, rStrm, bWithBoundSize );
+ // Use first cell of first range
+ if ( !aScRanges.empty() )
+ {
+ const ScRange & rScRange = aScRanges.front();
+ mxCellLink = std::make_shared<ScAddress>( rScRange.aStart );
+ }
+}
+
+void XclImpControlHelper::ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize )
+{
+ ScRangeList aScRanges;
+ ReadRangeList( aScRanges, rStrm, bWithBoundSize );
+ // Use first range
+ if ( !aScRanges.empty() )
+ {
+ const ScRange & rScRange = aScRanges.front();
+ mxSrcRange = std::make_shared<ScRange>( rScRange );
+ }
+}
+
+void XclImpControlHelper::DoProcessControl( ScfPropertySet& ) const
+{
+}
+
+void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm )
+{
+ XclTokenArray aXclTokArr;
+ sal_uInt16 nSize = XclTokenArray::ReadSize(rStrm);
+ rStrm.Ignore( 4 );
+ aXclTokArr.ReadArray(nSize, rStrm);
+ mrRoot.GetFormulaCompiler().CreateRangeList( rScRanges, EXC_FMLATYPE_CONTROL, aXclTokArr, rStrm );
+}
+
+void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize )
+{
+ if( bWithBoundSize )
+ {
+ sal_uInt16 nSize;
+ nSize = rStrm.ReaduInt16();
+ if( nSize > 0 )
+ {
+ rStrm.PushPosition();
+ ReadRangeList( rScRanges, rStrm );
+ rStrm.PopPosition();
+ rStrm.Ignore( nSize );
+ }
+ }
+ else
+ {
+ ReadRangeList( rScRanges, rStrm );
+ }
+}
+
+XclImpTbxObjBase::XclImpTbxObjBase( const XclImpRoot& rRoot ) :
+ XclImpTextObj( rRoot ),
+ XclImpControlHelper( rRoot, EXC_CTRL_BINDPOSITION )
+{
+ SetSimpleMacro( false );
+ SetCustomDffObj( true );
+}
+
+namespace {
+
+void lclExtractColor( sal_uInt8& rnColorIdx, const DffPropSet& rDffPropSet, sal_uInt32 nPropId )
+{
+ if( rDffPropSet.IsProperty( nPropId ) )
+ {
+ sal_uInt32 nColor = rDffPropSet.GetPropertyValue( nPropId, 0 );
+ if( (nColor & 0xFF000000) == 0x08000000 )
+ rnColorIdx = ::extract_value< sal_uInt8 >( nColor, 0, 8 );
+ }
+}
+
+} // namespace
+
+void XclImpTbxObjBase::SetDffProperties( const DffPropSet& rDffPropSet )
+{
+ maFillData.mnPattern = rDffPropSet.GetPropertyBool( DFF_Prop_fFilled ) ? EXC_PATT_SOLID : EXC_PATT_NONE;
+ lclExtractColor( maFillData.mnBackColorIdx, rDffPropSet, DFF_Prop_fillBackColor );
+ lclExtractColor( maFillData.mnPattColorIdx, rDffPropSet, DFF_Prop_fillColor );
+ ::set_flag( maFillData.mnAuto, EXC_OBJ_LINE_AUTO, false );
+
+ maLineData.mnStyle = rDffPropSet.GetPropertyBool( DFF_Prop_fLine ) ? EXC_OBJ_LINE_SOLID : EXC_OBJ_LINE_NONE;
+ lclExtractColor( maLineData.mnColorIdx, rDffPropSet, DFF_Prop_lineColor );
+ ::set_flag( maLineData.mnAuto, EXC_OBJ_FILL_AUTO, false );
+}
+
+void XclImpControlHelper::SetStringProperty(const OUString& sName, const OUString& sVal)
+{
+ Reference<XControlModel> xCtrlModel = XclControlHelper::GetControlModel(mxShape);
+ if (!xCtrlModel.is())
+ return;
+
+ ScfPropertySet aProps(xCtrlModel);
+ aProps.SetStringProperty(sName, sVal);
+}
+
+bool XclImpTbxObjBase::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor ) const
+{
+ return XclControlHelper::FillMacroDescriptor( rDescriptor, DoGetEventType(), GetMacroName(), GetDocShell() );
+}
+
+void XclImpTbxObjBase::ConvertFont( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ const XclFormatRunVec& rFormatRuns = maTextData.mxString->GetFormats();
+ if( rFormatRuns.empty() )
+ GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet );
+ else
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, rFormatRuns.front().mnFontIdx );
+ }
+}
+
+void XclImpTbxObjBase::ConvertLabel( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ OUString aLabel = maTextData.mxString->GetText();
+ if( maTextData.maData.mnShortcut > 0 )
+ {
+ sal_Int32 nPos = aLabel.indexOf( static_cast< sal_Unicode >( maTextData.maData.mnShortcut ) );
+ if( nPos != -1 )
+ aLabel = aLabel.replaceAt( nPos, 0, u"~" );
+ }
+ rPropSet.SetStringProperty( "Label", aLabel );
+
+ //Excel Alt text <==> Aoo description
+ //For TBX control, if user does not operate alt text, alt text will be set label text as default value in Excel.
+ //In this case, DFF_Prop_wzDescription will not be set in excel file.
+ //So In the end of SvxMSDffManager::ImportShape, description will not be set. But actually in excel,
+ //the alt text is the label value. So here set description as label text first which is called before ImportShape.
+ Reference< css::beans::XPropertySet > xPropset( mxShape, UNO_QUERY );
+ try{
+ if(xPropset.is())
+ xPropset->setPropertyValue( "Description", Any(aLabel) );
+ }catch( ... )
+ {
+ SAL_WARN("sc.filter", "Can't set a default text for TBX Control ");
+ }
+ }
+ ConvertFont( rPropSet );
+}
+
+rtl::Reference<SdrObject> XclImpTbxObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ rtl::Reference<SdrObject> xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpTbxObjBase::DoPreProcessSdrObj( XclImpDffConverter& /*rDffConv*/, SdrObject& /*rSdrObj*/ ) const
+{
+ // do not call DoPreProcessSdrObj() from base class (to skip text processing)
+ ProcessControl( *this );
+}
+
+XclImpButtonObj::XclImpButtonObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ /* Horizontal text alignment. For unknown reason, the property type is a
+ simple sal_Int16 and not a com.sun.star.style.HorizontalAlignment. */
+ sal_Int16 nHorAlign = 1;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: nHorAlign = 0; break;
+ case EXC_OBJ_HOR_CENTER: nHorAlign = 1; break;
+ case EXC_OBJ_HOR_RIGHT: nHorAlign = 2; break;
+ }
+ rPropSet.SetProperty( "Align", nHorAlign );
+
+ // vertical text alignment
+ namespace csss = ::com::sun::star::style;
+ csss::VerticalAlignment eVerAlign = csss::VerticalAlignment_MIDDLE;
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eVerAlign = csss::VerticalAlignment_TOP; break;
+ case EXC_OBJ_VER_CENTER: eVerAlign = csss::VerticalAlignment_MIDDLE; break;
+ case EXC_OBJ_VER_BOTTOM: eVerAlign = csss::VerticalAlignment_BOTTOM; break;
+ }
+ rPropSet.SetProperty( "VerticalAlign", eVerAlign );
+
+ // always wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", true );
+
+ // default button
+ bool bDefButton = ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_DEFAULT );
+ rPropSet.SetBoolProperty( "DefaultButton", bDefButton );
+
+ // button type (flags cannot be combined in OOo)
+ namespace cssa = ::com::sun::star::awt;
+ cssa::PushButtonType eButtonType = cssa::PushButtonType_STANDARD;
+ if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CLOSE ) )
+ eButtonType = cssa::PushButtonType_OK;
+ else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CANCEL ) )
+ eButtonType = cssa::PushButtonType_CANCEL;
+ else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_HELP ) )
+ eButtonType = cssa::PushButtonType_HELP;
+ // property type is short, not enum
+ rPropSet.SetProperty( "PushButtonType", sal_Int16( eButtonType ) );
+}
+
+OUString XclImpButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.CommandButton";
+}
+
+XclTbxEventType XclImpButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpCheckBoxObj::XclImpCheckBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnState( EXC_OBJ_CHECKBOX_UNCHECKED ),
+ mnCheckBoxFlags( 0 )
+{
+}
+
+void XclImpCheckBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 20 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnState = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+}
+
+void XclImpCheckBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJCBLS:
+ // do not read EXC_ID_OBJCBLSDATA, not written by OOo Excel export
+ mnState = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_OBJCBLSFMLA:
+ ReadCellLinkFormula( rStrm, false );
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpCheckBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ // state
+ bool bSupportsTristate = GetObjType() == EXC_OBJTYPE_CHECKBOX;
+ sal_Int16 nApiState = 0;
+ switch( mnState )
+ {
+ case EXC_OBJ_CHECKBOX_UNCHECKED: nApiState = 0; break;
+ case EXC_OBJ_CHECKBOX_CHECKED: nApiState = 1; break;
+ case EXC_OBJ_CHECKBOX_TRISTATE: nApiState = bSupportsTristate ? 2 : 1; break;
+ }
+ if( bSupportsTristate )
+ rPropSet.SetBoolProperty( "TriState", nApiState == 2 );
+ rPropSet.SetProperty( "DefaultState", nApiState );
+
+ // box style
+ namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect;
+ sal_Int16 nEffect = ::get_flagvalue( mnCheckBoxFlags, EXC_OBJ_CHECKBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
+ rPropSet.SetProperty( "VisualEffect", nEffect );
+
+ // do not wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", false );
+
+ // #i40279# always centered vertically
+ namespace csss = ::com::sun::star::style;
+ rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_MIDDLE );
+
+ // background color
+ if( maFillData.IsFilled() )
+ {
+ sal_Int32 nColor = static_cast< sal_Int32 >( GetSolidFillColor( maFillData ) );
+ rPropSet.SetProperty( "BackgroundColor", nColor );
+ }
+}
+
+OUString XclImpCheckBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.CheckBox";
+}
+
+XclTbxEventType XclImpCheckBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpOptionButtonObj::XclImpOptionButtonObj( const XclImpRoot& rRoot ) :
+ XclImpCheckBoxObj( rRoot ),
+ mnNextInGroup( 0 ),
+ mnFirstInGroup( 1 )
+{
+}
+
+void XclImpOptionButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 32 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnState = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+ mnNextInGroup = rStrm.ReaduInt16();
+ mnFirstInGroup = rStrm.ReaduInt16();
+}
+
+void XclImpOptionButtonObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJRBODATA:
+ mnNextInGroup = rStrm.ReaduInt16();
+ mnFirstInGroup = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpCheckBoxObj::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpOptionButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ XclImpCheckBoxObj::DoProcessControl( rPropSet );
+ // TODO: grouping
+ XclImpOptionButtonObj* pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( mnNextInGroup ).get() );
+ if ( pTbxObj && pTbxObj->mnFirstInGroup )
+ {
+ // Group has terminated
+ // traverse each RadioButton in group and
+ // a) apply the groupname
+ // b) propagate the linked cell from the lead radiobutton
+ // c) apply the correct Ref value
+ XclImpOptionButtonObj* pLeader = pTbxObj;
+
+ sal_Int32 nRefVal = 1;
+ do
+ {
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( pTbxObj->mxShape );
+ if ( xCtrlModel.is() )
+ {
+ ScfPropertySet aProps( xCtrlModel );
+ OUString sGroupName = OUString::number( pLeader->GetDffShapeId() );
+
+ aProps.SetStringProperty( "GroupName", sGroupName );
+ aProps.SetStringProperty( "RefValue", OUString::number( nRefVal++ ) );
+ if ( pLeader->HasCellLink() && !pTbxObj->HasCellLink() )
+ {
+ // propagate cell link info
+ pTbxObj->mxCellLink = std::make_shared<ScAddress>( *pLeader->mxCellLink );
+ pTbxObj->ApplySheetLinkProps();
+ }
+ pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( pTbxObj->mnNextInGroup ).get() );
+ }
+ else
+ pTbxObj = nullptr;
+ } while ( pTbxObj && ( pTbxObj->mnFirstInGroup != 1 ) );
+ }
+ else
+ {
+ // not the leader? try and find it
+ }
+}
+
+OUString XclImpOptionButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.RadioButton";
+}
+
+XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+bool XclImpOptionButtonObj::IsInGroup() const
+{
+ return mnNextInGroup;
+}
+
+XclImpLabelObj::XclImpLabelObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpLabelObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ // text alignment (always top/left aligned)
+ rPropSet.SetProperty( "Align", sal_Int16( 0 ) );
+ namespace csss = ::com::sun::star::style;
+ rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_TOP );
+
+ // always wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", true );
+}
+
+OUString XclImpLabelObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.FixedText";
+}
+
+XclTbxEventType XclImpLabelObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpGroupBoxObj::XclImpGroupBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnGroupBoxFlags( 0 )
+{
+}
+
+void XclImpGroupBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 26 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16( );
+ mnGroupBoxFlags = rStrm.ReaduInt16();
+}
+
+void XclImpGroupBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJGBODATA:
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnGroupBoxFlags = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpGroupBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+}
+
+OUString XclImpGroupBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.GroupBox";
+}
+
+XclTbxEventType XclImpGroupBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpDialogObj::XclImpDialogObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpDialogObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+}
+
+OUString XclImpDialogObj::DoGetServiceName() const
+{
+ // dialog frame faked by a groupbox
+ return "com.sun.star.form.component.GroupBox";
+}
+
+XclTbxEventType XclImpDialogObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpEditObj::XclImpEditObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnContentType( EXC_OBJ_EDIT_TEXT ),
+ mnMultiLine( 0 ),
+ mnScrollBar( 0 ),
+ mnListBoxObjId( 0 )
+{
+}
+
+bool XclImpEditObj::IsNumeric() const
+{
+ return (mnContentType == EXC_OBJ_EDIT_INTEGER) || (mnContentType == EXC_OBJ_EDIT_DOUBLE);
+}
+
+void XclImpEditObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 14 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnContentType = rStrm.ReaduInt16();
+ mnMultiLine = rStrm.ReaduInt16();
+ mnScrollBar = rStrm.ReaduInt16();
+ mnListBoxObjId = rStrm.ReaduInt16();
+}
+
+void XclImpEditObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJEDODATA:
+ mnContentType = rStrm.ReaduInt16();
+ mnMultiLine = rStrm.ReaduInt16();
+ mnScrollBar = rStrm.ReaduInt16();
+ mnListBoxObjId = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpEditObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ OUString aText = maTextData.mxString->GetText();
+ if( IsNumeric() )
+ {
+ // TODO: OUString::toDouble() does not handle local decimal separator
+ rPropSet.SetProperty( "DefaultValue", aText.toDouble() );
+ rPropSet.SetBoolProperty( "Spin", mnScrollBar != 0 );
+ }
+ else
+ {
+ rPropSet.SetProperty( "DefaultText", aText );
+ rPropSet.SetBoolProperty( "MultiLine", mnMultiLine != 0 );
+ rPropSet.SetBoolProperty( "VScroll", mnScrollBar != 0 );
+ }
+ }
+ ConvertFont( rPropSet );
+}
+
+OUString XclImpEditObj::DoGetServiceName() const
+{
+ return IsNumeric() ?
+ OUString( "com.sun.star.form.component.NumericField" ) :
+ OUString( "com.sun.star.form.component.TextField" );
+}
+
+XclTbxEventType XclImpEditObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_TEXT;
+}
+
+XclImpTbxObjScrollableBase::XclImpTbxObjScrollableBase( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnValue( 0 ),
+ mnMin( 0 ),
+ mnMax( 100 ),
+ mnStep( 1 ),
+ mnPageStep( 10 ),
+ mnOrient( 0 ),
+ mnThumbWidth( 1 ),
+ mnScrollFlags( 0 )
+{
+}
+
+void XclImpTbxObjScrollableBase::ReadSbs( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 4 );
+ mnValue = rStrm.ReaduInt16();
+ mnMin = rStrm.ReaduInt16();
+ mnMax = rStrm.ReaduInt16();
+ mnStep = rStrm.ReaduInt16();
+ mnPageStep = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ mnThumbWidth = rStrm.ReaduInt16();
+ mnScrollFlags = rStrm.ReaduInt16();
+}
+
+void XclImpTbxObjScrollableBase::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJSBS:
+ ReadSbs( rStrm );
+ break;
+ case EXC_ID_OBJSBSFMLA:
+ ReadCellLinkFormula( rStrm, false );
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+XclImpSpinButtonObj::XclImpSpinButtonObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot )
+{
+}
+
+void XclImpSpinButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+}
+
+void XclImpSpinButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#)
+ rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE );
+ rPropSet.SetProperty< sal_Int32 >( "DefaultSpinValue", mnValue );
+ rPropSet.SetProperty< sal_Int32 >( "SpinValueMin", mnMin );
+ rPropSet.SetProperty< sal_Int32 >( "SpinValueMax", mnMax );
+ rPropSet.SetProperty< sal_Int32 >( "SpinIncrement", mnStep );
+
+ // Excel spin buttons always vertical
+ rPropSet.SetProperty( "Orientation", css::awt::ScrollBarOrientation::VERTICAL );
+}
+
+OUString XclImpSpinButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.SpinButton";
+}
+
+XclTbxEventType XclImpSpinButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_VALUE;
+}
+
+XclImpScrollBarObj::XclImpScrollBarObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot )
+{
+}
+
+void XclImpScrollBarObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+}
+
+void XclImpScrollBarObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#)
+ rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE );
+ rPropSet.SetProperty< sal_Int32 >( "DefaultScrollValue", mnValue );
+ rPropSet.SetProperty< sal_Int32 >( "ScrollValueMin", mnMin );
+ rPropSet.SetProperty< sal_Int32 >( "ScrollValueMax", mnMax );
+ rPropSet.SetProperty< sal_Int32 >( "LineIncrement", mnStep );
+ rPropSet.SetProperty< sal_Int32 >( "BlockIncrement", mnPageStep );
+ rPropSet.SetProperty( "VisibleSize", ::std::min< sal_Int32 >( mnPageStep, 1 ) );
+
+ namespace AwtScrollOrient = ::com::sun::star::awt::ScrollBarOrientation;
+ sal_Int32 nApiOrient = ::get_flagvalue( mnOrient, EXC_OBJ_SCROLLBAR_HOR, AwtScrollOrient::HORIZONTAL, AwtScrollOrient::VERTICAL );
+ rPropSet.SetProperty( "Orientation", nApiOrient );
+}
+
+OUString XclImpScrollBarObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.ScrollBar";
+}
+
+XclTbxEventType XclImpScrollBarObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_VALUE;
+}
+
+XclImpTbxObjListBase::XclImpTbxObjListBase( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot ),
+ mnEntryCount( 0 ),
+ mnSelEntry( 0 ),
+ mnListFlags( 0 ),
+ mnEditObjId( 0 ),
+ mbHasDefFontIdx( false )
+{
+}
+
+void XclImpTbxObjListBase::ReadLbsData( XclImpStream& rStrm )
+{
+ ReadSourceRangeFormula( rStrm, true );
+ mnEntryCount = rStrm.ReaduInt16();
+ mnSelEntry = rStrm.ReaduInt16();
+ mnListFlags = rStrm.ReaduInt16();
+ mnEditObjId = rStrm.ReaduInt16();
+}
+
+void XclImpTbxObjListBase::SetBoxFormatting( ScfPropertySet& rPropSet ) const
+{
+ // border style
+ namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect;
+ sal_Int16 nApiBorder = ::get_flagvalue( mnListFlags, EXC_OBJ_LISTBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
+ rPropSet.SetProperty( "Border", nApiBorder );
+
+ // font formatting
+ if( mbHasDefFontIdx )
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, maTextData.maData.mnDefFontIdx );
+ else
+ GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet );
+}
+
+XclImpListBoxObj::XclImpListBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjListBase( rRoot )
+{
+}
+
+void XclImpListBoxObj::ReadFullLbsData( XclImpStream& rStrm, std::size_t nRecLeft )
+{
+ std::size_t nRecEnd = rStrm.GetRecPos() + nRecLeft;
+ ReadLbsData( rStrm );
+ OSL_ENSURE( (rStrm.GetRecPos() == nRecEnd) || (rStrm.GetRecPos() + mnEntryCount == nRecEnd),
+ "XclImpListBoxObj::ReadFullLbsData - invalid size of OBJLBSDATA record" );
+ while (rStrm.IsValid())
+ {
+ if (rStrm.GetRecPos() >= nRecEnd)
+ break;
+ maSelection.push_back( rStrm.ReaduInt8() );
+ }
+}
+
+void XclImpListBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ rStrm.Ignore( 18 );
+ maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ ReadFullLbsData( rStrm, rStrm.GetRecLeft() );
+ mbHasDefFontIdx = true;
+}
+
+void XclImpListBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJLBSDATA:
+ ReadFullLbsData( rStrm, nSubRecSize );
+ break;
+ default:
+ XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpListBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // listbox formatting
+ SetBoxFormatting( rPropSet );
+
+ // selection type
+ sal_uInt8 nSelType = ::extract_value< sal_uInt8 >( mnListFlags, 4, 2 );
+ bool bMultiSel = nSelType != EXC_OBJ_LISTBOX_SINGLE;
+ rPropSet.SetBoolProperty( "MultiSelection", bMultiSel );
+
+ // selection (do not set, if listbox is linked to a cell)
+ if( HasCellLink() )
+ return;
+
+ ScfInt16Vec aSelVec;
+
+ // multi selection: API expects sequence of list entry indexes
+ if( bMultiSel )
+ {
+ sal_Int16 nIndex = 0;
+ for( const auto& rItem : maSelection )
+ {
+ if( rItem != 0 )
+ aSelVec.push_back( nIndex );
+ ++nIndex;
+ }
+ }
+ // single selection: mnSelEntry is one-based, API expects zero-based
+ else if( mnSelEntry > 0 )
+ aSelVec.push_back( static_cast< sal_Int16 >( mnSelEntry - 1 ) );
+
+ if( !aSelVec.empty() )
+ {
+ Sequence<sal_Int16> aSelSeq(aSelVec.data(), static_cast<sal_Int32>(aSelVec.size()));
+ rPropSet.SetProperty( "DefaultSelection", aSelSeq );
+ }
+}
+
+OUString XclImpListBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.ListBox";
+}
+
+XclTbxEventType XclImpListBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_CHANGE;
+}
+
+XclImpDropDownObj::XclImpDropDownObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjListBase( rRoot ),
+ mnLeft( 0 ),
+ mnTop( 0 ),
+ mnRight( 0 ),
+ mnBottom( 0 ),
+ mnDropDownFlags( 0 ),
+ mnLineCount( 0 ),
+ mnMinWidth( 0 )
+{
+}
+
+sal_uInt16 XclImpDropDownObj::GetDropDownType() const
+{
+ return ::extract_value< sal_uInt8 >( mnDropDownFlags, 0, 2 );
+}
+
+void XclImpDropDownObj::ReadFullLbsData( XclImpStream& rStrm )
+{
+ ReadLbsData( rStrm );
+ mnDropDownFlags = rStrm.ReaduInt16();
+ mnLineCount = rStrm.ReaduInt16();
+ mnMinWidth = rStrm.ReaduInt16();
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ // dropdowns of auto-filters have 'simple' style, they don't have a text area
+ if( GetDropDownType() == EXC_OBJ_DROPDOWN_SIMPLE )
+ SetProcessSdrObj( false );
+}
+
+void XclImpDropDownObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ rStrm.Ignore( 18 );
+ maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 14 );
+ mnLeft = rStrm.ReaduInt16();
+ mnTop = rStrm.ReaduInt16();
+ mnRight = rStrm.ReaduInt16();
+ mnBottom = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ ReadFullLbsData( rStrm );
+ mbHasDefFontIdx = true;
+}
+
+void XclImpDropDownObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJLBSDATA:
+ ReadFullLbsData( rStrm );
+ break;
+ default:
+ XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpDropDownObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // dropdown listbox formatting
+ SetBoxFormatting( rPropSet );
+ // enable dropdown button
+ rPropSet.SetBoolProperty( "Dropdown", true );
+ // dropdown line count
+ rPropSet.SetProperty( "LineCount", mnLineCount );
+
+ if( GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX )
+ {
+ // text of editable combobox
+ if( maTextData.mxString )
+ rPropSet.SetStringProperty( "DefaultText", maTextData.mxString->GetText() );
+ }
+ else
+ {
+ // selection (do not set, if dropdown is linked to a cell)
+ if( !HasCellLink() && (mnSelEntry > 0) )
+ {
+ Sequence< sal_Int16 > aSelSeq{ o3tl::narrowing<sal_Int16>(mnSelEntry - 1) };
+ rPropSet.SetProperty( "DefaultSelection", aSelSeq );
+ }
+ }
+}
+
+OUString XclImpDropDownObj::DoGetServiceName() const
+{
+ return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ?
+ OUString( "com.sun.star.form.component.ComboBox" ) :
+ OUString( "com.sun.star.form.component.ListBox" );
+}
+
+XclTbxEventType XclImpDropDownObj::DoGetEventType() const
+{
+ return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? EXC_TBX_EVENT_TEXT : EXC_TBX_EVENT_CHANGE;
+}
+
+XclImpPictureObj::XclImpPictureObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot ),
+ XclImpControlHelper( rRoot, EXC_CTRL_BINDCONTENT ),
+ mnStorageId( 0 ),
+ mnCtlsStrmPos( 0 ),
+ mnCtlsStrmSize( 0 ),
+ mbEmbedded( false ),
+ mbLinked( false ),
+ mbSymbol( false ),
+ mbControl( false ),
+ mbUseCtlsStrm( false )
+{
+ SetAreaObj( true );
+ SetSimpleMacro( true );
+ SetCustomDffObj( true );
+}
+
+OUString XclImpPictureObj::GetOleStorageName() const
+{
+ OUStringBuffer aStrgName;
+ if( (mbEmbedded || mbLinked) && !mbControl && (mnStorageId > 0) )
+ {
+ aStrgName = mbEmbedded ? std::u16string_view(u"" EXC_STORAGE_OLE_EMBEDDED) : std::u16string_view(u"" EXC_STORAGE_OLE_LINKED);
+ static const char spcHexChars[] = "0123456789ABCDEF";
+ for( sal_uInt8 nIndex = 32; nIndex > 0; nIndex -= 4 )
+ aStrgName.append(OUStringChar( spcHexChars[ ::extract_value< sal_uInt8 >( mnStorageId, nIndex - 4, 4 ) ] ));
+ }
+ return aStrgName.makeStringAndClear();
+}
+
+void XclImpPictureObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+}
+
+void XclImpPictureObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+}
+
+void XclImpPictureObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ {
+ // page background is stored as hidden picture with name "__BkgndObj"
+ if ( IsHidden() && (GetObjName() == "__BkgndObj") )
+ GetPageSettings().ReadImgData( rStrm );
+ else
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+ }
+}
+
+void XclImpPictureObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJFLAGS:
+ ReadFlags8( rStrm );
+ break;
+ case EXC_ID_OBJPICTFMLA:
+ ReadPictFmla( rStrm, rStrm.ReaduInt16() );
+ break;
+ default:
+ XclImpDrawObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+rtl::Reference<SdrObject> XclImpPictureObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ // try to create an OLE object or form control
+ rtl::Reference<SdrObject> xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) );
+
+ // insert a graphic replacement for unsupported ole object ( if none already
+ // exists ) Hmm ok, it's possibly that there has been some imported
+ // graphic at a base level but unlikely, normally controls have a valid
+ // preview in the IMGDATA record ( see below )
+ // It might be possible to push such an imported graphic up to this
+ // XclImpPictureObj instance but there are so many layers of indirection I
+ // don't see an easy way. This way at least ensures that we can
+ // avoid a 'blank' shape that can result from a failed control import
+ if ( !xSdrObj && IsOcxControl() && maGraphic.GetType() == GraphicType::NONE )
+ {
+ const_cast< XclImpPictureObj* >( this )->maGraphic =
+ SdrOle2Obj::GetEmptyOLEReplacementGraphic();
+ }
+ // no OLE - create a plain picture from IMGDATA record data
+ if( !xSdrObj && (maGraphic.GetType() != GraphicType::NONE) )
+ {
+ xSdrObj =
+ new SdrGrafObj(
+ *GetDoc().GetDrawLayer(),
+ maGraphic,
+ rAnchorRect);
+ ConvertRectStyle( *xSdrObj );
+ }
+
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+OUString XclImpPictureObj::GetObjName() const
+{
+ if( IsOcxControl() )
+ {
+ OUString sName( GetObjectManager().GetOleNameOverride( GetTab(), GetObjId() ) );
+ if (!sName.isEmpty())
+ return sName;
+ }
+ return XclImpDrawObjBase::GetObjName();
+}
+
+void XclImpPictureObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ if( IsOcxControl() )
+ {
+ // do not call XclImpRectObj::DoPreProcessSdrObj(), it would trace missing "printable" feature
+ ProcessControl( *this );
+ }
+ else if( mbEmbedded || mbLinked )
+ {
+ // trace missing "printable" feature
+ XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+
+ SfxObjectShell* pDocShell = GetDocShell();
+ SdrOle2Obj* pOleSdrObj = dynamic_cast< SdrOle2Obj* >( &rSdrObj );
+ if( pOleSdrObj && pDocShell )
+ {
+ comphelper::EmbeddedObjectContainer& rEmbObjCont = pDocShell->GetEmbeddedObjectContainer();
+ Reference< XEmbeddedObject > xEmbObj = pOleSdrObj->GetObjRef();
+ OUString aOldName( pOleSdrObj->GetPersistName() );
+
+ /* The object persistence should be already in the storage, but
+ the object still might not be inserted into the container. */
+ if( rEmbObjCont.HasEmbeddedObject( aOldName ) )
+ {
+ if( !rEmbObjCont.HasEmbeddedObject( xEmbObj ) )
+ // filter code is allowed to call the following method
+ rEmbObjCont.AddEmbeddedObject( xEmbObj, aOldName );
+ }
+ else
+ {
+ /* If the object is still not in container it must be inserted
+ there, the name must be generated in this case. */
+ OUString aNewName;
+ rEmbObjCont.InsertEmbeddedObject( xEmbObj, aNewName );
+ if( aOldName != aNewName )
+ // SetPersistName, not SetName
+ pOleSdrObj->SetPersistName( aNewName );
+ }
+ }
+ }
+}
+
+void XclImpPictureObj::ReadFlags3( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL );
+}
+
+void XclImpPictureObj::ReadFlags8( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL );
+ mbControl = ::get_flag( nFlags, EXC_OBJ_PIC_CONTROL );
+ mbUseCtlsStrm = ::get_flag( nFlags, EXC_OBJ_PIC_CTLSSTREAM );
+ OSL_ENSURE( mbControl || !mbUseCtlsStrm, "XclImpPictureObj::ReadFlags8 - CTLS stream for controls only" );
+ SetProcessSdrObj( mbControl || !mbUseCtlsStrm );
+}
+
+void XclImpPictureObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize )
+{
+ std::size_t nLinkEnd = rStrm.GetRecPos() + nLinkSize;
+ if( nLinkSize >= 6 )
+ {
+ sal_uInt16 nFmlaSize;
+ nFmlaSize = rStrm.ReaduInt16();
+ OSL_ENSURE( nFmlaSize > 0, "XclImpPictureObj::ReadPictFmla - missing link formula" );
+ // BIFF3/BIFF4 do not support storages, nothing to do here
+ if( (nFmlaSize > 0) && (GetBiff() >= EXC_BIFF5) )
+ {
+ rStrm.Ignore( 4 );
+ sal_uInt8 nToken;
+ nToken = rStrm.ReaduInt8();
+
+ // different processing for linked vs. embedded OLE objects
+ if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) )
+ {
+ mbLinked = true;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ sal_Int16 nRefIdx;
+ sal_uInt16 nNameIdx;
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nNameIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 12 );
+ const ExtName* pExtName = GetOldRoot().pExtNameBuff->GetNameByIndex( nRefIdx, nNameIdx );
+ if( pExtName && pExtName->IsOLE() )
+ mnStorageId = pExtName->nStorageId;
+ }
+ break;
+ case EXC_BIFF8:
+ {
+ sal_uInt16 nXti, nExtName;
+ nXti = rStrm.ReaduInt16();
+ nExtName = rStrm.ReaduInt16();
+ const XclImpExtName* pExtName = GetLinkManager().GetExternName( nXti, nExtName );
+ if( pExtName && (pExtName->GetType() == xlExtOLE) )
+ mnStorageId = pExtName->GetStorageId();
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+ else if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_TBL, EXC_TOKCLASS_NONE ) )
+ {
+ mbEmbedded = true;
+ OSL_ENSURE( nFmlaSize == 5, "XclImpPictureObj::ReadPictFmla - unexpected formula size" );
+ rStrm.Ignore( nFmlaSize - 1 ); // token ID already read
+ if( nFmlaSize & 1 )
+ rStrm.Ignore( 1 ); // padding byte
+
+ // a class name may follow inside the picture link
+ if( rStrm.GetRecPos() + 2 <= nLinkEnd )
+ {
+ sal_uInt16 nLen = rStrm.ReaduInt16();
+ if( nLen > 0 )
+ maClassName = (GetBiff() == EXC_BIFF8) ? rStrm.ReadUniString( nLen ) : rStrm.ReadRawByteString( nLen );
+ }
+ }
+ // else: ignore other formulas, e.g. pictures linked to cell ranges
+ }
+ }
+
+ // seek behind picture link data
+ rStrm.Seek( nLinkEnd );
+
+ // read additional data for embedded OLE objects following the picture link
+ if( IsOcxControl() )
+ {
+ // #i26521# form controls to be ignored
+ if( maClassName == "Forms.HTML:Hidden.1" )
+ {
+ SetProcessSdrObj( false );
+ return;
+ }
+
+ if( rStrm.GetRecLeft() <= 8 ) return;
+
+ // position and size of control data in 'Ctls' stream
+ mnCtlsStrmPos = static_cast< std::size_t >( rStrm.ReaduInt32() );
+ mnCtlsStrmSize = static_cast< std::size_t >( rStrm.ReaduInt32() );
+
+ if( rStrm.GetRecLeft() <= 8 ) return;
+
+ // additional string (16-bit characters), e.g. for progress bar control
+ sal_uInt32 nAddStrSize;
+ nAddStrSize = rStrm.ReaduInt32();
+ OSL_ENSURE( rStrm.GetRecLeft() >= nAddStrSize + 4, "XclImpPictureObj::ReadPictFmla - missing data" );
+ if( rStrm.GetRecLeft() >= nAddStrSize + 4 )
+ {
+ rStrm.Ignore( nAddStrSize );
+ // cell link and source range
+ ReadCellLinkFormula( rStrm, true );
+ ReadSourceRangeFormula( rStrm, true );
+ }
+ }
+ else if( mbEmbedded && (rStrm.GetRecLeft() >= 4) )
+ {
+ mnStorageId = rStrm.ReaduInt32();
+ }
+}
+
+// DFF stream conversion ======================================================
+
+void XclImpSolverContainer::InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, ShapeFlag nDffFlags )
+{
+ if( nDffShapeId > 0 )
+ {
+ maSdrInfoMap[ nDffShapeId ].Set( &rSdrObj, nDffFlags );
+ maSdrObjMap[ &rSdrObj ] = nDffShapeId;
+ }
+}
+
+void XclImpSolverContainer::RemoveSdrObjectInfo( SdrObject& rSdrObj )
+{
+ // remove info of passed object from the maps
+ XclImpSdrObjMap::iterator aIt = maSdrObjMap.find( &rSdrObj );
+ if( aIt != maSdrObjMap.end() )
+ {
+ maSdrInfoMap.erase( aIt->second );
+ maSdrObjMap.erase( aIt );
+ }
+
+ // remove info of all child objects of a group object
+ if( SdrObjGroup* pGroupObj = dynamic_cast< SdrObjGroup* >( &rSdrObj ) )
+ {
+ if( SdrObjList* pSubList = pGroupObj->GetSubList() )
+ {
+ // iterate flat over the list because this function already works recursively
+ SdrObjListIter aObjIt( pSubList, SdrIterMode::Flat );
+ for( SdrObject* pChildObj = aObjIt.Next(); pChildObj; pChildObj = aObjIt.Next() )
+ RemoveSdrObjectInfo( *pChildObj );
+ }
+ }
+}
+
+void XclImpSolverContainer::UpdateConnectorRules()
+{
+ for (auto const & pRule : aCList)
+ {
+ UpdateConnection( pRule->nShapeA, pRule->pAObj, &pRule->nSpFlagsA );
+ UpdateConnection( pRule->nShapeB, pRule->pBObj, &pRule->nSpFlagsB );
+ UpdateConnection( pRule->nShapeC, pRule->pCObj );
+ }
+}
+
+void XclImpSolverContainer::RemoveConnectorRules()
+{
+ aCList.clear();
+ maSdrInfoMap.clear();
+ maSdrObjMap.clear();
+}
+
+void XclImpSolverContainer::UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, ShapeFlag* pnDffFlags )
+{
+ XclImpSdrInfoMap::const_iterator aIt = maSdrInfoMap.find( nDffShapeId );
+ if( aIt != maSdrInfoMap.end() )
+ {
+ rpSdrObj = aIt->second.mpSdrObj;
+ if( pnDffFlags )
+ *pnDffFlags = aIt->second.mnDffFlags;
+ }
+}
+
+XclImpSimpleDffConverter::XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) :
+ SvxMSDffManager( rDffStrm, rRoot.GetBasePath(), 0, nullptr, rRoot.GetDoc().GetDrawLayer(), 1440, COL_DEFAULT, nullptr ),
+ XclImpRoot( rRoot )
+{
+ SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS | SVXMSDFF_SETTINGS_IMPORT_EXCEL );
+}
+
+XclImpSimpleDffConverter::~XclImpSimpleDffConverter()
+{
+}
+
+bool XclImpSimpleDffConverter::GetColorFromPalette( sal_uInt16 nIndex, Color& rColor ) const
+{
+ Color nColor = GetPalette().GetColor( nIndex );
+
+ if( nColor == COL_AUTO )
+ return false;
+
+ rColor = nColor;
+ return true;
+}
+
+XclImpDffConverter::XclImpDffConvData::XclImpDffConvData(
+ XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) :
+ mrDrawing( rDrawing ),
+ mrSdrModel( rSdrModel ),
+ mrSdrPage( rSdrPage ),
+ mnLastCtrlIndex( -1 ),
+ mbHasCtrlForm( false )
+{
+}
+
+constexpr OUString gaStdFormName( u"Standard"_ustr ); /// Standard name of control forms.
+
+XclImpDffConverter::XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) :
+ XclImpSimpleDffConverter( rRoot, rDffStrm ),
+ oox::ole::MSConvertOCXControls( rRoot.GetDocShell()->GetModel() ),
+ mnOleImpFlags( 0 ),
+ mbNotifyMacroEventRead(false)
+{
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if( rFilterOpt.IsMathType2Math() )
+ mnOleImpFlags |= OLE_MATHTYPE_2_STARMATH;
+ if( rFilterOpt.IsWinWord2Writer() )
+ mnOleImpFlags |= OLE_WINWORD_2_STARWRITER;
+ if( rFilterOpt.IsPowerPoint2Impress() )
+ mnOleImpFlags |= OLE_POWERPOINT_2_STARIMPRESS;
+
+ // try to open the 'Ctls' storage stream containing OCX control properties
+ mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
+
+ // default text margin (convert EMU to drawing layer units)
+ mnDefTextMargin = EXC_OBJ_TEXT_MARGIN;
+ ScaleEmu( mnDefTextMargin );
+}
+
+XclImpDffConverter::~XclImpDffConverter()
+{
+}
+
+OUString XclImpObjectManager::GetOleNameOverride( SCTAB nTab, sal_uInt16 nObjId )
+{
+ OUString sOleName;
+ OUString sCodeName = GetExtDocOptions().GetCodeName( nTab );
+
+ if (mxOleCtrlNameOverride.is() && mxOleCtrlNameOverride->hasByName(sCodeName))
+ {
+ Reference< XIndexContainer > xIdToOleName;
+ mxOleCtrlNameOverride->getByName( sCodeName ) >>= xIdToOleName;
+ xIdToOleName->getByIndex( nObjId ) >>= sOleName;
+ }
+
+ return sOleName;
+}
+
+void XclImpDffConverter::StartProgressBar( std::size_t nProgressSize )
+{
+ mxProgress = std::make_shared<ScfProgressBar>( GetDocShell(), STR_PROGRESS_CALCULATING );
+ mxProgress->AddSegment( nProgressSize );
+ mxProgress->Activate();
+}
+
+void XclImpDffConverter::Progress( std::size_t nDelta )
+{
+ OSL_ENSURE( mxProgress, "XclImpDffConverter::Progress - invalid call, no progress bar" );
+ mxProgress->Progress( nDelta );
+}
+
+void XclImpDffConverter::InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage )
+{
+ XclImpDffConvDataRef xConvData = std::make_shared<XclImpDffConvData>( rDrawing, rSdrModel, rSdrPage );
+ maDataStack.push_back( xConvData );
+ SetModel( &xConvData->mrSdrModel, 1440 );
+}
+
+void XclImpDffConverter::ProcessObject( SdrObjList& rObjList, XclImpDrawObjBase& rDrawObj )
+{
+ if( !rDrawObj.IsProcessSdrObj() )
+ return;
+
+ const XclObjAnchor* pAnchor = rDrawObj.GetAnchor();
+ if(!pAnchor)
+ return;
+
+ tools::Rectangle aAnchorRect = GetConvData().mrDrawing.CalcAnchorRect( *pAnchor, false );
+ if( rDrawObj.IsValidSize( aAnchorRect ) )
+ {
+ // CreateSdrObject() recursively creates embedded child objects
+ rtl::Reference<SdrObject> xSdrObj( rDrawObj.CreateSdrObject( *this, aAnchorRect, false ) );
+ if( xSdrObj )
+ rDrawObj.PreProcessSdrObject( *this, *xSdrObj );
+ // call InsertSdrObject() also, if SdrObject is missing
+ InsertSdrObject( rObjList, rDrawObj, xSdrObj.get() );
+ }
+}
+
+void XclImpDffConverter::ProcessDrawing( const XclImpDrawObjVector& rDrawObjs )
+{
+ SdrPage& rSdrPage = GetConvData().mrSdrPage;
+ for( const auto& rxDrawObj : rDrawObjs )
+ ProcessObject( rSdrPage, *rxDrawObj );
+}
+
+void XclImpDffConverter::ProcessDrawing( SvStream& rDffStrm )
+{
+ if( rDffStrm.TellEnd() > 0 )
+ {
+ rDffStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ OSL_ENSURE( aHeader.nRecType == DFF_msofbtDgContainer, "XclImpDffConverter::ProcessDrawing - unexpected record" );
+ if( aHeader.nRecType == DFF_msofbtDgContainer )
+ ProcessDgContainer( rDffStrm, aHeader );
+ }
+}
+
+void XclImpDffConverter::FinalizeDrawing()
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::FinalizeDrawing - no drawing manager on stack" );
+ maDataStack.pop_back();
+ // restore previous model at core DFF converter
+ if( !maDataStack.empty() )
+ SetModel( &maDataStack.back()->mrSdrModel, 1440 );
+}
+
+void XclImpDffConverter::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(mxModel);
+ mbNotifyMacroEventRead = true;
+}
+
+rtl::Reference<SdrObject> XclImpDffConverter::CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect )
+{
+ rtl::Reference<SdrObject> xSdrObj;
+
+ OUString aServiceName = rTbxObj.GetServiceName();
+ if( SupportsOleObjects() && !aServiceName.isEmpty() ) try
+ {
+ // create the form control from scratch
+ Reference< XFormComponent > xFormComp( ScfApiHelper::CreateInstance( GetDocShell(), aServiceName ), UNO_QUERY_THROW );
+ // set controls form, needed in virtual function InsertControl()
+ InitControlForm();
+ // try to insert the control into the form
+ css::awt::Size aDummySize;
+ Reference< XShape > xShape;
+ XclImpDffConvData& rConvData = GetConvData();
+ if( rConvData.mxCtrlForm.is() && InsertControl( xFormComp, aDummySize, &xShape, true ) )
+ {
+ xSdrObj = rTbxObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
+ // try to attach a macro to the control
+ ScriptEventDescriptor aDescriptor;
+ if( (rConvData.mnLastCtrlIndex >= 0) && rTbxObj.FillMacroDescriptor( aDescriptor ) )
+ {
+ NotifyMacroEventRead();
+ Reference< XEventAttacherManager > xEventMgr( rConvData.mxCtrlForm, UNO_QUERY_THROW );
+ xEventMgr->registerScriptEvent( rConvData.mnLastCtrlIndex, aDescriptor );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+
+ return xSdrObj;
+}
+
+rtl::Reference<SdrObject> XclImpDffConverter::CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect )
+{
+ rtl::Reference<SdrObject> xSdrObj;
+
+ if( SupportsOleObjects() )
+ {
+ if( rPicObj.IsOcxControl() )
+ {
+ if( mxCtlsStrm.is() ) try
+ {
+ /* set controls form, needed in virtual function InsertControl()
+ called from ReadOCXExcelKludgeStream() */
+ InitControlForm();
+
+ // read from mxCtlsStrm into xShape, insert the control model into the form
+ Reference< XShape > xShape;
+ if( GetConvData().mxCtrlForm.is() )
+ {
+ Reference< XFormComponent > xFComp;
+ ReadOCXCtlsStream( mxCtlsStrm, xFComp, rPicObj.GetCtlsStreamPos(), rPicObj.GetCtlsStreamSize() );
+ // recreate the method formerly known as ReadOCXExcelKludgeStream()
+ if ( xFComp.is() )
+ {
+ css::awt::Size aSz; // not used in import
+ ScfPropertySet aPropSet( xFComp );
+ aPropSet.SetStringProperty( "Name", rPicObj.GetObjName() );
+ InsertControl( xFComp, aSz,&xShape,true);
+ xSdrObj = rPicObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ else
+ {
+ SfxObjectShell* pDocShell = GetDocShell();
+ tools::SvRef<SotStorage> xSrcStrg = GetRootStorage();
+ OUString aStrgName = rPicObj.GetOleStorageName();
+ if( pDocShell && xSrcStrg.is() && (!aStrgName.isEmpty()) )
+ {
+ // first try to resolve graphic from DFF storage
+ Graphic aGraphic;
+ tools::Rectangle aVisArea;
+ if( !GetBLIP( GetPropertyValue( DFF_Prop_pib, 0 ), aGraphic, &aVisArea ) )
+ {
+ // if not found, use graphic from object (imported from IMGDATA record)
+ aGraphic = rPicObj.GetGraphic();
+ }
+ if( aGraphic.GetType() != GraphicType::NONE )
+ {
+ ErrCode nError = ERRCODE_NONE;
+ namespace cssea = ::com::sun::star::embed::Aspects;
+ sal_Int64 nAspects = rPicObj.IsSymbol() ? cssea::MSOLE_ICON : cssea::MSOLE_CONTENT;
+ xSdrObj =
+ CreateSdrOLEFromStorage(
+ GetConvData().mrSdrModel,
+ aStrgName,
+ xSrcStrg,
+ pDocShell->GetStorage(),
+ aGraphic,
+ rAnchorRect,
+ aVisArea,
+ nullptr,
+ nError,
+ mnOleImpFlags,
+ nAspects,
+ GetRoot().GetMedium().GetBaseURL());
+ }
+ }
+ }
+ }
+
+ return xSdrObj;
+}
+
+bool XclImpDffConverter::SupportsOleObjects() const
+{
+ return GetConvData().mrDrawing.SupportsOleObjects();
+}
+
+// virtual functions ----------------------------------------------------------
+
+void XclImpDffConverter::ProcessClientAnchor2( SvStream& rDffStrm,
+ DffRecordHeader& rHeader, DffObjData& rObjData )
+{
+ // find the OBJ record data related to the processed shape
+ XclImpDffConvData& rConvData = GetConvData();
+ XclImpDrawObjBase* pDrawObj = rConvData.mrDrawing.FindDrawObj( rObjData.rSpHd ).get();
+ if(!pDrawObj)
+ return;
+
+ OSL_ENSURE( rHeader.nRecType == DFF_msofbtClientAnchor, "XclImpDffConverter::ProcessClientAnchor2 - no client anchor record" );
+ XclObjAnchor aAnchor;
+ rHeader.SeekToContent( rDffStrm );
+ sal_uInt8 nFlags(0);
+ rDffStrm.ReadUChar( nFlags );
+ rDffStrm.SeekRel( 1 ); // flags
+ rDffStrm >> aAnchor; // anchor format equal to BIFF5 OBJ records
+
+ if (!rDffStrm.good())
+ {
+ SAL_WARN("sc.filter", "ProcessClientAnchor2 short read");
+ return;
+ }
+
+ pDrawObj->SetAnchor( aAnchor );
+ rObjData.aChildAnchor = rConvData.mrDrawing.CalcAnchorRect( aAnchor, true );
+ rObjData.bChildAnchor = true;
+ // page anchoring is the best approximation we have if mbMove
+ // is set
+ rObjData.bPageAnchor = ( nFlags & 0x1 );
+}
+
+namespace {
+
+struct XclImpDrawObjClientData : public SvxMSDffClientData
+{
+ const XclImpDrawObjBase* m_pTopLevelObj;
+
+ XclImpDrawObjClientData()
+ : m_pTopLevelObj(nullptr)
+ {
+ }
+ virtual void NotifyFreeObj(SdrObject*) override {}
+};
+
+}
+
+rtl::Reference<SdrObject> XclImpDffConverter::ProcessObj( SvStream& rDffStrm, DffObjData& rDffObjData,
+ SvxMSDffClientData& rClientData, tools::Rectangle& /*rTextRect*/, SdrObject* pOldSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+
+ /* pOldSdrObj passes a generated SdrObject. This function owns this object
+ and can modify it. The function has either to return it back to caller
+ or to delete it by itself. */
+ rtl::Reference<SdrObject> xSdrObj( pOldSdrObj );
+
+ // find the OBJ record data related to the processed shape
+ XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd );
+ const tools::Rectangle& rAnchorRect = rDffObjData.aChildAnchor;
+
+ // Do not process the global page group shape
+ bool bGlobalPageGroup( rDffObjData.nSpFlags & ShapeFlag::Patriarch );
+ if( !xDrawObj || !xDrawObj->IsProcessSdrObj() || bGlobalPageGroup )
+ return nullptr; // simply return, xSdrObj will be destroyed
+
+ /* Pass pointer to top-level object back to caller. If the processed
+ object is embedded in a group, the pointer is already set to the
+ top-level parent object. */
+ XclImpDrawObjClientData& rDrawObjClientData = static_cast<XclImpDrawObjClientData&>(rClientData);
+ const bool bIsTopLevel = !rDrawObjClientData.m_pTopLevelObj;
+ if (bIsTopLevel )
+ rDrawObjClientData.m_pTopLevelObj = xDrawObj.get();
+
+ // connectors don't have to be area objects
+ if( dynamic_cast< SdrEdgeObj* >( xSdrObj.get() ) )
+ xDrawObj->SetAreaObj( false );
+
+ /* Check for valid size for all objects. Needed to ignore lots of invisible
+ phantom objects from deleted rows or columns (for performance reasons).
+ #i30816# Include objects embedded in groups.
+ #i58780# Ignore group shapes, size is not initialized. */
+ bool bEmbeddedGroup = !bIsTopLevel && dynamic_cast< SdrObjGroup* >( xSdrObj.get() );
+ if( !bEmbeddedGroup && !xDrawObj->IsValidSize( rAnchorRect ) )
+ return nullptr; // simply return, xSdrObj will be destroyed
+
+ // set shape information from DFF stream
+ OUString aObjName = GetPropertyString( DFF_Prop_wzName, rDffStrm );
+ OUString aHyperlink = ReadHlinkProperty( rDffStrm );
+ bool bVisible = !GetPropertyBool( DFF_Prop_fHidden );
+ bool bAutoMargin = GetPropertyBool( DFF_Prop_AutoTextMargin );
+ xDrawObj->SetDffData( rDffObjData, aObjName, aHyperlink, bVisible, bAutoMargin );
+
+ /* Connect textbox data (string, alignment, text orientation) to object.
+ don't ask for a text-ID, DFF export doesn't set one. */
+ if( XclImpTextObj* pTextObj = dynamic_cast< XclImpTextObj* >( xDrawObj.get() ) )
+ if( const XclImpObjTextData* pTextData = rConvData.mrDrawing.FindTextData( rDffObjData.rSpHd ) )
+ pTextObj->SetTextData( *pTextData );
+
+ // copy line and fill formatting of TBX form controls from DFF properties
+ if( XclImpTbxObjBase* pTbxObj = dynamic_cast< XclImpTbxObjBase* >( xDrawObj.get() ) )
+ pTbxObj->SetDffProperties( *this );
+
+ // try to create a custom SdrObject that overwrites the passed object
+ rtl::Reference<SdrObject> xNewSdrObj( xDrawObj->CreateSdrObject( *this, rAnchorRect, true ) );
+ if( xNewSdrObj )
+ xSdrObj = std::move( xNewSdrObj );
+
+ // process the SdrObject
+ if( xSdrObj )
+ {
+ // filled without color -> set system window color
+ if( GetPropertyBool( DFF_Prop_fFilled ) && !IsProperty( DFF_Prop_fillColor ) )
+ xSdrObj->SetMergedItem( XFillColorItem( OUString(), GetPalette().GetColor( EXC_COLOR_WINDOWBACK ) ) );
+
+ // additional processing on the SdrObject
+ xDrawObj->PreProcessSdrObject( *this, *xSdrObj );
+
+ /* If the SdrObject will not be inserted into the draw page, delete it
+ here. Happens e.g. for notes: The PreProcessSdrObject() call above
+ has inserted the note into the document, and the SdrObject is not
+ needed anymore. */
+ if( !xDrawObj->IsInsertSdrObj() )
+ xSdrObj.clear();
+ }
+
+ if( xSdrObj )
+ {
+ /* Store the relation between shape ID and SdrObject for connectors.
+ Must be done here (and not in InsertSdrObject() function),
+ otherwise all SdrObjects embedded in groups would be lost. */
+ rConvData.maSolverCont.InsertSdrObjectInfo( *xSdrObj, xDrawObj->GetDffShapeId(), xDrawObj->GetDffFlags() );
+
+ /* If the drawing object is embedded in a group object, call
+ PostProcessSdrObject() here. For top-level objects this will be
+ done automatically in InsertSdrObject() but grouped shapes are
+ inserted into their groups somewhere in the SvxMSDffManager base
+ class without chance of notification. Unfortunately, now this is
+ called before the object is really inserted into its group object,
+ but that should not have any effect for grouped objects. */
+ if( !bIsTopLevel )
+ xDrawObj->PostProcessSdrObject( *this, *xSdrObj );
+ }
+
+ return xSdrObj;
+}
+
+SdrObject* XclImpDffConverter::FinalizeObj(DffObjData& rDffObjData, SdrObject* pOldSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+
+ /* pOldSdrObj passes a generated SdrObject. This function owns this object
+ and can modify it. The function has either to return it back to caller
+ or to delete it by itself. */
+ rtl::Reference<SdrObject> xSdrObj( pOldSdrObj );
+
+ // find the OBJ record data related to the processed shape
+ XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd );
+
+ if( xSdrObj && xDrawObj )
+ {
+ // cell anchoring
+ if ( !rDffObjData.bPageAnchor )
+ ScDrawLayer::SetCellAnchoredFromPosition( *xSdrObj, GetDoc(), xDrawObj->GetTab(), false );
+ }
+
+ return xSdrObj.get();
+}
+
+bool XclImpDffConverter::InsertControl( const Reference< XFormComponent >& rxFormComp,
+ const css::awt::Size& /*rSize*/, Reference< XShape >* pxShape,
+ bool /*bFloatingCtrl*/ )
+{
+ if( GetDocShell() ) try
+ {
+ XclImpDffConvData& rConvData = GetConvData();
+ Reference< XIndexContainer > xFormIC( rConvData.mxCtrlForm, UNO_QUERY_THROW );
+ Reference< XControlModel > xCtrlModel( rxFormComp, UNO_QUERY_THROW );
+
+ // create the control shape
+ Reference< XShape > xShape( ScfApiHelper::CreateInstance( GetDocShell(), "com.sun.star.drawing.ControlShape" ), UNO_QUERY_THROW );
+ Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY_THROW );
+
+ // insert the new control into the form
+ sal_Int32 nNewIndex = xFormIC->getCount();
+ xFormIC->insertByIndex( nNewIndex, Any( rxFormComp ) );
+ // on success: store new index of the control for later use (macro events)
+ rConvData.mnLastCtrlIndex = nNewIndex;
+
+ // set control model at control shape and pass back shape to caller
+ xCtrlShape->setControl( xCtrlModel );
+ if( pxShape ) *pxShape = xShape;
+ return true;
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "XclImpDffConverter::InsertControl - cannot create form control" );
+ }
+
+ return false;
+}
+
+// private --------------------------------------------------------------------
+
+XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData()
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" );
+ return *maDataStack.back();
+}
+
+const XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() const
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" );
+ return *maDataStack.back();
+}
+
+OUString XclImpDffConverter::ReadHlinkProperty( SvStream& rDffStrm ) const
+{
+ /* Reads hyperlink data from a complex DFF property. Contents of this
+ property are equal to the HLINK record, import of this record is
+ implemented in class XclImpHyperlink. This function has to create an
+ instance of the XclImpStream class to be able to reuse the
+ functionality of XclImpHyperlink. */
+ OUString aString;
+ sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
+ if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rDffStrm ) )
+ {
+ // create a faked BIFF record that can be read by XclImpStream class
+ SvMemoryStream aMemStream;
+ aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
+
+ // copy from DFF stream to memory stream
+ ::std::vector< sal_uInt8 > aBuffer( nBufferSize );
+ sal_uInt8* pnData = aBuffer.data();
+ if (rDffStrm.ReadBytes(pnData, nBufferSize) == nBufferSize)
+ {
+ aMemStream.WriteBytes(pnData, nBufferSize);
+
+ // create BIFF import stream to be able to use XclImpHyperlink class
+ XclImpStream aXclStrm( aMemStream, GetRoot() );
+ if( aXclStrm.StartNextRecord() )
+ aString = XclImpHyperlink::ReadEmbeddedData( aXclStrm );
+ }
+ }
+ return aString;
+}
+
+bool XclImpDffConverter::ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader )
+{
+ std::size_t nEndPos = rDgHeader.GetRecEndFilePos();
+ bool isBreak(false);
+ while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos)
+ {
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ switch( aHeader.nRecType )
+ {
+ case DFF_msofbtSolverContainer:
+ isBreak = !ProcessSolverContainer( rDffStrm, aHeader );
+ break;
+ case DFF_msofbtSpgrContainer:
+ isBreak = !ProcessShGrContainer( rDffStrm, aHeader );
+ break;
+ default:
+ isBreak = !aHeader.SeekToEndOfRecord( rDffStrm );
+ }
+ }
+ // seek to end of drawing page container
+ isBreak = !rDgHeader.SeekToEndOfRecord( rDffStrm );
+
+ // #i12638# #i37900# connector rules
+ XclImpSolverContainer& rSolverCont = GetConvData().maSolverCont;
+ rSolverCont.UpdateConnectorRules();
+ SolveSolver( rSolverCont );
+ rSolverCont.RemoveConnectorRules();
+ return !isBreak;
+}
+
+bool XclImpDffConverter::ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader )
+{
+ std::size_t nEndPos = rShGrHeader.GetRecEndFilePos();
+ bool isBreak(false);
+ while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos)
+ {
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ switch( aHeader.nRecType )
+ {
+ case DFF_msofbtSpgrContainer:
+ case DFF_msofbtSpContainer:
+ isBreak = !ProcessShContainer( rDffStrm, aHeader );
+ break;
+ default:
+ isBreak = !aHeader.SeekToEndOfRecord( rDffStrm );
+ }
+ }
+ // seek to end of shape group container
+ return rShGrHeader.SeekToEndOfRecord( rDffStrm ) && !isBreak;
+}
+
+bool XclImpDffConverter::ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader )
+{
+ // solver container wants to read the solver container header again
+ rSolverHeader.SeekToBegOfRecord( rDffStrm );
+ // read the entire solver container
+ ReadSvxMSDffSolverContainer( rDffStrm, GetConvData().maSolverCont );
+ // seek to end of solver container
+ return rSolverHeader.SeekToEndOfRecord( rDffStrm );
+}
+
+bool XclImpDffConverter::ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader )
+{
+ rShHeader.SeekToBegOfRecord( rDffStrm );
+ tools::Rectangle aDummy;
+ XclImpDrawObjClientData aDrawObjClientData;
+ /* The call to ImportObj() creates and returns a new SdrObject for the
+ processed shape. We take ownership of the returned object here. If the
+ shape is a group object, all embedded objects are created recursively,
+ and the returned group object contains them all. ImportObj() calls the
+ virtual functions ProcessClientAnchor2() and ProcessObj() and writes
+ the pointer to the related draw object data (OBJ record) into aDrawObjClientData. */
+ rtl::Reference<SdrObject> xSdrObj( ImportObj( rDffStrm, aDrawObjClientData, aDummy, aDummy, /*nCalledByGroup*/0, /*pShapeId*/nullptr ) );
+ if (aDrawObjClientData.m_pTopLevelObj && xSdrObj )
+ InsertSdrObject( GetConvData().mrSdrPage, *aDrawObjClientData.m_pTopLevelObj, xSdrObj.get() );
+ return rShHeader.SeekToEndOfRecord( rDffStrm );
+}
+
+void XclImpDffConverter::InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+ /* Take ownership of the passed object. If insertion fails (e.g. rDrawObj
+ states to skip insertion), the object is automatically deleted. */
+ rtl::Reference<SdrObject> xSdrObj( pSdrObj );
+ if( xSdrObj && rDrawObj.IsInsertSdrObj() )
+ {
+ rObjList.NbcInsertObject( xSdrObj.get() );
+ // callback to drawing manager for e.g. tracking of used sheet area
+ rConvData.mrDrawing.OnObjectInserted( rDrawObj );
+ // callback to drawing object for post processing (use pSdrObj, xSdrObj already released)
+ rDrawObj.PostProcessSdrObject( *this, *pSdrObj );
+ }
+ /* SdrObject still here? Insertion failed, remove data from shape ID map.
+ The SdrObject will be destructed then. */
+ if( xSdrObj )
+ rConvData.maSolverCont.RemoveSdrObjectInfo( *xSdrObj );
+}
+
+void XclImpDffConverter::InitControlForm()
+{
+ XclImpDffConvData& rConvData = GetConvData();
+ if( rConvData.mbHasCtrlForm )
+ return;
+
+ rConvData.mbHasCtrlForm = true;
+ if( !SupportsOleObjects() )
+ return;
+
+ try
+ {
+ Reference< XFormsSupplier > xFormsSupplier( rConvData.mrSdrPage.getUnoPage(), UNO_QUERY_THROW );
+ Reference< XNameContainer > xFormsNC( xFormsSupplier->getForms(), UNO_SET_THROW );
+ // find or create the Standard form used to insert the imported controls
+ if( xFormsNC->hasByName( gaStdFormName ) )
+ {
+ xFormsNC->getByName( gaStdFormName ) >>= rConvData.mxCtrlForm;
+ }
+ else if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ rConvData.mxCtrlForm.set( ScfApiHelper::CreateInstance( pDocShell, "com.sun.star.form.component.Form" ), UNO_QUERY_THROW );
+ xFormsNC->insertByName( gaStdFormName, Any( rConvData.mxCtrlForm ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+// Drawing manager ============================================================
+
+XclImpDrawing::XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects ) :
+ XclImpRoot( rRoot ),
+ mbOleObjs( bOleObjects )
+{
+}
+
+XclImpDrawing::~XclImpDrawing()
+{
+}
+
+Graphic XclImpDrawing::ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ Graphic aGraphic;
+ sal_uInt16 nFormat = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );//nEnv
+ sal_uInt32 nDataSize = rStrm.ReaduInt32();
+ if( nDataSize <= rStrm.GetRecLeft() )
+ {
+ switch( nFormat )
+ {
+ case EXC_IMGDATA_WMF: ReadWmf( aGraphic, rStrm ); break;
+ case EXC_IMGDATA_BMP: ReadBmp( aGraphic, rRoot, rStrm ); break;
+ default: OSL_FAIL( "XclImpDrawing::ReadImgData - unknown image format" );
+ }
+ }
+ return aGraphic;
+}
+
+void XclImpDrawing::ReadObj( XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ /* #i61786# In BIFF8 streams, OBJ records may occur without MSODRAWING
+ records. In this case, the OBJ records are in BIFF5 format. Do a sanity
+ check here that there is no DFF data loaded before. */
+ OSL_ENSURE( maDffStrm.Tell() == 0, "XclImpDrawing::ReadObj - unexpected DFF stream data, OBJ will be ignored" );
+ if( maDffStrm.Tell() == 0 ) switch( GetBiff() )
+ {
+ case EXC_BIFF3:
+ xDrawObj = XclImpDrawObjBase::ReadObj3( GetRoot(), rStrm );
+ break;
+ case EXC_BIFF4:
+ xDrawObj = XclImpDrawObjBase::ReadObj4( GetRoot(), rStrm );
+ break;
+ case EXC_BIFF5:
+ case EXC_BIFF8:
+ xDrawObj = XclImpDrawObjBase::ReadObj5( GetRoot(), rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+
+ if( xDrawObj )
+ {
+ // insert into maRawObjs or into the last open group object
+ maRawObjs.InsertGrouped( xDrawObj );
+ // to be able to find objects by ID
+ maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj;
+ }
+}
+
+void XclImpDrawing::ReadMsoDrawing( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ // disable internal CONTINUE handling
+ rStrm.ResetRecord( false );
+ // read leading MSODRAWING record
+ ReadDffRecord( rStrm );
+
+ // read following drawing records, but do not start following unrelated record
+ bool bLoop = true;
+ while( bLoop ) switch( rStrm.GetNextRecId() )
+ {
+ case EXC_ID_MSODRAWING:
+ case EXC_ID_MSODRAWINGSEL:
+ case EXC_ID_CONT:
+ rStrm.StartNextRecord();
+ ReadDffRecord( rStrm );
+ break;
+ case EXC_ID_OBJ:
+ rStrm.StartNextRecord();
+ ReadObj8( rStrm );
+ break;
+ case EXC_ID_TXO:
+ rStrm.StartNextRecord();
+ ReadTxo( rStrm );
+ break;
+ default:
+ bLoop = false;
+ }
+
+ // re-enable internal CONTINUE handling
+ rStrm.ResetRecord( true );
+}
+
+XclImpDrawObjRef XclImpDrawing::FindDrawObj( const DffRecordHeader& rHeader ) const
+{
+ /* maObjMap stores objects by position of the client data (OBJ record) in
+ the DFF stream, which is always behind shape start position of the
+ passed header. The function upper_bound() finds the first element in
+ the map whose key is greater than the start position of the header. Its
+ end position is used to test whether the found object is really related
+ to the shape. */
+ XclImpDrawObjRef xDrawObj;
+ XclImpObjMap::const_iterator aIt = maObjMap.upper_bound( rHeader.GetRecBegFilePos() );
+ if( (aIt != maObjMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) )
+ xDrawObj = aIt->second;
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawing::FindDrawObj( sal_uInt16 nObjId ) const
+{
+ XclImpDrawObjRef xDrawObj;
+ XclImpObjMapById::const_iterator aIt = maObjMapId.find( nObjId );
+ if( aIt != maObjMapId.end() )
+ xDrawObj = aIt->second;
+ return xDrawObj;
+}
+
+const XclImpObjTextData* XclImpDrawing::FindTextData( const DffRecordHeader& rHeader ) const
+{
+ /* maTextMap stores textbox data by position of the client data (TXO
+ record) in the DFF stream, which is always behind shape start position
+ of the passed header. The function upper_bound() finds the first
+ element in the map whose key is greater than the start position of the
+ header. Its end position is used to test whether the found object is
+ really related to the shape. */
+ XclImpObjTextMap::const_iterator aIt = maTextMap.upper_bound( rHeader.GetRecBegFilePos() );
+ if( (aIt != maTextMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) )
+ return aIt->second.get();
+ return nullptr;
+}
+
+void XclImpDrawing::ApplyGroupBoxes()
+{
+ // sorted: smallest to largest - looking for smallest contained-in GroupBox
+ // multimap: allows duplicate key values - may have identical areas.
+ std::multimap<double, XclImpDrawObjRef> aGroupBoxAreaMap;
+ for (auto& rGroupBox : maObjMapId)
+ {
+ if (rGroupBox.second->GetObjType() != EXC_OBJTYPE_GROUPBOX)
+ continue;
+ const tools::Rectangle& rRect = rGroupBox.second->GetDffRect();
+ const double fArea = double(rRect.GetWidth()) * rRect.GetHeight();
+ aGroupBoxAreaMap.insert(std::pair<double, XclImpDrawObjRef>(fArea, rGroupBox.second));
+ }
+
+ for (auto& rGroupedObj : maObjMapId)
+ {
+ auto pRadioButton = dynamic_cast<XclImpOptionButtonObj*>(rGroupedObj.second.get());
+ if (!pRadioButton || pRadioButton->IsInGroup())
+ continue;
+
+ OUString sGroupName("autoGroup_");
+ for (auto& rGroupBox : aGroupBoxAreaMap)
+ {
+ assert(pRadioButton->GetTab() == rGroupBox.second->GetTab() && "impossible right?");
+ if (!rGroupBox.second->GetDffRect().Contains(pRadioButton->GetDffRect()))
+ continue;
+
+ sGroupName = rGroupBox.second->GetObjName();
+ if (sGroupName.isEmpty())
+ sGroupName += "autoGroup_" + OUString::number(rGroupBox.second->GetObjId());
+ // I ASSUME the smallest box wins in MS Word. (otherwise first? last?)
+ break;
+ }
+ pRadioButton->SetStringProperty("GroupName", sGroupName);
+ }
+}
+
+void XclImpDrawing::SetSkipObj( sal_uInt16 nObjId )
+{
+ maSkipObjs.push_back( nObjId );
+}
+
+std::size_t XclImpDrawing::GetProgressSize() const
+{
+ return std::accumulate(maObjMap.begin(), maObjMap.end(), maRawObjs.GetProgressSize(),
+ [](const std::size_t& rSum, const XclImpObjMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); });
+}
+
+void XclImpDrawing::ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage )
+{
+ //rhbz#636521, disable undo during conversion. faster, smaller and stops
+ //temp objects being inserted into the undo list
+ bool bOrigUndoStatus = rSdrModel.IsUndoEnabled();
+ rSdrModel.EnableUndo(false);
+ // register this drawing manager at the passed (global) DFF manager
+ rDffConv.InitializeDrawing( *this, rSdrModel, rSdrPage );
+ // process list of objects to be skipped
+ for( const auto& rSkipObj : maSkipObjs )
+ if( XclImpDrawObjBase* pDrawObj = FindDrawObj( rSkipObj ).get() )
+ pDrawObj->SetProcessSdrObj( false );
+ // process drawing objects without DFF data
+ rDffConv.ProcessDrawing( maRawObjs );
+ // process all objects in the DFF stream
+ rDffConv.ProcessDrawing( maDffStrm );
+ // assign groups based on being contained in the same GroupBox/sheet
+ ApplyGroupBoxes();
+ // unregister this drawing manager at the passed (global) DFF manager
+ rDffConv.FinalizeDrawing();
+ rSdrModel.EnableUndo(bOrigUndoStatus);
+}
+
+// protected ------------------------------------------------------------------
+
+void XclImpDrawing::AppendRawObject( const XclImpDrawObjRef& rxDrawObj )
+{
+ OSL_ENSURE( rxDrawObj, "XclImpDrawing::AppendRawObject - unexpected empty reference" );
+ maRawObjs.push_back( rxDrawObj );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpDrawing::ReadWmf( Graphic& rGraphic, XclImpStream& rStrm ) // static helper
+{
+ // extract graphic data from IMGDATA and following CONTINUE records
+ rStrm.Ignore( 8 );
+ SvMemoryStream aMemStrm;
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ // import the graphic from memory stream
+ GDIMetaFile aGDIMetaFile;
+ if( ::ReadWindowMetafile( aMemStrm, aGDIMetaFile ) )
+ rGraphic = aGDIMetaFile;
+}
+
+void XclImpDrawing::ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm ) // static helper
+{
+ // extract graphic data from IMGDATA and following CONTINUE records
+ SvMemoryStream aMemStrm;
+
+ /* Excel 3 and 4 seem to write broken BMP data. Usually they write a
+ DIBCOREHEADER (12 bytes) containing width, height, planes = 1, and
+ pixel depth = 32 bit. After that, 3 unused bytes are added before the
+ actual pixel data. This does even confuse Excel 5 and later, which
+ cannot read the image data correctly. */
+ if( rRoot.GetBiff() <= EXC_BIFF4 )
+ {
+ rStrm.PushPosition();
+ sal_uInt32 nHdrSize;
+ sal_uInt16 nWidth, nHeight, nPlanes, nDepth;
+ nHdrSize = rStrm.ReaduInt32();
+ nWidth = rStrm.ReaduInt16();
+ nHeight = rStrm.ReaduInt16();
+ nPlanes = rStrm.ReaduInt16();
+ nDepth = rStrm.ReaduInt16();
+ if( (nHdrSize == 12) && (nPlanes == 1) && (nDepth == 32) )
+ {
+ rStrm.Ignore( 3 );
+ aMemStrm.SetEndian( SvStreamEndian::LITTLE );
+ aMemStrm.WriteUInt32( nHdrSize ).WriteUInt16( nWidth ).WriteUInt16( nHeight ).WriteUInt16( nPlanes ).WriteUInt16( nDepth );
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+ }
+ rStrm.PopPosition();
+ }
+
+ // no special handling above -> just copy the remaining record data
+ if( aMemStrm.Tell() == 0 )
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+
+ // import the graphic from memory stream
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ Bitmap aBitmap;
+ if( ReadDIB(aBitmap, aMemStrm, false) ) // read DIB without file header
+ rGraphic = BitmapEx(aBitmap);
+}
+
+void XclImpDrawing::ReadDffRecord( XclImpStream& rStrm )
+{
+ maDffStrm.Seek( STREAM_SEEK_TO_END );
+ rStrm.CopyRecordToStream( maDffStrm );
+}
+
+void XclImpDrawing::ReadObj8( XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj = XclImpDrawObjBase::ReadObj8( GetRoot(), rStrm );
+ // store the new object in the internal containers
+ maObjMap[ maDffStrm.Tell() ] = xDrawObj;
+ maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj;
+}
+
+void XclImpDrawing::ReadTxo( XclImpStream& rStrm )
+{
+ XclImpObjTextRef xTextData = std::make_shared<XclImpObjTextData>();
+ maTextMap[ maDffStrm.Tell() ] = xTextData;
+
+ // 1) read the TXO record
+ xTextData->maData.ReadTxo8( rStrm );
+
+ // 2) first CONTINUE with string
+ xTextData->mxString.reset();
+ bool bValid = true;
+ if( xTextData->maData.mnTextLen > 0 )
+ {
+ bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord();
+ OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" );
+ if( bValid )
+ xTextData->mxString = std::make_shared<XclImpString>( rStrm.ReadUniString( xTextData->maData.mnTextLen ) );
+ }
+
+ // 3) second CONTINUE with formatting runs
+ if( xTextData->maData.mnFormatSize > 0 )
+ {
+ bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord();
+ OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" );
+ if( bValid )
+ xTextData->ReadFormats( rStrm );
+ }
+}
+
+XclImpSheetDrawing::XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab ) :
+ XclImpDrawing( rRoot, true ),
+ maScUsedArea( ScAddress::INITIALIZE_INVALID )
+{
+ maScUsedArea.aStart.SetTab( nScTab );
+ maScUsedArea.aEnd.SetTab( nScTab );
+}
+
+void XclImpSheetDrawing::ReadNote( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ ReadNote3( rStrm );
+ break;
+ case EXC_BIFF8:
+ ReadNote8( rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpSheetDrawing::ReadTabChart( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF5 );
+ auto xChartObj = std::make_shared<XclImpChartObj>( GetRoot(), true );
+ xChartObj->ReadChartSubStream( rStrm );
+ // insert the chart as raw object without connected DFF data
+ AppendRawObject( xChartObj );
+}
+
+void XclImpSheetDrawing::ConvertObjects( XclImpDffConverter& rDffConv )
+{
+ if( SdrModel* pSdrModel = GetDoc().GetDrawLayer() )
+ if( SdrPage* pSdrPage = GetSdrPage( maScUsedArea.aStart.Tab() ) )
+ ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
+}
+
+tools::Rectangle XclImpSheetDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool /*bDffAnchor*/ ) const
+{
+ return rAnchor.GetRect( GetRoot(), maScUsedArea.aStart.Tab(), MapUnit::Map100thMM );
+}
+
+void XclImpSheetDrawing::OnObjectInserted( const XclImpDrawObjBase& rDrawObj )
+{
+ ScRange aScObjArea = rDrawObj.GetUsedArea( maScUsedArea.aStart.Tab() );
+ if( aScObjArea.IsValid() )
+ maScUsedArea.ExtendTo( aScObjArea );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpSheetDrawing::ReadNote3( XclImpStream& rStrm )
+{
+ XclAddress aXclPos;
+ rStrm >> aXclPos;
+ sal_uInt16 nTotalLen = rStrm.ReaduInt16();
+
+ ScAddress aScNotePos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) )
+ return;
+
+ sal_uInt16 nPartLen = ::std::min( nTotalLen, static_cast< sal_uInt16 >( rStrm.GetRecLeft() ) );
+ OUStringBuffer aNoteText(rStrm.ReadRawByteString( nPartLen ));
+ nTotalLen = nTotalLen - nPartLen;
+ while (true)
+ {
+ if (!nTotalLen)
+ break;
+ if (rStrm.GetNextRecId() != EXC_ID_NOTE)
+ break;
+ if (!rStrm.StartNextRecord())
+ break;
+ rStrm >> aXclPos;
+ nPartLen = rStrm.ReaduInt16();
+ OSL_ENSURE( aXclPos.mnRow == 0xFFFF, "XclImpObjectManager::ReadNote3 - missing continuation NOTE record" );
+ if( aXclPos.mnRow == 0xFFFF )
+ {
+ OSL_ENSURE( nPartLen <= nTotalLen, "XclImpObjectManager::ReadNote3 - string too long" );
+ aNoteText.append(rStrm.ReadRawByteString( nPartLen ));
+ nTotalLen = nTotalLen - ::std::min( nTotalLen, nPartLen );
+ }
+ else
+ {
+ // seems to be a new note, record already started -> load the note
+ rStrm.Seek( EXC_REC_SEEK_TO_BEGIN );
+ ReadNote( rStrm );
+ nTotalLen = 0;
+ }
+ }
+ ScNoteUtil::CreateNoteFromString( GetDoc(), aScNotePos, aNoteText.makeStringAndClear(), false, false );
+}
+
+void XclImpSheetDrawing::ReadNote8( XclImpStream& rStrm )
+{
+ XclAddress aXclPos;
+ sal_uInt16 nFlags, nObjId;
+ rStrm >> aXclPos;
+ nFlags = rStrm.ReaduInt16();
+ nObjId = rStrm.ReaduInt16();
+
+ ScAddress aScNotePos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) )
+ if( nObjId != EXC_OBJ_INVALID_ID )
+ if( XclImpNoteObj* pNoteObj = dynamic_cast< XclImpNoteObj* >( FindDrawObj( nObjId ).get() ) )
+ pNoteObj->SetNoteData( aScNotePos, nFlags );
+}
+
+// The object manager =========================================================
+
+XclImpObjectManager::XclImpObjectManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ maDefObjNames[ EXC_OBJTYPE_GROUP ] = "Group";
+ maDefObjNames[ EXC_OBJTYPE_LINE ] = ScResId( STR_SHAPE_LINE );
+ maDefObjNames[ EXC_OBJTYPE_RECTANGLE ] = ScResId( STR_SHAPE_RECTANGLE );
+ maDefObjNames[ EXC_OBJTYPE_OVAL ] = ScResId( STR_SHAPE_OVAL );
+ maDefObjNames[ EXC_OBJTYPE_ARC ] = "Arc";
+ maDefObjNames[ EXC_OBJTYPE_CHART ] = "Chart";
+ maDefObjNames[ EXC_OBJTYPE_TEXT ] = "Text";
+ maDefObjNames[ EXC_OBJTYPE_BUTTON ] = ScResId( STR_FORM_BUTTON );
+ maDefObjNames[ EXC_OBJTYPE_PICTURE ] = "Picture";
+ maDefObjNames[ EXC_OBJTYPE_POLYGON ] = "Freeform";
+ maDefObjNames[ EXC_OBJTYPE_CHECKBOX ] = ScResId( STR_FORM_CHECKBOX );
+ maDefObjNames[ EXC_OBJTYPE_OPTIONBUTTON ] = ScResId( STR_FORM_OPTIONBUTTON );
+ maDefObjNames[ EXC_OBJTYPE_EDIT ] = "Edit Box";
+ maDefObjNames[ EXC_OBJTYPE_LABEL ] = ScResId( STR_FORM_LABEL );
+ maDefObjNames[ EXC_OBJTYPE_DIALOG ] = "Dialog Frame";
+ maDefObjNames[ EXC_OBJTYPE_SPIN ] = ScResId( STR_FORM_SPINNER );
+ maDefObjNames[ EXC_OBJTYPE_SCROLLBAR ] = ScResId( STR_FORM_SCROLLBAR );
+ maDefObjNames[ EXC_OBJTYPE_LISTBOX ] = ScResId( STR_FORM_LISTBOX );
+ maDefObjNames[ EXC_OBJTYPE_GROUPBOX ] = ScResId( STR_FORM_GROUPBOX );
+ maDefObjNames[ EXC_OBJTYPE_DROPDOWN ] = ScResId( STR_FORM_DROPDOWN );
+ maDefObjNames[ EXC_OBJTYPE_NOTE ] = "Comment";
+ maDefObjNames[ EXC_OBJTYPE_DRAWING ] = ScResId( STR_SHAPE_AUTOSHAPE );
+}
+
+XclImpObjectManager::~XclImpObjectManager()
+{
+}
+
+void XclImpObjectManager::ReadMsoDrawingGroup( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ // Excel continues this record with MSODRAWINGGROUP and CONTINUE records, hmm.
+ rStrm.ResetRecord( true, EXC_ID_MSODRAWINGGROUP );
+ maDggStrm.Seek( STREAM_SEEK_TO_END );
+ rStrm.CopyRecordToStream( maDggStrm );
+}
+
+XclImpSheetDrawing& XclImpObjectManager::GetSheetDrawing( SCTAB nScTab )
+{
+ XclImpSheetDrawingRef& rxDrawing = maSheetDrawings[ nScTab ];
+ if( !rxDrawing )
+ rxDrawing = std::make_shared<XclImpSheetDrawing>( GetRoot(), nScTab );
+ return *rxDrawing;
+}
+
+void XclImpObjectManager::ConvertObjects()
+{
+ // do nothing if the document does not contain a drawing layer
+ if( !GetDoc().GetDrawLayer() )
+ return;
+
+ // get total progress bar size for all sheet drawing managers
+ std::size_t nProgressSize = std::accumulate(maSheetDrawings.begin(), maSheetDrawings.end(), std::size_t(0),
+ [](const std::size_t& rSum, const XclImpSheetDrawingMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); });
+ // nothing to do if progress bar is zero (no objects present)
+ if( nProgressSize == 0 )
+ return;
+
+ XclImpDffConverter aDffConv( GetRoot(), maDggStrm );
+ aDffConv.StartProgressBar( nProgressSize );
+ for( auto& rEntry : maSheetDrawings )
+ rEntry.second->ConvertObjects( aDffConv );
+
+ // #i112436# don't call ScChartListenerCollection::SetDirty here,
+ // instead use InterpretDirtyCells in ScDocument::CalcAfterLoad.
+}
+
+OUString XclImpObjectManager::GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const
+{
+ OUString aDefName;
+ DefObjNameMap::const_iterator aIt = maDefObjNames.find( rDrawObj.GetObjType() );
+ if( aIt != maDefObjNames.end() )
+ aDefName = aIt->second;
+ return aDefName + " " + OUString::number(static_cast<sal_Int32>(rDrawObj.GetObjId()));
+}
+
+ScRange XclImpObjectManager::GetUsedArea( SCTAB nScTab ) const
+{
+ XclImpSheetDrawingMap::const_iterator aIt = maSheetDrawings.find( nScTab );
+ if( aIt != maSheetDrawings.end() )
+ return aIt->second->GetUsedArea();
+ return ScRange( ScAddress::INITIALIZE_INVALID );
+}
+
+// DFF property set helper ====================================================
+
+XclImpDffPropSet::XclImpDffPropSet( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maDffConv( rRoot, maDummyStrm )
+{
+}
+
+void XclImpDffPropSet::Read( XclImpStream& rStrm )
+{
+ sal_uInt32 nPropSetSize;
+
+ rStrm.PushPosition();
+ rStrm.Ignore( 4 );
+ nPropSetSize = rStrm.ReaduInt32();
+ rStrm.PopPosition();
+
+ mxMemStrm.reset( new SvMemoryStream );
+ rStrm.CopyToStream( *mxMemStrm, 8 + nPropSetSize );
+ mxMemStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ maDffConv.ReadPropSet( *mxMemStrm, nullptr );
+}
+
+sal_uInt32 XclImpDffPropSet::GetPropertyValue( sal_uInt16 nPropId ) const
+{
+ return maDffConv.GetPropertyValue( nPropId, 0 );
+}
+
+void XclImpDffPropSet::FillToItemSet( SfxItemSet& rItemSet ) const
+{
+ if( mxMemStrm )
+ maDffConv.ApplyAttributes( *mxMemStrm, rItemSet );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet )
+{
+ rPropSet.Read( rStrm );
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiformula.cxx b/sc/source/filter/excel/xiformula.cxx
new file mode 100644
index 0000000000..a5f4d78642
--- /dev/null
+++ b/sc/source/filter/excel/xiformula.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 <xiformula.hxx>
+#include <rangelst.hxx>
+#include <xistream.hxx>
+
+#include <excform.hxx>
+
+// Formula compiler ===========================================================
+
+/** Implementation class of the export formula compiler. */
+class XclImpFmlaCompImpl : protected XclImpRoot, protected XclTokenArrayHelper
+{
+public:
+ explicit XclImpFmlaCompImpl( const XclImpRoot& rRoot );
+
+ /** Creates a range list from the passed Excel token array. */
+ void CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm );
+
+ std::unique_ptr<ScTokenArray> CreateFormula( XclFormulaType eType, const XclTokenArray& rXclTokArr );
+
+};
+
+XclImpFmlaCompImpl::XclImpFmlaCompImpl( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpFmlaCompImpl::CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType /*eType*/,
+ const XclTokenArray& rXclTokArr, XclImpStream& /*rStrm*/ )
+{
+ rScRanges.RemoveAll();
+
+ //FIXME: evil hack, using old formula import :-)
+ if( !rXclTokArr.Empty() )
+ {
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( EXC_ID_EOF ).WriteUInt16( rXclTokArr.GetSize() );
+ aMemStrm.WriteBytes(rXclTokArr.GetData(), rXclTokArr.GetSize());
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ GetOldFmlaConverter().GetAbsRefs( rScRanges, aFmlaStrm, aFmlaStrm.GetRecSize() );
+ }
+}
+
+std::unique_ptr<ScTokenArray> XclImpFmlaCompImpl::CreateFormula(
+ XclFormulaType /*eType*/, const XclTokenArray& rXclTokArr )
+{
+ if (rXclTokArr.Empty())
+ return nullptr;
+
+ // evil hack! are we trying to phase out the old style formula converter ?
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( EXC_ID_EOF ).WriteUInt16( rXclTokArr.GetSize() );
+ aMemStrm.WriteBytes(rXclTokArr.GetData(), rXclTokArr.GetSize());
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ std::unique_ptr<ScTokenArray> pArray;
+ GetOldFmlaConverter().Reset();
+ GetOldFmlaConverter().Convert(pArray, aFmlaStrm, aFmlaStrm.GetRecSize(), true);
+ return pArray;
+}
+
+XclImpFormulaCompiler::XclImpFormulaCompiler( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mxImpl( std::make_shared<XclImpFmlaCompImpl>( rRoot ) )
+{
+}
+
+XclImpFormulaCompiler::~XclImpFormulaCompiler()
+{
+}
+
+void XclImpFormulaCompiler::CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm )
+{
+ mxImpl->CreateRangeList( rScRanges, eType, rXclTokArr, rStrm );
+}
+
+std::unique_ptr<ScTokenArray> XclImpFormulaCompiler::CreateFormula(
+ XclFormulaType eType, const XclTokenArray& rXclTokArr )
+{
+ return mxImpl->CreateFormula(eType, rXclTokArr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xihelper.cxx b/sc/source/filter/excel/xihelper.cxx
new file mode 100644
index 0000000000..e095d22018
--- /dev/null
+++ b/sc/source/filter/excel/xihelper.cxx
@@ -0,0 +1,902 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xihelper.hxx>
+#include <svl/itemset.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <editeng/editobj.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <rangelst.hxx>
+#include <editutil.hxx>
+#include <attrib.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xistring.hxx>
+#include <xistyle.hxx>
+#include <excform.hxx>
+#include <scmatrix.hxx>
+#include <documentimport.hxx>
+#include <sal/log.hxx>
+
+// Excel->Calc cell address/range conversion ==================================
+
+namespace {
+
+/** Fills the passed Calc address with the passed Excel cell coordinates without checking any limits. */
+void lclFillAddress( ScAddress& rScPos, sal_uInt16 nXclCol, sal_uInt32 nXclRow, SCTAB nScTab )
+{
+ rScPos.SetCol( static_cast< SCCOL >( nXclCol ) );
+ rScPos.SetRow( static_cast< SCROW >( nXclRow ) );
+ rScPos.SetTab( nScTab );
+}
+
+} // namespace
+
+XclImpAddressConverter::XclImpAddressConverter( const XclImpRoot& rRoot ) :
+ XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetScMaxPos() )
+{
+}
+
+// cell address ---------------------------------------------------------------
+
+bool XclImpAddressConverter::CheckAddress( const XclAddress& rXclPos, bool bWarn )
+{
+ bool bValidCol = rXclPos.mnCol <= mnMaxCol;
+ bool bValidRow = rXclPos.mnRow <= mnMaxRow;
+ bool bValid = bValidCol && bValidRow;
+ if( !bValid && bWarn )
+ {
+ mbColTrunc |= !bValidCol;
+ mbRowTrunc |= !bValidRow;
+ mrTracer.TraceInvalidAddress( ScAddress(
+ static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), 0 ), maMaxPos );
+ }
+ return bValid;
+}
+
+bool XclImpAddressConverter::ConvertAddress( ScAddress& rScPos,
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
+{
+ bool bValid = CheckAddress( rXclPos, bWarn );
+ if( bValid )
+ lclFillAddress( rScPos, rXclPos.mnCol, rXclPos.mnRow, nScTab );
+ return bValid;
+}
+
+ScAddress XclImpAddressConverter::CreateValidAddress(
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
+{
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !ConvertAddress( aScPos, rXclPos, nScTab, bWarn ) )
+ {
+ aScPos.SetCol( static_cast< SCCOL >( ::std::min( rXclPos.mnCol, mnMaxCol ) ) );
+ aScPos.SetRow( static_cast< SCROW >( ::std::min( rXclPos.mnRow, mnMaxRow ) ) );
+ aScPos.SetTab( limit_cast< SCTAB >( nScTab, 0, maMaxPos.Tab() ) );
+ }
+ return aScPos;
+}
+
+// cell range -----------------------------------------------------------------
+
+bool XclImpAddressConverter::ConvertRange( ScRange& rScRange,
+ const XclRange& rXclRange, SCTAB nScTab1, SCTAB nScTab2, bool bWarn )
+{
+ // check start position
+ bool bValidStart = CheckAddress( rXclRange.maFirst, bWarn );
+ if( bValidStart )
+ {
+ lclFillAddress( rScRange.aStart, rXclRange.maFirst.mnCol, rXclRange.maFirst.mnRow, nScTab1 );
+
+ // check & correct end position
+ sal_uInt16 nXclCol2 = rXclRange.maLast.mnCol;
+ sal_uInt32 nXclRow2 = rXclRange.maLast.mnRow;
+ if( !CheckAddress( rXclRange.maLast, bWarn ) )
+ {
+ nXclCol2 = ::std::min( nXclCol2, mnMaxCol );
+ nXclRow2 = ::std::min( nXclRow2, mnMaxRow );
+ }
+ lclFillAddress( rScRange.aEnd, nXclCol2, nXclRow2, nScTab2 );
+ }
+ return bValidStart;
+}
+
+// cell range list ------------------------------------------------------------
+
+void XclImpAddressConverter::ConvertRangeList( ScRangeList& rScRanges,
+ const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn )
+{
+ rScRanges.RemoveAll();
+ for( const auto& rXclRange : rXclRanges )
+ {
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( ConvertRange( aScRange, rXclRange, nScTab, nScTab, bWarn ) )
+ rScRanges.push_back( aScRange );
+ }
+}
+
+// String->EditEngine conversion ==============================================
+
+namespace {
+
+std::unique_ptr<EditTextObject> lclCreateTextObject( const XclImpRoot& rRoot,
+ const XclImpString& rString, XclFontItemType eType, sal_uInt16 nXFIndex )
+{
+ std::unique_ptr<EditTextObject> pTextObj;
+
+ const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer();
+ const XclImpFont* pFirstFont = rXFBuffer.GetFont( nXFIndex );
+ bool bFirstEscaped = pFirstFont && pFirstFont->HasEscapement();
+
+ if( rString.IsRich() || bFirstEscaped )
+ {
+ const XclImpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ const XclFormatRunVec& rFormats = rString.GetFormats();
+
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ rEE.SetTextCurrentDefaults( rString.GetText() );
+
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+ if( bFirstEscaped )
+ rFontBuffer.FillToItemSet( aItemSet, eType, rXFBuffer.GetFontIndex( nXFIndex ) );
+ ESelection aSelection;
+
+ XclFormatRun aNextRun;
+ XclFormatRunVec::const_iterator aIt = rFormats.begin();
+ XclFormatRunVec::const_iterator aEnd = rFormats.end();
+
+ if( aIt != aEnd )
+ aNextRun = *aIt++;
+ else
+ aNextRun.mnChar = 0xFFFF;
+
+ sal_Int32 nLen = rString.GetText().getLength();
+ for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
+ {
+ // reached new different formatted text portion
+ if( nChar >= aNextRun.mnChar )
+ {
+ // send items to edit engine
+ rEE.QuickSetAttribs( aItemSet, aSelection );
+
+ // start new item set
+ aItemSet.ClearItem();
+ rFontBuffer.FillToItemSet( aItemSet, eType, aNextRun.mnFontIdx );
+
+ // read new formatting information
+ if( aIt != aEnd )
+ aNextRun = *aIt++;
+ else
+ aNextRun.mnChar = 0xFFFF;
+
+ // reset selection start to current position
+ aSelection.nStartPara = aSelection.nEndPara;
+ aSelection.nStartPos = aSelection.nEndPos;
+ }
+
+ // set end of selection to current position
+ if( rString.GetText()[ nChar ] == '\n' )
+ {
+ ++aSelection.nEndPara;
+ aSelection.nEndPos = 0;
+ }
+ else
+ ++aSelection.nEndPos;
+ }
+
+ // send items of last text portion to edit engine
+ rEE.QuickSetAttribs( aItemSet, aSelection );
+
+ pTextObj = rEE.CreateTextObject();
+ }
+
+ return pTextObj;
+}
+
+} // namespace
+
+std::unique_ptr<EditTextObject> XclImpStringHelper::CreateTextObject(
+ const XclImpRoot& rRoot, const XclImpString& rString )
+{
+ return lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, 0 );
+}
+
+void XclImpStringHelper::SetToDocument(
+ ScDocumentImport& rDoc, const ScAddress& rPos, const XclImpRoot& rRoot,
+ const XclImpString& rString, sal_uInt16 nXFIndex )
+{
+ if (rString.GetText().isEmpty())
+ return;
+
+ ::std::unique_ptr< EditTextObject > pTextObj( lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, nXFIndex ) );
+
+ if (pTextObj)
+ {
+ rDoc.setEditCell(rPos, std::move(pTextObj));
+ }
+ else
+ {
+ const OUString& aStr = rString.GetText();
+ if (aStr.indexOf('\n') != -1 || aStr.indexOf('\r') != -1)
+ {
+ const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer();
+ const XclImpXF* pXF = rXFBuffer.GetXF( nXFIndex );
+ bool bSingleLine = pXF ? !pXF->GetLineBreak() : false;
+
+ // Multiline content.
+ ScFieldEditEngine& rEngine = rDoc.getDoc().GetEditEngine();
+ rEngine.SetSingleLine(bSingleLine);
+ rEngine.SetTextCurrentDefaults(aStr);
+ rDoc.setEditCell(rPos, rEngine.CreateTextObject());
+ rEngine.SetSingleLine(false);
+ }
+ else
+ {
+ // Normal text cell.
+ rDoc.setStringCell(rPos, aStr);
+ }
+ }
+}
+
+// Header/footer conversion ===================================================
+
+XclImpHFConverter::XclImpHFPortionInfo::XclImpHFPortionInfo() :
+ mnHeight( 0 ),
+ mnMaxLineHt( 0 )
+{
+ maSel.nStartPara = maSel.nEndPara = 0;
+ maSel.nStartPos = maSel.nEndPos = 0;
+}
+
+XclImpHFConverter::XclImpHFConverter( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mrEE( rRoot.GetHFEditEngine() ),
+ mxFontData( new XclFontData ),
+ meCurrObj( EXC_HF_CENTER )
+{
+}
+
+XclImpHFConverter::~XclImpHFConverter()
+{
+}
+
+void XclImpHFConverter::ParseString( const OUString& rHFString )
+{
+ // edit engine objects
+ mrEE.SetText( OUString() );
+ maInfos.clear();
+ maInfos.resize( EXC_HF_PORTION_COUNT );
+ meCurrObj = EXC_HF_CENTER;
+
+ // parser temporaries
+ maCurrText.truncate();
+ OUStringBuffer aReadFont; // current font name
+ OUStringBuffer aReadStyle; // current font style
+ sal_uInt16 nReadHeight = 0; // current font height
+ ResetFontData();
+
+ /** State of the parser. */
+ enum XclHFParserState
+ {
+ xlPSText, /// Read text, search for functions.
+ xlPSFunc, /// Read function (token following a '&').
+ xlPSFont, /// Read font name ('&' is followed by '"', reads until next '"' or ',').
+ xlPSFontStyle, /// Read font style name (font part after ',', reads until next '"').
+ xlPSHeight /// Read font height ('&' is followed by num. digits, reads until non-digit).
+ } eState = xlPSText;
+
+ const sal_Unicode* pChar = rHFString.getStr();
+ const sal_Unicode* pNull = pChar + rHFString.getLength(); // pointer to terminating null char
+ while( *pChar )
+ {
+ switch( eState )
+ {
+
+// --- read text character ---
+
+ case xlPSText:
+ {
+ switch( *pChar )
+ {
+ case '&': // new command
+ InsertText();
+ eState = xlPSFunc;
+ break;
+ case '\n': // line break
+ InsertText();
+ InsertLineBreak();
+ break;
+ default:
+ maCurrText.append(OUStringChar(*pChar));
+ }
+ }
+ break;
+
+// --- read control sequence ---
+
+ case xlPSFunc:
+ {
+ eState = xlPSText;
+ switch( *pChar )
+ {
+ case '&': maCurrText.append("&"); break; // the '&' character
+
+ case 'L': SetNewPortion( EXC_HF_LEFT ); break; // Left portion
+ case 'C': SetNewPortion( EXC_HF_CENTER ); break; // Center portion
+ case 'R': SetNewPortion( EXC_HF_RIGHT ); break; // Right portion
+
+ case 'P': InsertField( SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD ) ); break; // page
+ case 'N': InsertField( SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD ) ); break; // page count
+ case 'D': InsertField( SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD ) ); break; // date
+ case 'T': InsertField( SvxFieldItem( SvxTimeField(), EE_FEATURE_FIELD ) ); break; // time
+ case 'A': InsertField( SvxFieldItem( SvxTableField(), EE_FEATURE_FIELD ) ); break; // table name
+
+ case 'Z': // file path
+ InsertField( SvxFieldItem( SvxExtFileField(), EE_FEATURE_FIELD ) ); // convert to full name
+ if( (pNull - pChar >= 2) && (*(pChar + 1) == '&') && (*(pChar + 2) == 'F') )
+ {
+ // &Z&F found - ignore the &F part
+ pChar += 2;
+ }
+ break;
+ case 'F': // file name
+ InsertField( SvxFieldItem( SvxExtFileField( OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt ), EE_FEATURE_FIELD ) );
+ break;
+
+ case 'U': // underline
+ SetAttribs();
+ mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_SINGLE) ?
+ EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_SINGLE;
+ break;
+ case 'E': // double underline
+ SetAttribs();
+ mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_DOUBLE) ?
+ EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_DOUBLE;
+ break;
+ case 'S': // strikeout
+ SetAttribs();
+ mxFontData->mbStrikeout = !mxFontData->mbStrikeout;
+ break;
+ case 'X': // superscript
+ SetAttribs();
+ mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUPER) ?
+ EXC_FONTESC_NONE : EXC_FONTESC_SUPER;
+ break;
+ case 'Y': // subscript
+ SetAttribs();
+ mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUB) ?
+ EXC_FONTESC_NONE : EXC_FONTESC_SUB;
+ break;
+
+ case '\"': // font name
+ aReadFont.setLength(0);
+ aReadStyle.setLength(0);
+ eState = xlPSFont;
+ break;
+ default:
+ if( ('0' <= *pChar) && (*pChar <= '9') ) // font size
+ {
+ nReadHeight = *pChar - '0';
+ eState = xlPSHeight;
+ }
+ }
+ }
+ break;
+
+// --- read font name ---
+
+ case xlPSFont:
+ {
+ switch( *pChar )
+ {
+ case '\"':
+ --pChar;
+ [[fallthrough]];
+ case ',':
+ eState = xlPSFontStyle;
+ break;
+ default:
+ aReadFont.append(*pChar);
+ }
+ }
+ break;
+
+// --- read font style ---
+
+ case xlPSFontStyle:
+ {
+ switch( *pChar )
+ {
+ case '\"':
+ SetAttribs();
+ if( !aReadFont.isEmpty() )
+ mxFontData->maName = aReadFont.toString();
+ mxFontData->maStyle = aReadStyle.toString();
+ eState = xlPSText;
+ break;
+ default:
+ aReadStyle.append(*pChar);
+ }
+ }
+ break;
+
+// --- read font height ---
+
+ case xlPSHeight:
+ {
+ if( ('0' <= *pChar) && (*pChar <= '9') )
+ {
+ if( nReadHeight != 0xFFFF )
+ {
+ nReadHeight *= 10;
+ nReadHeight += (*pChar - '0');
+ if( nReadHeight > 1600 ) // max 1600pt = 32000twips
+ nReadHeight = 0xFFFF;
+ }
+ }
+ else
+ {
+ if( (nReadHeight != 0) && (nReadHeight != 0xFFFF) )
+ {
+ SetAttribs();
+ mxFontData->mnHeight = nReadHeight * 20;
+ }
+ --pChar;
+ eState = xlPSText;
+ }
+ }
+ break;
+ }
+ ++pChar;
+ }
+
+ // finalize
+ CreateCurrObject();
+ maInfos[ EXC_HF_LEFT ].mnHeight += GetMaxLineHeight( EXC_HF_LEFT );
+ maInfos[ EXC_HF_CENTER ].mnHeight += GetMaxLineHeight( EXC_HF_CENTER );
+ maInfos[ EXC_HF_RIGHT ].mnHeight += GetMaxLineHeight( EXC_HF_RIGHT );
+}
+
+void XclImpHFConverter::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const
+{
+ ScPageHFItem aHFItem( nWhichId );
+ if( maInfos[ EXC_HF_LEFT ].mxObj )
+ aHFItem.SetLeftArea( *maInfos[ EXC_HF_LEFT ].mxObj );
+ if( maInfos[ EXC_HF_CENTER ].mxObj )
+ aHFItem.SetCenterArea( *maInfos[ EXC_HF_CENTER ].mxObj );
+ if( maInfos[ EXC_HF_RIGHT ].mxObj )
+ aHFItem.SetRightArea( *maInfos[ EXC_HF_RIGHT ].mxObj );
+ rItemSet.Put( aHFItem );
+}
+
+sal_Int32 XclImpHFConverter::GetTotalHeight() const
+{
+ return ::std::max( maInfos[ EXC_HF_LEFT ].mnHeight,
+ ::std::max( maInfos[ EXC_HF_CENTER ].mnHeight, maInfos[ EXC_HF_RIGHT ].mnHeight ) );
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt16 XclImpHFConverter::GetMaxLineHeight( XclImpHFPortion ePortion ) const
+{
+ sal_uInt16 nMaxHt = maInfos[ ePortion ].mnMaxLineHt;
+ return (nMaxHt == 0) ? mxFontData->mnHeight : nMaxHt;
+}
+
+void XclImpHFConverter::UpdateMaxLineHeight( XclImpHFPortion ePortion )
+{
+ sal_uInt16& rnMaxHt = maInfos[ ePortion ].mnMaxLineHt;
+ rnMaxHt = ::std::max( rnMaxHt, mxFontData->mnHeight );
+}
+
+void XclImpHFConverter::UpdateCurrMaxLineHeight()
+{
+ UpdateMaxLineHeight( meCurrObj );
+}
+
+void XclImpHFConverter::SetAttribs()
+{
+ ESelection& rSel = GetCurrSel();
+ if( (rSel.nStartPara != rSel.nEndPara) || (rSel.nStartPos != rSel.nEndPos) )
+ {
+ SfxItemSet aItemSet( mrEE.GetEmptyItemSet() );
+ XclImpFont aFont( GetRoot(), *mxFontData );
+ aFont.FillToItemSet( aItemSet, XclFontItemType::HeaderFooter );
+ mrEE.QuickSetAttribs( aItemSet, rSel );
+ rSel.nStartPara = rSel.nEndPara;
+ rSel.nStartPos = rSel.nEndPos;
+ }
+}
+
+void XclImpHFConverter::ResetFontData()
+{
+ if( const XclImpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
+ *mxFontData = pFirstFont->GetFontData();
+ else
+ {
+ mxFontData->Clear();
+ mxFontData->mnHeight = 200;
+ }
+}
+
+void XclImpHFConverter::InsertText()
+{
+ if( !maCurrText.isEmpty() )
+ {
+ ESelection& rSel = GetCurrSel();
+ OUString sString(maCurrText.makeStringAndClear());
+ mrEE.QuickInsertText( sString, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ rSel.nEndPos = rSel.nEndPos + sString.getLength();
+ UpdateCurrMaxLineHeight();
+ }
+}
+
+void XclImpHFConverter::InsertField( const SvxFieldItem& rFieldItem )
+{
+ ESelection& rSel = GetCurrSel();
+ mrEE.QuickInsertField( rFieldItem, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ ++rSel.nEndPos;
+ UpdateCurrMaxLineHeight();
+}
+
+void XclImpHFConverter::InsertLineBreak()
+{
+ ESelection& rSel = GetCurrSel();
+ mrEE.QuickInsertText( OUString('\n'), ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ ++rSel.nEndPara;
+ rSel.nEndPos = 0;
+ GetCurrInfo().mnHeight += GetMaxLineHeight( meCurrObj );
+ GetCurrInfo().mnMaxLineHt = 0;
+}
+
+void XclImpHFConverter::CreateCurrObject()
+{
+ InsertText();
+ SetAttribs();
+ GetCurrObj() = mrEE.CreateTextObject();
+}
+
+void XclImpHFConverter::SetNewPortion( XclImpHFPortion eNew )
+{
+ if( eNew != meCurrObj )
+ {
+ CreateCurrObject();
+ meCurrObj = eNew;
+ if( GetCurrObj() )
+ mrEE.SetText( *GetCurrObj() );
+ else
+ mrEE.SetText( OUString() );
+ ResetFontData();
+ }
+}
+
+// URL conversion =============================================================
+
+namespace {
+
+void lclAppendUrlChar( OUString& rUrl, sal_Unicode cChar )
+{
+ // encode special characters
+ switch( cChar )
+ {
+ case '#': rUrl += "%23"; break;
+ case '%': rUrl += "%25"; break;
+ default: rUrl += OUStringChar( cChar );
+ }
+}
+
+} // namespace
+
+void XclImpUrlHelper::DecodeUrl(
+ OUString& rUrl, OUString& rTabName, bool& rbSameWb,
+ const XclImpRoot& rRoot, const OUString& rEncodedUrl )
+{
+ enum
+ {
+ xlUrlInit, /// Initial state, read string mode character.
+ xlUrlPath, /// Read URL path.
+ xlUrlFileName, /// Read file name.
+ xlUrlSheetName, /// Read sheet name.
+ xlUrlRaw /// Raw mode. No control characters will occur.
+ } eState = xlUrlInit;
+
+ bool bEncoded = true;
+ rbSameWb = false;
+
+ sal_Unicode cCurrDrive = 0;
+ OUString aDosBase( INetURLObject( rRoot.GetBasePath() ).getFSysPath( FSysStyle::Dos ) );
+ if (!aDosBase.isEmpty() && aDosBase.match(":\\", 1))
+ cCurrDrive = aDosBase[0];
+
+ const sal_Unicode* pChar = rEncodedUrl.getStr();
+ while( *pChar )
+ {
+ switch( eState )
+ {
+
+// --- first character ---
+
+ case xlUrlInit:
+ {
+ switch( *pChar )
+ {
+ case EXC_URLSTART_ENCODED:
+ eState = xlUrlPath;
+ break;
+ case EXC_URLSTART_SELF:
+ case EXC_URLSTART_SELFENCODED:
+ rbSameWb = true;
+ eState = xlUrlSheetName;
+ break;
+ case '[':
+ bEncoded = false;
+ eState = xlUrlFileName;
+ break;
+ default:
+ bEncoded = false;
+ lclAppendUrlChar( rUrl, *pChar );
+ eState = xlUrlPath;
+ }
+ }
+ break;
+
+// --- URL path ---
+
+ case xlUrlPath:
+ {
+ switch( *pChar )
+ {
+ case EXC_URL_DOSDRIVE:
+ {
+ if( *(pChar + 1) )
+ {
+ ++pChar;
+ if( *pChar == '@' )
+ rUrl += "\\\\";
+ else
+ {
+ lclAppendUrlChar( rUrl, *pChar );
+ rUrl += ":\\";
+ }
+ }
+ else
+ rUrl += "<NULL-DRIVE!>";
+ }
+ break;
+ case EXC_URL_DRIVEROOT:
+ if( cCurrDrive )
+ {
+ lclAppendUrlChar( rUrl, cCurrDrive );
+ rUrl += ":";
+ }
+ [[fallthrough]];
+ case EXC_URL_SUBDIR:
+ if( bEncoded )
+ rUrl += "\\";
+ else // control character in raw name -> DDE link
+ {
+ rUrl += OUStringChar(EXC_DDE_DELIM);
+ eState = xlUrlRaw;
+ }
+ break;
+ case EXC_URL_PARENTDIR:
+ rUrl += "..\\";
+ break;
+ case EXC_URL_RAW:
+ {
+ if( *(pChar + 1) )
+ {
+ sal_Int32 nLen = *++pChar;
+ for( sal_Int32 nChar = 0; (nChar < nLen) && *(pChar + 1); ++nChar )
+ lclAppendUrlChar( rUrl, *++pChar );
+// rUrl.Append( ':' );
+ }
+ }
+ break;
+ case '[':
+ eState = xlUrlFileName;
+ break;
+ default:
+ lclAppendUrlChar( rUrl, *pChar );
+ }
+ }
+ break;
+
+// --- file name ---
+
+ case xlUrlFileName:
+ {
+ switch( *pChar )
+ {
+ case ']': eState = xlUrlSheetName; break;
+ default: lclAppendUrlChar( rUrl, *pChar );
+ }
+ }
+ break;
+
+// --- sheet name ---
+
+ case xlUrlSheetName:
+ rTabName += OUStringChar( *pChar );
+ break;
+
+// --- raw read mode ---
+
+ case xlUrlRaw:
+ lclAppendUrlChar( rUrl, *pChar );
+ break;
+ }
+
+ ++pChar;
+ }
+}
+
+void XclImpUrlHelper::DecodeUrl(
+ OUString& rUrl, bool& rbSameWb, const XclImpRoot& rRoot, const OUString& rEncodedUrl )
+{
+ OUString aTabName;
+ OUString aUrl;
+ DecodeUrl( aUrl, aTabName, rbSameWb, rRoot, rEncodedUrl );
+ rUrl = aUrl;
+ OSL_ENSURE( aTabName.isEmpty(), "XclImpUrlHelper::DecodeUrl - sheet name ignored" );
+}
+
+bool XclImpUrlHelper::DecodeLink( OUString& rApplic, OUString& rTopic, std::u16string_view aEncUrl )
+{
+ size_t nPos = aEncUrl.find( EXC_DDE_DELIM );
+ if( nPos != std::u16string_view::npos && (nPos > 0) && (nPos + 1 < aEncUrl.size()) )
+ {
+ rApplic = aEncUrl.substr( 0, nPos );
+ rTopic = aEncUrl.substr( nPos + 1 );
+ return true;
+ }
+ return false;
+}
+
+// Cached Values ==============================================================
+
+XclImpCachedValue::XclImpCachedValue( XclImpStream& rStrm ) :
+ mfValue( 0.0 ),
+ mnBoolErr( 0 )
+{
+ mnType = rStrm.ReaduInt8();
+ switch( mnType )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ rStrm.Ignore( 8 );
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ mfValue = rStrm.ReadDouble();
+ break;
+ case EXC_CACHEDVAL_STRING:
+ maStr = rStrm.ReadUniString();
+ break;
+ case EXC_CACHEDVAL_BOOL:
+ case EXC_CACHEDVAL_ERROR:
+ {
+ double fVal;
+ mnBoolErr = rStrm.ReaduInt8();
+ rStrm.Ignore( 7 );
+
+ std::unique_ptr<ScTokenArray> pScTokArr = rStrm.GetRoot().GetOldFmlaConverter().GetBoolErr(
+ XclTools::ErrorToEnum( fVal, mnType == EXC_CACHEDVAL_ERROR, mnBoolErr ) );
+ if( pScTokArr )
+ mxTokArr = std::move( pScTokArr );
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpCachedValue::XclImpCachedValue - unknown data type" );
+ }
+}
+
+XclImpCachedValue::~XclImpCachedValue()
+{
+}
+
+FormulaError XclImpCachedValue::GetScError() const
+{
+ return (mnType == EXC_CACHEDVAL_ERROR) ? XclTools::GetScErrorCode( mnBoolErr ) : FormulaError::NONE;
+}
+
+// Matrix Cached Values ==============================================================
+
+XclImpCachedMatrix::XclImpCachedMatrix( XclImpStream& rStrm ) :
+ mnScCols( 0 ),
+ mnScRows( 0 )
+{
+ mnScCols = rStrm.ReaduInt8();
+ mnScRows = rStrm.ReaduInt16();
+
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ {
+ // in BIFF2-BIFF7: 256 columns represented by 0 columns
+ if( mnScCols == 0 )
+ mnScCols = 256;
+ }
+ else
+ {
+ // in BIFF8: columns and rows decreased by 1
+ ++mnScCols;
+ ++mnScRows;
+ }
+
+ //assuming worst case scenario of unknown types
+ const size_t nMinRecordSize = 1;
+ const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * mnScCols);
+ if (mnScRows > nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << mnScRows << " claimed, truncating");
+ mnScRows = nMaxRows;
+ }
+
+ for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
+ for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
+ maValueList.push_back( std::make_unique<XclImpCachedValue>( rStrm ) );
+}
+
+XclImpCachedMatrix::~XclImpCachedMatrix()
+{
+}
+
+ScMatrixRef XclImpCachedMatrix::CreateScMatrix( svl::SharedStringPool& rPool ) const
+{
+ ScMatrixRef xScMatrix;
+ OSL_ENSURE( mnScCols * mnScRows == maValueList.size(), "XclImpCachedMatrix::CreateScMatrix - element count mismatch" );
+ if( mnScCols && mnScRows && static_cast< sal_uLong >( mnScCols * mnScRows ) <= maValueList.size() )
+ {
+ xScMatrix = new ScMatrix(mnScCols, mnScRows, 0.0);
+ XclImpValueList::const_iterator itValue = maValueList.begin();
+ for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
+ {
+ for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
+ {
+ switch( (*itValue)->GetType() )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ // Excel shows 0.0 here, not an empty cell
+ xScMatrix->PutEmpty( nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ xScMatrix->PutDouble( (*itValue)->GetValue(), nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_STRING:
+ xScMatrix->PutString(rPool.intern((*itValue)->GetString()), nScCol, nScRow);
+ break;
+ case EXC_CACHEDVAL_BOOL:
+ xScMatrix->PutBoolean( (*itValue)->GetBool(), nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_ERROR:
+ xScMatrix->PutError( (*itValue)->GetScError(), nScCol, nScRow );
+ break;
+ default:
+ OSL_FAIL( "XclImpCachedMatrix::CreateScMatrix - unknown value type" );
+ xScMatrix->PutEmpty( nScCol, nScRow );
+ }
+ ++itValue;
+ }
+ }
+ }
+ return xScMatrix;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xilink.cxx b/sc/source/filter/excel/xilink.cxx
new file mode 100644
index 0000000000..cfaf9af568
--- /dev/null
+++ b/sc/source/filter/excel/xilink.cxx
@@ -0,0 +1,964 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <utility>
+#include <xilink.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scextopt.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiname.hxx>
+#include <xltools.hxx>
+#include <excform.hxx>
+#include <tokenarray.hxx>
+#include <externalrefmgr.hxx>
+#include <scmatrix.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+#include <vector>
+#include <memory>
+
+// *** Helper classes ***
+
+// Cached external cells ======================================================
+
+namespace {
+
+/**
+ * Contains the address and value of an external referenced cell.
+ * Note that this is non-copyable, so cannot be used in most stl/boost containers.
+ */
+class XclImpCrn : public XclImpCachedValue
+{
+public:
+ /** Reads a cached value and stores it with its cell address. */
+ explicit XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
+
+ const XclAddress& GetAddress() const { return maXclPos;}
+
+private:
+ XclAddress maXclPos; /// Excel position of the cached cell.
+};
+
+// Sheet in an external document ==============================================
+
+/** Contains the name and sheet index of one sheet in an external document. */
+class XclImpSupbookTab
+{
+public:
+ /** Stores the sheet name and marks the sheet index as invalid.
+ The sheet index is set while creating the Calc sheet with CreateTable(). */
+ explicit XclImpSupbookTab( OUString aTabName );
+
+ const OUString& GetTabName() const { return maTabName; }
+
+ /** Reads a CRN record (external referenced cell) at the specified address. */
+ void ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
+
+ void LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable,
+ svl::SharedStringPool& rPool );
+
+private:
+ typedef std::shared_ptr< XclImpCrn > XclImpCrnRef;
+
+ std::vector< XclImpCrnRef > maCrnList; /// List of CRN records (cached cell values).
+ OUString maTabName; /// Name of the external sheet.
+};
+
+}
+
+// External document (SUPBOOK) ================================================
+
+/** This class represents an external linked document (record SUPBOOK).
+ @descr Contains a list of all referenced sheets in the document. */
+class XclImpSupbook : protected XclImpRoot
+{
+public:
+ /** Reads the SUPBOOK record from stream. */
+ explicit XclImpSupbook( XclImpStream& rStrm );
+
+ /** Reads an XCT record (count of following CRNs and current sheet). */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record (external referenced cell). */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns the SUPBOOK record type. */
+ XclSupbookType GetType() const { return meType; }
+
+ /** Returns the URL of the external document. */
+ const OUString& GetXclUrl() const { return maXclUrl; }
+
+ /** Returns the external name specified by an index from the Excel document (one-based). */
+ const XclImpExtName* GetExternName( sal_uInt16 nXclIndex ) const;
+ /** Tries to decode the URL to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rDoc ) const;
+ /** Returns the specified macro name (1-based) or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nXclNameIdx ) const;
+
+ OUString GetTabName( sal_uInt16 nXtiTab ) const;
+
+ sal_uInt16 GetTabCount() const;
+
+ void LoadCachedValues();
+
+ svl::SharedStringPool& GetSharedStringPool();
+
+private:
+
+ std::vector< std::unique_ptr<XclImpSupbookTab> >
+ maSupbTabList; /// All sheet names of the document.
+ std::vector< std::unique_ptr<XclImpExtName> >
+ maExtNameList; /// All external names of the document.
+ OUString maXclUrl; /// URL of the external document (Excel mode).
+ XclSupbookType meType; /// Type of the supbook record.
+ sal_uInt16 mnSBTab; /// Current Excel sheet index from SUPBOOK for XCT/CRN records.
+};
+
+// Import link manager ========================================================
+
+namespace {
+
+/** Contains the SUPBOOK index and sheet indexes of an external link.
+ @descr It is possible to enter a formula like =SUM(Sheet1:Sheet3!A1),
+ therefore here occurs a sheet range. */
+struct XclImpXti
+{
+ sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
+ sal_uInt16 mnSBTabFirst; /// Index to the first sheet of the range in the SUPBOOK.
+ sal_uInt16 mnSBTabLast; /// Index to the last sheet of the range in the SUPBOOK.
+ explicit XclImpXti() : mnSupbook( SAL_MAX_UINT16 ), mnSBTabFirst( SAL_MAX_UINT16 ), mnSBTabLast( SAL_MAX_UINT16 ) {}
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpXti& rXti )
+{
+ rXti.mnSupbook = rStrm.ReaduInt16();
+ rXti.mnSBTabFirst = rStrm.ReaduInt16();
+ rXti.mnSBTabLast = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+}
+
+/** Implementation of the link manager. */
+class XclImpLinkManagerImpl : protected XclImpRoot
+{
+public:
+ explicit XclImpLinkManagerImpl( const XclImpRoot& rRoot );
+
+ /** Reads the EXTERNSHEET record. */
+ void ReadExternsheet( XclImpStream& rStrm );
+ /** Reads a SUPBOOK record. */
+ void ReadSupbook( XclImpStream& rStrm );
+ /** Reads an XCT record and appends it to the current SUPBOOK. */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record and appends it to the current SUPBOOK. */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns true, if the specified XTI entry contains an internal reference. */
+ bool IsSelfRef( sal_uInt16 nXtiIndex ) const;
+ /** Returns the Calc sheet index range of the specified XTI entry.
+ @return true = XTI data found, returned sheet index range is valid. */
+ bool GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab,
+ sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified external name or 0 on error. */
+ const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const;
+
+ /** Returns the absolute file URL of a supporting workbook specified by
+ the index. */
+ const OUString* GetSupbookUrl( sal_uInt16 nXtiIndex ) const;
+
+ OUString GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const;
+
+ /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified macro name or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const;
+
+private:
+ /** Returns the specified XTI (link entry from BIFF8 EXTERNSHEET record). */
+ const XclImpXti* GetXti( sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified SUPBOOK (external document). */
+ const XclImpSupbook* GetSupbook( sal_uInt16 nXtiIndex ) const;
+
+ void LoadCachedValues();
+
+private:
+ typedef std::vector< XclImpXti > XclImpXtiVector;
+
+ XclImpXtiVector maXtiList; /// List of all XTI structures.
+ std::vector< std::unique_ptr<XclImpSupbook> >
+ maSupbookList; /// List of external documents.
+};
+
+// *** Implementation ***
+
+// Excel sheet indexes ========================================================
+
+// original Excel sheet names -------------------------------------------------
+
+void XclImpTabInfo::AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab )
+{
+ maTabNames[ rXclTabName ] = nScTab;
+}
+
+void XclImpTabInfo::InsertScTab( SCTAB nScTab )
+{
+ for( auto& rEntry : maTabNames )
+ if( rEntry.second >= nScTab )
+ ++rEntry.second;
+}
+
+SCTAB XclImpTabInfo::GetScTabFromXclName( const OUString& rXclTabName ) const
+{
+ XclTabNameMap::const_iterator aIt = maTabNames.find( rXclTabName );
+ return (aIt != maTabNames.end()) ? aIt->second : SCTAB_INVALID;
+}
+
+// record creation order - TABID record ---------------------------------------
+
+void XclImpTabInfo::ReadTabid( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ rStrm.EnableDecryption();
+ std::size_t nReadCount = rStrm.GetRecLeft() / 2;
+ OSL_ENSURE( nReadCount <= 0xFFFF, "XclImpTabInfo::ReadTabid - record too long" );
+ maTabIdVec.clear();
+ maTabIdVec.reserve( nReadCount );
+ for( std::size_t nIndex = 0; rStrm.IsValid() && (nIndex < nReadCount); ++nIndex )
+ // zero index is not allowed in BIFF8, but it seems that it occurs in real life
+ maTabIdVec.push_back( rStrm.ReaduInt16() );
+ }
+}
+
+sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const
+{
+ sal_uInt16 nReturn = 0;
+ for( sal_uInt16 nValue : maTabIdVec )
+ {
+ if( nValue == nCreatedId )
+ return nReturn;
+ if( nValue <= nMaxTabId )
+ ++nReturn;
+ }
+ return 0;
+}
+
+// External names =============================================================
+
+XclImpExtName::MOper::MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm) :
+ mxCached(new ScMatrix(0,0))
+{
+ SCSIZE nLastCol = rStrm.ReaduInt8();
+ SCSIZE nLastRow = rStrm.ReaduInt16();
+
+ //assuming worst case scenario of nOp + one byte unistring len
+ const size_t nMinRecordSize = 2;
+ const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * (nLastCol+1));
+ if (nLastRow >= nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << nLastRow << " index claimed, truncating");
+ if (nMaxRows > 0)
+ nLastRow = nMaxRows-1;
+ else
+ return;
+ }
+
+ mxCached->Resize(nLastCol+1, nLastRow+1);
+ for (SCSIZE nRow = 0; nRow <= nLastRow; ++nRow)
+ {
+ for (SCSIZE nCol = 0; nCol <= nLastCol; ++nCol)
+ {
+ sal_uInt8 nOp;
+ nOp = rStrm.ReaduInt8();
+ switch (nOp)
+ {
+ case 0x01:
+ {
+ double fVal = rStrm.ReadDouble();
+ mxCached->PutDouble(fVal, nCol, nRow);
+ }
+ break;
+ case 0x02:
+ {
+ OUString aStr = rStrm.ReadUniString();
+ mxCached->PutString(rPool.intern(aStr), nCol, nRow);
+ }
+ break;
+ case 0x04:
+ {
+ bool bVal = rStrm.ReaduInt8();
+ mxCached->PutBoolean(bVal, nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ case 0x10:
+ {
+ sal_uInt8 nErr = rStrm.ReaduInt8();
+ // Map the error code from xls to calc.
+ mxCached->PutError(XclTools::GetScErrorCode(nErr), nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ default:
+ rStrm.Ignore(8);
+ }
+ }
+ }
+}
+
+const ScMatrix& XclImpExtName::MOper::GetCache() const
+{
+ return *mxCached;
+}
+
+XclImpExtName::XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv )
+ : mnStorageId(0)
+{
+ sal_uInt16 nFlags(0);
+ sal_uInt8 nLen(0);
+
+ nFlags = rStrm.ReaduInt16();
+ mnStorageId = rStrm.ReaduInt32();
+ nLen = rStrm.ReaduInt8();
+ maName = rStrm.ReadUniString( nLen );
+ if( ::get_flag( nFlags, EXC_EXTN_BUILTIN ) || !::get_flag( nFlags, EXC_EXTN_OLE_OR_DDE ) )
+ {
+ if( eSubType == XclSupbookType::Addin )
+ {
+ meType = xlExtAddIn;
+ maName = XclImpRoot::GetScAddInName( maName );
+ }
+ else if ( (eSubType == XclSupbookType::Eurotool) &&
+ maName.equalsIgnoreAsciiCase( "EUROCONVERT" ) )
+ meType = xlExtEuroConvert;
+ else
+ {
+ meType = xlExtName;
+ maName = ScfTools::ConvertToScDefinedName( maName );
+ }
+ }
+ else
+ {
+ meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE );
+ }
+
+ switch (meType)
+ {
+ case xlExtDDE:
+ if (rStrm.GetRecLeft() > 1)
+ mxDdeMatrix.reset(new XclImpCachedMatrix(rStrm));
+ break;
+ case xlExtName:
+ // TODO: For now, only global external names are supported. In future
+ // we should extend this to supporting per-sheet external names.
+ if (mnStorageId == 0 && pFormulaConv)
+ {
+ std::unique_ptr<ScTokenArray> pArray;
+ sal_uInt16 nFmlaLen;
+ nFmlaLen = rStrm.ReaduInt16();
+ std::vector<OUString> aTabNames;
+ sal_uInt16 nCount = rSupbook.GetTabCount();
+ aTabNames.reserve(nCount);
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ aTabNames.push_back(rSupbook.GetTabName(i));
+
+ pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames);
+ if (pArray)
+ mxArray = std::move( pArray );
+ }
+ break;
+ case xlExtOLE:
+ moMOper.emplace( rSupbook.GetSharedStringPool(), rStrm );
+ break;
+ default:
+ ;
+ }
+}
+
+XclImpExtName::~XclImpExtName()
+{
+}
+
+void XclImpExtName::CreateDdeData( ScDocument& rDoc, const OUString& rApplic, const OUString& rTopic ) const
+{
+ ScMatrixRef xResults;
+ if( mxDdeMatrix )
+ xResults = mxDdeMatrix->CreateScMatrix(rDoc.GetSharedStringPool());
+ rDoc.CreateDdeLink( rApplic, rTopic, maName, SC_DDE_DEFAULT, xResults );
+}
+
+void XclImpExtName::CreateExtNameData( const ScDocument& rDoc, sal_uInt16 nFileId ) const
+{
+ if (!mxArray)
+ return;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray);
+}
+
+namespace {
+
+/**
+ * Decompose the name into sheet name and range name. An OLE link name is
+ * always formatted like this [ !Sheet1!R1C1:R5C2 ] and it always uses R1C1
+ * notation.
+ */
+bool extractSheetAndRange(const OUString& rName, OUString& rSheet, OUString& rRange)
+{
+ sal_Int32 n = rName.getLength();
+ const sal_Unicode* p = rName.getStr();
+ OUStringBuffer aBuf;
+ bool bInSheet = true;
+ for (sal_Int32 i = 0; i < n; ++i, ++p)
+ {
+ if (i == 0)
+ {
+ // first character must be '!'.
+ if (*p != '!')
+ return false;
+ continue;
+ }
+
+ if (*p == '!')
+ {
+ // sheet name to range separator.
+ if (!bInSheet)
+ return false;
+ rSheet = aBuf.makeStringAndClear();
+ bInSheet = false;
+ continue;
+ }
+
+ aBuf.append(*p);
+ }
+
+ rRange = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+bool XclImpExtName::CreateOleData(const ScDocument& rDoc, const OUString& rUrl,
+ sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const
+{
+ if (!moMOper)
+ return false;
+
+ OUString aSheet, aRangeStr;
+ if (!extractSheetAndRange(maName, aSheet, aRangeStr))
+ return false;
+
+ ScRange aRange;
+ ScRefFlags nRes = aRange.ParseAny(aRangeStr, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+ if ((nRes & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ return false;
+
+ if (aRange.aStart.Tab() != aRange.aEnd.Tab())
+ // We don't support multi-sheet range for this.
+ return false;
+
+ const ScMatrix& rCache = moMOper->GetCache();
+ SCSIZE nC, nR;
+ rCache.GetDimensions(nC, nR);
+ if (!nC || !nR)
+ // cache matrix is empty.
+ return false;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(rUrl);
+ ScExternalRefCache::TableTypeRef xTab = pRefMgr->getCacheTable(nFileId, aSheet, true);
+ if (!xTab)
+ // cache table creation failed.
+ return false;
+
+ xTab->setWholeTableCached();
+ for (SCSIZE i = 0; i < nR; ++i)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ SCCOL nCol = aRange.aStart.Col() + j;
+ SCROW nRow = aRange.aStart.Row() + i;
+
+ ScMatrixValue aVal = rCache.Get(j, i);
+ switch (aVal.nType)
+ {
+ case ScMatValType::Boolean:
+ {
+ bool b = aVal.GetBoolean();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case ScMatValType::Value:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(aVal.fVal));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case ScMatValType::String:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(aVal.GetString()));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ rFileId = nFileId;
+ rTabName = aSheet;
+ rRange = aRange;
+ return true;
+}
+
+bool XclImpExtName::HasFormulaTokens() const
+{
+ return bool(mxArray);
+}
+
+// Cached external cells ======================================================
+
+XclImpCrn::XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) :
+ XclImpCachedValue( rStrm ),
+ maXclPos( rXclPos )
+{
+}
+
+// Sheet in an external document ==============================================
+
+XclImpSupbookTab::XclImpSupbookTab( OUString aTabName ) :
+ maTabName(std::move( aTabName ))
+{
+}
+
+void XclImpSupbookTab::ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos )
+{
+ XclImpCrnRef crnRef = std::make_shared<XclImpCrn>(rStrm, rXclPos);
+ maCrnList.push_back( crnRef );
+}
+
+void XclImpSupbookTab::LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable,
+ svl::SharedStringPool& rPool )
+{
+ if (maCrnList.empty())
+ return;
+
+ for (const auto& rxCrn : maCrnList)
+ {
+ const XclImpCrn* const pCrn = rxCrn.get();
+ const XclAddress& rAddr = pCrn->GetAddress();
+ switch (pCrn->GetType())
+ {
+ case EXC_CACHEDVAL_BOOL:
+ {
+ bool b = pCrn->GetBool();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ {
+ double f = pCrn->GetValue();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(f));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_ERROR:
+ {
+ double fError = XclTools::ErrorToDouble( pCrn->GetXclError() );
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(fError));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_STRING:
+ {
+ svl::SharedString aSS( rPool.intern( pCrn->GetString()));
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken( std::move(aSS) ));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+// External document (SUPBOOK) ================================================
+
+XclImpSupbook::XclImpSupbook( XclImpStream& rStrm ) :
+ XclImpRoot( rStrm.GetRoot() ),
+ meType( XclSupbookType::Unknown ),
+ mnSBTab( EXC_TAB_DELETED )
+{
+ sal_uInt16 nSBTabCnt;
+ nSBTabCnt = rStrm.ReaduInt16();
+
+ if( rStrm.GetRecLeft() == 2 )
+ {
+ switch( rStrm.ReaduInt16() )
+ {
+ case EXC_SUPB_SELF: meType = XclSupbookType::Self; break;
+ case EXC_SUPB_ADDIN: meType = XclSupbookType::Addin; break;
+ default: OSL_FAIL( "XclImpSupbook::XclImpSupbook - unknown special SUPBOOK type" );
+ }
+ return;
+ }
+
+ OUString aEncUrl( rStrm.ReadUniString() );
+ bool bSelf = false;
+ XclImpUrlHelper::DecodeUrl( maXclUrl, bSelf, GetRoot(), aEncUrl );
+
+ if( maXclUrl.equalsIgnoreAsciiCase( "\010EUROTOOL.XLA" ) )
+ {
+ meType = XclSupbookType::Eurotool;
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) );
+ }
+ else if( nSBTabCnt )
+ {
+ meType = XclSupbookType::Extern;
+
+ //assuming all empty strings with just len header of 0
+ const size_t nMinRecordSize = sizeof(sal_Int16);
+ const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
+ if (nSBTabCnt > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nSBTabCnt << " claimed, truncating");
+ nSBTabCnt = nMaxRecords;
+ }
+
+ for( sal_uInt16 nSBTab = 0; nSBTab < nSBTabCnt; ++nSBTab )
+ {
+ OUString aTabName( rStrm.ReadUniString() );
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( aTabName ) );
+ }
+ }
+ else
+ {
+ meType = XclSupbookType::Special;
+ // create dummy list entry
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) );
+ }
+}
+
+void XclImpSupbook::ReadXct( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 2 );
+ mnSBTab = rStrm.ReaduInt16();
+}
+
+void XclImpSupbook::ReadCrn( XclImpStream& rStrm )
+{
+ if (mnSBTab >= maSupbTabList.size())
+ return;
+ XclImpSupbookTab& rSbTab = *maSupbTabList[mnSBTab];
+ sal_uInt8 nXclColLast, nXclColFirst;
+ sal_uInt16 nXclRow;
+ nXclColLast = rStrm.ReaduInt8();
+ nXclColFirst = rStrm.ReaduInt8();
+ nXclRow = rStrm.ReaduInt16();
+
+ for( sal_uInt8 nXclCol = nXclColFirst; (nXclCol <= nXclColLast) && (rStrm.GetRecLeft() > 1); ++nXclCol )
+ rSbTab.ReadCrn( rStrm, XclAddress( nXclCol, nXclRow ) );
+}
+
+void XclImpSupbook::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ maExtNameList.push_back( std::make_unique<XclImpExtName>( *this, rStrm, meType, pFormulaConv ) );
+}
+
+const XclImpExtName* XclImpSupbook::GetExternName( sal_uInt16 nXclIndex ) const
+{
+ if (nXclIndex == 0)
+ {
+ SAL_WARN("sc", "XclImpSupbook::GetExternName - index must be >0");
+ return nullptr;
+ }
+ if (meType == XclSupbookType::Self || nXclIndex > maExtNameList.size())
+ return nullptr;
+ return maExtNameList[nXclIndex-1].get();
+}
+
+bool XclImpSupbook::GetLinkData( OUString& rApplic, OUString& rTopic ) const
+{
+ return (meType == XclSupbookType::Special) && XclImpUrlHelper::DecodeLink( rApplic, rTopic, maXclUrl );
+}
+
+OUString XclImpSupbook::GetMacroName( sal_uInt16 nXclNameIdx ) const
+{
+ OSL_ENSURE( nXclNameIdx > 0, "XclImpSupbook::GetMacroName - index must be >0" );
+ const XclImpName* pName = (meType == XclSupbookType::Self) ? GetNameManager().GetName( nXclNameIdx ) : nullptr;
+ return (pName && pName->IsVBName()) ? pName->GetScName() : OUString();
+}
+
+OUString XclImpSupbook::GetTabName( sal_uInt16 nXtiTab ) const
+{
+ if (nXtiTab >= maSupbTabList.size())
+ return OUString();
+ return maSupbTabList[nXtiTab]->GetTabName();
+}
+
+sal_uInt16 XclImpSupbook::GetTabCount() const
+{
+ return ulimit_cast<sal_uInt16>(maSupbTabList.size());
+}
+
+void XclImpSupbook::LoadCachedValues()
+{
+ if (meType != XclSupbookType::Extern || GetExtDocOptions().GetDocSettings().mnLinkCnt > 0 || !GetDocShell())
+ return;
+
+ OUString aAbsUrl( ScGlobal::GetAbsDocName(maXclUrl, GetDocShell()) );
+
+ ScExternalRefManager* pRefMgr = GetRoot().GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aAbsUrl);
+
+ for (auto& rxTab : maSupbTabList)
+ {
+ const OUString& rTabName = rxTab->GetTabName();
+ ScExternalRefCache::TableTypeRef pCacheTable = pRefMgr->getCacheTable(nFileId, rTabName, true);
+ rxTab->LoadCachedValues( pCacheTable, GetSharedStringPool());
+ pCacheTable->setWholeTableCached();
+ }
+}
+
+svl::SharedStringPool& XclImpSupbook::GetSharedStringPool()
+{
+ return GetDoc().GetSharedStringPool();
+}
+
+// Import link manager ========================================================
+
+XclImpLinkManagerImpl::XclImpLinkManagerImpl( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpLinkManagerImpl::ReadExternsheet( XclImpStream& rStrm )
+{
+ sal_uInt16 nXtiCount;
+ nXtiCount = rStrm.ReaduInt16();
+ OSL_ENSURE( static_cast< std::size_t >( nXtiCount * 6 ) == rStrm.GetRecLeft(), "XclImpLinkManagerImpl::ReadExternsheet - invalid count" );
+ nXtiCount = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nXtiCount, rStrm.GetRecLeft() / 6 ) );
+
+ /* #i104057# A weird external XLS generator writes multiple EXTERNSHEET
+ records instead of only one as expected. Surprisingly, Excel seems to
+ insert the entries of the second record before the entries of the first
+ record. */
+ XclImpXtiVector aNewEntries( nXtiCount );
+ for( auto& rNewEntry : aNewEntries )
+ {
+ if (!rStrm.IsValid())
+ break;
+ rStrm >> rNewEntry;
+ }
+ maXtiList.insert( maXtiList.begin(), aNewEntries.begin(), aNewEntries.end() );
+
+ LoadCachedValues();
+}
+
+void XclImpLinkManagerImpl::ReadSupbook( XclImpStream& rStrm )
+{
+ maSupbookList.push_back( std::make_unique<XclImpSupbook>( rStrm ) );
+}
+
+void XclImpLinkManagerImpl::ReadXct( XclImpStream& rStrm )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadXct( rStrm );
+}
+
+void XclImpLinkManagerImpl::ReadCrn( XclImpStream& rStrm )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadCrn( rStrm );
+}
+
+void XclImpLinkManagerImpl::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadExternname( rStrm, pFormulaConv );
+}
+
+bool XclImpLinkManagerImpl::IsSelfRef( sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook && (pSupbook->GetType() == XclSupbookType::Self);
+}
+
+bool XclImpLinkManagerImpl::GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
+{
+ if( const XclImpXti* pXti = GetXti( nXtiIndex ) )
+ {
+ if (!maSupbookList.empty() && (pXti->mnSupbook < maSupbookList.size()) )
+ {
+ rnFirstScTab = pXti->mnSBTabFirst;
+ rnLastScTab = pXti->mnSBTabLast;
+ return true;
+ }
+ }
+ return false;
+}
+
+const XclImpExtName* XclImpLinkManagerImpl::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook ? pSupbook->GetExternName( nExtName ) : nullptr;
+}
+
+const OUString* XclImpLinkManagerImpl::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* p = GetSupbook( nXtiIndex );
+ if (!p)
+ return nullptr;
+ return &p->GetXclUrl();
+}
+
+OUString XclImpLinkManagerImpl::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
+{
+ const XclImpSupbook* p = GetSupbook(nXti);
+ return p ? p->GetTabName(nXtiTab) : OUString();
+}
+
+bool XclImpLinkManagerImpl::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook && pSupbook->GetLinkData( rApplic, rTopic );
+}
+
+OUString XclImpLinkManagerImpl::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nExtSheet );
+ return pSupbook ? pSupbook->GetMacroName( nExtName ) : OUString();
+}
+
+const XclImpXti* XclImpLinkManagerImpl::GetXti( sal_uInt16 nXtiIndex ) const
+{
+ return (nXtiIndex < maXtiList.size()) ? &maXtiList[ nXtiIndex ] : nullptr;
+}
+
+const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( sal_uInt16 nXtiIndex ) const
+{
+ if ( maSupbookList.empty() )
+ return nullptr;
+ const XclImpXti* pXti = GetXti( nXtiIndex );
+ if (!pXti || pXti->mnSupbook >= maSupbookList.size())
+ return nullptr;
+ return maSupbookList.at( pXti->mnSupbook ).get();
+}
+
+void XclImpLinkManagerImpl::LoadCachedValues()
+{
+ // Read all CRN records which can be accessed via XclImpSupbook, and store
+ // the cached values to the external reference manager.
+ for (auto& rxSupbook : maSupbookList)
+ rxSupbook->LoadCachedValues();
+}
+
+XclImpLinkManager::XclImpLinkManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mxImpl( new XclImpLinkManagerImpl( rRoot ) )
+{
+}
+
+XclImpLinkManager::~XclImpLinkManager()
+{
+}
+
+void XclImpLinkManager::ReadExternsheet( XclImpStream& rStrm )
+{
+ mxImpl->ReadExternsheet( rStrm );
+}
+
+void XclImpLinkManager::ReadSupbook( XclImpStream& rStrm )
+{
+ mxImpl->ReadSupbook( rStrm );
+}
+
+void XclImpLinkManager::ReadXct( XclImpStream& rStrm )
+{
+ mxImpl->ReadXct( rStrm );
+}
+
+void XclImpLinkManager::ReadCrn( XclImpStream& rStrm )
+{
+ mxImpl->ReadCrn( rStrm );
+}
+
+void XclImpLinkManager::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ mxImpl->ReadExternname( rStrm, pFormulaConv );
+}
+
+bool XclImpLinkManager::IsSelfRef( sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->IsSelfRef( nXtiIndex );
+}
+
+bool XclImpLinkManager::GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetScTabRange( rnFirstScTab, rnLastScTab, nXtiIndex );
+}
+
+const XclImpExtName* XclImpLinkManager::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
+{
+ return mxImpl->GetExternName( nXtiIndex, nExtName );
+}
+
+const OUString* XclImpLinkManager::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetSupbookUrl(nXtiIndex);
+}
+
+OUString XclImpLinkManager::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
+{
+ return mxImpl->GetSupbookTabName(nXti, nXtiTab);
+}
+
+bool XclImpLinkManager::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetLinkData( rApplic, rTopic, nXtiIndex );
+}
+
+OUString XclImpLinkManager::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
+{
+ return mxImpl->GetMacroName( nExtSheet, nExtName );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiname.cxx b/sc/source/filter/excel/xiname.cxx
new file mode 100644
index 0000000000..cd9e92dfaa
--- /dev/null
+++ b/sc/source/filter/excel/xiname.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 .
+ */
+
+#include <xiname.hxx>
+#include <xlname.hxx>
+#include <rangenam.hxx>
+#include <xistream.hxx>
+#include <excform.hxx>
+#include <excimp8.hxx>
+#include <scextopt.hxx>
+#include <document.hxx>
+
+// *** Implementation ***
+
+XclImpName::TokenStrmData::TokenStrmData( XclImpStream& rStrm ) :
+ mrStrm(rStrm), mnStrmPos(0), mnStrmSize(0) {}
+
+XclImpName::XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx ) :
+ XclImpRoot( rStrm.GetRoot() ),
+ mpScData( nullptr ),
+ mnScTab( SCTAB_MAX ),
+ meNameType( ScRangeData::Type::Name ),
+ mnXclTab( EXC_NAME_GLOBAL ),
+ mnNameIndex( nXclNameIdx ),
+ mbVBName( false ),
+ mbMacro( false )
+{
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+
+ // 1) *** read data from stream *** ---------------------------------------
+
+ sal_uInt16 nFlags = 0, nFmlaSize = 0, nExtSheet = EXC_NAME_GLOBAL;
+ sal_uInt8 nNameLen = 0;
+ sal_Unicode cBuiltIn(EXC_BUILTIN_UNKNOWN); /// Excel built-in name index.
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ {
+ sal_uInt8 nFlagsBiff2;
+ nFlagsBiff2 = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt8();
+ ::set_flag( nFlags, EXC_NAME_FUNC, ::get_flag( nFlagsBiff2, EXC_NAME2_FUNC ) );
+ }
+ break;
+
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ {
+ nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt16();
+ }
+ break;
+
+ case EXC_BIFF5:
+ case EXC_BIFF8:
+ {
+ nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt16();
+ nExtSheet = rStrm.ReaduInt16();
+ mnXclTab = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ }
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+
+ if( GetBiff() <= EXC_BIFF5 )
+ maXclName = rStrm.ReadRawByteString( nNameLen );
+ else
+ maXclName = rStrm.ReadUniString( nNameLen );
+
+ // 2) *** convert sheet index and name *** --------------------------------
+
+ // functions and VBA
+ bool bFunction = ::get_flag( nFlags, EXC_NAME_FUNC );
+ mbVBName = ::get_flag( nFlags, EXC_NAME_VB );
+ mbMacro = ::get_flag( nFlags, EXC_NAME_PROC );
+
+ // get built-in name, or convert characters invalid in Calc
+ bool bBuiltIn = ::get_flag( nFlags, EXC_NAME_BUILTIN );
+
+ // special case for BIFF5 filter range - name appears as plain text without built-in flag
+ if( (GetBiff() == EXC_BIFF5) && (maXclName == XclTools::GetXclBuiltInDefName(EXC_BUILTIN_FILTERDATABASE)) )
+ {
+ bBuiltIn = true;
+ maXclName = OUStringChar(EXC_BUILTIN_FILTERDATABASE);
+ }
+
+ // convert Excel name to Calc name
+ if( mbVBName )
+ {
+ // VB macro name
+ maScName = maXclName;
+ }
+ else if( bBuiltIn )
+ {
+ // built-in name
+ if( !maXclName.isEmpty() )
+ cBuiltIn = maXclName[0];
+ if( cBuiltIn == '?' ) // NUL character is imported as '?'
+ cBuiltIn = '\0';
+ maScName = XclTools::GetBuiltInDefName( cBuiltIn );
+ }
+ else
+ {
+ // any other name
+ maScName = ScfTools::ConvertToScDefinedName( maXclName );
+ }
+
+ // add index for local names
+ if( mnXclTab != EXC_NAME_GLOBAL )
+ {
+ sal_uInt16 nUsedTab = (GetBiff() == EXC_BIFF8) ? mnXclTab : nExtSheet;
+ // TODO: may not work for BIFF5, handle skipped sheets (all BIFF)
+ mnScTab = static_cast< SCTAB >( nUsedTab - 1 );
+ }
+
+ // 3) *** convert the name definition formula *** -------------------------
+
+ rFmlaConv.Reset();
+ std::unique_ptr<ScTokenArray> pTokArr;
+
+ if( ::get_flag( nFlags, EXC_NAME_BIG ) )
+ {
+ // special, unsupported name
+ pTokArr = rFmlaConv.GetDummy();
+ }
+ else if( bBuiltIn )
+ {
+ SCTAB const nLocalTab = (mnXclTab == EXC_NAME_GLOBAL) ? SCTAB_MAX : (mnXclTab - 1);
+
+ // --- print ranges or title ranges ---
+ rStrm.PushPosition();
+ switch( cBuiltIn )
+ {
+ case EXC_BUILTIN_PRINTAREA:
+ if( rFmlaConv.Convert( GetPrintAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvErr::OK )
+ meNameType |= ScRangeData::Type::PrintArea;
+ break;
+ case EXC_BUILTIN_PRINTTITLES:
+ if( rFmlaConv.Convert( GetTitleAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvErr::OK )
+ meNameType |= ScRangeData::Type::ColHeader | ScRangeData::Type::RowHeader;
+ break;
+ }
+ rStrm.PopPosition();
+
+ // --- name formula ---
+ // JEG : double check this. It is clearly false for normal names
+ // but some of the builtins (sheettitle?) might be able to handle arrays
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, false, FT_RangeName );
+
+ // --- auto or advanced filter ---
+ if ((GetBiff() == EXC_BIFF8) && pTokArr)
+ {
+ ScRange aRange;
+ if (pTokArr->IsReference(aRange, ScAddress()))
+ {
+ switch( cBuiltIn )
+ {
+ case EXC_BUILTIN_FILTERDATABASE:
+ GetFilterManager().Insert( &GetOldRoot(), aRange);
+ break;
+ case EXC_BUILTIN_CRITERIA:
+ GetFilterManager().AddAdvancedRange( aRange );
+ meNameType |= ScRangeData::Type::Criteria;
+ break;
+ case EXC_BUILTIN_EXTRACT:
+ if (pTokArr->IsValidReference(aRange, ScAddress()))
+ GetFilterManager().AddExtractPos( aRange );
+ break;
+ }
+ }
+ }
+ }
+ else if( nFmlaSize > 0 )
+ {
+ // Regular defined name. We need to convert the tokens after all the
+ // names have been registered (for cross-referenced names).
+ mpTokensData.reset(new TokenStrmData(rStrm));
+ mpTokensData->mnStrmPos = rStrm.GetSvStreamPos();
+ rStrm.StorePosition(mpTokensData->maStrmPos);
+ mpTokensData->mnStrmSize = nFmlaSize;
+ }
+
+ if (pTokArr && !bFunction && !mbVBName)
+ InsertName(pTokArr.get());
+}
+
+void XclImpName::ConvertTokens()
+{
+ if (!mpTokensData)
+ return;
+
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+ rFmlaConv.Reset();
+ std::unique_ptr<ScTokenArray> pArray;
+
+ XclImpStreamPos aOldPos;
+ XclImpStream& rStrm = mpTokensData->mrStrm;
+ rStrm.StorePosition(aOldPos);
+ rStrm.RestorePosition(mpTokensData->maStrmPos);
+ rFmlaConv.Convert(pArray, rStrm, mpTokensData->mnStrmSize, true, FT_RangeName);
+ rStrm.RestorePosition(aOldPos);
+
+ if (pArray)
+ InsertName(pArray.get());
+
+ mpTokensData.reset();
+}
+
+void XclImpName::InsertName(const ScTokenArray* pArray)
+{
+ // create the Calc name data
+ ScRangeData* pData = new ScRangeData(GetDoc(), maScName, *pArray, ScAddress(), meNameType);
+ pData->GuessPosition(); // calculate base position for relative refs
+ pData->SetIndex( mnNameIndex ); // used as unique identifier in formulas
+ if (mnXclTab == EXC_NAME_GLOBAL)
+ {
+ if (!GetDoc().GetRangeName()->insert(pData))
+ pData = nullptr;
+ }
+ else
+ {
+ ScRangeName* pLocalNames = GetDoc().GetRangeName(mnScTab);
+ if (pLocalNames)
+ {
+ if (!pLocalNames->insert(pData))
+ pData = nullptr;
+ }
+ else
+ {
+ delete pData;
+ pData = nullptr;
+ }
+
+ if (GetBiff() == EXC_BIFF8 && pData)
+ {
+ ScRange aRange;
+ // discard deleted ranges ( for the moment at least )
+ if ( pData->IsValidReference( aRange ) )
+ {
+ GetExtDocOptions().GetOrCreateTabSettings( mnXclTab );
+ }
+ }
+ }
+ if (pData)
+ {
+ GetDoc().CheckLinkFormulaNeedingCheck( *pData->GetCode());
+ mpScData = pData; // cache for later use
+ }
+}
+
+XclImpNameManager::XclImpNameManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpNameManager::ReadName( XclImpStream& rStrm )
+{
+ size_t nCount = maNameList.size();
+ if( nCount < 0xFFFF )
+ maNameList.push_back( std::make_unique<XclImpName>( rStrm, static_cast< sal_uInt16 >( nCount + 1 ) ) );
+}
+
+const XclImpName* XclImpNameManager::FindName( std::u16string_view rXclName, SCTAB nScTab ) const
+{
+ const XclImpName* pGlobalName = nullptr; // a found global name
+ const XclImpName* pLocalName = nullptr; // a found local name
+ // If a duplicate name is seen by ScRangeName::insert then the existing
+ // name is erased and the new one inserted, so in the case of duplicates
+ // the last one seen is valid and the others invalid. So do this lookup in
+ // reverse in order to return the XclImpName* that references the valid
+ // entry (see tdf#44831 for the insert behavior and 'forum-mso-en4-30276.xls'
+ // for an example of this problem)
+ for (auto itName = maNameList.rbegin(); itName != maNameList.rend(); ++itName)
+ {
+ const auto& rxName = *itName;
+ if( rxName->GetXclName() == rXclName )
+ {
+ if( rxName->GetScTab() == nScTab )
+ pLocalName = rxName.get();
+ else if( rxName->IsGlobal() )
+ pGlobalName = rxName.get();
+ }
+
+ if (pLocalName)
+ break;
+ }
+ return pLocalName ? pLocalName : pGlobalName;
+}
+
+const XclImpName* XclImpNameManager::GetName( sal_uInt16 nXclNameIdx ) const
+{
+ OSL_ENSURE( nXclNameIdx > 0, "XclImpNameManager::GetName - index must be >0" );
+ return ( nXclNameIdx <= 0 || nXclNameIdx > maNameList.size() ) ? nullptr : maNameList.at( nXclNameIdx - 1 ).get();
+}
+
+void XclImpNameManager::ConvertAllTokens()
+{
+ for (auto& rxName : maNameList)
+ rxName->ConvertTokens();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xipage.cxx b/sc/source/filter/excel/xipage.cxx
new file mode 100644
index 0000000000..c06308ba78
--- /dev/null
+++ b/sc/source/filter/excel/xipage.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xipage.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/graph.hxx>
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <document.hxx>
+#include <stlsheet.hxx>
+#include <attrib.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiescher.hxx>
+
+// Page settings ==============================================================
+
+XclImpPageSettings::XclImpPageSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ Initialize();
+}
+
+void XclImpPageSettings::Initialize()
+{
+ maData.SetDefaults();
+ mbValidPaper = false;
+}
+
+void XclImpPageSettings::ReadSetup( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF4 );
+ if( GetBiff() < EXC_BIFF4 )
+ return;
+
+ // BIFF4 - BIFF8
+ sal_uInt16 nFlags;
+ maData.mnPaperSize = rStrm.ReaduInt16();
+ maData.mnScaling = rStrm.ReaduInt16();
+ maData.mnStartPage = rStrm.ReaduInt16();
+ maData.mnFitToWidth = rStrm.ReaduInt16();
+ maData.mnFitToHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ mbValidPaper = maData.mbValid = !::get_flag( nFlags, EXC_SETUP_INVALID );
+ maData.mbPrintInRows = ::get_flag( nFlags, EXC_SETUP_INROWS );
+ maData.mbPortrait = ::get_flag( nFlags, EXC_SETUP_PORTRAIT );
+ maData.mbBlackWhite = ::get_flag( nFlags, EXC_SETUP_BLACKWHITE );
+ maData.mbManualStart = true;
+
+ // new in BIFF5 - BIFF8
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ maData.mnHorPrintRes = rStrm.ReaduInt16();
+ maData.mnVerPrintRes = rStrm.ReaduInt16();
+ maData.mfHeaderMargin = rStrm.ReadDouble();
+ maData.mfFooterMargin = rStrm.ReadDouble();
+ maData.mnCopies = rStrm.ReaduInt16();
+
+ maData.mbDraftQuality = ::get_flag( nFlags, EXC_SETUP_DRAFT );
+ maData.mbPrintNotes = ::get_flag( nFlags, EXC_SETUP_PRINTNOTES );
+ maData.mbManualStart = ::get_flag( nFlags, EXC_SETUP_STARTPAGE );
+ }
+}
+
+void XclImpPageSettings::ReadMargin( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_LEFTMARGIN: maData.mfLeftMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_RIGHTMARGIN: maData.mfRightMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_TOPMARGIN: maData.mfTopMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_BOTTOMMARGIN: maData.mfBottomMargin = rStrm.ReadDouble(); break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadMargin - unknown record" );
+ }
+}
+
+void XclImpPageSettings::ReadCenter( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 ); // read it anyway
+ bool bCenter = (rStrm.ReaduInt16() != 0);
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HCENTER: maData.mbHorCenter = bCenter; break;
+ case EXC_ID_VCENTER: maData.mbVerCenter = bCenter; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadCenter - unknown record" );
+ }
+}
+
+void XclImpPageSettings::ReadHeaderFooter( XclImpStream& rStrm )
+{
+ OUString aString;
+ if( rStrm.GetRecLeft() )
+ aString = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
+
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HEADER: maData.maHeader = aString; break;
+ case EXC_ID_FOOTER: maData.maFooter = aString; break;
+ case EXC_ID_HEADER_EVEN: maData.maHeaderEven = aString; break;
+ case EXC_ID_FOOTER_EVEN: maData.maFooterEven = aString; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadHeaderFooter - unknown record" );
+ }
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ if (maData.maHeader.getLength() > 10)
+ maData.maHeader = maData.maHeader.copy(0, 10);
+ if (maData.maFooter.getLength() > 10)
+ maData.maFooter = maData.maFooter.copy(0, 10);
+ if (maData.maHeaderEven.getLength() > 10)
+ maData.maHeaderEven = maData.maHeaderEven.copy(0, 10);
+ if (maData.maFooterEven.getLength() > 10)
+ maData.maFooterEven = maData.maFooterEven.copy(0, 10);
+ }
+}
+
+void XclImpPageSettings::ReadPageBreaks( XclImpStream& rStrm )
+{
+ ScfUInt16Vec* pVec = nullptr;
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HORPAGEBREAKS: pVec = &maData.maHorPageBreaks; break;
+ case EXC_ID_VERPAGEBREAKS: pVec = &maData.maVerPageBreaks; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadPageBreaks - unknown record" );
+ }
+
+ if( !pVec )
+ return;
+
+ bool bIgnore = GetBiff() == EXC_BIFF8; // ignore start/end columns or rows in BIFF8
+
+ sal_uInt16 nCount, nBreak;
+ nCount = rStrm.ReaduInt16();
+ pVec->clear();
+ pVec->reserve( nCount );
+
+ while( nCount-- )
+ {
+ nBreak = rStrm.ReaduInt16();
+ if( nBreak )
+ pVec->push_back( nBreak );
+ if( bIgnore )
+ rStrm.Ignore( 4 );
+ }
+}
+
+void XclImpPageSettings::ReadPrintHeaders( XclImpStream& rStrm )
+{
+ maData.mbPrintHeadings = (rStrm.ReaduInt16() != 0);
+}
+
+void XclImpPageSettings::ReadPrintGridLines( XclImpStream& rStrm )
+{
+ maData.mbPrintGrid = (rStrm.ReaduInt16() != 0);
+}
+
+void XclImpPageSettings::ReadImgData( XclImpStream& rStrm )
+{
+ Graphic aGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+ if( aGraphic.GetType() != GraphicType::NONE )
+ maData.mxBrushItem.reset( new SvxBrushItem( aGraphic, GPOS_TILED, ATTR_BACKGROUND ) );
+}
+
+void XclImpPageSettings::SetPaperSize( sal_uInt16 nXclPaperSize, bool bPortrait )
+{
+ maData.mnPaperSize = nXclPaperSize;
+ maData.mbPortrait = bPortrait;
+ mbValidPaper = true;
+}
+
+namespace {
+
+void lclPutMarginItem( SfxItemSet& rItemSet, sal_uInt16 nRecId, double fMarginInch )
+{
+ sal_uInt16 nMarginTwips = XclTools::GetTwipsFromInch( fMarginInch );
+ switch( nRecId )
+ {
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN:
+ {
+ SvxULSpaceItem aItem( rItemSet.Get( ATTR_ULSPACE ) );
+ if( nRecId == EXC_ID_TOPMARGIN )
+ aItem.SetUpperValue( nMarginTwips );
+ else
+ aItem.SetLowerValue( nMarginTwips );
+ rItemSet.Put( aItem );
+ }
+ break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ {
+ SvxLRSpaceItem aItem( rItemSet.Get( ATTR_LRSPACE ) );
+ if( nRecId == EXC_ID_LEFTMARGIN )
+ aItem.SetLeftValue( nMarginTwips );
+ else
+ aItem.SetRightValue( nMarginTwips );
+ rItemSet.Put( aItem );
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpPageSettings::SetMarginItem - unknown record id" );
+ }
+}
+
+} // namespace
+
+void XclImpPageSettings::Finalize()
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // *** create page style sheet ***
+
+ OUString aStyleName;
+ OUString aTableName;
+ if( GetDoc().GetName( nScTab, aTableName ) )
+ aStyleName = "PageStyle_" + aTableName;
+ else
+ aStyleName = "PageStyle_" + OUString::number(static_cast<sal_Int32>(nScTab+1));
+
+ ScStyleSheet& rStyleSheet = ScfTools::MakePageStyleSheet(
+ GetStyleSheetPool(), aStyleName, false);
+
+ SfxItemSet& rItemSet = rStyleSheet.GetItemSet();
+
+ // *** page settings ***
+
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_TOPDOWN, !maData.mbPrintInRows ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HORCENTER, maData.mbHorCenter ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_VERCENTER, maData.mbVerCenter ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HEADERS, maData.mbPrintHeadings ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_GRID, maData.mbPrintGrid ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_NOTES, maData.mbPrintNotes ), true );
+
+ sal_uInt16 nStartPage = maData.mbManualStart ? maData.mnStartPage : 0;
+ ScfTools::PutItem( rItemSet, SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, nStartPage ), true );
+
+ if( maData.mxBrushItem )
+ rItemSet.Put( *maData.mxBrushItem );
+
+ if( mbValidPaper )
+ {
+ SvxPageItem aPageItem( rItemSet.Get( ATTR_PAGE ) );
+ aPageItem.SetLandscape( !maData.mbPortrait );
+ rItemSet.Put( aPageItem );
+ ScfTools::PutItem( rItemSet, SvxSizeItem( ATTR_PAGE_SIZE, maData.GetScPaperSize() ), true );
+ }
+
+ if( maData.mbFitToPages )
+ rItemSet.Put( ScPageScaleToItem( maData.mnFitToWidth, maData.mnFitToHeight ) );
+ else if( maData.mbValid )
+ rItemSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, maData.mnScaling ) );
+
+ // *** margin preparations ***
+
+ double fLeftMargin = maData.mfLeftMargin;
+ double fRightMargin = maData.mfRightMargin;
+ double fTopMargin = maData.mfTopMargin;
+ double fBottomMargin = maData.mfBottomMargin;
+ // distances between header/footer and page area
+ double fHeaderHeight = 0.0;
+ double fHeaderDist = 0.0;
+ double fFooterHeight = 0.0;
+ double fFooterDist = 0.0;
+ // in Calc, "header/footer left/right margin" is X distance between header/footer and page margin
+ double fHdrLeftMargin = maData.mfHdrLeftMargin - maData.mfLeftMargin;
+ double fHdrRightMargin = maData.mfHdrRightMargin - maData.mfRightMargin;
+ double fFtrLeftMargin = maData.mfFtrLeftMargin - maData.mfLeftMargin;
+ double fFtrRightMargin = maData.mfFtrRightMargin - maData.mfRightMargin;
+
+ // *** header and footer ***
+
+ XclImpHFConverter aHFConv( GetRoot() );
+
+ // header
+ bool bHasHeader = !maData.maHeader.isEmpty();
+ SvxSetItem aHdrSetItem( rItemSet.Get( ATTR_PAGE_HEADERSET ) );
+ SfxItemSet& rHdrItemSet = aHdrSetItem.GetItemSet();
+ rHdrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasHeader ) );
+ if( bHasHeader )
+ {
+ aHFConv.ParseString( maData.maHeader );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERLEFT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERRIGHT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERFIRST );
+ // #i23296# In Calc, "top margin" is distance to header
+ fTopMargin = maData.mfHeaderMargin;
+ // Calc uses distance between header and sheet data area
+ fHeaderHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() );
+ fHeaderDist = maData.mfTopMargin - maData.mfHeaderMargin - fHeaderHeight;
+ }
+ if( fHeaderDist < 0.0 )
+ {
+ /* #i23296# Header overlays sheet data:
+ -> set fixed header height to get correct sheet data position. */
+ ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true );
+ // shrink header height
+ tools::Long nHdrHeight = XclTools::GetTwipsFromInch( fHeaderHeight + fHeaderDist );
+ ScfTools::PutItem( rHdrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nHdrHeight ) ), true );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, 0.0 );
+ }
+ else
+ {
+ // use dynamic header height
+ ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, fHeaderDist );
+ }
+ lclPutMarginItem( rHdrItemSet, EXC_ID_LEFTMARGIN, fHdrLeftMargin );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_RIGHTMARGIN, fHdrRightMargin );
+ rItemSet.Put( aHdrSetItem );
+
+ // footer
+ bool bHasFooter = !maData.maFooter.isEmpty();
+ SvxSetItem aFtrSetItem( rItemSet.Get( ATTR_PAGE_FOOTERSET ) );
+ SfxItemSet& rFtrItemSet = aFtrSetItem.GetItemSet();
+ rFtrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasFooter ) );
+ if( bHasFooter )
+ {
+ aHFConv.ParseString( maData.maFooter );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERLEFT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERRIGHT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERFIRST );
+ // #i23296# In Calc, "bottom margin" is distance to footer
+ fBottomMargin = maData.mfFooterMargin;
+ // Calc uses distance between footer and sheet data area
+ fFooterHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() );
+ fFooterDist = maData.mfBottomMargin - maData.mfFooterMargin - fFooterHeight;
+ }
+ if( fFooterDist < 0.0 )
+ {
+ /* #i23296# Footer overlays sheet data:
+ -> set fixed footer height to get correct sheet data end position. */
+ ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true );
+ // shrink footer height
+ tools::Long nFtrHeight = XclTools::GetTwipsFromInch( fFooterHeight + fFooterDist );
+ ScfTools::PutItem( rFtrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nFtrHeight ) ), true );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, 0.0 );
+ }
+ else
+ {
+ // use dynamic footer height
+ ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, fFooterDist );
+ }
+ lclPutMarginItem( rFtrItemSet, EXC_ID_LEFTMARGIN, fFtrLeftMargin );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_RIGHTMARGIN, fFtrRightMargin );
+ rItemSet.Put( aFtrSetItem );
+
+ // *** set final margins ***
+
+ lclPutMarginItem( rItemSet, EXC_ID_LEFTMARGIN, fLeftMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_RIGHTMARGIN, fRightMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_TOPMARGIN, fTopMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_BOTTOMMARGIN, fBottomMargin );
+
+ // *** put style sheet into document ***
+
+ rDoc.SetPageStyle( nScTab, rStyleSheet.GetName() );
+
+ // *** page breaks ***
+
+ for( const auto& rHorPageBreak : maData.maHorPageBreaks )
+ {
+ SCROW nScRow = static_cast< SCROW >( rHorPageBreak );
+ if( nScRow <= rDoc.MaxRow() )
+ rDoc.SetRowBreak(nScRow, nScTab, false, true);
+ }
+
+ for( const auto& rVerPageBreak : maData.maVerPageBreaks )
+ {
+ SCCOL nScCol = static_cast< SCCOL >( rVerPageBreak );
+ if( nScCol <= rDoc.MaxCol() )
+ rDoc.SetColBreak(nScCol, nScTab, false, true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xipivot.cxx b/sc/source/filter/excel/xipivot.cxx
new file mode 100644
index 0000000000..ac3ebaa96b
--- /dev/null
+++ b/sc/source/filter/excel/xipivot.cxx
@@ -0,0 +1,1736 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xipivot.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+
+#include <tools/datetime.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sot/storage.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dpoutputgeometry.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <xiescher.hxx>
+
+//TODO ExcelToSc usage
+#include <excform.hxx>
+#include <documentimport.hxx>
+
+#include <vector>
+
+using namespace com::sun::star;
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
+using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+using ::com::sun::star::sheet::DataPilotFieldReference;
+using ::std::vector;
+
+// Pivot cache
+
+XclImpPCItem::XclImpPCItem( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break;
+ case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break;
+ case EXC_ID_SXERROR: ReadSxerror( rStrm ); break;
+ case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break;
+ case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break;
+ case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break;
+ case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break;
+ default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
+ }
+}
+
+namespace {
+
+void lclSetValue( XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, SvNumFormatType nFormatType )
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ rDoc.setNumericCell(rScPos, fValue);
+ sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() );
+ rDoc.getDoc().ApplyAttr(
+ rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT, nScNumFmt));
+}
+
+} // namespace
+
+void XclImpPCItem::WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ if( const OUString* pText = GetText() )
+ rDoc.setStringCell(rScPos, *pText);
+ else if( const double* pfValue = GetDouble() )
+ rDoc.setNumericCell(rScPos, *pfValue);
+ else if( const sal_Int16* pnValue = GetInteger() )
+ rDoc.setNumericCell(rScPos, *pnValue);
+ else if( const bool* pbValue = GetBool() )
+ lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, SvNumFormatType::LOGICAL );
+ else if( const DateTime* pDateTime = GetDateTime() )
+ {
+ // set number format date, time, or date/time, depending on the value
+ double fValue = rRoot.GetDoubleFromDateTime( *pDateTime );
+ double fInt = 0.0;
+ double fFrac = modf( fValue, &fInt );
+ SvNumFormatType nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? SvNumFormatType::DATE :
+ ((fInt == 0.0) ? SvNumFormatType::TIME : SvNumFormatType::DATETIME);
+ lclSetValue( rRoot, rScPos, fValue, nFormatType );
+ }
+ else if( const sal_uInt16* pnError = GetError() )
+ {
+ double fValue;
+ sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError );
+ std::unique_ptr<ScTokenArray> pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr(
+ XclTools::ErrorToEnum( fValue, true, nErrCode ) );
+ ScFormulaCell* pCell = pScTokArr
+ ? new ScFormulaCell(rDoc.getDoc(), rScPos, std::move(pScTokArr))
+ : new ScFormulaCell(rDoc.getDoc(), rScPos);
+ pCell->SetHybridDouble( fValue );
+ rDoc.setFormulaCell(rScPos, pCell);
+ }
+}
+
+void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
+ SetDouble( rStrm.ReadDouble() );
+}
+
+void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
+ SetBool( rStrm.ReaduInt16() != 0 );
+}
+
+void XclImpPCItem::ReadSxerror( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
+ SetError( rStrm.ReaduInt16() );
+}
+
+void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
+ SetInteger( rStrm.ReadInt16() );
+}
+
+void XclImpPCItem::ReadSxstring( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
+ SetText( rStrm.ReadUniString() );
+}
+
+void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
+ sal_uInt16 nYear, nMonth;
+ sal_uInt8 nDay, nHour, nMin, nSec;
+ nYear = rStrm.ReaduInt16();
+ nMonth = rStrm.ReaduInt16();
+ nDay = rStrm.ReaduInt8();
+ nHour = rStrm.ReaduInt8();
+ nMin = rStrm.ReaduInt8();
+ nSec = rStrm.ReaduInt8();
+ SetDateTime( DateTime( Date( nDay, nMonth, nYear ), tools::Time( nHour, nMin, nSec ) ) );
+}
+
+void XclImpPCItem::ReadSxempty( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
+ SetEmpty();
+}
+
+XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) :
+ XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ),
+ XclImpRoot( rRoot ),
+ mrPCache( rPCache ),
+ mnSourceScCol( -1 ),
+ mbNumGroupInfoRead( false )
+{
+}
+
+XclImpPCField::~XclImpPCField()
+{
+}
+
+// general field/item access --------------------------------------------------
+
+const OUString& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const
+{
+ if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) )
+ {
+ const OUString& rVisName = rVisNames[ mnFieldIdx ];
+ if (!rVisName.isEmpty())
+ return rVisName;
+ }
+ return maFieldInfo.maName;
+}
+
+const XclImpPCField* XclImpPCField::GetGroupBaseField() const
+{
+ OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
+ return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : nullptr;
+}
+
+const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
+}
+
+const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const
+{
+ OSL_ENSURE( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" );
+ OSL_ENSURE( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" );
+ return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : nullptr;
+}
+
+void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab )
+{
+ OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
+ GetDocImport().setStringCell(ScAddress(nScCol, 0, nScTab), maFieldInfo.maName);
+ mnSourceScCol = nScCol;
+}
+
+void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx )
+{
+ if( nItemIdx < maOrigItems.size() )
+ maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
+}
+
+void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab )
+{
+ if( !maOrigItems.empty() )
+ maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPCField::ReadSxfield( XclImpStream& rStrm )
+{
+ rStrm >> maFieldInfo;
+
+ /* Detect the type of this field. This is done very restrictive to detect
+ any unexpected state. */
+ meFieldType = EXC_PCFIELD_UNKNOWN;
+
+ bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS );
+ bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
+ bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED );
+ bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+ bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP );
+
+ sal_uInt16 nVisC = maFieldInfo.mnVisItems;
+ sal_uInt16 nGroupC = maFieldInfo.mnGroupItems;
+ sal_uInt16 nBaseC = maFieldInfo.mnBaseItems;
+ sal_uInt16 nOrigC = maFieldInfo.mnOrigItems;
+ OSL_ENSURE( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" );
+
+ sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK;
+ bool bType =
+ (nType == EXC_SXFIELD_DATA_STR) ||
+ (nType == EXC_SXFIELD_DATA_INT) ||
+ (nType == EXC_SXFIELD_DATA_DBL) ||
+ (nType == EXC_SXFIELD_DATA_STR_INT) ||
+ (nType == EXC_SXFIELD_DATA_STR_DBL) ||
+ (nType == EXC_SXFIELD_DATA_DATE) ||
+ (nType == EXC_SXFIELD_DATA_DATE_EMP) ||
+ (nType == EXC_SXFIELD_DATA_DATE_NUM) ||
+ (nType == EXC_SXFIELD_DATA_DATE_STR);
+ bool bTypeNone =
+ (nType == EXC_SXFIELD_DATA_NONE);
+ // for now, ignore data type of calculated fields
+ OSL_ENSURE( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" );
+
+ if( !(nVisC > 0 || bPostp) )
+ return;
+
+ if( bItems && !bPostp )
+ {
+ if( !bCalced )
+ {
+ // 1) standard fields and standard grouping fields
+ if( !bNum )
+ {
+ // 1a) standard field without grouping
+ if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) )
+ meFieldType = EXC_PCFIELD_STANDARD;
+
+ // 1b) standard grouping field
+ else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_STDGROUP;
+ }
+ // 2) numerical grouping fields
+ else if( (nGroupC == nVisC) && (nBaseC == 0) )
+ {
+ // 2a) single num/date grouping field without child grouping field
+ if( !bChild && bType && (nOrigC > 0) )
+ {
+ switch( nType )
+ {
+ case EXC_SXFIELD_DATA_INT:
+ case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break;
+ case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break;
+ default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
+ }
+ }
+
+ // 2b) first date grouping field with child grouping field
+ else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) )
+ meFieldType = EXC_PCFIELD_DATEGROUP;
+
+ // 2c) additional date grouping field
+ else if( bTypeNone && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_DATECHILD;
+ }
+ OSL_ENSURE( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
+ }
+
+ // 3) calculated field
+ else
+ {
+ if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_CALCED;
+ OSL_ENSURE( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" );
+ }
+ }
+
+ else if( !bItems && bPostp )
+ {
+ // 4) standard field with postponed items
+ if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_STANDARD;
+ OSL_ENSURE( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" );
+ }
+}
+
+void XclImpPCField::ReadItem( XclImpStream& rStrm )
+{
+ OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
+
+ // read the item
+ XclImpPCItemRef xItem = std::make_shared<XclImpPCItem>( rStrm );
+
+ // try to insert into an item list
+ if( mbNumGroupInfoRead )
+ {
+ // there are 3 items after SXNUMGROUP that contain grouping limits and step count
+ if( maNumGroupItems.size() < 3 )
+ maNumGroupItems.push_back( xItem );
+ else
+ maOrigItems.push_back( xItem );
+ }
+ else if( HasInlineItems() || HasPostponedItems() )
+ {
+ maItems.push_back( xItem );
+ // visible item is original item in standard fields
+ if( IsStandardField() )
+ maOrigItems.push_back( xItem );
+ }
+}
+
+void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm )
+{
+ OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
+ OSL_ENSURE( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
+ OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
+ rStrm >> maNumGroupInfo;
+ mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField();
+}
+
+void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm )
+{
+ OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
+ OSL_ENSURE( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
+ OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
+ OSL_ENSURE( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
+ maGroupOrder.clear();
+ size_t nSize = rStrm.GetRecLeft() / 2;
+ maGroupOrder.resize( nSize, 0 );
+ for( size_t nIdx = 0; nIdx < nSize; ++nIdx )
+ maGroupOrder[ nIdx ] = rStrm.ReaduInt16();
+}
+
+// grouping -------------------------------------------------------------------
+
+void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ if (!GetFieldName(rVisNames).isEmpty())
+ {
+ if( IsStdGroupField() )
+ ConvertStdGroupField( rSaveData, rVisNames );
+ else if( IsNumGroupField() )
+ ConvertNumGroupField( rSaveData, rVisNames );
+ else if( IsDateGroupField() )
+ ConvertDateGroupField( rSaveData, rVisNames );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ const XclImpPCField* pBaseField = GetGroupBaseField();
+ if(!pBaseField)
+ return;
+
+ const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
+ if( rBaseFieldName.isEmpty() )
+ return;
+
+ // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
+ ScDPSaveGroupItemVec aGroupItems;
+ aGroupItems.reserve( maItems.size() );
+ // initialize with own item names
+ for( const auto& rxItem : maItems )
+ aGroupItems.emplace_back( rxItem->ConvertToText() );
+
+ // *** iterate over all base items, set their names at corresponding own items ***
+ for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx )
+ if( maGroupOrder[ nItemIdx ] < aGroupItems.size() )
+ if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) )
+ if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) )
+ if( *pBaseItem != *pGroupItem )
+ aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() );
+
+ // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
+ ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
+ for( const auto& rGroupItem : aGroupItems )
+ if( !rGroupItem.IsEmpty() )
+ aGroupDim.AddGroupItem( rGroupItem );
+ rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
+}
+
+void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() );
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+}
+
+void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() );
+ sal_Int32 nScDateType = maNumGroupInfo.GetScDateType();
+
+ switch( meFieldType )
+ {
+ case EXC_PCFIELD_DATEGROUP:
+ {
+ if( aDateInfo.mbDateValues )
+ {
+ // special case for days only with step value - create numeric grouping
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+ }
+ else
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() );
+ aNumGroupDim.SetDateInfo( aDateInfo, nScDateType );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+ }
+ }
+ break;
+
+ case EXC_PCFIELD_DATECHILD:
+ {
+ if( const XclImpPCField* pBaseField = GetGroupBaseField() )
+ {
+ const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
+ if( !rBaseFieldName.isEmpty() )
+ {
+ ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
+ aGroupDim.SetDateInfo( aDateInfo, nScDateType );
+ rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
+ }
+}
+
+ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const
+{
+ ScDPNumGroupInfo aNumInfo;
+ aNumInfo.mbEnable = true;
+ aNumInfo.mbDateValues = false;
+ aNumInfo.mbAutoStart = true;
+ aNumInfo.mbAutoEnd = true;
+
+ if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
+ {
+ aNumInfo.mfStart = *pfMinValue;
+ aNumInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
+ }
+ if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
+ {
+ aNumInfo.mfEnd = *pfMaxValue;
+ aNumInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
+ }
+ if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) )
+ aNumInfo.mfStep = *pfStepValue;
+
+ return aNumInfo;
+}
+
+ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const
+{
+ ScDPNumGroupInfo aDateInfo;
+ aDateInfo.mbEnable = true;
+ aDateInfo.mbDateValues = false;
+ aDateInfo.mbAutoStart = true;
+ aDateInfo.mbAutoEnd = true;
+
+ if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
+ {
+ aDateInfo.mfStart = GetDoubleFromDateTime( *pMinDate );
+ aDateInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
+ }
+ if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
+ {
+ aDateInfo.mfEnd = GetDoubleFromDateTime( *pMaxDate );
+ aDateInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
+ }
+ // GetDateGroupStep() returns a value for date type "day" in single date groups only
+ if( const sal_Int16* pnStepValue = GetDateGroupStep() )
+ {
+ aDateInfo.mfStep = *pnStepValue;
+ aDateInfo.mbDateValues = true;
+ }
+
+ return aDateInfo;
+}
+
+const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const
+{
+ OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
+ if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
+ {
+ OSL_ENSURE( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
+ return pItem->GetDouble();
+ }
+ return nullptr;
+}
+
+const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const
+{
+ OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
+ if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
+ {
+ OSL_ENSURE( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
+ return pItem->GetDateTime();
+ }
+ return nullptr;
+}
+
+const sal_Int16* XclImpPCField::GetDateGroupStep() const
+{
+ // only for single date grouping fields, not for grouping chains
+ if( !IsGroupBaseField() && !IsGroupChildField() )
+ {
+ // only days may have a step value, return 0 for all other date types
+ if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY )
+ {
+ if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) )
+ {
+ OSL_ENSURE( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
+ if( const sal_Int16* pnStep = pItem->GetInteger() )
+ {
+ OSL_ENSURE( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
+ // return nothing for step count 1 - this is also a standard date group in Excel
+ return (*pnStep > 1) ? pnStep : nullptr;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maSrcRange( ScAddress::INITIALIZE_INVALID ),
+ mnStrmId( 0 ),
+ mnSrcType( EXC_SXVS_UNKNOWN ),
+ mbSelfRef( false )
+{
+}
+
+XclImpPivotCache::~XclImpPivotCache()
+{
+}
+
+// data access ----------------------------------------------------------------
+
+const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm )
+{
+ mnStrmId = rStrm.ReaduInt16();
+}
+
+void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm )
+{
+ mnSrcType = rStrm.ReaduInt16();
+ GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET );
+}
+
+void XclImpPivotCache::ReadDconref( XclImpStream& rStrm )
+{
+ /* Read DCONREF only once (by checking maTabName), there may be other
+ DCONREF records in another context. Read reference only if a leading
+ SXVS record is present (by checking mnSrcType). */
+ if( !maTabName.isEmpty() || (mnSrcType != EXC_SXVS_SHEET) )
+ return;
+
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ aXclRange.Read( rStrm, false );
+ OUString aEncUrl = rStrm.ReadUniString();
+
+ XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl );
+
+ /* Do not convert maTabName to Calc sheet name -> original name is used to
+ find the sheet in the document. Sheet index of source range will be
+ found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
+ may not exist yet. */
+ if( mbSelfRef )
+ GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true );
+}
+
+void XclImpPivotCache::ReadDConName( XclImpStream& rStrm )
+{
+ maSrcRangeName = rStrm.ReadUniString();
+
+ // This 2-byte value equals the length of string that follows, or if 0 it
+ // indicates that the name has a workbook scope. For now, we only support
+ // internal defined name with a workbook scope.
+ sal_uInt16 nFlag;
+ nFlag = rStrm.ReaduInt16();
+ mbSelfRef = (nFlag == 0);
+
+ if (!mbSelfRef)
+ // External name is not supported yet.
+ maSrcRangeName.clear();
+}
+
+void XclImpPivotCache::ReadPivotCacheStream( const XclImpStream& rStrm )
+{
+ if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) )
+ return;
+
+ ScDocument& rDoc = GetDoc();
+ SCCOL nFieldScCol = 0; // column index of source data for next field
+ SCROW nItemScRow = 0; // row index of source data for current items
+ SCTAB nScTab = 0; // sheet index of source data
+ bool bGenerateSource = false; // true = write source data from cache to dummy table
+
+ if( mbSelfRef )
+ {
+ if (maSrcRangeName.isEmpty())
+ {
+ // try to find internal sheet containing the source data
+ nScTab = GetTabInfo().GetScTabFromXclName( maTabName );
+ if( rDoc.HasTable( nScTab ) )
+ {
+ // set sheet index to source range
+ maSrcRange.aStart.SetTab( nScTab );
+ maSrcRange.aEnd.SetTab( nScTab );
+ }
+ else
+ {
+ // create dummy sheet for deleted internal sheet
+ bGenerateSource = true;
+ }
+ }
+ }
+ else
+ {
+ // create dummy sheet for external sheet
+ bGenerateSource = true;
+ }
+
+ // create dummy sheet for source data from external or deleted sheet
+ if( bGenerateSource )
+ {
+ if( rDoc.GetTableCount() >= MAXTABCOUNT )
+ // cannot create more sheets -> exit
+ return;
+
+ nScTab = rDoc.GetTableCount();
+ rDoc.MakeTable( nScTab );
+ OUStringBuffer aDummyName("DPCache");
+ if( maTabName.getLength() > 0 )
+ aDummyName.append( "_" + maTabName );
+ OUString aName = aDummyName.makeStringAndClear();
+ rDoc.CreateValidTabName( aName );
+ rDoc.RenameTab( nScTab, aName );
+ // set sheet index to source range
+ maSrcRange.aStart.SetTab( nScTab );
+ maSrcRange.aEnd.SetTab( nScTab );
+ }
+
+ // open pivot cache storage stream
+ tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) );
+ if( !xSvStrm.is() )
+ return;
+
+ // create Excel record stream object
+ XclImpStream aPCStrm( *xSvStrm, GetRoot() );
+ aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted
+
+ XclImpPCFieldRef xCurrField; // current field for new items
+ XclImpPCFieldVec aOrigFields; // all standard fields with inline original items
+ XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items
+ size_t nPostpIdx = 0; // index to current field with postponed items
+ bool bLoop = true; // true = continue loop
+
+ while( bLoop && aPCStrm.StartNextRecord() )
+ {
+ switch( aPCStrm.GetRecId() )
+ {
+ case EXC_ID_EOF:
+ bLoop = false;
+ break;
+
+ case EXC_ID_SXDB:
+ aPCStrm >> maPCInfo;
+ break;
+
+ case EXC_ID_SXFIELD:
+ {
+ xCurrField.reset();
+ sal_uInt16 nNewFieldIdx = static_cast< sal_uInt16 >( maFields.size() );
+ if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT )
+ {
+ xCurrField = std::make_shared<XclImpPCField>( GetRoot(), *this, nNewFieldIdx );
+ maFields.push_back( xCurrField );
+ xCurrField->ReadSxfield( aPCStrm );
+ if( xCurrField->HasOrigItems() )
+ {
+ if( xCurrField->HasPostponedItems() )
+ aPostpFields.push_back( xCurrField );
+ else
+ aOrigFields.push_back( xCurrField );
+ // insert field name into generated source data, field remembers its column index
+ if( bGenerateSource && (nFieldScCol <= rDoc.MaxCol()) )
+ xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab );
+ }
+ // do not read items into invalid/postponed fields
+ if( !xCurrField->HasInlineItems() )
+ xCurrField.reset();
+ }
+ }
+ break;
+
+ case EXC_ID_SXINDEXLIST:
+ // read index list and insert all items into generated source data
+ if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) && (++nItemScRow <= rDoc.MaxRow()) )
+ {
+ for( const auto& rxOrigField : aOrigFields )
+ {
+ sal_uInt16 nItemIdx = rxOrigField->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8();
+ rxOrigField->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx );
+ }
+ }
+ xCurrField.reset();
+ break;
+
+ case EXC_ID_SXDOUBLE:
+ case EXC_ID_SXBOOLEAN:
+ case EXC_ID_SXERROR:
+ case EXC_ID_SXINTEGER:
+ case EXC_ID_SXSTRING:
+ case EXC_ID_SXDATETIME:
+ case EXC_ID_SXEMPTY:
+ if( xCurrField ) // inline items
+ {
+ xCurrField->ReadItem( aPCStrm );
+ }
+ else if( !aPostpFields.empty() ) // postponed items
+ {
+ // read postponed item
+ aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm );
+ // write item to source
+ if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) )
+ {
+ // start new row, if there are only postponed fields
+ if( aOrigFields.empty() && (nPostpIdx == 0) )
+ ++nItemScRow;
+ if( nItemScRow <= rDoc.MaxRow() )
+ aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab );
+ }
+ // get index of next postponed field
+ ++nPostpIdx;
+ if( nPostpIdx >= aPostpFields.size() )
+ nPostpIdx = 0;
+ }
+ break;
+
+ case EXC_ID_SXNUMGROUP:
+ if( xCurrField )
+ xCurrField->ReadSxnumgroup( aPCStrm );
+ break;
+
+ case EXC_ID_SXGROUPINFO:
+ if( xCurrField )
+ xCurrField->ReadSxgroupinfo( aPCStrm );
+ break;
+
+ // known but ignored records
+ case EXC_ID_SXRULE:
+ case EXC_ID_SXFILT:
+ case EXC_ID_00F5:
+ case EXC_ID_SXNAME:
+ case EXC_ID_SXPAIR:
+ case EXC_ID_SXFMLA:
+ case EXC_ID_SXFORMULA:
+ case EXC_ID_SXDBEX:
+ case EXC_ID_SXFDBTYPE:
+ break;
+
+ default:
+ SAL_WARN("sc.filter", "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x" << std::hex << aPCStrm.GetRecId() );
+ }
+ }
+
+ OSL_ENSURE( maPCInfo.mnTotalFields == maFields.size(),
+ "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
+
+ if (static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_SAVEDATA))
+ {
+ SCROW nNewEnd = maSrcRange.aStart.Row() + maPCInfo.mnSrcRecs;
+ maSrcRange.aEnd.SetRow(nNewEnd);
+ }
+
+ // set source range for external source data
+ if( bGenerateSource && (nFieldScCol > 0) )
+ {
+ maSrcRange.aStart.SetCol( 0 );
+ maSrcRange.aStart.SetRow( 0 );
+ // nFieldScCol points to first unused column
+ maSrcRange.aEnd.SetCol( nFieldScCol - 1 );
+ // nItemScRow points to last used row
+ maSrcRange.aEnd.SetRow( nItemScRow );
+ }
+}
+
+bool XclImpPivotCache::IsRefreshOnLoad() const
+{
+ return static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_REFRESH_LOAD);
+}
+
+bool XclImpPivotCache::IsValid() const
+{
+ if (!maSrcRangeName.isEmpty())
+ return true;
+
+ return maSrcRange.IsValid();
+}
+
+// Pivot table
+
+XclImpPTItem::XclImpPTItem( const XclImpPCField* pCacheField ) :
+ mpCacheField( pCacheField )
+{
+}
+
+const OUString* XclImpPTItem::GetItemName() const
+{
+ if( mpCacheField )
+ if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) )
+ //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
+ return pCacheItem->IsEmpty() ? nullptr : pCacheItem->GetText();
+ return nullptr;
+}
+
+std::pair<bool, OUString> XclImpPTItem::GetItemName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot) const
+{
+ if(!mpCacheField)
+ return std::pair<bool, OUString>(false, OUString());
+
+ const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx );
+ if(!pCacheItem)
+ return std::pair<bool, OUString>(false, OUString());
+
+ OUString sItemName;
+ if(pCacheItem->GetType() == EXC_PCITEM_TEXT || pCacheItem->GetType() == EXC_PCITEM_ERROR)
+ {
+ const OUString* pItemName = pCacheItem->GetText();
+ if(!pItemName)
+ return std::pair<bool, OUString>(false, OUString());
+ sItemName = *pItemName;
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_DOUBLE)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), *pCacheItem->GetDouble());
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_INTEGER)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetInteger()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_BOOL)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetBool()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_DATETIME)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), rRoot.GetDoubleFromDateTime(*pCacheItem->GetDateTime()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_EMPTY)
+ {
+ // sItemName is an empty string
+ }
+ else // EXC_PCITEM_INVALID
+ return std::pair<bool, OUString>(false, OUString());
+
+ return std::pair<bool, OUString>(true, sItemName);
+}
+
+void XclImpPTItem::ReadSxvi( XclImpStream& rStrm )
+{
+ rStrm >> maItemInfo;
+}
+
+void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot ) const
+{
+ // Find member and set properties
+ std::pair<bool, OUString> aReturnedName = GetItemName(rSaveDim, pObj, rRoot);
+ if(aReturnedName.first)
+ {
+ ScDPSaveMember* pMember = rSaveDim.GetExistingMemberByName(aReturnedName.second);
+ if(pMember)
+ {
+ pMember->SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
+ pMember->SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
+ if (maItemInfo.HasVisName())
+ pMember->SetLayoutName(*maItemInfo.GetVisName());
+ }
+ }
+}
+
+XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
+ mrPTable( rPTable )
+{
+ maFieldInfo.mnCacheIdx = nCacheIdx;
+}
+
+// general field/item access --------------------------------------------------
+
+const XclImpPCField* XclImpPTField::GetCacheField() const
+{
+ XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache();
+ return xPCache ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : nullptr;
+}
+
+OUString XclImpPTField::GetFieldName() const
+{
+ const XclImpPCField* pField = GetCacheField();
+ return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : OUString();
+}
+
+OUString XclImpPTField::GetVisFieldName() const
+{
+ const OUString* pVisName = maFieldInfo.GetVisName();
+ return pVisName ? *pVisName : OUString();
+}
+
+const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
+}
+
+const OUString* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const
+{
+ const XclImpPTItem* pItem = GetItem( nItemIdx );
+ return pItem ? pItem->GetItemName() : nullptr;
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPTField::ReadSxvd( XclImpStream& rStrm )
+{
+ rStrm >> maFieldInfo;
+}
+
+void XclImpPTField::ReadSxvdex( XclImpStream& rStrm )
+{
+ rStrm >> maFieldExtInfo;
+}
+
+void XclImpPTField::ReadSxvi( XclImpStream& rStrm )
+{
+ XclImpPTItemRef xItem = std::make_shared<XclImpPTItem>( GetCacheField() );
+ maItems.push_back( xItem );
+ xItem->ReadSxvi( rStrm );
+}
+
+// row/column fields ----------------------------------------------------------
+
+void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" );
+ // special data orientation field?
+ if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA )
+ rSaveData.GetDataLayoutDimension()->SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) );
+ else
+ ConvertRCPField( rSaveData );
+}
+
+// page fields ----------------------------------------------------------------
+
+void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo )
+{
+ maPageInfo = rPageInfo;
+}
+
+void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" );
+ ConvertRCPField( rSaveData );
+}
+
+// hidden fields --------------------------------------------------------------
+
+void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
+ ConvertRCPField( rSaveData );
+}
+
+// data fields ----------------------------------------------------------------
+
+bool XclImpPTField::HasDataFieldInfo() const
+{
+ return !maDataInfoVector.empty();
+}
+
+void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo )
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" );
+ maDataInfoVector.push_back( rDataInfo );
+}
+
+void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" );
+ OSL_ENSURE( !maDataInfoVector.empty(), "XclImpPTField::ConvertDataField - no data field info" );
+ if (maDataInfoVector.empty())
+ return;
+
+ OUString aFieldName = GetFieldName();
+ if (aFieldName.isEmpty())
+ return;
+
+ ScDPSaveDimension* pSaveDim = rSaveData.GetNewDimensionByName(aFieldName);
+ if (!pSaveDim)
+ {
+ SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName);
+ return;
+ }
+
+ auto aIt = maDataInfoVector.begin(), aEnd = maDataInfoVector.end();
+
+ ConvertDataField( *pSaveDim, *aIt );
+
+ // multiple data fields -> clone dimension
+ for( ++aIt; aIt != aEnd; ++aIt )
+ {
+ ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( *pSaveDim );
+ ConvertDataFieldInfo( rDupDim, *aIt );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+/**
+ * Convert Excel-encoded subtotal name to a Calc-encoded one.
+ */
+static OUString lcl_convertExcelSubtotalName(const OUString& rName)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rName.getStr();
+ sal_Int32 n = rName.getLength();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (c == '\\')
+ {
+ aBuf.append(OUStringChar(c) + OUStringChar(c));
+ }
+ else
+ aBuf.append(c);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const
+{
+ const OUString& rFieldName = GetFieldName();
+ if( rFieldName.isEmpty() )
+ return;
+
+ const XclImpPCField* pCacheField = GetCacheField();
+ if( !pCacheField || !pCacheField->IsSupportedField() )
+ return;
+
+ ScDPSaveDimension* pTest = rSaveData.GetNewDimensionByName(rFieldName);
+ if (!pTest)
+ return;
+
+ ScDPSaveDimension& rSaveDim = *pTest;
+
+ // orientation
+ rSaveDim.SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) );
+
+ // visible name
+ if (const OUString* pVisName = maFieldInfo.GetVisName())
+ if (!pVisName->isEmpty())
+ rSaveDim.SetLayoutName( *pVisName );
+
+ // subtotal function(s)
+ XclPTSubtotalVec aSubtotalVec;
+ maFieldInfo.GetSubtotals( aSubtotalVec );
+ if( !aSubtotalVec.empty() )
+ rSaveDim.SetSubTotals( std::move(aSubtotalVec) );
+
+ // sorting
+ DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField );
+ aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC );
+ aSortInfo.Mode = maFieldExtInfo.GetApiSortMode();
+ rSaveDim.SetSortInfo( &aSortInfo );
+
+ // auto show
+ DataPilotFieldAutoShowInfo aShowInfo;
+ aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW );
+ aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode();
+ aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount();
+ aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField );
+ rSaveDim.SetAutoShowInfo( &aShowInfo );
+
+ // layout
+ DataPilotFieldLayoutInfo aLayoutInfo;
+ aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode();
+ aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK );
+ rSaveDim.SetLayoutInfo( &aLayoutInfo );
+
+ // grouping info
+ pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
+
+ // custom subtotal name
+ if (maFieldExtInfo.mpFieldTotalName)
+ {
+ OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName);
+ rSaveDim.SetSubtotalName(aSubName);
+ }
+}
+
+void XclImpPTField::ConvertFieldInfo( const ScDPSaveData& rSaveData, ScDPObject* pObj, const XclImpRoot& rRoot, bool bPageField ) const
+{
+ const OUString& rFieldName = GetFieldName();
+ if( rFieldName.isEmpty() )
+ return;
+
+ const XclImpPCField* pCacheField = GetCacheField();
+ if( !pCacheField || !pCacheField->IsSupportedField() )
+ return;
+
+ ScDPSaveDimension* pSaveDim = rSaveData.GetExistingDimensionByName(rFieldName);
+ if (!pSaveDim)
+ return;
+
+ pSaveDim->SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) );
+ for( const auto& rxItem : maItems )
+ rxItem->ConvertItem( *pSaveDim, pObj, rRoot );
+
+ if(bPageField && maPageInfo.mnSelItem != EXC_SXPI_ALLITEMS)
+ {
+ const XclImpPTItem* pItem = GetItem( maPageInfo.mnSelItem );
+ if(pItem)
+ {
+ std::pair<bool, OUString> aReturnedName = pItem->GetItemName(*pSaveDim, pObj, rRoot);
+ if(aReturnedName.first)
+ pSaveDim->SetCurrentPage(&aReturnedName.second);
+ }
+ }
+}
+
+void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
+{
+ // orientation
+ rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA );
+ // extended data field info
+ ConvertDataFieldInfo( rSaveDim, rDataInfo );
+}
+
+void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
+{
+ // visible name
+ const OUString* pVisName = rDataInfo.GetVisName();
+ if (pVisName && !pVisName->isEmpty())
+ rSaveDim.SetLayoutName(*pVisName);
+
+ // aggregation function
+ rSaveDim.SetFunction( rDataInfo.GetApiAggFunc() );
+
+ // result field reference
+ sal_Int32 nRefType = rDataInfo.GetApiRefType();
+ DataPilotFieldReference aFieldRef;
+ aFieldRef.ReferenceType = nRefType;
+ const XclImpPTField* pRefField = mrPTable.GetField(rDataInfo.mnRefField);
+ if (pRefField)
+ {
+ aFieldRef.ReferenceField = pRefField->GetFieldName();
+ aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType();
+ if (aFieldRef.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
+ {
+ const OUString* pRefItemName = pRefField->GetItemName(rDataInfo.mnRefItem);
+ if (pRefItemName)
+ aFieldRef.ReferenceItemName = *pRefItemName;
+ }
+ }
+
+ rSaveDim.SetReferenceValue(&aFieldRef);
+}
+
+XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maDataOrientField( *this, EXC_SXIVD_DATA ),
+ mpDPObj(nullptr)
+{
+}
+
+XclImpPivotTable::~XclImpPivotTable()
+{
+}
+
+// cache/field access, misc. --------------------------------------------------
+
+sal_uInt16 XclImpPivotTable::GetFieldCount() const
+{
+ return static_cast< sal_uInt16 >( maFields.size() );
+}
+
+const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField :
+ ((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr);
+}
+
+XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx )
+{
+ // do not return maDataOrientField
+ return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
+}
+
+const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const
+{
+ if( nDataFieldIdx < maOrigDataFields.size() )
+ return GetField( maOrigDataFields[ nDataFieldIdx ] );
+ return nullptr;
+}
+
+OUString XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const
+{
+ if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) )
+ return pField->GetFieldName();
+ return OUString();
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPivotTable::ReadSxview( XclImpStream& rStrm )
+{
+ rStrm >> maPTInfo;
+
+ GetAddressConverter().ConvertRange(
+ maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true );
+
+ mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx );
+ mxCurrField.reset();
+}
+
+void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm )
+{
+ sal_uInt16 nFieldCount = GetFieldCount();
+ if( nFieldCount < EXC_PT_MAXFIELDCOUNT )
+ {
+ // cache index for the field is equal to the SXVD record index
+ mxCurrField = std::make_shared<XclImpPTField>( *this, nFieldCount );
+ maFields.push_back( mxCurrField );
+ mxCurrField->ReadSxvd( rStrm );
+ // add visible name of new field to list of visible names
+ maVisFieldNames.push_back( mxCurrField->GetVisFieldName() );
+ OSL_ENSURE( maFields.size() == maVisFieldNames.size(),
+ "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
+ }
+ else
+ mxCurrField.reset();
+}
+
+void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm )
+{
+ if( mxCurrField )
+ mxCurrField->ReadSxvi( rStrm );
+}
+
+void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm )
+{
+ if( mxCurrField )
+ mxCurrField->ReadSxvdex( rStrm );
+}
+
+void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ // find the index vector to fill (row SXIVD doesn't exist without row fields)
+ ScfUInt16Vec* pFieldVec = nullptr;
+ if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) )
+ pFieldVec = &maRowFields;
+ else if( maColFields.empty() && (maPTInfo.mnColFields > 0) )
+ pFieldVec = &maColFields;
+
+ // fill the vector from record data
+ if( !pFieldVec )
+ return;
+
+ sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT );
+ pFieldVec->reserve( nSize );
+ for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx )
+ {
+ sal_uInt16 nFieldIdx;
+ nFieldIdx = rStrm.ReaduInt16();
+ pFieldVec->push_back( nFieldIdx );
+
+ // set orientation at special data orientation field
+ if( nFieldIdx == EXC_SXIVD_DATA )
+ {
+ sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL;
+ maDataOrientField.SetAxes( nAxis );
+ }
+ }
+}
+
+void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 );
+ for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry )
+ {
+ XclPTPageFieldInfo aPageInfo;
+ rStrm >> aPageInfo;
+ if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) )
+ {
+ maPageFields.push_back( aPageInfo.mnField );
+ pField->SetPageFieldInfo( aPageInfo );
+ }
+ GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId );
+ }
+}
+
+void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ XclPTDataFieldInfo aDataInfo;
+ rStrm >> aDataInfo;
+ if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) )
+ {
+ maOrigDataFields.push_back( aDataInfo.mnField );
+ // DataPilot does not support double data fields -> add first appearance to index list only
+ if( !pField->HasDataFieldInfo() )
+ maFiltDataFields.push_back( aDataInfo.mnField );
+ pField->AddDataFieldInfo( aDataInfo );
+ }
+}
+
+void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
+{
+ rStrm >> maPTExtInfo;
+}
+
+void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
+{
+ rStrm >> maPTViewEx9Info;
+}
+
+void XclImpPivotTable::ReadSxAddl( XclImpStream& rStrm )
+{
+ rStrm >> maPTAddlInfo;
+}
+
+void XclImpPivotTable::Convert()
+{
+ if( !mxPCache || !mxPCache->IsValid() )
+ return;
+
+ if (utl::ConfigManager::IsFuzzing()) //just too slow
+ return;
+
+ ScDPSaveData aSaveData;
+
+ // *** global settings ***
+
+ aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) );
+ aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) );
+ aSaveData.SetFilterButton( false );
+ aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) );
+ aSaveData.SetIgnoreEmptyRows( false );
+ aSaveData.SetRepeatIfEmpty( false );
+
+ // *** fields ***
+
+ // row fields
+ for( const auto& rRowField : maRowFields )
+ if( const XclImpPTField* pField = GetField( rRowField ) )
+ pField->ConvertRowColField( aSaveData );
+
+ // column fields
+ for( const auto& rColField : maColFields )
+ if( const XclImpPTField* pField = GetField( rColField ) )
+ pField->ConvertRowColField( aSaveData );
+
+ // page fields
+ for( const auto& rPageField : maPageFields )
+ if( const XclImpPTField* pField = GetField( rPageField ) )
+ pField->ConvertPageField( aSaveData );
+
+ // We need to import hidden fields because hidden fields may contain
+ // special settings for subtotals (aggregation function, filters, custom
+ // name etc.) and members (hidden, custom name etc.).
+
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if (!pField->GetAxes())
+ pField->ConvertHiddenField( aSaveData );
+
+ // data fields
+ for( const auto& rFiltDataField : maFiltDataFields )
+ if( const XclImpPTField* pField = GetField( rFiltDataField ) )
+ pField->ConvertDataField( aSaveData );
+
+ // *** insert into Calc document ***
+
+ // create source descriptor
+ ScSheetSourceDesc aDesc(&GetDoc());
+ const OUString& rSrcName = mxPCache->GetSourceRangeName();
+ if (!rSrcName.isEmpty())
+ // Range name is the data source.
+ aDesc.SetRangeName(rSrcName);
+ else
+ // Normal cell range.
+ aDesc.SetSourceRange(mxPCache->GetSourceRange());
+
+ // adjust output range to include the page fields
+ ScRange aOutRange( maOutScRange );
+ if( !maPageFields.empty() )
+ {
+ SCROW nDecRows = ::std::min< SCROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 );
+ aOutRange.aStart.IncRow( -nDecRows );
+ }
+
+ // create the DataPilot
+ std::unique_ptr<ScDPObject> pDPObj(new ScDPObject( &GetDoc() ));
+ pDPObj->SetName( maPTInfo.maTableName );
+ if (!maPTInfo.maDataName.isEmpty())
+ aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
+
+ if (!maPTViewEx9Info.maGrandTotalName.isEmpty())
+ aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
+
+ pDPObj->SetSaveData( aSaveData );
+ pDPObj->SetSheetDesc( aDesc );
+ pDPObj->SetOutRange( aOutRange );
+ pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
+
+ mpDPObj = GetDoc().GetDPCollection()->InsertNewTable(std::move(pDPObj));
+
+ ApplyFieldInfo();
+ ApplyMergeFlags(aOutRange, aSaveData);
+}
+
+void XclImpPivotTable::MaybeRefresh()
+{
+ if (mpDPObj && mxPCache->IsRefreshOnLoad())
+ {
+ // 'refresh table on load' flag is set. Refresh the table now. Some
+ // Excel files contain partial table output when this flag is set.
+ ScRange aOutRange = mpDPObj->GetOutRange();
+ mpDPObj->Output(aOutRange.aStart);
+ }
+}
+
+void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData)
+{
+ // Apply merge flags for various datapilot controls.
+
+ ScDPOutputGeometry aGeometry(rOutRange, false);
+ aGeometry.setColumnFieldCount(maPTInfo.mnColFields);
+ aGeometry.setPageFieldCount(maPTInfo.mnPageFields);
+ aGeometry.setDataFieldCount(maPTInfo.mnDataFields);
+ aGeometry.setRowFieldCount(maPTInfo.mnRowFields);
+
+ // Make sure we set headerlayout when input file has additional raw header
+ if(maPTInfo.mnColFields == 0)
+ {
+ mpDPObj->SetHeaderLayout( maPTInfo.mnFirstHeadRow - 2 == static_cast<sal_uInt16>(aGeometry.getRowFieldHeaderRow()) );
+ }
+ aGeometry.setHeaderLayout(mpDPObj->GetHeaderLayout());
+ aGeometry.setCompactMode(maPTAddlInfo.mbCompactMode);
+
+ ScDocument& rDoc = GetDoc();
+
+ vector<const ScDPSaveDimension*> aFieldDims;
+ vector<ScAddress> aFieldBtns;
+
+ aGeometry.getPageFieldPositions(aFieldBtns);
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), ScMF::Button);
+
+ ScMF nMFlag = ScMF::ButtonPopup;
+ OUString aName = rDoc.GetString(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab());
+ if (rSaveData.HasInvisibleMember(aName))
+ nMFlag |= ScMF::HiddenMember;
+
+ rDoc.ApplyFlagsTab(rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ }
+
+ aGeometry.getColumnFieldPositions(aFieldBtns);
+ rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aFieldDims);
+ if (aFieldBtns.size() == aFieldDims.size())
+ {
+ vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ ScMF nMFlag = ScMF::Button;
+ const ScDPSaveDimension* pDim = *itDim;
+ if (pDim->HasInvisibleMember())
+ nMFlag |= ScMF::HiddenMember;
+ if (!pDim->IsDataLayout())
+ nMFlag |= ScMF::ButtonPopup;
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ ++itDim;
+ }
+ }
+
+ aGeometry.getRowFieldPositions(aFieldBtns);
+ rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aFieldDims);
+ if (!((aFieldBtns.size() == aFieldDims.size()) || (maPTAddlInfo.mbCompactMode && aFieldBtns.size() == 1)))
+ return;
+
+ vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ ScMF nMFlag = ScMF::Button;
+ const ScDPSaveDimension* pDim = itDim != aFieldDims.end() ? *itDim++ : nullptr;
+ if (pDim && pDim->HasInvisibleMember())
+ nMFlag |= ScMF::HiddenMember;
+ if (!pDim || !pDim->IsDataLayout())
+ nMFlag |= ScMF::ButtonPopup;
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ }
+}
+
+
+void XclImpPivotTable::ApplyFieldInfo()
+{
+ mpDPObj->BuildAllDimensionMembers();
+ ScDPSaveData& rSaveData = *mpDPObj->GetSaveData();
+
+ // row fields
+ for( const auto& rRowField : maRowFields )
+ if( const XclImpPTField* pField = GetField( rRowField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+
+ // column fields
+ for( const auto& rColField : maColFields )
+ if( const XclImpPTField* pField = GetField( rColField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+
+ // page fields
+ for( const auto& rPageField : maPageFields )
+ if( const XclImpPTField* pField = GetField( rPageField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this, true );
+
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if (!pField->GetAxes())
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+}
+
+XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+XclImpPivotTableManager::~XclImpPivotTableManager()
+{
+}
+
+// pivot cache records --------------------------------------------------------
+
+XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx )
+{
+ XclImpPivotCacheRef xPCache;
+ if( nCacheIdx < maPCaches.size() )
+ xPCache = maPCaches[ nCacheIdx ];
+ return xPCache;
+}
+
+void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm )
+{
+ XclImpPivotCacheRef xPCache = std::make_shared<XclImpPivotCache>( GetRoot() );
+ maPCaches.push_back( xPCache );
+ xPCache->ReadSxidstm( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadSxvs( rStrm );
+}
+
+void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadDconref( rStrm );
+}
+
+void XclImpPivotTableManager::ReadDConName( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadDConName( rStrm );
+}
+
+// pivot table records --------------------------------------------------------
+
+void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm )
+{
+ XclImpPivotTableRef xPTable = std::make_shared<XclImpPivotTable>( GetRoot() );
+ maPTables.push_back( xPTable );
+ xPTable->ReadSxview( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvd( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvdex( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxivd( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxpi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxdi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxex( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxViewEx9( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxAddl( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxAddl( rStrm );
+}
+
+void XclImpPivotTableManager::ReadPivotCaches( const XclImpStream& rStrm )
+{
+ for( auto& rxPCache : maPCaches )
+ rxPCache->ReadPivotCacheStream( rStrm );
+}
+
+void XclImpPivotTableManager::ConvertPivotTables()
+{
+ for( auto& rxPTable : maPTables )
+ rxPTable->Convert();
+}
+
+void XclImpPivotTableManager::MaybeRefreshPivotTables()
+{
+ for( auto& rxPTable : maPTables )
+ rxPTable->MaybeRefresh();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiroot.cxx b/sc/source/filter/excel/xiroot.cxx
new file mode 100644
index 0000000000..6734147450
--- /dev/null
+++ b/sc/source/filter/excel/xiroot.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 <xiroot.hxx>
+#include <addincol.hxx>
+#include <colrowst.hxx>
+#include <document.hxx>
+#include <formel.hxx>
+#include <scextopt.hxx>
+#include <xihelper.hxx>
+#include <xiformula.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+#include <xistyle.hxx>
+#include <xicontent.hxx>
+#include <xiescher.hxx>
+#include <xipivot.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+
+#include <root.hxx>
+#include <excimp8.hxx>
+#include <documentimport.hxx>
+#include <sot/storage.hxx>
+
+// Global data ================================================================
+
+XclImpRootData::XclImpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) :
+ XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, false ),
+ mxDocImport(std::make_shared<ScDocumentImport>(rDoc)),
+ mbHasCodePage( false ),
+ mbHasBasic( false )
+{
+}
+
+XclImpRootData::~XclImpRootData()
+{
+}
+
+XclImpRoot::XclImpRoot( XclImpRootData& rImpRootData ) :
+ XclRoot( rImpRootData ),
+ mrImpData( rImpRootData )
+{
+ mrImpData.mxAddrConv = std::make_shared<XclImpAddressConverter>( GetRoot() );
+ mrImpData.mxFmlaComp = std::make_shared<XclImpFormulaCompiler>( GetRoot() );
+ mrImpData.mxPalette = std::make_shared<XclImpPalette>( GetRoot() );
+ mrImpData.mxFontBfr = std::make_shared<XclImpFontBuffer>( GetRoot() );
+ mrImpData.mxNumFmtBfr = std::make_shared<XclImpNumFmtBuffer>( GetRoot() );
+ mrImpData.mpXFBfr = std::make_shared<XclImpXFBuffer>( GetRoot() );
+ mrImpData.mxXFRangeBfr = std::make_shared<XclImpXFRangeBuffer>( GetRoot() );
+ mrImpData.mxTabInfo = std::make_shared<XclImpTabInfo>();
+ mrImpData.mxNameMgr = std::make_shared<XclImpNameManager>( GetRoot() );
+ mrImpData.mxObjMgr = std::make_shared<XclImpObjectManager>( GetRoot() );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mrImpData.mxLinkMgr = std::make_shared<XclImpLinkManager>( GetRoot() );
+ mrImpData.mxSst = std::make_shared<XclImpSst>( GetRoot() );
+ mrImpData.mxCondFmtMgr = std::make_shared<XclImpCondFormatManager>( GetRoot() );
+ mrImpData.mxValidMgr = std::make_shared<XclImpValidationManager>( GetRoot() );
+ // TODO still in old RootData (deleted by RootData)
+ GetOldRoot().pAutoFilterBuffer.reset( new XclImpAutoFilterBuffer );
+ mrImpData.mxWebQueryBfr = std::make_shared<XclImpWebQueryBuffer>( GetRoot() );
+ mrImpData.mxPTableMgr = std::make_shared<XclImpPivotTableManager>( GetRoot() );
+ mrImpData.mxTabProtect = std::make_shared<XclImpSheetProtectBuffer>( GetRoot() );
+ mrImpData.mxDocProtect = std::make_shared<XclImpDocProtectBuffer>( GetRoot() );
+ }
+
+ mrImpData.mxPageSett = std::make_shared<XclImpPageSettings>( GetRoot() );
+ mrImpData.mxDocViewSett = std::make_shared<XclImpDocViewSettings>( GetRoot() );
+ mrImpData.mxTabViewSett = std::make_shared<XclImpTabViewSettings>( GetRoot() );
+ mrImpData.mpPrintRanges = std::make_unique<ScRangeListTabs>( GetRoot() );
+ mrImpData.mpPrintTitles = std::make_unique<ScRangeListTabs>( GetRoot() );
+}
+
+void XclImpRoot::SetCodePage( sal_uInt16 nCodePage )
+{
+ SetTextEncoding( XclTools::GetTextEncoding( nCodePage ) );
+ mrImpData.mbHasCodePage = true;
+}
+
+void XclImpRoot::InitializeTable( SCTAB nScTab )
+{
+ if( GetBiff() <= EXC_BIFF4 )
+ {
+ GetPalette().Initialize();
+ GetFontBuffer().Initialize();
+ GetNumFmtBuffer().Initialize();
+ GetXFBuffer().Initialize();
+ }
+ GetXFRangeBuffer().Initialize();
+ GetPageSettings().Initialize();
+ GetTabViewSettings().Initialize();
+ // delete the automatically generated codename
+ GetDoc().SetCodeName( nScTab, OUString() );
+}
+
+void XclImpRoot::FinalizeTable()
+{
+ GetXFRangeBuffer().Finalize();
+ GetOldRoot().pColRowBuff->Convert( GetCurrScTab() );
+ GetPageSettings().Finalize();
+ GetTabViewSettings().Finalize();
+}
+
+XclImpAddressConverter& XclImpRoot::GetAddressConverter() const
+{
+ return *mrImpData.mxAddrConv;
+}
+
+XclImpFormulaCompiler& XclImpRoot::GetFormulaCompiler() const
+{
+ return *mrImpData.mxFmlaComp;
+}
+
+ExcelToSc& XclImpRoot::GetOldFmlaConverter() const
+{
+ // TODO still in old RootData
+ return *GetOldRoot().pFmlaConverter;
+}
+
+XclImpSst& XclImpRoot::GetSst() const
+{
+ assert(mrImpData.mxSst && "XclImpRoot::GetSst - invalid call, wrong BIFF");
+ return *mrImpData.mxSst;
+}
+
+XclImpPalette& XclImpRoot::GetPalette() const
+{
+ return *mrImpData.mxPalette;
+}
+
+XclImpFontBuffer& XclImpRoot::GetFontBuffer() const
+{
+ return *mrImpData.mxFontBfr;
+}
+
+XclImpNumFmtBuffer& XclImpRoot::GetNumFmtBuffer() const
+{
+ return *mrImpData.mxNumFmtBfr;
+}
+
+XclImpXFBuffer& XclImpRoot::GetXFBuffer() const
+{
+ return *mrImpData.mpXFBfr;
+}
+
+XclImpXFRangeBuffer& XclImpRoot::GetXFRangeBuffer() const
+{
+ return *mrImpData.mxXFRangeBfr;
+}
+
+ScRangeListTabs& XclImpRoot::GetPrintAreaBuffer() const
+{
+ return *mrImpData.mpPrintRanges;
+}
+
+ScRangeListTabs& XclImpRoot::GetTitleAreaBuffer() const
+{
+ return *mrImpData.mpPrintTitles;
+}
+
+XclImpTabInfo& XclImpRoot::GetTabInfo() const
+{
+ return *mrImpData.mxTabInfo;
+}
+
+XclImpNameManager& XclImpRoot::GetNameManager() const
+{
+ return *mrImpData.mxNameMgr;
+}
+
+XclImpLinkManager& XclImpRoot::GetLinkManager() const
+{
+ assert(mrImpData.mxLinkMgr && "XclImpRoot::GetLinkManager - invalid call, wrong BIFF");
+ return *mrImpData.mxLinkMgr;
+}
+
+XclImpObjectManager& XclImpRoot::GetObjectManager() const
+{
+ return *mrImpData.mxObjMgr;
+}
+
+XclImpSheetDrawing& XclImpRoot::GetCurrSheetDrawing() const
+{
+ OSL_ENSURE( !IsInGlobals(), "XclImpRoot::GetCurrSheetDrawing - must not be called from workbook globals" );
+ return mrImpData.mxObjMgr->GetSheetDrawing( GetCurrScTab() );
+}
+
+XclImpCondFormatManager& XclImpRoot::GetCondFormatManager() const
+{
+ assert(mrImpData.mxCondFmtMgr && "XclImpRoot::GetCondFormatManager - invalid call, wrong BIFF");
+ return *mrImpData.mxCondFmtMgr;
+}
+
+XclImpValidationManager& XclImpRoot::GetValidationManager() const
+{
+ assert(mrImpData.mxValidMgr && "XclImpRoot::GetValidationManager - invalid call, wrong BIFF");
+ return *mrImpData.mxValidMgr;
+}
+
+XclImpAutoFilterBuffer& XclImpRoot::GetFilterManager() const
+{
+ // TODO still in old RootData
+ assert(GetOldRoot().pAutoFilterBuffer && "XclImpRoot::GetFilterManager - invalid call, wrong BIFF");
+ return *GetOldRoot().pAutoFilterBuffer;
+}
+
+XclImpWebQueryBuffer& XclImpRoot::GetWebQueryBuffer() const
+{
+ assert(mrImpData.mxWebQueryBfr && "XclImpRoot::GetWebQueryBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxWebQueryBfr;
+}
+
+XclImpPivotTableManager& XclImpRoot::GetPivotTableManager() const
+{
+ assert(mrImpData.mxPTableMgr && "XclImpRoot::GetPivotTableManager - invalid call, wrong BIFF");
+ return *mrImpData.mxPTableMgr;
+}
+
+XclImpSheetProtectBuffer& XclImpRoot::GetSheetProtectBuffer() const
+{
+ assert(mrImpData.mxTabProtect && "XclImpRoot::GetSheetProtectBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxTabProtect;
+}
+
+XclImpDocProtectBuffer& XclImpRoot::GetDocProtectBuffer() const
+{
+ assert(mrImpData.mxDocProtect && "XclImpRoot::GetDocProtectBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxDocProtect;
+}
+
+XclImpPageSettings& XclImpRoot::GetPageSettings() const
+{
+ return *mrImpData.mxPageSett;
+}
+
+XclImpDocViewSettings& XclImpRoot::GetDocViewSettings() const
+{
+ return *mrImpData.mxDocViewSett;
+}
+
+XclImpTabViewSettings& XclImpRoot::GetTabViewSettings() const
+{
+ return *mrImpData.mxTabViewSett;
+}
+
+OUString XclImpRoot::GetScAddInName( const OUString& rXclName )
+{
+ OUString aScName;
+ if( ScGlobal::GetAddInCollection()->GetCalcName( rXclName, aScName ) )
+ return aScName;
+ return rXclName;
+}
+
+void XclImpRoot::ReadCodeName( XclImpStream& rStrm, bool bGlobals )
+{
+ if( !(mrImpData.mbHasBasic && (GetBiff() == EXC_BIFF8)) )
+ return;
+
+ OUString aName = rStrm.ReadUniString();
+ if( aName.isEmpty() )
+ return;
+
+ if( bGlobals )
+ {
+ GetExtDocOptions().GetDocSettings().maGlobCodeName = aName;
+ GetDoc().SetCodeName( aName );
+ }
+ else
+ {
+ GetExtDocOptions().SetCodeName( GetCurrScTab(), aName );
+ GetDoc().SetCodeName( GetCurrScTab(), aName );
+ }
+}
+
+ScDocumentImport& XclImpRoot::GetDocImport()
+{
+ return *mrImpData.mxDocImport;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistream.cxx b/sc/source/filter/excel/xistream.cxx
new file mode 100644
index 0000000000..0a6c24aca6
--- /dev/null
+++ b/sc/source/filter/excel/xistream.cxx
@@ -0,0 +1,1084 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/docpasswordhelper.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <ftools.hxx>
+#include <xistream.hxx>
+#include <xlstring.hxx>
+#include <xiroot.hxx>
+
+#include <vector>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+// Decryption
+XclImpDecrypter::XclImpDecrypter() :
+ mnError( EXC_ENCR_ERROR_UNSUPP_CRYPT ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::XclImpDecrypter( const XclImpDecrypter& rSrc ) :
+ ::comphelper::IDocPasswordVerifier(),
+ mnError( rSrc.mnError ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::~XclImpDecrypter()
+{
+}
+
+XclImpDecrypterRef XclImpDecrypter::Clone() const
+{
+ XclImpDecrypterRef xNewDecr;
+ if( IsValid() )
+ xNewDecr.reset( OnClone() );
+ return xNewDecr;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
+{
+ o_rEncryptionData = OnVerifyPassword( rPassword );
+ mnError = o_rEncryptionData.hasElements() ? ERRCODE_NONE : ERRCODE_ABORT;
+ return o_rEncryptionData.hasElements() ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ bool bValid = OnVerifyEncryptionData( rEncryptionData );
+ mnError = bValid ? ERRCODE_NONE : ERRCODE_ABORT;
+ return bValid ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+void XclImpDecrypter::Update( const SvStream& rStrm, sal_uInt16 nRecSize )
+{
+ if( IsValid() )
+ {
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ if( (mnOldPos != nNewPos) || (mnRecSize != nRecSize) )
+ {
+ OnUpdate( mnOldPos, nNewPos, nRecSize );
+ mnOldPos = nNewPos;
+ mnRecSize = nRecSize;
+ }
+ }
+}
+
+sal_uInt16 XclImpDecrypter::Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+ if( pData && nBytes )
+ {
+ if( IsValid() )
+ {
+ Update( rStrm, mnRecSize );
+ nRet = OnRead( rStrm, static_cast< sal_uInt8* >( pData ), nBytes );
+ mnOldPos = rStrm.Tell();
+ }
+ else
+ nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pData, nBytes));
+ }
+ return nRet;
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash ) :
+ mnKey( nKey ),
+ mnHash( nHash )
+{
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc ) :
+ XclImpDecrypter( rSrc ),
+ maEncryptionData( rSrc.maEncryptionData ),
+ mnKey( rSrc.mnKey ),
+ mnHash( rSrc.mnHash )
+{
+ if( IsValid() )
+ maCodec.InitCodec( maEncryptionData );
+}
+
+XclImpBiff5Decrypter* XclImpBiff5Decrypter::OnClone() const
+{
+ return new XclImpBiff5Decrypter( *this );
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff5Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ /* Convert password to a byte string. TODO: this needs some fine tuning
+ according to the spec... */
+ OString aBytePassword = OUStringToOString( rPassword, osl_getThreadTextEncoding() );
+ sal_Int32 nLen = aBytePassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // init codec
+ maCodec.InitKey( reinterpret_cast<sal_uInt8 const *>(aBytePassword.getStr()) );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ {
+ maEncryptionData = maCodec.GetEncryptionData();
+
+ // since the export uses Std97 encryption always we have to request it here
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ sal_Int32 nInd = 0;
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&rPassword, &nInd](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( rPassword[nInd] );
+ ++nInd;
+ });
+
+ uno::Sequence< sal_Int8 > aDocId = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
+ OSL_ENSURE( aDocId.getLength() == 16, "Unexpected length of the sequence!" );
+
+ ::msfilter::MSCodec_Std97 aCodec97;
+ aCodec97.InitKey(aPassVect.data(), reinterpret_cast<sal_uInt8 const *>(aDocId.getConstArray()));
+
+ // merge the EncryptionData, there should be no conflicts
+ ::comphelper::SequenceAsHashMap aEncryptionHash( maEncryptionData );
+ aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
+ aEncryptionHash >> maEncryptionData;
+ }
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff5Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ maCodec.InitCodec( rEncryptionData );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff5Decrypter::OnUpdate( std::size_t /*nOldStrmPos*/, std::size_t nNewStrmPos, sal_uInt16 nRecSize )
+{
+ maCodec.InitCipher();
+ maCodec.Skip( (nNewStrmPos + nRecSize) & 0x0F );
+}
+
+sal_uInt16 XclImpBiff5Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pnData, nBytes));
+ maCodec.Decode( pnData, nRet );
+ return nRet;
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : maSalt(std::move(rSalt))
+ , maVerifier(std::move(rVerifier))
+ , maVerifierHash(std::move(rVerifierHash))
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter(const XclImpBiff8Decrypter& rSrc)
+ : XclImpDecrypter(rSrc)
+ , maEncryptionData(rSrc.maEncryptionData)
+ , maSalt(rSrc.maSalt)
+ , maVerifier(rSrc.maVerifier)
+ , maVerifierHash(rSrc.maVerifierHash)
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8StdDecrypter::XclImpBiff8StdDecrypter(const XclImpBiff8StdDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8StdDecrypter* XclImpBiff8StdDecrypter::OnClone() const
+{
+ return new XclImpBiff8StdDecrypter(*this);
+}
+
+XclImpBiff8CryptoAPIDecrypter::XclImpBiff8CryptoAPIDecrypter(const XclImpBiff8CryptoAPIDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8CryptoAPIDecrypter* XclImpBiff8CryptoAPIDecrypter::OnClone() const
+{
+ return new XclImpBiff8CryptoAPIDecrypter(*this);
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff8Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ sal_Int32 nLen = rPassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // copy string to sal_uInt16 array
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ const sal_Unicode* pcChar = rPassword.getStr();
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&pcChar](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( *pcChar );
+ ++pcChar;
+ });
+
+ // init codec
+ mpCodec->InitKey(aPassVect.data(), maSalt.data());
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = mpCodec->GetEncryptionData();
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff8Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ mpCodec->InitCodec( rEncryptionData );
+
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff8Decrypter::OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 /*nRecSize*/ )
+{
+ if( nNewStrmPos == nOldStrmPos )
+ return;
+
+ sal_uInt32 nOldBlock = GetBlock( nOldStrmPos );
+ sal_uInt16 nOldOffset = GetOffset( nOldStrmPos );
+
+ sal_uInt32 nNewBlock = GetBlock( nNewStrmPos );
+ sal_uInt16 nNewOffset = GetOffset( nNewStrmPos );
+
+ /* Rekey cipher, if block changed or if previous offset in same block. */
+ if( (nNewBlock != nOldBlock) || (nNewOffset < nOldOffset) )
+ {
+ mpCodec->InitCipher( nNewBlock );
+ nOldOffset = 0; // reset nOldOffset for next if() statement
+ }
+
+ /* Seek to correct offset. */
+ if( nNewOffset > nOldOffset )
+ mpCodec->Skip( nNewOffset - nOldOffset );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+
+ sal_uInt8* pnCurrData = pnData;
+ sal_uInt16 nBytesLeft = nBytes;
+ while( nBytesLeft )
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - GetOffset( rStrm.Tell() );
+ sal_uInt16 nDecBytes = ::std::min< sal_uInt16 >( nBytesLeft, nBlockLeft );
+
+ // read the block from stream
+ nRet = nRet + static_cast<sal_uInt16>(rStrm.ReadBytes(pnCurrData, nDecBytes));
+ // decode the block inplace
+ mpCodec->Decode( pnCurrData, nDecBytes, pnCurrData, nDecBytes );
+ if( GetOffset( rStrm.Tell() ) == 0 )
+ mpCodec->InitCipher( GetBlock( rStrm.Tell() ) );
+
+ pnCurrData += nDecBytes;
+ nBytesLeft = nBytesLeft - nDecBytes;
+ }
+
+ return nRet;
+}
+
+sal_uInt32 XclImpBiff8Decrypter::GetBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::GetOffset( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+// Stream
+XclImpStreamPos::XclImpStreamPos() :
+ mnPos( STREAM_SEEK_TO_BEGIN ),
+ mnNextPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrSize( 0 ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mbValid( false )
+{
+}
+
+void XclImpStreamPos::Set(
+ const SvStream& rStrm, std::size_t nNextPos, std::size_t nCurrSize,
+ sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
+ bool bValid )
+{
+ mnPos = rStrm.Tell();
+ mnNextPos = nNextPos;
+ mnCurrSize = nCurrSize;
+ mnRawRecId = nRawRecId;
+ mnRawRecSize = nRawRecSize;
+ mnRawRecLeft = nRawRecLeft;
+ mbValid = bValid;
+}
+
+void XclImpStreamPos::Get(
+ SvStream& rStrm, std::size_t& rnNextPos, std::size_t& rnCurrSize,
+ sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
+ bool& rbValid ) const
+{
+ rStrm.Seek( mnPos );
+ rnNextPos = mnNextPos;
+ rnCurrSize = mnCurrSize;
+ rnRawRecId = mnRawRecId;
+ rnRawRecSize = mnRawRecSize;
+ rnRawRecLeft = mnRawRecLeft;
+ rbValid = mbValid;
+}
+
+XclBiff XclImpStream::DetectBiffVersion( SvStream& rStrm )
+{
+ XclBiff eBiff = EXC_BIFF_UNKNOWN;
+
+ rStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ sal_uInt16 nBofId(0), nBofSize(0);
+ rStrm.ReadUInt16( nBofId ).ReadUInt16( nBofSize );
+
+ if (rStrm.good() && 4 <= nBofSize && nBofSize <= 16) switch( nBofId )
+ {
+ case EXC_ID2_BOF:
+ eBiff = EXC_BIFF2;
+ break;
+ case EXC_ID3_BOF:
+ eBiff = EXC_BIFF3;
+ break;
+ case EXC_ID4_BOF:
+ eBiff = EXC_BIFF4;
+ break;
+ case EXC_ID5_BOF:
+ {
+ sal_uInt16 nVersion(0);
+ rStrm.ReadUInt16( nVersion );
+ // #i23425# #i44031# #i62752# there are some *really* broken documents out there...
+ switch( nVersion & 0xFF00 )
+ {
+ case 0: eBiff = EXC_BIFF5; break; // #i44031# #i62752#
+ case EXC_BOF_BIFF2: eBiff = EXC_BIFF2; break;
+ case EXC_BOF_BIFF3: eBiff = EXC_BIFF3; break;
+ case EXC_BOF_BIFF4: eBiff = EXC_BIFF4; break;
+ case EXC_BOF_BIFF5: eBiff = EXC_BIFF5; break;
+ case EXC_BOF_BIFF8: eBiff = EXC_BIFF8; break;
+ default: SAL_WARN("sc", "XclImpStream::DetectBiffVersion - unknown BIFF version: 0x" << std::hex << nVersion );
+ }
+ }
+ break;
+ }
+ return eBiff;
+}
+
+XclImpStream::XclImpStream( SvStream& rInStrm, const XclImpRoot& rRoot ) :
+ mrStrm( rInStrm ),
+ mrRoot( rRoot ),
+ mnGlobRecId( EXC_ID_UNKNOWN ),
+ mbGlobValidRec( false ),
+ mbHasGlobPos( false ),
+ mnNextRecPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrRecSize( 0 ),
+ mnComplRecSize( 0 ),
+ mbHasComplRec( false ),
+ mnRecId( EXC_ID_UNKNOWN ),
+ mnAltContId( EXC_ID_UNKNOWN ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mcNulSubst( '?' ),
+ mbCont( true ),
+ mbUseDecr( false ),
+ mbValidRec( false ),
+ mbValid( false )
+{
+ mnStreamSize = mrStrm.TellEnd();
+ mrStrm.Seek( STREAM_SEEK_TO_BEGIN );
+}
+
+XclImpStream::~XclImpStream()
+{
+}
+
+bool XclImpStream::StartNextRecord()
+{
+ maPosStack.clear();
+
+ /* #i4266# Counter to ignore zero records (id==len==0) (i.e. the application
+ "Crystal Report" writes zero records between other records) */
+ std::size_t nZeroRecCount = 5;
+ bool bIsZeroRec = false;
+
+ do
+ {
+ mbValidRec = ReadNextRawRecHeader();
+ bIsZeroRec = (mnRawRecId == 0) && (mnRawRecSize == 0);
+ if( bIsZeroRec ) --nZeroRecCount;
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ }
+ while( mbValidRec && ((mbCont && IsContinueId( mnRawRecId )) || (bIsZeroRec && nZeroRecCount)) );
+
+ mbValidRec = mbValidRec && !bIsZeroRec;
+ mbValid = mbValidRec;
+ SetupRecord();
+
+ return mbValidRec;
+}
+
+bool XclImpStream::StartNextRecord( std::size_t nNextRecPos )
+{
+ mnNextRecPos = nNextRecPos;
+ return StartNextRecord();
+}
+
+void XclImpStream::ResetRecord( bool bContLookup, sal_uInt16 nAltContId )
+{
+ if( mbValidRec )
+ {
+ maPosStack.clear();
+ RestorePosition( maFirstRec );
+ mnCurrRecSize = mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !bContLookup;
+ mbCont = bContLookup;
+ mnAltContId = nAltContId;
+ EnableDecryption();
+ }
+}
+
+void XclImpStream::RewindRecord()
+{
+ mnNextRecPos = maFirstRec.GetPos();
+ mbValid = mbValidRec = false;
+}
+
+void XclImpStream::SetDecrypter( XclImpDecrypterRef const & xDecrypter )
+{
+ mxDecrypter = xDecrypter;
+ EnableDecryption();
+ SetupDecrypter();
+}
+
+void XclImpStream::CopyDecrypterFrom( const XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xNewDecr;
+ if( rStrm.mxDecrypter )
+ xNewDecr = rStrm.mxDecrypter->Clone();
+ SetDecrypter( xNewDecr );
+}
+
+void XclImpStream::EnableDecryption( bool bEnable )
+{
+ mbUseDecr = bEnable && mxDecrypter && mxDecrypter->IsValid();
+}
+
+void XclImpStream::PushPosition()
+{
+ maPosStack.emplace_back( );
+ StorePosition( maPosStack.back() );
+}
+
+void XclImpStream::PopPosition()
+{
+ OSL_ENSURE( !maPosStack.empty(), "XclImpStream::PopPosition - stack empty" );
+ if( !maPosStack.empty() )
+ {
+ RestorePosition( maPosStack.back() );
+ maPosStack.pop_back();
+ }
+}
+
+void XclImpStream::StoreGlobalPosition()
+{
+ StorePosition( maGlobPos );
+ mnGlobRecId = mnRecId;
+ mbGlobValidRec = mbValidRec;
+ mbHasGlobPos = true;
+}
+
+void XclImpStream::SeekGlobalPosition()
+{
+ OSL_ENSURE( mbHasGlobPos, "XclImpStream::SeekGlobalPosition - no position stored" );
+ if( mbHasGlobPos )
+ {
+ RestorePosition( maGlobPos );
+ mnRecId = mnGlobRecId;
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = !mbCont;
+ mbValidRec = mbGlobValidRec;
+ }
+}
+
+std::size_t XclImpStream::GetRecPos() const
+{
+ return mbValid ? (mnCurrRecSize - mnRawRecLeft) : EXC_REC_SEEK_TO_END;
+}
+
+std::size_t XclImpStream::GetRecSize()
+{
+ if( !mbHasComplRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // JumpToNextContinue() adds up mnCurrRecSize
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = true;
+ PopPosition();
+ }
+ return mnComplRecSize;
+}
+
+std::size_t XclImpStream::GetRecLeft()
+{
+ return mbValid ? (GetRecSize() - GetRecPos()) : 0;
+}
+
+sal_uInt16 XclImpStream::GetNextRecId()
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if( mbValidRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // skip following CONTINUE records
+ if( mnNextRecPos < mnStreamSize )
+ {
+ mrStrm.Seek( mnNextRecPos );
+ mrStrm.ReadUInt16( nRecId );
+ }
+ PopPosition();
+ }
+ return nRecId;
+}
+
+sal_uInt16 XclImpStream::PeekRecId( std::size_t nPos )
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if (mbValidRec && nPos < mnStreamSize)
+ {
+ sal_uInt64 const nCurPos = mrStrm.Tell();
+ mrStrm.Seek(nPos);
+ mrStrm.ReadUInt16( nRecId );
+ mrStrm.Seek(nCurPos);
+ }
+ return nRecId;
+}
+
+sal_uInt8 XclImpStream::ReaduInt8()
+{
+ sal_uInt8 nValue = 0;
+ if( EnsureRawReadSize( 1 ) )
+ {
+ if( mbUseDecr )
+ mxDecrypter->Read( mrStrm, &nValue, 1 );
+ else
+ mrStrm.ReadUChar( nValue );
+ --mnRawRecLeft;
+ }
+ return nValue;
+}
+
+sal_Int16 XclImpStream::ReadInt16()
+{
+ sal_Int16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = static_cast< sal_Int16 >( SVBT16ToUInt16( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_uInt16 XclImpStream::ReaduInt16()
+{
+ sal_uInt16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = SVBT16ToUInt16( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_Int32 XclImpStream::ReadInt32()
+{
+ sal_Int32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = static_cast< sal_Int32 >( SVBT32ToUInt32( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+sal_uInt32 XclImpStream::ReaduInt32()
+{
+ sal_uInt32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = SVBT32ToUInt32( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+double XclImpStream::ReadDouble()
+{
+ double nValue = 0;
+ if( EnsureRawReadSize( 8 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT64 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 8 );
+ nValue = SVBT64ToDouble( pnBuffer );
+ }
+ else
+ mrStrm.ReadDouble( nValue );
+ mnRawRecLeft -= 8;
+ }
+ return nValue;
+}
+
+std::size_t XclImpStream::Read( void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( mbValid && pData && (nBytes > 0) )
+ {
+ sal_uInt8* pnBuffer = static_cast< sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+
+ while( mbValid && (nBytesLeft > 0) )
+ {
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ sal_uInt16 nReadRet = ReadRawData( pnBuffer, nReadSize );
+ nRet += nReadRet;
+ mbValid = (nReadSize == nReadRet);
+ OSL_ENSURE( mbValid, "XclImpStream::Read - stream read error" );
+ pnBuffer += nReadRet;
+ nBytesLeft -= nReadRet;
+ if( mbValid && (nBytesLeft > 0) )
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Read - record overread" );
+ }
+ }
+ return nRet;
+}
+
+std::size_t XclImpStream::CopyToStream( SvStream& rOutStrm, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if (mbValid && nBytes)
+ {
+ const std::size_t nMaxBuffer = 4096;
+ std::vector<sal_uInt8> aBuffer(o3tl::sanitizing_min(nBytes, nMaxBuffer));
+ std::size_t nBytesLeft = nBytes;
+
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadSize = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ nRet += Read(aBuffer.data(), nReadSize);
+ // writing more bytes than read results in invalid memory access
+ SAL_WARN_IF(nRet != nReadSize, "sc", "read less bytes than requested");
+ rOutStrm.WriteBytes(aBuffer.data(), nReadSize);
+ nBytesLeft -= nReadSize;
+ }
+ }
+ return nRet;
+}
+
+void XclImpStream::CopyRecordToStream( SvStream& rOutStrm )
+{
+ if( mbValidRec )
+ {
+ PushPosition();
+ RestorePosition( maFirstRec );
+ CopyToStream( rOutStrm, GetRecSize() );
+ PopPosition();
+ }
+}
+
+void XclImpStream::Seek( std::size_t nPos )
+{
+ if( !mbValidRec )
+ return;
+
+ std::size_t nCurrPos = GetRecPos();
+ if( !mbValid || (nPos < nCurrPos) ) // from invalid state or backward
+ {
+ RestorePosition( maFirstRec );
+ Ignore( nPos );
+ }
+ else if( nPos > nCurrPos ) // forward
+ {
+ Ignore( nPos - nCurrPos );
+ }
+}
+
+void XclImpStream::Ignore( std::size_t nBytes )
+{
+ // implementation similar to Read(), but without really reading anything
+ std::size_t nBytesLeft = nBytes;
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ mbValid = checkSeek(mrStrm, mrStrm.Tell() + nReadSize);
+ mnRawRecLeft = mnRawRecLeft - nReadSize;
+ nBytesLeft -= nReadSize;
+ if (mbValid && nBytesLeft > 0)
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Ignore - record overread" );
+ }
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader(
+ bool& rb16Bit, bool& rbRich, bool& rbFareast,
+ sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags )
+{
+ OSL_ENSURE( !::get_flag( nFlags, EXC_STRF_UNKNOWN ), "XclImpStream::ReadUniStringExt - unknown flags" );
+ rb16Bit = ::get_flag( nFlags, EXC_STRF_16BIT );
+ rbRich = ::get_flag( nFlags, EXC_STRF_RICH );
+ rbFareast = ::get_flag( nFlags, EXC_STRF_FAREAST );
+ rnFormatRuns = rbRich ? ReaduInt16() : 0;
+ rnExtInf = rbFareast ? ReaduInt32() : 0;
+ return rnExtInf + 4 * rnFormatRuns;
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags )
+{
+ bool bRich, bFareast;
+ sal_uInt16 nCrun;
+ sal_uInt32 nExtInf;
+ return ReadUniStringExtHeader( rb16Bit, bRich, bFareast, nCrun, nExtInf, nFlags );
+}
+
+OUString XclImpStream::ReadRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ OUStringBuffer aRet(o3tl::sanitizing_min<sal_uInt16>(nChars, mnRawRecLeft / (b16Bit ? 2 : 1)));
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while (IsValid() && nCharsLeft)
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::ReadRawUniString - missing a byte" );
+ }
+ else
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+
+ std::unique_ptr<sal_Unicode[]> pcBuffer(new sal_Unicode[nReadSize + 1]);
+
+ sal_Unicode* pcUniChar = pcBuffer.get();
+ sal_Unicode* pcEndChar = pcBuffer.get() + nReadSize;
+
+ if( b16Bit )
+ {
+ sal_uInt16 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt16();
+ (*pcUniChar) = (nReadChar == EXC_NUL) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+ else
+ {
+ sal_uInt8 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt8();
+ (*pcUniChar) = (nReadChar == EXC_NUL_C) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+
+ *pcEndChar = '\0';
+ // this has the side-effect of only copying as far as the first null, which appears to be intentional. e.g.
+ // see tdf#124318
+ aRet.append( pcBuffer.get() );
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+
+ return aRet.makeStringAndClear();
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ OUString aRet( ReadRawUniString( nChars, b16Bit ) );
+ Ignore( nExtSize );
+ return aRet;
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars )
+{
+ return ReadUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadUniString()
+{
+ return ReadUniString( ReaduInt16() );
+}
+
+void XclImpStream::IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while( IsValid() && (nCharsLeft > 0) )
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::IgnoreRawUniString - missing a byte" );
+ Ignore( nReadSize * 2 );
+ }
+ else
+ {
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+ Ignore( nReadSize );
+ }
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ IgnoreRawUniString( nChars, b16Bit );
+ Ignore( nExtSize );
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars )
+{
+ IgnoreUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadRawByteString( sal_uInt16 nChars )
+{
+ nChars = GetMaxRawReadSize(nChars);
+ std::unique_ptr<char[]> pcBuffer(new char[ nChars + 1 ]);
+ sal_uInt16 nCharsRead = ReadRawData( pcBuffer.get(), nChars );
+ pcBuffer[ nCharsRead ] = '\0';
+ OUString aRet( pcBuffer.get(), strlen(pcBuffer.get()), mrRoot.GetTextEncoding() );
+ return aRet;
+}
+
+OUString XclImpStream::ReadByteString( bool b16BitLen )
+{
+ return ReadRawByteString( b16BitLen ? ReaduInt16() : ReaduInt8() );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpStream::StorePosition( XclImpStreamPos& rPos )
+{
+ rPos.Set( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+}
+
+void XclImpStream::RestorePosition( const XclImpStreamPos& rPos )
+{
+ rPos.Get( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+ SetupDecrypter();
+}
+
+bool XclImpStream::ReadNextRawRecHeader()
+{
+ bool bRet = checkSeek(mrStrm, mnNextRecPos) && (mnNextRecPos + 4 <= mnStreamSize);
+ if (bRet)
+ {
+ mrStrm.ReadUInt16( mnRawRecId ).ReadUInt16( mnRawRecSize );
+ bRet = mrStrm.good();
+ }
+ return bRet;
+}
+
+void XclImpStream::SetupDecrypter()
+{
+ if( mxDecrypter )
+ mxDecrypter->Update( mrStrm, mnRawRecSize );
+}
+
+void XclImpStream::SetupRawRecord()
+{
+ // pre: mnRawRecSize contains current raw record size
+ // pre: mrStrm points to start of raw record data
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ mnRawRecLeft = mnRawRecSize;
+ mnCurrRecSize += mnRawRecSize;
+ SetupDecrypter(); // decrypter works on raw record level
+}
+
+void XclImpStream::SetupRecord()
+{
+ mnRecId = mnRawRecId;
+ mnAltContId = EXC_ID_UNKNOWN;
+ mnCurrRecSize = 0;
+ mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !mbCont;
+ SetupRawRecord();
+ SetNulSubstChar();
+ EnableDecryption();
+ StorePosition( maFirstRec );
+}
+
+bool XclImpStream::IsContinueId( sal_uInt16 nRecId ) const
+{
+ return (nRecId == EXC_ID_CONT) || (nRecId == mnAltContId);
+}
+
+bool XclImpStream::JumpToNextContinue()
+{
+ mbValid = mbValid && mbCont && ReadNextRawRecHeader() && IsContinueId( mnRawRecId );
+ if( mbValid ) // do not setup a following non-CONTINUE record
+ SetupRawRecord();
+ return mbValid;
+}
+
+bool XclImpStream::JumpToNextStringContinue( bool& rb16Bit )
+{
+ OSL_ENSURE( mnRawRecLeft == 0, "XclImpStream::JumpToNextStringContinue - unexpected garbage" );
+
+ if( mbCont && (GetRecLeft() > 0) )
+ {
+ JumpToNextContinue();
+ }
+ else if( mnRecId == EXC_ID_CONT )
+ {
+ // CONTINUE handling is off, but we have started reading in a CONTINUE record
+ // -> start next CONTINUE for TXO import
+ mbValidRec = ReadNextRawRecHeader() && ((mnRawRecId != 0) || (mnRawRecSize > 0));
+ mbValid = mbValidRec && (mnRawRecId == EXC_ID_CONT);
+ // we really start a new record here - no chance to return to string origin
+ if( mbValid )
+ SetupRecord();
+ }
+ else
+ mbValid = false;
+
+ if( mbValid )
+ rb16Bit = ::get_flag( ReaduInt8(), EXC_STRF_16BIT );
+ return mbValid;
+}
+
+bool XclImpStream::EnsureRawReadSize( sal_uInt16 nBytes )
+{
+ if( mbValid && nBytes )
+ {
+ while( mbValid && !mnRawRecLeft ) JumpToNextContinue();
+ mbValid = mbValid && (nBytes <= mnRawRecLeft);
+ OSL_ENSURE( mbValid, "XclImpStream::EnsureRawReadSize - record overread" );
+ }
+ return mbValid;
+}
+
+sal_uInt16 XclImpStream::GetMaxRawReadSize( std::size_t nBytes ) const
+{
+ return static_cast<sal_uInt16>(o3tl::sanitizing_min<std::size_t>(nBytes, mnRawRecLeft));
+}
+
+sal_uInt16 XclImpStream::ReadRawData( void* pData, sal_uInt16 nBytes )
+{
+ OSL_ENSURE( (nBytes <= mnRawRecLeft), "XclImpStream::ReadRawData - record overread" );
+ sal_uInt16 nRet = 0;
+ if( mbUseDecr )
+ nRet = mxDecrypter->Read( mrStrm, pData, nBytes );
+ else
+ nRet = static_cast<sal_uInt16>(mrStrm.ReadBytes(pData, nBytes));
+ mnRawRecLeft = mnRawRecLeft - nRet;
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistring.cxx b/sc/source/filter/excel/xistring.cxx
new file mode 100644
index 0000000000..2ac049353d
--- /dev/null
+++ b/sc/source/filter/excel/xistring.cxx
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <utility>
+#include <xistring.hxx>
+#include <xlstyle.hxx>
+#include <xistream.hxx>
+#include <xiroot.hxx>
+#include <xltools.hxx>
+#include <sal/log.hxx>
+
+// Byte/Unicode strings =======================================================
+
+/** All allowed flags for import. */
+const XclStrFlags nAllowedFlags = XclStrFlags::EightBitLength | XclStrFlags::SmartFlags | XclStrFlags::SeparateFormats;
+
+XclImpString::XclImpString()
+{
+}
+
+XclImpString::XclImpString( OUString aString ) :
+ maString(std::move( aString ))
+{
+}
+
+void XclImpString::Read( XclImpStream& rStrm, XclStrFlags nFlags )
+{
+ if( !( nFlags & XclStrFlags::SeparateFormats ) )
+ maFormats.clear();
+
+ SAL_WARN_IF(
+ nFlags & ~nAllowedFlags, "sc.filter",
+ "XclImpString::Read - unknown flag");
+ bool b16BitLen = !( nFlags & XclStrFlags::EightBitLength );
+
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ // no integrated formatting in BIFF2-BIFF7
+ maString = rStrm.ReadByteString( b16BitLen );
+ break;
+
+ case EXC_BIFF8:
+ {
+ // --- string header ---
+ sal_uInt16 nChars = b16BitLen ? rStrm.ReaduInt16() : rStrm.ReaduInt8();
+ sal_uInt8 nFlagField = 0;
+ if( nChars || !( nFlags & XclStrFlags::SmartFlags ) )
+ nFlagField = rStrm.ReaduInt8();
+
+ bool b16Bit, bRich, bFarEast;
+ sal_uInt16 nRunCount;
+ sal_uInt32 nExtInf;
+ rStrm.ReadUniStringExtHeader( b16Bit, bRich, bFarEast, nRunCount, nExtInf, nFlagField );
+ // ignore the flags, they may be wrong
+
+ // --- character array ---
+ maString = rStrm.ReadRawUniString( nChars, b16Bit );
+
+ // --- formatting ---
+ if (nRunCount)
+ ReadFormats( rStrm, maFormats, nRunCount );
+
+ // --- extended (FarEast) information ---
+ rStrm.Ignore( nExtInf );
+ }
+ break;
+
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpString::AppendFormat( XclFormatRunVec& rFormats, sal_uInt16 nChar, sal_uInt16 nFontIdx )
+{
+ // #i33341# real life -- same character index may occur several times
+ OSL_ENSURE( rFormats.empty() || (rFormats.back().mnChar <= nChar), "XclImpString::AppendFormat - wrong char order" );
+ if( rFormats.empty() || (rFormats.back().mnChar < nChar) )
+ rFormats.emplace_back( nChar, nFontIdx );
+ else
+ rFormats.back().mnFontIdx = nFontIdx;
+}
+
+void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats )
+{
+ bool bBiff8 = rStrm.GetRoot().GetBiff() == EXC_BIFF8;
+ sal_uInt16 nRunCount = bBiff8 ? rStrm.ReaduInt16() : rStrm.ReaduInt8();
+ ReadFormats( rStrm, rFormats, nRunCount );
+}
+
+void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nRunCount )
+{
+ rFormats.clear();
+
+ size_t nElementSize = rStrm.GetRoot().GetBiff() == EXC_BIFF8 ? 4 : 2;
+ size_t nAvailableBytes = rStrm.GetRecLeft();
+ size_t nMaxElements = nAvailableBytes / nElementSize;
+ if (nRunCount > nMaxElements)
+ {
+ SAL_WARN("sc.filter", "XclImpString::ReadFormats - more formats claimed than stream could contain");
+ rStrm.SetSvStreamError(SVSTREAM_FILEFORMAT_ERROR);
+ return;
+ }
+
+ rFormats.reserve( nRunCount );
+ /* #i33341# real life -- same character index may occur several times
+ -> use AppendFormat() to validate formats */
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt16 nChar = rStrm.ReaduInt16();
+ sal_uInt16 nFontIdx = rStrm.ReaduInt16();
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+ }
+ else
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt8 nChar = rStrm.ReaduInt8();
+ sal_uInt8 nFontIdx = rStrm.ReaduInt8();
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+ }
+}
+
+void XclImpString::ReadObjFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nFormatSize )
+{
+ // number of formatting runs, each takes 8 bytes
+ sal_uInt16 nRunCount = nFormatSize / 8;
+ rFormats.clear();
+ rFormats.reserve( nRunCount );
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt16 nChar = rStrm.ReaduInt16();
+ sal_uInt16 nFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+}
+
+// String iterator ============================================================
+
+XclImpStringIterator::XclImpStringIterator( const XclImpString& rString ) :
+ mrText( rString.GetText() ),
+ mrFormats( rString.GetFormats() ),
+ mnPortion( 0 ),
+ mnTextBeg( 0 ),
+ mnTextEnd( 0 ),
+ mnFormatsBeg( 0 ),
+ mnFormatsEnd( 0 )
+{
+ // first portion is formatted, adjust vector index to next portion
+ if( !mrFormats.empty() && (mrFormats.front().mnChar == 0) )
+ ++mnFormatsEnd;
+ // find end position of the first portion
+ mnTextEnd = (mnFormatsEnd < mrFormats.size() ?
+ mrFormats[ mnFormatsEnd ].mnChar : mrText.getLength() );
+}
+
+OUString XclImpStringIterator::GetPortionText() const
+{
+ return mrText.copy( mnTextBeg, mnTextEnd - mnTextBeg );
+}
+
+sal_uInt16 XclImpStringIterator::GetPortionFont() const
+{
+ return (mnFormatsBeg < mnFormatsEnd) ? mrFormats[ mnFormatsBeg ].mnFontIdx : EXC_FONT_NOTFOUND;
+}
+
+XclImpStringIterator& XclImpStringIterator::operator++()
+{
+ if( Is() )
+ {
+ ++mnPortion;
+ do
+ {
+ // indexes into vector of formatting runs
+ if( mnFormatsBeg < mnFormatsEnd )
+ ++mnFormatsBeg;
+ if( mnFormatsEnd < mrFormats.size() )
+ ++mnFormatsEnd;
+ // character positions of next portion
+ mnTextBeg = mnTextEnd;
+ mnTextEnd = (mnFormatsEnd < mrFormats.size()) ?
+ mrFormats[ mnFormatsEnd ].mnChar : mrText.getLength();
+ }
+ while( Is() && (mnTextBeg == mnTextEnd) );
+ }
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistyle.cxx b/sc/source/filter/excel/xistyle.cxx
new file mode 100644
index 0000000000..21b86a4ed4
--- /dev/null
+++ b/sc/source/filter/excel/xistyle.cxx
@@ -0,0 +1,2085 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xistyle.hxx>
+#include <sfx2/objsh.hxx>
+#include <svtools/ctrltool.hxx>
+#include <editeng/editobj.hxx>
+#include <scitems.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/editids.hrc>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/outdev.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <attarray.hxx>
+#include <xladdress.hxx>
+#include <xlcontent.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xistream.hxx>
+#include <xicontent.hxx>
+
+#include <root.hxx>
+#include <colrowst.hxx>
+
+#include <string_view>
+#include <vector>
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <svl/numformat.hxx>
+#include <o3tl/string_view.hxx>
+
+using ::std::vector;
+using namespace ::com::sun::star;
+
+typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE;
+typedef ::std::vector< Color > ColorVec;
+
+namespace {
+
+class PaletteIndex : public XIndexAccess_BASE
+{
+public:
+ explicit PaletteIndex( ColorVec&& rColorTable ) : maColor( std::move(rColorTable) ) {}
+
+ // Methods XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount() override
+ {
+ return maColor.size();
+ }
+
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ //--Index; // apparently the palette is already 1 based
+ return uno::Any( sal_Int32( maColor[ Index ] ) );
+ }
+
+ // Methods XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return ::cppu::UnoType<sal_Int32>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return (!maColor.empty());
+ }
+
+private:
+ ColorVec maColor;
+};
+
+}
+
+void
+XclImpPalette::ExportPalette()
+{
+ ScDocShell* pDocShell = mrRoot.GetDocShell();
+ if(!pDocShell)
+ return;
+
+ // copy values in color palette
+ sal_Int16 nColors = maColorTable.size();
+ ColorVec aColors;
+ aColors.resize( nColors );
+ for( sal_uInt16 nIndex = 0; nIndex < nColors; ++nIndex )
+ aColors[ nIndex ] = GetColor( nIndex );
+
+ ScModelObj* pModel = pDocShell->GetModel();
+ if ( pModel )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( new PaletteIndex( std::move(aColors) ) );
+ pModel->setPropertyValue( "ColorPalette", uno::Any( xIndex ) );
+ }
+
+}
+// PALETTE record - color information =========================================
+
+XclImpPalette::XclImpPalette( const XclImpRoot& rRoot ) :
+ XclDefaultPalette( rRoot ), mrRoot( rRoot )
+{
+}
+
+void XclImpPalette::Initialize()
+{
+ maColorTable.clear();
+}
+
+Color XclImpPalette::GetColor( sal_uInt16 nXclIndex ) const
+{
+ if( nXclIndex >= EXC_COLOR_USEROFFSET )
+ {
+ sal_uInt32 nIx = nXclIndex - EXC_COLOR_USEROFFSET;
+ if( nIx < maColorTable.size() )
+ return maColorTable[ nIx ];
+ }
+ return GetDefColor( nXclIndex );
+}
+
+void XclImpPalette::ReadPalette( XclImpStream& rStrm )
+{
+ sal_uInt16 nCount;
+ nCount = rStrm.ReaduInt16();
+
+ const size_t nMinRecordSize = 4;
+ const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+
+ maColorTable.resize( nCount );
+ Color aColor;
+ for( sal_uInt16 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ rStrm >> aColor;
+ maColorTable[ nIndex ] = aColor;
+ }
+ ExportPalette();
+}
+
+// FONT record - font information =============================================
+XclImpFont::XclImpFont( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mbHasCharSet( false ),
+ mbHasWstrn( true ),
+ mbHasAsian( false ),
+ mbHasCmplx( false )
+{
+ SetAllUsedFlags( false );
+}
+
+XclImpFont::XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData ) :
+ XclImpRoot( rRoot )
+{
+ SetFontData( rFontData, false );
+}
+
+void XclImpFont::SetAllUsedFlags( bool bUsed )
+{
+ mbFontNameUsed = mbHeightUsed = mbColorUsed = mbWeightUsed = mbEscapemUsed =
+ mbUnderlUsed = mbItalicUsed = mbStrikeUsed = mbOutlineUsed = mbShadowUsed = bUsed;
+}
+
+void XclImpFont::SetFontData( const XclFontData& rFontData, bool bHasCharSet )
+{
+ maData = rFontData;
+ mbHasCharSet = bHasCharSet;
+ if( !maData.maStyle.isEmpty() )
+ {
+ if( ScDocShell* pDocShell = GetDocShell() )
+ {
+ if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
+ pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
+ {
+ if( const FontList* pFontList = pInfoItem->GetFontList() )
+ {
+ FontMetric aFontMetric( pFontList->Get( maData.maName, maData.maStyle ) );
+ maData.SetScWeight( aFontMetric.GetWeight() );
+ maData.SetScPosture( aFontMetric.GetItalic() );
+ }
+ }
+ }
+ maData.maStyle.clear();
+ }
+ GuessScriptType();
+ SetAllUsedFlags( true );
+}
+
+rtl_TextEncoding XclImpFont::GetFontEncoding() const
+{
+ // #i63105# use text encoding from FONT record
+ // #i67768# BIFF2-BIFF4 FONT records do not contain character set
+ rtl_TextEncoding eFontEnc = mbHasCharSet ? maData.GetFontEncoding() : GetTextEncoding();
+ return (eFontEnc == RTL_TEXTENCODING_DONTKNOW) ? GetTextEncoding() : eFontEnc;
+}
+
+void XclImpFont::ReadFont( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ ReadFontData2( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ ReadFontData2( rStrm );
+ ReadFontColor( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF5:
+ ReadFontData5( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF8:
+ ReadFontData5( rStrm );
+ ReadFontName8( rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ return;
+ }
+ GuessScriptType();
+ SetAllUsedFlags( true );
+}
+
+void XclImpFont::ReadEfont( XclImpStream& rStrm )
+{
+ ReadFontColor( rStrm );
+}
+
+void XclImpFont::ReadCFFontBlock( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ rStrm.Ignore( 64 );
+ sal_uInt32 nHeight = rStrm.ReaduInt32();
+ sal_uInt32 nStyle = rStrm.ReaduInt32();
+ sal_uInt16 nWeight = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 ); //nEscapem
+ sal_uInt8 nUnderl = rStrm.ReaduInt8();
+ rStrm.Ignore( 3 );
+ sal_uInt32 nColor = rStrm.ReaduInt32();
+ rStrm.Ignore( 4 );
+ sal_uInt32 nFontFlags1 = rStrm.ReaduInt32();
+ rStrm.Ignore( 4 ); //nFontFlags2
+ sal_uInt32 nFontFlags3 = rStrm.ReaduInt32();
+ rStrm.Ignore( 18 );
+
+ if( (mbHeightUsed = (nHeight <= 0x7FFF)) )
+ maData.mnHeight = static_cast< sal_uInt16 >( nHeight );
+ if( (mbWeightUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE ) && (nWeight < 0x7FFF)) )
+ maData.mnWeight = nWeight;
+ if( (mbItalicUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE )) )
+ maData.mbItalic = ::get_flag( nStyle, EXC_CF_FONT_STYLE );
+ if( (mbUnderlUsed = !::get_flag( nFontFlags3, EXC_CF_FONT_UNDERL ) && (nUnderl <= 0x7F)) )
+ maData.mnUnderline = nUnderl;
+ if( (mbColorUsed = (nColor <= 0x7FFF)) )
+ maData.maComplexColor.setColor(GetPalette().GetColor(sal_uInt16(nColor)));
+ if( (mbStrikeUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT )) )
+ maData.mbStrikeout = ::get_flag( nStyle, EXC_CF_FONT_STRIKEOUT );
+}
+
+void XclImpFont::FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType, bool bSkipPoolDefs ) const
+{
+ // true = edit engine Which-IDs (EE_CHAR_*); false = Calc Which-IDs (ATTR_*)
+ bool bEE = eType != XclFontItemType::Cell;
+
+// item = the item to put into the item set
+// sc_which = the Calc Which-ID of the item
+// ee_which = the edit engine Which-ID of the item
+#define PUTITEM( item, sc_which, ee_which ) \
+ ScfTools::PutItem( rItemSet, item, (bEE ? (static_cast<sal_uInt16>(ee_which)) : (sc_which)), bSkipPoolDefs )
+
+// Font item
+ if( mbFontNameUsed )
+ {
+ rtl_TextEncoding eFontEnc = maData.GetFontEncoding();
+ rtl_TextEncoding eTempTextEnc = (bEE && (eFontEnc == GetTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+
+ //add corresponding pitch for FontFamily
+ FontPitch ePitch = PITCH_DONTKNOW;
+ FontFamily eFtFamily = maData.GetScFamily( GetTextEncoding() );
+ switch( eFtFamily ) //refer http://msdn.microsoft.com/en-us/library/aa246306(v=VS.60).aspx
+ {
+ case FAMILY_ROMAN: ePitch = PITCH_VARIABLE; break;
+ case FAMILY_SWISS: ePitch = PITCH_VARIABLE; break;
+ case FAMILY_MODERN: ePitch = PITCH_FIXED; break;
+ default: break;
+ }
+ SvxFontItem aFontItem( eFtFamily , maData.maName, OUString(), ePitch, eTempTextEnc, ATTR_FONT );
+
+ // set only for valid script types
+ if( mbHasWstrn )
+ PUTITEM( aFontItem, ATTR_FONT, EE_CHAR_FONTINFO );
+ if( mbHasAsian )
+ PUTITEM( aFontItem, ATTR_CJK_FONT, EE_CHAR_FONTINFO_CJK );
+ if( mbHasCmplx )
+ PUTITEM( aFontItem, ATTR_CTL_FONT, EE_CHAR_FONTINFO_CTL );
+ }
+
+// Font height (for all script types)
+ if( mbHeightUsed )
+ {
+ sal_Int32 nHeight = maData.mnHeight;
+ if( bEE && (eType != XclFontItemType::HeaderFooter) ) // do not convert header/footer height
+ nHeight = convertTwipToMm100(nHeight);
+
+ SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT );
+ PUTITEM( aHeightItem, ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT );
+ PUTITEM( aHeightItem, ATTR_CJK_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CJK );
+ PUTITEM( aHeightItem, ATTR_CTL_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CTL );
+ }
+
+// Font color - pass AUTO_COL to item
+ if( mbColorUsed )
+ PUTITEM(SvxColorItem(maData.maComplexColor.getFinalColor(), maData.maComplexColor, ATTR_FONT_COLOR ), ATTR_FONT_COLOR, EE_CHAR_COLOR);
+
+// Font weight (for all script types)
+ if( mbWeightUsed )
+ {
+ SvxWeightItem aWeightItem( maData.GetScWeight(), ATTR_FONT_WEIGHT );
+ PUTITEM( aWeightItem, ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT );
+ PUTITEM( aWeightItem, ATTR_CJK_FONT_WEIGHT, EE_CHAR_WEIGHT_CJK );
+ PUTITEM( aWeightItem, ATTR_CTL_FONT_WEIGHT, EE_CHAR_WEIGHT_CTL );
+ }
+
+// Font underline
+ if( mbUnderlUsed )
+ {
+ SvxUnderlineItem aUnderlItem( maData.GetScUnderline(), ATTR_FONT_UNDERLINE );
+ PUTITEM( aUnderlItem, ATTR_FONT_UNDERLINE, EE_CHAR_UNDERLINE );
+ }
+
+// Font posture (for all script types)
+ if( mbItalicUsed )
+ {
+ SvxPostureItem aPostItem( maData.GetScPosture(), ATTR_FONT_POSTURE );
+ PUTITEM( aPostItem, ATTR_FONT_POSTURE, EE_CHAR_ITALIC );
+ PUTITEM( aPostItem, ATTR_CJK_FONT_POSTURE, EE_CHAR_ITALIC_CJK );
+ PUTITEM( aPostItem, ATTR_CTL_FONT_POSTURE, EE_CHAR_ITALIC_CTL );
+ }
+
+// Boolean attributes crossed out, contoured, shadowed
+ if( mbStrikeUsed )
+ PUTITEM( SvxCrossedOutItem( maData.GetScStrikeout(), ATTR_FONT_CROSSEDOUT ), ATTR_FONT_CROSSEDOUT, EE_CHAR_STRIKEOUT );
+ if( mbOutlineUsed )
+ PUTITEM( SvxContourItem( maData.mbOutline, ATTR_FONT_CONTOUR ), ATTR_FONT_CONTOUR, EE_CHAR_OUTLINE );
+ if( mbShadowUsed )
+ PUTITEM( SvxShadowedItem( maData.mbShadow, ATTR_FONT_SHADOWED ), ATTR_FONT_SHADOWED, EE_CHAR_SHADOW );
+
+// Super-/subscript: only on edit engine objects
+ if( mbEscapemUsed && bEE )
+ rItemSet.Put( SvxEscapementItem( maData.GetScEscapement(), EE_CHAR_ESCAPEMENT ) );
+
+#undef PUTITEM
+}
+
+void XclImpFont::WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, const Color* pFontColor ) const
+{
+ GetFontPropSetHelper().WriteFontProperties(
+ rPropSet, eType, maData, mbHasWstrn, mbHasAsian, mbHasCmplx, pFontColor );
+}
+
+void XclImpFont::ReadFontData2( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ maData.mnHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ maData.mnWeight = ::get_flagvalue( nFlags, EXC_FONTATTR_BOLD, EXC_FONTWGHT_BOLD, EXC_FONTWGHT_NORMAL );
+ maData.mnUnderline = ::get_flagvalue( nFlags, EXC_FONTATTR_UNDERLINE, EXC_FONTUNDERL_SINGLE, EXC_FONTUNDERL_NONE );
+ maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
+ maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
+ maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
+ maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
+ mbHasCharSet = false;
+}
+
+void XclImpFont::ReadFontData5( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+
+ maData.mnHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+ ReadFontColor( rStrm );
+ maData.mnWeight = rStrm.ReaduInt16();
+ maData.mnEscapem = rStrm.ReaduInt16();
+ maData.mnUnderline = rStrm.ReaduInt8();
+ maData.mnFamily = rStrm.ReaduInt8();
+ maData.mnCharSet = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+
+ maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
+ maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
+ maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
+ maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
+ mbHasCharSet = maData.mnCharSet != 0;
+}
+
+void XclImpFont::ReadFontColor( XclImpStream& rStrm )
+{
+ maData.maComplexColor.setColor(GetPalette().GetColor(rStrm.ReaduInt16()));
+}
+
+void XclImpFont::ReadFontName2( XclImpStream& rStrm )
+{
+ maData.maName = rStrm.ReadByteString( false );
+}
+
+void XclImpFont::ReadFontName8( XclImpStream& rStrm )
+{
+ maData.maName = rStrm.ReadUniString( rStrm.ReaduInt8() );
+}
+
+void XclImpFont::GuessScriptType()
+{
+ mbHasWstrn = true;
+ mbHasAsian = mbHasCmplx = false;
+
+ // find the script types for which the font contains characters
+ OutputDevice* pPrinter = GetPrinter();
+ if(!pPrinter)
+ return;
+
+ vcl::Font aFont( maData.maName, Size( 0, 10 ) );
+ FontCharMapRef xFontCharMap;
+
+ pPrinter->SetFont( aFont );
+ if( !pPrinter->GetFontCharMap( xFontCharMap ) )
+ return;
+
+ // CJK fonts
+ mbHasAsian =
+ xFontCharMap->HasChar( 0x3041 ) || // 3040-309F: Hiragana
+ xFontCharMap->HasChar( 0x30A1 ) || // 30A0-30FF: Katakana
+ xFontCharMap->HasChar( 0x3111 ) || // 3100-312F: Bopomofo
+ xFontCharMap->HasChar( 0x3131 ) || // 3130-318F: Hangul Compatibility Jamo
+ xFontCharMap->HasChar( 0x3301 ) || // 3300-33FF: CJK Compatibility
+ xFontCharMap->HasChar( 0x3401 ) || // 3400-4DBF: CJK Unified Ideographs Extension A
+ xFontCharMap->HasChar( 0x4E01 ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFontCharMap->HasChar( 0x7E01 ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFontCharMap->HasChar( 0xA001 ) || // A001-A48F: Yi Syllables
+ xFontCharMap->HasChar( 0xAC01 ) || // AC00-D7AF: Hangul Syllables
+ xFontCharMap->HasChar( 0xCC01 ) || // AC00-D7AF: Hangul Syllables
+ xFontCharMap->HasChar( 0xF901 ) || // F900-FAFF: CJK Compatibility Ideographs
+ xFontCharMap->HasChar( 0xFF71 ); // FF00-FFEF: Halfwidth/Fullwidth Forms
+ // CTL fonts
+ mbHasCmplx =
+ xFontCharMap->HasChar( 0x05D1 ) || // 0590-05FF: Hebrew
+ xFontCharMap->HasChar( 0x0631 ) || // 0600-06FF: Arabic
+ xFontCharMap->HasChar( 0x0721 ) || // 0700-074F: Syriac
+ xFontCharMap->HasChar( 0x0911 ) || // 0900-0DFF: Indic scripts
+ xFontCharMap->HasChar( 0x0E01 ) || // 0E00-0E7F: Thai
+ xFontCharMap->HasChar( 0xFB21 ) || // FB1D-FB4F: Hebrew Presentation Forms
+ xFontCharMap->HasChar( 0xFB51 ) || // FB50-FDFF: Arabic Presentation Forms-A
+ xFontCharMap->HasChar( 0xFE71 ); // FE70-FEFF: Arabic Presentation Forms-B
+ // Western fonts
+ mbHasWstrn = (!mbHasAsian && !mbHasCmplx) || xFontCharMap->HasChar( 'A' );
+}
+
+XclImpFontBuffer::XclImpFontBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maFont4( rRoot ),
+ maCtrlFont( rRoot )
+{
+ Initialize();
+
+ // default font for form controls without own font information
+ XclFontData aCtrlFontData;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ aCtrlFontData.maName = "Helv";
+ aCtrlFontData.mnHeight = 160;
+ aCtrlFontData.mnWeight = EXC_FONTWGHT_BOLD;
+ break;
+ case EXC_BIFF8:
+ aCtrlFontData.maName = "Tahoma";
+ aCtrlFontData.mnHeight = 160;
+ aCtrlFontData.mnWeight = EXC_FONTWGHT_NORMAL;
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ maCtrlFont.SetFontData( aCtrlFontData, false );
+}
+
+void XclImpFontBuffer::Initialize()
+{
+ maFontList.clear();
+
+ // application font for column width calculation, later filled with first font from font list
+ XclFontData aAppFontData;
+ aAppFontData.maName = "Arial";
+ aAppFontData.mnHeight = 200;
+ aAppFontData.mnWeight = EXC_FONTWGHT_NORMAL;
+ UpdateAppFont( aAppFontData, false );
+}
+
+const XclImpFont* XclImpFontBuffer::GetFont( sal_uInt16 nFontIndex ) const
+{
+ /* Font with index 4 is not stored in an Excel file, but used e.g. by
+ BIFF5 form pushbutton objects. It is the bold default font.
+ This also means that entries above 4 are out by one in the list. */
+
+ if (nFontIndex == 4)
+ return &maFont4;
+
+ if (nFontIndex < 4)
+ {
+ // Font ID is zero-based when it's less than 4.
+ return nFontIndex >= maFontList.size() ? nullptr : &maFontList[nFontIndex];
+ }
+
+ // Font ID is greater than 4. It is now 1-based.
+ return nFontIndex > maFontList.size() ? nullptr : &maFontList[nFontIndex-1];
+}
+
+void XclImpFontBuffer::ReadFont( XclImpStream& rStrm )
+{
+ maFontList.emplace_back( GetRoot() );
+ XclImpFont& rFont = maFontList.back();
+ rFont.ReadFont( rStrm );
+
+ if( maFontList.size() == 1 )
+ {
+ UpdateAppFont( rFont.GetFontData(), rFont.HasCharSet() );
+ }
+}
+
+void XclImpFontBuffer::ReadEfont( XclImpStream& rStrm )
+{
+ if( !maFontList.empty() )
+ maFontList.back().ReadEfont( rStrm );
+}
+
+void XclImpFontBuffer::FillToItemSet(
+ SfxItemSet& rItemSet, XclFontItemType eType,
+ sal_uInt16 nFontIdx, bool bSkipPoolDefs ) const
+{
+ if( const XclImpFont* pFont = GetFont( nFontIdx ) )
+ pFont->FillToItemSet( rItemSet, eType, bSkipPoolDefs );
+}
+
+void XclImpFontBuffer::WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, sal_uInt16 nFontIdx, const Color* pFontColor ) const
+{
+ if( const XclImpFont* pFont = GetFont( nFontIdx ) )
+ pFont->WriteFontProperties( rPropSet, eType, pFontColor );
+}
+
+void XclImpFontBuffer::WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const
+{
+ maCtrlFont.WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL );
+}
+
+void XclImpFontBuffer::UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet )
+{
+ maAppFont = rFontData;
+ // #i3006# Calculate the width of '0' from first font and current printer.
+ SetCharWidth( maAppFont );
+
+ // font 4 is bold font 0
+ XclFontData aFont4Data( maAppFont );
+ aFont4Data.mnWeight = EXC_FONTWGHT_BOLD;
+ maFont4.SetFontData( aFont4Data, bHasCharSet );
+}
+
+// FORMAT record - number formats =============================================
+
+XclImpNumFmtBuffer::XclImpNumFmtBuffer( const XclImpRoot& rRoot ) :
+ XclNumFmtBuffer( rRoot ),
+ XclImpRoot( rRoot ),
+ mnNextXclIdx( 0 )
+{
+}
+
+void XclImpNumFmtBuffer::Initialize()
+{
+ maIndexMap.clear();
+ mnNextXclIdx = 0;
+ InitializeImport(); // base class
+}
+
+void XclImpNumFmtBuffer::ReadFormat( XclImpStream& rStrm )
+{
+ OUString aFormat;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF4:
+ rStrm.Ignore( 2 ); // in BIFF4 the index field exists, but is undefined
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF5:
+ mnNextXclIdx = rStrm.ReaduInt16();
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF8:
+ mnNextXclIdx = rStrm.ReaduInt16();
+ aFormat = rStrm.ReadUniString();
+ break;
+
+ default:
+ DBG_ERROR_BIFF();
+ return;
+ }
+
+ if( mnNextXclIdx < 0xFFFF )
+ {
+ InsertFormat( mnNextXclIdx, aFormat );
+ ++mnNextXclIdx;
+ }
+}
+
+sal_uInt16 XclImpNumFmtBuffer::ReadCFFormat( XclImpStream& rStrm, bool bIFmt )
+{
+ // internal number format ?
+ if(bIFmt)
+ {
+ rStrm.Ignore(1);
+ sal_uInt8 nIndex;
+ nIndex = rStrm.ReaduInt8();
+ return nIndex;
+ }
+ else
+ {
+ OUString aFormat = rStrm.ReadUniString();
+ InsertFormat( mnNextXclIdx, aFormat );
+ ++mnNextXclIdx;
+ return mnNextXclIdx - 1;
+ }
+}
+
+void XclImpNumFmtBuffer::CreateScFormats()
+{
+ OSL_ENSURE( maIndexMap.empty(), "XclImpNumFmtBuffer::CreateScFormats - already created" );
+
+ SvNumberFormatter& rFormatter = GetFormatter();
+ for( const auto& [rXclNumFmt, rNumFmt] : GetFormatMap() )
+ {
+ // insert/convert the Excel number format
+ sal_uInt32 nKey;
+ if( !rNumFmt.maFormat.isEmpty() )
+ {
+ OUString aFormat( rNumFmt.maFormat );
+ sal_Int32 nCheckPos;
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ rFormatter.PutandConvertEntry( aFormat, nCheckPos,
+ nType, nKey, LANGUAGE_ENGLISH_US, rNumFmt.meLanguage, false);
+ }
+ else
+ nKey = rFormatter.GetFormatIndex( rNumFmt.meOffset, rNumFmt.meLanguage );
+
+ // insert the resulting format key into the Excel->Calc index map
+ maIndexMap[ rXclNumFmt ] = nKey;
+ }
+}
+
+sal_uInt32 XclImpNumFmtBuffer::GetScFormat( sal_uInt16 nXclNumFmt ) const
+{
+ XclImpIndexMap::const_iterator aIt = maIndexMap.find( nXclNumFmt );
+ return (aIt != maIndexMap.end()) ? aIt->second : NUMBERFORMAT_ENTRY_NOT_FOUND;
+}
+
+void XclImpNumFmtBuffer::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt, bool bSkipPoolDefs ) const
+{
+ sal_uInt32 nScNumFmt = GetScFormat( nXclNumFmt );
+ if( nScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ nScNumFmt = GetStdScNumFmt();
+ FillScFmtToItemSet( rItemSet, nScNumFmt, bSkipPoolDefs );
+}
+
+void XclImpNumFmtBuffer::FillScFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nScNumFmt, bool bSkipPoolDefs ) const
+{
+ OSL_ENSURE( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND, "XclImpNumFmtBuffer::FillScFmtToItemSet - invalid number format" );
+ ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs );
+ if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, false ) == SfxItemState::SET )
+ ScGlobal::AddLanguage( rItemSet, GetFormatter() );
+}
+
+// XF, STYLE record - Cell formatting =========================================
+
+void XclImpCellProt::FillFromXF2( sal_uInt8 nNumFmt )
+{
+ mbLocked = ::get_flag( nNumFmt, EXC_XF2_LOCKED );
+ mbHidden = ::get_flag( nNumFmt, EXC_XF2_HIDDEN );
+}
+
+void XclImpCellProt::FillFromXF3( sal_uInt16 nProt )
+{
+ mbLocked = ::get_flag( nProt, EXC_XF_LOCKED );
+ mbHidden = ::get_flag( nProt, EXC_XF_HIDDEN );
+}
+
+void XclImpCellProt::FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ ScfTools::PutItem( rItemSet, ScProtectionAttr( mbLocked, mbHidden ), bSkipPoolDefs );
+}
+
+void XclImpCellAlign::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nFlags, 0, 3 );
+}
+
+void XclImpCellAlign::FillFromXF3( sal_uInt16 nAlign )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); // new in BIFF3
+}
+
+void XclImpCellAlign::FillFromXF4( sal_uInt16 nAlign )
+{
+ FillFromXF3( nAlign );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 2 ); // new in BIFF4
+ mnOrient = ::extract_value< sal_uInt8 >( nAlign, 6, 2 ); // new in BIFF4
+}
+
+void XclImpCellAlign::FillFromXF5( sal_uInt16 nAlign )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
+ mnOrient = ::extract_value< sal_uInt8 >( nAlign, 8, 2 );
+}
+
+void XclImpCellAlign::FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
+ mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 ); // new in BIFF8
+ mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 ); // new in BIFF8
+ mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK ); // new in BIFF8
+ mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 ); // new in BIFF8
+}
+
+void XclImpCellAlign::FillFromCF( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
+{
+ mnHorAlign = extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mbLineBreak = get_flag< sal_uInt8 >( nAlign, EXC_XF_LINEBREAK );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 );
+ mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 );
+ mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK );
+ mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 );
+}
+
+void XclImpCellAlign::FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs ) const
+{
+ // horizontal alignment
+ ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScHorJustifyMethod(), ATTR_HOR_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // text wrap (#i74508# always if vertical alignment is justified or distributed)
+ bool bLineBreak = mbLineBreak || (mnVerAlign == EXC_XF_VER_JUSTIFY) || (mnVerAlign == EXC_XF_VER_DISTRIB);
+ ScfTools::PutItem( rItemSet, ScLineBreakCell( bLineBreak ), bSkipPoolDefs );
+
+ // vertical alignment
+ ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScVerJustifyMethod(), ATTR_VER_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // indent
+ sal_uInt16 nScIndent = mnIndent * 200; // 1 Excel unit == 10 pt == 200 twips
+ ScfTools::PutItem( rItemSet, ScIndentItem( nScIndent ), bSkipPoolDefs );
+
+ // shrink to fit
+ ScfTools::PutItem( rItemSet, ScShrinkToFitCell( mbShrink ), bSkipPoolDefs );
+
+ // text orientation/rotation (BIFF2-BIFF7 sets mnOrient)
+ sal_uInt8 nXclRot = (mnOrient == EXC_ORIENT_NONE) ? mnRotation : XclTools::GetXclRotFromOrient( mnOrient );
+ bool bStacked = (nXclRot == EXC_ROT_STACKED);
+ ScfTools::PutItem( rItemSet, ScVerticalStackCell( bStacked ), bSkipPoolDefs );
+ // set an angle in the range from -90 to 90 degrees
+ Degree100 nAngle = XclTools::GetScRotation( nXclRot, 0_deg100 );
+ ScfTools::PutItem( rItemSet, ScRotateValueItem( nAngle ), bSkipPoolDefs );
+ // set "Use asian vertical layout", if cell is stacked and font contains CKJ characters
+ bool bAsianVert = bStacked && pFont && pFont->HasAsianChars();
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_VERTICAL_ASIAN, bAsianVert ), bSkipPoolDefs );
+
+ // CTL text direction
+ ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs );
+}
+
+XclImpCellBorder::XclImpCellBorder()
+{
+ SetUsedFlags( false, false );
+}
+
+void XclImpCellBorder::SetUsedFlags( bool bOuterUsed, bool bDiagUsed )
+{
+ mbLeftUsed = mbRightUsed = mbTopUsed = mbBottomUsed = bOuterUsed;
+ mbDiagUsed = bDiagUsed;
+}
+
+void XclImpCellBorder::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnLeftLine = ::get_flagvalue( nFlags, EXC_XF2_LEFTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnRightLine = ::get_flagvalue( nFlags, EXC_XF2_RIGHTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnTopLine = ::get_flagvalue( nFlags, EXC_XF2_TOPLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnBottomLine = ::get_flagvalue( nFlags, EXC_XF2_BOTTOMLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnLeftColor = mnRightColor = mnTopColor = mnBottomColor = EXC_COLOR_BIFF2_BLACK;
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF3( sal_uInt32 nBorder )
+{
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 8, 3 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nBorder, 16, 3 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 24, 3 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 3, 5 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 11, 5 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nBorder, 19, 5 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 27, 5 );
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea )
+{
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 3, 3 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nArea, 22, 3 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 6, 3 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 9, 7 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 16, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nArea, 25, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 23, 7 );
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 )
+{
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder1, 0, 4 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder1, 4, 4 );
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder1, 8, 4 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nBorder1, 12, 4 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder1, 16, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder1, 23, 7 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder2, 0, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nBorder2, 7, 7 );
+ mbDiagTLtoBR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_TL_TO_BR );
+ mbDiagBLtoTR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_BL_TO_TR );
+ if( mbDiagTLtoBR || mbDiagBLtoTR )
+ {
+ mnDiagLine = ::extract_value< sal_uInt8 >( nBorder2, 21, 4 );
+ mnDiagColor = ::extract_value< sal_uInt16 >( nBorder2, 14, 7 );
+ }
+ SetUsedFlags( true, true );
+}
+
+void XclImpCellBorder::FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags )
+{
+ mnLeftLine = ::extract_value< sal_uInt8 >( nLineStyle, 0, 4 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nLineStyle, 4, 4 );
+ mnTopLine = ::extract_value< sal_uInt8 >( nLineStyle, 8, 4 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nLineStyle, 12, 4 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nLineColor, 0, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nLineColor, 7, 7 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nLineColor, 16, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nLineColor, 23, 7 );
+ mbLeftUsed = !::get_flag( nFlags, EXC_CF_BORDER_LEFT );
+ mbRightUsed = !::get_flag( nFlags, EXC_CF_BORDER_RIGHT );
+ mbTopUsed = !::get_flag( nFlags, EXC_CF_BORDER_TOP );
+ mbBottomUsed = !::get_flag( nFlags, EXC_CF_BORDER_BOTTOM );
+ mbDiagUsed = false;
+}
+
+bool XclImpCellBorder::HasAnyOuterBorder() const
+{
+ return
+ (mbLeftUsed && (mnLeftLine != EXC_LINE_NONE)) ||
+ (mbRightUsed && (mnRightLine != EXC_LINE_NONE)) ||
+ (mbTopUsed && (mnTopLine != EXC_LINE_NONE)) ||
+ (mbBottomUsed && (mnBottomLine != EXC_LINE_NONE));
+}
+
+namespace {
+
+/** Converts the passed line style to a ::editeng::SvxBorderLine, or returns false, if style is "no line". */
+bool lclConvertBorderLine( ::editeng::SvxBorderLine& rLine, const XclImpPalette& rPalette, sal_uInt8 nXclLine, sal_uInt16 nXclColor )
+{
+ static const sal_uInt16 ppnLineParam[][ 4 ] =
+ {
+ // outer width, type
+ { 0, table::BorderLineStyle::SOLID }, // 0 = none
+ { EXC_BORDER_THIN, table::BorderLineStyle::SOLID }, // 1 = thin
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::SOLID }, // 2 = medium
+ { EXC_BORDER_THIN, table::BorderLineStyle::FINE_DASHED }, // 3 = dashed
+ { EXC_BORDER_THIN, table::BorderLineStyle::DOTTED }, // 4 = dotted
+ { EXC_BORDER_THICK, table::BorderLineStyle::SOLID }, // 5 = thick
+ { EXC_BORDER_THICK, table::BorderLineStyle::DOUBLE_THIN }, // 6 = double
+ { EXC_BORDER_HAIR, table::BorderLineStyle::SOLID }, // 7 = hair
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASHED }, // 8 = med dash
+ { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT }, // 9 = thin dashdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT }, // A = med dashdot
+ { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT_DOT }, // B = thin dashdotdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT_DOT }, // C = med dashdotdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT } // D = med slant dashdot
+ };
+
+ if( nXclLine == EXC_LINE_NONE )
+ return false;
+ if( nXclLine >= SAL_N_ELEMENTS( ppnLineParam ) )
+ nXclLine = EXC_LINE_THIN;
+
+ rLine.SetColor( rPalette.GetColor( nXclColor ) );
+ rLine.SetWidth( ppnLineParam[ nXclLine ][ 0 ] );
+ rLine.SetBorderLineStyle( static_cast< SvxBorderLineStyle>(
+ ppnLineParam[ nXclLine ][ 1 ]) );
+ return true;
+}
+
+} // namespace
+
+void XclImpCellBorder::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
+{
+ if( mbLeftUsed || mbRightUsed || mbTopUsed || mbBottomUsed )
+ {
+ SvxBoxItem aBoxItem( ATTR_BORDER );
+ ::editeng::SvxBorderLine aLine;
+ if( mbLeftUsed && lclConvertBorderLine( aLine, rPalette, mnLeftLine, mnLeftColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ if( mbRightUsed && lclConvertBorderLine( aLine, rPalette, mnRightLine, mnRightColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ if( mbTopUsed && lclConvertBorderLine( aLine, rPalette, mnTopLine, mnTopColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::TOP );
+ if( mbBottomUsed && lclConvertBorderLine( aLine, rPalette, mnBottomLine, mnBottomColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs );
+ }
+ if( !mbDiagUsed )
+ return;
+
+ SvxLineItem aTLBRItem( ATTR_BORDER_TLBR );
+ SvxLineItem aBLTRItem( ATTR_BORDER_BLTR );
+ ::editeng::SvxBorderLine aLine;
+ if( lclConvertBorderLine( aLine, rPalette, mnDiagLine, mnDiagColor ) )
+ {
+ if( mbDiagTLtoBR )
+ aTLBRItem.SetLine( &aLine );
+ if( mbDiagBLtoTR )
+ aBLTRItem.SetLine( &aLine );
+ }
+ ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs );
+}
+
+XclImpCellArea::XclImpCellArea()
+{
+ SetUsedFlags( false );
+}
+
+void XclImpCellArea::SetUsedFlags( bool bUsed )
+{
+ mbForeUsed = mbBackUsed = mbPattUsed = bUsed;
+}
+
+void XclImpCellArea::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnPattern = ::get_flagvalue( nFlags, EXC_XF2_BACKGROUND, EXC_PATT_12_5_PERC, EXC_PATT_NONE );
+ mnForeColor = EXC_COLOR_BIFF2_BLACK;
+ mnBackColor = EXC_COLOR_BIFF2_WHITE;
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF3( sal_uInt16 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nArea, 0, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 6, 5 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 11, 5 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF5( sal_uInt32 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nArea, 16, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nBorder2, 26, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags )
+{
+ mnForeColor = ::extract_value< sal_uInt16 >( nColor, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nColor, 7, 7 );
+ mnPattern = ::extract_value< sal_uInt8 >( nPattern, 10, 6 );
+ mbForeUsed = !::get_flag( nFlags, EXC_CF_AREA_FGCOLOR );
+ mbBackUsed = !::get_flag( nFlags, EXC_CF_AREA_BGCOLOR );
+ mbPattUsed = !::get_flag( nFlags, EXC_CF_AREA_PATTERN );
+
+ if( mbBackUsed && (!mbPattUsed || (mnPattern == EXC_PATT_SOLID)) )
+ {
+ mnForeColor = mnBackColor;
+ mnPattern = EXC_PATT_SOLID;
+ mbForeUsed = mbPattUsed = true;
+ }
+ else if( !mbBackUsed && mbPattUsed && (mnPattern == EXC_PATT_SOLID) )
+ {
+ mbPattUsed = false;
+ }
+}
+
+void XclImpCellArea::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
+{
+ if( !mbPattUsed ) // colors may be both unused in cond. formats
+ return;
+
+ SvxBrushItem aBrushItem( ATTR_BACKGROUND );
+
+ // do not use IsTransparent() - old Calc filter writes transparency with different color indexes
+ if( mnPattern == EXC_PATT_NONE )
+ {
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ }
+ else
+ {
+ Color aFore( rPalette.GetColor( mbForeUsed ? mnForeColor : EXC_COLOR_WINDOWTEXT ) );
+ Color aBack( rPalette.GetColor( mbBackUsed ? mnBackColor : EXC_COLOR_WINDOWBACK ) );
+ aBrushItem.SetColor( XclTools::GetPatternColor( aFore, aBack, mnPattern ) );
+ }
+
+ ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs );
+}
+
+XclImpXF::XclImpXF( const XclImpRoot& rRoot ) :
+ XclXFBase( true ), // default is cell XF
+ XclImpRoot( rRoot ),
+ mpStyleSheet( nullptr ),
+ mnXclNumFmt( 0 ),
+ mnXclFont( 0 )
+{
+}
+
+XclImpXF::~XclImpXF()
+{
+}
+
+void XclImpXF::ReadXF2( XclImpStream& rStrm )
+{
+ sal_uInt8 nReadFont, nReadNumFmt, nFlags;
+ nReadFont = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ nReadNumFmt = rStrm.ReaduInt8();
+ nFlags = rStrm.ReaduInt8();
+
+ // XF type always cell, no parent, used flags always true
+ SetAllUsedFlags( true );
+
+ // attributes
+ maProtection.FillFromXF2( nReadNumFmt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt & EXC_XF2_VALFMT_MASK;
+ maAlignment.FillFromXF2( nFlags );
+ maBorder.FillFromXF2( nFlags );
+ maArea.FillFromXF2( nFlags );
+}
+
+void XclImpXF::ReadXF3( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder;
+ sal_uInt16 nTypeProt, nAlign, nArea;
+ sal_uInt8 nReadFont, nReadNumFmt;
+ nReadFont = rStrm.ReaduInt8();
+ nReadNumFmt = rStrm.ReaduInt8();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt16();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); // new in BIFF3
+ mnParent = ::extract_value< sal_uInt16 >( nAlign, 4, 12 ); // new in BIFF3
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nTypeProt, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt;
+ maAlignment.FillFromXF3( nAlign );
+ maBorder.FillFromXF3( nBorder );
+ maArea.FillFromXF3( nArea ); // new in BIFF3
+}
+
+void XclImpXF::ReadXF4( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder;
+ sal_uInt16 nTypeProt, nAlign, nArea;
+ sal_uInt8 nReadFont, nReadNumFmt;
+ nReadFont = rStrm.ReaduInt8();
+ nReadNumFmt = rStrm.ReaduInt8();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt16();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt;
+ maAlignment.FillFromXF4( nAlign );
+ maBorder.FillFromXF3( nBorder );
+ maArea.FillFromXF3( nArea );
+}
+
+void XclImpXF::ReadXF5( XclImpStream& rStrm )
+{
+ sal_uInt32 nArea, nBorder;
+ sal_uInt16 nTypeProt, nAlign;
+ mnXclFont = rStrm.ReaduInt16();
+ mnXclNumFmt = rStrm.ReaduInt16();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt32();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ maAlignment.FillFromXF5( nAlign );
+ maBorder.FillFromXF5( nBorder, nArea );
+ maArea.FillFromXF5( nArea );
+}
+
+void XclImpXF::ReadXF8( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder1, nBorder2;
+ sal_uInt16 nTypeProt, nAlign, nMiscAttrib, nArea;
+ mnXclFont = rStrm.ReaduInt16();
+ mnXclNumFmt = rStrm.ReaduInt16();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nMiscAttrib = rStrm.ReaduInt16();
+ nBorder1 = rStrm.ReaduInt32();
+ nBorder2 = rStrm.ReaduInt32( );
+ nArea = rStrm.ReaduInt16();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nMiscAttrib, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ maAlignment.FillFromXF8( nAlign, nMiscAttrib );
+ maBorder.FillFromXF8( nBorder1, nBorder2 );
+ maArea.FillFromXF8( nBorder2, nArea );
+}
+
+void XclImpXF::ReadXF( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2: ReadXF2( rStrm ); break;
+ case EXC_BIFF3: ReadXF3( rStrm ); break;
+ case EXC_BIFF4: ReadXF4( rStrm ); break;
+ case EXC_BIFF5: ReadXF5( rStrm ); break;
+ case EXC_BIFF8: ReadXF8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+const ScPatternAttr& XclImpXF::CreatePattern( bool bSkipPoolDefs )
+{
+ if( mpPattern )
+ return *mpPattern;
+
+ // create new pattern attribute set
+ mpPattern.reset( new ScPatternAttr( GetDoc().GetPool() ) );
+ SfxItemSet& rItemSet = mpPattern->GetItemSet();
+ XclImpXF* pParentXF = IsCellXF() ? GetXFBuffer().GetXF( mnParent ) : nullptr;
+
+ // parent cell style
+ if( IsCellXF() && !mpStyleSheet )
+ {
+ mpStyleSheet = GetXFBuffer().CreateStyleSheet( mnParent );
+
+ /* Enables mb***Used flags, if the formatting attributes differ from
+ the passed XF record. In cell XFs Excel uses the cell attributes,
+ if they differ from the parent style XF.
+ ...or if the respective flag is not set in parent style XF. */
+ if( pParentXF )
+ {
+ if( !mbProtUsed )
+ mbProtUsed = !pParentXF->mbProtUsed || !(maProtection == pParentXF->maProtection);
+ if( !mbFontUsed )
+ mbFontUsed = !pParentXF->mbFontUsed || (mnXclFont != pParentXF->mnXclFont);
+ if( !mbFmtUsed )
+ mbFmtUsed = !pParentXF->mbFmtUsed || (mnXclNumFmt != pParentXF->mnXclNumFmt);
+ if( !mbAlignUsed )
+ mbAlignUsed = !pParentXF->mbAlignUsed || !(maAlignment == pParentXF->maAlignment);
+ if( !mbBorderUsed )
+ mbBorderUsed = !pParentXF->mbBorderUsed || !(maBorder == pParentXF->maBorder);
+ if( !mbAreaUsed )
+ mbAreaUsed = !pParentXF->mbAreaUsed || !(maArea == pParentXF->maArea);
+ }
+ }
+
+ // cell protection
+ if( mbProtUsed )
+ maProtection.FillToItemSet( rItemSet, bSkipPoolDefs );
+
+ // font
+ if( mbFontUsed )
+ GetFontBuffer().FillToItemSet( rItemSet, XclFontItemType::Cell, mnXclFont, bSkipPoolDefs );
+
+ // value format
+ if( mbFmtUsed )
+ {
+ GetNumFmtBuffer().FillToItemSet( rItemSet, mnXclNumFmt, bSkipPoolDefs );
+ // Trace occurrences of Windows date formats
+ GetTracer().TraceDates( mnXclNumFmt );
+ }
+
+ // alignment
+ if( mbAlignUsed )
+ maAlignment.FillToItemSet( rItemSet, GetFontBuffer().GetFont( mnXclFont ), bSkipPoolDefs );
+
+ // border
+ if( mbBorderUsed )
+ {
+ maBorder.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
+ GetTracer().TraceBorderLineStyle(maBorder.mnLeftLine > EXC_LINE_HAIR ||
+ maBorder.mnRightLine > EXC_LINE_HAIR || maBorder.mnTopLine > EXC_LINE_HAIR ||
+ maBorder.mnBottomLine > EXC_LINE_HAIR );
+ }
+
+ // area
+ if( mbAreaUsed )
+ {
+ maArea.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
+ GetTracer().TraceFillPattern(maArea.mnPattern != EXC_PATT_NONE &&
+ maArea.mnPattern != EXC_PATT_SOLID);
+ }
+
+ /* #i38709# Decide which rotation reference mode to use. If any outer
+ border line of the cell is set (either explicitly or via cell style),
+ and the cell contents are rotated, set rotation reference to bottom of
+ cell. This causes the borders to be painted rotated with the text. */
+ if( mbAlignUsed || mbBorderUsed )
+ {
+ SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD;
+ const XclImpCellAlign* pAlign = mbAlignUsed ? &maAlignment : (pParentXF ? &pParentXF->maAlignment : nullptr);
+ const XclImpCellBorder* pBorder = mbBorderUsed ? &maBorder : (pParentXF ? &pParentXF->maBorder : nullptr);
+ if( pAlign && pBorder && (0 < pAlign->mnRotation) && (pAlign->mnRotation <= 180) && pBorder->HasAnyOuterBorder() )
+ eRotateMode = SVX_ROTATE_MODE_BOTTOM;
+ ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs );
+ }
+
+ // Excel's cell margins are different from Calc's default margins.
+ SvxMarginItem aItem(40, 40, 40, 40, ATTR_MARGIN);
+ ScfTools::PutItem(rItemSet, aItem, bSkipPoolDefs);
+
+ return *mpPattern;
+}
+
+void XclImpXF::ApplyPatternToAttrVector(
+ std::vector<ScAttrEntry>& rAttrs, SCROW nRow1, SCROW nRow2, sal_uInt32 nForceScNumFmt)
+{
+ // force creation of cell style and hard formatting, do it here to have mpStyleSheet
+ CreatePattern();
+ ScPatternAttr& rPat = *mpPattern;
+
+ // insert into document
+ ScDocument& rDoc = GetDoc();
+
+ if (IsCellXF())
+ {
+ if (mpStyleSheet)
+ {
+ // Apply style sheet. Don't clear the direct formats.
+ rPat.SetStyleSheet(mpStyleSheet, false);
+ }
+ else
+ {
+ // When the cell format is not associated with any style, use the
+ // 'Default' style. Some buggy XLS docs generated by apps other
+ // than Excel (such as 1C) may not have any built-in styles at
+ // all.
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if (pStylePool)
+ {
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rPat.SetStyleSheet(pStyleSheet, false);
+ }
+
+ }
+ }
+
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ ScPatternAttr aNumPat(rDoc.GetPool());
+ GetNumFmtBuffer().FillScFmtToItemSet(aNumPat.GetItemSet(), nForceScNumFmt);
+ rPat.GetItemSet().Put(aNumPat.GetItemSet());
+ }
+
+ // Make sure we skip unnamed styles.
+ if (!rPat.GetStyleName())
+ return;
+
+ // Check for a gap between the last entry and this one.
+ bool bHasGap = false;
+ if (rAttrs.empty() && nRow1 > 0)
+ // First attribute range doesn't start at row 0.
+ bHasGap = true;
+
+ if (!rAttrs.empty() && rAttrs.back().nEndRow + 1 < nRow1)
+ bHasGap = true;
+
+ if (bHasGap)
+ {
+ // Fill this gap with the default pattern.
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow1 - 1;
+ aEntry.pPattern = rDoc.GetDefPattern();
+ rAttrs.push_back(aEntry);
+ }
+
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow2;
+ aEntry.pPattern = &rDoc.GetPool()->DirectPutItemInPool(rPat);
+ rAttrs.push_back(aEntry);
+}
+
+void XclImpXF::ApplyPattern(
+ SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2,
+ SCTAB nScTab )
+{
+ // force creation of cell style and hard formatting, do it here to have mpStyleSheet
+ const ScPatternAttr& rPattern = CreatePattern();
+
+ // insert into document
+ ScDocument& rDoc = GetDoc();
+ if( IsCellXF() && mpStyleSheet )
+ rDoc.ApplyStyleAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, *mpStyleSheet );
+ if( HasUsedFlags() )
+ rDoc.ApplyPatternAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, rPattern );
+
+}
+
+/*static*/ void XclImpXF::ApplyPatternForBiff2CellFormat( const XclImpRoot& rRoot,
+ const ScAddress& rScPos, sal_uInt8 nFlags1, sal_uInt8 nFlags2, sal_uInt8 nFlags3 )
+{
+ /* Create an XF object and let it do the work. We will have access to its
+ private members here. */
+ XclImpXF aXF( rRoot );
+
+ // no used flags available in BIFF2 (always true)
+ aXF.SetAllUsedFlags( true );
+
+ // set the attributes
+ aXF.maProtection.FillFromXF2( nFlags1 );
+ aXF.maAlignment.FillFromXF2( nFlags3 );
+ aXF.maBorder.FillFromXF2( nFlags3 );
+ aXF.maArea.FillFromXF2( nFlags3 );
+ aXF.mnXclNumFmt = ::extract_value< sal_uInt16 >( nFlags2, 0, 6 );
+ aXF.mnXclFont = ::extract_value< sal_uInt16 >( nFlags2, 6, 2 );
+
+ // write the attributes to the cell
+ aXF.ApplyPattern( rScPos.Col(), rScPos.Row(), rScPos.Col(), rScPos.Row(), rScPos.Tab() );
+}
+
+void XclImpXF::SetUsedFlags( sal_uInt8 nUsedFlags )
+{
+ /* Notes about finding the mb***Used flags:
+ - In cell XFs a *set* bit means a used attribute.
+ - In style XFs a *cleared* bit means a used attribute.
+ The mb***Used members always store true, if the attribute is used.
+ The "mbCellXF == ::get_flag(...)" construct evaluates to true in
+ both mentioned cases: cell XF and set bit; or style XF and cleared bit.
+ */
+ mbProtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_PROT ));
+ mbFontUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_FONT ));
+ mbFmtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_VALFMT ));
+ mbAlignUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_ALIGN ));
+ mbBorderUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_BORDER ));
+ mbAreaUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_AREA ));
+}
+
+XclImpStyle::XclImpStyle( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnXfId( EXC_XF_NOTFOUND ),
+ mnBuiltinId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL ),
+ mbBuiltin( false ),
+ mbCustom( false ),
+ mbHidden( false ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void XclImpStyle::ReadStyle( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 );
+
+ sal_uInt16 nXFIndex;
+ nXFIndex = rStrm.ReaduInt16();
+ mnXfId = nXFIndex & EXC_STYLE_XFMASK;
+ mbBuiltin = ::get_flag( nXFIndex, EXC_STYLE_BUILTIN );
+
+ if( mbBuiltin )
+ {
+ mnBuiltinId = rStrm.ReaduInt8();
+ mnLevel = rStrm.ReaduInt8();
+ }
+ else
+ {
+ maName = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
+ // #i103281# check if this is a new built-in style introduced in XL2007
+ if( (GetBiff() == EXC_BIFF8) && (rStrm.GetNextRecId() == EXC_ID_STYLEEXT) && rStrm.StartNextRecord() )
+ {
+ sal_uInt8 nExtFlags;
+ rStrm.Ignore( 12 );
+ nExtFlags = rStrm.ReaduInt8();
+ mbBuiltin = ::get_flag( nExtFlags, EXC_STYLEEXT_BUILTIN );
+ mbCustom = ::get_flag( nExtFlags, EXC_STYLEEXT_CUSTOM );
+ mbHidden = ::get_flag( nExtFlags, EXC_STYLEEXT_HIDDEN );
+ if( mbBuiltin )
+ {
+ rStrm.Ignore( 1 ); // category
+ mnBuiltinId = rStrm.ReaduInt8();
+ mnLevel = rStrm.ReaduInt8();
+ }
+ }
+ }
+}
+
+ScStyleSheet* XclImpStyle::CreateStyleSheet()
+{
+ // #i1624# #i1768# ignore unnamed user styles
+ if( !mpStyleSheet && (!maFinalName.isEmpty()) )
+ {
+ bool bCreatePattern = false;
+ XclImpXF* pXF = GetXFBuffer().GetXF( mnXfId );
+
+ bool bDefStyle = mbBuiltin && (mnBuiltinId == EXC_STYLE_NORMAL);
+ if( bDefStyle )
+ {
+ // set all flags to true to get all items in XclImpXF::CreatePattern()
+ if( pXF ) pXF->SetAllUsedFlags( true );
+ // use existing "Default" style sheet
+ mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find(
+ ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) );
+ OSL_ENSURE( mpStyleSheet, "XclImpStyle::CreateStyleSheet - Default style not found" );
+ bCreatePattern = true;
+ }
+ else
+ {
+ /* #i103281# do not create another style sheet of the same name,
+ if it exists already. This is needed to prevent that styles
+ pasted from clipboard get duplicated over and over. */
+ mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find( maFinalName, SfxStyleFamily::Para ) );
+ if( !mpStyleSheet )
+ {
+ mpStyleSheet = &static_cast< ScStyleSheet& >( GetStyleSheetPool().Make( maFinalName, SfxStyleFamily::Para, SfxStyleSearchBits::UserDefined ) );
+ bCreatePattern = true;
+ }
+ }
+
+ // bDefStyle==true omits default pool items in CreatePattern()
+ if( bCreatePattern && mpStyleSheet && pXF )
+ mpStyleSheet->GetItemSet().Put( pXF->CreatePattern( bDefStyle ).GetItemSet() );
+ }
+ return mpStyleSheet;
+}
+
+void XclImpStyle::CreateUserStyle( const OUString& rFinalName )
+{
+ maFinalName = rFinalName;
+ if( !IsBuiltin() || mbCustom )
+ CreateStyleSheet();
+}
+
+XclImpXFBuffer::XclImpXFBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpXFBuffer::Initialize()
+{
+ maXFList.clear();
+ maBuiltinStyles.clear();
+ maUserStyles.clear();
+ maStylesByXf.clear();
+}
+
+void XclImpXFBuffer::ReadXF( XclImpStream& rStrm )
+{
+ std::unique_ptr<XclImpXF> xXF = std::make_unique<XclImpXF>(GetRoot());
+ xXF->ReadXF(rStrm);
+ maXFList.emplace_back(std::move(xXF));
+}
+
+void XclImpXFBuffer::ReadStyle( XclImpStream& rStrm )
+{
+ std::unique_ptr<XclImpStyle> xStyle(std::make_unique<XclImpStyle>(GetRoot()));
+ xStyle->ReadStyle(rStrm);
+ XclImpStyleList& rStyleList = (xStyle->IsBuiltin() ? maBuiltinStyles : maUserStyles);
+ rStyleList.emplace_back(std::move(xStyle));
+ XclImpStyle* pStyle = rStyleList.back().get();
+ OSL_ENSURE( maStylesByXf.count( pStyle->GetXfId() ) == 0, "XclImpXFBuffer::ReadStyle - multiple styles with equal XF identifier" );
+ maStylesByXf[ pStyle->GetXfId() ] = pStyle;
+}
+
+sal_uInt16 XclImpXFBuffer::GetFontIndex( sal_uInt16 nXFIndex ) const
+{
+ const XclImpXF* pXF = GetXF( nXFIndex );
+ return pXF ? pXF->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+const XclImpFont* XclImpXFBuffer::GetFont( sal_uInt16 nXFIndex ) const
+{
+ return GetFontBuffer().GetFont( GetFontIndex( nXFIndex ) );
+}
+
+namespace {
+
+/** Functor for case-insensitive string comparison, usable in maps etc. */
+struct IgnoreCaseCompare
+{
+ bool operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
+ { return o3tl::compareToIgnoreAsciiCase( rName1, rName2 ) < 0; }
+};
+
+} // namespace
+
+void XclImpXFBuffer::CreateUserStyles()
+{
+ // calculate final names of all styles
+ std::map< OUString, XclImpStyle*, IgnoreCaseCompare > aCellStyles;
+ std::vector< XclImpStyle* > aConflictNameStyles;
+
+ /* First, reserve style names that are built-in in Calc. This causes that
+ imported cell styles get different unused names and thus do not try to
+ overwrite these built-in styles. For BIFF4 workbooks (which contain a
+ separate list of cell styles per sheet), reserve all existing styles if
+ current sheet is not the first sheet (this styles buffer will be
+ initialized again for every new sheet). This will create unique names
+ for styles in different sheets with the same name. Assuming that the
+ BIFF4W import filter is never used to import from clipboard... */
+ bool bReserveAll = (GetBiff() == EXC_BIFF4) && (GetCurrScTab() > 0);
+ SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
+ OUString aStandardName = ScResId( STR_STYLENAME_STANDARD );
+ for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
+ if( (pStyleSheet->GetName() != aStandardName) && (bReserveAll || !pStyleSheet->IsUserDefined()) )
+ if( aCellStyles.count( pStyleSheet->GetName() ) == 0 )
+ aCellStyles[ pStyleSheet->GetName() ] = nullptr;
+
+ /* Calculate names of built-in styles. Store styles with reserved names
+ in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maBuiltinStyles )
+ {
+ OUString aStyleName = XclTools::GetBuiltInStyleName( rxStyle->GetBuiltinId(), rxStyle->GetName(), rxStyle->GetLevel() );
+ OSL_ENSURE( bReserveAll || (aCellStyles.count( aStyleName ) == 0),
+ "XclImpXFBuffer::CreateUserStyles - multiple styles with equal built-in identifier" );
+ if( aCellStyles.count( aStyleName ) > 0 )
+ aConflictNameStyles.push_back( rxStyle.get() );
+ else
+ aCellStyles[ aStyleName ] = rxStyle.get();
+ }
+
+ /* Calculate names of user defined styles. Store styles with reserved
+ names in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maUserStyles )
+ {
+ // #i1624# #i1768# ignore unnamed user styles
+ if( !rxStyle->GetName().isEmpty() )
+ {
+ if( aCellStyles.count( rxStyle->GetName() ) > 0 )
+ aConflictNameStyles.push_back( rxStyle.get() );
+ else
+ aCellStyles[ rxStyle->GetName() ] = rxStyle.get();
+ }
+ }
+
+ // find unused names for all styles with conflicting names
+ for( XclImpStyle* pStyle : aConflictNameStyles )
+ {
+ OUString aUnusedName;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aUnusedName = pStyle->GetName() + " " + OUString::number( ++nIndex );
+ }
+ while( aCellStyles.count( aUnusedName ) > 0 );
+ aCellStyles[ aUnusedName ] = pStyle;
+ }
+
+ // set final names and create user-defined and modified built-in cell styles
+ for( auto& [rStyleName, rpStyle] : aCellStyles )
+ if( rpStyle )
+ rpStyle->CreateUserStyle( rStyleName );
+}
+
+ScStyleSheet* XclImpXFBuffer::CreateStyleSheet( sal_uInt16 nXFIndex )
+{
+ XclImpStyleMap::iterator aIt = maStylesByXf.find( nXFIndex );
+ return (aIt == maStylesByXf.end()) ? nullptr : aIt->second->CreateStyleSheet();
+}
+
+// Buffer for XF indexes in cells =============================================
+
+bool XclImpXFRange::Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex )
+{
+ if( maXFIndex != rXFIndex )
+ return false;
+
+ if( mnScRow2 + 1 == nScRow )
+ {
+ ++mnScRow2;
+ return true;
+ }
+ if( mnScRow1 > 0 && (mnScRow1 - 1 == nScRow) )
+ {
+ --mnScRow1;
+ return true;
+ }
+
+ return false;
+}
+
+bool XclImpXFRange::Expand( const XclImpXFRange& rNextRange )
+{
+ OSL_ENSURE( mnScRow2 < rNextRange.mnScRow1, "XclImpXFRange::Expand - rows out of order" );
+ if( (maXFIndex == rNextRange.maXFIndex) && (mnScRow2 + 1 == rNextRange.mnScRow1) )
+ {
+ mnScRow2 = rNextRange.mnScRow2;
+ return true;
+ }
+ return false;
+}
+
+void XclImpXFRangeColumn::SetDefaultXF( const XclImpXFIndex& rXFIndex, const XclImpRoot& rRoot )
+{
+ // List should be empty when inserting the default column format.
+ // Later explicit SetXF() calls will break up this range.
+ OSL_ENSURE( maIndexList.empty(), "XclImpXFRangeColumn::SetDefaultXF - Setting Default Column XF is not empty" );
+
+ // insert a complete row range with one insert.
+ maIndexList.push_back( std::make_unique<XclImpXFRange>( 0, rRoot.GetDoc().MaxRow(), rXFIndex ) );
+}
+
+void XclImpXFRangeColumn::SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex )
+{
+ XclImpXFRange* pPrevRange;
+ XclImpXFRange* pNextRange;
+ sal_uLong nNextIndex;
+
+ Find( pPrevRange, pNextRange, nNextIndex, nScRow );
+
+ // previous range:
+ // try to overwrite XF (if row is contained in) or try to expand range
+ if( pPrevRange )
+ {
+ if( pPrevRange->Contains( nScRow ) ) // overwrite old XF
+ {
+ if( rXFIndex == pPrevRange->maXFIndex )
+ return;
+
+ SCROW nFirstScRow = pPrevRange->mnScRow1;
+ SCROW nLastScRow = pPrevRange->mnScRow2;
+ sal_uLong nIndex = nNextIndex - 1;
+ XclImpXFRange* pThisRange = pPrevRange;
+ pPrevRange = (nIndex > 0 && nIndex <= maIndexList.size()) ? maIndexList[ nIndex - 1 ].get() : nullptr;
+
+ if( nFirstScRow == nLastScRow ) // replace solely XF
+ {
+ pThisRange->maXFIndex = rXFIndex;
+ TryConcatPrev( nNextIndex ); // try to concat. next with this
+ TryConcatPrev( nIndex ); // try to concat. this with previous
+ }
+ else if( nFirstScRow == nScRow ) // replace first XF
+ {
+ ++(pThisRange->mnScRow1);
+ // try to concatenate with previous of this
+ if( !pPrevRange || !pPrevRange->Expand( nScRow, rXFIndex ) )
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex );
+ }
+ else if( nLastScRow == nScRow ) // replace last XF
+ {
+ --(pThisRange->mnScRow2);
+ if( !pNextRange || !pNextRange->Expand( nScRow, rXFIndex ) )
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
+ }
+ else // insert in the middle of the range
+ {
+ pThisRange->mnScRow1 = nScRow + 1;
+ // List::Insert() moves entries towards end of list, so insert twice at nIndex
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex );
+ Insert( new XclImpXFRange( nFirstScRow, nScRow - 1, pThisRange->maXFIndex ), nIndex );
+ }
+ return;
+ }
+ else if( pPrevRange->Expand( nScRow, rXFIndex ) ) // try to expand
+ {
+ TryConcatPrev( nNextIndex ); // try to concatenate next with expanded
+ return;
+ }
+ }
+
+ // try to expand next range
+ if( pNextRange && pNextRange->Expand( nScRow, rXFIndex ) )
+ return;
+
+ // create new range
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
+}
+
+void XclImpXFRangeColumn::Insert(XclImpXFRange* pXFRange, sal_uLong nIndex)
+{
+ maIndexList.insert( maIndexList.begin() + nIndex, std::unique_ptr<XclImpXFRange>(pXFRange) );
+}
+
+void XclImpXFRangeColumn::Find(
+ XclImpXFRange*& rpPrevRange, XclImpXFRange*& rpNextRange,
+ sal_uLong& rnNextIndex, SCROW nScRow )
+{
+
+ // test whether list is empty
+ if( maIndexList.empty() )
+ {
+ rpPrevRange = rpNextRange = nullptr;
+ rnNextIndex = 0;
+ return;
+ }
+
+ rpPrevRange = maIndexList.front().get();
+ rpNextRange = maIndexList.back().get();
+
+ // test whether row is at end of list (contained in or behind last range)
+ // rpPrevRange will contain a possible existing row
+ if( rpNextRange->mnScRow1 <= nScRow )
+ {
+ rpPrevRange = rpNextRange;
+ rpNextRange = nullptr;
+ rnNextIndex = maIndexList.size();
+ return;
+ }
+
+ // test whether row is at beginning of list (really before first range)
+ if( nScRow < rpPrevRange->mnScRow1 )
+ {
+ rpNextRange = rpPrevRange;
+ rpPrevRange = nullptr;
+ rnNextIndex = 0;
+ return;
+ }
+
+ // loop: find range entries before and after new row
+ // break the loop if there is no more range between first and last -or-
+ // if rpPrevRange contains nScRow (rpNextRange will never contain nScRow)
+ sal_uLong nPrevIndex = 0;
+ sal_uLong nMidIndex;
+ rnNextIndex = maIndexList.size() - 1;
+ XclImpXFRange* pMidRange;
+ while( ((rnNextIndex - nPrevIndex) > 1) && (rpPrevRange->mnScRow2 < nScRow) )
+ {
+ nMidIndex = (nPrevIndex + rnNextIndex) / 2;
+ pMidRange = maIndexList[nMidIndex].get();
+ OSL_ENSURE( pMidRange, "XclImpXFRangeColumn::Find - missing XF index range" );
+ if( nScRow < pMidRange->mnScRow1 ) // row is really before pMidRange
+ {
+ rpNextRange = pMidRange;
+ rnNextIndex = nMidIndex;
+ }
+ else // row is in or after pMidRange
+ {
+ rpPrevRange = pMidRange;
+ nPrevIndex = nMidIndex;
+ }
+ }
+
+ // find next rpNextRange if rpPrevRange contains nScRow
+ if( nScRow <= rpPrevRange->mnScRow2 )
+ {
+ rnNextIndex = nPrevIndex + 1;
+ rpNextRange = maIndexList[rnNextIndex].get();
+ }
+}
+
+void XclImpXFRangeColumn::TryConcatPrev( sal_uLong nIndex )
+{
+ if( !nIndex || nIndex >= maIndexList.size() )
+ return;
+
+ XclImpXFRange& prevRange = *maIndexList[ nIndex - 1 ];
+ XclImpXFRange& nextRange = *maIndexList[ nIndex ];
+
+ if( prevRange.Expand( nextRange ) )
+ maIndexList.erase( maIndexList.begin() + nIndex );
+}
+
+XclImpXFRangeBuffer::XclImpXFRangeBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+XclImpXFRangeBuffer::~XclImpXFRangeBuffer()
+{
+}
+
+void XclImpXFRangeBuffer::Initialize()
+{
+ maColumns.clear();
+ maHyperlinks.clear();
+ maMergeList.RemoveAll();
+}
+
+void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode )
+{
+ SCCOL nScCol = rScPos.Col();
+ SCROW nScRow = rScPos.Row();
+
+ // set cell XF's
+ size_t nIndex = static_cast< size_t >( nScCol );
+ if( maColumns.size() <= nIndex )
+ maColumns.resize( nIndex + 1 );
+ if( !maColumns[ nIndex ] )
+ maColumns[ nIndex ] = std::make_shared<XclImpXFRangeColumn>();
+ // remember all Boolean cells, they will get 'Standard' number format
+ maColumns[ nIndex ]->SetXF( nScRow, XclImpXFIndex( nXFIndex, eMode == xlXFModeBoolCell ) );
+
+ // set "center across selection" and "fill" attribute for all following empty cells
+ // ignore it on row default XFs
+ if( eMode == xlXFModeRow )
+ return;
+
+ const XclImpXF* pXF = GetXFBuffer().GetXF( nXFIndex );
+ if( pXF && ((pXF->GetHorAlign() == EXC_XF_HOR_CENTER_AS) || (pXF->GetHorAlign() == EXC_XF_HOR_FILL)) )
+ {
+ // expand last merged range if this attribute is set repeatedly
+ ScRange* pRange = maMergeList.empty() ? nullptr : &maMergeList.back();
+ if (pRange && (pRange->aEnd.Row() == nScRow) && (pRange->aEnd.Col() + 1 == nScCol) && (eMode == xlXFModeBlank))
+ pRange->aEnd.IncCol();
+ else if( eMode != xlXFModeBlank ) // do not merge empty cells
+ maMergeList.push_back( ScRange( nScCol, nScRow, 0 ) );
+ }
+}
+
+void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeCell );
+}
+
+void XclImpXFRangeBuffer::SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeBlank );
+}
+
+void XclImpXFRangeBuffer::SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeBoolCell );
+}
+
+void XclImpXFRangeBuffer::SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex )
+{
+ for( SCCOL nScCol = 0; nScCol <= GetDoc().MaxCol(); ++nScCol )
+ SetXF( ScAddress( nScCol, nScRow, 0 ), nXFIndex, xlXFModeRow );
+}
+
+void XclImpXFRangeBuffer::SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex )
+{
+ // our array should not have values when creating the default column format.
+ size_t nIndex = static_cast< size_t >( nScCol );
+ if( maColumns.size() <= nIndex )
+ maColumns.resize( nIndex + 1 );
+ OSL_ENSURE( !maColumns[ nIndex ], "XclImpXFRangeBuffer::SetColumnDefXF - default column of XFs already has values" );
+ maColumns[ nIndex ] = std::make_shared<XclImpXFRangeColumn>();
+ maColumns[ nIndex ]->SetDefaultXF( XclImpXFIndex( nXFIndex ), GetRoot());
+}
+
+void XclImpXFRangeBuffer::SetBorderLine( const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
+{
+ SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
+ SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
+ ScDocument& rDoc = GetDoc();
+
+ const SvxBoxItem* pFromItem =
+ rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
+ const SvxBoxItem* pToItem =
+ rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
+
+ SvxBoxItem aNewItem( *pToItem );
+ aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
+ rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
+}
+
+void XclImpXFRangeBuffer::SetHyperlink( const XclRange& rXclRange, const OUString& rUrl )
+{
+ maHyperlinks.emplace_back( rXclRange, rUrl );
+}
+
+void XclImpXFRangeBuffer::SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 )
+{
+ if( (nScCol1 < nScCol2) || (nScRow1 < nScRow2) )
+ maMergeList.push_back( ScRange( nScCol1, nScRow1, 0, nScCol2, nScRow2, 0 ) );
+}
+
+void XclImpXFRangeBuffer::Finalize()
+{
+ ScDocumentImport& rDocImport = GetDocImport();
+ ScDocument& rDoc = rDocImport.getDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // apply patterns
+ XclImpXFBuffer& rXFBuffer = GetXFBuffer();
+ ScDocumentImport::Attrs aPendingAttrParam;
+ SCCOL pendingColStart = -1;
+ SCCOL pendingColEnd = -1;
+ SCCOL nScCol = 0;
+ for( const auto& rxColumn : maColumns )
+ {
+ // apply all cell styles of an existing column
+ if( rxColumn )
+ {
+ XclImpXFRangeColumn& rColumn = *rxColumn;
+ std::vector<ScAttrEntry> aAttrs;
+ aAttrs.reserve(rColumn.end() - rColumn.begin());
+
+ for (const auto& rxStyle : rColumn)
+ {
+ XclImpXFRange& rStyle = *rxStyle;
+ const XclImpXFIndex& rXFIndex = rStyle.maXFIndex;
+ XclImpXF* pXF = rXFBuffer.GetXF( rXFIndex.GetXFIndex() );
+ if (!pXF)
+ continue;
+
+ sal_uInt32 nForceScNumFmt = rXFIndex.IsBoolCell() ?
+ GetNumFmtBuffer().GetStdScNumFmt() : NUMBERFORMAT_ENTRY_NOT_FOUND;
+
+ pXF->ApplyPatternToAttrVector(aAttrs, rStyle.mnScRow1, rStyle.mnScRow2, nForceScNumFmt);
+ }
+
+ if (aAttrs.empty() || aAttrs.back().nEndRow != rDoc.MaxRow())
+ {
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = rDoc.MaxRow();
+ aEntry.pPattern = rDoc.GetDefPattern();
+ aAttrs.push_back(aEntry);
+ }
+
+ aAttrs.shrink_to_fit();
+ assert(aAttrs.size() > 0);
+ ScDocumentImport::Attrs aAttrParam;
+ aAttrParam.mvData.swap(aAttrs);
+ aAttrParam.mbLatinNumFmtOnly = false; // when unsure, set it to false.
+
+ // Compress setting the attributes, set the same set in one call.
+ if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
+ ++pendingColEnd;
+ else
+ {
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+ pendingColStart = pendingColEnd = nScCol;
+ aPendingAttrParam = std::move( aAttrParam );
+ }
+ }
+ ++nScCol;
+ }
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+
+ // insert hyperlink cells
+ for( const auto& [rXclRange, rUrl] : maHyperlinks )
+ XclImpHyperlink::InsertUrl( GetRoot(), rXclRange, rUrl );
+
+ // apply cell merging
+ for ( size_t i = 0, nRange = maMergeList.size(); i < nRange; ++i )
+ {
+ const ScRange & rRange = maMergeList[ i ];
+ const ScAddress& rStart = rRange.aStart;
+ const ScAddress& rEnd = rRange.aEnd;
+ bool bMultiCol = rStart.Col() != rEnd.Col();
+ bool bMultiRow = rStart.Row() != rEnd.Row();
+ // set correct right border
+ if( bMultiCol )
+ SetBorderLine( rRange, nScTab, SvxBoxItemLine::RIGHT );
+ // set correct lower border
+ if( bMultiRow )
+ SetBorderLine( rRange, nScTab, SvxBoxItemLine::BOTTOM );
+ // do merge
+ if( bMultiCol || bMultiRow )
+ rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), nScTab );
+ // #i93609# merged range in a single row: test if manual row height is needed
+ if( !bMultiRow )
+ {
+ bool bTextWrap = rDoc.GetAttr( rStart, ATTR_LINEBREAK )->GetValue();
+ if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
+ if (const EditTextObject* pEditObj = rDoc.GetEditText(rStart))
+ bTextWrap = pEditObj->GetParagraphCount() > 1;
+ if( bTextWrap )
+ GetOldRoot().pColRowBuff->SetManualRowHeight( rStart.Row() );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiview.cxx b/sc/source/filter/excel/xiview.cxx
new file mode 100644
index 0000000000..49878de6ee
--- /dev/null
+++ b/sc/source/filter/excel/xiview.cxx
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xiview.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <viewopti.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xistyle.hxx>
+
+// Document view settings =====================================================
+
+XclImpDocViewSettings::XclImpDocViewSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpDocViewSettings::ReadWindow1( XclImpStream& rStrm )
+{
+ maData.mnWinX = rStrm.ReaduInt16();
+ maData.mnWinY = rStrm.ReaduInt16();
+ maData.mnWinWidth = rStrm.ReaduInt16();
+ maData.mnWinHeight = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ maData.mnDisplXclTab = rStrm.ReaduInt16();
+ maData.mnFirstVisXclTab = rStrm.ReaduInt16();
+ maData.mnXclSelectCnt = rStrm.ReaduInt16();
+ maData.mnTabBarWidth = rStrm.ReaduInt16();
+ }
+}
+
+SCTAB XclImpDocViewSettings::GetDisplScTab() const
+{
+ /* Simply cast Excel index to Calc index.
+ TODO: This may fail if the document contains scenarios. */
+ sal_uInt16 nMaxXclTab = static_cast< sal_uInt16 >( GetMaxPos().Tab() );
+ return static_cast< SCTAB >( (maData.mnDisplXclTab <= nMaxXclTab) ? maData.mnDisplXclTab : 0 );
+}
+
+void XclImpDocViewSettings::Finalize()
+{
+ ScViewOptions aViewOpt( GetDoc().GetViewOptions() );
+ aViewOpt.SetOption( VOPT_HSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_HOR_SCROLLBAR ) );
+ aViewOpt.SetOption( VOPT_VSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_VER_SCROLLBAR ) );
+ aViewOpt.SetOption( VOPT_TABCONTROLS, ::get_flag( maData.mnFlags, EXC_WIN1_TABBAR ) );
+ GetDoc().SetViewOptions( aViewOpt );
+
+ // displayed sheet
+ GetExtDocOptions().GetDocSettings().mnDisplTab = GetDisplScTab();
+
+ // width of the tabbar with sheet names
+ if( maData.mnTabBarWidth <= 1000 )
+ GetExtDocOptions().GetDocSettings().mfTabBarWidth = static_cast< double >( maData.mnTabBarWidth ) / 1000.0;
+}
+
+// Sheet view settings ========================================================
+
+namespace {
+
+tools::Long lclGetScZoom( sal_uInt16 nXclZoom, sal_uInt16 nDefZoom )
+{
+ return static_cast< tools::Long >( nXclZoom ? nXclZoom : nDefZoom );
+}
+
+} // namespace
+
+XclImpTabViewSettings::XclImpTabViewSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ Initialize();
+}
+
+void XclImpTabViewSettings::Initialize()
+{
+ maData.SetDefaults();
+}
+
+void XclImpTabViewSettings::ReadTabBgColor( XclImpStream& rStrm, const XclImpPalette& rPal )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF8 );
+ if( GetBiff() < EXC_BIFF8 )
+ return;
+
+ sal_uInt8 ColorIndex;
+
+ rStrm.Ignore( 16 );
+ ColorIndex = rStrm.ReaduInt8() & EXC_SHEETEXT_TABCOLOR; //0x7F
+ if ( ColorIndex >= 8 && ColorIndex <= 63 ) //only accept valid index values
+ {
+ maData.maTabBgColor = rPal.GetColor( ColorIndex );
+ }
+}
+
+void XclImpTabViewSettings::ReadWindow2( XclImpStream& rStrm, bool bChart )
+{
+ if( GetBiff() == EXC_BIFF2 )
+ {
+ maData.mbShowFormulas = rStrm.ReaduInt8() != 0;
+ maData.mbShowGrid = rStrm.ReaduInt8() != 0;
+ maData.mbShowHeadings = rStrm.ReaduInt8() != 0;
+ maData.mbFrozenPanes = rStrm.ReaduInt8() != 0;
+ maData.mbShowZeros = rStrm.ReaduInt8() != 0;
+ rStrm >> maData.maFirstXclPos;
+ maData.mbDefGridColor = rStrm.ReaduInt8() != 0;
+ rStrm >> maData.maGridColor;
+ }
+ else
+ {
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ rStrm >> maData.maFirstXclPos;
+
+ // #i59590# real life: Excel ignores some view settings in chart sheets
+ maData.mbSelected = ::get_flag( nFlags, EXC_WIN2_SELECTED );
+ maData.mbDisplayed = ::get_flag( nFlags, EXC_WIN2_DISPLAYED );
+ maData.mbMirrored = !bChart && ::get_flag( nFlags, EXC_WIN2_MIRRORED );
+ maData.mbFrozenPanes = !bChart && ::get_flag( nFlags, EXC_WIN2_FROZEN );
+ maData.mbPageMode = !bChart && ::get_flag( nFlags, EXC_WIN2_PAGEBREAKMODE );
+ maData.mbDefGridColor = bChart || ::get_flag( nFlags, EXC_WIN2_DEFGRIDCOLOR );
+ maData.mbShowFormulas = !bChart && ::get_flag( nFlags, EXC_WIN2_SHOWFORMULAS );
+ maData.mbShowGrid = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWGRID );
+ maData.mbShowHeadings = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWHEADINGS );
+ maData.mbShowZeros = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWZEROS );
+ maData.mbShowOutline = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWOUTLINE );
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ rStrm >> maData.maGridColor;
+ break;
+ case EXC_BIFF8:
+ {
+ sal_uInt16 nGridColorIdx;
+ nGridColorIdx = rStrm.ReaduInt16();
+ // zoom data not included in chart sheets
+ if( rStrm.GetRecLeft() >= 6 )
+ {
+ rStrm.Ignore( 2 );
+ maData.mnPageZoom = rStrm.ReaduInt16();
+ maData.mnNormalZoom = rStrm.ReaduInt16();
+ }
+
+ if( !maData.mbDefGridColor )
+ maData.maGridColor = GetPalette().GetColor( nGridColorIdx );
+ }
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+ }
+
+ // do not scroll chart sheets
+ if( bChart )
+ maData.maFirstXclPos.Set( 0, 0 );
+}
+
+void XclImpTabViewSettings::ReadScl( XclImpStream& rStrm )
+{
+ sal_uInt16 nNum, nDenom;
+ nNum = rStrm.ReaduInt16();
+ nDenom = rStrm.ReaduInt16();
+ OSL_ENSURE( nDenom > 0, "XclImpPageSettings::ReadScl - invalid denominator" );
+ if( nDenom > 0 )
+ maData.mnCurrentZoom = limit_cast< sal_uInt16 >( (nNum * 100) / nDenom );
+}
+
+void XclImpTabViewSettings::ReadPane( XclImpStream& rStrm )
+{
+ maData.mnSplitX = rStrm.ReaduInt16();
+ maData.mnSplitY = rStrm.ReaduInt16();
+
+ rStrm >> maData.maSecondXclPos;
+ maData.mnActivePane = rStrm.ReaduInt8();
+}
+
+void XclImpTabViewSettings::ReadSelection( XclImpStream& rStrm )
+{
+ // pane of this selection
+ sal_uInt8 nPane;
+ nPane = rStrm.ReaduInt8();
+ XclSelectionData& rSelData = maData.CreateSelectionData( nPane );
+ // cursor position and selection
+ rStrm >> rSelData.maXclCursor;
+ rSelData.mnCursorIdx = rStrm.ReaduInt16();
+ rSelData.maXclSelection.Read( rStrm, false );
+}
+
+void XclImpTabViewSettings::Finalize()
+{
+ SCTAB nScTab = GetCurrScTab();
+ ScDocument& rDoc = GetDoc();
+ XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ ScExtTabSettings& rTabSett = GetExtDocOptions().GetOrCreateTabSettings( nScTab );
+ bool bDisplayed = GetDocViewSettings().GetDisplScTab() == nScTab;
+
+ // *** sheet options: cursor, selection, splits, zoom ***
+
+ // sheet flags
+ if( maData.mbMirrored )
+ // do not call this function with sal_False, it would mirror away all drawing objects
+ rDoc.SetLayoutRTL( nScTab, true );
+ rTabSett.mbSelected = maData.mbSelected || bDisplayed;
+
+ // first visible cell in top-left pane and in additional pane(s)
+ rTabSett.maFirstVis = rAddrConv.CreateValidAddress( maData.maFirstXclPos, nScTab, false );
+ rTabSett.maSecondVis = rAddrConv.CreateValidAddress( maData.maSecondXclPos, nScTab, false );
+
+ // cursor position and selection
+ if( const XclSelectionData* pSelData = maData.GetSelectionData( maData.mnActivePane ) )
+ {
+ rTabSett.maCursor = rAddrConv.CreateValidAddress( pSelData->maXclCursor, nScTab, false );
+ rAddrConv.ConvertRangeList( rTabSett.maSelection, pSelData->maXclSelection, nScTab, false );
+ }
+
+ // active pane
+ switch( maData.mnActivePane )
+ {
+ case EXC_PANE_TOPLEFT: rTabSett.meActivePane = SCEXT_PANE_TOPLEFT; break;
+ case EXC_PANE_TOPRIGHT: rTabSett.meActivePane = SCEXT_PANE_TOPRIGHT; break;
+ case EXC_PANE_BOTTOMLEFT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMLEFT; break;
+ case EXC_PANE_BOTTOMRIGHT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMRIGHT; break;
+ }
+
+ // freeze/split position
+ rTabSett.mbFrozenPanes = maData.mbFrozenPanes;
+ if( maData.mbFrozenPanes )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */
+ if( (maData.mnSplitX > 0) && (maData.maFirstXclPos.mnCol + maData.mnSplitX <= GetScMaxPos().Col()) )
+ rTabSett.maFreezePos.SetCol( static_cast< SCCOL >( maData.maFirstXclPos.mnCol + maData.mnSplitX ) );
+ if( (maData.mnSplitY > 0) && (maData.maFirstXclPos.mnRow + maData.mnSplitY <= o3tl::make_unsigned(GetScMaxPos().Row())) )
+ rTabSett.maFreezePos.SetRow( static_cast< SCROW >( maData.maFirstXclPos.mnRow + maData.mnSplitY ) );
+ }
+ else
+ {
+ // split window: position is in twips
+ rTabSett.maSplitPos.setX( static_cast< tools::Long >( maData.mnSplitX ) );
+ rTabSett.maSplitPos.setY( static_cast< tools::Long >( maData.mnSplitY ) );
+ }
+
+ // grid color
+ if( maData.mbDefGridColor )
+ rTabSett.maGridColor = COL_AUTO;
+ else
+ rTabSett.maGridColor = maData.maGridColor;
+
+ // show grid option
+ rTabSett.mbShowGrid = maData.mbShowGrid;
+
+ // view mode and zoom
+ if( maData.mnCurrentZoom != 0 )
+ (maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom) = maData.mnCurrentZoom;
+ rTabSett.mbPageMode = maData.mbPageMode;
+ rTabSett.mnNormalZoom = lclGetScZoom( maData.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF );
+ rTabSett.mnPageZoom = lclGetScZoom( maData.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF );
+
+ // *** additional handling for displayed sheet ***
+
+ if( bDisplayed )
+ {
+ // set Excel sheet settings globally at Calc document, take settings from displayed sheet
+ ScViewOptions aViewOpt( rDoc.GetViewOptions() );
+ aViewOpt.SetOption( VOPT_FORMULAS, maData.mbShowFormulas );
+ aViewOpt.SetOption( VOPT_HEADER, maData.mbShowHeadings );
+ aViewOpt.SetOption( VOPT_NULLVALS, maData.mbShowZeros );
+ aViewOpt.SetOption( VOPT_OUTLINER, maData.mbShowOutline );
+ rDoc.SetViewOptions( aViewOpt );
+ }
+
+ // *** set tab bg color
+ if ( !maData.IsDefaultTabBgColor() )
+ rDoc.SetTabBgColor(nScTab, maData.maTabBgColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xladdress.cxx b/sc/source/filter/excel/xladdress.cxx
new file mode 100644
index 0000000000..20ef4fa7fc
--- /dev/null
+++ b/sc/source/filter/excel/xladdress.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xladdress.hxx>
+#include <xestream.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+void XclAddress::Read( XclImpStream& rStrm )
+{
+ mnRow = rStrm.ReaduInt16();
+ mnCol = rStrm.ReaduInt16();
+}
+
+void XclAddress::Write( XclExpStream& rStrm ) const
+{
+ rStrm << static_cast<sal_uInt16> (mnRow);
+ rStrm << mnCol;
+}
+
+bool XclRange::Contains( const XclAddress& rPos ) const
+{
+ return (maFirst.mnCol <= rPos.mnCol) && (rPos.mnCol <= maLast.mnCol) &&
+ (maFirst.mnRow <= rPos.mnRow) && (rPos.mnRow <= maLast.mnRow);
+}
+
+void XclRange::Read( XclImpStream& rStrm, bool bCol16Bit )
+{
+ maFirst.mnRow = rStrm.ReaduInt16();
+ maLast.mnRow = rStrm.ReaduInt16();
+
+ if( bCol16Bit )
+ {
+ maFirst.mnCol = rStrm.ReaduInt16();
+ maLast.mnCol = rStrm.ReaduInt16();
+ }
+ else
+ {
+ maFirst.mnCol = rStrm.ReaduInt8();
+ maLast.mnCol = rStrm.ReaduInt8();
+ }
+}
+
+void XclRange::Write( XclExpStream& rStrm, bool bCol16Bit ) const
+{
+ rStrm << static_cast<sal_uInt16>(maFirst.mnRow) << static_cast<sal_uInt16>(maLast.mnRow);
+ if( bCol16Bit )
+ rStrm << maFirst.mnCol << maLast.mnCol;
+ else
+ rStrm << static_cast< sal_uInt8 >( maFirst.mnCol ) << static_cast< sal_uInt8 >( maLast.mnCol );
+}
+
+XclRange XclRangeList::GetEnclosingRange() const
+{
+ XclRange aXclRange;
+ if( !mRanges.empty() )
+ {
+ XclRangeVector::const_iterator aIt = mRanges.begin(), aEnd = mRanges.end();
+ aXclRange = *aIt;
+ for( ++aIt; aIt != aEnd; ++aIt )
+ {
+ aXclRange.maFirst.mnCol = ::std::min( aXclRange.maFirst.mnCol, aIt->maFirst.mnCol );
+ aXclRange.maFirst.mnRow = ::std::min( aXclRange.maFirst.mnRow, aIt->maFirst.mnRow );
+ aXclRange.maLast.mnCol = ::std::max( aXclRange.maLast.mnCol, aIt->maLast.mnCol );
+ aXclRange.maLast.mnRow = ::std::max( aXclRange.maLast.mnRow, aIt->maLast.mnRow );
+ }
+ }
+ return aXclRange;
+}
+
+void XclRangeList::Read( XclImpStream& rStrm, bool bCol16Bit, sal_uInt16 nCountInStream )
+{
+ sal_uInt16 nCount;
+ if (nCountInStream)
+ nCount = nCountInStream;
+ else
+ nCount = rStrm.ReaduInt16();
+
+ if (!nCount)
+ return;
+
+ XclRange aRange;
+ while (true)
+ {
+ aRange.Read(rStrm, bCol16Bit);
+ if (!rStrm.IsValid())
+ break;
+ mRanges.emplace_back(aRange);
+ --nCount;
+ if (!nCount)
+ break;
+ }
+}
+
+void XclRangeList::Write( XclExpStream& rStrm, bool bCol16Bit, sal_uInt16 nCountInStream ) const
+{
+ WriteSubList( rStrm, 0, mRanges.size(), bCol16Bit, nCountInStream );
+}
+
+void XclRangeList::WriteSubList( XclExpStream& rStrm, size_t nBegin, size_t nCount, bool bCol16Bit,
+ sal_uInt16 nCountInStream ) const
+{
+ OSL_ENSURE( nBegin <= mRanges.size(), "XclRangeList::WriteSubList - invalid start position" );
+ size_t nEnd = ::std::min< size_t >( nBegin + nCount, mRanges.size() );
+ if (!nCountInStream)
+ {
+ sal_uInt16 nXclCount = ulimit_cast< sal_uInt16 >( nEnd - nBegin );
+ rStrm << nXclCount;
+ }
+ rStrm.SetSliceSize( bCol16Bit ? 8 : 6 );
+ std::for_each(mRanges.begin() + nBegin, mRanges.begin() + nEnd,
+ [&rStrm, &bCol16Bit](const XclRange& rRange) { rRange.Write(rStrm, bCol16Bit); });
+}
+
+XclAddressConverterBase::XclAddressConverterBase( XclTracer& rTracer, const ScAddress& rMaxPos ) :
+ mrTracer( rTracer ),
+ maMaxPos( rMaxPos ),
+ mnMaxCol( static_cast< sal_uInt16 >( rMaxPos.Col() ) ),
+ mnMaxRow( static_cast< sal_uInt16 >( rMaxPos.Row() ) ),
+ mbColTrunc( false ),
+ mbRowTrunc( false ),
+ mbTabTrunc( false )
+{
+ OSL_ENSURE( o3tl::make_unsigned( rMaxPos.Col() ) <= SAL_MAX_UINT16, "XclAddressConverterBase::XclAddressConverterBase - invalid max column" );
+ OSL_ENSURE( o3tl::make_unsigned( rMaxPos.Row() ) <= SAL_MAX_UINT32, "XclAddressConverterBase::XclAddressConverterBase - invalid max row" );
+}
+
+XclAddressConverterBase::~XclAddressConverterBase()
+{
+}
+
+void XclAddressConverterBase::CheckScTab( SCTAB nScTab )
+{
+ bool bValid = (0 <= nScTab) && (nScTab <= maMaxPos.Tab());
+ if( !bValid )
+ {
+ mbTabTrunc |= (nScTab > maMaxPos.Tab()); // do not warn for deleted refs
+ mrTracer.TraceInvalidTab( nScTab, maMaxPos.Tab() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlchart.cxx b/sc/source/filter/excel/xlchart.cxx
new file mode 100644
index 0000000000..011d4578ff
--- /dev/null
+++ b/sc/source/filter/excel/xlchart.cxx
@@ -0,0 +1,1284 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xlchart.hxx>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/XAxisXSupplier.hpp>
+#include <com/sun/star/chart/XAxisYSupplier.hpp>
+#include <com/sun/star/chart/XAxisZSupplier.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <sal/macros.h>
+#include <sal/mathconf.h>
+#include <svl/itemset.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/unomid.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include <xlroot.hxx>
+#include <xlstyle.hxx>
+#include <xltools.hxx>
+
+using namespace css;
+
+// Common =====================================================================
+
+XclChRectangle::XclChRectangle() :
+ mnX( 0 ),
+ mnY( 0 ),
+ mnWidth( 0 ),
+ mnHeight( 0 )
+{
+}
+
+XclChDataPointPos::XclChDataPointPos( sal_uInt16 nSeriesIdx, sal_uInt16 nPointIdx ) :
+ mnSeriesIdx( nSeriesIdx ),
+ mnPointIdx( nPointIdx )
+{
+}
+
+bool operator<( const XclChDataPointPos& rL, const XclChDataPointPos& rR )
+{
+ return (rL.mnSeriesIdx < rR.mnSeriesIdx) ||
+ ((rL.mnSeriesIdx == rR.mnSeriesIdx) && (rL.mnPointIdx < rR.mnPointIdx));
+}
+
+XclChFrBlock::XclChFrBlock( sal_uInt16 nType ) :
+ mnType( nType ),
+ mnContext( 0 ),
+ mnValue1( 0 ),
+ mnValue2( 0 )
+{
+}
+
+// Frame formatting ===========================================================
+
+XclChFramePos::XclChFramePos() :
+ mnTLMode( EXC_CHFRAMEPOS_PARENT ),
+ mnBRMode( EXC_CHFRAMEPOS_PARENT )
+{
+}
+
+XclChLineFormat::XclChLineFormat() :
+ maColor( COL_BLACK ),
+ mnPattern( EXC_CHLINEFORMAT_SOLID ),
+ mnWeight( EXC_CHLINEFORMAT_SINGLE ),
+ mnFlags( EXC_CHLINEFORMAT_AUTO )
+{
+}
+
+XclChAreaFormat::XclChAreaFormat() :
+ maPattColor( COL_WHITE ),
+ maBackColor( COL_BLACK ),
+ mnPattern( EXC_PATT_SOLID ),
+ mnFlags( EXC_CHAREAFORMAT_AUTO )
+{
+}
+
+XclChEscherFormat::XclChEscherFormat()
+{
+}
+
+XclChEscherFormat::~XclChEscherFormat()
+{
+}
+
+XclChPicFormat::XclChPicFormat() :
+ mnBmpMode( EXC_CHPICFORMAT_NONE ),
+ mnFlags( EXC_CHPICFORMAT_TOPBOTTOM | EXC_CHPICFORMAT_FRONTBACK | EXC_CHPICFORMAT_LEFTRIGHT ),
+ mfScale( 0.5 )
+{
+}
+
+XclChFrame::XclChFrame() :
+ mnFormat( EXC_CHFRAME_STANDARD ),
+ mnFlags( EXC_CHFRAME_AUTOSIZE | EXC_CHFRAME_AUTOPOS )
+{
+}
+
+// Source links ===============================================================
+
+XclChSourceLink::XclChSourceLink() :
+ mnDestType( EXC_CHSRCLINK_TITLE ),
+ mnLinkType( EXC_CHSRCLINK_DEFAULT ),
+ mnFlags( 0 ),
+ mnNumFmtIdx( 0 )
+{
+}
+
+// Text =======================================================================
+
+XclChObjectLink::XclChObjectLink() :
+ mnTarget( EXC_CHOBJLINK_NONE )
+{
+}
+
+XclChFrLabelProps::XclChFrLabelProps() :
+ mnFlags( 0 )
+{
+}
+
+XclChText::XclChText() :
+ mnHAlign( EXC_CHTEXT_ALIGN_CENTER ),
+ mnVAlign( EXC_CHTEXT_ALIGN_CENTER ),
+ mnBackMode( EXC_CHTEXT_TRANSPARENT ),
+ mnFlags( EXC_CHTEXT_AUTOCOLOR | EXC_CHTEXT_AUTOFILL ),
+ mnFlags2( EXC_CHTEXT_POS_DEFAULT ),
+ mnRotation( EXC_ROT_NONE )
+{
+ maTextComplexColor.setColor(COL_BLACK);
+}
+
+// Data series ================================================================
+
+XclChMarkerFormat::XclChMarkerFormat() :
+ maLineColor( COL_BLACK ),
+ maFillColor( COL_WHITE ),
+ mnMarkerSize( EXC_CHMARKERFORMAT_SINGLESIZE ),
+ mnMarkerType( EXC_CHMARKERFORMAT_NOSYMBOL ),
+ mnFlags( EXC_CHMARKERFORMAT_AUTO )
+{
+};
+
+XclCh3dDataFormat::XclCh3dDataFormat() :
+ mnBase( EXC_CH3DDATAFORMAT_RECT ),
+ mnTop( EXC_CH3DDATAFORMAT_STRAIGHT )
+{
+}
+
+XclChDataFormat::XclChDataFormat() :
+ mnFormatIdx( EXC_CHDATAFORMAT_DEFAULT ),
+ mnFlags( 0 )
+{
+}
+
+XclChSerTrendLine::XclChSerTrendLine() :
+ mfForecastFor( 0.0 ),
+ mfForecastBack( 0.0 ),
+ mnLineType( EXC_CHSERTREND_POLYNOMIAL ),
+ mnOrder( 1 ),
+ mnShowEquation( 0 ),
+ mnShowRSquared( 0 )
+{
+ /* Set all bits in mfIntercept to 1 (that is -1.#NAN) to indicate that
+ there is no interception point. Cannot use ::rtl::math::setNan() here
+ cause it misses the sign bit. */
+ sal_math_Double* pDouble = reinterpret_cast< sal_math_Double* >( &mfIntercept );
+ pDouble->w32_parts.msw = pDouble->w32_parts.lsw = 0xFFFFFFFF;
+}
+
+XclChSerErrorBar::XclChSerErrorBar() :
+ mfValue( 0.0 ),
+ mnValueCount( 1 ),
+ mnBarType( EXC_CHSERERR_NONE ),
+ mnSourceType( EXC_CHSERERR_FIXED ),
+ mnLineEnd( EXC_CHSERERR_END_TSHAPE )
+{
+}
+
+XclChSeries::XclChSeries() :
+ mnCategType( EXC_CHSERIES_NUMERIC ),
+ mnValueType( EXC_CHSERIES_NUMERIC ),
+ mnBubbleType( EXC_CHSERIES_NUMERIC ),
+ mnCategCount( 0 ),
+ mnValueCount( 0 ),
+ mnBubbleCount( 0 )
+{
+}
+
+// Chart type groups ==========================================================
+
+XclChType::XclChType() :
+ mnOverlap( 0 ),
+ mnGap( 150 ),
+ mnRotation( 0 ),
+ mnPieHole( 0 ),
+ mnBubbleSize( 100 ),
+ mnBubbleType( EXC_CHSCATTER_AREA ),
+ mnFlags( 0 )
+{
+}
+
+XclChChart3d::XclChChart3d() :
+ mnRotation( 20 ),
+ mnElevation( 15 ),
+ mnEyeDist( 30 ),
+ mnRelHeight( 100 ),
+ mnRelDepth( 100 ),
+ mnDepthGap( 150 ),
+ mnFlags( EXC_CHCHART3D_AUTOHEIGHT )
+{
+}
+
+XclChLegend::XclChLegend() :
+ mnDockMode( EXC_CHLEGEND_RIGHT ),
+ mnSpacing( EXC_CHLEGEND_MEDIUM ),
+ mnFlags( EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOSERIES |
+ EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY | EXC_CHLEGEND_STACKED )
+{
+}
+
+XclChTypeGroup::XclChTypeGroup() :
+ mnFlags( 0 ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE )
+{
+}
+
+XclChProperties::XclChProperties() :
+ mnFlags( 0 ),
+ mnEmptyMode( EXC_CHPROPS_EMPTY_SKIP )
+{
+}
+
+// Axes =======================================================================
+
+XclChLabelRange::XclChLabelRange() :
+ mnCross( 1 ),
+ mnLabelFreq( 1 ),
+ mnTickFreq( 1 ),
+ mnFlags( 0 )
+{
+}
+
+XclChDateRange::XclChDateRange() :
+ mnMinDate( 0 ),
+ mnMaxDate( 0 ),
+ mnMajorStep( 0 ),
+ mnMajorUnit( EXC_CHDATERANGE_DAYS ),
+ mnMinorStep( 0 ),
+ mnMinorUnit( EXC_CHDATERANGE_DAYS ),
+ mnBaseUnit( EXC_CHDATERANGE_DAYS ),
+ mnCross( 0 ),
+ mnFlags( EXC_CHDATERANGE_AUTOMIN | EXC_CHDATERANGE_AUTOMAX |
+ EXC_CHDATERANGE_AUTOMAJOR | EXC_CHDATERANGE_AUTOMINOR |
+ EXC_CHDATERANGE_AUTOBASE | EXC_CHDATERANGE_AUTOCROSS |
+ EXC_CHDATERANGE_AUTODATE )
+{
+}
+
+XclChValueRange::XclChValueRange() :
+ mfMin( 0.0 ),
+ mfMax( 0.0 ),
+ mfMajorStep( 0.0 ),
+ mfMinorStep( 0.0 ),
+ mfCross( 0.0 ),
+ mnFlags( EXC_CHVALUERANGE_AUTOMIN | EXC_CHVALUERANGE_AUTOMAX |
+ EXC_CHVALUERANGE_AUTOMAJOR | EXC_CHVALUERANGE_AUTOMINOR |
+ EXC_CHVALUERANGE_AUTOCROSS | EXC_CHVALUERANGE_BIT8 )
+{
+}
+
+XclChTick::XclChTick() :
+ mnMajor( EXC_CHTICK_INSIDE | EXC_CHTICK_OUTSIDE ),
+ mnMinor( 0 ),
+ mnLabelPos( EXC_CHTICK_NEXT ),
+ mnBackMode( EXC_CHTICK_TRANSPARENT ),
+ mnFlags( EXC_CHTICK_AUTOCOLOR | EXC_CHTICK_AUTOROT ),
+ mnRotation( EXC_ROT_NONE )
+{
+ maTextComplexColor.setColor(COL_BLACK);
+}
+
+XclChAxis::XclChAxis() :
+ mnType( EXC_CHAXIS_NONE )
+{
+}
+
+sal_Int32 XclChAxis::GetApiAxisDimension() const
+{
+ sal_Int32 nApiAxisDim = EXC_CHART_AXIS_NONE;
+ switch( mnType )
+ {
+ case EXC_CHAXIS_X: nApiAxisDim = EXC_CHART_AXIS_X; break;
+ case EXC_CHAXIS_Y: nApiAxisDim = EXC_CHART_AXIS_Y; break;
+ case EXC_CHAXIS_Z: nApiAxisDim = EXC_CHART_AXIS_Z; break;
+ }
+ return nApiAxisDim;
+}
+
+XclChAxesSet::XclChAxesSet() :
+ mnAxesSetId( EXC_CHAXESSET_PRIMARY )
+{
+}
+
+sal_Int32 XclChAxesSet::GetApiAxesSetIndex() const
+{
+ sal_Int32 nApiAxesSetIdx = EXC_CHART_AXESSET_NONE;
+ switch( mnAxesSetId )
+ {
+ case EXC_CHAXESSET_PRIMARY: nApiAxesSetIdx = EXC_CHART_AXESSET_PRIMARY; break;
+ case EXC_CHAXESSET_SECONDARY: nApiAxesSetIdx = EXC_CHART_AXESSET_SECONDARY; break;
+ }
+ return nApiAxesSetIdx;
+}
+
+// Static helper functions ====================================================
+
+sal_uInt16 XclChartHelper::GetSeriesLineAutoColorIdx( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnLineColors[] =
+ {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 63
+ };
+ return spnLineColors[ nFormatIdx % SAL_N_ELEMENTS( spnLineColors ) ];
+}
+
+sal_uInt16 XclChartHelper::GetSeriesFillAutoColorIdx( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnFillColors[] =
+ {
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23
+ };
+ return spnFillColors[ nFormatIdx % SAL_N_ELEMENTS( spnFillColors ) ];
+}
+
+sal_uInt8 XclChartHelper::GetSeriesFillAutoTransp( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt8 spnTrans[] = { 0x00, 0x40, 0x20, 0x60, 0x70 };
+ return spnTrans[ (nFormatIdx / 56) % SAL_N_ELEMENTS( spnTrans ) ];
+}
+
+sal_uInt16 XclChartHelper::GetAutoMarkerType( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnSymbols[] = {
+ EXC_CHMARKERFORMAT_DIAMOND, EXC_CHMARKERFORMAT_SQUARE, EXC_CHMARKERFORMAT_TRIANGLE,
+ EXC_CHMARKERFORMAT_CROSS, EXC_CHMARKERFORMAT_STAR, EXC_CHMARKERFORMAT_CIRCLE,
+ EXC_CHMARKERFORMAT_PLUS, EXC_CHMARKERFORMAT_DOWJ, EXC_CHMARKERFORMAT_STDDEV };
+ return spnSymbols[ nFormatIdx % SAL_N_ELEMENTS( spnSymbols ) ];
+}
+
+bool XclChartHelper::HasMarkerFillColor( sal_uInt16 nMarkerType )
+{
+ static const bool spbFilled[] = {
+ false, true, true, true, false, false, false, false, true, false };
+ return (nMarkerType < SAL_N_ELEMENTS( spbFilled )) && spbFilled[ nMarkerType ];
+}
+
+OUString XclChartHelper::GetErrorBarValuesRole( sal_uInt8 nBarType )
+{
+ switch( nBarType )
+ {
+ case EXC_CHSERERR_XPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSX;
+ case EXC_CHSERERR_XMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGX;
+ case EXC_CHSERERR_YPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSY;
+ case EXC_CHSERERR_YMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGY;
+ default: OSL_FAIL( "XclChartHelper::GetErrorBarValuesRole - unknown bar type" );
+ }
+ return OUString();
+}
+
+// Chart formatting info provider =============================================
+
+namespace {
+
+const XclChFormatInfo spFmtInfos[] =
+{
+ // object type property mode auto line color auto line weight auto pattern color missing frame type create delete isframe
+ { EXC_CHOBJTYPE_BACKGROUND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true },
+ { EXC_CHOBJTYPE_PLOTFRAME, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true },
+ { EXC_CHOBJTYPE_WALL3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, false, true },
+ { EXC_CHOBJTYPE_FLOOR3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, 23, EXC_CHFRAMETYPE_AUTO, true, false, true },
+ { EXC_CHOBJTYPE_TEXT, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, true },
+ { EXC_CHOBJTYPE_LEGEND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, true, true },
+ { EXC_CHOBJTYPE_LINEARSERIES, EXC_CHPROPMODE_LINEARSERIES, 0xFFFF, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false },
+ { EXC_CHOBJTYPE_FILLEDSERIES, EXC_CHPROPMODE_FILLEDSERIES, EXC_COLOR_CHBORDERAUTO, EXC_CHLINEFORMAT_SINGLE, 0xFFFF, EXC_CHFRAMETYPE_AUTO, false, false, true },
+ { EXC_CHOBJTYPE_AXISLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false },
+ { EXC_CHOBJTYPE_GRIDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, false },
+ { EXC_CHOBJTYPE_TRENDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_DOUBLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_ERRORBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_CONNECTLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_HILOLINE, EXC_CHPROPMODE_LINEARSERIES, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_WHITEDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, true },
+ { EXC_CHOBJTYPE_BLACKDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWTEXT, EXC_CHFRAMETYPE_INVISIBLE, false, false, true }
+};
+
+}
+
+XclChFormatInfoProvider::XclChFormatInfoProvider()
+{
+ for(auto const &rIt : spFmtInfos)
+ maInfoMap[ rIt.meObjType ] = &rIt;
+}
+
+const XclChFormatInfo& XclChFormatInfoProvider::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ XclFmtInfoMap::const_iterator aIt = maInfoMap.find( eObjType );
+ OSL_ENSURE( aIt != maInfoMap.end(), "XclChFormatInfoProvider::GetFormatInfo - unknown object type" );
+ return (aIt == maInfoMap.end()) ? *spFmtInfos : *aIt->second;
+}
+
+// Chart type info provider ===================================================
+
+namespace {
+
+// chart type service names
+const char SERVICE_CHART2_AREA[] = "com.sun.star.chart2.AreaChartType";
+const char SERVICE_CHART2_CANDLE[] = "com.sun.star.chart2.CandleStickChartType";
+const char SERVICE_CHART2_COLUMN[] = "com.sun.star.chart2.ColumnChartType";
+const char SERVICE_CHART2_LINE[] = "com.sun.star.chart2.LineChartType";
+const char SERVICE_CHART2_NET[] = "com.sun.star.chart2.NetChartType";
+const char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType";
+const char SERVICE_CHART2_PIE[] = "com.sun.star.chart2.PieChartType";
+const char SERVICE_CHART2_SCATTER[] = "com.sun.star.chart2.ScatterChartType";
+const char SERVICE_CHART2_BUBBLE[] = "com.sun.star.chart2.BubbleChartType";
+const char SERVICE_CHART2_SURFACE[] = "com.sun.star.chart2.ColumnChartType"; // Todo
+
+namespace csscd = css::chart::DataLabelPlacement;
+
+const XclChTypeInfo spTypeInfos[] =
+{
+ // chart type chart type category record id service varied point color def label pos comb2d 3d polar area2d area3d 1stvis xcateg swap stack revers betw
+ { EXC_CHTYPEID_BAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true },
+ { EXC_CHTYPEID_HORBAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, false, true, false, true, true, false, true, true, true, false, true },
+ { EXC_CHTYPEID_LINE, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_LINE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, true, false, false, true, false, true, false, true, false, false },
+ { EXC_CHTYPEID_AREA, EXC_CHTYPECATEG_LINE, EXC_ID_CHAREA, SERVICE_CHART2_AREA, EXC_CHVARPOINT_NONE, csscd::CENTER, true, true, false, true, true, false, true, false, true, true, false },
+ { EXC_CHTYPEID_STOCK, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_CANDLE, EXC_CHVARPOINT_NONE, csscd::RIGHT, true, false, false, false, false, false, true, false, true, false, false },
+ { EXC_CHTYPEID_RADARLINE, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARLINE, SERVICE_CHART2_NET, EXC_CHVARPOINT_SINGLE, csscd::TOP, false, false, true, false, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_RADARAREA, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARAREA, SERVICE_CHART2_FILLEDNET, EXC_CHVARPOINT_NONE, csscd::TOP, false, false, true, true, true, false, true, false, false, true, false },
+ { EXC_CHTYPEID_PIE, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, true, true, false, false, false, false },
+ { EXC_CHTYPEID_DONUT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_PIEEXT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIEEXT, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, false, true, true, true, true, true, false, false, false, false },
+ { EXC_CHTYPEID_SCATTER, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_SCATTER, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, false, false, false, true, false, false, false, false, false, false },
+ { EXC_CHTYPEID_BUBBLES, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_BUBBLE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, false, false, false, true, true, false, false, false, false, false, false },
+ { EXC_CHTYPEID_SURFACE, EXC_CHTYPECATEG_SURFACE, EXC_ID_CHSURFACE, SERVICE_CHART2_SURFACE, EXC_CHVARPOINT_NONE, csscd::RIGHT, false, true, false, true, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_UNKNOWN, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true }
+};
+
+} // namespace
+
+XclChExtTypeInfo::XclChExtTypeInfo( const XclChTypeInfo& rTypeInfo ) :
+ XclChTypeInfo( rTypeInfo ),
+ mb3dChart( false ),
+ mbSpline( false )
+{
+}
+
+void XclChExtTypeInfo::Set( const XclChTypeInfo& rTypeInfo, bool b3dChart, bool bSpline )
+{
+ static_cast< XclChTypeInfo& >( *this ) = rTypeInfo;
+ mb3dChart = mbSupports3d && b3dChart;
+ mbSpline = bSpline;
+}
+
+XclChTypeInfoProvider::XclChTypeInfoProvider()
+{
+ for(const auto &rIt : spTypeInfos)
+ maInfoMap[ rIt.meTypeId ] = &rIt;
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfo( XclChTypeId eTypeId ) const
+{
+ XclChTypeInfoMap::const_iterator aIt = maInfoMap.find( eTypeId );
+ OSL_ENSURE( aIt != maInfoMap.end(), "XclChTypeInfoProvider::GetTypeInfo - unknown chart type" );
+ return (aIt == maInfoMap.end()) ? *maInfoMap.rbegin()->second : *aIt->second;
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromRecId( sal_uInt16 nRecId ) const
+{
+ for(const auto &rIt : spTypeInfos)
+ {
+ if(rIt.mnRecId == nRecId)
+ return rIt;
+ }
+ OSL_FAIL( "XclChTypeInfoProvider::GetTypeInfoFromRecId - unknown record id" );
+ return GetTypeInfo( EXC_CHTYPEID_UNKNOWN );
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromService( std::u16string_view rServiceName ) const
+{
+ for(auto const &rIt : spTypeInfos)
+ if( o3tl::equalsAscii( rServiceName, rIt.mpcServiceName ) )
+ return rIt;
+ OSL_FAIL( "XclChTypeInfoProvider::GetTypeInfoFromService - unknown service name" );
+ return GetTypeInfo( EXC_CHTYPEID_UNKNOWN );
+}
+
+// Property helpers ===========================================================
+
+XclChObjectTable::XclChObjectTable(uno::Reference<lang::XMultiServiceFactory> xFactory,
+ OUString aServiceName, OUString aObjNameBase ) :
+ mxFactory(std::move( xFactory )),
+ maServiceName(std::move( aServiceName )),
+ maObjNameBase(std::move( aObjNameBase )),
+ mnIndex( 0 )
+{
+}
+
+uno::Any XclChObjectTable::GetObject( const OUString& rObjName )
+{
+ // get object table
+ if( !mxContainer.is() )
+ mxContainer.set(ScfApiHelper::CreateInstance( mxFactory, maServiceName ), uno::UNO_QUERY);
+ OSL_ENSURE( mxContainer.is(), "XclChObjectTable::GetObject - container not found" );
+
+ uno::Any aObj;
+ if( mxContainer.is() )
+ {
+ // get object from container
+ try
+ {
+ aObj = mxContainer->getByName( rObjName );
+ }
+ catch (uno::Exception &)
+ {
+ OSL_FAIL( "XclChObjectTable::GetObject - object not found" );
+ }
+ }
+ return aObj;
+}
+
+OUString XclChObjectTable::InsertObject(const uno::Any& rObj)
+{
+
+ // create object table
+ if( !mxContainer.is() )
+ mxContainer.set(ScfApiHelper::CreateInstance( mxFactory, maServiceName ), uno::UNO_QUERY);
+ OSL_ENSURE( mxContainer.is(), "XclChObjectTable::InsertObject - container not found" );
+
+ OUString aObjName;
+ if( mxContainer.is() )
+ {
+ // create new unused identifier
+ do
+ {
+ aObjName = maObjNameBase + OUString::number( ++mnIndex );
+ }
+ while( mxContainer->hasByName( aObjName ) );
+
+ // insert object
+ try
+ {
+ mxContainer->insertByName( aObjName, rObj );
+ }
+ catch (uno::Exception &)
+ {
+ OSL_FAIL( "XclChObjectTable::InsertObject - cannot insert object" );
+ aObjName.clear();
+ }
+ }
+ return aObjName;
+}
+
+// Property names -------------------------------------------------------------
+
+namespace {
+
+/** Property names for line style in common objects. */
+const char* const sppcLineNamesCommon[] =
+ { "LineStyle", "LineWidth", "LineColor", "LineTransparence", "LineDashName", nullptr };
+/** Property names for line style in linear series objects. */
+const char* const sppcLineNamesLinear[] =
+ { "LineStyle", "LineWidth", "Color", "Transparency", "LineDashName", nullptr };
+/** Property names for line style in filled series objects. */
+const char* const sppcLineNamesFilled[] =
+ { "BorderStyle", "BorderWidth", "BorderColor", "BorderTransparency", "BorderDashName", nullptr };
+
+/** Property names for solid area style in common objects. */
+const char* const sppcAreaNamesCommon[] = { "FillStyle", "FillColor", "FillTransparence", nullptr };
+/** Property names for solid area style in filled series objects. */
+const char* const sppcAreaNamesFilled[] = { "FillStyle", "Color", "Transparency", nullptr };
+/** Property names for gradient area style in common objects. */
+const char* const sppcGradNamesCommon[] = { "FillStyle", "FillGradientName", nullptr };
+/** Property names for gradient area style in filled series objects. */
+const char* const sppcGradNamesFilled[] = { "FillStyle", "GradientName", nullptr };
+/** Property names for hatch area style in common objects. */
+const char* const sppcHatchNamesCommon[] = { "FillStyle", "FillHatchName", "FillColor", "FillBackground", nullptr };
+/** Property names for hatch area style in filled series objects. */
+const char* const sppcHatchNamesFilled[] = { "FillStyle", "HatchName", "Color", "FillBackground", nullptr };
+/** Property names for bitmap area style. */
+const char* const sppcBitmapNames[] = { "FillStyle", "FillBitmapName", "FillBitmapMode", nullptr };
+
+} // namespace
+
+XclChPropSetHelper::XclChPropSetHelper() :
+ maLineHlpCommon( sppcLineNamesCommon ),
+ maLineHlpLinear( sppcLineNamesLinear ),
+ maLineHlpFilled( sppcLineNamesFilled ),
+ maAreaHlpCommon( sppcAreaNamesCommon ),
+ maAreaHlpFilled( sppcAreaNamesFilled ),
+ maGradHlpCommon( sppcGradNamesCommon ),
+ maGradHlpFilled( sppcGradNamesFilled ),
+ maHatchHlpCommon( sppcHatchNamesCommon ),
+ maHatchHlpFilled( sppcHatchNamesFilled ),
+ maBitmapHlp( sppcBitmapNames )
+{
+}
+
+// read properties ------------------------------------------------------------
+
+void XclChPropSetHelper::ReadLineProperties(
+ XclChLineFormat& rLineFmt, XclChObjectTable& rDashTable,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read properties from property set
+ drawing::LineStyle eApiStyle = drawing::LineStyle_NONE;
+ sal_Int32 nApiWidth = 0;
+ sal_Int16 nApiTrans = 0;
+ uno::Any aDashNameAny;
+
+ ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode );
+ rLineHlp.ReadFromPropertySet( rPropSet );
+ rLineHlp >> eApiStyle >> nApiWidth >> rLineFmt.maColor >> nApiTrans >> aDashNameAny;
+
+ // clear automatic flag
+ ::set_flag( rLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
+
+ // line width
+ if( nApiWidth <= 0 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
+ else if( nApiWidth <= 35 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE;
+ else if( nApiWidth <= 70 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE;
+ else rLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE;
+
+ // line style
+ switch( eApiStyle )
+ {
+ case drawing::LineStyle_NONE:
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ break;
+ case drawing::LineStyle_SOLID:
+ {
+ if( nApiTrans < 13 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ else if( nApiTrans < 38 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS;
+ else if( nApiTrans < 63 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS;
+ else if( nApiTrans < 100 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS;
+ else rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ }
+ break;
+ case drawing::LineStyle_DASH:
+ {
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ OUString aDashName;
+ drawing::LineDash aApiDash;
+ if( (aDashNameAny >>= aDashName) && (rDashTable.GetObject( aDashName ) >>= aApiDash) )
+ {
+ // reorder dashes that are shorter than dots
+ if( (aApiDash.Dashes == 0) || (aApiDash.DashLen < aApiDash.DotLen) )
+ {
+ ::std::swap( aApiDash.Dashes, aApiDash.Dots );
+ ::std::swap( aApiDash.DashLen, aApiDash.DotLen );
+ }
+ // ignore dots that are nearly equal to dashes
+ if( aApiDash.DotLen * 3 > aApiDash.DashLen * 2 )
+ aApiDash.Dots = 0;
+
+ // convert line dash to predefined Excel dash types
+ if( (aApiDash.Dashes == 1) && (aApiDash.Dots >= 1) )
+ // one dash and one or more dots
+ rLineFmt.mnPattern = (aApiDash.Dots == 1) ?
+ EXC_CHLINEFORMAT_DASHDOT : EXC_CHLINEFORMAT_DASHDOTDOT;
+ else if( aApiDash.Dashes >= 1 )
+ // one or more dashes and no dots (also: dash-dash-dot)
+ rLineFmt.mnPattern = (aApiDash.DashLen < 250) ?
+ EXC_CHLINEFORMAT_DOT : EXC_CHLINEFORMAT_DASH;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::ReadLineProperties - unknown line style" );
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ }
+}
+
+bool XclChPropSetHelper::ReadAreaProperties( XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read properties from property set
+ drawing::FillStyle eApiStyle = drawing::FillStyle_NONE;
+ sal_Int16 nTransparency = 0;
+
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.ReadFromPropertySet( rPropSet );
+ rAreaHlp >> eApiStyle >> rAreaFmt.maPattColor >> nTransparency;
+
+ // clear automatic flag
+ ::set_flag( rAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
+
+ // set fill style transparent or solid (set solid for anything but transparent)
+ rAreaFmt.mnPattern = (eApiStyle == drawing::FillStyle_NONE) ? EXC_PATT_NONE : EXC_PATT_SOLID;
+
+ // return true to indicate complex fill (gradient, bitmap, solid transparency)
+ return (eApiStyle != drawing::FillStyle_NONE) && ((eApiStyle != drawing::FillStyle_SOLID) || (nTransparency > 0));
+}
+
+void XclChPropSetHelper::ReadEscherProperties(
+ XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
+ XclChObjectTable& rGradientTable, XclChObjectTable& rHatchTable, XclChObjectTable& rBitmapTable,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read style and transparency properties from property set
+ drawing::FillStyle eApiStyle = drawing::FillStyle_NONE;
+ Color aColor;
+ sal_Int16 nTransparency = 0;
+
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.ReadFromPropertySet( rPropSet );
+ rAreaHlp >> eApiStyle >> aColor >> nTransparency;
+
+ switch( eApiStyle )
+ {
+ case drawing::FillStyle_SOLID:
+ {
+ OSL_ENSURE( nTransparency > 0, "XclChPropSetHelper::ReadEscherProperties - unexpected solid area without transparency" );
+ if( (0 < nTransparency) && (nTransparency <= 100) )
+ {
+ // convert to Escher properties
+ sal_uInt32 nEscherColor = 0x02000000;
+ ::insert_value( nEscherColor, aColor.GetBlue(), 16, 8 );
+ ::insert_value( nEscherColor, aColor.GetGreen(), 8, 8 );
+ ::insert_value( nEscherColor, aColor.GetRed(), 0, 8 );
+ sal_uInt32 nEscherOpacity = static_cast< sal_uInt32 >( (100 - nTransparency) * 655.36 );
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, nEscherColor );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillOpacity, nEscherOpacity );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x02FFFFFF );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackOpacity, 0x00010000 );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fNoFillHitTest, 0x001F001C );
+ }
+ }
+ break;
+ case drawing::FillStyle_GRADIENT:
+ {
+ // extract gradient from global gradient table
+ OUString aGradientName;
+ ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode );
+ rGradHlp.ReadFromPropertySet( rPropSet );
+ rGradHlp >> eApiStyle >> aGradientName;
+ awt::Gradient aGradient;
+ if( rGradientTable.GetObject( aGradientName ) >>= aGradient )
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateGradientProperties( aGradient );
+ }
+ }
+ break;
+ case drawing::FillStyle_HATCH:
+ {
+ // extract hatch from global hatch table
+ OUString aHatchName;
+ bool bFillBackground;
+ ScfPropSetHelper& rHatchHlp = GetHatchHelper( ePropMode );
+ rHatchHlp.ReadFromPropertySet( rPropSet );
+ rHatchHlp >> eApiStyle >> aHatchName >> aColor >> bFillBackground;
+ drawing::Hatch aHatch;
+ if( rHatchTable.GetObject( aHatchName ) >>= aHatch )
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateEmbeddedHatchProperties( aHatch, aColor, bFillBackground );
+ rPicFmt.mnBmpMode = EXC_CHPICFORMAT_STACK;
+ }
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ {
+ // extract bitmap URL from global bitmap table
+ OUString aBitmapName;
+ drawing::BitmapMode eApiBmpMode;
+ maBitmapHlp.ReadFromPropertySet( rPropSet );
+ maBitmapHlp >> eApiStyle >> aBitmapName >> eApiBmpMode;
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (rBitmapTable.GetObject( aBitmapName ) >>= xBitmap)
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateEmbeddedBitmapProperties( xBitmap, eApiBmpMode );
+ rPicFmt.mnBmpMode = (eApiBmpMode == drawing::BitmapMode_REPEAT) ?
+ EXC_CHPICFORMAT_STACK : EXC_CHPICFORMAT_STRETCH;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::ReadEscherProperties - unknown fill style" );
+ }
+}
+
+void XclChPropSetHelper::ReadMarkerProperties(
+ XclChMarkerFormat& rMarkerFmt, const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
+{
+ chart2::Symbol aApiSymbol;
+ if( !rPropSet.GetProperty( aApiSymbol, EXC_CHPROP_SYMBOL ) )
+ return;
+
+ // clear automatic flag
+ ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
+
+ // symbol style
+ switch( aApiSymbol.Style )
+ {
+ case chart2::SymbolStyle_NONE:
+ rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
+ break;
+ case chart2::SymbolStyle_STANDARD:
+ switch( aApiSymbol.StandardSymbol )
+ {
+ case 0: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_SQUARE; break; // square
+ case 1: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // diamond
+ case 2: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // arrow down
+ case 3: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_TRIANGLE; break; // arrow up
+ case 4: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ; break; // arrow right, same as import
+ case 5: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // arrow left
+ case 6: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // bow tie
+ case 7: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // sand glass
+ case 8: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CIRCLE; break; // circle new in LibO3.5
+ case 9: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // star new in LibO3.5
+ case 10: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // X new in LibO3.5
+ case 11: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // plus new in LibO3.5
+ case 12: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // asterisk new in LibO3.5
+ case 13: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // horizontal bar new in LibO3.5
+ case 14: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // vertical bar new in LibO3.5
+ default: rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ }
+ break;
+ default:
+ rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ }
+ bool bHasFillColor = XclChartHelper::HasMarkerFillColor( rMarkerFmt.mnMarkerType );
+ ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOFILL, !bHasFillColor );
+
+ // symbol size
+ sal_Int32 nApiSize = (aApiSymbol.Size.Width + aApiSymbol.Size.Height + 1) / 2;
+ rMarkerFmt.mnMarkerSize = XclTools::GetTwipsFromHmm( nApiSize );
+
+ // symbol colors
+ rMarkerFmt.maLineColor = Color( ColorTransparency, aApiSymbol.BorderColor );
+ rMarkerFmt.maFillColor = Color( ColorTransparency, aApiSymbol.FillColor );
+}
+
+sal_uInt16 XclChPropSetHelper::ReadRotationProperties( const ScfPropertySet& rPropSet, bool bSupportsStacked )
+{
+ // chart2 handles rotation as double in the range [0,360)
+ double fAngle = 0.0;
+ rPropSet.GetProperty( fAngle, EXC_CHPROP_TEXTROTATION );
+ bool bStacked = bSupportsStacked && rPropSet.GetBoolProperty( EXC_CHPROP_STACKCHARACTERS );
+ return bStacked ? EXC_ROT_STACKED :
+ XclTools::GetXclRotation( Degree100(static_cast< sal_Int32 >( fAngle * 100.0 + 0.5 )) );
+}
+
+// write properties -----------------------------------------------------------
+
+void XclChPropSetHelper::WriteLineProperties(
+ ScfPropertySet& rPropSet, XclChObjectTable& rDashTable,
+ const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode )
+{
+ // line width
+ sal_Int32 nApiWidth = 0; // 0 is the width of a hair line
+ switch( rLineFmt.mnWeight )
+ {
+ case EXC_CHLINEFORMAT_SINGLE: nApiWidth = 35; break;
+ case EXC_CHLINEFORMAT_DOUBLE: nApiWidth = 70; break;
+ case EXC_CHLINEFORMAT_TRIPLE: nApiWidth = 105; break;
+ }
+
+ // line style
+ drawing::LineStyle eApiStyle = drawing::LineStyle_NONE;
+ sal_Int16 nApiTrans = 0;
+ sal_Int32 nDotLen = ::std::min< sal_Int32 >( rLineFmt.mnWeight + 105, 210 );
+ drawing::LineDash aApiDash( drawing::DashStyle_RECT, 0, nDotLen, 0, 4 * nDotLen, nDotLen );
+
+ switch( rLineFmt.mnPattern )
+ {
+ case EXC_CHLINEFORMAT_SOLID:
+ eApiStyle = drawing::LineStyle_SOLID;
+ break;
+ case EXC_CHLINEFORMAT_DARKTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 25;
+ break;
+ case EXC_CHLINEFORMAT_MEDTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 50;
+ break;
+ case EXC_CHLINEFORMAT_LIGHTTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 75;
+ break;
+ case EXC_CHLINEFORMAT_DASH:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = 1;
+ break;
+ case EXC_CHLINEFORMAT_DOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dots = 1;
+ break;
+ case EXC_CHLINEFORMAT_DASHDOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = aApiDash.Dots = 1;
+ break;
+ case EXC_CHLINEFORMAT_DASHDOTDOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = 1; aApiDash.Dots = 2;
+ break;
+ }
+
+ // line color
+ sal_Int32 nApiColor = sal_Int32( rLineFmt.maColor );
+
+ // try to insert the dash style and receive its name
+ uno::Any aDashNameAny;
+ if( eApiStyle == drawing::LineStyle_DASH )
+ {
+ OUString aDashName = rDashTable.InsertObject( uno::Any( aApiDash ) );
+ if( !aDashName.isEmpty() )
+ aDashNameAny <<= aDashName;
+ }
+
+ // write the properties
+ ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode );
+ rLineHlp.InitializeWrite();
+ rLineHlp << eApiStyle << nApiWidth << nApiColor << nApiTrans << aDashNameAny;
+ rLineHlp.WriteToPropertySet( rPropSet );
+}
+
+void XclChPropSetHelper::WriteAreaProperties( ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode )
+{
+ drawing::FillStyle eFillStyle = drawing::FillStyle_NONE;
+ Color aColor;
+
+ // fill color
+ if( rAreaFmt.mnPattern != EXC_PATT_NONE )
+ {
+ eFillStyle = drawing::FillStyle_SOLID;
+ aColor = XclTools::GetPatternColor( rAreaFmt.maPattColor, rAreaFmt.maBackColor, rAreaFmt.mnPattern );
+ }
+
+ // write the properties
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.InitializeWrite();
+ rAreaHlp << eFillStyle << aColor << sal_Int16(0)/*nTransparency*/;
+ rAreaHlp.WriteToPropertySet( rPropSet );
+}
+
+void XclChPropSetHelper::WriteEscherProperties( ScfPropertySet& rPropSet,
+ XclChObjectTable& rGradientTable, XclChObjectTable& rBitmapTable,
+ const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType, XclChPropertyMode ePropMode )
+{
+ if( !rEscherFmt.mxItemSet )
+ return;
+
+ const XFillStyleItem* pStyleItem = rEscherFmt.mxItemSet->GetItem<XFillStyleItem>( XATTR_FILLSTYLE, false );
+ if( !pStyleItem )
+ return;
+
+ switch( pStyleItem->GetValue() )
+ {
+ case drawing::FillStyle_SOLID:
+ // #i84812# Excel 2007 writes Escher properties for solid fill
+ if( const XFillColorItem* pColorItem = rEscherFmt.mxItemSet->GetItem<XFillColorItem>( XATTR_FILLCOLOR, false ) )
+ {
+ // get solid transparence too
+ const XFillTransparenceItem* pTranspItem = rEscherFmt.mxItemSet->GetItem<XFillTransparenceItem>( XATTR_FILLTRANSPARENCE, false );
+ sal_uInt16 nTransp = pTranspItem ? pTranspItem->GetValue() : 0;
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.InitializeWrite();
+ rAreaHlp << drawing::FillStyle_SOLID << pColorItem->GetColorValue() << static_cast< sal_Int16 >( nTransp );
+ rAreaHlp.WriteToPropertySet( rPropSet );
+ }
+ break;
+ case drawing::FillStyle_GRADIENT:
+ if( const XFillGradientItem* pGradItem = rEscherFmt.mxItemSet->GetItem<XFillGradientItem>( XATTR_FILLGRADIENT, false ) )
+ {
+ uno::Any aGradientAny;
+ if( pGradItem->QueryValue( aGradientAny, MID_FILLGRADIENT ) )
+ {
+ OUString aGradName = rGradientTable.InsertObject( aGradientAny );
+ if( !aGradName.isEmpty() )
+ {
+ ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode );
+ rGradHlp.InitializeWrite();
+ rGradHlp << drawing::FillStyle_GRADIENT << aGradName;
+ rGradHlp.WriteToPropertySet( rPropSet );
+ }
+ }
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ if( const XFillBitmapItem* pBmpItem = rEscherFmt.mxItemSet->GetItem<XFillBitmapItem>( XATTR_FILLBITMAP, false ) )
+ {
+ uno::Any aBitmapAny;
+ if (pBmpItem->QueryValue(aBitmapAny, MID_BITMAP))
+ {
+ OUString aBmpName = rBitmapTable.InsertObject( aBitmapAny );
+ if( !aBmpName.isEmpty() )
+ {
+ /* #i71810# Caller decides whether to use a CHPICFORMAT record for bitmap mode.
+ If not passed, detect fill mode from the DFF property 'fill-type'. */
+ bool bStretch = pPicFmt ? (pPicFmt->mnBmpMode == EXC_CHPICFORMAT_STRETCH) : (nDffFillType == mso_fillPicture);
+ drawing::BitmapMode eApiBmpMode = bStretch ? drawing::BitmapMode_STRETCH : drawing::BitmapMode_REPEAT;
+ maBitmapHlp.InitializeWrite();
+ maBitmapHlp << drawing::FillStyle_BITMAP << aBmpName << eApiBmpMode;
+ maBitmapHlp.WriteToPropertySet( rPropSet );
+ }
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::WriteEscherProperties - unknown fill mode" );
+ }
+}
+
+void XclChPropSetHelper::WriteMarkerProperties(
+ ScfPropertySet& rPropSet, const XclChMarkerFormat& rMarkerFmt )
+{
+ // symbol style
+ chart2::Symbol aApiSymbol;
+ aApiSymbol.Style = chart2::SymbolStyle_STANDARD;
+ switch( rMarkerFmt.mnMarkerType )
+ {
+ case EXC_CHMARKERFORMAT_NOSYMBOL: aApiSymbol.Style = chart2::SymbolStyle_NONE; break;
+ case EXC_CHMARKERFORMAT_SQUARE: aApiSymbol.StandardSymbol = 0; break; // square
+ case EXC_CHMARKERFORMAT_DIAMOND: aApiSymbol.StandardSymbol = 1; break; // diamond
+ case EXC_CHMARKERFORMAT_TRIANGLE: aApiSymbol.StandardSymbol = 3; break; // arrow up
+ case EXC_CHMARKERFORMAT_CROSS: aApiSymbol.StandardSymbol = 10; break; // X, legacy bow tie
+ case EXC_CHMARKERFORMAT_STAR: aApiSymbol.StandardSymbol = 12; break; // asterisk, legacy sand glass
+ case EXC_CHMARKERFORMAT_DOWJ: aApiSymbol.StandardSymbol = 4; break; // arrow right, same as export
+ case EXC_CHMARKERFORMAT_STDDEV: aApiSymbol.StandardSymbol = 13; break; // horizontal bar, legacy arrow down
+ case EXC_CHMARKERFORMAT_CIRCLE: aApiSymbol.StandardSymbol = 8; break; // circle, legacy arrow right
+ case EXC_CHMARKERFORMAT_PLUS: aApiSymbol.StandardSymbol = 11; break; // plus, legacy arrow left
+ default: break;
+ }
+
+ // symbol size
+ sal_Int32 nApiSize = XclTools::GetHmmFromTwips( rMarkerFmt.mnMarkerSize );
+ aApiSymbol.Size = awt::Size( nApiSize, nApiSize );
+
+ // symbol colors
+ aApiSymbol.FillColor = sal_Int32( rMarkerFmt.maFillColor );
+ aApiSymbol.BorderColor = ::get_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOLINE ) ?
+ aApiSymbol.FillColor : sal_Int32( rMarkerFmt.maLineColor );
+
+ // set the property
+ rPropSet.SetProperty( EXC_CHPROP_SYMBOL, aApiSymbol );
+}
+
+void XclChPropSetHelper::WriteRotationProperties(
+ ScfPropertySet& rPropSet, sal_uInt16 nRotation, bool bSupportsStacked )
+{
+ if( nRotation != EXC_CHART_AUTOROTATION )
+ {
+ // chart2 handles rotation as double in the range [0,360)
+ double fAngle = XclTools::GetScRotation( nRotation, 0_deg100 ).get() / 100.0;
+ rPropSet.SetProperty( EXC_CHPROP_TEXTROTATION, fAngle );
+ if( bSupportsStacked )
+ rPropSet.SetProperty( EXC_CHPROP_STACKCHARACTERS, nRotation == EXC_ROT_STACKED );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+ScfPropSetHelper& XclChPropSetHelper::GetLineHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maLineHlpCommon;
+ case EXC_CHPROPMODE_LINEARSERIES: return maLineHlpLinear;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maLineHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetLineHelper - unknown property mode" );
+ }
+ return maLineHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetAreaHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maAreaHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maAreaHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetAreaHelper - unknown property mode" );
+ }
+ return maAreaHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetGradientHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maGradHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maGradHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetGradientHelper - unknown property mode" );
+ }
+ return maGradHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetHatchHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maHatchHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maHatchHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetHatchHelper - unknown property mode" );
+ }
+ return maHatchHlpCommon;
+}
+
+namespace {
+
+/* The following local functions implement getting the XShape interface of all
+ supported title objects (chart and axes). This needs some effort due to the
+ design of the old Chart1 API used to access these objects. */
+
+/** Returns the drawing shape of the main title, if existing. */
+uno::Reference<drawing::XShape> lclGetMainTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ ScfPropertySet aPropSet(rxChart1Doc);
+ if (rxChart1Doc.is() && aPropSet.GetBoolProperty("HasMainTitle"))
+ return rxChart1Doc->getTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetXAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XAxisXSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasXAxisTitle"))
+ return xAxisSupp->getXAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetYAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc )
+{
+ uno::Reference<chart::XAxisYSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasYAxisTitle"))
+ return xAxisSupp->getYAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetZAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc )
+{
+ uno::Reference<chart::XAxisZSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasZAxisTitle"))
+ return xAxisSupp->getZAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetSecXAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XSecondAxisTitleSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasSecondaryXAxisTitle"))
+ return xAxisSupp->getSecondXAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetSecYAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XSecondAxisTitleSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasSecondaryYAxisTitle"))
+ return xAxisSupp->getSecondYAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+} // namespace
+
+XclChRootData::XclChRootData()
+ : mxTypeInfoProv(std::make_shared<XclChTypeInfoProvider>())
+ , mxFmtInfoProv(std::make_shared<XclChFormatInfoProvider>())
+ , mnBorderGapX(0)
+ , mnBorderGapY(0)
+ , mfUnitSizeX(0.0)
+ , mfUnitSizeY(0.0)
+{
+ // remember some title shape getter functions
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_TITLE ) ] = lclGetMainTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_X ) ] = lclGetXAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Y ) ] = lclGetYAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Z ) ] = lclGetZAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_X ) ] = lclGetSecXAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_Y ) ] = lclGetSecYAxisTitleShape;
+}
+
+XclChRootData::~XclChRootData()
+{
+}
+
+void XclChRootData::InitConversion(const XclRoot& rRoot, const uno::Reference<chart2::XChartDocument> & rxChartDoc, const tools::Rectangle& rChartRect)
+{
+ // remember chart document reference and chart shape position/size
+ OSL_ENSURE( rxChartDoc.is(), "XclChRootData::InitConversion - missing chart document" );
+ mxChartDoc = rxChartDoc;
+ maChartRect = rChartRect;
+
+ // Excel excludes a border of 5 pixels in each direction from chart area
+ mnBorderGapX = rRoot.GetHmmFromPixelX( 5.0 );
+ mnBorderGapY = rRoot.GetHmmFromPixelY( 5.0 );
+
+ // size of a chart unit in 1/100 mm
+ mfUnitSizeX = std::max<double>( maChartRect.GetWidth() - 2 * mnBorderGapX, mnBorderGapX ) / EXC_CHART_TOTALUNITS;
+ mfUnitSizeY = std::max<double>( maChartRect.GetHeight() - 2 * mnBorderGapY, mnBorderGapY ) / EXC_CHART_TOTALUNITS;
+
+ // create object tables
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxChartDoc, uno::UNO_QUERY);
+ mxLineDashTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_DASHTABLE, "Excel line dash ");
+ mxGradientTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_GRADIENTTABLE, "Excel gradient ");
+ mxHatchTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_HATCHTABLE, "Excel hatch ");
+ mxBitmapTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_BITMAPTABLE, "Excel bitmap ");
+}
+
+void XclChRootData::FinishConversion()
+{
+ // forget formatting object tables
+ mxBitmapTable.reset();
+ mxHatchTable.reset();
+ mxGradientTable.reset();
+ mxLineDashTable.reset();
+ // forget chart document reference
+ mxChartDoc.clear();
+}
+
+uno::Reference<drawing::XShape> XclChRootData::GetTitleShape(const XclChTextKey& rTitleKey) const
+{
+ XclChGetShapeFuncMap::const_iterator aIt = maGetShapeFuncs.find( rTitleKey );
+ OSL_ENSURE( aIt != maGetShapeFuncs.end(), "XclChRootData::GetTitleShape - invalid title key" );
+ uno::Reference<chart::XChartDocument> xChart1Doc( mxChartDoc, uno::UNO_QUERY );
+ uno::Reference<drawing::XShape> xTitleShape;
+ if (xChart1Doc.is() && (aIt != maGetShapeFuncs.end()))
+ xTitleShape = (aIt->second)(xChart1Doc);
+ return xTitleShape;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlescher.cxx b/sc/source/filter/excel/xlescher.cxx
new file mode 100644
index 0000000000..8ae663cdda
--- /dev/null
+++ b/sc/source/filter/excel/xlescher.cxx
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <xlescher.hxx>
+
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <tools/UnitConversion.hxx>
+#include <document.hxx>
+#include <xistream.hxx>
+#include <xlroot.hxx>
+#include <xltools.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XControlShape;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::script::ScriptEventDescriptor;
+
+namespace {
+
+/** Returns the scaling factor to calculate coordinates from twips. */
+double lclGetTwipsScale( MapUnit eMapUnit )
+{
+ double fScale = 1.0;
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ fScale = o3tl::convert(1.0, o3tl::Length::twip, eTo);
+ else
+ OSL_FAIL("lclGetTwipsScale - map unit not implemented");
+ return fScale;
+}
+
+/** Calculates a drawing layer X position (in twips) from an object column position. */
+tools::Long lclGetXFromCol( const ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclCol, sal_uInt16 nOffset, double fScale )
+{
+ SCCOL nScCol = static_cast< SCCOL >( nXclCol );
+ return static_cast< tools::Long >( fScale * (rDoc.GetColOffset( nScCol, nScTab ) +
+ ::std::min( nOffset / 1024.0, 1.0 ) * rDoc.GetColWidth( nScCol, nScTab )) + 0.5 );
+}
+
+/** Calculates a drawing layer Y position (in twips) from an object row position. */
+tools::Long lclGetYFromRow( const ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclRow, sal_uInt16 nOffset, double fScale )
+{
+ SCROW nScRow = static_cast< SCROW >( nXclRow );
+ return static_cast< tools::Long >( fScale * (rDoc.GetRowOffset( nScRow, nScTab ) +
+ ::std::min( nOffset / 256.0, 1.0 ) * rDoc.GetRowHeight( nScRow, nScTab )) + 0.5 );
+}
+
+/** Calculates an object column position from a drawing layer X position (in twips). */
+void lclGetColFromX(
+ const ScDocument& rDoc, SCTAB nScTab, sal_uInt16& rnXclCol,
+ sal_uInt16& rnOffset, sal_uInt16 nXclStartCol, sal_uInt16 nXclMaxCol,
+ tools::Long& rnStartW, tools::Long nX, double fScale )
+{
+ // rnStartW in conjunction with nXclStartCol is used as buffer for previously calculated width
+ tools::Long nTwipsX = static_cast< tools::Long >( nX / fScale + 0.5 );
+ tools::Long nColW = 0;
+ for( rnXclCol = nXclStartCol; rnXclCol <= nXclMaxCol; ++rnXclCol )
+ {
+ nColW = rDoc.GetColWidth( static_cast< SCCOL >( rnXclCol ), nScTab );
+ if( rnStartW + nColW > nTwipsX )
+ break;
+ rnStartW += nColW;
+ }
+ rnOffset = nColW ? static_cast< sal_uInt16 >( (nTwipsX - rnStartW) * 1024.0 / nColW + 0.5 ) : 0;
+}
+
+/** Calculates an object row position from a drawing layer Y position (in twips). */
+void lclGetRowFromY(
+ const ScDocument& rDoc, SCTAB nScTab, sal_uInt32& rnXclRow,
+ sal_uInt32& rnOffset, sal_uInt32 nXclStartRow, sal_uInt32 nXclMaxRow,
+ tools::Long& rnStartH, tools::Long nY, double fScale )
+{
+ // rnStartH in conjunction with nXclStartRow is used as buffer for previously calculated height
+ tools::Long nTwipsY = static_cast< tools::Long >( nY / fScale + 0.5 );
+ tools::Long nRowH = 0;
+ bool bFound = false;
+ for( sal_uInt32 nRow = nXclStartRow; nRow <= nXclMaxRow; ++nRow )
+ {
+ nRowH = rDoc.GetRowHeight( nRow, nScTab );
+ if( rnStartH + nRowH > nTwipsY )
+ {
+ rnXclRow = nRow;
+ bFound = true;
+ break;
+ }
+ rnStartH += nRowH;
+ }
+ if( !bFound )
+ rnXclRow = nXclMaxRow;
+ rnOffset = static_cast< sal_uInt32 >( nRowH ? std::max((nTwipsY - rnStartH) * 256.0 / nRowH + 0.5, 0.0) : 0 );
+}
+
+/** Mirrors a rectangle (from LTR to RTL layout or vice versa). */
+void lclMirrorRectangle( tools::Rectangle& rRect )
+{
+ tools::Long nLeft = rRect.Left();
+ rRect.SetLeft( -rRect.Right() );
+ rRect.SetRight( -nLeft );
+}
+
+sal_uInt16 lclGetEmbeddedScale( tools::Long nPageSize, sal_Int32 nPageScale, tools::Long nPos, double fPosScale )
+{
+ return static_cast< sal_uInt16 >( nPos * fPosScale / nPageSize * nPageScale + 0.5 );
+}
+
+} // namespace
+
+XclObjAnchor::XclObjAnchor() :
+ mnLX( 0 ),
+ mnTY( 0 ),
+ mnRX( 0 ),
+ mnBY( 0 )
+{
+}
+
+tools::Rectangle XclObjAnchor::GetRect( const XclRoot& rRoot, SCTAB nScTab, MapUnit eMapUnit ) const
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ double fScale = lclGetTwipsScale( eMapUnit );
+ tools::Rectangle aRect(
+ lclGetXFromCol(rDoc, nScTab, std::min<SCCOL>(maFirst.mnCol, rDoc.MaxCol()), mnLX, fScale),
+ lclGetYFromRow(rDoc, nScTab, std::min<SCROW>(maFirst.mnRow, rDoc.MaxRow()), mnTY, fScale),
+ lclGetXFromCol(rDoc, nScTab, std::min<SCCOL>(maLast.mnCol, rDoc.MaxCol()), mnRX + 1, fScale),
+ lclGetYFromRow(rDoc, nScTab, std::min<SCROW>(maLast.mnRow, rDoc.MaxRow()), mnBY, fScale));
+
+ // adjust coordinates in mirrored sheets
+ if( rDoc.IsLayoutRTL( nScTab ) )
+ lclMirrorRectangle( aRect );
+ return aRect;
+}
+
+void XclObjAnchor::SetRect( const XclRoot& rRoot, SCTAB nScTab, const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ sal_uInt16 nXclMaxCol = rRoot.GetXclMaxPos().Col();
+ sal_uInt16 nXclMaxRow = static_cast<sal_uInt16>( rRoot.GetXclMaxPos().Row());
+
+ // adjust coordinates in mirrored sheets
+ tools::Rectangle aRect( rRect );
+ if( rDoc.IsLayoutRTL( nScTab ) )
+ lclMirrorRectangle( aRect );
+
+ double fScale = lclGetTwipsScale( eMapUnit );
+ tools::Long nDummy = 0;
+ lclGetColFromX( rDoc, nScTab, maFirst.mnCol, mnLX, 0, nXclMaxCol, nDummy, aRect.Left(), fScale );
+ lclGetColFromX( rDoc, nScTab, maLast.mnCol, mnRX, maFirst.mnCol, nXclMaxCol, nDummy, aRect.Right(), fScale );
+ nDummy = 0;
+ lclGetRowFromY( rDoc, nScTab, maFirst.mnRow, mnTY, 0, nXclMaxRow, nDummy, aRect.Top(), fScale );
+ lclGetRowFromY( rDoc, nScTab, maLast.mnRow, mnBY, maFirst.mnRow, nXclMaxRow, nDummy, aRect.Bottom(), fScale );
+}
+
+void XclObjAnchor::SetRect( const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY,
+ const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ double fScale = 1.0;
+ if (const auto eFrom = MapToO3tlLength(eMapUnit); eFrom != o3tl::Length::invalid)
+ fScale = o3tl::convert(1.0, eFrom, o3tl::Length::mm100);
+ else
+ OSL_FAIL("XclObjAnchor::SetRect - map unit not implemented");
+
+ /* In objects with DFF client anchor, the position of the shape is stored
+ in the cell address components of the client anchor. In old BIFF3-BIFF5
+ objects, the position is stored in the offset components of the anchor. */
+ maFirst.mnCol = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Left(), fScale );
+ maFirst.mnRow = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Top(), fScale );
+ maLast.mnCol = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Right(), fScale );
+ maLast.mnRow = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Bottom(), fScale );
+
+ // for safety, clear the other members
+ mnLX = mnTY = mnRX = mnBY = 0;
+}
+
+XclObjLineData::XclObjLineData() :
+ mnColorIdx( EXC_OBJ_LINE_AUTOCOLOR ),
+ mnStyle( EXC_OBJ_LINE_SOLID ),
+ mnWidth( EXC_OBJ_LINE_HAIR ),
+ mnAuto( EXC_OBJ_LINE_AUTO )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjLineData& rLineData )
+{
+ rLineData.mnColorIdx = rStrm.ReaduInt8();
+ rLineData.mnStyle = rStrm.ReaduInt8();
+ rLineData.mnWidth = rStrm.ReaduInt8();
+ rLineData.mnAuto = rStrm.ReaduInt8();
+ return rStrm;
+}
+
+XclObjFillData::XclObjFillData() :
+ mnBackColorIdx( EXC_OBJ_LINE_AUTOCOLOR ),
+ mnPattColorIdx( EXC_OBJ_FILL_AUTOCOLOR ),
+ mnPattern( EXC_PATT_SOLID ),
+ mnAuto( EXC_OBJ_FILL_AUTO )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjFillData& rFillData )
+{
+ rFillData.mnBackColorIdx = rStrm.ReaduInt8();
+ rFillData.mnPattColorIdx = rStrm.ReaduInt8();
+ rFillData.mnPattern = rStrm.ReaduInt8();
+ rFillData.mnAuto = rStrm.ReaduInt8();
+ return rStrm;
+}
+
+XclObjTextData::XclObjTextData() :
+ mnTextLen( 0 ),
+ mnFormatSize( 0 ),
+ mnLinkSize( 0 ),
+ mnDefFontIdx( EXC_FONT_APP ),
+ mnFlags( 0 ),
+ mnOrient( EXC_OBJ_ORIENT_NONE ),
+ mnButtonFlags( 0 ),
+ mnShortcut( 0 ),
+ mnShortcutEA( 0 )
+{
+}
+
+void XclObjTextData::ReadObj3( XclImpStream& rStrm )
+{
+ mnTextLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFormatSize = rStrm.ReaduInt16();
+ mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+}
+
+void XclObjTextData::ReadObj5( XclImpStream& rStrm )
+{
+ mnTextLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFormatSize = rStrm.ReaduInt16();
+ mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnButtonFlags = rStrm.ReaduInt16();
+ mnShortcut = rStrm.ReaduInt16();
+ mnShortcutEA = rStrm.ReaduInt16();
+}
+
+void XclObjTextData::ReadTxo8( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ mnButtonFlags = rStrm.ReaduInt16();
+ mnShortcut = rStrm.ReaduInt16();
+ mnShortcutEA = rStrm.ReaduInt16();
+ mnTextLen = rStrm.ReaduInt16();
+ mnFormatSize = rStrm.ReaduInt16();
+}
+
+Reference< XControlModel > XclControlHelper::GetControlModel( Reference< XShape > const & xShape )
+{
+ Reference< XControlModel > xCtrlModel;
+ Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY );
+ if( xCtrlShape.is() )
+ xCtrlModel = xCtrlShape->getControl();
+ return xCtrlModel;
+}
+
+namespace {
+
+const struct
+{
+ const char* mpcListenerType;
+ const char* mpcEventMethod;
+}
+spTbxListenerData[] =
+{
+ // Attention: MUST be in order of the XclTbxEventType enum!
+ /*EXC_TBX_EVENT_ACTION*/ { "XActionListener", "actionPerformed" },
+ /*EXC_TBX_EVENT_MOUSE*/ { "XMouseListener", "mouseReleased" },
+ /*EXC_TBX_EVENT_TEXT*/ { "XTextListener", "textChanged" },
+ /*EXC_TBX_EVENT_VALUE*/ { "XAdjustmentListener", "adjustmentValueChanged" },
+ /*EXC_TBX_EVENT_CHANGE*/ { "XChangeListener", "changed" }
+};
+
+} // namespace
+
+bool XclControlHelper::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType, const OUString& rXclMacroName, SfxObjectShell* pDocShell )
+{
+ if( !rXclMacroName.isEmpty() )
+ {
+ rDescriptor.ListenerType = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcListenerType );
+ rDescriptor.EventMethod = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcEventMethod );
+ rDescriptor.ScriptType = "Script";
+ rDescriptor.ScriptCode = XclTools::GetSbMacroUrl( rXclMacroName, pDocShell );
+ return true;
+ }
+ return false;
+}
+
+OUString XclControlHelper::ExtractFromMacroDescriptor(
+ const ScriptEventDescriptor& rDescriptor, XclTbxEventType eEventType )
+{
+ if( (!rDescriptor.ScriptCode.isEmpty()) &&
+ rDescriptor.ScriptType.equalsIgnoreAsciiCase("Script") &&
+ rDescriptor.ListenerType.equalsAscii( spTbxListenerData[ eEventType ].mpcListenerType ) &&
+ rDescriptor.EventMethod.equalsAscii( spTbxListenerData[ eEventType ].mpcEventMethod ) )
+ return XclTools::GetXclMacroName( rDescriptor.ScriptCode );
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx
new file mode 100644
index 0000000000..2612f0d3dc
--- /dev/null
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -0,0 +1,1022 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlformula.hxx>
+
+#include <refdata.hxx>
+#include <tokenarray.hxx>
+#include <xestream.hxx>
+#include <xistream.hxx>
+#include <xlroot.hxx>
+
+#include <comphelper/string.hxx>
+#include <svl/sharedstringpool.hxx>
+
+using namespace ::formula;
+
+// Function data ==============================================================
+
+OUString XclFunctionInfo::GetMacroFuncName() const
+{
+ if( IsMacroFunc() )
+ return OUString( mpcMacroName, strlen(mpcMacroName), RTL_TEXTENCODING_UTF8 );
+ return OUString();
+}
+
+OUString XclFunctionInfo::GetAddInEquivalentFuncName() const
+{
+ if( IsAddInEquivalent() )
+ return OUString( mpcMacroName, strlen(mpcMacroName), RTL_TEXTENCODING_UTF8 );
+ return OUString();
+}
+
+// abbreviations for function return token class
+const sal_uInt8 R = EXC_TOKCLASS_REF;
+const sal_uInt8 V = EXC_TOKCLASS_VAL;
+const sal_uInt8 A = EXC_TOKCLASS_ARR;
+
+// abbreviations for parameter infos
+#define RO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, false }
+#define RA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, false }
+#define RR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, false }
+#define RX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, false }
+#define VO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, true }
+#define VV { EXC_PARAM_REGULAR, EXC_PARAMCONV_VAL, true }
+#define VA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, true }
+#define VR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, true }
+#define VX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, true }
+#define RO_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_ORG, false }
+#define VR_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_RPT, true }
+#define C { EXC_PARAM_CALCONLY, EXC_PARAMCONV_ORG, false }
+
+const sal_uInt16 NOID = SAL_MAX_UINT16; /// No BIFF/OOBIN function identifier available.
+const sal_uInt8 MX = 30; /// Maximum parameter count.
+
+#define EXC_FUNCNAME( ascii ) "_xlfn." ascii
+#define EXC_FUNCNAME_ODF( ascii ) "_xlfnodf." ascii
+#define EXC_FUNCNAME_ADDIN( ascii ) "com.sun.star.sheet.addin." ascii
+
+/** Functions new in BIFF2. */
+const XclFunctionInfo saFuncTable_2[] =
+{
+ { ocCount, 0, 0, MX, V, { RX }, 0, nullptr },
+ { ocIf, 1, 2, 3, R, { VO, RO }, 0, nullptr },
+ { ocIsNA, 2, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsError, 3, 1, 1, V, { VR }, 0, nullptr },
+ { ocSum, 4, 0, MX, V, { RX }, 0, nullptr },
+ { ocAverage, 5, 1, MX, V, { RX }, 0, nullptr },
+ { ocMin, 6, 1, MX, V, { RX }, 0, nullptr },
+ { ocMax, 7, 1, MX, V, { RX }, 0, nullptr },
+ { ocRow, 8, 0, 1, V, { RO }, 0, nullptr },
+ { ocColumn, 9, 0, 1, V, { RO }, 0, nullptr },
+ { ocNotAvail, 10, 0, 0, V, {}, 0, nullptr },
+ { ocNPV, 11, 2, MX, V, { VR, RX }, 0, nullptr },
+ { ocStDev, 12, 1, MX, V, { RX }, 0, nullptr },
+ { ocCurrency, 13, 1, 2, V, { VR }, 0, nullptr },
+ { ocFixed, 14, 1, 2, V, { VR, VR, C }, 0, nullptr },
+ { ocSin, 15, 1, 1, V, { VR }, 0, nullptr },
+ { ocCosecant, 15, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocCos, 16, 1, 1, V, { VR }, 0, nullptr },
+ { ocSecant, 16, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocTan, 17, 1, 1, V, { VR }, 0, nullptr },
+ { ocCot, 17, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocArcTan, 18, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCot, 18, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocPi, 19, 0, 0, V, {}, 0, nullptr },
+ { ocSqrt, 20, 1, 1, V, { VR }, 0, nullptr },
+ { ocExp, 21, 1, 1, V, { VR }, 0, nullptr },
+ { ocLn, 22, 1, 1, V, { VR }, 0, nullptr },
+ { ocLog10, 23, 1, 1, V, { VR }, 0, nullptr },
+ { ocAbs, 24, 1, 1, V, { VR }, 0, nullptr },
+ { ocInt, 25, 1, 1, V, { VR }, 0, nullptr },
+ { ocPlusMinus, 26, 1, 1, V, { VR }, 0, nullptr },
+ { ocRound, 27, 2, 2, V, { VR }, 0, nullptr },
+ { ocLookup, 28, 2, 3, V, { VR, RA }, 0, nullptr },
+ { ocIndex, 29, 2, 4, R, { RA, VV }, 0, nullptr },
+ { ocRept, 30, 2, 2, V, { VR }, 0, nullptr },
+ { ocMid, 31, 3, 3, V, { VR }, 0, nullptr },
+ { ocLen, 32, 1, 1, V, { VR }, 0, nullptr },
+ { ocValue, 33, 1, 1, V, { VR }, 0, nullptr },
+ { ocTrue, 34, 0, 0, V, {}, 0, nullptr },
+ { ocFalse, 35, 0, 0, V, {}, 0, nullptr },
+ { ocAnd, 36, 1, MX, V, { RX }, 0, nullptr },
+ { ocOr, 37, 1, MX, V, { RX }, 0, nullptr },
+ { ocNot, 38, 1, 1, V, { VR }, 0, nullptr },
+ { ocMod, 39, 2, 2, V, { VR }, 0, nullptr },
+ { ocDBCount, 40, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBSum, 41, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBAverage, 42, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBMin, 43, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBMax, 44, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBStdDev, 45, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocVar, 46, 1, MX, V, { RX }, 0, nullptr },
+ { ocDBVar, 47, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocText, 48, 2, 2, V, { VR }, 0, nullptr },
+ { ocLinest, 49, 1, 2, A, { RA, RA, C, C }, 0, nullptr },
+ { ocTrend, 50, 1, 3, A, { RA, RA, RA, C }, 0, nullptr },
+ { ocLogest, 51, 1, 2, A, { RA, RA, C, C }, 0, nullptr },
+ { ocGrowth, 52, 1, 3, A, { RA, RA, RA, C }, 0, nullptr },
+ { ocPV, 56, 3, 5, V, { VR }, 0, nullptr },
+ { ocFV, 57, 3, 5, V, { VR }, 0, nullptr },
+ { ocNper, 58, 3, 5, V, { VR }, 0, nullptr },
+ { ocPMT, 59, 3, 5, V, { VR }, 0, nullptr },
+ { ocRate, 60, 3, 6, V, { VR }, 0, nullptr },
+ { ocMIRR, 61, 3, 3, V, { RA, VR }, 0, nullptr },
+ { ocIRR, 62, 1, 2, V, { RA, VR }, 0, nullptr },
+ { ocRandom, 63, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocMatch, 64, 2, 3, V, { VR, RX, RR }, 0, nullptr },
+ { ocGetDate, 65, 3, 3, V, { VR }, 0, nullptr },
+ { ocGetTime, 66, 3, 3, V, { VR }, 0, nullptr },
+ { ocGetDay, 67, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetMonth, 68, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetYear, 69, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetDayOfWeek, 70, 1, 1, V, { VR, C }, 0, nullptr },
+ { ocGetHour, 71, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetMin, 72, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetSec, 73, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetActTime, 74, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocAreas, 75, 1, 1, V, { RO }, 0, nullptr },
+ { ocRows, 76, 1, 1, V, { RO }, 0, nullptr },
+ { ocColumns, 77, 1, 1, V, { RO }, 0, nullptr },
+ { ocOffset, 78, 3, 5, R, { RO, VR }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocSearch, 82, 2, 3, V, { VR }, 0, nullptr },
+ { ocMatTrans, 83, 1, 1, A, { VO }, 0, nullptr },
+ { ocType, 86, 1, 1, V, { VX }, 0, nullptr },
+ { ocArcTan2, 97, 2, 2, V, { VR }, 0, nullptr },
+ { ocArcSin, 98, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCos, 99, 1, 1, V, { VR }, 0, nullptr },
+ { ocChoose, 100, 2, MX, R, { VO, RO }, 0, nullptr },
+ { ocHLookup, 101, 3, 3, V, { VV, RO, RO, C }, 0, nullptr },
+ { ocVLookup, 102, 3, 3, V, { VV, RO, RO, C }, 0, nullptr },
+ { ocIsRef, 105, 1, 1, V, { RX }, 0, nullptr },
+ { ocLog, 109, 1, 2, V, { VR }, 0, nullptr },
+ { ocChar, 111, 1, 1, V, { VR }, 0, nullptr },
+ { ocLower, 112, 1, 1, V, { VR }, 0, nullptr },
+ { ocUpper, 113, 1, 1, V, { VR }, 0, nullptr },
+ { ocProper, 114, 1, 1, V, { VR }, 0, nullptr },
+ { ocLeft, 115, 1, 2, V, { VR }, 0, nullptr },
+ { ocRight, 116, 1, 2, V, { VR }, 0, nullptr },
+ { ocExact, 117, 2, 2, V, { VR }, 0, nullptr },
+ { ocTrim, 118, 1, 1, V, { VR }, 0, nullptr },
+ { ocReplace, 119, 4, 4, V, { VR }, 0, nullptr },
+ { ocSubstitute, 120, 3, 4, V, { VR }, 0, nullptr },
+ { ocCode, 121, 1, 1, V, { VR }, 0, nullptr },
+ { ocFind, 124, 2, 3, V, { VR }, 0, nullptr },
+ { ocCell, 125, 1, 2, V, { VV, RO }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocIsErr, 126, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsString, 127, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsValue, 128, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsEmpty, 129, 1, 1, V, { VR }, 0, nullptr },
+ { ocT, 130, 1, 1, V, { RO }, 0, nullptr },
+ { ocN, 131, 1, 1, V, { RO }, 0, nullptr },
+ { ocGetDateValue, 140, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetTimeValue, 141, 1, 1, V, { VR }, 0, nullptr },
+ { ocSLN, 142, 3, 3, V, { VR }, 0, nullptr },
+ { ocSYD, 143, 4, 4, V, { VR }, 0, nullptr },
+ { ocDDB, 144, 4, 5, V, { VR }, 0, nullptr },
+ { ocIndirect, 148, 1, 2, R, { VR }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocClean, 162, 1, 1, V, { VR }, 0, nullptr },
+ { ocMatDet, 163, 1, 1, V, { VA }, 0, nullptr },
+ { ocMatInv, 164, 1, 1, A, { VA }, 0, nullptr },
+ { ocMatMult, 165, 2, 2, A, { VA }, 0, nullptr },
+ { ocIpmt, 167, 4, 6, V, { VR }, 0, nullptr },
+ { ocPpmt, 168, 4, 6, V, { VR }, 0, nullptr },
+ { ocCount2, 169, 0, MX, V, { RX }, 0, nullptr },
+ { ocProduct, 183, 0, MX, V, { RX }, 0, nullptr },
+ { ocFact, 184, 1, 1, V, { VR }, 0, nullptr },
+ { ocDBProduct, 189, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocIsNonString, 190, 1, 1, V, { VR }, 0, nullptr },
+ { ocStDevP, 193, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarP, 194, 1, MX, V, { RX }, 0, nullptr },
+ { ocDBStdDevP, 195, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBVarP, 196, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocTrunc, 197, 1, 1, V, { VR, C }, 0, nullptr },
+ { ocIsLogical, 198, 1, 1, V, { VR }, 0, nullptr },
+ { ocDBCount2, 199, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocCurrency, 204, 1, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr },
+ { ocFindB, 205, 2, 3, V, { VR }, 0, nullptr },
+ { ocSearchB, 206, 2, 3, V, { VR }, 0, nullptr },
+ { ocReplaceB, 207, 4, 4, V, { VR }, 0, nullptr },
+ { ocLeftB, 208, 1, 2, V, { VR }, 0, nullptr },
+ { ocRightB, 209, 1, 2, V, { VR }, 0, nullptr },
+ { ocMidB, 210, 3, 3, V, { VR }, 0, nullptr },
+ { ocLenB, 211, 1, 1, V, { VR }, 0, nullptr },
+ { ocRoundUp, 212, 2, 2, V, { VR }, 0, nullptr },
+ { ocRoundDown, 213, 2, 2, V, { VR }, 0, nullptr },
+ { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_IMPORTONLY, nullptr }
+};
+
+/** Functions new in BIFF3. */
+const XclFunctionInfo saFuncTable_3[] =
+{
+ { ocLinest, 49, 1, 4, A, { RA, RA, VV }, 0, nullptr }, // BIFF2: 1-2, BIFF3: 1-4
+ { ocTrend, 50, 1, 4, A, { RA, RA, RA, VV }, 0, nullptr }, // BIFF2: 1-3, BIFF3: 1-4
+ { ocLogest, 51, 1, 4, A, { RA, RA, VV }, 0, nullptr }, // BIFF2: 1-2, BIFF3: 1-4
+ { ocGrowth, 52, 1, 4, A, { RA, RA, RA, VV }, 0, nullptr }, // BIFF2: 1-3, BIFF3: 1-4
+ { ocTrunc, 197, 1, 2, V, { VR }, 0, nullptr }, // BIFF2: 1, BIFF3: 1-2
+ { ocAddress, 219, 2, 5, V, { VR }, 0, nullptr },
+ { ocGetDiffDate360, 220, 2, 2, V, { VR, VR, C }, 0, nullptr },
+ { ocGetActDate, 221, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocVBD, 222, 5, 7, V, { VR }, 0, nullptr },
+ { ocMedian, 227, 1, MX, V, { RX }, 0, nullptr },
+ { ocSumProduct, 228, 1, MX, V, { VA }, 0, nullptr },
+ { ocSinHyp, 229, 1, 1, V, { VR }, 0, nullptr },
+ { ocCosecantHyp, 229, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocCosHyp, 230, 1, 1, V, { VR }, 0, nullptr },
+ { ocSecantHyp, 230, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocTanHyp, 231, 1, 1, V, { VR }, 0, nullptr },
+ { ocCotHyp, 231, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocArcSinHyp, 232, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCosHyp, 233, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcTanHyp, 234, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCotHyp, 234, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocDBGet, 235, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocInfo, 244, 1, 1, V, { VR }, EXC_FUNCFLAG_VOLATILE, nullptr }
+};
+
+/** Functions new in BIFF4. */
+const XclFunctionInfo saFuncTable_4[] =
+{
+ { ocFixed, 14, 1, 3, V, { VR }, 0, nullptr }, // BIFF2-3: 1-2, BIFF4: 1-3
+ { ocAsc, 214, 1, 1, V, { VR }, 0, nullptr },
+ { ocJis, 215, 1, 1, V, { VR }, 0, nullptr },
+ { ocRank, 216, 2, 3, V, { VR, RO, VR }, 0, nullptr },
+ { ocDB, 247, 4, 5, V, { VR }, 0, nullptr },
+ { ocFrequency, 252, 2, 2, A, { RA }, 0, nullptr },
+ { ocErrorType_ODF, 261, 1, 1, V, { VR }, 0, nullptr },
+ { ocAveDev, 269, 1, MX, V, { RX }, 0, nullptr },
+ { ocBetaDist, 270, 3, 5, V, { VR }, 0, nullptr },
+ { ocGammaLn, 271, 1, 1, V, { VR }, 0, nullptr },
+ { ocBetaInv, 272, 3, 5, V, { VR }, 0, nullptr },
+ { ocBinomDist, 273, 4, 4, V, { VR }, 0, nullptr },
+ { ocChiDist, 274, 2, 2, V, { VR }, 0, nullptr },
+ { ocChiInv, 275, 2, 2, V, { VR }, 0, nullptr },
+ { ocCombin, 276, 2, 2, V, { VR }, 0, nullptr },
+ { ocConfidence, 277, 3, 3, V, { VR }, 0, nullptr },
+ { ocCritBinom, 278, 3, 3, V, { VR }, 0, nullptr },
+ { ocEven, 279, 1, 1, V, { VR }, 0, nullptr },
+ { ocExpDist, 280, 3, 3, V, { VR }, 0, nullptr },
+ { ocFDist, 281, 3, 3, V, { VR }, 0, nullptr },
+ { ocFInv, 282, 3, 3, V, { VR }, 0, nullptr },
+ { ocFisher, 283, 1, 1, V, { VR }, 0, nullptr },
+ { ocFisherInv, 284, 1, 1, V, { VR }, 0, nullptr },
+ { ocFloor_MS, 285, 2, 2, V, { VR }, 0, nullptr },
+ { ocGammaDist, 286, 4, 4, V, { VR }, 0, nullptr },
+ { ocGammaInv, 287, 3, 3, V, { VR }, 0, nullptr },
+ { ocCeil_MS, 288, 2, 2, V, { VR }, 0, nullptr },
+ { ocHypGeomDist, 289, 4, 4, V, { VR }, 0, nullptr },
+ { ocLogNormDist, 290, 3, 3, V, { VR }, 0, nullptr },
+ { ocLogInv, 291, 3, 3, V, { VR }, 0, nullptr },
+ { ocNegBinomVert, 292, 3, 3, V, { VR }, 0, nullptr },
+ { ocNormDist, 293, 4, 4, V, { VR }, 0, nullptr },
+ { ocStdNormDist, 294, 1, 1, V, { VR }, 0, nullptr },
+ { ocNormInv, 295, 3, 3, V, { VR }, 0, nullptr },
+ { ocSNormInv, 296, 1, 1, V, { VR }, 0, nullptr },
+ { ocStandard, 297, 3, 3, V, { VR }, 0, nullptr },
+ { ocOdd, 298, 1, 1, V, { VR }, 0, nullptr },
+ { ocPermut, 299, 2, 2, V, { VR }, 0, nullptr },
+ { ocPoissonDist, 300, 3, 3, V, { VR }, 0, nullptr },
+ { ocTDist, 301, 3, 3, V, { VR }, 0, nullptr },
+ { ocWeibull, 302, 4, 4, V, { VR }, 0, nullptr },
+ { ocSumXMY2, 303, 2, 2, V, { VA }, 0, nullptr },
+ { ocSumX2MY2, 304, 2, 2, V, { VA }, 0, nullptr },
+ { ocSumX2DY2, 305, 2, 2, V, { VA }, 0, nullptr },
+ { ocChiTest, 306, 2, 2, V, { VA }, 0, nullptr },
+ { ocCorrel, 307, 2, 2, V, { VA }, 0, nullptr },
+ { ocCovar, 308, 2, 2, V, { VA }, 0, nullptr },
+ { ocForecast, 309, 3, 3, V, { VR, VA }, 0, nullptr },
+ { ocFTest, 310, 2, 2, V, { VA }, 0, nullptr },
+ { ocIntercept, 311, 2, 2, V, { VA }, 0, nullptr },
+ { ocPearson, 312, 2, 2, V, { VA }, 0, nullptr },
+ { ocRSQ, 313, 2, 2, V, { VA }, 0, nullptr },
+ { ocSTEYX, 314, 2, 2, V, { VA }, 0, nullptr },
+ { ocSlope, 315, 2, 2, V, { VA }, 0, nullptr },
+ { ocTTest, 316, 4, 4, V, { VA, VA, VR }, 0, nullptr },
+ { ocProb, 317, 3, 4, V, { VA, VA, VR }, 0, nullptr },
+ { ocDevSq, 318, 1, MX, V, { RX }, 0, nullptr },
+ { ocGeoMean, 319, 1, MX, V, { RX }, 0, nullptr },
+ { ocHarMean, 320, 1, MX, V, { RX }, 0, nullptr },
+ { ocSumSQ, 321, 0, MX, V, { RX }, 0, nullptr },
+ { ocKurt, 322, 1, MX, V, { RX }, 0, nullptr },
+ { ocSkew, 323, 1, MX, V, { RX }, 0, nullptr },
+ { ocZTest, 324, 2, 3, V, { RX, VR }, 0, nullptr },
+ { ocLarge, 325, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocSmall, 326, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocQuartile, 327, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocPercentile, 328, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocPercentrank, 329, 2, 3, V, { RX, VR, VR_E }, 0, nullptr },
+ { ocModalValue, 330, 1, MX, V, { VA }, 0, nullptr },
+ { ocTrimMean, 331, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocTInv, 332, 2, 2, V, { VR }, 0, nullptr },
+ // Functions equivalent to add-in functions, use same parameters as
+ // ocExternal but add programmatical function name (here without
+ // "com.sun.star.sheet.addin.") so it can be looked up and stored as
+ // add-in, as older Excel versions only know them as add-in.
+ { ocIsEven, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getIseven" ) },
+ { ocIsOdd, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getIsodd" ) },
+ { ocGCD, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getGcd" ) },
+ { ocLCM, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getLcm" ) },
+ { ocEffect, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getEffect" ) },
+ { ocCumPrinc, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getCumprinc" ) },
+ { ocCumIpmt, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getCumipmt" ) },
+ { ocNominal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getNominal" ) },
+ { ocNetWorkdays, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getNetworkdays" ) }
+};
+
+/** Functions new in BIFF5/BIFF7. Unsupported functions: DATESTRING, NUMBERSTRING. */
+const XclFunctionInfo saFuncTable_5[] =
+{
+ { ocGetDayOfWeek, 70, 1, 2, V, { VR }, 0, nullptr }, // BIFF2-4: 1, BIFF5: 1-2
+ { ocHLookup, 101, 3, 4, V, { VV, RO, RO, VV }, 0, nullptr }, // BIFF2-4: 3, BIFF5: 3-4
+ { ocVLookup, 102, 3, 4, V, { VV, RO, RO, VV }, 0, nullptr }, // BIFF2-4: 3, BIFF5: 3-4
+ { ocGetDiffDate360, 220, 2, 3, V, { VR }, 0, nullptr }, // BIFF3-4: 2, BIFF5: 2-3
+ { ocMacro, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocConcat, 336, 0, MX, V, { VR }, 0, nullptr },
+ { ocPower, 337, 2, 2, V, { VR }, 0, nullptr },
+ { ocRad, 342, 1, 1, V, { VR }, 0, nullptr },
+ { ocDeg, 343, 1, 1, V, { VR }, 0, nullptr },
+ { ocSubTotal, 344, 2, MX, V, { VR, RO }, 0, nullptr },
+ { ocSumIf, 345, 2, 3, V, { RO, VR, RO }, 0, nullptr },
+ { ocCountIf, 346, 2, 2, V, { RO, VR }, 0, nullptr },
+ { ocCountEmptyCells, 347, 1, 1, V, { RO }, 0, nullptr },
+ { ocISPMT, 350, 4, 4, V, { VR }, 0, nullptr },
+ { ocGetDateDif, 351, 3, 3, V, { VR }, 0, nullptr },
+ { ocNoName, 352, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // DATESTRING
+ { ocNoName, 353, 2, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // NUMBERSTRING
+ { ocRoman, 354, 1, 2, V, { VR }, 0, nullptr }
+};
+
+/** Functions new in BIFF8. Unsupported functions: PHONETIC. */
+const XclFunctionInfo saFuncTable_8[] =
+{
+ { ocGetPivotData, 358, 2, MX, V, { RR, RR, VR }, 0, nullptr },
+ { ocHyperLink, 359, 1, 2, V, { VV, VO }, 0, nullptr },
+ { ocNoName, 360, 1, 1, V, { RO }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // PHONETIC
+ { ocAverageA, 361, 1, MX, V, { RX }, 0, nullptr },
+ { ocMaxA, 362, 1, MX, V, { RX }, 0, nullptr },
+ { ocMinA, 363, 1, MX, V, { RX }, 0, nullptr },
+ { ocStDevPA, 364, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarPA, 365, 1, MX, V, { RX }, 0, nullptr },
+ { ocStDevA, 366, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarA, 367, 1, MX, V, { RX }, 0, nullptr },
+ { ocBahtText, 368, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) },
+ { ocBahtText, 255, 2, 2, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) },
+ { ocEuroConvert, 255, 4, 6, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, "EUROCONVERT" }
+};
+
+#define EXC_FUNCENTRY_V_VR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+/** Functions new in OOXML. */
+const XclFunctionInfo saFuncTable_Oox[] =
+{
+ { ocCountIfs, NOID, 2, MX, V, { RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "COUNTIFS" ) },
+ { ocCountIfs, 255, 3, MX, V, { RO_E, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "COUNTIFS" ) },
+ { ocSumIfs, NOID, 3, MX, V, { RO, RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "SUMIFS" ) },
+ { ocSumIfs, 255, 4, MX, V, { RO_E, RO, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "SUMIFS" ) },
+ { ocAverageIf, NOID, 2, 3, V, { RO, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "AVERAGEIF" ) },
+ { ocAverageIf, 255, 3, 4, V, { RO_E, RO, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "AVERAGEIF" ) },
+ { ocAverageIfs, NOID, 3, MX, V, { RO, RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+ { ocAverageIfs, 255, 4, MX, V, { RO_E, RO, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+ { ocIfError, NOID, 2, 2, V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "IFERROR" ) },
+ { ocIfError, 255, 3, 3, V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "IFERROR" ) },
+ { ocNetWorkdays_MS, NOID, 2, 4, V, { VR, VR, RO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "NETWORKDAYS.INTL" ) },
+ { ocNetWorkdays_MS, 255, 3, 5, V, { RO_E, VR, VR, RO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "NETWORKDAYS.INTL" ) },
+ { ocWorkday_MS, NOID, 2, 4, V, { VR, VR, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "WORKDAY.INTL" ) },
+ { ocWorkday_MS, 255, 3, 5, V, { RO_E, VR, VR, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "WORKDAY.INTL" ) },
+ EXC_FUNCENTRY_V_VR( ocCeil_ISO, 1, 2, 0, "ISO.CEILING" )
+};
+
+#define EXC_FUNCENTRY_V_VR_IMPORT( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_RO_EXPORT( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_A_VR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, A, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, A, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_RO( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { RO }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+// implicit maxparam=MX
+#define EXC_FUNCENTRY_V_RX( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, MX, V, { RX }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, MX, V, { RO_E, RX }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_VA( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VA }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+/** Functions new in Excel 2010.
+
+ See http://office.microsoft.com/en-us/excel-help/what-s-new-changes-made-to-excel-functions-HA010355760.aspx
+ A lot of statistical functions have been renamed (the 'old' function names still exist).
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2010 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2010[] =
+{
+ EXC_FUNCENTRY_V_VA( ocCovarianceP, 2, 2, 0, "COVARIANCE.P" ),
+ EXC_FUNCENTRY_V_VA( ocCovarianceS, 2, 2, 0, "COVARIANCE.S" ),
+ EXC_FUNCENTRY_V_RX( ocStDevP_MS, 1, MX, 0, "STDEV.P" ),
+ EXC_FUNCENTRY_V_RX( ocStDevS, 1, MX, 0, "STDEV.S" ),
+ EXC_FUNCENTRY_V_RX( ocVarP_MS, 1, MX, 0, "VAR.P" ),
+ EXC_FUNCENTRY_V_RX( ocVarS, 1, MX, 0, "VAR.S" ),
+ EXC_FUNCENTRY_V_VR( ocBetaDist_MS, 4, 6, 0, "BETA.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocBetaInv_MS, 3, 5, 0, "BETA.INV" ),
+ EXC_FUNCENTRY_V_VR( ocBinomDist_MS, 4, 4, 0, "BINOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocBinomInv, 3, 3, 0, "BINOM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocChiSqDist_MS, 3, 3, 0, "CHISQ.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocChiSqInv_MS, 2, 2, 0, "CHISQ.INV" ),
+ EXC_FUNCENTRY_V_VR( ocChiDist_MS, 2, 2, 0, "CHISQ.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocChiInv_MS, 2, 2, 0, "CHISQ.INV.RT" ),
+ EXC_FUNCENTRY_V_VR( ocChiTest_MS, 2, 2, 0, "CHISQ.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocConfidence_N, 3, 3, 0, "CONFIDENCE.NORM" ),
+ EXC_FUNCENTRY_V_VR( ocConfidence_T, 3, 3, 0, "CONFIDENCE.T" ),
+ EXC_FUNCENTRY_V_VR( ocFDist_LT, 4, 4, 0, "F.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocFDist_RT, 3, 3, 0, "F.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocFInv_LT, 3, 3, 0, "F.INV" ),
+ EXC_FUNCENTRY_V_VR( ocFInv_RT, 3, 3, 0, "F.INV.RT" ),
+ EXC_FUNCENTRY_V_VR( ocFTest_MS, 2, 2, 0, "F.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocExpDist_MS, 3, 3, 0, "EXPON.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocHypGeomDist_MS, 5, 5, 0, "HYPGEOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocPoissonDist_MS, 3, 3, 0, "POISSON.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocWeibull_MS, 4, 4, 0, "WEIBULL.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocGammaDist_MS, 4, 4, 0, "GAMMA.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocGammaInv_MS, 3, 3, 0, "GAMMA.INV" ),
+ EXC_FUNCENTRY_V_VR( ocGammaLn_MS, 1, 1, 0, "GAMMALN.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocLogNormDist_MS, 4, 4, 0, "LOGNORM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocLogInv_MS, 3, 3, 0, "LOGNORM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocNormDist_MS, 4, 4, 0, "NORM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocNormInv_MS, 3, 3, 0, "NORM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocStdNormDist_MS, 2, 2, 0, "NORM.S.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocSNormInv_MS, 1, 1, 0, "NORM.S.INV" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_2T, 2, 2, 0, "T.DIST.2T" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_MS, 3, 3, 0, "T.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_RT, 2, 2, 0, "T.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocTInv_2T, 2, 2, 0, "T.INV.2T" ),
+ EXC_FUNCENTRY_V_VR( ocTInv_MS, 2, 2, 0, "T.INV" ),
+ EXC_FUNCENTRY_V_VR( ocTTest_MS, 4, 4, 0, "T.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocPercentile_Inc, 2, 2, 0, "PERCENTILE.INC" ),
+ EXC_FUNCENTRY_V_VR( ocPercentrank_Inc, 2, 3, 0, "PERCENTRANK.INC" ),
+ EXC_FUNCENTRY_V_VR( ocQuartile_Inc, 2, 2, 0, "QUARTILE.INC" ),
+ EXC_FUNCENTRY_V_VR( ocRank_Eq, 2, 3, 0, "RANK.EQ" ),
+ EXC_FUNCENTRY_V_VR( ocPercentile_Exc, 2, 2, 0, "PERCENTILE.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocPercentrank_Exc, 2, 3, 0, "PERCENTRANK.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocQuartile_Exc, 2, 2, 0, "QUARTILE.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocRank_Avg, 2, 3, 0, "RANK.AVG" ),
+ EXC_FUNCENTRY_V_RX( ocModalValue_MS, 1, MX, 0, "MODE.SNGL" ),
+ EXC_FUNCENTRY_V_RX( ocModalValue_Multi, 1, MX, 0, "MODE.MULT" ),
+ EXC_FUNCENTRY_V_VR( ocNegBinomDist_MS, 4, 4, 0, "NEGBINOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocZTest_MS, 2, 3, 0, "Z.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocCeil_Precise, 1, 2, 0, "CEILING.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocFloor_Precise, 1, 2, 0, "FLOOR.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocErf_MS, 1, 1, 0, "ERF.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocErfc_MS, 1, 1, 0, "ERFC.PRECISE" ),
+ EXC_FUNCENTRY_V_RX( ocAggregate, 3, MX, 0, "AGGREGATE" ),
+};
+
+/** Functions new in Excel 2013.
+
+ See http://office.microsoft.com/en-us/excel-help/new-functions-in-excel-2013-HA103980604.aspx
+ Most functions apparently were added for ODF1.2 ODFF / OpenFormula
+ compatibility.
+
+ Functions with EXC_FUNCENTRY_V_VR_IMPORT are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for BIFF, OOXML export
+ uses a different mapping but still uses this mapping here to determine the
+ feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance this mapping here such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2013 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2013[] =
+{
+ EXC_FUNCENTRY_V_VR_IMPORT( ocArcCot, 1, 1, 0, "ACOT" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocArcCotHyp, 1, 1, 0, "ACOTH" ),
+ EXC_FUNCENTRY_V_VR( ocArabic, 1, 1, 0, "ARABIC" ),
+ EXC_FUNCENTRY_V_VR( ocBase, 2, 3, 0, "BASE" ),
+ EXC_FUNCENTRY_V_VR( ocB, 3, 4, 0, "BINOM.DIST.RANGE" ),
+ EXC_FUNCENTRY_V_VR( ocBitAnd, 2, 2, 0, "BITAND" ),
+ EXC_FUNCENTRY_V_VR( ocBitLshift, 2, 2, 0, "BITLSHIFT" ),
+ EXC_FUNCENTRY_V_VR( ocBitOr, 2, 2, 0, "BITOR" ),
+ EXC_FUNCENTRY_V_VR( ocBitRshift, 2, 2, 0, "BITRSHIFT" ),
+ EXC_FUNCENTRY_V_VR( ocBitXor, 2, 2, 0, "BITXOR" ),
+ EXC_FUNCENTRY_V_VR( ocCeil_Math, 1, 3, 0, "CEILING.MATH" ),
+ EXC_FUNCENTRY_V_RO_EXPORT( ocCeil, 1, 3, 0, "CEILING.MATH" ),
+ EXC_FUNCENTRY_V_VR( ocCombinA, 2, 2, 0, "COMBINA" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCot, 1, 1, 0, "COT" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCotHyp, 1, 1, 0, "COTH" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCosecant, 1, 1, 0, "CSC" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCosecantHyp, 1, 1, 0, "CSCH" ),
+ EXC_FUNCENTRY_V_VR( ocGetDiffDate, 2, 2, 0, "DAYS" ),
+ EXC_FUNCENTRY_V_VR( ocDecimal, 2, 2, 0, "DECIMAL" ),
+ EXC_FUNCENTRY_V_VR( ocEncodeURL, 1, 1, 0, "ENCODEURL" ),
+ // NOTE: this FDIST is not our LEGACY.FDIST
+ EXC_FUNCENTRY_V_VR( ocNoName, 3, 4, 0, "FDIST" ),
+ // NOTE: this FINV is not our LEGACY.FINV
+ EXC_FUNCENTRY_V_VR( ocNoName, 3, 3, 0, "FINV" ),
+ EXC_FUNCENTRY_V_VR( ocFilterXML, 2, 2, 0, "FILTERXML" ),
+ EXC_FUNCENTRY_V_VR( ocFloor_Math, 1, 3, 0, "FLOOR.MATH" ),
+ EXC_FUNCENTRY_V_RO_EXPORT( ocFloor, 1, 3, 0, "FLOOR.MATH" ),
+ EXC_FUNCENTRY_V_RO( ocFormula, 1, 1, 0, "FORMULATEXT" ),
+ EXC_FUNCENTRY_V_VR( ocGamma, 1, 1, 0, "GAMMA" ),
+ EXC_FUNCENTRY_V_VR( ocGauss, 1, 1, 0, "GAUSS" ),
+ { ocIfNA, NOID, 2, 2, V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "IFNA" ) },
+ { ocIfNA, 255, 3, 3, V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "IFNA" ) },
+ // IMCOSH, IMCOT, IMCSC, IMCSCH, IMSEC, IMSECH, IMSINH and IMTAN are
+ // implemented in the Analysis Add-In.
+ EXC_FUNCENTRY_V_RO( ocIsFormula, 1, 1, 0, "ISFORMULA" ),
+ EXC_FUNCENTRY_V_VR( ocWeek, 1, 2, 0, "WEEKNUM" ),
+ EXC_FUNCENTRY_V_VR( ocIsoWeeknum, 1, 1, 0, "ISOWEEKNUM" ),
+ EXC_FUNCENTRY_A_VR( ocMatrixUnit, 1, 1, 0, "MUNIT" ),
+ EXC_FUNCENTRY_V_VR( ocNumberValue, 1, 3, 0, "NUMBERVALUE" ),
+ EXC_FUNCENTRY_V_VR( ocPDuration, 3, 3, 0, "PDURATION" ),
+ EXC_FUNCENTRY_V_VR( ocPermutationA, 2, 2, 0, "PERMUTATIONA" ),
+ EXC_FUNCENTRY_V_VR( ocPhi, 1, 1, 0, "PHI" ),
+ EXC_FUNCENTRY_V_VR( ocRRI, 3, 3, 0, "RRI" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocSecant, 1, 1, 0, "SEC" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocSecantHyp, 1, 1, 0, "SECH" ),
+ EXC_FUNCENTRY_V_RO( ocSheet, 0, 1, 0, "SHEET" ),
+ EXC_FUNCENTRY_V_RO( ocSheets, 0, 1, 0, "SHEETS" ),
+ EXC_FUNCENTRY_V_RX( ocSkewp, 1, MX, 0, "SKEW.P" ),
+ EXC_FUNCENTRY_V_VR( ocUnichar, 1, 1, 0, "UNICHAR" ),
+ EXC_FUNCENTRY_V_VR( ocUnicode, 1, 1, 0, "UNICODE" ),
+ EXC_FUNCENTRY_V_VR( ocWebservice, 1, 1, 0, "WEBSERVICE" ),
+ EXC_FUNCENTRY_V_RX( ocXor, 1, MX, 0, "XOR" ),
+ EXC_FUNCENTRY_V_VR( ocErrorType_ODF, 1, 1, 0, "ERROR.TYPE" )
+};
+
+/** Functions new in Excel 2016.
+
+ See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+ and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2016 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2016[] =
+{
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_ADD, 3, 6, 0, "FORECAST.ETS" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_PIA, 3, 7, 0, "FORECAST.ETS.CONFINT" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_SEA, 2, 4, 0, "FORECAST.ETS.SEASONALITY" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_STA, 3, 6, 0, "FORECAST.ETS.STAT" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_LIN, 3, 3, 0, "FORECAST.LINEAR" ),
+ EXC_FUNCENTRY_V_VR( ocConcat_MS, 1, MX, 0, "CONCAT" ),
+ EXC_FUNCENTRY_V_VR( ocTextJoin_MS, 3, MX, 0, "TEXTJOIN" ),
+ EXC_FUNCENTRY_V_VR( ocIfs_MS, 2, MX, 0, "IFS" ),
+ EXC_FUNCENTRY_V_VR( ocSwitch_MS, 3, MX, 0, "SWITCH" ),
+ EXC_FUNCENTRY_V_VR( ocMinIfs_MS, 3, MX, 0, "MINIFS" ),
+ EXC_FUNCENTRY_V_VR( ocMaxIfs_MS, 3, MX, 0, "MAXIFS" )
+};
+
+#define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }
+
+/** Functions defined by OpenFormula, but not supported by Calc (ocNoName) or by Excel (defined op-code). */
+const XclFunctionInfo saFuncTable_Odf[] =
+{
+ EXC_FUNCENTRY_ODF( ocChiSqDist, 2, 3, 0, "CHISQDIST" ),
+ EXC_FUNCENTRY_ODF( ocChiSqInv, 2, 2, 0, "CHISQINV" )
+};
+
+#undef EXC_FUNCENTRY_ODF
+
+#define EXC_FUNCENTRY_OOO( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+// Import Broken Raw ... even without leading _xlfn.
+#define EXC_FUNCENTRY_OOO_IBR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), asciiname }
+
+/** Functions defined by Calc, but not in OpenFormula nor supported by Excel. */
+const XclFunctionInfo saFuncTable_OOoLO[] =
+{
+ EXC_FUNCENTRY_OOO( ocErrorType, 1, 1, 0, "ORG.OPENOFFICE.ERRORTYPE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocErrorType, 1, 1, 0, "ERRORTYPE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocMultiArea, 1, MX, 0, "ORG.OPENOFFICE.MULTIRANGE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocMultiArea, 1, MX, 0, "MULTIRANGE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocBackSolver, 3, 3, 0, "ORG.OPENOFFICE.GOALSEEK" ),
+ EXC_FUNCENTRY_OOO_IBR( ocBackSolver,3, 3, 0, "GOALSEEK" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocEasterSunday, 1, 1, 0, "ORG.OPENOFFICE.EASTERSUNDAY" ),
+ EXC_FUNCENTRY_OOO_IBR( ocEasterSunday,1,1, 0, "EASTERSUNDAY" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocCurrent, 0, 0, 0, "ORG.OPENOFFICE.CURRENT" ),
+ EXC_FUNCENTRY_OOO_IBR( ocCurrent, 0, 0, 0, "CURRENT" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocStyle, 1, 3, 0, "ORG.OPENOFFICE.STYLE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocStyle, 1, 3, 0, "STYLE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocConvertOOo, 3, 3, 0, "ORG.OPENOFFICE.CONVERT" ),
+ EXC_FUNCENTRY_OOO( ocColor, 3, 4, 0, "ORG.LIBREOFFICE.COLOR" ),
+ EXC_FUNCENTRY_OOO( ocRawSubtract, 2, MX, 0, "ORG.LIBREOFFICE.RAWSUBTRACT" ),
+ EXC_FUNCENTRY_OOO( ocWeeknumOOo, 2, 2, 0, "ORG.LIBREOFFICE.WEEKNUM_OOO" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_MUL, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.MULT" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_PIM, 3, 7, 0, "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_STM, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT" ),
+ EXC_FUNCENTRY_OOO( ocRoundSig, 2, 2, 0, "ORG.LIBREOFFICE.ROUNDSIG" ),
+ EXC_FUNCENTRY_OOO( ocRegex, 2, 4, 0, "ORG.LIBREOFFICE.REGEX" ),
+ EXC_FUNCENTRY_OOO( ocFourier, 2, 5, 0, "ORG.LIBREOFFICE.FOURIER" ),
+ EXC_FUNCENTRY_OOO( ocRandomNV, 0, 0, 0, "ORG.LIBREOFFICE.RAND.NV" ),
+ EXC_FUNCENTRY_OOO( ocRandbetweenNV, 2, 2, 0, "ORG.LIBREOFFICE.RANDBETWEEN.NV" )
+};
+
+#undef EXC_FUNCENTRY_OOO_IBR
+#undef EXC_FUNCENTRY_OOO
+
+XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot )
+{
+ void (XclFunctionProvider::*pFillFunc)( const XclFunctionInfo*, const XclFunctionInfo* ) =
+ rRoot.IsImport() ? &XclFunctionProvider::FillXclFuncMap : &XclFunctionProvider::FillScFuncMap;
+
+ /* Only read/write functions supported in the current BIFF version.
+ Function tables from later BIFF versions may overwrite single functions
+ from earlier tables. */
+ XclBiff eBiff = rRoot.GetBiff();
+ if( eBiff >= EXC_BIFF2 )
+ (this->*pFillFunc)(saFuncTable_2, std::end(saFuncTable_2));
+ if( eBiff >= EXC_BIFF3 )
+ (this->*pFillFunc)(saFuncTable_3, std::end(saFuncTable_3));
+ if( eBiff >= EXC_BIFF4 )
+ (this->*pFillFunc)(saFuncTable_4, std::end(saFuncTable_4));
+ if( eBiff >= EXC_BIFF5 )
+ (this->*pFillFunc)(saFuncTable_5, std::end(saFuncTable_5));
+ if( eBiff >= EXC_BIFF8 )
+ (this->*pFillFunc)(saFuncTable_8, std::end(saFuncTable_8));
+ (this->*pFillFunc)(saFuncTable_Oox, std::end(saFuncTable_Oox));
+ (this->*pFillFunc)(saFuncTable_2010, std::end(saFuncTable_2010));
+ (this->*pFillFunc)(saFuncTable_2013, std::end(saFuncTable_2013));
+ (this->*pFillFunc)(saFuncTable_2016, std::end(saFuncTable_2016));
+ (this->*pFillFunc)(saFuncTable_Odf, std::end(saFuncTable_Odf));
+ (this->*pFillFunc)(saFuncTable_OOoLO, std::end(saFuncTable_OOoLO));
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclFunc( sal_uInt16 nXclFunc ) const
+{
+ // only in import filter allowed
+ OSL_ENSURE( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclFunc - wrong filter" );
+ XclFuncMap::const_iterator aIt = maXclFuncMap.find( nXclFunc );
+ return (aIt == maXclFuncMap.end()) ? nullptr : aIt->second;
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclMacroName( const OUString& rXclMacroName ) const
+{
+ // only in import filter allowed, but do not test maXclMacroNameMap, it may be empty for old BIFF versions
+ OSL_ENSURE( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclMacroName - wrong filter" );
+ XclMacroNameMap::const_iterator aIt = maXclMacroNameMap.find( rXclMacroName );
+ return (aIt == maXclMacroNameMap.end()) ? nullptr : aIt->second;
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromOpCode( OpCode eOpCode ) const
+{
+ // only in export filter allowed
+ OSL_ENSURE( !maScFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromOpCode - wrong filter" );
+ ScFuncMap::const_iterator aIt = maScFuncMap.find( eOpCode );
+ return (aIt == maScFuncMap.end()) ? nullptr : aIt->second;
+}
+
+void XclFunctionProvider::FillXclFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd )
+{
+ for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt )
+ {
+ if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_EXPORTONLY ) )
+ {
+ if( pIt->mnXclFunc != NOID )
+ maXclFuncMap[ pIt->mnXclFunc ] = pIt;
+ if( pIt->IsMacroFunc() )
+ maXclMacroNameMap[ pIt->GetMacroFuncName() ] = pIt;
+ }
+ }
+}
+
+void XclFunctionProvider::FillScFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd )
+{
+ for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt )
+ if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_IMPORTONLY ) )
+ maScFuncMap[ pIt->meOpCode ] = pIt;
+}
+
+// Token array ================================================================
+
+XclTokenArray::XclTokenArray( bool bVolatile ) :
+ mbVolatile( bVolatile )
+{
+}
+
+XclTokenArray::XclTokenArray( ScfUInt8Vec& rTokVec, ScfUInt8Vec& rExtDataVec, bool bVolatile ) :
+ mbVolatile( bVolatile )
+{
+ maTokVec.swap( rTokVec );
+ maExtDataVec.swap( rExtDataVec );
+}
+
+sal_uInt16 XclTokenArray::GetSize() const
+{
+ OSL_ENSURE( maTokVec.size() <= 0xFFFF, "XclTokenArray::GetSize - array too long" );
+ return limit_cast< sal_uInt16 >( maTokVec.size() );
+}
+
+sal_uInt16 XclTokenArray::ReadSize(XclImpStream& rStrm)
+{
+ return rStrm.ReaduInt16();
+}
+
+void XclTokenArray::ReadArray(sal_uInt16 nSize, XclImpStream& rStrm)
+{
+ maTokVec.resize(0);
+
+ const std::size_t nMaxBuffer = 4096;
+ std::size_t nBytesLeft = nSize;
+ std::size_t nTotalRead = 0;
+
+ while (true)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadRequest = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ maTokVec.resize(maTokVec.size() + nReadRequest);
+ auto nRead = rStrm.Read(maTokVec.data() + nTotalRead, nReadRequest);
+ nTotalRead += nRead;
+ if (nRead != nReadRequest)
+ {
+ maTokVec.resize(nTotalRead);
+ break;
+ }
+ nBytesLeft -= nRead;
+ }
+}
+
+void XclTokenArray::Read( XclImpStream& rStrm )
+{
+ ReadArray(ReadSize(rStrm), rStrm);
+}
+
+void XclTokenArray::WriteSize( XclExpStream& rStrm ) const
+{
+ rStrm << GetSize();
+}
+
+void XclTokenArray::WriteArray( XclExpStream& rStrm ) const
+{
+ if( !maTokVec.empty() )
+ rStrm.Write(maTokVec.data(), GetSize());
+ if( !maExtDataVec.empty() )
+ rStrm.Write(maExtDataVec.data(), maExtDataVec.size());
+}
+
+void XclTokenArray::Write( XclExpStream& rStrm ) const
+{
+ WriteSize( rStrm );
+ WriteArray( rStrm );
+}
+
+bool XclTokenArray::operator==( const XclTokenArray& rTokArr ) const
+{
+ return (mbVolatile == rTokArr.mbVolatile) && (maTokVec == rTokArr.maTokVec) && (maExtDataVec == rTokArr.maExtDataVec);
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArray& rTokArr )
+{
+ rTokArr.Read( rStrm );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArray& rTokArr )
+{
+ rTokArr.Write( rStrm );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArrayRef& rxTokArr )
+{
+ if( rxTokArr )
+ rxTokArr->Write( rStrm );
+ else
+ rStrm << sal_uInt16( 0 );
+ return rStrm;
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator() :
+ mppScTokenBeg( nullptr ),
+ mppScTokenEnd( nullptr ),
+ mppScToken( nullptr ),
+ mbSkipSpaces( false )
+{
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator( const ScTokenArray& rScTokArr, bool bSkipSpaces )
+{
+ Init( rScTokArr, bSkipSpaces );
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator( const XclTokenArrayIterator& rTokArrIt, bool bSkipSpaces ) :
+ mppScTokenBeg( rTokArrIt.mppScTokenBeg ),
+ mppScTokenEnd( rTokArrIt.mppScTokenEnd ),
+ mppScToken( rTokArrIt.mppScToken ),
+ mbSkipSpaces( bSkipSpaces )
+{
+ SkipSpaces();
+}
+
+void XclTokenArrayIterator::Init( const ScTokenArray& rScTokArr, bool bSkipSpaces )
+{
+ sal_uInt16 nTokArrLen = rScTokArr.GetLen();
+ mppScTokenBeg = static_cast< const FormulaToken* const* >( nTokArrLen ? rScTokArr.GetArray() : nullptr );
+ mppScTokenEnd = mppScTokenBeg ? (mppScTokenBeg + nTokArrLen) : nullptr;
+ mppScToken = (mppScTokenBeg != mppScTokenEnd) ? mppScTokenBeg : nullptr;
+ mbSkipSpaces = bSkipSpaces;
+ SkipSpaces();
+}
+
+XclTokenArrayIterator& XclTokenArrayIterator::operator++()
+{
+ NextRawToken();
+ SkipSpaces();
+ return *this;
+}
+
+void XclTokenArrayIterator::NextRawToken()
+{
+ if( mppScToken )
+ if( (++mppScToken == mppScTokenEnd) || !*mppScToken )
+ mppScToken = nullptr;
+}
+
+void XclTokenArrayIterator::SkipSpaces()
+{
+ if( mbSkipSpaces )
+ {
+ OpCode eOp;
+ while( Is() && (((eOp = (*this)->GetOpCode()) == ocSpaces) || eOp == ocWhitespace) )
+ NextRawToken();
+ }
+}
+
+// strings and string lists ---------------------------------------------------
+
+bool XclTokenArrayHelper::GetTokenString( OUString& rString, const FormulaToken& rScToken )
+{
+ bool bIsStr = (rScToken.GetType() == svString) && (rScToken.GetOpCode() == ocPush);
+ if( bIsStr ) rString = rScToken.GetString().getString();
+ return bIsStr;
+}
+
+bool XclTokenArrayHelper::GetString( OUString& rString, const ScTokenArray& rScTokArr )
+{
+ XclTokenArrayIterator aIt( rScTokArr, true );
+ // something is following the string token -> error
+ return aIt.Is() && GetTokenString( rString, *aIt ) && !++aIt;
+}
+
+bool XclTokenArrayHelper::GetStringList( OUString& rStringList, const ScTokenArray& rScTokArr, sal_Unicode cSep )
+{
+ bool bRet = true;
+ XclTokenArrayIterator aIt( rScTokArr, true );
+ enum { STATE_START, STATE_STR, STATE_SEP, STATE_END } eState = STATE_START;
+ while( eState != STATE_END ) switch( eState )
+ {
+ case STATE_START:
+ eState = aIt.Is() ? STATE_STR : STATE_END;
+ break;
+ case STATE_STR:
+ {
+ OUString aString;
+ bRet = GetTokenString( aString, *aIt );
+ if( bRet ) rStringList += aString ;
+ eState = (bRet && (++aIt).Is()) ? STATE_SEP : STATE_END;
+ break;
+ }
+ case STATE_SEP:
+ bRet = aIt->GetOpCode() == ocSep;
+ if( bRet ) rStringList += OUStringChar(cSep);
+ eState = (bRet && (++aIt).Is()) ? STATE_STR : STATE_END;
+ break;
+ default:;
+ }
+ return bRet;
+}
+
+void XclTokenArrayHelper::ConvertStringToList(
+ ScTokenArray& rScTokArr, svl::SharedStringPool& rSPool, sal_Unicode cStringSep )
+{
+ OUString aString;
+ if( !GetString( aString, rScTokArr ) )
+ return;
+
+ rScTokArr.Clear();
+ if (aString.isEmpty())
+ return;
+ sal_Int32 nStringIx = 0;
+ for (;;)
+ {
+ OUString aToken( aString.getToken( 0, cStringSep, nStringIx ) );
+ rScTokArr.AddString(rSPool.intern(comphelper::string::stripStart(aToken, ' ')));
+ if (nStringIx<0)
+ break;
+ rScTokArr.AddOpCode( ocSep );
+ }
+}
+
+// multiple operations --------------------------------------------------------
+
+namespace {
+
+bool lclGetAddress( const ScDocument& rDoc, ScAddress& rAddress, const FormulaToken& rToken, const ScAddress& rPos )
+{
+ OpCode eOpCode = rToken.GetOpCode();
+ bool bIsSingleRef = (eOpCode == ocPush) && (rToken.GetType() == svSingleRef);
+ if( bIsSingleRef )
+ {
+ const ScSingleRefData& rRef = *rToken.GetSingleRef();
+ rAddress = rRef.toAbs(rDoc, rPos);
+ bIsSingleRef = !rRef.IsDeleted();
+ }
+ return bIsSingleRef;
+}
+
+} // namespace
+
+bool XclTokenArrayHelper::GetMultipleOpRefs(
+ const ScDocument& rDoc,
+ XclMultipleOpRefs& rRefs, const ScTokenArray& rScTokArr, const ScAddress& rScPos )
+{
+ rRefs.mbDblRefMode = false;
+ enum
+ {
+ stBegin, stTableOp, stOpen, stFormula, stFormulaSep,
+ stColFirst, stColFirstSep, stColRel, stColRelSep,
+ stRowFirst, stRowFirstSep, stRowRel, stClose, stError
+ } eState = stBegin; // last read token
+ for( XclTokenArrayIterator aIt( rScTokArr, true ); aIt.Is() && (eState != stError); ++aIt )
+ {
+ OpCode eOpCode = aIt->GetOpCode();
+ bool bIsSep = eOpCode == ocSep;
+ switch( eState )
+ {
+ case stBegin:
+ eState = (eOpCode == ocTableOp) ? stTableOp : stError;
+ break;
+ case stTableOp:
+ eState = (eOpCode == ocOpen) ? stOpen : stError;
+ break;
+ case stOpen:
+ eState = lclGetAddress(rDoc, rRefs.maFmlaScPos, *aIt, rScPos) ? stFormula : stError;
+ break;
+ case stFormula:
+ eState = bIsSep ? stFormulaSep : stError;
+ break;
+ case stFormulaSep:
+ eState = lclGetAddress(rDoc, rRefs.maColFirstScPos, *aIt, rScPos) ? stColFirst : stError;
+ break;
+ case stColFirst:
+ eState = bIsSep ? stColFirstSep : stError;
+ break;
+ case stColFirstSep:
+ eState = lclGetAddress(rDoc, rRefs.maColRelScPos, *aIt, rScPos) ? stColRel : stError;
+ break;
+ case stColRel:
+ eState = bIsSep ? stColRelSep : ((eOpCode == ocClose) ? stClose : stError);
+ break;
+ case stColRelSep:
+ eState = lclGetAddress(rDoc, rRefs.maRowFirstScPos, *aIt, rScPos) ? stRowFirst : stError;
+ rRefs.mbDblRefMode = true;
+ break;
+ case stRowFirst:
+ eState = bIsSep ? stRowFirstSep : stError;
+ break;
+ case stRowFirstSep:
+ eState = lclGetAddress(rDoc, rRefs.maRowRelScPos, *aIt, rScPos) ? stRowRel : stError;
+ break;
+ case stRowRel:
+ eState = (eOpCode == ocClose) ? stClose : stError;
+ break;
+ default:
+ eState = stError;
+ }
+ }
+ return eState == stClose;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlpage.cxx b/sc/source/filter/excel/xlpage.cxx
new file mode 100644
index 0000000000..937aa9427f
--- /dev/null
+++ b/sc/source/filter/excel/xlpage.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlpage.hxx>
+#include <xltools.hxx>
+#include <editeng/paperinf.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sal/macros.h>
+#include <editeng/brushitem.hxx>
+
+namespace{
+
+struct XclPaperSize
+{
+ Paper mePaper; /// SVX paper size identifier.
+ tools::Long mnWidth; /// Paper width in twips.
+ tools::Long mnHeight; /// Paper height in twips.
+};
+
+constexpr tools::Long in2twips(double n_inch)
+{
+ return o3tl::convert(n_inch, o3tl::Length::in, o3tl::Length::twip) + 0.5;
+}
+constexpr tools::Long mm2twips(double n_mm)
+{
+ return o3tl::convert(n_mm, o3tl::Length::mm, o3tl::Length::twip) + 0.5;
+}
+constexpr tools::Long twips2mm(tools::Long n_twips)
+{
+ return o3tl::convert(n_twips, o3tl::Length::twip, o3tl::Length::mm);
+}
+
+// see ApiPaperSize spPaperSizeTable in filter and aDinTab in i18nutil
+constexpr XclPaperSize pPaperSizeTable[] =
+{
+/* 0*/ { PAPER_USER, 0, 0 }, // undefined
+ { PAPER_LETTER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter Small
+ { PAPER_TABLOID, in2twips( 11 ), in2twips( 17 ) }, // Tabloid
+ { PAPER_LEDGER, in2twips( 17 ), in2twips( 11 ) }, // Ledger
+/* 5*/ { PAPER_LEGAL, in2twips( 8.5 ), in2twips( 14 ) }, // Legal
+ { PAPER_STATEMENT, in2twips( 5.5 ), in2twips( 8.5 ) }, // Statement
+ { PAPER_EXECUTIVE, in2twips( 7.25 ), in2twips( 10.5 ) }, // Executive
+ { PAPER_A3, mm2twips( 297 ), mm2twips( 420 ) }, // A3
+ { PAPER_A4, mm2twips( 210 ), mm2twips( 297 ) }, // A4
+/* 10*/ { PAPER_USER, mm2twips( 210 ), mm2twips( 297 ) }, // A4 Small
+ { PAPER_A5, mm2twips( 148 ), mm2twips( 210 ) }, // A5
+ /* for JIS vs ISO B confusion see:
+ https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes
+ http://wiki.openoffice.org/wiki/DefaultPaperSize comments
+ http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf */
+ { PAPER_B4_JIS, mm2twips( 257 ), mm2twips( 364 ) }, // B4 (JIS)
+ { PAPER_B5_JIS, mm2twips( 182 ), mm2twips( 257 ) }, // B5 (JIS)
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 13 ) }, // Folio
+/* 15*/ { PAPER_QUARTO, mm2twips( 215 ), mm2twips( 275 ) }, // Quarto
+ { PAPER_10x14, in2twips( 10 ), in2twips( 14 ) }, // 10x14
+ { PAPER_USER, in2twips( 11 ), in2twips( 17 ) }, // 11x17
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Note
+ { PAPER_ENV_9, in2twips( 3.875 ), in2twips( 8.875 ) }, // Envelope #9
+/* 20*/ { PAPER_ENV_10, in2twips( 4.125 ), in2twips( 9.5 ) }, // Envelope #10
+ { PAPER_ENV_11, in2twips( 4.5 ), in2twips( 10.375 ) }, // Envelope #11
+ { PAPER_ENV_12, in2twips( 4.75 ), in2twips( 11 ) }, // Envelope #12
+ { PAPER_ENV_14, in2twips( 5 ), in2twips( 11.5 ) }, // Envelope #14
+ { PAPER_C, in2twips( 17 ), in2twips( 22 ) }, // ANSI-C
+/* 25*/ { PAPER_D, in2twips( 22 ), in2twips( 34 ) }, // ANSI-D
+ { PAPER_E, in2twips( 34 ), in2twips( 44 ) }, // ANSI-E
+ { PAPER_ENV_DL, mm2twips( 110 ), mm2twips( 220 ) }, // Envelope DL
+ { PAPER_ENV_C5, mm2twips( 162 ), mm2twips( 229 ) }, // Envelope C5
+ { PAPER_ENV_C3, mm2twips( 324 ), mm2twips( 458 ) }, // Envelope C3
+/* 30*/ { PAPER_ENV_C4, mm2twips( 229 ), mm2twips( 324 ) }, // Envelope C4
+ { PAPER_ENV_C6, mm2twips( 114 ), mm2twips( 162 ) }, // Envelope C6
+ { PAPER_ENV_C65, mm2twips( 114 ), mm2twips( 229 ) }, // Envelope C65
+ { PAPER_B4_ISO, mm2twips( 250 ), mm2twips( 353 ) }, // B4 (ISO)
+ { PAPER_B5_ISO, mm2twips( 176 ), mm2twips( 250 ) }, // B5 (ISO)
+/* 35*/ { PAPER_B6_ISO, mm2twips( 125 ), mm2twips( 176 ) }, // B6 (ISO)
+ { PAPER_ENV_ITALY, mm2twips( 110 ), mm2twips( 230 ) }, // Envelope Italy
+ { PAPER_ENV_MONARCH, in2twips( 3.875 ), in2twips( 7.5 ) }, // Envelope Monarch
+ { PAPER_ENV_PERSONAL, in2twips( 3.625 ), in2twips( 6.5 ) }, // 6 3/4 Envelope
+ { PAPER_FANFOLD_US, in2twips( 14.875 ), in2twips( 11 ) }, // US Std Fanfold
+/* 40*/ { PAPER_FANFOLD_DE, in2twips( 8.5 ), in2twips( 12 ) }, // German Std Fanfold
+ { PAPER_FANFOLD_LEGAL_DE, in2twips( 8.5 ), in2twips( 13 ) }, // German Legal Fanfold
+ { PAPER_B4_ISO, mm2twips( 250 ), mm2twips( 353 ) }, // B4 (ISO)
+ { PAPER_POSTCARD_JP,mm2twips( 100 ), mm2twips( 148 ) }, // Japanese Postcard
+ { PAPER_9x11, in2twips( 9 ), in2twips( 11 ) }, // 9x11
+/* 45*/ { PAPER_10x11, in2twips( 10 ), in2twips( 11 ) }, // 10x11
+ { PAPER_15x11, in2twips( 15 ), in2twips( 11 ) }, // 15x11
+ { PAPER_ENV_INVITE, mm2twips( 220 ), mm2twips( 220 ) }, // Envelope Invite
+ { PAPER_USER, 0, 0 }, // undefined
+ { PAPER_USER, 0, 0 }, // undefined
+ /* See: https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes */
+/* 50*/ { PAPER_USER, in2twips( 9.5 ), in2twips( 12 ) }, // Letter Extra
+ { PAPER_USER, in2twips( 9.5 ), in2twips( 15 ) }, // Legal Extra
+ { PAPER_USER, in2twips( 11.69 ), in2twips( 18 ) }, // Tabloid Extra
+ { PAPER_USER, mm2twips( 235 ), mm2twips( 322 ) }, // A4 Extra
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter Transverse
+/* 55*/ { PAPER_USER, mm2twips( 210 ), mm2twips( 297 ) }, // A4 Transverse
+ { PAPER_USER, in2twips( 9.5 ), in2twips( 12 ) }, // Letter Extra Transverse
+ { PAPER_A_PLUS, mm2twips( 227 ), mm2twips( 356 ) }, // Super A/A4
+ { PAPER_B_PLUS, mm2twips( 305 ), mm2twips( 487 ) }, // Super B/A3
+ { PAPER_LETTER_PLUS,in2twips( 8.5 ), in2twips( 12.69 ) }, // Letter Plus
+/* 60*/ { PAPER_A4_PLUS, mm2twips( 210 ), mm2twips( 330 ) }, // A4 Plus
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 210 ) }, // A5 Transverse
+ { PAPER_USER, mm2twips( 182 ), mm2twips( 257 ) }, // B5 (JIS) Transverse
+ { PAPER_USER, mm2twips( 322 ), mm2twips( 445 ) }, // A3 Extra
+ { PAPER_USER, mm2twips( 174 ), mm2twips( 235 ) }, // A5 Extra
+/* 65*/ { PAPER_USER, mm2twips( 201 ), mm2twips( 276 ) }, // B5 (ISO) Extra
+ { PAPER_A2, mm2twips( 420 ), mm2twips( 594 ) }, // A2
+ { PAPER_USER, mm2twips( 297 ), mm2twips( 420 ) }, // A3 Transverse
+ { PAPER_USER, mm2twips( 322 ), mm2twips( 445 ) }, // A3 Extra Transverse
+ { PAPER_DOUBLEPOSTCARD_JP, mm2twips( 200 ), mm2twips( 148 ) }, // Double Japanese Postcard
+/* 70*/ { PAPER_A6, mm2twips( 105 ), mm2twips( 148 ) }, // A6
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #2
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #3
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #3
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #4
+/* 75*/ { PAPER_USER, in2twips( 11 ), in2twips( 8.5 ) }, // Letter Rotated
+ { PAPER_USER, mm2twips( 420 ), mm2twips( 297 ) }, // A3 Rotated
+ { PAPER_USER, mm2twips( 297 ), mm2twips( 210 ) }, // A4 Rotated
+ { PAPER_USER, mm2twips( 210 ), mm2twips( 148 ) }, // A5 Rotated
+ { PAPER_USER, mm2twips( 364 ), mm2twips( 257 ) }, // B4 (JIS) Rotated
+/* 80*/ { PAPER_USER, mm2twips( 257 ), mm2twips( 182 ) }, // B5 (JIS) Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 100 ) }, // Japanese Postcard Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 200 ) }, // Double Japanese Postcard Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 105 ) }, // A6 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #2 Rotated
+/* 85*/ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #3 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #3 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #4 Rotated
+ { PAPER_B6_JIS, mm2twips( 128 ), mm2twips( 182 ) }, // B6 (JIS)
+ { PAPER_USER, mm2twips( 182 ), mm2twips( 128 ) }, // B6 (JIS) Rotated
+/* 90*/ { PAPER_12x11, in2twips( 12 ), in2twips( 11 ) } // 12x11
+};
+
+} //namespace
+
+// Page settings ==============================================================
+
+XclPageData::XclPageData()
+{
+ SetDefaults();
+}
+
+XclPageData::~XclPageData()
+{
+ // SvxBrushItem incomplete in header
+}
+
+void XclPageData::SetDefaults()
+{
+ maHorPageBreaks.clear();
+ maVerPageBreaks.clear();
+ mxBrushItem.reset();
+ maHeader.clear();
+ maFooter.clear();
+ maHeaderEven.clear();
+ maFooterEven.clear();
+ mfLeftMargin = mfRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_LR );
+ mfTopMargin = mfBottomMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_TB );
+ mfHeaderMargin = mfFooterMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HF );
+ mfHdrLeftMargin = mfHdrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HLR );
+ mfFtrLeftMargin = mfFtrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_FLR );
+ mnStrictPaperSize = mnPaperSize = EXC_PAPERSIZE_DEFAULT;
+ mnPaperWidth = 0;
+ mnPaperHeight = 0;
+ mnCopies = 1;
+ mnStartPage = 0;
+ mnScaling = 100;
+ mnFitToWidth = mnFitToHeight = 1;
+ mnHorPrintRes = mnVerPrintRes = 300;
+ mbUseEvenHF = mbUseFirstHF = false;
+ mbValid = false;
+ mbPortrait = true;
+ mbPrintInRows = mbBlackWhite = mbDraftQuality = mbPrintNotes = mbManualStart = mbFitToPages = false;
+ mbHorCenter = mbVerCenter = mbPrintHeadings = mbPrintGrid = false;
+}
+
+Size XclPageData::GetScPaperSize() const
+{
+ const XclPaperSize* pEntry = pPaperSizeTable;
+ if( mnPaperSize < SAL_N_ELEMENTS( pPaperSizeTable ) )
+ pEntry += mnPaperSize;
+
+ Size aSize;
+ if( pEntry->mePaper == PAPER_USER )
+ aSize = Size( pEntry->mnWidth, pEntry->mnHeight );
+ else
+ aSize = SvxPaperInfo::GetPaperSize( pEntry->mePaper );
+
+ // invalid size -> back to default
+ if( !aSize.Width() || !aSize.Height() )
+ aSize = SvxPaperInfo::GetDefaultPaperSize();
+
+ if( !mbPortrait )
+ {
+ // swap width and height
+ tools::Long n = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(n);
+ }
+
+ return aSize;
+}
+
+void XclPageData::SetScPaperSize( const Size& rSize, bool bPortrait, bool bStrictSize )
+{
+ mbPortrait = bPortrait;
+ mnPaperSize = 0;
+ tools::Long nWidth = bPortrait ? rSize.Width() : rSize.Height();
+ tools::Long nHeight = bPortrait ? rSize.Height() : rSize.Width();
+ tools::Long nMaxWDiff = 80;
+ tools::Long nMaxHDiff = 50;
+
+ mnPaperWidth = twips2mm( nWidth );
+ mnPaperHeight = twips2mm( nHeight );
+ if( bStrictSize )
+ {
+ nMaxWDiff = 5;
+ nMaxHDiff = 5;
+ mnStrictPaperSize = EXC_PAPERSIZE_USER;
+ }
+ else
+ {
+ mnPaperSize = EXC_PAPERSIZE_DEFAULT;
+ }
+
+ for( const auto &rEntry : pPaperSizeTable)
+ {
+ tools::Long nWDiff = std::abs( rEntry.mnWidth - nWidth );
+ tools::Long nHDiff = std::abs( rEntry.mnHeight - nHeight );
+ if( ((nWDiff <= nMaxWDiff) && (nHDiff < nMaxHDiff)) ||
+ ((nWDiff < nMaxWDiff) && (nHDiff <= nMaxHDiff)) )
+ {
+ sal_uInt16 nIndex = static_cast< sal_uInt16 >( &rEntry - pPaperSizeTable );
+ mnPaperSize = nIndex;
+ if( bStrictSize )
+ mnStrictPaperSize = nIndex;
+
+ nMaxWDiff = nWDiff;
+ nMaxHDiff = nHDiff;
+ }
+ }
+ if( !bStrictSize )
+ SetScPaperSize( rSize, bPortrait, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlpivot.cxx b/sc/source/filter/excel/xlpivot.cxx
new file mode 100644
index 0000000000..d18ab7416a
--- /dev/null
+++ b/sc/source/filter/excel/xlpivot.cxx
@@ -0,0 +1,1043 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpsave.hxx>
+#include <xestream.hxx>
+#include <xistream.hxx>
+#include <xestring.hxx>
+#include <xlpivot.hxx>
+#include <generalfunction.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation;
+
+namespace ScDPSortMode = ::com::sun::star::sheet::DataPilotFieldSortMode;
+namespace ScDPShowItemsMode = ::com::sun::star::sheet::DataPilotFieldShowItemsMode;
+namespace ScDPLayoutMode = ::com::sun::star::sheet::DataPilotFieldLayoutMode;
+namespace ScDPRefItemType = ::com::sun::star::sheet::DataPilotFieldReferenceItemType;
+namespace ScDPGroupBy = ::com::sun::star::sheet::DataPilotFieldGroupBy;
+
+// Pivot cache
+
+XclPCItem::XclPCItem() :
+ meType( EXC_PCITEM_INVALID ),
+ maDateTime( DateTime::EMPTY )
+{
+}
+
+XclPCItem::~XclPCItem()
+{
+}
+
+void XclPCItem::SetEmpty()
+{
+ meType = EXC_PCITEM_EMPTY;
+ maText.clear();
+}
+
+void XclPCItem::SetText( const OUString& rText )
+{
+ meType = EXC_PCITEM_TEXT;
+ maText = rText;
+}
+
+void XclPCItem::SetDouble( double fValue, const OUString& rText )
+{
+ meType = EXC_PCITEM_DOUBLE;
+ maText = rText;
+ mfValue = fValue;
+}
+
+void XclPCItem::SetDateTime( const DateTime& rDateTime, const OUString& rText )
+{
+ meType = EXC_PCITEM_DATETIME;
+ maText = rText;
+ maDateTime = rDateTime;
+}
+
+void XclPCItem::SetInteger( sal_Int16 nValue )
+{
+ meType = EXC_PCITEM_INTEGER;
+ maText = OUString::number(nValue);
+ mnValue = nValue;
+}
+
+void XclPCItem::SetError( sal_uInt16 nError )
+{
+ meType = EXC_PCITEM_ERROR;
+ maText.clear();
+ mnError = nError;
+ switch( nError )
+ {
+ case 0x00: maText = "#nullptr!"; break;
+ case 0x07: maText = "#DIV/0!"; break;
+ case 0x0F: maText = "#VALUE!"; break;
+ case 0x17: maText = "#REF!"; break;
+ case 0x1D: maText = "#NAME?"; break;
+ case 0x24: maText = "#NUM!"; break;
+ case 0x2A: maText = "#N/A"; break;
+ default: break;
+ }
+}
+
+void XclPCItem::SetBool( bool bValue, const OUString& rText )
+{
+ meType = EXC_PCITEM_BOOL;
+ maText = rText;
+ mbValue = bValue;
+}
+
+bool XclPCItem::IsEqual( const XclPCItem& rItem ) const
+{
+ if( meType == rItem.meType ) switch( meType )
+ {
+ case EXC_PCITEM_INVALID: return true;
+ case EXC_PCITEM_EMPTY: return true;
+ case EXC_PCITEM_TEXT: return maText == rItem.maText;
+ case EXC_PCITEM_DOUBLE: return mfValue == rItem.mfValue;
+ case EXC_PCITEM_DATETIME: return maDateTime == rItem.maDateTime;
+ case EXC_PCITEM_INTEGER: return mnValue == rItem.mnValue;
+ case EXC_PCITEM_BOOL: return mbValue == rItem.mbValue;
+ case EXC_PCITEM_ERROR: return mnError == rItem.mnError;
+ default: OSL_FAIL( "XclPCItem::IsEqual - unknown pivot cache item type" );
+ }
+ return false;
+}
+
+bool XclPCItem::IsEmpty() const
+{
+ return meType == EXC_PCITEM_EMPTY;
+}
+
+const OUString* XclPCItem::GetText() const
+{
+ return (meType == EXC_PCITEM_TEXT || meType == EXC_PCITEM_ERROR) ? &maText : nullptr;
+}
+
+const double* XclPCItem::GetDouble() const
+{
+ return (meType == EXC_PCITEM_DOUBLE) ? &mfValue : nullptr;
+}
+
+const DateTime* XclPCItem::GetDateTime() const
+{
+ return (meType == EXC_PCITEM_DATETIME) ? &maDateTime : nullptr;
+}
+
+const sal_Int16* XclPCItem::GetInteger() const
+{
+ return (meType == EXC_PCITEM_INTEGER) ? &mnValue : nullptr;
+}
+
+const sal_uInt16* XclPCItem::GetError() const
+{
+ return (meType == EXC_PCITEM_ERROR) ? &mnError : nullptr;
+}
+
+const bool* XclPCItem::GetBool() const
+{
+ return (meType == EXC_PCITEM_BOOL) ? &mbValue : nullptr;
+}
+
+XclPCItemType XclPCItem::GetType() const
+{
+ return meType;
+}
+
+// Field settings =============================================================
+
+XclPCFieldInfo::XclPCFieldInfo() :
+ mnFlags( 0 ),
+ mnGroupChild( 0 ),
+ mnGroupBase( 0 ),
+ mnVisItems( 0 ),
+ mnGroupItems( 0 ),
+ mnBaseItems( 0 ),
+ mnOrigItems( 0 )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCFieldInfo& rInfo )
+{
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnGroupChild = rStrm.ReaduInt16();
+ rInfo.mnGroupBase = rStrm.ReaduInt16();
+ rInfo.mnVisItems = rStrm.ReaduInt16();
+ rInfo.mnGroupItems = rStrm.ReaduInt16();
+ rInfo.mnBaseItems = rStrm.ReaduInt16();
+ rInfo.mnOrigItems = rStrm.ReaduInt16();
+ if( rStrm.GetRecLeft() >= 3 )
+ rInfo.maName = rStrm.ReadUniString();
+ else
+ rInfo.maName.clear();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnFlags
+ << rInfo.mnGroupChild
+ << rInfo.mnGroupBase
+ << rInfo.mnVisItems
+ << rInfo.mnGroupItems
+ << rInfo.mnBaseItems
+ << rInfo.mnOrigItems
+ << XclExpString( rInfo.maName );
+}
+
+// Numeric grouping field settings ============================================
+
+XclPCNumGroupInfo::XclPCNumGroupInfo() :
+ mnFlags( EXC_SXNUMGROUP_AUTOMIN | EXC_SXNUMGROUP_AUTOMAX )
+{
+ SetNumType();
+}
+
+void XclPCNumGroupInfo::SetNumType()
+{
+ SetXclDataType( EXC_SXNUMGROUP_TYPE_NUM );
+}
+
+sal_Int32 XclPCNumGroupInfo::GetScDateType() const
+{
+ sal_Int32 nScType = 0;
+ switch( GetXclDataType() )
+ {
+ case EXC_SXNUMGROUP_TYPE_SEC: nScType = ScDPGroupBy::SECONDS; break;
+ case EXC_SXNUMGROUP_TYPE_MIN: nScType = ScDPGroupBy::MINUTES; break;
+ case EXC_SXNUMGROUP_TYPE_HOUR: nScType = ScDPGroupBy::HOURS; break;
+ case EXC_SXNUMGROUP_TYPE_DAY: nScType = ScDPGroupBy::DAYS; break;
+ case EXC_SXNUMGROUP_TYPE_MONTH: nScType = ScDPGroupBy::MONTHS; break;
+ case EXC_SXNUMGROUP_TYPE_QUART: nScType = ScDPGroupBy::QUARTERS; break;
+ case EXC_SXNUMGROUP_TYPE_YEAR: nScType = ScDPGroupBy::YEARS; break;
+ default: SAL_WARN("sc.filter", "XclPCNumGroupInfo::GetScDateType - unexpected date type " << GetXclDataType() );
+ }
+ return nScType;
+}
+
+void XclPCNumGroupInfo::SetScDateType( sal_Int32 nScType )
+{
+ sal_uInt16 nXclType = EXC_SXNUMGROUP_TYPE_NUM;
+ switch( nScType )
+ {
+ case ScDPGroupBy::SECONDS: nXclType = EXC_SXNUMGROUP_TYPE_SEC; break;
+ case ScDPGroupBy::MINUTES: nXclType = EXC_SXNUMGROUP_TYPE_MIN; break;
+ case ScDPGroupBy::HOURS: nXclType = EXC_SXNUMGROUP_TYPE_HOUR; break;
+ case ScDPGroupBy::DAYS: nXclType = EXC_SXNUMGROUP_TYPE_DAY; break;
+ case ScDPGroupBy::MONTHS: nXclType = EXC_SXNUMGROUP_TYPE_MONTH; break;
+ case ScDPGroupBy::QUARTERS: nXclType = EXC_SXNUMGROUP_TYPE_QUART; break;
+ case ScDPGroupBy::YEARS: nXclType = EXC_SXNUMGROUP_TYPE_YEAR; break;
+ default:
+ SAL_INFO("sc.filter", "unexpected date type " << nScType);
+ }
+ SetXclDataType( nXclType );
+}
+
+sal_uInt16 XclPCNumGroupInfo::GetXclDataType() const
+{
+ return ::extract_value< sal_uInt16 >( mnFlags, 2, 4 );
+}
+
+void XclPCNumGroupInfo::SetXclDataType( sal_uInt16 nXclType )
+{
+ ::insert_value( mnFlags, nXclType, 2, 4 );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCNumGroupInfo& rInfo )
+{
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCNumGroupInfo& rInfo )
+{
+ return rStrm << rInfo.mnFlags;
+}
+
+// Base class for pivot cache fields ==========================================
+
+XclPCField::XclPCField( XclPCFieldType eFieldType, sal_uInt16 nFieldIdx ) :
+ meFieldType( eFieldType ),
+ mnFieldIdx( nFieldIdx )
+{
+}
+
+XclPCField::~XclPCField()
+{
+}
+
+bool XclPCField::IsSupportedField() const
+{
+ return (meFieldType != EXC_PCFIELD_CALCED) && (meFieldType != EXC_PCFIELD_UNKNOWN);
+}
+
+bool XclPCField::IsStandardField() const
+{
+ return meFieldType == EXC_PCFIELD_STANDARD;
+}
+
+bool XclPCField::IsStdGroupField() const
+{
+ return meFieldType == EXC_PCFIELD_STDGROUP;
+}
+
+bool XclPCField::IsNumGroupField() const
+{
+ return meFieldType == EXC_PCFIELD_NUMGROUP;
+}
+
+bool XclPCField::IsDateGroupField() const
+{
+ return (meFieldType == EXC_PCFIELD_DATEGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD);
+}
+
+bool XclPCField::IsGroupField() const
+{
+ return IsStdGroupField() || IsNumGroupField() || IsDateGroupField();
+}
+
+bool XclPCField::IsGroupBaseField() const
+{
+ return ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+}
+
+bool XclPCField::IsGroupChildField() const
+{
+ return (meFieldType == EXC_PCFIELD_STDGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD);
+}
+
+bool XclPCField::HasOrigItems() const
+{
+ return IsSupportedField() && ((maFieldInfo.mnOrigItems > 0) || HasPostponedItems());
+}
+
+bool XclPCField::HasInlineItems() const
+{
+ return (IsStandardField() || IsGroupField()) && ((maFieldInfo.mnGroupItems > 0) || (maFieldInfo.mnOrigItems > 0));
+}
+
+bool XclPCField::HasPostponedItems() const
+{
+ return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
+}
+
+bool XclPCField::Has16BitIndexes() const
+{
+ return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT );
+}
+
+// Pivot cache settings =======================================================
+
+/** Contains data for a pivot cache (SXDB record). */
+XclPCInfo::XclPCInfo() :
+ mnSrcRecs( 0 ),
+ mnStrmId( 0xFFFF ),
+ mnFlags( EXC_SXDB_DEFAULTFLAGS ),
+ mnBlockRecs( EXC_SXDB_BLOCKRECS ),
+ mnStdFields( 0 ),
+ mnTotalFields( 0 ),
+ mnSrcType( EXC_SXDB_SRC_SHEET )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCInfo& rInfo )
+{
+ rInfo.mnSrcRecs = rStrm.ReaduInt32();
+ rInfo.mnStrmId = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnBlockRecs = rStrm.ReaduInt16();
+ rInfo.mnStdFields = rStrm.ReaduInt16();
+ rInfo.mnTotalFields = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ rInfo.mnSrcType = rStrm.ReaduInt16();
+ rInfo.maUserName = rStrm.ReadUniString();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnSrcRecs
+ << rInfo.mnStrmId
+ << rInfo.mnFlags
+ << rInfo.mnBlockRecs
+ << rInfo.mnStdFields
+ << rInfo.mnTotalFields
+ << sal_uInt16( 0 )
+ << rInfo.mnSrcType
+ << XclExpString( rInfo.maUserName );
+}
+
+// Pivot table
+
+// cached name ================================================================
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTCachedName& rCachedName )
+{
+ sal_uInt16 nStrLen;
+ nStrLen = rStrm.ReaduInt16();
+ rCachedName.mbUseCache = nStrLen == EXC_PT_NOSTRING;
+ if( rCachedName.mbUseCache )
+ rCachedName.maName.clear();
+ else
+ rCachedName.maName = rStrm.ReadUniString( nStrLen );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTCachedName& rCachedName )
+{
+ if( rCachedName.mbUseCache )
+ rStrm << EXC_PT_NOSTRING;
+ else
+ rStrm << XclExpString( rCachedName.maName, XclStrFlags::NONE, EXC_PT_MAXSTRLEN );
+ return rStrm;
+}
+
+const OUString* XclPTVisNameInfo::GetVisName() const
+{
+ return HasVisName() ? &maVisName.maName : nullptr;
+}
+
+void XclPTVisNameInfo::SetVisName( const OUString& rName )
+{
+ maVisName.maName = rName;
+ maVisName.mbUseCache = rName.isEmpty();
+}
+
+// Field item settings ========================================================
+
+XclPTItemInfo::XclPTItemInfo() :
+ mnType( EXC_SXVI_TYPE_DATA ),
+ mnFlags( EXC_SXVI_DEFAULTFLAGS ),
+ mnCacheIdx( EXC_SXVI_DEFAULT_CACHE )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTItemInfo& rInfo )
+{
+ rInfo.mnType = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnCacheIdx = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTItemInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnType
+ << rInfo.mnFlags
+ << rInfo.mnCacheIdx
+ << rInfo.maVisName;
+}
+
+// General field settings =====================================================
+
+XclPTFieldInfo::XclPTFieldInfo() :
+ mnAxes( EXC_SXVD_AXIS_NONE ),
+ mnSubtCount( 1 ),
+ mnSubtotals( EXC_SXVD_SUBT_DEFAULT ),
+ mnItemCount( 0 ),
+ mnCacheIdx( EXC_SXVD_DEFAULT_CACHE )
+{
+}
+
+DataPilotFieldOrientation XclPTFieldInfo::GetApiOrient( sal_uInt16 nMask ) const
+{
+ using namespace ::com::sun::star::sheet;
+ DataPilotFieldOrientation eOrient = DataPilotFieldOrientation_HIDDEN;
+ sal_uInt16 nUsedAxes = mnAxes & nMask;
+ if( nUsedAxes & EXC_SXVD_AXIS_ROW )
+ eOrient = DataPilotFieldOrientation_ROW;
+ else if( nUsedAxes & EXC_SXVD_AXIS_COL )
+ eOrient = DataPilotFieldOrientation_COLUMN;
+ else if( nUsedAxes & EXC_SXVD_AXIS_PAGE )
+ eOrient = DataPilotFieldOrientation_PAGE;
+ else if( nUsedAxes & EXC_SXVD_AXIS_DATA )
+ eOrient = DataPilotFieldOrientation_DATA;
+ return eOrient;
+}
+
+void XclPTFieldInfo::AddApiOrient( DataPilotFieldOrientation eOrient )
+{
+ using namespace ::com::sun::star::sheet;
+ switch( eOrient )
+ {
+ case DataPilotFieldOrientation_ROW: mnAxes |= EXC_SXVD_AXIS_ROW; break;
+ case DataPilotFieldOrientation_COLUMN: mnAxes |= EXC_SXVD_AXIS_COL; break;
+ case DataPilotFieldOrientation_PAGE: mnAxes |= EXC_SXVD_AXIS_PAGE; break;
+ case DataPilotFieldOrientation_DATA: mnAxes |= EXC_SXVD_AXIS_DATA; break;
+ default:;
+ }
+}
+
+//TODO: should be a Sequence<GeneralFunction> in ScDPSaveData
+void XclPTFieldInfo::GetSubtotals( XclPTSubtotalVec& rSubtotals ) const
+{
+ rSubtotals.clear();
+ rSubtotals.reserve( 16 );
+
+ if( mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) rSubtotals.push_back( ScGeneralFunction::AUTO );
+ if( mnSubtotals & EXC_SXVD_SUBT_SUM ) rSubtotals.push_back( ScGeneralFunction::SUM );
+ if( mnSubtotals & EXC_SXVD_SUBT_COUNT ) rSubtotals.push_back( ScGeneralFunction::COUNT );
+ if( mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) rSubtotals.push_back( ScGeneralFunction::AVERAGE );
+ if( mnSubtotals & EXC_SXVD_SUBT_MAX ) rSubtotals.push_back( ScGeneralFunction::MAX );
+ if( mnSubtotals & EXC_SXVD_SUBT_MIN ) rSubtotals.push_back( ScGeneralFunction::MIN );
+ if( mnSubtotals & EXC_SXVD_SUBT_PROD ) rSubtotals.push_back( ScGeneralFunction::PRODUCT );
+ if( mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) rSubtotals.push_back( ScGeneralFunction::COUNTNUMS );
+ if( mnSubtotals & EXC_SXVD_SUBT_STDDEV ) rSubtotals.push_back( ScGeneralFunction::STDEV );
+ if( mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) rSubtotals.push_back( ScGeneralFunction::STDEVP );
+ if( mnSubtotals & EXC_SXVD_SUBT_VAR ) rSubtotals.push_back( ScGeneralFunction::VAR );
+ if( mnSubtotals & EXC_SXVD_SUBT_VARP ) rSubtotals.push_back( ScGeneralFunction::VARP );
+}
+
+void XclPTFieldInfo::SetSubtotals( const XclPTSubtotalVec& rSubtotals )
+{
+ mnSubtotals = EXC_SXVD_SUBT_NONE;
+ for( const auto& rSubtotal : rSubtotals )
+ {
+ switch( rSubtotal )
+ {
+ case ScGeneralFunction::AUTO: mnSubtotals |= EXC_SXVD_SUBT_DEFAULT; break;
+ case ScGeneralFunction::SUM: mnSubtotals |= EXC_SXVD_SUBT_SUM; break;
+ case ScGeneralFunction::COUNT: mnSubtotals |= EXC_SXVD_SUBT_COUNT; break;
+ case ScGeneralFunction::AVERAGE: mnSubtotals |= EXC_SXVD_SUBT_AVERAGE; break;
+ case ScGeneralFunction::MAX: mnSubtotals |= EXC_SXVD_SUBT_MAX; break;
+ case ScGeneralFunction::MIN: mnSubtotals |= EXC_SXVD_SUBT_MIN; break;
+ case ScGeneralFunction::PRODUCT: mnSubtotals |= EXC_SXVD_SUBT_PROD; break;
+ case ScGeneralFunction::COUNTNUMS: mnSubtotals |= EXC_SXVD_SUBT_COUNTNUM; break;
+ case ScGeneralFunction::STDEV: mnSubtotals |= EXC_SXVD_SUBT_STDDEV; break;
+ case ScGeneralFunction::STDEVP: mnSubtotals |= EXC_SXVD_SUBT_STDDEVP; break;
+ case ScGeneralFunction::VAR: mnSubtotals |= EXC_SXVD_SUBT_VAR; break;
+ case ScGeneralFunction::VARP: mnSubtotals |= EXC_SXVD_SUBT_VARP; break;
+ default: break;
+ }
+ }
+
+ mnSubtCount = 0;
+ for( sal_uInt16 nMask = 0x8000; nMask; nMask >>= 1 )
+ if( mnSubtotals & nMask )
+ ++mnSubtCount;
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldInfo& rInfo )
+{
+ // rInfo.mnCacheIdx is not part of the SXVD record
+ rInfo.mnAxes = rStrm.ReaduInt16();
+ rInfo.mnSubtCount = rStrm.ReaduInt16();
+ rInfo.mnSubtotals = rStrm.ReaduInt16();
+ rInfo.mnItemCount = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo )
+{
+ // rInfo.mnCacheIdx is not part of the SXVD record
+ return rStrm
+ << rInfo.mnAxes
+ << rInfo.mnSubtCount
+ << rInfo.mnSubtotals
+ << rInfo.mnItemCount
+ << rInfo.maVisName;
+}
+
+// Extended field settings ====================================================
+
+XclPTFieldExtInfo::XclPTFieldExtInfo() :
+ mnFlags( EXC_SXVDEX_DEFAULTFLAGS ),
+ mnSortField( EXC_SXVDEX_SORT_OWN ),
+ mnShowField( EXC_SXVDEX_SHOW_NONE ),
+ mnNumFmt(0)
+{
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiSortMode() const
+{
+ sal_Int32 nSortMode = ScDPSortMode::MANUAL;
+ if( ::get_flag( mnFlags, EXC_SXVDEX_SORT ) )
+ nSortMode = (mnSortField == EXC_SXVDEX_SORT_OWN) ? ScDPSortMode::NAME : ScDPSortMode::DATA;
+ return nSortMode;
+}
+
+void XclPTFieldExtInfo::SetApiSortMode( sal_Int32 nSortMode )
+{
+ bool bSort = (nSortMode == ScDPSortMode::NAME) || (nSortMode == ScDPSortMode::DATA);
+ ::set_flag( mnFlags, EXC_SXVDEX_SORT, bSort );
+ if( nSortMode == ScDPSortMode::NAME )
+ mnSortField = EXC_SXVDEX_SORT_OWN; // otherwise sort field has to be set by caller
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiAutoShowMode() const
+{
+ return ::get_flagvalue( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC,
+ ScDPShowItemsMode::FROM_TOP, ScDPShowItemsMode::FROM_BOTTOM );
+}
+
+void XclPTFieldExtInfo::SetApiAutoShowMode( sal_Int32 nShowMode )
+{
+ ::set_flag( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC, nShowMode == ScDPShowItemsMode::FROM_TOP );
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiAutoShowCount() const
+{
+ return ::extract_value< sal_Int32 >( mnFlags, 24, 8 );
+}
+
+void XclPTFieldExtInfo::SetApiAutoShowCount( sal_Int32 nShowCount )
+{
+ ::insert_value( mnFlags, limit_cast< sal_uInt8 >( nShowCount ), 24, 8 );
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiLayoutMode() const
+{
+ sal_Int32 nLayoutMode = ScDPLayoutMode::TABULAR_LAYOUT;
+ if( ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT ) )
+ nLayoutMode = ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP ) ?
+ ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP : ScDPLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ return nLayoutMode;
+}
+
+void XclPTFieldExtInfo::SetApiLayoutMode( sal_Int32 nLayoutMode )
+{
+ ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT, nLayoutMode != ScDPLayoutMode::TABULAR_LAYOUT );
+ ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP, nLayoutMode == ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo )
+{
+ sal_uInt8 nNameLen = 0;
+ rInfo.mnFlags = rStrm.ReaduInt32();
+ rInfo.mnSortField = rStrm.ReaduInt16();
+ rInfo.mnShowField = rStrm.ReaduInt16();
+ rInfo.mnNumFmt = rStrm.ReaduInt16();
+ nNameLen = rStrm.ReaduInt8();
+
+ rStrm.Ignore(10);
+ if (nNameLen != 0xFF)
+ // Custom field total name is used. Pick it up.
+ rInfo.mpFieldTotalName = rStrm.ReadUniString(nNameLen, 0);
+
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo )
+{
+ rStrm << rInfo.mnFlags
+ << rInfo.mnSortField
+ << rInfo.mnShowField
+ << EXC_SXVDEX_FORMAT_NONE;
+
+ if (rInfo.mpFieldTotalName && !rInfo.mpFieldTotalName->isEmpty())
+ {
+ OUString aFinalName = *rInfo.mpFieldTotalName;
+ if (aFinalName.getLength() >= 254)
+ aFinalName = aFinalName.copy(0, 254);
+ sal_uInt8 nNameLen = static_cast<sal_uInt8>(aFinalName.getLength());
+ rStrm << nNameLen;
+ rStrm.WriteZeroBytes(10);
+ rStrm << XclExpString(aFinalName, XclStrFlags::NoHeader);
+ }
+ else
+ {
+ rStrm << sal_uInt16(0xFFFF);
+ rStrm.WriteZeroBytes(8);
+ }
+ return rStrm;
+}
+
+// Page field settings ========================================================
+
+XclPTPageFieldInfo::XclPTPageFieldInfo() :
+ mnField( 0 ),
+ mnSelItem( EXC_SXPI_ALLITEMS ),
+ mnObjId( 0xFFFF )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTPageFieldInfo& rInfo )
+{
+ rInfo.mnField = rStrm.ReaduInt16();
+ rInfo.mnSelItem = rStrm.ReaduInt16();
+ rInfo.mnObjId = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTPageFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnField
+ << rInfo.mnSelItem
+ << rInfo.mnObjId;
+}
+
+// Data field settings ========================================================
+
+XclPTDataFieldInfo::XclPTDataFieldInfo() :
+ mnField( 0 ),
+ mnAggFunc( EXC_SXDI_FUNC_SUM ),
+ mnRefType( EXC_SXDI_REF_NORMAL ),
+ mnRefField( 0 ),
+ mnRefItem( 0 ),
+ mnNumFmt( 0 )
+{
+}
+
+ScGeneralFunction XclPTDataFieldInfo::GetApiAggFunc() const
+{
+ ScGeneralFunction eAggFunc;
+ switch( mnAggFunc )
+ {
+ case EXC_SXDI_FUNC_SUM: eAggFunc = ScGeneralFunction::SUM; break;
+ case EXC_SXDI_FUNC_COUNT: eAggFunc = ScGeneralFunction::COUNT; break;
+ case EXC_SXDI_FUNC_AVERAGE: eAggFunc = ScGeneralFunction::AVERAGE; break;
+ case EXC_SXDI_FUNC_MAX: eAggFunc = ScGeneralFunction::MAX; break;
+ case EXC_SXDI_FUNC_MIN: eAggFunc = ScGeneralFunction::MIN; break;
+ case EXC_SXDI_FUNC_PRODUCT: eAggFunc = ScGeneralFunction::PRODUCT; break;
+ case EXC_SXDI_FUNC_COUNTNUM: eAggFunc = ScGeneralFunction::COUNTNUMS; break;
+ case EXC_SXDI_FUNC_STDDEV: eAggFunc = ScGeneralFunction::STDEV; break;
+ case EXC_SXDI_FUNC_STDDEVP: eAggFunc = ScGeneralFunction::STDEVP; break;
+ case EXC_SXDI_FUNC_VAR: eAggFunc = ScGeneralFunction::VAR; break;
+ case EXC_SXDI_FUNC_VARP: eAggFunc = ScGeneralFunction::VARP; break;
+ default: eAggFunc = ScGeneralFunction::SUM;
+ }
+ return eAggFunc;
+}
+
+void XclPTDataFieldInfo::SetApiAggFunc( ScGeneralFunction eAggFunc )
+{
+ switch( eAggFunc )
+ {
+ case ScGeneralFunction::SUM: mnAggFunc = EXC_SXDI_FUNC_SUM; break;
+ case ScGeneralFunction::COUNT: mnAggFunc = EXC_SXDI_FUNC_COUNT; break;
+ case ScGeneralFunction::AVERAGE: mnAggFunc = EXC_SXDI_FUNC_AVERAGE; break;
+ case ScGeneralFunction::MAX: mnAggFunc = EXC_SXDI_FUNC_MAX; break;
+ case ScGeneralFunction::MIN: mnAggFunc = EXC_SXDI_FUNC_MIN; break;
+ case ScGeneralFunction::PRODUCT: mnAggFunc = EXC_SXDI_FUNC_PRODUCT; break;
+ case ScGeneralFunction::COUNTNUMS: mnAggFunc = EXC_SXDI_FUNC_COUNTNUM; break;
+ case ScGeneralFunction::STDEV: mnAggFunc = EXC_SXDI_FUNC_STDDEV; break;
+ case ScGeneralFunction::STDEVP: mnAggFunc = EXC_SXDI_FUNC_STDDEVP; break;
+ case ScGeneralFunction::VAR: mnAggFunc = EXC_SXDI_FUNC_VAR; break;
+ case ScGeneralFunction::VARP: mnAggFunc = EXC_SXDI_FUNC_VARP; break;
+ default: mnAggFunc = EXC_SXDI_FUNC_SUM;
+ }
+}
+
+sal_Int32 XclPTDataFieldInfo::GetApiRefType() const
+{
+ namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType;
+ sal_Int32 nRefType;
+ switch( mnRefType )
+ {
+ case EXC_SXDI_REF_DIFF: nRefType = ScDPRefType::ITEM_DIFFERENCE; break;
+ case EXC_SXDI_REF_PERC: nRefType = ScDPRefType::ITEM_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_DIFF: nRefType = ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE; break;
+ case EXC_SXDI_REF_RUN_TOTAL: nRefType = ScDPRefType::RUNNING_TOTAL; break;
+ case EXC_SXDI_REF_PERC_ROW: nRefType = ScDPRefType::ROW_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_COL: nRefType = ScDPRefType::COLUMN_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_TOTAL: nRefType = ScDPRefType::TOTAL_PERCENTAGE; break;
+ case EXC_SXDI_REF_INDEX: nRefType = ScDPRefType::INDEX; break;
+ default: nRefType = ScDPRefType::NONE;
+ }
+ return nRefType;
+}
+
+void XclPTDataFieldInfo::SetApiRefType( sal_Int32 nRefType )
+{
+ namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType;
+ switch( nRefType )
+ {
+ case ScDPRefType::ITEM_DIFFERENCE: mnRefType = EXC_SXDI_REF_DIFF; break;
+ case ScDPRefType::ITEM_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC; break;
+ case ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE: mnRefType = EXC_SXDI_REF_PERC_DIFF; break;
+ case ScDPRefType::RUNNING_TOTAL: mnRefType = EXC_SXDI_REF_RUN_TOTAL; break;
+ case ScDPRefType::ROW_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_ROW; break;
+ case ScDPRefType::COLUMN_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_COL; break;
+ case ScDPRefType::TOTAL_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_TOTAL;break;
+ case ScDPRefType::INDEX: mnRefType = EXC_SXDI_REF_INDEX; break;
+ default: mnRefType = EXC_SXDI_REF_NORMAL;
+ }
+}
+
+sal_Int32 XclPTDataFieldInfo::GetApiRefItemType() const
+{
+ sal_Int32 nRefItemType;
+ switch( mnRefItem )
+ {
+ case EXC_SXDI_PREVITEM: nRefItemType = ScDPRefItemType::PREVIOUS; break;
+ case EXC_SXDI_NEXTITEM: nRefItemType = ScDPRefItemType::NEXT; break;
+ default: nRefItemType = ScDPRefItemType::NAMED;
+ }
+ return nRefItemType;
+}
+
+void XclPTDataFieldInfo::SetApiRefItemType( sal_Int32 nRefItemType )
+{
+ switch( nRefItemType )
+ {
+ case ScDPRefItemType::PREVIOUS: mnRefItem = EXC_SXDI_PREVITEM; break;
+ case ScDPRefItemType::NEXT: mnRefItem = EXC_SXDI_NEXTITEM; break;
+ // nothing for named item reference
+ }
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTDataFieldInfo& rInfo )
+{
+ rInfo.mnField = rStrm.ReaduInt16();
+ rInfo.mnAggFunc = rStrm.ReaduInt16();
+ rInfo.mnRefType = rStrm.ReaduInt16();
+ rInfo.mnRefField = rStrm.ReaduInt16();
+ rInfo.mnRefItem = rStrm.ReaduInt16();
+ rInfo.mnNumFmt = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTDataFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnField
+ << rInfo.mnAggFunc
+ << rInfo.mnRefType
+ << rInfo.mnRefField
+ << rInfo.mnRefItem
+ << rInfo.mnNumFmt
+ << rInfo.maVisName;
+}
+
+// Pivot table settings =======================================================
+
+XclPTInfo::XclPTInfo() :
+ mnFirstHeadRow( 0 ),
+ mnCacheIdx( 0xFFFF ),
+ mnDataAxis( EXC_SXVD_AXIS_NONE ),
+ mnDataPos( EXC_SXVIEW_DATALAST ),
+ mnFields( 0 ),
+ mnRowFields( 0 ),
+ mnColFields( 0 ),
+ mnPageFields( 0 ),
+ mnDataFields( 0 ),
+ mnDataRows( 0 ),
+ mnDataCols( 0 ),
+ mnFlags( EXC_SXVIEW_DEFAULTFLAGS ),
+ mnAutoFmtIdx( EXC_SXVIEW_AUTOFMT )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTInfo& rInfo )
+{
+ sal_uInt16 nTabLen, nDataLen;
+
+ rStrm >> rInfo.maOutXclRange;
+ rInfo.mnFirstHeadRow = rStrm.ReaduInt16();
+ rStrm >> rInfo.maDataXclPos;
+ rInfo.mnCacheIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ rInfo.mnDataAxis = rStrm.ReaduInt16();
+ rInfo.mnDataPos = rStrm.ReaduInt16();
+ rInfo.mnFields = rStrm.ReaduInt16();
+ rInfo.mnRowFields = rStrm.ReaduInt16();
+ rInfo.mnColFields = rStrm.ReaduInt16();
+ rInfo.mnPageFields = rStrm.ReaduInt16();
+ rInfo.mnDataFields = rStrm.ReaduInt16();
+ rInfo.mnDataRows = rStrm.ReaduInt16();
+ rInfo.mnDataCols = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnAutoFmtIdx = rStrm.ReaduInt16();
+ nTabLen = rStrm.ReaduInt16();
+ nDataLen = rStrm.ReaduInt16();
+ rInfo.maTableName = rStrm.ReadUniString( nTabLen );
+ rInfo.maDataName = rStrm.ReadUniString( nDataLen );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTInfo& rInfo )
+{
+ XclExpString aXclTableName( rInfo.maTableName );
+ XclExpString aXclDataName( rInfo.maDataName );
+
+ rStrm << rInfo.maOutXclRange
+ << rInfo.mnFirstHeadRow
+ << rInfo.maDataXclPos
+ << rInfo.mnCacheIdx
+ << sal_uInt16( 0 )
+ << rInfo.mnDataAxis << rInfo.mnDataPos
+ << rInfo.mnFields
+ << rInfo.mnRowFields << rInfo.mnColFields
+ << rInfo.mnPageFields << rInfo.mnDataFields
+ << rInfo.mnDataRows << rInfo.mnDataCols
+ << rInfo.mnFlags
+ << rInfo.mnAutoFmtIdx
+ << aXclTableName.Len() << aXclDataName.Len();
+ aXclTableName.WriteFlagField( rStrm );
+ aXclTableName.WriteBuffer( rStrm );
+ aXclDataName.WriteFlagField( rStrm );
+ aXclDataName.WriteBuffer( rStrm );
+ return rStrm;
+}
+
+// Extended pivot table settings ==============================================
+
+XclPTExtInfo::XclPTExtInfo() :
+ mnSxformulaRecs( 0 ),
+ mnSxselectRecs( 0 ),
+ mnPagePerRow( 0 ),
+ mnPagePerCol( 0 ),
+ mnFlags( EXC_SXEX_DEFAULTFLAGS )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTExtInfo& rInfo )
+{
+ rInfo.mnSxformulaRecs = rStrm.ReaduInt16();
+ rStrm.Ignore( 6 );
+ rInfo.mnSxselectRecs = rStrm.ReaduInt16();
+ rInfo.mnPagePerRow = rStrm.ReaduInt16();
+ rInfo.mnPagePerCol = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt32();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnSxformulaRecs
+ << EXC_PT_NOSTRING // length of alt. error text
+ << EXC_PT_NOSTRING // length of alt. empty text
+ << EXC_PT_NOSTRING // length of tag
+ << rInfo.mnSxselectRecs
+ << rInfo.mnPagePerRow
+ << rInfo.mnPagePerCol
+ << rInfo.mnFlags
+ << EXC_PT_NOSTRING // length of page field style name
+ << EXC_PT_NOSTRING // length of table style name
+ << EXC_PT_NOSTRING; // length of vacate style name
+}
+
+// Pivot table autoformat settings ============================================
+
+/**
+classic : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+default : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+report01 : 10 08 02 00 00 00 00 00 20 00 00 00 00 10 00 00 00
+report02 : 10 08 02 00 00 00 00 00 20 00 00 00 01 10 00 00 00
+report03 : 10 08 02 00 00 00 00 00 20 00 00 00 02 10 00 00 00
+report04 : 10 08 02 00 00 00 00 00 20 00 00 00 03 10 00 00 00
+report05 : 10 08 02 00 00 00 00 00 20 00 00 00 04 10 00 00 00
+report06 : 10 08 02 00 00 00 00 00 20 00 00 00 05 10 00 00 00
+report07 : 10 08 02 00 00 00 00 00 20 00 00 00 06 10 00 00 00
+report08 : 10 08 02 00 00 00 00 00 20 00 00 00 07 10 00 00 00
+report09 : 10 08 02 00 00 00 00 00 20 00 00 00 08 10 00 00 00
+report10 : 10 08 02 00 00 00 00 00 20 00 00 00 09 10 00 00 00
+table01 : 10 08 00 00 00 00 00 00 20 00 00 00 0a 10 00 00 00
+table02 : 10 08 00 00 00 00 00 00 20 00 00 00 0b 10 00 00 00
+table03 : 10 08 00 00 00 00 00 00 20 00 00 00 0c 10 00 00 00
+table04 : 10 08 00 00 00 00 00 00 20 00 00 00 0d 10 00 00 00
+table05 : 10 08 00 00 00 00 00 00 20 00 00 00 0e 10 00 00 00
+table06 : 10 08 00 00 00 00 00 00 20 00 00 00 0f 10 00 00 00
+table07 : 10 08 00 00 00 00 00 00 20 00 00 00 10 10 00 00 00
+table08 : 10 08 00 00 00 00 00 00 20 00 00 00 11 10 00 00 00
+table09 : 10 08 00 00 00 00 00 00 20 00 00 00 12 10 00 00 00
+table10 : 10 08 00 00 00 00 00 00 20 00 00 00 13 10 00 00 00
+none : 10 08 00 00 00 00 00 00 20 00 00 00 15 10 00 00 00
+**/
+
+XclPTViewEx9Info::XclPTViewEx9Info() :
+ mbReport( 0 ),
+ mnAutoFormat( 0 ),
+ mnGridLayout( 0x10 )
+{
+}
+
+void XclPTViewEx9Info::Init( const ScDPObject& rDPObj )
+{
+ if( rDPObj.GetHeaderLayout() )
+ {
+ mbReport = 0;
+ mnAutoFormat = 1;
+ mnGridLayout = 0;
+ }
+ else
+ {
+ // Report1 for now
+ // TODO : sync with autoformat indices
+ mbReport = 2;
+ mnAutoFormat = 1;
+ mnGridLayout = 0x10;
+ }
+
+ const ScDPSaveData* pData = rDPObj.GetSaveData();
+ if (pData)
+ {
+ const std::optional<OUString> & pGrandTotal = pData->GetGrandTotalName();
+ if (pGrandTotal)
+ maGrandTotalName = *pGrandTotal;
+ }
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo )
+{
+ rStrm.Ignore( 2 );
+ rInfo.mbReport = rStrm.ReaduInt32(); /// 2 for report* fmts ?
+ rStrm.Ignore( 6 );
+ rInfo.mnAutoFormat = rStrm.ReaduInt8();
+ rInfo.mnGridLayout = rStrm.ReaduInt8();
+ rInfo.maGrandTotalName = rStrm.ReadUniString();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo )
+{
+ return rStrm
+ << EXC_PT_AUTOFMT_HEADER
+ << rInfo.mbReport
+ << EXC_PT_AUTOFMT_ZERO
+ << EXC_PT_AUTOFMT_FLAGS
+ << rInfo.mnAutoFormat
+ << rInfo.mnGridLayout
+ << XclExpString(rInfo.maGrandTotalName, XclStrFlags::NONE, EXC_PT_MAXSTRLEN);
+}
+
+XclPTAddl::XclPTAddl() :
+ mbCompactMode(false)
+{
+}
+
+XclImpStream& operator>>(XclImpStream& rStrm, XclPTAddl& rInfo)
+{
+ rStrm.Ignore(4);
+ sal_uInt8 sxc = rStrm.ReaduInt8();
+ sal_uInt8 sxd = rStrm.ReaduInt8();
+ if(sxc == 0x00 && sxd == 0x19) // SxcView / sxdVer12Info
+ {
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+ rInfo.mbCompactMode = ((nFlags & 0x00000008) != 0);
+ }
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlroot.cxx b/sc/source/filter/excel/xlroot.cxx
new file mode 100644
index 0000000000..c26529e7fe
--- /dev/null
+++ b/sc/source/filter/excel/xlroot.cxx
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+#include <xlroot.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sot/storage.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/languageoptions.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <vcl/font.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <editeng/editstat.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <editutil.hxx>
+#include <drwlayer.hxx>
+#include <scextopt.hxx>
+#include <patattr.hxx>
+#include <fapihelper.hxx>
+#include <xlconst.hxx>
+#include <xlstyle.hxx>
+#include <xlchart.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/useroptions.hxx>
+#include <root.hxx>
+
+namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::awt::XDevice;
+using ::com::sun::star::awt::DeviceInfo;
+using ::com::sun::star::frame::XFrame;
+
+using namespace ::com::sun::star;
+
+// Global data ================================================================
+
+#ifdef DBG_UTIL
+XclDebugObjCounter::~XclDebugObjCounter()
+{
+ OSL_ENSURE( mnObjCnt == 0, "XclDebugObjCounter::~XclDebugObjCounter - wrong root object count" );
+}
+#endif
+
+XclRootData::XclRootData( XclBiff eBiff, SfxMedium& rMedium,
+ tools::SvRef<SotStorage> xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc, bool bExport ) :
+ meBiff( eBiff ),
+ meOutput( EXC_OUTPUT_BINARY ),
+ mrMedium( rMedium ),
+ mxRootStrg(std::move( xRootStrg )),
+ mrDoc( rDoc ),
+ meTextEnc( eTextEnc ),
+ meSysLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ meDocLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ meUILang( Application::GetSettings().GetUILanguageTag().getLanguageType() ),
+ mnDefApiScript( ApiScriptType::LATIN ),
+ maScMaxPos( mrDoc.MaxCol(), mrDoc.MaxRow(), MAXTAB ),
+ maXclMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
+ maMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
+ mxFontPropSetHlp( std::make_shared<XclFontPropSetHelper>() ),
+ mxChPropSetHlp( std::make_shared<XclChPropSetHelper>() ),
+ mxRD( std::make_shared<RootData>() ),
+ mfScreenPixelX( 50.0 ),
+ mfScreenPixelY( 50.0 ),
+ mnCharWidth( 110 ),
+ mnSpaceWidth(45),
+ mnScTab( 0 ),
+ mbExport( bExport )
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ maUserName = SvtUserOptions().GetLastName();
+ if (maUserName.isEmpty())
+ maUserName = "Calc";
+
+ switch( ScGlobal::GetDefaultScriptType() )
+ {
+ case SvtScriptType::LATIN: mnDefApiScript = ApiScriptType::LATIN; break;
+ case SvtScriptType::ASIAN: mnDefApiScript = ApiScriptType::ASIAN; break;
+ case SvtScriptType::COMPLEX: mnDefApiScript = ApiScriptType::COMPLEX; break;
+ default: SAL_WARN( "sc", "XclRootData::XclRootData - unknown script type" );
+ }
+
+ // maximum cell position
+ switch( meBiff )
+ {
+ case EXC_BIFF2: maXclMaxPos.Set( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ); break;
+ case EXC_BIFF3: maXclMaxPos.Set( EXC_MAXCOL3, EXC_MAXROW3, EXC_MAXTAB3 ); break;
+ case EXC_BIFF4: maXclMaxPos.Set( EXC_MAXCOL4, EXC_MAXROW4, EXC_MAXTAB4 ); break;
+ case EXC_BIFF5: maXclMaxPos.Set( EXC_MAXCOL5, EXC_MAXROW5, EXC_MAXTAB5 ); break;
+ case EXC_BIFF8: maXclMaxPos.Set( EXC_MAXCOL8, EXC_MAXROW8, EXC_MAXTAB8 ); break;
+ default: DBG_ERROR_BIFF();
+ }
+ maMaxPos.SetCol( ::std::min( maScMaxPos.Col(), maXclMaxPos.Col() ) );
+ maMaxPos.SetRow( ::std::min( maScMaxPos.Row(), maXclMaxPos.Row() ) );
+ maMaxPos.SetTab( ::std::min( maScMaxPos.Tab(), maXclMaxPos.Tab() ) );
+
+ // document URL and path
+ if( const SfxStringItem* pItem = mrMedium.GetItemSet().GetItem( SID_FILE_NAME ) )
+ maDocUrl = pItem->GetValue();
+ maBasePath = maDocUrl.copy( 0, maDocUrl.lastIndexOf( '/' ) + 1 );
+
+ // extended document options - always own object, try to copy existing data from document
+ if( const ScExtDocOptions* pOldDocOpt = mrDoc.GetExtDocOptions() )
+ mxExtDocOpt = std::make_shared<ScExtDocOptions>( *pOldDocOpt );
+ else
+ mxExtDocOpt = std::make_shared<ScExtDocOptions>();
+
+ // screen pixel size
+ try
+ {
+ Reference< frame::XDesktop2 > xFramesSupp = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XFrame > xFrame( xFramesSupp->getActiveFrame(), UNO_SET_THROW );
+ Reference< XDevice > xDevice( xFrame->getContainerWindow(), UNO_QUERY_THROW );
+ DeviceInfo aDeviceInfo = xDevice->getInfo();
+ mfScreenPixelX = (aDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterX) : 50.0;
+ mfScreenPixelY = (aDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterY) : 50.0;
+ }
+ catch( const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "XclRootData::XclRootData - cannot get output device info");
+ }
+}
+
+XclRootData::~XclRootData()
+{
+}
+
+XclRoot::XclRoot( XclRootData& rRootData ) :
+ mrData( rRootData )
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ ++mrData.mnObjCnt;
+#endif
+
+ // filter tracer
+ mrData.mxTracer = std::make_shared<XclTracer>( GetDocUrl() );
+}
+
+XclRoot::XclRoot( const XclRoot& rRoot ) :
+ mrData( rRoot.mrData )
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ ++mrData.mnObjCnt;
+#endif
+}
+
+XclRoot::~XclRoot()
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ --mrData.mnObjCnt;
+#endif
+}
+
+XclRoot& XclRoot::operator=( const XclRoot& rRoot )
+{
+ (void)rRoot; // avoid compiler warning
+ // allowed for assignment in derived classes - but test if the same root data is used
+ OSL_ENSURE( &mrData == &rRoot.mrData, "XclRoot::operator= - incompatible root data" );
+ return *this;
+}
+
+void XclRoot::SetTextEncoding( rtl_TextEncoding eTextEnc )
+{
+ if( eTextEnc != RTL_TEXTENCODING_DONTKNOW )
+ mrData.meTextEnc = eTextEnc;
+}
+
+void XclRoot::SetCharWidth( const XclFontData& rFontData )
+{
+ mrData.mnCharWidth = 0;
+ bool bIsLOK = comphelper::LibreOfficeKit::isActive();
+ if( OutputDevice* pPrinter = GetPrinter( bIsLOK ) )
+ {
+ vcl::Font aFont( rFontData.maName, Size( 0, rFontData.mnHeight ) );
+ aFont.SetFamily( rFontData.GetScFamily( GetTextEncoding() ) );
+ aFont.SetCharSet( rFontData.GetFontEncoding() );
+ aFont.SetWeight( rFontData.GetScWeight() );
+ pPrinter->SetFont( aFont );
+ // Usually digits have the same width, but in some fonts they don't ...
+ // Match the import in sc/source/filter/oox/unitconverter.cxx
+ // UnitConverter::finalizeImport()
+ for (sal_Unicode cChar = '0'; cChar <= '9'; ++cChar)
+ mrData.mnCharWidth = std::max( pPrinter->GetTextWidth( OUString(cChar)), mrData.mnCharWidth);
+
+ // Set the width of space ' ' character.
+ mrData.mnSpaceWidth = pPrinter->GetTextWidth(OUString(' '));
+ }
+ if( mrData.mnCharWidth <= 0 )
+ {
+ // #i48717# Win98 with HP LaserJet returns 0
+ SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
+ mrData.mnCharWidth = 11 * rFontData.mnHeight / 20;
+ }
+ if (mrData.mnSpaceWidth <= 0)
+ {
+ SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
+ mrData.mnSpaceWidth = 45;
+ }
+}
+
+sal_Int32 XclRoot::GetHmmFromPixelX( double fPixelX ) const
+{
+ return static_cast< sal_Int32 >( fPixelX * mrData.mfScreenPixelX + 0.5 );
+}
+
+sal_Int32 XclRoot::GetHmmFromPixelY( double fPixelY ) const
+{
+ return static_cast< sal_Int32 >( fPixelY * mrData.mfScreenPixelY + 0.5 );
+}
+
+uno::Sequence< beans::NamedValue > XclRoot::RequestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const
+{
+ ::std::vector< OUString > aDefaultPasswords { XclRootData::gaDefPassword };
+ return ScfApiHelper::QueryEncryptionDataForMedium( mrData.mrMedium, rVerifier, &aDefaultPasswords );
+}
+
+bool XclRoot::HasVbaStorage() const
+{
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ return xRootStrg.is() && xRootStrg->IsContained( EXC_STORAGE_VBA_PROJECT );
+}
+
+tools::SvRef<SotStorage> XclRoot::OpenStorage( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName ) const
+{
+ return mrData.mbExport ?
+ ScfTools::OpenStorageWrite( xStrg, rStrgName ) :
+ ScfTools::OpenStorageRead( xStrg, rStrgName );
+}
+
+tools::SvRef<SotStorage> XclRoot::OpenStorage( const OUString& rStrgName ) const
+{
+ return OpenStorage( GetRootStorage(), rStrgName );
+}
+
+tools::SvRef<SotStorageStream> XclRoot::OpenStream( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName ) const
+{
+ return mrData.mbExport ?
+ ScfTools::OpenStorageStreamWrite( xStrg, rStrmName ) :
+ ScfTools::OpenStorageStreamRead( xStrg, rStrmName );
+}
+
+tools::SvRef<SotStorageStream> XclRoot::OpenStream( const OUString& rStrmName ) const
+{
+ return OpenStream( GetRootStorage(), rStrmName );
+}
+
+ScDocument& XclRoot::GetDoc() const
+{
+ return mrData.mrDoc;
+}
+
+ScDocShell* XclRoot::GetDocShell() const
+{
+ return GetDoc().GetDocumentShell();
+}
+
+ScModelObj* XclRoot::GetDocModelObj() const
+{
+ ScDocShell* pDocShell = GetDocShell();
+ return pDocShell ? pDocShell->GetModel() : nullptr;
+}
+
+OutputDevice* XclRoot::GetPrinter(bool bForceVirtDev) const
+{
+ return GetDoc().GetRefDevice(bForceVirtDev);
+}
+
+ScStyleSheetPool& XclRoot::GetStyleSheetPool() const
+{
+ return *GetDoc().GetStyleSheetPool();
+}
+
+ScRangeName& XclRoot::GetNamedRanges() const
+{
+ return *GetDoc().GetRangeName();
+}
+
+SdrPage* XclRoot::GetSdrPage( SCTAB nScTab ) const
+{
+ return ((nScTab >= 0) && GetDoc().GetDrawLayer()) ?
+ GetDoc().GetDrawLayer()->GetPage( static_cast< sal_uInt16 >( nScTab ) ) : nullptr;
+}
+
+SvNumberFormatter& XclRoot::GetFormatter() const
+{
+ return *GetDoc().GetFormatTable();
+}
+
+DateTime XclRoot::GetNullDate() const
+{
+ return GetFormatter().GetNullDate();
+}
+
+sal_uInt16 XclRoot::GetBaseYear() const
+{
+ // return 1904 for 1904-01-01, and 1900 for 1899-12-30
+ return (GetNullDate().GetYear() == 1904) ? 1904 : 1900;
+}
+
+const DateTime theOurCompatNullDate( Date( 30, 12, 1899 ));
+const DateTime theExcelCutOverDate( Date( 1, 3, 1900 ));
+
+double XclRoot::GetDoubleFromDateTime( const DateTime& rDateTime ) const
+{
+ double fValue = DateTime::Sub( rDateTime, GetNullDate());
+ // adjust dates before 1900-03-01 to get correct time values in the range [0.0,1.0)
+ /* XXX: this is only used when reading BIFF, otherwise we'd have to check
+ * for dateCompatibility==true as mentioned below. */
+ if( rDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
+ fValue -= 1.0;
+ return fValue;
+}
+
+DateTime XclRoot::GetDateTimeFromDouble( double fValue ) const
+{
+ DateTime aDateTime = GetNullDate() + fValue;
+ // adjust dates before 1900-03-01 to get correct time values
+ /* FIXME: correction should only be done when writing BIFF or OOXML
+ * transitional with dateCompatibility==true (or absent for default true),
+ * but not if strict ISO/IEC 29500 which does not have the Excel error
+ * compatibility and the null date is the same 1899-12-30 as ours. */
+ if( aDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
+ aDateTime.AddDays(1);
+ return aDateTime;
+}
+
+ScEditEngineDefaulter& XclRoot::GetEditEngine() const
+{
+ if( !mrData.mxEditEngine )
+ {
+ mrData.mxEditEngine = std::make_shared<ScEditEngineDefaulter>( GetDoc().GetEnginePool() );
+ ScEditEngineDefaulter& rEE = *mrData.mxEditEngine;
+ rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ rEE.SetEditTextObjectPool( GetDoc().GetEditPool() );
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+ }
+ return *mrData.mxEditEngine;
+}
+
+ScHeaderEditEngine& XclRoot::GetHFEditEngine() const
+{
+ if( !mrData.mxHFEditEngine )
+ {
+ mrData.mxHFEditEngine = std::make_shared<ScHeaderEditEngine>( EditEngine::CreatePool().get() );
+ ScHeaderEditEngine& rEE = *mrData.mxHFEditEngine;
+ rEE.SetRefMapMode(MapMode(MapUnit::MapTwip)); // headers/footers use twips as default metric
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+
+ // set Calc header/footer defaults
+ auto pEditSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *GetDoc().GetPool() );
+ ScPatternAttr::FillToEditItemSet( *pEditSet, aItemSet );
+ // FillToEditItemSet() adjusts font height to 1/100th mm, we need twips
+ pEditSet->Put( aItemSet.Get( ATTR_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ pEditSet->Put( aItemSet.Get( ATTR_CJK_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ pEditSet->Put( aItemSet.Get( ATTR_CTL_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ rEE.SetDefaults( std::move(pEditSet) ); // takes ownership
+ }
+ return *mrData.mxHFEditEngine;
+}
+
+EditEngine& XclRoot::GetDrawEditEngine() const
+{
+ if( !mrData.mxDrawEditEng )
+ {
+ mrData.mxDrawEditEng = std::make_shared<EditEngine>( &GetDoc().GetDrawLayer()->GetItemPool() );
+ EditEngine& rEE = *mrData.mxDrawEditEng;
+ rEE.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetDoc().GetDrawLayer()->GetStyleSheetPool()));
+ rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+ }
+ return *mrData.mxDrawEditEng;
+}
+
+XclFontPropSetHelper& XclRoot::GetFontPropSetHelper() const
+{
+ return *mrData.mxFontPropSetHlp;
+}
+
+XclChPropSetHelper& XclRoot::GetChartPropSetHelper() const
+{
+ return *mrData.mxChPropSetHlp;
+}
+
+ScExtDocOptions& XclRoot::GetExtDocOptions() const
+{
+ return *mrData.mxExtDocOpt;
+}
+
+XclTracer& XclRoot::GetTracer() const
+{
+ return *mrData.mxTracer;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlstyle.cxx b/sc/source/filter/excel/xlstyle.cxx
new file mode 100644
index 0000000000..5c494a7e60
--- /dev/null
+++ b/sc/source/filter/excel/xlstyle.cxx
@@ -0,0 +1,1749 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlstyle.hxx>
+#include <com/sun/star/awt/FontFamily.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/font.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <rtl/tencinfo.h>
+#include <svl/numformat.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/unohelp.hxx>
+#include <editeng/svxfont.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <global.hxx>
+#include <xlroot.hxx>
+#include <xltools.hxx>
+// Color data =================================================================
+
+/** Standard EGA colors, bright. */
+#define EXC_PALETTE_EGA_COLORS_LIGHT \
+ Color(0x000000), Color(0xFFFFFF), Color(0xFF0000), Color(0x00FF00), Color(0x0000FF), Color(0xFFFF00), Color(0xFF00FF), Color(0x00FFFF)
+/** Standard EGA colors, dark. */
+#define EXC_PALETTE_EGA_COLORS_DARK \
+ Color(0x800000), Color(0x008000), Color(0x000080), Color(0x808000), Color(0x800080), Color(0x008080), Color(0xC0C0C0), Color(0x808080)
+
+/** Default color table for BIFF2. */
+const Color spnDefColorTable2[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT
+};
+
+/** Default color table for BIFF3/BIFF4. */
+const Color spnDefColorTable3[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK
+};
+
+/** Default color table for BIFF5/BIFF7. */
+const Color spnDefColorTable5[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x8080FF), Color(0x802060), Color(0xFFFFC0), Color(0xA0E0E0), Color(0x600080), Color(0xFF8080), Color(0x0080C0), Color(0xC0C0FF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CFFF), Color(0x69FFFF), Color(0xE0FFE0), Color(0xFFFF80), Color(0xA6CAF0), Color(0xDD9CB3), Color(0xB38FEE), Color(0xE3E3E3),
+/* 48 */ Color(0x2A6FF9), Color(0x3FB8CD), Color(0x488436), Color(0x958C41), Color(0x8E5E42), Color(0xA0627A), Color(0x624FAC), Color(0x969696),
+/* 56 */ Color(0x1D2FBE), Color(0x286676), Color(0x004500), Color(0x453E01), Color(0x6A2813), Color(0x85396A), Color(0x4A3285), Color(0x424242)
+};
+
+/** Default color table for BIFF8. */
+const Color spnDefColorTable8[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x9999FF), Color(0x993366), Color(0xFFFFCC), Color(0xCCFFFF), Color(0x660066), Color(0xFF8080), Color(0x0066CC), Color(0xCCCCFF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CCFF), Color(0xCCFFFF), Color(0xCCFFCC), Color(0xFFFF99), Color(0x99CCFF), Color(0xFF99CC), Color(0xCC99FF), Color(0xFFCC99),
+/* 48 */ Color(0x3366FF), Color(0x33CCCC), Color(0x99CC00), Color(0xFFCC00), Color(0xFF9900), Color(0xFF6600), Color(0x666699), Color(0x969696),
+/* 56 */ Color(0x003366), Color(0x339966), Color(0x003300), Color(0x333300), Color(0x993300), Color(0x993366), Color(0x333399), Color(0x333333)
+};
+
+#undef EXC_PALETTE_EGA_COLORS_LIGHT
+#undef EXC_PALETTE_EGA_COLORS_DARK
+
+XclDefaultPalette::XclDefaultPalette( const XclRoot& rRoot ) :
+ mpnColorTable( nullptr ),
+ mnTableSize( 0 )
+{
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ mnFaceColor = rSett.GetFaceColor();
+ // Don't use the system HelpBack and HelpText colours as it causes problems
+ // with modern gnome. This is because mnNoteText and mnNoteBack are used
+ // when colour indices ( instead of real colours ) are specified.
+ // Note: That this it is not an unusual scenario that we get the Note
+ // background specified as a real colour and the text specified as a
+ // colour index. That means the text colour would be picked from
+ // the system where the note background would be picked from a real colour.
+ // Previously the note text colour was picked from the system tooltip
+ // text colour, on modern gnome(e.g. 3) that tends to be 'white' with the
+ // default theme.
+ // Using the Libreoffice defaults ( instead of system specific colours
+ // ) lessens the chance of the one colour being an unsuitable combination
+ // because by default the note text is black and the note background is
+ // a light yellow colour ( very similar to Excel's normal defaults )
+ mnNoteText = svtools::ColorConfig::GetDefaultColor( svtools::FONTCOLOR );
+ mnNoteBack = svtools::ColorConfig::GetDefaultColor( svtools::CALCNOTESBACKGROUND );
+
+ // default colors
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF2:
+ mpnColorTable = spnDefColorTable2;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable2 );
+ break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ mpnColorTable = spnDefColorTable3;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable3 );
+ break;
+ case EXC_BIFF5:
+ mpnColorTable = spnDefColorTable5;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable5 );
+ break;
+ case EXC_BIFF8:
+ mpnColorTable = spnDefColorTable8;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable8 );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+Color XclDefaultPalette::GetDefColor( sal_uInt16 nXclIndex ) const
+{
+ Color nColor;
+ if( nXclIndex < mnTableSize )
+ nColor = mpnColorTable[ nXclIndex ];
+ else switch( nXclIndex )
+ {
+ case EXC_COLOR_WINDOWTEXT3:
+ case EXC_COLOR_WINDOWTEXT:
+ case EXC_COLOR_CHWINDOWTEXT: nColor = COL_BLACK; break;
+ case EXC_COLOR_WINDOWBACK3:
+ case EXC_COLOR_WINDOWBACK:
+ case EXC_COLOR_CHWINDOWBACK: nColor = COL_WHITE; break;
+ case EXC_COLOR_BUTTONBACK: nColor = mnFaceColor; break;
+ case EXC_COLOR_CHBORDERAUTO: nColor = COL_BLACK; break; // TODO: really always black?
+ case EXC_COLOR_NOTEBACK: nColor = mnNoteBack; break;
+ case EXC_COLOR_NOTETEXT: nColor = mnNoteText; break;
+ case EXC_COLOR_FONTAUTO: nColor = COL_AUTO; break;
+ default:
+ SAL_WARN("sc", "XclDefaultPalette::GetDefColor - unknown default color index: " << nXclIndex );
+ nColor = COL_AUTO;
+ }
+ return nColor;
+}
+
+// Font Data ==================================================================
+
+namespace Awt = ::com::sun::star::awt;
+namespace AwtFontFamily = Awt::FontFamily;
+namespace AwtFontLineStyle = Awt::FontUnderline;
+namespace AwtFontStrikeout = Awt::FontStrikeout;
+
+XclFontData::XclFontData()
+{
+ Clear();
+}
+
+XclFontData::XclFontData(const vcl::Font& rFont, model::ComplexColor const& rComplexColor)
+{
+ Clear();
+ FillFromVclFont(rFont, rComplexColor);
+}
+
+XclFontData::XclFontData(const SvxFont& rFont, model::ComplexColor const& rComplexColor)
+{
+ FillFromSvxFont(rFont, rComplexColor);
+}
+
+void XclFontData::Clear()
+{
+ maName.clear();
+ maStyle.clear();
+ maComplexColor.setColor(COL_AUTO);
+ mnHeight = 0;
+ mnWeight = EXC_FONTWGHT_DONTKNOW;
+ mnEscapem = EXC_FONTESC_NONE;
+ mnFamily = EXC_FONTFAM_SYSTEM;
+ mnCharSet = EXC_FONTCSET_ANSI_LATIN;
+ mnUnderline = EXC_FONTUNDERL_NONE;
+ mbItalic = mbStrikeout = mbOutline = mbShadow = false;
+}
+
+void XclFontData::FillFromVclFont(const vcl::Font& rFont, model::ComplexColor const& rComplexColor)
+{
+ maName = XclTools::GetXclFontName( rFont.GetFamilyName() ); // substitute with MS fonts
+ maStyle.clear();
+ SetScUnderline( rFont.GetUnderline() );
+ mnEscapem = EXC_FONTESC_NONE;
+ SetScHeight( rFont.GetFontSize().Height() );
+ SetScWeight( rFont.GetWeight() );
+ SetScFamily( rFont.GetFamilyType() );
+ SetFontEncoding( rFont.GetCharSet() );
+ SetScPosture( rFont.GetItalic() );
+ SetScStrikeout( rFont.GetStrikeout() );
+ mbOutline = rFont.IsOutline();
+ mbShadow = rFont.IsShadow();
+
+ maComplexColor = rComplexColor;
+}
+
+void XclFontData::FillFromSvxFont(const SvxFont& rFont, model::ComplexColor const& rComplexColor)
+{
+ FillFromVclFont(rFont, rComplexColor);
+ SetScEscapement(rFont.GetEscapement());
+}
+
+// *** conversion of VCL/SVX constants *** ------------------------------------
+
+FontFamily XclFontData::GetScFamily( rtl_TextEncoding eDefTextEnc ) const
+{
+ FontFamily eScFamily;
+ // ! format differs from Windows documentation: family is in lower nibble, pitch unknown
+ switch( mnFamily & 0x0F )
+ {
+ case EXC_FONTFAM_ROMAN: eScFamily = FAMILY_ROMAN; break;
+ case EXC_FONTFAM_SWISS: eScFamily = FAMILY_SWISS; break;
+ case EXC_FONTFAM_MODERN: eScFamily = FAMILY_MODERN; break;
+ case EXC_FONTFAM_SCRIPT: eScFamily = FAMILY_SCRIPT; break;
+ case EXC_FONTFAM_DECORATIVE: eScFamily = FAMILY_DECORATIVE; break;
+ default:
+ eScFamily =
+ ((eDefTextEnc == RTL_TEXTENCODING_APPLE_ROMAN) &&
+ (maName.equalsIgnoreAsciiCase( "Geneva" ) || maName.equalsIgnoreAsciiCase( "Chicago" ))) ?
+ FAMILY_SWISS : FAMILY_DONTKNOW;
+ }
+ return eScFamily;
+}
+
+rtl_TextEncoding XclFontData::GetFontEncoding() const
+{
+ // convert Windows character set to text encoding identifier
+ return rtl_getTextEncodingFromWindowsCharset( mnCharSet );
+}
+
+FontItalic XclFontData::GetScPosture() const
+{
+ return mbItalic ? ITALIC_NORMAL : ITALIC_NONE;
+}
+
+FontWeight XclFontData::GetScWeight() const
+{
+ FontWeight eScWeight;
+
+ if( !mnWeight ) eScWeight = WEIGHT_DONTKNOW;
+ else if( mnWeight < 150 ) eScWeight = WEIGHT_THIN;
+ else if( mnWeight < 250 ) eScWeight = WEIGHT_ULTRALIGHT;
+ else if( mnWeight < 325 ) eScWeight = WEIGHT_LIGHT;
+ else if( mnWeight < 375 ) eScWeight = WEIGHT_SEMILIGHT;
+ else if( mnWeight < 450 ) eScWeight = WEIGHT_NORMAL;
+ else if( mnWeight < 550 ) eScWeight = WEIGHT_MEDIUM;
+ else if( mnWeight < 650 ) eScWeight = WEIGHT_SEMIBOLD;
+ else if( mnWeight < 750 ) eScWeight = WEIGHT_BOLD;
+ else if( mnWeight < 850 ) eScWeight = WEIGHT_ULTRABOLD;
+ else eScWeight = WEIGHT_BLACK;
+
+ return eScWeight;
+}
+
+FontLineStyle XclFontData::GetScUnderline() const
+{
+ FontLineStyle eScUnderl = LINESTYLE_NONE;
+ switch( mnUnderline )
+ {
+ case EXC_FONTUNDERL_SINGLE:
+ case EXC_FONTUNDERL_SINGLE_ACC: eScUnderl = LINESTYLE_SINGLE; break;
+ case EXC_FONTUNDERL_DOUBLE:
+ case EXC_FONTUNDERL_DOUBLE_ACC: eScUnderl = LINESTYLE_DOUBLE; break;
+ }
+ return eScUnderl;
+}
+
+SvxEscapement XclFontData::GetScEscapement() const
+{
+ SvxEscapement eScEscapem = SvxEscapement::Off;
+ switch( mnEscapem )
+ {
+ case EXC_FONTESC_SUPER: eScEscapem = SvxEscapement::Superscript; break;
+ case EXC_FONTESC_SUB: eScEscapem = SvxEscapement::Subscript; break;
+ }
+ return eScEscapem;
+}
+
+FontStrikeout XclFontData::GetScStrikeout() const
+{
+ return mbStrikeout ? STRIKEOUT_SINGLE : STRIKEOUT_NONE;
+}
+
+void XclFontData::SetScHeight( sal_Int32 nTwips )
+{
+ mnHeight = static_cast< sal_uInt16 >( ::std::min( nTwips, static_cast<sal_Int32>(0x7FFFL) ) );
+}
+
+void XclFontData::SetScFamily( FontFamily eScFamily )
+{
+ switch( eScFamily )
+ {
+ case FAMILY_DONTKNOW: mnFamily = EXC_FONTFAM_DONTKNOW; break;
+ case FAMILY_DECORATIVE: mnFamily = EXC_FONTFAM_DECORATIVE; break;
+ case FAMILY_MODERN: mnFamily = EXC_FONTFAM_MODERN; break;
+ case FAMILY_ROMAN: mnFamily = EXC_FONTFAM_ROMAN; break;
+ case FAMILY_SCRIPT: mnFamily = EXC_FONTFAM_SCRIPT; break;
+ case FAMILY_SWISS: mnFamily = EXC_FONTFAM_SWISS; break;
+ case FAMILY_SYSTEM: mnFamily = EXC_FONTFAM_SYSTEM; break;
+ default:
+ OSL_FAIL( "XclFontData::SetScFamily - unknown font family" );
+ mnFamily = EXC_FONTFAM_DONTKNOW;
+ }
+}
+
+void XclFontData::SetFontEncoding( rtl_TextEncoding eFontEnc )
+{
+ // convert text encoding identifier to Windows character set
+ mnCharSet = rtl_getBestWindowsCharsetFromTextEncoding( eFontEnc );
+}
+
+void XclFontData::SetScPosture( FontItalic eScPosture )
+{
+ mbItalic = (eScPosture == ITALIC_OBLIQUE) || (eScPosture == ITALIC_NORMAL);
+}
+
+void XclFontData::SetScWeight( FontWeight eScWeight )
+{
+ switch( eScWeight )
+ {
+ case WEIGHT_DONTKNOW: mnWeight = EXC_FONTWGHT_DONTKNOW; break;
+ case WEIGHT_THIN: mnWeight = EXC_FONTWGHT_THIN; break;
+ case WEIGHT_ULTRALIGHT: mnWeight = EXC_FONTWGHT_ULTRALIGHT; break;
+ case WEIGHT_LIGHT: mnWeight = EXC_FONTWGHT_LIGHT; break;
+ case WEIGHT_SEMILIGHT: mnWeight = EXC_FONTWGHT_SEMILIGHT; break;
+ case WEIGHT_NORMAL: mnWeight = EXC_FONTWGHT_NORMAL; break;
+ case WEIGHT_MEDIUM: mnWeight = EXC_FONTWGHT_MEDIUM; break;
+ case WEIGHT_SEMIBOLD: mnWeight = EXC_FONTWGHT_SEMIBOLD; break;
+ case WEIGHT_BOLD: mnWeight = EXC_FONTWGHT_BOLD; break;
+ case WEIGHT_ULTRABOLD: mnWeight = EXC_FONTWGHT_ULTRABOLD; break;
+ case WEIGHT_BLACK: mnWeight = EXC_FONTWGHT_BLACK; break;
+ default: mnWeight = EXC_FONTWGHT_NORMAL;
+ }
+}
+
+void XclFontData::SetScUnderline( FontLineStyle eScUnderl )
+{
+ switch( eScUnderl )
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case LINESTYLE_DOUBLE:
+ case LINESTYLE_DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+}
+
+void XclFontData::SetScEscapement( short nScEscapem )
+{
+ if( nScEscapem > 0 )
+ mnEscapem = EXC_FONTESC_SUPER;
+ else if( nScEscapem < 0 )
+ mnEscapem = EXC_FONTESC_SUB;
+ else
+ mnEscapem = EXC_FONTESC_NONE;
+}
+
+void XclFontData::SetScStrikeout( FontStrikeout eScStrikeout )
+{
+ mbStrikeout =
+ (eScStrikeout == STRIKEOUT_SINGLE) || (eScStrikeout == STRIKEOUT_DOUBLE) ||
+ (eScStrikeout == STRIKEOUT_BOLD) || (eScStrikeout == STRIKEOUT_SLASH) ||
+ (eScStrikeout == STRIKEOUT_X);
+}
+
+// *** conversion of API constants *** ----------------------------------------
+
+float XclFontData::GetApiHeight() const
+{
+ return o3tl::convert<double>(mnHeight, o3tl::Length::twip, o3tl::Length::pt);
+}
+
+sal_Int16 XclFontData::GetApiFamily() const
+{
+ sal_Int16 nApiFamily = AwtFontFamily::DONTKNOW;
+ switch( mnFamily )
+ {
+ case FAMILY_DECORATIVE: nApiFamily = AwtFontFamily::DECORATIVE; break;
+ case FAMILY_MODERN: nApiFamily = AwtFontFamily::MODERN; break;
+ case FAMILY_ROMAN: nApiFamily = AwtFontFamily::ROMAN; break;
+ case FAMILY_SCRIPT: nApiFamily = AwtFontFamily::SCRIPT; break;
+ case FAMILY_SWISS: nApiFamily = AwtFontFamily::SWISS; break;
+ case FAMILY_SYSTEM: nApiFamily = AwtFontFamily::SYSTEM; break;
+ }
+ return nApiFamily;
+}
+
+sal_Int16 XclFontData::GetApiFontEncoding() const
+{
+ // API constants are equal to rtl_TextEncoding constants
+ return static_cast< sal_Int16 >( GetFontEncoding() );
+}
+
+Awt::FontSlant XclFontData::GetApiPosture() const
+{
+ return mbItalic ? Awt::FontSlant_ITALIC : Awt::FontSlant_NONE;
+}
+
+float XclFontData::GetApiWeight() const
+{
+ return vcl::unohelper::ConvertFontWeight( GetScWeight() );
+}
+
+sal_Int16 XclFontData::GetApiUnderline() const
+{
+ sal_Int16 nApiUnderl = AwtFontLineStyle::NONE;
+ switch( mnUnderline )
+ {
+ case EXC_FONTUNDERL_SINGLE:
+ case EXC_FONTUNDERL_SINGLE_ACC: nApiUnderl = AwtFontLineStyle::SINGLE; break;
+ case EXC_FONTUNDERL_DOUBLE:
+ case EXC_FONTUNDERL_DOUBLE_ACC: nApiUnderl = AwtFontLineStyle::DOUBLE; break;
+ }
+ return nApiUnderl;
+}
+
+sal_Int16 XclFontData::GetApiEscapement() const
+{
+ sal_Int16 nApiEscapem = 0;
+ switch( mnEscapem )
+ {
+ case EXC_FONTESC_SUPER: nApiEscapem = 33; break;
+ case EXC_FONTESC_SUB: nApiEscapem = -33; break;
+ }
+ return nApiEscapem;
+}
+
+sal_Int16 XclFontData::GetApiStrikeout() const
+{
+ return mbStrikeout ? AwtFontStrikeout::SINGLE : AwtFontStrikeout::NONE;
+}
+
+void XclFontData::SetApiHeight( float fPoint )
+{
+ mnHeight = std::min(o3tl::convert(fPoint, o3tl::Length::pt, o3tl::Length::twip) + 0.5, 32767.0);
+}
+
+void XclFontData::SetApiFamily( sal_Int16 nApiFamily )
+{
+ switch( nApiFamily )
+ {
+ case AwtFontFamily::DECORATIVE: mnFamily = FAMILY_DECORATIVE; break;
+ case AwtFontFamily::MODERN: mnFamily = FAMILY_MODERN; break;
+ case AwtFontFamily::ROMAN: mnFamily = FAMILY_ROMAN; break;
+ case AwtFontFamily::SCRIPT: mnFamily = FAMILY_SCRIPT; break;
+ case AwtFontFamily::SWISS: mnFamily = FAMILY_SWISS; break;
+ case AwtFontFamily::SYSTEM: mnFamily = FAMILY_SYSTEM; break;
+ default: mnFamily = FAMILY_DONTKNOW;
+ }
+}
+
+void XclFontData::SetApiPosture( Awt::FontSlant eApiPosture )
+{
+ mbItalic =
+ (eApiPosture == Awt::FontSlant_OBLIQUE) ||
+ (eApiPosture == Awt::FontSlant_ITALIC) ||
+ (eApiPosture == Awt::FontSlant_REVERSE_OBLIQUE) ||
+ (eApiPosture == Awt::FontSlant_REVERSE_ITALIC);
+}
+
+void XclFontData::SetApiWeight( float fApiWeight )
+{
+ SetScWeight( vcl::unohelper::ConvertFontWeight( fApiWeight ) );
+}
+
+void XclFontData::SetApiUnderline( sal_Int16 nApiUnderl )
+{
+ switch( nApiUnderl )
+ {
+ case AwtFontLineStyle::NONE:
+ case AwtFontLineStyle::DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case AwtFontLineStyle::DOUBLE:
+ case AwtFontLineStyle::DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+}
+
+void XclFontData::SetApiEscapement( sal_Int16 nApiEscapem )
+{
+ if( nApiEscapem > 0 )
+ mnEscapem = EXC_FONTESC_SUPER;
+ else if( nApiEscapem < 0 )
+ mnEscapem = EXC_FONTESC_SUB;
+ else
+ mnEscapem = EXC_FONTESC_NONE;
+}
+
+void XclFontData::SetApiStrikeout( sal_Int16 nApiStrikeout )
+{
+ mbStrikeout =
+ (nApiStrikeout != AwtFontStrikeout::NONE) &&
+ (nApiStrikeout != AwtFontStrikeout::DONTKNOW);
+}
+
+bool operator==( const XclFontData& rLeft, const XclFontData& rRight )
+{
+ return
+ (rLeft.mnHeight == rRight.mnHeight) &&
+ (rLeft.mnWeight == rRight.mnWeight) &&
+ (rLeft.mnUnderline == rRight.mnUnderline) &&
+ (rLeft.maComplexColor == rRight.maComplexColor) &&
+ (rLeft.mnEscapem == rRight.mnEscapem) &&
+ (rLeft.mnFamily == rRight.mnFamily) &&
+ (rLeft.mnCharSet == rRight.mnCharSet) &&
+ (rLeft.mbItalic == rRight.mbItalic) &&
+ (rLeft.mbStrikeout == rRight.mbStrikeout) &&
+ (rLeft.mbOutline == rRight.mbOutline) &&
+ (rLeft.mbShadow == rRight.mbShadow) &&
+ (rLeft.maName == rRight.maName);
+}
+
+namespace {
+
+/** Property names for common font settings. */
+const char *const sppcPropNamesChCommon[] =
+{
+ "CharUnderline", "CharStrikeout", "CharColor", "CharContoured", "CharShadowed", nullptr
+};
+/** Property names for Western font settings. */
+const char *const sppcPropNamesChWstrn[] =
+{
+ "CharFontName", "CharHeight", "CharPosture", "CharWeight", nullptr
+};
+/** Property names for Asian font settings. */
+const char *const sppcPropNamesChAsian[] =
+{
+ "CharFontNameAsian", "CharHeightAsian", "CharPostureAsian", "CharWeightAsian", nullptr
+};
+/** Property names for Complex font settings. */
+const char *const sppcPropNamesChCmplx[] =
+{
+ "CharFontNameComplex", "CharHeightComplex", "CharPostureComplex", "CharWeightComplex", nullptr
+};
+/** Property names for escapement. */
+const char *const sppcPropNamesChEscapement[] =
+{
+ "CharEscapement", "CharEscapementHeight", nullptr
+};
+const sal_Int8 EXC_API_ESC_HEIGHT = 58; /// Default escapement font height.
+
+/** Property names for Western font settings without font name. */
+const char *const *const sppcPropNamesChWstrnNoName = sppcPropNamesChWstrn + 1;
+/** Property names for Asian font settings without font name. */
+const char *const *const sppcPropNamesChAsianNoName = sppcPropNamesChAsian + 1;
+/** Property names for Complex font settings without font name. */
+const char *const *const sppcPropNamesChCmplxNoName = sppcPropNamesChCmplx + 1;
+
+/** Property names for font settings in form controls. */
+const char *const sppcPropNamesControl[] =
+{
+ "FontName", "FontFamily", "FontCharset", "FontHeight", "FontSlant",
+ "FontWeight", "FontLineStyle", "FontStrikeout", "TextColor", nullptr
+};
+
+/** Inserts all passed API font settings into the font data object. */
+void lclSetApiFontSettings( XclFontData& rFontData,
+ const OUString& rApiFontName, float fApiHeight, float fApiWeight,
+ Awt::FontSlant eApiPosture, sal_Int16 nApiUnderl, sal_Int16 nApiStrikeout )
+{
+ rFontData.maName = XclTools::GetXclFontName( rApiFontName );
+ rFontData.SetApiHeight( fApiHeight );
+ rFontData.SetApiWeight( fApiWeight );
+ rFontData.SetApiPosture( eApiPosture );
+ rFontData.SetApiUnderline( nApiUnderl );
+ rFontData.SetApiStrikeout( nApiStrikeout );
+}
+
+/** Writes script dependent properties to a font property set helper. */
+void lclWriteChartFont( ScfPropertySet& rPropSet,
+ ScfPropSetHelper& rHlpName, ScfPropSetHelper& rHlpNoName,
+ const XclFontData& rFontData, bool bHasFontName )
+{
+ // select the font helper
+ ScfPropSetHelper& rPropSetHlp = bHasFontName ? rHlpName : rHlpNoName;
+ // initialize the font helper (must be called before writing any properties)
+ rPropSetHlp.InitializeWrite();
+ // write font name
+ if( bHasFontName )
+ rPropSetHlp << rFontData.maName;
+ // write remaining properties
+ rPropSetHlp << rFontData.GetApiHeight() << rFontData.GetApiPosture() << rFontData.GetApiWeight();
+ // write properties to property set
+ rPropSetHlp.WriteToPropertySet( rPropSet );
+}
+
+} // namespace
+
+XclFontPropSetHelper::XclFontPropSetHelper() :
+ maHlpChCommon( sppcPropNamesChCommon ),
+ maHlpChWstrn( sppcPropNamesChWstrn ),
+ maHlpChAsian( sppcPropNamesChAsian ),
+ maHlpChCmplx( sppcPropNamesChCmplx ),
+ maHlpChWstrnNoName( sppcPropNamesChWstrnNoName ),
+ maHlpChAsianNoName( sppcPropNamesChAsianNoName ),
+ maHlpChCmplxNoName( sppcPropNamesChCmplxNoName ),
+ maHlpChEscapement( sppcPropNamesChEscapement ),
+ maHlpControl( sppcPropNamesControl )
+{
+}
+
+void XclFontPropSetHelper::ReadFontProperties( XclFontData& rFontData,
+ const ScfPropertySet& rPropSet, XclFontPropSetType eType, sal_Int16 nScript )
+{
+ switch( eType )
+ {
+ case EXC_FONTPROPSET_CHART:
+ {
+ OUString aApiFontName;
+ float fApiHeight, fApiWeight;
+ sal_Int16 nApiUnderl = 0, nApiStrikeout = 0;
+ Awt::FontSlant eApiPosture;
+
+ // read script type dependent properties
+ ScfPropSetHelper& rPropSetHlp = GetChartHelper( nScript );
+ rPropSetHlp.ReadFromPropertySet( rPropSet );
+ rPropSetHlp >> aApiFontName >> fApiHeight >> eApiPosture >> fApiWeight;
+ // read common properties
+ maHlpChCommon.ReadFromPropertySet( rPropSet );
+ maHlpChCommon >> nApiUnderl;
+ maHlpChCommon >> nApiStrikeout;
+ Color aColor;
+ maHlpChCommon >> aColor;
+ rFontData.maComplexColor.setColor(aColor);
+ maHlpChCommon >> rFontData.mbOutline;
+ maHlpChCommon >> rFontData.mbShadow;
+
+ // convert API property values to Excel settings
+ lclSetApiFontSettings( rFontData, aApiFontName,
+ fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout );
+
+ // font escapement
+ sal_Int16 nApiEscapement = 0;
+ sal_Int8 nApiEscHeight = 0;
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement >> nApiEscapement >> nApiEscHeight;
+ rFontData.SetApiEscapement( nApiEscapement );
+ }
+ break;
+
+ case EXC_FONTPROPSET_CONTROL:
+ {
+ OUString aApiFontName;
+ float fApiHeight(0.0), fApiWeight(0.0);
+ sal_Int16 nApiFamily(0), nApiCharSet(0), nApiPosture(0), nApiUnderl(0), nApiStrikeout(0);
+
+ // read font properties
+ maHlpControl.ReadFromPropertySet( rPropSet );
+ maHlpControl >> aApiFontName;
+ maHlpControl >> nApiFamily;
+ maHlpControl >> nApiCharSet;
+ maHlpControl >> fApiHeight;
+ maHlpControl >> nApiPosture;
+ maHlpControl >> fApiWeight;
+ maHlpControl >> nApiUnderl;
+ maHlpControl >> nApiStrikeout;
+ Color aColor;
+ maHlpControl >> aColor;
+ rFontData.maComplexColor.setColor(aColor);
+
+ // convert API property values to Excel settings
+ Awt::FontSlant eApiPosture = static_cast< Awt::FontSlant >( nApiPosture );
+ lclSetApiFontSettings( rFontData, aApiFontName,
+ fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout );
+ rFontData.SetApiFamily( nApiFamily );
+ rFontData.SetFontEncoding( nApiCharSet );
+ }
+ break;
+ }
+}
+
+void XclFontPropSetHelper::WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ const XclFontData& rFontData, bool bHasWstrn, bool bHasAsian, bool bHasCmplx,
+ const Color* pFontColor )
+{
+ switch( eType )
+ {
+ case EXC_FONTPROPSET_CHART:
+ {
+ // write common properties
+ maHlpChCommon.InitializeWrite();
+ Color aColor = pFontColor ? *pFontColor : rFontData.maComplexColor.getFinalColor();
+ maHlpChCommon << rFontData.GetApiUnderline()
+ << rFontData.GetApiStrikeout()
+ << aColor
+ << rFontData.mbOutline
+ << rFontData.mbShadow;
+ maHlpChCommon.WriteToPropertySet( rPropSet );
+
+ // write script type dependent properties
+ lclWriteChartFont( rPropSet, maHlpChWstrn, maHlpChWstrnNoName, rFontData, bHasWstrn );
+ lclWriteChartFont( rPropSet, maHlpChAsian, maHlpChAsianNoName, rFontData, bHasAsian );
+ lclWriteChartFont( rPropSet, maHlpChCmplx, maHlpChCmplxNoName, rFontData, bHasCmplx );
+
+ // font escapement
+ if( rFontData.GetScEscapement() != SvxEscapement::Off )
+ {
+ maHlpChEscapement.InitializeWrite();
+ maHlpChEscapement << rFontData.GetApiEscapement() << EXC_API_ESC_HEIGHT;
+ maHlpChEscapement.WriteToPropertySet( rPropSet );
+ }
+ }
+ break;
+
+ case EXC_FONTPROPSET_CONTROL:
+ {
+ maHlpControl.InitializeWrite();
+ maHlpControl << rFontData.maName
+ << rFontData.GetApiFamily()
+ << rFontData.GetApiFontEncoding()
+ << static_cast< sal_Int16 >( rFontData.GetApiHeight() + 0.5 )
+ << rFontData.GetApiPosture()
+ << rFontData.GetApiWeight()
+ << rFontData.GetApiUnderline()
+ << rFontData.GetApiStrikeout()
+ << rFontData.maComplexColor.getFinalColor();
+ maHlpControl.WriteToPropertySet( rPropSet );
+ }
+ break;
+ }
+}
+
+ScfPropSetHelper& XclFontPropSetHelper::GetChartHelper( sal_Int16 nScript )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ switch( nScript )
+ {
+ case ApiScriptType::LATIN: return maHlpChWstrn;
+ case ApiScriptType::ASIAN: return maHlpChAsian;
+ case ApiScriptType::COMPLEX: return maHlpChCmplx;
+ default: OSL_FAIL( "XclFontPropSetHelper::GetChartHelper - unknown script type" );
+ }
+ return maHlpChWstrn;
+}
+
+// Number formats =============================================================
+
+namespace {
+
+/** Special number format index describing a reused format. */
+const NfIndexTableOffset PRV_NF_INDEX_REUSE = NF_INDEX_TABLE_ENTRIES;
+
+/** German primary language not defined, LANGUAGE_GERMAN belongs to Germany. */
+constexpr LanguageType PRV_LANGUAGE_GERMAN_PRIM = primary(LANGUAGE_GERMAN);
+/** French primary language not defined, LANGUAGE_FRENCH belongs to France. */
+constexpr LanguageType PRV_LANGUAGE_FRENCH_PRIM = primary(LANGUAGE_FRENCH);
+/** Parent language identifier for Asian languages. */
+constexpr LanguageType PRV_LANGUAGE_ASIAN_PRIM = primary(LANGUAGE_CHINESE);
+
+/** Stores the number format used in Calc for an Excel built-in number format. */
+struct XclBuiltInFormat
+{
+ sal_uInt16 mnXclNumFmt; /// Excel built-in index.
+ const char* mpFormat; /// Format string, may be 0 (meOffset used then).
+ NfIndexTableOffset meOffset; /// SvNumberFormatter format index, if mpFormat==0.
+ sal_uInt16 mnXclReuseFmt; /// Use this Excel format, if meOffset==PRV_NF_INDEX_REUSE.
+};
+
+/** Defines a literal Excel built-in number format. */
+#define EXC_NUMFMT_STRING( nXclNumFmt, pcUtf8 ) \
+ { nXclNumFmt, pcUtf8, NF_NUMBER_STANDARD, 0 }
+
+/** Defines an Excel built-in number format that maps to an own built-in format. */
+#define EXC_NUMFMT_OFFSET( nXclNumFmt, eOffset ) \
+ { nXclNumFmt, nullptr, eOffset, 0 }
+
+/** Defines an Excel built-in number format that is the same as the specified. */
+#define EXC_NUMFMT_REUSE( nXclNumFmt, nXclReuse ) \
+ { nXclNumFmt, nullptr, PRV_NF_INDEX_REUSE, nXclReuse }
+
+/** Terminates an Excel built-in number format table. */
+#define EXC_NUMFMT_ENDTABLE() \
+ { EXC_FORMAT_NOTFOUND, nullptr, NF_NUMBER_STANDARD, 0 }
+
+// Currency unit characters
+#define UTF8_BAHT "\340\270\277"
+#define UTF8_EURO "\342\202\254"
+#define UTF8_POUND_UK "\302\243"
+#define UTF8_SHEQEL "\342\202\252"
+#define UTF8_WON "\357\277\246"
+#define UTF8_YEN_CS "\357\277\245"
+#define UTF8_YEN_JP "\302\245"
+
+// Japanese/Chinese date/time characters
+#define UTF8_CJ_YEAR "\345\271\264"
+#define UTF8_CJ_MON "\346\234\210"
+#define UTF8_CJ_DAY "\346\227\245"
+#define UTF8_CJ_HOUR "\346\231\202"
+#define UTF8_CJ_MIN "\345\210\206"
+#define UTF8_CJ_SEC "\347\247\222"
+
+// Chinese Simplified date/time characters
+#define UTF8_CS_HOUR "\346\227\266"
+
+// Korean date/time characters
+#define UTF8_KO_YEAR "\353\205\204"
+#define UTF8_KO_MON "\354\233\224"
+#define UTF8_KO_DAY "\354\235\274"
+#define UTF8_KO_HOUR "\354\213\234"
+#define UTF8_KO_MIN "\353\266\204"
+#define UTF8_KO_SEC "\354\264\210"
+
+/** Default number format table. Last parent of all other tables, used for unknown languages. */
+const XclBuiltInFormat spBuiltInFormats_DONTKNOW[] =
+{
+ EXC_NUMFMT_OFFSET( 0, NF_NUMBER_STANDARD ), // General
+ EXC_NUMFMT_OFFSET( 1, NF_NUMBER_INT ), // 0
+ EXC_NUMFMT_OFFSET( 2, NF_NUMBER_DEC2 ), // 0.00
+ EXC_NUMFMT_OFFSET( 3, NF_NUMBER_1000INT ), // #,##0
+ EXC_NUMFMT_OFFSET( 4, NF_NUMBER_1000DEC2 ), // #,##0.00
+ // 5...8 contained in file
+ EXC_NUMFMT_OFFSET( 9, NF_PERCENT_INT ), // 0%
+ EXC_NUMFMT_OFFSET( 10, NF_PERCENT_DEC2 ), // 0.00%
+ EXC_NUMFMT_OFFSET( 11, NF_SCIENTIFIC_000E00 ), // 0.00E+00
+ EXC_NUMFMT_OFFSET( 12, NF_FRACTION_1D ), // # ?/?
+ EXC_NUMFMT_OFFSET( 13, NF_FRACTION_2D ), // # ??/??
+
+ // 14...22 date and time formats
+ EXC_NUMFMT_OFFSET( 14, NF_DATE_SYS_DDMMYYYY ),
+ EXC_NUMFMT_OFFSET( 15, NF_DATE_SYS_DMMMYY ),
+ EXC_NUMFMT_OFFSET( 16, NF_DATE_SYS_DDMMM ),
+ EXC_NUMFMT_OFFSET( 17, NF_DATE_SYS_MMYY ),
+ EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ),
+ EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ),
+ EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ),
+ EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ),
+ EXC_NUMFMT_OFFSET( 22, NF_DATETIME_SYSTEM_SHORT_HHMM ),
+
+ // 23...36 international formats
+ EXC_NUMFMT_REUSE( 23, 0 ),
+ EXC_NUMFMT_REUSE( 24, 0 ),
+ EXC_NUMFMT_REUSE( 25, 0 ),
+ EXC_NUMFMT_REUSE( 26, 0 ),
+ EXC_NUMFMT_REUSE( 27, 14 ),
+ EXC_NUMFMT_REUSE( 28, 14 ),
+ EXC_NUMFMT_REUSE( 29, 14 ),
+ EXC_NUMFMT_REUSE( 30, 14 ),
+ EXC_NUMFMT_REUSE( 31, 14 ),
+ EXC_NUMFMT_REUSE( 32, 21 ),
+ EXC_NUMFMT_REUSE( 33, 21 ),
+ EXC_NUMFMT_REUSE( 34, 21 ),
+ EXC_NUMFMT_REUSE( 35, 21 ),
+ EXC_NUMFMT_REUSE( 36, 14 ),
+
+ // 37...44 accounting formats
+ // 41...44 contained in file
+ EXC_NUMFMT_STRING( 37, "#,##0;-#,##0" ),
+ EXC_NUMFMT_STRING( 38, "#,##0;[RED]-#,##0" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00;-#,##0.00" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00;[RED]-#,##0.00" ),
+
+ // 45...49 more special formats
+ EXC_NUMFMT_STRING( 45, "mm:ss" ),
+ EXC_NUMFMT_STRING( 46, "[h]:mm:ss" ),
+ EXC_NUMFMT_STRING( 47, "mm:ss.0" ),
+ EXC_NUMFMT_STRING( 48, "##0.0E+0" ),
+ EXC_NUMFMT_OFFSET( 49, NF_TEXT ),
+
+ // 50...81 international formats
+ EXC_NUMFMT_REUSE( 50, 14 ),
+ EXC_NUMFMT_REUSE( 51, 14 ),
+ EXC_NUMFMT_REUSE( 52, 14 ),
+ EXC_NUMFMT_REUSE( 53, 14 ),
+ EXC_NUMFMT_REUSE( 54, 14 ),
+ EXC_NUMFMT_REUSE( 55, 14 ),
+ EXC_NUMFMT_REUSE( 56, 14 ),
+ EXC_NUMFMT_REUSE( 57, 14 ),
+ EXC_NUMFMT_REUSE( 58, 14 ),
+ EXC_NUMFMT_REUSE( 59, 1 ),
+ EXC_NUMFMT_REUSE( 60, 2 ),
+ EXC_NUMFMT_REUSE( 61, 3 ),
+ EXC_NUMFMT_REUSE( 62, 4 ),
+ EXC_NUMFMT_REUSE( 67, 9 ),
+ EXC_NUMFMT_REUSE( 68, 10 ),
+ EXC_NUMFMT_REUSE( 69, 12 ),
+ EXC_NUMFMT_REUSE( 70, 13 ),
+ EXC_NUMFMT_REUSE( 71, 14 ),
+ EXC_NUMFMT_REUSE( 72, 14 ),
+ EXC_NUMFMT_REUSE( 73, 15 ),
+ EXC_NUMFMT_REUSE( 74, 16 ),
+ EXC_NUMFMT_REUSE( 75, 17 ),
+ EXC_NUMFMT_REUSE( 76, 20 ),
+ EXC_NUMFMT_REUSE( 77, 21 ),
+ EXC_NUMFMT_REUSE( 78, 22 ),
+ EXC_NUMFMT_REUSE( 79, 45 ),
+ EXC_NUMFMT_REUSE( 80, 46 ),
+ EXC_NUMFMT_REUSE( 81, 47 ),
+
+ // 82...163 not used, must not occur in a file (Excel may crash)
+
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ENGLISH --------------------------------------------------------------------
+
+/** Base table for English locales. */
+const XclBuiltInFormat spBuiltInFormats_ENGLISH[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_UK[] =
+{
+ EXC_NUMFMT_STRING( 63, UTF8_POUND_UK "#,##0;-" UTF8_POUND_UK "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_POUND_UK "#,##0;[RED]-" UTF8_POUND_UK "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_POUND_UK "#,##0.00;-" UTF8_POUND_UK "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_POUND_UK "#,##0.00;[RED]-" UTF8_POUND_UK "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_EIRE[] =
+{
+ EXC_NUMFMT_STRING( 63, UTF8_EURO "#,##0;-" UTF8_EURO "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO "#,##0;[RED]-" UTF8_EURO "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO "#,##0.00;-" UTF8_EURO "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO "#,##0.00;[RED]-" UTF8_EURO "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_US[] =
+{
+ EXC_NUMFMT_STRING( 14, "M/D/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "M/D/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0_);(#,##0)" ),
+ EXC_NUMFMT_STRING( 38, "#,##0_);[RED](#,##0)" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00_);(#,##0.00)" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00_);[RED](#,##0.00)" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0_);($#,##0)" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0_);[RED]($#,##0)" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00_);($#,##0.00)" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00_);[RED]($#,##0.00)" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_CAN[] =
+{
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_AUS[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_SAFRICA[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY/MM/DD" ),
+ EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ),
+ EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ),
+ EXC_NUMFMT_STRING( 22, "YYYY/MM/DD hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\\R #,##0;\\R -#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\\R #,##0;[RED]\\R -#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\\R #,##0.00;\\R -#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\\R #,##0.00;[RED]\\R -#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// FRENCH ---------------------------------------------------------------------
+
+/** Base table for French locales. */
+const XclBuiltInFormat spBuiltInFormats_FRENCH[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_FRANCE[] =
+{
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0\\ _" UTF8_EURO ";-#,##0\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0\\ _" UTF8_EURO ";[RED]-#,##0\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00\\ _" UTF8_EURO ";-#,##0.00\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00\\ _" UTF8_EURO ";[RED]-#,##0.00\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0\\ " UTF8_EURO ";-#,##0\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0\\ " UTF8_EURO ";[RED]-#,##0\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00\\ " UTF8_EURO ";-#,##0.00\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00\\ " UTF8_EURO ";[RED]-#,##0.00\\ " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_CANADIAN[] =
+{
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0\\ _$_-;#,##0\\ _$-" ),
+ EXC_NUMFMT_STRING( 38, "#,##0\\ _$_-;[RED]#,##0\\ _$-" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00\\ _$_-;#,##0.00\\ _$-" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00\\ _$_-;[RED]#,##0.00\\ _$-" ),
+ EXC_NUMFMT_STRING( 63, "#,##0\\ $_-;#,##0\\ $-" ),
+ EXC_NUMFMT_STRING( 64, "#,##0\\ $_-;[RED]#,##0\\ $-" ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00\\ $_-;#,##0.00\\ $-" ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00\\ $_-;[RED]#,##0.00\\ $-" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_BELGIAN[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// GERMAN ---------------------------------------------------------------------
+
+/** Base table for German locales. */
+const XclBuiltInFormat spBuiltInFormats_GERMAN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD. MMM YY" ),
+ EXC_NUMFMT_STRING( 16, "DD. MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_GERMANY[] =
+{
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_AUSTRIAN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_LUXEMBOURG[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_LIECHTENSTEIN[] =
+{
+ EXC_NUMFMT_STRING( 63, "\"CHF \"#,##0;\"CHF \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"CHF \"#,##0;[RED]\"CHF \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"CHF \"#,##0.00;\"CHF \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"CHF \"#,##0.00;[RED]\"CHF \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ITALIAN --------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_ITALIAN_ITALY[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ITALIAN_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// SWEDISH --------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_SWEDISH_SWEDEN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _k_r;-#,##0 _k_r" ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _k_r;[RED]-#,##0 _k_r" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _k_r;-#,##0.00 _k_r" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _k_r;[RED]-#,##0.00 _k_r" ),
+ EXC_NUMFMT_STRING( 63, "#,##0 \"kr\";-#,##0 \"kr\"" ),
+ EXC_NUMFMT_STRING( 64, "#,##0 \"kr\";[RED]-#,##0 \"kr\"" ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 \"kr\";-#,##0.00 \"kr\"" ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 \"kr\";[RED]-#,##0.00 \"kr\"" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_SWEDISH_FINLAND[] =
+{
+ EXC_NUMFMT_STRING( 9, "0 %" ),
+ EXC_NUMFMT_STRING( 10, "0.00 %" ),
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "D.M.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ASIAN ----------------------------------------------------------------------
+
+/** Base table for Asian locales. */
+const XclBuiltInFormat spBuiltInFormats_ASIAN[] =
+{
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 23, "$#,##0_);($#,##0)" ),
+ EXC_NUMFMT_STRING( 24, "$#,##0_);[RED]($#,##0)" ),
+ EXC_NUMFMT_STRING( 25, "$#,##0.00_);($#,##0.00)" ),
+ EXC_NUMFMT_STRING( 26, "$#,##0.00_);[RED]($#,##0.00)" ),
+ EXC_NUMFMT_REUSE( 29, 28 ),
+ EXC_NUMFMT_REUSE( 36, 27 ),
+ EXC_NUMFMT_REUSE( 50, 27 ),
+ EXC_NUMFMT_REUSE( 51, 28 ),
+ EXC_NUMFMT_REUSE( 52, 34 ),
+ EXC_NUMFMT_REUSE( 53, 35 ),
+ EXC_NUMFMT_REUSE( 54, 28 ),
+ EXC_NUMFMT_REUSE( 55, 34 ),
+ EXC_NUMFMT_REUSE( 56, 35 ),
+ EXC_NUMFMT_REUSE( 57, 27 ),
+ EXC_NUMFMT_REUSE( 58, 28 ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_JAPANESE[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY/M/D" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY/M/D h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0411]GE.M.D" ),
+ EXC_NUMFMT_STRING( 28, "[$-0411]GGGE" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0411]M/D/YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ),
+ EXC_NUMFMT_STRING( 35, "[$-0411]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 63, UTF8_YEN_JP "#,##0;-" UTF8_YEN_JP "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_YEN_JP "#,##0;[RED]-" UTF8_YEN_JP "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_YEN_JP "#,##0.00;-" UTF8_YEN_JP "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_YEN_JP "#,##0.00;[RED]-" UTF8_YEN_JP "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_KOREAN[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY-MM-DD" ),
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0412]YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 28, "[$-0412]MM-DD" ),
+ EXC_NUMFMT_STRING( 30, "[$-0412]MM-DD-YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0412]YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN " ss" UTF8_KO_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0412]YYYY\"/\"MM\"/\"DD" ),
+ EXC_NUMFMT_STRING( 35, "[$-0412]YYYY-MM-DD" ),
+ EXC_NUMFMT_STRING( 63, UTF8_WON "#,##0;-" UTF8_WON "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_WON "#,##0;[RED]-" UTF8_WON "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_WON "#,##0.00;-" UTF8_WON "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_WON "#,##0.00;[RED]-" UTF8_WON "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_CHINESE_SIMPLIFIED[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY-M-D" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-M-D h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ),
+ EXC_NUMFMT_STRING( 28, "[$-0804]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0804]M-D-YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 35, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_REUSE( 52, 27 ),
+ EXC_NUMFMT_REUSE( 53, 28 ),
+ EXC_NUMFMT_STRING( 63, UTF8_YEN_CS "#,##0;-" UTF8_YEN_CS "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_YEN_CS "#,##0;[RED]-" UTF8_YEN_CS "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_YEN_CS "#,##0.00;-" UTF8_YEN_CS "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_YEN_CS "#,##0.00;[RED]-" UTF8_YEN_CS "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_CHINESE_TRADITIONAL[] =
+{
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "hh:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "hh:mm:ss AM/PM" ),
+ EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ),
+ EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ),
+ EXC_NUMFMT_STRING( 22, "YYYY/M/D hh:mm" ),
+ EXC_NUMFMT_STRING( 23, "US$#,##0_);(US$#,##0)" ),
+ EXC_NUMFMT_STRING( 24, "US$#,##0_);[RED](US$#,##0)" ),
+ EXC_NUMFMT_STRING( 25, "US$#,##0.00_);(US$#,##0.00)" ),
+ EXC_NUMFMT_STRING( 26, "US$#,##0.00_);[RED](US$#,##0.00)" ),
+ EXC_NUMFMT_STRING( 27, "[$-0404]E/M/D" ),
+ EXC_NUMFMT_STRING( 28, "[$-0404]E" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0404]M/D/YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0404]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 35, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// OTHER ----------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_HEBREW[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMMM" ),
+ EXC_NUMFMT_STRING( 17, "MMMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 63, UTF8_SHEQEL " #,##0;" UTF8_SHEQEL " -#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_SHEQEL " #,##0;[RED]" UTF8_SHEQEL " -#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_SHEQEL " #,##0.00;" UTF8_SHEQEL " -#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_SHEQEL " #,##0.00;[RED]" UTF8_SHEQEL " -#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_THAI[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/M/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "D/M/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 59, "t0" ),
+ EXC_NUMFMT_STRING( 60, "t0.00" ),
+ EXC_NUMFMT_STRING( 61, "t#,##0" ),
+ EXC_NUMFMT_STRING( 62, "t#,##0.00" ),
+ EXC_NUMFMT_STRING( 63, "t" UTF8_BAHT "#,##0_);t(" UTF8_BAHT "#,##0)" ),
+ EXC_NUMFMT_STRING( 64, "t" UTF8_BAHT "#,##0_);[RED]t(" UTF8_BAHT "#,##0)" ),
+ EXC_NUMFMT_STRING( 65, "t" UTF8_BAHT "#,##0.00_);t(" UTF8_BAHT "#,##0.00)" ),
+ EXC_NUMFMT_STRING( 66, "t" UTF8_BAHT "#,##0.00_);[RED]t(" UTF8_BAHT "#,##0.00)" ),
+ EXC_NUMFMT_STRING( 67, "t0%" ),
+ EXC_NUMFMT_STRING( 68, "t0.00%" ),
+ EXC_NUMFMT_STRING( 69, "t# ?/?" ),
+ EXC_NUMFMT_STRING( 70, "t# ?\?/?\?" ),
+ EXC_NUMFMT_STRING( 71, "tD/M/EE" ),
+ EXC_NUMFMT_STRING( 72, "tD-MMM-E" ),
+ EXC_NUMFMT_STRING( 73, "tD-MMM" ),
+ EXC_NUMFMT_STRING( 74, "tMMM-E" ),
+ EXC_NUMFMT_STRING( 75, "th:mm" ),
+ EXC_NUMFMT_STRING( 76, "th:mm:ss" ),
+ EXC_NUMFMT_STRING( 77, "tD/M/EE h:mm" ),
+ EXC_NUMFMT_STRING( 78, "tmm:ss" ),
+ EXC_NUMFMT_STRING( 79, "t[h]:mm:ss" ),
+ EXC_NUMFMT_STRING( 80, "tmm:ss.0" ),
+ EXC_NUMFMT_STRING( 81, "D/M/E" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+#undef EXC_NUMFMT_ENDTABLE
+#undef EXC_NUMFMT_REUSE
+#undef EXC_NUMFMT_OFFSET
+#undef EXC_NUMFMT_STRING
+
+/** Specifies a number format table for a specific language. */
+struct XclBuiltInFormatTable
+{
+ LanguageType meLanguage; /// The language of this table.
+ LanguageType meParentLang; /// The language of the parent table.
+ const XclBuiltInFormat* mpFormats; /// The number format table.
+};
+
+const XclBuiltInFormatTable spBuiltInFormatTables[] =
+{ // language parent language format table
+ { LANGUAGE_DONTKNOW, LANGUAGE_NONE, spBuiltInFormats_DONTKNOW },
+
+ { LANGUAGE_ENGLISH, LANGUAGE_DONTKNOW, spBuiltInFormats_ENGLISH },
+ { LANGUAGE_ENGLISH_UK, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_UK },
+ { LANGUAGE_ENGLISH_EIRE, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_EIRE },
+ { LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_US },
+ { LANGUAGE_ENGLISH_CAN, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_CAN },
+ { LANGUAGE_ENGLISH_AUS, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_AUS },
+ { LANGUAGE_ENGLISH_SAFRICA, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_SAFRICA },
+ { LANGUAGE_ENGLISH_NZ, LANGUAGE_ENGLISH_AUS, nullptr },
+
+ { PRV_LANGUAGE_FRENCH_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_FRENCH },
+ { LANGUAGE_FRENCH, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_FRANCE },
+ { LANGUAGE_FRENCH_CANADIAN, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_CANADIAN },
+ { LANGUAGE_FRENCH_SWISS, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_SWISS },
+ { LANGUAGE_FRENCH_BELGIAN, LANGUAGE_FRENCH, spBuiltInFormats_FRENCH_BELGIAN },
+ { LANGUAGE_FRENCH_LUXEMBOURG, LANGUAGE_FRENCH, nullptr },
+ { LANGUAGE_FRENCH_MONACO, LANGUAGE_FRENCH, nullptr },
+
+ { PRV_LANGUAGE_GERMAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_GERMAN },
+ { LANGUAGE_GERMAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_GERMANY },
+ { LANGUAGE_GERMAN_AUSTRIAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_AUSTRIAN },
+ { LANGUAGE_GERMAN_SWISS, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_SWISS },
+ { LANGUAGE_GERMAN_LUXEMBOURG, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LUXEMBOURG },
+ { LANGUAGE_GERMAN_LIECHTENSTEIN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LIECHTENSTEIN },
+
+ { LANGUAGE_ITALIAN, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_ITALY },
+ { LANGUAGE_ITALIAN_SWISS, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_SWISS },
+
+ { LANGUAGE_SWEDISH, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_SWEDEN },
+ { LANGUAGE_SWEDISH_FINLAND, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_FINLAND },
+
+ { PRV_LANGUAGE_ASIAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_ASIAN },
+ { LANGUAGE_JAPANESE, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_JAPANESE },
+ { LANGUAGE_KOREAN, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_KOREAN },
+ { LANGUAGE_CHINESE_SIMPLIFIED, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_SIMPLIFIED },
+ { LANGUAGE_CHINESE_TRADITIONAL, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_TRADITIONAL },
+
+ { LANGUAGE_HEBREW, LANGUAGE_DONTKNOW, spBuiltInFormats_HEBREW },
+ { LANGUAGE_THAI, LANGUAGE_DONTKNOW, spBuiltInFormats_THAI }
+};
+
+} // namespace
+
+XclNumFmtBuffer::XclNumFmtBuffer( const XclRoot& rRoot ) :
+ meSysLang( rRoot.GetSysLanguage() ),
+ mnStdScNumFmt( rRoot.GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
+{
+ // *** insert default formats (BIFF5+ only)***
+
+ if( rRoot.GetBiff() >= EXC_BIFF5 )
+ InsertBuiltinFormats();
+}
+
+void XclNumFmtBuffer::InitializeImport()
+{
+ maFmtMap.clear();
+}
+
+void XclNumFmtBuffer::InsertFormat( sal_uInt16 nXclNumFmt, const OUString& rFormat )
+{
+ XclNumFmt& rNumFmt = maFmtMap[ nXclNumFmt ];
+ rNumFmt.maFormat = rFormat;
+ // #i62053# rFormat may be an empty string, meOffset must be initialized
+ rNumFmt.meOffset = NF_NUMBER_STANDARD;
+ rNumFmt.meLanguage = LANGUAGE_SYSTEM;
+}
+
+void XclNumFmtBuffer::InsertBuiltinFormats()
+{
+ // build a map containing tables for all languages
+ typedef ::std::map< LanguageType, const XclBuiltInFormatTable* > XclBuiltInMap;
+ XclBuiltInMap aBuiltInMap;
+ for(const auto &rTable : spBuiltInFormatTables)
+ aBuiltInMap[ rTable.meLanguage ] = &rTable;
+
+ // build a list of table pointers for the current language, with all parent tables
+ typedef ::std::vector< const XclBuiltInFormatTable* > XclBuiltInVec;
+ XclBuiltInVec aBuiltInVec;
+ for( XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( meSysLang ), aMEnd = aBuiltInMap.end();
+ aMIt != aMEnd; aMIt = aBuiltInMap.find( aMIt->second->meParentLang ) )
+ aBuiltInVec.push_back( aMIt->second );
+ // language not supported
+ if( aBuiltInVec.empty() )
+ {
+ SAL_WARN("sc", "XclNumFmtBuffer::InsertBuiltinFormats - language not supported (#i29949#) 0x" << std::hex << meSysLang );
+ XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( LANGUAGE_DONTKNOW );
+ OSL_ENSURE( aMIt != aBuiltInMap.end(), "XclNumFmtBuffer::InsertBuiltinFormats - default map not found" );
+ if( aMIt != aBuiltInMap.end() )
+ aBuiltInVec.push_back( aMIt->second );
+ }
+
+ // insert the default formats in the format map, from root parent to system language
+ std::map< sal_uInt16, sal_uInt16 > aReuseMap;
+ for( XclBuiltInVec::reverse_iterator aVIt = aBuiltInVec.rbegin(), aVEnd = aBuiltInVec.rend(); aVIt != aVEnd; ++aVIt )
+ {
+ // put LANGUAGE_SYSTEM for all entries in default table
+ LanguageType eLang = ((*aVIt)->meLanguage == LANGUAGE_DONTKNOW) ? LANGUAGE_SYSTEM : meSysLang;
+ for( const XclBuiltInFormat* pBuiltIn = (*aVIt)->mpFormats; pBuiltIn && (pBuiltIn->mnXclNumFmt != EXC_FORMAT_NOTFOUND); ++pBuiltIn )
+ {
+ XclNumFmt& rNumFmt = maFmtMap[ pBuiltIn->mnXclNumFmt ];
+
+ rNumFmt.meOffset = pBuiltIn->meOffset;
+ rNumFmt.meLanguage = eLang;
+
+ if( pBuiltIn->mpFormat )
+ rNumFmt.maFormat = OUString( pBuiltIn->mpFormat, strlen(pBuiltIn->mpFormat), RTL_TEXTENCODING_UTF8 );
+ else
+ rNumFmt.maFormat.clear();
+
+ if( pBuiltIn->meOffset == PRV_NF_INDEX_REUSE )
+ aReuseMap[ pBuiltIn->mnXclNumFmt ] = pBuiltIn->mnXclReuseFmt;
+ else
+ aReuseMap.erase( pBuiltIn->mnXclNumFmt );
+ }
+ }
+
+ // copy reused number formats
+ for( const auto& [rXclNumFmt, rXclReuseFmt] : aReuseMap )
+ maFmtMap[ rXclNumFmt ] = maFmtMap[ rXclReuseFmt ];
+}
+
+// Cell formatting data (XF) ==================================================
+
+XclCellProt::XclCellProt() :
+ mbLocked( true ), // default in Excel and Calc
+ mbHidden( false )
+{
+}
+
+bool operator==( const XclCellProt& rLeft, const XclCellProt& rRight )
+{
+ return (rLeft.mbLocked == rRight.mbLocked) && (rLeft.mbHidden == rRight.mbHidden);
+}
+
+XclCellAlign::XclCellAlign() :
+ mnHorAlign( EXC_XF_HOR_GENERAL ),
+ mnVerAlign( EXC_XF_VER_BOTTOM ),
+ mnOrient( EXC_ORIENT_NONE ),
+ mnTextDir( EXC_XF_TEXTDIR_CONTEXT ),
+ mnRotation( EXC_ROT_NONE ),
+ mnIndent( 0 ),
+ mbLineBreak( false ),
+ mbShrink( false )
+{
+}
+
+SvxCellHorJustify XclCellAlign::GetScHorAlign() const
+{
+ SvxCellHorJustify eHorJust = SvxCellHorJustify::Standard;
+ switch( mnHorAlign )
+ {
+ case EXC_XF_HOR_GENERAL: eHorJust = SvxCellHorJustify::Standard; break;
+ case EXC_XF_HOR_LEFT: eHorJust = SvxCellHorJustify::Left; break;
+ case EXC_XF_HOR_CENTER_AS:
+ case EXC_XF_HOR_CENTER: eHorJust = SvxCellHorJustify::Center; break;
+ case EXC_XF_HOR_RIGHT: eHorJust = SvxCellHorJustify::Right; break;
+ case EXC_XF_HOR_FILL: eHorJust = SvxCellHorJustify::Repeat; break;
+ case EXC_XF_HOR_JUSTIFY:
+ case EXC_XF_HOR_DISTRIB: eHorJust = SvxCellHorJustify::Block; break;
+ default: OSL_FAIL( "XclCellAlign::GetScHorAlign - unknown horizontal alignment" );
+ }
+ return eHorJust;
+}
+
+SvxCellJustifyMethod XclCellAlign::GetScHorJustifyMethod() const
+{
+ return (mnHorAlign == EXC_XF_HOR_DISTRIB) ? SvxCellJustifyMethod::Distribute : SvxCellJustifyMethod::Auto;
+}
+
+SvxCellVerJustify XclCellAlign::GetScVerAlign() const
+{
+ SvxCellVerJustify eVerJust = SvxCellVerJustify::Standard;
+ switch( mnVerAlign )
+ {
+ case EXC_XF_VER_TOP: eVerJust = SvxCellVerJustify::Top; break;
+ case EXC_XF_VER_CENTER: eVerJust = SvxCellVerJustify::Center; break;
+ case EXC_XF_VER_BOTTOM: eVerJust = SvxCellVerJustify::Standard; break;
+ case EXC_XF_VER_JUSTIFY:
+ case EXC_XF_VER_DISTRIB: eVerJust = SvxCellVerJustify::Block; break;
+ default: OSL_FAIL( "XclCellAlign::GetScVerAlign - unknown vertical alignment" );
+ }
+ return eVerJust;
+}
+
+SvxCellJustifyMethod XclCellAlign::GetScVerJustifyMethod() const
+{
+ return (mnVerAlign == EXC_XF_VER_DISTRIB) ? SvxCellJustifyMethod::Distribute : SvxCellJustifyMethod::Auto;
+}
+
+SvxFrameDirection XclCellAlign::GetScFrameDir() const
+{
+ SvxFrameDirection eFrameDir = SvxFrameDirection::Environment;
+ switch( mnTextDir )
+ {
+ case EXC_XF_TEXTDIR_CONTEXT: eFrameDir = SvxFrameDirection::Environment; break;
+ case EXC_XF_TEXTDIR_LTR: eFrameDir = SvxFrameDirection::Horizontal_LR_TB; break;
+ case EXC_XF_TEXTDIR_RTL: eFrameDir = SvxFrameDirection::Horizontal_RL_TB; break;
+ default: OSL_FAIL( "XclCellAlign::GetScFrameDir - unknown CTL text direction" );
+ }
+ return eFrameDir;
+}
+
+void XclCellAlign::SetScHorAlign( SvxCellHorJustify eHorJust )
+{
+ switch( eHorJust )
+ {
+ case SvxCellHorJustify::Standard: mnHorAlign = EXC_XF_HOR_GENERAL; break;
+ case SvxCellHorJustify::Left: mnHorAlign = EXC_XF_HOR_LEFT; break;
+ case SvxCellHorJustify::Center: mnHorAlign = EXC_XF_HOR_CENTER; break;
+ case SvxCellHorJustify::Right: mnHorAlign = EXC_XF_HOR_RIGHT; break;
+ case SvxCellHorJustify::Block: mnHorAlign = EXC_XF_HOR_JUSTIFY; break;
+ case SvxCellHorJustify::Repeat: mnHorAlign = EXC_XF_HOR_FILL; break;
+ default: mnHorAlign = EXC_XF_HOR_GENERAL;
+ OSL_FAIL( "XclCellAlign::SetScHorAlign - unknown horizontal alignment" );
+ }
+}
+
+void XclCellAlign::SetScVerAlign( SvxCellVerJustify eVerJust )
+{
+ switch( eVerJust )
+ {
+ case SvxCellVerJustify::Standard: mnVerAlign = EXC_XF_VER_BOTTOM; break;
+ case SvxCellVerJustify::Top: mnVerAlign = EXC_XF_VER_TOP; break;
+ case SvxCellVerJustify::Center: mnVerAlign = EXC_XF_VER_CENTER; break;
+ case SvxCellVerJustify::Bottom: mnVerAlign = EXC_XF_VER_BOTTOM; break;
+ default: mnVerAlign = EXC_XF_VER_BOTTOM;
+ OSL_FAIL( "XclCellAlign::SetScVerAlign - unknown vertical alignment" );
+ }
+}
+
+void XclCellAlign::SetScFrameDir( SvxFrameDirection eFrameDir )
+{
+ switch( eFrameDir )
+ {
+ case SvxFrameDirection::Environment: mnTextDir = EXC_XF_TEXTDIR_CONTEXT; break;
+ case SvxFrameDirection::Horizontal_LR_TB: mnTextDir = EXC_XF_TEXTDIR_LTR; break;
+ case SvxFrameDirection::Horizontal_RL_TB: mnTextDir = EXC_XF_TEXTDIR_RTL; break;
+ default: mnTextDir = EXC_XF_TEXTDIR_CONTEXT;
+ OSL_FAIL( "XclCellAlign::SetScFrameDir - unknown CTL text direction" );
+ }
+}
+
+bool operator==( const XclCellAlign& rLeft, const XclCellAlign& rRight )
+{
+ return
+ (rLeft.mnHorAlign == rRight.mnHorAlign) && (rLeft.mnVerAlign == rRight.mnVerAlign) &&
+ (rLeft.mnTextDir == rRight.mnTextDir) && (rLeft.mnOrient == rRight.mnOrient) &&
+ (rLeft.mnRotation == rRight.mnRotation) && (rLeft.mnIndent == rRight.mnIndent) &&
+ (rLeft.mbLineBreak == rRight.mbLineBreak) && (rLeft.mbShrink == rRight.mbShrink);
+}
+
+XclCellBorder::XclCellBorder() :
+ mnLeftColor( 0 ),
+ mnRightColor( 0 ),
+ mnTopColor( 0 ),
+ mnBottomColor( 0 ),
+ mnDiagColor( 0 ),
+ mnLeftLine( EXC_LINE_NONE ),
+ mnRightLine( EXC_LINE_NONE ),
+ mnTopLine( EXC_LINE_NONE ),
+ mnBottomLine( EXC_LINE_NONE ),
+ mnDiagLine( EXC_LINE_NONE ),
+ mbDiagTLtoBR( false ),
+ mbDiagBLtoTR( false )
+{
+}
+
+bool operator==( const XclCellBorder& rLeft, const XclCellBorder& rRight )
+{
+ return
+ (rLeft.mnLeftColor == rRight.mnLeftColor) && (rLeft.mnRightColor == rRight.mnRightColor) &&
+ (rLeft.mnTopColor == rRight.mnTopColor) && (rLeft.mnBottomColor == rRight.mnBottomColor) &&
+ (rLeft.mnLeftLine == rRight.mnLeftLine) && (rLeft.mnRightLine == rRight.mnRightLine) &&
+ (rLeft.mnTopLine == rRight.mnTopLine) && (rLeft.mnBottomLine == rRight.mnBottomLine) &&
+ (rLeft.mnDiagColor == rRight.mnDiagColor) && (rLeft.mnDiagLine == rRight.mnDiagLine) &&
+ (rLeft.mbDiagTLtoBR == rRight.mbDiagTLtoBR) && (rLeft.mbDiagBLtoTR == rRight.mbDiagBLtoTR);
+}
+
+XclCellArea::XclCellArea() :
+ mnForeColor( EXC_COLOR_WINDOWTEXT ),
+ mnBackColor( EXC_COLOR_WINDOWBACK ),
+ mnPattern( EXC_PATT_NONE )
+{
+}
+
+XclCellArea::XclCellArea(sal_uInt8 nPattern) :
+ mnForeColor( EXC_COLOR_WINDOWTEXT ),
+ mnBackColor( EXC_COLOR_WINDOWBACK ),
+ mnPattern( nPattern )
+{
+}
+
+bool XclCellArea::IsTransparent() const
+{
+ return (mnPattern == EXC_PATT_NONE) && (mnBackColor == EXC_COLOR_WINDOWBACK);
+}
+
+bool operator==( const XclCellArea& rLeft, const XclCellArea& rRight )
+{
+ return
+ (rLeft.mnForeColor == rRight.mnForeColor) && (rLeft.mnBackColor == rRight.mnBackColor) &&
+ (rLeft.mnPattern == rRight.mnPattern);
+}
+
+XclXFBase::XclXFBase( bool bCellXF ) :
+ mnParent( bCellXF ? EXC_XF_DEFAULTSTYLE : EXC_XF_STYLEPARENT ),
+ mbCellXF( bCellXF )
+{
+ SetAllUsedFlags( false );
+}
+
+XclXFBase::~XclXFBase()
+{
+}
+
+void XclXFBase::SetAllUsedFlags( bool bUsed )
+{
+ mbProtUsed = mbFontUsed = mbFmtUsed = mbAlignUsed = mbBorderUsed = mbAreaUsed = bUsed;
+}
+
+bool XclXFBase::HasUsedFlags() const
+{
+ return mbProtUsed || mbFontUsed || mbFmtUsed || mbAlignUsed || mbBorderUsed || mbAreaUsed;
+}
+
+bool XclXFBase::Equals( const XclXFBase& rCmp ) const
+{
+ return
+ (mbCellXF == rCmp.mbCellXF) && (mnParent == rCmp.mnParent) &&
+ (mbProtUsed == rCmp.mbProtUsed) && (mbFontUsed == rCmp.mbFontUsed) &&
+ (mbFmtUsed == rCmp.mbFmtUsed) && (mbAlignUsed == rCmp.mbAlignUsed) &&
+ (mbBorderUsed == rCmp.mbBorderUsed) && (mbAreaUsed == rCmp.mbAreaUsed);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltoolbar.cxx b/sc/source/filter/excel/xltoolbar.cxx
new file mode 100644
index 0000000000..efb54925bd
--- /dev/null
+++ b/sc/source/filter/excel/xltoolbar.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/.
+ */
+#include "xltoolbar.hxx"
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/ref.hxx>
+#include <map>
+
+using namespace com::sun::star;
+
+typedef std::map< sal_Int16, OUString > IdToString;
+
+namespace {
+
+class MSOExcelCommandConvertor : public MSOCommandConvertor
+{
+ IdToString msoToOOcmd;
+ IdToString tcidToOOcmd;
+public:
+ MSOExcelCommandConvertor();
+ virtual OUString MSOCommandToOOCommand( sal_Int16 msoCmd ) override;
+ virtual OUString MSOTCIDToOOCommand( sal_Int16 key ) override;
+};
+
+}
+
+MSOExcelCommandConvertor::MSOExcelCommandConvertor()
+{
+/*
+ // mso command id to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ msoToOOcmd[ 0x20b ] = ".uno:CloseDoc";
+ msoToOOcmd[ 0x50 ] = ".uno:Open";
+
+ // mso tcid to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ tcidToOOcmd[ 0x9d9 ] = ".uno:Print";
+*/
+}
+
+OUString MSOExcelCommandConvertor::MSOCommandToOOCommand( sal_Int16 key )
+{
+ OUString sResult;
+ IdToString::iterator it = msoToOOcmd.find( key );
+ if ( it != msoToOOcmd.end() )
+ sResult = it->second;
+ return sResult;
+}
+
+OUString MSOExcelCommandConvertor::MSOTCIDToOOCommand( sal_Int16 key )
+{
+ OUString sResult;
+ IdToString::iterator it = tcidToOOcmd.find( key );
+ if ( it != tcidToOOcmd.end() )
+ sResult = it->second;
+ return sResult;
+}
+
+CTBS::CTBS() : bSignature(0), bVersion(0), reserved1(0), reserved2(0), reserved3(0), ctb(0), ctbViews(0), ictbView(0)
+{
+}
+
+ScCTB::ScCTB(sal_uInt16 nNum ) : nViews( nNum ), ectbid(0)
+{
+}
+
+bool ScCTB::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ tb.Read( rS );
+
+ {
+ const size_t nMinRecordSize = 20; // TBVisualData reads 20 bytes
+ const size_t nMaxPossibleRecords = rS.remainingSize() / nMinRecordSize;
+ if (nViews > nMaxPossibleRecords)
+ {
+ SAL_WARN("sc.filter", "ScCTB::Read more entries claimed than stream could contain");
+ return false;
+ }
+ }
+
+ for ( sal_uInt16 index = 0; index < nViews; ++index )
+ {
+ TBVisualData aVisData;
+ aVisData.Read( rS );
+ rVisualData.push_back( aVisData );
+ }
+ rS.ReadUInt32( ectbid );
+
+ sal_Int16 nCL = tb.getcCL();
+ if (nCL > 0)
+ {
+ auto nIndexes = o3tl::make_unsigned(nCL);
+
+ const size_t nMinRecordSize = 11; // ScTBC's TBCHeader reads min 11 bytes
+ const size_t nMaxPossibleRecords = rS.remainingSize() / nMinRecordSize;
+ if (nIndexes > nMaxPossibleRecords)
+ {
+ SAL_WARN("sc.filter", "ScCTB::Read more entries claimed than stream could contain");
+ return false;
+ }
+
+ for (decltype(nIndexes) index = 0; index < nIndexes; ++index)
+ {
+ ScTBC aTBC;
+ aTBC.Read( rS );
+ rTBC.push_back( aTBC );
+ }
+ }
+
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void ScCTB::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScCTB -- dump\n", nOffSet );
+ indent_printf( fp, " nViews 0x%x\n", nViews);
+ tb.Print( fp );
+
+ sal_Int32 counter = 0;
+ for ( auto& rItem : rVisualData )
+ {
+ indent_printf( fp, " TBVisualData [%d]\n", counter++ );
+ Indent b;
+ rItem.Print( fp );
+ }
+ indent_printf( fp, " ectbid 0x%x\n", ectbid);
+ counter = 0;
+ for ( auto& rItem : rTBC )
+ {
+ indent_printf( fp, " ScTBC [%d]\n", counter++);
+ Indent c;
+ rItem.Print( fp );
+ }
+}
+#endif
+
+bool ScCTB::IsMenuToolbar() const
+{
+ return tb.IsMenuToolbar();
+}
+
+bool ScCTB::ImportMenuTB( ScCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& xMenuDesc, CustomToolBarImportHelper& helper )
+{
+ for ( auto& rItem : rTBC )
+ {
+ if ( !rItem.ImportToolBarControl( rWrapper, xMenuDesc, helper, IsMenuToolbar() ) )
+ return false;
+ }
+ return true;
+}
+
+bool ScCTB::ImportCustomToolBar( ScCTBWrapper& rWrapper, CustomToolBarImportHelper& helper )
+{
+
+ bool bRes = false;
+ try
+ {
+ if ( !tb.IsEnabled() )
+ return true; // didn't fail, just ignoring
+
+ // Create default setting
+ uno::Reference< container::XIndexContainer > xIndexContainer( helper.getCfgManager()->createSettings(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xIndexContainer, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xIndexContainer, uno::UNO_QUERY_THROW );
+ WString& name = tb.getName();
+ // set UI name for toolbar
+ xProps->setPropertyValue("UIName", uno::Any( name.getString() ) );
+
+ OUString sToolBarName = "private:resource/toolbar/custom_" + name.getString();
+ for ( auto& rItem : rTBC )
+ {
+ if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, helper, IsMenuToolbar() ) )
+ return false;
+ }
+
+ helper.getCfgManager()->insertSettings( sToolBarName, xIndexAccess );
+ helper.applyIcons();
+
+ uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager()->getImageManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ xPersistence.set( helper.getCfgManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ bRes = true;
+ }
+ catch( uno::Exception& )
+ {
+ bRes = false;
+ }
+ return bRes;
+}
+bool CTBS::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUChar( bSignature ).ReadUChar( bVersion ).ReadUInt16( reserved1 ).ReadUInt16( reserved2 ).ReadUInt16( reserved3 ).ReadUInt16( ctb ).ReadUInt16( ctbViews ).ReadUInt16( ictbView );
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void CTBS::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] CTBS -- dump\n", nOffSet );
+
+ indent_printf( fp, " bSignature 0x%x\n", bSignature);
+ indent_printf( fp, " bVersion 0x%x\n", bVersion);
+
+ indent_printf( fp, " reserved1 0x%x\n", reserved1 );
+ indent_printf( fp, " reserved2 0x%x\n", reserved2 );
+ indent_printf( fp, " reserved3 0x%x\n", reserved3 );
+
+ indent_printf( fp, " ctb 0x%x\n", ctb );
+ indent_printf( fp, " ctbViews 0x%x\n", ctbViews );
+ indent_printf( fp, " ictbView 0x%x\n", ictbView );
+}
+#endif
+
+ScTBC::ScTBC()
+{
+}
+
+bool
+ScTBC::Read(SvStream &rS)
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ if ( !tbch.Read( rS ) )
+ return false;
+ sal_uInt16 tcid = tbch.getTcID();
+ sal_uInt8 tct = tbch.getTct();
+ if ( ( tcid != 0x0001 && tcid != 0x06CC && tcid != 0x03D8 && tcid != 0x03EC && tcid != 0x1051 ) && ( ( tct > 0 && tct < 0x0B ) || ( ( tct > 0x0B && tct < 0x10 ) || tct == 0x15 ) ) )
+ {
+ tbcCmd = std::make_shared<TBCCmd>();
+ if ( ! tbcCmd->Read( rS ) )
+ return false;
+ }
+ if ( tct != 0x16 )
+ {
+ tbcd = std::make_shared<TBCData>( tbch );
+ if ( !tbcd->Read( rS ) )
+ return false;
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+ScTBC::Print(FILE* fp)
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScTBC -- dump\n", nOffSet );
+ tbch.Print( fp );
+ if ( tbcCmd.get() )
+ tbcCmd->Print( fp );
+ if ( tbcd.get() )
+ tbcd->Print( fp );
+}
+#endif
+
+bool ScTBC::ImportToolBarControl( ScCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuToolbar )
+{
+ // how to identify built-in-command ?
+// bool bBuiltin = false;
+ if ( tbcd )
+ {
+ std::vector< css::beans::PropertyValue > props;
+ bool bBeginGroup = false;
+ tbcd->ImportToolBarControl( helper, props, bBeginGroup, bIsMenuToolbar );
+ TBCMenuSpecific* pMenu = tbcd->getMenuSpecific();
+ if ( pMenu )
+ {
+ // search for ScCTB with the appropriate name ( it contains the
+ // menu items, although we cannot import ( or create ) a menu on
+ // a custom toolbar we can import the menu items in a separate
+ // toolbar ( better than nothing )
+ ScCTB* pCustTB = rWrapper.GetCustomizationData( pMenu->Name() );
+ if ( pCustTB )
+ {
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xMenuDesc = new comphelper::IndexedPropertyValuesContainer();
+ if ( !pCustTB->ImportMenuTB( rWrapper, xMenuDesc, helper ) )
+ return false;
+ if ( !bIsMenuToolbar )
+ {
+ if ( !helper.createMenu( pMenu->Name(), xMenuDesc ) )
+ return false;
+ }
+ else
+ {
+ beans::PropertyValue aProp;
+ aProp.Name = "ItemDescriptorContainer";
+ aProp.Value <<= uno::Reference< container::XIndexContainer >(xMenuDesc);
+ props.push_back( aProp );
+ }
+ }
+ }
+
+ if ( bBeginGroup )
+ {
+ // insert spacer
+ uno::Sequence sProps{ comphelper::makePropertyValue("Type",
+ ui::ItemType::SEPARATOR_LINE) };
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( sProps ) );
+ }
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( comphelper::containerToSequence(props) ) );
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+TBCCmd::Print(FILE* fp)
+{
+ Indent a;
+ indent_printf( fp, " TBCCmd -- dump\n" );
+ indent_printf( fp, " cmdID 0x%x\n", cmdID );
+ indent_printf( fp, " A ( fHideDrawing ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " B ( reserved - ignored ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " cmdType 0x%x\n", cmdType );
+ indent_printf( fp, " C ( reserved - ignored ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " reserved3 0x%x\n", reserved3 );
+}
+#endif
+
+bool TBCCmd::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUInt16( cmdID );
+ sal_uInt16 temp;
+ rS.ReadUInt16( temp );
+ A = (temp & 0x8000 ) == 0x8000;
+ B = (temp & 0x4000) == 0x4000;
+ cmdType = ( temp & 0x3E00 ) >> 9;
+ C = ( temp & 0x100 ) == 0x100;
+ reserved3 = ( temp & 0xFF );
+ return true;
+}
+
+ScCTBWrapper::ScCTBWrapper()
+{
+}
+
+ScCTBWrapper::~ScCTBWrapper()
+{
+}
+
+bool
+ScCTBWrapper::Read( SvStream &rS)
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ if (!ctbSet.Read(rS))
+ return false;
+
+ //ScCTB is 1 TB which is min 15bytes, nViews TBVisualData which is min 20bytes
+ //and one 32bit number (4 bytes)
+ const size_t nMinRecordSize = 19 + o3tl::sanitizing_min(ctbSet.ctbViews * 20, 0);
+ const size_t nMaxPossibleRecords = rS.remainingSize()/nMinRecordSize;
+ if (ctbSet.ctb > nMaxPossibleRecords)
+ return false;
+
+ for ( sal_uInt16 index = 0; index < ctbSet.ctb; ++index )
+ {
+ ScCTB aCTB( ctbSet.ctbViews );
+ if ( !aCTB.Read( rS ) )
+ return false;
+ rCTB.push_back( aCTB );
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+ScCTBWrapper::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScCTBWrapper -- dump\n", nOffSet );
+ ctbSet.Print( fp );
+ for ( auto& rItem : rCTB )
+ {
+ Indent b;
+ rItem.Print( fp );
+ }
+}
+#endif
+
+ScCTB* ScCTBWrapper::GetCustomizationData( const OUString& sTBName )
+{
+ ScCTB* pCTB = nullptr;
+ auto it = std::find_if(rCTB.begin(), rCTB.end(), [&sTBName](ScCTB& rItem) { return rItem.GetName() == sTBName; });
+ if (it != rCTB.end())
+ pCTB = &(*it);
+ return pCTB;
+}
+
+void ScCTBWrapper::ImportCustomToolBar( SfxObjectShell& rDocSh )
+{
+ if(rCTB.empty())
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xAppCfgSupp( ui::theModuleUIConfigurationManagerSupplier::get(xContext) );
+
+ for ( auto& rItem : rCTB )
+ {
+ // for each customtoolbar
+ CustomToolBarImportHelper helper( rDocSh, xAppCfgSupp->getUIConfigurationManager( "com.sun.star.sheet.SpreadsheetDocument" ) );
+ helper.setMSOCommandMap( new MSOExcelCommandConvertor() );
+ // Ignore menu toolbars, excel doesn't ( afaics ) store
+ // menu customizations ( but you can have menus in a customtoolbar
+ // such menus will be dealt with when they are encountered
+ // as part of importing the appropriate MenuSpecific toolbar control )
+
+ if ( !rItem.IsMenuToolbar() && !rItem.ImportCustomToolBar( *this, helper ) )
+ return;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltoolbar.hxx b/sc/source/filter/excel/xltoolbar.hxx
new file mode 100644
index 0000000000..f7da78b2ff
--- /dev/null
+++ b/sc/source/filter/excel/xltoolbar.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <filter/msfilter/mstoolbar.hxx>
+
+namespace com::sun::star::container { class XIndexContainer; }
+
+class ScCTBWrapper;
+// hmm I don't normally use these packed structures
+// but... hey always good to do something different
+class TBCCmd : public TBBase
+{
+public:
+ TBCCmd() : cmdID(0), A(false), B(false), cmdType(0), C(false), reserved3(0)
+ {}
+ sal_uInt16 cmdID;
+ bool A:1;
+ bool B:1;
+ sal_uInt16 cmdType:5;
+ bool C:1;
+ sal_uInt16 reserved3:8;
+ bool Read( SvStream& rS ) override;
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print(FILE* fp) override;
+#endif
+};
+
+class ScTBC : public TBBase
+{
+ TBCHeader tbch;
+ std::shared_ptr<TBCCmd> tbcCmd; // optional
+ std::shared_ptr<TBCData> tbcd;
+public:
+ ScTBC();
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+ bool ImportToolBarControl( ScCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuBar );
+};
+
+class ScCTB : public TBBase
+{
+ sal_uInt16 nViews;
+ TB tb;
+ std::vector<TBVisualData> rVisualData;
+ sal_uInt32 ectbid;
+ std::vector< ScTBC > rTBC;
+public:
+ explicit ScCTB(sal_uInt16);
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+ bool IsMenuToolbar() const;
+ bool ImportCustomToolBar( ScCTBWrapper&, CustomToolBarImportHelper& );
+ bool ImportMenuTB( ScCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper& );
+ const OUString& GetName() { return tb.getName().getString(); }
+
+};
+
+class CTBS : public TBBase
+{
+public:
+ sal_uInt8 bSignature;
+ sal_uInt8 bVersion;
+ sal_uInt16 reserved1;
+ sal_uInt16 reserved2;
+ sal_uInt16 reserved3;
+ sal_uInt16 ctb;
+ sal_uInt16 ctbViews;
+ sal_uInt16 ictbView;
+ CTBS(const CTBS&);
+ CTBS& operator = ( const CTBS&);
+ CTBS();
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+};
+
+class ScCTBWrapper : public TBBase
+{
+ CTBS ctbSet;
+
+ std::vector< ScCTB > rCTB;
+
+public:
+ ScCTBWrapper();
+ virtual ~ScCTBWrapper() override;
+ bool Read(SvStream &rS) override;
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ void ImportCustomToolBar( SfxObjectShell& rDocSh );
+ ScCTB* GetCustomizationData( const OUString& name );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltools.cxx b/sc/source/filter/excel/xltools.cxx
new file mode 100644
index 0000000000..3a69e7da06
--- /dev/null
+++ b/sc/source/filter/excel/xltools.cxx
@@ -0,0 +1,744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <math.h>
+#include <string_view>
+
+#include <o3tl/unit_conversion.hxx>
+#include <sal/mathconf.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <o3tl/string_view.hxx>
+#include <unotools/fontdefs.hxx>
+#include <filter/msfilter/msvbahelper.hxx>
+#include <xestream.hxx>
+#include <formula/errorcodes.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xlstyle.hxx>
+#include <xlname.hxx>
+#include <xistream.hxx>
+#include <xltools.hxx>
+
+// GUID import/export
+
+XclGuid::XclGuid()
+ : mpnData{}
+{
+}
+
+XclGuid::XclGuid(
+ sal_uInt32 nData1, sal_uInt16 nData2, sal_uInt16 nData3,
+ sal_uInt8 nData41, sal_uInt8 nData42, sal_uInt8 nData43, sal_uInt8 nData44,
+ sal_uInt8 nData45, sal_uInt8 nData46, sal_uInt8 nData47, sal_uInt8 nData48 )
+{
+ // convert to little endian -> makes streaming easy
+ UInt32ToSVBT32( nData1, mpnData );
+ ShortToSVBT16( nData2, mpnData + 4 );
+ ShortToSVBT16( nData3, mpnData + 6 );
+ mpnData[ 8 ] = nData41;
+ mpnData[ 9 ] = nData42;
+ mpnData[ 10 ] = nData43;
+ mpnData[ 11 ] = nData44;
+ mpnData[ 12 ] = nData45;
+ mpnData[ 13 ] = nData46;
+ mpnData[ 14 ] = nData47;
+ mpnData[ 15 ] = nData48;
+}
+
+bool operator==( const XclGuid& rCmp1, const XclGuid& rCmp2 )
+{
+ return ::std::equal( rCmp1.mpnData, std::end( rCmp1.mpnData ), rCmp2.mpnData );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclGuid& rGuid )
+{
+ rStrm.Read( rGuid.mpnData, 16 ); // mpnData always in little endian
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclGuid& rGuid )
+{
+ rStrm.Write( rGuid.mpnData, 16 ); // mpnData already in little endian
+ return rStrm;
+}
+
+// Excel Tools
+
+// GUID's
+const XclGuid XclTools::maGuidStdLink(
+ 0x79EAC9D0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B );
+
+const XclGuid XclTools::maGuidUrlMoniker(
+ 0x79EAC9E0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B );
+
+const XclGuid XclTools::maGuidFileMoniker(
+ 0x00000303, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 );
+
+// numeric conversion
+
+double XclTools::GetDoubleFromRK( sal_Int32 nRKValue )
+{
+ union
+ {
+ double fVal;
+ sal_math_Double smD;
+ };
+ fVal = 0.0;
+
+ if( ::get_flag( nRKValue, EXC_RK_INTFLAG ) )
+ {
+ sal_Int32 nTemp = nRKValue >> 2;
+ ::set_flag< sal_Int32 >( nTemp, 0xE0000000, nRKValue < 0 );
+ fVal = nTemp;
+ }
+ else
+ {
+ smD.w32_parts.msw = nRKValue & EXC_RK_VALUEMASK;
+ }
+
+ if( ::get_flag( nRKValue, EXC_RK_100FLAG ) )
+ fVal /= 100.0;
+
+ return fVal;
+}
+
+bool XclTools::GetRKFromDouble( sal_Int32& rnRKValue, double fValue )
+{
+ double fFrac, fInt;
+
+ // integer
+ fFrac = modf( fValue, &fInt );
+ if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) ) // 2^29
+ {
+ rnRKValue
+ = static_cast<sal_Int32>(
+ static_cast<sal_uInt32>(static_cast<sal_Int32>(fInt)) << 2)
+ | EXC_RK_INT;
+ return true;
+ }
+
+ // integer/100
+ fFrac = modf( fValue * 100.0, &fInt );
+ if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) )
+ {
+ rnRKValue
+ = static_cast<sal_Int32>(
+ static_cast<sal_uInt32>(static_cast<sal_Int32>(fInt)) << 2)
+ | EXC_RK_INT100;
+ return true;
+ }
+
+ // double
+ return false;
+}
+
+Degree100 XclTools::GetScRotation( sal_uInt16 nXclRot, Degree100 nRotForStacked )
+{
+ if( nXclRot == EXC_ROT_STACKED )
+ return nRotForStacked;
+ OSL_ENSURE( nXclRot <= 180, "XclTools::GetScRotation - illegal rotation angle" );
+ return Degree100(static_cast< sal_Int32 >( (nXclRot <= 180) ? (100 * ((nXclRot > 90) ? (450 - nXclRot) : nXclRot)) : 0 ));
+}
+
+sal_uInt8 XclTools::GetXclRotation( Degree100 nScRot )
+{
+ sal_Int32 nXclRot = nScRot.get() / 100;
+ if( (0 <= nXclRot) && (nXclRot <= 90) )
+ return static_cast< sal_uInt8 >( nXclRot );
+ if( nXclRot < 180 )
+ return static_cast< sal_uInt8 >( 270 - nXclRot );
+ if( nXclRot < 270 )
+ return static_cast< sal_uInt8 >( nXclRot - 180 );
+ if( nXclRot < 360 )
+ return static_cast< sal_uInt8 >( 450 - nXclRot );
+ return 0;
+}
+
+sal_uInt8 XclTools::GetXclRotFromOrient( sal_uInt8 nXclOrient )
+{
+ switch( nXclOrient )
+ {
+ case EXC_ORIENT_NONE: return EXC_ROT_NONE;
+ case EXC_ORIENT_STACKED: return EXC_ROT_STACKED;
+ case EXC_ORIENT_90CCW: return EXC_ROT_90CCW;
+ case EXC_ORIENT_90CW: return EXC_ROT_90CW;
+ default: OSL_FAIL( "XclTools::GetXclRotFromOrient - unknown text orientation" );
+ }
+ return EXC_ROT_NONE;
+}
+
+sal_uInt8 XclTools::GetXclOrientFromRot( sal_uInt16 nXclRot )
+{
+ if( nXclRot == EXC_ROT_STACKED )
+ return EXC_ORIENT_STACKED;
+ OSL_ENSURE( nXclRot <= 180, "XclTools::GetXclOrientFromRot - unknown text rotation" );
+ if( (45 < nXclRot) && (nXclRot <= 90) )
+ return EXC_ORIENT_90CCW;
+ if( (135 < nXclRot) && (nXclRot <= 180) )
+ return EXC_ORIENT_90CW;
+ return EXC_ORIENT_NONE;
+}
+
+sal_uInt8 XclTools::GetXclErrorCode( FormulaError nScError )
+{
+ switch( nScError )
+ {
+ case FormulaError::IllegalArgument: return EXC_ERR_VALUE;
+ case FormulaError::IllegalFPOperation: return EXC_ERR_NUM; // maybe DIV/0 or NUM...
+ case FormulaError::DivisionByZero: return EXC_ERR_DIV0;
+ case FormulaError::IllegalParameter: return EXC_ERR_VALUE;
+ case FormulaError::PairExpected: return EXC_ERR_VALUE;
+ case FormulaError::OperatorExpected: return EXC_ERR_VALUE;
+ case FormulaError::VariableExpected: return EXC_ERR_VALUE;
+ case FormulaError::ParameterExpected: return EXC_ERR_VALUE;
+ case FormulaError::NoValue: return EXC_ERR_VALUE;
+ case FormulaError::CircularReference: return EXC_ERR_VALUE;
+ case FormulaError::NoCode: return EXC_ERR_NULL;
+ case FormulaError::NoRef: return EXC_ERR_REF;
+ case FormulaError::NoName: return EXC_ERR_NAME;
+ case FormulaError::NoAddin: return EXC_ERR_NAME;
+ case FormulaError::NoMacro: return EXC_ERR_NAME;
+ case FormulaError::NotAvailable: return EXC_ERR_NA;
+ default: break;
+ }
+ return EXC_ERR_NA;
+}
+
+FormulaError XclTools::GetScErrorCode( sal_uInt8 nXclError )
+{
+ switch( nXclError )
+ {
+ case EXC_ERR_NULL: return FormulaError::NoCode;
+ case EXC_ERR_DIV0: return FormulaError::DivisionByZero;
+ case EXC_ERR_VALUE: return FormulaError::NoValue;
+ case EXC_ERR_REF: return FormulaError::NoRef;
+ case EXC_ERR_NAME: return FormulaError::NoName;
+ case EXC_ERR_NUM: return FormulaError::IllegalFPOperation;
+ case EXC_ERR_NA: return FormulaError::NotAvailable;
+ default: OSL_FAIL( "XclTools::GetScErrorCode - unknown error code" );
+ }
+ return FormulaError::NotAvailable;
+}
+
+double XclTools::ErrorToDouble( sal_uInt8 nXclError )
+{
+ return CreateDoubleError(GetScErrorCode( nXclError ));
+}
+
+XclBoolError XclTools::ErrorToEnum( double& rfDblValue, bool bErrOrBool, sal_uInt8 nValue )
+{
+ XclBoolError eType;
+ if( bErrOrBool )
+ {
+ // error value
+ switch( nValue )
+ {
+ case EXC_ERR_NULL: eType = xlErrNull; break;
+ case EXC_ERR_DIV0: eType = xlErrDiv0; break;
+ case EXC_ERR_VALUE: eType = xlErrValue; break;
+ case EXC_ERR_REF: eType = xlErrRef; break;
+ case EXC_ERR_NAME: eType = xlErrName; break;
+ case EXC_ERR_NUM: eType = xlErrNum; break;
+ case EXC_ERR_NA: eType = xlErrNA; break;
+ default: eType = xlErrUnknown;
+ }
+ rfDblValue = 0.0;
+ }
+ else
+ {
+ // Boolean value
+ eType = nValue ? xlErrTrue : xlErrFalse;
+ rfDblValue = nValue ? 1.0 : 0.0;
+ }
+ return eType;
+}
+
+template <typename N> static N to(double f) { return limit_cast<N>(f + 0.5); }
+
+sal_uInt16 XclTools::GetTwipsFromInch( double fInches )
+{
+ return to<sal_uInt16>(o3tl::convert(fInches, o3tl::Length::in, o3tl::Length::twip));
+}
+
+sal_uInt16 XclTools::GetTwipsFromHmm( sal_Int32 nHmm )
+{
+ return limit_cast<sal_uInt16>(o3tl::convert(nHmm, o3tl::Length::mm100, o3tl::Length::twip));
+}
+
+double XclTools::GetInchFromTwips( sal_Int32 nTwips )
+{
+ return o3tl::convert<double>(nTwips, o3tl::Length::twip, o3tl::Length::in);
+}
+
+double XclTools::GetInchFromHmm( sal_Int32 nHmm )
+{
+ return o3tl::convert<double>(nHmm, o3tl::Length::mm100, o3tl::Length::in);
+}
+
+sal_Int32 XclTools::GetHmmFromInch( double fInches )
+{
+ return to<sal_Int32>(o3tl::convert(fInches, o3tl::Length::in, o3tl::Length::mm100));
+}
+
+sal_Int32 XclTools::GetHmmFromTwips( sal_Int32 nTwips )
+{
+ return limit_cast<sal_Int32>(o3tl::convert(nTwips, o3tl::Length::twip, o3tl::Length::mm100));
+}
+
+sal_uInt16 XclTools::GetScColumnWidth( sal_uInt16 nXclWidth, tools::Long nScCharWidth )
+{
+ double fScWidth = static_cast< double >( nXclWidth ) / 256.0 * nScCharWidth - 0.5;
+ return limit_cast< sal_uInt16 >( fScWidth );
+}
+
+sal_uInt16 XclTools::GetXclColumnWidth( sal_uInt16 nScWidth, tools::Long nScCharWidth )
+{
+ double fXclWidth = ( static_cast< double >( nScWidth ) + 0.5 ) * 256.0 / nScCharWidth;
+ return limit_cast< sal_uInt16 >( fXclWidth );
+}
+
+// takes font height in twips (1/20 pt = 1/1440 in)
+// returns correction value in 1/256th of *digit width* of default font
+double XclTools::GetXclDefColWidthCorrection( tools::Long nXclDefFontHeight )
+{
+ // Excel uses *max digit width of default font* (W) as cell width unit. Also it has 5-pixel
+ // "correction" to cell widths (ECMA-376-1:2016 18.3.1.81): each cell has 1-pixel padding, then
+ // 3 pixels for the border (which may be 1-pixel - hairline - then it will have 2 additional
+ // 1-pixel spacings from each side; or e.g. 2 hairlines with 1-pixel spacing in the middle; or
+ // thick 3-pixel). Obviously, correction size entirely depends on pixel size (and it is actually
+ // different in Excel on monitors with different resolution). Thus actual (displayed/printed)
+ // cell widths consist of X*W+5px; stored in file is the X (or X*256 if 1/256th of digit width
+ // units are used) value.
+ // This formula apparently converts this 5-pixel correction to 1/256th of digit width units.
+ // Looks like it is created from
+ //
+ // 5 * 256 * 1440 * 2.1333 / (96 * max(N-15, 60)) + 50.0
+ //
+ // where 5 - pixel correction; 256 - used to produce 1/256th of digit width; 1440 - used to
+ // convert font height N (in twips) to inches; 2.1333 - an (empirical?) quotient to convert
+ // font *height* into digit *width*; 96 - "standard" monitor resolution (DPI).
+ // Additionally the formula uses 15 (of unknown origin), 60 (minimal font height 3 pt), and
+ // 50.0 (also of unknown origin).
+ //
+ // TODO: convert this to take font digit width directly (and possibly DPI?), to avoid guessing
+ // the digit width and pixel size. Or DPI might stay 96, to not follow Excel dependency on DPI
+ // in addition to used font, and have absolute size of the correction fixed 5/96 in.
+ return 40960.0 / ::std::max( nXclDefFontHeight - 15, tools::Long(60) ) + 50.0;
+}
+
+// formatting
+
+Color XclTools::GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt16 nXclPattern )
+{
+ // 0x00 == 0% transparence (full rPattColor)
+ // 0x80 == 100% transparence (full rBackColor)
+ static const sal_uInt8 pnRatioTable[] =
+ {
+ 0x80, 0x00, 0x40, 0x20, 0x60, 0x40, 0x40, 0x40, // 00 - 07
+ 0x40, 0x40, 0x20, 0x60, 0x60, 0x60, 0x60, 0x48, // 08 - 15
+ 0x50, 0x70, 0x78 // 16 - 18
+ };
+ return (nXclPattern < SAL_N_ELEMENTS( pnRatioTable )) ?
+ ScfTools::GetMixedColor( rPattColor, rBackColor, pnRatioTable[ nXclPattern ] ) : rPattColor;
+}
+
+// text encoding
+
+namespace {
+
+const struct XclCodePageEntry
+{
+ sal_uInt16 mnCodePage;
+ rtl_TextEncoding meTextEnc;
+}
+pCodePageTable[] =
+{
+ { 437, RTL_TEXTENCODING_IBM_437 }, // OEM US
+// { 720, RTL_TEXTENCODING_IBM_720 }, // OEM Arabic
+ { 737, RTL_TEXTENCODING_IBM_737 }, // OEM Greek
+ { 775, RTL_TEXTENCODING_IBM_775 }, // OEM Baltic
+ { 850, RTL_TEXTENCODING_IBM_850 }, // OEM Latin I
+ { 852, RTL_TEXTENCODING_IBM_852 }, // OEM Latin II (Central European)
+ { 855, RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic
+ { 857, RTL_TEXTENCODING_IBM_857 }, // OEM Turkish
+// { 858, RTL_TEXTENCODING_IBM_858 }, // OEM Multilingual Latin I with Euro
+ { 860, RTL_TEXTENCODING_IBM_860 }, // OEM Portuguese
+ { 861, RTL_TEXTENCODING_IBM_861 }, // OEM Icelandic
+ { 862, RTL_TEXTENCODING_IBM_862 }, // OEM Hebrew
+ { 863, RTL_TEXTENCODING_IBM_863 }, // OEM Canadian (French)
+ { 864, RTL_TEXTENCODING_IBM_864 }, // OEM Arabic
+ { 865, RTL_TEXTENCODING_IBM_865 }, // OEM Nordic
+ { 866, RTL_TEXTENCODING_IBM_866 }, // OEM Cyrillic (Russian)
+ { 869, RTL_TEXTENCODING_IBM_869 }, // OEM Greek (Modern)
+ { 874, RTL_TEXTENCODING_MS_874 }, // MS Windows Thai
+ { 932, RTL_TEXTENCODING_MS_932 }, // MS Windows Japanese Shift-JIS
+ { 936, RTL_TEXTENCODING_MS_936 }, // MS Windows Chinese Simplified GBK
+ { 949, RTL_TEXTENCODING_MS_949 }, // MS Windows Korean (Wansung)
+ { 950, RTL_TEXTENCODING_MS_950 }, // MS Windows Chinese Traditional BIG5
+ { 1200, RTL_TEXTENCODING_DONTKNOW }, // Unicode (BIFF8) - return *_DONTKNOW to preserve old code page
+ { 1250, RTL_TEXTENCODING_MS_1250 }, // MS Windows Latin II (Central European)
+ { 1251, RTL_TEXTENCODING_MS_1251 }, // MS Windows Cyrillic
+ { 1252, RTL_TEXTENCODING_MS_1252 }, // MS Windows Latin I (BIFF4-BIFF8)
+ { 1253, RTL_TEXTENCODING_MS_1253 }, // MS Windows Greek
+ { 1254, RTL_TEXTENCODING_MS_1254 }, // MS Windows Turkish
+ { 1255, RTL_TEXTENCODING_MS_1255 }, // MS Windows Hebrew
+ { 1256, RTL_TEXTENCODING_MS_1256 }, // MS Windows Arabic
+ { 1257, RTL_TEXTENCODING_MS_1257 }, // MS Windows Baltic
+ { 1258, RTL_TEXTENCODING_MS_1258 }, // MS Windows Vietnamese
+ { 1361, RTL_TEXTENCODING_MS_1361 }, // MS Windows Korean (Johab)
+ { 10000, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman
+ { 32768, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman
+ { 32769, RTL_TEXTENCODING_MS_1252 } // MS Windows Latin I (BIFF2-BIFF3)
+};
+const XclCodePageEntry* const pCodePageTableEnd = std::end(pCodePageTable);
+
+struct XclCodePageEntry_CPPred
+{
+ explicit XclCodePageEntry_CPPred( sal_uInt16 nCodePage ) : mnCodePage( nCodePage ) {}
+ bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.mnCodePage == mnCodePage; }
+ sal_uInt16 mnCodePage;
+};
+
+struct XclCodePageEntry_TEPred
+{
+ explicit XclCodePageEntry_TEPred( rtl_TextEncoding eTextEnc ) : meTextEnc( eTextEnc ) {}
+ bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.meTextEnc == meTextEnc; }
+ rtl_TextEncoding meTextEnc;
+};
+
+} // namespace
+
+rtl_TextEncoding XclTools::GetTextEncoding( sal_uInt16 nCodePage )
+{
+ const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_CPPred( nCodePage ) );
+ if( pEntry == pCodePageTableEnd )
+ {
+ SAL_WARN("sc", "XclTools::GetTextEncoding - unknown code page: 0x" << std::hex << nCodePage );
+ return RTL_TEXTENCODING_DONTKNOW;
+ }
+ return pEntry->meTextEnc;
+}
+
+sal_uInt16 XclTools::GetXclCodePage( rtl_TextEncoding eTextEnc )
+{
+ if( eTextEnc == RTL_TEXTENCODING_UNICODE )
+ return 1200; // for BIFF8
+
+ const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_TEPred( eTextEnc ) );
+ if( pEntry == pCodePageTableEnd )
+ {
+ SAL_WARN("sc", "XclTools::GetXclCodePage - unsupported text encoding: 0x" << std::hex << eTextEnc );
+ return 1252;
+ }
+ return pEntry->mnCodePage;
+}
+
+OUString XclTools::GetXclFontName( const OUString& rFontName )
+{
+ // substitute with MS fonts
+ OUString aNewName = GetSubsFontName(rFontName, SubsFontFlags::ONLYONE | SubsFontFlags::MS);
+ return aNewName.isEmpty() ? rFontName : aNewName;
+}
+
+// built-in defined names
+const char maDefNamePrefix[] = "Excel_BuiltIn_"; /// Prefix for built-in defined names.
+const char maDefNamePrefixXml[] = "_xlnm."; /// Prefix for built-in defined names for OOX
+
+const char* const ppcDefNames[] =
+{
+ "Consolidate_Area",
+ "Auto_Open",
+ "Auto_Close",
+ "Extract",
+ "Database",
+ "Criteria",
+ "Print_Area",
+ "Print_Titles",
+ "Recorder",
+ "Data_Form",
+ "Auto_Activate",
+ "Auto_Deactivate",
+ "Sheet_Title",
+ "_FilterDatabase"
+};
+
+OUString XclTools::GetXclBuiltInDefName( sal_Unicode cBuiltIn )
+{
+ OSL_ENSURE( SAL_N_ELEMENTS( ppcDefNames ) == EXC_BUILTIN_UNKNOWN,
+ "XclTools::GetXclBuiltInDefName - built-in defined name list modified" );
+
+ if( cBuiltIn < SAL_N_ELEMENTS( ppcDefNames ) )
+ return OUString::createFromAscii(ppcDefNames[cBuiltIn]);
+ else
+ return OUString::number(cBuiltIn);
+}
+
+OUString XclTools::GetBuiltInDefName( sal_Unicode cBuiltIn )
+{
+ return maDefNamePrefix + GetXclBuiltInDefName(cBuiltIn);
+}
+
+OUString XclTools::GetBuiltInDefNameXml( sal_Unicode cBuiltIn )
+{
+ return maDefNamePrefixXml + GetXclBuiltInDefName(cBuiltIn);
+}
+
+sal_Unicode XclTools::GetBuiltInDefNameIndex( const OUString& rDefName )
+{
+ sal_Int32 nPrefixLen = 0;
+ if( rDefName.startsWithIgnoreAsciiCase( maDefNamePrefix ) )
+ nPrefixLen = strlen(maDefNamePrefix);
+ else if( rDefName.startsWithIgnoreAsciiCase( maDefNamePrefixXml ) )
+ nPrefixLen = strlen(maDefNamePrefixXml);
+ if( nPrefixLen > 0 )
+ {
+ for( sal_Unicode cBuiltIn = 0; cBuiltIn < EXC_BUILTIN_UNKNOWN; ++cBuiltIn )
+ {
+ OUString aBuiltInName(GetXclBuiltInDefName(cBuiltIn));
+ sal_Int32 nBuiltInLen = aBuiltInName.getLength();
+ if( rDefName.matchIgnoreAsciiCase( aBuiltInName, nPrefixLen ) )
+ {
+ // name can be followed by underline or space character
+ sal_Int32 nNextCharPos = nPrefixLen + nBuiltInLen;
+ sal_Unicode cNextChar = (rDefName.getLength() > nNextCharPos) ? rDefName[nNextCharPos] : '\0';
+ if( (cNextChar == '\0') || (cNextChar == ' ') || (cNextChar == '_') )
+ return cBuiltIn;
+ }
+ }
+ }
+ return EXC_BUILTIN_UNKNOWN;
+}
+
+// built-in style names
+
+const char maStyleNamePrefix1[] = "Excel_BuiltIn_"; /// Prefix for built-in cell style names.
+const char maStyleNamePrefix2[] = "Excel Built-in "; /// Prefix for built-in cell style names from OOX filter.
+
+const char* const ppcStyleNames[] =
+{
+ "", // "Normal" not used directly, but localized "Default"
+ "RowLevel_", // outline level will be appended
+ "ColumnLevel_", // outline level will be appended
+ "Comma",
+ "Currency",
+ "Percent",
+ "Comma_0",
+ "Currency_0",
+ "Hyperlink",
+ "Followed_Hyperlink"
+};
+
+OUString XclTools::GetBuiltInStyleName( sal_uInt8 nStyleId, std::u16string_view rName, sal_uInt8 nLevel )
+{
+ OUString aStyleName;
+
+ if( nStyleId == EXC_STYLE_NORMAL ) // "Normal" becomes "Default" style
+ {
+ aStyleName = ScResId( STR_STYLENAME_STANDARD );
+ }
+ else
+ {
+ OUStringBuffer aBuf(maStyleNamePrefix1);
+ if( nStyleId < SAL_N_ELEMENTS( ppcStyleNames ) )
+ aBuf.appendAscii(ppcStyleNames[nStyleId]);
+ else if (!rName.empty())
+ aBuf.append(rName);
+ else
+ aBuf.append(static_cast<sal_Int32>(nStyleId));
+
+ if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) )
+ aBuf.append(static_cast<sal_Int32>(nLevel+1));
+
+ aStyleName = aBuf.makeStringAndClear();
+ }
+
+ return aStyleName;
+}
+
+bool XclTools::IsBuiltInStyleName( const OUString& rStyleName, sal_uInt8* pnStyleId, sal_Int32* pnNextChar )
+{
+ // "Default" becomes "Normal"
+ if (rStyleName == ScResId(STR_STYLENAME_STANDARD))
+ {
+ if( pnStyleId ) *pnStyleId = EXC_STYLE_NORMAL;
+ if( pnNextChar ) *pnNextChar = rStyleName.getLength();
+ return true;
+ }
+
+ // try the other built-in styles
+ sal_uInt8 nFoundId = 0;
+ sal_Int32 nNextChar = 0;
+
+ sal_Int32 nPrefixLen = 0;
+ if( rStyleName.startsWithIgnoreAsciiCase( maStyleNamePrefix1 ) )
+ nPrefixLen = strlen(maStyleNamePrefix1);
+ else if( rStyleName.startsWithIgnoreAsciiCase( maStyleNamePrefix2 ) )
+ nPrefixLen = strlen(maStyleNamePrefix2);
+ if( nPrefixLen > 0 )
+ {
+ for( sal_uInt8 nId = 0; nId < SAL_N_ELEMENTS( ppcStyleNames ); ++nId )
+ {
+ if( nId != EXC_STYLE_NORMAL )
+ {
+ OUString aShortName = OUString::createFromAscii(ppcStyleNames[nId]);
+ if( rStyleName.matchIgnoreAsciiCase( aShortName, nPrefixLen ) &&
+ (nNextChar < nPrefixLen + aShortName.getLength()))
+ {
+ nFoundId = nId;
+ nNextChar = nPrefixLen + aShortName.getLength();
+ }
+ }
+ }
+ }
+
+ if( nNextChar > 0 )
+ {
+ if( pnStyleId ) *pnStyleId = nFoundId;
+ if( pnNextChar ) *pnNextChar = nNextChar;
+ return true;
+ }
+
+ if( pnStyleId ) *pnStyleId = EXC_STYLE_USERDEF;
+ if( pnNextChar ) *pnNextChar = 0;
+ return nPrefixLen > 0; // also return true for unknown built-in styles
+}
+
+bool XclTools::GetBuiltInStyleId( sal_uInt8& rnStyleId, sal_uInt8& rnLevel, const OUString& rStyleName )
+{
+ sal_uInt8 nStyleId;
+ sal_Int32 nNextChar;
+ if( IsBuiltInStyleName( rStyleName, &nStyleId, &nNextChar ) && (nStyleId != EXC_STYLE_USERDEF) )
+ {
+ if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) )
+ {
+ std::u16string_view aLevel = rStyleName.subView(nNextChar);
+ sal_Int32 nLevel = o3tl::toInt32(aLevel);
+ if (std::u16string_view(OUString::number(nLevel)) == aLevel
+ && nLevel > 0 && nLevel <= EXC_STYLE_LEVELCOUNT)
+ {
+ rnStyleId = nStyleId;
+ rnLevel = static_cast< sal_uInt8 >( nLevel - 1 );
+ return true;
+ }
+ }
+ else if( rStyleName.getLength() == nNextChar )
+ {
+ rnStyleId = nStyleId;
+ rnLevel = EXC_STYLE_NOLEVEL;
+ return true;
+ }
+ }
+
+ rnStyleId = EXC_STYLE_USERDEF;
+ rnLevel = EXC_STYLE_NOLEVEL;
+ return false;
+}
+
+// conditional formatting style names
+
+const char maCFStyleNamePrefix1[] = "Excel_CondFormat_"; /// Prefix for cond. formatting style names.
+const char maCFStyleNamePrefix2[] = "ConditionalStyle_"; /// Prefix for cond. formatting style names from OOX filter.
+const char maCFStyleNamePrefix3[] = "ExtConditionalStyle_";
+
+OUString XclTools::GetCondFormatStyleName( SCTAB nScTab, sal_Int32 nFormat, sal_uInt16 nCondition )
+{
+ return maCFStyleNamePrefix1 +
+ OUString::number(static_cast<sal_Int32>(nScTab+1)) +
+ "_" +
+ OUString::number(static_cast<sal_Int32>(nFormat+1)) +
+ "_" +
+ OUString::number(static_cast<sal_Int32>(nCondition+1));
+}
+
+bool XclTools::IsCondFormatStyleName( const OUString& rStyleName )
+{
+ if( rStyleName.startsWithIgnoreAsciiCase( maCFStyleNamePrefix1 ) )
+ return true;
+
+ if( rStyleName.startsWithIgnoreAsciiCase( maCFStyleNamePrefix2 ) )
+ return true;
+
+ if (rStyleName.startsWithIgnoreAsciiCase(maCFStyleNamePrefix3))
+ return true;
+
+ return false;
+}
+
+// stream handling
+
+void XclTools::SkipSubStream( XclImpStream& rStrm )
+{
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_EOF;
+ if( (nRecId == EXC_ID2_BOF) || (nRecId == EXC_ID3_BOF) || (nRecId == EXC_ID4_BOF) || (nRecId == EXC_ID5_BOF) )
+ SkipSubStream( rStrm );
+ }
+}
+
+// Basic macro names
+
+const char maSbMacroPrefix[] = "vnd.sun.star.script:"; /// Prefix for StarBasic macros.
+const char maSbMacroSuffix[] = "?language=Basic&location=document"; /// Suffix for StarBasic macros.
+
+OUString XclTools::GetSbMacroUrl( const OUString& rMacroName, SfxObjectShell* pDocShell )
+{
+ OSL_ENSURE( !rMacroName.isEmpty(), "XclTools::GetSbMacroUrl - macro name is empty" );
+ ::ooo::vba::MacroResolvedInfo aMacroInfo = ::ooo::vba::resolveVBAMacro( pDocShell, rMacroName );
+ if( aMacroInfo.mbFound )
+ return ::ooo::vba::makeMacroURL( aMacroInfo.msResolvedMacro );
+ return OUString();
+}
+
+OUString XclTools::GetXclMacroName( const OUString& rSbMacroUrl )
+{
+ sal_Int32 nSbMacroUrlLen = rSbMacroUrl.getLength();
+ sal_Int32 nMacroNameLen = nSbMacroUrlLen - strlen(maSbMacroPrefix) - strlen(maSbMacroSuffix);
+ if( (nMacroNameLen > 0) && rSbMacroUrl.startsWithIgnoreAsciiCase( maSbMacroPrefix ) &&
+ rSbMacroUrl.endsWithIgnoreAsciiCase( maSbMacroSuffix ) )
+ {
+ sal_Int32 nPrjDot = rSbMacroUrl.indexOf( '.', strlen(maSbMacroPrefix) ) + 1;
+ return rSbMacroUrl.copy( nPrjDot, nSbMacroUrlLen - nPrjDot - strlen(maSbMacroSuffix) );
+ }
+ return OUString();
+}
+
+// read/write colors
+
+XclImpStream& operator>>( XclImpStream& rStrm, Color& rColor )
+{
+ sal_uInt8 nR = rStrm.ReaduInt8();
+ sal_uInt8 nG = rStrm.ReaduInt8();
+ sal_uInt8 nB = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );//nD
+ rColor = Color( nR, nG, nB );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const Color& rColor )
+{
+ return rStrm << rColor.GetRed() << rColor.GetGreen() << rColor.GetBlue() << sal_uInt8( 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltracer.cxx b/sc/source/filter/excel/xltracer.cxx
new file mode 100644
index 0000000000..2b51478e85
--- /dev/null
+++ b/sc/source/filter/excel/xltracer.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 <xltracer.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <address.hxx>
+
+XclTracer::XclTracer(std::u16string_view /*rDocUrl*/)
+ : mbEnabled(false)
+ , maFirstTimes(eTraceLength, true)
+{
+}
+
+XclTracer::~XclTracer() {}
+
+void XclTracer::ProcessTraceOnce(XclTracerId eProblem)
+{
+ if (mbEnabled && maFirstTimes[eProblem])
+ {
+ maFirstTimes[eProblem] = false;
+ }
+}
+
+void XclTracer::TraceInvalidAddress(const ScAddress& rPos, const ScAddress& rMaxPos)
+{
+ TraceInvalidRow(rPos.Row(), rMaxPos.Row());
+ TraceInvalidTab(rPos.Tab(), rMaxPos.Tab());
+}
+
+void XclTracer::TraceInvalidRow(sal_uInt32 nRow, sal_uInt32 nMaxRow)
+{
+ if (nRow > nMaxRow)
+ ProcessTraceOnce(eRowLimitExceeded);
+}
+
+void XclTracer::TraceInvalidTab(SCTAB nTab, SCTAB nMaxTab)
+{
+ if (nTab > nMaxTab)
+ ProcessTraceOnce(eTabLimitExceeded);
+}
+
+void XclTracer::TracePrintRange() { ProcessTraceOnce(ePrintRange); }
+
+void XclTracer::TraceDates(sal_uInt16 nNumFmt)
+{
+ // Short Date = 14 and Short Date+Time = 22
+ if (nNumFmt == 14 || nNumFmt == 22)
+ ProcessTraceOnce(eShortDate);
+}
+
+void XclTracer::TraceBorderLineStyle(bool bBorderLineStyle)
+{
+ if (bBorderLineStyle)
+ ProcessTraceOnce(eBorderLineStyle);
+}
+
+void XclTracer::TraceFillPattern(bool bFillPattern)
+{
+ if (bFillPattern)
+ ProcessTraceOnce(eFillPattern);
+}
+
+void XclTracer::TraceFormulaMissingArg()
+{
+ // missing parameter in Formula record
+ ProcessTraceOnce(eFormulaMissingArg);
+}
+
+void XclTracer::TracePivotDataSource(bool bExternal)
+{
+ if (bExternal)
+ ProcessTraceOnce(ePivotDataSource);
+}
+
+void XclTracer::TracePivotChartExists()
+{
+ // Pivot Charts not currently displayed.
+ ProcessTraceOnce(ePivotChartExists);
+}
+
+void XclTracer::TraceChartUnKnownType() { ProcessTraceOnce(eChartUnKnownType); }
+
+void XclTracer::TraceChartOnlySheet() { ProcessTraceOnce(eChartOnlySheet); }
+
+void XclTracer::TraceChartDataTable()
+{
+ // Data table is not supported.
+ ProcessTraceOnce(eChartDataTable);
+}
+
+void XclTracer::TraceChartLegendPosition()
+{
+ // If position is set to "not docked or inside the plot area" then
+ // we cannot guarantee the legend position.
+ ProcessTraceOnce(eChartLegendPosition);
+}
+
+void XclTracer::TraceUnsupportedObjects()
+{
+ // Called from Excel 5.0 - limited Graphical object support.
+ ProcessTraceOnce(eUnsupportedObject);
+}
+
+void XclTracer::TraceObjectNotPrintable() { ProcessTraceOnce(eObjectNotPrintable); }
+
+void XclTracer::TraceDVType(bool bType)
+{
+ // Custom types work if 'Data->validity dialog' is not OKed.
+ if (bType)
+ ProcessTraceOnce(eDVType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlview.cxx b/sc/source/filter/excel/xlview.cxx
new file mode 100644
index 0000000000..b5768e2df1
--- /dev/null
+++ b/sc/source/filter/excel/xlview.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 <xlview.hxx>
+#include <osl/diagnose.h>
+
+// Structs ====================================================================
+
+XclDocViewData::XclDocViewData() :
+ mnWinX( 0 ),
+ mnWinY( 0 ),
+ mnWinWidth( 0 ),
+ mnWinHeight( 0 ),
+ mnFlags( EXC_WIN1_HOR_SCROLLBAR | EXC_WIN1_VER_SCROLLBAR | EXC_WIN1_TABBAR ),
+ mnDisplXclTab( 0 ),
+ mnFirstVisXclTab( 0 ),
+ mnXclSelectCnt( 1 ),
+ mnTabBarWidth( 600 )
+{
+}
+
+XclTabViewData::XclTabViewData() :
+ maFirstXclPos( ScAddress::UNINITIALIZED ),
+ maSecondXclPos( ScAddress::UNINITIALIZED )
+{
+ SetDefaults();
+}
+
+XclTabViewData::~XclTabViewData()
+{
+}
+
+void XclTabViewData::SetDefaults()
+{
+ maSelMap.clear();
+ maGridColor = COL_AUTO;
+ maFirstXclPos.Set( 0, 0 );
+ maSecondXclPos.Set( 0, 0 );
+ mnSplitX = mnSplitY = 0;
+ mnNormalZoom = EXC_WIN2_NORMALZOOM_DEF;
+ mnPageZoom = EXC_WIN2_PAGEZOOM_DEF;
+ mnCurrentZoom = 0; // default to mnNormalZoom or mnPageZoom
+ mnActivePane = EXC_PANE_TOPLEFT;
+ mbSelected = mbDisplayed = false;
+ mbMirrored = false;
+ mbFrozenPanes = false;
+ mbPageMode = false;
+ mbDefGridColor = true;
+ mbShowFormulas = false;
+ mbShowGrid = mbShowHeadings = mbShowZeros = mbShowOutline = true;
+ maTabBgColor = COL_AUTO;
+ mnTabBgColorId = 0;
+}
+
+bool XclTabViewData::IsSplit() const
+{
+ return (mnSplitX > 0) || (mnSplitY > 0);
+}
+
+bool XclTabViewData::HasPane( sal_uInt8 nPaneId ) const
+{
+ switch( nPaneId )
+ {
+ case EXC_PANE_BOTTOMRIGHT: return (mnSplitX > 0) && (mnSplitY > 0);
+ case EXC_PANE_TOPRIGHT: return mnSplitX > 0;
+ case EXC_PANE_BOTTOMLEFT: return mnSplitY > 0;
+ case EXC_PANE_TOPLEFT: return true;
+ }
+ OSL_FAIL( "XclExpPane::HasPane - wrong pane ID" );
+ return false;
+}
+
+const XclSelectionData* XclTabViewData::GetSelectionData( sal_uInt8 nPane ) const
+{
+ XclSelectionMap::const_iterator aIt = maSelMap.find( nPane );
+ return (aIt == maSelMap.end()) ? nullptr : aIt->second.get();
+}
+
+XclSelectionData& XclTabViewData::CreateSelectionData( sal_uInt8 nPane )
+{
+ XclSelectionDataRef& rxSelData = maSelMap[ nPane ];
+ if( !rxSelData )
+ rxSelData = std::make_shared<XclSelectionData>();
+ return *rxSelData;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/fapihelper.cxx b/sc/source/filter/ftools/fapihelper.cxx
new file mode 100644
index 0000000000..a8cb2f59d1
--- /dev/null
+++ b/sc/source/filter/ftools/fapihelper.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fapihelper.hxx>
+
+#include <algorithm>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/beans/XPropertySetOption.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sal/log.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/stritem.hxx>
+#include <svl/itemset.hxx>
+#include <miscuno.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::beans::XPropertyState;
+using ::com::sun::star::lang::XServiceName;
+using ::com::sun::star::lang::XMultiServiceFactory;
+
+using namespace ::com::sun::star;
+
+// Static helper functions ====================================================
+
+OUString ScfApiHelper::GetServiceName( const Reference< XInterface >& xInt )
+{
+ OUString aService;
+ Reference< XServiceName > xServiceName( xInt, UNO_QUERY );
+ if( xServiceName.is() )
+ aService = xServiceName->getServiceName();
+ return aService;
+}
+
+Reference< XMultiServiceFactory > ScfApiHelper::GetServiceFactory( const SfxObjectShell* pShell )
+{
+ Reference< XMultiServiceFactory > xFactory;
+ if( pShell )
+ xFactory.set( pShell->GetModel(), UNO_QUERY );
+ return xFactory;
+}
+
+Reference< XInterface > ScfApiHelper::CreateInstance(
+ const Reference< XMultiServiceFactory >& xFactory, const OUString& rServiceName )
+{
+ Reference< XInterface > xInt;
+ if( xFactory.is() )
+ {
+ try
+ {
+ xInt = xFactory->createInstance( rServiceName );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ScfApiHelper::CreateInstance - cannot create instance" );
+ }
+ }
+ return xInt;
+}
+
+Reference< XInterface > ScfApiHelper::CreateInstance( const SfxObjectShell* pShell, const OUString& rServiceName )
+{
+ return CreateInstance( GetServiceFactory( pShell ), rServiceName );
+}
+
+Reference< XInterface > ScfApiHelper::CreateInstance( const OUString& rServiceName )
+{
+ return CreateInstance( ::comphelper::getProcessServiceFactory(), rServiceName );
+}
+
+uno::Sequence< beans::NamedValue > ScfApiHelper::QueryEncryptionDataForMedium( SfxMedium& rMedium,
+ ::comphelper::IDocPasswordVerifier& rVerifier, const ::std::vector< OUString >* pDefaultPasswords )
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = rMedium.GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+
+ OUString aPassword;
+ const SfxStringItem* pPasswordItem = rMedium.GetItemSet().GetItem(SID_PASSWORD, false);
+ if ( pPasswordItem )
+ aPassword = pPasswordItem->GetValue();
+
+ bool bIsDefaultPassword = false;
+ aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
+ rVerifier, aEncryptionData, aPassword, rMedium.GetInteractionHandler(), rMedium.GetOrigURL(),
+ ::comphelper::DocPasswordRequestType::MS, pDefaultPasswords, &bIsDefaultPassword );
+
+ rMedium.GetItemSet().ClearItem( SID_PASSWORD );
+ rMedium.GetItemSet().ClearItem( SID_ENCRYPTIONDATA );
+
+ if( !bIsDefaultPassword && aEncryptionData.hasElements() )
+ rMedium.GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+
+ return aEncryptionData;
+}
+
+// Property sets ==============================================================
+
+ScfPropertySet::~ScfPropertySet()
+{
+ Reference<beans::XPropertySetOption> xPropSetOpt(mxPropSet, UNO_QUERY);
+ if (xPropSetOpt.is())
+ // Turn the property value change notification back on when finished.
+ xPropSetOpt->enableChangeListenerNotification(true);
+}
+
+void ScfPropertySet::Set( Reference< XPropertySet > const & xPropSet )
+{
+ mxPropSet = xPropSet;
+ mxMultiPropSet.set( mxPropSet, UNO_QUERY );
+ Reference<beans::XPropertySetOption> xPropSetOpt(mxPropSet, UNO_QUERY);
+ if (xPropSetOpt.is())
+ // We don't want to broadcast property value changes during import to
+ // improve performance.
+ xPropSetOpt->enableChangeListenerNotification(false);
+}
+
+OUString ScfPropertySet::GetServiceName() const
+{
+ return ScfApiHelper::GetServiceName( mxPropSet );
+}
+
+// Get properties -------------------------------------------------------------
+
+bool ScfPropertySet::HasProperty( const OUString& rPropName ) const
+{
+ bool bHasProp = false;
+ try
+ {
+ Reference< XPropertyState > xPropState( mxPropSet, UNO_QUERY_THROW );
+ bHasProp = xPropState->getPropertyState( rPropName ) == css::beans::PropertyState_DIRECT_VALUE;
+ }
+ catch( Exception& )
+ {
+ }
+ return bHasProp;
+}
+
+bool ScfPropertySet::GetAnyProperty( Any& rValue, const OUString& rPropName ) const
+{
+ bool bHasValue = false;
+ try
+ {
+ if( mxPropSet.is() )
+ {
+ rValue = mxPropSet->getPropertyValue( rPropName );
+ bHasValue = true;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return bHasValue;
+}
+
+bool ScfPropertySet::GetBoolProperty( const OUString& rPropName ) const
+{
+ Any aAny;
+ return GetAnyProperty( aAny, rPropName ) && ScUnoHelpFunctions::GetBoolFromAny( aAny );
+}
+
+OUString ScfPropertySet::GetStringProperty( const OUString& rPropName ) const
+{
+ OUString aOUString;
+ GetProperty( aOUString, rPropName );
+ return aOUString;
+}
+
+bool ScfPropertySet::GetColorProperty( Color& rColor, const OUString& rPropName ) const
+{
+ sal_Int32 nApiColor = 0;
+ bool bRet = GetProperty( nApiColor, rPropName );
+ rColor = Color( ColorTransparency, nApiColor );
+ return bRet;
+}
+
+void ScfPropertySet::GetProperties( Sequence< Any >& rValues, const Sequence< OUString >& rPropNames ) const
+{
+ try
+ {
+ OSL_ENSURE( mxMultiPropSet.is(), "ScfPropertySet::GetProperties - multi property set not available" );
+ if( mxMultiPropSet.is() ) // first try the XMultiPropertySet
+ {
+ rValues = mxMultiPropSet->getPropertyValues( rPropNames );
+ }
+ else if( mxPropSet.is() )
+ {
+ sal_Int32 nLen = rPropNames.getLength();
+ rValues.realloc( nLen );
+ std::transform(rPropNames.begin(), rPropNames.end(), rValues.getArray(),
+ [this](const OUString& rPropName) -> Any { return mxPropSet->getPropertyValue(rPropName); });
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+// Set properties -------------------------------------------------------------
+
+void ScfPropertySet::SetAnyProperty( const OUString& rPropName, const Any& rValue )
+{
+ try
+ {
+ if( mxPropSet.is() )
+ mxPropSet->setPropertyValue( rPropName, rValue );
+ }
+ catch (const Exception&)
+ {
+ SAL_WARN("sc", "ScfPropertySet::SetAnyProperty - cannot set property \"" + rPropName + "\"");
+ }
+}
+
+void ScfPropertySet::SetProperties( const Sequence< OUString >& rPropNames, const Sequence< Any >& rValues )
+{
+ OSL_ENSURE( rPropNames.getLength() == rValues.getLength(), "ScfPropertySet::SetProperties - length of sequences different" );
+ try
+ {
+ if( mxMultiPropSet.is() ) // first try the XMultiPropertySet
+ {
+ mxMultiPropSet->setPropertyValues( rPropNames, rValues );
+ }
+ else if( mxPropSet.is() )
+ {
+ OSL_FAIL( "ScfPropertySet::SetProperties - multi property set not available" );
+ const OUString* pPropName = rPropNames.getConstArray();
+ const OUString* pPropNameEnd = pPropName + rPropNames.getLength();
+ const Any* pValue = rValues.getConstArray();
+ for( ; pPropName != pPropNameEnd; ++pPropName, ++pValue )
+ mxPropSet->setPropertyValue( *pPropName, *pValue );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ScfPropertySet::SetAnyProperty - cannot set multiple properties" );
+ }
+}
+
+ScfPropSetHelper::ScfPropSetHelper( const char* const* ppcPropNames ) :
+ mnNextIdx( 0 )
+{
+ OSL_ENSURE( ppcPropNames, "ScfPropSetHelper::ScfPropSetHelper - no strings found" );
+
+ // create OUStrings from ASCII property names
+ typedef ::std::pair< OUString, size_t > IndexedOUString;
+ std::vector<IndexedOUString> aPropNameVec;
+ for( size_t nVecIdx = 0; *ppcPropNames; ++ppcPropNames, ++nVecIdx )
+ {
+ OUString aPropName = OUString::createFromAscii( *ppcPropNames );
+ aPropNameVec.emplace_back( aPropName, nVecIdx );
+ }
+
+ // sorts the pairs, which will be sorted by first component (the property name)
+ ::std::sort( aPropNameVec.begin(), aPropNameVec.end() );
+
+ // resize member sequences
+ size_t nSize = aPropNameVec.size();
+ maNameSeq.realloc( static_cast< sal_Int32 >( nSize ) );
+ auto pNameSeq = maNameSeq.getArray();
+ maValueSeq.realloc( static_cast< sal_Int32 >( nSize ) );
+ maNameOrder.resize( nSize );
+
+ // fill the property name sequence and store original sort order
+ sal_Int32 nSeqIdx = 0;
+ for( auto& aPropName : aPropNameVec )
+ {
+ pNameSeq[ nSeqIdx ] = aPropName.first;
+ maNameOrder[ aPropName.second ] = nSeqIdx;
+ ++nSeqIdx;
+ }
+}
+
+// read properties ------------------------------------------------------------
+
+void ScfPropSetHelper::ReadFromPropertySet( const ScfPropertySet& rPropSet )
+{
+ rPropSet.GetProperties( maValueSeq, maNameSeq );
+ mnNextIdx = 0;
+}
+
+void ScfPropSetHelper::ReadValue( Any& rAny )
+{
+ Any* pAny = GetNextAny();
+ if( pAny )
+ rAny = *pAny;
+}
+
+void ScfPropSetHelper::ReadValue( Color& rColor )
+{
+ sal_Int32 nApiColor(0);
+ ReadValue( nApiColor );
+ rColor = Color( ColorTransparency, nApiColor );
+}
+
+void ScfPropSetHelper::ReadValue( bool& rbValue )
+{
+ Any aAny;
+ ReadValue( aAny );
+ rbValue = ScUnoHelpFunctions::GetBoolFromAny( aAny );
+}
+
+// write properties -----------------------------------------------------------
+
+void ScfPropSetHelper::InitializeWrite()
+{
+ mnNextIdx = 0;
+}
+
+void ScfPropSetHelper::WriteValue( const Any& rAny )
+{
+ if( Any* pAny = GetNextAny() )
+ *pAny = rAny;
+}
+
+void ScfPropSetHelper::WriteValue( bool rbValue )
+{
+ if( Any* pAny = GetNextAny() )
+ *pAny <<= rbValue;
+}
+
+void ScfPropSetHelper::WriteToPropertySet( ScfPropertySet& rPropSet ) const
+{
+ rPropSet.SetProperties( maNameSeq, maValueSeq );
+}
+
+// private --------------------------------------------------------------------
+
+Any* ScfPropSetHelper::GetNextAny()
+{
+ OSL_ENSURE( mnNextIdx < maNameOrder.size(), "ScfPropSetHelper::GetNextAny - sequence overflow" );
+ Any* pAny = nullptr;
+ if( mnNextIdx < maNameOrder.size() )
+ pAny = &maValueSeq.getArray()[ maNameOrder[ mnNextIdx++ ] ];
+ return pAny;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/fprogressbar.cxx b/sc/source/filter/ftools/fprogressbar.cxx
new file mode 100644
index 0000000000..2ce5ab4035
--- /dev/null
+++ b/sc/source/filter/ftools/fprogressbar.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 <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <progress.hxx>
+#include <osl/diagnose.h>
+#include <tools/stream.hxx>
+
+#include <limits>
+#include <utility>
+
+ScfProgressBar::ScfProgressSegment::ScfProgressSegment( std::size_t nSize ) :
+ mnSize( nSize ),
+ mnPos( 0 )
+{
+}
+
+ScfProgressBar::ScfProgressSegment::~ScfProgressSegment()
+{
+}
+
+ScfProgressBar::ScfProgressBar( SfxObjectShell* pDocShell, OUString aText ) :
+ maText(std::move( aText ))
+{
+ Init( pDocShell );
+}
+
+ScfProgressBar::ScfProgressBar(SfxObjectShell* pDocShell, TranslateId pResId)
+ : maText(ScResId(pResId))
+{
+ Init( pDocShell );
+}
+
+ScfProgressBar::ScfProgressBar( ScfProgressBar& rParProgress, ScfProgressSegment* pParSegment )
+{
+ Init( rParProgress.mpDocShell );
+ mpParentProgress = &rParProgress;
+ mpParentSegment = pParSegment;
+}
+
+ScfProgressBar::~ScfProgressBar()
+{
+}
+
+void ScfProgressBar::Init( SfxObjectShell* pDocShell )
+{
+ mpDocShell = pDocShell;
+ mpParentProgress = nullptr;
+ mpParentSegment = mpCurrSegment = nullptr;
+ mnTotalSize = mnTotalPos = mnUnitSize = mnNextUnitPos = 0;
+ mnSysProgressScale = 1; // used to workaround the SfxProgress sal_uInt32 limit
+ mbInProgress = false;
+}
+
+ScfProgressBar::ScfProgressSegment* ScfProgressBar::GetSegment( sal_Int32 nSegment )
+{
+ if( nSegment < 0 )
+ return nullptr;
+ return maSegments.at( nSegment ).get();
+}
+
+void ScfProgressBar::SetCurrSegment( ScfProgressSegment* pSegment )
+{
+ if( mpCurrSegment == pSegment )
+ return;
+
+ mpCurrSegment = pSegment;
+
+ if( mpParentProgress && mpParentSegment )
+ {
+ mpParentProgress->SetCurrSegment( mpParentSegment );
+ }
+ else if( !mxSysProgress && (mnTotalSize > 0) )
+ {
+ // SfxProgress has a limit of sal_uInt32.
+ mnSysProgressScale = 1;
+ std::size_t nSysTotalSize = mnTotalSize;
+ while( nSysTotalSize > std::numeric_limits<sal_uInt32>::max() )
+ {
+ nSysTotalSize /= 2;
+ mnSysProgressScale *= 2;
+ }
+ mxSysProgress.reset( new ScProgress( mpDocShell, maText, nSysTotalSize, true ) );
+ }
+
+ if( !mbInProgress && mpCurrSegment && (mnTotalSize > 0) )
+ {
+ mnUnitSize = mnTotalSize / 256 + 1; // at most 256 calls of system progress
+ mnNextUnitPos = 0;
+ mbInProgress = true;
+ }
+}
+
+void ScfProgressBar::IncreaseProgressBar( std::size_t nDelta )
+{
+ std::size_t nNewPos = mnTotalPos + nDelta;
+
+ // call back to parent progress bar
+ if( mpParentProgress && mpParentSegment )
+ {
+ // calculate new position of parent progress bar
+ std::size_t nParentPos = static_cast< std::size_t >(
+ static_cast< double >( nNewPos ) * mpParentSegment->mnSize / mnTotalSize );
+ mpParentProgress->ProgressAbs( nParentPos );
+ }
+ // modify system progress bar
+ else if( mxSysProgress )
+ {
+ if( nNewPos >= mnNextUnitPos )
+ {
+ mnNextUnitPos = nNewPos + mnUnitSize;
+ mxSysProgress->SetState( static_cast< sal_uLong >( nNewPos / mnSysProgressScale ) );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ScfProgressBar::IncreaseProgressBar - no progress bar found" );
+ }
+
+ mnTotalPos = nNewPos;
+}
+
+sal_Int32 ScfProgressBar::AddSegment( std::size_t nSize )
+{
+ OSL_ENSURE( !mbInProgress, "ScfProgressBar::AddSegment - already in progress mode" );
+ if( nSize == 0 )
+ return SCF_INV_SEGMENT;
+
+ maSegments.push_back( std::make_unique<ScfProgressSegment>( nSize ) );
+ mnTotalSize += nSize;
+ return static_cast< sal_Int32 >( maSegments.size() - 1 );
+}
+
+ScfProgressBar& ScfProgressBar::GetSegmentProgressBar( sal_Int32 nSegment )
+{
+ ScfProgressSegment* pSegment = GetSegment( nSegment );
+ OSL_ENSURE( !pSegment || (pSegment->mnPos == 0), "ScfProgressBar::GetSegmentProgressBar - segment already started" );
+ if( pSegment && (pSegment->mnPos == 0) )
+ {
+ if( !pSegment->mxProgress )
+ pSegment->mxProgress.reset( new ScfProgressBar( *this, pSegment ) );
+ return *pSegment->mxProgress;
+ }
+ return *this;
+}
+
+bool ScfProgressBar::IsFull() const
+{
+ OSL_ENSURE( mbInProgress && mpCurrSegment, "ScfProgressBar::IsFull - no segment started" );
+ return mpCurrSegment && (mpCurrSegment->mnPos >= mpCurrSegment->mnSize);
+}
+
+void ScfProgressBar::ActivateSegment( sal_Int32 nSegment )
+{
+ OSL_ENSURE( mnTotalSize > 0, "ScfProgressBar::ActivateSegment - progress range is zero" );
+ if( mnTotalSize > 0 )
+ SetCurrSegment( GetSegment( nSegment ) );
+}
+
+void ScfProgressBar::ProgressAbs( std::size_t nPos )
+{
+ OSL_ENSURE( mbInProgress && mpCurrSegment, "ScfProgressBar::ProgressAbs - no segment started" );
+ if( mpCurrSegment )
+ {
+ OSL_ENSURE( mpCurrSegment->mnPos <= nPos, "ScfProgressBar::ProgressAbs - delta pos < 0" );
+ OSL_ENSURE( nPos <= mpCurrSegment->mnSize, "ScfProgressBar::ProgressAbs - segment overflow" );
+ if( (mpCurrSegment->mnPos < nPos) && (nPos <= mpCurrSegment->mnSize) )
+ {
+ IncreaseProgressBar( nPos - mpCurrSegment->mnPos );
+ mpCurrSegment->mnPos = nPos;
+ }
+ }
+}
+
+void ScfProgressBar::Progress( std::size_t nDelta )
+{
+ ProgressAbs( mpCurrSegment ? (mpCurrSegment->mnPos + nDelta) : 0 );
+}
+
+ScfSimpleProgressBar::ScfSimpleProgressBar( std::size_t nSize, SfxObjectShell* pDocShell, const OUString& rText ) :
+ maProgress( pDocShell, rText )
+{
+ Init( nSize );
+}
+
+ScfSimpleProgressBar::ScfSimpleProgressBar(std::size_t nSize, SfxObjectShell* pDocShell, TranslateId pResId)
+ : maProgress(pDocShell, pResId)
+{
+ Init( nSize );
+}
+
+void ScfSimpleProgressBar::Init( std::size_t nSize )
+{
+ sal_Int32 nSegment = maProgress.AddSegment( nSize );
+ if( nSegment >= 0 )
+ maProgress.ActivateSegment( nSegment );
+}
+
+ScfStreamProgressBar::ScfStreamProgressBar( SvStream& rStrm, SfxObjectShell* pDocShell ) :
+ mrStrm( rStrm )
+{
+ Init( pDocShell, ScResId( STR_LOAD_DOC ) );
+}
+
+void ScfStreamProgressBar::Progress()
+{
+ mxProgress->ProgressAbs( mrStrm.Tell() );
+}
+
+void ScfStreamProgressBar::Init( SfxObjectShell* pDocShell, const OUString& rText )
+{
+ sal_uInt64 const nSize = mrStrm.TellEnd();
+ mxProgress.reset( new ScfSimpleProgressBar( nSize, pDocShell, rText ) );
+ Progress();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/ftools.cxx b/sc/source/filter/ftools/ftools.cxx
new file mode 100644
index 0000000000..94ac690c13
--- /dev/null
+++ b/sc/source/filter/ftools/ftools.cxx
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ftools.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <tools/color.hxx>
+#include <unotools/charclass.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/poolitem.hxx>
+#include <sot/storage.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <math.h>
+#include <global.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <compiler.hxx>
+
+#include <orcusfiltersimpl.hxx>
+
+
+// ScFilterTools::ReadLongDouble()
+
+void ScfTools::ReadLongDouble(SvStream& rStrm, double& fResult)
+
+#ifdef __SIMPLE_FUNC // for <=VC 1.5
+{
+ long double fRet;
+ bool bOk = 10 == rStrm.Read(&fRet, 10);
+ if (!bOk)
+ return;
+ fResult = static_cast<double>(fRet);
+}
+#undef __SIMPLE_FUNC
+
+#else // detailed for all others
+{
+
+/*
+"Mapping - Guide" 10-Byte Intel
+
+77777777 77666666 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 x10
+98765432 10987654 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 Bit-# total
+9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 Byte-#
+76543210 76543210 76543210 76543210 76543210 76543210 76543210 76543210 76543210 76543210 Bit-# in Byte
+SEEEEEEE EEEEEEEE IMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM Group
+01111110 00000000 06665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 x10
+14321098 76543210 02109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 Bit in Group
+*/
+
+ long double lfDouble;
+ long double lfFactor = 256.0;
+ sal_uInt8 pDouble10[ 10 ];
+
+ bool bOk = 10 == rStrm.ReadBytes(pDouble10, 10); // Intel-10 in pDouble10
+ if (!bOk)
+ return;
+
+ lfDouble = static_cast< long double >( pDouble10[ 7 ] ); // Byte 7
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 6 ] ); // Byte 6
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 5 ] ); // Byte 5
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 4 ] ); // Byte 4
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 3 ] ); // Byte 3
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 2 ] ); // Byte 2
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 1 ] ); // Byte 1
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 0 ] ); // Byte 0
+
+ // For value 0.0 all bits are zero; pow(2.0,-16446) does not work with CSet compilers
+ if( lfDouble != 0.0 )
+ {
+ // exponent
+ sal_Int32 nExp;
+ nExp = pDouble10[ 9 ] & 0x7F;
+ nExp <<= 8;
+ nExp += pDouble10[ 8 ];
+ nExp -= 16446;
+
+ lfDouble *= pow( 2.0, static_cast< double >( nExp ) );
+ }
+
+ // sign
+ if( pDouble10[ 9 ] & 0x80 )
+ lfDouble *= static_cast< long double >( -1.0 );
+
+ fResult = static_cast<double>(lfDouble);
+}
+#endif
+
+// *** common methods *** -----------------------------------------------------
+
+rtl_TextEncoding ScfTools::GetSystemTextEncoding()
+{
+ return osl_getThreadTextEncoding();
+}
+
+OUString ScfTools::GetHexStr( sal_uInt16 nValue )
+{
+ const char pHex[] = "0123456789ABCDEF";
+ OUString aStr = OUStringChar( pHex[ nValue >> 12 ] )
+ + OUStringChar( pHex[ (nValue >> 8) & 0x000F ] )
+ + OUStringChar( pHex[ (nValue >> 4) & 0x000F ] )
+ + OUStringChar( pHex[ nValue & 0x000F ] );
+ return aStr;
+}
+
+sal_uInt8 ScfTools::GetMixedColorComp( sal_uInt8 nFore, sal_uInt8 nBack, sal_uInt8 nTrans )
+{
+ sal_Int32 nTemp = ((static_cast< sal_Int32 >( nBack ) - nFore) * nTrans) / 0x80 + nFore;
+ return static_cast< sal_uInt8 >( nTemp );
+}
+
+Color ScfTools::GetMixedColor( const Color& rFore, const Color& rBack, sal_uInt8 nTrans )
+{
+ return Color(
+ GetMixedColorComp( rFore.GetRed(), rBack.GetRed(), nTrans ),
+ GetMixedColorComp( rFore.GetGreen(), rBack.GetGreen(), nTrans ),
+ GetMixedColorComp( rFore.GetBlue(), rBack.GetBlue(), nTrans ) );
+}
+
+// *** conversion of names *** ------------------------------------------------
+
+/* XXX As in sc/source/core/tool/rangenam.cxx ScRangeData::IsValidName() */
+
+OUString ScfTools::ConvertToScDefinedName(const OUString& rName )
+{
+ //fdo#37872: we don't allow points in range names any more
+ OUString sName = rName.replace(u'.',
+ u'_');
+ sal_Int32 nLen = sName.getLength();
+ if( nLen && !ScCompiler::IsCharFlagAllConventions( sName, 0, ScCharFlags::CharName ) )
+ sName = sName.replaceAt( 0, 1, u"_" );
+ for( sal_Int32 nPos = 1; nPos < nLen; ++nPos )
+ if( !ScCompiler::IsCharFlagAllConventions( sName, nPos, ScCharFlags::Name ) )
+ sName = sName.replaceAt( nPos, 1, u"_" );
+ return sName;
+}
+
+// *** streams and storages *** -----------------------------------------------
+
+tools::SvRef<SotStorage> ScfTools::OpenStorageRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName )
+{
+ tools::SvRef<SotStorage> xSubStrg;
+ if( xStrg.is() && xStrg->IsContained( rStrgName ) )
+ xSubStrg = xStrg->OpenSotStorage( rStrgName, StreamMode::STD_READ );
+ return xSubStrg;
+}
+
+tools::SvRef<SotStorage> ScfTools::OpenStorageWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName )
+{
+ tools::SvRef<SotStorage> xSubStrg;
+ if( xStrg.is() )
+ xSubStrg = xStrg->OpenSotStorage( rStrgName, StreamMode::STD_WRITE );
+ return xSubStrg;
+}
+
+tools::SvRef<SotStorageStream> ScfTools::OpenStorageStreamRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName )
+{
+ tools::SvRef<SotStorageStream> xStrm;
+ if( xStrg.is() && xStrg->IsContained( rStrmName ) && xStrg->IsStream( rStrmName ) )
+ xStrm = xStrg->OpenSotStream( rStrmName, StreamMode::STD_READ );
+ return xStrm;
+}
+
+tools::SvRef<SotStorageStream> ScfTools::OpenStorageStreamWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName )
+{
+ OSL_ENSURE( !xStrg.is() || !xStrg->IsContained( rStrmName ), "ScfTools::OpenStorageStreamWrite - stream exists already" );
+ tools::SvRef<SotStorageStream> xStrm;
+ if( xStrg.is() )
+ xStrm = xStrg->OpenSotStream( rStrmName, StreamMode::STD_WRITE | StreamMode::TRUNC );
+ return xStrm;
+}
+
+// *** item handling *** ------------------------------------------------------
+
+bool ScfTools::CheckItem( const SfxItemSet& rItemSet, sal_uInt16 nWhichId, bool bDeep )
+{
+ return rItemSet.GetItemState( nWhichId, bDeep ) == SfxItemState::SET;
+}
+
+bool ScfTools::CheckItems( const SfxItemSet& rItemSet, const sal_uInt16* pnWhichIds, bool bDeep )
+{
+ OSL_ENSURE( pnWhichIds, "ScfTools::CheckItems - no which id list" );
+ for( const sal_uInt16* pnWhichId = pnWhichIds; *pnWhichId != 0; ++pnWhichId )
+ if( CheckItem( rItemSet, *pnWhichId, bDeep ) )
+ return true;
+ return false;
+}
+
+void ScfTools::PutItem( SfxItemSet& rItemSet, const SfxPoolItem& rItem, sal_uInt16 nWhichId, bool bSkipPoolDef )
+{
+ if( !bSkipPoolDef || (rItem != rItemSet.GetPool()->GetDefaultItem( nWhichId )) )
+ {
+ rItemSet.Put( rItem.CloneSetWhich(nWhichId) );
+ }
+}
+
+void ScfTools::PutItem( SfxItemSet& rItemSet, const SfxPoolItem& rItem, bool bSkipPoolDef )
+{
+ PutItem( rItemSet, rItem, rItem.Which(), bSkipPoolDef );
+}
+
+// *** style sheet handling *** -----------------------------------------------
+
+namespace {
+
+ScStyleSheet& lclMakeStyleSheet( ScStyleSheetPool& rPool, const OUString& rStyleName, SfxStyleFamily eFamily, bool bForceName )
+{
+ // find an unused name
+ OUString aNewName( rStyleName );
+ sal_Int32 nIndex = 0;
+ SfxStyleSheetBase* pOldStyleSheet = nullptr;
+ while( SfxStyleSheetBase* pStyleSheet = rPool.Find( aNewName, eFamily ) )
+ {
+ if( !pOldStyleSheet )
+ pOldStyleSheet = pStyleSheet;
+ aNewName = rStyleName + " " + OUString::number( ++nIndex );
+ }
+
+ // rename existing style
+ if( pOldStyleSheet && bForceName )
+ {
+ pOldStyleSheet->SetName( aNewName );
+ aNewName = rStyleName;
+ }
+
+ // create new style sheet
+ return static_cast< ScStyleSheet& >( rPool.Make( aNewName, eFamily, SfxStyleSearchBits::UserDefined ) );
+}
+
+} // namespace
+
+ScStyleSheet& ScfTools::MakeCellStyleSheet( ScStyleSheetPool& rPool, const OUString& rStyleName, bool bForceName )
+{
+ return lclMakeStyleSheet( rPool, rStyleName, SfxStyleFamily::Para, bForceName );
+}
+
+ScStyleSheet& ScfTools::MakePageStyleSheet( ScStyleSheetPool& rPool, const OUString& rStyleName, bool bForceName )
+{
+ return lclMakeStyleSheet( rPool, rStyleName, SfxStyleFamily::Page, bForceName );
+}
+
+// *** byte string import operations *** --------------------------------------
+
+OString ScfTools::read_zeroTerminated_uInt8s_ToOString(SvStream& rStrm, sal_Int32& rnBytesLeft)
+{
+ OString aRet(::read_zeroTerminated_uInt8s_ToOString(rStrm));
+ rnBytesLeft -= aRet.getLength(); //we read this number of bytes anyway
+ if (rStrm.good()) //if the stream is happy we read the null terminator as well
+ --rnBytesLeft;
+ return aRet;
+}
+
+void ScfTools::AppendCString( SvStream& rStrm, OUString& rString, rtl_TextEncoding eTextEnc )
+{
+ rString += ::read_zeroTerminated_uInt8s_ToOUString(rStrm, eTextEnc);
+}
+
+// *** HTML table names <-> named range names *** -----------------------------
+
+const OUString& ScfTools::GetHTMLDocName()
+{
+ static constexpr OUString saHTMLDoc( u"HTML_all"_ustr );
+ return saHTMLDoc;
+}
+
+const OUString& ScfTools::GetHTMLTablesName()
+{
+ static constexpr OUString saHTMLTables( u"HTML_tables"_ustr );
+ return saHTMLTables;
+}
+
+const OUString& ScfTools::GetHTMLIndexPrefix()
+{
+ static constexpr OUString saHTMLIndexPrefix( u"HTML_"_ustr );
+ return saHTMLIndexPrefix;
+
+}
+
+const OUString& ScfTools::GetHTMLNamePrefix()
+{
+ static constexpr OUString saHTMLNamePrefix( u"HTML__"_ustr );
+ return saHTMLNamePrefix;
+}
+
+OUString ScfTools::GetNameFromHTMLIndex( sal_uInt32 nIndex )
+{
+ OUString aName = GetHTMLIndexPrefix() +
+ OUString::number( static_cast< sal_Int32 >( nIndex ) );
+ return aName;
+}
+
+OUString ScfTools::GetNameFromHTMLName( std::u16string_view rTabName )
+{
+ return GetHTMLNamePrefix() + rTabName;
+}
+
+bool ScfTools::IsHTMLDocName( std::u16string_view rSource )
+{
+ return o3tl::equalsIgnoreAsciiCase( rSource, GetHTMLDocName() );
+}
+
+bool ScfTools::IsHTMLTablesName( std::u16string_view rSource )
+{
+ return o3tl::equalsIgnoreAsciiCase( rSource, GetHTMLTablesName() );
+}
+
+bool ScfTools::GetHTMLNameFromName( const OUString& rSource, OUString& rName )
+{
+ rName.clear();
+ if( rSource.startsWithIgnoreAsciiCase( GetHTMLNamePrefix() ) )
+ {
+ rName = rSource.copy( GetHTMLNamePrefix().getLength() );
+ ScGlobal::AddQuotes( rName, '"', false );
+ }
+ else if( rSource.startsWithIgnoreAsciiCase( GetHTMLIndexPrefix() ) )
+ {
+ OUString aIndex( rSource.copy( GetHTMLIndexPrefix().getLength() ) );
+ if( CharClass::isAsciiNumeric( aIndex ) && (aIndex.toInt32() > 0) )
+ rName = aIndex;
+ }
+ return !rName.isEmpty();
+}
+
+ScFormatFilterPluginImpl::ScFormatFilterPluginImpl() {}
+ScFormatFilterPluginImpl::~ScFormatFilterPluginImpl() {}
+
+ScOrcusFilters* ScFormatFilterPluginImpl::GetOrcusFilters()
+{
+ static ScOrcusFiltersImpl aImpl;
+ return &aImpl;
+}
+
+ScFormatFilterPlugin * ScFilterCreate()
+{
+ return new ScFormatFilterPluginImpl();
+}
+
+// implementation class inside the filters
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/sharedformulagroups.cxx b/sc/source/filter/ftools/sharedformulagroups.cxx
new file mode 100644
index 0000000000..9b028d9eb3
--- /dev/null
+++ b/sc/source/filter/ftools/sharedformulagroups.cxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <sharedformulagroups.hxx>
+#include <tokenarray.hxx>
+
+namespace sc {
+
+void SharedFormulaGroups::set( size_t nSharedId, std::unique_ptr<ScTokenArray> pArray )
+{
+ m_Store.try_emplace(nSharedId, std::move(pArray), ScAddress(ScAddress::INITIALIZE_INVALID));
+}
+
+void SharedFormulaGroups::set( size_t nSharedId, std::unique_ptr<ScTokenArray> pArray, const ScAddress& rOrigin )
+{
+ m_Store.try_emplace(nSharedId, std::move(pArray), rOrigin);
+}
+
+const ScTokenArray* SharedFormulaGroups::get( size_t nSharedId ) const
+{
+ StoreType::const_iterator const it = m_Store.find(nSharedId);
+ return it == m_Store.end() ? nullptr : it->second.getTokenArray();
+}
+
+const SharedFormulaGroupEntry* SharedFormulaGroups::getEntry( size_t nSharedId ) const
+{
+ StoreType::const_iterator const it = m_Store.find(nSharedId);
+ return it == m_Store.end() ? nullptr : &(it->second);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlexp.cxx b/sc/source/filter/html/htmlexp.cxx
new file mode 100644
index 0000000000..2a3cb6a4f8
--- /dev/null
+++ b/sc/source/filter/html/htmlexp.cxx
@@ -0,0 +1,1405 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svx/xoutbmp.hxx>
+#include <editeng/editeng.hxx>
+#include <svtools/htmlcfg.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frmhtmlw.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/urihelper.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/parhtml.hxx>
+#include <vcl/outdev.hxx>
+#include <stdio.h>
+#include <osl/diagnose.h>
+#include <o3tl/unit_conversion.hxx>
+
+#include <htmlexp.hxx>
+#include <global.hxx>
+#include <postit.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <stlpool.hxx>
+#include <scresid.hxx>
+#include <formulacell.hxx>
+#include <cellform.hxx>
+#include <docoptio.hxx>
+#include <editutil.hxx>
+#include <ftools.hxx>
+#include <cellvalue.hxx>
+#include <conditio.hxx>
+#include <colorscale.hxx>
+#include <mtvelements.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/borderline.hxx>
+
+// Without strings.hrc: error C2679: binary '=' : no operator defined which takes a
+// right-hand operand of type 'const class String (__stdcall *)(class ScResId)'
+// at
+// const String aStrTable( ScResId( SCSTR_TABLE ) ); aStrOut = aStrTable;
+// ?!???
+#include <strings.hrc>
+#include <globstr.hrc>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <rtl/strbuf.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+const char sMyBegComment[] = "<!-- ";
+const char sMyEndComment[] = " -->";
+const char sDisplay[] = "display:";
+const char sBorder[] = "border:";
+const char sBackground[] = "background:";
+
+const sal_uInt16 ScHTMLExport::nDefaultFontSize[SC_HTML_FONTSIZES] =
+{
+ HTMLFONTSZ1_DFLT, HTMLFONTSZ2_DFLT, HTMLFONTSZ3_DFLT, HTMLFONTSZ4_DFLT,
+ HTMLFONTSZ5_DFLT, HTMLFONTSZ6_DFLT, HTMLFONTSZ7_DFLT
+};
+
+sal_uInt16 ScHTMLExport::nFontSize[SC_HTML_FONTSIZES] = { 0 };
+
+const char* ScHTMLExport::pFontSizeCss[SC_HTML_FONTSIZES] =
+{
+ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
+};
+
+const sal_uInt16 ScHTMLExport::nCellSpacing = 0;
+const char ScHTMLExport::sIndentSource[nIndentMax+1] =
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+// Macros for HTML export
+
+#define TAG_ON( tag ) HTMLOutFuncs::Out_AsciiTag( rStrm, tag )
+#define TAG_OFF( tag ) HTMLOutFuncs::Out_AsciiTag( rStrm, tag, false )
+#define OUT_STR( str ) HTMLOutFuncs::Out_String( rStrm, str, &aNonConvertibleChars )
+#define OUT_LF() rStrm.WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() )
+#define TAG_ON_LF( tag ) (TAG_ON( tag ).WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() ))
+#define TAG_OFF_LF( tag ) (TAG_OFF( tag ).WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() ))
+#define OUT_HR() TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_horzrule )
+#define OUT_COMMENT( comment ) (rStrm.WriteOString( sMyBegComment ), OUT_STR( comment ) \
+ .WriteOString( sMyEndComment ).WriteOString( SAL_NEWLINE_STRING ) \
+ .WriteOString( GetIndentStr() ))
+
+#define OUT_SP_CSTR_ASS( s ) rStrm.WriteChar(' ').WriteOString( s ).WriteChar( '=' )
+
+#define GLOBSTR(id) ScResId( id )
+
+void ScFormatFilterPluginImpl::ScExportHTML( SvStream& rStrm, const OUString& rBaseURL, ScDocument* pDoc,
+ const ScRange& rRange, const rtl_TextEncoding /*eNach*/, bool bAll,
+ const OUString& rStreamPath, OUString& rNonConvertibleChars, const OUString& rFilterOptions )
+{
+ ScHTMLExport aEx( rStrm, rBaseURL, pDoc, rRange, bAll, rStreamPath, rFilterOptions );
+ aEx.Write();
+ rNonConvertibleChars = aEx.GetNonConvertibleChars();
+}
+
+static OString lcl_getColGroupString(sal_Int32 nSpan, sal_Int32 nWidth)
+{
+ OStringBuffer aByteStr(OString::Concat(OOO_STRING_SVTOOLS_HTML_colgroup)
+ + " ");
+ if( nSpan > 1 )
+ {
+ aByteStr.append(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_span)
+ + "=\""
+ + OString::number(nSpan)
+ + "\" ");
+ }
+ aByteStr.append(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_width)
+ + "=\""
+ + OString::number(nWidth)
+ + "\"");
+ return aByteStr.makeStringAndClear();
+}
+
+static void lcl_AddStamp( OUString& rStr, std::u16string_view rName,
+ const css::util::DateTime& rDateTime,
+ const LocaleDataWrapper& rLoc )
+{
+ Date aD(rDateTime.Day, rDateTime.Month, rDateTime.Year);
+ tools::Time aT(rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds,
+ rDateTime.NanoSeconds);
+ DateTime aDateTime(aD,aT);
+
+ OUString aStrDate = rLoc.getDate( aDateTime );
+ OUString aStrTime = rLoc.getTime( aDateTime );
+
+ rStr += GLOBSTR( STR_BY ) + " ";
+ if (!rName.empty())
+ rStr += rName;
+ else
+ rStr += "???";
+ rStr += " " + GLOBSTR( STR_ON ) + " ";
+ if (!aStrDate.isEmpty())
+ rStr += aStrDate;
+ else
+ rStr += "???";
+ rStr += ", ";
+ if (!aStrTime.isEmpty())
+ rStr += aStrTime;
+ else
+ rStr += "???";
+}
+
+static OString lcl_makeHTMLColorTriplet(const Color& rColor)
+{
+ char buf[24];
+
+ // <font COLOR="#00FF40">hello</font>
+ snprintf( buf, 24, "\"#%02X%02X%02X\"", rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
+
+ return buf;
+}
+
+ScHTMLExport::ScHTMLExport( SvStream& rStrmP, OUString _aBaseURL, ScDocument* pDocP,
+ const ScRange& rRangeP, bool bAllP,
+ OUString aStreamPathP, std::u16string_view rFilterOptions ) :
+ ScExportBase( rStrmP, pDocP, rRangeP ),
+ aBaseURL(std::move( _aBaseURL )),
+ aStreamPath(std::move( aStreamPathP )),
+ pAppWin( Application::GetDefaultDevice() ),
+ nUsedTables( 0 ),
+ nIndent( 0 ),
+ bAll( bAllP ),
+ bTabHasGraphics( false ),
+ bTabAlignedLeft( false ),
+ bCalcAsShown( pDocP->GetDocOptions().IsCalcAsShown() ),
+ bTableDataHeight( true ),
+ mbSkipImages ( false ),
+ mbSkipHeaderFooter( false )
+{
+ strcpy( sIndent, sIndentSource );
+ sIndent[0] = 0;
+
+ // set HTML configuration
+ bCopyLocalFileToINet = officecfg::Office::Common::Filter::HTML::Export::LocalGraphic::get();
+
+ if (rFilterOptions == u"SkipImages")
+ {
+ mbSkipImages = true;
+ }
+ else if (rFilterOptions == u"SkipHeaderFooter")
+ {
+ mbSkipHeaderFooter = true;
+ }
+
+ for ( sal_uInt16 j=0; j < SC_HTML_FONTSIZES; j++ )
+ {
+ sal_uInt16 nSize = SvxHtmlOptions::GetFontSize( j );
+ // remember in Twips, like our SvxFontHeightItem
+ if ( nSize )
+ nFontSize[j] = nSize * 20;
+ else
+ nFontSize[j] = nDefaultFontSize[j] * 20;
+ }
+
+ const SCTAB nCount = pDoc->GetTableCount();
+ for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
+ {
+ if ( !IsEmptyTable( nTab ) )
+ nUsedTables++;
+ }
+}
+
+ScHTMLExport::~ScHTMLExport()
+{
+ aGraphList.clear();
+}
+
+sal_uInt16 ScHTMLExport::GetFontSizeNumber( sal_uInt16 nHeight )
+{
+ sal_uInt16 nSize = 1;
+ for ( sal_uInt16 j=SC_HTML_FONTSIZES-1; j>0; j-- )
+ {
+ if( nHeight > (nFontSize[j] + nFontSize[j-1]) / 2 )
+ { // The one next to it
+ nSize = j+1;
+ break;
+ }
+ }
+ return nSize;
+}
+
+const char* ScHTMLExport::GetFontSizeCss( sal_uInt16 nHeight )
+{
+ sal_uInt16 nSize = GetFontSizeNumber( nHeight );
+ return pFontSizeCss[ nSize-1 ];
+}
+
+sal_uInt16 ScHTMLExport::ToPixel( sal_uInt16 nVal )
+{
+ if( nVal )
+ {
+ nVal = static_cast<sal_uInt16>(pAppWin->LogicToPixel(
+ Size( nVal, nVal ), MapMode( MapUnit::MapTwip ) ).Width());
+ if( !nVal ) // If there's a Twip there should also be a Pixel
+ nVal = 1;
+ }
+ return nVal;
+}
+
+Size ScHTMLExport::MMToPixel( const Size& rSize )
+{
+ Size aSize = pAppWin->LogicToPixel( rSize, MapMode( MapUnit::Map100thMM ) );
+ // If there's something there should also be a Pixel
+ if ( !aSize.Width() && rSize.Width() )
+ aSize.setWidth( 1 );
+ if ( !aSize.Height() && rSize.Height() )
+ aSize.setHeight( 1 );
+ return aSize;
+}
+
+void ScHTMLExport::Write()
+{
+ if (!mbSkipHeaderFooter)
+ {
+ rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_doctype ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_doctype5 ).WriteChar( '>' )
+ .WriteOString( SAL_NEWLINE_STRING ).WriteOString( SAL_NEWLINE_STRING );
+ TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_html );
+ WriteHeader();
+ OUT_LF();
+ }
+ WriteBody();
+ OUT_LF();
+ if (!mbSkipHeaderFooter)
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_html );
+}
+
+void ScHTMLExport::WriteHeader()
+{
+ IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_head );
+
+ if ( pDoc->IsClipOrUndo() )
+ { // no real DocInfo available, but some META information like charset needed
+ SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, nullptr, sIndent, &aNonConvertibleChars );
+ }
+ else
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<cppu::OWeakObject*>(pDoc->GetDocumentShell()->GetModel()), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+ SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, xDocProps,
+ sIndent, &aNonConvertibleChars );
+ OUT_LF();
+
+ if (!xDocProps->getPrintedBy().isEmpty())
+ {
+ OUT_COMMENT( GLOBSTR( STR_DOC_INFO ) );
+ OUString aStrOut = GLOBSTR( STR_DOC_PRINTED ) + ": ";
+ lcl_AddStamp( aStrOut, xDocProps->getPrintedBy(),
+ xDocProps->getPrintDate(), ScGlobal::getLocaleData() );
+ OUT_COMMENT( aStrOut );
+ }
+
+ }
+ OUT_LF();
+
+ // CSS1 StyleSheet
+ PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
+ IncIndent(1);
+ rStrm.WriteOString( "<" ).WriteOString( OOO_STRING_SVTOOLS_HTML_style ).WriteOString( " " ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_type ).WriteOString( "=\"text/css\">" );
+
+ OUT_LF();
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_body);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_division);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_table);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_thead);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tbody);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tfoot);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tablerow);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tableheader);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_tabledata);
+ rStrm.WriteOString(",");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_parabreak);
+ rStrm.WriteOString(" { ");
+ rStrm.WriteOString("font-family:");
+
+ if (!aHTMLStyle.aFontFamilyName.isEmpty())
+ {
+ const OUString& rList = aHTMLStyle.aFontFamilyName;
+ for(sal_Int32 nPos {0};;)
+ {
+ rStrm.WriteChar( '\"' );
+ OUT_STR( rList.getToken( 0, ';', nPos ) );
+ rStrm.WriteChar( '\"' );
+ if (nPos<0)
+ break;
+ rStrm.WriteOString( ", " );
+ }
+ }
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString("font-size:");
+ rStrm.WriteOString(GetFontSizeCss(static_cast<sal_uInt16>(aHTMLStyle.nFontHeight)));
+ rStrm.WriteOString(" }");
+
+ OUT_LF();
+
+ // write the style for the comments to make them stand out from normal cell content
+ // this is done through only showing the cell contents when the custom indicator is hovered
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_anchor);
+ rStrm.WriteOString(".comment-indicator:hover");
+ rStrm.WriteOString(" + ");
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_comment2);
+ rStrm.WriteOString(" { ");
+ rStrm.WriteOString(sBackground);
+ rStrm.WriteOString("#ffd");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString("position:");
+ rStrm.WriteOString("absolute");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(sDisplay);
+ rStrm.WriteOString("block");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(sBorder);
+ rStrm.WriteOString("1px solid black");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString("padding:");
+ rStrm.WriteOString("0.5em");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(" } ");
+
+ OUT_LF();
+
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_anchor);
+ rStrm.WriteOString(".comment-indicator");
+ rStrm.WriteOString(" { ");
+ rStrm.WriteOString(sBackground);
+ rStrm.WriteOString("red");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(sDisplay);
+ rStrm.WriteOString("inline-block");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(sBorder);
+ rStrm.WriteOString("1px solid black");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString("width:");
+ rStrm.WriteOString("0.5em");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString("height:");
+ rStrm.WriteOString("0.5em");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(" } ");
+
+ OUT_LF();
+
+ rStrm.WriteOString(OOO_STRING_SVTOOLS_HTML_comment2);
+ rStrm.WriteOString(" { ");
+ rStrm.WriteOString(sDisplay);
+ rStrm.WriteOString("none");
+ rStrm.WriteOString("; ");
+ rStrm.WriteOString(" } ");
+
+
+ IncIndent(-1);
+ OUT_LF();
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_style );
+
+ IncIndent(-1);
+ OUT_LF();
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head );
+}
+
+void ScHTMLExport::WriteOverview()
+{
+ if ( nUsedTables <= 1 )
+ return;
+
+ IncIndent(1);
+ OUT_HR();
+ IncIndent(1); TAG_ON( OOO_STRING_SVTOOLS_HTML_parabreak ); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_center );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
+ OUT_STR( ScResId( STR_OVERVIEW ) );
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head1 );
+
+ OUString aStr;
+
+ const SCTAB nCount = pDoc->GetTableCount();
+ for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
+ {
+ if ( !IsEmptyTable( nTab ) )
+ {
+ pDoc->GetName( nTab, aStr );
+ rStrm.WriteOString( "<A HREF=\"#table" )
+ .WriteOString( OString::number(nTab) )
+ .WriteOString( "\">" );
+ OUT_STR( aStr );
+ rStrm.WriteOString( "</A>" );
+ TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_linebreak );
+ }
+ }
+
+ IncIndent(-1); OUT_LF();
+ IncIndent(-1); TAG_OFF( OOO_STRING_SVTOOLS_HTML_center ); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_parabreak );
+}
+
+const SfxItemSet& ScHTMLExport::PageDefaults( SCTAB nTab )
+{
+ SfxStyleSheetBasePool* pStylePool = pDoc->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = nullptr;
+ OSL_ENSURE( pStylePool, "StylePool not found! :-(" );
+
+ // remember defaults for compare in WriteCell
+ if ( !aHTMLStyle.bInitialized )
+ {
+ pStyleSheet = pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD),
+ SfxStyleFamily::Para );
+ OSL_ENSURE( pStyleSheet, "ParaStyle not found! :-(" );
+ if (!pStyleSheet)
+ pStyleSheet = pStylePool->First(SfxStyleFamily::Para);
+ const SfxItemSet& rSetPara = pStyleSheet->GetItemSet();
+
+ aHTMLStyle.nDefaultScriptType = ScGlobal::GetDefaultScriptType();
+ aHTMLStyle.aFontFamilyName = static_cast<const SvxFontItem&>((rSetPara.Get(
+ ScGlobal::GetScriptedWhichID(
+ aHTMLStyle.nDefaultScriptType, ATTR_FONT
+ )))).GetFamilyName();
+ aHTMLStyle.nFontHeight = static_cast<const SvxFontHeightItem&>((rSetPara.Get(
+ ScGlobal::GetScriptedWhichID(
+ aHTMLStyle.nDefaultScriptType, ATTR_FONT_HEIGHT
+ )))).GetHeight();
+ aHTMLStyle.nFontSizeNumber = GetFontSizeNumber( static_cast< sal_uInt16 >( aHTMLStyle.nFontHeight ) );
+ }
+
+ // Page style sheet printer settings, e.g. for background graphics.
+ // There's only one background graphic in HTML!
+ pStyleSheet = pStylePool->Find( pDoc->GetPageStyle( nTab ), SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-(" );
+ if (!pStyleSheet)
+ pStyleSheet = pStylePool->First(SfxStyleFamily::Page);
+ const SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ if ( !aHTMLStyle.bInitialized )
+ {
+ const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
+ aHTMLStyle.aBackgroundColor = pBrushItem->GetColor();
+ aHTMLStyle.bInitialized = true;
+ }
+ return rSet;
+}
+
+OString ScHTMLExport::BorderToStyle(const char* pBorderName,
+ const SvxBorderLine* pLine, bool& bInsertSemicolon)
+{
+ OStringBuffer aOut;
+
+ if ( pLine )
+ {
+ if ( bInsertSemicolon )
+ aOut.append("; ");
+
+ // which border
+ aOut.append(OString::Concat("border-") + pBorderName + ": ");
+
+ // thickness
+ int nWidth = pLine->GetWidth();
+ int nPxWidth = (nWidth > 0) ?
+ std::max(o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px), sal_Int64(1)) : 0;
+ aOut.append(OString::number(nPxWidth) + "px ");
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ aOut.append("solid");
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ aOut.append("dotted");
+ break;
+ case SvxBorderLineStyle::DASHED:
+ case SvxBorderLineStyle::DASH_DOT:
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ aOut.append("dashed");
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ aOut.append("double");
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ aOut.append("ridge");
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ aOut.append("groove");
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ aOut.append("outset");
+ break;
+ case SvxBorderLineStyle::INSET:
+ aOut.append("inset");
+ break;
+ default:
+ aOut.append("hidden");
+ }
+ aOut.append(" #");
+
+ // color
+ char hex[7];
+ snprintf( hex, 7, "%06" SAL_PRIxUINT32, static_cast<sal_uInt32>( pLine->GetColor().GetRGBColor() ) );
+ hex[6] = 0;
+
+ aOut.append(hex);
+
+ bInsertSemicolon = true;
+ }
+
+ return aOut.makeStringAndClear();
+}
+
+void ScHTMLExport::WriteBody()
+{
+ const SfxItemSet& rSet = PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
+ const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
+
+ // default text color black
+ if (!mbSkipHeaderFooter)
+ {
+ rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_body );
+
+ if (!mbSkipImages)
+ {
+ if ( bAll && GPOS_NONE != pBrushItem->GetGraphicPos() )
+ {
+ OUString aLink = pBrushItem->GetGraphicLink();
+ OUString aGrfNm;
+
+ // Embedded graphic -> write using WriteGraphic
+ if( aLink.isEmpty() )
+ {
+ const Graphic* pGrf = pBrushItem->GetGraphic();
+ if( pGrf )
+ {
+ // Save graphic as (JPG) file
+ aGrfNm = aStreamPath;
+ ErrCode nErr = XOutBitmap::WriteGraphic( *pGrf, aGrfNm,
+ "JPG", XOutFlags::UseNativeIfPossible );
+ if( !nErr ) // Contains errors, as we have nothing to output
+ {
+ aGrfNm = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ aGrfNm, URIHelper::GetMaybeFileHdl());
+ aLink = aGrfNm;
+ }
+ }
+ }
+ else
+ {
+ aGrfNm = aLink;
+ if( bCopyLocalFileToINet )
+ {
+ CopyLocalFileToINet( aGrfNm, aStreamPath );
+ }
+ else
+ aGrfNm = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ aGrfNm, URIHelper::GetMaybeFileHdl());
+ aLink = aGrfNm;
+ }
+ if( !aLink.isEmpty() )
+ {
+ rStrm.WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_background ).WriteOString( "=\"" );
+ OUT_STR( URIHelper::simpleNormalizedMakeRelative(
+ aBaseURL,
+ aLink ) ).WriteChar( '\"' );
+ }
+ }
+ }
+ if ( !aHTMLStyle.aBackgroundColor.IsTransparent() )
+ { // A transparent background color should always result in default
+ // background of the browser. Also, HTMLOutFuncs::Out_Color() writes
+ // black #000000 for COL_AUTO which is the same as white #ffffff with
+ // transparency set to 0xff, our default background.
+ OUT_SP_CSTR_ASS( OOO_STRING_SVTOOLS_HTML_O_bgcolor );
+ HTMLOutFuncs::Out_Color( rStrm, aHTMLStyle.aBackgroundColor );
+ }
+
+ rStrm.WriteChar( '>' ); OUT_LF();
+ }
+
+ if ( bAll )
+ WriteOverview();
+
+ WriteTables();
+
+ if (!mbSkipHeaderFooter)
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_body );
+}
+
+void ScHTMLExport::WriteTables()
+{
+ const SCTAB nTabCount = pDoc->GetTableCount();
+ const OUString aStrTable( ScResId( SCSTR_TABLE ) );
+ OUString aStr;
+ OUString aStrOut;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ SCCOL nStartColFix = 0;
+ SCROW nStartRowFix = 0;
+ SCCOL nEndColFix = 0;
+ SCROW nEndRowFix = 0;
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ if ( bAll )
+ {
+ nStartTab = 0;
+ nEndTab = nTabCount - 1;
+ }
+ else
+ {
+ nStartCol = nStartColFix = aRange.aStart.Col();
+ nStartRow = nStartRowFix = aRange.aStart.Row();
+ nStartTab = aRange.aStart.Tab();
+ nEndCol = nEndColFix = aRange.aEnd.Col();
+ nEndRow = nEndRowFix = aRange.aEnd.Row();
+ nEndTab = aRange.aEnd.Tab();
+ }
+ SCTAB nTableStrNum = 1;
+ for ( SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++ )
+ {
+ if ( !pDoc->IsVisible( nTab ) )
+ continue; // for
+
+ if ( bAll )
+ {
+ if ( !GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
+ continue; // for
+
+ if ( nUsedTables > 1 )
+ {
+ aStrOut = aStrTable + " " + OUString::number( nTableStrNum++ ) + ": ";
+
+ OUT_HR();
+
+ // Write anchor
+ rStrm.WriteOString( "<A NAME=\"table" )
+ .WriteOString( OString::number(nTab) )
+ .WriteOString( "\">" );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
+ OUT_STR( aStrOut );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_emphasis );
+
+ pDoc->GetName( nTab, aStr );
+ OUT_STR( aStr );
+
+ TAG_OFF( OOO_STRING_SVTOOLS_HTML_emphasis );
+ TAG_OFF( OOO_STRING_SVTOOLS_HTML_head1 );
+ rStrm.WriteOString( "</A>" ); OUT_LF();
+ }
+ }
+ else
+ {
+ nStartCol = nStartColFix;
+ nStartRow = nStartRowFix;
+ nEndCol = nEndColFix;
+ nEndRow = nEndRowFix;
+ if ( !TrimDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
+ continue; // for
+ }
+
+ // <TABLE ...>
+ OStringBuffer aByteStrOut(OOO_STRING_SVTOOLS_HTML_table);
+
+ bTabHasGraphics = bTabAlignedLeft = false;
+ if ( bAll && pDrawLayer )
+ PrepareGraphics( pDrawLayer, nTab, nStartCol, nStartRow,
+ nEndCol, nEndRow );
+
+ // more <TABLE ...>
+ if ( bTabAlignedLeft )
+ {
+ aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
+ "=\""
+ OOO_STRING_SVTOOLS_HTML_AL_left "\"");
+ }
+ // ALIGN=LEFT allow text and graphics to flow around
+ // CELLSPACING
+ aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
+ "=\"" +
+ OString::number(nCellSpacing) + "\"");
+
+ // BORDER=0, we do the styling of the cells in <TD>
+ aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_border "=\"0\"");
+ IncIndent(1); TAG_ON_LF( aByteStrOut.makeStringAndClear() );
+
+ // --- <COLGROUP> ----
+ {
+ SCCOL nCol = nStartCol;
+ sal_Int32 nWidth = 0;
+ sal_Int32 nSpan = 0;
+ while( nCol <= nEndCol )
+ {
+ if( pDoc->ColHidden(nCol, nTab) )
+ {
+ ++nCol;
+ continue;
+ }
+
+ if( nWidth != ToPixel( pDoc->GetColWidth( nCol, nTab ) ) )
+ {
+ if( nSpan != 0 )
+ {
+ TAG_ON(lcl_getColGroupString(nSpan, nWidth));
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
+ }
+ nWidth = ToPixel( pDoc->GetColWidth( nCol, nTab ) );
+ nSpan = 1;
+ }
+ else
+ nSpan++;
+ nCol++;
+ }
+ if( nSpan )
+ {
+ TAG_ON(lcl_getColGroupString(nSpan, nWidth));
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
+ }
+ }
+
+ // <TBODY> // Re-enable only when THEAD and TFOOT are exported
+ // IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tbody );
+ // At least old (3.x, 4.x?) Netscape doesn't follow <TABLE COLS=n> and
+ // <COL WIDTH=x> specified, but needs a width at every column.
+ bool bHasHiddenRows = pDoc->HasHiddenRows(nStartRow, nEndRow, nTab);
+ // We need to cache sc::ColumnBlockPosition per each column.
+ std::vector< sc::ColumnBlockPosition > blockPos( nEndCol - nStartCol + 1 );
+ for( SCCOL i = nStartCol; i <= nEndCol; ++i )
+ pDoc->InitColumnBlockPosition( blockPos[ i - nStartCol ], nTab, i );
+ for ( SCROW nRow=nStartRow; nRow<=nEndRow; nRow++ )
+ {
+ if ( bHasHiddenRows && pDoc->RowHidden(nRow, nTab) )
+ {
+ nRow = pDoc->FirstVisibleRow(nRow+1, nEndRow, nTab);
+ --nRow;
+ continue; // for
+ }
+
+ IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
+ bTableDataHeight = true; // height at every first cell of each row
+ for ( SCCOL nCol2=nStartCol; nCol2<=nEndCol; nCol2++ )
+ {
+ if ( pDoc->ColHidden(nCol2, nTab) )
+ continue; // for
+
+ if ( nCol2 == nEndCol )
+ IncIndent(-1);
+ WriteCell( blockPos[ nCol2 - nStartCol ], nCol2, nRow, nTab );
+ bTableDataHeight = false;
+ }
+
+ if ( nRow == nEndRow )
+ IncIndent(-1);
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
+ }
+ // TODO: Uncomment later
+ // IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tbody );
+
+ IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_table );
+
+ if ( bTabHasGraphics && !mbSkipImages )
+ {
+ // the rest that is not in a cell
+ size_t ListSize = aGraphList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScHTMLGraphEntry* pE = &aGraphList[ i ];
+ if ( !pE->bWritten )
+ WriteGraphEntry( pE );
+ }
+ aGraphList.clear();
+ if ( bTabAlignedLeft )
+ {
+ // clear <TABLE ALIGN=LEFT> with <BR CLEAR=LEFT>
+ aByteStrOut.append(
+ OOO_STRING_SVTOOLS_HTML_linebreak
+ " "
+ OOO_STRING_SVTOOLS_HTML_O_clear "="
+ OOO_STRING_SVTOOLS_HTML_AL_left);
+ TAG_ON_LF( aByteStrOut.makeStringAndClear() );
+ }
+ }
+
+ if ( bAll )
+ OUT_COMMENT( "**************************************************************************" );
+ }
+}
+
+void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ std::optional<Color> aColorScale;
+ ScAddress aPos( nCol, nRow, nTab );
+ ScRefCellValue aCell(*pDoc, aPos, rBlockPos);
+ const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
+ const SfxItemSet* pCondItemSet = pDoc->GetCondResult( nCol, nRow, nTab, &aCell );
+ if (!pCondItemSet)
+ {
+ ScConditionalFormatList* pCondList = pDoc->GetCondFormList(nTab);
+ const ScCondFormatItem& rCondItem = pAttr->GetItem(ATTR_CONDITIONAL);
+ const ScCondFormatIndexes& rCondIndex = rCondItem.GetCondFormatData();
+ if (rCondIndex.size() > 0)
+ {
+ ScConditionalFormat* pCondFmt = pCondList->GetFormat(rCondIndex[0]);
+ if (pCondFmt)
+ {
+ const ScColorScaleFormat* pEntry = dynamic_cast<const ScColorScaleFormat*>(pCondFmt->GetEntry(0));
+ if (pEntry)
+ aColorScale = pEntry->GetColor(aPos);
+ }
+ }
+ }
+
+ const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG, pCondItemSet );
+ if ( rMergeFlagAttr.IsOverlapped() )
+ return ;
+
+ ScHTMLGraphEntry* pGraphEntry = nullptr;
+ if ( bTabHasGraphics && !mbSkipImages )
+ {
+ size_t ListSize = aGraphList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScHTMLGraphEntry* pE = &aGraphList[ i ];
+ if ( pE->bInCell && pE->aRange.Contains( aPos ) )
+ {
+ if ( pE->aRange.aStart == aPos )
+ {
+ pGraphEntry = pE;
+ break; // for
+ }
+ else
+ return ; // Is a Col/RowSpan, Overlapped
+ }
+ }
+ }
+
+ sal_uInt32 nFormat = pAttr->GetNumberFormat( pFormatter );
+ bool bValueData = aCell.hasNumeric();
+ SvtScriptType nScriptType = SvtScriptType::NONE;
+ if (!aCell.isEmpty())
+ nScriptType = pDoc->GetScriptType(nCol, nRow, nTab, &aCell);
+
+ if ( nScriptType == SvtScriptType::NONE )
+ nScriptType = aHTMLStyle.nDefaultScriptType;
+
+ OStringBuffer aStrTD(OOO_STRING_SVTOOLS_HTML_tabledata);
+
+ // border of the cells
+ const SvxBoxItem* pBorder = pDoc->GetAttr( nCol, nRow, nTab, ATTR_BORDER );
+ if ( pBorder && (pBorder->GetTop() || pBorder->GetBottom() || pBorder->GetLeft() || pBorder->GetRight()) )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_style "=\"");
+
+ bool bInsertSemicolon = false;
+ aStrTD.append(BorderToStyle("top", pBorder->GetTop(),
+ bInsertSemicolon));
+ aStrTD.append(BorderToStyle("bottom", pBorder->GetBottom(),
+ bInsertSemicolon));
+ aStrTD.append(BorderToStyle("left", pBorder->GetLeft(),
+ bInsertSemicolon));
+ aStrTD.append(BorderToStyle("right", pBorder->GetRight(),
+ bInsertSemicolon));
+
+ aStrTD.append('"');
+ }
+
+ const char* pChar;
+ sal_uInt16 nHeightPixel;
+
+ const ScMergeAttr& rMergeAttr = pAttr->GetItem( ATTR_MERGE, pCondItemSet );
+ if ( pGraphEntry || rMergeAttr.IsMerged() )
+ {
+ SCCOL nC, jC;
+ SCROW nR;
+ tools::Long v;
+ if ( pGraphEntry )
+ nC = std::max( SCCOL(pGraphEntry->aRange.aEnd.Col() - nCol + 1),
+ rMergeAttr.GetColMerge() );
+ else
+ nC = rMergeAttr.GetColMerge();
+ if ( nC > 1 )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_colspan
+ "=" + OString::number(static_cast<sal_Int32>(nC)));
+ nC = nC + nCol;
+ for ( jC=nCol, v=0; jC<nC; jC++ )
+ v += pDoc->GetColWidth( jC, nTab );
+ }
+
+ if ( pGraphEntry )
+ nR = std::max( SCROW(pGraphEntry->aRange.aEnd.Row() - nRow + 1),
+ rMergeAttr.GetRowMerge() );
+ else
+ nR = rMergeAttr.GetRowMerge();
+ if ( nR > 1 )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_rowspan
+ "=" + OString::number(static_cast<sal_Int32>(nR)));
+ nR += nRow;
+ v = pDoc->GetRowHeight( nRow, nR-1, nTab );
+ nHeightPixel = ToPixel( static_cast< sal_uInt16 >( v ) );
+ }
+ else
+ nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
+ }
+ else
+ nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
+
+ if ( bTableDataHeight )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"" +
+ OString::number(nHeightPixel) + "\"");
+ }
+
+ const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>( pAttr->GetItem(
+ ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT),
+ pCondItemSet) );
+
+ const SvxFontHeightItem& rFontHeightItem = static_cast<const SvxFontHeightItem&>(
+ pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
+ ATTR_FONT_HEIGHT), pCondItemSet) );
+
+ const SvxWeightItem& rWeightItem = static_cast<const SvxWeightItem&>( pAttr->GetItem(
+ ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT_WEIGHT),
+ pCondItemSet) );
+
+ const SvxPostureItem& rPostureItem = static_cast<const SvxPostureItem&>(
+ pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
+ ATTR_FONT_POSTURE), pCondItemSet) );
+
+ const SvxUnderlineItem& rUnderlineItem =
+ pAttr->GetItem( ATTR_FONT_UNDERLINE, pCondItemSet );
+
+ const SvxCrossedOutItem& rCrossedOutItem =
+ pAttr->GetItem( ATTR_FONT_CROSSEDOUT, pCondItemSet );
+
+ const SvxColorItem& rColorItem = pAttr->GetItem(
+ ATTR_FONT_COLOR, pCondItemSet );
+
+ const SvxHorJustifyItem& rHorJustifyItem =
+ pAttr->GetItem( ATTR_HOR_JUSTIFY, pCondItemSet );
+
+ const SvxVerJustifyItem& rVerJustifyItem =
+ pAttr->GetItem( ATTR_VER_JUSTIFY, pCondItemSet );
+
+ const SvxBrushItem& rBrushItem = pAttr->GetItem(
+ ATTR_BACKGROUND, pCondItemSet );
+
+ Color aBgColor;
+ if ( aColorScale )
+ aBgColor = *aColorScale;
+ else if ( rBrushItem.GetColor().GetAlpha() == 0 )
+ aBgColor = aHTMLStyle.aBackgroundColor; // No unwanted background color
+ else
+ aBgColor = rBrushItem.GetColor();
+
+ bool bBold = ( WEIGHT_BOLD <= rWeightItem.GetWeight() );
+ bool bItalic = ( ITALIC_NONE != rPostureItem.GetPosture() );
+ bool bUnderline = ( LINESTYLE_NONE != rUnderlineItem.GetLineStyle() );
+ bool bCrossedOut = ( STRIKEOUT_SINGLE <= rCrossedOutItem.GetStrikeout() );
+ bool bSetFontColor = ( COL_AUTO != rColorItem.GetValue() ); // default is AUTO now
+ bool bSetFontName = ( aHTMLStyle.aFontFamilyName != rFontItem.GetFamilyName() );
+ sal_uInt16 nSetFontSizeNumber = 0;
+ sal_uInt32 nFontHeight = rFontHeightItem.GetHeight();
+ if ( nFontHeight != aHTMLStyle.nFontHeight )
+ {
+ nSetFontSizeNumber = GetFontSizeNumber( static_cast<sal_uInt16>(nFontHeight) );
+ if ( nSetFontSizeNumber == aHTMLStyle.nFontSizeNumber )
+ nSetFontSizeNumber = 0; // no difference, don't set
+ }
+
+ bool bSetFont = (bSetFontColor || bSetFontName || nSetFontSizeNumber);
+
+ //! TODO: we could entirely use CSS1 here instead, but that would exclude
+ //! Netscape 3.0 and Netscape 4.x without JavaScript enabled.
+ //! Do we want that?
+
+ switch( rHorJustifyItem.GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ pChar = (bValueData ? OOO_STRING_SVTOOLS_HTML_AL_right : OOO_STRING_SVTOOLS_HTML_AL_left);
+ break;
+ case SvxCellHorJustify::Center: pChar = OOO_STRING_SVTOOLS_HTML_AL_center; break;
+ case SvxCellHorJustify::Block: pChar = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
+ case SvxCellHorJustify::Right: pChar = OOO_STRING_SVTOOLS_HTML_AL_right; break;
+ case SvxCellHorJustify::Left:
+ case SvxCellHorJustify::Repeat:
+ default: pChar = OOO_STRING_SVTOOLS_HTML_AL_left; break;
+ }
+
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" +
+ OString::Concat(pChar) + "\"");
+
+ switch( rVerJustifyItem.GetValue() )
+ {
+ case SvxCellVerJustify::Top: pChar = OOO_STRING_SVTOOLS_HTML_VA_top; break;
+ case SvxCellVerJustify::Center: pChar = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
+ case SvxCellVerJustify::Bottom: pChar = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
+ case SvxCellVerJustify::Standard:
+ default: pChar = nullptr;
+ }
+ if ( pChar )
+ {
+ aStrTD.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_valign "=") + pChar);
+ }
+
+ if ( aHTMLStyle.aBackgroundColor != aBgColor )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_bgcolor "="
+ + lcl_makeHTMLColorTriplet(aBgColor));
+ }
+
+ double fVal = 0.0;
+ if ( bValueData )
+ {
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ fVal = aCell.getDouble();
+ if ( bCalcAsShown && fVal != 0.0 )
+ fVal = pDoc->RoundValueAsShown( fVal, nFormat );
+ break;
+ case CELLTYPE_FORMULA:
+ fVal = aCell.getFormula()->GetValue();
+ break;
+ default:
+ OSL_FAIL( "value data with unsupported cell type" );
+ }
+ }
+
+ aStrTD.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValueData, fVal,
+ nFormat, *pFormatter, &aNonConvertibleChars));
+
+ TAG_ON(aStrTD.makeStringAndClear());
+
+ //write the note for this as the first thing in the tag
+ ScPostIt* pNote = pDoc->HasNote(aPos) ? pDoc->GetNote(aPos) : nullptr;
+ if (pNote)
+ {
+ //create the comment indicator
+ OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " "
+ OOO_STRING_SVTOOLS_HTML_O_class "=\"comment-indicator\""_ostr;
+ TAG_ON(aStr);
+ TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor);
+ OUT_LF();
+
+ //create the element holding the contents
+ //this is a bit naive, since it doesn't separate
+ //lines into html breaklines yet
+ TAG_ON(OOO_STRING_SVTOOLS_HTML_comment2);
+ OUT_STR( pNote->GetText() );
+ TAG_OFF(OOO_STRING_SVTOOLS_HTML_comment2);
+ OUT_LF();
+ }
+
+ if ( bBold ) TAG_ON( OOO_STRING_SVTOOLS_HTML_bold );
+ if ( bItalic ) TAG_ON( OOO_STRING_SVTOOLS_HTML_italic );
+ if ( bUnderline ) TAG_ON( OOO_STRING_SVTOOLS_HTML_underline );
+ if ( bCrossedOut ) TAG_ON( OOO_STRING_SVTOOLS_HTML_strikethrough );
+
+ if ( bSetFont )
+ {
+ OStringBuffer aStr(OOO_STRING_SVTOOLS_HTML_font);
+ if ( bSetFontName )
+ {
+ aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_face "=\"");
+
+ if (!rFontItem.GetFamilyName().isEmpty())
+ {
+ const OUString& rList = rFontItem.GetFamilyName();
+ for (sal_Int32 nPos {0};;)
+ {
+ OString aTmpStr = HTMLOutFuncs::ConvertStringToHTML(
+ rList.getToken( 0, ';', nPos ),
+ &aNonConvertibleChars);
+ aStr.append(aTmpStr);
+ if (nPos<0)
+ break;
+ aStr.append(',');
+ }
+ }
+
+ aStr.append('\"');
+ }
+ if ( nSetFontSizeNumber )
+ {
+ aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_size "="
+ + OString::number(static_cast<sal_Int32>(nSetFontSizeNumber)));
+ }
+ if ( bSetFontColor )
+ {
+ Color aColor = rColorItem.GetValue();
+
+ // always export automatic text color as black
+ if ( aColor == COL_AUTO )
+ aColor = COL_BLACK;
+
+ aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_color "="
+ + lcl_makeHTMLColorTriplet(aColor));
+ }
+ TAG_ON(aStr.makeStringAndClear());
+ }
+
+ OUString aURL;
+ bool bWriteHyperLink(false);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ if (pFCell->IsHyperLinkCell())
+ {
+ OUString aCellText;
+ pFCell->GetURLResult(aURL, aCellText);
+ bWriteHyperLink = true;
+ }
+ }
+
+ if (bWriteHyperLink)
+ {
+ OString aURLStr = HTMLOutFuncs::ConvertStringToHTML(aURL, &aNonConvertibleChars);
+ OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " " OOO_STRING_SVTOOLS_HTML_O_href "=\"" + aURLStr + "\"";
+ TAG_ON(aStr);
+ }
+
+ OUString aStrOut;
+ bool bFieldText = false;
+
+ const Color* pColor;
+ switch (aCell.getType())
+ {
+ case CELLTYPE_EDIT :
+ bFieldText = WriteFieldText(aCell.getEditText());
+ if ( bFieldText )
+ break;
+ [[fallthrough]];
+ default:
+ aStrOut = ScCellFormat::GetString(aCell, nFormat, &pColor, *pFormatter, *pDoc);
+ }
+
+ if ( !bFieldText )
+ {
+ if ( aStrOut.isEmpty() )
+ {
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak ); // No completely empty line
+ }
+ else
+ {
+ sal_Int32 nPos = aStrOut.indexOf( '\n' );
+ if ( nPos == -1 )
+ {
+ OUT_STR( aStrOut );
+ }
+ else
+ {
+ sal_Int32 nStartPos = 0;
+ do
+ {
+ OUString aSingleLine = aStrOut.copy( nStartPos, nPos - nStartPos );
+ OUT_STR( aSingleLine );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
+ nStartPos = nPos + 1;
+ }
+ while( ( nPos = aStrOut.indexOf( '\n', nStartPos ) ) != -1 );
+ OUString aSingleLine = aStrOut.copy( nStartPos );
+ OUT_STR( aSingleLine );
+ }
+ }
+ }
+ if ( pGraphEntry )
+ WriteGraphEntry( pGraphEntry );
+
+ if (bWriteHyperLink) { TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor); }
+
+ if ( bSetFont ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_font );
+ if ( bCrossedOut ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_strikethrough );
+ if ( bUnderline ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_underline );
+ if ( bItalic ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_italic );
+ if ( bBold ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_bold );
+
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tabledata );
+}
+
+bool ScHTMLExport::WriteFieldText( const EditTextObject* pData )
+{
+ bool bFields = false;
+ // text and anchor of URL fields, Doc-Engine is a ScFieldEditEngine
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText( *pData );
+ sal_Int32 nParas = rEngine.GetParagraphCount();
+ if ( nParas )
+ {
+ ESelection aSel( 0, 0, nParas-1, rEngine.GetTextLen( nParas-1 ) );
+ SfxItemSet aSet( rEngine.GetAttribs( aSel ) );
+ SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ bFields = true;
+ }
+ if ( bFields )
+ {
+ bool bOldUpdateMode = rEngine.SetUpdateLayout( true ); // no portions if not formatted
+ for ( sal_Int32 nPar=0; nPar < nParas; nPar++ )
+ {
+ if ( nPar > 0 )
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
+ std::vector<sal_Int32> aPortions;
+ rEngine.GetPortions( nPar, aPortions );
+ sal_Int32 nStart = 0;
+ for ( const sal_Int32 nEnd : aPortions )
+ {
+ ESelection aSel( nPar, nStart, nPar, nEnd );
+ bool bUrl = false;
+ // fields are single characters
+ if ( nEnd == nStart+1 )
+ {
+ SfxItemSet aSet = rEngine.GetAttribs( aSel );
+ if ( const SvxFieldItem* pFieldItem = aSet.GetItemIfSet( EE_FEATURE_FIELD, false ) )
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ bUrl = true;
+ rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_href ).WriteOString( "=\"" );
+ OUT_STR( pURLField->GetURL() );
+ rStrm.WriteOString( "\">" );
+ OUT_STR( pURLField->GetRepresentation() );
+ rStrm.WriteOString( "</" ).WriteOString( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( '>' );
+ }
+ }
+ }
+ if ( !bUrl )
+ OUT_STR( rEngine.GetText( aSel ) );
+ nStart = nEnd;
+ }
+ }
+ rEngine.SetUpdateLayout( bOldUpdateMode );
+ }
+ return bFields;
+}
+
+void ScHTMLExport::CopyLocalFileToINet( OUString& rFileNm,
+ std::u16string_view rTargetNm )
+{
+ INetURLObject aFileUrl, aTargetUrl;
+ aFileUrl.SetSmartURL( rFileNm );
+ aTargetUrl.SetSmartURL( rTargetNm );
+ if (!(INetProtocol::File == aFileUrl.GetProtocol()
+ && (INetProtocol::Http == aTargetUrl.GetProtocol()
+ || INetProtocol::Https == aTargetUrl.GetProtocol()
+ || INetProtocol::VndSunStarWebdav == aTargetUrl.GetProtocol()
+ || INetProtocol::Smb == aTargetUrl.GetProtocol()
+ || INetProtocol::Sftp == aTargetUrl.GetProtocol()
+ || INetProtocol::Cmis == aTargetUrl.GetProtocol())))
+ {
+ return;
+ }
+
+ if( pFileNameMap )
+ {
+ // Did we already move the file?
+ std::map<OUString, OUString>::iterator it = pFileNameMap->find( rFileNm );
+ if( it != pFileNameMap->end() )
+ {
+ rFileNm = it->second;
+ return;
+ }
+ }
+ else
+ {
+ pFileNameMap.reset( new std::map<OUString, OUString> );
+ }
+
+ bool bRet = false;
+ SvFileStream aTmp( aFileUrl.PathToFileName(), StreamMode::READ );
+
+ OUString aSrc = rFileNm;
+ OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName();
+
+ SfxMedium aMedium( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE );
+
+ {
+ SvFileStream aCpy( aMedium.GetPhysicalName(), StreamMode::WRITE );
+ aCpy.WriteStream( aTmp );
+ }
+
+ // Take over
+ aMedium.Close();
+ aMedium.Commit();
+
+ bRet = ERRCODE_NONE == aMedium.GetErrorIgnoreWarning();
+
+ if( bRet )
+ {
+ pFileNameMap->insert( std::make_pair( aSrc, aDest ) );
+ rFileNm = aDest;
+ }
+}
+
+void ScHTMLExport::IncIndent( short nVal )
+{
+ sIndent[nIndent] = '\t';
+ nIndent = nIndent + nVal;
+ if ( nIndent < 0 )
+ nIndent = 0;
+ else if ( nIndent > nIndentMax )
+ nIndent = nIndentMax;
+ sIndent[nIndent] = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlexp2.cxx b/sc/source/filter/html/htmlexp2.cxx
new file mode 100644
index 0000000000..59c1d27b6c
--- /dev/null
+++ b/sc/source/filter/html/htmlexp2.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/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/xoutbmp.hxx>
+#include <svx/svdxcgv.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <svl/urihelper.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+
+#include <htmlexp.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <rtl/strbuf.hxx>
+
+using namespace com::sun::star;
+
+void ScHTMLExport::PrepareGraphics( ScDrawLayer* pDrawLayer, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ if ( !pDrawLayer->HasObjectsInRows( nTab, nStartRow, nEndRow ) )
+ return;
+
+ SdrPage* pDrawPage = pDrawLayer->GetPage( static_cast<sal_uInt16>(nTab) );
+ if ( !pDrawPage )
+ return;
+
+ bTabHasGraphics = true;
+ FillGraphList( pDrawPage, nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ size_t ListSize = aGraphList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScHTMLGraphEntry* pE = &aGraphList[ i ];
+ if ( !pE->bInCell )
+ { // not all cells: table next to some
+ bTabAlignedLeft = true;
+ break;
+ }
+ }
+}
+
+void ScHTMLExport::FillGraphList( const SdrPage* pPage, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ if ( !pPage->GetObjCount() )
+ return;
+
+ tools::Rectangle aRect;
+ if ( !bAll )
+ aRect = pDoc->GetMMRect( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while ( pObject )
+ {
+ tools::Rectangle aObjRect = pObject->GetCurrentBoundRect();
+ if ( (bAll || aRect.Contains( aObjRect )) && !ScDrawLayer::IsNoteCaption(pObject) )
+ {
+ Size aSpace;
+ ScRange aR = pDoc->GetRange( nTab, aObjRect );
+ // Rectangle in mm/100
+ Size aSize( MMToPixel( aObjRect.GetSize() ) );
+ // If the image is somewhere in a merged range we must
+ // move the anchor to the upper left (THE span cell).
+ pDoc->ExtendOverlapped( aR );
+ SCCOL nCol1 = aR.aStart.Col();
+ SCROW nRow1 = aR.aStart.Row();
+ SCCOL nCol2 = aR.aEnd.Col();
+ SCROW nRow2 = aR.aEnd.Row();
+ // All cells empty under object?
+ bool bInCell = pDoc->IsEmptyData( nCol1, nRow1, nCol2, nRow2, nTab );
+ if ( bInCell )
+ { // Spacing in spanning cell
+ tools::Rectangle aCellRect = pDoc->GetMMRect(
+ nCol1, nRow1, nCol2, nRow2, nTab );
+ aSpace = MMToPixel( Size(
+ aCellRect.GetWidth() - aObjRect.GetWidth(),
+ aCellRect.GetHeight() - aObjRect.GetHeight() ));
+ aSpace.AdjustWidth((nCol2-nCol1) * (nCellSpacing+1) );
+ aSpace.AdjustHeight((nRow2-nRow1) * (nCellSpacing+1) );
+ aSpace.setWidth( aSpace.Width() / 2 );
+ aSpace.setHeight( aSpace.Height() / 2 );
+ }
+ aGraphList.emplace_back( pObject,
+ aR, aSize, bInCell, aSpace );
+ }
+ pObject = aIter.Next();
+ }
+}
+
+void ScHTMLExport::WriteGraphEntry( ScHTMLGraphEntry* pE )
+{
+ SdrObject* pObject = pE->pObject;
+ OStringBuffer aBuf =
+ " " OOO_STRING_SVTOOLS_HTML_O_width "="
+ + OString::number(static_cast<sal_Int32>(pE->aSize.Width()))
+ + " " OOO_STRING_SVTOOLS_HTML_O_height "="
+ + OString::number(static_cast<sal_Int32>(pE->aSize.Height()));
+ if ( pE->bInCell )
+ {
+ aBuf.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace "="
+ + OString::number(static_cast<sal_Int32>(pE->aSpace.Width()))
+ + " " OOO_STRING_SVTOOLS_HTML_O_vspace "="
+ + OString::number(static_cast<sal_Int32>(pE->aSpace.Height())));
+ }
+ OString aOpt = aBuf.makeStringAndClear();
+ switch ( pObject->GetObjIdentifier() )
+ {
+ case SdrObjKind::Graphic:
+ {
+ const SdrGrafObj* pSGO = static_cast<SdrGrafObj*>(pObject);
+ std::unique_ptr<SdrGrafObjGeoData> pGeo(static_cast<SdrGrafObjGeoData*>(pSGO->GetGeoData().release()));
+ sal_uInt16 nMirrorCase = (pGeo->maGeo.m_nRotationAngle == 18000_deg100 ?
+ ( pGeo->bMirrored ? 3 : 4 ) : ( pGeo->bMirrored ? 2 : 1 ));
+ bool bHMirr = ( ( nMirrorCase == 2 ) || ( nMirrorCase == 4 ) );
+ bool bVMirr = ( ( nMirrorCase == 3 ) || ( nMirrorCase == 4 ) );
+ XOutFlags nXOutFlags = XOutFlags::NONE;
+ if ( bHMirr )
+ nXOutFlags |= XOutFlags::MirrorHorz;
+ if ( bVMirr )
+ nXOutFlags |= XOutFlags::MirrorVert;
+ OUString aLinkName;
+ if ( pSGO->IsLinkedGraphic() )
+ aLinkName = pSGO->GetFileName();
+ WriteImage( aLinkName, pSGO->GetGraphic(), aOpt, nXOutFlags );
+ pE->bWritten = true;
+ }
+ break;
+ case SdrObjKind::OLE2:
+ {
+ const Graphic* pGraphic = static_cast<SdrOle2Obj*>(pObject)->GetGraphic();
+ if ( pGraphic )
+ {
+ OUString aLinkName;
+ WriteImage( aLinkName, *pGraphic, aOpt );
+ pE->bWritten = true;
+ }
+ }
+ break;
+ default:
+ {
+ Graphic aGraph(SdrExchangeView::GetObjGraphic(*pObject));
+ OUString aLinkName;
+ WriteImage( aLinkName, aGraph, aOpt );
+ pE->bWritten = true;
+ }
+ }
+}
+
+void ScHTMLExport::WriteImage( OUString& rLinkName, const Graphic& rGrf,
+ std::string_view rImgOptions, XOutFlags nXOutFlags )
+{
+ // Embedded graphic -> create an image file
+ if( rLinkName.isEmpty() )
+ {
+ if( !aStreamPath.isEmpty() )
+ {
+ // Save as a PNG
+ OUString aGrfNm( aStreamPath );
+ nXOutFlags |= XOutFlags::UseNativeIfPossible;
+ ErrCode nErr = XOutBitmap::WriteGraphic( rGrf, aGrfNm,
+ "PNG", nXOutFlags );
+
+ // If it worked, create a URL for the IMG tag
+ if( !nErr )
+ {
+ rLinkName = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ aGrfNm,
+ URIHelper::GetMaybeFileHdl());
+ }
+ }
+ }
+ else
+ {
+ // Linked graphic - figure out the URL for the IMG tag
+ if( bCopyLocalFileToINet )
+ {
+ CopyLocalFileToINet( rLinkName, aStreamPath );
+ }
+ else
+ rLinkName = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ rLinkName,
+ URIHelper::GetMaybeFileHdl());
+ }
+
+ // If a URL was set, output the IMG tag.
+ // <IMG SRC="..."[ rImgOptions]>
+ if( !rLinkName.isEmpty() )
+ {
+ rStrm.WriteChar( '<' ).WriteOString( OOO_STRING_SVTOOLS_HTML_image ).WriteChar( ' ' ).WriteOString( OOO_STRING_SVTOOLS_HTML_O_src ).WriteOString( "=\"" );
+ HTMLOutFuncs::Out_String( rStrm, URIHelper::simpleNormalizedMakeRelative(
+ aBaseURL,
+ rLinkName ) ).WriteChar( '\"' );
+ if ( !rImgOptions.empty() )
+ rStrm.WriteOString( rImgOptions );
+ rStrm.WriteChar( '>' ).WriteOString( SAL_NEWLINE_STRING ).WriteOString( GetIndentStr() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlimp.cxx b/sc/source/filter/html/htmlimp.cxx
new file mode 100644
index 0000000000..3168a02fc4
--- /dev/null
+++ b/sc/source/filter/html/htmlimp.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 <scitems.hxx>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+
+#include <editeng/lrspitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <htmlimp.hxx>
+#include <htmlpars.hxx>
+#include <filter.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <refdata.hxx>
+#include <rangenam.hxx>
+#include <attrib.hxx>
+#include <ftools.hxx>
+#include <tokenarray.hxx>
+
+ErrCode ScFormatFilterPluginImpl::ScImportHTML( SvStream &rStream, const OUString& rBaseURL, ScDocument *pDoc,
+ ScRange& rRange, double nOutputFactor, bool bCalcWidthHeight, SvNumberFormatter* pFormatter,
+ bool bConvertDate, bool bConvertScientific )
+{
+ ScHTMLImport aImp( pDoc, rBaseURL, rRange, bCalcWidthHeight );
+ ErrCode nErr = aImp.Read( rStream, rBaseURL );
+ ScRange aR = aImp.GetRange();
+ rRange.aEnd = aR.aEnd;
+ aImp.WriteToDocument( true, nOutputFactor, pFormatter, bConvertDate, bConvertScientific );
+ return nErr;
+}
+
+std::unique_ptr<ScEEAbsImport> ScFormatFilterPluginImpl::CreateHTMLImport( ScDocument* pDocP, const OUString& rBaseURL, const ScRange& rRange )
+{
+ return std::make_unique<ScHTMLImport>( pDocP, rBaseURL, rRange, true/*bCalcWidthHeight*/ );
+}
+
+ScHTMLImport::ScHTMLImport( ScDocument* pDocP, const OUString& rBaseURL, const ScRange& rRange, bool bCalcWidthHeight ) :
+ ScEEImport( pDocP, rRange )
+{
+ Size aPageSize;
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ const OUString& aPageStyle = mpDoc->GetPageStyle( rRange.aStart.Tab() );
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(mpDoc->
+ GetStyleSheetPool()->Find( aPageStyle, SfxStyleFamily::Page ));
+ if ( pStyleSheet )
+ {
+ const SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ const SvxLRSpaceItem* pLRItem = &rSet.Get( ATTR_LRSPACE );
+ tools::Long nLeftMargin = pLRItem->GetLeft();
+ tools::Long nRightMargin = pLRItem->GetRight();
+ const SvxULSpaceItem* pULItem = &rSet.Get( ATTR_ULSPACE );
+ tools::Long nTopMargin = pULItem->GetUpper();
+ tools::Long nBottomMargin = pULItem->GetLower();
+ aPageSize = rSet.Get(ATTR_PAGE_SIZE).GetSize();
+ if ( !aPageSize.Width() || !aPageSize.Height() )
+ {
+ OSL_FAIL("PageSize Null ?!?!?");
+ aPageSize = SvxPaperInfo::GetPaperSize( PAPER_A4 );
+ }
+ aPageSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
+ aPageSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
+ aPageSize = pDefaultDev->LogicToPixel( aPageSize, MapMode( MapUnit::MapTwip ) );
+ }
+ else
+ {
+ OSL_FAIL("no StyleSheet?!?");
+ aPageSize = pDefaultDev->LogicToPixel(
+ SvxPaperInfo::GetPaperSize( PAPER_A4 ), MapMode( MapUnit::MapTwip ) );
+ }
+ if( bCalcWidthHeight )
+ mpParser.reset( new ScHTMLLayoutParser( mpEngine.get(), rBaseURL, aPageSize, pDocP ));
+ else
+ mpParser.reset( new ScHTMLQueryParser( mpEngine.get(), pDocP ));
+}
+
+void ScHTMLImport::InsertRangeName( ScDocument& rDoc, const OUString& rName, const ScRange& rRange )
+{
+ ScComplexRefData aRefData;
+ aRefData.InitRange( rRange );
+ aRefData.Ref1.SetFlag3D( true );
+ aRefData.Ref2.SetFlag3D( aRefData.Ref2.Tab() != aRefData.Ref1.Tab() );
+ ScTokenArray aTokArray(rDoc);
+ aTokArray.AddDoubleReference( aRefData );
+ ScRangeData* pRangeData = new ScRangeData( rDoc, rName, aTokArray );
+ rDoc.GetRangeName()->insert( pRangeData );
+}
+
+void ScHTMLImport::WriteToDocument(
+ bool bSizeColsRows, double nOutputFactor, SvNumberFormatter* pFormatter, bool bConvertDate,
+ bool bConvertScientific )
+{
+ ScEEImport::WriteToDocument( bSizeColsRows, nOutputFactor, pFormatter, bConvertDate, bConvertScientific );
+
+ const ScHTMLParser* pParser = static_cast<ScHTMLParser*>(mpParser.get());
+ const ScHTMLTable* pGlobTable = pParser->GetGlobalTable();
+ if( !pGlobTable )
+ return;
+
+ // set cell borders for HTML table cells
+ pGlobTable->ApplyCellBorders( mpDoc, maRange.aStart );
+
+ // correct cell borders for merged cells
+ for ( size_t i = 0, n = pParser->ListSize(); i < n; ++i )
+ {
+ const ScEEParseEntry* pEntry = pParser->ListEntry( i );
+ if( (pEntry->nColOverlap > 1) || (pEntry->nRowOverlap > 1) )
+ {
+ SCTAB nTab = maRange.aStart.Tab();
+ const ScMergeAttr* pItem = mpDoc->GetAttr( pEntry->nCol, pEntry->nRow, nTab, ATTR_MERGE );
+ if( pItem->IsMerged() )
+ {
+ SCCOL nColMerge = pItem->GetColMerge();
+ SCROW nRowMerge = pItem->GetRowMerge();
+
+ const SvxBoxItem* pToItem = mpDoc->GetAttr( pEntry->nCol, pEntry->nRow, nTab, ATTR_BORDER );
+ SvxBoxItem aNewItem( *pToItem );
+ if( nColMerge > 1 )
+ {
+ const SvxBoxItem* pFromItem =
+ mpDoc->GetAttr( pEntry->nCol + nColMerge - 1, pEntry->nRow, nTab, ATTR_BORDER );
+ aNewItem.SetLine( pFromItem->GetLine( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::RIGHT );
+ }
+ if( nRowMerge > 1 )
+ {
+ const SvxBoxItem* pFromItem =
+ mpDoc->GetAttr( pEntry->nCol, pEntry->nRow + nRowMerge - 1, nTab, ATTR_BORDER );
+ aNewItem.SetLine( pFromItem->GetLine( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::BOTTOM );
+ }
+ mpDoc->ApplyAttr( pEntry->nCol, pEntry->nRow, nTab, aNewItem );
+ }
+ }
+ }
+
+ // create ranges for HTML tables
+ // 1 - entire document
+ ScRange aNewRange( maRange.aStart );
+ aNewRange.aEnd.IncCol( static_cast<SCCOL>(pGlobTable->GetDocSize( tdCol )) - 1 );
+ aNewRange.aEnd.IncRow( pGlobTable->GetDocSize( tdRow ) - 1 );
+ InsertRangeName( *mpDoc, ScfTools::GetHTMLDocName(), aNewRange );
+
+ // 2 - all tables
+ InsertRangeName( *mpDoc, ScfTools::GetHTMLTablesName(), ScRange( maRange.aStart ) );
+
+ // 3 - single tables
+ SCCOL nColDiff = maRange.aStart.Col();
+ SCROW nRowDiff = maRange.aStart.Row();
+ SCTAB nTabDiff = maRange.aStart.Tab();
+
+ ScHTMLTable* pTable = nullptr;
+ ScHTMLTableId nTableId = SC_HTML_GLOBAL_TABLE;
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ while( (pTable = pGlobTable->FindNestedTable( ++nTableId )) != nullptr )
+ {
+ pTable->GetDocRange( aNewRange );
+ if (!aNewRange.Move( nColDiff, nRowDiff, nTabDiff, aErrorRange, *mpDoc ))
+ {
+ assert(!"can't move");
+ }
+ // insert table number as name
+ OUStringBuffer aName(ScfTools::GetNameFromHTMLIndex(nTableId));
+ // insert table id as name
+ if (!pTable->GetTableName().isEmpty())
+ aName.append(" - " + pTable->GetTableName());
+ // insert table caption as name
+ if (!pTable->GetTableCaption().isEmpty())
+ aName.append(" - " + pTable->GetTableCaption());
+ const OUString sName(aName.makeStringAndClear());
+ if (!mpDoc->GetRangeName()->findByUpperName(ScGlobal::getCharClass().uppercase(sName)))
+ InsertRangeName(*mpDoc, sName, aNewRange);
+ }
+}
+
+OUString ScFormatFilterPluginImpl::GetHTMLRangeNameList( ScDocument& rDoc, const OUString& rOrigName )
+{
+ return ScHTMLImport::GetHTMLRangeNameList( rDoc, rOrigName );
+}
+
+OUString ScHTMLImport::GetHTMLRangeNameList( const ScDocument& rDoc, std::u16string_view rOrigName )
+{
+ if (rOrigName.empty())
+ return OUString();
+
+ OUString aNewName;
+ ScRangeName* pRangeNames = rDoc.GetRangeName();
+ ScRangeList aRangeList;
+ sal_Int32 nStringIx = 0;
+ do
+ {
+ OUString aToken( o3tl::getToken(rOrigName, 0, ';', nStringIx ) );
+ if( pRangeNames && ScfTools::IsHTMLTablesName( aToken ) )
+ { // build list with all HTML tables
+ sal_uLong nIndex = 1;
+ for(;;)
+ {
+ aToken = ScfTools::GetNameFromHTMLIndex( nIndex++ );
+ const ScRangeData* pRangeData = pRangeNames->findByUpperName(ScGlobal::getCharClass().uppercase(aToken));
+ if (!pRangeData)
+ break;
+ ScRange aRange;
+ if( pRangeData->IsReference( aRange ) && !aRangeList.Contains( aRange ) )
+ {
+ aNewName = ScGlobal::addToken(aNewName, aToken, ';');
+ aRangeList.push_back( aRange );
+ }
+ }
+ }
+ else
+ aNewName = ScGlobal::addToken(aNewName, aToken, ';');
+ }
+ while (nStringIx>0);
+ return aNewName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlpars.cxx b/sc/source/filter/html/htmlpars.cxx
new file mode 100644
index 0000000000..f5f8900815
--- /dev/null
+++ b/sc/source/filter/html/htmlpars.cxx
@@ -0,0 +1,3146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <comphelper/string.hxx>
+
+#include <scitems.hxx>
+
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <sal/log.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/numformat.hxx>
+#include <svl/intitem.hxx>
+#include <utility>
+#include <vcl/graphicfilter.hxx>
+#include <svtools/parhtml.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmltokn.h>
+
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <rtl/tencinfo.h>
+
+#include <attrib.hxx>
+#include <htmlpars.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <rangelst.hxx>
+
+#include <orcus/css_parser.hpp>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <numeric>
+#include <officecfg/Office/Common.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+ScHTMLStyles::ScHTMLStyles() : maEmpty() {}
+
+void ScHTMLStyles::add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
+ const OUString& aProp, const OUString& aValue)
+{
+ if (nElemName)
+ {
+ OUString aElem(pElemName, nElemName, RTL_TEXTENCODING_UTF8);
+ aElem = aElem.toAsciiLowerCase();
+ if (nClassName)
+ {
+ // Both element and class names given.
+ ElemsType::iterator itrElem = m_ElemProps.find(aElem);
+ if (itrElem == m_ElemProps.end())
+ {
+ // new element
+ std::pair<ElemsType::iterator, bool> r =
+ m_ElemProps.insert(std::make_pair(aElem, NamePropsType()));
+ if (!r.second)
+ // insertion failed.
+ return;
+ itrElem = r.first;
+ }
+
+ NamePropsType& rClsProps = itrElem->second;
+ OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
+ aClass = aClass.toAsciiLowerCase();
+ insertProp(rClsProps, aClass, aProp, aValue);
+ }
+ else
+ {
+ // Element name only. Add it to the element global.
+ insertProp(m_ElemGlobalProps, aElem, aProp, aValue);
+ }
+ }
+ else
+ {
+ if (nClassName)
+ {
+ // Class name only. Add it to the global.
+ OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
+ aClass = aClass.toAsciiLowerCase();
+ insertProp(m_GlobalProps, aClass, aProp, aValue);
+ }
+ }
+}
+
+const OUString& ScHTMLStyles::getPropertyValue(
+ const OUString& rElem, const OUString& rClass, const OUString& rPropName) const
+{
+ // First, look into the element-class storage.
+ {
+ auto const itr = m_ElemProps.find(rElem);
+ if (itr != m_ElemProps.end())
+ {
+ const NamePropsType& rClasses = itr->second;
+ NamePropsType::const_iterator itr2 = rClasses.find(rClass);
+ if (itr2 != rClasses.end())
+ {
+ const PropsType& rProps = itr2->second;
+ PropsType::const_iterator itr3 = rProps.find(rPropName);
+ if (itr3 != rProps.end())
+ return itr3->second;
+ }
+ }
+ }
+ // Next, look into the class global storage.
+ {
+ auto const itr = m_GlobalProps.find(rClass);
+ if (itr != m_GlobalProps.end())
+ {
+ const PropsType& rProps = itr->second;
+ PropsType::const_iterator itr2 = rProps.find(rPropName);
+ if (itr2 != rProps.end())
+ return itr2->second;
+ }
+ }
+ // As the last resort, look into the element global storage.
+ {
+ auto const itr = m_ElemGlobalProps.find(rClass);
+ if (itr != m_ElemGlobalProps.end())
+ {
+ const PropsType& rProps = itr->second;
+ PropsType::const_iterator itr2 = rProps.find(rPropName);
+ if (itr2 != rProps.end())
+ return itr2->second;
+ }
+ }
+
+ return maEmpty; // nothing found.
+}
+
+void ScHTMLStyles::insertProp(
+ NamePropsType& rStore, const OUString& aName,
+ const OUString& aProp, const OUString& aValue)
+{
+ NamePropsType::iterator itr = rStore.find(aName);
+ if (itr == rStore.end())
+ {
+ // new element
+ std::pair<NamePropsType::iterator, bool> r =
+ rStore.insert(std::make_pair(aName, PropsType()));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+
+ PropsType& rProps = itr->second;
+ rProps.emplace(aProp, aValue);
+}
+
+// BASE class for HTML parser classes
+
+ScHTMLParser::ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
+ ScEEParser( pEditEngine ),
+ mpDoc( pDoc )
+{
+ maFontHeights[0] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_1::get() * 20;
+ maFontHeights[1] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_2::get() * 20;
+ maFontHeights[2] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_3::get() * 20;
+ maFontHeights[3] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_4::get() * 20;
+ maFontHeights[4] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_5::get() * 20;
+ maFontHeights[5] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_6::get() * 20;
+ maFontHeights[6] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_7::get() * 20;
+}
+
+ScHTMLParser::~ScHTMLParser()
+{
+}
+
+ScHTMLLayoutParser::ScHTMLLayoutParser(
+ EditEngine* pEditP, OUString _aBaseURL, const Size& aPageSizeP,
+ ScDocument* pDocP ) :
+ ScHTMLParser( pEditP, pDocP ),
+ aPageSize( aPageSizeP ),
+ aBaseURL(std::move( _aBaseURL )),
+ xLockedList( new ScRangeList ),
+ pLocalColOffset( new ScHTMLColOffset ),
+ nFirstTableCell(0),
+ nTableLevel(0),
+ nTable(0),
+ nMaxTable(0),
+ nColCntStart(0),
+ nMaxCol(0),
+ nTableWidth(0),
+ nColOffset(0),
+ nColOffsetStart(0),
+ nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
+ bFirstRow( true ),
+ bTabInTabCell( false ),
+ bInCell( false ),
+ bInTitle( false )
+{
+ MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
+ MakeColNoRef( &maColOffset, 0, 0, 0, 0 );
+}
+
+ScHTMLLayoutParser::~ScHTMLLayoutParser()
+{
+ while ( !aTableStack.empty() )
+ {
+ ScHTMLTableStackEntry * pS = aTableStack.top().get();
+ if ( pS->pLocalColOffset != pLocalColOffset )
+ delete pS->pLocalColOffset;
+ aTableStack.pop();
+ }
+ delete pLocalColOffset;
+ if ( pTables )
+ {
+ for( const auto& rEntry : *pTables)
+ delete rEntry.second;
+ pTables.reset();
+ }
+}
+
+ErrCode ScHTMLLayoutParser::Read( SvStream& rStream, const OUString& rBaseURL )
+{
+ Link<HtmlImportInfo&,void> aOldLink = pEdit->GetHtmlImportHdl();
+ pEdit->SetHtmlImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
+
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ bool bLoading = pObjSh && pObjSh->IsLoading();
+
+ SvKeyValueIteratorRef xValues;
+ SvKeyValueIterator* pAttributes = nullptr;
+ if ( bLoading )
+ pAttributes = pObjSh->GetHeaderAttributes();
+ else
+ {
+ // When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
+ // (used when pasting from clipboard)
+ const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
+ if( pCharSet )
+ {
+ OUString aContentType = "text/html; charset=" +
+ OUString::createFromAscii( pCharSet );
+
+ xValues = new SvKeyValueIterator;
+ xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
+ pAttributes = xValues.get();
+ }
+ }
+
+ ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Html, pAttributes );
+
+ pEdit->SetHtmlImportHdl( aOldLink );
+ // Create column width
+ Adjust();
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ sal_uInt16 nCount = maColOffset.size();
+ sal_uLong nOff = maColOffset[0];
+ Size aSize;
+ for ( sal_uInt16 j = 1; j < nCount; j++ )
+ {
+ aSize.setWidth( maColOffset[j] - nOff );
+ aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MapUnit::MapTwip ) );
+ maColWidths[ j-1 ] = aSize.Width();
+ nOff = maColOffset[j];
+ }
+ return nErr;
+}
+
+const ScHTMLTable* ScHTMLLayoutParser::GetGlobalTable() const
+{
+ return nullptr;
+}
+
+void ScHTMLLayoutParser::NewActEntry( const ScEEParseEntry* pE )
+{
+ ScEEParser::NewActEntry( pE );
+ if ( pE )
+ {
+ if ( !pE->aSel.HasRange() )
+ { // Completely empty, following text ends up in the same paragraph!
+ mxActEntry->aSel.nStartPara = pE->aSel.nEndPara;
+ mxActEntry->aSel.nStartPos = pE->aSel.nEndPos;
+ }
+ }
+ mxActEntry->aSel.nEndPara = mxActEntry->aSel.nStartPara;
+ mxActEntry->aSel.nEndPos = mxActEntry->aSel.nStartPos;
+}
+
+void ScHTMLLayoutParser::EntryEnd( ScEEParseEntry* pE, const ESelection& rSel )
+{
+ if ( rSel.nEndPara >= pE->aSel.nStartPara )
+ {
+ pE->aSel.nEndPara = rSel.nEndPara;
+ pE->aSel.nEndPos = rSel.nEndPos;
+ }
+ else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
+ { // Did not attach a paragraph, but empty, do nothing
+ }
+ else
+ {
+ OSL_FAIL( "EntryEnd: EditEngine ESelection End < Start" );
+ }
+}
+
+void ScHTMLLayoutParser::NextRow( const HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo );
+ if ( nRowMax < ++nRowCnt )
+ nRowMax = nRowCnt;
+ nColCnt = nColCntStart;
+ nColOffset = nColOffsetStart;
+ bFirstRow = false;
+}
+
+bool ScHTMLLayoutParser::SeekOffset( const ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
+ SCCOL* pCol, sal_uInt16 nOffsetTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
+ ScHTMLColOffset::const_iterator it = pOffset->find( nOffset );
+ bool bFound = it != pOffset->end();
+ sal_uInt16 nPos = it - pOffset->begin();
+ *pCol = static_cast<SCCOL>(nPos);
+ if ( bFound )
+ return true;
+ sal_uInt16 nCount = pOffset->size();
+ if ( !nCount )
+ return false;
+ // nPos is the position of insertion, that's where the next higher one is (or isn't)
+ if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
+ return true;
+ // Not smaller than everything else? Then compare with the next lower one
+ else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
+ {
+ (*pCol)--;
+ return true;
+ }
+ return false;
+}
+
+void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
+ sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
+ SCCOL nPos;
+ if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
+ nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ else
+ pOffset->insert( nOffset );
+ if ( nWidth )
+ {
+ if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
+ nWidth = static_cast<sal_uInt16>((*pOffset)[nPos]) - nOffset;
+ else
+ pOffset->insert( nOffset + nWidth );
+ }
+}
+
+void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
+ sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
+ SCCOL nPos;
+ if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
+ nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ else
+ pOffset->insert( nOffset );
+ if ( nWidth )
+ {
+ if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
+ pOffset->insert( nOffset + nWidth );
+ }
+}
+
+void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
+ sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
+ SCCOL nPos;
+ if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
+ {
+ if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
+ nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ else
+ pOffset->insert( nNewOffset );
+ return ;
+ }
+ nOldOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ SCCOL nPos2;
+ if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
+ {
+ nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos2]);
+ return ;
+ }
+ tools::Long nDiff = nNewOffset - nOldOffset;
+ if ( nDiff < 0 )
+ {
+ do
+ {
+ const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
+ } while ( nPos-- );
+ }
+ else
+ {
+ do
+ {
+ const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
+ } while ( ++nPos < static_cast<sal_uInt16>(pOffset->size()) );
+ }
+}
+
+void ScHTMLLayoutParser::SkipLocked( ScEEParseEntry* pE, bool bJoin )
+{
+ if ( !mpDoc->ValidCol(pE->nCol) )
+ return;
+
+// Or else this would create a wrong value at ScAddress (chance for an infinite loop)!
+ bool bBadCol = false;
+ bool bAgain;
+ ScRange aRange( pE->nCol, pE->nRow, 0,
+ pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
+ do
+ {
+ bAgain = false;
+ for ( size_t i = 0, nRanges = xLockedList->size(); i < nRanges; ++i )
+ {
+ ScRange & rR = (*xLockedList)[i];
+ if ( rR.Intersects( aRange ) )
+ {
+ pE->nCol = rR.aEnd.Col() + 1;
+ SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
+ if ( pE->nCol > mpDoc->MaxCol() || nTmp > mpDoc->MaxCol() )
+ bBadCol = true;
+ else
+ {
+ bAgain = true;
+ aRange.aStart.SetCol( pE->nCol );
+ aRange.aEnd.SetCol( nTmp );
+ }
+ break;
+ }
+ }
+ } while ( bAgain );
+ if ( bJoin && !bBadCol )
+ xLockedList->Join( aRange );
+}
+
+void ScHTMLLayoutParser::Adjust()
+{
+ xLockedList->RemoveAll();
+
+ std::stack< std::unique_ptr<ScHTMLAdjustStackEntry> > aStack;
+ sal_uInt16 nTab = 0;
+ SCCOL nLastCol = SCCOL_MAX;
+ SCROW nNextRow = 0;
+ SCROW nCurRow = 0;
+ sal_uInt16 nPageWidth = static_cast<sal_uInt16>(aPageSize.Width());
+ InnerMap* pTab = nullptr;
+ for (auto& pE : maList)
+ {
+ if ( pE->nTab < nTab )
+ { // Table finished
+ if ( !aStack.empty() )
+ {
+ std::unique_ptr<ScHTMLAdjustStackEntry> pS = std::move(aStack.top());
+ aStack.pop();
+
+ nLastCol = pS->nLastCol;
+ nNextRow = pS->nNextRow;
+ nCurRow = pS->nCurRow;
+ }
+ nTab = pE->nTab;
+ if (pTables)
+ {
+ OuterMap::const_iterator it = pTables->find( nTab );
+ if ( it != pTables->end() )
+ pTab = it->second;
+ }
+
+ }
+ SCROW nRow = pE->nRow;
+ if ( pE->nCol <= nLastCol )
+ { // Next row
+ if ( pE->nRow < nNextRow )
+ pE->nRow = nCurRow = nNextRow;
+ else
+ nCurRow = nNextRow = pE->nRow;
+ SCROW nR = 0;
+ if ( pTab )
+ {
+ InnerMap::const_iterator it = pTab->find( nCurRow );
+ if ( it != pTab->end() )
+ nR = it->second;
+ }
+ if ( nR )
+ nNextRow += nR;
+ else
+ nNextRow++;
+ }
+ else
+ pE->nRow = nCurRow;
+ nLastCol = pE->nCol; // Read column
+ if ( pE->nTab > nTab )
+ { // New table
+ aStack.push( std::make_unique<ScHTMLAdjustStackEntry>(
+ nLastCol, nNextRow, nCurRow ) );
+ nTab = pE->nTab;
+ if ( pTables )
+ {
+ OuterMap::const_iterator it = pTables->find( nTab );
+ if ( it != pTables->end() )
+ pTab = it->second;
+ }
+ // New line spacing
+ SCROW nR = 0;
+ if ( pTab )
+ {
+ InnerMap::const_iterator it = pTab->find( nCurRow );
+ if ( it != pTab->end() )
+ nR = it->second;
+ }
+ if ( nR )
+ nNextRow = nCurRow + nR;
+ else
+ nNextRow = nCurRow + 1;
+ }
+ if ( nTab == 0 )
+ pE->nWidth = nPageWidth;
+ else
+ { // Real table, no paragraphs on the field
+ if ( pTab )
+ {
+ SCROW nRowSpan = pE->nRowOverlap;
+ for ( SCROW j=0; j < nRowSpan; j++ )
+ { // RowSpan resulting from merged rows
+ SCROW nRows = 0;
+ InnerMap::const_iterator it = pTab->find( nRow+j );
+ if ( it != pTab->end() )
+ nRows = it->second;
+ if ( nRows > 1 )
+ {
+ pE->nRowOverlap += nRows - 1;
+ if ( j == 0 )
+ { // Merged rows move the next row
+ SCROW nTmp = nCurRow + nRows;
+ if ( nNextRow < nTmp )
+ nNextRow = nTmp;
+ }
+ }
+ }
+ }
+ }
+ // Real column
+ (void)SeekOffset( &maColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
+ SCCOL nColBeforeSkip = pE->nCol;
+ SkipLocked(pE.get(), false);
+ if ( pE->nCol != nColBeforeSkip )
+ {
+ SCCOL nCount = static_cast<SCCOL>(maColOffset.size());
+ if ( nCount <= pE->nCol )
+ {
+ pE->nOffset = static_cast<sal_uInt16>(maColOffset[nCount-1]);
+ MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
+ }
+ else
+ {
+ pE->nOffset = static_cast<sal_uInt16>(maColOffset[pE->nCol]);
+ }
+ }
+ SCCOL nPos;
+ if ( pE->nWidth && SeekOffset( &maColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
+ pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
+ else
+ {
+ //FIXME: This may not be correct, but works anyway ...
+ pE->nColOverlap = 1;
+ }
+ xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
+ pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
+ // Take over MaxDimensions
+ SCCOL nColTmp = pE->nCol + pE->nColOverlap;
+ if ( nColMax < nColTmp )
+ nColMax = nColTmp;
+ SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
+ if ( nRowMax < nRowTmp )
+ nRowMax = nRowTmp;
+ }
+}
+
+sal_uInt16 ScHTMLLayoutParser::GetWidth( const ScEEParseEntry* pE )
+{
+ if ( pE->nWidth )
+ return pE->nWidth;
+ sal_Int32 nTmp = std::min( static_cast<sal_Int32>( pE->nCol -
+ nColCntStart + pE->nColOverlap),
+ static_cast<sal_Int32>( pLocalColOffset->size() - 1));
+ SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
+ sal_uInt16 nOff2 = static_cast<sal_uInt16>((*pLocalColOffset)[nPos]);
+ if ( pE->nOffset < nOff2 )
+ return nOff2 - pE->nOffset;
+ return 0;
+}
+
+void ScHTMLLayoutParser::SetWidths()
+{
+ SCCOL nCol;
+ if ( !nTableWidth )
+ nTableWidth = static_cast<sal_uInt16>(aPageSize.Width());
+ SCCOL nColsPerRow = nMaxCol - nColCntStart;
+ if ( nColsPerRow <= 0 )
+ nColsPerRow = 1;
+ if ( pLocalColOffset->size() <= 2 )
+ { // Only PageSize, there was no width setting
+ sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
+ sal_uInt16 nOff = nColOffsetStart;
+ pLocalColOffset->clear();
+ for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
+ {
+ MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
+ }
+ nTableWidth = static_cast<sal_uInt16>(pLocalColOffset->back() - pLocalColOffset->front());
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable )
+ {
+ pE->nOffset = static_cast<sal_uInt16>((*pLocalColOffset)[pE->nCol - nColCntStart]);
+ pE->nWidth = 0; // to be recalculated later
+ }
+ }
+ }
+ else
+ { // Some without width
+ // Why actually no pE?
+ if ( nFirstTableCell < maList.size() )
+ {
+ std::unique_ptr<sal_uInt16[]> pOffsets(new sal_uInt16[ nColsPerRow+1 ]);
+ memset( pOffsets.get(), 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
+ std::unique_ptr<sal_uInt16[]> pWidths(new sal_uInt16[ nColsPerRow ]);
+ memset( pWidths.get(), 0, nColsPerRow * sizeof(sal_uInt16) );
+ pOffsets[0] = nColOffsetStart;
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable && pE->nWidth )
+ {
+ nCol = pE->nCol - nColCntStart;
+ if ( nCol < nColsPerRow )
+ {
+ if ( pE->nColOverlap == 1 )
+ {
+ if ( pWidths[nCol] < pE->nWidth )
+ pWidths[nCol] = pE->nWidth;
+ }
+ else
+ { // try to find a single undefined width
+ sal_uInt16 nTotal = 0;
+ bool bFound = false;
+ SCCOL nHere = 0;
+ SCCOL nStop = std::min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
+ for ( ; nCol < nStop; nCol++ )
+ {
+ if ( pWidths[nCol] )
+ nTotal = nTotal + pWidths[nCol];
+ else
+ {
+ if ( bFound )
+ {
+ bFound = false;
+ break; // for
+ }
+ bFound = true;
+ nHere = nCol;
+ }
+ }
+ if ( bFound && pE->nWidth > nTotal )
+ pWidths[nHere] = pE->nWidth - nTotal;
+ }
+ }
+ }
+ }
+ sal_uInt16 nWidths = 0;
+ sal_uInt16 nUnknown = 0;
+ for ( nCol = 0; nCol < nColsPerRow; nCol++ )
+ {
+ if ( pWidths[nCol] )
+ nWidths = nWidths + pWidths[nCol];
+ else
+ nUnknown++;
+ }
+ if ( nUnknown )
+ {
+ sal_uInt16 nW = ((nWidths < nTableWidth) ?
+ ((nTableWidth - nWidths) / nUnknown) :
+ (nTableWidth / nUnknown));
+ for ( nCol = 0; nCol < nColsPerRow; nCol++ )
+ {
+ if ( !pWidths[nCol] )
+ pWidths[nCol] = nW;
+ }
+ }
+ for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
+ {
+ pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
+ }
+ pLocalColOffset->clear();
+ for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
+ {
+ MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
+ }
+ nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
+
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable )
+ {
+ nCol = pE->nCol - nColCntStart;
+ OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
+ if ( nCol < nColsPerRow )
+ {
+ pE->nOffset = pOffsets[nCol];
+ nCol = nCol + pE->nColOverlap;
+ if ( nCol > nColsPerRow )
+ nCol = nColsPerRow;
+ pE->nWidth = pOffsets[nCol] - pE->nOffset;
+ }
+ }
+ }
+ }
+ }
+ if ( !pLocalColOffset->empty() )
+ {
+ sal_uInt16 nMax = static_cast<sal_uInt16>(pLocalColOffset->back());
+ if ( aPageSize.Width() < nMax )
+ aPageSize.setWidth( nMax );
+ if (nTableLevel == 0)
+ {
+ // Local table is very outer table, create missing offsets.
+ for (auto it = pLocalColOffset->begin(); it != pLocalColOffset->end(); ++it)
+ {
+ // Only exact offsets, do not use MakeColNoRef().
+ maColOffset.insert(*it);
+ }
+ }
+ }
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable )
+ {
+ if ( !pE->nWidth )
+ {
+ pE->nWidth = GetWidth(pE.get());
+ OSL_ENSURE( pE->nWidth, "SetWidths: pE->nWidth == 0" );
+ }
+ MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
+ }
+ }
+}
+
+void ScHTMLLayoutParser::Colonize( ScEEParseEntry* pE )
+{
+ if ( pE->nCol == SCCOL_MAX )
+ pE->nCol = nColCnt;
+ if ( pE->nRow == SCROW_MAX )
+ pE->nRow = nRowCnt;
+ SCCOL nCol = pE->nCol;
+ SkipLocked( pE ); // Change of columns to the right
+
+ if ( nCol < pE->nCol )
+ { // Replaced
+ nCol = pE->nCol - nColCntStart;
+ SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size());
+ if ( nCol < nCount )
+ nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCol]);
+ else
+ nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCount - 1]);
+ }
+ pE->nOffset = nColOffset;
+ sal_uInt16 nWidth = GetWidth( pE );
+ MakeCol( pLocalColOffset, pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance );
+ if ( pE->nWidth )
+ pE->nWidth = nWidth;
+ nColOffset = pE->nOffset + nWidth;
+ if ( nTableWidth < nColOffset - nColOffsetStart )
+ nTableWidth = nColOffset - nColOffsetStart;
+}
+
+void ScHTMLLayoutParser::CloseEntry( const HtmlImportInfo* pInfo )
+{
+ bInCell = false;
+ if ( bTabInTabCell )
+ { // From the stack in TableOff
+ bTabInTabCell = false;
+ NewActEntry(maList.back().get()); // New free flying mxActEntry
+ return ;
+ }
+ if (mxActEntry->nTab == 0)
+ mxActEntry->nWidth = static_cast<sal_uInt16>(aPageSize.Width());
+ Colonize(mxActEntry.get());
+ nColCnt = mxActEntry->nCol + mxActEntry->nColOverlap;
+ if ( nMaxCol < nColCnt )
+ nMaxCol = nColCnt; // TableStack MaxCol
+ if ( nColMax < nColCnt )
+ nColMax = nColCnt; // Global MaxCol for ScEEParser GetDimensions!
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+ ESelection& rSel = mxActEntry->aSel;
+ while ( rSel.nStartPara < rSel.nEndPara
+ && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
+ { // Strip preceding empty paragraphs
+ rSel.nStartPara++;
+ }
+ while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
+ { // Strip successive empty paragraphs
+ rSel.nEndPara--;
+ rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
+ }
+ if ( rSel.nStartPara > rSel.nEndPara )
+ { // Gives GPF in CreateTextObject
+ OSL_FAIL( "CloseEntry: EditEngine ESelection Start > End" );
+ rSel.nEndPara = rSel.nStartPara;
+ }
+ if ( rSel.HasRange() )
+ mxActEntry->aItemSet.Put( ScLineBreakCell(true) );
+ maList.push_back(mxActEntry);
+ NewActEntry(mxActEntry.get()); // New free flying mxActEntry
+}
+
+IMPL_LINK( ScHTMLLayoutParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
+{
+ switch ( rInfo.eState )
+ {
+ case HtmlImportState::NextToken:
+ ProcToken( &rInfo );
+ break;
+ case HtmlImportState::Start:
+ break;
+ case HtmlImportState::End:
+ if ( rInfo.aSelection.nEndPos )
+ {
+ // If text remains: create paragraph, without calling CloseEntry().
+ if( bInCell ) // ...but only in opened table cells.
+ {
+ bInCell = false;
+ NextRow( &rInfo );
+ bInCell = true;
+ }
+ CloseEntry( &rInfo );
+ }
+ while ( nTableLevel > 0 )
+ TableOff( &rInfo ); // close tables, if </TABLE> missing
+ break;
+ case HtmlImportState::SetAttr:
+ break;
+ case HtmlImportState::InsertText:
+ break;
+ case HtmlImportState::InsertPara:
+ if ( nTableLevel < 1 )
+ {
+ CloseEntry( &rInfo );
+ NextRow( &rInfo );
+ }
+ break;
+ case HtmlImportState::InsertField:
+ break;
+ default:
+ OSL_FAIL("HTMLImportHdl: unknown ImportInfo.eState");
+ }
+}
+
+void ScHTMLLayoutParser::TableDataOn( HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo );
+ if ( !nTableLevel )
+ {
+ OSL_FAIL( "dumbo doc! <TH> or <TD> without previous <TABLE>" );
+ TableOn( pInfo );
+ }
+ bInCell = true;
+ bool bHorJustifyCenterTH = (pInfo->nToken == HtmlTokenId::TABLEHEADER_ON);
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::COLSPAN:
+ {
+ mxActEntry->nColOverlap = static_cast<SCCOL>(rOption.GetString().toInt32());
+ }
+ break;
+ case HtmlOptionId::ROWSPAN:
+ {
+ mxActEntry->nRowOverlap = static_cast<SCROW>(rOption.GetString().toInt32());
+ }
+ break;
+ case HtmlOptionId::ALIGN:
+ {
+ bHorJustifyCenterTH = false;
+ SvxCellHorJustify eVal;
+ const OUString& rOptVal = rOption.GetString();
+ if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
+ eVal = SvxCellHorJustify::Right;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
+ eVal = SvxCellHorJustify::Center;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
+ eVal = SvxCellHorJustify::Left;
+ else
+ eVal = SvxCellHorJustify::Standard;
+ if ( eVal != SvxCellHorJustify::Standard )
+ mxActEntry->aItemSet.Put(SvxHorJustifyItem(eVal, ATTR_HOR_JUSTIFY));
+ }
+ break;
+ case HtmlOptionId::VALIGN:
+ {
+ SvxCellVerJustify eVal;
+ const OUString& rOptVal = rOption.GetString();
+ if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
+ eVal = SvxCellVerJustify::Top;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
+ eVal = SvxCellVerJustify::Center;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
+ eVal = SvxCellVerJustify::Bottom;
+ else
+ eVal = SvxCellVerJustify::Standard;
+ mxActEntry->aItemSet.Put(SvxVerJustifyItem(eVal, ATTR_VER_JUSTIFY));
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ {
+ mxActEntry->nWidth = GetWidthPixel(rOption);
+ }
+ break;
+ case HtmlOptionId::BGCOLOR:
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ mxActEntry->aItemSet.Put(SvxBrushItem(aColor, ATTR_BACKGROUND));
+ }
+ break;
+ case HtmlOptionId::SDVAL:
+ {
+ mxActEntry->pValStr = rOption.GetString();
+ }
+ break;
+ case HtmlOptionId::SDNUM:
+ {
+ mxActEntry->pNumStr = rOption.GetString();
+ }
+ break;
+ default: break;
+ }
+ }
+
+ mxActEntry->nCol = nColCnt;
+ mxActEntry->nRow = nRowCnt;
+ mxActEntry->nTab = nTable;
+
+ if ( bHorJustifyCenterTH )
+ mxActEntry->aItemSet.Put(
+ SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY) );
+}
+
+void ScHTMLLayoutParser::TableRowOn( const HtmlImportInfo* pInfo )
+{
+ if ( nColCnt > nColCntStart )
+ NextRow( pInfo ); // The optional TableRowOff wasn't there
+ nColOffset = nColOffsetStart;
+}
+
+void ScHTMLLayoutParser::TableRowOff( const HtmlImportInfo* pInfo )
+{
+ NextRow( pInfo );
+}
+
+void ScHTMLLayoutParser::TableDataOff( const HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo ); // Only if it really was one
+}
+
+void ScHTMLLayoutParser::TableOn( HtmlImportInfo* pInfo )
+{
+ if ( ++nTableLevel > 1 )
+ { // Table in Table
+ sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize()
+ Colonize(mxActEntry.get());
+ aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
+ mxActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
+ nRowCnt, nColCntStart, nMaxCol, nTable,
+ nTableWidth, nColOffset, nColOffsetStart,
+ bFirstRow ) );
+ sal_uInt16 nLastWidth = nTableWidth;
+ nTableWidth = GetWidth(mxActEntry.get());
+ if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
+ { // There must be more than one, so this one cannot be enough
+ nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
+ }
+ nLastWidth = nTableWidth;
+ if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
+ { // It can still be TD or TH, if we didn't have a TABLE earlier
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::WIDTH:
+ { // Percent: of document width or outer cell
+ nTableWidth = GetWidthPixel( rOption );
+ }
+ break;
+ case HtmlOptionId::BORDER:
+ // Border is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
+ break;
+ default: break;
+ }
+ }
+ }
+ bInCell = false;
+ if ( bTabInTabCell && (nTableWidth >= nLastWidth) )
+ { // Multiple tables in one cell, underneath each other
+ bTabInTabCell = false;
+ NextRow( pInfo );
+ }
+ else
+ { // It start's in this cell or next to each other
+ bTabInTabCell = false;
+ nColCntStart = nColCnt;
+ nColOffset = nTmpColOffset;
+ nColOffsetStart = nColOffset;
+ }
+
+ NewActEntry(!maList.empty() ? maList.back().get() : nullptr); // New free flying mxActEntry
+ xLockedList = new ScRangeList;
+ }
+ else
+ { // Simple table at the document level
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+ if (mxActEntry->aSel.HasRange())
+ { // Flying text left
+ CloseEntry( pInfo );
+ NextRow( pInfo );
+ }
+ aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
+ mxActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
+ nRowCnt, nColCntStart, nMaxCol, nTable,
+ nTableWidth, nColOffset, nColOffsetStart,
+ bFirstRow ) );
+ // As soon as we have multiple tables we need to be tolerant with the offsets.
+ if (nMaxTable > 0)
+ nOffsetTolerance = SC_HTML_OFFSET_TOLERANCE_LARGE;
+ nTableWidth = 0;
+ if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
+ {
+ // It can still be TD or TH, if we didn't have a TABLE earlier
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::WIDTH:
+ { // Percent: of document width or outer cell
+ nTableWidth = GetWidthPixel( rOption );
+ }
+ break;
+ case HtmlOptionId::BORDER:
+ //BorderOn is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ nTable = ++nMaxTable;
+ bFirstRow = true;
+ nFirstTableCell = maList.size();
+
+ pLocalColOffset = new ScHTMLColOffset;
+ MakeColNoRef( pLocalColOffset, nColOffsetStart, 0, 0, 0 );
+}
+
+void ScHTMLLayoutParser::TableOff( const HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo );
+ if ( nColCnt > nColCntStart )
+ TableRowOff( pInfo ); // The optional TableRowOff wasn't
+ if ( !nTableLevel )
+ {
+ OSL_FAIL( "dumbo doc! </TABLE> without opening <TABLE>" );
+ return ;
+ }
+ if ( --nTableLevel > 0 )
+ { // Table in Table done
+ if ( !aTableStack.empty() )
+ {
+ std::unique_ptr<ScHTMLTableStackEntry> pS = std::move(aTableStack.top());
+ aTableStack.pop();
+
+ auto& pE = pS->xCellEntry;
+ SCROW nRows = nRowCnt - pS->nRowCnt;
+ if ( nRows > 1 )
+ { // Insert size of table at this position
+ SCROW nRow = pS->nRowCnt;
+ sal_uInt16 nTab = pS->nTable;
+ if ( !pTables )
+ pTables.reset( new OuterMap );
+ // Height of outer table
+ OuterMap::const_iterator it = pTables->find( nTab );
+ InnerMap* pTab1;
+ if ( it == pTables->end() )
+ {
+ pTab1 = new InnerMap;
+ (*pTables)[ nTab ] = pTab1;
+ }
+ else
+ pTab1 = it->second;
+ SCROW nRowSpan = pE->nRowOverlap;
+ SCROW nRowKGV;
+ SCROW nRowsPerRow1; // Outer table
+ SCROW nRowsPerRow2; // Inner table
+ if ( nRowSpan > 1 )
+ { // LCM to which we can map the inner and outer rows
+ nRowKGV = std::lcm( nRowSpan, nRows );
+ nRowsPerRow1 = nRowKGV / nRowSpan;
+ nRowsPerRow2 = nRowKGV / nRows;
+ }
+ else
+ {
+ nRowKGV = nRowsPerRow1 = nRows;
+ nRowsPerRow2 = 1;
+ }
+ InnerMap* pTab2 = nullptr;
+ if ( nRowsPerRow2 > 1 )
+ { // Height of the inner table
+ pTab2 = new InnerMap;
+ (*pTables)[ nTable ] = pTab2;
+ }
+ // Abuse void* Data entry of the Table class for height mapping
+ if ( nRowKGV > 1 )
+ {
+ if ( nRowsPerRow1 > 1 )
+ { // Outer
+ for ( SCROW j=0; j < nRowSpan; j++ )
+ {
+ sal_uLong nRowKey = nRow + j;
+ SCROW nR = (*pTab1)[ nRowKey ];
+ if ( !nR )
+ (*pTab1)[ nRowKey ] = nRowsPerRow1;
+ else if ( nRowsPerRow1 > nR )
+ (*pTab1)[ nRowKey ] = nRowsPerRow1;
+ //TODO: How can we improve on this?
+ else if ( nRowsPerRow1 < nR && nRowSpan == 1
+ && nTable == nMaxTable )
+ { // Still some space left, merge in a better way (if possible)
+ SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
+ nR += nAdd;
+ if ( (nR % nRows) == 0 )
+ { // Only if representable
+ SCROW nR2 = (*pTab1)[ nRowKey+1 ];
+ if ( nR2 > nAdd )
+ { // Only if we really have enough space
+ (*pTab1)[ nRowKey ] = nR;
+ (*pTab1)[ nRowKey+1 ] = nR2 - nAdd;
+ nRowsPerRow2 = nR / nRows;
+ }
+ }
+ }
+ }
+ }
+ if ( nRowsPerRow2 > 1 )
+ { // Inner
+ if ( !pTab2 )
+ { // nRowsPerRow2 could be've been incremented
+ pTab2 = new InnerMap;
+ (*pTables)[ nTable ] = pTab2;
+ }
+ for ( SCROW j=0; j < nRows; j++ )
+ {
+ sal_uLong nRowKey = nRow + j;
+ (*pTab2)[ nRowKey ] = nRowsPerRow2;
+ }
+ }
+ }
+ }
+
+ SetWidths();
+
+ if ( !pE->nWidth )
+ pE->nWidth = nTableWidth;
+ else if ( pE->nWidth < nTableWidth )
+ {
+ sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
+ sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
+ ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
+ sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
+ pE->nWidth = nNewOffset - pE->nOffset;
+ pS->nTableWidth = pS->nTableWidth + nTmp;
+ if ( pS->nColOffset >= nOldOffset )
+ pS->nColOffset = pS->nColOffset + nTmp;
+ }
+
+ nColCnt = pE->nCol + pE->nColOverlap;
+ nRowCnt = pS->nRowCnt;
+ nColCntStart = pS->nColCntStart;
+ nMaxCol = pS->nMaxCol;
+ nTable = pS->nTable;
+ nTableWidth = pS->nTableWidth;
+ nFirstTableCell = pS->nFirstTableCell;
+ nColOffset = pS->nColOffset;
+ nColOffsetStart = pS->nColOffsetStart;
+ bFirstRow = pS->bFirstRow;
+ xLockedList = pS->xLockedList;
+ pLocalColOffset = pS->pLocalColOffset;
+ // mxActEntry is kept around if a table is started in the same row
+ // (anything's possible in HTML); will be deleted by CloseEntry
+ mxActEntry = pE;
+ }
+ bTabInTabCell = true;
+ bInCell = true;
+ }
+ else
+ { // Simple table finished
+ SetWidths();
+ nMaxCol = 0;
+ nTable = 0;
+ if ( !aTableStack.empty() )
+ {
+ ScHTMLTableStackEntry* pS = aTableStack.top().get();
+ delete pLocalColOffset;
+ pLocalColOffset = pS->pLocalColOffset;
+ aTableStack.pop();
+ }
+ }
+}
+
+void ScHTMLLayoutParser::Image( HtmlImportInfo* pInfo )
+{
+ mxActEntry->maImageList.push_back(std::make_unique<ScHTMLImage>());
+ ScHTMLImage* pImage = mxActEntry->maImageList.back().get();
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::SRC:
+ {
+ pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, rOption.GetString() );
+ }
+ break;
+ case HtmlOptionId::ALT:
+ {
+ if (!mxActEntry->bHasGraphic)
+ { // ALT text only if not any image loaded
+ if (!mxActEntry->aAltText.isEmpty())
+ mxActEntry->aAltText += "; ";
+
+ mxActEntry->aAltText += rOption.GetString();
+ }
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ {
+ pImage->aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::HEIGHT:
+ {
+ pImage->aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::HSPACE:
+ {
+ pImage->aSpace.setX( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::VSPACE:
+ {
+ pImage->aSpace.setY( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ default: break;
+ }
+ }
+ if (pImage->aURL.isEmpty())
+ {
+ OSL_FAIL( "Image: graphic without URL ?!?" );
+ return ;
+ }
+
+ sal_uInt16 nFormat;
+ std::optional<Graphic> oGraphic(std::in_place);
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ if ( ERRCODE_NONE != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
+ *oGraphic, &rFilter, &nFormat ) )
+ {
+ return ; // Bad luck
+ }
+ if (!mxActEntry->bHasGraphic)
+ { // discard any ALT text in this cell if we have any image
+ mxActEntry->bHasGraphic = true;
+ mxActEntry->aAltText.clear();
+ }
+ pImage->aFilterName = rFilter.GetImportFormatName( nFormat );
+ pImage->oGraphic = std::move( oGraphic );
+ if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
+ {
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ pImage->aSize = pDefaultDev->LogicToPixel( pImage->oGraphic->GetPrefSize(),
+ pImage->oGraphic->GetPrefMapMode() );
+ }
+ if (mxActEntry->maImageList.empty())
+ return;
+
+ tools::Long nWidth = 0;
+ for (const std::unique_ptr<ScHTMLImage> & pI : mxActEntry->maImageList)
+ {
+ if ( pI->nDir & nHorizontal )
+ nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
+ else
+ nWidth = 0;
+ }
+ if ( mxActEntry->nWidth
+ && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
+ >= mxActEntry->nWidth) )
+ mxActEntry->maImageList.back()->nDir = nVertical;
+}
+
+void ScHTMLLayoutParser::ColOn( HtmlImportInfo* pInfo )
+{
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ if( rOption.GetToken() == HtmlOptionId::WIDTH )
+ {
+ sal_uInt16 nVal = GetWidthPixel( rOption );
+ MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
+ nColOffset = nColOffset + nVal;
+ }
+ }
+}
+
+sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption& rOption )
+{
+ const OUString& rOptVal = rOption.GetString();
+ if ( rOptVal.indexOf('%') != -1 )
+ { // Percent
+ sal_uInt16 nW = (nTableWidth ? nTableWidth : static_cast<sal_uInt16>(aPageSize.Width()));
+ return static_cast<sal_uInt16>((rOption.GetNumber() * nW) / 100);
+ }
+ else
+ {
+ if ( rOptVal.indexOf('*') != -1 )
+ { // Relative to what?
+ // TODO: Collect all relative values in ColArray and then MakeCol
+ return 0;
+ }
+ else
+ return static_cast<sal_uInt16>(rOption.GetNumber()); // Pixel
+ }
+}
+
+void ScHTMLLayoutParser::AnchorOn( HtmlImportInfo* pInfo )
+{
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ if( rOption.GetToken() == HtmlOptionId::NAME )
+ mxActEntry->pName = rOption.GetString();
+ }
+}
+
+bool ScHTMLLayoutParser::IsAtBeginningOfText( const HtmlImportInfo* pInfo )
+{
+ ESelection& rSel = mxActEntry->aSel;
+ return rSel.nStartPara == rSel.nEndPara &&
+ rSel.nStartPara <= pInfo->aSelection.nEndPara &&
+ pEdit->GetTextLen( rSel.nStartPara ) == 0;
+}
+
+void ScHTMLLayoutParser::FontOn( HtmlImportInfo* pInfo )
+{
+ if ( !IsAtBeginningOfText( pInfo ) )
+ return;
+
+// Only at the start of the text; applies to whole line
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::FACE :
+ {
+ const OUString& rFace = rOption.GetString();
+ OUStringBuffer aFontName;
+ sal_Int32 nPos = 0;
+ while( nPos != -1 )
+ {
+ // Font list, VCL uses the semicolon as separator
+ // HTML uses the comma
+ std::u16string_view aFName = o3tl::getToken(rFace, 0, ',', nPos );
+ aFName = comphelper::string::strip(aFName, ' ');
+ if( !aFontName.isEmpty() )
+ aFontName.append(";");
+ aFontName.append(aFName);
+ }
+ if ( !aFontName.isEmpty() )
+ mxActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
+ aFontName.makeStringAndClear(), OUString(), PITCH_DONTKNOW,
+ RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
+ }
+ break;
+ case HtmlOptionId::SIZE :
+ {
+ sal_uInt16 nSize = static_cast<sal_uInt16>(rOption.GetNumber());
+ if ( nSize == 0 )
+ nSize = 1;
+ else if ( nSize > SC_HTML_FONTSIZES )
+ nSize = SC_HTML_FONTSIZES;
+ mxActEntry->aItemSet.Put( SvxFontHeightItem(
+ maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlOptionId::COLOR :
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ mxActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void ScHTMLLayoutParser::ProcToken( HtmlImportInfo* pInfo )
+{
+ switch ( pInfo->nToken )
+ {
+ case HtmlTokenId::META:
+ {
+ HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser);
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<cppu::OWeakObject*>(mpDoc->GetDocumentShell()->GetModel()), uno::UNO_QUERY_THROW);
+ pParser->ParseMetaOptions(
+ xDPS->getDocumentProperties(),
+ mpDoc->GetDocumentShell()->GetHeaderAttributes() );
+ }
+ break;
+ case HtmlTokenId::TITLE_ON:
+ {
+ bInTitle = true;
+ aString.clear();
+ }
+ break;
+ case HtmlTokenId::TITLE_OFF:
+ {
+ if ( bInTitle && !aString.isEmpty() )
+ {
+ // Remove blanks from line breaks
+ aString = aString.trim();
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<cppu::OWeakObject*>(mpDoc->GetDocumentShell()->GetModel()),
+ uno::UNO_QUERY_THROW);
+ xDPS->getDocumentProperties()->setTitle(aString);
+ }
+ bInTitle = false;
+ }
+ break;
+ case HtmlTokenId::TABLE_ON:
+ {
+ TableOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::COL_ON:
+ {
+ ColOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEHEADER_ON: // Opens row
+ {
+ if ( bInCell )
+ CloseEntry( pInfo );
+ // Do not set bInCell to true, TableDataOn does that
+ mxActEntry->aItemSet.Put(
+ SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT) );
+ [[fallthrough]];
+ }
+ case HtmlTokenId::TABLEDATA_ON: // Opens cell
+ {
+ TableDataOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEHEADER_OFF:
+ case HtmlTokenId::TABLEDATA_OFF: // Closes cell
+ {
+ TableDataOff( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEROW_ON: // Before first cell in row
+ {
+ TableRowOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEROW_OFF: // After last cell in row
+ {
+ TableRowOff( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLE_OFF:
+ {
+ TableOff( pInfo );
+ }
+ break;
+ case HtmlTokenId::IMAGE:
+ {
+ Image( pInfo );
+ }
+ break;
+ case HtmlTokenId::PARABREAK_OFF:
+ { // We continue vertically after an image
+ if (!mxActEntry->maImageList.empty())
+ mxActEntry->maImageList.back()->nDir = nVertical;
+ }
+ break;
+ case HtmlTokenId::ANCHOR_ON:
+ {
+ AnchorOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::FONT_ON :
+ {
+ FontOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::BIGPRINT_ON :
+ {
+ // TODO: Remember current font size and increase by 1
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxFontHeightItem(
+ maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlTokenId::SMALLPRINT_ON :
+ {
+ // TODO: Remember current font size and decrease by 1
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxFontHeightItem(
+ maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlTokenId::BOLD_ON :
+ case HtmlTokenId::STRONG_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
+ ATTR_FONT_WEIGHT ) );
+ }
+ break;
+ case HtmlTokenId::ITALIC_ON :
+ case HtmlTokenId::EMPHASIS_ON :
+ case HtmlTokenId::ADDRESS_ON :
+ case HtmlTokenId::BLOCKQUOTE_ON :
+ case HtmlTokenId::BLOCKQUOTE30_ON :
+ case HtmlTokenId::CITATION_ON :
+ case HtmlTokenId::VARIABLE_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
+ ATTR_FONT_POSTURE ) );
+ }
+ break;
+ case HtmlTokenId::DEFINSTANCE_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ {
+ mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
+ ATTR_FONT_WEIGHT ) );
+ mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
+ ATTR_FONT_POSTURE ) );
+ }
+ }
+ break;
+ case HtmlTokenId::UNDERLINE_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE,
+ ATTR_FONT_UNDERLINE ) );
+ }
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ {
+ if ( bInTitle )
+ aString += pInfo->aText;
+ }
+ break;
+ default: ;
+ }
+}
+
+// HTML DATA QUERY PARSER
+
+template< typename Type >
+static Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
+{ return std::clamp( rValue, rMin, rMax ); }
+
+ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
+ ScEEParseEntry( rItemSet ),
+ mbImportAlways( false )
+{
+ nTab = nTableId;
+ bEntirePara = false;
+}
+
+bool ScHTMLEntry::HasContents() const
+{
+ return mbImportAlways || aSel.HasRange() || !aAltText.isEmpty() || IsTable();
+}
+
+void ScHTMLEntry::AdjustStart( const HtmlImportInfo& rInfo )
+{
+ // set start position
+ aSel.nStartPara = rInfo.aSelection.nStartPara;
+ aSel.nStartPos = rInfo.aSelection.nStartPos;
+ // adjust end position
+ if( (aSel.nEndPara < aSel.nStartPara) || ((aSel.nEndPara == aSel.nStartPara) && (aSel.nEndPos < aSel.nStartPos)) )
+ {
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nEndPos = aSel.nStartPos;
+ }
+}
+
+void ScHTMLEntry::AdjustEnd( const HtmlImportInfo& rInfo )
+{
+ OSL_ENSURE( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
+ ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
+ "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
+ // set end position
+ aSel.nEndPara = rInfo.aSelection.nEndPara;
+ aSel.nEndPos = rInfo.aSelection.nEndPos;
+}
+
+void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
+{
+ // strip leading empty paragraphs
+ while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
+ {
+ ++aSel.nStartPara;
+ aSel.nStartPos = 0;
+ }
+ // strip trailing empty paragraphs
+ while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
+ {
+ --aSel.nEndPara;
+ aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
+ }
+}
+
+/** A map of ScHTMLTable objects.
+
+ Organizes the tables with a unique table key. Stores nested tables inside
+ the parent table and forms in this way a tree structure of tables. An
+ instance of this class owns the contained table objects and deletes them
+ on destruction.
+ */
+class ScHTMLTableMap final
+{
+private:
+ typedef std::shared_ptr< ScHTMLTable > ScHTMLTablePtr;
+ typedef std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
+
+public:
+ typedef ScHTMLTableStdMap::iterator iterator;
+ typedef ScHTMLTableStdMap::const_iterator const_iterator;
+
+private:
+ ScHTMLTable& mrParentTable; /// Reference to parent table.
+ ScHTMLTableStdMap maTables; /// Container for all table objects.
+ mutable ScHTMLTable* mpCurrTable; /// Current table, used for fast search.
+
+public:
+ explicit ScHTMLTableMap( ScHTMLTable& rParentTable );
+
+ const_iterator begin() const { return maTables.begin(); }
+ const_iterator end() const { return maTables.end(); }
+
+ /** Returns the specified table.
+ @param nTableId Unique identifier of the table.
+ @param bDeep true = searches deep in all nested table; false = only in this container. */
+ ScHTMLTable* FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
+
+ /** Inserts a new table into the container. This container owns the created table.
+ @param bPreFormText true = New table is based on preformatted text (<pre> tag). */
+ ScHTMLTable* CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText, const ScDocument& rDoc );
+
+private:
+ /** Sets a working table with its index for search optimization. */
+ void SetCurrTable( ScHTMLTable* pTable ) const
+ { if( pTable ) mpCurrTable = pTable; }
+};
+
+ScHTMLTableMap::ScHTMLTableMap( ScHTMLTable& rParentTable ) :
+ mrParentTable(rParentTable),
+ mpCurrTable(nullptr)
+{
+}
+
+ScHTMLTable* ScHTMLTableMap::FindTable( ScHTMLTableId nTableId, bool bDeep ) const
+{
+ ScHTMLTable* pResult = nullptr;
+ if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
+ pResult = mpCurrTable; // cached table
+ else
+ {
+ const_iterator aFind = maTables.find( nTableId );
+ if( aFind != maTables.end() )
+ pResult = aFind->second.get(); // table from this container
+ }
+
+ // not found -> search deep in nested tables
+ if( !pResult && bDeep )
+ for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
+ pResult = aIter->second->FindNestedTable( nTableId );
+
+ SetCurrTable( pResult );
+ return pResult;
+}
+
+ScHTMLTable* ScHTMLTableMap::CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText, const ScDocument& rDoc )
+{
+ ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText, rDoc );
+ maTables[ pTable->GetTableId() ].reset( pTable );
+ SetCurrTable( pTable );
+ return pTable;
+}
+
+namespace {
+
+/** Simplified forward iterator for convenience.
+
+ Before the iterator can be dereferenced, it must be tested with the is()
+ method. The iterator may be invalid directly after construction (e.g. empty
+ container).
+ */
+class ScHTMLTableIterator
+{
+public:
+ /** Constructs the iterator for the passed table map.
+ @param pTableMap Pointer to the table map (is allowed to be NULL). */
+ explicit ScHTMLTableIterator( const ScHTMLTableMap* pTableMap );
+
+ bool is() const { return mpTableMap && maIter != maEnd; }
+ ScHTMLTable* operator->() { return maIter->second.get(); }
+ ScHTMLTableIterator& operator++() { ++maIter; return *this; }
+
+private:
+ ScHTMLTableMap::const_iterator maIter;
+ ScHTMLTableMap::const_iterator maEnd;
+ const ScHTMLTableMap* mpTableMap;
+};
+
+}
+
+ScHTMLTableIterator::ScHTMLTableIterator( const ScHTMLTableMap* pTableMap ) :
+ mpTableMap(pTableMap)
+{
+ if( pTableMap )
+ {
+ maIter = pTableMap->begin();
+ maEnd = pTableMap->end();
+ }
+}
+
+ScHTMLTableAutoId::ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId ) :
+ mnTableId( rnUnusedId ),
+ mrnUnusedId( rnUnusedId )
+{
+ ++mrnUnusedId;
+}
+
+ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const HtmlImportInfo& rInfo, bool bPreFormText, const ScDocument& rDoc ) :
+ mpParentTable( &rParentTable ),
+ maTableId( rParentTable.maTableId.mrnUnusedId ),
+ maTableItemSet( rParentTable.GetCurrItemSet() ),
+ mrEditEngine( rParentTable.mrEditEngine ),
+ mrEEParseList( rParentTable.mrEEParseList ),
+ mpCurrEntryVector( nullptr ),
+ maSize( 1, 1 ),
+ mpParser(rParentTable.mpParser),
+ mrDoc(rDoc),
+ mbBorderOn( false ),
+ mbPreFormText( bPreFormText ),
+ mbRowOn( false ),
+ mbDataOn( false ),
+ mbPushEmptyLine( false ),
+ mbCaptionOn ( false )
+{
+ if( mbPreFormText )
+ {
+ ImplRowOn();
+ ImplDataOn( ScHTMLSize( 1, 1 ) );
+ }
+ else
+ {
+ ProcessFormatOptions( maTableItemSet, rInfo );
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ for (const auto& rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::BORDER:
+ mbBorderOn = rOption.GetString().isEmpty() || (rOption.GetNumber() != 0);
+ break;
+ case HtmlOptionId::ID:
+ maTableName = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+ }
+
+ CreateNewEntry( rInfo );
+}
+
+ScHTMLTable::ScHTMLTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
+ ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser, const ScDocument& rDoc
+) :
+ mpParentTable( nullptr ),
+ maTableId( rnUnusedId ),
+ maTableItemSet( rPool ),
+ mrEditEngine( rEditEngine ),
+ mrEEParseList( rEEParseList ),
+ mpCurrEntryVector( nullptr ),
+ maSize( 1, 1 ),
+ mpParser(pParser),
+ mrDoc(rDoc),
+ mbBorderOn( false ),
+ mbPreFormText( false ),
+ mbRowOn( false ),
+ mbDataOn( false ),
+ mbPushEmptyLine( false ),
+ mbCaptionOn ( false )
+{
+ // open the first "cell" of the document
+ ImplRowOn();
+ ImplDataOn( ScHTMLSize( 1, 1 ) );
+ mxCurrEntry = CreateEntry();
+}
+
+ScHTMLTable::~ScHTMLTable()
+{
+}
+
+const SfxItemSet& ScHTMLTable::GetCurrItemSet() const
+{
+ // first try cell item set, then row item set, then table item set
+ return moDataItemSet ? *moDataItemSet : (moRowItemSet ? *moRowItemSet : maTableItemSet);
+}
+
+ScHTMLSize ScHTMLTable::GetSpan( const ScHTMLPos& rCellPos ) const
+{
+ ScHTMLSize aSpan( 1, 1 );
+ const ScRange* pRange = maVMergedCells.Find( rCellPos.MakeAddr() );
+ if (!pRange)
+ pRange = maHMergedCells.Find( rCellPos.MakeAddr() );
+ if (pRange)
+ aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
+ return aSpan;
+}
+
+ScHTMLTable* ScHTMLTable::FindNestedTable( ScHTMLTableId nTableId ) const
+{
+ return mxNestedTables ? mxNestedTables->FindTable( nTableId ) : nullptr;
+}
+
+void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutItem - no current entry" );
+ if( mxCurrEntry && mxCurrEntry->IsEmpty() )
+ mxCurrEntry->GetItemSet().Put( rItem );
+}
+
+void ScHTMLTable::PutText( const HtmlImportInfo& rInfo )
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutText - no current entry" );
+ if( mxCurrEntry )
+ {
+ if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
+ mxCurrEntry->AdjustStart( rInfo );
+ else
+ mxCurrEntry->AdjustEnd( rInfo );
+ if (mbCaptionOn)
+ maCaptionBuffer.append(rInfo.aText);
+
+ }
+}
+
+void ScHTMLTable::InsertPara( const HtmlImportInfo& rInfo )
+{
+ if( mxCurrEntry && mbDataOn && !IsEmptyCell() )
+ mxCurrEntry->SetImportAlways();
+ PushEntry( rInfo );
+ CreateNewEntry( rInfo );
+ InsertLeadingEmptyLine();
+}
+
+void ScHTMLTable::BreakOn()
+{
+ // empty line, if <br> is at start of cell
+ mbPushEmptyLine = !mbPreFormText && mbDataOn && IsEmptyCell();
+}
+
+void ScHTMLTable::HeadingOn()
+{
+ // call directly, InsertPara() has not been called before
+ InsertLeadingEmptyLine();
+}
+
+void ScHTMLTable::InsertLeadingEmptyLine()
+{
+ // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
+ mbPushEmptyLine = !mbPreFormText && mbDataOn && !IsEmptyCell();
+}
+
+void ScHTMLTable::AnchorOn()
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::AnchorOn - no current entry" );
+ // don't skip entries with single hyperlinks
+ if( mxCurrEntry )
+ mxCurrEntry->SetImportAlways();
+}
+
+ScHTMLTable* ScHTMLTable::TableOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo );
+ return InsertNestedTable( rInfo, false );
+}
+
+ScHTMLTable* ScHTMLTable::TableOff( const HtmlImportInfo& rInfo )
+{
+ return mbPreFormText ? this : CloseTable( rInfo );
+}
+
+void ScHTMLTable::CaptionOn()
+{
+ mbCaptionOn = true;
+ maCaptionBuffer.setLength(0);
+}
+
+void ScHTMLTable::CaptionOff()
+{
+ if (!mbCaptionOn)
+ return;
+ maCaption = maCaptionBuffer.makeStringAndClear().trim();
+ mbCaptionOn = false;
+}
+
+ScHTMLTable* ScHTMLTable::PreOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo );
+ return InsertNestedTable( rInfo, true );
+}
+
+ScHTMLTable* ScHTMLTable::PreOff( const HtmlImportInfo& rInfo )
+{
+ return mbPreFormText ? CloseTable( rInfo ) : this;
+}
+
+void ScHTMLTable::RowOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
+ {
+ ImplRowOn();
+ ProcessFormatOptions( *moRowItemSet, rInfo );
+ }
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::RowOff( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
+ ImplRowOff();
+ CreateNewEntry( rInfo );
+}
+
+namespace {
+
+/**
+ * Decode a number format string stored in Excel-generated HTML's CSS
+ * region.
+ */
+OUString decodeNumberFormat(const OUString& rFmt)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rFmt.getStr();
+ sal_Int32 n = rFmt.getLength();
+ for (sal_Int32 i = 0; i < n; ++i, ++p)
+ {
+ if (*p == '\\')
+ {
+ // Skip '\'.
+ ++i;
+ ++p;
+
+ // Parse all subsequent digits until first non-digit is found.
+ sal_Int32 nDigitCount = 0;
+ const sal_Unicode* p1 = p;
+ for (; i < n; ++i, ++p, ++nDigitCount)
+ {
+ if (*p < '0' || '9' < *p)
+ {
+ --i;
+ --p;
+ break;
+ }
+
+ }
+ if (nDigitCount)
+ {
+ // Hex-encoded character found. Decode it back into its
+ // original character. An example of number format with
+ // hex-encoded chars: "\0022$\0022\#\,\#\#0\.00"
+ sal_uInt32 nVal = OUString(p1, nDigitCount).toUInt32(16);
+ aBuf.append(static_cast<sal_Unicode>(nVal));
+ }
+ }
+ else
+ aBuf.append(*p);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+}
+
+void ScHTMLTable::DataOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
+ {
+ // read needed options from the <td> tag
+ ScHTMLSize aSpanSize( 1, 1 );
+ std::optional<OUString> pValStr, pNumStr;
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ sal_uInt32 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ for (const auto& rOption : rOptions)
+ {
+ switch (rOption.GetToken())
+ {
+ case HtmlOptionId::COLSPAN:
+ aSpanSize.mnCols = static_cast<SCCOL>( getLimitedValue<sal_Int32>( rOption.GetString().toInt32(), 1, 256 ) );
+ break;
+ case HtmlOptionId::ROWSPAN:
+ aSpanSize.mnRows = static_cast<SCROW>( getLimitedValue<sal_Int32>( rOption.GetString().toInt32(), 1, 256 ) );
+ break;
+ case HtmlOptionId::SDVAL:
+ pValStr = rOption.GetString();
+ break;
+ case HtmlOptionId::SDNUM:
+ pNumStr = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ {
+ // Pick up the number format associated with this class (if
+ // any).
+ OUString aClass = rOption.GetString();
+ const ScHTMLStyles& rStyles = mpParser->GetStyles();
+ const OUString& rVal = rStyles.getPropertyValue("td", aClass, "mso-number-format");
+ if (!rVal.isEmpty())
+ {
+ OUString aNumFmt = decodeNumberFormat(rVal);
+
+ nNumberFormat = GetFormatTable()->GetEntryKey(aNumFmt);
+ if (nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nDummy;
+ bool bValidFmt = GetFormatTable()->PutEntry(aNumFmt, nErrPos, nDummy, nNumberFormat);
+ if (!bValidFmt)
+ nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ ImplDataOn( aSpanSize );
+
+ if (nNumberFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ moDataItemSet->Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat) );
+
+ ProcessFormatOptions( *moDataItemSet, rInfo );
+ CreateNewEntry( rInfo );
+ mxCurrEntry->pValStr = std::move(pValStr);
+ mxCurrEntry->pNumStr = std::move(pNumStr);
+ }
+ else
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::DataOff( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
+ ImplDataOff();
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::BodyOn( const HtmlImportInfo& rInfo )
+{
+ bool bPushed = PushEntry( rInfo );
+ if( !mpParentTable )
+ {
+ // do not start new row, if nothing (no title) precedes the body.
+ if( bPushed || !mbRowOn )
+ ImplRowOn();
+ if( bPushed || !mbDataOn )
+ ImplDataOn( ScHTMLSize( 1, 1 ) );
+ ProcessFormatOptions( *moDataItemSet, rInfo );
+ }
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::BodyOff( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo );
+ if( !mpParentTable )
+ {
+ ImplDataOff();
+ ImplRowOff();
+ }
+ CreateNewEntry( rInfo );
+}
+
+ScHTMLTable* ScHTMLTable::CloseTable( const HtmlImportInfo& rInfo )
+{
+ if( mpParentTable ) // not allowed to close global table
+ {
+ PushEntry( rInfo, mbDataOn );
+ ImplDataOff();
+ ImplRowOff();
+ mpParentTable->PushTableEntry( GetTableId() );
+ mpParentTable->CreateNewEntry( rInfo );
+ if( mbPreFormText ) // enclose preformatted table with empty lines in parent table
+ mpParentTable->InsertLeadingEmptyLine();
+ return mpParentTable;
+ }
+ return this;
+}
+
+SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
+{
+ const ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ size_t nIndex = static_cast< size_t >( nCellPos );
+ if( nIndex >= rSizes.size() ) return 0;
+ return (nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]);
+}
+
+SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const
+{
+ const ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ size_t nBeginIdx = static_cast< size_t >( std::max< SCCOLROW >( nCellBegin, 0 ) );
+ size_t nEndIdx = static_cast< size_t >( std::min< SCCOLROW >( nCellEnd, static_cast< SCCOLROW >( rSizes.size() ) ) );
+ if (nBeginIdx >= nEndIdx ) return 0;
+ return rSizes[ nEndIdx - 1 ] - ((nBeginIdx == 0) ? 0 : rSizes[ nBeginIdx - 1 ]);
+}
+
+SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient ) const
+{
+ const ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ return rSizes.empty() ? 0 : rSizes.back();
+}
+
+ScHTMLSize ScHTMLTable::GetDocSize( const ScHTMLPos& rCellPos ) const
+{
+ ScHTMLSize aCellSpan = GetSpan( rCellPos );
+ return ScHTMLSize(
+ static_cast< SCCOL >( GetDocSize( tdCol, rCellPos.mnCol, rCellPos.mnCol + aCellSpan.mnCols ) ),
+ static_cast< SCROW >( GetDocSize( tdRow, rCellPos.mnRow, rCellPos.mnRow + aCellSpan.mnRows ) ) );
+}
+
+SCCOLROW ScHTMLTable::GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
+{
+ return maDocBasePos.Get( eOrient ) + GetDocSize( eOrient, 0, nCellPos );
+}
+
+ScHTMLPos ScHTMLTable::GetDocPos( const ScHTMLPos& rCellPos ) const
+{
+ return ScHTMLPos(
+ static_cast< SCCOL >( GetDocPos( tdCol, rCellPos.mnCol ) ),
+ static_cast< SCROW >( GetDocPos( tdRow, rCellPos.mnRow ) ) );
+}
+
+void ScHTMLTable::GetDocRange( ScRange& rRange ) const
+{
+ rRange.aStart = rRange.aEnd = maDocBasePos.MakeAddr();
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!rRange.aEnd.Move( static_cast< SCCOL >( GetDocSize( tdCol ) ) - 1,
+ static_cast< SCROW >( GetDocSize( tdRow ) ) - 1, 0, aErrorPos, mrDoc ))
+ {
+ assert(!"can't move");
+ }
+}
+
+void ScHTMLTable::ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const
+{
+ OSL_ENSURE( pDoc, "ScHTMLTable::ApplyCellBorders - no document" );
+ if( pDoc && mbBorderOn )
+ {
+ const SCCOL nLastCol = maSize.mnCols - 1;
+ const SCROW nLastRow = maSize.mnRows - 1;
+ const tools::Long nOuterLine = SvxBorderLineWidth::Medium;
+ const tools::Long nInnerLine = SvxBorderLineWidth::Hairline;
+ SvxBorderLine aOuterLine(nullptr, nOuterLine, SvxBorderLineStyle::SOLID);
+ SvxBorderLine aInnerLine(nullptr, nInnerLine, SvxBorderLineStyle::SOLID);
+ SvxBoxItem aBorderItem( ATTR_BORDER );
+
+ for( SCCOL nCol = 0; nCol <= nLastCol; ++nCol )
+ {
+ SvxBorderLine* pLeftLine = (nCol == 0) ? &aOuterLine : &aInnerLine;
+ SvxBorderLine* pRightLine = (nCol == nLastCol) ? &aOuterLine : &aInnerLine;
+ SCCOL nCellCol1 = static_cast< SCCOL >( GetDocPos( tdCol, nCol ) ) + rFirstPos.Col();
+ SCCOL nCellCol2 = nCellCol1 + static_cast< SCCOL >( GetDocSize( tdCol, nCol ) ) - 1;
+ for( SCROW nRow = 0; nRow <= nLastRow; ++nRow )
+ {
+ SvxBorderLine* pTopLine = (nRow == 0) ? &aOuterLine : &aInnerLine;
+ SvxBorderLine* pBottomLine = (nRow == nLastRow) ? &aOuterLine : &aInnerLine;
+ SCROW nCellRow1 = GetDocPos( tdRow, nRow ) + rFirstPos.Row();
+ SCROW nCellRow2 = nCellRow1 + GetDocSize( tdRow, nRow ) - 1;
+ for( SCCOL nCellCol = nCellCol1; nCellCol <= nCellCol2; ++nCellCol )
+ {
+ aBorderItem.SetLine( (nCellCol == nCellCol1) ? pLeftLine : nullptr, SvxBoxItemLine::LEFT );
+ aBorderItem.SetLine( (nCellCol == nCellCol2) ? pRightLine : nullptr, SvxBoxItemLine::RIGHT );
+ for( SCROW nCellRow = nCellRow1; nCellRow <= nCellRow2; ++nCellRow )
+ {
+ aBorderItem.SetLine( (nCellRow == nCellRow1) ? pTopLine : nullptr, SvxBoxItemLine::TOP );
+ aBorderItem.SetLine( (nCellRow == nCellRow2) ? pBottomLine : nullptr, SvxBoxItemLine::BOTTOM );
+ pDoc->ApplyAttr( nCellCol, nCellRow, rFirstPos.Tab(), aBorderItem );
+ }
+ }
+ }
+ }
+ }
+
+ for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
+ aIter->ApplyCellBorders( pDoc, rFirstPos );
+}
+
+SvNumberFormatter* ScHTMLTable::GetFormatTable()
+{
+ return mpParser->GetDoc().GetFormatTable();
+}
+
+bool ScHTMLTable::IsEmptyCell() const
+{
+ return mpCurrEntryVector && mpCurrEntryVector->empty();
+}
+
+bool ScHTMLTable::IsSpaceCharInfo( const HtmlImportInfo& rInfo )
+{
+ return (rInfo.nToken == HtmlTokenId::TEXTTOKEN) && (rInfo.aText.getLength() == 1) && (rInfo.aText[ 0 ] == ' ');
+}
+
+ScHTMLTable::ScHTMLEntryPtr ScHTMLTable::CreateEntry() const
+{
+ return std::make_unique<ScHTMLEntry>( GetCurrItemSet() );
+}
+
+void ScHTMLTable::CreateNewEntry( const HtmlImportInfo& rInfo )
+{
+ OSL_ENSURE( !mxCurrEntry, "ScHTMLTable::CreateNewEntry - old entry still present" );
+ mxCurrEntry = CreateEntry();
+ mxCurrEntry->aSel = rInfo.aSelection;
+}
+
+void ScHTMLTable::ImplPushEntryToVector( ScHTMLEntryVector& rEntryVector, ScHTMLEntryPtr& rxEntry )
+{
+ // HTML entry list does not own the entries
+ rEntryVector.push_back( rxEntry.get() );
+ // mrEEParseList (reference to member of ScEEParser) owns the entries
+ mrEEParseList.push_back(std::shared_ptr<ScEEParseEntry>(rxEntry.release()));
+}
+
+bool ScHTMLTable::PushEntry( ScHTMLEntryPtr& rxEntry )
+{
+ bool bPushed = false;
+ if( rxEntry && rxEntry->HasContents() )
+ {
+ if( mpCurrEntryVector )
+ {
+ if( mbPushEmptyLine )
+ {
+ ScHTMLEntryPtr xEmptyEntry = CreateEntry();
+ ImplPushEntryToVector( *mpCurrEntryVector, xEmptyEntry );
+ mbPushEmptyLine = false;
+ }
+ ImplPushEntryToVector( *mpCurrEntryVector, rxEntry );
+ bPushed = true;
+ }
+ else if( mpParentTable )
+ {
+ bPushed = mpParentTable->PushEntry( rxEntry );
+ }
+ else
+ {
+ OSL_FAIL( "ScHTMLTable::PushEntry - cannot push entry, no parent found" );
+ }
+ }
+ return bPushed;
+}
+
+bool ScHTMLTable::PushEntry( const HtmlImportInfo& rInfo, bool bLastInCell )
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PushEntry - no current entry" );
+ bool bPushed = false;
+ if( mxCurrEntry )
+ {
+ mxCurrEntry->AdjustEnd( rInfo );
+ mxCurrEntry->Strip( mrEditEngine );
+
+ // import entry always, if it is the last in cell, and cell is still empty
+ if( bLastInCell && IsEmptyCell() )
+ {
+ mxCurrEntry->SetImportAlways();
+ // don't insert empty lines before single empty entries
+ if( mxCurrEntry->IsEmpty() )
+ mbPushEmptyLine = false;
+ }
+
+ bPushed = PushEntry( mxCurrEntry );
+ mxCurrEntry.reset();
+ }
+ return bPushed;
+}
+
+void ScHTMLTable::PushTableEntry( ScHTMLTableId nTableId )
+{
+ OSL_ENSURE( nTableId != SC_HTML_GLOBAL_TABLE, "ScHTMLTable::PushTableEntry - cannot push global table" );
+ if( nTableId != SC_HTML_GLOBAL_TABLE )
+ {
+ ScHTMLEntryPtr xEntry( new ScHTMLEntry( maTableItemSet, nTableId ) );
+ PushEntry( xEntry );
+ }
+}
+
+ScHTMLTable* ScHTMLTable::GetExistingTable( ScHTMLTableId nTableId ) const
+{
+ ScHTMLTable* pTable = ((nTableId != SC_HTML_GLOBAL_TABLE) && mxNestedTables) ?
+ mxNestedTables->FindTable( nTableId, false ) : nullptr;
+ OSL_ENSURE( pTable || (nTableId == SC_HTML_GLOBAL_TABLE), "ScHTMLTable::GetExistingTable - table not found" );
+ return pTable;
+}
+
+ScHTMLTable* ScHTMLTable::InsertNestedTable( const HtmlImportInfo& rInfo, bool bPreFormText )
+{
+ if( !mxNestedTables )
+ mxNestedTables.reset( new ScHTMLTableMap( *this ) );
+ if( bPreFormText ) // enclose new preformatted table with empty lines
+ InsertLeadingEmptyLine();
+ return mxNestedTables->CreateTable( rInfo, bPreFormText, mrDoc );
+}
+
+void ScHTMLTable::InsertNewCell( const ScHTMLSize& rSpanSize )
+{
+ ScRange* pRange;
+
+ /* Find an unused cell by skipping all merged ranges that cover the
+ current cell position stored in maCurrCell. */
+ for (;;)
+ {
+ pRange = maVMergedCells.Find( maCurrCell.MakeAddr() );
+ if (!pRange)
+ pRange = maHMergedCells.Find( maCurrCell.MakeAddr() );
+ if (!pRange)
+ break;
+ maCurrCell.mnCol = pRange->aEnd.Col() + 1;
+ }
+ mpCurrEntryVector = &maEntryMap[ maCurrCell ];
+
+ /* If the new cell is merged horizontally, try to find collisions with
+ other vertically merged ranges. In this case, shrink existing
+ vertically merged ranges (do not shrink the new cell). */
+ SCCOL nColEnd = maCurrCell.mnCol + rSpanSize.mnCols;
+ for( ScAddress aAddr( maCurrCell.MakeAddr() ); aAddr.Col() < nColEnd; aAddr.IncCol() )
+ if( (pRange = maVMergedCells.Find( aAddr )) != nullptr )
+ pRange->aEnd.SetRow( maCurrCell.mnRow - 1 );
+
+ // insert the new range into the cell lists
+ ScRange aNewRange( maCurrCell.MakeAddr() );
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aNewRange.aEnd.Move( rSpanSize.mnCols - 1, rSpanSize.mnRows - 1, 0, aErrorPos, mrDoc ))
+ {
+ assert(!"can't move");
+ }
+ if( rSpanSize.mnRows > 1 )
+ {
+ maVMergedCells.push_back( aNewRange );
+ /* Do not insert vertically merged ranges into maUsedCells yet,
+ because they may be shrunken (see above). The final vertically
+ merged ranges are inserted in FillEmptyCells(). */
+ }
+ else
+ {
+ if( rSpanSize.mnCols > 1 )
+ maHMergedCells.push_back( aNewRange );
+ /* Insert horizontally merged ranges and single cells into
+ maUsedCells, they will not be changed anymore. */
+ maUsedCells.Join( aNewRange );
+ }
+
+ // adjust table size
+ maSize.mnCols = std::max< SCCOL >( maSize.mnCols, aNewRange.aEnd.Col() + 1 );
+ maSize.mnRows = std::max< SCROW >( maSize.mnRows, aNewRange.aEnd.Row() + 1 );
+}
+
+void ScHTMLTable::ImplRowOn()
+{
+ if( mbRowOn )
+ ImplRowOff();
+ moRowItemSet.emplace( maTableItemSet );
+ maCurrCell.mnCol = 0;
+ mbRowOn = true;
+ mbDataOn = false;
+}
+
+void ScHTMLTable::ImplRowOff()
+{
+ if( mbDataOn )
+ ImplDataOff();
+ if( mbRowOn )
+ {
+ moRowItemSet.reset();
+ ++maCurrCell.mnRow;
+ mbRowOn = mbDataOn = false;
+ }
+}
+
+void ScHTMLTable::ImplDataOn( const ScHTMLSize& rSpanSize )
+{
+ if( mbDataOn )
+ ImplDataOff();
+ if( !mbRowOn )
+ ImplRowOn();
+ moDataItemSet.emplace( *moRowItemSet );
+ InsertNewCell( rSpanSize );
+ mbDataOn = true;
+ mbPushEmptyLine = false;
+}
+
+void ScHTMLTable::ImplDataOff()
+{
+ if( mbDataOn )
+ {
+ moDataItemSet.reset();
+ ++maCurrCell.mnCol;
+ mpCurrEntryVector = nullptr;
+ mbDataOn = false;
+ }
+}
+
+void ScHTMLTable::ProcessFormatOptions( SfxItemSet& rItemSet, const HtmlImportInfo& rInfo )
+{
+ // special handling for table header cells
+ if( rInfo.nToken == HtmlTokenId::TABLEHEADER_ON )
+ {
+ rItemSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ rItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
+ }
+
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ for (const auto& rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ALIGN:
+ {
+ SvxCellHorJustify eVal = SvxCellHorJustify::Standard;
+ const OUString& rOptVal = rOption.GetString();
+ if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
+ eVal = SvxCellHorJustify::Right;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
+ eVal = SvxCellHorJustify::Center;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
+ eVal = SvxCellHorJustify::Left;
+ if( eVal != SvxCellHorJustify::Standard )
+ rItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY ) );
+ }
+ break;
+
+ case HtmlOptionId::VALIGN:
+ {
+ SvxCellVerJustify eVal = SvxCellVerJustify::Standard;
+ const OUString& rOptVal = rOption.GetString();
+ if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
+ eVal = SvxCellVerJustify::Top;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
+ eVal = SvxCellVerJustify::Center;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
+ eVal = SvxCellVerJustify::Bottom;
+ if( eVal != SvxCellVerJustify::Standard )
+ rItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY ) );
+ }
+ break;
+
+ case HtmlOptionId::BGCOLOR:
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ rItemSet.Put( SvxBrushItem( aColor, ATTR_BACKGROUND ) );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void ScHTMLTable::SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize )
+{
+ OSL_ENSURE( nCellPos >= 0, "ScHTMLTable::SetDocSize - unexpected negative position" );
+ ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ size_t nIndex = static_cast< size_t >( nCellPos );
+ // expand with height/width == 1
+ while( nIndex >= rSizes.size() )
+ rSizes.push_back( rSizes.empty() ? 1 : (rSizes.back() + 1) );
+ // update size of passed position and all following
+ // #i109987# only grow, don't shrink - use the largest needed size
+ SCCOLROW nDiff = nSize - ((nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]));
+ if( nDiff > 0 )
+ std::for_each(rSizes.begin() + nIndex, rSizes.end(), [&nDiff](SCCOLROW& rSize) { rSize += nDiff; });
+}
+
+void ScHTMLTable::CalcNeededDocSize(
+ ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nCellSpan, SCCOLROW nRealDocSize )
+{
+ SCCOLROW nDiffSize = 0;
+ // in merged columns/rows: reduce needed size by size of leading columns
+ while( nCellSpan > 1 )
+ {
+ nDiffSize += GetDocSize( eOrient, nCellPos );
+ --nCellSpan;
+ ++nCellPos;
+ }
+ // set remaining needed size to last column/row
+ nRealDocSize -= std::min< SCCOLROW >( nRealDocSize - 1, nDiffSize );
+ SetDocSize( eOrient, nCellPos, nRealDocSize );
+}
+
+void ScHTMLTable::FillEmptyCells()
+{
+ for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
+ aIter->FillEmptyCells();
+
+ // insert the final vertically merged ranges into maUsedCells
+ for ( size_t i = 0, nRanges = maVMergedCells.size(); i < nRanges; ++i )
+ {
+ ScRange & rRange = maVMergedCells[ i ];
+ maUsedCells.Join( rRange );
+ }
+
+ for( ScAddress aAddr; aAddr.Row() < maSize.mnRows; aAddr.IncRow() )
+ {
+ for( aAddr.SetCol( 0 ); aAddr.Col() < maSize.mnCols; aAddr.IncCol() )
+ {
+ if( !maUsedCells.Find( aAddr ) )
+ {
+ // create a range for the lock list (used to calc. cell span)
+ ScRange aRange( aAddr );
+ do
+ {
+ aRange.aEnd.IncCol();
+ }
+ while( (aRange.aEnd.Col() < maSize.mnCols) && !maUsedCells.Find( aRange.aEnd ) );
+ aRange.aEnd.IncCol( -1 );
+ maUsedCells.Join( aRange );
+
+ // insert a dummy entry
+ ScHTMLEntryPtr xEntry = CreateEntry();
+ ImplPushEntryToVector( maEntryMap[ ScHTMLPos( aAddr ) ], xEntry );
+ }
+ }
+ }
+}
+
+void ScHTMLTable::RecalcDocSize()
+{
+ // recalc table sizes recursively from inner to outer
+ for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
+ aIter->RecalcDocSize();
+
+ /* Two passes: first calculates the sizes of single columns/rows, then
+ the sizes of spanned columns/rows. This allows to fill nested tables
+ into merged cells optimally. */
+ static const sal_uInt16 PASS_SINGLE = 0;
+ static const sal_uInt16 PASS_SPANNED = 1;
+ for( sal_uInt16 nPass = PASS_SINGLE; nPass <= PASS_SPANNED; ++nPass )
+ {
+ // iterate through every table cell
+ for( const auto& [rCellPos, rEntryVector] : maEntryMap )
+ {
+ ScHTMLSize aCellSpan = GetSpan( rCellPos );
+
+ // process the dimension of the current cell in this pass?
+ // (pass is single and span is 1) or (pass is not single and span is not 1)
+ bool bProcessColWidth = ((nPass == PASS_SINGLE) == (aCellSpan.mnCols == 1));
+ bool bProcessRowHeight = ((nPass == PASS_SINGLE) == (aCellSpan.mnRows == 1));
+ if( bProcessColWidth || bProcessRowHeight )
+ {
+ ScHTMLSize aDocSize( 1, 0 ); // resulting size of the cell in document
+
+ // expand the cell size for each cell parse entry
+ for( const auto& rpEntry : rEntryVector )
+ {
+ ScHTMLTable* pTable = GetExistingTable( rpEntry->GetTableId() );
+ // find entry with maximum width
+ if( bProcessColWidth && pTable )
+ aDocSize.mnCols = std::max( aDocSize.mnCols, static_cast< SCCOL >( pTable->GetDocSize( tdCol ) ) );
+ // add up height of each entry
+ if( bProcessRowHeight )
+ aDocSize.mnRows += pTable ? pTable->GetDocSize( tdRow ) : 1;
+ }
+ if( !aDocSize.mnRows )
+ aDocSize.mnRows = 1;
+
+ if( bProcessColWidth )
+ CalcNeededDocSize( tdCol, rCellPos.mnCol, aCellSpan.mnCols, aDocSize.mnCols );
+ if( bProcessRowHeight )
+ CalcNeededDocSize( tdRow, rCellPos.mnRow, aCellSpan.mnRows, aDocSize.mnRows );
+ }
+ }
+ }
+}
+
+void ScHTMLTable::RecalcDocPos( const ScHTMLPos& rBasePos )
+{
+ maDocBasePos = rBasePos;
+ // after the previous assignment it is allowed to call GetDocPos() methods
+
+ // iterate through every table cell
+ for( auto& [rCellPos, rEntryVector] : maEntryMap )
+ {
+ // fixed doc position of the entire cell (first entry)
+ const ScHTMLPos aCellDocPos( GetDocPos( rCellPos ) );
+ // fixed doc size of the entire cell
+ const ScHTMLSize aCellDocSize( GetDocSize( rCellPos ) );
+
+ // running doc position for single entries
+ ScHTMLPos aEntryDocPos( aCellDocPos );
+
+ ScHTMLEntry* pEntry = nullptr;
+ for( const auto& rpEntry : rEntryVector )
+ {
+ pEntry = rpEntry;
+ if( ScHTMLTable* pTable = GetExistingTable( pEntry->GetTableId() ) )
+ {
+ pTable->RecalcDocPos( aEntryDocPos ); // recalc nested table
+ pEntry->nCol = SCCOL_MAX;
+ pEntry->nRow = SCROW_MAX;
+ SCROW nTableRows = static_cast< SCROW >( pTable->GetDocSize( tdRow ) );
+
+ // use this entry to pad empty space right of table
+ if( mpParentTable ) // ... but not in global table
+ {
+ SCCOL nStartCol = aEntryDocPos.mnCol + static_cast< SCCOL >( pTable->GetDocSize( tdCol ) );
+ SCCOL nNextCol = aEntryDocPos.mnCol + aCellDocSize.mnCols;
+ if( nStartCol < nNextCol )
+ {
+ pEntry->nCol = nStartCol;
+ pEntry->nRow = aEntryDocPos.mnRow;
+ pEntry->nColOverlap = nNextCol - nStartCol;
+ pEntry->nRowOverlap = nTableRows;
+ }
+ }
+ aEntryDocPos.mnRow += nTableRows;
+ }
+ else
+ {
+ pEntry->nCol = aEntryDocPos.mnCol;
+ pEntry->nRow = aEntryDocPos.mnRow;
+ if( mpParentTable ) // do not merge in global table
+ pEntry->nColOverlap = aCellDocSize.mnCols;
+ ++aEntryDocPos.mnRow;
+ }
+ }
+
+ // pEntry points now to last entry.
+ if( pEntry )
+ {
+ if( (pEntry == rEntryVector.front()) && (pEntry->GetTableId() == SC_HTML_NO_TABLE) )
+ {
+ // pEntry is the only entry in this cell - merge rows of cell with single non-table entry.
+ pEntry->nRowOverlap = aCellDocSize.mnRows;
+ }
+ else
+ {
+ // fill up incomplete entry lists
+ SCROW nFirstUnusedRow = aCellDocPos.mnRow + aCellDocSize.mnRows;
+ while( aEntryDocPos.mnRow < nFirstUnusedRow )
+ {
+ ScHTMLEntryPtr xDummyEntry( new ScHTMLEntry( pEntry->GetItemSet() ) );
+ xDummyEntry->nCol = aEntryDocPos.mnCol;
+ xDummyEntry->nRow = aEntryDocPos.mnRow;
+ xDummyEntry->nColOverlap = aCellDocSize.mnCols;
+ ImplPushEntryToVector( rEntryVector, xDummyEntry );
+ ++aEntryDocPos.mnRow;
+ }
+ }
+ }
+ }
+}
+
+ScHTMLGlobalTable::ScHTMLGlobalTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseVector,
+ ScHTMLTableId& rnUnusedId,
+ ScHTMLParser* pParser,
+ const ScDocument& rDoc
+) :
+ ScHTMLTable( rPool, rEditEngine, rEEParseVector, rnUnusedId, pParser, rDoc )
+{
+}
+
+ScHTMLGlobalTable::~ScHTMLGlobalTable()
+{
+}
+
+void ScHTMLGlobalTable::Recalc()
+{
+ // Fills up empty cells with a dummy entry. */
+ FillEmptyCells();
+ // recalc table sizes of all nested tables and this table
+ RecalcDocSize();
+ // recalc document positions of all entries in this table and in nested tables
+ RecalcDocPos( GetDocPos() );
+}
+
+ScHTMLQueryParser::ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
+ ScHTMLParser( pEditEngine, pDoc ),
+ mnUnusedId( SC_HTML_GLOBAL_TABLE ),
+ mbTitleOn( false )
+{
+ mxGlobTable.reset(
+ new ScHTMLGlobalTable(*pPool, *pEdit, maList, mnUnusedId, this, *pDoc));
+ mpCurrTable = mxGlobTable.get();
+}
+
+ScHTMLQueryParser::~ScHTMLQueryParser()
+{
+}
+
+ErrCode ScHTMLQueryParser::Read( SvStream& rStrm, const OUString& rBaseURL )
+{
+ SvKeyValueIteratorRef xValues;
+ SvKeyValueIterator* pAttributes = nullptr;
+
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if( pObjSh && pObjSh->IsLoading() )
+ {
+ pAttributes = pObjSh->GetHeaderAttributes();
+ }
+ else
+ {
+ /* When not loading, set up fake HTTP headers to force the SfxHTMLParser
+ to use UTF8 (used when pasting from clipboard) */
+ const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
+ if( pCharSet )
+ {
+ OUString aContentType = "text/html; charset=" +
+ OUString::createFromAscii( pCharSet );
+
+ xValues = new SvKeyValueIterator;
+ xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
+ pAttributes = xValues.get();
+ }
+ }
+
+ Link<HtmlImportInfo&,void> aOldLink = pEdit->GetHtmlImportHdl();
+ pEdit->SetHtmlImportHdl( LINK( this, ScHTMLQueryParser, HTMLImportHdl ) );
+ ErrCode nErr = pEdit->Read( rStrm, rBaseURL, EETextFormat::Html, pAttributes );
+ pEdit->SetHtmlImportHdl( aOldLink );
+
+ mxGlobTable->Recalc();
+ nColMax = static_cast< SCCOL >( mxGlobTable->GetDocSize( tdCol ) - 1 );
+ nRowMax = static_cast< SCROW >( mxGlobTable->GetDocSize( tdRow ) - 1 );
+
+ return nErr;
+}
+
+const ScHTMLTable* ScHTMLQueryParser::GetGlobalTable() const
+{
+ return mxGlobTable.get();
+}
+
+void ScHTMLQueryParser::ProcessToken( const HtmlImportInfo& rInfo )
+{
+ switch( rInfo.nToken )
+ {
+// --- meta data ---
+ case HtmlTokenId::META: MetaOn( rInfo ); break; // <meta>
+
+// --- title handling ---
+ case HtmlTokenId::TITLE_ON: TitleOn(); break; // <title>
+ case HtmlTokenId::TITLE_OFF: TitleOff( rInfo ); break; // </title>
+
+ case HtmlTokenId::STYLE_ON: break;
+ case HtmlTokenId::STYLE_OFF: ParseStyle(rInfo.aText); break;
+
+// --- body handling ---
+ case HtmlTokenId::BODY_ON: mpCurrTable->BodyOn( rInfo ); break; // <body>
+ case HtmlTokenId::BODY_OFF: mpCurrTable->BodyOff( rInfo ); break; // </body>
+
+// --- insert text ---
+ case HtmlTokenId::TEXTTOKEN: InsertText( rInfo ); break; // any text
+ case HtmlTokenId::LINEBREAK: mpCurrTable->BreakOn(); break; // <br>
+ case HtmlTokenId::HEAD1_ON: // <h1>
+ case HtmlTokenId::HEAD2_ON: // <h2>
+ case HtmlTokenId::HEAD3_ON: // <h3>
+ case HtmlTokenId::HEAD4_ON: // <h4>
+ case HtmlTokenId::HEAD5_ON: // <h5>
+ case HtmlTokenId::HEAD6_ON: // <h6>
+ case HtmlTokenId::PARABREAK_ON: mpCurrTable->HeadingOn(); break; // <p>
+
+// --- misc. contents ---
+ case HtmlTokenId::ANCHOR_ON: mpCurrTable->AnchorOn(); break; // <a>
+
+// --- table handling ---
+ case HtmlTokenId::TABLE_ON: TableOn( rInfo ); break; // <table>
+ case HtmlTokenId::TABLE_OFF: TableOff( rInfo ); break; // </table>
+ case HtmlTokenId::CAPTION_ON: mpCurrTable->CaptionOn(); break; // <caption>
+ case HtmlTokenId::CAPTION_OFF: mpCurrTable->CaptionOff(); break; // </caption>
+ case HtmlTokenId::TABLEROW_ON: mpCurrTable->RowOn( rInfo ); break; // <tr>
+ case HtmlTokenId::TABLEROW_OFF: mpCurrTable->RowOff( rInfo ); break; // </tr>
+ case HtmlTokenId::TABLEHEADER_ON: // <th>
+ case HtmlTokenId::TABLEDATA_ON: mpCurrTable->DataOn( rInfo ); break; // <td>
+ case HtmlTokenId::TABLEHEADER_OFF: // </th>
+ case HtmlTokenId::TABLEDATA_OFF: mpCurrTable->DataOff( rInfo ); break; // </td>
+ case HtmlTokenId::PREFORMTXT_ON: PreOn( rInfo ); break; // <pre>
+ case HtmlTokenId::PREFORMTXT_OFF: PreOff( rInfo ); break; // </pre>
+
+// --- formatting ---
+ case HtmlTokenId::FONT_ON: FontOn( rInfo ); break; // <font>
+
+ case HtmlTokenId::BIGPRINT_ON: // <big>
+ //! TODO: store current font size, use following size
+ mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 3 ], 100, ATTR_FONT_HEIGHT ) );
+ break;
+ case HtmlTokenId::SMALLPRINT_ON: // <small>
+ //! TODO: store current font size, use preceding size
+ mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 0 ], 100, ATTR_FONT_HEIGHT ) );
+ break;
+
+ case HtmlTokenId::BOLD_ON: // <b>
+ case HtmlTokenId::STRONG_ON: // <strong>
+ mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ break;
+
+ case HtmlTokenId::ITALIC_ON: // <i>
+ case HtmlTokenId::EMPHASIS_ON: // <em>
+ case HtmlTokenId::ADDRESS_ON: // <address>
+ case HtmlTokenId::BLOCKQUOTE_ON: // <blockquote>
+ case HtmlTokenId::BLOCKQUOTE30_ON: // <bq>
+ case HtmlTokenId::CITATION_ON: // <cite>
+ case HtmlTokenId::VARIABLE_ON: // <var>
+ mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ break;
+
+ case HtmlTokenId::DEFINSTANCE_ON: // <dfn>
+ mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ break;
+
+ case HtmlTokenId::UNDERLINE_ON: // <u>
+ mpCurrTable->PutItem( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
+ break;
+ default: break;
+ }
+}
+
+void ScHTMLQueryParser::InsertText( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable->PutText( rInfo );
+ if( mbTitleOn )
+ maTitle.append(rInfo.aText);
+}
+
+void ScHTMLQueryParser::FontOn( const HtmlImportInfo& rInfo )
+{
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ for (const auto& rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::FACE :
+ {
+ const OUString& rFace = rOption.GetString();
+ OUString aFontName;
+ sal_Int32 nPos = 0;
+ while( nPos != -1 )
+ {
+ // font list separator: VCL = ';' HTML = ','
+ std::u16string_view aFName = comphelper::string::strip(o3tl::getToken(rFace, 0, ',', nPos), ' ');
+ aFontName = ScGlobal::addToken(aFontName, aFName, ';');
+ }
+ if ( !aFontName.isEmpty() )
+ mpCurrTable->PutItem( SvxFontItem( FAMILY_DONTKNOW,
+ aFontName, OUString(), PITCH_DONTKNOW,
+ RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
+ }
+ break;
+ case HtmlOptionId::SIZE :
+ {
+ sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( rOption.GetNumber(), 1, SC_HTML_FONTSIZES );
+ mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ nSize - 1 ], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlOptionId::COLOR :
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ mpCurrTable->PutItem( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void ScHTMLQueryParser::MetaOn( const HtmlImportInfo& rInfo )
+{
+ if( mpDoc->GetDocumentShell() )
+ {
+ HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<cppu::OWeakObject*>(mpDoc->GetDocumentShell()->GetModel()), uno::UNO_QUERY_THROW);
+ pParser->ParseMetaOptions(
+ xDPS->getDocumentProperties(),
+ mpDoc->GetDocumentShell()->GetHeaderAttributes() );
+ }
+}
+
+void ScHTMLQueryParser::TitleOn()
+{
+ mbTitleOn = true;
+ maTitle.setLength(0);
+}
+
+void ScHTMLQueryParser::TitleOff( const HtmlImportInfo& rInfo )
+{
+ if( !mbTitleOn )
+ return;
+
+ OUString aTitle = maTitle.makeStringAndClear().trim();
+ if (!aTitle.isEmpty() && mpDoc->GetDocumentShell())
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<cppu::OWeakObject*>(mpDoc->GetDocumentShell()->GetModel()), uno::UNO_QUERY_THROW);
+
+ xDPS->getDocumentProperties()->setTitle(aTitle);
+ }
+ InsertText( rInfo );
+ mbTitleOn = false;
+}
+
+void ScHTMLQueryParser::TableOn( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->TableOn( rInfo );
+}
+
+void ScHTMLQueryParser::TableOff( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->TableOff( rInfo );
+}
+
+void ScHTMLQueryParser::PreOn( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->PreOn( rInfo );
+}
+
+void ScHTMLQueryParser::PreOff( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->PreOff( rInfo );
+}
+
+void ScHTMLQueryParser::CloseTable( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->CloseTable( rInfo );
+}
+
+namespace {
+
+/**
+ * Handler class for the CSS parser.
+ */
+class CSSHandler: public orcus::css_handler
+{
+ typedef std::pair<std::string_view, std::string_view> SelectorName; // element : class
+ typedef std::vector<SelectorName> SelectorNames;
+
+ SelectorNames maSelectorNames; // current selector names
+ std::string_view maPropName; // current property name.
+ std::string_view maPropValue; // current property value.
+ ScHTMLStyles& mrStyles;
+
+public:
+ explicit CSSHandler(ScHTMLStyles& rStyles):
+ mrStyles(rStyles)
+ {}
+
+ // selector name not starting with "." or "#" (i.e. element selectors)
+ void simple_selector_type(std::string_view aElem)
+ {
+ std::string_view aClass{}; // class name not given - to be added in the "element global" storage
+ SelectorName aName(aElem, aClass);
+
+ maSelectorNames.push_back(aName);
+ }
+
+ // selector names starting with a "." (i.e. class selector)
+ void simple_selector_class(std::string_view aClass)
+ {
+ std::string_view aElem{}; // no element given - should be added in the "global" storage
+ SelectorName aName(aElem, aClass);
+
+ maSelectorNames.push_back(aName);
+ }
+
+ // TODO: Add other selectors
+
+ void property_name(std::string_view aPropName)
+ {
+ maPropName = aPropName;
+ }
+
+ void value(std::string_view aValue)
+ {
+ maPropValue = aValue;
+ }
+
+ void end_block()
+ {
+ maSelectorNames.clear();
+ }
+
+ void end_property()
+ {
+ for (const auto& rSelName : maSelectorNames)
+ {
+ // Add this property to the collection for each selector.
+ std::string_view aElem = rSelName.first;
+ std::string_view aClass = rSelName.second;
+ OUString aName(maPropName.data(), maPropName.size(), RTL_TEXTENCODING_UTF8);
+ OUString aValue(maPropValue.data(), maPropValue.size(), RTL_TEXTENCODING_UTF8);
+ mrStyles.add(aElem.data(), aElem.size(), aClass.data(), aClass.size(), aName, aValue);
+ }
+ maPropName = std::string_view{};
+ maPropValue = std::string_view{};
+ }
+
+};
+
+}
+
+void ScHTMLQueryParser::ParseStyle(std::u16string_view rStrm)
+{
+ OString aStr = OUStringToOString(rStrm, RTL_TEXTENCODING_UTF8);
+ CSSHandler aHdl(GetStyles());
+ orcus::css_parser<CSSHandler> aParser(aStr, aHdl);
+ try
+ {
+ aParser.parse();
+ }
+ catch (const orcus::parse_error& rOrcusParseError)
+ {
+ SAL_WARN("sc", "ScHTMLQueryParser::ParseStyle: " << rOrcusParseError.what());
+ // TODO: Parsing of CSS failed. Do nothing for now.
+ }
+}
+
+IMPL_LINK( ScHTMLQueryParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
+{
+ switch( rInfo.eState )
+ {
+ case HtmlImportState::Start:
+ break;
+
+ case HtmlImportState::NextToken:
+ ProcessToken( rInfo );
+ break;
+
+ case HtmlImportState::InsertPara:
+ mpCurrTable->InsertPara( rInfo );
+ break;
+
+ case HtmlImportState::SetAttr:
+ case HtmlImportState::InsertText:
+ case HtmlImportState::InsertField:
+ break;
+
+ case HtmlImportState::End:
+ while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
+ CloseTable( rInfo );
+ break;
+
+ default:
+ OSL_FAIL( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/importfilterdata.cxx b/sc/source/filter/importfilterdata.cxx
new file mode 100644
index 0000000000..100188928a
--- /dev/null
+++ b/sc/source/filter/importfilterdata.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <importfilterdata.hxx>
+
+namespace sc {
+
+ImportPostProcessData::DataStream::DataStream() :
+ mbRefreshOnEmpty(false), meInsertPos(InsertBottom) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/SparklineFragment.hxx b/sc/source/filter/inc/SparklineFragment.hxx
new file mode 100644
index 0000000000..0d4e76e6b9
--- /dev/null
+++ b/sc/source/filter/inc/SparklineFragment.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include <oox/core/contexthandler.hxx>
+#include <SparklineGroup.hxx>
+
+#include <vector>
+#include <memory>
+
+namespace oox
+{
+class AttributeList;
+}
+
+namespace oox::xls
+{
+/** Transitional sparkline data */
+class Sparkline
+{
+public:
+ ScRangeList m_aInputRange;
+ ScRangeList m_aTargetRange;
+ Sparkline() {}
+};
+
+/** Transitional sparkline group data */
+class SparklineGroup
+{
+private:
+ std::vector<Sparkline> m_aSparklines;
+
+ std::shared_ptr<sc::SparklineGroup> m_pSparklineGroup;
+
+public:
+ SparklineGroup()
+ : m_pSparklineGroup(new sc::SparklineGroup)
+ {
+ }
+
+ const std::shared_ptr<sc::SparklineGroup>& getSparklineGroup() { return m_pSparklineGroup; }
+
+ std::vector<Sparkline>& getSparklines() { return m_aSparklines; }
+};
+
+/** Handle import of the sparkline, sparkline group and attributes */
+class SparklineGroupsContext : public WorksheetContextBase
+{
+private:
+ std::vector<SparklineGroup> m_aSparklineGroups;
+
+public:
+ explicit SparklineGroupsContext(WorksheetContextBase& rFragment);
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs) override;
+ void onStartElement(const AttributeList& rAttribs) override;
+ void onCharacters(const OUString& rCharacters) override;
+ void onEndElement() override;
+
+ void insertSparkline(SparklineGroup& rSparklineGroup, Sparkline& rSparkline);
+};
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/XclExpChangeTrack.hxx b/sc/source/filter/inc/XclExpChangeTrack.hxx
new file mode 100644
index 0000000000..b23b6f8cdc
--- /dev/null
+++ b/sc/source/filter/inc/XclExpChangeTrack.hxx
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <stack>
+#include <tools/datetime.hxx>
+#include <chgtrack.hxx>
+#include <document.hxx>
+#include "xelink.hxx"
+#include "xestring.hxx"
+#include "excrecds.hxx"
+#include "xlformula.hxx"
+#include "xllink.hxx"
+#include "xeformula.hxx"
+
+class ExcXmlRecord : public ExcRecord
+{
+public:
+ virtual std::size_t GetLen() const override;
+ virtual sal_uInt16 GetNum() const override;
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// XclExpUserBView - one UserBView record for each user
+
+class XclExpUserBView : public ExcRecord
+{
+private:
+ XclExpString sUsername;
+ sal_uInt8 aGUID[ 16 ];
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpUserBView( const OUString& rUsername, const sal_uInt8* pGUID );
+
+ const sal_uInt8* GetGUID() const { return aGUID; }
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpUserBViewList - list of UserBView records
+
+class XclExpUserBViewList : public ExcEmptyRec
+{
+private:
+ std::vector<XclExpUserBView> aViews;
+
+public:
+
+ typedef std::vector<XclExpUserBView>::const_iterator const_iterator;
+
+ XclExpUserBViewList( const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpUserBViewList() override;
+
+ const_iterator cbegin () { return aViews.cbegin(); }
+ const_iterator cend () { return aViews.cend(); }
+
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// XclExpUsersViewBegin - begin of view block (one per sheet)
+
+class XclExpUsersViewBegin : public ExcRecord
+{
+private:
+ sal_uInt8 aGUID[ 16 ];
+ sal_uInt32 nCurrTab;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpUsersViewBegin( const sal_uInt8* pGUID, sal_uInt32 nTab );
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpUsersViewEnd - end of view block (one per sheet)
+
+class XclExpUsersViewEnd : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0191 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0198 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0192 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0197 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record without content
+
+class XclExpChTrEmpty : public ExcRecord
+{
+private:
+ sal_uInt16 nRecNum;
+
+public:
+ XclExpChTrEmpty( sal_uInt16 nNum ) : nRecNum( nNum ) {}
+ virtual ~XclExpChTrEmpty() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "Revision Log" stream
+
+class XclExpChTr0x0195 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual ~XclExpChTr0x0195() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "Revision Log" stream
+
+class XclExpChTr0x0194 : public ExcRecord
+{
+private:
+ XclExpString sUsername;
+ DateTime aDateTime;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ inline XclExpChTr0x0194( const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpChTr0x0194() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+inline XclExpChTr0x0194::XclExpChTr0x0194( const ScChangeTrack& rChangeTrack ) :
+ sUsername( rChangeTrack.GetUser() ),
+ aDateTime( rChangeTrack.GetFixDateTime() )
+{
+}
+
+// XclExpChTrHeader - header record, includes action count
+
+class XclExpChTrHeader : public ExcRecord
+{
+private:
+ sal_uInt8 aGUID[ 16 ];
+ sal_uInt32 nCount;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpChTrHeader() : nCount( 0 ) {}
+ virtual ~XclExpChTrHeader() override;
+
+ void SetGUID( const sal_uInt8* pGUID ) { memcpy( aGUID, pGUID, 16 ); }
+ void SetCount( sal_uInt32 nNew ) { nCount = nNew; }
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlChTrHeaders : public ExcXmlRecord
+{
+ sal_uInt8 maGUID[16];
+public:
+ void SetGUID( const sal_uInt8* pGUID );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpChTrTabIdBuffer;
+class XclExpChTrAction;
+
+class XclExpXmlChTrHeader : public ExcXmlRecord
+{
+ OUString maUserName;
+ DateTime maDateTime;
+ sal_uInt8 maGUID[16];
+ sal_Int32 mnLogNumber;
+ sal_uInt32 mnMinAction;
+ sal_uInt32 mnMaxAction;
+
+ std::vector<sal_uInt16> maTabBuffer;
+ std::vector<std::unique_ptr<XclExpChTrAction>> maActions;
+
+public:
+ XclExpXmlChTrHeader(
+ OUString aUserName, const DateTime& rDateTime, const sal_uInt8* pGUID,
+ sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void AppendAction( std::unique_ptr<XclExpChTrAction> pAction );
+};
+
+// XclExpChTrInfo - header of action group of a user
+
+class XclExpChTrInfo : public ExcRecord
+{
+private:
+ XclExpString sUsername;
+ DateTime aDateTime;
+ sal_uInt8 aGUID[ 16 ];
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime,
+ const sal_uInt8* pGUID );
+
+ virtual ~XclExpChTrInfo() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpChTrTabIdBuffer - buffer for tab id's
+
+class XclExpChTrTabIdBuffer
+{
+private:
+ std::unique_ptr<sal_uInt16[]>
+ pBuffer;
+ sal_uInt16* pLast;
+ sal_uInt16 nBufSize;
+ sal_uInt16 nLastId;
+
+public:
+ XclExpChTrTabIdBuffer( sal_uInt16 nCount );
+ XclExpChTrTabIdBuffer( const XclExpChTrTabIdBuffer& rCopy );
+ ~XclExpChTrTabIdBuffer();
+
+ void InitFill( sal_uInt16 nIndex );
+ void InitFillup();
+
+ sal_uInt16 GetId( sal_uInt16 nIndex ) const;
+ bool HasId( sal_uInt16 nIndex ) const;
+ void Remove();
+
+ sal_uInt16 GetBufferCount() const
+ { return static_cast< sal_uInt16 >( (pLast - pBuffer.get()) + 1 ); }
+ void GetBufferCopy( sal_uInt16* pDest ) const
+ { memcpy( pDest, pBuffer.get(), sizeof(sal_uInt16) * GetBufferCount() ); }
+};
+
+// XclExpChTrTabId - tab id record
+
+class XclExpChTrTabId : public ExcRecord
+{
+private:
+ std::unique_ptr<sal_uInt16[]> pBuffer;
+ sal_uInt16 nTabCount;
+
+ void Clear() { pBuffer.reset(); }
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpChTrTabId( sal_uInt16 nCount ) : nTabCount( nCount ) {}
+ XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer );
+ virtual ~XclExpChTrTabId() override;
+
+ void Copy( const XclExpChTrTabIdBuffer& rBuffer );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpChTrAction - base class for action records
+
+class XclExpChTrAction : public ExcRecord
+{
+private:
+ OUString sUsername;
+ DateTime aDateTime;
+ sal_uInt32 nIndex; // action number
+ std::unique_ptr<XclExpChTrAction>
+ pAddAction; // additional record for this action
+ bool bAccepted;
+
+protected:
+ const XclExpTabInfo& rTabInfo; // for table num export (sc num -> xcl num)
+ const XclExpChTrTabIdBuffer& rIdBuffer; // for table num export (xcl num -> tab id)
+ sal_uInt32 nLength; // this is not the record size
+ sal_uInt16 nOpCode; // EXC_CHTR_OP_***
+ bool bForceInfo;
+
+ XclExpChTrAction( const XclExpChTrAction& rCopy );
+
+ void SetAddAction( XclExpChTrAction* pAction );
+ void AddDependentContents(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const ScChangeTrack& rChangeTrack );
+
+ static inline void Write2DAddress( XclExpStream& rStrm, const ScAddress& rAddress );
+ static inline void Write2DRange( XclExpStream& rStrm, const ScRange& rRange );
+ inline sal_uInt16 GetTabId( SCTAB nTabId ) const;
+ inline bool IsDeletedTab( SCTAB nTab ) const;
+ inline void WriteTabId( XclExpStream& rStrm, SCTAB nTabId ) const;
+
+ // save header data, call SaveActionData()
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+ static std::size_t GetHeaderByteCount() { return 12; }
+
+ // override to save action data without header, called by SaveCont()
+ virtual void SaveActionData( XclExpStream& rStrm ) const = 0;
+ // override to get action size without header, called by GetLen()
+ virtual std::size_t GetActionByteCount() const = 0;
+
+ // true if export would attempt to get the tab id of an unknown tab
+ virtual bool UsesDeletedTab() const = 0;
+
+ // do something before writing the record
+ virtual void PrepareSaveAction( XclExpStream& rStrm ) const;
+ // do something after writing the record
+ virtual void CompleteSaveAction( XclExpStream& rStrm ) const;
+
+ bool GetAccepted() const { return bAccepted; }
+
+public:
+ XclExpChTrAction(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ sal_uInt16 nNewOpCode = EXC_CHTR_OP_UNKNOWN );
+ virtual ~XclExpChTrAction() override;
+
+ const OUString& GetUsername() const { return sUsername; }
+ const DateTime& GetDateTime() const { return aDateTime; }
+ const XclExpChTrTabIdBuffer& GetTabIdBuffer() const { return rIdBuffer; }
+ bool ForceInfoRecord() const { return bForceInfo; }
+
+ // set own index & return new index
+ // could override to use more indexes per action
+ void SetIndex( sal_uInt32& rIndex );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual std::size_t GetLen() const override;
+
+ XclExpChTrAction* GetAddAction() { return pAddAction.get(); }
+ sal_uInt32 GetActionNumber() const { return nIndex; }
+};
+
+inline void XclExpChTrAction::Write2DAddress( XclExpStream& rStrm, const ScAddress& rAddress )
+{
+ rStrm << static_cast<sal_uInt16>(rAddress.Row())
+ << static_cast<sal_uInt16>(rAddress.Col());
+}
+
+inline void XclExpChTrAction::Write2DRange( XclExpStream& rStrm, const ScRange& rRange )
+{
+ rStrm << static_cast<sal_uInt16>(rRange.aStart.Row())
+ << static_cast<sal_uInt16>(rRange.aEnd.Row())
+ << static_cast<sal_uInt16>(rRange.aStart.Col())
+ << static_cast<sal_uInt16>(rRange.aEnd.Col());
+}
+
+inline bool XclExpChTrAction::IsDeletedTab(SCTAB nTab) const
+{
+ return rTabInfo.GetXclTab(nTab) == EXC_TAB_DELETED;
+}
+
+inline sal_uInt16 XclExpChTrAction::GetTabId( SCTAB nTab ) const
+{
+ return rIdBuffer.GetId( rTabInfo.GetXclTab( nTab ) );
+}
+
+inline void XclExpChTrAction::WriteTabId( XclExpStream& rStrm, SCTAB nTab ) const
+{
+ rStrm << GetTabId( nTab );
+}
+
+// XclExpChTrData - cell content itself
+
+struct XclExpChTrData
+{
+ std::unique_ptr<XclExpString> pString;
+ XclExpStringRef mpFormattedString;
+ const ScFormulaCell* mpFormulaCell;
+ XclTokenArrayRef mxTokArr;
+ XclExpRefLog maRefLog;
+ double fValue;
+ sal_Int32 nRKValue;
+ sal_uInt16 nType;
+ std::size_t nSize;
+
+ XclExpChTrData();
+ ~XclExpChTrData();
+ void Clear();
+
+ void WriteFormula(
+ XclExpStream& rStrm,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+ void Write(
+ XclExpStream& rStrm,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+
+ bool UsesDeletedTab(const XclExpChTrTabIdBuffer& rTabIdBuffer) const;
+};
+
+// XclExpChTrCellContent - changed cell content
+
+class XclExpChTrCellContent final : public XclExpChTrAction, protected XclExpRoot
+{
+ std::unique_ptr<XclExpChTrData> pOldData;
+ std::unique_ptr<XclExpChTrData> pNewData;
+ sal_uInt16 nOldLength; // this is not the record size
+ ScAddress aPosition;
+
+ static void MakeEmptyChTrData( std::unique_ptr<XclExpChTrData>& rpData );
+
+ void GetCellData(
+ const XclExpRoot& rRoot, const ScCellValue& rScCell, std::unique_ptr<XclExpChTrData>& rpData,
+ sal_uInt32& rXclLength1, sal_uInt16& rXclLength2 );
+
+ virtual bool UsesDeletedTab() const override;
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrCellContent(
+ const ScChangeActionContent& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+ virtual ~XclExpChTrCellContent() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTrInsert - insert/delete columns/rows
+
+class XclExpChTrInsert : public XclExpChTrAction
+{
+ bool mbEndOfList;
+
+protected:
+ ScRange aRange;
+
+ XclExpChTrInsert( const XclExpChTrInsert& rCopy );
+
+ virtual bool UsesDeletedTab() const override;
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+ virtual void PrepareSaveAction( XclExpStream& rStrm ) const override;
+ virtual void CompleteSaveAction( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrInsert(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpChTrInsert() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTrInsertTab - insert table
+
+class XclExpChTrInsertTab : public XclExpChTrAction, protected XclExpRoot
+{
+private:
+ SCTAB nTab;
+
+protected:
+
+ virtual bool UsesDeletedTab() const override;
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrInsertTab(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+ virtual ~XclExpChTrInsertTab() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTrMoveRange - move cell range
+
+class XclExpChTrMoveRange final : public XclExpChTrAction
+{
+ ScRange aSourceRange;
+ ScRange aDestRange;
+
+ virtual bool UsesDeletedTab() const override;
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+ virtual void PrepareSaveAction( XclExpStream& rStrm ) const override;
+ virtual void CompleteSaveAction( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrMoveRange(
+ const ScChangeActionMove& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpChTrMoveRange() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTr0x019A - additional data for delete action
+
+class XclExpChTr0x014A : public XclExpChTrInsert
+{
+protected:
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTr0x014A( const XclExpChTrInsert& rAction );
+ virtual ~XclExpChTr0x014A() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChangeTrack - exports the "Revision Log" stream
+
+class XclExpChangeTrack : protected XclExpRoot
+{
+ typedef std::vector<std::unique_ptr<ExcRecord>> RecListType;
+ RecListType maRecList; // list of "Revision Log" stream records
+ std::stack<XclExpChTrAction*> aActionStack;
+ XclExpChTrTabIdBuffer* pTabIdBuffer;
+ std::vector<std::unique_ptr<XclExpChTrTabIdBuffer>>
+ maBuffers;
+
+ ScDocumentUniquePtr xTempDoc; // empty document
+
+ ScChangeTrack* CreateTempChangeTrack();
+ void PushActionRecord( const ScChangeAction& rAction );
+
+ bool WriteUserNamesStream();
+
+public:
+ XclExpChangeTrack( const XclExpRoot& rRoot );
+ virtual ~XclExpChangeTrack() override;
+
+ void Write();
+ void WriteXml( XclExpXmlStream& rStrm );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/XclImpChangeTrack.hxx b/sc/source/filter/inc/XclImpChangeTrack.hxx
new file mode 100644
index 0000000000..532cc9e324
--- /dev/null
+++ b/sc/source/filter/inc/XclImpChangeTrack.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 .
+ */
+
+#pragma once
+
+#include "xiroot.hxx"
+#include "xistream.hxx"
+#include "excform.hxx"
+
+struct ScCellValue;
+class ScChangeAction;
+class ScChangeTrack;
+class DateTime;
+
+struct XclImpChTrRecHeader
+{
+ sal_uInt32 nSize;
+ sal_uInt32 nIndex;
+ sal_uInt16 nOpCode;
+ sal_uInt16 nAccept;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclImpChTrRecHeader& rRecHeader )
+{
+ rRecHeader.nSize = rStrm.ReaduInt32();
+ rRecHeader.nIndex = rStrm.ReaduInt32();
+ rRecHeader.nOpCode = rStrm.ReaduInt16();
+ rRecHeader.nAccept = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+class XclImpChangeTrack : protected XclImpRoot
+{
+private:
+ XclImpChTrRecHeader aRecHeader;
+ OUString sOldUsername;
+
+ std::unique_ptr<ScChangeTrack> pChangeTrack;
+ tools::SvRef<SotStorageStream> xInStrm; // input stream
+ std::unique_ptr<XclImpStream> pStrm; // stream import class
+ sal_uInt16 nTabIdCount;
+ bool bGlobExit; // global exit loop
+
+ enum { nmBase, nmFound, nmNested }
+ eNestedMode; // action with nested content actions
+
+ bool FoundNestedMode() { return eNestedMode == nmFound; }
+
+ void DoAcceptRejectAction( ScChangeAction* pAction );
+ void DoAcceptRejectAction( sal_uInt32 nFirst, sal_uInt32 nLast );
+
+ void DoInsertRange( const ScRange& rRange, bool bEndOfList );
+ void DoDeleteRange( const ScRange& rRange );
+
+ inline sal_uInt8 LookAtuInt8();
+ inline void Read2DAddress( ScAddress& rAddress );
+ inline void Read2DRange( ScRange& rRange );
+ SCTAB ReadTabNum();
+ void ReadDateTime( DateTime& rDateTime );
+
+ bool CheckRecord( sal_uInt16 nOpCode );
+
+ void ReadFormula(
+ std::unique_ptr<ScTokenArray>& rpTokenArray,
+ const ScAddress& rPosition );
+ void ReadCell( ScCellValue& rCell, sal_uInt32& rFormat, sal_uInt16 nFlags, const ScAddress& rPosition );
+
+ void ReadChTrInsert(); // 0x0137
+ void ReadChTrInfo(); // 0x0138
+ void ReadChTrCellContent(); // 0x013B
+ void ReadChTrTabId(); // 0x013D
+ void ReadChTrMoveRange(); // 0x0140
+ void ReadChTrInsertTab(); // 0x014D
+ void InitNestedMode(); // 0x014E, 0x0150
+ void ReadNestedRecords();
+ bool EndNestedMode(); // 0x014F, 0x0151
+
+ void ReadRecords();
+
+public:
+ XclImpChangeTrack( const XclImpRoot& rRoot, const XclImpStream& rBookStrm );
+ virtual ~XclImpChangeTrack() override;
+
+ // reads extended 3D ref info following the formulas, returns sc tab nums
+ // ( called by XclImpChTrFmlConverter::Read3DTabReference() )
+ void Read3DTabRefInfo( SCTAB& rFirstTab, SCTAB& rLastTab, ExcelToSc8::ExternalTabInfo& rExtInfo );
+
+ void Apply();
+};
+
+inline sal_uInt8 XclImpChangeTrack::LookAtuInt8()
+{
+ pStrm->PushPosition();
+ sal_uInt8 nValue;
+ nValue = pStrm->ReaduInt8();
+ pStrm->PopPosition();
+ return nValue;
+}
+
+inline void XclImpChangeTrack::Read2DAddress( ScAddress& rAddress )
+{
+ rAddress.SetRow( static_cast<SCROW>(pStrm->ReaduInt16()) );
+ rAddress.SetCol( static_cast<SCCOL>(pStrm->ReaduInt16()) );
+}
+
+inline void XclImpChangeTrack::Read2DRange( ScRange& rRange )
+{
+ rRange.aStart.SetRow( static_cast<SCROW>(pStrm->ReaduInt16()) );
+ rRange.aEnd.SetRow( static_cast<SCROW>(pStrm->ReaduInt16()) );
+ rRange.aStart.SetCol( static_cast<SCCOL>(pStrm->ReaduInt16()) );
+ rRange.aEnd.SetCol( static_cast<SCCOL>(pStrm->ReaduInt16()) );
+}
+
+// derived class for special 3D ref handling
+
+class XclImpChTrFmlConverter : public ExcelToSc8
+{
+private:
+ XclImpChangeTrack& rChangeTrack;
+
+ virtual bool Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo ) override;
+
+public:
+ XclImpChTrFmlConverter( XclImpRoot& rRoot, XclImpChangeTrack& rXclChTr );
+ virtual ~XclImpChTrFmlConverter() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/addressconverter.hxx b/sc/source/filter/inc/addressconverter.hxx
new file mode 100644
index 0000000000..19c1b001be
--- /dev/null
+++ b/sc/source/filter/inc/addressconverter.hxx
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <rangelst.hxx>
+#include "workbookhelper.hxx"
+#include <com/sun/star/uno/Sequence.h>
+
+namespace com::sun::star::table { struct CellRangeAddress; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox {
+namespace xls {
+
+/** A 2D cell address struct for binary filters. */
+struct BinAddress
+{
+ sal_Int32 mnCol;
+ sal_Int32 mnRow;
+
+ explicit BinAddress() : mnCol( 0 ), mnRow( 0 ) {}
+ explicit BinAddress( sal_Int32 nCol, sal_Int32 nRow ) : mnCol( nCol ), mnRow( nRow ) {}
+ explicit BinAddress( const ScAddress& rAddr ) : mnCol( rAddr.Col() ), mnRow( rAddr.Row() ) {}
+
+ void read( SequenceInputStream& rStrm );
+};
+
+inline bool operator<( const BinAddress& rL, const BinAddress& rR )
+{
+ return (rL.mnCol < rR.mnCol) || ((rL.mnCol == rR.mnCol) && (rL.mnRow < rR.mnRow));
+}
+
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, BinAddress& orPos )
+{
+ orPos.read( rStrm );
+ return rStrm;
+}
+
+/** A 2D cell range address struct for binary filters. */
+struct BinRange
+{
+ BinAddress maFirst;
+ BinAddress maLast;
+
+ void read( SequenceInputStream& rStrm );
+};
+
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, BinRange& orRange )
+{
+ orRange.read( rStrm );
+ return rStrm;
+}
+
+/** A 2D cell range address list for binary filters. */
+class BinRangeList
+{
+public:
+ explicit BinRangeList() : mvRanges() {}
+
+ ::std::vector< BinRange >::const_iterator begin() const { return mvRanges.begin(); }
+ ::std::vector< BinRange >::const_iterator end() const { return mvRanges.end(); }
+
+ void read( SequenceInputStream& rStrm );
+
+private:
+ ::std::vector< BinRange > mvRanges;
+};
+
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, BinRangeList& orRanges )
+{
+ orRanges.read( rStrm );
+ return rStrm;
+}
+
+/** Converter for cell addresses and cell ranges for OOXML and BIFF filters.
+ */
+class AddressConverter final : public WorkbookHelper
+{
+public:
+ explicit AddressConverter( const WorkbookHelper& rHelper );
+
+ /** Tries to parse the passed string for a 2d cell address in A1 notation.
+
+ This function accepts all strings that match the regular expression
+ "[a-zA-Z]{1,6}0*[1-9][0-9]{0,8}" (without quotes), i.e. 1 to 6 letters
+ for the column index (translated to 0-based column indexes from 0 to
+ 321,272,405), and 1 to 9 digits for the 1-based row index (translated
+ to 0-based row indexes from 0 to 999,999,998). The row number part may
+ contain leading zeros, they will be ignored. It is up to the caller to
+ handle cell addresses outside of a specific valid range (e.g. the
+ entire spreadsheet).
+
+ @param ornColumn (out-parameter) Returns the converted column index.
+ @param ornRow (out-parameter) returns the converted row index.
+ @param rString The string containing the cell address.
+ @param nStart Start index of string part in rString to be parsed.
+ @param nLength Length of string part in rString to be parsed.
+
+ @return true = Parsed string was valid, returned values can be used.
+ */
+ static bool parseOoxAddress2d(
+ sal_Int32& ornColumn, sal_Int32& ornRow,
+ std::u16string_view aString,
+ sal_Int32 nStart = 0,
+ sal_Int32 nLength = SAL_MAX_INT32 );
+
+ static bool parseOoxAddress2d(
+ sal_Int32& ornColumn, sal_Int32& ornRow, std::string_view pStr );
+
+ /** Tries to parse the passed string for a 2d cell range in A1 notation.
+
+ This function accepts all strings that match the regular expression
+ "ADDR(:ADDR)?" (without quotes), where ADDR is a cell address accepted
+ by the parseOoxAddress2d() function of this class. It is up to the
+ caller to handle cell ranges outside of a specific valid range (e.g.
+ the entire spreadsheet).
+
+ @param ornStartColumn (out-parameter) Returns the converted start column index.
+ @param ornStartRow (out-parameter) returns the converted start row index.
+ @param ornEndColumn (out-parameter) Returns the converted end column index.
+ @param ornEndRow (out-parameter) returns the converted end row index.
+ @param rString The string containing the cell address.
+ @param nStart Start index of string part in rString to be parsed.
+
+ @return true = Parsed string was valid, returned values can be used.
+ */
+ static bool parseOoxRange2d(
+ sal_Int32& ornStartColumn, sal_Int32& ornStartRow,
+ sal_Int32& ornEndColumn, sal_Int32& ornEndRow,
+ std::u16string_view aString,
+ sal_Int32 nStart = 0 );
+
+ /** Returns the biggest valid cell address in the own Calc document. */
+ const ScAddress&
+ getMaxApiAddress() const { return maMaxApiPos; }
+
+ /** Returns the biggest valid cell address in the imported/exported
+ Excel document. */
+ const ScAddress&
+ getMaxXlsAddress() const { return maMaxXlsPos; }
+
+ /** Returns the biggest valid cell address in both Calc and the
+ imported/exported Excel document. */
+ const ScAddress&
+ getMaxAddress() const { return maMaxPos; }
+
+ /** Checks if the passed column index is valid.
+
+ @param nCol The column index to check.
+ @param bTrackOverflow true = Update the internal overflow flag, if the
+ column index is outside of the supported limits.
+ @return true = Passed column index is valid (no index overflow).
+ */
+ bool checkCol( sal_Int32 nCol, bool bTrackOverflow );
+
+ /** Checks if the passed row index is valid.
+
+ @param nRow The row index to check.
+ @param bTrackOverflow true = Update the internal overflow flag, if the
+ row index is outside of the supported limits.
+ @return true = Passed row index is valid (no index overflow).
+ */
+ bool checkRow( sal_Int32 nRow, bool bTrackOverflow );
+
+ /** Checks if the passed sheet index is valid.
+
+ @param nSheet The sheet index to check.
+ @param bTrackOverflow true = Update the internal overflow flag, if the
+ sheet index is outside of the supported limits.
+ @return true = Passed sheet index is valid (no index overflow).
+ */
+ bool checkTab( sal_Int16 nSheet, bool bTrackOverflow );
+
+ /** Checks the passed cell address if it fits into the spreadsheet limits.
+
+ @param rAddress The cell address to be checked.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return true = Passed address is valid (no index overflow).
+ */
+ bool checkCellAddress(
+ const ScAddress& rAddress,
+ bool bTrackOverflow );
+
+ /** Converts the passed string to a single cell address, without checking
+ any sheet limits.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rString Cell address string in A1 notation.
+ @param nSheet Sheet index to be inserted into orAddress.
+ @return true = Cell address could be parsed from the passed string.
+ */
+ static bool convertToCellAddressUnchecked(
+ ScAddress& orAddress,
+ const OUString& rString,
+ sal_Int16 nSheet );
+
+ static bool convertToCellAddressUnchecked(
+ ScAddress& orAddress, std::string_view pStr, sal_Int16 nSheet );
+
+ /** Tries to convert the passed string to a single cell address.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rString Cell address string in A1 notation.
+ @param nSheet Sheet index to be inserted into orAddress (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return true = Converted address is valid (no index overflow).
+ */
+ bool convertToCellAddress(
+ ScAddress& orAddress,
+ const OUString& rString,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ bool convertToCellAddress(
+ ScAddress& rAddress,
+ std::string_view pStr, sal_Int16 nSheet, bool bTrackOverflow );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+
+ @param rString Cell address string in A1 notation.
+ @param nSheet Sheet index for the returned address (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return A valid cell address struct. */
+ ScAddress createValidCellAddress(
+ const OUString& rString,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Converts the passed address to a single cell address, without checking
+ any sheet limits.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rBinAddress Binary cell address struct.
+ @param nSheet Sheet index to be inserted into orAddress.
+ */
+ static void convertToCellAddressUnchecked(
+ ScAddress& orAddress,
+ const BinAddress& rBinAddress,
+ sal_Int16 nSheet );
+
+ /** Tries to convert the passed address to a single cell address.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rBinAddress Binary cell address struct.
+ @param nSheet Sheet index to be inserted into orAddress (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return true = Converted address is valid (no index overflow).
+ */
+ bool convertToCellAddress(
+ ScAddress& orAddress,
+ const BinAddress& rBinAddress,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+
+ @param rBinAddress Binary cell address struct.
+ @param nSheet Sheet index for the returned address (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return A valid cell address struct. */
+ ScAddress createValidCellAddress(
+ const BinAddress& rBinAddress,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Checks the passed cell range if it fits into the spreadsheet limits.
+
+ @param rRange The cell range address to be checked.
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits.
+ false = Do not allow ranges that overflow the supported limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the passed range contains cells outside of the supported sheet
+ limits.
+ @return true = Cell range is valid. This function returns also true,
+ if only parts of the range are outside the current sheet limits and
+ such an overflow is allowed via parameter bAllowOverflow. Returns
+ false, if the entire range is outside the sheet limits, or if
+ overflow is not allowed via parameter bAllowOverflow.
+ */
+ bool checkCellRange(
+ const ScRange& rRange,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+ /** Checks the passed cell range, may try to fit it to current sheet limits.
+
+ First, this function reorders the column and row indexes so that the
+ starting indexes are less than or equal to the end indexes. Then,
+ depending on the parameter bAllowOverflow, the range is just checked or
+ cropped to the current sheet limits.
+
+ @param orRange (in-out-parameter) Converts the passed cell range
+ into a valid cell range address. If the passed range contains cells
+ outside the currently supported spreadsheet limits, it will be
+ cropped to these limits.
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits. The
+ cell range returned in orRange will be cropped to these limits.
+ false = Do not allow ranges that overflow the supported limits. The
+ function will return false when the range overflows the sheet limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original range contains cells outside of the supported sheet
+ limits.
+ @return true = Converted range address is valid. This function
+ returns also true, if overflowing ranges are allowed via parameter
+ bAllowOverflow and the range has been cropped, but still contains
+ cells inside the current sheet limits. Returns false, if the entire
+ range is outside the sheet limits or overflowing ranges are not
+ allowed via parameter bAllowOverflow.
+ */
+ bool validateCellRange(
+ ScRange& orRange,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+ /** Converts the passed string to a cell range address, without checking
+ any sheet limits.
+
+ @param orRange (out-parameter) Returns the converted range address.
+ @param rString Cell range string in A1 notation.
+ @param nSheet Sheet index to be inserted into orRange.
+ @return true = Range address could be parsed from the passed string.
+ */
+ static bool convertToCellRangeUnchecked(
+ ScRange& orRange,
+ std::u16string_view aString,
+ sal_Int16 nSheet );
+
+ /** Tries to convert the passed string to a cell range address.
+
+ @param orRange (out-parameter) Returns the converted cell range
+ address. If the original range in the passed string contains cells
+ outside the currently supported spreadsheet limits, and parameter
+ bAllowOverflow is set to true, the range will be cropped to these
+ limits. Example: the range string "A1:ZZ100000" may be converted to
+ the range A1:IV65536.
+ @param aString Cell range string in A1 notation.
+ @param nSheet Sheet index to be inserted into orRange (will be checked).
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits. The
+ cell range returned in orRange will be cropped to these limits.
+ false = Do not allow ranges that overflow the supported limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original range contains cells outside of the supported sheet
+ limits.
+ @return true = Converted and returned range is valid. This function
+ returns also true, if overflowing ranges are allowed via parameter
+ bAllowOverflow and the range has been cropped, but still contains
+ cells inside the current sheet limits. Returns false, if the entire
+ range is outside the sheet limits or overflowing ranges are not
+ allowed via parameter bAllowOverflow.
+ */
+ bool convertToCellRange(
+ ScRange& orRange,
+ std::u16string_view aString,
+ sal_Int16 nSheet,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+ /** Converts the passed range to a cell range address, without checking any
+ sheet limits.
+
+ @param orRange (out-parameter) Returns the converted range address.
+ @param rBinRange Binary cell range struct.
+ @param nSheet Sheet index to be inserted into orRange.
+ */
+ static void convertToCellRangeUnchecked(
+ ScRange& orRange,
+ const BinRange& rBinRange,
+ sal_Int16 nSheet );
+
+ /** Tries to convert the passed range to a cell range address.
+
+ @param orRange (out-parameter) Returns the converted cell range
+ address. If the passed original range contains cells outside the
+ currently supported spreadsheet limits, and parameter bAllowOverflow
+ is set to true, the range will be cropped to these limits.
+ @param rBinRange Binary cell range struct.
+ @param nSheet Sheet index to be inserted into orRange (will be checked).
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits. The
+ cell range returned in orRange will be cropped to these limits.
+ false = Do not allow ranges that overflow the supported limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original range contains cells outside of the supported sheet
+ limits.
+ @return true = Converted and returned range is valid. This function
+ returns also true, if overflowing ranges are allowed via parameter
+ bAllowOverflow and the range has been cropped, but still contains
+ cells inside the current sheet limits. Returns false, if the entire
+ range is outside the sheet limits or if overflowing ranges are not
+ allowed via parameter bAllowOverflow.
+ */
+ bool convertToCellRange(
+ ScRange& orRange,
+ const BinRange& rBinRange,
+ sal_Int16 nSheet,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+
+ /** Tries to restrict the passed cell range list to current sheet limits.
+
+ @param orRanges (in-out-parameter) Restricts the cell range addresses
+ in the passed list to the current sheet limits and removes invalid
+ ranges from the list.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original ranges contain cells outside of the supported sheet
+ limits.
+ */
+ void validateCellRangeList(
+ ScRangeList& orRanges,
+ bool bTrackOverflow );
+
+ /** Tries to convert the passed string to a cell range list.
+
+ @param orRanges (out-parameter) Returns the converted cell range
+ addresses. If a range in the passed string contains cells outside
+ the currently supported spreadsheet limits, it will be cropped to
+ these limits. Example: the range string "A1:ZZ100000" may be
+ converted to the range A1:IV65536. If a range is completely outside
+ the limits, it will be omitted.
+ @param rString Cell range list string in A1 notation, space separated.
+ @param nSheet Sheet index to be inserted into orRanges (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original ranges contain cells outside of the supported sheet
+ limits.
+ */
+ void convertToCellRangeList(
+ ScRangeList& orRanges,
+ std::u16string_view aString,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Tries to convert the passed range list to a cell range list.
+
+ @param orRanges (out-parameter) Returns the converted cell range
+ addresses. If a range in the passed string contains cells outside
+ the currently supported spreadsheet limits, it will be cropped to
+ these limits. Example: the range string "A1:ZZ100000" may be
+ converted to the range A1:IV65536. If a range is completely outside
+ the limits, it will be omitted.
+ @param rBinRanges List of binary cell range objects.
+ @param nSheet Sheet index to be inserted into orRanges (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original ranges contain cells outside of the supported sheet
+ limits.
+ */
+ void convertToCellRangeList(
+ ScRangeList& orRanges,
+ const BinRangeList& rBinRanges,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Converts the passed range list to a sequence of cell range addresses.
+
+ @param orRanges List of range objects.
+ @return A uno sequence of cell range addresses as used in API calls.
+ Does not check ranges for supported sheet limits.
+ */
+ static css::uno::Sequence<css::table::CellRangeAddress>
+ toApiSequence(const ScRangeList& orRanges);
+
+ bool isColOverflow() const { return mbColOverflow; }
+ bool isRowOverflow() const { return mbRowOverflow; }
+ bool isTabOverflow() const { return mbTabOverflow; }
+
+private:
+ void initializeMaxPos(
+ sal_Int16 nMaxXlsTab, sal_Int32 nMaxXlsCol, sal_Int32 nMaxXlsRow );
+
+private:
+ ScAddress maMaxApiPos; /// Maximum valid cell address in Calc.
+ ScAddress maMaxXlsPos; /// Maximum valid cell address in Excel.
+ ScAddress maMaxPos; /// Maximum valid cell address in Calc/Excel.
+ bool mbColOverflow; /// Flag for "columns overflow".
+ bool mbRowOverflow; /// Flag for "rows overflow".
+ bool mbTabOverflow; /// Flag for "tables overflow".
+};
+
+} // namespace xls
+} // namespace oox
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/autofilterbuffer.hxx b/sc/source/filter/inc/autofilterbuffer.hxx
new file mode 100644
index 0000000000..fad4de53bf
--- /dev/null
+++ b/sc/source/filter/inc/autofilterbuffer.hxx
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/helper.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+#include <com/sun/star/sheet/TableFilterField3.hpp>
+#include <com/sun/star/util/Color.hpp>
+
+namespace com::sun::star {
+ namespace sheet { class XDatabaseRange; }
+ namespace sheet { class XSheetFilterDescriptor3; }
+}
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox {
+namespace xls {
+
+/** Contains UNO API filter settings for a column in a filtered range. */
+struct ApiFilterSettings
+{
+ typedef ::std::vector<css::sheet::TableFilterField3> FilterFieldVector;
+
+ FilterFieldVector maFilterFields; /// List of UNO API filter settings.
+ std::optional< bool > mobNeedsRegExp; /// If set, requires regular expressions to be enabled/disabled.
+
+ explicit ApiFilterSettings();
+
+ void appendField( bool bAnd, sal_Int32 nOperator, double fValue );
+ void appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue );
+ void appendField( bool bAnd, css::util::Color aColor, bool bIsBackgroundColor );
+ void appendField( bool bAnd, const std::vector<std::pair<OUString, bool>>& rValues );
+};
+
+/** Base class for specific filter settings for a column in a filtered range.
+ */
+class FilterSettingsBase : public WorkbookHelper
+{
+public:
+ explicit FilterSettingsBase( const WorkbookHelper& rHelper );
+
+ /** Derived classes import filter settings from the passed attribute list. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Derived classes import filter settings from the passed record. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+ /** Derived classes return converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport();
+};
+
+
+/** Settings for a discrete filter, specifying a list of values to be shown in
+ the filtered range.
+ */
+class DiscreteFilter final : public FilterSettingsBase
+{
+public:
+ explicit DiscreteFilter( const WorkbookHelper& rHelper );
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+
+ std::vector<std::pair<OUString, bool>> maValues; // first->values, second->bDatefFormat
+ sal_Int32 mnCalendarType;
+ bool mbShowBlank;
+};
+
+/** Settings for a top-10 filter. */
+class Top10Filter final : public FilterSettingsBase
+{
+public:
+ explicit Top10Filter( const WorkbookHelper& rHelper );
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+ double mfValue; /// Number of items or percentage.
+ bool mbTop; /// True = show top (greatest) items/percentage.
+ bool mbPercent; /// True = percentage, false = number of items.
+};
+
+/** Settings for a color filter. */
+class ColorFilter final : public FilterSettingsBase
+{
+public:
+ explicit ColorFilter(const WorkbookHelper& rHelper);
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs(sal_Int32 nElement, const AttributeList& rAttribs) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord(sal_Int32 nRecId, SequenceInputStream& rStrm) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+ /// Whether we are dealing with the background color (vs. text color)
+ bool mbIsBackgroundColor;
+ /// Style name to retrieve the color from
+ OUString msStyleName;
+};
+
+/** A filter criterion for a custom filter. */
+struct FilterCriterionModel
+{
+ css::uno::Any maValue; /// Comparison operand.
+ sal_Int32 mnOperator; /// Comparison operator.
+ sal_uInt8 mnDataType; /// Operand data type (BIFF only).
+
+ explicit FilterCriterionModel();
+
+ /** Sets the passed BIFF operator constant. */
+ void setBiffOperator( sal_uInt8 nOperator );
+
+ /** Imports the criterion model from the passed BIFF12 stream. */
+ void readBiffData( SequenceInputStream& rStrm );
+};
+
+/** Settings for a custom filter, specifying one or two comparison operators
+ associated with some values.
+ */
+class CustomFilter final : public FilterSettingsBase
+{
+public:
+ explicit CustomFilter( const WorkbookHelper& rHelper );
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+ /** Appends the passed filter criterion, if it contains valid settings. */
+ void appendCriterion( const FilterCriterionModel& rCriterion );
+
+private:
+ typedef ::std::vector< FilterCriterionModel > FilterCriterionVector;
+
+ FilterCriterionVector maCriteria;
+ bool mbAnd;
+};
+
+/** A column in a filtered range. Contains an object with specific filter
+ settings for the cells in the column.
+ */
+class FilterColumn final : public WorkbookHelper
+{
+public:
+ explicit FilterColumn( const WorkbookHelper& rHelper );
+
+ /** Imports auto filter column settings from the filterColumn element. */
+ void importFilterColumn( const AttributeList& rAttribs );
+ /** Imports auto filter column settings from the FILTERCOLUMN record. */
+ void importFilterColumn( SequenceInputStream& rStrm );
+
+ /** Creates and returns the specified filter settings object. */
+ template< typename FilterSettingsType >
+ FilterSettingsBase& createFilterSettings()
+ { mxSettings = std::make_shared<FilterSettingsType>( *this ); return *mxSettings; }
+
+ /** Returns converted UNO API filter settings representing all filter
+ settings of this column. */
+ ApiFilterSettings finalizeImport();
+ bool isButtonHidden();
+
+private:
+ std::shared_ptr< FilterSettingsBase >
+ mxSettings;
+ sal_Int32 mnColId;
+ bool mbHiddenButton;
+ bool mbShowButton;
+};
+
+// class SortCondition
+
+class SortCondition final : public WorkbookHelper
+{
+public:
+ explicit SortCondition( const WorkbookHelper& rHelper );
+
+ void importSortCondition( const AttributeList& rAttribs, sal_Int16 nSheet );
+
+ ScRange maRange; // Column/Row that this sort condition applies to.
+ OUString maSortCustomList; // Sort by a custom list.
+ bool mbDescending;
+};
+
+// class AutoFilter
+
+class AutoFilter final : public WorkbookHelper
+{
+public:
+ explicit AutoFilter( const WorkbookHelper& rHelper );
+
+ /** Imports auto filter settings from the autoFilter element. */
+ void importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet );
+ /** Imports auto filter settings from the AUTOFILTER record. */
+ void importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet );
+
+ void importSortState( const AttributeList& rAttribs, sal_Int16 nSheet );
+
+ /** Creates a new auto filter column and stores it internally. */
+ FilterColumn& createFilterColumn();
+
+ SortCondition& createSortCondition();
+
+ /** Applies the filter to the passed filter descriptor. */
+ void finalizeImport( const css::uno::Reference< css::sheet::XDatabaseRange >& rxDatabaseRange,
+ sal_Int16 nSheet );
+
+private:
+ typedef RefVector< FilterColumn > FilterColumnVector;
+
+ FilterColumnVector maFilterColumns;
+ ScRange maRange;
+
+ ScRange maSortRange; // The whole range of data to sort (not just the sort-by column).
+ typedef RefVector< SortCondition > SortConditionVector;
+ SortConditionVector maSortConditions;
+};
+
+class AutoFilterBuffer final : public WorkbookHelper
+{
+public:
+ explicit AutoFilterBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new auto filter and stores it internally. */
+ AutoFilter& createAutoFilter();
+
+ /** Applies filter settings to a new database range object (used for sheet
+ autofilter or advanced filter as specified by built-in defined names). */
+ void finalizeImport( sal_Int16 nSheet );
+
+ /** Applies the filters to the passed database range object.
+ @return True = this buffer contains valid auto filter settings. */
+ bool finalizeImport( const css::uno::Reference< css::sheet::XDatabaseRange >& rxDatabaseRange,
+ sal_Int16 nSheet );
+
+private:
+ /** Returns the auto filter object used to perform auto filtering. */
+ AutoFilter* getActiveAutoFilter();
+
+private:
+ typedef RefVector< AutoFilter > AutoFilterVector;
+ AutoFilterVector maAutoFilters;
+};
+
+} // namespace xls
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/autofiltercontext.hxx b/sc/source/filter/inc/autofiltercontext.hxx
new file mode 100644
index 0000000000..f6eadab801
--- /dev/null
+++ b/sc/source/filter/inc/autofiltercontext.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "autofilterbuffer.hxx"
+
+namespace oox::xls {
+
+class AutoFilter;
+class FilterColumn;
+class FilterSettingsBase;
+
+class FilterSettingsContext final : public WorksheetContextBase
+{
+public:
+ explicit FilterSettingsContext( WorksheetContextBase& rParent, FilterSettingsBase& rFilterSettings );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ FilterSettingsBase& mrFilterSettings;
+};
+
+class FilterColumnContext final : public WorksheetContextBase
+{
+public:
+ explicit FilterColumnContext( WorksheetContextBase& rParent, FilterColumn& rFilterColumn );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ FilterColumn& mrFilterColumn;
+};
+
+// class SortConditionContext
+
+class SortConditionContext final : public WorksheetContextBase
+{
+public:
+ explicit SortConditionContext( WorksheetContextBase& rFragment, SortCondition& rSortCondition );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ SortCondition& mrSortCondition;
+};
+
+// class SortStateContext
+
+class SortStateContext final : public WorksheetContextBase
+{
+public:
+ explicit SortStateContext( WorksheetContextBase& rFragment, AutoFilter& rAutoFilter );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ AutoFilter& mrAutoFilter;
+};
+
+// class AutoFilterContext
+
+class AutoFilterContext final : public WorksheetContextBase
+{
+public:
+ explicit AutoFilterContext( WorksheetFragmentBase& rFragment, AutoFilter& rAutoFilter );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ AutoFilter& mrAutoFilter;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/biffhelper.hxx b/sc/source/filter/inc/biffhelper.hxx
new file mode 100644
index 0000000000..1cf3ed9c6c
--- /dev/null
+++ b/sc/source/filter/inc/biffhelper.hxx
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+// BIFF12 record identifiers ==================================================
+
+const sal_Int32 BIFF12_ID_ARRAY = 0x01AA;
+const sal_Int32 BIFF12_ID_AUTOFILTER = 0x00A1;
+const sal_Int32 BIFF12_ID_AUTOSORTSCOPE = 0x01CB;
+const sal_Int32 BIFF12_ID_BINARYINDEXBLOCK = 0x002A;
+const sal_Int32 BIFF12_ID_BINARYINDEXROWS = 0x0028;
+const sal_Int32 BIFF12_ID_BOOKVIEWS = 0x0087;
+const sal_Int32 BIFF12_ID_BORDER = 0x002E;
+const sal_Int32 BIFF12_ID_BORDERS = 0x0265;
+const sal_Int32 BIFF12_ID_BRK = 0x018C;
+const sal_Int32 BIFF12_ID_CALCPR = 0x009D;
+const sal_Int32 BIFF12_ID_CELL_BLANK = 0x0001;
+const sal_Int32 BIFF12_ID_CELL_BOOL = 0x0004;
+const sal_Int32 BIFF12_ID_CELL_DOUBLE = 0x0005;
+const sal_Int32 BIFF12_ID_CELL_ERROR = 0x0003;
+const sal_Int32 BIFF12_ID_CELL_RK = 0x0002;
+const sal_Int32 BIFF12_ID_CELL_RSTRING = 0x003E;
+const sal_Int32 BIFF12_ID_CELL_SI = 0x0007;
+const sal_Int32 BIFF12_ID_CELL_STRING = 0x0006;
+const sal_Int32 BIFF12_ID_CELLSTYLE = 0x0030;
+const sal_Int32 BIFF12_ID_CELLSTYLES = 0x026B;
+const sal_Int32 BIFF12_ID_CELLSTYLEXFS = 0x0272;
+const sal_Int32 BIFF12_ID_CELLXFS = 0x0269;
+const sal_Int32 BIFF12_ID_CFCOLOR = 0x0234;
+const sal_Int32 BIFF12_ID_CFRULE = 0x01CF;
+const sal_Int32 BIFF12_ID_CHARTPAGESETUP = 0x028C;
+const sal_Int32 BIFF12_ID_CHARTPROTECTION = 0x029D;
+const sal_Int32 BIFF12_ID_CHARTSHEETPR = 0x028B;
+const sal_Int32 BIFF12_ID_CHARTSHEETVIEW = 0x008D;
+const sal_Int32 BIFF12_ID_CHARTSHEETVIEWS = 0x008B;
+const sal_Int32 BIFF12_ID_COL = 0x003C;
+const sal_Int32 BIFF12_ID_COLBREAKS = 0x018A;
+const sal_Int32 BIFF12_ID_COLOR = 0x023C;
+const sal_Int32 BIFF12_ID_COLORS = 0x01D9;
+const sal_Int32 BIFF12_ID_COLORSCALE = 0x01D5;
+const sal_Int32 BIFF12_ID_COLS = 0x0186;
+const sal_Int32 BIFF12_ID_COMMENT = 0x027B;
+const sal_Int32 BIFF12_ID_COMMENTAUTHOR = 0x0278;
+const sal_Int32 BIFF12_ID_COMMENTAUTHORS = 0x0276;
+const sal_Int32 BIFF12_ID_COMMENTLIST = 0x0279;
+const sal_Int32 BIFF12_ID_COMMENTS = 0x0274;
+const sal_Int32 BIFF12_ID_COMMENTTEXT = 0x027D;
+const sal_Int32 BIFF12_ID_CONDFORMATTING = 0x01CD;
+const sal_Int32 BIFF12_ID_CONNECTION = 0x00C9;
+const sal_Int32 BIFF12_ID_CONNECTIONS = 0x01AD;
+const sal_Int32 BIFF12_ID_CONTROL = 0x0284;
+const sal_Int32 BIFF12_ID_CONTROLS = 0x0283;
+const sal_Int32 BIFF12_ID_CUSTOMCHARTVIEW = 0x028F;
+const sal_Int32 BIFF12_ID_CUSTOMCHARTVIEWS = 0x028D;
+const sal_Int32 BIFF12_ID_CUSTOMFILTER = 0x00AE;
+const sal_Int32 BIFF12_ID_CUSTOMFILTERS = 0x00AC;
+const sal_Int32 BIFF12_ID_CUSTOMSHEETVIEW = 0x01A7;
+const sal_Int32 BIFF12_ID_CUSTOMSHEETVIEWS = 0x01A6;
+const sal_Int32 BIFF12_ID_CUSTOMWORKBOOKVIEW= 0x018D;
+const sal_Int32 BIFF12_ID_DATABAR = 0x01D3;
+const sal_Int32 BIFF12_ID_DATATABLE = 0x01AC;
+const sal_Int32 BIFF12_ID_DATAVALIDATION = 0x0040;
+const sal_Int32 BIFF12_ID_DATAVALIDATIONS = 0x023D;
+const sal_Int32 BIFF12_ID_DDEITEMVALUES = 0x0242;
+const sal_Int32 BIFF12_ID_DDEITEM_BOOL = 0x0248;
+const sal_Int32 BIFF12_ID_DDEITEM_DOUBLE = 0x0244;
+const sal_Int32 BIFF12_ID_DDEITEM_ERROR = 0x0245;
+const sal_Int32 BIFF12_ID_DDEITEM_STRING = 0x0246;
+const sal_Int32 BIFF12_ID_DEFINEDNAME = 0x0027;
+const sal_Int32 BIFF12_ID_DIMENSION = 0x0094;
+const sal_Int32 BIFF12_ID_DISCRETEFILTER = 0x00A7;
+const sal_Int32 BIFF12_ID_DISCRETEFILTERS = 0x00A5;
+const sal_Int32 BIFF12_ID_DRAWING = 0x0226;
+const sal_Int32 BIFF12_ID_DXF = 0x01FB;
+const sal_Int32 BIFF12_ID_DXFS = 0x01F9;
+const sal_Int32 BIFF12_ID_EXTCELL_BLANK = 0x016F;
+const sal_Int32 BIFF12_ID_EXTCELL_BOOL = 0x0171;
+const sal_Int32 BIFF12_ID_EXTCELL_DOUBLE = 0x0170;
+const sal_Int32 BIFF12_ID_EXTCELL_ERROR = 0x0172;
+const sal_Int32 BIFF12_ID_EXTCELL_STRING = 0x0173;
+const sal_Int32 BIFF12_ID_EXTERNALADDIN = 0x029B;
+const sal_Int32 BIFF12_ID_EXTERNALBOOK = 0x0168;
+const sal_Int32 BIFF12_ID_EXTERNALNAME = 0x0241;
+const sal_Int32 BIFF12_ID_EXTERNALREF = 0x0163;
+const sal_Int32 BIFF12_ID_EXTERNALREFS = 0x0161;
+const sal_Int32 BIFF12_ID_EXTERNALSELF = 0x0165;
+const sal_Int32 BIFF12_ID_EXTERNALSAME = 0x0166;
+const sal_Int32 BIFF12_ID_EXTERNALSHEETS = 0x016A;
+const sal_Int32 BIFF12_ID_EXTROW = 0x016E;
+const sal_Int32 BIFF12_ID_EXTSHEETDATA = 0x016B;
+const sal_Int32 BIFF12_ID_EXTERNALNAMEFLAGS = 0x024A;
+const sal_Int32 BIFF12_ID_EXTSHEETNAMES = 0x0167;
+const sal_Int32 BIFF12_ID_FILESHARING = 0x0224;
+const sal_Int32 BIFF12_ID_FILEVERSION = 0x0080;
+const sal_Int32 BIFF12_ID_FILL = 0x002D;
+const sal_Int32 BIFF12_ID_FILLS = 0x025B;
+const sal_Int32 BIFF12_ID_FILTERCOLUMN = 0x00A3;
+const sal_Int32 BIFF12_ID_FONT = 0x002B;
+const sal_Int32 BIFF12_ID_FONTS = 0x0263;
+const sal_Int32 BIFF12_ID_FORMULA_STRING = 0x0008;
+const sal_Int32 BIFF12_ID_FORMULA_DOUBLE = 0x0009;
+const sal_Int32 BIFF12_ID_FORMULA_BOOL = 0x000A;
+const sal_Int32 BIFF12_ID_FORMULA_ERROR = 0x000B;
+const sal_Int32 BIFF12_ID_FUNCTIONGROUP = 0x0299;
+const sal_Int32 BIFF12_ID_FUNCTIONGROUPS = 0x0298;
+const sal_Int32 BIFF12_ID_HEADERFOOTER = 0x01DF;
+const sal_Int32 BIFF12_ID_HYPERLINK = 0x01EE;
+const sal_Int32 BIFF12_ID_ICONSET = 0x01D1;
+const sal_Int32 BIFF12_ID_INDEXEDCOLORS = 0x0235;
+const sal_Int32 BIFF12_ID_INPUTCELLS = 0x01F8;
+const sal_Int32 BIFF12_ID_LEGACYDRAWING = 0x0227;
+const sal_Int32 BIFF12_ID_MERGECELL = 0x00B0;
+const sal_Int32 BIFF12_ID_MERGECELLS = 0x00B1;
+const sal_Int32 BIFF12_ID_MRUCOLORS = 0x0239;
+const sal_Int32 BIFF12_ID_MULTCELL_BLANK = 0x000C;
+const sal_Int32 BIFF12_ID_MULTCELL_BOOL = 0x000F;
+const sal_Int32 BIFF12_ID_MULTCELL_DOUBLE = 0x0010;
+const sal_Int32 BIFF12_ID_MULTCELL_ERROR = 0x000E;
+const sal_Int32 BIFF12_ID_MULTCELL_RK = 0x000D;
+const sal_Int32 BIFF12_ID_MULTCELL_RSTRING = 0x003D;
+const sal_Int32 BIFF12_ID_MULTCELL_SI = 0x0012;
+const sal_Int32 BIFF12_ID_MULTCELL_STRING = 0x0011;
+const sal_Int32 BIFF12_ID_NUMFMT = 0x002C;
+const sal_Int32 BIFF12_ID_NUMFMTS = 0x0267;
+const sal_Int32 BIFF12_ID_OLEOBJECT = 0x027F;
+const sal_Int32 BIFF12_ID_OLEOBJECTS = 0x027E;
+const sal_Int32 BIFF12_ID_OLESIZE = 0x0225;
+const sal_Int32 BIFF12_ID_PAGEMARGINS = 0x01DC;
+const sal_Int32 BIFF12_ID_PAGESETUP = 0x01DE;
+const sal_Int32 BIFF12_ID_PANE = 0x0097;
+const sal_Int32 BIFF12_ID_PCDEFINITION = 0x00B3;
+const sal_Int32 BIFF12_ID_PCDFDISCRETEPR = 0x00E1;
+const sal_Int32 BIFF12_ID_PCDFGROUPITEMS = 0x00DD;
+const sal_Int32 BIFF12_ID_PCDFIELD = 0x00B7;
+const sal_Int32 BIFF12_ID_PCDFIELDGROUP = 0x00DB;
+const sal_Int32 BIFF12_ID_PCDFIELDS = 0x00B5;
+const sal_Int32 BIFF12_ID_PCDFRANGEPR = 0x00DF;
+const sal_Int32 BIFF12_ID_PCDFSHAREDITEMS = 0x00BD;
+const sal_Int32 BIFF12_ID_PCDSHEETSOURCE = 0x00BB;
+const sal_Int32 BIFF12_ID_PCDSOURCE = 0x00B9;
+const sal_Int32 BIFF12_ID_PCITEM_ARRAY = 0x00BF;
+const sal_Int32 BIFF12_ID_PCITEM_BOOL = 0x0016;
+const sal_Int32 BIFF12_ID_PCITEM_DATE = 0x0019;
+const sal_Int32 BIFF12_ID_PCITEM_DOUBLE = 0x0015;
+const sal_Int32 BIFF12_ID_PCITEM_ERROR = 0x0017;
+const sal_Int32 BIFF12_ID_PCITEM_INDEX = 0x001A;
+const sal_Int32 BIFF12_ID_PCITEM_MISSING = 0x0014;
+const sal_Int32 BIFF12_ID_PCITEM_STRING = 0x0018;
+const sal_Int32 BIFF12_ID_PCITEMA_BOOL = 0x001D;
+const sal_Int32 BIFF12_ID_PCITEMA_DATE = 0x0020;
+const sal_Int32 BIFF12_ID_PCITEMA_DOUBLE = 0x001C;
+const sal_Int32 BIFF12_ID_PCITEMA_ERROR = 0x001E;
+const sal_Int32 BIFF12_ID_PCITEMA_MISSING = 0x001B;
+const sal_Int32 BIFF12_ID_PCITEMA_STRING = 0x001F;
+const sal_Int32 BIFF12_ID_PCRECORD = 0x0021;
+const sal_Int32 BIFF12_ID_PCRECORDDT = 0x0022;
+const sal_Int32 BIFF12_ID_PCRECORDS = 0x00C1;
+const sal_Int32 BIFF12_ID_PHONETICPR = 0x0219;
+const sal_Int32 BIFF12_ID_PICTURE = 0x0232;
+const sal_Int32 BIFF12_ID_PIVOTAREA = 0x00F7;
+const sal_Int32 BIFF12_ID_PIVOTCACHE = 0x0182;
+const sal_Int32 BIFF12_ID_PIVOTCACHES = 0x0180;
+const sal_Int32 BIFF12_ID_PRINTOPTIONS = 0x01DD;
+const sal_Int32 BIFF12_ID_PTCOLFIELDS = 0x0137;
+const sal_Int32 BIFF12_ID_PTDATAFIELD = 0x0125;
+const sal_Int32 BIFF12_ID_PTDATAFIELDS = 0x0127;
+const sal_Int32 BIFF12_ID_PTDEFINITION = 0x0118;
+const sal_Int32 BIFF12_ID_PTFIELD = 0x011D;
+const sal_Int32 BIFF12_ID_PTFIELDS = 0x011F;
+const sal_Int32 BIFF12_ID_PTFILTER = 0x0259;
+const sal_Int32 BIFF12_ID_PTFILTERS = 0x0257;
+const sal_Int32 BIFF12_ID_PTFITEM = 0x011A;
+const sal_Int32 BIFF12_ID_PTFITEMS = 0x011B;
+const sal_Int32 BIFF12_ID_PTLOCATION = 0x013A;
+const sal_Int32 BIFF12_ID_PTPAGEFIELD = 0x0121;
+const sal_Int32 BIFF12_ID_PTPAGEFIELDS = 0x0123;
+const sal_Int32 BIFF12_ID_PTREFERENCE = 0x00FB;
+const sal_Int32 BIFF12_ID_PTREFERENCEITEM = 0x017E;
+const sal_Int32 BIFF12_ID_PTREFERENCES = 0x00F9;
+const sal_Int32 BIFF12_ID_PTROWFIELDS = 0x0135;
+const sal_Int32 BIFF12_ID_QUERYTABLE = 0x01BF;
+const sal_Int32 BIFF12_ID_QUERYTABLEREFRESH = 0x01C1;
+const sal_Int32 BIFF12_ID_RGBCOLOR = 0x01DB;
+const sal_Int32 BIFF12_ID_ROW = 0x0000;
+const sal_Int32 BIFF12_ID_ROWBREAKS = 0x0188;
+const sal_Int32 BIFF12_ID_SCENARIO = 0x01F6;
+const sal_Int32 BIFF12_ID_SCENARIOS = 0x01F4;
+const sal_Int32 BIFF12_ID_SELECTION = 0x0098;
+const sal_Int32 BIFF12_ID_SHAREDFMLA = 0x01AB;
+const sal_Int32 BIFF12_ID_SHEET = 0x009C;
+const sal_Int32 BIFF12_ID_SHEETDATA = 0x0091;
+const sal_Int32 BIFF12_ID_SHEETFORMATPR = 0x01E5;
+const sal_Int32 BIFF12_ID_SHEETPR = 0x0093;
+const sal_Int32 BIFF12_ID_SHEETPROTECTION = 0x0217;
+const sal_Int32 BIFF12_ID_SHEETS = 0x008F;
+const sal_Int32 BIFF12_ID_SHEETVIEW = 0x0089;
+const sal_Int32 BIFF12_ID_SHEETVIEWS = 0x0085;
+const sal_Int32 BIFF12_ID_SI = 0x0013;
+const sal_Int32 BIFF12_ID_SST = 0x009F;
+const sal_Int32 BIFF12_ID_STYLESHEET = 0x0116;
+const sal_Int32 BIFF12_ID_TABLE = 0x0157;
+const sal_Int32 BIFF12_ID_TABLEPART = 0x0295;
+const sal_Int32 BIFF12_ID_TABLEPARTS = 0x0294;
+const sal_Int32 BIFF12_ID_TABLESTYLEINFO = 0x0201;
+const sal_Int32 BIFF12_ID_TABLESTYLES = 0x01FC;
+const sal_Int32 BIFF12_ID_TOP10FILTER = 0x00AA;
+const sal_Int32 BIFF12_ID_VOLTYPE = 0x0204;
+const sal_Int32 BIFF12_ID_VOLTYPEMAIN = 0x0206;
+const sal_Int32 BIFF12_ID_VOLTYPES = 0x0202;
+const sal_Int32 BIFF12_ID_VOLTYPESTP = 0x020A;
+const sal_Int32 BIFF12_ID_VOLTYPETR = 0x020B;
+const sal_Int32 BIFF12_ID_WEBPR = 0x0105;
+const sal_Int32 BIFF12_ID_WEBPRTABLES = 0x0107;
+const sal_Int32 BIFF12_ID_WORKBOOK = 0x0083;
+const sal_Int32 BIFF12_ID_WORKBOOKPR = 0x0099;
+const sal_Int32 BIFF12_ID_WORKBOOKVIEW = 0x009E;
+const sal_Int32 BIFF12_ID_WORKSHEET = 0x0081;
+const sal_Int32 BIFF12_ID_XF = 0x002F;
+
+// BIFF2-BIFF8 record identifiers =============================================
+
+/** all binary Excel file format types (BIFF types).
+ BIFF2 /// MS Excel 2.1.
+ BIFF3 /// MS Excel 3.0.
+ BIFF4 /// MS Excel 4.0.
+ BIFF5 /// MS Excel 5.0, MS Excel 7.0 (95).
+ BIFF8 /// MS Excel 8.0 (97), 9.0 (2000), 10.0 (XP), 11.0 (2003).
+ BIFF_UNKNOWN /// Unknown BIFF version.
+*/
+
+//const sal_uInt16 BIFF2_MAXRECSIZE = 2080;
+//const sal_uInt16 BIFF8_MAXRECSIZE = 8224;
+
+// record identifiers ---------------------------------------------------------
+
+const sal_uInt16 BIFF2_ID_BOF = 0x0009;
+const sal_uInt16 BIFF3_ID_BOF = 0x0209;
+const sal_uInt16 BIFF4_ID_BOF = 0x0409;
+const sal_uInt16 BIFF5_ID_BOF = 0x0809;
+const sal_uInt16 BIFF_ID_CONT = 0x003C;
+const sal_uInt16 BIFF_ID_EOF = 0x000A;
+const sal_uInt16 BIFF_ID_PCDEFINITION = 0x00C6;
+const sal_uInt16 BIFF_ID_PCDEFINITION2 = 0x0122;
+const sal_uInt16 BIFF_ID_PCDFDISCRETEPR = 0x00D9;
+const sal_uInt16 BIFF_ID_PCDFIELD = 0x00C7;
+const sal_uInt16 BIFF_ID_PCDFRANGEPR = 0x00D8;
+const sal_uInt16 BIFF_ID_PCDFSQLTYPE = 0x01BB;
+const sal_uInt16 BIFF_ID_PCITEM_BOOL = 0x00CA;
+const sal_uInt16 BIFF_ID_PCITEM_DATE = 0x00CE;
+const sal_uInt16 BIFF_ID_PCITEM_DOUBLE = 0x00C9;
+const sal_uInt16 BIFF_ID_PCITEM_ERROR = 0x00CB;
+const sal_uInt16 BIFF_ID_PCITEM_INDEXLIST = 0x00C8;
+const sal_uInt16 BIFF_ID_PCITEM_INTEGER = 0x00CC;
+const sal_uInt16 BIFF_ID_PCITEM_MISSING = 0x00CF;
+const sal_uInt16 BIFF_ID_PCITEM_STRING = 0x00CD;
+
+const sal_uInt16 BIFF_ID_UNKNOWN = SAL_MAX_UINT16;
+
+/* Many of these constants might be unused, but please keep for documentation. If you notice
+ * hardcoded numbers in the code that actually correspond in meaning in the context (not just value)
+ * to one of the named constants, feel free to change it to use the constant instead, of course.
+ */
+const sal_uInt16 BIFF2_ID_ARRAY = 0x0021;
+const sal_uInt16 BIFF3_ID_ARRAY = 0x0221;
+const sal_uInt16 BIFF_ID_AUTOFILTER = 0x009D;
+const sal_uInt16 BIFF2_ID_BLANK = 0x0001;
+const sal_uInt16 BIFF3_ID_BLANK = 0x0201;
+const sal_uInt16 BIFF_ID_BOOKBOOL = 0x00DA;
+const sal_uInt16 BIFF_ID_BOOKEXT = 0x0863;
+const sal_uInt16 BIFF2_ID_BOOLERR = 0x0005;
+const sal_uInt16 BIFF3_ID_BOOLERR = 0x0205;
+const sal_uInt16 BIFF_ID_BOTTOMMARGIN = 0x0029;
+const sal_uInt16 BIFF_ID_CALCCOUNT = 0x000C;
+const sal_uInt16 BIFF_ID_CALCMODE = 0x000D;
+const sal_uInt16 BIFF_ID_CFHEADER = 0x01B0;
+const sal_uInt16 BIFF_ID_CFRULE = 0x01B1;
+const sal_uInt16 BIFF_ID_CFRULE12 = 0x087A;
+const sal_uInt16 BIFF_ID_CFRULEEXT = 0x087B;
+const sal_uInt16 BIFF_ID_CH3DDATAFORMAT = 0x105F;
+const sal_uInt16 BIFF_ID_CHAREA = 0x101A;
+const sal_uInt16 BIFF_ID_CHAREAFORMAT = 0x100A;
+const sal_uInt16 BIFF_ID_CHATTACHEDLABEL = 0x100C;
+const sal_uInt16 BIFF_ID_CHAXESSET = 0x1041;
+const sal_uInt16 BIFF_ID_CHAXIS = 0x101D;
+const sal_uInt16 BIFF_ID_CHAXISLINE = 0x1021;
+const sal_uInt16 BIFF_ID_CHBAR = 0x1017;
+const sal_uInt16 BIFF_ID_CHBEGIN = 0x1033;
+const sal_uInt16 BIFF_ID_CHCHART = 0x1002;
+const sal_uInt16 BIFF_ID_CHCHART3D = 0x103A;
+const sal_uInt16 BIFF_ID_CHCHARTLINE = 0x101C;
+const sal_uInt16 BIFF_ID_CHDATAFORMAT = 0x1006;
+const sal_uInt16 BIFF_ID_CHDATERANGE = 0x1062;
+const sal_uInt16 BIFF_ID_CHDEFAULTTEXT = 0x1024;
+const sal_uInt16 BIFF_ID_CHDROPBAR = 0x103D;
+const sal_uInt16 BIFF_ID_CHECKCOMPAT = 0x088C;
+const sal_uInt16 BIFF_ID_CHEND = 0x1034;
+const sal_uInt16 BIFF_ID_CHESCHERFORMAT = 0x1066;
+const sal_uInt16 BIFF_ID_CHFONT = 0x1026;
+const sal_uInt16 BIFF_ID_CHFORMAT = 0x104E;
+const sal_uInt16 BIFF_ID_CHFORMATRUNS = 0x1050;
+const sal_uInt16 BIFF_ID_CHFRAME = 0x1032;
+const sal_uInt16 BIFF_ID_CHFRAMEPOS = 0x104F;
+const sal_uInt16 BIFF_ID_CHFRBLOCKBEGIN = 0x0852;
+const sal_uInt16 BIFF_ID_CHFRBLOCKEND = 0x0853;
+const sal_uInt16 BIFF_ID_CHFRCATEGORYPROPS = 0x0856;
+const sal_uInt16 BIFF_ID_CHFREXTPROPS = 0x089E;
+const sal_uInt16 BIFF_ID_CHFREXTPROPSCONT = 0x089F;
+const sal_uInt16 BIFF_ID_CHFRINFO = 0x0850;
+const sal_uInt16 BIFF_ID_CHFRLABELPROPS = 0x086B;
+const sal_uInt16 BIFF_ID_CHFRLAYOUT = 0x089D;
+const sal_uInt16 BIFF_ID_CHFRPLOTAREALAYOUT = 0x08A7;
+const sal_uInt16 BIFF_ID_CHFRSHAPEPROPS = 0x08A4;
+const sal_uInt16 BIFF_ID_CHFRTEXTPROPS = 0x08A5;
+const sal_uInt16 BIFF_ID_CHFRUNITPROPS = 0x0857;
+const sal_uInt16 BIFF_ID_CHFRWRAPPER = 0x0851;
+const sal_uInt16 BIFF_ID_CHLABELRANGE = 0x1020;
+const sal_uInt16 BIFF_ID_CHLEGEND = 0x1015;
+const sal_uInt16 BIFF_ID_CHLINE = 0x1018;
+const sal_uInt16 BIFF_ID_CHLINEFORMAT = 0x1007;
+const sal_uInt16 BIFF_ID_CHMARKERFORMAT = 0x1009;
+const sal_uInt16 BIFF_ID_CHOBJECTLINK = 0x1027;
+const sal_uInt16 BIFF_ID_CHPICFORMAT = 0x103C;
+const sal_uInt16 BIFF_ID_CHPIE = 0x1019;
+const sal_uInt16 BIFF_ID_CHPIEEXT = 0x1061;
+const sal_uInt16 BIFF_ID_CHPIEFORMAT = 0x100B;
+const sal_uInt16 BIFF_ID_CHPIVOTFLAGS = 0x0859;
+const sal_uInt16 BIFF5_ID_CHPIVOTREF = 0x1048;
+const sal_uInt16 BIFF8_ID_CHPIVOTREF = 0x0858;
+const sal_uInt16 BIFF_ID_CHPLOTFRAME = 0x1035;
+const sal_uInt16 BIFF_ID_CHPLOTGROWTH = 0x1064;
+const sal_uInt16 BIFF_ID_CHPROPERTIES = 0x1044;
+const sal_uInt16 BIFF_ID_CHRADARLINE = 0x103E;
+const sal_uInt16 BIFF_ID_CHRADARAREA = 0x1040;
+const sal_uInt16 BIFF_ID_CHSCATTER = 0x101B;
+const sal_uInt16 BIFF_ID_CHSERERRORBAR = 0x105B;
+const sal_uInt16 BIFF_ID_CHSERGROUP = 0x1045;
+const sal_uInt16 BIFF_ID_CHSERIES = 0x1003;
+const sal_uInt16 BIFF_ID_CHSERIESFORMAT = 0x105D;
+const sal_uInt16 BIFF_ID_CHSERPARENT = 0x104A;
+const sal_uInt16 BIFF_ID_CHSERTRENDLINE = 0x104B;
+const sal_uInt16 BIFF_ID_CHSOURCELINK = 0x1051;
+const sal_uInt16 BIFF_ID_CHSTRING = 0x100D;
+const sal_uInt16 BIFF_ID_CHSURFACE = 0x103F;
+const sal_uInt16 BIFF_ID_CHTEXT = 0x1025;
+const sal_uInt16 BIFF_ID_CHTICK = 0x101E;
+const sal_uInt16 BIFF_ID_CHTYPEGROUP = 0x1014;
+const sal_uInt16 BIFF_ID_CHVALUERANGE = 0x101F;
+const sal_uInt16 BIFF_ID_CODENAME = 0x01BA;
+const sal_uInt16 BIFF_ID_CODEPAGE = 0x0042;
+const sal_uInt16 BIFF_ID_COLINFO = 0x007D;
+const sal_uInt16 BIFF_ID_COLUMNDEFAULT = 0x0020;
+const sal_uInt16 BIFF_ID_COLWIDTH = 0x0024;
+const sal_uInt16 BIFF_ID_COMPRESSPICS = 0x089B;
+const sal_uInt16 BIFF_ID_CONNECTION = 0x0876;
+const sal_uInt16 BIFF_ID_COORDLIST = 0x00A9;
+const sal_uInt16 BIFF_ID_COUNTRY = 0x008C;
+const sal_uInt16 BIFF_ID_CRN = 0x005A;
+const sal_uInt16 BIFF2_ID_DATATABLE = 0x0036;
+const sal_uInt16 BIFF3_ID_DATATABLE = 0x0236;
+const sal_uInt16 BIFF2_ID_DATATABLE2 = 0x0037;
+const sal_uInt16 BIFF_ID_DATAVALIDATION = 0x01BE;
+const sal_uInt16 BIFF_ID_DATAVALIDATIONS = 0x01B2;
+const sal_uInt16 BIFF_ID_DATEMODE = 0x0022;
+const sal_uInt16 BIFF_ID_DBCELL = 0x00D7;
+const sal_uInt16 BIFF_ID_DBQUERY = 0x00DC;
+const sal_uInt16 BIFF_ID_DCONBINAME = 0x01B5;
+const sal_uInt16 BIFF_ID_DCONNAME = 0x0052;
+const sal_uInt16 BIFF_ID_DCONREF = 0x0051;
+const sal_uInt16 BIFF_ID_DEFCOLWIDTH = 0x0055;
+const sal_uInt16 BIFF2_ID_DEFINEDNAME = 0x0018;
+const sal_uInt16 BIFF3_ID_DEFINEDNAME = 0x0218;
+const sal_uInt16 BIFF5_ID_DEFINEDNAME = 0x0018;
+const sal_uInt16 BIFF2_ID_DEFROWHEIGHT = 0x0025;
+const sal_uInt16 BIFF3_ID_DEFROWHEIGHT = 0x0225;
+const sal_uInt16 BIFF_ID_DELTA = 0x0010;
+const sal_uInt16 BIFF2_ID_DIMENSION = 0x0000;
+const sal_uInt16 BIFF3_ID_DIMENSION = 0x0200;
+const sal_uInt16 BIFF_ID_DXF = 0x088D;
+const sal_uInt16 BIFF_ID_EXTERNALBOOK = 0x01AE;
+const sal_uInt16 BIFF2_ID_EXTERNALNAME = 0x0023;
+const sal_uInt16 BIFF3_ID_EXTERNALNAME = 0x0223;
+const sal_uInt16 BIFF5_ID_EXTERNALNAME = 0x0023;
+const sal_uInt16 BIFF_ID_EXTERNSHEET = 0x0017;
+const sal_uInt16 BIFF_ID_EXTSST = 0x00FF;
+const sal_uInt16 BIFF_ID_FILEPASS = 0x002F;
+const sal_uInt16 BIFF_ID_FILESHARING = 0x005B;
+const sal_uInt16 BIFF_ID_FILTERCOLUMN = 0x009E;
+const sal_uInt16 BIFF_ID_FILTERMODE = 0x009B;
+const sal_uInt16 BIFF2_ID_FONT = 0x0031;
+const sal_uInt16 BIFF3_ID_FONT = 0x0231;
+const sal_uInt16 BIFF5_ID_FONT = 0x0031;
+const sal_uInt16 BIFF_ID_FONTCOLOR = 0x0045;
+const sal_uInt16 BIFF_ID_FOOTER = 0x0015;
+const sal_uInt16 BIFF_ID_FORCEFULLCALC = 0x08A3;
+const sal_uInt16 BIFF2_ID_FORMAT = 0x001E;
+const sal_uInt16 BIFF4_ID_FORMAT = 0x041E;
+const sal_uInt16 BIFF2_ID_FORMULA = 0x0006;
+const sal_uInt16 BIFF3_ID_FORMULA = 0x0206;
+const sal_uInt16 BIFF4_ID_FORMULA = 0x0406;
+const sal_uInt16 BIFF5_ID_FORMULA = 0x0006;
+const sal_uInt16 BIFF_ID_GUTS = 0x0080;
+const sal_uInt16 BIFF_ID_HCENTER = 0x0083;
+const sal_uInt16 BIFF_ID_HEADER = 0x0014;
+const sal_uInt16 BIFF_ID_HEADERFOOTER = 0x089C;
+const sal_uInt16 BIFF_ID_HIDEOBJ = 0x008D;
+const sal_uInt16 BIFF_ID_HORPAGEBREAKS = 0x001B;
+const sal_uInt16 BIFF_ID_HYPERLINK = 0x01B8;
+const sal_uInt16 BIFF3_ID_IMGDATA = 0x007F;
+const sal_uInt16 BIFF8_ID_IMGDATA = 0x00E9;
+const sal_uInt16 BIFF2_ID_INDEX = 0x000B;
+const sal_uInt16 BIFF3_ID_INDEX = 0x020B;
+const sal_uInt16 BIFF2_ID_INTEGER = 0x0002;
+const sal_uInt16 BIFF_ID_INTERFACEHDR = 0x00E1;
+const sal_uInt16 BIFF_ID_ITERATION = 0x0011;
+const sal_uInt16 BIFF_ID_IXFE = 0x0044;
+const sal_uInt16 BIFF2_ID_LABEL = 0x0004;
+const sal_uInt16 BIFF3_ID_LABEL = 0x0204;
+const sal_uInt16 BIFF_ID_LABELRANGES = 0x015F;
+const sal_uInt16 BIFF_ID_LABELSST = 0x00FD;
+const sal_uInt16 BIFF_ID_LEFTMARGIN = 0x0026;
+const sal_uInt16 BIFF_ID_MERGEDCELLS = 0x00E5;
+const sal_uInt16 BIFF_ID_MSODRAWING = 0x00EC;
+const sal_uInt16 BIFF_ID_MSODRAWINGGROUP = 0x00EB;
+const sal_uInt16 BIFF_ID_MSODRAWINGSEL = 0x00ED;
+const sal_uInt16 BIFF_ID_MTHREADSETTINGS = 0x089A;
+const sal_uInt16 BIFF_ID_MULTBLANK = 0x00BE;
+const sal_uInt16 BIFF_ID_MULTRK = 0x00BD;
+const sal_uInt16 BIFF_ID_NOTE = 0x001C;
+const sal_uInt16 BIFF_ID_NOTESOUND = 0x0096;
+const sal_uInt16 BIFF2_ID_NUMBER = 0x0003;
+const sal_uInt16 BIFF3_ID_NUMBER = 0x0203;
+const sal_uInt16 BIFF_ID_OBJ = 0x005D;
+const sal_uInt16 BIFF_ID_OBJECTPROTECT = 0x0063;
+const sal_uInt16 BIFF_ID_OLESIZE = 0x00DE;
+const sal_uInt16 BIFF_ID_PAGELAYOUTVIEW = 0x088B;
+const sal_uInt16 BIFF_ID_PAGESETUP = 0x00A1;
+const sal_uInt16 BIFF_ID_PALETTE = 0x0092;
+const sal_uInt16 BIFF_ID_PANE = 0x0041;
+const sal_uInt16 BIFF_ID_PARAMQUERY = 0x00DC;
+const sal_uInt16 BIFF_ID_PASSWORD = 0x0013;
+const sal_uInt16 BIFF_ID_PCDFIELDINDEX = 0x0103;
+const sal_uInt16 BIFF_ID_PCDFORMULAFIELD = 0x00F9;
+const sal_uInt16 BIFF_ID_PCDSOURCE = 0x00E3;
+const sal_uInt16 BIFF_ID_PHONETICPR = 0x00EF;
+const sal_uInt16 BIFF_ID_PICTURE = 0x00E9;
+const sal_uInt16 BIFF_ID_PIVOTCACHE = 0x00D5;
+const sal_uInt16 BIFF_ID_PRECISION = 0x000E;
+const sal_uInt16 BIFF_ID_PRINTGRIDLINES = 0x002B;
+const sal_uInt16 BIFF_ID_PRINTHEADERS = 0x002A;
+const sal_uInt16 BIFF_ID_PROJEXTSHEET = 0x00A3;
+const sal_uInt16 BIFF_ID_PROTECT = 0x0012;
+const sal_uInt16 BIFF_ID_PTDATAFIELD = 0x00C5;
+const sal_uInt16 BIFF_ID_PTDEFINITION = 0x00B0;
+const sal_uInt16 BIFF_ID_PTDEFINITION2 = 0x00F1;
+const sal_uInt16 BIFF_ID_PTFIELD = 0x00B1;
+const sal_uInt16 BIFF_ID_PTFIELD2 = 0x0100;
+const sal_uInt16 BIFF_ID_PTFITEM = 0x00B2;
+const sal_uInt16 BIFF_ID_PTPAGEFIELDS = 0x00B6;
+const sal_uInt16 BIFF_ID_PTROWCOLFIELDS = 0x00B4;
+const sal_uInt16 BIFF_ID_PTROWCOLITEMS = 0x00B5;
+const sal_uInt16 BIFF_ID_QUERYTABLE = 0x01AD;
+const sal_uInt16 BIFF_ID_QUERYTABLEREFRESH = 0x0802;
+const sal_uInt16 BIFF_ID_QUERYTABLESETTINGS = 0x0803;
+const sal_uInt16 BIFF_ID_QUERYTABLESTRING = 0x0804;
+const sal_uInt16 BIFF_ID_RECALCID = 0x01C1;
+const sal_uInt16 BIFF_ID_REFMODE = 0x000F;
+const sal_uInt16 BIFF_ID_RIGHTMARGIN = 0x0027;
+const sal_uInt16 BIFF_ID_RK = 0x027E;
+const sal_uInt16 BIFF2_ID_ROW = 0x0008;
+const sal_uInt16 BIFF3_ID_ROW = 0x0208;
+const sal_uInt16 BIFF_ID_RSTRING = 0x00D6;
+const sal_uInt16 BIFF_ID_SAVERECALC = 0x005F;
+const sal_uInt16 BIFF_ID_SCENARIO = 0x00AF;
+const sal_uInt16 BIFF_ID_SCENARIOS = 0x00AE;
+const sal_uInt16 BIFF_ID_SCL = 0x00A0;
+const sal_uInt16 BIFF_ID_SCENPROTECT = 0x00DD;
+const sal_uInt16 BIFF_ID_SCREENTIP = 0x0800;
+const sal_uInt16 BIFF_ID_SELECTION = 0x001D;
+const sal_uInt16 BIFF_ID_SHAREDFEATHEAD = 0x0867;
+const sal_uInt16 BIFF_ID_SHAREDFMLA = 0x04BC;
+const sal_uInt16 BIFF_ID_SHEET = 0x0085;
+const sal_uInt16 BIFF_ID_SHEETEXT = 0x0862;
+const sal_uInt16 BIFF_ID_SHEETHEADER = 0x008F;
+const sal_uInt16 BIFF_ID_SHEETPR = 0x0081;
+const sal_uInt16 BIFF_ID_SST = 0x00FC;
+const sal_uInt16 BIFF_ID_STANDARDWIDTH = 0x0099;
+const sal_uInt16 BIFF2_ID_STRING = 0x0007;
+const sal_uInt16 BIFF3_ID_STRING = 0x0207;
+const sal_uInt16 BIFF_ID_STYLE = 0x0293;
+const sal_uInt16 BIFF_ID_STYLEEXT = 0x0892;
+const sal_uInt16 BIFF_ID_TABLESTYLES = 0x088E;
+const sal_uInt16 BIFF_ID_THEME = 0x0896;
+const sal_uInt16 BIFF_ID_TOPMARGIN = 0x0028;
+const sal_uInt16 BIFF_ID_TXO = 0x01B6;
+const sal_uInt16 BIFF_ID_UNCALCED = 0x005E;
+const sal_uInt16 BIFF_ID_USESELFS = 0x0160;
+const sal_uInt16 BIFF_ID_VBAPROJECT = 0x00D3;
+const sal_uInt16 BIFF_ID_VBAPROJECTEMPTY = 0x01BD;
+const sal_uInt16 BIFF_ID_VCENTER = 0x0084;
+const sal_uInt16 BIFF_ID_VERPAGEBREAKS = 0x001A;
+const sal_uInt16 BIFF_ID_WINDOW1 = 0x003D;
+const sal_uInt16 BIFF2_ID_WINDOW2 = 0x003E;
+const sal_uInt16 BIFF3_ID_WINDOW2 = 0x023E;
+const sal_uInt16 BIFF_ID_WRITEACCESS = 0x005C;
+const sal_uInt16 BIFF_ID_XCT = 0x0059;
+const sal_uInt16 BIFF2_ID_XF = 0x0043;
+const sal_uInt16 BIFF3_ID_XF = 0x0243;
+const sal_uInt16 BIFF4_ID_XF = 0x0443;
+const sal_uInt16 BIFF5_ID_XF = 0x00E0;
+const sal_uInt16 BIFF_ID_XFCRC = 0x087C;
+const sal_uInt16 BIFF_ID_XFEXT = 0x087D;
+
+// OBJ subrecord identifiers --------------------------------------------------
+const sal_uInt16 BIFF_ID_OBJEND = 0x0000; /// End of OBJ.
+const sal_uInt16 BIFF_ID_OBJMACRO = 0x0004; /// Macro link.
+const sal_uInt16 BIFF_ID_OBJBUTTON = 0x0005; /// Button data.
+const sal_uInt16 BIFF_ID_OBJGMO = 0x0006; /// Group marker.
+const sal_uInt16 BIFF_ID_OBJCF = 0x0007; /// Clipboard format.
+const sal_uInt16 BIFF_ID_OBJFLAGS = 0x0008; /// Option flags.
+const sal_uInt16 BIFF_ID_OBJPICTFMLA = 0x0009; /// OLE link formula.
+const sal_uInt16 BIFF_ID_OBJCBLS = 0x000A; /// Check box/radio button data.
+const sal_uInt16 BIFF_ID_OBJRBO = 0x000B; /// Radio button group data.
+const sal_uInt16 BIFF_ID_OBJSBS = 0x000C; /// Scroll bar data.
+const sal_uInt16 BIFF_ID_OBJNTS = 0x000C; /// Note data.
+const sal_uInt16 BIFF_ID_OBJSBSFMLA = 0x000E; /// Scroll bar/list box/combo box cell link.
+const sal_uInt16 BIFF_ID_OBJGBODATA = 0x000F; /// Group box data.
+const sal_uInt16 BIFF_ID_OBJEDODATA = 0x0010; /// Edit box data.
+const sal_uInt16 BIFF_ID_OBJRBODATA = 0x0011; /// Radio button group data.
+const sal_uInt16 BIFF_ID_OBJCBLSDATA = 0x0012; /// Check box/radio button data.
+const sal_uInt16 BIFF_ID_OBJLBSDATA = 0x0013; /// List box/combo box data.
+const sal_uInt16 BIFF_ID_OBJCBLSFMLA = 0x0014; /// Check box/radio button cell link.
+const sal_uInt16 BIFF_ID_OBJCMO = 0x0015; /// Common object settings.
+
+// record constants -----------------------------------------------------------
+
+const sal_uInt8 BIFF_ERR_NULL = 0x00;
+const sal_uInt8 BIFF_ERR_DIV0 = 0x07;
+const sal_uInt8 BIFF_ERR_VALUE = 0x0F;
+const sal_uInt8 BIFF_ERR_REF = 0x17;
+const sal_uInt8 BIFF_ERR_NAME = 0x1D;
+const sal_uInt8 BIFF_ERR_NUM = 0x24;
+const sal_uInt8 BIFF_ERR_NA = 0x2A;
+
+const sal_uInt16 BIFF_BOF_BIFF2 = 0x0200;
+const sal_uInt16 BIFF_BOF_BIFF3 = 0x0300;
+const sal_uInt16 BIFF_BOF_BIFF4 = 0x0400;
+const sal_uInt16 BIFF_BOF_BIFF5 = 0x0500;
+const sal_uInt16 BIFF_BOF_BIFF8 = 0x0600;
+
+const sal_uInt8 BIFF_DATATYPE_EMPTY = 0;
+const sal_uInt8 BIFF_DATATYPE_DOUBLE = 1;
+const sal_uInt8 BIFF_DATATYPE_STRING = 2;
+const sal_uInt8 BIFF_DATATYPE_BOOL = 4;
+const sal_uInt8 BIFF_DATATYPE_ERROR = 16;
+
+const sal_uInt8 BIFF_BOOLERR_BOOL = 0;
+const sal_uInt8 BIFF_BOOLERR_ERROR = 1;
+
+// BIFF8 unicode strings ------------------------------------------------------
+
+const sal_uInt8 BIFF_STRF_16BIT = 0x01;
+const sal_uInt8 BIFF_STRF_PHONETIC = 0x04;
+const sal_uInt8 BIFF_STRF_RICH = 0x08;
+const sal_uInt8 BIFF_STRF_UNKNOWN = 0xF2;
+
+/** Static helper functions for BIFF filters. */
+class BiffHelper
+{
+public:
+ // conversion -------------------------------------------------------------
+
+ /** Converts the passed packed number to a double. */
+ static double calcDoubleFromRk( sal_Int32 nRkValue );
+
+ /** Converts the passed BIFF error to a double containing the respective Calc error code. */
+ static double calcDoubleFromError( sal_uInt8 nErrorCode );
+
+ // BIFF12 import ----------------------------------------------------------
+
+ /** Reads a BIFF12 string with leading 16-bit or 32-bit length field. */
+ static OUString readString( SequenceInputStream& rStrm, bool b32BitLen = true );
+
+private:
+ BiffHelper() = delete;
+ ~BiffHelper() = delete;
+};
+
+/** BIFF12 stream operator for an OUString, reads 32-bit string length and Unicode array. */
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, OUString& orString )
+{
+ orString = BiffHelper::readString( rStrm );
+ return rStrm;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/chartsheetfragment.hxx b/sc/source/filter/inc/chartsheetfragment.hxx
new file mode 100644
index 0000000000..c2cce27c2c
--- /dev/null
+++ b/sc/source/filter/inc/chartsheetfragment.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 "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class ChartsheetFragment final : public WorksheetFragmentBase
+{
+public:
+ explicit ChartsheetFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void initializeImport() override;
+ virtual void finalizeImport() override;
+
+ /** Imports the relation identifier for the DrawingML part. */
+ void importDrawing( const AttributeList& rAttribs );
+ /** Imports the DRAWING record containing the relation identifier for the DrawingML part. */
+ void importDrawing( SequenceInputStream& rStrm );
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/colrowst.hxx b/sc/source/filter/inc/colrowst.hxx
new file mode 100644
index 0000000000..963ea96822
--- /dev/null
+++ b/sc/source/filter/inc/colrowst.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xiroot.hxx"
+#include <mdds/flat_segment_tree.hpp>
+#include <o3tl/typed_flags_set.hxx>
+
+enum class ExcColRowFlags : sal_uInt8 {
+ NONE = 0x00,
+ Used = 0x01,
+ Default = 0x02,
+ Hidden = 0x04,
+ Man = 0x08,
+};
+namespace o3tl {
+ template<> struct typed_flags<ExcColRowFlags> : is_typed_flags<ExcColRowFlags, 0x0f> {};
+}
+
+
+class XclImpColRowSettings final : protected XclImpRoot
+{
+public:
+ explicit XclImpColRowSettings( const XclImpRoot& rRoot );
+ virtual ~XclImpColRowSettings() override;
+
+ void SetDefWidth( sal_uInt16 nDefWidth, bool bStdWidthRec = false );
+ void SetWidthRange( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nWidth );
+ void HideCol( SCCOL nCol );
+ void HideColRange( SCCOL nCol1, SCCOL nCol2 );
+
+ void SetDefHeight( sal_uInt16 nDefHeight, sal_uInt16 nFlags );
+ void SetHeight( SCROW nRow, sal_uInt16 nHeight );
+ void SetRowSettings( SCROW nRow, sal_uInt16 nHeight, sal_uInt16 nFlags );
+ void SetManualRowHeight( SCROW nScRow );
+
+ void SetDefaultXF( SCCOL nScCol1, SCCOL nScCol2, sal_uInt16 nXFIndex );
+ /** Inserts all column and row settings of the specified sheet, except the hidden flags. */
+ void Convert( SCTAB nScTab );
+ /** Sets the HIDDEN flags at all hidden columns and rows in the specified sheet. */
+ void ConvertHiddenFlags( SCTAB nScTab );
+
+private:
+ void ApplyColFlag(SCCOL nCol, ExcColRowFlags nNewVal);
+ bool GetColFlag(SCCOL nCol, ExcColRowFlags nMask) const;
+
+private:
+ typedef ::mdds::flat_segment_tree<SCROW, sal_uInt16> WidthHeightStoreType;
+ typedef ::mdds::flat_segment_tree<SCROW, ExcColRowFlags> ColRowFlagsType;
+ typedef ::mdds::flat_segment_tree<SCROW, bool> RowHiddenType;
+
+ WidthHeightStoreType maColWidths;
+ ColRowFlagsType maColFlags;
+ WidthHeightStoreType maRowHeights;
+ ColRowFlagsType maRowFlags;
+ RowHiddenType maHiddenRows;
+
+ SCROW mnLastScRow;
+
+ sal_uInt16 mnDefWidth; /// Default width from DEFCOLWIDTH or STANDARDWIDTH record.
+ sal_uInt16 mnDefHeight; /// Default height from DEFAULTROWHEIGHT record.
+ sal_uInt16 mnDefRowFlags; /// Default row flags from DEFAULTROWHEIGHT record.
+
+ bool mbHasStdWidthRec; /// true = Width from STANDARDWIDTH (overrides DEFCOLWIDTH record).
+ bool mbHasDefHeight; /// true = mnDefHeight and mnDefRowFlags are valid.
+ bool mbDirty;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/commentsbuffer.hxx b/sc/source/filter/inc/commentsbuffer.hxx
new file mode 100644
index 0000000000..c30d6765d6
--- /dev/null
+++ b/sc/source/filter/inc/commentsbuffer.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 "richstring.hxx"
+#include "worksheethelper.hxx"
+#include <com/sun/star/awt/Rectangle.hpp>
+
+namespace oox::xls {
+
+struct CommentModel
+{
+ ScRange maRange; /// Position of the comment in the worksheet.
+ RichStringRef mxText; /// Formatted text of the comment (not used in BIFF8).
+ sal_Int32 mnAuthorId; /// Identifier of the comment's author (OOXML and BIFF12 only).
+ bool mbAutoFill; /// Auto Selection of comment object's fill style
+ bool mbAutoScale; /// Auto Scale comment text
+ bool mbColHidden; /// Comment cell's Column is Hidden
+ bool mbLocked; /// Comment changes Locked
+ bool mbRowHidden; /// Comment cell's Row is Hidden
+ sal_Int32 mnTHA; /// Horizontal Alignment
+ sal_Int32 mnTVA; /// Vertical Alignment
+ css::awt::Rectangle maAnchor; /// Anchor parameters
+
+ explicit CommentModel();
+};
+
+class Comment final : public WorksheetHelper
+{
+public:
+ explicit Comment( const WorksheetHelper& rHelper );
+
+ /** Imports a cell comment from the passed attributes of the comment element. */
+ void importComment( const AttributeList& rAttribs );
+ /** Imports a cell comment Properties from the passed attributes of the comment element. */
+ void importCommentPr( const AttributeList& rAttribs );
+ /** Imports a cell comment from the passed stream of a COMMENT record. */
+ void importComment( SequenceInputStream& rStrm );
+
+ /** Creates and returns a new rich-string object for the comment text. */
+ RichStringRef const & createText();
+
+ /** Finalizes the formatted string of the comment. */
+ void finalizeImport();
+
+private:
+ CommentModel maModel;
+};
+
+typedef std::shared_ptr< Comment > CommentRef;
+
+class CommentsBuffer final : public WorksheetHelper
+{
+public:
+ explicit CommentsBuffer( const WorksheetHelper& rHelper );
+
+ /** Appends a new author to the list of comment authors. */
+ void appendAuthor( const OUString& rAuthor );
+ /** Creates and returns a new comment. */
+ CommentRef createComment();
+
+ /** Finalizes the formatted string of all comments. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< Comment > CommentVector;
+
+ std::vector< OUString > maAuthors;
+ CommentVector maComments;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/commentsfragment.hxx b/sc/source/filter/inc/commentsfragment.hxx
new file mode 100644
index 0000000000..4acf4b0d88
--- /dev/null
+++ b/sc/source/filter/inc/commentsfragment.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "commentsbuffer.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class CommentsFragment final : public WorksheetFragmentBase
+{
+public:
+ explicit CommentsFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onEndRecord() override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+ /** Imports comment data from the comment element. */
+ void importComment( const AttributeList& rAttribs );
+ /** Imports comment data from the COMMENT record. */
+ void importComment( SequenceInputStream& rStrm );
+
+ CommentRef mxComment;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/condformatbuffer.hxx b/sc/source/filter/inc/condformatbuffer.hxx
new file mode 100644
index 0000000000..9c8896dbf2
--- /dev/null
+++ b/sc/source/filter/inc/condformatbuffer.hxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "worksheethelper.hxx"
+#include <tools/color.hxx>
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <rangelst.hxx>
+
+#include <memory>
+#include <vector>
+
+class ScColorScaleFormat;
+class ScDataBarFormat;
+struct ScDataBarFormatData;
+class ScIconSetFormat;
+struct ScIconSetFormatData;
+
+namespace oox { class AttributeList; }
+
+namespace oox::xls {
+
+class CondFormat;
+
+/** Model for a single rule in a conditional formatting. */
+struct CondFormatRuleModel
+{
+ typedef ::std::vector< ApiTokenSequence > ApiTokenSequenceVector;
+
+ ApiTokenSequenceVector maFormulas; /// Formulas for rule conditions.
+ OUString maText; /// Text for 'contains' rules.
+ sal_Int32 mnPriority; /// Priority of this rule.
+ sal_Int32 mnType; /// Type of the rule.
+ sal_Int32 mnOperator; /// In cell-is rules: Comparison operator.
+ sal_Int32 mnTimePeriod; /// In time-period rules: Type of time period.
+ sal_Int32 mnRank; /// In top-10 rules: True = bottom, false = top.
+ sal_Int32 mnStdDev; /// In average rules: Number of std deviations.
+ sal_Int32 mnDxfId; /// Differential formatting identifier.
+ bool mbStopIfTrue; /// True = stop evaluating rules, if this rule is true.
+ bool mbBottom; /// In top-10 rules: True = bottom, false = top.
+ bool mbPercent; /// In top-10 rules: True = percent, false = rank.
+ bool mbAboveAverage; /// In average rules: True = above average, false = below.
+ bool mbEqualAverage; /// In average rules: True = include average, false = exclude.
+
+ explicit CondFormatRuleModel();
+
+ /** Sets the passed BIFF operator for condition type cellIs. */
+ void setBiffOperator( sal_Int32 nOperator );
+
+ /** Sets the passed BIFF12 text comparison type and operator. */
+ void setBiff12TextType( sal_Int32 nOperator );
+};
+
+struct ColorScaleRuleModelEntry
+{
+ ::Color maColor;
+ double mnVal;
+
+ bool mbMin;
+ bool mbMax;
+ bool mbPercent;
+ bool mbPercentile;
+ bool mbNum;
+ OUString maFormula;
+
+ ColorScaleRuleModelEntry():
+ maColor(),
+ mnVal(0),
+ mbMin(false),
+ mbMax(false),
+ mbPercent(false),
+ mbPercentile(false),
+ mbNum(false) {}
+};
+
+class ColorScaleRule final : public WorksheetHelper
+{
+public:
+ ColorScaleRule( const CondFormat& rFormat );
+
+ void importCfvo( const AttributeList& rAttribs );
+ void importColor( const AttributeList& rAttribs );
+
+ void AddEntries( ScColorScaleFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr );
+
+private:
+ std::vector< ColorScaleRuleModelEntry > maColorScaleRuleEntries;
+
+ sal_uInt32 mnCfvo;
+ sal_uInt32 mnCol;
+};
+
+class DataBarRule final : public WorksheetHelper
+{
+public:
+ DataBarRule( const CondFormat& rFormat );
+ void importCfvo( const AttributeList& rAttribs );
+ void importColor( const AttributeList& rAttribs );
+ void importAttribs( const AttributeList& rAttribs );
+
+ void SetData( ScDataBarFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr );
+
+ ScDataBarFormatData* getDataBarFormatData() { return mxFormat.get(); }
+
+private:
+ std::unique_ptr<ScDataBarFormatData> mxFormat;
+
+ std::unique_ptr<ColorScaleRuleModelEntry> mpUpperLimit;
+ std::unique_ptr<ColorScaleRuleModelEntry> mpLowerLimit;
+};
+
+class IconSetRule final : public WorksheetHelper
+{
+public:
+ IconSetRule( const WorksheetHelper& rParent );
+ void importCfvo( const AttributeList& rAttribs );
+ void importAttribs( const AttributeList& rAttribs );
+ void importFormula(const OUString& rFormula);
+ void importIcon(const AttributeList& rAttribs);
+
+ void SetData( ScIconSetFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr );
+
+private:
+ std::vector< ColorScaleRuleModelEntry > maEntries;
+ std::unique_ptr<ScIconSetFormatData> mxFormatData;
+ OUString maIconSetType;
+ bool mbCustom;
+};
+
+/** Represents a single rule in a conditional formatting. */
+class CondFormatRule final : public WorksheetHelper
+{
+friend class CondFormatBuffer;
+public:
+ explicit CondFormatRule( const CondFormat& rCondFormat, ScConditionalFormat* pFormat );
+
+ /** Imports rule settings from the cfRule element. */
+ void importCfRule( const AttributeList& rAttribs );
+ /** Appends a new condition formula string. */
+ void appendFormula( const OUString& rFormula );
+
+ /** Imports rule settings from a CFRULE record. */
+ void importCfRule( SequenceInputStream& rStrm );
+
+ /** Directly set a ScFormatEntry with a priority ready for finalizeImport(). */
+ void setFormatEntry(sal_Int32 nPriority, ScFormatEntry* pEntry);
+
+ /** Creates a conditional formatting rule in the Calc document. */
+ void finalizeImport();
+
+ /** Returns the priority of this rule. */
+ sal_Int32 getPriority() const { return maModel.mnPriority; }
+
+ ColorScaleRule* getColorScale();
+ DataBarRule* getDataBar();
+ IconSetRule* getIconSet();
+
+private:
+ const CondFormat& mrCondFormat;
+ CondFormatRuleModel maModel;
+ ScConditionalFormat* mpFormat;
+ ScFormatEntry* mpFormatEntry;
+ std::unique_ptr<ColorScaleRule> mpColor;
+ std::unique_ptr<DataBarRule> mpDataBar;
+ std::unique_ptr<IconSetRule> mpIconSet;
+};
+
+typedef std::shared_ptr< CondFormatRule > CondFormatRuleRef;
+
+/** Model for a conditional formatting object. */
+struct CondFormatModel
+{
+ ScRangeList maRanges; /// Cell ranges for this conditional format.
+ bool mbPivot; /// Conditional formatting belongs to pivot table.
+
+ explicit CondFormatModel();
+};
+
+class CondFormatBuffer;
+
+/** Represents a conditional formatting object with a list of affected cell ranges. */
+class CondFormat final : public WorksheetHelper
+{
+friend class CondFormatBuffer;
+public:
+ explicit CondFormat( const WorksheetHelper& rHelper );
+ ~CondFormat();
+
+ /** Imports settings from the conditionalFormatting element. */
+ void importConditionalFormatting( const AttributeList& rAttribs );
+ /** Imports a conditional formatting rule from the cfRule element. */
+ CondFormatRuleRef importCfRule( const AttributeList& rAttribs );
+
+ /** Imports settings from the CONDFORMATTING record. */
+ void importCondFormatting( SequenceInputStream& rStrm );
+ /** Imports a conditional formatting rule from the CFRULE record. */
+ void importCfRule( SequenceInputStream& rStrm );
+
+ /** Creates the conditional formatting in the Calc document. */
+ void finalizeImport();
+
+ /** Returns the cell ranges this conditional formatting belongs to. */
+ const ScRangeList& getRanges() const { return maModel.maRanges; }
+
+ void setReadyForFinalize() { mbReadyForFinalize = true; }
+ void insertRule( CondFormatRuleRef const & xRule );
+private:
+ CondFormatRuleRef createRule();
+
+private:
+ typedef RefMap< sal_Int32, CondFormatRule > CondFormatRuleMap;
+
+ CondFormatModel maModel; /// Model of this conditional formatting.
+ CondFormatRuleMap maRules; /// Maps formatting rules by priority.
+ ScConditionalFormat* mpFormat;
+ bool mbReadyForFinalize;
+};
+
+struct ExCfRuleModel
+{
+ ExCfRuleModel() : mnAxisColor( ColorTransparency, UNSIGNED_RGB_TRANSPARENT ), mnNegativeColor( ColorTransparency, UNSIGNED_RGB_TRANSPARENT ), mbGradient( false ), mbIsLower( true ) {}
+ // AxisColor
+ ::Color mnAxisColor;
+ ::Color mnPositiveColor;
+ // NegativeFillColor
+ ::Color mnNegativeColor;
+ OUString maAxisPosition; // DataBar
+ OUString maColorScaleType; // Cfvo
+ OUString msScaleTypeValue; // Cfvo
+ bool mbGradient; // DataBar
+ bool mbIsLower; // Cfvo
+};
+
+class ExtCfDataBarRule : public WorksheetHelper
+{
+ enum RuleType
+ {
+ DATABAR,
+ POSITIVEFILLCOLOR,
+ NEGATIVEFILLCOLOR,
+ AXISCOLOR,
+ CFVO,
+ UNKNOWN,
+ };
+ ExCfRuleModel maModel;
+ RuleType mnRuleType;
+ ScDataBarFormatData* mpTarget;
+public:
+
+ ExtCfDataBarRule(ScDataBarFormatData* pTarget, const WorksheetHelper& rParent);
+ void finalizeImport();
+ void importDataBar( const AttributeList& rAttribs );
+ void importPositiveFillColor( const AttributeList& rAttribs );
+ void importNegativeFillColor( const AttributeList& rAttribs );
+ void importAxisColor( const AttributeList& rAttribs );
+ void importCfvo( const AttributeList& rAttribs );
+ ExCfRuleModel& getModel() { return maModel; }
+ const ScDataBarFormatData* GetDataBarData() { return mpTarget; }
+};
+
+class ExtCfCondFormat
+{
+public:
+ ExtCfCondFormat(ScRangeList aRange, std::vector< std::unique_ptr<ScFormatEntry> >& rEntries,
+ const std::vector<sal_Int32>* pPriorities = nullptr);
+ ~ExtCfCondFormat();
+
+ const ScRangeList& getRange() const;
+ const std::vector< std::unique_ptr<ScFormatEntry> >& getEntries() const;
+ const std::vector<sal_Int32>& getPriorities() const { return maPriorities; }
+
+private:
+ std::vector< std::unique_ptr<ScFormatEntry> > maEntries;
+ std::vector<sal_Int32> maPriorities;
+ ScRangeList maRange;
+};
+
+typedef std::shared_ptr< CondFormat > CondFormatRef;
+typedef std::shared_ptr< ExtCfDataBarRule > ExtCfDataBarRuleRef;
+
+class CondFormatBuffer final : public WorksheetHelper
+{
+public:
+ explicit CondFormatBuffer( const WorksheetHelper& rHelper );
+
+ /** Imports settings from the conditionalFormatting element. */
+ CondFormatRef importConditionalFormatting( const AttributeList& rAttribs );
+ /** Imports settings from the CONDFORMATTING record. */
+ CondFormatRef importCondFormatting( SequenceInputStream& rStrm );
+ ExtCfDataBarRuleRef createExtCfDataBarRule(ScDataBarFormatData* pTarget);
+ std::vector< std::unique_ptr<ExtCfCondFormat> >& importExtCondFormat();
+ std::vector<std::unique_ptr<ScFormatEntry> >& importExtFormatEntries();
+
+ /** Converts an OOXML condition operator token to the API constant. */
+ static sal_Int32 convertToApiOperator( sal_Int32 nToken );
+ static ScConditionMode convertToInternalOperator( sal_Int32 nToken );
+ void finalizeImport();
+ bool insertRule(CondFormatRef const & xCondFmt, CondFormatRuleRef const & xRule);
+
+private:
+ CondFormatRef createCondFormat();
+ void updateImport(const ScDataBarFormatData* pTarget);
+
+private:
+ typedef RefVector< CondFormat > CondFormatVec;
+ typedef RefVector< ExtCfDataBarRule > ExtCfDataBarRuleVec;
+ CondFormatVec maCondFormats; /// All conditional formatting in a sheet.
+ ExtCfDataBarRuleVec maCfRules; /// All external conditional formatting rules in a sheet.
+ std::vector< std::unique_ptr<ExtCfCondFormat> > maExtCondFormats;
+ std::vector<std::unique_ptr<ScFormatEntry> > maExtFormatEntries;
+ sal_Int32 mnNonPrioritizedRuleNextPriority = 1048576;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/condformatcontext.hxx b/sc/source/filter/inc/condformatcontext.hxx
new file mode 100644
index 0000000000..351d2541b8
--- /dev/null
+++ b/sc/source/filter/inc/condformatcontext.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 .
+ */
+
+#pragma once
+
+#include "condformatbuffer.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class CondFormatContext;
+
+class ColorScaleContext final : public WorksheetContextBase
+{
+public:
+ explicit ColorScaleContext( CondFormatContext& rFragment, CondFormatRuleRef xRule );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+ CondFormatRuleRef mxRule;
+};
+
+class DataBarContext final : public WorksheetContextBase
+{
+public:
+ explicit DataBarContext( CondFormatContext& rFormat, CondFormatRuleRef xRule );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+ CondFormatRuleRef mxRule;
+};
+
+class IconSetContext final : public WorksheetContextBase
+{
+public:
+ explicit IconSetContext( WorksheetContextBase& rParent, IconSetRule* pIconSet );
+
+ virtual oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters(const OUString& rChars) override;
+ virtual void onEndElement() override;
+
+private:
+ IconSetRule* mpIconSet;
+ OUString maChars;
+};
+
+class CondFormatContext final : public WorksheetContextBase
+{
+public:
+ explicit CondFormatContext( WorksheetFragmentBase& rFragment );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+ virtual void onEndRecord() override;
+
+ CondFormatRef mxCondFmt;
+ CondFormatRuleRef mxRule;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/connectionsbuffer.hxx b/sc/source/filter/inc/connectionsbuffer.hxx
new file mode 100644
index 0000000000..9308da5a36
--- /dev/null
+++ b/sc/source/filter/inc/connectionsbuffer.hxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+const sal_Int32 BIFF12_CONNECTION_UNKNOWN = 0;
+const sal_Int32 BIFF12_CONNECTION_ODBC = 1;
+const sal_Int32 BIFF12_CONNECTION_DAO = 2;
+const sal_Int32 BIFF12_CONNECTION_FILE = 3;
+const sal_Int32 BIFF12_CONNECTION_HTML = 4;
+const sal_Int32 BIFF12_CONNECTION_OLEDB = 5;
+const sal_Int32 BIFF12_CONNECTION_TEXT = 6;
+const sal_Int32 BIFF12_CONNECTION_ADO = 7;
+const sal_Int32 BIFF12_CONNECTION_DSP = 8;
+
+/** Special properties for data connections representing web queries. */
+struct WebPrModel
+{
+ typedef ::std::vector< css::uno::Any > TablesVector;
+
+ TablesVector maTables; /// Names or indexes of the web query tables.
+ OUString maUrl; /// Source URL to refresh the data.
+ OUString maPostMethod; /// POST method to query data.
+ OUString maEditPage; /// Web page showing query data (for XML queries).
+ sal_Int32 mnHtmlFormat; /// Plain text, rich text, or HTML.
+ bool mbXml; /// True = XML query, false = HTML query.
+ bool mbSourceData; /// True = import XML source data referred by HTML table.
+ bool mbParsePre; /// True = parse preformatted sections (<pre> tag).
+ bool mbConsecutive; /// True = join consecutive delimiters.
+ bool mbFirstRow; /// True = use column widths of first row for entire <pre> tag.
+ bool mbXl97Created; /// True = web query created with Excel 97.
+ bool mbTextDates; /// True = read date values as text, false = parse dates.
+ bool mbXl2000Refreshed; /// True = refreshed with Excel 2000 or newer.
+ bool mbHtmlTables; /// True = HTML tables, false = entire document.
+
+ explicit WebPrModel();
+};
+
+/** Common properties of an external data connection. */
+struct ConnectionModel
+{
+ typedef ::std::unique_ptr< WebPrModel > WebPrModelPtr;
+
+ WebPrModelPtr mxWebPr; /// Special settings for web queries.
+ OUString maName; /// Unique name of this connection.
+ OUString maDescription; /// User description of this connection.
+ OUString maSourceFile; /// URL of a source data file.
+ OUString maSourceConnFile; /// URL of a source connection file.
+ OUString maSsoId; /// Single sign-on identifier.
+ sal_Int32 mnId; /// Unique connection identifier.
+ sal_Int32 mnType; /// Data source type.
+ sal_Int32 mnReconnectMethod; /// Reconnection method.
+ sal_Int32 mnCredentials; /// Credentials method.
+ sal_Int32 mnInterval; /// Refresh interval in minutes.
+ bool mbKeepAlive; /// True = keep connection open after import.
+ bool mbNew; /// True = new connection, never updated.
+ bool mbDeleted; /// True = connection has been deleted.
+ bool mbOnlyUseConnFile; /// True = use maSourceConnFile, ignore mnReconnectMethod.
+ bool mbBackground; /// True = background refresh enabled.
+ bool mbRefreshOnLoad; /// True = refresh connection on import.
+ bool mbSaveData; /// True = save cached data with connection.
+ bool mbSavePassword; /// True = save password in connection string.
+
+ explicit ConnectionModel();
+
+ WebPrModel& createWebPr();
+};
+
+/** An external data connection (database, web query, etc.). */
+class Connection final : public WorkbookHelper
+{
+public:
+ explicit Connection( const WorkbookHelper& rHelper );
+
+ /** Imports connection settings from the connection element. */
+ void importConnection( const AttributeList& rAttribs );
+ /** Imports web query settings from the webPr element. */
+ void importWebPr( const AttributeList& rAttribs );
+ /** Imports web query table settings from the tables element. */
+ void importTables();
+ /** Imports a web query table identifier from the m, s, or x element. */
+ void importTable( const AttributeList& rAttribs, sal_Int32 nElement );
+
+ /** Imports connection settings from the CONNECTION record. */
+ void importConnection( SequenceInputStream& rStrm );
+ /** Imports web query settings from the WEBPR record. */
+ void importWebPr( SequenceInputStream& rStrm );
+ /** Imports web query table settings from the WEBPRTABLES record. */
+ void importWebPrTables( SequenceInputStream& rStrm );
+ /** Imports a web query table identifier from the PCITEM_MISSING, PCITEM_STRING, or PCITEM_INDEX record. */
+ void importWebPrTable( SequenceInputStream& rStrm, sal_Int32 nRecId );
+
+ /** Returns the unique connection identifier. */
+ sal_Int32 getConnectionId() const { return maModel.mnId; }
+ /** Returns the source data type of the connection. */
+ sal_Int32 getConnectionType() const { return maModel.mnType; }
+ /** Returns read-only access to the connection model data. */
+ const ConnectionModel& getModel() const { return maModel; }
+
+private:
+ ConnectionModel maModel;
+};
+
+typedef std::shared_ptr< Connection > ConnectionRef;
+
+class ConnectionsBuffer final : public WorkbookHelper
+{
+public:
+ explicit ConnectionsBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty connection. */
+ Connection& createConnection();
+
+ /** Maps all connections by their identifier. */
+ void finalizeImport();
+
+ /** Returns a data connection by its unique identifier. */
+ ConnectionRef getConnection( sal_Int32 nConnId ) const;
+
+private:
+ /** Inserts the passed connection into the map according to its identifier. */
+ void insertConnectionToMap( const ConnectionRef& rxConnection );
+
+private:
+ typedef RefVector< Connection > ConnectionVector;
+ typedef RefMap< sal_Int32, Connection > ConnectionMap;
+
+ ConnectionVector maConnections;
+ ConnectionMap maConnectionsById;
+ sal_Int32 mnUnusedId;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/connectionsfragment.hxx b/sc/source/filter/inc/connectionsfragment.hxx
new file mode 100644
index 0000000000..8eaec1c410
--- /dev/null
+++ b/sc/source/filter/inc/connectionsfragment.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 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class Connection;
+
+class ConnectionContext final : public WorkbookContextBase
+{
+public:
+ explicit ConnectionContext( WorkbookFragmentBase& rParent, Connection& rConnection );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ Connection& mrConnection;
+};
+
+class ConnectionsFragment final : public WorkbookFragmentBase
+{
+public:
+ explicit ConnectionsFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/decl.h b/sc/source/filter/inc/decl.h
new file mode 100644
index 0000000000..0cfbc239c0
--- /dev/null
+++ b/sc/source/filter/inc/decl.h
@@ -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 .
+ */
+
+
+#pragma once
+
+enum WKTYP { eWK_UNKNOWN = -2, eWK_1 = 0, eWK_2, eWK3, eWK4, eWK_Error, eWK123 };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/defnamesbuffer.hxx b/sc/source/filter/inc/defnamesbuffer.hxx
new file mode 100644
index 0000000000..ade1236827
--- /dev/null
+++ b/sc/source/filter/inc/defnamesbuffer.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "workbookhelper.hxx"
+#include <oox/helper/binarystreambase.hxx>
+#include <oox/helper/refvector.hxx>
+
+#include <memory>
+
+class ScTokenArray;
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+// codes for built-in names
+const sal_Unicode BIFF_DEFNAME_CONSOLIDATEAREA = '\x00';
+const sal_Unicode BIFF_DEFNAME_AUTOOPEN = '\x01'; // Sheet macro executed when workbook is opened.
+const sal_Unicode BIFF_DEFNAME_AUTOCLOSE = '\x02'; // Sheet macro executed when workbook is closed.
+const sal_Unicode BIFF_DEFNAME_EXTRACT = '\x03'; // Filter output destination for advanced filter.
+const sal_Unicode BIFF_DEFNAME_DATABASE = '\x04';
+const sal_Unicode BIFF_DEFNAME_CRITERIA = '\x05'; // Filter criteria source range for advanced filter.
+const sal_Unicode BIFF_DEFNAME_PRINTAREA = '\x06'; // Print ranges.
+const sal_Unicode BIFF_DEFNAME_PRINTTITLES = '\x07'; // Rows/columns repeated on each page when printing.
+const sal_Unicode BIFF_DEFNAME_RECORDER = '\x08';
+const sal_Unicode BIFF_DEFNAME_DATAFORM = '\x09';
+const sal_Unicode BIFF_DEFNAME_AUTOACTIVATE = '\x0A'; // Sheet macro executed when workbook is activated.
+const sal_Unicode BIFF_DEFNAME_AUTODEACTIVATE = '\x0B'; // Sheet macro executed when workbook is deactivated.
+const sal_Unicode BIFF_DEFNAME_SHEETTITLE = '\x0C';
+const sal_Unicode BIFF_DEFNAME_FILTERDATABASE = '\x0D'; // Sheet range autofilter or advanced filter works on.
+const sal_Unicode BIFF_DEFNAME_UNKNOWN = '\x0E';
+
+struct DefinedNameModel
+{
+ OUString maName; /// The original name.
+ OUString maFormula; /// The formula string.
+ sal_Int32 mnSheet; /// Sheet index for local names.
+ sal_Int32 mnFuncGroupId; /// Function group identifier.
+ bool mbMacro; /// True = Macro name (VBA or sheet macro).
+ bool mbFunction; /// True = function, false = command.
+ bool mbVBName; /// True = VBA macro, false = sheet macro.
+ bool mbHidden; /// True = name hidden in UI.
+
+ explicit DefinedNameModel();
+};
+
+/** Base class for defined names and external names. */
+class DefinedNameBase : public WorkbookHelper
+{
+public:
+ explicit DefinedNameBase( const WorkbookHelper& rHelper );
+
+ /** Returns the original name as imported from or exported to the file. */
+ const OUString& getModelName() const { return maModel.maName; }
+ /** Returns the name as used in the Calc document. */
+ const OUString& getCalcName() const { return maCalcName; }
+
+ /** Returns the original name as imported from or exported to the file. */
+ const OUString& getUpcaseModelName() const;
+
+protected:
+ DefinedNameModel maModel; /// Model data for this defined name.
+ mutable OUString maUpModelName; /// Model name converted to uppercase ASCII.
+ OUString maCalcName; /// Final name used in the Calc document.
+};
+
+class DefinedName final : public DefinedNameBase
+{
+public:
+ explicit DefinedName( const WorkbookHelper& rHelper );
+ virtual ~DefinedName() override;
+
+ /** Sets the attributes for this defined name from the passed attribute set. */
+ void importDefinedName( const AttributeList& rAttribs );
+ /** Sets the formula string from the body of the definedName element. */
+ void setFormula( const OUString& rFormula );
+ /** Imports the defined name from a DEFINEDNAME record in the passed stream. */
+ void importDefinedName( SequenceInputStream& rStrm );
+
+ /** Creates a defined name in the Calc document. */
+ void createNameObject( sal_Int32 nIndex );
+ /** Converts the formula string or BIFF token array for this defined name. */
+ void convertFormula( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks );
+ std::unique_ptr<ScTokenArray> getScTokens( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks );
+ /** Returns true, if this defined name is global in the document. */
+ bool isGlobalName() const { return mnCalcSheet < 0; }
+ /** Returns true, if this defined name is a special builtin name. */
+ bool isBuiltinName() const { return mcBuiltinId != BIFF_DEFNAME_UNKNOWN; }
+ /** Returns true, if this defined name is a macro function call. */
+ bool isMacroFunction() const { return maModel.mbMacro && maModel.mbFunction; }
+ /** Returns true, if this defined name is a reference to a VBA macro. */
+ bool isVBName() const { return maModel.mbMacro && maModel.mbVBName; }
+
+ /** Returns the 0-based sheet index for local names, or -1 for global names. */
+ sal_Int16 getLocalCalcSheet() const { return mnCalcSheet; }
+ /** Returns the built-in identifier of the defined name. */
+ sal_Unicode getBuiltinId() const { return mcBuiltinId; }
+ /** Returns the token index used in API token arrays (com.sun.star.sheet.FormulaToken). */
+ sal_Int32 getTokenIndex() const { return mnTokenIndex; }
+ /** Tries to resolve the defined name to an absolute cell range. */
+ bool getAbsoluteRange( ScRange& orRange ) const;
+ bool isValid(const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks) const;
+
+private:
+ typedef ::std::unique_ptr< StreamDataSequence > StreamDataSeqPtr;
+
+ RangeDataRet maScRangeData; /// ScRangeData of the defined name.
+ sal_Int32 mnTokenIndex; /// Name index used in API token array.
+ sal_Int16 mnCalcSheet; /// Calc sheet index for sheet-local names.
+ sal_Unicode mcBuiltinId; /// Identifier for built-in defined names.
+ StreamDataSeqPtr mxFormula; /// Formula data for BIFF12 import.
+};
+
+typedef std::shared_ptr< DefinedName > DefinedNameRef;
+
+class DefinedNamesBuffer final : public WorkbookHelper
+{
+public:
+ explicit DefinedNamesBuffer( const WorkbookHelper& rHelper );
+
+ /** Imports a defined name from the passed attribute set. */
+ DefinedNameRef importDefinedName( const AttributeList& rAttribs );
+ /** Imports a defined name from a DEFINEDNAME record in the passed stream. */
+ void importDefinedName( SequenceInputStream& rStrm );
+
+ /** Creates all defined names in the document. */
+ void finalizeImport();
+
+ /** Returns a defined name by zero-based index (order of appearance). */
+ DefinedNameRef getByIndex( sal_Int32 nIndex ) const;
+ /** Returns a defined name by token index (index in XDefinedNames container). */
+ DefinedNameRef getByTokenIndex( sal_Int32 nIndex ) const;
+ /** Returns a defined name by its model name.
+ @param nSheet The sheet index for local names or -1 for global names.
+ If no local name is found, tries to find a matching global name.
+ @return Reference to the defined name or empty reference. */
+ DefinedNameRef getByModelName( const OUString& rModelName, sal_Int16 nCalcSheet = -1 ) const;
+ /** Returns a built-in defined name by its built-in identifier.
+ @param nSheet The sheet index of the built-in name.
+ @return Reference to the defined name or empty reference. */
+ DefinedNameRef getByBuiltinId( sal_Unicode cBuiltinId, sal_Int16 nCalcSheet ) const;
+
+private:
+ DefinedNameRef createDefinedName();
+
+private:
+ typedef ::std::pair< sal_Int16, OUString > SheetNameKey;
+ typedef ::std::pair< sal_Int16, sal_Unicode > BuiltinKey;
+
+ typedef RefVector< DefinedName > DefNameVector;
+ typedef RefMap< SheetNameKey, DefinedName > DefNameNameMap;
+ typedef RefMap< BuiltinKey, DefinedName > DefNameBuiltinMap;
+ typedef RefMap< sal_Int32, DefinedName > DefNameTokenIdMap;
+
+ DefNameVector maDefNames; /// List of all defined names in insertion order.
+ DefNameNameMap maModelNameMap; /// Maps all defined names by sheet index and model name.
+ DefNameBuiltinMap maBuiltinMap; /// Maps all defined names by sheet index and built-in identifier.
+ DefNameTokenIdMap maTokenIdMap; /// Maps all defined names by API token index.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/dif.hxx b/sc/source/filter/inc/dif.hxx
new file mode 100644
index 0000000000..594327058a
--- /dev/null
+++ b/sc/source/filter/inc/dif.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <types.hxx>
+
+class SvStream;
+class SvNumberFormatter;
+class ScDocument;
+
+extern const std::u16string_view pKeyTABLE;
+extern const std::u16string_view pKeyVECTORS;
+extern const std::u16string_view pKeyTUPLES;
+extern const std::u16string_view pKeyDATA;
+extern const std::u16string_view pKeyBOT;
+extern const std::u16string_view pKeyEOD;
+
+enum TOPIC
+{
+ T_UNKNOWN,
+ T_TABLE, T_VECTORS, T_TUPLES, T_DATA, T_LABEL, T_COMMENT, T_SIZE,
+ T_PERIODICITY, T_MAJORSTART, T_MINORSTART, T_TRUELENGTH, T_UINITS,
+ T_DISPLAYUNITS,
+ T_END
+};
+
+enum DATASET { D_BOT, D_EOD, D_NUMERIC, D_STRING, D_UNKNOWN, D_SYNT_ERROR };
+
+class DifParser
+{
+public:
+ OUStringBuffer m_aData;
+ double fVal;
+ sal_uInt32 nVector;
+ sal_uInt32 nVal;
+ sal_uInt32 nNumFormat;
+private:
+ SvNumberFormatter* pNumFormatter;
+ SvStream& rIn;
+ OUString aLookAheadLine;
+
+ bool ReadNextLine( OUString& rStr );
+ bool LookAhead();
+ DATASET GetNumberDataset( const sal_Unicode* pPossibleNumericData );
+ static inline bool IsBOT( const sal_Unicode* pRef );
+ static inline bool IsEOD( const sal_Unicode* pRef );
+ static inline bool Is1_0( const sal_Unicode* pRef );
+public:
+ DifParser( SvStream&, const ScDocument&, rtl_TextEncoding );
+
+ TOPIC GetNextTopic();
+
+ DATASET GetNextDataset();
+
+ static const sal_Unicode* ScanIntVal( const sal_Unicode* pStart, sal_uInt32& rRet );
+
+ static inline bool IsNumber( const sal_Unicode cChar );
+
+ static inline bool IsV( const sal_Unicode* pRef );
+};
+
+inline bool DifParser::IsBOT( const sal_Unicode* pRef )
+{
+ return pRef == pKeyBOT;
+}
+
+inline bool DifParser::IsEOD( const sal_Unicode* pRef )
+{
+ return pRef == pKeyEOD;
+}
+
+inline bool DifParser::Is1_0( const sal_Unicode* pRef )
+{
+ return pRef == std::u16string_view(u"1,0");
+}
+
+inline bool DifParser::IsV( const sal_Unicode* pRef )
+{
+ return pRef == std::u16string_view(u"V");
+}
+
+inline bool DifParser::IsNumber( const sal_Unicode cChar )
+{
+ return ( cChar >= '0' && cChar <= '9' );
+}
+
+class DifColumn
+{
+ friend class DifAttrCache;
+
+ struct ENTRY
+ {
+ sal_uInt32 nNumFormat;
+ SCROW nStart;
+ SCROW nEnd;
+ };
+
+ ENTRY *mpCurrent;
+ std::vector<ENTRY> maEntries;
+
+ DifColumn();
+
+ void SetNumFormat( const ScDocument* pDoc, SCROW nRow, const sal_uInt32 nNumFormat );
+
+ void NewEntry( const SCROW nPos, const sal_uInt32 nNumFormat );
+
+ void Apply( ScDocument &rDoc, const SCCOL nCol, const SCTAB nTab );
+};
+
+class DifAttrCache
+{
+public:
+
+ DifAttrCache();
+
+ ~DifAttrCache();
+
+ void SetNumFormat( const ScDocument* pDoc, const SCCOL nCol, const SCROW nRow, const sal_uInt32 nNumFormat );
+
+ void Apply( ScDocument&, SCTAB nTab );
+
+private:
+
+ std::map<SCCOL, std::unique_ptr<DifColumn>> maColMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/drawingbase.hxx b/sc/source/filter/inc/drawingbase.hxx
new file mode 100644
index 0000000000..576a3619db
--- /dev/null
+++ b/sc/source/filter/inc/drawingbase.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/drawingml/drawingmltypes.hxx>
+#include "worksheethelper.hxx"
+
+namespace oox { class AttributeList; }
+
+namespace oox::xls {
+
+/** Absolute position in a spreadsheet (in EMUs) independent from cells. */
+struct AnchorPointModel : public ::oox::drawingml::EmuPoint
+{
+ explicit AnchorPointModel() : ::oox::drawingml::EmuPoint( -1, -1 ) {}
+ bool isValid() const { return (X >= 0) && (Y >= 0); }
+};
+
+/** Absolute size in a spreadsheet (in EMUs). */
+struct AnchorSizeModel : public ::oox::drawingml::EmuSize
+{
+ explicit AnchorSizeModel() : ::oox::drawingml::EmuSize( -1, -1 ) {}
+ bool isValid() const { return (Width >= 0) && (Height >= 0); }
+};
+
+/** Position in spreadsheet (cell position and offset inside cell). */
+struct CellAnchorModel
+{
+ sal_Int32 mnCol; /// Column index.
+ sal_Int32 mnRow; /// Row index.
+ sal_Int64 mnColOffset; /// X offset inside the column.
+ sal_Int64 mnRowOffset; /// Y offset inside the row.
+
+ explicit CellAnchorModel();
+ bool isValid() const { return (mnCol >= 0) && (mnRow >= 0); }
+};
+
+/** Application-specific client data of a shape. */
+struct AnchorClientDataModel
+{
+ bool mbLocksWithSheet;
+ bool mbPrintsWithSheet;
+
+ explicit AnchorClientDataModel();
+};
+
+/** Contains the position of a shape in the spreadsheet. Supports different
+ shape anchor modes (absolute, one-cell, two-cell). */
+class ShapeAnchor final : public WorksheetHelper
+{
+public:
+ enum AnchorType
+ {
+ ANCHOR_INVALID, /// Anchor type is unknown.
+ ANCHOR_ABSOLUTE, /// Absolute anchor (top-left corner and size in absolute units).
+ /// Matches our "Page" anchor -> ScAnchorType::SCA_PAGE
+ ANCHOR_ONECELL, /// One-cell anchor (top-left corner at cell, size in absolute units).
+ /// Matches our "Cell" anchor -> ScAnchorType::SCA_CELL
+ ANCHOR_TWOCELL, /// Two-cell anchor (top-left and bottom-right corner at cell).
+ /// Matches our "Cell (resize with cell)" anchor -> ScAnchorType::SCA_CELL_RESIZE
+ ANCHOR_VML
+ };
+ explicit ShapeAnchor( const WorksheetHelper& rHelper );
+
+ /** Imports the shape anchor (one of the elements xdr:absoluteAnchor, xdr:oneCellAnchor, xdr:twoCellAnchor). */
+ void importAnchor( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports the absolute anchor position from the xdr:pos element. */
+ void importPos( const AttributeList& rAttribs );
+ /** Imports the absolute anchor size from the xdr:ext element. */
+ void importExt( const AttributeList& rAttribs );
+ /** Imports the shape client data from the xdr:clientData element. */
+ void importClientData( const AttributeList& rAttribs );
+ /** Sets an attribute of the cell-dependent anchor position from xdr:from and xdr:to elements. */
+ void setCellPos( sal_Int32 nElement, sal_Int32 nParentContext, std::u16string_view rValue );
+ /** Imports the client anchor settings from a VML element. */
+ void importVmlAnchor( std::u16string_view rAnchor );
+
+ /** Checks whether the shape is visible based on the anchor
+
+ If From and To anchor has the same attribute values, the shape
+ will not have width and height and thus we can assume that
+ such kind of shape will be not be visible
+ */
+ bool isAnchorValid() const;
+
+ /** Calculates the resulting shape anchor in EMUs. */
+ ::oox::drawingml::EmuRectangle calcAnchorRectEmu( const css::awt::Size& rPageSizeHmm ) const;
+ /** Calculates the resulting shape anchor in 1/100 mm. */
+ css::awt::Rectangle calcAnchorRectHmm( const css::awt::Size& rPageSizeHmm ) const;
+ AnchorType getEditAs() const { return meEditAs; }
+private:
+ /** Converts the passed anchor to an absolute position in EMUs. */
+ ::oox::drawingml::EmuPoint calcCellAnchorEmu( const CellAnchorModel& rModel ) const;
+
+private:
+
+ /** Specifies how cell positions from CellAnchorModel have to be processed. */
+ enum class CellAnchorType
+ {
+ Emu, /// Offsets are given in EMUs.
+ Pixel, /// Offsets are given in screen pixels.
+ };
+
+ AnchorType meAnchorType; /// Type of this shape anchor.
+ CellAnchorType meCellAnchorType; /// Type of the cell anchor models.
+ AnchorPointModel maPos; /// Top-left position, if anchor is of type absolute.
+ AnchorSizeModel maSize; /// Anchor size, if anchor is not of type two-cell.
+ CellAnchorModel maFrom; /// Top-left position, if anchor is not of type absolute.
+ CellAnchorModel maTo; /// Bottom-right position, if anchor is of type two-cell.
+ AnchorClientDataModel maClientData; /// Shape client data.
+ AnchorType meEditAs; /// Anchor mode as shown in the UI.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/drawingfragment.hxx b/sc/source/filter/inc/drawingfragment.hxx
new file mode 100644
index 0000000000..0ba281db09
--- /dev/null
+++ b/sc/source/filter/inc/drawingfragment.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <oox/drawingml/shapegroupcontext.hxx>
+#include <oox/drawingml/color.hxx>
+#include <oox/ole/axcontrol.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/ole/vbaproject.hxx>
+#include <oox/vml/vmldrawing.hxx>
+#include <oox/vml/vmldrawingfragment.hxx>
+#include <oox/vml/vmltextbox.hxx>
+#include "drawingbase.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::ole {
+ struct AxFontData;
+}
+
+namespace oox::xls {
+
+// DrawingML
+
+class ShapeMacroAttacher final : public ::oox::ole::VbaMacroAttacherBase
+{
+public:
+ explicit ShapeMacroAttacher( const OUString& rMacroName,
+ const css::uno::Reference< css::drawing::XShape >& rxShape );
+
+private:
+ virtual void attachMacro( const OUString& rMacroUrl ) override;
+
+private:
+ css::uno::Reference< css::drawing::XShape > mxShape;
+};
+
+class Shape final : public ::oox::drawingml::Shape, public WorksheetHelper
+{
+public:
+ explicit Shape(
+ const WorksheetHelper& rHelper,
+ const AttributeList& rAttribs,
+ const char* pcServiceName );
+
+private:
+ virtual void finalizeXShape(
+ ::oox::core::XmlFilterBase& rFilter,
+ const css::uno::Reference< css::drawing::XShapes >& rxShapes ) override;
+
+ OUString maMacroName;
+};
+
+/** Context handler for creation of shapes embedded in group shapes. */
+class GroupShapeContext final : public ::oox::drawingml::ShapeGroupContext, public WorksheetHelper
+{
+public:
+ explicit GroupShapeContext(
+ const ::oox::core::FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper,
+ const ::oox::drawingml::ShapePtr& rxParentShape,
+ const ::oox::drawingml::ShapePtr& rxShape );
+
+ static ::oox::core::ContextHandlerRef
+ createShapeContext(
+ ::oox::core::FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper,
+ sal_Int32 nElement,
+ const AttributeList& rAttribs,
+ const ::oox::drawingml::ShapePtr& rxParentShape,
+ ::oox::drawingml::ShapePtr* pxShape = nullptr );
+
+private:
+ virtual ::oox::core::ContextHandlerRef
+ onCreateContext(
+ sal_Int32 nElement,
+ const ::oox::AttributeList& rAttribs ) override;
+};
+
+/** Fragment handler for a complete sheet drawing. */
+class DrawingFragment final : public WorksheetFragmentBase
+{
+public:
+ explicit DrawingFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ typedef ::std::unique_ptr< ShapeAnchor > ShapeAnchorRef;
+
+ css::uno::Reference< css::drawing::XShapes >
+ mxDrawPage; /// Drawing page of this sheet.
+ ::oox::drawingml::ShapePtr mxShape; /// Current top-level shape.
+ ShapeAnchorRef mxAnchor; /// Current anchor of top-level shape.
+ // for SmartArt. Apply the colors in rFontRefColor (from clrScheme) to all shapes in pShape,
+ // the group object which represents the SmartArt.
+ void applyFontRefColor(const oox::drawingml::ShapePtr& pShape,
+ const oox::drawingml::Color& rFontRefColor);
+};
+
+// VML
+
+class VmlControlMacroAttacher final : public ::oox::ole::VbaMacroAttacherBase
+{
+public:
+ explicit VmlControlMacroAttacher( const OUString& rMacroName,
+ const css::uno::Reference< css::container::XIndexContainer >& rxCtrlFormIC,
+ sal_Int32 nCtrlIndex, sal_Int32 nCtrlType, sal_Int32 nDropStyle );
+
+private:
+ virtual void attachMacro( const OUString& rMacroUrl ) override;
+
+private:
+ css::uno::Reference< css::container::XIndexContainer > mxCtrlFormIC;
+ sal_Int32 mnCtrlIndex;
+ sal_Int32 mnCtrlType;
+ sal_Int32 mnDropStyle;
+};
+
+class VmlDrawing final : public ::oox::vml::Drawing, public WorksheetHelper
+{
+public:
+ explicit VmlDrawing( const WorksheetHelper& rHelper );
+
+ /** Returns the drawing shape for a cell note at the specified position. */
+ const ::oox::vml::ShapeBase* getNoteShape( const ScAddress& rPos ) const;
+
+ /** Filters cell note shapes. */
+ virtual bool isShapeSupported( const ::oox::vml::ShapeBase& rShape ) const override;
+
+ /** Returns additional base names for automatic shape name creation. */
+ virtual OUString getShapeBaseName( const ::oox::vml::ShapeBase& rShape ) const override;
+
+ /** Calculates the shape rectangle from a cell anchor string. */
+ virtual bool convertClientAnchor(
+ css::awt::Rectangle& orShapeRect,
+ const OUString& rShapeAnchor ) const override;
+
+ /** Creates a UNO control shape for legacy drawing controls. */
+ virtual css::uno::Reference< css::drawing::XShape >
+ createAndInsertClientXShape(
+ const ::oox::vml::ShapeBase& rShape,
+ const css::uno::Reference< css::drawing::XShapes >& rxShapes,
+ const css::awt::Rectangle& rShapeRect ) const override;
+
+ /** Updates the bounding box covering all shapes of this drawing. */
+ virtual void notifyXShapeInserted(
+ const css::uno::Reference< css::drawing::XShape >& rxShape,
+ const css::awt::Rectangle& rShapeRect,
+ const ::oox::vml::ShapeBase& rShape, bool bGroupChild ) override;
+
+private:
+ /** Converts the passed VML textbox text color to an OLE color. */
+ sal_uInt32 convertControlTextColor( std::u16string_view aTextColor ) const;
+ /** Converts the passed VML textbox font to an ActiveX form control font. */
+ void convertControlFontData(
+ ::oox::ole::AxFontData& rAxFontData, sal_uInt32& rnOleTextColor,
+ const ::oox::vml::TextFontModel& rFontModel ) const;
+ /** Converts the caption, the font settings, and the horizontal alignment
+ from the passed VML textbox to ActiveX form control settings. */
+ void convertControlText(
+ ::oox::ole::AxFontData& rAxFontData, sal_uInt32& rnOleTextColor, OUString& rCaption,
+ const ::oox::vml::TextBox* pTextBox, sal_Int32 nTextHAlign ) const;
+ /** Converts the passed VML shape background formatting to ActiveX control formatting. */
+ void convertControlBackground(
+ ::oox::ole::AxMorphDataModelBase& rAxModel,
+ const ::oox::vml::ShapeBase& rShape ) const;
+
+private:
+ ::oox::ole::ControlConverter maControlConv;
+ ::oox::vml::TextFontModel maListBoxFont;
+};
+
+class VmlDrawingFragment final : public ::oox::vml::DrawingFragment, public WorksheetHelper
+{
+public:
+ explicit VmlDrawingFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/eeimport.hxx b/sc/source/filter/inc/eeimport.hxx
new file mode 100644
index 0000000000..0d0466eb89
--- /dev/null
+++ b/sc/source/filter/inc/eeimport.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <address.hxx>
+#include <filter.hxx>
+#include <map>
+
+class ScDocument;
+class ScEEParser;
+class ScTabEditEngine;
+class SvStream;
+
+struct ScEEParseEntry;
+
+typedef std::map<SCROW, long> RowHeightMap;
+
+class ScEEImport : public ScEEAbsImport
+{
+protected:
+ ScRange maRange;
+ ScDocument* mpDoc;
+ std::unique_ptr<ScTabEditEngine>
+ mpEngine;
+ std::unique_ptr<ScEEParser> // needs mpEngine
+ mpParser; // must reset before mpEngine resets
+ RowHeightMap maRowHeights;
+
+ bool GraphicSize( SCCOL nCol, SCROW nRow,
+ ScEEParseEntry* );
+ void InsertGraphic( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ ScEEParseEntry* );
+public:
+ ScEEImport( ScDocument* pDoc, const ScRange& rRange );
+ virtual ~ScEEImport() override;
+
+ virtual ErrCode Read( SvStream& rStream, const OUString& rBaseURL ) override;
+ virtual ScRange GetRange() override { return maRange; }
+ virtual void WriteToDocument( bool bSizeColsRows = false,
+ double nOutputFactor = 1.0,
+ SvNumberFormatter* pFormatter = nullptr,
+ bool bConvertDate = true,
+ bool bConvertScientific = true ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/eeparser.hxx b/sc/source/filter/inc/eeparser.hxx
new file mode 100644
index 0000000000..ebc383e32d
--- /dev/null
+++ b/sc/source/filter/inc/eeparser.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include <utility>
+#include <comphelper/errcode.hxx>
+#include <vcl/graph.hxx>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/editdata.hxx>
+#include <optional>
+#include <address.hxx>
+#include <memory>
+#include <vector>
+#include <map>
+
+const char nHorizontal = 1;
+const char nVertical = 2;
+
+struct ScHTMLImage
+{
+ OUString aURL;
+ Size aSize;
+ Point aSpace;
+ OUString aFilterName;
+ std::optional<Graphic>
+ oGraphic; // is taken over by WriteToDocument
+ char nDir; // 1==hori, 2==verti, 3==both
+
+ ScHTMLImage() :
+ aSize( 0, 0 ), aSpace( 0, 0 ), nDir( nHorizontal )
+ {}
+};
+
+struct ScEEParseEntry
+{
+ SfxItemSet aItemSet;
+ ESelection aSel; // Selection in EditEngine
+ std::optional<OUString>
+ pValStr; // HTML possibly SDVAL string
+ std::optional<OUString>
+ pNumStr; // HTML possibly SDNUM string
+ std::optional<OUString>
+ pName; // HTML possibly anchor/RangeName
+ OUString aAltText; // HTML IMG ALT Text
+ std::vector< std::unique_ptr<ScHTMLImage> > maImageList; // graphics in this cell
+ SCCOL nCol; // relative to the beginning of the parse
+ SCROW nRow;
+ sal_uInt16 nTab; // HTML TableInTable
+ sal_uInt16 nTwips; // RTF ColAdjust etc.
+ SCCOL nColOverlap; // merged cells if >1
+ SCROW nRowOverlap; // merged cells if >1
+ sal_uInt16 nOffset; // HTML PixelOffset
+ sal_uInt16 nWidth; // HTML PixelWidth
+ bool bHasGraphic:1; // HTML any image loaded
+ bool bEntirePara:1; // true = use entire paragraph, false = use selection
+
+ ScEEParseEntry( SfxItemPool* pPool ) :
+ aItemSet( *pPool ),
+ nCol(SCCOL_MAX), nRow(SCROW_MAX), nTab(0),
+ nTwips(0), nColOverlap(1), nRowOverlap(1),
+ nOffset(0), nWidth(0), bHasGraphic(false), bEntirePara(true)
+ {}
+
+ ScEEParseEntry( SfxItemSet _aItemSet ) :
+ aItemSet(std::move( _aItemSet )),
+ nCol(SCCOL_MAX), nRow(SCROW_MAX), nTab(0),
+ nTwips(0), nColOverlap(1), nRowOverlap(1),
+ nOffset(0), nWidth(0), bHasGraphic(false), bEntirePara(true)
+ {}
+
+ ~ScEEParseEntry()
+ {
+ maImageList.clear();
+ }
+};
+
+class EditEngine;
+
+typedef std::map<SCCOL, sal_uInt16> ColWidthsMap;
+
+class ScEEParser
+{
+protected:
+ EditEngine* pEdit;
+ rtl::Reference<SfxItemPool> pPool;
+ rtl::Reference<SfxItemPool> pDocPool;
+ std::vector<std::shared_ptr<ScEEParseEntry>> maList;
+ std::shared_ptr<ScEEParseEntry> mxActEntry;
+ ColWidthsMap maColWidths;
+ int nRtfLastToken;
+ SCCOL nColCnt;
+ SCROW nRowCnt;
+ SCCOL nColMax;
+ SCROW nRowMax;
+
+ void NewActEntry( const ScEEParseEntry* );
+
+public:
+ ScEEParser( EditEngine* );
+ virtual ~ScEEParser();
+
+ virtual ErrCode Read( SvStream&, const OUString& rBaseURL ) = 0;
+
+ const ColWidthsMap& GetColWidths() const { return maColWidths; }
+ ColWidthsMap& GetColWidths() { return maColWidths; }
+ void GetDimensions( SCCOL& nCols, SCROW& nRows ) const
+ { nCols = nColMax; nRows = nRowMax; }
+
+ size_t ListSize() const{ return maList.size(); }
+ ScEEParseEntry* ListEntry( size_t index ) { return maList[index].get(); }
+ const ScEEParseEntry* ListEntry( size_t index ) const { return maList[index].get(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excdefs.hxx b/sc/source/filter/inc/excdefs.hxx
new file mode 100644
index 0000000000..272940a35a
--- /dev/null
+++ b/sc/source/filter/inc/excdefs.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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+// (0x009B, 0x009D, 0x009E) AUTOFILTER ========================================
+
+// flags
+const sal_uInt16 EXC_AFFLAG_AND = 0x0000;
+const sal_uInt16 EXC_AFFLAG_OR = 0x0001;
+const sal_uInt16 EXC_AFFLAG_ANDORMASK = 0x0003;
+const sal_uInt16 EXC_AFFLAG_SIMPLE1 = 0x0004;
+const sal_uInt16 EXC_AFFLAG_SIMPLE2 = 0x0008;
+const sal_uInt16 EXC_AFFLAG_TOP10 = 0x0010;
+const sal_uInt16 EXC_AFFLAG_TOP10TOP = 0x0020;
+const sal_uInt16 EXC_AFFLAG_TOP10PERC = 0x0040;
+
+// data types
+const sal_uInt8 EXC_AFTYPE_NOTUSED = 0x00;
+const sal_uInt8 EXC_AFTYPE_RK = 0x02;
+const sal_uInt8 EXC_AFTYPE_DOUBLE = 0x04;
+const sal_uInt8 EXC_AFTYPE_STRING = 0x06;
+const sal_uInt8 EXC_AFTYPE_BOOLERR = 0x08;
+const sal_uInt8 EXC_AFTYPE_INVALID = 0x0A;
+const sal_uInt8 EXC_AFTYPE_EMPTY = 0x0C;
+const sal_uInt8 EXC_AFTYPE_NOTEMPTY = 0x0E;
+
+// comparison operands
+const sal_uInt8 EXC_AFOPER_NONE = 0x00;
+const sal_uInt8 EXC_AFOPER_LESS = 0x01;
+const sal_uInt8 EXC_AFOPER_EQUAL = 0x02;
+const sal_uInt8 EXC_AFOPER_LESSEQUAL = 0x03;
+const sal_uInt8 EXC_AFOPER_GREATER = 0x04;
+const sal_uInt8 EXC_AFOPER_NOTEQUAL = 0x05;
+const sal_uInt8 EXC_AFOPER_GREATEREQUAL = 0x06;
+
+// (0x00AE, 0x00AF) SCENARIO, SCENMAN =========================================
+
+#define EXC_SCEN_MAXCELL 32
+
+// defines for change tracking ================================================
+
+inline constexpr OUString EXC_STREAM_USERNAMES = u"User Names"_ustr;
+inline constexpr OUString EXC_STREAM_REVLOG = u"Revision Log"_ustr;
+
+// opcodes
+#define EXC_CHTR_OP_COLFLAG 0x0001
+#define EXC_CHTR_OP_DELFLAG 0x0002
+#define EXC_CHTR_OP_INSROW 0x0000
+#define EXC_CHTR_OP_INSCOL EXC_CHTR_OP_COLFLAG
+#define EXC_CHTR_OP_DELROW EXC_CHTR_OP_DELFLAG
+#define EXC_CHTR_OP_DELCOL (EXC_CHTR_OP_COLFLAG|EXC_CHTR_OP_DELFLAG)
+#define EXC_CHTR_OP_MOVE 0x0004
+#define EXC_CHTR_OP_INSTAB 0x0005
+#define EXC_CHTR_OP_CELL 0x0008
+#define EXC_CHTR_OP_FORMAT 0x000B
+#define EXC_CHTR_OP_UNKNOWN 0xFFFF
+
+// data types
+#define EXC_CHTR_TYPE_MASK 0x0007
+#define EXC_CHTR_TYPE_FORMATMASK 0xFF00
+#define EXC_CHTR_TYPE_EMPTY 0x0000
+#define EXC_CHTR_TYPE_RK 0x0001
+#define EXC_CHTR_TYPE_DOUBLE 0x0002
+#define EXC_CHTR_TYPE_STRING 0x0003
+#define EXC_CHTR_TYPE_BOOL 0x0004
+#define EXC_CHTR_TYPE_FORMULA 0x0005
+
+// accept flags
+#define EXC_CHTR_NOTHING 0x0000
+#define EXC_CHTR_ACCEPT 0x0001
+#define EXC_CHTR_REJECT 0x0003
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excdoc.hxx b/sc/source/filter/inc/excdoc.hxx
new file mode 100644
index 0000000000..40748b38f6
--- /dev/null
+++ b/sc/source/filter/inc/excdoc.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xeroot.hxx"
+#include "xerecord.hxx"
+#include "excrecds.hxx"
+#include <memory>
+
+// Forwards -
+
+class SvStream;
+class XclExpNote;
+class XclExpStream;
+class XclExpXmlStream;
+class XclExpChangeTrack;
+
+
+class XclExpCellTable;
+
+class ExcTable final : public XclExpRecordBase, public XclExpRoot
+{
+private:
+ typedef XclExpRecordList< ExcBundlesheetBase > ExcBoundsheetList;
+ typedef rtl::Reference< XclExpCellTable > XclExpCellTableRef;
+ typedef XclExpRecordList< XclExpNote > XclExpNoteList;
+ typedef rtl::Reference< XclExpNoteList > XclExpNoteListRef;
+
+ XclExpRecordList<> aRecList;
+ XclExpCellTableRef mxCellTable;
+
+ SCTAB mnScTab; // table number SC document
+ sal_uInt16 nExcTab; // table number Excel document
+
+ XclExpNoteListRef mxNoteList;
+
+ // re-create and forget pRec; delete is done by ExcTable itself!
+ void Add( XclExpRecordBase* pRec );
+
+public:
+ ExcTable( const XclExpRoot& rRoot );
+ ExcTable( const XclExpRoot& rRoot, SCTAB nScTab );
+ virtual ~ExcTable() override;
+
+ void FillAsHeaderBinary( ExcBoundsheetList& rBoundsheetList );
+ void FillAsHeaderXml( ExcBoundsheetList& rBoundsheetList );
+
+ void FillAsTableBinary( SCTAB nCodeNameIdx );
+ void FillAsTableXml();
+
+ void FillAsEmptyTable( SCTAB nCodeNameIdx );
+
+ void Write( XclExpStream& );
+ void WriteXml( XclExpXmlStream& );
+};
+
+class ExcDocument final : protected XclExpRoot
+{
+friend class ExcTable;
+
+private:
+ typedef XclExpRecordList< ExcTable > ExcTableList;
+ typedef XclExpRecordList< ExcBundlesheetBase > ExcBoundsheetList;
+ typedef ExcBoundsheetList::RecordRefType ExcBoundsheetRef;
+
+ ExcTable aHeader;
+
+ ExcTableList maTableList;
+ ExcBoundsheetList maBoundsheetList;
+
+ std::unique_ptr<XclExpChangeTrack> m_xExpChangeTrack;
+
+public:
+ explicit ExcDocument( const XclExpRoot& rRoot );
+ virtual ~ExcDocument() override;
+
+ void ReadDoc();
+ void Write( SvStream& rSvStrm );
+ void WriteXml( XclExpXmlStream& );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelchartconverter.hxx b/sc/source/filter/inc/excelchartconverter.hxx
new file mode 100644
index 0000000000..363966a4c0
--- /dev/null
+++ b/sc/source/filter/inc/excelchartconverter.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 .
+ */
+
+#pragma once
+
+#include <oox/drawingml/chart/chartconverter.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox::xls {
+
+class ExcelChartConverter final : public ::oox::drawingml::chart::ChartConverter, public WorkbookHelper
+{
+public:
+ explicit ExcelChartConverter( const WorkbookHelper& rHelper );
+ virtual ~ExcelChartConverter() override;
+
+ /** Creates an external data provider that is able to use spreadsheet data. */
+ virtual void createDataProvider(
+ const css::uno::Reference< css::chart2::XChartDocument >& rxChartDoc ) override;
+
+ /** Creates a data sequence from the passed formula. */
+ virtual css::uno::Reference<css::chart2::data::XDataSequence>
+ createDataSequence(
+ const css::uno::Reference<css::chart2::data::XDataProvider>& rxDataProvider,
+ const oox::drawingml::chart::DataSequenceModel& rDataSeq, const OUString& rRole,
+ const OUString& aRoleQualifier ) override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelfilter.hxx b/sc/source/filter/inc/excelfilter.hxx
new file mode 100644
index 0000000000..2aeb675cd7
--- /dev/null
+++ b/sc/source/filter/inc/excelfilter.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 .
+ */
+
+#pragma once
+
+#include <oox/core/xmlfilterbase.hxx>
+
+namespace oox::xls {
+
+class WorkbookGlobals;
+
+class ExcelFilter final : public ::oox::core::XmlFilterBase
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ExcelFilter(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~ExcelFilter() override;
+
+ void registerWorkbookGlobals( WorkbookGlobals& rBookGlob );
+ WorkbookGlobals& getWorkbookGlobals() const;
+ void unregisterWorkbookGlobals();
+
+ virtual bool importDocument() override;
+ virtual bool exportDocument() noexcept override;
+
+ virtual const ::oox::drawingml::Theme* getCurrentTheme() const override;
+ virtual ::oox::vml::Drawing* getVmlDrawing() override;
+ virtual ::oox::drawingml::table::TableStyleListPtr getTableStyles() override;
+ virtual ::oox::drawingml::chart::ChartConverter* getChartConverter() override;
+ virtual void useInternalChartDataTable( bool bInternal ) override;
+
+ virtual sal_Bool SAL_CALL filter( const css::uno::Sequence< css::beans::PropertyValue >& rDescriptor ) override;
+
+private:
+ virtual GraphicHelper* implCreateGraphicHelper() const override;
+ virtual ::oox::ole::VbaProject* implCreateVbaProject() const override;
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ WorkbookGlobals* mpBookGlob;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelhandlers.hxx b/sc/source/filter/inc/excelhandlers.hxx
new file mode 100644
index 0000000000..fd029e9958
--- /dev/null
+++ b/sc/source/filter/inc/excelhandlers.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 <oox/core/fragmenthandler2.hxx>
+#include <oox/core/contexthandler2.hxx>
+#include "workbookhelper.hxx"
+#include "worksheethelper.hxx"
+
+namespace oox::xls {
+
+/** Context handler derived from the WorkbookHelper helper class.
+
+ Used to import contexts in global workbook fragments.
+ */
+class WorkbookContextBase : public ::oox::core::ContextHandler2, public WorkbookHelper
+{
+public:
+ template< typename ParentType >
+ explicit WorkbookContextBase( ParentType& rParent ) :
+ ::oox::core::ContextHandler2( rParent ), WorkbookHelper( rParent ) {}
+};
+
+/** Context handler derived from the WorksheetHelper helper class.
+
+ Used to import contexts in sheet fragments.
+ */
+class WorksheetContextBase : public ::oox::core::ContextHandler2, public WorksheetHelper
+{
+public:
+ template< typename ParentType >
+ explicit WorksheetContextBase( ParentType& rParent ) :
+ ::oox::core::ContextHandler2( rParent ), WorksheetHelper( rParent ) {}
+};
+
+/** Fragment handler derived from the WorkbookHelper helper class.
+
+ Used to import global workbook fragments.
+ */
+class WorkbookFragmentBase : public ::oox::core::FragmentHandler2, public WorkbookHelper
+{
+public:
+ explicit WorkbookFragmentBase(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+};
+
+/** Fragment handler derived from the WorksheetHelper helper class.
+
+ Used to import sheet fragments.
+ */
+class WorksheetFragmentBase : public ::oox::core::FragmentHandler2, public WorksheetHelper
+{
+public:
+ explicit WorksheetFragmentBase(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+};
+
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelvbaproject.hxx b/sc/source/filter/inc/excelvbaproject.hxx
new file mode 100644
index 0000000000..7c10472912
--- /dev/null
+++ b/sc/source/filter/inc/excelvbaproject.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 .
+ */
+
+#pragma once
+
+#include <oox/ole/vbaproject.hxx>
+
+namespace com::sun::star {
+ namespace sheet { class XSpreadsheetDocument; }
+}
+
+namespace oox::xls {
+
+/** Special implementation of the VBA project for the Excel filters. */
+class ExcelVbaProject final : public ::oox::ole::VbaProject
+{
+public:
+ explicit ExcelVbaProject(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::sheet::XSpreadsheetDocument >& rxDocument );
+
+private:
+ /** Adds dummy modules for sheets without imported code name. */
+ virtual void prepareImport() override;
+
+ css::uno::Reference< css::sheet::XSpreadsheetDocument >
+ mxDocument;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excform.hxx b/sc/source/filter/inc/excform.hxx
new file mode 100644
index 0000000000..4bc19b1855
--- /dev/null
+++ b/sc/source/filter/inc/excform.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 .
+ */
+
+#pragma once
+
+#include "xlformula.hxx"
+#include "xiroot.hxx"
+#include "xltools.hxx"
+#include "formel.hxx"
+
+#include <vector>
+
+class ScFormulaCell;
+class ScRangeList;
+
+class ExcelToSc : public ExcelConverterBase, protected XclImpRoot
+{
+protected:
+ enum ExtensionType { EXTENSION_ARRAY, EXTENSION_NLR, EXTENSION_MEMAREA };
+ typedef ::std::vector< ExtensionType > ExtensionTypeVec;
+
+ static const sal_uInt16 nRowMask;
+
+ XclFunctionProvider maFuncProv;
+ const XclBiff meBiff;
+
+ void DoMulArgs( DefTokenId eId, sal_uInt8 nNumArgs );
+
+ void ExcRelToScRel( sal_uInt16 nRow, sal_uInt8 nCol, ScSingleRefData&, const bool bName );
+
+public:
+ ExcelToSc( XclImpRoot& rRoot );
+ virtual ~ExcelToSc() override;
+ virtual ConvErr Convert( std::unique_ptr<ScTokenArray>&, XclImpStream& rStrm, std::size_t nFormulaLen,
+ bool bAllowArrays, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual ConvErr Convert( ScRangeListTabs&, XclImpStream& rStrm, std::size_t nFormulaLen, SCTAB nTab, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual void ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const ::std::vector<OUString>& rTabNames );
+
+ virtual void GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen );
+
+ std::unique_ptr<ScTokenArray> GetDummy();
+ std::unique_ptr<ScTokenArray> GetBoolErr( XclBoolError );
+
+ static bool ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow );
+ const ScTokenArray* GetSharedFormula( const ScAddress& rRefPos ) const;
+
+ static void SetError( ScFormulaCell& rCell, const ConvErr eErr );
+
+ static inline bool IsComplColRange( const sal_uInt16 nCol1, const sal_uInt16 nCol2 );
+ static inline bool IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 );
+
+ void SetComplCol( ScComplexRefData& );
+ void SetComplRow( ScComplexRefData& );
+
+ void ReadExtensions( const ExtensionTypeVec& rExtensions,
+ XclImpStream& aIn );
+ void ReadExtensionArray( unsigned int n,
+ XclImpStream& aIn );
+ static void ReadExtensionNlr( XclImpStream& aIn );
+ void ReadExtensionMemArea( XclImpStream& aIn );
+};
+
+inline bool ExcelToSc::IsComplColRange( const sal_uInt16 nCol1, const sal_uInt16 nCol2 )
+{
+ return ( nCol1 == 0x00 ) && ( nCol2 == 0xFF );
+}
+
+inline bool ExcelToSc::IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 )
+{
+ return ( ( nRow1 & 0x3FFF ) == 0x0000 ) && ( ( nRow2 & 0x3FFF ) == 0x3FFF );
+}
+
+class XclImpLinkManager;
+class XclImpExtName;
+
+class ExcelToSc8 : public ExcelToSc
+{
+public:
+
+ struct ExternalTabInfo
+ {
+ ScRange maRange;
+ OUString maTabName;
+ sal_uInt16 mnFileId;
+ bool mbExternal;
+
+ ExternalTabInfo();
+ };
+
+private:
+ const XclImpLinkManager& rLinkMan;
+
+ void ExcRelToScRel8( sal_uInt16 nRow, sal_uInt16 nCol, ScSingleRefData&,
+ const bool bName );
+
+ bool GetExternalFileIdFromXti( sal_uInt16 nIxti, sal_uInt16& rFileId ) const;
+
+ virtual bool Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo );
+
+ bool HandleOleLink(sal_uInt16 nXtiIndex, const XclImpExtName& rExtName, ExternalTabInfo& rExtInfo);
+public:
+ ExcelToSc8( XclImpRoot& rRoot );
+ virtual ~ExcelToSc8() override;
+
+ virtual ConvErr Convert( std::unique_ptr<ScTokenArray>& rpTokArray, XclImpStream& rStrm, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual ConvErr Convert( ScRangeListTabs&, XclImpStream& rStrm, std::size_t nFormulaLen, SCTAB nTab, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual void ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const ::std::vector<OUString>& rTabNames ) override;
+
+ static inline bool IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 );
+
+ virtual void GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen ) override;
+};
+
+inline bool ExcelToSc8::IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 )
+{
+ return ( nRow1 == 0x0000 ) && ( nRow2 == 0xFFFF );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excimp8.hxx b/sc/source/filter/inc/excimp8.hxx
new file mode 100644
index 0000000000..4096ca0682
--- /dev/null
+++ b/sc/source/filter/inc/excimp8.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "imp_op.hxx"
+#include "root.hxx"
+#include "excscen.hxx"
+#include <queryparam.hxx>
+
+class ScDBData;
+class XclImpStream;
+
+class ImportExcel8 : public ImportExcel
+{
+public:
+ ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm );
+ virtual ~ImportExcel8() override;
+
+ virtual ErrCode Read() override;
+
+ void Calccount(); // 0x0C
+ void Precision(); // 0x0E
+ void Delta(); // 0x10
+ void Iteration(); // 0x11
+ void Boundsheet(); // 0x85
+ void FilterMode(); // 0x9B
+ void AutoFilterInfo(); // 0x9D
+ void AutoFilter(); // 0x9E
+ void Scenman(); // 0xAE
+ void Scenario(); // 0xAF
+ void ReadBasic(); // 0xD3
+ void Labelsst(); // 0xFD
+
+ void FeatHdr(); // 0x0867
+ void Feat(); // 0x0868
+
+ virtual void EndSheet() override;
+ virtual void PostDocLoad() override;
+
+private:
+ ExcScenarioList maScenList;
+};
+
+
+class XclImpAutoFilterData : private ExcRoot
+{
+private:
+ ScDBData* pCurrDBData;
+ ScQueryParam aParam;
+ ScRange aCriteriaRange;
+ bool bActive:1;
+ bool bCriteria:1;
+ bool bAutoOrAdvanced:1;
+
+ void SetCellAttribs();
+ void InsertQueryParam();
+
+protected:
+public:
+ XclImpAutoFilterData(
+ RootData* pRoot,
+ const ScRange& rRange);
+
+ bool IsActive() const { return bActive; }
+ bool IsFiltered() const { return bAutoOrAdvanced; }
+ SCTAB Tab() const { return aParam.nTab; }
+ SCCOL StartCol() const { return aParam.nCol1; }
+ SCROW StartRow() const { return aParam.nRow1; }
+ SCCOL EndCol() const { return aParam.nCol2; }
+ SCROW EndRow() const { return aParam.nRow2; }
+
+ void ReadAutoFilter( XclImpStream& rStrm, svl::SharedStringPool& rPool );
+
+ void Activate() { bActive = true; }
+ void SetAdvancedRange( const ScRange* pRange );
+ void SetExtractPos( const ScAddress& rAddr );
+ void SetAutoOrAdvanced() { bAutoOrAdvanced = true; }
+ void Apply();
+ void EnableRemoveFilter();
+};
+
+class XclImpAutoFilterBuffer
+{
+public:
+
+ void Insert( RootData* pRoot, const ScRange& rRange);
+ void AddAdvancedRange( const ScRange& rRange );
+ void AddExtractPos( const ScRange& rRange );
+ void Apply();
+
+ XclImpAutoFilterData* GetByTab( SCTAB nTab );
+
+private:
+ typedef std::shared_ptr<XclImpAutoFilterData> XclImpAutoFilterSharePtr;
+ std::vector<XclImpAutoFilterSharePtr> maFilters;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excrecds.hxx b/sc/source/filter/inc/excrecds.hxx
new file mode 100644
index 0000000000..c7ab0aa96b
--- /dev/null
+++ b/sc/source/filter/inc/excrecds.hxx
@@ -0,0 +1,460 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/color.hxx>
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+#include "excdefs.hxx"
+
+//------------------------------------------------------------------ Forwards -
+
+class ScDBData;
+struct ScQueryEntry;
+
+//----------------------------------------------------------- class ExcRecord -
+
+class ExcRecord : public XclExpRecord
+{
+public:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual sal_uInt16 GetNum() const = 0;
+ virtual std::size_t GetLen() const = 0;
+
+protected:
+ virtual void SaveCont( XclExpStream& rStrm );
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+//--------------------------------------------------------- class ExcEmptyRec -
+
+class ExcEmptyRec : public ExcRecord
+{
+private:
+protected:
+public:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcDummyRec -
+
+class ExcDummyRec : public ExcRecord
+{
+protected:
+public:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual sal_uInt16 GetNum() const override;
+ virtual const sal_uInt8* GetData() const = 0; // byte data must contain header and body
+};
+
+//------------------------------------------------------- class ExcBoolRecord -
+// stores bool as 16bit val ( 0x0000 | 0x0001 )
+
+class ExcBoolRecord : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+protected:
+ bool bVal;
+
+ ExcBoolRecord() : bVal( false ) {}
+
+public:
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcBof_Base -
+
+class ExcBof_Base : public ExcRecord
+{
+private:
+protected:
+ sal_uInt16 nDocType;
+ sal_uInt16 nVers;
+ sal_uInt16 nRupBuild;
+ sal_uInt16 nRupYear;
+public:
+ ExcBof_Base();
+};
+
+//-------------------------------------------------------------- class ExcBof -
+// Header Record for WORKSHEETS
+
+class ExcBof : public ExcBof_Base
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ ExcBof();
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//------------------------------------------------------------- class ExcBofW -
+// Header Record for WORKBOOKS
+
+class ExcBofW : public ExcBof_Base
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ ExcBofW();
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//-------------------------------------------------------------- class ExcEof -
+
+class ExcEof : public ExcRecord
+{
+private:
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcDummy_00 -
+// INTERFACEHDR to FNGROUPCOUNT (see excrecds.cxx)
+
+class ExcDummy_00 : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+// EXC_ID_WINDOWPROTECTION
+class XclExpWindowProtection : public XclExpBoolRecord
+{
+ public:
+ XclExpWindowProtection(bool bValue);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// EXC_ID_PROTECT Document Protection
+class XclExpProtection : public XclExpBoolRecord
+{
+ public:
+ XclExpProtection(bool bValue);
+};
+
+class XclExpSheetProtection : public XclExpProtection
+{
+ SCTAB mnTab;
+ public:
+ XclExpSheetProtection(bool bValue, SCTAB nTab);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpPassHash : public XclExpRecord
+{
+public:
+ XclExpPassHash(const css::uno::Sequence<sal_Int8>& aHash);
+ virtual ~XclExpPassHash() override;
+
+private:
+ virtual void WriteBody(XclExpStream& rStrm) override;
+
+private:
+ sal_uInt16 mnHash;
+};
+
+//-------------------------------------------------------- class ExcDummy_04x -
+// PASSWORD to BOOKBOOL (see excrecds.cxx), no 1904
+
+class ExcDummy_040 : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+class ExcDummy_041 : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+//------------------------------------------------------------- class Exc1904 -
+
+class Exc1904 : public ExcBoolRecord
+{
+public:
+ Exc1904( const ScDocument& rDoc );
+ virtual sal_uInt16 GetNum() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ bool bDateCompatibility;
+};
+
+//------------------------------------------------------ class ExcBundlesheet -
+
+class ExcBundlesheetBase : public ExcRecord
+{
+protected:
+ sal_uInt64 m_nStrPos;
+ sal_uInt64 m_nOwnPos; // Position after # and Len
+ sal_uInt16 nGrbit;
+ SCTAB nTab;
+
+ ExcBundlesheetBase();
+
+public:
+ ExcBundlesheetBase( const RootData& rRootData, SCTAB nTab );
+
+ void SetStreamPos(sal_uInt64 const nStrPos) { m_nStrPos = nStrPos; }
+ void UpdateStreamPos( XclExpStream& rStrm );
+
+ virtual sal_uInt16 GetNum() const override;
+};
+
+class ExcBundlesheet : public ExcBundlesheetBase
+{
+private:
+ OString aName;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ ExcBundlesheet( const RootData& rRootData, SCTAB nTab );
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcDummy_02 -
+// sheet dummies: CALCMODE to SETUP
+
+class ExcDummy_02a : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+/** This record contains the Windows country IDs for the UI and document language. */
+class XclExpCountry : public XclExpRecord
+{
+public:
+ explicit XclExpCountry( const XclExpRoot& rRoot );
+
+private:
+ sal_uInt16 mnUICountry; /// The UI country ID.
+ sal_uInt16 mnDocCountry; /// The document country ID.
+
+ /** Writes the body of the COUNTRY record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+// XclExpWsbool ===============================================================
+
+class XclExpWsbool : public XclExpUInt16Record
+{
+public:
+ explicit XclExpWsbool( bool bFitToPages );
+};
+
+/**
+ * Save sheetPr element and its children for xlsx export.
+ */
+class XclExpXmlSheetPr : public XclExpRecordBase
+{
+public:
+ explicit XclExpXmlSheetPr(
+ bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ SCTAB mnScTab;
+ XclExpFilterManager* mpManager;
+ bool mbFitToPage;
+ Color maTabColor;
+};
+
+class XclExpFiltermode : public XclExpEmptyRecord
+{
+public:
+ explicit XclExpFiltermode();
+};
+
+class XclExpAutofilterinfo : public XclExpUInt16Record
+{
+public:
+ explicit XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol );
+
+ const ScAddress& GetStartPos() const { return maStartPos; }
+ SCCOL GetColCount() const { return static_cast< SCCOL >( GetValue() ); }
+
+private:
+ ScAddress maStartPos;
+};
+
+class ExcFilterCondition
+{
+private:
+ sal_uInt8 nType;
+ sal_uInt8 nOper;
+ std::unique_ptr<XclExpString>
+ pText;
+
+protected:
+public:
+ ExcFilterCondition();
+ ~ExcFilterCondition();
+
+ bool IsEmpty() const { return (nType == EXC_AFTYPE_NOTUSED); }
+ std::size_t GetTextBytes() const;
+
+ void SetCondition( sal_uInt8 nTp, sal_uInt8 nOp, const OUString* pT );
+
+ void Save( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+ void SaveText( XclExpStream& rStrm );
+};
+
+class XclExpAutofilter : public XclExpRecord, protected XclExpRoot
+{
+private:
+ enum FilterType
+ {
+ Empty,
+ FilterCondition,
+ MultiValue,
+ BlankValue,
+ ColorValue
+ };
+ FilterType meType;
+ sal_uInt16 nCol;
+ bool bIsButtonHidden;
+ sal_uInt16 nFlags;
+ bool bHasBlankValue;
+ ExcFilterCondition aCond[ 2 ];
+ std::vector<std::pair<OUString, bool>> maMultiValues; // first->values, second->bDateFormat
+ std::vector<std::pair<::Color, bool>> maColorValues; // first->Color, second->bIsBackgroundColor (vs. TextColor)
+
+ bool AddCondition( ScQueryConnect eConn, sal_uInt8 nType,
+ sal_uInt8 nOp, const OUString* pText, bool bSimple = false );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+public:
+ XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC, bool bIsEmpty = false );
+
+ sal_uInt16 GetCol() const { return nCol; }
+ bool HasTop10() const { return ::get_flag( nFlags, EXC_AFFLAG_TOP10 ); }
+
+ bool HasCondition() const;
+ bool AddEntry( const ScQueryEntry& rEntry );
+ void AddMultiValueEntry( const ScQueryEntry& rEntry );
+ void SetButtonHidden(bool bValue) { bIsButtonHidden = bValue; }
+ void AddColorEntry( const ScQueryEntry& rEntry );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class ExcAutoFilterRecs : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** @param pDefinedData
+ If nullptr, obtain anonymous ScDBData from sheet nTab.
+ Else, use defined database range; used with XclExpTables.
+ */
+ explicit ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData );
+ virtual ~ExcAutoFilterRecs() override;
+
+ void AddObjRecs();
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ bool HasFilterMode() const;
+
+private:
+ XclExpAutofilter* GetByCol( SCCOL nCol ); // always 0-based
+ bool IsFiltered( SCCOL nCol );
+
+private:
+ typedef XclExpRecordList< XclExpAutofilter > XclExpAutofilterList;
+ typedef XclExpAutofilterList::RecordRefType XclExpAutofilterRef;
+
+ XclExpAutofilterList maFilterList;
+ rtl::Reference<XclExpFiltermode> m_pFilterMode;
+ rtl::Reference<XclExpAutofilterinfo> m_pFilterInfo;
+ ScRange maRef;
+ bool mbAutoFilter;
+
+ ScRange maSortRef; // sort area without headers
+ std::vector< std::tuple<ScRange, OUString, bool> > maSortCustomList; // sorted column with list of sorted items
+};
+
+/** Sheet filter manager. Contains auto filters or advanced filters from all sheets. */
+class XclExpFilterManager : protected XclExpRoot
+{
+public:
+ explicit XclExpFilterManager( const XclExpRoot& rRoot );
+
+ /** Creates the filter records for the specified sheet.
+ @descr Creates and inserts related built-in NAME records. Therefore this
+ function is called from the name buffer itself. */
+ void InitTabFilter( SCTAB nScTab );
+
+ /** Returns a record object containing all filter records for the specified sheet. */
+ XclExpRecordRef CreateRecord( SCTAB nScTab );
+
+ /** Returns whether or not FilterMode is present */
+ bool HasFilterMode( SCTAB nScTab );
+
+private:
+ using XclExpRoot::CreateRecord;
+
+ typedef rtl::Reference< ExcAutoFilterRecs > XclExpTabFilterRef;
+ typedef ::std::map< SCTAB, XclExpTabFilterRef > XclExpTabFilterMap;
+
+ XclExpTabFilterMap maFilterMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excscen.hxx b/sc/source/filter/inc/excscen.hxx
new file mode 100644
index 0000000000..81e97e1b12
--- /dev/null
+++ b/sc/source/filter/inc/excscen.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 <vector>
+#include <memory>
+#include <rtl/ustring.hxx>
+
+struct RootData;
+class XclImpRoot;
+class XclImpStream;
+
+class ExcScenarioCell
+{
+private:
+ OUString aValue;
+public:
+ const sal_uInt16 nCol;
+ const sal_uInt16 nRow;
+
+ ExcScenarioCell( const sal_uInt16 nC, const sal_uInt16 nR );
+
+ void SetValue( const OUString& rVal ) { aValue = rVal; }
+
+ const OUString& GetValue() const { return aValue; }
+};
+
+class ExcScenario final
+{
+public:
+ ExcScenario( XclImpStream& rIn, const RootData& rRoot );
+
+ void Apply( const XclImpRoot& rRoot, const bool bLast );
+
+private:
+ OUString aName;
+ OUString aComment;
+ sal_uInt8 nProtected;
+ const sal_uInt16 nTab;
+ std::vector<ExcScenarioCell> aEntries;
+};
+
+struct ExcScenarioList
+{
+ ExcScenarioList () : nLastScenario(0) {}
+
+ void Apply( const XclImpRoot& rRoot );
+
+ sal_uInt16 nLastScenario;
+ std::vector< std::unique_ptr<ExcScenario> > aEntries;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/exp_op.hxx b/sc/source/filter/inc/exp_op.hxx
new file mode 100644
index 0000000000..87ed95b2ee
--- /dev/null
+++ b/sc/source/filter/inc/exp_op.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/stream.hxx>
+#include <memory>
+#include "xeroot.hxx"
+
+class ExcDocument;
+
+class ExportTyp
+{
+protected:
+ ~ExportTyp() {}
+
+ SvStream& aOut;
+public:
+ ExportTyp( SvStream& aStream ) : aOut( aStream ) {}
+
+ virtual ErrCode Write() = 0;
+};
+
+class ExportBiff5 : public ExportTyp, protected XclExpRoot
+{
+private:
+ std::unique_ptr<ExcDocument>
+ pExcDoc;
+
+protected:
+ RootData* pExcRoot;
+
+public:
+ ExportBiff5( XclExpRootData& rExpData, SvStream& rStrm );
+ virtual ~ExportBiff5() override;
+ ErrCode Write() override;
+};
+
+class ExportBiff8 : public ExportBiff5
+{
+public:
+ ExportBiff8( XclExpRootData& rExpData, SvStream& rStrm );
+ virtual ~ExportBiff8() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/expbase.hxx b/sc/source/filter/inc/expbase.hxx
new file mode 100644
index 0000000000..84cc558cbd
--- /dev/null
+++ b/sc/source/filter/inc/expbase.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <address.hxx>
+
+class SvNumberFormatter;
+class SvStream;
+class ScFieldEditEngine;
+
+class ScExportBase
+{
+protected:
+
+ SvStream& rStrm;
+ ScRange aRange;
+ ScDocument* pDoc;
+ SvNumberFormatter* pFormatter;
+ std::unique_ptr<ScFieldEditEngine>
+ pEditEngine;
+
+public:
+
+ ScExportBase( SvStream&, ScDocument*, const ScRange& );
+ virtual ~ScExportBase();
+
+ // Trim borders of hidden Cols/Rows,
+ // return: sal_True if range exists
+ // Start/End/Col/Row must have valid starting values
+ bool TrimDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const;
+
+ // Get Data Area of a table,
+ // adjust borders of hidden Cols/Rows,
+ // return: sal_True if range exists
+ bool GetDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const;
+
+ // table does not exist or is empty
+ bool IsEmptyTable( SCTAB nTab ) const;
+
+ ScFieldEditEngine& GetEditEngine() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/export/ExportTools.hxx b/sc/source/filter/inc/export/ExportTools.hxx
new file mode 100644
index 0000000000..369728f06e
--- /dev/null
+++ b/sc/source/filter/inc/export/ExportTools.hxx
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <docmodel/color/ComplexColor.hxx>
+#include <sax/fshelper.hxx>
+
+namespace oox::xls
+{
+void writeComplexColor(sax_fastparser::FSHelperPtr& pFS, sal_Int32 nElement,
+ model::ComplexColor const& rComplexColor, Color const& rColor);
+void writeComplexColor(sax_fastparser::FSHelperPtr& pFS, sal_Int32 nElement,
+ model::ComplexColor const& rComplexColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/export/SparklineExt.hxx b/sc/source/filter/inc/export/SparklineExt.hxx
new file mode 100644
index 0000000000..f2bff1c7d3
--- /dev/null
+++ b/sc/source/filter/inc/export/SparklineExt.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/.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <rangelst.hxx>
+#include <Sparkline.hxx>
+#include <SparklineAttributes.hxx>
+
+#include <sax/fastattribs.hxx>
+
+#include <xerecord.hxx>
+#include <xeroot.hxx>
+#include <xeextlst.hxx>
+
+namespace xcl::exp
+{
+/** Export for sparkline type of <ext> element - top sparkline element. */
+class SparklineExt : public XclExpExt
+{
+public:
+ SparklineExt(const XclExpRoot& rRoot);
+
+ void SaveXml(XclExpXmlStream& rStream) override;
+ void addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines);
+
+ static void
+ addSparklineGroupAttributes(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineAttributes& rSparklineAttributes);
+ static void addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineAttributes& rSparklineAttributes);
+
+ XclExpExtType GetType() override { return XclExpExtSparklineType; }
+};
+
+/** Determines if sparklines needs to be exported and initiates the export. */
+class SparklineBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit SparklineBuffer(const XclExpRoot& rRoot, const XclExtLstRef& xExtLst);
+};
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/externallinkbuffer.hxx b/sc/source/filter/inc/externallinkbuffer.hxx
new file mode 100644
index 0000000000..374a54b38c
--- /dev/null
+++ b/sc/source/filter/inc/externallinkbuffer.hxx
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <com/sun/star/sheet/ExternalLinkInfo.hpp>
+#include <oox/helper/containerhelper.hxx>
+#include "defnamesbuffer.hxx"
+#include "formulabase.hxx"
+
+namespace com::sun::star {
+ namespace sheet { struct DDEItemInfo; }
+ namespace sheet { class XDDELink; }
+ namespace sheet { class XExternalDocLink; }
+ namespace sheet { class XExternalSheetCache; }
+}
+
+namespace oox::core {
+ class Relations;
+}
+
+namespace oox::xls {
+
+struct ExternalNameModel
+{
+ bool mbNotify; /// Notify application on data change.
+ bool mbPreferPic; /// Picture link.
+ bool mbStdDocName; /// Name is the StdDocumentName for DDE.
+ bool mbOleObj; /// Name is an OLE object.
+ bool mbIconified; /// Iconified object link.
+
+ explicit ExternalNameModel();
+};
+
+class ExternalLink;
+
+class ExternalName : public DefinedNameBase
+{
+public:
+ explicit ExternalName( const ExternalLink& rParentLink );
+
+ /** Appends the passed value to the result set. */
+ template< typename Type >
+ void appendResultValue( const Type& rValue )
+ { if( maCurrIt != maResults.end() ) (*maCurrIt++) <<= rValue; }
+
+ /** Imports the definedName element. */
+ void importDefinedName( const AttributeList& rAttribs );
+ /** Imports the ddeItem element describing an item of a DDE link. */
+ void importDdeItem( const AttributeList& rAttribs );
+ /** Imports the values element containing the size of the DDE result matrix. */
+ void importValues( const AttributeList& rAttribs );
+ /** Imports the oleItem element describing an object of an OLE link. */
+ void importOleItem( const AttributeList& rAttribs );
+
+ /** Imports the EXTERNALNAME record containing the name (only). */
+ void importExternalName( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALNAMEFLAGS record containing the settings of an external name. */
+ void importExternalNameFlags( SequenceInputStream& rStrm );
+ /** Imports the DDEITEMVALUES record containing the size of the DDE result matrix. */
+ void importDdeItemValues( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_BOOL record containing a boolean value in a link result. */
+ void importDdeItemBool( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_DOUBLE record containing a double value in a link result. */
+ void importDdeItemDouble( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_ERROR record containing an error code in a link result. */
+ void importDdeItemError( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_STRING record containing a string in a link result. */
+ void importDdeItemString( SequenceInputStream& rStrm );
+
+ /** Returns the DDE item info needed by the XML formula parser. */
+ bool getDdeItemInfo(
+ css::sheet::DDEItemInfo& orItemInfo ) const;
+
+ /** Returns the complete DDE link data of this DDE item. */
+ bool getDdeLinkData(
+ OUString& orDdeServer,
+ OUString& orDdeTopic,
+ OUString& orDdeItem );
+
+private:
+ /** Sets the size of the result matrix. */
+ void setResultSize( sal_Int32 nColumns, sal_Int32 nRows );
+
+private:
+ typedef Matrix< css::uno::Any > ResultMatrix;
+
+ const ExternalLink& mrParentLink; /// External link this name belongs to.
+ ExternalNameModel maExtNameModel; /// Additional name data.
+ ResultMatrix maResults; /// DDE/OLE link results.
+ ResultMatrix::iterator maCurrIt; /// Current position in result matrix.
+ css::uno::Reference< css::sheet::XDDELink >
+ mxDdeLink; /// Interface of a DDE link.
+ bool mbDdeLinkCreated; /// True = already tried to create the DDE link.
+};
+
+typedef std::shared_ptr< ExternalName > ExternalNameRef;
+
+/** Contains indexes for a range of sheets in the spreadsheet document. */
+class LinkSheetRange
+{
+public:
+ explicit LinkSheetRange() { setDeleted(); }
+
+ /** Sets this struct to deleted state. */
+ void setDeleted();
+ /** Sets this struct to "use current sheet" state. */
+ void setSameSheet();
+ /** Sets the passed absolute sheet range to the members of this struct. */
+ void setRange( sal_Int32 nFirst, sal_Int32 nLast );
+ /** Sets the passed external sheet cache range to the members of this struct. */
+ void setExternalRange( sal_Int32 nDocLink, sal_Int32 nFirst, sal_Int32 nLast );
+
+ /** Returns true, if the sheet indexes are valid and different. */
+ bool isDeleted() const { return mnFirst < 0; }
+ /** Returns true, if the sheet range points to an external document. */
+ bool isExternal() const { return !isDeleted() && (meType == LINKSHEETRANGE_EXTERNAL); }
+ /** Returns true, if the sheet indexes are valid and different. */
+ bool isSameSheet() const { return meType == LINKSHEETRANGE_SAMESHEET; }
+ /** Returns true, if the sheet indexes are valid and different. */
+ bool is3dRange() const { return (0 <= mnFirst) && (mnFirst < mnLast); }
+
+ sal_Int32 getDocLinkIndex() const { return mnDocLink; }
+ sal_Int32 getFirstSheet() const { return mnFirst; }
+ sal_Int32 getLastSheet() const { return mnLast; }
+
+private:
+ enum LinkSheetRangeType
+ {
+ LINKSHEETRANGE_INTERNAL, /// Sheet range in the own document.
+ LINKSHEETRANGE_EXTERNAL, /// Sheet range in an external document.
+ LINKSHEETRANGE_SAMESHEET /// Current sheet depending on context.
+ };
+
+ LinkSheetRangeType meType; /// Link sheet range type.
+ sal_Int32 mnDocLink; /// Document link token index for external links.
+ sal_Int32 mnFirst; /// Index of the first sheet or index of first external sheet cache.
+ sal_Int32 mnLast; /// Index of the last sheet or index of last external sheet cache.
+};
+
+enum class ExternalLinkType
+{
+ Self, /// Link refers to the current workbook.
+ Same, /// Link refers to the current sheet.
+ External, /// Link refers to an external spreadsheet document.
+ // let's ignore xlStartup and xlAlternateStartup for now
+ PathMissing, /// Just for round-tripping (FIXME: Functionality not actually implemented after all.)
+ Library, /// Link refers to an external add-in.
+ DDE, /// DDE link.
+ OLE, /// OLE link.
+ Unknown /// Unknown or unsupported link type.
+};
+
+template< typename charT, typename traits >
+inline std::basic_ostream<charT, traits> & operator <<(
+ std::basic_ostream<charT, traits> & stream, const ExternalLinkType& type )
+{
+ switch (type)
+ {
+ case ExternalLinkType::Self: return stream << "self";
+ case ExternalLinkType::Same: return stream << "same";
+ case ExternalLinkType::External: return stream << "external";
+ case ExternalLinkType::PathMissing: return stream << "pathmissing";
+ case ExternalLinkType::Library: return stream << "library";
+ case ExternalLinkType::DDE: return stream << "dde";
+ case ExternalLinkType::OLE: return stream << "ole";
+ case ExternalLinkType::Unknown: return stream << "unknown";
+ default: return stream << static_cast<int>(type);
+ }
+}
+
+class ExternalLink : public WorkbookHelper
+{
+public:
+ explicit ExternalLink( const WorkbookHelper& rHelper );
+
+ /** Imports the externalReference element containing the relation identifier. */
+ void importExternalReference( const AttributeList& rAttribs );
+ /** Imports the externalBook element describing an externally linked document. */
+ void importExternalBook( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports the sheetName element containing the sheet name in an externally linked document. */
+ void importSheetName( const AttributeList& rAttribs );
+ /** Imports the definedName element describing an external name. */
+ void importDefinedName( const AttributeList& rAttribs );
+ /** Imports the ddeLink element describing a DDE link. */
+ void importDdeLink( const AttributeList& rAttribs );
+ /** Imports the ddeItem element describing an item of a DDE link. */
+ ExternalNameRef importDdeItem( const AttributeList& rAttribs );
+ /** Imports the oleLink element describing an OLE link. */
+ void importOleLink( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports the oleItem element describing an object of an OLE link. */
+ ExternalNameRef importOleItem( const AttributeList& rAttribs );
+
+ /** Imports the EXTERNALBOOK record describing an externally linked document, DDE link, or OLE link. */
+ void importExternalBook( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+ /** Imports the EXTSHEETNAMES record containing the sheet names in an externally linked document. */
+ void importExtSheetNames( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALNAME record describing an external name. */
+ ExternalNameRef importExternalName( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALREF record from the passed stream. */
+ void importExternalRef( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSELF record from the passed stream. */
+ void importExternalSelf( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSAME record from the passed stream. */
+ void importExternalSame( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALADDIN record from the passed stream. */
+ void importExternalAddin( SequenceInputStream& rStrm );
+
+ /** Sets the link type to 'self reference'. */
+ void setSelfLinkType() { meLinkType = ExternalLinkType::Self; }
+
+ /** Returns the type of this external link. */
+ ExternalLinkType getLinkType() const { return meLinkType; }
+
+ /** Returns the relation identifier for the external link fragment. */
+ const OUString& getRelId() const { return maRelId; }
+ /** Returns the class name of this external link. */
+ const OUString& getClassName() const { return maClassName; }
+ /** Returns the target URL of this external link. */
+ const OUString& getTargetUrl() const { return maTargetUrl; }
+ /** Returns the link info needed by the XML formula parser. */
+ css::sheet::ExternalLinkInfo getLinkInfo() const;
+
+ /** Returns the type of the external library if this is a library link. */
+ FunctionLibraryType getFuncLibraryType() const;
+
+ /** Returns the token index of the external document. */
+ sal_Int32 getDocumentLinkIndex() const;
+ /** Returns the external sheet cache index or for the passed sheet. */
+ sal_Int32 getSheetCacheIndex( sal_Int32 nTabId ) const;
+ /** Returns the sheet cache of the external sheet with the passed index. */
+ css::uno::Reference< css::sheet::XExternalSheetCache >
+ getSheetCache( sal_Int32 nTabId ) const;
+
+ /** Returns the internal sheet range or range of external sheet caches for the passed sheet range (BIFF only). */
+ void getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const;
+
+ /** Returns the external name with the passed zero-based index. */
+ ExternalNameRef getNameByIndex( sal_Int32 nIndex ) const;
+
+private:
+ void setExternalTargetUrl( const OUString& rTargetUrl, const OUString& rTargetType );
+ void setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType );
+ void parseExternalReference( const ::oox::core::Relations& rRelations, const OUString& rRelId );
+
+ /** Creates an external document link and the sheet cache for the passed sheet name. */
+ void insertExternalSheet( const OUString& rSheetName );
+
+ ExternalNameRef createExternalName();
+
+private:
+ ExternalLinkType meLinkType; /// Type of this link object.
+ FunctionLibraryType meFuncLibType; /// Type of the function library, if link type is ExternalLinkType::Library.
+ OUString maRelId; /// Relation identifier for the external link fragment.
+ OUString maClassName; /// DDE service, OLE class name.
+ OUString maTargetUrl; /// Target link, DDE topic, OLE target.
+ css::uno::Reference< css::sheet::XExternalDocLink >
+ mxDocLink; /// Interface for an external document.
+ std::vector< sal_Int32 > maSheetCaches; /// External sheet cache indexes.
+ RefVector< ExternalName > maExtNames; /// Defined names in external document.
+};
+
+typedef std::shared_ptr< ExternalLink > ExternalLinkRef;
+
+/** Represents a REF entry in the BIFF12 EXTERNALSHEETS or in the BIFF8
+ EXTERNSHEET record.
+
+ This struct is used to map ref identifiers to external books (BIFF12:
+ EXTERNALREF records, BIFF8: EXTERNALBOOK records), and provides sheet
+ indexes into the sheet list of the external document.
+ */
+struct RefSheetsModel
+{
+ sal_Int32 mnExtRefId; /// Zero-based index into list of external documents.
+ sal_Int32 mnTabId1; /// Zero-based index to first sheet in external document.
+ sal_Int32 mnTabId2; /// Zero-based index to last sheet in external document.
+
+ explicit RefSheetsModel();
+
+ void readBiff12Data( SequenceInputStream& rStrm );
+};
+
+class ExternalLinkBuffer : public WorkbookHelper
+{
+public:
+ explicit ExternalLinkBuffer( const WorkbookHelper& rHelper );
+
+ /** Imports the externalReference element containing . */
+ ExternalLinkRef importExternalReference( const AttributeList& rAttribs );
+
+ /** Imports the EXTERNALREF record from the passed stream. */
+ ExternalLinkRef importExternalRef( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSELF record from the passed stream. */
+ void importExternalSelf( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSAME record from the passed stream. */
+ void importExternalSame( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALADDIN record from the passed stream. */
+ void importExternalAddin( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSHEETS record from the passed stream. */
+ void importExternalSheets( SequenceInputStream& rStrm );
+
+ /** Returns the sequence of link infos needed by the XML formula parser. */
+ css::uno::Sequence< css::sheet::ExternalLinkInfo >
+ getLinkInfos() const;
+
+ /** Returns the external link for the passed reference identifier. */
+ ExternalLinkRef getExternalLink( sal_Int32 nRefId, bool bUseRefSheets = true ) const;
+
+ /** Returns the sheet range for the specified reference (BIFF8 only). */
+ LinkSheetRange getSheetRange( sal_Int32 nRefId ) const;
+
+private:
+ /** Creates a new external link and inserts it into the list of links. */
+ ExternalLinkRef createExternalLink();
+
+ /** Returns the specified sheet indexes for a reference identifier. */
+ const RefSheetsModel* getRefSheets( sal_Int32 nRefId ) const;
+
+private:
+ typedef RefVector< ExternalLink > ExternalLinkVec;
+ typedef ::std::vector< RefSheetsModel > RefSheetsModelVec;
+
+ ExternalLinkRef mxSelfRef; /// Implicit self reference at index 0.
+ ExternalLinkVec maLinks; /// List of link structures for all kinds of links.
+ ExternalLinkVec maExtLinks; /// Real external links needed for formula parser.
+ RefSheetsModelVec maRefSheets; /// Sheet indexes for reference ids.
+ bool mbUseRefSheets; /// True = use maRefSheets list (BIFF12 only).
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/externallinkfragment.hxx b/sc/source/filter/inc/externallinkfragment.hxx
new file mode 100644
index 0000000000..f14ba0b0af
--- /dev/null
+++ b/sc/source/filter/inc/externallinkfragment.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "externallinkbuffer.hxx"
+
+namespace oox::xls {
+
+/** This class implements importing the sheetData element in external sheets.
+
+ The sheetData element embedded in the externalBook element contains cached
+ cells from externally linked sheets.
+ */
+class ExternalSheetDataContext : public WorkbookContextBase
+{
+public:
+ explicit ExternalSheetDataContext(
+ WorkbookFragmentBase& rFragment,
+ const css::uno::Reference< css::sheet::XExternalSheetCache >& rxSheetCache );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+private:
+ /** Imports cell settings from a c element. */
+ void importCell( const AttributeList& rAttribs );
+
+ /** Imports the EXTCELL_BLANK from the passed stream. */
+ void importExtCellBlank( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_BOOL from the passed stream. */
+ void importExtCellBool( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_DOUBLE from the passed stream. */
+ void importExtCellDouble( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_ERROR from the passed stream. */
+ void importExtCellError( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_STRING from the passed stream. */
+ void importExtCellString( SequenceInputStream& rStrm );
+
+ /** Sets the passed cell value to the current position in the sheet cache. */
+ void setCellValue( const css::uno::Any& rValue );
+
+private:
+ css::uno::Reference< css::sheet::XExternalSheetCache >
+ mxSheetCache; /// The sheet cache used to store external cell values.
+ ScAddress maCurrPos; /// Position of current cell.
+ sal_Int32 mnCurrType; /// Data type of current cell.
+};
+
+class ExternalLinkFragment : public WorkbookFragmentBase
+{
+public:
+ explicit ExternalLinkFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath,
+ ExternalLink& rExtLink );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ ::oox::core::ContextHandlerRef createSheetDataContext( sal_Int32 nSheetId );
+
+private:
+ ExternalLink& mrExtLink;
+ ExternalNameRef mxExtName;
+ OUString maResultValue;
+ sal_Int32 mnResultType;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/extlstcontext.hxx b/sc/source/filter/inc/extlstcontext.hxx
new file mode 100644
index 0000000000..077ebdbebf
--- /dev/null
+++ b/sc/source/filter/inc/extlstcontext.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include <oox/core/contexthandler.hxx>
+#include "condformatbuffer.hxx"
+
+#include <vector>
+#include <memory>
+
+extern sal_Int32 rStyleIdx; // Holds index of the <extlst> <cfRule> style (Will be reset by finalize import)
+
+struct ScDataBarFormatData;
+namespace oox { class AttributeList; }
+namespace oox::xls { class WorkbookFragment; }
+namespace oox::xls { class WorksheetFragment; }
+
+namespace oox::xls {
+
+class ExtCfRuleContext : public WorksheetContextBase
+{
+public:
+ explicit ExtCfRuleContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pDataBar );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+private:
+ ScDataBarFormatData* mpTarget;
+ bool mbFirstEntry;
+ ExtCfDataBarRuleRef mpRule;
+};
+
+struct ExtCondFormatRuleModel
+{
+ sal_Int32 nPriority;
+ ScConditionMode eOperator;
+ OUString aFormula;
+ OUString aStyle;
+};
+
+class ExtConditionalFormattingContext : public WorksheetContextBase
+{
+public:
+ explicit ExtConditionalFormattingContext(WorksheetContextBase& rFragment);
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters(const OUString& rCharacters) override;
+ virtual void onEndElement() override;
+
+private:
+ ExtCondFormatRuleModel maModel;
+ sal_Int32 nFormulaCount;
+ OUString aChars; // Characters of between xml elements.
+ sal_Int32 nPriority; // Priority of last cfRule element.
+ ScConditionMode eOperator; // Used only when cfRule type is "cellIs"
+ bool isPreviousElementF; // Used to distinguish alone <sqref> from <f> and <sqref>
+ std::vector<std::unique_ptr<ScFormatEntry> > maEntries;
+ std::unique_ptr<IconSetRule> mpCurrentRule;
+ std::vector<sal_Int32> maPriorities;
+ std::vector<ExtCondFormatRuleModel> maModels;
+};
+
+/**
+ * Handle ExtLst entries in xlsx. These entries are a way to extend the standard
+ * without actually changing it
+ *
+ * Needed right now for data bars
+ *
+ * ExtLstLocalContext is for the entry in the datastructure that needs to be extended
+ */
+class ExtLstLocalContext : public WorksheetContextBase
+{
+public:
+ explicit ExtLstLocalContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pTarget ); // until now a ExtLst always extends an existing entry
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+private:
+ ScDataBarFormatData* mpTarget;
+};
+
+/**
+ * A single ext entry. Will be skipped until the actual entry with the correct uri is found
+ */
+class ExtGlobalContext : public WorksheetContextBase
+{
+public:
+ explicit ExtGlobalContext( WorksheetContextBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+};
+
+/**
+ * Used for the actual ExtLst containing the new extended definition.
+ * Uses the saved data from the ExtLstLocalContext
+ */
+class ExtLstGlobalContext : public WorksheetContextBase
+{
+public:
+ explicit ExtLstGlobalContext( WorksheetFragment& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+};
+
+class ExtGlobalWorkbookContext : public WorkbookContextBase
+{
+public:
+ explicit ExtGlobalWorkbookContext( WorkbookContextBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+};
+
+class ExtLstGlobalWorkbookContext : public WorkbookContextBase
+{
+public:
+ explicit ExtLstGlobalWorkbookContext( WorkbookFragment& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+};
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/fapihelper.hxx b/sc/source/filter/inc/fapihelper.hxx
new file mode 100644
index 0000000000..7449f11937
--- /dev/null
+++ b/sc/source/filter/inc/fapihelper.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/diagnose.h>
+#include <tools/color.hxx>
+#include "ftools.hxx"
+
+namespace com::sun::star {
+ namespace lang { class XMultiServiceFactory; }
+}
+
+namespace com::sun::star::beans { struct NamedValue; }
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::beans { class XMultiPropertySet; }
+
+namespace comphelper { class IDocPasswordVerifier; }
+
+// Static helper functions ====================================================
+
+class SfxMedium;
+class SfxObjectShell;
+
+/** Static API helper functions. */
+class ScfApiHelper
+{
+public:
+ /** Converts a non-empty vector into a UNO sequence containing elements of the same type. */
+ template< typename Type >
+ static css::uno::Sequence< Type >
+ VectorToSequence( const ::std::vector< Type >& rVector );
+
+ /** Returns the service name provided via the XServiceName interface, or an empty string on error. */
+ static OUString GetServiceName( const css::uno::Reference< css::uno::XInterface >& xInt );
+
+ /** Returns the multi service factory from a document shell. */
+ static css::uno::Reference< css::lang::XMultiServiceFactory > GetServiceFactory( const SfxObjectShell* pShell );
+
+ /** Creates an instance from the passed service name, using the passed service factory. */
+ static css::uno::Reference< css::uno::XInterface > CreateInstance(
+ const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory,
+ const OUString& rServiceName );
+
+ /** Creates an instance from the passed service name, using the service factory of the passed object. */
+ static css::uno::Reference< css::uno::XInterface > CreateInstance(
+ const SfxObjectShell* pShell,
+ const OUString& rServiceName );
+
+ /** Creates an instance from the passed service name, using the process service factory. */
+ static css::uno::Reference< css::uno::XInterface > CreateInstance( const OUString& rServiceName );
+
+ /** Opens a password dialog and returns the encryption data.
+ @return The encryption data or an empty sequence on 'Cancel' or any error. */
+ static css::uno::Sequence< css::beans::NamedValue > QueryEncryptionDataForMedium( SfxMedium& rMedium,
+ ::comphelper::IDocPasswordVerifier& rVerifier,
+ const ::std::vector< OUString >* pDefaultPasswords );
+};
+
+template< typename Type >
+css::uno::Sequence< Type > ScfApiHelper::VectorToSequence( const ::std::vector< Type >& rVector )
+{
+ OSL_ENSURE( !rVector.empty(), "ScfApiHelper::VectorToSequence - vector is empty" );
+ return css::uno::Sequence<Type>(rVector.data(), static_cast< sal_Int32 >(rVector.size()));
+}
+
+// Property sets ==============================================================
+
+/** A wrapper for a UNO property set.
+
+ This class provides functions to silently get and set properties (without
+ exceptions, without the need to check validity of the UNO property set).
+
+ An instance is constructed with the reference to a UNO property set or any
+ other interface (the constructor will query for the XPropertySet interface
+ then). The reference to the property set will be kept as long as the
+ instance of this class is alive.
+
+ The functions GetProperties() and SetProperties() try to handle all passed
+ values at once, using the XMultiPropertySet interface. If the
+ implementation does not support the XMultiPropertySet interface, all
+ properties are handled separately in a loop.
+ */
+class ScfPropertySet
+{
+public:
+ explicit ScfPropertySet() {}
+ /** Constructs a property set wrapper with the passed UNO property set. */
+ explicit ScfPropertySet( const css::uno::Reference< css::beans::XPropertySet > & xPropSet ) { Set( xPropSet ); }
+ /** Constructs a property set wrapper after querying the XPropertySet interface. */
+ template< typename InterfaceType >
+ explicit ScfPropertySet( const css::uno::Reference< InterfaceType >& xInterface ) { Set( xInterface ); }
+
+ ~ScfPropertySet();
+ //TODO:
+ ScfPropertySet(ScfPropertySet const &) = default;
+ ScfPropertySet(ScfPropertySet &&) = default;
+ ScfPropertySet & operator =(ScfPropertySet const &) = default;
+ ScfPropertySet & operator =(ScfPropertySet &&) = default;
+
+ /** Sets the passed UNO property set and releases the old UNO property set. */
+ void Set( css::uno::Reference< css::beans::XPropertySet > const & xPropSet );
+ /** Queries the passed interface for an XPropertySet and releases the old UNO property set. */
+ template< typename InterfaceType >
+ void Set( css::uno::Reference< InterfaceType > xInterface )
+ { Set( css::uno::Reference< css::beans::XPropertySet >( xInterface, css::uno::UNO_QUERY ) ); }
+
+ /** Returns true, if the contained XPropertySet interface is valid. */
+ bool Is() const { return mxPropSet.is(); }
+
+ /** Returns the contained XPropertySet interface. */
+ const css::uno::Reference< css::beans::XPropertySet >& GetApiPropertySet() const { return mxPropSet; }
+
+ /** Returns the service name provided via the XServiceName interface, or an empty string on error. */
+ OUString GetServiceName() const;
+
+ // Get properties ---------------------------------------------------------
+
+ /** Returns true, if the property set contains the specified property. */
+ bool HasProperty( const OUString& rPropName ) const;
+
+ /** Gets the specified property from the property set.
+ @return true, if the Any could be filled with the property value. */
+ bool GetAnyProperty( css::uno::Any& rValue, const OUString& rPropName ) const;
+
+ /** Gets the specified property from the property set.
+ @return true, if the passed variable could be filled with the property value. */
+ template< typename Type >
+ bool GetProperty( Type& rValue, const OUString& rPropName ) const
+ { css::uno::Any aAny; return GetAnyProperty( aAny, rPropName ) && (aAny >>= rValue); }
+
+ /** Gets the specified Boolean property from the property set.
+ @return true = property contains true; false = property contains false or error occurred. */
+ bool GetBoolProperty( const OUString& rPropName ) const;
+
+ /** Gets the specified Boolean property from the property set. */
+ OUString GetStringProperty( const OUString& rPropName ) const;
+
+ /** Gets the specified color property from the property set.
+ @return true, if the passed color variable could be filled with the property value. */
+ bool GetColorProperty( Color& rColor, const OUString& rPropName ) const;
+
+ /** Gets the specified properties from the property set. Tries to use the XMultiPropertySet interface.
+ @param rPropNames The property names. MUST be ordered alphabetically.
+ @param rValues The related property values. */
+ void GetProperties( css::uno::Sequence< css::uno::Any >& rValues, const css::uno::Sequence< OUString >& rPropNames ) const;
+
+ // Set properties ---------------------------------------------------------
+
+ /** Puts the passed Any into the property set. */
+ void SetAnyProperty( const OUString& rPropName, const css::uno::Any& rValue );
+
+ /** Puts the passed value into the property set. */
+ template< typename Type >
+ void SetProperty( const OUString& rPropName, const Type& rValue )
+ { SetAnyProperty( rPropName, css::uno::Any( rValue ) ); }
+
+ /** Puts the passed Boolean value into the property set. */
+ void SetBoolProperty( const OUString& rPropName, bool bValue )
+ { SetAnyProperty( rPropName, css::uno::Any( bValue ) ); }
+
+ /** Puts the passed string into the property set. */
+ void SetStringProperty( const OUString& rPropName, const OUString& rValue )
+ { SetProperty( rPropName, rValue ); }
+
+ /** Puts the passed color into the property set. */
+ void SetColorProperty( const OUString& rPropName, const Color& rColor )
+ { SetProperty( rPropName, sal_Int32( rColor ) ); }
+
+ /** Puts the passed properties into the property set. Tries to use the XMultiPropertySet interface.
+ @param rPropNames The property names. MUST be ordered alphabetically.
+ @param rValues The related property values. */
+ void SetProperties( const css::uno::Sequence< OUString > & rPropNames, const css::uno::Sequence< css::uno::Any >& rValues );
+
+private:
+ css::uno::Reference< css::beans::XPropertySet > mxPropSet; /// The mandatory property set interface.
+ css::uno::Reference< css::beans::XMultiPropertySet > mxMultiPropSet; /// The optional multi property set interface.
+};
+
+/** Generic helper class for reading from and writing to property sets.
+
+ Usage:
+ 1) Call the constructor with a null-terminated array of ASCII strings.
+ 2a) Read properties from a property set: Call the ReadFromPropertySet()
+ function, then get the properties with the ReadValue() functions or the
+ operator>> stream operator. The properties are returned in order of the
+ array of property names passed in the constructor.
+ 2b) Write properties to a property set: Call InitializeWrite() to start a
+ new cycle. Set the values with the WriteValue() functions or the
+ operator<< stream operator. The order of the properties is equal to the
+ array of property names passed in the constructor. Finally, call the
+ WriteToPropertySet() function.
+ */
+class ScfPropSetHelper
+{
+public:
+ /** @param ppPropNames A null-terminated array of ASCII property names. */
+ explicit ScfPropSetHelper( const char* const* ppcPropNames );
+
+ // read properties --------------------------------------------------------
+
+ /** Reads all values from the passed property set. */
+ void ReadFromPropertySet( const ScfPropertySet& rPropSet );
+
+ /** Reads the next value from the value sequence. */
+ template< typename Type >
+ void ReadValue( Type& rValue );
+ /** Reads an Any from the value sequence. */
+ void ReadValue( css::uno::Any& rAny );
+ /** Reads a color value from the value sequence. */
+ void ReadValue( Color& rColor );
+ /** Reads a C++ boolean value from the value sequence. */
+ void ReadValue( bool& rbValue );
+
+ // write properties -------------------------------------------------------
+
+ /** Must be called before reading or storing property values in the helper. */
+ void InitializeWrite();
+
+ /** Writes the next value to the value sequence. */
+ template< typename Type >
+ void WriteValue( const Type& rValue );
+ /** Writes an Any to the value sequence. */
+ void WriteValue( const css::uno::Any& rAny );
+ /** Writes a color value to the value sequence. */
+ void WriteValue( const Color& rColor )
+ { WriteValue( sal_Int32( rColor ) ); }
+ /** Writes a C++ boolean value to the value sequence. */
+ void WriteValue( bool rbValue );
+
+ /** Writes all values to the passed property set. */
+ void WriteToPropertySet( ScfPropertySet& rPropSet ) const;
+
+private:
+ /** Returns a pointer to the next Any to be written to. */
+ css::uno::Any* GetNextAny();
+
+private:
+ css::uno::Sequence< OUString > maNameSeq; /// Sequence of property names.
+ css::uno::Sequence< css::uno::Any > maValueSeq; /// Sequence of property values.
+ ScfInt32Vec maNameOrder; /// Maps initial order to alphabetical order.
+ size_t mnNextIdx; /// Counter for next Any to be processed.
+};
+
+template< typename Type >
+void ScfPropSetHelper::ReadValue( Type& rValue )
+{
+ css::uno::Any* pAny = GetNextAny();
+ if (pAny)
+ *pAny >>= rValue;
+}
+
+template< typename Type >
+void ScfPropSetHelper::WriteValue( const Type& rValue )
+{
+ css::uno::Any* pAny = GetNextAny();
+ if( pAny )
+ *pAny <<= rValue;
+}
+
+template< typename Type >
+ScfPropSetHelper& operator>>( ScfPropSetHelper& rPropSetHelper, Type& rValue )
+{
+ rPropSetHelper.ReadValue( rValue );
+ return rPropSetHelper;
+}
+
+template< typename Type >
+ScfPropSetHelper& operator<<( ScfPropSetHelper& rPropSetHelper, const Type& rValue )
+{
+ rPropSetHelper.WriteValue( rValue );
+ return rPropSetHelper;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/flttypes.hxx b/sc/source/filter/inc/flttypes.hxx
new file mode 100644
index 0000000000..9863278c08
--- /dev/null
+++ b/sc/source/filter/inc/flttypes.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
+
+enum BiffTyp
+{
+ BiffX = 0x0000,
+ Biff2 = 0x2000, Biff2M = 0x2002, Biff2C = 0x2004,
+ Biff3 = 0x3000, Biff3W = 0x3001, Biff3M = 0x3002, Biff3C = 0x3004,
+ Biff4 = 0x4000, Biff4W = 0x4001, Biff4M = 0x4002, Biff4C = 0x4004,
+ Biff5 = 0x5000, Biff5W = 0x5001, Biff5V = 0x5002, Biff5C = 0x5004, Biff5M4 = 0x5008,
+ Biff8 = 0x8000, Biff8W = 0x8001, Biff8V = 0x8002, Biff8C = 0x8004, Biff8M4 = 0x8008
+};
+
+enum class Lotus123Typ
+{
+ X,
+ WK3,
+ WK4,
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formel.hxx b/sc/source/filter/inc/formel.hxx
new file mode 100644
index 0000000000..2c01a560e8
--- /dev/null
+++ b/sc/source/filter/inc/formel.hxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/stream.hxx>
+
+#include "tokstack.hxx"
+#include "xiroot.hxx"
+
+#include <memory>
+#include <vector>
+#include <map>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+class XclImpStream;
+class ScTokenArray;
+
+enum class ConvErr
+{
+ OK = 0,
+ Ni, // unimplemented/unknown opcode occurred
+ Count // did not get all bytes of formula
+};
+
+enum FORMULA_TYPE
+{
+ FT_CellFormula,
+ FT_RangeName,
+ FT_SharedFormula,
+ FT_CondFormat
+};
+
+class ScRangeListTabs : protected XclImpRoot
+{
+ typedef ::std::vector<ScRange> RangeListType;
+ typedef ::std::map<SCTAB, RangeListType> TabRangeType;
+ TabRangeType m_TabRanges;
+ RangeListType::const_iterator maItrCur;
+ RangeListType::const_iterator maItrCurEnd;
+
+public:
+ ScRangeListTabs( const XclImpRoot& rRoot );
+ ~ScRangeListTabs();
+
+ void Append( const ScAddress& aSRD, SCTAB nTab );
+ void Append( const ScRange& aCRD, SCTAB nTab );
+
+ const ScRange* First ( SCTAB nTab );
+ const ScRange* Next ();
+
+ bool HasRanges () const { return !m_TabRanges.empty(); }
+};
+
+class ConverterBase
+{
+protected:
+ TokenPool aPool; // user token + predefined token
+ TokenStack aStack;
+ ScAddress aEingPos;
+
+ ConverterBase( svl::SharedStringPool& rSPool );
+ virtual ~ConverterBase();
+
+ void Reset();
+};
+
+class ExcelConverterBase : public ConverterBase
+{
+protected:
+ ExcelConverterBase( svl::SharedStringPool& rSPool );
+ virtual ~ExcelConverterBase() override;
+
+public:
+ void Reset();
+ void Reset( const ScAddress& rEingPos );
+
+ virtual ConvErr Convert( std::unique_ptr<ScTokenArray>& rpErg, XclImpStream& rStrm, std::size_t nFormulaLen,
+ bool bAllowArrays, const FORMULA_TYPE eFT = FT_CellFormula ) = 0;
+ virtual ConvErr Convert( ScRangeListTabs&, XclImpStream& rStrm, std::size_t nFormulaLen, SCTAB nTab,
+ const FORMULA_TYPE eFT = FT_CellFormula ) = 0;
+};
+
+class LotusConverterBase : public ConverterBase
+{
+protected:
+ SvStream& aIn;
+ sal_Int32 nBytesLeft;
+
+ inline void Ignore( const tools::Long nSeekRel );
+ inline void Read( sal_uInt8& nByte );
+ inline void Read( sal_uInt16& nUINT16 );
+ inline void Read( sal_Int16& nINT16 );
+ inline void Read( double& fDouble );
+ inline void Read( sal_uInt32& nUINT32 );
+
+ LotusConverterBase( SvStream& rStr, svl::SharedStringPool& rSPool );
+ virtual ~LotusConverterBase() override;
+
+public:
+ void Reset( const ScAddress& rEingPos );
+
+ virtual void Convert( std::unique_ptr<ScTokenArray>& rpErg, sal_Int32& nRest ) = 0;
+
+ bool good() const { return aIn.good(); }
+
+protected:
+ using ConverterBase::Reset;
+};
+
+inline void LotusConverterBase::Ignore( const tools::Long nSeekRel )
+{
+ aIn.SeekRel( nSeekRel );
+ nBytesLeft -= nSeekRel;
+}
+
+inline void LotusConverterBase::Read( sal_uInt8& nByte )
+{
+ aIn.ReadUChar( nByte );
+ if (aIn.good())
+ nBytesLeft--;
+ else
+ {
+ // SvStream::ReadUChar() does not init a single char on failure. This
+ // behaviour is even tested in a unit test.
+ nByte = 0;
+ nBytesLeft = -1; // bail out early
+ }
+}
+
+inline void LotusConverterBase::Read( sal_uInt16& nUINT16 )
+{
+ aIn.ReadUInt16( nUINT16 );
+ if (aIn.good())
+ nBytesLeft -= 2;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+inline void LotusConverterBase::Read( sal_Int16& nINT16 )
+{
+ aIn.ReadInt16( nINT16 );
+ if (aIn.good())
+ nBytesLeft -= 2;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+inline void LotusConverterBase::Read( double& fDouble )
+{
+ aIn.ReadDouble( fDouble );
+ if (aIn.good())
+ nBytesLeft -= 8;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+inline void LotusConverterBase::Read( sal_uInt32& nUINT32 )
+{
+ aIn.ReadUInt32( nUINT32 );
+ if (aIn.good())
+ nBytesLeft -= 4;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formulabase.hxx b/sc/source/filter/inc/formulabase.hxx
new file mode 100644
index 0000000000..f90d073191
--- /dev/null
+++ b/sc/source/filter/inc/formulabase.hxx
@@ -0,0 +1,771 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/Pair.hpp>
+#include <com/sun/star/sheet/FormulaToken.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <oox/helper/propertyset.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace com::sun::star {
+ namespace lang { class XMultiServiceFactory; }
+ namespace sheet { class XFormulaParser; }
+}
+
+namespace oox { template< typename Type > class Matrix; }
+namespace com::sun::star::sheet { struct FormulaOpCodeMapEntry; }
+namespace oox { class SequenceInputStream; }
+namespace oox::xls { struct BinAddress; }
+class ScRangeList;
+
+namespace oox::xls {
+
+// Constants ==================================================================
+
+const size_t BIFF_TOKARR_MAXLEN = 4096; /// Maximum size of a token array.
+
+// token class flags ----------------------------------------------------------
+
+const sal_uInt8 BIFF_TOKCLASS_MASK = 0x60;
+const sal_uInt8 BIFF_TOKCLASS_NONE = 0x00; /// 00-1F: Base tokens.
+const sal_uInt8 BIFF_TOKCLASS_REF = 0x20; /// 20-3F: Reference class tokens.
+const sal_uInt8 BIFF_TOKCLASS_VAL = 0x40; /// 40-5F: Value class tokens.
+const sal_uInt8 BIFF_TOKCLASS_ARR = 0x60; /// 60-7F: Array class tokens.
+
+const sal_uInt8 BIFF_TOKFLAG_INVALID = 0x80; /// This bit must be null for a valid token identifier.
+
+// base token identifiers -----------------------------------------------------
+
+const sal_uInt8 BIFF_TOKID_MASK = 0x1F;
+
+const sal_uInt8 BIFF_TOKID_NONE = 0x00; /// Placeholder for invalid token id.
+const sal_uInt8 BIFF_TOKID_EXP = 0x01; /// Array or shared formula reference.
+const sal_uInt8 BIFF_TOKID_TBL = 0x02; /// Multiple operation reference.
+const sal_uInt8 BIFF_TOKID_ADD = 0x03; /// Addition operator.
+const sal_uInt8 BIFF_TOKID_SUB = 0x04; /// Subtraction operator.
+const sal_uInt8 BIFF_TOKID_MUL = 0x05; /// Multiplication operator.
+const sal_uInt8 BIFF_TOKID_DIV = 0x06; /// Division operator.
+const sal_uInt8 BIFF_TOKID_POWER = 0x07; /// Power operator.
+const sal_uInt8 BIFF_TOKID_CONCAT = 0x08; /// String concatenation operator.
+const sal_uInt8 BIFF_TOKID_LT = 0x09; /// Less than operator.
+const sal_uInt8 BIFF_TOKID_LE = 0x0A; /// Less than or equal operator.
+const sal_uInt8 BIFF_TOKID_EQ = 0x0B; /// Equal operator.
+const sal_uInt8 BIFF_TOKID_GE = 0x0C; /// Greater than or equal operator.
+const sal_uInt8 BIFF_TOKID_GT = 0x0D; /// Greater than operator.
+const sal_uInt8 BIFF_TOKID_NE = 0x0E; /// Not equal operator.
+const sal_uInt8 BIFF_TOKID_ISECT = 0x0F; /// Intersection operator.
+const sal_uInt8 BIFF_TOKID_LIST = 0x10; /// List operator.
+const sal_uInt8 BIFF_TOKID_RANGE = 0x11; /// Range operator.
+const sal_uInt8 BIFF_TOKID_UPLUS = 0x12; /// Unary plus.
+const sal_uInt8 BIFF_TOKID_UMINUS = 0x13; /// Unary minus.
+const sal_uInt8 BIFF_TOKID_PERCENT = 0x14; /// Percent sign.
+const sal_uInt8 BIFF_TOKID_PAREN = 0x15; /// Parentheses.
+const sal_uInt8 BIFF_TOKID_MISSARG = 0x16; /// Missing argument.
+const sal_uInt8 BIFF_TOKID_STR = 0x17; /// String constant.
+const sal_uInt8 BIFF_TOKID_NLR = 0x18; /// Natural language reference (NLR).
+const sal_uInt8 BIFF_TOKID_ATTR = 0x19; /// Special attribute.
+const sal_uInt8 BIFF_TOKID_SHEET = 0x1A; /// Start of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 BIFF_TOKID_ENDSHEET = 0x1B; /// End of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 BIFF_TOKID_ERR = 0x1C; /// Error constant.
+const sal_uInt8 BIFF_TOKID_BOOL = 0x1D; /// Boolean constant.
+const sal_uInt8 BIFF_TOKID_INT = 0x1E; /// Integer constant.
+const sal_uInt8 BIFF_TOKID_NUM = 0x1F; /// Floating-point constant.
+
+// base identifiers of classified tokens --------------------------------------
+
+const sal_uInt8 BIFF_TOKID_ARRAY = 0x00; /// Array constant.
+const sal_uInt8 BIFF_TOKID_FUNC = 0x01; /// Function, fixed number of arguments.
+const sal_uInt8 BIFF_TOKID_FUNCVAR = 0x02; /// Function, variable number of arguments.
+const sal_uInt8 BIFF_TOKID_NAME = 0x03; /// Defined name.
+const sal_uInt8 BIFF_TOKID_REF = 0x04; /// 2D cell reference.
+const sal_uInt8 BIFF_TOKID_AREA = 0x05; /// 2D area reference.
+const sal_uInt8 BIFF_TOKID_MEMAREA = 0x06; /// Constant reference subexpression.
+const sal_uInt8 BIFF_TOKID_MEMERR = 0x07; /// Deleted reference subexpression.
+const sal_uInt8 BIFF_TOKID_MEMNOMEM = 0x08; /// Constant reference subexpression without result.
+const sal_uInt8 BIFF_TOKID_MEMFUNC = 0x09; /// Variable reference subexpression.
+const sal_uInt8 BIFF_TOKID_REFERR = 0x0A; /// Deleted 2D cell reference.
+const sal_uInt8 BIFF_TOKID_AREAERR = 0x0B; /// Deleted 2D area reference.
+const sal_uInt8 BIFF_TOKID_REFN = 0x0C; /// Relative 2D cell reference (in names).
+const sal_uInt8 BIFF_TOKID_AREAN = 0x0D; /// Relative 2D area reference (in names).
+const sal_uInt8 BIFF_TOKID_MEMAREAN = 0x0E; /// Reference subexpression (in names).
+const sal_uInt8 BIFF_TOKID_MEMNOMEMN = 0x0F; /// Reference subexpression (in names) without result.
+const sal_uInt8 BIFF_TOKID_FUNCCE = 0x18;
+const sal_uInt8 BIFF_TOKID_NAMEX = 0x19; /// External reference.
+const sal_uInt8 BIFF_TOKID_REF3D = 0x1A; /// 3D cell reference.
+const sal_uInt8 BIFF_TOKID_AREA3D = 0x1B; /// 3D area reference.
+const sal_uInt8 BIFF_TOKID_REFERR3D = 0x1C; /// Deleted 3D cell reference.
+const sal_uInt8 BIFF_TOKID_AREAERR3D = 0x1D; /// Deleted 3D area reference
+
+// specific token constants ---------------------------------------------------
+
+const sal_uInt8 BIFF_TOK_ARRAY_DOUBLE = 0; /// Double value in an array.
+const sal_uInt8 BIFF_TOK_ARRAY_STRING = 1; /// String value in an array.
+const sal_uInt8 BIFF_TOK_ARRAY_BOOL = 2; /// Boolean value in an array.
+const sal_uInt8 BIFF_TOK_ARRAY_ERROR = 4; /// Error code in an array.
+
+const sal_uInt8 BIFF_TOK_BOOL_FALSE = 0; /// FALSE value of a tBool token.
+const sal_uInt8 BIFF_TOK_BOOL_TRUE = 1; /// TRUE value of a tBool token.
+
+const sal_uInt8 BIFF_TOK_ATTR_VOLATILE = 0x01; /// Volatile function.
+const sal_uInt8 BIFF_TOK_ATTR_IF = 0x02; /// Start of true condition in IF function.
+const sal_uInt8 BIFF_TOK_ATTR_CHOOSE = 0x04; /// Jump array of CHOOSE function.
+const sal_uInt8 BIFF_TOK_ATTR_SKIP = 0x08; /// Skip tokens.
+const sal_uInt8 BIFF_TOK_ATTR_SUM = 0x10; /// SUM function with one parameter.
+const sal_uInt8 BIFF_TOK_ATTR_ASSIGN = 0x20; /// BASIC style assignment.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE = 0x40; /// Spaces in formula representation.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_VOLATILE = 0x41; /// Leading spaces and volatile formula.
+const sal_uInt8 BIFF_TOK_ATTR_IFERROR = 0x80; /// Start of condition in IFERROR function (BIFF12 only).
+
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP = 0x00; /// Spaces before next token.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_BR = 0x01; /// Line breaks before next token.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP_OPEN = 0x02; /// Spaces before opening parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_BR_OPEN = 0x03; /// Line breaks before opening parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP_CLOSE = 0x04; /// Spaces before closing parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_BR_CLOSE = 0x05; /// Line breaks before closing parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP_PRE = 0x06; /// Spaces before formula (BIFF3).
+
+const sal_uInt16 BIFF_TOK_FUNCVAR_CMD = 0x8000; /// Macro command.
+const sal_uInt16 BIFF_TOK_FUNCVAR_FUNCIDMASK = 0x7FFF; /// Mask for function/command index.
+const sal_uInt8 BIFF_TOK_FUNCVAR_CMDPROMPT = 0x80; /// User prompt for macro commands.
+const sal_uInt8 BIFF_TOK_FUNCVAR_COUNTMASK = 0x7F; /// Mask for parameter count.
+
+const sal_uInt16 BIFF12_TOK_REF_COLMASK = 0x3FFF; /// Mask to extract column from reference (BIFF12).
+const sal_Int32 BIFF12_TOK_REF_ROWMASK = 0xFFFFF; /// Mask to extract row from reference (BIFF12).
+const sal_uInt16 BIFF12_TOK_REF_COLREL = 0x4000; /// True = column is relative (BIFF12).
+const sal_uInt16 BIFF12_TOK_REF_ROWREL = 0x8000; /// True = row is relative (BIFF12).
+
+const sal_uInt16 BIFF_TOK_REF_COLMASK = 0x00FF; /// Mask to extract BIFF8 column from reference.
+const sal_uInt16 BIFF_TOK_REF_ROWMASK = 0x3FFF; /// Mask to extract BIFF2-BIFF5 row from reference.
+const sal_uInt16 BIFF_TOK_REF_COLREL = 0x4000; /// True = column is relative.
+const sal_uInt16 BIFF_TOK_REF_ROWREL = 0x8000; /// True = row is relative.
+
+const sal_uInt16 BIFF12_TOK_TABLE_COLUMN = 0x0001; /// Table reference: Single column.
+const sal_uInt16 BIFF12_TOK_TABLE_COLRANGE = 0x0002; /// Table reference: Range of columns.
+const sal_uInt16 BIFF12_TOK_TABLE_ALL = 0x0004; /// Table reference: Special [#All] range.
+const sal_uInt16 BIFF12_TOK_TABLE_HEADERS = 0x0008; /// Table reference: Special [#Headers] range.
+const sal_uInt16 BIFF12_TOK_TABLE_DATA = 0x0010; /// Table reference: Special [#Data] range.
+const sal_uInt16 BIFF12_TOK_TABLE_TOTALS = 0x0020; /// Table reference: Special [#Totals] range.
+const sal_uInt16 BIFF12_TOK_TABLE_THISROW = 0x0040; /// Table reference: Special [#This Row] range.
+const sal_uInt16 BIFF12_TOK_TABLE_SP_BRACKETS = 0x0080; /// Table reference: Spaces in outer brackets.
+const sal_uInt16 BIFF12_TOK_TABLE_SP_SEP = 0x0100; /// Table reference: Spaces after separators.
+const sal_uInt16 BIFF12_TOK_TABLE_ROW = 0x0200; /// Table reference: Single row.
+const sal_uInt16 BIFF12_TOK_TABLE_CELL = 0x0400; /// Table reference: Single cell.
+
+const sal_uInt8 BIFF_TOK_NLR_ERR = 0x01; /// NLR: Invalid/deleted.
+const sal_uInt8 BIFF_TOK_NLR_ROWR = 0x02; /// NLR: Row index.
+const sal_uInt8 BIFF_TOK_NLR_COLR = 0x03; /// NLR: Column index.
+const sal_uInt8 BIFF_TOK_NLR_ROWV = 0x06; /// NLR: Value in row.
+const sal_uInt8 BIFF_TOK_NLR_COLV = 0x07; /// NLR: Value in column.
+const sal_uInt8 BIFF_TOK_NLR_RANGE = 0x0A; /// NLR: Range.
+const sal_uInt8 BIFF_TOK_NLR_SRANGE = 0x0B; /// Stacked NLR: Range.
+const sal_uInt8 BIFF_TOK_NLR_SROWR = 0x0C; /// Stacked NLR: Row index.
+const sal_uInt8 BIFF_TOK_NLR_SCOLR = 0x0D; /// Stacked NLR: Column index.
+const sal_uInt8 BIFF_TOK_NLR_SROWV = 0x0E; /// Stacked NLR: Value in row.
+const sal_uInt8 BIFF_TOK_NLR_SCOLV = 0x0F; /// Stacked NLR: Value in column.
+const sal_uInt8 BIFF_TOK_NLR_RANGEERR = 0x10; /// NLR: Invalid/deleted range.
+const sal_uInt8 BIFF_TOK_NLR_SXNAME = 0x1D; /// NLR: Pivot table name.
+const sal_uInt16 BIFF_TOK_NLR_REL = 0x8000; /// True = NLR is relative.
+const sal_uInt16 BIFF_TOK_NLR_MASK = 0x3FFF; /// Mask to extract BIFF8 column from NLR.
+
+const sal_uInt32 BIFF_TOK_NLR_ADDREL = 0x80000000; /// NLR relative (in appended data).
+const sal_uInt32 BIFF_TOK_NLR_ADDMASK = 0x3FFFFFFF; /// Mask for number of appended ranges.
+
+// function constants ---------------------------------------------------------
+
+const sal_uInt8 OOX_MAX_PARAMCOUNT = 255; /// Maximum parameter count for OOXML/BIFF12 files.
+const sal_uInt8 BIFF_MAX_PARAMCOUNT = 30; /// Maximum parameter count for BIFF2-BIFF8 files.
+
+const sal_uInt16 BIFF_FUNC_IF = 1; /// Function identifier of the IF function.
+const sal_uInt16 BIFF_FUNC_SUM = 4; /// Function identifier of the SUM function.
+const sal_uInt16 BIFF_FUNC_TRUE = 34; /// Function identifier of the TRUE function.
+const sal_uInt16 BIFF_FUNC_FALSE = 35; /// Function identifier of the FALSE function.
+const sal_uInt16 BIFF_FUNC_ROWS = 76; /// Function identifier of the ROWS function.
+const sal_uInt16 BIFF_FUNC_COLUMNS = 77; /// Function identifier of the COLUMNS function.
+const sal_uInt16 BIFF_FUNC_OFFSET = 78; /// Function identifier of the OFFSET function.
+const sal_uInt16 BIFF_FUNC_EXTERNCALL = 255; /// BIFF function id of the EXTERN.CALL function.
+const sal_uInt16 BIFF_FUNC_FLOOR = 285; /// Function identifier of the FLOOR function.
+const sal_uInt16 BIFF_FUNC_CEILING = 288; /// Function identifier of the CEILING function.
+const sal_uInt16 BIFF_FUNC_HYPERLINK = 359; /// Function identifier of the HYPERLINK function.
+const sal_uInt16 BIFF_FUNC_WEEKNUM = 465; /// Function identifier of the WEEKNUM function.
+
+// Formula type ===============================================================
+
+/** Enumerates all possible types of a formula. */
+enum class FormulaType
+{
+ Cell, /// Simple cell formula, or reference to a shared formula name.
+ Array, /// Array (matrix) formula.
+ SharedFormula, /// Shared formula definition.
+ CondFormat, /// Condition of a conditional format rule.
+ Validation /// Condition of a data validation.
+};
+
+// Reference helpers ==========================================================
+
+/** A 2D formula cell reference struct with relative flags. */
+struct BinSingleRef2d
+{
+ sal_Int32 mnCol; /// Column index.
+ sal_Int32 mnRow; /// Row index.
+ bool mbColRel; /// True = relative column reference.
+ bool mbRowRel; /// True = relative row reference.
+
+ explicit BinSingleRef2d();
+
+ void setBiff12Data( sal_uInt16 nCol, sal_Int32 nRow, bool bRelativeAsOffset );
+
+ void readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset );
+};
+
+/** A 2D formula cell range reference struct with relative flags. */
+struct BinComplexRef2d
+{
+ BinSingleRef2d maRef1; /// Start (top-left) cell address.
+ BinSingleRef2d maRef2; /// End (bottom-right) cell address.
+
+ void readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset );
+};
+
+// Token vector, token sequence ===============================================
+
+typedef css::sheet::FormulaToken ApiToken;
+typedef css::uno::Sequence< ApiToken > ApiTokenSequence;
+
+/** Contains the base address and type of a special token representing an array
+ formula or a shared formula (sal_False), or a table operation (sal_True). */
+typedef css::beans::Pair< css::table::CellAddress, sal_Bool > ApiSpecialTokenInfo;
+
+/** A vector of formula tokens with additional convenience functions. */
+class ApiTokenVector
+{
+public:
+ explicit ApiTokenVector();
+
+ ApiToken& operator[]( size_t i ) { return mvTokens[i]; }
+
+ size_t size() const { return mvTokens.size(); }
+
+ ApiToken& back() { return mvTokens.back(); }
+ const ApiToken& back() const { return mvTokens.back(); }
+
+ void clear() { mvTokens.clear(); }
+
+ void pop_back() { mvTokens.pop_back(); }
+
+ void push_back( const ApiToken& rToken ) { mvTokens.push_back( rToken ); }
+
+ void reserve( size_t n ) { mvTokens.reserve( n ); }
+
+ void resize( size_t n ) { mvTokens.resize( n ); }
+
+ /** Appends a new token with the passed op-code, returns its data field. */
+ css::uno::Any& append( sal_Int32 nOpCode );
+
+ /** Appends a new token with the passed op-code and data. */
+ template< typename Type >
+ void append( sal_Int32 nOpCode, const Type& rData ) { append( nOpCode ) <<= rData; }
+
+ /** Converts to a sequence. */
+ ApiTokenSequence toSequence() const;
+
+private:
+ ::std::vector< ApiToken > mvTokens;
+};
+
+// Token sequence iterator ====================================================
+
+/** Token sequence iterator that is able to skip space tokens. */
+class ApiTokenIterator
+{
+public:
+ explicit ApiTokenIterator( const ApiTokenSequence& rTokens, sal_Int32 nSpacesOpCode );
+ bool is() const { return mpToken != mpTokenEnd; }
+ const ApiToken* operator->() const { return mpToken; }
+
+ ApiTokenIterator& operator++();
+
+private:
+ void skipSpaces();
+
+private:
+ const ApiToken* mpToken; /// Pointer to current token of the token sequence.
+ const ApiToken* mpTokenEnd; /// Pointer behind last token of the token sequence.
+ const sal_Int32 mnSpacesOpCode; /// Op-code for whitespace tokens.
+};
+
+// List of API op-codes =======================================================
+
+/** Contains all API op-codes needed to build formulas with tokens. */
+struct ApiOpCodes
+{
+ // special
+ sal_Int32 OPCODE_UNKNOWN; /// Internal: function name unknown to mapper.
+ sal_Int32 OPCODE_EXTERNAL; /// External function call (e.g. add-ins).
+ // formula structure
+ sal_Int32 OPCODE_PUSH; /// Op-code for common value operands.
+ sal_Int32 OPCODE_MISSING; /// Placeholder for a missing function parameter.
+ sal_Int32 OPCODE_SPACES; /// Spaces between other formula tokens.
+ sal_Int32 OPCODE_NAME; /// Index of a defined name.
+ sal_Int32 OPCODE_DBAREA; /// Index of a database area.
+ sal_Int32 OPCODE_NLR; /// Natural language reference.
+ sal_Int32 OPCODE_DDE; /// DDE link function.
+ sal_Int32 OPCODE_MACRO; /// Macro function call.
+ sal_Int32 OPCODE_BAD; /// Bad token (unknown name, formula error).
+ sal_Int32 OPCODE_NONAME; /// Function style #NAME? error.
+ // separators
+ sal_Int32 OPCODE_OPEN; /// Opening parenthesis.
+ sal_Int32 OPCODE_CLOSE; /// Closing parenthesis.
+ sal_Int32 OPCODE_SEP; /// Function parameter separator.
+ // array separators
+ sal_Int32 OPCODE_ARRAY_OPEN; /// Opening brace for constant arrays.
+ sal_Int32 OPCODE_ARRAY_CLOSE; /// Closing brace for constant arrays.
+ sal_Int32 OPCODE_ARRAY_ROWSEP; /// Row separator in constant arrays.
+ sal_Int32 OPCODE_ARRAY_COLSEP; /// Column separator in constant arrays.
+ // unary operators
+ sal_Int32 OPCODE_PLUS_SIGN; /// Unary plus sign.
+ sal_Int32 OPCODE_MINUS_SIGN; /// Unary minus sign.
+ sal_Int32 OPCODE_PERCENT; /// Percent sign.
+ // binary operators
+ sal_Int32 OPCODE_ADD; /// Addition operator.
+ sal_Int32 OPCODE_SUB; /// Subtraction operator.
+ sal_Int32 OPCODE_MULT; /// Multiplication operator.
+ sal_Int32 OPCODE_DIV; /// Division operator.
+ sal_Int32 OPCODE_POWER; /// Power operator.
+ sal_Int32 OPCODE_CONCAT; /// String concatenation operator.
+ sal_Int32 OPCODE_EQUAL; /// Compare equal operator.
+ sal_Int32 OPCODE_NOT_EQUAL; /// Compare not equal operator.
+ sal_Int32 OPCODE_LESS; /// Compare less operator.
+ sal_Int32 OPCODE_LESS_EQUAL; /// Compare less or equal operator.
+ sal_Int32 OPCODE_GREATER; /// Compare greater operator.
+ sal_Int32 OPCODE_GREATER_EQUAL; /// Compare greater or equal operator.
+ sal_Int32 OPCODE_INTERSECT; /// Range intersection operator.
+ sal_Int32 OPCODE_LIST; /// Range list operator.
+ sal_Int32 OPCODE_RANGE; /// Range operator.
+};
+
+// Function parameter info ====================================================
+
+/** Enumerates validity modes for a function parameter. */
+enum class FuncParamValidity
+{
+ Regular, /// Parameter supported by Calc and Excel.
+ CalcOnly, /// Parameter supported by Calc only.
+ ExcelOnly /// Parameter supported by Excel only.
+};
+
+/** Structure that contains all needed information for a parameter in a
+ function.
+
+ The member meValid specifies which application supports the parameter. If
+ set to CALCONLY, import filters have to insert a default value for this
+ parameter, and export filters have to skip the parameter. If set to
+ EXCELONLY, import filters have to skip the parameter, and export filters
+ have to insert a default value for this parameter.
+
+ The member mbValType specifies whether the parameter requires tokens to be
+ of value type (VAL or ARR class).
+
+ If set to false, the parameter is called to be REFTYPE. Tokens with REF
+ default class can be inserted for the parameter (e.g. tAreaR tokens).
+
+ If set to true, the parameter is called to be VALTYPE. Tokens with REF
+ class need to be converted to VAL tokens first (e.g. tAreaR will be
+ converted to tAreaV), and further conversion is done according to this
+ new token class.
+
+ The member meConv specifies how to convert the current token class of the
+ token inserted for the parameter. If the token class is still REF this
+ means that the token has default REF class and the parameter is REFTYPE
+ (see member mbValType), the token will not be converted at all and remains
+ in REF class. Otherwise, token class conversion is depending on the actual
+ token class of the return value of the function containing this parameter.
+ The function may return REF class (tFuncR, tFuncVarR, tFuncCER), or it may
+ return VAL or ARR class (tFuncV, tFuncA, tFuncVarV, tFuncVarA, tFuncCEV,
+ tFuncCEA). Even if the function is able to return REF class, it may return
+ VAL or ARR class instead due to the VALTYPE data type of the parent
+ function parameter that calls the own function. Example: The INDIRECT
+ function returns REF class by default. But if called from a VALTYPE
+ function parameter, e.g. in the formula =ABS(INDIRECT("A1")), it returns
+ VAL or ARR class instead. Additionally, the repeating conversion types RPT
+ and RPX rely on the conversion executed for the function token class.
+
+ 1) ORG:
+ Use the original class of the token (VAL or ARR), regardless of any
+ conversion done for the function return class.
+
+ 2) VAL:
+ Convert ARR tokens to VAL class, regardless of any conversion done for
+ the function return class.
+
+ 3) ARR:
+ Convert VAL tokens to ARR class, regardless of any conversion done for
+ the function return class.
+
+ 4) RPT:
+ If the own function returns REF class (thus it is called from a REFTYPE
+ parameter, see above), and the parent conversion type (for the function
+ return class) was ORG, VAL, or ARR, ignore that conversion and always
+ use VAL conversion for the own token instead. If the parent conversion
+ type was RPT or RPX, repeat the conversion that would have been used if
+ the function would return value type.
+ If the own function returns value type (VAL or ARR class, see above),
+ and the parent conversion type (for the function return class) was ORG,
+ VAL, ARR, or RPT, repeat this conversion for the own token. If the
+ parent conversion type was RPX, always use ORG conversion type for the
+ own token instead.
+
+ 5) RPX:
+ This type of conversion only occurs in functions returning VAL class by
+ default. If the own token is value type, and the VAL return class of
+ the own function has been changed to ARR class (due to direct ARR
+ conversion, or due to ARR conversion repeated by RPT or RPX), set the
+ own token to ARR type. Otherwise use the original token type (VAL
+ conversion from parent parameter will not be repeated at all). If
+ nested functions have RPT or value-type RPX parameters, they will not
+ repeat this conversion type, but will use ORG conversion instead (see
+ description of RPT above).
+
+ 6) RPO:
+ This type of conversion is only used for the operands of all operators
+ (unary and binary arithmetic operators, comparison operators, and range
+ operators). It is not used for function parameters. On conversion, it
+ will be replaced by the last conversion type that was not the RPO
+ conversion. This leads to a slightly different behaviour than the RPT
+ conversion for operands in conjunction with a parent RPX conversion.
+ */
+struct FunctionParamInfo
+{
+ FuncParamValidity meValid; /// Parameter validity.
+};
+
+// Function data ==============================================================
+
+/** This enumeration contains constants for all known external libraries
+ containing supported sheet functions. */
+enum FunctionLibraryType
+{
+ FUNCLIB_UNKNOWN = 0, /// Unknown library (must be zero).
+ FUNCLIB_EUROTOOL /// EuroTool add-in with EUROCONVERT function.
+};
+
+/** Represents information for a spreadsheet function.
+
+ The member mpParamInfos points to a C-array of type information structures
+ for all parameters of the function. The last initialized structure
+ describing a regular parameter (member meValid == FuncParamValidity::Regular) in
+ this array is used repeatedly for all following parameters supported by a
+ function.
+ */
+struct FunctionInfo
+{
+ OUString maOdfFuncName; /// ODF function name.
+ OUString maOoxFuncName; /// OOXML function name.
+ OUString maBiffMacroName; /// Expected macro name in EXTERN.CALL function.
+ OUString maExtProgName; /// Programmatic function name for external functions.
+ FunctionLibraryType meFuncLibType; /// The external library this function is part of.
+ sal_Int32 mnApiOpCode; /// API function opcode.
+ sal_uInt16 mnBiff12FuncId; /// BIFF12 function identifier.
+ sal_uInt16 mnBiffFuncId; /// BIFF2-BIFF8 function identifier.
+ sal_uInt8 mnMinParamCount; /// Minimum number of parameters.
+ sal_uInt8 mnMaxParamCount; /// Maximum number of parameters.
+ sal_uInt8 mnRetClass; /// BIFF token class of the return value.
+ const FunctionParamInfo* mpParamInfos; /// Information about all parameters.
+ bool mbParamPairs; /// True = optional parameters are expected to appear in pairs.
+ bool mbVolatile; /// True = volatile function.
+ bool mbExternal; /// True = external function in Calc.
+ bool mbInternal; /// True = internal function in Calc. (Both can be true!)
+ bool mbMacroFunc; /// True = macro sheet function or command.
+ bool mbVarParam; /// True = use a tFuncVar token, also if min/max are equal.
+};
+
+typedef RefVector< FunctionInfo > FunctionInfoVector;
+
+// Function info parameter class iterator =====================================
+
+/** Iterator working on the mpParamInfos member of the FunctionInfo struct.
+
+ This iterator can be used to iterate through the array containing the
+ token class conversion information of function parameters. This iterator
+ repeats the last valid structure in the array - it stops automatically
+ before the first empty array entry or before the end of the array, even for
+ repeated calls to the increment operator.
+ */
+class FunctionParamInfoIterator
+{
+public:
+ explicit FunctionParamInfoIterator( const FunctionInfo& rFuncInfo );
+
+ bool isCalcOnlyParam() const;
+ bool isExcelOnlyParam() const;
+ FunctionParamInfoIterator& operator++();
+
+private:
+ const FunctionParamInfo* mpParamInfo;
+ const FunctionParamInfo* mpParamInfoEnd;
+ bool mbParamPairs;
+};
+
+// Base function provider =====================================================
+
+struct FunctionProviderImpl;
+
+/** Provides access to function info structs for all available sheet functions.
+ */
+class FunctionProvider // not derived from WorkbookHelper to make it usable in file dumpers
+{
+public:
+ explicit FunctionProvider(bool bImportFilter);
+ virtual ~FunctionProvider();
+
+ FunctionProvider(FunctionProvider const &) = default;
+ FunctionProvider(FunctionProvider &&) = default;
+ FunctionProvider & operator =(FunctionProvider const &) = delete;
+ FunctionProvider & operator =(FunctionProvider &&) = delete;
+
+ /** Returns the function info for an OOXML function name, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const;
+
+ /** Returns the function info for a BIFF12 function index, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromBiff12FuncId( sal_uInt16 nFuncId ) const;
+
+ /** Returns the function info for a macro function referred by the
+ EXTERN.CALL function, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromMacroName( const OUString& rFuncName ) const;
+
+ /** Returns the library type associated with the passed URL of a function
+ library (function add-in). */
+ static FunctionLibraryType getFuncLibTypeFromLibraryName( std::u16string_view rLibraryName );
+
+protected:
+ /** Returns the list of all function infos. */
+ const FunctionInfoVector& getFuncs() const;
+
+private:
+ typedef std::shared_ptr< FunctionProviderImpl > FunctionProviderImplRef;
+ FunctionProviderImplRef mxFuncImpl; /// Shared implementation between all copies of the provider.
+};
+
+// Op-code and function provider ==============================================
+
+struct OpCodeProviderImpl;
+
+/** Provides access to API op-codes for all available formula tokens and to
+ function info structs for all available sheet functions.
+ */
+class OpCodeProvider : public FunctionProvider // not derived from WorkbookHelper to make it usable as UNO service
+{
+public:
+ explicit OpCodeProvider(const css::uno::Reference<css::lang::XMultiServiceFactory>& rxModelFactory,
+ bool bImportFilter);
+ virtual ~OpCodeProvider() override;
+
+ OpCodeProvider(OpCodeProvider const &) = default;
+ OpCodeProvider(OpCodeProvider &&) = default;
+ OpCodeProvider & operator =(OpCodeProvider const &) = delete;
+ OpCodeProvider & operator =(OpCodeProvider &&) = delete;
+
+ /** Returns the structure containing all token op-codes for operators and
+ special tokens used by the Calc document and its formula parser. */
+ const ApiOpCodes& getOpCodes() const;
+
+ /** Returns the function info for an API token, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromApiToken( const ApiToken& rToken ) const;
+
+ /** Returns the op-code map that is used by the OOXML formula parser. */
+ css::uno::Sequence< css::sheet::FormulaOpCodeMapEntry >
+ getOoxParserMap() const;
+
+private:
+ typedef std::shared_ptr< OpCodeProviderImpl > OpCodeProviderImplRef;
+ OpCodeProviderImplRef mxOpCodeImpl; /// Shared implementation between all copies of the provider.
+};
+
+// API formula parser wrapper =================================================
+
+/** A wrapper around the FormulaParser service provided by the Calc document. */
+class ApiParserWrapper : public OpCodeProvider
+{
+public:
+ explicit ApiParserWrapper(
+ const css::uno::Reference< css::lang::XMultiServiceFactory >& rxModelFactory,
+ const OpCodeProvider& rOpCodeProv );
+
+ /** Returns read/write access to the formula parser property set. */
+ PropertySet& getParserProperties() { return maParserProps; }
+
+ /** Calls the XFormulaParser::parseFormula() function of the API parser. */
+ ApiTokenSequence parseFormula(
+ const OUString& rFormula,
+ const ScAddress& rRefPos );
+
+private:
+ css::uno::Reference< css::sheet::XFormulaParser >
+ mxParser;
+ PropertySet maParserProps;
+};
+
+// Formula parser/printer base class for filters ==============================
+
+/** Base class for import formula parsers and export formula compilers. */
+class FormulaProcessorBase : public OpCodeProvider, protected ApiOpCodes, public WorkbookHelper
+{
+public:
+ explicit FormulaProcessorBase( const WorkbookHelper& rHelper );
+
+ /** Generates a cell address string in A1 notation from the passed cell
+ address.
+
+ @param rAddress The cell address containing column and row index.
+ @param bAbsolute True = adds dollar signs before column and row.
+ */
+ static OUString generateAddress2dString(
+ const ScAddress& rAddress,
+ bool bAbsolute );
+
+ /** Generates a cell address string in A1 notation from the passed binary
+ cell address.
+
+ @param rAddress The cell address containing column and row index.
+ @param bAbsolute True = adds dollar signs before column and row.
+ */
+ static OUString generateAddress2dString(
+ const BinAddress& rAddress,
+ bool bAbsolute );
+
+ /** Generates an array string in Calc formula notation from the passed
+ matrix with Any's containing double values or strings.
+
+ @param rMatrix The matrix containing double values or strings.
+ */
+ static OUString generateApiArray( const Matrix< css::uno::Any >& rMatrix );
+
+ /** Tries to extract a single cell reference from a formula token sequence.
+
+ @param rTokens The token sequence to be parsed. Should contain exactly
+ one address token or cell range address token. The token sequence
+ may contain whitespace tokens.
+
+ @return If the token sequence is valid, this function returns an Any
+ containing a com.sun.star.sheet.SingleReference object, or a
+ com.sun.star.sheet.ComplexReference object. If the token sequence
+ contains too many, or unexpected tokens, an empty Any is returned.
+ */
+ css::uno::Any
+ extractReference( const ApiTokenSequence& rTokens ) const;
+
+ /** Tries to extract a cell range address from a formula token sequence.
+ Only real absolute references will be accepted.
+
+ @param orAddress (output parameter) If the token sequence is valid,
+ this parameter will contain the extracted cell range address. If
+ the token sequence contains unexpected tokens, nothing meaningful
+ is inserted, and the function returns false.
+
+ @param rTokens The token sequence to be parsed. Should contain exactly
+ one cell range address token. The token sequence may contain
+ whitespace tokens.
+
+ @return True, if the token sequence contains a valid cell range
+ address which has been extracted to orRange, false otherwise.
+ */
+ bool extractCellRange(
+ ScRange& orRange,
+ const ApiTokenSequence& rTokens ) const;
+
+ /** Tries to extract a cell range list from a formula token sequence.
+ Only real absolute references will be accepted.
+
+ @param orRanges (output parameter) If the token sequence is valid,
+ this parameter will contain the extracted cell range list. Deleted
+ cells or cell ranges (shown as #REF! error in a formula) will be
+ skipped. If the token sequence contains unexpected tokens, an empty
+ list is returned here.
+
+ @param rTokens The token sequence to be parsed. Should contain cell
+ address tokens or cell range address tokens, separated by the
+ standard function parameter separator token. The token sequence may
+ contain parentheses and whitespace tokens.
+
+ @param nFilterBySheet If non-negative, this function returns only cell
+ ranges located in the specified sheet, otherwise returns all cell
+ ranges contained in the token sequence.
+ */
+ void extractCellRangeList(
+ ScRangeList& orRanges,
+ const ApiTokenSequence& rTokens,
+ sal_Int32 nFilterBySheet ) const;
+
+ /** Tries to extract a string from a formula token sequence.
+
+ @param orString (output parameter) The extracted string.
+
+ @param rTokens The token sequence to be parsed. Should contain exactly
+ one string token, may contain whitespace tokens.
+
+ @return True = token sequence is valid, output parameter orString
+ contains the string extracted from the token sequence.
+ */
+ bool extractString(
+ OUString& orString,
+ const ApiTokenSequence& rTokens ) const;
+
+ /** Tries to extract information about a special token used for array
+ formulas, shared formulas, or table operations.
+
+ @param orTokenInfo (output parameter) The extracted information about
+ the token. Contains the base address and the token type (sal_False
+ for array or shared formulas, sal_True for table operations).
+
+ @param rTokens The token sequence to be parsed. If it contains exactly
+ one OPCODE_BAD token with special token information, this
+ information will be extracted.
+
+ @return True = token sequence is valid, output parameter orTokenInfo
+ contains the token information extracted from the token sequence.
+ */
+ bool extractSpecialTokenInfo(
+ ApiSpecialTokenInfo& orTokenInfo,
+ const ApiTokenSequence& rTokens ) const;
+
+ /** Converts a single string with separators in the passed formula token
+ sequence to a list of string tokens.
+
+ @param orTokens (input/output parameter) Expects a single string token
+ in this token sequence (whitespace tokens are allowed). The string
+ is split into substrings. A list of string tokens separated with
+ parameter separator tokens is returned in this parameter.
+
+ @param cStringSep The separator character used to split the input
+ string.
+
+ @param bTrimLeadingSpaces True = removes leading whitespace from all
+ substrings inserted into the formula token sequence.
+ */
+ void convertStringToStringList(
+ ApiTokenSequence& orTokens,
+ sal_Unicode cStringSep,
+ bool bTrimLeadingSpaces ) const;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formulabuffer.hxx b/sc/source/filter/inc/formulabuffer.hxx
new file mode 100644
index 0000000000..512b1feb3c
--- /dev/null
+++ b/sc/source/filter/inc/formulabuffer.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "workbookhelper.hxx"
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace oox::xls {
+
+class FormulaBuffer final : public WorkbookHelper
+{
+public:
+ /**
+ * Represents a shared formula definition.
+ */
+ struct SharedFormulaEntry
+ {
+ ScAddress maAddress;
+ OUString maTokenStr;
+ sal_Int32 mnSharedId;
+
+ SharedFormulaEntry(
+ const ScAddress& rAddress,
+ OUString aTokenStr, sal_Int32 nSharedId );
+ };
+
+ /**
+ * Represents a formula cell that uses shared formula.
+ */
+ struct SharedFormulaDesc
+ {
+ ScAddress maAddress;
+ OUString maCellValue;
+ sal_Int32 mnSharedId;
+ sal_Int32 mnValueType;
+
+ SharedFormulaDesc(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ OUString aCellValue, sal_Int32 nValueType );
+ };
+
+ struct TokenAddressItem
+ {
+ OUString maTokenStr;
+ ScAddress maAddress;
+ TokenAddressItem( OUString aTokenStr, const ScAddress& rAddress ) : maTokenStr(std::move( aTokenStr )), maAddress( rAddress ) {}
+ };
+
+ struct TokenRangeAddressItem
+ {
+ TokenAddressItem maTokenAndAddress;
+ ScRange maRange;
+ TokenRangeAddressItem( TokenAddressItem aTokenAndAddress, const ScRange& rRange ) : maTokenAndAddress(std::move( aTokenAndAddress )), maRange( rRange ) {}
+ };
+
+ struct FormulaValue
+ {
+ ScAddress maAddress;
+ OUString maValueStr;
+ sal_Int32 mnCellType;
+ };
+
+ struct SheetItem
+ {
+ std::vector<TokenAddressItem>* mpCellFormulas;
+ std::vector<TokenRangeAddressItem>* mpArrayFormulas;
+ std::vector<FormulaValue>* mpCellFormulaValues;
+ std::vector<SharedFormulaEntry>* mpSharedFormulaEntries;
+ std::vector<SharedFormulaDesc>* mpSharedFormulaIDs;
+
+ SheetItem();
+ };
+
+private:
+
+ std::mutex maMtxData;
+ // Vectors indexed by SCTAB - cf. SetSheetCount
+ std::vector< std::vector<TokenAddressItem> > maCellFormulas;
+ std::vector< std::vector<TokenRangeAddressItem> > maCellArrayFormulas;
+ std::vector< std::vector<SharedFormulaEntry> > maSharedFormulas; // sheet -> stuff needed to create shared formulae
+ std::vector< std::vector<SharedFormulaDesc> > maSharedFormulaIds; // sheet -> list of shared formula descriptions
+ std::vector< std::vector<FormulaValue> > maCellFormulaValues; // sheet -> stuff needed to create shared formulae
+
+ SheetItem getSheetItem( SCTAB nTab );
+
+public:
+ explicit FormulaBuffer( const WorkbookHelper& rHelper );
+ void finalizeImport();
+ void setCellFormula( const ScAddress& rAddress, const OUString& );
+
+ void setCellFormula(
+ const ScAddress& rAddress, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType );
+
+ void setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType );
+
+ void setCellArrayFormula( const ScRange& rRangeAddress,
+ const ScAddress& rTokenAddress,
+ const OUString& );
+
+ void createSharedFormulaMapEntry( const ScAddress& rAddress,
+ sal_Int32 nSharedId, const OUString& rTokens );
+
+ /// ensure sizes of vectors matches the number of sheets
+ void SetSheetCount( SCTAB nSheets );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formulaparser.hxx b/sc/source/filter/inc/formulaparser.hxx
new file mode 100644
index 0000000000..6b9c8fa8fa
--- /dev/null
+++ b/sc/source/filter/inc/formulaparser.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "formulabase.hxx"
+
+namespace oox::xls {
+
+// formula finalizer ==========================================================
+
+/** A generic formula token array finalizer.
+
+ After building a formula token array from alien binary file formats, or
+ parsing an XML formula string using the com.sun.star.sheet.FormulaParser
+ service, the token array is still not ready to be put into the spreadsheet
+ document. There may be functions with a wrong number of parameters (missing
+ but required parameters, or unsupported parameters) or intermediate tokens
+ used to encode references to macro functions or add-in functions. This
+ helper processes a passed token array and builds a new compatible token
+ array.
+
+ Derived classes may add more functionality by overwriting the virtual
+ functions.
+ */
+class FormulaFinalizer : public OpCodeProvider, protected ApiOpCodes
+{
+public:
+ explicit FormulaFinalizer( const OpCodeProvider& rOpCodeProv );
+
+ /** Finalizes and returns the passed token array. */
+ ApiTokenSequence finalizeTokenArray( const ApiTokenSequence& rTokens );
+
+protected:
+ /** Derived classed may try to find a function info struct from the passed
+ string extracted from an OPCODE_BAD token.
+
+ @param rTokenData The string that has been found in an OPCODE_BAD
+ token preceding the function parentheses.
+ */
+ virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const;
+
+ /** Derived classed may try to find the name of a defined name with the
+ passed index extracted from an OPCODE_NAME token.
+
+ @param nTokenIndex The index of the defined name that has been found
+ in an OPCODE_NAME token preceding the function parentheses.
+ */
+ virtual OUString resolveDefinedName( sal_Int32 nTokenIndex ) const;
+
+private:
+ typedef ::std::vector< const ApiToken* > ParameterPosVector;
+
+ const FunctionInfo* getFunctionInfo( ApiToken& orFuncToken );
+ const FunctionInfo* getExternCallInfo( ApiToken& orFuncToken, const ApiToken& rECToken );
+
+ void processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd );
+ const ApiToken* processParameters( const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd );
+
+ bool isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ const ApiToken* getSingleToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ const ApiToken* skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ const ApiToken* findParameters( ParameterPosVector& rParams, const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ void appendEmptyParameter( const FunctionInfo& rFuncInfo, size_t nParam );
+ void appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam, size_t nParamCount );
+ void appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount );
+
+ bool appendFinalToken( const ApiToken& rToken );
+
+private:
+ ApiTokenVector maTokens;
+};
+
+class FormulaParserImpl;
+
+/** Import formula parser for OOXML and BIFF filters.
+
+ This class implements formula import for the OOXML and BIFF filter. One
+ instance is contained in the global filter data to prevent construction and
+ destruction of internal buffers for every imported formula.
+ */
+class FormulaParser final : public FormulaProcessorBase
+{
+public:
+ explicit FormulaParser( const WorkbookHelper& rHelper );
+ virtual ~FormulaParser() override;
+
+ /** Converts an OOXML formula string. */
+ ApiTokenSequence importFormula(
+ const ScAddress& rBaseAddr,
+ const OUString& rFormulaString ) const;
+
+ /** Imports and converts a BIFF12 token array from the passed stream. */
+ ApiTokenSequence importFormula(
+ const ScAddress& rBaseAddr,
+ FormulaType eType,
+ SequenceInputStream& rStrm ) const;
+
+ /** Converts the passed XML formula to an OLE link target. */
+ OUString importOleTargetLink( std::u16string_view aFormulaString );
+
+ /** Imports and converts an OLE link target from the passed stream. */
+ OUString importOleTargetLink( SequenceInputStream& rStrm );
+
+ /** Converts the passed formula to a macro name for a drawing shape. */
+ OUString importMacroName( std::u16string_view aFormulaString );
+
+private:
+ ::std::unique_ptr< FormulaParserImpl > mxImpl;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/fprogressbar.hxx b/sc/source/filter/inc/fprogressbar.hxx
new file mode 100644
index 0000000000..ea9dafa242
--- /dev/null
+++ b/sc/source/filter/inc/fprogressbar.hxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <rtl/ustring.hxx>
+#include <progress.hxx>
+#include <unotools/resmgr.hxx>
+
+class SfxObjectShell;
+class SvStream;
+
+const sal_Int32 SCF_INV_SEGMENT = -1;
+
+/** Progress bar for complex progress representation.
+
+ The progress bar contains one or more segments, each with customizable
+ size. Each segment is represented by a unique identifier. While showing the
+ progress bar, several segments can be started simultaneously. The progress
+ bar displays the sum of all started segments on screen.
+
+ It is possible to create a full featured ScfProgressBar object from
+ any segment. This sub progress bar works only on that parent segment, with
+ the effect, that if the sub progress bar reaches 100%, the parent segment is
+ filled completely.
+
+ After adding segments, the progress bar has to be activated. In this step the
+ total size of all segments is calculated. Therefore it is not possible to add
+ more segments from here.
+
+ If a sub progress bar is created from a segment, and the main progress bar
+ has been started (but not the sub progress bar), it is still possible to add
+ segments to the sub progress bar. It is not allowed to get the sub progress bar
+ of a started segment. And it is not allowed to modify the segment containing
+ a sub progress bar directly.
+
+ Following a few code examples, how to use the progress bar.
+
+ Example 1: Simple progress bar (see also ScfSimpleProgressBar below).
+
+ ScfProgressBar aProgress( ... );
+ sal_Int32 nSeg = aProgress.AddSegment( 50 ); // segment with 50 steps (1 step = 2%)
+
+ aProgress.ActivateSegment( nSeg ); // start segment nSeg
+ aProgress.Progress(); // 0->1; display: 2%
+ aProgress.ProgressAbs( 9 ); // 1->9; display: 18%
+
+ Example 2: Progress bar with 2 segments.
+
+ ScfProgressBar aProgress( ... );
+ sal_Int32 nSeg1 = aProgress.AddSegment( 70 ); // segment with 70 steps
+ sal_Int32 nSeg2 = aProgress.AddSegment( 30 ); // segment with 30 steps
+ // both segments: 100 steps (1 step = 1%)
+
+ aProgress.ActivateSegment( nSeg1 ); // start first segment
+ aProgress.Progress(); // 0->1, display: 1%
+ aProgress.Progress( 2 ); // 1->3, display: 3%
+ aProgress.ActivateSegment( nSeg2 ); // start second segment
+ aProgress.Progress( 5 ); // 0->5, display: 8% (5+3 steps)
+ aProgress.ActivateSegment( nSeg1 ); // continue with first segment
+ aProgress.Progress(); // 3->4, display: 9% (5+4 steps)
+
+ Example 3: Progress bar with 2 segments, one contains a sub progress bar.
+
+ ScfProgressBar aProgress( ... );
+ sal_Int32 nSeg1 = aProgress.AddSegment( 75 ); // segment with 75 steps
+ sal_Int32 nSeg2 = aProgress.AddSegment( 25 ); // segment with 25 steps
+ // both segments: 100 steps (1 step = 1%)
+
+ aProgress.ActivateSegment( nSeg1 ); // start first segment
+ aProgress.Progress(); // 0->1, display: 1%
+
+ ScfProgressBar& rSubProgress = aProgress.GetSegmentProgressBar( nSeg2 );
+ // sub progress bar from second segment
+ sal_Int32 nSubSeg = rSubProgress.AddSegment( 5 ); // 5 steps, mapped to second segment
+ // => 1 step = 5 steps in parent = 5%
+
+ rSubProgress.ActivateSegment( nSubSeg ); // start the segment (auto activate parent segment)
+ rSubProgress.Progress(); // 0->1 (0->5 in parent); display: 6% (1+5)
+
+ // not allowed (second segment active): aProgress.Progress();
+ // not allowed (first segment not empty): aProgress.GetSegmentProgressBar( nSeg1 );
+ */
+class ScfProgressBar final
+{
+public:
+ ScfProgressBar(const ScfProgressBar&) = delete;
+ const ScfProgressBar operator=(const ScfProgressBar&) = delete;
+
+ explicit ScfProgressBar(SfxObjectShell* pDocShell, OUString aText);
+ explicit ScfProgressBar(SfxObjectShell* pDocShell, TranslateId pResId);
+ ~ScfProgressBar();
+
+ /** Adds a new segment to the progress bar.
+ @return the identifier of the segment. */
+ sal_Int32 AddSegment( std::size_t nSize );
+ /** Returns a complete progress bar for the specified segment.
+ @descr The progress bar can be used to create sub segments inside of the
+ segment. Do not delete it (done by root progress bar)!
+ @return A reference to an ScfProgressBar connected to the segment. */
+ ScfProgressBar& GetSegmentProgressBar( sal_Int32 nSegment );
+
+ /** Returns true, if the current progress segment is already full. */
+ bool IsFull() const;
+
+ /** Starts the progress bar or activates another segment. */
+ void ActivateSegment( sal_Int32 nSegment );
+ /** Starts the progress bar (with first segment). */
+ void Activate() { ActivateSegment( 0 ); }
+ /** Set current segment to the specified absolute position. */
+ void ProgressAbs( std::size_t nPos );
+ /** Increase current segment by the passed value. */
+ void Progress( std::size_t nDelta = 1 );
+
+private:
+ struct ScfProgressSegment;
+
+ /** Used to create sub progress bars. */
+ explicit ScfProgressBar(
+ ScfProgressBar& rParProgress,
+ ScfProgressSegment* pParSegment );
+
+ /** Initializes all members on construction. */
+ void Init( SfxObjectShell* pDocShell );
+
+ /** Returns the segment specified by list index. */
+ ScfProgressSegment* GetSegment( sal_Int32 nSegment );
+ /** Activates progress bar and sets current segment. */
+ void SetCurrSegment( ScfProgressSegment* pSegment );
+ /** Increases mnTotalPos and calls the system progress bar. */
+ void IncreaseProgressBar( std::size_t nDelta );
+
+private:
+ /** Contains all data of a segment of the progress bar. */
+ struct ScfProgressSegment
+ {
+ typedef ::std::unique_ptr< ScfProgressBar > ScfProgressBarPtr;
+
+ ScfProgressBarPtr mxProgress; /// Pointer to sub progress bar for this segment.
+ std::size_t mnSize; /// Size of this segment.
+ std::size_t mnPos; /// Current position of this segment.
+
+ explicit ScfProgressSegment( std::size_t nSize );
+ ~ScfProgressSegment();
+ };
+
+ typedef ::std::unique_ptr< ScProgress > ScProgressPtr;
+ typedef std::vector< std::unique_ptr<ScfProgressSegment> > ScfSegmentList;
+
+ ScfSegmentList maSegments; /// List of progress segments.
+ OUString maText; /// UI string for system progress.
+
+ ScProgressPtr mxSysProgress; /// System progress bar.
+ SfxObjectShell* mpDocShell; /// The document shell for the progress bar.
+ ScfProgressBar* mpParentProgress; /// Parent progress bar, if this is a segment progress bar.
+ ScfProgressSegment* mpParentSegment; /// Parent segment, if this is a segment progress bar.
+ ScfProgressSegment* mpCurrSegment; /// Current segment for progress.
+
+ std::size_t mnTotalSize; /// Total size of all segments.
+ std::size_t mnTotalPos; /// Sum of positions of all segments.
+ std::size_t mnUnitSize; /// Size between two calls of system progress.
+ std::size_t mnNextUnitPos; /// Limit for next system progress call.
+ std::size_t mnSysProgressScale; /// Additionally scaling factor for system progress.
+ bool mbInProgress; /// true = progress bar started.
+};
+
+/** A simplified progress bar with only one segment. */
+class ScfSimpleProgressBar
+{
+public:
+ explicit ScfSimpleProgressBar(std::size_t nSize, SfxObjectShell* pDocShell, const OUString& rText);
+ explicit ScfSimpleProgressBar(std::size_t nSize, SfxObjectShell* pDocShell, TranslateId pResId);
+
+ /** Set progress bar to the specified position. */
+ void ProgressAbs( std::size_t nPos ) { maProgress.ProgressAbs( nPos ); }
+
+private:
+ /** Initializes and starts the progress bar. */
+ void Init( std::size_t nSize );
+
+private:
+ ScfProgressBar maProgress; /// The used progress bar.
+};
+
+/** A simplified progress bar based on the stream position of an existing stream. */
+class ScfStreamProgressBar
+{
+public:
+ explicit ScfStreamProgressBar( SvStream& rStrm, SfxObjectShell* pDocShell );
+
+ /** Sets the progress bar to the current stream position. */
+ void Progress();
+
+private:
+ /** Initializes and starts the progress bar. */
+ void Init( SfxObjectShell* pDocShell, const OUString& rText );
+
+private:
+ typedef ::std::unique_ptr< ScfSimpleProgressBar > ScfSimpleProgressBarPtr;
+
+ ScfSimpleProgressBarPtr mxProgress; /// The used progress bar.
+ SvStream& mrStrm; /// The used stream.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/ftools.hxx b/sc/source/filter/inc/ftools.hxx
new file mode 100644
index 0000000000..5e5b8dd6c5
--- /dev/null
+++ b/sc/source/filter/inc/ftools.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <vector>
+#include <limits>
+#include <string_view>
+
+#include <tools/ref.hxx>
+#include <filter.hxx>
+
+// Common macros ==============================================================
+
+// items and item sets --------------------------------------------------------
+
+/** Expands to the item (with type 'itemtype') with Which-ID 'which'. */
+#define GETITEM( itemset, itemtype, which ) \
+ static_cast< const itemtype & >( (itemset).Get( which ) )
+
+/** Expands to the value of the SfxBoolItem with Which-ID 'which'. */
+#define GETITEMBOOL( itemset, which ) \
+ (static_cast<const SfxBoolItem &>( (itemset).Get( which )).GetValue() )
+
+// Global static helpers ======================================================
+
+// Value range limit helpers --------------------------------------------------
+
+/** Returns the value, if it is not less than nMin, otherwise nMin. */
+template< typename ReturnType, typename Type >
+inline ReturnType llimit_cast( Type nValue, ReturnType nMin )
+{ return static_cast< ReturnType >( ::std::max< Type >( nValue, nMin ) ); }
+
+/** Returns the value, if it is not greater than nMax, otherwise nMax. */
+template< typename ReturnType, typename Type >
+inline ReturnType ulimit_cast( Type nValue, ReturnType nMax )
+{ return static_cast< ReturnType >( ::std::min< Type >( nValue, nMax ) ); }
+
+/** Returns the value, if it fits into ReturnType, otherwise the maximum value of ReturnType. */
+template< typename ReturnType, typename Type >
+inline ReturnType ulimit_cast( Type nValue )
+{ return ulimit_cast( nValue, ::std::numeric_limits< ReturnType >::max() ); }
+
+/** Returns the value, if it is not less than nMin and not greater than nMax, otherwise one of the limits. */
+template< typename ReturnType, typename Type >
+inline ReturnType limit_cast( Type nValue, ReturnType nMin, ReturnType nMax )
+{ return static_cast< ReturnType >( ::std::clamp< Type >( nValue, nMin, nMax ) ); }
+
+/** Returns the value, if it fits into ReturnType, otherwise one of the limits of ReturnType. */
+template< typename ReturnType, typename Type >
+inline ReturnType limit_cast( Type nValue )
+{ return limit_cast( nValue, ::std::numeric_limits< ReturnType >::min(), ::std::numeric_limits< ReturnType >::max() ); }
+
+// Read from bitfields --------------------------------------------------------
+
+/** Returns true, if at least one of the bits set in nMask is set in nBitField. */
+template< typename Type >
+inline bool get_flag( Type nBitField, Type nMask )
+{ return (nBitField & nMask) != 0; }
+
+/** Returns nSet, if at least one bit of nMask is set in nBitField, otherwise nUnset. */
+template< typename ReturnType, typename Type >
+inline ReturnType get_flagvalue( Type nBitField, Type nMask, ReturnType nSet, ReturnType nUnset )
+{ return ::get_flag( nBitField, nMask ) ? nSet : nUnset; }
+
+/** Extracts a value from a bit field.
+ @descr Returns in rnRet the data fragment from nBitField, that starts at bit nStartBit
+ (0-based, bit 0 is rightmost) with the width of nBitCount. rnRet will be right-aligned (normalized).
+ For instance: extract_value( n, 0x4321, 8, 4 ) stores 3 in n (value in bits 8-11). */
+template< typename ReturnType, typename Type >
+inline ReturnType extract_value( Type nBitField, sal_uInt8 nStartBit, sal_uInt8 nBitCount )
+{ return static_cast< ReturnType >( ((1UL << nBitCount) - 1) & (nBitField >> nStartBit) ); }
+
+// Write to bitfields ---------------------------------------------------------
+
+/** Sets or clears (according to bSet) all set bits of nMask in rnBitField. */
+template< typename Type >
+inline void set_flag( Type& rnBitField, Type nMask, bool bSet = true )
+{ if( bSet ) rnBitField |= nMask; else rnBitField &= ~nMask; }
+
+/** Inserts a value into a bitfield.
+ @descr Inserts the lower nBitCount bits of nValue into rnBitField, starting
+ there at bit nStartBit. Other contents of rnBitField keep unchanged. */
+template< typename Type, typename InsertType >
+void insert_value( Type& rnBitField, InsertType nValue, sal_uInt8 nStartBit, sal_uInt8 nBitCount )
+{
+ unsigned int nMask = (1U << nBitCount) - 1;
+ Type nNewValue = static_cast< Type >( nValue & nMask );
+ rnBitField = (rnBitField & ~(nMask << nStartBit)) | (nNewValue << nStartBit);
+}
+
+class Color;
+class SfxPoolItem;
+class SfxItemSet;
+class ScStyleSheet;
+class ScStyleSheetPool;
+class SvStream;
+class SotStorage;
+class SotStorageStream;
+
+/** Contains static methods used anywhere in the filters. */
+class ScfTools
+{
+public:
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static items. */
+ ScfTools() = delete;
+ ScfTools(const ScfTools&) = delete;
+ const ScfTools& operator=(const ScfTools&) = delete;
+
+// *** common methods *** -----------------------------------------------------
+
+ /** Reads a 10-byte-long-double and converts it to double. */
+ static void ReadLongDouble(SvStream& rStrm, double& fResult);
+ /** Returns system text encoding for byte string conversion. */
+ static rtl_TextEncoding GetSystemTextEncoding();
+ /** Returns a string representing the hexadecimal value of nValue. */
+ static OUString GetHexStr( sal_uInt16 nValue );
+
+ /** Mixes RGB components with given transparence.
+ @param nTrans Foreground transparence (0x00 == full nFore ... 0x80 = full nBack). */
+ static sal_uInt8 GetMixedColorComp( sal_uInt8 nFore, sal_uInt8 nBack, sal_uInt8 nTrans );
+ /** Mixes colors with given transparence.
+ @param nTrans Foreground transparence (0x00 == full rFore ... 0x80 = full rBack). */
+ static Color GetMixedColor( const Color& rFore, const Color& rBack, sal_uInt8 nTrans );
+
+// *** conversion of names *** ------------------------------------------------
+
+ /** Converts a string to a valid Calc defined name or database range name.
+ @descr Defined names in Calc may contain letters, digits (*), underscores, periods (*),
+ colons (*), question marks, and dollar signs.
+ (*) = not allowed at first position. */
+ static OUString ConvertToScDefinedName( const OUString& rName );
+
+// *** streams and storages *** -----------------------------------------------
+
+ /** Tries to open an existing storage with the specified name in the passed storage (read-only). */
+ static tools::SvRef<SotStorage> OpenStorageRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName );
+ /** Creates and opens a storage with the specified name in the passed storage (read/write). */
+ static tools::SvRef<SotStorage> OpenStorageWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName );
+
+ /** Tries to open an existing stream with the specified name in the passed storage (read-only). */
+ static tools::SvRef<SotStorageStream> OpenStorageStreamRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName );
+ /** Creates and opens a stream with the specified name in the passed storage (read/write). */
+ static tools::SvRef<SotStorageStream> OpenStorageStreamWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName );
+
+// *** item handling *** ------------------------------------------------------
+
+ /** Returns true, if the passed item set contains the item.
+ @param bDeep true = Searches in parent item sets too. */
+ static bool CheckItem( const SfxItemSet& rItemSet, sal_uInt16 nWhichId, bool bDeep );
+ /** Returns true, if the passed item set contains at least one of the items.
+ @param pnWhichIds Zero-terminated array of Which-IDs.
+ @param bDeep true = Searches in parent item sets too. */
+ static bool CheckItems( const SfxItemSet& rItemSet, const sal_uInt16* pnWhichIds, bool bDeep );
+
+ /** Puts the item into the passed item set.
+ @descr The item will be put into the item set, if bSkipPoolDef is false,
+ or if the item differs from the default pool item.
+ @param rItemSet The destination item set.
+ @param rItem The item to put into the item set.
+ @param nWhichId The Which-ID to set with the item.
+ @param bSkipPoolDef true = Do not put item if it is equal to pool default; false = Always put the item. */
+ static void PutItem(
+ SfxItemSet& rItemSet, const SfxPoolItem& rItem,
+ sal_uInt16 nWhichId, bool bSkipPoolDef );
+
+ /** Puts the item into the passed item set.
+ @descr The item will be put into the item set, if bSkipPoolDef is false,
+ or if the item differs from the default pool item.
+ @param rItemSet The destination item set.
+ @param rItem The item to put into the item set.
+ @param bSkipPoolDef true = Do not put item if it is equal to pool default; false = Always put the item. */
+ static void PutItem( SfxItemSet& rItemSet, const SfxPoolItem& rItem, bool bSkipPoolDef );
+
+// *** style sheet handling *** -----------------------------------------------
+
+ /** Creates and returns a cell style sheet and inserts it into the pool.
+ @descr If the style sheet is already in the pool, another unused style name is used.
+ @param bForceName Controls behaviour, if the style already exists:
+ true = Old existing style will be renamed; false = New style gets another name. */
+ static ScStyleSheet& MakeCellStyleSheet(
+ ScStyleSheetPool& rPool,
+ const OUString& rStyleName, bool bForceName );
+ /** Creates and returns a page style sheet and inserts it into the pool.
+ @descr If the style sheet is already in the pool, another unused style name is used.
+ @param bForceName Controls behaviour, if the style already exists:
+ true = Old existing style will be renamed; false = New style gets another name. */
+ static ScStyleSheet& MakePageStyleSheet(
+ ScStyleSheetPool& rPool,
+ const OUString& rStyleName, bool bForceName );
+
+// *** byte string import operations *** --------------------------------------
+
+ /** Reads and returns a zero terminated byte string and decreases a stream counter. */
+ static OString read_zeroTerminated_uInt8s_ToOString(SvStream& rStrm, sal_Int32& rnBytesLeft);
+ /** Reads and returns a zero terminated byte string and decreases a stream counter. */
+ static OUString read_zeroTerminated_uInt8s_ToOUString(SvStream& rStrm, sal_Int32& rnBytesLeft, rtl_TextEncoding eTextEnc)
+ {
+ return OStringToOUString(read_zeroTerminated_uInt8s_ToOString(rStrm, rnBytesLeft), eTextEnc);
+ }
+
+ /** Appends a zero terminated byte string. */
+ static void AppendCString( SvStream& rStrm, OUString& rString, rtl_TextEncoding eTextEnc );
+
+// *** HTML table names <-> named range names *** -----------------------------
+
+ /** Returns the built-in range name for an HTML document. */
+ static const OUString& GetHTMLDocName();
+ /** Returns the built-in range name for all HTML tables. */
+ static const OUString& GetHTMLTablesName();
+ /** Returns the built-in range name for an HTML table, specified by table index. */
+ static OUString GetNameFromHTMLIndex( sal_uInt32 nIndex );
+ /** Returns the built-in range name for an HTML table, specified by table name. */
+ static OUString GetNameFromHTMLName( std::u16string_view rTabName );
+
+ /** Returns true, if rSource is the built-in range name for an HTML document. */
+ static bool IsHTMLDocName( std::u16string_view rSource );
+ /** Returns true, if rSource is the built-in range name for all HTML tables. */
+ static bool IsHTMLTablesName( std::u16string_view rSource );
+ /** Converts a built-in range name to an HTML table name.
+ @param rSource The string to be determined.
+ @param rName The HTML table name.
+ @return true, if conversion was successful. */
+ static bool GetHTMLNameFromName( const OUString& rSource, OUString& rName );
+
+private:
+ /** Returns the prefix for table index names. */
+ static const OUString& GetHTMLIndexPrefix();
+ /** Returns the prefix for table names. */
+ static const OUString& GetHTMLNamePrefix();
+};
+
+// Containers =================================================================
+
+typedef ::std::vector< sal_uInt8 > ScfUInt8Vec;
+typedef ::std::vector< sal_Int16 > ScfInt16Vec;
+typedef ::std::vector< sal_uInt16 > ScfUInt16Vec;
+typedef ::std::vector< sal_Int32 > ScfInt32Vec;
+typedef ::std::vector< sal_uInt32 > ScfUInt32Vec;
+typedef ::std::vector< OUString > ScfStringVec;
+
+class ScFormatFilterPluginImpl : public ScFormatFilterPlugin
+{
+public:
+ ScFormatFilterPluginImpl();
+ virtual ~ScFormatFilterPluginImpl();
+ // various import filters
+ virtual ErrCode ScImportLotus123( SfxMedium&, ScDocument&, rtl_TextEncoding eSrc ) override;
+ virtual ErrCode ScImportQuattroPro(SvStream* pStream, ScDocument& rDoc) override;
+ virtual ErrCode ScImportExcel( SfxMedium&, ScDocument*, const EXCIMPFORMAT ) override;
+ // eFormat == EIF_AUTO -> matching filter is used automatically
+ // eFormat == EIF_BIFF5 -> only Biff5 stream leads to success (even in an Excel97 doc)
+ // eFormat == EIF_BIFF8 -> only Biff8 stream leads to success (only in Excel97 docs)
+ // eFormat == EIF_BIFF_LE4 -> only non-storage files _could_ lead to success
+ virtual ErrCode ScImportDif( SvStream&, ScDocument*, const ScAddress& rInsPos,
+ const rtl_TextEncoding eSrc ) override;
+ virtual ErrCode ScImportRTF( SvStream&, const OUString& rBaseURL, ScDocument*, ScRange& rRange ) override;
+ virtual ErrCode ScImportHTML( SvStream&, const OUString& rBaseURL, ScDocument*, ScRange& rRange,
+ double nOutputFactor, bool bCalcWidthHeight,
+ SvNumberFormatter* pFormatter, bool bConvertDate, bool bConvertScientific ) override;
+
+ virtual std::unique_ptr<ScEEAbsImport> CreateRTFImport( ScDocument* pDoc, const ScRange& rRange ) override;
+ virtual std::unique_ptr<ScEEAbsImport> CreateHTMLImport( ScDocument* pDocP, const OUString& rBaseURL, const ScRange& rRange ) override;
+ virtual OUString GetHTMLRangeNameList( ScDocument& rDoc, const OUString& rOrigName ) override;
+
+ // various export filters
+ virtual ErrCode ScExportExcel5( SfxMedium&, ScDocument*, ExportFormatExcel eFormat, rtl_TextEncoding eDest ) override;
+ virtual void ScExportDif( SvStream&, ScDocument*, const ScAddress& rOutPos, const rtl_TextEncoding eDest ) override;
+ virtual void ScExportDif( SvStream&, ScDocument*, const ScRange& rRange, const rtl_TextEncoding eDest ) override;
+ virtual void ScExportHTML( SvStream&, const OUString& rBaseURL, ScDocument*, const ScRange& rRange, const rtl_TextEncoding eDest, bool bAll,
+ const OUString& rStreamPath, OUString& rNonConvertibleChars, const OUString& rFilterOptions ) override;
+ virtual void ScExportRTF( SvStream&, ScDocument*, const ScRange& rRange, const rtl_TextEncoding eDest ) override;
+
+ virtual ScOrcusFilters* GetOrcusFilters() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/htmlexp.hxx b/sc/source/filter/inc/htmlexp.hxx
new file mode 100644
index 0000000000..3cb47c0322
--- /dev/null
+++ b/sc/source/filter/inc/htmlexp.hxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/textenc.h>
+#include <tools/gen.hxx>
+#include <tools/color.hxx>
+#include <vcl/vclptr.hxx>
+#include <vector>
+#include <memory>
+#include <map>
+#include <svx/xoutbmp.hxx>
+
+#include "expbase.hxx"
+
+class ScDocument;
+class SfxItemSet;
+class SdrPage;
+class Graphic;
+class SdrObject;
+class OutputDevice;
+class ScDrawLayer;
+class EditTextObject;
+enum class SvtScriptType : sal_uInt8;
+namespace editeng { class SvxBorderLine; }
+
+namespace sc {
+struct ColumnBlockPosition;
+}
+
+struct ScHTMLStyle
+{ // Defaults from stylesheet
+ Color aBackgroundColor;
+ OUString aFontFamilyName;
+ sal_uInt32 nFontHeight; // Item-Value
+ sal_uInt16 nFontSizeNumber; // HTML value 1-7
+ SvtScriptType nDefaultScriptType; // Font values are valid for the default script type
+ bool bInitialized;
+
+ ScHTMLStyle() :
+ nFontHeight(0),
+ nFontSizeNumber(2),
+ nDefaultScriptType(),
+ bInitialized(false)
+ {}
+
+ const ScHTMLStyle& operator=( const ScHTMLStyle& rScHTMLStyle )
+ {
+ aBackgroundColor = rScHTMLStyle.aBackgroundColor;
+ aFontFamilyName = rScHTMLStyle.aFontFamilyName;
+ nFontHeight = rScHTMLStyle.nFontHeight;
+ nFontSizeNumber = rScHTMLStyle.nFontSizeNumber;
+ nDefaultScriptType = rScHTMLStyle.nDefaultScriptType;
+ bInitialized = rScHTMLStyle.bInitialized;
+ return *this;
+ }
+};
+
+struct ScHTMLGraphEntry
+{
+ ScRange aRange; // mapped range
+ Size aSize; // size in pixels
+ Size aSpace; // spacing in pixels
+ SdrObject* pObject;
+ bool bInCell; // if output is in cell
+ bool bWritten;
+
+ ScHTMLGraphEntry( SdrObject* pObj, const ScRange& rRange,
+ const Size& rSize, bool bIn, const Size& rSpace ) :
+ aRange( rRange ),
+ aSize( rSize ),
+ aSpace( rSpace ),
+ pObject( pObj ),
+ bInCell( bIn ),
+ bWritten( false )
+ {}
+};
+
+#define SC_HTML_FONTSIZES 7
+const short nIndentMax = 23;
+
+class ScHTMLExport : public ScExportBase
+{
+ // default HtmlFontSz[1-7]
+ static const sal_uInt16 nDefaultFontSize[SC_HTML_FONTSIZES];
+ // HtmlFontSz[1-7] in s*3.ini [user]
+ static sal_uInt16 nFontSize[SC_HTML_FONTSIZES];
+ static const char* pFontSizeCss[SC_HTML_FONTSIZES];
+ static const sal_uInt16 nCellSpacing;
+ static const char sIndentSource[];
+
+ typedef std::unique_ptr<std::map<OUString, OUString> > FileNameMapPtr;
+ typedef std::vector<ScHTMLGraphEntry> GraphEntryList;
+
+ GraphEntryList aGraphList;
+ ScHTMLStyle aHTMLStyle;
+ OUString aBaseURL;
+ OUString aStreamPath;
+ VclPtr<OutputDevice> pAppWin; // for Pixel-work
+ FileNameMapPtr pFileNameMap; // for CopyLocalFileToINet
+ OUString aNonConvertibleChars; // collect nonconvertible characters
+ SCTAB nUsedTables;
+ short nIndent;
+ char sIndent[nIndentMax+1];
+ bool bAll; // whole document
+ bool bTabHasGraphics;
+ bool bTabAlignedLeft;
+ bool bCalcAsShown;
+ bool bCopyLocalFileToINet;
+ bool bTableDataHeight;
+ bool mbSkipImages;
+ /// If HTML header and footer should be written as well, or just the content itself.
+ bool mbSkipHeaderFooter;
+
+ const SfxItemSet& PageDefaults( SCTAB nTab );
+
+ void WriteBody();
+ void WriteHeader();
+ void WriteOverview();
+ void WriteTables();
+ void WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow, SCTAB nTab );
+ void WriteGraphEntry( ScHTMLGraphEntry* );
+ void WriteImage( OUString& rLinkName,
+ const Graphic&, std::string_view rImgOptions,
+ XOutFlags nXOutFlags = XOutFlags::NONE );
+ // nXOutFlags for XOutBitmap::WriteGraphic
+
+ // write to stream if and only if URL fields in edit cell
+ bool WriteFieldText( const EditTextObject* pData );
+
+ // copy a local file to internet if needed
+ void CopyLocalFileToINet( OUString& rFileNm, std::u16string_view rTargetNm );
+
+ void PrepareGraphics( ScDrawLayer*, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow );
+
+ void FillGraphList( const SdrPage*, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow );
+
+ static OString BorderToStyle(const char* pBorderName,
+ const editeng::SvxBorderLine* pLine,
+ bool& bInsertSemicolon);
+
+ static sal_uInt16 GetFontSizeNumber( sal_uInt16 nHeight );
+ static const char* GetFontSizeCss( sal_uInt16 nHeight );
+ sal_uInt16 ToPixel( sal_uInt16 nTwips );
+ Size MMToPixel( const Size& r100thMMSize );
+ void IncIndent( short nVal );
+
+ const char* GetIndentStr() const
+ {
+ return sIndent;
+ }
+
+public:
+ ScHTMLExport( SvStream&, OUString , ScDocument*, const ScRange&,
+ bool bAll, OUString aStreamPath, std::u16string_view aFilterOptions );
+ virtual ~ScHTMLExport() override;
+ void Write();
+ const OUString& GetNonConvertibleChars() const
+ {
+ return aNonConvertibleChars;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/htmlimp.hxx b/sc/source/filter/inc/htmlimp.hxx
new file mode 100644
index 0000000000..bff4b381cc
--- /dev/null
+++ b/sc/source/filter/inc/htmlimp.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 "eeimport.hxx"
+
+class ScHTMLImport : public ScEEImport
+{
+private:
+ static void InsertRangeName( ScDocument& rDoc, const OUString& rName, const ScRange& rRange );
+
+public:
+ ScHTMLImport( ScDocument* pDoc, const OUString& rBaseURL, const ScRange& rRange, bool bCalcWidthHeight );
+
+ virtual void WriteToDocument( bool bSizeColsRows = false, double nOutputFactor = 1.0,
+ SvNumberFormatter* pFormatter = nullptr, bool bConvertDate = true,
+ bool bConvertScientific = true ) override;
+
+ static OUString GetHTMLRangeNameList( const ScDocument& rDoc, std::u16string_view rOrigName );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/htmlpars.hxx b/sc/source/filter/inc/htmlpars.hxx
new file mode 100644
index 0000000000..fcdf6b4443
--- /dev/null
+++ b/sc/source/filter/inc/htmlpars.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include <optional>
+#include <stack>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#include <o3tl/sorted_vector.hxx>
+
+#include <rangelst.hxx>
+#include "eeparser.hxx"
+
+const sal_uInt32 SC_HTML_FONTSIZES = 7; // like export, HTML options
+
+// Pixel tolerance for SeekOffset and related.
+const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_SMALL = 1; // single table
+const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_LARGE = 10; // nested
+
+// BASE class for HTML parser classes
+
+class ScHTMLTable;
+
+/**
+ * Collection of HTML style data parsed from the content of <style>
+ * elements.
+ */
+class ScHTMLStyles
+{
+ typedef std::unordered_map<OUString, OUString> PropsType;
+ typedef ::std::map<OUString, PropsType> NamePropsType;
+ typedef ::std::map<OUString, NamePropsType> ElemsType;
+
+ NamePropsType m_GlobalProps; /// global properties (for a given class for all elements)
+ NamePropsType m_ElemGlobalProps; /// element global properties (no class specified)
+ ElemsType m_ElemProps; /// element to class to properties (both element and class are given)
+ const OUString maEmpty; /// just a persistent empty string.
+public:
+ ScHTMLStyles();
+
+ void add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
+ const OUString& aProp, const OUString& aValue);
+
+ /**
+ * Find best-matching property value for given element and class names.
+ */
+ const OUString& getPropertyValue(
+ const OUString& rElem, const OUString& rClass, const OUString& rPropName) const;
+
+private:
+ static void insertProp(
+ NamePropsType& rProps, const OUString& aName,
+ const OUString& aProp, const OUString& aValue);
+};
+
+/** Base class for HTML parser classes. */
+class ScHTMLParser : public ScEEParser
+{
+ ScHTMLStyles maStyles;
+protected:
+ sal_uInt32 maFontHeights[ SC_HTML_FONTSIZES ];
+ ScDocument* mpDoc; /// The destination document.
+
+public:
+ explicit ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc );
+ virtual ~ScHTMLParser() override;
+
+ virtual ErrCode Read( SvStream& rStrm, const OUString& rBaseURL ) override = 0;
+
+ ScHTMLStyles& GetStyles() { return maStyles;}
+ ScDocument& GetDoc() { return *mpDoc;}
+
+ /** Returns the "global table" which contains the entire HTML document. */
+ virtual const ScHTMLTable* GetGlobalTable() const = 0;
+};
+
+typedef o3tl::sorted_vector<sal_uLong> ScHTMLColOffset;
+
+struct ScHTMLTableStackEntry
+{
+ ScRangeListRef xLockedList;
+ std::shared_ptr<ScEEParseEntry> xCellEntry;
+ ScHTMLColOffset* pLocalColOffset;
+ sal_uLong nFirstTableCell;
+ SCROW nRowCnt;
+ SCCOL nColCntStart;
+ SCCOL nMaxCol;
+ sal_uInt16 nTable;
+ sal_uInt16 nTableWidth;
+ sal_uInt16 nColOffset;
+ sal_uInt16 nColOffsetStart;
+ bool bFirstRow;
+ ScHTMLTableStackEntry( std::shared_ptr<ScEEParseEntry> xE,
+ ScRangeListRef xL, ScHTMLColOffset* pTO,
+ sal_uLong nFTC,
+ SCROW nRow,
+ SCCOL nStart, SCCOL nMax, sal_uInt16 nTab,
+ sal_uInt16 nTW, sal_uInt16 nCO, sal_uInt16 nCOS,
+ bool bFR )
+ : xLockedList(std::move( xL )), xCellEntry(std::move(xE)),
+ pLocalColOffset( pTO ),
+ nFirstTableCell( nFTC ),
+ nRowCnt( nRow ),
+ nColCntStart( nStart ), nMaxCol( nMax ),
+ nTable( nTab ), nTableWidth( nTW ),
+ nColOffset( nCO ), nColOffsetStart( nCOS ),
+ bFirstRow( bFR )
+ {}
+};
+
+struct ScHTMLAdjustStackEntry
+{
+ SCCOL nLastCol;
+ SCROW nNextRow;
+ SCROW nCurRow;
+ ScHTMLAdjustStackEntry( SCCOL nLCol, SCROW nNRow,
+ SCROW nCRow )
+ : nLastCol( nLCol ), nNextRow( nNRow ),
+ nCurRow( nCRow )
+ {}
+};
+
+class EditEngine;
+class ScDocument;
+class HTMLOption;
+
+// TODO these need better names
+typedef ::std::map<SCROW, SCROW> InnerMap;
+typedef ::std::map<sal_uInt16, InnerMap*> OuterMap;
+
+class ScHTMLLayoutParser : public ScHTMLParser
+{
+private:
+ Size aPageSize;
+ OUString aBaseURL;
+ ::std::stack< std::unique_ptr<ScHTMLTableStackEntry> >
+ aTableStack;
+ OUString aString;
+ ScRangeListRef xLockedList; // per table
+ std::unique_ptr<OuterMap> pTables;
+ ScHTMLColOffset maColOffset;
+ ScHTMLColOffset* pLocalColOffset; // per table
+ sal_uLong nFirstTableCell; // per table
+ short nTableLevel;
+ sal_uInt16 nTable;
+ sal_uInt16 nMaxTable;
+ SCCOL nColCntStart; // first Col per table
+ SCCOL nMaxCol; // per table
+ sal_uInt16 nTableWidth; // per table
+ sal_uInt16 nColOffset; // current, pixel
+ sal_uInt16 nColOffsetStart; // start value per table, in pixel
+ sal_uInt16 nOffsetTolerance; // for use with SeekOffset and related
+ bool bFirstRow; // per table, whether in first row
+ bool bTabInTabCell:1;
+ bool bInCell:1;
+ bool bInTitle:1;
+
+ DECL_LINK( HTMLImportHdl, HtmlImportInfo&, void );
+ void NewActEntry( const ScEEParseEntry* );
+ static void EntryEnd( ScEEParseEntry*, const ESelection& );
+ void ProcToken( HtmlImportInfo* );
+ void CloseEntry( const HtmlImportInfo* );
+ void NextRow( const HtmlImportInfo* );
+ void SkipLocked( ScEEParseEntry*, bool bJoin = true );
+ static bool SeekOffset( const ScHTMLColOffset*, sal_uInt16 nOffset,
+ SCCOL* pCol, sal_uInt16 nOffsetTol );
+ static void MakeCol( ScHTMLColOffset*, sal_uInt16& nOffset,
+ sal_uInt16& nWidth, sal_uInt16 nOffsetTol,
+ sal_uInt16 nWidthTol );
+ static void MakeColNoRef( ScHTMLColOffset*, sal_uInt16 nOffset,
+ sal_uInt16 nWidth, sal_uInt16 nOffsetTol,
+ sal_uInt16 nWidthTol );
+ static void ModifyOffset( ScHTMLColOffset*, sal_uInt16& nOldOffset,
+ sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol );
+ void Colonize( ScEEParseEntry* );
+ sal_uInt16 GetWidth( const ScEEParseEntry* );
+ void SetWidths();
+ void Adjust();
+
+ sal_uInt16 GetWidthPixel( const HTMLOption& );
+ bool IsAtBeginningOfText( const HtmlImportInfo* );
+
+ void TableOn( HtmlImportInfo* );
+ void ColOn( HtmlImportInfo* );
+ void TableRowOn( const HtmlImportInfo* );
+ void TableRowOff( const HtmlImportInfo* );
+ void TableDataOn( HtmlImportInfo* );
+ void TableDataOff( const HtmlImportInfo* );
+ void TableOff( const HtmlImportInfo* );
+ void Image( HtmlImportInfo* );
+ void AnchorOn( HtmlImportInfo* );
+ void FontOn( HtmlImportInfo* );
+
+public:
+ ScHTMLLayoutParser( EditEngine*, OUString aBaseURL, const Size& aPageSize, ScDocument* );
+ virtual ~ScHTMLLayoutParser() override;
+ virtual ErrCode Read( SvStream&, const OUString& rBaseURL ) override;
+ virtual const ScHTMLTable* GetGlobalTable() const override;
+};
+
+// HTML DATA QUERY PARSER
+
+/** Declares the orientation in or for a table: column or row. */
+enum ScHTMLOrient { tdCol = 0 , tdRow = 1 };
+
+/** Type for a unique identifier for each table. */
+typedef sal_uInt16 ScHTMLTableId;
+/** Identifier of the "global table" (the entire HTML document). */
+const ScHTMLTableId SC_HTML_GLOBAL_TABLE = 0;
+/** Used as table index for normal (non-table) entries in ScHTMLEntry structs. */
+const ScHTMLTableId SC_HTML_NO_TABLE = 0;
+
+/** A 2D cell position in an HTML table. */
+struct ScHTMLPos
+{
+ SCCOL mnCol;
+ SCROW mnRow;
+
+ explicit ScHTMLPos() : mnCol( 0 ), mnRow( 0 ) {}
+ explicit ScHTMLPos( SCCOL nCol, SCROW nRow ) :
+ mnCol( nCol ), mnRow( nRow ) {}
+ explicit ScHTMLPos( const ScAddress& rAddr ) { Set( rAddr ); }
+
+ SCCOLROW Get( ScHTMLOrient eOrient ) const
+ { return (eOrient == tdCol) ? mnCol : mnRow; }
+ void Set( SCCOL nCol, SCROW nRow )
+ { mnCol = nCol; mnRow = nRow; }
+ void Set( const ScAddress& rAddr )
+ { Set( rAddr.Col(), rAddr.Row() ); }
+ ScAddress MakeAddr() const
+ { return ScAddress( mnCol, mnRow, 0 ); }
+};
+
+inline bool operator<( const ScHTMLPos& rPos1, const ScHTMLPos& rPos2 )
+{
+ return (rPos1.mnRow < rPos2.mnRow) || ((rPos1.mnRow == rPos2.mnRow) && (rPos1.mnCol < rPos2.mnCol));
+}
+
+/** A 2D cell size in an HTML table. */
+struct ScHTMLSize
+{
+ SCCOL mnCols;
+ SCROW mnRows;
+
+ explicit ScHTMLSize( SCCOL nCols, SCROW nRows ) :
+ mnCols( nCols ), mnRows( nRows ) {}
+ void Set( SCCOL nCols, SCROW nRows )
+ { mnCols = nCols; mnRows = nRows; }
+};
+
+/** A single entry containing a line of text or representing a table. */
+struct ScHTMLEntry : public ScEEParseEntry
+{
+public:
+ explicit ScHTMLEntry(
+ const SfxItemSet& rItemSet,
+ ScHTMLTableId nTableId = SC_HTML_NO_TABLE );
+
+ /** Returns true, if the selection of the entry is empty. */
+ bool IsEmpty() const { return !aSel.HasRange(); }
+ /** Returns true, if the entry has any content to be imported. */
+ bool HasContents() const;
+ /** Returns true, if the entry represents a table. */
+ bool IsTable() const { return nTab != SC_HTML_NO_TABLE; }
+ /** Returns true, if the entry represents a table. */
+ ScHTMLTableId GetTableId() const { return nTab; }
+
+ /** Sets or clears the import always state. */
+ void SetImportAlways() { mbImportAlways = true; }
+ /** Sets start point of the entry selection to the start of the import info object. */
+ void AdjustStart( const HtmlImportInfo& rInfo );
+ /** Sets end point of the entry selection to the end of the import info object. */
+ void AdjustEnd( const HtmlImportInfo& rInfo );
+ /** Deletes leading and trailing empty paragraphs from the entry. */
+ void Strip( const EditEngine& rEditEngine );
+
+ /** Returns read/write access to the item set of this entry. */
+ SfxItemSet& GetItemSet() { return aItemSet; }
+ /** Returns read-only access to the item set of this entry. */
+ const SfxItemSet& GetItemSet() const { return aItemSet; }
+
+private:
+ bool mbImportAlways; /// true = Always import this entry.
+};
+
+/** This struct handles creation of unique table identifiers. */
+struct ScHTMLTableAutoId
+{
+ const ScHTMLTableId mnTableId; /// The created unique table identifier.
+ ScHTMLTableId& mrnUnusedId; /// Reference to global unused identifier variable.
+
+ /** The constructor assigns an unused identifier to member mnTableId. */
+ explicit ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId );
+};
+
+class ScHTMLTableMap;
+
+/** Stores data for one table in an HTML document.
+
+ This class does the main work for importing an HTML document. It manages
+ the correct insertion of parse entries into the correct cells and the
+ creation of nested tables. Recalculation of resulting document size and
+ position is done recursively in all nested tables.
+ */
+class ScHTMLTable
+{
+public:
+ /** Creates a new HTML table without content.
+ @descr Internally handles a current cell position. This position is
+ invalid until first calls of RowOn() and DataOn().
+ @param rParentTable Reference to the parent table that owns this table.
+ @param bPreFormText true = Table is based on preformatted text (<pre> tag). */
+ explicit ScHTMLTable(
+ ScHTMLTable& rParentTable,
+ const HtmlImportInfo& rInfo,
+ bool bPreFormText,
+ const ScDocument& rDoc );
+
+ virtual ~ScHTMLTable();
+
+ /** Returns the name of the table, specified in the TABLE tag. */
+ const OUString& GetTableName() const { return maTableName; }
+ /** Returns the caption of the table, specified in the <caption> tag. */
+ const OUString& GetTableCaption() const { return maCaption; }
+ /** Returns the unique identifier of the table. */
+ ScHTMLTableId GetTableId() const { return maTableId.mnTableId; }
+ /** Returns the cell spanning of the specified cell. */
+ ScHTMLSize GetSpan( const ScHTMLPos& rCellPos ) const;
+
+ /** Searches in all nested tables for the specified table.
+ @param nTableId Unique identifier of the table. */
+ ScHTMLTable* FindNestedTable( ScHTMLTableId nTableId ) const;
+
+ /** Puts the item into the item set of the current entry. */
+ void PutItem( const SfxPoolItem& rItem );
+ /** Inserts a text portion into current entry. */
+ void PutText( const HtmlImportInfo& rInfo );
+ /** Inserts a new line, if in preformatted text, else does nothing. */
+ void InsertPara( const HtmlImportInfo& rInfo );
+
+ /** Inserts a line break (<br> tag).
+ @descr Inserts the current entry regardless if it is empty. */
+ void BreakOn();
+ /** Inserts a heading line (<p> and <h*> tags). */
+ void HeadingOn();
+ /** Processes a hyperlink (<a> tag). */
+ void AnchorOn();
+
+ /** Starts a *new* table nested in this table (<table> tag).
+ @return Pointer to the new table. */
+ ScHTMLTable* TableOn( const HtmlImportInfo& rInfo );
+ /** Closes *this* table (</table> tag).
+ @return Pointer to the parent table. */
+ ScHTMLTable* TableOff( const HtmlImportInfo& rInfo );
+ /** Processes the caption of the table (<caption> tag). */
+ void CaptionOn();
+ /** Processes the caption of the table (</caption> tag). */
+ void CaptionOff();
+ /** Starts a *new* table based on preformatted text (<pre> tag).
+ @return Pointer to the new table. */
+ ScHTMLTable* PreOn( const HtmlImportInfo& rInfo );
+ /** Closes *this* table based on preformatted text (</pre> tag).
+ @return Pointer to the parent table. */
+ ScHTMLTable* PreOff( const HtmlImportInfo& rInfo );
+
+ /** Starts next row (<tr> tag).
+ @descr Cell address is invalid until first call of DataOn(). */
+ void RowOn( const HtmlImportInfo& rInfo );
+ /** Closes the current row (<tr> tag).
+ @descr Cell address is invalid until call of RowOn() and DataOn(). */
+ void RowOff( const HtmlImportInfo& rInfo );
+ /** Starts the next cell (<td> or <th> tag). */
+ void DataOn( const HtmlImportInfo& rInfo );
+ /** Closes the current cell (</td> or </th> tag).
+ @descr Cell address is invalid until next call of DataOn(). */
+ void DataOff( const HtmlImportInfo& rInfo );
+
+ /** Starts the body of the HTML document (<body> tag). */
+ void BodyOn( const HtmlImportInfo& rInfo );
+ /** Closes the body of the HTML document (</body> tag). */
+ void BodyOff( const HtmlImportInfo& rInfo );
+
+ /** Closes *this* table (</table> tag) or preformatted text (</pre> tag).
+ @descr Used to close this table object regardless on opening tag type.
+ @return Pointer to the parent table, or this, if no parent found. */
+ ScHTMLTable* CloseTable( const HtmlImportInfo& rInfo );
+
+ /** Returns the resulting document row/column count of the specified HTML row/column. */
+ SCCOLROW GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const;
+ /** Returns the resulting document row/column count in the half-open range [nCellBegin, nCellEnd). */
+ SCCOLROW GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const;
+ /** Returns the total document row/column count in the specified direction. */
+ SCCOLROW GetDocSize( ScHTMLOrient eOrient ) const;
+ /** Returns the total document row/column count of the specified HTML cell. */
+ ScHTMLSize GetDocSize( const ScHTMLPos& rCellPos ) const;
+
+ /** Returns the resulting Calc position of the top left edge of the table. */
+ const ScHTMLPos& GetDocPos() const { return maDocBasePos; }
+ /** Calculates the resulting Calc position of the specified HTML column/row. */
+ SCCOLROW GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const;
+ /** Calculates the resulting Calc position of the specified HTML cell. */
+ ScHTMLPos GetDocPos( const ScHTMLPos& rCellPos ) const;
+
+ /** Calculates the current Calc document area of this table. */
+ void GetDocRange( ScRange& rRange ) const;
+
+ /** Applies border formatting to the passed document. */
+ void ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const;
+
+ SvNumberFormatter* GetFormatTable();
+
+protected:
+ /** Creates a new HTML table without parent.
+ @descr This constructor is used to create the "global table". */
+ explicit ScHTMLTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
+ ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser,
+ const ScDocument& rDoc );
+
+ /** Fills all empty cells in this and nested tables with dummy parse entries. */
+ void FillEmptyCells();
+ /** Recalculates the size of all columns/rows in the table, regarding nested tables. */
+ void RecalcDocSize();
+ /** Recalculates the position of all cell entries and nested tables.
+ @param rBasePos The origin of the table in the Calc document. */
+ void RecalcDocPos( const ScHTMLPos& rBasePos );
+
+private:
+ typedef ::std::unique_ptr< ScHTMLTableMap > ScHTMLTableMapPtr;
+ typedef ::std::vector< SCCOLROW > ScSizeVec;
+ typedef ::std::vector< ScHTMLEntry* > ScHTMLEntryVector;
+ typedef ::std::unique_ptr< ScHTMLEntry > ScHTMLEntryPtr;
+
+ /** Returns true, if the current cell does not contain an entry yet. */
+ bool IsEmptyCell() const;
+ /** Returns the item set from cell, row, or table, depending on current state. */
+ const SfxItemSet& GetCurrItemSet() const;
+
+ /** Returns true, if import info represents a space character. */
+ static bool IsSpaceCharInfo( const HtmlImportInfo& rInfo );
+
+ /** Creates and returns a new empty flying entry at position (0,0). */
+ ScHTMLEntryPtr CreateEntry() const;
+ /** Creates a new flying entry.
+ @param rInfo Contains the initial edit engine selection for the entry. */
+ void CreateNewEntry( const HtmlImportInfo& rInfo );
+
+ /** Inserts an empty line in front of the next entry. */
+ void InsertLeadingEmptyLine();
+
+ /** Pushes the passed entry into the list of the current cell. */
+ void ImplPushEntryToVector( ScHTMLEntryVector& rEntryVector, ScHTMLEntryPtr& rxEntry );
+ /** Tries to insert the entry into the current cell.
+ @descr If insertion is not possible (i.e., currently no cell open), the
+ entry will be inserted into the parent table.
+ @return true = Entry has been pushed into the current cell; false = Entry dropped. */
+ bool PushEntry( ScHTMLEntryPtr& rxEntry );
+ /** Puts the current entry into the entry list, if it is not empty.
+ @param rInfo The import info struct containing the end position of the current entry.
+ @param bLastInCell true = If cell is still empty, put this entry always.
+ @return true = Entry as been pushed into the current cell; false = Entry dropped. */
+ bool PushEntry( const HtmlImportInfo& rInfo, bool bLastInCell = false );
+ /** Pushes a new entry into current cell which references a nested table.*/
+ void PushTableEntry( ScHTMLTableId nTableId );
+
+ /** Tries to find a table from the table container.
+ @descr Assumes that the table is located in the current container or
+ that the passed table identifier is 0.
+ @param nTableId Unique identifier of the table or 0. */
+ ScHTMLTable* GetExistingTable( ScHTMLTableId nTableId ) const;
+ /** Inserts a nested table in the current cell at the specified position.
+ @param bPreFormText true = New table is based on preformatted text (<pre> tag). */
+ ScHTMLTable* InsertNestedTable( const HtmlImportInfo& rInfo, bool bPreFormText );
+
+ /** Inserts a new cell in an unused position, starting from current cell position. */
+ void InsertNewCell( const ScHTMLSize& rSpanSize );
+
+ /** Set internal states for a new table row. */
+ void ImplRowOn();
+ /** Set internal states for leaving a table row. */
+ void ImplRowOff();
+ /** Set internal states for entering a new table cell. */
+ void ImplDataOn( const ScHTMLSize& rSpanSize );
+ /** Set internal states for leaving a table cell. */
+ void ImplDataOff();
+
+ /** Inserts additional formatting options from import info into the item set. */
+ static void ProcessFormatOptions( SfxItemSet& rItemSet, const HtmlImportInfo& rInfo );
+
+ /** Updates the document column/row size of the specified column or row.
+ @descr Only increases the present count, never decreases. */
+ void SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize );
+ /** Calculates and sets the resulting size the cell needs in the document.
+ @descr Reduces the needed size in merged cells.
+ @param nCellPos The first column/row position of the (merged) cell.
+ @param nCellSpan The cell spanning in the specified orientation.
+ @param nRealDocSize The raw document size of all entries of the cell. */
+ void CalcNeededDocSize(
+ ScHTMLOrient eOrient, SCCOLROW nCellPos,
+ SCCOLROW nCellSpan, SCCOLROW nRealDocSize );
+
+private:
+ ScHTMLTable* mpParentTable; /// Pointer to parent table.
+ ScHTMLTableMapPtr mxNestedTables; /// Table of nested HTML tables.
+ OUString maTableName; /// Table name from <table id> option.
+ OUString maCaption; /// Caption name of the table from <caption> </caption>
+ OUStringBuffer maCaptionBuffer; /// Caption buffer of the table from <caption> </caption>
+ ScHTMLTableAutoId maTableId; /// Unique identifier of this table.
+ SfxItemSet maTableItemSet; /// Items for the entire table.
+ std::optional<SfxItemSet> moRowItemSet; /// Items for the current table row.
+ std::optional<SfxItemSet> moDataItemSet; /// Items for the current cell.
+ ScRangeList maHMergedCells; /// List of all horizontally merged cells.
+ ScRangeList maVMergedCells; /// List of all vertically merged cells.
+ ScRangeList maUsedCells; /// List of all used cells.
+ EditEngine& mrEditEngine; /// Edit engine (from ScEEParser).
+ std::vector<std::shared_ptr<ScEEParseEntry>>& mrEEParseList; /// List that owns the parse entries (from ScEEParser).
+ std::map< ScHTMLPos, ScHTMLEntryVector > maEntryMap; /// List of entries for each cell.
+ ScHTMLEntryVector* mpCurrEntryVector; /// Current entry vector from map for faster access.
+ ScHTMLEntryPtr mxCurrEntry; /// Working entry, not yet inserted in a list.
+ ScSizeVec maCumSizes[ 2 ]; /// Cumulated cell counts for each HTML table column/row.
+ ScHTMLSize maSize; /// Size of the table.
+ ScHTMLPos maCurrCell; /// Address of current cell to fill.
+ ScHTMLPos maDocBasePos; /// Resulting base address in a Calc document.
+ ScHTMLParser* mpParser;
+ const ScDocument& mrDoc;
+ bool mbBorderOn:1; /// true = Table borders on.
+ bool mbPreFormText:1; /// true = Table from preformatted text (<pre> tag).
+ bool mbRowOn:1; /// true = Inside of <tr> </tr>.
+ bool mbDataOn:1; /// true = Inside of <td> </td> or <th> </th>.
+ bool mbPushEmptyLine:1; /// true = Insert empty line before current entry.
+ bool mbCaptionOn:1; /// true = Inside of <caption> </caption>
+};
+
+/** The "global table" representing the entire HTML document. */
+class ScHTMLGlobalTable : public ScHTMLTable
+{
+public:
+ explicit ScHTMLGlobalTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
+ ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser,
+ const ScDocument& rDoc );
+
+ virtual ~ScHTMLGlobalTable() override;
+
+ /** Recalculates sizes and resulting positions of all document entries. */
+ void Recalc();
+};
+
+/** The HTML parser for data queries. Focuses on data import, not on layout.
+
+ Builds the table structure correctly, ignores extended formatting like
+ pictures or column widths.
+ */
+class ScHTMLQueryParser : public ScHTMLParser
+{
+public:
+ explicit ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc );
+ virtual ~ScHTMLQueryParser() override;
+
+ virtual ErrCode Read( SvStream& rStrm, const OUString& rBaseURL ) override;
+
+ /** Returns the "global table" which contains the entire HTML document. */
+ virtual const ScHTMLTable* GetGlobalTable() const override;
+
+private:
+ /** Handles all possible tags in the HTML document. */
+ void ProcessToken( const HtmlImportInfo& rInfo );
+ /** Inserts a text portion into current entry. */
+ void InsertText( const HtmlImportInfo& rInfo );
+ /** Processes the <font> tag. */
+ void FontOn( const HtmlImportInfo& rInfo );
+
+ /** Processes the <meta> tag. */
+ void MetaOn( const HtmlImportInfo& rInfo );
+ /** Opens the title of the HTML document (<title> tag). */
+ void TitleOn();
+ /** Closes the title of the HTML document (</title> tag). */
+ void TitleOff( const HtmlImportInfo& rInfo );
+
+ /** Opens a new table at the current position. */
+ void TableOn( const HtmlImportInfo& rInfo );
+ /** Closes the current table. */
+ void TableOff( const HtmlImportInfo& rInfo );
+ /** Opens a new table based on preformatted text. */
+ void PreOn( const HtmlImportInfo& rInfo );
+ /** Closes the current preformatted text table. */
+ void PreOff( const HtmlImportInfo& rInfo );
+
+ /** Closes the current table, regardless on opening tag. */
+ void CloseTable( const HtmlImportInfo& rInfo );
+
+ void ParseStyle(std::u16string_view rStrm);
+
+ DECL_LINK( HTMLImportHdl, HtmlImportInfo&, void );
+
+private:
+ typedef ::std::unique_ptr< ScHTMLGlobalTable > ScHTMLGlobalTablePtr;
+
+ OUStringBuffer maTitle; /// The title of the document.
+ ScHTMLGlobalTablePtr mxGlobTable; /// Contains the entire imported document.
+ ScHTMLTable* mpCurrTable; /// Pointer to current table (performance).
+ ScHTMLTableId mnUnusedId; /// First unused table identifier.
+ bool mbTitleOn; /// true = Inside of <title> </title>.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/imp_op.hxx b/sc/source/filter/inc/imp_op.hxx
new file mode 100644
index 0000000000..398ae11827
--- /dev/null
+++ b/sc/source/filter/inc/imp_op.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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include "xiroot.hxx"
+#include "xistream.hxx"
+#include "ftools.hxx"
+
+#include <vector>
+#include <memory>
+#include <unordered_map>
+
+class SvStream;
+
+class ScFormulaCell;
+class ScDocument;
+class ScTokenArray;
+
+class ExcelToSc;
+class XclImpOutlineBuffer;
+class XclImpColRowSettings;
+struct XclAddress;
+
+class ImportTyp
+{
+protected:
+ rtl_TextEncoding eQuellChar; // source (Quelle) character set
+ ScDocument& rD; // document
+
+public:
+ ImportTyp( ScDocument&, rtl_TextEncoding eSrc );
+ virtual ~ImportTyp();
+};
+
+class XclImpOutlineDataBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpOutlineDataBuffer( const XclImpRoot& rRoot, SCTAB nScTab );
+ virtual ~XclImpOutlineDataBuffer() override;
+
+ XclImpColRowSettings* GetColRowBuff() const { return mxColRowBuff.get(); }
+ XclImpOutlineBuffer* GetColOutline() const { return mxColOutlineBuff.get(); }
+ XclImpOutlineBuffer* GetRowOutline() const { return mxRowOutlineBuff.get(); }
+ void Convert();
+
+private:
+ typedef std::shared_ptr< XclImpOutlineBuffer > XclImpOutlineBfrRef;
+ typedef std::shared_ptr< XclImpColRowSettings > XclImpColRowSettRef;
+
+ XclImpOutlineBfrRef mxColOutlineBuff;
+ XclImpOutlineBfrRef mxRowOutlineBuff;
+ XclImpColRowSettRef mxColRowBuff;
+ SCTAB mnScTab;
+};
+
+class ImportExcel : public ImportTyp, protected XclImpRoot
+{
+protected:
+ struct LastFormula
+ {
+ sal_uInt16 mnXF;
+ SCCOL mnCol;
+ SCROW mnRow;
+ double mfValue;
+ ScFormulaCell* mpCell;
+ };
+ typedef std::unordered_map<SCCOL, LastFormula> LastFormulaMapType;
+
+ RootData* pExcRoot;
+
+ XclImpStream maStrm; // input stream
+ XclImpStream& aIn; // input stream
+
+ ScfUInt32Vec maSheetOffsets;
+ ScRange maScOleSize; /// Visible range if embedded.
+
+ std::unique_ptr<ExcelToSc> pFormConv; // formula-converter
+
+ XclImpOutlineBuffer* pColOutlineBuff;
+ XclImpOutlineBuffer* pRowOutlineBuff;
+ XclImpColRowSettings* pColRowBuff; // Col/Row settings 1 table
+
+ typedef std::vector< std::unique_ptr<XclImpOutlineDataBuffer> > XclImpOutlineListBuffer;
+ std::unique_ptr<XclImpOutlineListBuffer> pOutlineListBuffer;
+
+ LastFormulaMapType maLastFormulaCells; // Keep track of last formula cells in each column.
+ LastFormula* mpLastFormula;
+
+ sal_Int16 mnLastRefIdx;
+ sal_uInt16 mnIxfeIndex; /// Current XF identifier from IXFE record.
+ sal_uInt16 mnLastRecId;
+
+ SCTAB nBdshtTab; // Counter for Boundsheet
+
+ bool bTabTruncated; // if extended range leads to
+ // truncation of cells
+
+ bool mbBiff2HasXfs:1; /// Select XF formatting or direct formatting in BIFF2.
+ bool mbBiff2HasXfsValid:1; /// False = mbBiff2HasXfs is undetermined yet.
+ bool mbFuzzing:1; /// True if fuzzing filter
+
+ void SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell );
+
+ // Record functions
+ void ReadFileSharing();
+
+ sal_uInt16 ReadXFIndex( const ScAddress& rScPos, bool bBiff2 );
+
+ void ReadDimensions();
+ void ReadBlank();
+ void ReadInteger();
+ void ReadNumber();
+ void ReadLabel();
+ void ReadBoolErr();
+ void ReadRk();
+
+ void Window1();
+ void Formula25(); // 0x06 -> excform.cxx
+ void Row25(); // 0x08
+ void Bof2(); // 0x09
+ void Eof(); // 0x0A
+ void DocProtect(); // 0x12
+ void SheetProtect(); // 0x12 Sheet Protection
+ void DocPassword(); // 0x13 document password
+ void SheetPassword(); // 0x13 sheet password
+ void Externsheet(); // 0x17
+ void WinProtection(); // 0x19
+ void Columndefault(); // 0x20
+ void Array25(); // 0x21
+ void Rec1904(); // 0x22
+ void Externname25(); // 0x23
+ void Colwidth(); // 0x24
+ void Defrowheight2(); // 0x25
+// void Window1(); // 0x3D
+ void Codepage(); // 0x42
+ void Ixfe(); // 0x44
+ void DefColWidth(); // 0x55
+ void Colinfo(); // 0x7D
+ void Wsbool(); // 0x81
+ void Boundsheet(); // 0x85
+ void Country(); // 0x8C
+ void Hideobj(); // 0x8D
+ void Standardwidth(); // 0x99
+ void Shrfmla(); // 0xBC
+ void Mulrk(); // 0xBD
+ void Mulblank(); // 0xBE
+ void Rstring(); // 0xD6
+ void Cellmerging(); // 0xE5
+ void Olesize(); // 0xDE
+ void ReadUsesElfs(); // 0x0160
+ void Formula3(); // 0x0206 -> excform.cxx
+ // 0x0207 -> 0x07
+ void Row34(); // 0x0208
+ void Bof3(); // 0x0209
+ void Array34(); // 0x0221
+ void Defrowheight345(); // 0x0225
+ void TableOp(); // 0x0236
+ //void Rk(); // 0x027E -> 0x7E
+ void Formula4(); // 0x0406 -> excform.cxx
+ void Bof4(); // 0x0409
+ void Bof5(); // 0x0809
+
+ void Formula(
+ const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla);
+ // -> excform.cxx
+
+ virtual void EndSheet();
+ void NewTable();
+ std::unique_ptr<ScTokenArray> ErrorToFormula( bool bErrOrVal, sal_uInt8 nError,
+ double& rVal );
+
+ void AdjustRowHeight();
+ virtual void PostDocLoad();
+
+public:
+ ImportExcel( XclImpRootData& rImpData, SvStream& rStrm );
+
+ virtual ~ImportExcel() override;
+
+ virtual ErrCode Read();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotattr.hxx b/sc/source/filter/inc/lotattr.hxx
new file mode 100644
index 0000000000..475d46547e
--- /dev/null
+++ b/sc/source/filter/inc/lotattr.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+
+#include <address.hxx>
+
+class ScDocumentPool;
+class ScPatternAttr;
+class SvxColorItem;
+class Color;
+struct LotusContext;
+
+namespace editeng { class SvxBorderLine; }
+
+struct LotAttrWK3
+{
+ sal_uInt8 nFont;
+ sal_uInt8 nLineStyle;
+ sal_uInt8 nFontCol;
+ sal_uInt8 nBack;
+
+ bool HasStyles () const
+ {
+ return ( nFont || nLineStyle || nFontCol || ( nBack & 0x7F ) );
+ // !! without center bit!!
+ }
+
+ bool IsCentered () const
+ {
+ return ( nBack & 0x80 );
+ }
+};
+
+class LotAttrCache
+{
+public:
+
+ LotAttrCache(LotusContext& rContext);
+
+ ~LotAttrCache();
+
+ const ScPatternAttr& GetPattAttr( const LotAttrWK3& );
+
+private:
+
+ friend class LotAttrTable;
+
+ struct ENTRY
+ {
+ std::unique_ptr<ScPatternAttr> pPattAttr;
+ sal_uInt32 nHash0;
+
+ ENTRY(std::unique_ptr<ScPatternAttr> p);
+
+ ~ENTRY();
+ };
+
+ static void MakeHash( const LotAttrWK3& rAttr, sal_uInt32& rOut )
+ {
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 0 ] = rAttr.nFont & 0x7F;
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 1 ] = rAttr.nLineStyle;
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 2 ] = rAttr.nFontCol;
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 3 ] = rAttr.nBack;
+ }
+
+ static void LotusToScBorderLine( sal_uInt8 nLine, ::editeng::SvxBorderLine& );
+
+ const SvxColorItem& GetColorItem( const sal_uInt8 nLotIndex ) const;
+
+ const Color& GetColor( const sal_uInt8 nLotIndex ) const;
+
+ ScDocumentPool* pDocPool;
+ std::unique_ptr<SvxColorItem> ppColorItems[6]; // 0 and 7 are missing!
+ std::unique_ptr<SvxColorItem> pWhite;
+ std::unique_ptr<Color[]> pColTab;
+ std::vector< std::unique_ptr<ENTRY> > aEntries;
+
+ LotusContext& mrContext;
+};
+
+class LotAttrCol
+{
+public:
+ void SetAttr (const ScDocument* pDoc, const SCROW nRow, const ScPatternAttr&);
+
+ void Apply(LotusContext& rContext, const SCCOL nCol, const SCTAB nTab);
+private:
+
+ struct ENTRY
+ {
+ const ScPatternAttr* pPattAttr;
+ SCROW nFirstRow;
+ SCROW nLastRow;
+ };
+
+ std::vector< std::unique_ptr<ENTRY> > aEntries;
+};
+
+class LotAttrTable
+{
+public:
+ LotAttrTable(LotusContext& rContext);
+
+ void SetAttr(const LotusContext& rContext, const SCCOL nColFirst, const SCCOL nColLast, const SCROW nRow, const LotAttrWK3& );
+
+ void Apply(LotusContext& rContext, const SCTAB nTabNum);
+
+private:
+
+ LotAttrCol pCols[ MAXCOLCOUNT ];
+ LotAttrCache aAttrCache;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotfntbf.hxx b/sc/source/filter/inc/lotfntbf.hxx
new file mode 100644
index 0000000000..99d1c9a9d8
--- /dev/null
+++ b/sc/source/filter/inc/lotfntbf.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 .
+ */
+
+#pragma once
+
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <optional>
+
+// Code in fontbuff.cxx (excel)
+
+class LotusFontBuffer
+{
+private:
+ struct ENTRY
+ {
+ std::optional<OUString> xTmpName;
+ std::unique_ptr<SvxFontItem> pFont;
+ std::unique_ptr<SvxFontHeightItem> pHeight;
+ sal_Int32 nType = -1; // < 0 -> undefined
+ void TmpName( const OUString &rNew )
+ {
+ xTmpName = rNew;
+ }
+ void Height( std::unique_ptr<SvxFontHeightItem> pNew )
+ {
+ pHeight = std::move(pNew);
+ }
+ void Type( const sal_uInt16 nNew ) { nType = nNew; }
+ };
+
+ static void MakeFont( ENTRY* pEntry );
+public:
+ const static sal_uInt16 nSize = 8;
+ void Fill( const sal_uInt8 nIndex, SfxItemSet& rItemSet );
+ void SetName( const sal_uInt16 nIndex, const OUString& rName );
+ void SetHeight( const sal_uInt16 nIndex, const sal_uInt16 nHeight );
+ void SetType( const sal_uInt16 nIndex, const sal_uInt16 nType );
+private:
+ ENTRY pData[ nSize ];
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotform.hxx b/sc/source/filter/inc/lotform.hxx
new file mode 100644
index 0000000000..29751a2f55
--- /dev/null
+++ b/sc/source/filter/inc/lotform.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "formel.hxx"
+
+enum FUNC_TYPE
+{
+ FT_Return = 0, // End Formula
+ FT_FuncFix0, // Function, 0 Parameter
+ FT_FuncFix1, // Function, 0 Parameter
+ FT_FuncFix2, // Function, 0 Parameter
+ FT_FuncFix3, // Function, 0 Parameter
+ FT_FuncFix4, // Function, 0 Parameter
+ FT_FuncVar, // ~, var. P.
+ FT_Neg, // Negation
+ FT_Op, // Operator
+ FT_NotImpl, // not implemented
+ FT_ConstFloat, // Double (8-Byte)
+ FT_Variable, // Single Ref
+ FT_Range, // Double Ref
+ FT_Braces, // Braces
+ FT_ConstInt, // Integer
+ FT_ConstString, // String
+ FT_NOP, // nothing
+ // additionally since WK3
+ FT_Cref, // Cell Reference
+ FT_Rref, // Range Reference
+ FT_Nrref, // Named range reference
+ FT_Absnref, // Absolute named range
+ FT_Erref, // Err range reference
+ FT_Ecref, // Err cell reference
+ FT_Econstant, // Err constant
+ FT_Splfunc, // SPLfunction
+ FT_Const10Float,// Float (10-Byte)
+ FT_Snum // Const Short Num
+ // for 'Problem Cases' during Import
+};
+
+struct LotusContext;
+
+class LotusToSc : public LotusConverterBase
+{
+private:
+ LotusContext& m_rContext;
+ rtl_TextEncoding eSrcChar;
+ TokenId nAddToken; // ')+1.0'
+ TokenId nSubToken; // ~
+ TokenId n0Token; // '0.0';
+
+ static FUNC_TYPE IndexToType( sal_uInt8 );
+ static DefTokenId IndexToToken( sal_uInt8 );
+ static FUNC_TYPE IndexToTypeWK123( sal_uInt8 );
+ static DefTokenId IndexToTokenWK123( sal_uInt8 );
+ void DoFunc( DefTokenId eOc, sal_uInt8 nCnt, const char* pExtName );
+ void LotusRelToScRel(sal_uInt16 nCol, sal_uInt16 nRow, ScSingleRefData& rSRD);
+ bool bWK3; // alternative Code translation for < WK1
+ bool bWK123; // alternative for 123
+
+ void ReadSRD( const ScDocument& rDoc, ScSingleRefData& rSRD, sal_uInt8 nFlags );
+ inline void ReadCRD( const ScDocument& rDoc, ScComplexRefData& rCRD, sal_uInt8 nFlags );
+ void IncToken( TokenId &rParam );
+ // Attention: here the Token-chain is extended in Pool
+ // with '(<rParam>)+1' and finished with Store() !
+
+ void DecToken( TokenId& rParam );
+ // Attention: ~
+ void NegToken( TokenId& rParam );
+ // Attention: like ~, but with '-(<rParam>)'
+public:
+ LotusToSc(LotusContext &rContext, SvStream& aStr, svl::SharedStringPool& rSPool, rtl_TextEncoding eSrc, bool b);
+
+ virtual void Convert( std::unique_ptr<ScTokenArray>& rpErg, sal_Int32& nRest ) override;
+
+ void Reset( const ScAddress& rEingPos );
+ inline void SetWK3();
+ LotusContext& getContext() { return m_rContext; }
+
+private:
+ using LotusConverterBase::Reset;
+};
+
+inline void LotusToSc::ReadCRD( const ScDocument& rDoc, ScComplexRefData& rCRD, sal_uInt8 nRelBit )
+{
+ // 1st part
+ ReadSRD( rDoc, rCRD.Ref1, nRelBit );
+
+ // 2nd part
+ ReadSRD( rDoc, rCRD.Ref2, nRelBit >> 3 );
+}
+
+inline void LotusToSc::SetWK3()
+{
+ bWK3 = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotimpop.hxx b/sc/source/filter/inc/lotimpop.hxx
new file mode 100644
index 0000000000..916cbda11a
--- /dev/null
+++ b/sc/source/filter/inc/lotimpop.hxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+#include "imp_op.hxx"
+#include "ftools.hxx"
+#include "lotform.hxx"
+#include "lotattr.hxx"
+
+struct LotusContext;
+
+class ImportLotus : public ImportTyp
+{
+private:
+ SvStream* pIn; // needed due to multiple Read()!
+ LotusToSc aConv;
+ sal_uInt16 nTab; // currently handled table
+ sal_Int32 nExtTab;
+
+ // in WK?-Datei
+ void Bof(); // 0x0000 00
+ bool BofFm3(); // 0x0000 00
+ void Columnwidth( sal_uInt16 nRecLen ); // 0x0007 07
+ void Hiddencolumn( sal_uInt16 nRecLen ); // 0x0008 08
+ void Userrange(); // 0x0009 09
+ void Errcell(); // 0x0014 20
+ void Nacell(); // 0x0015 21
+ void Labelcell(); // 0x0016 22
+ void Numbercell(); // 0x0017 23
+ void Smallnumcell(); // 0x0018 24
+ void Formulacell( sal_uInt16 nRecLen ); // 0x0019 25
+ // 0x001b 27 special
+ void NamedSheet(); // 14000
+ void RowPresentation( sal_uInt16 nRecLen ); // 2007
+
+ // in FM? file
+ void Font_Face(); // 174
+ void Font_Type(); // 176
+ void Font_Ysize(); // 177
+ void Row_( const sal_uInt16 nRecLen ); // 197 ?
+
+ inline void Read( ScAddress& );
+ inline void Read( ScRange& );
+ // for addresses/ranges in the format row(16)/tab(8)/col(8)
+ inline void Read( char& );
+ inline void Read( sal_uInt8& );
+ inline void Read( sal_uInt16& );
+ inline void Read( sal_Int16& );
+ inline void Read( double& ); // read 10-byte IEEE
+ inline void Read( LotAttrWK3& );
+ void Read( OUString& ); // read in 0-terminated string
+ inline void Skip( const sal_uInt16 nNumBytes );
+
+public:
+ ImportLotus(LotusContext& rContext, SvStream&, rtl_TextEncoding eSrc);
+
+ virtual ~ImportLotus() override;
+
+ ErrCode parse(); //parse + CalcAfterLoad
+ ErrCode Read();
+ ErrCode Read( SvStream& ); // special for *.fm3 files
+};
+
+inline void ImportLotus::Read( ScAddress& rAddr )
+{
+ sal_uInt16 nRow;
+ pIn->ReadUInt16( nRow );
+ rAddr.SetRow( static_cast<SCROW>(nRow) );
+ sal_uInt8 nByte;
+ pIn->ReadUChar( nByte );
+ rAddr.SetTab( static_cast<SCTAB>(nByte) );
+ pIn->ReadUChar( nByte );
+ rAddr.SetCol( static_cast<SCCOL>(nByte) );
+}
+
+inline void ImportLotus::Read( ScRange& rRange )
+{
+ Read( rRange.aStart );
+ Read( rRange.aEnd );
+}
+
+inline void ImportLotus::Read( char& r )
+{
+ pIn->ReadChar( r );
+}
+
+inline void ImportLotus::Read( sal_uInt8& r )
+{
+ pIn->ReadUChar( r );
+}
+
+inline void ImportLotus::Read( sal_uInt16& r )
+{
+ pIn->ReadUInt16( r );
+}
+
+inline void ImportLotus::Read( sal_Int16& r )
+{
+ pIn->ReadInt16( r );
+}
+
+inline void ImportLotus::Read( double& r )
+{
+ ScfTools::ReadLongDouble(*pIn, r);
+}
+
+inline void ImportLotus::Read( LotAttrWK3& r )
+{
+ pIn->ReadUChar( r.nFont ).ReadUChar( r.nFontCol ).ReadUChar( r.nBack ).ReadUChar( r.nLineStyle );
+}
+
+inline void ImportLotus::Skip( const sal_uInt16 n )
+{
+ pIn->SeekRel( n );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotrange.hxx b/sc/source/filter/inc/lotrange.hxx
new file mode 100644
index 0000000000..bbf98ab1fa
--- /dev/null
+++ b/sc/source/filter/inc/lotrange.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <refdata.hxx>
+#include <types.hxx>
+
+#include <memory>
+
+typedef sal_uInt16 LR_ID;
+
+#define ID_FAIL 0xFFFF
+
+class LotusRange
+{
+ friend class LotusRangeList;
+private:
+ sal_uInt32 nHash;
+ SCCOL nColStart;
+ SCROW nRowStart;
+ SCCOL nColEnd;
+ SCROW nRowEnd;
+ LR_ID nId;
+ void MakeHash();
+ inline void Copy( const LotusRange& );
+ inline void SetId( LR_ID nId );
+public:
+ LotusRange( SCCOL nCol, SCROW nRow );
+ LotusRange( SCCOL nColS, SCROW nRowS, SCCOL nColE, SCROW nRowE );
+ LotusRange( const LotusRange& );
+ inline LotusRange &operator =( const LotusRange& );
+ inline bool operator ==( const LotusRange& ) const;
+ inline bool IsSingle() const;
+};
+
+inline void LotusRange::Copy( const LotusRange& rCpy )
+{
+ nHash = rCpy.nHash;
+ nColStart = rCpy.nColStart;
+ nRowStart = rCpy.nRowStart;
+ nColEnd = rCpy.nColEnd;
+ nRowEnd = rCpy.nRowEnd;
+ nId = rCpy.nId;
+}
+
+inline void LotusRange::SetId( LR_ID nNewId )
+{
+ nId = nNewId;
+}
+
+inline LotusRange &LotusRange::operator =( const LotusRange& rCpy )
+{
+ Copy( rCpy );
+ return *this;
+}
+
+inline bool LotusRange::operator ==( const LotusRange& rRef ) const
+{
+ return ( nHash == rRef.nHash && nColStart == rRef.nColStart &&
+ nRowStart == rRef.nRowStart && nColEnd == rRef.nColEnd &&
+ nRowEnd == rRef.nRowEnd );
+}
+
+inline bool LotusRange::IsSingle() const
+{
+ return ( nColStart == nColEnd && nRowStart == nRowEnd );
+}
+
+class LotusRangeList
+{
+private:
+ LR_ID nIdCnt;
+ ScComplexRefData aComplRef;
+ std::vector<std::unique_ptr<LotusRange>> maRanges;
+
+public:
+ LotusRangeList();
+ ~LotusRangeList();
+ inline LR_ID GetIndex( SCCOL nCol, SCROW nRow );
+ inline LR_ID GetIndex( SCCOL nColS, SCROW nRowS, SCCOL nColE, SCROW nRowE );
+ LR_ID GetIndex( const LotusRange& );
+ void Append( const ScDocument* pDoc, std::unique_ptr<LotusRange> pLR );
+};
+
+inline LR_ID LotusRangeList::GetIndex( SCCOL nCol, SCROW nRow )
+{
+ LotusRange aRef( nCol, nRow );
+ return GetIndex( aRef );
+}
+
+inline LR_ID LotusRangeList::GetIndex( SCCOL nColS, SCROW nRowS, SCCOL nColE, SCROW nRowE )
+{
+ LotusRange aRef( nColS, nRowS, nColE, nRowE );
+ return GetIndex( aRef );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/namebuff.hxx b/sc/source/filter/inc/namebuff.hxx
new file mode 100644
index 0000000000..bd98a8ab14
--- /dev/null
+++ b/sc/source/filter/inc/namebuff.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include <utility>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include "root.hxx"
+#include "xiroot.hxx"
+#include <refdata.hxx>
+#include <tokenarray.hxx>
+
+#include <unordered_map>
+
+class StringHashEntry
+{
+private:
+ OUString aString;
+ sal_uInt32 nHash;
+
+ static sal_uInt32 MakeHashCode( const OUString& );
+public:
+ inline StringHashEntry( const OUString& );
+ inline bool operator ==( const StringHashEntry& ) const;
+};
+
+inline StringHashEntry::StringHashEntry( const OUString& r )
+ : aString( r )
+ , nHash( MakeHashCode(r) )
+{
+}
+
+inline bool StringHashEntry::operator ==( const StringHashEntry& r ) const
+{
+ return ( nHash == r.nHash && aString == r.aString );
+}
+
+/**
+ * Store and manage shared formula tokens.
+ */
+class SharedFormulaBuffer : public ExcRoot
+{
+ typedef std::unordered_map<ScAddress, ScTokenArray, ScAddressHashFunctor> TokenArraysType;
+ TokenArraysType maTokenArrays;
+
+public:
+ SharedFormulaBuffer( RootData* pRD );
+ virtual ~SharedFormulaBuffer();
+ void Clear();
+ void Store( const ScAddress& rPos, const ScTokenArray& rArray );
+ const ScTokenArray* Find( const ScAddress& rRefPos ) const;
+};
+
+class RangeNameBufferWK3 final
+{
+private:
+ struct Entry
+ {
+ StringHashEntry aStrHashEntry;
+ ScComplexRefData aScComplexRefDataRel;
+ sal_uInt16 nAbsInd; // == 0 -> no Abs-Name yet!
+ sal_uInt16 nRelInd;
+ bool bSingleRef;
+ Entry( const OUString& rName, const ScComplexRefData& rCRD )
+ : aStrHashEntry( rName )
+ , aScComplexRefDataRel( rCRD )
+ , nAbsInd(0)
+ , nRelInd(0)
+ , bSingleRef(false)
+ {
+ }
+ };
+
+ std::unique_ptr<ScTokenArray>
+ pScTokenArray;
+ sal_uInt16 nIntCount;
+ std::vector<Entry> maEntries;
+
+public:
+ RangeNameBufferWK3(const ScDocument& rDoc);
+ ~RangeNameBufferWK3();
+ void Add( const ScDocument& rDoc, const OUString& rName, const ScComplexRefData& rCRD );
+ inline void Add( const ScDocument& rDoc, const OUString& rName, const ScRange& aScRange );
+ bool FindRel( const OUString& rRef, sal_uInt16& rIndex );
+ bool FindAbs( std::u16string_view rRef, sal_uInt16& rIndex );
+};
+
+inline void RangeNameBufferWK3::Add( const ScDocument& rDoc, const OUString& rName, const ScRange& aScRange )
+{
+ ScComplexRefData aCRD;
+ ScSingleRefData* pSRD;
+
+ pSRD = &aCRD.Ref1;
+ pSRD->InitAddress(aScRange.aStart);
+ pSRD->SetFlag3D(true);
+
+ pSRD = &aCRD.Ref2;
+ pSRD->InitAddress(aScRange.aEnd);
+ pSRD->SetFlag3D(true);
+
+ Add( rDoc, rName, aCRD );
+}
+
+class ExtSheetBuffer : public ExcRoot
+{
+private:
+ struct Cont
+ {
+ OUString aFile;
+ OUString aTab;
+ sal_uInt16 nTabNum; // 0xFFFF -> not set yet
+ // 0xFFFE -> tried to set, but failed
+ // 0xFFFD -> should be in the same workbook, but not found
+ bool bSWB;
+ Cont( OUString aFilePathAndName, OUString aTabName,
+ const bool bSameWB ) :
+ aFile(std::move( aFilePathAndName )),
+ aTab(std::move( aTabName ))
+ {
+ nTabNum = 0xFFFF; // -> table not created yet
+ bSWB = bSameWB;
+ }
+ };
+
+ std::vector<Cont> maEntries;
+
+public:
+ inline ExtSheetBuffer( RootData* );
+
+ sal_Int16 Add( const OUString& rFilePathAndName,
+ const OUString& rTabName, const bool bSameWorkbook );
+
+ bool GetScTabIndex( sal_uInt16 nExcSheetIndex, sal_uInt16& rIn_LastTab_Out_ScIndex );
+
+ void Reset();
+};
+
+inline ExtSheetBuffer::ExtSheetBuffer( RootData* p ) : ExcRoot( p )
+{
+}
+
+struct ExtName
+{
+ sal_uInt32 nStorageId;
+ sal_uInt16 nFlags;
+
+ ExtName( sal_uInt16 n ) : nStorageId( 0 ), nFlags( n ) {}
+
+ bool IsOLE() const;
+};
+
+class ExtNameBuff : protected XclImpRoot
+{
+public:
+ explicit ExtNameBuff( const XclImpRoot& rRoot );
+
+ void AddDDE( sal_Int16 nRefIdx );
+ void AddOLE( sal_Int16 nRefIdx, sal_uInt32 nStorageId );
+ void AddName( sal_Int16 nRefIdx );
+
+ const ExtName* GetNameByIndex( sal_Int16 nRefIdx, sal_uInt16 nNameIdx ) const;
+
+ void Reset();
+
+private:
+ typedef ::std::vector< ExtName > ExtNameVec;
+ typedef ::std::map< sal_Int16, ExtNameVec > ExtNameMap;
+
+ ExtNameMap maExtNames;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/numberformatsbuffer.hxx b/sc/source/filter/inc/numberformatsbuffer.hxx
new file mode 100644
index 0000000000..10ec0de1ce
--- /dev/null
+++ b/sc/source/filter/inc/numberformatsbuffer.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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/Locale.hpp>
+#include "workbookhelper.hxx"
+
+namespace com::sun::star {
+ namespace util { class XNumberFormats; }
+}
+
+class SfxItemSet;
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+struct NumFmtModel
+{
+ css::lang::Locale maLocale;
+ OUString maFmtCode;
+ sal_Int16 mnPredefId;
+
+ explicit NumFmtModel();
+};
+
+/** Contains all API number format attributes. */
+struct ApiNumFmtData
+{
+ sal_Int32 mnIndex; /// API number format index.
+
+ explicit ApiNumFmtData();
+};
+
+/** Contains all data for a number format code. */
+class NumberFormat : public WorkbookHelper
+{
+public:
+ explicit NumberFormat( const WorkbookHelper& rHelper );
+
+ /** Sets the passed format code. */
+ void setFormatCode( std::u16string_view aFmtCode );
+ /** Sets the passed format code, encoded in UTF-8. */
+ void setFormatCode(
+ const css::lang::Locale& rLocale,
+ const char* pcFmtCode );
+ /** Sets the passed predefined format code identifier. */
+ void setPredefinedId(
+ const css::lang::Locale& rLocale,
+ sal_Int16 nPredefId );
+
+ /** Final processing after import of all style settings. Returns the API format index. */
+ void finalizeImport(
+ const css::uno::Reference< css::util::XNumberFormats >& rxNumFmts,
+ const css::lang::Locale& rFromLocale );
+ sal_uInt32 fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ NumFmtModel maModel;
+ ApiNumFmtData maApiData;
+};
+
+typedef std::shared_ptr< NumberFormat > NumberFormatRef;
+
+class NumberFormatsBuffer : public WorkbookHelper
+{
+public:
+ explicit NumberFormatsBuffer( const WorkbookHelper& rHelper );
+
+ /** Inserts a new number format. */
+ NumberFormatRef createNumFmt( sal_uInt32 nNumFmtId, std::u16string_view aFmtCode );
+
+ /** Inserts a new number format code. */
+ NumberFormatRef importNumFmt( const AttributeList& rAttribs );
+ /** Inserts a new number format code from a NUMFMT record. */
+ void importNumFmt( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ sal_uInt32 fillToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const;
+
+ sal_uInt32 nextFreeId(){ return ++mnHighestId; }
+private:
+ /** Inserts built-in number formats for the current system language. */
+ void insertBuiltinFormats();
+
+private:
+ typedef RefMap< sal_uInt32, NumberFormat > NumberFormatMap;
+
+ NumberFormatMap maNumFmts; /// List of number formats.
+ OUString maLocaleStr; /// Current office locale.
+ sal_uInt32 mnHighestId;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/ooxformulaparser.hxx b/sc/source/filter/inc/ooxformulaparser.hxx
new file mode 100644
index 0000000000..25ba76dfc1
--- /dev/null
+++ b/sc/source/filter/inc/ooxformulaparser.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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sheet/XFilterFormulaParser.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::lang { class XComponent; }
+
+namespace oox::xls {
+
+class OOXMLFormulaParserImpl;
+
+typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::sheet::XFilterFormulaParser > OOXMLFormulaParser_BASE;
+
+/** OOXML formula parser/compiler service for usage in ODF filters. */
+class OOXMLFormulaParser : public OOXMLFormulaParser_BASE
+{
+public:
+ explicit OOXMLFormulaParser();
+ virtual ~OOXMLFormulaParser() override;
+
+ // com.sun.star.lang.XServiceInfo interface -------------------------------
+
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL
+ supportsService( const OUString& rService ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // com.sun.star.lang.XInitialization interface ----------------------------
+
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence< css::uno::Any >& rArgs ) override;
+
+ // com.sun.star.sheet.XFilterFormulaParser interface ----------------------
+
+ virtual OUString SAL_CALL
+ getSupportedNamespace() override;
+
+ // com.sun.star.sheet.XFormulaParser interface ----------------------------
+
+ virtual css::uno::Sequence< css::sheet::FormulaToken > SAL_CALL
+ parseFormula(
+ const OUString& rFormula,
+ const css::table::CellAddress& rReferencePos ) override;
+
+ virtual OUString SAL_CALL
+ printFormula(
+ const css::uno::Sequence< css::sheet::FormulaToken >& rTokens,
+ const css::table::CellAddress& rReferencePos ) override;
+
+private:
+ typedef std::shared_ptr< OOXMLFormulaParserImpl > ParserImplRef;
+
+ css::uno::Reference< css::lang::XComponent >
+ mxComponent;
+ ParserImplRef mxParserImpl; /// Implementation of import parser.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/op.h b/sc/source/filter/inc/op.h
new file mode 100644
index 0000000000..13b8e7012f
--- /dev/null
+++ b/sc/source/filter/inc/op.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+class SfxItemSet;
+
+// OP code functions
+class SvStream;
+struct LotusContext;
+void NI(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_BOF(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_EOF(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Integer(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Number(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Label(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Formula(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_ColumnWidth(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_NamedRange(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_SymphNamedRange(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Footer(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Header(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Margins(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_HiddenCols(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Window1(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Blank(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+// Lotus 123 bits.
+void OP_BOF123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_EOF123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Number123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Label123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Formula123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_IEEENumber123(LotusContext &rContext,SvStream& r, sal_uInt16 n);
+void OP_Note123(LotusContext &rContext,SvStream &aStream, sal_uInt16 nLength);
+void OP_CreatePattern123(LotusContext &rContext,SvStream &aStream, sal_uInt16 nLength);
+void OP_SheetName123(LotusContext &rContext, SvStream &rStream, sal_uInt16 nLength );
+void OP_HorAlign123(LotusContext &rContext,sal_uInt8 nAlignPattern, SfxItemSet& rPattern /* const ScPatternAttr& rPattern*/ );
+void OP_VerAlign123(LotusContext &rContext,sal_uInt8 nAlignPattern, SfxItemSet& rPattern /* const ScPatternAttr& rPattern*/ );
+void OP_ApplyPatternArea123(LotusContext &rContext,SvStream& r);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/optab.h b/sc/source/filter/inc/optab.h
new file mode 100644
index 0000000000..fb85d5e516
--- /dev/null
+++ b/sc/source/filter/inc/optab.h
@@ -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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+class SvStream;
+
+struct LotusContext;
+
+typedef void (*OPCODE_FKT)(LotusContext& rContext, SvStream& rStream, sal_uInt16 nLength);
+
+#define FKT_LIMIT 101
+
+#define FKT_LIMIT123 101
+
+#define LOTUS_EOF 0x01
+
+#define LOTUS_FILEPASSWD 0x4b
+
+#define LOTUS_PATTERN 0x284
+
+#define LOTUS_FORMAT_INDEX 0x800
+
+#define LOTUS_FORMAT_INFO 0x801
+
+#define ROW_FORMAT_MARKER 0x106
+
+#define COL_FORMAT_MARKER 0x107
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/orcusfiltersimpl.hxx b/sc/source/filter/inc/orcusfiltersimpl.hxx
new file mode 100644
index 0000000000..7bba1410dc
--- /dev/null
+++ b/sc/source/filter/inc/orcusfiltersimpl.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <orcusfilters.hxx>
+
+#include <orcus/xml_namespace.hpp>
+
+class ScOrcusFiltersImpl : public ScOrcusFilters
+{
+public:
+ virtual ImportResult importByName(ScDocument& rDoc, SfxMedium& rMedium,
+ const OUString& rFilterName) const override;
+
+ virtual bool importODS_Styles(ScDocument& rDoc, OUString& aFileName) const override;
+
+ virtual std::unique_ptr<ScOrcusXMLContext>
+ createXMLContext(ScDocument& rDoc, const OUString& rPath) const override;
+};
+
+class ScOrcusXMLContextImpl : public ScOrcusXMLContext
+{
+ ScDocument& mrDoc;
+ OUString maPath;
+
+ orcus::xmlns_repository maNsRepo; /// XML namespace repository for this context.
+
+public:
+ ScOrcusXMLContextImpl(ScDocument& rDoc, OUString aPath);
+ virtual ~ScOrcusXMLContextImpl() override;
+
+ virtual void loadXMLStructure(weld::TreeView& rTreeCtrl, ScOrcusXMLTreeParam& rParam) override;
+
+ virtual void importXML(const ScOrcusImportXMLParam& rParam) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/orcusinterface.hxx b/sc/source/filter/inc/orcusinterface.hxx
new file mode 100644
index 0000000000..2fd1d5ed40
--- /dev/null
+++ b/sc/source/filter/inc/orcusinterface.hxx
@@ -0,0 +1,773 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <address.hxx>
+#include <documentimport.hxx>
+#include <editutil.hxx>
+
+#include <tools/color.hxx>
+#include <tools/fontenum.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/editobj.hxx>
+
+#include "sharedformulagroups.hxx"
+
+#include <conditio.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <editeng/borderline.hxx>
+
+#include <orcus/spreadsheet/import_interface.hpp>
+#include <orcus/spreadsheet/import_interface_styles.hpp>
+
+#include <memory>
+#include <map>
+#include <unordered_map>
+#include <vector>
+#include <variant>
+
+class ScOrcusSheet;
+class ScOrcusStyles;
+class ScOrcusFactory;
+class SfxItemSet;
+namespace com::sun::star::task { class XStatusIndicator; }
+
+class ScOrcusGlobalSettings : public orcus::spreadsheet::iface::import_global_settings
+{
+ ScDocumentImport& mrDoc;
+ formula::FormulaGrammar::Grammar meCalcGrammar;
+ orcus::spreadsheet::formula_grammar_t meOrcusGrammar;
+ rtl_TextEncoding mnTextEncoding;
+
+public:
+ ScOrcusGlobalSettings(ScDocumentImport& rDoc);
+
+ virtual void set_origin_date(int year, int month, int day) override;
+ virtual void set_character_set(orcus::character_set_t cs) override;
+
+ virtual void set_default_formula_grammar(orcus::spreadsheet::formula_grammar_t grammar) override;
+ virtual orcus::spreadsheet::formula_grammar_t get_default_formula_grammar() const override;
+
+ formula::FormulaGrammar::Grammar getCalcGrammar() const
+ {
+ return meCalcGrammar;
+ }
+
+ rtl_TextEncoding getTextEncoding() const
+ {
+ return mnTextEncoding;
+ }
+
+ ScDocumentImport& getDoc() const
+ {
+ return mrDoc;
+ }
+};
+
+class ScOrcusRefResolver : public orcus::spreadsheet::iface::import_reference_resolver
+{
+ const ScOrcusGlobalSettings& mrGlobalSettings;
+
+public:
+ ScOrcusRefResolver( const ScOrcusGlobalSettings& rGS );
+
+ orcus::spreadsheet::src_address_t resolve_address(std::string_view address) override;
+ orcus::spreadsheet::src_range_t resolve_range(std::string_view range) override;
+};
+
+class ScOrcusNamedExpression : public orcus::spreadsheet::iface::import_named_expression
+{
+ ScDocumentImport& mrDoc;
+ const ScOrcusGlobalSettings& mrGlobalSettings;
+ ScAddress maBasePos;
+ OUString maName;
+ OUString maExpr;
+ const SCTAB mnTab; //< negative if global, else >= 0 for sheet-local named expressions.
+
+public:
+ ScOrcusNamedExpression( ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab = -1 );
+
+ void reset();
+
+ virtual void set_base_position(const orcus::spreadsheet::src_address_t& pos) override;
+ virtual void set_named_expression(std::string_view name, std::string_view expression) override;
+ virtual void set_named_range(std::string_view name, std::string_view range) override;
+ virtual void commit() override;
+};
+
+class ScOrcusSharedStrings : public orcus::spreadsheet::iface::import_shared_strings
+{
+ ScOrcusFactory& mrFactory;
+ ScFieldEditEngine& mrEditEngine;
+
+ SfxItemSet maCurFormat;
+ std::vector<std::pair<ESelection, SfxItemSet>> maFormatSegments;
+
+ OUString toOUString(std::string_view s);
+
+public:
+ ScOrcusSharedStrings(ScOrcusFactory& rFactory);
+
+ virtual size_t append(std::string_view s) override;
+ virtual size_t add(std::string_view s) override;
+
+ virtual void set_segment_bold(bool b) override;
+ virtual void set_segment_italic(bool b) override;
+ virtual void set_segment_font(size_t font_index) override;
+ virtual void set_segment_font_name(std::string_view s) override;
+ virtual void set_segment_font_size(double point) override;
+ virtual void set_segment_font_color(orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ virtual void append_segment(std::string_view s) override;
+
+ virtual size_t commit_segments() override;
+};
+
+class ScOrcusConditionalFormat : public orcus::spreadsheet::iface::import_conditional_format
+{
+public:
+ ScOrcusConditionalFormat(SCTAB nTab, ScDocument& rDoc);
+ virtual ~ScOrcusConditionalFormat() override;
+
+ virtual void set_color(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+
+ virtual void set_formula(std::string_view formula) override;
+
+ virtual void set_condition_type(orcus::spreadsheet::condition_type_t type) override;
+
+ virtual void set_date(orcus::spreadsheet::condition_date_t date) override;
+
+ virtual void commit_condition() override;
+
+ virtual void set_icon_name(std::string_view name) override;
+
+ virtual void set_databar_gradient(bool gradient) override;
+
+ virtual void set_databar_axis(orcus::spreadsheet::databar_axis_t axis) override;
+
+ virtual void set_databar_color_positive(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+
+ virtual void set_databar_color_negative(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+
+ virtual void set_min_databar_length(double length) override;
+
+ virtual void set_max_databar_length(double length) override;
+
+ virtual void set_show_value(bool show) override;
+
+ virtual void set_iconset_reverse(bool reverse) override;
+
+ virtual void set_xf_id(size_t xf) override;
+
+ virtual void set_operator(orcus::spreadsheet::condition_operator_t condition_type) override;
+
+ virtual void set_type(orcus::spreadsheet::conditional_format_t type) override;
+
+ virtual void commit_entry() override;
+
+ virtual void set_range(std::string_view range) override;
+
+ virtual void set_range(orcus::spreadsheet::row_t row_start, orcus::spreadsheet::col_t col_start,
+ orcus::spreadsheet::row_t row_end, orcus::spreadsheet::col_t col_end) override;
+
+ virtual void commit_format() override;
+
+private:
+
+ SCTAB mnTab;
+ ScDocument& mrDoc;
+
+ std::unique_ptr<ScConditionalFormat> mpCurrentFormat;
+
+ ScFormatEntry::Type meEntryType;
+};
+
+class ScOrcusAutoFilter : public orcus::spreadsheet::iface::import_auto_filter
+{
+public:
+ ScOrcusAutoFilter( const ScOrcusGlobalSettings& rGS );
+
+ virtual ~ScOrcusAutoFilter() override;
+
+ virtual void set_range(const orcus::spreadsheet::range_t& range) override;
+
+ virtual void set_column(orcus::spreadsheet::col_t col) override;
+
+ virtual void append_column_match_value(std::string_view value) override;
+
+ virtual void commit_column() override;
+
+ virtual void commit() override;
+
+private:
+ const ScOrcusGlobalSettings& mrGlobalSettings;
+ ScRange maRange;
+};
+
+class ScOrcusSheetProperties : public orcus::spreadsheet::iface::import_sheet_properties
+{
+ ScDocumentImport& mrDoc;
+ SCTAB mnTab;
+public:
+ ScOrcusSheetProperties(SCTAB nTab, ScDocumentImport& rDoc);
+ virtual ~ScOrcusSheetProperties() override;
+
+ virtual void set_column_width(
+ orcus::spreadsheet::col_t col, orcus::spreadsheet::col_t col_span,
+ double width, orcus::length_unit_t unit) override;
+
+ virtual void set_column_hidden(
+ orcus::spreadsheet::col_t col, orcus::spreadsheet::col_t col_span,
+ bool hidden) override;
+
+ virtual void set_row_height(orcus::spreadsheet::row_t row, double height, orcus::length_unit_t unit) override;
+
+ virtual void set_row_hidden(orcus::spreadsheet::row_t row, bool hidden) override;
+
+ virtual void set_merge_cell_range(const orcus::spreadsheet::range_t& range) override;
+};
+
+class ScOrcusFormula : public orcus::spreadsheet::iface::import_formula
+{
+ enum class ResultType { NotSet, String, Value, Empty };
+
+ friend class ScOrcusSheet;
+
+ ScOrcusSheet& mrSheet;
+
+ SCCOL mnCol;
+ SCROW mnRow;
+ OUString maFormula;
+ formula::FormulaGrammar::Grammar meGrammar;
+ size_t mnSharedFormulaIndex;
+ bool mbShared;
+
+ ResultType meResType;
+ OUString maResult; // result string.
+ double mfResult;
+
+ void reset();
+
+public:
+ ScOrcusFormula( ScOrcusSheet& rSheet );
+ virtual ~ScOrcusFormula() override;
+
+ virtual void set_position(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col) override;
+ virtual void set_formula(orcus::spreadsheet::formula_grammar_t grammar, std::string_view formula) override;
+ virtual void set_shared_formula_index(size_t index) override;
+ virtual void set_result_value(double value) override;
+ virtual void set_result_string(std::string_view value) override;
+ virtual void set_result_empty() override;
+ virtual void set_result_bool(bool value) override;
+ virtual void commit() override;
+};
+
+class ScOrcusArrayFormula : public orcus::spreadsheet::iface::import_array_formula
+{
+ friend class ScOrcusSheet;
+
+ ScOrcusSheet& mrSheet;
+
+ SCCOL mnCol;
+ SCROW mnRow;
+ uint32_t mnColRange;
+ uint32_t mnRowRange;
+ OUString maFormula;
+ formula::FormulaGrammar::Grammar meGrammar;
+
+ void reset();
+
+public:
+ ScOrcusArrayFormula( ScOrcusSheet& rSheet );
+ virtual ~ScOrcusArrayFormula() override;
+
+ virtual void set_range(const orcus::spreadsheet::range_t& range) override;
+ virtual void set_formula(orcus::spreadsheet::formula_grammar_t grammar, std::string_view formula) override;
+ virtual void set_result_value(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, double value) override;
+ virtual void set_result_string(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, std::string_view value) override;
+ virtual void set_result_empty(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col) override;
+ virtual void set_result_bool(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, bool value) override;
+ virtual void commit() override;
+};
+
+class ScOrcusSheet : public orcus::spreadsheet::iface::import_sheet
+{
+ friend class ScOrcusFormula;
+ friend class ScOrcusArrayFormula;
+
+ ScDocumentImport& mrDoc;
+ SCTAB mnTab;
+ ScOrcusFactory& mrFactory;
+ ScOrcusStyles& mrStyles;
+ sc::SharedFormulaGroups maFormulaGroups;
+
+ ScOrcusAutoFilter maAutoFilter;
+ ScOrcusSheetProperties maProperties;
+ ScOrcusConditionalFormat maConditionalFormat;
+ ScOrcusNamedExpression maNamedExpressions;
+ ScOrcusFormula maFormula;
+ ScOrcusArrayFormula maArrayFormula;
+
+ int mnCellCount;
+
+ void cellInserted();
+
+ ScDocumentImport& getDoc();
+
+public:
+ ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& rFactory);
+
+ virtual orcus::spreadsheet::iface::import_auto_filter* get_auto_filter() override;
+ virtual orcus::spreadsheet::iface::import_table* get_table() override;
+ virtual orcus::spreadsheet::iface::import_sheet_properties* get_sheet_properties() override;
+ virtual orcus::spreadsheet::iface::import_conditional_format* get_conditional_format() override;
+ virtual orcus::spreadsheet::iface::import_named_expression* get_named_expression() override;
+ virtual orcus::spreadsheet::iface::import_formula* get_formula() override;
+ virtual orcus::spreadsheet::iface::import_array_formula* get_array_formula() override;
+
+ // Orcus import interface
+ virtual void set_auto(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, std::string_view value) override;
+ virtual void set_string(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, orcus::spreadsheet::string_id_t sindex) override;
+ virtual void set_value(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, double value) override;
+ virtual void set_bool(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, bool value) override;
+ virtual void set_date_time(
+ orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, int year, int month, int day, int hour, int minute, double second) override;
+
+ virtual void set_format(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, size_t xf_index) override;
+ virtual void set_format(orcus::spreadsheet::row_t row_start, orcus::spreadsheet::col_t col_start,
+ orcus::spreadsheet::row_t row_end, orcus::spreadsheet::col_t col_end, size_t xf_index) override;
+ virtual void set_column_format(
+ orcus::spreadsheet::col_t col, orcus::spreadsheet::col_t col_span, std::size_t xf_index) override;
+ virtual void set_row_format(orcus::spreadsheet::row_t row, std::size_t xf_index) override;
+
+ virtual orcus::spreadsheet::range_size_t get_sheet_size() const override;
+
+ virtual void fill_down_cells(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, orcus::spreadsheet::row_t range_size) override;
+
+ SCTAB getIndex() const { return mnTab; }
+
+ const sc::SharedFormulaGroups& getSharedFormulaGroups() const;
+ sc::SharedFormulaGroups& getSharedFormulaGroups();
+ ScOrcusFactory& getFactory();
+};
+
+struct ScOrcusFont
+{
+ std::optional<OUString> maName;
+ std::optional<OUString> maNameAsian;
+ std::optional<OUString> maNameComplex;
+ std::optional<double> mnSize;
+ std::optional<double> mnSizeAsian;
+ std::optional<double> mnSizeComplex;
+ std::optional<Color> maColor;
+ std::optional<bool> mbBold;
+ std::optional<bool> mbBoldAsian;
+ std::optional<bool> mbBoldComplex;
+ std::optional<bool> mbItalic;
+ std::optional<bool> mbItalicAsian;
+ std::optional<bool> mbItalicComplex;
+ std::optional<FontLineStyle> meUnderline;
+ std::optional<Color> maUnderlineColor;
+ std::optional<FontStrikeout> meStrikeout;
+
+ void applyToItemSet( SfxItemSet& rSet ) const;
+};
+
+struct ScOrcusFill
+{
+ std::optional<orcus::spreadsheet::fill_pattern_t> mePattern;
+ std::optional<Color> maFgColor;
+ std::optional<Color> maBgColor; // currently not used.
+
+ void applyToItemSet( SfxItemSet& rSet ) const;
+};
+
+struct ScOrcusBorder
+{
+ struct BorderLine
+ {
+ std::optional<SvxBorderLineStyle> meStyle;
+ std::optional<Color> maColor;
+ std::optional<double> mnWidth;
+ };
+
+ std::map<orcus::spreadsheet::border_direction_t, BorderLine> maBorders;
+
+ void applyToItemSet( SfxItemSet& rSet ) const;
+};
+
+struct ScOrcusProtection
+{
+ std::optional<bool> mbLocked;
+ std::optional<bool> mbHidden;
+ std::optional<bool> mbPrintContent;
+ std::optional<bool> mbFormulaHidden;
+
+ void applyToItemSet( SfxItemSet& rSet ) const;
+};
+
+struct ScOrcusNumberFormat
+{
+ std::optional<OUString> maCode;
+
+ void applyToItemSet( SfxItemSet& rSet, const ScDocument& rDoc ) const;
+};
+
+struct ScOrcusXf
+{
+ std::size_t mnFontId;
+ std::size_t mnFillId;
+ std::size_t mnBorderId;
+ std::size_t mnProtectionId;
+ std::size_t mnNumberFormatId;
+ std::size_t mnStyleXf;
+
+ bool mbApplyAlignment;
+ std::optional<bool> mbWrapText;
+ std::optional<bool> mbShrinkToFit;
+
+ SvxCellHorJustify meHorAlignment;
+ SvxCellVerJustify meVerAlignment;
+ SvxCellJustifyMethod meHorAlignMethod;
+ SvxCellJustifyMethod meVerAlignMethod;
+
+ ScOrcusXf();
+};
+
+struct ScOrcusCellStyle
+{
+ OUString maName;
+ OUString maDisplayName;
+ OUString maParentName;
+ std::size_t mnXFId;
+ std::size_t mnBuiltInId;
+
+ ScOrcusCellStyle();
+};
+
+class ScOrcusImportFontStyle : public orcus::spreadsheet::iface::import_font_style
+{
+ ScOrcusFont maCurrentFont;
+ ScOrcusFactory& mrFactory;
+ std::vector<ScOrcusFont>& mrFonts;
+
+public:
+ ScOrcusImportFontStyle( ScOrcusFactory& rFactory, std::vector<ScOrcusFont>& rFonts );
+
+ void reset();
+
+ void set_bold(bool b) override;
+ void set_bold_asian(bool b) override;
+ void set_bold_complex(bool b) override;
+ void set_italic(bool b) override;
+ void set_italic_asian(bool b) override;
+ void set_italic_complex(bool b) override;
+ void set_name(std::string_view s) override;
+ void set_name_asian(std::string_view s) override;
+ void set_name_complex(std::string_view s) override;
+ void set_size(double point) override;
+ void set_size_asian(double point) override;
+ void set_size_complex(double point) override;
+ void set_underline(orcus::spreadsheet::underline_t e) override;
+ void set_underline_width(orcus::spreadsheet::underline_width_t e) override;
+ void set_underline_mode(orcus::spreadsheet::underline_mode_t e) override;
+ void set_underline_type(orcus::spreadsheet::underline_type_t e) override;
+ void set_underline_color(
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ void set_color(
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ void set_strikethrough_style(orcus::spreadsheet::strikethrough_style_t s) override;
+ void set_strikethrough_type(orcus::spreadsheet::strikethrough_type_t s) override;
+ void set_strikethrough_width(orcus::spreadsheet::strikethrough_width_t s) override;
+ void set_strikethrough_text(orcus::spreadsheet::strikethrough_text_t s) override;
+ std::size_t commit() override;
+};
+
+class ScOrcusImportFillStyle : public orcus::spreadsheet::iface::import_fill_style
+{
+ ScOrcusFill maCurrentFill;
+ std::vector<ScOrcusFill>& mrFills;
+
+public:
+ ScOrcusImportFillStyle( std::vector<ScOrcusFill>& rFills );
+
+ void reset();
+
+ void set_pattern_type(orcus::spreadsheet::fill_pattern_t fp) override;
+ void set_fg_color(
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ void set_bg_color(
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ std::size_t commit() override;
+};
+
+class ScOrcusImportBorderStyle : public orcus::spreadsheet::iface::import_border_style
+{
+ ScOrcusBorder maCurrentBorder;
+ std::vector<ScOrcusBorder>& mrBorders;
+
+public:
+ ScOrcusImportBorderStyle( std::vector<ScOrcusBorder>& rBorders );
+
+ void reset();
+
+ void set_width(
+ orcus::spreadsheet::border_direction_t dir, double width, orcus::length_unit_t unit) override;
+ void set_style(
+ orcus::spreadsheet::border_direction_t dir, orcus::spreadsheet::border_style_t style) override;
+ void set_color(
+ orcus::spreadsheet::border_direction_t dir,
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ std::size_t commit() override;
+};
+
+class ScOrcusImportCellProtection : public orcus::spreadsheet::iface::import_cell_protection
+{
+ ScOrcusProtection maCurrentProtection;
+ std::vector<ScOrcusProtection>& mrProtections;
+
+public:
+ ScOrcusImportCellProtection( std::vector<ScOrcusProtection>& rProtections );
+
+ void reset();
+
+ void set_hidden(bool b) override;
+ void set_locked(bool b) override;
+ void set_print_content(bool b) override;
+ void set_formula_hidden(bool b) override;
+ std::size_t commit() override;
+};
+
+class ScOrcusImportNumberFormat : public orcus::spreadsheet::iface::import_number_format
+{
+ ScOrcusNumberFormat maCurrentFormat;
+ ScOrcusFactory& mrFactory;
+ std::vector<ScOrcusNumberFormat>& mrNumberFormats;
+
+public:
+ ScOrcusImportNumberFormat( ScOrcusFactory& rFactory, std::vector<ScOrcusNumberFormat>& rFormats );
+
+ void reset();
+
+ void set_identifier(std::size_t id) override;
+ void set_code(std::string_view s) override;
+ std::size_t commit() override;
+};
+
+class ScOrucsImportCellStyle : public orcus::spreadsheet::iface::import_cell_style
+{
+ ScOrcusCellStyle maCurrentStyle;
+ ScOrcusFactory& mrFactory;
+ ScOrcusStyles& mrStyles;
+ const std::vector<ScOrcusXf>& mrXfs;
+
+public:
+ ScOrucsImportCellStyle(
+ ScOrcusFactory& rFactory, ScOrcusStyles& rStyles, const std::vector<ScOrcusXf>& rXfs );
+
+ void reset();
+
+ void set_name(std::string_view s) override;
+ void set_display_name(std::string_view s) override;
+ void set_xf(std::size_t index) override;
+ void set_builtin(std::size_t index) override;
+ void set_parent_name(std::string_view s) override;
+ void commit() override;
+};
+
+class ScOrcusImportXf : public orcus::spreadsheet::iface::import_xf
+{
+ ScOrcusXf maCurrentXf;
+ std::vector<ScOrcusXf>* mpXfs = nullptr;
+
+public:
+ void reset( std::vector<ScOrcusXf>& rXfs );
+
+ void set_font(std::size_t index) override;
+ void set_fill(std::size_t index) override;
+ void set_border(std::size_t index) override;
+ void set_protection(std::size_t index) override;
+ void set_number_format(std::size_t index) override;
+ void set_style_xf(std::size_t index) override;
+ void set_apply_alignment(bool b) override;
+ void set_horizontal_alignment(orcus::spreadsheet::hor_alignment_t align) override;
+ void set_vertical_alignment(orcus::spreadsheet::ver_alignment_t align) override;
+ void set_wrap_text(bool b) override;
+ void set_shrink_to_fit(bool b) override;
+ std::size_t commit() override;
+};
+
+class ScOrcusStyles : public orcus::spreadsheet::iface::import_styles
+{
+private:
+ ScOrcusFactory& mrFactory;
+
+ std::vector<ScOrcusFont> maFonts;
+ std::vector<ScOrcusFill> maFills;
+ std::vector<ScOrcusBorder> maBorders;
+ std::vector<ScOrcusProtection> maProtections;
+ std::vector<ScOrcusNumberFormat> maNumberFormats;
+ std::vector<ScOrcusXf> maCellXfs;
+ std::vector<ScOrcusXf> maCellStyleXfs;
+ std::vector<ScOrcusXf> maCellDiffXfs;
+
+ ScOrcusImportFontStyle maFontStyle;
+ ScOrcusImportFillStyle maFillStyle;
+ ScOrcusImportBorderStyle maBorderStyle;
+ ScOrcusImportCellProtection maCellProtection;
+ ScOrcusImportNumberFormat maNumberFormat;
+ ScOrucsImportCellStyle maCellStyle;
+ ScOrcusImportXf maXf;
+
+public:
+ ScOrcusStyles( ScOrcusFactory& rFactory, bool bSkipDefaultStyles=false );
+
+ void applyXfToItemSet( SfxItemSet& rSet, const ScOrcusXf& rXf );
+ void applyXfToItemSet( SfxItemSet& rSet, std::size_t xfId );
+
+ virtual orcus::spreadsheet::iface::import_font_style* start_font_style() override;
+ virtual orcus::spreadsheet::iface::import_fill_style* start_fill_style() override;
+ virtual orcus::spreadsheet::iface::import_border_style* start_border_style() override;
+ virtual orcus::spreadsheet::iface::import_cell_protection* start_cell_protection() override;
+ virtual orcus::spreadsheet::iface::import_number_format* start_number_format() override;
+ virtual orcus::spreadsheet::iface::import_xf* start_xf(orcus::spreadsheet::xf_category_t cat) override;
+ virtual orcus::spreadsheet::iface::import_cell_style* start_cell_style() override;
+
+ virtual void set_font_count(size_t n) override;
+ virtual void set_fill_count(size_t n) override;
+ virtual void set_border_count(size_t n) override;
+ virtual void set_number_format_count(size_t n) override;
+ virtual void set_xf_count(orcus::spreadsheet::xf_category_t cat, size_t n) override;
+ virtual void set_cell_style_count(size_t n) override;
+};
+
+class ScOrcusFactory : public orcus::spreadsheet::iface::import_factory
+{
+ struct CellStoreToken
+ {
+ enum class Type : sal_uInt8
+ {
+ Auto,
+ Numeric,
+ String,
+ Formula,
+ FormulaWithResult,
+ SharedFormula,
+ SharedFormulaWithResult,
+ Matrix,
+ FillDownCells
+ };
+
+
+ OUString maStr1;
+ OUString maStr2;
+ double mfValue;
+
+ ScAddress maPos;
+ Type meType;
+
+ uint32_t mnIndex1;
+ uint32_t mnIndex2;
+ formula::FormulaGrammar::Grammar meGrammar;
+
+ CellStoreToken( const ScAddress& rPos, Type eType );
+ CellStoreToken( const ScAddress& rPos, double fValue );
+ CellStoreToken( const ScAddress& rPos, uint32_t nIndex );
+ CellStoreToken( const ScAddress& rPos, OUString aFormula, formula::FormulaGrammar::Grammar eGrammar );
+ };
+
+ using StringValueType = std::variant<OUString, std::unique_ptr<EditTextObject>>;
+ typedef std::unordered_map<OUString, size_t> StringHashType;
+ typedef std::vector<CellStoreToken> CellStoreTokensType;
+
+ ScDocumentImport maDoc;
+
+ std::vector<StringValueType> maStrings;
+ StringHashType maStringHash;
+
+ CellStoreTokensType maCellStoreTokens;
+ ScOrcusGlobalSettings maGlobalSettings;
+ ScOrcusRefResolver maRefResolver;
+ ScOrcusSharedStrings maSharedStrings;
+ ScOrcusNamedExpression maNamedExpressions;
+ std::vector<std::unique_ptr<ScOrcusSheet>> maSheets;
+ ScOrcusStyles maStyles;
+
+ int mnProgress;
+
+ css::uno::Reference<css::task::XStatusIndicator> mxStatusIndicator;
+
+public:
+ ScOrcusFactory(ScDocument& rDoc, bool bSkipDefaultStyles=false);
+
+ virtual orcus::spreadsheet::iface::import_sheet* append_sheet(
+ orcus::spreadsheet::sheet_t sheet_index, std::string_view sheet_name) override;
+ virtual orcus::spreadsheet::iface::import_sheet* get_sheet(std::string_view sheet_name) override;
+ virtual orcus::spreadsheet::iface::import_sheet* get_sheet(orcus::spreadsheet::sheet_t sheet_index) override;
+ virtual orcus::spreadsheet::iface::import_global_settings* get_global_settings() override;
+ virtual orcus::spreadsheet::iface::import_shared_strings* get_shared_strings() override;
+ virtual orcus::spreadsheet::iface::import_named_expression* get_named_expression() override;
+ virtual orcus::spreadsheet::iface::import_styles* get_styles() override;
+ virtual orcus::spreadsheet::iface::import_reference_resolver* get_reference_resolver(
+ orcus::spreadsheet::formula_ref_context_t cxt) override;
+ virtual void finalize() override;
+
+ ScDocumentImport& getDoc();
+
+ size_t appendString(const OUString& rStr);
+ size_t addString(const OUString& rStr);
+ std::size_t appendFormattedString(std::unique_ptr<EditTextObject> pEditText);
+
+ const OUString* getString(size_t nIndex) const;
+
+ void pushCellStoreAutoToken( const ScAddress& rPos, const OUString& rVal );
+ void pushCellStoreToken( const ScAddress& rPos, uint32_t nStrIndex );
+ void pushCellStoreToken( const ScAddress& rPos, double fValue );
+ void pushCellStoreToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar );
+ void pushFillDownCellsToken( const ScAddress& rPos, uint32_t nFillSize );
+
+ void pushSharedFormulaToken( const ScAddress& rPos, uint32_t nIndex );
+ void pushMatrixFormulaToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
+ uint32_t nRowRange, uint32_t nColRange );
+
+ void pushFormulaResult( const ScAddress& rPos, double fValue );
+ void pushFormulaResult( const ScAddress& rPos, const OUString& rValue );
+
+ void incrementProgress();
+
+ void setStatusIndicator(const css::uno::Reference<css::task::XStatusIndicator>& rIndicator);
+
+ const ScOrcusGlobalSettings& getGlobalSettings() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/otlnbuff.hxx b/sc/source/filter/inc/otlnbuff.hxx
new file mode 100644
index 0000000000..288971f513
--- /dev/null
+++ b/sc/source/filter/inc/otlnbuff.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 .
+ */
+
+#pragma once
+
+#include <mdds/flat_segment_tree.hpp>
+#include <set>
+#include <address.hxx>
+
+class ScOutlineArray;
+
+class XclImpOutlineBuffer
+{
+public:
+ XclImpOutlineBuffer( SCSIZE nNewSize );
+ ~XclImpOutlineBuffer();
+
+ void SetLevel( SCSIZE nIndex, sal_uInt8 nVal, bool bCollapsed );
+ void SetOutlineArray( ScOutlineArray* pOArray );
+ void MakeScOutline();
+ void SetLevelRange( SCSIZE nF, SCSIZE nL, sal_uInt8 nVal, bool bCollapsed );
+ void SetButtonMode( bool bRightOrUnder );
+
+private:
+ typedef ::mdds::flat_segment_tree<SCSIZE, sal_uInt8> OutlineLevels;
+ OutlineLevels maLevels;
+ ::std::set<SCSIZE> maCollapsedPosSet;
+ ScOutlineArray* mpOutlineArray;
+ SCSIZE mnEndPos;
+ sal_uInt8 mnMaxLevel;
+ bool mbButtonAfter:1;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pagesettings.hxx b/sc/source/filter/inc/pagesettings.hxx
new file mode 100644
index 0000000000..f951f3197b
--- /dev/null
+++ b/sc/source/filter/inc/pagesettings.hxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+
+#include "worksheethelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class PropertySet; }
+namespace oox::core { class Relations; }
+
+namespace oox::xls {
+
+class HeaderFooterParser;
+
+/** Holds page style data for a single sheet. */
+struct PageSettingsModel
+{
+ css::uno::Reference<css::graphic::XGraphic> mxGraphic; /// Background Graphic
+ OUString maBinSettPath; /// Relation identifier of binary printer settings.
+ OUString maOddHeader; /// Header string for odd pages.
+ OUString maOddFooter; /// Footer string for odd pages.
+ OUString maEvenHeader; /// Header string for even pages.
+ OUString maEvenFooter; /// Footer string for even pages.
+ OUString maFirstHeader; /// Header string for first page of the sheet.
+ OUString maFirstFooter; /// Footer string for first page of the sheet.
+ double mfLeftMargin; /// Margin between left edge of page and begin of sheet area.
+ double mfRightMargin; /// Margin between end of sheet area and right edge of page.
+ double mfTopMargin; /// Margin between top edge of page and begin of sheet area.
+ double mfBottomMargin; /// Margin between end of sheet area and bottom edge of page.
+ double mfHeaderMargin; /// Margin between top edge of page and begin of header.
+ double mfFooterMargin; /// Margin between end of footer and bottom edge of page.
+ sal_Int32 mnPaperSize; /// Paper size (enumeration).
+ sal_Int32 mnPaperWidth; /// Paper width in twips
+ sal_Int32 mnPaperHeight; /// Paper height in twips
+ sal_Int32 mnCopies; /// Number of copies to print.
+ sal_Int32 mnScale; /// Page scale (zoom in percent).
+ sal_Int32 mnFirstPage; /// First page number.
+ sal_Int32 mnFitToWidth; /// Fit to number of pages in horizontal direction.
+ sal_Int32 mnFitToHeight; /// Fit to number of pages in vertical direction.
+ sal_Int32 mnHorPrintRes; /// Horizontal printing resolution in DPI.
+ sal_Int32 mnVerPrintRes; /// Vertical printing resolution in DPI.
+ sal_Int32 mnOrientation; /// Landscape or portrait.
+ sal_Int32 mnPageOrder; /// Page order through sheet area (to left or down).
+ sal_Int32 mnCellComments; /// Cell comments printing mode.
+ sal_Int32 mnPrintErrors; /// Cell error printing mode.
+ bool mbUseEvenHF; /// True = use maEvenHeader/maEvenFooter.
+ bool mbUseFirstHF; /// True = use maFirstHeader/maFirstFooter.
+ bool mbValidSettings; /// True = use imported settings.
+ bool mbUseFirstPage; /// True = start page numbering with mnFirstPage.
+ bool mbBlackWhite; /// True = print black and white.
+ bool mbDraftQuality; /// True = print in draft quality.
+ bool mbFitToPages; /// True = Fit to width/height; false = scale in percent.
+ bool mbHorCenter; /// True = horizontally centered.
+ bool mbVerCenter; /// True = vertically centered.
+ bool mbPrintGrid; /// True = print grid lines.
+ bool mbPrintHeadings; /// True = print column/row headings.
+
+ explicit PageSettingsModel();
+
+ /** Sets the BIFF print errors mode. */
+ void setBiffPrintErrors( sal_uInt8 nPrintErrors );
+};
+
+class PageSettings : public WorksheetHelper
+{
+public:
+ explicit PageSettings( const WorksheetHelper& rHelper );
+
+ /** Imports printing options from a printOptions element. */
+ void importPrintOptions( const AttributeList& rAttribs );
+ /** Imports pageMarings element containing page margins. */
+ void importPageMargins( const AttributeList& rAttribs );
+ /** Imports pageSetup element for worksheets. */
+ void importPageSetup( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports pageSetup element for chart sheets. */
+ void importChartPageSetup( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports header and footer settings from a headerFooter element. */
+ void importHeaderFooter( const AttributeList& rAttribs );
+ /** Imports header/footer characters from a headerFooter element. */
+ void importHeaderFooterCharacters(
+ std::u16string_view rChars, sal_Int32 nElement );
+ /** Imports the picture element. */
+ void importPicture( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+
+ /** Imports the PRINTOPTIONS record from the passed stream. */
+ void importPrintOptions( SequenceInputStream& rStrm );
+ /** Imports the PAGEMARGINS record from the passed stream. */
+ void importPageMargins( SequenceInputStream& rStrm );
+ /** Imports the PAGESETUP record from the passed stream. */
+ void importPageSetup( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+ /** Imports the CHARTPAGESETUP record from the passed stream. */
+ void importChartPageSetup( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+ /** Imports the HEADERFOOTER record from the passed stream. */
+ void importHeaderFooter( SequenceInputStream& rStrm );
+ /** Imports the PICTURE record from the passed stream. */
+ void importPicture( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+
+ /** Sets whether percentual scaling or fit to width/height scaling is used. */
+ void setFitToPagesMode( bool bFitToPages );
+
+ /** Creates a page style for the spreadsheet and sets all page properties. */
+ void finalizeImport();
+
+private:
+ /** Imports the binary picture data from the fragment with the passed identifier. */
+ void importPictureData( const ::oox::core::Relations& rRelations, const OUString& rRelId );
+
+private:
+ PageSettingsModel maModel;
+};
+
+class PageSettingsConverter : public WorkbookHelper
+{
+public:
+ explicit PageSettingsConverter( const WorkbookHelper& rHelper );
+ virtual ~PageSettingsConverter() override;
+
+ /** Writes all properties to the passed property set of a page style object. */
+ void writePageSettingsProperties(
+ PropertySet& rPropSet,
+ const PageSettingsModel& rModel,
+ WorksheetType eSheetType );
+
+private:
+ struct HFHelperData
+ {
+ sal_Int32 mnLeftPropId;
+ sal_Int32 mnRightPropId;
+ sal_Int32 mnFirstPropId;
+ sal_Int32 mnHeight;
+ sal_Int32 mnBodyDist;
+ bool mbHasContent;
+ bool mbShareOddEven;
+ bool mbShareFirst;
+ bool mbDynamicHeight;
+
+ explicit HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId, sal_Int32 nFirstPropId );
+ };
+
+private:
+ void convertHeaderFooterData(
+ PropertySet& rPropSet,
+ HFHelperData& orHFData,
+ const OUString& rOddContent,
+ const OUString& rEvenContent,
+ const OUString& rFirstContent,
+ bool bUseEvenContent,
+ bool bUseFirstContent,
+ double fPageMargin,
+ double fContentMargin );
+
+ sal_Int32 writeHeaderFooter(
+ PropertySet& rPropSet,
+ sal_Int32 nPropId,
+ const OUString& rContent );
+
+private:
+ typedef ::std::unique_ptr< HeaderFooterParser > HeaderFooterParserPtr;
+ HeaderFooterParserPtr mxHFParser;
+ HFHelperData maHeaderData;
+ HFHelperData maFooterData;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/patterncache.hxx b/sc/source/filter/inc/patterncache.hxx
new file mode 100644
index 0000000000..25013a9301
--- /dev/null
+++ b/sc/source/filter/inc/patterncache.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+class ScPatternAttr;
+
+constexpr sal_Int32 nPatternCacheSize = 16;
+class ScPatternCache
+{
+ struct Entry
+ {
+ sal_Int32 nXfId;
+ sal_Int32 nNumFmtId;
+ ScPatternAttr* pPattern;
+
+ Entry();
+ };
+
+ Entry maEntries[nPatternCacheSize];
+ sal_Int32 nNextPos;
+
+public:
+ ScPatternCache();
+
+ ScPatternAttr* query(sal_Int32 nXfId, sal_Int32 nNumFmtId) const;
+ void add(sal_Int32 nXfId, sal_Int32 nNumFmtId, ScPatternAttr* pPattern);
+};
diff --git a/sc/source/filter/inc/pivotcachebuffer.hxx b/sc/source/filter/inc/pivotcachebuffer.hxx
new file mode 100644
index 0000000000..e255c79eae
--- /dev/null
+++ b/sc/source/filter/inc/pivotcachebuffer.hxx
@@ -0,0 +1,458 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/util/DateTime.hpp>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace com::sun::star {
+ namespace sheet { class XDataPilotField; }
+}
+
+namespace oox::core { class Relations; }
+
+class ScDPSaveDimension;
+class ScDPObject;
+class DateTime;
+
+namespace oox::xls {
+
+class WorksheetHelper;
+
+typedef ::std::pair< sal_Int32, OUString > IdCaptionPair;
+typedef ::std::vector< IdCaptionPair > IdCaptionPairList;
+
+class PivotCacheItem
+{
+public:
+ explicit PivotCacheItem();
+
+ /** Reads the string value from a pivot cache item. */
+ void readString( const AttributeList& rAttribs );
+ /** Reads the double value from a pivot cache item. */
+ void readNumeric( const AttributeList& rAttribs );
+ /** Reads the date/time value from a pivot cache item. */
+ void readDate( const AttributeList& rAttribs );
+ /** Reads the boolean value from a pivot cache item. */
+ void readBool( const AttributeList& rAttribs );
+ /** Reads the error code value from a pivot cache item. */
+ void readError( const AttributeList& rAttribs );
+ /** Reads the index of a shared item. */
+ void readIndex( const AttributeList& rAttribs );
+
+ /** Reads the string value from a pivot cache item. */
+ void readString( SequenceInputStream& rStrm );
+ /** Reads the double value from a pivot cache item. */
+ void readDouble( SequenceInputStream& rStrm );
+ /** Reads the date/time value from a pivot cache item. */
+ void readDate( SequenceInputStream& rStrm );
+ /** Reads the boolean value from a pivot cache item. */
+ void readBool( SequenceInputStream& rStrm );
+ /** Reads the error code value from a pivot cache item. */
+ void readError(SequenceInputStream& rStrm, const UnitConverter& rUnitConverter);
+ /** Reads the index of a shared item. */
+ void readIndex( SequenceInputStream& rStrm );
+
+ /** Returns the type of the item. */
+ sal_Int32 getType() const { return mnType; }
+ /** Returns the value of the item. */
+ const css::uno::Any& getValue() const { return maValue; }
+ /** Returns the string representation of the item. */
+ OUString getName() const;
+
+ /** Returns the string representation of the item, using the actual formatting. */
+ OUString getFormattedName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const DateTime& rNullDate) const;
+ /** Returns true if the item is unused. */
+ bool isUnused() const { return mbUnused; }
+
+private:
+friend class PivotCacheItemList;
+ // #FIXME hack Sets the value of this item to the given string ( and overwrites type if necessary
+ void setStringValue( const OUString& sName );
+ css::uno::Any maValue; /// Value of the item.
+ sal_Int32 mnType; /// Value type (OOXML token identifier).
+ bool mbUnused;
+};
+
+class PivotCacheItemList : public WorkbookHelper
+{
+public:
+ explicit PivotCacheItemList( const WorkbookHelper& rHelper );
+
+ /** Imports the item from the passed attribute list. */
+ void importItem( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports the item from the passed stream and record. */
+ void importItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+ /** Returns true, if this item list is empty. */
+ bool empty() const { return maItems.empty(); }
+ /** Returns the size of the item list. */
+ size_t size() const { return maItems.size(); }
+
+ /** Returns the specified item. */
+ const PivotCacheItem* getCacheItem( sal_Int32 nItemIdx ) const;
+ /** Returns the names of all items. */
+ void getCacheItemNames( ::std::vector< OUString >& orItemNames ) const;
+ void applyItemCaptions( const IdCaptionPairList& vCaptions );
+
+private:
+ /** Creates and returns a new item at the end of the items list. */
+ PivotCacheItem& createItem();
+ /** Imports an array of items from the PCITEM_ARRAY record */
+ void importArray( SequenceInputStream& rStrm );
+
+private:
+ std::vector< PivotCacheItem > maItems; /// All items of this list.
+};
+
+struct PCFieldModel
+{
+ OUString maName; /// Fixed name of the cache field.
+ OUString maCaption; /// Caption of the cache field.
+ OUString maPropertyName; /// OLAP property name.
+ OUString maFormula; /// Formula of a calculated field.
+ sal_Int32 mnNumFmtId; /// Number format for all items.
+ sal_Int32 mnSqlType; /// Data type from ODBC data source.
+ sal_Int32 mnHierarchy; /// Hierarchy this field is part of.
+ sal_Int32 mnLevel; /// Hierarchy level this field is part of.
+ sal_Int32 mnMappingCount; /// Number of property mappings.
+ bool mbDatabaseField; /// True = field from source data; false = calculated field.
+ bool mbServerField; /// True = ODBC server-based page field.
+ bool mbUniqueList; /// True = list of unique ODBC items exists.
+ bool mbMemberPropField; /// True = contains OLAP member properties.
+
+ explicit PCFieldModel();
+};
+
+struct PCSharedItemsModel
+{
+ bool mbHasSemiMixed; /// True = has (blank|string|bool|error) item(s), maybe other types.
+ bool mbHasNonDate; /// True = has non-date item(s), maybe date items.
+ bool mbHasDate; /// True = has date item(s), maybe other types.
+ bool mbHasString; /// True = has (string|bool|error) item(s), maybe other types.
+ bool mbHasBlank; /// True = has blank item(s), maybe other types.
+ bool mbHasMixed; /// True = has [(string|bool|error) and (number|date)] or (number and date).
+ bool mbIsNumeric; /// True = has numeric item(s), maybe other types except date.
+ bool mbIsInteger; /// True = has numeric item(s) with only integers, maybe other types except date.
+ bool mbHasLongText; /// True = contains strings with >255 characters.
+
+ explicit PCSharedItemsModel();
+};
+
+struct PCFieldGroupModel
+{
+ css::util::DateTime maStartDate; /// Manual or calculated start date for range grouping.
+ css::util::DateTime maEndDate; /// Manual or calculated end date for range grouping.
+ double mfStartValue; /// Manual or calculated start value for range grouping.
+ double mfEndValue; /// Manual or calculated end value for range grouping.
+ double mfInterval; /// Interval for numeric range grouping.
+ sal_Int32 mnParentField; /// Index of cache field that contains item groups based on this field.
+ sal_Int32 mnBaseField; /// Index of cache field this grouped field is based on.
+ sal_Int32 mnGroupBy; /// Type of numeric or date range grouping.
+ bool mbRangeGroup; /// True = items are grouped by numeric ranges or date ranges.
+ bool mbDateGroup; /// True = items are grouped by date ranges or by item names.
+ bool mbAutoStart; /// True = start value for range groups is calculated from source data.
+ bool mbAutoEnd; /// True = end value for range groups is calculated from source data.
+ OUString msFinalGroupName ; /// Finalized group name of this field used in internal pivot table collection.
+
+
+ explicit PCFieldGroupModel();
+
+ /** Sets the group-by value for BIFF import. */
+ void setBiffGroupBy( sal_uInt8 nGroupBy );
+};
+
+/** Helper struct for mapping original item names from/to group item names. */
+struct PivotCacheGroupItem
+{
+ OUString maOrigName;
+ OUString maGroupName;
+
+ explicit PivotCacheGroupItem( const OUString& rItemName ) :
+ maOrigName( rItemName ), maGroupName( rItemName ) {}
+};
+
+typedef ::std::vector< PivotCacheGroupItem > PivotCacheGroupItemVector;
+
+class PivotCacheField : public WorkbookHelper
+{
+public:
+ explicit PivotCacheField( const WorkbookHelper& rHelper, bool bIsDatabaseField );
+
+ /** Imports pivot cache field settings from the cacheField element. */
+ void importCacheField( const AttributeList& rAttribs );
+ /** Imports shared items settings from the sharedItems element. */
+ void importSharedItems( const AttributeList& rAttribs );
+ /** Imports a shared item from the passed element. */
+ void importSharedItem( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports grouping settings from the fieldGroup element. */
+ void importFieldGroup( const AttributeList& rAttribs );
+ /** Imports numeric grouping settings from the rangePr element. */
+ void importRangePr( const AttributeList& rAttribs );
+ /** Imports an item of the mapping between group items and base items from the passed element. */
+ void importDiscretePrItem( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports a group item from the passed element. */
+ void importGroupItem( sal_Int32 nElement, const AttributeList& rAttribs );
+
+ /** Imports pivot cache field settings from the PCDFIELD record. */
+ void importPCDField( SequenceInputStream& rStrm );
+ /** Imports shared items settings from the PCDFSHAREDITEMS record. */
+ void importPCDFSharedItems( SequenceInputStream& rStrm );
+ /** Imports one or more shared items from the passed record. */
+ void importPCDFSharedItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+ /** Imports grouping settings from the PCDFIELDGROUP record. */
+ void importPCDFieldGroup( SequenceInputStream& rStrm );
+ /** Imports numeric grouping settings from the PCDFRANGEPR record. */
+ void importPCDFRangePr( SequenceInputStream& rStrm );
+ /** Imports an item of the mapping between group items and base items from the passed record. */
+ void importPCDFDiscretePrItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+ /** Imports one or more group items from the passed record. */
+ void importPCDFGroupItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+ /** Apply user Captions to imported group data */
+ void applyItemCaptions( const IdCaptionPairList& vCaptions );
+
+ /** Returns true, if the field is based on source data, or false if it is grouped or calculated. */
+ bool isDatabaseField() const { return maFieldModel.mbDatabaseField; }
+
+ /** Returns true, if the field contains a list of shared items. */
+ bool hasSharedItems() const { return !maSharedItems.empty(); }
+ /** Returns true, if the field contains a list of grouping items. */
+ bool hasGroupItems() const { return !maGroupItems.empty(); }
+ /** Returns true, if the field has inplace numeric grouping settings. */
+ bool hasNumericGrouping() const { return maFieldGroupModel.mbRangeGroup && !maFieldGroupModel.mbDateGroup; }
+ /** Returns true, if the field has inplace date grouping settings. */
+ bool hasDateGrouping() const { return maFieldGroupModel.mbRangeGroup && maFieldGroupModel.mbDateGroup; }
+ /** Returns true, if the field has a parent group field that groups the items of this field. */
+ bool hasParentGrouping() const { return maFieldGroupModel.mnParentField >= 0; }
+
+ /** Returns the name of the cache field. */
+ const OUString& getName() const { return maFieldModel.maName; }
+ /** Returns the index of the parent group field that groups the items of this field. */
+ sal_Int32 getParentGroupField() const { return maFieldGroupModel.mnParentField; }
+ /** Returns the index of the base field grouping is based on. */
+ sal_Int32 getGroupBaseField() const { return maFieldGroupModel.mnBaseField; }
+ /** Returns the finalized group name of this field. */
+ const OUString& getFinalGroupName() const { return maFieldGroupModel.msFinalGroupName; }
+ /** Set the finalized group name of this field. */
+ void setFinalGroupName(const OUString& rFinalGroupName) { maFieldGroupModel.msFinalGroupName = rFinalGroupName; }
+
+ /** Returns the shared or group item with the specified index. */
+ const PivotCacheItem* getCacheItem( sal_Int32 nItemIdx ) const;
+ /** Returns the names of all shared or group items. */
+ void getCacheItemNames( ::std::vector< OUString >& orItemNames ) const;
+ /** Returns shared or group items. */
+ const PivotCacheItemList& getCacheItems() const;
+
+ /** Creates inplace numeric grouping settings. */
+ void convertNumericGrouping(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxDPField ) const;
+ /** Creates inplace date grouping settings or a new date group field. */
+ OUString createDateGroupField(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField ) const;
+ /** Creates a new grouped DataPilot field and returns its name. */
+ OUString createParentGroupField(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField,
+ PivotCacheGroupItemVector& orItemNames ) const;
+
+ /** Writes the title of the field into the passed sheet at the passed address. */
+ void writeSourceHeaderCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow ) const;
+ /** Writes a source field item value into the passed sheet. */
+ void writeSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow,
+ const PivotCacheItem& rItem ) const;
+
+ /** Reads an item from the PCRECORD record and writes it to the passed sheet. */
+ void importPCRecordItem( SequenceInputStream& rStrm,
+ const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const;
+
+private:
+ /** Tries to write the passed value to the passed sheet position. */
+ static void writeItemToSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem );
+ /** Tries to write the value of a shared item to the passed sheet position. */
+ void writeSharedItemToSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nItemIdx ) const;
+
+private:
+ typedef ::std::vector< sal_Int32 > IndexVector;
+
+ PivotCacheItemList maSharedItems; /// All shared items of this field.
+ PivotCacheItemList maGroupItems; /// All group items of this field.
+ IndexVector maDiscreteItems; /// Mapping between group and base items.
+ PCFieldModel maFieldModel; /// Settings for this cache field.
+ PCSharedItemsModel maSharedItemsModel; /// Settings for shared items.
+ PCFieldGroupModel maFieldGroupModel; /// Settings for item grouping.
+};
+
+struct PCDefinitionModel
+{
+ OUString maRelId; /// Relation identifier for cache records fragment.
+ OUString maRefreshedBy; /// Name of user who last refreshed the cache.
+ double mfRefreshedDate; /// Date/time of last refresh.
+ sal_Int32 mnRecords; /// Number of data records in the cache.
+ sal_Int32 mnMissItemsLimit; /// Limit for discarding unused items.
+ bool mbInvalid; /// True = cache needs refresh.
+ bool mbSaveData; /// True = cached item values are present.
+ bool mbRefreshOnLoad; /// True = try to refresh cache on load.
+ bool mbOptimizeMemory; /// True = application may optimize memory usage.
+ bool mbEnableRefresh; /// True = refreshing cache is enabled in UI.
+ bool mbBackgroundQuery; /// True = application queries data asynchronously.
+ bool mbUpgradeOnRefresh; /// True = application may upgrade cache version.
+ bool mbTupleCache; /// True = cache stores OLAP functions.
+ bool mbSupportSubquery; /// True = data source supports subqueries.
+ bool mbSupportDrill; /// True = data source supports drilldown.
+
+ explicit PCDefinitionModel();
+};
+
+struct PCSourceModel
+{
+ sal_Int32 mnSourceType; /// Type of the source data (sheet, consolidation, scenario, external).
+ sal_Int32 mnConnectionId; /// Connection identifier for external data source.
+
+ explicit PCSourceModel();
+};
+
+struct PCWorksheetSourceModel
+{
+ OUString maRelId; /// Relation identifier for an external document URL.
+ OUString maSheet; /// Sheet name for cell range or sheet-local defined names.
+ OUString maDefName; /// Defined name containing a cell range if present.
+ ScRange maRange; /// Source cell range of the data.
+
+ explicit PCWorksheetSourceModel();
+};
+
+class PivotCache : public WorkbookHelper
+{
+public:
+ explicit PivotCache( const WorkbookHelper& rHelper );
+
+ /** Reads pivot cache global settings from the pivotCacheDefinition element. */
+ void importPivotCacheDefinition( const AttributeList& rAttribs );
+ /** Reads cache source settings from the cacheSource element. */
+ void importCacheSource( const AttributeList& rAttribs );
+ /** Reads sheet source settings from the worksheetSource element. */
+ void importWorksheetSource( const AttributeList& rAttribs, const ::oox::core::Relations& rRelations );
+
+ /** Reads pivot cache global settings from the PCDEFINITION record. */
+ void importPCDefinition( SequenceInputStream& rStrm );
+ /** Reads cache source settings from the PCDSOURCE record. */
+ void importPCDSource( SequenceInputStream& rStrm );
+ /** Reads sheet source settings from the PCDSHEETSOURCE record. */
+ void importPCDSheetSource( SequenceInputStream& rStrm, const ::oox::core::Relations& rRelations );
+
+ /** Creates and returns a new pivot cache field. */
+ PivotCacheField& createCacheField();
+ /** Checks validity of source data and creates a dummy data sheet for external sheet sources. */
+ void finalizeImport();
+
+ /** Returns true, if the pivot cache is based on a valid data source, so
+ that pivot tables can be created based on this pivot cache. */
+ bool isValidDataSource() const { return mbValidSource; }
+ /** Returns true, if the pivot cache is based on a dummy sheet created in finalizeImport. */
+ bool isBasedOnDummySheet() const { return mbDummySheet; }
+ /** Returns the internal cell range the cache is based on. */
+ const ScRange&
+ getSourceRange() const { return maSheetSrcModel.maRange; }
+ /** Returns the relation identifier of the pivot cache records fragment. */
+ const OUString& getRecordsRelId() const { return maDefModel.maRelId; }
+
+ /** Returns the cache field with the specified index. */
+ PivotCacheField* getCacheField( sal_Int32 nFieldIdx );
+ const PivotCacheField* getCacheField( sal_Int32 nFieldIdx ) const;
+ /** Returns the source column index of the field with the passed index. */
+ sal_Int32 getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const;
+
+ /** Writes the titles of all source fields into the passed sheet. */
+ void writeSourceHeaderCells( const WorksheetHelper& rSheetHelper ) const;
+ /** Writes a source field item value into the passed sheet. */
+ void writeSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nColIdx, sal_Int32 nRowIdx,
+ const PivotCacheItem& rItem ) const;
+
+ /** Reads a PCRECORD record and writes all item values to the passed sheet. */
+ void importPCRecord( SequenceInputStream& rStrm,
+ const WorksheetHelper& rSheetHelper, sal_Int32 nRowIdx ) const;
+
+private:
+
+ /** Finalizes the pivot cache if it is based on internal sheet data. */
+ void finalizeInternalSheetSource();
+ /** Finalizes the pivot cache if it is based on sheet data of an external spreadsheet document. */
+ void finalizeExternalSheetSource();
+ /** Creates a dummy sheet that will be filled with the pivot cache data. */
+ void prepareSourceDataSheet();
+ /** Checks, if the row index has changed since last call, and initializes the sheet data buffer. */
+ void updateSourceDataRow( sal_Int32 nRow ) const;
+
+private:
+ typedef RefVector< PivotCacheField > PivotCacheFieldVector;
+ typedef ::std::vector< sal_Int32 > IndexVector;
+
+ PivotCacheFieldVector maFields; /// All pivot cache fields.
+ PivotCacheFieldVector maDatabaseFields; /// All cache fields that are based on source data.
+ IndexVector maDatabaseIndexes; /// Database field index for all fields.
+ PCDefinitionModel maDefModel; /// Global pivot cache settings.
+ PCSourceModel maSourceModel; /// Pivot cache source settings.
+ PCWorksheetSourceModel maSheetSrcModel; /// Sheet source data if cache type is sheet.
+ ValueRangeSet maColSpans; /// Column spans used by SheetDataBuffer for optimized cell import.
+ OUString maTargetUrl; /// URL of an external source document.
+ mutable sal_Int32 mnCurrRow; /// Current row index in dummy sheet.
+ bool mbValidSource; /// True = pivot cache is based on supported data source.
+ bool mbDummySheet; /// True = pivot cache is based on a dummy sheet.
+};
+
+class PivotCacheBuffer : public WorkbookHelper
+{
+public:
+ explicit PivotCacheBuffer( const WorkbookHelper& rHelper );
+
+ /** Registers a pivot cache definition fragment. The fragment will be loaded on demand (OOXML/BIFF12 only). */
+ void registerPivotCacheFragment( sal_Int32 nCacheId, const OUString& rFragmentPath );
+
+ /** Imports and stores a pivot cache definition fragment on first call,
+ returns the imported cache on subsequent calls with the same identifier. */
+ PivotCache* importPivotCacheFragment( sal_Int32 nCacheId );
+
+private:
+ /** Creates and returns a new pivot cache object with the passed identifier. */
+ PivotCache& createPivotCache( sal_Int32 nCacheId );
+
+private:
+ typedef ::std::map< sal_Int32, OUString > FragmentPathMap;
+ typedef RefMap< sal_Int32, PivotCache > PivotCacheMap;
+
+ FragmentPathMap maFragmentPaths;
+ PivotCacheMap maCaches;
+ std::vector< sal_Int32 > maCacheIds;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivotcachefragment.hxx b/sc/source/filter/inc/pivotcachefragment.hxx
new file mode 100644
index 0000000000..38f1b77b15
--- /dev/null
+++ b/sc/source/filter/inc/pivotcachefragment.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 "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class PivotCache;
+class PivotCacheField;
+
+class PivotCacheFieldContext : public WorkbookContextBase
+{
+public:
+ explicit PivotCacheFieldContext(
+ WorkbookFragmentBase& rFragment,
+ PivotCacheField& rCacheField );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ PivotCacheField& mrCacheField;
+};
+
+class PivotCacheDefinitionFragment : public WorkbookFragmentBase
+{
+public:
+ explicit PivotCacheDefinitionFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath,
+ PivotCache& rPivotCache );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+
+private:
+ PivotCache& mrPivotCache;
+};
+
+class PivotCacheRecordsFragment : public WorksheetFragmentBase
+{
+public:
+ explicit PivotCacheRecordsFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath,
+ const PivotCache& rPivotCache );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ void startCacheRecord();
+ void importPCRecord( SequenceInputStream& rStrm );
+ void importPCRecordItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+private:
+ const PivotCache& mrPivotCache;
+ sal_Int32 mnColIdx; /// Relative column index in source data.
+ sal_Int32 mnRowIdx; /// Relative row index in source data.
+ bool mbInRecord;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivottablebuffer.hxx b/sc/source/filter/inc/pivottablebuffer.hxx
new file mode 100644
index 0000000000..a4ba60022d
--- /dev/null
+++ b/sc/source/filter/inc/pivottablebuffer.hxx
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "pivotcachebuffer.hxx"
+#include "stylesbuffer.hxx"
+#include <rtl/ref.hxx>
+
+namespace com::sun::star {
+ namespace sheet { class XDataPilotDescriptor; }
+ namespace sheet { class XDataPilotField; }
+}
+
+class ScDPObject;
+class ScDataPilotDescriptorBase;
+
+namespace oox::xls {
+
+class PivotTable;
+
+struct PTFieldItemModel
+{
+ sal_Int32 mnCacheItem; /// Index to shared item in pivot cache.
+ sal_Int32 mnType; /// Type of the item.
+ OUString msCaption; /// User caption of the item
+ bool mbShowDetails; /// True = show item details (items of child fields).
+ bool mbHidden; /// True = item is hidden.
+
+ explicit PTFieldItemModel();
+
+ /** Sets item type for BIFF import. */
+ void setBiffType( sal_uInt16 nType );
+};
+
+struct PTFieldModel
+{
+ sal_Int32 mnAxis; /// Axis this field is assigned to (none, row, column, page).
+ sal_Int32 mnNumFmtId; /// Number format for field items.
+ sal_Int32 mnAutoShowItems; /// Number of items (or percent/sum) to be shown in auto show filter.
+ sal_Int32 mnAutoShowRankBy; /// Index of the data field auto show filter is based on.
+ sal_Int32 mnSortType; /// Autosorting type.
+ sal_Int32 mnSortRefField; /// Reference field for autosorting.
+ sal_Int32 mnSortRefItem; /// Item in reference field for autosorting.
+ bool mbDataField; /// True = field appears in data area.
+ bool mbDefaultSubtotal; /// True = show default subtotals.
+ bool mbSumSubtotal; /// True = show sum subtotals.
+ bool mbCountASubtotal; /// True = show count all subtotals.
+ bool mbAverageSubtotal; /// True = show average subtotals.
+ bool mbMaxSubtotal; /// True = show maximum subtotals.
+ bool mbMinSubtotal; /// True = show minimum subtotals.
+ bool mbProductSubtotal; /// True = show product subtotals.
+ bool mbCountSubtotal; /// True = show count numbers subtotals.
+ bool mbStdDevSubtotal; /// True = show standard deviation subtotals.
+ bool mbStdDevPSubtotal; /// True = show standard deviation of population subtotals.
+ bool mbVarSubtotal; /// True = show variance subtotals.
+ bool mbVarPSubtotal; /// True = show variance of population subtotals.
+ bool mbShowAll; /// True = show items without data.
+ bool mbOutline; /// True = show in outline view, false = show in tabular view.
+ bool mbSubtotalTop; /// True = show subtotals on top of items in outline or compact mode.
+ bool mbCompact; /// True = show in compact view, false = show in either outline or tabular view.
+ bool mbInsertBlankRow; /// True = insert blank rows after items.
+ bool mbInsertPageBreak; /// True = insert page breaks after items.
+ bool mbAutoShow; /// True = auto show (top 10) filter enabled.
+ bool mbTopAutoShow; /// True = auto show filter shows top entries, false = bottom.
+ bool mbMultiPageItems; /// True = multiple items selectable in page dimension.
+
+ explicit PTFieldModel();
+
+ /** Sets axis type for BIFF import. */
+ void setBiffAxis( sal_uInt8 nAxisFlags );
+};
+
+struct PTPageFieldModel
+{
+ OUString maName; /// Unique name of the page field.
+ sal_Int32 mnField; /// Base pivot field.
+ sal_Int32 mnItem; /// Index of field item that is shown by the page field.
+
+ explicit PTPageFieldModel();
+};
+
+struct PTDataFieldModel
+{
+ OUString maName; /// Name of the data field.
+ sal_Int32 mnField; /// Base pivot field.
+ sal_Int32 mnSubtotal; /// Subtotal aggregation function.
+ sal_Int32 mnShowDataAs; /// Show data as, based on another field.
+ sal_Int32 mnBaseField; /// Base field for 'show data as'.
+ sal_Int32 mnBaseItem; /// Base item for 'show data as'.
+ sal_Int32 mnNumFmtId; /// Number format for the result.
+
+ explicit PTDataFieldModel();
+
+ /** Sets the subtotal aggregation function for BIFF import. */
+ void setBiffSubtotal( sal_Int32 nSubtotal );
+ /** Sets the 'show data as' type for BIFF import. */
+ void setBiffShowDataAs( sal_Int32 nShowDataAs );
+};
+
+class PivotTableField : public WorkbookHelper
+{
+public:
+ explicit PivotTableField( PivotTable& rPivotTable, sal_Int32 nFieldIndex );
+
+ /** Imports pivot field settings from the pivotField element. */
+ void importPivotField( const AttributeList& rAttribs );
+ /** Imports settings of an item in this pivot field from the item element. */
+ void importItem( const AttributeList& rAttribs );
+ /** Imports pivot field reference settings from the reference element. */
+ void importReference( const AttributeList& rAttribs );
+ /** Imports pivot field item reference settings from the x element. */
+ void importReferenceItem( const AttributeList& rAttribs );
+
+ /** Imports pivot field settings from the PTFIELD record. */
+ void importPTField( SequenceInputStream& rStrm );
+ /** Imports settings of an item in this pivot field from the PTFITEM record. */
+ void importPTFItem( SequenceInputStream& rStrm );
+ /** Imports pivot field reference settings from the PTREFERENCE record. */
+ void importPTReference( SequenceInputStream& rStrm );
+ /** Imports pivot field item reference settings from the PTREFERENCEITEM record. */
+ void importPTReferenceItem( SequenceInputStream& rStrm );
+
+ /** Finalizes the field after import, creates grouping and other settings. */
+ void finalizeImport(
+ const css::uno::Reference< css::sheet::XDataPilotDescriptor >& rxDPDesc );
+ /** Finalizes the grouped date field after import. */
+ void finalizeDateGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ sal_Int32 nBaseFieldIdx );
+ /** Finalizes the grouped field after import. */
+ void finalizeParentGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField,
+ PivotCacheGroupItemVector& orItemNames );
+ void finalizeImportBasedOnCache(
+ const css::uno::Reference< css::sheet::XDataPilotDescriptor >& rxDPDesc);
+
+ /** Returns the name of the DataPilot field in the fields collection. */
+ const OUString& getDPFieldName() const { return maDPFieldName; }
+
+ /** Converts dimension and other settings for a row field. */
+ void convertRowField();
+ /** Converts dimension and other settings for a column field. */
+ void convertColField();
+ /** Converts dimension and other settings for a hidden field. */
+ void convertHiddenField();
+ /** Converts dimension and other settings for a page field */
+ void convertPageField( const PTPageFieldModel& rPageField );
+ /** Converts dimension and other settings for a data field. */
+ void convertDataField( const PTDataFieldModel& rDataField );
+
+private:
+ /** Converts dimension and other settings for row, column, page, or hidden fields. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ convertRowColPageField( sal_Int32 nAxis );
+
+private:
+ typedef ::std::vector< PTFieldItemModel > ItemModelVector;
+
+ PivotTable& mrPivotTable; /// The parent pivot table object.
+ ItemModelVector maItems; /// All items of this field.
+ PTFieldModel maModel; /// Pivot field settings.
+ OUString maDPFieldName; /// Name of the field in DataPilot field collection.
+ sal_Int32 mnFieldIndex; /// Zero-based index of this field.
+};
+
+struct PTFilterModel
+{
+ OUString maName; /// Name of the field filter.
+ OUString maDescription; /// Description of the field filter.
+ OUString maStrValue1; /// First string value for label filter.
+ OUString maStrValue2; /// Second string value for label filter.
+ double mfValue; /// Number of items or percent or sum to be shown.
+ sal_Int32 mnField; /// Base pivot field.
+ sal_Int32 mnMemPropField; /// Member property field.
+ sal_Int32 mnType; /// Filter type.
+ sal_Int32 mnEvalOrder; /// Evaluation order index.
+ sal_Int32 mnId; /// Unique identifier.
+ sal_Int32 mnMeasureField; /// Data field for filter calculation.
+ sal_Int32 mnMeasureHier; /// Hierarchy for filter calculation.
+ bool mbTopFilter; /// True = filter shows top entries, false = bottom.
+
+ explicit PTFilterModel();
+};
+
+class PivotTableFilter : public WorkbookHelper
+{
+public:
+ explicit PivotTableFilter( const PivotTable& rPivotTable );
+
+ /** Reads the settings of a field filter from the filter element. */
+ void importFilter( const AttributeList& rAttribs );
+ /** Reads additional settings of a field filter from the top10 element. */
+ void importTop10( const AttributeList& rAttribs );
+
+ /** Reads the settings of a field filter from the PTFILTER record. */
+ void importPTFilter( SequenceInputStream& rStrm );
+ /** Reads additional settings of a field filter from the TOP10FILTER record. */
+ void importTop10Filter( SequenceInputStream& rStrm );
+
+ /** Applies the filter to the associated pivot table field if possible. */
+ void finalizeImport();
+
+private:
+ const PivotTable& mrPivotTable;
+ PTFilterModel maModel;
+};
+
+struct PTDefinitionModel : public AutoFormatModel
+{
+ OUString maName;
+ OUString maDataCaption;
+ OUString maGrandTotalCaption;
+ OUString maRowHeaderCaption;
+ OUString maColHeaderCaption;
+ OUString maErrorCaption;
+ OUString maMissingCaption;
+ OUString maPageStyle;
+ OUString maPivotTableStyle;
+ OUString maVacatedStyle;
+ OUString maTag;
+ sal_Int32 mnCacheId;
+ sal_Int32 mnDataPosition;
+ sal_Int32 mnPageWrap;
+ sal_Int32 mnIndent;
+ sal_Int32 mnChartFormat;
+ bool mbDataOnRows;
+ bool mbShowError;
+ bool mbShowMissing;
+ bool mbShowItems;
+ bool mbDisableFieldList;
+ bool mbShowCalcMembers;
+ bool mbVisualTotals;
+ bool mbShowDrill;
+ bool mbPrintDrill;
+ bool mbEnableDrill;
+ bool mbPreserveFormatting;
+ bool mbUseAutoFormat;
+ bool mbPageOverThenDown;
+ bool mbSubtotalHiddenItems;
+ bool mbRowGrandTotals;
+ bool mbColGrandTotals;
+ bool mbFieldPrintTitles;
+ bool mbItemPrintTitles;
+ bool mbMergeItem;
+ bool mbShowEmptyRow;
+ bool mbShowEmptyCol;
+ bool mbShowHeaders;
+ bool mbFieldListSortAsc;
+ bool mbCustomListSort;
+
+ explicit PTDefinitionModel();
+};
+
+struct PTLocationModel
+{
+ ScRange maRange; /// Target cell range for the pivot table.
+ sal_Int32 mnFirstHeaderRow; /// First row of header cells (relative in pivot table).
+ sal_Int32 mnFirstDataRow; /// First row of data cells (relative in pivot table).
+ sal_Int32 mnFirstDataCol; /// First column of data cells (relative in pivot table).
+ sal_Int32 mnRowPageCount; /// Number of rows in page filter area.
+ sal_Int32 mnColPageCount; /// Number of columns in page filter area.
+
+ explicit PTLocationModel();
+};
+
+class PivotTable : public WorkbookHelper
+{
+public:
+ explicit PivotTable( const WorkbookHelper& rHelper );
+
+ /** Reads global pivot table settings from the pivotTableDefinition element. */
+ void importPivotTableDefinition( const AttributeList& rAttribs );
+ /** Reads the location of the pivot table from the location element. */
+ void importLocation( const AttributeList& rAttribs, sal_Int16 nSheet );
+ /** Reads the index of a field located in the row dimension. */
+ void importRowField( const AttributeList& rAttribs );
+ /** Reads the index of a field located in the column dimension. */
+ void importColField( const AttributeList& rAttribs );
+ /** Reads the settings of a field located in the page dimension from the pageField element. */
+ void importPageField( const AttributeList& rAttribs );
+ /** Reads the settings of a field located in the data dimension from the dataField element. */
+ void importDataField( const AttributeList& rAttribs );
+ /** Puts the attributes to the named grab bag value. */
+ void putToInteropGrabBag(const OUString& sName, const AttributeList& rAttribs);
+
+ /** Reads global pivot table settings from the PTDEFINITION record. */
+ void importPTDefinition( SequenceInputStream& rStrm );
+ /** Reads the location of the pivot table from the PTLOCATION record. */
+ void importPTLocation( SequenceInputStream& rStrm, sal_Int16 nSheet );
+ /** Reads the indexes of all fields located in the row dimension from a PTROWFIELDS record. */
+ void importPTRowFields( SequenceInputStream& rStrm );
+ /** Reads the indexes of all fields located in the column dimension from a PTCOLFIELDS record. */
+ void importPTColFields( SequenceInputStream& rStrm );
+ /** Reads the settings of a field located in the page dimension from the PTPAGEFIELD record. */
+ void importPTPageField( SequenceInputStream& rStrm );
+ /** Reads the settings of a field located in the data dimension from the PTDATAFIELD record. */
+ void importPTDataField( SequenceInputStream& rStrm );
+
+ /** Creates and returns a new pivot table field. */
+ PivotTableField& createTableField();
+ /** Creates and returns a new pivot table filter. */
+ PivotTableFilter& createTableFilter();
+ /** Inserts the pivot table into the sheet. */
+ void finalizeImport();
+ /** Finalizes all fields, finds field names and creates grouping fields. */
+ void finalizeFieldsImport();
+ /** Creates all date group fields for the specified cache field after import. */
+ void finalizeDateGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ sal_Int32 nBaseFieldIdx );
+ /** Creates all grouped fields for the specified cache field after import. */
+ void finalizeParentGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField,
+ PivotCacheGroupItemVector& orItemNames );
+
+ /** Returns the associated data pilot field for the specified pivot table field. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ getDataPilotField( const OUString& rFieldName ) const;
+ /** Returns the associated data pilot field for the specified pivot table field. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ getDataPilotField( sal_Int32 nFieldIdx ) const;
+ /** Returns the data layout field used to store all data fields in row/col dimension. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ getDataLayoutField() const;
+
+ /** Returns the cache field with the specified index. */
+ PivotCacheField* getCacheField( sal_Int32 nFieldIdx );
+ const PivotCacheField* getCacheField( sal_Int32 nFieldIdx ) const;
+ /** Returns the base cache field of the data field item with the specified index. */
+ const PivotCacheField* getCacheFieldOfDataField( sal_Int32 nDataItemIdx ) const;
+ /** Returns the source column index of the pivot field with the passed index, or -1. */
+ sal_Int32 getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const;
+
+ ScDPObject* getDPObject() { return mpDPObject; }
+
+private:
+ typedef RefVector< PivotTableField > PivotTableFieldVector;
+ typedef RefVector< PivotTableFilter > PivotTableFilterVector;
+ typedef ::std::vector< sal_Int32 > IndexVector;
+ typedef ::std::vector< PTPageFieldModel > PageFieldVector;
+ typedef ::std::vector< PTDataFieldModel > DataFieldVector;
+
+private:
+ /** Returns a pivot table field by its index. */
+ PivotTableField* getTableField( sal_Int32 nFieldIdx );
+
+ /** Reads a field index for the row or column dimension. */
+ static void importField( IndexVector& orFields, const AttributeList& rAttribs );
+ /** Reads an array of field indexes for the row or column dimension. */
+ static void importFields( IndexVector& orFields, SequenceInputStream& rStrm );
+
+private:
+ ScDPObject* mpDPObject;
+ PivotTableFieldVector maFields; /// All pivot table fields.
+ PivotTableField maDataField; /// Data layout field.
+ IndexVector maRowFields; /// Indexes to fields in row dimension.
+ IndexVector maColFields; /// Indexes to fields in column dimension.
+ PageFieldVector maPageFields; /// Settings for all fields in page dimension.
+ DataFieldVector maDataFields; /// Settings for all fields in data area.
+ PivotTableFilterVector maFilters; /// All field filters.
+ PTDefinitionModel maDefModel; /// Global pivot table settings.
+ PTLocationModel maLocationModel; /// Location settings of the pivot table.
+ PivotCache* mpPivotCache; /// The pivot cache this table is based on.
+ rtl::Reference< ScDataPilotDescriptorBase > // css::sheet::XDataPilotDescriptor
+ mxDPDescriptor; /// Descriptor of the DataPilot object.
+ std::map<OUString, css::uno::Any> maInteropGrabBag;
+
+};
+
+class PivotTableBuffer : public WorkbookHelper
+{
+public:
+ explicit PivotTableBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates and returns a new pivot table. */
+ PivotTable& createPivotTable();
+
+ /** Inserts all pivot tables into the sheet. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< PivotTable > PivotTableVector;
+ PivotTableVector maTables;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivottablefragment.hxx b/sc/source/filter/inc/pivottablefragment.hxx
new file mode 100644
index 0000000000..c6c82f6b7f
--- /dev/null
+++ b/sc/source/filter/inc/pivottablefragment.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 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "worksheethelper.hxx"
+
+namespace oox::xls {
+
+class PivotTable;
+class PivotTableField;
+class PivotTableFilter;
+
+class PivotTableFieldContext : public WorksheetContextBase
+{
+public:
+ explicit PivotTableFieldContext(
+ WorksheetFragmentBase& rFragment,
+ PivotTableField& rTableField );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ PivotTableField& mrTableField;
+};
+
+class PivotTableFilterContext : public WorksheetContextBase
+{
+public:
+ explicit PivotTableFilterContext(
+ WorksheetFragmentBase& rFragment,
+ PivotTableFilter& rTableFilter );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ PivotTableFilter& mrTableFilter;
+};
+
+class PivotTableFragment : public WorksheetFragmentBase
+{
+public:
+ explicit PivotTableFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ PivotTable& mrPivotTable;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/qpro.hxx b/sc/source/filter/inc/qpro.hxx
new file mode 100644
index 0000000000..eb11942e5f
--- /dev/null
+++ b/sc/source/filter/inc/qpro.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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <comphelper/errcode.hxx>
+#include <types.hxx>
+
+class ScDocument;
+class SvStream;
+class ScQProStyle;
+
+// Stream wrapper class
+class ScQProReader
+{
+ sal_uInt16 mnId;
+ sal_uInt16 mnLength;
+ sal_uInt32 mnOffset;
+ SvStream* mpStream;
+ bool mbEndOfFile;
+ const SCTAB mnMaxTab;
+
+public:
+ ScQProReader(SvStream* pStream);
+ ~ScQProReader();
+
+ bool recordsLeft();
+ void SetEof(bool bValue) { mbEndOfFile = bValue; }
+ bool nextRecord();
+ sal_uInt16 getId() const { return mnId; }
+ sal_uInt16 getLength() const { return mnLength; }
+ OUString readString(sal_uInt16 nLength);
+
+ ErrCode parse(ScDocument& rDoc);
+ ErrCode import(ScDocument& rDoc); //parse + CalcAfterLoad
+ ErrCode readSheet(SCTAB nTab, ScDocument& rDoc, ScQProStyle* pStyle);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/qproform.hxx b/sc/source/filter/inc/qproform.hxx
new file mode 100644
index 0000000000..bedb5b3111
--- /dev/null
+++ b/sc/source/filter/inc/qproform.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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include "formel.hxx"
+
+typedef OpCode DefTokenId;
+
+enum QPRO_FUNC_TYPE
+{
+ FT_Return,
+ FT_FuncFix0,
+ FT_FuncFix1,
+ FT_FuncFix2,
+ FT_FuncFix3,
+ FT_FuncFix4,
+ FT_FuncFix5,
+ FT_FuncFix6,
+ FT_FuncVar,
+ FT_DLL,
+ FT_Neg,
+ FT_Op,
+ FT_NotImpl,
+ FT_ConstFloat,
+ FT_Range,
+ FT_Braces,
+ FT_ConstInt,
+ FT_ConstString,
+ FT_NOP,
+ FT_Cref
+};
+
+class QProToSc : public ConverterBase
+{
+private:
+ TokenId mnAddToken;
+ SvStream& maIn;
+
+public:
+ static const size_t nBufSize = 256;
+ QProToSc(SvStream& aStr, svl::SharedStringPool& rSPool, const ScAddress& rRefPos);
+ ConvErr Convert(const ScDocument& rDoc, std::unique_ptr<ScTokenArray>& pArray);
+ void DoFunc(DefTokenId eOc, sal_uInt16 nArgs, const char* pExtString);
+ void ReadSRD(const ScDocument& rDoc, ScSingleRefData& rR, sal_Int8 nPage, sal_Int8 nCol,
+ sal_uInt16 rRel);
+ void IncToken(TokenId& aParam);
+ static DefTokenId IndexToToken(sal_uInt16 nToken);
+ static QPRO_FUNC_TYPE IndexToType(sal_uInt8 nToken);
+ static DefTokenId IndexToDLLId(sal_uInt16 nIndex);
+ static const char* getString(sal_uInt8 nIndex);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/qprostyle.hxx b/sc/source/filter/inc/qprostyle.hxx
new file mode 100644
index 0000000000..708b0ae1db
--- /dev/null
+++ b/sc/source/filter/inc/qprostyle.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 <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <types.hxx>
+
+class ScDocument;
+
+class ScQProStyle
+{
+ enum limits { maxsize = 256 };
+ sal_uInt8 maAlign[ maxsize ] = {};
+ sal_uInt8 maFont[ maxsize ] = {};
+ sal_uInt16 maFontRecord[ maxsize ] = {};
+ sal_uInt16 maFontHeight[ maxsize ] = {};
+ OUString maFontType[ maxsize ];
+
+ public:
+ ScQProStyle() = default;
+ void SetFormat( ScDocument *pDoc, sal_uInt8 nCol, sal_uInt16 nRow, SCTAB nTab, sal_uInt16 nStyle );
+ void setFontRecord(sal_uInt16 nIndex, sal_uInt16 nData, sal_uInt16 nPtSize)
+ {
+ if (nIndex < maxsize)
+ {
+ maFontRecord[ nIndex ] = nData;
+ maFontHeight[ nIndex ] = nPtSize;
+ }
+ }
+ void setFontType( sal_uInt16 nIndex, const OUString &aLabel )
+ { if (nIndex < maxsize) maFontType[ nIndex ] = aLabel; }
+ void setAlign( sal_uInt16 nIndex, sal_uInt8 nData )
+ { if (nIndex < maxsize) maAlign[ nIndex ] = nData; }
+ void setFont( sal_uInt16 nIndex, sal_uInt8 nData )
+ { if (nIndex < maxsize) maFont[ nIndex ] = nData; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/querytablebuffer.hxx b/sc/source/filter/inc/querytablebuffer.hxx
new file mode 100644
index 0000000000..16410ec8cf
--- /dev/null
+++ b/sc/source/filter/inc/querytablebuffer.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 .
+ */
+
+#pragma once
+
+#include "stylesbuffer.hxx"
+#include "worksheethelper.hxx"
+
+namespace oox::xls {
+
+struct QueryTableModel : public AutoFormatModel
+{
+ OUString maDefName; /// Defined name containing the target cell range.
+ sal_Int32 mnConnId; /// Identifier of the external connection used to query the data.
+ sal_Int32 mnGrowShrinkType; /// Behaviour when source data size changes.
+ bool mbHeaders; /// True = source data contains a header row.
+ bool mbRowNumbers; /// True = first column contains row numbers.
+ bool mbDisableRefresh; /// True = refreshing data disabled.
+ bool mbBackground; /// True = refresh asynchronously.
+ bool mbFirstBackground; /// True = first background refresh not yet finished.
+ bool mbRefreshOnLoad; /// True = refresh table after import.
+ bool mbFillFormulas; /// True = expand formulas next to range when source data grows.
+ bool mbRemoveDataOnSave; /// True = remove queried data before saving.
+ bool mbDisableEdit; /// True = connection locked for editing.
+ bool mbPreserveFormat; /// True = use existing formatting for new rows.
+ bool mbAdjustColWidth; /// True = adjust column widths after refresh.
+ bool mbIntermediate; /// True = query table defined but not built yet.
+
+ explicit QueryTableModel();
+};
+
+class QueryTable : public WorksheetHelper
+{
+public:
+ explicit QueryTable( const WorksheetHelper& rHelper );
+
+ /** Imports query table settings from the queryTable element. */
+ void importQueryTable( const AttributeList& rAttribs );
+ /** Imports query table settings from the QUERYTABLE record. */
+ void importQueryTable( SequenceInputStream& rStrm );
+
+ /** Inserts a web query into the sheet. */
+ void finalizeImport();
+
+private:
+ QueryTableModel maModel;
+};
+
+class QueryTableBuffer : public WorksheetHelper
+{
+public:
+ explicit QueryTableBuffer( const WorksheetHelper& rHelper );
+
+ /** Creates a new query table and stores it into the internal vector. */
+ QueryTable& createQueryTable();
+
+ /** Inserts all web queries into the sheet. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< QueryTable > QueryTableVector;
+ QueryTableVector maQueryTables;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/querytablefragment.hxx b/sc/source/filter/inc/querytablefragment.hxx
new file mode 100644
index 0000000000..281826178f
--- /dev/null
+++ b/sc/source/filter/inc/querytablefragment.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 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class QueryTable;
+
+class QueryTableFragment : public WorksheetFragmentBase
+{
+public:
+ explicit QueryTableFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ QueryTable& mrQueryTable;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/revisionfragment.hxx b/sc/source/filter/inc/revisionfragment.hxx
new file mode 100644
index 0000000000..30a953160e
--- /dev/null
+++ b/sc/source/filter/inc/revisionfragment.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/.
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include <memory>
+
+class ScChangeTrack;
+
+namespace oox::xls {
+
+class RevisionHeadersFragment : public WorkbookFragmentBase
+{
+ struct Impl;
+ std::unique_ptr<Impl> mpImpl;
+
+public:
+ explicit RevisionHeadersFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath );
+
+ virtual ~RevisionHeadersFragment() override;
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual void finalizeImport() override;
+
+private:
+ void importHeader( const AttributeList& rAttribs );
+};
+
+class RevisionLogFragment : public WorkbookFragmentBase
+{
+ struct Impl;
+ std::unique_ptr<Impl> mpImpl;
+
+public:
+ explicit RevisionLogFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack );
+
+ virtual ~RevisionLogFragment() override;
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual void finalizeImport() override;
+
+private:
+ void importCommon( const AttributeList& rAttribs );
+ void importRcc( const AttributeList& rAttribs );
+ void importRrc( const AttributeList& rAttribs );
+
+ void pushRevision();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/filter/inc/richstring.hxx b/sc/source/filter/inc/richstring.hxx
new file mode 100644
index 0000000000..3969de6b79
--- /dev/null
+++ b/sc/source/filter/inc/richstring.hxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/refvector.hxx>
+#include "stylesbuffer.hxx"
+
+class EditTextObject;
+struct ESelection;
+class ScEditEngineDefaulter;
+
+namespace com::sun::star {
+ namespace text { class XText; }
+}
+
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+/** Contains text data and font attributes for a part of a rich formatted string. */
+class RichStringPortion
+{
+public:
+ RichStringPortion();
+
+ /** Sets text data for this portion. */
+ void setText( const OUString& rText );
+ /** Creates and returns a new font formatting object. */
+ FontRef const & createFont(const WorkbookHelper& rHelper);
+ /** Links this portion to a font object from the global font list. */
+ void setFontId( sal_Int32 nFontId );
+
+ /** Final processing after import of all strings. */
+ void finalizeImport(const WorkbookHelper& rHelper);
+
+ /** Returns the text data of this portion. */
+ const OUString& getText() const { return maText; }
+ /** Returns true, if the portion contains font formatting. */
+ bool hasFont() const { return bool(mxFont); }
+
+ /** Converts the portion and replaces or appends to the passed XText. */
+ void convert(
+ const css::uno::Reference< css::text::XText >& rxText,
+ bool bReplace );
+ void convert( ScEditEngineDefaulter& rEE, ESelection& rSelection, const oox::xls::Font* pFont );
+
+ void writeFontProperties(
+ const css::uno::Reference< css::text::XText >& rxText ) const;
+
+private:
+ OUString maText; /// Portion text.
+ FontRef mxFont; /// Embedded portion font, may be empty.
+ sal_Int32 mnFontId; /// Link to global font list.
+ bool mbConverted; /// Without repeatedly convert
+};
+
+/** Represents a position in a rich-string containing current font identifier.
+
+ This object stores the position of a formatted character in a rich-string
+ and the identifier of a font from the global font list used to format this
+ and the following characters. Used in binary filters only.
+ */
+struct FontPortionModel
+{
+ sal_Int32 mnPos; /// First character in the string.
+ sal_Int32 mnFontId; /// Font identifier for the next characters.
+
+ explicit FontPortionModel() : mnPos( 0 ), mnFontId( -1 ) {}
+ explicit FontPortionModel( sal_Int32 nPos ) : mnPos( nPos ), mnFontId( -1 ) {}
+
+ void read( SequenceInputStream& rStrm );
+};
+
+/** A vector with all font portions in a rich-string. */
+class FontPortionModelList {
+ ::std::vector< FontPortionModel > mvModels;
+
+public:
+ explicit FontPortionModelList() : mvModels() {}
+
+ bool empty() const { return mvModels.empty(); }
+
+ const FontPortionModel& back() const { return mvModels.back(); }
+ const FontPortionModel& front() const { return mvModels.front(); }
+
+ void push_back(const FontPortionModel& rModel) { mvModels.push_back(rModel); }
+
+ void insert(::std::vector< FontPortionModel >::iterator it,
+ const FontPortionModel& rModel)
+ { mvModels.insert(it, rModel); }
+
+ ::std::vector< FontPortionModel >::iterator begin() { return mvModels.begin(); }
+
+ /** Appends a rich-string font identifier. */
+ void appendPortion( const FontPortionModel& rPortion );
+ /** Reads count and font identifiers from the passed stream. */
+ void importPortions( SequenceInputStream& rStrm );
+};
+
+struct PhoneticDataModel
+{
+ sal_Int32 mnFontId; /// Font identifier for text formatting.
+ sal_Int32 mnType; /// Phonetic text type.
+ sal_Int32 mnAlignment; /// Phonetic portion alignment.
+
+ explicit PhoneticDataModel();
+
+ /** Sets the passed data from binary import. */
+ void setBiffData( sal_Int32 nType, sal_Int32 nAlignment );
+};
+
+class PhoneticSettings : public WorkbookHelper
+{
+public:
+ explicit PhoneticSettings( const WorkbookHelper& rHelper );
+
+ /** Imports phonetic settings from the phoneticPr element. */
+ void importPhoneticPr( const AttributeList& rAttribs );
+ /** Imports phonetic settings from the PHONETICPR record. */
+ void importPhoneticPr( SequenceInputStream& rStrm );
+
+ /** Imports phonetic settings from a rich string. */
+ void importStringData( SequenceInputStream& rStrm );
+
+private:
+ PhoneticDataModel maModel;
+};
+
+/** Contains text data and positioning information for a phonetic text portion. */
+class RichStringPhonetic
+{
+public:
+ RichStringPhonetic();
+
+ /** Sets text data for this phonetic portion. */
+ void setText( const OUString& rText );
+ /** Imports attributes of a phonetic run (rPh element). */
+ void importPhoneticRun( const AttributeList& rAttribs );
+ /** Sets the associated range in base text for this phonetic portion. */
+ void setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd );
+
+private:
+ OUString maText; /// Portion text.
+ sal_Int32 mnBasePos; /// Start position in base text.
+ sal_Int32 mnBaseEnd; /// One-past-end position in base text.
+};
+
+typedef std::shared_ptr< RichStringPhonetic > RichStringPhoneticRef;
+
+/** Represents a phonetic text portion in a rich-string with phonetic text.
+ Used in binary filters only. */
+struct PhoneticPortionModel
+{
+ sal_Int32 mnPos; /// First character in phonetic text.
+ sal_Int32 mnBasePos; /// First character in base text.
+ sal_Int32 mnBaseLen; /// Number of characters in base text.
+
+ explicit PhoneticPortionModel() : mnPos( -1 ), mnBasePos( -1 ), mnBaseLen( 0 ) {}
+ explicit PhoneticPortionModel( sal_Int32 nPos, sal_Int32 nBasePos, sal_Int32 nBaseLen ) :
+ mnPos( nPos ), mnBasePos( nBasePos ), mnBaseLen( nBaseLen ) {}
+
+ void read( SequenceInputStream& rStrm );
+};
+
+/** A vector with all phonetic portions in a rich-string. */
+class PhoneticPortionModelList
+{
+public:
+ explicit PhoneticPortionModelList() : mvModels() {}
+
+ bool empty() const { return mvModels.empty(); }
+
+ const PhoneticPortionModel& back() const { return mvModels.back(); }
+
+ void push_back(const PhoneticPortionModel& rModel) { mvModels.push_back(rModel); }
+
+ ::std::vector< PhoneticPortionModel >::const_iterator begin() const { return mvModels.begin(); }
+
+ /** Appends a rich-string phonetic portion. */
+ void appendPortion( const PhoneticPortionModel& rPortion );
+ /** Reads all phonetic portions from the passed stream. */
+ void importPortions( SequenceInputStream& rStrm );
+
+private:
+ ::std::vector< PhoneticPortionModel > mvModels;
+};
+
+/** Contains string data and a list of formatting runs for a rich formatted string. */
+class RichString
+{
+public:
+
+ /** Appends and returns an index of a portion object for a plain string (t element). */
+ sal_Int32 importText(const AttributeList& rAttribs);
+ /** Appends and returns an index of a portion object for a new formatting run (r element). */
+ sal_Int32 importRun();
+ /** Appends and returns a phonetic text object for a new phonetic run (rPh element). */
+ RichStringPhoneticRef importPhoneticRun( const AttributeList& rAttribs );
+ /** Imports phonetic settings from the rPhoneticPr element. */
+ void importPhoneticPr( const AttributeList& rAttribs, const WorkbookHelper& rHelper );
+
+ /** Imports a Unicode rich-string from the passed record stream. */
+ void importString( SequenceInputStream& rStrm, bool bRich, const WorkbookHelper& rHelper );
+
+ /** Final processing after import of all strings. */
+ void finalizeImport(const WorkbookHelper& rHelper);
+
+ /** Tries to extract a plain string from this object. Returns the string,
+ if there is only one unformatted portion. */
+ bool extractPlainString(
+ OUString& orString,
+ const oox::xls::Font* pFirstPortionFont ) const;
+
+ /** Get the text of all portions as a single string regardless of formatted or not */
+ OUString getStringContent() const;
+
+ /** Converts the string and writes it into the passed XText, replace old contents of the text object,.
+ @param rxText The XText interface of the target object.
+ */
+ void convert( const css::uno::Reference< css::text::XText >& rxText );
+ std::unique_ptr<EditTextObject> convert( ScEditEngineDefaulter& rEE, const oox::xls::Font* pFont );
+
+ RichStringPortion& getPortion(sal_Int32 nPortionIdx) { return maTextPortions[nPortionIdx]; }
+
+ void setAttributes(const AttributeList& rAttribs);
+
+ bool isPreserveSpace() const { return mbPreserveSpace; }
+
+private:
+ /** Creates, appends, and returns a new empty string portion. */
+ sal_Int32 createPortion();
+ /** Creates, appends, and returns a new empty phonetic text portion. */
+ RichStringPhoneticRef createPhonetic();
+
+ /** Create base text portions from the passed string and character formatting. */
+ void createTextPortions( std::u16string_view aText, FontPortionModelList& rPortions );
+ /** Create phonetic text portions from the passed string and portion data. */
+ void createPhoneticPortions( std::u16string_view aText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen );
+
+private:
+ typedef RefVector< RichStringPhonetic > PhoneticVector;
+
+ std::vector<RichStringPortion> maTextPortions; /// String portions with font data.
+ std::unique_ptr<PhoneticSettings> mxPhonSettings; /// Phonetic settings for this string.
+ PhoneticVector maPhonPortions; /// Phonetic text portions.
+ bool mbPreserveSpace = false;
+};
+
+typedef std::shared_ptr< RichString > RichStringRef;
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/richstringcontext.hxx b/sc/source/filter/inc/richstringcontext.hxx
new file mode 100644
index 0000000000..2aef4334a7
--- /dev/null
+++ b/sc/source/filter/inc/richstringcontext.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 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "richstring.hxx"
+#include <osl/diagnose.h>
+
+#include <utility>
+
+namespace oox::xls {
+
+class RichStringContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit RichStringContext( ParentType& rParent, RichStringRef xString );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+private:
+ RichStringRef mxString; /// Processed string.
+ sal_Int32 mnPortionIdx = -1; /// Processed portion in the string.
+ RichStringPhoneticRef mxPhonetic; /// Processed phonetic text portion.
+};
+
+template< typename ParentType >
+RichStringContext::RichStringContext( ParentType& rParent, RichStringRef xString ) :
+ WorkbookContextBase( rParent ),
+ mxString(std::move( xString ))
+{
+ mbEnableTrimSpace = false;
+ OSL_ENSURE( mxString, "RichStringContext::RichStringContext - missing string object" );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/root.hxx b/sc/source/filter/inc/root.hxx
new file mode 100644
index 0000000000..0e4697dcd3
--- /dev/null
+++ b/sc/source/filter/inc/root.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "flttypes.hxx"
+#include <memory>
+
+class ScRangeName;
+
+class RangeNameBufferWK3;
+class SharedFormulaBuffer;
+class ExtNameBuff;
+class ExtSheetBuffer;
+class ExcelToSc;
+
+class XclImpColRowSettings;
+class XclImpAutoFilterBuffer;
+class XclExpChTrTabId;
+class XclExpUserBViewList;
+
+class XclImpRoot;
+class XclExpRoot;
+
+// Excel Imp~/Exp~ -
+
+struct RootData // -> incarnation in each case in the ImportExcel object!
+{
+ BiffTyp eDateiTyp; // fine differentiation
+ std::unique_ptr<ExtSheetBuffer> pExtSheetBuff;
+ std::unique_ptr<SharedFormulaBuffer> pShrfmlaBuff;
+ std::unique_ptr<ExtNameBuff> pExtNameBuff;
+ ExcelToSc* pFmlaConverter;
+ XclImpColRowSettings* pColRowBuff; // col/row settings 1 table
+
+ // Biff8
+ std::unique_ptr<XclImpAutoFilterBuffer> pAutoFilterBuffer; // ranges for autofilter and advanced filter
+
+ // extensions for export
+ XclExpChTrTabId* pTabId; // pointer to rec list, do not destroy
+ XclExpUserBViewList* pUserBViewList; // pointer to rec list, do not destroy
+
+ XclImpRoot* pIR;
+ XclExpRoot* pER;
+
+ RootData(); // -> exctools.cxx
+ ~RootData(); // -> exctools.cxx
+};
+
+class ExcRoot
+{
+protected:
+ RootData* pExcRoot;
+ ExcRoot( RootData* pNexExcRoot ) : pExcRoot( pNexExcRoot ) {}
+ ExcRoot( const ExcRoot& rCopy ) : pExcRoot( rCopy.pExcRoot ) {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/rtfexp.hxx b/sc/source/filter/inc/rtfexp.hxx
new file mode 100644
index 0000000000..9d0b204540
--- /dev/null
+++ b/sc/source/filter/inc/rtfexp.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 .
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include "expbase.hxx"
+#include <tools/solar.h>
+
+class ScRTFExport : public ScExportBase
+{
+ std::unique_ptr<sal_uLong[]> m_pCellX; // cumulative range in a table
+ std::map<OUString, sal_Int32> m_pFontTable;
+ SvMemoryStream m_aFontStrm;
+ SvMemoryStream m_aDocStrm;
+
+ int AddFont( const SvxFontItem& rFontItem );
+ void WriteTab( SCTAB nTab );
+ void WriteRow( SCTAB nTab, SCROW nRow );
+ void WriteCell( SCTAB nTab, SCROW nRow, SCCOL nCol );
+ void WriteFontTable(const SvxFontItem& rFontItem, int nIndex);
+
+public:
+
+ ScRTFExport( SvStream&, ScDocument*, const ScRange& );
+ virtual ~ScRTFExport() override;
+
+ void Write();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/rtfimp.hxx b/sc/source/filter/inc/rtfimp.hxx
new file mode 100644
index 0000000000..ee9fd248b5
--- /dev/null
+++ b/sc/source/filter/inc/rtfimp.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 .
+ */
+
+#pragma once
+
+#include "eeimport.hxx"
+
+class ScRTFImport : public ScEEImport
+{
+public:
+ ScRTFImport(ScDocument* pDoc, const ScRange& rRange);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/rtfparse.hxx b/sc/source/filter/inc/rtfparse.hxx
new file mode 100644
index 0000000000..ac0f81e148
--- /dev/null
+++ b/sc/source/filter/inc/rtfparse.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "eeparser.hxx"
+
+#include <vector>
+#include <memory>
+#include <o3tl/sorted_vector.hxx>
+
+struct ScRTFCellDefault
+{
+ SfxItemSet aItemSet;
+ SCCOL nCol;
+ sal_uInt16 nTwips; // right border of cell
+ SCCOL nColOverlap; // MergeCell if >1, merged cells if 0
+
+ ScRTFCellDefault( SfxItemPool* pPool )
+ : aItemSet(*pPool)
+ , nCol(0)
+ , nTwips(0)
+ , nColOverlap(1)
+ {
+ }
+};
+
+class ScRTFColTwips : public o3tl::sorted_vector<sal_uLong> {};
+
+class EditEngine;
+
+class ScRTFParser : public ScEEParser
+{
+private:
+ typedef std::vector< std::unique_ptr<ScRTFCellDefault> > DefaultList;
+
+ DefaultList maDefaultList;
+ size_t mnCurPos;
+
+ ScRTFColTwips aColTwips;
+ std::unique_ptr<ScRTFCellDefault> pInsDefault;
+ ScRTFCellDefault* pActDefault;
+ ScRTFCellDefault* pDefMerge;
+ sal_uLong nStartAdjust;
+ sal_uInt16 nLastWidth;
+ bool bNewDef;
+
+ DECL_LINK( RTFImportHdl, RtfImportInfo&, void );
+ inline void NextRow();
+ void EntryEnd( ScEEParseEntry*, const ESelection& );
+ void ProcToken( RtfImportInfo* );
+ void ColAdjust();
+ bool SeekTwips( sal_uInt16 nTwips, SCCOL* pCol );
+ void NewCellRow();
+
+public:
+ ScRTFParser( EditEngine* );
+ virtual ~ScRTFParser() override;
+ virtual ErrCode Read( SvStream&, const OUString& rBaseURL ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scenariobuffer.hxx b/sc/source/filter/inc/scenariobuffer.hxx
new file mode 100644
index 0000000000..97ef44daa5
--- /dev/null
+++ b/sc/source/filter/inc/scenariobuffer.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/refmap.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+struct ScenarioCellModel
+{
+ ScAddress maPos;
+ OUString maValue;
+ sal_Int32 mnNumFmtId;
+ bool mbDeleted;
+
+ explicit ScenarioCellModel();
+};
+
+struct ScenarioModel
+{
+ OUString maName; /// Name of the scenario.
+ OUString maComment; /// Comment.
+ OUString maUser; /// Name of user created the scenario.
+ bool mbLocked; /// True = input cell values locked.
+ bool mbHidden; /// True = scenario is hidden.
+ bool mbActive;
+
+ explicit ScenarioModel();
+};
+
+class Scenario : public WorkbookHelper
+{
+public:
+ explicit Scenario( const WorkbookHelper& rHelper, sal_Int16 nSheet, bool bIsActive );
+
+ /** Imports a scenario definition from a scenario element. */
+ void importScenario( const AttributeList& rAttribs );
+ /** Imports a new cell for this scenario from an inputCells element. */
+ void importInputCells( const AttributeList& rAttribs );
+
+ /** Imports a scenario definition from a SCENARIO record. */
+ void importScenario( SequenceInputStream& rStrm );
+ /** Imports a new cell for this scenario from an INPUTCELLS record. */
+ void importInputCells( SequenceInputStream& rStrm );
+
+ /** Creates the scenario in the Calc document. */
+ void finalizeImport();
+
+private:
+ std::vector< ScenarioCellModel > maCells; /// Scenario cells.
+ ScenarioModel maModel; /// Scenario model data.
+ sal_Int16 mnSheet; /// Index of the sheet this scenario is based on.
+};
+
+struct SheetScenariosModel
+{
+ sal_Int32 mnCurrent; /// Selected scenario.
+ sal_Int32 mnShown; /// Visible scenario.
+
+ explicit SheetScenariosModel();
+};
+
+class SheetScenarios : public WorkbookHelper
+{
+public:
+ explicit SheetScenarios( const WorkbookHelper& rHelper, sal_Int16 nSheet );
+
+ /** Imports sheet scenario settings from a scenarios element. */
+ void importScenarios( const AttributeList& rAttribs );
+ /** Imports sheet scenario settings from a SCENARIOS record. */
+ void importScenarios( SequenceInputStream& rStrm );
+
+ /** Creates and returns a new scenario in this collection. */
+ Scenario& createScenario();
+
+ /** Creates all scenarios in the Calc sheet. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< Scenario > ScenarioVector;
+ ScenarioVector maScenarios;
+ SheetScenariosModel maModel;
+ sal_Int16 mnSheet;
+};
+
+class ScenarioBuffer : public WorkbookHelper
+{
+public:
+ explicit ScenarioBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates and returns a scenario collection for the passed sheet. */
+ SheetScenarios& createSheetScenarios( sal_Int16 nSheet );
+
+ /** Creates all scenarios in the Calc document. */
+ void finalizeImport();
+
+private:
+ typedef RefMap< sal_Int16, SheetScenarios, ::std::greater< sal_Int16 > > SheetScenariosMap;
+ SheetScenariosMap maSheetScenarios;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scenariocontext.hxx b/sc/source/filter/inc/scenariocontext.hxx
new file mode 100644
index 0000000000..80368810d8
--- /dev/null
+++ b/sc/source/filter/inc/scenariocontext.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class Scenario;
+class SheetScenarios;
+
+class ScenarioContext : public WorksheetContextBase
+{
+public:
+ explicit ScenarioContext( WorksheetContextBase& rParent, SheetScenarios& rSheetScenarios );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ Scenario& mrScenario;
+};
+
+class ScenariosContext : public WorksheetContextBase
+{
+public:
+ explicit ScenariosContext( WorksheetFragmentBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ SheetScenarios& mrSheetScenarios;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scmem.h b/sc/source/filter/inc/scmem.h
new file mode 100644
index 0000000000..b54b7d850e
--- /dev/null
+++ b/sc/source/filter/inc/scmem.h
@@ -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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+struct LotusContext;
+
+bool MemNew(LotusContext& rContext);
+void MemDelete(LotusContext& rContext);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sharedformulagroups.hxx b/sc/source/filter/inc/sharedformulagroups.hxx
new file mode 100644
index 0000000000..e8b0492859
--- /dev/null
+++ b/sc/source/filter/inc/sharedformulagroups.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <tokenarray.hxx>
+#include <memory>
+#include <map>
+class ScTokenArray;
+
+namespace sc
+{
+class SharedFormulaGroupEntry
+{
+private:
+ std::unique_ptr<ScTokenArray> mpArray;
+ ScAddress maOrigin;
+
+public:
+ SharedFormulaGroupEntry(std::unique_ptr<ScTokenArray> pArray, const ScAddress& rOrigin)
+ : mpArray(std::move(pArray))
+ , maOrigin(rOrigin)
+ {
+ }
+
+ const ScTokenArray* getTokenArray() const { return mpArray.get(); }
+ const ScAddress& getOrigin() const { return maOrigin; }
+};
+
+class SharedFormulaGroups
+{
+private:
+ typedef std::map<size_t, SharedFormulaGroupEntry> StoreType;
+ StoreType m_Store;
+
+public:
+ void set(size_t nSharedId, std::unique_ptr<ScTokenArray> pArray);
+ void set(size_t nSharedId, std::unique_ptr<ScTokenArray> pArray, const ScAddress& rOrigin);
+ const ScTokenArray* get(size_t nSharedId) const;
+ const SharedFormulaGroupEntry* getEntry(size_t nSharedId) const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sharedstringsbuffer.hxx b/sc/source/filter/inc/sharedstringsbuffer.hxx
new file mode 100644
index 0000000000..f5cc18bca0
--- /dev/null
+++ b/sc/source/filter/inc/sharedstringsbuffer.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 .
+ */
+
+#pragma once
+
+#include "richstring.hxx"
+
+namespace oox::xls {
+
+/** Collects all strings from the shared strings substream. */
+class SharedStringsBuffer : public WorkbookHelper
+{
+public:
+ explicit SharedStringsBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates and returns a new string entry. */
+ RichStringRef createRichString();
+
+ /** Final processing after import of all strings. */
+ void finalizeImport();
+
+ /** Returns the specified string. */
+ RichStringRef getString( sal_Int32 nStringId ) const;
+
+private:
+ typedef RefVector< RichString > StringVector;
+ StringVector maStrings;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sharedstringsfragment.hxx b/sc/source/filter/inc/sharedstringsfragment.hxx
new file mode 100644
index 0000000000..791832397f
--- /dev/null
+++ b/sc/source/filter/inc/sharedstringsfragment.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 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class SharedStringsFragment : public WorkbookFragmentBase
+{
+public:
+ explicit SharedStringsFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sheetdatabuffer.hxx b/sc/source/filter/inc/sheetdatabuffer.hxx
new file mode 100644
index 0000000000..5386313300
--- /dev/null
+++ b/sc/source/filter/inc/sheetdatabuffer.hxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <map>
+#include <o3tl/sorted_vector.hxx>
+
+#include "richstring.hxx"
+#include "worksheethelper.hxx"
+#include "addressconverter.hxx"
+
+namespace com::sun::star {
+ namespace util { struct DateTime; }
+}
+
+namespace oox::xls {
+
+/** Stores basic data about cell values and formatting. */
+struct CellModel
+{
+ ScAddress maCellAddr; /// The address of the current cell.
+ sal_Int32 mnCellType; /// Data type of the cell value.
+ sal_Int32 mnXfId; /// XF (cell formatting) identifier.
+ bool mbShowPhonetic; /// True = show phonetic text.
+
+ explicit CellModel();
+};
+
+/** Stores data about cell formulas. */
+struct CellFormulaModel
+{
+ ScRange maFormulaRef; /// Formula range for array/shared formulas and data tables.
+ sal_Int32 mnFormulaType; /// Type of the formula (regular, array, shared, table).
+ sal_Int32 mnSharedId; /// Identifier of a shared formula (OOXML only).
+
+ explicit CellFormulaModel();
+
+ /** Returns true, if the passed cell address is valid for an array formula. */
+ bool isValidArrayRef( const ScAddress& rCellAddr );
+ /** Returns true, if the passed cell address is valid for a shared formula. */
+ bool isValidSharedRef( const ScAddress& rCellAddr );
+};
+
+/** Stores data about table operations. */
+struct DataTableModel
+{
+ OUString maRef1; /// First reference cell for table operations.
+ OUString maRef2; /// Second reference cell for table operations.
+ bool mb2dTable; /// True = 2-dimensional data table.
+ bool mbRowTable; /// True = row oriented data table.
+ bool mbRef1Deleted; /// True = first reference cell deleted.
+ bool mbRef2Deleted; /// True = second reference cell deleted.
+
+ explicit DataTableModel();
+};
+
+/** Manages the cell contents and cell formatting of a sheet.
+ */
+class SheetDataBuffer : public WorksheetHelper
+{
+public:
+ explicit SheetDataBuffer( const WorksheetHelper& rHelper );
+
+ /** Inserts a blank cell (with formatting) into the sheet. */
+ void setBlankCell( const CellModel& rModel );
+ /** Inserts a value cell into the sheet. */
+ void setValueCell( const CellModel& rModel, double fValue );
+ /** Inserts a simple string cell into the sheet. */
+ void setStringCell( const CellModel& rModel, const OUString& rText );
+ /** Inserts a rich-string cell into the sheet. */
+ void setStringCell( const CellModel& rModel, const RichStringRef& rxString );
+ /** Inserts a shared string cell into the sheet. */
+ void setStringCell( const CellModel& rModel, sal_Int32 nStringId );
+ /** Inserts a date/time cell into the sheet and adjusts number format. */
+ void setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime );
+ /** Inserts a boolean cell into the sheet and adjusts number format. */
+ void setBooleanCell( const CellModel& rModel, bool bValue );
+ /** Inserts an error cell from the passed error code into the sheet. */
+ void setErrorCell( const CellModel& rModel, const OUString& rErrorCode );
+ /** Inserts an error cell from the passed BIFF error code into the sheet. */
+ void setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode );
+ /** Inserts a formula cell into the sheet. */
+ void setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens );
+ /** Inserts an ISO 8601 date cell into the sheet. */
+ void setDateCell( const CellModel& rModel, const OUString& rDateString );
+
+ void createSharedFormula(
+ const ScAddress& rRange,
+ const ApiTokenSequence& rTokens);
+
+ /** Inserts the passed token array as array formula. */
+ void createArrayFormula(
+ const ScRange& rRange,
+ const ApiTokenSequence& rTokens );
+ /** Sets a multiple table operation to the passed range. */
+ void createTableOperation(
+ const ScRange& rRange,
+ const DataTableModel& rModel );
+
+ /** Sets default cell formatting for the specified range of rows. */
+ void setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat );
+ /** Merges the cells in the passed cell range. */
+ void setMergedRange( const ScRange& rRange );
+
+ /** Processes the cell formatting data of the passed cell. */
+ void setCellFormat( const CellModel& rModel );
+
+ /** Final processing after the sheet has been imported. */
+ void finalizeImport();
+
+ /** Sets the passed formula token array into a cell. */
+ void setCellFormula(
+ const ScAddress& rCellAddr,
+ const ApiTokenSequence& rTokens );
+private:
+
+ /** Creates a formula token array representing the shared formula with the
+ passed identifier. */
+ ApiTokenSequence resolveSharedFormula( const ScAddress& rMapKey ) const;
+
+ /** Inserts the passed array formula into the sheet. */
+ void finalizeArrayFormula(
+ const ScRange& rRange,
+ const ApiTokenSequence& rTokens ) const;
+ /** Inserts the passed table operation into the sheet. */
+ void finalizeTableOperation(
+ const ScRange& rRange, const DataTableModel& rModel );
+
+ /** Writes all cell formatting attributes to the passed cell range list. (depreciates writeXfIdRangeProperties) */
+ void applyCellMerging( const ScRange& rRange );
+ void addColXfStyles();
+ void addColXfStyleProcessRowRanges();
+private:
+ /** Stores cell range address and formula token array of an array formula. */
+ typedef std::pair< ScRange, ApiTokenSequence > ArrayFormula;
+ typedef ::std::vector< ArrayFormula > ArrayFormulaVector;
+
+ /** Stores cell range address and settings of a table operation. */
+ typedef std::pair< ScRange, DataTableModel > TableOperation;
+
+ /** Stores information about a range of rows with equal cell formatting. */
+ struct XfIdRowRange
+ {
+ ValueRange maRowRange; /// Indexes of first and last row.
+ sal_Int32 mnXfId; /// XF identifier for the row range.
+
+ explicit XfIdRowRange();
+ void set( sal_Int32 nRow, sal_Int32 nXfId );
+ bool tryExpand( sal_Int32 nRow, sal_Int32 nXfId );
+ };
+
+ typedef ::std::pair< sal_Int32, sal_Int32 > XfIdNumFmtKey;
+
+ struct RowRangeStyle
+ {
+ sal_Int32 mnStartRow;
+ sal_Int32 mnEndRow;
+ XfIdNumFmtKey mnNumFmt;
+ };
+ struct StyleRowRangeComp
+ {
+ bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) const
+ {
+ // This end-vs-start comparison is needed by the lower_bound() use
+ // in SheetDataBuffer::addColXfStyleProcessRowRanges() that searches
+ // for partially overlapping ranges. In all other places the ranges
+ // should be non-overlapping, in which case this is the same as the "normal"
+ // comparison.
+ return lhs.mnEndRow<rhs.mnStartRow;
+ }
+ };
+ typedef ::o3tl::sorted_vector< RowRangeStyle, StyleRowRangeComp > RowStyles;
+ typedef ::std::map< sal_Int32, RowStyles > ColStyles;
+ typedef ::std::vector< RowRangeStyle > TmpRowStyles;
+ typedef ::std::map< sal_Int32, TmpRowStyles > TmpColStyles;
+ /** Stores information about a merged cell range. */
+ struct MergedRange
+ {
+ ScRange maRange; /// The formatted cell range.
+ sal_Int32 mnHorAlign; /// Horizontal alignment in the range.
+
+ explicit MergedRange( const ScRange& rRange );
+ explicit MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign );
+ bool tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign );
+ };
+ typedef ::std::vector< MergedRange > MergedRangeVector;
+
+ ColStyles maStylesPerColumn; /// Stores cell styles by column ( in row ranges )
+ ArrayFormulaVector maArrayFormulas; /// All array formulas in the sheet.
+ std::vector< TableOperation >
+ maTableOperations; /// All table operations in the sheet.
+ ::std::map< BinAddress, ApiTokenSequence >
+ maSharedFormulas; /// Maps shared formula base address to defined name token index.
+ ScAddress maSharedFmlaAddr; /// Address of a cell containing a pending shared formula.
+ ScAddress maSharedBaseAddr; /// Base address of the pending shared formula.
+ XfIdRowRange maXfIdRowRange; /// Cached XF identifier for a range of rows.
+ std::map< XfIdNumFmtKey, ScRangeList >
+ maXfIdRangeLists; /// Collected XF identifiers for cell rangelists.
+ MergedRangeVector maMergedRanges; /// Merged cell ranges.
+ MergedRangeVector maCenterFillRanges; /// Merged cell ranges from 'center across' or 'fill' alignment.
+ bool mbPendingSharedFmla; /// True = maSharedFmlaAddr and maSharedBaseAddr are valid.
+ std::map< sal_Int32, std::vector< ValueRange > > maXfIdRowRangeList; /// Cached XF identifiers for a ranges of rows, we try and process rowranges with the same XF id together
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sheetdatacontext.hxx b/sc/source/filter/inc/sheetdatacontext.hxx
new file mode 100644
index 0000000000..b413e3d921
--- /dev/null
+++ b/sc/source/filter/inc/sheetdatacontext.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "addressconverter.hxx"
+#include "excelhandlers.hxx"
+#include "richstring.hxx"
+#include "sheetdatabuffer.hxx"
+#include <vcl/svapp.hxx>
+
+#define MULTI_THREAD_SHEET_PARSING 1
+
+namespace oox::xls {
+
+/** This class implements importing the sheetData element.
+
+ The sheetData element contains all row settings and all cells in a single
+ sheet of a spreadsheet document.
+ */
+class SheetDataContext : public WorksheetContextBase
+{
+ AddressConverter& mrAddressConv; /// The address converter.
+ std::unique_ptr<FormulaParser> mxFormulaParser; /// The formula parser, different one for each SheetDataContext
+ SheetDataBuffer& mrSheetData; /// The sheet data buffer for cell content and formatting.
+ CellModel maCellData; /// Position, contents, formatting of current imported cell.
+ CellFormulaModel maFmlaData; /// Settings for a cell formula.
+ sal_Int16 mnSheet; /// Index of the current sheet.
+ // If we are doing threaded parsing, this SheetDataContext
+ // forms the inner loop for bulk data parsing, and for the
+ // duration of this we can drop the solar mutex.
+#if MULTI_THREAD_SHEET_PARSING
+ SolarMutexReleaser aReleaser;
+#endif
+
+public:
+ explicit SheetDataContext( WorksheetFragmentBase& rFragment );
+ virtual ~SheetDataContext() override;
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+private:
+ /** Different types of cell records. */
+ enum CellType { CELLTYPE_VALUE, CELLTYPE_MULTI, CELLTYPE_FORMULA };
+
+ /** Imports row settings from a row element. */
+ void importRow( const AttributeList& rAttribs );
+ /** Imports cell settings from a c element. */
+ bool importCell( const AttributeList& rAttribs );
+ /** Imports cell settings from an f element. */
+ void importFormula( const AttributeList& rAttribs );
+
+ /** Imports row settings from a ROW record. */
+ void importRow( SequenceInputStream& rStrm );
+
+ /** Reads a cell address and the following XF identifier. */
+ bool readCellHeader( SequenceInputStream& rStrm, CellType eCellType );
+ /** Reads a cell formula for the current cell. */
+ ApiTokenSequence readCellFormula( SequenceInputStream& rStrm );
+ /** Reads the formula range used by shared formulas, arrays, and data tables. */
+ bool readFormulaRef( SequenceInputStream& rStrm );
+
+ /** Imports an empty cell from a CELL_BLANK or MULTCELL_BLANK record. */
+ void importCellBlank( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a boolean cell from a CELL_BOOL, MULTCELL_BOOL, or FORMULA_BOOL record. */
+ void importCellBool( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a numeric cell from a CELL_DOUBLE, MULTCELL_DOUBLE, or FORMULA_DOUBLE record. */
+ void importCellDouble( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports an error code cell from a CELL_ERROR, MULTCELL_ERROR, or FORMULA_ERROR record. */
+ void importCellError( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports an encoded numeric cell from a CELL_RK or MULTCELL_RK record. */
+ void importCellRk( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a rich-string cell from a CELL_RSTRING or MULTCELL_RSTRING record. */
+ void importCellRString( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a string cell from a CELL_SI or MULTCELL_SI record. */
+ void importCellSi( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a string cell from a CELL_STRING, MULTCELL_STRING, or FORMULA_STRING record. */
+ void importCellString( SequenceInputStream& rStrm, CellType eCellType );
+
+ /** Imports an array formula from an ARRAY record. */
+ void importArray( SequenceInputStream& rStrm );
+ /** Imports table operation from a DATATABLE record. */
+ void importDataTable( SequenceInputStream& rStrm );
+ /** Imports a shared formula from a SHAREDFORMULA record. */
+ void importSharedFmla( SequenceInputStream& rStrm );
+
+private:
+ OUString maCellValue; /// Cell value string (OOXML only).
+ RichStringRef mxInlineStr; /// Inline rich string (OOXML only).
+ OUString maFormulaStr;
+ DataTableModel maTableData; /// Settings for table operations.
+ BinAddress maCurrPos; /// Current cell position (BIFF12 only).
+ bool mbHasFormula; /// True = current cell has formula data (OOXML only).
+ bool mbValidRange; /// True = maFmlaData.maFormulaRef is valid (OOXML only).
+
+ sal_Int32 mnRow; /// row index (0-based)
+ sal_Int32 mnCol; /// column index (0-based)
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/stylesbuffer.hxx b/sc/source/filter/inc/stylesbuffer.hxx
new file mode 100644
index 0000000000..c462da48ee
--- /dev/null
+++ b/sc/source/filter/inc/stylesbuffer.hxx
@@ -0,0 +1,901 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <oox/drawingml/color.hxx>
+#include <oox/helper/graphichelper.hxx>
+#include <oox/helper/refmap.hxx>
+#include <oox/helper/refvector.hxx>
+#include "numberformatsbuffer.hxx"
+#include <editeng/svxenum.hxx>
+#include <editeng/frmdir.hxx>
+#include <docmodel/color/ComplexColor.hxx>
+#include <attarray.hxx>
+#include <vector>
+
+class ScPatternCache;
+
+namespace oox { class SequenceInputStream; }
+
+namespace oox { class PropertySet;
+ class PropertyMap;
+ class AttributeList; }
+
+namespace oox::xls {
+
+const sal_Int32 OOX_COLOR_WINDOWTEXT3 = 24; /// System window text color (BIFF3-BIFF4).
+const sal_Int32 OOX_COLOR_WINDOWBACK3 = 25; /// System window background color (BIFF3-BIFF4).
+const sal_Int32 OOX_COLOR_WINDOWTEXT = 64; /// System window text color (BIFF5+).
+const sal_Int32 OOX_COLOR_WINDOWBACK = 65; /// System window background color (BIFF5+).
+const sal_Int32 OOX_COLOR_BUTTONBACK = 67; /// System button background color (face color).
+const sal_Int32 OOX_COLOR_CHWINDOWTEXT = 77; /// System window text color (BIFF8 charts).
+const sal_Int32 OOX_COLOR_CHWINDOWBACK = 78; /// System window background color (BIFF8 charts).
+const sal_Int32 OOX_COLOR_CHBORDERAUTO = 79; /// Automatic frame border (BIFF8 charts).
+const sal_Int32 OOX_COLOR_NOTEBACK = 80; /// Note background color.
+const sal_Int32 OOX_COLOR_NOTETEXT = 81; /// Note text color.
+const sal_Int32 OOX_COLOR_FONTAUTO = 0x7FFF; /// Font auto color (system window text color).
+
+const sal_Int16 API_LINE_NONE = 0;
+const sal_Int16 API_LINE_HAIR = 1;
+const sal_Int16 API_LINE_THIN = 15;
+const sal_Int16 API_LINE_MEDIUM = 35;
+const sal_Int16 API_LINE_THICK = 50;
+
+const sal_Int16 API_ESCAPE_NONE = 0; /// No escapement.
+const sal_Int16 API_ESCAPE_SUPERSCRIPT = 101; /// Superscript: raise characters automatically (magic value 101).
+const sal_Int16 API_ESCAPE_SUBSCRIPT = -101; /// Subscript: lower characters automatically (magic value -101).
+
+const sal_Int8 API_ESCAPEHEIGHT_NONE = 100; /// Relative character height if not escaped.
+const sal_Int8 API_ESCAPEHEIGHT_DEFAULT = 58; /// Relative character height if escaped.
+
+/** Special implementation of the GraphicHelper for Excel palette and scheme
+ colors.
+ */
+class ExcelGraphicHelper : public GraphicHelper, public WorkbookHelper
+{
+public:
+ explicit ExcelGraphicHelper( const WorkbookHelper& rHelper );
+
+ /** Derived classes may implement to resolve a scheme color from the passed XML token identifier. */
+ virtual ::Color getSchemeColor( sal_Int32 nToken ) const override;
+ /** Derived classes may implement to resolve a palette index to an RGB color. */
+ virtual ::Color getPaletteColor( sal_Int32 nPaletteIdx ) const override;
+};
+
+class XlsColor : public ::oox::drawingml::Color
+{
+public:
+ /** Sets the color to automatic. */
+ void setAuto();
+ /** Sets the color to the passed RGB value. */
+ void setRgb( ::Color nRgbValue, double fTint = 0.0 );
+ /** Sets the color to the passed theme index. */
+ void setTheme( sal_Int32 nThemeIdx, double fTint = 0.0 );
+ /** Sets the color to the passed palette index. */
+ void setIndexed( sal_Int32 nPaletteIdx, double fTint = 0.0 );
+
+ /** Imports the color from the passed attribute list. */
+ void importColor( const AttributeList& rAttribs );
+
+ /** Imports a 64-bit color from the passed binary stream. */
+ void importColor( SequenceInputStream& rStrm );
+ /** Imports a 32-bit palette color identifier from the passed BIFF12 stream. */
+ void importColorId( SequenceInputStream& rStrm );
+
+ /** Returns true, if the color is set to automatic. */
+ bool isAuto() const { return isPlaceHolder(); }
+};
+
+SequenceInputStream& operator>>( SequenceInputStream& rStrm, XlsColor& orColor );
+
+/** Stores all colors of the color palette. */
+class ColorPalette : public WorkbookHelper
+{
+public:
+ /** Constructs the color palette with predefined color values. */
+ explicit ColorPalette( const WorkbookHelper& rHelper );
+
+ /** Appends a new color from the passed attributes. */
+ void importPaletteColor( const AttributeList& rAttribs );
+ /** Appends a new color from the passed RGBCOLOR record. */
+ void importPaletteColor( SequenceInputStream& rStrm );
+
+ /** Returns the RGB value of the color with the passed index. */
+ ::Color getColor( sal_Int32 nPaletteIdx ) const;
+
+private:
+ /** Appends the passed color. */
+ void appendColor( ::Color nRGBValue );
+
+private:
+ ::std::vector< ::Color > maColors; /// List of RGB values.
+ size_t mnAppendIndex; /// Index to append a new color.
+};
+
+/** Contains all XML font attributes, e.g. from a font or rPr element. */
+struct FontModel
+{
+ OUString maName; /// Font name.
+ XlsColor maColor; /// Font color.
+ sal_Int32 mnScheme; /// Major/minor scheme font.
+ sal_Int32 mnFamily; /// Font family.
+ sal_Int32 mnCharSet; /// Windows font character set.
+ double mfHeight; /// Font height in points.
+ sal_Int32 mnUnderline; /// Underline style.
+ sal_Int32 mnEscapement; /// Escapement style.
+ bool mbBold; /// True = bold characters.
+ bool mbItalic; /// True = italic characters.
+ bool mbStrikeout; /// True = Strike out characters.
+ bool mbOutline; /// True = outlined characters.
+ bool mbShadow; /// True = shadowed chgaracters.
+
+ explicit FontModel();
+
+ void setBiff12Scheme( sal_uInt8 nScheme );
+ void setBiffHeight( sal_uInt16 nHeight );
+ void setBiffWeight( sal_uInt16 nWeight );
+ void setBiffUnderline( sal_uInt16 nUnderline );
+ void setBiffEscapement( sal_uInt16 nEscapement );
+};
+
+/** Contains used flags for all API font attributes. */
+struct ApiFontUsedFlags
+{
+ bool mbNameUsed; /// True = font name/family/char set are used.
+ bool mbColorUsed; /// True = font color is used.
+ bool mbSchemeUsed; /// True = font scheme is used.
+ bool mbHeightUsed; /// True = font height is used.
+ bool mbUnderlineUsed; /// True = underline style is used.
+ bool mbEscapementUsed; /// True = escapement style is used.
+ bool mbWeightUsed; /// True = font weight (boldness) is used.
+ bool mbPostureUsed; /// True = font posture (italic) is used.
+ bool mbStrikeoutUsed; /// True = strike out style is used.
+ bool mbOutlineUsed; /// True = outline style is used.
+ bool mbShadowUsed; /// True = shadow style is used.
+
+ explicit ApiFontUsedFlags( bool bAllUsed );
+};
+
+/** Contains API font name, family, and charset for a script type. */
+struct ApiScriptFontName
+{
+ OUString maName; /// Font name.
+ sal_Int16 mnFamily; /// Font family.
+ sal_Int16 mnTextEnc; /// Font text encoding.
+
+ explicit ApiScriptFontName();
+};
+
+/** Contains all API font attributes. */
+struct ApiFontData
+{
+ ApiScriptFontName maLatinFont; /// Font name for latin scripts.
+ ApiScriptFontName maAsianFont; /// Font name for east-asian scripts.
+ ApiScriptFontName maCmplxFont; /// Font name for complex scripts.
+ css::awt::FontDescriptor maDesc; /// Font descriptor (height in twips, weight in %).
+ ::Color mnColor; /// Font color.
+ model::ComplexColor maComplexColor; /// Font complex color.
+ sal_Int16 mnEscapement; /// Escapement style.
+ sal_Int8 mnEscapeHeight; /// Escapement font height.
+ bool mbOutline; /// True = outlined characters.
+ bool mbShadow; /// True = shadowed chgaracters.
+
+ explicit ApiFontData();
+};
+
+class Font : public WorkbookHelper
+{
+public:
+ explicit Font( const WorkbookHelper& rHelper, bool bDxf );
+ explicit Font( const WorkbookHelper& rHelper, FontModel aModel );
+
+ /** Sets font formatting attributes for the passed element. */
+ void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs );
+
+ /** Imports the FONT record from the passed stream. */
+ void importFont( SequenceInputStream& rStrm );
+ /** Imports the font name from a DXF record. */
+ void importDxfName( SequenceInputStream& rStrm );
+ /** Imports the font color from a DXF record. */
+ void importDxfColor( SequenceInputStream& rStrm );
+ /** Imports the font scheme from a DXF record. */
+ void importDxfScheme( SequenceInputStream& rStrm );
+ /** Imports the font height from a DXF record. */
+ void importDxfHeight( SequenceInputStream& rStrm );
+ /** Imports the font weight from a DXF record. */
+ void importDxfWeight( SequenceInputStream& rStrm );
+ /** Imports the font underline style from a DXF record. */
+ void importDxfUnderline( SequenceInputStream& rStrm );
+ /** Imports the font escapement style from a DXF record. */
+ void importDxfEscapement( SequenceInputStream& rStrm );
+ /** Imports a font style flag from a DXF record. */
+ void importDxfFlag( sal_Int32 nElement, SequenceInputStream& rStrm );
+
+ /** Returns the font model structure. This function can be called before
+ finalizeImport() has been called. */
+ const FontModel& getModel() const { return maModel; }
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns an API font descriptor with own font information. */
+ const css::awt::FontDescriptor& getFontDescriptor() const { return maApiData.maDesc;}
+ /** Returns true, if the font requires rich text formatting in Calc.
+ @descr Example: Font escapement is a cell attribute in Excel, but Calc
+ needs a rich text cell for this attribute. */
+ bool needsRichTextFormat() const;
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bEditEngineText, bool bSkipPoolDefs = false ) const;
+ /** Writes all font attributes to the passed property map. */
+ void writeToPropertyMap(
+ PropertyMap& rPropMap ) const;
+ /** Writes all font attributes to the passed property set. */
+ void writeToPropertySet(
+ PropertySet& rPropSet ) const;
+
+private:
+ FontModel maModel;
+ ApiFontData maApiData;
+ ApiFontUsedFlags maUsedFlags;
+ bool mbDxf;
+};
+
+typedef std::shared_ptr< Font > FontRef;
+
+/** Contains all XML cell alignment attributes, e.g. from an alignment element. */
+struct AlignmentModel
+{
+ sal_Int32 mnHorAlign; /// Horizontal alignment.
+ sal_Int32 mnVerAlign; /// Vertical alignment.
+ sal_Int32 mnTextDir; /// CTL text direction.
+ sal_Int32 mnRotation; /// Text rotation angle.
+ sal_Int32 mnIndent; /// Indentation.
+ bool mbWrapText; /// True = multi-line text.
+ bool mbShrink; /// True = shrink to fit cell size.
+ bool mbJustLastLine; /// True = justify last line in block text.
+
+ explicit AlignmentModel();
+
+ /** Sets horizontal alignment from the passed BIFF data. */
+ void setBiffHorAlign( sal_uInt8 nHorAlign );
+ /** Sets vertical alignment from the passed BIFF data. */
+ void setBiffVerAlign( sal_uInt8 nVerAlign );
+};
+
+/** Contains all API cell alignment attributes. */
+struct ApiAlignmentData
+{
+ css::table::CellHoriJustify meHorJustify; /// Horizontal alignment.
+ sal_Int32 mnHorJustifyMethod;
+ sal_Int32 mnVerJustify; /// Vertical alignment.
+ sal_Int32 mnVerJustifyMethod;
+ css::table::CellOrientation meOrientation; /// Normal or stacked text.
+ Degree100 mnRotation; /// Text rotation angle.
+ sal_Int16 mnWritingMode; /// CTL text direction.
+ sal_Int16 mnIndent; /// Indentation.
+ bool mbWrapText; /// True = multi-line text.
+ bool mbShrink; /// True = shrink to fit cell size.
+
+ explicit ApiAlignmentData();
+};
+
+bool operator==( const ApiAlignmentData& rLeft, const ApiAlignmentData& rRight );
+
+class Alignment : public WorkbookHelper
+{
+public:
+ explicit Alignment( const WorkbookHelper& rHelper );
+
+ /** Sets all attributes from the alignment element. */
+ void importAlignment( const AttributeList& rAttribs );
+
+ /** Sets the alignment attributes from the passed BIFF12 XF record data. */
+ void setBiff12Data( sal_uInt32 nFlags );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the alignment model structure. */
+ const AlignmentModel& getModel() const { return maModel; }
+ /** Returns the converted API alignment data struct. */
+ const ApiAlignmentData& getApiData() const { return maApiData; }
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ ::SvxCellHorJustify GetScHorAlign() const;
+ ::SvxCellVerJustify GetScVerAlign() const;
+ ::SvxFrameDirection GetScFrameDir() const;
+ AlignmentModel maModel; /// Alignment model data.
+ ApiAlignmentData maApiData; /// Alignment data converted to API constants.
+};
+
+/** Contains all XML cell protection attributes, e.g. from a protection element. */
+struct ProtectionModel
+{
+ bool mbLocked; /// True = locked against editing.
+ bool mbHidden; /// True = formula is hidden.
+
+ explicit ProtectionModel();
+};
+
+/** Contains all API cell protection attributes. */
+struct ApiProtectionData
+{
+ typedef css::util::CellProtection ApiCellProtection;
+
+ ApiCellProtection maCellProt;
+
+ explicit ApiProtectionData();
+};
+
+bool operator==( const ApiProtectionData& rLeft, const ApiProtectionData& rRight );
+
+class Protection : public WorkbookHelper
+{
+public:
+ explicit Protection( const WorkbookHelper& rHelper );
+
+ /** Sets all attributes from the protection element. */
+ void importProtection( const AttributeList& rAttribs );
+
+ /** Sets the protection attributes from the passed BIFF12 XF record data. */
+ void setBiff12Data( sal_uInt32 nFlags );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the converted API protection data struct. */
+ const ApiProtectionData& getApiData() const { return maApiData; }
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+private:
+ ProtectionModel maModel; /// Protection model data.
+ ApiProtectionData maApiData; /// Protection data converted to API constants.
+};
+
+/** Contains XML attributes of a single border line. */
+struct BorderLineModel
+{
+ XlsColor maColor; /// Borderline color.
+ sal_Int32 mnStyle; /// Border line style.
+ bool mbUsed; /// True = line format used.
+
+ explicit BorderLineModel( bool bDxf );
+
+ /** Sets the passed BIFF line style. */
+ void setBiffStyle( sal_Int32 nLineStyle );
+};
+
+/** Contains XML attributes of a complete cell border. */
+struct BorderModel
+{
+ BorderLineModel maLeft; /// Left line format.
+ BorderLineModel maRight; /// Right line format.
+ BorderLineModel maTop; /// Top line format.
+ BorderLineModel maBottom; /// Bottom line format.
+ BorderLineModel maDiagonal; /// Diagonal line format.
+ bool mbDiagTLtoBR; /// True = top-left to bottom-right on.
+ bool mbDiagBLtoTR; /// True = bottom-left to top-right on.
+
+ explicit BorderModel( bool bDxf );
+};
+
+/** Contains API attributes of a complete cell border. */
+struct ApiBorderData
+{
+ typedef css::table::BorderLine2 ApiBorderLine;
+
+ ApiBorderLine maLeft; /// Left line format
+ ApiBorderLine maRight; /// Right line format
+ ApiBorderLine maTop; /// Top line format
+ ApiBorderLine maBottom; /// Bottom line format
+ model::ComplexColor maComplexColorLeft;
+ model::ComplexColor maComplexColorRight;
+ model::ComplexColor maComplexColorTop;
+ model::ComplexColor maComplexColorBottom;
+ ApiBorderLine maTLtoBR; /// Diagonal top-left to bottom-right line format.
+ ApiBorderLine maBLtoTR; /// Diagonal bottom-left to top-right line format.
+ bool mbBorderUsed; /// True = left/right/top/bottom line format used.
+ bool mbDiagUsed; /// True = diagonal line format used.
+
+ explicit ApiBorderData();
+
+ /** Returns true, if any of the outer border lines is visible. */
+ bool hasAnyOuterBorder() const;
+};
+
+class Border : public WorkbookHelper
+{
+public:
+ explicit Border( const WorkbookHelper& rHelper, bool bDxf );
+
+ /** Sets global border attributes from the border element. */
+ void importBorder( const AttributeList& rAttribs );
+ /** Sets border attributes for the border line with the passed element identifier. */
+ void importStyle( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Sets color attributes for the border line with the passed element identifier. */
+ void importColor( sal_Int32 nElement, const AttributeList& rAttribs );
+
+ /** Imports the BORDER record from the passed stream. */
+ void importBorder( SequenceInputStream& rStrm );
+ /** Imports a border from a DXF record from the passed stream. */
+ void importDxfBorder( sal_Int32 nElement, SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport( bool bRTL );
+
+ /** Returns the converted API border data struct. */
+ const ApiBorderData& getApiData() const { return maApiData; }
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ /** Returns the border line struct specified by the passed XML token identifier. */
+ BorderLineModel* getBorderLine( sal_Int32 nElement );
+
+ /** Converts border line data to an API struct, returns true, if the line is marked as used. */
+ bool convertBorderLine(
+ css::table::BorderLine2& rBorderLine,
+ const BorderLineModel& rModel );
+
+private:
+ BorderModel maModel;
+ ApiBorderData maApiData;
+ bool mbDxf;
+};
+
+typedef std::shared_ptr< Border > BorderRef;
+
+/** Contains XML pattern fill attributes from the patternFill element. */
+struct PatternFillModel
+{
+ XlsColor maPatternColor; /// Pattern foreground color.
+ XlsColor maFilterPatternColor; /// Pattern foreground for color filter.
+ XlsColor maFillColor; /// Background fill color.
+ sal_Int32 mnPattern; /// Pattern identifier (e.g. solid).
+ bool mbPattColorUsed; /// True = pattern foreground color used.
+ bool mbFillColorUsed; /// True = background fill color used.
+ bool mbPatternUsed; /// True = pattern used.
+
+ explicit PatternFillModel( bool bDxf );
+
+ /** Sets the passed BIFF pattern identifier. */
+ void setBiffPattern( sal_Int32 nPattern );
+};
+
+/** Contains XML gradient fill attributes from the gradientFill element. */
+struct GradientFillModel
+{
+ typedef ::std::map<double, XlsColor> ColorMap;
+
+ sal_Int32 mnType; /// Gradient type, linear or path.
+ double mfAngle; /// Rotation angle for type linear.
+ double mfLeft; /// Left convergence for type path.
+ double mfRight; /// Right convergence for type path.
+ double mfTop; /// Top convergence for type path.
+ double mfBottom; /// Bottom convergence for type path.
+ ColorMap maColors; /// Gradient colors.
+
+ explicit GradientFillModel();
+
+ /** Reads BIFF12 gradient settings from a FILL or DXF record. */
+ void readGradient( SequenceInputStream& rStrm );
+ /** Reads BIFF12 gradient stop settings from a FILL or DXF record. */
+ void readGradientStop( SequenceInputStream& rStrm, bool bDxf );
+};
+
+/** Contains API fill attributes. */
+struct ApiSolidFillData
+{
+ ::Color mnColor; /// Fill color.
+ model::ComplexColor maComplexColor;
+ ::Color mnFilterColor; /// Fill color filtering.
+ bool mbTransparent; /// True = transparent area.
+ bool mbUsed; /// True = fill data is valid.
+
+ explicit ApiSolidFillData();
+};
+
+/** Contains cell fill attributes, either a pattern fill or a gradient fill. */
+class Fill : public WorkbookHelper
+{
+public:
+ explicit Fill( const WorkbookHelper& rHelper, bool bDxf );
+
+ /** Sets attributes of a patternFill element. */
+ void importPatternFill( const AttributeList& rAttribs );
+ /** Sets the pattern color from the fgColor element. */
+ void importFgColor( const AttributeList& rAttribs );
+ /** Sets the background color from the bgColor element. */
+ void importBgColor( const AttributeList& rAttribs );
+ /** Sets attributes of a gradientFill element. */
+ void importGradientFill( const AttributeList& rAttribs );
+ /** Sets a color from the color element in a gradient fill. */
+ void importColor( const AttributeList& rAttribs, double fPosition );
+
+ /** Imports the FILL record from the passed stream. */
+ void importFill( SequenceInputStream& rStrm );
+ /** Imports the fill pattern from a DXF record. */
+ void importDxfPattern( SequenceInputStream& rStrm );
+ /** Imports the pattern color from a DXF record. */
+ void importDxfFgColor( SequenceInputStream& rStrm );
+ /** Imports the background color from a DXF record. */
+ void importDxfBgColor( SequenceInputStream& rStrm );
+ /** Imports gradient settings from a DXF record. */
+ void importDxfGradient( SequenceInputStream& rStrm );
+ /** Imports gradient stop settings from a DXF record. */
+ void importDxfStop( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ typedef std::shared_ptr< PatternFillModel > PatternModelRef;
+ typedef std::shared_ptr< GradientFillModel > GradientModelRef;
+
+ PatternModelRef mxPatternModel;
+ GradientModelRef mxGradientModel;
+ ApiSolidFillData maApiData;
+ bool mbDxf;
+};
+
+typedef std::shared_ptr< Fill > FillRef;
+
+/** Contains all data for a cell format or cell style. */
+struct XfModel
+{
+ sal_Int32 mnStyleXfId; /// Index to parent style XF.
+ sal_Int32 mnFontId; /// Index to font data list.
+ sal_Int32 mnNumFmtId; /// Index to number format list.
+ sal_Int32 mnBorderId; /// Index to list of cell borders.
+ sal_Int32 mnFillId; /// Index to list of cell areas.
+ bool mbCellXf; /// True = cell XF, false = style XF.
+ bool mbFontUsed; /// True = font index used.
+ bool mbNumFmtUsed; /// True = number format used.
+ bool mbAlignUsed; /// True = alignment used.
+ bool mbProtUsed; /// True = cell protection used.
+ bool mbBorderUsed; /// True = border data used.
+ bool mbAreaUsed; /// True = area data used.
+
+ explicit XfModel();
+};
+
+bool operator==( const XfModel& rXfModel1, const XfModel& rXfModel2 );
+
+/** Represents a cell format or a cell style (called XF, extended format).
+
+ This class stores the type (cell/style), the index to the parent style (if
+ it is a cell format) and all "attribute used" flags, which reflect the
+ state of specific attribute groups (true = user has changed the attributes)
+ and all formatting data.
+ */
+class Xf : public WorkbookHelper
+{
+ friend bool operator==( const Xf& rXf1, const Xf& rXf2 );
+public:
+ struct AttrList
+ {
+ std::vector<ScAttrEntry> maAttrs;
+ bool mbLatinNumFmtOnly;
+ const ScPatternAttr* mpDefPattern;
+
+ AttrList(const ScPatternAttr* pDefPatternAttr);
+ };
+
+ explicit Xf( const WorkbookHelper& rHelper );
+
+ /** Sets all attributes from the xf element. */
+ void importXf( const AttributeList& rAttribs, bool bCellXf );
+ /** Sets all attributes from the alignment element. */
+ void importAlignment( const AttributeList& rAttribs );
+ /** Sets all attributes from the protection element. */
+ void importProtection( const AttributeList& rAttribs );
+
+ /** Imports the XF record from the passed stream. */
+ void importXf( SequenceInputStream& rStrm, bool bCellXf );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns true, if the XF is a cell XF, and false, if it is a style XF. */
+ bool isCellXf() const { return maModel.mbCellXf; }
+
+ /** Returns the referred font object. */
+ FontRef getFont() const;
+ /** Returns the alignment data of this style. */
+ const Alignment& getAlignment() const { return maAlignment; }
+
+ void applyPatternToAttrList(
+ AttrList& rAttrs, SCROW nRow1, SCROW nRow2, sal_Int32 nXfId,
+ sal_Int32 nForceScNumFmt, ScPatternCache& rCache );
+
+ void writeToDoc( ScDocumentImport& rDoc, const ScRange& rRange );
+
+ const ::ScPatternAttr& createPattern( bool bSkipPoolDefs = false );
+
+private:
+ typedef ::std::unique_ptr< ::ScPatternAttr > ScPatternAttrPtr;
+
+ ScPatternAttrPtr mpPattern; /// Calc item set.
+ sal_uInt32 mnScNumFmt; /// Calc number format.
+
+ XfModel maModel; /// Cell XF or style XF model data.
+ Alignment maAlignment; /// Cell alignment data.
+ Protection maProtection; /// Cell protection data.
+ sal_Int32 meRotationRef; /// Rotation reference dependent on border.
+ ::ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+};
+
+bool operator==( const Xf& rXf1, const Xf& rXf2 );
+
+typedef std::shared_ptr< Xf > XfRef;
+
+class Dxf : public WorkbookHelper
+{
+public:
+ explicit Dxf( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty font object. */
+ FontRef const & createFont( bool bAlwaysNew = true );
+ /** Creates a new empty border object. */
+ BorderRef const & createBorder( bool bAlwaysNew = true );
+ /** Creates a new empty fill object. */
+ FillRef const & createFill( bool bAlwaysNew = true );
+
+ /** Inserts a new number format code. */
+ void importNumFmt( const AttributeList& rAttribs );
+
+ /** Imports the DXF record from the passed stream. */
+ void importDxf( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ void fillToItemSet( SfxItemSet& rSet ) const;
+
+private:
+ FontRef mxFont; /// Font data.
+ NumberFormatRef mxNumFmt; /// Number format data.
+ std::shared_ptr< Alignment >
+ mxAlignment; /// Alignment data.
+ std::shared_ptr< Protection >
+ mxProtection; /// Protection data.
+ BorderRef mxBorder; /// Border data.
+ FillRef mxFill; /// Fill data.
+};
+
+typedef std::shared_ptr< Dxf > DxfRef;
+
+/** Contains attributes of a cell style, e.g. from the cellStyle element. */
+struct CellStyleModel
+{
+ OUString maName; /// Cell style name.
+ sal_Int32 mnXfId; /// Formatting for this cell style.
+ sal_Int32 mnBuiltinId; /// Identifier for builtin styles.
+ sal_Int32 mnLevel; /// Level for builtin column/row styles.
+ bool mbBuiltin; /// True = builtin style.
+ bool mbCustom; /// True = customized builtin style.
+ bool mbHidden; /// True = style not visible in GUI.
+
+ explicit CellStyleModel();
+
+ /** Returns true, if this style is a builtin style. */
+ bool isBuiltin() const;
+ /** Returns true, if this style represents the default document cell style. */
+ bool isDefaultStyle() const;
+};
+
+class CellStyle : public WorkbookHelper
+{
+public:
+ explicit CellStyle( const WorkbookHelper& rHelper );
+
+ /** Imports passed attributes from the cellStyle element. */
+ void importCellStyle( const AttributeList& rAttribs );
+ /** Imports style settings from a CELLSTYLE record. */
+ void importCellStyle( SequenceInputStream& rStrm );
+
+ /** Creates the style sheet in the document described by this cell style object. */
+ void createCellStyle();
+ /** Stores the passed final style name and creates the cell style, if it is
+ user-defined or modified built-in. */
+ void finalizeImport( const OUString& rFinalName );
+
+ /** Returns the cell style model structure. */
+ const CellStyleModel& getModel() const { return maModel; }
+ /** Returns the final style name used in the document. */
+ const OUString& getFinalStyleName() const { return maFinalName; }
+ ::ScStyleSheet* getStyleSheet() { return mpStyleSheet; }
+private:
+ CellStyleModel maModel;
+ OUString maFinalName; /// Final style name used in API.
+ bool mbCreated; /// True = style sheet created.
+ ::ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+
+};
+
+typedef std::shared_ptr< CellStyle > CellStyleRef;
+
+class CellStyleBuffer : public WorkbookHelper
+{
+public:
+ explicit CellStyleBuffer( const WorkbookHelper& rHelper );
+
+ /** Appends and returns a new named cell style object. */
+ CellStyleRef importCellStyle( const AttributeList& rAttribs );
+ /** Imports the CELLSTYLE record from the passed stream. */
+ CellStyleRef importCellStyle( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the XF identifier associated to the default cell style. */
+ sal_Int32 getDefaultXfId() const;
+ /** Returns the default style sheet for unused cells. */
+ OUString getDefaultStyleName() const;
+ /** Creates the style sheet described by the style XF with the passed identifier. */
+ OUString createCellStyle( sal_Int32 nXfId ) const;
+ ::ScStyleSheet* getCellStyleSheet( sal_Int32 nXfId ) const;
+
+private:
+ /** Inserts the passed cell style object into the internal maps. */
+ void insertCellStyle( CellStyleRef const & xCellStyle );
+ /** Creates the style sheet described by the passed cell style object. */
+ static OUString createCellStyle( const CellStyleRef& rxCellStyle );
+ static ::ScStyleSheet* getCellStyleSheet( const CellStyleRef& rxCellStyle );
+
+private:
+ typedef RefVector< CellStyle > CellStyleVector;
+ typedef RefMap< sal_Int32, CellStyle > CellStyleXfIdMap;
+
+ CellStyleVector maBuiltinStyles; /// All built-in cell styles.
+ CellStyleVector maUserStyles; /// All user defined cell styles.
+ CellStyleXfIdMap maStylesByXf; /// All cell styles, mapped by XF identifier.
+ CellStyleRef mxDefStyle; /// Default cell style.
+};
+
+struct AutoFormatModel
+{
+ sal_Int32 mnAutoFormatId; /// Index of predefined autoformatting.
+ bool mbApplyNumFmt; /// True = apply number format from autoformatting.
+ bool mbApplyFont; /// True = apply font from autoformatting.
+ bool mbApplyAlignment; /// True = apply alignment from autoformatting.
+ bool mbApplyBorder; /// True = apply border from autoformatting.
+ bool mbApplyFill; /// True = apply fill from autoformatting.
+ bool mbApplyProtection; /// True = apply protection from autoformatting.
+
+ explicit AutoFormatModel();
+};
+
+class StylesBuffer : public WorkbookHelper
+{
+public:
+ explicit StylesBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty font object. */
+ FontRef createFont();
+ /** Creates a number format. */
+ NumberFormatRef createNumFmt( sal_Int32 nNumFmtId, std::u16string_view aFmtCode );
+ sal_Int32 nextFreeNumFmtId();
+ /** Creates a new empty border object. */
+ BorderRef createBorder();
+ /** Creates a new empty fill object. */
+ FillRef createFill();
+ /** Creates a new empty cell formatting object. */
+ XfRef createCellXf();
+ /** Creates a new empty style formatting object. */
+ XfRef createStyleXf();
+ /** Creates a new empty differential formatting object. */
+ DxfRef createDxf();
+ DxfRef createExtDxf();
+
+ /** Appends a new color to the color palette. */
+ void importPaletteColor( const AttributeList& rAttribs );
+ /** Inserts a new number format code. */
+ NumberFormatRef importNumFmt( const AttributeList& rAttribs );
+ /** Appends and returns a new named cell style object. */
+ CellStyleRef importCellStyle( const AttributeList& rAttribs );
+
+ /** Appends a new color to the color palette. */
+ void importPaletteColor( SequenceInputStream& rStrm );
+ /** Imports the NUMFMT record from the passed stream. */
+ void importNumFmt( SequenceInputStream& rStrm );
+ /** Imports the CELLSTYLE record from the passed stream. */
+ void importCellStyle( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the palette color with the specified index. */
+ ::Color getPaletteColor( sal_Int32 nIndex ) const;
+ /** Returns the specified font object. */
+ FontRef getFont( sal_Int32 nFontId ) const;
+ /** Returns the specified border object. */
+ BorderRef getBorder( sal_Int32 nBorderId ) const;
+ /** Returns the specified cell format object. */
+ XfRef getCellXf( sal_Int32 nXfId ) const;
+ /** Returns the specified style format object. */
+ XfRef getStyleXf( sal_Int32 nXfId ) const;
+
+ /** Returns the font object of the specified cell XF. */
+ FontRef getFontFromCellXf( sal_Int32 nXfId ) const;
+ /** Returns the default application font (used in the "Normal" cell style). */
+ FontRef getDefaultFont() const;
+ /** Returns the model of the default application font (used in the "Normal" cell style). */
+ const FontModel& getDefaultFontModel() const;
+
+ /** Returns true, if the specified borders are equal. */
+ static bool equalBorders( sal_Int32 nBorderId1, sal_Int32 nBorderId2 );
+ /** Returns true, if the specified fills are equal. */
+ static bool equalFills( sal_Int32 nFillId1, sal_Int32 nFillId2 );
+
+ /** Returns the default style sheet for unused cells. */
+ OUString getDefaultStyleName() const;
+ /** Creates the style sheet described by the style XF with the passed identifier. */
+ OUString createCellStyle( sal_Int32 nXfId ) const;
+ ::ScStyleSheet* getCellStyleSheet( sal_Int32 nXfId ) const;
+ /** Creates the style sheet described by the DXF with the passed identifier. */
+ OUString createDxfStyle( sal_Int32 nDxfId ) const;
+ OUString createExtDxfStyle( sal_Int32 nDxfId ) const;
+
+ void writeFontToItemSet( SfxItemSet& rItemSet, sal_Int32 nFontId, bool bSkipPoolDefs ) const;
+ sal_uInt32 writeNumFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const;
+ /** Writes the specified number format to the passed property map. */
+ void writeBorderToItemSet( SfxItemSet& rItemSet, sal_Int32 nBorderId, bool bSkipPoolDefs ) const;
+ /** Writes the fill attributes of the specified fill data to the passed property map. */
+ void writeFillToItemSet( SfxItemSet& rItemSet, sal_Int32 nFillId, bool bSkipPoolDefs ) const;
+
+ /** Writes the cell formatting attributes of the specified XF to the passed property set. */
+ void writeCellXfToDoc( ScDocumentImport& rDoc, const ScRange& rRange, sal_Int32 nXfId ) const;
+ const RefVector< Dxf >& getExtDxfs() const { return maExtDxfs; }
+
+private:
+ typedef RefVector< Font > FontVector;
+ typedef RefVector< Border > BorderVector;
+ typedef RefVector< Fill > FillVector;
+ typedef RefVector< Xf > XfVector;
+ typedef RefVector< Dxf > DxfVector;
+ typedef ::std::map< sal_Int32, OUString > DxfStyleMap;
+
+ ColorPalette maPalette; /// Color palette.
+ FontVector maFonts; /// List of font objects.
+ NumberFormatsBuffer maNumFmts; /// List of all number format codes.
+ BorderVector maBorders; /// List of cell border objects.
+ FillVector maFills; /// List of cell area fill objects.
+ XfVector maCellXfs; /// List of cell formats.
+ XfVector maStyleXfs; /// List of cell styles.
+ CellStyleBuffer maCellStyles; /// All built-in and user defined cell styles.
+ DxfVector maDxfs; /// List of differential cell styles.
+ DxfVector maExtDxfs; /// List of differential extlst cell styles.
+ mutable DxfStyleMap maDxfStyles; /// Maps DXF identifiers to Calc style sheet names.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/stylesfragment.hxx b/sc/source/filter/inc/stylesfragment.hxx
new file mode 100644
index 0000000000..84ba3a0a32
--- /dev/null
+++ b/sc/source/filter/inc/stylesfragment.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <utility>
+
+#include "excelhandlers.hxx"
+#include "stylesbuffer.hxx"
+
+namespace oox::xls {
+
+class IndexedColorsContext : public WorkbookContextBase
+{
+public:
+ explicit IndexedColorsContext( WorkbookFragmentBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+};
+
+class FontContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit FontContext( ParentType& rParent, FontRef xFont ) :
+ WorkbookContextBase( rParent ), mxFont(std::move( xFont )) {}
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ FontRef mxFont;
+};
+
+class BorderContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit BorderContext( ParentType& rParent, BorderRef xBorder ) :
+ WorkbookContextBase( rParent ), mxBorder(std::move( xBorder )) {}
+
+protected:
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ BorderRef mxBorder;
+};
+
+class FillContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit FillContext( ParentType& rParent, FillRef xFill ) :
+ WorkbookContextBase( rParent ), mxFill(std::move( xFill )), mfGradPos( -1.0 ) {}
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ FillRef mxFill;
+ double mfGradPos; /// Gradient color position.
+};
+
+class XfContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit XfContext( ParentType& rParent, XfRef xXf, bool bCellXf ) :
+ WorkbookContextBase( rParent ), mxXf(std::move( xXf )), mbCellXf( bCellXf ) {}
+
+protected:
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ XfRef mxXf;
+ bool mbCellXf; /// True = cell XF, false = style XF.
+};
+
+class DxfContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit DxfContext( ParentType& rParent, const DxfRef& rxDxf ) :
+ WorkbookContextBase( rParent ), mxDxf( rxDxf ), mxExtDxf( rxDxf ) {}
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ DxfRef mxDxf;
+ DxfRef mxExtDxf;
+};
+
+class StylesFragment : public WorkbookFragmentBase
+{
+public:
+ explicit StylesFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablebuffer.hxx b/sc/source/filter/inc/tablebuffer.hxx
new file mode 100644
index 0000000000..66f6816c1e
--- /dev/null
+++ b/sc/source/filter/inc/tablebuffer.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "autofilterbuffer.hxx"
+#include "tablecolumnsbuffer.hxx"
+#include "workbookhelper.hxx"
+
+namespace oox::xls {
+
+struct TableModel
+{
+ ScRange maRange; /// Original (unchecked) range of the table.
+ OUString maProgName; /// Programmatical name.
+ OUString maDisplayName; /// Display name.
+ sal_Int32 mnId; /// Unique table identifier.
+ sal_Int32 mnType; /// Table type (worksheet, query, etc.).
+ sal_Int32 mnHeaderRows; /// Number of header rows.
+ sal_Int32 mnTotalsRows; /// Number of totals rows.
+
+ explicit TableModel();
+};
+
+class Table : public WorkbookHelper
+{
+public:
+ explicit Table( const WorkbookHelper& rHelper );
+
+ /** Imports a table definition from the passed attributes. */
+ void importTable( const AttributeList& rAttribs, sal_Int16 nSheet );
+ /** Imports a table definition from a TABLE record. */
+ void importTable( SequenceInputStream& rStrm, sal_Int16 nSheet );
+ /** Creates a new auto filter and stores it internally. */
+ AutoFilter& createAutoFilter() { return maAutoFilters.createAutoFilter(); }
+ /** Creates a new tableColumns handler and stores it internally. */
+ TableColumns& createTableColumns() { return maTableColumns.createTableColumns(); }
+
+ /** Creates a database range from this tables. */
+ void finalizeImport();
+ void applyAutoFilters();
+ void applyTableColumns();
+
+ /** Returns the unique table identifier. */
+ sal_Int32 getTableId() const { return maModel.mnId; }
+ /** Returns the token index used in API token arrays (com.sun.star.sheet.FormulaToken). */
+ sal_Int32 getTokenIndex() const { return mnTokenIndex; }
+ /** Returns the original display name of the table. */
+ const OUString& getDisplayName() const { return maModel.maDisplayName; }
+
+ /** Returns the original (unchecked) total range of the table. */
+ const ScRange& getOriginalRange() const { return maModel.maRange; }
+ /** Returns the cell range of this table. */
+ const ScRange& getRange() const { return maDestRange; }
+ /** Returns the number of columns of this table. */
+ SCCOL getWidth() const { return maDestRange.aEnd.Col() - maDestRange.aStart.Col() + 1; }
+ /** Returns the number of rows of this table. */
+ SCROW getHeight() const { return maDestRange.aEnd.Row() - maDestRange.aStart.Row() + 1; }
+ /** Returns the number of header rows in the table range. */
+ sal_Int32 getHeaderRows() const { return maModel.mnHeaderRows; }
+ /** Returns the number of totals rows in the table range. */
+ sal_Int32 getTotalsRows() const { return maModel.mnTotalsRows; }
+
+private:
+ TableModel maModel;
+ AutoFilterBuffer maAutoFilters; /// Filter settings for this table.
+ TableColumnsBuffer maTableColumns; /// Column names of this table.
+ OUString maDBRangeName; /// Name of the database range in the Calc document.
+ ScRange maDestRange; /// Validated range of the table in the worksheet.
+ sal_Int32 mnTokenIndex; /// Token index used in API token array.
+};
+
+typedef std::shared_ptr< Table > TableRef;
+
+class TableBuffer : public WorkbookHelper
+{
+public:
+ explicit TableBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty table. */
+ Table& createTable();
+
+ /** Creates database ranges from all imported tables. */
+ void finalizeImport();
+ /** Applies autofilters from created database range ( requires finalizeImport to have run before being called */
+ void applyAutoFilters();
+ /** Applies columns names from created database range ( requires finalizeImport to have run before being called */
+ void applyTableColumns();
+ /** Returns a table by its identifier. */
+ TableRef getTable( sal_Int32 nTableId ) const;
+ /** Returns a table by its display name. */
+ TableRef getTable( const OUString& rDispName ) const;
+
+private:
+ /** Inserts the passed table into the maps according to its identifier and name. */
+ void insertTableToMaps( const TableRef& rxTable );
+
+private:
+ typedef RefVector< Table > TableVector;
+
+ TableVector maTables;
+ RefMap< sal_Int32, Table > maIdTables;
+ RefMap< OUString, Table > maNameTables;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablecolumnsbuffer.hxx b/sc/source/filter/inc/tablecolumnsbuffer.hxx
new file mode 100644
index 0000000000..f54c2eb553
--- /dev/null
+++ b/sc/source/filter/inc/tablecolumnsbuffer.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 .
+ */
+
+#pragma once
+
+#include <oox/helper/refvector.hxx>
+#include <dbdata.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+class ScDBData;
+
+namespace oox::xls {
+
+/** A column in a table (database range).
+ */
+class TableColumn : public WorkbookHelper
+{
+public:
+ explicit TableColumn( const WorkbookHelper& rHelper );
+
+ /** Imports table column settings from the tableColumn element. */
+ void importTableColumn( const AttributeList& rAttribs );
+ /** Imports table column settings from the TABLECOLUMN (?) record. */
+ void importTableColumn( SequenceInputStream& rStrm );
+ /** Gets the name of this column. */
+ const OUString& getName() const;
+ /** Gets the attributes of this column. */
+ const TableColumnAttributes& getColumnAttributes() const;
+
+private:
+ OUString maName;
+ sal_Int32 mnId;
+ sal_Int32 mnDataDxfId;
+ TableColumnAttributes maColumnAttributes;
+};
+
+class TableColumns : public WorkbookHelper
+{
+public:
+ explicit TableColumns( const WorkbookHelper& rHelper );
+
+ /** Imports settings from the tableColumns element. */
+ void importTableColumns( const AttributeList& rAttribs );
+ /** Imports settings from the TABLECOLUMNS (?) record. */
+ void importTableColumns( SequenceInputStream& rStrm );
+
+ /** Creates a new table column and stores it internally. */
+ TableColumn& createTableColumn();
+
+ /** Applies the columns to the passed database range. */
+ bool finalizeImport( ScDBData* pDBData );
+
+private:
+ typedef RefVector< TableColumn > TableColumnVector;
+
+ TableColumnVector maTableColumnVector;
+ sal_Int32 mnCount;
+};
+
+class TableColumnsBuffer : public WorkbookHelper
+{
+public:
+ explicit TableColumnsBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new table columns object and stores it internally. */
+ TableColumns& createTableColumns();
+
+ /** Applies the table columns to the passed database range. */
+ void finalizeImport( ScDBData* pDBData );
+
+private:
+ /** Returns the table columns object used. */
+ TableColumns* getActiveTableColumns();
+
+private:
+ typedef RefVector< TableColumns > TableColumnsVector;
+ TableColumnsVector maTableColumnsVector;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablecolumnscontext.hxx b/sc/source/filter/inc/tablecolumnscontext.hxx
new file mode 100644
index 0000000000..1f5a5ba046
--- /dev/null
+++ b/sc/source/filter/inc/tablecolumnscontext.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class TableColumn;
+class TableColumns;
+
+class TableColumnContext : public WorksheetContextBase
+{
+public:
+ explicit TableColumnContext( WorksheetContextBase& rParent, TableColumn& rTableColumn );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ TableColumn& mrTableColumn;
+};
+
+class TableColumnsContext : public WorksheetContextBase
+{
+public:
+ explicit TableColumnsContext( WorksheetFragmentBase& rFragment, TableColumns& rTableColumns );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ TableColumns& mrTableColumns;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablefragment.hxx b/sc/source/filter/inc/tablefragment.hxx
new file mode 100644
index 0000000000..7aad73e03e
--- /dev/null
+++ b/sc/source/filter/inc/tablefragment.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 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class Table;
+
+class TableFragment : public WorksheetFragmentBase
+{
+public:
+ explicit TableFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ Table& mrTable;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/themebuffer.hxx b/sc/source/filter/inc/themebuffer.hxx
new file mode 100644
index 0000000000..f1443a0730
--- /dev/null
+++ b/sc/source/filter/inc/themebuffer.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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <oox/drawingml/theme.hxx>
+#include "workbookhelper.hxx"
+#include "stylesbuffer.hxx"
+
+namespace oox::xls {
+
+class ThemeBuffer : public ::oox::drawingml::Theme, public WorkbookHelper
+{
+public:
+ explicit ThemeBuffer( const WorkbookHelper& rHelper );
+ virtual ~ThemeBuffer() override;
+
+ /** Returns the theme color with the specified token identifier. */
+ ::Color getColorByToken( sal_Int32 nToken ) const;
+
+ ::Color getColorByIndex(size_t nIndex) const;
+
+ /** Returns the default font data for the current file type. */
+ const FontModel& getDefaultFontModel() const { return *mxDefFontModel; }
+
+private:
+ typedef ::std::unique_ptr< FontModel > FontModelPtr;
+ FontModelPtr mxDefFontModel;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tokstack.hxx b/sc/source/filter/inc/tokstack.hxx
new file mode 100644
index 0000000000..66c1918eab
--- /dev/null
+++ b/sc/source/filter/inc/tokstack.hxx
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <tokenarray.hxx>
+#include <refdata.hxx>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+typedef OpCode DefTokenId;
+// in PRODUCT version: ambiguity between OpCode (being sal_uInt16) and UINT16
+// Unfortunately a typedef is just a dumb alias and not a real type ...
+//typedef sal_uInt16 TokenId;
+struct TokenId
+{
+ sal_uInt16 nId;
+
+ TokenId() : nId( 0 ) {}
+ TokenId( sal_uInt16 n ) : nId( n ) {}
+ TokenId( const TokenId& r ) : nId( r.nId ) {}
+ TokenId& operator =( const TokenId& r ) { nId = r.nId; return *this; }
+ TokenId& operator =( sal_uInt16 n ) { nId = n; return *this; }
+ operator const sal_uInt16&() const { return nId; }
+};
+
+class TokenStack;
+
+enum E_TYPE
+{
+ T_Id, // Id-Folge
+ T_Str, // String
+ T_D, // Double
+ T_Err, // Error code
+ T_RefC, // Cell Reference
+ T_RefA, // Area Reference
+ T_RN, // Range Name
+ T_Ext, // something unknown with function name
+ T_Nlf, // token for natural language formula
+ T_Matrix, // token for inline arrays
+ T_ExtName, // token for external names
+ T_ExtRefC,
+ T_ExtRefA
+};
+
+template<typename T, int InitialCapacity>
+struct TokenPoolPool
+{
+ std::unique_ptr<T[]> ppP_Str;
+ sal_uInt16 m_capacity;
+ sal_uInt16 m_writemark;
+
+ TokenPoolPool() :
+ ppP_Str( new T[InitialCapacity] ),
+ m_capacity(InitialCapacity),
+ m_writemark(0)
+ {
+ }
+ bool Grow(sal_uInt16 nByMin = 1)
+ {
+ sal_uInt16 nP_StrNew = lcl_canGrow(m_capacity, nByMin);
+ if (!nP_StrNew)
+ return false;
+
+ T* ppP_StrNew = new T[ nP_StrNew ];
+
+ for( sal_uInt16 i = 0 ; i < m_capacity ; i++ )
+ ppP_StrNew[ i ] = std::move(ppP_Str[ i ]);
+
+ m_capacity = nP_StrNew;
+
+ ppP_Str.reset( ppP_StrNew );
+ return true;
+ }
+ /** Returns the new number of elements, or 0 if overflow. */
+ static sal_uInt16 lcl_canGrow( sal_uInt16 nOld, sal_uInt16 nByMin )
+ {
+ if (!nOld)
+ return nByMin ? nByMin : 1;
+ if (nOld == SAL_MAX_UINT16)
+ return 0;
+ sal_uInt32 nNew = ::std::max( static_cast<sal_uInt32>(nOld) * 2,
+ static_cast<sal_uInt32>(nOld) + nByMin);
+ if (nNew > SAL_MAX_UINT16)
+ nNew = SAL_MAX_UINT16;
+ if (nNew - nByMin < nOld)
+ nNew = 0;
+ return static_cast<sal_uInt16>(nNew);
+ }
+ T* getIfInRange(sal_uInt16 n) const
+ {
+ return ( n < m_capacity ) ? &ppP_Str[ n ] : nullptr;
+ }
+ T const & operator[](sal_uInt16 n) const
+ {
+ return ppP_Str[ n ];
+ }
+ T & operator[](sal_uInt16 n)
+ {
+ return ppP_Str[ n ];
+ }
+};
+
+class TokenPool
+{
+ // !ATTENTION!: external Id-Basis is 1, internal 0!
+ // return Id = 0 -> Error
+private:
+ svl::SharedStringPool& mrStringPool;
+
+ TokenPoolPool<std::unique_ptr<OUString>, 4>
+ ppP_Str; // Pool for Strings
+
+ TokenPoolPool<double, 8> pP_Dbl; // Pool for Doubles
+
+ TokenPoolPool<sal_uInt16, 8>
+ pP_Err; // Pool for error codes
+
+ TokenPoolPool<std::unique_ptr<ScSingleRefData>, 32>
+ ppP_RefTr; // Pool for References
+ std::unique_ptr<sal_uInt16[]> pP_Id; // Pool for Id-sets
+ sal_uInt16 nP_Id;
+ sal_uInt16 nP_IdCurrent;
+ sal_uInt16 nP_IdLast; // last set-start
+
+ struct EXTCONT
+ {
+ DefTokenId eId;
+ OUString aText;
+ EXTCONT( const DefTokenId e, OUString a ) :
+ eId( e ), aText(std::move( a )){}
+ };
+ TokenPoolPool<std::unique_ptr<EXTCONT>, 32>
+ ppP_Ext;
+
+ TokenPoolPool<std::unique_ptr<ScSingleRefData>, 16>
+ ppP_Nlf;
+
+ std::unique_ptr<ScMatrix*[]> ppP_Matrix; // Pool for Matrices
+ sal_uInt16 nP_Matrix;
+ sal_uInt16 nP_MatrixCurrent;
+
+ /** for storage of named ranges */
+ struct RangeName
+ {
+ sal_uInt16 mnIndex;
+ sal_Int16 mnSheet;
+ };
+ ::std::vector<RangeName> maRangeNames;
+
+ /** for storage of external names */
+ struct ExtName
+ {
+ sal_uInt16 mnFileId;
+ OUString maName;
+ };
+ ::std::vector<ExtName> maExtNames;
+
+ /** for storage of external cell references */
+ struct ExtCellRef
+ {
+ OUString maTabName;
+ ScSingleRefData maRef;
+ sal_uInt16 mnFileId;
+ };
+ ::std::vector<ExtCellRef> maExtCellRefs;
+
+ /** for storage of external area references */
+ struct ExtAreaRef
+ {
+ OUString maTabName;
+ ScComplexRefData maRef;
+ sal_uInt16 mnFileId;
+ };
+ ::std::vector<ExtAreaRef> maExtAreaRefs;
+
+ std::unique_ptr<sal_uInt16[]> pElement; // Array with Indices for elements
+ std::unique_ptr<E_TYPE[]> pType; // ...with Type-Info
+ std::unique_ptr<sal_uInt16[]> pSize; // ...with size
+ sal_uInt16 nElement;
+ sal_uInt16 nElementCurrent;
+
+ static const sal_uInt16 nScTokenOff;// Offset for SC-Token
+#ifdef DBG_UTIL
+ sal_uInt16 m_nRek; // recursion counter
+#endif
+
+ bool GrowTripel( sal_uInt16 nByMin );
+ bool GrowId();
+ bool GrowElement();
+ bool GrowMatrix();
+ /** @return false means nElementCurrent range
+ below nScTokenOff would overflow or
+ further allocation is not possible, no
+ new ID available other than
+ nElementCurrent+1.
+ */
+ bool CheckElementOrGrow();
+ bool GetElement( const sal_uInt16 nId, ScTokenArray* pScToken );
+ bool GetElementRek( const sal_uInt16 nId, ScTokenArray* pScToken );
+ void ClearMatrix();
+public:
+ TokenPool( svl::SharedStringPool& rSPool );
+ ~TokenPool();
+ inline TokenPool& operator <<( const TokenId& rId );
+ inline TokenPool& operator <<( const DefTokenId eId );
+ inline TokenPool& operator <<( TokenStack& rStack );
+ void operator >>( TokenId& rId );
+ inline void operator >>( TokenStack& rStack );
+ inline TokenId Store();
+ TokenId Store( const double& rDouble );
+
+ // only for Range-Names
+ TokenId Store( const sal_uInt16 nIndex );
+
+ TokenId Store( const OUString& rString );
+ TokenId Store( const ScSingleRefData& rTr );
+ TokenId Store( const ScComplexRefData& rTr );
+
+ TokenId Store( const DefTokenId eId, const OUString& rName );
+ // 4 externals (e.g. AddIns, Macros...)
+ TokenId StoreNlf( const ScSingleRefData& rTr );
+ TokenId StoreMatrix();
+ TokenId StoreName( sal_uInt16 nIndex, sal_Int16 nSheet );
+ TokenId StoreExtName( sal_uInt16 nFileId, const OUString& rName );
+ TokenId StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef );
+ TokenId StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef );
+
+ std::unique_ptr<ScTokenArray> GetTokenArray( const ScDocument& rDoc, const TokenId& rId );
+ void Reset();
+ bool IsSingleOp( const TokenId& rId, const DefTokenId eId ) const;
+ const OUString* GetExternal( const TokenId& rId ) const;
+ ScMatrix* GetMatrix( unsigned int n ) const;
+};
+
+class TokenStack
+ // Stack for Token-Ids: reserve Id=0 for error; e.g. Get() returns 0 on error
+
+{
+ private:
+ std::unique_ptr<TokenId[]> pStack; // Stack as Array
+ sal_uInt16 nPos; // Write-mark
+ static const sal_uInt16 nSize = 1024; // first Index outside of stack
+ public:
+ TokenStack();
+ ~TokenStack();
+ inline TokenStack& operator <<( const TokenId& rNewId );
+ inline void operator >>( TokenId &rId );
+
+ inline void Reset();
+
+ bool HasMoreTokens() const { return nPos > 0; }
+ inline TokenId Get();
+};
+
+inline TokenId TokenStack::Get()
+{
+ TokenId nRet;
+
+ if( nPos == 0 )
+ {
+ SAL_WARN("sc.filter", "*TokenStack::Get(): is empty, is empty, ...");
+ nRet = 0;
+ }
+ else
+ {
+ nPos--;
+ nRet = pStack[ nPos ];
+ }
+
+ return nRet;
+}
+
+inline TokenStack &TokenStack::operator <<( const TokenId& rNewId )
+{// Element on Stack
+ if( nPos < nSize )
+ {
+ pStack[ nPos ] = rNewId;
+ nPos++;
+ }
+ else
+ {
+ SAL_WARN("sc.filter", "*TokenStack::<<(): Stack overflow for " << static_cast<sal_uInt16>(rNewId));
+ }
+
+ return *this;
+}
+
+inline void TokenStack::operator >>( TokenId& rId )
+{// Element of Stack
+ if( nPos > 0 )
+ {
+ nPos--;
+ rId = pStack[ nPos ];
+ }
+ else
+ {
+ SAL_WARN("sc.filter", "*TokenStack::>>(): is empty, is empty, ...");
+ rId = 0;
+ }
+}
+
+inline void TokenStack::Reset()
+{
+ nPos = 0;
+}
+
+inline TokenPool& TokenPool::operator <<( const TokenId& rId )
+{
+ // POST: rId's are stored consecutively in Pool under a new Id;
+ // finalize with >> or Store()
+ // rId -> ( sal_uInt16 ) rId - 1;
+ sal_uInt16 nId = static_cast<sal_uInt16>(rId);
+ if (nId == 0)
+ {
+ // This would result in nId-1==0xffff, create error.
+ SAL_WARN("sc.filter", "-TokenPool::operator <<: TokenId 0");
+ nId = static_cast<sal_uInt16>(ocErrNull) + nScTokenOff + 1;
+ }
+ else if (nId >= nScTokenOff)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::operator <<: TokenId in DefToken-Range! " << static_cast<sal_uInt16>(rId));
+
+ // Do not "invent" OpCode values by arbitrarily mapping into the Calc
+ // space. This badly smells like an overflow or binary garbage, so
+ // treat as error.
+ nId = static_cast<sal_uInt16>(ocErrNull) + nScTokenOff + 1;
+ }
+
+ if( nP_IdCurrent >= nP_Id && !GrowId())
+ return *this;
+
+ pP_Id[ nP_IdCurrent ] = nId - 1;
+ nP_IdCurrent++;
+
+ return *this;
+}
+
+inline TokenPool& TokenPool::operator <<( const DefTokenId eId )
+{
+ if (static_cast<sal_uInt32>(eId) + nScTokenOff >= 0xFFFF)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::operator<<: enum too large! " << static_cast<sal_uInt32>(eId));
+ }
+
+ if( nP_IdCurrent >= nP_Id && !GrowId())
+ return *this;
+
+ pP_Id[ nP_IdCurrent ] = static_cast<sal_uInt16>(eId) + nScTokenOff;
+ nP_IdCurrent++;
+
+ return *this;
+}
+
+inline TokenPool& TokenPool::operator <<( TokenStack& rStack )
+{
+ if( nP_IdCurrent >= nP_Id && !GrowId())
+ return *this;
+
+ sal_uInt16 nId = static_cast<sal_uInt16>(rStack.Get());
+ if (nId == 0)
+ {
+ // Indicates error, so generate one. Empty stack, overflow, ...
+ nId = static_cast<sal_uInt16>(ocErrNull) + nScTokenOff + 1;
+ }
+ pP_Id[ nP_IdCurrent ] = nId - 1;
+ nP_IdCurrent++;
+
+ return *this;
+}
+
+inline void TokenPool::operator >>( TokenStack& rStack )
+{
+ TokenId nId;
+ *this >> nId;
+ rStack << nId;
+}
+
+inline TokenId TokenPool::Store()
+{
+ TokenId nId;
+ *this >> nId;
+ return nId;
+}
+
+inline std::unique_ptr<ScTokenArray> TokenPool::GetTokenArray( const ScDocument& rDoc, const TokenId& rId )
+{
+ std::unique_ptr<ScTokenArray> pScToken( new ScTokenArray(rDoc) );
+
+ if( rId )
+ {//...only if rId > 0!
+#ifdef DBG_UTIL
+ m_nRek = 0;
+#endif
+ GetElement( static_cast<sal_uInt16>(rId) - 1, pScToken.get());
+ }
+
+ return pScToken;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tool.h b/sc/source/filter/inc/tool.h
new file mode 100644
index 0000000000..786e7f6698
--- /dev/null
+++ b/sc/source/filter/inc/tool.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <document.hxx>
+
+#include <i18nlangtag/lang.h>
+#include <svl/intitem.hxx>
+#include <types.hxx>
+#include <osl/diagnose.h>
+
+// Default values
+const sal_uInt8 nFractionalStd = 0; // Number of digits in fractional part for standard cells
+const sal_uInt8 nFractionalFloat = 2; // " " " " float cells
+
+struct LotusContext;
+
+void PutFormString(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, char *pString);
+
+void SetFormat(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt8 nFormat, sal_uInt8 nSt);
+
+double SnumToDouble( sal_Int16 nVal );
+
+double Snum32ToDouble( sal_uInt32 nValue );
+
+typedef sal_uInt16 StampTyp;
+
+#define MAKE_STAMP(nF,nS) ((nS&0x0F)+((nF&0x7F)*16))
+ // Bit 0...3 = Bit 0...3 of number of digits
+ // Bit 4...10 = Bit 0...6 of Formatbyte
+
+class FormIdent
+{
+private:
+ StampTyp nStamp; // ID key
+ std::unique_ptr<SfxUInt32Item> pAttr; // associated attribute
+public:
+ FormIdent( void )
+ {
+ nStamp = 0;
+ pAttr = nullptr;
+ }
+
+ FormIdent( sal_uInt8 nFormat, sal_uInt8 nSt, SfxUInt32Item& rAttr )
+ {
+ nStamp = MAKE_STAMP( nFormat, nSt );
+ pAttr.reset(&rAttr);
+ }
+
+ StampTyp GetStamp( void ) const
+ {
+ return nStamp;
+ }
+
+ SfxUInt32Item* GetAttr( void )
+ {
+ return pAttr.get();
+ }
+
+ void SetStamp( sal_uInt8 nFormat, sal_uInt8 nSt )
+ {
+ nStamp = MAKE_STAMP( nFormat, nSt );
+ }
+};
+
+
+#define nSize_ 2048
+
+
+class FormCache
+{
+private:
+ FormIdent aIdents[ nSize_ ]; //buffered formats
+ bool bValid[ nSize_ ];
+ FormIdent aCompareIdent; // for comparing
+ SvNumberFormatter* pFormTable; // value format table anchor
+ StampTyp nIndex;
+ LanguageType eLanguage; // System language
+
+ SfxUInt32Item* NewAttr( sal_uInt8 nFormat, sal_uInt8 nSt );
+public:
+ FormCache( const ScDocument* );
+ ~FormCache();
+
+ inline const SfxUInt32Item* GetAttr( sal_uInt8 nFormat, sal_uInt8 nSt );
+};
+
+
+inline const SfxUInt32Item* FormCache::GetAttr( sal_uInt8 nFormat, sal_uInt8 nSt )
+{
+ // PREC: nFormat = Lotus format byte
+ // nSt = Number of digit
+ // POST: return = SC-format fitting nFormat and nSt
+ SfxUInt32Item* pAttr;
+ SfxUInt32Item* pRet;
+
+ aCompareIdent.SetStamp( nFormat, nSt );
+ nIndex = aCompareIdent.GetStamp();
+ OSL_ENSURE( nIndex < nSize_, "FormCache::GetAttr(): Oups... not this way!" );
+ if( bValid[ nIndex ] )
+ pRet = aIdents[ nIndex ].GetAttr();
+ else
+ {
+ // create new attribute
+ pAttr = NewAttr( nFormat, nSt );
+ OSL_ENSURE( pAttr, "FormCache::GetAttr(): Nothing to save" );
+
+ aIdents[ nIndex ] = FormIdent( nFormat, nSt, *pAttr );
+ bValid[ nIndex ] = true;
+
+ pRet = pAttr;
+ }
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/unitconverter.hxx b/sc/source/filter/inc/unitconverter.hxx
new file mode 100644
index 0000000000..f418eaaf04
--- /dev/null
+++ b/sc/source/filter/inc/unitconverter.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 .
+ */
+
+#pragma once
+
+#include <map>
+#include <vector>
+#include <o3tl/enumarray.hxx>
+#include "workbookhelper.hxx"
+
+namespace com::sun::star {
+ namespace util { struct Date; struct DateTime; }
+}
+
+namespace oox::xls {
+
+/** Units supported by the UnitConverter class. */
+enum class Unit
+{
+ Twip, /// Twips (1/20 point).
+ Emu, /// English Metric Unit (1/360,000 cm).
+ ScreenX, /// Horizontal screen pixels.
+ ScreenY, /// Vertical screen pixels.
+ Digit, /// Digit width of document default font.
+ Space, /// Space character width of document default font.
+ LAST
+};
+
+/** Helper class that provides functions to convert values from and to
+ different units.
+
+ Provides functions to calculate the width of certain characters of the
+ default font of the imported/exported document. The default font is always
+ the first font in the styles font list, and is always referenced by the
+ default cell style ("Normal" style in Excel) which is used by all empty
+ unformatted cells in the document. To be able to calculate the character
+ width correctly, the default font must be known, which is the case after
+ the finalizeImport() or finalizeExport() functions have been called. Caller
+ must make sure to not call the character width conversion functions before.
+ */
+class UnitConverter : public WorkbookHelper
+{
+public:
+ explicit UnitConverter( const WorkbookHelper& rHelper );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+ /** Updates internal nulldate for date/serial conversion. */
+ void finalizeNullDate( const css::util::Date& rNullDate );
+
+ /** Converts the passed value between the passed units. */
+ double scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const;
+
+ /** Returns the serial value of the passed datetime, based on current nulldate. */
+ double calcSerialFromDateTime( const css::util::DateTime& rDateTime ) const;
+ /** Returns the datetime of the passed serial value, based on current nulldate. */
+ css::util::DateTime calcDateTimeFromSerial( double fSerial ) const;
+
+ /** Returns a BIFF error code from the passed error string. */
+ sal_uInt8 calcBiffErrorCode( const OUString& rErrorCode ) const;
+
+ /** Returns an error string from the passed BIFF error code. */
+ OUString calcErrorString( sal_uInt8 nErrorCode ) const;
+
+private:
+ /** Adds an error code to the internal maps. */
+ void addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode );
+
+ /** Returns the conversion coefficient for the passed unit. */
+ double getCoefficient( Unit eUnit ) const;
+
+private:
+ o3tl::enumarray<Unit, double> maCoeffs; /// Coefficients for unit conversion.
+ std::map<OUString, sal_uInt8> maOoxErrCodes; /// Maps error code strings to BIFF error constants.
+ sal_Int32 mnNullDate; /// Nulldate of this workbook (number of days since 0000-01-01).
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/viewsettings.hxx b/sc/source/filter/inc/viewsettings.hxx
new file mode 100644
index 0000000000..70797967af
--- /dev/null
+++ b/sc/source/filter/inc/viewsettings.hxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rangelst.hxx>
+#include "stylesbuffer.hxx"
+#include "worksheethelper.hxx"
+#include "workbookhelper.hxx"
+
+namespace oox::xls {
+
+/** Contains all settings for a selection in a single pane of a sheet. */
+struct PaneSelectionModel
+{
+ ScAddress maActiveCell; /// Position of active cell (cursor).
+ ScRangeList maSelection; /// Selected cell ranges.
+ sal_Int32 mnActiveCellId; /// Index of active cell in selection list.
+
+ explicit PaneSelectionModel();
+};
+
+/** Contains all view settings for a single sheet. */
+struct SheetViewModel
+{
+ typedef RefMap< sal_Int32, PaneSelectionModel > PaneSelectionModelMap;
+
+ PaneSelectionModelMap maPaneSelMap; /// Selections of all panes.
+ XlsColor maGridColor; /// Grid color.
+ ScAddress maFirstPos; /// First visible cell.
+ ScAddress maSecondPos; /// First visible cell in additional panes.
+ sal_Int32 mnWorkbookViewId; /// Index into list of workbookView elements.
+ sal_Int32 mnViewType; /// View type (normal, page break, layout).
+ sal_Int32 mnActivePaneId; /// Active pane (with cell cursor).
+ sal_Int32 mnPaneState; /// Pane state (frozen, split).
+ double mfSplitX; /// Split X position (twips), or number of frozen columns.
+ double mfSplitY; /// Split Y position (twips), or number of frozen rows.
+ sal_Int32 mnCurrentZoom; /// Zoom factor for current view.
+ sal_Int32 mnNormalZoom; /// Zoom factor for normal view.
+ sal_Int32 mnSheetLayoutZoom; /// Zoom factor for pagebreak preview.
+ sal_Int32 mnPageLayoutZoom; /// Zoom factor for page layout view.
+ bool mbSelected; /// True = sheet is selected.
+ bool mbRightToLeft; /// True = sheet in right-to-left mode.
+ bool mbDefGridColor; /// True = default grid color.
+ bool mbShowFormulas; /// True = show formulas instead of results.
+ bool mbShowGrid; /// True = show cell grid.
+ bool mbShowHeadings; /// True = show column/row headings.
+ bool mbShowZeros; /// True = show zero value zells.
+ bool mbShowOutline; /// True = show outlines.
+ bool mbZoomToFit; /// True = zoom chart sheet to fit window.
+
+ explicit SheetViewModel();
+
+ /** Returns true, if page break preview is active. */
+ bool isPageBreakPreview() const;
+ /** Returns the zoom in normal view (returns default, if current value is 0). */
+ sal_Int32 getNormalZoom() const;
+ /** Returns the zoom in pagebreak preview (returns default, if current value is 0). */
+ sal_Int32 getPageBreakZoom() const;
+ /** Returns the grid color as RGB value. */
+ ::Color getGridColor( const ::oox::core::FilterBase& rFilter ) const;
+
+ /** Returns the selection data of the active pane. */
+ const PaneSelectionModel* getActiveSelection() const;
+ /** Returns read/write access to the selection data of the specified pane. */
+ PaneSelectionModel& createPaneSelection( sal_Int32 nPaneId );
+};
+
+typedef std::shared_ptr< SheetViewModel > SheetViewModelRef;
+
+class SheetViewSettings : public WorksheetHelper
+{
+public:
+ explicit SheetViewSettings( const WorksheetHelper& rHelper );
+
+ /** Imports the sheetView element containing sheet view settings. */
+ void importSheetView( const AttributeList& rAttribs );
+ /** Imports the pane element containing sheet pane settings. */
+ void importPane( const AttributeList& rAttribs );
+ /** Imports the selection element containing selection settings for a pane. */
+ void importSelection( const AttributeList& rAttribs );
+ /** Imports the sheetView element containing view settings of a chart sheet. */
+ void importChartSheetView( const AttributeList& rAttribs );
+
+ /** Imports the SHEETVIEW record containing sheet view settings. */
+ void importSheetView( SequenceInputStream& rStrm );
+ /** Imports the PANE record containing sheet pane settings. */
+ void importPane( SequenceInputStream& rStrm );
+ /** Imports the SELECTION record containing selection settings for a pane. */
+ void importSelection( SequenceInputStream& rStrm );
+ /** Imports the CHARTSHEETVIEW record containing view settings of a chart sheet. */
+ void importChartSheetView( SequenceInputStream& rStrm );
+
+ /** Converts all imported sheet view settings. */
+ void finalizeImport();
+
+ /** Returns true, if the sheet layout is set to right-to-left. */
+ bool isSheetRightToLeft() const;
+
+private:
+ SheetViewModelRef createSheetView();
+
+private:
+ typedef RefVector< SheetViewModel > SheetViewModelVec;
+ SheetViewModelVec maSheetViews;
+};
+
+/** Contains all view settings for the entire document. */
+struct WorkbookViewModel
+{
+ sal_Int32 mnWinX; /// X position of the workbook window (twips).
+ sal_Int32 mnWinY; /// Y position of the workbook window (twips).
+ sal_Int32 mnWinWidth; /// Width of the workbook window (twips).
+ sal_Int32 mnWinHeight; /// Height of the workbook window (twips).
+ sal_Int32 mnActiveSheet; /// Displayed (active) sheet.
+ sal_Int32 mnFirstVisSheet; /// First visible sheet in sheet tabbar.
+ sal_Int32 mnTabBarWidth; /// Width of sheet tabbar (1/1000 of window width).
+ sal_Int32 mnVisibility; /// Visibility state of workbook window.
+ bool mbShowTabBar; /// True = show sheet tabbar.
+ bool mbShowHorScroll; /// True = show horizontal sheet scrollbars.
+ bool mbShowVerScroll; /// True = show vertical sheet scrollbars.
+ bool mbMinimized; /// True = workbook window is minimized.
+
+ explicit WorkbookViewModel();
+};
+
+typedef std::shared_ptr< WorkbookViewModel > WorkbookViewModelRef;
+
+class ViewSettings : public WorkbookHelper
+{
+public:
+ explicit ViewSettings( const WorkbookHelper& rHelper );
+
+ /** Imports the workbookView element containing workbook view settings. */
+ void importWorkbookView( const AttributeList& rAttribs );
+ /** Imports the oleSize element containing the visible size of the workbook. */
+ void importOleSize( const AttributeList& rAttribs );
+ /** Imports the WORKBOOKVIEW record containing workbook view settings. */
+ void importWorkbookView( SequenceInputStream& rStrm );
+ /** Imports the OLESIZE record containing the visible size of the workbook. */
+ void importOleSize( SequenceInputStream& rStrm );
+
+ /** Stores converted view settings for a specific worksheet. */
+ void setSheetViewSettings( sal_Int16 nSheet,
+ const SheetViewModelRef& rxSheetView,
+ const css::uno::Any& rProperties );
+ /** Stores the used area for a specific worksheet. */
+ void setSheetUsedArea( const ScRange& rUsedArea );
+
+ /** Converts all imported document view settings. */
+ void finalizeImport();
+
+ /** Returns the Calc index of the active sheet. */
+ sal_Int16 getActiveCalcSheet() const;
+
+private:
+ WorkbookViewModel& createWorkbookView();
+
+private:
+ typedef RefVector< WorkbookViewModel > WorkbookViewModelVec;
+ typedef RefMap< sal_Int16, SheetViewModel > SheetViewModelMap;
+
+ WorkbookViewModelVec maBookViews; /// Workbook view models.
+ SheetViewModelMap maSheetViews; /// Active view model for each sheet.
+ std::map< sal_Int16, css::uno::Any > maSheetProps; /// Converted property sequences for each sheet.
+ std::map< sal_Int16, ScRange > maSheetUsedAreas; /// Used area (cell range) of every sheet.
+ ScRange maOleSize; /// Visible area if this is an embedded OLE object.
+ bool mbValidOleSize; /// True = imported OLE size is a valid cell range.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/workbookfragment.hxx b/sc/source/filter/inc/workbookfragment.hxx
new file mode 100644
index 0000000000..f2556678be
--- /dev/null
+++ b/sc/source/filter/inc/workbookfragment.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "defnamesbuffer.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class ExternalLink;
+
+class WorkbookFragment : public WorkbookFragmentBase
+{
+public:
+ explicit WorkbookFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+
+private:
+ void importExternalReference( const AttributeList& rAttribs );
+ void importDefinedName( const AttributeList& rAttribs );
+ void importPivotCache( const AttributeList& rAttribs );
+
+ void importExternalRef( SequenceInputStream& rStrm );
+ void importPivotCache( SequenceInputStream& rStrm );
+
+ void importExternalLinkFragment( ExternalLink& rExtLink );
+ void importPivotCacheDefFragment( const OUString& rRelId, sal_Int32 nCacheId );
+
+ void recalcFormulaCells();
+
+private:
+ DefinedNameRef mxCurrName;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/workbookhelper.hxx b/sc/source/filter/inc/workbookhelper.hxx
new file mode 100644
index 0000000000..e76285ca37
--- /dev/null
+++ b/sc/source/filter/inc/workbookhelper.hxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+
+#include <o3tl/hash_combine.hxx>
+#include <oox/helper/storagebase.hxx>
+#include <address.hxx>
+#include <rtl/ref.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+
+namespace oox::drawingml::chart { class ChartConverter; }
+namespace rtl { template <class reference_type> class Reference; }
+
+namespace com::sun::star {
+ namespace container { class XNameContainer; }
+ namespace sheet { class XDatabaseRange; }
+ namespace sheet { class XSpreadsheet; }
+ namespace sheet { class XSpreadsheetDocument; }
+ namespace sheet { struct FormulaToken; }
+ namespace style { class XStyle; }
+ namespace table { class XCellRange; }
+}
+
+namespace oox {
+ class SegmentProgressBar;
+}
+
+namespace oox::core {
+ class FilterBase;
+ class FragmentHandler;
+ class XmlFilterBase;
+ class FastParser;
+}
+
+class ScDocument;
+class ScDocumentImport;
+class ScEditEngineDefaulter;
+class ScDBData;
+class ScRangeData;
+class ScModelObj;
+
+namespace oox::xls {
+
+enum class FontClassification : sal_uInt8
+{
+ None = 0x0000,
+ Asian = 0x0001,
+ Cmplx = 0x0002,
+ Latin = 0x0004
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<oox::xls::FontClassification> : is_typed_flags<oox::xls::FontClassification, 0x07> {};
+}
+
+namespace oox::xls {
+
+class ExcelFilter;
+
+/** Functor for case-insensitive string comparison, usable in maps etc. */
+struct IgnoreCaseCompare
+{
+ bool operator()( std::u16string_view rName1, std::u16string_view rName2 ) const;
+};
+
+class AddressConverter;
+class ConnectionsBuffer;
+class DefinedNamesBuffer;
+class ExternalLinkBuffer;
+class FormulaParser;
+class PageSettingsConverter;
+class PivotCacheBuffer;
+class PivotTableBuffer;
+class ScenarioBuffer;
+class SharedStringsBuffer;
+class StylesBuffer;
+class TableBuffer;
+class ThemeBuffer;
+class UnitConverter;
+class ViewSettings;
+class WorkbookSettings;
+class WorksheetBuffer;
+class FormulaBuffer;
+
+class WorkbookGlobals;
+typedef std::shared_ptr< WorkbookGlobals > WorkbookGlobalsRef;
+
+struct FontDescriptorHash
+{
+ size_t operator()( const css::awt::FontDescriptor& rKey) const
+ {
+ std::size_t seed = rKey.Name.hashCode();
+ o3tl::hash_combine(seed, rKey.Height);
+ o3tl::hash_combine(seed, rKey.Width);
+ o3tl::hash_combine(seed, rKey.StyleName.hashCode());
+ o3tl::hash_combine(seed, rKey.Family);
+ o3tl::hash_combine(seed, rKey.CharSet);
+ o3tl::hash_combine(seed, rKey.Pitch);
+ o3tl::hash_combine(seed, rKey.CharacterWidth);
+ o3tl::hash_combine(seed, rKey.Weight);
+ o3tl::hash_combine(seed, rKey.Slant);
+ o3tl::hash_combine(seed, rKey.Underline);
+ o3tl::hash_combine(seed, rKey.Strikeout);
+ o3tl::hash_combine(seed, rKey.Orientation);
+ o3tl::hash_combine(seed, rKey.Kerning);
+ o3tl::hash_combine(seed, rKey.WordLineMode);
+ o3tl::hash_combine(seed, rKey.Type);
+ return seed;
+ }
+};
+
+typedef std::unordered_map<css::awt::FontDescriptor, FontClassification, FontDescriptorHash> FontClassificationMap;
+
+/** Helper class to provide access to global workbook data.
+
+ All classes derived from this helper class will have access to a singleton
+ object of type WorkbookGlobals containing global workbook settings,
+ buffers, converters, etc. Nearly all classes in this filter implementation
+ are derived directly or indirectly from this class.
+
+ This class contains just a simple reference to the WorkbookGlobals object
+ to prevent circular references, as the WorkbookGlobals object contains a
+ lot of objects derived from this class.
+ */
+class WorkbookHelper
+{
+public:
+ /*implicit*/ WorkbookHelper( WorkbookGlobals& rBookGlob ) : mrBookGlob( rBookGlob ) {}
+ virtual ~WorkbookHelper();
+
+ WorkbookHelper(WorkbookHelper const &) = default;
+ WorkbookHelper(WorkbookHelper &&) = default;
+ WorkbookHelper & operator =(WorkbookHelper const &) = delete; // due to mrBookGlob
+ WorkbookHelper & operator =(WorkbookHelper &&) = delete; // due to mrBookGlob
+
+ static WorkbookGlobalsRef constructGlobals( ExcelFilter& rFilter );
+
+ // filter -----------------------------------------------------------------
+
+ /** Returns the base filter object (base class of all filters). */
+ ::oox::core::FilterBase& getBaseFilter() const;
+ /** Returns the filter progress bar. */
+ SegmentProgressBar& getProgressBar() const;
+ /** Returns the index of the current Calc sheet, if filter currently processes a sheet. */
+ sal_Int16 getCurrentSheetIndex() const;
+ /** Returns true when reading a file generated by a known good generator. */
+ bool isGeneratorKnownGood() const;
+ /** Returns true if any formula cell is calculated. */
+ bool hasCalculatedFormulaCells() const;
+ /** Set if any formula cell is calculated. */
+ void setCalculatedFormulaCells();
+
+ /** Sets the VBA project storage used to import VBA source code and forms. */
+ void setVbaProjectStorage( const StorageRef& rxVbaPrjStrg );
+ /** Sets the index of the current Calc sheet, if filter currently processes a sheet. */
+ void setCurrentSheetIndex( SCTAB nSheet );
+ /** Final conversion after importing the workbook. */
+ void finalizeWorkbookImport();
+ void useInternalChartDataTable( bool bInternal );
+
+ // document model ---------------------------------------------------------
+ ScDocument& getScDocument();
+ const ScDocument& getScDocument() const;
+
+ ScDocumentImport& getDocImport();
+ const ScDocumentImport& getDocImport() const;
+
+ ScEditEngineDefaulter& getEditEngine() const;
+ /** Returns a reference to the source/target spreadsheet document model (XSpreadsheetDocument). */
+ const rtl::Reference< ScModelObj >& getDocument() const;
+
+ /** Returns a reference to the specified spreadsheet in the document model. */
+ css::uno::Reference< css::sheet::XSpreadsheet >
+ getSheetFromDoc( sal_Int32 nSheet ) const;
+ /** Returns a reference to the specified spreadsheet in the document model. */
+ css::uno::Reference< css::sheet::XSpreadsheet >
+ getSheetFromDoc( const OUString& rSheet ) const;
+
+ /** Returns the XCellRange interface for the passed cell range address. */
+ css::uno::Reference< css::table::XCellRange >
+ getCellRangeFromDoc( const ScRange& rRange ) const;
+
+ /** Returns the cell styles container from the Calc document. */
+ css::uno::Reference< css::container::XNameContainer >
+ getCellStyleFamily() const;
+ /** Returns the specified cell or page style from the Calc document. */
+ css::uno::Reference< css::style::XStyle >
+ getStyleObject( const OUString& rStyleName, bool bPageStyle ) const;
+
+ // second is true if ownership belongs to the caller
+ typedef std::pair<ScRangeData*, bool> RangeDataRet;
+
+ /** Creates and returns a defined name on-the-fly in the Calc document.
+ The name will not be buffered in the global defined names buffer.
+ @param orName (in/out-parameter) Returns the resulting used name. */
+ RangeDataRet createNamedRangeObject(
+ OUString& orName,
+ sal_Int32 nIndex,
+ sal_Int32 nNameFlags ) const;
+
+ /** Creates and returns a defined name on-the-fly in the sheet.
+ The name will not be buffered in the global defined names buffer.
+ @param orName (in/out-parameter) Returns the resulting used name. */
+ RangeDataRet createLocalNamedRangeObject(
+ OUString& orName,
+ sal_Int32 nIndex,
+ sal_Int32 nNameFlags, sal_Int32 nTab ) const;
+
+ /** Creates and returns a database range on-the-fly in the Calc document.
+ The range will not be buffered in the global table buffer.
+ @param orName (in/out-parameter) Returns the resulting used name. */
+ css::uno::Reference< css::sheet::XDatabaseRange >
+ createDatabaseRangeObject(
+ OUString& orName,
+ const ScRange& rRangeAddr ) const;
+
+ /** Creates and returns an unnamed database range on-the-fly in the Calc document.
+ The range will not be buffered in the global table buffer. */
+ css::uno::Reference< css::sheet::XDatabaseRange >
+ createUnnamedDatabaseRangeObject(
+ const ScRange& rRangeAddr ) const;
+
+ /** Finds the (already existing) database range of the given formula token index. */
+ ScDBData* findDatabaseRangeByIndex( sal_uInt16 nIndex ) const;
+
+ /** Creates and returns a com.sun.star.style.Style object for cells or pages. */
+ css::uno::Reference< css::style::XStyle >
+ createStyleObject(
+ OUString& orStyleName,
+ bool bPageStyle ) const;
+
+ // buffers ----------------------------------------------------------------
+
+ FormulaBuffer& getFormulaBuffer() const;
+ /** Returns the global workbook settings object. */
+ WorkbookSettings& getWorkbookSettings() const;
+ /** Returns the workbook and sheet view settings object. */
+ ViewSettings& getViewSettings() const;
+ /** Returns the worksheet buffer containing sheet names and properties. */
+ WorksheetBuffer& getWorksheets() const;
+ /** Returns the office theme object read from the theme substorage. */
+ ThemeBuffer& getTheme() const;
+ /** Returns all cell formatting objects read from the styles substream. */
+ StylesBuffer& getStyles() const;
+ /** Returns the shared strings read from the shared strings substream. */
+ SharedStringsBuffer& getSharedStrings() const;
+ /** Returns the external links read from the external links substream. */
+ ExternalLinkBuffer& getExternalLinks() const;
+ /** Returns the defined names read from the workbook globals. */
+ DefinedNamesBuffer& getDefinedNames() const;
+ /** Returns the tables collection (equivalent to Calc's database ranges). */
+ TableBuffer& getTables() const;
+ /** Returns the scenarios collection. */
+ ScenarioBuffer& getScenarios() const;
+ /** Returns the collection of external data connections. */
+ ConnectionsBuffer& getConnections() const;
+ /** Returns the collection of pivot caches. */
+ PivotCacheBuffer& getPivotCaches() const;
+ /** Returns the collection of pivot tables. */
+ PivotTableBuffer& getPivotTables() const;
+ /** Shared cache of Font Classifications to avoid repeated lookups */
+ FontClassificationMap& getFontClassificationCache() const;
+
+ // converters -------------------------------------------------------------
+
+ /** Returns a shared import formula parser (import filter only!). */
+ FormulaParser& getFormulaParser() const;
+ /** Returns an unshared import formula parser (import filter only!). */
+ FormulaParser* createFormulaParser() const;
+ /** Returns the measurement unit converter. */
+ UnitConverter& getUnitConverter() const;
+ /** Returns the converter for string to cell address/range conversion. */
+ AddressConverter& getAddressConverter() const;
+ /** Returns the chart object converter. */
+ oox::drawingml::chart::ChartConverter* getChartConverter() const;
+ /** Returns the page and print settings converter. */
+ PageSettingsConverter& getPageSettingsConverter() const;
+
+ // OOXML/BIFF12 specific (MUST NOT be called in BIFF filter) --------------
+
+ /** Returns the base OOXML/BIFF12 filter object.
+ Must not be called, if current filter is not the OOXML/BIFF12 filter. */
+ ::oox::core::XmlFilterBase& getOoxFilter() const;
+
+ /** Imports a fragment using the passed fragment handler, which contains
+ the full path to the fragment stream. */
+ bool importOoxFragment( const rtl::Reference<oox::core::FragmentHandler>& rxHandler );
+
+ bool importOoxFragment( const rtl::Reference<oox::core::FragmentHandler>& rxHandler, oox::core::FastParser& rParser );
+
+ // BIFF2-BIFF8 specific (MUST NOT be called in OOXML/BIFF12 filter) -------
+
+ /** Returns the text encoding used to import/export byte strings. */
+ rtl_TextEncoding getTextEncoding() const;
+
+private:
+ WorkbookGlobals& mrBookGlob;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/workbooksettings.hxx b/sc/source/filter/inc/workbooksettings.hxx
new file mode 100644
index 0000000000..1078b3e6c0
--- /dev/null
+++ b/sc/source/filter/inc/workbooksettings.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 "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace com::sun::star::util { struct Date; }
+
+namespace oox::xls {
+
+/** Settings for workbook write protection. */
+struct FileSharingModel
+{
+ OUString maUserName; /// User who added the write protection password.
+ OUString maAlgorithmName; /// Algorithm name, "SHA-512", "SHA-1", ...
+ OUString maHashValue; /// Hash value computed by the algorithm, base-64 encoded
+ OUString maSaltValue; /// Salt value to be prepended to the password, base-64 encoded
+ sal_uInt32 mnSpinCount; /// Spin count, iterations to run algorithm
+ sal_uInt16 mnPasswordHash; /// Hash value of the write protection password. (unrelated to the above)
+ bool mbRecommendReadOnly; /// True = recommend read-only mode on opening.
+
+ explicit FileSharingModel();
+};
+
+/** Global workbook settings. */
+struct WorkbookSettingsModel
+{
+ OUString maCodeName; /// VBA codename for the workbook.
+ sal_Int32 mnShowObjectMode; /// Specifies how objects are shown.
+ sal_Int32 mnUpdateLinksMode; /// Specifies how external links are updated.
+ sal_Int32 mnDefaultThemeVer; /// Default theme version.
+ bool mbDateMode1904; /// True = null date is 1904-01-01.
+ bool mbDateCompatibility; /// False = null date is 1899-12-30.
+ bool mbSaveExtLinkValues; /// True = save cached cell values for external links.
+
+ explicit WorkbookSettingsModel();
+
+ /** Sets BIFF object visibility mode. */
+ void setBiffObjectMode( sal_uInt16 nObjMode );
+};
+
+/** Workbook calculation settings. */
+struct CalcSettingsModel
+{
+ double mfIterateDelta; /// Minimum change in circular references.
+ sal_Int32 mnCalcId; /// Calculation engine identifier.
+ sal_Int32 mnRefMode; /// Cell reference mode: A1 or R1C1.
+ sal_Int32 mnCalcMode; /// Automatic or manual recalculation.
+ sal_Int32 mnIterateCount; /// Number of iterations in circular references.
+ sal_Int32 mnProcCount; /// Number of processors for concurrent calculation.
+ bool mbCalcOnSave; /// True = always recalculate formulas before save.
+ bool mbCalcCompleted; /// True = formulas have been recalculated before save.
+ bool mbFullPrecision; /// True = use full precision on calculation.
+ bool mbIterate; /// True = allow circular references.
+ bool mbConcurrent; /// True = concurrent calculation enabled.
+
+ explicit CalcSettingsModel();
+};
+
+class WorkbookSettings : public WorkbookHelper
+{
+public:
+ explicit WorkbookSettings( const WorkbookHelper& rHelper );
+
+ /** Imports the fileSharing element containing write protection settings. */
+ void importFileSharing( const AttributeList& rAttribs );
+ /** Imports the workbookPr element containing global workbook settings. */
+ void importWorkbookPr( const AttributeList& rAttribs );
+ /** Imports the calcPr element containing workbook calculation settings. */
+ void importCalcPr( const AttributeList& rAttribs );
+
+ /** Imports the FILESHARING record containing write protection settings. */
+ void importFileSharing( SequenceInputStream& rStrm );
+ /** Imports the WORKBOOKPR record containing global workbook settings. */
+ void importWorkbookPr( SequenceInputStream& rStrm );
+ /** Imports the CALCPR record containing workbook calculation settings. */
+ void importCalcPr( SequenceInputStream& rStrm );
+
+ /** Converts the imported workbook settings. */
+ void finalizeImport();
+
+ /** Returns the show objects mode (considered a view setting in Calc). */
+ sal_Int16 getApiShowObjectMode() const;
+ /** Returns the nulldate of this workbook. */
+ css::util::Date const & getNullDate() const;
+
+private:
+ /** Updates date mode and unit converter nulldate. */
+ void setDateMode( bool bDateMode1904, bool bDateCompatibility=true );
+
+private:
+ FileSharingModel maFileSharing;
+ WorkbookSettingsModel maBookSettings;
+ CalcSettingsModel maCalcSettings;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheetbuffer.hxx b/sc/source/filter/inc/worksheetbuffer.hxx
new file mode 100644
index 0000000000..98a71e617a
--- /dev/null
+++ b/sc/source/filter/inc/worksheetbuffer.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <utility>
+#include <oox/helper/refmap.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class SequenceInputStream; }
+namespace oox { class AttributeList; }
+
+namespace oox::xls {
+
+/** Contains data from the 'sheet' element describing a sheet in the workbook. */
+struct SheetInfoModel
+{
+ OUString maRelId; /// Relation identifier for the sheet substream.
+ OUString maName; /// Original name of the sheet.
+ sal_Int32 mnSheetId; /// Sheet identifier.
+ sal_Int32 mnState; /// Visibility state.
+
+ explicit SheetInfoModel();
+};
+
+/** Stores information about all sheets in a spreadsheet document.
+
+ Information about sheets includes the sheet name, the visibility state, and
+ for the OOXML filter, the relation identifier of the sheet used to obtain
+ the related worksheet fragment.
+ */
+class WorksheetBuffer : public WorkbookHelper
+{
+public:
+ explicit WorksheetBuffer( const WorkbookHelper& rHelper );
+
+ /** Imports the attributes of a sheet element. */
+ void importSheet( const AttributeList& rAttribs );
+ /** Imports the SHEET record from the passed BIFF12 stream. */
+ void importSheet( SequenceInputStream& rStrm );
+ /** Inserts a new empty sheet into the document. Looks for an unused name.
+ @return Index of the new sheet in the Calc document. */
+ sal_Int16 insertEmptySheet( const OUString& rPreferredName );
+
+ /** Returns the number of original sheets contained in the workbook. */
+ sal_Int32 getWorksheetCount() const;
+ /** Returns the number of all sheets, workbook + dummy ones (pivot table cache records ) */
+ sal_Int32 getAllSheetCount() const;
+ /** Returns the OOXML relation identifier of the specified worksheet. */
+ OUString getWorksheetRelId( sal_Int32 nWorksheet ) const;
+
+ /** Returns the Calc index of the specified worksheet. */
+ sal_Int16 getCalcSheetIndex( sal_Int32 nWorksheet ) const;
+ /** Returns the finalized name of the specified worksheet. */
+ OUString getCalcSheetName( sal_Int32 nWorksheet ) const;
+
+ /** Returns the Calc index of the sheet with the passed original worksheet name. */
+ sal_Int16 getCalcSheetIndex( const OUString& rWorksheetName ) const;
+ /** Returns the finalized name of the sheet with the passed worksheet name. */
+ OUString getCalcSheetName( const OUString& rWorksheetName ) const;
+ /** Converts sSheetNameRef (e.g. '#SheetName!A1' to '#SheetName.A1' )
+ if sSheetNameRef doesn't start with '#' it is ignored and not modified
+ */
+ void convertSheetNameRef( OUString& sSheetNameRef ) const;
+ void finalizeImport( sal_Int16 nActiveSheet );
+
+private:
+ struct SheetInfo : public SheetInfoModel
+ {
+ OUString maCalcName;
+ OUString maCalcQuotedName;
+ sal_Int16 mnCalcSheet;
+
+ explicit SheetInfo( const SheetInfoModel& rModel, sal_Int16 nCalcSheet, const OUString& rCalcName );
+ };
+
+ typedef ::std::pair< sal_Int16, OUString > IndexNamePair;
+
+ /** Creates a new sheet in the Calc document. Does not insert anything in the own lists. */
+ IndexNamePair createSheet( const OUString& rPreferredName, sal_Int32 nSheetPos );
+ /** Creates a new sheet in the Calc document and inserts the related SheetInfo. */
+ void insertSheet( const SheetInfoModel& rModel );
+
+private:
+ RefVector< SheetInfo > maSheetInfos;
+ RefMap< OUString, SheetInfo, IgnoreCaseCompare > maSheetInfosByName;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheetfragment.hxx b/sc/source/filter/inc/worksheetfragment.hxx
new file mode 100644
index 0000000000..05ec7d02f4
--- /dev/null
+++ b/sc/source/filter/inc/worksheetfragment.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class DataValidationsContextBase
+{
+public:
+ void SetSqref( const OUString& rChars ) { maSqref = rChars; }
+ void SetFormula1( const OUString& rChars ) { maFormula1 = rChars; }
+ void SetFormula2( const OUString& rChars ) { maFormula2 = rChars; }
+ void SetValidation( WorksheetHelper& rTarget );
+ /** Imports the dataValidation element containing data validation settings. */
+ void importDataValidation( const AttributeList& rAttribs );
+ /** Imports the DATAVALIDATION record containing data validation settings. */
+ static void importDataValidation( SequenceInputStream& rStrm, WorksheetHelper& rTarget );
+ bool isFormula1Set() const { return !maFormula1.isEmpty(); }
+ bool isFormula2Set() const { return !maFormula2.isEmpty(); }
+
+private:
+ std::unique_ptr< ValidationModel > mxValModel;
+
+ OUString maSqref;
+ OUString maFormula1;
+ OUString maFormula2;
+};
+
+// For following types of validations:
+//
+// <dataValidations count="1">
+// <dataValidation allowBlank="true" operator="equal" showDropDown="false" showErrorMessage="true" showInputMessage="false" sqref="C1:C5" type="list">
+// <formula1>Sheet1!$A$1:$A$5</formula1>
+// <formula2>0</formula2>
+// </dataValidation>
+// </dataValidations>
+//
+// or
+//
+// <dataValidations count="1">
+// <dataValidation type="list" operator="equal" allowBlank="1" showErrorMessage="1" sqref="A1">
+// <mc:AlternateContent xmlns:x12ac="http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
+// <mc:Choice Requires="x12ac">
+// <x12ac:list>1,"2,3",4</x12ac:list>
+// </mc:Choice>
+// <mc:Fallback>
+// <formula1>"1,2,3,4"</formula1>
+// </mc:Fallback>
+// </mc:AlternateContent>
+// </dataValidation>
+// </dataValidations>
+
+class DataValidationsContext : public WorksheetContextBase, private DataValidationsContextBase
+{
+public:
+ explicit DataValidationsContext( WorksheetFragmentBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+};
+
+// For following types of validations:
+//
+// <extLst>
+// <ext uri="{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">
+// <x14:dataValidations count="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+// <x14:dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1">
+// <x14:formula1>
+// <xm:f>Sheet1!$A$2:$A$272</xm:f>
+// </x14:formula1>
+// <xm:sqref>A6:A22</xm:sqref>
+// </x14:dataValidation>
+// </x14:dataValidations>
+// </ext>
+// </extLst>
+
+class ExtDataValidationsContext : public WorksheetContextBase, private DataValidationsContextBase
+{
+public:
+ explicit ExtDataValidationsContext( WorksheetContextBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+private:
+ sal_Int32 mCurrFormula;
+};
+
+class WorksheetFragment : public WorksheetFragmentBase
+{
+public:
+ explicit WorksheetFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void initializeImport() override;
+ virtual void finalizeImport() override;
+
+private:
+ /** Imports page settings from a pageSetUpPr element. */
+ void importPageSetUpPr( const AttributeList& rAttribs );
+ /** Imports the dimension element containing the used area of the sheet. */
+ void importDimension( const AttributeList& rAttribs );
+ /** Imports sheet format properties from a sheetFormatPr element. */
+ void importSheetFormatPr( const AttributeList& rAttribs );
+ /** Imports column settings from a col element. */
+ void importCol( const AttributeList& rAttribs );
+ /** Imports a merged cell range from a mergeCell element. */
+ void importMergeCell( const AttributeList& rAttribs );
+ /** Imports the hyperlink element containing a hyperlink for a cell range. */
+ void importHyperlink( const AttributeList& rAttribs );
+ /** Imports individual break that is either within row or column break context. */
+ void importBrk( const AttributeList& rAttribs, bool bRowBreak );
+ /** Imports the relation identifier for the DrawingML part. */
+ void importDrawing( const AttributeList& rAttribs );
+ /** Imports the relation identifier for the legacy VML drawing part. */
+ void importLegacyDrawing( const AttributeList& rAttribs );
+ /** Imports additional data for an OLE object. */
+ void importOleObject( const AttributeList& rAttribs );
+ /** Imports additional data for an OCX form control. */
+ void importControl( const AttributeList& rAttribs );
+
+ /** Imports the DIMENSION record containing the used area of the sheet. */
+ void importDimension( SequenceInputStream& rStrm );
+ /** Imports sheet format properties from a SHEETFORMATPR record. */
+ void importSheetFormatPr( SequenceInputStream& rStrm );
+ /** Imports column settings from a COL record. */
+ void importCol( SequenceInputStream& rStrm );
+ /** Imports a merged cell range from a MERGECELL record. */
+ void importMergeCell( SequenceInputStream& rStrm );
+ /** Imports a hyperlink for a cell range from a HYPERLINK record. */
+ void importHyperlink( SequenceInputStream& rStrm );
+ /** Imports the BRK record for an individual row or column page break. */
+ void importBrk( SequenceInputStream& rStrm, bool bRowBreak );
+ /** Imports the DRAWING record containing the relation identifier for the DrawingML part. */
+ void importDrawing( SequenceInputStream& rStrm );
+ /** Imports the LEGACYDRAWING record containing the relation identifier for the VML drawing part. */
+ void importLegacyDrawing( SequenceInputStream& rStrm );
+ /** Imports additional data for an OLE object. */
+ void importOleObject( SequenceInputStream& rStrm );
+ /** Imports additional data for an OCX form control. */
+ void importControl( SequenceInputStream& rStrm );
+
+ /** Imports the binary data of an embedded OLE object from the fragment with the passed ID. */
+ void importEmbeddedOleData( StreamDataSequence& orEmbeddedData, const OUString& rRelId );
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheethelper.hxx b/sc/source/filter/inc/worksheethelper.hxx
new file mode 100644
index 0000000000..4081d248e3
--- /dev/null
+++ b/sc/source/filter/inc/worksheethelper.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/progressbar.hxx>
+#include <oox/ole/olehelper.hxx>
+#include <rangelst.hxx>
+#include "formulabase.hxx"
+
+struct ScDataBarFormatData;
+class ScDocument;
+
+namespace com::sun::star {
+ namespace awt { struct Point; }
+ namespace awt { struct Rectangle; }
+ namespace awt { struct Size; }
+ namespace drawing { class XDrawPage; }
+ namespace sheet { class XSpreadsheet; }
+ namespace table { class XCell; }
+ namespace table { class XCellRange; }
+}
+
+namespace oox::xls {
+
+class AutoFilterBuffer;
+class CommentsBuffer;
+class CondFormatBuffer;
+class Font;
+class PageSettings;
+class QueryTableBuffer;
+class RichString;
+class SheetDataBuffer;
+class SheetViewSettings;
+class VmlDrawing;
+class WorksheetSettings;
+
+typedef ::std::map< OUString, ScDataBarFormatData* > ExtLst;
+
+/** An enumeration for all types of sheets in a workbook. */
+enum class WorksheetType
+{
+ Work, /// Worksheet.
+ Chart, /// Chart sheet.
+ Macro, /// Macro sheet.
+ Dialog, /// Dialog sheet (BIFF5+).
+ Empty /// Other (unsupported) sheet type.
+};
+
+/** Stores settings and formatting data about a range of sheet columns. */
+struct ColumnModel
+{
+ ValueRange maRange; /// 1-based (!) range of the described columns.
+ double mfWidth; /// Column width in number of characters.
+ sal_Int32 mnXfId; /// Column default formatting.
+ sal_Int32 mnLevel; /// Column outline level.
+ bool mbShowPhonetic:1; /// True = cells in column show phonetic settings.
+ bool mbHidden:1; /// True = column is hidden.
+ bool mbCollapsed:1; /// True = column outline is collapsed.
+
+ explicit ColumnModel();
+
+ /** Returns true, if this entry can be merged with the passed column range (column settings are equal). */
+ bool isMergeable( const ColumnModel& rModel ) const;
+};
+
+/** Stores settings and formatting data about a sheet row. */
+struct RowModel
+{
+ sal_Int32 mnRow; /// 1-based (!) index of the described row.
+ double mfHeight; /// Row height in points.
+ sal_Int32 mnXfId; /// Row default formatting (see mbIsFormatted).
+ sal_Int32 mnLevel; /// Row outline level.
+ bool mbCustomHeight:1; /// True = row has custom height.
+ bool mbCustomFormat:1; /// True = cells in row have explicit formatting.
+ bool mbShowPhonetic:1; /// True = cells in row show phonetic settings.
+ bool mbHidden:1; /// True = row is hidden.
+ bool mbCollapsed:1; /// True = row outline is collapsed.
+ bool mbThickTop:1; /// True = row has extra space above text.
+ bool mbThickBottom:1; /// True = row has extra space below text.
+
+ explicit RowModel();
+
+ /** Returns true, if this entry can be merged with the passed row range (row settings are equal). */
+ bool isMergeable( const RowModel& rModel ) const;
+};
+
+/** Stores formatting data about a page break. */
+struct PageBreakModel
+{
+ sal_Int32 mnColRow; /// 0-based (!) index of column/row.
+ sal_Int32 mnMin; /// Start of limited break.
+ sal_Int32 mnMax; /// End of limited break.
+ bool mbManual; /// True = manual page break.
+
+ explicit PageBreakModel();
+};
+
+/** Stores data about a hyperlink range. */
+struct HyperlinkModel : public ::oox::ole::StdHlinkInfo
+{
+ ScRange maRange; /// The cell area containing the hyperlink.
+ OUString maTooltip; /// Additional tooltip text.
+
+ explicit HyperlinkModel();
+};
+
+/** Stores data about ranges with data validation settings. */
+struct ValidationModel
+{
+ ScRangeList maRanges;
+ ApiTokenSequence maTokens1;
+ ApiTokenSequence maTokens2;
+ OUString msRef;
+ OUString maInputTitle;
+ OUString maInputMessage;
+ OUString maErrorTitle;
+ OUString maErrorMessage;
+ sal_Int32 mnType;
+ sal_Int32 mnOperator;
+ sal_Int32 mnErrorStyle;
+ bool mbShowInputMsg:1;
+ bool mbShowErrorMsg:1;
+ bool mbNoDropDown:1;
+ bool mbAllowBlank:1;
+
+ explicit ValidationModel();
+
+ /** Sets the passed BIFF validation type. */
+ void setBiffType( sal_uInt8 nType );
+ /** Sets the passed BIFF operator. */
+ void setBiffOperator( sal_uInt8 nOperator );
+ /** Sets the passed BIFF error style. */
+ void setBiffErrorStyle( sal_uInt8 nErrorStyle );
+};
+
+class WorksheetGlobals;
+typedef std::shared_ptr< WorksheetGlobals > WorksheetGlobalsRef;
+
+class IWorksheetProgress {
+public:
+ virtual ~IWorksheetProgress() {}
+ virtual ISegmentProgressBarRef getRowProgress() = 0;
+ virtual void setCustomRowProgress(
+ const ISegmentProgressBarRef &rxRowProgress ) = 0;
+};
+
+class WorksheetHelper : public WorkbookHelper
+{
+public:
+ /*implicit*/ WorksheetHelper( WorksheetGlobals& rSheetGlob );
+
+ static WorksheetGlobalsRef constructGlobals(
+ const WorkbookHelper& rHelper,
+ const ISegmentProgressBarRef& rxProgressBar,
+ WorksheetType eSheetType,
+ SCTAB nSheet );
+
+ // horrible accessor for hidden WorksheetGlobals ...
+ static IWorksheetProgress *getWorksheetInterface( const WorksheetGlobalsRef &xRef );
+
+ /** Returns the type of this sheet. */
+ WorksheetType getSheetType() const;
+ /** Returns the index of the current sheet. */
+ SCTAB getSheetIndex() const;
+ /** Returns the XSpreadsheet interface of the current sheet. */
+ const css::uno::Reference< css::sheet::XSpreadsheet >&
+ getSheet() const;
+
+ /** Returns the XCell interface for the passed cell address. */
+ css::uno::Reference< css::table::XCell >
+ getCell( const ScAddress& rAddress ) const;
+ /** Returns the XCellRange interface for the passed cell range address. */
+ css::uno::Reference< css::table::XCellRange >
+ getCellRange( const ScRange& rRange ) const;
+
+ /** Returns the XDrawPage interface of the draw page of the current sheet. */
+ css::uno::Reference< css::drawing::XDrawPage >
+ getDrawPage() const;
+
+ /** Returns the absolute cell position in 1/100 mm. */
+ css::awt::Point getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const;
+ /** Returns the size of the entire drawing page in 1/100 mm. */
+ const css::awt::Size& getDrawPageSize() const;
+
+ /** Returns the buffer for cell contents and cell formatting. */
+ SheetDataBuffer& getSheetData() const;
+ /** Returns the conditional formatting in this sheet. */
+ CondFormatBuffer& getCondFormats() const;
+ /** Returns the buffer for all cell comments in this sheet. */
+ CommentsBuffer& getComments() const;
+ /** Returns the auto filters for the sheet. */
+ AutoFilterBuffer& getAutoFilters() const;
+ /** Returns the buffer for all web query tables in this sheet. */
+ QueryTableBuffer& getQueryTables() const;
+ /** Returns the worksheet settings object. */
+ WorksheetSettings& getWorksheetSettings() const;
+ /** Returns the page/print settings for this sheet. */
+ PageSettings& getPageSettings() const;
+ /** Returns the view settings for this sheet. */
+ SheetViewSettings& getSheetViewSettings() const;
+ /** Returns the VML drawing page for this sheet (OOXML/BIFF12 only). */
+ VmlDrawing& getVmlDrawing() const;
+
+ ExtLst& getExtLst() const;
+
+ /** Sets a column or row page break described in the passed struct. */
+ void setPageBreak( const PageBreakModel& rModel, bool bRowBreak );
+ /** Inserts the hyperlink URL into the spreadsheet. */
+ void setHyperlink( const HyperlinkModel& rModel );
+ /** Inserts the data validation settings into the spreadsheet. */
+ void setValidation( const ValidationModel& rModel );
+ /** Sets the path to the DrawingML fragment of this sheet. */
+ void setDrawingPath( const OUString& rDrawingPath );
+ /** Sets the path to the legacy VML drawing fragment of this sheet. */
+ void setVmlDrawingPath( const OUString& rVmlDrawingPath );
+
+ /** Extends the used area of this sheet by the passed cell position. */
+ void extendUsedArea( const ScAddress& rAddress );
+ /** Extends the used area of this sheet by the passed cell range. */
+ void extendUsedArea( const ScRange& rRange );
+ /** Extends the shape bounding box by the position and size of the passed rectangle (in 1/100 mm). */
+ void extendShapeBoundingBox( const css::awt::Rectangle& rShapeRect );
+
+ /** Sets base width for all columns (without padding pixels). This value
+ is only used, if width has not been set with setDefaultColumnWidth(). */
+ void setBaseColumnWidth( sal_Int32 nWidth );
+ /** Sets default width for all columns. This function overrides the base
+ width set with the setBaseColumnWidth() function. */
+ void setDefaultColumnWidth( double fWidth );
+ /** Sets column settings for a specific range of columns.
+ @descr Column default formatting is converted directly, other settings
+ are cached and converted in the finalizeWorksheetImport() call. */
+ void setColumnModel( const ColumnModel& rModel );
+
+ /** Sets default height and hidden state for all unused rows in the sheet. */
+ void setDefaultRowSettings(
+ double fHeight, bool bCustomHeight,
+ bool bHidden, bool bThickTop, bool bThickBottom );
+ /** Sets row settings for a specific range of rows.
+ @descr Row default formatting is converted directly, other settings
+ are cached and converted in the finalizeWorksheetImport() call. */
+ void setRowModel( const RowModel& rModel );
+
+ /** Inserts a rich-string cell directly into the Calc sheet. */
+ void putRichString(
+ const ScAddress& rAddress,
+ RichString& rString,
+ const oox::xls::Font* pFirstPortionFont,
+ bool bSingleLine = false);
+
+ /** Inserts a formula cell directly into the Calc sheet. */
+ void putFormulaTokens(
+ const ScAddress& rAddress, const ApiTokenSequence& rTokens );
+
+ /** Initial conversion before importing the worksheet. */
+ void initializeWorksheetImport();
+ /** Final conversion after importing the worksheet. */
+ void finalizeWorksheetImport();
+ /** Final import of drawing objects. Has to be called after all content has been imported */
+ void finalizeDrawingImport();
+
+ void setCellFormula( const ScAddress& rTokenAddress, const OUString& );
+
+ void setCellFormula(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType );
+
+ void setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr );
+
+ void createSharedFormulaMapEntry(
+ const ScAddress& rAddress,
+ sal_Int32 nSharedId, const OUString& rTokens );
+
+ void setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType );
+
+ ScDocument& getScDocument();
+
+
+private:
+ WorksheetGlobals& mrSheetGlob;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheetsettings.hxx b/sc/source/filter/inc/worksheetsettings.hxx
new file mode 100644
index 0000000000..2985d4b95b
--- /dev/null
+++ b/sc/source/filter/inc/worksheetsettings.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "richstring.hxx"
+#include "worksheethelper.hxx"
+#include <tabprotection.hxx>
+
+namespace oox::xls {
+
+/** Sheet and outline settings. */
+struct SheetSettingsModel
+{
+ OUString maCodeName; /// VBA module codename.
+ XlsColor maTabColor; /// Sheet tab color.
+ bool mbFilterMode; /// True = sheet contains active filter.
+ bool mbApplyStyles; /// True = automatic styles when creating outlines.
+ bool mbSummaryBelow; /// True = row outline symbols below group.
+ bool mbSummaryRight; /// True = column outline symbols right of group.
+
+ explicit SheetSettingsModel();
+};
+
+/** Sheet protection settings. */
+struct SheetProtectionModel
+{
+ OUString maAlgorithmName; /// Algorithm name, "SHA-512", "SHA-1", ...
+ OUString maHashValue; /// Hash value computed by the algorithm, base-64 encoded
+ OUString maSaltValue; /// Salt value to be prepended to the password, base-64 encoded
+ sal_uInt32 mnSpinCount; /// Spin count, iterations to run algorithm
+ sal_uInt16 mnPasswordHash; /// Hash value from sheet protection password. (unrelated to the above)
+ bool mbSheet; /// True = sheet protection enabled, locked cells are protected.
+ bool mbObjects; /// True = objects locked.
+ bool mbScenarios; /// True = scenarios locked.
+ bool mbFormatCells; /// True = format cells locked.
+ bool mbFormatColumns; /// True = format columns locked.
+ bool mbFormatRows; /// True = format rows locked.
+ bool mbInsertColumns; /// True = insert columns locked.
+ bool mbInsertRows; /// True = insert rows locked.
+ bool mbInsertHyperlinks; /// True = insert hyperlinks locked.
+ bool mbDeleteColumns; /// True = delete columns locked.
+ bool mbDeleteRows; /// True = delete rows locked.
+ bool mbSelectLocked; /// True = select locked cells locked.
+ bool mbSort; /// True = sorting locked.
+ bool mbAutoFilter; /// True = autofilters locked.
+ bool mbPivotTables; /// True = pivot tables locked.
+ bool mbSelectUnlocked; /// True = select unlocked cells locked.
+
+ ::std::vector< ScEnhancedProtection > maEnhancedProtections;
+
+ explicit SheetProtectionModel();
+};
+
+class WorksheetSettings : public WorksheetHelper
+{
+public:
+ explicit WorksheetSettings( const WorksheetHelper& rHelper );
+
+ /** Imports sheet settings from the sheetPr element. */
+ void importSheetPr( const AttributeList& rAttribs );
+ /** Imports chart sheet settings from the sheetPr element. */
+ void importChartSheetPr( const AttributeList& rAttribs );
+ /** Imports the sheet tab color from the tabColor element. */
+ void importTabColor( const AttributeList& rAttribs );
+ /** Imports outline settings from the outlinePr element. */
+ void importOutlinePr( const AttributeList& rAttribs );
+ /** Imports protection settings from the sheetProtection element. */
+ void importSheetProtection( const AttributeList& rAttribs );
+ /** Imports enhanced protection settings from the protectedRange element. */
+ void importProtectedRange( const AttributeList& rAttribs );
+ /** Imports protection settings from the sheetProtection element of a chart sheet. */
+ void importChartProtection( const AttributeList& rAttribs );
+ /** Imports phonetic settings from the phoneticPr element. */
+ void importPhoneticPr( const AttributeList& rAttribs );
+
+ /** Imports sheet properties from the SHEETPR record. */
+ void importSheetPr( SequenceInputStream& rStrm );
+ /** Imports sheet properties from the CHARTSHEETPR record. */
+ void importChartSheetPr( SequenceInputStream& rStrm );
+ /** Imports sheet protection settings from the SHEETPROTECTION record. */
+ void importSheetProtection( SequenceInputStream& rStrm );
+ /** Imports chart sheet protection settings from the CHARTPROTECTION record. */
+ void importChartProtection( SequenceInputStream& rStrm );
+ /** Imports phonetic settings from the PHONETICPR record. */
+ void importPhoneticPr( SequenceInputStream& rStrm );
+
+ /** Converts the imported worksheet settings. */
+ void finalizeImport();
+
+private:
+ PhoneticSettings maPhoneticSett;
+ SheetSettingsModel maSheetSettings;
+ SheetProtectionModel maSheetProt;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xcl97esc.hxx b/sc/source/filter/inc/xcl97esc.hxx
new file mode 100644
index 0000000000..3a0a606113
--- /dev/null
+++ b/sc/source/filter/inc/xcl97esc.hxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <stack>
+#include <filter/msfilter/escherex.hxx>
+#include "xeroot.hxx"
+#include <unotools/tempfile.hxx>
+
+namespace com::sun::star::awt { class XControlModel; }
+
+class XclEscherExGlobal : public EscherExGlobal, protected XclExpRoot
+{
+public:
+ explicit XclEscherExGlobal( const XclExpRoot& rRoot );
+
+private:
+ /** Override to create a new temporary file and return its stream. */
+ virtual SvStream* ImplQueryPictureStream() override;
+
+private:
+ ::std::optional< ::utl::TempFileFast > moPicTempFile;
+ SvStream* mpPicStrm;
+};
+
+class XclObj;
+class XclExpDffAnchorBase;
+class XclEscherHostAppData;
+class XclEscherClientData;
+class XclEscherClientTextbox;
+class XclExpOcxControlObj;
+class XclExpTbxControlObj;
+class XclExpShapeObj;
+class ShapeInteractionHelper
+{
+public:
+ static XclExpShapeObj* CreateShapeObj( XclExpObjectManager& rObjMgr, const css::uno::Reference<
+ css::drawing::XShape >& xShape, ScDocument* pDoc );
+ static void PopulateShapeInteractionInfo( const XclExpObjectManager& rObjMgr, const css::uno::Reference< css::drawing::XShape >& xShape, EscherExHostAppData& rHostAppData );
+};
+
+class XclEscherEx : public EscherEx, protected XclExpRoot
+{
+public:
+ explicit XclEscherEx(
+ const XclExpRoot& rRoot,
+ XclExpObjectManager& rObjMgr,
+ SvStream& rStrm,
+ const XclEscherEx* pParent = nullptr );
+ virtual ~XclEscherEx() override;
+
+ /** Called by MSODRAWING record constructors to initialize the DFF stream
+ fragment they will own. returns the DFF fragment identifier. */
+ sal_uInt32 InitNextDffFragment();
+ /** Called after some data has been written to the DFF stream, to update
+ the end position of the DFF fragment owned by an MSODRAWING record. */
+ void UpdateDffFragmentEnd();
+
+ /** Returns the position of the specified DFF stream fragment. */
+ sal_uInt32 GetDffFragmentPos( sal_uInt32 nFragmentKey );
+ /** Returns the size of the specified DFF stream fragment. */
+ sal_uInt32 GetDffFragmentSize( sal_uInt32 nFragmentKey );
+ /** Returns true, if there is more data left in the DFF stream than owned
+ by the last MSODRAWING record. */
+ bool HasPendingDffData();
+
+ /** Creates a new DFF client anchor object and calculates the anchor
+ position of the passed object. Caller takes ownership! */
+ XclExpDffAnchorBase* CreateDffAnchor( const SdrObject& rSdrObj ) const;
+
+ virtual EscherExHostAppData* StartShape(
+ const css::uno::Reference< css::drawing::XShape>& rxShape,
+ const tools::Rectangle* pChildAnchor ) override;
+ virtual void EndShape( sal_uInt16 nShapeType, sal_uInt32 nShapeID ) override;
+ virtual EscherExHostAppData* EnterAdditionalTextGroup() override;
+
+ /// Flush and merge PicStream into EscherStream
+ void EndDocument();
+ /** Creates an OCX form control OBJ record from the passed form control.
+ @descr Writes the form control data to the 'Ctls' stream. */
+ std::unique_ptr<XclExpOcxControlObj> CreateOCXCtrlObj(
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor );
+
+private:
+ tools::SvRef<SotStorageStream> mxCtlsStrm; /// The 'Ctls' stream.
+ /** Creates a TBX form control OBJ record from the passed form control. */
+ std::unique_ptr<XclExpTbxControlObj> CreateTBXCtrlObj(
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor );
+
+private:
+ /** Tries to get the name of a Basic macro from a control. */
+ void ConvertTbxMacro(
+ XclExpTbxControlObj& rTbxCtrlObj,
+ css::uno::Reference< css::awt::XControlModel > const & xCtrlModel );
+
+ void DeleteCurrAppData();
+
+private:
+ XclExpObjectManager& mrObjMgr;
+ std::stack< std::pair< XclObj*, std::unique_ptr<XclEscherHostAppData> > > aStack;
+ XclObj* pCurrXclObj;
+ std::unique_ptr<XclEscherHostAppData> pCurrAppData;
+ std::unique_ptr<XclEscherClientData> pTheClientData; // always the same
+ XclEscherClientTextbox* pAdditionalText;
+ sal_uInt16 nAdditionalText;
+ sal_uInt32 mnNextKey;
+ bool mbIsRootDff;
+};
+
+// --- class XclEscherHostAppData ------------------------------------
+
+class XclEscherHostAppData : public EscherExHostAppData
+{
+private:
+ bool bStackedGroup;
+
+public:
+ XclEscherHostAppData() : bStackedGroup( false )
+ {}
+ void SetStackedGroup( bool b ) { bStackedGroup = b; }
+ bool IsStackedGroup() const { return bStackedGroup; }
+};
+
+// --- class XclEscherClientData -------------------------------------
+
+class XclEscherClientData : public EscherExClientRecord_Base
+{
+public:
+ XclEscherClientData() {}
+ virtual void WriteData( EscherEx& rEx ) const override;
+};
+
+// --- class XclEscherClientTextbox ----------------------------------
+
+class SdrTextObj;
+
+class XclEscherClientTextbox : public EscherExClientRecord_Base, protected XclExpRoot
+{
+private:
+ const SdrTextObj& rTextObj;
+ XclObj* pXclObj;
+
+public:
+ XclEscherClientTextbox(
+ const XclExpRoot& rRoot,
+ const SdrTextObj& rObj,
+ XclObj* pObj );
+
+ //! ONLY for the AdditionalText mimic
+ void SetXclObj( XclObj* p ) { pXclObj = p; }
+
+ virtual void WriteData( EscherEx& rEx ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xcl97rec.hxx b/sc/source/filter/inc/xcl97rec.hxx
new file mode 100644
index 0000000000..57210208dd
--- /dev/null
+++ b/sc/source/filter/inc/xcl97rec.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "excrecds.hxx"
+#include "xlescher.hxx"
+#include "xestring.hxx"
+#include <tabprotection.hxx>
+#include <svx/svdobj.hxx>
+#include <oox/export/drawingml.hxx>
+
+class XclObj;
+class XclExpMsoDrawing;
+class SdrCaptionObj;
+class SdrTextObj;
+class XclTxo;
+class XclEscherEx;
+class SdrOle2Obj;
+
+class ScURLTransformer : public oox::drawingml::URLTransformer
+{
+public:
+ explicit ScURLTransformer(ScDocument& rDoc);
+
+ virtual OUString getTransformedString(const OUString& rURL) const override;
+
+ virtual bool isExternalURL(const OUString& rURL) const override;
+
+private:
+ ScDocument& mrDoc;
+};
+
+class XclExpObjList : public ExcEmptyRec, protected XclExpRoot
+{
+public:
+
+ typedef std::vector<std::unique_ptr<XclObj>>::iterator iterator;
+
+ explicit XclExpObjList( const XclExpRoot& rRoot, XclEscherEx& rEscherEx );
+ virtual ~XclExpObjList() override;
+
+ /// return: 1-based ObjId
+ ///! count>=0xFFFF: Obj will be deleted, return 0
+ sal_uInt16 Add( std::unique_ptr<XclObj> );
+
+ /**
+ * @brief Remove last element in the list.
+ */
+ std::unique_ptr<XclObj> pop_back ();
+
+ bool empty () const { return maObjs.empty(); }
+
+ size_t size () const { return maObjs.size(); }
+
+ iterator begin () { return maObjs.begin(); }
+
+ iterator end () { return maObjs.end(); }
+
+ XclExpMsoDrawing* GetMsodrawingPerSheet() { return pMsodrawingPerSheet.get(); }
+
+ /// close groups and DgContainer opened in ctor
+ void EndSheet();
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ SCTAB mnScTab;
+
+ XclEscherEx& mrEscherEx;
+ std::unique_ptr<XclExpMsoDrawing> pMsodrawingPerSheet;
+ std::unique_ptr<XclExpMsoDrawing> pSolverContainer;
+
+ std::vector<std::unique_ptr<XclObj>> maObjs;
+};
+
+// --- class XclObj --------------------------------------------------
+
+class XclObj : public XclExpRecord
+{
+protected:
+ XclEscherEx& mrEscherEx;
+ XclExpMsoDrawing* pMsodrawing;
+ std::unique_ptr<XclExpMsoDrawing> pClientTextbox;
+ std::unique_ptr<XclTxo> pTxo;
+ sal_uInt16 mnObjType;
+ sal_uInt16 nObjId;
+ sal_uInt16 nGrbit;
+ SCTAB mnScTab;
+ bool bFirstOnSheet;
+
+ bool mbOwnEscher; /// true = Escher part created on the fly.
+
+ /** @param bOwnEscher If set to true, this object will create its escher data.
+ See SetOwnEscher() for details. */
+ explicit XclObj( XclExpObjectManager& rObjMgr, sal_uInt16 nObjType, bool bOwnEscher = false );
+
+ void ImplWriteAnchor( const SdrObject* pSdrObj, const tools::Rectangle* pChildAnchor );
+
+ // overwritten for writing MSODRAWING record
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ virtual void WriteSubRecs( XclExpStream& rStrm );
+ void SaveTextRecs( XclExpStream& rStrm );
+
+public:
+ virtual ~XclObj() override;
+
+ sal_uInt16 GetObjType() const { return mnObjType; }
+
+ void SetId( sal_uInt16 nId ) { nObjId = nId; }
+
+ void SetTab( SCTAB nScTab ) { mnScTab = nScTab; }
+ SCTAB GetTab() const { return mnScTab; }
+
+ void SetLocked( bool b ) { SetGrBit(b, 0x0001); }
+ void SetPrintable( bool b ) { SetGrBit(b, 0x0010); }
+ void SetAutoFill( bool b ) { SetGrBit(b, 0x2000); }
+ void SetAutoLine( bool b ) { SetGrBit(b, 0x4000); }
+ void SetGrBit( bool b, int f )
+ {
+ if (b)
+ nGrbit |= f;
+ else
+ nGrbit &= ~f;
+ }
+
+ // set corresponding Excel object type in OBJ/ftCmo
+ void SetEscherShapeType( sal_uInt16 nType );
+ void SetEscherShapeTypeGroup() { mnObjType = EXC_OBJTYPE_GROUP; }
+
+ /** If set to true, this object has created its own escher data.
+ @descr This causes the function EscherEx::EndShape() to not post process
+ this object. This is used i.e. for form controls. They are not handled in
+ the svx base code, so the XclExpEscherOcxCtrl c'tor creates the escher data
+ itself. The svx base code does not receive the correct shape ID after the
+ EscherEx::StartShape() call, which would result in deleting the object in
+ EscherEx::EndShape(). */
+ /** Returns true, if the object has created the escher data itself.
+ @descr See SetOwnEscher() for details. */
+ bool IsOwnEscher() const { return mbOwnEscher; }
+
+ //! actually writes ESCHER_ClientTextbox
+ void SetText( const XclExpRoot& rRoot, const SdrTextObj& rObj );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// --- class XclObjComment -------------------------------------------
+
+class XclObjComment : public XclObj
+{
+ ScAddress maScPos;
+
+ // no need to use std::unique_ptr< SdrCaptionObj >
+ SdrCaptionObj* mpCaption;
+
+ bool mbVisible;
+ tools::Rectangle maFrom;
+ tools::Rectangle maTo;
+
+public:
+ XclObjComment( XclExpObjectManager& rObjMgr,
+ const tools::Rectangle& rRect, const EditTextObject& rEditObj, SdrCaptionObj* pCaption, bool bVisible, const ScAddress& rAddress, const tools::Rectangle &rFrom, const tools::Rectangle &To );
+ virtual ~XclObjComment() override;
+
+ /** c'tor process for formatted text objects above .
+ @descr used to construct the MSODRAWING Escher object properties. */
+ void ProcessEscherObj( const XclExpRoot& rRoot,
+ const tools::Rectangle& rRect, SdrObject* pCaption, bool bVisible );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// --- class XclObjDropDown ------------------------------------------
+
+class XclObjDropDown : public XclObj
+{
+private:
+ bool bIsFiltered;
+
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+protected:
+public:
+ XclObjDropDown( XclExpObjectManager& rObjMgr, const ScAddress& rPos, bool bFilt );
+ virtual ~XclObjDropDown() override;
+};
+
+// --- class XclTxo --------------------------------------------------
+
+class XclTxo : public ExcRecord
+{
+public:
+ XclTxo( const OUString& rString, sal_uInt16 nFontIx );
+ XclTxo( const XclExpRoot& rRoot, const SdrTextObj& rEditObj );
+ XclTxo( const XclExpRoot& rRoot, const EditTextObject& rEditObj, SdrObject* pCaption );
+
+ void SetHorAlign( sal_uInt8 nHorAlign ) { mnHorAlign = nHorAlign; }
+ void SetVerAlign( sal_uInt8 nVerAlign ) { mnVerAlign = nVerAlign; }
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+private:
+ XclExpStringRef mpString; /// Text and formatting data.
+ sal_uInt16 mnRotation; /// Text rotation.
+ sal_uInt8 mnHorAlign; /// Horizontal alignment.
+ sal_uInt8 mnVerAlign; /// Vertical alignment.
+};
+
+// --- class XclObjOle -----------------------------------------------
+
+class XclObjOle : public XclObj
+{
+private:
+
+ const SdrOle2Obj& rOleObj;
+ SotStorage* pRootStorage;
+
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+public:
+ XclObjOle( XclExpObjectManager& rObjMgr, const SdrOle2Obj& rObj );
+ virtual ~XclObjOle() override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// --- class XclObjAny -----------------------------------------------
+
+class XclObjAny : public XclObj
+{
+protected:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+public:
+ XclObjAny( XclExpObjectManager& rObjMgr,
+ const css::uno::Reference< css::drawing::XShape >& rShape,
+ ScDocument* pDoc);
+ virtual ~XclObjAny() override;
+
+ const css::uno::Reference< css::drawing::XShape >&
+ GetShape() const { return mxShape; }
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+ static void WriteFromTo( XclExpXmlStream& rStrm, const XclObjAny& rObj );
+ static void WriteFromTo( XclExpXmlStream& rStrm, const css::uno::Reference< css::drawing::XShape >& rShape, SCTAB nTab );
+
+private:
+ css::uno::Reference< css::drawing::XShape >
+ mxShape;
+ ScDocument* mpDoc;
+};
+
+// --- class ExcBof8_Base --------------------------------------------
+
+class ExcBof8_Base : public ExcBof_Base
+{
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ ExcBof8_Base();
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// --- class ExcBofW8 ------------------------------------------------
+// Header Record for WORKBOOKS
+
+class ExcBofW8 : public ExcBof8_Base
+{
+public:
+ ExcBofW8();
+};
+
+// --- class ExcBof8 -------------------------------------------------
+// Header Record for WORKSHEETS
+
+class ExcBof8 : public ExcBof8_Base
+{
+public:
+ ExcBof8();
+};
+
+// --- class ExcBundlesheet8 -----------------------------------------
+
+class ExcBundlesheet8 : public ExcBundlesheetBase
+{
+private:
+ OUString sUnicodeName;
+ XclExpString GetName() const { return XclExpString( sUnicodeName, XclStrFlags::EightBitLength );}
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ ExcBundlesheet8( const RootData& rRootData, SCTAB nTab );
+ ExcBundlesheet8( OUString aString );
+
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// --- class XclObproj -----------------------------------------------
+
+class XclObproj : public ExcRecord
+{
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// ---- class XclCodename --------------------------------------------
+
+class XclCodename : public ExcRecord
+{
+private:
+ XclExpString aName;
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclCodename( const OUString& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// ---- Scenarios ----------------------------------------------------
+// - ExcEScenarioCell a cell of a scenario range
+// - ExcEScenario all ranges of a scenario table
+// - ExcEScenarioManager list of scenario tables
+
+class ExcEScenarioCell
+{
+private:
+ sal_uInt16 nCol;
+ sal_uInt16 nRow;
+ XclExpString sText;
+
+protected:
+public:
+ ExcEScenarioCell( sal_uInt16 nC, sal_uInt16 nR, const OUString& rTxt );
+
+ std::size_t GetStringBytes() const
+ { return sText.GetSize(); }
+
+ void WriteAddress( XclExpStream& rStrm ) const ;
+ void WriteText( XclExpStream& rStrm ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+class ExcEScenario : public ExcRecord
+{
+private:
+ std::size_t nRecLen;
+ XclExpString sName;
+ XclExpString sComment;
+ XclExpString sUserName;
+ bool bProtected;
+
+ std::vector<ExcEScenarioCell> aCells;
+
+ bool Append( sal_uInt16 nCol, sal_uInt16 nRow, const OUString& rTxt );
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+protected:
+public:
+ ExcEScenario( const XclExpRoot& rRoot, SCTAB nTab );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class ExcEScenarioManager : public ExcRecord
+{
+private:
+ sal_uInt16 nActive;
+ std::vector<ExcEScenario> aScenes;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+protected:
+public:
+ ExcEScenarioManager( const XclExpRoot& rRoot, SCTAB nTab );
+ virtual ~ExcEScenarioManager() override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+/** Represents a FEATHDR (SHEETPROTECTION) record that stores sheet protection
+ options. Note that a sheet still needs to save its sheet protection
+ options even when it's not protected. */
+class XclExpSheetProtectOptions : public XclExpRecord
+{
+public:
+ explicit XclExpSheetProtectOptions( const XclExpRoot& rRoot, SCTAB nTab );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnOptions; /// Encoded sheet protection options.
+};
+
+/** Represents one EnhancedProtection feature in a FEAT record.
+ To be written only if such feature exists. */
+class XclExpSheetEnhancedProtection : public XclExpRecord
+{
+public:
+ explicit XclExpSheetEnhancedProtection( const XclExpRoot& rRoot, ScEnhancedProtection aProt );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+ ScEnhancedProtection maEnhancedProtection;
+};
+
+class XclCalccount : public ExcRecord
+{
+private:
+ sal_uInt16 nCount;
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclCalccount( const ScDocument& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclIteration : public ExcRecord
+{
+private:
+ sal_uInt16 nIter;
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclIteration( const ScDocument& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclDelta : public ExcRecord
+{
+private:
+ double fDelta;
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclDelta( const ScDocument& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclRefmode : public XclExpBoolRecord
+{
+public:
+ XclRefmode( const ScDocument& );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpFileEncryption : public XclExpRecord
+{
+public:
+ explicit XclExpFileEncryption( const XclExpRoot& rRoot );
+ virtual ~XclExpFileEncryption() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+};
+
+/** Beginning of User Interface Records */
+class XclExpInterfaceHdr : public XclExpUInt16Record
+{
+public:
+ explicit XclExpInterfaceHdr( sal_uInt16 nCodePage );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+/** End of User Interface Records */
+class XclExpInterfaceEnd : public XclExpRecord
+{
+public:
+ explicit XclExpInterfaceEnd();
+ virtual ~XclExpInterfaceEnd() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+/** Write Access User Name - This record contains the user name, which is
+ the name you type when you install Excel. */
+class XclExpWriteAccess : public XclExpRecord
+{
+public:
+ explicit XclExpWriteAccess();
+ virtual ~XclExpWriteAccess() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+class XclExpFileSharing : public XclExpRecord
+{
+public:
+ explicit XclExpFileSharing( const XclExpRoot& rRoot, sal_uInt16 nPasswordHash, bool bRecommendReadOnly );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maUserName;
+ sal_uInt16 mnPasswordHash;
+ bool mbRecommendReadOnly;
+};
+
+class XclExpProt4Rev : public XclExpRecord
+{
+public:
+ explicit XclExpProt4Rev();
+ virtual ~XclExpProt4Rev() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+class XclExpProt4RevPass : public XclExpRecord
+{
+public:
+ explicit XclExpProt4RevPass();
+ virtual ~XclExpProt4RevPass() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+class XclExpRecalcId : public XclExpDummyRecord
+{
+public:
+ explicit XclExpRecalcId();
+};
+
+class XclExpBookExt : public XclExpDummyRecord
+{
+public:
+ explicit XclExpBookExt();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xechart.hxx b/sc/source/filter/inc/xechart.hxx
new file mode 100644
index 0000000000..fb1eefca85
--- /dev/null
+++ b/sc/source/filter/inc/xechart.hxx
@@ -0,0 +1,1192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "xerecord.hxx"
+#include "xlchart.hxx"
+#include "xlformula.hxx"
+#include "xlstyle.hxx"
+#include "xeroot.hxx"
+#include "xestring.hxx"
+
+#include <memory>
+#include <map>
+
+class Size;
+namespace tools { class Rectangle; }
+
+namespace com::sun::star {
+ namespace awt
+ {
+ struct Rectangle;
+ }
+ namespace frame
+ {
+ class XModel;
+ }
+ namespace chart
+ {
+ class XAxis;
+ }
+ namespace chart2
+ {
+ struct ScaleData;
+ class XChartDocument;
+ class XDiagram;
+ class XCoordinateSystem;
+ class XChartType;
+ class XDataSeries;
+ class XAxis;
+ class XTitle;
+ class XFormattedString;
+ class XRegressionCurve;
+ namespace data
+ {
+ class XDataSequence;
+ class XLabeledDataSequence;
+ }
+ }
+}
+
+// Common =====================================================================
+
+struct XclExpChRootData;
+class XclExpChChart;
+
+/** Base class for complex chart classes, provides access to other components
+ of the chart.
+
+ Keeps also track of future record levels and writes the needed future
+ records on demand.
+ */
+class XclExpChRoot : public XclExpRoot
+{
+public:
+ explicit XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData );
+ virtual ~XclExpChRoot() override;
+
+ XclExpChRoot(XclExpChRoot const &) = default;
+ XclExpChRoot(XclExpChRoot &&) = default;
+ XclExpChRoot & operator =(XclExpChRoot const &) = delete; // due to XclExpRoot
+ XclExpChRoot & operator =(XclExpChRoot &&) = delete; // due to XclExpRoot
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclExpChRoot& GetChRoot() const { return *this; }
+ /** Returns the API Chart document model. */
+ css::uno::Reference< css::chart2::XChartDocument > const &
+ GetChartDocument() const;
+ /** Returns a reference to the parent chart data object. */
+ XclExpChChart& GetChartData() const;
+ /** Returns chart type info for a unique chart type identifier. */
+ const XclChTypeInfo& GetChartTypeInfo( XclChTypeId eType ) const;
+ /** Returns the first fitting chart type info for the passed service name. */
+ const XclChTypeInfo& GetChartTypeInfo( std::u16string_view rServiceName ) const;
+
+ /** Returns an info struct about auto formatting for the passed object type. */
+ const XclChFormatInfo& GetFormatInfo( XclChObjectType eObjType ) const;
+
+ /** Starts the API chart document conversion. Must be called once before all API conversion. */
+ void InitConversion( css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc,
+ const tools::Rectangle& rChartRect ) const;
+ /** Finishes the API chart document conversion. Must be called once after all API conversion. */
+ void FinishConversion() const;
+
+ /** Returns true, if the passed color equals to the specified system color. */
+ bool IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const;
+ /** Sets a system color and the respective color identifier. */
+ void SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const;
+
+ /** Converts the passed horizontal coordinate from 1/100 mm to Excel chart units. */
+ sal_Int32 CalcChartXFromHmm( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from 1/100 mm to Excel chart units. */
+ sal_Int32 CalcChartYFromHmm( sal_Int32 nPosY ) const;
+ /** Converts the passed rectangle from 1/100 mm to Excel chart units. */
+ XclChRectangle CalcChartRectFromHmm( const css::awt::Rectangle& rRect ) const;
+
+ /** Reads all line properties from the passed property set. */
+ void ConvertLineFormat(
+ XclChLineFormat& rLineFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode ) const;
+ /** Reads solid area properties from the passed property set.
+ @return true = object contains complex fill properties. */
+ bool ConvertAreaFormat(
+ XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode ) const;
+ /** Reads gradient or bitmap area properties from the passed property set. */
+ void ConvertEscherFormat(
+ XclChEscherFormat& rEscherFmt,
+ XclChPicFormat& rPicFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode ) const;
+ /** Reads font properties from the passed property set. */
+ sal_uInt16 ConvertFont(
+ const ScfPropertySet& rPropSet,
+ sal_Int16 nScript ) const;
+
+ /** Reads the pie rotation property and returns the converted angle. */
+ static sal_uInt16 ConvertPieRotation( const ScfPropertySet& rPropSet );
+
+protected:
+ /** Called from XclExpChGroupBase::Save, registers a new future record level. */
+ void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock );
+ /** Called from XclExpChFutureRecordBase::Save, Initializes the current future record level. */
+ void InitializeFutureRecBlock( XclExpStream& rStrm );
+ /** Called from XclExpChGroupBase::Save, finalizes the current future record level. */
+ void FinalizeFutureRecBlock( XclExpStream& rStrm );
+
+private:
+ typedef std::shared_ptr< XclExpChRootData > XclExpChRootDataRef;
+ XclExpChRootDataRef mxChData; /// Reference to the root data object.
+};
+
+/** Base class for chart record groups. Provides helper functions to write sub records.
+
+ A chart record group consists of a header record, followed by a CHBEGIN
+ record, followed by group sub records, and finished with a CHEND record.
+ */
+class XclExpChGroupBase : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChGroupBase(
+ const XclExpChRoot& rRoot, sal_uInt16 nFrType,
+ sal_uInt16 nRecId, std::size_t nRecSize = 0 );
+ virtual ~XclExpChGroupBase() override;
+
+ /** Saves the header record. Calls WriteSubRecords() to let derived classes write sub records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ /** Derived classes return whether there are any records embedded in this group. */
+ virtual bool HasSubRecords() const;
+ /** Derived classes implement writing any records embedded in this group. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) = 0;
+
+protected:
+ /** Sets context information for future record blocks. */
+ void SetFutureRecordContext( sal_uInt16 nFrContext,
+ sal_uInt16 nFrValue1 = 0, sal_uInt16 nFrValue2 = 0 );
+
+private:
+ XclChFrBlock maFrBlock; /// Future records block settings.
+};
+
+/** Base class for chart future records. On saving, the record writes missing
+ CHFRBLOCKBEGIN records automatically.
+ */
+class XclExpChFutureRecordBase : public XclExpFutureRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChFutureRecordBase( const XclExpChRoot& rRoot,
+ XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize );
+
+ /** Writes missing CHFRBLOCKBEGIN records and this record. */
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// Frame formatting ===========================================================
+
+class XclExpChFramePos : public XclExpRecord
+{
+public:
+ explicit XclExpChFramePos( sal_uInt16 nTLMode );
+
+ /** Returns read/write access to the frame position data. */
+ XclChFramePos& GetFramePosData() { return maData; }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChFramePos maData; /// Position of the frame.
+};
+
+typedef rtl::Reference< XclExpChFramePos > XclExpChFramePosRef;
+
+class XclExpChLineFormat : public XclExpRecord
+{
+public:
+ explicit XclExpChLineFormat( const XclExpChRoot& rRoot );
+
+ /** Converts line formatting properties from the passed property set. */
+ void Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+ /** Sets or clears the automatic flag. */
+ void SetAuto( bool bAuto ) { ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto ); }
+ /** Sets flag to show or hide an axis. */
+ void SetShowAxis( bool bShowAxis )
+ { ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS, bShowAxis ); }
+ /** Sets the line format to the specified default type. */
+ void SetDefault( XclChFrameType eDefFrameType );
+
+ /** Returns true, if the line format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO ); }
+ /** Returns true, if the line style is set to something visible. */
+ bool HasLine() const { return maData.mnPattern != EXC_CHLINEFORMAT_NONE; }
+ /** Returns true, if the line contains default formatting according to the passed frame type. */
+ bool IsDefault( XclChFrameType eDefFrameType ) const;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChLineFormat maData; /// Contents of the CHLINEFORMAT record.
+ sal_uInt32 mnColorId; /// Line color identifier.
+};
+
+typedef rtl::Reference< XclExpChLineFormat > XclExpChLineFormatRef;
+
+class XclExpChAreaFormat : public XclExpRecord
+{
+public:
+ explicit XclExpChAreaFormat( const XclExpChRoot& rRoot );
+
+ /** Converts area formatting properties from the passed property set.
+ @return true = object contains complex fill properties. */
+ bool Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+ /** Sets or clears the automatic flag. */
+ void SetAuto( bool bAuto ) { ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bAuto ); }
+ /** Sets the area format to the specified default type. */
+ void SetDefault( XclChFrameType eDefFrameType );
+
+ /** Returns true, if the area format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO ); }
+ /** Returns true, if the area style is set to something visible. */
+ bool HasArea() const { return maData.mnPattern != EXC_PATT_NONE; }
+ /** Returns true, if the area contains default formatting according to the passed frame type. */
+ bool IsDefault( XclChFrameType eDefFrameType ) const;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChAreaFormat maData; /// Contents of the CHAREAFORMAT record.
+ sal_uInt32 mnPattColorId; /// Pattern color identifier.
+ sal_uInt32 mnBackColorId; /// Pattern background color identifier.
+};
+
+typedef rtl::Reference< XclExpChAreaFormat > XclExpChAreaFormatRef;
+
+class XclExpChEscherFormat : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChEscherFormat( const XclExpChRoot& rRoot );
+
+ /** Converts complex area formatting from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+
+ /** Returns true, if the object contains valid formatting data. */
+ bool IsValid() const;
+
+ /** Writes the CHESCHERFORMAT record group to the stream, if complex formatting is extant. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ /** Returns true, if this record group contains a CHPICFORMAT record. */
+ virtual bool HasSubRecords() const override;
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Inserts a color from the contained Escher property set into the color palette. */
+ sal_uInt32 RegisterColor( sal_uInt16 nPropId );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChEscherFormat maData; /// Fill properties for complex areas (CHESCHERFORMAT record).
+ XclChPicFormat maPicFmt; /// Image options, e.g. stretched, stacked (CHPICFORMAT record).
+ sal_uInt32 mnColor1Id; /// First fill color identifier.
+ sal_uInt32 mnColor2Id; /// Second fill color identifier.
+};
+
+typedef rtl::Reference< XclExpChEscherFormat > XclExpChEscherFormatRef;
+
+/** Base class for record groups containing frame formatting.
+
+ Frame formatting can be part of several record groups, e.g. CHFRAME,
+ CHDATAFORMAT, CHDROPBAR. It consists of CHLINEFORMAT, CHAREAFORMAT, and
+ CHESCHERFORMAT group.
+ */
+class XclExpChFrameBase
+{
+public:
+ explicit XclExpChFrameBase();
+ virtual ~XclExpChFrameBase();
+
+protected:
+ /** Converts frame formatting properties from the passed property set. */
+ void ConvertFrameBase( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+ /** Sets the frame formatting to the specified default type. */
+ void SetDefaultFrameBase( const XclExpChRoot& rRoot,
+ XclChFrameType eDefFrameType, bool bIsFrame );
+
+ /** Returns true, if the frame contains default formatting (as if the frame is missing). */
+ bool IsDefaultFrameBase( XclChFrameType eDefFrameType ) const;
+
+ /** Writes all contained frame records to the passed stream. */
+ void WriteFrameRecords( XclExpStream& rStrm );
+
+private:
+ XclExpChLineFormatRef mxLineFmt; /// Line format (CHLINEFORMAT record).
+ XclExpChAreaFormatRef mxAreaFmt; /// Area format (CHAREAFORMAT record).
+ XclExpChEscherFormatRef mxEscherFmt; /// Complex area format (CHESCHERFORMAT record).
+};
+
+/** Represents the CHFRAME record group containing object frame properties.
+
+ The CHFRAME group consists of: CHFRAME, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclExpChFrame : public XclExpChGroupBase, public XclExpChFrameBase
+{
+public:
+ explicit XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType );
+
+ /** Converts frame formatting properties from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+ /** Sets the specified automatic flags. */
+ void SetAutoFlags( bool bAutoPos, bool bAutoSize );
+
+ /** Returns true, if the frame object contains default formats. */
+ bool IsDefault() const;
+ /** Returns true, if the frame object can be deleted because it contains default formats. */
+ bool IsDeleteable() const;
+
+ /** Writes the entire record group. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChFrame maData; /// Contents of the CHFRAME record.
+ XclChObjectType meObjType; /// Type of the represented object.
+};
+
+typedef rtl::Reference< XclExpChFrame > XclExpChFrameRef;
+
+// Source links ===============================================================
+
+class XclExpChSourceLink : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType );
+
+ void ConvertString( const OUString& aString );
+ /** Converts the passed source link, returns the number of linked values. */
+ sal_uInt16 ConvertDataSequence( css::uno::Reference< css::chart2::data::XDataSequence > const & xDataSeq,
+ bool bSplitToColumns, sal_uInt16 nDefCount = 0 );
+ /** Converts the passed sequence of formatted string objects, returns leading font index. */
+ sal_uInt16 ConvertStringSequence( const css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >& rStringSeq );
+ /** Converts the number format from the passed property set. */
+ void ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent );
+
+ void AppendString( std::u16string_view rStr );
+
+ /** Returns true, if this source link contains explicit string data. */
+ bool HasString() const { return mxString && !mxString->IsEmpty(); }
+
+ /** Writes the CHSOURCELINK record and optionally a CHSTRING record with explicit string data. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChSourceLink maData; /// Contents of the CHSOURCELINK record.
+ XclTokenArrayRef mxLinkFmla; /// Formula with link to source data.
+ XclExpStringRef mxString; /// Text data (CHSTRING record).
+};
+
+typedef rtl::Reference< XclExpChSourceLink > XclExpChSourceLinkRef;
+
+// Text =======================================================================
+
+/** The CHFONT record containing a font index for text objects. */
+class XclExpChFont : public XclExpUInt16Record
+{
+public:
+ explicit XclExpChFont( sal_uInt16 nFontIdx );
+};
+
+typedef rtl::Reference< XclExpChFont > XclExpChFontRef;
+
+/** The CHOBJECTLINK record linking a text object to a specific chart object. */
+class XclExpChObjectLink : public XclExpRecord
+{
+public:
+ explicit XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChObjectLink maData; /// Contents of the CHOBJECTLINK record.
+};
+
+typedef rtl::Reference< XclExpChObjectLink > XclExpChObjectLinkRef;
+
+/** Additional data label settings in the future record CHFRLABELPROPS. */
+class XclExpChFrLabelProps : public XclExpChFutureRecordBase
+{
+public:
+ explicit XclExpChFrLabelProps( const XclExpChRoot& rRoot );
+
+ /** Converts separator and the passed data label flags. */
+ void Convert(
+ const ScfPropertySet& rPropSet,
+ bool bShowCateg, bool bShowValue,
+ bool bShowPercent, bool bShowBubble );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChFrLabelProps maData; /// Contents of the CHFRLABELPROPS record.
+};
+
+typedef rtl::Reference< XclExpChFrLabelProps > XclExpChFrLabelPropsRef;
+
+/** Base class for objects with font settings. Provides font conversion helper functions. */
+class XclExpChFontBase
+{
+public:
+ virtual ~XclExpChFontBase();
+
+ /** Derived classes set font color and color identifier to internal data structures. */
+ virtual void SetFont( XclExpChFontRef xFont, const model::ComplexColor& rComplexColor, sal_uInt32 nColorId ) = 0;
+ /** Derived classes set text rotation to internal data structures. */
+ virtual void SetRotation( sal_uInt16 nRotation ) = 0;
+
+ /** Creates a CHFONT record from the passed font index, calls virtual function SetFont(). */
+ void ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx );
+ /** Creates a CHFONT record from the passed font index, calls virtual function SetFont(). */
+ void ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet );
+ /** Converts rotation settings, calls virtual function SetRotation(). */
+ void ConvertRotationBase( const ScfPropertySet& rPropSet, bool bSupportsStacked );
+};
+
+/** Represents the CHTEXT record group containing text object properties.
+
+ The CHTEXT group consists of: CHTEXT, CHBEGIN, CHFRAMEPOS, CHFONT,
+ CHFORMATRUNS, CHSOURCELINK, CHSTRING, CHFRAME group, CHOBJECTLINK, and CHEND.
+ */
+class XclExpChText : public XclExpChGroupBase, public XclExpChFontBase
+{
+public:
+ explicit XclExpChText( const XclExpChRoot& rRoot );
+
+ /** Sets font color and color identifier to internal data structures. */
+ virtual void SetFont( XclExpChFontRef xFont, model::ComplexColor const& rComplexColor, sal_uInt32 nColorId ) override;
+ /** Sets text rotation to internal data structures. */
+ virtual void SetRotation( sal_uInt16 nRotation ) override;
+
+ /** Converts all text settings of the passed title text object. */
+ void ConvertTitle( css::uno::Reference< css::chart2::XTitle > const & xTitle, sal_uInt16 nTarget, const OUString* pSubTitle );
+ /** Converts all text settings of the passed legend. */
+ void ConvertLegend( const ScfPropertySet& rPropSet );
+ /** Converts all settings of the passed data point caption text object. */
+ bool ConvertDataLabel( const ScfPropertySet& rPropSet,
+ const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos );
+ /** Converts all settings of the passed trend line equation box. */
+ void ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos );
+
+ /** Returns true, if the string object does not contain any text data. */
+ bool HasString() const { return mxSrcLink && mxSrcLink->HasString(); }
+ /** Returns the flags needed for the CHATTACHEDLABEL record. */
+ sal_uInt16 GetAttLabelFlags() const;
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChText maData; /// Contents of the CHTEXT record.
+ XclExpChFramePosRef mxFramePos; /// Relative text frame position (CHFRAMEPOS record).
+ XclExpChSourceLinkRef mxSrcLink; /// Linked data (CHSOURCELINK with CHSTRING record).
+ XclExpChFrameRef mxFrame; /// Text object frame properties (CHFRAME group).
+ XclExpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclExpChObjectLinkRef mxObjLink; /// Link target for this text object.
+ XclExpChFrLabelPropsRef mxLabelProps; /// Extended data label properties (CHFRLABELPROPS record).
+ sal_uInt32 mnTextColorId; /// Text color identifier.
+};
+
+typedef rtl::Reference< XclExpChText > XclExpChTextRef;
+
+// Data series ================================================================
+
+/** The CHMARKERFORMAT record containing data point marker formatting data. */
+class XclExpChMarkerFormat : public XclExpRecord
+{
+public:
+ explicit XclExpChMarkerFormat( const XclExpChRoot& rRoot );
+
+ /** Converts symbol properties from the passed property set. */
+ void Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx );
+ /** Converts symbol properties for stock charts from the passed property set. */
+ void ConvertStockSymbol( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, bool bCloseSymbol );
+
+ /** Returns true, if markers are enabled. */
+ bool HasMarker() const { return maData.mnMarkerType != EXC_CHMARKERFORMAT_NOSYMBOL; }
+ /** Returns true, if border line of markers is visible. */
+ bool HasLineColor() const { return !::get_flag( maData.mnFlags, EXC_CHMARKERFORMAT_NOLINE ); }
+ /** Returns true, if fill area of markers is visible. */
+ bool HasFillColor() const { return !::get_flag( maData.mnFlags, EXC_CHMARKERFORMAT_NOFILL ); }
+
+private:
+ /** Registers marker colors in palette and stores color identifiers. */
+ void RegisterColors( const XclExpChRoot& rRoot );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChMarkerFormat maData; /// Contents of the CHMARKERFORMAT record.
+ sal_uInt32 mnLineColorId; /// Border line color identifier.
+ sal_uInt32 mnFillColorId; /// Fill color identifier.
+};
+
+typedef rtl::Reference< XclExpChMarkerFormat > XclExpChMarkerFormatRef;
+
+/** The CHPIEFORMAT record containing data point formatting data for pie segments. */
+class XclExpChPieFormat : public XclExpUInt16Record
+{
+public:
+ explicit XclExpChPieFormat();
+
+ /** Sets pie segment properties from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+};
+
+typedef rtl::Reference< XclExpChPieFormat > XclExpChPieFormatRef;
+
+/** The CH3DDATAFORMAT record containing the bar type in 3D bar charts. */
+class XclExpCh3dDataFormat : public XclExpRecord
+{
+public:
+ explicit XclExpCh3dDataFormat();
+
+ /** Sets 3d bar properties from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclCh3dDataFormat maData; /// Contents of the CH3DDATAFORMAT record.
+};
+
+typedef rtl::Reference< XclExpCh3dDataFormat > XclExpCh3dDataFormatRef;
+
+/** The CHATTACHEDLABEL record that contains the type of a data point label. */
+class XclExpChAttachedLabel : public XclExpUInt16Record
+{
+public:
+ explicit XclExpChAttachedLabel( sal_uInt16 nFlags );
+};
+
+typedef rtl::Reference< XclExpChAttachedLabel > XclExpChAttLabelRef;
+
+/** Represents the CHDATAFORMAT record group containing data point properties.
+
+ The CHDATAFORMAT group consists of: CHDATAFORMAT, CHBEGIN, CHFRAME group,
+ CHMARKERFORMAT, CHPIEFORMAT, CH3DDATAFORMAT, CHSERIESFORMAT,
+ CHATTACHEDLABEL, CHEND.
+ */
+class XclExpChDataFormat : public XclExpChGroupBase, public XclExpChFrameBase
+{
+public:
+ explicit XclExpChDataFormat( const XclExpChRoot& rRoot,
+ const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx );
+
+ /** Converts the passed data series or data point formatting. */
+ void ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo );
+ /** Sets default formatting for a series in a stock chart. */
+ void ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol );
+ /** Converts line formatting for the specified object (e.g. trend lines, error bars). */
+ void ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+
+ /** Returns true, if this objects describes the formatting of an entire series. */
+ bool IsSeriesFormat() const { return maData.maPointPos.mnPointIdx == EXC_CHDATAFORMAT_ALLPOINTS; }
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChDataFormat maData; /// Contents of the CHDATAFORMAT record.
+ XclExpChMarkerFormatRef mxMarkerFmt; /// Data point marker (CHMARKERFORMAT record).
+ XclExpChPieFormatRef mxPieFmt; /// Pie segment format (CHPIEFORMAT record).
+ XclExpRecordRef mxSeriesFmt; /// Series properties (CHSERIESFORMAT record).
+ XclExpCh3dDataFormatRef mx3dDataFmt; /// 3D bar format (CH3DDATAFORMAT record).
+ XclExpChAttLabelRef mxAttLabel; /// Data point label type (CHATTACHEDLABEL record).
+};
+
+typedef rtl::Reference< XclExpChDataFormat > XclExpChDataFormatRef;
+
+/** Represents the CHSERTRENDLINE record containing settings for a trend line. */
+class XclExpChSerTrendLine : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChSerTrendLine( const XclExpChRoot& rRoot );
+
+ /** Converts the passed trend line, returns true if trend line type is supported. */
+ bool Convert( css::uno::Reference< css::chart2::XRegressionCurve > const & xRegCurve,
+ sal_uInt16 nSeriesIdx );
+
+ /** Returns formatting information of the trend line, created in Convert(). */
+ const XclExpChDataFormatRef& GetDataFormat() const { return mxDataFmt; }
+ /** Returns formatting of the equation text box, created in Convert(). */
+ const XclExpChTextRef& GetDataLabel() const { return mxLabel; }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChSerTrendLine maData; /// Contents of the CHSERTRENDLINE record.
+ XclExpChDataFormatRef mxDataFmt; /// Formatting settings of the trend line.
+ XclExpChTextRef mxLabel; /// Formatting of the equation text box.
+};
+
+typedef rtl::Reference< XclExpChSerTrendLine > XclExpChSerTrendLineRef;
+
+/** Represents the CHSERERRORBAR record containing settings for error bars. */
+class XclExpChSerErrorBar : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType );
+
+ /** Converts the passed error bar settings, returns true if error bar type is supported. */
+ bool Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChSerErrorBar maData; /// Contents of the CHSERERRORBAR record.
+};
+
+typedef rtl::Reference< XclExpChSerErrorBar > XclExpChSerErrorBarRef;
+
+/** Represents the CHSERIES record group describing a data series in a chart.
+
+ The CHSERIES group consists of: CHSERIES, CHBEGIN, CHSOURCELINK groups,
+ CHDATAFORMAT groups, CHSERGROUP, CHSERPARENT, CHSERERRORBAR,
+ CHSERTRENDLINE, CHEND.
+ */
+class XclExpChSeries : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx );
+
+ /** Converts the passed data series (source links and formatting). */
+ bool ConvertDataSeries(
+ css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ const XclChExtTypeInfo& rTypeInfo,
+ sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx );
+ /** Converts the passed data series for stock charts. */
+ bool ConvertStockSeries(
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole,
+ sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol );
+ /** Converts the passed error bar settings (called at trend line child series). */
+ bool ConvertTrendLine( const XclExpChSeries& rParent,
+ css::uno::Reference< css::chart2::XRegressionCurve > const & xRegCurve );
+ /** Converts the passed error bar settings (called at error bar child series). */
+ bool ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId );
+ /** Converts and inserts category ranges for all inserted series. */
+ void ConvertCategSequence( css::uno::Reference< css::chart2::data::XLabeledDataSequence > const & xCategSeq );
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Initializes members of this series to represent a child of the passed series. */
+ void InitFromParent( const XclExpChSeries& rParent );
+ /** Tries to create trend line series objects (called at parent series). */
+ void CreateTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries );
+ /** Tries to create positive and negative error bar series objects (called at parent series). */
+ void CreateErrorBars( const ScfPropertySet& rPropSet,
+ const OUString& rBarPropName,
+ sal_uInt8 nPosBarId, sal_uInt8 nNegBarId );
+ /** Tries to create an error bar series object (called at parent series). */
+ void CreateErrorBar( const ScfPropertySet& rPropSet,
+ const OUString& rShowPropName, sal_uInt8 nBarId );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+
+private:
+ XclChSeries maData; /// Contents of the CHSERIES record.
+ XclExpChSourceLinkRef mxTitleLink; /// Link data for series title.
+ XclExpChSourceLinkRef mxValueLink; /// Link data for series values.
+ XclExpChSourceLinkRef mxCategLink; /// Link data for series category names.
+ XclExpChSourceLinkRef mxBubbleLink; /// Link data for series bubble sizes.
+ XclExpChDataFormatRef mxSeriesFmt; /// CHDATAFORMAT group for series format.
+ XclExpRecordList< XclExpChDataFormat >
+ maPointFmts; /// CHDATAFORMAT groups for data point formats.
+ XclExpChSerTrendLineRef mxTrendLine; /// Trend line settings (CHSERTRENDLINE record).
+ XclExpChSerErrorBarRef mxErrorBar; /// Error bar settings (CHSERERRORBAR record).
+ sal_uInt16 mnGroupIdx; /// Chart type group (CHTYPEGROUP group) this series is assigned to.
+ sal_uInt16 mnSeriesIdx; /// 0-based series index.
+ sal_uInt16 mnParentIdx; /// 0-based index of parent series (trend lines and error bars).
+};
+
+typedef rtl::Reference< XclExpChSeries > XclExpChSeriesRef;
+
+// Chart type groups ==========================================================
+
+/** Represents the chart type record for all supported chart types. */
+class XclExpChType : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChType( const XclExpChRoot& rRoot );
+
+ /** Converts the passed chart type and the contained data series. */
+ void Convert( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels );
+ /** Sets stacking mode (standard or percent) for the series in this chart type group. */
+ void SetStacked( bool bPercent );
+
+ /** Returns true, if this is object represents a valid chart type. */
+ bool IsValidType() const { return maTypeInfo.meTypeId != EXC_CHTYPEID_UNKNOWN; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChType maData; /// Contents of the chart type record.
+ XclChTypeInfo maTypeInfo; /// Chart type info for the contained type.
+};
+
+/** Represents the CHCHART3D record that contains 3D view settings. */
+class XclExpChChart3d : public XclExpRecord
+{
+public:
+ explicit XclExpChChart3d();
+
+ /** Converts 3d settings for the passed chart type. */
+ void Convert( const ScfPropertySet& rPropSet, bool b3dWallChart );
+ /** Sets flag that the data points are clustered on the X axis. */
+ void SetClustered() { ::set_flag( maData.mnFlags, EXC_CHCHART3D_CLUSTER ); }
+
+ /** Returns true, if the data points are clustered on the X axis. */
+ bool IsClustered() const { return ::get_flag( maData.mnFlags, EXC_CHCHART3D_CLUSTER ); }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChChart3d maData; /// Contents of the CHCHART3D record.
+};
+
+typedef rtl::Reference< XclExpChChart3d > XclExpChChart3dRef;
+
+/** Represents the CHLEGEND record group describing the chart legend.
+
+ The CHLEGEND group consists of: CHLEGEND, CHBEGIN, CHFRAMEPOS, CHFRAME
+ group, CHTEXT group, CHEND.
+ */
+class XclExpChLegend : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChLegend( const XclExpChRoot& rRoot );
+
+ /** Converts all legend settings from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChLegend maData; /// Contents of the CHLEGEND record.
+ XclExpChFramePosRef mxFramePos; /// Legend frame position (CHFRAMEPOS record).
+ XclExpChTextRef mxText; /// Legend text format (CHTEXT group).
+ XclExpChFrameRef mxFrame; /// Legend frame format (CHFRAME group).
+};
+
+typedef rtl::Reference< XclExpChLegend > XclExpChLegendRef;
+
+/** Represents the CHDROPBAR record group describing pos/neg bars in line charts.
+
+ The CHDROPBAR group consists of: CHDROPBAR, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclExpChDropBar : public XclExpChGroupBase, public XclExpChFrameBase
+{
+public:
+ explicit XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType );
+
+ /** Converts and writes the contained frame data to the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChObjectType meObjType; /// Type of the dropbar.
+};
+
+typedef rtl::Reference< XclExpChDropBar > XclExpChDropBarRef;
+
+/** Represents the CHTYPEGROUP record group describing a group of series.
+
+ The CHTYPEGROUP group consists of: CHTYPEGROUP, CHBEGIN, a chart type
+ record (e.g. CHBAR, CHLINE, CHAREA, CHPIE, ...), CHCHART3D, CHLEGEND group,
+ CHDROPBAR groups, CHCHARTLINE groups (CHCHARTLINE with CHLINEFORMAT),
+ CHDATAFORMAT group, CHEND.
+ */
+class XclExpChTypeGroup : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx );
+
+ /** Converts the passed chart type to Excel type settings. */
+ void ConvertType( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels );
+ /** Converts and inserts all series from the passed chart type. */
+ void ConvertSeries( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectorLines );
+ /** Converts and inserts category ranges for all inserted series. */
+ void ConvertCategSequence( css::uno::Reference< css::chart2::data::XLabeledDataSequence > const & xCategSeq );
+ /** Creates a legend object and converts all legend settings. */
+ void ConvertLegend( const ScfPropertySet& rPropSet );
+
+ /** Returns true, if this chart type group contains at least one valid series. */
+ bool IsValidGroup() const { return !maSeries.IsEmpty() && maType.IsValidType(); }
+ /** Returns the index of this chart type group format. */
+ sal_uInt16 GetGroupIdx() const { return maData.mnGroupIdx; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChExtTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+ /** Returns true, if the chart is three-dimensional. */
+ bool Is3dChart() const { return maTypeInfo.mb3dChart; }
+ /** Returns true, if chart type supports wall and floor format. */
+ bool Is3dWallChart() const { return Is3dChart() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE); }
+ /** Returns true, if the series in this chart type group are ordered on the Z axis. */
+ bool Is3dDeepChart() const { return Is3dWallChart() && mxChart3d && !mxChart3d->IsClustered(); }
+ /** Returns true, if this chart type can be combined with other types. */
+ bool IsCombinable2d() const { return !Is3dChart() && maTypeInfo.mbCombinable2d; }
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns an unused format index to be used for the next created series. */
+ sal_uInt16 GetFreeFormatIdx() const;
+ /** Creates all data series of any chart type except stock charts. */
+ void CreateDataSeries( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries );
+ /** Creates all data series of a stock chart. */
+ void CreateAllStockSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries );
+ /** Creates a single data series of a stock chart. */
+ bool CreateStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, bool bCloseSymbol );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpChSeries > XclExpChSeriesList;
+
+ XclChTypeGroup maData; /// Contents of the CHTYPEGROUP record.
+ XclExpChType maType; /// Chart type (e.g. CHBAR, CHLINE, ...).
+ XclChExtTypeInfo maTypeInfo; /// Extended chart type info.
+ XclExpChSeriesList maSeries; /// List of series data (CHSERIES groups).
+ XclExpChChart3dRef mxChart3d; /// 3D settings (CHCHART3D record).
+ XclExpChLegendRef mxLegend; /// Chart legend (CHLEGEND group).
+ XclExpChDropBarRef mxUpBar; /// White dropbars (CHDROPBAR group).
+ XclExpChDropBarRef mxDownBar; /// Black dropbars (CHDROPBAR group).
+ std::map<sal_uInt16, std::unique_ptr<XclExpChLineFormat>>
+ m_ChartLines; /// Global line formats (CHCHARTLINE group).
+};
+
+typedef rtl::Reference< XclExpChTypeGroup > XclExpChTypeGroupRef;
+
+// Axes =======================================================================
+
+class XclExpChLabelRange : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChLabelRange( const XclExpChRoot& rRoot );
+
+ /** Converts category axis scaling settings. */
+ void Convert( const css::chart2::ScaleData& rScaleData,
+ const ScfPropertySet& rChart1Axis, bool bMirrorOrient );
+ /** Converts position settings of a crossing axis at this axis. */
+ void ConvertAxisPosition( const ScfPropertySet& rPropSet );
+ /** Sets flag for tickmark position between categories or on categories. */
+ void SetTicksBetweenCateg( bool bTicksBetween )
+ { ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_BETWEEN, bTicksBetween ); }
+
+private:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChLabelRange maLabelData; /// Contents of the CHLABELRANGE record.
+ XclChDateRange maDateData; /// Contents of the CHDATERANGE record.
+};
+
+typedef rtl::Reference< XclExpChLabelRange > XclExpChLabelRangeRef;
+
+class XclExpChValueRange : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChValueRange( const XclExpChRoot& rRoot );
+
+ /** Converts value axis scaling settings. */
+ void Convert( const css::chart2::ScaleData& rScaleData );
+ /** Converts position settings of a crossing axis at this axis. */
+ void ConvertAxisPosition( const ScfPropertySet& rPropSet );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChValueRange maData; /// Contents of the CHVALUERANGE record.
+};
+
+typedef rtl::Reference< XclExpChValueRange > XclExpChValueRangeRef;
+
+class XclExpChTick : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChTick( const XclExpChRoot& rRoot );
+
+ /** Converts axis tick mark settings. */
+ void Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType );
+ /** Sets font color and color identifier to internal data structures. */
+ void SetFontColor(model::ComplexColor const& rComplexColor, sal_uInt32 nColorId);
+
+ /** Sets text rotation to internal data structures. */
+ void SetRotation( sal_uInt16 nRotation );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChTick maData; /// Contents of the CHTICK record.
+ sal_uInt32 mnTextColorId; /// Axis labels text color identifier.
+};
+
+typedef rtl::Reference< XclExpChTick > XclExpChTickRef;
+
+/** Represents the CHAXIS record group describing an entire chart axis.
+
+ The CHAXIS group consists of: CHAXIS, CHBEGIN, CHLABELRANGE, CHEXTRANGE,
+ CHVALUERANGE, CHFORMAT, CHTICK, CHFONT, CHAXISLINE groups (CHAXISLINE with
+ CHLINEFORMAT, CHAREAFORMAT, and CHESCHERFORMAT group), CHEND.
+ */
+class XclExpChAxis : public XclExpChGroupBase, public XclExpChFontBase
+{
+public:
+ explicit XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType );
+
+ /** Sets font color and color identifier to internal data structures. */
+ virtual void SetFont( XclExpChFontRef xFont, model::ComplexColor const& rComplexColor, sal_uInt32 nColorId ) override;
+ /** Sets text rotation to internal data structures. */
+ virtual void SetRotation( sal_uInt16 nRotation ) override;
+
+ /** Converts formatting and scaling settings from the passed axis. */
+ void Convert( css::uno::Reference< css::chart2::XAxis > const & xAxis,
+ css::uno::Reference< css::chart2::XAxis > const & xCrossingAxis,
+ css::uno::Reference< css::chart::XAxis > const & xChart1Axis,
+ const XclChExtTypeInfo& rTypeInfo );
+ /** Converts and writes 3D wall/floor properties from the passed diagram. */
+ void ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram );
+
+ /** Returns the type of this axis. */
+ sal_uInt16 GetAxisType() const { return maData.mnType; }
+ /** Returns the axis dimension index used by the chart API. */
+ sal_Int32 GetApiAxisDimension() const { return maData.GetApiAxisDimension(); }
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChAxis maData; /// Contents of the CHAXIS record.
+ XclExpChLabelRangeRef mxLabelRange; /// Category scaling (CHLABELRANGE record).
+ XclExpChValueRangeRef mxValueRange; /// Value scaling (CHVALUERANGE record).
+ XclExpChTickRef mxTick; /// Axis ticks (CHTICK record).
+ XclExpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclExpChLineFormatRef mxAxisLine; /// Axis line format (CHLINEFORMAT record).
+ XclExpChLineFormatRef mxMajorGrid; /// Major grid line format (CHLINEFORMAT record).
+ XclExpChLineFormatRef mxMinorGrid; /// Minor grid line format (CHLINEFORMAT record).
+ XclExpChFrameRef mxWallFrame; /// Wall/floor format (sub records of CHFRAME group).
+ sal_uInt16 mnNumFmtIdx; /// Index into number format buffer (CHFORMAT record).
+};
+
+typedef rtl::Reference< XclExpChAxis > XclExpChAxisRef;
+
+/** Represents the CHAXESSET record group describing an axes set (X/Y/Z axes).
+
+ The CHAXESSET group consists of: CHAXESSET, CHBEGIN, CHFRAMEPOS, CHAXIS
+ groups, CHTEXT groups, CHPLOTFRAME group (CHPLOTFRAME with CHFRAME group),
+ CHTYPEGROUP group, CHEND.
+ */
+class XclExpChAxesSet : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId );
+
+ /** Converts the passed diagram to chart record data.
+ @return First unused chart type group index. */
+ sal_uInt16 Convert( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ sal_uInt16 nFirstGroupIdx );
+
+ /** Returns true, if this axes set exists (returns false if this is a dummy object). */
+ bool IsValidAxesSet() const { return !maTypeGroups.IsEmpty(); }
+ /** Returns the index of the axes set (primary/secondary). */
+ sal_uInt16 GetAxesSetId() const { return maData.mnAxesSetId; }
+ /** Returns the axes set index used by the chart API. */
+ sal_Int32 GetApiAxesSetIndex() const { return maData.GetApiAxesSetIndex(); }
+ /** Returns true, if the chart is three-dimensional. */
+ bool Is3dChart() const;
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns first inserted chart type group. */
+ XclExpChTypeGroupRef GetFirstTypeGroup() const;
+ /** Returns last inserted chart type group. */
+ XclExpChTypeGroupRef GetLastTypeGroup() const;
+
+ /** Converts a complete axis object including axis title. */
+ void ConvertAxis( XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
+ XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
+ css::uno::Reference< css::chart2::XCoordinateSystem > const & xCoordSystem,
+ const XclChExtTypeInfo& rTypeInfo,
+ sal_Int32 nCrossingAxisDim );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChAxesSet maData; /// Contents of the CHAXESSET record.
+ XclExpChFramePosRef mxFramePos; /// Outer plot area position (CHFRAMEPOS record).
+ XclExpChAxisRef mxXAxis; /// The X axis (CHAXIS group).
+ XclExpChAxisRef mxYAxis; /// The Y axis (CHAXIS group).
+ XclExpChAxisRef mxZAxis; /// The Z axis (CHAXIS group).
+ XclExpChTextRef mxXAxisTitle; /// The X axis title (CHTEXT group).
+ XclExpChTextRef mxYAxisTitle; /// The Y axis title (CHTEXT group).
+ XclExpChTextRef mxZAxisTitle; /// The Z axis title (CHTEXT group).
+ XclExpChFrameRef mxPlotFrame; /// Plot area (CHPLOTFRAME group).
+ XclExpRecordList< XclExpChTypeGroup >
+ maTypeGroups; /// Chart type groups (CHTYPEGROUP group).
+};
+
+typedef std::shared_ptr< XclExpChAxesSet > XclExpChAxesSetRef;
+
+// The chart object ===========================================================
+
+/** Represents the CHCHART record group describing the chart contents.
+
+ The CHCHART group consists of: CHCHART, CHBEGIN, SCL, CHPLOTGROWTH, CHFRAME
+ group, CHSERIES groups, CHPROPERTIES, CHDEFAULTTEXT groups (CHDEFAULTTEXT
+ with CHTEXT groups), CHUSEDAXESSETS, CHAXESSET groups, CHTEXT groups, CHEND.
+ */
+class XclExpChChart : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChChart( const XclExpRoot& rRoot,
+ css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc,
+ const tools::Rectangle& rChartRect );
+
+ /** Creates, registers and returns a new data series object. */
+ XclExpChSeriesRef CreateSeries();
+ /** Removes the last created data series object from the series list. */
+ void RemoveLastSeries();
+ /** Stores a CHTEXT group that describes a data point label. */
+ void SetDataLabel( XclExpChTextRef const & xText );
+ /** Sets the plot area position and size to manual mode. */
+ void SetManualPlotArea();
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpChSeries > XclExpChSeriesList;
+
+ XclChRectangle maRect; /// Position of the chart on the sheet (CHCHART record).
+ XclExpChSeriesList maSeries; /// List of series data (CHSERIES groups).
+ XclExpChFrameRef mxFrame; /// Chart frame format (CHFRAME group).
+ XclChProperties maProps; /// Chart properties (CHPROPERTIES record).
+ XclExpChAxesSetRef mxPrimAxesSet; /// Primary axes set (CHAXESSET group).
+ XclExpChAxesSetRef mxSecnAxesSet; /// Secondary axes set (CHAXESSET group).
+ XclExpChTextRef mxTitle; /// Chart title (CHTEXT group).
+ XclExpRecordList< XclExpChText >
+ maLabels; /// Data point labels (CHTEXT groups).
+};
+
+/** Represents the group of DFF and OBJ records containing all drawing shapes
+ embedded in the chart object.
+ */
+class XclExpChartDrawing : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpChartDrawing(
+ const XclExpRoot& rRoot,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const Size& rChartSize );
+ virtual ~XclExpChartDrawing() override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ std::shared_ptr< XclExpObjectManager > mxObjMgr;
+ rtl::Reference< XclExpRecordBase > mxObjRecs;
+};
+
+/** Represents the entire chart substream (all records in BOF/EOF block). */
+class XclExpChart : public XclExpSubStream, protected XclExpRoot
+{
+public:
+ explicit XclExpChart( const XclExpRoot& rRoot,
+ css::uno::Reference< css::frame::XModel > const & xModel,
+ const tools::Rectangle& rChartRect );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xecontent.hxx b/sc/source/filter/inc/xecontent.hxx
new file mode 100644
index 0000000000..07318a037c
--- /dev/null
+++ b/sc/source/filter/inc/xecontent.hxx
@@ -0,0 +1,416 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rangelst.hxx>
+#include "xladdress.hxx"
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+#include "xestring.hxx"
+#include "xeextlst.hxx"
+#include "xlformula.hxx"
+
+#include <colorscale.hxx>
+
+/* ============================================================================
+Classes to export the big Excel document contents (related to several cells or
+globals for the sheet or document).
+- Shared string table
+- Merged cells
+- Hyperlinks
+- Label ranges
+- Conditional formatting
+- Data validation
+- Web Queries
+============================================================================ */
+
+// Shared string table ========================================================
+
+class XclExpSstImpl;
+
+/** Provides export of the SST (shared string table) record.
+ @descr Contains all strings in the document and writes the SST. */
+class XclExpSst : public XclExpRecordBase
+{
+public:
+ explicit XclExpSst();
+ virtual ~XclExpSst() override;
+
+ /** Inserts a new string into the table.
+ @return The index of the string in the SST, used in other records. */
+ sal_uInt32 Insert( const XclExpStringRef& xString );
+
+ /** Writes the complete SST and EXTSST records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef std::unique_ptr< XclExpSstImpl > XclExpSstImplPtr;
+ XclExpSstImplPtr mxImpl;
+};
+
+// Merged cells ===============================================================
+
+/** Represents a MERGEDCELLS record containing all merged cell ranges in a sheet. */
+class XclExpMergedcells : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpMergedcells( const XclExpRoot& rRoot );
+
+ /** Appends a new range to the list of merged cell ranges. */
+ void AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId );
+ /** Returns the XF identifier of the top-left cell in a merged range. */
+ sal_uInt32 GetBaseXFId( const ScAddress& rPos ) const;
+
+ /** Writes the record, if it contains at least one merged cell range. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScRangeList maMergedRanges; /// All merged cell ranges of the sheet.
+ ScfUInt32Vec maBaseXFIds; /// The XF identifiers of the top-left cells.
+};
+
+// Hyperlinks =================================================================
+
+class SvxURLField;
+
+/** Provides export of hyperlink data. */
+class XclExpHyperlink : public XclExpRecord
+{
+public:
+ /** Constructs the HLINK record from a URL text field. */
+ explicit XclExpHyperlink( const XclExpRoot& rRoot,
+ const SvxURLField& rUrlField, const ScAddress& rScPos );
+ virtual ~XclExpHyperlink() override;
+
+ /** Returns the cell representation text or 0, if not available. */
+ const OUString* GetRepr() const { return m_Repr.isEmpty() ? nullptr : &m_Repr; }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void WriteEmbeddedData( XclExpStream& rStrm );
+
+ /** Builds file name from the passed file URL. Tries to convert to relative file name.
+ @param rnLevel (out-param) The parent directory level.
+ @param rbRel (out-param) true = path is relative.
+ @param bEncoded if true return an IURI encoded name, not a DOS name. */
+ static OUString BuildFileName(
+ sal_uInt16& rnLevel, bool& rbRel,
+ const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded );
+private:
+
+ /** Writes the body of the HLINK record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::unique_ptr< SvStream > SvStreamPtr;
+
+ ScAddress maScPos; /// Position of the hyperlink.
+ OUString m_Repr; /// Cell representation text.
+ SvStreamPtr mxVarData; /// Buffer stream with variable data.
+ sal_uInt32 mnFlags; /// Option flags.
+ XclExpStringRef mxTextMark; /// Location within m_Repr
+ OUString msTarget; /// Target URL
+};
+
+typedef XclExpRecordList< XclExpHyperlink > XclExpHyperlinkList;
+
+// Label ranges ===============================================================
+
+/** Provides export of the row/column label range list of a sheet. */
+class XclExpLabelranges : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Fills the cell range lists with all ranges of the current sheet. */
+ explicit XclExpLabelranges( const XclExpRoot& rRoot );
+
+ /** Writes the LABELRANGES record if it contains at least one range. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Fills the specified range list with all label headers of the current sheet.
+ @param rRanges The cell range list to fill.
+ @param xLabelRangesRef The core range list with all ranges.
+ @param nScTab The current Calc sheet index. */
+ static void FillRangeList( ScRangeList& rScRanges,
+ const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab );
+
+private:
+ ScRangeList maRowRanges; /// Cell range list for row labels.
+ ScRangeList maColRanges; /// Cell range list for column labels.
+};
+
+// Conditional formatting =====================================================
+
+class XclExpCFImpl;
+
+/** Represents a CF record that contains one condition of a conditional format. */
+class XclExpCF : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
+ virtual ~XclExpCF() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the CF record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::unique_ptr< XclExpCFImpl > XclExpCFImplPtr;
+ XclExpCFImplPtr mxImpl;
+};
+
+class XclExpDateFormat : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority );
+ virtual ~XclExpDateFormat() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ const ScCondDateFormatEntry& mrFormatEntry;
+ sal_Int32 mnPriority;
+};
+
+class XclExpCfvo : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rFormatEntry, const ScAddress& rPos, bool bFirst = true);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ const ScColorScaleEntry& mrEntry;
+ ScAddress maSrcPos;
+ bool mbFirst;
+};
+
+class XclExpColScaleCol : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor);
+ virtual ~XclExpColScaleCol() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ const Color& mrColor;
+};
+
+/** Represents a CONDFMT record that contains all conditions of a conditional format.
+ @descr Contains the conditions which are stored in CF records. */
+class XclExpCondfmt : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex );
+ virtual ~XclExpCondfmt() override;
+
+ /** Returns true, if this conditional format contains at least one cell range and CF record. */
+ bool IsValidForBinary() const;
+ bool IsValidForXml() const;
+
+ /** Writes the CONDFMT record with following CF records, if there is valid data. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the CONDFMT record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpRecordList< XclExpRecord >
+ maCFList; /// List of CF records.
+ XclRangeList maXclRanges; /// Cell ranges for this conditional format.
+ OUString msSeqRef; /// OOXML Sequence of References
+};
+
+class XclExpColorScale: public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ typedef XclExpRecordList< XclExpCfvo > XclExpCfvoList;
+ typedef XclExpRecordList< XclExpColScaleCol > XclExpColScaleColList;
+
+ XclExpCfvoList maCfvoList;
+ XclExpColScaleColList maColList;
+ sal_Int32 mnPriority;
+};
+
+class XclExpDataBar : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, OString aGUID);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ std::unique_ptr<XclExpCfvo> mpCfvoLowerLimit;
+ std::unique_ptr<XclExpCfvo> mpCfvoUpperLimit;
+ std::unique_ptr<XclExpColScaleCol> mpCol;
+
+ const ScDataBarFormat& mrFormat;
+ sal_Int32 mnPriority;
+ OString maGUID;
+};
+
+class XclExpIconSet : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ typedef XclExpRecordList< XclExpCfvo > XclExpCfvoList;
+
+ XclExpCfvoList maCfvoList;
+ const ScIconSetFormat& mrFormat;
+ sal_Int32 mnPriority;
+};
+
+/** Contains all conditional formats of a specific sheet. */
+class XclExpCondFormatBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Constructs CONDFMT and CF records containing the conditional formats of the current sheet. */
+ explicit XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst );
+
+ /** Writes all contained CONDFMT records with their CF records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpCondfmt > XclExpCondfmtList;
+ XclExpCondfmtList maCondfmtList; /// List of CONDFMT records.
+};
+
+// Data Validation ============================================================
+
+/** Provides export of the data of a DV record.
+ @descr This record contains the settings for a data validation. In detail
+ this is a pointer to the core validation data and a cell range list with all
+ affected cells. The handle index is used to optimize list search algorithm. */
+class XclExpDV : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDV( const XclExpRoot& rRoot, sal_uInt32 nScHandle );
+ virtual ~XclExpDV() override;
+
+ /** Returns the core handle of the validation data. */
+ sal_uInt32 GetScHandle() const { return mnScHandle; }
+
+ /** Inserts a new cell range into the cell range list. */
+ void InsertCellRange( const ScRange& rPos );
+ /** Converts the Calc range list to the Excel range list.
+ @return false = Resulting range list empty - do not write this record. */
+ bool Finalize();
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the DV record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ ScRangeList maScRanges; /// Calc range list with all affected cells.
+ XclRangeList maXclRanges; /// Excel range list with all affected cells.
+ XclExpString maPromptTitle; /// The prompt title.
+ XclExpString maPromptText; /// The prompt text.
+ XclExpString maErrorTitle; /// The error title.
+ XclExpString maErrorText; /// The error text.
+ XclExpStringRef mxString1; /// String for first condition formula.
+ XclTokenArrayRef mxTokArr1; /// Formula for first condition.
+ OUString msFormula1; /// OOXML Formula for first condition.
+ OUString msList; /// x12ac:list for first condition.
+ XclTokenArrayRef mxTokArr2; /// Formula for second condition.
+ OUString msFormula2; /// OOXML Formula for second condition.
+ sal_uInt32 mnFlags; /// Miscellaneous flags.
+ sal_uInt32 mnScHandle; /// The core handle for quick list search.
+};
+
+/** This class contains the DV record list following the DVAL record. */
+class XclExpDval : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDval( const XclExpRoot& rRoot );
+ virtual ~XclExpDval() override;
+
+ /** Inserts the cell range into the range list of the DV record with the specified handle. */
+ void InsertCellRange( const ScRange& rRange, sal_uInt32 nScHandle );
+
+ /** Writes the DVAL record and the DV record list. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Searches for or creates a XclExpDV record object with the specified handle. */
+ XclExpDV& SearchOrCreateDv( sal_uInt32 nScHandle );
+
+ /** Writes the body of the DVAL record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpDV > XclExpDVList;
+ typedef XclExpDVList::RecordRefType XclExpDVRef;
+
+ XclExpDVList maDVList; /// List of DV records
+ XclExpDVRef mxLastFoundDV; /// For search optimization.
+};
+
+// Web Queries ================================================================
+
+/** Contains all records for a web query (linked tables in an HTML document).
+ @descr mode 1 (entire document): mpQryTables==0, mbEntireDoc==true;
+ mode 2 (all tables): mpQryTables==0, mbEntireDoc==false;
+ mode 3 (custom range list): mpQryTables!=0, mbEntireDoc==false. */
+class XclExpWebQuery : public XclExpRecordBase
+{
+public:
+ /** Constructs a web query record container with settings from Calc. */
+ explicit XclExpWebQuery(
+ const OUString& rRangeName,
+ const OUString& rUrl,
+ std::u16string_view rSource,
+ sal_Int32 nRefrSecs );
+ virtual ~XclExpWebQuery() override;
+
+ /** Writes all needed records for this web query. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maDestRange; /// Destination range.
+ XclExpString maUrl; /// Source document URL.
+ XclExpStringRef mxQryTables; /// List of source range names.
+ sal_Int16 mnRefresh; /// Refresh time in minutes.
+ bool mbEntireDoc; /// true = entire document.
+};
+
+/** Contains all web query records for this document. */
+class XclExpWebQueryBuffer : public XclExpRecordList< XclExpWebQuery >
+{
+public:
+ explicit XclExpWebQueryBuffer( const XclExpRoot& rRoot );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xedbdata.hxx b/sc/source/filter/inc/xedbdata.hxx
new file mode 100644
index 0000000000..a7fddfe04e
--- /dev/null
+++ b/sc/source/filter/inc/xedbdata.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xeroot.hxx"
+#include "xerecord.hxx"
+
+class ScDBData;
+
+class XclExpTables : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpTables( const XclExpRoot& rRoot );
+ virtual ~XclExpTables() override;
+
+ void AppendTable( const ScDBData* pData, sal_Int32 nTableId );
+
+protected:
+ struct Entry
+ {
+ const ScDBData* mpData;
+ sal_Int32 mnTableId; /// used as [n] in table[n].xml part name.
+
+ Entry( const ScDBData* pData, sal_Int32 nTableId );
+ };
+
+ typedef ::std::vector<Entry> TablesType;
+ TablesType maTables;
+
+ static void SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry );
+};
+
+/** Stores all data for database ranges (tables in Excel speak).
+ Only OOXML export, BIFF not implemented.*/
+class XclExpTablesManager : protected XclExpRoot
+{
+public:
+ explicit XclExpTablesManager( const XclExpRoot& rRoot );
+ virtual ~XclExpTablesManager() override;
+
+ void Initialize();
+ rtl::Reference< XclExpTables > GetTablesBySheet( SCTAB nTab );
+
+private:
+ typedef ::std::map< SCTAB, rtl::Reference< XclExpTables > > TablesMapType;
+ TablesMapType maTablesMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeescher.hxx b/sc/source/filter/inc/xeescher.hxx
new file mode 100644
index 0000000000..c7adf1050b
--- /dev/null
+++ b/sc/source/filter/inc/xeescher.hxx
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/graph.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include "xcl97rec.hxx"
+#include "xlescher.hxx"
+#include "xlformula.hxx"
+#include <svx/sdtaitm.hxx>
+#include <rtl/ustring.hxx>
+#include <unotools/tempfile.hxx>
+#include <memory>
+#include <optional>
+
+class ScPostIt;
+
+namespace com::sun::star::chart { class XChartDocument; }
+namespace com::sun::star::script { struct ScriptEventDescriptor; }
+
+
+// DFF client anchor ==========================================================
+
+/** Base class for DFF client anchor atoms used in spreadsheets. */
+class XclExpDffAnchorBase : public EscherExClientAnchor_Base, protected XclExpRoot
+{
+public:
+ /** Constructs a dummy client anchor. */
+ explicit XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags = 0 );
+
+ /** Sets the flags according to the passed SdrObject. */
+ void SetFlags( const SdrObject& rSdrObj );
+ /** Sets the anchor position and flags according to the passed SdrObject. */
+ void SetSdrObject( const SdrObject& rSdrObj );
+
+ /** Writes the DFF client anchor structure with the current anchor position. */
+ void WriteDffData( EscherEx& rEscherEx ) const;
+
+ /** Called from SVX DFF converter.
+ @param rRect The object anchor rectangle to be exported (in twips). */
+ virtual void WriteData( EscherEx& rEscherEx, const tools::Rectangle& rRect ) override;
+
+private:
+ virtual void ImplSetFlags( const SdrObject& rSdrObj );
+ virtual void ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit );
+
+protected: // for access in derived classes
+ XclObjAnchor maAnchor; /// The client anchor data.
+ sal_uInt16 mnFlags; /// Flags for DFF stream export.
+};
+
+/** Represents the position (anchor) of an object in a Calc sheet. */
+class XclExpDffSheetAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffSheetAnchor( const XclExpRoot& rRoot );
+
+private:
+ virtual void ImplSetFlags( const SdrObject& rSdrObj ) override;
+ virtual void ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit ) override;
+
+private:
+ SCTAB mnScTab; /// Calc sheet index.
+};
+
+/** Represents the position (anchor) of a shape in an embedded draw page. */
+class XclExpDffEmbeddedAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot,
+ const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY );
+
+private:
+ virtual void ImplSetFlags( const SdrObject& rSdrObj ) override;
+ virtual void ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit ) override;
+
+private:
+ Size maPageSize;
+ sal_Int32 mnScaleX;
+ sal_Int32 mnScaleY;
+};
+
+/** Represents the position (anchor) of a note object. */
+class XclExpDffNoteAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffNoteAnchor( const XclExpRoot& rRoot, const tools::Rectangle& rRect );
+};
+
+/** Represents the position (anchor) of a cell dropdown object. */
+class XclExpDffDropDownAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos );
+};
+
+// MSODRAWING* records ========================================================
+
+/** Base class for records holding DFF stream fragments. */
+class XclExpMsoDrawingBase : public XclExpRecord
+{
+public:
+ explicit XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+protected:
+ XclEscherEx& mrEscherEx; /// Reference to the DFF converter containing the DFF stream.
+ sal_uInt32 mnFragmentKey; /// The key of the DFF stream fragment to be written by this record.
+};
+
+/** The MSODRAWINGGROUP record contains the DGGCONTAINER with global DFF data
+ such as the picture container.
+ */
+class XclExpMsoDrawingGroup : public XclExpMsoDrawingBase
+{
+public:
+ explicit XclExpMsoDrawingGroup( XclEscherEx& rEscherEx );
+};
+
+/** One or more MSODRAWING records contain the DFF stream data for a drawing
+ shape.
+ */
+class XclExpMsoDrawing : public XclExpMsoDrawingBase
+{
+public:
+ explicit XclExpMsoDrawing( XclEscherEx& rEscherEx );
+};
+
+/** Provides export of bitmap data to an IMGDATA record. */
+class XclExpImgData : public XclExpRecordBase
+{
+public:
+ explicit XclExpImgData( Graphic aGraphic, sal_uInt16 nRecId );
+
+ /** Writes the BITMAP record. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ Graphic maGraphic; /// The VCL graphic.
+ sal_uInt16 mnRecId; /// Record identifier for the IMGDATA record.
+};
+
+/** Helper class for form controls to manage spreadsheet links . */
+class XclExpControlHelper : protected XclExpRoot
+{
+public:
+ explicit XclExpControlHelper( const XclExpRoot& rRoot );
+ virtual ~XclExpControlHelper() override;
+
+protected:
+ /** Tries to get spreadsheet cell link and source range link from the passed shape. */
+ void ConvertSheetLinks(
+ css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ /** Returns the Excel token array of the cell link, or 0, if no link present. */
+ const XclTokenArray* GetCellLinkTokArr() const { return mxCellLink.get(); }
+ /** Returns the Excel token array of the source range, or 0, if no link present. */
+ const XclTokenArray* GetSourceRangeTokArr() const { return mxSrcRange.get(); }
+ /** Returns the number of entries in the source range, or 0, if no source set. */
+ sal_uInt16 GetSourceEntryCount() const { return mnEntryCount; }
+
+ /** Writes a formula with special style only valid in OBJ records. */
+ static void WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr );
+ /** Writes a formula subrecord with special style only valid in OBJ records. */
+ static void WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr );
+
+private:
+ XclTokenArrayRef mxCellLink; /// Formula for linked cell.
+ XclTokenArrayRef mxSrcRange; /// Formula for source data range.
+ sal_uInt16 mnEntryCount; /// Number of entries in source range.
+protected:
+ ScAddress mxCellLinkAddress;
+};
+
+class XclMacroHelper : public XclExpControlHelper
+{
+ XclTokenArrayRef mxMacroLink; /// Token array containing a link to an attached macro.
+ OUString maMacroName;
+
+public:
+ explicit XclMacroHelper( const XclExpRoot& rRoot );
+ virtual ~XclMacroHelper() override;
+ /** Writes an ftMacro subrecord containing a macro link, or nothing, if no macro present. */
+ void WriteMacroSubRec( XclExpStream& rStrm );
+ /** Sets the name of a macro for object of passed type
+ @return true = The passed event descriptor was valid, macro name has been found. */
+ bool SetMacroLink( const css::script::ScriptEventDescriptor& rEvent, const XclTbxEventType& nEventType );
+
+ /** Sets the name of a macro
+ @return true = The passed macro name has been found. */
+ bool SetMacroLink( const OUString& rMacro );
+ const OUString& GetMacroName() const;
+};
+
+class XclExpShapeObj : public XclObjAny, public XclMacroHelper
+{
+public:
+ explicit XclExpShapeObj( XclExpObjectManager& rRoot, css::uno::Reference< css::drawing::XShape > const & xShape, ScDocument* pDoc );
+ virtual ~XclExpShapeObj() override;
+private:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+};
+
+//delete for exporting OCX
+//#if EXC_EXP_OCX_CTRL
+
+/** Represents an OBJ record for an OCX form control. */
+class XclExpOcxControlObj : public XclObj, public XclExpControlHelper
+{
+public:
+ explicit XclExpOcxControlObj(
+ XclExpObjectManager& rObjMgr,
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor,
+ OUString aClassName,
+ sal_uInt32 nStrmStart, sal_uInt32 nStrmSize );
+
+private:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+private:
+ OUString maClassName; /// Class name of the control.
+ sal_uInt32 mnStrmStart; /// Start position in 'Ctls' stream.
+ sal_uInt32 mnStrmSize; /// Size in 'Ctls' stream.
+};
+
+//#else
+
+/** Represents an OBJ record for a TBX form control. */
+class XclExpTbxControlObj : public XclObj, public XclMacroHelper
+{
+public:
+ explicit XclExpTbxControlObj(
+ XclExpObjectManager& rObjMgr,
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor );
+
+ /** Sets the name of a macro attached to this control.
+ @return true = The passed event descriptor was valid, macro name has been found. */
+ bool SetMacroLink( const css::script::ScriptEventDescriptor& rEvent );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void SaveVml(XclExpXmlStream& rStrm);
+
+ OUString SaveControlPropertiesXml(XclExpXmlStream& rStrm) const;
+ void SaveSheetXml(XclExpXmlStream& rStrm, const OUString& aIdFormControlPr) const;
+
+ void setShapeId(sal_Int32 aShapeId);
+
+private:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+ /** Writes a subrecord containing a cell link, or nothing, if no link present. */
+ void WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId );
+ /** Writes the ftSbs sub structure containing scrollbar data. */
+ void WriteSbs( XclExpStream& rStrm );
+
+private:
+ const css::uno::Reference< css::drawing::XShape > mxShape;
+ ScfInt16Vec maMultiSel; /// Indexes of all selected entries in a multi selection.
+ XclTbxEventType meEventType; /// Type of supported macro event.
+ sal_Int32 mnHeight; /// Height of the control.
+ sal_uInt16 mnState; /// Checked/unchecked state.
+ sal_Int16 mnLineCount; /// Combobox dropdown line count.
+ sal_Int16 mnSelEntry; /// Selected entry in combobox (1-based).
+ sal_uInt16 mnScrollValue; /// Scrollbar: Current value.
+ sal_uInt16 mnScrollMin; /// Scrollbar: Minimum value.
+ sal_uInt16 mnScrollMax; /// Scrollbar: Maximum value.
+ sal_uInt16 mnScrollStep; /// Scrollbar: Single step.
+ sal_uInt16 mnScrollPage; /// Scrollbar: Page step.
+ bool mbFlatButton; /// False = 3D button style; True = Flat button style.
+ bool mbFlatBorder; /// False = 3D border style; True = Flat border style.
+ bool mbMultiSel; /// true = Multi selection in listbox.
+ bool mbScrollHor; /// Scrollbar: true = horizontal.
+ bool mbPrint;
+ bool mbVisible;
+ OUString msCtrlName;
+ OUString msLabel;
+ sal_Int32 mnShapeId;
+ tools::Rectangle maAreaFrom;
+ tools::Rectangle maAreaTo;
+ XclExpObjectManager& mrRoot;
+};
+
+//#endif
+
+class XclExpChart;
+
+/** A chart object. This is the drawing object wrapper for the chart data. */
+class XclExpChartObj : public XclObj, protected XclExpRoot
+{
+public:
+ explicit XclExpChartObj(
+ XclExpObjectManager& rObjMgr,
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor,
+ ScDocument* pDoc );
+ virtual ~XclExpChartObj() override;
+
+ /** Writes the OBJ record and the entire chart substream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ css::uno::Reference<css::chart::XChartDocument> GetChartDoc() const;
+
+private:
+ typedef std::shared_ptr< XclExpChart > XclExpChartRef;
+ XclExpChartRef mxChart; /// The chart itself (BOF/EOF substream data).
+ css::uno::Reference< css::drawing::XShape > mxShape;
+ ScDocument* mpDoc;
+};
+
+/** Represents a NOTE record containing the relevant data of a cell note.
+
+ NOTE records differ significantly in various BIFF versions. This class
+ encapsulates all needed actions for each supported BIFF version.
+ BIFF5/BIFF7: Stores the note text and generates a single or multiple NOTE
+ records on saving.
+ BIFF8: Creates the Escher object containing the drawing information and the
+ note text.
+ */
+class XclExpNote : public XclExpRecord
+{
+public:
+ /** Constructs a NOTE record from the passed note object and/or the text.
+ @descr The additional text will be separated from the note text with
+ an empty line.
+ @param rScPos The Calc cell address of the note.
+ @param pScNote The Calc note object. May be 0 to create a note from rAddText only.
+ @param rAddText Additional text appended to the note text. */
+ explicit XclExpNote(
+ const XclExpRoot& rRoot,
+ const ScAddress& rScPos,
+ const ScPostIt* pScNote,
+ std::u16string_view rAddText );
+
+ /** Writes the NOTE record, if the respective Escher object is present. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ void WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm );
+
+ const XclExpString& GetAuthor() const { return maAuthor; }
+private:
+ /** Writes the body of the NOTE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+ XclExpString maAuthor; /// Name of the author.
+ OString maNoteText; /// Main text of the note (<=BIFF7).
+ XclExpStringRef mpNoteContents; /// Text and formatting data (OOXML)
+ ScAddress maScPos; /// Calc cell address of the note.
+ sal_uInt16 mnObjId; /// Escher object ID (BIFF8).
+ bool mbVisible; /// true = permanently visible.
+ SdrTextHorzAdjust meTHA; /// text horizontal adjust
+ SdrTextVertAdjust meTVA; /// text vertical adjust
+ bool mbAutoScale; /// Auto scale text
+ bool mbLocked; /// Position & Size locked
+ bool mbAutoFill; /// Auto Fill Style
+ bool mbColHidden; /// Column containing the comment is hidden
+ bool mbRowHidden; /// Row containing the comment is hidden
+ tools::Rectangle maCommentFrom; /// From and From Offset
+ tools::Rectangle maCommentTo; /// To and To Offsets
+};
+
+class XclExpComments : public XclExpRecord
+{
+public:
+ XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ SCTAB mnTab;
+ XclExpRecordList< XclExpNote >& mrNotes;
+};
+
+// object manager =============================================================
+
+class XclExpObjectManager : public XclExpRoot
+{
+public:
+ explicit XclExpObjectManager( const XclExpRoot& rRoot );
+ virtual ~XclExpObjectManager() override;
+
+ /** Creates a new DFF client anchor object. Caller takes ownership! May be
+ overwritten in derived classes. */
+ virtual XclExpDffAnchorBase* CreateDffAnchor() const;
+
+ /** Creates and returns the MSODRAWINGGROUP record containing global DFF
+ data in the DGGCONTAINER. */
+ rtl::Reference< XclExpRecordBase > CreateDrawingGroup();
+
+ /** Initializes the object manager for a new sheet. */
+ void StartSheet();
+
+ /** Processes a drawing page and returns the record block containing all
+ related records (MSODRAWING, OBJ, TXO, charts, etc.). */
+ rtl::Reference< XclExpRecordBase > ProcessDrawing( const SdrPage* pSdrPage );
+ /** Processes a collection of UNO shapes and returns the record block
+ containing all related records (MSODRAWING, OBJ, TXO, charts, etc.). */
+ rtl::Reference< XclExpRecordBase > ProcessDrawing( const css::uno::Reference< css::drawing::XShapes >& rxShapes );
+
+ /** Finalizes the object manager after conversion of all sheets. */
+ void EndDocument();
+
+ XclEscherEx& GetEscherEx() { return *mxEscherEx; }
+ XclExpMsoDrawing* GetMsodrawingPerSheet();
+ bool HasObj() const;
+ sal_uInt16 AddObj( std::unique_ptr<XclObj> pObjRec );
+ std::unique_ptr<XclObj> RemoveLastObj();
+
+protected:
+ explicit XclExpObjectManager( const XclExpObjectManager& rParent );
+
+private:
+ void InitStream( bool bTempFile );
+
+private:
+ std::optional< ::utl::TempFileFast > moTempFile;
+ SvStream* mpDffStrm = nullptr;
+ std::unique_ptr<SvMemoryStream> mpBackupStrm;
+ std::shared_ptr< XclEscherEx > mxEscherEx;
+ rtl::Reference< XclExpObjList > mxObjList;
+};
+
+class XclExpEmbeddedObjectManager : public XclExpObjectManager
+{
+public:
+ explicit XclExpEmbeddedObjectManager(
+ const XclExpObjectManager& rParent,
+ const Size& rPageSize,
+ sal_Int32 nScaleX, sal_Int32 nScaleY );
+
+ /** Creates a new DFF client anchor object for embedded objects according
+ to the scaling data passed to the constructor. Caller takes ownership! */
+ virtual XclExpDffAnchorBase* CreateDffAnchor() const override;
+
+private:
+ Size maPageSize;
+ sal_Int32 mnScaleX;
+ sal_Int32 mnScaleY;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeextlst.hxx b/sc/source/filter/inc/xeextlst.hxx
new file mode 100644
index 0000000000..1a33b5106e
--- /dev/null
+++ b/sc/source/filter/inc/xeextlst.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+
+#include <colorscale.hxx>
+
+#include <memory>
+
+enum XclExpExtType
+{
+ XclExpExtDataBarType,
+ XclExpExtDataFooType,
+ XclExpExtSparklineType,
+};
+
+struct XclExpExtCondFormatData
+{
+ // -1 means don't write priority
+ sal_Int32 nPriority;
+ OString aGUID;
+ const ScFormatEntry* pEntry;
+};
+
+/**
+ * Base class for ext entries. Extend this class to provide the needed functionality
+ */
+class XclExpExt : public XclExpRecordBase, public XclExpRoot
+{
+public:
+ explicit XclExpExt( const XclExpRoot& rRoot );
+ virtual XclExpExtType GetType() = 0;
+
+protected:
+ OString maURI;
+};
+
+class XclExpExtCfvo : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpExtCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rPos, bool bFirst );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScColorScaleEntryType meType;
+ OString maValue;
+ bool mbFirst;
+};
+
+class XclExpExtNegativeColor
+{
+public:
+ XclExpExtNegativeColor( const Color& rColor );
+ void SaveXml( XclExpXmlStream& rStrm);
+
+private:
+ Color maColor;
+};
+
+class XclExpExtAxisColor
+{
+public:
+ XclExpExtAxisColor( const Color& maColor );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ Color maAxisColor;
+};
+
+class XclExpExtIcon : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtIcon( const XclExpRoot& rRoot, const std::pair<ScIconSetType, sal_Int32>& rCustomEntry);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ const char* pIconSetName;
+ sal_Int32 nIndex;
+};
+
+class XclExpExtCF : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormat );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ OUString aFormula;
+ ScCondFormatEntry mrFormat;
+};
+
+class XclExpExtDataBar : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, const ScAddress& rPos );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ databar::ScAxisPosition meAxisPosition;
+ bool mbGradient;
+ double mnMinLength;
+ double mnMaxLength;
+
+ std::unique_ptr<XclExpExtCfvo> mpLowerLimit;
+ std::unique_ptr<XclExpExtCfvo> mpUpperLimit;
+ std::unique_ptr<XclExpExtNegativeColor> mpNegativeColor;
+ std::unique_ptr<XclExpExtAxisColor> mpAxisColor;
+
+};
+
+class XclExpExtIconSet : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtIconSet(const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, const ScAddress& rPos);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ XclExpRecordList<XclExpExtCfvo> maCfvos;
+ XclExpRecordList<XclExpExtIcon> maCustom;
+ bool mbCustom;
+ bool mbReverse;
+ bool mbShowValue;
+ const char* mpIconSetName;
+};
+
+
+class XclExpExtCfRule : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpExtCfRule( const XclExpRoot& rRoot, const ScFormatEntry& rFormat, const ScAddress& rPos, OString aId, sal_Int32 nPriority );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ XclExpRecordRef mxEntry;
+ OString maId;
+ const char* pType;
+ sal_Int32 mnPriority;
+ const char* mOperator;
+};
+
+typedef rtl::Reference<XclExpExt> XclExpExtRef;
+
+class XclExpExtConditionalFormatting : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtConditionalFormatting( const XclExpRoot& rRoot, std::vector<XclExpExtCondFormatData>& rData, ScRangeList aRange);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ XclExpRecordList<XclExpExtCfRule> maCfRules;
+ ScRangeList maRange;
+};
+
+typedef rtl::Reference<XclExpExtConditionalFormatting> XclExpExtConditionalFormattingRef;
+
+class XclExpExtCondFormat : public XclExpExt
+{
+public:
+ XclExpExtCondFormat( const XclExpRoot& rRoot );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual XclExpExtType GetType() override { return XclExpExtDataBarType; }
+
+ void AddRecord( XclExpExtConditionalFormatting* pFormat );
+
+private:
+ XclExpRecordList< XclExpExtConditionalFormatting > maCF;
+};
+
+class XclExpExtCalcPr : public XclExpExt
+{
+public:
+ XclExpExtCalcPr( const XclExpRoot& rRoot, formula::FormulaGrammar::AddressConvention eConv );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual XclExpExtType GetType() override { return XclExpExtDataFooType; }
+
+private:
+ OString maSyntax;
+};
+
+class XclExtLst : public XclExpRecordBase, public XclExpRoot
+{
+public:
+ explicit XclExtLst( const XclExpRoot& rRoot);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void AddRecord( XclExpExt* pEntry );
+ void AddRecord( const XclExpExtRef& aEntry ) { AddRecord(aEntry.get()); }
+
+ XclExpExt* GetItem( XclExpExtType eType );
+
+private:
+ XclExpRecordList< XclExpExt > maExtEntries;
+};
+
+typedef rtl::Reference< XclExtLst > XclExtLstRef;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeformula.hxx b/sc/source/filter/inc/xeformula.hxx
new file mode 100644
index 0000000000..d050b2217d
--- /dev/null
+++ b/sc/source/filter/inc/xeformula.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 "xlformula.hxx"
+#include "xeroot.hxx"
+#include <memory>
+
+struct XclAddress;
+
+// External reference log =====================================================
+
+/** Log entry for external references in a formula, used i.e. in change tracking. */
+struct XclExpRefLogEntry
+{
+ const XclExpString* mpUrl; /// URL of the document containing the first sheet.
+ const XclExpString* mpFirstTab; /// Name of the first sheet.
+ const XclExpString* mpLastTab; /// Name of the last sheet.
+ sal_uInt16 mnFirstXclTab; /// Calc index of the first sheet.
+ sal_uInt16 mnLastXclTab; /// Calc index of the last sheet.
+
+ explicit XclExpRefLogEntry();
+};
+
+/** Vector containing a log for all external references in a formula, used i.e. in change tracking. */
+typedef ::std::vector< XclExpRefLogEntry > XclExpRefLog;
+
+// Formula compiler ===========================================================
+
+class ScRangeList;
+class XclExpFmlaCompImpl;
+
+/** The formula compiler to create Excel token arrays from Calc token arrays. */
+class XclExpFormulaCompiler : protected XclExpRoot
+{
+public:
+ explicit XclExpFormulaCompiler( const XclExpRoot& rRoot );
+ virtual ~XclExpFormulaCompiler() override;
+
+ /** Creates and returns the token array of a formula. */
+ XclTokenArrayRef CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos = nullptr, XclExpRefLog* pRefLog = nullptr );
+
+ /** Creates and returns a token array containing a single cell address. */
+ XclTokenArrayRef CreateFormula( XclFormulaType eType, const ScAddress& rScPos );
+
+ /** Creates and returns a token array containing a single cell range address. */
+ XclTokenArrayRef CreateFormula( XclFormulaType eType, const ScRange& rScRange );
+
+ /** Creates and returns the token array for a cell range list. */
+ XclTokenArrayRef CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges );
+
+ /** Creates a single error token containing the passed error code. */
+ XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode );
+
+ /** Creates a single token for a special cell reference.
+ @descr This is used for array formulas and shared formulas (token tExp),
+ and multiple operation tables (token tTbl). */
+ XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos );
+
+ /** Creates a single tNameXR token for a reference to an external name.
+ @descr This is used i.e. for linked macros in push buttons. */
+ XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
+
+ bool IsRef2D( const ScSingleRefData& rRefData ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData ) const;
+
+private:
+ typedef std::shared_ptr< XclExpFmlaCompImpl > XclExpFmlaCompImplRef;
+ XclExpFmlaCompImplRef mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xehelper.hxx b/sc/source/filter/inc/xehelper.hxx
new file mode 100644
index 0000000000..fac6b3bb24
--- /dev/null
+++ b/sc/source/filter/inc/xehelper.hxx
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <string_view>
+
+#include "ftools.hxx"
+#include <rangelst.hxx>
+#include "xladdress.hxx"
+#include "xeroot.hxx"
+#include "xlstring.hxx"
+
+// Export progress bar ========================================================
+
+class ScfProgressBar;
+
+/** The main progress bar for the export filter.
+
+ This class encapsulates creation and initialization of sub progress
+ segments. The Activate***Segment() functions activate a specific segment
+ of the main progress bar. The implementation of these functions contain the
+ calculation of the needed size of the segment. Following calls of the
+ Progress() function increase the currently activated sub segment.
+ */
+class XclExpProgressBar : protected XclExpRoot
+{
+public:
+ explicit XclExpProgressBar( const XclExpRoot& rRoot );
+ virtual ~XclExpProgressBar() override;
+
+ /** Initializes all segments and sub progress bars. */
+ void Initialize();
+
+ /** Increases the number of existing ROW records by 1. */
+ void IncRowRecordCount();
+
+ /** Activates the progress segment to create ROW records. */
+ void ActivateCreateRowsSegment();
+ /** Activates the progress segment to finalize ROW records. */
+ void ActivateFinalRowsSegment();
+
+ /** Increases the currently activated (sub) progress bar by 1 step. */
+ void Progress();
+
+private:
+ typedef std::unique_ptr< ScfProgressBar > ScfProgressBarPtr;
+
+ ScfProgressBarPtr mxProgress; /// Progress bar implementation.
+ ScfProgressBar* mpSubProgress; /// Current sub progress bar.
+
+ ScfProgressBar* mpSubRowCreate; /// Sub progress bar for creating table rows.
+ ScfInt32Vec maSubSegRowCreate; /// Segment ID's for all sheets in sub progress bar.
+
+ ScfProgressBar* mpSubRowFinal; /// Sub progress bar for finalizing ROW records.
+ sal_Int32 mnSegRowFinal; /// Progress segment for finalizing ROW records.
+
+ std::size_t mnRowCount; /// Number of created ROW records.
+};
+
+// Calc->Excel cell address/range conversion ==================================
+
+/** Provides functions to convert Calc cell addresses to Excel cell addresses. */
+class XclExpAddressConverter : public XclAddressConverterBase
+{
+public:
+ explicit XclExpAddressConverter( const XclExpRoot& rRoot );
+
+ // cell address -----------------------------------------------------------
+
+ /** Checks if the passed Calc cell address is valid.
+ @param rScPos The Calc cell address to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is not valid.
+ @return true = Cell address in rScPos is valid. */
+ bool CheckAddress( const ScAddress& rScPos, bool bWarn );
+
+ /** Converts the passed Calc cell address to an Excel cell address.
+ @param rXclPos (Out) The converted Excel cell address, if valid.
+ @param rScPos The Calc cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is not valid.
+ @return true = Cell address returned in rXclPos is valid. */
+ bool ConvertAddress( XclAddress& rXclPos,
+ const ScAddress& rScPos, bool bWarn );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+ @param rScPos The Calc cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is invalid.
+ @return The converted Excel cell address. */
+ XclAddress CreateValidAddress( const ScAddress& rScPos, bool bWarn );
+
+ // cell range -------------------------------------------------------------
+
+ /** Checks if the passed cell range is valid (checks start and end position).
+ @param rScRange The Calc cell range to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range is not valid.
+ @return true = Cell range in rScRange is valid. */
+ bool CheckRange( const ScRange& rScRange, bool bWarn );
+
+ /** Checks and eventually crops the cell range to valid dimensions.
+ @descr The start position of the range will not be modified.
+ @param rScRange (In/out) The cell range to validate.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range contains invalid
+ cells. If the range is partly valid, this function sets the warning
+ flag, corrects the range and returns true.
+ @return true = Cell range in rScRange is valid (original or cropped). */
+ bool ValidateRange( ScRange& rScRange, bool bWarn );
+
+ /** Converts the passed Calc cell range to an Excel cell range.
+ @param rXclRange (Out) The converted Excel cell range, if valid.
+ @param rScRange The Calc cell range to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range contains invalid cells.
+ @return true = Cell range returned in rXclRange is valid (original or cropped). */
+ bool ConvertRange( XclRange& rXclRange, const ScRange& rScRange, bool bWarn );
+
+ // cell range list --------------------------------------------------------
+
+ /** Checks and eventually crops the cell ranges to valid dimensions.
+ @descr The start position of the ranges will not be modified. Cell
+ ranges that fit partly into valid dimensions are cropped
+ accordingly. Cell ranges that do not fit at all, are removed from
+ the cell range list.
+ @param rScRanges (In/out) The cell range list to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if at least one of the cell ranges
+ contains invalid cells. */
+ void ValidateRangeList( ScRangeList& rScRanges, bool bWarn );
+
+ /** Converts the passed Calc cell range list to an Excel cell range list.
+ @descr The start position of the ranges will not be modified. Cell
+ ranges that fit partly into valid dimensions are cropped
+ accordingly. Cell ranges that do not fit at all, are not inserted
+ into the Excel cell range list.
+ @param rXclRanges (Out) The converted Excel cell range list.
+ @param rScRanges The Calc cell range list to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if at least one of the cell ranges
+ contains invalid cells. */
+ void ConvertRangeList( XclRangeList& rXclRanges,
+ const ScRangeList& rScRanges, bool bWarn );
+};
+
+// EditEngine->String conversion ==============================================
+
+class SvxURLField;
+class XclExpHyperlink;
+
+/** Helper to create HLINK records during creation of formatted cell strings.
+
+ In Excel it is not possible to have more than one hyperlink in a cell. This
+ helper detects multiple occurrences of hyperlinks and fills a string which
+ is used to create a cell note containing all URLs. Only cells containing
+ one hyperlink are exported as hyperlink cells.
+ */
+class XclExpHyperlinkHelper : protected XclExpRoot
+{
+public:
+ typedef rtl::Reference< XclExpHyperlink > XclExpHyperlinkRef;
+
+ explicit XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos );
+ virtual ~XclExpHyperlinkHelper() override;
+
+ /** Processes the passed URL field (tries to create a HLINK record).
+ @return The representation string of the URL field. */
+ OUString ProcessUrlField( const SvxURLField& rUrlField );
+
+ /** Returns true, if a single HLINK record has been created. */
+ bool HasLinkRecord() const;
+ /** Returns the created single HLINk record, or an empty reference. */
+ XclExpHyperlinkRef GetLinkRecord() const;
+
+ /** Returns true, if multiple URLs have been processed. */
+ bool HasMultipleUrls() const { return mbMultipleUrls; }
+ /** Returns a string containing all processed URLs. */
+ const OUString& GetUrlList() const { return maUrlList; }
+
+private:
+ XclExpHyperlinkRef mxLinkRec; /// Created HLINK record.
+ ScAddress maScPos; /// Cell position to set at the HLINK record.
+ OUString maUrlList; /// List with all processed URLs.
+ bool mbMultipleUrls; /// true = Multiple URL fields processed.
+};
+
+class EditEngine;
+class EditTextObject;
+class SdrTextObj;
+class ScPatternAttr;
+
+/** This class provides methods to create an XclExpString.
+ @descr The string can be created from an edit engine text object or
+ directly from a Calc edit cell. */
+class XclExpStringHelper
+{
+public:
+ /** removes copy constructor */
+ XclExpStringHelper(const XclExpStringHelper &) = delete;
+ /** remove copy-assignment operator */
+ const XclExpStringHelper& operator=(const XclExpStringHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods */
+ XclExpStringHelper() = delete;
+
+ /** Creates a new unformatted string from the passed string.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param rString The source string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ const OUString& rString,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Creates a new unformatted string from the passed character.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param cChar The source character. The NUL character is explicitly allowed.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ sal_Unicode cChar,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Appends an unformatted string to an Excel string object.
+ @descr Selects the correct Append() function depending on the current
+ BIFF version contained in the passed XclExpRoot object.
+ @param rXclString The Excel string object.
+ @param rString The source string. */
+ static void AppendString(
+ XclExpString& rXclString,
+ const XclExpRoot& rRoot,
+ std::u16string_view rString);
+
+ /** Appends a character to an Excel string object.
+ @descr Selects the correct Append() function depending on the current
+ BIFF version contained in the passed XclExpRoot object.
+ @param rXclString The Excel string object.
+ @param rString The source string. */
+ static void AppendChar(
+ XclExpString& rXclString,
+ const XclExpRoot& rRoot,
+ sal_Unicode cChar );
+
+ /** Creates a new formatted string from a Calc string cell.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ May create a formatted string object, if the cell text contains
+ different script types.
+ @param rStringCell The Calc string cell object.
+ @param pCellAttr The set item containing the cell formatting.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateCellString(
+ const XclExpRoot& rRoot,
+ const OUString& rString,
+ const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Creates a new formatted string from a Calc edit cell.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param rEditCell The Calc edit cell object.
+ @param pCellAttr The set item containing the cell formatting.
+ @param rLinkHelper Helper object for hyperlink conversion.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateCellString(
+ const XclExpRoot& rRoot,
+ const EditTextObject& rEditText,
+ const ScPatternAttr* pCellAttr,
+ XclExpHyperlinkHelper& rLinkHelper,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Creates a new formatted string from a drawing text box.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param rTextObj The text box object.
+ @param nFlags Modifiers for string export.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ const SdrTextObj& rTextObj,
+ XclStrFlags nFlags = XclStrFlags::NONE );
+
+ /** Creates a new formatted string from an edit text string.
+ @param rEditObj The edittext object.
+ @param nFlags Modifiers for string export.
+ @return The new string object. */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ const EditTextObject& rEditObj,
+ XclStrFlags nFlags = XclStrFlags::NONE );
+
+ /** Returns the script type first text portion different to WEAK, or the system
+ default script type, if there is only weak script in the passed string. */
+ static sal_Int16 GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString );
+};
+
+// Header/footer conversion ===================================================
+
+/** Converts edit engine text objects to an Excel header/footer string.
+ @descr Header/footer content is divided into three parts: Left, center and
+ right portion. All formatting information will be encoded in the Excel string
+ using special character sequences. A control sequence starts with the ampersand
+ character.
+
+ Supported control sequences:
+ &L start of left portion
+ &C start of center portion
+ &R start of right portion
+ &P current page number
+ &N page count
+ &D current date
+ &T current time
+ &A table name
+ &F file name without path
+ &Z file path without file name
+ &Z&F file path and name
+ &U underlining on/off
+ &E double underlining on/off
+ &S strikeout characters on/off
+ &X superscript on/off
+ &Y subscript on/off
+ &"fontname,fontstyle" use font with name 'fontname' and style 'fontstyle'
+ &fontheight set font height in points ('fontheight' is a decimal value)
+
+ Known but unsupported control sequences:
+ &G picture
+ */
+class XclExpHFConverter : protected XclExpRoot
+{
+public:
+ /** delete copy constructor */
+ XclExpHFConverter(const XclExpHFConverter&) = delete;
+ /** delete copy-assignment operator */
+ const XclExpHFConverter& operator=(const XclExpHFConverter&) = delete;
+
+ explicit XclExpHFConverter( const XclExpRoot& rRoot );
+
+ /** Generates the header/footer string from the passed edit engine text objects. */
+ void GenerateString(
+ const EditTextObject* pLeftObj,
+ const EditTextObject* pCenterObj,
+ const EditTextObject* pRightObj );
+
+ /** Returns the last generated header/footer string. */
+ const OUString& GetHFString() const { return maHFString; }
+ /** Returns the total height of the last generated header/footer in twips. */
+ sal_Int32 GetTotalHeight() const { return mnTotalHeight; }
+
+private:
+ /** Converts the text object contents and stores it in the passed string. */
+ void AppendPortion(
+ const EditTextObject* pTextObj,
+ sal_Unicode cPortionCode );
+
+private:
+ EditEngine& mrEE; /// The header/footer edit engine.
+ OUString maHFString; /// The last generated header/footer string.
+ sal_Int32 mnTotalHeight; /// Total height of the last header/footer (twips).
+};
+
+// URL conversion =============================================================
+
+/** This class contains static methods to encode a file URL.
+ @descr Excel stores URLs in a format that contains special control characters,
+ i.e. for directory separators or volume names. */
+class XclExpUrlHelper
+{
+public:
+ /** delete copy constructor */
+ XclExpUrlHelper(const XclExpUrlHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclExpUrlHelper& operator=(const XclExpUrlHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclExpUrlHelper() = delete;
+
+ /** Encodes and returns the URL passed in rAbsUrl to an Excel like URL.
+ @param pTableName Optional pointer to a table name to be encoded in this URL. */
+ static OUString EncodeUrl( const XclExpRoot& rRoot, std::u16string_view rAbsUrl, const OUString* pTableName = nullptr );
+ /** Encodes and returns the passed DDE link to an Excel like DDE link. */
+ static OUString EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic );
+};
+
+class ScMatrix;
+
+/** Contains cached values in a 2-dimensional array. */
+class XclExpCachedMatrix
+{
+ void GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const;
+public:
+ /** Constructs and fills a new matrix.
+ @param rMatrix The Calc value matrix. */
+ explicit XclExpCachedMatrix( const ScMatrix& rMatrix );
+ ~XclExpCachedMatrix();
+
+ /** Returns the byte count of all contained data. */
+ std::size_t GetSize() const;
+ /** Writes the complete matrix to stream. */
+ void Save( XclExpStream& rStrm ) const;
+
+private:
+ const ScMatrix& mrMatrix;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xelink.hxx b/sc/source/filter/inc/xelink.hxx
new file mode 100644
index 0000000000..1c816d21fb
--- /dev/null
+++ b/sc/source/filter/inc/xelink.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 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+#include <externalrefmgr.hxx>
+#include <memory>
+#include <o3tl/typed_flags_set.hxx>
+
+struct ScSingleRefData;
+struct ScComplexRefData;
+struct XclExpRefLogEntry;
+
+/* ============================================================================
+Classes for export of different kinds of internal/external references.
+- 3D cell and cell range links
+- External cell and cell range links
+- External defined names
+- Macro calls
+- Add-in functions
+- DDE links
+- OLE object links
+============================================================================ */
+
+// Excel sheet indexes ========================================================
+
+enum class ExcTabBufFlags : sal_uInt8 {
+ NONE = 0x00,
+ Ignore = 0x01, /// Sheet will be ignored completely.
+ Extern = 0x02, /// Sheet is linked externally.
+ SkipMask = 0x03, /// Sheet will be skipped, if any flag is set.
+ Visible = 0x10, /// Sheet is visible.
+ Selected = 0x20, /// Sheet is selected.
+ Mirrored = 0x40 /// Sheet is mirrored (right-to-left).
+};
+namespace o3tl {
+ template<> struct typed_flags<ExcTabBufFlags> : is_typed_flags<ExcTabBufFlags, 0x73> {};
+}
+
+/** Stores the correct Excel sheet index for each Calc sheet.
+ @descr The class knows all sheets which will not exported
+ (i.e. external link sheets, scenario sheets). */
+class XclExpTabInfo : protected XclExpRoot
+{
+public:
+ /** Initializes the complete buffer from the current exported document. */
+ explicit XclExpTabInfo( const XclExpRoot& rRoot );
+
+ /** Returns true, if the specified Calc sheet will be exported. */
+ bool IsExportTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is used to store external cell contents. */
+ bool IsExternalTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is visible and will be exported. */
+ bool IsVisibleTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is selected and will be exported. */
+ bool IsSelectedTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is the displayed (active) sheet. */
+ bool IsDisplayedTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is displayed in right-to-left mode. */
+ bool IsMirroredTab( SCTAB nScTab ) const;
+ /** Returns the Calc name of the specified sheet. */
+ OUString GetScTabName( SCTAB nScTab ) const;
+
+ /** Returns the Excel sheet index for a given Calc sheet. */
+ sal_uInt16 GetXclTab( SCTAB nScTab ) const;
+
+ /** Returns the Calc sheet index of the nSortedTab-th entry in the sorted sheet names list. */
+ SCTAB GetRealScTab( SCTAB nSortedScTab ) const;
+
+ /** Returns the number of Calc sheets. */
+ SCTAB GetScTabCount() const { return mnScCnt; }
+
+ /** Returns the number of Excel sheets to be exported. */
+ sal_uInt16 GetXclTabCount() const { return mnXclCnt; }
+ /** Returns the number of external linked sheets. */
+ sal_uInt16 GetXclExtTabCount() const { return mnXclExtCnt; }
+ /** Returns the number of exported selected sheets. */
+ sal_uInt16 GetXclSelectedCount() const { return mnXclSelCnt; }
+
+ /** Returns the Excel index of the active, displayed sheet. */
+ sal_uInt16 GetDisplayedXclTab() const { return mnDisplXclTab; }
+ /** Returns the Excel index of the first visible sheet. */
+ sal_uInt16 GetFirstVisXclTab() const { return mnFirstVisXclTab; }
+
+private:
+ /** Returns true, if any of the passed flags is set for the specified Calc sheet. */
+ bool GetFlag( SCTAB nScTab, ExcTabBufFlags nFlags ) const;
+ /** Sets or clears (depending on bSet) all passed flags for the specified Calc sheet. */
+ void SetFlag( SCTAB nScTab, ExcTabBufFlags nFlags, bool bSet = true );
+
+ /** Searches for sheets not to be exported. */
+ void CalcXclIndexes();
+ /** Sorts the names of all tables and stores the indexes of the sorted indexes. */
+ void CalcSortedIndexes();
+
+private:
+ /** Data structure with information about one Calc sheet. */
+ struct XclExpTabInfoEntry
+ {
+ OUString maScName;
+ sal_uInt16 mnXclTab;
+ ExcTabBufFlags mnFlags;
+ explicit XclExpTabInfoEntry() : mnXclTab( 0 ), mnFlags( ExcTabBufFlags::NONE ) {}
+ };
+
+ typedef ::std::vector< SCTAB > ScTabVec;
+
+ std::vector< XclExpTabInfoEntry >
+ maTabInfoVec; /// Array of Calc sheet index information.
+
+ SCTAB mnScCnt; /// Count of Calc sheets.
+ sal_uInt16 mnXclCnt; /// Count of Excel sheets to be exported.
+ sal_uInt16 mnXclExtCnt; /// Count of external link sheets.
+ sal_uInt16 mnXclSelCnt; /// Count of selected and exported sheets.
+ sal_uInt16 mnDisplXclTab; /// Displayed (active) sheet.
+ sal_uInt16 mnFirstVisXclTab; /// First visible sheet.
+
+ ScTabVec maFromSortedVec; /// Sorted Calc sheet index -> real Calc sheet index.
+ ScTabVec maToSortedVec; /// Real Calc sheet index -> sorted Calc sheet index.
+};
+
+// Export link manager ========================================================
+
+class XclExpLinkManagerImpl;
+
+/** Stores all data for internal/external references (the link table). */
+class XclExpLinkManager : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpLinkManager( const XclExpRoot& rRoot );
+ virtual ~XclExpLinkManager() override;
+
+ /** Searches for an EXTERNSHEET index for the given Calc sheet.
+ @descr See above for the meaning of EXTERNSHEET indexes.
+ @param rnExtSheet (out-param) Returns the EXTERNSHEET index.
+ @param rnXclTab (out-param) Returns the Excel sheet index.
+ @param nScTab The Calc sheet index to process.
+ param pRefLogEntry If not 0, data about the external link is stored here. */
+ void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnXclTab, SCTAB nScTab,
+ XclExpRefLogEntry* pRefLogEntry = nullptr );
+ /** Searches for an EXTERNSHEET index for the given Calc sheet range.
+ @descr See above for the meaning of EXTERNSHEET indexes.
+ @param rnExtSheet (out-param) Returns the EXTERNSHEET index.
+ @param rnFirstXclTab (out-param) Returns the Excel sheet index of the first sheet.
+ @param rnXclTab (out-param) Returns the Excel sheet index of the last sheet.
+ @param nFirstScTab The first Calc sheet index to process.
+ @param nLastScTab The last Calc sheet index to process.
+ param pRefLogEntry If not 0, data about the external link is stored here. */
+ void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry );
+ /** Searches for a special EXTERNSHEET index for the own document. */
+ sal_uInt16 FindExtSheet( sal_Unicode cCode );
+
+ void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry );
+
+ /** Stores the cell with the given address in a CRN record list. */
+ void StoreCell( const ScSingleRefData& rRef, const ScAddress& rPos );
+ /** Stores all cells in the given range in a CRN record list. */
+ void StoreCellRange( const ScComplexRefData& rRef, const ScAddress& rPos );
+
+ void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos );
+
+ void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange );
+
+ /** Finds or inserts an EXTERNNAME record for an add-in function name.
+ @param rnExtSheet (out-param) Returns the index of the EXTSHEET structure for the add-in function name.
+ @param rnExtName (out-param) Returns the 1-based EXTERNNAME record index.
+ @return true = add-in function inserted; false = error (i.e. not supported in current BIFF). */
+ bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** InsertEuroTool */
+ bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @param rnExtSheet (out-param) Returns the index of the EXTSHEET structure for the DDE link.
+ @param rnExtName (out-param) Returns the 1-based EXTERNNAME record index.
+ @return true = DDE link inserted; false = error (i.e. not supported in current BIFF). */
+ bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem );
+
+ bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Writes the entire Link table. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the entire Link table to OOXML. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpLinkManagerImpl > XclExpLinkMgrImplPtr;
+ XclExpLinkMgrImplPtr mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xename.hxx b/sc/source/filter/inc/xename.hxx
new file mode 100644
index 0000000000..a81a12804b
--- /dev/null
+++ b/sc/source/filter/inc/xename.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlformula.hxx"
+#include "xeroot.hxx"
+#include <memory>
+
+class ScRangeList;
+class XclExpNameManagerImpl;
+
+/** Manager that stores all internal defined names (NAME records) of the document. */
+class XclExpNameManager : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpNameManager( const XclExpRoot& rRoot );
+ virtual ~XclExpNameManager() override;
+
+ /** Creates NAME records for built-in and user defined names. */
+ void Initialize();
+
+ /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
+ sal_uInt16 InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab );
+
+ /** Inserts a new built-in defined name, referring to the passed sheet range. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange );
+ /** Inserts a new built-in defined name, referring to the passed sheet range list. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList );
+
+ /** Inserts a new defined name. Sets another unused name, if rName already exists. */
+ sal_uInt16 InsertUniqueName( const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab );
+ /** Returns index of an existing name, or creates a name without definition. */
+ sal_uInt16 InsertRawName( const OUString& rName );
+ /** Searches or inserts a defined name describing a macro name.
+ @param bVBasic true = Visual Basic macro, false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ sal_uInt16 InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden = false );
+
+ /** Returns the Calc sheet of a local defined name, or SCTAB_GLOBAL for global defined names. */
+ OUString GetOrigName( sal_uInt16 nNameIdx ) const;
+ /** Returns the Calc sheet of a local defined name, or SCTAB_GLOBAL for global defined names. */
+ SCTAB GetScTab( sal_uInt16 nNameIdx ) const;
+ /** Returns true, if the specified defined name is volatile. */
+ bool IsVolatile( sal_uInt16 nNameIdx ) const;
+
+ /** Writes the entire list of NAME records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpNameManagerImpl > XclExpNameMgrImplRef;
+ XclExpNameMgrImplRef mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xepage.hxx b/sc/source/filter/inc/xepage.hxx
new file mode 100644
index 0000000000..97100c125b
--- /dev/null
+++ b/sc/source/filter/inc/xepage.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlpage.hxx"
+#include "xeroot.hxx"
+
+class XclExpImgData;
+
+// Page settings records ======================================================
+
+// Header/footer --------------------------------------------------------------
+
+/** Represents a HEADER or FOOTER record. */
+class XclExpHeaderFooter : public XclExpRecord
+{
+public:
+ explicit XclExpHeaderFooter( sal_uInt16 nRecId, OUString aHdrString );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the header or footer string. Writes an empty record, if no header/footer present. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ OUString maHdrString; /// Header or footer contents.
+};
+
+// General page settings ------------------------------------------------------
+
+/** Represents a SETUP record that contains common page settings. */
+class XclExpSetup : public XclExpRecord
+{
+public:
+ explicit XclExpSetup( const XclPageData& rPageData );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the contents of the SETUP record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclPageData& mrData; /// Page settings data of current sheet.
+};
+
+// Manual page breaks ---------------------------------------------------------
+
+/** Stores an array of manual page breaks for columns or rows. */
+class XclExpPageBreaks : public XclExpRecord
+{
+public:
+ explicit XclExpPageBreaks(
+ sal_uInt16 nRecId,
+ const ScfUInt16Vec& rPageBreaks,
+ sal_uInt16 nMaxPos );
+
+ /** Writes the record, if the list is not empty. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the page break list. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const ScfUInt16Vec& mrPageBreaks; /// Page settings data of current sheet.
+ sal_uInt16 mnMaxPos; /// Maximum row/column for BIFF8 page breaks.
+};
+
+// Page settings ==============================================================
+
+/** Contains all page (print) settings records for a single sheet. */
+class XclExpPageSettings : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Creates all records containing the current page settings. */
+ explicit XclExpPageSettings( const XclExpRoot& rRoot );
+
+ /** Returns read-only access to the page data. */
+ const XclPageData& GetPageData() const { return maData; }
+
+ /** Writes all page settings records to the stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ XclExpImgData* getGraphicExport();
+
+private:
+ XclPageData maData; /// Page settings data.
+};
+
+/** Contains all page (print) settings records for a chart object. */
+class XclExpChartPageSettings : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Creates all records containing the current page settings. */
+ explicit XclExpChartPageSettings( const XclExpRoot& rRoot );
+
+ /** Writes all page settings records to the stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ XclPageData maData; /// Page settings data.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xepivot.hxx b/sc/source/filter/inc/xepivot.hxx
new file mode 100644
index 0000000000..1fb61696c9
--- /dev/null
+++ b/sc/source/filter/inc/xepivot.hxx
@@ -0,0 +1,436 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "xerecord.hxx"
+#include "xlpivot.hxx"
+#include "xeroot.hxx"
+
+class ScDPObject;
+class ScDPSaveData;
+class ScDPSaveDimension;
+class ScDPSaveMember;
+class ScDPSaveGroupDimension;
+struct ScDPNumGroupInfo;
+
+// Pivot cache
+
+/** Represents a data item in a pivot cache containing data of any type. */
+class XclExpPCItem : public XclExpRecord, public XclPCItem
+{
+public:
+ explicit XclExpPCItem( const OUString& rText );
+ explicit XclExpPCItem( double fValue, const OUString& rText = OUString() );
+ explicit XclExpPCItem( const DateTime& rDateTime, const OUString& rText = OUString() );
+ explicit XclExpPCItem( sal_Int16 nValue );
+ explicit XclExpPCItem( bool bValue, const OUString& rText );
+
+ sal_uInt16 GetTypeFlag() const { return mnTypeFlag; }
+
+ bool EqualsText( std::u16string_view rText ) const;
+ bool EqualsDouble( double fValue ) const;
+ bool EqualsDateTime( const DateTime& rDateTime ) const;
+ bool EqualsBool( bool bValue ) const;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnTypeFlag; /// Data type flag.
+};
+
+class XclExpPCField : public XclExpRecord, public XclPCField, protected XclExpRoot
+{
+public:
+ /** Creates a standard pivot cache field, filled from sheet source data. */
+ explicit XclExpPCField( const XclExpRoot& rRoot,
+ sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScRange& rRange );
+ /** Creates a child grouping pivot cache field, filled from the passed grouping info. */
+ explicit XclExpPCField( const XclExpRoot& rRoot,
+ sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim,
+ const XclExpPCField& rBaseField );
+ virtual ~XclExpPCField() override;
+
+ /** Sets the passed field as direct grouping child field of this field. */
+ void SetGroupChildField( const XclExpPCField& rChildField );
+
+ /** Returns the name of this cache field. */
+ const OUString& GetFieldName() const { return maFieldInfo.maName; }
+
+ /** Returns the number of visible items of this field. */
+ sal_uInt16 GetItemCount() const;
+ /** Returns the specified pivot cache item (returns visible items in groupings). */
+ const XclExpPCItem* GetItem( sal_uInt16 nItemIdx ) const;
+ /** Returns the index of a pivot cache item, or EXC_PC_NOITEM on error. */
+ sal_uInt16 GetItemIndex( std::u16string_view rItemName ) const;
+
+ /** Returns the size an item index needs to write out. */
+ std::size_t GetIndexSize() const;
+ /** Writes the item index at the passed source row position as part of the SXINDEXLIST record. */
+ void WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const;
+
+ /** Writes the pivot cache field and all items and other related records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpPCItem > XclExpPCItemList;
+
+ /** Returns the item list that contains the visible items.
+ @descr Visible items are equal to source items in standard fields,
+ but are generated items in grouping and calculated fields. */
+ const XclExpPCItemList& GetVisItemList() const;
+
+ /** Initializes a standard field. Inserts all original source items. */
+ void InitStandardField( const ScRange& rRange );
+ /** Initializes a standard grouping field. Inserts all visible grouping items. */
+ void InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim );
+ /** Initializes a numeric grouping field. Inserts all visible grouping items and the limit settings. */
+ void InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo );
+ /** Initializes a date grouping field. Inserts all visible grouping items and the limit settings. */
+ void InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart );
+
+ /** Inserts the passed index into the item index array of original items. */
+ void InsertItemArrayIndex( size_t nListPos );
+ /** Inserts an original source item. Updates item index array. */
+ void InsertOrigItem( XclExpPCItem* pNewItem );
+ /** Inserts an original text item, if it is not contained already. */
+ void InsertOrigTextItem( const OUString& rText );
+ /** Inserts an original value item, if it is not contained already. */
+ void InsertOrigDoubleItem( double fValue, const OUString& rText );
+ /** Inserts an original date/time item, if it is not contained already. */
+ void InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText );
+ /** Inserts an original boolean item, if it is not contained already. */
+ void InsertOrigBoolItem( bool bValue, const OUString& rText );
+
+ /** Inserts an item into the grouping item list. Does not change anything else.
+ @return The list index of the new item. */
+ sal_uInt16 InsertGroupItem( XclExpPCItem* pNewItem );
+ /** Generates and inserts all visible items for numeric or date grouping. */
+ void InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart = 0 );
+
+ /** Inserts the SXDOUBLE items that specify the limits for a numeric grouping. */
+ void SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo );
+ /** Inserts the SXDATETIME/SXINTEGER items that specify the limits for a date grouping.
+ @param bUseStep true = Insert the passed step value; false = always insert 1. */
+ void SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep );
+
+ /** Initializes flags and item count fields. */
+ void Finalize();
+
+ /** Writes an SXNUMGROUP record and the additional items for a numeric grouping field. */
+ void WriteSxnumgroup( XclExpStream& rStrm );
+ /** Writes an SXGROUPINFO record describing the item order in grouping fields. */
+ void WriteSxgroupinfo( XclExpStream& rStrm );
+
+ /** Writes the contents of the SXFIELD record for this field. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpPCItemList maOrigItemList; /// List with original items.
+ XclExpPCItemList maGroupItemList; /// List with grouping items.
+ ScfUInt16Vec maIndexVec; /// Indexes into maItemList.
+ XclExpPCItemList maNumGroupLimits; /// List with limit values for numeric grouping.
+ sal_uInt16 mnTypeFlags; /// Collected item data type flags.
+};
+
+class XclExpPivotCache : public salhelper::SimpleReferenceObject, protected XclExpRoot
+{
+public:
+ explicit XclExpPivotCache( const XclExpRoot& rRoot,
+ const ScDPObject& rDPObj, sal_uInt16 nListIdx );
+
+ /** Returns true, if the cache has been constructed successfully. */
+ bool IsValid() const { return mbValid; }
+ /** Returns true, if the item index list will be written. */
+ bool HasItemIndexList() const;
+
+ /** Returns the list index of the cache used in pivot table records. */
+ sal_uInt16 GetCacheIndex() const { return mnListIdx; }
+
+ /** Returns the number of pivot cache fields. */
+ sal_uInt16 GetFieldCount() const;
+ /** Returns the specified pivot cache field. */
+ const XclExpPCField* GetField( sal_uInt16 nFieldIdx ) const;
+ /** Returns true, if this pivot cache contains non-standard fields (e.g. grouping fields). */
+ bool HasAddFields() const;
+
+ /** Returns true, if the passed DP object has the same data source as this cache. */
+ bool HasEqualDataSource( const ScDPObject& rDPObj ) const;
+
+ /** Writes related records into Workbook stream and creates the pivot cache storage stream. */
+ void Save( XclExpStream& rStrm );
+ static void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ /** Adds all pivot cache fields. */
+ void AddFields( const ScDPObject& rDPObj );
+
+ /** Adds all standard pivot cache fields based on source data. */
+ void AddStdFields( const ScDPObject& rDPObj );
+ /** Adds all grouping pivot cache fields. */
+ void AddGroupFields( const ScDPObject& rDPObj );
+
+ /** Writes the DCONREF record containing the source range. */
+ void WriteDconref( XclExpStream& rStrm ) const;
+ /** DCONNAME record contains range name source. */
+ void WriteDConName( XclExpStream& rStrm ) const;
+
+ /** Creates the pivot cache storage stream and writes the cache. */
+ void WriteCacheStream();
+ /** Writes the SXDB record. */
+ void WriteSxdb( XclExpStream& rStrm ) const;
+ /** Writes the SXDBEX record. */
+ static void WriteSxdbex( XclExpStream& rStrm );
+ /** Writes the SXINDEXLIST record list containing the item index table. */
+ void WriteSxindexlistList( XclExpStream& rStrm ) const;
+
+private:
+ typedef XclExpRecordList< XclExpPCField > XclExpPCFieldList;
+ typedef XclExpPCFieldList::RecordRefType XclExpPCFieldRef;
+
+ XclPCInfo maPCInfo; /// Pivot cache settings (SXDB record).
+ XclExpPCFieldList maFieldList; /// List of all pivot cache fields.
+ OUString maTabName; /// Name of source data sheet.
+ OUString maSrcRangeName; /// Range name for source data.
+ ScRange maOrigSrcRange; /// The original sheet source range.
+ ScRange maExpSrcRange; /// The exported sheet source range.
+ ScRange maDocSrcRange; /// The range used to build the cache fields and items.
+ sal_uInt16 mnListIdx; /// List index in pivot cache buffer.
+ bool mbValid; /// true = The cache is valid for export.
+};
+
+typedef rtl::Reference<XclExpPivotCache> XclExpPivotCacheRef;
+
+// Pivot table
+
+class XclExpPivotTable;
+
+/** Data field position specifying the pivot table field index (first) and data info index (second). */
+typedef ::std::pair< sal_uInt16, sal_uInt16 > XclPTDataFieldPos;
+
+class XclExpPTItem : public XclExpRecord
+{
+public:
+ explicit XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx );
+ explicit XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx );
+
+ /** Returns the internal name of this item. */
+ OUString GetItemName() const;
+
+ /** Fills this item with properties from the passed save member. */
+ void SetPropertiesFromMember( const ScDPSaveMember& rSaveMem );
+
+private:
+ /** Writes the SXVI record body describing the pivot table item. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpPCItem* mpCacheItem; /// The referred pivot cache item.
+ XclPTItemInfo maItemInfo; /// General data for this item.
+};
+
+class XclExpPTField : public XclExpRecordBase
+{
+public:
+ explicit XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx );
+
+ // data access ------------------------------------------------------------
+
+ /** Returns the name of this field. */
+ OUString GetFieldName() const;
+ /** Returns the pivot table field list index of this field.
+ * The field index is always equal to cache index.
+ */
+ sal_uInt16 GetFieldIndex() const { return maFieldInfo.mnCacheIdx; }
+
+ /** Returns the index of the last inserted data info struct. */
+ sal_uInt16 GetLastDataInfoIndex() const;
+
+ /** Returns the list index of an item by its name.
+ @param nDefaultIdx This value will be returned, if the item could not be found. */
+ sal_uInt16 GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const;
+
+ // fill data --------------------------------------------------------------
+
+ /** Fills this field with row/column/page properties from the passed save dimension. */
+ void SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+ /** Fills this field with data field properties from the passed save dimension. */
+ void SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+
+ /** Appends special items describing the field subtotal entries. */
+ void AppendSubtotalItems();
+
+ // records ----------------------------------------------------------------
+
+ /** Writes an entry for an SXPI record containing own page field info. */
+ void WriteSxpiEntry( XclExpStream& rStrm ) const;
+ /** Writes an SXDI records containing info about a data field. */
+ void WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const;
+
+ /** Writes the entire pivot table field. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns an item by its name. */
+ XclExpPTItem* GetItemAcc( std::u16string_view rName );
+
+ /** Appends a special item describing a field subtotal entry. */
+ void AppendSubtotalItem( sal_uInt16 nItemType );
+
+ /** Writes the SXVD record introducing the field. */
+ void WriteSxvd( XclExpStream& rStrm ) const;
+ /** Writes the SXVDEX record containing additional settings. */
+ void WriteSxvdex( XclExpStream& rStrm ) const;
+
+private:
+
+ const XclExpPivotTable& mrPTable; /// Parent pivot table containing this field.
+ const XclExpPCField* mpCacheField; /// The referred pivot cache field.
+ XclPTFieldInfo maFieldInfo; /// General field info (SXVD record).
+ XclPTFieldExtInfo maFieldExtInfo; /// Extended field info (SXVDEX record).
+ XclPTPageFieldInfo maPageInfo; /// Page field info (entry in SXPI record).
+ std::vector< XclPTDataFieldInfo >
+ maDataInfoVec; /// List of extended data field info (SXDI records).
+ XclExpRecordList< XclExpPTItem >
+ maItemList; /// List of all items of this field.
+};
+
+class XclExpPivotTable : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpPivotTable( const XclExpRoot& rRoot,
+ const ScDPObject& rDPObj, const XclExpPivotCache& rPCache );
+
+ /** Returns a pivot cache field. */
+ const XclExpPCField* GetCacheField( sal_uInt16 nCacheIdx ) const;
+
+ /** Returns the output range of the pivot table. */
+ SCTAB GetScTab() const { return mnOutScTab; }
+
+ /** Returns a pivot table field by its name. */
+ const XclExpPTField* GetField( sal_uInt16 nFieldIdx ) const;
+ /** Returns a pivot table field by its name. */
+ const XclExpPTField* GetField( std::u16string_view rName ) const;
+
+ /** Returns the data-field-only index of the first data field with the passed name.
+ @param nDefaultIdx This value will be returned, if the field could not be found. */
+ sal_uInt16 GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const;
+
+ /** Writes the entire pivot table. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns a pivot table field by its name. */
+ XclExpPTField* GetFieldAcc( std::u16string_view rName );
+ /** Returns a pivot table field corresponding to the passed save dimension. */
+ XclExpPTField* GetFieldAcc( const ScDPSaveDimension& rSaveDim );
+
+ // fill data --------------------------------------------------------------
+
+ /** Fills internal members with all properties from the passed save data. */
+ void SetPropertiesFromDP( const ScDPSaveData& rSaveData );
+ /** Fills a pivot table field with all properties from the passed save dimension. */
+ void SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+ /** Fills a pivot table data field with all properties from the passed save dimension. */
+ void SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+
+ /** Initializes any data after processing the entire source DataPilot. */
+ void Finalize();
+
+ // records ----------------------------------------------------------------
+
+ /** Writes the SXVIEW record starting the pivot table. */
+ void WriteSxview( XclExpStream& rStrm ) const;
+ /** Writes an SXIVD record for row field or column field order. */
+ static void WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields );
+ /** Writes the SXPI record containing page field info. */
+ void WriteSxpi( XclExpStream& rStrm ) const;
+ /** Writes all SXDI records containing info about the data fields. */
+ void WriteSxdiList( XclExpStream& rStrm ) const;
+ /** Writes a dummy SXLI records containing item layout info. */
+ static void WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount );
+ /** Writes the SXEX records containing additional pivot table info. */
+ void WriteSxex( XclExpStream& rStrm ) const;
+
+ void WriteQsiSxTag( XclExpStream& rStrm ) const;
+ /** Writes the SX_AUTOFORMAT records with the autoformat id and header layout */
+ void WriteSxViewEx9( XclExpStream& rStrm ) const;
+
+private:
+ typedef XclExpRecordList< XclExpPTField > XclExpPTFieldList;
+ typedef XclExpPTFieldList::RecordRefType XclExpPTFieldRef;
+
+ const XclExpPivotCache& mrPCache; /// The pivot cache this pivot table bases on.
+ XclPTInfo maPTInfo; /// Info about the pivot table (SXVIEW record).
+ XclPTExtInfo maPTExtInfo; /// Extended info about the pivot table (SXEX record).
+ XclPTViewEx9Info maPTViewEx9Info; /// The selected autoformat (SXVIEWEX9)
+ XclExpPTFieldList maFieldList; /// All fields in pivot cache order.
+ ScfUInt16Vec maRowFields; /// Row field indexes.
+ ScfUInt16Vec maColFields; /// Column field indexes.
+ ScfUInt16Vec maPageFields; /// Page field indexes.
+ std::vector< XclPTDataFieldPos >
+ maDataFields; /// Data field indexes.
+ XclExpPTField maDataOrientField; /// Special data field orientation field.
+ SCTAB mnOutScTab; /// Sheet index of the output range.
+ bool mbValid; /// true = The pivot table is valid for export.
+ bool mbFilterBtn; /// true = DataPilot has filter button.
+};
+
+/** The main class for pivot table export.
+
+ This class contains all pivot caches and pivot tables in a Calc document.
+ It creates the pivot cache streams and pivot table records in the main
+ workbook stream. It supports sharing of pivot caches between multiple pivot
+ tables to decrease file size.
+ */
+class XclExpPivotTableManager : protected XclExpRoot
+{
+public:
+ explicit XclExpPivotTableManager( const XclExpRoot& rRoot );
+
+ /** Creates all pivot tables and caches from the Calc DataPilot objects. */
+ void CreatePivotTables();
+
+ /** Creates a record wrapper for exporting all pivot caches. */
+ XclExpRecordRef CreatePivotCachesRecord();
+ /** Creates a record wrapper for exporting all pivot tables of the specified sheet. */
+ XclExpRecordRef CreatePivotTablesRecord( SCTAB nScTab );
+
+ /** Writes all pivot caches (all Workbook records and cache streams). */
+ void WritePivotCaches( XclExpStream& rStrm );
+ /** Writes all pivot tables of the specified Calc sheet. */
+ void WritePivotTables( XclExpStream& rStrm, SCTAB nScTab );
+
+private:
+ /** Finds an existing (if enabled in mbShareCaches) or creates a new pivot cache.
+ @return Pointer to the pivot cache or 0, if the passed source range was invalid. */
+ const XclExpPivotCache* CreatePivotCache( const ScDPObject& rDPObj );
+
+private:
+ typedef XclExpRecordList< XclExpPivotTable > XclExpPivotTableList;
+ typedef XclExpPivotTableList::RecordRefType XclExpPivotTableRef;
+
+ XclExpRecordList< XclExpPivotCache > maPCacheList; /// List of all pivot caches.
+ XclExpPivotTableList maPTableList; /// List of all pivot tables.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xepivotxml.hxx b/sc/source/filter/inc/xepivotxml.hxx
new file mode 100644
index 0000000000..30fb25a30f
--- /dev/null
+++ b/sc/source/filter/inc/xepivotxml.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/.
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+
+#include <memory>
+#include <map>
+#include <unordered_map>
+
+class ScDPCache;
+class ScDPObject;
+
+class XclExpXmlPivotCaches : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ struct Entry
+ {
+ const ScDPCache* mpCache;
+ ScRange maSrcRange;
+ };
+
+ XclExpXmlPivotCaches(const XclExpRoot& rRoot);
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+ void SetCaches(std::vector<Entry>&& rCaches);
+ bool HasCaches() const;
+ const Entry* GetCache(sal_Int32 nCacheId) const;
+
+private:
+ void SavePivotCacheXml(XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCacheId);
+
+private:
+ std::vector<Entry> maCaches;
+};
+
+class XclExpXmlPivotTables : public XclExpRecordBase, protected XclExpRoot
+{
+ struct Entry
+ {
+ const ScDPObject* mpTable;
+ sal_Int32 mnCacheId;
+ sal_Int32 mnPivotId; /// used as [n] in pivotTable[n].xml part name.
+
+ Entry(const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId);
+ };
+
+ const XclExpXmlPivotCaches& mrCaches;
+ typedef std::vector<Entry> TablesType;
+ TablesType maTables;
+
+public:
+ XclExpXmlPivotTables(const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches);
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+ void AppendTable(const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId);
+
+private:
+ void SavePivotTableXml(XclExpXmlStream& rStrm, const ScDPObject& rObj, sal_Int32 nCacheId);
+};
+
+class XclExpXmlPivotTableManager : protected XclExpRoot
+{
+ typedef std::map<SCTAB, std::unique_ptr<XclExpXmlPivotTables>> TablesType;
+ typedef std::unordered_map<const ScDPObject*, sal_Int32> CacheIdMapType;
+
+public:
+ XclExpXmlPivotTableManager(const XclExpRoot& rRoot);
+
+ void Initialize();
+
+ XclExpXmlPivotCaches& GetCaches();
+ XclExpXmlPivotTables* GetTablesBySheet(SCTAB nTab);
+
+private:
+ XclExpXmlPivotCaches maCaches;
+ TablesType m_Tables;
+ CacheIdMapType maCacheIdMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xerecord.hxx b/sc/source/filter/inc/xerecord.hxx
new file mode 100644
index 0000000000..a05ea97cbb
--- /dev/null
+++ b/sc/source/filter/inc/xerecord.hxx
@@ -0,0 +1,419 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "xlconst.hxx"
+#include "xestream.hxx"
+#include "xlstream.hxx"
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ref.hxx>
+#include <algorithm>
+
+// Base classes to export Excel records =======================================
+
+/** Base class for all Excel records.
+
+ Derive from this class to implement any functionality performed during
+ saving the records - except really writing a record (i.e. write a list of
+ records contained in the class). Derive from XclExpRecord (instead from
+ this class) to write common records.
+ */
+class XclExpRecordBase : public salhelper::SimpleReferenceObject
+{
+public:
+ XclExpRecordBase() {}
+
+ // this class is stored both ref-counted and by value
+ XclExpRecordBase(XclExpRecordBase const &)
+ : salhelper::SimpleReferenceObject() {}
+ XclExpRecordBase(XclExpRecordBase &&)
+ : salhelper::SimpleReferenceObject() {}
+ XclExpRecordBase& operator=(XclExpRecordBase const &)
+ { return *this; }
+ XclExpRecordBase& operator=(XclExpRecordBase &&) noexcept
+ { return *this; }
+
+ virtual ~XclExpRecordBase();
+
+ /** Overwrite this method to do any operation while saving the record. */
+ virtual void Save( XclExpStream& rStrm );
+ virtual void SaveXml( XclExpXmlStream& rStrm );
+};
+
+/*namespace std
+{
+ template<typename T,
+ typename = typename std::enable_if<
+ std::is_base_of<XclExpRecordBase,T>::value
+ >::type>
+ class shared_ptr
+ {
+ shared_ptr() {}
+ };
+};*/
+
+class XclExpDelegatingRecord : public XclExpRecordBase
+{
+public:
+ XclExpDelegatingRecord( XclExpRecordBase* pRecord );
+ virtual ~XclExpDelegatingRecord() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ XclExpRecordBase* mpRecord;
+};
+
+class XclExpXmlElementRecord : public XclExpRecordBase
+{
+public:
+ explicit XclExpXmlElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlElementRecord() override;
+
+protected:
+ sal_Int32 mnElement;
+};
+
+class XclExpXmlStartElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlStartElementRecord() override;
+
+ /** Starts the element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlEndElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlEndElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlEndElementRecord() override;
+
+ /** Ends the element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlStartSingleElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartSingleElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlStartSingleElementRecord() override;
+
+ /** Starts the single element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlEndSingleElementRecord : public XclExpRecordBase
+{
+public:
+ XclExpXmlEndSingleElementRecord();
+ virtual ~XclExpXmlEndSingleElementRecord() override;
+
+ /** Ends the single element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/** Base class for single records with any content.
+
+ This class handles writing the record header. Derived classes only have to
+ write the record body. Calculating the record size before saving optimizes
+ the write process (the stream does not have to seek back and update the
+ written record size). But it is not required to calculate a valid size
+ (maybe it would be too complex or just impossible until the record is
+ really written).
+ */
+class XclExpRecord : public XclExpRecordBase
+{
+public:
+ /** @param nRecId The record ID of this record. May be set later with SetRecId().
+ @param nRecSize The predicted record size. May be set later with SetRecSize(). */
+ explicit XclExpRecord(
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN,
+ std::size_t nRecSize = 0 );
+
+ XclExpRecord(XclExpRecord const &) = default;
+
+ virtual ~XclExpRecord() override;
+
+ /** Returns the current record ID. */
+ sal_uInt16 GetRecId() const { return mnRecId; }
+ /** Returns the current record size prediction. */
+ std::size_t GetRecSize() const { return mnRecSize; }
+
+ /** Sets a new record ID. */
+ void SetRecId( sal_uInt16 nRecId ) { mnRecId = nRecId; }
+ /** Sets a new record size prediction. */
+ void SetRecSize( std::size_t nRecSize ) { mnRecSize = nRecSize; }
+ /** Adds a size value to the record size prediction. */
+ void AddRecSize( std::size_t nRecSize ) { mnRecSize += nRecSize; }
+ /** Sets record ID and size with one call. */
+ void SetRecHeader( sal_uInt16 nRecId, std::size_t nRecSize );
+
+ /** Writes the record header and calls WriteBody(). */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+protected:
+ /** Writes the body of the record (without record header).
+ @descr Usually this method will be overwritten by derived classes. */
+ virtual void WriteBody( XclExpStream& rStrm );
+
+private:
+ std::size_t mnRecSize; /// The predicted record size.
+ sal_uInt16 mnRecId; /// The record ID.
+};
+
+/** A record without body. Only the record ID and the size 0 will be written. */
+class XclExpEmptyRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record. */
+ inline explicit XclExpEmptyRecord( sal_uInt16 nRecId );
+};
+
+inline XclExpEmptyRecord::XclExpEmptyRecord( sal_uInt16 nRecId ) :
+ XclExpRecord( nRecId, 0 )
+{
+}
+
+/** A record with a single value of type Type.
+ @descr Requires operator<<( XclExpStream&, const Type& ). */
+template< typename Type >
+class XclExpValueRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record.
+ @param rValue The value for the record body.
+ @param nSize Record size. Uses sizeof( Type ), if this parameter is omitted. */
+ explicit XclExpValueRecord( sal_uInt16 nRecId, const Type& rValue, std::size_t nSize = sizeof( Type ) ) :
+ XclExpRecord( nRecId, nSize ), maValue( rValue ), mnAttribute( -1 ) {}
+
+ /** Returns the value of the record. */
+ const Type& GetValue() const { return maValue; }
+ /** Sets a new record value. */
+ void SetValue( const Type& rValue ) { maValue = rValue; }
+
+ /** Sets the OOXML attribute this record corresponds to */
+ XclExpValueRecord* SetAttribute( sal_Int32 nId );
+
+ /** Write the OOXML attribute and its value */
+ void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override { rStrm << maValue; }
+ // inlining prevents warning in wntmsci10
+
+private:
+ Type maValue; /// The record data.
+ sal_Int32 mnAttribute; /// The OOXML attribute Id
+};
+
+template< typename Type >
+void XclExpValueRecord< Type >::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+ rStrm.WriteAttributes(mnAttribute, OUString::number(maValue));
+}
+
+template<>
+void XclExpValueRecord<double>::SaveXml( XclExpXmlStream& rStrm );
+
+template< typename Type >
+XclExpValueRecord< Type >* XclExpValueRecord< Type >::SetAttribute( sal_Int32 nId )
+{
+ mnAttribute = nId;
+ return this;
+}
+
+/** A record containing an unsigned 16-bit value. */
+typedef XclExpValueRecord< sal_uInt16 > XclExpUInt16Record;
+
+/** A record containing a double value. */
+typedef XclExpValueRecord< double > XclExpDoubleRecord;
+
+/** Record which contains a Boolean value.
+ @descr The value is stored as 16-bit value: 0x0000 = sal_False, 0x0001 = TRUE. */
+class XclExpBoolRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record.
+ @param nValue The value for the record body. */
+ explicit XclExpBoolRecord( sal_uInt16 nRecId, bool bValue, sal_Int32 nAttribute = -1 ) :
+ XclExpRecord( nRecId, 2 ), mbValue( bValue ), mnAttribute( nAttribute ) {}
+
+ /** Returns the Boolean value of the record. */
+ bool GetBool() const { return mbValue; }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ bool mbValue; /// The record data.
+ sal_Int32 mnAttribute; /// The attribute to generate within SaveXml()
+};
+
+/** Record which exports a memory data array. */
+class XclExpDummyRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record.
+ @param pRecData Pointer to the data array representing the record body.
+ @param nRecSize Size of the data array. */
+ explicit XclExpDummyRecord(
+ sal_uInt16 nRecId, const void* pRecData, std::size_t nRecSize );
+
+ /** Sets a data array. */
+ void SetData( const void* pRecData, std::size_t nRecSize );
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const void* mpData; /// The record data.
+};
+
+// Future records =============================================================
+
+class XclExpFutureRecord : public XclExpRecord
+{
+public:
+ explicit XclExpFutureRecord( XclFutureRecType eRecType,
+ sal_uInt16 nRecId, std::size_t nRecSize );
+
+ /** Writes the extended record header and calls WriteBody(). */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ XclFutureRecType meRecType;
+};
+
+// List of records ============================================================
+
+/** A list of Excel record objects.
+
+ Provides saving the compete list. This class is derived from
+ XclExpRecordBase, so it can be used as record in another record list.
+ Requires RecType::Save( XclExpStream& ).
+ */
+template< typename RecType = XclExpRecordBase >
+class XclExpRecordList : public XclExpRecordBase
+{
+public:
+ typedef rtl::Reference< RecType > RecordRefType;
+
+ bool IsEmpty() const { return maRecs.empty(); }
+ size_t GetSize() const { return maRecs.size(); }
+
+ /** Returns true, if the passed index points to an exiting record. */
+ bool HasRecord( size_t nPos ) const
+ { return nPos < maRecs.size(); }
+ /** Returns reference to an existing record or empty reference on error. */
+ RecType* GetRecord( size_t nPos ) const
+ { return nPos < maRecs.size() ? maRecs[ nPos ].get() : nullptr; }
+ /** Returns reference to the first existing record or empty reference, if list is empty. */
+ RecType* GetFirstRecord() const
+ { return maRecs.empty() ? nullptr : maRecs.front().get(); }
+ /** Returns reference to the last existing record or empty reference, if list is empty. */
+ RecType* GetLastRecord() const
+ { return maRecs.empty() ? nullptr : maRecs.back().get(); }
+
+ /** Inserts a record at the specified position into the list. */
+ void InsertRecord( RecType* pRec, size_t nPos )
+ { assert(pRec); maRecs.insert( maRecs.begin() + ::std::min( nPos, maRecs.size() ), pRec ); }
+ void InsertRecord( RecordRefType pRec, size_t nPos )
+ { assert(pRec); maRecs.insert( maRecs.begin() + ::std::min( nPos, maRecs.size() ), std::move(pRec) ); }
+ /** Appends a record to the list. */
+ void AppendRecord( RecType* pRec )
+ { if (pRec) maRecs.push_back( pRec ); }
+ void AppendRecord( const RecordRefType& xRec )
+ { if (xRec) maRecs.push_back( xRec.get() ); }
+ void AppendRecord( RecordRefType xRec ) &&
+ { if (xRec) maRecs.push_back( std::move(xRec) ); }
+ /** Replaces the record at the specified position from the list with the passed record. */
+ void ReplaceRecord( RecType* pRec, size_t nPos )
+ { if (pRec) maRecs[nPos] = pRec; else RemoveRecord( nPos ); }
+ void ReplaceRecord( RecordRefType const & xRec, size_t nPos )
+ { ReplaceRecord(xRec.get(), nPos); }
+
+ /** Appends a newly created record to the list. */
+ void AppendNewRecord( RecType* pRec )
+ { assert(pRec); maRecs.push_back( pRec ); }
+ void AppendNewRecord( RecordRefType const & xRec )
+ { AppendNewRecord(xRec.get()); }
+ void AppendNewRecord( RecordRefType xRec ) &&
+ { assert(xRec); maRecs.append(std::move(xRec)); }
+
+ /** Removes the record at the specified position from the list. */
+ void RemoveRecord( size_t nPos )
+ { maRecs.erase( maRecs.begin() + nPos ); }
+ /** Removes all records from the list. */
+ void RemoveAllRecords() { maRecs.clear(); }
+
+ /** Writes the complete record list. */
+ virtual void Save( XclExpStream& rStrm ) override
+ {
+ // inlining prevents warning in wntmsci10
+ for( typename RecordVec::iterator aIt = maRecs.begin(), aEnd = maRecs.end(); aIt != aEnd; ++aIt )
+ (*aIt)->Save( rStrm );
+ }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override
+ {
+ // inlining prevents warning in wntmsci10
+ for( typename RecordVec::iterator aIt = maRecs.begin(), aEnd = maRecs.end(); aIt != aEnd; ++aIt )
+ (*aIt)->SaveXml( rStrm );
+ }
+
+ /**
+ Optimization for repeated removal. Since this is internally a vector, repeated RemoveRecord()
+ would repeatedly move all items after the removed position. Instead it's possible to invalidate
+ a record and then call RemoveInvalidatedRecords() at the end (which is necessary, the list is generally
+ not allowed to contain invalid entries).
+ */
+ void InvalidateRecord( size_t nPos ) { maRecs[ nPos ] = nullptr; }
+ void RemoveInvalidatedRecords()
+ {
+ std::erase_if(maRecs, [](const RecordRefType& xRec) { return xRec == nullptr; } );
+ }
+
+private:
+ typedef ::std::vector< RecordRefType > RecordVec;
+ RecordVec maRecs;
+};
+
+/** Represents a complete substream of records enclosed into a pair of BOF/EOF records. */
+class XclExpSubStream : public XclExpRecordList<>
+{
+public:
+ explicit XclExpSubStream( sal_uInt16 nSubStrmType );
+
+ /** Writes the complete substream, including leading BOF and trailing EOF. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnSubStrmType; /// Substream type, stored in leading BOF record.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeroot.hxx b/sc/source/filter/inc/xeroot.hxx
new file mode 100644
index 0000000000..421a389a37
--- /dev/null
+++ b/sc/source/filter/inc/xeroot.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlroot.hxx"
+#include <compiler.hxx>
+#include <memory>
+#include <rtl/ref.hxx>
+
+// Forward declarations of objects in public use ==============================
+
+namespace com::sun::star::beans { struct NamedValue; }
+
+class XclExpRecordBase;
+class XclExpString;
+
+typedef rtl::Reference< XclExpRecordBase > XclExpRecordRef;
+typedef std::shared_ptr< XclExpString > XclExpStringRef;
+
+// Global data ================================================================
+
+class XclExpTabInfo;
+class XclExpAddressConverter;
+class XclExpFormulaCompiler;
+class XclExpProgressBar;
+class XclExpSst;
+class XclExpPalette;
+class XclExpFontBuffer;
+class XclExpNumFmtBuffer;
+class XclExpXFBuffer;
+class XclExpLinkManager;
+class XclExpNameManager;
+class XclExpObjectManager;
+class XclExpFilterManager;
+class XclExpPivotTableManager;
+class XclExpDxfs;
+class XclExpXmlPivotTableManager;
+class XclExpTablesManager;
+namespace sc { class CompileFormulaContext; }
+
+/** Stores global buffers and data needed for Excel export filter. */
+struct XclExpRootData : public XclRootData
+{
+ typedef std::shared_ptr< XclExpTabInfo > XclExpTabInfoRef;
+ typedef std::shared_ptr< XclExpAddressConverter > XclExpAddrConvRef;
+ typedef std::shared_ptr< XclExpFormulaCompiler > XclExpFmlaCompRef;
+ typedef std::shared_ptr< XclExpProgressBar > XclExpProgressRef;
+
+ typedef rtl::Reference< XclExpSst > XclExpSstRef;
+ typedef rtl::Reference< XclExpPalette > XclExpPaletteRef;
+ typedef rtl::Reference< XclExpFontBuffer > XclExpFontBfrRef;
+ typedef rtl::Reference< XclExpNumFmtBuffer > XclExpNumFmtBfrRef;
+ typedef rtl::Reference< XclExpXFBuffer > XclExpXFBfrRef;
+ typedef rtl::Reference< XclExpNameManager > XclExpNameMgrRef;
+ typedef rtl::Reference< XclExpLinkManager > XclExpLinkMgrRef;
+ typedef std::shared_ptr< XclExpObjectManager > XclExpObjectMgrRef;
+ typedef std::shared_ptr< XclExpFilterManager > XclExpFilterMgrRef;
+ typedef std::shared_ptr< XclExpPivotTableManager > XclExpPTableMgrRef;
+ typedef rtl::Reference< XclExpDxfs > XclExpDxfsRef;
+
+ XclExpTabInfoRef mxTabInfo; /// Calc->Excel sheet index conversion.
+ XclExpAddrConvRef mxAddrConv; /// The address converter.
+ XclExpFmlaCompRef mxFmlaComp; /// The formula compiler.
+ XclExpProgressRef mxProgress; /// The export progress bar.
+
+ XclExpSstRef mxSst; /// The shared string table.
+ XclExpPaletteRef mxPalette; /// The color buffer.
+ XclExpFontBfrRef mxFontBfr; /// All fonts in the file.
+ XclExpNumFmtBfrRef mxNumFmtBfr; /// All number formats in the file.
+ XclExpXFBfrRef mxXFBfr; /// All XF records in the file.
+ XclExpNameMgrRef mxNameMgr; /// Internal defined names.
+ XclExpLinkMgrRef mxGlobLinkMgr; /// Global link manager for defined names.
+ XclExpLinkMgrRef mxLocLinkMgr; /// Local link manager for a sheet.
+ XclExpObjectMgrRef mxObjMgr; /// All drawing objects.
+ XclExpFilterMgrRef mxFilterMgr; /// Manager for filtered areas in all sheets.
+ XclExpPTableMgrRef mxPTableMgr; /// All pivot tables and pivot caches.
+ XclExpDxfsRef mxDxfs; /// All delta formatting entries
+
+ std::shared_ptr<XclExpXmlPivotTableManager> mxXmlPTableMgr;
+ std::shared_ptr<XclExpTablesManager> mxTablesMgr;
+ std::shared_ptr<sc::CompileFormulaContext> mpCompileFormulaCxt;
+
+ ScCompiler::OpCodeMapPtr mxOpCodeMap; /// mapping between op-codes and names
+
+ bool mbRelUrl; /// true = Store URLs relative.
+
+ OStringBuffer maStringBuf; /// buffer to avoid massive OUString allocations
+
+ explicit XclExpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc );
+ virtual ~XclExpRootData() override;
+};
+
+/** Access to global data from other classes. */
+class XclExpRoot : public XclRoot
+{
+public:
+ explicit XclExpRoot( XclExpRootData& rExpRootData );
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclExpRoot& GetRoot() const { return *this; }
+ /** Returns true, if URLs should be stored relative to the document location. */
+ bool IsRelUrl() const { return mrExpData.mbRelUrl; }
+ sc::CompileFormulaContext& GetCompileFormulaContext() const { return *mrExpData.mpCompileFormulaCxt; }
+
+ /** Returns the buffer for Calc->Excel sheet index conversion. */
+ XclExpTabInfo& GetTabInfo() const;
+ /** Returns the address converter. */
+ XclExpAddressConverter& GetAddressConverter() const;
+ /** Returns the formula compiler to produce formula token arrays. */
+ XclExpFormulaCompiler& GetFormulaCompiler() const;
+ /** Returns the export progress bar. */
+ XclExpProgressBar& GetProgressBar() const;
+
+ /** Returns the shared string table. */
+ XclExpSst& GetSst() const;
+ /** Returns the color buffer. */
+ XclExpPalette& GetPalette() const;
+ /** Returns the font buffer. */
+ XclExpFontBuffer& GetFontBuffer() const;
+ /** Returns the number format buffer. */
+ XclExpNumFmtBuffer& GetNumFmtBuffer() const;
+ /** Returns the cell formatting attributes buffer. */
+ XclExpXFBuffer& GetXFBuffer() const;
+ /** Returns the global link manager for defined names. */
+ XclExpLinkManager& GetGlobalLinkManager() const;
+ /** Returns the local link manager for the current sheet. */
+ XclExpLinkManager& GetLocalLinkManager() const;
+ /** Returns the buffer that contains internal defined names. */
+ XclExpNameManager& GetNameManager() const;
+ /** Returns the drawing object manager. */
+ XclExpObjectManager& GetObjectManager() const;
+ /** Returns the filter manager. */
+ XclExpFilterManager& GetFilterManager() const;
+ /** Returns the pivot table manager. */
+ XclExpPivotTableManager& GetPivotTableManager() const;
+ /** Returns the differential formatting list */
+ XclExpDxfs& GetDxfs() const;
+
+ /** Clean and return the OStringBuffer */
+ OStringBuffer& GetStringBuf() const { mrExpData.maStringBuf.setLength(0); return mrExpData.maStringBuf; }
+
+ XclExpXmlPivotTableManager& GetXmlPivotTableManager();
+
+ XclExpTablesManager& GetTablesManager();
+
+ /** Is called when export filter starts to create the Excel document (all BIFF versions). */
+ void InitializeConvert();
+ /** Is called when export filter starts to create the workbook global data (>=BIFF5). */
+ void InitializeGlobals();
+ /** Is called when export filter starts to create data for a single sheet (all BIFF versions). */
+ void InitializeTable( SCTAB nScTab );
+ /** Is called before export filter starts to write the records to the stream. */
+ void InitializeSave();
+ /** Returns the reference to a record (or record list) representing a root object.
+ @param nRecId Identifier that specifies which record is returned. */
+ XclExpRecordRef CreateRecord( sal_uInt16 nRecId ) const;
+
+ bool IsDocumentEncrypted() const;
+
+ static css::uno::Sequence< css::beans::NamedValue > GenerateEncryptionData( std::u16string_view aPass );
+ css::uno::Sequence< css::beans::NamedValue > GetEncryptionData() const;
+ static css::uno::Sequence< css::beans::NamedValue > GenerateDefaultEncryptionData();
+
+private:
+
+ /** Returns the local or global link manager, depending on current context. */
+ XclExpRootData::XclExpLinkMgrRef const & GetLocalLinkMgrRef() const;
+
+private:
+ XclExpRootData& mrExpData; /// Reference to the global export data struct.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xestream.hxx b/sc/source/filter/inc/xestream.hxx
new file mode 100644
index 0000000000..669dadaec0
--- /dev/null
+++ b/sc/source/filter/inc/xestream.hxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <stack>
+#include <string_view>
+
+#include <rtl/strbuf.hxx>
+
+#include <oox/core/xmlfilterbase.hxx>
+#include <sax/fshelper.hxx>
+#include <tools/stream.hxx>
+#include <formula/errorcodes.hxx>
+#include "ftools.hxx"
+#include <types.hxx>
+
+#include <filter/msfilter/mscodec.hxx>
+#include <vector>
+
+namespace com::sun::star::beans { struct NamedValue; }
+
+/* ============================================================================
+Output stream class for Excel export
+- CONTINUE record handling
+============================================================================ */
+
+class XclExpString;
+class XclExpRoot;
+class XclExpBiff8Encrypter;
+typedef std::shared_ptr< XclExpBiff8Encrypter > XclExpEncrypterRef;
+
+/** This class is used to export Excel record streams.
+ @descr An instance is constructed with an SvStream and the maximum size of Excel
+ record contents (in BIFF5: 2080 bytes, in BIFF8: 8224 bytes).
+
+ To start writing a record call StartRecord(). Parameters are the record identifier
+ and any calculated record size. This is for optimizing the write process: if the real
+ written data has the same size as the calculated, the stream will not seek back and
+ update the record size field. But it is not mandatory to calculate a size. Each
+ record must be closed by calling EndRecord(). This will check (and update) the record
+ size field.
+
+ If some data exceeds the record size limit, a CONTINUE record is started automatically
+ and the new data will be written to this record.
+
+ If specific data pieces must not be split, use SetSliceSize(). For instance:
+ To write a sequence of 16-bit values, where 4 values form a unit and cannot be
+ split, call SetSliceSize( 8 ) first (4*2 bytes == 8).
+
+ To write unicode character arrays, call WriteUnicodeBuffer(). It creates CONTINUE
+ records and repeats the unicode string flag byte automatically. This function is used
+ for instance from the class XclExpString which can write complete unicode strings.
+*/
+class XclExpStream
+{
+public:
+ /** Constructs the Excel record export stream.
+ @param rOutStrm The system output stream to write to.
+ @param nMaxRecSize The maximum allowed size of record content (depending on BIFF type).
+ If 0 is passed, the record size will be set automatically, depending on the current BIFF type. */
+ XclExpStream(
+ SvStream& rOutStrm,
+ const XclExpRoot& rRoot,
+ sal_uInt16 nMaxRecSize = 0 );
+
+ ~XclExpStream();
+
+ /** Returns the filter root data. */
+ const XclExpRoot& GetRoot() const { return mrRoot; }
+
+ /** Starts a new record: writes header data, stores calculated record size. */
+ void StartRecord( sal_uInt16 nRecId, std::size_t nRecSize );
+ /** Checks and corrects real record length. Must be called every time a record is finished. */
+ void EndRecord();
+
+ /** Returns the position inside of current record (starts by 0 in every CONTINUE). */
+ sal_uInt16 GetRawRecPos() const { return mnCurrSize; }
+
+ /** Sets data slice length. 0 = no slices. */
+ void SetSliceSize( sal_uInt16 nSize );
+
+ XclExpStream& operator<<( sal_Int8 nValue );
+ XclExpStream& operator<<( sal_uInt8 nValue );
+ XclExpStream& operator<<( sal_Int16 nValue );
+ XclExpStream& operator<<( sal_uInt16 nValue );
+ XclExpStream& operator<<( sal_Int32 nValue );
+ XclExpStream& operator<<( sal_uInt32 nValue );
+ XclExpStream& operator<<( float fValue );
+ XclExpStream& operator<<( double fValue );
+
+ /** Writes nBytes bytes from memory. */
+ std::size_t Write( const void* pData, std::size_t nBytes );
+ /** Writes a sequence of nBytes zero bytes (respects slice setting). */
+ void WriteZeroBytes( std::size_t nBytes );
+
+ void WriteZeroBytesToRecord( std::size_t nBytes );
+
+ /** Copies nBytes bytes from current position of the stream rInStrm.
+ @descr Omitting the second parameter means: read to end of stream. */
+ void CopyFromStream( SvStream& rInStrm, sal_uInt64 nBytes = STREAM_SEEK_TO_END );
+
+ // *** unicode string export is realized with helper class XclExpString ***
+ // (slice length setting has no effect here -> disabled automatically)
+
+ /** Writes Unicode buffer as 8/16 bit, repeats nFlags at start of a CONTINUE record. */
+ void WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags );
+
+ // *** write 8-bit-strings ***
+ // (slice length setting has no effect here -> disabled automatically)
+
+ /** Writes string length field and OString buffer. */
+ void WriteByteString( const OString& rString );
+
+ /** Writes 8-bit character buffer. */
+ void WriteCharBuffer( const ScfUInt8Vec& rBuffer );
+
+ // *** SvStream access ***
+
+ /** Sets position of system stream (only allowed outside of records). */
+ void SetSvStreamPos(sal_uInt64 nPos);
+ /** Returns the absolute position of the system stream. */
+ sal_uInt64 GetSvStreamPos() const { return mrStrm.Tell(); }
+
+ void SetEncrypter( XclExpEncrypterRef const & xEncrypter );
+
+ bool HasValidEncrypter() const;
+
+ void EnableEncryption( bool bEnable = true );
+
+ void DisableEncryption();
+
+private:
+ /** Writes header data, internal setup. */
+ void InitRecord( sal_uInt16 nRecId );
+ /** Rewrites correct record length, if different from calculated. */
+ void UpdateRecSize();
+ /** Recalculates mnCurrSize and mnSliceSize. */
+ void UpdateSizeVars( std::size_t nSize );
+ /** Writes CONTINUE header, internal setup. */
+ void StartContinue();
+ /** Refreshes counter vars, creates CONTINUE records. */
+ void PrepareWrite( sal_uInt16 nSize );
+ /** Creates CONTINUE record at end of record.
+ @return Maximum data block size remaining. */
+ sal_uInt16 PrepareWrite();
+
+ /** Writes a raw sequence of zero bytes. */
+ void WriteRawZeroBytes( std::size_t nBytes );
+
+private:
+ SvStream& mrStrm; /// Reference to the system output stream.
+ const XclExpRoot& mrRoot; /// Filter root data.
+
+ bool mbUseEncrypter;
+ XclExpEncrypterRef mxEncrypter;
+
+ // length data
+ sal_uInt16 mnMaxRecSize; /// Maximum size of record content.
+ sal_uInt16 mnMaxContSize; /// Maximum size of CONTINUE content.
+ sal_uInt16 mnCurrMaxSize; /// Current maximum, either mnMaxRecSize or mnMaxContSize.
+ sal_uInt16 mnMaxSliceSize; /// Maximum size of data slices (parts that cannot be split).
+ sal_uInt16 mnHeaderSize; /// Record size written in last record header.
+ sal_uInt16 mnCurrSize; /// Count of bytes already written in current record.
+ sal_uInt16 mnSliceSize; /// Count of bytes already written in current slice.
+ std::size_t mnPredictSize; /// Predicted size received from calling function.
+
+ // stream position data
+ std::size_t mnLastSizePos; /// Stream position of size field in current header.
+ bool mbInRec; /// true = currently writing inside of a record.
+};
+
+class XclExpBiff8Encrypter
+{
+public:
+ explicit XclExpBiff8Encrypter( const XclExpRoot& rRoot );
+ ~XclExpBiff8Encrypter();
+
+ bool IsValid() const { return mbValid; }
+
+ void GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const;
+ void GetSalt( sal_uInt8 pnSalt[16] ) const;
+ void GetDocId( sal_uInt8 pnDocId[16] ) const;
+
+ void Encrypt( SvStream& rStrm, sal_uInt8 nData );
+ void Encrypt( SvStream& rStrm, sal_uInt16 nData );
+ void Encrypt( SvStream& rStrm, sal_uInt32 nData );
+
+ void Encrypt( SvStream& rStrm, sal_Int8 nData );
+ void Encrypt( SvStream& rStrm, sal_Int16 nData );
+ void Encrypt( SvStream& rStrm, sal_Int32 nData );
+
+ void Encrypt( SvStream& rStrm, float fValue );
+ void Encrypt( SvStream& rStrm, double fValue );
+
+ void EncryptBytes( SvStream& rStrm, ::std::vector<sal_uInt8>& aBytes );
+
+private:
+ void Init( const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData );
+
+ static sal_uInt32 GetBlockPos( std::size_t nStrmPos );
+ static sal_uInt16 GetOffsetInBlock( std::size_t nStrmPos );
+
+private:
+ ::msfilter::MSCodec_Std97 maCodec; /// Crypto algorithm implementation.
+ sal_uInt8 mpnDocId[16];
+ sal_uInt8 mpnSalt[16];
+ sal_uInt8 mpnSaltDigest[16];
+
+ sal_uInt64 mnOldPos; /// Last known stream position
+ bool mbValid;
+};
+
+// `s.GetChar(0) != 0` needed because some strings on export only contain NULL.
+#define XESTRING_TO_PSZ(s) \
+ (s.Len() && s.GetChar( 0 ) != 0 ? XclXmlUtils::ToOString( s ).getStr() : nullptr)
+
+class ScAddress;
+class ScDocShell;
+class ScFormulaCell;
+class ScRange;
+class ScRangeList;
+class ScTokenArray;
+struct XclAddress;
+struct XclFontData;
+class XclRangeList;
+namespace sc { class CompileFormulaContext; }
+
+class XclXmlUtils
+{
+public:
+ XclXmlUtils() = delete;
+ ~XclXmlUtils() = delete;
+ XclXmlUtils(const XclXmlUtils&) = delete;
+ XclXmlUtils& operator=(const XclXmlUtils&) = delete;
+
+ static void GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& sType, OUString& rValue);
+ static OUString GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId );
+
+ static OString ToOString( const Color& rColor );
+ static OString ToOString( const ScfUInt16Vec& rBuffer );
+ static OStringBuffer& ToOString( OStringBuffer& s, const ScAddress& rRange );
+ static OString ToOString( const ScDocument& rDoc, const ScRange& rRange, bool bFullAddressNotation = false );
+ static OString ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList );
+ static OStringBuffer& ToOString( OStringBuffer& s, const XclAddress& rAddress );
+ static OString ToOString( const XclExpString& s );
+ static OString ToOString( const ScDocument& rDoc, const XclRangeList& rRangeList );
+
+ static OUString ToOUString( const char* s );
+ static OUString ToOUString( const ScfUInt16Vec& rBuffer, sal_Int32 nStart = 0, sal_Int32 nLength = -1 );
+ static OUString ToOUString( sc::CompileFormulaContext& rCtx, const ScAddress& rAddress,
+ const ScTokenArray* pTokenArray, FormulaError nErrCode = FormulaError::NONE );
+ static OUString ToOUString( const XclExpString& s );
+
+ template <class T>
+ static sax_fastparser::FSHelperPtr WriteElement(sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, const T& value)
+ {
+ pStream->startElement(nElement);
+ pStream->write(value);
+ pStream->endElement(nElement);
+
+ return pStream;
+ }
+ static sax_fastparser::FSHelperPtr WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nNameId );
+};
+
+class XclExpXmlStream : public oox::core::XmlFilterBase
+{
+public:
+ XclExpXmlStream( const css::uno::Reference< css::uno::XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate );
+ virtual ~XclExpXmlStream() override;
+
+ /** Returns the filter root data. */
+ const XclExpRoot& GetRoot() const { return *mpRoot; }
+
+ sax_fastparser::FSHelperPtr& GetCurrentStream();
+ void PushStream( sax_fastparser::FSHelperPtr const & aStream );
+ void PopStream();
+
+ sax_fastparser::FSHelperPtr GetStreamForPath( const OUString& rPath );
+
+ template <typename Str, typename... Args>
+ void WriteAttributes(sal_Int32 nAttribute, Str&& value, Args&&... rest)
+ {
+ WriteAttribute(nAttribute, std::forward<Str>(value));
+ if constexpr(sizeof...(rest) > 0)
+ {
+ // coverity[stray_semicolon : FALSE] - coverity parse error
+ WriteAttributes(std::forward<Args>(rest)...);
+ }
+ }
+
+ sax_fastparser::FSHelperPtr CreateOutputStream (
+ const OUString& sFullStream,
+ std::u16string_view sRelativeStream,
+ const css::uno::Reference< css::io::XOutputStream >& xParentRelation,
+ const char* sContentType,
+ std::u16string_view sRelationshipType,
+ OUString* pRelationshipId = nullptr );
+
+ // ignore
+ virtual bool exportDocument() override;
+
+ // only needed for import; ignore
+ virtual bool importDocument() noexcept override;
+ virtual oox::vml::Drawing* getVmlDrawing() override;
+ virtual const oox::drawingml::Theme* getCurrentTheme() const override;
+ virtual oox::drawingml::table::TableStyleListPtr getTableStyles() override;
+ virtual oox::drawingml::chart::ChartConverter* getChartConverter() override;
+
+private:
+ virtual ::oox::ole::VbaProject* implCreateVbaProject() const override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ ScDocShell *getDocShell();
+ void WriteAttribute(sal_Int32 nAttr, std::u16string_view sVal);
+ void WriteAttribute(sal_Int32 nAttr, std::string_view sVal)
+ {
+ WriteAttribute(nAttr, OStringToOUString(sVal, RTL_TEXTENCODING_UTF8));
+ }
+ void WriteAttribute(sal_Int32 nAttr, const char* sVal)
+ {
+ if (sVal)
+ WriteAttribute(nAttr, OUString(sVal, strlen(sVal), RTL_TEXTENCODING_UTF8));
+ }
+
+ void validateTabNames(std::vector<OUString>& aOriginalTabNames);
+ void restoreTabNames(const std::vector<OUString>& aOriginalTabNames);
+ void renameTab(SCTAB aTab, OUString aNewName);
+
+ typedef std::map< OUString,
+ std::pair< OUString,
+ sax_fastparser::FSHelperPtr > > XclExpXmlPathToStateMap;
+
+ const XclExpRoot* mpRoot;
+ std::stack< sax_fastparser::FSHelperPtr > maStreams;
+ XclExpXmlPathToStateMap maOpenedStreamMap;
+
+ bool mbExportVBA;
+ bool mbExportTemplate;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xestring.hxx b/sc/source/filter/inc/xestring.hxx
new file mode 100644
index 0000000000..a61e563f01
--- /dev/null
+++ b/sc/source/filter/inc/xestring.hxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "xlstring.hxx"
+#include "ftools.hxx"
+
+class XclExpStream;
+class XclExpXmlStream;
+
+/** This class stores an unformatted or formatted string for Excel export.
+
+ The class supports two completely different types of Excel strings:
+ 1) BIFF2-BIFF7 byte strings: The text is encoded as a 8-bit character
+ array. The strings cannot contain any character formatting.
+ 2) BIFF8 Unicode strings: The text may be stored as UCS-2 character array,
+ or compressed to an 8-bit array, if all characters are less than
+ U+0100. Unicode strings may contain a formatting array, that specifies
+ the used FONT record for different ranges of characters.
+
+ The class provides full support for NUL characters in strings. On
+ construction or assignment the passed flags specify the behaviour of the
+ string while it is written to a stream (the 'Write' functions and
+ 'operator<<').
+ */
+class XclExpString
+{
+public:
+ // constructors -----------------------------------------------------------
+
+ /** Constructs an empty BIFF8 Unicode string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ explicit XclExpString(
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Constructs an unformatted BIFF8 Unicode string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ explicit XclExpString(
+ const OUString& rString,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ // assign -----------------------------------------------------------------
+
+ /** Assigns an unformatted string, converts this object to a BIFF8 Unicode string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ void Assign(
+ const OUString& rString,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Assigns a Unicode character, converts this object to a BIFF8 Unicode string. */
+ void Assign( sal_Unicode cChar );
+
+ /** Assigns an unformatted string, converts this object to a BIFF2-BIFF7 byte string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ void AssignByte(
+ std::u16string_view rString,
+ rtl_TextEncoding eTextEnc,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ // append -----------------------------------------------------------------
+
+ /** Appends a string. Uses the string flags used in constructor or last Assign().
+ @descr This object must be a BIFF8 Unicode string. */
+ void Append( std::u16string_view rString );
+
+ /** Appends a string. Uses the string flags used in constructor or last Assign().
+ @descr This object must be a BIFF2-BIFF7 byte string. */
+ void AppendByte( std::u16string_view rString, rtl_TextEncoding eTextEnc );
+ /** Appends a character. Uses the string flags used in constructor or last Assign().
+ @descr This object must be a BIFF2-BIFF7 byte string. */
+ void AppendByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc );
+
+ // formatting runs --------------------------------------------------------
+
+ /** Appends a formatting run. nChar must be greater than last contained character index. */
+ void AppendFormat( sal_uInt16 nChar, sal_uInt16 nFontIdx, bool bDropDuplicate = true );
+ /** Appends a trailing formatting run with the passed font index. */
+ void AppendTrailingFormat( sal_uInt16 nFontIdx );
+ /** Removes formatting runs at the end, if the string contains too much. */
+ void LimitFormatCount( sal_uInt16 nMaxCount );
+ /** Returns the font index of the first char in the formatting run, or EXC_FONT_NOTFOUND. */
+ sal_uInt16 GetLeadingFont();
+ /** The same as above + additionally remove the given font from the formatting run*/
+ sal_uInt16 RemoveLeadingFont();
+
+ // get data ---------------------------------------------------------------
+
+ /** Returns the character count of the string. */
+ sal_uInt16 Len() const { return mnLen; }
+ /** Returns true, if the string is empty. */
+ bool IsEmpty() const { return mnLen == 0; }
+ /** Returns true, if the string contains line breaks. */
+ bool IsWrapped() const { return mbWrapped; }
+ /** Returns true, if this string is equal to the passed string. */
+ bool IsEqual( const XclExpString& rCmp ) const;
+ /** Returns true, if this string is less than the passed string. */
+ bool IsLessThan( const XclExpString& rCmp ) const;
+
+ /** Returns true, if the string contains formatting information. */
+ bool IsRich() const { return !maFormats.empty(); }
+ /** Returns the current count of formatting runs for rich strings. */
+ sal_uInt16 GetFormatsCount() const;
+ /** Returns the vector with all formatting runs. */
+ const XclFormatRunVec& GetFormats() const { return maFormats; }
+
+ /** Returns the current string flags field to export. */
+ sal_uInt8 GetFlagField() const;
+ /** Returns the byte count the header will take on export. */
+ sal_uInt16 GetHeaderSize() const;
+ /** Returns the byte count the character buffer will take on export. */
+ std::size_t GetBufferSize() const;
+ /** Returns the byte count the whole string will take on export. */
+ std::size_t GetSize() const;
+
+ /** Returns the specified character from the (already encoded) string. */
+ sal_uInt16 GetChar( sal_uInt16 nCharIdx ) const;
+ /** Returns a hash value for the string. */
+ sal_uInt16 GetHash() const;
+
+ const ScfUInt16Vec& GetUnicodeBuffer() const { return maUniBuffer; }
+
+ // streaming --------------------------------------------------------------
+
+ /** Writes the string length field (1 byte or 2 bytes). */
+ void WriteLenField( XclExpStream& rStrm ) const;
+ /** Writes the string flags field (1 byte). */
+ void WriteFlagField( XclExpStream& rStrm ) const;
+ /** Writes 8-bit or 16-bit length field and string flags field. */
+ void WriteHeader( XclExpStream& rStrm ) const;
+ /** Writes the raw character buffer. */
+ void WriteBuffer( XclExpStream& rStrm ) const;
+ /** Writes the raw formatting run buffer. */
+ void WriteFormats( XclExpStream& rStrm, bool bWriteSize = false ) const;
+ /** Writes the complete Unicode string. */
+ void Write( XclExpStream& rStrm ) const;
+
+ /** Writes the string header to memory. */
+ void WriteHeaderToMem( sal_uInt8* pnMem ) const;
+ /** Writes the raw character buffer to memory (8-bit or 16-bit little-endian). */
+ void WriteBufferToMem( sal_uInt8* pnMem ) const;
+ /** Writes the entire string to memory. */
+ void WriteToMem( sal_uInt8* pnMem ) const;
+
+ void WriteXml( XclExpXmlStream& rStrm ) const;
+
+private:
+ /** Returns true, if the flag field should be written. */
+ bool IsWriteFlags() const;
+ /** Returns true, if the formatting run vector should be written. */
+ bool IsWriteFormats() const;
+
+ /** Sets the string length but regards the limit given in mnMaxLen. */
+ void SetStrLen( sal_Int32 nNewLen );
+ /** Inserts the passed character array into the internal character buffer.
+ @param nBegin First index in internal buffer to fill.
+ @param nLen Number of characters to insert. */
+ void CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, sal_Int32 nLen );
+ /** Inserts the passed character array into the internal character buffer.
+ @param nBegin First index in internal buffer to fill.
+ @param nLen Number of characters to insert. */
+ void CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_Int32 nLen );
+
+ /** Initializes flags, string length, and resizes character buffer.
+ @param nFlags Modifiers for string export.
+ @param nCurrLen The requested number of characters for the string.
+ @param nMaxLen The maximum length allowed of the resulting string.
+ @param bBiff8 true = BIFF8 Unicode string; false = BIFF2-BIFF7 byte string. */
+ void Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 );
+ /** Creates the character buffer from the given Unicode array.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary.
+ @param nFlags Modifiers for string export.
+ @param nCurrLen The real count of characters contained in the passed buffer.
+ @param nMaxLen The maximum length allowed of the resulting string. */
+ void Build(
+ const sal_Unicode* pcSource, sal_Int32 nCurrLen,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen );
+ /** Creates the character buffer from the given character array.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary.
+ @param nFlags Modifiers for string export.
+ @param nCurrLen The real count of characters contained in the passed buffer.
+ @param nMaxLen The maximum length allowed of the resulting string. */
+ void Build(
+ const char* pcSource, sal_Int32 nCurrLen,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen );
+
+ /** Initializes string length and resizes character buffers for appending operation.
+ @param nAddLen The number of characters to be appended. */
+ void InitAppend( sal_Int32 nAddLen );
+ /** Appends the given Unicode array to the character buffer.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary. */
+ void BuildAppend( std::u16string_view );
+ /** Appends the given character array to the character buffer.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary. */
+ void BuildAppend( std::string_view );
+
+ /** Initializes write process on stream. */
+ void PrepareWrite( XclExpStream& rStrm, sal_uInt16 nBytes ) const;
+
+private:
+ ScfUInt16Vec maUniBuffer; /// The Unicode character buffer.
+ ScfUInt8Vec maCharBuffer; /// The byte character buffer.
+ XclFormatRunVec maFormats; /// All formatting runs.
+ sal_uInt16 mnLen; /// Character count to export.
+ sal_uInt16 mnMaxLen; /// Maximum allowed number of characters.
+ bool mbIsBiff8; /// true = BIFF8 Unicode string, false = BIFF2-7 bytestring.
+ bool mbIsUnicode; /// true, if at least one character is >0xFF.
+ bool mb8BitLen; /// true = write 8-bit string length; false = 16-bit.
+ bool mbSmartFlags; /// true = omit flags on empty string; false = always write flags.
+ bool mbSkipFormats; /// true = skip formats on export; false = write complete formatted string.
+ bool mbWrapped; /// true = text contains several paragraphs.
+ bool mbSkipHeader; /// true = skip length and flags when writing string bytes.
+};
+
+inline bool operator==( const XclExpString& rLeft, const XclExpString& rRight )
+{
+ return rLeft.IsEqual( rRight );
+}
+
+inline bool operator!=( const XclExpString& rLeft, const XclExpString& rRight )
+{
+ return !(rLeft == rRight);
+}
+
+inline bool operator<( const XclExpString& rLeft, const XclExpString& rRight )
+{
+ return rLeft.IsLessThan( rRight );
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclExpString& rString )
+{
+ rString.Write( rStrm );
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xestyle.hxx b/sc/source/filter/inc/xestyle.hxx
new file mode 100644
index 0000000000..dd290af068
--- /dev/null
+++ b/sc/source/filter/inc/xestyle.hxx
@@ -0,0 +1,786 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <svl/zforlist.hxx>
+#include <svl/nfkeytab.hxx>
+#include <editeng/svxfont.hxx>
+#include "xerecord.hxx"
+#include "xlstyle.hxx"
+#include "xeroot.hxx"
+#include <fonthelper.hxx>
+#include <memory>
+#include <utility>
+#include <vector>
+#include <docmodel/color/ComplexColor.hxx>
+
+/* ============================================================================
+- Buffers for style records (PALETTE, FONT, FORMAT, XF, STYLE).
+============================================================================ */
+
+const sal_uInt16 EXC_ID_FONTLIST = 0x8031; /// For internal use only.
+const sal_uInt16 EXC_ID_FORMATLIST = 0x801E; /// For internal use only.
+const sal_uInt16 EXC_ID_XFLIST = 0x8043; /// For internal use only.
+const sal_uInt16 EXC_ID_DXFS = 0x9999; /// For internal use only. TODO:moggi: find a better/correct value
+
+// PALETTE record - color information =========================================
+
+/** Different types of colors in a document. */
+enum XclExpColorType
+{
+ EXC_COLOR_CELLTEXT, /// Text in a cell.
+ EXC_COLOR_CELLBORDER, /// Border of a cell.
+ EXC_COLOR_CELLAREA, /// Background area of a cell.
+ EXC_COLOR_CHARTTEXT, /// Text color in a chart.
+ EXC_COLOR_CHARTLINE, /// Line in a chart.
+ EXC_COLOR_CHARTAREA, /// Area in a chart.
+ EXC_COLOR_CTRLTEXT, /// Text color in a form control.
+ EXC_COLOR_GRID, /// Spreadsheet grid color.
+ EXC_COLOR_TABBG /// Spreadsheet tab bg color.
+};
+
+class XclExpPaletteImpl;
+
+/** Stores all used colors in the document.
+
+ Supports color reduction to the maximum count of the current BIFF version.
+ An instance of this class collects all colors in the conversion phase of
+ the export, using the InsertColor() function. It returns a unique
+ identifier for each passed color.
+
+ After the entire document is converted, the Finalize() function will reduce
+ the palette to the number of colors supported by the current BIFF version.
+
+ Then, in the streaming phase, the functions GetColorIndex() and
+ GetMixedColors() return the real Excel palette index for all color
+ identifiers.
+ */
+class XclExpPalette : public XclDefaultPalette, public XclExpRecord
+{
+public:
+ explicit XclExpPalette( const XclExpRoot& rRoot );
+ virtual ~XclExpPalette() override;
+
+ /** Inserts the color into the list and updates weighting.
+ @param nAutoDefault The Excel palette index for automatic color.
+ @return A unique ID for this color. */
+ sal_uInt32 InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 );
+ /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */
+ static sal_uInt32 GetColorIdFromIndex( sal_uInt16 nIndex );
+
+ /** Reduces the color list to the maximum count of the current BIFF version. */
+ void Finalize();
+
+ /** Returns the Excel palette index of the color with passed color ID. */
+ sal_uInt16 GetColorIndex( sal_uInt32 nColorId ) const;
+
+ /** Returns a foreground and background color for the two passed color IDs.
+ @descr If rnXclPattern contains a solid pattern, this function tries to find
+ the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId.
+ This will result in a better approximation to the passed foreground color. */
+ void GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const;
+
+ /** Returns the color for a (non-zero-based) Excel palette entry.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Saves the PALETTE record, if it differs from the default palette. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the PALETTE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpPaletteImpl > XclExpPaletteImplRef;
+ XclExpPaletteImplRef mxImpl;
+};
+
+// FONT record - font information =============================================
+
+const size_t EXC_FONTLIST_NOTFOUND = static_cast< size_t >( -1 );
+
+/** Helper functions for font export. */
+namespace XclExpFontHelper
+{
+ /** Returns the script type of the first font item found in the item set and its parents. */
+ sal_Int16 GetFirstUsedScript(
+ const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet );
+
+ /** Returns a VCL font object filled from the passed item set. */
+ vcl::Font GetFontFromItemSet(
+ const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet,
+ sal_Int16 nScript );
+
+ /**
+ * Get a dxf related font object from the item set.
+ * Only items that are explicitly set in the item set
+ * are also set in the returned object.
+ */
+ ScDxfFont GetDxfFontFromItemSet(const XclExpRoot& rRoot, const SfxItemSet& rSet);
+
+ /** Returns true, if at least one font related item is set in the passed item set.
+ @param bDeep true = Searches in parent item sets too. */
+ bool CheckItems(
+ const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet,
+ sal_Int16 nScript,
+ bool bDeep );
+}
+
+/** Stores all data of an Excel font and provides export of FONT records. */
+class XclExpFont : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpFont( const XclExpRoot& rRoot,
+ const XclFontData& rFontData, XclExpColorType eColorType );
+
+ /** Returns read-only access to font data. */
+ const XclFontData& GetFontData() const { return maData; }
+ /** Returns the font color identifier. */
+ sal_uInt32 GetFontColorId() const { return mnColorId; }
+ /** Compares this font with the passed font data.
+ @param nHash The hash value calculated from the font data. */
+ virtual bool Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the FONT record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclFontData maData; /// All font attributes.
+ sal_uInt32 mnColorId; /// Unique color ID for text color.
+ sal_uInt32 mnHash; /// Hash value for fast comparison.
+};
+
+class XclExpDxfFont : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpDxfFont(const XclExpRoot& rRoot, const SfxItemSet& rItemSet);
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+private:
+
+ ScDxfFont maDxfData;
+};
+
+/** Used as placeholder for font index 4, which is not used in Excel. */
+class XclExpBlindFont : public XclExpFont
+{
+public:
+ explicit XclExpBlindFont( const XclExpRoot& rRoot );
+
+ /** Returns always false to never find this font while searching the font list. */
+ virtual bool Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const override;
+
+ /** Skips writing this record. */
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+class ScPatternAttr;
+
+/** Stores the data of all fonts used in the document. */
+class XclExpFontBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpFontBuffer( const XclExpRoot& rRoot );
+
+ /** Returns the specified font from font list. */
+ const XclExpFont* GetFont( sal_uInt16 nXclFont ) const;
+ /** Returns the application font data of this file, needed e.g. for column width. */
+ const XclFontData& GetAppFontData() const;
+
+ /** Inserts a new font with the passed font data into the buffer if not present.
+ @param bAppFont true = Sets the application font; false = Inserts a new font.
+ @return The resulting Excel font index. */
+ sal_uInt16 Insert(const XclFontData& rFontData, XclExpColorType eColorType, bool bAppFont = false );
+ /** Inserts the SvxFont into the buffer if not present, e.g. where escapements are used.
+ @return The resulting Excel font index. */
+ sal_uInt16 Insert(const SvxFont& rFont, model::ComplexColor const& rComplexColor, XclExpColorType eColorType);
+ /** Inserts the font contained in the passed item set into the buffer, if not present.
+ @param nScript The script type of the font properties to be used.
+ @param bAppFont true = Sets the application font; false = Inserts a new font.
+ @return The resulting Excel font index. */
+ sal_uInt16 Insert(const SfxItemSet& rItemSet, sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont);
+
+ /** Writes all FONT records contained in this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Initializes the default fonts for the current BIFF version. */
+ void InitDefaultFonts();
+ /** Tries to find the passed font and returns the current list index. */
+ size_t Find( const XclFontData& rFontData );
+
+private:
+ typedef XclExpRecordList< XclExpFont > XclExpFontList;
+ typedef XclExpFontList::RecordRefType XclExpFontRef;
+
+ XclExpFontList maFontList; /// List of all FONT records.
+ size_t mnXclMaxSize; /// Maximum number of fonts.
+};
+
+// FORMAT record - number formats =============================================
+
+/** Stores a core number format index with corresponding Excel format index. */
+struct XclExpNumFmt
+{
+ sal_uInt32 mnScNumFmt; /// Core index of the number format.
+ sal_uInt16 mnXclNumFmt; /// Resulting Excel format index.
+ OUString maNumFmtString; /// format string
+
+ explicit XclExpNumFmt( sal_uInt32 nScNumFmt, sal_uInt16 nXclNumFmt, OUString aFrmt ) :
+ mnScNumFmt( nScNumFmt ), mnXclNumFmt( nXclNumFmt ), maNumFmtString(std::move( aFrmt )) {}
+
+ void SaveXml( XclExpXmlStream& rStrm );
+};
+
+typedef ::std::unique_ptr< SvNumberFormatter > SvNumberFormatterPtr;
+
+/** Stores all number formats used in the document. */
+class XclExpNumFmtBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpNumFmtBuffer( const XclExpRoot& rRoot );
+ virtual ~XclExpNumFmtBuffer() override;
+
+ /** Returns the core index of the current standard number format. */
+ sal_uInt32 GetStandardFormat() const { return mnStdFmt; }
+
+ /** Inserts a number format into the format buffer.
+ @param nScNumFmt The core index of the number format.
+ @return The resulting Excel format index. */
+ sal_uInt16 Insert( sal_uInt32 nScNumFmt );
+
+ /** Writes all FORMAT records contained in this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ OUString GetFormatCode ( sal_uInt32 nScNumFmt );
+
+private:
+ /** Writes the FORMAT record with index nXclIx and format string rFormatStr. */
+ void WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const OUString& rFormatStr );
+ /** Writes the FORMAT record represented by rFormat. */
+ void WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat );
+
+private:
+ typedef ::std::vector< XclExpNumFmt > XclExpNumFmtVec;
+
+ SvNumberFormatterPtr mxFormatter; /// Special number formatter for conversion.
+ XclExpNumFmtVec maFormatMap; /// Maps core formats to Excel indexes.
+ std::unique_ptr<NfKeywordTable> mpKeywordTable; /// Replacement table.
+ sal_uInt32 mnStdFmt; /// Key for standard number format.
+ sal_uInt16 mnXclOffset; /// Offset to first user defined format.
+};
+
+// XF, STYLE record - Cell formatting =========================================
+
+/** Extends the XclCellProt struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellProt : public XclCellProt
+{
+ /** Fills the protection attributes from the passed item set.
+ @return true = At least one protection item is set. */
+ bool FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle = false );
+
+ /** Fills the data to the passed fields of a BIFF3-BIFF8 XF record. */
+ void FillToXF3( sal_uInt16& rnProt ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** Extends the XclCellAlign struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellAlign : public XclCellAlign
+{
+ /** Fills the alignment attributes from the passed item set.
+ @descr Fills only the attributes exported in the passed BIFF version.
+ @param bForceLineBreak true = Set line break flag unconditionally.
+ @return true = At least one alignment item is set. */
+ bool FillFromItemSet(const XclRoot& rRoot, const SfxItemSet& rItemSet,
+ bool bForceLineBreak, XclBiff eBiff, bool bStyle = false );
+
+ /** Fills the data to the passed fields of a BIFF5/BIFF7 XF record. */
+ void FillToXF5( sal_uInt16& rnAlign ) const;
+ /** Fills the data to the passed fields of a BIFF8 XF record. */
+ void FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** Extends the XclCellBorder struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellBorder : public XclCellBorder
+{
+ sal_uInt32 mnLeftColorId; /// Color ID for left line.
+ sal_uInt32 mnRightColorId; /// Color ID for right line.
+ sal_uInt32 mnTopColorId; /// Color ID for top line.
+ sal_uInt32 mnBottomColorId; /// Color ID for bottom line.
+ sal_uInt32 mnDiagColorId; /// Color ID for diagonal line(s).
+
+ model::ComplexColor maComplexColorLeft;
+ model::ComplexColor maComplexColorRight;
+ model::ComplexColor maComplexColorTop;
+ model::ComplexColor maComplexColorBottom;
+ model::ComplexColor maComplexColorDiagonal;
+
+ explicit XclExpCellBorder();
+
+ /** Fills the border attributes from the passed item set.
+ @descr Fills only the attributes exported in the passed BIFF version.
+ @return true = At least one border item is set. */
+ bool FillFromItemSet( const SfxItemSet& rItemSet,
+ XclExpPalette& rPalette, XclBiff eBiff, bool bStyle = false );
+ /** Fills the mn***Color base members from the mn***ColorId members. */
+ void SetFinalColors( const XclExpPalette& rPalette );
+
+ /** Fills the data to the passed fields of a BIFF5/BIFF7 XF record. */
+ void FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const;
+ /** Fills the data to the passed fields of a BIFF8 XF record. */
+ void FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const;
+
+ /** Fills the data to the passed fields of a BIFF8 CF (conditional format) record. */
+ void FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** Extends the XclCellArea struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellArea : public XclCellArea
+{
+ sal_uInt32 mnForeColorId; /// Foreground color ID.
+ sal_uInt32 mnBackColorId; /// Background color ID.
+
+ Color maForeColor; // Actual foreground color
+ Color maBackColor; // Actual background color
+
+ model::ComplexColor maForegroundComplexColor;
+ model::ComplexColor maBackgroundComplexColor;
+
+ explicit XclExpCellArea();
+ explicit XclExpCellArea(Color aForeColor, Color aBackColor);
+
+ /** Fills the area attributes from the passed item set.
+ @return true = At least one area item is set. */
+ bool FillFromItemSet(
+ const SfxItemSet& rItemSet, XclExpPalette& rPalette,
+ bool bStyle );
+ /** Fills the mn***Color base members from the mn***ColorId members. */
+ void SetFinalColors( const XclExpPalette& rPalette );
+
+ /** Fills the data to the passed fields of a BIFF5/BIFF7 XF record. */
+ void FillToXF5( sal_uInt32& rnArea ) const;
+ /** Fills the data to the passed fields of a BIFF8 XF record. */
+ void FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const;
+
+ /** Fills the data to the passed fields of a BIFF8 CF (conditional format) record. */
+ void FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+struct XclExpColor
+{
+ Color maColor;
+ model::ComplexColor maComplexColor;
+
+ bool FillFromItemSet( const SfxItemSet& rItemSet );
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** A combination of unique XF identifier with real Excel XF index. */
+struct XclExpXFId
+{
+ sal_uInt32 mnXFId; /// Temporary XF identifier.
+ sal_uInt16 mnXFIndex; /// Real Excel XF index.
+
+ explicit XclExpXFId();
+ explicit XclExpXFId( sal_uInt32 nXFId )
+ : mnXFId( nXFId ), mnXFIndex( EXC_XF_DEFAULTCELL ) {}
+
+ /** Converts the XF identifier in mnXFId to an Excel XF index and stores it in mnXFIndex. */
+ void ConvertXFIndex( const XclExpRoot& rRoot );
+};
+
+class SfxStyleSheetBase;
+
+/** Represents an XF record which contains all formatting data of a cell or cell style. */
+class XclExpXF : public XclXFBase, public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Constructs a cell XF record from the passed Calc cell formatting. */
+ explicit XclExpXF(
+ const XclExpRoot& rRoot,
+ const ScPatternAttr& rPattern,
+ sal_Int16 nScript,
+ sal_uInt32 nScForceNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND,
+ sal_uInt16 nForceXclFont = EXC_FONT_NOTFOUND,
+ bool bForceLineBreak = false );
+ /** Constructs a style XF record from the passed cell style sheet. */
+ explicit XclExpXF(
+ const XclExpRoot& rRoot,
+ const SfxStyleSheetBase& rStyleSheet );
+
+ /** Returns the cell protection settings of this XF. */
+ const XclExpCellProt& GetProtectionData() const { return maProtection; }
+ /** Returns the alignment settings of this XF. */
+ const XclExpCellAlign& GetAlignmentData() const { return maAlignment; }
+ /** Returns the cell border settings of this XF. */
+ const XclExpCellBorder& GetBorderData() const { return maBorder; }
+ /** Returns the cell fill settings of this XF. */
+ const XclExpCellArea& GetAreaData() const { return maArea; }
+
+ /** Returns true, if this XF record represents the passed cell formatting.
+ @descr Searches for cell XFs only. */
+ bool Equals(
+ const ScPatternAttr& rPattern,
+ sal_uInt32 nScForceNumFmt,
+ sal_uInt16 nForceXclFont,
+ bool bForceLineBreak ) const;
+
+ /** Returns true, if this XF record represents the passed style.
+ @descr Searches for style XFs only. */
+ bool Equals( const SfxStyleSheetBase& rStyleSheet ) const;
+
+ /** Sets the resulting Excel palette index from all used color IDs (border and area). */
+ void SetFinalColors();
+
+ /** Returns true, if this XF record is completely equal to the passed. */
+ bool Equals( const XclExpXF& rCmpXF ) const;
+
+ void SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ const SfxItemSet* GetItemSet() const { return mpItemSet; }
+
+ sal_uInt32 GetScNumFmt() const { return mnScNumFmt; }
+ sal_uInt16 GetXclFont() const { return mnXclFont; }
+
+protected:
+ explicit XclExpXF( const XclExpRoot& rRoot, bool bCellXF );
+
+protected: // access for XclExpDefaultXF
+ const SfxItemSet* mpItemSet; /// Pointer to the item set (we do not own it).
+
+ XclExpCellProt maProtection; /// Cell protection flags.
+ XclExpCellAlign maAlignment; /// All alignment attributes.
+ XclExpCellBorder maBorder; /// Border line style.
+ XclExpCellArea maArea; /// Background area style.
+ sal_uInt32 mnParentXFId; /// XF ID of parent XF record.
+ sal_uInt32 mnScNumFmt; /// Calc number format index.
+ sal_uInt16 mnXclFont; /// Excel font index.
+ sal_uInt16 mnXclNumFmt; /// Excel number format index.
+ sal_Int32 mnBorderId; /// OOXML Border Index
+ sal_Int32 mnFillId; /// OOXML Fill Index
+
+private:
+ using XclXFBase::Equals;
+
+ /** Initializes with default values. */
+ void InitDefault();
+ /** Fills all members from the passed item set.
+ @param bDefStyle true = This is the "Default"/"Normal" style - needs special handling. */
+ void Init(
+ const SfxItemSet& rItemSet,
+ sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt,
+ sal_uInt16 nForceXclFont,
+ bool bForceLineBreak,
+ bool bDefStyle );
+
+ /** Returns the bits specifying the used attributes.
+ @descr In cell XFs a set bit means a used attribute, in style XF a cleared
+ bit means a used attribute. This method regards the cell/style state.
+ @return The mask based on bit 0 (not yet bit-shifted as needed for export). */
+ sal_uInt8 GetUsedFlags() const;
+
+ void WriteBody5( XclExpStream& rStrm );
+ void WriteBody8( XclExpStream& rStrm );
+
+ /** Writes the contents of the XF record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+/** Represents a default XF record. Supports methods to set attributes directly. */
+class XclExpDefaultXF : public XclExpXF
+{
+public:
+ explicit XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF );
+
+ /** Sets the Excel font index. */
+ void SetFont( sal_uInt16 nXclFont );
+ /** Sets the Excel number format index. */
+ void SetNumFmt( sal_uInt16 nXclNumFmt );
+};
+
+/** Represents a STYLE record containing the data of a cell style.
+ @descr The class is able to store built-in and user-defined styles. */
+class XclExpStyle : public XclExpRecord
+{
+public:
+ explicit XclExpStyle( sal_uInt32 nXFId, OUString aStyleName );
+ explicit XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel );
+
+ /** Returns true, if this record represents an Excel built-in style. */
+ bool IsBuiltIn() const { return mnStyleId != EXC_STYLE_USERDEF; }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the STYLE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ OUString maName; /// Name of the cell style.
+ XclExpXFId maXFId; /// XF identifier for style formatting.
+ sal_uInt8 mnStyleId; /// Built-in style identifier.
+ sal_uInt8 mnLevel; /// Outline level for RowLevel and ColLevel styles.
+};
+
+/** Stores all XF records (cell formats and cell styles) in the document.
+
+ Stores also the names of user defined cell styles (STYLE records). Supports
+ reduction to the maximum count of XF records of the current BIFF version.
+
+ An instance of this class collects all XF records in the conversion phase
+ of the export, using the Insert() and InsertStyle() functions. It returns a
+ unique identifier for each XF record.
+
+ After the entire document is converted, the Finalize() function will reduce
+ the list to the number of XF records supported by the current BIFF version.
+
+ Then, in the streaming phase, the function GetXFIndex() returns the real
+ Excel XF index for all XF identifiers.
+ */
+class XclExpXFBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXFBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts predefined built-in styles and user-defined styles. */
+ void Initialize();
+
+ /** Finds or creates a cell XF record for the passed item set.
+ @return A unique XF record ID. */
+ sal_uInt32 Insert( const ScPatternAttr* pPattern, sal_Int16 nScript );
+ /** Finds or creates a cell XF record for the passed item set.
+ @param nForceXclFont The font to be exported. If not equal to EXC_FONT_NOTFOUND,
+ this font index will be used unconditionally and the cell font will be ignored.
+ @param bForceLineBreak true = Set line break flag unconditionally.
+ This is required for cells that contain multi-line text.
+ @return A unique XF record ID. */
+ sal_uInt32 InsertWithFont(
+ const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt16 nForceXclFont,
+ bool bForceLineBreak );
+ /** Finds or creates a cell XF record for the passed item set, with custom number format.
+ @param nXFFlags Additional flags allowing to control the creation of an XF.
+ @param nForceScNumFmt The number format to be exported, e.g. formula
+ result type. This format will always overwrite the cell's number format.
+ @param bForceLineBreak true = Set line break flag unconditionally.
+ This is required for cells that contain multi-line text.
+ @return A unique XF record ID. */
+ sal_uInt32 InsertWithNumFmt(
+ const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt,
+ bool bForceLineBreak );
+ /** Inserts the passed cell style. Creates a style XF record and a STYLE record.
+ @return A unique XF record ID. */
+ sal_uInt32 InsertStyle( const SfxStyleSheetBase* pStyleSheet );
+ /** Returns the XF identifier representing a fixed Excel XF index (e.g. for built-in XFs). */
+ static sal_uInt32 GetXFIdFromIndex( sal_uInt16 nXFIndex );
+ /** Returns the XF identifier representing the default cell XF. */
+ static sal_uInt32 GetDefCellXFId();
+
+ /** Returns an XF record by its unique identifier. */
+ const XclExpXF* GetXFById( sal_uInt32 nXFId ) const;
+
+ /** Reduces the XF record list to the maximum allowed number of records. */
+ void Finalize();
+
+ /** Returns the Excel XF index of the XF record with passed XF ID. */
+ sal_uInt16 GetXFIndex( sal_uInt32 nXFId ) const;
+
+ sal_Int32 GetXmlStyleIndex( sal_uInt32 nXFId ) const;
+ sal_Int32 GetXmlCellIndex( sal_uInt32 nXFId ) const;
+
+ /** Writes all XF records contained in this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpXF > XclExpXFList;
+ typedef XclExpXFList::RecordRefType XclExpXFRef;
+
+private:
+ /** Returns the XF ID of the cell XF containing the passed format. */
+ sal_uInt32 FindXF( const ScPatternAttr& rPattern, sal_uInt32 nForceScNumFmt,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak ) const;
+ /** Returns the XF ID of the style XF containing the passed style. */
+ sal_uInt32 FindXF( const SfxStyleSheetBase& rStyleSheet ) const;
+
+ /** Returns the XF ID of a built-in style XF, searches by style identifier. */
+ sal_uInt32 FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const;
+
+ /** Tries to find the XF record containing the passed format or inserts a new record.
+ @return The XF record ID. */
+ sal_uInt32 InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak );
+ /** Inserts the passed cell style. Creates a style XF record and a STYLE record.
+ @return The XF record ID. */
+ sal_uInt32 InsertStyleXF( const SfxStyleSheetBase& rStyleSheet );
+
+ /** Inserts an XF and a STYLE record for all user defined style sheets. */
+ void InsertUserStyles();
+
+ /** Inserts a built-in XF record without a STYLE record and returns the XF ID.
+ @param bCreateStyleRec true = Creates the related STYLE record. */
+ sal_uInt32 AppendBuiltInXF( XclExpXFRef const & xXF,
+ sal_uInt8 nStyleId, sal_uInt8 nLevel = EXC_STYLE_NOLEVEL );
+ /** Inserts a built-in XF and STYLE record and returns the XF ID.
+ @param bCreateStyleRec true = Creates the related STYLE record. */
+ sal_uInt32 AppendBuiltInXFWithStyle( XclExpXFRef const & xXF,
+ sal_uInt8 nStyleId, sal_uInt8 nLevel = EXC_STYLE_NOLEVEL );
+
+ /** Inserts all default XF and STYLE records. */
+ void InsertDefaultRecords();
+
+ /** Appends a XF index to the internal ID<->index maps. */
+ void AppendXFIndex( sal_uInt32 nXFId );
+
+ void AddBorderAndFill( const XclExpXF& rXF );
+ void SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF );
+
+private:
+ /** Extended info about a built-in XF. */
+ struct XclExpBuiltInInfo
+ {
+ sal_uInt8 mnStyleId; /// Built-in style identifier.
+ sal_uInt8 mnLevel; /// Level for RowLevel/ColLevel styles.
+ bool mbPredefined; /// true = XF still predefined.
+ bool mbHasStyleRec; /// true = STYLE record created.
+ explicit XclExpBuiltInInfo();
+ };
+ typedef ::std::map< sal_uInt32, XclExpBuiltInInfo > XclExpBuiltInMap;
+ typedef ::std::vector< XclExpCellBorder > XclExpBorderList;
+ typedef ::std::vector< XclExpCellArea > XclExpFillList;
+
+ /** composite key for the find-map, so we can do partial key searching */
+ struct FindKey
+ {
+ bool mbCellXF; // is this a hard cell format, or a cell style
+ const SfxItemSet* mpItemSet;
+ sal_uInt32 mnScNumFmt;
+ sal_uInt16 mnXclFont;
+
+ bool operator<(const FindKey& other) const
+ {
+ if (mbCellXF != other.mbCellXF)
+ return mbCellXF < other.mbCellXF;
+ if (mpItemSet != other.mpItemSet)
+ return mpItemSet < other.mpItemSet;
+ if (mnScNumFmt != other.mnScNumFmt)
+ return mnScNumFmt < other.mnScNumFmt;
+ return mnXclFont < other.mnXclFont;
+ }
+ };
+ static FindKey ToFindKey(XclExpXF const &);
+
+ XclExpXFList maXFList; /// List of all XF records.
+ std::map<FindKey, std::vector<sal_uInt32>>
+ maXFFindMap; /// map of itemset to vector of positions, to speed up find
+ XclExpRecordList< XclExpStyle >
+ maStyleList; /// List of all STYLE records.
+ XclExpBuiltInMap maBuiltInMap; /// Contained elements describe built-in XFs.
+ ScfUInt16Vec maXFIndexVec; /// Maps XF IDs to XF indexes.
+ ScfUInt16Vec maStyleIndexes; /// Maps XF IDs to OOXML Style indexes
+ ScfUInt16Vec maCellIndexes; /// Maps XF IDs to OOXML Cell indexes
+ XclExpXFList maSortedXFList; /// List of XF records in XF index order.
+ XclExpBorderList maBorders; /// List of borders used by XF records
+ XclExpFillList maFills; /// List of fills used by XF records
+
+};
+
+class XclExpDxf : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellAlign> pAlign, std::unique_ptr<XclExpCellBorder> pBorder,
+ std::unique_ptr<XclExpDxfFont> pFont, std::unique_ptr<XclExpNumFmt> pNumberFmt,
+ std::unique_ptr<XclExpCellProt> pProt, std::unique_ptr<XclExpColor> pColor);
+ XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellArea> pCellArea);
+ virtual ~XclExpDxf() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+ void SaveXmlExt( XclExpXmlStream& rStrm);
+ void SetFinalColors();
+
+private:
+ std::unique_ptr<XclExpCellAlign> mpAlign;
+ std::unique_ptr<XclExpCellBorder> mpBorder;
+ std::unique_ptr<XclExpDxfFont> mpFont;
+ std::unique_ptr<XclExpNumFmt> mpNumberFmt;
+ std::unique_ptr<XclExpCellProt> mpProt;
+ std::unique_ptr<XclExpColor> mpColor;
+ std::unique_ptr<XclExpCellArea> mpCellArea;
+};
+
+class XclExpDxfs : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpDxfs( const XclExpRoot& rRoot );
+
+ sal_Int32 GetDxfId(const OUString& rName) const;
+ sal_Int32 GetDxfByColor(Color aColor) const;
+ void addColor(Color aColor);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm) override;
+ void Finalize();
+
+private:
+ typedef std::vector< std::unique_ptr<XclExpDxf> > DxfContainer;
+ std::map<OUString, sal_Int32> maStyleNameToDxfId;
+ std::map<Color, sal_Int32> maColorToDxfId;
+ DxfContainer maDxf;
+ std::unique_ptr<NfKeywordTable> mpKeywordTable; /// Replacement table.
+};
+
+class XclExpXmlStyleSheet : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXmlStyleSheet( const XclExpRoot& rRoot );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xetable.hxx b/sc/source/filter/inc/xetable.hxx
new file mode 100644
index 0000000000..ed16140f6d
--- /dev/null
+++ b/sc/source/filter/inc/xetable.hxx
@@ -0,0 +1,1028 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "xltable.hxx"
+
+#include <vector>
+#include "xladdress.hxx"
+#include "xecontent.hxx"
+#include "xerecord.hxx"
+#include "xestyle.hxx"
+#include "xlformula.hxx"
+
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <o3tl/sorted_vector.hxx>
+
+class XclExtLst;
+
+/* ============================================================================
+Export of cell tables including row and column description.
+- Managing all used and formatted cells in a sheet.
+- Row and column properties, i.e. width/height, visibility.
+- Find default row formatting and default column formatting.
+- Merged cell ranges.
+============================================================================ */
+
+// Helper records for cell records
+
+/** Represents a STRING record that contains the result of a string formula. */
+class XclExpStringRec : public XclExpRecord
+{
+public:
+ explicit XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpStringRef mxResult;
+};
+
+// Additional records for special formula ranges
+
+/** Base record for additional range formula records (i.e. ARRAY, SHRFMLA). */
+class XclExpRangeFmlaBase : public XclExpRecord
+{
+public:
+ /** Returns true, if the passed cell position is equal to own base position. */
+ bool IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const;
+
+ /** Derived classes create the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const = 0;
+ /** Derived classes return true, if the own formula contains volatile functions. */
+ virtual bool IsVolatile() const = 0;
+
+protected:
+ /** Constructs the record with a single cell. */
+ explicit XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos );
+ /** Constructs the record with a cell range. */
+ explicit XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange );
+
+ /** Extends the cell range to include the passed cell address. */
+ void Extend( const ScAddress& rScPos );
+
+ /** Writes the range address covered by this record. */
+ void WriteRangeAddress( XclExpStream& rStrm ) const;
+
+protected:
+ XclRange maXclRange; /// Range described by this record.
+ XclAddress maBaseXclPos; /// Address of base cell (first FORMULA record).
+};
+
+typedef rtl::Reference< XclExpRangeFmlaBase > XclExpRangeFmlaRef;
+
+// Array formulas =============================================================
+
+class ScTokenArray;
+
+/** Represents an ARRAY record that contains the token array of a matrix formula.
+
+ An ARRAY record is stored following the first FORMULA record that is part
+ of a matrix formula. All FORMULA records of a matrix formula contain a
+ reference to the ARRAY record, while the ARRAY record contains the formula
+ token array used by all formulas.
+ */
+class XclExpArray : public XclExpRangeFmlaBase
+{
+public:
+ explicit XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange );
+
+ /** Creates and returns the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const override;
+ /** Returns true, if the array formula contains volatile functions. */
+ virtual bool IsVolatile() const override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclTokenArrayRef mxTokArr; /// The token array of a matrix formula.
+};
+
+typedef rtl::Reference< XclExpArray > XclExpArrayRef;
+
+/** Caches all ARRAY records. */
+class XclExpArrayBuffer : protected XclExpRoot
+{
+public:
+ explicit XclExpArrayBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts a new ARRAY record into the buffer and returns it. */
+ XclExpArrayRef CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange );
+ /** Tries to find an ARRAY record that corresponds to an ocMatRef token. */
+ XclExpArrayRef FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const;
+
+private:
+ typedef ::std::map< ScAddress, XclExpArrayRef > XclExpArrayMap;
+ XclExpArrayMap maRecMap; /// Map containing the ARRAY records.
+};
+
+// Shared formulas ============================================================
+
+/** Represents a SHRFMLA record that contains the token array of a shared formula.
+
+ A SHRFMLA record is stored following the first FORMULA record that is part
+ of a shared formula. All FORMULA records of a shared formula contain a
+ reference to the SHRFMLA record, while the SHRFMLA record contains the
+ formula token array used by all formulas.
+ */
+class XclExpShrfmla : public XclExpRangeFmlaBase
+{
+public:
+ /** Creates a SHRFMLA record that consists of the passed cell address only. */
+ explicit XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos );
+
+ /** Extends the cell range to include the passed cell address. */
+ void ExtendRange( const ScAddress& rScPos );
+
+ /** Creates and returns the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const override;
+ /** Returns true, if the shared formula contains volatile functions. */
+ virtual bool IsVolatile() const override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclTokenArrayRef mxTokArr; /// The token array of a shared formula.
+ sal_uInt8 mnUsedCount; /// Number of FORMULA records referring to this record.
+};
+
+typedef rtl::Reference< XclExpShrfmla > XclExpShrfmlaRef;
+
+/** Caches all SHRFMLA records and provides functions to update their ranges. */
+class XclExpShrfmlaBuffer : protected XclExpRoot
+{
+public:
+ explicit XclExpShrfmlaBuffer( const XclExpRoot& rRoot );
+
+ /** Tries to create a new or to update an existing SHRFMLA record.
+ @return An empty reference, if the passed token array does not contain
+ a shared formula. If the token array is a shared formula, this
+ function updates its cell range to include the passed cell position,
+ if there is a SHRFMLA record for the passed token array; otherwise
+ this function creates and returns a new SHRFMLA record. */
+ XclExpShrfmlaRef CreateOrExtendShrfmla( const ScFormulaCell& rScCell, const ScAddress& rScPos );
+
+private:
+ /**
+ * Check for presence of token that's not allowed in Excel's shared
+ * formula. Refer to the "SharedParsedFormula" section of [MS-XLS] spec
+ * for more info.
+ */
+ bool IsValidTokenArray( const ScTokenArray& rArray ) const;
+
+ typedef std::unordered_map<const ScTokenArray*, XclExpShrfmlaRef> TokensType;
+ typedef o3tl::sorted_vector<const ScTokenArray*> BadTokenArraysType;
+
+ TokensType maRecMap; /// Map containing the SHRFMLA records.
+ BadTokenArraysType maBadTokens; /// shared tokens we should *not* export as SHRFMLA
+};
+
+// Multiple operations ========================================================
+
+/** Represents a TABLEOP record for a multiple operations range. */
+class XclExpTableop : public XclExpRangeFmlaBase
+{
+public:
+ explicit XclExpTableop( const ScAddress& rScPos,
+ const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode );
+
+ /** Returns true, if the cell range has been extended to the passed position.
+ @descr All references passed in rRefs must fit the ranges passed in the constructor. */
+ bool TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs );
+
+ /** Finalizes the record. Tests on valid cell range and reference addresses. */
+ void Finalize();
+
+ /** Creates and returns the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const override;
+ /** Returns true, if the multiple operations range is volatile. */
+ virtual bool IsVolatile() const override;
+ /** Writes the record if it is valid. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns true, if the passed cell position can be appended to this record. */
+ bool IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const;
+
+ /** Writes the contents of the TABLEOP record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnLastAppXclCol;/// Column index of last appended cell.
+ sal_uInt16 mnColInpXclCol; /// Column index of column input cell.
+ sal_uInt32 mnColInpXclRow; /// Row index of column input cell.
+ sal_uInt16 mnRowInpXclCol; /// Column index of row input cell.
+ sal_uInt32 mnRowInpXclRow; /// Row index of row input cell.
+ sal_uInt8 mnScMode; /// Type of the multiple operation (Calc constant).
+ bool mbValid; /// true = Contains valid references.
+};
+
+typedef rtl::Reference< XclExpTableop > XclExpTableopRef;
+
+/** Contains all created TABLEOP records and supports creating or updating them. */
+class XclExpTableopBuffer : protected XclExpRoot
+{
+public:
+ explicit XclExpTableopBuffer( const XclExpRoot& rRoot );
+
+ /** Tries to update an existing or to create a new TABLEOP record.
+ @return Reference to the TABLEOP record for this cell (existing or new),
+ or an empty reference, if rScTokArr does not contain a multiple
+ operations formula. */
+ XclExpTableopRef CreateOrExtendTableop(
+ const ScTokenArray& rScTokArr, const ScAddress& rScPos );
+
+ /** Finalizes all contained TABLEOP records. */
+ void Finalize();
+
+private:
+ /** Tries to create a new TABLEOP record, if rRefs contains valid references. */
+ XclExpTableopRef TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs );
+
+private:
+ XclExpRecordList< XclExpTableop > maTableopList; /// List of all TABLEOP records.
+};
+
+// Cell records
+
+/** The base class of all cell records. */
+class XclExpCellBase : public XclExpRecord
+{
+public:
+ /** Returns the (first) address of the cell(s). */
+ const XclAddress& GetXclPos() const { return maXclPos; }
+ /** Returns the (first) Excel column index of the cell(s). */
+ sal_uInt16 GetXclCol() const { return maXclPos.mnCol; }
+ /** Returns the Excel row index of the cell. */
+ sal_uInt32 GetXclRow() const { return maXclPos.mnRow; }
+
+ /** Derived classes return the column index of the last contained cell. */
+ virtual sal_uInt16 GetLastXclCol() const = 0;
+ /** Derived classes return the XF identifier of the first contained cell. */
+ virtual sal_uInt32 GetFirstXFId() const = 0;
+ /** Derived classes return true, if this record does not contain at least one valid cell. */
+ virtual bool IsEmpty() const = 0;
+ /** Derived classes return whether the cell contains multi-line text. */
+ virtual bool IsMultiLineText() const;
+
+ /** Derived classes try to merge the contents of the passed cell to own data. */
+ virtual bool TryMerge( const XclExpCellBase& rCell );
+ /** Derived classes convert the XF identifier(s) into the Excel XF index(es).
+ @param rXFIndexes The converted XF index(es) are inserted here. */
+ virtual void ConvertXFIndexes( const XclExpRoot& rRoot ) = 0;
+ /** Derived classes for blank cells insert the Excel XF index(es) into the passed vector. */
+ virtual void GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const;
+ /** Derived classes for blank cells remove unused Excel XF index(es). */
+ virtual void RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound );
+
+protected:
+ explicit XclExpCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos );
+
+ /** Sets this record to a new column position. */
+ void SetXclCol( sal_uInt16 nXclCol ) { maXclPos.mnCol = nXclCol; }
+
+private:
+ XclAddress maXclPos; /// Address of the cell.
+};
+
+typedef rtl::Reference< XclExpCellBase > XclExpCellRef;
+
+// Single cell records ========================================================
+
+/** Base class for all cell records not supporting multiple contents. */
+class XclExpSingleCellBase : public XclExpCellBase
+{
+public:
+ /** Returns the last column, which is equal to the first column for single cells. */
+ virtual sal_uInt16 GetLastXclCol() const override;
+ /** Return the XF identifier of the cell. */
+ virtual sal_uInt32 GetFirstXFId() const override;
+ /** Returns true, if this record does not contain at least one valid cell. */
+ virtual bool IsEmpty() const override;
+ /** Converts the XF identifier into the Excel XF index. */
+ virtual void ConvertXFIndexes( const XclExpRoot& rRoot ) override;
+ /** Writes cell address, XF index, and calls WriteContents() for each cell. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+protected:
+ explicit XclExpSingleCellBase( sal_uInt16 nRecId, std::size_t nContSize,
+ const XclAddress& rXclPos, sal_uInt32 nXFId );
+
+ explicit XclExpSingleCellBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId );
+
+ void SetContSize( std::size_t nContSize ) { mnContSize = nContSize; }
+ std::size_t GetContSize() const { return mnContSize; }
+
+ void SetXFId( sal_uInt32 nXFId ) { maXFId.mnXFId = nXFId; }
+ sal_uInt32 GetXFId() const { return maXFId.mnXFId; }
+
+private:
+ /** Writes cell address, XF index, and calls WriteContents() for each cell. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Derived classes write the contents of the specified cell (without XF index). */
+ virtual void WriteContents( XclExpStream& rStrm ) = 0;
+
+private:
+ XclExpXFId maXFId; /// The XF identifier of the cell formatting.
+ std::size_t mnContSize; /// The size of the cell contents.
+};
+
+/** Represents a NUMBER record that describes a cell with a double value. */
+class XclExpNumberCell : public XclExpSingleCellBase
+{
+public:
+ explicit XclExpNumberCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ double fValue );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ double mfValue; /// The cell value.
+};
+
+/** Represents a BOOLERR record that describes a cell with a Boolean value. */
+class XclExpBooleanCell : public XclExpSingleCellBase
+{
+public:
+ explicit XclExpBooleanCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ bool bValue );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ bool mbValue; /// The cell value.
+};
+
+class XclExpHyperlinkHelper;
+class EditTextObject;
+
+/** Represents a text cell record.
+
+ May contain a BIFF2-BIFF7 LABEL record for a simple string, or a BIFF2-BIFF7
+ RSTRING record for a formatted string, or a BIFF8 LABELSST string for any
+ string (simply stores a reference to the Shared String Table).
+ */
+class XclExpLabelCell : public XclExpSingleCellBase
+{
+public:
+ /** Constructs the record from an unformatted Calc string cell. */
+ explicit XclExpLabelCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const OUString& rStr );
+
+ /** Constructs the record from a formatted Calc edit cell. */
+ explicit XclExpLabelCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const EditTextObject* pEditText, XclExpHyperlinkHelper& rHlinkHelper );
+
+ /** Returns true if the cell contains multi-line text. */
+ virtual bool IsMultiLineText() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Initializes the record contents. Called from constructors. */
+ void Init( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, XclExpStringRef const & xText );
+
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ XclExpStringRef mxText; /// The cell text.
+ sal_uInt32 mnSstIndex; /// Index into Shared String Table (only used for BIFF8).
+ bool mbLineBreak; /// True = cell has automatic linebreaks enabled.
+};
+
+class ScFormulaCell;
+
+/** Represents a FORMULA record that describes a cell with a formula. */
+class XclExpFormulaCell : public XclExpSingleCellBase
+{
+public:
+ explicit XclExpFormulaCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const ScFormulaCell& rScFmlaCell,
+ XclExpArrayBuffer& rArrayBfr,
+ XclExpShrfmlaBuffer& rShrfmlaBfr,
+ XclExpTableopBuffer& rTableopBfr );
+
+ /** Writes the FORMULA record and additional records related to the formula. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ ScFormulaCell& mrScFmlaCell; /// The Calc formula cell.
+ XclTokenArrayRef mxTokArr; /// The token array of the formula.
+ XclExpRangeFmlaRef mxAddRec; /// Additional record for matrix/shared formulas.
+ XclExpRecordRef mxStringRec; /// STRING record for string result.
+};
+
+// Multiple cell records ======================================================
+
+struct XclExpMultiXFId : public XclExpXFId
+{
+ sal_uInt16 mnCount; /// Number of XF identifiers.
+
+ explicit XclExpMultiXFId( sal_uInt32 nXFId, sal_uInt16 nCount = 1 ) :
+ XclExpXFId( nXFId ), mnCount( nCount ) {}
+};
+
+/** Base class for all cell records supporting multiple contents. */
+class XclExpMultiCellBase : public XclExpCellBase
+{
+public:
+ /** Returns the column index of the last cell this record describes. */
+ virtual sal_uInt16 GetLastXclCol() const override;
+ /** Return the XF identifier of the first contained cell. */
+ virtual sal_uInt32 GetFirstXFId() const override;
+ /** Returns true, if this record does not contain at least one valid cell. */
+ virtual bool IsEmpty() const override;
+
+ /** Convert all XF identifiers into the Excel XF indexes. */
+ virtual void ConvertXFIndexes( const XclExpRoot& rRoot ) override;
+ /** Writes the record, calls WriteContents() for each contained cell.
+ @descr May write several records, if unused XF indexes are contained. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+protected:
+ explicit XclExpMultiCellBase( sal_uInt16 nRecId, sal_uInt16 nMulRecId,
+ std::size_t nContSize, const XclAddress& rXclPos );
+
+ /** Returns the number of cells this record represents. */
+ sal_uInt16 GetCellCount() const;
+
+ /** Appends the passed XF identifier nCount times to the list of XF identifiers. */
+ void AppendXFId( const XclExpMultiXFId& rXFId );
+ /** Appends the passed cell format nCount times to the list of XF identifiers. */
+ void AppendXFId( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, sal_uInt16 nScript,
+ sal_uInt32 nForcedXFId, sal_uInt16 nCount = 1 );
+
+ /** Tries to merge the XF ID list of the passed cell with the own list. */
+ bool TryMergeXFIds( const XclExpMultiCellBase& rCell );
+ /** Inserts the Excel XF index(es) into the passed vector. */
+ void GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const;
+
+ /** Removes unused Excel XF index(es).
+ @param rXFIndexes Specifies which XF indexes are used.
+ @param nStartAllNotFound Index in rXFIndexes which starts EXC_XF_NOTFOUND until the end.
+ */
+ void RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound );
+
+ /** Return starting column at which all indexes until the end are EXC_XF_DEFAULTCELL .*/
+ sal_uInt16 GetStartColAllDefaultCell() const;
+
+private:
+ /** Derived classes write the remaining contents of the specified cell (without XF index).
+ @param nRelCol Relative column index (starts with 0 for first cell of this record). */
+ virtual void WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) = 0;
+ virtual void WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) = 0;
+
+private:
+ typedef ::std::vector< XclExpMultiXFId > XclExpMultiXFIdDeq;
+
+ sal_uInt16 mnMulRecId; /// Record ID for multiple record variant.
+ std::size_t mnContSize; /// Data size of contents for one cell
+ XclExpMultiXFIdDeq maXFIds; /// The XF identifiers of the cell formatting.
+};
+
+/** Represents a BLANK or MULBLANK record that describes empty but formatted cells. */
+class XclExpBlankCell : public XclExpMultiCellBase
+{
+public:
+ explicit XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId );
+
+ explicit XclExpBlankCell( const XclExpRoot& rRoot,
+ const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId );
+
+ /** Tries to merge the contents of the passed cell to own data. */
+ virtual bool TryMerge( const XclExpCellBase& rCell ) override;
+ /** Inserts the Excel XF index(es) into the passed vector. */
+ virtual void GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const override;
+ /** Tries to remove unused Excel XF index(es). */
+ virtual void RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound ) override;
+
+ using XclExpMultiCellBase::GetStartColAllDefaultCell;
+
+private:
+ /** Writes the remaining contents of the specified cell (without XF index). */
+ virtual void WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) override;
+ virtual void WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) override;
+};
+
+/** Represents an RK or MULRK record that describes cells with a compressed double values. */
+class XclExpRkCell : public XclExpMultiCellBase
+{
+public:
+ explicit XclExpRkCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ sal_Int32 nRkValue );
+
+ /** Tries to merge the contents of the passed cell to own data. */
+ virtual bool TryMerge( const XclExpCellBase& rCell ) override;
+
+private:
+ /** Writes the remaining contents of the specified cell (without XF index). */
+ virtual void WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) override;
+ virtual void WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) override;
+
+private:
+ ScfInt32Vec maRkValues; /// The cell values.
+};
+
+// Rows and Columns
+
+class ScOutlineArray;
+
+/** Base class for buffers containing row or column outline data. */
+class XclExpOutlineBuffer
+{
+public:
+ /** Returns true, if a collapsed group ends at the last processed position. */
+ bool IsCollapsed() const { return mbCurrCollapse; }
+ /** Returns the highest level of an open group at the last processed position. */
+ sal_uInt8 GetLevel() const { return ::std::min( mnCurrLevel, EXC_OUTLINE_MAX ); }
+
+protected:
+ /** Constructs the outline buffer.
+ @param bRows true = Process row outline array; false = Process column outline array. */
+ explicit XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows );
+
+ /** Updates the current state by processing the settings at the passed Calc position. */
+ void UpdateColRow( SCCOLROW nScPos );
+
+private:
+ /** Data about an outline level. */
+ struct XclExpLevelInfo
+ {
+ SCCOLROW mnScEndPos; /// The end position of a group in a level.
+ bool mbHidden; /// true = Group in this level is hidden.
+ explicit XclExpLevelInfo() : mnScEndPos( 0 ), mbHidden( false ) {}
+ };
+
+ const ScOutlineArray* mpScOLArray; /// Pointer to Calc outline array.
+ std::vector< XclExpLevelInfo >
+ maLevelInfos; /// Info for current row and all levels.
+ sal_uInt8 mnCurrLevel; /// Highest level of an open group for current position.
+ bool mbCurrCollapse; /// true = Collapsed group ends at current position.
+};
+
+/** The outline buffer for column outlines. */
+class XclExpColOutlineBuffer : public XclExpOutlineBuffer
+{
+public:
+ explicit XclExpColOutlineBuffer( const XclExpRoot& rRoot ) :
+ XclExpOutlineBuffer( rRoot, false ) {}
+
+ /** Updates the current state by processing the settings of the passed Calc column. */
+ void Update( SCCOL nScCol )
+ { UpdateColRow( static_cast< SCCOLROW >( nScCol ) ); }
+};
+
+/** The outline buffer for row outlines. */
+class XclExpRowOutlineBuffer : public XclExpOutlineBuffer
+{
+public:
+ explicit XclExpRowOutlineBuffer( const XclExpRoot& rRoot ) :
+ XclExpOutlineBuffer( rRoot, true ) {}
+
+ /** Updates the current state by processing the settings of the passed Calc row. */
+ void Update( SCROW nScRow )
+ { UpdateColRow( static_cast< SCCOLROW >( nScRow ) ); }
+};
+
+/** Represents a GUTS record containing the level count of row and column outlines. */
+class XclExpGuts : public XclExpRecord
+{
+public:
+ explicit XclExpGuts( const XclExpRoot& rRoot );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnColLevels; /// Number of visible column outline levels.
+ sal_uInt16 mnColWidth; /// Width of column outline area (pixels).
+ sal_uInt16 mnRowLevels; /// Number of visible row outline levels.
+ sal_uInt16 mnRowWidth; /// Width of row outline area (pixels).
+};
+
+/** Represents a DIMENSIONS record containing the used area of a sheet. */
+class XclExpDimensions : public XclExpRecord
+{
+public:
+ explicit XclExpDimensions( const XclExpRoot& rRoot );
+
+ /** Sets the used area to the record. */
+ void SetDimensions(
+ sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
+ sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the contents of the DIMENSIONS record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+ sal_uInt32 mnFirstUsedXclRow; /// First used row.
+ sal_uInt32 mnFirstFreeXclRow; /// First unused row after used area.
+ sal_uInt16 mnFirstUsedXclCol; /// First used column.
+ sal_uInt16 mnFirstFreeXclCol; /// First free column after used area.
+};
+
+/** Represents the DEFCOLWIDTH record containing the default column width of a sheet.
+
+ Excel stores the default column width in entire character widths of the '0'
+ character using the application default font (i.e. the default width is 10,
+ if the '0' character fits 10 times into a cell in a column with default
+ width.
+
+ Half of character width is reserved for non character display.
+ It is margin padding (two on each side) and padding for the gridlines.
+
+ The IsDefWidth() function returns true, if the passed width (measured in
+ 1/256 of the width of the '0' character) could be converted exactly to the
+ default width. If the passed width is rounded up or down to get the default
+ width, the function returns false.
+ */
+class XclExpDefcolwidth : public XclExpDoubleRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDefcolwidth( const XclExpRoot& rRoot );
+
+ /** Returns true, if the own default width exactly matches the passed width. */
+ bool IsDefWidth( sal_uInt16 nXclColWidth ) const;
+
+ /** Sets the passed column width (in 1/256 character width) as default width. */
+ void SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS );
+
+ virtual void Save(XclExpStream& rStrm) override;
+};
+
+/** Contains the column settings for a range of columns.
+
+ After construction the record contains a temporary XF identifier returned
+ from the XF buffer. After creating the entire Excel document in memory, the
+ ConvertXFIndexes() function converts it into the real Excel XF index.
+ */
+class XclExpColinfo : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Constructs the record with the settings in the Calc document. */
+ explicit XclExpColinfo( const XclExpRoot& rRoot,
+ SCCOL nScCol, SCROW nLastScRow,
+ XclExpColOutlineBuffer& rOutlineBfr );
+
+ /** Converts the XF identifier into the Excel XF index. */
+ void ConvertXFIndexes();
+
+ /** Tries to merge this record with the passed record.
+ @descr Possible, if passed record directly follows this record and has equal contents.
+ @return true = This record is equal to passed record and has been updated. */
+ bool TryMerge( const XclExpColinfo& rColInfo );
+
+ /** Returns the Excel width of the column(s). */
+ sal_uInt16 GetColWidth() const { return mnWidth; }
+ /** Returns the final Excel XF index of the column(s). */
+ sal_uInt16 GetXFIndex() const { return maXFId.mnXFIndex; }
+ /** Returns the number of columns represented by this record. */
+ sal_uInt16 GetColCount() const { return mnLastXclCol - mnFirstXclCol + 1; }
+
+ /** Returns true, if the column has default format and width. Also sets mbCustomWidth */
+ bool IsDefault( const XclExpDefcolwidth& rDefColWidth );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of this COLINFO record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpXFId maXFId; /// The XF identifier for column default format.
+ bool mbCustomWidth; /// True = Column width is different from default width
+ sal_uInt16 mnWidth; /// Excel width of the column.
+ sal_uInt16 mnScWidth; /// Calc width of the column.
+ sal_uInt16 mnFlags; /// Additional column flags.
+ sal_uInt8 mnOutlineLevel; /// Outline Level of column (for OOXML)
+ sal_uInt16 mnFirstXclCol; /// Index to first column.
+ sal_uInt16 mnLastXclCol; /// Index to last column.
+};
+
+/** Contains COLINFO records for all columns of a Calc sheet.
+
+ On construction one COLINFO record per column is created. After creating
+ the entire Excel document in memory, the ConvertXFIndexes() function converts
+ all temporary XF identifiers into real Excel XF indexes and merges all equal
+ COLINFO records together.
+ */
+class XclExpColinfoBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpColinfoBuffer( const XclExpRoot& rRoot );
+
+ /** Initializes the buffer: finds settings and formatting of all columns.
+ @param nLastScRow Last row used to find default formatting. */
+ void Initialize( SCROW nLastScRow );
+ /** Converts the XF identifiers into the Excel XF indexes and merges equal columns.
+ @param rXFIndexes Returns the final XF indexes of all columns. */
+ void Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS );
+
+ /** Writes all COLINFO records of this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+ sal_uInt8 GetHighestOutlineLevel() const { return mnHighestOutlineLevel; }
+ double GetDefColWidth() const { return maDefcolwidth.GetValue(); }
+
+private:
+ typedef XclExpRecordList< XclExpColinfo > XclExpColinfoList;
+ typedef XclExpColinfoList::RecordRefType XclExpColinfoRef;
+
+ XclExpColinfoList maColInfos; /// List of COLINFO records.
+ XclExpDefcolwidth maDefcolwidth; /// The DEFCOLWIDTH record.
+ XclExpColOutlineBuffer maOutlineBfr; /// Buffer for column outline groups.
+ sal_uInt8 mnHighestOutlineLevel; /// Highest number of outline levels for columns in sheet.
+};
+
+class XclExpRow;
+
+/** Contains all possible default row settings. */
+struct XclExpDefaultRowData
+{
+ sal_uInt16 mnFlags; /// Default flags for unspecified rows.
+ sal_uInt16 mnHeight; /// Default height for unspecified rows.
+
+ explicit XclExpDefaultRowData();
+ explicit XclExpDefaultRowData( const XclExpRow& rRow );
+
+ /** Returns true, if rows are hidden by default. */
+ bool IsHidden() const { return ::get_flag( mnFlags, EXC_DEFROW_HIDDEN ); }
+ /** Returns true, if the rows have a manually set height by default. */
+ bool IsUnsynced() const { return ::get_flag( mnFlags, EXC_DEFROW_UNSYNCED ); }
+};
+
+/** Represents a DEFROWHEIGHT record containing default format for unused rows. */
+class XclExpDefrowheight : public XclExpRecord
+{
+public:
+ explicit XclExpDefrowheight();
+
+ /** Sets the passed default data as current record contents. */
+ void SetDefaultData( const XclExpDefaultRowData& rDefData );
+ XclExpDefaultRowData& GetDefaultData() { return maDefData; }
+private:
+ /** Writes the contents of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpDefaultRowData maDefData; /// Record data.
+};
+
+/** Represents a ROW record and additionally contains all cells records of a row.
+
+ This class contains all cell records of a row in a spreadsheet. There are 2
+ cell records in Excel that support storing a range of cells in one record
+ (MULBLANK for multiple blank cells, and MULRK for multiple RK values). The
+ insertion functions try to merge a new inserted cell with existing
+ neighbors, if this is supported by the current type of cell record.
+
+ The Finalize() function converts the XF identifiers of all cell records to
+ the final Excel XF indexes. Then a default
+ */
+class XclExpRow : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Constructs the ROW record and converts the Calc row settings.
+ @param bAlwaysEmpty true = This row will not be filled with blank cells
+ in the Finalize() function. */
+ explicit XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
+ XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight );
+
+ /** Returns the excel row index of this ROW record. */
+ sal_uInt32 GetXclRow() const { return mnXclRow; }
+ /** Returns the height of the row in twips. */
+ sal_uInt16 GetHeight() const { return mnHeight; }
+ /** Returns true, if this row does not contain at least one valid cell. */
+ bool IsEmpty() const { return maCellList.IsEmpty(); }
+ /** Returns true, if this row is hidden. */
+ bool IsHidden() const { return ::get_flag( mnFlags, EXC_ROW_HIDDEN ); }
+ /** Returns true, if this row contains a manually set height. */
+ bool IsUnsynced() const { return ::get_flag( mnFlags, EXC_ROW_UNSYNCED ); }
+ /** Returns true, if this row is enabled (will be exported). */
+ bool IsEnabled() const { return mbEnabled; }
+
+ /** Appends the passed cell object to this row. */
+ void AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase );
+
+ /** Converts all XF identifiers into the Excel XF indexes. */
+ void Finalize( const ScfUInt16Vec& rColXFIndexes,
+ ScfUInt16Vec& aXFIndexes,
+ size_t nStartColAllDefault,
+ bool bUpdateProgress );
+
+ /** Returns the column index of the first used cell in this row.
+ @descr This function can only be called after Finalize(). */
+ sal_uInt16 GetFirstUsedXclCol() const;
+ /** Returns the column index of the first unused cell following all used cells in this row.
+ @descr This function can only be called after Finalize(). */
+ sal_uInt16 GetFirstFreeXclCol() const;
+
+ /** Returns true, if this row may be omitted by using the DEFROWHEIGHT record.
+ @descr A row may be omitted, if it does not contain any cell or
+ explicit default cell formatting, and is not part of an outline.
+ This function can only be called after Finalize(). */
+ bool IsDefaultable() const;
+ /** Disables this row, if it is defaultable and has the passed default format.
+ @descr Disabled rows will not be saved.
+ This function can only be called after Finalize(). */
+ void DisableIfDefault( const XclExpDefaultRowData& rDefRowData );
+
+ /** Writes all cell records of this row. */
+ void WriteCellList( XclExpStream& rStrm );
+
+ /** Writes the ROW record if the row is not disabled (see DisableIfDefault() function). */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ sal_uInt32 GetXclRowRpt() const { return mnXclRowRpt; }
+ void SetXclRowRpt( sal_uInt32 nRpt ){ mnXclRowRpt = nRpt; }
+private:
+ /** Inserts a cell at the specified list position, tries to merge with neighbors. */
+ void InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase );
+
+ /** Writes the contents of the ROW record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpRecordList< XclExpCellBase >
+ maCellList; /// List of cell records for this row.
+ sal_uInt32 mnXclRow; /// Excel row index of this row.
+ sal_uInt16 mnHeight; /// Row height in twips.
+ sal_uInt16 mnFlags; /// Flags for the ROW record.
+ sal_uInt16 mnXFIndex; /// Default row formatting.
+ sal_uInt8 mnOutlineLevel; /// Outline Level of row (for OOXML)
+ sal_uInt32 mnXclRowRpt;
+ sal_uInt32 mnCurrentRow;
+ bool mbAlwaysEmpty; /// true = Do not add blank cells in Finalize().
+ bool mbEnabled; /// true = Write this ROW record.
+};
+
+/** Collects all rows which contain all cells of a sheet.
+
+ This row buffer automatically creates ROW records when cells are inserted
+ with the AppendCell() function. It is possible to force creation of more
+ ROW records with the CreateRows() function. In both cases, all preceding
+ missing ROW records are inserted too.
+ */
+class XclExpRowBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpRowBuffer( const XclExpRoot& rRoot );
+
+ /** Appends the passed cell object to the row that the cell specifies. */
+ void AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase );
+ /** Forces insertion of all ROW records before the passed row. */
+ void CreateRows( SCROW nFirstFreeScRow );
+
+ /** Converts all XF identifiers into the Excel XF indexes and calculates default formats.
+ @param rDefRowData (out-param) The default row format is returned here.
+ @param rColXFIndexes The column default XF indexes.
+ @param nStartColAllDefault Index in rColXFIndexes which starts EXC_XF_DEFAULTCELL until the end.
+ */
+ void Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault );
+
+ /** Writes the DIMENSIONS record, all ROW records and all cell records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ XclExpDimensions& GetDimensions() { return maDimensions; }
+ sal_uInt8 GetHighestOutlineLevel() const { return mnHighestOutlineLevel; }
+
+private:
+ /** Returns access to the specified ROW record. Inserts preceding missing ROW records.
+ @param bRowAlwaysEmpty true = Created rows will not be filled with blank cells
+ in the XclExpRow::Finalize() function. */
+ XclExpRow& GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty );
+
+private:
+ typedef std::shared_ptr<XclExpRow> RowRef;
+ typedef ::std::map<sal_uInt32, RowRef> RowMap;
+
+ RowMap maRowMap;
+ XclExpRowOutlineBuffer maOutlineBfr; /// Buffer for row outline groups.
+ XclExpDimensions maDimensions; /// DIMENSIONS record for used area.
+ sal_uInt8 mnHighestOutlineLevel; /// Highest number of outline levels for rows in sheet.
+};
+
+// Cell Table
+
+class XclExpNote;
+class XclExpMergedcells;
+class XclExpHyperlink;
+class XclExpDval;
+
+/** This class contains the cell contents and more of an entire sheet.
+
+ The cell table includes the settings and default formatting of all columns,
+ the settings and default formatting of all used rows, and the contents of
+ all cells of one sheet in a spreadsheet document.
+
+ The constructor does all the work creating the cell table. It reads the
+ Calc sheet and converts all columns, rows, and cells to Excel record data.
+ Additionally, hyperlink records, note records, additional records for
+ formula cells, data validation records, and outline records are created.
+
+ The Finalize() function does even more work. It calculates default column
+ settings and removes column records that are equal to this default. The
+ same happens with rows: A default format is calculated for each row, and
+ all blank cells in this row that have the same format are removed. Then,
+ the most used row settings are calculated, and all empty rows that have the
+ same settings are removed too.
+
+ Records that are not stored inside the cell table area in an Excel file
+ (i.e. DEFROWHEIGHT record, NOTE records, MERGEDCELLS record, HLINK records,
+ DVAL and DV records for data validation) can be accessed with the function
+ CreateRecord(). It returns the reference to the respective record (or
+ record list) which can be inserted into a record list.
+ */
+class XclExpCellTable : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpCellTable( const XclExpRoot& rRoot );
+
+ /** Converts all XF identifiers into the Excel XF indexes and calculates default formats. */
+ void Finalize(bool bXLS);
+
+ /** Returns the reference to an internal record specified by the passed record id.
+ @param nRecId The record identifier that specifies which record is
+ returned. Possible values are: EXC_ID_DEFROWHEIGHT, EXC_ID_NOTE,
+ EXC_ID_MERGEDCELLS, EXC_ID_HLINK, EXC_ID_DVAL. */
+ XclExpRecordRef CreateRecord( sal_uInt16 nRecId ) const;
+ /** Saves the entire cell table. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpNote > XclExpNoteList;
+ typedef XclExpRecordList< XclExpHyperlink > XclExpHyperlinkList;
+
+ typedef rtl::Reference< XclExpDefrowheight > XclExpDefrowhRef;
+ typedef rtl::Reference< XclExpNoteList > XclExpNoteListRef;
+ typedef rtl::Reference< XclExpMergedcells > XclExpMergedcellsRef;
+ typedef rtl::Reference< XclExpHyperlinkList > XclExpHyperlinkRef;
+ typedef rtl::Reference< XclExpDval > XclExpDvalRef;
+ typedef rtl::Reference< XclExtLst > XclExtLstRef;
+
+ XclExpColinfoBuffer maColInfoBfr; /// Buffer for column formatting.
+ XclExpRowBuffer maRowBfr; /// Rows and cell records.
+ XclExpArrayBuffer maArrayBfr; /// Buffer for ARRAY records.
+ XclExpShrfmlaBuffer maShrfmlaBfr; /// Buffer for SHRFMLA records.
+ XclExpTableopBuffer maTableopBfr; /// Buffer for TABLEOP records.
+ XclExpDefrowhRef mxDefrowheight; /// DEFROWHEIGHT record for default row format.
+ XclExpRecordRef mxGuts; /// GUTS record for outline areas.
+ XclExpNoteListRef mxNoteList; /// List of NOTE records.
+ XclExpMergedcellsRef mxMergedcells; /// MERGEDCELLS record for merged cell ranges.
+ XclExpHyperlinkRef mxHyperlinkList; /// List of HLINK records.
+ XclExpDvalRef mxDval; /// Data validation with DVAL and DV records.
+ XclExtLstRef mxExtLst;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeview.hxx b/sc/source/filter/inc/xeview.hxx
new file mode 100644
index 0000000000..6b7980ca84
--- /dev/null
+++ b/sc/source/filter/inc/xeview.hxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlview.hxx"
+#include "xeroot.hxx"
+
+// Workbook view settings records =============================================
+
+/** Represents the WINDOW1 record containing global workbook view settings. */
+class XclExpWindow1 : public XclExpRecord
+{
+public:
+ explicit XclExpWindow1( const XclExpRoot& rRoot );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the WINDOW1 record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnFlags; /// Option flags.
+ sal_uInt16 mnTabBarSize; /// Size of tabbar relative to window width (per mill).
+};
+
+// Sheet view settings records ================================================
+
+/** Represents a WINDOW2 record with general view settings for a sheet. */
+class XclExpWindow2 : public XclExpRecord
+{
+public:
+ explicit XclExpWindow2( const XclExpRoot& rRoot,
+ const XclTabViewData& rData, sal_uInt32 nGridColorId );
+
+private:
+ /** Writes the contents of the WINDOW2 record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ Color maGridColor; /// Grid color (<=BIFF5).
+ sal_uInt32 mnGridColorId; /// Color ID of grid color (>=BIFF8).
+ sal_uInt16 mnFlags; /// Option flags.
+ XclAddress maFirstXclPos; /// First visible cell.
+ sal_uInt16 mnNormalZoom; /// Zoom factor for normal view.
+ sal_uInt16 mnPageZoom; /// Zoom factor for pagebreak preview.
+};
+
+/** Represents an SCL record for the zoom factor of the current view of a sheet. */
+class XclExpScl : public XclExpRecord
+{
+public:
+ explicit XclExpScl( sal_uInt16 nZoom );
+
+private:
+ /** Tries to shorten numerator and denominator by the passed value. */
+ void Shorten( sal_uInt16 nFactor );
+ /** Writes the contents of the SCL record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnNum; /// Numerator of the zoom factor.
+ sal_uInt16 mnDenom; /// Denominator of the zoom factor.
+};
+
+/** Represents a PANE record containing settings for split/frozen windows. */
+class XclExpPane : public XclExpRecord
+{
+public:
+ explicit XclExpPane( const XclTabViewData& rData );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the PANE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnSplitX; /// Split X position, or frozen column.
+ sal_uInt32 mnSplitY; /// Split Y position, or frozen row.
+ XclAddress maSecondXclPos; /// First visible cell in additional panes.
+ sal_uInt8 mnActivePane; /// Active pane (with cell cursor).
+ bool mbFrozenPanes; /// true = "frozen" panes; false = "split" window.
+};
+
+/** Represents a SELECTION record with selection data for a pane. */
+class XclExpSelection : public XclExpRecord
+{
+public:
+ explicit XclExpSelection( const XclTabViewData& rData, sal_uInt8 nPane );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the contents of the SELECTION record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclSelectionData maSelData; /// Selection data.
+ sal_uInt8 mnPane; /// Pane identifier of this selection.
+};
+
+class XclExpTabBgColor : public XclExpRecord
+{
+public:
+ explicit XclExpTabBgColor( const XclTabViewData& rTabViewData );
+
+ /* virtual void SaveXml( XclExpXmlStream& rStrm ); TODO Fix XML Saving Stream */
+private:
+ /** Writes the contents of the SHEETEXT record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclTabViewData& mrTabViewData; /// view settings data of current sheet.
+};
+
+// View settings ==============================================================
+
+/** Contains all view settings records for a single sheet. */
+class XclExpTabViewSettings : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Creates all records containing the view settings of the specified sheet. */
+ explicit XclExpTabViewSettings( const XclExpRoot& rRoot, SCTAB nScTab );
+
+ /** Writes all view settings records to the stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Creates selection data for the specified pane. */
+ void CreateSelectionData( sal_uInt8 nPane,
+ const ScAddress& rCursor, const ScRangeList& rSelection );
+
+ void WriteWindow2( XclExpStream& rStrm ) const;
+ void WriteScl( XclExpStream& rStrm ) const;
+ void WritePane( XclExpStream& rStrm ) const;
+ void WriteSelection( XclExpStream& rStrm, sal_uInt8 nPane ) const;
+ void WriteTabBgColor( XclExpStream& rStrm ) const;
+
+private:
+ XclTabViewData maData; /// All view settings for a sheet.
+ sal_uInt32 mnGridColorId; /// Color identifier for grid color.
+ bool mbHasTabSettings; /// It's false for embedded OLE spreadsheets.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xichart.hxx b/sc/source/filter/inc/xichart.hxx
new file mode 100644
index 0000000000..e26ed4a0cf
--- /dev/null
+++ b/sc/source/filter/inc/xichart.hxx
@@ -0,0 +1,1433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <salhelper/simplereferenceobject.hxx>
+#include <set>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <types.hxx>
+#include "xlchart.hxx"
+#include "xlstyle.hxx"
+#include "xiescher.hxx"
+#include "xistring.hxx"
+
+namespace com::sun::star {
+ namespace awt
+ {
+ struct Rectangle;
+ }
+ namespace frame
+ {
+ class XModel;
+ }
+ namespace drawing
+ {
+ class XShape;
+ }
+ namespace chart2
+ {
+ struct ScaleData;
+ class XChartDocument;
+ class XDiagram;
+ class XCoordinateSystem;
+ class XChartType;
+ class XDataSeries;
+ class XRegressionCurve;
+ class XAxis;
+ class XLegend;
+ class XTitle;
+ class XFormattedString;
+ namespace data
+ {
+ class XDataProvider;
+ class XDataSequence;
+ class XLabeledDataSequence;
+ }
+ }
+}
+
+struct XclObjLineData;
+struct XclObjFillData;
+
+// Common =====================================================================
+
+struct XclImpChRootData;
+class XclImpChChart;
+class ScTokenArray;
+
+/** Base class for complex chart classes, provides access to other components of the chart. */
+class XclImpChRoot : public XclImpRoot
+{
+public:
+ explicit XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData );
+ virtual ~XclImpChRoot() override;
+
+ XclImpChRoot(XclImpChRoot const &) = default;
+ XclImpChRoot(XclImpChRoot &&) = default;
+ XclImpChRoot & operator =(XclImpChRoot const &) = delete; // due to XclImpRoot
+ XclImpChRoot & operator =(XclImpChRoot &&) = delete; // due to XclImpRoot
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclImpChRoot& GetChRoot() const { return *this; }
+ /** Returns a reference to the parent chart data object. */
+ XclImpChChart& GetChartData() const;
+ /** Returns chart type info for a unique chart type identifier. */
+ const XclChTypeInfo& GetChartTypeInfo( XclChTypeId eType ) const;
+ /** Returns the first fitting chart type info for an Excel chart type record identifier. */
+ const XclChTypeInfo& GetChartTypeInfo( sal_uInt16 nRecId ) const;
+ /** Returns an info struct about auto formatting for the passed object type. */
+ const XclChFormatInfo& GetFormatInfo( XclChObjectType eObjType ) const;
+
+ /** Returns the default text color for charts. */
+ Color GetFontAutoColor() const;
+ /** Returns the automatic line color of linear series. */
+ Color GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const;
+ /** Returns the automatic fill color of filled series. */
+ Color GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const;
+
+ /** Starts the API chart document conversion. Must be called once before all API conversion. */
+ void InitConversion(
+ const css::uno::Reference< css::chart2::XChartDocument>& xChartDoc,
+ const tools::Rectangle& rChartRect ) const;
+
+ /** Finishes the API chart document conversion. Must be called once after all API conversion. */
+ void FinishConversion( XclImpDffConverter& rDffConv ) const;
+
+ /** Returns the data provider for the chart document. */
+ css::uno::Reference< css::chart2::data::XDataProvider >
+ GetDataProvider() const;
+ /** Returns the drawing shape interface of the specified title object. */
+ css::uno::Reference< css::drawing::XShape >
+ GetTitleShape( const XclChTextKey& rTitleKey ) const;
+
+ /** Converts the passed horizontal coordinate from Excel chart units into 1/100 mm. */
+ sal_Int32 CalcHmmFromChartX( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from Excel chart units into 1/100 mm. */
+ sal_Int32 CalcHmmFromChartY( sal_Int32 nPosY ) const;
+ /** Converts the passed rectangle from Excel chart units into 1/100 mm. */
+ css::awt::Rectangle CalcHmmFromChartRect( const XclChRectangle& rRect ) const;
+
+ /** Converts the passed horizontal coordinate from 1/100 mm into a relative position. */
+ double CalcRelativeFromHmmX( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from 1/100 mm into a relative position. */
+ double CalcRelativeFromHmmY( sal_Int32 nPosY ) const;
+
+ /** Converts the passed horizontal coordinate from Excel chart units into a relative position. */
+ double CalcRelativeFromChartX( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from Excel chart units into a relative position. */
+ double CalcRelativeFromChartY( sal_Int32 nPosY ) const;
+
+ /** Writes all line properties to the passed property set. */
+ void ConvertLineFormat(
+ ScfPropertySet& rPropSet,
+ const XclChLineFormat& rLineFmt,
+ XclChPropertyMode ePropMode ) const;
+ /** Writes solid area properties to the passed property set. */
+ void ConvertAreaFormat(
+ ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt,
+ XclChPropertyMode ePropMode ) const;
+ /** Writes gradient or bitmap area properties to the passed property set. */
+ void ConvertEscherFormat(
+ ScfPropertySet& rPropSet,
+ const XclChEscherFormat& rEscherFmt,
+ const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType,
+ XclChPropertyMode ePropMode ) const;
+ /** Writes font properties to the passed property set. */
+ void ConvertFont(
+ ScfPropertySet& rPropSet,
+ sal_uInt16 nFontIdx,
+ const Color* pFontColor = nullptr ) const;
+
+ /** Writes the pie rotation property for the passed angle. */
+ static void ConvertPieRotation(
+ ScfPropertySet& rPropSet,
+ sal_uInt16 nAngle );
+
+private:
+ typedef std::shared_ptr< XclImpChRootData > XclImpChRootDataRef;
+ XclImpChRootDataRef mxChData; /// Reference to the root data object.
+};
+
+/** Base class for chart record groups. Provides helper functions to read sub records.
+
+ A chart record group consists of a header record, followed by a CHBEGIN
+ record, followed by group sub records, and finished with a CHEND record.
+ */
+class XclImpChGroupBase
+{
+public:
+ XclImpChGroupBase() = default;
+ XclImpChGroupBase(XclImpChGroupBase const &) = default;
+ XclImpChGroupBase(XclImpChGroupBase &&) = default;
+ XclImpChGroupBase & operator =(XclImpChGroupBase const &) = default;
+ XclImpChGroupBase & operator =(XclImpChGroupBase &&) = default;
+
+ virtual ~XclImpChGroupBase();
+
+ /** Reads the entire record group.
+ @descr First calls ReadHeaderRecord() to read the contents of the
+ header record. Then tries to read the sub records. If next record
+ is a CHBEGIN record, ReadSubRecord() is called for each following
+ record until a CHEND record is found. */
+ void ReadRecordGroup( XclImpStream& rStrm );
+
+ /** Helper to skip a CHBEGIN/CHEND block, includes nested blocks. */
+ static void SkipBlock( XclImpStream& rStrm );
+
+ /** Derived classes implement to read the group header record. */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) = 0;
+ /** Derived classes implement to read a record from the group. */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) = 0;
+};
+
+// Frame formatting ===========================================================
+
+class XclImpChFramePos
+{
+public:
+ /** Reads the CHFRAMEPOS record (frame position and size). */
+ void ReadChFramePos( XclImpStream& rStrm );
+
+ /** Returns read-only access to the imported frame position data. */
+ const XclChFramePos& GetFramePosData() const { return maData; }
+
+private:
+ XclChFramePos maData; /// Position of the frame.
+};
+
+typedef std::shared_ptr< XclImpChFramePos > XclImpChFramePosRef;
+
+/** The CHLINEFORMAT record containing line formatting data. */
+class XclImpChLineFormat : public salhelper::SimpleReferenceObject
+{
+public:
+ /** Creates a new line format object with automatic formatting. */
+ explicit XclImpChLineFormat() {}
+ /** Creates a new line format object with the passed formatting. */
+ explicit XclImpChLineFormat( const XclChLineFormat& rLineFmt ) : maData( rLineFmt ) {}
+
+ // this class is stored both ref-counted and by value
+ XclImpChLineFormat(XclImpChLineFormat const & rOther)
+ : salhelper::SimpleReferenceObject(), maData(rOther.maData) {}
+ XclImpChLineFormat(XclImpChLineFormat && rOther)
+ : salhelper::SimpleReferenceObject(), maData(std::move(rOther.maData)) {}
+ XclImpChLineFormat& operator=(XclImpChLineFormat const & rOther)
+ { maData = rOther.maData; return *this; }
+ XclImpChLineFormat& operator=(XclImpChLineFormat && rOther) noexcept
+ { maData = std::move(rOther.maData); return *this; }
+
+ /** Reads the CHLINEFORMAT record (basic line properties). */
+ void ReadChLineFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the line format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO ); }
+ /** Returns true, if the line style is set to something visible. */
+ bool HasLine() const { return IsAuto() || (maData.mnPattern != EXC_CHLINEFORMAT_NONE); }
+ /** Returns the line width of this line format (returns 'single', if the line is invisible). */
+ sal_Int16 GetWeight() const { return (IsAuto() || !HasLine()) ? EXC_CHLINEFORMAT_SINGLE : maData.mnWeight; }
+ /** Returns true, if the "show axis" flag is set. */
+ bool IsShowAxis() const { return ::get_flag( maData.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS ); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN ) const;
+
+private:
+ XclChLineFormat maData; /// Contents of the CHLINEFORMAT record.
+};
+
+typedef rtl::Reference< XclImpChLineFormat > XclImpChLineFormatRef;
+
+/** The CHAREAFORMAT record containing simple area formatting data (solid or patterns). */
+class XclImpChAreaFormat
+{
+public:
+ /** Creates a new area format object with automatic formatting. */
+ explicit XclImpChAreaFormat() {}
+ /** Creates a new area format object with the passed formatting. */
+ explicit XclImpChAreaFormat( const XclChAreaFormat& rAreaFmt ) : maData( rAreaFmt ) {}
+
+ /** Reads the CHAREAFORMAT record (basic fill properties, e.g. transparent or colored). */
+ void ReadChAreaFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the area format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO ); }
+ /** Returns true, if the area style is set to something visible. */
+ bool HasArea() const { return IsAuto() || (maData.mnPattern != EXC_PATT_NONE); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx ) const;
+
+private:
+ XclChAreaFormat maData; /// Contents of the CHAREAFORMAT record.
+};
+
+typedef std::shared_ptr< XclImpChAreaFormat > XclImpChAreaFormatRef;
+
+/** The CHESCHERFORMAT record containing complex area formatting data (bitmaps, hatches). */
+class XclImpChEscherFormat : public XclImpChGroupBase
+{
+public:
+ explicit XclImpChEscherFormat( const XclImpRoot& rRoot );
+
+ /** Reads the CHESCHERFORMAT record (complex fill data) (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHESCHERFORMAT group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet,
+ XclChObjectType eObjType, bool bUsePicFmt ) const;
+
+private:
+ XclChEscherFormat maData; /// Fill properties for complex areas (CHESCHERFORMAT record).
+ XclChPicFormat maPicFmt; /// Image options, e.g. stretched, stacked (CHPICFORMAT record).
+ sal_uInt32 mnDffFillType; /// Fill type imported from the DFF property set.
+};
+
+typedef std::shared_ptr< XclImpChEscherFormat > XclImpChEscherFormatRef;
+
+/** Base class for record groups containing frame formatting.
+
+ Frame formatting can be part of several record groups, e.g. CHFRAME,
+ CHDATAFORMAT, CHDROPBAR. It consists of CHLINEFORMAT, CHAREAFORMAT, and
+ CHESCHERFORMAT group.
+ */
+class XclImpChFrameBase : public XclImpChGroupBase
+{
+public:
+ /** Creates a new frame object without internal formatting objects. */
+ explicit XclImpChFrameBase() {}
+ /** Creates a new frame object with specific default formatting. */
+ explicit XclImpChFrameBase( const XclChFormatInfo& rFmtInfo );
+
+ /** Reads a frame formatting record (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Returns true, if the line format is set to automatic. */
+ bool IsAutoLine() const { return !mxLineFmt || mxLineFmt->IsAuto(); }
+ /** Returns true, if the line style is set to something visible. */
+ bool HasLine() const { return IsAutoLine() || mxLineFmt->HasLine(); }
+ /** Returns the line weight used for this frame. */
+ sal_Int16 GetLineWeight() const { return mxLineFmt ? mxLineFmt->GetWeight() : EXC_CHLINEFORMAT_SINGLE; }
+
+ /** Returns true, if the area format is set to automatic. */
+ bool IsAutoArea() const { return !mxEscherFmt && (!mxAreaFmt || mxAreaFmt->IsAuto()); }
+
+protected:
+ /** Converts and writes the contained line formatting to the passed property set. */
+ void ConvertLineBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN ) const;
+ /** Converts and writes the contained area formatting to the passed property set. */
+ void ConvertAreaBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN, bool bUsePicFmt = false ) const;
+ /** Converts and writes the contained data to the passed property set. */
+ void ConvertFrameBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN, bool bUsePicFmt = false ) const;
+
+protected:
+ XclImpChLineFormatRef mxLineFmt; /// Line format (CHLINEFORMAT record).
+ XclImpChAreaFormatRef mxAreaFmt; /// Area format (CHAREAFORMAT record).
+ XclImpChEscherFormatRef mxEscherFmt; /// Complex area format (CHESCHERFORMAT record).
+};
+
+/** Represents the CHFRAME record group containing object frame properties.
+
+ The CHFRAME group consists of: CHFRAME, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclImpChFrame : public XclImpChFrameBase, protected XclImpChRoot
+{
+public:
+ /** Creates a new frame object with specific default formatting. */
+ explicit XclImpChFrame(
+ const XclImpChRoot& rRoot,
+ XclChObjectType eObjType );
+
+ /** Reads the CHFRAME record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+
+ /** Sets formatting from BIFF3-BIFF5 OBJ record, if own formatting is invisible. */
+ void UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData );
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet, bool bUsePicFmt = false ) const;
+
+private:
+ XclChFrame maData; /// Contents of the CHFRAME record.
+ XclChObjectType meObjType; /// Type of the represented object.
+};
+
+typedef std::shared_ptr< XclImpChFrame > XclImpChFrameRef;
+
+// Source links ===============================================================
+
+class XclImpChSourceLink : protected XclImpChRoot
+{
+public:
+ explicit XclImpChSourceLink( const XclImpChRoot& rRoot );
+ virtual ~XclImpChSourceLink() override;
+
+ /** Reads the CHSOURCELINK record (link to source data). */
+ void ReadChSourceLink( XclImpStream& rStrm );
+ /** Sets explicit string data for this text object. */
+ void SetString( const OUString& rString );
+ /** Sets formatting runs read from a CHFORMATRUNS record. */
+ void SetTextFormats( XclFormatRunVec&& rFormats );
+
+ /** Returns the destination object (title, values, category, ...). */
+ sal_uInt8 GetDestType() const { return maData.mnDestType; }
+ /** Returns the link type (to worksheet, directly, default, ...). */
+ sal_uInt8 GetLinkType() const { return maData.mnLinkType; }
+
+ /** Returns true, if the source link contains explicit string data. */
+ bool HasString() const { return mxString && !mxString->IsEmpty(); }
+ /** Returns explicit string data or an empty string. */
+ OUString GetString() const {
+ if (mxString) return mxString->GetText();
+ return OUString();
+ }
+ /** Returns the number of data points of this source link. */
+ sal_uInt16 GetCellCount() const;
+
+ /** Converts and writes the contained number format to the passed property set. */
+ void ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const;
+
+ /** Creates a data sequence containing the link into the Calc document. */
+ css::uno::Reference< css::chart2::data::XDataSequence >
+ CreateDataSequence( const OUString& rRole ) const;
+ /** Creates a sequence of formatted string objects. */
+ css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >
+ CreateStringSequence( const XclImpChRoot& rRoot,
+ sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const;
+
+ void FillSourceLink(::std::vector<ScTokenRef>& rTokens) const;
+
+private:
+ XclChSourceLink maData; /// Contents of the CHSOURCELINK record.
+ XclImpStringRef mxString; /// Text data (CHSTRING record).
+ std::shared_ptr< ScTokenArray> mxTokenArray; /// Token array representing the data ranges.
+};
+
+typedef std::shared_ptr< XclImpChSourceLink > XclImpChSourceLinkRef;
+
+// Text =======================================================================
+
+/** Base class for objects with font settings. Provides font conversion helper functions. */
+class XclImpChFontBase
+{
+public:
+ XclImpChFontBase() = default;
+ XclImpChFontBase(XclImpChFontBase const &) = default;
+ XclImpChFontBase(XclImpChFontBase &&) = default;
+ XclImpChFontBase & operator =(XclImpChFontBase const &) = default;
+ XclImpChFontBase & operator =(XclImpChFontBase &&) = default;
+
+ virtual ~XclImpChFontBase();
+
+ /** Derived classes return the leading font index for the text object. */
+ virtual sal_uInt16 GetFontIndex() const = 0;
+ /** Derived classes return the leading font color for the text object. */
+ virtual Color GetFontColor() const = 0;
+ /** Derived classes return the rotation value for the text object. */
+ virtual sal_uInt16 GetRotation() const = 0;
+
+ /** Converts and writes the contained font settings to the passed property set. */
+ void ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const;
+ /** Converts and writes the contained rotation settings to the passed property set. */
+ void ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const;
+};
+
+/** The CHFONT record containing a font index for text objects. */
+class XclImpChFont
+{
+public:
+ explicit XclImpChFont();
+ /** Reads the CHFONT record (font index). */
+ void ReadChFont( XclImpStream& rStrm );
+
+ /** Returns the contained font index. */
+ sal_uInt16 GetFontIndex() const { return mnFontIdx; }
+
+private:
+ sal_uInt16 mnFontIdx; /// Index into font buffer.
+};
+
+typedef std::shared_ptr< XclImpChFont > XclImpChFontRef;
+
+/** Represents the CHTEXT record group containing text object properties.
+
+ The CHTEXT group consists of: CHTEXT, CHBEGIN, CHFRAMEPOS, CHFONT,
+ CHFORMATRUNS, CHSOURCELINK, CHSTRING, CHFRAME group, CHOBJECTLINK, and CHEND.
+ */
+class XclImpChText : public XclImpChGroupBase, public XclImpChFontBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChText( const XclImpChRoot& rRoot );
+
+ /** Reads the CHTEXT record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHTEXT group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Returns the leading font index for the text object. */
+ virtual sal_uInt16 GetFontIndex() const override;
+ /** Returns the leading font color for the text object. */
+ virtual Color GetFontColor() const override;
+ /** Returns the rotation value for the text object. */
+ virtual sal_uInt16 GetRotation() const override;
+
+ /** Sets explicit string data for this text object. */
+ void SetString( const OUString& rString );
+ /** Updates missing parts of this text object from the passed object. */
+ void UpdateText( const XclImpChText* pParentText );
+ /** Updates display type of this data point label text object. */
+ void UpdateDataLabel( bool bCateg, bool bValue, bool bPercent );
+
+ /** Returns the target object this text is linked to. */
+ sal_uInt16 GetLinkTarget() const { return maObjLink.mnTarget; }
+ /** Returns the position of the data point label this text is linked to. */
+ const XclChDataPointPos& GetPointPos() const { return maObjLink.maPointPos; }
+ /** Returns true, if this text group contains string data. */
+ bool HasString() const { return mxSrcLink && mxSrcLink->HasString(); }
+ /** Returns true, if the text object is marked as deleted. */
+ bool IsDeleted() const { return ::get_flag( maData.mnFlags, EXC_CHTEXT_DELETED ); }
+
+ /** Converts and writes the contained font settings to the passed property set. */
+ void ConvertFont( ScfPropertySet& rPropSet ) const;
+ /** Converts and writes the contained rotation settings to the passed property set. */
+ void ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const;
+ /** Converts and writes the contained frame data to the passed property set. */
+ void ConvertFrame( ScfPropertySet& rPropSet ) const;
+ /** Converts and writes the contained number format to the passed property set. */
+ void ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const;
+ /** Converts and writes all contained data to the passed data point label property set. */
+ void ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const;
+ /** Creates a title text object. */
+ css::uno::Reference< css::chart2::XTitle >
+ CreateTitle() const;
+ /** Converts the manual position of the specified title */
+ void ConvertTitlePosition( const XclChTextKey& rTitleKey ) const;
+
+private:
+ using XclImpChRoot::ConvertFont;
+
+ /** Reads a CHFRLABELPROPS record. */
+ void ReadChFrLabelProps( XclImpStream& rStrm );
+
+private:
+ typedef std::shared_ptr< XclChFrLabelProps > XclChFrLabelPropsRef;
+
+ XclChText maData; /// Contents of the CHTEXT record.
+ XclChObjectLink maObjLink; /// Link target for this text object.
+ XclFormatRunVec maFormats; /// Formatting runs (CHFORMATRUNS record).
+ XclImpChFramePosRef mxFramePos; /// Relative text frame position (CHFRAMEPOS record).
+ XclImpChSourceLinkRef mxSrcLink; /// Linked data (CHSOURCELINK with CHSTRING record).
+ XclImpChFrameRef mxFrame; /// Text object frame properties (CHFRAME group).
+ XclImpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclChFrLabelPropsRef mxLabelProps; /// Extended data label properties (CHFRLABELPROPS record).
+};
+
+typedef std::shared_ptr< XclImpChText > XclImpChTextRef;
+
+// Data series ================================================================
+
+/** The CHMARKERFORMAT record containing data point marker formatting data. */
+class XclImpChMarkerFormat
+{
+public:
+ /** Reads the CHMARKERFORMAT record (data point marker properties). */
+ void ReadChMarkerFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the marker format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO ); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet,
+ sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const;
+ /** Sets the marker fill color as main color to the passed property set. */
+ void ConvertColor( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const;
+
+private:
+ XclChMarkerFormat maData; /// Contents of the CHMARKERFORMAT record.
+};
+
+typedef std::shared_ptr< XclImpChMarkerFormat > XclImpChMarkerFormatRef;
+
+/** The CHPIEFORMAT record containing data point formatting data for pie segments. */
+class XclImpChPieFormat
+{
+public:
+ explicit XclImpChPieFormat();
+ /** Reads the CHPIEFORMAT record (pie segment properties). */
+ void ReadChPieFormat( XclImpStream& rStrm );
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet ) const;
+
+private:
+ sal_uInt16 mnPieDist; /// Pie distance to diagram center.
+};
+
+typedef std::shared_ptr< XclImpChPieFormat > XclImpChPieFormatRef;
+
+/** The CHSERIESFORMAT record containing additional settings for a data series. */
+class XclImpChSeriesFormat
+{
+public:
+ explicit XclImpChSeriesFormat();
+ /** Reads the CHSERIESFORMAT record (additional settings for a series). */
+ void ReadChSeriesFormat( XclImpStream& rStrm );
+ /** Returns true, if the series line is smoothed. */
+ bool HasSpline() const { return ::get_flag( mnFlags, EXC_CHSERIESFORMAT_SMOOTHED ); }
+
+private:
+ sal_uInt16 mnFlags; /// Additional flags.
+};
+
+typedef std::shared_ptr< XclImpChSeriesFormat > XclImpChSeriesFormatRef;
+
+/** The CH3DDATAFORMAT record containing the bar type in 3D bar charts. */
+class XclImpCh3dDataFormat
+{
+public:
+ /** Reads the CH3DDATAFORMAT record (3D bar properties). */
+ void ReadCh3dDataFormat( XclImpStream& rStrm );
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet ) const;
+
+private:
+ XclCh3dDataFormat maData; /// Contents of the CH3DDATAFORMAT record.
+};
+
+typedef std::shared_ptr< XclImpCh3dDataFormat > XclImpCh3dDataFormatRef;
+
+/** The CHATTACHEDLABEL record that contains the type of a data point label. */
+class XclImpChAttachedLabel : protected XclImpChRoot
+{
+public:
+ explicit XclImpChAttachedLabel( const XclImpChRoot& rRoot );
+ /** Reads the CHATTACHEDLABEL record (data series/point labels). */
+ void ReadChAttachedLabel( XclImpStream& rStrm );
+ /** Creates a CHTEXT group for the label. Clones xParentText and sets additional label settings */
+ XclImpChTextRef CreateDataLabel( const XclImpChText* pParent ) const;
+
+private:
+ sal_uInt16 mnFlags; /// Additional flags.
+};
+
+typedef std::shared_ptr< XclImpChAttachedLabel > XclImpChAttLabelRef;
+
+/** Represents the CHDATAFORMAT record group containing data point properties.
+
+ The CHDATAFORMAT group consists of: CHDATAFORMAT, CHBEGIN, CHFRAME group,
+ CHMARKERFORMAT, CHPIEFORMAT, CH3DDATAFORMAT, CHSERIESFORMAT,
+ CHATTACHEDLABEL, CHEND.
+ */
+class XclImpChDataFormat : public XclImpChFrameBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChDataFormat( const XclImpChRoot& rRoot );
+
+ /** Reads the CHDATAFORMAT record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHDATAFORMAT group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Sets this object to the specified data point position. */
+ void SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx );
+ /** Sets type and text formatting for a data point label (CHTEXT group). */
+ void SetDataLabel( XclImpChTextRef xLabel ) { mxLabel = xLabel; }
+
+ /** Updates default data format for series group. */
+ void UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo );
+ /** Updates missing series settings from the passed chart type group data format. */
+ void UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt );
+ /** Updates missing data point settings from the passed series format. */
+ void UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt );
+ /** Updates default data format for trend lines. */
+ void UpdateTrendLineFormat();
+
+ /** Returns the position of the data point described by this group. */
+ const XclChDataPointPos& GetPointPos() const { return maData.maPointPos; }
+ /** Returns the format index of the data point described by this group. */
+ sal_uInt16 GetFormatIdx() const { return maData.mnFormatIdx; }
+ /** Returns true, if markers are set to automatic format. */
+ bool IsAutoMarker() const { return !mxMarkerFmt || mxMarkerFmt->IsAuto(); }
+ /** Returns true, if the series line is smoothed. */
+ bool HasSpline() const { return mxSeriesFmt && mxSeriesFmt->HasSpline(); }
+ /** Returns the data label text object. */
+ const XclImpChText* GetDataLabel() const { return mxLabel.get(); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet = nullptr ) const;
+ /** Writes the line format only, e.g. for trend lines or error bars. */
+ void ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const;
+ /** Writes the area format only for the series or a data point. */
+ void ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const;
+
+private:
+ /** Removes unused formatting (e.g. pie distance in a bar chart). */
+ void RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo );
+ /** Updates or creates the data point label. */
+ void UpdateDataLabel( const XclImpChDataFormat* pParentFmt );
+
+private:
+ XclChDataFormat maData; /// Contents of the CHDATAFORMAT record.
+ XclImpChMarkerFormatRef mxMarkerFmt; /// Data point marker (CHMARKERFORMAT record).
+ XclImpChPieFormatRef mxPieFmt; /// Pie segment format (CHPIEFORMAT record).
+ XclImpChSeriesFormatRef mxSeriesFmt; /// Series properties (CHSERIESFORMAT record).
+ XclImpCh3dDataFormatRef mx3dDataFmt; /// 3D bar format (CH3DDATAFORMAT record).
+ XclImpChAttLabelRef mxAttLabel; /// Data point label type (CHATTACHEDLABEL record).
+ XclImpChTextRef mxLabel; /// Data point label formatting (CHTEXT group).
+};
+
+typedef std::shared_ptr< XclImpChDataFormat > XclImpChDataFormatRef;
+
+/** Represents the CHSERTRENDLINE record containing settings for a trend line. */
+class XclImpChSerTrendLine : protected XclImpChRoot
+{
+public:
+ explicit XclImpChSerTrendLine( const XclImpChRoot& rRoot );
+
+ /** Reads the CHSERTRENDLINE record. */
+ void ReadChSerTrendLine( XclImpStream& rStrm );
+ /** Sets formatting information for the trend line. */
+ void SetDataFormat( XclImpChDataFormatRef xDataFmt ) { mxDataFmt = xDataFmt; }
+
+ void SetTrendlineName( const OUString& aTrendlineName) { maTrendLineName = aTrendlineName; }
+
+ /** Creates an API object representing this trend line. */
+ css::uno::Reference< css::chart2::XRegressionCurve >
+ CreateRegressionCurve() const;
+
+private:
+ OUString maTrendLineName;
+ XclChSerTrendLine maData; /// Contents of the CHSERTRENDLINE record.
+ XclImpChDataFormatRef mxDataFmt; /// Formatting settings of the trend line.
+};
+
+typedef std::shared_ptr< XclImpChSerTrendLine > XclImpChSerTrendLineRef;
+
+/** Represents the CHSERERRORBAR record containing settings for error bars. */
+class XclImpChSerErrorBar : protected XclImpChRoot
+{
+public:
+ explicit XclImpChSerErrorBar( const XclImpChRoot& rRoot );
+
+ /** Reads the CHSERERRORBAR record. */
+ void ReadChSerErrorBar( XclImpStream& rStrm );
+ /** Sets link and formatting information for the error bars. */
+ void SetSeriesData(
+ XclImpChSourceLinkRef const & xValueLink,
+ XclImpChDataFormatRef const & xDataFmt );
+
+ /** Returns the type of this error bar (X/Y, plus/minus). */
+ sal_uInt8 GetBarType() const { return maData.mnBarType; }
+ /** Creates a labeled data sequence object from value data link. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateValueSequence() const;
+
+ /** Tries to create an error bar API object from the specified Excel error bars. */
+ static css::uno::Reference< css::beans::XPropertySet >
+ CreateErrorBar(
+ const XclImpChSerErrorBar* pPosBar,
+ const XclImpChSerErrorBar* pNegBar );
+
+private:
+ XclChSerErrorBar maData; /// Contents of the CHSERERRORBAR record.
+ XclImpChSourceLinkRef mxValueLink; /// Link data for manual error bar values.
+ XclImpChDataFormatRef mxDataFmt; /// Formatting settings of the error bars.
+};
+
+
+/** Represents the CHSERIES record group describing a data series in a chart.
+
+ The CHSERIES group consists of: CHSERIES, CHBEGIN, CHSOURCELINK groups,
+ CHDATAFORMAT groups, CHSERGROUP, CHSERPARENT, CHSERERRORBAR,
+ CHSERTRENDLINE, CHEND.
+ */
+class XclImpChSeries : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx );
+
+ /** Reads the CHSERIES record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHSERIES group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Sets a data point or series format (CHDATAFORMAT group) for this series. */
+ void SetDataFormat( const XclImpChDataFormatRef& xDataFmt );
+ /** Sets a label text (CHTEXT group) attached to a series or data point. */
+ void SetDataLabel( const XclImpChTextRef& xLabel );
+ /** Adds error bar settings from the passed series to the own series. */
+ void AddChildSeries( const XclImpChSeries& rSeries );
+ /** Updates missing series formatting by using default formatting from axes sets. */
+ void FinalizeDataFormats();
+
+ /** Returns the axes set identifier this series is assigned to (primary/secondary). */
+ sal_uInt16 GetGroupIdx() const { return mnGroupIdx; }
+ /** Returns the 0-based index of the parent series (e.g. of a trend line). */
+ sal_uInt16 GetParentIdx() const { return mnParentIdx; }
+ /** Returns true, if the series is child of another series (e.g. trend line). */
+ bool HasParentSeries() const { return mnParentIdx != EXC_CHSERIES_INVALID; }
+ /** Returns true, if the series contains child series (e.g. trend lines). */
+ bool HasChildSeries() const { return !maTrendLines.empty() || !m_ErrorBars.empty(); }
+ /** Returns series title or an empty string, if the series does not contain a title. */
+ OUString GetTitle() const { return mxTitleLink ? mxTitleLink->GetString() : OUString(); }
+
+ /** Returns true, if the series line is smoothed. */
+ bool HasSpline() const { return mxSeriesFmt && mxSeriesFmt->HasSpline(); }
+
+ /** Creates a labeled data sequence object from value data link. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateValueSequence( const OUString& rValueRole ) const;
+ /** Creates a labeled data sequence object from category data link. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateCategSequence( const OUString& rCategRole ) const;
+ /** Creates a data series object with initialized source links. */
+ css::uno::Reference< css::chart2::XDataSeries >
+ CreateDataSeries() const;
+
+ void FillAllSourceLinks(::std::vector<ScTokenRef>& rTokens) const;
+
+private:
+ /** Reads a CHSOURCELINK record. */
+ void ReadChSourceLink( XclImpStream& rStrm );
+ /** Reads a CHDATAFORMAT group containing series and point formatting. */
+ void ReadChDataFormat( XclImpStream& rStrm );
+ /** Reads a CHSERPARENT record specifying the parent series of this series. */
+ void ReadChSerParent( XclImpStream& rStrm );
+ /** Reads a CHSERTRENDLINE record containing trend line settings. */
+ void ReadChSerTrendLine( XclImpStream& rStrm );
+ /** Reads a CHSERERRORBAR record containing error bar settings. */
+ void ReadChSerErrorBar( XclImpStream& rStrm );
+
+ void ReadChLegendException( XclImpStream& rStrm );
+ /** Creates a new CHDATAFORMAT group with the specified point index. */
+ XclImpChDataFormatRef CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx );
+
+ /** Converts all trend lines and inserts them into the passed API data series object. */
+ void ConvertTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries ) const;
+ /** Tries to create an error bar API object from the specified Excel error bars. */
+ css::uno::Reference< css::beans::XPropertySet >
+ CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const;
+
+private:
+ typedef ::std::map<sal_uInt16, XclImpChDataFormatRef> XclImpChDataFormatMap;
+ typedef ::std::map<sal_uInt16, XclImpChTextRef> XclImpChTextMap;
+ typedef ::std::map<sal_uInt8, std::unique_ptr<XclImpChSerErrorBar>> XclImpChSerErrorBarMap;
+
+ XclChSeries maData; /// Contents of the CHSERIES record.
+ XclImpChSourceLinkRef mxValueLink; /// Link data for series values.
+ XclImpChSourceLinkRef mxCategLink; /// Link data for series category names.
+ XclImpChSourceLinkRef mxTitleLink; /// Link data for series title.
+ XclImpChSourceLinkRef mxBubbleLink; /// Link data for series bubble sizes.
+ XclImpChDataFormatRef mxSeriesFmt; /// CHDATAFORMAT group for series format.
+ XclImpChDataFormatMap maPointFmts; /// CHDATAFORMAT groups for data point formats.
+ XclImpChTextMap maLabels; /// Data point labels (CHTEXT groups).
+ std::vector< XclImpChSerTrendLineRef > maTrendLines; /// Trend line settings (CHSERTRENDLINE records).
+ XclImpChSerErrorBarMap m_ErrorBars; /// Error bar settings (CHSERERRORBAR records).
+ sal_uInt16 mnGroupIdx; /// Chart type group (CHTYPEGROUP group) this series is assigned to.
+ sal_uInt16 mnSeriesIdx; /// 0-based series index.
+ sal_uInt16 mnParentIdx; /// 0-based index of parent series (trend lines and error bars).
+ bool mbLabelDeleted; /// Legend label deleted
+};
+
+typedef std::shared_ptr< XclImpChSeries > XclImpChSeriesRef;
+
+// Chart type groups ==========================================================
+
+class XclImpChType : protected XclImpChRoot
+{
+public:
+ explicit XclImpChType( const XclImpChRoot& rRoot );
+
+ /** Reads a chart type record (e.g. CHBAR, CHLINE, CHPIE, ...). */
+ void ReadChType( XclImpStream& rStrm );
+ /** Final processing after reading the entire chart. */
+ void Finalize( bool bStockChart );
+
+ /** Returns the record identifier of the chart type record. */
+ sal_uInt16 GetRecId() const { return mnRecId; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+ /** Returns true, if the series in this chart type group are stacked on each other (no percentage). */
+ bool IsStacked() const;
+ /** Returns true, if the series in this chart type group are stacked on each other as percentage. */
+ bool IsPercent() const;
+ /** Returns true, if chart type has category labels enabled (may be disabled in radar charts). */
+ bool HasCategoryLabels() const;
+
+ /** Creates a coordinate system according to the contained chart type. */
+ css::uno::Reference< css::chart2::XCoordinateSystem >
+ CreateCoordSystem( bool b3dChart ) const;
+ /** Creates and returns an object that represents the contained chart type. */
+ css::uno::Reference< css::chart2::XChartType >
+ CreateChartType( css::uno::Reference< css::chart2::XDiagram > const & xDiagram, bool b3dChart ) const;
+
+private:
+ XclChType maData; /// Contents of the chart type record.
+ sal_uInt16 mnRecId; /// Record identifier for chart type.
+ XclChTypeInfo maTypeInfo; /// Chart type info for the contained type.
+};
+
+/** Represents the CHCHART3D record that contains 3D view settings. */
+class XclImpChChart3d
+{
+public:
+ /** Reads the CHCHART3D record (properties for 3D charts). */
+ void ReadChChart3d( XclImpStream& rStrm );
+ /** Returns true, if the data points are clustered on the X axis. */
+ bool IsClustered() const { return ::get_flag( maData.mnFlags, EXC_CHCHART3D_CLUSTER ); }
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const;
+
+private:
+ XclChChart3d maData; /// Contents of the CHCHART3D record.
+};
+
+typedef std::shared_ptr< XclImpChChart3d > XclImpChChart3dRef;
+
+/** Represents the CHLEGEND record group describing the chart legend.
+
+ The CHLEGEND group consists of: CHLEGEND, CHBEGIN, CHFRAMEPOS, CHFRAME
+ group, CHTEXT group, CHEND.
+ */
+class XclImpChLegend : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChLegend( const XclImpChRoot& rRoot );
+
+ /** Reads the CHLEGEND record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHLEGEND group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Creates a new legend object. */
+ css::uno::Reference< css::chart2::XLegend >
+ CreateLegend() const;
+
+private:
+ XclChLegend maData; /// Contents of the CHLEGEND record.
+ XclImpChFramePosRef mxFramePos; /// Legend frame position (CHFRAMEPOS record).
+ XclImpChTextRef mxText; /// Legend text format (CHTEXT group).
+ XclImpChFrameRef mxFrame; /// Legend frame format (CHFRAME group).
+};
+
+typedef std::shared_ptr< XclImpChLegend > XclImpChLegendRef;
+
+/** Represents the CHDROPBAR record group describing pos/neg bars in line charts.
+
+ The CHDROPBAR group consists of: CHDROPBAR, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclImpChDropBar : public XclImpChFrameBase
+{
+public:
+ explicit XclImpChDropBar( sal_uInt16 nDropBar );
+
+ /** Reads the CHDROPBAR record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+
+ /** Converts and writes the contained frame data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const;
+
+private:
+ sal_uInt16 mnDropBar; /// Drop bar identifier, needed for auto format.
+ sal_uInt16 mnBarDist; /// Distance between bars (CHDROPBAR record).
+};
+
+
+/** Represents the CHTYPEGROUP record group describing a group of series.
+
+ The CHTYPEGROUP group consists of: CHTYPEGROUP, CHBEGIN, a chart type
+ record (e.g. CHBAR, CHLINE, CHAREA, CHPIE, ...), CHCHART3D, CHLEGEND group,
+ CHDEFAULTTEXT groups (CHDEFAULTTEXT with CHTEXT groups), CHDROPBAR groups,
+ CHCHARTLINE groups (CHCHARTLINE with CHLINEFORMAT), CHDATAFORMAT group,
+ CHEND.
+ */
+class XclImpChTypeGroup : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChTypeGroup( const XclImpChRoot& rRoot );
+
+ /** Reads the CHTYPEGROUP record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHTYPEGROUP group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Inserts a series attached to this chart type group.*/
+ void AddSeries( XclImpChSeriesRef const & xSeries );
+ /** Marks the passed format index as used. PopUnusedFormatIndex() will not return this index. */
+ void SetUsedFormatIndex( sal_uInt16 nFormatIdx );
+ /** Returns the next unused format index and marks it as used. */
+ sal_uInt16 PopUnusedFormatIndex();
+
+ /** Returns the index of this chart type group. */
+ sal_uInt16 GetGroupIdx() const { return maData.mnGroupIdx; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChExtTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+ /** Returns true, if this chart type group contains at least one valid series. */
+ bool IsValidGroup() const { return !maSeries.empty(); }
+ /** Returns true, if the series in this chart type group are stacked on each other as percentage. */
+ bool IsPercent() const { return maType.IsPercent(); }
+ /** Returns true, if the chart is three-dimensional. */
+ bool Is3dChart() const { return mxChart3d && maTypeInfo.mbSupports3d; }
+ /** Returns true, if chart type supports wall and floor format in 3d mode. */
+ bool Is3dWallChart() const { return Is3dChart() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE); }
+ /** Returns true, if the series in this chart type group are ordered on the Z axis. */
+ bool Is3dDeepChart() const { return Is3dWallChart() && mxChart3d && !mxChart3d->IsClustered(); }
+ /** Returns true, if category (X axis) labels are enabled (may be disabled in radar charts). */
+ bool HasCategoryLabels() const { return maType.HasCategoryLabels(); }
+ /** Returns true, if points of a series show varying automatic area format. */
+ bool HasVarPointFormat() const;
+ /** Returns true, if bars are connected with lines (stacked bar charts only). */
+ bool HasConnectorLines() const;
+
+ /** Returns the legend object. */
+ const XclImpChLegendRef& GetLegend() const { return mxLegend; }
+ /** Returns the default series data format. */
+ const XclImpChDataFormatRef& GetGroupFormat() const { return mxGroupFmt; }
+ /** Returns series title, if the chart type group contains only one single series. */
+ OUString GetSingleSeriesTitle() const;
+
+ /** Converts and writes all 3D settings to the passed diagram. */
+ void ConvertChart3d( ScfPropertySet& rPropSet ) const;
+ /** Creates a coordinate system according to the contained chart type. */
+ css::uno::Reference< css::chart2::XCoordinateSystem >
+ CreateCoordSystem() const;
+ /** Creates and returns an object that represents the contained chart type. */
+ css::uno::Reference< css::chart2::XChartType >
+ CreateChartType( css::uno::Reference< css::chart2::XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const;
+ /** Creates a labeled data sequence object for axis categories. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateCategSequence() const;
+
+private:
+ /** Reads a CHDROPBAR record group. */
+ void ReadChDropBar( XclImpStream& rStrm );
+ /** Reads a CHCHARTLINE record group. */
+ void ReadChChartLine( XclImpStream& rStrm );
+ /** Reads a CHDATAFORMAT record group (default series format). */
+ void ReadChDataFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the chart type group contains drop bar formats. */
+ bool HasDropBars() const { return !m_DropBars.empty(); }
+
+ /** Inserts the passed series into the chart type. Adds additional properties to the series. */
+ void InsertDataSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ css::uno::Reference< css::chart2::XDataSeries > const & xSeries,
+ sal_Int32 nApiAxesSetIdx ) const;
+ /** Creates all data series of any chart type except stock charts. */
+ void CreateDataSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx ) const;
+ /** Creates all data series of a stock chart. */
+ void CreateStockSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx ) const;
+
+private:
+ typedef ::std::vector< XclImpChSeriesRef > XclImpChSeriesVec;
+ typedef ::std::map<sal_uInt16, std::unique_ptr<XclImpChDropBar>> XclImpChDropBarMap;
+ typedef ::std::map<sal_uInt16, XclImpChLineFormat> XclImpChLineFormatMap;
+
+ XclChTypeGroup maData; /// Contents of the CHTYPEGROUP record.
+ XclImpChType maType; /// Chart type (e.g. CHBAR, CHLINE, ...).
+ XclChExtTypeInfo maTypeInfo; /// Extended chart type info.
+ XclImpChSeriesVec maSeries; /// Series attached to this chart type group (CHSERIES groups).
+ XclImpChSeriesRef mxFirstSeries; /// First series in this chart type group (CHSERIES groups).
+ XclImpChChart3dRef mxChart3d; /// 3D settings (CHCHART3D record).
+ XclImpChLegendRef mxLegend; /// Chart legend (CHLEGEND group).
+ XclImpChDropBarMap m_DropBars; /// Dropbars (CHDROPBAR group).
+ XclImpChLineFormatMap m_ChartLines; /// Global line formats (CHCHARTLINE group).
+ XclImpChDataFormatRef mxGroupFmt; /// Default format for all series (CHDATAFORMAT group).
+ std::set< sal_uInt16 >
+ maUnusedFormats; /// Contains unused format indexes for automatic colors.
+};
+
+typedef std::shared_ptr< XclImpChTypeGroup > XclImpChTypeGroupRef;
+
+// Axes =======================================================================
+
+class XclImpChLabelRange : protected XclImpChRoot
+{
+public:
+ explicit XclImpChLabelRange( const XclImpChRoot& rRoot );
+ /** Reads the CHLABELRANGE record (category axis scaling properties). */
+ void ReadChLabelRange( XclImpStream& rStrm );
+ /** Reads the CHDATERANGE record (date axis scaling properties). */
+ void ReadChDateRange( XclImpStream& rStrm );
+ /** Converts category axis scaling settings. */
+ void Convert( ScfPropertySet& rPropSet, css::chart2::ScaleData& rScaleData, bool bMirrorOrient ) const;
+ /** Converts position settings of this axis at a crossing axis. */
+ void ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const;
+
+private:
+ XclChLabelRange maLabelData; /// Contents of the CHLABELRANGE record.
+ XclChDateRange maDateData; /// Contents of the CHDATERANGE record.
+};
+
+typedef std::shared_ptr< XclImpChLabelRange > XclImpChLabelRangeRef;
+
+class XclImpChValueRange : protected XclImpChRoot
+{
+public:
+ explicit XclImpChValueRange( const XclImpChRoot& rRoot );
+ /** Reads the CHVALUERANGE record (numeric axis scaling properties). */
+ void ReadChValueRange( XclImpStream& rStrm );
+ /** Converts value axis scaling settings. */
+ void Convert( css::chart2::ScaleData& rScaleData, bool bMirrorOrient ) const;
+ /** Converts position settings of this axis at a crossing axis. */
+ void ConvertAxisPosition( ScfPropertySet& rPropSet ) const;
+
+private:
+ XclChValueRange maData; /// Contents of the CHVALUERANGE record.
+};
+
+typedef std::shared_ptr< XclImpChValueRange > XclImpChValueRangeRef;
+
+class XclImpChTick : protected XclImpChRoot
+{
+public:
+ explicit XclImpChTick( const XclImpChRoot& rRoot );
+ /** Reads the CHTICK record (axis ticks properties). */
+ void ReadChTick( XclImpStream& rStrm );
+
+ /** Returns true, if the axis shows attached labels. */
+ bool HasLabels() const { return maData.mnLabelPos != EXC_CHTICK_NOLABEL; }
+ /** Returns the leading font color for the axis labels. */
+ Color GetFontColor() const;
+ /** Returns the rotation value for the axis labels. */
+ sal_uInt16 GetRotation() const;
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet ) const;
+
+private:
+ XclChTick maData; /// Contents of the CHTICK record.
+};
+
+typedef std::shared_ptr< XclImpChTick > XclImpChTickRef;
+
+/** Represents the CHAXIS record group describing an entire chart axis.
+
+ The CHAXIS group consists of: CHAXIS, CHBEGIN, CHLABELRANGE, CHEXTRANGE,
+ CHVALUERANGE, CHFORMAT, CHTICK, CHFONT, CHAXISLINE groups (CHAXISLINE with
+ CHLINEFORMAT, CHAREAFORMAT, and CHESCHERFORMAT group), CHEND.
+ */
+class XclImpChAxis : public XclImpChGroupBase, public XclImpChFontBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType = EXC_CHAXIS_NONE );
+
+ /** Reads the CHAXIS record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHAXIS group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Returns the font index for the axis labels. */
+ virtual sal_uInt16 GetFontIndex() const override;
+ /** Returns the font color for the axis labels. */
+ virtual Color GetFontColor() const override;
+ /** Returns the rotation value for axis labels. */
+ virtual sal_uInt16 GetRotation() const override;
+
+ /** Returns the type of this axis. */
+ sal_uInt16 GetAxisType() const { return maData.mnType; }
+ /** Returns the axis dimension index used by the chart API. */
+ sal_Int32 GetApiAxisDimension() const { return maData.GetApiAxisDimension(); }
+
+ /** Creates an API axis object. */
+ css::uno::Reference< css::chart2::XAxis >
+ CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const;
+ /** Converts and writes 3D wall/floor properties to the passed property set. */
+ void ConvertWall( ScfPropertySet& rPropSet ) const;
+ /** Converts position settings of this axis at a crossing axis. */
+ void ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const;
+
+private:
+ /** Reads a CHAXISLINE record specifying the target for following line properties. */
+ void ReadChAxisLine( XclImpStream& rStrm );
+ /** Creates a CHFRAME object and stores it into the mxWallFrame member. */
+ void CreateWallFrame();
+
+private:
+ XclChAxis maData; /// Contents of the CHAXIS record.
+ XclImpChLabelRangeRef mxLabelRange; /// Category scaling (CHLABELRANGE record).
+ XclImpChValueRangeRef mxValueRange; /// Value scaling (CHVALUERANGE record).
+ XclImpChTickRef mxTick; /// Axis ticks (CHTICK record).
+ XclImpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclImpChLineFormatRef mxAxisLine; /// Axis line format (CHLINEFORMAT record).
+ XclImpChLineFormatRef mxMajorGrid; /// Major grid line format (CHLINEFORMAT record).
+ XclImpChLineFormatRef mxMinorGrid; /// Minor grid line format (CHLINEFORMAT record).
+ XclImpChFrameRef mxWallFrame; /// Wall/floor format (sub records of CHFRAME group).
+ sal_uInt16 mnNumFmtIdx; /// Index into number format buffer (CHFORMAT record).
+};
+
+typedef std::shared_ptr< XclImpChAxis > XclImpChAxisRef;
+
+/** Represents the CHAXESSET record group describing an axes set (X/Y/Z axes).
+
+ The CHAXESSET group consists of: CHAXESSET, CHBEGIN, CHFRAMEPOS, CHAXIS
+ groups, CHTEXT groups, CHPLOTFRAME group (CHPLOTFRAME with CHFRAME group),
+ CHTYPEGROUP group, CHEND.
+ */
+class XclImpChAxesSet : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId );
+
+ /** Reads the CHAXESSET record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHAXESSET group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Returns true, if this axes set exists (returns false if this is a dummy object). */
+ bool IsValidAxesSet() const { return !maTypeGroups.empty(); }
+ /** Returns the index of the axes set (primary/secondary). */
+ sal_uInt16 GetAxesSetId() const { return maData.mnAxesSetId; }
+ /** Returns the axes set index used by the chart API. */
+ sal_Int32 GetApiAxesSetIndex() const { return maData.GetApiAxesSetIndex(); }
+
+ /** Returns the outer plot area position, if existing. */
+ const XclImpChFramePosRef& GetPlotAreaFramePos() const { return mxFramePos; }
+ /** Returns the specified chart type group. */
+ XclImpChTypeGroupRef GetTypeGroup( sal_uInt16 nGroupIdx ) const;
+ /** Returns the first chart type group. */
+ XclImpChTypeGroupRef GetFirstTypeGroup() const;
+ /** Looks for a legend in all chart type groups and returns it. */
+ XclImpChLegendRef GetLegend() const;
+ /** Returns series title, if the axes set contains only one single series. */
+ OUString GetSingleSeriesTitle() const;
+
+ /** Creates a coordinate system and converts all series and axis settings. */
+ void Convert( css::uno::Reference< css::chart2::XDiagram > const & xDiagram ) const;
+ /** Converts the manual positions of all axis titles. */
+ void ConvertTitlePositions() const;
+
+private:
+ /** Reads a CHAXIS record group containing a single axis. */
+ void ReadChAxis( XclImpStream& rStrm );
+ /** Reads a CHTEXT record group containing an axis title. */
+ void ReadChText( XclImpStream& rStrm );
+ /** Reads the CHPLOTFRAME record group containing diagram area formatting. */
+ void ReadChPlotFrame( XclImpStream& rStrm );
+ /** Reads a CHTYPEGROUP record group containing chart type and chart settings. */
+ void ReadChTypeGroup( XclImpStream& rStrm );
+
+ /** Creates a coordinate system that contains all chart types for this axes set. */
+ css::uno::Reference< css::chart2::XCoordinateSystem >
+ CreateCoordSystem( css::uno::Reference< css::chart2::XDiagram > const & xDiagram ) const;
+ /** Creates and inserts an axis into the container and registers the coordinate system. */
+ void ConvertAxis( XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
+ css::uno::Reference< css::chart2::XCoordinateSystem > const & xCoordSystem,
+ const XclImpChAxis* pCrossingAxis ) const;
+ /** Creates and returns an API axis object. */
+ css::uno::Reference< css::chart2::XAxis >
+ CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const;
+ /** Writes all properties of the background area to the passed diagram. */
+ void ConvertBackground( css::uno::Reference< css::chart2::XDiagram > const & xDiagram ) const;
+
+private:
+ typedef ::std::map<sal_uInt16, XclImpChTypeGroupRef> XclImpChTypeGroupMap;
+
+ XclChAxesSet maData; /// Contents of the CHAXESSET record.
+ XclImpChFramePosRef mxFramePos; /// Outer plot area position (CHFRAMEPOS record).
+ XclImpChAxisRef mxXAxis; /// The X axis (CHAXIS group).
+ XclImpChAxisRef mxYAxis; /// The Y axis (CHAXIS group).
+ XclImpChAxisRef mxZAxis; /// The Z axis (CHAXIS group).
+ XclImpChTextRef mxXAxisTitle; /// The X axis title (CHTEXT group).
+ XclImpChTextRef mxYAxisTitle; /// The Y axis title (CHTEXT group).
+ XclImpChTextRef mxZAxisTitle; /// The Z axis title (CHTEXT group).
+ XclImpChFrameRef mxPlotFrame; /// Plot area (CHPLOTFRAME group).
+ XclImpChTypeGroupMap maTypeGroups; /// Chart type groups (CHTYPEGROUP group).
+};
+
+typedef std::shared_ptr< XclImpChAxesSet > XclImpChAxesSetRef;
+
+// The chart object ===========================================================
+
+/** Represents the CHCHART record group describing the chart contents.
+
+ The CHCHART group consists of: CHCHART, CHBEGIN, SCL, CHPLOTGROWTH, CHFRAME
+ group, CHSERIES groups, CHPROPERTIES, CHDEFAULTTEXT groups (CHDEFAULTTEXT
+ with CHTEXT groups), CHUSEDAXESSETS, CHAXESSET groups, CHTEXT groups, CHEND.
+ */
+class XclImpChChart : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChChart( const XclImpRoot& rRoot );
+ virtual ~XclImpChChart() override;
+
+ /** Reads the CHCHART record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHCHART group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Reads a CHDEFAULTTEXT group (default text formats). */
+ void ReadChDefaultText( XclImpStream& rStrm );
+ /** Reads a CHDATAFORMAT group describing a series format or a data point format. */
+ void ReadChDataFormat( XclImpStream& rStrm );
+
+ /** Sets formatting from BIFF3-BIFF5 OBJ record, if own formatting is invisible. */
+ void UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData );
+
+ /** Returns the specified chart type group. */
+ XclImpChTypeGroupRef GetTypeGroup( sal_uInt16 nGroupIdx ) const;
+ /** Returns the specified default text. */
+ const XclImpChText* GetDefaultText( XclChTextType eTextType ) const;
+ /** Returns true, if the plot area has benn moved and/or resized manually. */
+ bool IsManualPlotArea() const;
+ /** Returns the number of units on the progress bar needed for the chart. */
+ static std::size_t GetProgressSize() { return 2 * EXC_CHART_PROGRESS_SIZE; }
+
+ /** Converts and writes all properties to the passed chart. */
+ void Convert(
+ const css::uno::Reference< css::chart2::XChartDocument>& xChartDoc,
+ XclImpDffConverter& rDffConv,
+ const OUString& rObjName,
+ const tools::Rectangle& rChartRect ) const;
+
+private:
+ /** Reads a CHSERIES group (data series source and formatting). */
+ void ReadChSeries( XclImpStream& rStrm );
+ /** Reads a CHPROPERTIES record (global chart properties). */
+ void ReadChProperties( XclImpStream& rStrm );
+ /** Reads a CHAXESSET group (primary/secondary axes set). */
+ void ReadChAxesSet( XclImpStream& rStrm );
+ /** Reads a CHTEXT group (chart title and series/point captions). */
+ void ReadChText( XclImpStream& rStrm );
+
+ /** Final processing after reading the entire chart data. */
+ void Finalize();
+ /** Finalizes series list, assigns child series to parent series. */
+ void FinalizeSeries();
+ /** Assigns all imported CHDATAFORMAT groups to the respective series. */
+ void FinalizeDataFormats();
+ /** Finalizes chart title, tries to detect title auto-generated from series name. */
+ void FinalizeTitle();
+
+ /** Creates and returns a new diagram object and converts global chart settings. */
+ css::uno::Reference<css::chart2::XDiagram>
+ CreateDiagram() const;
+
+private:
+ typedef ::std::vector< XclImpChSeriesRef > XclImpChSeriesVec;
+ typedef ::std::map<XclChDataPointPos, XclImpChDataFormatRef> XclImpChDataFormatMap;
+ typedef ::std::map<sal_uInt16, std::unique_ptr<XclImpChText>> XclImpChTextMap;
+
+ XclChRectangle maRect; /// Position of the chart on the sheet (CHCHART record).
+ XclImpChSeriesVec maSeries; /// List of series data (CHSERIES groups).
+ XclImpChDataFormatMap maDataFmts; /// All series and point formats (CHDATAFORMAT groups).
+ XclImpChFrameRef mxFrame; /// Chart frame format (CHFRAME group).
+ XclChProperties maProps; /// Chart properties (CHPROPERTIES record).
+ XclImpChTextMap m_DefTexts; /// Default text objects (CHDEFAULTTEXT groups).
+ XclImpChAxesSetRef mxPrimAxesSet; /// Primary axes set (CHAXESSET group).
+ XclImpChAxesSetRef mxSecnAxesSet; /// Secondary axes set (CHAXESSET group).
+ XclImpChTextRef mxTitle; /// Chart title (CHTEXT group).
+ XclImpChLegendRef mxLegend; /// Chart legend (CHLEGEND group).
+};
+
+typedef std::shared_ptr< XclImpChChart > XclImpChChartRef;
+
+/** Drawing container of a chart. */
+class XclImpChartDrawing : public XclImpDrawing
+{
+public:
+ explicit XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab );
+
+ /** Converts all objects and inserts them into the chart drawing page. */
+ void ConvertObjects(
+ XclImpDffConverter& rDffConv,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const tools::Rectangle& rChartRect );
+
+ /** Calculate the resulting rectangle of the passed anchor. */
+ virtual tools::Rectangle CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const override;
+ /** Called whenever an object has been inserted into the draw page. */
+ virtual void OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) override;
+
+private:
+ tools::Rectangle maChartRect; /// Position and size of the chart shape in 1/100 mm.
+ SCTAB mnScTab; /// Index of the sheet that contains the chart.
+ bool mbOwnTab; /// True = own sheet, false = embedded object.
+};
+
+/** Represents the entire chart substream (all records in BOF/EOF block). */
+class XclImpChart : protected XclImpRoot
+{
+public:
+ /** Constructs a new chart object.
+ @param bOwnTab True = chart is on an own sheet; false = chart is an embedded object. */
+ explicit XclImpChart( const XclImpRoot& rRoot, bool bOwnTab );
+ virtual ~XclImpChart() override;
+
+ /** Reads the complete chart substream (BOF/EOF block).
+ @descr The passed stream must be located in the BOF record of the chart substream. */
+ void ReadChartSubStream( XclImpStream& rStrm );
+ /** Sets formatting from BIFF3-BIFF5 OBJ record, if own formatting is invisible. */
+ void UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData );
+
+ /** Returns the number of units on the progress bar needed for the chart. */
+ std::size_t GetProgressSize() const;
+ /** Returns true, if the chart is based on a pivot table. */
+ bool IsPivotChart() const { return mbIsPivotChart; }
+
+ /** Creates the chart object in the passed component. */
+ void Convert( css::uno::Reference< css::frame::XModel > const & xModel,
+ XclImpDffConverter& rDffConv,
+ const OUString& rObjName,
+ const tools::Rectangle& rChartRect ) const;
+
+private:
+ /** Returns (initially creates) the drawing container for embedded shapes. **/
+ XclImpChartDrawing& GetChartDrawing();
+ /** Reads the CHCHART group (entire chart data). */
+ void ReadChChart( XclImpStream& rStrm );
+
+private:
+ typedef std::shared_ptr< XclImpChartDrawing > XclImpChartDrawingRef;
+
+ XclImpChChartRef mxChartData; /// The chart data (CHCHART group).
+ XclImpChartDrawingRef mxChartDrawing; /// Drawing container for embedded shapes.
+ bool mbOwnTab; /// true = own sheet; false = embedded object.
+ bool mbIsPivotChart; /// true = chart is based on a pivot table.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xicontent.hxx b/sc/source/filter/inc/xicontent.hxx
new file mode 100644
index 0000000000..fa0fc2a9fa
--- /dev/null
+++ b/sc/source/filter/inc/xicontent.hxx
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rangelst.hxx>
+#include "xistring.hxx"
+#include "xiroot.hxx"
+#include <validat.hxx>
+#include <tabprotection.hxx>
+
+#include <map>
+#include <vector>
+#include <memory>
+
+class ErrCode;
+struct XclRange;
+
+/* ============================================================================
+Classes to import the big Excel document contents (related to several cells or
+globals for the document).
+- Shared
+ tables
+- Hyperlinks
+- Label ranges
+- Conditional formatting
+- Data validation
+- Web queries
+- Stream decryption
+============================================================================ */
+
+// Shared string table ========================================================
+
+/** The SST (shared string table) contains all strings used in a BIFF8 file.
+
+ This class loads the SST, provides access to the strings, and is able to
+ create Calc string or edit cells.
+ */
+class XclImpSst : protected XclImpRoot
+{
+public:
+ explicit XclImpSst( const XclImpRoot& rRoot );
+
+ /** Reads the entire SST record.
+ @descr Import stream must be located at start of a SST record. */
+ void ReadSst( XclImpStream& rStrm );
+
+ /** Returns a pointer to the string with the passed index. */
+ const XclImpString* GetString( sal_uInt32 nSstIndex ) const;
+
+private:
+ typedef ::std::vector< XclImpString > XclImpStringVec;
+ XclImpStringVec maStrings; /// List with all strings in the SST.
+};
+
+// Hyperlinks =================================================================
+
+/** Provides importing hyperlinks and inserting them into a document. */
+class XclImpHyperlink
+{
+public:
+ /** delete copy constructor */
+ XclImpHyperlink(const XclImpHyperlink&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpHyperlink& operator=(const XclImpHyperlink&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpHyperlink() = delete;
+
+ /** Reads a HLINK record and inserts it into the document.
+ @descr Import stream must be located at start of a HLINK record. */
+ static void ReadHlink( XclImpStream& rStrm );
+
+ /** Reads the (undocumented) embedded hyperlink data and returns the URL. */
+ static OUString ReadEmbeddedData( XclImpStream& rStrm );
+
+ /** Inserts the URL into a range of cells. Does not modify value or formula cells. */
+ static void InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl );
+
+ /** Convert the sheet name with invalid character(s) in URL when the URL is
+ to a location within the same document (e.g. #'Sheet&Name'.A1). */
+ static void ConvertToValidTabName(OUString& rName);
+};
+
+// Label ranges ===============================================================
+
+/** Provides importing label ranges and inserting them into a document. */
+class XclImpLabelranges
+{
+public:
+ /** delete copy constructor */
+ XclImpLabelranges(const XclImpLabelranges&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpLabelranges& operator=(const XclImpLabelranges&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpLabelranges() = delete;
+ /** Reads a LABELRANGES record and inserts the label ranges into the document.
+ @descr Import stream must be located at start of a LABELRANGES record. */
+ static void ReadLabelranges( XclImpStream& rStrm );
+};
+
+// Conditional formatting =====================================================
+
+/** Represents a conditional format with condition formulas, and formatting attributes. */
+class XclImpCondFormat : protected XclImpRoot
+{
+public:
+ explicit XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex );
+ virtual ~XclImpCondFormat() override;
+
+ /** Reads a CONDFMT record and initializes this conditional format. */
+ void ReadCondfmt( XclImpStream& rStrm );
+ /** Reads a CF record and adds a new condition and the formatting attributes. */
+ void ReadCF( XclImpStream& rStrm );
+
+ /** Inserts this conditional format into the document. */
+ void Apply();
+
+private:
+ typedef ::std::unique_ptr< ScConditionalFormat > ScCondFmtPtr;
+
+ ScRangeList maRanges; /// Destination cell ranges.
+ ScCondFmtPtr mxScCondFmt; /// Calc conditional format.
+ sal_uInt32 mnFormatIndex; /// Index of this conditional format in list.
+ sal_uInt16 mnCondCount; /// Number of conditions to be inserted.
+ sal_uInt16 mnCondIndex; /// Condition index to be inserted next.
+};
+
+/** Imports and collects all conditional formatting of a sheet. */
+class XclImpCondFormatManager : protected XclImpRoot
+{
+public:
+ explicit XclImpCondFormatManager( const XclImpRoot& rRoot );
+
+ /** Reads a CONDFMT record and starts a new conditional format to be filled from CF records. */
+ void ReadCondfmt( XclImpStream& rStrm );
+ /** Reads a CF record and inserts the formatting data to the current conditional format. */
+ void ReadCF( XclImpStream& rStrm );
+
+ /** Inserts the conditional formatting into the document. */
+ void Apply();
+
+private:
+ std::vector< std::unique_ptr<XclImpCondFormat> >
+ maCondFmtList; /// List with all conditional formatting.
+};
+
+// Data Validation ============================================================
+
+/** Imports validation data. */
+class XclImpValidationManager : protected XclImpRoot
+{
+public:
+ explicit XclImpValidationManager( const XclImpRoot& rRoot );
+
+ /** Reads a DVAL record and sets marks the dropdown arrow control to be ignored. */
+ static void ReadDval( XclImpStream& rStrm );
+ /** Reads a DV record and inserts validation data into the document. */
+ void ReadDV( XclImpStream& rStrm );
+
+ void Apply();
+private:
+ struct DVItem
+ {
+ ScRangeList maRanges;
+ ScValidationData maValidData;
+
+ explicit DVItem ( ScRangeList aRanges, const ScValidationData& rValidData );
+ };
+
+ std::vector< std::unique_ptr<DVItem> > maDVItems;
+};
+
+// Web queries ================================================================
+
+/** Stores the data of one web query. */
+class XclImpWebQuery
+{
+public:
+ explicit XclImpWebQuery( const ScRange& rDestRange );
+
+ /** Reads a PARAMQRY record and sets data to the web query. */
+ void ReadParamqry( XclImpStream& rStrm );
+ /** Reads a WQSTRING record and sets URL. */
+ void ReadWqstring( XclImpStream& rStrm );
+ /** Reads a WEBQRYSETTINGS record and sets refresh rate. */
+ void ReadWqsettings( XclImpStream& rStrm );
+ /** Reads a WEBQRYTABLES record and sets source range list. */
+ void ReadWqtables( XclImpStream& rStrm );
+
+ /** Inserts the web query into the document. */
+ void Apply( ScDocument& rDoc, const OUString& rFilterName );
+
+private:
+ /** Specifies the type of the web query (which ranges are imported). */
+ enum XclImpWebQueryMode
+ {
+ xlWQUnknown, /// Not specified.
+ xlWQDocument, /// Entire document.
+ xlWQAllTables, /// All tables.
+ xlWQSpecTables /// Specific tables.
+ };
+
+ OUString maURL; /// Source document URL.
+ OUString maTables; /// List of source range names.
+ ScRange maDestRange; /// Destination range.
+ XclImpWebQueryMode meMode; /// Current mode of the web query.
+ sal_uInt16 mnRefresh; /// Refresh time in minutes.
+};
+
+class XclImpWebQueryBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpWebQueryBuffer( const XclImpRoot& rRoot );
+
+ /** Reads the QSI record and creates a new web query in the buffer. */
+ void ReadQsi( XclImpStream& rStrm );
+ /** Reads a PARAMQRY record and sets data to the current web query. */
+ void ReadParamqry( XclImpStream& rStrm );
+ /** Reads a WQSTRING record and sets URL to the current web query. */
+ void ReadWqstring( XclImpStream& rStrm );
+ /** Reads a WEBQRYSETTINGS record and sets refresh rate to the current web query. */
+ void ReadWqsettings( XclImpStream& rStrm );
+ /** Reads a WEBQRYTABLES record and sets source range list to the current web query. */
+ void ReadWqtables( XclImpStream& rStrm );
+
+ /** Inserts all web queries into the document. */
+ void Apply();
+
+private:
+ typedef std::vector< XclImpWebQuery > XclImpWebQueryList;
+ XclImpWebQueryList maWQList; /// List of the web query objects.
+};
+
+// Decryption =================================================================
+
+/** Provides static functions to import stream decryption settings. */
+class XclImpDecryptHelper
+{
+public:
+ /** delete copy constructor */
+ XclImpDecryptHelper(const XclImpDecryptHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpDecryptHelper& operator=(const XclImpDecryptHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpDecryptHelper() = delete;
+
+ /** Reads the FILEPASS record, queries a password and sets decryption algorithm.
+ @return Error code that may cause an error message after import. */
+ static const ErrCode& ReadFilepass( XclImpStream& rStrm );
+};
+
+// Document protection ========================================================
+
+class XclImpDocProtectBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpDocProtectBuffer( const XclImpRoot& rRoot );
+
+ /** document structure protection flag */
+ void ReadDocProtect( XclImpStream& rStrm );
+
+ /** document windows properties protection flag */
+ void ReadWinProtect( XclImpStream& rStrm );
+
+ void ReadPasswordHash( XclImpStream& rStrm );
+
+ void Apply() const;
+
+private:
+ sal_uInt16 mnPassHash;
+ bool mbDocProtect:1;
+ bool mbWinProtect:1;
+};
+
+// Sheet protection ===========================================================
+
+class XclImpSheetProtectBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpSheetProtectBuffer( const XclImpRoot& rRoot );
+
+ void ReadProtect( XclImpStream& rStrm, SCTAB nTab );
+
+ void ReadOptions( XclImpStream& rStrm, SCTAB nTab );
+
+ void AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab );
+
+ void ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab );
+
+ void Apply() const;
+
+private:
+ struct Sheet
+ {
+ bool mbProtected;
+ sal_uInt16 mnPasswordHash;
+ sal_uInt16 mnOptions;
+ ::std::vector< ScEnhancedProtection > maEnhancedProtections;
+
+ Sheet();
+ Sheet(const Sheet& r);
+ };
+
+ Sheet* GetSheetItem( SCTAB nTab );
+
+private:
+ typedef ::std::map<SCTAB, Sheet> ProtectedSheetMap;
+ ProtectedSheetMap maProtectedSheets;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiescher.hxx b/sc/source/filter/inc/xiescher.hxx
new file mode 100644
index 0000000000..cf09c161fe
--- /dev/null
+++ b/sc/source/filter/inc/xiescher.hxx
@@ -0,0 +1,1218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <filter/msfilter/msdffimp.hxx>
+#include <vcl/graph.hxx>
+#include "xlescher.hxx"
+#include "xiroot.hxx"
+#include <oox/ole/olehelper.hxx>
+#include <rtl/ustring.hxx>
+#include <svx/svdobj.hxx>
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace com::sun::star {
+ namespace drawing { class XShape; }
+ namespace form { class XForm; }
+}
+
+namespace com::sun::star::container { class XNameContainer; }
+
+class SdrObjList;
+class ScfProgressBar;
+class ScfPropertySet;
+class ScRangeList;
+class XclImpChart;
+class XclImpDffConverter;
+class XclImpDrawing;
+
+// Drawing objects ============================================================
+
+class XclImpDrawObjBase;
+typedef std::shared_ptr< XclImpDrawObjBase > XclImpDrawObjRef;
+
+/** Base class for drawing objects (OBJ records). */
+class XclImpDrawObjBase : protected XclImpRoot
+{
+public:
+ explicit XclImpDrawObjBase( const XclImpRoot& rRoot );
+ virtual ~XclImpDrawObjBase() override;
+
+ /** Reads the BIFF3 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm );
+ /** Reads the BIFF4 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm );
+ /** Reads the BIFF5 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm );
+ /** Reads the BIFF8 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm );
+
+ /** Sets whether this is an area object (then its width and height must be greater than 0). */
+ void SetAreaObj( bool bAreaObj ) { mbAreaObj = bAreaObj; }
+ /** If set to true, a new SdrObject will be created while in DFF import. */
+ void SetSimpleMacro( bool bMacro ) { mbSimpleMacro = bMacro; }
+
+ /** Sets the object anchor explicitly. */
+ void SetAnchor( const XclObjAnchor& rAnchor );
+ /** Sets shape data from DFF stream. */
+ void SetDffData(
+ const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink,
+ bool bVisible, bool bAutoMargin );
+
+ /** If set to false, the SdrObject will not be created, processed, or inserted into the draw page. */
+ void SetProcessSdrObj( bool bProcess ) { mbProcessSdr = bProcess; }
+ /** If set to false, the SdrObject will be created or processed, but not be inserted into the draw page. */
+ void SetInsertSdrObj( bool bInsert ) { mbInsertSdr = bInsert; }
+ /** If set to true, a new SdrObject will be created while in DFF import. */
+ void SetCustomDffObj( bool bCustom ) { mbCustomDff = bCustom; }
+
+ /** Returns the sheet index and Excel object identifier from OBJ record. */
+ sal_uInt16 GetObjId() const { return mnObjId; }
+ /** Returns the Excel object type from OBJ record. */
+ sal_uInt16 GetObjType() const { return mnObjType; }
+ /** Returns the name of this object, may generate a default name. */
+ virtual OUString GetObjName() const;
+ /** Returns associated macro name, if set, otherwise zero length string. */
+ const OUString& GetMacroName() const { return maMacroName; }
+
+ /** Returns the shape identifier used in the DFF stream. */
+ sal_uInt32 GetDffShapeId() const { return mnDffShapeId; }
+ /** Returns the shape flags from the DFF stream. */
+ ShapeFlag GetDffFlags() const { return mnDffFlags; }
+ const tools::Rectangle& GetDffRect() const;
+
+ /** Returns true, if the object is hidden. */
+ bool IsHidden() const { return mbHidden; }
+ /** Returns true, if the object is visible. */
+ bool IsVisible() const { return mbVisible; }
+ /** Returns true, if the object is printable. */
+ bool IsPrintable() const { return mbPrintable; }
+
+ /** Returns the object anchor if existing, null otherwise. */
+ const XclObjAnchor* GetAnchor() const;
+ /** Returns true, if the passed size is valid for this object. */
+ bool IsValidSize( const tools::Rectangle& rAnchorRect ) const;
+ /** Returns the range in the sheet covered by this object. */
+ ScRange GetUsedArea( SCTAB nScTab ) const;
+
+ /** Returns true, if the object is valid and will be processed. */
+ bool IsProcessSdrObj() const { return mbProcessSdr && !mbHidden; }
+ /** Returns true, if the SdrObject will be created or processed, but not be inserted into the draw page. */
+ bool IsInsertSdrObj() const { return mbInsertSdr; }
+
+ /** Returns the needed size on the progress bar (calls virtual DoGetProgressSize() function). */
+ std::size_t GetProgressSize() const;
+ /** Creates and returns an SdrObject from the contained data. Caller takes ownership! */
+ rtl::Reference<SdrObject> CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const;
+ /** Additional processing for the passed SdrObject before insertion into
+ the drawing page (calls virtual DoPreProcessSdrObj() function). */
+ void PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj );
+ /** Additional processing for the passed SdrObject after insertion into the
+ drawing page (calls virtual DoPostProcessSdrObj() function). */
+ void PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const;
+ SCTAB GetTab() const { return mnTab; }
+
+protected:
+ /** Reads the object name in a BIFF5 OBJ record. */
+ void ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen );
+ /** Reads the macro link in a BIFF3 OBJ record. */
+ void ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Reads the macro link in a BIFF4 OBJ record. */
+ void ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Reads the macro link in a BIFF5 OBJ record. */
+ void ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Reads the contents of the ftMacro sub structure in an OBJ record. */
+ void ReadMacro8( XclImpStream& rStrm );
+
+ /** Converts the passed line formatting to the passed SdrObject. */
+ void ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const;
+ /** Converts the passed fill formatting to the passed SdrObject. */
+ void ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const;
+ /** Converts the passed frame flags to the passed SdrObject. */
+ void ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const;
+
+ /** Returns a solid line color from the passed line data struct. */
+ Color GetSolidLineColor( const XclObjLineData& rLineData ) const;
+ /** Returns a solid fill color from the passed fill data struct. */
+ Color GetSolidFillColor( const XclObjFillData& rFillData ) const;
+
+ /** Derived classes read the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Derived classes read the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Derived classes read the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize );
+ /** Derived classes read the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize );
+
+ /** Derived classes may return a progress bar size different from 1. */
+ virtual std::size_t DoGetProgressSize() const;
+ /** Derived classes create and return a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const;
+ /** Derived classes may perform additional processing for the passed SdrObject before insertion. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const;
+ /** Derived classes may perform additional processing for the passed SdrObject after insertion. */
+ virtual void DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const;
+
+ /** Notify that the document contains a macro event handler */
+ void NotifyMacroEventRead();
+private:
+ /** Reads the contents of a BIFF3 OBJ record. */
+ void ImplReadObj3( XclImpStream& rStrm );
+ /** Reads the contents of a BIFF4 OBJ record. */
+ void ImplReadObj4( XclImpStream& rStrm );
+ /** Reads the contents of a BIFF5 OBJ record. */
+ void ImplReadObj5( XclImpStream& rStrm );
+ /** Reads the contents of a BIFF8 OBJ record. */
+ void ImplReadObj8( XclImpStream& rStrm );
+
+private:
+ XclObjAnchor maAnchor; /// The position of the object in its parent.
+ sal_uInt16 mnObjId; /// The object identifier (unique per drawing).
+ SCTAB mnTab; /// Location of object
+ sal_uInt16 mnObjType; /// The Excel object type from OBJ record.
+ sal_uInt32 mnDffShapeId; /// Shape ID from DFF stream.
+ ShapeFlag mnDffFlags; /// Shape flags from DFF stream.
+ tools::Rectangle maDffRect;
+ OUString maObjName; /// Name of the object.
+ OUString maMacroName; /// Name of an attached macro.
+ OUString maHyperlink; /// On-click hyperlink URL.
+ bool mbHasAnchor; /// true = maAnchor is initialized.
+ bool mbHidden; /// true = Object is hidden.
+ bool mbVisible; /// true = Object is visible.
+ bool mbPrintable; /// true = Object is printable.
+ bool mbAreaObj; /// true = Width and height must be greater than 0.
+ bool mbAutoMargin; /// true = Set automatic text margin.
+ bool mbSimpleMacro; /// true = Create simple macro link and hyperlink.
+ bool mbProcessSdr; /// true = Object is valid, do processing and insertion.
+ bool mbInsertSdr; /// true = Insert the SdrObject into draw page.
+ bool mbCustomDff; /// true = Recreate SdrObject in DFF import.
+ bool mbNotifyMacroEventRead; /// true == If we have already seen a macro event
+};
+
+class XclImpDrawObjVector
+{
+private:
+ std::vector< XclImpDrawObjRef > mObjs;
+
+public:
+ explicit XclImpDrawObjVector() : mObjs() {}
+
+ std::vector< XclImpDrawObjRef >::const_iterator begin() const { return mObjs.begin(); }
+ std::vector< XclImpDrawObjRef >::const_iterator end() const { return mObjs.end(); }
+ void push_back(const XclImpDrawObjRef& rObj) { mObjs.push_back(rObj); }
+
+ /** Tries to insert the passed object into the last group or appends it. */
+ void InsertGrouped( XclImpDrawObjRef const & xDrawObj );
+
+ /** Returns the needed size on the progress bar for all contained objects. */
+ std::size_t GetProgressSize() const;
+};
+
+/** A placeholder object for unknown object types. */
+class XclImpPhObj : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpPhObj( const XclImpRoot& rRoot );
+};
+
+/** A group object. */
+class XclImpGroupObj final : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpGroupObj( const XclImpRoot& rRoot );
+
+ /** Tries to insert the drawing object into this or a nested group. */
+ bool TryInsert( XclImpDrawObjRef const & xDrawObj );
+
+private:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Returns a progress bar size that takes all group children into account. */
+ virtual std::size_t DoGetProgressSize() const override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ XclImpDrawObjVector maChildren; /// Grouped objects.
+ sal_uInt16 mnFirstUngrouped; /// Object identifier of first object not grouped into this group.
+};
+
+/** A line object. */
+class XclImpLineObj final : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpLineObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ XclObjLineData maLineData; /// BIFF5 line formatting.
+ sal_uInt16 mnArrows; /// Line arrows.
+ sal_uInt8 mnStartPoint; /// Starting point.
+};
+
+/** A rectangle or oval object. */
+class XclImpRectObj : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpRectObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads fil data, line data, and frame flags. */
+ void ReadFrameData( XclImpStream& rStrm );
+
+ /** Converts fill formatting, line formatting, and frame style. */
+ void ConvertRectStyle( SdrObject& rSdrObj ) const;
+
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+protected:
+ XclObjFillData maFillData; /// BIFF5 fill formatting.
+ XclObjLineData maLineData; /// BIFF5 line formatting.
+ sal_uInt16 mnFrameFlags; /// Additional flags.
+};
+
+/** An oval object. */
+class XclImpOvalObj : public XclImpRectObj
+{
+public:
+ explicit XclImpOvalObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+};
+
+/** An arc object. */
+class XclImpArcObj final : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpArcObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ XclObjFillData maFillData; /// BIFF5 fill formatting.
+ XclObjLineData maLineData; /// BIFF5 line formatting.
+ sal_uInt8 mnQuadrant; /// Visible quadrant of the circle.
+};
+
+/** A polygon object. */
+class XclImpPolygonObj final : public XclImpRectObj
+{
+public:
+ explicit XclImpPolygonObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the COORDLIST record following the OBJ record. */
+ void ReadCoordList( XclImpStream& rStrm );
+
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ typedef std::vector< Point > PointVector;
+ PointVector maCoords; /// Coordinates relative to bounding rectangle.
+ sal_uInt16 mnPolyFlags; /// Additional flags.
+ sal_uInt16 mnPointCount; /// Polygon point count.
+};
+
+struct XclImpObjTextData
+{
+ XclObjTextData maData; /// BIFF5 text data.
+ XclImpStringRef mxString; /// Plain or rich string.
+
+ /** Reads a byte string from the passed stream. */
+ void ReadByteString( XclImpStream& rStrm );
+ /** Reads text formatting from the passed stream. */
+ void ReadFormats( XclImpStream& rStrm );
+};
+
+/** A drawing object supporting text contents. Used for all simple objects in BIFF8. */
+class XclImpTextObj : public XclImpRectObj
+{
+public:
+ explicit XclImpTextObj( const XclImpRoot& rRoot );
+
+ /** Stores the passed textbox data. */
+ void SetTextData( const XclImpObjTextData& rTextData ) { maTextData = rTextData; }
+
+protected:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Inserts the contained text data at the passed object. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+protected:
+ XclImpObjTextData maTextData; /// Textbox data from BIFF5 OBJ or BIFF8 TXO record.
+};
+
+/** A chart object. This is the drawing object wrapper for the chart data. */
+class XclImpChartObj : public XclImpRectObj
+{
+public:
+ /** @param bOwnTab True = chart is on an own sheet; false = chart is an embedded object. */
+ explicit XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab = false );
+
+ /** Reads the complete chart substream (BOF/EOF block). */
+ void ReadChartSubStream( XclImpStream& rStrm );
+
+protected:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Returns the needed size on the progress bar. */
+ virtual std::size_t DoGetProgressSize() const override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Converts the chart document. */
+ virtual void DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+private:
+ /** Calculates the object anchor of a sheet chart (chart fills one page). */
+ void FinalizeTabChart();
+
+private:
+ typedef std::shared_ptr< XclImpChart > XclImpChartRef;
+
+ XclImpChartRef mxChart; /// The chart itself (BOF/EOF substream data).
+ bool mbOwnTab; /// true = own sheet; false = embedded object.
+};
+
+/** A note object, which is a specialized text box object. */
+class XclImpNoteObj : public XclImpTextObj
+{
+public:
+ explicit XclImpNoteObj( const XclImpRoot& rRoot );
+
+ /** Sets note flags and the note position in the Calc sheet. */
+ void SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags );
+
+protected:
+ /** Inserts the note into the document, sets visibility. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+private:
+ ScAddress maScPos; /// Cell position of the note object.
+ sal_uInt16 mnNoteFlags; /// Flags from NOTE record.
+};
+
+/** Helper base class for TBX and OCX form controls to manage spreadsheet links. */
+class XclImpControlHelper
+{
+public:
+ explicit XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode );
+ virtual ~XclImpControlHelper();
+
+ /** Returns true, if a linked cell address is present. */
+ bool HasCellLink() const { return mxCellLink != nullptr; }
+
+ /** Returns the SdrObject from the passed control shape and sets the bounding rectangle. */
+ rtl::Reference<SdrObject> CreateSdrObjectFromShape(
+ const css::uno::Reference< css::drawing::XShape >& rxShape,
+ const tools::Rectangle& rAnchorRect ) const;
+
+ /** Sets additional properties to the form control model, calls virtual DoProcessControl(). */
+ void ProcessControl( const XclImpDrawObjBase& rDrawObj ) const;
+ void SetStringProperty(const OUString& sName, const OUString& sVal);
+
+protected:
+ /** Reads the formula for the linked cell from the current position of the stream. */
+ void ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize );
+ /** Reads the formula for the source range from the current position of the stream. */
+ void ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize );
+
+ /** Derived classes will set additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const;
+
+ void ApplySheetLinkProps() const;
+ mutable css::uno::Reference< css::drawing::XShape >
+ mxShape; /// The UNO wrapper of the control shape.
+ std::shared_ptr< ScAddress > mxCellLink; /// Linked cell in the Calc document.
+private:
+ /** Reads a list of cell ranges from a formula at the current stream position. */
+ void ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm );
+ /** Reads leading formula size and a list of cell ranges from a formula if the leading size is not zero. */
+ void ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize );
+
+private:
+ const XclImpRoot& mrRoot; /// Not derived from XclImpRoot to allow multiple inheritance.
+ std::shared_ptr< ScRange > mxSrcRange; /// Source data range in the Calc document.
+ XclCtrlBindMode meBindMode; /// Value binding mode.
+};
+
+/** Base class for textbox based form controls. */
+class XclImpTbxObjBase : public XclImpTextObj, public XclImpControlHelper
+{
+public:
+ explicit XclImpTbxObjBase( const XclImpRoot& rRoot );
+
+ /** Sets line and fill formatting from the passed DFF property set. */
+ void SetDffProperties( const DffPropSet& rDffPropSet );
+
+ /** Returns the service name of the control component to be created. */
+ OUString GetServiceName() const { return DoGetServiceName(); }
+ /** Fills the passed macro event descriptor. */
+ bool FillMacroDescriptor(
+ css::script::ScriptEventDescriptor& rDescriptor ) const;
+
+protected:
+ /** Sets control text formatting. */
+ void ConvertFont( ScfPropertySet& rPropSet ) const;
+ /** Sets control label and text formatting. */
+ void ConvertLabel( ScfPropertySet& rPropSet ) const;
+
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Additional processing on the SdrObject, calls new virtual function DoProcessControl(). */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+ /** Derived classes return the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const = 0;
+ /** Derived classes return the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const = 0;
+};
+
+/** A button control. */
+class XclImpButtonObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpButtonObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** A checkbox control. */
+class XclImpCheckBoxObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpCheckBoxObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+protected:
+ sal_uInt16 mnState;
+ sal_uInt16 mnCheckBoxFlags;
+};
+
+/** An option button control. */
+class XclImpOptionButtonObj final : public XclImpCheckBoxObj
+{
+public:
+ explicit XclImpOptionButtonObj( const XclImpRoot& rRoot );
+ bool IsInGroup() const;
+
+private:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnNextInGroup; /// Next option button in a group.
+ sal_uInt16 mnFirstInGroup; /// 1 = Button is the first in a group.
+};
+
+/** A label control. */
+class XclImpLabelObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpLabelObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** A groupbox control. */
+class XclImpGroupBoxObj final : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpGroupBoxObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnGroupBoxFlags;
+};
+
+/** A dialog control. */
+class XclImpDialogObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpDialogObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** An edit control. */
+class XclImpEditObj final : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpEditObj( const XclImpRoot& rRoot );
+
+private:
+ /** REturns true, if the field type is numeric. */
+ bool IsNumeric() const;
+
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnContentType;
+ sal_uInt16 mnMultiLine;
+ sal_uInt16 mnScrollBar;
+ sal_uInt16 mnListBoxObjId;
+};
+
+/** Base class of scrollable form controls (spin button, scrollbar, listbox, dropdown). */
+class XclImpTbxObjScrollableBase : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpTbxObjScrollableBase( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads scrollbar data. */
+ void ReadSbs( XclImpStream& rStrm );
+
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+
+protected:
+ sal_uInt16 mnValue;
+ sal_uInt16 mnMin;
+ sal_uInt16 mnMax;
+ sal_uInt16 mnStep;
+ sal_uInt16 mnPageStep;
+ sal_uInt16 mnOrient;
+ sal_uInt16 mnThumbWidth;
+ sal_uInt16 mnScrollFlags;
+};
+
+/** A spinbutton control. */
+class XclImpSpinButtonObj : public XclImpTbxObjScrollableBase
+{
+public:
+ explicit XclImpSpinButtonObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** A scrollbar control. */
+class XclImpScrollBarObj : public XclImpTbxObjScrollableBase
+{
+public:
+ explicit XclImpScrollBarObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** Base class for list controls (listbox, dropdown). */
+class XclImpTbxObjListBase : public XclImpTbxObjScrollableBase
+{
+public:
+ explicit XclImpTbxObjListBase( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads common listbox settings. */
+ void ReadLbsData( XclImpStream& rStrm );
+ /** Sets common listbox/dropdown formatting attributes. */
+ void SetBoxFormatting( ScfPropertySet& rPropSet ) const;
+
+protected:
+ sal_uInt16 mnEntryCount;
+ sal_uInt16 mnSelEntry;
+ sal_uInt16 mnListFlags;
+ sal_uInt16 mnEditObjId;
+ bool mbHasDefFontIdx;
+};
+
+/** A listbox control. */
+class XclImpListBoxObj final : public XclImpTbxObjListBase
+{
+public:
+ explicit XclImpListBoxObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads listbox settings and selection. */
+ void ReadFullLbsData( XclImpStream& rStrm, std::size_t nRecLeft );
+
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ ScfUInt8Vec maSelection;
+};
+
+/** A dropdown listbox control. */
+class XclImpDropDownObj final : public XclImpTbxObjListBase
+{
+public:
+ explicit XclImpDropDownObj( const XclImpRoot& rRoot );
+
+private:
+ /** Returns the type of the dropdown control. */
+ sal_uInt16 GetDropDownType() const;
+
+ /** Reads dropdown box settings. */
+ void ReadFullLbsData( XclImpStream& rStrm );
+
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnLeft;
+ sal_uInt16 mnTop;
+ sal_uInt16 mnRight;
+ sal_uInt16 mnBottom;
+ sal_uInt16 mnDropDownFlags;
+ sal_uInt16 mnLineCount;
+ sal_uInt16 mnMinWidth;
+};
+
+/** A picture, an embedded or linked OLE object, or an OCX form control. */
+class XclImpPictureObj : public XclImpRectObj, public XclImpControlHelper
+{
+public:
+ explicit XclImpPictureObj( const XclImpRoot& rRoot );
+ /** Returns the ObjectName - can use non-obvious lookup for override in the associated vba document module stream**/
+ virtual OUString GetObjName() const override;
+ /** Returns the graphic imported from the IMGDATA record. */
+ const Graphic& GetGraphic() const { return maGraphic; }
+
+ /** Returns true, if the OLE object will be shown as symbol. */
+ bool IsSymbol() const { return mbSymbol; }
+ /** Returns the storage name for the OLE object. */
+ OUString GetOleStorageName() const;
+
+ /** Returns true, if this object is an OCX form control. */
+ bool IsOcxControl() const { return mbEmbedded && mbControl && mbUseCtlsStrm; }
+ /** Returns the position in the 'Ctls' stream for additional form control data. */
+ std::size_t GetCtlsStreamPos() const { return mnCtlsStrmPos; }
+ /** Returns the size in the 'Ctls' stream for additional form control data. */
+ std::size_t GetCtlsStreamSize() const { return mnCtlsStrmSize; }
+
+protected:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual rtl::Reference<SdrObject> DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Override to do additional processing on the SdrObject. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+private:
+ /** Reads and sets the picture flags from a BIFF3-BIFF5 OBJ picture record. */
+ void ReadFlags3( XclImpStream& rStrm );
+ /** Reads the contents of the OBJFLAGS subrecord. */
+ void ReadFlags8( XclImpStream& rStrm );
+ /** Reads the contents of the OBJPICTFMLA subrecord. */
+ void ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize );
+
+private:
+ Graphic maGraphic; /// Picture or OLE placeholder graphic.
+ OUString maClassName; /// Class name of embedded OLE object.
+ sal_uInt32 mnStorageId; /// Identifier of the storage for this object.
+ std::size_t mnCtlsStrmPos; /// Position in 'Ctls' stream for this control.
+ std::size_t mnCtlsStrmSize; /// Size in 'Ctls' stream for this control.
+ bool mbEmbedded; /// true = Embedded OLE object.
+ bool mbLinked; /// true = Linked OLE object.
+ bool mbSymbol; /// true = Show as symbol.
+ bool mbControl; /// true = Form control, false = OLE object.
+ bool mbUseCtlsStrm; /// true = Form control data in 'Ctls' stream, false = Own storage.
+};
+
+// DFF stream conversion ======================================================
+
+/** The solver container collects all connector rules for connected objects. */
+class XclImpSolverContainer : public SvxMSDffSolverContainer
+{
+public:
+
+ /** Inserts information about a new SdrObject. */
+ void InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, ShapeFlag nDffFlags );
+ /** Removes information of an SdrObject (and all child objects if it is a group). */
+ void RemoveSdrObjectInfo( SdrObject& rSdrObj );
+
+ /** Inserts the SdrObject pointers into all connector rules. */
+ void UpdateConnectorRules();
+ /** Removes all contained connector rules. */
+ void RemoveConnectorRules();
+
+private:
+ /** Updates the data of a connected shape in a connector rule. */
+ void UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, ShapeFlag* pnDffFlags = nullptr );
+
+private:
+ /** Stores data about an SdrObject processed during import. */
+ struct XclImpSdrInfo
+ {
+ SdrObject* mpSdrObj; /// Pointer to an SdrObject.
+ ShapeFlag mnDffFlags; /// Shape flags from DFF stream.
+ explicit XclImpSdrInfo() : mpSdrObj( nullptr ), mnDffFlags( ShapeFlag::NONE ) {}
+ void Set( SdrObject* pSdrObj, ShapeFlag nDffFlags )
+ { mpSdrObj = pSdrObj; mnDffFlags = nDffFlags; }
+ };
+ typedef std::map< sal_uInt32, XclImpSdrInfo > XclImpSdrInfoMap;
+ typedef std::map< SdrObject*, sal_uInt32 > XclImpSdrObjMap;
+
+ XclImpSdrInfoMap maSdrInfoMap; /// Maps shape IDs to SdrObjects and flags.
+ XclImpSdrObjMap maSdrObjMap; /// Maps SdrObjects to shape IDs.
+};
+
+/** Simple implementation of the SVX DFF manager. Implements resolving palette
+ colors. Used by XclImpDffPropSet (as is), extended by XclImpDffConverter.
+ */
+class XclImpSimpleDffConverter : public SvxMSDffManager, protected XclImpRoot
+{
+public:
+ explicit XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm );
+ virtual ~XclImpSimpleDffConverter() override;
+
+protected:
+ /** Returns a color from the Excel color palette. */
+ virtual bool GetColorFromPalette( sal_uInt16 nIndex, Color& rColor ) const override;
+};
+
+/** This is the central instance for converting binary DFF data into shape
+ objects. Used for all sheet shapes and shapes embedded in chart objects.
+
+ The class derives from SvxMSDffManager and SvxMSConvertOCXControls and
+ contains core implementation of DFF stream import and OCX form control
+ import.
+ */
+class XclImpDffConverter : public XclImpSimpleDffConverter, private oox::ole::MSConvertOCXControls
+{
+public:
+ explicit XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm );
+ virtual ~XclImpDffConverter() override;
+
+ /** Initializes the internal progress bar with the passed size and starts it. */
+ void StartProgressBar( std::size_t nProgressSize );
+ /** Increase the progress bar by the passed value. */
+ void Progress( std::size_t nDelta = 1 );
+
+ /** Initially called before the objects of the passed drawing manager are converted. */
+ void InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage );
+ /** Processes BIFF5 drawing objects without DFF data, inserts into the passed object list. */
+ void ProcessObject( SdrObjList& rObjList, XclImpDrawObjBase& rDrawObj );
+ /** Processes all objects in the passed list. */
+ void ProcessDrawing( const XclImpDrawObjVector& rDrawObjs );
+ /** Processes a drawing container in the passed DFF stream, converts all objects. */
+ void ProcessDrawing( SvStream& rDffStrm );
+ /** Finally called after the objects of the passed drawing manager have been converted. */
+ void FinalizeDrawing();
+
+ /** Creates the SdrObject for the passed Excel TBX form control object. */
+ rtl::Reference<SdrObject> CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect );
+ /** Creates the SdrObject for the passed Excel OLE object or OCX form control object. */
+ rtl::Reference<SdrObject> CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect );
+
+ /** Returns true, if the conversion of OLE objects is supported. */
+ bool SupportsOleObjects() const;
+ /** Returns the default text margin in drawing layer units. */
+ sal_Int32 GetDefaultTextMargin() const { return mnDefTextMargin; }
+
+private:
+ // virtual functions of SvxMSDffManager
+
+ /** Reads the client anchor from the DFF stream and sets it at the correct object. */
+ virtual void ProcessClientAnchor2(
+ SvStream& rDffStrm,
+ DffRecordHeader& rHeader,
+ DffObjData& rObjData ) override;
+ /** Processes a DFF object, reads properties from DFF stream. */
+ virtual rtl::Reference<SdrObject> ProcessObj(
+ SvStream& rDffStrm,
+ DffObjData& rDffObjData,
+ SvxMSDffClientData& rClientData,
+ tools::Rectangle& rTextRect,
+ SdrObject* pOldSdrObj ) override;
+
+ /** Finalize a DFF object, sets anchor after nested objs have been loaded. */
+ virtual SdrObject* FinalizeObj(
+ DffObjData& rDffObjData,
+ SdrObject* pOldSdrObj ) override;
+
+ // virtual functions of SvxMSConvertOCXControls
+
+ /** Inserts the passed control rxFComp into the form. Needs call to SetCurrentForm() before. */
+ virtual bool InsertControl(
+ const css::uno::Reference<
+ css::form::XFormComponent >& rxFormComp,
+ const css::awt::Size& rSize,
+ css::uno::Reference<
+ css::drawing::XShape >* pxShape,
+ bool bFloatingCtrl ) override;
+
+private:
+ /** Data per registered drawing manager, will be stacked for recursive calls. */
+ struct XclImpDffConvData
+ {
+ XclImpDrawing& mrDrawing; /// Current drawing container with all drawing objects.
+ SdrModel& mrSdrModel; /// The SdrModel of the drawing manager.
+ SdrPage& mrSdrPage; /// The SdrPage of the drawing manager.
+ XclImpSolverContainer maSolverCont; /// The solver container for connector rules.
+ css::uno::Reference< css::form::XForm >
+ mxCtrlForm; /// Controls form of current drawing page.
+ sal_Int32 mnLastCtrlIndex; /// Last insertion index of a form control (for macro events).
+ bool mbHasCtrlForm; /// True = mxCtrlForm is initialized (but maybe still null).
+
+ explicit XclImpDffConvData( XclImpDrawing& rDrawing,
+ SdrModel& rSdrModel, SdrPage& rSdrPage );
+ };
+
+ /** Returns the current drawing manager data struct from top of the stack. */
+ XclImpDffConvData& GetConvData();
+ /** Returns the current drawing manager data struct from top of the stack. */
+ const XclImpDffConvData& GetConvData() const;
+
+ /** Reads contents of a hyperlink property and returns the extracted URL. */
+ OUString ReadHlinkProperty( SvStream& rDffStrm ) const;
+
+ /** Processes a drawing container (all drawing data of a sheet). */
+ bool ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader );
+ /** Processes the global shape group container (all shapes of a sheet). */
+ bool ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader );
+ /** Processes the solver container (connectors of a sheet). */
+ bool ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader );
+ /** Processes a shape or shape group container (one top-level shape). */
+ bool ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader );
+
+ /** Inserts the passed SdrObject into the document. This function takes ownership of pSdrObj! */
+ void InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj );
+ /** Initializes the mxCtrlForm referring to the standard controls form. */
+ void InitControlForm();
+ /** Notify that this document contains a macro event handler */
+ void NotifyMacroEventRead();
+
+private:
+ typedef std::shared_ptr< ScfProgressBar > ScfProgressBarRef;
+ typedef std::shared_ptr< XclImpDffConvData > XclImpDffConvDataRef;
+
+ tools::SvRef<SotStorageStream> mxCtlsStrm; /// The 'Ctls' stream for OCX form controls.
+ ScfProgressBarRef mxProgress; /// The progress bar used in ProcessObj().
+ std::vector< XclImpDffConvDataRef >
+ maDataStack; /// Stack for registered drawing managers.
+ sal_uInt32 mnOleImpFlags; /// Application OLE import settings.
+ sal_Int32 mnDefTextMargin; /// Default margin in text boxes.
+ bool mbNotifyMacroEventRead; /// If we have already seen a macro event
+};
+
+// Drawing manager ============================================================
+
+/** Base class for a container for all objects on a drawing (spreadsheet or
+ embedded chart object). */
+class XclImpDrawing : protected XclImpRoot
+{
+public:
+ explicit XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects );
+ virtual ~XclImpDrawing() override;
+
+ /** Reads and returns a bitmap from the IMGDATA record. */
+ static Graphic ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm );
+
+ /** Reads a plain OBJ record (without leading DFF data). */
+ void ReadObj( XclImpStream& rStrm );
+ /** Reads the MSODRAWING or MSODRAWINGSELECTION record. */
+ void ReadMsoDrawing( XclImpStream& rStrm );
+
+ /** Returns true, if the conversion of OLE objects is supported. */
+ bool SupportsOleObjects() const { return mbOleObjs; }
+ /** Finds the OBJ record data related to the DFF shape at the passed position. */
+ XclImpDrawObjRef FindDrawObj( const DffRecordHeader& rHeader ) const;
+ /** Finds the OBJ record data specified by the passed object identifier. */
+ XclImpDrawObjRef FindDrawObj( sal_uInt16 nObjId ) const;
+ /** Finds the textbox data related to the DFF shape at the passed position. */
+ const XclImpObjTextData* FindTextData( const DffRecordHeader& rHeader ) const;
+
+ void ApplyGroupBoxes();
+
+ /** Sets the object with the passed identification to be skipped on import. */
+ void SetSkipObj( sal_uInt16 nObjId );
+ /** Returns the size of the progress bar shown while processing all objects. */
+ std::size_t GetProgressSize() const;
+
+ /** Derived classes calculate the resulting rectangle of the passed anchor. */
+ virtual tools::Rectangle CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const = 0;
+ /** Called whenever an object has been inserted into the draw page. */
+ virtual void OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) = 0;
+
+protected:
+ /** Appends a new drawing object to the list of raw objects (without DFF data). */
+ void AppendRawObject( const XclImpDrawObjRef& rxDrawObj );
+ /** Converts all objects and inserts them into the current drawing page. */
+ void ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage );
+
+private:
+ /** Reads and returns a bitmap from WMF/PICT format. */
+ static void ReadWmf( Graphic& rGraphic, XclImpStream& rStrm );
+ /** Reads and returns a bitmap from BMP format. */
+ static void ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm );
+
+ /** Reads contents of a DFF record and append data to internal DFF stream. */
+ void ReadDffRecord( XclImpStream& rStrm );
+ /** Reads a BIFF8 OBJ record following an MSODRAWING record. */
+ void ReadObj8( XclImpStream& rStrm );
+ /** Reads the TXO record and following CONTINUE records containing string and formatting. */
+ void ReadTxo( XclImpStream& rStrm );
+
+private:
+ typedef std::map< std::size_t, XclImpDrawObjRef > XclImpObjMap;
+ typedef std::map< sal_uInt16, XclImpDrawObjRef > XclImpObjMapById;
+ typedef std::shared_ptr< XclImpObjTextData > XclImpObjTextRef;
+ typedef std::map< std::size_t, XclImpObjTextRef > XclImpObjTextMap;
+
+ XclImpDrawObjVector maRawObjs; /// BIFF5 objects without DFF data.
+ SvMemoryStream maDffStrm; /// Copy of the DFF page stream in memory.
+ XclImpObjMap maObjMap; /// Maps BIFF8 drawing objects to DFF stream position.
+ XclImpObjMapById maObjMapId; /// Maps BIFF8 drawing objects to object ID.
+ XclImpObjTextMap maTextMap; /// Maps BIFF8 TXO textbox data to DFF stream position.
+ ScfUInt16Vec maSkipObjs; /// IDs of all objects to be skipped.
+ bool mbOleObjs; /// True = draw model supports OLE objects.
+};
+
+/** Drawing manager of a single sheet. */
+class XclImpSheetDrawing : public XclImpDrawing
+{
+public:
+ explicit XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab );
+
+ /** Reads the NOTE record. */
+ void ReadNote( XclImpStream& rStrm );
+ /** Inserts a new chart object and reads the chart substream (BOF/EOF block).
+ @descr Used to import chart sheets, which do not have a corresponding OBJ record. */
+ void ReadTabChart( XclImpStream& rStrm );
+
+ /** Returns the total cell range covered by any shapes in the sheet. */
+ const ScRange& GetUsedArea() const { return maScUsedArea; }
+ /** Converts all objects and inserts them into the sheet drawing page. */
+ void ConvertObjects( XclImpDffConverter& rDffConv );
+
+ /** Calculate the resulting rectangle of the passed anchor. */
+ virtual tools::Rectangle CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const override;
+ /** On call, updates the used area of the sheet. */
+ virtual void OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) override;
+
+private:
+ /** Reads a BIFF3-BIFF5 NOTE record. */
+ void ReadNote3( XclImpStream& rStrm );
+ /** Reads a BIFF8 NOTE record. */
+ void ReadNote8( XclImpStream& rStrm );
+
+private:
+ ScRange maScUsedArea; /// Sheet index and used area in this sheet.
+};
+
+// The object manager =========================================================
+
+/** Stores all drawing and OLE objects and additional data related to these objects. */
+class XclImpObjectManager : protected XclImpRoot
+{
+public:
+ explicit XclImpObjectManager( const XclImpRoot& rRoot );
+ virtual ~XclImpObjectManager() override;
+
+ /** Reads the MSODRAWINGGROUP record. */
+ void ReadMsoDrawingGroup( XclImpStream& rStrm );
+
+ /** Returns (initially creates) the drawing manager of the specified sheet. */
+ XclImpSheetDrawing& GetSheetDrawing( SCTAB nScTab );
+ /** Inserts all objects into the Calc document. */
+ void ConvertObjects();
+
+ /** Returns the default name for the passed object. */
+ OUString GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const;
+ /** Returns the used area in the sheet with the passed index. */
+ ScRange GetUsedArea( SCTAB nScTab ) const;
+ /** Sets the container to receive overridden shape/ctrl names from
+ the filter. */
+ void SetOleNameOverrideInfo( const css::uno::Reference< css::container::XNameContainer >& rxOverrideInfo ) { mxOleCtrlNameOverride = rxOverrideInfo; }
+ /** Returns the name of overridden name ( or zero length string ) for
+ associated object id. */
+ OUString GetOleNameOverride( SCTAB nTab, sal_uInt16 nObjId );
+
+private:
+ typedef std::map< sal_uInt16, OUString > DefObjNameMap;
+ typedef std::shared_ptr< XclImpSheetDrawing > XclImpSheetDrawingRef;
+ typedef std::map< SCTAB, XclImpSheetDrawingRef > XclImpSheetDrawingMap;
+
+ css::uno::Reference< css::container::XNameContainer > mxOleCtrlNameOverride;
+ DefObjNameMap maDefObjNames; /// Default base names for all object types.
+ SvMemoryStream maDggStrm; /// Copy of global DFF data (DGG container) in memory.
+ XclImpSheetDrawingMap maSheetDrawings; /// Drawing managers of all sheets.
+};
+
+// DFF property set helper ====================================================
+
+/** This class reads a DFF property set (msofbtOPT record).
+
+ It can return separate property values or an item set which contains items
+ translated from these properties.
+ */
+class XclImpDffPropSet : protected XclImpRoot
+{
+public:
+ explicit XclImpDffPropSet( const XclImpRoot& rRoot );
+
+ /** Reads a DFF property set from the stream.
+ @descr The stream must point to the start of a DFF record containing properties. */
+ void Read( XclImpStream& rStrm );
+
+ /** Returns the specified property or zero, if not extant. */
+ sal_uInt32 GetPropertyValue( sal_uInt16 nPropId ) const;
+
+ /** Translates the properties and fills the item set. */
+ void FillToItemSet( SfxItemSet& rItemSet ) const;
+
+private:
+ typedef std::unique_ptr<SvMemoryStream> SvMemoryStreamPtr;
+
+ SvMemoryStream maDummyStrm; /// Dummy DGG stream for DFF manager.
+ XclImpSimpleDffConverter maDffConv; /// DFF converter used to resolve palette colors.
+ SvMemoryStreamPtr mxMemStrm; /// Helper stream.
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiformula.hxx b/sc/source/filter/inc/xiformula.hxx
new file mode 100644
index 0000000000..b379766f0a
--- /dev/null
+++ b/sc/source/filter/inc/xiformula.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 .
+ */
+
+#pragma once
+
+#include "xlformula.hxx"
+#include "xiroot.hxx"
+#include <memory>
+
+// Formula compiler ===========================================================
+
+class ScRangeList;
+class XclImpFmlaCompImpl;
+
+/** The formula compiler to create Calc token arrays from Excel token arrays. */
+class XclImpFormulaCompiler : protected XclImpRoot
+{
+public:
+ explicit XclImpFormulaCompiler( const XclImpRoot& rRoot );
+ virtual ~XclImpFormulaCompiler() override;
+
+ /** Creates a range list from the passed Excel token array.
+ @param rStrm Stream pointing to additional formula data (e.g. constant array data). */
+ void CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm );
+
+ /**
+ * Creates a formula token array from the Excel token array.
+ */
+ std::unique_ptr<ScTokenArray> CreateFormula( XclFormulaType eType, const XclTokenArray& rXclTokArr );
+
+private:
+ typedef std::shared_ptr< XclImpFmlaCompImpl > XclImpFmlaCompImplRef;
+ XclImpFmlaCompImplRef mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xihelper.hxx b/sc/source/filter/inc/xihelper.hxx
new file mode 100644
index 0000000000..746259e844
--- /dev/null
+++ b/sc/source/filter/inc/xihelper.hxx
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/editdata.hxx>
+#include <types.hxx>
+#include "xladdress.hxx"
+#include "xiroot.hxx"
+#include "xltools.hxx"
+#include <memory>
+#include <vector>
+
+class ScRangeList;
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+// Excel->Calc cell address/range conversion ==================================
+
+/** Provides functions to convert Excel cell addresses to Calc cell addresses. */
+class XclImpAddressConverter : public XclAddressConverterBase
+{
+public:
+ explicit XclImpAddressConverter( const XclImpRoot& rRoot );
+
+ // cell address -----------------------------------------------------------
+
+ /** Checks if the passed Excel cell address is valid.
+ @param rXclPos The Excel cell address to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is not valid.
+ @return true = Cell address in rXclPos is valid. */
+ bool CheckAddress( const XclAddress& rXclPos, bool bWarn );
+
+ /** Converts the passed Excel cell address to a Calc cell address.
+ @param rScPos (Out) The converted Calc cell address, if valid.
+ @param rXclPos The Excel cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is invalid.
+ @return true = Cell address returned in rScPos is valid. */
+ bool ConvertAddress( ScAddress& rScPos,
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+ @param rXclPos The Excel cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is invalid.
+ @return The converted Calc cell address. */
+ ScAddress CreateValidAddress( const XclAddress& rXclPos,
+ SCTAB nScTab, bool bWarn );
+
+ // cell range -------------------------------------------------------------
+
+ /** Converts the passed Excel cell range to a Calc cell range.
+ @param rScRange (Out) The converted Calc cell range, if valid.
+ @param rXclRange The Excel cell range to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range contains invalid cells.
+ @return true = Cell range returned in rScRange is valid (original or cropped). */
+ bool ConvertRange( ScRange& rScRange, const XclRange& rXclRange,
+ SCTAB nScTab1, SCTAB nScTab2, bool bWarn );
+
+ // cell range list --------------------------------------------------------
+
+ /** Converts the passed Excel cell range list to a Calc cell range list.
+ @descr The start position of the ranges will not be modified. Cell
+ ranges that fit partly into valid dimensions are cropped
+ accordingly. Cell ranges that do not fit at all, are not inserted
+ into the Calc cell range list.
+ @param rScRanges (Out) The converted Calc cell range list.
+ @param rXclRanges The Excel cell range list to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if at least one of the cell ranges
+ contains invalid cells. */
+ void ConvertRangeList( ScRangeList& rScRanges,
+ const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn );
+};
+
+// String->EditEngine conversion ==============================================
+
+class EditTextObject;
+
+/** This class provides methods to convert an XclImpString.
+ @The string can be converted to an edit engine text object or directly
+ to a Calc edit cell. */
+class XclImpStringHelper
+{
+public:
+ /** delete copy constructor */
+ XclImpStringHelper(const XclImpStringHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpStringHelper& operator=(const XclImpStringHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpStringHelper() = delete;
+ /** Returns a new edit engine text object.
+ @param nXFIndex Index to XF for first text portion (for escapement). */
+ static std::unique_ptr<EditTextObject> CreateTextObject(
+ const XclImpRoot& rRoot,
+ const XclImpString& rString );
+
+ static void SetToDocument(
+ ScDocumentImport& rDoc, const ScAddress& rPos, const XclImpRoot& rRoot,
+ const XclImpString& rString, sal_uInt16 nXFIndex );
+};
+
+// Header/footer conversion ===================================================
+
+class EditEngine;
+class SfxItemSet;
+class SvxFieldItem;
+struct XclFontData;
+
+/** Converts an Excel header/footer string into three edit engine text objects.
+ @descr Header/footer content is divided into three parts: Left, center and
+ right portion. All formatting information is encoded in the Excel string
+ using special character sequences. A control sequence starts with the ampersand
+ character.
+
+ Supported control sequences:
+ &L start of left portion
+ &C start of center portion
+ &R start of right portion
+ &P current page number
+ &N page count
+ &D current date
+ &T current time
+ &A table name
+ &F file name without path (see also &Z&F)
+ &Z file path without file name (converted to full file name, see also &Z&F)
+ &Z&F file path and name
+ &U underlining on/off
+ &E double underlining on/off
+ &S strikeout characters on/off
+ &X superscript on/off
+ &Y subscript on/off
+ &"fontname,fontstyle" use font with name 'fontname' and style 'fontstyle'
+ &fontheight set font height in points ('fontheight' is a decimal value)
+
+ Known but unsupported control sequences:
+ &G picture
+ */
+class XclImpHFConverter : protected XclImpRoot
+{
+public:
+ /** delete copy constructor */
+ XclImpHFConverter(const XclImpHFConverter&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpHFConverter& operator=(const XclImpHFConverter&) = delete;
+
+ explicit XclImpHFConverter( const XclImpRoot& rRoot );
+ virtual ~XclImpHFConverter() override;
+
+ /** Parses the passed string and creates three new edit engine text objects. */
+ void ParseString( const OUString& rHFString );
+
+ /** Creates a ScPageHFItem and inserts it into the passed item set. */
+ void FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const;
+ /** Returns the total height of the converted header or footer in twips. */
+ sal_Int32 GetTotalHeight() const;
+
+private: // types
+ typedef ::std::unique_ptr< XclFontData > XclFontDataPtr;
+
+ /** Enumerates the supported header/footer portions. */
+ enum XclImpHFPortion { EXC_HF_LEFT, EXC_HF_CENTER, EXC_HF_RIGHT, EXC_HF_PORTION_COUNT };
+
+ /** Contains all information about a header/footer portion. */
+ struct XclImpHFPortionInfo
+ {
+ typedef std::shared_ptr< EditTextObject > EditTextObjectRef;
+ EditTextObjectRef mxObj; /// Edit engine text object.
+ ESelection maSel; /// Edit engine selection.
+ sal_Int32 mnHeight; /// Height of previous lines in twips.
+ sal_uInt16 mnMaxLineHt; /// Maximum font height for the current text line.
+ explicit XclImpHFPortionInfo();
+ };
+
+private:
+ /** Returns the current edit engine text object. */
+ XclImpHFPortionInfo& GetCurrInfo() { return maInfos[ meCurrObj ]; }
+ /** Returns the current edit engine text object. */
+ XclImpHFPortionInfo::EditTextObjectRef& GetCurrObj() { return GetCurrInfo().mxObj; }
+ /** Returns the current selection. */
+ ESelection& GetCurrSel() { return GetCurrInfo().maSel; }
+
+ /** Returns the maximum line height of the specified portion. */
+ sal_uInt16 GetMaxLineHeight( XclImpHFPortion ePortion ) const;
+
+ /** Updates the maximum line height of the specified portion, using the current font size. */
+ void UpdateMaxLineHeight( XclImpHFPortion ePortion );
+ /** Updates the current maximum line height, using the current font size. */
+ void UpdateCurrMaxLineHeight();
+
+ /** Sets the font attributes at the current selection.
+ @descr After that, the start position of the current selection object is
+ adjusted to the end of the selection. */
+ void SetAttribs();
+ /** Resets font data to application default font. */
+ void ResetFontData();
+
+ /** Inserts maCurrText into edit engine and adjusts the current selection object.
+ @descr The text shall not contain a newline character.
+ The text will be cleared after insertion. */
+ void InsertText();
+ /** Inserts the passed text field and adjusts the current selection object. */
+ void InsertField( const SvxFieldItem& rFieldItem );
+ /** Inserts a line break and adjusts the current selection object. */
+ void InsertLineBreak();
+
+ /** Creates the edit engine text object of current portion from edit engine. */
+ void CreateCurrObject();
+ /** Changes current header/footer portion to eNew.
+ @descr Creates text object of current portion and reinitializes edit engine. */
+ void SetNewPortion( XclImpHFPortion eNew );
+
+private:
+ EditEngine& mrEE; /// The header/footer edit engine.
+ std::vector< XclImpHFPortionInfo >
+ maInfos; /// Edit engine text objects for all portions.
+ OUStringBuffer maCurrText; /// Current text to insert into edit engine.
+ XclFontDataPtr mxFontData; /// Font data of current text.
+ XclImpHFPortion meCurrObj; /// The current portion.
+};
+
+// URL conversion =============================================================
+
+/** This class contains static methods to decode a URL stored in an Excel file.
+ @descr Excel URLs can contain a sheet name, for instance: path\[test.xls]Sheet1
+ This sheet name will be extracted automatically. */
+class XclImpUrlHelper
+{
+public:
+ /** delete copy constructor */
+ XclImpUrlHelper(const XclImpUrlHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpUrlHelper& operator=(const XclImpUrlHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpUrlHelper() = delete;
+
+ /** Decodes an encoded external document URL with optional sheet name.
+ @param rUrl Returns the decoded file name incl. path.
+ @param rTabName Returns the decoded sheet name.
+ @param rbSameWb Returns true, if the URL is a reference to the own workbook.
+ @param rEncodedUrl An encoded URL from Excel. */
+ static void DecodeUrl(
+ OUString& rUrl,
+ OUString& rTabName,
+ bool& rbSameWb,
+ const XclImpRoot& rRoot,
+ const OUString& rEncodedUrl );
+
+ /** Decodes an encoded external document URL without sheet name.
+ @param rUrl Returns the decoded file name incl. path.
+ @param rbSameWb Returns true, if the URL is a reference to the own workbook.
+ @param rEncodedUrl An encoded URL from Excel. */
+
+ static void DecodeUrl(
+ OUString& rUrl,
+ bool& rbSameWb,
+ const XclImpRoot& rRoot,
+ const OUString& rEncodedUrl );
+
+ /** Decodes the passed URL to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ static bool DecodeLink( OUString& rApplic, OUString& rTopic, std::u16string_view aEncUrl );
+};
+
+// Cached values ==============================================================
+
+class ScTokenArray;
+
+/** This class stores one cached value of a cached value list (used for instance in
+ CRN, EXTERNNAME, tArray). */
+class XclImpCachedValue
+{
+public:
+ /** delete copy constructor */
+ XclImpCachedValue(const XclImpCachedValue&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpCachedValue& operator=(const XclImpCachedValue&) = delete;
+ /** Creates a cached value and reads contents from stream and stores it with its array address. */
+ explicit XclImpCachedValue( XclImpStream& rStrm );
+ virtual ~XclImpCachedValue();
+
+ /** Returns the type of the cached value (EXC_CACHEDVAL_*). */
+ sal_uInt8 GetType() const { return mnType; }
+ /** Returns the cached string value, if this value is a string, else an empty string. */
+ const OUString& GetString() const { return maStr;}
+ /** Returns the cached number, if this value has number type, else 0.0. */
+ double GetValue() const { return mfValue; }
+ /** Returns the cached Boolean value, if this value has Boolean type, else false. */
+ bool GetBool() const { return (mnType == EXC_CACHEDVAL_BOOL) && (mnBoolErr != 0); }
+ /** Returns the cached Calc error code, if this value has Error type, else 0. */
+ sal_uInt8 GetXclError() const { return (mnType == EXC_CACHEDVAL_ERROR) ? mnBoolErr : EXC_ERR_NA; }
+ /** Returns the cached Calc error code, if this value has Error type, else 0. */
+ FormulaError GetScError() const;
+
+private:
+ typedef ::std::unique_ptr< const ScTokenArray > ScTokenArrayPtr;
+
+ OUString maStr; /// Cached value is a string.
+ double mfValue; /// Cached value is a double.
+ ScTokenArrayPtr mxTokArr; /// Cached value is a formula or error code or Boolean.
+ sal_uInt8 mnBoolErr; /// Boolean value or Excel error code.
+ sal_uInt8 mnType; /// The type of the cached value (EXC_CACHEDVAL_*).
+};
+
+/** Contains cached values in a 2-dimensional array. */
+class XclImpCachedMatrix
+{
+public:
+ explicit XclImpCachedMatrix( XclImpStream& rStrm );
+ ~XclImpCachedMatrix();
+
+ /** Creates a new ScMatrix object and fills it with the contained values. */
+ ScMatrixRef CreateScMatrix( svl::SharedStringPool& rPool ) const;
+
+private:
+ typedef std::vector< std::unique_ptr<XclImpCachedValue> > XclImpValueList;
+
+ XclImpValueList maValueList; /// List of cached cell values.
+ SCSIZE mnScCols; /// Number of cached columns.
+ SCSIZE mnScRows; /// Number of cached rows.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xilink.hxx b/sc/source/filter/inc/xilink.hxx
new file mode 100644
index 0000000000..686f928ff1
--- /dev/null
+++ b/sc/source/filter/inc/xilink.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <map>
+#include "xllink.hxx"
+#include "xiroot.hxx"
+#include "ftools.hxx"
+#include <types.hxx>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+/* ============================================================================
+Classes for import of different kinds of internal/external references.
+- 3D cell and cell range links
+- External cell and cell range links
+- External defined names
+- Add-in functions
+- DDE links
+- OLE object links
+============================================================================ */
+
+// Excel sheet indexes ========================================================
+
+/** A buffer containing information about names and creation order of sheets.
+
+ The first purpose of this buffer is to translate original Excel
+ sheet names into Calc sheet indexes. This is not trivial because the filter
+ may rename the Calc sheets during creation. This buffer stores the original
+ Excel sheet names with the corresponding Calc sheet indexes.
+
+ The second purpose is to store the creation order of all sheets inside the
+ Excel workbook. The creation order list is contained in the TABID record
+ and needed to import the change log. Example: if the list contains 3;1;2
+ this means that the second sheet in the file was created first, then the
+ third sheet in the file was created and finally the first sheet.
+ */
+class XclImpTabInfo
+{
+public:
+ // original Excel sheet names ---------------------------------------------
+
+ /** Appends an original Excel sheet name with corresponding Calc sheet index. */
+ void AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab );
+ /** Inserts a Calc sheet index (increases all following sheet indexes). */
+ void InsertScTab( SCTAB nScTab );
+
+ /** Returns the Calc sheet index from the passed original Excel sheet name. */
+ SCTAB GetScTabFromXclName( const OUString& rXclTabName ) const;
+
+ // record creation order - TABID record -----------------------------------
+
+ /** Reads the TABID record. */
+ void ReadTabid( XclImpStream& rStrm );
+
+ /** Returns the current sheet index calculated from creation index.
+ @param nCreatedId The creation index of the sheet (1-based).
+ @param nMaxTabId All values greater than this parameter are not used to find the index.
+ @return The 0-based index of the sheet nCreatedId if it is contained in the list.
+ Example: The buffer is 3;5;2;4;1, nCreatedId is 1 and nMaxTabId is 3. The function will
+ return 2 which is the 0-based index of sheet 1 in the list 3;2;1. */
+ sal_uInt16 GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const;
+
+private:
+ typedef ::std::map< OUString, SCTAB > XclTabNameMap;
+
+ XclTabNameMap maTabNames; /// All Excel sheet names with Calc sheet index.
+ ScfUInt16Vec maTabIdVec; /// The vector with sheet indexes.
+};
+
+// External names =============================================================
+
+/** Type of an external name. */
+enum XclImpExtNameType
+{
+ xlExtName, /// An external defined name.
+ xlExtAddIn, /// An add-in function name.
+ xlExtDDE, /// A DDE link range.
+ xlExtOLE, /// An OLE object link.
+ xlExtEuroConvert /// An external in Excel, but internal in OO function name.
+};
+
+class XclImpCachedMatrix;
+class ScTokenArray;
+class XclImpSupbook;
+
+/** Stores contents of an external name.
+ @descr Supported: External defined names, AddIn names, DDE links and OLE objects. */
+class XclImpExtName
+{
+ /**
+ * MOper, multiple operands, stores cached values of external range
+ * specified in the record.
+ */
+ class MOper
+ {
+ public:
+ MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm);
+ const ScMatrix& GetCache() const;
+ private:
+ ScMatrixRef mxCached;
+ };
+
+public:
+ /** Reads the external name from the stream. */
+ explicit XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm,
+ XclSupbookType eSubType, ExcelToSc* pFormulaConv );
+ ~XclImpExtName();
+
+ /** Create and apply the cached list of this DDE Link to the document. */
+ void CreateDdeData( ScDocument& rDoc,
+ const OUString& rApplc, const OUString& rExtDoc ) const;
+
+ void CreateExtNameData( const ScDocument& rDoc, sal_uInt16 nFileId ) const;
+
+ /**
+ * Create OLE link data. OLE link data is converted to external
+ * reference, since OLE link doesn't work cross-platform, and is not very
+ * reliable even on Windows.
+ */
+ bool CreateOleData(const ScDocument& rDoc, const OUString& rUrl,
+ sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const;
+
+ bool HasFormulaTokens() const;
+
+ XclImpExtNameType GetType() const { return meType; }
+ const OUString& GetName() const { return maName; }
+ sal_uInt32 GetStorageId() const { return mnStorageId; }
+
+private:
+ typedef ::std::unique_ptr< XclImpCachedMatrix > XclImpCachedMatrixPtr;
+ typedef ::std::unique_ptr< ScTokenArray > TokenArrayPtr;
+
+ XclImpCachedMatrixPtr mxDdeMatrix; /// Cached results of the DDE link.
+ std::optional<MOper> moMOper; /// Cached values for OLE link
+ TokenArrayPtr mxArray; /// Formula tokens for external name.
+ OUString maName; /// The name of the external name.
+ sal_uInt32 mnStorageId; /// Storage ID for OLE object storages.
+ XclImpExtNameType meType; /// Type of the external name.
+};
+
+// Import link manager ========================================================
+
+class XclImpLinkManagerImpl;
+
+/** This is the central class for the import of all internal/external links.
+ @descr This manager stores all data about external documents with their sheets
+ and cached cell contents. Additionally it handles external names, such as add-in
+ function names, DDE links, and OLE object links.
+ File contents in BIFF8:
+ - Record SUPBOOK: Contains the name of an external document and the names of its sheets.
+ This record is optionally followed by NAME, EXTERNNAME, XCT and CRN records.
+ - Record XCT: Contains the number and sheet index of the following CRN records.
+ - Record CRN: Contains addresses (row and column) and values of external referenced cells.
+ - Record NAME: Contains defined names of the own workbook.
+ - Record EXTERNNAME: Contains external defined names, DDE links, or OLE object links.
+ - Record EXTERNSHEET: Contains indexes to URLs of external documents (SUPBOOKs)
+ and sheet indexes for each external reference used anywhere in the workbook.
+ This record follows a list of SUPBOOK records (with their attached records).
+*/
+class XclImpLinkManager : protected XclImpRoot
+{
+public:
+ explicit XclImpLinkManager( const XclImpRoot& rRoot );
+ virtual ~XclImpLinkManager() override;
+
+ /** Reads the EXTERNSHEET record. */
+ void ReadExternsheet( XclImpStream& rStrm );
+ /** Reads a SUPBOOK record. */
+ void ReadSupbook( XclImpStream& rStrm );
+ /** Reads an XCT record and appends it to the current SUPBOOK. */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record and appends it to the current SUPBOOK. */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns true, if the specified XTI entry contains an internal reference. */
+ bool IsSelfRef( sal_uInt16 nXtiIndex ) const;
+ /** Returns the Calc sheet index range of the specified XTI entry.
+ @return true = XTI data found, returned sheet index range is valid. */
+ bool GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab,
+ sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified external name or 0 on error. */
+ const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const;
+
+ const OUString* GetSupbookUrl( sal_uInt16 nXtiIndex ) const;
+
+ OUString GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const;
+
+ /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified macro name or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const;
+
+private:
+ typedef ::std::unique_ptr< XclImpLinkManagerImpl > XclImpLinkMgrImplPtr;
+ XclImpLinkMgrImplPtr mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiname.hxx b/sc/source/filter/inc/xiname.hxx
new file mode 100644
index 0000000000..8d86909c03
--- /dev/null
+++ b/sc/source/filter/inc/xiname.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xiroot.hxx"
+#include "xistream.hxx"
+
+#include <rangenam.hxx>
+
+#include <memory>
+#include <vector>
+
+class ScTokenArray;
+
+/** Represents a defined name. It may be related to a single sheet or global. */
+class XclImpName : protected XclImpRoot
+{
+ struct TokenStrmData
+ {
+ XclImpStream& mrStrm;
+ XclImpStreamPos maStrmPos;
+ std::size_t mnStrmPos;
+ std::size_t mnStrmSize;
+
+ TokenStrmData( XclImpStream& rStrm );
+ };
+
+public:
+ XclImpName(const XclImpName&) = delete;
+ const XclImpName& operator=(const XclImpName&) = delete;
+
+ explicit XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx );
+
+ const OUString& GetXclName() const { return maXclName; }
+ const OUString& GetScName() const { return maScName; }
+ SCTAB GetScTab() const { return mnScTab; }
+ const ScRangeData* GetScRangeData() const { return mpScData; }
+ bool IsGlobal() const { return mnScTab == SCTAB_MAX; }
+ bool IsVBName() const { return mbVBName; }
+ bool IsMacro() const { return mbMacro; }
+ void ConvertTokens();
+
+private:
+ void InsertName(const ScTokenArray* pArray);
+
+ OUString maXclName; /// Original name read from the file.
+ OUString maScName; /// Name inserted into the Calc document.
+ const ScRangeData* mpScData; /// Pointer to Calc defined name (no ownership).
+ SCTAB mnScTab; /// Calc sheet index of local names.
+ ScRangeData::Type meNameType;
+ sal_uInt16 mnXclTab;
+ sal_uInt16 mnNameIndex;
+ bool mbVBName:1; /// true = Visual Basic procedure or function.
+ bool mbMacro:1; /// Whether it's a user-defined macro.
+
+ std::unique_ptr<TokenStrmData> mpTokensData; /// For later conversion of token array.
+};
+
+/** This buffer contains all internal defined names of the document.
+ @descr It manages the position of the names in the document, means if they are
+ global or attached to a specific sheet. While inserting the names into the Calc
+ document this buffer resolves conflicts caused by equal names from different
+ sheets. */
+class XclImpNameManager : protected XclImpRoot
+{
+public:
+ explicit XclImpNameManager( const XclImpRoot& rRoot );
+
+ /** Reads a NAME record and creates an entry in this buffer. */
+ void ReadName( XclImpStream& rStrm );
+
+ /** Tries to find the name used in Calc, based on the original Excel defined name.
+ @param nScTab The sheet index for local names or SCTAB_MAX for global names.
+ If no local name is found, tries to find a matching global name.
+ @return Pointer to the defined name or 0 on error. */
+ const XclImpName* FindName( std::u16string_view rXclName, SCTAB nScTab ) const;
+
+ /** Returns the defined name specified by its Excel index.
+ @param nXclNameIdx The index of the internal defined name.
+ @return Pointer to the defined name or 0 on error. */
+ const XclImpName* GetName( sal_uInt16 nXclNameIdx ) const;
+
+ void ConvertAllTokens();
+
+private:
+ typedef std::vector< std::unique_ptr<XclImpName> > XclImpNameList;
+ XclImpNameList maNameList;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xipage.hxx b/sc/source/filter/inc/xipage.hxx
new file mode 100644
index 0000000000..9aad4a506e
--- /dev/null
+++ b/sc/source/filter/inc/xipage.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 "xlpage.hxx"
+#include "xiroot.hxx"
+
+// Page settings ==============================================================
+
+/** Contains all page (print) settings for a single sheet.
+ @descr Supports reading all related records and creating a page style sheet. */
+class XclImpPageSettings : protected XclImpRoot
+{
+public:
+ explicit XclImpPageSettings( const XclImpRoot& rRoot );
+
+ /** Returns read-only access to the page data. */
+ const XclPageData& GetPageData() const { return maData; }
+
+ /** Initializes the object to be used for a new sheet. */
+ void Initialize();
+
+ /** Reads a SETUP record and inserts contained data. */
+ void ReadSetup( XclImpStream& rStrm );
+ /** Reads a ***MARGIN record (reads all 4 margin records). */
+ void ReadMargin( XclImpStream& rStrm );
+ /** Reads a HCENTER or VCENTER record. */
+ void ReadCenter( XclImpStream& rStrm );
+ /** Reads a HEADER or FOOTER record. */
+ void ReadHeaderFooter( XclImpStream& rStrm );
+ /** Reads a HORIZONTALPAGEBREAKS or VERTICALPAGEBREAKS record. */
+ void ReadPageBreaks( XclImpStream& rStrm );
+ /** Reads a PRINTHEADERS record. */
+ void ReadPrintHeaders( XclImpStream& rStrm );
+ /** Reads a PRINTGRIDLINES record. */
+ void ReadPrintGridLines( XclImpStream& rStrm );
+ /** Reads an IMGDATA record and creates the SvxBrushItem. */
+ void ReadImgData( XclImpStream& rStrm );
+
+ /** Overrides paper size and orientation (used in sheet-charts). */
+ void SetPaperSize( sal_uInt16 nXclPaperSize, bool bPortrait );
+ /** Sets or clears the fit-to-pages setting (contained in WSBOOL record). */
+ void SetFitToPages( bool bFitToPages ) { maData.mbFitToPages = bFitToPages; }
+
+ /** Creates a page stylesheet from current settings and sets it at current sheet. */
+ void Finalize();
+
+private:
+ XclPageData maData; /// Page settings data.
+ bool mbValidPaper; /// true = Paper size and orientation valid.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xipivot.hxx b/sc/source/filter/inc/xipivot.hxx
new file mode 100644
index 0000000000..611de505e8
--- /dev/null
+++ b/sc/source/filter/inc/xipivot.hxx
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "xlpivot.hxx"
+#include "xiroot.hxx"
+#include <vector>
+#include <memory>
+
+class ScDPSaveData;
+class ScDPSaveDimension;
+
+// Pivot cache
+
+/** Represents a data item in a pivot cache. */
+class XclImpPCItem : public XclPCItem
+{
+public:
+ explicit XclImpPCItem( XclImpStream& rStrm );
+
+ /** Inserts the item data into the passed document. */
+ void WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const;
+
+private:
+ /** Reads an SXDOUBLE record describing a floating-point item. */
+ void ReadSxdouble( XclImpStream& rStrm );
+ /** Reads an SXBOOLEAN record describing a boolean item. */
+ void ReadSxboolean( XclImpStream& rStrm );
+ /** Reads an SXERROR record describing an error code item. */
+ void ReadSxerror( XclImpStream& rStrm );
+ /** Reads an SXINTEGER record describing an integer item. */
+ void ReadSxinteger( XclImpStream& rStrm );
+ /** Reads an SXSTRING record describing a text item. */
+ void ReadSxstring( XclImpStream& rStrm );
+ /** Reads an SXDATETIME record describing a date/time item. */
+ void ReadSxdatetime( XclImpStream& rStrm );
+ /** Reads an SXEMPTY record describing an empty item. */
+ void ReadSxempty( XclImpStream& rStrm );
+};
+
+typedef std::shared_ptr< XclImpPCItem > XclImpPCItemRef;
+
+struct ScDPNumGroupInfo;
+class XclImpPivotCache;
+
+/** Represents a field in a pivot cache (a column of data items in the source area). */
+class XclImpPCField : public XclPCField, protected XclImpRoot
+{
+public:
+ /** Creates a pivot cache field by reading an SXFIELD record. */
+ explicit XclImpPCField( const XclImpRoot& rRoot,
+ XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx );
+ virtual ~XclImpPCField() override;
+
+ // general field/item access ----------------------------------------------
+
+ /** Returns the name of the field, uses the passed visible name if supported. */
+ const OUString& GetFieldName( const ScfStringVec& rVisNames ) const;
+
+ /** Returns the base field if this is a grouping field. */
+ const XclImpPCField* GetGroupBaseField() const;
+
+ /** Returns the item at the specified position or 0 on error. */
+ const XclImpPCItem* GetItem( sal_uInt16 nItemIdx ) const;
+ /** Returns the item representing a limit value in numeric/date/time grouping fields.
+ @param nItemIdx One of EXC_SXFIELD_INDEX_MIN, EXC_SXFIELD_INDEX_MAX, or EXC_SXFIELD_INDEX_STEP. */
+ const XclImpPCItem* GetLimitItem( sal_uInt16 nItemIdx ) const;
+
+ /** Inserts the field name into the document. */
+ void WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab );
+ /** Inserts the specified item data into the document. */
+ void WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx );
+ /** Inserts the data of the last inserted item into the document. */
+ void WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab );
+
+ // records ----------------------------------------------------------------
+
+ /** Reads the SXFIELD record describing the field. */
+ void ReadSxfield( XclImpStream& rStrm );
+ /** Reads an item data record describing a new item. */
+ void ReadItem( XclImpStream& rStrm );
+ /** Reads the SXNUMGROUP record describing numeric grouping fields. */
+ void ReadSxnumgroup( XclImpStream& rStrm );
+ /** Reads the SXGROUPINFO record describing the item order in grouping fields. */
+ void ReadSxgroupinfo( XclImpStream& rStrm );
+
+ // grouping ---------------------------------------------------------------
+
+ /** Inserts grouping information of this field into the passed ScDPSaveData. */
+ void ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+
+private:
+ /** Inserts standard grouping information of this field into the passed ScDPSaveData. */
+ void ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+ /** Inserts numeric grouping information of this field into the passed ScDPSaveData. */
+ void ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+ /** Inserts date grouping information of this field into the passed ScDPSaveData. */
+ void ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+
+ /** Returns a Calc struct with numeric grouping data. */
+ ScDPNumGroupInfo GetScNumGroupInfo() const;
+ /** Returns a Calc struct with date grouping data. */
+ ScDPNumGroupInfo GetScDateGroupInfo() const;
+
+ /** Returns a limit value for numeric grouping fields. */
+ const double* GetNumGroupLimit( sal_uInt16 nLimitIdx ) const;
+ /** Returns a limit value for date grouping fields (minimum/maximum only). */
+ const DateTime* GetDateGroupLimit( sal_uInt16 nLimitIdx ) const;
+ /** Returns the step value for date grouping fields. */
+ const sal_Int16* GetDateGroupStep() const;
+
+private:
+ typedef ::std::vector< XclImpPCItemRef > XclImpPCItemVec;
+
+ XclImpPivotCache& mrPCache; /// Parent pivot cache containing this field.
+ XclImpPCItemVec maItems; /// List of all displayed data items.
+ XclImpPCItemVec maOrigItems; /// List of all source data items.
+ XclImpPCItemVec maNumGroupItems; /// List of items containing numeric grouping limits.
+ mutable SCCOL mnSourceScCol; /// Column index of source data for this field.
+ bool mbNumGroupInfoRead; /// true = Numeric grouping info read (SXNUMGROUP record).
+};
+
+typedef std::shared_ptr< XclImpPCField > XclImpPCFieldRef;
+
+class XclImpPivotCache : protected XclImpRoot
+{
+public:
+ explicit XclImpPivotCache( const XclImpRoot& rRoot );
+ virtual ~XclImpPivotCache() override;
+
+ // data access ------------------------------------------------------------
+
+ /** Returns the data source range read from the DCONREF record. */
+ const ScRange& GetSourceRange() const { return maSrcRange; }
+
+ const OUString& GetSourceRangeName() const { return maSrcRangeName; }
+
+ /** Returns read-only access to a pivot cache field. */
+ const XclImpPCField* GetField( sal_uInt16 nFieldIdx ) const;
+
+ // records ----------------------------------------------------------------
+
+ /** Reads an SXIDSTM record containing a pivot cache stream identifier and the pivot cache. */
+ void ReadSxidstm( XclImpStream& rStrm );
+ /** Reads an SXVS record containing the source type of the pivot cache. */
+ void ReadSxvs( XclImpStream& rStrm );
+ /** Reads a DCONREF record containing the source range of the pivot cache. */
+ void ReadDconref( XclImpStream& rStrm );
+ /**
+ * Read DECONNAME record which contains the defined name of the source
+ * range.
+ */
+ void ReadDConName( XclImpStream& rStrm );
+ /** Reads the entire pivot cache stream. Uses decrypter from passed stream. */
+ void ReadPivotCacheStream( const XclImpStream& rStrm );
+
+ bool IsRefreshOnLoad() const;
+ bool IsValid() const;
+
+private:
+ typedef ::std::vector< XclImpPCFieldRef > XclImpPCFieldVec;
+
+ XclPCInfo maPCInfo; /// Pivot cache settings (SXDB record).
+ XclImpPCFieldVec maFields; /// List of pivot cache fields.
+ ScRange maSrcRange; /// Source range in the spreadsheet.
+ OUString maUrl; /// URL of the source data.
+ OUString maTabName; /// Sheet name of the source data.
+ OUString maSrcRangeName; /// Name of the source data range.
+ sal_uInt16 mnStrmId; /// Pivot cache stream identifier.
+ sal_uInt16 mnSrcType; /// Source data type.
+ bool mbSelfRef; /// true = Source data from own document.
+};
+
+typedef std::shared_ptr< XclImpPivotCache > XclImpPivotCacheRef;
+
+// Pivot table
+
+class XclImpPivotTable;
+
+class XclImpPTItem
+{
+public:
+ explicit XclImpPTItem( const XclImpPCField* pCacheField );
+
+ /** Returns the internal name of the item or 0, if no name could be found. */
+ const OUString* GetItemName() const;
+ /** Returns the internal name of the item. */
+ std::pair<bool, OUString> GetItemName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot) const;
+
+ /** Reads an SXVI record containing data of this item. */
+ void ReadSxvi( XclImpStream& rStrm );
+
+ /** Inserts this item into the passed ScDPSaveDimension. */
+ void ConvertItem( ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot ) const;
+
+private:
+ XclPTItemInfo maItemInfo; /// General data for this item.
+ const XclImpPCField* mpCacheField; /// Corresponding pivot cache field.
+};
+
+typedef std::shared_ptr< XclImpPTItem > XclImpPTItemRef;
+
+class XclImpPTField
+{
+public:
+ explicit XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx );
+
+ // general field/item access ----------------------------------------------
+
+ /** Returns the corresponding pivot cache field of this field. */
+ const XclImpPCField* GetCacheField() const;
+ /** Returns the name of this field that is used to create the Calc dimensions. */
+ OUString GetFieldName() const;
+ /** Returns the internally set visible name of this field. */
+ OUString GetVisFieldName() const;
+
+ /** Returns the specified item. */
+ const XclImpPTItem* GetItem( sal_uInt16 nItemIdx ) const;
+ /** Returns the internal name of the specified item. */
+ const OUString* GetItemName( sal_uInt16 nItemIdx ) const;
+
+ /** Returns the flags of the axes this field is part of. */
+ sal_uInt16 GetAxes() const { return maFieldInfo.mnAxes; }
+ /** Sets the flags of the axes this field is part of. */
+ void SetAxes( sal_uInt16 nAxes ) { maFieldInfo.mnAxes = nAxes; }
+
+ // records ----------------------------------------------------------------
+
+ /** Reads an SXVD record describing the field. */
+ void ReadSxvd( XclImpStream& rStrm );
+ /** Reads an SXVDEX record describing extended options of the field. */
+ void ReadSxvdex( XclImpStream& rStrm );
+ /** Reads an SXVI record describing a new item of this field. */
+ void ReadSxvi( XclImpStream& rStrm );
+
+ // row/column fields ------------------------------------------------------
+
+ void ConvertRowColField( ScDPSaveData& rSaveData ) const;
+
+ // page fields ------------------------------------------------------------
+
+ void SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo );
+ void ConvertPageField( ScDPSaveData& rSaveData ) const;
+
+ // hidden fields ----------------------------------------------------------
+
+ void ConvertHiddenField( ScDPSaveData& rSaveData ) const;
+
+ // data fields ------------------------------------------------------------
+
+ bool HasDataFieldInfo() const;
+ void AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo );
+ void ConvertDataField( ScDPSaveData& rSaveData ) const;
+
+ void ConvertFieldInfo( const ScDPSaveData& rSaveData, ScDPObject* pObj, const XclImpRoot& rRoot, bool bPageField = false ) const;
+
+private:
+ void ConvertRCPField( ScDPSaveData& rSaveData ) const;
+
+ void ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const;
+ void ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const;
+
+private:
+
+ const XclImpPivotTable& mrPTable; /// Parent pivot table containing this field.
+ XclPTFieldInfo maFieldInfo; /// General field info (SXVD record).
+ XclPTFieldExtInfo maFieldExtInfo; /// Extended field info (SXVDEX record).
+ XclPTPageFieldInfo maPageInfo; /// Page field info (entry from SXPI record).
+ std::vector< XclPTDataFieldInfo > maDataInfoVector; /// Vector of extended data field info (SXDI records).
+ std::vector< XclImpPTItemRef > maItems; /// List of all items of this field.
+};
+
+typedef std::shared_ptr< XclImpPTField > XclImpPTFieldRef;
+
+class XclImpPivotTable : protected XclImpRoot
+{
+public:
+ explicit XclImpPivotTable( const XclImpRoot& rRoot );
+ virtual ~XclImpPivotTable() override;
+
+ // cache/field access, misc. ----------------------------------------------
+
+ const XclImpPivotCacheRef& GetPivotCache() const { return mxPCache; }
+ const ScfStringVec& GetVisFieldNames() const { return maVisFieldNames; }
+
+ sal_uInt16 GetFieldCount() const;
+ const XclImpPTField* GetField( sal_uInt16 nFieldIdx ) const;
+ XclImpPTField* GetFieldAcc( sal_uInt16 nFieldIdx );
+
+ const XclImpPTField* GetDataField( sal_uInt16 nDataFieldIdx ) const;
+ OUString GetDataFieldName( sal_uInt16 nDataFieldIdx ) const;
+
+ // records ----------------------------------------------------------------
+
+ /** Reads an SXVIEW record starting a new pivot table. */
+ void ReadSxview( XclImpStream& rStrm );
+ /** Reads an SXVD record describing a new field. */
+ void ReadSxvd( XclImpStream& rStrm );
+ /** Reads an SXVI record describing a new item of the current field. */
+ void ReadSxvi( XclImpStream& rStrm );
+ /** Reads an SXVDEX record describing extended options of the current field. */
+ void ReadSxvdex( XclImpStream& rStrm );
+ /** Reads an SXIVD record containing the row field or column field order. */
+ void ReadSxivd( XclImpStream& rStrm );
+ /** Reads an SXPI record containing page field data. */
+ void ReadSxpi( XclImpStream& rStrm );
+ /** Reads an SXDI record containing data field data. */
+ void ReadSxdi( XclImpStream& rStrm );
+ /** Reads an SXEX record containing additional settings for the pivot table. */
+ void ReadSxex( XclImpStream& rStrm );
+ /** Reads an SXVIEWEX9 record that specifies the pivot tables
+ * autoformat. */
+ void ReadSxViewEx9( XclImpStream& rStrm );
+
+ /** Reads an SXADDL record that specifies additional info for pivot table. */
+ void ReadSxAddl( XclImpStream& rStrm );
+
+ /** Inserts the pivot table into the Calc document. */
+ void Convert();
+
+ void MaybeRefresh();
+
+ void ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData);
+ void ApplyFieldInfo();
+
+private:
+ XclImpPivotCacheRef mxPCache; /// Pivot cache containing field/item names.
+
+ XclPTInfo maPTInfo; /// General info about the pivot table (SXVIEW record).
+ XclPTExtInfo maPTExtInfo; /// Extended info about the pivot table (SXEX record).
+ XclPTViewEx9Info maPTViewEx9Info; /// (SXVIEWEX9 record)
+ XclPTAddl maPTAddlInfo;
+ std::vector< XclImpPTFieldRef >
+ maFields; /// Vector containing all fields.
+ XclImpPTFieldRef mxCurrField; /// Current field for importing additional info.
+ ScfStringVec maVisFieldNames; /// Vector containing all visible field names.
+ ScfUInt16Vec maRowFields; /// Row field indexes.
+ ScfUInt16Vec maColFields; /// Column field indexes.
+ ScfUInt16Vec maPageFields; /// Page field indexes.
+ ScfUInt16Vec maOrigDataFields; /// Original data field indexes.
+ ScfUInt16Vec maFiltDataFields; /// Filtered data field indexes.
+ XclImpPTField maDataOrientField; /// Special data field orientation field.
+ ScRange maOutScRange; /// Output range in the Calc document.
+ ScDPObject* mpDPObj;
+};
+
+typedef std::shared_ptr< XclImpPivotTable > XclImpPivotTableRef;
+
+/** The main class for pivot table import.
+
+ This class contains functions to read all records related to pivot tables
+ and pivot caches.
+ */
+class XclImpPivotTableManager : protected XclImpRoot
+{
+public:
+ explicit XclImpPivotTableManager( const XclImpRoot& rRoot );
+ virtual ~XclImpPivotTableManager() override;
+
+ // pivot cache records ----------------------------------------------------
+
+ /** Returns the pivot cache with the specified 0-based index. */
+ XclImpPivotCacheRef GetPivotCache( sal_uInt16 nCacheIdx );
+
+ /** Reads an SXIDSTM record containing a pivot cache stream identifier and the pivot cache. */
+ void ReadSxidstm( XclImpStream& rStrm );
+ /** Reads an SXVS record containing the source type of a pivot cache. */
+ void ReadSxvs( XclImpStream& rStrm );
+ /** Reads a DCONREF record containing the source range of a pivot cache. */
+ void ReadDconref( XclImpStream& rStrm );
+ void ReadDConName( XclImpStream& rStrm );
+
+ // pivot table records ----------------------------------------------------
+
+ /** Reads an SXVIEW record describing a new pivot table. */
+ void ReadSxview( XclImpStream& rStrm );
+ /** Reads an SXVD record describing a new field. */
+ void ReadSxvd( XclImpStream& rStrm );
+ /** Reads an SXVDEX record describing extended options of a field. */
+ void ReadSxvdex( XclImpStream& rStrm );
+ /** Reads an SXIVD record containing the row field or column field order. */
+ void ReadSxivd( XclImpStream& rStrm );
+ /** Reads an SXPI record containing page field data. */
+ void ReadSxpi( XclImpStream& rStrm );
+ /** Reads an SXDI record containing data field data. */
+ void ReadSxdi( XclImpStream& rStrm );
+ /** Reads an SXVI record describing a new item of the current field. */
+ void ReadSxvi( XclImpStream& rStrm );
+ /** Reads an SXEX record containing additional settings for a pivot table. */
+ void ReadSxex( XclImpStream& rStrm );
+ /** Reads an SXVIEWEX9 record that specifies the pivot tables
+ * autoformat. */
+ void ReadSxViewEx9( XclImpStream& rStrm );
+ /** Reads an SXADDL record that specifies additional info for pivot table. */
+ void ReadSxAddl( XclImpStream& rStrm );
+
+ /** Reads all used pivot caches and creates additional sheets for external data sources. */
+ void ReadPivotCaches( const XclImpStream& rStrm );
+ /** Inserts all pivot tables into the Calc document. */
+ void ConvertPivotTables();
+
+ void MaybeRefreshPivotTables();
+
+private:
+
+ std::vector< XclImpPivotCacheRef > maPCaches; /// List of all pivot caches.
+ std::vector< XclImpPivotTableRef > maPTables; /// List of all pivot tables.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiroot.hxx b/sc/source/filter/inc/xiroot.hxx
new file mode 100644
index 0000000000..d290a01db0
--- /dev/null
+++ b/sc/source/filter/inc/xiroot.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "xlroot.hxx"
+#include <memory>
+
+// Forward declarations of objects in public use ==============================
+
+class XclImpStream;
+class XclImpString;
+
+typedef std::shared_ptr< XclImpString > XclImpStringRef;
+
+// Global data ================================================================
+
+class XclImpAddressConverter;
+class XclImpFormulaCompiler;
+class XclImpSst;
+class XclImpPalette;
+class XclImpFontBuffer;
+class XclImpNumFmtBuffer;
+class XclImpXFBuffer;
+class XclImpXFRangeBuffer;
+class XclImpTabInfo;
+class XclImpNameManager;
+class XclImpLinkManager;
+class XclImpObjectManager;
+class XclImpSheetDrawing;
+class XclImpCondFormatManager;
+class XclImpValidationManager;
+class XclImpAutoFilterBuffer;
+class XclImpWebQueryBuffer;
+class XclImpPivotTableManager;
+class XclImpPageSettings;
+class XclImpDocViewSettings;
+class XclImpTabViewSettings;
+class XclImpSheetProtectBuffer;
+class XclImpDocProtectBuffer;
+
+class ScRangeListTabs;
+class ExcelToSc;
+class ScDocumentImport;
+
+/** Stores global buffers and data needed for Excel import filter. */
+struct XclImpRootData : public XclRootData
+{
+ typedef std::shared_ptr< XclImpAddressConverter > XclImpAddrConvRef;
+ typedef std::shared_ptr< XclImpFormulaCompiler > XclImpFmlaCompRef;
+
+ typedef std::shared_ptr< XclImpSst > XclImpSstRef;
+ typedef std::shared_ptr< XclImpPalette > XclImpPaletteRef;
+ typedef std::shared_ptr< XclImpFontBuffer > XclImpFontBfrRef;
+ typedef std::shared_ptr< XclImpNumFmtBuffer > XclImpNumFmtBfrRef;
+ typedef std::shared_ptr< XclImpXFBuffer > XclImpXFBfrRef;
+ typedef std::shared_ptr< XclImpXFRangeBuffer > XclImpXFRangeBfrRef;
+ typedef std::shared_ptr< XclImpTabInfo > XclImpTabInfoRef;
+ typedef std::shared_ptr< XclImpNameManager > XclImpNameMgrRef;
+ typedef std::shared_ptr< XclImpLinkManager > XclImpLinkMgrRef;
+ typedef std::shared_ptr< XclImpObjectManager > XclImpObjectMgrRef;
+ typedef std::shared_ptr< XclImpCondFormatManager > XclImpCondFmtMgrRef;
+ typedef std::shared_ptr< XclImpValidationManager > XclImpValidationMgrRef;
+ typedef std::shared_ptr< XclImpWebQueryBuffer > XclImpWebQueryBfrRef;
+ typedef std::shared_ptr< XclImpPivotTableManager > XclImpPTableMgrRef;
+ typedef std::shared_ptr< XclImpPageSettings > XclImpPageSettRef;
+ typedef std::shared_ptr< XclImpDocViewSettings > XclImpDocViewSettRef;
+ typedef std::shared_ptr< XclImpTabViewSettings > XclImpTabViewSettRef;
+ typedef std::shared_ptr< XclImpSheetProtectBuffer > XclImpTabProtectRef;
+ typedef std::shared_ptr< XclImpDocProtectBuffer > XclImpDocProtectRef;
+
+ XclImpAddrConvRef mxAddrConv; /// The address converter.
+ XclImpFmlaCompRef mxFmlaComp; /// The formula compiler.
+
+ XclImpSstRef mxSst; /// The shared string table.
+ XclImpPaletteRef mxPalette; /// The color buffer.
+ XclImpFontBfrRef mxFontBfr; /// All fonts in the file.
+ XclImpNumFmtBfrRef mxNumFmtBfr; /// All number formats in the file.
+ XclImpXFBfrRef mpXFBfr; /// All XF record data in the file.
+ XclImpXFRangeBfrRef mxXFRangeBfr; /// Buffer of XF index ranges in a sheet.
+
+ XclImpTabInfoRef mxTabInfo; /// Sheet creation order list.
+ XclImpNameMgrRef mxNameMgr; /// Internal defined names.
+ XclImpLinkMgrRef mxLinkMgr; /// Manager for internal/external links.
+
+ XclImpObjectMgrRef mxObjMgr; /// All drawing objects.
+ XclImpCondFmtMgrRef mxCondFmtMgr; /// Conditional formatting.
+ XclImpValidationMgrRef mxValidMgr; /// Data validation
+ XclImpWebQueryBfrRef mxWebQueryBfr; /// All web queries.
+ XclImpPTableMgrRef mxPTableMgr; /// All pivot tables and pivot caches.
+
+ XclImpPageSettRef mxPageSett; /// Page settings for current sheet.
+ XclImpDocViewSettRef mxDocViewSett; /// View settings for entire document.
+ XclImpTabViewSettRef mxTabViewSett; /// View settings for current sheet.
+ XclImpTabProtectRef mxTabProtect; /// Sheet protection options for current sheet.
+ XclImpDocProtectRef mxDocProtect; /// Document protection options.
+
+ std::shared_ptr<ScDocumentImport> mxDocImport;
+
+ std::unique_ptr<ScRangeListTabs> mpPrintRanges;
+ std::unique_ptr<ScRangeListTabs> mpPrintTitles;
+
+ bool mbHasCodePage; /// true = CODEPAGE record exists.
+ bool mbHasBasic; /// true = document contains VB project.
+
+ explicit XclImpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc );
+ virtual ~XclImpRootData() override;
+};
+
+/** Access to global data from other classes. */
+class XclImpRoot : public XclRoot
+{
+public:
+ explicit XclImpRoot( XclImpRootData& rImpRootData );
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclImpRoot& GetRoot() const { return *this; }
+ XclImpRoot& GetRoot() { return *this; }
+
+ /** Sets a code page read from a CODEPAGE record for byte string import. */
+ void SetCodePage( sal_uInt16 nCodePage );
+
+ /** Is called when import filter starts importing a single sheet (all BIFF versions). */
+ void InitializeTable( SCTAB nScTab );
+ /** Is called when import filter stops importing a single sheet (all BIFF versions). */
+ void FinalizeTable();
+
+ /** Returns the address converter. */
+ XclImpAddressConverter& GetAddressConverter() const;
+ /** Returns the formula converter. */
+ XclImpFormulaCompiler& GetFormulaCompiler() const;
+ /** Returns the old formula converter. */
+ ExcelToSc& GetOldFmlaConverter() const;
+
+ /** Returns the shared string table. */
+ XclImpSst& GetSst() const;
+ /** Returns the color buffer. */
+ XclImpPalette& GetPalette() const;
+ /** Returns the font buffer. */
+ XclImpFontBuffer& GetFontBuffer() const;
+ /** Returns the number format buffer. */
+ XclImpNumFmtBuffer& GetNumFmtBuffer() const;
+ /** Returns the cell formatting attributes buffer. */
+ XclImpXFBuffer& GetXFBuffer() const;
+ /** Returns the buffer of XF index ranges for a sheet. */
+ XclImpXFRangeBuffer& GetXFRangeBuffer() const;
+
+ /** Returns the buffer that contains all print areas in the document. */
+ ScRangeListTabs& GetPrintAreaBuffer() const;
+ /** Returns the buffer that contains all print titles in the document. */
+ ScRangeListTabs& GetTitleAreaBuffer() const;
+
+ /** Returns the buffer that contains the sheet creation order. */
+ XclImpTabInfo& GetTabInfo() const;
+ /** Returns the buffer that contains internal defined names. */
+ XclImpNameManager& GetNameManager() const;
+ /** Returns the link manager. */
+ XclImpLinkManager& GetLinkManager() const;
+
+ /** Returns the drawing object manager. */
+ XclImpObjectManager& GetObjectManager() const;
+ /** Returns the drawing container of the current sheet. */
+ XclImpSheetDrawing& GetCurrSheetDrawing() const;
+ /** Returns the conditional formatting manager. */
+ XclImpCondFormatManager& GetCondFormatManager() const;
+
+ XclImpValidationManager& GetValidationManager() const;
+ /** Returns the filter manager. */
+ XclImpAutoFilterBuffer& GetFilterManager() const;
+ /** Returns the web query buffer. */
+ XclImpWebQueryBuffer& GetWebQueryBuffer() const;
+ /** Returns the pivot table manager. */
+ XclImpPivotTableManager& GetPivotTableManager() const;
+ /** Returns the sheet protection options of the current sheet. */
+ XclImpSheetProtectBuffer& GetSheetProtectBuffer() const;
+ /** Returns the document protection options. */
+ XclImpDocProtectBuffer& GetDocProtectBuffer() const;
+
+ /** Returns the page settings of the current sheet. */
+ XclImpPageSettings& GetPageSettings() const;
+ /** Returns the view settings of the entire document. */
+ XclImpDocViewSettings& GetDocViewSettings() const;
+ /** Returns the view settings of the current sheet. */
+ XclImpTabViewSettings& GetTabViewSettings() const;
+
+ /** Returns the Calc add-in function name for an Excel function name. */
+ static OUString GetScAddInName( const OUString& rXclName );
+
+ /** Returns true, if the document contains a VB project. */
+ bool HasBasic() const { return mrImpData.mbHasBasic; }
+ /** Called to indicate that the document contains a VB project. */
+ void SetHasBasic() { mrImpData.mbHasBasic = true; }
+ /** Reads the CODENAME record and inserts the codename into the document. */
+ void ReadCodeName( XclImpStream& rStrm, bool bGlobals );
+
+ ScDocumentImport& GetDocImport();
+
+private:
+ XclImpRootData& mrImpData; /// Reference to the global import data struct.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xistream.hxx b/sc/source/filter/inc/xistream.hxx
new file mode 100644
index 0000000000..df440f3b9b
--- /dev/null
+++ b/sc/source/filter/inc/xistream.hxx
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <comphelper/docpasswordhelper.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <filter/msfilter/mscodec.hxx>
+#include <tools/stream.hxx>
+#include <memory>
+#include "xlstream.hxx"
+#include "xlconst.hxx"
+
+class XclImpRoot;
+
+/* ============================================================================
+Input stream class for Excel import
+- CONTINUE record handling
+- Decryption
+============================================================================ */
+
+// Decryption
+
+class XclImpDecrypter;
+typedef std::shared_ptr< XclImpDecrypter > XclImpDecrypterRef;
+
+/** Base class for BIFF stream decryption. */
+class XclImpDecrypter : public ::comphelper::IDocPasswordVerifier
+{
+public:
+ explicit XclImpDecrypter();
+ virtual ~XclImpDecrypter() override;
+
+ /** Returns the current error code of the decrypter. */
+ const ErrCode& GetError() const { return mnError; }
+ /** Returns true, if the decoder has been initialized correctly. */
+ bool IsValid() const { return mnError == ERRCODE_NONE; }
+
+ /** Creates a (ref-counted) copy of this decrypter object. */
+ XclImpDecrypterRef Clone() const;
+
+ /** Implementation of the ::comphelper::IDocPasswordVerifier interface */
+ virtual ::comphelper::DocPasswordVerifierResult verifyPassword( const OUString& rPassword, css::uno::Sequence< css::beans::NamedValue >& o_rEncryptionData ) override;
+ virtual ::comphelper::DocPasswordVerifierResult verifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) override;
+
+ /** Updates the decrypter on start of a new record or after seeking stream. */
+ void Update( const SvStream& rStrm, sal_uInt16 nRecSize );
+ /** Reads and decrypts nBytes bytes and stores data into the existing(!) buffer pData.
+ @return Count of bytes really read. */
+ sal_uInt16 Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes );
+
+protected:
+ /** Protected copy c'tor for OnClone(). */
+ explicit XclImpDecrypter( const XclImpDecrypter& rSrc );
+
+private:
+ /** Implementation of cloning this object. */
+ virtual XclImpDecrypter* OnClone() const = 0;
+ /** Derived classes implement password verification and initialization of
+ the decoder. */
+ virtual css::uno::Sequence< css::beans::NamedValue >
+ OnVerifyPassword( const OUString& rPassword ) = 0;
+ virtual bool OnVerifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) = 0;
+
+ /** Implementation of updating the decrypter. */
+ virtual void OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 nRecSize ) = 0;
+ /** Implementation of the decryption. */
+ virtual sal_uInt16 OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) = 0;
+
+private:
+ ErrCode mnError; /// Decrypter error code.
+ sal_uInt64 mnOldPos; /// Last known stream position.
+ sal_uInt16 mnRecSize; /// Current record size.
+};
+
+/** Decrypts BIFF5 stream contents. */
+class XclImpBiff5Decrypter : public XclImpDecrypter
+{
+public:
+ explicit XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash );
+
+private:
+ /** Private copy c'tor for OnClone(). */
+ explicit XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc );
+
+ /** Implementation of cloning this object. */
+ virtual XclImpBiff5Decrypter* OnClone() const override;
+ /** Implements password verification and initialization of the decoder. */
+ virtual css::uno::Sequence< css::beans::NamedValue >
+ OnVerifyPassword( const OUString& rPassword ) override;
+ virtual bool OnVerifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) override;
+ /** Implementation of updating the decrypter. */
+ virtual void OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 nRecSize ) override;
+ /** Implementation of the decryption. */
+ virtual sal_uInt16 OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) override;
+
+private:
+ ::msfilter::MSCodec_XorXLS95 maCodec; /// Crypto algorithm implementation.
+ css::uno::Sequence< css::beans::NamedValue > maEncryptionData;
+ sal_uInt16 mnKey;
+ sal_uInt16 mnHash;
+};
+
+/** Decrypts BIFF8 stream contents using the given document identifier. */
+class XclImpBiff8Decrypter : public XclImpDecrypter
+{
+private:
+ /** Implements password verification and initialization of the decoder. */
+ virtual css::uno::Sequence< css::beans::NamedValue >
+ OnVerifyPassword( const OUString& rPassword ) override;
+ virtual bool OnVerifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) override;
+ /** Implementation of updating the decrypter. */
+ virtual void OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 nRecSize ) override;
+ /** Implementation of the decryption. */
+ virtual sal_uInt16 OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) override;
+
+ /** Returns the block number corresponding to the passed stream position. */
+ static sal_uInt32 GetBlock( std::size_t nStrmPos );
+ /** Returns the block offset corresponding to the passed stream position. */
+ static sal_uInt16 GetOffset( std::size_t nStrmPos );
+
+protected:
+ explicit XclImpBiff8Decrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash);
+
+ explicit XclImpBiff8Decrypter(const XclImpBiff8Decrypter& rSrc);
+
+ css::uno::Sequence< css::beans::NamedValue > maEncryptionData;
+ std::vector< sal_uInt8 > maSalt;
+ std::vector< sal_uInt8 > maVerifier;
+ std::vector< sal_uInt8 > maVerifierHash;
+ msfilter::MSCodec97* mpCodec; /// Crypto algorithm implementation.
+};
+
+class XclImpBiff8StdDecrypter : public XclImpBiff8Decrypter
+{
+public:
+ explicit XclImpBiff8StdDecrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : XclImpBiff8Decrypter(std::move(rSalt), std::move(rVerifier), std::move(rVerifierHash))
+ {
+ mpCodec = &maCodec;
+ }
+
+private:
+ /** Private copy c'tor for OnClone(). */
+ explicit XclImpBiff8StdDecrypter(const XclImpBiff8StdDecrypter& rSrc);
+
+ /** Implementation of cloning this object. */
+ virtual XclImpBiff8StdDecrypter* OnClone() const override;
+
+private:
+ ::msfilter::MSCodec_Std97 maCodec; /// Crypto algorithm implementation.
+};
+
+class XclImpBiff8CryptoAPIDecrypter : public XclImpBiff8Decrypter
+{
+public:
+ explicit XclImpBiff8CryptoAPIDecrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : XclImpBiff8Decrypter(std::move(rSalt), std::move(rVerifier), std::move(rVerifierHash))
+ {
+ mpCodec = &maCodec;
+ }
+
+private:
+ /** Private copy c'tor for OnClone(). */
+ explicit XclImpBiff8CryptoAPIDecrypter(const XclImpBiff8CryptoAPIDecrypter& rSrc);
+
+ /** Implementation of cloning this object. */
+ virtual XclImpBiff8CryptoAPIDecrypter* OnClone() const override;
+
+private:
+ ::msfilter::MSCodec_CryptoAPI maCodec; /// Crypto algorithm implementation.
+};
+
+// Stream
+
+/** This class represents an Excel stream position.
+ @descr It contains the relevant data for a stream position inside of a record
+ (including CONTINUE records). */
+class XclImpStreamPos
+{
+public:
+ /** Constructs an invalid stream position data object. */
+ explicit XclImpStreamPos();
+
+ /** Sets the stream position data to the passed values. */
+ void Set( const SvStream& rStrm, std::size_t nNextPos, std::size_t nCurrSize,
+ sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
+ bool bValid );
+
+ /** Writes the contained stream position data to the given variables. */
+ void Get( SvStream& rStrm, std::size_t& rnNextPos, std::size_t& rnCurrSize,
+ sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
+ bool& rbValid ) const;
+
+ /** Returns the stored stream position. */
+ std::size_t GetPos() const { return mnPos; }
+
+private:
+ std::size_t mnPos; /// Absolute position of the stream.
+ std::size_t mnNextPos; /// Absolute position of next record.
+ std::size_t mnCurrSize; /// Current calculated size of the record.
+ sal_uInt16 mnRawRecId; /// Current raw record ID (including CONTINUEs).
+ sal_uInt16 mnRawRecSize; /// Current raw record size (without following CONTINUEs).
+ sal_uInt16 mnRawRecLeft; /// Bytes left in current raw record (without following CONTINUEs).
+ bool mbValid; /// Read state: false = record overread.
+};
+
+/** This class is used to import record oriented streams.
+ @descr An instance is constructed with an SvStream. The SvStream stream is
+ reset to its start while constructing this stream.
+
+ To start reading a record call StartNextRecord(). Now it is possible to
+ read all contents of the record using operator>>() or any of the Read***()
+ functions. If some data exceeds the record size limit, the stream looks for
+ a following CONTINUE record and jumps automatically to it. It is NOT
+ allowed that an atomic data type is split into two records (i.e. 4 bytes of
+ a double in one record and the other 4 bytes in a following CONTINUE).
+
+ Trying to read over the record limits results in a stream error. The
+ IsValid() function indicates that with returning false. From now on it is
+ undefined what data the read functions will return. The error state will be
+ reset, if the record is reset (with the method ResetRecord()) or if the
+ next record is started.
+
+ To switch off the automatic lookup of CONTINUE records, use ResetRecord()
+ with false parameter. This is useful i.e. on import of Escher objects,
+ where sometimes solely CONTINUE records will occur. The automatic lookup
+ keeps switched off until the method ResetRecord() is called with parameter
+ true. All other settings done on the stream (i.e. alternative CONTINUE
+ record identifier, enabled decryption, NUL substitution character) will be
+ reset to default values, if a new record is started.
+
+ The import stream supports decrypting the stream data. The contents of a
+ record (not the record header) will be encrypted by Excel if the file has
+ been stored with password protection. The functions SetDecrypter(),
+ EnableDecryption(), and DisableDecryption() control the usage of the
+ decryption algorithms. SetDecrypter() sets a new decryption algorithm and
+ initially enables it. DisableDecryption() may be used to stop the usage of
+ the decryption temporarily (sometimes record contents are never encrypted,
+ i.e. all BOF records or the stream position in BOUNDSHEET). Decryption will
+ be re-enabled automatically, if a new record is started with the function
+ StartNextRecord().
+
+ It is possible to store several stream positions inside a record (including
+ its CONTINUE records). The positions are stored on a stack, which can be
+ controlled with the functions PushPosition(), PopPosition() and
+ RejectPosition(). The stack will be cleared whenever a new record is
+ started with the function StartNextRecord().
+
+ Additionally a single global stream position can be stored which keeps
+ valid during the whole import process (methods StoreGlobalPosition(),
+ SeekGlobalPosition() and DeleteGlobalPosition()). This is the only way to
+ jump back to a previous record (that is a real jump without return).
+*/
+class XclImpStream
+{
+public:
+ /** Detects the BIFF version of the passed workbook stream. */
+ static XclBiff DetectBiffVersion( SvStream& rStrm );
+
+ /** Constructs the Excel record import stream using a TOOLS stream object.
+ @param rInStrm The system input stream. Will be set to its start position.
+ Must exist as long as this object exists */
+ explicit XclImpStream(
+ SvStream& rInStrm,
+ const XclImpRoot& rRoot );
+
+ ~XclImpStream();
+
+ /** Returns the filter root data. */
+ const XclImpRoot& GetRoot() const { return mrRoot; }
+
+ /** Sets stream pointer to the start of the next record content.
+ @descr Ignores all CONTINUE records of the current record, if automatic
+ CONTINUE usage is switched on.
+ @return false = no record found (end of stream). */
+ bool StartNextRecord();
+ /** Sets stream pointer to the start of the record content for the record
+ at the passed absolute stream position.
+ @return false = no record found (end of stream). */
+ bool StartNextRecord( std::size_t nNextRecPos );
+ /** Sets stream pointer to begin of record content.
+ @param bContLookup Automatic CONTINUE lookup on/off. In difference
+ to other stream settings, this setting is persistent until next call of
+ this function (because it is wanted to receive the next CONTINUE
+ records separately).
+ @param nAltContId Sets an alternative record ID for content
+ continuation. This value is reset automatically when a new record is
+ started with StartNextRecord(). */
+ void ResetRecord( bool bContLookup,
+ sal_uInt16 nAltContId = EXC_ID_UNKNOWN );
+ /** Sets stream pointer before current record and invalidates stream.
+ @descr The next call to StartNextRecord() will start again the current
+ record. This can be used in situations where a loop or a function
+ leaves on a specific record, but the parent context expects to start
+ this record by itself. The stream is invalid as long as the first
+ record has not been started (it is not allowed to call any other stream
+ operation then). */
+ void RewindRecord();
+
+ /** Enables decryption of record contents for the rest of the stream. */
+ void SetDecrypter( XclImpDecrypterRef const & xDecrypter );
+ /** Sets decrypter from another stream. */
+ void CopyDecrypterFrom( const XclImpStream& rStrm );
+ /** Switches usage of current decryption algorithm on/off.
+ @descr Encryption is re-enabled automatically, if a new record is
+ started using the function StartNextRecord(). */
+ void EnableDecryption( bool bEnable = true );
+ /** Switches usage of current decryption algorithm off.
+ @descr This is a record-local setting. The function StartNextRecord()
+ always enables decryption. */
+ void DisableDecryption() { EnableDecryption( false ); }
+
+ /** Pushes current position on user position stack.
+ @descr This stack is emptied when starting a new record with
+ StartNextRecord(). The decryption state (enabled/disabled) is not
+ pushed onto the stack. */
+ void PushPosition();
+ /** Seeks to last position from user position stack.
+ @descr This position will be removed from the stack. */
+ void PopPosition();
+
+ /** Stores current position. This position keeps valid in all records. */
+ void StoreGlobalPosition();
+ /** Seeks to the stored global user position. */
+ void SeekGlobalPosition();
+
+ /** Returns record reading state: false = record overread. */
+ bool IsValid() const { return mbValid; }
+ /** Returns the current record ID. */
+ sal_uInt16 GetRecId() const { return mnRecId; }
+ /** Returns the position inside of the whole record content. */
+ std::size_t GetRecPos() const;
+ /** Returns the data size of the whole record without record headers. */
+ std::size_t GetRecSize();
+ /** Returns remaining data size of the whole record without record headers. */
+ std::size_t GetRecLeft();
+ /** Returns the record ID of the following record. */
+ sal_uInt16 GetNextRecId();
+
+ sal_uInt16 PeekRecId( std::size_t nPos );
+
+ [[nodiscard]]
+ sal_uInt8 ReaduInt8();
+ [[nodiscard]]
+ sal_Int16 ReadInt16();
+ [[nodiscard]]
+ sal_uInt16 ReaduInt16();
+ [[nodiscard]]
+ sal_Int32 ReadInt32();
+ [[nodiscard]]
+ sal_uInt32 ReaduInt32();
+ [[nodiscard]]
+ double ReadDouble();
+
+ /** Reads nBytes bytes to the existing(!) buffer pData.
+ @return Count of bytes really read. */
+ std::size_t Read( void* pData, std::size_t nBytes );
+ /** Copies nBytes bytes to rOutStrm.
+ @return Count of bytes really written. */
+ std::size_t CopyToStream( SvStream& rOutStrm, std::size_t nBytes );
+
+ /** Copies the entire record to rOutStrm. The current record position keeps unchanged. */
+ void CopyRecordToStream( SvStream& rOutStrm );
+
+ /** Seeks absolute in record content to the specified position.
+ @descr The value 0 means start of record, independent from physical stream position. */
+ void Seek( std::size_t nPos );
+ /** Seeks forward inside the current record. */
+ void Ignore( std::size_t nBytes );
+
+ // *** special string functions *** ---------------------------------------
+
+ // *** read/ignore unicode strings *** ------------------------------------
+ /* - look for CONTINUE records even if CONTINUE handling disabled
+ (only if inside of a CONTINUE record - for TXO import)
+ - no overread assertions (for Applix wrong string length export bug)
+
+ structure of an Excel unicode string:
+ (1) 2 byte character count
+ (2) 1 byte flags (16-bit-characters, rich string, far east string)
+ (3) [2 byte rich string format run count]
+ (4) [4 byte far east data size]
+ (5) character array
+ (6) [4 * (rich string format run count) byte]
+ (7) [(far east data size) byte]
+ header = (1), (2)
+ ext. header = (3), (4)
+ ext. data = (6), (7)
+ */
+
+ /** Reads ext. header, detects 8/16 bit mode, sets all ext. info.
+ @return Total size of ext. data. */
+ std::size_t ReadUniStringExtHeader(
+ bool& rb16Bit, bool& rbRich, bool& rbFareast,
+ sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags );
+ /** Seeks to begin of character array, detects 8/16 bit mode.
+ @return Total size of ext. data. */
+ std::size_t ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags );
+
+ /** Sets a replacement character for NUL characters.
+ @descr NUL characters must be replaced, because Tools strings cannot
+ handle them. The substitution character is reset to '?' automatically,
+ if a new record is started using the function StartNextRecord().
+ @param cNulSubst The character to use for NUL replacement. It is
+ possible to specify NUL here. in this case strings are terminated when
+ the first NUL occurs during string import. */
+ void SetNulSubstChar( sal_Unicode cNulSubst = '?' ) { mcNulSubst = cNulSubst; }
+
+ /** Reads nChars characters and returns the string. */
+ OUString ReadRawUniString( sal_uInt16 nChars, bool b16Bit );
+ /** Reads ext. header, nChar characters, ext. data and returns the string. */
+ OUString ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags );
+ /** Reads 8 bit flags, ext. header, nChar characters, ext. data and returns the string. */
+ OUString ReadUniString( sal_uInt16 nChars );
+ /** Reads 16 bit character count, 8 bit flags, ext. header, character array,
+ ext. data and returns the string. */
+ OUString ReadUniString();
+
+ /** Ignores nChars characters. */
+ void IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit );
+ /** Ignores ext. header, nChar characters, ext. data. */
+ void IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags );
+ /** Ignores 8 bit flags, ext. header, nChar characters, ext. data. */
+ void IgnoreUniString( sal_uInt16 nChars );
+
+ // *** read/ignore 8-bit-strings, store in String *** ---------------------
+
+ /** Reads nChar byte characters and returns the string. */
+ OUString ReadRawByteString( sal_uInt16 nChars );
+ /** Reads 8/16 bit string length, character array and returns the string. */
+ OUString ReadByteString( bool b16BitLen );
+
+ // *** SvStream functions *** ---------------------------------------------
+
+ /** Returns the absolute stream position. */
+ std::size_t GetSvStreamPos() const { return mrStrm.Tell(); }
+ /** Returns the stream size. */
+ std::size_t GetSvStreamSize() const { return mnStreamSize; }
+
+ /** Stores current stream position into rPos. */
+ void StorePosition( XclImpStreamPos& rPos );
+ /** Restores stream position contained in rPos. */
+ void RestorePosition( const XclImpStreamPos& rPos );
+
+ /** Set an SVSTREAM_..._ERROR. */
+ void SetSvStreamError( const ErrCode& rErrCode )
+ { mrStrm.SetError( rErrCode ); }
+
+private:
+ /** Seeks to next raw record header and reads record ID and size.
+ @descr This is a "raw" function, means that stream members are
+ inconsistent after return. Does only change mnRawRecId, mnRawRecSize,
+ and the base stream position, but no other members.
+ @return false = No record header found (end of stream). */
+ bool ReadNextRawRecHeader();
+
+ /** Initializes the decrypter to read a new record. */
+ void SetupDecrypter();
+ /** Initializes all members after base stream has been sought to new raw record. */
+ void SetupRawRecord();
+ /** Initializes all members after base stream has been sought to new record. */
+ void SetupRecord();
+
+ /** Returns true, if the passed ID is real or alternative continuation record ID. */
+ bool IsContinueId( sal_uInt16 nRecId ) const;
+
+ /** Goes to start of the next CONTINUE record.
+ @descr Stream must be located at the end of a raw record, and handling
+ of CONTINUE records must be enabled.
+ @return Copy of mbValid. */
+ bool JumpToNextContinue();
+ /** Goes to start of the next CONTINUE record while reading strings.
+ @descr Stream must be located at the end of a raw record. If reading
+ has been started in a CONTINUE record, jumps to an existing following
+ CONTINUE record, even if handling of CONTINUE records is disabled (This
+ is a special handling for TXO string data). Reads additional Unicode
+ flag byte at start of the new raw record and sets or resets rb16Bit.
+ @return Copy of mbValid. */
+ bool JumpToNextStringContinue( bool& rb16Bit );
+
+ /** Ensures that reading nBytes bytes is possible with next stream access.
+ @descr Stream must be located at the end of a raw record, and handling
+ of CONTINUE records must be enabled.
+ @return Copy of mbValid. */
+ bool EnsureRawReadSize( sal_uInt16 nBytes );
+ /** Returns the maximum size of raw data possible to read in one block. */
+ sal_uInt16 GetMaxRawReadSize( std::size_t nBytes ) const;
+
+ /** Reads and decrypts nBytes bytes to the existing(!) buffer pData.
+ @return Count of bytes really read. */
+ sal_uInt16 ReadRawData( void* pData, sal_uInt16 nBytes );
+
+private:
+ SvStream& mrStrm; /// Reference to the system input stream.
+ const XclImpRoot& mrRoot; /// Filter root data.
+
+ XclImpDecrypterRef mxDecrypter; /// Provides methods to decrypt data.
+
+ XclImpStreamPos maFirstRec; /// Start position of current record.
+ std::vector< XclImpStreamPos >
+ maPosStack; /// Stack for record positions.
+
+ XclImpStreamPos maGlobPos; /// User defined position elsewhere in stream.
+ sal_uInt16 mnGlobRecId; /// Record ID for user defined position.
+ bool mbGlobValidRec; /// Was user position a valid record?
+ bool mbHasGlobPos; /// Is user position defined?
+
+ std::size_t mnStreamSize; /// Size of system stream.
+ std::size_t mnNextRecPos; /// Start of next record header.
+ std::size_t mnCurrRecSize; /// Helper for record position.
+ std::size_t mnComplRecSize; /// Size of complete record data (with CONTINUEs).
+ bool mbHasComplRec; /// true = mnComplRecSize is valid.
+
+ sal_uInt16 mnRecId; /// Current record ID (not the CONTINUE ID).
+ sal_uInt16 mnAltContId; /// Alternative record ID for content continuation.
+
+ sal_uInt16 mnRawRecId; /// Current raw record ID (including CONTINUEs).
+ sal_uInt16 mnRawRecSize; /// Current raw record size (without following CONTINUEs).
+ sal_uInt16 mnRawRecLeft; /// Bytes left in current raw record (without following CONTINUEs).
+
+ sal_Unicode mcNulSubst; /// Replacement for NUL characters.
+
+ bool mbCont; /// Automatic CONTINUE lookup on/off.
+ bool mbUseDecr; /// Usage of decryption.
+ bool mbValidRec; /// false = No more records to read.
+ bool mbValid; /// false = Record overread.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xistring.hxx b/sc/source/filter/inc/xistring.hxx
new file mode 100644
index 0000000000..ec0822a343
--- /dev/null
+++ b/sc/source/filter/inc/xistring.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include "xlstring.hxx"
+
+// Byte/Unicode strings =======================================================
+
+class XclImpStream;
+
+/** This class represents an unformatted or formatted string and provides importing from stream. */
+class XclImpString
+{
+public:
+ /** Constructs an empty string. */
+ explicit XclImpString();
+ /** Constructs an unformatted string. */
+ explicit XclImpString( OUString aString );
+
+ /** Reads a complete string from the passed stream. */
+ void Read( XclImpStream& rStrm, XclStrFlags nFlags = XclStrFlags::NONE );
+
+ /** Sets the passed string data. */
+ void SetText( const OUString& rText ) { maString = rText; }
+ /** Sets the passed formatting buffer. */
+ void SetFormats( XclFormatRunVec&& rFormats ) { maFormats = std::move(rFormats); }
+ /** Reads and appends the formatting information (run count and runs) from stream. */
+ void ReadFormats( XclImpStream& rStrm ) { ReadFormats( rStrm, maFormats ); }
+ /** Reads and appends formatting runs from an OBJ or TXO record. */
+ void ReadObjFormats( XclImpStream& rStrm, sal_uInt16 nFormatSize ) { ReadObjFormats( rStrm, maFormats, nFormatSize ); }
+
+ /** Returns true, if the string is empty. */
+ bool IsEmpty() const { return maString.isEmpty(); }
+ /** Returns the pure text data of the string. */
+ const OUString& GetText() const { return maString; }
+
+ /** Returns true, if the string contains formatting information. */
+ bool IsRich() const { return !maFormats.empty(); }
+ /** Returns the formatting run vector. */
+ const XclFormatRunVec& GetFormats() const { return maFormats; }
+
+ /** Insert a formatting run to the passed format buffer. */
+ static void AppendFormat( XclFormatRunVec& rFormats, sal_uInt16 nChar, sal_uInt16 nFontIdx );
+ /** Reads and appends the formatting information (run count and runs) from stream. */
+ static void ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats );
+ /** Reads and appends nRunCount formatting runs from stream. */
+ static void ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nRunCount );
+ /** Reads and appends formatting runs from an OBJ or TXO record. */
+ static void ReadObjFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nFormatSize );
+
+private:
+ OUString maString; /// The text data of the string.
+ XclFormatRunVec maFormats; /// All formatting runs.
+};
+
+// String iterator ============================================================
+
+/** Iterates over formatted string portions. */
+class XclImpStringIterator
+{
+public:
+ explicit XclImpStringIterator( const XclImpString& rString );
+
+ /** Returns true, if the iterator references a valid text portion. */
+ bool Is() const { return mnTextBeg < mrText.getLength(); }
+ /** Returns the index of the current text portion. */
+ size_t GetPortionIndex() const { return mnPortion; }
+ /** Returns the string of the current text portion. */
+ OUString GetPortionText() const;
+ /** Returns the font index of the current text portion. */
+ sal_uInt16 GetPortionFont() const;
+
+ /** Moves iterator to next text portion. */
+ XclImpStringIterator& operator++();
+
+private:
+ const OUString& mrText; /// The processed string.
+ const XclFormatRunVec& mrFormats; /// The vector of formatting runs.
+ size_t mnPortion; /// Current text portion.
+ sal_Int32 mnTextBeg; /// First character of current portion.
+ sal_Int32 mnTextEnd; /// First character of next portion.
+ size_t mnFormatsBeg; /// Formatting run index for current portion.
+ size_t mnFormatsEnd; /// Formatting run index for next portion.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xistyle.hxx b/sc/source/filter/inc/xistyle.hxx
new file mode 100644
index 0000000000..b4e5250dc3
--- /dev/null
+++ b/sc/source/filter/inc/xistyle.hxx
@@ -0,0 +1,670 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/solar.h>
+#include <vector>
+#include <memory>
+#include <rangelst.hxx>
+#include "xlstyle.hxx"
+#include "xiroot.hxx"
+
+class ScPatternAttr;
+
+struct XclRange;
+struct ScAttrEntry;
+enum class SvxBoxItemLine;
+
+/* ============================================================================
+- Buffers for style records (PALETTE, FONT, FORMAT, XF)
+ and a container for XF indexes for every used cell in a sheet.
+============================================================================ */
+
+// PALETTE record - color information =========================================
+
+/** Stores the default colors for the current BIFF version and the contents of
+ a PALETTE record. */
+class XclImpPalette : public XclDefaultPalette
+{
+public:
+ explicit XclImpPalette( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Returns the color for a (non-zero-based) Excel palette entry.
+ @descr First looks for a color read from file, then looks for a default color.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Reads a PALETTE record. */
+ void ReadPalette( XclImpStream& rStrm );
+
+private:
+ void ExportPalette();
+ typedef ::std::vector< Color > ColorVec;
+ ColorVec maColorTable; /// Colors read from file.
+ const XclImpRoot& mrRoot;
+};
+
+// FONT record - font information =============================================
+
+/** Stores all data of an Excel font and provides import of FONT records. */
+class XclImpFont : protected XclImpRoot
+{
+public:
+ explicit XclImpFont( const XclImpRoot& rRoot );
+
+ /** Constructs a font from font data.
+ @descr Special handling for font style (bold, italic) in font name,
+ overwrites settings in rFontData. */
+ explicit XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData );
+
+ /** Sets all font attributes to used or unused. */
+ void SetAllUsedFlags( bool bUsed );
+ /** Sets the passed font data and all used flags to 'used'. */
+ void SetFontData( const XclFontData& rFontData, bool bHasCharSet );
+
+ /** Returns read-only access to font data. */
+ const XclFontData& GetFontData() const { return maData; }
+ /** Returns true, if the font character set is valid. */
+ bool HasCharSet() const { return mbHasCharSet; }
+ /** Returns true, if the font contains superscript or subscript. */
+ bool HasEscapement() const { return maData.mnEscapem != EXC_FONTESC_NONE; }
+ /** Returns the text encoding for strings used with this font. */
+ rtl_TextEncoding GetFontEncoding() const;
+
+ /** Returns true, if this font contains characters for Asian scripts (CJK). */
+ bool HasAsianChars() const { return mbHasAsian; }
+
+ /** Reads a FONT record for all BIFF versions. */
+ void ReadFont( XclImpStream& rStrm );
+ /** Reads an EFONT record (BIFF2 font color). */
+ void ReadEfont( XclImpStream& rStrm );
+ /** Reads the font block from a CF (conditional format) record. */
+ void ReadCFFontBlock( XclImpStream& rStrm );
+
+ /** Fills all font properties to the item set.
+ @param rItemSet The destination item set.
+ @param eType The type of Which-IDs.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType,
+ bool bSkipPoolDefs = false ) const;
+ /** Writes all font properties to the passed property set.
+ @param pFontColor If set, overrides internal stored font color. */
+ void WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, const Color* pFontColor = nullptr ) const;
+
+private:
+ /** Reads and sets height and flags. */
+ void ReadFontData2( XclImpStream& rStrm );
+ /** Reads and sets height, flags, color, boldness, script, family and charset. */
+ void ReadFontData5( XclImpStream& rStrm );
+
+ /** Reads and sets the font color. */
+ void ReadFontColor( XclImpStream& rStrm );
+
+ /** Reads and sets a byte string as font name. */
+ void ReadFontName2( XclImpStream& rStrm );
+ /** Reads and sets a Unicode string as font name. */
+ void ReadFontName8( XclImpStream& rStrm );
+
+ /** Tests whether the font contains CJK or CTL characters.
+ @descr This is only a weak guess using preselected characters. */
+ void GuessScriptType();
+
+private:
+ XclFontData maData; /// All font attributes.
+ bool mbHasCharSet; /// true = Font contains own character set info.
+ bool mbHasWstrn; /// true = Font contains Western script characters.
+ bool mbHasAsian; /// true = Font contains Asian script characters.
+ bool mbHasCmplx; /// true = Font contains Complex script characters.
+ bool mbFontNameUsed; /// true = Font name, family, charset used.
+ bool mbHeightUsed; /// true = Font height used.
+ bool mbColorUsed; /// true = Color used.
+ bool mbWeightUsed; /// true = Weight used.
+ bool mbEscapemUsed; /// true = Escapement type used.
+ bool mbUnderlUsed; /// true = Underline style used.
+ bool mbItalicUsed; /// true = Italic used.
+ bool mbStrikeUsed; /// true = Strikeout used.
+ bool mbOutlineUsed; /// true = Outlined used.
+ bool mbShadowUsed; /// true = Shadowed used.
+};
+
+/** Stores the data of all fonts occurred in an Excel file. */
+class XclImpFontBuffer : protected XclImpRoot
+{
+public:
+ /** delete copy constructor */
+ XclImpFontBuffer(const XclImpFontBuffer&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpFontBuffer& operator=(const XclImpFontBuffer&) = delete;
+
+ explicit XclImpFontBuffer( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Returns the object that stores all contents of a FONT record. */
+ const XclImpFont* GetFont( sal_uInt16 nFontIndex ) const;
+ /** Returns the application font data of this file, needed i.e. for column width. */
+ const XclFontData& GetAppFontData() const { return maAppFont; }
+
+ /** Reads a FONT record. */
+ void ReadFont( XclImpStream& rStrm );
+ /** Reads an EFONT record (BIFF2 font color). */
+ void ReadEfont( XclImpStream& rStrm );
+
+ /** Fills all font properties from a FONT record to the item set.
+ @param rItemSet The destination item set.
+ @param eType The type of Which-IDs.
+ @param nFontIdx The Excel index of the font.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType,
+ sal_uInt16 nFontIdx, bool bSkipPoolDefs = false ) const;
+ /** Writes all font properties to the passed property set.
+ @param pFontColor If set, overrides internal stored font color. */
+ void WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ sal_uInt16 nFontIdx, const Color* pFontColor = nullptr ) const;
+ /** Writes default font properties for form controls to the passed property set. */
+ void WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const;
+
+private:
+ /** Updates the application default font. */
+ void UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet );
+
+private:
+ std::vector< XclImpFont > maFontList; /// List of all FONT records in the Excel file.
+ XclFontData maAppFont; /// Application font (for column width).
+ XclImpFont maFont4; /// Built-in font with index 4.
+ XclImpFont maCtrlFont; /// BIFF5 default form controls font (Helv,8pt,bold).
+};
+
+// FORMAT record - number formats =============================================
+
+/** Stores all user defined number formats occurred in the file. */
+class XclImpNumFmtBuffer : public XclNumFmtBuffer, protected XclImpRoot
+{
+public:
+ explicit XclImpNumFmtBuffer( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Reads a FORMAT record. */
+ void ReadFormat( XclImpStream& rStrm );
+
+ /** Read NumFmt from conditional format record */
+ sal_uInt16 ReadCFFormat( XclImpStream& rStrm, bool bIFmt );
+
+ /** Creates the number formats in the Calc document. */
+ void CreateScFormats();
+
+ /** Returns the format key with the passed Excel index or NUMBERFORMAT_ENTRY_NOT_FOUND on error. */
+ sal_uInt32 GetScFormat( sal_uInt16 nXclNumFmt ) const;
+
+ /** Fills an Excel number format to the passed item set.
+ @param rItemSet The destination item set.
+ @param nXclNumFmt The Excel number format index.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet(
+ SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt,
+ bool bSkipPoolDefs = false ) const;
+ /** Fills a Calc number format to the passed item set.
+ @param rItemSet The destination item set.
+ @param nScNumFmt The Calc number formatter index of the format.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillScFmtToItemSet(
+ SfxItemSet& rItemSet, sal_uInt32 nScNumFmt,
+ bool bSkipPoolDefs = false ) const;
+
+private:
+ typedef ::std::map< sal_uInt16, sal_uLong > XclImpIndexMap;
+
+ XclImpIndexMap maIndexMap; /// Maps Excel format indexes to Calc formats.
+ sal_uInt16 mnNextXclIdx; /// Index counter for BIFF2-BIFF4.
+};
+
+// XF, STYLE record - Cell formatting =========================================
+
+/** Extends the XclCellProt struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellProt : public XclCellProt
+{
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nNumFmt );
+ /** Fills this struct with BIFF3-BIFF8 XF record data. */
+ void FillFromXF3( sal_uInt16 nProt );
+
+ /** Inserts items representing this protection style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+};
+
+/** Extends the XclCellAlign struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellAlign : public XclCellAlign
+{
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nFlags );
+ /** Fills this struct with BIFF3 XF record data. */
+ void FillFromXF3( sal_uInt16 nAlign );
+ /** Fills this struct with BIFF4 XF record data. */
+ void FillFromXF4( sal_uInt16 nAlign );
+ /** Fills this struct with BIFF5/BIFF7 XF record data. */
+ void FillFromXF5( sal_uInt16 nAlign );
+ /** Fills this struct with BIFF8 XF record data. */
+ void FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib );
+ /** Fills this struct with CF record data. */
+ void FillFromCF( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib );
+
+ /** Inserts items representing this alignment style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs = false ) const;
+};
+
+/** Extends the XclCellBorder struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellBorder : public XclCellBorder
+{
+ bool mbLeftUsed; /// true = Left line style used.
+ bool mbRightUsed; /// true = Right line style used.
+ bool mbTopUsed; /// true = Top line style used.
+ bool mbBottomUsed; /// true = Bottom line style used.
+ bool mbDiagUsed; /// true = Diagonal line style used.
+
+ explicit XclImpCellBorder();
+
+ /** Sets outer line states and diagonal line states to used or unused. */
+ void SetUsedFlags( bool bOuterUsed, bool bDiagUsed );
+
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nFlags );
+ /** Fills this struct with BIFF3/BIFF4 XF record data. */
+ void FillFromXF3( sal_uInt32 nBorder );
+ /** Fills this struct with BIFF5/BIFF7 XF record data. */
+ void FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea );
+ /** Fills this struct with BIFF8 XF record data. */
+ void FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 );
+
+ /** Fills this struct with BIFF8 CF (conditional format) record data. */
+ void FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags );
+
+ /** Returns true, if any of the outer border lines is visible. */
+ bool HasAnyOuterBorder() const;
+
+ /** Inserts a box item representing this border style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet(
+ SfxItemSet& rItemSet,
+ const XclImpPalette& rPalette,
+ bool bSkipPoolDefs = false ) const;
+};
+
+/** Extends the XclCellArea struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellArea : public XclCellArea
+{
+ bool mbForeUsed; /// true = Foreground color used.
+ bool mbBackUsed; /// true = Background color used.
+ bool mbPattUsed; /// true = Pattern used.
+
+ explicit XclImpCellArea();
+
+ /** Sets colors and pattern state to used or unused. */
+ void SetUsedFlags( bool bUsed );
+
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nFlags );
+ /** Fills this struct with BIFF3/BIFF4 XF record data. */
+ void FillFromXF3( sal_uInt16 nArea );
+ /** Fills this struct with BIFF5/BIFF7 XF record data. */
+ void FillFromXF5( sal_uInt32 nArea );
+ /** Fills this struct with BIFF8 XF record data. */
+ void FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea );
+
+ /** Fills this struct with BIFF8 CF (conditional format) record data. */
+ void FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags );
+
+ /** Inserts a brush item representing this area style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet(
+ SfxItemSet& rItemSet,
+ const XclImpPalette& rPalette,
+ bool bSkipPoolDefs = false ) const;
+};
+
+/** Represents an XF record index with additional information. */
+class XclImpXFIndex
+{
+public:
+ explicit XclImpXFIndex( sal_uInt16 nXFIndex, bool bBoolCell = false ) :
+ mnXFIndex( nXFIndex ), mbBoolCell( bBoolCell ) {}
+
+ sal_uInt16 GetXFIndex() const { return mnXFIndex; }
+ bool IsBoolCell() const { return mbBoolCell; }
+
+private:
+ sal_uInt16 mnXFIndex; /// The XF record index.
+ bool mbBoolCell; /// true = A Boolean value cell.
+};
+
+inline bool operator==( const XclImpXFIndex& rLeft, const XclImpXFIndex& rRight )
+{ return (rLeft.GetXFIndex() == rRight.GetXFIndex()) && (rLeft.IsBoolCell() == rRight.IsBoolCell()); }
+
+inline bool operator!=( const XclImpXFIndex& rLeft, const XclImpXFIndex& rRight )
+{ return !(rLeft == rRight); }
+
+/** Contains all data of a XF record and a Calc item set. */
+class XclImpXF : public XclXFBase, protected XclImpRoot
+{
+public:
+ /** make noncopyable */
+ XclImpXF(const XclImpXF&) = delete;
+ const XclImpXF& operator=(const XclImpXF&) = delete;
+
+ explicit XclImpXF( const XclImpRoot& rRoot );
+ virtual ~XclImpXF() override;
+
+ /** Reads an XF record. */
+ void ReadXF( XclImpStream& rStrm );
+
+ bool GetLineBreak() const { return maAlignment.mbLineBreak; }
+ sal_uInt8 GetHorAlign() const { return maAlignment.mnHorAlign; }
+ sal_uInt16 GetFontIndex() const { return mnXclFont; }
+
+ /** Creates a Calc item set containing an item set with all cell properties.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items.
+ @return A read-only reference to the item set stored internally. */
+ const ScPatternAttr& CreatePattern( bool bSkipPoolDefs = false );
+
+ void ApplyPatternToAttrVector(
+ ::std::vector<ScAttrEntry>& rAttrs, SCROW nRow1, SCROW nRow2,
+ sal_uInt32 nForceScNumFmt);
+
+ /** Inserts all formatting attributes to the specified area in the Calc document.
+ @param nForcedNumFmt If not set to NUMBERFORMAT_ENTRY_NOT_FOUND, it will overwrite
+ the number format of the XF. */
+ void ApplyPattern(
+ SCCOL nScCol1, SCROW nScRow1,
+ SCCOL nScCol2, SCROW nScRow2,
+ SCTAB nScTab );
+
+ /** Converts formatting information from BIFF2 cell record data directly. */
+ static void ApplyPatternForBiff2CellFormat(
+ const XclImpRoot& rRoot, const ScAddress& rScPos,
+ sal_uInt8 nFlags1, sal_uInt8 nFlags2, sal_uInt8 nFlags3 );
+
+private:
+ void ReadXF2( XclImpStream& rStrm );
+ void ReadXF3( XclImpStream& rStrm );
+ void ReadXF4( XclImpStream& rStrm );
+ void ReadXF5( XclImpStream& rStrm );
+ void ReadXF8( XclImpStream& rStrm );
+
+ /** Sets all "attribute used" flags according to the passed mask.
+ @descr In cell XFs, a set bit represents "used", in style XFs it is a cleared bit.
+ Therefore mbCellXF must be set correctly before calling this method. */
+ void SetUsedFlags( sal_uInt8 nUsedFlags );
+
+private:
+ typedef ::std::unique_ptr< ScPatternAttr > ScPatternAttrPtr;
+
+ ScPatternAttrPtr mpPattern; /// Calc item set.
+ ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+
+ XclImpCellProt maProtection; /// Cell protection flags.
+ XclImpCellAlign maAlignment; /// All alignment attributes.
+ XclImpCellBorder maBorder; /// Border line style.
+ XclImpCellArea maArea; /// Background area style.
+ sal_uInt16 mnXclNumFmt; /// Index to number format.
+ sal_uInt16 mnXclFont; /// Index to font record.
+};
+
+/** Contains all data of a cell style associated with an XF record. */
+class XclImpStyle : protected XclImpRoot
+{
+public:
+ explicit XclImpStyle( const XclImpRoot& rRoot );
+
+ /** Reads a STYLE record. */
+ void ReadStyle( XclImpStream& rStrm );
+
+ const OUString& GetName() const { return maName; }
+ sal_uInt16 GetXfId() const { return mnXfId; }
+ bool IsBuiltin() const { return mbBuiltin && (mnBuiltinId != EXC_STYLE_USERDEF); }
+ sal_uInt8 GetBuiltinId() const { return mnBuiltinId; }
+ sal_uInt8 GetLevel() const { return mnLevel; }
+
+ /** Creates a cell style sheet and inserts it into the Calc document.
+ @return The pointer to the cell style sheet, or 0, if there is no style sheet. */
+ ScStyleSheet* CreateStyleSheet();
+ /** Creates the Calc style sheet, if this is a user-defined style. */
+ void CreateUserStyle( const OUString& rFinalName );
+
+private:
+ OUString maName; /// Cell style name.
+ sal_uInt16 mnXfId; /// Formatting for this cell style.
+ sal_uInt8 mnBuiltinId; /// Identifier for builtin styles.
+ sal_uInt8 mnLevel; /// Level for builtin column/row styles.
+ bool mbBuiltin; /// True = builtin style.
+ bool mbCustom; /// True = customized builtin style.
+ bool mbHidden; /// True = style not visible in GUI.
+
+ OUString maFinalName; /// Final name used in the Calc document.
+ ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+};
+
+/** Contains all XF records occurred in the file.
+ @descr This class is able to read XF records (BIFF2 - BIFF8) and STYLE records (BIFF8). */
+class XclImpXFBuffer : protected XclImpRoot
+{
+public:
+ /** make noncopyable */
+ XclImpXFBuffer(const XclImpXFBuffer&) = delete;
+ const XclImpXFBuffer& operator=(const XclImpXFBuffer&) = delete;
+
+ explicit XclImpXFBuffer( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Reads an XF record. */
+ void ReadXF( XclImpStream& rStrm );
+ /** Reads a STYLE record. */
+ void ReadStyle( XclImpStream& rStrm );
+
+ /** Returns the object that stores all contents of an XF record. */
+ XclImpXF* GetXF( sal_uInt16 nXFIndex )
+ { return (nXFIndex >= maXFList.size()) ? nullptr : maXFList.at(nXFIndex).get(); }
+
+ const XclImpXF* GetXF( sal_uInt16 nXFIndex ) const
+ { return (nXFIndex >= maXFList.size()) ? nullptr : maXFList.at(nXFIndex).get(); }
+
+ /** Returns the index to the Excel font used in the specified XF record. */
+ sal_uInt16 GetFontIndex( sal_uInt16 nXFIndex ) const;
+ /** Returns the Excel font used in the specified XF record. */
+ const XclImpFont* GetFont( sal_uInt16 nXFIndex ) const;
+
+ /** Creates all user defined style sheets. */
+ void CreateUserStyles();
+ /** Creates a cell style sheet of the passed XF and inserts it into the Calc document.
+ @return The pointer to the cell style sheet, or 0, if there is no style sheet. */
+ ScStyleSheet* CreateStyleSheet( sal_uInt16 nXFIndex );
+
+private:
+ typedef std::vector< std::unique_ptr<XclImpStyle> > XclImpStyleList;
+ typedef ::std::map< sal_uInt16, XclImpStyle* > XclImpStyleMap;
+
+ std::vector< std::unique_ptr<XclImpXF> > maXFList; /// List of contents of all XF record.
+ XclImpStyleList maBuiltinStyles; /// List of built-in cell styles.
+ XclImpStyleList maUserStyles; /// List of user defined cell styles.
+ XclImpStyleMap maStylesByXf; /// Maps XF records to cell styles.
+};
+
+// Buffer for XF indexes in cells =============================================
+
+/** Contains an (encoded) XF index for a range of rows in a single column. */
+class XclImpXFRange
+{
+public:
+ SCROW mnScRow1; /// The first row of an equal-formatted range.
+ SCROW mnScRow2; /// The last row of an equal-formatted range.
+ XclImpXFIndex maXFIndex; /// Extended XF index.
+
+ inline explicit XclImpXFRange( SCROW nScRow, const XclImpXFIndex& rXFIndex );
+ inline explicit XclImpXFRange( SCROW nFirstScRow, SCROW nLastScRow, const XclImpXFIndex& rXFIndex );
+
+ /** Returns true, if nScRow is contained in own row range. */
+ inline bool Contains( SCROW nScRow ) const;
+
+ /** Returns true, if the range has been expanded. */
+ bool Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex );
+ /** Returns true, if the range has been expanded. */
+ bool Expand( const XclImpXFRange& rNextRange );
+};
+
+inline XclImpXFRange::XclImpXFRange( SCROW nScRow, const XclImpXFIndex& rXFIndex ) :
+ mnScRow1( nScRow ),
+ mnScRow2( nScRow ),
+ maXFIndex( rXFIndex )
+{
+}
+
+inline XclImpXFRange::XclImpXFRange( SCROW nFirstScRow, SCROW nLastScRow, const XclImpXFIndex& rXFIndex ) :
+ mnScRow1( nFirstScRow ),
+ mnScRow2( nLastScRow ),
+ maXFIndex( rXFIndex )
+{
+}
+
+inline bool XclImpXFRange::Contains( SCROW nScRow ) const
+{
+ return (mnScRow1 <= nScRow) && (nScRow <= mnScRow2);
+}
+
+/** Contains the XF indexes for every used cell in a column. */
+class XclImpXFRangeColumn
+{
+public:
+ /** make noncopyable */
+ XclImpXFRangeColumn(const XclImpXFRangeColumn&) = delete;
+ const XclImpXFRangeColumn& operator=(const XclImpXFRangeColumn&) = delete;
+
+ typedef std::vector< std::unique_ptr<XclImpXFRange> > IndexList;
+
+ explicit XclImpXFRangeColumn() {}
+
+ IndexList::iterator begin() { return maIndexList.begin(); }
+ IndexList::iterator end() { return maIndexList.end(); }
+
+ /** Inserts a single row range into the list. */
+ void SetDefaultXF( const XclImpXFIndex& rXFIndex, const XclImpRoot& rRoot );
+
+ /** Inserts a new (encoded) XF index (first try to expand the last range). */
+ void SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex );
+
+private:
+ /** Finds the previous and next row range from row position nScRow.
+ @descr If an XF still exists, it is contained in rpPrevRange. */
+ void Find(
+ XclImpXFRange*& rpPrevRange,
+ XclImpXFRange*& rpNextRange,
+ sal_uLong& rnNextIndex,
+ SCROW nScRow );
+
+ /** Tries to concatenate a range with its predecessor.
+ @descr The ranges must have the same XF index and must not have a gap.
+ The resulting range has the index nIndex-1. */
+ void TryConcatPrev( sal_uLong nIndex );
+
+ /** Insert a range into the list at the specified index. */
+ void Insert(XclImpXFRange* pXFRange, sal_uLong nIndex);
+
+private:
+ IndexList maIndexList; /// The list of XF index range.
+};
+
+/** Contains the XF indexes for every used cell in a single sheet. */
+class XclImpXFRangeBuffer : protected XclImpRoot
+{
+public:
+ /** make noncopyable */
+ XclImpXFRangeBuffer(const XclImpXFRangeBuffer&) = delete;
+ const XclImpXFRangeBuffer& operator=(const XclImpXFRangeBuffer&) = delete;
+
+ explicit XclImpXFRangeBuffer( const XclImpRoot& rRoot );
+ virtual ~XclImpXFRangeBuffer() override;
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Inserts a new XF index. */
+ void SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for blank cells. */
+ void SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for boolean cells. */
+ void SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for all cells in a row. */
+ void SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for all cells in a column. */
+ void SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex );
+
+ /** Inserts a range of hyperlink cells. */
+ void SetHyperlink( const XclRange& rXclRange, const OUString& rUrl );
+
+ /** Inserts a complete merged cell range. */
+ void SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 );
+
+ /** Applies styles and cell merging to the current sheet in the document. */
+ void Finalize();
+
+private:
+ /** Insertion mode of an XF index. */
+ enum XclImpXFInsertMode
+ {
+ xlXFModeCell, /// Filled cell.
+ xlXFModeBoolCell, /// Cell with a single Boolean value.
+ xlXFModeBlank, /// Blank cell.
+ xlXFModeRow /// Row default XF.
+ };
+
+private:
+ /** Inserts a new XF index for the specified cell type. */
+ void SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode );
+
+ /** Copies border of the last cell of the range to the first cell to keep it visible
+ when the range is merged.
+ @param nLine
+ SvxBoxItemLine::RIGHT = copy most-right border of top row;
+ SvxBoxItemLine::BOTTOM = copy most-bottom border of first column. */
+ void SetBorderLine( const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine );
+
+private:
+
+ std::vector< std::shared_ptr< XclImpXFRangeColumn > >
+ maColumns; /// Array of column XF index buffers.
+ std::vector< std::pair< XclRange, OUString > >
+ maHyperlinks; /// Maps URLs to hyperlink cells.
+ ScRangeList maMergeList; /// List of merged cell ranges.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiview.hxx b/sc/source/filter/inc/xiview.hxx
new file mode 100644
index 0000000000..fdf3201fac
--- /dev/null
+++ b/sc/source/filter/inc/xiview.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 .
+ */
+
+#pragma once
+
+#include "xlview.hxx"
+#include "xiroot.hxx"
+
+// Document view settings =====================================================
+
+/** Contains document view settings (WINDOW1 record). */
+class XclImpDocViewSettings : protected XclImpRoot
+{
+public:
+ explicit XclImpDocViewSettings( const XclImpRoot& rRoot );
+
+ /** Reads a WINDOW1 record. */
+ void ReadWindow1( XclImpStream& rStrm );
+
+ /** Returns the Calc index of the displayed sheet. */
+ SCTAB GetDisplScTab() const;
+
+ /** Sets the view settings at the document. */
+ void Finalize();
+
+private:
+ XclDocViewData maData; /// Document view settings data.
+};
+
+// Sheet view settings ========================================================
+
+/** Contains all view settings for a single sheet.
+
+ Usage:
+ 1) When import filter starts reading a worksheet substream, initialize an
+ instance of this class with the Initialize() function. This will set
+ all view options to Excel default values.
+ 2) Read all view related records using the Read*() functions.
+ 3) When import filter ends reading a worksheet substream, call Finalize()
+ to set all view settings to the current sheet of the Calc document.
+ */
+class XclImpTabViewSettings : protected XclImpRoot
+{
+public:
+ explicit XclImpTabViewSettings( const XclImpRoot& rRoot );
+
+ /** Initializes the object to be used for a new sheet. */
+ void Initialize();
+
+ /** Reads a WINDOW2 record. */
+ void ReadWindow2( XclImpStream& rStrm, bool bChart );
+ /** Reads an SCL record. */
+ void ReadScl( XclImpStream& rStrm );
+ /** Reads a PANE record. */
+ void ReadPane( XclImpStream& rStrm );
+ /** Reads a SELECTION record. */
+ void ReadSelection( XclImpStream& rStrm );
+ /** Reads a SHEETEXT record (Tab Color). */
+ void ReadTabBgColor( XclImpStream& rStrm, const XclImpPalette& rPal );
+ /** Sets the view settings at the current sheet or the extended sheet options object. */
+ void Finalize();
+
+private:
+ XclTabViewData maData; /// Sheet view settings data.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xladdress.hxx b/sc/source/filter/inc/xladdress.hxx
new file mode 100644
index 0000000000..e090d84458
--- /dev/null
+++ b/sc/source/filter/inc/xladdress.hxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <address.hxx>
+
+class XclImpStream;
+class XclExpStream;
+
+/** A 2D cell address struct with Excel column and row indexes. */
+struct XclAddress
+{
+ sal_uInt16 mnCol;
+ sal_uInt32 mnRow;
+
+ // coverity[uninit_member] - members deliberately not initialized
+ explicit XclAddress( ScAddress::Uninitialized ) {}
+ explicit XclAddress() : mnCol( 0 ), mnRow( 0 ) {}
+ explicit XclAddress( sal_uInt16 nCol, sal_uInt32 nRow ) : mnCol( nCol ), mnRow( nRow ) {}
+
+ void Set( sal_uInt16 nCol, sal_uInt32 nRow ) { mnCol = nCol; mnRow = nRow; }
+
+ void Read( XclImpStream& rStrm );
+ void Write( XclExpStream& rStrm ) const;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclAddress& rXclPos )
+{
+ rXclPos.Read( rStrm );
+ return rStrm;
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclAddress& rXclPos )
+{
+ rXclPos.Write( rStrm );
+ return rStrm;
+}
+
+/** A 2D cell range address struct with Excel column and row indexes. */
+struct XclRange
+{
+ XclAddress maFirst;
+ XclAddress maLast;
+
+ explicit XclRange( ScAddress::Uninitialized e ) : maFirst( e ), maLast( e ) {}
+ explicit XclRange() {}
+ explicit XclRange( const XclAddress& rPos ) : maFirst( rPos ), maLast( rPos ) {}
+ explicit XclRange( sal_uInt16 nCol1, sal_uInt32 nRow1, sal_uInt16 nCol2, sal_uInt32 nRow2 ) :
+ maFirst( nCol1, nRow1 ), maLast( nCol2, nRow2 ) {}
+
+ void Set( sal_uInt16 nCol1, sal_uInt32 nRow1, sal_uInt16 nCol2, sal_uInt32 nRow2 )
+ { maFirst.Set( nCol1, nRow1 ); maLast.Set( nCol2, nRow2 ); }
+
+ sal_uInt16 GetColCount() const {
+ return maFirst.mnCol <= maLast.mnCol && maFirst.mnRow <= maLast.mnRow
+ ? maLast.mnCol - maFirst.mnCol + 1 : 0;
+ }
+ sal_uInt32 GetRowCount() const {
+ return maFirst.mnCol <= maLast.mnCol && maFirst.mnRow <= maLast.mnRow
+ ? maLast.mnRow - maFirst.mnRow + 1 : 0;
+ }
+ bool Contains( const XclAddress& rPos ) const;
+
+ void Read( XclImpStream& rStrm, bool bCol16Bit = true );
+ void Write( XclExpStream& rStrm, bool bCol16Bit = true ) const;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclRange& rXclRange )
+{
+ rXclRange.Read( rStrm );
+ return rStrm;
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclRange& rXclRange )
+{
+ rXclRange.Write( rStrm );
+ return rStrm;
+}
+
+typedef ::std::vector< XclRange > XclRangeVector;
+
+/** A 2D cell range address list with Excel column and row indexes. */
+class XclRangeList
+{
+private:
+ XclRangeVector mRanges;
+
+public:
+ explicit XclRangeList() : mRanges() {}
+
+ size_t size() const { return mRanges.size(); }
+ bool empty() const { return mRanges.empty(); }
+ XclRangeVector::const_iterator begin() const { return mRanges.begin(); }
+ XclRangeVector::const_iterator end() const { return mRanges.end(); }
+ void clear() { mRanges.clear(); }
+ void push_back(const XclRange &rRange) { mRanges.push_back(rRange); }
+
+ XclRange GetEnclosingRange() const;
+
+ void Read( XclImpStream& rStrm, bool bCol16Bit = true, sal_uInt16 nCountInStream = 0 );
+ void Write( XclExpStream& rStrm, bool bCol16Bit = true, sal_uInt16 nCountInStream = 0 ) const;
+ void WriteSubList( XclExpStream& rStrm,
+ size_t nBegin, size_t nCount, bool bCol16Bit = true, sal_uInt16 nCountInStream = 0 ) const;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclRangeList& rXclRanges )
+{
+ rXclRanges.Read( rStrm );
+ return rStrm;
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclRangeList& rXclRanges )
+{
+ rXclRanges.Write( rStrm );
+ return rStrm;
+}
+
+class XclTracer;
+
+/** Base class for import/export address converters. */
+class XclAddressConverterBase
+{
+public:
+ explicit XclAddressConverterBase( XclTracer& rTracer, const ScAddress& rMaxPos );
+ virtual ~XclAddressConverterBase();
+
+ /** Returns whether the "some columns have been cut" warning box should be shown. */
+ bool IsColTruncated() const { return mbColTrunc; }
+ /** Returns whether the "some rows have been cut" warning box should be shown. */
+ bool IsRowTruncated() const { return mbRowTrunc; }
+ /** Returns whether the "some sheets have been cut" warning box should be shown. */
+ bool IsTabTruncated() const { return mbTabTrunc; }
+
+ /** Checks if the passed sheet index is valid.
+ @param nScTab The sheet index to check.
+ Sets the internal flag that produces a warning box
+ after loading/saving the file, if the sheet index is not valid.
+ */
+ void CheckScTab( SCTAB nScTab );
+
+protected:
+ XclTracer& mrTracer; /// Tracer for invalid addresses.
+ ScAddress maMaxPos; /// Default maximum position.
+ sal_uInt16 mnMaxCol; /// Maximum column index, as 16-bit value.
+ sal_uInt32 mnMaxRow; /// Maximum row index.
+ bool mbColTrunc; /// Flag for "columns truncated" warning box.
+ bool mbRowTrunc; /// Flag for "rows truncated" warning box.
+ bool mbTabTrunc; /// Flag for "tables truncated" warning box.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlchart.hxx b/sc/source/filter/inc/xlchart.hxx
new file mode 100644
index 0000000000..71ac94585e
--- /dev/null
+++ b/sc/source/filter/inc/xlchart.hxx
@@ -0,0 +1,1439 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include <docmodel/color/ComplexColor.hxx>
+#include "fapihelper.hxx"
+#include <map>
+#include <memory>
+
+namespace com::sun::star {
+ namespace container { class XNameContainer; }
+ namespace lang { class XMultiServiceFactory; }
+ namespace chart { class XChartDocument; }
+ namespace chart2 { class XChartDocument; }
+ namespace drawing { class XShape; }
+}
+
+class XclRoot;
+
+// Property names =============================================================
+
+// service names
+inline constexpr OUString SERVICE_DRAWING_BITMAPTABLE = u"com.sun.star.drawing.BitmapTable"_ustr;
+inline constexpr OUString SERVICE_DRAWING_DASHTABLE = u"com.sun.star.drawing.DashTable"_ustr;
+inline constexpr OUString SERVICE_DRAWING_GRADIENTTABLE = u"com.sun.star.drawing.GradientTable"_ustr;
+inline constexpr OUString SERVICE_DRAWING_HATCHTABLE = u"com.sun.star.drawing.HatchTable"_ustr;
+
+inline constexpr OUString SERVICE_CHART2_AXIS = u"com.sun.star.chart2.Axis"_ustr;
+inline constexpr OUString SERVICE_CHART2_DATAPROVIDER = u"com.sun.star.chart2.data.DataProvider"_ustr;
+inline constexpr OUString SERVICE_CHART2_DATASERIES = u"com.sun.star.chart2.DataSeries"_ustr;
+inline constexpr OUString SERVICE_CHART2_DIAGRAM = u"com.sun.star.chart2.Diagram"_ustr;
+inline constexpr OUString SERVICE_CHART2_ERRORBAR = u"com.sun.star.chart2.ErrorBar"_ustr;
+inline constexpr OUString SERVICE_CHART2_LEGEND = u"com.sun.star.chart2.Legend"_ustr;
+inline constexpr OUString SERVICE_CHART2_TITLE = u"com.sun.star.chart2.Title"_ustr;
+
+// property names
+inline constexpr OUString EXC_CHPROP_ADDITIONALSHAPES = u"AdditionalShapes"_ustr;
+inline constexpr OUString EXC_CHPROP_ANCHORPOSITION = u"AnchorPosition"_ustr;
+inline constexpr OUString EXC_CHPROP_ARRANGEORDER = u"ArrangeOrder"_ustr;
+inline constexpr OUString EXC_CHPROP_ATTAXISINDEX = u"AttachedAxisIndex"_ustr;
+inline constexpr OUString EXC_CHPROP_ATTRIBDATAPOINTS = u"AttributedDataPoints"_ustr;
+inline constexpr OUString EXC_CHPROP_BLACKDAY = u"BlackDay"_ustr;
+inline constexpr OUString EXC_CHPROP_COLOR = u"Color"_ustr;
+inline constexpr OUString EXC_CHPROP_CONNECTBARS = u"ConnectBars"_ustr;
+inline constexpr OUString EXC_CHPROP_CROSSOVERPOSITION = u"CrossoverPosition"_ustr;
+inline constexpr OUString EXC_CHPROP_CROSSOVERVALUE = u"CrossoverValue"_ustr;
+inline constexpr OUString EXC_CHPROP_CURVESTYLE = u"CurveStyle"_ustr;
+inline constexpr OUString EXC_CHPROP_CURVENAME = u"CurveName"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENEAMBIENTCOLOR = u"D3DSceneAmbientColor"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENELIGHTON1 = u"D3DSceneLightOn1"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENELIGHTCOLOR2 = u"D3DSceneLightColor2"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENELIGHTDIR2 = u"D3DSceneLightDirection2"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENELIGHTON2 = u"D3DSceneLightOn2"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENEPERSPECTIVE = u"D3DScenePerspective"_ustr;
+inline constexpr OUString EXC_CHPROP_D3DSCENESHADEMODE = u"D3DSceneShadeMode"_ustr;
+inline constexpr OUString EXC_CHPROP_DISPLAYLABELS = u"DisplayLabels"_ustr;
+inline constexpr OUString EXC_CHPROP_ERRORBARSTYLE = u"ErrorBarStyle"_ustr;
+inline constexpr OUString EXC_CHPROP_ERRORBARX = u"ErrorBarX"_ustr;
+inline constexpr OUString EXC_CHPROP_ERRORBARY = u"ErrorBarY"_ustr;
+inline constexpr OUString EXC_CHPROP_EXPANSION = u"Expansion"_ustr;
+inline constexpr OUString EXC_CHPROP_EXPTIMEINCREMENT = u"ExplicitTimeIncrement"_ustr;
+inline constexpr OUString EXC_CHPROP_EXTRAPOLATE_FORWARD = u"ExtrapolateForward"_ustr;
+inline constexpr OUString EXC_CHPROP_EXTRAPOLATE_BACKWARD = u"ExtrapolateBackward"_ustr;
+inline constexpr OUString EXC_CHPROP_FORCE_INTERCEPT = u"ForceIntercept"_ustr;
+inline constexpr OUString EXC_CHPROP_GAPWIDTHSEQ = u"GapwidthSequence"_ustr;
+inline constexpr OUString EXC_CHPROP_GEOMETRY3D = u"Geometry3D"_ustr;
+inline constexpr OUString EXC_CHPROP_INCLUDEHIDDENCELLS = u"IncludeHiddenCells"_ustr;
+inline constexpr OUString EXC_CHPROP_INTERCEPT_VALUE = u"InterceptValue"_ustr;
+inline constexpr OUString EXC_CHPROP_JAPANESE = u"Japanese"_ustr;
+inline constexpr OUString EXC_CHPROP_LABEL = u"Label"_ustr;
+inline constexpr OUString EXC_CHPROP_LABELPLACEMENT = u"LabelPlacement"_ustr;
+inline constexpr OUString EXC_CHPROP_LABELPOSITION = u"LabelPosition"_ustr;
+inline constexpr OUString EXC_CHPROP_LABELSEPARATOR = u"LabelSeparator"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWLEGENDENTRY = u"ShowLegendEntry"_ustr;
+inline constexpr OUString EXC_CHPROP_MAJORTICKS = u"MajorTickmarks"_ustr;
+inline constexpr OUString EXC_CHPROP_MARKPOSITION = u"MarkPosition"_ustr;
+inline constexpr OUString EXC_CHPROP_MINORTICKS = u"MinorTickmarks"_ustr;
+inline constexpr OUString EXC_CHPROP_MISSINGVALUETREATMENT = u"MissingValueTreatment"_ustr;
+inline constexpr OUString EXC_CHPROP_MOVING_AVERAGE_PERIOD = u"MovingAveragePeriod"_ustr;
+inline constexpr OUString EXC_CHPROP_NEGATIVEERROR = u"NegativeError"_ustr;
+inline constexpr OUString EXC_CHPROP_NUMBERFORMAT = u"NumberFormat"_ustr;
+inline constexpr OUString EXC_CHPROP_NUMBERFORMAT_LINKSRC = u"LinkNumberFormatToSource"_ustr;
+inline constexpr OUString EXC_CHPROP_OFFSET = u"Offset"_ustr;
+inline constexpr OUString EXC_CHPROP_OVERLAPSEQ = u"OverlapSequence"_ustr;
+inline constexpr OUString EXC_CHPROP_PERCENTAGENUMFMT = u"PercentageNumberFormat"_ustr;
+inline constexpr OUString EXC_CHPROP_PERCENTDIAGONAL = u"PercentDiagonal"_ustr;
+inline constexpr OUString EXC_CHPROP_PERSPECTIVE = u"Perspective"_ustr;
+inline constexpr OUString EXC_CHPROP_POSITIVEERROR = u"PositiveError"_ustr;
+inline constexpr OUString EXC_CHPROP_POLYNOMIAL_DEGREE = u"PolynomialDegree"_ustr;
+inline constexpr OUString EXC_CHPROP_RELATIVEPOSITION = u"RelativePosition"_ustr;
+inline constexpr OUString EXC_CHPROP_RELATIVESIZE = u"RelativeSize"_ustr;
+inline constexpr OUString EXC_CHPROP_RIGHTANGLEDAXES = u"RightAngledAxes"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE = u"Role"_ustr;
+inline constexpr OUString EXC_CHPROP_ROTATIONHORIZONTAL = u"RotationHorizontal"_ustr;
+inline constexpr OUString EXC_CHPROP_ROTATIONVERTICAL = u"RotationVertical"_ustr;
+inline constexpr OUString EXC_CHPROP_3DRELATIVEHEIGHT = u"3DRelativeHeight"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOW = u"Show"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWCORRELATION = u"ShowCorrelationCoefficient"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWEQUATION = u"ShowEquation"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWFIRST = u"ShowFirst"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWHIGHLOW = u"ShowHighLow"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWNEGATIVEERROR = u"ShowNegativeError"_ustr;
+inline constexpr OUString EXC_CHPROP_SHOWPOSITIVEERROR = u"ShowPositiveError"_ustr;
+inline constexpr OUString EXC_CHPROP_STACKCHARACTERS = u"StackCharacters"_ustr;
+inline constexpr OUString EXC_CHPROP_STACKINGDIR = u"StackingDirection"_ustr;
+inline constexpr OUString EXC_CHPROP_STARTINGANGLE = u"StartingAngle"_ustr;
+inline constexpr OUString EXC_CHPROP_SWAPXANDYAXIS = u"SwapXAndYAxis"_ustr;
+inline constexpr OUString EXC_CHPROP_SYMBOL = u"Symbol"_ustr;
+inline constexpr OUString EXC_CHPROP_TEXTBREAK = u"TextBreak"_ustr;
+inline constexpr OUString EXC_CHPROP_TEXTOVERLAP = u"TextOverlap"_ustr;
+inline constexpr OUString EXC_CHPROP_TEXTROTATION = u"TextRotation"_ustr;
+inline constexpr OUString EXC_CHPROP_USERINGS = u"UseRings"_ustr;
+inline constexpr OUString EXC_CHPROP_VARYCOLORSBY = u"VaryColorsByPoint"_ustr;
+inline constexpr OUString EXC_CHPROP_WEIGHT = u"Weight"_ustr;
+inline constexpr OUString EXC_CHPROP_WHITEDAY = u"WhiteDay"_ustr;
+
+// data series roles
+inline constexpr OUString EXC_CHPROP_ROLE_CATEG = u"categories"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_ERRORBARS_NEGX = u"error-bars-x-negative"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_ERRORBARS_NEGY = u"error-bars-y-negative"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_ERRORBARS_POSX = u"error-bars-x-positive"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_ERRORBARS_POSY = u"error-bars-y-positive"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_LABEL = u"label"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_XVALUES = u"values-x"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_YVALUES = u"values-y"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_OPENVALUES = u"values-first"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_CLOSEVALUES = u"values-last"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_LOWVALUES = u"values-min"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_HIGHVALUES = u"values-max"_ustr;
+inline constexpr OUString EXC_CHPROP_ROLE_SIZEVALUES = u"values-size"_ustr;
+
+// Constants and Enumerations =================================================
+
+const std::size_t EXC_CHART_PROGRESS_SIZE = 10;
+const sal_uInt16 EXC_CHART_AUTOROTATION = 0xFFFF; /// Automatic rotation, e.g. axis labels (internal use only).
+
+const sal_Int32 EXC_CHART_AXIS_NONE = -1; /// For internal use only.
+const sal_Int32 EXC_CHART_AXIS_X = 0; /// API X axis index.
+const sal_Int32 EXC_CHART_AXIS_Y = 1; /// API Y axis index.
+const sal_Int32 EXC_CHART_AXIS_Z = 2; /// API Z axis index.
+const sal_Int32 EXC_CHART_AXESSET_NONE = -1; /// For internal use only.
+const sal_Int32 EXC_CHART_AXESSET_PRIMARY = 0; /// API primary axes set index.
+const sal_Int32 EXC_CHART_AXESSET_SECONDARY = 1; /// API secondary axes set index.
+
+const sal_Int32 EXC_CHART_TOTALUNITS = 4000; /// Most chart objects are positioned in 1/4000 of chart area.
+const sal_Int32 EXC_CHART_PLOTAREAUNITS = 1000; /// For objects that are positioned in 1/1000 of plot area.
+
+// (0x0850) CHFRINFO ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRINFO = 0x0850;
+
+const sal_uInt8 EXC_CHFRINFO_EXCEL2000 = 9;
+const sal_uInt8 EXC_CHFRINFO_EXCELXP2003 = 10;
+const sal_uInt8 EXC_CHFRINFO_EXCEL2007 = 11;
+
+// (0x0852, 0x0853) CHFRBLOCKBEGIN, CHFRBLOCKEND ------------------------------
+
+const sal_uInt16 EXC_ID_CHFRBLOCKBEGIN = 0x0852;
+const sal_uInt16 EXC_ID_CHFRBLOCKEND = 0x0853;
+
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_AXESSET = 0;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_TEXT = 2;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_AXIS = 4;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_TYPEGROUP = 5;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_DATATABLE = 6;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_FRAME = 7;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_LEGEND = 9;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_LEGENDEX = 10;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_SERIES = 12;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_CHART = 13;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_DATAFORMAT = 14;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_DROPBAR = 15;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_UNKNOWN = 0xFFFF; /// For internal use only.
+
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_TITLE = 0;
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_DEFTEXT = 2;
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_AXISTITLE = 4;
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_DATALABEL = 5;
+
+const sal_uInt16 EXC_CHFRBLOCK_FRAME_STANDARD = 0;
+const sal_uInt16 EXC_CHFRBLOCK_FRAME_PLOTFRAME = 1;
+const sal_uInt16 EXC_CHFRBLOCK_FRAME_BACKGROUND = 2;
+
+// (0x086B) CHFRLABELPROPS ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRLABELPROPS = 0x086B;
+
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWSERIES = 0x0001;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWCATEG = 0x0002;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWVALUE = 0x0004;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWPERCENT = 0x0008;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWBUBBLE = 0x0010;
+
+// (0x1001) CHUNITS -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHUNITS = 0x1001;
+
+const sal_uInt16 EXC_CHUNITS_TWIPS = 0;
+const sal_uInt16 EXC_CHUNITS_PIXELS = 1;
+
+// (0x1002) CHCHART -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHCHART = 0x1002;
+
+// (0x1003) CHSERIES ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERIES = 0x1003;
+
+const sal_uInt16 EXC_CHSERIES_DATE = 0;
+const sal_uInt16 EXC_CHSERIES_NUMERIC = 1;
+const sal_uInt16 EXC_CHSERIES_SEQUENCE = 2;
+const sal_uInt16 EXC_CHSERIES_TEXT = 3;
+
+const sal_uInt16 EXC_CHSERIES_MAXSERIES = 255; /// Maximum valid series index.
+const sal_uInt16 EXC_CHSERIES_INVALID = 0xFFFF; /// Invalid series index (for internal use).
+
+// (0x1006) CHDATAFORMAT ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDATAFORMAT = 0x1006;
+
+const sal_uInt16 EXC_CHDATAFORMAT_MAXPOINTCOUNT = 32000; /// Maximum number of data points.
+const sal_uInt16 EXC_CHDATAFORMAT_DEFAULT = 0xFFFD; /// As format index: global default for an axes set.
+const sal_uInt16 EXC_CHDATAFORMAT_UNKNOWN = 0xFFFE; /// As point index: unknown format, don't use.
+const sal_uInt16 EXC_CHDATAFORMAT_ALLPOINTS = 0xFFFF; /// As point index: default for a series.
+
+const sal_uInt16 EXC_CHDATAFORMAT_OLDCOLORS = 0x0001;
+
+// (0x1007) CHLINEFORMAT ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLINEFORMAT = 0x1007;
+
+const sal_uInt16 EXC_CHLINEFORMAT_SOLID = 0;
+const sal_uInt16 EXC_CHLINEFORMAT_DASH = 1;
+const sal_uInt16 EXC_CHLINEFORMAT_DOT = 2;
+const sal_uInt16 EXC_CHLINEFORMAT_DASHDOT = 3;
+const sal_uInt16 EXC_CHLINEFORMAT_DASHDOTDOT = 4;
+const sal_uInt16 EXC_CHLINEFORMAT_NONE = 5;
+const sal_uInt16 EXC_CHLINEFORMAT_DARKTRANS = 6;
+const sal_uInt16 EXC_CHLINEFORMAT_MEDTRANS = 7;
+const sal_uInt16 EXC_CHLINEFORMAT_LIGHTTRANS = 8;
+
+const sal_Int16 EXC_CHLINEFORMAT_HAIR = -1;
+const sal_Int16 EXC_CHLINEFORMAT_SINGLE = 0;
+const sal_Int16 EXC_CHLINEFORMAT_DOUBLE = 1;
+const sal_Int16 EXC_CHLINEFORMAT_TRIPLE = 2;
+
+const sal_uInt16 EXC_CHLINEFORMAT_AUTO = 0x0001;
+const sal_uInt16 EXC_CHLINEFORMAT_SHOWAXIS = 0x0004;
+
+// (0x1009) CHMARKERFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHMARKERFORMAT = 0x1009;
+
+const sal_uInt16 EXC_CHMARKERFORMAT_NOSYMBOL = 0;
+const sal_uInt16 EXC_CHMARKERFORMAT_SQUARE = 1;
+const sal_uInt16 EXC_CHMARKERFORMAT_DIAMOND = 2;
+const sal_uInt16 EXC_CHMARKERFORMAT_TRIANGLE = 3;
+const sal_uInt16 EXC_CHMARKERFORMAT_CROSS = 4;
+const sal_uInt16 EXC_CHMARKERFORMAT_STAR = 5;
+const sal_uInt16 EXC_CHMARKERFORMAT_DOWJ = 6;
+const sal_uInt16 EXC_CHMARKERFORMAT_STDDEV = 7;
+const sal_uInt16 EXC_CHMARKERFORMAT_CIRCLE = 8;
+const sal_uInt16 EXC_CHMARKERFORMAT_PLUS = 9;
+
+const sal_uInt32 EXC_CHMARKERFORMAT_HAIRSIZE = 60; /// Automatic symbol size for hair lines.
+const sal_uInt32 EXC_CHMARKERFORMAT_SINGLESIZE = 100; /// Automatic symbol size for single lines.
+const sal_uInt32 EXC_CHMARKERFORMAT_DOUBLESIZE = 140; /// Automatic symbol size for double lines.
+const sal_uInt32 EXC_CHMARKERFORMAT_TRIPLESIZE = 180; /// Automatic symbol size for triple lines.
+
+const sal_uInt16 EXC_CHMARKERFORMAT_AUTO = 0x0001;
+const sal_uInt16 EXC_CHMARKERFORMAT_NOFILL = 0x0010;
+const sal_uInt16 EXC_CHMARKERFORMAT_NOLINE = 0x0020;
+
+// (0x100A) CHAREAFORMAT ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAREAFORMAT = 0x100A;
+
+const sal_uInt16 EXC_CHAREAFORMAT_AUTO = 0x0001;
+const sal_uInt16 EXC_CHAREAFORMAT_INVERTNEG = 0x0002;
+
+// (0x100B) CHPIEFORMAT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPIEFORMAT = 0x100B;
+
+// (0x100C) CHATTACHEDLABEL ---------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHATTACHEDLABEL = 0x100C;
+
+const sal_uInt16 EXC_CHATTLABEL_SHOWVALUE = 0x0001;
+const sal_uInt16 EXC_CHATTLABEL_SHOWPERCENT = 0x0002;
+const sal_uInt16 EXC_CHATTLABEL_SHOWCATEGPERC = 0x0004;
+const sal_uInt16 EXC_CHATTLABEL_SMOOTHED = 0x0008; /// Smoothed line.
+const sal_uInt16 EXC_CHATTLABEL_SHOWCATEG = 0x0010;
+const sal_uInt16 EXC_CHATTLABEL_SHOWBUBBLE = 0x0020;
+
+// (0x100D) CHSTRING ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSTRING = 0x100D;
+
+// (0x1014) CHTYPEGROUP -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHTYPEGROUP = 0x1014;
+
+const sal_uInt16 EXC_CHTYPEGROUP_VARIEDCOLORS = 0x0001; /// Varied colors for points.
+
+// (0x1015) CHLEGEND ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLEGEND = 0x1015;
+
+const sal_uInt8 EXC_CHLEGEND_BOTTOM = 0;
+const sal_uInt8 EXC_CHLEGEND_CORNER = 1;
+const sal_uInt8 EXC_CHLEGEND_TOP = 2;
+const sal_uInt8 EXC_CHLEGEND_RIGHT = 3;
+const sal_uInt8 EXC_CHLEGEND_LEFT = 4;
+const sal_uInt8 EXC_CHLEGEND_NOTDOCKED = 7;
+
+const sal_uInt8 EXC_CHLEGEND_CLOSE = 0;
+const sal_uInt8 EXC_CHLEGEND_MEDIUM = 1;
+const sal_uInt8 EXC_CHLEGEND_OPEN = 2;
+
+const sal_uInt16 EXC_CHLEGEND_DOCKED = 0x0001;
+const sal_uInt16 EXC_CHLEGEND_AUTOSERIES = 0x0002;
+const sal_uInt16 EXC_CHLEGEND_AUTOPOSX = 0x0004;
+const sal_uInt16 EXC_CHLEGEND_AUTOPOSY = 0x0008;
+const sal_uInt16 EXC_CHLEGEND_STACKED = 0x0010;
+const sal_uInt16 EXC_CHLEGEND_DATATABLE = 0x0020;
+
+// (0x1017) CHBAR, CHCOLUMN ---------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHBAR = 0x1017;
+
+const sal_uInt16 EXC_CHBAR_HORIZONTAL = 0x0001;
+const sal_uInt16 EXC_CHBAR_STACKED = 0x0002;
+const sal_uInt16 EXC_CHBAR_PERCENT = 0x0004;
+const sal_uInt16 EXC_CHBAR_SHADOW = 0x0008;
+
+// (0x1018, 0x101A) CHLINE, CHAREA --------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLINE = 0x1018;
+const sal_uInt16 EXC_ID_CHAREA = 0x101A;
+
+const sal_uInt16 EXC_CHLINE_STACKED = 0x0001;
+const sal_uInt16 EXC_CHLINE_PERCENT = 0x0002;
+const sal_uInt16 EXC_CHLINE_SHADOW = 0x0004;
+
+// (0x1019) CHPIE -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPIE = 0x1019;
+
+const sal_uInt16 EXC_CHPIE_SHADOW = 0x0001;
+const sal_uInt16 EXC_CHPIE_LINES = 0x0002;
+
+// (0x101B) CHSCATTER ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSCATTER = 0x101B;
+
+const sal_uInt16 EXC_CHSCATTER_AREA = 1; /// Bubble area refers to value.
+const sal_uInt16 EXC_CHSCATTER_WIDTH = 2; /// Bubble width refers to value.
+
+const sal_uInt16 EXC_CHSCATTER_BUBBLES = 0x0001;
+const sal_uInt16 EXC_CHSCATTER_SHOWNEG = 0x0002;
+const sal_uInt16 EXC_CHSCATTER_SHADOW = 0x0004;
+
+// (0x001C) CHCHARTLINE -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHCHARTLINE = 0x101C;
+
+const sal_uInt16 EXC_CHCHARTLINE_DROP = 0; /// Drop lines.
+const sal_uInt16 EXC_CHCHARTLINE_HILO = 1; /// Hi-lo lines.
+const sal_uInt16 EXC_CHCHARTLINE_CONNECT = 2; /// Connector lines in stacked bar charts.
+
+// (0x101D) CHAXIS ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAXIS = 0x101D;
+
+const sal_uInt16 EXC_CHAXIS_X = 0;
+const sal_uInt16 EXC_CHAXIS_Y = 1;
+const sal_uInt16 EXC_CHAXIS_Z = 2;
+const sal_uInt16 EXC_CHAXIS_NONE = 0xFFFF; /// For internal use only.
+
+// (0x101E) CHTICK ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHTICK = 0x101E;
+
+const sal_uInt8 EXC_CHTICK_INSIDE = 0x01;
+const sal_uInt8 EXC_CHTICK_OUTSIDE = 0x02;
+
+const sal_uInt8 EXC_CHTICK_NOLABEL = 0;
+const sal_uInt8 EXC_CHTICK_LOW = 1; /// Below diagram/right of diagram.
+const sal_uInt8 EXC_CHTICK_HIGH = 2; /// Above diagram/left of diagram.
+const sal_uInt8 EXC_CHTICK_NEXT = 3; /// Next to axis.
+
+const sal_uInt8 EXC_CHTICK_TRANSPARENT = 1;
+const sal_uInt8 EXC_CHTICK_OPAQUE = 2;
+
+const sal_uInt16 EXC_CHTICK_AUTOCOLOR = 0x0001;
+const sal_uInt16 EXC_CHTICK_AUTOFILL = 0x0002;
+const sal_uInt16 EXC_CHTICK_AUTOROT = 0x0020;
+
+// (0x101F) CHVALUERANGE ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHVALUERANGE = 0x101F;
+
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMIN = 0x0001;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMAX = 0x0002;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMAJOR = 0x0004;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMINOR = 0x0008;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOCROSS = 0x0010;
+const sal_uInt16 EXC_CHVALUERANGE_LOGSCALE = 0x0020;
+const sal_uInt16 EXC_CHVALUERANGE_REVERSE = 0x0040; /// Axis direction reversed.
+const sal_uInt16 EXC_CHVALUERANGE_MAXCROSS = 0x0080; /// Other axis crosses at own maximum.
+const sal_uInt16 EXC_CHVALUERANGE_BIT8 = 0x0100; /// This bit is always set in BIFF5+.
+
+// (0x1020) CHLABELRANGE -----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLABELRANGE = 0x1020;
+
+const sal_uInt16 EXC_CHLABELRANGE_BETWEEN = 0x0001; /// Axis between categories.
+const sal_uInt16 EXC_CHLABELRANGE_MAXCROSS = 0x0002; /// Other axis crosses at own maximum.
+const sal_uInt16 EXC_CHLABELRANGE_REVERSE = 0x0004; /// Axis direction reversed.
+
+// (0x1021) CHAXISLINE --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAXISLINE = 0x1021;
+
+const sal_uInt16 EXC_CHAXISLINE_AXISLINE = 0; /// Axis line itself.
+const sal_uInt16 EXC_CHAXISLINE_MAJORGRID = 1; /// Major grid line.
+const sal_uInt16 EXC_CHAXISLINE_MINORGRID = 2; /// Minor grid line.
+const sal_uInt16 EXC_CHAXISLINE_WALLS = 3; /// Walls (X, Z axis), floor (Y axis).
+
+// (0x1024) CHDEFAULTTEXT -----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDEFAULTTEXT = 0x1024;
+
+const sal_uInt16 EXC_CHDEFTEXT_TEXTLABEL = 0; /// Default for text data labels (not used?).
+const sal_uInt16 EXC_CHDEFTEXT_NUMLABEL = 1; /// Default for numeric data labels (not used?).
+const sal_uInt16 EXC_CHDEFTEXT_GLOBAL = 2; /// Default text for all chart objects.
+const sal_uInt16 EXC_CHDEFTEXT_AXESSET = 3; /// Default text for axes and data points (BIFF8 only).
+const sal_uInt16 EXC_CHDEFTEXT_NONE = 0xFFFF; /// No default text available.
+
+// (0x1025) CHTEXT ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHTEXT = 0x1025;
+
+const sal_uInt8 EXC_CHTEXT_ALIGN_TOPLEFT = 1; /// Horizontal: left, vertical: top.
+const sal_uInt8 EXC_CHTEXT_ALIGN_CENTER = 2;
+const sal_uInt8 EXC_CHTEXT_ALIGN_BOTTOMRIGHT = 3; /// Horizontal: right, vertical: bottom.
+const sal_uInt8 EXC_CHTEXT_ALIGN_JUSTIFY = 4;
+const sal_uInt8 EXC_CHTEXT_ALIGN_DISTRIBUTE = 5;
+
+const sal_uInt16 EXC_CHTEXT_TRANSPARENT = 1;
+const sal_uInt16 EXC_CHTEXT_OPAQUE = 2;
+
+const sal_uInt16 EXC_CHTEXT_AUTOCOLOR = 0x0001; /// Automatic text color.
+const sal_uInt16 EXC_CHTEXT_SHOWSYMBOL = 0x0002; /// Legend symbol for data point caption.
+const sal_uInt16 EXC_CHTEXT_SHOWVALUE = 0x0004; /// Data point caption is the value.
+const sal_uInt16 EXC_CHTEXT_VERTICAL = 0x0008;
+const sal_uInt16 EXC_CHTEXT_AUTOTEXT = 0x0010; /// Label text generated from chart data.
+const sal_uInt16 EXC_CHTEXT_AUTOGEN = 0x0020; /// Text object is inserted automatically.
+const sal_uInt16 EXC_CHTEXT_DELETED = 0x0040; /// Text object is removed.
+const sal_uInt16 EXC_CHTEXT_AUTOFILL = 0x0080; /// Automatic text background mode (transparent/opaque).
+const sal_uInt16 EXC_CHTEXT_SHOWCATEGPERC = 0x0800; /// Data point caption is category and percent.
+const sal_uInt16 EXC_CHTEXT_SHOWPERCENT = 0x1000; /// Data point caption as percent.
+const sal_uInt16 EXC_CHTEXT_SHOWBUBBLE = 0x2000; /// Show bubble size.
+const sal_uInt16 EXC_CHTEXT_SHOWCATEG = 0x4000; /// Data point caption is category name.
+
+const sal_uInt16 EXC_CHTEXT_POS_DEFAULT = 0;
+const sal_uInt16 EXC_CHTEXT_POS_OUTSIDE = 1;
+const sal_uInt16 EXC_CHTEXT_POS_INSIDE = 2;
+const sal_uInt16 EXC_CHTEXT_POS_CENTER = 3;
+const sal_uInt16 EXC_CHTEXT_POS_AXIS = 4;
+const sal_uInt16 EXC_CHTEXT_POS_ABOVE = 5;
+const sal_uInt16 EXC_CHTEXT_POS_BELOW = 6;
+const sal_uInt16 EXC_CHTEXT_POS_LEFT = 7;
+const sal_uInt16 EXC_CHTEXT_POS_RIGHT = 8;
+const sal_uInt16 EXC_CHTEXT_POS_AUTO = 9;
+const sal_uInt16 EXC_CHTEXT_POS_MOVED = 10;
+
+// (0x1026) CHFONT ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFONT = 0x1026;
+
+// (0x1027) CHOBJECTLINK ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHOBJECTLINK = 0x1027;
+
+// link targets
+const sal_uInt16 EXC_CHOBJLINK_NONE = 0; /// No link target.
+const sal_uInt16 EXC_CHOBJLINK_TITLE = 1; /// Chart title.
+const sal_uInt16 EXC_CHOBJLINK_YAXIS = 2; /// Value axis (Y axis).
+const sal_uInt16 EXC_CHOBJLINK_XAXIS = 3; /// Category axis (X axis).
+const sal_uInt16 EXC_CHOBJLINK_DATA = 4; /// Data series/point.
+const sal_uInt16 EXC_CHOBJLINK_ZAXIS = 7; /// Series axis (Z axis).
+const sal_uInt16 EXC_CHOBJLINK_AXISUNIT = 12; /// Unit name for axis labels.
+
+// (0x1032) CHFRAME -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRAME = 0x1032;
+
+const sal_uInt16 EXC_CHFRAME_STANDARD = 0;
+const sal_uInt16 EXC_CHFRAME_SHADOW = 4;
+
+const sal_uInt16 EXC_CHFRAME_AUTOSIZE = 0x0001;
+const sal_uInt16 EXC_CHFRAME_AUTOPOS = 0x0002;
+
+// (0x1033, 0x1034) CHBEGIN, CHEND --------------------------------------------
+
+const sal_uInt16 EXC_ID_CHBEGIN = 0x1033;
+const sal_uInt16 EXC_ID_CHEND = 0x1034;
+
+// (0x1035) CHPLOTFRAME -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPLOTFRAME = 0x1035;
+
+// (0x103A) CHCHART3D ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHCHART3D = 0x103A;
+
+const sal_uInt16 EXC_CHCHART3D_REAL3D = 0x0001; /// true = real 3d perspective.
+const sal_uInt16 EXC_CHCHART3D_CLUSTER = 0x0002; /// false = Z axis, true = clustered/stacked.
+const sal_uInt16 EXC_CHCHART3D_AUTOHEIGHT = 0x0004; /// true = automatic height to width ratio.
+const sal_uInt16 EXC_CHCHART3D_HASWALLS = 0x0010; /// true = 3d chart has walls/floor.
+const sal_uInt16 EXC_CHCHART3D_2DWALLS = 0x0020; /// true = 2d wall/gridlines, no floor.
+
+// (0x103C) CHPICFORMAT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPICFORMAT = 0x103C;
+
+const sal_uInt16 EXC_CHPICFORMAT_NONE = 0; /// For internal use only.
+const sal_uInt16 EXC_CHPICFORMAT_STRETCH = 1; /// Bitmap stretched to area.
+const sal_uInt16 EXC_CHPICFORMAT_STACK = 2; /// Bitmap stacked.
+const sal_uInt16 EXC_CHPICFORMAT_SCALE = 3; /// Bitmap scaled to axis scale.
+
+const sal_uInt16 EXC_CHPICFORMAT_TOPBOTTOM = 0x0200;
+const sal_uInt16 EXC_CHPICFORMAT_FRONTBACK = 0x0400;
+const sal_uInt16 EXC_CHPICFORMAT_LEFTRIGHT = 0x0800;
+
+// (0x103D) CHDROPBAR ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDROPBAR = 0x103D;
+
+const sal_uInt16 EXC_CHDROPBAR_UP = 0;
+const sal_uInt16 EXC_CHDROPBAR_DOWN = 1;
+const sal_uInt16 EXC_CHDROPBAR_NONE = 0xFFFF;
+
+// (0x103E, 0x1040) CHRADARLINE, CHRADARAREA ----------------------------------
+
+const sal_uInt16 EXC_ID_CHRADARLINE = 0x103E;
+const sal_uInt16 EXC_ID_CHRADARAREA = 0x1040;
+
+const sal_uInt16 EXC_CHRADAR_AXISLABELS = 0x0001;
+const sal_uInt16 EXC_CHRADAR_SHADOW = 0x0002;
+
+// (0x103F) CHSURFACE ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSURFACE = 0x103F;
+
+const sal_uInt16 EXC_CHSURFACE_FILLED = 0x0001;
+const sal_uInt16 EXC_CHSURFACE_SHADING = 0x0002;
+
+// (0x1041) CHAXESSET ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAXESSET = 0x1041;
+
+const sal_uInt16 EXC_CHAXESSET_PRIMARY = 0;
+const sal_uInt16 EXC_CHAXESSET_SECONDARY = 1;
+const sal_uInt16 EXC_CHAXESSET_NONE = 0xFFFF; /// For internal use.
+
+// (0x1043) LEGENDEXCEPTION
+
+const sal_uInt16 EXC_ID_CHLEGENDEXCEPTION = 0x1043;
+
+const sal_uInt16 EXC_CHLEGENDEXCEPTION_DELETED = 0x0001;
+const sal_uInt16 EXC_CHLEGENDEXCEPTION_LABEL = 0x0002;
+
+// (0x1044) CHPROPERTIES ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPROPERTIES = 0x1044;
+
+const sal_uInt16 EXC_CHPROPS_MANSERIES = 0x0001; /// Manual series allocation.
+const sal_uInt16 EXC_CHPROPS_SHOWVISIBLEONLY = 0x0002; /// Show visible cells only.
+const sal_uInt16 EXC_CHPROPS_NORESIZE = 0x0004; /// Do not resize chart with window.
+const sal_uInt16 EXC_CHPROPS_MANPLOTAREA = 0x0008; /// Manual plot area mode.
+const sal_uInt16 EXC_CHPROPS_USEMANPLOTAREA = 0x0010; /// Manual plot area layout in CHFRAMEPOS record.
+
+const sal_uInt8 EXC_CHPROPS_EMPTY_SKIP = 0; /// Skip empty values.
+const sal_uInt8 EXC_CHPROPS_EMPTY_ZERO = 1; /// Plot empty values as zero.
+const sal_uInt8 EXC_CHPROPS_EMPTY_INTERPOLATE = 2; /// Interpolate empty values.
+
+// (0x1045) CHSERGROUP --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERGROUP = 0x1045;
+
+const sal_uInt16 EXC_CHSERGROUP_NONE = 0xFFFF; /// For internal use: no chart type group.
+
+// (0x1048, 0x0858) CHPIVOTREF ------------------------------------------------
+
+const sal_uInt16 EXC_ID5_CHPIVOTREF = 0x1048;
+const sal_uInt16 EXC_ID8_CHPIVOTREF = 0x0858;
+
+// (0x104A) CHSERPARENT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERPARENT = 0x104A;
+
+// (0x104B) CHSERTRENDLINE ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERTRENDLINE = 0x104B;
+
+const sal_uInt8 EXC_CHSERTREND_POLYNOMIAL = 0; /// If order is 1, trend line is linear.
+const sal_uInt8 EXC_CHSERTREND_EXPONENTIAL = 1;
+const sal_uInt8 EXC_CHSERTREND_LOGARITHMIC = 2;
+const sal_uInt8 EXC_CHSERTREND_POWER = 3;
+const sal_uInt8 EXC_CHSERTREND_MOVING_AVG = 4;
+
+// (0x104E) CHFORMAT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFORMAT = 0x104E;
+
+// (0x104F) CHFRAMEPOS --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRAMEPOS = 0x104F;
+
+const sal_uInt16 EXC_CHFRAMEPOS_POINTS = 0;
+const sal_uInt16 EXC_CHFRAMEPOS_ABSSIZE_POINTS = 1;
+const sal_uInt16 EXC_CHFRAMEPOS_PARENT = 2;
+const sal_uInt16 EXC_CHFRAMEPOS_DEFOFFSET_PLOT = 3;
+const sal_uInt16 EXC_CHFRAMEPOS_CHARTSIZE = 5;
+
+// (0x1050) CHFORMATRUNS ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFORMATRUNS = 0x1050;
+
+// (0x1051) CHSOURCELINK ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSOURCELINK = 0x1051;
+
+const sal_uInt8 EXC_CHSRCLINK_TITLE = 0;
+const sal_uInt8 EXC_CHSRCLINK_VALUES = 1;
+const sal_uInt8 EXC_CHSRCLINK_CATEGORY = 2;
+const sal_uInt8 EXC_CHSRCLINK_BUBBLES = 3;
+
+const sal_uInt8 EXC_CHSRCLINK_DEFAULT = 0;
+const sal_uInt8 EXC_CHSRCLINK_DIRECTLY = 1;
+const sal_uInt8 EXC_CHSRCLINK_WORKSHEET = 2;
+
+const sal_uInt16 EXC_CHSRCLINK_NUMFMT = 0x0001;
+
+// (0x105B) CHSERERRORBAR -----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERERRORBAR = 0x105B;
+
+const sal_uInt8 EXC_CHSERERR_NONE = 0; /// For internal use only.
+const sal_uInt8 EXC_CHSERERR_XPLUS = 1;
+const sal_uInt8 EXC_CHSERERR_XMINUS = 2;
+const sal_uInt8 EXC_CHSERERR_YPLUS = 3;
+const sal_uInt8 EXC_CHSERERR_YMINUS = 4;
+
+const sal_uInt8 EXC_CHSERERR_PERCENT = 1;
+const sal_uInt8 EXC_CHSERERR_FIXED = 2;
+const sal_uInt8 EXC_CHSERERR_STDDEV = 3;
+const sal_uInt8 EXC_CHSERERR_CUSTOM = 4;
+const sal_uInt8 EXC_CHSERERR_STDERR = 5;
+
+const sal_uInt8 EXC_CHSERERR_END_BLANK = 0; /// Line end: blank.
+const sal_uInt8 EXC_CHSERERR_END_TSHAPE = 1; /// Line end: t-shape.
+
+// (0x105D) CHSERIESFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERIESFORMAT = 0x105D;
+
+const sal_uInt16 EXC_CHSERIESFORMAT_SMOOTHED = 0x0001;
+const sal_uInt16 EXC_CHSERIESFORMAT_BUBBLE3D = 0x0002;
+const sal_uInt16 EXC_CHSERIESFORMAT_SHADOW = 0x0004;
+
+// (0x105F) CH3DDATAFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CH3DDATAFORMAT = 0x105F;
+
+const sal_uInt8 EXC_CH3DDATAFORMAT_RECT = 0; /// Rectangular base.
+const sal_uInt8 EXC_CH3DDATAFORMAT_CIRC = 1; /// Circular base.
+
+const sal_uInt8 EXC_CH3DDATAFORMAT_STRAIGHT = 0; /// Straight to top.
+const sal_uInt8 EXC_CH3DDATAFORMAT_SHARP = 1; /// Sharp top.
+const sal_uInt8 EXC_CH3DDATAFORMAT_TRUNC = 2; /// Sharp top, truncated.
+
+// (0x1061) CHPIEEXT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPIEEXT = 0x1061;
+
+// (0x1062) CHDATERANGE -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDATERANGE = 0x1062;
+
+const sal_uInt16 EXC_CHDATERANGE_AUTOMIN = 0x0001;
+const sal_uInt16 EXC_CHDATERANGE_AUTOMAX = 0x0002;
+const sal_uInt16 EXC_CHDATERANGE_AUTOMAJOR = 0x0004;
+const sal_uInt16 EXC_CHDATERANGE_AUTOMINOR = 0x0008;
+const sal_uInt16 EXC_CHDATERANGE_DATEAXIS = 0x0010;
+const sal_uInt16 EXC_CHDATERANGE_AUTOBASE = 0x0020;
+const sal_uInt16 EXC_CHDATERANGE_AUTOCROSS = 0x0040; /// Other axis crosses at own maximum.
+const sal_uInt16 EXC_CHDATERANGE_AUTODATE = 0x0080; /// Recognize date/text automatically.
+
+const sal_uInt16 EXC_CHDATERANGE_DAYS = 0;
+const sal_uInt16 EXC_CHDATERANGE_MONTHS = 1;
+const sal_uInt16 EXC_CHDATERANGE_YEARS = 2;
+
+// (0x1066) CHESCHERFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHESCHERFORMAT = 0x1066;
+
+// Other record IDs -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHWRAPPEDRECORD = 0x0851;
+const sal_uInt16 EXC_ID_CHUNITPROPERTIES = 0x0857;
+const sal_uInt16 EXC_ID_CHUSEDAXESSETS = 0x1046;
+const sal_uInt16 EXC_ID_CHLABELRANGE2 = 0x1062;
+const sal_uInt16 EXC_ID_CHPLOTGROWTH = 0x1064;
+const sal_uInt16 EXC_ID_CHSERINDEX = 0x1065;
+const sal_uInt16 EXC_ID_CHUNKNOWN = 0xFFFF;
+
+// Structs and classes
+
+// Common =====================================================================
+
+struct XclChRectangle
+{
+ sal_Int32 mnX; /// X position of the object in 1/4000 of chart width.
+ sal_Int32 mnY; /// Y position of the object in 1/4000 of chart height.
+ sal_Int32 mnWidth; /// Width of the object in 1/4000 of chart width.
+ sal_Int32 mnHeight; /// Height of the object in 1/4000 of chart height.
+
+ explicit XclChRectangle();
+};
+
+/** Specifies the position of a data series or data point. */
+struct XclChDataPointPos
+{
+ sal_uInt16 mnSeriesIdx; /// Series index of series or a data point.
+ sal_uInt16 mnPointIdx; /// Index of a data point inside a series.
+
+ explicit XclChDataPointPos(
+ sal_uInt16 nSeriesIdx = EXC_CHSERIES_INVALID,
+ sal_uInt16 nPointIdx = EXC_CHDATAFORMAT_ALLPOINTS );
+};
+
+bool operator<( const XclChDataPointPos& rL, const XclChDataPointPos& rR );
+
+/** Contains the type and context of a block of future records which are
+ guarded by CHFRBLOCKBEGIN and CHFRBLOCKEND records. */
+struct XclChFrBlock
+{
+ sal_uInt16 mnType; /// Type of the future record block.
+ sal_uInt16 mnContext; /// Context dependent on type.
+ sal_uInt16 mnValue1; /// Optional primary value for current context.
+ sal_uInt16 mnValue2; /// Optional secondary value for current context.
+
+ explicit XclChFrBlock( sal_uInt16 nType );
+};
+
+// Frame formatting ===========================================================
+
+struct XclChFramePos
+{
+ XclChRectangle maRect; /// Object dependent position data.
+ sal_uInt16 mnTLMode; /// Top-left position mode.
+ sal_uInt16 mnBRMode; /// Bottom-right position mode.
+
+ explicit XclChFramePos();
+};
+
+struct XclChLineFormat
+{
+ Color maColor; /// Line color.
+ sal_uInt16 mnPattern; /// Line pattern (solid, dashed, ...).
+ sal_Int16 mnWeight; /// Line weight (hairline, single, ...).
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChLineFormat();
+};
+
+struct XclChAreaFormat
+{
+ Color maPattColor; /// Pattern color.
+ Color maBackColor; /// Pattern background color.
+ sal_uInt16 mnPattern; /// Fill pattern.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChAreaFormat();
+};
+
+class SfxItemSet;
+class EscherPropertyContainer;
+
+struct XclChEscherFormat
+{
+ typedef std::shared_ptr< SfxItemSet > SfxItemSetRef;
+ typedef std::shared_ptr< EscherPropertyContainer > EscherPropSetRef;
+
+ SfxItemSetRef mxItemSet; /// Item set for Escher properties import.
+ EscherPropSetRef mxEscherSet; /// Container for Escher properties export.
+
+ explicit XclChEscherFormat();
+ ~XclChEscherFormat();
+};
+
+struct XclChPicFormat
+{
+ sal_uInt16 mnBmpMode; /// Bitmap mode, e.g. stretched, stacked.
+ sal_uInt16 mnFlags; /// Additional flags.
+ double mfScale; /// Picture scaling (units).
+
+ explicit XclChPicFormat();
+};
+
+struct XclChFrame
+{
+ sal_uInt16 mnFormat; /// Format type of the frame.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChFrame();
+};
+
+// Source links ===============================================================
+
+struct XclChSourceLink
+{
+ sal_uInt8 mnDestType; /// Type of the destination (title, values, ...).
+ sal_uInt8 mnLinkType; /// Link type (directly, linked to worksheet, ...).
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnNumFmtIdx; /// Number format index.
+
+ explicit XclChSourceLink();
+};
+
+// Text =======================================================================
+
+struct XclChObjectLink
+{
+ XclChDataPointPos maPointPos; /// Position of the data point.
+ sal_uInt16 mnTarget; /// Target of the link.
+
+ explicit XclChObjectLink();
+};
+
+struct XclChFrLabelProps
+{
+ OUString maSeparator; /// Separator between label values.
+ sal_uInt16 mnFlags; /// Flags indicating which values to be displayed.
+
+ explicit XclChFrLabelProps();
+};
+
+struct XclChText
+{
+ XclChRectangle maRect; /// Position of the text object.
+ //Color maTextColor; /// Text color.
+ model::ComplexColor maTextComplexColor;
+ sal_uInt8 mnHAlign; /// Horizontal alignment.
+ sal_uInt8 mnVAlign; /// Vertical alignment.
+ sal_uInt16 mnBackMode; /// Background mode: transparent, opaque.
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnFlags2; /// Text object placement and text direction (BIFF8+).
+ sal_uInt16 mnRotation; /// Text object rotation (BIFF8+).
+
+ explicit XclChText();
+};
+
+// Data series ================================================================
+
+struct XclChMarkerFormat
+{
+ Color maLineColor; /// Border line color.
+ Color maFillColor; /// Fill color.
+ sal_uInt32 mnMarkerSize; /// Size of a marker
+ sal_uInt16 mnMarkerType; /// Marker type (none, diamond, ...).
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChMarkerFormat();
+};
+
+struct XclCh3dDataFormat
+{
+ sal_uInt8 mnBase; /// Base form.
+ sal_uInt8 mnTop; /// Top edge mode.
+
+ explicit XclCh3dDataFormat();
+};
+
+struct XclChDataFormat
+{
+ XclChDataPointPos maPointPos; /// Position of the data point or series.
+ sal_uInt16 mnFormatIdx; /// Formatting index for automatic colors.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChDataFormat();
+};
+
+struct XclChSerTrendLine
+{
+ double mfIntercept; /// Forced intercept.
+ double mfForecastFor; /// Counter to forecast forward.
+ double mfForecastBack; /// Counter to forecast backward.
+ sal_uInt8 mnLineType; /// Type of the trend line.
+ sal_uInt8 mnOrder; /// Polynomial order or moving average counter.
+ sal_uInt8 mnShowEquation; /// 1 = Show equation.
+ sal_uInt8 mnShowRSquared; /// 1 = Show R-squared.
+
+ explicit XclChSerTrendLine();
+};
+
+struct XclChSerErrorBar
+{
+ double mfValue; /// Fixed value for several source types.
+ sal_uInt16 mnValueCount; /// Number of custom error values.
+ sal_uInt8 mnBarType; /// Type of the error bar (X/Y).
+ sal_uInt8 mnSourceType; /// Type of source values.
+ sal_uInt8 mnLineEnd; /// Type of the line ends.
+
+ explicit XclChSerErrorBar();
+};
+
+struct XclChSeries
+{
+ sal_uInt16 mnCategType; /// Data type for category entries.
+ sal_uInt16 mnValueType; /// Data type for value entries.
+ sal_uInt16 mnBubbleType; /// Data type for bubble entries.
+ sal_uInt16 mnCategCount; /// Number of category entries.
+ sal_uInt16 mnValueCount; /// Number of value entries.
+ sal_uInt16 mnBubbleCount; /// Number of bubble entries.
+
+ explicit XclChSeries();
+};
+
+// Chart type groups ==========================================================
+
+struct XclChType
+{
+ sal_Int16 mnOverlap; /// Bar overlap width (CHBAR).
+ sal_Int16 mnGap; /// Gap between bars (CHBAR).
+ sal_uInt16 mnRotation; /// Rotation angle of first pie (CHPIE).
+ sal_uInt16 mnPieHole; /// Hole size in donut chart (CHPIE).
+ sal_uInt16 mnBubbleSize; /// Bubble size in bubble chart (CHSCATTER).
+ sal_uInt16 mnBubbleType; /// Bubble type in bubble chart (CHSCATTER).
+ sal_uInt16 mnFlags; /// Additional flags (all chart types).
+
+ explicit XclChType();
+};
+
+struct XclChChart3d
+{
+ sal_uInt16 mnRotation; /// Rotation (0...359deg).
+ sal_Int16 mnElevation; /// Elevation (-90...+90deg).
+ sal_uInt16 mnEyeDist; /// Eye distance to chart (0...100).
+ sal_uInt16 mnRelHeight; /// Height relative to width.
+ sal_uInt16 mnRelDepth; /// Depth relative to width.
+ sal_uInt16 mnDepthGap; /// Space between series.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChChart3d();
+};
+
+struct XclChLegend
+{
+ XclChRectangle maRect; /// Position of the legend.
+ sal_uInt8 mnDockMode; /// Docking mode.
+ sal_uInt8 mnSpacing; /// Spacing between elements.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChLegend();
+};
+
+struct XclChTypeGroup
+{
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnGroupIdx; /// Chart type group index.
+
+ explicit XclChTypeGroup();
+};
+
+struct XclChProperties
+{
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt8 mnEmptyMode; /// Plotting mode for empty cells.
+
+ explicit XclChProperties();
+};
+
+// Axes =======================================================================
+
+struct XclChLabelRange
+{
+ sal_uInt16 mnCross; /// Crossing position of other axis.
+ sal_uInt16 mnLabelFreq; /// Frequency of labels.
+ sal_uInt16 mnTickFreq; /// Frequency of ticks.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChLabelRange();
+};
+
+struct XclChDateRange
+{
+ sal_uInt16 mnMinDate; /// Minimum value on axis.
+ sal_uInt16 mnMaxDate; /// Maximum value on axis.
+ sal_uInt16 mnMajorStep; /// Distance for major grid lines.
+ sal_uInt16 mnMajorUnit; /// Time unit for major step.
+ sal_uInt16 mnMinorStep; /// Distance for minor grid lines.
+ sal_uInt16 mnMinorUnit; /// Time unit for minor step.
+ sal_uInt16 mnBaseUnit; /// Time unit for axis values.
+ sal_uInt16 mnCross; /// Crossing position of other axis.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChDateRange();
+};
+
+struct XclChValueRange
+{
+ double mfMin; /// Minimum value on axis.
+ double mfMax; /// Maximum value on axis.
+ double mfMajorStep; /// Distance for major grid lines.
+ double mfMinorStep; /// Distance for minor grid lines.
+ double mfCross; /// Crossing position of other axis.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChValueRange();
+};
+
+struct XclChTick
+{
+ model::ComplexColor maTextComplexColor; /// Tick labels color.
+ sal_uInt8 mnMajor; /// Type of tick marks of major grid.
+ sal_uInt8 mnMinor; /// Type of tick marks of minor grid.
+ sal_uInt8 mnLabelPos; /// Position of labels relative to axis.
+ sal_uInt8 mnBackMode; /// Background mode: transparent, opaque.
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnRotation; /// Tick labels angle (BIFF8+).
+
+ explicit XclChTick();
+};
+
+struct XclChAxis
+{
+ sal_uInt16 mnType; /// Axis type.
+
+ explicit XclChAxis();
+
+ /** Returns the axis dimension index used by the chart API. */
+ sal_Int32 GetApiAxisDimension() const;
+};
+
+struct XclChAxesSet
+{
+ XclChRectangle maRect; /// Position of the axes set (inner plot area).
+ sal_uInt16 mnAxesSetId; /// Primary/secondary axes set.
+
+ explicit XclChAxesSet();
+
+ /** Returns the axes set index used by the chart API. */
+ sal_Int32 GetApiAxesSetIndex() const;
+};
+
+// Property mode ==============================================================
+
+/** Specifies the type of a formatting. This results in different property names. */
+enum XclChPropertyMode
+{
+ EXC_CHPROPMODE_COMMON, /// Common objects, no special handling.
+ EXC_CHPROPMODE_LINEARSERIES, /// Specific to data series drawn as lines.
+ EXC_CHPROPMODE_FILLEDSERIES /// Specific to data series drawn as areas.
+};
+
+// Static helper functions ====================================================
+
+/** Contains static helper functions for the chart filters. */
+class XclChartHelper
+{
+public:
+ /** Returns a palette index for automatic series line colors. */
+ static sal_uInt16 GetSeriesLineAutoColorIdx( sal_uInt16 nFormatIdx );
+ /** Returns a palette index for automatic series fill colors. */
+ static sal_uInt16 GetSeriesFillAutoColorIdx( sal_uInt16 nFormatIdx );
+ /** Returns a transparency value for automatic series fill colors. */
+ static sal_uInt8 GetSeriesFillAutoTransp( sal_uInt16 nFormatIdx );
+ /** Returns an automatic symbol index for the passed format index. */
+ static sal_uInt16 GetAutoMarkerType( sal_uInt16 nFormatIdx );
+ /** Returns true, if the passed marker type is filled. */
+ static bool HasMarkerFillColor( sal_uInt16 nMarkerType );
+ /** Returns the role name for a manual data source for error bars. */
+ static OUString GetErrorBarValuesRole( sal_uInt8 nBarType );
+};
+
+// Chart formatting info provider =============================================
+
+/** Enumerates different object types for specific automatic formatting behaviour. */
+enum XclChObjectType
+{
+ EXC_CHOBJTYPE_BACKGROUND, /// Chart background.
+ EXC_CHOBJTYPE_PLOTFRAME, /// Wall formatting in 2d charts.
+ EXC_CHOBJTYPE_WALL3D, /// Wall formatting in 3d charts.
+ EXC_CHOBJTYPE_FLOOR3D, /// Floor formatting in 3d charts.
+ EXC_CHOBJTYPE_TEXT, /// Text boxes (titles, data point labels).
+ EXC_CHOBJTYPE_LEGEND, /// Chart legend.
+ EXC_CHOBJTYPE_LINEARSERIES, /// Series formatting in a chart supporting line formatting only.
+ EXC_CHOBJTYPE_FILLEDSERIES, /// Series formatting in a chart supporting area formatting.
+ EXC_CHOBJTYPE_AXISLINE, /// Axis line format.
+ EXC_CHOBJTYPE_GRIDLINE, /// Axis grid line format.
+ EXC_CHOBJTYPE_TRENDLINE, /// Series trend line.
+ EXC_CHOBJTYPE_ERRORBAR, /// Series error bar.
+ EXC_CHOBJTYPE_CONNECTLINE, /// Data point connector line.
+ EXC_CHOBJTYPE_HILOLINE, /// High/low lines in stock charts.
+ EXC_CHOBJTYPE_WHITEDROPBAR, /// White-day drop bar in stock charts.
+ EXC_CHOBJTYPE_BLACKDROPBAR /// Black-day drop bar in stock charts.
+};
+
+/** Enumerates different types to handle missing frame objects. */
+enum XclChFrameType
+{
+ EXC_CHFRAMETYPE_AUTO, /// Missing frame represents automatic formatting.
+ EXC_CHFRAMETYPE_INVISIBLE /// Missing frame represents invisible formatting.
+};
+
+/** Contains information about auto formatting of a specific chart object type. */
+struct XclChFormatInfo
+{
+ XclChObjectType meObjType; /// Object type for automatic format.
+ XclChPropertyMode mePropMode; /// Property mode for property set helper.
+ sal_uInt16 mnAutoLineColorIdx; /// Automatic line color index.
+ sal_Int16 mnAutoLineWeight; /// Automatic line weight (hairline, single, ...).
+ sal_uInt16 mnAutoPattColorIdx; /// Automatic fill pattern color index.
+ XclChFrameType meDefFrameType; /// Default format type for missing frame objects.
+ bool mbCreateDefFrame; /// true = Create missing frame objects on import.
+ bool mbDeleteDefFrame; /// true = Delete default frame formatting on export.
+ bool mbIsFrame; /// true = Object is a frame, false = Object is a line.
+};
+
+/** Provides access to chart auto formatting for all available object types. */
+class XclChFormatInfoProvider
+{
+public:
+ explicit XclChFormatInfoProvider();
+
+ /** Returns an info struct about auto formatting for the passed object type. */
+ const XclChFormatInfo& GetFormatInfo( XclChObjectType eObjType ) const;
+
+private:
+ typedef ::std::map< XclChObjectType, const XclChFormatInfo* > XclFmtInfoMap;
+ XclFmtInfoMap maInfoMap; /// Maps object type to formatting data.
+};
+
+// Chart type info provider ===================================================
+
+/** Enumerates all kinds of different chart types. */
+enum XclChTypeId
+{
+ EXC_CHTYPEID_BAR, /// Vertical bar chart.
+ EXC_CHTYPEID_HORBAR, /// Horizontal bar chart.
+ EXC_CHTYPEID_LINE, /// Line chart.
+ EXC_CHTYPEID_AREA, /// Area chart.
+ EXC_CHTYPEID_STOCK, /// Stock chart.
+ EXC_CHTYPEID_RADARLINE, /// Linear radar chart.
+ EXC_CHTYPEID_RADARAREA, /// Filled radar chart.
+ EXC_CHTYPEID_PIE, /// Pie chart.
+ EXC_CHTYPEID_DONUT, /// Donut chart.
+ EXC_CHTYPEID_PIEEXT, /// Pie-to-pie or pie-to-bar chart.
+ EXC_CHTYPEID_SCATTER, /// Scatter (XY) chart.
+ EXC_CHTYPEID_BUBBLES, /// Bubble chart.
+ EXC_CHTYPEID_SURFACE, /// Surface chart.
+ EXC_CHTYPEID_UNKNOWN /// Default for unknown chart types.
+};
+
+/** Enumerates different categories of similar chart types. */
+enum XclChTypeCateg
+{
+ EXC_CHTYPECATEG_BAR, /// Bar charts (horizontal or vertical).
+ EXC_CHTYPECATEG_LINE, /// Line charts (line, area, stock charts).
+ EXC_CHTYPECATEG_RADAR, /// Radar charts (linear or filled).
+ EXC_CHTYPECATEG_PIE, /// Pie and donut charts.
+ EXC_CHTYPECATEG_SCATTER, /// Scatter and bubble charts.
+ EXC_CHTYPECATEG_SURFACE /// Surface charts.
+};
+
+/** Enumerates modes for varying point colors in a series. */
+enum XclChVarPointMode
+{
+ EXC_CHVARPOINT_NONE, /// No varied colors supported.
+ EXC_CHVARPOINT_SINGLE, /// Only supported, if type group contains only one series.
+ EXC_CHVARPOINT_MULTI /// Supported for multiple series in a chart type group.
+};
+
+/** Contains information for a chart type. */
+struct XclChTypeInfo
+{
+ XclChTypeId meTypeId; /// Unique chart type identifier.
+ XclChTypeCateg meTypeCateg; /// Chart type category this type belongs to.
+ sal_uInt16 mnRecId; /// Record identifier written to the file.
+ const char* mpcServiceName; /// Service name of the type.
+ XclChVarPointMode meVarPointMode; /// Mode for varying point colors.
+ sal_Int32 mnDefaultLabelPos; /// Default data label position (API constant).
+ bool mbCombinable2d; /// true = Types can be combined in one axes set.
+ bool mbSupports3d; /// true = 3d type allowed, false = Only 2d type.
+ bool mbPolarCoordSystem; /// true = Polar, false = Cartesian.
+ bool mbSeriesIsFrame2d; /// true = Series with area formatting (2d charts).
+ bool mbSeriesIsFrame3d; /// true = Series with area formatting (3d charts).
+ bool mbSingleSeriesVis; /// true = Only first series visible.
+ bool mbCategoryAxis; /// true = X axis contains categories.
+ bool mbSwappedAxesSet; /// true = X and Y axes are swapped.
+ bool mbSupportsStacking; /// true = Series can be stacked on each other.
+ bool mbReverseSeries; /// true = Insert unstacked series in reverse order.
+ bool mbTicksBetweenCateg; /// true = X axis ticks between categories.
+};
+
+/** Extended chart type information and access functions. */
+struct XclChExtTypeInfo : public XclChTypeInfo
+{
+ bool mb3dChart; /// Chart is actually a 3D chart.
+ bool mbSpline; /// Series lines are smoothed.
+
+ explicit XclChExtTypeInfo( const XclChTypeInfo& rTypeInfo );
+
+ void Set( const XclChTypeInfo& rTypeInfo, bool b3dChart, bool bSpline );
+
+ /** Returns true, if this chart type supports area formatting for its series. */
+ bool IsSeriesFrameFormat() const
+ { return mb3dChart ? mbSeriesIsFrame3d : mbSeriesIsFrame2d; }
+ /** Returns the correct object type identifier for series and data points. */
+ XclChObjectType GetSeriesObjectType() const
+ { return IsSeriesFrameFormat() ? EXC_CHOBJTYPE_FILLEDSERIES : EXC_CHOBJTYPE_LINEARSERIES; }
+};
+
+/** Provides access to chart type info structs for all available chart types. */
+class XclChTypeInfoProvider
+{
+public:
+ explicit XclChTypeInfoProvider();
+
+ /** Returns chart type info for a unique chart type identifier. */
+ const XclChTypeInfo& GetTypeInfo( XclChTypeId eType ) const;
+ /** Returns the first fitting chart type info for an Excel chart type record identifier. */
+ const XclChTypeInfo& GetTypeInfoFromRecId( sal_uInt16 nRecId ) const;
+ /** Returns the first fitting chart type info for the passed service name. */
+ const XclChTypeInfo& GetTypeInfoFromService( std::u16string_view rServiceName ) const;
+
+private:
+ typedef ::std::map< XclChTypeId, const XclChTypeInfo* > XclChTypeInfoMap;
+ XclChTypeInfoMap maInfoMap; /// Maps chart types to type info data.
+};
+
+// Chart text and title object helpers ========================================
+
+/** Enumerates different text box types for default text formatting and title
+ positioning. */
+enum XclChTextType
+{
+ EXC_CHTEXTTYPE_TITLE, /// Chart title.
+ EXC_CHTEXTTYPE_LEGEND, /// Chart legend.
+ EXC_CHTEXTTYPE_AXISTITLE, /// Chart axis titles.
+ EXC_CHTEXTTYPE_AXISLABEL, /// Chart axis labels.
+ EXC_CHTEXTTYPE_DATALABEL /// Data point labels.
+};
+
+/** A map key for text and title objects. */
+struct XclChTextKey : public ::std::pair< XclChTextType, ::std::pair< sal_uInt16, sal_uInt16 > >
+{
+ explicit XclChTextKey( XclChTextType eTextType, sal_uInt16 nMainIdx = 0, sal_uInt16 nSubIdx = 0 )
+ { first = eTextType; second.first = nMainIdx; second.second = nSubIdx; }
+};
+
+/** Function prototype receiving a chart document and returning a title shape. */
+typedef css::uno::Reference< css::drawing::XShape >
+ (*XclChGetShapeFunc)( const css::uno::Reference< css::chart::XChartDocument >& );
+
+// Property helpers ===========================================================
+
+class XclChObjectTable
+{
+public:
+ explicit XclChObjectTable( css::uno::Reference< css::lang::XMultiServiceFactory > xFactory,
+ OUString aServiceName, OUString aObjNameBase );
+
+ /** Returns a named formatting object from the chart document. */
+ css::uno::Any GetObject( const OUString& rObjName );
+ /** Inserts a named formatting object into the chart document. */
+ OUString InsertObject( const css::uno::Any& rObj );
+
+private:
+ css::uno::Reference< css::lang::XMultiServiceFactory > mxFactory; /// Factory to create the container.
+ css::uno::Reference< css::container::XNameContainer > mxContainer; /// Container for the objects.
+ OUString maServiceName; /// Service name to create the container.
+ OUString maObjNameBase; /// Base of names for inserted objects.
+ sal_Int32 mnIndex; /// Index to create unique identifiers.
+};
+
+/** Helper class for usage of property sets. */
+class XclChPropSetHelper
+{
+public:
+ explicit XclChPropSetHelper();
+
+ /** Reads all line properties from the passed property set. */
+ void ReadLineProperties(
+ XclChLineFormat& rLineFmt,
+ XclChObjectTable& rDashTable,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode );
+ /** Reads solid area properties from the passed property set.
+ @return true = object contains complex fill properties. */
+ bool ReadAreaProperties(
+ XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode );
+ /** Reads gradient or bitmap area properties from the passed property set. */
+ void ReadEscherProperties(
+ XclChEscherFormat& rEscherFmt,
+ XclChPicFormat& rPicFmt,
+ XclChObjectTable& rGradientTable,
+ XclChObjectTable& rHatchTable,
+ XclChObjectTable& rBitmapTable,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode );
+ /** Reads all marker properties from the passed property set. */
+ static void ReadMarkerProperties(
+ XclChMarkerFormat& rMarkerFmt,
+ const ScfPropertySet& rPropSet,
+ sal_uInt16 nFormatIdx );
+ /** Reads rotation properties from the passed property set. */
+ static sal_uInt16 ReadRotationProperties(
+ const ScfPropertySet& rPropSet,
+ bool bSupportsStacked );
+
+ /** Writes all line properties to the passed property set. */
+ void WriteLineProperties(
+ ScfPropertySet& rPropSet,
+ XclChObjectTable& rDashTable,
+ const XclChLineFormat& rLineFmt,
+ XclChPropertyMode ePropMode );
+ /** Writes solid area properties to the passed property set. */
+ void WriteAreaProperties(
+ ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt,
+ XclChPropertyMode ePropMode );
+ /** Writes gradient or bitmap area properties to the passed property set. */
+ void WriteEscherProperties(
+ ScfPropertySet& rPropSet,
+ XclChObjectTable& rGradientTable,
+ XclChObjectTable& rBitmapTable,
+ const XclChEscherFormat& rEscherFmt,
+ const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType,
+ XclChPropertyMode ePropMode );
+ /** Writes all marker properties to the passed property set. */
+ static void WriteMarkerProperties(
+ ScfPropertySet& rPropSet,
+ const XclChMarkerFormat& rMarkerFmt );
+ /** Writes rotation properties to the passed property set. */
+ static void WriteRotationProperties(
+ ScfPropertySet& rPropSet,
+ sal_uInt16 nRotation,
+ bool bSupportsStacked );
+
+private:
+ /** Returns a line property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetLineHelper( XclChPropertyMode ePropMode );
+ /** Returns an area property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetAreaHelper( XclChPropertyMode ePropMode );
+ /** Returns a gradient property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetGradientHelper( XclChPropertyMode ePropMode );
+ /** Returns a hatch property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetHatchHelper( XclChPropertyMode ePropMode );
+
+private:
+ ScfPropSetHelper maLineHlpCommon; /// Properties for lines in common objects.
+ ScfPropSetHelper maLineHlpLinear; /// Properties for lines in linear series.
+ ScfPropSetHelper maLineHlpFilled; /// Properties for lines in filled series.
+ ScfPropSetHelper maAreaHlpCommon; /// Properties for areas in common objects.
+ ScfPropSetHelper maAreaHlpFilled; /// Properties for areas in filled series.
+ ScfPropSetHelper maGradHlpCommon; /// Properties for gradients in common objects.
+ ScfPropSetHelper maGradHlpFilled; /// Properties for gradients in filled series.
+ ScfPropSetHelper maHatchHlpCommon; /// Properties for hatches in common objects.
+ ScfPropSetHelper maHatchHlpFilled; /// Properties for hatches in filled series.
+ ScfPropSetHelper maBitmapHlp; /// Properties for bitmaps.
+};
+
+/** Base struct for internal root data structs for import and export. */
+struct XclChRootData
+{
+ typedef std::shared_ptr< XclChTypeInfoProvider > XclChTypeProvRef;
+ typedef std::shared_ptr< XclChFormatInfoProvider > XclChFmtInfoProvRef;
+ typedef std::shared_ptr< XclChObjectTable > XclChObjectTableRef;
+ typedef std::map< XclChTextKey, XclChGetShapeFunc > XclChGetShapeFuncMap;
+
+ css::uno::Reference< css::chart2::XChartDocument >
+ mxChartDoc; /// The chart document.
+ tools::Rectangle maChartRect; /// Position and size of the chart shape.
+ XclChTypeProvRef mxTypeInfoProv; /// Provides info about chart types.
+ XclChFmtInfoProvRef mxFmtInfoProv; /// Provides info about auto formatting.
+ XclChObjectTableRef mxLineDashTable; /// Container for line dash styles.
+ XclChObjectTableRef mxGradientTable; /// Container for gradient fill styles.
+ XclChObjectTableRef mxHatchTable; /// Container for hatch fill styles.
+ XclChObjectTableRef mxBitmapTable; /// Container for bitmap fill styles.
+ XclChGetShapeFuncMap maGetShapeFuncs; /// Maps title shape getter functions.
+ sal_Int32 mnBorderGapX; /// Border gap to chart space in 1/100mm.
+ sal_Int32 mnBorderGapY; /// Border gap to chart space in 1/100mm.
+ double mfUnitSizeX; /// Size of a chart X unit (1/4000 of chart width) in 1/100 mm.
+ double mfUnitSizeY; /// Size of a chart Y unit (1/4000 of chart height) in 1/100 mm.
+
+ explicit XclChRootData();
+ virtual ~XclChRootData();
+
+ /** Starts the API chart document conversion. Must be called once before any API access. */
+ void InitConversion(
+ const XclRoot& rRoot,
+ const css::uno::Reference< css::chart2::XChartDocument >& rxChartDoc,
+ const tools::Rectangle& rChartRect );
+ /** Finishes the API chart document conversion. Must be called once before any API access. */
+ void FinishConversion();
+
+ /** Returns the drawing shape interface of the specified title object. */
+ css::uno::Reference< css::drawing::XShape >
+ GetTitleShape( const XclChTextKey& rTitleKey ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlconst.hxx b/sc/source/filter/inc/xlconst.hxx
new file mode 100644
index 0000000000..e9d791d08e
--- /dev/null
+++ b/sc/source/filter/inc/xlconst.hxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+
+// Common =====================================================================
+
+// BIFF versions --------------------------------------------------------------
+
+/** An enumeration for all Excel file format types (BIFF types). */
+enum XclBiff
+{
+ EXC_BIFF2 = 0, /// MS Excel 2.1
+ EXC_BIFF3, /// MS Excel 3.0
+ EXC_BIFF4, /// MS Excel 4.0
+ EXC_BIFF5, /// MS Excel 5.0, MS Excel 7.0 (95)
+ EXC_BIFF8, /// MS Excel 8.0 (97), 9.0 (2000), 10.0 (XP), 11.0 (2003)
+ EXC_BIFF_UNKNOWN /// Unknown BIFF version.
+};
+
+/** An enumeration for all Excel output format types. */
+enum XclOutput
+{
+ EXC_OUTPUT_BINARY, /// MS Excel binary .xls
+ EXC_OUTPUT_XML_2007, /// MS Excel 2007 .xlsx
+};
+
+// Excel sheet dimensions -----------------------------------------------------
+
+const SCCOL EXC_MAXCOL2 = 255;
+const SCROW EXC_MAXROW2 = 16383;
+const SCTAB EXC_MAXTAB2 = 0;
+
+const SCCOL EXC_MAXCOL3 = EXC_MAXCOL2;
+const SCROW EXC_MAXROW3 = EXC_MAXROW2;
+const SCTAB EXC_MAXTAB3 = EXC_MAXTAB2;
+
+const SCCOL EXC_MAXCOL4 = EXC_MAXCOL3;
+const SCROW EXC_MAXROW4 = EXC_MAXROW3;
+const SCTAB EXC_MAXTAB4 = 32767;
+
+const SCCOL EXC_MAXCOL5 = EXC_MAXCOL4;
+const SCROW EXC_MAXROW5 = EXC_MAXROW4;
+const SCTAB EXC_MAXTAB5 = EXC_MAXTAB4;
+
+const SCCOL EXC_MAXCOL8 = EXC_MAXCOL5;
+const SCROW EXC_MAXROW8 = 65535;
+const SCTAB EXC_MAXTAB8 = EXC_MAXTAB5;
+
+const SCCOL EXC_MAXCOL_XML_2007 = 16383;
+const SCROW EXC_MAXROW_XML_2007 = 1048575;
+const SCTAB EXC_MAXTAB_XML_2007 = 1023;
+
+const sal_uInt16 EXC_NOTAB = SAL_MAX_UINT16; /// An invalid Excel sheet index, for common use.
+const SCTAB SCTAB_INVALID = SCTAB_MAX; /// An invalid Calc sheet index, for common use.
+const SCTAB SCTAB_GLOBAL = SCTAB_MAX; /// A Calc sheet index for the workbook globals.
+
+// Storage/stream names -------------------------------------------------------
+
+#define EXC_STORAGE_OLE_LINKED "LNK"
+#define EXC_STORAGE_OLE_EMBEDDED "MBD"
+inline constexpr OUString EXC_STORAGE_VBA_PROJECT = u"_VBA_PROJECT_CUR"_ustr;
+
+inline constexpr OUString EXC_STREAM_BOOK = u"Book"_ustr;
+inline constexpr OUString EXC_STREAM_WORKBOOK = u"Workbook"_ustr;
+inline constexpr OUString EXC_STREAM_CTLS = u"Ctls"_ustr;
+
+// Encoded URLs ---------------------------------------------------------------
+
+const sal_Unicode EXC_URLSTART_ENCODED = '\x01'; /// Encoded URL.
+const sal_Unicode EXC_URLSTART_SELF = '\x02'; /// Reference to own workbook.
+const sal_Unicode EXC_URLSTART_SELFENCODED = '\x03'; /// Encoded self reference.
+const sal_Unicode EXC_URLSTART_OWNDOC = '\x04'; /// Reference to own workbook (BIFF5/BIFF7).
+
+const sal_Unicode EXC_URL_DOSDRIVE = '\x01'; /// DOS drive letter or UNC server name.
+const sal_Unicode EXC_URL_DRIVEROOT = '\x02'; /// Root directory of current drive.
+const sal_Unicode EXC_URL_SUBDIR = '\x03'; /// Directory name delimiter.
+const sal_Unicode EXC_URL_PARENTDIR = '\x04'; /// Parent directory.
+const sal_Unicode EXC_URL_RAW = '\x05'; /// Unencoded URL.
+const sal_Unicode EXC_URL_SHEETNAME = '\x09'; /// Sheet name starts here (BIFF4).
+
+const sal_Unicode EXC_DDE_DELIM = '\x03'; /// DDE application-topic delimiter
+
+// Error codes ----------------------------------------------------------------
+
+const sal_uInt8 EXC_ERR_NULL = 0x00;
+const sal_uInt8 EXC_ERR_DIV0 = 0x07;
+const sal_uInt8 EXC_ERR_VALUE = 0x0F;
+const sal_uInt8 EXC_ERR_REF = 0x17;
+const sal_uInt8 EXC_ERR_NAME = 0x1D;
+const sal_uInt8 EXC_ERR_NUM = 0x24;
+const sal_uInt8 EXC_ERR_NA = 0x2A;
+
+// Cached values list (EXTERNNAME, ptgArray, ...) -----------------------------
+
+const sal_uInt8 EXC_CACHEDVAL_EMPTY = 0x00;
+const sal_uInt8 EXC_CACHEDVAL_DOUBLE = 0x01;
+const sal_uInt8 EXC_CACHEDVAL_STRING = 0x02;
+const sal_uInt8 EXC_CACHEDVAL_BOOL = 0x04;
+const sal_uInt8 EXC_CACHEDVAL_ERROR = 0x10;
+
+// RK values ------------------------------------------------------------------
+
+const sal_Int32 EXC_RK_100FLAG = 0x00000001;
+const sal_Int32 EXC_RK_INTFLAG = 0x00000002;
+const sal_Int32 EXC_RK_VALUEMASK = 0xFFFFFFFC;
+
+const sal_Int32 EXC_RK_DBL = 0x00000000;
+const sal_Int32 EXC_RK_DBL100 = EXC_RK_100FLAG;
+const sal_Int32 EXC_RK_INT = EXC_RK_INTFLAG;
+const sal_Int32 EXC_RK_INT100 = EXC_RK_100FLAG | EXC_RK_INTFLAG;
+
+// Measures -------------------------------------------------------------------
+
+const sal_uInt8 EXC_ORIENT_NONE = 0; /// Text orientation: not rotated.
+const sal_uInt8 EXC_ORIENT_STACKED = 1; /// Text orientation: vertically stacked.
+const sal_uInt8 EXC_ORIENT_90CCW = 2; /// Text orientation: 90 deg counterclockwise.
+const sal_uInt8 EXC_ORIENT_90CW = 3; /// Text orientation: 90 deg clockwise.
+
+const sal_uInt8 EXC_ROT_NONE = 0; /// Text rotation: not rotated.
+const sal_uInt8 EXC_ROT_90CCW = 90; /// Text rotation: 90 deg counterclockwise.
+const sal_uInt8 EXC_ROT_90CW = 180; /// Text rotation: 90 deg clockwise.
+const sal_uInt8 EXC_ROT_STACKED = 255; /// Text rotation: vertically stacked.
+
+// Records (ordered by lowest record ID) ======================================
+
+// (0x0009, 0x0209, 0x0409, 0x0809) BOF ---------------------------------------
+
+const sal_uInt16 EXC_ID2_BOF = 0x0009;
+const sal_uInt16 EXC_ID3_BOF = 0x0209;
+const sal_uInt16 EXC_ID4_BOF = 0x0409;
+const sal_uInt16 EXC_ID5_BOF = 0x0809;
+
+const sal_uInt16 EXC_BOF_BIFF2 = 0x0200;
+const sal_uInt16 EXC_BOF_BIFF3 = 0x0300;
+const sal_uInt16 EXC_BOF_BIFF4 = 0x0400;
+const sal_uInt16 EXC_BOF_BIFF5 = 0x0500;
+const sal_uInt16 EXC_BOF_BIFF8 = 0x0600;
+
+const sal_uInt16 EXC_BOF_GLOBALS = 0x0005; /// BIFF5-BIFF8 workbook globals.
+const sal_uInt16 EXC_BOF_VBMODULE = 0x0006; /// BIFF5-BIFF8 Visual BASIC module.
+const sal_uInt16 EXC_BOF_SHEET = 0x0010; /// Regular worksheet.
+const sal_uInt16 EXC_BOF_CHART = 0x0020; /// Chart sheet.
+const sal_uInt16 EXC_BOF_MACROSHEET = 0x0040; /// Macro sheet.
+const sal_uInt16 EXC_BOF_WORKSPACE = 0x0100; /// Workspace.
+const sal_uInt16 EXC_BOF_UNKNOWN = 0xFFFF; /// Internal use only.
+
+// (0x000A) EOF ---------------------------------------------------------------
+const sal_uInt16 EXC_ID_EOF = 0x000A;
+
+// (0x0012) PROTECT -----------------------------------------------------------
+const sal_uInt16 EXC_ID_PROTECT = 0x0012;
+
+// (0x0013) PASSWORD ----------------------------------------------------------
+const sal_uInt16 EXC_ID_PASSWORD = 0x0013;
+
+// (0x0019) WINDOWPROTECT -----------------------------------------------------
+const sal_uInt16 EXC_ID_WINDOWPROTECT = 0x0019;
+
+// (0x0042) CODEPAGE ----------------------------------------------------------
+const sal_uInt16 EXC_ID_CODEPAGE = 0x0042;
+
+// (0x0081) WSBOOL ------------------------------------------------------------
+const sal_uInt16 EXC_ID_WSBOOL = 0x0081;
+
+const sal_uInt16 EXC_WSBOOL_ROWBELOW = 0x0040;
+const sal_uInt16 EXC_WSBOOL_COLBELOW = 0x0080;
+const sal_uInt16 EXC_WSBOOL_FITTOPAGE = 0x0100;
+
+const sal_uInt16 EXC_WSBOOL_DEFAULTFLAGS = 0x04C1;
+
+// (0x0086) WRITEPROT ---------------------------------------------------------
+const sal_uInt16 EXC_ID_WRITEPROT = 0x0086;
+
+// (0x008C) COUNTRY -----------------------------------------------------------
+const sal_uInt16 EXC_ID_COUNTRY = 0x008C;
+
+// (0x009B) FILTERMODE --------------------------------------------------------
+const sal_uInt16 EXC_ID_FILTERMODE = 0x009B;
+
+// (0x009C) FNGROUPCOUNT ------------------------------------------------------
+const sal_uInt16 EXC_ID_FNGROUPCOUNT = 0x009C;
+
+// (0x009D) AUTOFILTERINFO ----------------------------------------------------
+const sal_uInt16 EXC_ID_AUTOFILTERINFO = 0x009D;
+
+// (0x009E) AUTOFILTER --------------------------------------------------------
+const sal_uInt16 EXC_ID_AUTOFILTER = 0x009E;
+
+// (0x00BF, 0x00C0, 0x00C1) TOOLBARHDR, TOOLBAREND, MMS -----------------------
+const sal_uInt16 EXC_ID_TOOLBARHDR = 0x00BF;
+const sal_uInt16 EXC_ID_TOOLBAREND = 0x00C0;
+const sal_uInt16 EXC_ID_MMS = 0x00C1;
+
+// (0x00E1, 0x00E2) INTERFACEHDR, INTERFACEEND --------------------------------
+const sal_uInt16 EXC_ID_INTERFACEHDR = 0x00E1;
+const sal_uInt16 EXC_ID_INTERFACEEND = 0x00E2;
+
+// (0x0160) USESELFS ----------------------------------------------------------
+const sal_uInt16 EXC_ID_USESELFS = 0x0160;
+
+// (0x0161) DSF ---------------------------------------------------------------
+const sal_uInt16 EXC_ID_DSF = 0x0161;
+
+// (0x01AA,0x01AB) USERSVIEWBEGIN, USERSVIEWEND -------------------------------
+const sal_uInt16 EXC_ID_USERSVIEWBEGIN = 0x01AA;
+const sal_uInt16 EXC_ID_USERSVIEWEND = 0x01AB;
+
+// (0x01BA) CODENAME ----------------------------------------------------------
+const sal_uInt16 EXC_ID_CODENAME = 0x01BA;
+
+// (0x01C0) XL9FILE -----------------------------------------------------------
+const sal_uInt16 EXC_ID_XL9FILE = 0x01C0;
+
+// (0x8xx) Future records -----------------------------------------------------
+
+/** Enumerates different header types of future records. */
+enum XclFutureRecType
+{
+ EXC_FUTUREREC_SIMPLE, /// Record identifier and empty flags field.
+ EXC_FUTUREREC_UNUSEDREF /// Record identifier, empty flags field, unused range address.
+};
+
+const sal_uInt16 EXC_FUTUREREC_EMPTYFLAGS = 0x0000;
+const sal_uInt16 EXC_FUTUREREC_HASREF = 0x0001;
+const sal_uInt16 EXC_FUTUREREC_ALERT = 0x0002;
+
+// Border import/export
+
+const sal_uInt16 EXC_BORDER_THICK = 50;
+const sal_uInt16 EXC_BORDER_MEDIUM = 35;
+const sal_uInt16 EXC_BORDER_THIN = 15;
+const sal_uInt16 EXC_BORDER_HAIR = 1;
+
+// SharedFeatureType enumeration
+const sal_uInt16 EXC_ISFPROTECTION = 0x0002;
+const sal_uInt16 EXC_ISFFEC2 = 0x0003;
+const sal_uInt16 EXC_ISFFACTOID = 0x0004;
+const sal_uInt16 EXC_ISFLIST = 0x0005;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlcontent.hxx b/sc/source/filter/inc/xlcontent.hxx
new file mode 100644
index 0000000000..207c6e4c0a
--- /dev/null
+++ b/sc/source/filter/inc/xlcontent.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+// Constants ==================================================================
+
+// (0x005B) FILESHARING -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_FILESHARING = 0x005B;
+
+// (0x00E5) MERGEDCELLS -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_MERGEDCELLS = 0x00E5;
+const sal_uInt16 EXC_MERGEDCELLS_MAXCOUNT = 1027;
+
+// (0x002F) FILEPASS ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_FILEPASS = 0x002F;
+
+const sal_uInt16 EXC_FILEPASS_BIFF5 = 0x0000;
+const sal_uInt16 EXC_FILEPASS_BIFF8 = 0x0001;
+
+// (0x00FC, 0x00FF) SST, EXTSST -----------------------------------------------
+
+const sal_uInt16 EXC_ID_SST = 0x00FC;
+const sal_uInt16 EXC_ID_EXTSST = 0x00FF;
+
+// (0x015F) LABELRANGES -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_LABELRANGES = 0x015F;
+
+// (0x01B0) CONDFMT, (0x01B1) CF ----------------------------------------------
+
+const sal_uInt16 EXC_ID_CONDFMT = 0x01B0;
+const sal_uInt16 EXC_ID_CF = 0x01B1;
+
+const sal_uInt8 EXC_CF_TYPE_NONE = 0x00;
+const sal_uInt8 EXC_CF_TYPE_CELL = 0x01;
+const sal_uInt8 EXC_CF_TYPE_FMLA = 0x02;
+
+const sal_uInt8 EXC_CF_CMP_NONE = 0x00;
+const sal_uInt8 EXC_CF_CMP_BETWEEN = 0x01;
+const sal_uInt8 EXC_CF_CMP_NOT_BETWEEN = 0x02;
+const sal_uInt8 EXC_CF_CMP_EQUAL = 0x03;
+const sal_uInt8 EXC_CF_CMP_NOT_EQUAL = 0x04;
+const sal_uInt8 EXC_CF_CMP_GREATER = 0x05;
+const sal_uInt8 EXC_CF_CMP_LESS = 0x06;
+const sal_uInt8 EXC_CF_CMP_GREATER_EQUAL = 0x07;
+const sal_uInt8 EXC_CF_CMP_LESS_EQUAL = 0x08;
+
+const sal_uInt32 EXC_CF_BORDER_LEFT = 0x00000400; /// Left border line modified?
+const sal_uInt32 EXC_CF_BORDER_RIGHT = 0x00000800; /// Right border line modified?
+const sal_uInt32 EXC_CF_BORDER_TOP = 0x00001000; /// Top border line modified?
+const sal_uInt32 EXC_CF_BORDER_BOTTOM = 0x00002000; /// Bottom border line modified?
+const sal_uInt32 EXC_CF_BORDER_ALL = 0x00003C00; /// Any border line modified?
+const sal_uInt32 EXC_CF_AREA_PATTERN = 0x00010000; /// Pattern modified?
+const sal_uInt32 EXC_CF_AREA_FGCOLOR = 0x00020000; /// Foreground color modified?
+const sal_uInt32 EXC_CF_AREA_BGCOLOR = 0x00040000; /// Background color modified?
+const sal_uInt32 EXC_CF_AREA_ALL = 0x00070000; /// Any area attribute modified?
+const sal_uInt32 EXC_CF_ALLDEFAULT = 0x003FFFFF; /// Default flags.
+const sal_uInt32 EXC_CF_BLOCK_NUMFMT = 0x02000000; /// Font block present?
+const sal_uInt32 EXC_CF_BLOCK_FONT = 0x04000000; /// Font block present?
+const sal_uInt32 EXC_CF_BLOCK_ALIGNMENT = 0x08000000; /// Alignment block present?
+const sal_uInt32 EXC_CF_BLOCK_BORDER = 0x10000000; /// Border block present?
+const sal_uInt32 EXC_CF_BLOCK_AREA = 0x20000000; /// Pattern block present?
+const sal_uInt32 EXC_CF_BLOCK_PROTECTION = 0x40000000; /// Protection block present?
+const sal_uInt32 EXC_CF_IFMT_USER = 0x1; /// NumberFormat String or Id?
+
+const sal_uInt32 EXC_CF_FONT_STYLE = 0x00000002; /// Font posture or weight modified?
+const sal_uInt32 EXC_CF_FONT_STRIKEOUT = 0x00000080; /// Font cancellation modified?
+const sal_uInt32 EXC_CF_FONT_ALLDEFAULT = 0x0000009A; /// Default flags.
+
+const sal_uInt32 EXC_CF_FONT_UNDERL = 0x00000001; /// Font underline type modified?
+const sal_uInt32 EXC_CF_FONT_ESCAPEM = 0x00000001; /// Font escapement type modified?
+
+// (0x01B2) DVAL --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_DVAL = 0x01B2;
+const sal_uInt32 EXC_DVAL_NOOBJ = 0xFFFFFFFF;
+
+// (0x01BE) DV ----------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_DV = 0x01BE;
+
+// data validation flags
+const sal_uInt32 EXC_DV_STRINGLIST = 0x00000080;
+const sal_uInt32 EXC_DV_IGNOREBLANK = 0x00000100;
+const sal_uInt32 EXC_DV_SUPPRESSDROPDOWN = 0x00000200;
+const sal_uInt32 EXC_DV_SHOWPROMPT = 0x00040000;
+const sal_uInt32 EXC_DV_SHOWERROR = 0x00080000;
+
+// data validation data mode
+const sal_uInt32 EXC_DV_MODE_MASK = 0x0000000F;
+const sal_uInt32 EXC_DV_MODE_ANY = 0x00000000;
+const sal_uInt32 EXC_DV_MODE_WHOLE = 0x00000001;
+const sal_uInt32 EXC_DV_MODE_DECIMAL = 0x00000002;
+const sal_uInt32 EXC_DV_MODE_LIST = 0x00000003;
+const sal_uInt32 EXC_DV_MODE_DATE = 0x00000004;
+const sal_uInt32 EXC_DV_MODE_TIME = 0x00000005;
+const sal_uInt32 EXC_DV_MODE_TEXTLEN = 0x00000006;
+const sal_uInt32 EXC_DV_MODE_CUSTOM = 0x00000007;
+
+// data validation conditions
+const sal_uInt32 EXC_DV_COND_MASK = 0x00F00000;
+const sal_uInt32 EXC_DV_COND_BETWEEN = 0x00000000;
+const sal_uInt32 EXC_DV_COND_NOTBETWEEN = 0x00100000;
+const sal_uInt32 EXC_DV_COND_EQUAL = 0x00200000;
+const sal_uInt32 EXC_DV_COND_NOTEQUAL = 0x00300000;
+const sal_uInt32 EXC_DV_COND_GREATER = 0x00400000;
+const sal_uInt32 EXC_DV_COND_LESS = 0x00500000;
+const sal_uInt32 EXC_DV_COND_EQGREATER = 0x00600000;
+const sal_uInt32 EXC_DV_COND_EQLESS = 0x00700000;
+
+// data validation error style
+const sal_uInt32 EXC_DV_ERROR_MASK = 0x00000070;
+const sal_uInt32 EXC_DV_ERROR_STOP = 0x00000000;
+const sal_uInt32 EXC_DV_ERROR_WARNING = 0x00000010;
+const sal_uInt32 EXC_DV_ERROR_INFO = 0x00000020;
+
+// (0x01B8) HLINK -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_HLINK = 0x01B8;
+
+const sal_uInt32 EXC_HLINK_BODY = 0x00000001; /// Contains file link or URL.
+const sal_uInt32 EXC_HLINK_ABS = 0x00000002; /// Absolute path.
+const sal_uInt32 EXC_HLINK_DESCR = 0x00000014; /// Description.
+const sal_uInt32 EXC_HLINK_MARK = 0x00000008; /// Text mark.
+const sal_uInt32 EXC_HLINK_FRAME = 0x00000080; /// Target frame.
+const sal_uInt32 EXC_HLINK_UNC = 0x00000100; /// UNC path.
+
+// web queries ================================================================
+
+inline constexpr OUString EXC_WEBQRY_FILTER = u"calc_HTML_WebQuery"_ustr;
+
+// (0x00CD) WQSTRING
+const sal_uInt16 EXC_ID_WQSTRING = 0x00CD;
+
+// (0x00DC) PARAMQRY
+const sal_uInt16 EXC_ID_PQRY = 0x00DC;
+const sal_uInt16 EXC_PQRYTYPE_ODBC = 1; /// Source type: ODBC.
+const sal_uInt16 EXC_PQRYTYPE_WEBQUERY = 4; /// Source type: webquery.
+const sal_uInt16 EXC_PQRY_ODBC = 0x0008; /// ODBC connection.
+const sal_uInt16 EXC_PQRY_WEBQUERY = 0x0040; /// Web query.
+const sal_uInt16 EXC_PQRY_TABLES = 0x0100; /// All tables.
+
+// (0x01AD) QSI
+const sal_uInt16 EXC_ID_QSI = 0x01AD;
+const sal_uInt16 EXC_QSI_DEFAULTFLAGS = 0x0349; /// Flags for export.
+
+// (0x0802) unknown record
+const sal_uInt16 EXC_ID_0802 = 0x0802;
+
+// (0x0803) WEBQRYSETTINGS
+const sal_uInt16 EXC_ID_WQSETT = 0x0803;
+const sal_uInt16 EXC_WQSETT_ALL = 0x0000; /// All tables or entire document.
+const sal_uInt16 EXC_WQSETT_SPECTABLES = 0x0002; /// Specific tables.
+const sal_uInt16 EXC_WQSETT_DEFAULTFLAGS = 0x0023; /// Flags for export.
+const sal_uInt16 EXC_WQSETT_NOFORMAT = 0x0001;
+const sal_uInt16 EXC_WQSETT_FORMATRTF = 0x0002;
+const sal_uInt16 EXC_WQSETT_FORMATFULL = 0x0003;
+
+// (0x0804) WEBQRYTABLES
+const sal_uInt16 EXC_ID_WQTABLES = 0x0804;
+
+const sal_uInt16 EXC_ID_EXTLST = 0x9988; /// it is just a random number
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlescher.hxx b/sc/source/filter/inc/xlescher.hxx
new file mode 100644
index 0000000000..913f667593
--- /dev/null
+++ b/sc/source/filter/inc/xlescher.hxx
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include <tools/mapunit.hxx>
+#include "xladdress.hxx"
+#include "xlstyle.hxx"
+#include "xistream.hxx"
+
+namespace com::sun::star {
+ namespace drawing { class XShape; }
+ namespace awt { class XControlModel; }
+ namespace script { struct ScriptEventDescriptor; }
+}
+
+// Constants and Enumerations =================================================
+
+// (0x001C) NOTE --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_NOTE = 0x001C;
+const sal_uInt16 EXC_NOTE_VISIBLE = 0x0002;
+const sal_uInt16 EXC_NOTE5_MAXLEN = 2048;
+
+// (0x005D) OBJ ---------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_OBJ = 0x005D;
+
+const sal_uInt16 EXC_OBJ_INVALID_ID = 0;
+
+// object types
+const sal_uInt16 EXC_OBJTYPE_GROUP = 0;
+const sal_uInt16 EXC_OBJTYPE_LINE = 1;
+const sal_uInt16 EXC_OBJTYPE_RECTANGLE = 2;
+const sal_uInt16 EXC_OBJTYPE_OVAL = 3;
+const sal_uInt16 EXC_OBJTYPE_ARC = 4;
+const sal_uInt16 EXC_OBJTYPE_CHART = 5;
+const sal_uInt16 EXC_OBJTYPE_TEXT = 6;
+const sal_uInt16 EXC_OBJTYPE_BUTTON = 7;
+const sal_uInt16 EXC_OBJTYPE_PICTURE = 8;
+const sal_uInt16 EXC_OBJTYPE_POLYGON = 9; // new in BIFF4
+const sal_uInt16 EXC_OBJTYPE_CHECKBOX = 11; // new in BIFF5
+const sal_uInt16 EXC_OBJTYPE_OPTIONBUTTON = 12;
+const sal_uInt16 EXC_OBJTYPE_EDIT = 13;
+const sal_uInt16 EXC_OBJTYPE_LABEL = 14;
+const sal_uInt16 EXC_OBJTYPE_DIALOG = 15;
+const sal_uInt16 EXC_OBJTYPE_SPIN = 16;
+const sal_uInt16 EXC_OBJTYPE_SCROLLBAR = 17;
+const sal_uInt16 EXC_OBJTYPE_LISTBOX = 18;
+const sal_uInt16 EXC_OBJTYPE_GROUPBOX = 19;
+const sal_uInt16 EXC_OBJTYPE_DROPDOWN = 20;
+const sal_uInt16 EXC_OBJTYPE_NOTE = 25; // new in BIFF8
+const sal_uInt16 EXC_OBJTYPE_DRAWING = 30;
+const sal_uInt16 EXC_OBJTYPE_UNKNOWN = 0xFFFF; /// For internal use only.
+
+// BIFF3-BIFF5 flags
+const sal_uInt16 EXC_OBJ_HIDDEN = 0x0100;
+const sal_uInt16 EXC_OBJ_VISIBLE = 0x0200;
+const sal_uInt16 EXC_OBJ_PRINTABLE = 0x0400;
+
+// BIFF5 line formatting
+const sal_uInt8 EXC_OBJ_LINE_AUTOCOLOR = 64;
+
+const sal_uInt8 EXC_OBJ_LINE_SOLID = 0;
+const sal_uInt8 EXC_OBJ_LINE_DASH = 1;
+const sal_uInt8 EXC_OBJ_LINE_DOT = 2;
+const sal_uInt8 EXC_OBJ_LINE_DASHDOT = 3;
+const sal_uInt8 EXC_OBJ_LINE_DASHDOTDOT = 4;
+const sal_uInt8 EXC_OBJ_LINE_MEDTRANS = 5;
+const sal_uInt8 EXC_OBJ_LINE_DARKTRANS = 6;
+const sal_uInt8 EXC_OBJ_LINE_LIGHTTRANS = 7;
+const sal_uInt8 EXC_OBJ_LINE_NONE = 255;
+
+const sal_uInt8 EXC_OBJ_LINE_HAIR = 0;
+const sal_uInt8 EXC_OBJ_LINE_THIN = 1;
+const sal_uInt8 EXC_OBJ_LINE_MEDIUM = 2;
+const sal_uInt8 EXC_OBJ_LINE_THICK = 3;
+
+const sal_uInt8 EXC_OBJ_LINE_AUTO = 0x01;
+
+const sal_uInt8 EXC_OBJ_ARROW_NONE = 0;
+const sal_uInt8 EXC_OBJ_ARROW_OPEN = 1;
+const sal_uInt8 EXC_OBJ_ARROW_FILLED = 2;
+const sal_uInt8 EXC_OBJ_ARROW_OPENBOTH = 3;
+const sal_uInt8 EXC_OBJ_ARROW_FILLEDBOTH = 4;
+
+const sal_uInt8 EXC_OBJ_ARROW_NARROW = 0;
+const sal_uInt8 EXC_OBJ_ARROW_MEDIUM = 1;
+const sal_uInt8 EXC_OBJ_ARROW_WIDE = 2;
+
+const sal_uInt8 EXC_OBJ_LINE_TL = 0;
+const sal_uInt8 EXC_OBJ_LINE_TR = 1;
+const sal_uInt8 EXC_OBJ_LINE_BR = 2;
+const sal_uInt8 EXC_OBJ_LINE_BL = 3;
+
+// BIFF5 fill formatting
+const sal_uInt8 EXC_OBJ_FILL_AUTOCOLOR = 65;
+
+const sal_uInt8 EXC_OBJ_FILL_AUTO = 0x01;
+
+// BIFF5 frame formatting
+const sal_uInt16 EXC_OBJ_FRAME_SHADOW = 0x0002;
+
+// BIFF5 text objects
+const sal_uInt8 EXC_OBJ_HOR_LEFT = 1;
+const sal_uInt8 EXC_OBJ_HOR_CENTER = 2;
+const sal_uInt8 EXC_OBJ_HOR_RIGHT = 3;
+const sal_uInt8 EXC_OBJ_HOR_JUSTIFY = 4;
+
+const sal_uInt8 EXC_OBJ_VER_TOP = 1;
+const sal_uInt8 EXC_OBJ_VER_CENTER = 2;
+const sal_uInt8 EXC_OBJ_VER_BOTTOM = 3;
+const sal_uInt8 EXC_OBJ_VER_JUSTIFY = 4;
+
+const sal_uInt16 EXC_OBJ_ORIENT_NONE = 0;
+const sal_uInt16 EXC_OBJ_ORIENT_STACKED = 1; /// Stacked top to bottom.
+const sal_uInt16 EXC_OBJ_ORIENT_90CCW = 2; /// 90 degr. counterclockwise.
+const sal_uInt16 EXC_OBJ_ORIENT_90CW = 3; /// 90 degr. clockwise.
+
+const sal_uInt16 EXC_OBJ_TEXT_AUTOSIZE = 0x0080;
+const sal_uInt16 EXC_OBJ_TEXT_LOCKED = 0x0200;
+
+const sal_Int32 EXC_OBJ_TEXT_MARGIN = 20000; /// Automatic text margin (EMUs).
+
+// BIFF5 arc objects
+const sal_uInt8 EXC_OBJ_ARC_TR = 0;
+const sal_uInt8 EXC_OBJ_ARC_TL = 1;
+const sal_uInt8 EXC_OBJ_ARC_BL = 2;
+const sal_uInt8 EXC_OBJ_ARC_BR = 3;
+
+// BIFF5 polygon objects
+const sal_uInt16 EXC_OBJ_POLY_CLOSED = 0x0100;
+
+// BIFF5 pictures/OLE objects
+const sal_uInt16 EXC_OBJ_PIC_MANUALSIZE = 0x0001;
+const sal_uInt16 EXC_OBJ_PIC_DDE = 0x0002;
+const sal_uInt16 EXC_OBJ_PIC_SYMBOL = 0x0008;
+const sal_uInt16 EXC_OBJ_PIC_CONTROL = 0x0010; /// Form control (BIFF8).
+const sal_uInt16 EXC_OBJ_PIC_CTLSSTREAM = 0x0020; /// Data in Ctls stream (BIFF8).
+const sal_uInt16 EXC_OBJ_PIC_AUTOLOAD = 0x0200; /// Auto-load form control (BIFF8).
+
+// BIFF5 button objects
+const sal_uInt16 EXC_OBJ_BUTTON_DEFAULT = 0x0001;
+const sal_uInt16 EXC_OBJ_BUTTON_HELP = 0x0002;
+const sal_uInt16 EXC_OBJ_BUTTON_CANCEL = 0x0004;
+const sal_uInt16 EXC_OBJ_BUTTON_CLOSE = 0x0008;
+
+// BIFF5 checkboxes, radio buttons
+const sal_uInt16 EXC_OBJ_CHECKBOX_UNCHECKED = 0;
+const sal_uInt16 EXC_OBJ_CHECKBOX_CHECKED = 1;
+const sal_uInt16 EXC_OBJ_CHECKBOX_TRISTATE = 2;
+const sal_uInt16 EXC_OBJ_CHECKBOX_FLAT = 0x0001;
+
+// BIFF5 editbox objects
+const sal_uInt16 EXC_OBJ_EDIT_TEXT = 0;
+const sal_uInt16 EXC_OBJ_EDIT_INTEGER = 1;
+const sal_uInt16 EXC_OBJ_EDIT_DOUBLE = 2;
+const sal_uInt16 EXC_OBJ_EDIT_REFERENCE = 3;
+const sal_uInt16 EXC_OBJ_EDIT_FORMULA = 4;
+
+// BIFF5 scrollbars/spinbuttons
+const sal_uInt16 EXC_OBJ_SCROLLBAR_MIN = 0;
+const sal_uInt16 EXC_OBJ_SCROLLBAR_MAX = 30000;
+
+const sal_uInt16 EXC_OBJ_SCROLLBAR_HOR = 0x0001;
+
+const sal_uInt16 EXC_OBJ_SCROLLBAR_DEFFLAGS = 0x0001;
+const sal_uInt16 EXC_OBJ_SCROLLBAR_FLAT = 0x0008;
+
+// BIFF5 listboxes/dropdowns
+const sal_uInt8 EXC_OBJ_LISTBOX_SINGLE = 0; /// Single selection.
+const sal_uInt8 EXC_OBJ_LISTBOX_MULTI = 1; /// Multi selection.
+const sal_uInt8 EXC_OBJ_LISTBOX_RANGE = 2; /// Range selection.
+
+const sal_uInt16 EXC_OBJ_LISTBOX_EDIT = 0x0002;
+const sal_uInt16 EXC_OBJ_LISTBOX_FLAT = 0x0008;
+
+// BIFF5 dropdown listboxes
+const sal_uInt16 EXC_OBJ_DROPDOWN_LISTBOX = 0; /// Listbox, text not editable.
+const sal_uInt16 EXC_OBJ_DROPDOWN_COMBOBOX = 1; /// Dropdown listbox with editable text.
+const sal_uInt16 EXC_OBJ_DROPDOWN_SIMPLE = 2; /// Dropdown button only, no text area.
+const sal_uInt16 EXC_OBJ_DROPDOWN_MAX = 3;
+const sal_uInt16 EXC_OBJ_DROPDOWN_FILTERED = 0x0008; /// Dropdown style: filtered.
+
+// BIFF5 groupboxes
+const sal_uInt16 EXC_OBJ_GROUPBOX_FLAT = 0x0001;
+
+// BIFF8 sub records
+const sal_uInt16 EXC_ID_OBJEND = 0x0000; /// End of OBJ.
+const sal_uInt16 EXC_ID_OBJMACRO = 0x0004; /// Macro link.
+const sal_uInt16 EXC_ID_OBJBUTTON = 0x0005; /// Button data.
+const sal_uInt16 EXC_ID_OBJGMO = 0x0006; /// Group marker.
+const sal_uInt16 EXC_ID_OBJCF = 0x0007; /// Clipboard format.
+const sal_uInt16 EXC_ID_OBJFLAGS = 0x0008; /// Option flags.
+const sal_uInt16 EXC_ID_OBJPICTFMLA = 0x0009; /// OLE link formula.
+const sal_uInt16 EXC_ID_OBJCBLS = 0x000A; /// Check box/radio button data.
+const sal_uInt16 EXC_ID_OBJRBO = 0x000B; /// Radio button group data.
+const sal_uInt16 EXC_ID_OBJSBS = 0x000C; /// Scroll bar data.
+const sal_uInt16 EXC_ID_OBJNTS = 0x000D; /// Note data.
+const sal_uInt16 EXC_ID_OBJSBSFMLA = 0x000E; /// Scroll bar/list box/combo box cell link.
+const sal_uInt16 EXC_ID_OBJGBODATA = 0x000F; /// Group box data.
+const sal_uInt16 EXC_ID_OBJEDODATA = 0x0010; /// Edit box data.
+const sal_uInt16 EXC_ID_OBJRBODATA = 0x0011; /// Radio button group data.
+const sal_uInt16 EXC_ID_OBJCBLSDATA = 0x0012; /// Check box/radio button data.
+const sal_uInt16 EXC_ID_OBJLBSDATA = 0x0013; /// List box/combo box data.
+const sal_uInt16 EXC_ID_OBJCBLSFMLA = 0x0014; /// Check box/radio button cell link.
+const sal_uInt16 EXC_ID_OBJCMO = 0x0015; /// Common object settings.
+const sal_uInt16 EXC_ID_OBJUNKNOWN = 0xFFFF; /// For internal use only.
+
+// BIFF8 OBJCMO: flags
+const sal_uInt16 EXC_OBJCMO_PRINTABLE = 0x0010; /// Object printable.
+const sal_uInt16 EXC_OBJCMO_AUTOLINE = 0x2000; /// Automatic line formatting.
+const sal_uInt16 EXC_OBJCMO_AUTOFILL = 0x4000; /// Automatic fill formatting.
+
+/** Value binding mode for cells linked to form controls. */
+enum XclCtrlBindMode
+{
+ EXC_CTRL_BINDCONTENT, /// Binds cell to content of control.
+ EXC_CTRL_BINDPOSITION /// Binds cell to position in control (e.g. listbox selection index).
+};
+
+// (0x007F) IMGDATA -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID3_IMGDATA = 0x007F;
+const sal_uInt16 EXC_ID8_IMGDATA = 0x00E9;
+
+const sal_uInt16 EXC_IMGDATA_WMF = 2;
+const sal_uInt16 EXC_IMGDATA_BMP = 9;
+
+const sal_uInt16 EXC_IMGDATA_WIN = 1;
+const sal_uInt16 EXC_IMGDATA_MAC = 2;
+
+const sal_uInt32 EXC_IMGDATA_MAXREC8 = 0x201C;
+const sal_uInt32 EXC_IMGDATA_MAXCONT8 = 0x2014;
+
+// (0x00A9) COORDLIST ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_COORDLIST = 0x00A9;
+
+// (0x00EB) MSODRAWINGGROUP ---------------------------------------------------
+
+const sal_uInt16 EXC_ID_MSODRAWINGGROUP = 0x00EB;
+
+// (0x00EC) MSODRAWING --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_MSODRAWING = 0x00EC;
+
+// additional flags not extant in svx headers
+const sal_uInt16 EXC_ESC_ANCHOR_POSLOCKED = 0x0001;
+const sal_uInt16 EXC_ESC_ANCHOR_SIZELOCKED = 0x0002;
+const sal_uInt16 EXC_ESC_ANCHOR_LOCKED = EXC_ESC_ANCHOR_POSLOCKED|EXC_ESC_ANCHOR_SIZELOCKED;
+
+// (0x00ED) MSODRAWINGSELECTION -----------------------------------------------
+
+const sal_uInt16 EXC_ID_MSODRAWINGSEL = 0x00ED;
+
+// (0x01B6) TXO ---------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_TXO = 0x01B6;
+
+// TXO constants are equal to BIFF5 OBJ text object flags
+
+// Structs and classes ========================================================
+
+/** Represents the position (anchor) of an object in a Calc document. */
+struct XclObjAnchor : public XclRange
+{
+ sal_uInt16 mnLX; /// X offset in left column (1/1024 of column width).
+ sal_uInt32 mnTY; /// Y offset in top row (1/256 of row height).
+ sal_uInt16 mnRX; /// X offset in right column (1/1024 of column width).
+ sal_uInt32 mnBY; /// Y offset in bottom row (1/256 of row height).
+
+ explicit XclObjAnchor();
+
+ /** Calculates a rectangle from the contained coordinates. */
+ tools::Rectangle GetRect( const XclRoot& rRoot, SCTAB nScTab, MapUnit eMapUnit ) const;
+ /** Initializes the anchor coordinates for a sheet. */
+ void SetRect( const XclRoot& rRoot, SCTAB nScTab, const tools::Rectangle& rRect, MapUnit eMapUnit );
+
+ /** Initializes the anchor coordinates for an embedded draw page. */
+ void SetRect( const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY,
+ const tools::Rectangle& rRect, MapUnit eMapUnit );
+};
+
+inline SvStream& operator>>( SvStream& rStrm, XclObjAnchor& rAnchor )
+{
+ sal_uInt16 tmpFirstRow, tmpTY, tmpLastRow, tmpBY;
+
+ rStrm
+ .ReadUInt16( rAnchor.maFirst.mnCol ).ReadUInt16( rAnchor.mnLX )
+ .ReadUInt16( tmpFirstRow ).ReadUInt16( tmpTY )
+ .ReadUInt16( rAnchor.maLast.mnCol ).ReadUInt16( rAnchor.mnRX )
+ .ReadUInt16( tmpLastRow ).ReadUInt16( tmpBY );
+
+ rAnchor.maFirst.mnRow = static_cast<sal_uInt32> (tmpFirstRow);
+ rAnchor.mnTY = static_cast<sal_uInt32> (tmpTY);
+ rAnchor.maLast.mnRow = static_cast<sal_uInt32> (tmpLastRow);
+ rAnchor.mnBY = static_cast<sal_uInt32> (tmpBY);
+
+ return rStrm;
+}
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclObjAnchor& rAnchor )
+{
+ sal_uInt16 tmpFirstRow, tmpTY, tmpLastRow, tmpBY;
+
+ rAnchor.maFirst.mnCol = rStrm.ReaduInt16();
+ rAnchor.mnLX = rStrm.ReaduInt16();
+ tmpFirstRow = rStrm.ReaduInt16();
+ tmpTY = rStrm.ReaduInt16();
+ rAnchor.maLast.mnCol = rStrm.ReaduInt16();
+ rAnchor.mnRX = rStrm.ReaduInt16();
+ tmpLastRow = rStrm.ReaduInt16();
+ tmpBY = rStrm.ReaduInt16();
+
+ rAnchor.maFirst.mnRow = static_cast<sal_uInt32> (tmpFirstRow);
+ rAnchor.mnTY = static_cast<sal_uInt32> (tmpTY);
+ rAnchor.maLast.mnRow = static_cast<sal_uInt32> (tmpLastRow);
+ rAnchor.mnBY = static_cast<sal_uInt32> (tmpBY);
+
+ return rStrm;
+}
+
+inline SvStream& WriteXclObjAnchor( SvStream& rStrm, const XclObjAnchor& rAnchor )
+{
+ return rStrm
+ .WriteUInt16( rAnchor.maFirst.mnCol ).WriteUInt16( rAnchor.mnLX )
+ .WriteUInt16( rAnchor.maFirst.mnRow ).WriteUInt16( rAnchor.mnTY )
+ .WriteUInt16( rAnchor.maLast.mnCol ).WriteUInt16( rAnchor.mnRX )
+ .WriteUInt16( rAnchor.maLast.mnRow).WriteUInt16(rAnchor.mnBY);
+}
+
+struct XclObjLineData
+{
+ sal_uInt8 mnColorIdx;
+ sal_uInt8 mnStyle;
+ sal_uInt8 mnWidth;
+ sal_uInt8 mnAuto;
+
+ explicit XclObjLineData();
+
+ bool IsAuto() const { return ::get_flag( mnAuto, EXC_OBJ_LINE_AUTO ); }
+ bool IsVisible() const { return IsAuto() || (mnStyle != EXC_OBJ_LINE_NONE); }
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjLineData& rLineData );
+
+struct XclObjFillData
+{
+ sal_uInt8 mnBackColorIdx;
+ sal_uInt8 mnPattColorIdx;
+ sal_uInt8 mnPattern;
+ sal_uInt8 mnAuto;
+
+ explicit XclObjFillData();
+
+ bool IsAuto() const { return ::get_flag( mnAuto, EXC_OBJ_FILL_AUTO ); }
+ bool IsFilled() const { return IsAuto() || (mnPattern != EXC_PATT_NONE); }
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjFillData& rFillData );
+
+struct XclObjTextData
+{
+ sal_uInt16 mnTextLen;
+ sal_uInt16 mnFormatSize;
+ sal_uInt16 mnLinkSize;
+ sal_uInt16 mnDefFontIdx;
+ sal_uInt16 mnFlags;
+ sal_uInt16 mnOrient;
+ sal_uInt16 mnButtonFlags;
+ sal_uInt16 mnShortcut;
+ sal_uInt16 mnShortcutEA;
+
+ explicit XclObjTextData();
+
+ /** Reads text data from a BIFF3/BIFF4 OBJ record. */
+ void ReadObj3( XclImpStream& rStrm );
+ /** Reads text data from a BIFF5 OBJ record. */
+ void ReadObj5( XclImpStream& rStrm );
+ /** Reads text data from a BIFF8 TXO record. */
+ void ReadTxo8( XclImpStream& rStrm );
+
+ sal_uInt8 GetHorAlign() const { return ::extract_value< sal_uInt8 >( mnFlags, 1, 3 ); }
+ sal_uInt8 GetVerAlign() const { return ::extract_value< sal_uInt8 >( mnFlags, 4, 3 ); }
+};
+
+enum XclTbxEventType
+{
+ EXC_TBX_EVENT_ACTION, /// XActionListener.actionPerformed
+ EXC_TBX_EVENT_MOUSE, /// XMouseListener.mouseReleased
+ EXC_TBX_EVENT_TEXT, /// XTextListener.textChanged
+ EXC_TBX_EVENT_VALUE, /// XAdjustmentListener.adjustmentValueChanged
+ EXC_TBX_EVENT_CHANGE /// XChangeListener.changed
+};
+
+/** Provides static helper functions for form controls. */
+class XclControlHelper
+{
+public:
+ /** Returns the API control model from the passed API shape object. */
+ static css::uno::Reference< css::awt::XControlModel >
+ GetControlModel( css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ /** Fills the macro descriptor according to the passed macro name. */
+ static bool FillMacroDescriptor(
+ css::script::ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType,
+ const OUString& rXclMacroName,
+ SfxObjectShell* pDocShell );
+ /** Tries to extract an Excel macro name from the passed macro descriptor. */
+ static OUString ExtractFromMacroDescriptor(
+ const css::script::ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlformula.hxx b/sc/source/filter/inc/xlformula.hxx
new file mode 100644
index 0000000000..43f220bd64
--- /dev/null
+++ b/sc/source/filter/inc/xlformula.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 .
+ */
+
+#pragma once
+
+#include <osl/diagnose.h>
+#include <formula/opcode.hxx>
+#include <address.hxx>
+#include "ftools.hxx"
+#include <map>
+#include <memory>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+// Constants ==================================================================
+
+const size_t EXC_TOKARR_MAXLEN = 4096; /// Maximum size of a token array.
+
+// Token class flags ----------------------------------------------------------
+
+const sal_uInt8 EXC_TOKCLASS_MASK = 0x60;
+const sal_uInt8 EXC_TOKCLASS_NONE = 0x00; /// 00-1F: Base tokens.
+const sal_uInt8 EXC_TOKCLASS_REF = 0x20; /// 20-3F: Reference class tokens.
+const sal_uInt8 EXC_TOKCLASS_VAL = 0x40; /// 40-5F: Value class tokens.
+const sal_uInt8 EXC_TOKCLASS_ARR = 0x60; /// 60-7F: Array class tokens.
+
+// Base tokens ----------------------------------------------------------------
+
+const sal_uInt8 EXC_TOKID_MASK = 0x1F;
+
+const sal_uInt8 EXC_TOKID_NONE = 0x00; /// Placeholder for invalid token id.
+const sal_uInt8 EXC_TOKID_EXP = 0x01; /// Array or shared formula reference.
+const sal_uInt8 EXC_TOKID_TBL = 0x02; /// Multiple operation reference.
+const sal_uInt8 EXC_TOKID_ADD = 0x03; /// Addition operator.
+const sal_uInt8 EXC_TOKID_SUB = 0x04; /// Subtraction operator.
+const sal_uInt8 EXC_TOKID_MUL = 0x05; /// Multiplication operator.
+const sal_uInt8 EXC_TOKID_DIV = 0x06; /// Division operator.
+const sal_uInt8 EXC_TOKID_POWER = 0x07; /// Power operator.
+const sal_uInt8 EXC_TOKID_CONCAT = 0x08; /// String concatenation operator.
+const sal_uInt8 EXC_TOKID_LT = 0x09; /// Less than operator.
+const sal_uInt8 EXC_TOKID_LE = 0x0A; /// Less than or equal operator.
+const sal_uInt8 EXC_TOKID_EQ = 0x0B; /// Equal operator.
+const sal_uInt8 EXC_TOKID_GE = 0x0C; /// Greater than or equal operator.
+const sal_uInt8 EXC_TOKID_GT = 0x0D; /// Greater than operator.
+const sal_uInt8 EXC_TOKID_NE = 0x0E; /// Not equal operator.
+const sal_uInt8 EXC_TOKID_ISECT = 0x0F; /// Intersection operator.
+const sal_uInt8 EXC_TOKID_LIST = 0x10; /// List operator.
+const sal_uInt8 EXC_TOKID_RANGE = 0x11; /// Range operator.
+const sal_uInt8 EXC_TOKID_UPLUS = 0x12; /// Unary plus.
+const sal_uInt8 EXC_TOKID_UMINUS = 0x13; /// Unary minus.
+const sal_uInt8 EXC_TOKID_PERCENT = 0x14; /// Percent sign.
+const sal_uInt8 EXC_TOKID_PAREN = 0x15; /// Parentheses.
+const sal_uInt8 EXC_TOKID_MISSARG = 0x16; /// Missing argument.
+const sal_uInt8 EXC_TOKID_STR = 0x17; /// String constant.
+const sal_uInt8 EXC_TOKID_NLR = 0x18; /// Natural language reference (NLR).
+const sal_uInt8 EXC_TOKID_ATTR = 0x19; /// Special attribute.
+const sal_uInt8 EXC_TOKID_SHEET = 0x1A; /// Start of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 EXC_TOKID_ENDSHEET = 0x1B; /// End of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 EXC_TOKID_ERR = 0x1C; /// Error constant.
+const sal_uInt8 EXC_TOKID_BOOL = 0x1D; /// Boolean constant.
+const sal_uInt8 EXC_TOKID_INT = 0x1E; /// Integer constant.
+const sal_uInt8 EXC_TOKID_NUM = 0x1F; /// Floating-point constant.
+
+// Base IDs of classified tokens ----------------------------------------------
+
+const sal_uInt8 EXC_TOKID_ARRAY = 0x00; /// Array constant.
+const sal_uInt8 EXC_TOKID_FUNC = 0x01; /// Function, fixed number of arguments.
+const sal_uInt8 EXC_TOKID_FUNCVAR = 0x02; /// Function, variable number of arguments.
+const sal_uInt8 EXC_TOKID_NAME = 0x03; /// Defined name.
+const sal_uInt8 EXC_TOKID_REF = 0x04; /// 2D cell reference.
+const sal_uInt8 EXC_TOKID_AREA = 0x05; /// 2D area reference.
+const sal_uInt8 EXC_TOKID_MEMAREA = 0x06; /// Constant reference subexpression.
+const sal_uInt8 EXC_TOKID_MEMERR = 0x07; /// Deleted reference subexpression.
+const sal_uInt8 EXC_TOKID_MEMNOMEM = 0x08; /// Constant reference subexpression without result.
+const sal_uInt8 EXC_TOKID_MEMFUNC = 0x09; /// Variable reference subexpression.
+const sal_uInt8 EXC_TOKID_REFERR = 0x0A; /// Deleted 2D cell reference.
+const sal_uInt8 EXC_TOKID_AREAERR = 0x0B; /// Deleted 2D area reference.
+const sal_uInt8 EXC_TOKID_REFN = 0x0C; /// Relative 2D cell reference (in names).
+const sal_uInt8 EXC_TOKID_AREAN = 0x0D; /// Relative 2D area reference (in names).
+const sal_uInt8 EXC_TOKID_MEMAREAN = 0x0E; /// Reference subexpression (in names).
+const sal_uInt8 EXC_TOKID_MEMNOMEMN = 0x0F; /// Reference subexpression (in names) without result.
+const sal_uInt8 EXC_TOKID_FUNCCE = 0x18;
+const sal_uInt8 EXC_TOKID_NAMEX = 0x19; /// External reference.
+const sal_uInt8 EXC_TOKID_REF3D = 0x1A; /// 3D cell reference.
+const sal_uInt8 EXC_TOKID_AREA3D = 0x1B; /// 3D area reference.
+const sal_uInt8 EXC_TOKID_REFERR3D = 0x1C; /// Deleted 3D cell reference.
+const sal_uInt8 EXC_TOKID_AREAERR3D = 0x1D; /// Deleted 3D area reference
+
+// specific token constants ---------------------------------------------------
+
+const sal_uInt16 EXC_TOK_STR_MAXLEN = 255; /// Maximum string length of a tStr token.
+
+const sal_uInt8 EXC_TOK_BOOL_FALSE = 0; /// sal_False value of a tBool token.
+const sal_uInt8 EXC_TOK_BOOL_TRUE = 1; /// sal_True value of a tBool token.
+
+const sal_uInt8 EXC_TOK_ATTR_VOLATILE = 0x01; /// Volatile function.
+const sal_uInt8 EXC_TOK_ATTR_IF = 0x02; /// Start of true condition in IF function.
+const sal_uInt8 EXC_TOK_ATTR_CHOOSE = 0x04; /// Jump array of CHOOSE function.
+const sal_uInt8 EXC_TOK_ATTR_GOTO = 0x08; /// Jump to token.
+const sal_uInt8 EXC_TOK_ATTR_SUM = 0x10; /// SUM function with one parameter.
+const sal_uInt8 EXC_TOK_ATTR_ASSIGN = 0x20; /// BASIC style assignment.
+const sal_uInt8 EXC_TOK_ATTR_SPACE = 0x40; /// Spaces in formula representation.
+
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP = 0x00; /// Spaces before next token.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_BR = 0x01; /// Line breaks before next token.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP_OPEN = 0x02; /// Spaces before opening parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_BR_OPEN = 0x03; /// Line breaks before opening parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP_CLOSE = 0x04; /// Spaces before closing parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_BR_CLOSE = 0x05; /// Line breaks before closing parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP_PRE = 0x06; /// Spaces before formula (BIFF3).
+
+const sal_uInt16 EXC_TOK_FUNCVAR_CMD = 0x8000; /// Macro command.
+const sal_uInt16 EXC_TOK_FUNCVAR_INDEXMASK = 0x7FFF; /// Mask for function/command index.
+const sal_uInt8 EXC_TOK_FUNCVAR_PROMPT = 0x80; /// User prompt for macro commands.
+const sal_uInt8 EXC_TOK_FUNCVAR_COUNTMASK = 0x7F; /// Mask for parameter count.
+
+const sal_uInt16 EXC_TOK_REF_COLREL = 0x4000; /// True = Column is relative.
+const sal_uInt16 EXC_TOK_REF_ROWREL = 0x8000; /// True = Row is relative.
+
+const sal_uInt8 EXC_TOK_NLR_ERR = 0x01; /// NLR: Invalid/deleted.
+const sal_uInt8 EXC_TOK_NLR_ROWR = 0x02; /// NLR: Row index.
+const sal_uInt8 EXC_TOK_NLR_COLR = 0x03; /// NLR: Column index.
+const sal_uInt8 EXC_TOK_NLR_ROWV = 0x06; /// NLR: Value in row.
+const sal_uInt8 EXC_TOK_NLR_COLV = 0x07; /// NLR: Value in column.
+const sal_uInt8 EXC_TOK_NLR_RANGE = 0x0A; /// NLR: Range.
+const sal_uInt8 EXC_TOK_NLR_SRANGE = 0x0B; /// Stacked NLR: Range.
+const sal_uInt8 EXC_TOK_NLR_SROWR = 0x0C; /// Stacked NLR: Row index.
+const sal_uInt8 EXC_TOK_NLR_SCOLR = 0x0D; /// Stacked NLR: Column index.
+const sal_uInt8 EXC_TOK_NLR_SROWV = 0x0E; /// Stacked NLR: Value in row.
+const sal_uInt8 EXC_TOK_NLR_SCOLV = 0x0F; /// Stacked NLR: Value in column.
+const sal_uInt8 EXC_TOK_NLR_RANGEERR = 0x10; /// NLR: Invalid/deleted range.
+const sal_uInt8 EXC_TOK_NLR_SXNAME = 0x1D; /// NLR: Pivot table name.
+const sal_uInt16 EXC_TOK_NLR_REL = 0x8000; /// True = Natural language ref is relative.
+
+const sal_uInt32 EXC_TOK_NLR_ADDREL = 0x80000000; /// NLR relative (in appended data).
+const sal_uInt32 EXC_TOK_NLR_ADDMASK = 0x3FFFFFFF; /// Mask for number of appended ranges.
+
+/** Type of a formula. */
+enum XclFormulaType
+{
+ EXC_FMLATYPE_CELL, /// Simple cell formula, also used in change tracking.
+ EXC_FMLATYPE_MATRIX, /// Matrix (array) formula.
+ EXC_FMLATYPE_SHARED, /// Shared formula.
+ EXC_FMLATYPE_CONDFMT, /// Conditional format.
+ EXC_FMLATYPE_DATAVAL, /// Data validation.
+ EXC_FMLATYPE_NAME, /// Defined name.
+ EXC_FMLATYPE_CHART, /// Chart source ranges.
+ EXC_FMLATYPE_CONTROL, /// Spreadsheet links in form controls.
+ EXC_FMLATYPE_WQUERY, /// Web query source range.
+ EXC_FMLATYPE_LISTVAL /// List (cell range) validation.
+};
+
+// Function parameter info ====================================================
+
+/** Enumerates validity modes for a function parameter. */
+enum XclFuncParamValidity
+{
+ EXC_PARAM_NONE = 0, /// Default for an unspecified entry in a C-array.
+ EXC_PARAM_REGULAR, /// Parameter supported by Calc and Excel.
+ EXC_PARAM_CALCONLY, /// Parameter supported by Calc only.
+ EXC_PARAM_EXCELONLY /// Parameter supported by Excel only.
+};
+
+/** Enumerates different types of token class conversion in function parameters. */
+enum XclFuncParamConv
+{
+ EXC_PARAMCONV_ORG, /// Use original class of current token.
+ EXC_PARAMCONV_VAL, /// Convert tokens to VAL class.
+ EXC_PARAMCONV_ARR, /// Convert tokens to ARR class.
+ EXC_PARAMCONV_RPT, /// Repeat parent conversion in VALTYPE parameters.
+ EXC_PARAMCONV_RPX, /// Repeat parent conversion in REFTYPE parameters.
+ EXC_PARAMCONV_RPO /// Repeat parent conversion in operands of operators.
+};
+
+/** Structure that contains all needed information for a parameter in a
+ function.
+
+ The member meValid specifies which application supports the parameter. If
+ set to CALCONLY, import filters have to insert a default value for this
+ parameter, and export filters have to skip the parameter. If set to
+ EXCELONLY, import filters have to skip the parameter, and export filters
+ have to insert a default value for this parameter.
+
+ The member mbValType specifies whether the parameter requires tokens to be
+ of value type (VAL or ARR class).
+
+ If set to false, the parameter is called to be REFTYPE. Tokens with REF
+ default class can be inserted for the parameter (e.g. tAreaR tokens).
+
+ If set to true, the parameter is called to be VALTYPE. Tokens with REF
+ class need to be converted to VAL tokens first (e.g. tAreaR will be
+ converted to tAreaV), and further conversion is done according to this
+ new token class.
+
+ The member meConv specifies how to convert the current token class of the
+ token inserted for the parameter. If the token class is still REF this
+ means that the token has default REF class and the parameter is REFTYPE
+ (see member mbValType), the token will not be converted at all and remains
+ in REF class. Otherwise, token class conversion is depending on the actual
+ token class of the return value of the function containing this parameter.
+ The function may return REF class (tFuncR, tFuncVarR, tFuncCER), or it may
+ return VAL or ARR class (tFuncV, tFuncA, tFuncVarV, tFuncVarA, tFuncCEV,
+ tFuncCEA). Even if the function is able to return REF class, it may return
+ VAL or ARR class instead due to the VALTYPE data type of the parent
+ function parameter that calls the own function. Example: The INDIRECT
+ function returns REF class by default. But if called from a VALTYPE
+ function parameter, e.g. in the formula =ABS(INDIRECT("A1")), it returns
+ VAL or ARR class instead. Additionally, the repeating conversion types RPT
+ and RPX rely on the conversion executed for the function token class.
+
+ 1) ORG:
+ Use the original class of the token (VAL or ARR), regardless of any
+ conversion done for the function return class.
+
+ 2) VAL:
+ Convert ARR tokens to VAL class, regardless of any conversion done for
+ the function return class.
+
+ 3) ARR:
+ Convert VAL tokens to ARR class, regardless of any conversion done for
+ the function return class.
+
+ 4) RPT:
+ If the own function returns REF class (thus it is called from a REFTYPE
+ parameter, see above), and the parent conversion type (for the function
+ return class) was ORG, VAL, or ARR, ignore that conversion and always
+ use VAL conversion for the own token instead. If the parent conversion
+ type was RPT or RPX, repeat the conversion that would have been used if
+ the function would return value type.
+ If the own function returns value type (VAL or ARR class, see above),
+ and the parent conversion type (for the function return class) was ORG,
+ VAL, ARR, or RPT, repeat this conversion for the own token. If the
+ parent conversion type was RPX, always use ORG conversion type for the
+ own token instead.
+
+ 5) RPX:
+ This type of conversion only occurs in functions returning VAL class by
+ default. If the own token is value type, and the VAL return class of
+ the own function has been changed to ARR class (due to direct ARR
+ conversion, or due to ARR conversion repeated by RPT or RPX), set the
+ own token to ARR type. Otherwise use the original token type (VAL
+ conversion from parent parameter will not be repeated at all). If
+ nested functions have RPT or value-type RPX parameters, they will not
+ repeat this conversion type, but will use ORG conversion instead (see
+ description of RPT above).
+
+ 6) RPO:
+ This type of conversion is only used for the operands of all operators
+ (unary and binary arithmetic operators, comparison operators, and range
+ operators). It is not used for function parameters. On conversion, it
+ will be replaced by the last conversion type that was not the RPO
+ conversion. This leads to a slightly different behaviour than the RPT
+ conversion for operands in conjunction with a parent RPX conversion.
+ */
+struct XclFuncParamInfo
+{
+ XclFuncParamValidity meValid; /// Parameter validity.
+ XclFuncParamConv meConv; /// Token class conversion type.
+ bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE).
+};
+
+// Function data ==============================================================
+
+const sal_uInt8 EXC_FUNC_MAXPARAM = 30; /// Maximum parameter count.
+
+const size_t EXC_FUNCINFO_PARAMINFO_COUNT = 5; /// Number of parameter info entries.
+
+const sal_uInt8 EXC_FUNCFLAG_VOLATILE = 0x01; /// Result is volatile (e.g. NOW() function).
+const sal_uInt8 EXC_FUNCFLAG_IMPORTONLY = 0x02; /// Only used in import filter.
+const sal_uInt8 EXC_FUNCFLAG_EXPORTONLY = 0x04; /// Only used in export filter.
+const sal_uInt8 EXC_FUNCFLAG_PARAMPAIRS = 0x08; /// Optional parameters are expected to appear in pairs.
+const sal_uInt8 EXC_FUNCFLAG_ADDINEQUIV = 0x10; /// Function is an add-in equivalent
+
+// selected function IDs
+const sal_uInt16 EXC_FUNCID_IF = 1;
+const sal_uInt16 EXC_FUNCID_SUM = 4;
+const sal_uInt16 EXC_FUNCID_AND = 36;
+const sal_uInt16 EXC_FUNCID_OR = 37;
+const sal_uInt16 EXC_FUNCID_CHOOSE = 100;
+const sal_uInt16 EXC_FUNCID_EXTERNCALL = 255;
+
+/** Represents information for a spreadsheet function for import and export.
+
+ The member mpParamInfos points to an array of type information structures
+ for all parameters of the function. The last initialized structure
+ describing a regular parameter (member meValid == EXC_PARAMVALID_ALWAYS) in
+ this array is used repeatedly for all following parameters supported by a
+ function.
+ */
+struct XclFunctionInfo
+{
+ OpCode meOpCode; /// Calc function opcode.
+ sal_uInt16 mnXclFunc; /// Excel function index.
+ sal_uInt8 mnMinParamCount; /// Minimum number of parameters.
+ sal_uInt8 mnMaxParamCount; /// Maximum number of parameters.
+ sal_uInt8 mnRetClass; /// Token class of the return value.
+ XclFuncParamInfo mpParamInfos[ EXC_FUNCINFO_PARAMINFO_COUNT ]; /// Information for all parameters.
+ sal_uInt8 mnFlags; /// Additional flags (EXC_FUNCFLAG_* constants).
+ const char* mpcMacroName; /** Function name, if simulated by
+ a macro call (UTF-8) EXC_FUNCFLAG_ADDINEQUIV is 0;
+ or programmatical add-in name
+ if stored as such and
+ EXC_FUNCFLAG_ADDINEQUIV is set. */
+
+ /** Returns true, if the function is volatile. */
+ bool IsVolatile() const { return ::get_flag( mnFlags, EXC_FUNCFLAG_VOLATILE ); }
+ /** Returns true, if optional parameters are expected to appear in pairs. */
+ bool IsParamPairs() const { return ::get_flag( mnFlags, EXC_FUNCFLAG_PARAMPAIRS ); }
+ /** Returns true, if the function parameter count is fixed. */
+ bool IsFixedParamCount() const { return (mnXclFunc != EXC_FUNCID_EXTERNCALL) && (mnMinParamCount == mnMaxParamCount); }
+ /** Returns true, if the function is simulated by a macro call. */
+ bool IsMacroFunc() const { return mpcMacroName != nullptr && !(mnFlags & EXC_FUNCFLAG_ADDINEQUIV); }
+ /** Returns true, if the function is stored as an add-in call. */
+ bool IsAddInEquivalent() const { return mpcMacroName != nullptr && (mnFlags & EXC_FUNCFLAG_ADDINEQUIV); }
+ /** Returns the name of the external function as string. */
+ OUString GetMacroFuncName() const;
+ /** Returns the programmatical name of the Add-In function as string. */
+ OUString GetAddInEquivalentFuncName() const;
+};
+
+class XclRoot;
+
+/** Provides access to function info structs for all available functions. */
+class XclFunctionProvider
+{
+public:
+ explicit XclFunctionProvider( const XclRoot& rRoot );
+
+ /** Returns the function data for an Excel function index, or 0 on error. */
+ const XclFunctionInfo* GetFuncInfoFromXclFunc( sal_uInt16 nXclFunc ) const;
+ /** Returns the function data for an Excel function simulated by a macro call, or 0 on error. */
+ const XclFunctionInfo* GetFuncInfoFromXclMacroName( const OUString& rXclMacroName ) const;
+ /** Returns the function data for a Calc opcode, or 0 on error. */
+ const XclFunctionInfo* GetFuncInfoFromOpCode( OpCode eOpCode ) const;
+
+private:
+ void FillXclFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd );
+ void FillScFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd );
+
+private:
+ typedef ::std::map< sal_uInt16, const XclFunctionInfo* > XclFuncMap;
+ typedef ::std::map< OUString, const XclFunctionInfo* > XclMacroNameMap;
+ typedef ::std::map< OpCode, const XclFunctionInfo* > ScFuncMap;
+
+ XclFuncMap maXclFuncMap; /// Maps Excel function indexes to function data.
+ XclMacroNameMap maXclMacroNameMap; /// Maps macro function names to function data.
+ ScFuncMap maScFuncMap; /// Maps Calc opcodes to function data.
+};
+
+// Token array ================================================================
+
+class XclImpStream;
+class XclExpStream;
+
+/** Binary representation of an Excel token array. */
+class XclTokenArray
+{
+public:
+ /** Creates an empty token array. */
+ explicit XclTokenArray( bool bVolatile = false );
+ /** Creates a token array, swaps passed token vectors into own data. */
+ explicit XclTokenArray( ScfUInt8Vec& rTokVec, ScfUInt8Vec& rExtDataVec, bool bVolatile = false );
+
+ /** Returns true, if the token array is empty. */
+ bool Empty() const { return maTokVec.empty(); }
+ /** Returns the size of the token array in bytes. */
+ sal_uInt16 GetSize() const;
+ /** Returns read-only access to the byte vector storing token data. */
+ const sal_uInt8* GetData() const { return maTokVec.empty() ? nullptr : maTokVec.data(); }
+ /** Returns true, if the formula contains a volatile function. */
+ bool IsVolatile() const { return mbVolatile; }
+
+ /** Reads the size field of the token array. */
+ static sal_uInt16 ReadSize(XclImpStream& rStrm);
+ /** Reads the tokens of the token array (without size field). */
+ void ReadArray(sal_uInt16 nSize, XclImpStream& rStrm);
+ /** Reads size field and the tokens. */
+ void Read( XclImpStream& rStrm );
+
+ /** Writes the size field of the token array. */
+ void WriteSize( XclExpStream& rStrm ) const;
+ /** Writes the tokens of the token array (without size field). */
+ void WriteArray( XclExpStream& rStrm ) const;
+ /** Writes size field and the tokens. */
+ void Write( XclExpStream& rStrm ) const;
+
+ /** Compares this token array with the passed. */
+ bool operator==( const XclTokenArray& rTokArr ) const;
+
+private:
+ ScfUInt8Vec maTokVec; /// Byte vector containing token data.
+ ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs).
+ bool mbVolatile; /// True = Formula contains volatile function.
+};
+
+typedef std::shared_ptr< XclTokenArray > XclTokenArrayRef;
+
+/** Calls the Read() function at the passed token array. */
+XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArray& rTokArr );
+/** Calls the Write() function at the passed token array. */
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArray& rTokArr );
+/** Calls the Write() function at the passed token array. */
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArrayRef& rxTokArr );
+
+namespace formula { class FormulaToken; }
+class ScTokenArray;
+
+/** Special token array iterator for the Excel filters.
+
+ Iterates over a Calc token array without modifying it (therefore the
+ iterator can be used with constant token arrays).
+
+ Usage: Construct a new iterator object and pass a Calc token array, or use
+ the Init() function to assign another Calc token array. As long as the Is()
+ function returns true, the accessor functions can be used to get the
+ current Calc token.
+ */
+class XclTokenArrayIterator
+{
+public:
+ explicit XclTokenArrayIterator();
+ explicit XclTokenArrayIterator( const ScTokenArray& rScTokArr, bool bSkipSpaces );
+ /** Copy constructor that allows to change the skip-spaces mode. */
+ explicit XclTokenArrayIterator( const XclTokenArrayIterator& rTokArrIt, bool bSkipSpaces );
+
+ void Init( const ScTokenArray& rScTokArr, bool bSkipSpaces );
+
+ bool Is() const { return mppScToken != nullptr; }
+ bool operator!() const { return !Is(); }
+ const ::formula::FormulaToken* Get() const { return mppScToken ? *mppScToken : nullptr; }
+ const ::formula::FormulaToken* operator->() const { return Get(); }
+ const ::formula::FormulaToken& operator*() const { return *Get(); }
+
+ XclTokenArrayIterator& operator++();
+
+private:
+ void NextRawToken();
+ void SkipSpaces();
+
+private:
+ const ::formula::FormulaToken*const* mppScTokenBeg; /// Pointer to first token pointer of token array.
+ const ::formula::FormulaToken*const* mppScTokenEnd; /// Pointer behind last token pointer of token array.
+ const ::formula::FormulaToken*const* mppScToken; /// Pointer to current token pointer of token array.
+ bool mbSkipSpaces; /// true = Skip whitespace tokens.
+};
+
+/** Contains all cell references that can be extracted from a multiple operations formula. */
+struct XclMultipleOpRefs
+{
+ ScAddress maFmlaScPos; /// Position of the (first) formula cell.
+ ScAddress maColFirstScPos;
+ ScAddress maColRelScPos;
+ ScAddress maRowFirstScPos;
+ ScAddress maRowRelScPos;
+ bool mbDblRefMode; /// true = One formula with row and column values.
+};
+
+/** A helper with Excel specific token array functions.
+
+ The purpose to not add these functions to ScTokenArray is to prevent code
+ changes in low-level Calc headers and to keep the Excel specific source
+ code in the filter directory. Deriving from ScTokenArray is not viable
+ because that would need expensive copy-constructions of the token arrays.
+ */
+class XclTokenArrayHelper
+{
+public:
+ // token identifiers ------------------------------------------------------
+
+ /** Returns the classified token ID from a base ID and the token class. */
+ inline static sal_uInt8 GetTokenId( sal_uInt8 nBaseId, sal_uInt8 nTokenClass );
+
+ /** Returns the token class of the passed token ID. */
+ static sal_uInt8 GetTokenClass( sal_uInt8 nTokenId ) { return nTokenId & EXC_TOKCLASS_MASK; }
+ /** Changes the token class in the passed classified token ID. */
+ inline static void ChangeTokenClass( sal_uInt8& rnTokenId, sal_uInt8 nTokenClass );
+
+ // strings and string lists -----------------------------------------------
+
+ /** Tries to extract a string from the passed token.
+ @param rString (out-parameter) The string contained in the token.
+ @return true = Passed token is a string token, rString parameter is valid. */
+ static bool GetTokenString( OUString& rString, const ::formula::FormulaToken& rScToken );
+
+ /** Parses the passed formula and tries to find a single string token, i.e. "abc".
+ @param rString (out-parameter) The string contained in the formula.
+ @return true = String token found, rString parameter is valid. */
+ static bool GetString( OUString& rString, const ScTokenArray& rScTokArr );
+
+ /** Parses the passed formula and tries to find a string token list, i.e. "abc";"def";"ghi".
+ @descr Returns the unquoted (!) strings in a single string, separated with the
+ passed character. If a comma is specified, the function will return abc,def,ghi from
+ the example above.
+ @param rStringList (out-parameter) All strings contained in the formula as list.
+ @param cSep List separator character.
+ @return true = String token list found, rString parameter is valid. */
+ static bool GetStringList( OUString& rStringList, const ScTokenArray& rScTokArr, sal_Unicode cSep );
+
+ /** Tries to convert a formula that consists of a single string token to a list of strings.
+ Removes leading spaces from each token.
+ @descr Example: The formula ="abc\ndef\nghi" will be converted to the formula
+ ="abc";"def";"ghi", if the LF character is specified as separator.
+ @param rScTokArr (in/out-parameter) The token array to modify.
+ @param cStringSep The separator in the source string. */
+ static void ConvertStringToList(
+ ScTokenArray& rScTokArr, svl::SharedStringPool& rSPool, sal_Unicode cStringSep );
+
+ // multiple operations ----------------------------------------------------
+
+ /** Parses the passed formula and tries to extract references of a multiple operation.
+ @descr Requires that the formula contains a single MULTIPLE.OPERATION function call.
+ Spaces in the formula are silently ignored.
+ @return true = Multiple operation found, and all references successfully extracted. */
+ static bool GetMultipleOpRefs( const ScDocument& rDoc, XclMultipleOpRefs& rRefs, const ScTokenArray& rScTokArr, const ScAddress& rScPos );
+};
+
+inline sal_uInt8 XclTokenArrayHelper::GetTokenId( sal_uInt8 nBaseId, sal_uInt8 nTokenClass )
+{
+ OSL_ENSURE( !::get_flag( nBaseId, static_cast< sal_uInt8 >( ~EXC_TOKID_MASK ) ), "XclTokenArrayHelper::GetTokenId - invalid token ID" );
+ OSL_ENSURE( !::get_flag( nTokenClass, static_cast< sal_uInt8 >( ~EXC_TOKCLASS_MASK ) ), "XclTokenArrayHelper::GetTokenId - invalid token class" );
+ return nBaseId | nTokenClass;
+}
+
+inline void XclTokenArrayHelper::ChangeTokenClass( sal_uInt8& rnTokenId, sal_uInt8 nTokenClass )
+{
+ OSL_ENSURE( !::get_flag( nTokenClass, static_cast< sal_uInt8 >( ~EXC_TOKCLASS_MASK ) ), "XclTokenArrayHelper::ChangeTokenClass - invalid token class" );
+ ::set_flag( rnTokenId, EXC_TOKCLASS_MASK, false );
+ ::set_flag( rnTokenId, nTokenClass );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xllink.hxx b/sc/source/filter/inc/xllink.hxx
new file mode 100644
index 0000000000..f87567d09f
--- /dev/null
+++ b/sc/source/filter/inc/xllink.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <sal/types.h>
+
+// Constants and Enumerations =================================================
+
+const sal_uInt16 EXC_TAB_EXTERNAL = 0xFFFE; /// Special sheet index for external links.
+const sal_uInt16 EXC_TAB_DELETED = 0xFFFF; /// Deleted sheet in a 3D reference.
+
+// (0x0016) EXTERNCOUNT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EXTERNCOUNT = 0x0016;
+
+// (0x0017) EXTERNSHEET -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EXTERNSHEET = 0x0017;
+
+const sal_Unicode EXC_EXTSH_URL = '\x01';
+const sal_Unicode EXC_EXTSH_OWNTAB = '\x02';
+const sal_Unicode EXC_EXTSH_TABNAME = '\x03';
+const sal_Unicode EXC_EXTSH_OWNDOC = '\x04';
+const sal_Unicode EXC_EXTSH_ADDIN = '\x3A';
+
+// (0x0023) EXTERNNAME --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EXTERNNAME = 0x0023;
+
+const sal_uInt16 EXC_EXTN_BUILTIN = 0x0001;
+const sal_uInt16 EXC_EXTN_OLE = 0x0010;
+const sal_uInt16 EXC_EXTN_OLE_OR_DDE = 0xFFFE;
+
+const sal_uInt16 EXC_EXTN_EXPDDE_STDDOC = 0x7FEA; /// for export
+const sal_uInt16 EXC_EXTN_EXPDDE = 0x7FE2; /// for export
+
+// (0x0059, 0x005A) XCT, CRN --------------------------------------------------
+
+const sal_uInt16 EXC_ID_XCT = 0x0059;
+const sal_uInt16 EXC_ID_CRN = 0x005A;
+
+// (0x013D) TABID -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_TABID = 0x013D;
+
+// (0x01AE) SUPBOOK -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SUPBOOK = 0x01AE;
+
+const sal_uInt16 EXC_SUPB_SELF = 0x0401;
+const sal_uInt16 EXC_SUPB_ADDIN = 0x3A01;
+
+/** This enumeration specifies the type of a SUPBOOK record. */
+enum class XclSupbookType
+{
+ Unknown, /// unknown SUPBOOK record type.
+ Self, /// SUPBOOK is used for internal references.
+ Extern, /// SUPBOOK is used for external references.
+ Addin, /// SUPBOOK contains add-in functions.
+ Special, /// SUPBOOK is used for DDE or OLE links.
+ Eurotool /// SUPBOOK is used for EUROCONVERT.
+};
+
+template< typename charT, typename traits >
+inline std::basic_ostream<charT, traits> & operator <<(
+ std::basic_ostream<charT, traits> & stream, const XclSupbookType& type )
+{
+ switch (type)
+ {
+ case XclSupbookType::Unknown: return stream << "unknown";
+ case XclSupbookType::Self: return stream << "self";
+ case XclSupbookType::Extern: return stream << "extern";
+ case XclSupbookType::Addin: return stream << "addin";
+ case XclSupbookType::Special: return stream << "special";
+ case XclSupbookType::Eurotool: return stream << "eurotool";
+ default: return stream << static_cast<int>(type);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlname.hxx b/sc/source/filter/inc/xlname.hxx
new file mode 100644
index 0000000000..f469919ecf
--- /dev/null
+++ b/sc/source/filter/inc/xlname.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+// Constants and Enumerations =================================================
+
+// (0x0018, 0x0218) NAME ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_NAME = 0x0018;
+const sal_uInt16 EXC_ID34_NAME = 0x0218;
+
+// flags
+const sal_uInt16 EXC_NAME_DEFAULT = 0x0000;
+const sal_uInt16 EXC_NAME_HIDDEN = 0x0001;
+const sal_uInt16 EXC_NAME_FUNC = 0x0002;
+const sal_uInt16 EXC_NAME_VB = 0x0004;
+const sal_uInt16 EXC_NAME_PROC = 0x0008;
+const sal_uInt16 EXC_NAME_CALCEXP = 0x0010;
+const sal_uInt16 EXC_NAME_BUILTIN = 0x0020;
+const sal_uInt16 EXC_NAME_FGROUPMASK = 0x0FC0;
+const sal_uInt16 EXC_NAME_BIG = 0x1000;
+
+const sal_uInt8 EXC_NAME2_FUNC = 0x02; /// BIFF2 function/command flag.
+
+const sal_uInt16 EXC_NAME_GLOBAL = 0; /// 0 = Globally defined name.
+
+// codes for built-in names
+const sal_Unicode EXC_BUILTIN_CONSOLIDATEAREA = '\x00';
+const sal_Unicode EXC_BUILTIN_AUTOOPEN = '\x01';
+const sal_Unicode EXC_BUILTIN_AUTOCLOSE = '\x02';
+const sal_Unicode EXC_BUILTIN_EXTRACT = '\x03';
+const sal_Unicode EXC_BUILTIN_DATABASE = '\x04';
+const sal_Unicode EXC_BUILTIN_CRITERIA = '\x05';
+const sal_Unicode EXC_BUILTIN_PRINTAREA = '\x06';
+const sal_Unicode EXC_BUILTIN_PRINTTITLES = '\x07';
+const sal_Unicode EXC_BUILTIN_RECORDER = '\x08';
+const sal_Unicode EXC_BUILTIN_DATAFORM = '\x09';
+const sal_Unicode EXC_BUILTIN_AUTOACTIVATE = '\x0A';
+const sal_Unicode EXC_BUILTIN_AUTODEACTIVATE = '\x0B';
+const sal_Unicode EXC_BUILTIN_SHEETTITLE = '\x0C';
+const sal_Unicode EXC_BUILTIN_FILTERDATABASE = '\x0D';
+const sal_Unicode EXC_BUILTIN_UNKNOWN = '\x0E';
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlpage.hxx b/sc/source/filter/inc/xlpage.hxx
new file mode 100644
index 0000000000..d200e8b804
--- /dev/null
+++ b/sc/source/filter/inc/xlpage.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include "ftools.hxx"
+#include <memory>
+
+// Constants and Enumerations =================================================
+
+// (0x0014, 0x0015) HEADER, FOOTER --------------------------------------------
+
+const sal_uInt16 EXC_ID_HEADER = 0x0014;
+const sal_uInt16 EXC_ID_FOOTER = 0x0015;
+
+// (0x0016, 0x0017) EVEN HEADER, EVEN FOOTER ----------------------------------
+
+const sal_uInt16 EXC_ID_HEADER_EVEN = 0x0016;
+const sal_uInt16 EXC_ID_FOOTER_EVEN = 0x0017;
+
+// (0x0018, 0x0019) FIRST HEADER, FIRST FOOTER ----------------------------------
+
+const sal_uInt16 EXC_ID_HEADER_FIRST = 0x0018;
+const sal_uInt16 EXC_ID_FOOTER_FIRST = 0x0019;
+
+// (0x001A, 0x001B) VERTICAL-, HORIZONTALPAGEBREAKS ---------------------------
+
+const sal_uInt16 EXC_ID_VERPAGEBREAKS = 0x001A;
+const sal_uInt16 EXC_ID_HORPAGEBREAKS = 0x001B;
+
+// (0x0026, 0x0027, 0x0028, 0x0029) LEFT-, RIGHT-, TOP-, BOTTOMMARGIN ---------
+
+const sal_uInt16 EXC_ID_LEFTMARGIN = 0x0026;
+const sal_uInt16 EXC_ID_RIGHTMARGIN = 0x0027;
+const sal_uInt16 EXC_ID_TOPMARGIN = 0x0028;
+const sal_uInt16 EXC_ID_BOTTOMMARGIN = 0x0029;
+
+const sal_Int32 EXC_MARGIN_DEFAULT_LR = 1900; /// Left/right default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_TB = 2500; /// Top/bottom default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_HF = 1300; /// Header/footer default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_HLR = 1900; /// Left/right header default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_FLR = 1900; /// Left/right footer default margin in 1/100mm.
+
+// (0x002A, 0x002B) PRINTHEADERS, PRINTGRIDLINES ------------------------------
+
+const sal_uInt16 EXC_ID_PRINTHEADERS = 0x002A;
+const sal_uInt16 EXC_ID_PRINTGRIDLINES = 0x002B;
+
+// (0x0033) PRINTSIZE ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_PRINTSIZE = 0x0033;
+
+const sal_uInt16 EXC_PRINTSIZE_SCREEN = 1;
+const sal_uInt16 EXC_PRINTSIZE_PAGE = 2;
+const sal_uInt16 EXC_PRINTSIZE_FULL = 3;
+
+// (0x0082, 0x0083, 0x0084) GRIDSET, HCENTER, VCENTER -------------------------
+
+const sal_uInt16 EXC_ID_GRIDSET = 0x0082;
+const sal_uInt16 EXC_ID_HCENTER = 0x0083;
+const sal_uInt16 EXC_ID_VCENTER = 0x0084;
+
+// (0x00A1) SETUP -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SETUP = 0x00A1;
+
+const sal_uInt16 EXC_SETUP_INROWS = 0x0001;
+const sal_uInt16 EXC_SETUP_PORTRAIT = 0x0002;
+const sal_uInt16 EXC_SETUP_INVALID = 0x0004;
+const sal_uInt16 EXC_SETUP_BLACKWHITE = 0x0008;
+const sal_uInt16 EXC_SETUP_DRAFT = 0x0010;
+const sal_uInt16 EXC_SETUP_PRINTNOTES = 0x0020;
+const sal_uInt16 EXC_SETUP_STARTPAGE = 0x0080;
+const sal_uInt16 EXC_SETUP_NOTES_END = 0x0200;
+
+const sal_uInt16 EXC_PAPERSIZE_DEFAULT = 0;
+const sal_uInt16 EXC_PAPERSIZE_USER = 0xFFFF;
+
+// Page settings ==============================================================
+
+class SvxBrushItem;
+
+/** Contains all page (print) settings for a single sheet. */
+struct XclPageData
+{
+ /** noncopyable */
+ XclPageData(const XclPageData&) = delete;
+ const XclPageData& operator=(const XclPageData&) = delete;
+
+ typedef std::unique_ptr< SvxBrushItem > SvxBrushItemPtr;
+
+ ScfUInt16Vec maHorPageBreaks; /// Horizontal page breaks.
+ ScfUInt16Vec maVerPageBreaks; /// Vertical page breaks.
+ SvxBrushItemPtr mxBrushItem; /// Background bitmap.
+ OUString maHeader; /// Excel header string (empty = off).
+ OUString maFooter; /// Excel footer string (empty = off).
+ OUString maHeaderEven; /// Excel header string for even pages (empty = off).
+ OUString maFooterEven; /// Excel footer string for even pages (empty = off).
+ OUString maHeaderFirst; /// Excel header string for first page (empty = off).
+ OUString maFooterFirst; /// Excel footer string for first page (empty = off).
+ double mfLeftMargin; /// Left margin in inches.
+ double mfRightMargin; /// Right margin in inches.
+ double mfTopMargin; /// Top margin in inches.
+ double mfBottomMargin; /// Bottom margin in inches.
+ double mfHeaderMargin; /// Margin main page to header.
+ double mfFooterMargin; /// Margin main page to footer.
+ double mfHdrLeftMargin; /// Left margin to header.
+ double mfHdrRightMargin; /// Right margin to header.
+ double mfFtrLeftMargin; /// Left margin to footer.
+ double mfFtrRightMargin; /// Right margin to footer.
+ sal_uInt16 mnPaperSize; /// Index into paper size table.
+ sal_uInt16 mnStrictPaperSize; /// Same as papersize - but for ooxml (considering stricter dimensions)
+ sal_uInt16 mnPaperWidth; /// Paper Width in mm
+ sal_uInt16 mnPaperHeight; /// Paper Height in mm
+ sal_uInt16 mnCopies; /// Number of copies.
+ sal_uInt16 mnStartPage; /// Start page number.
+ sal_uInt16 mnScaling; /// Scaling in percent.
+ sal_uInt16 mnFitToWidth; /// Fit to number of pages in width.
+ sal_uInt16 mnFitToHeight; /// Fit to number of pages in height.
+ sal_uInt16 mnHorPrintRes; /// Horizontal printing resolution.
+ sal_uInt16 mnVerPrintRes; /// Vertical printing resolution.
+ bool mbUseEvenHF; /// True = use maHeaderEven/maFooterEven.
+ bool mbUseFirstHF; /// True = use maHeaderFirst/maFooterFirst.
+ bool mbValid; /// false = some of the values are not valid.
+ bool mbPortrait; /// true = portrait; false = landscape.
+ bool mbPrintInRows; /// true = in rows; false = in columns.
+ bool mbBlackWhite; /// true = black/white; false = colors.
+ bool mbDraftQuality; /// true = draft; false = default quality.
+ bool mbPrintNotes; /// true = print notes.
+ bool mbManualStart; /// true = mnStartPage valid; false = automatic.
+ bool mbFitToPages; /// true = fit to pages; false = scale in percent.
+ bool mbHorCenter; /// true = centered horizontally; false = left aligned.
+ bool mbVerCenter; /// true = centered vertically; false = top aligned.
+ bool mbPrintHeadings; /// true = print column and row headings.
+ bool mbPrintGrid; /// true = print grid lines.
+
+ explicit XclPageData();
+ ~XclPageData();
+
+ /** Sets Excel default page settings. */
+ void SetDefaults();
+
+ /** Returns the real paper size (twips) from the paper size index and paper orientation. */
+ Size GetScPaperSize() const;
+ /** Sets the Excel paper size index and paper orientation from Calc paper size (twips). */
+ void SetScPaperSize( const Size& rSize, bool bPortrait, bool bStrict = false );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlpivot.hxx b/sc/source/filter/inc/xlpivot.hxx
new file mode 100644
index 0000000000..eb9814926b
--- /dev/null
+++ b/sc/source/filter/inc/xlpivot.hxx
@@ -0,0 +1,774 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <tools/datetime.hxx>
+#include "ftools.hxx"
+#include "xladdress.hxx"
+#include <dpobject.hxx>
+
+#include <optional>
+
+class XclImpStream;
+class XclExpStream;
+enum class ScGeneralFunction;
+
+// Constants and Enumerations =================================================
+
+// misc -----------------------------------------------------------------------
+
+inline constexpr OUString EXC_STORAGE_PTCACHE = u"_SX_DB_CUR"_ustr;
+
+// strings
+const sal_uInt16 EXC_PT_NOSTRING = 0xFFFF;
+const sal_uInt16 EXC_PT_MAXSTRLEN = 0xFFFE;
+
+// pivot cache fields
+const size_t EXC_PC_MAXFIELDCOUNT = 0xFFFE;
+const sal_uInt16 EXC_PC_NOFIELD = 0xFFFF;
+const sal_Int32 EXC_PC_MAXSTRLEN = 255;
+
+// pivot cache items
+const size_t EXC_PC_MAXITEMCOUNT = 32500;
+const sal_uInt16 EXC_PC_NOITEM = 0xFFFF;
+
+// pivot table fields
+const sal_uInt16 EXC_PT_MAXFIELDCOUNT = 0xFFFE;
+const sal_uInt16 EXC_PT_MAXROWCOLCOUNT = EXC_PT_MAXFIELDCOUNT;
+const sal_uInt16 EXC_PT_MAXPAGECOUNT = 256;
+const sal_uInt16 EXC_PT_MAXDATACOUNT = 256;
+
+// pivot table items
+const sal_uInt16 EXC_PT_MAXITEMCOUNT = 32500;
+
+const sal_uInt16 EXC_PT_AUTOFMT_HEADER = 0x810;
+const sal_uInt16 EXC_PT_AUTOFMT_ZERO = 0;
+const sal_uInt32 EXC_PT_AUTOFMT_FLAGS = 0x20;
+
+/** Data type of a pivot cache item. */
+enum XclPCItemType
+{
+ EXC_PCITEM_INVALID, /// Special state, not used in Excel files.
+ EXC_PCITEM_EMPTY, /// Empty cell.
+ EXC_PCITEM_TEXT, /// String data.
+ EXC_PCITEM_DOUBLE, /// Floating-point value.
+ EXC_PCITEM_DATETIME, /// Date/time.
+ EXC_PCITEM_INTEGER, /// 16-bit integer value.
+ EXC_PCITEM_BOOL, /// Boolean value.
+ EXC_PCITEM_ERROR /// Error code.
+};
+
+/** Specifies the type of a pivot cache field. */
+enum XclPCFieldType
+{
+ EXC_PCFIELD_STANDARD, /// Standard field without grouping.
+ EXC_PCFIELD_STDGROUP, /// Standard grouping field.
+ EXC_PCFIELD_NUMGROUP, /// Numeric grouping field.
+ EXC_PCFIELD_DATEGROUP, /// First date grouping field (opt. with child grouping field).
+ EXC_PCFIELD_DATECHILD, /// Additional date grouping field.
+ EXC_PCFIELD_CALCED, /// Calculated field.
+ EXC_PCFIELD_UNKNOWN /// Unknown field state, handled like standard field.
+};
+
+// (0x0051,0x0052) DCONREF, DCONNAME ------------------------------------------
+
+const sal_uInt16 EXC_ID_DCONREF = 0x0051;
+const sal_uInt16 EXC_ID_DCONNAME = 0x0052;
+
+// (0x00B0) SXVIEW ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SXVIEW = 0x00B0;
+
+const sal_uInt16 EXC_SXVIEW_ROWGRAND = 0x0001;
+const sal_uInt16 EXC_SXVIEW_COLGRAND = 0x0002;
+const sal_uInt16 EXC_SXVIEW_DEFAULTFLAGS = 0x0208;
+
+const sal_uInt16 EXC_SXVIEW_DATALAST = 0xFFFF;
+const sal_uInt16 EXC_SXVIEW_AUTOFMT = 0x0001;
+
+// (0x00B1) SXVD --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVD = 0x00B1;
+
+const sal_uInt16 EXC_SXVD_AXIS_NONE = 0x0000;
+const sal_uInt16 EXC_SXVD_AXIS_ROW = 0x0001;
+const sal_uInt16 EXC_SXVD_AXIS_COL = 0x0002;
+const sal_uInt16 EXC_SXVD_AXIS_PAGE = 0x0004;
+const sal_uInt16 EXC_SXVD_AXIS_DATA = 0x0008;
+const sal_uInt16 EXC_SXVD_AXIS_ROWCOL = EXC_SXVD_AXIS_ROW | EXC_SXVD_AXIS_COL;
+const sal_uInt16 EXC_SXVD_AXIS_ROWCOLPAGE = EXC_SXVD_AXIS_ROWCOL | EXC_SXVD_AXIS_PAGE;
+
+const sal_uInt16 EXC_SXVD_SUBT_NONE = 0x0000;
+const sal_uInt16 EXC_SXVD_SUBT_DEFAULT = 0x0001;
+const sal_uInt16 EXC_SXVD_SUBT_SUM = 0x0002;
+const sal_uInt16 EXC_SXVD_SUBT_COUNT = 0x0004;
+const sal_uInt16 EXC_SXVD_SUBT_AVERAGE = 0x0008;
+const sal_uInt16 EXC_SXVD_SUBT_MAX = 0x0010;
+const sal_uInt16 EXC_SXVD_SUBT_MIN = 0x0020;
+const sal_uInt16 EXC_SXVD_SUBT_PROD = 0x0040;
+const sal_uInt16 EXC_SXVD_SUBT_COUNTNUM = 0x0080;
+const sal_uInt16 EXC_SXVD_SUBT_STDDEV = 0x0100;
+const sal_uInt16 EXC_SXVD_SUBT_STDDEVP = 0x0200;
+const sal_uInt16 EXC_SXVD_SUBT_VAR = 0x0400;
+const sal_uInt16 EXC_SXVD_SUBT_VARP = 0x0800;
+
+const sal_uInt16 EXC_SXVD_DEFAULT_CACHE = EXC_PC_NOFIELD;
+
+// (0x00B2) SXVI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVI = 0x00B2;
+
+const sal_uInt16 EXC_SXVI_TYPE_PAGE = 0x00FE;
+const sal_uInt16 EXC_SXVI_TYPE_NULL = 0x00FF;
+const sal_uInt16 EXC_SXVI_TYPE_DATA = 0x0000;
+const sal_uInt16 EXC_SXVI_TYPE_DEFAULT = 0x0001;
+const sal_uInt16 EXC_SXVI_TYPE_SUM = 0x0002;
+const sal_uInt16 EXC_SXVI_TYPE_COUNT = 0x0003;
+const sal_uInt16 EXC_SXVI_TYPE_AVERAGE = 0x0004;
+const sal_uInt16 EXC_SXVI_TYPE_MAX = 0x0005;
+const sal_uInt16 EXC_SXVI_TYPE_MIN = 0x0006;
+const sal_uInt16 EXC_SXVI_TYPE_PROD = 0x0007;
+const sal_uInt16 EXC_SXVI_TYPE_COUNTNUM = 0x0008;
+const sal_uInt16 EXC_SXVI_TYPE_STDDEV = 0x0009;
+const sal_uInt16 EXC_SXVI_TYPE_STDDEVP = 0x000A;
+const sal_uInt16 EXC_SXVI_TYPE_VAR = 0x000B;
+const sal_uInt16 EXC_SXVI_TYPE_VARP = 0x000C;
+const sal_uInt16 EXC_SXVI_TYPE_GRAND = 0x000D;
+
+const sal_uInt16 EXC_SXVI_DEFAULTFLAGS = 0x0000;
+const sal_uInt16 EXC_SXVI_HIDDEN = 0x0001;
+const sal_uInt16 EXC_SXVI_HIDEDETAIL = 0x0002;
+const sal_uInt16 EXC_SXVI_FORMULA = 0x0004;
+const sal_uInt16 EXC_SXVI_MISSING = 0x0008;
+
+const sal_uInt16 EXC_SXVI_DEFAULT_CACHE = EXC_PC_NOFIELD;
+
+// (0x00B4) SXIVD -------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXIVD = 0x00B4;
+const sal_uInt16 EXC_SXIVD_DATA = 0xFFFE;
+
+// (0x00B5) SXLI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXLI = 0x00B5;
+const sal_uInt16 EXC_SXLI_DEFAULTFLAGS = 0x0000;
+
+// (0x00B6) SXPI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXPI = 0x00B6;
+const sal_uInt16 EXC_SXPI_ALLITEMS = 0x7FFD;
+
+// (0x00C5) SXDI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDI = 0x00C5;
+
+const sal_uInt16 EXC_SXDI_FUNC_SUM = 0x0000;
+const sal_uInt16 EXC_SXDI_FUNC_COUNT = 0x0001;
+const sal_uInt16 EXC_SXDI_FUNC_AVERAGE = 0x0002;
+const sal_uInt16 EXC_SXDI_FUNC_MAX = 0x0003;
+const sal_uInt16 EXC_SXDI_FUNC_MIN = 0x0004;
+const sal_uInt16 EXC_SXDI_FUNC_PRODUCT = 0x0005;
+const sal_uInt16 EXC_SXDI_FUNC_COUNTNUM = 0x0006;
+const sal_uInt16 EXC_SXDI_FUNC_STDDEV = 0x0007;
+const sal_uInt16 EXC_SXDI_FUNC_STDDEVP = 0x0008;
+const sal_uInt16 EXC_SXDI_FUNC_VAR = 0x0009;
+const sal_uInt16 EXC_SXDI_FUNC_VARP = 0x000A;
+
+const sal_uInt16 EXC_SXDI_REF_NORMAL = 0x0000;
+const sal_uInt16 EXC_SXDI_REF_DIFF = 0x0001;
+const sal_uInt16 EXC_SXDI_REF_PERC = 0x0002;
+const sal_uInt16 EXC_SXDI_REF_PERC_DIFF = 0x0003;
+const sal_uInt16 EXC_SXDI_REF_RUN_TOTAL = 0x0004;
+const sal_uInt16 EXC_SXDI_REF_PERC_ROW = 0x0005;
+const sal_uInt16 EXC_SXDI_REF_PERC_COL = 0x0006;
+const sal_uInt16 EXC_SXDI_REF_PERC_TOTAL = 0x0007;
+const sal_uInt16 EXC_SXDI_REF_INDEX = 0x0008;
+
+const sal_uInt16 EXC_SXDI_PREVITEM = 0x7FFB;
+const sal_uInt16 EXC_SXDI_NEXTITEM = 0x7FFC;
+
+// (0x00C6) SXDB --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDB = 0x00C6;
+
+const sal_uInt16 EXC_SXDB_SAVEDATA = 0x0001;
+const sal_uInt16 EXC_SXDB_INVALID = 0x0002;
+const sal_uInt16 EXC_SXDB_REFRESH_LOAD = 0x0004;
+const sal_uInt16 EXC_SXDB_OPT_CACHE = 0x0008;
+const sal_uInt16 EXC_SXDB_BG_QUERY = 0x0010;
+const sal_uInt16 EXC_SXDB_ENABLE_REFRESH = 0x0020;
+const sal_uInt16 EXC_SXDB_DEFAULTFLAGS = EXC_SXDB_SAVEDATA | EXC_SXDB_ENABLE_REFRESH;
+
+const sal_uInt16 EXC_SXDB_BLOCKRECS = 0x1FFF;
+
+const sal_uInt16 EXC_SXDB_SRC_SHEET = 0x0001;
+const sal_uInt16 EXC_SXDB_SRC_EXTERN = 0x0002;
+const sal_uInt16 EXC_SXDB_SRC_CONSOLID = 0x0004;
+const sal_uInt16 EXC_SXDB_SRC_SCENARIO = 0x0008;
+
+// (0x00C7) SXFIELD -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFIELD = 0x00C7;
+
+const sal_uInt16 EXC_SXFIELD_HASITEMS = 0x0001;
+const sal_uInt16 EXC_SXFIELD_POSTPONE = 0x0002;
+const sal_uInt16 EXC_SXFIELD_CALCED = 0x0004;
+const sal_uInt16 EXC_SXFIELD_HASCHILD = 0x0008;
+const sal_uInt16 EXC_SXFIELD_NUMGROUP = 0x0010;
+const sal_uInt16 EXC_SXFIELD_16BIT = 0x0200;
+
+const sal_uInt16 EXC_SXFIELD_DATA_MASK = 0x0DE0;
+// known data types
+const sal_uInt16 EXC_SXFIELD_DATA_NONE = 0x0000; /// Special state for groupings.
+const sal_uInt16 EXC_SXFIELD_DATA_STR = 0x0480; /// Only strings, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_INT = 0x0520; /// Only integers, opt. with doubles.
+const sal_uInt16 EXC_SXFIELD_DATA_DBL = 0x0560; /// Only doubles, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_STR_INT = 0x05A0; /// Only strings and integers, opt. with doubles.
+const sal_uInt16 EXC_SXFIELD_DATA_STR_DBL = 0x05E0; /// Only strings and doubles, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_DATE = 0x0900; /// Only dates, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_DATE_EMP = 0x0980; /// Dates and empty strings, nothing else (?).
+const sal_uInt16 EXC_SXFIELD_DATA_DATE_NUM = 0x0D00; /// Dates with integers or doubles without strings.
+const sal_uInt16 EXC_SXFIELD_DATA_DATE_STR = 0x0D80; /// Dates and strings, opt. with integers or doubles.
+
+const sal_uInt16 EXC_SXFIELD_INDEX_MIN = 0; /// List index for minimum item in groupings.
+const sal_uInt16 EXC_SXFIELD_INDEX_MAX = 1; /// List index for maximum item in groupings.
+const sal_uInt16 EXC_SXFIELD_INDEX_STEP = 2; /// List index for step item in groupings.
+
+// (0x00C8) SXINDEXLIST -------------------------------------------------------
+const sal_uInt16 EXC_ID_SXINDEXLIST = 0x00C8;
+
+// (0x00C9) SXDOUBLE ----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDOUBLE = 0x00C9;
+
+// (0x00CA) SXBOOLEAN ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXBOOLEAN = 0x00CA;
+
+// (0x00CB) SXERROR -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXERROR = 0x00CB;
+
+// (0x00CC) SXINTEGER ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXINTEGER = 0x00CC;
+
+// (0x00CD) SXSTRING ----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXSTRING = 0x00CD;
+
+// (0x00CE) SXDATETIME --------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDATETIME = 0x00CE;
+
+// (0x00CF) SXEMPTY -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXEMPTY = 0x00CF;
+
+// (0x00D5) SXIDSTM -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXIDSTM = 0x00D5;
+
+// (0x00D8) SXNUMGROUP --------------------------------------------------------
+const sal_uInt16 EXC_ID_SXNUMGROUP = 0x00D8;
+
+const sal_uInt16 EXC_SXNUMGROUP_AUTOMIN = 0x0001;
+const sal_uInt16 EXC_SXNUMGROUP_AUTOMAX = 0x0002;
+
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_SEC = 1;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_MIN = 2;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_HOUR = 3;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_DAY = 4;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_MONTH = 5;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_QUART = 6;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_YEAR = 7;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_NUM = 8;
+
+// (0x00D9) SXGROUPINFO -------------------------------------------------------
+const sal_uInt16 EXC_ID_SXGROUPINFO = 0x00D9;
+
+// (0x00DC) SXEXT -------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXEXT = 0x00DC;
+
+// (0x00E3) SXVS --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVS = 0x00E3;
+
+const sal_uInt16 EXC_SXVS_UNKNOWN = 0x0000;
+const sal_uInt16 EXC_SXVS_SHEET = 0x0001;
+const sal_uInt16 EXC_SXVS_EXTERN = 0x0002;
+const sal_uInt16 EXC_SXVS_CONSOLID = 0x0004;
+const sal_uInt16 EXC_SXVS_PIVOTTAB = 0x0008;
+const sal_uInt16 EXC_SXVS_SCENARIO = 0x0010;
+
+// (0x00F0) SXRULE ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXRULE = 0x00F0;
+
+// (0x00F1) SXEX --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXEX = 0x00F1;
+
+const sal_uInt32 EXC_SXEX_DRILLDOWN = 0x00020000;
+const sal_uInt32 EXC_SXEX_DEFAULTFLAGS = 0x004F0200;
+
+// (0x00F2) SXFILT ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFILT = 0x00F2;
+
+// (0x00F5) -------------------------------------------------------------------
+const sal_uInt16 EXC_ID_00F5 = 0x00F5; /// Unknown record
+
+// (0x00F6) SXNAME ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXNAME = 0x00F6;
+
+// (0x00F8) SXPAIR ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXPAIR = 0x00F8;
+
+// (0x00F9) SXFMLA ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFMLA = 0x00F9;
+
+// (0x0100) SXVDEX ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVDEX = 0x0100;
+
+const sal_uInt32 EXC_SXVDEX_SHOWALL = 0x00000001;
+const sal_uInt32 EXC_SXVDEX_SORT = 0x00000200;
+const sal_uInt32 EXC_SXVDEX_SORT_ASC = 0x00000400;
+const sal_uInt32 EXC_SXVDEX_AUTOSHOW = 0x00000800;
+const sal_uInt32 EXC_SXVDEX_AUTOSHOW_ASC = 0x00001000;
+const sal_uInt32 EXC_SXVDEX_LAYOUT_REPORT = 0x00200000;
+const sal_uInt32 EXC_SXVDEX_LAYOUT_BLANK = 0x00400000;
+const sal_uInt32 EXC_SXVDEX_LAYOUT_TOP = 0x00800000;
+const sal_uInt32 EXC_SXVDEX_DEFAULTFLAGS = 0x0A00001E | EXC_SXVDEX_SORT_ASC | EXC_SXVDEX_AUTOSHOW_ASC;
+
+const sal_uInt16 EXC_SXVDEX_SORT_OWN = 0xFFFF;
+const sal_uInt16 EXC_SXVDEX_SHOW_NONE = 0xFFFF;
+const sal_uInt16 EXC_SXVDEX_FORMAT_NONE = 0x0000;
+
+// (0x0103) SXFORMULA ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFORMULA = 0x0103;
+
+// (0x0122) SXDBEX ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDBEX = 0x0122;
+const double EXC_SXDBEX_CREATION_DATE = 51901.029652778;
+
+// (0x01BB) SXFDBTYPE ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFDBTYPE = 0x01BB;
+const sal_uInt16 EXC_SXFDBTYPE_DEFAULT = 0x0000;
+
+// (0x0810) SXVIEWEX9 ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVIEWEX9 = 0x0810;
+
+// (0x0864) SXADDL ("Pivot Table Additional Info") ----------------------------
+const sal_uInt16 EXC_ID_SXADDL = 0x0864;
+
+// Pivot cache
+
+/** Represents a data item of any type in a pivot cache. Supposed as base class for import and export. */
+class XclPCItem
+{
+public:
+ explicit XclPCItem();
+ virtual ~XclPCItem();
+
+ XclPCItem(XclPCItem const &) = default;
+ XclPCItem(XclPCItem &&) = default;
+ XclPCItem & operator =(XclPCItem const &) = default;
+ XclPCItem & operator =(XclPCItem &&) = default;
+
+ /** Sets the item to 'empty' type. */
+ void SetEmpty();
+ /** Sets the item to 'text' type and adds the passed text. */
+ void SetText( const OUString& rText );
+ /** Sets the item to 'double' type and adds the passed value. */
+ void SetDouble( double fValue, const OUString& rText = OUString() );
+ /** Sets the item to 'date/time' type and adds the passed date. */
+ void SetDateTime( const DateTime& rDateTime, const OUString& rText = OUString() );
+ /** Sets the item to 'integer' type and adds the passed value. */
+ void SetInteger( sal_Int16 nValue );
+ /** Sets the item to 'error' type and adds the passed Excel error code. */
+ void SetError( sal_uInt16 nError );
+ /** Sets the item to 'boolean' type and adds the passed Boolean value. */
+ void SetBool( bool bValue, const OUString& rText = OUString() );
+
+ /** Returns the text representation of the item. */
+ const OUString& ConvertToText() const { return maText; }
+
+ /** Returns true, if the passed term equals this item. */
+ bool IsEqual( const XclPCItem& rItem ) const;
+
+ /** Returns true, if the item type is 'empty'. */
+ bool IsEmpty() const;
+ /** Returns pointer to text, if the item type is 'text', otherwise 0. */
+ const OUString* GetText() const;
+ /** Returns pointer to value, if the item type is 'double', otherwise 0. */
+ const double* GetDouble() const;
+ /** Returns pointer to date, if the item type is 'date/time', otherwise 0. */
+ const DateTime* GetDateTime() const;
+ /** Returns pointer to integer, if the item type is 'integer', otherwise 0. */
+ const sal_Int16* GetInteger() const;
+ /** Returns pointer to error code, if the item type is 'error', otherwise 0. */
+ const sal_uInt16* GetError() const;
+ /** Returns pointer to Boolean value, if the item type is 'boolean', otherwise 0. */
+ const bool* GetBool() const;
+
+ /** Returns the type of the item */
+ XclPCItemType GetType() const;
+
+private:
+ XclPCItemType meType; /// Type of the item.
+ OUString maText; /// Text representation of the item.
+ DateTime maDateTime; /// Value of a date/time item.
+ union
+ {
+ double mfValue; /// Value of a floating-point item.
+ sal_Int16 mnValue; /// Value of an integer item.
+ sal_uInt16 mnError; /// Error code of an error item.
+ bool mbValue; /// Value of a boolean item.
+ };
+};
+
+inline bool operator==( const XclPCItem& rLeft, const XclPCItem& rRight ) { return rLeft.IsEqual( rRight ); }
+inline bool operator!=( const XclPCItem& rLeft, const XclPCItem& rRight ) { return !(rLeft == rRight); }
+
+// Field settings =============================================================
+
+/** Contains data for a pivot cache field (SXFIELD record). */
+struct XclPCFieldInfo
+{
+ OUString maName; /// Name of the pivot cache field.
+ sal_uInt16 mnFlags; /// Various flags.
+ sal_uInt16 mnGroupChild; /// Field containing grouping info for this field.
+ sal_uInt16 mnGroupBase; /// Base field if this field contains grouping info.
+ sal_uInt16 mnVisItems; /// Number of visible items for this field.
+ sal_uInt16 mnGroupItems; /// Number of special items in a grouping field.
+ sal_uInt16 mnBaseItems; /// Number of items in the base field.
+ sal_uInt16 mnOrigItems; /// Number of original source data items.
+
+ explicit XclPCFieldInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCFieldInfo& rInfo );
+
+// Numeric grouping field settings ============================================
+
+/** Contains data for a numeric grouping field (SXNUMGROUP record). */
+struct XclPCNumGroupInfo
+{
+ sal_uInt16 mnFlags; /// Various flags.
+
+ explicit XclPCNumGroupInfo();
+
+ void SetNumType();
+
+ sal_Int32 GetScDateType() const;
+ void SetScDateType( sal_Int32 nScType );
+
+ sal_uInt16 GetXclDataType() const;
+ void SetXclDataType( sal_uInt16 nXclType );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCNumGroupInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCNumGroupInfo& rInfo );
+
+// Base class for pivot cache fields ==========================================
+
+/** Represents a field in a pivot cache. Supposed as base class for import and export. */
+class XclPCField
+{
+public:
+ explicit XclPCField( XclPCFieldType eFieldType, sal_uInt16 nFieldIdx );
+ virtual ~XclPCField();
+
+ /** Returns the index of this field in the containing pivot cache. */
+ sal_uInt16 GetFieldIndex() const { return mnFieldIdx; }
+
+ /** Returns true, if the type of the field is supported by Calc. */
+ bool IsSupportedField() const;
+
+ /** Returns true, if this is a standard field build directly from source data. */
+ bool IsStandardField() const;
+
+ /** Returns true, if this field is a grouping field. */
+ bool IsStdGroupField() const;
+ /** Returns true, if this field is a numeric grouping field. */
+ bool IsNumGroupField() const;
+ /** Returns true, if this field is a date/time grouping field. */
+ bool IsDateGroupField() const;
+ /** Returns true, if this field is a grouping field of any type. */
+ bool IsGroupField() const;
+
+ /** Returns true, if this field has a child field in a grouping. */
+ bool IsGroupBaseField() const;
+ /** Returns true, if this field is a child field in a grouping (it has a base field). */
+ bool IsGroupChildField() const;
+
+ /** Returns true, if the field is based on a column in the source data area. */
+ bool HasOrigItems() const;
+ /** Returns true, if any items are stored after the SXFIELD record. */
+ bool HasInlineItems() const;
+ /** Returns true, if the items are stored separately after the last field. */
+ bool HasPostponedItems() const;
+ /** Returns true, if the item indexes in the SXINDEXLIST record are stored as 16-bit values. */
+ bool Has16BitIndexes() const;
+
+protected:
+ XclPCFieldInfo maFieldInfo; /// Pivot cache field info (SXFIELD record).
+ XclPCFieldType meFieldType; /// Type of this pivot cache field.
+ sal_uInt16 mnFieldIdx; /// Own field index in pivot cache.
+ ScfUInt16Vec maGroupOrder; /// Order of items in a grouping field (SXGROUPINFO record).
+ XclPCNumGroupInfo maNumGroupInfo; /// Info for numeric grouping (SXNUMGROUP record).
+};
+
+// Pivot cache settings =======================================================
+
+/** Contains data for a pivot cache (SXDB record). */
+struct XclPCInfo
+{
+ sal_uInt32 mnSrcRecs; /// Records in source database.
+ sal_uInt16 mnStrmId; /// Stream identifier.
+ sal_uInt16 mnFlags; /// Flags for the cache.
+ sal_uInt16 mnBlockRecs; /// Records in a source database block.
+ sal_uInt16 mnStdFields; /// Number of standard pivot cache fields.
+ sal_uInt16 mnTotalFields; /// Number of all fields (standard, grouped, calculated).
+ sal_uInt16 mnSrcType; /// Database type.
+ OUString maUserName; /// Name of user who last modified the cache.
+
+ explicit XclPCInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCInfo& rInfo );
+
+// Pivot table
+
+// cached name ================================================================
+
+/** A name for various pivot table info structs. Includes 'use cache' state. */
+struct XclPTCachedName
+{
+ OUString maName; /// The visible name, if used.
+ bool mbUseCache; /// true = Use name in cache instead of maName.
+
+ explicit XclPTCachedName() : mbUseCache( true ) {}
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTCachedName& rCachedName );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTCachedName& rCachedName );
+
+/** Base struct for named info structs. Supports explicit naming and using the cache. */
+struct XclPTVisNameInfo
+{
+ XclPTCachedName maVisName; /// The displayed name of the item.
+
+ /** Returns true, if the name is set explicitly (maVisName.mbUseCache is false). */
+ bool HasVisName() const { return !maVisName.mbUseCache; }
+ /** Returns the name, if set explicitly (maVisName.mbUseCache is false). */
+ const OUString* GetVisName() const;
+ /** Sets the visible name and enables usage of cache if name is empty. */
+ void SetVisName( const OUString& rName );
+};
+
+// Field item settings ========================================================
+
+/** Contains data for a pivot table data item (SXVI record). */
+struct XclPTItemInfo : public XclPTVisNameInfo
+{
+ sal_uInt16 mnType; /// Type of the item (e.g. data, function, grand total).
+ sal_uInt16 mnFlags; /// Several flags.
+ sal_uInt16 mnCacheIdx; /// Index into cache for item name.
+
+ explicit XclPTItemInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTItemInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTItemInfo& rInfo );
+
+// General field settings =====================================================
+
+typedef ::std::vector< ScGeneralFunction > XclPTSubtotalVec;
+
+/** Contains data for a pivot table field (SXVD record). */
+struct XclPTFieldInfo : public XclPTVisNameInfo
+{
+ sal_uInt16 mnAxes; /// Flags for axes this field is part of.
+ sal_uInt16 mnSubtCount; /// Number of subtotal functions.
+ sal_uInt16 mnSubtotals; /// Bitfield for subtotal functions.
+ sal_uInt16 mnItemCount; /// Number of items of this field.
+ sal_uInt16 mnCacheIdx; /// Index into cache for field name (not part of record).
+
+ explicit XclPTFieldInfo();
+
+ /** Returns the API enum representing the orientation (first of row/col/page/data).
+ @param nMask Restricts the axes taken into account.
+ @return The first found axis orientation, that is allowed in nMask parameter. */
+ css::sheet::DataPilotFieldOrientation GetApiOrient( sal_uInt16 nMask ) const;
+ /** Adds the axis orientation represented by the passed API enum. */
+ void AddApiOrient( css::sheet::DataPilotFieldOrientation eOrient );
+
+ /** Returns a vector of all set subtotal functions. */
+ void GetSubtotals( XclPTSubtotalVec& rSubtotals ) const;
+ /** Sets the subtotal functions contained in the passed sequence. */
+ void SetSubtotals( const XclPTSubtotalVec& rSubtotals );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo );
+
+// Extended field settings ====================================================
+
+/** Contains extended data for a pivot table field (SXVDEX record). */
+struct XclPTFieldExtInfo
+{
+ sal_uInt32 mnFlags; /// Several flags and number of items for AutoShow.
+ sal_uInt16 mnSortField; /// Index to data field sorting bases on.
+ sal_uInt16 mnShowField; /// Index to data field AutoShow bases on.
+ sal_uInt16 mnNumFmt;
+ std::optional<OUString> mpFieldTotalName;
+
+ explicit XclPTFieldExtInfo();
+
+ /** Returns the API constant representing the sorting mode. */
+ sal_Int32 GetApiSortMode() const;
+ /** Sets the sorting mode represented by the passed API constant. */
+ void SetApiSortMode( sal_Int32 nSortMode );
+
+ /** Returns the API constant representing the AutoShow mode. */
+ sal_Int32 GetApiAutoShowMode() const;
+ /** Sets the AutoShow mode represented by the passed API constant. */
+ void SetApiAutoShowMode( sal_Int32 nShowMode );
+
+ /** Returns the number of items to be shown in AutoShow mode. */
+ sal_Int32 GetApiAutoShowCount() const;
+ /** Sets the number of items to be shown in AutoShow mode. */
+ void SetApiAutoShowCount( sal_Int32 nShowCount );
+
+ /** Returns the API constant representing the layout mode. */
+ sal_Int32 GetApiLayoutMode() const;
+ /** Sets the layout mode represented by the passed API constant. */
+ void SetApiLayoutMode( sal_Int32 nLayoutMode );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo );
+
+// Page field settings ========================================================
+
+/** Contains data for a pivot table page field (part of SXPI record). */
+struct XclPTPageFieldInfo
+{
+ sal_uInt16 mnField; /// Base field for this page info.
+ sal_uInt16 mnSelItem; /// Index to selected item.
+ sal_uInt16 mnObjId; /// Escher object ID of dropdown listbox.
+
+ explicit XclPTPageFieldInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTPageFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTPageFieldInfo& rInfo );
+
+// Data field settings ========================================================
+
+/** Contains data for a pivot table data field (SXDI record). */
+struct XclPTDataFieldInfo : public XclPTVisNameInfo
+{
+ sal_uInt16 mnField; /// Base field for this data info.
+ sal_uInt16 mnAggFunc; /// Data aggregation function.
+ sal_uInt16 mnRefType; /// Result reference type.
+ sal_uInt16 mnRefField; /// Index to SXVD of referred field used for the results.
+ sal_uInt16 mnRefItem; /// Index to SXVI of referred item of the used field.
+ sal_uInt16 mnNumFmt; /// Number format of the results.
+
+ explicit XclPTDataFieldInfo();
+
+ /** Returns the API enum representing the aggregation function. */
+ ScGeneralFunction GetApiAggFunc() const;
+ /** Sets the aggregation function represented by the passed API enum. */
+ void SetApiAggFunc( ScGeneralFunction eAggFunc );
+
+ /** Returns the API constant representing the result reference type. */
+ sal_Int32 GetApiRefType() const;
+ /** Sets the result reference type represented by the passed API constant. */
+ void SetApiRefType( sal_Int32 nRefType );
+
+ /** Returns the API constant representing the result reference item type. */
+ sal_Int32 GetApiRefItemType() const;
+ /** Sets the result reference item type represented by the passed API constant. */
+ void SetApiRefItemType( sal_Int32 nRefItemType );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTDataFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTDataFieldInfo& rInfo );
+
+// Pivot table settings =======================================================
+
+/** Contains data for a pivot table (SXVIEW record). */
+struct XclPTInfo
+{
+ OUString maTableName; /// The name of the pivot table.
+ OUString maDataName; /// The visible name of the data field.
+ XclRange maOutXclRange; /// Output range.
+ XclAddress maDataXclPos; /// First cell containing data.
+ sal_uInt16 mnFirstHeadRow; /// First heading row.
+ sal_uInt16 mnCacheIdx; /// 0-based index of the pivot cache.
+ sal_uInt16 mnDataAxis; /// Orientation of data fields.
+ sal_uInt16 mnDataPos; /// Position of data fields.
+ sal_uInt16 mnFields; /// Number of all fields.
+ sal_uInt16 mnRowFields; /// Number of row fields.
+ sal_uInt16 mnColFields; /// Number of column fields.
+ sal_uInt16 mnPageFields; /// Number of page fields.
+ sal_uInt16 mnDataFields; /// Number of data fields.
+ sal_uInt16 mnDataRows; /// Number of rows containing data.
+ sal_uInt16 mnDataCols; /// Number of columns containing data.
+ sal_uInt16 mnFlags; /// Flags for the entire pivot table.
+ sal_uInt16 mnAutoFmtIdx; /// Index to pivot table autoformat.
+
+ explicit XclPTInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTInfo& rInfo );
+
+// Extended pivot table settings ==============================================
+
+/** Extended information about a pivot table (SXEX record). */
+struct XclPTExtInfo
+{
+ sal_uInt16 mnSxformulaRecs; /// Number of SXFORMULA records.
+ sal_uInt16 mnSxselectRecs; /// Number of SXSELECT records.
+ sal_uInt16 mnPagePerRow; /// Number of page fields per row.
+ sal_uInt16 mnPagePerCol; /// Number of page fields per column.
+ sal_uInt32 mnFlags; /// Flags for the entire pivot table.
+
+ explicit XclPTExtInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTExtInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo );
+
+// Pivot table autoformat settings ==============================================
+
+/** Pivot table autoformat settings (SXVIEWEX9 record). */
+struct XclPTViewEx9Info
+{
+ sal_uInt32 mbReport; /// 2 for report* fmts ?
+ sal_uInt8 mnAutoFormat; /// AutoFormat ID
+ sal_uInt8 mnGridLayout; /// 0 == gridlayout, 0x10 == modern
+ OUString maGrandTotalName;
+
+ explicit XclPTViewEx9Info();
+ void Init( const ScDPObject& rDPObj );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo );
+
+/** Additional pivot table settings (SXADDL record). */
+struct XclPTAddl
+{
+ bool mbCompactMode;
+ explicit XclPTAddl();
+};
+
+XclImpStream& operator>>(XclImpStream& rStrm, XclPTAddl& rInfo);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlroot.hxx b/sc/source/filter/inc/xlroot.hxx
new file mode 100644
index 0000000000..8d9c549282
--- /dev/null
+++ b/sc/source/filter/inc/xlroot.hxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <i18nlangtag/lang.h>
+#include <tools/ref.hxx>
+#include <tools/long.hxx>
+#include "xlconst.hxx"
+#include <memory>
+
+namespace com::sun::star::beans { struct NamedValue; }
+namespace comphelper { class IDocPasswordVerifier; }
+
+// Forward declarations of objects in public use ==============================
+
+class DateTime;
+class SotStorage;
+class SotStorageStream;
+class ScDocShell;
+
+// Global data ================================================================
+
+#ifdef DBG_UTIL
+/** Counts the number of created root objects. */
+struct XclDebugObjCounter
+{
+ sal_Int32 mnObjCnt;
+ explicit XclDebugObjCounter() : mnObjCnt( 0 ) {}
+ ~XclDebugObjCounter();
+};
+#endif
+
+class SfxMedium;
+class ScEditEngineDefaulter;
+class ScHeaderEditEngine;
+class EditEngine;
+class ScExtDocOptions;
+class XclFontPropSetHelper;
+class XclChPropSetHelper;
+class XclTracer;
+
+struct RootData;//!
+
+/** Stores global buffers and data needed elsewhere in the Excel filters. */
+struct XclRootData
+#ifdef DBG_UTIL
+ : public XclDebugObjCounter
+#endif
+{
+ typedef std::shared_ptr< ScEditEngineDefaulter > ScEEDefaulterRef;
+ typedef std::shared_ptr< ScHeaderEditEngine > ScHeaderEERef;
+ typedef std::shared_ptr< EditEngine > EditEngineRef;
+ typedef std::shared_ptr< XclFontPropSetHelper > XclFontPropSetHlpRef;
+ typedef std::shared_ptr< XclChPropSetHelper > XclChPropSetHlpRef;
+ typedef std::shared_ptr< ScExtDocOptions > ScExtDocOptRef;
+ typedef std::shared_ptr< XclTracer > XclTracerRef;
+ typedef std::shared_ptr< RootData > RootDataRef;
+
+ XclBiff meBiff; /// Current BIFF version.
+ XclOutput meOutput; /// Current Output format.
+ SfxMedium& mrMedium; /// The medium to import from.
+ tools::SvRef<SotStorage> mxRootStrg; /// The root OLE storage of imported/exported file.
+ ScDocument& mrDoc; /// The source or destination document.
+ OUString maDocUrl; /// Document URL of imported/exported file.
+ OUString maBasePath; /// Base path of imported/exported file (path of maDocUrl).
+ OUString maUserName; /// Current user name.
+ static constexpr OUString gaDefPassword = u"VelvetSweatshop"_ustr; /// The default password used for stream encryption.
+ rtl_TextEncoding meTextEnc; /// Text encoding to import/export byte strings.
+ LanguageType meSysLang; /// System language.
+ LanguageType meDocLang; /// Document language (import: from file, export: from system).
+ LanguageType meUILang; /// UI language (import: from file, export: from system).
+ sal_Int16 mnDefApiScript; /// Default script type for blank cells (API constant).
+ ScAddress maScMaxPos; /// Highest Calc cell position.
+ ScAddress maXclMaxPos; /// Highest Excel cell position.
+ ScAddress maMaxPos; /// Highest position valid in Calc and Excel.
+
+ ScEEDefaulterRef mxEditEngine; /// Edit engine for rich strings etc.
+ ScHeaderEERef mxHFEditEngine; /// Edit engine for header/footer.
+ EditEngineRef mxDrawEditEng; /// Edit engine for text boxes.
+
+ XclFontPropSetHlpRef mxFontPropSetHlp; /// Property set helper for fonts.
+ XclChPropSetHlpRef mxChPropSetHlp; /// Property set helper for chart filter.
+
+ ScExtDocOptRef mxExtDocOpt; /// Extended document options.
+ XclTracerRef mxTracer; /// Filter tracer.
+ RootDataRef mxRD; /// Old RootData struct. Will be removed.
+
+ double mfScreenPixelX; /// Width of a screen pixel (1/100 mm).
+ double mfScreenPixelY; /// Height of a screen pixel (1/100 mm).
+ tools::Long mnCharWidth; /// Width of '0' in default font (twips).
+ tools::Long mnSpaceWidth; /// Width of space char ' ' using default font.
+ SCTAB mnScTab; /// Current Calc sheet index.
+ const bool mbExport; /// false = Import, true = Export.
+
+ explicit XclRootData( XclBiff eBiff, SfxMedium& rMedium,
+ tools::SvRef<SotStorage> xRootStrg, ScDocument& rDoc,
+ rtl_TextEncoding eTextEnc, bool bExport );
+ virtual ~XclRootData();
+};
+
+class SfxObjectShell;
+class ScModelObj;
+class OutputDevice;
+class SvNumberFormatter;
+class SdrPage;
+class ScStyleSheetPool;
+class ScRangeName;
+struct XclFontData;
+
+/** Access to global data for a filter object (imported or exported document) from other classes. */
+class XclRoot
+{
+public:
+ explicit XclRoot( XclRootData& rRootData );
+ XclRoot( const XclRoot& rRoot );
+
+ virtual ~XclRoot();
+
+ XclRoot& operator=( const XclRoot& rRoot );
+
+ /** Returns old RootData struct. Deprecated. */
+ RootData& GetOldRoot() const { return *mrData.mxRD; }
+
+ /** Returns the current BIFF version of the importer/exporter. */
+ XclBiff GetBiff() const { return mrData.meBiff; }
+ /** Returns the current output format of the importer/exporter. */
+ XclOutput GetOutput() const { return mrData.meOutput; }
+ /** Returns true, if currently a document is imported. */
+ bool IsImport() const { return !mrData.mbExport; }
+ /** Returns the text encoding to import/export byte strings. */
+ rtl_TextEncoding GetTextEncoding() const { return mrData.meTextEnc; }
+ /** Returns the system language, i.e. for number formats. */
+ LanguageType GetSysLanguage() const { return mrData.meSysLang; }
+ /** Returns the document language. */
+ LanguageType GetDocLanguage() const { return mrData.meDocLang; }
+ /** Returns the UI language. */
+ LanguageType GetUILanguage() const { return mrData.meUILang; }
+ /** Returns the default script type, e.g. for blank cells. */
+ sal_Int16 GetDefApiScript() const { return mrData.mnDefApiScript; }
+ /** Returns the width of the '0' character (default font) for the current printer (twips). */
+ tools::Long GetCharWidth() const { return mrData.mnCharWidth; }
+ tools::Long GetSpaceWidth() const { return mrData.mnSpaceWidth; }
+ /** Returns the current Calc sheet index. */
+ bool IsInGlobals() const { return mrData.mnScTab == SCTAB_GLOBAL; }
+ /** Returns the current Calc sheet index. */
+ SCTAB GetCurrScTab() const { return mrData.mnScTab; }
+
+ /** Calculates the width of the passed number of pixels in 1/100 mm. */
+ sal_Int32 GetHmmFromPixelX( double fPixelX ) const;
+ /** Calculates the height of the passed number of pixels in 1/100 mm. */
+ sal_Int32 GetHmmFromPixelY( double fPixelY ) const;
+
+ /** Returns the medium to import from. */
+ SfxMedium& GetMedium() const { return mrData.mrMedium; }
+ /** Returns the document URL of the imported/exported file. */
+ const OUString& GetDocUrl() const { return mrData.maDocUrl; }
+ /** Returns the base path of the imported/exported file. */
+ const OUString& GetBasePath() const { return mrData.maBasePath; }
+ /** Returns the current user name. */
+ const OUString& GetUserName() const { return mrData.maUserName; }
+
+ /** Returns the default password used for stream encryption. */
+ static OUString GetDefaultPassword() { return XclRootData::gaDefPassword; }
+ /** Requests and verifies a password from the medium or the user. */
+ css::uno::Sequence< css::beans::NamedValue >
+ RequestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const;
+
+ /** Returns the OLE2 root storage of the imported/exported file.
+ @return Pointer to root storage or 0, if the file is a simple stream. */
+ const tools::SvRef<SotStorage>& GetRootStorage() const { return mrData.mxRootStrg; }
+ /** Returns true, if the document contains a VBA storage. */
+ bool HasVbaStorage() const;
+
+ /** Tries to open a storage as child of the specified storage for reading or writing. */
+ tools::SvRef<SotStorage> OpenStorage( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName ) const;
+ /** Tries to open a storage as child of the root storage for reading or writing. */
+ tools::SvRef<SotStorage> OpenStorage( const OUString& rStrgName ) const;
+ /** Tries to open a new stream in the specified storage for reading or writing. */
+ tools::SvRef<SotStorageStream> OpenStream( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName ) const;
+ /** Tries to open a new stream in the root storage for reading or writing. */
+ tools::SvRef<SotStorageStream> OpenStream( const OUString& rStrmName ) const;
+
+ /** Returns reference to the destination document (import) or source document (export). */
+ ScDocument& GetDoc() const;
+
+ /** Returns the object shell of the Calc document. May be 0 (i.e. import from clipboard). */
+ ScDocShell* GetDocShell() const;
+ /** Returns the object model of the Calc document. */
+ ScModelObj* GetDocModelObj() const;
+ /** Returns pointer to the printer of the Calc document. */
+ OutputDevice* GetPrinter(bool bForceVirtDev = false) const;
+ /** Returns the style sheet pool of the Calc document. */
+ ScStyleSheetPool& GetStyleSheetPool() const;
+ /** Returns the defined names container of the Calc document. */
+ ScRangeName& GetNamedRanges() const;
+ /** Returns the drawing layer page of the passed sheet, if present. */
+ SdrPage* GetSdrPage( SCTAB nScTab ) const;
+
+ /** Returns the number formatter of the Calc document. */
+ SvNumberFormatter& GetFormatter() const;
+ /** Returns the null date of the current number formatter. */
+ DateTime GetNullDate() const;
+ /** Returns the base year depending on the current null date (1900 or 1904). */
+ sal_uInt16 GetBaseYear() const;
+ /** Converts a date/time value to a floating-point value. */
+ double GetDoubleFromDateTime( const DateTime& rDateTime ) const;
+ /** Converts a floating-point value to a date/time value. */
+ DateTime GetDateTimeFromDouble( double fValue ) const;
+
+ /** Returns the edit engine for import/export of rich strings etc. */
+ ScEditEngineDefaulter& GetEditEngine() const;
+ /** Returns the edit engine for import/export of headers/footers. */
+ ScHeaderEditEngine& GetHFEditEngine() const;
+ /** Returns the edit engine for import/export of drawing text boxes. */
+ EditEngine& GetDrawEditEngine() const;
+
+ /** Returns the property set helper for fonts. */
+ XclFontPropSetHelper& GetFontPropSetHelper() const;
+ /** Returns the property set helper for the chart filters. */
+ XclChPropSetHelper& GetChartPropSetHelper() const;
+
+ /** Returns the extended document options. */
+ ScExtDocOptions& GetExtDocOptions() const;
+ /** Returns the filter tracer. */
+ XclTracer& GetTracer() const;
+
+ /** Returns the highest possible cell address in a Calc document. */
+ const ScAddress& GetScMaxPos() const { return mrData.maScMaxPos; }
+ /** Returns the highest possible cell address in an Excel document (using current BIFF version). */
+ const ScAddress& GetXclMaxPos() const { return mrData.maXclMaxPos; }
+ /** Returns the highest possible cell address valid in Calc and Excel (using current BIFF version). */
+ const ScAddress& GetMaxPos() const { return mrData.maMaxPos; }
+
+ /** Sets the document language. */
+ void SetDocLanguage( LanguageType eLang ) { mrData.meDocLang = eLang; }
+ /** Sets the UI language, i.e. if it has been read from a file. */
+ void SetUILanguage( LanguageType eLang ) { mrData.meUILang = eLang; }
+ /** Sets the text encoding to import/export byte strings. */
+ void SetTextEncoding( rtl_TextEncoding eTextEnc );
+ /** Sets the width of the '0' - '9' digit character as well as the ' ' space char
+ (using the default font) for the current printer (twips).
+ @param rFontData The font used for the '0' character. */
+ void SetCharWidth( const XclFontData& rFontData );
+ /** Sets the current Calc sheet index. */
+ void SetCurrScTab( SCTAB nScTab ) { mrData.mnScTab = nScTab; }
+ /** Increases the current Calc sheet index by 1. */
+ void IncCurrScTab() { ++mrData.mnScTab; }
+
+private:
+ XclRootData& mrData; /// Reference to the global data struct.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlstream.hxx b/sc/source/filter/inc/xlstream.hxx
new file mode 100644
index 0000000000..6e87890a14
--- /dev/null
+++ b/sc/source/filter/inc/xlstream.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/svxerr.hxx>
+
+// Constants ==================================================================
+
+const std::size_t EXC_REC_SEEK_TO_BEGIN = 0;
+const std::size_t EXC_REC_SEEK_TO_END = static_cast<std::size_t>( -1 );
+
+const sal_uInt16 EXC_MAXRECSIZE_BIFF5 = 2080;
+const sal_uInt16 EXC_MAXRECSIZE_BIFF8 = 8224;
+
+const ErrCode EXC_ENCR_ERROR_UNSUPP_CRYPT = ERRCODE_SVX_READ_FILTER_CRYPT;
+const sal_uInt16 EXC_ENCR_BLOCKSIZE = 1024;
+
+const sal_uInt16 EXC_ID_UNKNOWN = SAL_MAX_UINT16;
+const sal_uInt16 EXC_ID_CONT = 0x003C;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlstring.hxx b/sc/source/filter/inc/xlstring.hxx
new file mode 100644
index 0000000000..b346953bac
--- /dev/null
+++ b/sc/source/filter/inc/xlstring.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <sal/types.h>
+#include <o3tl/typed_flags_set.hxx>
+
+// Constants and enumerations =================================================
+
+/** Flags used to specify import/export mode of strings. */
+enum class XclStrFlags : sal_uInt16 {
+ NONE = 0x0000, /// Default string settings.
+ ForceUnicode = 0x0001, /// Always use UCS-2 characters (default: try to compress). BIFF8 only.
+ EightBitLength = 0x0002, /// 8-bit string length field (default: 16-bit).
+ SmartFlags = 0x0004, /// Omit flags on empty string (default: read/write always). BIFF8 only.
+ SeparateFormats = 0x0008, /// Import: Keep old formats when reading unformatted string (default: clear formats); Export: Write unformatted string.
+ NoHeader = 0x0010, /// Export: Don't write the length and flag fields.
+};
+namespace o3tl {
+ template<> struct typed_flags<XclStrFlags> : is_typed_flags<XclStrFlags, 0x001f> {};
+}
+
+
+const sal_uInt16 EXC_STR_MAXLEN_8BIT = 0x00FF;
+const sal_uInt16 EXC_STR_MAXLEN = 0x7FFF;
+
+const sal_uInt8 EXC_STRF_16BIT = 0x01;
+const sal_uInt8 EXC_STRF_FAREAST = 0x04;
+const sal_uInt8 EXC_STRF_RICH = 0x08;
+const sal_uInt8 EXC_STRF_UNKNOWN = 0xF2;
+
+// Fixed-size characters
+const sal_uInt8 EXC_LF_C = '\x0A'; /// LF character (used for line break).
+const sal_uInt16 EXC_LF = EXC_LF_C; /// LF character (unicode).
+const sal_uInt8 EXC_NUL_C = '\x00'; /// NUL character.
+const sal_uInt16 EXC_NUL = EXC_NUL_C; /// NUL character (unicode).
+
+// Rich-string formatting runs ================================================
+
+/** Represents a formatting run for rich-strings.
+
+ An Excel formatting run stores the first formatted character in a
+ rich-string and the index of a font used to format this and the following
+ characters.
+ */
+struct XclFormatRun
+{
+ sal_uInt16 mnChar; /// First character this format applies to.
+ sal_uInt16 mnFontIdx; /// Excel font index for the next characters.
+
+ explicit XclFormatRun() : mnChar( 0 ), mnFontIdx( 0 ) {}
+ explicit XclFormatRun( sal_uInt16 nChar, sal_uInt16 nFontIdx ) :
+ mnChar( nChar ), mnFontIdx( nFontIdx ) {}
+};
+
+inline bool operator==( const XclFormatRun& rLeft, const XclFormatRun& rRight )
+{
+ return (rLeft.mnChar == rRight.mnChar) && (rLeft.mnFontIdx == rRight.mnFontIdx);
+}
+
+inline bool operator<( const XclFormatRun& rLeft, const XclFormatRun& rRight )
+{
+ return (rLeft.mnChar < rRight.mnChar) || ((rLeft.mnChar == rRight.mnChar) && (rLeft.mnFontIdx < rRight.mnFontIdx));
+}
+
+/** A vector with all formatting runs for a rich-string. */
+typedef ::std::vector< XclFormatRun > XclFormatRunVec;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlstyle.hxx b/sc/source/filter/inc/xlstyle.hxx
new file mode 100644
index 0000000000..2385b00136
--- /dev/null
+++ b/sc/source/filter/inc/xlstyle.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <map>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <docmodel/color/ComplexColor.hxx>
+#include <tools/color.hxx>
+#include <tools/fontenum.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/frmdir.hxx>
+#include <svl/zforlist.hxx>
+#include "fapihelper.hxx"
+
+class XclRoot;
+
+// Constants and Enumerations =================================================
+
+// Line styles ----------------------------------------------------------------
+
+const sal_uInt8 EXC_LINE_NONE = 0x00;
+const sal_uInt8 EXC_LINE_THIN = 0x01;
+const sal_uInt8 EXC_LINE_MEDIUM = 0x02;
+const sal_uInt8 EXC_LINE_DASHED = 0x03;
+const sal_uInt8 EXC_LINE_DOTTED = 0x04;
+const sal_uInt8 EXC_LINE_THICK = 0x05;
+const sal_uInt8 EXC_LINE_DOUBLE = 0x06;
+const sal_uInt8 EXC_LINE_HAIR = 0x07;
+const sal_uInt8 EXC_LINE_MEDIUM_DASHED = 0x08;
+const sal_uInt8 EXC_LINE_THIN_DASHDOT = 0x09;
+const sal_uInt8 EXC_LINE_MEDIUM_DASHDOT = 0x0A;
+const sal_uInt8 EXC_LINE_THIN_DASHDOTDOT = 0x0B;
+const sal_uInt8 EXC_LINE_MEDIUM_DASHDOTDOT = 0x0C;
+const sal_uInt8 EXC_LINE_MEDIUM_SLANT_DASHDOT = 0x0D;
+
+// Background patterns --------------------------------------------------------
+
+const sal_uInt8 EXC_PATT_NONE = 0x00;
+const sal_uInt8 EXC_PATT_SOLID = 0x01;
+const sal_uInt8 EXC_PATT_50_PERC = 0x02;
+const sal_uInt8 EXC_PATT_75_PERC = 0x03;
+const sal_uInt8 EXC_PATT_25_PERC = 0x04;
+const sal_uInt8 EXC_PATT_12_5_PERC = 0x11;
+const sal_uInt8 EXC_PATT_6_25_PERC = 0x12;
+
+// (0x001E, 0x041E) FORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID2_FORMAT = 0x001E;
+const sal_uInt16 EXC_ID4_FORMAT = 0x041E;
+
+const sal_uInt16 EXC_FORMAT_OFFSET5 = 164;
+const sal_uInt16 EXC_FORMAT_OFFSET8 = 164;
+const sal_uInt16 EXC_FORMAT_NOTFOUND = 0xFFFF;
+
+// (0x0031) FONT --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID2_FONT = 0x0031;
+const sal_uInt16 EXC_ID3_FONT = 0x0231;
+
+const sal_uInt16 EXC_FONT_APP = 0; /// Application font index.
+const sal_uInt16 EXC_FONT_NOTFOUND = 0xFFFF;
+
+const size_t EXC_FONT_MAXCOUNT4 = 0x00FF;
+const size_t EXC_FONT_MAXCOUNT5 = 0x00FF;
+const size_t EXC_FONT_MAXCOUNT8 = 0xFFFF;
+
+// families
+const sal_uInt8 EXC_FONTFAM_DONTKNOW = 0x00;
+const sal_uInt8 EXC_FONTFAM_ROMAN = 0x01;
+const sal_uInt8 EXC_FONTFAM_SWISS = 0x02;
+const sal_uInt8 EXC_FONTFAM_SYSTEM = EXC_FONTFAM_SWISS;
+const sal_uInt8 EXC_FONTFAM_MODERN = 0x03;
+const sal_uInt8 EXC_FONTFAM_SCRIPT = 0x04;
+const sal_uInt8 EXC_FONTFAM_DECORATIVE = 0x05;
+
+// charsets
+const sal_uInt8 EXC_FONTCSET_ANSI_LATIN = 0x00;
+
+// attributes
+const sal_uInt16 EXC_FONTATTR_NONE = 0x0000;
+const sal_uInt16 EXC_FONTATTR_BOLD = 0x0001;
+const sal_uInt16 EXC_FONTATTR_ITALIC = 0x0002;
+const sal_uInt16 EXC_FONTATTR_UNDERLINE = 0x0004;
+const sal_uInt16 EXC_FONTATTR_STRIKEOUT = 0x0008;
+const sal_uInt16 EXC_FONTATTR_OUTLINE = 0x0010;
+const sal_uInt16 EXC_FONTATTR_SHADOW = 0x0020;
+
+// weight
+const sal_uInt16 EXC_FONTWGHT_DONTKNOW = 0;
+const sal_uInt16 EXC_FONTWGHT_THIN = 100;
+const sal_uInt16 EXC_FONTWGHT_ULTRALIGHT = 200;
+const sal_uInt16 EXC_FONTWGHT_LIGHT = 300;
+const sal_uInt16 EXC_FONTWGHT_SEMILIGHT = 350;
+const sal_uInt16 EXC_FONTWGHT_NORMAL = 400;
+const sal_uInt16 EXC_FONTWGHT_MEDIUM = 500;
+const sal_uInt16 EXC_FONTWGHT_SEMIBOLD = 600;
+const sal_uInt16 EXC_FONTWGHT_BOLD = 700;
+const sal_uInt16 EXC_FONTWGHT_ULTRABOLD = 800;
+const sal_uInt16 EXC_FONTWGHT_BLACK = 900;
+
+// underline
+const sal_uInt8 EXC_FONTUNDERL_NONE = 0x00;
+const sal_uInt8 EXC_FONTUNDERL_SINGLE = 0x01;
+const sal_uInt8 EXC_FONTUNDERL_DOUBLE = 0x02;
+const sal_uInt8 EXC_FONTUNDERL_SINGLE_ACC = 0x21;
+const sal_uInt8 EXC_FONTUNDERL_DOUBLE_ACC = 0x22;
+
+// escapement
+const sal_uInt16 EXC_FONTESC_NONE = 0x00;
+const sal_uInt16 EXC_FONTESC_SUPER = 0x01;
+const sal_uInt16 EXC_FONTESC_SUB = 0x02;
+
+// (0x0043, 0x0243, 0x0443, 0x00E0) XF ----------------------------------------
+
+const sal_uInt16 EXC_ID2_XF = 0x0043;
+const sal_uInt16 EXC_ID3_XF = 0x0243;
+const sal_uInt16 EXC_ID4_XF = 0x0443;
+const sal_uInt16 EXC_ID5_XF = 0x00E0;
+
+const sal_uInt32 EXC_XF_MAXCOUNT = 4050; /// Maximum number of all XF records.
+const sal_uInt32 EXC_XF_MAXSTYLECOUNT = 1536; /// Arbitrary maximum number of style XFs.
+const sal_uInt16 EXC_XF_DEFAULTSTYLE = 0; /// Excel index to default style XF.
+const sal_uInt16 EXC_XF_DEFAULTCELL = 15; /// Excel index to default cell XF.
+const sal_uInt16 EXC_XF_NOTFOUND = 0xFFFF; /// Special index for "not found" state.
+
+const sal_uInt32 EXC_XFID_NOTFOUND = 0xFFFFFFFF;
+
+const sal_uInt16 EXC_XF_LOCKED = 0x0001;
+const sal_uInt16 EXC_XF_HIDDEN = 0x0002;
+const sal_uInt16 EXC_XF_STYLE = 0x0004;
+const sal_uInt16 EXC_XF_STYLEPARENT = 0x0FFF; /// Styles don't have a parent.
+const sal_uInt16 EXC_XF_LINEBREAK = 0x0008; /// Automatic line break.
+const sal_uInt16 EXC_XF_SHRINK = 0x0010; /// Shrink to fit into cell.
+
+const sal_uInt8 EXC_XF_DIFF_VALFMT = 0x01;
+const sal_uInt8 EXC_XF_DIFF_FONT = 0x02;
+const sal_uInt8 EXC_XF_DIFF_ALIGN = 0x04;
+const sal_uInt8 EXC_XF_DIFF_BORDER = 0x08;
+const sal_uInt8 EXC_XF_DIFF_AREA = 0x10;
+const sal_uInt8 EXC_XF_DIFF_PROT = 0x20;
+
+const sal_uInt8 EXC_XF_HOR_GENERAL = 0x00;
+const sal_uInt8 EXC_XF_HOR_LEFT = 0x01;
+const sal_uInt8 EXC_XF_HOR_CENTER = 0x02;
+const sal_uInt8 EXC_XF_HOR_RIGHT = 0x03;
+const sal_uInt8 EXC_XF_HOR_FILL = 0x04;
+const sal_uInt8 EXC_XF_HOR_JUSTIFY = 0x05;
+const sal_uInt8 EXC_XF_HOR_CENTER_AS = 0x06;
+const sal_uInt8 EXC_XF_HOR_DISTRIB = 0x07;
+
+const sal_uInt8 EXC_XF_VER_TOP = 0x00;
+const sal_uInt8 EXC_XF_VER_CENTER = 0x01;
+const sal_uInt8 EXC_XF_VER_BOTTOM = 0x02;
+const sal_uInt8 EXC_XF_VER_JUSTIFY = 0x03;
+const sal_uInt8 EXC_XF_VER_DISTRIB = 0x04;
+
+const sal_uInt8 EXC_XF_TEXTDIR_CONTEXT = 0x00;
+const sal_uInt8 EXC_XF_TEXTDIR_LTR = 0x01;
+const sal_uInt8 EXC_XF_TEXTDIR_RTL = 0x02;
+
+const sal_uInt8 EXC_XF2_VALFMT_MASK = 0x3F;
+const sal_uInt8 EXC_XF2_LOCKED = 0x40;
+const sal_uInt8 EXC_XF2_HIDDEN = 0x80;
+const sal_uInt8 EXC_XF2_LEFTLINE = 0x08;
+const sal_uInt8 EXC_XF2_RIGHTLINE = 0x10;
+const sal_uInt8 EXC_XF2_TOPLINE = 0x20;
+const sal_uInt8 EXC_XF2_BOTTOMLINE = 0x40;
+const sal_uInt8 EXC_XF2_BACKGROUND = 0x80;
+
+const sal_uInt16 EXC_XF8_SHRINK = 0x0010; /// Shrink to fit into cell.
+const sal_uInt16 EXC_XF8_MERGE = 0x0020;
+
+const sal_uInt32 EXC_XF_DIAGONAL_TL_TO_BR = 0x40000000; /// Top-left to bottom-right.
+const sal_uInt32 EXC_XF_DIAGONAL_BL_TO_TR = 0x80000000; /// Bottom-left to top-right.
+const sal_uInt32 EXC_XF_DIAGONAL_BOTH = 0xC0000000; /// Both.
+
+// (0x0045) EFONT -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EFONT = 0x0045;
+
+// (0x0092) PALETTE -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_PALETTE = 0x0092;
+
+const sal_uInt16 EXC_COLOR_BIFF2_BLACK = 0;
+const sal_uInt16 EXC_COLOR_BIFF2_WHITE = 1;
+
+const sal_uInt16 EXC_COLOR_USEROFFSET = 8; /// First user defined color.
+const sal_uInt16 EXC_COLOR_WINDOWTEXT3 = 24; /// System window text color (BIFF3-BIFF4).
+const sal_uInt16 EXC_COLOR_WINDOWBACK3 = 25; /// System window background color (BIFF3-BIFF4).
+const sal_uInt16 EXC_COLOR_WINDOWTEXT = 64; /// System window text color (>=BIFF5).
+const sal_uInt16 EXC_COLOR_WINDOWBACK = 65; /// System window background color (>=BIFF5).
+const sal_uInt16 EXC_COLOR_BUTTONBACK = 67; /// System button background color (face color).
+const sal_uInt16 EXC_COLOR_CHWINDOWTEXT = 77; /// Chart window text color (BIFF8 charts).
+const sal_uInt16 EXC_COLOR_CHWINDOWBACK = 78; /// Chart window background color (BIFF8 charts).
+const sal_uInt16 EXC_COLOR_CHBORDERAUTO = 79; /// Automatic frame border for series (BIFF8 charts).
+const sal_uInt16 EXC_COLOR_NOTEBACK = 80; /// Note background color.
+const sal_uInt16 EXC_COLOR_NOTETEXT = 81; /// Note text color.
+const sal_uInt16 EXC_COLOR_FONTAUTO = 0x7FFF; /// Font auto color (system window text color).
+
+// (0x0293) STYLE -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_STYLE = 0x0293;
+
+const sal_uInt16 EXC_STYLE_BUILTIN = 0x8000;
+const sal_uInt16 EXC_STYLE_XFMASK = 0x0FFF;
+
+const sal_uInt8 EXC_STYLE_NORMAL = 0x00; /// "Normal" style.
+const sal_uInt8 EXC_STYLE_ROWLEVEL = 0x01; /// "RowLevel_*" styles.
+const sal_uInt8 EXC_STYLE_COLLEVEL = 0x02; /// "ColLevel_*" styles.
+const sal_uInt8 EXC_STYLE_COMMA = 0x03; /// "Comma" style.
+const sal_uInt8 EXC_STYLE_CURRENCY = 0x04; /// "Currency" style.
+const sal_uInt8 EXC_STYLE_PERCENT = 0x05; /// "Percent" style.
+const sal_uInt8 EXC_STYLE_COMMA_0 = 0x06; /// "Comma [0]" style.
+const sal_uInt8 EXC_STYLE_CURRENCY_0 = 0x07; /// "Currency [0]" style.
+const sal_uInt8 EXC_STYLE_HYPERLINK = 0x08; /// "Hyperlink" style.
+const sal_uInt8 EXC_STYLE_FOLLOWED_HYPERLINK= 0x09; /// "Followed_Hyperlink" style.
+const sal_uInt8 EXC_STYLE_USERDEF = 0xFF; /// No built-in style.
+
+const sal_uInt8 EXC_STYLE_LEVELCOUNT = 7; /// Number of outline level styles.
+const sal_uInt8 EXC_STYLE_NOLEVEL = 0xFF; /// Default value for unused level.
+
+// (0x0892) STYLEEXT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_STYLEEXT = 0x0892;
+
+const sal_uInt8 EXC_STYLEEXT_BUILTIN = 0x01;
+const sal_uInt8 EXC_STYLEEXT_HIDDEN = 0x02;
+const sal_uInt8 EXC_STYLEEXT_CUSTOM = 0x04;
+
+// Color data =================================================================
+
+/** Stores all default colors for a specific BIFF version. */
+class XclDefaultPalette
+{
+public:
+ explicit XclDefaultPalette( const XclRoot& rRoot );
+
+ /** Returns the color count in the current palette. */
+ sal_uInt32 GetColorCount() const { return mnTableSize - EXC_COLOR_USEROFFSET; }
+
+ /** Returns the default color for a (non-zero-based) Excel color or COL_AUTO on error. */
+ Color GetDefColor( sal_uInt16 nXclIndex ) const;
+
+ /** Returns true, if the passed Excel color index is a system color. */
+ bool IsSystemColor( sal_uInt16 nXclIndex ) const { return nXclIndex >= mnTableSize; }
+
+private:
+ const Color* mpnColorTable; /// The table with RGB values.
+ Color mnFaceColor; /// System button background color.
+ Color mnNoteText; /// Note text color.
+ Color mnNoteBack; /// Note background color.
+ sal_uInt32 mnTableSize; /// The color table size.
+};
+
+// Font data ==================================================================
+
+namespace vcl { class Font; }
+class SvxFont;
+
+/** This struct helps reading and writing Excel fonts.
+
+ It stores all Excel compatible properties of a font. In detail this is the
+ name, family, character set, height, color, boldness, posture, script,
+ underline, strikeout, outline and shadow of the font.
+ */
+struct XclFontData
+{
+ OUString maName; /// Font name.
+ OUString maStyle; /// String with styles (bold, italic).
+ sal_uInt16 mnHeight; /// Font height in twips (1/20 of a point).
+ sal_uInt16 mnWeight; /// Boldness: 400=normal, 700=bold.
+ sal_uInt16 mnEscapem; /// Escapement type.
+ sal_uInt8 mnFamily; /// Windows font family.
+ sal_uInt8 mnCharSet; /// Windows character set.
+ sal_uInt8 mnUnderline; /// Underline style.
+ bool mbItalic; /// true = Italic.
+ bool mbStrikeout; /// true = Struck out.
+ bool mbOutline; /// true = Outlined.
+ bool mbShadow; /// true = Shadowed.
+
+ model::ComplexColor maComplexColor; /// Font color.
+
+ /** Constructs an empty font data structure. */
+ explicit XclFontData();
+ /** Constructs a font data structure and fills it with the passed font attributes (except color). */
+ explicit XclFontData(const vcl::Font& rFont, model::ComplexColor const& rComplexColor);
+ /** As directly above but also fills in the escapement member. */
+ explicit XclFontData(const SvxFont& rFont, model::ComplexColor const& rComplexColor);
+
+ /** Resets all members to default (empty) values. */
+ void Clear();
+ /** Fills all members (except color and escapement) from the passed font. */
+ void FillFromVclFont(const vcl::Font& rFont, model::ComplexColor const& rComplexColor);
+ /** Fills all members (except color) from the passed SVX font. */
+ void FillFromSvxFont(const SvxFont& rFont, model::ComplexColor const& rComplexColor);
+
+// *** conversion of VCL/SVX constants *** ------------------------------------
+
+ /** Returns the Calc font family. */
+ FontFamily GetScFamily( rtl_TextEncoding eDefTextEnc ) const;
+ /** Returns the font text encoding. */
+ rtl_TextEncoding GetFontEncoding() const;
+ /** Returns the Calc font posture. */
+ FontItalic GetScPosture() const;
+ /** Returns the Calc font weight. */
+ FontWeight GetScWeight() const;
+ /** Returns the Calc font underline style. */
+ FontLineStyle GetScUnderline() const;
+ /** Returns the Calc escapement style. */
+ SvxEscapement GetScEscapement() const;
+ /** Returns the Calc strike-out style. */
+ FontStrikeout GetScStrikeout() const;
+
+ /** Sets the Calc font height (in twips). */
+ void SetScHeight( sal_Int32 nTwips );
+ /** Sets the Calc font family. */
+ void SetScFamily( FontFamily eScFamily );
+ /** Sets the font text encoding. */
+ void SetFontEncoding( rtl_TextEncoding eFontEnc );
+ /** Sets the Calc font posture. */
+ void SetScPosture( FontItalic eScPosture );
+ /** Sets the Calc font weight. */
+ void SetScWeight( FontWeight eScWeight );
+ /** Sets the Calc underline style. */
+ void SetScUnderline( FontLineStyle eScUnderl );
+ /** Sets the Calc escapement style. */
+ void SetScEscapement( short nScEscapem );
+ /** Sets the Calc strike-out style. */
+ void SetScStrikeout( FontStrikeout eScStrikeout );
+
+// *** conversion of API constants *** ----------------------------------------
+
+ /** Returns the API font height. */
+ float GetApiHeight() const;
+ /** Returns the API font family. */
+ sal_Int16 GetApiFamily() const;
+ /** Returns the API font text encoding. */
+ sal_Int16 GetApiFontEncoding() const;
+ /** Returns the API font posture. */
+ css::awt::FontSlant GetApiPosture() const;
+ /** Returns the API font weight. */
+ float GetApiWeight() const;
+ /** Returns the API font underline style. */
+ sal_Int16 GetApiUnderline() const;
+ /** Returns the API escapement style. */
+ sal_Int16 GetApiEscapement() const;
+ /** Returns the API font strike-out style. */
+ sal_Int16 GetApiStrikeout() const;
+
+ /** Sets the API font height. */
+ void SetApiHeight( float fPoint );
+ /** Sets the API font family. */
+ void SetApiFamily( sal_Int16 nApiFamily );
+ /** Sets the API font posture. */
+ void SetApiPosture( css::awt::FontSlant eApiPosture );
+ /** Sets the API font weight. */
+ void SetApiWeight( float fApiWeight );
+ /** Sets the API font underline style. */
+ void SetApiUnderline( sal_Int16 nApiUnderl );
+ /** Sets the API escapement style. */
+ void SetApiEscapement( sal_Int16 nApiEscapem );
+ /** Sets the API font strike-out style. */
+ void SetApiStrikeout( sal_Int16 nApiStrikeout );
+};
+
+bool operator==( const XclFontData& rLeft, const XclFontData& rRight );
+
+/** Enumerates different types of Which-IDs for font items. */
+enum class XclFontItemType
+{
+ Cell, /// Use Calc Which-IDs (ATTR_*).
+ Editeng, /// Use edit engine Which-IDs (EE_CHAR_*).
+ HeaderFooter /// Use header/footer edit engine Which-IDs (EE_CHAR_*).
+};
+
+/** Enumerates different types for objects with font settings (using different property names). */
+enum XclFontPropSetType
+{
+ EXC_FONTPROPSET_CHART, /// All text objects in charts.
+ EXC_FONTPROPSET_CONTROL /// Text formatting in form controls.
+};
+
+/** Helper class for usage of property sets. */
+class XclFontPropSetHelper
+{
+public:
+ explicit XclFontPropSetHelper();
+
+ /** Reads all font properties from the passed property set. */
+ void ReadFontProperties( XclFontData& rFontData,
+ const ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ sal_Int16 nScript = -1 );
+
+ /** Writes all font properties to the passed property set, uses passed color as font color. */
+ void WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ const XclFontData& rFontData,
+ bool bHasWstrn, bool bHasAsian, bool bHasCmplx,
+ const Color* pFontColor );
+
+private:
+ /** Returns a chart property set helper according to the passed script type. */
+ ScfPropSetHelper& GetChartHelper( sal_Int16 nScript );
+
+private:
+ ScfPropSetHelper maHlpChCommon; /// Chart properties for all scripts.
+ ScfPropSetHelper maHlpChWstrn; /// Chart properties for Western script.
+ ScfPropSetHelper maHlpChAsian; /// Chart properties for Asian script.
+ ScfPropSetHelper maHlpChCmplx; /// Chart properties for Complex script.
+ ScfPropSetHelper maHlpChWstrnNoName; /// Chart properties for Western script, no font name.
+ ScfPropSetHelper maHlpChAsianNoName; /// Chart properties for Asian script, no font name.
+ ScfPropSetHelper maHlpChCmplxNoName; /// Chart properties for Complex script, no font name.
+ ScfPropSetHelper maHlpChEscapement; /// Chart properties for font escapement.
+ ScfPropSetHelper maHlpControl; /// Properties for form controls.
+};
+
+// Number formats =============================================================
+
+struct XclNumFmt
+{
+ OUString maFormat; /// Format string, may be empty (meOffset used then).
+ NfIndexTableOffset meOffset; /// SvNumberFormatter format index, if maFormat is empty.
+ LanguageType meLanguage; /// Language type to be set with the number format.
+};
+
+class XclNumFmtBuffer
+{
+public:
+ explicit XclNumFmtBuffer( const XclRoot& rRoot );
+
+ /** Returns the core index of the current standard number format. */
+ sal_uInt32 GetStdScNumFmt() const { return mnStdScNumFmt; }
+
+protected:
+ typedef ::std::map< sal_uInt16, XclNumFmt > XclNumFmtMap;
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void InitializeImport();
+
+ /** Returns the current number format map. */
+ const XclNumFmtMap& GetFormatMap() const { return maFmtMap; }
+
+ /** Inserts a new number format for the specified Excel format index. */
+ void InsertFormat( sal_uInt16 nXclNumFmt, const OUString& rFormat );
+
+private:
+ /** Inserts built-in number formats for the current system language. */
+ void InsertBuiltinFormats();
+
+ XclNumFmtMap maFmtMap; /// Map containing all default and user-defined formats.
+ const LanguageType meSysLang; /// Current system language.
+ const sal_uInt32 mnStdScNumFmt; /// Calc format key for standard number format.
+};
+
+// Cell formatting data (XF) ==================================================
+
+/** Contains all cell protection attributes. */
+struct XclCellProt
+{
+ bool mbLocked; /// true = Locked against editing.
+ bool mbHidden; /// true = Formula is hidden.
+
+ explicit XclCellProt();
+};
+
+bool operator==( const XclCellProt& rLeft, const XclCellProt& rRight );
+
+/** Contains all cell alignment attributes. */
+struct XclCellAlign
+{
+ sal_uInt8 mnHorAlign; /// Horizontal alignment.
+ sal_uInt8 mnVerAlign; /// Vertical alignment.
+ sal_uInt8 mnOrient; /// Text orientation.
+ sal_uInt8 mnTextDir; /// CTL text direction.
+ sal_uInt8 mnRotation; /// Text rotation angle.
+ sal_uInt8 mnIndent; /// Indentation.
+ bool mbLineBreak; /// true = Multi-line text.
+ bool mbShrink; /// true = Shrink to fit cell size.
+
+ explicit XclCellAlign();
+
+ /** Returns the Calc horizontal alignment. */
+ SvxCellHorJustify GetScHorAlign() const;
+ /** Returns horizontal justification method as Calc's attribute. */
+ SvxCellJustifyMethod GetScHorJustifyMethod() const;
+ /** Returns the Calc vertical alignment. */
+ SvxCellVerJustify GetScVerAlign() const;
+ /** Returns vertical justification method as Calc's attribute. */
+ SvxCellJustifyMethod GetScVerJustifyMethod() const;
+ /** Returns the Calc frame direction. */
+ SvxFrameDirection GetScFrameDir() const;
+
+ /** Sets the Calc horizontal alignment. */
+ void SetScHorAlign( SvxCellHorJustify eHorJust );
+ /** Sets the Calc vertical alignment. */
+ void SetScVerAlign( SvxCellVerJustify eVerJust );
+ /** Sets the Calc frame direction. */
+ void SetScFrameDir( SvxFrameDirection eFrameDir );
+};
+
+bool operator==( const XclCellAlign& rLeft, const XclCellAlign& rRight );
+
+/** Contains color and line style for each cell border line. */
+struct XclCellBorder
+{
+ sal_uInt16 mnLeftColor; /// Palette index for left line.
+ sal_uInt16 mnRightColor; /// Palette index for right line.
+ sal_uInt16 mnTopColor; /// Palette index for top line.
+ sal_uInt16 mnBottomColor; /// Palette index for bottom line.
+ sal_uInt16 mnDiagColor; /// Palette index for diagonal line(s).
+ sal_uInt8 mnLeftLine; /// Style of left line.
+ sal_uInt8 mnRightLine; /// Style of right line.
+ sal_uInt8 mnTopLine; /// Style of top line.
+ sal_uInt8 mnBottomLine; /// Style of bottom line.
+ sal_uInt8 mnDiagLine; /// Style of diagonal line(s).
+ bool mbDiagTLtoBR; /// true = Top-left to bottom-right on.
+ bool mbDiagBLtoTR; /// true = Bottom-left to top-right on.
+
+ explicit XclCellBorder();
+};
+
+bool operator==( const XclCellBorder& rLeft, const XclCellBorder& rRight );
+
+/** Contains background colors and pattern for a cell. */
+struct XclCellArea
+{
+ sal_uInt16 mnForeColor; /// Palette index to foreground color.
+ sal_uInt16 mnBackColor; /// Palette index to background color.
+ sal_uInt8 mnPattern; /// Fill pattern.
+
+ explicit XclCellArea();
+ explicit XclCellArea(sal_uInt8 nPattern);
+
+ /** Returns true, if the area represents transparent state. */
+ bool IsTransparent() const;
+};
+
+bool operator==( const XclCellArea& rLeft, const XclCellArea& rRight );
+
+/** Contains base members for XF record import/export.
+ @descr In detail this class stores the XF type (cell/style), the index to the
+ parent style XF and all "attribute used" flags, which reflect the state of
+ specific attribute groups (true = user has changed the attributes). */
+class XclXFBase
+{
+public:
+ explicit XclXFBase( bool bCellXF );
+ virtual ~XclXFBase();
+
+ XclXFBase(XclXFBase const &) = default;
+ XclXFBase(XclXFBase &&) = default;
+ XclXFBase & operator =(XclXFBase const &) = default;
+ XclXFBase & operator =(XclXFBase &&) = default;
+
+ /** Sets all "attribute used" flags to the passed state. */
+ void SetAllUsedFlags( bool bUsed );
+ /** Returns true, if any "attribute used" flags are ste in this XF. */
+ bool HasUsedFlags() const;
+
+ /** Returns true, if this is a hard cell format. */
+ bool IsCellXF() const { return mbCellXF; }
+ /** Returns true, if this is a cell style. */
+ bool IsStyleXF() const { return !IsCellXF(); }
+
+protected:
+ /** Returns true, if this object is equal to the passed. */
+ bool Equals( const XclXFBase& rCmp ) const;
+
+protected:
+ sal_uInt16 mnParent; /// Index to parent style XF.
+ bool mbCellXF; /// true = cell XF, false = style XF.
+ bool mbProtUsed; /// true = cell protection used.
+ bool mbFontUsed; /// true = font index used.
+ bool mbFmtUsed; /// true = number format used.
+ bool mbAlignUsed; /// true = alignment used.
+ bool mbBorderUsed; /// true = border data used.
+ bool mbAreaUsed; /// true = area data used.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xltable.hxx b/sc/source/filter/inc/xltable.hxx
new file mode 100644
index 0000000000..d5e210a4a4
--- /dev/null
+++ b/sc/source/filter/inc/xltable.hxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+// Constants and Enumerations =================================================
+
+// Specials for outlines ------------------------------------------------------
+
+const sal_uInt8 EXC_OUTLINE_MAX = 7;
+const sal_uInt8 EXC_OUTLINE_COUNT = EXC_OUTLINE_MAX + 1;
+
+// (0x0000, 0x0200) DIMENSIONS ------------------------------------------------
+const sal_uInt16 EXC_ID2_DIMENSIONS = 0x0000;
+const sal_uInt16 EXC_ID3_DIMENSIONS = 0x0200;
+
+// (0x0001, 0x0201) BLANK -----------------------------------------------------
+const sal_uInt16 EXC_ID2_BLANK = 0x0001;
+const sal_uInt16 EXC_ID3_BLANK = 0x0201;
+
+// (0x0002) INTEGER -----------------------------------------------------------
+const sal_uInt16 EXC_ID2_INTEGER = 0x0002;
+
+// (0x0003, 0x0203) NUMBER ----------------------------------------------------
+const sal_uInt16 EXC_ID2_NUMBER = 0x0003;
+const sal_uInt16 EXC_ID3_NUMBER = 0x0203;
+
+// (0x0004, 0x0204) LABEL -----------------------------------------------------
+const sal_uInt16 EXC_ID2_LABEL = 0x0004;
+const sal_uInt16 EXC_ID3_LABEL = 0x0204;
+
+const sal_uInt8 EXC_LABEL_MAXLEN = 0xFF;
+
+// (0x0005, 0x0205) BOOLERR ---------------------------------------------------
+const sal_uInt16 EXC_ID2_BOOLERR = 0x0005;
+const sal_uInt16 EXC_ID3_BOOLERR = 0x0205;
+
+const sal_uInt8 EXC_BOOLERR_BOOL = 0x00;
+const sal_uInt8 EXC_BOOLERR_ERROR = 0x01;
+
+// (0x0006, 0x0206, 0x0406) FORMULA -------------------------------------------
+const sal_uInt16 EXC_ID2_FORMULA = 0x0006;
+const sal_uInt16 EXC_ID3_FORMULA = 0x0206;
+const sal_uInt16 EXC_ID4_FORMULA = 0x0406;
+
+const sal_uInt16 EXC_FORMULA_RECALC_ALWAYS = 0x0001;
+const sal_uInt16 EXC_FORMULA_RECALC_ONLOAD = 0x0002;
+const sal_uInt16 EXC_FORMULA_SHARED = 0x0008;
+const sal_uInt16 EXC_FORMULA_DEFAULTFLAGS = 0x0000;
+
+const sal_uInt8 EXC_FORMULA_RES_STRING = 0x00; /// Result is a string.
+const sal_uInt8 EXC_FORMULA_RES_BOOL = 0x01; /// Result is Boolean value.
+const sal_uInt8 EXC_FORMULA_RES_ERROR = 0x02; /// Result is error code.
+const sal_uInt8 EXC_FORMULA_RES_EMPTY = 0x03; /// Result is empty cell (BIFF8 only).
+
+// (0x0007, 0x0207) STRING ----------------------------------------------------
+const sal_uInt16 EXC_ID2_STRING = 0x0007;
+const sal_uInt16 EXC_ID3_STRING = 0x0207;
+
+// (0x0008, 0x0208) ROW -------------------------------------------------------
+const sal_uInt16 EXC_ID2_ROW = 0x0008;
+const sal_uInt16 EXC_ID3_ROW = 0x0208;
+
+const sal_uInt16 EXC_ROW_COLLAPSED = 0x0010;
+const sal_uInt16 EXC_ROW_HIDDEN = 0x0020;
+const sal_uInt16 EXC_ROW_UNSYNCED = 0x0040;
+const sal_uInt16 EXC_ROW_USEDEFXF = 0x0080;
+const sal_uInt16 EXC_ROW_DEFAULTFLAGS = 0x0100;
+
+const sal_uInt16 EXC_ROW_XFMASK = 0x0FFF;
+
+const sal_uInt16 EXC_ROW_DEFAULTHEIGHT = 255;
+const sal_uInt16 EXC_ROW_FLAGDEFHEIGHT = 0x8000;
+const sal_uInt16 EXC_ROW_HEIGHTMASK = 0x7FFF;
+
+const sal_uInt16 EXC_ROW_ROWBLOCKSIZE = 32; /// Number of rows in a row block.
+
+// (0x0020) COLUMNDEFAULT -----------------------------------------------------
+const sal_uInt16 EXC_ID_COLUMNDEFAULT = 0x0020;
+
+// (0x0021, 0x0221) ARRAY -----------------------------------------------------
+const sal_uInt16 EXC_ID2_ARRAY = 0x0021;
+const sal_uInt16 EXC_ID3_ARRAY = 0x0221;
+
+const sal_uInt16 EXC_ARRAY_RECALC_ALWAYS = 0x0001;
+const sal_uInt16 EXC_ARRAY_RECALC_ONLOAD = 0x0002;
+const sal_uInt16 EXC_ARRAY_DEFAULTFLAGS = EXC_ARRAY_RECALC_ONLOAD;
+
+// (0x0024) COLWIDTH ----------------------------------------------------------
+const sal_uInt16 EXC_ID_COLWIDTH = 0x0024;
+
+// (0x0025, 0x0225) DEFAULTROWHEIGHT ------------------------------------------
+const sal_uInt16 EXC_ID2_DEFROWHEIGHT = 0x0025;
+const sal_uInt16 EXC_ID3_DEFROWHEIGHT = 0x0225;
+
+const sal_uInt16 EXC_DEFROW_UNSYNCED = 0x0001;
+const sal_uInt16 EXC_DEFROW_HIDDEN = 0x0002;
+const sal_uInt16 EXC_DEFROW_SPACEABOVE = 0x0004;
+const sal_uInt16 EXC_DEFROW_SPACEBELOW = 0x0008;
+const sal_uInt16 EXC_DEFROW_DEFAULTFLAGS = 0x0000;
+
+const sal_uInt16 EXC_DEFROW_DEFAULTHEIGHT = 255;
+
+// (0x0036, 0x0236) TABLEOP ---------------------------------------------------
+const sal_uInt16 EXC_ID2_TABLEOP = 0x0036;
+const sal_uInt16 EXC_ID3_TABLEOP = 0x0236;
+
+const sal_uInt16 EXC_TABLEOP_RECALC_ALWAYS = 0x0001;
+const sal_uInt16 EXC_TABLEOP_RECALC_ONLOAD = 0x0002;
+const sal_uInt16 EXC_TABLEOP_ROW = 0x0004;
+const sal_uInt16 EXC_TABLEOP_BOTH = 0x0008;
+const sal_uInt16 EXC_TABLEOP_DEFAULTFLAGS = EXC_TABLEOP_RECALC_ONLOAD;
+
+// (0x0037) TABLEOP2 ----------------------------------------------------------
+const sal_uInt16 EXC_ID2_TABLEOP2 = 0x0037;
+
+// (0x0055) DEFCOLWIDTH -------------------------------------------------------
+const sal_uInt16 EXC_ID_DEFCOLWIDTH = 0x0055;
+const sal_uInt16 EXC_DEFCOLWIDTH_DEF = 10;
+
+// (0x007D) COLINFO -----------------------------------------------------------
+const sal_uInt16 EXC_ID_COLINFO = 0x007D;
+
+const sal_uInt16 EXC_COLINFO_HIDDEN = 0x0001;
+const sal_uInt16 EXC_COLINFO_CUSTOMWIDTH = 0x0002;
+const sal_uInt16 EXC_COLINFO_COLLAPSED = 0x1000;
+
+// (0x0080) GUTS --------------------------------------------------------------
+const sal_uInt16 EXC_ID_GUTS = 0x0080;
+
+// (0x00BD) MULRK -------------------------------------------------------------
+const sal_uInt16 EXC_ID_MULRK = 0x00BD;
+
+// (0x00BE) MULBLANK ----------------------------------------------------------
+const sal_uInt16 EXC_ID_MULBLANK = 0x00BE;
+
+// (0x00D6) RSTRING -----------------------------------------------------------
+const sal_uInt16 EXC_ID_RSTRING = 0x00D6;
+
+// (0x00FD) LABELSST ----------------------------------------------------------
+const sal_uInt16 EXC_ID_LABELSST = 0x00FD;
+
+// (0x027E) RK ----------------------------------------------------------------
+const sal_uInt16 EXC_ID_RK = 0x027E;
+
+// (0x04BC) SHRFMLA -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SHRFMLA = 0x04BC;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xltools.hxx b/sc/source/filter/inc/xltools.hxx
new file mode 100644
index 0000000000..f67d896af9
--- /dev/null
+++ b/sc/source/filter/inc/xltools.hxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include "ftools.hxx"
+
+#include <tools/long.hxx>
+#include <tools/degree.hxx>
+
+class SfxObjectShell;
+enum class FormulaError : sal_uInt16;
+
+// BIFF versions ==============================================================
+
+#define DBG_ERROR_BIFF() OSL_FAIL( "Unknown BIFF type!" )
+#define OSL_ENSURE_BIFF( c ) OSL_ENSURE( c, "Unknown BIFF type!" )
+
+// Enumerations ===============================================================
+
+/** An enumeration for all Excel error codes and the values true and false. */
+enum XclBoolError
+{
+ xlErrNull, /// The error code #NULL!
+ xlErrDiv0, /// The error code #DIV/0!
+ xlErrValue, /// The error code #VALUE!
+ xlErrRef, /// The error code #REF!
+ xlErrName, /// The error code #NAME?
+ xlErrNum, /// The error code #NUM!
+ xlErrNA, /// The error code #N/A!
+ xlErrTrue, /// The Boolean value true.
+ xlErrFalse, /// The Boolean value false.
+ xlErrUnknown /// For unknown codes and values.
+};
+
+// GUID import/export =========================================================
+
+class XclImpStream;
+class XclExpStream;
+
+/** This struct stores a GUID (class ID) and supports reading, writing and comparison. */
+struct XclGuid
+{
+ sal_uInt8 mpnData[ 16 ]; /// Stores GUID always in little endian.
+
+ explicit XclGuid();
+ explicit XclGuid(
+ sal_uInt32 nData1,
+ sal_uInt16 nData2, sal_uInt16 nData3,
+ sal_uInt8 nData41, sal_uInt8 nData42,
+ sal_uInt8 nData43, sal_uInt8 nData44,
+ sal_uInt8 nData45, sal_uInt8 nData46,
+ sal_uInt8 nData47, sal_uInt8 nData48 );
+};
+
+bool operator==( const XclGuid& rCmp1, const XclGuid& rCmp2 );
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclGuid& rGuid );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclGuid& rGuid );
+
+// Excel Tools ================================================================
+
+/** This class contains static helper methods for the Excel import and export filters. */
+class XclTools
+{
+public:
+ // noncopyable nonconstructable -------------------------------------------
+
+ XclTools(const XclTools&) = delete;
+ const XclTools& operator=(const XclTools&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static items. */
+ XclTools() = delete;
+
+
+ // GUID's -----------------------------------------------------------------
+
+ static const XclGuid maGuidStdLink; /// GUID of StdLink (HLINK record).
+ static const XclGuid maGuidUrlMoniker; /// GUID of URL moniker (HLINK record).
+ static const XclGuid maGuidFileMoniker; /// GUID of file moniker (HLINK record).
+
+ // numeric conversion -----------------------------------------------------
+
+ /** Calculates the double value from an RK value (encoded integer or double). */
+ static double GetDoubleFromRK( sal_Int32 nRKValue );
+ /** Calculates an RK value (encoded integer or double) from a double value.
+ @param rnRKValue Returns the calculated RK value.
+ @param fValue The double value.
+ @return true = An RK value could be created. */
+ static bool GetRKFromDouble( sal_Int32& rnRKValue, double fValue );
+
+ /** Calculates an angle (in 1/100 of degrees) from an Excel angle value.
+ @param nRotForStacked This value will be returned, if nXclRot contains 'stacked'. */
+ static Degree100 GetScRotation( sal_uInt16 nXclRot, Degree100 nRotForStacked );
+ /** Calculates the Excel angle value from an angle in 1/100 of degrees. */
+ static sal_uInt8 GetXclRotation( Degree100 nScRot );
+
+ /** Calculates BIFF8 rotation angle from BIFF2-BIFF5 text orientation. */
+ static sal_uInt8 GetXclRotFromOrient( sal_uInt8 nXclOrient );
+ /** Calculates BIFF2-BIFF5 text orientation from BIFF8 rotation angle. */
+ static sal_uInt8 GetXclOrientFromRot( sal_uInt16 nXclRot );
+
+ /** Converts a Calc error code to an Excel error code. */
+ static sal_uInt8 GetXclErrorCode( FormulaError nScError );
+ /** Converts an Excel error code to a Calc error code. */
+ static FormulaError GetScErrorCode( sal_uInt8 nXclError );
+
+ /** Converts the passed BIFF error to a double containing the respective Calc error code. */
+ static double ErrorToDouble( sal_uInt8 nXclError );
+ /** Gets a translated error code or Boolean value from Excel error codes.
+ @param rfDblValue Returns 0.0 for error codes or the value of a Boolean (0.0 or 1.0).
+ @param bErrorOrBool false = nError is a Boolean value; true = is an error value.
+ @param nValue The error code or Boolean value. */
+ static XclBoolError ErrorToEnum( double& rfDblValue, bool bErrOrBool, sal_uInt8 nValue );
+
+ /** Returns the length in twips calculated from a length in inches. */
+ static sal_uInt16 GetTwipsFromInch( double fInches );
+ /** Returns the length in twips calculated from a length in 1/100 mm. */
+ static sal_uInt16 GetTwipsFromHmm( sal_Int32 nHmm );
+
+ /** Returns the length in inches calculated from a length in twips. */
+ static double GetInchFromTwips( sal_Int32 nTwips );
+ /** Returns the length in inches calculated from a length in 1/100 mm. */
+ static double GetInchFromHmm( sal_Int32 nHmm );
+
+ /** Returns the length in 1/100 mm calculated from a length in inches. */
+ static sal_Int32 GetHmmFromInch( double fInches );
+ /** Returns the length in 1/100 mm calculated from a length in twips. */
+ static sal_Int32 GetHmmFromTwips( sal_Int32 nTwips );
+
+ /** Returns the Calc column width (twips) for the passed Excel width.
+ * Excel Column width is stored as 1/256th of a character.
+ @param nScCharWidth Width of the '0' character in Calc (twips). */
+ static sal_uInt16 GetScColumnWidth( sal_uInt16 nXclWidth, tools::Long nScCharWidth );
+ /** Returns the Excel column width for the passed Calc width (twips).
+ @param nScCharWidth Width of the '0' character in Calc (twips). */
+ static sal_uInt16 GetXclColumnWidth( sal_uInt16 nScWidth, tools::Long nScCharWidth );
+
+ /** Returns a correction value to convert column widths from/to default column widths.
+ @param nXclDefFontHeight Excel height of application default font. */
+ static double GetXclDefColWidthCorrection( tools::Long nXclDefFontHeight );
+
+ // formatting -------------------------------------------------------------
+
+ /** Returns the best fitting color for an Excel pattern area format. */
+ static Color GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt16 nXclPattern );
+
+ // text encoding ----------------------------------------------------------
+
+ /** Returns a text encoding from an Excel code page.
+ @return The corresponding text encoding or RTL_TEXTENCODING_DONTKNOW. */
+ static rtl_TextEncoding GetTextEncoding( sal_uInt16 nCodePage );
+
+ /** Returns an Excel code page from a text encoding. */
+ static sal_uInt16 GetXclCodePage( rtl_TextEncoding eTextEnc );
+
+ // font names -------------------------------------------------------------
+
+ /** Returns the matching Excel font name for a passed Calc font name. */
+ static OUString GetXclFontName( const OUString& rFontName );
+
+ // built-in defined names -------------------------------------------------
+
+ /** Returns the raw English UI representation of a built-in defined name used in NAME records.
+ @param cBuiltIn Excel index of the built-in name. */
+ static OUString GetXclBuiltInDefName( sal_Unicode cBuiltIn );
+ /** Returns the Calc UI representation of a built-in defined name used in NAME records.
+ @descr Adds a prefix to the representation returned by GetXclBuiltInDefName().
+ @param cBuiltIn Excel index of the built-in name. */
+ static OUString GetBuiltInDefName( sal_Unicode cBuiltIn );
+ /** Returns the Excel built-in name with OOXML prefix
+ @descr Adds the "_xlnm." prefix to the representation returned by GetXclBuiltInDefName()
+ @param cBuiltIn Excel index of the built in name.*/
+ static OUString GetBuiltInDefNameXml( sal_Unicode cBuiltIn );
+ /** Returns the Excel built-in name index of the passed defined name from Calc.
+ @descr Ignores any characters following a valid representation of a built-in name.
+ @param rDefName raw English UI representation of a built-in defined name used in NAME records.
+ @return the index of the built-in name, or EXC_BUILTIN_UNKNOWN if it is not a built-in name. */
+ static sal_Unicode GetBuiltInDefNameIndex( const OUString& rDefName );
+
+ // built-in style names ---------------------------------------------------
+
+ /** Returns the specified built-in cell style name.
+ @param nStyleId The identifier of the built-in style.
+ @param rName Default name for unknown styles.
+ @param nLevel The zero-based outline level for RowLevel and ColLevel styles.
+ @return The style name or an empty string, if the parameters are not valid. */
+ static OUString GetBuiltInStyleName( sal_uInt8 nStyleId, std::u16string_view rName, sal_uInt8 nLevel );
+
+ /** Returns true, if the passed string is a name of an Excel built-in style.
+ @param pnStyleId If not 0, the found style identifier will be returned here.
+ @param pnNextChar If not 0, the index of the char after the evaluated substring will be returned here. */
+ static bool IsBuiltInStyleName( const OUString& rStyleName, sal_uInt8* pnStyleId = nullptr, sal_Int32* pnNextChar = nullptr );
+ /** Returns the Excel built-in style identifier of a passed style name.
+ @param rnStyleId The style identifier is returned here.
+ @param rnLevel The zero-based outline level for RowLevel and ColLevel styles is returned here.
+ @param rStyleName The style name to examine.
+ @return true = passed string is a built-in style name, false = user style. */
+ static bool GetBuiltInStyleId(
+ sal_uInt8& rnStyleId, sal_uInt8& rnLevel,
+ const OUString& rStyleName );
+
+ // conditional formatting style names -------------------------------------
+
+ /** Returns the style name for a single condition of a conditional formatting.
+ @param nScTab The current Calc sheet index.
+ @param nFormat The zero-based index of the conditional formatting.
+ @param nCondition The zero-based index of the condition.
+ @return A style sheet name in the form "Excel_CondFormat_<sheet>_<format>_<condition>". */
+ static OUString GetCondFormatStyleName( SCTAB nScTab, sal_Int32 nFormat, sal_uInt16 nCondition );
+ /** Returns true, if the passed string is a name of a conditional format style created by Excel import.
+ @param pnNextChar If not 0, the index of the char after the evaluated substring will be returned here. */
+ static bool IsCondFormatStyleName( const OUString& rStyleName );
+
+ // stream handling --------------------------------------------------------
+
+ /** Skips a substream (BOF/EOF record block). Includes all embedded substreams. */
+ static void SkipSubStream( XclImpStream& rStrm );
+
+ // Basic macro names ------------------------------------------------------
+
+ /** Returns the full StarBasic macro URL from an Excel macro name. */
+ static OUString GetSbMacroUrl( const OUString& rMacroName, SfxObjectShell* pDocShell );
+ /** Returns the Excel macro name from a full StarBasic macro URL. */
+ static OUString GetXclMacroName( const OUString& rSbMacroUrl );
+
+};
+
+// read/write colors ----------------------------------------------------------
+
+/** Reads a color from the passed stream.
+ @descr The color has the format (all values 8-bit): Red, Green, Blue, 0. */
+XclImpStream& operator>>( XclImpStream& rStrm, Color& rColor );
+
+/** Reads a color to the passed stream.
+ @descr The color has the format (all values 8-bit): Red, Green, Blue, 0. */
+XclExpStream& operator<<( XclExpStream& rStrm, const Color& rColor );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xltracer.hxx b/sc/source/filter/inc/xltracer.hxx
new file mode 100644
index 0000000000..eef1901398
--- /dev/null
+++ b/sc/source/filter/inc/xltracer.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <vector>
+
+// As Trace features become implemented, we can safely delete the enum entry as
+// we use the member mnID to keep track of the actual trace tag ID value.
+enum XclTracerId
+{
+ eUnKnown , /// unused but allows us to set the correct index
+ eRowLimitExceeded ,
+ eTabLimitExceeded ,
+ ePrintRange ,
+ eShortDate ,
+ eBorderLineStyle ,
+ eFillPattern ,
+ eFormulaMissingArg ,
+ ePivotDataSource ,
+ ePivotChartExists ,
+ eChartUnKnownType ,
+ eChartOnlySheet ,
+ eChartDataTable,
+ eChartLegendPosition,
+ eUnsupportedObject ,
+ eObjectNotPrintable ,
+ eDVType,
+ eTraceLength /// this *should* always be the final entry
+};
+
+/** This class wraps an MSFilterTracer to create trace logs for import/export filters. */
+class XclTracer final
+{
+public:
+ explicit XclTracer( std::u16string_view rDocUrl );
+ ~XclTracer();
+
+ /** Returns true, if tracing is enabled. */
+ bool IsEnabled() const { return mbEnabled; }
+
+ /** Ensure that particular traces are logged once per document. */
+ void ProcessTraceOnce(XclTracerId eProblem);
+
+ void TraceInvalidAddress(const ScAddress& rPos, const ScAddress& rMaxPos);
+ void TraceInvalidRow( sal_uInt32 nRow, sal_uInt32 nMaxrow );
+ void TraceInvalidTab( SCTAB nTab, SCTAB nMaxTab);
+ void TracePrintRange();
+ void TraceDates(sal_uInt16 nNumFmt);
+ void TraceBorderLineStyle(bool bBorderLineStyle);
+ void TraceFillPattern(bool bFillPattern);
+ void TraceFormulaMissingArg();
+ void TracePivotDataSource(bool bExternal);
+ void TracePivotChartExists();
+ void TraceChartUnKnownType();
+ void TraceChartOnlySheet();
+ void TraceChartDataTable();
+ void TraceChartLegendPosition();
+ void TraceUnsupportedObjects();
+ void TraceObjectNotPrintable();
+ void TraceDVType(bool bType);
+
+private:
+ bool mbEnabled;
+ /** array of flags corresponding to each entry in the XclTracerDetails table. */
+ std::vector<bool> maFirstTimes;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlview.hxx b/sc/source/filter/inc/xlview.hxx
new file mode 100644
index 0000000000..9f9daa95c7
--- /dev/null
+++ b/sc/source/filter/inc/xlview.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <tools/color.hxx>
+#include "xladdress.hxx"
+#include <map>
+#include <memory>
+
+// Constants and enumerations =================================================
+
+const sal_uInt16 EXC_ZOOM_MIN = 10;
+const sal_uInt16 EXC_ZOOM_MAX = 400;
+
+// (0x001D) SELECTION ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SELECTION = 0x001D;
+
+// (0x003D) WINDOW1 -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_WINDOW1 = 0x003D;
+
+const sal_uInt16 EXC_WIN1_HIDDEN = 0x0001;
+const sal_uInt16 EXC_WIN1_MINIMIZED = 0x0002;
+const sal_uInt16 EXC_WIN1_HOR_SCROLLBAR = 0x0008;
+const sal_uInt16 EXC_WIN1_VER_SCROLLBAR = 0x0010;
+const sal_uInt16 EXC_WIN1_TABBAR = 0x0020;
+
+// (0x003E, 0x023E) WINDOW2 ---------------------------------------------------
+
+const sal_uInt16 EXC_ID2_WINDOW2 = 0x003E;
+const sal_uInt16 EXC_ID_WINDOW2 = 0x023E;
+
+const sal_uInt16 EXC_WIN2_SHOWFORMULAS = 0x0001;
+const sal_uInt16 EXC_WIN2_SHOWGRID = 0x0002;
+const sal_uInt16 EXC_WIN2_SHOWHEADINGS = 0x0004;
+const sal_uInt16 EXC_WIN2_FROZEN = 0x0008;
+const sal_uInt16 EXC_WIN2_SHOWZEROS = 0x0010;
+const sal_uInt16 EXC_WIN2_DEFGRIDCOLOR = 0x0020;
+const sal_uInt16 EXC_WIN2_MIRRORED = 0x0040;
+const sal_uInt16 EXC_WIN2_SHOWOUTLINE = 0x0080;
+const sal_uInt16 EXC_WIN2_FROZENNOSPLIT = 0x0100;
+const sal_uInt16 EXC_WIN2_SELECTED = 0x0200;
+const sal_uInt16 EXC_WIN2_DISPLAYED = 0x0400;
+const sal_uInt16 EXC_WIN2_PAGEBREAKMODE = 0x0800;
+
+const sal_uInt16 EXC_WIN2_NORMALZOOM_DEF = 100; /// Default zoom for normal view.
+const sal_uInt16 EXC_WIN2_PAGEZOOM_DEF = 60; /// Default zoom for pagebreak preview.
+
+// (0x0041) PANE --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_PANE = 0x0041;
+
+const sal_uInt8 EXC_PANE_BOTTOMRIGHT = 0; /// Bottom-right pane.
+const sal_uInt8 EXC_PANE_TOPRIGHT = 1; /// Right, or top-right pane.
+const sal_uInt8 EXC_PANE_BOTTOMLEFT = 2; /// Bottom, or bottom-left pane.
+const sal_uInt8 EXC_PANE_TOPLEFT = 3; /// Single, top, left, or top-left pane.
+
+// (0x00A0) SCL ---------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SCL = 0x00A0;
+
+// (0x0862) SHEETEXT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SHEETEXT = 0x0862; /// header id for sheetext
+const sal_uInt8 EXC_SHEETEXT_TABCOLOR = 0x7F; /// mask for tab color
+const sal_uInt16 EXC_COLOR_NOTABBG = 0x7F; /// Excel ignores Tab color when set to this value...
+// Structs ====================================================================
+
+/** Contains all view settings for the entire document. */
+struct XclDocViewData
+{
+ sal_uInt16 mnWinX; /// X position of the document window (twips).
+ sal_uInt16 mnWinY; /// Y position of the document window (twips).
+ sal_uInt16 mnWinWidth; /// Width of the document window (twips).
+ sal_uInt16 mnWinHeight; /// Height of the document window (twips).
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnDisplXclTab; /// Displayed (active) sheet.
+ sal_uInt16 mnFirstVisXclTab; /// First visible sheet.
+ sal_uInt16 mnXclSelectCnt; /// Number of selected sheets.
+ sal_uInt16 mnTabBarWidth; /// Width of sheet tabbar (1/1000 of window width).
+
+ explicit XclDocViewData();
+};
+
+/** Contains all settings for a selection in a single pane of a sheet. */
+struct XclSelectionData
+{
+ XclAddress maXclCursor; /// Cell cursor position.
+ XclRangeList maXclSelection; /// Selected cell ranges.
+ sal_uInt16 mnCursorIdx; /// Index of cursor in selection list.
+
+ explicit XclSelectionData() : mnCursorIdx( 0 ) {}
+};
+
+typedef std::shared_ptr< XclSelectionData > XclSelectionDataRef;
+
+/** Contains all view settings for a single sheet. */
+struct XclTabViewData
+{
+ typedef ::std::map< sal_uInt8, XclSelectionDataRef > XclSelectionMap;
+
+ XclSelectionMap maSelMap; /// Selections of all panes.
+ Color maGridColor; /// Grid color.
+ XclAddress maFirstXclPos; /// First visible cell.
+ XclAddress maSecondXclPos; /// First visible cell in additional panes.
+ sal_uInt16 mnSplitX; /// Split X position, or number of frozen columns.
+ sal_uInt32 mnSplitY; /// Split Y position, or number of frozen rows.
+ sal_uInt16 mnNormalZoom; /// Zoom factor for normal view.
+ sal_uInt16 mnPageZoom; /// Zoom factor for pagebreak preview.
+ sal_uInt16 mnCurrentZoom; /// Zoom factor for current view.
+ sal_uInt8 mnActivePane; /// Active pane (with cell cursor).
+ bool mbSelected; /// true = Sheet is selected.
+ bool mbDisplayed; /// true = Sheet is displayed (active).
+ bool mbMirrored; /// true = Mirrored (right-to-left) sheet.
+ bool mbFrozenPanes; /// true = Frozen panes; false = split window.
+ bool mbPageMode; /// true = Pagebreak preview; false = Normal view.
+ bool mbDefGridColor; /// true = Default grid color.
+ bool mbShowFormulas; /// true = Show formulas instead of results.
+ bool mbShowGrid; /// true = Show cell grid.
+ bool mbShowHeadings; /// true = Show column/row headings.
+ bool mbShowZeros; /// true = Show zero value zells.
+ bool mbShowOutline; /// true = Show outlines.
+ Color maTabBgColor; /// Tab Color default = (COL_AUTO )
+ bool IsDefaultTabBgColor() const { return maTabBgColor == COL_AUTO; };
+ sal_uInt32 mnTabBgColorId; /// palette color id
+
+ explicit XclTabViewData();
+ ~XclTabViewData();
+
+ /** Sets Excel default view settings. */
+ void SetDefaults();
+
+ /** Returns true, if the window is split in any direction. */
+ bool IsSplit() const;
+ /** Returns true, if the specified pane (EXC_PANE_*) is available. */
+ bool HasPane( sal_uInt8 nPaneId ) const;
+
+ /** Returns the selection data, if available, otherwise 0. */
+ const XclSelectionData* GetSelectionData( sal_uInt8 nPane ) const;
+ /** Returns read/write access to the selection data of the specified pane. */
+ XclSelectionData& CreateSelectionData( sal_uInt8 nPane );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/filter.cxx b/sc/source/filter/lotus/filter.cxx
new file mode 100644
index 0000000000..3c89cf6931
--- /dev/null
+++ b/sc/source/filter/lotus/filter.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 .
+ */
+
+// Discover WKS, WK1 and WK3; s.a op.cpp
+
+#include <map>
+
+#include <filter.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scerrors.hxx>
+
+#include <optab.h>
+#include <op.h>
+#include <scmem.h>
+#include <decl.h>
+#include <fprogressbar.hxx>
+#include "lotfilter.hxx"
+#include <tools/stream.hxx>
+
+static ErrCode
+generate_Opcodes(LotusContext &rContext, SvStream& aStream,
+ ScfStreamProgressBar& aPrgrsBar)
+{
+ OPCODE_FKT *pOps = nullptr;
+ int nOps = 0;
+
+ ErrCode nErr = ERRCODE_NONE;
+
+ switch (rContext.eTyp)
+ {
+ case eWK_1:
+ case eWK_2:
+ pOps = LotusContext::pOpFkt;
+ nOps = FKT_LIMIT;
+ break;
+ case eWK123:
+ pOps = LotusContext::pOpFkt123;
+ nOps = FKT_LIMIT123;
+ break;
+ case eWK3:
+ nErr = SCERR_IMPORT_NI;
+ break;
+ case eWK_Error:
+ nErr = SCERR_IMPORT_FORMAT;
+ break;
+ default:
+ nErr = SCERR_IMPORT_UNKNOWN_WK;
+ break;
+ }
+
+ if (nErr != ERRCODE_NONE)
+ {
+ MemDelete(rContext);
+ return nErr;
+ }
+
+ // #i76299# seems that SvStream::IsEof() does not work correctly
+ sal_uInt64 const nStrmSize = aStream.TellEnd();
+ aStream.Seek( STREAM_SEEK_TO_BEGIN );
+ while (!rContext.bEOF && aStream.good() && (aStream.Tell() < nStrmSize))
+ {
+ sal_uInt16 nOpcode(LOTUS_EOF), nLength(0);
+
+ aStream.ReadUInt16(nOpcode).ReadUInt16(nLength);
+ if (!aStream.good())
+ break;
+
+ aPrgrsBar.Progress();
+ if( nOpcode == LOTUS_EOF )
+ rContext.bEOF = true;
+ else if( nOpcode == LOTUS_FILEPASSWD )
+ {
+ nErr = SCERR_IMPORT_FILEPASSWD;
+ break;
+ }
+ else if( nOpcode < nOps )
+ pOps[ nOpcode ] (rContext, aStream, nLength);
+ else if (rContext.eTyp == eWK123 && nOpcode == LOTUS_PATTERN)
+ {
+ // This is really ugly - needs re-factoring ...
+ aStream.SeekRel(nLength);
+ aStream.ReadUInt16( nOpcode ).ReadUInt16( nLength );
+ if ( nOpcode == 0x29a)
+ {
+ aStream.SeekRel(nLength);
+ aStream.ReadUInt16( nOpcode ).ReadUInt16( nLength );
+ if ( nOpcode == 0x804 )
+ {
+ aStream.SeekRel(nLength);
+ OP_ApplyPatternArea123(rContext, aStream);
+ }
+ else
+ aStream.SeekRel(nLength);
+ }
+ else
+ aStream.SeekRel(nLength);
+ }
+ else
+ aStream.SeekRel( nLength );
+ }
+
+ MemDelete(rContext);
+
+ if (!aStream.good())
+ nErr = SCERR_IMPORT_FORMAT;
+ else if (nErr == ERRCODE_NONE)
+ rContext.rDoc.CalcAfterLoad();
+
+ return nErr;
+}
+
+static WKTYP ScanVersion(SvStream& aStream)
+{
+ // PREC: pWKFile: pointer to open file
+ // POST: return: type of file
+ sal_uInt16 nOpcode(0), nVersNr(0), nRecLen(0);
+
+ // first byte has to be 0 because of BOF!
+ aStream.ReadUInt16( nOpcode );
+ if (nOpcode != LotusContext::nBOF)
+ return eWK_UNKNOWN;
+
+ aStream.ReadUInt16( nRecLen ).ReadUInt16( nVersNr );
+
+ if (!aStream.good())
+ return eWK_Error;
+
+ switch( nVersNr )
+ {
+ case 0x0404:
+ if( nRecLen == 2 )
+ return eWK_1;
+ else
+ return eWK_UNKNOWN;
+
+ case 0x0406:
+ if( nRecLen == 2 )
+ return eWK_2;
+ else
+ return eWK_UNKNOWN;
+
+ case 0x1000:
+ aStream.ReadUInt16( nVersNr );
+ if (!aStream.good())
+ return eWK_Error;
+ if( nVersNr == 0x0004 && nRecLen == 26 )
+ {
+ // 4 bytes of 26 read => skip 22 (read instead of seek to make IsEof() work just in case)
+ char aDummy[22];
+ aStream.ReadBytes(aDummy, 22);
+ return !aStream.good() ? eWK_Error : eWK3;
+ }
+ break;
+ case 0x1003:
+ if( nRecLen == 0x1a )
+ return eWK123;
+ else
+ return eWK_UNKNOWN;
+ case 0x1005:
+ if( nRecLen == 0x1a )
+ return eWK123;
+ else
+ return eWK_UNKNOWN;
+ }
+
+ return eWK_UNKNOWN;
+}
+
+ErrCode ScImportLotus123old(LotusContext& rContext, SvStream& aStream, rtl_TextEncoding eSrc )
+{
+ aStream.Seek( 0 );
+
+ // make document pointer global
+ rContext.bEOF = false;
+ rContext.eCharset = eSrc;
+
+ // allocate memory
+ if( !MemNew(rContext) )
+ return SCERR_IMPORT_OUTOFMEM;
+
+ // initialize page format (only Tab 0!)
+ // initialize page format; meaning: get defaults from SC TODO:
+ //scGetPageFormat( 0, &aPage );
+
+ // start progressbar
+ ScfStreamProgressBar aPrgrsBar( aStream, rContext.rDoc.GetDocumentShell() );
+
+ // detect file type
+ rContext.eTyp = ScanVersion(aStream);
+ rContext.aLotusPatternPool.clear();
+
+ return generate_Opcodes(rContext, aStream, aPrgrsBar);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotattr.cxx b/sc/source/filter/lotus/lotattr.cxx
new file mode 100644
index 0000000000..458e3c677b
--- /dev/null
+++ b/sc/source/filter/lotus/lotattr.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <lotattr.hxx>
+
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <sal/log.hxx>
+
+#include <docpool.hxx>
+#include <document.hxx>
+#include <lotfntbf.hxx>
+#include "lotfilter.hxx"
+#include <patattr.hxx>
+#include <scitems.hxx>
+
+using namespace ::com::sun::star;
+
+LotAttrCache::ENTRY::ENTRY (std::unique_ptr<ScPatternAttr> p)
+ : pPattAttr(std::move(p))
+ , nHash0(0)
+{
+}
+
+LotAttrCache::ENTRY::~ENTRY ()
+{
+}
+
+LotAttrCache::LotAttrCache (LotusContext& rContext)
+ : mrContext(rContext)
+{
+ pDocPool = rContext.rDoc.GetPool();
+
+ pColTab.reset( new Color [ 8 ] );
+ pColTab[ 0 ] = COL_WHITE;
+ pColTab[ 1 ] = COL_LIGHTBLUE;
+ pColTab[ 2 ] = COL_LIGHTGREEN;
+ pColTab[ 3 ] = COL_LIGHTCYAN;
+ pColTab[ 4 ] = COL_LIGHTRED;
+ pColTab[ 5 ] = COL_LIGHTMAGENTA;
+ pColTab[ 6 ] = COL_YELLOW;
+ pColTab[ 7 ] = COL_BLACK;
+
+ ppColorItems[ 0 ].reset( new SvxColorItem( GetColor( 1 ), ATTR_FONT_COLOR ) ); // 1
+ ppColorItems[ 1 ].reset( new SvxColorItem( GetColor( 2 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 2 ].reset( new SvxColorItem( GetColor( 3 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 3 ].reset( new SvxColorItem( GetColor( 4 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 4 ].reset( new SvxColorItem( GetColor( 5 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 5 ].reset( new SvxColorItem( GetColor( 6 ), ATTR_FONT_COLOR ) ); // 6
+
+ pWhite.reset( new SvxColorItem( COL_WHITE, ATTR_FONT_COLOR ) );
+}
+
+LotAttrCache::~LotAttrCache()
+{
+}
+
+const ScPatternAttr& LotAttrCache::GetPattAttr( const LotAttrWK3& rAttr )
+{
+ sal_uInt32 nRefHash;
+ MakeHash( rAttr, nRefHash );
+
+ std::vector< std::unique_ptr<ENTRY> >::const_iterator iter
+ = std::find_if(aEntries.begin(),aEntries.end(),
+ [nRefHash] (const std::unique_ptr<ENTRY>& rEntry) { return rEntry->nHash0 == nRefHash; } );
+
+ if (iter != aEntries.end())
+ return *((*iter)->pPattAttr);
+
+ // generate new Pattern Attribute
+ ScPatternAttr* pNewPatt = new ScPatternAttr(pDocPool);
+
+ SfxItemSet& rItemSet = pNewPatt->GetItemSet();
+ ENTRY *pCurrent = new ENTRY( std::unique_ptr<ScPatternAttr>(pNewPatt) );
+
+ pCurrent->nHash0 = nRefHash;
+
+ mrContext.maFontBuff.Fill( rAttr.nFont, rItemSet );
+
+ sal_uInt8 nLine = rAttr.nLineStyle;
+ if( nLine )
+ {
+ SvxBoxItem aBox( ATTR_BORDER );
+ ::editeng::SvxBorderLine aTop, aLeft, aBottom, aRight;
+
+ LotusToScBorderLine( nLine, aLeft );
+ nLine >>= 2;
+ LotusToScBorderLine( nLine, aRight );
+ nLine >>= 2;
+ LotusToScBorderLine( nLine, aTop );
+ nLine >>= 2;
+ LotusToScBorderLine( nLine, aBottom );
+
+ aBox.SetLine( &aTop, SvxBoxItemLine::TOP );
+ aBox.SetLine( &aLeft, SvxBoxItemLine::LEFT );
+ aBox.SetLine( &aBottom, SvxBoxItemLine::BOTTOM );
+ aBox.SetLine( &aRight, SvxBoxItemLine::RIGHT );
+
+ rItemSet.Put( aBox );
+ }
+
+ sal_uInt8 nFontCol = rAttr.nFontCol & 0x07;
+ if( nFontCol )
+ {
+ // nFontCol > 0
+ if( nFontCol < 7 )
+ rItemSet.Put( GetColorItem( nFontCol ) );
+ else
+ rItemSet.Put( *pWhite );
+ }
+
+ sal_uInt8 nBack = rAttr.nBack & 0x1F;
+ if( nBack )
+ rItemSet.Put( SvxBrushItem( GetColor( nBack & 0x07 ), ATTR_BACKGROUND ) );
+
+ if( rAttr.nBack & 0x80 )
+ {
+ SvxHorJustifyItem aHorJustify(SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY );
+ rItemSet.Put( aHorJustify );
+ }
+
+ aEntries.push_back(std::unique_ptr<ENTRY>(pCurrent));
+
+ return *pNewPatt;
+}
+
+void LotAttrCache::LotusToScBorderLine( sal_uInt8 nLine, ::editeng::SvxBorderLine& aBL )
+{
+ nLine &= 0x03;
+
+ switch ( nLine )
+ {
+ case 0: aBL.SetBorderLineStyle(SvxBorderLineStyle::NONE); break;
+ case 1: aBL.SetWidth( SvxBorderLineWidth::Thin ); break;
+ case 2: aBL.SetWidth( SvxBorderLineWidth::Medium ); break;
+ case 3:
+ {
+ aBL.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE_THIN);
+ aBL.SetWidth( SvxBorderLineWidth::Thin );
+ }
+ break;
+ }
+}
+
+const SvxColorItem& LotAttrCache::GetColorItem( const sal_uInt8 nLotIndex ) const
+{
+ // *LotAttrCache::GetColorItem(): caller has to check index!
+ assert( nLotIndex > 0 && nLotIndex < 7 );
+
+ return *ppColorItems[ nLotIndex - 1 ];
+}
+
+const Color& LotAttrCache::GetColor( const sal_uInt8 nLotIndex ) const
+{
+ // color <-> index fits background, but not for fonts (0 <-> 7)!
+ // *LotAttrCache::GetColor(): Index > 7, caller hast to check index!"
+ assert( nLotIndex < 8 );
+
+ return pColTab[ nLotIndex ];
+}
+
+void LotAttrCol::SetAttr( const ScDocument* pDoc, const SCROW nRow, const ScPatternAttr& rAttr )
+{
+ // Actually with the current implementation of MAXROWCOUNT1>=64k and nRow
+ // being read as sal_uInt16 there's no chance that nRow would be invalid...
+ SAL_WARN_IF( !pDoc->ValidRow(nRow), "sc.filter", "*LotAttrCol::SetAttr(): ... and failed?!" );
+
+ std::vector<std::unique_ptr<ENTRY> >::reverse_iterator iterLast = aEntries.rbegin();
+
+ if(iterLast != aEntries.rend())
+ {
+ if( ( (*iterLast)->nLastRow == nRow - 1 ) && SfxPoolItem::areSame( &rAttr, (*iterLast)->pPattAttr ) )
+ (*iterLast)->nLastRow = nRow;
+ else
+ {
+ ENTRY *pCurrent = new ENTRY;
+
+ pCurrent->pPattAttr = &rAttr;
+ pCurrent->nFirstRow = pCurrent->nLastRow = nRow;
+
+ aEntries.push_back(std::unique_ptr<ENTRY>(pCurrent));
+ }
+ }
+ else
+ { // first entry
+ ENTRY *pCurrent = new ENTRY;
+ pCurrent->pPattAttr = &rAttr;
+ pCurrent->nFirstRow = pCurrent->nLastRow = nRow;
+
+ aEntries.push_back(std::unique_ptr<ENTRY>(pCurrent));
+ }
+}
+
+void LotAttrCol::Apply(LotusContext& rContext, const SCCOL nColNum, const SCTAB nTabNum)
+{
+ ScDocument& rDoc = rContext.rDoc;
+
+ for (const auto& rxEntry : aEntries)
+ {
+ rDoc.ApplyPatternAreaTab(nColNum, rxEntry->nFirstRow, nColNum, rxEntry->nLastRow,
+ nTabNum, *(rxEntry->pPattAttr));
+ }
+}
+
+LotAttrTable::LotAttrTable(LotusContext& rContext):
+ aAttrCache(rContext)
+{
+}
+
+void LotAttrTable::SetAttr( const LotusContext& rContext, const SCCOL nColFirst, const SCCOL nColLast, const SCROW nRow,
+ const LotAttrWK3& rAttr )
+{
+ // With the current implementation of MAXCOLCOUNT>=1024 and nColFirst and
+ // nColLast being calculated as sal_uInt8+sal_uInt8 there's no chance that
+ // they would be invalid.
+ const ScPatternAttr &rPattAttr = aAttrCache.GetPattAttr( rAttr );
+ SCCOL nColCnt;
+
+ for( nColCnt = nColFirst ; nColCnt <= nColLast ; nColCnt++ )
+ pCols[ nColCnt ].SetAttr( &rContext.rDoc, nRow, rPattAttr );
+}
+
+void LotAttrTable::Apply(LotusContext& rContext, const SCTAB nTabNum)
+{
+ SCCOL nColCnt;
+ for( nColCnt = 0 ; nColCnt <= aAttrCache.mrContext.rDoc.MaxCol() ; nColCnt++ )
+ pCols[ nColCnt ].Apply(rContext, nColCnt, nTabNum); // does a Clear() at end
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotfilter.hxx b/sc/source/filter/lotus/lotfilter.hxx
new file mode 100644
index 0000000000..596b9a9de7
--- /dev/null
+++ b/sc/source/filter/lotus/lotfilter.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <decl.h>
+#include <optab.h>
+#include <patattr.hxx>
+#include <lotattr.hxx>
+#include <lotrange.hxx>
+#include <lotfntbf.hxx>
+#include <flttypes.hxx>
+#include <namebuff.hxx>
+
+class SvxHorJustifyItem;
+class FormCache;
+
+struct LotusContext
+{
+ static const sal_uInt16 nBOF = 0x0000;
+ static OPCODE_FKT pOpFkt[ FKT_LIMIT ];
+ static OPCODE_FKT pOpFkt123[ FKT_LIMIT123 ]; // -> optab.cxx; table of possible Opcodes
+
+ WKTYP eTyp; // type of file being processed
+ bool bEOF; // shows end of file
+ rtl_TextEncoding eCharset;
+ ScDocument& rDoc; // reference to access document
+ std::map<sal_uInt16, ScPatternAttr> aLotusPatternPool;
+
+ std::unique_ptr<SvxHorJustifyItem> xAttrRight, xAttrLeft, xAttrCenter, xAttrRepeat, xAttrStandard;
+
+ std::unique_ptr<FormCache> xValueFormCache; // -> initialized in memory.cxx
+
+ LotusRangeList maRangeNames;
+ Lotus123Typ eFirstType;
+ Lotus123Typ eActType;
+ ScRange aActRange;
+ std::unique_ptr<RangeNameBufferWK3> pRngNmBffWK3;
+ LotusFontBuffer maFontBuff;
+ LotAttrTable maAttrTable;
+
+ LotusContext(ScDocument& rDocP, rtl_TextEncoding eQ);
+ ~LotusContext();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotform.cxx b/sc/source/filter/lotus/lotform.cxx
new file mode 100644
index 0000000000..88ddfcfe25
--- /dev/null
+++ b/sc/source/filter/lotus/lotform.cxx
@@ -0,0 +1,2106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <decl.h>
+#include <lotform.hxx>
+#include "lotfilter.hxx"
+#include <lotrange.hxx>
+#include <namebuff.hxx>
+#include <ftools.hxx>
+#include <tool.h>
+#include <document.hxx>
+
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+#include <memory>
+#include <string_view>
+
+static const char* GetAddInName( const sal_uInt8 nIndex );
+
+static DefTokenId lcl_KnownAddIn(std::string_view rTest);
+
+void LotusToSc::DoFunc( DefTokenId eOc, sal_uInt8 nCnt, const char* pExtString )
+{
+ TokenId eParam[ 256 ];
+ sal_Int32 nPass;
+ TokenId nBuf0;
+
+ bool bAddIn = false;
+
+ SAL_WARN_IF( nCnt > 128, "sc.filter", "-LotusToSc::DoFunc(): Too many (128)!" );
+
+ if( eOc == ocNoName )
+ {
+ OString t;
+ if( pExtString )
+ {
+ static constexpr std::string_view s("@<<@123>>");
+
+ t = pExtString;
+
+ sal_Int32 n = t.indexOf(s);
+ if( n != -1 )
+ t = t.copy(n + s.size());
+
+ t = comphelper::string::stripEnd(t, '(');
+
+ eOc = lcl_KnownAddIn( t );
+
+ if( eOc == ocNoName )
+ t = "L123_" + t;
+ }
+ else
+ t = "#UNKNOWN FUNC NAME#"_ostr;
+
+ if( eOc == ocNoName )
+ {
+ bAddIn = true;
+ nBuf0 = aPool.Store(eOc, OStringToOUString(t, eSrcChar));
+
+ aPool << nBuf0;
+ }
+ }
+
+ for( nPass = 0 ; nPass < nCnt && aStack.HasMoreTokens() ; nPass++ )
+ aStack >> eParam[ nPass ];
+
+ if (nPass < nCnt)
+ // Adapt count to reality. All sort of binary crap is possible.
+ nCnt = static_cast<sal_uInt8>(nPass);
+
+ // special cases...
+ switch( eOc )
+ {
+ case ocIndex:
+ SAL_WARN_IF( nCnt < 2, "sc.filter", "+LotusToSc::DoFunc(): ocIndex needs at least 2 parameters!" );
+ nBuf0 = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nBuf0;
+ IncToken( eParam[ 0 ] );
+ IncToken( eParam[ 1 ] );
+ break;
+ case ocIRR:
+ {
+ SAL_WARN_IF( nCnt != 2, "sc.filter", "+LotusToSc::DoFunc(): ocIRR needs 2 parameters!" );
+ nBuf0 = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nBuf0;
+ }
+ break;
+ case ocGetYear:
+ {
+ nBuf0 = aPool.Store( 1900.0 );
+ aPool << ocOpen;
+ }
+ break;
+ case ocChoose:
+ {// 1. Parameter ++
+ if (nCnt >= 1)
+ IncToken( eParam[ nCnt - 1 ] );
+ }
+ break;
+ case ocFind:
+ case ocHLookup:
+ case ocVLookup:
+ {// last parameter ++
+ IncToken( eParam[ 0 ] );
+ }
+ break;
+ case ocMid:
+ case ocReplace:
+ {// 2. Parameter ++
+ if (nCnt >= 2)
+ IncToken( eParam[ nCnt - 2 ] );
+ }
+ break;
+ case ocRate:
+ {
+ // new quantity = 4!
+ SAL_WARN_IF( nCnt != 3, "sc.filter",
+ "*LotusToSc::DoFunc(): ZINS() needs 3 parameters!" );
+ nCnt = 4;
+ eParam[ 3 ] = eParam[ 0 ]; // 3. -> 1.
+ eParam[ 0 ] = eParam[ 2 ]; // 1. -> 4.
+ NegToken( eParam[ 1 ] ); // 2. -> -2. (+ 2. -> 3.)
+ eParam[ 2 ] = n0Token; // -> 2. as Default
+ }
+ break;
+ case ocNper:
+ {
+ SAL_WARN_IF( nCnt != 3, "sc.filter",
+ "*LotusToSc::DoFunc(): TERM() or CTERM() need 3 parameters!" );
+ nCnt = 4;
+ if ( pExtString == std::string_view("TERM") )
+ {
+ // @TERM(pmt,int,fv) -> NPER(int,-pmt,pv=0,fv)
+ NegToken( eParam[ 2 ] );
+ eParam[ 3 ] = eParam[ 1 ];
+ eParam[ 1 ] = aPool.Store( 0.0 );
+ }
+ else //CTERM()
+ {
+ // @CTERM(int,fv,pv) -> NPER(int,pmt=0,-pv,fv)
+ NegToken( eParam[ 0 ] );
+ nBuf0 = eParam[ 1 ];
+ eParam[ 1 ] = eParam[ 0 ];
+ eParam[ 0 ] = nBuf0;
+ eParam[ 3 ] = eParam[ 2 ];
+ eParam[ 2 ] = aPool.Store( 0.0 );
+ }
+ }
+ break;
+ case ocRoundUp:
+ case ocRoundDown:
+ {
+ // omit optional 3rd parameter
+ if ( nCnt == 3 )
+ {
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = eParam[ 2 ];
+ nCnt = 2;
+ }
+
+ }
+ break;
+ default:;
+ }
+
+ if( !bAddIn )
+ aPool << eOc;
+
+ aPool << ocOpen;
+
+ if( nCnt > 0 )
+ {
+ // ATTENTION: 0 is the last parameter, nCnt-1 the first one
+
+ sal_Int16 nLast = nCnt - 1;
+
+ if( eOc == ocPMT )
+ { // special case ocPMT, negate last parameter!
+ // additionally: 1. -> 3., 3. -> 2., 2. -> 1.
+ SAL_WARN_IF( nCnt != 3, "sc.filter", "+LotusToSc::DoFunc(): ocPMT needs 3 parameters!" );
+ // There should be at least 3 arguments, but with binary crap may not...
+ switch (nCnt)
+ {
+ case 1:
+ aPool << eParam[ 1 ];
+ break;
+ case 2:
+ aPool << eParam[ 1 ] << ocSep << eParam[ 0 ];
+ break;
+ default:
+ case 3:
+ aPool << eParam[ 1 ] << ocSep << eParam[ 0 ] << ocSep << ocNegSub << eParam[ 2 ];
+ break;
+ }
+ }
+ else
+ { // default
+ // [Parameter{;Parameter}]
+ aPool << eParam[ nLast ];
+
+ for( nPass = nLast - 1 ; nPass >= 0 ; nPass-- )
+ {
+ aPool << ocSep << eParam[nPass];
+ }
+ }
+ }
+
+ // special cases ...
+ if( eOc == ocGetYear )
+ {
+ aPool << ocClose << ocSub << nBuf0;
+ }
+ else if( eOc == ocFixed )
+ {
+ aPool << ocSep << ocTrue << ocOpen << ocClose;
+ }
+ else if( eOc == ocFind )
+ {
+ TokenId nBuf1 = aPool.Store();
+ DecToken( nBuf1 );
+ aPool << nBuf1;
+ }
+
+ aPool << ocClose;
+
+ aPool >> aStack;
+}
+
+void LotusToSc::LotusRelToScRel( sal_uInt16 nCol, sal_uInt16 nRow, ScSingleRefData& rSRD )
+{
+ // Column
+ if( nCol & 0x8000 )
+ {
+ if( nCol & 0x0080 )
+ nCol |= 0xFF00;
+ else
+ nCol &= 0x00FF;
+ // #i36252# first cast unsigned 16-bit to signed 16-bit, and then to SCCOL
+ rSRD.SetRelCol(static_cast<SCCOL>(static_cast<sal_Int16>(nCol)));
+ }
+ else
+ {
+ rSRD.SetAbsCol(static_cast<SCCOL>(nCol & 0x00FF));
+ }
+
+ // Row
+ if( nRow & 0x8000 )
+ {
+ rSRD.SetRowRel(true);
+ // sign correct extension
+ switch (m_rContext.eTyp)
+ {
+ // 5432 1098 7654 3210
+ // 8421 8421 8421 8421
+ // xxx xxxx xxxx
+ case eWK_1:
+ if( nRow & 0x0400 )
+ nRow |= 0xF800;
+ else
+ nRow &= 0x07FF;
+ break;
+ // 8421 8421 8421 8421
+ // x xxxx xxxx xxxx
+ case eWK_2:
+ if( nRow & 0x1000 )
+ nRow |= 0xE000;
+ else
+ nRow &= 0x1FFF;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "*LotusToSc::LotusRelToScRel(): unhandled case? " << m_rContext.eTyp );
+ }
+ }
+ else
+ {
+ rSRD.SetRowRel(false);
+ switch (m_rContext.eTyp)
+ {
+ // 5432 1098 7654 3210
+ // 8421 8421 8421 8421
+ // xxx xxxx xxxx
+ case eWK_1:
+ nRow &= 0x07FF;
+ break;
+ // 8421 8421 8421 8421
+ // xx xxxx xxxx xxxx
+ case eWK_2:
+ nRow &= 0x3FFF;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "*LotusToSc::LotusRelToScRel(): unhandled case? " << m_rContext.eTyp );
+ }
+ }
+
+ if( rSRD.IsRowRel() )
+ // #i36252# first cast unsigned 16-bit to signed 16-bit, and then to SCROW
+ rSRD.SetRelRow(static_cast<SCROW>(static_cast<sal_Int16>(nRow)));
+ else
+ rSRD.SetAbsRow(static_cast<SCROW>(nRow));
+}
+
+void LotusToSc::ReadSRD( const ScDocument& rDoc, ScSingleRefData& rSRD, sal_uInt8 nRelBit )
+{
+ sal_uInt8 nTab, nCol;
+ sal_uInt16 nRow;
+
+ Read( nRow );
+ Read( nTab );
+ Read( nCol );
+
+ if (!aIn.good())
+ {
+ SAL_WARN("sc.filter", "LotusToSc::ReadSRD short read");
+ return;
+ }
+
+ bool b3D = (static_cast<SCTAB>(nTab) != aEingPos.Tab());
+
+ rSRD.SetColRel( ( nRelBit & 0x01 ) != 0 );
+ rSRD.SetRowRel( ( nRelBit & 0x02 ) != 0 );
+ rSRD.SetTabRel( ( ( nRelBit & 0x04) != 0 ) || !b3D );
+ rSRD.SetFlag3D( b3D );
+
+ rSRD.SetAddress(rDoc.GetSheetLimits(), ScAddress(nCol, nRow, nTab), aEingPos);
+}
+
+void LotusToSc::IncToken( TokenId &rParam )
+{
+ aPool << ocOpen << rParam << nAddToken;
+ rParam = aPool.Store();
+}
+
+void LotusToSc::DecToken( TokenId &rParam )
+{
+ aPool << ocOpen << rParam << nSubToken;
+ rParam = aPool.Store();
+}
+
+void LotusToSc::NegToken( TokenId &rParam )
+{
+ aPool << ocNegSub << ocOpen << rParam << ocClose;
+ rParam = aPool.Store();
+}
+
+void LotusToSc::Reset( const ScAddress& rEingPos )
+{
+ LotusConverterBase::Reset( rEingPos );
+
+ TokenId nEins = aPool.Store( 1.0 );
+
+ aPool << ocClose << ocAdd << nEins;
+ nAddToken = aPool.Store();
+
+ aPool << ocClose << ocSub << nEins;
+ nSubToken = aPool.Store();
+
+ n0Token = aPool.Store( 0.0 );
+}
+
+LotusToSc::LotusToSc(LotusContext &rContext, SvStream &rStream, svl::SharedStringPool& rSPool,
+ rtl_TextEncoding e, bool b)
+ : LotusConverterBase(rStream, rSPool)
+ , m_rContext(rContext)
+{
+ eSrcChar = e;
+ bWK3 = false;
+ bWK123 = b;
+}
+
+typedef FUNC_TYPE ( FuncType1 ) ( sal_uInt8 );
+typedef DefTokenId ( FuncType2 ) ( sal_uInt8 );
+
+void LotusToSc::Convert( std::unique_ptr<ScTokenArray>& rpErg, sal_Int32& rRest )
+{
+ FUNC_TYPE eType = FT_NOP;
+ const char* pExtName = nullptr;
+ RangeNameBufferWK3& rRangeNameBufferWK3 = *m_rContext.pRngNmBffWK3;
+
+ ScComplexRefData aCRD;
+ aCRD.InitFlags();
+ ScSingleRefData& rR = aCRD.Ref1;
+
+ LotusRangeList& rRangeList = m_rContext.maRangeNames;
+
+ FuncType1* pIndexToType;
+ FuncType2* pIndexToToken;
+
+ if( bWK3 )
+ { // for > WK3
+ pIndexToType = IndexToTypeWK123;
+ pIndexToToken = IndexToTokenWK123;
+ }
+ else if( bWK123 )
+ {
+ pIndexToType = IndexToTypeWK123;
+ pIndexToToken = IndexToTokenWK123;
+ }
+ else
+ {
+ pIndexToType = IndexToType;
+ pIndexToToken = IndexToToken;
+
+ rR.SetRelTab(0);
+ rR.SetFlag3D( false );
+ }
+
+ aCRD.Ref2 = rR;
+
+ nBytesLeft = rRest;
+
+ while( eType ) // != FT_Return (==0)
+ {
+ sal_uInt8 nOc;
+ Read(nOc);
+
+ if( nBytesLeft < 0 )
+ {
+ rpErg = aPool.GetTokenArray(m_rContext.rDoc, aStack.Get());
+ return;
+ }
+
+ eType = pIndexToType( nOc );
+ DefTokenId eOc = pIndexToToken(nOc);
+ if( eOc == ocNoName )
+ pExtName = GetAddInName( nOc );
+
+ switch( eType )
+ {
+ case FT_Return:
+ {
+ if( bWK3 || bWK123 )
+ nBytesLeft = 0; // not used for >= WK3
+
+ rRest = nBytesLeft;
+ break;
+ }
+ case FT_NotImpl:
+ case FT_FuncFix0: DoFunc( eOc, 0, pExtName ); break;
+ case FT_FuncFix1: DoFunc( eOc, 1, pExtName ); break;
+ case FT_FuncFix2: DoFunc( eOc, 2, pExtName ); break;
+ case FT_FuncFix3: DoFunc( eOc, 3, pExtName ); break;
+ case FT_FuncFix4: DoFunc( eOc, 4, pExtName ); break;
+ case FT_FuncVar:
+ {
+ sal_uInt8 nCnt(0);
+ Read( nCnt );
+ DoFunc( eOc, nCnt, pExtName );
+ break;
+ }
+ case FT_Neg:
+ aPool << ocOpen << ocNegSub << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case FT_Op:
+ {
+ TokenId nBuf0;
+ aStack >> nBuf0;
+ aPool << aStack << eOc << nBuf0;
+ aPool >> aStack;
+ break;
+ }
+ case FT_ConstFloat:
+ {
+ double fDouble;
+ Read( fDouble );
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case FT_Variable:
+ {
+ sal_uInt16 nCol(0), nRow(0);
+ Read( nCol );
+ Read( nRow );
+
+ LotusRelToScRel( nCol, nRow, rR );
+
+ TokenId nNewId;
+
+ if( bWK3 )
+ nNewId = aPool.Store( rR );
+ else
+ {
+ LR_ID nId = rRangeList.GetIndex(rR.Col(), rR.Row());
+ if( nId == ID_FAIL )
+ // missing range
+ nNewId = aPool.Store( rR );
+ else
+ nNewId = aPool.Store( static_cast<sal_uInt16>(nId) );
+ }
+
+ aStack << nNewId;
+ break;
+ }
+ case FT_Range:
+ {
+ sal_uInt16 nColS(0), nRowS(0), nColE(0), nRowE(0);
+ Read( nColS );
+ Read( nRowS );
+ Read( nColE );
+ Read( nRowE );
+
+ LotusRelToScRel( nColS, nRowS, rR );
+ LotusRelToScRel( nColE, nRowE, aCRD.Ref2 );
+
+ TokenId nNewId;
+
+ if( bWK3 )
+ nNewId = aPool.Store( aCRD );
+ else
+ {
+ LR_ID nId = rRangeList.GetIndex(rR.Col(), rR.Row(), aCRD.Ref2.Col(), aCRD.Ref2.Row());
+
+ if( nId == ID_FAIL )
+ // missing range
+ nNewId = aPool.Store( aCRD );
+ else
+ nNewId = aPool.Store( static_cast<sal_uInt16>(nId) );
+ }
+
+ aStack << nNewId;
+ break;
+ }
+ case FT_Braces:
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case FT_ConstInt:
+ {
+ sal_Int16 nVal(0);
+ Read( nVal );
+ aStack << aPool.Store( static_cast<double>(nVal) );
+ break;
+ }
+ case FT_ConstString:
+ {
+ OUString aTmp(ScfTools::read_zeroTerminated_uInt8s_ToOUString(aIn, nBytesLeft, eSrcChar));
+ aStack << aPool.Store( aTmp );
+ break;
+ }
+ case FT_NOP:
+ break;
+ // for > WK3
+ case FT_Cref:
+ {
+ sal_uInt8 nRelBits(0);
+ Read( nRelBits );
+ ReadSRD( m_rContext.rDoc, rR, nRelBits );
+ aStack << aPool.Store( rR );
+ break;
+ }
+ case FT_Rref:
+ {
+ sal_uInt8 nRelBits(0);
+ Read( nRelBits );
+ ReadCRD( m_rContext.rDoc, aCRD, nRelBits );
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case FT_Nrref:
+ {
+ OUString aTmp(ScfTools::read_zeroTerminated_uInt8s_ToOUString(aIn, nBytesLeft, eSrcChar));
+ sal_uInt16 nRngIndex;
+ if( rRangeNameBufferWK3.FindRel( aTmp, nRngIndex ) )
+ aStack << aPool.Store( nRngIndex );
+ else
+ {
+ OUString aText = "NRREF " + aTmp;
+ aStack << aPool.Store( aText );
+ }
+ break;
+ }
+ case FT_Absnref:
+ {
+ OUString aTmp(ScfTools::read_zeroTerminated_uInt8s_ToOUString(aIn, nBytesLeft, eSrcChar));
+ sal_uInt16 nRngIndex;
+ if( rRangeNameBufferWK3.FindAbs( aTmp, nRngIndex ) )
+ aStack << aPool.Store( nRngIndex );
+ else
+ {
+ OUString aText = "ABSNREF " + aTmp;
+ aStack << aPool.Store( aText );
+ }
+ break;
+ }
+ case FT_Erref:
+ Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case FT_Ecref:
+ Ignore( 5 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case FT_Econstant:
+ Ignore( 10 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case FT_Splfunc:
+ {
+ sal_uInt8 nCnt(0);
+ Read( nCnt );
+ sal_uInt16 nStrLen(0);
+ Read( nStrLen );
+
+ const size_t nMaxEntries = aIn.remainingSize();
+ if (nStrLen > nMaxEntries)
+ nStrLen = nMaxEntries;
+
+ if( nStrLen )
+ {
+ std::vector<char> aBuffer(nStrLen + 1);
+ aBuffer[aIn.ReadBytes(aBuffer.data(), nStrLen)] = 0;
+ DoFunc(ocNoName, nCnt, aBuffer.data());
+ }
+ else
+ DoFunc( ocNoName, nCnt, nullptr );
+ break;
+ }
+ case FT_Const10Float:
+ {
+ double fValue;
+ if (bWK123)
+ Read(fValue);
+ else
+ ScfTools::ReadLongDouble(aIn, fValue);
+ aStack << aPool.Store(fValue);
+ break;
+ }
+ case FT_Snum:
+ {
+ if ( bWK123 )
+ {
+ sal_uInt32 nValue(0);
+ Read(nValue);
+ double fValue = Snum32ToDouble( nValue );
+ aStack << aPool.Store( fValue );
+ }
+ else
+ {
+ sal_Int16 nVal(0);
+ Read(nVal);
+ aStack << aPool.Store( SnumToDouble( nVal ) );
+ }
+ break;
+ }
+ default:
+ SAL_WARN( "sc.filter", "*LotusToSc::Convert(): unknown enum!" );
+ }
+ }
+
+ rpErg = aPool.GetTokenArray( m_rContext.rDoc, aStack.Get());
+
+ SAL_WARN_IF( nBytesLeft < 0, "sc.filter", "*LotusToSc::Convert(): processed too much!");
+ SAL_WARN_IF( nBytesLeft > 0, "sc.filter", "*LotusToSc::Convert(): what happens with the rest?" );
+
+ if( rRest )
+ aIn.SeekRel( nBytesLeft ); // Correct any remainder/overflow
+
+ rRest = 0;
+}
+
+FUNC_TYPE LotusToSc::IndexToType( sal_uInt8 nIndex )
+{
+ static const FUNC_TYPE pType[ 256 ] =
+ { // Code Description
+ FT_ConstFloat, // 0 8-Byte-IEEE-Float
+ FT_Variable, // 1 Variable
+ FT_Range, // 2 Range
+ FT_Return, // 3 return
+ FT_Braces, // 4 Braces
+ FT_ConstInt, // 5 2-Byte integer
+ FT_ConstString, // 6 ASCII string
+ FT_NOP, // 7 NOP
+ FT_Neg, // 8 Negation
+ FT_Op, // 9 Addition
+ FT_Op, // 10 Subtraction
+ FT_Op, // 11 Multiplication
+ FT_Op, // 12 Division
+ FT_Op, // 13 Power of
+ FT_Op, // 14 equal
+ FT_Op, // 15 unequal
+ FT_Op, // 16 <=
+ FT_Op, // 17 >=
+ FT_Op, // 18 <
+ FT_Op, // 19 >
+ FT_Op, // 20 And (logic)
+ FT_Op, // 21 Or (logic)
+ FT_FuncFix1, // 22 Not (logic)
+ FT_NOP, // 23 unary Plus
+ FT_NotImpl, // 24
+ FT_NotImpl, // 25
+ FT_NotImpl, // 26
+ FT_NotImpl, // 27
+ FT_NotImpl, // 28
+ FT_NotImpl, // 29
+ FT_NotImpl, // 30
+ FT_FuncFix0, // 31 Not applicable
+ FT_FuncFix0, // 32 Error
+ FT_FuncFix1, // 33 Absolute value ABS()
+ FT_FuncFix1, // 34 Integer INT()
+ FT_FuncFix1, // 35 Square Root
+ FT_FuncFix1, // 36 Log10
+ FT_FuncFix1, // 37 Natural Logarithm
+ FT_FuncFix0, // 38 PI
+ FT_FuncFix1, // 39 Sine
+ FT_FuncFix1, // 40 Cosine
+ FT_FuncFix1, // 41 Tangens
+ FT_FuncFix2, // 42 Arctangens 2 (4.Quadrant) <- TODO: correct?
+ FT_FuncFix1, // 43 Arctangens (2.Quadrant)
+ FT_FuncFix1, // 44 Arcsine
+ FT_FuncFix1, // 45 Arccosine
+ FT_FuncFix1, // 46 Exponential function
+ FT_FuncFix2, // 47 Modulo
+ FT_FuncVar, // 48 Selection
+ FT_FuncFix1, // 49 Is not applicable?
+ FT_FuncFix1, // 50 Is Error?
+ FT_FuncFix0, // 51 FALSE
+ FT_FuncFix0, // 52 TRUE
+ FT_FuncFix0, // 53 Random number
+ FT_FuncFix3, // 54 Date
+ FT_FuncFix0, // 55 Today
+ FT_FuncFix3, // 56 Payment
+ FT_FuncFix3, // 57 Present Value
+ FT_FuncFix3, // 58 Future Value
+ FT_FuncFix3, // 59 If ... then ... else ...
+ FT_FuncFix1, // 60 Day of month
+ FT_FuncFix1, // 61 Month
+ FT_FuncFix1, // 62 Year
+ FT_FuncFix2, // 63 Round
+ FT_FuncFix3, // 64 Time
+ FT_FuncFix1, // 65 Hour
+ FT_FuncFix1, // 66 Minute
+ FT_FuncFix1, // 67 Second
+ FT_FuncFix1, // 68 Is Number?
+ FT_FuncFix1, // 69 Is Text?
+ FT_FuncFix1, // 70 Len()
+ FT_FuncFix1, // 71 Val()
+ FT_FuncFix2, // 72 String()
+ FT_FuncFix3, // 73 Mid()
+ FT_FuncFix1, // 74 Char()
+ FT_FuncFix1, // 75 Ascii()
+ FT_FuncFix3, // 76 Find()
+ FT_FuncFix1, // 77 Datevalue
+ FT_FuncFix1, // 78 Timevalue
+ FT_FuncFix1, // 79 Cellpointer
+ FT_FuncVar, // 80 Sum()
+ FT_FuncVar, // 81 Avg()
+ FT_FuncVar, // 82 Cnt()
+ FT_FuncVar, // 83 Min()
+ FT_FuncVar, // 84 Max()
+ FT_FuncFix3, // 85 Vlookup()
+ FT_FuncFix2, // 86 Npv()
+ FT_FuncVar, // 87 Var()
+ FT_FuncVar, // 88 Std()
+ FT_FuncFix2, // 89 Irr()
+ FT_FuncFix3, // 90 Hlookup()
+ FT_FuncFix3, // 91 ?
+ FT_FuncFix3, // 92 ?
+ FT_FuncFix3, // 93 ?
+ FT_FuncFix3, // 94 ?
+ FT_FuncFix3, // 95 ?
+ FT_FuncFix3, // 96 ?
+ FT_FuncFix3, // 97 ?
+ FT_FuncFix3, // 98 Index() <- TODO: correct?
+ FT_FuncFix1, // 99 Columns()
+ FT_FuncFix1, // 100 Rows()
+ FT_FuncFix2, // 101 Repeat()
+ FT_FuncFix1, // 102 Upper()
+ FT_FuncFix1, // 103 Lower()
+ FT_FuncFix2, // 104 Left()
+ FT_FuncFix2, // 105 Right()
+ FT_FuncFix4, // 106 Replace()
+ FT_FuncFix1, // 107 Proper()
+ FT_FuncFix2, // 108 Cell()
+ FT_FuncFix1, // 109 Trim()
+ FT_FuncFix1, // 110 Clean()
+ FT_FuncFix1, // 111 F()
+ FT_FuncFix1, // 112 W()
+ FT_FuncFix2, // 113 Exact()
+ FT_NotImpl, // 114 Call()
+ FT_FuncFix1, // 115 @@()
+ FT_FuncFix3, // 116 Rate()
+ FT_FuncFix3, // 117 Term()
+ FT_FuncFix3, // 118 Cterm()
+ FT_FuncFix3, // 119 Sln()
+ FT_FuncFix4, // 120 Syd(), Soy()
+ FT_FuncFix4, // 121 Ddb()
+ FT_NotImpl, // 122
+ FT_NotImpl, // 123
+ FT_NotImpl, // 124
+ FT_NotImpl, // 125
+ FT_NotImpl, // 126
+ FT_NotImpl, // 127
+ FT_NotImpl, // 128
+ FT_NotImpl, // 129
+ FT_NotImpl, // 130
+ FT_NotImpl, // 131
+ FT_NotImpl, // 132
+ FT_NotImpl, // 133
+ FT_NotImpl, // 134
+ FT_NotImpl, // 135
+ FT_NotImpl, // 136
+ FT_NotImpl, // 137
+ FT_NotImpl, // 138
+ FT_NotImpl, // 139
+ FT_NotImpl, // 140
+ FT_NotImpl, // 141
+ FT_NotImpl, // 142
+ FT_NotImpl, // 143
+ FT_NotImpl, // 144
+ FT_NotImpl, // 145
+ FT_NotImpl, // 146
+ FT_NotImpl, // 147
+ FT_NotImpl, // 148
+ FT_NotImpl, // 149
+ FT_NotImpl, // 150
+ FT_NotImpl, // 151
+ FT_NotImpl, // 152
+ FT_NotImpl, // 153
+ FT_NotImpl, // 154
+ FT_NotImpl, // 155
+ FT_FuncVar, // 156 TODO: ?
+ FT_NotImpl, // 157
+ FT_NotImpl, // 158
+ FT_NotImpl, // 159
+ FT_NotImpl, // 160
+ FT_NotImpl, // 161
+ FT_NotImpl, // 162
+ FT_NotImpl, // 163
+ FT_NotImpl, // 164
+ FT_NotImpl, // 165
+ FT_NotImpl, // 166
+ FT_NotImpl, // 167
+ FT_NotImpl, // 168
+ FT_NotImpl, // 169
+ FT_NotImpl, // 170
+ FT_NotImpl, // 171
+ FT_NotImpl, // 172
+ FT_NotImpl, // 173
+ FT_NotImpl, // 174
+ FT_NotImpl, // 175
+ FT_NotImpl, // 176
+ FT_NotImpl, // 177
+ FT_NotImpl, // 178
+ FT_NotImpl, // 179
+ FT_NotImpl, // 180
+ FT_NotImpl, // 181
+ FT_NotImpl, // 182
+ FT_NotImpl, // 183
+ FT_NotImpl, // 184
+ FT_NotImpl, // 185
+ FT_NotImpl, // 186
+ FT_NotImpl, // 187
+ FT_NotImpl, // 188
+ FT_NotImpl, // 189
+ FT_NotImpl, // 190
+ FT_NotImpl, // 191
+ FT_NotImpl, // 192
+ FT_NotImpl, // 193
+ FT_NotImpl, // 194
+ FT_NotImpl, // 195
+ FT_NotImpl, // 196
+ FT_NotImpl, // 197
+ FT_NotImpl, // 198
+ FT_NotImpl, // 199
+ FT_NotImpl, // 200
+ FT_NotImpl, // 201
+ FT_NotImpl, // 202
+ FT_NotImpl, // 203
+ FT_NotImpl, // 204
+ FT_NotImpl, // 205
+ FT_FuncVar, // 206 TODO: ?
+ FT_NotImpl, // 207
+ FT_NotImpl, // 208
+ FT_NotImpl, // 209
+ FT_NotImpl, // 210
+ FT_NotImpl, // 211
+ FT_NotImpl, // 212
+ FT_NotImpl, // 213
+ FT_NotImpl, // 214
+ FT_NotImpl, // 215
+ FT_NotImpl, // 216
+ FT_NotImpl, // 217
+ FT_NotImpl, // 218
+ FT_NotImpl, // 219
+ FT_NotImpl, // 220
+ FT_NotImpl, // 221
+ FT_NotImpl, // 222
+ FT_NotImpl, // 223
+ FT_NotImpl, // 224
+ FT_NotImpl, // 225
+ FT_NotImpl, // 226
+ FT_NotImpl, // 227
+ FT_NotImpl, // 228
+ FT_NotImpl, // 229
+ FT_NotImpl, // 230
+ FT_NotImpl, // 231
+ FT_NotImpl, // 232
+ FT_NotImpl, // 233
+ FT_NotImpl, // 234
+ FT_NotImpl, // 235
+ FT_NotImpl, // 236
+ FT_NotImpl, // 237
+ FT_NotImpl, // 238
+ FT_NotImpl, // 239
+ FT_NotImpl, // 240
+ FT_NotImpl, // 241
+ FT_NotImpl, // 242
+ FT_NotImpl, // 243
+ FT_NotImpl, // 244
+ FT_NotImpl, // 245
+ FT_NotImpl, // 246
+ FT_NotImpl, // 247
+ FT_NotImpl, // 248
+ FT_NotImpl, // 249
+ FT_NotImpl, // 250
+ FT_NotImpl, // 251
+ FT_NotImpl, // 252
+ FT_NotImpl, // 253
+ FT_NotImpl, // 254
+ FT_FuncVar, // 255 TODO: ?
+ };
+ return pType[ nIndex ];
+}
+
+DefTokenId LotusToSc::IndexToToken( sal_uInt8 nIndex )
+{
+ static const DefTokenId pToken[ 256 ] =
+ { // Code Description
+ ocPush, // 0 8-Byte-IEEE-Float
+ ocPush, // 1 Variable
+ ocPush, // 2 Range
+ ocPush, // 3 return
+ ocPush, // 4 Braces
+ ocPush, // 5 2-Byte integer
+ ocPush, // 6 ASCII string
+ ocPush, // 7 NOP
+ ocNegSub, // 8 Negation
+ ocAdd, // 9 Addition
+ ocSub, // 10 Subtraction
+ ocMul, // 11 Multiplication
+ ocDiv, // 12 Division
+ ocPow, // 13 Power of
+ ocEqual, // 14 equal
+ ocNotEqual, // 15 unequal
+ ocLessEqual, // 16 <=
+ ocGreaterEqual, // 17 >=
+ ocLess, // 18 <
+ ocGreater, // 19 >
+ ocAnd, // 20 And (logic)
+ ocOr, // 21 Or (logic)
+ ocNot, // 22 Not (logic)
+ ocPush, // 23 unary Plus
+ ocNoName, // 24
+ ocNoName, // 25
+ ocNoName, // 26
+ ocNoName, // 27
+ ocNoName, // 28
+ ocNoName, // 29
+ ocNoName, // 30
+ ocNotAvail, // 31 Not available
+ ocNoName, // 32 Error
+ ocAbs, // 33 Absolute value ABS()
+ ocInt, // 34 Integer INT()
+ ocSqrt, // 35 Square Root
+ ocLog10, // 36 Log10
+ ocLn, // 37 Natural Logarithm
+ ocPi, // 38 PI
+ ocSin, // 39 Sine
+ ocCos, // 40 Cosine
+ ocTan, // 41 Tangens
+ ocArcTan2, // 42 Arctangens 2 (4.Quadrant)
+ ocArcTan, // 43 Arctangens (2.Quadrant)
+ ocArcSin, // 44 Arcsine
+ ocArcCos, // 45 Arccosine
+ ocExp, // 46 Exponential function
+ ocMod, // 47 Modulo
+ ocChoose, // 48 Selection
+ ocIsNA, // 49 Is not available?
+ ocIsError, // 50 Is Error?
+ ocFalse, // 51 FALSE
+ ocTrue, // 52 TRUE
+ ocRandom, // 53 Random number
+ ocGetDate, // 54 Date
+ ocGetActDate, // 55 Today
+ ocPMT, // 56 Payment
+ ocPV, // 57 Present Value
+ ocFV, // 58 Future Value
+ ocIf, // 59 If ... then ... else ...
+ ocGetDay, // 60 Day of month
+ ocGetMonth, // 61 Month
+ ocGetYear, // 62 Year
+ ocRound, // 63 Round
+ ocGetTime, // 64 Time
+ ocGetHour, // 65 Hour
+ ocGetMin, // 66 Minute
+ ocGetSec, // 67 Second
+ ocIsValue, // 68 Is Number?
+ ocIsString, // 69 Is Text?
+ ocLen, // 70 Len()
+ ocValue, // 71 Val()
+ ocFixed, // 72 String() ocFixed replacement + special case
+ ocMid, // 73 Mid()
+ ocChar, // 74 Char()
+ ocCode, // 75 Ascii()
+ ocFind, // 76 Find()
+ ocGetDateValue, // 77 Datevalue
+ ocGetTimeValue, // 78 Timevalue
+ ocNoName, // 79 Cellpointer
+ ocSum, // 80 Sum()
+ ocAverageA, // 81 Avg()
+ ocCount2, // 82 Cnt()
+ ocMinA, // 83 Min()
+ ocMaxA, // 84 Max()
+ ocVLookup, // 85 Vlookup()
+ ocNPV, // 86 Npv()
+ ocVarPA, // 87 Var()
+ ocStDevPA, // 88 Std()
+ ocIRR, // 89 Irr()
+ ocHLookup, // 90 Hlookup()
+ ocDBSum, // 91 XlfDsum
+ ocDBAverage, // 92 XlfDaverage
+ ocDBCount2, // 93 XlfDcount
+ ocDBMin, // 94 XlfDmin
+ ocDBMax, // 95 XlfDmax
+ ocDBVarP, // 96 XlfDvar
+ ocDBStdDevP, // 97 XlfDstdev
+ ocIndex, // 98 Index()
+ ocColumns, // 99 Columns()
+ ocRows, // 100 Rows()
+ ocRept, // 101 Repeat()
+ ocUpper, // 102 Upper()
+ ocLower, // 103 Lower()
+ ocLeft, // 104 Left()
+ ocRight, // 105 Right()
+ ocReplace, // 106 Replace()
+ ocProper, // 107 Proper()
+ ocNoName, // 108 Cell()
+ ocTrim, // 109 Trim()
+ ocClean, // 110 Clean()
+ ocFalse, // 111 F()
+ ocTrue, // 112 W()
+ ocExact, // 113 Exact()
+ ocNoName, // 114 Call()
+ ocIndirect, // 115 @@()
+ ocRate, // 116 Rate()
+ ocNoName, // 117 Term()
+ ocNoName, // 118 Cterm()
+ ocSLN, // 119 Sln()
+ ocSYD, // 120 Syd(), Soy()
+ ocDDB, // 121 Ddb()
+ ocNoName, // 122
+ ocNoName, // 123
+ ocNoName, // 124
+ ocNoName, // 125
+ ocNoName, // 126
+ ocNoName, // 127
+ ocNoName, // 128
+ ocNoName, // 129
+ ocNoName, // 130
+ ocNoName, // 131
+ ocNoName, // 132
+ ocNoName, // 133
+ ocNoName, // 134
+ ocNoName, // 135
+ ocNoName, // 136
+ ocNoName, // 137
+ ocNoName, // 138
+ ocNoName, // 139
+ ocNoName, // 140
+ ocNoName, // 141
+ ocNoName, // 142
+ ocNoName, // 143
+ ocNoName, // 144
+ ocNoName, // 145
+ ocNoName, // 146
+ ocNoName, // 147
+ ocNoName, // 148
+ ocNoName, // 149
+ ocNoName, // 150
+ ocNoName, // 151
+ ocNoName, // 152
+ ocNoName, // 153
+ ocNoName, // 154
+ ocNoName, // 155
+ ocNoName, // 156 TODO: ?
+ ocNoName, // 157
+ ocNoName, // 158
+ ocNoName, // 159
+ ocNoName, // 160
+ ocNoName, // 161
+ ocNoName, // 162
+ ocNoName, // 163
+ ocNoName, // 164
+ ocNoName, // 165
+ ocNoName, // 166
+ ocNoName, // 167
+ ocNoName, // 168
+ ocNoName, // 169
+ ocNoName, // 170
+ ocNoName, // 171
+ ocNoName, // 172
+ ocNoName, // 173
+ ocNoName, // 174
+ ocNoName, // 175
+ ocNoName, // 176
+ ocNoName, // 177
+ ocNoName, // 178
+ ocNoName, // 179
+ ocNoName, // 180
+ ocNoName, // 181
+ ocNoName, // 182
+ ocNoName, // 183
+ ocNoName, // 184
+ ocNoName, // 185
+ ocNoName, // 186
+ ocNoName, // 187
+ ocNoName, // 188
+ ocNoName, // 189
+ ocNoName, // 190
+ ocNoName, // 191
+ ocNoName, // 192
+ ocNoName, // 193
+ ocNoName, // 194
+ ocNoName, // 195
+ ocNoName, // 196
+ ocNoName, // 197
+ ocNoName, // 198
+ ocNoName, // 199
+ ocNoName, // 200
+ ocNoName, // 201
+ ocNoName, // 202
+ ocNoName, // 203
+ ocNoName, // 204
+ ocNoName, // 205
+ ocNoName, // 206 TODO: ?
+ ocNoName, // 207
+ ocNoName, // 208
+ ocNoName, // 209
+ ocNoName, // 210
+ ocNoName, // 211
+ ocNoName, // 212
+ ocNoName, // 213
+ ocNoName, // 214
+ ocNoName, // 215
+ ocNoName, // 216
+ ocNoName, // 217
+ ocNoName, // 218
+ ocNoName, // 219
+ ocNoName, // 220
+ ocNoName, // 221
+ ocNoName, // 222
+ ocNoName, // 223
+ ocNoName, // 224
+ ocNoName, // 225
+ ocNoName, // 226
+ ocNoName, // 227
+ ocNoName, // 228
+ ocNoName, // 229
+ ocNoName, // 230
+ ocNoName, // 231
+ ocNoName, // 232
+ ocNoName, // 233
+ ocNoName, // 234
+ ocNoName, // 235
+ ocNoName, // 236
+ ocNoName, // 237
+ ocNoName, // 238
+ ocNoName, // 239
+ ocNoName, // 240
+ ocNoName, // 241
+ ocNoName, // 242
+ ocNoName, // 243
+ ocNoName, // 244
+ ocNoName, // 245
+ ocNoName, // 246
+ ocNoName, // 247
+ ocNoName, // 248
+ ocNoName, // 249
+ ocNoName, // 250
+ ocNoName, // 251
+ ocNoName, // 252
+ ocNoName, // 253
+ ocNoName, // 254
+ ocNoName // 255 TODO: ?
+ };
+
+ return pToken[ nIndex ];
+}
+
+FUNC_TYPE LotusToSc::IndexToTypeWK123( sal_uInt8 nIndex )
+{
+ static const FUNC_TYPE pType[ 256 ] =
+ { // Code Description
+ FT_Const10Float, // 0 8-Byte-IEEE-Long-Number
+ FT_Cref, // 1 Cell Reference
+ FT_Rref, // 2 Area Reference
+ FT_Return, // 3 return
+ FT_Braces, // 4 Braces
+ FT_Snum, // 5 Short number
+ FT_ConstString, // 6 ASCII string
+ FT_Nrref, // 7 Named range reference
+ FT_Absnref, // 8 Absolute named range
+ FT_Erref, // 9 Err range reference
+ FT_Ecref, // 10 Err cell reference
+ FT_Econstant, // 11 Err constant
+ FT_NotImpl, // 12
+ FT_NotImpl, // 13
+ FT_Neg, // 14 Negation
+ FT_Op, // 15 Addition
+ FT_Op, // 16 Subtraction
+ FT_Op, // 17 Multiplication
+ FT_Op, // 18 Division
+ FT_Op, // 19 Power of
+ FT_Op, // 20 equal
+ FT_Op, // 21 unequal
+ FT_Op, // 22 <=
+ FT_Op, // 23 >=
+ FT_Op, // 24 <
+ FT_Op, // 25 >
+ FT_Op, // 26 And (logical)
+ FT_Op, // 27 Or (logical)
+ FT_FuncFix1, // 28 Not (logical)
+ FT_NOP, // 29 unary plus
+ FT_Op, // 30 Concatenation
+ FT_FuncFix0, // 31 Not applicable
+ FT_FuncFix0, // 32 Error
+ FT_FuncFix1, // 33 Absolute value ABS()
+ FT_FuncFix1, // 34 Integer INT()
+ FT_FuncFix1, // 35 Square root
+ FT_FuncFix1, // 36 Log10
+ FT_FuncFix1, // 37 Natural Logarithm
+ FT_FuncFix0, // 38 PI
+ FT_FuncFix1, // 39 Sine
+ FT_FuncFix1, // 40 Cosine
+ FT_FuncFix1, // 41 Tangens
+ FT_FuncFix2, // 42 Arctangens 2 (4.Quadrant)
+ FT_FuncFix1, // 43 Arctangens (2.Quadrant)
+ FT_FuncFix1, // 44 Arcsine
+ FT_FuncFix1, // 45 Arccosine
+ FT_FuncFix1, // 46 Exponential function
+ FT_FuncFix2, // 47 Modulo
+ FT_FuncVar, // 48 Selection
+ FT_FuncFix1, // 49 Is not applicable?
+ FT_FuncFix1, // 50 Is Error?
+ FT_FuncFix0, // 51 FALSE
+ FT_FuncFix0, // 52 TRUE
+ FT_FuncFix0, // 53 Random number
+ FT_FuncFix3, // 54 Date
+ FT_FuncFix0, // 55 Today
+ FT_FuncFix3, // 56 Payment
+ FT_FuncFix3, // 57 Present Value
+ FT_FuncFix3, // 58 Future Value
+ FT_FuncFix3, // 59 If ... then ... else ...
+ FT_FuncFix1, // 60 Day of Month
+ FT_FuncFix1, // 61 Month
+ FT_FuncFix1, // 62 Year
+ FT_FuncFix2, // 63 Round
+ FT_FuncFix3, // 64 Time
+ FT_FuncFix1, // 65 Hour
+ FT_FuncFix1, // 66 Minute
+ FT_FuncFix1, // 67 Second
+ FT_FuncFix1, // 68 Is Number?
+ FT_FuncFix1, // 69 Is Text?
+ FT_FuncFix1, // 70 Len()
+ FT_FuncFix1, // 71 Val()
+ FT_FuncFix2, // 72 String()
+ FT_FuncFix3, // 73 Mid()
+ FT_FuncFix1, // 74 Char()
+ FT_FuncFix1, // 75 Ascii()
+ FT_FuncFix3, // 76 Find()
+ FT_FuncFix1, // 77 Datevalue
+ FT_FuncFix1, // 78 Timevalue
+ FT_FuncFix1, // 79 Cellpointer
+ FT_FuncVar, // 80 Sum()
+ FT_FuncVar, // 81 Avg()
+ FT_FuncVar, // 82 Cnt()
+ FT_FuncVar, // 83 Min()
+ FT_FuncVar, // 84 Max()
+ FT_FuncFix3, // 85 Vlookup()
+ FT_FuncFix2, // 86 Npv()
+ FT_FuncVar, // 87 Var()
+ FT_FuncVar, // 88 Std()
+ FT_FuncFix2, // 89 Irr()
+ FT_FuncFix3, // 90 Hlookup()
+ FT_FuncVar, // 91 Dsum <- new
+ FT_FuncVar, // 92 Davg <- new
+ FT_FuncVar, // 93 Dcnt <- new
+ FT_FuncVar, // 94 Dmin <- new
+ FT_FuncVar, // 95 Dmax <- new
+ FT_FuncVar, // 96 Dvar <- new
+ FT_FuncVar, // 97 Dstd <- new
+ FT_FuncVar, // 98 Index() <- change!
+ FT_FuncFix1, // 99 Columns() <- new
+ FT_FuncFix1, // 100 Rows() <- new
+ FT_FuncFix2, // 101 Repeat() <- new
+ FT_FuncFix1, // 102 Upper() <- new
+ FT_FuncFix1, // 103 Lower() <- new
+ FT_FuncFix2, // 104 Left() <- new
+ FT_FuncFix2, // 105 Right() <- new
+ FT_FuncFix4, // 106 Replace() <- new
+ FT_FuncFix1, // 107 Proper() <- new
+ FT_FuncFix2, // 108 Cell() <- new
+ FT_FuncFix1, // 109 Trim() <- new
+ FT_FuncFix1, // 110 Clean() <- new
+ FT_FuncFix1, // 111 S() <- change in name
+ FT_FuncFix1, // 112 N() <- change in name
+ FT_FuncFix2, // 113 Exact() <- new
+ FT_NotImpl, // 114 App <- change in name
+ FT_FuncFix1, // 115 @@() <- new
+ FT_FuncFix3, // 116 Rate() <- new
+ FT_FuncFix3, // 117 Term()
+ FT_FuncFix3, // 118 Cterm()
+ FT_FuncFix3, // 119 Sln() <- new
+ FT_FuncFix4, // 120 Syd() <- new
+ FT_FuncFix4, // 121 Ddb() <- new
+ FT_Splfunc, // 122 Splfunc <- new
+ FT_FuncFix1, // 123 Sheets <- new
+ FT_FuncFix1, // 124 Info <- new
+ FT_FuncVar, // 125 Sumproduct <- new
+ FT_FuncFix1, // 126 Isrange <- new
+ FT_FuncVar, // 127 Dget <- new
+ FT_FuncVar, // 128 Dquery <- new
+ FT_FuncFix4, // 129 Coord <- new
+ FT_NOP, // 130 Reserved (internal) <- new
+ FT_FuncFix0, // 131 Today <- new
+ FT_FuncVar, // 132 Vdb <- new
+ FT_FuncVar, // 133 Dvars <- new
+ FT_FuncVar, // 134 Dstds <- new
+ FT_FuncVar, // 135 Vars <- new
+ FT_FuncVar, // 136 Stds <- new
+ FT_FuncFix2, // 137 D360 <- new
+ FT_NOP, // 138 Reserved (internal) <- new
+ FT_FuncFix0, // 139 Isapp <- new quantity ?
+ FT_FuncVar, // 140 Isaaf <- new quantity ?
+ FT_FuncFix1, // 141 Weekday <- new
+ FT_FuncFix3, // 142 Datedif <- new
+ FT_FuncVar, // 143 Rank <- new
+ FT_FuncFix2, // 144 Numberstring <- new
+ FT_FuncFix1, // 145 Datestring <- new
+ FT_FuncFix1, // 146 Decimal <- new
+ FT_FuncFix1, // 147 Hex <- new
+ FT_FuncFix4, // 148 Db <- new
+ FT_FuncFix4, // 149 Pmti <- new
+ FT_FuncFix4, // 150 Spi <- new
+ FT_FuncFix1, // 151 Fullp <- new
+ FT_FuncFix1, // 152 Halfp <- new
+ FT_FuncVar, // 153 Pureavg <- new
+ FT_FuncVar, // 154 Purecount <- new
+ FT_FuncVar, // 155 Puremax <- new
+ FT_FuncVar, // 156 Puremin <- new
+ FT_FuncVar, // 157 Purestd <- new
+ FT_FuncVar, // 158 Purevar <- new
+ FT_FuncVar, // 159 Purestds <- new
+ FT_FuncVar, // 160 Purevars <- new
+ FT_FuncFix3, // 161 Pmt2 <- new
+ FT_FuncFix3, // 162 Pv2 <- new
+ FT_FuncFix3, // 163 Fv2 <- new
+ FT_FuncFix3, // 164 Term2 <- new
+ FT_NotImpl, // 165-- <- new quantity ?
+ FT_FuncFix2, // 166 D360 (US-Version)
+ FT_NotImpl, // 167
+ FT_NotImpl, // 168
+ FT_NotImpl, // 169
+ FT_NotImpl, // 170
+ FT_NotImpl, // 171
+ FT_NotImpl, // 172
+ FT_NotImpl, // 173
+ FT_NotImpl, // 174
+ FT_NotImpl, // 175
+ FT_NotImpl, // 176
+ FT_NotImpl, // 177
+ FT_NotImpl, // 178
+ FT_NotImpl, // 179
+ FT_NotImpl, // 180
+ FT_NotImpl, // 181
+ FT_NotImpl, // 182
+ FT_NotImpl, // 183
+ FT_NotImpl, // 184
+ FT_NotImpl, // 185
+ FT_FuncVar, // 186 Solver <- new
+ FT_NotImpl, // 187
+ FT_NotImpl, // 188
+ FT_NotImpl, // 189
+ FT_NotImpl, // 190
+ FT_NotImpl, // 191
+ FT_NotImpl, // 192
+ FT_NotImpl, // 193
+ FT_NotImpl, // 194
+ FT_NotImpl, // 195
+ FT_NotImpl, // 196
+ FT_NotImpl, // 197
+ FT_NotImpl, // 198
+ FT_NotImpl, // 199
+ FT_NotImpl, // 200
+ FT_NotImpl, // 201
+ FT_NotImpl, // 202
+ FT_NotImpl, // 203
+ FT_NotImpl, // 204
+ FT_NotImpl, // 205
+ FT_NotImpl, // 206
+ FT_NotImpl, // 207
+ FT_NotImpl, // 208
+ FT_NotImpl, // 209
+ FT_NotImpl, // 210
+ FT_NotImpl, // 211
+ FT_NotImpl, // 212
+ FT_NotImpl, // 213
+ FT_NotImpl, // 214
+ FT_NotImpl, // 215
+ FT_NotImpl, // 216
+ FT_NotImpl, // 217
+ FT_NotImpl, // 218
+ FT_NotImpl, // 219
+ FT_NotImpl, // 220
+ FT_NotImpl, // 221
+ FT_NotImpl, // 222
+ FT_NotImpl, // 223
+ FT_NotImpl, // 224
+ FT_NotImpl, // 225
+ FT_NotImpl, // 226
+ FT_NotImpl, // 227
+ FT_NotImpl, // 228
+ FT_NotImpl, // 229
+ FT_NotImpl, // 230
+ FT_NotImpl, // 231
+ FT_NotImpl, // 232
+ FT_NotImpl, // 233
+ FT_NotImpl, // 234
+ FT_NotImpl, // 235
+ FT_NotImpl, // 236
+ FT_NotImpl, // 237
+ FT_NotImpl, // 238
+ FT_NotImpl, // 239
+ FT_NotImpl, // 240
+ FT_NotImpl, // 241
+ FT_NotImpl, // 242
+ FT_NotImpl, // 243
+ FT_NotImpl, // 244
+ FT_NotImpl, // 245
+ FT_NotImpl, // 246
+ FT_NotImpl, // 247
+ FT_NotImpl, // 248
+ FT_NotImpl, // 249
+ FT_NotImpl, // 250
+ FT_NotImpl, // 251
+ FT_NotImpl, // 252
+ FT_NotImpl, // 253
+ FT_NotImpl, // 254
+ FT_NotImpl, // 255
+ };
+ return pType[ nIndex ];
+}
+
+DefTokenId LotusToSc::IndexToTokenWK123( sal_uInt8 nIndex )
+{
+ static const DefTokenId pToken[ 256 ] =
+ { // Code Description
+ ocPush, // 0 8-Byte-IEEE-Long-Numbers
+ ocPush, // 1 Variable
+ ocPush, // 2 Range
+ ocPush, // 3 return
+ ocPush, // 4 Braces
+ ocPush, // 5 Numbers
+ ocPush, // 6 ASCII string
+ ocPush, // 7 Named range reference
+ ocPush, // 8 Absolute named range
+ ocPush, // 9 Err range reference
+ ocPush, // 10 Err cell reference
+ ocPush, // 11 Err constant
+ ocPush, // 12
+ ocPush, // 13
+ ocNegSub, // 14 Negation
+ ocAdd, // 15 Addition
+ ocSub, // 16 Subtraction
+ ocMul, // 17 Multiplication
+ ocDiv, // 18 Division
+ ocPow, // 19 Power of
+ ocEqual, // 20 Equality
+ ocNotEqual, // 21 Inequality
+ ocLessEqual, // 22 <=
+ ocGreaterEqual, // 23 >=
+ ocLess, // 24 <
+ ocGreater, // 25 >
+ ocAnd, // 26 And (logic)
+ ocOr, // 27 Or (logic)
+ ocNot, // 28 Not (logic)
+ ocPush, // 29 unary Plus
+ ocAmpersand, // 30 Concatenation
+ ocNotAvail, // 31 Not available
+ ocNoName, // 32 Error
+ ocAbs, // 33 Absolute Value ABS()
+ ocInt, // 34 Integer INT()
+ ocSqrt, // 35 Square Root
+ ocLog10, // 36 log10
+ ocLn, // 37 Natural logarithm
+ ocPi, // 38 Pi
+ ocSin, // 39 Sine
+ ocCos, // 40 Cosine
+ ocTan, // 41 Tangens
+ ocArcTan2, // 42 Arctangens 2 (4.Quadrant)
+ ocArcTan, // 43 Arctangens (2.Quadrant)
+ ocArcSin, // 44 Arcsine
+ ocArcCos, // 45 Arccosine
+ ocExp, // 46 Exponential function
+ ocMod, // 47 Modulo
+ ocChoose, // 48 Selection
+ ocIsNA, // 49 Is not available?
+ ocIsError, // 50 Is Error?
+ ocFalse, // 51 FALSE
+ ocTrue, // 52 TRUE
+ ocRandom, // 53 Random number
+ ocGetDate, // 54 Date
+ ocGetActDate, // 55 Today
+ ocPMT, // 56 Payment
+ ocPV, // 57 Present Value
+ ocFV, // 58 Future Value
+ ocIf, // 59 If... then... else...
+ ocGetDay, // 60 Day of Month
+ ocGetMonth, // 61 Month
+ ocGetYear, // 62 Year
+ ocRound, // 63 Round
+ ocGetTime, // 64 Time
+ ocGetHour, // 65 Hour
+ ocGetMin, // 66 Minute
+ ocGetSec, // 67 Second
+ ocIsValue, // 68 Is number?
+ ocIsString, // 69 Is text?
+ ocLen, // 70 Len()
+ ocValue, // 71 Val()
+ ocFixed, // 72 String() ocFixed alternatively + special case
+ ocMid, // 73 Mid()
+ ocChar, // 74 Char()
+ ocCode, // 75 Ascii()
+ ocFind, // 76 Find()
+ ocGetDateValue, // 77 Datevalue
+ ocGetTimeValue, // 78 Timevalue
+ ocNoName, // 79 Cellpointer
+ ocSum, // 80 Sum()
+ ocAverageA, // 81 Avg()
+ ocCount2, // 82 Cnt()
+ ocMinA, // 83 Min()
+ ocMaxA, // 84 Max()
+ ocVLookup, // 85 Vlookup()
+ ocNPV, // 86 Npv()
+ ocVarPA, // 87 Var()
+ ocStDevPA, // 88 Std()
+ ocIRR, // 89 Irr()
+ ocHLookup, // 90 Hlookup()
+ ocDBSum, // 91 XlfDsum
+ ocDBAverage, // 92 XlfDaverage
+ ocDBCount2, // 93 XlfDcount
+ ocDBMin, // 94 XlfDmin
+ ocDBMax, // 95 XlfDmax
+ ocDBVarP, // 96 XlfDvar
+ ocDBStdDevP, // 97 XlfDstdev
+ ocIndex, // 98 Index()
+ ocColumns, // 99 Cols()
+ ocRows, // 100 Rows()
+ ocRept, // 101 Repeat()
+ ocUpper, // 102 Upper()
+ ocLower, // 103 Lower()
+ ocLeft, // 104 Left()
+ ocRight, // 105 Right()
+ ocReplace, // 106 Replace()
+ ocProper, // 107 Proper()
+ ocNoName, // 108 Cell()
+ ocTrim, // 109 Trim()
+ ocClean, // 110 Clean()
+ ocNoName, // 111 F() (Excel: T()?)
+ ocNoName, // 112 W()
+ ocExact, // 113 Exact()
+ ocNoName, // 114 Call()
+ ocIndirect, // 115 @@()
+ ocRate, // 116 Rate()
+ ocNoName, // 117 Term()
+ ocNoName, // 118 Cterm()
+ ocSLN, // 119 Sln()
+ ocSYD, // 120 Syd(), Soy()
+ ocDDB, // 121 Ddb()
+ ocNoName, // 122 Splfunc
+ ocNoName, // 123 Sheets
+ ocNoName, // 124 Info
+ ocSumProduct, // 125 Sumproduct
+ ocNoName, // 126 Isrange
+ ocDBGet, // 127 Dget
+ ocNoName, // 128 Dquery
+ ocNoName, // 129 Coord
+ ocNoName, // 130 Reserved (internal)
+ ocGetActDate, // 131 Today
+ ocNoName, // 132 Vdb
+ ocDBVar, // 133 Dvars
+ ocDBStdDev, // 134 Dstds
+ ocVarA, // 135 Vars
+ ocStDevA, // 136 Stds
+ ocGetDiffDate360, // 137 D360
+ ocNoName, // 138 Reserved (internal)
+ ocNoName, // 139 Isapp
+ ocNoName, // 140 Isaaf
+ ocGetDayOfWeek, // 141 Weekday
+ ocGetDiffDate, // 142 Datedif
+ ocRank, // 143 Rank
+ ocNoName, // 144 Numberstring
+ ocNoName, // 145 Datestring
+ ocNoName, // 146 Decimal
+ ocNoName, // 147 Hex
+ ocNoName, // 148 Db
+ ocNoName, // 149 Pmti
+ ocNoName, // 150 Spi
+ ocNoName, // 151 Fullp
+ ocNoName, // 152 Halfp
+ ocAverage, // 153 Pureavg
+ ocCount, // 154 Purecount
+ ocMax, // 155 Puremax
+ ocMin, // 156 Puremin
+ ocStDevP, // 157 Purestd
+ ocVarP, // 158 Purevar
+ ocStDev, // 159 Purestds
+ ocVar, // 160 Purevars
+ ocNoName, // 161 Pmt2
+ ocNoName, // 162 Pv2
+ ocNoName, // 163 Fv2
+ ocNoName, // 164 Term2
+ ocNoName, // 165-- <- new quantity ?
+ ocGetDiffDate360, // 166 D360 (US-Version, alternatively as other D360 function)
+ ocNoName, // 167
+ ocNoName, // 168
+ ocNoName, // 169
+ ocNoName, // 170
+ ocNoName, // 171
+ ocNoName, // 172
+ ocNoName, // 173
+ ocNoName, // 174
+ ocNoName, // 175
+ ocNoName, // 176
+ ocNoName, // 177
+ ocNoName, // 178
+ ocNoName, // 179
+ ocNoName, // 180
+ ocNoName, // 181
+ ocNoName, // 182
+ ocNoName, // 183
+ ocNoName, // 184
+ ocNoName, // 185
+ ocNoName, // 186
+ ocNoName, // 187
+ ocNoName, // 188
+ ocNoName, // 189
+ ocNoName, // 190
+ ocNoName, // 191
+ ocNoName, // 192
+ ocNoName, // 193
+ ocNoName, // 194
+ ocNoName, // 195
+ ocNoName, // 196
+ ocNoName, // 197
+ ocNoName, // 198
+ ocNoName, // 199
+ ocNoName, // 200
+ ocNoName, // 201
+ ocNoName, // 202
+ ocNoName, // 203
+ ocNoName, // 204
+ ocNoName, // 205
+ ocNoName, // 206 ?
+ ocNoName, // 207
+ ocNoName, // 208
+ ocNoName, // 209
+ ocNoName, // 210
+ ocNoName, // 211
+ ocNoName, // 212
+ ocNoName, // 213
+ ocNoName, // 214
+ ocNoName, // 215
+ ocNoName, // 216
+ ocNoName, // 217
+ ocNoName, // 218
+ ocNoName, // 219
+ ocNoName, // 220
+ ocNoName, // 221
+ ocNoName, // 222
+ ocNoName, // 223
+ ocNoName, // 224
+ ocNoName, // 225
+ ocNoName, // 226
+ ocNoName, // 227
+ ocNoName, // 228
+ ocNoName, // 229
+ ocNoName, // 230
+ ocNoName, // 231
+ ocNoName, // 232
+ ocNoName, // 233
+ ocNoName, // 234
+ ocNoName, // 235
+ ocNoName, // 236
+ ocNoName, // 237
+ ocNoName, // 238
+ ocNoName, // 239
+ ocNoName, // 240
+ ocNoName, // 241
+ ocNoName, // 242
+ ocNoName, // 243
+ ocNoName, // 244
+ ocNoName, // 245
+ ocNoName, // 246
+ ocNoName, // 247
+ ocNoName, // 248
+ ocNoName, // 249
+ ocNoName, // 250
+ ocNoName, // 251
+ ocNoName, // 252
+ ocNoName, // 253
+ ocNoName, // 254
+ ocNoName // 255 ?
+ };
+
+ return pToken[ nIndex ];
+}
+
+const char* GetAddInName( const sal_uInt8 n )
+{
+ static const char* pNames[ 256 ] =
+ {
+ nullptr, // 0 8-Byte-IEEE-Float
+ nullptr, // 1 Variable
+ nullptr, // 2 Range
+ nullptr, // 3 return
+ nullptr, // 4 Braces
+ nullptr, // 5 2-Byte integer
+ nullptr, // 6 ASCII string
+ nullptr, // 7 Named range reference
+ nullptr, // 8 Absolute named range
+ nullptr, // 9 Err range reference
+ nullptr, // 10 Err cell reference
+ nullptr, // 11 Err constant
+ nullptr, // 12
+ nullptr, // 13
+ nullptr, // 14 Negation
+ nullptr, // 15 Addition
+ nullptr, // 16 Subtraction
+ nullptr, // 17 Multiplication
+ nullptr, // 18 Division
+ nullptr, // 19 Power of
+ nullptr, // 20 equal
+ nullptr, // 21 unequal
+ nullptr, // 22 <=
+ nullptr, // 23 >=
+ nullptr, // 24 <
+ nullptr, // 25 >
+ nullptr, // 26 And (logic)
+ nullptr, // 27 Or (logic)
+ nullptr, // 28 Not (logic)
+ nullptr, // 29 unary Plus
+ nullptr, // 30 Concatenation
+ nullptr, // 31 Not applicable
+ nullptr, // 32 Error
+ nullptr, // 33 Absolute Value ABS()
+ nullptr, // 34 Integer INT()
+ nullptr, // 35 Square Root
+ nullptr, // 36 log10
+ nullptr, // 37 Natural logarithm
+ nullptr, // 38 PI
+ nullptr, // 39 Sine
+ nullptr, // 40 Cosine
+ nullptr, // 41 Tangens
+ nullptr, // 42 Arctangens 2 (4.Quadrant)
+ nullptr, // 43 Arctangens (2.Quadrant)
+ nullptr, // 44 Arcsine
+ nullptr, // 45 Arccosine
+ nullptr, // 46 Exponential function
+ nullptr, // 47 Modulo
+ nullptr, // 48 Selection
+ nullptr, // 49 Is not applicable?
+ nullptr, // 50 Is Error?
+ nullptr, // 51 FALSE
+ nullptr, // 52 TRUE
+ nullptr, // 53 Random number
+ nullptr, // 54 Date
+ nullptr, // 55 Today
+ nullptr, // 56 Payment
+ nullptr, // 57 Present Value
+ nullptr, // 58 Future Value
+ nullptr, // 59 If ... then ... else ...
+ nullptr, // 60 Day of Month
+ nullptr, // 61 Month
+ nullptr, // 62 Year
+ nullptr, // 63 Round
+ nullptr, // 64 Time
+ nullptr, // 65 Hour
+ nullptr, // 66 Minute
+ nullptr, // 67 Second
+ nullptr, // 68 Is Number?
+ nullptr, // 69 Is Text?
+ nullptr, // 70 Len()
+ nullptr, // 71 Val()
+ nullptr, // 72 String() ocFixed as substitute + special case
+ nullptr, // 73 Mid()
+ nullptr, // 74 Char()
+ nullptr, // 75 Ascii()
+ nullptr, // 76 Find()
+ nullptr, // 77 Datevalue
+ nullptr, // 78 Timevalue
+ "ZELLZEIGER", // 79 Cellpointer
+ nullptr, // 80 Sum()
+ nullptr, // 81 Avg()
+ nullptr, // 82 Cnt()
+ nullptr, // 83 Min()
+ nullptr, // 84 Max()
+ nullptr, // 85 Vlookup()
+ nullptr, // 86 Npv()
+ nullptr, // 87 Var()
+ nullptr, // 88 Std()
+ nullptr, // 89 Irr()
+ nullptr, // 90 Hlookup()
+ nullptr, // 91 XlfDsum
+ nullptr, // 92 XlfDaverage
+ nullptr, // 93 XlfDcount
+ nullptr, // 94 XlfDmin
+ nullptr, // 95 XlfDmax
+ nullptr, // 96 XlfDvar
+ nullptr, // 97 XlfDstdev
+ nullptr, // 98 Index()
+ nullptr, // 99 Cols()
+ nullptr, // 100 Rows()
+ nullptr, // 101 Repeat()
+ nullptr, // 102 Upper()
+ nullptr, // 103 Lower()
+ nullptr, // 104 Left()
+ nullptr, // 105 Right()
+ nullptr, // 106 Replace()
+ nullptr, // 107 Proper()
+ "ZELLE", // 108 Cell()
+ nullptr, // 109 Trim()
+ nullptr, // 110 Clean()
+ "F", // 111 F() (Excel: T()?)
+ "W", // 112 W()
+ nullptr, // 113 Exact()
+ nullptr, // 114 Call()
+ nullptr, // 115 @@()
+ nullptr, // 116 Rate()
+ "TERM", // 117 Term()
+ "CTERM", // 118 Cterm()
+ nullptr, // 119 Sln()
+ nullptr, // 120 Syd(), Soy()
+ nullptr, // 121 Ddb()
+ "SplFunc", // 122 Splfunc
+ "BLAETTER", // 123 Sheets
+ "INFO", // 124 Info
+ nullptr, // 125 Sumproduct
+ "ISTBEREICH", // 126 Isrange
+ nullptr, // 127 Dget
+ "DABFRAGE", // 128 Dquery
+ "KOORD", // 129 Coord
+ nullptr, // 130 Reserved (internal)
+ nullptr, // 131 Today
+ nullptr, // 132 Vdb
+ nullptr, // 133 Dvars
+ nullptr, // 134 Dstds
+ nullptr, // 135 Vars
+ nullptr, // 136 Stds
+ nullptr, // 137 D360
+ nullptr, // 138 Reserved (internal)
+ nullptr, // 139 Isapp
+ "ISTDEFZUS", // 140 Isaaf
+ nullptr, // 141 Weekday
+ nullptr, // 142 Datedif
+ nullptr, // 143 Rank
+ nullptr, // 144 Numberstring
+ "DATUMFOLGE", // 145 Datestring
+ "DEZIMAL", // 146 Decimal
+ "HEX", // 147 Hex
+ nullptr, // 148 Db
+ nullptr, // 149 Pmti
+ nullptr, // 150 Spi
+ nullptr, // 151 Fullp
+ nullptr, // 152 Halfp
+ "PURMITTELWERT", // 153 Pureavg
+ "PURquantity", // 154 Purecount
+ "PURMAX", // 155 Puremax
+ "PURMIN", // 156 Puremin
+ "PURSTDABW", // 157 Purestd
+ "PURVAR", // 158 Purevar
+ "PURSTDABWP", // 159 Purestds
+ "PURVARP", // 160 Purevars
+ nullptr, // 161 Pmt2
+ nullptr, // 162 Pv2
+ nullptr, // 163 Fv2
+ nullptr, // 164 Term2
+ nullptr, // 165-- <- new quantity ?
+ nullptr, // 166 D360 (US-Version, alternatively as in D360-function)
+ nullptr, // 167
+ nullptr, // 168
+ nullptr, // 169
+ nullptr, // 170
+ nullptr, // 171
+ nullptr, // 172
+ nullptr, // 173
+ nullptr, // 174
+ nullptr, // 175
+ nullptr, // 176
+ nullptr, // 177
+ nullptr, // 178
+ nullptr, // 179
+ nullptr, // 180
+ nullptr, // 181
+ nullptr, // 182
+ nullptr, // 183
+ nullptr, // 184
+ nullptr, // 185
+ nullptr, // 186
+ nullptr, // 187
+ nullptr, // 188
+ nullptr, // 189
+ nullptr, // 190
+ nullptr, // 191
+ nullptr, // 192
+ nullptr, // 193
+ nullptr, // 194
+ nullptr, // 195
+ nullptr, // 196
+ nullptr, // 197
+ nullptr, // 198
+ nullptr, // 199
+ nullptr, // 200
+ nullptr, // 201
+ nullptr, // 202
+ nullptr, // 203
+ nullptr, // 204
+ nullptr, // 205
+ nullptr, // 206 TODO: ?
+ nullptr, // 207
+ nullptr, // 208
+ nullptr, // 209
+ nullptr, // 210
+ nullptr, // 211
+ nullptr, // 212
+ nullptr, // 213
+ nullptr, // 214
+ nullptr, // 215
+ nullptr, // 216
+ nullptr, // 217
+ nullptr, // 218
+ nullptr, // 219
+ nullptr, // 220
+ nullptr, // 221
+ nullptr, // 222
+ nullptr, // 223
+ nullptr, // 224
+ nullptr, // 225
+ nullptr, // 226
+ nullptr, // 227
+ nullptr, // 228
+ nullptr, // 229
+ nullptr, // 230
+ nullptr, // 231
+ nullptr, // 232
+ nullptr, // 233
+ nullptr, // 234
+ nullptr, // 235
+ nullptr, // 236
+ nullptr, // 237
+ nullptr, // 238
+ nullptr, // 239
+ nullptr, // 240
+ nullptr, // 241
+ nullptr, // 242
+ nullptr, // 243
+ nullptr, // 244
+ nullptr, // 245
+ nullptr, // 246
+ nullptr, // 247
+ nullptr, // 248
+ nullptr, // 249
+ nullptr, // 250
+ nullptr, // 251
+ nullptr, // 252
+ nullptr, // 253
+ nullptr, // 254
+ nullptr // 255 TODO: ?
+ };
+
+ return pNames[ n ];
+}
+
+static DefTokenId lcl_KnownAddIn( std::string_view rTest )
+{
+ DefTokenId eId = ocNoName;
+
+ if (rTest == "FACT")
+ eId = ocFact;
+ else if (rTest == "ISEMPTY")
+ eId=ocIsEmpty;
+ else if (rTest == "DEGTORAD")
+ eId=ocRad;
+ else if (rTest == "RADTODEG")
+ eId=ocDeg;
+ else if (rTest == "SIGN")
+ eId=ocPlusMinus;
+ else if (rTest == "ACOSH")
+ eId=ocArcCosHyp;
+ else if (rTest == "ACOTH")
+ eId=ocArcCotHyp;
+ else if (rTest == "ASINH")
+ eId=ocArcSinHyp;
+ else if (rTest == "ATANH")
+ eId=ocArcTanHyp;
+ else if (rTest == "COSH")
+ eId=ocCosHyp;
+ else if (rTest == "COTH")
+ eId=ocCotHyp;
+ else if (rTest == "SINH")
+ eId=ocSinHyp;
+ else if (rTest == "TANH")
+ eId=ocTanHyp;
+ else if (rTest == "EVEN")
+ eId=ocEven;
+ else if (rTest == "ODD")
+ eId=ocOdd;
+ else if (rTest == "ACOT")
+ eId=ocArcCot;
+ else if (rTest == "COT")
+ eId=ocCot;
+ else if (rTest == "TRUNC")
+ eId=ocTrunc;
+ else if (rTest == "GEOMEAN")
+ eId=ocGeoMean;
+ else if (rTest == "HARMEAN")
+ eId=ocHarMean;
+ else if (rTest == "CORREL")
+ eId=ocCorrel;
+ else if (rTest == "MEDIAN")
+ eId=ocMedian;
+ else if (rTest == "COV")
+ eId=ocCovar;
+ else if (rTest == "SKEWNESS")
+ eId=ocSkew;
+ else if (rTest == "CHITEST")
+ eId=ocChiTest;
+ else if (rTest == "FTEST")
+ eId=ocFTest;
+ else if (rTest == "AVEDEV")
+ eId=ocAveDev;
+ else if (rTest == "PRODUCT")
+ eId=ocProduct;
+ else if (rTest == "PERMUT")
+ eId=ocPermut;
+ else if (rTest == "GAMMALN")
+ eId=ocGammaLn;
+ else if (rTest =="POISSON")
+ eId=ocPoissonDist;
+ else if (rTest == "NORMAL")
+ eId=ocNormDist;
+ else if (rTest == "CRITBINOMIAL")
+ eId=ocCritBinom;
+ else if (rTest == "TERM")
+ eId=ocNper;
+ else if (rTest == "CTERM")
+ eId=ocNper;
+ else if (rTest == "SUMIF")
+ eId=ocSumIf;
+ else if (rTest == "COUNTIF")
+ eId=ocCountIf;
+ else if (rTest == "DPURECOUNT")
+ eId=ocDBCount;
+ else if (rTest == "CSC")
+ eId=ocCosecant;
+ else if (rTest == "CSCH")
+ eId=ocCosecantHyp;
+ else if (rTest == "LARGE")
+ eId=ocLarge;
+ else if (rTest == "SMALL")
+ eId=ocSmall;
+ else if (rTest == "MODULO")
+ eId=ocMod;
+ else if (rTest == "ROUNDDOWN")
+ eId=ocRoundDown;
+ else if (rTest == "ROUNDUP")
+ eId=ocRoundUp;
+ else if (rTest == "SEC")
+ eId=ocSecant;
+ else if (rTest == "SECH")
+ eId=ocSecantHyp;
+ return eId;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotimpop.cxx b/sc/source/filter/lotus/lotimpop.cxx
new file mode 100644
index 0000000000..df7f17380e
--- /dev/null
+++ b/sc/source/filter/lotus/lotimpop.cxx
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "lotfilter.hxx"
+#include <lotimpop.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <global.hxx>
+
+#include <lotfntbf.hxx>
+#include <lotform.hxx>
+#include <tool.h>
+#include <namebuff.hxx>
+#include <lotattr.hxx>
+#include <stringutil.hxx>
+#include <config_fuzzers.h>
+
+
+static osl::Mutex aLotImpSemaphore;
+
+ImportLotus::ImportLotus(LotusContext &rContext, SvStream& aStream, rtl_TextEncoding eQ)
+ : ImportTyp(rContext.rDoc, eQ)
+ , pIn(&aStream)
+ , aConv(rContext, *pIn, rContext.rDoc.GetSharedStringPool(), eQ, false)
+ , nTab(0)
+ , nExtTab(0)
+{
+ // good point to start locking of import lotus
+ aLotImpSemaphore.acquire();
+}
+
+ImportLotus::~ImportLotus()
+{
+ // no need 4 pLotusRoot anymore
+ aLotImpSemaphore.release();
+}
+
+void ImportLotus::Bof()
+{
+ sal_uInt16 nFileCode, nFileSub, nSaveCnt;
+ sal_uInt8 nMajorId, nMinorId, nFlags;
+
+ Read( nFileCode );
+ Read( nFileSub );
+ LotusContext &rContext = aConv.getContext();
+ Read( rContext.aActRange );
+ Read( nSaveCnt );
+ Read( nMajorId );
+ Read( nMinorId );
+ Skip( 1 );
+ Read( nFlags );
+
+ if (!pIn->good())
+ return;
+
+ if( nFileSub == 0x0004 )
+ {
+ if( nFileCode == 0x1000 )
+ {// <= WK3
+ rContext.eFirstType = rContext.eActType = Lotus123Typ::WK3;
+ }
+ else if( nFileCode == 0x1002 )
+ {// WK4
+ rContext.eFirstType = rContext.eActType = Lotus123Typ::WK4;
+ }
+ }
+}
+
+bool ImportLotus::BofFm3()
+{
+ sal_uInt16 nFileCode(0), nFileSub(0);
+
+ Read( nFileCode );
+ Read( nFileSub );
+
+ return ( nFileCode == 0x8007 && ( nFileSub == 0x0000 || nFileSub == 0x00001 ) );
+}
+
+void ImportLotus::Columnwidth( sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nRecLen < 4, "sc.filter", "*ImportLotus::Columnwidth(): Record too short!" );
+
+ sal_uInt16 nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 2;
+
+ sal_uInt8 nLTab(0), nWindow2(0);
+ Read( nLTab );
+ Read( nWindow2 );
+
+ if( !rD.HasTable( static_cast<SCTAB> (nLTab) ) )
+ rD.MakeTable( static_cast<SCTAB> (nLTab) );
+
+ if( nWindow2 )
+ return;
+
+ Skip( 2 );
+
+ while (nCnt && pIn->good())
+ {
+ sal_uInt8 nCol(0), nSpaces(0);
+ Read( nCol );
+ Read( nSpaces );
+ // Attention: ambiguous Correction factor!
+ rD.SetColWidth( static_cast<SCCOL> (nCol), static_cast<SCTAB> (nLTab), static_cast<sal_uInt16>( TWIPS_PER_CHAR * 1.28 * nSpaces ) );
+
+ nCnt--;
+ }
+
+ SAL_WARN_IF(!pIn->good(), "sc.filter", "*ImportLotus::Columnwidth(): short read");
+}
+
+void ImportLotus::Hiddencolumn( sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nRecLen < 4, "sc.filter", "*ImportLotus::Hiddencolumn(): Record too short!" );
+
+ sal_uInt16 nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 2;
+
+ sal_uInt8 nLTab(0), nWindow2(0);
+ Read( nLTab );
+ Read( nWindow2 );
+
+ if( nWindow2 )
+ return;
+
+ Skip( 2 );
+
+ while (nCnt && pIn->good())
+ {
+ sal_uInt8 nCol(0);
+ Read( nCol );
+
+ rD.SetColHidden(static_cast<SCCOL>(nCol), static_cast<SCCOL>(nCol), static_cast<SCTAB>(nLTab), true);
+ nCnt--;
+ }
+
+ SAL_WARN_IF(!pIn->good(), "sc.filter", "*ImportLotus::Hiddencolumn(): short read");
+}
+
+void ImportLotus::Userrange()
+{
+ sal_uInt16 nRangeType;
+ ScRange aScRange;
+
+ Read( nRangeType );
+
+ char aBuffer[ 17 ];
+ aBuffer[pIn->ReadBytes(aBuffer, 16)] = 0;
+ OUString aName(aBuffer, strlen(aBuffer), eQuellChar);
+
+ Read(aScRange);
+
+ if (!pIn->good())
+ {
+ SAL_WARN("sc.filter", "invalid range");
+ return;
+ }
+
+ LotusContext &rContext = aConv.getContext();
+ rContext.pRngNmBffWK3->Add( rContext.rDoc, aName, aScRange );
+}
+
+void ImportLotus::Errcell()
+{
+ ScAddress aA;
+
+ Read( aA );
+
+ if (!pIn->good() || !rD.ValidAddress(aA))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rD.EnsureTable(aA.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aA
+ rD.SetString(aA, "#ERR!", &aParam);
+}
+
+void ImportLotus::Nacell()
+{
+ ScAddress aA;
+
+ Read( aA );
+
+ if (!pIn->good() || !rD.ValidAddress(aA))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rD.EnsureTable(aA.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aA
+ rD.SetString(aA, "#NA!", &aParam);
+}
+
+void ImportLotus::Labelcell()
+{
+ ScAddress aA;
+ OUString aLabel;
+ char cAlign;
+
+ Read( aA );
+ Read( cAlign );
+ Read( aLabel );
+
+ if (!pIn->good() || !rD.ValidAddress(aA))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rD.EnsureTable(aA.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aA
+ rD.SetString(aA, aLabel, &aParam);
+}
+
+void ImportLotus::Numbercell()
+{
+ ScAddress aAddr;
+ double fVal;
+
+ Read( aAddr );
+ Read( fVal );
+
+ if (!pIn->good() || !rD.ValidAddress(aAddr))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ rD.EnsureTable(aAddr.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rD.SetValue(aAddr, fVal);
+}
+
+void ImportLotus::Smallnumcell()
+{
+ ScAddress aAddr;
+ sal_Int16 nVal;
+
+ Read( aAddr );
+ Read( nVal );
+
+ if (!pIn->good() || !rD.ValidAddress(aAddr))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ rD.EnsureTable(aAddr.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rD.SetValue(aAddr, SnumToDouble(nVal));
+}
+
+void ImportLotus::Formulacell( sal_uInt16 n )
+{
+ SAL_WARN_IF( !pIn, "sc.filter", "-ImportLotus::Formulacell(): Null-Stream!" );
+
+ ScAddress aAddr;
+
+ Read( aAddr );
+ Skip( 10 );
+
+ n -= std::min<sal_uInt16>(n, 14);
+
+ std::unique_ptr<ScTokenArray> pErg;
+ sal_Int32 nRest = n;
+
+ aConv.Reset( aAddr );
+ aConv.SetWK3();
+ aConv.Convert( pErg, nRest );
+ if (!aConv.good())
+ return;
+
+ if (!pIn->good() || !rD.ValidAddress(aAddr))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScFormulaCell* pCell = pErg ? new ScFormulaCell(rD, aAddr, std::move(pErg)) : new ScFormulaCell(rD, aAddr);
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ rD.EnsureTable(aAddr.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rD.SetFormulaCell(aAddr, pCell);
+}
+
+void ImportLotus::Read( OUString &r )
+{
+ ScfTools::AppendCString( *pIn, r, eQuellChar );
+}
+
+void ImportLotus::RowPresentation( sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nRecLen < 5, "sc.filter", "*ImportLotus::RowPresentation(): Record too short!" );
+
+ sal_uInt16 nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 8;
+
+ sal_uInt8 nLTab(0);
+ Read( nLTab );
+ Skip( 1 );
+
+ while (nCnt && pIn->good())
+ {
+ sal_uInt16 nRow(0);
+ Read( nRow );
+ sal_uInt16 nHeight(0);
+ Read( nHeight );
+ Skip( 2 );
+ sal_uInt8 nFlags(0);
+ Read( nFlags );
+ Skip( 1 );
+
+ if( nFlags & 0x02 ) // Fixed / Stretch to fit fonts
+ { // fixed
+ // Height in Lotus in 1/32 Points
+ nHeight *= 20; // -> 32 * TWIPS
+ nHeight /= 32; // -> TWIPS
+
+ rD.SetRowFlags( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab), rD.GetRowFlags( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab) ) | CRFlags::ManualSize );
+
+ rD.SetRowHeight( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab), nHeight );
+ }
+
+ nCnt--;
+ }
+}
+
+void ImportLotus::NamedSheet()
+{
+ sal_uInt16 nTmpTab(0);
+ Read(nTmpTab);
+ OUString aName;
+ Read(aName);
+
+ SCTAB nLTab(SanitizeTab(static_cast<SCTAB>(nTmpTab)));
+#if ENABLE_FUZZERS
+ //ofz#14167 arbitrary sheet limit to make fuzzing useful
+ if (nLTab > 5)
+ nLTab = 5;
+#endif
+
+ if (rD.HasTable(nLTab))
+ rD.RenameTab(nLTab, aName);
+ else
+ rD.InsertTab(nLTab, aName);
+}
+
+void ImportLotus::Font_Face()
+{
+ sal_uInt8 nNum;
+ OUString aName;
+
+ Read( nNum );
+
+ if( nNum >= LotusFontBuffer::nSize )
+ return; // nonsense
+
+ Read( aName );
+
+ LotusContext &rContext = aConv.getContext();
+ rContext.maFontBuff.SetName( nNum, aName );
+}
+
+void ImportLotus::Font_Type()
+{
+ LotusContext &rContext = aConv.getContext();
+ for( sal_uInt16 nCnt = 0 ; nCnt < LotusFontBuffer::nSize ; nCnt++ )
+ {
+ sal_uInt16 nType;
+ Read( nType );
+ rContext.maFontBuff.SetType( nCnt, nType );
+ }
+}
+
+void ImportLotus::Font_Ysize()
+{
+ LotusContext &rContext = aConv.getContext();
+ for( sal_uInt16 nCnt = 0 ; nCnt < LotusFontBuffer::nSize ; nCnt++ )
+ {
+ sal_uInt16 nSize;
+ Read( nSize );
+ rContext.maFontBuff.SetHeight( nCnt, nSize );
+ }
+}
+
+void ImportLotus::Row_( const sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nExtTab < 0, "sc.filter", "*ImportLotus::Row_(): not possible!" );
+
+ sal_uInt16 nCntDwn = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 5;
+ SCCOL nColCnt = 0;
+ sal_uInt8 nRepeats;
+ LotAttrWK3 aAttr;
+
+ bool bCenter = false;
+ SCCOL nCenterStart = 0, nCenterEnd = 0;
+ LotusContext &rContext = aConv.getContext();
+
+ sal_uInt16 nTmpRow(0);
+ Read(nTmpRow);
+ SCROW nRow(rContext.rDoc.SanitizeRow(static_cast<SCROW>(nTmpRow)));
+ sal_uInt16 nHeight(0);
+ Read(nHeight);
+
+ nHeight &= 0x0FFF;
+ nHeight *= 22;
+
+ SCTAB nDestTab(static_cast<SCTAB>(nExtTab));
+
+ if( nHeight )
+ rD.SetRowHeight(nRow, nDestTab, nHeight);
+
+ while( nCntDwn )
+ {
+ Read( aAttr );
+ Read( nRepeats );
+
+ if( aAttr.HasStyles() )
+ rContext.maAttrTable.SetAttr(
+ rContext, nColCnt, static_cast<SCCOL> ( nColCnt + nRepeats ), nRow, aAttr );
+
+ // Do this here and NOT in class LotAttrTable, as we only add attributes if the other
+ // attributes are set
+ // -> for Center-Attribute default is centered
+ if( aAttr.IsCentered() )
+ {
+ if( bCenter )
+ {
+ if (rD.HasData(nColCnt, nRow, nDestTab))
+ {
+ // new Center after previous Center
+ rD.DoMerge( nCenterStart, nRow, nCenterEnd, nRow, nDestTab);
+ nCenterStart = nColCnt;
+ }
+ }
+ else
+ {
+ // fully new Center
+ bCenter = true;
+ nCenterStart = nColCnt;
+ }
+ nCenterEnd = nColCnt + static_cast<SCCOL>(nRepeats);
+ }
+ else
+ {
+ if( bCenter )
+ {
+ // possibly reset old Center
+ rD.DoMerge( nCenterStart, nRow, nCenterEnd, nRow, nDestTab );
+ bCenter = false;
+ }
+ }
+
+ nColCnt = nColCnt + static_cast<SCCOL>(nRepeats);
+ nColCnt++;
+
+ nCntDwn--;
+ }
+
+ if( bCenter )
+ // possibly reset old Center
+ rD.DoMerge( nCenterStart, nRow, nCenterEnd, nRow, nDestTab );
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotread.cxx b/sc/source/filter/lotus/lotread.cxx
new file mode 100644
index 0000000000..4696156ffa
--- /dev/null
+++ b/sc/source/filter/lotus/lotread.cxx
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <docoptio.hxx>
+#include <docsh.hxx>
+
+#include <scdll.hxx>
+#include <scerrors.hxx>
+#include "lotfilter.hxx"
+#include <lotimpop.hxx>
+#include <lotattr.hxx>
+#include <fprogressbar.hxx>
+
+#include <sal/log.hxx>
+
+ErrCode ImportLotus::parse()
+{
+ enum STATE
+ {
+ S_START, // analyse first BOF
+ S_WK3, // in WK3-Section
+ S_WK4,
+ S_FM3,
+ S_END // Import finished
+ };
+
+ sal_uInt16 nOp;
+ sal_uInt16 nRecLen;
+ sal_uInt32 nNextRec = 0;
+ ErrCode eRet = ERRCODE_NONE;
+// ScFormulaCell *pLastFormCell;
+
+ STATE eCurrent = S_START;
+
+ nTab = 0;
+ nExtTab = -2;
+
+ pIn->Seek( nNextRec );
+
+ // start progressbar
+ ScfStreamProgressBar aPrgrsBar( *pIn, rD.GetDocumentShell() );
+ LotusContext &rContext = aConv.getContext();
+ while( eCurrent != S_END )
+ {
+ pIn->ReadUInt16( nOp ).ReadUInt16( nRecLen );
+
+ if (!pIn->good() || nNextRec > SAL_MAX_UINT32 - nRecLen - 4)
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ eCurrent = S_END;
+ if (!pIn->good())
+ break; // while
+ }
+
+ nNextRec += nRecLen + 4;
+
+ switch( eCurrent )
+ {
+
+ case S_START: // S_START
+ if( nOp )
+ {
+ eRet = SCERR_IMPORT_UNKNOWN_WK;
+ eCurrent = S_END;
+ }
+ else
+ {
+ if( nRecLen > 2 )
+ {
+ Bof();
+ switch (rContext.eFirstType)
+ {
+ case Lotus123Typ::WK3: eCurrent = S_WK3; break;
+ case Lotus123Typ::WK4: eCurrent = S_WK4; break;
+ default:
+ eRet = SCERR_IMPORT_UNKNOWN_WK;
+ eCurrent = S_END;
+ }
+ }
+ else
+ {
+ eCurrent = S_END; // TODO: add here something for <= WK1!
+ eRet = ErrCode(0xFFFFFFFF);
+ }
+ }
+ break;
+
+ case S_WK3: // S_WK3
+ case S_WK4: // S_WK4
+ switch( nOp )
+ {
+ case 0x0001: // EOF
+ eCurrent = S_FM3;
+ nTab++;
+ break;
+
+ case 0x0002: // PASSWORD
+ eRet = SCERR_IMPORT_FILEPASSWD;
+ eCurrent = S_END;
+ break;
+
+ case 0x0007: // COLUMNWIDTH
+ Columnwidth( nRecLen );
+ break;
+
+ case 0x0008: // HIDDENCOLUMN
+ Hiddencolumn( nRecLen );
+ break;
+
+ case 0x0009: // USERRANGE
+ Userrange();
+ break;
+
+ case 0x0013: // FORMAT
+
+ break;
+ case 0x0014: // ERRCELL
+ Errcell();
+ break;
+
+ case 0x0015: // NACELL
+ Nacell();
+ break;
+
+ case 0x0016: // LABELCELL
+ Labelcell();
+ break;
+
+ case 0x0017: // NUMBERCELL
+ Numbercell();
+ break;
+
+ case 0x0018: // SMALLNUMCELL
+ Smallnumcell();
+ break;
+
+ case 0x0019: // FORMULACELL
+ Formulacell( nRecLen );
+ break;
+
+ case 0x001b: // extended attributes
+ if (nRecLen > 2)
+ {
+ sal_uInt16 nSubType(0);
+ Read( nSubType );
+ nRecLen -= 2;
+ switch( nSubType )
+ {
+ case 2007: // ROW PRESENTATION
+ RowPresentation( nRecLen );
+ break;
+
+ case 14000: // NAMED SHEET
+ NamedSheet();
+ break;
+ }
+ }
+ else
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ eCurrent = S_END;
+ }
+ }
+
+ break;
+
+ case S_FM3: // S_FM3
+ break;
+
+ case S_END: // S_END
+ break;
+ }
+
+ SAL_WARN_IF( nNextRec < pIn->Tell(), "sc.filter",
+ "*ImportLotus::Read(): Read too much..." );
+
+ pIn->Seek( nNextRec );
+ aPrgrsBar.Progress();
+ }
+
+ // TODO: eliminate stupid names
+ SCTAB nTabs = rD.GetTableCount();
+ SCTAB nCnt;
+ OUString aTabName;
+ OUString aBaseName;
+ if( nTabs != 0 )
+ {
+ if( nTabs > 1 )
+ {
+ rD.GetName( 0, aBaseName );
+ aBaseName = aBaseName.copy(0, aBaseName.getLength()-1);
+ }
+ for( nCnt = 1 ; nCnt < nTabs ; nCnt++ )
+ {
+ SAL_WARN_IF( !rD.HasTable( nCnt ), "sc.filter",
+ "-ImportLotus::Read(): Where is my table?!" );
+ rD.GetName( nCnt, aTabName );
+ if( aTabName == "temp" )
+ {
+ aTabName = aBaseName;
+ rD.CreateValidTabName( aTabName );
+ rD.RenameTab( nCnt, aTabName );
+ }
+ }
+ }
+
+ return eRet;
+}
+
+ErrCode ImportLotus::Read()
+{
+ ErrCode eRet = parse();
+ rD.CalcAfterLoad();
+ return eRet;
+}
+
+ErrCode ImportLotus::Read(SvStream& rIn)
+{
+ pIn = &rIn;
+
+ bool bRead = true;
+ sal_uInt16 nOp;
+ sal_uInt16 nRecLen;
+ sal_uInt32 nNextRec = 0;
+ ErrCode eRet = ERRCODE_NONE;
+
+ nTab = 0;
+ nExtTab = -1;
+
+ pIn->Seek( nNextRec );
+
+ // start progressbar
+ ScfStreamProgressBar aPrgrsBar( *pIn, rD.GetDocumentShell() );
+ LotusContext &rContext = aConv.getContext();
+ while( bRead )
+ {
+ pIn->ReadUInt16( nOp ).ReadUInt16( nRecLen );
+
+ if (!pIn->good() || nNextRec > SAL_MAX_UINT32 - nRecLen - 4)
+ bRead = false;
+ else
+ {
+ nNextRec += nRecLen + 4;
+
+ switch( nOp )
+ {
+ case 0x0000: // BOF
+ if( nRecLen != 26 || !BofFm3() )
+ {
+ bRead = false;
+ eRet = SCERR_IMPORT_FORMAT;
+ }
+ break;
+
+ case 0x0001: // EOF
+ bRead = false;
+ SAL_WARN_IF( nTab != 0, "sc.filter",
+ "-ImportLotus::Read( SvStream& ): EOF twice!" );
+ nTab++;
+ break;
+
+ case 174: // FONT_FACE
+ Font_Face();
+ break;
+
+ case 176: // FONT_TYPE
+ Font_Type();
+ break;
+
+ case 177: // FONT_YSIZE
+ Font_Ysize();
+ break;
+
+ case 195:
+ if( nExtTab >= 0 )
+ rContext.maAttrTable.Apply(rContext, static_cast<SCTAB>(nExtTab));
+ nExtTab++;
+ break;
+ case 197:
+ Row_( nRecLen );
+ break;
+ }
+
+ SAL_WARN_IF( nNextRec < pIn->Tell(), "sc.filter",
+ "*ImportLotus::Read(): Read too much..." );
+ pIn->Seek( nNextRec );
+ aPrgrsBar.Progress();
+ }
+ }
+
+ rContext.maAttrTable.Apply(rContext, static_cast<SCTAB>(nExtTab));
+
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWKS(SvStream& rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+
+ LotusContext aContext(aDocument, RTL_TEXTENCODING_ASCII_US);
+ ImportLotus aLotusImport(aContext, rStream, RTL_TEXTENCODING_ASCII_US);
+
+ ErrCode eRet = aLotusImport.parse();
+ if (eRet == ErrCode(0xFFFFFFFF))
+ {
+ rStream.Seek(0);
+ eRet = ScImportLotus123old(aContext, rStream, RTL_TEXTENCODING_ASCII_US);
+ }
+
+ return eRet == ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotus.cxx b/sc/source/filter/lotus/lotus.cxx
new file mode 100644
index 0000000000..c89717cb68
--- /dev/null
+++ b/sc/source/filter/lotus/lotus.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 "lotfilter.hxx"
+#include <lotimpop.hxx>
+
+#include <editeng/justifyitem.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <scerrors.hxx>
+#include <filtopt.hxx>
+#include <ftools.hxx>
+#include <tool.h>
+
+ErrCode ScFormatFilterPluginImpl::ScImportLotus123( SfxMedium& rMedium, ScDocument& rDocument, rtl_TextEncoding eSrc )
+{
+ SvStream* pStream = rMedium.GetInStream();
+ if (!pStream)
+ return SCERR_IMPORT_OPEN;
+
+ pStream->Seek( 0 );
+
+ pStream->SetBufferSize( 32768 );
+
+ LotusContext aContext(rDocument, eSrc);
+
+ ImportLotus aLotusImport(aContext, *pStream, eSrc);
+
+ ErrCode eRet;
+ if (ScFilterOptions().GetWK3Flag())
+ eRet = aLotusImport.Read();
+ else
+ eRet = ErrCode(0xFFFFFFFF); // force WK1 /WKS
+
+ // WARNING: QUICK-HACK for WK1 / WKS <-> WK3 / WK4
+ if( eRet == ErrCode(0xFFFFFFFF) )
+ {
+ pStream->Seek( 0 );
+ pStream->SetBufferSize( 32768 );
+ assert(&rDocument == &aContext.rDoc);
+ eRet = ScImportLotus123old(aContext, *pStream, eSrc);
+ pStream->SetBufferSize( 0 );
+ return eRet;
+ }
+
+ if( eRet != ERRCODE_NONE )
+ return eRet;
+
+ if (aContext.eFirstType == Lotus123Typ::WK3)
+ {
+ // try to load *.FM3 file
+ INetURLObject aURL( rMedium.GetURLObject() );
+ aURL.setExtension( u"FM3" );
+ SfxMedium aMedium( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::STD_READ );
+ pStream = aMedium.GetInStream();
+ if ( pStream )
+ {
+ if( aLotusImport.Read( *pStream ) != ERRCODE_NONE )
+ eRet = SCWARN_IMPORT_WRONG_FM3;
+ }
+ else
+ eRet = SCWARN_IMPORT_OPEN_FM3;
+ }
+
+ return eRet;
+}
+
+LotusContext::LotusContext(ScDocument& rDocP, rtl_TextEncoding eQ)
+ : eTyp(eWK_UNKNOWN)
+ , bEOF(false)
+ , eCharset(eQ)
+ , rDoc(rDocP)
+ , eFirstType( Lotus123Typ::X)
+ , eActType( Lotus123Typ::X)
+ , pRngNmBffWK3( new RangeNameBufferWK3(rDocP) )
+ , maAttrTable( *this )
+{
+}
+
+LotusContext::~LotusContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/memory.cxx b/sc/source/filter/lotus/memory.cxx
new file mode 100644
index 0000000000..97ba1654cc
--- /dev/null
+++ b/sc/source/filter/lotus/memory.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 .
+ */
+
+// Remark: Variables are not declared in header file! Modules have to declare
+// their variables via extern!
+
+#include <scitems.hxx>
+#include <scmem.h>
+#include <editeng/justifyitem.hxx>
+#include "lotfilter.hxx"
+#include <tool.h>
+#include <editeng/svxenum.hxx>
+
+bool MemNew(LotusContext& rContext)
+{
+ rContext.xValueFormCache.reset(new FormCache(&rContext.rDoc));
+
+ // for tool.cxx::PutFormString()
+ rContext.xAttrRight.reset(new SvxHorJustifyItem(SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY));
+ rContext.xAttrLeft.reset(new SvxHorJustifyItem(SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY));
+ rContext.xAttrCenter.reset(new SvxHorJustifyItem(SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY));
+ rContext.xAttrRepeat.reset(new SvxHorJustifyItem(SvxCellHorJustify::Repeat, ATTR_HOR_JUSTIFY));
+ rContext.xAttrStandard.reset(
+ new SvxHorJustifyItem(SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY));
+
+ return true;
+}
+
+void MemDelete(LotusContext& rContext)
+{
+ rContext.xValueFormCache.reset();
+ rContext.xAttrRight.reset();
+ rContext.xAttrLeft.reset();
+ rContext.xAttrCenter.reset();
+ rContext.xAttrRepeat.reset();
+ rContext.xAttrStandard.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/op.cxx b/sc/source/filter/lotus/op.cxx
new file mode 100644
index 0000000000..58007a5183
--- /dev/null
+++ b/sc/source/filter/lotus/op.cxx
@@ -0,0 +1,684 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <rtl/character.hxx>
+
+#include <string.h>
+
+#include <scitems.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <postit.hxx>
+
+#include <op.h>
+#include <optab.h>
+#include <tool.h>
+#include "lotfilter.hxx"
+#include <lotform.hxx>
+#include <lotrange.hxx>
+#include <ftools.hxx>
+
+#include <vector>
+#include <map>
+#include <memory>
+
+static sal_uInt16 nDefWidth = sal_uInt16( TWIPS_PER_CHAR * 10 );
+
+void NI(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_BOF(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 /*n*/)
+{
+ r.SeekRel( 2 ); // skip version number
+}
+
+void OP_EOF(LotusContext& rContext, SvStream& /*r*/, sal_uInt16 /*n*/)
+{
+ rContext.bEOF = true;
+}
+
+void OP_Integer(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ sal_Int16 nValue(0);
+ r.ReadUChar(nFormat).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow).ReadInt16(nValue);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, 0);
+ if (rContext.rDoc.ValidAddress(aAddr))
+ {
+ rContext.rDoc.EnsureTable(0);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, static_cast<double>(nValue));
+
+ // 0 digits in fractional part!
+ SetFormat(rContext, nCol, nRow, 0, nFormat, 0);
+ }
+}
+
+void OP_Number(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ double fValue(0.0);
+ r.ReadUChar( nFormat ).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow).ReadDouble(fValue);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, 0);
+ if (rContext.rDoc.ValidAddress(aAddr))
+ {
+ fValue = ::rtl::math::round( fValue, 15 );
+ rContext.rDoc.EnsureTable(0);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, fValue);
+
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalFloat);
+ }
+}
+
+void OP_Label(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ r.ReadUChar(nFormat).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ n -= std::min<sal_uInt16>(n, 5);
+
+ std::vector<char> aText(n + 1);
+ n = r.ReadBytes(aText.data(), n);
+ aText[n] = 0;
+
+ if (rContext.rDoc.ValidColRow(nCol, nRow))
+ {
+ nFormat &= 0x80; // don't change Bit 7
+ nFormat |= 0x75; // protected does not matter, special-text is set
+
+ PutFormString(rContext, nCol, nRow, 0, aText.data());
+
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalStd);
+ }
+}
+
+void OP_Formula(LotusContext &rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ r.ReadUChar(nFormat).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow);
+ r.SeekRel(8); // skip result
+ sal_uInt16 nFormulaSize(0);
+ r.ReadUInt16(nFormulaSize);
+
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ std::unique_ptr<ScTokenArray> pResult;
+ sal_Int32 nBytesLeft = nFormulaSize;
+ ScAddress aAddress(nCol, nRow, 0);
+
+ svl::SharedStringPool& rSPool = rContext.rDoc.GetSharedStringPool();
+ LotusToSc aConv(rContext, r, rSPool, rContext.eCharset, false);
+ aConv.Reset( aAddress );
+ aConv.Convert( pResult, nBytesLeft );
+ if (!aConv.good())
+ return;
+
+ if (rContext.rDoc.ValidColRow(nCol, nRow))
+ {
+ ScFormulaCell* pCell = new ScFormulaCell(rContext.rDoc, aAddress, std::move(pResult));
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ rContext.rDoc.EnsureTable(0);
+ // coverity[tainted_data : FALSE] - ValidColRow has sanitized aAddr
+ rContext.rDoc.SetFormulaCell(ScAddress(nCol, nRow, 0), pCell);
+
+ // nFormat = Default -> number of digits in fractional part like Float
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalFloat);
+ }
+}
+
+void OP_ColumnWidth(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt16 nTmpCol(0);
+ sal_uInt8 nWidthSpaces(0);
+ r.ReadUInt16(nTmpCol).ReadUChar(nWidthSpaces);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+
+ if (!rContext.rDoc.ValidCol(nCol))
+ return;
+
+ nCol = rContext.rDoc.SanitizeCol(nCol);
+
+ sal_uInt16 nBreite;
+ if( nWidthSpaces )
+ // assuming 10cpi character set
+ nBreite = static_cast<sal_uInt16>( TWIPS_PER_CHAR * nWidthSpaces );
+ else
+ {
+ rContext.rDoc.SetColHidden(nCol, nCol, 0, true);
+ nBreite = nDefWidth;
+ }
+
+ rContext.rDoc.SetColWidth(nCol, 0, nBreite);
+}
+
+void OP_NamedRange(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ // POST: don't save for invalid coordinates
+ sal_uInt16 nColSt, nRowSt, nColEnd, nRowEnd;
+
+ char cBuffer[ 16+1 ];
+ r.ReadBytes(cBuffer, 16);
+ cBuffer[ 16 ] = 0;
+
+ r.ReadUInt16( nColSt ).ReadUInt16( nRowSt ).ReadUInt16( nColEnd ).ReadUInt16( nRowEnd );
+
+ if (!r.good())
+ return;
+
+ if (!rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColSt), nRowSt) || !rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColEnd), nRowEnd))
+ return;
+
+ std::unique_ptr<LotusRange> pRange;
+
+ if( nColSt == nColEnd && nRowSt == nRowEnd )
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt) ));
+ else
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt),
+ static_cast<SCCOL> (nColEnd), static_cast<SCROW> (nRowEnd) ));
+
+ char cBuf[sizeof(cBuffer)+1];
+ if( rtl::isAsciiDigit( static_cast<unsigned char>(*cBuffer) ) )
+ { // first char in name is a number -> prepend 'A'
+ cBuf[0] = 'A';
+ strcpy( cBuf + 1, cBuffer ); // #100211# - checked
+ }
+ else
+ strcpy( cBuf, cBuffer ); // #100211# - checked
+
+ OUString aTmp( cBuf, strlen(cBuf), rContext.eCharset );
+
+ aTmp = ScfTools::ConvertToScDefinedName( aTmp );
+
+ rContext.maRangeNames.Append( &rContext.rDoc, std::move(pRange) );
+}
+
+void OP_SymphNamedRange(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ // POST:don't save for invalid coordinates
+ sal_uInt16 nColSt, nRowSt, nColEnd, nRowEnd;
+ sal_uInt8 nType;
+
+ char cBuffer[ 16+1 ];
+ r.ReadBytes(cBuffer, 16);
+ cBuffer[ 16 ] = 0;
+
+ r.ReadUInt16( nColSt ).ReadUInt16( nRowSt ).ReadUInt16( nColEnd ).ReadUInt16( nRowEnd ).ReadUChar( nType );
+
+ if (!r.good())
+ return;
+
+ if (!rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColSt), nRowSt) || !rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColEnd), nRowEnd))
+ return;
+
+ std::unique_ptr<LotusRange> pRange;
+
+ if( nType )
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt) ));
+ else
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt),
+ static_cast<SCCOL> (nColEnd), static_cast<SCROW> (nRowEnd) ));
+
+ char cBuf[sizeof(cBuffer)+1];
+ if( rtl::isAsciiDigit( static_cast<unsigned char>(*cBuffer) ) )
+ { // first char in name is a number -> prepend 'A'
+ cBuf[0] = 'A';
+ strcpy( cBuf + 1, cBuffer ); // #100211# - checked
+ }
+ else
+ strcpy( cBuf, cBuffer ); // #100211# - checked
+
+ OUString aTmp( cBuf, strlen(cBuf), rContext.eCharset );
+ aTmp = ScfTools::ConvertToScDefinedName( aTmp );
+
+ rContext.maRangeNames.Append( &rContext.rDoc, std::move(pRange) );
+}
+
+void OP_Footer(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_Header(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_Margins(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_HiddenCols(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ SCCOL nCount = 0;
+ for (sal_uInt16 nByte = 0; nByte < 32; ++nByte) // 32 Bytes with ...
+ {
+ sal_uInt8 nCurrent(0);
+ r.ReadUChar(nCurrent);
+ for (sal_uInt16 nBit = 0; nBit < 8; ++nBit) // ...each 8 Bits = 256 Bits
+ {
+ if( nCurrent & 0x01 ) // is lowest Bit set?
+ {
+ // -> Hidden Col
+ rContext.rDoc.SetColHidden(nCount, nCount, 0, true);
+ }
+
+ nCount++;
+ nCurrent = nCurrent / 2; // the next please...
+ }
+ }
+}
+
+void OP_Window1(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( 4 ); // skip Cursor Pos
+
+ sal_uInt8 nDefaultFormat; // -> op.cpp, standard cell format
+ r.ReadUChar(nDefaultFormat);
+
+ r.SeekRel( 1 ); // skip 'unused'
+
+ r.ReadUInt16( nDefWidth );
+ if (!r.good())
+ return;
+
+ r.SeekRel( n - 8 ); // skip the rest
+
+ nDefWidth = static_cast<sal_uInt16>( TWIPS_PER_CHAR * nDefWidth );
+
+ const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+
+ // instead of default, set all Cols in SC by hand
+ for (SCCOL nCol = 0 ; nCol <= rContext.rDoc.MaxCol() ; nCol++)
+ {
+ rContext.rDoc.SetColWidth( nCol, 0, nDefWidth );
+ if (bFuzzing)
+ break;
+ }
+}
+
+void OP_Blank(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ r.ReadUChar( nFormat ).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalFloat);
+}
+
+void OP_BOF123(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 /*n*/)
+{
+ r.SeekRel( 26 );
+}
+
+void OP_EOF123(LotusContext& rContext, SvStream& /*r*/, sal_uInt16 /*n*/)
+{
+ rContext.bEOF = true;
+}
+
+void OP_Label123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt8 nTmpTab(0), nTmpCol(0);
+ sal_uInt16 nTmpRow(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ n -= std::min<sal_uInt16>(n, 4);
+
+ std::unique_ptr<char[]> pText(new char[n + 1]);
+ r.ReadBytes(pText.get(), n);
+ pText[ n ] = 0;
+
+ PutFormString(rContext, nCol, nRow, nTab, pText.get());
+}
+
+void OP_Number123(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpCol(0), nTmpTab(0);
+ sal_uInt32 nValue(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol).ReadUInt32(nValue);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, nTab);
+ if (rContext.rDoc.ValidAddress(aAddr) && nTab <= rContext.rDoc.GetMaxTableNumber())
+ {
+ double fValue = Snum32ToDouble( nValue );
+ rContext.rDoc.EnsureTable(nTab);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, fValue);
+ }
+}
+
+void OP_Formula123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpCol(0), nTmpTab(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+ r.SeekRel( 8 ); // skip Result
+
+ std::unique_ptr<ScTokenArray> pResult;
+ sal_Int32 nBytesLeft = (n > 12) ? n - 12 : 0;
+ ScAddress aAddress(nCol, nRow, nTab);
+
+ svl::SharedStringPool& rSPool = rContext.rDoc.GetSharedStringPool();
+ LotusToSc aConv(rContext, r, rSPool, rContext.eCharset, true);
+ aConv.Reset( aAddress );
+ aConv.Convert( pResult, nBytesLeft );
+ if (!aConv.good())
+ return;
+
+ if (rContext.rDoc.ValidAddress(aAddress) && nTab <= rContext.rDoc.GetMaxTableNumber())
+ {
+ ScFormulaCell* pCell = new ScFormulaCell(rContext.rDoc, aAddress, std::move(pResult));
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ rContext.rDoc.EnsureTable(nTab);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetFormulaCell(aAddress, pCell);
+ }
+}
+
+void OP_IEEENumber123(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpCol(0), nTmpTab(0);
+ double dValue(0.0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol).ReadDouble(dValue);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, nTab);
+ if (rContext.rDoc.ValidAddress(aAddr) && nTab <= rContext.rDoc.GetMaxTableNumber())
+ {
+ rContext.rDoc.EnsureTable(nTab);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, dValue);
+ }
+}
+
+void OP_Note123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpTab(0), nTmpCol(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ n -= std::min<sal_uInt16>(n, 4);
+
+ std::unique_ptr<char[]> pText(new char[n + 1]);
+ r.ReadBytes(pText.get(), n);
+ pText[ n ] = 0;
+
+ OUString aNoteText(pText.get(), strlen(pText.get()), rContext.eCharset);
+ pText.reset();
+
+ ScAddress aPos(nCol, nRow, nTab);
+ ScNoteUtil::CreateNoteFromString( rContext.rDoc, aPos, aNoteText, false, false );
+}
+
+void OP_HorAlign123(LotusContext& /*rContext*/, sal_uInt8 nAlignPattern, SfxItemSet& rPatternItemSet)
+{
+// pre: Pattern is stored in the last 3 bites of the 21st byte
+// post: Appropriate Horizontal Alignment is set in rPattern according to the bit pattern.
+//
+// LEFT:001, RIGHT:010, CENTER:011, JUSTIFY:110,
+// LEFT-Text/RIGHT-NUMBER:100, DEFAULT:000
+
+ nAlignPattern = ( nAlignPattern & 0x07);
+
+ switch (nAlignPattern)
+ {
+ case 1:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
+ break;
+ case 2:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY ) );
+ break;
+ case 3:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY) );
+ break;
+ case 4:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY ) );
+ break;
+ case 6:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Block, ATTR_HOR_JUSTIFY ) );
+ break;
+ default:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY ) );
+ break;
+ }
+}
+
+void OP_VerAlign123(LotusContext& /*rContext*/, sal_uInt8 nAlignPattern, SfxItemSet& rPatternItemSet)
+{
+// pre: Pattern is stored in the last 3 bites of the 22nd byte
+// post: Appropriate Vertical Alignment is set in rPattern according to the bit pattern.
+//
+// TOP:001, MIDDLE:010, DOWN:100, DEFAULT:000
+
+ nAlignPattern = ( nAlignPattern & 0x07);
+
+ switch (nAlignPattern)
+ {
+ case 0:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Standard, ATTR_VER_JUSTIFY) );
+ break;
+ case 1:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Top, ATTR_VER_JUSTIFY) );
+ break;
+ case 2:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Center, ATTR_VER_JUSTIFY) );
+ break;
+ case 4:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Bottom, ATTR_VER_JUSTIFY) );
+ break;
+ default:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Standard, ATTR_VER_JUSTIFY) );
+ break;
+ }
+}
+
+void OP_CreatePattern123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt16 nCode;
+
+ ScPatternAttr aPattern(rContext.rDoc.GetPool());
+ SfxItemSet& rItemSet = aPattern.GetItemSet();
+
+ r.ReadUInt16( nCode );
+ n -= std::min<sal_uInt16>(n, 2);
+
+ if ( nCode == 0x0fd2 )
+ {
+ sal_uInt16 nPatternId;
+ r.ReadUInt16( nPatternId );
+
+ sal_uInt8 Hor_Align, Ver_Align, temp;
+ bool bIsBold,bIsUnderLine,bIsItalics;
+
+ r.SeekRel(12);
+
+ // Read 17th Byte
+ r.ReadUChar( temp );
+
+ bIsBold = (temp & 0x01);
+ bIsItalics = (temp & 0x02);
+ bIsUnderLine = (temp & 0x04);
+
+ if ( bIsBold )
+ rItemSet.Put( SvxWeightItem(WEIGHT_BOLD,ATTR_FONT_WEIGHT) );
+ if ( bIsItalics )
+ rItemSet.Put( SvxPostureItem(ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ if ( bIsUnderLine )
+ rItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
+
+ r.SeekRel(3);
+
+ // Read 21st Byte
+ r.ReadUChar( Hor_Align );
+ OP_HorAlign123(rContext, Hor_Align, rItemSet );
+
+ r.ReadUChar( Ver_Align );
+ OP_VerAlign123(rContext, Ver_Align, rItemSet );
+
+ rContext.aLotusPatternPool.emplace( nPatternId, aPattern );
+ n -= std::min<sal_uInt16>(n, 20);
+ }
+ r.SeekRel(n);
+}
+
+void OP_SheetName123(LotusContext& rContext, SvStream& rStream, sal_uInt16 nLength)
+{
+ if (nLength <= 4)
+ {
+ rStream.SeekRel(nLength);
+ return;
+ }
+
+ // B0 36 [sheet number (2 bytes?)] [sheet name (null terminated char array)]
+ rStream.SeekRel(2); // ignore the first 2 bytes (B0 36).
+ sal_uInt16 nSheetNum(0);
+ rStream.ReadUInt16(nSheetNum);
+
+ const size_t nStrLen = nLength - 4;
+ std::vector<char> sSheetName(nStrLen + 1);
+ sSheetName[rStream.ReadBytes(sSheetName.data(), nStrLen)] = 0;
+
+ if (!ValidTab(nSheetNum))
+ return;
+ // coverity[tainted_data : FALSE] - ValidTab has sanitized nSheetNum
+ rContext.rDoc.MakeTable(nSheetNum);
+ if (!sSheetName.empty())
+ {
+ OUString aName(sSheetName.data(), strlen(sSheetName.data()), rContext.eCharset);
+ rContext.rDoc.RenameTab(nSheetNum, aName);
+ }
+}
+
+void OP_ApplyPatternArea123(LotusContext& rContext, SvStream& rStream)
+{
+ sal_uInt16 nOpcode, nLength;
+ sal_uInt16 nCol = 0, nColCount = 0, nRow = 0, nRowCount = 0, nTab = 0, nData, nTabCount = 0, nLevel = 0;
+
+ do
+ {
+ rStream.ReadUInt16( nOpcode ).ReadUInt16( nLength );
+ switch ( nOpcode )
+ {
+ case ROW_FORMAT_MARKER:
+ nLevel++;
+ break;
+ case COL_FORMAT_MARKER:
+ nLevel--;
+ if( nLevel == 1 )
+ {
+ nTab = nTab + nTabCount;
+ nCol = 0; nColCount = 0;
+ nRow = 0; nRowCount = 0;
+ }
+ break;
+ case LOTUS_FORMAT_INDEX:
+ if( nLength >= 2 )
+ {
+ rStream.ReadUInt16( nData );
+ rStream.SeekRel( nLength - 2 );
+ if( nLevel == 1 )
+ nTabCount = SanitizeTab(nData);
+ else if( nLevel == 2 )
+ {
+ nCol = nCol + nColCount;
+ nColCount = nData;
+ if ( nCol > 0xff ) // 256 is the max col size supported by 123
+ nCol = 0;
+ }
+ else if( nLevel == 3 )
+ {
+ nRow = nRow + nRowCount;
+ nRowCount = nData;
+ if ( nRow > 0x1fff ) // 8192 is the max row size supported by 123
+ nRow = 0;
+ }
+ }
+ else
+ rStream.SeekRel( nLength );
+ break;
+ case LOTUS_FORMAT_INFO:
+ if( nLength >= 2 )
+ {
+ rStream.ReadUInt16( nData );
+ rStream.SeekRel( nLength - 2 );
+ std::map<sal_uInt16, ScPatternAttr>::iterator loc = rContext.aLotusPatternPool.find( nData );
+ // #126338# apparently, files with invalid index occur in the wild -> don't crash then
+ if ( loc != rContext.aLotusPatternPool.end() )
+ for( int i = 0; i < nTabCount; i++)
+ {
+ rContext.rDoc.ApplyPatternAreaTab( nCol, nRow, nCol + nColCount - 1, nRow + nRowCount - 1, static_cast< SCTAB >( nTab + i ), loc->second );
+ }
+ }
+ else
+ rStream.SeekRel( nLength );
+ break;
+ default:
+ rStream.SeekRel( nLength );
+ break;
+ }
+ }
+ while( nLevel && rStream.good() );
+
+ rContext.aLotusPatternPool.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/optab.cxx b/sc/source/filter/lotus/optab.cxx
new file mode 100644
index 0000000000..b4cbaff91a
--- /dev/null
+++ b/sc/source/filter/lotus/optab.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 <op.h>
+#include <optab.h>
+#include "lotfilter.hxx"
+
+// edit function char *X( char * )
+OPCODE_FKT LotusContext::pOpFkt[ FKT_LIMIT ] =
+{ // Code
+ OP_BOF, // 0
+ OP_EOF, // 1
+ NI, // 2
+ NI, // 3
+ NI, // 4
+ NI, // 5
+ NI, // 6
+ OP_Window1, // 7
+ OP_ColumnWidth, // 8
+ NI, // 9
+ NI, // 10
+ OP_NamedRange, // 11
+ OP_Blank, // 12
+ OP_Integer, // 13
+ OP_Number, // 14
+ OP_Label, // 15
+ OP_Formula, // 16
+ NI, // 17
+ NI, // 18
+ NI, // 19
+ NI, // 20
+ NI, // 21
+ NI, // 22
+ NI, // 23
+ NI, // 24
+ NI, // 25
+ NI, // 26
+ NI, // 27
+ NI, // 28
+ NI, // 29
+ NI, // 30
+ NI, // 31
+ NI, // 32
+ NI, // 33
+ NI, // 34
+ NI, // 35
+ NI, // 36
+ OP_Footer, // 37
+ OP_Header, // 38
+ NI, // 39
+ OP_Margins, // 40
+ NI, // 41
+ NI, // 42
+ NI, // 43
+ NI, // 44
+ NI, // 45
+ NI, // 46
+ NI, // 47
+ NI, // 48
+ NI, // 49
+ NI, // 50
+ NI, // 51
+ NI, // 52
+ NI, // 53
+ NI, // 54
+ NI, // 55
+ NI, // 56
+ NI, // 57
+ NI, // 58
+ NI, // 59
+ NI, // 60
+ NI, // 61
+ NI, // 62
+ NI, // 63
+ NI, // 64
+ NI, // 65
+ NI, // 66
+ NI, // 67
+ NI, // 68
+ NI, // 69
+ NI, // 70
+ OP_SymphNamedRange, // 71
+ NI, // 72
+ NI, // 73
+ NI, // 74
+ NI, // 75
+ NI, // 76
+ NI, // 77
+ NI, // 78
+ NI, // 79
+ NI, // 80
+ NI, // 81
+ NI, // 82
+ NI, // 83
+ NI, // 84
+ NI, // 85
+ NI, // 86
+ NI, // 87
+ NI, // 88
+ NI, // 89
+ NI, // 90
+ NI, // 91
+ NI, // 92
+ NI, // 93
+ NI, // 94
+ NI, // 95
+ NI, // 96
+ NI, // 97
+ NI, // 98
+ NI, // 99
+ OP_HiddenCols, // 100
+};
+
+OPCODE_FKT LotusContext::pOpFkt123[ FKT_LIMIT123 ] =
+{ // Code
+ OP_BOF123, // 0
+ OP_EOF123, // 1
+ NI, // 2
+ NI, // 3
+ NI, // 4
+ NI, // 5
+ NI, // 6
+ NI, // 7
+ NI, // 8
+ NI, // 9
+ NI, // 10
+ NI, // 11
+ NI, // 12
+ NI, // 13
+ NI, // 14
+ NI, // 15
+ NI, // 16
+ NI, // 17
+ NI, // 18
+ NI, // 19
+ NI, // 20
+ NI, // 21
+ OP_Label123, // 22
+ NI, // 23
+ NI, // 24
+ NI, // 25
+ NI, // 26
+ OP_CreatePattern123, // 27
+ NI, // 28
+ NI, // 29
+ NI, // 30
+ NI, // 31
+ NI, // 32
+ NI, // 33
+ NI, // 34
+ OP_SheetName123, // 35
+ NI, // 36
+ OP_Number123, // 37
+ OP_Note123, // 38
+ OP_IEEENumber123, // 39
+ OP_Formula123, // 40
+ NI, // 41
+ NI, // 42
+ NI, // 43
+ NI, // 44
+ NI, // 45
+ NI, // 46
+ NI, // 47
+ NI, // 48
+ NI, // 49
+ NI, // 50
+ NI, // 51
+ NI, // 52
+ NI, // 53
+ NI, // 54
+ NI, // 55
+ NI, // 56
+ NI, // 57
+ NI, // 58
+ NI, // 59
+ NI, // 60
+ NI, // 61
+ NI, // 62
+ NI, // 63
+ NI, // 64
+ NI, // 65
+ NI, // 66
+ NI, // 67
+ NI, // 68
+ NI, // 69
+ NI, // 70
+ NI, // 71
+ NI, // 72
+ NI, // 73
+ NI, // 74
+ NI, // 75
+ NI, // 76
+ NI, // 77
+ NI, // 78
+ NI, // 79
+ NI, // 80
+ NI, // 81
+ NI, // 82
+ NI, // 83
+ NI, // 84
+ NI, // 85
+ NI, // 86
+ NI, // 87
+ NI, // 88
+ NI, // 89
+ NI, // 90
+ NI, // 91
+ NI, // 92
+ NI, // 93
+ NI, // 94
+ NI, // 95
+ NI, // 96
+ NI, // 97
+ NI, // 98
+ NI, // 99
+ NI // 100
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/tool.cxx b/sc/source/filter/lotus/tool.cxx
new file mode 100644
index 0000000000..c3bf46b4f8
--- /dev/null
+++ b/sc/source/filter/lotus/tool.cxx
@@ -0,0 +1,524 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sal/log.hxx>
+
+#include <attrib.hxx>
+#include <document.hxx>
+
+#include <tool.h>
+#include <lotrange.hxx>
+#include <namebuff.hxx>
+#include <stringutil.hxx>
+#include <tokenarray.hxx>
+#include "lotfilter.hxx"
+
+#include <math.h>
+
+void PutFormString(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, char* pString)
+{
+ // evaluate Label-Format
+ SAL_WARN_IF( pString == nullptr, "sc.filter", "PutFormString(): pString == NULL" );
+ if (!pString)
+ return;
+
+ char cForm;
+ SvxHorJustifyItem* pJustify = nullptr;
+
+ cForm = *pString;
+
+ switch( cForm )
+ {
+ case '"': // align-right
+ pJustify = rContext.xAttrRight.get();
+ pString++;
+ break;
+ case '\'': // align-left
+ pJustify = rContext.xAttrLeft.get();
+ pString++;
+ break;
+ case '^': // centered
+ pJustify = rContext.xAttrCenter.get();
+ pString++;
+ break;
+ case '|': // printer command
+ pString = nullptr;
+ break;
+ case '\\': // repetition
+ pJustify = rContext.xAttrRepeat.get();
+ pString++;
+ break;
+ default: // undefined case!
+ pJustify = rContext.xAttrStandard.get();
+ }
+
+ if (!pString)
+ return;
+
+ nCol = rContext.rDoc.SanitizeCol(nCol);
+ nRow = rContext.rDoc.SanitizeRow(nRow);
+ nTab = SanitizeTab(nTab);
+
+ rContext.rDoc.ApplyAttr( nCol, nRow, nTab, *pJustify );
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rContext.rDoc.SetString(ScAddress(nCol,nRow,nTab), OUString(pString, strlen(pString), rContext.eCharset), &aParam);
+}
+
+void SetFormat(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt8 nFormat, sal_uInt8 nSt)
+{
+ nCol = rContext.rDoc.SanitizeCol(nCol);
+ nRow = rContext.rDoc.SanitizeRow(nRow);
+ nTab = SanitizeTab(nTab);
+
+ // PREC: nSt = default number of decimal places
+ rContext.rDoc.ApplyAttr(nCol, nRow, nTab, *(rContext.xValueFormCache->GetAttr(nFormat, nSt)));
+
+ ScProtectionAttr aAttr;
+
+ aAttr.SetProtection( nFormat & 0x80 );
+
+ rContext.rDoc.ApplyAttr( nCol, nRow, nTab, aAttr );
+}
+
+double SnumToDouble( sal_Int16 nVal )
+{
+ const double pFacts[ 8 ] = {
+ 5000.0,
+ 500.0,
+ 0.05,
+ 0.005,
+ 0.0005,
+ 0.00005,
+ 0.0625,
+ 0.015625 };
+
+ double fVal;
+
+ if( nVal & 0x0001 )
+ {
+ fVal = pFacts[ ( nVal >> 1 ) & 0x0007 ];
+ fVal *= static_cast<sal_Int16>( nVal >> 4 );
+ }
+ else
+ fVal = static_cast<sal_Int16>( nVal >> 1 );
+
+ return fVal;
+}
+
+double Snum32ToDouble( sal_uInt32 nValue )
+{
+ double fValue, temp;
+
+ fValue = nValue >> 6;
+ temp = nValue & 0x0f;
+ if (temp)
+ {
+ if (nValue & 0x00000010)
+ fValue /= pow(double(10), temp);
+ else
+ fValue *= pow(double(10), temp);
+ }
+
+ if (nValue & 0x00000020)
+ fValue = -fValue;
+ return fValue;
+}
+
+FormCache::FormCache( const ScDocument* pDoc1 )
+ : nIndex(0)
+{
+ pFormTable = pDoc1->GetFormatTable();
+ for(bool & rb : bValid)
+ rb = false;
+ eLanguage = ScGlobal::eLnge;
+}
+
+FormCache::~FormCache()
+{
+}
+
+SfxUInt32Item* FormCache::NewAttr( sal_uInt8 nFormat, sal_uInt8 nSt )
+{
+ // setup new Format
+ sal_uInt8 nL, nH; // Low-/High-Nibble
+ OUString aFormString;
+ SvNumFormatType eType = SvNumFormatType::ALL;
+ sal_uInt32 nIndex1;
+ sal_uInt32 nHandle;
+ NfIndexTableOffset eIndexTableOffset = NF_NUMERIC_START;
+ bool bDefault = false;
+
+ // split into Low and High byte
+ nL = nFormat & 0x0F;
+ nH = ( nFormat & 0xF0 ) / 16;
+
+ nH &= 0x07; // extract bits 4-6
+ switch( nH )
+ {
+ case 0x00: // fixed-point number
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x01: // scientific notation
+ //fExponent;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::SCIENTIFIC, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x02: // currency
+ //fMoney;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::CURRENCY, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x03: // percentage
+ //fPercent;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::PERCENT, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x04: // Decimal
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, true, false, nL);
+ break;
+ case 0x05: // unspecified
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x06: // unspecified
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x07: // Special format
+ switch( nL )
+ {
+ case 0x00: // +/-
+ //fStandard;nSt;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, true, nSt);
+ break;
+ case 0x01: // general Format
+ //fStandard;nSt;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nSt);
+ break;
+ case 0x02: // Date: Day, Month, Year
+ //fDate;dfDayMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ eIndexTableOffset = NF_DATE_SYS_DDMMYYYY;
+ break;
+ case 0x03: // Date: Day, Month
+ //fDate;dfDayMonthLong;
+ eType = SvNumFormatType::DATE;
+ aFormString = pFormTable->GetKeyword( eLanguage, NF_KEY_DD) +
+ pFormTable->GetDateSep() + // matches last eLanguage
+ pFormTable->GetKeyword( eLanguage, NF_KEY_MMMM);
+ break;
+ case 0x04: // Date: Month, Year
+ //fDate;dfMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ aFormString = pFormTable->GetKeyword( eLanguage, NF_KEY_MM) +
+ pFormTable->GetDateSep() + // matches last eLanguage
+ pFormTable->GetKeyword( eLanguage, NF_KEY_YYYY);
+ break;
+ case 0x05: // Text formats
+ //fString;nSt;
+ eType = SvNumFormatType::TEXT;
+ eIndexTableOffset = NF_TEXT;
+ break;
+ case 0x06: // hidden
+ //wFlag |= paHideAll;bSetFormat = sal_False;
+ eType = SvNumFormatType::NUMBER;
+ aFormString = "\"\"";
+ break;
+ case 0x07: // Time: hour, min, sec
+ //fTime;tfHourMinSec24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMMSS;
+ break;
+ case 0x08: // Time: hour, min
+ //fTime;tfHourMin24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMM;
+ break;
+ case 0x09: // Date, intern sal_Int32 1
+ //fDate;dfDayMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ eIndexTableOffset = NF_DATE_SYS_DDMMYYYY;
+ break;
+ case 0x0A: // Date, intern sal_Int32 2
+ //fDate;dfDayMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ eIndexTableOffset = NF_DATE_SYS_DDMMYYYY;
+ break;
+ case 0x0B: // Time, intern sal_Int32 1
+ //fTime;tfHourMinSec24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMMSS;
+ break;
+ case 0x0C: // Time, intern sal_Int32 2
+ //fTime;tfHourMinSec24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMMSS;
+ break;
+ case 0x0F: // Default
+ //fStandard;nSt;
+ bDefault = true;
+ break;
+ default:
+ //fStandard;nSt;
+ bDefault = true;
+ break;
+ }
+ break;
+ }
+
+ // push Format into table
+ if( bDefault )
+ nHandle = 0;
+ else if (eIndexTableOffset != NF_NUMERIC_START)
+ nHandle = pFormTable->GetFormatIndex( eIndexTableOffset, eLanguage);
+ else
+ {
+ sal_Int32 nDummy;
+ pFormTable->PutEntry( aFormString, nDummy, eType, nHandle, eLanguage );
+ }
+
+ return new SfxUInt32Item( ATTR_VALUE_FORMAT, nHandle );
+}
+
+void LotusRange::MakeHash()
+{
+ // 33222222222211111111110000000000
+ // 10987654321098765432109876543210
+ // ******** nColS
+ // ******** nColE
+ // **************** nRowS
+ // **************** nRowE
+ nHash = static_cast<sal_uInt32>(nColStart);
+ nHash += static_cast<sal_uInt32>(nColEnd) << 6;
+ nHash += static_cast<sal_uInt32>(nRowStart) << 12;
+ nHash += static_cast<sal_uInt32>(nRowEnd ) << 16;
+}
+
+LotusRange::LotusRange( SCCOL nCol, SCROW nRow )
+{
+ nColStart = nColEnd = nCol;
+ nRowStart = nRowEnd = nRow;
+ nId = ID_FAIL;
+ MakeHash();
+}
+
+LotusRange::LotusRange( SCCOL nCS, SCROW nRS, SCCOL nCE, SCROW nRE )
+{
+ nColStart = nCS;
+ nColEnd = nCE;
+ nRowStart = nRS;
+ nRowEnd = nRE;
+ nId = ID_FAIL;
+ MakeHash();
+}
+
+LotusRange::LotusRange( const LotusRange& rCpy )
+{
+ Copy( rCpy );
+}
+
+LotusRangeList::LotusRangeList()
+{
+ aComplRef.InitFlags();
+
+ ScSingleRefData* pSingRef;
+ nIdCnt = 1;
+
+ pSingRef = &aComplRef.Ref1;
+ pSingRef->SetRelTab(0);
+ pSingRef->SetColRel( false );
+ pSingRef->SetRowRel( false );
+ pSingRef->SetFlag3D( false );
+
+ pSingRef = &aComplRef.Ref2;
+ pSingRef->SetRelTab(0);
+ pSingRef->SetColRel( false );
+ pSingRef->SetRowRel( false );
+ pSingRef->SetFlag3D( false );
+}
+
+LotusRangeList::~LotusRangeList ()
+{
+}
+
+LR_ID LotusRangeList::GetIndex( const LotusRange &rRef )
+{
+ auto pIter = std::find_if(maRanges.begin(), maRanges.end(),
+ [&rRef](const std::unique_ptr<LotusRange>& pRange) { return rRef == *pRange; });
+ if (pIter != maRanges.end())
+ return (*pIter)->nId;
+
+ return ID_FAIL;
+}
+
+void LotusRangeList::Append( const ScDocument* pDoc, std::unique_ptr<LotusRange> pLR )
+{
+ assert( pLR );
+ auto pLRTmp = pLR.get();
+ maRanges.push_back(std::move(pLR));
+
+ ScTokenArray aTokArray(*pDoc);
+
+ ScSingleRefData* pSingRef = &aComplRef.Ref1;
+
+ pSingRef->SetAbsCol(pLRTmp->nColStart);
+ pSingRef->SetAbsRow(pLRTmp->nRowStart);
+
+ if( pLRTmp->IsSingle() )
+ aTokArray.AddSingleReference( *pSingRef );
+ else
+ {
+ pSingRef = &aComplRef.Ref2;
+ pSingRef->SetAbsCol(pLRTmp->nColEnd);
+ pSingRef->SetAbsRow(pLRTmp->nRowEnd);
+ aTokArray.AddDoubleReference( aComplRef );
+ }
+
+ pLRTmp->SetId( nIdCnt );
+
+ nIdCnt++;
+}
+
+RangeNameBufferWK3::RangeNameBufferWK3(const ScDocument& rDoc)
+ : pScTokenArray( new ScTokenArray(rDoc) )
+{
+ nIntCount = 1;
+}
+
+RangeNameBufferWK3::~RangeNameBufferWK3()
+{
+}
+
+void RangeNameBufferWK3::Add( const ScDocument& rDoc, const OUString& rOrgName, const ScComplexRefData& rCRD )
+{
+ Entry aInsert( rOrgName, rCRD );
+
+ pScTokenArray->Clear();
+
+ const ScSingleRefData& rRef1 = rCRD.Ref1;
+ const ScSingleRefData& rRef2 = rCRD.Ref2;
+ ScAddress aAbs1 = rRef1.toAbs(rDoc, ScAddress());
+ ScAddress aAbs2 = rRef2.toAbs(rDoc, ScAddress());
+ if (aAbs1 == aAbs2)
+ {
+ pScTokenArray->AddSingleReference( rCRD.Ref1 );
+ aInsert.bSingleRef = true;
+ }
+ else
+ {
+ pScTokenArray->AddDoubleReference( rCRD );
+ aInsert.bSingleRef = false;
+ }
+
+ aInsert.nRelInd = nIntCount;
+ nIntCount++;
+
+ maEntries.push_back( aInsert );
+}
+
+bool RangeNameBufferWK3::FindRel( const OUString& rRef, sal_uInt16& rIndex )
+{
+ StringHashEntry aRef( rRef );
+
+ std::vector<Entry>::const_iterator itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [&aRef](const Entry& rEntry) { return aRef == rEntry.aStrHashEntry; });
+ if (itr != maEntries.end())
+ {
+ rIndex = itr->nRelInd;
+ return true;
+ }
+
+ return false;
+}
+
+bool RangeNameBufferWK3::FindAbs( std::u16string_view rRef, sal_uInt16& rIndex )
+{
+ if (rRef.empty())
+ return false;
+ StringHashEntry aRef(OUString(rRef.substr(1))); // search w/o '$'!
+
+ std::vector<Entry>::iterator itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [&aRef](const Entry& rEntry) { return aRef == rEntry.aStrHashEntry; });
+ if (itr != maEntries.end())
+ {
+ // setup new range if needed
+ if( itr->nAbsInd )
+ rIndex = itr->nAbsInd;
+ else
+ {
+ ScSingleRefData* pRef = &itr->aScComplexRefDataRel.Ref1;
+ pScTokenArray->Clear();
+
+ pRef->SetColRel( false );
+ pRef->SetRowRel( false );
+ pRef->SetTabRel( true );
+
+ if( itr->bSingleRef )
+ pScTokenArray->AddSingleReference( *pRef );
+ else
+ {
+ pRef = &itr->aScComplexRefDataRel.Ref2;
+ pRef->SetColRel( false );
+ pRef->SetRowRel( false );
+ pRef->SetTabRel( true );
+ pScTokenArray->AddDoubleReference( itr->aScComplexRefDataRel );
+ }
+
+ rIndex = itr->nAbsInd = nIntCount;
+ nIntCount++;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/SparklineFragment.cxx b/sc/source/filter/oox/SparklineFragment.cxx
new file mode 100644
index 0000000000..afb3f64285
--- /dev/null
+++ b/sc/source/filter/oox/SparklineFragment.cxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <SparklineFragment.hxx>
+#include <oox/core/contexthandler.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/helper/helper.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <Sparkline.hxx>
+#include <themebuffer.hxx>
+#include <docmodel/color/ComplexColor.hxx>
+
+using ::oox::core::ContextHandlerRef;
+
+namespace oox::xls
+{
+namespace
+{
+// TODO: deduplicate with importOOXColor
+::Color getColor(const AttributeList& rAttribs, ThemeBuffer const& rThemeBuffer)
+{
+ if (rAttribs.hasAttribute(XML_rgb))
+ {
+ return ::Color(ColorAlpha, rAttribs.getIntegerHex(XML_rgb, sal_Int32(API_RGB_TRANSPARENT)));
+ }
+ else if (rAttribs.hasAttribute(XML_theme))
+ {
+ sal_uInt32 nThemeIndex = rAttribs.getUnsigned(XML_theme, 0);
+
+ // Excel has a bug in the mapping of index 0, 1, 2 and 3.
+ if (nThemeIndex == 0)
+ nThemeIndex = 1;
+ else if (nThemeIndex == 1)
+ nThemeIndex = 0;
+ else if (nThemeIndex == 2)
+ nThemeIndex = 3;
+ else if (nThemeIndex == 3)
+ nThemeIndex = 2;
+
+ ::Color aColor = rThemeBuffer.getColorByIndex(nThemeIndex);
+ double nTint = rAttribs.getDouble(XML_tint, 0.0);
+
+ if (nTint > 0.0)
+ aColor.ApplyTintOrShade(nTint * 10000);
+ return aColor;
+ }
+
+ return ::Color();
+}
+
+model::ComplexColor fillComplexColor(const AttributeList& rAttribs, ThemeBuffer const& rThemeBuffer,
+ const GraphicHelper& rGraphicHelper)
+{
+ XlsColor aColor;
+ aColor.importColor(rAttribs);
+ model::ComplexColor aComplexColor = aColor.createComplexColor(rGraphicHelper, -1);
+ ::Color aFinalColor = getColor(rAttribs, rThemeBuffer);
+ aComplexColor.setFinalColor(aFinalColor);
+ return aComplexColor;
+}
+
+void addColorsToSparklineAttributes(sc::SparklineAttributes& rAttributes, sal_Int32 nElement,
+ const AttributeList& rAttribs, ThemeBuffer& rThemeBuffer,
+ const GraphicHelper& rHelper)
+{
+ switch (nElement)
+ {
+ case XLS14_TOKEN(colorSeries):
+ rAttributes.setColorSeries(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorNegative):
+ rAttributes.setColorNegative(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorAxis):
+ rAttributes.setColorAxis(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorMarkers):
+ rAttributes.setColorMarkers(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorFirst):
+ rAttributes.setColorFirst(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorLast):
+ rAttributes.setColorLast(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorHigh):
+ rAttributes.setColorHigh(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ case XLS14_TOKEN(colorLow):
+ rAttributes.setColorLow(fillComplexColor(rAttribs, rThemeBuffer, rHelper));
+ break;
+ default:
+ break;
+ }
+}
+
+sc::SparklineType parseSparklineType(std::u16string_view rString)
+{
+ if (rString == u"column")
+ return sc::SparklineType::Column;
+ else if (rString == u"stacked")
+ return sc::SparklineType::Stacked;
+ return sc::SparklineType::Line;
+}
+
+sc::DisplayEmptyCellsAs parseDisplayEmptyCellsAs(std::u16string_view rString)
+{
+ if (rString == u"span")
+ return sc::DisplayEmptyCellsAs::Span;
+ else if (rString == u"gap")
+ return sc::DisplayEmptyCellsAs::Gap;
+ return sc::DisplayEmptyCellsAs::Zero;
+}
+
+sc::AxisType parseAxisType(std::u16string_view rString)
+{
+ if (rString == u"group")
+ return sc::AxisType::Group;
+ else if (rString == u"custom")
+ return sc::AxisType::Custom;
+ return sc::AxisType::Individual;
+}
+
+void addAttributesToSparklineAttributes(sc::SparklineAttributes& rSparklineAttributes,
+ const AttributeList& rAttribs)
+{
+ auto oManualMax = rAttribs.getDouble(XML_manualMax);
+ auto oManualMin = rAttribs.getDouble(XML_manualMin);
+
+ rSparklineAttributes.setLineWeight(rAttribs.getDouble(XML_lineWeight, 0.75));
+
+ OUString sType = rAttribs.getString(XML_type, "line");
+ rSparklineAttributes.setType(parseSparklineType(sType));
+
+ rSparklineAttributes.setDateAxis(rAttribs.getBool(XML_dateAxis, false));
+
+ OUString sDisplayEmptyCellsAs = rAttribs.getString(XML_displayEmptyCellsAs, "zero");
+ rSparklineAttributes.setDisplayEmptyCellsAs(parseDisplayEmptyCellsAs(sDisplayEmptyCellsAs));
+
+ rSparklineAttributes.setMarkers(rAttribs.getBool(XML_markers, false));
+ rSparklineAttributes.setHigh(rAttribs.getBool(XML_high, false));
+ rSparklineAttributes.setLow(rAttribs.getBool(XML_low, false));
+ rSparklineAttributes.setFirst(rAttribs.getBool(XML_first, false));
+ rSparklineAttributes.setLast(rAttribs.getBool(XML_last, false));
+ rSparklineAttributes.setNegative(rAttribs.getBool(XML_negative, false));
+ rSparklineAttributes.setDisplayXAxis(rAttribs.getBool(XML_displayXAxis, false));
+ rSparklineAttributes.setDisplayHidden(rAttribs.getBool(XML_displayHidden, false));
+
+ OUString sMinAxisType = rAttribs.getString(XML_minAxisType, "individual");
+ rSparklineAttributes.setMinAxisType(parseAxisType(sMinAxisType));
+
+ OUString sMaxAxisType = rAttribs.getString(XML_maxAxisType, "individual");
+ rSparklineAttributes.setMaxAxisType(parseAxisType(sMaxAxisType));
+
+ rSparklineAttributes.setRightToLeft(rAttribs.getBool(XML_rightToLeft, false));
+
+ if (rSparklineAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ rSparklineAttributes.setManualMax(oManualMax.value());
+ if (rSparklineAttributes.getMinAxisType() == sc::AxisType::Custom)
+ rSparklineAttributes.setManualMin(oManualMin.value());
+}
+
+} // end anonymous namespace
+
+SparklineGroupsContext::SparklineGroupsContext(WorksheetContextBase& rFragment)
+ : WorksheetContextBase(rFragment)
+{
+}
+
+ContextHandlerRef SparklineGroupsContext::onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs)
+{
+ switch (nElement)
+ {
+ case XLS14_TOKEN(sparklineGroup):
+ {
+ auto& rLastGroup = m_aSparklineGroups.emplace_back();
+ auto& rSparklineAttributes = rLastGroup.getSparklineGroup()->getAttributes();
+ rSparklineAttributes.resetColors();
+ addAttributesToSparklineAttributes(rSparklineAttributes, rAttribs);
+ OUString sGUID = rAttribs.getString(XR2_TOKEN(uid), OUString());
+ tools::Guid aGuid(OUStringToOString(sGUID, RTL_TEXTENCODING_ASCII_US));
+ rLastGroup.getSparklineGroup()->setID(aGuid);
+ return this;
+ }
+ case XLS14_TOKEN(colorSeries):
+ case XLS14_TOKEN(colorNegative):
+ case XLS14_TOKEN(colorAxis):
+ case XLS14_TOKEN(colorMarkers):
+ case XLS14_TOKEN(colorFirst):
+ case XLS14_TOKEN(colorLast):
+ case XLS14_TOKEN(colorHigh):
+ case XLS14_TOKEN(colorLow):
+ {
+ auto& rLastGroup = m_aSparklineGroups.back();
+ auto& rSparklineAttributes = rLastGroup.getSparklineGroup()->getAttributes();
+ addColorsToSparklineAttributes(rSparklineAttributes, nElement, rAttribs, getTheme(),
+ getBaseFilter().getGraphicHelper());
+ return this;
+ }
+ case XLS14_TOKEN(sparklines):
+ {
+ return this;
+ }
+ case XLS14_TOKEN(sparkline):
+ {
+ auto& rLastGroup = m_aSparklineGroups.back();
+ rLastGroup.getSparklines().emplace_back();
+ return this;
+ }
+ }
+ return this;
+}
+
+void SparklineGroupsContext::onStartElement(const AttributeList&) {}
+
+void SparklineGroupsContext::onCharacters(const OUString& rChars)
+{
+ if (getCurrentElement() == XM_TOKEN(sqref) || getCurrentElement() == XM_TOKEN(f))
+ {
+ ScDocument& rDocument = getScDocument();
+ auto& rLastGroup = m_aSparklineGroups.back();
+ auto& rLastSparkline = rLastGroup.getSparklines().back();
+ ScRangeList aRange;
+ if (ScRangeStringConverter::GetRangeListFromString(aRange, rChars, rDocument,
+ formula::FormulaGrammar::CONV_XL_OOX))
+ {
+ if (!aRange.empty())
+ {
+ if (getCurrentElement() == XM_TOKEN(sqref))
+ {
+ rLastSparkline.m_aTargetRange = aRange;
+
+ // Need to set the current sheet index to the range as
+ // it is assumed that the address string refers to
+ // the current sheet and is not defined in the string.
+ for (auto& rRange : rLastSparkline.m_aTargetRange)
+ {
+ rRange.aStart.SetTab(getSheetIndex());
+ rRange.aEnd.SetTab(getSheetIndex());
+ }
+ }
+ else if (getCurrentElement() == XM_TOKEN(f))
+ rLastSparkline.m_aInputRange = aRange;
+ }
+ }
+ }
+}
+
+void SparklineGroupsContext::onEndElement()
+{
+ if (getCurrentElement() == XLS14_TOKEN(sparklineGroup))
+ {
+ auto& rLastGroup = m_aSparklineGroups.back();
+ for (Sparkline& rSparkline : rLastGroup.getSparklines())
+ {
+ insertSparkline(rLastGroup, rSparkline);
+ }
+ }
+}
+
+void SparklineGroupsContext::insertSparkline(SparklineGroup& rSparklineGroup, Sparkline& rSparkline)
+{
+ auto& rDocument = getScDocument();
+ if (rSparkline.m_aTargetRange.size() == 1)
+ {
+ auto& rRange = rSparkline.m_aTargetRange[0];
+ if (rRange.aStart == rRange.aEnd)
+ {
+ auto pSparklineGroup = rSparklineGroup.getSparklineGroup();
+ auto* pCreated = rDocument.CreateSparkline(rRange.aStart, pSparklineGroup);
+ pCreated->setInputRange(rSparkline.m_aInputRange);
+ }
+ }
+}
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/addressconverter.cxx b/sc/source/filter/oox/addressconverter.cxx
new file mode 100644
index 0000000000..2408e2670e
--- /dev/null
+++ b/sc/source/filter/oox/addressconverter.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 <addressconverter.hxx>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <convuno.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+//! TODO: this limit may change, is there a way to obtain it via API?
+const sal_Int16 API_MAXTAB = MAXTAB;
+
+const sal_Int32 OOX_MAXCOL = static_cast< sal_Int32 >( (1 << 14) - 1 );
+const sal_Int32 OOX_MAXROW = static_cast< sal_Int32 >( (1 << 20) - 1 );
+const sal_Int16 OOX_MAXTAB = static_cast< sal_Int16 >( (1 << 15) - 1 );
+
+} // namespace
+
+
+void BinAddress::read( SequenceInputStream& rStrm )
+{
+ mnRow = rStrm.readInt32();
+ mnCol = rStrm.readInt32();
+}
+
+void BinRange::read( SequenceInputStream& rStrm )
+{
+ maFirst.mnRow = rStrm.readInt32();
+ maLast.mnRow = rStrm.readInt32();
+ maFirst.mnCol = rStrm.readInt32();
+ maLast.mnCol = rStrm.readInt32();
+}
+
+void BinRangeList::read( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCount = rStrm.readInt32();
+ mvRanges.resize( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 16 ) );
+ for( auto& rRange : mvRanges )
+ rRange.read( rStrm );
+}
+
+AddressConverter::AddressConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbColOverflow( false ),
+ mbRowOverflow( false ),
+ mbTabOverflow( false )
+{
+ initializeMaxPos( OOX_MAXTAB, OOX_MAXCOL, OOX_MAXROW );
+}
+
+bool AddressConverter::parseOoxAddress2d(
+ sal_Int32& ornColumn, sal_Int32& ornRow,
+ std::u16string_view aString, sal_Int32 nStart, sal_Int32 nLength )
+{
+ ornColumn = ornRow = 0;
+ if( (nStart < 0) || (nStart >= sal_Int32(aString.size())) || (nLength < 2) )
+ return false;
+
+ const sal_Unicode* pcChar = aString.data() + nStart;
+ const sal_Unicode* pcEndChar = pcChar + ::std::min( nLength, sal_Int32(aString.size() - nStart) );
+
+ enum { STATE_COL, STATE_ROW } eState = STATE_COL;
+ while( pcChar < pcEndChar )
+ {
+ sal_Unicode cChar = *pcChar;
+ switch( eState )
+ {
+ case STATE_COL:
+ {
+ if( ('a' <= cChar) && (cChar <= 'z') )
+ cChar = (cChar - 'a') + 'A';
+ if( ('A' <= cChar) && (cChar <= 'Z') )
+ {
+ /* Return, if 1-based column index is already 6 characters
+ long (12356631 is column index for column AAAAAA). */
+ if( ornColumn >= 12356631 )
+ return false;
+ ornColumn = (ornColumn * 26) + (cChar - 'A' + 1);
+ }
+ else if( ornColumn > 0 )
+ {
+ --pcChar;
+ eState = STATE_ROW;
+ }
+ else
+ return false;
+ }
+ break;
+
+ case STATE_ROW:
+ {
+ if( ('0' <= cChar) && (cChar <= '9') )
+ {
+ // return, if 1-based row is already 9 digits long
+ if( ornRow >= 100000000 )
+ return false;
+ ornRow = (ornRow * 10) + (cChar - '0');
+ }
+ else
+ return false;
+ }
+ break;
+ }
+ ++pcChar;
+ }
+
+ --ornColumn;
+ --ornRow;
+ return (ornColumn >= 0) && (ornRow >= 0);
+}
+
+bool AddressConverter::parseOoxAddress2d( sal_Int32& ornColumn, sal_Int32& ornRow, std::string_view aStr )
+{
+ ornColumn = ornRow = 0;
+
+ enum { STATE_COL, STATE_ROW } eState = STATE_COL;
+
+ const char* pStr = aStr.data();
+ while (pStr != aStr.data() + aStr.size())
+ {
+ char cChar = *pStr;
+ switch( eState )
+ {
+ case STATE_COL:
+ {
+ if( ('a' <= cChar) && (cChar <= 'z') )
+ cChar = (cChar - 'a') + 'A';
+ if( ('A' <= cChar) && (cChar <= 'Z') )
+ {
+ /* Return, if 1-based column index is already 6 characters
+ long (12356631 is column index for column AAAAAA). */
+ if( ornColumn >= 12356631 )
+ return false;
+ ornColumn = (ornColumn * 26) + (cChar - 'A' + 1);
+ }
+ else if( ornColumn > 0 )
+ {
+ --pStr;
+ eState = STATE_ROW;
+ }
+ else
+ return false;
+ }
+ break;
+
+ case STATE_ROW:
+ {
+ if( ('0' <= cChar) && (cChar <= '9') )
+ {
+ // return, if 1-based row is already 9 digits long
+ if( ornRow >= 100000000 )
+ return false;
+ ornRow = (ornRow * 10) + (cChar - '0');
+ }
+ else
+ return false;
+ }
+ break;
+ }
+ ++pStr;
+ }
+
+ --ornColumn;
+ --ornRow;
+ return (ornColumn >= 0) && (ornRow >= 0);
+}
+
+bool AddressConverter::parseOoxRange2d(
+ sal_Int32& ornStartColumn, sal_Int32& ornStartRow,
+ sal_Int32& ornEndColumn, sal_Int32& ornEndRow,
+ std::u16string_view aString, sal_Int32 nStart )
+{
+ ornStartColumn = ornStartRow = ornEndColumn = ornEndRow = 0;
+ if( (nStart < 0) || (nStart >= sal_Int32(aString.size())) )
+ return false;
+
+ sal_Int32 nEnd = nStart + ( aString.size() - nStart );
+ size_t nColonPos = aString.find( ':', nStart );
+ if( nColonPos != std::u16string_view::npos && (nStart < sal_Int32(nColonPos)) && (sal_Int32(nColonPos + 1) < nEnd) )
+ {
+ return
+ parseOoxAddress2d( ornStartColumn, ornStartRow, aString, nStart, nColonPos - nStart ) &&
+ parseOoxAddress2d( ornEndColumn, ornEndRow, aString, nColonPos + 1, SAL_MAX_INT32 - nColonPos - 1 );
+ }
+
+ if( parseOoxAddress2d( ornStartColumn, ornStartRow, aString, nStart ) )
+ {
+ ornEndColumn = ornStartColumn;
+ ornEndRow = ornStartRow;
+ return true;
+ }
+
+ return false;
+}
+
+bool AddressConverter::checkCol( sal_Int32 nCol, bool bTrackOverflow )
+{
+ bool bValid = (0 <= nCol) && ( nCol <= maMaxPos.Col() );
+ if( !bValid && bTrackOverflow )
+ mbColOverflow = true;
+ return bValid;
+}
+
+bool AddressConverter::checkRow( sal_Int32 nRow, bool bTrackOverflow )
+{
+ bool bValid = (0 <= nRow) && ( nRow <= maMaxPos.Row() );
+ if( !bValid && bTrackOverflow )
+ mbRowOverflow = true;
+ return bValid;
+}
+
+bool AddressConverter::checkTab( sal_Int16 nSheet, bool bTrackOverflow )
+{
+ bool bValid = (0 <= nSheet) && ( nSheet <= maMaxPos.Tab() );
+ if( !bValid && bTrackOverflow )
+ mbTabOverflow |= ( nSheet > maMaxPos.Tab() ); // do not warn for deleted refs (-1)
+ return bValid;
+}
+
+bool AddressConverter::checkCellAddress( const ScAddress& rAddress, bool bTrackOverflow )
+{
+ return
+ checkTab( rAddress.Tab(), bTrackOverflow ) &&
+ checkCol( rAddress.Col(), bTrackOverflow ) &&
+ checkRow( rAddress.Row(), bTrackOverflow );
+}
+
+bool AddressConverter::convertToCellAddressUnchecked( ScAddress& orAddress,
+ const OUString& rString, sal_Int16 nSheet )
+{
+ orAddress.SetTab(nSheet);
+ sal_Int32 nCol = 0;
+ sal_Int32 nRow = 0;
+ bool bRes = parseOoxAddress2d( nCol, nRow, rString );
+ orAddress.SetRow(nRow);
+ orAddress.SetCol(nCol);
+
+ return bRes;
+}
+
+bool AddressConverter::convertToCellAddressUnchecked(
+ ScAddress& orAddress, std::string_view pStr, sal_Int16 nSheet )
+{
+ orAddress.SetTab(nSheet);
+ sal_Int32 nCol = 0;
+ sal_Int32 nRow = 0;
+ bool bRes = parseOoxAddress2d(nCol, nRow, pStr);
+ orAddress.SetRow(nRow);
+ orAddress.SetCol(nCol);
+
+ return bRes;
+}
+
+bool AddressConverter::convertToCellAddress( ScAddress& orAddress,
+ const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ return
+ convertToCellAddressUnchecked( orAddress, rString, nSheet ) &&
+ checkCellAddress( orAddress, bTrackOverflow );
+}
+
+bool AddressConverter::convertToCellAddress(
+ ScAddress& rAddress,
+ std::string_view pStr, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ if (!convertToCellAddressUnchecked(rAddress, pStr, nSheet))
+ return false;
+
+ return checkCellAddress(rAddress, bTrackOverflow);
+}
+
+ScAddress AddressConverter::createValidCellAddress(
+ const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ ScAddress aAddress( 0, 0, 0 );
+ if( !convertToCellAddress( aAddress, rString, nSheet, bTrackOverflow ) )
+ {
+ aAddress.SetTab( getLimitedValue< sal_Int16, sal_Int16 >( nSheet, 0, maMaxPos.Tab() ) );
+ aAddress.SetCol( ::std::min( aAddress.Col(), maMaxPos.Col() ) );
+ aAddress.SetRow( ::std::min( aAddress.Row(), maMaxPos.Row() ) );
+ }
+ return aAddress;
+}
+
+void AddressConverter::convertToCellAddressUnchecked( ScAddress& orAddress,
+ const BinAddress& rBinAddress, sal_Int16 nSheet )
+{
+ orAddress.SetTab(nSheet);
+ orAddress.SetCol(rBinAddress.mnCol);
+ orAddress.SetRow(rBinAddress.mnRow);
+}
+
+bool AddressConverter::convertToCellAddress( ScAddress& orAddress,
+ const BinAddress& rBinAddress, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ convertToCellAddressUnchecked( orAddress, rBinAddress, nSheet );
+ return checkCellAddress( orAddress, bTrackOverflow );
+}
+
+ScAddress AddressConverter::createValidCellAddress(
+ const BinAddress& rBinAddress, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ ScAddress aAddress ( 0, 0, 0 );
+ if( !convertToCellAddress( aAddress, rBinAddress, nSheet, bTrackOverflow ) )
+ {
+ aAddress.SetTab( getLimitedValue< sal_Int16, sal_Int16 >( nSheet, 0, maMaxPos.Tab() ) );
+ aAddress.SetCol( getLimitedValue< sal_Int32, sal_Int32 >( rBinAddress.mnCol, 0, sal_Int32( maMaxPos.Col() ) ) );
+ aAddress.SetRow( getLimitedValue< sal_Int32, sal_Int32 >( rBinAddress.mnRow, 0, sal_Int32( maMaxPos.Row() ) ) );
+ }
+ return aAddress;
+}
+
+bool AddressConverter::checkCellRange( const ScRange& rRange, bool bAllowOverflow, bool bTrackOverflow )
+{
+ return
+ (checkCol( rRange.aEnd.Col(), bTrackOverflow ) || bAllowOverflow) && // bAllowOverflow after checkCol to track overflow!
+ (checkRow( rRange.aEnd.Row(), bTrackOverflow ) || bAllowOverflow) && // bAllowOverflow after checkRow to track overflow!
+ checkTab( rRange.aStart.Tab(), bTrackOverflow ) &&
+ checkCol( rRange.aStart.Col(), bTrackOverflow ) &&
+ checkRow( rRange.aStart.Row(), bTrackOverflow );
+}
+
+bool AddressConverter::validateCellRange( ScRange& orRange, bool bAllowOverflow, bool bTrackOverflow )
+{
+ if( orRange.aStart.Col() > orRange.aEnd.Col() )
+ {
+ SCCOL nCol = orRange.aStart.Col();
+ orRange.aStart.SetCol( orRange.aEnd.Col() );
+ orRange.aEnd.SetCol( nCol );
+ }
+ if( orRange.aStart.Row() > orRange.aEnd.Row() )
+ {
+ SCROW nRow = orRange.aStart.Row();
+ orRange.aStart.SetRow( orRange.aEnd.Row() );
+ orRange.aEnd.SetRow( nRow );
+ }
+ if( !checkCellRange( orRange, bAllowOverflow, bTrackOverflow ) )
+ return false;
+ if( orRange.aEnd.Col() > maMaxPos.Col() )
+ orRange.aEnd.SetCol( maMaxPos.Col() );
+ if( orRange.aEnd.Row() > maMaxPos.Row() )
+ orRange.aEnd.SetRow( maMaxPos.Row() );
+ return true;
+}
+
+bool AddressConverter::convertToCellRangeUnchecked( ScRange& orRange,
+ std::u16string_view aString, sal_Int16 nSheet )
+{
+ orRange.aStart.SetTab( nSheet );
+ orRange.aEnd.SetTab( nSheet );
+ sal_Int32 aStartCol = orRange.aStart.Col();
+ sal_Int32 aStartRow = orRange.aStart.Row();
+ sal_Int32 aEndCol = orRange.aEnd.Col();
+ sal_Int32 aEndRow = orRange.aEnd.Row();
+ bool bReturnValue = parseOoxRange2d( aStartCol, aStartRow, aEndCol, aEndRow, aString );
+ orRange.aStart.SetCol( aStartCol );
+ orRange.aStart.SetRow( aStartRow );
+ orRange.aEnd.SetCol( aEndCol );
+ orRange.aEnd.SetRow( aEndRow );
+ return bReturnValue;
+}
+
+bool AddressConverter::convertToCellRange( ScRange& orRange,
+ std::u16string_view aString, sal_Int16 nSheet, bool bAllowOverflow, bool bTrackOverflow )
+{
+ return
+ convertToCellRangeUnchecked( orRange, aString, nSheet ) &&
+ validateCellRange( orRange, bAllowOverflow, bTrackOverflow );
+}
+
+void AddressConverter::convertToCellRangeUnchecked( ScRange& orRange,
+ const BinRange& rBinRange, sal_Int16 nSheet )
+{
+ orRange.aStart.SetTab( nSheet );
+ orRange.aStart.SetCol( rBinRange.maFirst.mnCol );
+ orRange.aStart.SetRow( rBinRange.maFirst.mnRow );
+ orRange.aEnd.SetTab( nSheet );
+ orRange.aEnd.SetCol( rBinRange.maLast.mnCol );
+ orRange.aEnd.SetRow( rBinRange.maLast.mnRow );
+}
+
+bool AddressConverter::convertToCellRange( ScRange& orRange,
+ const BinRange& rBinRange, sal_Int16 nSheet, bool bAllowOverflow, bool bTrackOverflow )
+{
+ convertToCellRangeUnchecked( orRange, rBinRange, nSheet );
+ return validateCellRange( orRange, bAllowOverflow, bTrackOverflow );
+}
+
+void AddressConverter::validateCellRangeList( ScRangeList& orRanges, bool bTrackOverflow )
+{
+ for( size_t nIndex = orRanges.size(); nIndex > 0; --nIndex )
+ if( !validateCellRange( orRanges[ nIndex - 1 ], true, bTrackOverflow ) )
+ orRanges.Remove( nIndex - 1 );
+}
+
+void AddressConverter::convertToCellRangeList( ScRangeList& orRanges,
+ std::u16string_view aString, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ size_t nPos = 0;
+ size_t nLen = aString.size();
+ ScRange aRange;
+ while( nPos != std::u16string_view::npos && (nPos < nLen) )
+ {
+ std::u16string_view aToken = o3tl::getToken(aString, u' ', nPos );
+ if( !aToken.empty() && convertToCellRange( aRange, aToken, nSheet, true, bTrackOverflow ) )
+ orRanges.push_back(aRange);
+ }
+}
+
+void AddressConverter::convertToCellRangeList( ScRangeList& orRanges,
+ const BinRangeList& rBinRanges, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ ScRange aRange;
+ for( const auto& rBinRange : rBinRanges )
+ if( convertToCellRange( aRange, rBinRange, nSheet, true, bTrackOverflow ) )
+ orRanges.push_back( aRange );
+}
+
+Sequence<CellRangeAddress> AddressConverter::toApiSequence(const ScRangeList& orRanges)
+{
+ const size_t nSize = orRanges.size();
+ Sequence<CellRangeAddress> aRangeSequence(nSize);
+ CellRangeAddress* pApiRanges = aRangeSequence.getArray();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ ScUnoConversion::FillApiRange(pApiRanges[i], orRanges[i]);
+ }
+ return aRangeSequence;
+}
+
+// private --------------------------------------------------------------------
+
+void AddressConverter::initializeMaxPos(
+ sal_Int16 nMaxXlsTab, sal_Int32 nMaxXlsCol, sal_Int32 nMaxXlsRow )
+{
+ maMaxXlsPos.Set( nMaxXlsCol, nMaxXlsRow, nMaxXlsTab);
+
+ // maximum cell position in Calc
+ try
+ {
+ Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ Reference< XCellRangeAddressable > xAddressable( xSheetsIA->getByIndex( 0 ), UNO_QUERY_THROW );
+ CellRangeAddress aRange = xAddressable->getRangeAddress();
+ maMaxApiPos = ScAddress( aRange.EndColumn, aRange.EndRow, API_MAXTAB );
+ maMaxPos = getBaseFilter().isImportFilter() ? maMaxApiPos : maMaxXlsPos;
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "AddressConverter::AddressConverter - cannot get sheet limits" );
+ }
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx
new file mode 100644
index 0000000000..9c93281760
--- /dev/null
+++ b/sc/source/filter/oox/autofilterbuffer.cxx
@@ -0,0 +1,973 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <autofilterbuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/FilterFieldType.hpp>
+#include <com/sun/star/sheet/FilterConnection.hpp>
+#include <com/sun/star/sheet/FilterOperator2.hpp>
+#include <com/sun/star/sheet/TableFilterField3.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <com/sun/star/sheet/XSheetFilterDescriptor3.hpp>
+#include <com/sun/star/table/TableOrientation.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <editeng/brushitem.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <defnamesbuffer.hxx>
+#include <biffhelper.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <scitems.hxx>
+#include <sortparam.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <stylesbuffer.hxx>
+#include <userlist.hxx>
+
+namespace oox::xls {
+
+using namespace css;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01;
+const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02;
+
+const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON = 0x0001;
+const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON = 0x0002;
+
+const sal_uInt8 BIFF_FILTER_DATATYPE_NONE = 0;
+const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE = 4;
+const sal_uInt8 BIFF_FILTER_DATATYPE_STRING = 6;
+const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN = 8;
+const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY = 12;
+const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY = 14;
+
+bool lclGetApiOperatorFromToken( sal_Int32& rnApiOperator, sal_Int32 nToken )
+{
+ switch( nToken )
+ {
+ case XML_lessThan: rnApiOperator = FilterOperator2::LESS; return true;
+ case XML_equal: rnApiOperator = FilterOperator2::EQUAL; return true;
+ case XML_lessThanOrEqual: rnApiOperator = FilterOperator2::LESS_EQUAL; return true;
+ case XML_greaterThan: rnApiOperator = FilterOperator2::GREATER; return true;
+ case XML_notEqual: rnApiOperator = FilterOperator2::NOT_EQUAL; return true;
+ case XML_greaterThanOrEqual: rnApiOperator = FilterOperator2::GREATER_EQUAL; return true;
+ }
+ return false;
+}
+
+/** Removes leading asterisk characters from the passed string.
+ @return True = at least one asterisk character has been removed. */
+bool lclTrimLeadingAsterisks( OUString& rValue )
+{
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nPos = 0;
+ while( (nPos < nLength) && (rValue[ nPos ] == '*') )
+ ++nPos;
+ if( nPos > 0 )
+ {
+ rValue = rValue.copy( nPos );
+ return true;
+ }
+ return false;
+}
+
+/** Removes trailing asterisk characters from the passed string.
+ @return True = at least one asterisk character has been removed. */
+bool lclTrimTrailingAsterisks( OUString& rValue )
+{
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nPos = nLength;
+ while( (nPos > 0) && (rValue[ nPos - 1 ] == '*') )
+ --nPos;
+ if( nPos < nLength )
+ {
+ rValue = rValue.copy( 0, nPos );
+ return true;
+ }
+ return false;
+}
+
+/** Converts wildcard characters '*' and '?' to regular expressions and quotes
+ RE meta characters.
+ @return True = passed string has been changed (RE needs to be enabled). */
+bool lclConvertWildcardsToRegExp( OUString& rValue )
+{
+ // check existence of the wildcard characters '*' and '?'
+ if( !rValue.isEmpty() && ((rValue.indexOf( '*' ) >= 0) || (rValue.indexOf( '?' ) >= 0)) )
+ {
+ OUStringBuffer aBuffer;
+ aBuffer.ensureCapacity( rValue.getLength() + 5 );
+ const sal_Unicode* pcChar = rValue.getStr();
+ const sal_Unicode* pcEnd = pcChar + rValue.getLength();
+ for( ; pcChar < pcEnd; ++pcChar )
+ {
+ switch( *pcChar )
+ {
+ case '?':
+ aBuffer.append( '.' );
+ break;
+ case '*':
+ aBuffer.append( ".*" );
+ break;
+ case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
+ // quote RE meta characters
+ aBuffer.append( "\\" + OUStringChar(*pcChar) );
+ break;
+ default:
+ aBuffer.append( *pcChar );
+ }
+ }
+ rValue = aBuffer.makeStringAndClear();
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+ApiFilterSettings::ApiFilterSettings()
+{
+}
+
+void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, double fValue )
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = nOperator;
+ rFilterField.Values.realloc(1);
+ auto pValues = rFilterField.Values.getArray();
+ pValues[0].FilterType = FilterFieldType::NUMERIC;
+ pValues[0].NumericValue = fValue;
+}
+
+void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue )
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = nOperator;
+ rFilterField.Values.realloc(1);
+ auto pValues = rFilterField.Values.getArray();
+ pValues[0].FilterType = FilterFieldType::STRING;
+ pValues[0].StringValue = rValue;
+}
+
+void ApiFilterSettings::appendField(bool bAnd, util::Color aColor, bool bIsBackgroundColor)
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = FilterOperator2::EQUAL;
+ rFilterField.Values.realloc(1);
+ auto pValues = rFilterField.Values.getArray();
+ pValues[0].FilterType
+ = bIsBackgroundColor ? FilterFieldType::BACKGROUND_COLOR : FilterFieldType::TEXT_COLOR;
+ pValues[0].ColorValue = aColor;
+}
+
+void ApiFilterSettings::appendField( bool bAnd, const std::vector<std::pair<OUString, bool>>& rValues )
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = FilterOperator2::EQUAL;
+ rFilterField.Values.realloc(rValues.size());
+ auto pValues = rFilterField.Values.getArray();
+ size_t i = 0;
+
+ for( auto const& it : rValues )
+ {
+ pValues[i].StringValue = it.first;
+ pValues[i++].FilterType
+ = it.second ? FilterFieldType::DATE : FilterFieldType::STRING;
+ }
+}
+
+FilterSettingsBase::FilterSettingsBase( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void FilterSettingsBase::importAttribs( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+}
+
+void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
+{
+}
+
+ApiFilterSettings FilterSettingsBase::finalizeImport()
+{
+ return ApiFilterSettings();
+}
+
+DiscreteFilter::DiscreteFilter( const WorkbookHelper& rHelper ) :
+ FilterSettingsBase( rHelper ),
+ mnCalendarType( XML_none ),
+ mbShowBlank( false )
+{
+}
+
+void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( filters ):
+ mnCalendarType = rAttribs.getToken( XML_calendarType, XML_none );
+ mbShowBlank = rAttribs.getBool( XML_blank, false );
+ break;
+
+ case XLS_TOKEN( filter ):
+ {
+ OUString aValue = rAttribs.getXString( XML_val, OUString() );
+ if( !aValue.isEmpty() )
+ maValues.push_back( std::make_pair(aValue, false) );
+ }
+ break;
+
+ case XLS_TOKEN( dateGroupItem ):
+ {
+ OUString aDateValue;
+ // it is just a fallback, we do not need the XML_day as default value,
+ // because if the dateGroupItem exists also XML_dateTimeGrouping exists!
+ sal_uInt16 nToken = rAttribs.getToken(XML_dateTimeGrouping, XML_day);
+ if( nToken == XML_year || nToken == XML_month || nToken == XML_day ||
+ nToken == XML_hour || nToken == XML_minute || nToken == XML_second )
+ {
+ aDateValue = rAttribs.getString(XML_year, OUString());
+
+ if( nToken == XML_month || nToken == XML_day || nToken == XML_hour ||
+ nToken == XML_minute || nToken == XML_second )
+ {
+ OUString aMonthName = rAttribs.getString(XML_month, OUString());
+ if( aMonthName.getLength() == 1 )
+ aMonthName = "0" + aMonthName;
+ aDateValue += "-" + aMonthName;
+
+ if( nToken == XML_day || nToken == XML_hour || nToken == XML_minute ||
+ nToken == XML_second )
+ {
+ OUString aDayName = rAttribs.getString(XML_day, OUString());
+ if( aDayName.getLength() == 1 )
+ aDayName = "0" + aDayName;
+ aDateValue += "-" + aDayName;
+
+ if( nToken == XML_hour || nToken == XML_minute || nToken == XML_second )
+ {
+ OUString aHourName = rAttribs.getString(XML_hour, OUString());
+ if( aHourName.getLength() == 1 )
+ aHourName = "0" + aHourName;
+ aDateValue += " " + aHourName;
+
+ if( nToken == XML_minute || nToken == XML_second )
+ {
+ OUString aMinName = rAttribs.getString(XML_minute, OUString());
+ if( aMinName.getLength() == 1 )
+ aMinName = "0" + aMinName;
+ aDateValue += ":" + aMinName;
+
+ if( nToken == XML_second )
+ {
+ OUString aSecName = rAttribs.getString(XML_second, OUString());
+ if( aSecName.getLength() == 1 )
+ aSecName = "0" + aSecName;
+ aDateValue += ":" + aSecName;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !aDateValue.isEmpty() )
+ maValues.push_back( std::make_pair(aDateValue, true) );
+ }
+ break;
+ }
+}
+
+void DiscreteFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( nRecId )
+ {
+ case BIFF12_ID_DISCRETEFILTERS:
+ {
+ sal_Int32 nShowBlank, nCalendarType;
+ nShowBlank = rStrm.readInt32();
+ nCalendarType = rStrm.readInt32();
+
+ static const sal_Int32 spnCalendarTypes[] = {
+ XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew,
+ XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench };
+ mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none );
+ mbShowBlank = nShowBlank != 0;
+ }
+ break;
+
+ case BIFF12_ID_DISCRETEFILTER:
+ {
+ OUString aValue = BiffHelper::readString( rStrm );
+ if( !aValue.isEmpty() )
+ maValues.push_back( std::make_pair(aValue, false) );
+ }
+ break;
+ }
+}
+
+ApiFilterSettings DiscreteFilter::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ aSettings.maFilterFields.reserve( maValues.size() );
+
+ // insert all filter values
+ aSettings.appendField( true, maValues );
+
+ // extra field for 'show empty'
+ if( mbShowBlank )
+ aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
+
+ /* Require disabled regular expressions, filter entries may contain
+ any RE meta characters. */
+ if( !maValues.empty() )
+ aSettings.mobNeedsRegExp = false;
+
+ return aSettings;
+}
+
+Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) :
+ FilterSettingsBase( rHelper ),
+ mfValue( 0.0 ),
+ mbTop( true ),
+ mbPercent( false )
+{
+}
+
+void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( nElement == XLS_TOKEN( top10 ) )
+ {
+ mfValue = rAttribs.getDouble( XML_val, 0.0 );
+ mbTop = rAttribs.getBool( XML_top, true );
+ mbPercent = rAttribs.getBool( XML_percent, false );
+ }
+}
+
+void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( nRecId == BIFF12_ID_TOP10FILTER )
+ {
+ sal_uInt8 nFlags;
+ nFlags = rStrm.readuChar();
+ mfValue = rStrm.readDouble();
+ mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
+ mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT );
+ }
+}
+
+ApiFilterSettings Top10Filter::finalizeImport()
+{
+ sal_Int32 nOperator = mbTop ?
+ (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
+ (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES);
+ ApiFilterSettings aSettings;
+ aSettings.appendField( true, nOperator, mfValue );
+ return aSettings;
+}
+
+ColorFilter::ColorFilter(const WorkbookHelper& rHelper)
+ : FilterSettingsBase(rHelper)
+ , mbIsBackgroundColor(false)
+{
+}
+
+void ColorFilter::importAttribs(sal_Int32 nElement, const AttributeList& rAttribs)
+{
+ if (nElement == XLS_TOKEN(colorFilter))
+ {
+ // When cellColor attribute not found, it means cellColor = true
+ // cellColor = 0 (false) -> TextColor
+ // cellColor = 1 (true) -> BackgroundColor
+ mbIsBackgroundColor = rAttribs.getBool(XML_cellColor, true);
+ msStyleName = getStyles().createDxfStyle( rAttribs.getInteger(XML_dxfId, -1) );
+ }
+}
+
+void ColorFilter::importRecord(sal_Int32 /* nRecId */, SequenceInputStream& /* rStrm */)
+{
+ // TODO
+}
+
+ApiFilterSettings ColorFilter::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ ScDocument& rDoc = getScDocument();
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ rDoc.GetStyleSheetPool()->Find(msStyleName, SfxStyleFamily::Para));
+ if (!pStyleSheet)
+ return aSettings;
+
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ // Color (whether text or background color) is always stored in ATTR_BACKGROUND
+ if (const SvxBrushItem* pItem = rItemSet.GetItem<SvxBrushItem>(ATTR_BACKGROUND))
+ {
+ ::Color aColor = pItem->GetFiltColor();
+ util::Color nColor(aColor);
+ aSettings.appendField(true, nColor, mbIsBackgroundColor);
+ }
+ return aSettings;
+}
+
+FilterCriterionModel::FilterCriterionModel() :
+ mnOperator( XML_equal ),
+ mnDataType( BIFF_FILTER_DATATYPE_NONE )
+{
+}
+
+void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator )
+{
+ static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID,
+ XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+void FilterCriterionModel::readBiffData( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nOperator;
+ mnDataType = rStrm.readuChar();
+ nOperator = rStrm.readuChar();
+ setBiffOperator( nOperator );
+
+ switch( mnDataType )
+ {
+ case BIFF_FILTER_DATATYPE_DOUBLE:
+ maValue <<= rStrm.readDouble();
+ break;
+ case BIFF_FILTER_DATATYPE_STRING:
+ {
+ rStrm.skip( 8 );
+ OUString aValue = BiffHelper::readString( rStrm ).trim();
+ if( !aValue.isEmpty() )
+ maValue <<= aValue;
+ }
+ break;
+ case BIFF_FILTER_DATATYPE_BOOLEAN:
+ maValue <<= (rStrm.readuInt8() != 0);
+ rStrm.skip( 7 );
+ break;
+ case BIFF_FILTER_DATATYPE_EMPTY:
+ rStrm.skip( 8 );
+ if( mnOperator == XML_equal )
+ maValue <<= OUString();
+ break;
+ case BIFF_FILTER_DATATYPE_NOTEMPTY:
+ rStrm.skip( 8 );
+ if( mnOperator == XML_notEqual )
+ maValue <<= OUString();
+ break;
+ default:
+ OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
+ rStrm.skip( 8 );
+ }
+}
+
+CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) :
+ FilterSettingsBase( rHelper ),
+ mbAnd( false )
+{
+}
+
+void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( customFilters ):
+ mbAnd = rAttribs.getBool( XML_and, false );
+ break;
+
+ case XLS_TOKEN( customFilter ):
+ {
+ FilterCriterionModel aCriterion;
+ aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal );
+ OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim();
+ if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (!aValue.isEmpty()) )
+ aCriterion.maValue <<= aValue;
+ appendCriterion( aCriterion );
+ }
+ break;
+ }
+}
+
+void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( nRecId )
+ {
+ case BIFF12_ID_CUSTOMFILTERS:
+ mbAnd = rStrm.readInt32() == 0;
+ break;
+
+ case BIFF12_ID_CUSTOMFILTER:
+ {
+ FilterCriterionModel aCriterion;
+ aCriterion.readBiffData( rStrm );
+ appendCriterion( aCriterion );
+ }
+ break;
+ }
+}
+
+ApiFilterSettings CustomFilter::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
+ for( const auto& rCriterion : maCriteria )
+ {
+ // first extract the filter operator
+ sal_Int32 nOperator = 0;
+ bool bValidOperator = lclGetApiOperatorFromToken( nOperator, rCriterion.mnOperator );
+ if( bValidOperator )
+ {
+ if( rCriterion.maValue.has< OUString >() )
+ {
+ // string argument
+ OUString aValue;
+ rCriterion.maValue >>= aValue;
+ // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
+ bool bEqual = nOperator == FilterOperator2::EQUAL;
+ bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL;
+ if( bEqual || bNotEqual )
+ {
+ if( aValue.isEmpty() )
+ {
+ // empty comparison string: create empty/not empty filters
+ nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY;
+ }
+ else
+ {
+ // compare to something: try to find begins/ends/contains
+ bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue );
+ bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue );
+ // just '***' matches everything, do not create a filter field
+ bValidOperator = !aValue.isEmpty();
+ if( bValidOperator )
+ {
+ if( bHasLeadingAsterisk && bHasTrailingAsterisk )
+ nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS;
+ else if( bHasLeadingAsterisk )
+ nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH;
+ else if( bHasTrailingAsterisk )
+ nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH;
+ // else: no asterisks, stick to equal/not equal
+ }
+ }
+ }
+
+ if( bValidOperator )
+ {
+ // if wildcards are present, require RE mode, otherwise keep don't care state
+ if( lclConvertWildcardsToRegExp( aValue ) )
+ aSettings.mobNeedsRegExp = true;
+ // create a new UNO API filter field
+ aSettings.appendField( mbAnd, nOperator, aValue );
+ }
+ }
+ else if( rCriterion.maValue.has< double >() )
+ {
+ // floating-point argument
+ double fValue = 0.0;
+ rCriterion.maValue >>= fValue;
+ aSettings.appendField( mbAnd, nOperator, fValue );
+ }
+ }
+ }
+ return aSettings;
+}
+
+void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion )
+{
+ if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() )
+ maCriteria.push_back( rCriterion );
+}
+
+FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnColId( -1 ),
+ mbHiddenButton( false ),
+ mbShowButton( true )
+{
+}
+
+void FilterColumn::importFilterColumn( const AttributeList& rAttribs )
+{
+ mnColId = rAttribs.getInteger( XML_colId, -1 );
+ mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false );
+ mbShowButton = rAttribs.getBool( XML_showButton, true );
+}
+
+void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ mnColId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON );
+ mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
+}
+
+ApiFilterSettings FilterColumn::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ if( (0 <= mnColId) && mxSettings )
+ {
+ // filter settings object creates a sequence of filter fields
+ aSettings = mxSettings->finalizeImport();
+ // add column index to all filter fields
+ for( auto& rFilterField : aSettings.maFilterFields )
+ rFilterField.Field = mnColId;
+ }
+ return aSettings;
+}
+
+bool FilterColumn::isButtonHidden()
+{
+ return (mbShowButton == false) || (mbHiddenButton == true);
+}
+
+// SortCondition
+
+SortCondition::SortCondition( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbDescending( false )
+{
+}
+
+void SortCondition::importSortCondition( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
+ AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
+
+ maSortCustomList = rAttribs.getString( XML_customList, OUString() );
+ mbDescending = rAttribs.getBool( XML_descending, false );
+}
+
+// AutoFilter
+
+AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
+ AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
+}
+
+void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ AddressConverter::convertToCellRangeUnchecked( maRange, aBinRange, nSheet );
+}
+
+void AutoFilter::importSortState( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
+ AddressConverter::convertToCellRangeUnchecked( maSortRange, aRangeStr, nSheet );
+}
+
+FilterColumn& AutoFilter::createFilterColumn()
+{
+ FilterColumnVector::value_type xFilterColumn = std::make_shared<FilterColumn>( *this );
+ maFilterColumns.push_back( xFilterColumn );
+ return *xFilterColumn;
+}
+
+SortCondition& AutoFilter::createSortCondition()
+{
+ SortConditionVector::value_type xSortCondition = std::make_shared<SortCondition>( *this );
+ maSortConditions.push_back( xSortCondition );
+ return *xSortCondition;
+}
+
+void AutoFilter::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange, sal_Int16 nSheet )
+{
+ // convert filter settings using the filter descriptor of the database range
+ const Reference<XSheetFilterDescriptor3> xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
+ if( !xFilterDesc.is() )
+ return;
+
+ // set some common properties for the auto filter range
+ PropertySet aDescProps( xFilterDesc );
+ aDescProps.setProperty( PROP_IsCaseSensitive, false );
+ aDescProps.setProperty( PROP_SkipDuplicates, false );
+ aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
+ aDescProps.setProperty( PROP_ContainsHeader, true );
+ aDescProps.setProperty( PROP_CopyOutputData, false );
+
+ // resulting list of all UNO API filter fields
+ ::std::vector<TableFilterField3> aFilterFields;
+
+ // track if columns require to enable or disable regular expressions
+ std::optional< bool > obNeedsRegExp;
+
+ /* Track whether the filter fields of the first filter column are
+ connected with 'or'. In this case, other filter fields cannot be
+ inserted without altering the result of the entire filter, due to
+ Calc's precedence for the 'and' connection operator. Example:
+ Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
+ B2 belong to filter column B, will be evaluated by Calc as
+ '(A1 and B1) or (B2 and C1)'. */
+ bool bHasOrConnection = false;
+
+ ScDocument& rDoc = getScDocument();
+ SCCOL nCol = maRange.aStart.Col();
+ SCROW nRow = maRange.aStart.Row();
+ SCTAB nTab = maRange.aStart.Tab();
+
+ // process all filter column objects, exit when 'or' connection exists
+ for( const auto& rxFilterColumn : maFilterColumns )
+ {
+ // the filter settings object creates a list of filter fields
+ ApiFilterSettings aSettings = rxFilterColumn->finalizeImport();
+ ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
+
+ if (rxFilterColumn->isButtonHidden())
+ {
+ auto nFlag = rDoc.GetAttr(nCol, nRow, nTab, ATTR_MERGE_FLAG)->GetValue();
+ rDoc.ApplyAttr(nCol, nRow, nTab, ScMergeFlagAttr(nFlag & ~ScMF::Auto));
+ }
+ nCol++;
+
+ /* Check whether mode for regular expressions is compatible with
+ the global mode in obNeedsRegExp. If either one is still in
+ don't-care state, all is fine. If both are set, they must be
+ equal. */
+ bool bRegExpCompatible = !obNeedsRegExp.has_value() || !aSettings.mobNeedsRegExp.has_value() || (obNeedsRegExp.value() == aSettings.mobNeedsRegExp.value());
+
+ // check whether fields are connected by 'or' (see comments above).
+ if( rColumnFields.size() >= 2 )
+ bHasOrConnection = std::any_of(rColumnFields.begin() + 1, rColumnFields.end(),
+ [](const css::sheet::TableFilterField3& rColumnField) { return rColumnField.Connection == FilterConnection_OR; });
+
+ /* Skip the column filter, if no filter fields have been created,
+ and if the mode for regular expressions of the
+ filter column does not fit. */
+ if( !rColumnFields.empty() && bRegExpCompatible )
+ {
+ /* Add 'and' connection to the first filter field to connect
+ it to the existing filter fields of other columns. */
+ rColumnFields[ 0 ].Connection = FilterConnection_AND;
+
+ // insert the new filter fields
+ aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
+
+ // update the regular expressions mode
+ assignIfUsed( obNeedsRegExp, aSettings.mobNeedsRegExp );
+ }
+
+ if( bHasOrConnection )
+ break;
+ }
+
+ // insert all filter fields to the filter descriptor
+ if( !aFilterFields.empty() )
+ xFilterDesc->setFilterFields3( comphelper::containerToSequence( aFilterFields ) );
+
+ // regular expressions
+ bool bUseRegExp = obNeedsRegExp.value_or( false );
+ aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
+
+ // sort
+ if (maSortConditions.empty())
+ return;
+
+ const SortConditionVector::value_type& xSortConditionPointer = *maSortConditions.begin();
+ const SortCondition& rSorConditionLoaded = *xSortConditionPointer;
+
+ ScSortParam aParam;
+ aParam.bUserDef = false;
+ aParam.nUserIndex = 0;
+ aParam.bByRow = false;
+
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (!rSorConditionLoaded.maSortCustomList.isEmpty())
+ {
+ for (size_t i=0; pUserList && i < pUserList->size(); i++)
+ {
+ const OUString aEntry((*pUserList)[i].GetString());
+ if (aEntry.equalsIgnoreAsciiCase(rSorConditionLoaded.maSortCustomList))
+ {
+ aParam.bUserDef = true;
+ aParam.nUserIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (!aParam.bUserDef)
+ {
+ pUserList->emplace_back(rSorConditionLoaded.maSortCustomList);
+ aParam.bUserDef = true;
+ aParam.nUserIndex = pUserList->size()-1;
+ }
+
+ // set sort parameter if we have detected it
+ if (!aParam.bUserDef)
+ return;
+
+ SCCOLROW nStartPos = aParam.bByRow ? maRange.aStart.Col() : maRange.aStart.Row();
+ if (rSorConditionLoaded.mbDescending)
+ {
+ // descending sort - need to enable 1st SortParam slot
+ assert(aParam.GetSortKeyCount() == DEFSORT);
+
+ aParam.maKeyState[0].bDoSort = true;
+ aParam.maKeyState[0].bAscending = false;
+ aParam.maKeyState[0].nField += nStartPos;
+ }
+
+ ScDBData* pDBData = rDoc.GetDBAtArea(
+ nSheet,
+ maRange.aStart.Col(), maRange.aStart.Row(),
+ maRange.aEnd.Col(), maRange.aEnd.Row());
+
+ if (pDBData)
+ pDBData->SetSortParam(aParam);
+ else
+ OSL_FAIL("AutoFilter::finalizeImport(): cannot find matching DBData");
+}
+
+AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+AutoFilter& AutoFilterBuffer::createAutoFilter()
+{
+ AutoFilterVector::value_type xAutoFilter = std::make_shared<AutoFilter>( *this );
+ maAutoFilters.push_back( xAutoFilter );
+ return *xAutoFilter;
+}
+
+void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet )
+{
+ // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
+ const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get();
+ if(!pFilterDBName)
+ return;
+
+ ScRange aFilterRange;
+ if( !(pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.aStart.Tab() == nSheet)) )
+ return;
+
+ // use the same name for the database range as used for the defined name '_FilterDatabase'
+ Reference< XDatabaseRange > xDatabaseRange = createUnnamedDatabaseRangeObject( aFilterRange );
+ // first, try to create an auto filter
+ bool bHasAutoFilter = finalizeImport( xDatabaseRange, nSheet );
+ // no success: try to create an advanced filter
+ if( bHasAutoFilter || !xDatabaseRange.is() )
+ return;
+
+ // the built-in defined name 'Criteria' must exist
+ const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get();
+ if( !pCriteriaName )
+ return;
+
+ ScRange aCriteriaRange;
+ if( !pCriteriaName->getAbsoluteRange( aCriteriaRange ) )
+ return;
+
+ // set some common properties for the filter descriptor
+ PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() );
+ aDescProps.setProperty( PROP_IsCaseSensitive, false );
+ aDescProps.setProperty( PROP_SkipDuplicates, false );
+ aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
+ aDescProps.setProperty( PROP_ContainsHeader, true );
+ // criteria range may contain wildcards, but these are incompatible with REs
+ aDescProps.setProperty( PROP_UseRegularExpressions, false );
+
+ // position of output data (if built-in defined name 'Extract' exists)
+ DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet );
+ ScRange aOutputRange;
+ bool bHasOutputRange = xExtractName && xExtractName->getAbsoluteRange( aOutputRange );
+ aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange );
+ if( bHasOutputRange )
+ {
+ aDescProps.setProperty( PROP_SaveOutputPosition, true );
+ aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.aStart.Tab(), aOutputRange.aStart.Col(), aOutputRange.aStart.Row() ) );
+ }
+
+ /* Properties of the database range (must be set after
+ modifying properties of the filter descriptor,
+ otherwise the 'FilterCriteriaSource' property gets
+ deleted). */
+ PropertySet aRangeProps( xDatabaseRange );
+ aRangeProps.setProperty( PROP_AutoFilter, false );
+ aRangeProps.setProperty( PROP_FilterCriteriaSource,
+ CellRangeAddress( aCriteriaRange.aStart.Tab(),
+ aCriteriaRange.aStart.Col(), aCriteriaRange.aStart.Row(),
+ aCriteriaRange.aEnd.Col(), aCriteriaRange.aEnd.Row() ));
+}
+
+bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange, sal_Int16 nSheet )
+{
+ AutoFilter* pAutoFilter = getActiveAutoFilter();
+ if( pAutoFilter && rxDatabaseRange.is() ) try
+ {
+ // the property 'AutoFilter' enables the drop-down buttons
+ PropertySet aRangeProps( rxDatabaseRange );
+ aRangeProps.setProperty( PROP_AutoFilter, true );
+
+ pAutoFilter->finalizeImport( rxDatabaseRange, nSheet );
+
+ // return true to indicate enabled autofilter
+ return true;
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+}
+
+AutoFilter* AutoFilterBuffer::getActiveAutoFilter()
+{
+ // Excel expects not more than one auto filter per sheet or table
+ OSL_ENSURE( maAutoFilters.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
+ // stick to the last imported auto filter
+ return maAutoFilters.empty() ? nullptr : maAutoFilters.back().get();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/autofiltercontext.cxx b/sc/source/filter/oox/autofiltercontext.cxx
new file mode 100644
index 0000000000..9cdbacea4e
--- /dev/null
+++ b/sc/source/filter/oox/autofiltercontext.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 <autofiltercontext.hxx>
+#include <biffhelper.hxx>
+
+#include <autofilterbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+FilterSettingsContext::FilterSettingsContext( WorksheetContextBase& rParent, FilterSettingsBase& rFilterSettings ) :
+ WorksheetContextBase( rParent ),
+ mrFilterSettings( rFilterSettings )
+{
+}
+
+ContextHandlerRef FilterSettingsContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( filters ):
+ if( nElement == XLS_TOKEN( filter ) || nElement == XLS_TOKEN( dateGroupItem )) return this;
+ break;
+ case XLS_TOKEN( customFilters ):
+ if( nElement == XLS_TOKEN( customFilter ) ) return this;
+ break;
+ case XLS_TOKEN( colorFilter ):
+ if( nElement == XLS_TOKEN( colorFilter ) ) return this;
+ break;
+ }
+ return nullptr;
+}
+
+void FilterSettingsContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrFilterSettings.importAttribs( getCurrentElement(), rAttribs );
+}
+
+ContextHandlerRef FilterSettingsContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_DISCRETEFILTERS:
+ if( nRecId == BIFF12_ID_DISCRETEFILTER ) return this;
+ break;
+ case BIFF12_ID_CUSTOMFILTERS:
+ if( nRecId == BIFF12_ID_CUSTOMFILTER ) return this;
+ break;
+ }
+ return nullptr;
+}
+
+void FilterSettingsContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrFilterSettings.importRecord( getCurrentElement(), rStrm );
+}
+
+FilterColumnContext::FilterColumnContext( WorksheetContextBase& rParent, FilterColumn& rFilterColumn ) :
+ WorksheetContextBase( rParent ),
+ mrFilterColumn( rFilterColumn )
+{
+}
+
+ContextHandlerRef FilterColumnContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( getCurrentElement() == XLS_TOKEN( filterColumn ) ) switch( nElement )
+ {
+ case XLS_TOKEN( filters ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< DiscreteFilter >() );
+ case XLS_TOKEN( top10 ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< Top10Filter >() );
+ case XLS_TOKEN( customFilters ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< CustomFilter >() );
+ case XLS_TOKEN( colorFilter ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< ColorFilter >() );
+ }
+ return nullptr;
+}
+
+void FilterColumnContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrFilterColumn.importFilterColumn( rAttribs );
+}
+
+ContextHandlerRef FilterColumnContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ if( getCurrentElement() == BIFF12_ID_FILTERCOLUMN ) switch( nRecId )
+ {
+ case BIFF12_ID_DISCRETEFILTERS:
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< DiscreteFilter >() );
+ case BIFF12_ID_TOP10FILTER:
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< Top10Filter >() );
+ case BIFF12_ID_CUSTOMFILTERS:
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< CustomFilter >() );
+ }
+ return nullptr;
+}
+
+void FilterColumnContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrFilterColumn.importFilterColumn( rStrm );
+}
+
+// class SortConditionContext
+
+SortConditionContext::SortConditionContext( WorksheetContextBase& rParent, SortCondition& rSortCondition ) :
+ WorksheetContextBase( rParent ),
+ mrSortCondition( rSortCondition )
+{
+}
+
+ContextHandlerRef SortConditionContext::onCreateContext( sal_Int32 , const AttributeList& )
+{
+ return nullptr;
+}
+
+void SortConditionContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrSortCondition.importSortCondition( rAttribs, getSheetIndex() );
+}
+
+ContextHandlerRef SortConditionContext::onCreateRecordContext( sal_Int32 , SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void SortConditionContext::onStartRecord( SequenceInputStream& )
+{
+}
+
+// class SortStateContext
+
+SortStateContext::SortStateContext( WorksheetContextBase& rParent, AutoFilter& rAutoFilter ) :
+ WorksheetContextBase( rParent ),
+ mrAutoFilter( rAutoFilter )
+{
+}
+
+ContextHandlerRef SortStateContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( getCurrentElement() == XLS_TOKEN( sortState ) ) switch( nElement )
+ {
+ case XLS_TOKEN( sortCondition ):
+ return new SortConditionContext( *this, mrAutoFilter.createSortCondition() );
+ }
+ return nullptr;
+}
+
+void SortStateContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrAutoFilter.importSortState( rAttribs, getSheetIndex() );
+}
+
+ContextHandlerRef SortStateContext::onCreateRecordContext( sal_Int32 , SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void SortStateContext::onStartRecord( SequenceInputStream& )
+{
+}
+
+AutoFilterContext::AutoFilterContext( WorksheetFragmentBase& rFragment, AutoFilter& rAutoFilter ) :
+ WorksheetContextBase( rFragment ),
+ mrAutoFilter( rAutoFilter )
+{
+}
+
+ContextHandlerRef AutoFilterContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( getCurrentElement() == XLS_TOKEN( autoFilter ) ) switch( nElement )
+ {
+ case XLS_TOKEN( sortState ):
+ return new SortStateContext( *this, mrAutoFilter );
+ case XLS_TOKEN( filterColumn ):
+ return new FilterColumnContext( *this, mrAutoFilter.createFilterColumn() );
+ }
+ return nullptr;
+}
+
+void AutoFilterContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrAutoFilter.importAutoFilter( rAttribs, getSheetIndex() );
+}
+
+ContextHandlerRef AutoFilterContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ if( (getCurrentElement() == BIFF12_ID_AUTOFILTER) && (nRecId == BIFF12_ID_FILTERCOLUMN) )
+ return new FilterColumnContext( *this, mrAutoFilter.createFilterColumn() );
+ return nullptr;
+}
+
+void AutoFilterContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrAutoFilter.importAutoFilter( rStrm, getSheetIndex() );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/biffhelper.cxx b/sc/source/filter/oox/biffhelper.cxx
new file mode 100644
index 0000000000..20a088fc73
--- /dev/null
+++ b/sc/source/filter/oox/biffhelper.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 <biffhelper.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/helper/binaryinputstream.hxx>
+
+#include <limits>
+
+namespace oox::xls {
+
+namespace {
+
+const sal_Int32 BIFF_RK_100FLAG = 0x00000001;
+const sal_Int32 BIFF_RK_INTFLAG = 0x00000002;
+const sal_Int32 BIFF_RK_VALUEMASK = 0xFFFFFFFC;
+
+} // namespace
+
+// conversion -----------------------------------------------------------------
+
+/*static*/ double BiffHelper::calcDoubleFromRk( sal_Int32 nRkValue )
+{
+ sal_math_Double aMathDouble{};
+ if( getFlag( nRkValue, BIFF_RK_INTFLAG ) )
+ {
+ sal_Int32 nTemp = nRkValue >> 2;
+ setFlag< sal_Int32 >( nTemp, 0xE0000000, nRkValue < 0 );
+ aMathDouble.value = nTemp;
+ }
+ else
+ {
+ aMathDouble.w32_parts.msw = static_cast< sal_uInt32 >( nRkValue & BIFF_RK_VALUEMASK );
+ }
+ if( getFlag( nRkValue, BIFF_RK_100FLAG ) )
+ aMathDouble.value /= 100.0;
+
+ return aMathDouble.value;
+}
+/*static*/ double BiffHelper::calcDoubleFromError( sal_uInt8 nErrorCode )
+{
+ sal_uInt16 nApiError = 0x7FFF;
+ switch( nErrorCode )
+ {
+ case BIFF_ERR_NULL: nApiError = 521; break;
+ case BIFF_ERR_DIV0: nApiError = 532; break;
+ case BIFF_ERR_VALUE: nApiError = 519; break;
+ case BIFF_ERR_REF: nApiError = 524; break;
+ case BIFF_ERR_NAME: nApiError = 525; break;
+ case BIFF_ERR_NUM: nApiError = 503; break;
+ case BIFF_ERR_NA: nApiError = 0x7FFF; break;
+ default: OSL_FAIL( "BiffHelper::calcDoubleFromError - unknown error code" );
+ }
+ sal_math_Double aMathDouble;
+ aMathDouble.value = std::numeric_limits<double>::quiet_NaN();
+ aMathDouble.nan_parts.fraction_lo = nApiError;
+ return aMathDouble.value;
+}
+
+// BIFF12 import --------------------------------------------------------------
+
+/*static*/ OUString BiffHelper::readString( SequenceInputStream& rStrm, bool b32BitLen )
+{
+ OUString aString;
+ if( !rStrm.isEof() )
+ {
+ sal_Int32 nCharCount = b32BitLen ? rStrm.readValue< sal_Int32 >() : rStrm.readValue< sal_Int16 >();
+ // string length -1 is often used to indicate a missing string
+ OSL_ENSURE( !rStrm.isEof() && (nCharCount >= -1), "BiffHelper::readString - invalid string length" );
+ if( !rStrm.isEof() && (nCharCount > 0) )
+ {
+ // SequenceInputStream always supports getRemaining()
+ nCharCount = ::std::min( nCharCount, static_cast< sal_Int32 >( rStrm.getRemaining() / 2 ) );
+ aString = rStrm.readUnicodeArray( nCharCount );
+ }
+ }
+ return aString;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/chartsheetfragment.cxx b/sc/source/filter/oox/chartsheetfragment.cxx
new file mode 100644
index 0000000000..10fde32051
--- /dev/null
+++ b/sc/source/filter/oox/chartsheetfragment.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 <chartsheetfragment.hxx>
+
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <pagesettings.hxx>
+#include <viewsettings.hxx>
+#include <worksheetsettings.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+ChartsheetFragment::ChartsheetFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef ChartsheetFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( chartsheet ) ) return this;
+ break;
+
+ case XLS_TOKEN( chartsheet ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetViews ): return this;
+
+ case XLS_TOKEN( sheetPr ): getWorksheetSettings().importChartSheetPr( rAttribs ); return this;
+ case XLS_TOKEN( sheetProtection ): getWorksheetSettings().importChartProtection( rAttribs ); break;
+ case XLS_TOKEN( pageMargins ): getPageSettings().importPageMargins( rAttribs ); break;
+ case XLS_TOKEN( pageSetup ): getPageSettings().importChartPageSetup( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( headerFooter ): getPageSettings().importHeaderFooter( rAttribs ); return this;
+ case XLS_TOKEN( picture ): getPageSettings().importPicture( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( drawing ): importDrawing( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( sheetViews ):
+ if( nElement == XLS_TOKEN( sheetView ) ) getSheetViewSettings().importChartSheetView( rAttribs );
+ break;
+
+ case XLS_TOKEN( headerFooter ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ): return this; // collect contents in onCharacters()
+ }
+ break;
+
+ case XLS_TOKEN( sheetPr ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( tabColor ): getWorksheetSettings().importTabColor( rAttribs ); break;
+ }
+ break;
+
+ }
+ return nullptr;
+}
+
+void ChartsheetFragment::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ):
+ getPageSettings().importHeaderFooterCharacters( rChars, getCurrentElement() );
+ break;
+ }
+}
+
+ContextHandlerRef ChartsheetFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_WORKSHEET ) return this;
+ break;
+
+ case BIFF12_ID_WORKSHEET:
+ switch( nRecId )
+ {
+ case BIFF12_ID_CHARTSHEETVIEWS: return this;
+
+ case BIFF12_ID_CHARTSHEETPR: getWorksheetSettings().importChartSheetPr( rStrm ); break;
+ case BIFF12_ID_CHARTPROTECTION: getWorksheetSettings().importChartProtection( rStrm ); break;
+ case BIFF12_ID_PAGEMARGINS: getPageSettings().importPageMargins( rStrm ); break;
+ case BIFF12_ID_CHARTPAGESETUP: getPageSettings().importChartPageSetup( getRelations(), rStrm ); break;
+ case BIFF12_ID_HEADERFOOTER: getPageSettings().importHeaderFooter( rStrm ); break;
+ case BIFF12_ID_PICTURE: getPageSettings().importPicture( getRelations(), rStrm ); break;
+ case BIFF12_ID_DRAWING: importDrawing( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_CHARTSHEETVIEWS:
+ if( nRecId == BIFF12_ID_CHARTSHEETVIEW ) getSheetViewSettings().importChartSheetView( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* ChartsheetFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_CHARTSHEETVIEW, BIFF12_ID_CHARTSHEETVIEW + 1 },
+ { BIFF12_ID_CHARTSHEETVIEWS, BIFF12_ID_CHARTSHEETVIEWS + 1 },
+ { BIFF12_ID_CUSTOMCHARTVIEW, BIFF12_ID_CUSTOMCHARTVIEW + 1 },
+ { BIFF12_ID_CUSTOMCHARTVIEWS, BIFF12_ID_CUSTOMCHARTVIEWS + 1 },
+ { BIFF12_ID_HEADERFOOTER, BIFF12_ID_HEADERFOOTER + 1 },
+ { BIFF12_ID_WORKSHEET, BIFF12_ID_WORKSHEET + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void ChartsheetFragment::initializeImport()
+{
+ // initial processing in base class WorksheetHelper
+ initializeWorksheetImport();
+}
+
+void ChartsheetFragment::finalizeImport()
+{
+ // final processing in base class WorksheetHelper
+ finalizeWorksheetImport();
+}
+
+// private --------------------------------------------------------------------
+
+void ChartsheetFragment::importDrawing( const AttributeList& rAttribs )
+{
+ setDrawingPath( getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ) );
+}
+
+void ChartsheetFragment::importDrawing( SequenceInputStream& rStrm )
+{
+ setDrawingPath( getFragmentPathFromRelId( BiffHelper::readString( rStrm ) ) );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/commentsbuffer.cxx b/sc/source/filter/oox/commentsbuffer.cxx
new file mode 100644
index 0000000000..1f1f2dd254
--- /dev/null
+++ b/sc/source/filter/oox/commentsbuffer.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 <oox/token/tokens.hxx>
+
+#include <commentsbuffer.hxx>
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <osl/diagnose.h>
+#include <oox/drawingml/shapepropertymap.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/vml/vmlshape.hxx>
+#include <addressconverter.hxx>
+#include <drawingfragment.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/unoshape.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <cellsuno.hxx>
+#include <docfunc.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <postit.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+static sal_Int32 lcl_ToHorizAlign( sal_Int32 nAlign )
+{
+ switch( nAlign )
+ {
+ case XML_left:
+ return SDRTEXTHORZADJUST_LEFT;
+ case XML_right:
+ return SDRTEXTHORZADJUST_RIGHT;
+ case XML_center:
+ return SDRTEXTHORZADJUST_CENTER;
+ default:
+ return SDRTEXTHORZADJUST_BLOCK;
+ }
+}
+
+static sal_Int32 lcl_ToVertAlign( sal_Int32 nAlign )
+{
+ switch( nAlign )
+ {
+ case XML_top:
+ case XML_Top:
+ return SDRTEXTVERTADJUST_TOP;
+ case XML_center:
+ case XML_Center:
+ return SDRTEXTVERTADJUST_CENTER;
+ case XML_bottom:
+ case XML_Bottom:
+ return SDRTEXTVERTADJUST_BOTTOM;
+ default:
+ return SDRTEXTVERTADJUST_BLOCK;
+ }
+}
+
+static sal_Int16 lcl_ToParaAlign(sal_Int32 nAlign)
+{
+ switch ( nAlign )
+ {
+ case XML_Left:
+ return sal_Int16(css::style::ParagraphAdjust_LEFT);
+ case XML_Right:
+ return sal_Int16(css::style::ParagraphAdjust_RIGHT);
+ case XML_Center:
+ return sal_Int16(css::style::ParagraphAdjust_CENTER);
+ default:
+ return sal_Int16(css::style::ParagraphAdjust_BLOCK);
+ }
+}
+
+CommentModel::CommentModel()
+ : mnAuthorId(-1)
+ , mbAutoFill(false)
+ , mbAutoScale(false)
+ , mbColHidden(false)
+ , mbLocked(false)
+ , mbRowHidden(false)
+ , mnTHA(0)
+ , mnTVA(0)
+{
+}
+
+Comment::Comment( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void Comment::importComment( const AttributeList& rAttribs )
+{
+ maModel.mnAuthorId = rAttribs.getInteger( XML_authorId, -1 );
+ // cell range will be checked while inserting the comment into the document
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() );
+}
+
+void Comment::importCommentPr( const AttributeList& rAttribs )
+{
+ maModel.mbAutoFill = rAttribs.getBool( XML_autoFill, true );
+ maModel.mbAutoScale = rAttribs.getBool( XML_autoScale, false );
+ maModel.mbColHidden = rAttribs.getBool( XML_colHidden, false );
+ maModel.mbLocked = rAttribs.getBool( XML_locked, false );
+ maModel.mbRowHidden = rAttribs.getBool( XML_rowHidden, false );
+ maModel.mnTHA = rAttribs.getToken( XML_textHAlign, XML_left );
+ maModel.mnTVA = rAttribs.getToken( XML_textVAlign, XML_top );
+}
+
+void Comment::importComment( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ maModel.mnAuthorId = rStrm.readInt32();
+ rStrm >> aBinRange;
+ // cell range will be checked while inserting the comment into the document
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, aBinRange, getSheetIndex() );
+}
+
+RichStringRef const & Comment::createText()
+{
+ maModel.mxText = std::make_shared<RichString>();
+ return maModel.mxText;
+}
+
+namespace
+{
+ struct OOXGenerateNoteCaption : public GenerateNoteCaption
+ {
+ css::uno::Sequence<OUString> maPropertyNames; /// import filter Caption object formatting property names
+ css::uno::Sequence<css::uno::Any> maPropertyValues; /// import filter Caption object formatting property values
+ std::shared_ptr<RichString> mxText;
+
+ OOXGenerateNoteCaption(std::shared_ptr<RichString>& rText)
+ : mxText(rText)
+ {
+ }
+
+ virtual void Generate(SdrCaptionObj& rCaptionObj) override
+ {
+ rtl::Reference<SvxShapeText> xAnnoShape(dynamic_cast<SvxShapeText*>(rCaptionObj.getUnoShape().get())); // SvxShapeText
+ assert(xAnnoShape && "will not be null");
+
+ if (maPropertyNames.getLength())
+ {
+ // setting a property triggers expensive process, so set them all at once
+ static_cast<SvxShape*>(xAnnoShape.get())->setPropertyValues(maPropertyNames, maPropertyValues);
+ }
+
+ // insert text and convert text formatting
+ Reference< XText > xAnnoText( xAnnoShape );
+ xAnnoShape->addActionLock();
+ mxText->convert( xAnnoText );
+ xAnnoShape->removeActionLock();
+ }
+
+ virtual OUString GetSimpleText() const override
+ {
+ return mxText->getStringContent();
+ }
+ };
+}
+
+void Comment::finalizeImport()
+{
+ // BIFF12 stores cell range instead of cell address, use first cell of this range
+ OSL_ENSURE( maModel.maRange.aStart == maModel.maRange.aEnd,
+ "Comment::finalizeImport - comment anchor should be a single cell" );
+ if( !getAddressConverter().checkCellAddress( maModel.maRange.aStart, true ) || !maModel.mxText )
+ return;
+
+ try
+ {
+ ScTableSheetObj* pAnnosSupp = static_cast<ScTableSheetObj*>(getSheet().get());
+ rtl::Reference<ScAnnotationsObj> xAnnos = static_cast<ScAnnotationsObj*>(pAnnosSupp->getAnnotations().get());
+ ScDocShell* pDocShell = xAnnos->GetDocShell();
+
+ auto xGenerator = std::make_unique<OOXGenerateNoteCaption>(maModel.mxText);
+
+ // Add shape formatting properties (autoFill, colHidden and rowHidden are dropped)
+ // vvv TODO vvv TextFitToSize should be a drawing::TextFitToSizeType not bool
+ xGenerator->maPropertyNames =
+ css::uno::Sequence<OUString>{ "TextFitToSize", "MoveProtect", "TextHorizontalAdjust", "TextVerticalAdjust" };
+ xGenerator->maPropertyValues =
+ css::uno::Sequence<css::uno::Any>{ Any(maModel.mbAutoScale), Any(maModel.mbLocked),
+ Any(lcl_ToHorizAlign( maModel.mnTHA )), Any(lcl_ToVertAlign( maModel.mnTVA )) };
+
+ tools::Rectangle aCaptionRect;
+ if( maModel.maAnchor.Width > 0 && maModel.maAnchor.Height > 0 )
+ {
+ aCaptionRect = tools::Rectangle(Point(maModel.maAnchor.X, maModel.maAnchor.Y),
+ Size(maModel.maAnchor.Width, maModel.maAnchor.Height));
+ }
+
+ // convert shape formatting and visibility
+ bool bVisible = true;
+ if( const ::oox::vml::ShapeBase* pVmlNoteShape = getVmlDrawing().getNoteShape( maModel.maRange.aStart ) )
+ {
+ // position and formatting
+ css::awt::Rectangle aShapeRect = pVmlNoteShape->getShapeRectangle();
+ if (aShapeRect.Width > 0 || aShapeRect.Height > 0)
+ {
+ aCaptionRect = tools::Rectangle(Point(aShapeRect.X, aShapeRect.Y),
+ Size(aShapeRect.Width, aShapeRect.Height));
+
+ ::oox::drawingml::ShapePropertyMap aPropMap(pVmlNoteShape->makeShapePropertyMap());
+
+ Sequence<OUString> aVMLPropNames;
+ Sequence<Any> aVMLPropValues;
+ aPropMap.fillSequences(aVMLPropNames, aVMLPropValues);
+
+ sal_uInt32 nOldPropLen = xGenerator->maPropertyNames.getLength();
+ sal_uInt32 nVMLPropLen = aVMLPropNames.getLength();
+ xGenerator->maPropertyNames.realloc(nOldPropLen + nVMLPropLen);
+ xGenerator->maPropertyValues.realloc(nOldPropLen + nVMLPropLen);
+ OUString* pNames = xGenerator->maPropertyNames.getArray();
+ Any* pValues = xGenerator->maPropertyValues.getArray();
+ for (sal_uInt32 i = 0; i < nVMLPropLen; ++i)
+ {
+ pNames[nOldPropLen + i] = aVMLPropNames[i];
+ pValues[nOldPropLen + i] = aVMLPropValues[i];
+ }
+ }
+
+ // visibility
+ bVisible = pVmlNoteShape->getTypeModel().mbVisible;
+
+ // Setting comment text alignment
+ const ::oox::vml::ClientData* xClientData = pVmlNoteShape->getClientData();
+ sal_uInt32 nOldPropLen = xGenerator->maPropertyNames.getLength();
+ xGenerator->maPropertyNames.realloc(nOldPropLen + 2);
+ xGenerator->maPropertyValues.realloc(nOldPropLen + 2);
+ OUString* pNames = xGenerator->maPropertyNames.getArray();
+ Any* pValues = xGenerator->maPropertyValues.getArray();
+ pNames[nOldPropLen] = "TextVerticalAdjust";
+ pValues[nOldPropLen] <<= lcl_ToVertAlign(xClientData->mnTextVAlign);
+ pNames[nOldPropLen + 1] = "ParaAdjust";
+ pValues[nOldPropLen + 1] <<= lcl_ToParaAlign( xClientData->mnTextHAlign);
+ }
+
+ xGenerator->mxText->finalizeImport(*this);
+
+ pDocShell->GetDocFunc().ImportNote(maModel.maRange.aStart, std::move(xGenerator),
+ aCaptionRect, bVisible);
+
+ if (bVisible)
+ pDocShell->GetDocFunc().ShowNote( maModel.maRange.aStart, bVisible );
+ }
+ catch( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sc");
+ }
+}
+
+// private --------------------------------------------------------------------
+
+CommentsBuffer::CommentsBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void CommentsBuffer::appendAuthor( const OUString& rAuthor )
+{
+ maAuthors.push_back( rAuthor );
+}
+
+CommentRef CommentsBuffer::createComment()
+{
+ CommentRef xComment = std::make_shared<Comment>( *this );
+ maComments.push_back( xComment );
+ return xComment;
+}
+
+void CommentsBuffer::finalizeImport()
+{
+ // keep the model locked to avoid repeated reformatting in the model
+ auto pModel = getScDocument().GetDrawLayer();
+ bool bWasLocked = pModel->isLocked();
+ pModel->setLock(true);
+ maComments.forEachMem( &Comment::finalizeImport );
+ pModel->setLock(bWasLocked);
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/commentsfragment.cxx b/sc/source/filter/oox/commentsfragment.cxx
new file mode 100644
index 0000000000..a5d1da8dac
--- /dev/null
+++ b/sc/source/filter/oox/commentsfragment.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 <commentsfragment.hxx>
+
+#include <biffhelper.hxx>
+#include <richstringcontext.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+CommentsFragment::CommentsFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef CommentsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( comments ) ) return this;
+ break;
+ case XLS_TOKEN( comments ):
+ if( nElement == XLS_TOKEN( authors ) ) return this;
+ if( nElement == XLS_TOKEN( commentList ) ) return this;
+ break;
+ case XLS_TOKEN( authors ):
+ if( nElement == XLS_TOKEN( author ) ) return this; // collect author in onCharacters()
+ break;
+ case XLS_TOKEN( commentList ):
+ if( nElement == XLS_TOKEN( comment ) ) { importComment( rAttribs ); return this; }
+ break;
+ case XLS_TOKEN( commentPr ):
+ if( nElement == XLS_TOKEN( anchor ) )
+ return this;
+ break;
+ case XLS_TOKEN( anchor ):
+ if( nElement == XDR_TOKEN( from ) || nElement == XDR_TOKEN( to ) )
+ return this;
+ break;
+ case XDR_TOKEN( from ):
+ case XDR_TOKEN( to ):
+ return this;
+ case XLS_TOKEN( comment ):
+ if( (nElement == XLS_TOKEN( text )) && mxComment )
+ return new RichStringContext( *this, mxComment->createText() );
+ if( nElement == XLS_TOKEN( commentPr ) ) { mxComment->importCommentPr( rAttribs ); return this; }
+ break;
+ }
+ return nullptr;
+}
+
+void CommentsFragment::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( author ) ) )
+ getComments().appendAuthor( rChars );
+}
+
+void CommentsFragment::onEndElement()
+{
+ if( isCurrentElement( XLS_TOKEN( comment ) ) )
+ mxComment.reset();
+}
+
+ContextHandlerRef CommentsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_COMMENTS ) return this;
+ break;
+ case BIFF12_ID_COMMENTS:
+ if( nRecId == BIFF12_ID_COMMENTAUTHORS ) return this;
+ if( nRecId == BIFF12_ID_COMMENTLIST ) return this;
+ break;
+ case BIFF12_ID_COMMENTAUTHORS:
+ if( nRecId == BIFF12_ID_COMMENTAUTHOR ) getComments().appendAuthor( BiffHelper::readString( rStrm ) );
+ break;
+ case BIFF12_ID_COMMENTLIST:
+ if( nRecId == BIFF12_ID_COMMENT ) { importComment( rStrm ); return this; }
+ break;
+ case BIFF12_ID_COMMENT:
+ if( (nRecId == BIFF12_ID_COMMENTTEXT) && mxComment )
+ mxComment->createText()->importString( rStrm, true, *this );
+ break;
+ }
+ return nullptr;
+}
+
+void CommentsFragment::onEndRecord()
+{
+ if( isCurrentElement( BIFF12_ID_COMMENT ) )
+ mxComment.reset();
+}
+
+const RecordInfo* CommentsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_COMMENT, BIFF12_ID_COMMENT + 1 },
+ { BIFF12_ID_COMMENTAUTHORS, BIFF12_ID_COMMENTAUTHORS + 1 },
+ { BIFF12_ID_COMMENTLIST, BIFF12_ID_COMMENTLIST + 1 },
+ { BIFF12_ID_COMMENTS, BIFF12_ID_COMMENTS + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+// private --------------------------------------------------------------------
+
+void CommentsFragment::importComment( const AttributeList& rAttribs )
+{
+ mxComment = getComments().createComment();
+ mxComment->importComment( rAttribs );
+}
+
+void CommentsFragment::importComment( SequenceInputStream& rStrm )
+{
+ mxComment = getComments().createComment();
+ mxComment->importComment( rStrm );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/condformatbuffer.cxx b/sc/source/filter/oox/condformatbuffer.cxx
new file mode 100644
index 0000000000..649c2417f3
--- /dev/null
+++ b/sc/source/filter/oox/condformatbuffer.cxx
@@ -0,0 +1,1582 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <unordered_set>
+#include <unordered_map>
+#include <condformatbuffer.hxx>
+#include <formulaparser.hxx>
+
+#include <com/sun/star/sheet/ConditionOperator2.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <stylesbuffer.hxx>
+#include <themebuffer.hxx>
+
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <document.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <extlstcontext.hxx>
+#include <utility>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_Int32 BIFF12_CFRULE_TYPE_CELLIS = 1;
+const sal_Int32 BIFF12_CFRULE_TYPE_EXPRESSION = 2;
+const sal_Int32 BIFF12_CFRULE_TYPE_COLORSCALE = 3;
+const sal_Int32 BIFF12_CFRULE_TYPE_DATABAR = 4;
+const sal_Int32 BIFF12_CFRULE_TYPE_TOPTEN = 5;
+const sal_Int32 BIFF12_CFRULE_TYPE_ICONSET = 6;
+
+const sal_Int32 BIFF12_CFRULE_SUB_CELLIS = 0;
+const sal_Int32 BIFF12_CFRULE_SUB_EXPRESSION = 1;
+const sal_Int32 BIFF12_CFRULE_SUB_COLORSCALE = 2;
+const sal_Int32 BIFF12_CFRULE_SUB_DATABAR = 3;
+const sal_Int32 BIFF12_CFRULE_SUB_ICONSET = 4;
+const sal_Int32 BIFF12_CFRULE_SUB_TOPTEN = 5;
+const sal_Int32 BIFF12_CFRULE_SUB_UNIQUE = 7;
+const sal_Int32 BIFF12_CFRULE_SUB_TEXT = 8;
+const sal_Int32 BIFF12_CFRULE_SUB_BLANK = 9;
+const sal_Int32 BIFF12_CFRULE_SUB_NOTBLANK = 10;
+const sal_Int32 BIFF12_CFRULE_SUB_ERROR = 11;
+const sal_Int32 BIFF12_CFRULE_SUB_NOTERROR = 12;
+const sal_Int32 BIFF12_CFRULE_SUB_TODAY = 15;
+const sal_Int32 BIFF12_CFRULE_SUB_TOMORROW = 16;
+const sal_Int32 BIFF12_CFRULE_SUB_YESTERDAY = 17;
+const sal_Int32 BIFF12_CFRULE_SUB_LAST7DAYS = 18;
+const sal_Int32 BIFF12_CFRULE_SUB_LASTMONTH = 19;
+const sal_Int32 BIFF12_CFRULE_SUB_NEXTMONTH = 20;
+const sal_Int32 BIFF12_CFRULE_SUB_THISWEEK = 21;
+const sal_Int32 BIFF12_CFRULE_SUB_NEXTWEEK = 22;
+const sal_Int32 BIFF12_CFRULE_SUB_LASTWEEK = 23;
+const sal_Int32 BIFF12_CFRULE_SUB_THISMONTH = 24;
+const sal_Int32 BIFF12_CFRULE_SUB_ABOVEAVERAGE = 25;
+const sal_Int32 BIFF12_CFRULE_SUB_BELOWAVERAGE = 26;
+const sal_Int32 BIFF12_CFRULE_SUB_DUPLICATE = 27;
+const sal_Int32 BIFF12_CFRULE_SUB_EQABOVEAVERAGE = 29;
+const sal_Int32 BIFF12_CFRULE_SUB_EQBELOWAVERAGE = 30;
+
+const sal_Int32 BIFF12_CFRULE_TIMEOP_TODAY = 0;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_YESTERDAY = 1;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_LAST7DAYS = 2;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_THISWEEK = 3;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_LASTWEEK = 4;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_LASTMONTH = 5;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_TOMORROW = 6;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_NEXTWEEK = 7;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_NEXTMONTH = 8;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_THISMONTH = 9;
+
+const sal_uInt16 BIFF12_CFRULE_STOPIFTRUE = 0x0002;
+const sal_uInt16 BIFF12_CFRULE_ABOVEAVERAGE = 0x0004;
+const sal_uInt16 BIFF12_CFRULE_BOTTOM = 0x0008;
+const sal_uInt16 BIFF12_CFRULE_PERCENT = 0x0010;
+
+bool isValue(std::u16string_view rStr, double& rVal)
+{
+ sal_Int32 nEnd = -1;
+ rVal = rtl::math::stringToDouble(o3tl::trim(rStr), '.', ',', nullptr, &nEnd);
+
+ return nEnd >= static_cast<sal_Int32>(rStr.size());
+}
+
+void SetCfvoData( ColorScaleRuleModelEntry* pEntry, const AttributeList& rAttribs )
+{
+ OUString aType = rAttribs.getString( XML_type, OUString() );
+ OUString aVal = rAttribs.getString(XML_val, OUString());
+
+ if (aVal != "\"\"")
+ {
+ double nVal = 0.0;
+ bool bVal = isValue(aVal, nVal);
+ if( !bVal || aType == "formula" )
+ {
+ pEntry->maFormula = aVal;
+ }
+ else
+ {
+ pEntry->mnVal = nVal;
+ }
+ }
+
+ if (aType == "num")
+ {
+ pEntry->mbNum = true;
+ }
+ else if( aType == "min" )
+ {
+ pEntry->mbMin = true;
+ }
+ else if( aType == "max" )
+ {
+ pEntry->mbMax = true;
+ }
+ else if( aType == "percent" )
+ {
+ pEntry->mbPercent = true;
+ }
+ else if( aType == "percentile" )
+ {
+ pEntry->mbPercentile = true;
+ }
+}
+
+}
+
+ColorScaleRule::ColorScaleRule( const CondFormat& rFormat ):
+ WorksheetHelper( rFormat ),
+ mnCfvo(0),
+ mnCol(0)
+{
+}
+
+void ColorScaleRule::importCfvo( const AttributeList& rAttribs )
+{
+ if(mnCfvo >= maColorScaleRuleEntries.size())
+ maColorScaleRuleEntries.emplace_back();
+
+ SetCfvoData( &maColorScaleRuleEntries[mnCfvo], rAttribs );
+
+ ++mnCfvo;
+}
+
+// https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.indexedcolors?view=openxml-2.8.1
+static ::Color IndexedColors[] = {
+ 0x00000000,
+ 0x00FFFFFF,
+ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF,
+ 0x00FFFF00,
+ 0x00FF00FF,
+ 0x0000FFFF,
+ 0x00000000,
+ 0x00FFFFFF,
+ 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF,
+ 0x00FFFF00,
+ 0x00FF00FF,
+ 0x0000FFFF,
+ 0x00800000,
+ 0x00008000,
+ 0x00000080,
+ 0x00808000,
+ 0x00800080,
+ 0x00008080,
+ 0x00C0C0C0,
+ 0x00808080,
+ 0x009999FF,
+ 0x00993366,
+ 0x00FFFFCC,
+ 0x00CCFFFF,
+ 0x00660066,
+ 0x00FF8080,
+ 0x000066CC,
+ 0x00CCCCFF,
+ 0x00000080,
+ 0x00FF00FF,
+ 0x00FFFF00,
+ 0x0000FFFF,
+ 0x00800080,
+ 0x00800000,
+ 0x00008080,
+ 0x000000FF,
+ 0x0000CCFF,
+ 0x00CCFFFF,
+ 0x00CCFFCC,
+ 0x00FFFF99,
+ 0x0099CCFF,
+ 0x00FF99CC,
+ 0x00CC99FF,
+ 0x00FFCC99,
+ 0x003366FF,
+ 0x0033CCCC,
+ 0x0099CC00,
+ 0x00FFCC00,
+ 0x00FF9900,
+ 0x00FF6600,
+ 0x00666699,
+ 0x00969696,
+ 0x00003366,
+ 0x00339966,
+ 0x00003300,
+ 0x00333300,
+ 0x00993300,
+ 0x00993366,
+ 0x00333399,
+ 0x00333333,
+ 0x00000000, // System Foreground ?
+ 0x00000000, // System Background ?
+};
+
+namespace {
+
+::Color importOOXColor(const AttributeList& rAttribs, const ThemeBuffer& rThemeBuffer, const GraphicHelper& rGraphicHelper)
+{
+ ::Color nColor;
+ if( rAttribs.hasAttribute( XML_rgb ) )
+ nColor = ::Color(ColorTransparency, rAttribs.getUnsignedHex( XML_rgb, UNSIGNED_RGB_TRANSPARENT ));
+ else if( rAttribs.hasAttribute( XML_theme ) )
+ {
+ sal_uInt32 nThemeIndex = rAttribs.getUnsigned( XML_theme, 0 );
+
+ // Excel has a bug in the mapping of index 0, 1, 2 and 3.
+ if (nThemeIndex == 0)
+ nThemeIndex = 1;
+ else if (nThemeIndex == 1)
+ nThemeIndex = 0;
+ else if (nThemeIndex == 2)
+ nThemeIndex = 3;
+ else if (nThemeIndex == 3)
+ nThemeIndex = 2;
+
+ nColor = rThemeBuffer.getColorByIndex( nThemeIndex );
+ }
+ else if (rAttribs.hasAttribute(XML_indexed))
+ {
+ sal_uInt32 nIndexed = rAttribs.getUnsigned(XML_indexed, 0);
+ if (nIndexed < std::size(IndexedColors))
+ nColor = IndexedColors[nIndexed];
+ }
+
+ ::Color aColor;
+ double nTint = rAttribs.getDouble(XML_tint, 0.0);
+ if (nTint != 0.0)
+ {
+ oox::drawingml::Color aDMColor;
+ aDMColor.setSrgbClr(nColor);
+ aDMColor.addExcelTintTransformation(nTint);
+ aColor = aDMColor.getColor(rGraphicHelper);
+ }
+ else
+ aColor = nColor.GetRGBColor();
+
+ return aColor;
+}
+
+}
+
+void ColorScaleRule::importColor( const AttributeList& rAttribs )
+{
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+
+ if(mnCol >= maColorScaleRuleEntries.size())
+ maColorScaleRuleEntries.emplace_back();
+
+ maColorScaleRuleEntries[mnCol].maColor = aColor;
+ ++mnCol;
+}
+
+namespace {
+
+ScColorScaleEntry* ConvertToModel( const ColorScaleRuleModelEntry& rEntry, ScDocument* pDoc, const ScAddress& rAddr )
+{
+ ScColorScaleEntry* pEntry = new ScColorScaleEntry(rEntry.mnVal, rEntry.maColor);
+
+ if(rEntry.mbMin)
+ pEntry->SetType(COLORSCALE_MIN);
+ if(rEntry.mbMax)
+ pEntry->SetType(COLORSCALE_MAX);
+ if(rEntry.mbPercent)
+ pEntry->SetType(COLORSCALE_PERCENT);
+ if(rEntry.mbPercentile)
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ if (rEntry.mbNum)
+ pEntry->SetType(COLORSCALE_VALUE);
+
+ if(!rEntry.maFormula.isEmpty())
+ {
+ pEntry->SetType(COLORSCALE_FORMULA);
+ pEntry->SetFormula(rEntry.maFormula, *pDoc, rAddr, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+
+ return pEntry;
+}
+
+}
+
+void ColorScaleRule::AddEntries( ScColorScaleFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr )
+{
+ for(const ColorScaleRuleModelEntry & rEntry : maColorScaleRuleEntries)
+ {
+ ScColorScaleEntry* pEntry = ConvertToModel( rEntry, pDoc, rAddr );
+
+ pFormat->AddEntry( pEntry );
+ }
+}
+
+DataBarRule::DataBarRule( const CondFormat& rFormat ):
+ WorksheetHelper( rFormat ),
+ mxFormat(new ScDataBarFormatData)
+{
+ mxFormat->meAxisPosition = databar::NONE;
+}
+
+void DataBarRule::importColor( const AttributeList& rAttribs )
+{
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+
+ mxFormat->maPositiveColor = aColor;
+}
+
+void DataBarRule::importCfvo( const AttributeList& rAttribs )
+{
+ ColorScaleRuleModelEntry* pEntry;
+ if(!mpLowerLimit)
+ {
+ mpLowerLimit.reset(new ColorScaleRuleModelEntry);
+ pEntry = mpLowerLimit.get();
+ }
+ else
+ {
+ mpUpperLimit.reset(new ColorScaleRuleModelEntry);
+ pEntry = mpUpperLimit.get();
+ }
+
+ SetCfvoData( pEntry, rAttribs );
+}
+
+void DataBarRule::importAttribs( const AttributeList& rAttribs )
+{
+ mxFormat->mbOnlyBar = !rAttribs.getBool( XML_showValue, true );
+ mxFormat->mnMinLength = rAttribs.getUnsigned( XML_minLength, 10);
+ mxFormat->mnMaxLength = rAttribs.getUnsigned( XML_maxLength, 90);
+}
+
+void DataBarRule::SetData( ScDataBarFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr )
+{
+ ScColorScaleEntry* pUpperEntry = ConvertToModel(*mpUpperLimit, pDoc, rAddr);
+ ScColorScaleEntry* pLowerEntry = ConvertToModel(*mpLowerLimit, pDoc, rAddr);
+
+ mxFormat->mpUpperLimit.reset( pUpperEntry );
+ mxFormat->mpLowerLimit.reset( pLowerEntry );
+ pFormat->SetDataBarData(mxFormat.release());
+}
+
+IconSetRule::IconSetRule( const WorksheetHelper& rParent ):
+ WorksheetHelper( rParent ),
+ mxFormatData( new ScIconSetFormatData ),
+ mbCustom(false)
+{
+}
+
+void IconSetRule::importCfvo( const AttributeList& rAttribs )
+{
+ ColorScaleRuleModelEntry aNewEntry;
+ SetCfvoData(&aNewEntry, rAttribs);
+
+ maEntries.push_back(aNewEntry);
+}
+
+void IconSetRule::importAttribs( const AttributeList& rAttribs )
+{
+ maIconSetType = rAttribs.getString( XML_iconSet, "3TrafficLights1" );
+ mxFormatData->mbShowValue = rAttribs.getBool( XML_showValue, true );
+ mxFormatData->mbReverse = rAttribs.getBool( XML_reverse, false );
+ mbCustom = rAttribs.getBool(XML_custom, false);
+}
+
+void IconSetRule::importFormula(const OUString& rFormula)
+{
+ ColorScaleRuleModelEntry& rEntry = maEntries.back();
+ double nVal = 0.0;
+ if ((rEntry.mbNum || rEntry.mbPercent || rEntry.mbPercentile) && isValue(rFormula, nVal))
+ {
+ rEntry.mnVal = nVal;
+ }
+ else if (!rFormula.isEmpty())
+ rEntry.maFormula = rFormula;
+}
+
+namespace {
+
+ScIconSetType getType(std::u16string_view rName)
+{
+ ScIconSetType eIconSetType = IconSet_3TrafficLights1;
+ const ScIconSetMap* pIconSetMap = ScIconSetFormat::g_IconSetMap;
+ for(size_t i = 0; pIconSetMap[i].pName; ++i)
+ {
+ if(OUString::createFromAscii(pIconSetMap[i].pName) == rName)
+ {
+ eIconSetType = pIconSetMap[i].eType;
+ break;
+ }
+ }
+
+ return eIconSetType;
+}
+
+}
+
+void IconSetRule::importIcon(const AttributeList& rAttribs)
+{
+ OUString aIconSet = rAttribs.getString(XML_iconSet, OUString());
+ sal_Int32 nIndex = rAttribs.getInteger(XML_iconId, -1);
+ if (aIconSet == "NoIcons")
+ {
+ nIndex = -1;
+ }
+
+ ScIconSetType eIconSetType = getType(aIconSet);
+ mxFormatData->maCustomVector.emplace_back(eIconSetType, nIndex);
+}
+
+void IconSetRule::SetData( ScIconSetFormat* pFormat, ScDocument* pDoc, const ScAddress& rPos )
+{
+ for(const ColorScaleRuleModelEntry & rEntry : maEntries)
+ {
+ ScColorScaleEntry* pModelEntry = ConvertToModel( rEntry, pDoc, rPos );
+ mxFormatData->m_Entries.emplace_back(pModelEntry);
+ }
+
+ mxFormatData->eIconSetType = getType(maIconSetType);
+ mxFormatData->mbCustom = mbCustom;
+ pFormat->SetIconSetData(mxFormatData.release());
+}
+
+CondFormatRuleModel::CondFormatRuleModel() :
+ mnPriority( -1 ),
+ mnType( XML_TOKEN_INVALID ),
+ mnOperator( XML_TOKEN_INVALID ),
+ mnTimePeriod( XML_TOKEN_INVALID ),
+ mnRank( 0 ),
+ mnStdDev( 0 ),
+ mnDxfId( -1 ),
+ mbStopIfTrue( false ),
+ mbBottom( false ),
+ mbPercent( false ),
+ mbAboveAverage( true ),
+ mbEqualAverage( false )
+{
+}
+
+void CondFormatRuleModel::setBiffOperator( sal_Int32 nOperator )
+{
+ static const sal_Int32 spnOperators[] = {
+ XML_TOKEN_INVALID, XML_between, XML_notBetween, XML_equal, XML_notEqual,
+ XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+void CondFormatRuleModel::setBiff12TextType( sal_Int32 nOperator )
+{
+ // note: type XML_notContainsText vs. operator XML_notContains
+ static const sal_Int32 spnTypes[] = { XML_containsText, XML_notContainsText, XML_beginsWith, XML_endsWith };
+ mnType = STATIC_ARRAY_SELECT( spnTypes, nOperator, XML_TOKEN_INVALID );
+ static const sal_Int32 spnOperators[] = { XML_containsText, XML_notContains, XML_beginsWith, XML_endsWith };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+CondFormatRule::CondFormatRule( const CondFormat& rCondFormat, ScConditionalFormat* pFormat ) :
+ WorksheetHelper( rCondFormat ),
+ mrCondFormat( rCondFormat ),
+ mpFormat(pFormat),
+ mpFormatEntry(nullptr)
+{
+}
+
+void CondFormatRule::importCfRule( const AttributeList& rAttribs )
+{
+ maModel.maText = rAttribs.getString( XML_text, OUString() );
+ maModel.mnPriority = rAttribs.getInteger( XML_priority, -1 );
+ maModel.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maModel.mnOperator = rAttribs.getToken( XML_operator, XML_TOKEN_INVALID );
+ maModel.mnTimePeriod = rAttribs.getToken( XML_timePeriod, XML_TOKEN_INVALID );
+ maModel.mnRank = rAttribs.getInteger( XML_rank, 0 );
+ maModel.mnStdDev = rAttribs.getInteger( XML_stdDev, 0 );
+ maModel.mnDxfId = rAttribs.getInteger( XML_dxfId, -1 );
+ maModel.mbStopIfTrue = rAttribs.getBool( XML_stopIfTrue, false );
+ maModel.mbBottom = rAttribs.getBool( XML_bottom, false );
+ maModel.mbPercent = rAttribs.getBool( XML_percent, false );
+ maModel.mbAboveAverage = rAttribs.getBool( XML_aboveAverage, true );
+ maModel.mbEqualAverage = rAttribs.getBool( XML_equalAverage, false );
+
+ if(maModel.mnType == XML_colorScale)
+ {
+ //import the remaining values
+
+ }
+}
+
+void CondFormatRule::appendFormula( const OUString& rFormula )
+{
+ ScAddress aBaseAddr = mrCondFormat.getRanges().GetTopLeftCorner();
+ ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, rFormula );
+ maModel.maFormulas.push_back( aTokens );
+}
+
+void CondFormatRule::importCfRule( SequenceInputStream& rStrm )
+{
+ sal_Int32 nType, nSubType, nOperator, nFmla1Size, nFmla2Size, nFmla3Size;
+ sal_uInt16 nFlags;
+ nType = rStrm.readInt32();
+ nSubType = rStrm.readInt32();
+ maModel.mnDxfId = rStrm.readInt32();
+ maModel.mnPriority = rStrm.readInt32();
+ nOperator = rStrm.readInt32();
+ rStrm.skip( 8 );
+ nFlags = rStrm.readuInt16();
+ nFmla1Size = rStrm.readInt32();
+ nFmla2Size = rStrm.readInt32();
+ nFmla3Size = rStrm.readInt32();
+ rStrm >> maModel.maText;
+
+ /* Import the formulas. For no obvious reason, the sizes of the formulas
+ are already stored before. Nevertheless the following formulas contain
+ their own sizes. */
+
+ // first formula
+ // I am not bored enough to bother simplifying these expressions
+ SAL_WARN_IF( !( (nFmla1Size >= 0) || ((nFmla2Size == 0) && (nFmla3Size == 0)) ), "sc.filter", "CondFormatRule::importCfRule - missing first formula" );
+ SAL_WARN_IF( !( (nFmla1Size > 0) == (rStrm.getRemaining() >= 8) ), "sc.filter", "CondFormatRule::importCfRule - formula size mismatch" );
+ if( rStrm.getRemaining() >= 8 )
+ {
+ ScAddress aBaseAddr = mrCondFormat.getRanges().GetTopLeftCorner();
+ ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, FormulaType::CondFormat, rStrm );
+ maModel.maFormulas.push_back( aTokens );
+
+ // second formula
+ OSL_ENSURE( (nFmla2Size >= 0) || (nFmla3Size == 0), "CondFormatRule::importCfRule - missing second formula" );
+ OSL_ENSURE( (nFmla2Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
+ if( rStrm.getRemaining() >= 8 )
+ {
+ aTokens = getFormulaParser().importFormula( aBaseAddr, FormulaType::CondFormat, rStrm );
+ maModel.maFormulas.push_back( aTokens );
+
+ // third formula
+ OSL_ENSURE( (nFmla3Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
+ if( rStrm.getRemaining() >= 8 )
+ {
+ aTokens = getFormulaParser().importFormula( aBaseAddr, FormulaType::CondFormat, rStrm );
+ maModel.maFormulas.push_back( aTokens );
+ }
+ }
+ }
+
+ // flags
+ maModel.mbStopIfTrue = getFlag( nFlags, BIFF12_CFRULE_STOPIFTRUE );
+ maModel.mbBottom = getFlag( nFlags, BIFF12_CFRULE_BOTTOM );
+ maModel.mbPercent = getFlag( nFlags, BIFF12_CFRULE_PERCENT );
+ maModel.mbAboveAverage = getFlag( nFlags, BIFF12_CFRULE_ABOVEAVERAGE );
+ // no flag for equalAverage, must be determined from subtype below...
+
+ // Convert the type/operator settings. This is a real mess...
+ switch( nType )
+ {
+ case BIFF12_CFRULE_TYPE_CELLIS:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_CELLIS, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ maModel.mnType = XML_cellIs;
+ maModel.setBiffOperator( nOperator );
+ OSL_ENSURE( maModel.mnOperator != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unknown operator" );
+ break;
+ case BIFF12_CFRULE_TYPE_EXPRESSION:
+ // here we have to look at the subtype to find the real type...
+ switch( nSubType )
+ {
+ case BIFF12_CFRULE_SUB_EXPRESSION:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_expression;
+ break;
+ case BIFF12_CFRULE_SUB_UNIQUE:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_uniqueValues;
+ break;
+ case BIFF12_CFRULE_SUB_TEXT:
+ maModel.setBiff12TextType( nOperator );
+ OSL_ENSURE( maModel.mnType != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unexpected operator value" );
+ break;
+ case BIFF12_CFRULE_SUB_BLANK:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_containsBlanks;
+ break;
+ case BIFF12_CFRULE_SUB_NOTBLANK:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_notContainsBlanks;
+ break;
+ case BIFF12_CFRULE_SUB_ERROR:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_containsErrors;
+ break;
+ case BIFF12_CFRULE_SUB_NOTERROR:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_notContainsErrors;
+ break;
+ case BIFF12_CFRULE_SUB_TODAY:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_TODAY, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_today;
+ break;
+ case BIFF12_CFRULE_SUB_TOMORROW:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_TOMORROW, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_tomorrow;
+ break;
+ case BIFF12_CFRULE_SUB_YESTERDAY:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_YESTERDAY,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_yesterday;
+ break;
+ case BIFF12_CFRULE_SUB_LAST7DAYS:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_LAST7DAYS,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_last7Days;
+ break;
+ case BIFF12_CFRULE_SUB_LASTMONTH:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_LASTMONTH,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_lastMonth;
+ break;
+ case BIFF12_CFRULE_SUB_NEXTMONTH:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_NEXTMONTH,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_nextMonth;
+ break;
+ case BIFF12_CFRULE_SUB_THISWEEK:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_THISWEEK, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_thisWeek;
+ break;
+ case BIFF12_CFRULE_SUB_NEXTWEEK:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_NEXTWEEK, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_nextWeek;
+ break;
+ case BIFF12_CFRULE_SUB_LASTWEEK:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_LASTWEEK, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_lastWeek;
+ break;
+ case BIFF12_CFRULE_SUB_THISMONTH:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_THISMONTH,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_thisMonth;
+ break;
+ case BIFF12_CFRULE_SUB_ABOVEAVERAGE:
+ OSL_ENSURE( maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = true;
+ maModel.mbEqualAverage = false; // does not exist as real flag...
+ break;
+ case BIFF12_CFRULE_SUB_BELOWAVERAGE:
+ OSL_ENSURE( !maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = false;
+ maModel.mbEqualAverage = false; // does not exist as real flag...
+ break;
+ case BIFF12_CFRULE_SUB_DUPLICATE:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_duplicateValues;
+ break;
+ case BIFF12_CFRULE_SUB_EQABOVEAVERAGE:
+ OSL_ENSURE( maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = true;
+ maModel.mbEqualAverage = true; // does not exist as real flag...
+ break;
+ case BIFF12_CFRULE_SUB_EQBELOWAVERAGE:
+ OSL_ENSURE( !maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = false;
+ maModel.mbEqualAverage = true; // does not exist as real flag...
+ break;
+ }
+ break;
+ case BIFF12_CFRULE_TYPE_COLORSCALE:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_COLORSCALE, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_colorScale;
+ break;
+ case BIFF12_CFRULE_TYPE_DATABAR:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_DATABAR, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_dataBar;
+ break;
+ case BIFF12_CFRULE_TYPE_TOPTEN:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_TOPTEN, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ maModel.mnType = XML_top10;
+ maModel.mnRank = nOperator; // operator field used for rank value
+ break;
+ case BIFF12_CFRULE_TYPE_ICONSET:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_ICONSET, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_iconSet;
+ break;
+ default:
+ OSL_FAIL( "CondFormatRule::importCfRule - unknown rule type" );
+ }
+}
+
+void CondFormatRule::setFormatEntry(sal_Int32 nPriority, ScFormatEntry* pEntry)
+{
+ maModel.mnPriority = nPriority;
+ mpFormatEntry = pEntry;
+}
+
+void CondFormatRule::finalizeImport()
+{
+ if (mpFormatEntry)
+ {
+ mpFormat->AddEntry(mpFormatEntry);
+ return;
+ }
+
+ ScConditionMode eOperator = ScConditionMode::NONE;
+
+ /* Replacement formula for unsupported rule types (text comparison rules,
+ time period rules, cell type rules). The replacement formulas below may
+ contain several placeholders:
+ - '#B' will be replaced by the current relative base address (may occur
+ several times).
+ - '#R' will be replaced by the entire range list of the conditional
+ formatting (absolute addresses).
+ - '#T' will be replaced by the quoted comparison text.
+ - '#L' will be replaced by the length of the comparison text (from
+ the 'text' attribute) used in text comparison rules.
+ - '#K' will be replaced by the rank (from the 'rank' attribute) used in
+ top-10 rules.
+ - '#M' will be replaced by the top/bottom flag (from the 'bottom'
+ attribute) used in the RANK function in top-10 rules.
+ - '#C' will be replaced by one of the comparison operators <, >, <=, or
+ >=, according to the 'aboveAverage' and 'equalAverage' flags.
+ */
+ OUString aReplaceFormula;
+
+ switch( maModel.mnType )
+ {
+ case XML_cellIs:
+ eOperator = CondFormatBuffer::convertToInternalOperator( maModel.mnOperator );
+ break;
+ case XML_duplicateValues:
+ eOperator = ScConditionMode::Duplicate;
+ break;
+ case XML_uniqueValues:
+ eOperator = ScConditionMode::NotDuplicate;
+ break;
+ case XML_expression:
+ eOperator = ScConditionMode::Direct;
+ break;
+ case XML_containsText:
+ OSL_ENSURE( maModel.mnOperator == XML_containsText, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::ContainsText;
+ break;
+ case XML_notContainsText:
+ // note: type XML_notContainsText vs. operator XML_notContains
+ OSL_ENSURE( maModel.mnOperator == XML_notContains, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::NotContainsText;
+ break;
+ case XML_beginsWith:
+ OSL_ENSURE( maModel.mnOperator == XML_beginsWith, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::BeginsWith;
+ break;
+ case XML_endsWith:
+ OSL_ENSURE( maModel.mnOperator == XML_endsWith, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::EndsWith;
+ break;
+ case XML_timePeriod:
+ break;
+ case XML_containsBlanks:
+ aReplaceFormula = "LEN(TRIM(#B))=0";
+ break;
+ case XML_notContainsBlanks:
+ aReplaceFormula = "LEN(TRIM(#B))>0";
+ break;
+ case XML_containsErrors:
+ eOperator = ScConditionMode::Error;
+ break;
+ case XML_notContainsErrors:
+ eOperator = ScConditionMode::NoError;
+ break;
+ case XML_top10:
+ if(maModel.mbPercent)
+ {
+ if(maModel.mbBottom)
+ eOperator = ScConditionMode::BottomPercent;
+ else
+ eOperator = ScConditionMode::TopPercent;
+ }
+ else
+ {
+ if(maModel.mbBottom)
+ eOperator = ScConditionMode::Bottom10;
+ else
+ eOperator = ScConditionMode::Top10;
+ }
+ break;
+ case XML_aboveAverage:
+ if(maModel.mbAboveAverage)
+ {
+ if(maModel.mbEqualAverage)
+ eOperator = ScConditionMode::AboveEqualAverage;
+ else
+ eOperator = ScConditionMode::AboveAverage;
+ }
+ else
+ {
+ if(maModel.mbEqualAverage)
+ eOperator = ScConditionMode::BelowEqualAverage;
+ else
+ eOperator = ScConditionMode::BelowAverage;
+ }
+ break;
+ case XML_colorScale:
+ break;
+ }
+
+ if( !aReplaceFormula.isEmpty() )
+ {
+ OUString aAddress;
+ sal_Int32 nStrPos = aReplaceFormula.getLength();
+ while( (nStrPos = aReplaceFormula.lastIndexOf( '#', nStrPos )) >= 0 )
+ {
+ switch( aReplaceFormula[ nStrPos + 1 ] )
+ {
+ case 'B': // current base address
+ if( aAddress.isEmpty() )
+ aAddress = FormulaProcessorBase::generateAddress2dString( mrCondFormat.getRanges().GetTopLeftCorner(), false );
+ aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aAddress );
+ break;
+ default:
+ OSL_FAIL( "CondFormatRule::finalizeImport - unknown placeholder" );
+ }
+ }
+
+ // set the replacement formula
+ maModel.maFormulas.clear();
+ appendFormula( aReplaceFormula );
+ eOperator = ScConditionMode::Direct;
+ }
+
+ ScAddress aPos = mrCondFormat.getRanges().GetTopLeftCorner();
+
+ if( eOperator == ScConditionMode::Error || eOperator == ScConditionMode::NoError )
+ {
+ ScDocument& rDoc = getScDocument();
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, nullptr, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( eOperator == ScConditionMode::BeginsWith || eOperator == ScConditionMode::EndsWith ||
+ eOperator == ScConditionMode::ContainsText || eOperator == ScConditionMode::NotContainsText )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScTokenArray aTokenArray(rDoc);
+ svl::SharedStringPool& rSPool = rDoc.GetSharedStringPool();
+ aTokenArray.AddString(rSPool.intern(maModel.maText));
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, &aTokenArray, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( (eOperator != ScConditionMode::NONE) && !maModel.maFormulas.empty() )
+ {
+ ScDocument& rDoc = getScDocument();
+ std::unique_ptr<ScTokenArray> pTokenArray2;
+ if( maModel.maFormulas.size() >= 2)
+ {
+ pTokenArray2.reset(new ScTokenArray(rDoc));
+ ScTokenConversion::ConvertToTokenArray(rDoc, *pTokenArray2, maModel.maFormulas[1]);
+ rDoc.CheckLinkFormulaNeedingCheck(*pTokenArray2);
+ }
+
+ ScTokenArray aTokenArray(rDoc);
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, maModel.maFormulas[ 0 ] );
+ rDoc.CheckLinkFormulaNeedingCheck( aTokenArray);
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry(eOperator,
+ &aTokenArray, pTokenArray2.get(), rDoc, aPos, aStyleName);
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if ( eOperator == ScConditionMode::Top10 || eOperator == ScConditionMode::Bottom10 ||
+ eOperator == ScConditionMode::TopPercent || eOperator == ScConditionMode::BottomPercent )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScTokenArray aTokenArray(rDoc);
+ aTokenArray.AddDouble( maModel.mnRank );
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, &aTokenArray, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( eOperator == ScConditionMode::AboveAverage || eOperator == ScConditionMode::BelowAverage ||
+ eOperator == ScConditionMode::AboveEqualAverage || eOperator == ScConditionMode::BelowEqualAverage )
+ {
+ ScDocument& rDoc = getScDocument();
+ // actually that is still unsupported
+ ScTokenArray aTokenArrayDev(rDoc);
+ aTokenArrayDev.AddDouble( maModel.mnStdDev );
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, &aTokenArrayDev, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( eOperator == ScConditionMode::Duplicate || eOperator == ScConditionMode::NotDuplicate )
+ {
+ ScDocument& rDoc = getScDocument();
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, nullptr, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( maModel.mnType == XML_timePeriod )
+ {
+ condformat::ScCondFormatDateType eDateType = condformat::TODAY;
+ switch( maModel.mnTimePeriod )
+ {
+ case XML_yesterday:
+ eDateType = condformat::YESTERDAY;
+ break;
+ case XML_today:
+ eDateType = condformat::TODAY;
+ break;
+ case XML_tomorrow:
+ eDateType = condformat::TOMORROW;
+ break;
+ case XML_last7Days:
+ eDateType = condformat::LAST7DAYS;
+ break;
+ case XML_lastWeek:
+ eDateType = condformat::LASTWEEK;
+ break;
+ case XML_thisWeek:
+ eDateType = condformat::THISWEEK;
+ break;
+ case XML_nextWeek:
+ eDateType = condformat::NEXTWEEK;
+ break;
+ case XML_lastMonth:
+ eDateType = condformat::LASTMONTH;
+ break;
+ case XML_thisMonth:
+ eDateType = condformat::THISMONTH;
+ break;
+ case XML_nextMonth:
+ eDateType = condformat::NEXTMONTH;
+ break;
+ default:
+ SAL_WARN("sc.filter", "CondFormatRule::finalizeImport - unknown time period type" );
+ }
+
+ ScDocument& rDoc = getScDocument();
+ ScCondDateFormatEntry* pFormatEntry = new ScCondDateFormatEntry(&rDoc);
+ pFormatEntry->SetDateType(eDateType);
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ pFormatEntry->SetStyleName( aStyleName );
+
+ mpFormat->AddEntry(pFormatEntry);
+ }
+ else if( mpColor )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScColorScaleFormat* pFormatEntry = new ScColorScaleFormat(&rDoc);
+
+ mpFormat->AddEntry(pFormatEntry);
+
+ mpColor->AddEntries( pFormatEntry, &rDoc, aPos );
+ }
+ else if (mpDataBar)
+ {
+ ScDocument& rDoc = getScDocument();
+ ScDataBarFormat* pFormatEntry = new ScDataBarFormat(&rDoc);
+
+ mpFormat->AddEntry(pFormatEntry);
+ mpDataBar->SetData( pFormatEntry, &rDoc, aPos );
+
+ }
+ else if(mpIconSet)
+ {
+ ScDocument& rDoc = getScDocument();
+ ScIconSetFormat* pFormatEntry = new ScIconSetFormat(&rDoc);
+
+ mpFormat->AddEntry(pFormatEntry);
+ mpIconSet->SetData( pFormatEntry, &rDoc, aPos );
+ }
+}
+
+ColorScaleRule* CondFormatRule::getColorScale()
+{
+ if(!mpColor)
+ mpColor.reset( new ColorScaleRule(mrCondFormat) );
+
+ return mpColor.get();
+}
+
+DataBarRule* CondFormatRule::getDataBar()
+{
+ if(!mpDataBar)
+ mpDataBar.reset( new DataBarRule(mrCondFormat) );
+
+ return mpDataBar.get();
+}
+
+IconSetRule* CondFormatRule::getIconSet()
+{
+ if(!mpIconSet)
+ mpIconSet.reset( new IconSetRule(mrCondFormat) );
+
+ return mpIconSet.get();
+}
+
+CondFormatModel::CondFormatModel() :
+ mbPivot( false )
+{
+}
+
+CondFormat::CondFormat( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ mpFormat(nullptr),
+ mbReadyForFinalize(false)
+{
+}
+
+CondFormat::~CondFormat()
+{
+ if (!mbReadyForFinalize && mpFormat)
+ delete mpFormat;
+}
+
+void CondFormat::importConditionalFormatting( const AttributeList& rAttribs )
+{
+ getAddressConverter().convertToCellRangeList( maModel.maRanges, rAttribs.getString( XML_sqref, OUString() ), getSheetIndex(), true );
+ maModel.mbPivot = rAttribs.getBool( XML_pivot, false );
+ mpFormat = new ScConditionalFormat(0, &getScDocument());
+}
+
+CondFormatRuleRef CondFormat::importCfRule( const AttributeList& rAttribs )
+{
+ CondFormatRuleRef xRule = createRule();
+ xRule->importCfRule( rAttribs );
+ return xRule;
+}
+
+void CondFormat::importCondFormatting( SequenceInputStream& rStrm )
+{
+ BinRangeList aRanges;
+ rStrm.skip( 8 );
+ rStrm >> aRanges;
+ getAddressConverter().convertToCellRangeList( maModel.maRanges, aRanges, getSheetIndex(), true );
+ mpFormat = new ScConditionalFormat(0, &getScDocument());
+}
+
+void CondFormat::importCfRule( SequenceInputStream& rStrm )
+{
+ CondFormatRuleRef xRule = createRule();
+ xRule->importCfRule( rStrm );
+ insertRule( xRule );
+}
+
+void CondFormat::finalizeImport()
+{
+ // probably some error in the xml if we are not ready
+ if ( !mbReadyForFinalize )
+ return;
+ ScDocument& rDoc = getScDocument();
+ mpFormat->SetRange(maModel.maRanges);
+ maRules.forEachMem( &CondFormatRule::finalizeImport );
+
+ if (mpFormat->size() > 0)
+ {
+ SCTAB nTab = maModel.maRanges.GetTopLeftCorner().Tab();
+ sal_Int32 nIndex = getScDocument().AddCondFormat(std::unique_ptr<ScConditionalFormat>(mpFormat), nTab);
+
+ rDoc.AddCondFormatData( maModel.maRanges, nTab, nIndex );
+ }
+ else
+ mbReadyForFinalize = false;
+}
+
+CondFormatRuleRef CondFormat::createRule()
+{
+ return std::make_shared<CondFormatRule>( *this, mpFormat );
+}
+
+void CondFormat::insertRule( CondFormatRuleRef const & xRule )
+{
+ if( xRule && (xRule->getPriority() > 0) )
+ {
+ OSL_ENSURE( maRules.find( xRule->getPriority() ) == maRules.end(), "CondFormat::insertRule - multiple rules with equal priority" );
+ maRules[ xRule->getPriority() ] = xRule;
+ }
+}
+
+CondFormatBuffer::CondFormatBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+CondFormatRef CondFormatBuffer::importConditionalFormatting( const AttributeList& rAttribs )
+{
+ CondFormatRef xCondFmt = createCondFormat();
+ xCondFmt->importConditionalFormatting( rAttribs );
+ return xCondFmt;
+}
+
+namespace {
+
+ScConditionalFormat* findFormatByRange(const ScRangeList& rRange, const ScDocument* pDoc, SCTAB nTab)
+{
+ ScConditionalFormatList* pList = pDoc->GetCondFormList(nTab);
+ for (auto const& it : *pList)
+ {
+ if (it->GetRange() == rRange)
+ {
+ return it.get();
+ }
+ }
+
+ return nullptr;
+}
+
+class ScRangeListHasher
+{
+public:
+ size_t operator() (ScRangeList const& rRanges) const
+ {
+ size_t nHash = 0;
+ for (size_t nIdx = 0; nIdx < rRanges.size(); ++nIdx)
+ nHash += rRanges[nIdx].hashArea();
+ return nHash;
+ }
+};
+
+}
+
+void CondFormatBuffer::updateImport(const ScDataBarFormatData* pTarget)
+{
+ for ( const auto& rRule : maCfRules )
+ {
+ if ( rRule && rRule->GetDataBarData() == pTarget )
+ rRule->finalizeImport();
+ }
+}
+
+bool CondFormatBuffer::insertRule(CondFormatRef const & xCondFmt, CondFormatRuleRef const & xRule)
+{
+ CondFormatRef xFoundFmt;
+ ScRangeList aRanges = xCondFmt->getRanges();
+
+ for (auto& rCondFmt : maCondFormats)
+ {
+ if (xCondFmt == rCondFmt)
+ continue;
+
+ if (aRanges == rCondFmt->getRanges())
+ {
+ xFoundFmt = rCondFmt;
+ break;
+ }
+ }
+
+ if (xFoundFmt)
+ {
+ xRule->mpFormat = xFoundFmt->mpFormat;
+ xFoundFmt->insertRule(xRule);
+ }
+
+ return static_cast<bool>(xFoundFmt);
+}
+
+void CondFormatBuffer::finalizeImport()
+{
+ std::unordered_set<size_t> aDoneExtCFs;
+ typedef std::unordered_map<ScRangeList, CondFormat*, ScRangeListHasher> RangeMap;
+ RangeMap aRangeMap;
+ for (auto& rxCondFormat : maCondFormats)
+ {
+ if (aRangeMap.find(rxCondFormat->getRanges()) != aRangeMap.end())
+ continue;
+ aRangeMap[rxCondFormat->getRanges()] = rxCondFormat.get();
+ }
+
+ size_t nExtCFIndex = 0;
+ for (const auto& rxExtCondFormat : maExtCondFormats)
+ {
+ ScDocument* pDoc = &getScDocument();
+ const ScRangeList& rRange = rxExtCondFormat->getRange();
+ RangeMap::iterator it = aRangeMap.find(rRange);
+ if (it != aRangeMap.end())
+ {
+ CondFormat& rCondFormat = *it->second;
+ const std::vector<std::unique_ptr<ScFormatEntry>>& rEntries = rxExtCondFormat->getEntries();
+ const std::vector<sal_Int32>& rPriorities = rxExtCondFormat->getPriorities();
+ size_t nEntryIdx = 0;
+ for (const auto& rxEntry : rEntries)
+ {
+ CondFormatRuleRef xRule = rCondFormat.createRule();
+ if (ScDataBarFormat *pData = dynamic_cast<ScDataBarFormat*>(rxEntry.get()))
+ updateImport(pData->GetDataBarData());
+ ScFormatEntry* pNewEntry = rxEntry->Clone(pDoc);
+ sal_Int32 nPriority = rPriorities[nEntryIdx];
+ if (nPriority == -1)
+ nPriority = mnNonPrioritizedRuleNextPriority++;
+ xRule->setFormatEntry(nPriority, pNewEntry);
+ rCondFormat.insertRule(xRule);
+ ++nEntryIdx;
+ }
+
+ aDoneExtCFs.insert(nExtCFIndex);
+ }
+
+ ++nExtCFIndex;
+ }
+
+ // tdf#138601 sort conditional formatting rules by their priority
+ if (maCondFormats.size() > 1)
+ {
+ size_t minIndex;
+ for (size_t i = 0; i < maCondFormats.size() - 1; ++i)
+ {
+ minIndex = i;
+ for (size_t j = i + 1; j < maCondFormats.size(); ++j)
+ {
+ const CondFormat::CondFormatRuleMap& rNextRules = maCondFormats[j]->maRules;
+ const CondFormat::CondFormatRuleMap& rMinRules = maCondFormats[minIndex]->maRules;
+ if (rNextRules.empty() || rMinRules.empty())
+ continue;
+ if (rNextRules.begin()->first < rMinRules.begin()->first)
+ minIndex = j;
+ }
+ if (i != minIndex)
+ std::swap(maCondFormats[i], maCondFormats[minIndex]);
+ }
+ }
+
+ for( const auto& rxCondFormat : maCondFormats )
+ {
+ if ( rxCondFormat)
+ rxCondFormat->finalizeImport();
+ }
+ for ( const auto& rxCfRule : maCfRules )
+ {
+ if ( rxCfRule )
+ rxCfRule->finalizeImport();
+ }
+
+ nExtCFIndex = 0;
+ for (const auto& rxExtCondFormat : maExtCondFormats)
+ {
+ if (aDoneExtCFs.count(nExtCFIndex))
+ {
+ ++nExtCFIndex;
+ continue;
+ }
+
+ ScDocument* pDoc = &getScDocument();
+ const ScRangeList& rRange = rxExtCondFormat->getRange();
+ SCTAB nTab = rRange.front().aStart.Tab();
+ ScConditionalFormat* pFormat = findFormatByRange(rRange, pDoc, nTab);
+ if (!pFormat)
+ {
+ // create new conditional format and insert it
+ auto pNewFormat = std::make_unique<ScConditionalFormat>(0, pDoc);
+ pFormat = pNewFormat.get();
+ pNewFormat->SetRange(rRange);
+ sal_uLong nKey = pDoc->AddCondFormat(std::move(pNewFormat), nTab);
+ pDoc->AddCondFormatData(rRange, nTab, nKey);
+ }
+
+ const std::vector< std::unique_ptr<ScFormatEntry> >& rEntries = rxExtCondFormat->getEntries();
+ for (const auto& rxEntry : rEntries)
+ {
+ pFormat->AddEntry(rxEntry->Clone(pDoc));
+ }
+
+ ++nExtCFIndex;
+ }
+
+ rStyleIdx = 0; // Resets <extlst> <cfRule> style index.
+}
+
+CondFormatRef CondFormatBuffer::importCondFormatting( SequenceInputStream& rStrm )
+{
+ CondFormatRef xCondFmt = createCondFormat();
+ xCondFmt->importCondFormatting( rStrm );
+ return xCondFmt;
+}
+
+ExtCfDataBarRuleRef CondFormatBuffer::createExtCfDataBarRule(ScDataBarFormatData* pTarget)
+{
+ ExtCfDataBarRuleRef extRule = std::make_shared<ExtCfDataBarRule>( pTarget, *this );
+ maCfRules.push_back( extRule );
+ return extRule;
+}
+
+std::vector< std::unique_ptr<ExtCfCondFormat> >& CondFormatBuffer::importExtCondFormat()
+{
+ return maExtCondFormats;
+}
+
+std::vector<std::unique_ptr<ScFormatEntry> >& CondFormatBuffer::importExtFormatEntries()
+{
+ return maExtFormatEntries;
+}
+
+sal_Int32 CondFormatBuffer::convertToApiOperator( sal_Int32 nToken )
+{
+ switch( nToken )
+ {
+ case XML_between: return ConditionOperator2::BETWEEN;
+ case XML_equal: return ConditionOperator2::EQUAL;
+ case XML_greaterThan: return ConditionOperator2::GREATER;
+ case XML_greaterThanOrEqual: return ConditionOperator2::GREATER_EQUAL;
+ case XML_lessThan: return ConditionOperator2::LESS;
+ case XML_lessThanOrEqual: return ConditionOperator2::LESS_EQUAL;
+ case XML_notBetween: return ConditionOperator2::NOT_BETWEEN;
+ case XML_notEqual: return ConditionOperator2::NOT_EQUAL;
+ case XML_duplicateValues: return ConditionOperator2::DUPLICATE;
+ }
+ return ConditionOperator2::NONE;
+}
+
+ScConditionMode CondFormatBuffer::convertToInternalOperator( sal_Int32 nToken )
+{
+ switch( nToken )
+ {
+ case XML_between: return ScConditionMode::Between;
+ case XML_equal: return ScConditionMode::Equal;
+ case XML_greaterThan: return ScConditionMode::Greater;
+ case XML_greaterThanOrEqual: return ScConditionMode::EqGreater;
+ case XML_lessThan: return ScConditionMode::Less;
+ case XML_lessThanOrEqual: return ScConditionMode::EqLess;
+ case XML_notBetween: return ScConditionMode::NotBetween;
+ case XML_notEqual: return ScConditionMode::NotEqual;
+ case XML_duplicateValues: return ScConditionMode::Duplicate;
+ case XML_uniqueValues: return ScConditionMode::NotDuplicate;
+ }
+ return ScConditionMode::NONE;
+}
+
+// private --------------------------------------------------------------------
+
+CondFormatRef CondFormatBuffer::createCondFormat()
+{
+ CondFormatRef xCondFmt = std::make_shared<CondFormat>( *this );
+ maCondFormats.push_back( xCondFmt );
+ return xCondFmt;
+}
+
+ExtCfDataBarRule::ExtCfDataBarRule(ScDataBarFormatData* pTarget, const WorksheetHelper& rParent):
+ WorksheetHelper(rParent),
+ mnRuleType( ExtCfDataBarRule::UNKNOWN ),
+ mpTarget(pTarget)
+{
+}
+
+void ExtCfDataBarRule::finalizeImport()
+{
+ switch ( mnRuleType )
+ {
+ case DATABAR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ if( maModel.maAxisPosition == "none" )
+ pDataBar->meAxisPosition = databar::NONE;
+ else if( maModel.maAxisPosition == "middle" )
+ pDataBar->meAxisPosition = databar::MIDDLE;
+ else
+ pDataBar->meAxisPosition = databar::AUTOMATIC;
+ pDataBar->mbGradient = maModel.mbGradient;
+ break;
+ }
+ case AXISCOLOR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ pDataBar->maAxisColor = maModel.mnAxisColor;
+ break;
+ }
+ case POSITIVEFILLCOLOR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ pDataBar->maPositiveColor = maModel.mnPositiveColor;
+ break;
+ }
+ case NEGATIVEFILLCOLOR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ pDataBar->mxNegativeColor = maModel.mnNegativeColor;
+ pDataBar->mbNeg = true;
+ break;
+ }
+ case CFVO:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ ScColorScaleEntry* pEntry = nullptr;
+ if(maModel.mbIsLower)
+ pEntry = pDataBar->mpLowerLimit.get();
+ else
+ pEntry = pDataBar->mpUpperLimit.get();
+
+ if(maModel.maColorScaleType == "min")
+ pEntry->SetType(COLORSCALE_MIN);
+ else if (maModel.maColorScaleType == "max")
+ pEntry->SetType(COLORSCALE_MAX);
+ else if (maModel.maColorScaleType == "autoMin")
+ pEntry->SetType(COLORSCALE_AUTO);
+ else if (maModel.maColorScaleType == "autoMax")
+ pEntry->SetType(COLORSCALE_AUTO);
+ else if (maModel.maColorScaleType == "percentile")
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ else if (maModel.maColorScaleType == "percent")
+ pEntry->SetType(COLORSCALE_PERCENT);
+ else if (maModel.maColorScaleType == "formula")
+ pEntry->SetType(COLORSCALE_FORMULA);
+ else if (maModel.maColorScaleType == "num")
+ {
+ pEntry->SetType(COLORSCALE_VALUE);
+ if (!maModel.msScaleTypeValue.isEmpty())
+ {
+ sal_Int32 nSize = 0;
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ double fValue = rtl::math::stringToDouble(maModel.msScaleTypeValue, '.', '\0', &eStatus, &nSize);
+ if (eStatus == rtl_math_ConversionStatus_Ok && nSize == maModel.msScaleTypeValue.getLength())
+ {
+ pEntry->SetValue(fValue);
+ }
+ }
+ }
+ break;
+ }
+ case UNKNOWN: // nothing to do
+ default:
+ break;
+ }
+}
+
+void ExtCfDataBarRule::importDataBar( const AttributeList& rAttribs )
+{
+ mnRuleType = DATABAR;
+ maModel.mbGradient = rAttribs.getBool( XML_gradient, true );
+ maModel.maAxisPosition = rAttribs.getString( XML_axisPosition, "automatic" );
+}
+
+void ExtCfDataBarRule::importPositiveFillColor( const AttributeList& rAttribs )
+{
+ mnRuleType = POSITIVEFILLCOLOR;
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+ maModel.mnPositiveColor = aColor;
+}
+
+void ExtCfDataBarRule::importNegativeFillColor( const AttributeList& rAttribs )
+{
+ mnRuleType = NEGATIVEFILLCOLOR;
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+ maModel.mnNegativeColor = aColor;
+}
+
+void ExtCfDataBarRule::importAxisColor( const AttributeList& rAttribs )
+{
+ mnRuleType = AXISCOLOR;
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+ maModel.mnAxisColor = aColor;
+}
+
+void ExtCfDataBarRule::importCfvo( const AttributeList& rAttribs )
+{
+ mnRuleType = CFVO;
+ maModel.maColorScaleType = rAttribs.getString( XML_type, OUString() );
+}
+
+ExtCfCondFormat::ExtCfCondFormat(ScRangeList aRange, std::vector< std::unique_ptr<ScFormatEntry> >& rEntries,
+ const std::vector<sal_Int32>* pPriorities):
+ maRange(std::move(aRange))
+{
+ maEntries.swap(rEntries);
+ if (pPriorities)
+ maPriorities = *pPriorities;
+ else
+ maPriorities.resize(maEntries.size(), -1);
+}
+
+ExtCfCondFormat::~ExtCfCondFormat()
+{
+}
+
+const ScRangeList& ExtCfCondFormat::getRange() const
+{
+ return maRange;
+}
+
+const std::vector< std::unique_ptr<ScFormatEntry> >& ExtCfCondFormat::getEntries() const
+{
+ return maEntries;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/condformatcontext.cxx b/sc/source/filter/oox/condformatcontext.cxx
new file mode 100644
index 0000000000..5da1f721ff
--- /dev/null
+++ b/sc/source/filter/oox/condformatcontext.cxx
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <condformatcontext.hxx>
+#include <extlstcontext.hxx>
+
+#include <biffhelper.hxx>
+#include <condformatbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+#include <utility>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+ColorScaleContext::ColorScaleContext( CondFormatContext& rFragment, CondFormatRuleRef xRule ) :
+ WorksheetContextBase( rFragment ),
+ mxRule(std::move( xRule ))
+{
+}
+
+ContextHandlerRef ColorScaleContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfRule ):
+ return (nElement == XLS_TOKEN( colorScale )) ? this : nullptr;
+ case XLS_TOKEN( colorScale ):
+ if (nElement == XLS_TOKEN( cfvo ))
+ return this;
+ else if (nElement == XLS_TOKEN( color ))
+ return this;
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void ColorScaleContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfvo ):
+ mxRule->getColorScale()->importCfvo( rAttribs );
+ break;
+ case XLS_TOKEN( color ):
+ mxRule->getColorScale()->importColor( rAttribs );
+ break;
+ }
+}
+
+DataBarContext::DataBarContext( CondFormatContext& rFragment, CondFormatRuleRef xRule ) :
+ WorksheetContextBase( rFragment ),
+ mxRule(std::move( xRule ))
+{
+}
+
+ContextHandlerRef DataBarContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfRule ):
+ return (nElement == XLS_TOKEN( dataBar )) ? this : nullptr;
+ case XLS_TOKEN( dataBar ):
+ if (nElement == XLS_TOKEN( cfvo ))
+ return this;
+ else if (nElement == XLS_TOKEN( color ))
+ return this;
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void DataBarContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( dataBar ):
+ mxRule->getDataBar()->importAttribs( rAttribs );
+ break;
+ case XLS_TOKEN( cfvo ):
+ mxRule->getDataBar()->importCfvo( rAttribs );
+ break;
+ case XLS_TOKEN( color ):
+ mxRule->getDataBar()->importColor( rAttribs );
+ break;
+ }
+}
+
+IconSetContext::IconSetContext(WorksheetContextBase& rParent, IconSetRule* pIconSet) :
+ WorksheetContextBase(rParent),
+ mpIconSet(pIconSet)
+{
+}
+
+ContextHandlerRef IconSetContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfRule ):
+ case XLS14_TOKEN( cfRule ):
+ return (nElement == XLS_TOKEN( iconSet ) || nElement == XLS14_TOKEN(iconSet)) ? this : nullptr;
+ case XLS_TOKEN( iconSet ):
+ case XLS14_TOKEN(iconSet):
+ if (nElement == XLS_TOKEN( cfvo ) ||
+ nElement == XLS14_TOKEN(cfvo) ||
+ nElement == XLS14_TOKEN(cfIcon))
+ return this;
+ else
+ return nullptr;
+ case XLS14_TOKEN(cfvo):
+ if (nElement == XM_TOKEN(f))
+ return this;
+ }
+ return nullptr;
+}
+
+void IconSetContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( iconSet ):
+ case XLS14_TOKEN( iconSet ):
+ mpIconSet->importAttribs( rAttribs );
+ break;
+ case XLS_TOKEN( cfvo ):
+ case XLS14_TOKEN( cfvo ):
+ mpIconSet->importCfvo( rAttribs );
+ break;
+ case XLS14_TOKEN(cfIcon):
+ mpIconSet->importIcon(rAttribs);
+ break;
+ }
+}
+
+void IconSetContext::onCharacters(const OUString& rChars)
+{
+ maChars = rChars;
+}
+
+void IconSetContext::onEndElement()
+{
+ switch(getCurrentElement())
+ {
+ case XM_TOKEN(f):
+ mpIconSet->importFormula(maChars);
+ maChars = OUString();
+ break;
+ }
+}
+
+CondFormatContext::CondFormatContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment )
+{
+}
+
+ContextHandlerRef CondFormatContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( conditionalFormatting ):
+ return (nElement == XLS_TOKEN( cfRule )) ? this : nullptr;
+ case XLS_TOKEN( cfRule ):
+ if (nElement == XLS_TOKEN( formula ))
+ return this;
+ else if (nElement == XLS_TOKEN( colorScale ) )
+ return new ColorScaleContext( *this, mxRule );
+ else if (nElement == XLS_TOKEN( dataBar ) )
+ return new DataBarContext( *this, mxRule );
+ else if (nElement == XLS_TOKEN( iconSet ) )
+ return new IconSetContext(*this, mxRule->getIconSet());
+ else if (nElement == XLS_TOKEN( extLst ) )
+ return new ExtLstLocalContext( *this, mxRule->getDataBar()->getDataBarFormatData() );
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void CondFormatContext::onEndElement()
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( conditionalFormatting ):
+ if(mxCondFmt)
+ mxCondFmt->setReadyForFinalize();
+ break;
+ case XLS_TOKEN( cfRule ):
+ if (mxCondFmt && mxRule)
+ {
+ ScRangeList aRanges = mxCondFmt->getRanges();
+ if ((aRanges.size() == 1 && aRanges.GetCellCount() == 1) ||
+ !getCondFormats().insertRule(mxCondFmt, mxRule))
+ mxCondFmt->insertRule(mxRule);
+ }
+ break;
+ }
+}
+
+void CondFormatContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( conditionalFormatting ):
+ mxCondFmt = getCondFormats().importConditionalFormatting( rAttribs );
+ break;
+ case XLS_TOKEN( cfRule ):
+ if( mxCondFmt ) mxRule = mxCondFmt->importCfRule( rAttribs );
+ break;
+ }
+}
+
+void CondFormatContext::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( formula ) ) && mxCondFmt && mxRule )
+ mxRule->appendFormula( rChars );
+}
+
+ContextHandlerRef CondFormatContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONDFORMATTING:
+ return (nRecId == BIFF12_ID_CFRULE) ? this : nullptr;
+ }
+ return nullptr;
+}
+
+void CondFormatContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONDFORMATTING:
+ mxCondFmt = getCondFormats().importCondFormatting( rStrm );
+ break;
+ case BIFF12_ID_CFRULE:
+ if( mxCondFmt ) mxCondFmt->importCfRule( rStrm );
+ break;
+ }
+}
+
+void CondFormatContext::onEndRecord()
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONDFORMATTING:
+ if( mxCondFmt )
+ {
+ mxCondFmt->setReadyForFinalize();
+ }
+ break;
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/connectionsbuffer.cxx b/sc/source/filter/oox/connectionsbuffer.cxx
new file mode 100644
index 0000000000..c8a2b700a8
--- /dev/null
+++ b/sc/source/filter/oox/connectionsbuffer.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 <connectionsbuffer.hxx>
+#include <biffhelper.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_Int32 BIFF12_RECONNECT_AS_REQUIRED = 1;
+
+const sal_uInt8 BIFF12_CONNECTION_SAVEPASSWORD_ON = 1;
+
+const sal_uInt16 BIFF12_CONNECTION_KEEPALIVE = 0x0001;
+const sal_uInt16 BIFF12_CONNECTION_NEW = 0x0002;
+const sal_uInt16 BIFF12_CONNECTION_DELETED = 0x0004;
+const sal_uInt16 BIFF12_CONNECTION_ONLYUSECONNFILE = 0x0008;
+const sal_uInt16 BIFF12_CONNECTION_BACKGROUND = 0x0010;
+const sal_uInt16 BIFF12_CONNECTION_REFRESHONLOAD = 0x0020;
+const sal_uInt16 BIFF12_CONNECTION_SAVEDATA = 0x0040;
+
+const sal_uInt16 BIFF12_CONNECTION_HAS_SOURCEFILE = 0x0001;
+const sal_uInt16 BIFF12_CONNECTION_HAS_SOURCECONNFILE = 0x0002;
+const sal_uInt16 BIFF12_CONNECTION_HAS_DESCRIPTION = 0x0004;
+const sal_uInt16 BIFF12_CONNECTION_HAS_NAME = 0x0008;
+const sal_uInt16 BIFF12_CONNECTION_HAS_SSOID = 0x0010;
+
+const sal_uInt32 BIFF12_WEBPR_XML = 0x00000100;
+const sal_uInt32 BIFF12_WEBPR_SOURCEDATA = 0x00000200;
+const sal_uInt32 BIFF12_WEBPR_PARSEPRE = 0x00000400;
+const sal_uInt32 BIFF12_WEBPR_CONSECUTIVE = 0x00000800;
+const sal_uInt32 BIFF12_WEBPR_FIRSTROW = 0x00001000;
+const sal_uInt32 BIFF12_WEBPR_XL97CREATED = 0x00002000;
+const sal_uInt32 BIFF12_WEBPR_TEXTDATES = 0x00004000;
+const sal_uInt32 BIFF12_WEBPR_XL2000REFRESHED = 0x00008000;
+const sal_uInt32 BIFF12_WEBPR_HTMLTABLES = 0x00010000;
+
+const sal_uInt8 BIFF12_WEBPR_HAS_POSTMETHOD = 0x01;
+const sal_uInt8 BIFF12_WEBPR_HAS_EDITPAGE = 0x02;
+const sal_uInt8 BIFF12_WEBPR_HAS_URL = 0x04;
+
+} // namespace
+
+WebPrModel::WebPrModel() :
+ mnHtmlFormat( XML_none ),
+ mbXml( false ),
+ mbSourceData( false ),
+ mbParsePre( false ),
+ mbConsecutive( false ),
+ mbFirstRow( false ),
+ mbXl97Created( false ),
+ mbTextDates( false ),
+ mbXl2000Refreshed( false ),
+ mbHtmlTables( false )
+{
+}
+
+ConnectionModel::ConnectionModel() :
+ mnId( -1 ),
+ mnType( BIFF12_CONNECTION_UNKNOWN ),
+ mnReconnectMethod( BIFF12_RECONNECT_AS_REQUIRED ),
+ mnCredentials( XML_integrated ),
+ mnInterval( 0 ),
+ mbKeepAlive( false ),
+ mbNew( false ),
+ mbDeleted( false ),
+ mbOnlyUseConnFile( false ),
+ mbBackground( false ),
+ mbRefreshOnLoad( false ),
+ mbSaveData( false ),
+ mbSavePassword( false )
+{
+}
+
+WebPrModel& ConnectionModel::createWebPr()
+{
+ OSL_ENSURE( !mxWebPr, "ConnectionModel::createWebPr - multiple call" );
+ mxWebPr.reset( new WebPrModel );
+ return *mxWebPr;
+}
+
+Connection::Connection( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+ maModel.mnId = -1;
+}
+
+void Connection::importConnection( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maDescription = rAttribs.getXString( XML_description, OUString() );
+ maModel.maSourceFile = rAttribs.getXString( XML_sourceFile, OUString() );
+ maModel.maSourceConnFile = rAttribs.getXString( XML_odcFile, OUString() );
+ maModel.maSsoId = rAttribs.getXString( XML_singleSignOnId, OUString() );
+ maModel.mnId = rAttribs.getInteger( XML_id, -1 );
+ // type and reconnectionMethod are using the BIFF12 constants instead of XML tokens
+ maModel.mnType = rAttribs.getInteger( XML_type, BIFF12_CONNECTION_UNKNOWN );
+ maModel.mnReconnectMethod = rAttribs.getInteger( XML_reconnectionMethod, BIFF12_RECONNECT_AS_REQUIRED );
+ maModel.mnCredentials = rAttribs.getToken( XML_credentials, XML_integrated );
+ maModel.mnInterval = rAttribs.getInteger( XML_interval, 0 );
+ maModel.mbKeepAlive = rAttribs.getBool( XML_keepAlive, false );
+ maModel.mbNew = rAttribs.getBool( XML_new, false );
+ maModel.mbDeleted = rAttribs.getBool( XML_deleted, false );
+ maModel.mbOnlyUseConnFile = rAttribs.getBool( XML_onlyUseConnectionFile, false );
+ maModel.mbBackground = rAttribs.getBool( XML_background, false );
+ maModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maModel.mbSaveData = rAttribs.getBool( XML_saveData, false );
+ maModel.mbSavePassword = rAttribs.getBool( XML_savePassword, false );
+}
+
+void Connection::importWebPr( const AttributeList& rAttribs )
+{
+ WebPrModel& rWebPr = maModel.createWebPr();
+
+ rWebPr.maUrl = rAttribs.getXString( XML_url, OUString() );
+ rWebPr.maPostMethod = rAttribs.getXString( XML_post, OUString() );
+ rWebPr.maEditPage = rAttribs.getXString( XML_editPage, OUString() );
+ rWebPr.mnHtmlFormat = rAttribs.getToken( XML_htmlFormat, XML_none );
+ rWebPr.mbXml = rAttribs.getBool( XML_xml, false );
+ rWebPr.mbSourceData = rAttribs.getBool( XML_sourceData, false );
+ rWebPr.mbParsePre = rAttribs.getBool( XML_parsePre, false );
+ rWebPr.mbConsecutive = rAttribs.getBool( XML_consecutive, false );
+ rWebPr.mbFirstRow = rAttribs.getBool( XML_firstRow, false );
+ rWebPr.mbXl97Created = rAttribs.getBool( XML_xl97, false );
+ rWebPr.mbTextDates = rAttribs.getBool( XML_textDates, false );
+ rWebPr.mbXl2000Refreshed = rAttribs.getBool( XML_xl2000, false );
+ rWebPr.mbHtmlTables = rAttribs.getBool( XML_htmlTables, false );
+}
+
+void Connection::importTables()
+{
+ if( maModel.mxWebPr )
+ {
+ OSL_ENSURE( maModel.mxWebPr->maTables.empty(), "Connection::importTables - multiple calls" );
+ maModel.mxWebPr->maTables.clear();
+ }
+}
+
+void Connection::importTable( const AttributeList& rAttribs, sal_Int32 nElement )
+{
+ if( !maModel.mxWebPr )
+ return;
+
+ Any aTableAny;
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): aTableAny <<= rAttribs.getXString( XML_v, OUString() ); break;
+ case XLS_TOKEN( x ): aTableAny <<= rAttribs.getInteger( XML_v, -1 ); break;
+ default:
+ OSL_ENSURE( false, "Connection::importTable - unexpected element" );
+ return;
+ }
+ maModel.mxWebPr->maTables.push_back( aTableAny );
+}
+
+void Connection::importConnection( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags, nStrFlags;
+ sal_uInt8 nSavePassword, nCredentials;
+ rStrm.skip( 2 );
+ nSavePassword = rStrm.readuChar();
+ rStrm.skip( 1 );
+ maModel.mnInterval = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ nStrFlags = rStrm.readuInt16();
+ maModel.mnType = rStrm.readInt32();
+ maModel.mnReconnectMethod = rStrm.readInt32();
+ maModel.mnId = rStrm.readInt32();
+ nCredentials = rStrm.readuChar();
+
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SOURCEFILE ) )
+ rStrm >> maModel.maSourceFile;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SOURCECONNFILE ) )
+ rStrm >> maModel.maSourceConnFile;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_DESCRIPTION ) )
+ rStrm >> maModel.maDescription;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_NAME ) )
+ rStrm >> maModel.maName;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SSOID ) )
+ rStrm >> maModel.maSsoId;
+
+ static const sal_Int32 spnCredentials[] = { XML_integrated, XML_none, XML_stored, XML_prompt };
+ maModel.mnCredentials = STATIC_ARRAY_SELECT( spnCredentials, nCredentials, XML_integrated );
+
+ maModel.mbKeepAlive = getFlag( nFlags, BIFF12_CONNECTION_KEEPALIVE );
+ maModel.mbNew = getFlag( nFlags, BIFF12_CONNECTION_NEW );
+ maModel.mbDeleted = getFlag( nFlags, BIFF12_CONNECTION_DELETED );
+ maModel.mbOnlyUseConnFile = getFlag( nFlags, BIFF12_CONNECTION_ONLYUSECONNFILE );
+ maModel.mbBackground = getFlag( nFlags, BIFF12_CONNECTION_BACKGROUND );
+ maModel.mbRefreshOnLoad = getFlag( nFlags, BIFF12_CONNECTION_REFRESHONLOAD );
+ maModel.mbSaveData = getFlag( nFlags, BIFF12_CONNECTION_SAVEDATA );
+ maModel.mbSavePassword = nSavePassword == BIFF12_CONNECTION_SAVEPASSWORD_ON;
+}
+
+void Connection::importWebPr( SequenceInputStream& rStrm )
+{
+ WebPrModel& rWebPr = maModel.createWebPr();
+
+ sal_uInt32 nFlags;
+ sal_uInt8 nStrFlags;
+ nFlags = rStrm.readuInt32();
+ nStrFlags = rStrm.readuChar();
+
+ if( getFlag( nStrFlags, BIFF12_WEBPR_HAS_URL ) )
+ rStrm >> rWebPr.maUrl;
+ if( getFlag( nStrFlags, BIFF12_WEBPR_HAS_POSTMETHOD ) )
+ rStrm >> rWebPr.maPostMethod;
+ if( getFlag( nStrFlags, BIFF12_WEBPR_HAS_EDITPAGE ) )
+ rStrm >> rWebPr.maEditPage;
+
+ static const sal_Int32 spnHmlFormats[] = { XML_none, XML_rtf, XML_all };
+ rWebPr.mnHtmlFormat = STATIC_ARRAY_SELECT( spnHmlFormats, extractValue< sal_uInt8 >( nFlags, 0, 8 ), XML_none );
+
+ rWebPr.mbXml = getFlag( nFlags, BIFF12_WEBPR_XML );
+ rWebPr.mbSourceData = getFlag( nFlags, BIFF12_WEBPR_SOURCEDATA );
+ rWebPr.mbParsePre = getFlag( nFlags, BIFF12_WEBPR_PARSEPRE );
+ rWebPr.mbConsecutive = getFlag( nFlags, BIFF12_WEBPR_CONSECUTIVE );
+ rWebPr.mbFirstRow = getFlag( nFlags, BIFF12_WEBPR_FIRSTROW );
+ rWebPr.mbXl97Created = getFlag( nFlags, BIFF12_WEBPR_XL97CREATED );
+ rWebPr.mbTextDates = getFlag( nFlags, BIFF12_WEBPR_TEXTDATES );
+ rWebPr.mbXl2000Refreshed = getFlag( nFlags, BIFF12_WEBPR_XL2000REFRESHED );
+ rWebPr.mbHtmlTables = getFlag( nFlags, BIFF12_WEBPR_HTMLTABLES );
+}
+
+void Connection::importWebPrTables( SequenceInputStream& /*rStrm*/ )
+{
+ if( maModel.mxWebPr )
+ {
+ OSL_ENSURE( maModel.mxWebPr->maTables.empty(), "Connection::importWebPrTables - multiple calls" );
+ maModel.mxWebPr->maTables.clear();
+ }
+}
+
+void Connection::importWebPrTable( SequenceInputStream& rStrm, sal_Int32 nRecId )
+{
+ if( !maModel.mxWebPr )
+ return;
+
+ Any aTableAny;
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCITEM_MISSING: break;
+ case BIFF12_ID_PCITEM_STRING: aTableAny <<= BiffHelper::readString( rStrm ); break;
+ case BIFF12_ID_PCITEM_INDEX: aTableAny <<= rStrm.readInt32(); break;
+ default:
+ OSL_ENSURE( false, "Connection::importWebPrTable - unexpected record" );
+ return;
+ }
+ maModel.mxWebPr->maTables.push_back( aTableAny );
+}
+
+ConnectionsBuffer::ConnectionsBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnUnusedId( 1 )
+{
+}
+
+Connection& ConnectionsBuffer::createConnection()
+{
+ ConnectionRef xConnection = std::make_shared<Connection>( *this );
+ maConnections.push_back( xConnection );
+ return *xConnection;
+}
+
+void ConnectionsBuffer::finalizeImport()
+{
+ for( const auto& rxConnection : maConnections )
+ insertConnectionToMap( rxConnection );
+}
+
+ConnectionRef ConnectionsBuffer::getConnection( sal_Int32 nConnId ) const
+{
+ return maConnectionsById.get( nConnId );
+}
+
+void ConnectionsBuffer::insertConnectionToMap( const ConnectionRef& rxConnection )
+{
+ sal_Int32 nConnId = rxConnection->getConnectionId();
+ if( nConnId > 0 )
+ {
+ OSL_ENSURE( !maConnectionsById.has( nConnId ), "ConnectionsBuffer::insertConnectionToMap - multiple connection identifier" );
+ maConnectionsById[ nConnId ] = rxConnection;
+ mnUnusedId = ::std::max< sal_Int32 >( mnUnusedId, nConnId + 1 );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/connectionsfragment.cxx b/sc/source/filter/oox/connectionsfragment.cxx
new file mode 100644
index 0000000000..3fafefd1dd
--- /dev/null
+++ b/sc/source/filter/oox/connectionsfragment.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <connectionsfragment.hxx>
+
+#include <oox/token/namespaces.hxx>
+#include <biffhelper.hxx>
+#include <connectionsbuffer.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+ConnectionContext::ConnectionContext( WorkbookFragmentBase& rParent, Connection& rConnection ) :
+ WorkbookContextBase( rParent ),
+ mrConnection( rConnection )
+{
+}
+
+ContextHandlerRef ConnectionContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( connection ):
+ if( nElement == XLS_TOKEN( webPr ) )
+ {
+ mrConnection.importWebPr( rAttribs );
+ return this;
+ }
+ break;
+
+ case XLS_TOKEN( webPr ):
+ if( nElement == XLS_TOKEN( tables ) )
+ {
+ mrConnection.importTables();
+ return this;
+ }
+ break;
+
+ case XLS_TOKEN( tables ):
+ mrConnection.importTable( rAttribs, nElement );
+ break;
+ }
+ return nullptr;
+}
+
+void ConnectionContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( getCurrentElement() == XLS_TOKEN( connection ) )
+ mrConnection.importConnection( rAttribs );
+}
+
+ContextHandlerRef ConnectionContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONNECTION:
+ if( nRecId == BIFF12_ID_WEBPR )
+ {
+ mrConnection.importWebPr( rStrm );
+ return this;
+ }
+ break;
+
+ case BIFF12_ID_WEBPR:
+ if( nRecId == BIFF12_ID_WEBPRTABLES )
+ {
+ mrConnection.importWebPrTables( rStrm );
+ return this;
+ }
+ break;
+
+ case BIFF12_ID_WEBPRTABLES:
+ mrConnection.importWebPrTable( rStrm, nRecId );
+ break;
+ }
+ return nullptr;
+}
+
+void ConnectionContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( getCurrentElement() == BIFF12_ID_CONNECTION )
+ mrConnection.importConnection( rStrm );
+}
+
+ConnectionsFragment::ConnectionsFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef ConnectionsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( connections ) )
+ return this;
+ break;
+
+ case XLS_TOKEN( connections ):
+ if( nElement == XLS_TOKEN( connection ) )
+ return new ConnectionContext( *this, getConnections().createConnection() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef ConnectionsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_CONNECTIONS )
+ return this;
+ break;
+
+ case BIFF12_ID_CONNECTIONS:
+ if( nRecId == BIFF12_ID_CONNECTION )
+ return new ConnectionContext( *this, getConnections().createConnection() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* ConnectionsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_CONNECTIONS, BIFF12_ID_CONNECTIONS + 1 },
+ { BIFF12_ID_CONNECTION, BIFF12_ID_CONNECTION + 1 },
+ { BIFF12_ID_WEBPR, BIFF12_ID_WEBPR + 1 },
+ { BIFF12_ID_WEBPRTABLES, BIFF12_ID_WEBPRTABLES + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void ConnectionsFragment::finalizeImport()
+{
+ getConnections().finalizeImport();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/defnamesbuffer.cxx b/sc/source/filter/oox/defnamesbuffer.cxx
new file mode 100644
index 0000000000..431ff8a34f
--- /dev/null
+++ b/sc/source/filter/oox/defnamesbuffer.cxx
@@ -0,0 +1,453 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <defnamesbuffer.hxx>
+
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <externallinkbuffer.hxx>
+#include <formulabase.hxx>
+#include <formulaparser.hxx>
+#include <worksheetbuffer.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt32 BIFF12_DEFNAME_HIDDEN = 0x00000001;
+const sal_uInt32 BIFF12_DEFNAME_FUNC = 0x00000002;
+const sal_uInt32 BIFF12_DEFNAME_VBNAME = 0x00000004;
+const sal_uInt32 BIFF12_DEFNAME_MACRO = 0x00000008;
+const sal_uInt32 BIFF12_DEFNAME_BUILTIN = 0x00000020;
+
+constexpr OUString spcOoxPrefix(u"_xlnm."_ustr);
+
+const char* const sppcBaseNames[] =
+{
+ "Consolidate_Area",
+ "Auto_Open",
+ "Auto_Close",
+ "Extract",
+ "Database",
+ "Criteria",
+ "Print_Area",
+ "Print_Titles",
+ "Recorder",
+ "Data_Form",
+ "Auto_Activate",
+ "Auto_Deactivate",
+ "Sheet_Title",
+ "_FilterDatabase"
+};
+
+OUString lclGetBaseName( sal_Unicode cBuiltinId )
+{
+ OSL_ENSURE( cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ), "lclGetBaseName - unsupported built-in identifier" );
+ OUStringBuffer aBuffer;
+ if( cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ) )
+ aBuffer.appendAscii( sppcBaseNames[ cBuiltinId ] );
+ else
+ aBuffer.append( static_cast< sal_Int32 >( cBuiltinId ) );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString lclGetPrefixedName( sal_Unicode cBuiltinId )
+{
+ return spcOoxPrefix + lclGetBaseName( cBuiltinId );
+}
+
+/** returns the built-in name identifier from a prefixed built-in name, e.g. '_xlnm.Print_Area'. */
+sal_Unicode lclGetBuiltinIdFromPrefixedName( std::u16string_view aModelName )
+{
+ if( o3tl::matchIgnoreAsciiCase( aModelName, spcOoxPrefix ) )
+ {
+ for( sal_Unicode cBuiltinId = 0; cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ); ++cBuiltinId )
+ {
+ OUString aBaseName = lclGetBaseName( cBuiltinId );
+ sal_Int32 nBaseNameLen = aBaseName.getLength();
+ if( (sal_Int32(aModelName.size()) == spcOoxPrefix.getLength() + nBaseNameLen) && o3tl::matchIgnoreAsciiCase( aModelName, aBaseName, spcOoxPrefix.getLength() ) )
+ return cBuiltinId;
+ }
+ }
+ return BIFF_DEFNAME_UNKNOWN;
+}
+
+/** returns the built-in name identifier from a built-in base name, e.g. 'Print_Area'. */
+sal_Unicode lclGetBuiltinIdFromBaseName( std::u16string_view rModelName )
+{
+ for( sal_Unicode cBuiltinId = 0; cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ); ++cBuiltinId )
+ if( o3tl::equalsIgnoreAsciiCase( rModelName, sppcBaseNames[ cBuiltinId ] ) )
+ return cBuiltinId;
+ return BIFF_DEFNAME_UNKNOWN;
+}
+
+OUString lclGetUpcaseModelName( const OUString& rModelName )
+{
+ // TODO: i18n?
+ return rModelName.toAsciiUpperCase();
+}
+
+} // namespace
+
+DefinedNameModel::DefinedNameModel() :
+ mnSheet( -1 ),
+ mnFuncGroupId( -1 ),
+ mbMacro( false ),
+ mbFunction( false ),
+ mbVBName( false ),
+ mbHidden( false )
+{
+}
+
+DefinedNameBase::DefinedNameBase( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+const OUString& DefinedNameBase::getUpcaseModelName() const
+{
+ if( maUpModelName.isEmpty() )
+ maUpModelName = lclGetUpcaseModelName( maModel.maName );
+ return maUpModelName;
+}
+
+DefinedName::DefinedName( const WorkbookHelper& rHelper ) :
+ DefinedNameBase( rHelper ),
+ maScRangeData(nullptr, false),
+ mnTokenIndex( -1 ),
+ mnCalcSheet( 0 ),
+ mcBuiltinId( BIFF_DEFNAME_UNKNOWN )
+{
+}
+
+void DefinedName::importDefinedName( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.mnSheet = rAttribs.getInteger( XML_localSheetId, -1 );
+ maModel.mnFuncGroupId = rAttribs.getInteger( XML_functionGroupId, -1 );
+ maModel.mbMacro = rAttribs.getBool( XML_xlm, false );
+ maModel.mbFunction = rAttribs.getBool( XML_function, false );
+ maModel.mbVBName = rAttribs.getBool( XML_vbProcedure, false );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+ mnCalcSheet = (maModel.mnSheet >= 0) ? getWorksheets().getCalcSheetIndex( maModel.mnSheet ) : -1;
+
+ /* Detect built-in state from name itself, there is no built-in flag.
+ Built-in names are prefixed with '_xlnm.' instead. */
+ mcBuiltinId = lclGetBuiltinIdFromPrefixedName( maModel.maName );
+}
+
+void DefinedName::setFormula( const OUString& rFormula )
+{
+ maModel.maFormula = rFormula;
+}
+
+void DefinedName::importDefinedName( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags;
+ nFlags = rStrm.readuInt32();
+ rStrm.skip( 1 ); // keyboard shortcut
+ maModel.mnSheet = rStrm.readInt32();
+ rStrm >> maModel.maName;
+ mnCalcSheet = (maModel.mnSheet >= 0) ? getWorksheets().getCalcSheetIndex( maModel.mnSheet ) : -1;
+
+ // macro function/command, hidden flag
+ maModel.mnFuncGroupId = extractValue< sal_Int32 >( nFlags, 6, 9 );
+ maModel.mbMacro = getFlag( nFlags, BIFF12_DEFNAME_MACRO );
+ maModel.mbFunction = getFlag( nFlags, BIFF12_DEFNAME_FUNC );
+ maModel.mbVBName = getFlag( nFlags, BIFF12_DEFNAME_VBNAME );
+ maModel.mbHidden = getFlag( nFlags, BIFF12_DEFNAME_HIDDEN );
+
+ // get built-in name index from name
+ if( getFlag( nFlags, BIFF12_DEFNAME_BUILTIN ) )
+ mcBuiltinId = lclGetBuiltinIdFromBaseName( maModel.maName );
+
+ // store token array data
+ sal_Int64 nRecPos = rStrm.tell();
+ sal_Int32 nFmlaSize = rStrm.readInt32();
+ rStrm.skip( nFmlaSize );
+ sal_Int32 nAddDataSize = rStrm.readInt32();
+ if( !rStrm.isEof() && (nFmlaSize > 0) && (nAddDataSize >= 0) && (rStrm.getRemaining() >= nAddDataSize) )
+ {
+ sal_Int32 nTotalSize = 8 + nFmlaSize + nAddDataSize;
+ mxFormula.reset( new StreamDataSequence );
+ rStrm.seek( nRecPos );
+ rStrm.readData( *mxFormula, nTotalSize );
+ }
+}
+
+void DefinedName::createNameObject( sal_Int32 nIndex )
+{
+ // do not create names for (macro) functions or VBA procedures
+ // #163146# do not ignore hidden names (may be regular names created by VBA scripts)
+ if( /*maModel.mbHidden ||*/ maModel.mbFunction || maModel.mbVBName )
+ return;
+
+ // convert original name to final Calc name (TODO: filter invalid characters from model name)
+ maCalcName = isBuiltinName() ? lclGetPrefixedName( mcBuiltinId ) : maModel.maName;
+
+ // #163146# do not rename sheet-local names by default, this breaks VBA scripts
+
+ // special flags for this name
+ sal_Int32 nNameFlags = 0;
+ using namespace ::com::sun::star::sheet;
+ if( !isGlobalName() ) switch( mcBuiltinId )
+ {
+ case BIFF_DEFNAME_CRITERIA:
+ case BIFF_DEFNAME_FILTERDATABASE:
+ nNameFlags = NamedRangeFlag::FILTER_CRITERIA;
+ break;
+ case BIFF_DEFNAME_PRINTAREA:
+ nNameFlags = NamedRangeFlag::PRINT_AREA;
+ break;
+ case BIFF_DEFNAME_PRINTTITLES:
+ nNameFlags = NamedRangeFlag::COLUMN_HEADER | NamedRangeFlag::ROW_HEADER;
+ break;
+ }
+
+ // Set the appropriate flag if it is a hidden named range
+ if (maModel.mbHidden)
+ nNameFlags |= NamedRangeFlag::HIDDEN;
+
+ // create the name and insert it into the document, maCalcName will be changed to the resulting name
+ if (maModel.mnSheet >= 0)
+ maScRangeData = createLocalNamedRangeObject(maCalcName, nIndex, nNameFlags, maModel.mnSheet);
+ else
+ maScRangeData = createNamedRangeObject( maCalcName, nIndex, nNameFlags);
+ mnTokenIndex = nIndex;
+}
+
+bool DefinedName::isValid(
+ const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks) const
+{
+ ScRange aRange;
+ OUString aExternDocName;
+ OUString aStartTabName;
+ OUString aEndTabName;
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID;
+ aRange.Parse_XL_Header(maModel.maFormula.getStr(), getScDocument(), aExternDocName,
+ aStartTabName, aEndTabName, nFlags, /*bOnlyAcceptSingle=*/false,
+ &rExternalLinks);
+ // aExternDocName is something like 'file:///path/to/my.xlsx' in the valid case, and it's an int
+ // when it's invalid.
+ bool bInvalidExternalRef = aExternDocName.toInt32() > 0;
+ return !bInvalidExternalRef;
+}
+
+std::unique_ptr<ScTokenArray> DefinedName::getScTokens(
+ const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks )
+{
+ ScCompiler aCompiler(getScDocument(), ScAddress(0, 0, mnCalcSheet), formula::FormulaGrammar::GRAM_OOXML);
+ aCompiler.SetExternalLinks( rExternalLinks);
+ std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(maModel.maFormula));
+ // Compile the tokens into RPN once to populate information into tokens
+ // where necessary, e.g. for TableRef inner reference. RPN can be discarded
+ // after, a resulting error must be reset.
+ FormulaError nErr = pArray->GetCodeError();
+ aCompiler.CompileTokenArray();
+ getScDocument().CheckLinkFormulaNeedingCheck( *pArray);
+ pArray->DelRPN();
+ pArray->SetCodeError(nErr);
+
+ return pArray;
+}
+
+void DefinedName::convertFormula( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks )
+{
+ ScRangeData* pScRangeData = maScRangeData.first;
+ // macro function or vba procedure
+ if (!pScRangeData)
+ return;
+
+ // convert and set formula of the defined name
+ {
+ std::unique_ptr<ScTokenArray> pTokenArray = getScTokens( rExternalLinks);
+ pScRangeData->SetCode( *pTokenArray );
+ }
+
+ ScTokenArray* pTokenArray = pScRangeData->GetCode();
+ Sequence< FormulaToken > aFTokenSeq;
+ ScTokenConversion::ConvertToTokenSequence( getScDocument(), aFTokenSeq, *pTokenArray );
+ // set built-in names (print ranges, repeated titles, filter ranges)
+ if( isGlobalName() )
+ return;
+
+ switch( mcBuiltinId )
+ {
+ case BIFF_DEFNAME_PRINTAREA:
+ {
+ Reference< XPrintAreas > xPrintAreas( getSheetFromDoc( mnCalcSheet ), UNO_QUERY );
+ ScRangeList aPrintRanges;
+ getFormulaParser().extractCellRangeList( aPrintRanges, aFTokenSeq, mnCalcSheet );
+ if( xPrintAreas.is() && !aPrintRanges.empty() )
+ xPrintAreas->setPrintAreas( AddressConverter::toApiSequence(aPrintRanges) );
+ }
+ break;
+ case BIFF_DEFNAME_PRINTTITLES:
+ {
+ Reference< XPrintAreas > xPrintAreas( getSheetFromDoc( mnCalcSheet ), UNO_QUERY );
+ ScRangeList aTitleRanges;
+ getFormulaParser().extractCellRangeList( aTitleRanges, aFTokenSeq, mnCalcSheet );
+ if( xPrintAreas.is() && !aTitleRanges.empty() )
+ {
+ bool bHasRowTitles = false;
+ bool bHasColTitles = false;
+ const ScAddress& rMaxPos = getAddressConverter().getMaxAddress();
+ for (size_t i = 0, nSize = aTitleRanges.size(); i < nSize; ++i)
+ {
+ const ScRange& rRange = aTitleRanges[i];
+ bool bFullRow = (rRange.aStart.Col() == 0) && ( rRange.aEnd.Col() >= rMaxPos.Col() );
+ bool bFullCol = (rRange.aStart.Row() == 0) && ( rRange.aEnd.Row() >= rMaxPos.Row() );
+ if( !bHasRowTitles && bFullRow && !bFullCol )
+ {
+ xPrintAreas->setTitleRows( CellRangeAddress(rRange.aStart.Tab(),
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row()) );
+ xPrintAreas->setPrintTitleRows( true );
+ bHasRowTitles = true;
+ }
+ else if( !bHasColTitles && bFullCol && !bFullRow )
+ {
+ xPrintAreas->setTitleColumns( CellRangeAddress(rRange.aStart.Tab(),
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row()) );
+ xPrintAreas->setPrintTitleColumns( true );
+ bHasColTitles = true;
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+bool DefinedName::getAbsoluteRange( ScRange& orRange ) const
+{
+ ScRangeData* pScRangeData = maScRangeData.first;
+ ScTokenArray* pTokenArray = pScRangeData->GetCode();
+ Sequence< FormulaToken > aFTokenSeq;
+ ScTokenConversion::ConvertToTokenSequence(getScDocument(), aFTokenSeq, *pTokenArray);
+ return getFormulaParser().extractCellRange( orRange, aFTokenSeq );
+}
+
+DefinedName::~DefinedName()
+{
+ // this kind of field is owned by us - see lcl_addNewByNameAndTokens
+ bool bOwned = maScRangeData.second;
+ if (bOwned)
+ delete maScRangeData.first;
+}
+
+DefinedNamesBuffer::DefinedNamesBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+DefinedNameRef DefinedNamesBuffer::importDefinedName( const AttributeList& rAttribs )
+{
+ DefinedNameRef xDefName = createDefinedName();
+ xDefName->importDefinedName( rAttribs );
+ return xDefName;
+}
+
+void DefinedNamesBuffer::importDefinedName( SequenceInputStream& rStrm )
+{
+ createDefinedName()->importDefinedName( rStrm );
+}
+
+void DefinedNamesBuffer::finalizeImport()
+{
+ // first insert all names without formula definition into the document, and insert them into the maps
+ int index = 0;
+ for( DefinedNameRef& xDefName : maDefNames )
+ {
+ if (!xDefName->isValid(getExternalLinks().getLinkInfos()))
+ {
+ continue;
+ }
+
+ xDefName->createNameObject( ++index );
+ // map by sheet index and original model name
+ maModelNameMap[ SheetNameKey( xDefName->getLocalCalcSheet(), xDefName->getUpcaseModelName() ) ] = xDefName;
+ // map by sheet index and built-in identifier
+ if( !xDefName->isGlobalName() && xDefName->isBuiltinName() )
+ maBuiltinMap[ BuiltinKey( xDefName->getLocalCalcSheet(), xDefName->getBuiltinId() ) ] = xDefName;
+ // map by API formula token identifier
+ sal_Int32 nTokenIndex = xDefName->getTokenIndex();
+ if( nTokenIndex >= 0 )
+ maTokenIdMap[ nTokenIndex ] = xDefName;
+ }
+
+ /* Now convert all name formulas, so that the formula parser can find all
+ names in case of circular dependencies. */
+ maDefNames.forEachMem( &DefinedName::convertFormula, getExternalLinks().getLinkInfos());
+}
+
+DefinedNameRef DefinedNamesBuffer::getByIndex( sal_Int32 nIndex ) const
+{
+ return maDefNames.get( nIndex );
+}
+
+DefinedNameRef DefinedNamesBuffer::getByTokenIndex( sal_Int32 nIndex ) const
+{
+ return maTokenIdMap.get( nIndex );
+}
+
+DefinedNameRef DefinedNamesBuffer::getByModelName( const OUString& rModelName, sal_Int16 nCalcSheet ) const
+{
+ OUString aUpcaseName = lclGetUpcaseModelName( rModelName );
+ DefinedNameRef xDefName = maModelNameMap.get( SheetNameKey( nCalcSheet, aUpcaseName ) );
+ // lookup global name, if no local name exists
+ if( !xDefName && (nCalcSheet >= 0) )
+ xDefName = maModelNameMap.get( SheetNameKey( -1, aUpcaseName ) );
+ return xDefName;
+}
+
+DefinedNameRef DefinedNamesBuffer::getByBuiltinId( sal_Unicode cBuiltinId, sal_Int16 nCalcSheet ) const
+{
+ return maBuiltinMap.get( BuiltinKey( nCalcSheet, cBuiltinId ) );
+}
+
+DefinedNameRef DefinedNamesBuffer::createDefinedName()
+{
+ DefinedNameRef xDefName = std::make_shared<DefinedName>( *this );
+ maDefNames.push_back( xDefName );
+ return xDefName;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/drawingbase.cxx b/sc/source/filter/oox/drawingbase.cxx
new file mode 100644
index 0000000000..0f1ee8690c
--- /dev/null
+++ b/sc/source/filter/oox/drawingbase.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 <drawingbase.hxx>
+#include <addressconverter.hxx>
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <unitconverter.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::drawingml;
+
+namespace {
+
+/** Converts the passed 32-bit integer value from 1/100 mm to EMUs. */
+sal_Int64 lclHmmToEmu( sal_Int32 nValue )
+{
+ return (nValue < 0) ? -1 : convertHmmToEmu( nValue );
+}
+
+/** Converts the passed 64-bit integer value from EMUs to 1/100 mm. */
+sal_Int32 lclEmuToHmm( sal_Int64 nValue )
+{
+ return (nValue < 0) ? -1 : convertEmuToHmm( nValue );
+}
+
+} // namespace
+
+CellAnchorModel::CellAnchorModel() :
+ mnCol( -1 ),
+ mnRow( -1 ),
+ mnColOffset( 0 ),
+ mnRowOffset( 0 )
+{
+}
+
+AnchorClientDataModel::AnchorClientDataModel() :
+ mbLocksWithSheet( true ),
+ mbPrintsWithSheet( true )
+{
+}
+
+ShapeAnchor::ShapeAnchor( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ meAnchorType( ANCHOR_INVALID ),
+ meCellAnchorType( CellAnchorType::Emu ),
+ meEditAs( ANCHOR_TWOCELL )
+{
+}
+
+void ShapeAnchor::importAnchor( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case XDR_TOKEN( absoluteAnchor ):
+ meAnchorType = ANCHOR_ABSOLUTE;
+ meEditAs = ANCHOR_ABSOLUTE;
+ break;
+ case XDR_TOKEN( oneCellAnchor ):
+ meAnchorType = ANCHOR_ONECELL;
+ meEditAs = ANCHOR_ONECELL;
+ break;
+ case XDR_TOKEN( twoCellAnchor ):
+ {
+ meAnchorType = ANCHOR_TWOCELL;
+ OUString sEditAs = rAttribs.getXString( XML_editAs, OUString() );
+ if ( !sEditAs.isEmpty() )
+ {
+ if ( sEditAs.equalsIgnoreAsciiCase("absolute" ) )
+ meEditAs = ANCHOR_ABSOLUTE;
+ else if ( sEditAs.equalsIgnoreAsciiCase("oneCell") )
+ meEditAs = ANCHOR_ONECELL;
+ else if (sEditAs.equalsIgnoreAsciiCase("twoCell") )
+ meEditAs = ANCHOR_TWOCELL;
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "ShapeAnchor::importAnchor - unexpected element" );
+ }
+ meCellAnchorType = CellAnchorType::Emu;
+}
+
+void ShapeAnchor::importPos( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( meAnchorType == ANCHOR_ABSOLUTE, "ShapeAnchor::importPos - unexpected 'xdr:pos' element" );
+ maPos.X = rAttribs.getHyper( XML_x, 0 );
+ maPos.Y = rAttribs.getHyper( XML_y, 0 );
+}
+
+void ShapeAnchor::importExt( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( (meAnchorType == ANCHOR_ABSOLUTE) || (meAnchorType == ANCHOR_ONECELL), "ShapeAnchor::importExt - unexpected 'xdr:ext' element" );
+ maSize.Width = rAttribs.getHyper( XML_cx, 0 );
+ maSize.Height = rAttribs.getHyper( XML_cy, 0 );
+}
+
+void ShapeAnchor::importClientData( const AttributeList& rAttribs )
+{
+ maClientData.mbLocksWithSheet = rAttribs.getBool( XML_fLocksWithSheet, true );
+ maClientData.mbPrintsWithSheet = rAttribs.getBool( XML_fPrintsWithSheet, true );
+}
+
+void ShapeAnchor::setCellPos( sal_Int32 nElement, sal_Int32 nParentContext, std::u16string_view rValue )
+{
+ CellAnchorModel* pCellAnchor = nullptr;
+ switch( nParentContext )
+ {
+ case XDR_TOKEN( from ):
+ OSL_ENSURE( (meAnchorType == ANCHOR_ONECELL) || (meAnchorType == ANCHOR_TWOCELL), "ShapeAnchor::setCellPos - unexpected 'xdr:from' element" );
+ pCellAnchor = &maFrom;
+ break;
+ case XDR_TOKEN( to ):
+ OSL_ENSURE( meAnchorType == ANCHOR_TWOCELL, "ShapeAnchor::setCellPos - unexpected 'xdr:to' element" );
+ pCellAnchor = &maTo;
+ break;
+ default:
+ OSL_ENSURE( false, "ShapeAnchor::setCellPos - unexpected parent element" );
+ }
+ if( pCellAnchor ) switch( nElement )
+ {
+ case XDR_TOKEN( col ): pCellAnchor->mnCol = o3tl::toInt32(rValue); break;
+ case XDR_TOKEN( row ): pCellAnchor->mnRow = o3tl::toInt32(rValue); break;
+ case XDR_TOKEN( colOff ): pCellAnchor->mnColOffset = o3tl::toInt64(rValue); break;
+ case XDR_TOKEN( rowOff ): pCellAnchor->mnRowOffset = o3tl::toInt64(rValue); break;
+ default: OSL_ENSURE( false, "ShapeAnchor::setCellPos - unexpected element" );
+ }
+}
+
+void ShapeAnchor::importVmlAnchor( std::u16string_view rAnchor )
+{
+ meAnchorType = ANCHOR_VML;
+ meCellAnchorType = CellAnchorType::Pixel;
+
+ sal_Int32 nValues[8];
+ sal_Int32 nI{ 0 };
+
+ for(sal_Int32 nIndex{ 0 }; nIndex>=0;)
+ {
+ nValues[nI] = o3tl::toInt32(o3tl::getToken(rAnchor, 0, ',', nIndex ));
+ if (++nI==8)
+ {
+ maFrom.mnCol = nValues[0];
+ maFrom.mnColOffset = nValues[1];
+ maFrom.mnRow = nValues[2];
+ maFrom.mnRowOffset = nValues[3];
+ maTo.mnCol = nValues[4];
+ maTo.mnColOffset = nValues[5];
+ maTo.mnRow = nValues[6];
+ maTo.mnRowOffset = nValues[7];
+ return;
+ }
+ }
+
+ OSL_FAIL("ShapeAnchor::importVmlAnchor - missing anchor tokens" );
+}
+
+bool ShapeAnchor::isAnchorValid() const
+{
+ // Checks whether the shape is visible based on the anchor
+ // if From and To anchor has the same attribute values, the shape will not have width and height
+ // and thus we can assume that such kind of shape will be not be visible
+ return !(meAnchorType == ANCHOR_TWOCELL &&
+ (maTo.mnRow == maFrom.mnRow && maTo.mnCol == maFrom.mnCol) &&
+ (maTo.mnColOffset == maFrom.mnColOffset && maTo.mnRowOffset == maFrom.mnRowOffset));
+}
+
+EmuRectangle ShapeAnchor::calcAnchorRectEmu( const css::awt::Size& rPageSizeHmm ) const
+{
+ AddressConverter& rAddrConv = getAddressConverter();
+ EmuSize aPageSize( lclHmmToEmu( rPageSizeHmm.Width ), lclHmmToEmu( rPageSizeHmm.Height ) );
+ EmuRectangle aAnchorRect( -1, -1, -1, -1 );
+
+ // calculate shape position
+ switch( meAnchorType )
+ {
+ case ANCHOR_ABSOLUTE:
+ OSL_ENSURE( maPos.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" );
+ if( maPos.isValid() && (maPos.X < aPageSize.Width) && (maPos.Y < aPageSize.Height) )
+ aAnchorRect.setPos( maPos );
+ break;
+ case ANCHOR_ONECELL:
+ case ANCHOR_TWOCELL:
+ case ANCHOR_VML:
+ OSL_ENSURE( maFrom.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" );
+ if( maFrom.isValid() && rAddrConv.checkCol( maFrom.mnCol, true ) && rAddrConv.checkRow( maFrom.mnRow, true ) )
+ {
+ EmuPoint aPoint = calcCellAnchorEmu( maFrom );
+ if( (aPoint.X < aPageSize.Width) && (aPoint.Y < aPageSize.Height) )
+ aAnchorRect.setPos( aPoint );
+ }
+ break;
+ case ANCHOR_INVALID:
+ OSL_ENSURE( false, "ShapeAnchor::calcAnchorRectEmu - invalid anchor" );
+ break;
+ }
+
+ // calculate shape size
+ if( (aAnchorRect.X >= 0) && (aAnchorRect.Y >= 0) ) switch( meAnchorType )
+ {
+ case ANCHOR_ABSOLUTE:
+ case ANCHOR_ONECELL:
+ OSL_ENSURE( maSize.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid size" );
+ if( maSize.isValid() )
+ {
+ aAnchorRect.Width = ::std::min< sal_Int64 >( maSize.Width, aPageSize.Width - aAnchorRect.X );
+ aAnchorRect.Height = ::std::min< sal_Int64 >( maSize.Height, aPageSize.Height - aAnchorRect.Y );
+ }
+ break;
+ case ANCHOR_TWOCELL:
+ case ANCHOR_VML:
+ OSL_ENSURE( maTo.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" );
+ if( maTo.isValid() )
+ {
+ /* Pass a valid cell address to calcCellAnchorEmu(), otherwise
+ nothing useful is returned, even if either row or column is valid. */
+ ScAddress aToCell = rAddrConv.createValidCellAddress( BinAddress( maTo.mnCol, maTo.mnRow ), getSheetIndex(), true );
+ CellAnchorModel aValidTo = maTo;
+ aValidTo.mnCol = aToCell.Col();
+ aValidTo.mnRow = aToCell.Row();
+ EmuPoint aPoint = calcCellAnchorEmu( aValidTo );
+ // width (if column index is valid, use the calculated offset, otherwise stretch to maximum available X position)
+ aAnchorRect.Width = aPageSize.Width - aAnchorRect.X;
+ if( aToCell.Col() == maTo.mnCol )
+ aAnchorRect.Width = ::std::min< sal_Int64 >( aPoint.X - aAnchorRect.X + 1, aAnchorRect.Width );
+ // height (if row index is valid, use the calculated offset, otherwise stretch to maximum available Y position)
+ aAnchorRect.Height = aPageSize.Height - aAnchorRect.Y;
+ if( aToCell.Row() == maTo.mnRow )
+ aAnchorRect.Height = ::std::min< sal_Int64 >( aPoint.Y - aAnchorRect.Y + 1, aAnchorRect.Height );
+ }
+ break;
+ case ANCHOR_INVALID:
+ break;
+ }
+
+ return aAnchorRect;
+}
+
+css::awt::Rectangle ShapeAnchor::calcAnchorRectHmm( const css::awt::Size& rPageSizeHmm ) const
+{
+ EmuRectangle aAnchorRect = calcAnchorRectEmu( rPageSizeHmm );
+ return css::awt::Rectangle( lclEmuToHmm( aAnchorRect.X ), lclEmuToHmm( aAnchorRect.Y ), lclEmuToHmm( aAnchorRect.Width ), lclEmuToHmm( aAnchorRect.Height ) );
+}
+
+EmuPoint ShapeAnchor::calcCellAnchorEmu( const CellAnchorModel& rModel ) const
+{
+ // calculate position of top-left edge of the cell
+ css::awt::Point aPoint = getCellPosition( rModel.mnCol, rModel.mnRow );
+ EmuPoint aEmuPoint( lclHmmToEmu( aPoint.X ), lclHmmToEmu( aPoint.Y ) );
+
+ // add the offset inside the cell
+ switch( meCellAnchorType )
+ {
+ case CellAnchorType::Emu:
+ aEmuPoint.X += rModel.mnColOffset;
+ aEmuPoint.Y += rModel.mnRowOffset;
+ break;
+
+ case CellAnchorType::Pixel:
+ {
+ const UnitConverter& rUnitConv = getUnitConverter();
+ aEmuPoint.X += std::round( rUnitConv.scaleValue( rModel.mnColOffset, Unit::ScreenX, Unit::Emu ) );
+ aEmuPoint.Y += std::round( rUnitConv.scaleValue( rModel.mnRowOffset, Unit::ScreenY, Unit::Emu ) );
+ }
+ break;
+ }
+
+ return aEmuPoint;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/drawingfragment.cxx b/sc/source/filter/oox/drawingfragment.cxx
new file mode 100644
index 0000000000..2d5416b377
--- /dev/null
+++ b/sc/source/filter/oox/drawingfragment.cxx
@@ -0,0 +1,843 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawingfragment.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/document/XEventsSupplier.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <rtl/strbuf.hxx>
+#include <svx/svdobj.hxx>
+#include <drwlayer.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/drawingml/connectorshapecontext.hxx>
+#include <oox/drawingml/graphicshapecontext.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/shape/ShapeDrawingFragmentHandler.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/vml/vmlshape.hxx>
+#include <oox/vml/vmlshapecontainer.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <formulaparser.hxx>
+#include <stylesbuffer.hxx>
+#include <themebuffer.hxx>
+#include <worksheetbuffer.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+using namespace ::oox::drawingml;
+using namespace ::oox::ole;
+
+using ::com::sun::star::awt::Size;
+using ::com::sun::star::awt::Point;
+using ::com::sun::star::awt::Rectangle;
+using ::com::sun::star::awt::XControlModel;
+// no using's for ::oox::vml, that may clash with ::oox::drawingml types
+
+ShapeMacroAttacher::ShapeMacroAttacher( const OUString& rMacroName, const Reference< XShape >& rxShape ) :
+ VbaMacroAttacherBase( rMacroName ),
+ mxShape( rxShape )
+{
+}
+
+void ShapeMacroAttacher::attachMacro( const OUString& rMacroUrl )
+{
+ try
+ {
+ Reference< XEventsSupplier > xSupplier( mxShape, UNO_QUERY_THROW );
+ Reference< XNameReplace > xEvents( xSupplier->getEvents(), UNO_SET_THROW );
+ Sequence aEventProps{ comphelper::makePropertyValue("EventType", OUString( "Script" )),
+ comphelper::makePropertyValue("Script", rMacroUrl) };
+ xEvents->replaceByName( "OnClick", Any( aEventProps ) );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+Shape::Shape( const WorksheetHelper& rHelper, const AttributeList& rAttribs, const char* pcServiceName ) :
+ ::oox::drawingml::Shape( pcServiceName ),
+ WorksheetHelper( rHelper )
+{
+ OUString aMacro = rAttribs.getXString( XML_macro, OUString() );
+ if( !aMacro.isEmpty() )
+ maMacroName = getFormulaParser().importMacroName( aMacro );
+}
+
+void Shape::finalizeXShape( XmlFilterBase& rFilter, const Reference< XShapes >& rxShapes )
+{
+ OUString sURL;
+ getShapeProperties().getProperty( PROP_URL ) >>= sURL;
+ getWorksheets().convertSheetNameRef( sURL );
+ if( !maMacroName.isEmpty() && mxShape.is() )
+ {
+ VbaMacroAttacherRef xAttacher = std::make_shared<ShapeMacroAttacher>( maMacroName, mxShape );
+ getBaseFilter().getVbaProject().registerMacroAttacher( xAttacher );
+ }
+ ::oox::drawingml::Shape::finalizeXShape( rFilter, rxShapes );
+ if ( !sURL.isEmpty() )
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( mxShape );
+ if ( pObj )
+ pObj->setHyperlink(sURL);
+ }
+}
+
+GroupShapeContext::GroupShapeContext( const FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper, const ShapePtr& rxParentShape, const ShapePtr& rxShape ) :
+ ShapeGroupContext( rParent, rxParentShape, rxShape ),
+ WorksheetHelper( rHelper )
+{
+}
+
+/*static*/ ContextHandlerRef GroupShapeContext::createShapeContext( FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper, sal_Int32 nElement, const AttributeList& rAttribs,
+ const ShapePtr& rxParentShape, ShapePtr* pxShape )
+{
+ switch( nElement )
+ {
+ case XDR_TOKEN( sp ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.CustomShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new ShapeContext( rParent, rxParentShape, xShape );
+ }
+ case XDR_TOKEN( cxnSp ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.ConnectorShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new ConnectorShapeContext(rParent, rxParentShape, xShape,
+ xShape->getConnectorShapeProperties());
+ }
+ case XDR_TOKEN( pic ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.GraphicObjectShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new GraphicShapeContext( rParent, rxParentShape, xShape );
+ }
+ case XDR_TOKEN( graphicFrame ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.GraphicObjectShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new GraphicalObjectFrameContext( rParent, rxParentShape, xShape, rHelper.getSheetType() != WorksheetType::Chart );
+ }
+ case XDR_TOKEN( grpSp ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.GroupShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new GroupShapeContext( rParent, rHelper, rxParentShape, xShape );
+ }
+ }
+ return nullptr;
+}
+
+ContextHandlerRef GroupShapeContext::onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ ContextHandlerRef xContext = createShapeContext( *this, *this, nElement, rAttribs, mpGroupShapePtr );
+ return xContext ? xContext : ShapeGroupContext::onCreateContext( nElement, rAttribs );
+}
+
+DrawingFragment::DrawingFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mxDrawPage( rHelper.getDrawPage() )
+{
+ OSL_ENSURE( mxDrawPage.is(), "DrawingFragment::DrawingFragment - missing drawing page" );
+}
+
+ContextHandlerRef DrawingFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XDR_TOKEN( wsDr ) ) return this;
+ break;
+
+ case XDR_TOKEN( wsDr ):
+ switch( nElement )
+ {
+ case XDR_TOKEN( absoluteAnchor ):
+ case XDR_TOKEN( oneCellAnchor ):
+ case XDR_TOKEN( twoCellAnchor ):
+ mxAnchor.reset( new ShapeAnchor( *this ) );
+ mxAnchor->importAnchor( nElement, rAttribs );
+ return this;
+ }
+ break;
+
+ case XDR_TOKEN( absoluteAnchor ):
+ case XDR_TOKEN( oneCellAnchor ):
+ case XDR_TOKEN( twoCellAnchor ):
+ {
+ switch( nElement )
+ {
+ case XDR_TOKEN( from ):
+ case XDR_TOKEN( to ): return this;
+
+ case XDR_TOKEN( pos ): if( mxAnchor ) mxAnchor->importPos( rAttribs ); break;
+ case XDR_TOKEN( ext ): if( mxAnchor ) mxAnchor->importExt( rAttribs ); break;
+ case XDR_TOKEN( clientData ): if( mxAnchor ) mxAnchor->importClientData( rAttribs ); break;
+
+ default: return GroupShapeContext::createShapeContext( *this, *this, nElement, rAttribs, ShapePtr(), &mxShape );
+ }
+ }
+ break;
+
+ case XDR_TOKEN( from ):
+ case XDR_TOKEN( to ):
+ switch( nElement )
+ {
+ case XDR_TOKEN( col ):
+ case XDR_TOKEN( row ):
+ case XDR_TOKEN( colOff ):
+ case XDR_TOKEN( rowOff ): return this; // collect index in onCharacters()
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void DrawingFragment::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XDR_TOKEN( col ):
+ case XDR_TOKEN( row ):
+ case XDR_TOKEN( colOff ):
+ case XDR_TOKEN( rowOff ):
+ if( mxAnchor ) mxAnchor->setCellPos( getCurrentElement(), getParentElement(), rChars );
+ break;
+ }
+}
+
+void DrawingFragment::onEndElement()
+{
+ switch( getCurrentElement() )
+ {
+ case XDR_TOKEN( absoluteAnchor ):
+ case XDR_TOKEN( oneCellAnchor ):
+ case XDR_TOKEN( twoCellAnchor ):
+ if( mxDrawPage.is() && mxShape && mxAnchor )
+ {
+ EmuRectangle aShapeRectEmu = mxAnchor->calcAnchorRectEmu( getDrawPageSize() );
+ const bool bIsShapeVisible = mxAnchor->isAnchorValid();
+ if( (aShapeRectEmu.X >= 0) && (aShapeRectEmu.Y >= 0) && (aShapeRectEmu.Width >= 0) && (aShapeRectEmu.Height >= 0) )
+ {
+ const sal_Int32 aRotation = mxShape->getRotation();
+ if ((aRotation >= 45 * PER_DEGREE && aRotation < 135 * PER_DEGREE)
+ || (aRotation >= 225 * PER_DEGREE && aRotation < 315 * PER_DEGREE))
+ {
+ // When rotating any shape in MSO Excel within the range of degrees given above,
+ // Excel changes the cells in which the shape is anchored. The new position of
+ // the anchors are always calculated using a 90 degrees rotation anticlockwise.
+ // There is an important result of this operation: the top left point of the shape changes,
+ // it will be another vertex.
+ // The anchor position is given in the xml file, it is in the xdr:from and xdr:to elements.
+ // Let's see what happens in time order:
+ // We create a shape in Excel, the anchor position is in a given cell, then the rotation happens
+ // as mentioned above, and excel recalculates the cells in which the anchors are positioned.
+ // This new cell is exported into the xml elements xdr:from and xdr:to, when Excel exports the document!
+ // Thus, if we have a 90 degrees rotation and an already rotated point from which we base
+ // our calculations here in LO, the result is an incorrect 180 degrees rotation.
+ // Now, we need to create the bounding rectangle of the shape with this in mind.
+ // (Important to mention that at this point we don't talk about rotations at all, this bounding
+ // rectangle contains the original not-rotated shape. Rotation happens later in the code.)
+ // We get the new (x, y) coords, then swap width with height.
+ // To get the new coords we reflect the rectangle in the line y = x. (This will return the
+ // correct vertex, which is the actual top left one.)
+ // Another fact that appears to be true in Excel is that there are only 2 of possible anchor
+ // positions for a shape that is only rotated (and not resized for example).
+ // The first position happens in the set of degrees {[45, 135) U [225, 315)} and the second
+ // set is all the other angles. The two sets partition the circle (of all rotations: 360 degrees).
+ sal_Int64 nHalfWidth = aShapeRectEmu.Width / 2;
+ sal_Int64 nHalfHeight = aShapeRectEmu.Height / 2;
+ aShapeRectEmu.X = aShapeRectEmu.X + nHalfWidth - nHalfHeight;
+ aShapeRectEmu.Y = aShapeRectEmu.Y + nHalfHeight - nHalfWidth;
+ std::swap(aShapeRectEmu.Width, aShapeRectEmu.Height);
+ }
+
+ // TODO: DrawingML implementation expects 32-bit coordinates for EMU rectangles (change that to EmuRectangle)
+ // tdf#135918: Negative X,Y position has to be allowed to avoid shape displacement on rotation.
+ // The negative values can exist because of previous lines where the anchor rectangle must be mirrored in some ranges.
+ Rectangle aShapeRectEmu32(
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.X, SAL_MIN_INT32, SAL_MAX_INT32 ),
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.Y, SAL_MIN_INT32, SAL_MAX_INT32 ),
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.Width, 0, SAL_MAX_INT32 ),
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.Height, 0, SAL_MAX_INT32 ) );
+
+ // Make sure to set the position and size *before* calling addShape().
+ mxShape->setPosition(Point(aShapeRectEmu32.X, aShapeRectEmu32.Y));
+ mxShape->setSize(Size(aShapeRectEmu32.Width, aShapeRectEmu32.Height));
+
+ // tdf#83671. Because Excel saves a diagram with zero size in xdr:xfm, the
+ // initial diagram import produces a background shape with zero size and no
+ // diagram shapes at all. Here the size has been determined from the anchor and
+ // thus repeating the import of diagram.xml gives the diagram shapes.
+ if (mxShape->getDiagramDoms().getLength() > 0
+ && mxShape->getChildren().size() == 1
+ && mxShape->getExtDrawings().size() == 1)
+ {
+ mxShape->getChildren()[0]->setSize(mxShape->getSize());
+ OUString sFragmentPath(
+ getFragmentPathFromRelId(mxShape->getExtDrawings()[0]));
+ // Don't know why importFragment looses shape name and id. Rescue them.
+ OUString sBackupName(mxShape->getName());
+ OUString sBackupId(mxShape->getId());
+ getOoxFilter().importFragment(new oox::shape::ShapeDrawingFragmentHandler(
+ getOoxFilter(), sFragmentPath, mxShape));
+ mxShape->setName(sBackupName);
+ mxShape->setId(sBackupId);
+ }
+
+ if (mxShape->getFontRefColorForNodes().isUsed())
+ applyFontRefColor(mxShape, mxShape->getFontRefColorForNodes());
+
+ basegfx::B2DHomMatrix aTransformation;
+ if ( !bIsShapeVisible)
+ mxShape->setHidden(true);
+
+ mxShape->addShape( getOoxFilter(), &getTheme(), mxDrawPage, aTransformation, mxShape->getFillProperties() );
+
+ /* Collect all shape positions in the WorksheetHelper base
+ class. But first, scale EMUs to 1/100 mm. */
+ Rectangle aShapeRectHmm(
+ convertEmuToHmm(aShapeRectEmu32.X > 0 ? aShapeRectEmu32.X : 0), convertEmuToHmm(aShapeRectEmu32.Y > 0 ? aShapeRectEmu32.Y : 0),
+ convertEmuToHmm(aShapeRectEmu32.Width ), convertEmuToHmm(aShapeRectEmu32.Height ) );
+ extendShapeBoundingBox( aShapeRectHmm );
+ // set cell Anchoring
+ if ( mxAnchor->getEditAs() != ShapeAnchor::ANCHOR_ABSOLUTE )
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( mxShape->getXShape() );
+ if ( pObj )
+ {
+ bool bResizeWithCell = mxAnchor->getEditAs() == ShapeAnchor::ANCHOR_TWOCELL;
+ ScDrawLayer::SetCellAnchoredFromPosition( *pObj, getScDocument(), getSheetIndex(), bResizeWithCell );
+ }
+ }
+ }
+ }
+ mxShape.reset();
+ mxAnchor.reset();
+ break;
+ }
+}
+
+void DrawingFragment::applyFontRefColor(const oox::drawingml::ShapePtr& pShape,
+ const oox::drawingml::Color& rFontRefColor)
+{
+ pShape->getShapeStyleRefs()[XML_fontRef].maPhClr = rFontRefColor;
+ std::vector<oox::drawingml::ShapePtr>& vChildren = pShape->getChildren();
+ for (auto const& child : vChildren)
+ {
+ applyFontRefColor(child, rFontRefColor);
+ }
+}
+
+// VML
+
+namespace {
+
+class VmlFindNoteFunc
+{
+public:
+ explicit VmlFindNoteFunc( const ScAddress& rPos );
+ bool operator()( const ::oox::vml::ShapeBase& rShape ) const;
+
+private:
+ sal_Int32 mnCol;
+ sal_Int32 mnRow;
+};
+
+VmlFindNoteFunc::VmlFindNoteFunc( const ScAddress& rPos ) :
+ mnCol( rPos.Col() ),
+ mnRow( rPos.Row() )
+{
+}
+
+bool VmlFindNoteFunc::operator()( const ::oox::vml::ShapeBase& rShape ) const
+{
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ return pClientData && (pClientData->mnCol == mnCol) && (pClientData->mnRow == mnRow);
+}
+
+} // namespace
+
+VmlControlMacroAttacher::VmlControlMacroAttacher( const OUString& rMacroName,
+ const Reference< XIndexContainer >& rxCtrlFormIC, sal_Int32 nCtrlIndex, sal_Int32 nCtrlType, sal_Int32 nDropStyle ) :
+ VbaMacroAttacherBase( rMacroName ),
+ mxCtrlFormIC( rxCtrlFormIC ),
+ mnCtrlIndex( nCtrlIndex ),
+ mnCtrlType( nCtrlType ),
+ mnDropStyle( nDropStyle )
+{
+}
+
+void VmlControlMacroAttacher::attachMacro( const OUString& rMacroUrl )
+{
+ ScriptEventDescriptor aEventDesc;
+ aEventDesc.ScriptType = "Script";
+ aEventDesc.ScriptCode = rMacroUrl;
+
+ // editable drop downs are treated like edit boxes
+ bool bEditDropDown = (mnCtrlType == XML_Drop) && (mnDropStyle == XML_ComboEdit);
+ sal_Int32 nCtrlType = bEditDropDown ? XML_Edit : mnCtrlType;
+
+ switch( nCtrlType )
+ {
+ case XML_Button:
+ case XML_Checkbox:
+ case XML_Radio:
+ aEventDesc.ListenerType = "XActionListener";
+ aEventDesc.EventMethod = "actionPerformed";
+ break;
+ case XML_Label:
+ case XML_GBox:
+ case XML_Dialog:
+ aEventDesc.ListenerType = "XMouseListener";
+ aEventDesc.EventMethod = "mouseReleased";
+ break;
+ case XML_Edit:
+ aEventDesc.ListenerType = "XTextListener";
+ aEventDesc.EventMethod = "textChanged";
+ break;
+ case XML_Spin:
+ case XML_Scroll:
+ aEventDesc.ListenerType = "XAdjustmentListener";
+ aEventDesc.EventMethod = "adjustmentValueChanged";
+ break;
+ case XML_List:
+ case XML_Drop:
+ aEventDesc.ListenerType = "XChangeListener";
+ aEventDesc.EventMethod = "changed";
+ break;
+ default:
+ OSL_ENSURE( false, "VmlControlMacroAttacher::attachMacro - unexpected object type" );
+ return;
+ }
+
+ try
+ {
+ Reference< XEventAttacherManager > xEventMgr( mxCtrlFormIC, UNO_QUERY_THROW );
+ xEventMgr->registerScriptEvent( mnCtrlIndex, aEventDesc );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+VmlDrawing::VmlDrawing( const WorksheetHelper& rHelper ) :
+ ::oox::vml::Drawing( rHelper.getOoxFilter(), rHelper.getDrawPage(), ::oox::vml::VMLDRAWING_EXCEL ),
+ WorksheetHelper( rHelper ),
+ maControlConv( rHelper.getBaseFilter().getModel(), rHelper.getBaseFilter().getGraphicHelper() )
+{
+ // default font for legacy listboxes and dropdowns: Tahoma, 8pt
+ maListBoxFont.moName = "Tahoma";
+ maListBoxFont.moColor = "auto";
+ maListBoxFont.monSize = 160;
+}
+
+const ::oox::vml::ShapeBase* VmlDrawing::getNoteShape( const ScAddress& rPos ) const
+{
+ return getShapes().findShape( VmlFindNoteFunc( rPos ) );
+}
+
+bool VmlDrawing::isShapeSupported( const ::oox::vml::ShapeBase& rShape ) const
+{
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ return !pClientData || (pClientData->mnObjType != XML_Note);
+}
+
+OUString VmlDrawing::getShapeBaseName( const ::oox::vml::ShapeBase& rShape ) const
+{
+ if( const ::oox::vml::ClientData* pClientData = rShape.getClientData() )
+ {
+ switch( pClientData->mnObjType )
+ {
+ case XML_Button: return "Button";
+ case XML_Checkbox: return "Check Box";
+ case XML_Dialog: return "Dialog Frame";
+ case XML_Drop: return "Drop Down";
+ case XML_Edit: return "Edit Box";
+ case XML_GBox: return "Group Box";
+ case XML_Label: return "Label";
+ case XML_List: return "List Box";
+ case XML_Note: return "Comment";
+ case XML_Pict: return (pClientData->mbDde || getOleObjectInfo( rShape.getShapeId() )) ? OUString( "Object" ) : OUString( "Picture" );
+ case XML_Radio: return "Option Button";
+ case XML_Scroll: return "Scroll Bar";
+ case XML_Spin: return "Spinner";
+ }
+ }
+ return ::oox::vml::Drawing::getShapeBaseName( rShape );
+}
+
+bool VmlDrawing::convertClientAnchor( Rectangle& orShapeRect, const OUString& rShapeAnchor ) const
+{
+ if( rShapeAnchor.isEmpty() )
+ return false;
+ ShapeAnchor aAnchor( *this );
+ aAnchor.importVmlAnchor( rShapeAnchor );
+ orShapeRect = aAnchor.calcAnchorRectHmm( getDrawPageSize() );
+ return (orShapeRect.Width >= 0) && (orShapeRect.Height >= 0);
+}
+
+Reference< XShape > VmlDrawing::createAndInsertClientXShape( const ::oox::vml::ShapeBase& rShape,
+ const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
+{
+ // simulate the legacy drawing controls with OLE form controls
+ OUString aShapeName = rShape.getShapeName();
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ if( !aShapeName.isEmpty() && pClientData )
+ {
+ Rectangle aShapeRect = rShapeRect;
+ const ::oox::vml::TextBox* pTextBox = rShape.getTextBox();
+ EmbeddedControl aControl( aShapeName );
+ switch( pClientData->mnObjType )
+ {
+ case XML_Button:
+ {
+ AxCommandButtonModel& rAxModel = aControl.createModel< AxCommandButtonModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ rAxModel.mnFlags = AX_FLAGS_ENABLED | AX_FLAGS_OPAQUE | AX_FLAGS_WORDWRAP;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ }
+ break;
+
+ case XML_Label:
+ {
+ AxLabelModel& rAxModel = aControl.createModel< AxLabelModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ rAxModel.mnFlags = AX_FLAGS_ENABLED | AX_FLAGS_WORDWRAP;
+ rAxModel.mnBorderStyle = AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = AX_SPECIALEFFECT_FLAT;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ }
+ break;
+
+ case XML_Edit:
+ {
+ bool bNumeric = (pClientData->mnVTEdit == ::oox::vml::VML_CLIENTDATA_INTEGER) || (pClientData->mnVTEdit == ::oox::vml::VML_CLIENTDATA_NUMBER);
+ AxMorphDataModelBase& rAxModel = bNumeric ?
+ static_cast< AxMorphDataModelBase& >( aControl.createModel< AxNumericFieldModel >() ) :
+ static_cast< AxMorphDataModelBase& >( aControl.createModel< AxTextBoxModel >() );
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maValue, pTextBox, pClientData->mnTextHAlign );
+ setFlag( rAxModel.mnFlags, AX_FLAGS_MULTILINE, pClientData->mbMultiLine );
+ setFlag( rAxModel.mnScrollBars, AX_SCROLLBAR_VERTICAL, pClientData->mbVScroll );
+ if( pClientData->mbSecretEdit )
+ rAxModel.mnPasswordChar = '*';
+ }
+ break;
+
+ case XML_GBox:
+ {
+ AxFrameModel& rAxModel = aControl.createModel< AxFrameModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ rAxModel.mnBorderStyle = pClientData->mbNo3D ? AX_BORDERSTYLE_SINGLE : AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_BUMPED;
+
+ /* Move top border of groupbox up by half font height, because
+ Excel specifies Y position of the groupbox border line
+ instead the top border of the caption text. */
+ if( const ::oox::vml::TextFontModel* pFontModel = pTextBox ? pTextBox->getFirstFont() : nullptr )
+ {
+ sal_Int32 nFontHeightHmm = o3tl::convert( pFontModel->monSize.value_or( 160 ), o3tl::Length::twip, o3tl::Length::mm100 );
+ sal_Int32 nYDiff = ::std::min< sal_Int32 >( nFontHeightHmm / 2, aShapeRect.Y );
+ aShapeRect.Y -= nYDiff;
+ aShapeRect.Height += nYDiff;
+ }
+ }
+ break;
+
+ case XML_Checkbox:
+ {
+ AxCheckBoxModel& rAxModel = aControl.createModel< AxCheckBoxModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ convertControlBackground( rAxModel, rShape );
+ rAxModel.maValue = OUString::number( pClientData->mnChecked );
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ bool bTriState = (pClientData->mnChecked != ::oox::vml::VML_CLIENTDATA_UNCHECKED) && (pClientData->mnChecked != ::oox::vml::VML_CLIENTDATA_CHECKED);
+ rAxModel.mnMultiSelect = bTriState ? AX_SELECTION_MULTI : AX_SELECTION_SINGLE;
+ }
+ break;
+
+ case XML_Radio:
+ {
+ AxOptionButtonModel& rAxModel = aControl.createModel< AxOptionButtonModel >();
+
+ // unique name to prevent autoGrouping with ActiveX controls and which a GroupBox may override - see vmldrawing.cxx.
+ rAxModel.maGroupName = "autoGroup_formControl";
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ convertControlBackground( rAxModel, rShape );
+ rAxModel.maValue = OUString::number( pClientData->mnChecked );
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ }
+ break;
+
+ case XML_List:
+ {
+ AxListBoxModel& rAxModel = aControl.createModel< AxListBoxModel >();
+ convertControlFontData( rAxModel.maFontData, rAxModel.mnTextColor, maListBoxFont );
+ rAxModel.mnBorderStyle = pClientData->mbNo3D2 ? AX_BORDERSTYLE_SINGLE : AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D2 ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ switch( pClientData->mnSelType )
+ {
+ case XML_Single: rAxModel.mnMultiSelect = AX_SELECTION_SINGLE; break;
+ case XML_Multi: rAxModel.mnMultiSelect = AX_SELECTION_MULTI; break;
+ case XML_Extend: rAxModel.mnMultiSelect = AX_SELECTION_EXTENDED; break;
+ }
+ }
+ break;
+
+ case XML_Drop:
+ {
+ AxComboBoxModel& rAxModel = aControl.createModel< AxComboBoxModel >();
+ convertControlFontData( rAxModel.maFontData, rAxModel.mnTextColor, maListBoxFont );
+ rAxModel.mnDisplayStyle = AX_DISPLAYSTYLE_DROPDOWN;
+ rAxModel.mnShowDropButton = AX_SHOWDROPBUTTON_ALWAYS;
+ rAxModel.mnBorderStyle = pClientData->mbNo3D2 ? AX_BORDERSTYLE_SINGLE : AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D2 ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ rAxModel.mnListRows = pClientData->mnDropLines;
+ }
+ break;
+
+ case XML_Spin:
+ {
+ AxSpinButtonModel& rAxModel = aControl.createModel< AxSpinButtonModel >();
+ rAxModel.mnMin = pClientData->mnMin;
+ rAxModel.mnMax = pClientData->mnMax;
+ rAxModel.mnPosition = pClientData->mnVal;
+ rAxModel.mnSmallChange = pClientData->mnInc;
+ }
+ break;
+
+ case XML_Scroll:
+ {
+ AxScrollBarModel& rAxModel = aControl.createModel< AxScrollBarModel >();
+ rAxModel.mnMin = pClientData->mnMin;
+ rAxModel.mnMax = pClientData->mnMax;
+ rAxModel.mnPosition = pClientData->mnVal;
+ rAxModel.mnSmallChange = pClientData->mnInc;
+ rAxModel.mnLargeChange = pClientData->mnPage;
+ }
+ break;
+
+ case XML_Dialog:
+ {
+ // fake with a group box
+ AxFrameModel& rAxModel = aControl.createModel< AxFrameModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, XML_Left );
+ rAxModel.mnBorderStyle = AX_BORDERSTYLE_SINGLE;
+ rAxModel.mnSpecialEffect = AX_SPECIALEFFECT_FLAT;
+ }
+ break;
+ }
+
+ if( ControlModelBase* pAxModel = aControl.getModel() )
+ {
+ // create the control shape
+ pAxModel->maSize.first = aShapeRect.Width;
+ pAxModel->maSize.second = aShapeRect.Height;
+ sal_Int32 nCtrlIndex = -1;
+ Reference< XShape > xShape = createAndInsertXControlShape( aControl, rxShapes, aShapeRect, nCtrlIndex );
+
+ // control shape macro
+ if( xShape.is() && (nCtrlIndex >= 0) && !pClientData->maFmlaMacro.isEmpty() )
+ {
+ OUString aMacroName = getFormulaParser().importMacroName( pClientData->maFmlaMacro );
+ if( !aMacroName.isEmpty() )
+ {
+ Reference< XIndexContainer > xFormIC = getControlForm().getXForm();
+ VbaMacroAttacherRef xAttacher = std::make_shared<VmlControlMacroAttacher>( aMacroName, xFormIC, nCtrlIndex, pClientData->mnObjType, pClientData->mnDropStyle );
+ getBaseFilter().getVbaProject().registerMacroAttacher( xAttacher );
+ }
+ }
+
+ return xShape;
+ }
+ }
+
+ return Reference< XShape >();
+}
+
+void VmlDrawing::notifyXShapeInserted( const Reference< XShape >& rxShape,
+ const Rectangle& rShapeRect, const ::oox::vml::ShapeBase& rShape, bool bGroupChild )
+{
+ // collect all shape positions in the WorksheetHelper base class (but not children of group shapes)
+ if( !bGroupChild )
+ extendShapeBoundingBox( rShapeRect );
+
+ // convert settings from VML client data
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ if(!pClientData)
+ return;
+
+ // specific settings for embedded form controls
+ try
+ {
+ Reference< XControlShape > xCtrlShape( rxShape, UNO_QUERY_THROW );
+ Reference< XControlModel > xCtrlModel( xCtrlShape->getControl(), UNO_SET_THROW );
+ PropertySet aPropSet( xCtrlModel );
+
+ // printable
+ aPropSet.setProperty( PROP_Printable, pClientData->mbPrintObject );
+
+ // control source links
+ if( !pClientData->maFmlaLink.isEmpty() || !pClientData->maFmlaRange.isEmpty() )
+ maControlConv.bindToSources( xCtrlModel, pClientData->maFmlaLink, pClientData->maFmlaRange, getSheetIndex() );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt32 VmlDrawing::convertControlTextColor( std::u16string_view aTextColor ) const
+{
+ // color attribute not present or 'auto' - use passed default color
+ if( aTextColor.empty() || o3tl::equalsIgnoreAsciiCase( aTextColor, u"auto" ) )
+ return AX_SYSCOLOR_WINDOWTEXT;
+
+ if( aTextColor[ 0 ] == '#' )
+ {
+ // RGB colors in the format '#RRGGBB'
+ if( aTextColor.size() == 7 )
+ return OleHelper::encodeOleColor( o3tl::toUInt32(aTextColor.substr( 1 ), 16) );
+
+ // RGB colors in the format '#RGB'
+ if( aTextColor.size() == 4 )
+ {
+ sal_Int32 nR = o3tl::toUInt32(aTextColor.substr( 1, 1 ), 16) * 0x11;
+ sal_Int32 nG = o3tl::toUInt32(aTextColor.substr( 2, 1 ), 16) * 0x11;
+ sal_Int32 nB = o3tl::toUInt32(aTextColor.substr( 3, 1 ), 16) * 0x11;
+ return OleHelper::encodeOleColor( (nR << 16) | (nG << 8) | nB );
+ }
+
+ OSL_ENSURE( false, OStringBuffer( "VmlDrawing::convertControlTextColor - invalid color name '"
+ + OUStringToOString( aTextColor, RTL_TEXTENCODING_ASCII_US ) + "\'" ).getStr() );
+ return AX_SYSCOLOR_WINDOWTEXT;
+ }
+
+ const GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+
+ /* Predefined color names or system color names (resolve to RGB to detect
+ valid color name). */
+ sal_Int32 nColorToken = AttributeConversion::decodeToken( aTextColor );
+ ::Color nRgbValue = Color::getVmlPresetColor( nColorToken, API_RGB_TRANSPARENT );
+ if( nRgbValue == API_RGB_TRANSPARENT )
+ nRgbValue = rGraphicHelper.getSystemColor( nColorToken );
+ if( nRgbValue != API_RGB_TRANSPARENT )
+ return OleHelper::encodeOleColor( nRgbValue );
+
+ // try palette color
+ return OleHelper::encodeOleColor( rGraphicHelper.getPaletteColor( o3tl::toInt32(aTextColor) ) );
+}
+
+void VmlDrawing::convertControlFontData( AxFontData& rAxFontData, sal_uInt32& rnOleTextColor, const ::oox::vml::TextFontModel& rFontModel ) const
+{
+ if( rFontModel.moName.has_value() )
+ rAxFontData.maFontName = rFontModel.moName.value();
+
+ // font height: convert from twips to points, then to internal representation of AX controls
+ rAxFontData.setHeightPoints( static_cast< sal_Int16 >( (rFontModel.monSize.value_or( 200 ) + 10) / 20 ) );
+
+ // font effects
+ rAxFontData.mnFontEffects = AxFontFlags::NONE;
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Bold, rFontModel.mobBold.value_or( false ) );
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Italic, rFontModel.mobItalic.value_or( false ) );
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Strikeout, rFontModel.mobStrikeout.value_or( false ) );
+ sal_Int32 nUnderline = rFontModel.monUnderline.value_or( XML_none );
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Underline, nUnderline != XML_none );
+ rAxFontData.mbDblUnderline = nUnderline == XML_double;
+
+ // font color
+ rnOleTextColor = convertControlTextColor( rFontModel.moColor.value_or( OUString() ) );
+}
+
+void VmlDrawing::convertControlText( AxFontData& rAxFontData, sal_uInt32& rnOleTextColor,
+ OUString& rCaption, const ::oox::vml::TextBox* pTextBox, sal_Int32 nTextHAlign ) const
+{
+ if( pTextBox )
+ {
+ rCaption = pTextBox->getText();
+ if( const ::oox::vml::TextFontModel* pFontModel = pTextBox->getFirstFont() )
+ convertControlFontData( rAxFontData, rnOleTextColor, *pFontModel );
+ }
+
+ switch( nTextHAlign )
+ {
+ case XML_Left: rAxFontData.mnHorAlign = AxHorizontalAlign::Left; break;
+ case XML_Center: rAxFontData.mnHorAlign = AxHorizontalAlign::Center; break;
+ case XML_Right: rAxFontData.mnHorAlign = AxHorizontalAlign::Right; break;
+ default: rAxFontData.mnHorAlign = AxHorizontalAlign::Left;
+ }
+}
+
+void VmlDrawing::convertControlBackground( AxMorphDataModelBase& rAxModel, const ::oox::vml::ShapeBase& rShape ) const
+{
+ const ::oox::vml::FillModel& rFillModel = rShape.getTypeModel().maFillModel;
+ bool bHasFill = rFillModel.moFilled.value_or( true );
+ setFlag( rAxModel.mnFlags, AX_FLAGS_OPAQUE, bHasFill );
+ if( bHasFill )
+ {
+ const GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color nSysWindowColor = rGraphicHelper.getSystemColor( XML_window, API_RGB_WHITE );
+ ::oox::drawingml::Color aColor = ::oox::vml::ConversionHelper::decodeColor( rGraphicHelper, rFillModel.moColor, rFillModel.moOpacity, nSysWindowColor );
+ ::Color nRgbValue = aColor.getColor( rGraphicHelper );
+ rAxModel.mnBackColor = OleHelper::encodeOleColor( nRgbValue );
+ }
+}
+
+VmlDrawingFragment::VmlDrawingFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ ::oox::vml::DrawingFragment( rHelper.getOoxFilter(), rFragmentPath, rHelper.getVmlDrawing() ),
+ WorksheetHelper( rHelper )
+{
+}
+
+void VmlDrawingFragment::finalizeImport()
+{
+ ::oox::vml::DrawingFragment::finalizeImport();
+ getVmlDrawing().convertAndInsert();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelchartconverter.cxx b/sc/source/filter/oox/excelchartconverter.cxx
new file mode 100644
index 0000000000..bc9a0bd030
--- /dev/null
+++ b/sc/source/filter/oox/excelchartconverter.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 <excelchartconverter.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart2/data/XSheetDataProvider.hpp>
+
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/drawingml/chart/datasourcemodel.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <formulaparser.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::chart2;
+using namespace ::com::sun::star::chart2::data;
+using namespace ::com::sun::star::uno;
+
+using ::oox::drawingml::chart::DataSequenceModel;
+
+ExcelChartConverter::ExcelChartConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+ExcelChartConverter::~ExcelChartConverter()
+{
+}
+
+void ExcelChartConverter::createDataProvider( const Reference< XChartDocument >& rxChartDoc )
+{
+ try
+ {
+ Reference< XDataReceiver > xDataRec( rxChartDoc, UNO_QUERY_THROW );
+ Reference< XDataProvider > xDataProv( getBaseFilter().getModelFactory()->createInstance(
+ "com.sun.star.chart2.data.DataProvider" ), UNO_QUERY_THROW );
+ xDataRec->attachDataProvider( xDataProv );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+Reference< XDataSequence > ExcelChartConverter::createDataSequence(
+ const Reference< XDataProvider >& rxDataProvider, const DataSequenceModel& rDataSeq,
+ const OUString& /*rRole*/, const OUString& /*aRoleQualifier*/ )
+{
+ Reference< XDataSequence > xDataSeq;
+ if (!rxDataProvider.is())
+ return xDataSeq;
+
+ Reference<XSheetDataProvider> xSheetProvider(rxDataProvider, UNO_QUERY);
+ if (!xSheetProvider.is())
+ return xDataSeq;
+
+ if (!rDataSeq.maFormula.isEmpty())
+ {
+ // parse the formula string, create a token sequence
+ FormulaParser& rParser = getFormulaParser();
+ ScAddress aBaseAddr( SCCOL( 0 ), SCROW( 0 ), SCTAB( getCurrentSheetIndex() ) );
+ ApiTokenSequence aTokens = rParser.importFormula( aBaseAddr, rDataSeq.maFormula );
+
+ try
+ {
+ // create the data sequence
+ xDataSeq = xSheetProvider->createDataSequenceByFormulaTokens(aTokens);
+ }
+ catch (Exception&)
+ {
+ OSL_FAIL( "ExcelChartConverter::createDataSequence - cannot create data sequence" );
+ }
+ }
+ else if (!rDataSeq.maData.empty())
+ {
+ // create a single-row array from constant source data
+ Matrix< Any > aMatrix( rDataSeq.maData.size(), 1 );
+ Matrix< Any >::iterator aMIt = aMatrix.begin();
+ // TODO: how to handle missing values in the map?
+ for( const auto& rEntry : rDataSeq.maData )
+ {
+ *aMIt = rEntry.second;
+ ++aMIt;
+ }
+ OUString aRangeRep = FormulaProcessorBase::generateApiArray( aMatrix );
+
+ if (!aRangeRep.isEmpty())
+ {
+ try
+ {
+ // create the data sequence
+ xDataSeq = rxDataProvider->createDataSequenceByRangeRepresentation( aRangeRep );
+ }
+ catch (Exception&)
+ {
+ OSL_FAIL( "ExcelChartConverter::createDataSequence - cannot create data sequence" );
+ }
+ }
+ }
+ return xDataSeq;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelfilter.cxx b/sc/source/filter/oox/excelfilter.cxx
new file mode 100644
index 0000000000..3a2d822d14
--- /dev/null
+++ b/sc/source/filter/oox/excelfilter.cxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excelfilter.hxx>
+
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <excelvbaproject.hxx>
+#include <stylesbuffer.hxx>
+#include <themebuffer.hxx>
+#include <workbookfragment.hxx>
+#include <xestream.hxx>
+
+#include <addressconverter.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scerrors.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+using ::oox::drawingml::table::TableStyleListPtr;
+
+ExcelFilter::ExcelFilter( const Reference< XComponentContext >& rxContext ) :
+ XmlFilterBase( rxContext ),
+ mpBookGlob( nullptr )
+{
+}
+
+ExcelFilter::~ExcelFilter()
+{
+ OSL_ENSURE( !mpBookGlob, "ExcelFilter::~ExcelFilter - workbook data not cleared" );
+}
+
+void ExcelFilter::registerWorkbookGlobals( WorkbookGlobals& rBookGlob )
+{
+ mpBookGlob = &rBookGlob;
+}
+
+WorkbookGlobals& ExcelFilter::getWorkbookGlobals() const
+{
+ OSL_ENSURE( mpBookGlob, "ExcelFilter::getWorkbookGlobals - missing workbook data" );
+ return *mpBookGlob;
+}
+
+void ExcelFilter::unregisterWorkbookGlobals()
+{
+ mpBookGlob = nullptr;
+}
+
+bool ExcelFilter::importDocument()
+{
+ /* To activate the XLSX/XLSB dumper, insert the full path to the file
+ file:///<path-to-oox-module>/source/dump/xlsbdumper.ini
+ into the environment variable OOO_XLSBDUMPER and start the office with
+ this variable (nonpro only). */
+ //OOX_DUMP_FILE( ::oox::dump::xlsb::Dumper );
+
+ OUString aWorkbookPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"officeDocument" );
+ if( aWorkbookPath.isEmpty() )
+ return false;
+
+ try
+ {
+ try
+ {
+ importDocumentProperties();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "exception when importing document properties");
+ }
+ catch( ... )
+ {
+ SAL_WARN("sc", "exception when importing document properties");
+ }
+ /* Construct the WorkbookGlobals object referred to by every instance of
+ the class WorkbookHelper, and execute the import filter by constructing
+ an instance of WorkbookFragment and loading the file. */
+ WorkbookGlobalsRef xBookGlob(WorkbookHelper::constructGlobals(*this));
+ if (xBookGlob)
+ {
+ rtl::Reference<WorkbookFragment> xWorkbookFragment( new WorkbookFragment(*xBookGlob, aWorkbookPath));
+
+ ScDocument& rDoc = xWorkbookFragment->getScDocument();
+ ScDocShell* pDocSh = rDoc.GetDocumentShell();
+ assert( pDocSh );
+ pDocSh->SetInitialLinkUpdate( pDocSh->GetMedium());
+
+ bool bRet = importFragment( xWorkbookFragment);
+ if (bRet && !pDocSh->GetErrorCode())
+ {
+ const AddressConverter& rAC = xWorkbookFragment->getAddressConverter();
+ if (rAC.isTabOverflow())
+ pDocSh->SetError(SCWARN_IMPORT_SHEET_OVERFLOW);
+ else if (rAC.isColOverflow())
+ pDocSh->SetError(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ else if (rAC.isRowOverflow())
+ pDocSh->SetError(SCWARN_IMPORT_ROW_OVERFLOW);
+ }
+ return bRet;
+ }
+ }
+ catch (...)
+ {
+ }
+
+ return false;
+}
+
+bool ExcelFilter::exportDocument() noexcept
+{
+ return false;
+}
+
+const ::oox::drawingml::Theme* ExcelFilter::getCurrentTheme() const
+{
+ return &WorkbookHelper( getWorkbookGlobals() ).getTheme();
+}
+
+::oox::vml::Drawing* ExcelFilter::getVmlDrawing()
+{
+ return nullptr;
+}
+
+TableStyleListPtr ExcelFilter::getTableStyles()
+{
+ return TableStyleListPtr();
+}
+
+::oox::drawingml::chart::ChartConverter* ExcelFilter::getChartConverter()
+{
+ return WorkbookHelper( getWorkbookGlobals() ).getChartConverter();
+}
+
+void ExcelFilter::useInternalChartDataTable( bool bInternal )
+{
+ return WorkbookHelper( getWorkbookGlobals() ).useInternalChartDataTable( bInternal );
+}
+
+GraphicHelper* ExcelFilter::implCreateGraphicHelper() const
+{
+ return new ExcelGraphicHelper( getWorkbookGlobals() );
+}
+
+::oox::ole::VbaProject* ExcelFilter::implCreateVbaProject() const
+{
+ return new ExcelVbaProject( getComponentContext(), Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
+}
+
+sal_Bool SAL_CALL ExcelFilter::filter( const css::uno::Sequence< css::beans::PropertyValue >& rDescriptor )
+{
+ if ( XmlFilterBase::filter( rDescriptor ) )
+ return true;
+
+ if ( isExportFilter() )
+ {
+ bool bExportVBA = exportVBA();
+ Reference< XExporter > xExporter(
+ new XclExpXmlStream( getComponentContext(), bExportVBA, isExportTemplate() ) );
+
+ Reference< XComponent > xDocument = getModel();
+ Reference< XFilter > xFilter( xExporter, UNO_QUERY );
+
+ if ( xFilter.is() )
+ {
+ xExporter->setSourceDocument( xDocument );
+ if ( xFilter->filter( rDescriptor ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+OUString ExcelFilter::getImplementationName()
+{
+ return "com.sun.star.comp.oox.xls.ExcelFilter";
+}
+
+} // namespace oox::xls
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_oox_xls_ExcelFilter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new oox::xls::ExcelFilter(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelhandlers.cxx b/sc/source/filter/oox/excelhandlers.cxx
new file mode 100644
index 0000000000..77b5dda2ad
--- /dev/null
+++ b/sc/source/filter/oox/excelhandlers.cxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excelhandlers.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::FragmentHandler2;
+
+WorkbookFragmentBase::WorkbookFragmentBase(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ FragmentHandler2( rHelper.getOoxFilter(), rFragmentPath ),
+ WorkbookHelper( rHelper )
+{
+}
+
+// tdf142905: Change mbEnableTrimSpace default value is false,
+// because it will not trim members that do not have an attribute.
+WorksheetFragmentBase::WorksheetFragmentBase(
+ const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ FragmentHandler2( rHelper.getOoxFilter(), rFragmentPath, false ),
+ WorksheetHelper( rHelper )
+{
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelvbaproject.cxx b/sc/source/filter/oox/excelvbaproject.cxx
new file mode 100644
index 0000000000..91fd5e04b1
--- /dev/null
+++ b/sc/source/filter/oox/excelvbaproject.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 <excelvbaproject.hxx>
+
+#include <utility>
+#include <vector>
+#include <set>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+ExcelVbaProject::ExcelVbaProject( const Reference< XComponentContext >& rxContext, const Reference< XSpreadsheetDocument >& rxDocument ) :
+ ::oox::ole::VbaProject( rxContext, Reference< XModel >( rxDocument, UNO_QUERY ), u"Calc" ),
+ mxDocument( rxDocument )
+{
+}
+
+// protected ------------------------------------------------------------------
+
+namespace {
+
+struct SheetCodeNameInfo
+{
+ PropertySet maSheetProps; /// Property set of the sheet without codename.
+ OUString maPrefix; /// Prefix for the codename to be generated.
+
+ explicit SheetCodeNameInfo( PropertySet aSheetProps, OUString aPrefix ) :
+ maSheetProps(std::move( aSheetProps )), maPrefix(std::move( aPrefix )) {}
+};
+
+} // namespace
+
+void ExcelVbaProject::prepareImport()
+{
+ /* Check if the sheets have imported codenames. Generate new unused
+ codenames if not. */
+ if( !mxDocument.is() )
+ return;
+
+ try
+ {
+ // collect existing codenames (do not use them when creating new codenames)
+ ::std::set< OUString > aUsedCodeNames;
+
+ // collect sheets without codenames
+ ::std::vector< SheetCodeNameInfo > aCodeNameInfos;
+
+ // iterate over all imported sheets
+ Reference< XEnumerationAccess > xSheetsEA( mxDocument->getSheets(), UNO_QUERY_THROW );
+ Reference< XEnumeration > xSheetsEnum( xSheetsEA->createEnumeration(), UNO_SET_THROW );
+ // own try/catch for every sheet
+ while( xSheetsEnum->hasMoreElements() ) try
+ {
+ PropertySet aSheetProp( xSheetsEnum->nextElement() );
+ OUString aCodeName;
+ aSheetProp.getProperty( aCodeName, PROP_CodeName );
+ if( !aCodeName.isEmpty() )
+ {
+ aUsedCodeNames.insert( aCodeName );
+ }
+ else
+ {
+ // TODO: once we have chart sheets we need a switch/case on sheet type ('SheetNNN' vs. 'ChartNNN')
+ aCodeNameInfos.emplace_back( aSheetProp, "Sheet" );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // create new codenames if sheets do not have one
+ for (auto & codeName : aCodeNameInfos)
+ {
+ // search for an unused codename
+ sal_Int32 nCounter = 1;
+ OUString aCodeName;
+ do
+ {
+ aCodeName = codeName.maPrefix + OUString::number( nCounter++ );
+ }
+ while( aUsedCodeNames.count( aCodeName ) > 0 );
+ aUsedCodeNames.insert( aCodeName );
+
+ // set codename at sheet
+ codeName.maSheetProps.setProperty( PROP_CodeName, aCodeName );
+
+ // tell base class to create a dummy module
+ addDummyModule( aCodeName, ModuleType::DOCUMENT );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/externallinkbuffer.cxx b/sc/source/filter/oox/externallinkbuffer.cxx
new file mode 100644
index 0000000000..a812a48cff
--- /dev/null
+++ b/sc/source/filter/oox/externallinkbuffer.cxx
@@ -0,0 +1,695 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <externallinkbuffer.hxx>
+#include <externalrefmgr.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DDELinkInfo.hpp>
+#include <com/sun/star/sheet/ExternalLinkType.hpp>
+#include <com/sun/star/sheet/XDDELinks.hpp>
+#include <com/sun/star/sheet/XDDELinkResults.hpp>
+#include <com/sun/star/sheet/XExternalDocLinks.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::Relation;
+using ::oox::core::Relations;
+
+namespace {
+
+const sal_uInt16 BIFF12_EXTERNALBOOK_BOOK = 0;
+const sal_uInt16 BIFF12_EXTERNALBOOK_DDE = 1;
+const sal_uInt16 BIFF12_EXTERNALBOOK_OLE = 2;
+
+const sal_uInt16 BIFF12_EXTNAME_AUTOMATIC = 0x0002;
+const sal_uInt16 BIFF12_EXTNAME_PREFERPIC = 0x0004;
+const sal_uInt16 BIFF12_EXTNAME_STDDOCNAME = 0x0008;
+const sal_uInt16 BIFF12_EXTNAME_OLEOBJECT = 0x0010;
+const sal_uInt16 BIFF12_EXTNAME_ICONIFIED = 0x0020;
+
+} // namespace
+
+ExternalNameModel::ExternalNameModel() :
+ mbNotify( false ),
+ mbPreferPic( false ),
+ mbStdDocName( false ),
+ mbOleObj( false ),
+ mbIconified( false )
+{
+}
+
+ExternalName::ExternalName( const ExternalLink& rParentLink ) :
+ DefinedNameBase( rParentLink ),
+ mrParentLink( rParentLink ),
+ mbDdeLinkCreated( false )
+{
+}
+
+void ExternalName::importDefinedName( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importDefinedName - empty name" );
+ maModel.maFormula = rAttribs.getXString(XML_refersTo, OUString());
+ OSL_ENSURE( !maModel.maFormula.isEmpty(), "ExternalName::importDefinedName - empty formula" );
+ // zero-based index into sheet list of externalBook
+ maModel.mnSheet = rAttribs.getInteger( XML_sheetId, -1 );
+ // cache external defined names and formulas
+ ScCompiler aComp(getScDocument(), ScAddress(0, 0, maModel.mnSheet), formula::FormulaGrammar::GRAM_OOXML);
+ aComp.SetExternalLinks(getExternalLinks().getLinkInfos());
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(maModel.maFormula);
+ FormulaError nErr = pArray->GetCodeError();
+ aComp.CompileTokenArray();
+ getScDocument().CheckLinkFormulaNeedingCheck(*pArray);
+ pArray->DelRPN();
+ pArray->SetCodeError(nErr);
+
+ if (pArray->HasReferences())
+ {
+ ScExternalRefManager* pRefMgr = getScDocument().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(mrParentLink.getTargetUrl());
+ pRefMgr->storeRangeNameTokens(nFileId, maModel.maName, *pArray);
+ }
+}
+
+void ExternalName::importDdeItem( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importDdeItem - empty name" );
+ maExtNameModel.mbOleObj = false;
+ maExtNameModel.mbStdDocName = rAttribs.getBool( XML_ole, false );
+ maExtNameModel.mbNotify = rAttribs.getBool( XML_advise, false );
+ maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
+}
+
+void ExternalName::importValues( const AttributeList& rAttribs )
+{
+ setResultSize( rAttribs.getInteger( XML_cols, 1 ), rAttribs.getInteger( XML_rows, 1 ) );
+}
+
+void ExternalName::importOleItem( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importOleItem - empty name" );
+ maExtNameModel.mbOleObj = true;
+ maExtNameModel.mbNotify = rAttribs.getBool( XML_advise, false );
+ maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
+ maExtNameModel.mbIconified = rAttribs.getBool( XML_icon, false );
+}
+
+void ExternalName::importExternalName( SequenceInputStream& rStrm )
+{
+ rStrm >> maModel.maName;
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importExternalName - empty name" );
+}
+
+void ExternalName::importExternalNameFlags( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ sal_Int32 nSheetId;
+ nFlags = rStrm.readuInt16();
+ nSheetId = rStrm.readInt32();
+ // index into sheet list of EXTSHEETNAMES (one-based in BIFF12)
+ maModel.mnSheet = nSheetId - 1;
+ // no flag for built-in names, as in OOXML...
+ maExtNameModel.mbNotify = getFlag( nFlags, BIFF12_EXTNAME_AUTOMATIC );
+ maExtNameModel.mbPreferPic = getFlag( nFlags, BIFF12_EXTNAME_PREFERPIC );
+ maExtNameModel.mbStdDocName = getFlag( nFlags, BIFF12_EXTNAME_STDDOCNAME );
+ maExtNameModel.mbOleObj = getFlag( nFlags, BIFF12_EXTNAME_OLEOBJECT );
+ maExtNameModel.mbIconified = getFlag( nFlags, BIFF12_EXTNAME_ICONIFIED );
+ OSL_ENSURE( (mrParentLink.getLinkType() == ExternalLinkType::OLE) == maExtNameModel.mbOleObj,
+ "ExternalName::importExternalNameFlags - wrong OLE flag in external name" );
+}
+
+void ExternalName::importDdeItemValues( SequenceInputStream& rStrm )
+{
+ sal_Int32 nRows, nCols;
+ nRows = rStrm.readInt32();
+ nCols = rStrm.readInt32();
+ setResultSize( nCols, nRows );
+}
+
+void ExternalName::importDdeItemBool( SequenceInputStream& rStrm )
+{
+ appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 );
+}
+
+void ExternalName::importDdeItemDouble( SequenceInputStream& rStrm )
+{
+ appendResultValue( rStrm.readDouble() );
+}
+
+void ExternalName::importDdeItemError( SequenceInputStream& rStrm )
+{
+ appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) );
+}
+
+void ExternalName::importDdeItemString( SequenceInputStream& rStrm )
+{
+ appendResultValue( BiffHelper::readString( rStrm ) );
+}
+
+bool ExternalName::getDdeItemInfo( DDEItemInfo& orItemInfo ) const
+{
+ if( (mrParentLink.getLinkType() == ExternalLinkType::DDE) && !maModel.maName.isEmpty() )
+ {
+ orItemInfo.Item = maModel.maName;
+ orItemInfo.Results = ContainerHelper::matrixToSequenceSequence( maResults );
+ return true;
+ }
+ return false;
+}
+
+bool ExternalName::getDdeLinkData( OUString& orDdeServer, OUString& orDdeTopic, OUString& orDdeItem )
+{
+ if( (mrParentLink.getLinkType() == ExternalLinkType::DDE) && !maModel.maName.isEmpty() )
+ {
+ // try to create a DDE link and to set the imported link results
+ if( !mbDdeLinkCreated ) try
+ {
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ Reference< XDDELinks > xDdeLinks( aDocProps.getAnyProperty( PROP_DDELinks ), UNO_QUERY_THROW );
+ mxDdeLink = xDdeLinks->addDDELink( mrParentLink.getClassName(), mrParentLink.getTargetUrl(), maModel.maName, css::sheet::DDELinkMode_DEFAULT );
+ mbDdeLinkCreated = true; // ignore if setting results fails
+ if( !maResults.empty() )
+ {
+ Reference< XDDELinkResults > xResults( mxDdeLink, UNO_QUERY_THROW );
+ xResults->setResults( ContainerHelper::matrixToSequenceSequence( maResults ) );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ExternalName::getDdeLinkData - cannot create DDE link" );
+ }
+ // get link data from created DDE link
+ if( mxDdeLink.is() )
+ {
+ orDdeServer = mxDdeLink->getApplication();
+ orDdeTopic = mxDdeLink->getTopic();
+ orDdeItem = mxDdeLink->getItem();
+ return true;
+ }
+ }
+ return false;
+}
+
+// private --------------------------------------------------------------------
+
+void ExternalName::setResultSize( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ OSL_ENSURE( (mrParentLink.getLinkType() == ExternalLinkType::DDE) || (mrParentLink.getLinkType() == ExternalLinkType::OLE),
+ "ExternalName::setResultSize - wrong link type" );
+ OSL_ENSURE( (nRows > 0) && (nColumns > 0), "ExternalName::setResultSize - invalid matrix size" );
+ const ScAddress& rMaxPos = getAddressConverter().getMaxApiAddress();
+ if( (0 < nRows) && (nRows <= rMaxPos.Row() + 1) && (0 < nColumns) && (nColumns <= rMaxPos.Col() + 1) )
+ maResults.resize( static_cast< size_t >( nColumns ), static_cast< size_t >( nRows ), Any( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) ) );
+ else
+ maResults.clear();
+ maCurrIt = maResults.begin();
+}
+
+void LinkSheetRange::setDeleted()
+{
+ meType = LINKSHEETRANGE_INTERNAL;
+ mnDocLink = mnFirst = mnLast = -1;
+}
+
+void LinkSheetRange::setSameSheet()
+{
+ meType = LINKSHEETRANGE_SAMESHEET;
+ mnDocLink = -1;
+ mnFirst = mnLast = 0;
+}
+
+void LinkSheetRange::setRange( sal_Int32 nFirst, sal_Int32 nLast )
+{
+ meType = LINKSHEETRANGE_INTERNAL;
+ mnDocLink = -1;
+ mnFirst = ::std::min( nFirst, nLast );
+ mnLast = ::std::max( nFirst, nLast );
+}
+
+void LinkSheetRange::setExternalRange( sal_Int32 nDocLink, sal_Int32 nFirst, sal_Int32 nLast )
+{
+ if( nDocLink < 0 )
+ {
+ setDeleted();
+ }
+ else
+ {
+ meType = LINKSHEETRANGE_EXTERNAL;
+ mnDocLink = nDocLink;
+ mnFirst = ::std::min( nFirst, nLast );
+ mnLast = ::std::max( nFirst, nLast );
+ }
+}
+
+ExternalLink::ExternalLink( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ meLinkType( ExternalLinkType::Unknown ),
+ meFuncLibType( FUNCLIB_UNKNOWN )
+{
+}
+
+void ExternalLink::importExternalReference( const AttributeList& rAttribs )
+{
+ maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+}
+
+void ExternalLink::importExternalBook( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ parseExternalReference( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
+}
+
+void ExternalLink::importSheetName( const AttributeList& rAttribs )
+{
+ insertExternalSheet( rAttribs.getXString( XML_val, OUString() ) );
+}
+
+void ExternalLink::importDefinedName( const AttributeList& rAttribs )
+{
+ createExternalName()->importDefinedName( rAttribs );
+}
+
+void ExternalLink::importDdeLink( const AttributeList& rAttribs )
+{
+ OUString aDdeService = rAttribs.getXString( XML_ddeService, OUString() );
+ OUString aDdeTopic = rAttribs.getXString( XML_ddeTopic, OUString() );
+ setDdeOleTargetUrl( aDdeService, aDdeTopic, ExternalLinkType::DDE );
+}
+
+ExternalNameRef ExternalLink::importDdeItem( const AttributeList& rAttribs )
+{
+ ExternalNameRef xExtName = createExternalName();
+ xExtName->importDdeItem( rAttribs );
+ return xExtName;
+}
+
+void ExternalLink::importOleLink( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ OUString aProgId = rAttribs.getXString( XML_progId, OUString() );
+ OUString aTargetUrl = rRelations.getExternalTargetFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ setDdeOleTargetUrl( aProgId, aTargetUrl, ExternalLinkType::OLE );
+}
+
+ExternalNameRef ExternalLink::importOleItem( const AttributeList& rAttribs )
+{
+ ExternalNameRef xExtName = createExternalName();
+ xExtName->importOleItem( rAttribs );
+ return xExtName;
+}
+
+void ExternalLink::importExternalRef( SequenceInputStream& rStrm )
+{
+ rStrm >> maRelId;
+}
+
+void ExternalLink::importExternalSelf( SequenceInputStream& )
+{
+ meLinkType = ExternalLinkType::Self;
+}
+
+void ExternalLink::importExternalSame( SequenceInputStream& )
+{
+ meLinkType = ExternalLinkType::Same;
+}
+
+void ExternalLink::importExternalAddin( SequenceInputStream& )
+{
+ meLinkType = ExternalLinkType::Unknown;
+}
+
+void ExternalLink::importExternalBook( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ switch( rStrm.readuInt16() )
+ {
+ case BIFF12_EXTERNALBOOK_BOOK:
+ parseExternalReference( rRelations, BiffHelper::readString( rStrm ) );
+ break;
+ case BIFF12_EXTERNALBOOK_DDE:
+ {
+ OUString aDdeService, aDdeTopic;
+ rStrm >> aDdeService >> aDdeTopic;
+ setDdeOleTargetUrl( aDdeService, aDdeTopic, ExternalLinkType::DDE );
+ }
+ break;
+ case BIFF12_EXTERNALBOOK_OLE:
+ {
+ OUString aTargetUrl = rRelations.getExternalTargetFromRelId( BiffHelper::readString( rStrm ) );
+ OUString aProgId = BiffHelper::readString( rStrm );
+ setDdeOleTargetUrl( aProgId, aTargetUrl, ExternalLinkType::OLE );
+ }
+ break;
+ default:
+ OSL_FAIL( "ExternalLink::importExternalBook - unknown link type" );
+ }
+}
+
+void ExternalLink::importExtSheetNames( SequenceInputStream& rStrm )
+{
+ // load external sheet names and create the sheet caches in the Calc document
+ SAL_WARN_IF( (meLinkType != ExternalLinkType::External) && (meLinkType != ExternalLinkType::Library),
+ "sc.filter",
+ "Invalid link type: " << meLinkType );
+ if( meLinkType == ExternalLinkType::External ) // ignore sheets of external libraries
+ for( sal_Int32 nSheet = 0, nCount = rStrm.readInt32(); !rStrm.isEof() && (nSheet < nCount); ++nSheet )
+ insertExternalSheet( BiffHelper::readString( rStrm ) );
+}
+
+ExternalNameRef ExternalLink::importExternalName( SequenceInputStream& rStrm )
+{
+ ExternalNameRef xExtName = createExternalName();
+ xExtName->importExternalName( rStrm );
+ return xExtName;
+}
+
+ExternalLinkInfo ExternalLink::getLinkInfo() const
+{
+ ExternalLinkInfo aLinkInfo;
+ switch( meLinkType )
+ {
+ case ExternalLinkType::Self:
+ case ExternalLinkType::Same:
+ aLinkInfo.Type = css::sheet::ExternalLinkType::SELF;
+ break;
+ case ExternalLinkType::External:
+ aLinkInfo.Type = css::sheet::ExternalLinkType::DOCUMENT;
+ aLinkInfo.Data <<= maTargetUrl;
+ break;
+ case ExternalLinkType::Library:
+ // parser will return library function names in OPCODE_BAD string tokens
+ aLinkInfo.Type = css::sheet::ExternalLinkType::SPECIAL;
+ break;
+ case ExternalLinkType::DDE:
+ {
+ aLinkInfo.Type = css::sheet::ExternalLinkType::DDE;
+ DDELinkInfo aDdeLinkInfo;
+ aDdeLinkInfo.Service = maClassName;
+ aDdeLinkInfo.Topic = maTargetUrl;
+ ::std::vector< DDEItemInfo > aItemInfos;
+ DDEItemInfo aItemInfo;
+ for( const auto& rxExtName : maExtNames )
+ if( rxExtName->getDdeItemInfo( aItemInfo ) )
+ aItemInfos.push_back( aItemInfo );
+ aDdeLinkInfo.Items = comphelper::containerToSequence( aItemInfos );
+ aLinkInfo.Data <<= aDdeLinkInfo;
+ }
+ break;
+ default:
+ aLinkInfo.Type = css::sheet::ExternalLinkType::UNKNOWN;
+ }
+ return aLinkInfo;
+}
+
+FunctionLibraryType ExternalLink::getFuncLibraryType() const
+{
+ return (meLinkType == ExternalLinkType::Library) ? meFuncLibType : FUNCLIB_UNKNOWN;
+}
+
+sal_Int32 ExternalLink::getDocumentLinkIndex() const
+{
+ OSL_ENSURE( meLinkType == ExternalLinkType::External, "ExternalLink::getDocumentLinkIndex - invalid link type" );
+ return mxDocLink.is() ? mxDocLink->getTokenIndex() : -1;
+}
+
+sal_Int32 ExternalLink::getSheetCacheIndex( sal_Int32 nTabId ) const
+{
+ OSL_ENSURE( meLinkType == ExternalLinkType::External, "ExternalLink::getSheetCacheIndex - invalid link type" );
+ return ContainerHelper::getVectorElement( maSheetCaches, nTabId, -1 );
+}
+
+Reference< XExternalSheetCache > ExternalLink::getSheetCache( sal_Int32 nTabId ) const
+{
+ sal_Int32 nCacheIdx = getSheetCacheIndex( nTabId );
+ if( mxDocLink.is() && (nCacheIdx >= 0) ) try
+ {
+ // existing mxDocLink implies that this is an external link
+ Reference< XExternalSheetCache > xSheetCache( mxDocLink->getByIndex( nCacheIdx ), UNO_QUERY_THROW );
+ return xSheetCache;
+ }
+ catch( Exception& )
+ {
+ }
+ return nullptr;
+}
+
+void ExternalLink::getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const
+{
+ switch( meLinkType )
+ {
+ case ExternalLinkType::Same:
+ orSheetRange.setSameSheet();
+ break;
+
+ case ExternalLinkType::Self:
+ orSheetRange.setRange( nTabId1, nTabId2 );
+ break;
+
+ case ExternalLinkType::External:
+ {
+ sal_Int32 nDocLinkIdx = getDocumentLinkIndex();
+ // BIFF12: passed indexes point into sheet list of EXTSHEETLIST
+ orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
+ }
+ break;
+
+ default:
+ // unsupported/unexpected link type: #REF! error
+ orSheetRange.setDeleted();
+ }
+}
+
+ExternalNameRef ExternalLink::getNameByIndex( sal_Int32 nIndex ) const
+{
+ return maExtNames.get( nIndex );
+}
+
+// private --------------------------------------------------------------------
+
+void ExternalLink::setExternalTargetUrl( const OUString& rTargetUrl, const OUString& rTargetType )
+{
+ meLinkType = ExternalLinkType::Unknown;
+ if( rTargetType == CREATE_OFFICEDOC_RELATION_TYPE( "externalLinkPath" ) ||
+ rTargetType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "externalLinkPath" ) )
+ {
+ maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl );
+ if( !maTargetUrl.isEmpty() )
+ meLinkType = ExternalLinkType::External;
+ }
+ else if( rTargetType == CREATE_MSOFFICE_RELATION_TYPE( "xlExternalLinkPath/xlPathMissing" ) )
+ {
+ meLinkType = ExternalLinkType::PathMissing;
+ }
+ else if( rTargetType == CREATE_MSOFFICE_RELATION_TYPE( "xlExternalLinkPath/xlLibrary" ) )
+ {
+ meLinkType = ExternalLinkType::Library;
+ meFuncLibType = FunctionProvider::getFuncLibTypeFromLibraryName( rTargetUrl );
+ }
+ SAL_WARN_IF( meLinkType == ExternalLinkType::Unknown, "sc.filter", "Empty target URL or unknown target type, URL='" << rTargetUrl << "', type='" << rTargetType << "'" );
+
+ // create the external document link API object that will contain the sheet caches
+ if( meLinkType == ExternalLinkType::External ) try
+ {
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ Reference< XExternalDocLinks > xDocLinks( aDocProps.getAnyProperty( PROP_ExternalDocLinks ), UNO_QUERY_THROW );
+ mxDocLink = xDocLinks->addDocLink( maTargetUrl );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void ExternalLink::setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType )
+{
+ maClassName = rClassName;
+ maTargetUrl = rTargetUrl;
+ meLinkType = (maClassName.isEmpty() || maTargetUrl.isEmpty()) ? ExternalLinkType::Unknown : eLinkType;
+ OSL_ENSURE( meLinkType == eLinkType, "ExternalLink::setDdeOleTargetUrl - missing classname or target" );
+}
+
+void ExternalLink::parseExternalReference( const Relations& rRelations, const OUString& rRelId )
+{
+ if( const Relation* pRelation = rRelations.getRelationFromRelId( rRelId ) )
+ setExternalTargetUrl( pRelation->maTarget, pRelation->maType );
+}
+
+void ExternalLink::insertExternalSheet( const OUString& rSheetName )
+{
+ OSL_ENSURE( !rSheetName.isEmpty(), "ExternalLink::insertExternalSheet - empty sheet name" );
+ if( mxDocLink.is() )
+ {
+ Reference< XExternalSheetCache > xSheetCache = mxDocLink->addSheetCache( rSheetName, false );
+ sal_Int32 nCacheIdx = xSheetCache.is() ? xSheetCache->getTokenIndex() : -1;
+ maSheetCaches.push_back( nCacheIdx );
+ }
+}
+
+ExternalNameRef ExternalLink::createExternalName()
+{
+ ExternalNameRef xExtName = std::make_shared<ExternalName>( *this );
+ maExtNames.push_back( xExtName );
+ return xExtName;
+}
+
+RefSheetsModel::RefSheetsModel() :
+ mnExtRefId( -1 ),
+ mnTabId1( -1 ),
+ mnTabId2( -1 )
+{
+}
+
+void RefSheetsModel::readBiff12Data( SequenceInputStream& rStrm )
+{
+ mnExtRefId = rStrm.readInt32();
+ mnTabId1 = rStrm.readInt32();
+ mnTabId2 = rStrm.readInt32();
+}
+
+ExternalLinkBuffer::ExternalLinkBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mxSelfRef( std::make_shared<ExternalLink>( rHelper ) ),
+ mbUseRefSheets( false )
+{
+ mxSelfRef->setSelfLinkType();
+}
+
+ExternalLinkRef ExternalLinkBuffer::importExternalReference( const AttributeList& rAttribs )
+{
+ ExternalLinkRef xExtLink = createExternalLink();
+ xExtLink->importExternalReference( rAttribs );
+ maExtLinks.push_back( xExtLink );
+ return xExtLink;
+}
+
+ExternalLinkRef ExternalLinkBuffer::importExternalRef( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ ExternalLinkRef xExtLink = createExternalLink();
+ xExtLink->importExternalRef( rStrm );
+ maExtLinks.push_back( xExtLink );
+ return xExtLink;
+}
+
+void ExternalLinkBuffer::importExternalSelf( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ createExternalLink()->importExternalSelf( rStrm );
+}
+
+void ExternalLinkBuffer::importExternalSame( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ createExternalLink()->importExternalSame( rStrm );
+}
+
+void ExternalLinkBuffer::importExternalAddin( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ createExternalLink()->importExternalAddin( rStrm );
+}
+
+void ExternalLinkBuffer::importExternalSheets( SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::importExternalSheets - missing EXTERNALREFS records" );
+ mbUseRefSheets = true;
+ OSL_ENSURE( maRefSheets.empty(), "ExternalLinkBuffer::importExternalSheets - multiple EXTERNALSHEETS records" );
+ maRefSheets.clear();
+ sal_Int32 nRefCount;
+ nRefCount = rStrm.readInt32();
+ size_t nMaxCount = getLimitedValue< size_t, sal_Int64 >( nRefCount, 0, rStrm.getRemaining() / 12 );
+ maRefSheets.reserve( nMaxCount );
+ for( size_t nRefId = 0; !rStrm.isEof() && (nRefId < nMaxCount); ++nRefId )
+ {
+ RefSheetsModel aRefSheets;
+ aRefSheets.readBiff12Data( rStrm );
+ maRefSheets.push_back( aRefSheets );
+ }
+}
+
+Sequence< ExternalLinkInfo > ExternalLinkBuffer::getLinkInfos() const
+{
+ ::std::vector< ExternalLinkInfo > aLinkInfos;
+ // add entry for implicit index 0 (self reference to this document)
+ aLinkInfos.push_back( mxSelfRef->getLinkInfo() );
+ for( const auto& rxExtLink : maExtLinks )
+ aLinkInfos.push_back( rxExtLink->getLinkInfo() );
+ return comphelper::containerToSequence( aLinkInfos );
+}
+
+ExternalLinkRef ExternalLinkBuffer::getExternalLink( sal_Int32 nRefId, bool bUseRefSheets ) const
+{
+ ExternalLinkRef xExtLink;
+ // OOXML: 0 = this document, otherwise one-based index into link list
+ if( !bUseRefSheets || !mbUseRefSheets )
+ xExtLink = (nRefId == 0) ? mxSelfRef : maLinks.get( nRefId - 1 );
+ // BIFF12: zero-based index into ref-sheets list
+ else if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
+ xExtLink = maLinks.get( pRefSheets->mnExtRefId );
+ return xExtLink;
+}
+
+LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId ) const
+{
+ OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::getSheetRange - wrong BIFF version" );
+ LinkSheetRange aSheetRange;
+ if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() )
+ if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
+ pExtLink->getSheetRange( aSheetRange, pRefSheets->mnTabId1, pRefSheets->mnTabId2 );
+ return aSheetRange;
+}
+
+// private --------------------------------------------------------------------
+
+ExternalLinkRef ExternalLinkBuffer::createExternalLink()
+{
+ ExternalLinkRef xExtLink = std::make_shared<ExternalLink>( *this );
+ maLinks.push_back( xExtLink );
+ return xExtLink;
+}
+
+const RefSheetsModel* ExternalLinkBuffer::getRefSheets( sal_Int32 nRefId ) const
+{
+ return ((0 <= nRefId) && (o3tl::make_unsigned( nRefId ) < maRefSheets.size())) ?
+ &maRefSheets[ static_cast< size_t >( nRefId ) ] : nullptr;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/externallinkfragment.cxx b/sc/source/filter/oox/externallinkfragment.cxx
new file mode 100644
index 0000000000..07fbece616
--- /dev/null
+++ b/sc/source/filter/oox/externallinkfragment.cxx
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <externallinkfragment.hxx>
+
+#include <com/sun/star/sheet/XExternalSheetCache.hpp>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <osl/diagnose.h>
+#include <addressconverter.hxx>
+#include <unitconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+ExternalSheetDataContext::ExternalSheetDataContext(
+ WorkbookFragmentBase& rFragment, const Reference< XExternalSheetCache >& rxSheetCache )
+ : WorkbookContextBase(rFragment)
+ , mxSheetCache(rxSheetCache)
+ , mnCurrType(XML_TOKEN_INVALID)
+{
+ OSL_ENSURE( mxSheetCache.is(), "ExternalSheetDataContext::ExternalSheetDataContext - missing sheet cache" );
+}
+
+ContextHandlerRef ExternalSheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( sheetData ):
+ if( nElement == XLS_TOKEN( row ) ) return this;
+ break;
+ case XLS_TOKEN( row ):
+ if( nElement == XLS_TOKEN( cell ) ) { importCell( rAttribs ); return this; }
+ break;
+ case XLS_TOKEN( cell ):
+ if( nElement == XLS_TOKEN( v ) ) return this; // collect characters in onCharacters()
+ break;
+ }
+ return nullptr;
+}
+
+void ExternalSheetDataContext::onCharacters( const OUString& rChars )
+{
+ if( !isCurrentElement( XLS_TOKEN( v ) ) )
+ return;
+
+ switch( mnCurrType )
+ {
+ case XML_b:
+ case XML_n:
+ setCellValue( Any( rChars.toDouble() ) );
+ break;
+ case XML_e:
+ setCellValue( Any( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( rChars ) ) ) );
+ break;
+ case XML_str:
+ setCellValue( Any( rChars ) );
+ break;
+ }
+ mnCurrType = XML_TOKEN_INVALID;
+}
+
+ContextHandlerRef ExternalSheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_EXTSHEETDATA:
+ if( nRecId == BIFF12_ID_EXTROW ) { maCurrPos.SetRow( rStrm.readInt32() ); return this; }
+ break;
+ case BIFF12_ID_EXTROW:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTCELL_BLANK: importExtCellBlank( rStrm ); break;
+ case BIFF12_ID_EXTCELL_BOOL: importExtCellBool( rStrm ); break;
+ case BIFF12_ID_EXTCELL_DOUBLE: importExtCellDouble( rStrm ); break;
+ case BIFF12_ID_EXTCELL_ERROR: importExtCellError( rStrm ); break;
+ case BIFF12_ID_EXTCELL_STRING: importExtCellString( rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+// private --------------------------------------------------------------------
+
+void ExternalSheetDataContext::importCell( const AttributeList& rAttribs )
+{
+ if( getAddressConverter().convertToCellAddress( maCurrPos, rAttribs.getString( XML_r, OUString() ), 0, false ) )
+ mnCurrType = rAttribs.getToken( XML_t, XML_n );
+ else
+ mnCurrType = XML_TOKEN_INVALID;
+}
+
+void ExternalSheetDataContext::importExtCellBlank( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( OUString() ) );
+}
+
+void ExternalSheetDataContext::importExtCellBool( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ double fValue = (rStrm.readuInt8() == 0) ? 0.0 : 1.0;
+ setCellValue( Any( fValue ) );
+}
+
+void ExternalSheetDataContext::importExtCellDouble( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( rStrm.readDouble() ) );
+}
+
+void ExternalSheetDataContext::importExtCellError( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) ) );
+}
+
+void ExternalSheetDataContext::importExtCellString( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( BiffHelper::readString( rStrm ) ) );
+}
+
+void ExternalSheetDataContext::setCellValue( const Any& rValue )
+{
+ if( mxSheetCache.is() && getAddressConverter().checkCellAddress( maCurrPos, false ) ) try
+ {
+ mxSheetCache->setCellValue( maCurrPos.Col(), maCurrPos.Row(), rValue );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+ExternalLinkFragment::ExternalLinkFragment( const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath, ExternalLink& rExtLink ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath ),
+ mrExtLink( rExtLink ),
+ mnResultType( XML_TOKEN_INVALID )
+{
+}
+
+ContextHandlerRef ExternalLinkFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( externalLink ) ) return this;
+ break;
+
+ case XLS_TOKEN( externalLink ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( externalBook ): mrExtLink.importExternalBook( getRelations(), rAttribs ); return this;
+ case XLS_TOKEN( ddeLink ): mrExtLink.importDdeLink( rAttribs ); return this;
+ case XLS_TOKEN( oleLink ): mrExtLink.importOleLink( getRelations(), rAttribs ); return this;
+ }
+ break;
+
+ case XLS_TOKEN( externalBook ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetNames ):
+ case XLS_TOKEN( definedNames ):
+ case XLS_TOKEN( sheetDataSet ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( sheetNames ):
+ if( nElement == XLS_TOKEN( sheetName ) ) mrExtLink.importSheetName( rAttribs );
+ break;
+ case XLS_TOKEN( definedNames ):
+ if( nElement == XLS_TOKEN( definedName ) ) mrExtLink.importDefinedName( rAttribs );
+ break;
+ case XLS_TOKEN( sheetDataSet ):
+ if( (nElement == XLS_TOKEN( sheetData )) && (mrExtLink.getLinkType() == ExternalLinkType::External) )
+ return createSheetDataContext( rAttribs.getInteger( XML_sheetId, -1 ) );
+ break;
+
+ case XLS_TOKEN( ddeLink ):
+ if( nElement == XLS_TOKEN( ddeItems ) ) return this;
+ break;
+ case XLS_TOKEN( ddeItems ):
+ if( nElement == XLS_TOKEN( ddeItem ) )
+ {
+ mxExtName = mrExtLink.importDdeItem( rAttribs );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( ddeItem ):
+ if( nElement == XLS_TOKEN( values ) )
+ {
+ if( mxExtName ) mxExtName->importValues( rAttribs );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( values ):
+ if( nElement == XLS_TOKEN( value ) )
+ {
+ mnResultType = rAttribs.getToken( XML_t, XML_n );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( value ):
+ if( nElement == XLS_TOKEN( val ) ) return this; // collect value in onCharacters()
+ break;
+
+ case XLS_TOKEN( oleLink ):
+ if( nElement == XLS_TOKEN( oleItems ) ) return this;
+ break;
+ case XLS_TOKEN( oleItems ):
+ if( nElement == XLS_TOKEN( oleItem ) ) mxExtName = mrExtLink.importOleItem( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void ExternalLinkFragment::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( val ) ) )
+ maResultValue = rChars;
+}
+
+void ExternalLinkFragment::onEndElement()
+{
+ if( !(isCurrentElement( XLS_TOKEN( value ) ) && mxExtName) )
+ return;
+
+ switch( mnResultType )
+ {
+ case XML_b:
+ mxExtName->appendResultValue( maResultValue.toDouble() );
+ break;
+ case XML_e:
+ mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( maResultValue ) ) );
+ break;
+ case XML_n:
+ mxExtName->appendResultValue( maResultValue.toDouble() );
+ break;
+ case XML_str:
+ mxExtName->appendResultValue( maResultValue );
+ break;
+ default:
+ mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) );
+ }
+}
+
+ContextHandlerRef ExternalLinkFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_EXTERNALBOOK )
+ {
+ mrExtLink.importExternalBook( getRelations(), rStrm );
+ return this;
+ }
+ break;
+
+ case BIFF12_ID_EXTERNALBOOK:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTSHEETDATA:
+ if( mrExtLink.getLinkType() == ExternalLinkType::External )
+ return createSheetDataContext( rStrm.readInt32() );
+ break;
+
+ case BIFF12_ID_EXTSHEETNAMES: mrExtLink.importExtSheetNames( rStrm ); break;
+ case BIFF12_ID_EXTERNALNAME: mxExtName = mrExtLink.importExternalName( rStrm ); return this;
+ }
+ break;
+
+ case BIFF12_ID_EXTERNALNAME:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTERNALNAMEFLAGS: if( mxExtName ) mxExtName->importExternalNameFlags( rStrm ); break;
+ case BIFF12_ID_DDEITEMVALUES: if( mxExtName ) mxExtName->importDdeItemValues( rStrm ); return this;
+ }
+ break;
+
+ case BIFF12_ID_DDEITEMVALUES:
+ switch( nRecId )
+ {
+ case BIFF12_ID_DDEITEM_BOOL: if( mxExtName ) mxExtName->importDdeItemBool( rStrm ); break;
+ case BIFF12_ID_DDEITEM_DOUBLE: if( mxExtName ) mxExtName->importDdeItemDouble( rStrm ); break;
+ case BIFF12_ID_DDEITEM_ERROR: if( mxExtName ) mxExtName->importDdeItemError( rStrm ); break;
+ case BIFF12_ID_DDEITEM_STRING: if( mxExtName ) mxExtName->importDdeItemString( rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef ExternalLinkFragment::createSheetDataContext( sal_Int32 nSheetId )
+{
+ return new ExternalSheetDataContext( *this, mrExtLink.getSheetCache( nSheetId ) );
+}
+
+const RecordInfo* ExternalLinkFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_DDEITEMVALUES, BIFF12_ID_DDEITEMVALUES + 1 },
+ { BIFF12_ID_EXTERNALBOOK, BIFF12_ID_EXTERNALBOOK + 228 },
+ { BIFF12_ID_EXTERNALNAME, BIFF12_ID_EXTERNALNAME + 10 },
+ { BIFF12_ID_EXTROW, -1 },
+ { BIFF12_ID_EXTSHEETDATA, BIFF12_ID_EXTSHEETDATA + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/extlstcontext.cxx b/sc/source/filter/oox/extlstcontext.cxx
new file mode 100644
index 0000000000..58e4c79317
--- /dev/null
+++ b/sc/source/filter/oox/extlstcontext.cxx
@@ -0,0 +1,484 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <extlstcontext.hxx>
+#include <worksheethelper.hxx>
+#include <oox/core/contexthandler.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <colorscale.hxx>
+#include <condformatbuffer.hxx>
+#include <condformatcontext.hxx>
+#include <document.hxx>
+#include <worksheetfragment.hxx>
+#include <workbookfragment.hxx>
+#include <stylesbuffer.hxx>
+#include <stylesfragment.hxx>
+#include <SparklineFragment.hxx>
+
+#include <rangeutl.hxx>
+#include <sal/log.hxx>
+
+using ::oox::core::ContextHandlerRef;
+using ::oox::xls::CondFormatBuffer;
+
+sal_Int32 rStyleIdx = 0;
+
+namespace oox::xls {
+
+ExtCfRuleContext::ExtCfRuleContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pTarget ):
+ WorksheetContextBase( rFragment ),
+ mpTarget( pTarget ),
+ mbFirstEntry(true)
+{
+}
+
+ContextHandlerRef ExtCfRuleContext::onCreateContext( sal_Int32 , const AttributeList& )
+{
+ return this;
+}
+
+void ExtCfRuleContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( dataBar ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importDataBar( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( fillColor ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importPositiveFillColor( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( negativeFillColor ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importNegativeFillColor( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( axisColor ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importAxisColor( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( cfvo ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importCfvo( rAttribs );
+ xRule->getModel().mbIsLower = mbFirstEntry;
+ mbFirstEntry = false;
+ mpRule = xRule;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void ExtCfRuleContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XM_TOKEN( f ):
+ {
+ if (mpRule)
+ {
+ mpRule->getModel().msScaleTypeValue = rChars;
+ }
+ }
+ break;
+ }
+}
+
+void ExtCfRuleContext::onEndElement()
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( cfvo ):
+ {
+ mpRule.reset();
+ break;
+ }
+ }
+}
+
+namespace {
+ bool IsSpecificTextCondMode(ScConditionMode eMode)
+ {
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+}
+
+ExtConditionalFormattingContext::ExtConditionalFormattingContext(WorksheetContextBase& rFragment)
+ : WorksheetContextBase(rFragment)
+ , nFormulaCount(0)
+ , nPriority(-1)
+ , eOperator(ScConditionMode::NONE)
+ , isPreviousElementF(false)
+{
+}
+
+ContextHandlerRef ExtConditionalFormattingContext::onCreateContext(sal_Int32 nElement, const AttributeList& rAttribs)
+{
+ if (mpCurrentRule)
+ {
+ ScFormatEntry& rFormat = **maEntries.rbegin();
+ assert(rFormat.GetType() == ScFormatEntry::Type::Iconset);
+ ScIconSetFormat& rIconSet = static_cast<ScIconSetFormat&>(rFormat);
+ ScDocument& rDoc = getScDocument();
+ SCTAB nTab = getSheetIndex();
+ ScAddress aPos(0, 0, nTab);
+ mpCurrentRule->SetData(&rIconSet, &rDoc, aPos);
+ mpCurrentRule.reset();
+ }
+ if (nElement == XLS14_TOKEN(cfRule))
+ {
+ OUString aType = rAttribs.getString(XML_type, OUString());
+ OUString aId = rAttribs.getString(XML_id, OUString());
+ nPriority = rAttribs.getInteger( XML_priority, -1 );
+ maPriorities.push_back(nPriority);
+ maModel.nPriority = nPriority;
+
+ if (aType == "dataBar")
+ {
+ // an ext entry does not need to have an existing corresponding entry
+ ScDataBarFormatData* pInfo;
+ ExtLst::const_iterator aExt = getExtLst().find( aId );
+ if (aExt == getExtLst().end())
+ {
+ pInfo = new ScDataBarFormatData();
+ if (pInfo)
+ {
+ auto pFormat = std::make_unique<ScDataBarFormat>(&getScDocument());
+ pFormat->SetDataBarData(pInfo);
+ getCondFormats().importExtFormatEntries().push_back(std::move(pFormat));
+ }
+ }
+ else
+ {
+ pInfo = aExt->second;
+ }
+
+ if (!pInfo)
+ {
+ return nullptr;
+ }
+ return new ExtCfRuleContext( *this, pInfo );
+ }
+ else if (aType == "iconSet")
+ {
+ ScDocument& rDoc = getScDocument();
+ mpCurrentRule.reset(new IconSetRule(*this));
+ maEntries.push_back(std::make_unique<ScIconSetFormat>(&rDoc));
+ return new IconSetContext(*this, mpCurrentRule.get());
+ }
+ else if (aType == "cellIs")
+ {
+ sal_Int32 aToken = rAttribs.getToken( XML_operator, XML_TOKEN_INVALID );
+ eOperator = CondFormatBuffer::convertToInternalOperator(aToken);
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "containsText")
+ {
+ eOperator = ScConditionMode::ContainsText;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "notContainsText")
+ {
+ eOperator = ScConditionMode::NotContainsText;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "beginsWith")
+ {
+ eOperator = ScConditionMode::BeginsWith;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "endsWith")
+ {
+ eOperator = ScConditionMode::EndsWith;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "expression")
+ {
+ eOperator = ScConditionMode::Direct;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else
+ {
+ SAL_WARN("sc", "unhandled XLS14_TOKEN(cfRule) with type: " << aType);
+ }
+ }
+ else if (nElement == XLS14_TOKEN( dxf ))
+ {
+ return new DxfContext( *this, getStyles().createExtDxf() );
+ }
+ else if (nElement == XM_TOKEN( sqref ) || nElement == XM_TOKEN( f ))
+ {
+ if(nElement == XM_TOKEN( f ))
+ nFormulaCount++;
+ return this;
+ }
+
+ return nullptr;
+}
+
+void ExtConditionalFormattingContext::onStartElement(const AttributeList& /*Attribs*/)
+{
+}
+
+void ExtConditionalFormattingContext::onCharacters(const OUString& rCharacters)
+{
+ switch (getCurrentElement())
+ {
+ case XM_TOKEN(f):
+ {
+ aChars = rCharacters;
+ isPreviousElementF = true;
+ }
+ break;
+ case XM_TOKEN(sqref):
+ {
+ aChars = rCharacters;
+ }
+ break;
+ }
+
+}
+
+void ExtConditionalFormattingContext::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XM_TOKEN(f):
+ {
+ if(!IsSpecificTextCondMode(eOperator) || nFormulaCount == 2)
+ maModel.aFormula = aChars;
+ }
+ break;
+ case XLS14_TOKEN( cfRule ):
+ {
+ if (IsSpecificTextCondMode(maModel.eOperator) && nFormulaCount == 1)
+ {
+ maModel.aFormula = aChars;
+ maModel.eOperator = ScConditionMode::Direct;
+ }
+
+ getStyles().getExtDxfs().forEachMem( &Dxf::finalizeImport );
+ maModel.aStyle = getStyles().createExtDxfStyle(rStyleIdx);
+ rStyleIdx++;
+ nFormulaCount = 0;
+ maModels.push_back(maModel);
+ }
+ break;
+ case XM_TOKEN(sqref):
+ {
+ ScRangeList aRange;
+ ScDocument& rDoc = getScDocument();
+ bool bSuccess = ScRangeStringConverter::GetRangeListFromString(aRange, aChars, rDoc, formula::FormulaGrammar::CONV_XL_OOX);
+ if (!bSuccess || aRange.empty())
+ break;
+
+ SCTAB nTab = getSheetIndex();
+ for (size_t i = 0; i < aRange.size(); ++i)
+ {
+ aRange[i].aStart.SetTab(nTab);
+ aRange[i].aEnd.SetTab(nTab);
+ }
+
+ if (maModels.size() > 1)
+ {
+ std::sort(maModels.begin(), maModels.end(),
+ [](const ExtCondFormatRuleModel& lhs, const ExtCondFormatRuleModel& rhs) {
+ return lhs.nPriority < rhs.nPriority;
+ });
+ }
+
+ if (isPreviousElementF) // sqref can be alone in some cases.
+ {
+ for (size_t i = 0; i < maModels.size(); ++i)
+ {
+ ScAddress rPos = aRange.GetTopLeftCorner();
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(maModels[i].eOperator, maModels[i].aFormula, "", rDoc,
+ rPos, maModels[i].aStyle, "", "",
+ formula::FormulaGrammar::GRAM_OOXML ,
+ formula::FormulaGrammar::GRAM_OOXML,
+ ScFormatEntry::Type::ExtCondition );
+ maEntries.push_back(std::unique_ptr<ScFormatEntry>(pEntry));
+ }
+
+ assert(maPriorities.size() >= maModels.size());
+ maModels.clear();
+ }
+
+ std::vector< std::unique_ptr<ExtCfCondFormat> >& rExtFormats = getCondFormats().importExtCondFormat();
+ rExtFormats.push_back(std::make_unique<ExtCfCondFormat>(aRange, maEntries, &maPriorities));
+
+ maPriorities.clear();
+ isPreviousElementF = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+ExtLstLocalContext::ExtLstLocalContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pTarget ):
+ WorksheetContextBase(rFragment),
+ mpTarget(pTarget)
+{
+}
+
+ContextHandlerRef ExtLstLocalContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( extLst ):
+ if(nElement == XLS_TOKEN( ext ))
+ return this;
+ else
+ return nullptr;
+ case XLS_TOKEN( ext ):
+ if (nElement == XLS14_TOKEN( id ))
+ return this;
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void ExtLstLocalContext::onStartElement( const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( id ):
+ break;
+ }
+}
+
+void ExtLstLocalContext::onCharacters( const OUString& rChars )
+{
+ if (getCurrentElement() == XLS14_TOKEN( id ))
+ {
+ getExtLst().insert( std::pair< OUString, ScDataBarFormatData*>(rChars, mpTarget) );
+ }
+}
+
+ExtGlobalContext::ExtGlobalContext( WorksheetContextBase& rFragment ):
+ WorksheetContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtGlobalContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch (nElement)
+ {
+ case XLS14_TOKEN(conditionalFormatting): return new ExtConditionalFormattingContext(*this);
+ case XLS14_TOKEN(dataValidations): return new ExtDataValidationsContext(*this);
+ case XLS14_TOKEN(sparklineGroups): return new SparklineGroupsContext(*this);
+ }
+ return this;
+}
+
+void ExtGlobalContext::onStartElement( const AttributeList& /*rAttribs*/ )
+{
+}
+
+ExtLstGlobalContext::ExtLstGlobalContext( WorksheetFragment& rFragment ):
+ WorksheetContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtLstGlobalContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ if (nElement == XLS_TOKEN( ext ))
+ return new ExtGlobalContext( *this );
+
+ return this;
+}
+
+ExtGlobalWorkbookContext::ExtGlobalWorkbookContext( WorkbookContextBase& rFragment ):
+ WorkbookContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtGlobalWorkbookContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if (nElement == LOEXT_TOKEN(extCalcPr))
+ {
+ ScDocument& rDoc = getScDocument();
+ sal_Int32 nToken = rAttribs.getToken( XML_stringRefSyntax, XML_CalcA1 );
+ ScCalcConfig aCalcConfig = rDoc.GetCalcConfig();
+
+ switch( nToken )
+ {
+ case XML_CalcA1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO );
+ break;
+ case XML_ExcelA1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 );
+ break;
+ case XML_ExcelR1C1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
+ break;
+ case XML_CalcA1ExcelA1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_A1_XL_A1 );
+ break;
+ default:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_UNSPECIFIED );
+ break;
+ }
+ rDoc.SetCalcConfig(aCalcConfig);
+ }
+
+ return this;
+}
+
+void ExtGlobalWorkbookContext::onStartElement( const AttributeList& /*rAttribs*/ )
+{
+}
+
+ExtLstGlobalWorkbookContext::ExtLstGlobalWorkbookContext( WorkbookFragment& rFragment ):
+ WorkbookContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtLstGlobalWorkbookContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ if (nElement == XLS_TOKEN( ext ))
+ return new ExtGlobalWorkbookContext( *this );
+
+ return this;
+}
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx
new file mode 100644
index 0000000000..7603d0f178
--- /dev/null
+++ b/sc/source/filter/oox/formulabase.cxx
@@ -0,0 +1,1707 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formulabase.hxx>
+#include <rangelst.hxx>
+#include <addressconverter.hxx>
+
+#include <map>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sheet/AddressConvention.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/SingleReference.hpp>
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+#include <com/sun/star/sheet/FormulaMapGroup.hpp>
+#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
+#include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp>
+#include <com/sun/star/sheet/XFormulaParser.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/properties.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace {
+
+enum class FuncFlags : sal_uInt16 {
+ NONE = 0x0000,
+ VOLATILE = 0x0001, /// Result is volatile (e.g. NOW() function).
+ IMPORTONLY = 0x0002, /// Only used in import filter.
+ EXPORTONLY = 0x0004, /// Only used in export filter.
+ MACROCALL = 0x0008, /// Function is stored as macro call in BIFF Excel (_xlfn. prefix). OOXML name MUST exist.
+ MACROCALLODF = 0x0010, /// ODF-only function stored as macro call in BIFF Excel (_xlfnodf. prefix). ODF name MUST exist.
+ EXTERNAL = 0x0020, /// Function is external in Calc.
+ MACROFUNC = 0x0040, /// Function is a macro-sheet function.
+ MACROCMD = 0x0080, /// Function is a macro-sheet command.
+ ALWAYSVAR = 0x0100, /// Function is always represented by a tFuncVar token.
+ PARAMPAIRS = 0x0200, /// Optional parameters are expected to appear in pairs.
+ MACROCALL_FN = 0x0400, /** Function is stored as macro call in Excel (_xlfn. prefix)
+ for OOXML. OOXML name MUST exist. Do not use without FuncFlags::MACROCALL. */
+ MACROCALL_NEW = MACROCALL | MACROCALL_FN, /** New Excel functions not
+ defined in OOXML, _xlfn. prefix in all formats. OOXML name
+ must exist. */
+ BIFFEXPORTONLY = 0x1000, /// Only used in BIFF binary export filter.
+ INTERNAL = 0x2000, /// Function is internal in Calc.
+ EUROTOOL = 0x4000, /// function of euro tool lib, FUNCLIB_EUROTOOL
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<FuncFlags> : is_typed_flags<FuncFlags, 0x77ff> {};
+}
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+// reference helpers ==========================================================
+
+BinSingleRef2d::BinSingleRef2d() :
+ mnCol( 0 ),
+ mnRow( 0 ),
+ mbColRel( false ),
+ mbRowRel( false )
+{
+}
+
+void BinSingleRef2d::setBiff12Data( sal_uInt16 nCol, sal_Int32 nRow, bool bRelativeAsOffset )
+{
+ mnCol = nCol & BIFF12_TOK_REF_COLMASK;
+ mnRow = nRow & BIFF12_TOK_REF_ROWMASK;
+ mbColRel = getFlag( nCol, BIFF12_TOK_REF_COLREL );
+ mbRowRel = getFlag( nCol, BIFF12_TOK_REF_ROWREL );
+ if( bRelativeAsOffset && mbColRel && (mnCol > (BIFF12_TOK_REF_COLMASK >> 1)) )
+ mnCol -= (BIFF12_TOK_REF_COLMASK + 1);
+ if( bRelativeAsOffset && mbRowRel && (mnRow > (BIFF12_TOK_REF_ROWMASK >> 1)) )
+ mnRow -= (BIFF12_TOK_REF_ROWMASK + 1);
+}
+
+void BinSingleRef2d::readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset )
+{
+ sal_Int32 nRow;
+ sal_uInt16 nCol;
+ nRow = rStrm.readInt32();
+ nCol = rStrm.readuInt16();
+ setBiff12Data( nCol, nRow, bRelativeAsOffset );
+}
+
+void BinComplexRef2d::readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset )
+{
+ sal_Int32 nRow1, nRow2;
+ sal_uInt16 nCol1, nCol2;
+ nRow1 = rStrm.readInt32();
+ nRow2 = rStrm.readInt32();
+ nCol1 = rStrm.readuInt16();
+ nCol2 = rStrm.readuInt16();
+ maRef1.setBiff12Data( nCol1, nRow1, bRelativeAsOffset );
+ maRef2.setBiff12Data( nCol2, nRow2, bRelativeAsOffset );
+}
+
+// token vector, sequence =====================================================
+
+ApiTokenVector::ApiTokenVector()
+{
+}
+
+Any& ApiTokenVector::append( sal_Int32 nOpCode )
+{
+ mvTokens.emplace_back();
+ mvTokens.back().OpCode = nOpCode;
+ return mvTokens.back().Data;
+}
+
+ApiTokenSequence ApiTokenVector::toSequence() const
+{
+ return comphelper::containerToSequence( mvTokens );
+}
+
+// token sequence iterator ====================================================
+
+ApiTokenIterator::ApiTokenIterator( const ApiTokenSequence& rTokens, sal_Int32 nSpacesOpCode ) :
+ mpToken( rTokens.getConstArray() ),
+ mpTokenEnd( rTokens.getConstArray() + rTokens.getLength() ),
+ mnSpacesOpCode( nSpacesOpCode )
+{
+ skipSpaces();
+}
+
+ApiTokenIterator& ApiTokenIterator::operator++()
+{
+ if( is() )
+ {
+ ++mpToken;
+ skipSpaces();
+ }
+ return *this;
+}
+
+void ApiTokenIterator::skipSpaces()
+{
+ while( is() && (mpToken->OpCode == mnSpacesOpCode) )
+ ++mpToken;
+}
+
+// function data ==============================================================
+
+namespace {
+
+const size_t FUNCINFO_PARAMINFOCOUNT = 5; /// Number of parameter type entries.
+
+typedef std::shared_ptr< FunctionInfo > FunctionInfoRef;
+
+struct FunctionData
+{
+ const char* mpcOdfFuncName; /// ODF function name.
+ const char* mpcOoxFuncName; /// OOXML function name.
+ sal_uInt16 mnBiff12FuncId; /// BIFF12 function identifier.
+ sal_uInt16 mnBiffFuncId; /// BIFF2-BIFF8 function identifier.
+ sal_uInt8 mnMinParamCount; /// Minimum number of parameters.
+ sal_uInt8 mnMaxParamCount; /// Maximum number of parameters.
+ sal_uInt8 mnRetClass; /// BIFF token class of the return value.
+ FunctionParamInfo mpParamInfos[ FUNCINFO_PARAMINFOCOUNT ]; /// Information about all parameters.
+ FuncFlags mnFlags; /// Additional flags.
+
+ bool isSupported(bool bImportFilter) const;
+};
+
+bool FunctionData::isSupported(bool bImportFilter) const
+{
+ /* For import filters: the FuncFlags::EXPORTONLY, FuncFlags::BIFFEXPORTONLY
+ must not be set.
+ For export filters: the FuncFlags::IMPORTONLY, FuncFlags::BIFFEXPORTONLY
+ must not be set. */
+ if (bImportFilter)
+ return !(mnFlags & ( FuncFlags::EXPORTONLY | FuncFlags::BIFFEXPORTONLY));
+ else
+ return !(mnFlags & ( FuncFlags::IMPORTONLY | FuncFlags::BIFFEXPORTONLY));
+}
+
+const sal_uInt16 NOID = SAL_MAX_UINT16; /// No BIFF function identifier available.
+const sal_uInt8 MX = SAL_MAX_UINT8; /// Maximum parameter count.
+
+// abbreviations for function return token class
+const sal_uInt8 R = BIFF_TOKCLASS_REF;
+const sal_uInt8 V = BIFF_TOKCLASS_VAL;
+const sal_uInt8 A = BIFF_TOKCLASS_ARR;
+
+// abbreviations for parameter infos
+#define RO { FuncParamValidity::Regular }
+#define RA { FuncParamValidity::Regular }
+#define RR { FuncParamValidity::Regular }
+#define RX { FuncParamValidity::Regular }
+#define VO { FuncParamValidity::Regular }
+#define VV { FuncParamValidity::Regular }
+#define VA { FuncParamValidity::Regular }
+#define VR { FuncParamValidity::Regular }
+#define VX { FuncParamValidity::Regular }
+#define RO_E { FuncParamValidity::ExcelOnly }
+#define VR_E { FuncParamValidity::ExcelOnly }
+#define C { FuncParamValidity::CalcOnly }
+
+// Note: parameter types of all macro sheet functions (FuncFlags::MACROFUNC/FuncFlags::MACROCMD) untested!
+
+/** Functions new in BIFF2. */
+const FunctionData saFuncTableBiff2[] =
+{
+ { "COUNT", "COUNT", 0, 0, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "IF", "IF", 1, 1, 2, 3, R, { VO, RO }, FuncFlags::NONE },
+ { "ISNA", "ISNA", 2, 2, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISERROR", "ISERROR", 3, 3, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SUM", "SUM", 4, 4, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "AVERAGE", "AVERAGE", 5, 5, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MIN", "MIN", 6, 6, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MAX", "MAX", 7, 7, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "ROW", "ROW", 8, 8, 0, 1, V, { RO }, FuncFlags::NONE },
+ { "COLUMN", "COLUMN", 9, 9, 0, 1, V, { RO }, FuncFlags::NONE },
+ { "NA", "NA", 10, 10, 0, 0, V, {}, FuncFlags::NONE },
+ { "NPV", "NPV", 11, 11, 2, MX, V, { VR, RX }, FuncFlags::NONE },
+ { "STDEV", "STDEV", 12, 12, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "DOLLAR", "DOLLAR", 13, 13, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "FIXED", "FIXED", 14, 14, 1, 2, V, { VR, VR, C }, FuncFlags::NONE },
+ { "SIN", "SIN", 15, 15, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "CSC", "SIN", 15, 15, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "COS", "COS", 16, 16, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SEC", "COS", 16, 16, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "TAN", "TAN", 17, 17, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "COT", "TAN", 17, 17, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "ATAN", "ATAN", 18, 18, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOT", "ATAN", 18, 18, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "PI", "PI", 19, 19, 0, 0, V, {}, FuncFlags::NONE },
+ { "SQRT", "SQRT", 20, 20, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "EXP", "EXP", 21, 21, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LN", "LN", 22, 22, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LOG10", "LOG10", 23, 23, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ABS", "ABS", 24, 24, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "INT", "INT", 25, 25, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SIGN", "SIGN", 26, 26, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ROUND", "ROUND", 27, 27, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "LOOKUP", "LOOKUP", 28, 28, 2, 3, V, { VR, RA }, FuncFlags::NONE },
+ { "INDEX", "INDEX", 29, 29, 2, 4, R, { RA, VV }, FuncFlags::NONE },
+ { "REPT", "REPT", 30, 30, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "MID", "MID", 31, 31, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEN", "LEN", 32, 32, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "VALUE", "VALUE", 33, 33, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "TRUE", "TRUE", 34, 34, 0, 0, V, {}, FuncFlags::NONE },
+ { "FALSE", "FALSE", 35, 35, 0, 0, V, {}, FuncFlags::NONE },
+ { "AND", "AND", 36, 36, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "OR", "OR", 37, 37, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "NOT", "NOT", 38, 38, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MOD", "MOD", 39, 39, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "DCOUNT", "DCOUNT", 40, 40, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DSUM", "DSUM", 41, 41, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DAVERAGE", "DAVERAGE", 42, 42, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DMIN", "DMIN", 43, 43, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DMAX", "DMAX", 44, 44, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DSTDEV", "DSTDEV", 45, 45, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "VAR", "VAR", 46, 46, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "DVAR", "DVAR", 47, 47, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "TEXT", "TEXT", 48, 48, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "LINEST", "LINEST", 49, 49, 1, 2, A, { RA, RA, C, C }, FuncFlags::NONE },
+ { "TREND", "TREND", 50, 50, 1, 3, A, { RA, RA, RA, C }, FuncFlags::NONE },
+ { "LOGEST", "LOGEST", 51, 51, 1, 2, A, { RA, RA, C, C }, FuncFlags::NONE },
+ { "GROWTH", "GROWTH", 52, 52, 1, 3, A, { RA, RA, RA, C }, FuncFlags::NONE },
+ { "PV", "PV", 56, 56, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "FV", "FV", 57, 57, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "NPER", "NPER", 58, 58, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "PMT", "PMT", 59, 59, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "RATE", "RATE", 60, 60, 3, 6, V, { VR }, FuncFlags::NONE },
+ { "MIRR", "MIRR", 61, 61, 3, 3, V, { RA, VR }, FuncFlags::NONE },
+ { "IRR", "IRR", 62, 62, 1, 2, V, { RA, VR }, FuncFlags::NONE },
+ { "RAND", "RAND", 63, 63, 0, 0, V, {}, FuncFlags::VOLATILE },
+ { "MATCH", "MATCH", 64, 64, 2, 3, V, { VR, RX, RR }, FuncFlags::NONE },
+ { "DATE", "DATE", 65, 65, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "TIME", "TIME", 66, 66, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "DAY", "DAY", 67, 67, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MONTH", "MONTH", 68, 68, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "YEAR", "YEAR", 69, 69, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "WEEKDAY", "WEEKDAY", 70, 70, 1, 1, V, { VR, C }, FuncFlags::NONE },
+ { "HOUR", "HOUR", 71, 71, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MINUTE", "MINUTE", 72, 72, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SECOND", "SECOND", 73, 73, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "NOW", "NOW", 74, 74, 0, 0, V, {}, FuncFlags::VOLATILE },
+ { "AREAS", "AREAS", 75, 75, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "ROWS", "ROWS", 76, 76, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "COLUMNS", "COLUMNS", 77, 77, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "OFFSET", "OFFSET", 78, 78, 3, 5, R, { RO, VR }, FuncFlags::VOLATILE },
+ { "SEARCH", "SEARCH", 82, 82, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "TRANSPOSE", "TRANSPOSE", 83, 83, 1, 1, A, { VO }, FuncFlags::NONE },
+ { "TYPE", "TYPE", 86, 86, 1, 1, V, { VX }, FuncFlags::NONE },
+ { "ATAN2", "ATAN2", 97, 97, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "ASIN", "ASIN", 98, 98, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOS", "ACOS", 99, 99, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "CHOOSE", "CHOOSE", 100, 100, 2, MX, R, { VO, RO }, FuncFlags::NONE },
+ { "HLOOKUP", "HLOOKUP", 101, 101, 3, 3, V, { VV, RO, RO, C }, FuncFlags::NONE },
+ { "VLOOKUP", "VLOOKUP", 102, 102, 3, 3, V, { VV, RO, RO, C }, FuncFlags::NONE },
+ { "ISREF", "ISREF", 105, 105, 1, 1, V, { RX }, FuncFlags::NONE },
+ { "LOG", "LOG", 109, 109, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "CHAR", "CHAR", 111, 111, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LOWER", "LOWER", 112, 112, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "UPPER", "UPPER", 113, 113, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "PROPER", "PROPER", 114, 114, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LEFT", "LEFT", 115, 115, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "RIGHT", "RIGHT", 116, 116, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "EXACT", "EXACT", 117, 117, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "TRIM", "TRIM", 118, 118, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "REPLACE", "REPLACE", 119, 119, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "SUBSTITUTE", "SUBSTITUTE", 120, 120, 3, 4, V, { VR }, FuncFlags::NONE },
+ { "CODE", "CODE", 121, 121, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "FIND", "FIND", 124, 124, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "CELL", "CELL", 125, 125, 1, 2, V, { VV, RO }, FuncFlags::VOLATILE },
+ { "ISERR", "ISERR", 126, 126, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISTEXT", "ISTEXT", 127, 127, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISNUMBER", "ISNUMBER", 128, 128, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISBLANK", "ISBLANK", 129, 129, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "T", "T", 130, 130, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "N", "N", 131, 131, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "DATEVALUE", "DATEVALUE", 140, 140, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "TIMEVALUE", "TIMEVALUE", 141, 141, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SLN", "SLN", 142, 142, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "SYD", "SYD", 143, 143, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "DDB", "DDB", 144, 144, 4, 5, V, { VR }, FuncFlags::NONE },
+ { "INDIRECT", "INDIRECT", 148, 148, 1, 2, R, { VR }, FuncFlags::VOLATILE },
+ { "CLEAN", "CLEAN", 162, 162, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MDETERM", "MDETERM", 163, 163, 1, 1, V, { VA }, FuncFlags::NONE },
+ { "MINVERSE", "MINVERSE", 164, 164, 1, 1, A, { VA }, FuncFlags::NONE },
+ { "MMULT", "MMULT", 165, 165, 2, 2, A, { VA }, FuncFlags::NONE },
+ { "IPMT", "IPMT", 167, 167, 4, 6, V, { VR }, FuncFlags::NONE },
+ { "PPMT", "PPMT", 168, 168, 4, 6, V, { VR }, FuncFlags::NONE },
+ { "COUNTA", "COUNTA", 169, 169, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "PRODUCT", "PRODUCT", 183, 183, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "FACT", "FACT", 184, 184, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "DPRODUCT", "DPRODUCT", 189, 189, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "ISNONTEXT", "ISNONTEXT", 190, 190, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "STDEVP", "STDEVP", 193, 193, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "VARP", "VARP", 194, 194, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "DSTDEVP", "DSTDEVP", 195, 195, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DVARP", "DVARP", 196, 196, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "TRUNC", "TRUNC", 197, 197, 1, 1, V, { VR, C }, FuncFlags::NONE },
+ { "ISLOGICAL", "ISLOGICAL", 198, 198, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "DCOUNTA", "DCOUNTA", 199, 199, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { nullptr, "EXTERN.CALL", 255, 255, 1, MX, R, { RO_E, RO }, FuncFlags::IMPORTONLY },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "A1.R1C1", 30, 30, 0, 1, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "RETURN", 55, 55, 0, 1, R, { RO }, FuncFlags::MACROFUNC },
+ { nullptr, "ABSREF", 79, 79, 2, 2, R, { VR, RO }, FuncFlags::MACROFUNC },
+ { nullptr, "ADD.ARROW", 81, 81, 0, 0, V, {}, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVE.CELL", 94, 94, 0, 0, R, {}, FuncFlags::MACROFUNC },
+ { nullptr, "ACTIVATE", 103, 103, 0, 2, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVATE.NEXT", 104, 104, 0, 0, V, {}, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVATE.PREV", 105, 105, 0, 0, V, {}, FuncFlags::MACROCMD },
+ { nullptr, "ADD.BAR", 151, 151, 0, 0, V, {}, FuncFlags::MACROFUNC | FuncFlags::ALWAYSVAR },
+ { nullptr, "ADD.MENU", 152, 152, 2, 2, V, { VR, RO }, FuncFlags::MACROFUNC | FuncFlags::ALWAYSVAR },
+ { nullptr, "ADD.COMMAND", 153, 153, 3, 3, V, { VR, RO }, FuncFlags::MACROFUNC | FuncFlags::ALWAYSVAR }
+};
+
+/** Functions new in BIFF3. */
+const FunctionData saFuncTableBiff3[] =
+{
+ { "LINEST", "LINEST", 49, 49, 1, 4, A, { RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-2, BIFF3: 1-4
+ { "TREND", "TREND", 50, 50, 1, 4, A, { RA, RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-3, BIFF3: 1-4
+ { "LOGEST", "LOGEST", 51, 51, 1, 4, A, { RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-2, BIFF3: 1-4
+ { "GROWTH", "GROWTH", 52, 52, 1, 4, A, { RA, RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-3, BIFF3: 1-4
+ { "TRUNC", "TRUNC", 197, 197, 1, 2, V, { VR }, FuncFlags::NONE }, // BIFF2: 1, BIFF3: 1-2
+ { "DOLLAR", "USDOLLAR", 204, 204, 1, 2, V, { VR }, FuncFlags::IMPORTONLY },
+ { "FINDB", "FINDB", 205, 205, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "SEARCHB", "SEARCHB", 206, 206, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "REPLACEB", "REPLACEB", 207, 207, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LEFTB", "LEFTB", 208, 208, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "RIGHTB", "RIGHTB", 209, 209, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "MIDB", "MIDB", 210, 210, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LENB", "LENB", 211, 211, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ROUNDUP", "ROUNDUP", 212, 212, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "ROUNDDOWN", "ROUNDDOWN", 213, 213, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "ASC", "ASC", 214, 214, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "JIS", "DBCS", 215, 215, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ADDRESS", "ADDRESS", 219, 219, 2, 5, V, { VR }, FuncFlags::NONE },
+ { "DAYS360", "DAYS360", 220, 220, 2, 2, V, { VR, VR, C }, FuncFlags::NONE },
+ { "TODAY", "TODAY", 221, 221, 0, 0, V, {}, FuncFlags::VOLATILE },
+ { "VDB", "VDB", 222, 222, 5, 7, V, { VR }, FuncFlags::NONE },
+ { "MEDIAN", "MEDIAN", 227, 227, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "SUMPRODUCT", "SUMPRODUCT", 228, 228, 1, MX, V, { VA }, FuncFlags::NONE },
+ { "SINH", "SINH", 229, 229, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "CSCH", "SINH", 229, 229, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "COSH", "COSH", 230, 230, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SECH", "COSH", 230, 230, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "TANH", "TANH", 231, 231, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "COTH", "TANH", 231, 231, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "ASINH", "ASINH", 232, 232, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOSH", "ACOSH", 233, 233, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ATANH", "ATANH", 234, 234, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOTH", "ATANH", 234, 234, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "DGET", "DGET", 235, 235, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "INFO", "INFO", 244, 244, 1, 1, V, { VR }, FuncFlags::VOLATILE },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "ADD.BAR", 151, 151, 0, 1, V, { VR }, FuncFlags::MACROFUNC }, // BIFF2: 0, BIFF3: 0-1
+ { nullptr, "ADD.MENU", 152, 152, 2, 3, V, { VR, RO }, FuncFlags::MACROFUNC }, // BIFF2: 2, BIFF3: 2-3
+ { nullptr, "ADD.COMMAND", 153, 153, 3, 4, V, { VR, RO }, FuncFlags::MACROFUNC } // BIFF2: 3, BIFF3: 3-4
+};
+
+/** Functions new in BIFF4. */
+const FunctionData saFuncTableBiff4[] =
+{
+ { "FIXED", "FIXED", 14, 14, 1, 3, V, { VR }, FuncFlags::NONE }, // BIFF2-3: 1-2, BIFF4: 1-3
+ { "RANK", "RANK", 216, 216, 2, 3, V, { VR, RO, VR }, FuncFlags::NONE },
+ { "DB", "DB", 247, 247, 4, 5, V, { VR }, FuncFlags::NONE },
+ { "FREQUENCY", "FREQUENCY", 252, 252, 2, 2, A, { RA }, FuncFlags::NONE },
+ { "ERROR.TYPE", "ERROR.TYPE", 261, 261, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "AVEDEV", "AVEDEV", 269, 269, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "BETADIST", "BETADIST", 270, 270, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "GAMMALN", "GAMMALN", 271, 271, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "BETAINV", "BETAINV", 272, 272, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "BINOMDIST", "BINOMDIST", 273, 273, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.CHIDIST", "CHIDIST", 274, 274, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.CHIINV", "CHIINV", 275, 275, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "COMBIN", "COMBIN", 276, 276, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "CONFIDENCE", "CONFIDENCE", 277, 277, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "CRITBINOM", "CRITBINOM", 278, 278, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "EVEN", "EVEN", 279, 279, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "EXPONDIST", "EXPONDIST", 280, 280, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.FDIST", "FDIST", 281, 281, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.FINV", "FINV", 282, 282, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "FISHER", "FISHER", 283, 283, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "FISHERINV", "FISHERINV", 284, 284, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "COM.MICROSOFT.FLOOR", "FLOOR", 285, 285, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "GAMMADIST", "GAMMADIST", 286, 286, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "GAMMAINV", "GAMMAINV", 287, 287, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "COM.MICROSOFT.CEILING", "CEILING", 288, 288, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "HYPGEOMDIST", "HYPGEOMDIST", 289, 289, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LOGNORMDIST", "LOGNORMDIST", 290, 290, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LOGINV", "LOGINV", 291, 291, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "NEGBINOMDIST", "NEGBINOMDIST", 292, 292, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "NORMDIST", "NORMDIST", 293, 293, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.NORMSDIST", "NORMSDIST", 294, 294, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "NORMINV", "NORMINV", 295, 295, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.NORMSINV", "NORMSINV", 296, 296, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "STANDARDIZE", "STANDARDIZE", 297, 297, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "ODD", "ODD", 298, 298, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "PERMUT", "PERMUT", 299, 299, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "POISSON", "POISSON", 300, 300, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.TDIST", "TDIST", 301, 301, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "WEIBULL", "WEIBULL", 302, 302, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "SUMXMY2", "SUMXMY2", 303, 303, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "SUMX2MY2", "SUMX2MY2", 304, 304, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "SUMX2PY2", "SUMX2PY2", 305, 305, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "LEGACY.CHITEST", "CHITEST", 306, 306, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "CORREL", "CORREL", 307, 307, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "COVAR", "COVAR", 308, 308, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "FORECAST", "FORECAST", 309, 309, 3, 3, V, { VR, VA }, FuncFlags::NONE },
+ { "FTEST", "FTEST", 310, 310, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "INTERCEPT", "INTERCEPT", 311, 311, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "PEARSON", "PEARSON", 312, 312, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "RSQ", "RSQ", 313, 313, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "STEYX", "STEYX", 314, 314, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "SLOPE", "SLOPE", 315, 315, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "TTEST", "TTEST", 316, 316, 4, 4, V, { VA, VA, VR }, FuncFlags::NONE },
+ { "PROB", "PROB", 317, 317, 3, 4, V, { VA, VA, VR }, FuncFlags::NONE },
+ { "DEVSQ", "DEVSQ", 318, 318, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "GEOMEAN", "GEOMEAN", 319, 319, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "HARMEAN", "HARMEAN", 320, 320, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "SUMSQ", "SUMSQ", 321, 321, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "KURT", "KURT", 322, 322, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "SKEW", "SKEW", 323, 323, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "ZTEST", "ZTEST", 324, 324, 2, 3, V, { RX, VR }, FuncFlags::NONE },
+ { "LARGE", "LARGE", 325, 325, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "SMALL", "SMALL", 326, 326, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "QUARTILE", "QUARTILE", 327, 327, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "PERCENTILE", "PERCENTILE", 328, 328, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "PERCENTRANK", "PERCENTRANK", 329, 329, 2, 3, V, { RX, VR, VR_E }, FuncFlags::NONE },
+ { "MODE", "MODE", 330, 330, 1, MX, V, { VA }, FuncFlags::NONE },
+ { "TRIMMEAN", "TRIMMEAN", 331, 331, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "TINV", "TINV", 332, 332, 2, 2, V, { VR }, FuncFlags::NONE },
+
+ // *** Analysis add-in ***
+
+ { "HEX2BIN", "HEX2BIN", 384, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "HEX2DEC", "HEX2DEC", 385, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "HEX2OCT", "HEX2OCT", 386, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DEC2BIN", "DEC2BIN", 387, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DEC2HEX", "DEC2HEX", 388, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DEC2OCT", "DEC2OCT", 389, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "OCT2BIN", "OCT2BIN", 390, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "OCT2HEX", "OCT2HEX", 391, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "OCT2DEC", "OCT2DEC", 392, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "BIN2DEC", "BIN2DEC", 393, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "BIN2OCT", "BIN2OCT", 394, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BIN2HEX", "BIN2HEX", 395, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSUB", "IMSUB", 396, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMDIV", "IMDIV", 397, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMPOWER", "IMPOWER", 398, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMABS", "IMABS", 399, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSQRT", "IMSQRT", 400, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMLN", "IMLN", 401, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMLOG2", "IMLOG2", 402, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMLOG10", "IMLOG10", 403, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSIN", "IMSIN", 404, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMCOS", "IMCOS", 405, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMEXP", "IMEXP", 406, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMARGUMENT", "IMARGUMENT", 407, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMCONJUGATE", "IMCONJUGATE", 408, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMAGINARY", "IMAGINARY", 409, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMREAL", "IMREAL", 410, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "COMPLEX", "COMPLEX", 411, NOID, 2, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSUM", "IMSUM", 412, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL },
+ { "IMPRODUCT", "IMPRODUCT", 413, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL },
+ { "SERIESSUM", "SERIESSUM", 414, NOID, 4, 4, V, { RR, RR, RR, RX }, FuncFlags::EXTERNAL },
+ { "FACTDOUBLE", "FACTDOUBLE", 415, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "SQRTPI", "SQRTPI", 416, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "QUOTIENT", "QUOTIENT", 417, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DELTA", "DELTA", 418, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "GESTEP", "GESTEP", 419, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "ISEVEN", "ISEVEN", 420, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "ISODD", "ISODD", 421, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "MROUND", "MROUND", 422, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "ERF", "ERF", 423, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "ERFC", "ERFC", 424, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELJ", "BESSELJ", 425, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELK", "BESSELK", 426, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELY", "BESSELY", 427, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELI", "BESSELI", 428, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "XIRR", "XIRR", 429, NOID, 2, 3, V, { RX, RX, RR }, FuncFlags::EXTERNAL },
+ { "XNPV", "XNPV", 430, NOID, 3, 3, V, { RR, RX, RX }, FuncFlags::EXTERNAL },
+ { "PRICEMAT", "PRICEMAT", 431, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL },
+ { "YIELDMAT", "YIELDMAT", 432, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL },
+ { "INTRATE", "INTRATE", 433, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "RECEIVED", "RECEIVED", 434, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "DISC", "DISC", 435, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "PRICEDISC", "PRICEDISC", 436, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "YIELDDISC", "YIELDDISC", 437, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "TBILLEQ", "TBILLEQ", 438, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "TBILLPRICE", "TBILLPRICE", 439, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "TBILLYIELD", "TBILLYIELD", 440, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "PRICE", "PRICE", 441, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "YIELD", "YIELD", 442, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "DOLLARDE", "DOLLARDE", 443, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DOLLARFR", "DOLLARFR", 444, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "NOMINAL", "NOMINAL", 445, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "EFFECT", "EFFECT", 446, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "CUMPRINC", "CUMPRINC", 447, NOID, 6, 6, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "CUMIPMT", "CUMIPMT", 448, NOID, 6, 6, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "EDATE", "EDATE", 449, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "EOMONTH", "EOMONTH", 450, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "YEARFRAC", "YEARFRAC", 451, NOID, 2, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPDAYBS", "COUPDAYBS", 452, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPDAYS", "COUPDAYS", 453, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPDAYSNC", "COUPDAYSNC", 454, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPNCD", "COUPNCD", 455, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPNUM", "COUPNUM", 456, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPPCD", "COUPPCD", 457, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "DURATION", "DURATION", 458, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL }, // Calc: builtin and add-in (but different!)
+ { "MDURATION", "MDURATION", 459, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDLPRICE", "ODDLPRICE", 460, NOID, 7, 8, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDLYIELD", "ODDLYIELD", 461, NOID, 8, 9, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDFPRICE", "ODDFPRICE", 462, NOID, 8, 9, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDFYIELD", "ODDFYIELD", 463, NOID, 8, 9, V, { RR }, FuncFlags::EXTERNAL },
+ { "RANDBETWEEN", "RANDBETWEEN", 464, NOID, 2, 2, V, { RR }, FuncFlags::VOLATILE | FuncFlags::EXTERNAL },
+ { "WEEKNUM", "WEEKNUM", 465, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "AMORDEGRC", "AMORDEGRC", 466, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "AMORLINC", "AMORLINC", 467, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "CONVERT", "CONVERT", 468, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL }, // Calc: builtin and add-in (but different!)
+ { "ACCRINT", "ACCRINT", 469, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "ACCRINTM", "ACCRINTM", 470, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "WORKDAY", "WORKDAY", 471, NOID, 2, 3, V, { RR, RR, RX, C }, FuncFlags::EXTERNAL },
+ { "NETWORKDAYS", "NETWORKDAYS", 472, NOID, 2, 3, V, { RR, RR, RX, C }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "GCD", "GCD", 473, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "MULTINOMIAL", "MULTINOMIAL", 474, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL },
+ { "LCM", "LCM", 475, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "FVSCHEDULE", "FVSCHEDULE", 476, NOID, 2, 2, V, { RR, RX }, FuncFlags::EXTERNAL },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "ACTIVATE.NEXT", 104, 104, 0, 1, V, { VR }, FuncFlags::MACROCMD }, // BIFF2-3: 0, BIFF4: 0-1
+ { nullptr, "ACTIVATE.PREV", 105, 105, 0, 1, V, { VR }, FuncFlags::MACROCMD } // BIFF2-3: 0, BIFF4: 0-1
+};
+
+/** Functions new in BIFF5/BIFF7. */
+const FunctionData saFuncTableBiff5[] =
+{
+ { "WEEKDAY", "WEEKDAY", 70, 70, 1, 2, V, { VR }, FuncFlags::NONE }, // BIFF2-4: 1, BIFF5: 1-2
+ { "HLOOKUP", "HLOOKUP", 101, 101, 3, 4, V, { VV, RO, RO, VV }, FuncFlags::NONE }, // BIFF2-4: 3, BIFF5: 3-4
+ { "VLOOKUP", "VLOOKUP", 102, 102, 3, 4, V, { VV, RO, RO, VV }, FuncFlags::NONE }, // BIFF2-4: 3, BIFF5: 3-4
+ { "DAYS360", "DAYS360", 220, 220, 2, 3, V, { VR }, FuncFlags::NONE }, // BIFF3-4: 2, BIFF5: 2-3
+ { nullptr, "EXTERN.CALL", 255, 255, 1, MX, R, { RO_E, RO }, FuncFlags::EXPORTONLY }, // MACRO or EXTERNAL
+ { "CONCATENATE", "CONCATENATE", 336, 336, 0, MX, V, { VR }, FuncFlags::NONE },
+ { "POWER", "POWER", 337, 337, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "RADIANS", "RADIANS", 342, 342, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "DEGREES", "DEGREES", 343, 343, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SUBTOTAL", "SUBTOTAL", 344, 344, 2, MX, V, { VR, RO }, FuncFlags::NONE },
+ { "SUMIF", "SUMIF", 345, 345, 2, 3, V, { RO, VR, RO }, FuncFlags::NONE },
+ { "COUNTIF", "COUNTIF", 346, 346, 2, 2, V, { RO, VR }, FuncFlags::NONE },
+ { "COUNTBLANK", "COUNTBLANK", 347, 347, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "ISPMT", "ISPMT", 350, 350, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "DATEDIF", "DATEDIF", 351, 351, 3, 3, V, { VR }, FuncFlags::NONE },
+ { nullptr, "DATESTRING", 352, 352, 1, 1, V, { VR }, FuncFlags::IMPORTONLY }, // not supported in Calc, missing in OOXML spec
+ { nullptr, "NUMBERSTRING", 353, 353, 2, 2, V, { VR }, FuncFlags::IMPORTONLY }, // not supported in Calc, missing in OOXML spec
+ { "ROMAN", "ROMAN", 354, 354, 1, 2, V, { VR }, FuncFlags::NONE },
+
+ // *** EuroTool add-in ***
+ { "EUROCONVERT", "EUROCONVERT", NOID, NOID, 3, 5, V, { VR }, FuncFlags::EUROTOOL },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "ADD.MENU", 152, 152, 2, 4, V, { VR, RO, RO, VR }, FuncFlags::MACROFUNC }, // BIFF3-4: 2-3, BIFF5: 2-4
+ { nullptr, "ADD.COMMAND", 153, 153, 3, 5, V, { VR, RO, RO, RO, VR }, FuncFlags::MACROFUNC }, // BIFF3-4: 3-4, BIFF5: 3-5
+ { nullptr, "ADD.CHART.AUTOFORMAT", 390, 390, 0, 2, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "ADD.LIST.ITEM", 451, 451, 0, 2, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVE.CELL.FONT", 476, 476, 0, 14, V, { VR }, FuncFlags::MACROCMD }
+};
+
+/** Functions new in BIFF8. */
+const FunctionData saFuncTableBiff8[] =
+{
+ { "GETPIVOTDATA", "GETPIVOTDATA", 358, 358, 2, MX, V, { RR, RR, VR, VR }, FuncFlags::IMPORTONLY | FuncFlags::PARAMPAIRS },
+ { "HYPERLINK", "HYPERLINK", 359, 359, 1, 2, V, { VV, VO }, FuncFlags::NONE },
+ { nullptr, "PHONETIC", 360, 360, 1, 1, V, { RO }, FuncFlags::IMPORTONLY },
+ { "AVERAGEA", "AVERAGEA", 361, 361, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MAXA", "MAXA", 362, 362, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MINA", "MINA", 363, 363, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "STDEVPA", "STDEVPA", 364, 364, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "VARPA", "VARPA", 365, 365, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "STDEVA", "STDEVA", 366, 366, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "VARA", "VARA", 367, 367, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "COM.MICROSOFT.BAHTTEXT", "BAHTTEXT", 368, 368, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIDAYOFWEEK", 369, 369, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIDIGIT", 370, 370, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIMONTHOFYEAR", 371, 371, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAINUMSOUND", 372, 372, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAINUMSTRING", 373, 373, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAISTRINGLENGTH", 374, 374, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "ISTHAIDIGIT", 375, 375, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "ROUNDBAHTDOWN", 376, 376, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "ROUNDBAHTUP", 377, 377, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIYEAR", 378, 378, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "RTD", 379, 379, 3, 3, A, { VR, VR, RO }, FuncFlags::NONE }
+};
+
+/** Functions new in OOXML. */
+const FunctionData saFuncTableOox[] =
+{
+ { nullptr, "CUBEVALUE", 380, NOID, 1, MX, V, { VR, RX }, FuncFlags::NONE },
+ { nullptr, "CUBEMEMBER", 381, NOID, 2, 3, V, { VR, RX, VR }, FuncFlags::NONE },
+ { nullptr, "CUBEMEMBERPROPERTY", 382, NOID, 3, 3, V, { VR }, FuncFlags::NONE },
+ { nullptr, "CUBERANKEDMEMBER", 383, NOID, 3, 4, V, { VR }, FuncFlags::NONE },
+ { nullptr, "CUBEKPIMEMBER", 477, NOID, 3, 4, V, { VR }, FuncFlags::NONE },
+ { nullptr, "CUBESET", 478, NOID, 2, 5, V, { VR, RX, VR }, FuncFlags::NONE },
+ { nullptr, "CUBESETCOUNT", 479, NOID, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "IFERROR", "IFERROR", 480, NOID, 2, 2, V, { VO, RO }, FuncFlags::MACROCALL },
+ { "COUNTIFS", "COUNTIFS", 481, NOID, 2, MX, V, { RO, VR }, FuncFlags::MACROCALL | FuncFlags::PARAMPAIRS },
+ { "SUMIFS", "SUMIFS", 482, NOID, 3, MX, V, { RO, RO, VR }, FuncFlags::MACROCALL | FuncFlags::PARAMPAIRS },
+ { "AVERAGEIF", "AVERAGEIF", 483, NOID, 2, 3, V, { RO, VR, RO }, FuncFlags::MACROCALL },
+ { "AVERAGEIFS", "AVERAGEIFS", 484, NOID, 3, MX, V, { RO, RO, VR }, FuncFlags::MACROCALL | FuncFlags::PARAMPAIRS },
+ { "COM.MICROSOFT.ISO.CEILING", "ISO.CEILING", NOID, NOID, 1, 2, V, { VR }, FuncFlags::MACROCALL },
+ { "COM.MICROSOFT.NETWORKDAYS.INTL", "NETWORKDAYS.INTL", NOID, NOID, 2, 4, V, { VR, VR, VR, RX }, FuncFlags::MACROCALL },
+ { "COM.MICROSOFT.WORKDAY.INTL", "WORKDAY.INTL", NOID, NOID, 2, 4, V, { VR, VR, VR, RX }, FuncFlags::MACROCALL }
+};
+
+/** Functions new in Excel 2010.
+
+ A lot of statistical functions have been renamed (although the 'old' function name still is valid).
+ See http://office.microsoft.com/en-us/excel-help/what-s-new-changes-made-to-excel-functions-HA010355760.aspx
+
+ Functions with FuncFlags::IMPORTONLY are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for
+ BIFF, OOXML export uses this different mapping here but still uses the
+ mapping there to determine the feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance that mapping there such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/excel/xlformula.cxx saFuncTable_2010
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+const FunctionData saFuncTable2010[] =
+{
+ { "COM.MICROSOFT.COVARIANCE.P", "COVARIANCE.P", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.COVARIANCE.S", "COVARIANCE.S", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.STDEV.P", "STDEV.P", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.STDEV.S", "STDEV.S", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.VAR.P", "VAR.P" , NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.VAR.S", "VAR.S", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BETA.DIST", "BETA.DIST" , NOID, NOID, 4, 6, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BETA.INV", "BETA.INV", NOID, NOID, 3, 5, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BINOM.DIST", "BINOM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BINOM.INV", "BINOM.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.DIST", "CHISQ.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.INV", "CHISQ.INV", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.DIST.RT", "CHISQ.DIST.RT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.INV.RT", "CHISQ.INV.RT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.TEST", "CHISQ.TEST", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CONFIDENCE.NORM", "CONFIDENCE.NORM", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CONFIDENCE.T", "CONFIDENCE.T", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FDIST", "F.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.F.DIST.RT", "F.DIST.RT", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FINV", "F.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.F.INV.RT", "F.INV.RT", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.F.TEST", "F.TEST", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.EXPON.DIST", "EXPON.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.HYPGEOM.DIST", "HYPGEOM.DIST", NOID, NOID, 5, 5, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.POISSON.DIST", "POISSON.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.WEIBULL.DIST", "WEIBULL.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.GAMMA.DIST", "GAMMA.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.GAMMA.INV", "GAMMA.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.GAMMALN.PRECISE", "GAMMALN.PRECISE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.LOGNORM.DIST", "LOGNORM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.LOGNORM.INV", "LOGNORM.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.DIST", "NORM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.INV", "NORM.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.S.DIST", "NORM.S.DIST", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.S.INV", "NORM.S.INV", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.DIST", "T.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.DIST.2T", "T.DIST.2T", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.DIST.RT", "T.DIST.RT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.INV", "T.INV", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.INV.2T", "T.INV.2T", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.TEST", "T.TEST", NOID, NOID, 4, 4, V, { VA, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTILE.INC", "PERCENTILE.INC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTRANK.INC", "PERCENTRANK.INC", NOID, NOID, 2, 3, V, { RX, VR, VR_E }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.QUARTILE.INC", "QUARTILE.INC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.RANK.EQ", "RANK.EQ", NOID, NOID, 2, 3, V, { VR, RO, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTILE.EXC", "PERCENTILE.EXC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTRANK.EXC", "PERCENTRANK.EXC", NOID, NOID, 2, 3, V, { RX, VR, VR_E }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.QUARTILE.EXC", "QUARTILE.EXC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.RANK.AVG", "RANK.AVG", NOID, NOID, 2, 3, V, { VR, RO, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MODE.SNGL", "MODE.SNGL", NOID, NOID, 1, MX, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MODE.MULT", "MODE.MULT", NOID, NOID, 1, MX, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NEGBINOM.DIST", "NEGBINOM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.Z.TEST", "Z.TEST", NOID, NOID, 2, 3, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CEILING.PRECISE", "CEILING.PRECISE", NOID, NOID, 1, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FLOOR.PRECISE", "FLOOR.PRECISE", NOID, NOID, 1, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.ERF.PRECISE", "ERF.PRECISE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.ERFC.PRECISE", "ERFC.PRECISE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.AGGREGATE", "AGGREGATE", NOID, NOID, 3, MX, V, { VR, RO }, FuncFlags::MACROCALL_NEW }
+};
+
+/** Functions new in Excel 2013.
+
+ See http://office.microsoft.com/en-us/excel-help/new-functions-in-excel-2013-HA103980604.aspx
+ Most functions apparently were added for ODF1.2 ODFF / OpenFormula
+ compatibility.
+
+ Functions with FuncFlags::IMPORTONLY are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for
+ BIFF, OOXML export uses this different mapping here but still uses the
+ mapping there to determine the feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance that mapping there such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/excel/xlformula.cxx saFuncTable_2013
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+const FunctionData saFuncTable2013[] =
+{
+ { "ACOT", "ACOT", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ACOTH", "ACOTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ARABIC", "ARABIC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BASE", "BASE", NOID, NOID, 2, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BINOM.DIST.RANGE", "BINOM.DIST.RANGE", NOID, NOID, 3, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITAND", "BITAND", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITLSHIFT", "BITLSHIFT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITOR", "BITOR", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITRSHIFT", "BITRSHIFT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITXOR", "BITXOR", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CEILING.MATH", "CEILING.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "CEILING", "CEILING.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::EXPORTONLY | FuncFlags::MACROCALL_NEW },
+ { "COMBINA", "COMBINA", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COT", "COT", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COTH", "COTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "CSC", "CSC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "CSCH", "CSCH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "DAYS", "DAYS", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "DECIMAL", "DECIMAL", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.ENCODEURL","ENCODEURL", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FILTERXML","FILTERXML", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FLOOR.MATH", "FLOOR.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FLOOR", "FLOOR.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::EXPORTONLY | FuncFlags::MACROCALL_NEW },
+ // NOTE: this FDIST is not our LEGACY.FDIST
+ { nullptr/*"FDIST"*/, "FDIST", NOID, NOID, 3, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ // NOTE: this FINV is not our LEGACY.FINV
+ { nullptr/*"FINV"*/, "FINV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FORMULA", "FORMULATEXT", NOID, NOID, 1, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "GAMMA", "GAMMA", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "GAUSS", "GAUSS", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "IFNA", "IFNA", NOID, NOID, 2, 2, V, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "IMCOSH", "IMCOSH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMCOT", "IMCOT", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMCSC", "IMCSC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMCSCH", "IMCSCH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMSEC", "IMSEC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMSECH", "IMSECH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMSINH", "IMSINH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMTAN", "IMTAN", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ISFORMULA", "ISFORMULA", NOID, NOID, 1, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "ISOWEEKNUM", "ISOWEEKNUM", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "MUNIT", "MUNIT", NOID, NOID, 1, 1, A, { VR }, FuncFlags::MACROCALL_NEW },
+ { "NUMBERVALUE", "NUMBERVALUE", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "PDURATION", "PDURATION", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "PERMUTATIONA", "PERMUTATIONA", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "PHI", "PHI", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "RRI", "RRI", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "SEC", "SEC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "SECH", "SECH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "SHEET", "SHEET", NOID, NOID, 0, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "SHEETS", "SHEETS", NOID, NOID, 0, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "SKEWP", "SKEW.P", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "UNICHAR", "UNICHAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "UNICODE", "UNICODE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.WEBSERVICE","WEBSERVICE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "XOR", "XOR", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW }
+};
+
+/** Functions new in Excel 2016.
+
+ See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+ and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US
+
+ @See sc/source/filter/excel/xlformula.cxx saFuncTable_2016
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+const FunctionData saFuncTable2016[] =
+{
+ { "COM.MICROSOFT.FORECAST.ETS", "FORECAST.ETS", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.ETS.CONFINT", "FORECAST.ETS.CONFINT", NOID, NOID, 4, 7, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.ETS.SEASONALITY", "FORECAST.ETS.SEASONALITY", NOID, NOID, 2, 4, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.ETS.STAT", "FORECAST.ETS.STAT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.LINEAR", "FORECAST.LINEAR", NOID, NOID, 3, 3, V, { VR, VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CONCAT", "CONCAT", NOID, NOID, 1, MX, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.TEXTJOIN", "TEXTJOIN", NOID, NOID, 3, MX, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.IFS", "IFS", NOID, NOID, 2, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.SWITCH", "SWITCH", NOID, NOID, 3, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MINIFS", "MINIFS", NOID, NOID, 3, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MAXIFS", "MAXIFS", NOID, NOID, 3, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW }
+};
+
+
+/** Functions defined by OpenFormula, but not supported by Calc or by Excel. */
+const FunctionData saFuncTableOdf[] =
+{
+ { "CHISQDIST", nullptr, NOID, NOID, 2, 3, V, { VR }, FuncFlags::MACROCALLODF },
+ { "CHISQINV", nullptr, NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALLODF }
+};
+
+/** Functions defined by Calc, but not in OpenFormula nor supported by Excel. */
+const FunctionData saFuncTableOOoLO[] =
+{
+ { "ORG.OPENOFFICE.WEEKS", "ORG.OPENOFFICE.WEEKS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.MONTHS", "ORG.OPENOFFICE.MONTHS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.YEARS", "ORG.OPENOFFICE.YEARS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ISLEAPYEAR", "ORG.OPENOFFICE.ISLEAPYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINMONTH", "ORG.OPENOFFICE.DAYSINMONTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINYEAR", "ORG.OPENOFFICE.DAYSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.WEEKSINYEAR", "ORG.OPENOFFICE.WEEKSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ROT13", "ORG.OPENOFFICE.ROT13", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ /* Next 8 lines are for importing from .xlsx files saved by Calc before
+ * fdo#59727 was patched with the entries above. */
+ { "ORG.OPENOFFICE.WEEKS", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFWEEKS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.MONTHS", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFMONTHS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.YEARS", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFYEARS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ISLEAPYEAR", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETISLEAPYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINMONTH", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINMONTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINYEAR", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.WEEKSINYEAR", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETWEEKSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ROT13", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ // More functions written wrongly in the past.
+ { "ORG.OPENOFFICE.ERRORTYPE", "ORG.OPENOFFICE.ERRORTYPE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.MULTIRANGE", "ORG.OPENOFFICE.MULTIRANGE", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.GOALSEEK", "ORG.OPENOFFICE.GOALSEEK", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.EASTERSUNDAY","ORG.OPENOFFICE.EASTERSUNDAY", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.CURRENT", "ORG.OPENOFFICE.CURRENT", NOID, NOID, 0, 0, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.STYLE", "ORG.OPENOFFICE.STYLE", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ // And the import for the wrongly written functions even without _xlfn.
+ { "ORG.OPENOFFICE.ERRORTYPE", "ERRORTYPE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.MULTIRANGE", "MULTIRANGE", NOID, NOID, 1, MX, V, { RX }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.GOALSEEK", "GOALSEEK", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.EASTERSUNDAY","EASTERSUNDAY", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.CURRENT", "CURRENT", NOID, NOID, 0, 0, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.STYLE", "STYLE", NOID, NOID, 1, 3, V, { VR }, FuncFlags::IMPORTONLY },
+ // Other functions.
+ { "ORG.OPENOFFICE.CONVERT", "ORG.OPENOFFICE.CONVERT", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.COLOR", "ORG.LIBREOFFICE.COLOR", NOID, NOID, 3, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.RAWSUBTRACT","ORG.LIBREOFFICE.RAWSUBTRACT",NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FORECAST.ETS.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.MULT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT", NOID, NOID, 4, 7, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.ROUNDSIG", "ORG.LIBREOFFICE.ROUNDSIG", NOID, NOID, 2, 2, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.REGEX", "ORG.LIBREOFFICE.REGEX", NOID, NOID, 2, 4, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FOURIER", "ORG.LIBREOFFICE.FOURIER", NOID, NOID, 2, 5, A, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.RAND.NV", "ORG.LIBREOFFICE.RAND.NV", NOID, NOID, 0, 0, V, {}, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.RANDBETWEEN.NV", "ORG.LIBREOFFICE.RANDBETWEEN.NV", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW }
+
+};
+
+const sal_Unicode API_TOKEN_OPEN = '(';
+const sal_Unicode API_TOKEN_CLOSE = ')';
+const sal_Unicode API_TOKEN_SEP = ';';
+
+const sal_Unicode API_TOKEN_ARRAY_OPEN = '{';
+const sal_Unicode API_TOKEN_ARRAY_CLOSE = '}';
+const sal_Unicode API_TOKEN_ARRAY_ROWSEP = '|';
+const sal_Unicode API_TOKEN_ARRAY_COLSEP = ';';
+
+} // namespace
+
+// function info parameter class iterator =====================================
+
+FunctionParamInfoIterator::FunctionParamInfoIterator( const FunctionInfo& rFuncInfo ) :
+ mpParamInfo( rFuncInfo.mpParamInfos ),
+ mpParamInfoEnd( rFuncInfo.mpParamInfos + FUNCINFO_PARAMINFOCOUNT ),
+ mbParamPairs( rFuncInfo.mbParamPairs )
+{
+}
+
+bool FunctionParamInfoIterator::isCalcOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == FuncParamValidity::CalcOnly);
+}
+
+bool FunctionParamInfoIterator::isExcelOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == FuncParamValidity::ExcelOnly);
+}
+
+FunctionParamInfoIterator& FunctionParamInfoIterator::operator++()
+{
+ if( mpParamInfo )
+ {
+ // move pointer to next entry, if something explicit follows
+ if( mpParamInfo + 1 < mpParamInfoEnd )
+ ++mpParamInfo;
+ // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it
+ else if( isExcelOnlyParam() || isCalcOnlyParam() )
+ mpParamInfo = nullptr;
+ // points to last info, but parameter pairs expected, move to previous info
+ else if( mbParamPairs )
+ --mpParamInfo;
+ // otherwise: repeat last parameter class
+ }
+ return *this;
+}
+
+// function provider ==========================================================
+
+struct FunctionProviderImpl
+{
+ typedef RefMap< OUString, FunctionInfo > FuncNameMap;
+ typedef RefMap< sal_uInt16, FunctionInfo > FuncIdMap;
+
+ FunctionInfoVector maFuncs; /// All function infos in one list.
+ FuncNameMap maOoxFuncs; /// Maps OOXML function names to function data.
+ FuncIdMap maBiff12Funcs; /// Maps BIFF12 function indexes to function data.
+ FuncIdMap maBiffFuncs; /// Maps BIFF2-BIFF8 function indexes to function data.
+ FuncNameMap maMacroFuncs; /// Maps macro function names to function data.
+
+ explicit FunctionProviderImpl(bool bImportFilter);
+
+private:
+ /** Creates and inserts a function info struct from the passed function data. */
+ void initFunc(const FunctionData& rFuncData);
+
+ /** Initializes the members from the passed function data list. */
+ void initFuncs(const FunctionData* pBeg, const FunctionData* pEnd, bool bImportFilter);
+};
+
+FunctionProviderImpl::FunctionProviderImpl( bool bImportFilter )
+{
+ /* Add functions supported in the current BIFF version only. Function
+ tables from later BIFF versions may overwrite single functions from
+ earlier tables. */
+ initFuncs(saFuncTableBiff2, std::end(saFuncTableBiff2), bImportFilter);
+ initFuncs(saFuncTableBiff3, std::end(saFuncTableBiff3), bImportFilter);
+ initFuncs(saFuncTableBiff4, std::end(saFuncTableBiff4), bImportFilter);
+ initFuncs(saFuncTableBiff5, std::end(saFuncTableBiff5), bImportFilter);
+ initFuncs(saFuncTableBiff8, std::end(saFuncTableBiff8), bImportFilter);
+ initFuncs(saFuncTableOox , std::end(saFuncTableOox) , bImportFilter);
+ initFuncs(saFuncTable2010 , std::end(saFuncTable2010) , bImportFilter);
+ initFuncs(saFuncTable2013 , std::end(saFuncTable2013) , bImportFilter);
+ initFuncs(saFuncTable2016 , std::end(saFuncTable2016) , bImportFilter);
+ initFuncs(saFuncTableOdf , std::end(saFuncTableOdf) , bImportFilter);
+ initFuncs(saFuncTableOOoLO, std::end(saFuncTableOOoLO), bImportFilter);
+}
+
+void FunctionProviderImpl::initFunc(const FunctionData& rFuncData)
+{
+ // create a function info object
+ FunctionInfoRef xFuncInfo = std::make_shared<FunctionInfo>();
+ if( rFuncData.mpcOdfFuncName )
+ xFuncInfo->maOdfFuncName = OUString::createFromAscii( rFuncData.mpcOdfFuncName );
+ if( rFuncData.mpcOoxFuncName )
+ xFuncInfo->maOoxFuncName = OUString::createFromAscii( rFuncData.mpcOoxFuncName );
+
+ if( rFuncData.mnFlags & FuncFlags::MACROCALL )
+ {
+ OSL_ENSURE( !xFuncInfo->maOoxFuncName.isEmpty(), "FunctionProviderImpl::initFunc - missing OOXML function name" );
+ OSL_ENSURE( !(rFuncData.mnFlags & FuncFlags::MACROCALLODF ), "FunctionProviderImpl::initFunc - unexpected flag FuncFlags::MACROCALLODF" );
+ xFuncInfo->maBiffMacroName = "_xlfn." + xFuncInfo->maOoxFuncName;
+ if( rFuncData.mnFlags & FuncFlags::MACROCALL_FN )
+ {
+ xFuncInfo->maOoxFuncName = "_xlfn." + xFuncInfo->maOoxFuncName;
+ //! From here on maOoxFuncName contains the _xlfn. prefix!
+ }
+ }
+ else if( rFuncData.mnFlags & FuncFlags::MACROCALLODF )
+ {
+ OSL_ENSURE( !xFuncInfo->maOdfFuncName.isEmpty(), "FunctionProviderImpl::initFunc - missing ODF function name" );
+ xFuncInfo->maBiffMacroName = "_xlfnodf." + xFuncInfo->maOdfFuncName;
+ }
+ xFuncInfo->meFuncLibType = (rFuncData.mnFlags & FuncFlags::EUROTOOL) ? FUNCLIB_EUROTOOL : FUNCLIB_UNKNOWN;
+ xFuncInfo->mnApiOpCode = -1;
+ xFuncInfo->mnBiff12FuncId = rFuncData.mnBiff12FuncId;
+ xFuncInfo->mnBiffFuncId = rFuncData.mnBiffFuncId;
+ xFuncInfo->mnMinParamCount = rFuncData.mnMinParamCount;
+ xFuncInfo->mnMaxParamCount = (rFuncData.mnMaxParamCount == MX) ? OOX_MAX_PARAMCOUNT : rFuncData.mnMaxParamCount;
+ xFuncInfo->mnRetClass = rFuncData.mnRetClass;
+ xFuncInfo->mpParamInfos = rFuncData.mpParamInfos;
+ xFuncInfo->mbParamPairs = bool(rFuncData.mnFlags & FuncFlags::PARAMPAIRS);
+ xFuncInfo->mbVolatile = bool(rFuncData.mnFlags & FuncFlags::VOLATILE);
+ xFuncInfo->mbExternal = bool(rFuncData.mnFlags & FuncFlags::EXTERNAL);
+ xFuncInfo->mbInternal = !xFuncInfo->mbExternal || ( rFuncData.mnFlags & FuncFlags::INTERNAL );
+ bool bMacroCmd(rFuncData.mnFlags & FuncFlags::MACROCMD);
+ xFuncInfo->mbMacroFunc = bMacroCmd || ( rFuncData.mnFlags & FuncFlags::MACROFUNC );
+ xFuncInfo->mbVarParam = bMacroCmd || (rFuncData.mnMinParamCount != rFuncData.mnMaxParamCount) || ( rFuncData.mnFlags & FuncFlags::ALWAYSVAR );
+
+ setFlag( xFuncInfo->mnBiff12FuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd );
+ setFlag( xFuncInfo->mnBiffFuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd );
+
+ // insert the function info into the member maps
+ maFuncs.push_back( xFuncInfo );
+ if( !xFuncInfo->maOoxFuncName.isEmpty() )
+ maOoxFuncs[ xFuncInfo->maOoxFuncName ] = xFuncInfo;
+ if( xFuncInfo->mnBiff12FuncId != NOID )
+ maBiff12Funcs[ xFuncInfo->mnBiff12FuncId ] = xFuncInfo;
+ if( xFuncInfo->mnBiffFuncId != NOID )
+ maBiffFuncs[ xFuncInfo->mnBiffFuncId ] = xFuncInfo;
+ if( !xFuncInfo->maBiffMacroName.isEmpty() )
+ maMacroFuncs[ xFuncInfo->maBiffMacroName ] = xFuncInfo;
+}
+
+void FunctionProviderImpl::initFuncs(const FunctionData* pBeg, const FunctionData* pEnd, bool bImportFilter)
+{
+ for( const FunctionData* pIt = pBeg; pIt != pEnd; ++pIt )
+ if( pIt->isSupported(bImportFilter) )
+ initFunc(*pIt);
+}
+
+FunctionProvider::FunctionProvider( bool bImportFilter ) :
+ mxFuncImpl( std::make_shared<FunctionProviderImpl>( bImportFilter ) )
+{
+}
+
+FunctionProvider::~FunctionProvider()
+{
+}
+
+const FunctionInfo* FunctionProvider::getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const
+{
+ return mxFuncImpl->maOoxFuncs.get( rFuncName ).get();
+}
+
+const FunctionInfo* FunctionProvider::getFuncInfoFromBiff12FuncId( sal_uInt16 nFuncId ) const
+{
+ return mxFuncImpl->maBiff12Funcs.get( nFuncId ).get();
+}
+
+const FunctionInfo* FunctionProvider::getFuncInfoFromMacroName( const OUString& rFuncName ) const
+{
+ return mxFuncImpl->maMacroFuncs.get( rFuncName ).get();
+}
+
+FunctionLibraryType FunctionProvider::getFuncLibTypeFromLibraryName( std::u16string_view rLibraryName )
+{
+ // the EUROTOOL add-in containing the EUROCONVERT function
+ if( o3tl::equalsIgnoreAsciiCase(rLibraryName, u"EUROTOOL.XLA")
+ || o3tl::equalsIgnoreAsciiCase(rLibraryName, u"EUROTOOL.XLAM"))
+ return FUNCLIB_EUROTOOL;
+
+ // default: unknown library
+ return FUNCLIB_UNKNOWN;
+}
+
+const FunctionInfoVector& FunctionProvider::getFuncs() const
+{
+ return mxFuncImpl->maFuncs;
+}
+
+// op-code and function provider ==============================================
+
+struct OpCodeProviderImpl : public ApiOpCodes
+{
+ typedef RefMap< sal_Int32, FunctionInfo > OpCodeFuncMap;
+ typedef RefMap< OUString, FunctionInfo > FuncNameMap;
+ typedef ::std::vector< FormulaOpCodeMapEntry > OpCodeEntryVector;
+
+ OpCodeFuncMap maOpCodeFuncs; /// Maps API function op-codes to function data.
+ FuncNameMap maExtProgFuncs; /// Maps programmatical API function names to function data.
+ OpCodeEntryVector maParserMap; /// OOXML token mapping for formula parser service.
+
+ explicit OpCodeProviderImpl(
+ const FunctionInfoVector& rFuncInfos,
+ const Reference< XMultiServiceFactory >& rxModelFactory );
+
+private:
+ typedef ::std::map< OUString, ApiToken > ApiTokenMap;
+ typedef Sequence< FormulaOpCodeMapEntry > OpCodeEntrySequence;
+
+ static bool fillEntrySeq( OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup );
+ static bool fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup );
+ bool fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const;
+
+ static bool initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId );
+ bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName );
+ bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const char* pcOdfName, const char* pcOoxName );
+ bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName );
+
+ bool initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap );
+ bool initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap, const FunctionInfoVector& rFuncInfos );
+};
+
+OpCodeProviderImpl::OpCodeProviderImpl( const FunctionInfoVector& rFuncInfos,
+ const Reference< XMultiServiceFactory >& rxModelFactory )
+{
+ if( !rxModelFactory.is() )
+ return;
+
+ try
+ {
+ Reference< XFormulaOpCodeMapper > xMapper( rxModelFactory->createInstance(
+ "com.sun.star.sheet.FormulaOpCodeMapper" ), UNO_QUERY_THROW );
+
+ // op-codes provided as attributes
+ OPCODE_UNKNOWN = xMapper->getOpCodeUnknown();
+ OPCODE_EXTERNAL = xMapper->getOpCodeExternal();
+
+ using namespace ::com::sun::star::sheet::FormulaMapGroup;
+ using namespace ::com::sun::star::sheet::FormulaMapGroupSpecialOffset;
+
+ OpCodeEntrySequence aEntrySeq;
+ ApiTokenMap aTokenMap, aExtFuncTokenMap;
+ bool bIsValid =
+ // special
+ fillEntrySeq( aEntrySeq, xMapper, SPECIAL ) &&
+ initOpCode( OPCODE_PUSH, aEntrySeq, PUSH ) &&
+ initOpCode( OPCODE_MISSING, aEntrySeq, MISSING ) &&
+ initOpCode( OPCODE_SPACES, aEntrySeq, SPACES ) &&
+ initOpCode( OPCODE_NAME, aEntrySeq, NAME ) &&
+ initOpCode( OPCODE_DBAREA, aEntrySeq, DB_AREA ) &&
+ initOpCode( OPCODE_NLR, aEntrySeq, COL_ROW_NAME ) &&
+ initOpCode( OPCODE_MACRO, aEntrySeq, MACRO ) &&
+ initOpCode( OPCODE_BAD, aEntrySeq, BAD ) &&
+ initOpCode( OPCODE_NONAME, aEntrySeq, NO_NAME ) &&
+ // separators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, SEPARATORS ) &&
+ initOpCode( OPCODE_OPEN, aTokenMap, API_TOKEN_OPEN, '(' ) &&
+ initOpCode( OPCODE_CLOSE, aTokenMap, API_TOKEN_CLOSE, ')' ) &&
+ initOpCode( OPCODE_SEP, aTokenMap, API_TOKEN_SEP, ',' ) &&
+ // array separators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, ARRAY_SEPARATORS ) &&
+ initOpCode( OPCODE_ARRAY_OPEN, aTokenMap, API_TOKEN_ARRAY_OPEN, '{' ) &&
+ initOpCode( OPCODE_ARRAY_CLOSE, aTokenMap, API_TOKEN_ARRAY_CLOSE, '}' ) &&
+ initOpCode( OPCODE_ARRAY_ROWSEP, aTokenMap, API_TOKEN_ARRAY_ROWSEP, ';' ) &&
+ initOpCode( OPCODE_ARRAY_COLSEP, aTokenMap, API_TOKEN_ARRAY_COLSEP, ',' ) &&
+ // unary operators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, UNARY_OPERATORS ) &&
+ initOpCode( OPCODE_PLUS_SIGN, aTokenMap, '+', '\0' ) && // same op-code as OPCODE_ADD
+ initOpCode( OPCODE_MINUS_SIGN, aTokenMap, '-', '-' ) &&
+ initOpCode( OPCODE_PERCENT, aTokenMap, '%', '%' ) &&
+ // binary operators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, BINARY_OPERATORS ) &&
+ initOpCode( OPCODE_ADD, aTokenMap, '+', '+' ) &&
+ initOpCode( OPCODE_SUB, aTokenMap, '-', '-' ) &&
+ initOpCode( OPCODE_MULT, aTokenMap, '*', '*' ) &&
+ initOpCode( OPCODE_DIV, aTokenMap, '/', '/' ) &&
+ initOpCode( OPCODE_POWER, aTokenMap, '^', '^' ) &&
+ initOpCode( OPCODE_CONCAT, aTokenMap, '&', '&' ) &&
+ initOpCode( OPCODE_EQUAL, aTokenMap, '=', '=' ) &&
+ initOpCode( OPCODE_NOT_EQUAL, aTokenMap, "<>", "<>" ) &&
+ initOpCode( OPCODE_LESS, aTokenMap, '<', '<' ) &&
+ initOpCode( OPCODE_LESS_EQUAL, aTokenMap, "<=", "<=" ) &&
+ initOpCode( OPCODE_GREATER, aTokenMap, '>', '>' ) &&
+ initOpCode( OPCODE_GREATER_EQUAL, aTokenMap, ">=", ">=" ) &&
+ initOpCode( OPCODE_INTERSECT, aTokenMap, '!', ' ' ) &&
+ initOpCode( OPCODE_LIST, aTokenMap, '~', ',' ) &&
+ initOpCode( OPCODE_RANGE, aTokenMap, ':', ':' ) &&
+ // functions
+ fillFuncTokenMaps( aTokenMap, aExtFuncTokenMap, aEntrySeq, xMapper ) &&
+ initFuncOpCodes( aTokenMap, aExtFuncTokenMap, rFuncInfos ) &&
+ initOpCode( OPCODE_DDE, aTokenMap, "DDE", nullptr );
+
+ OSL_ENSURE( bIsValid, "OpCodeProviderImpl::OpCodeProviderImpl - opcodes not initialized" );
+
+ // OPCODE_PLUS_SIGN and OPCODE_ADD should be equal, otherwise "+" has to be passed above
+ OSL_ENSURE( OPCODE_PLUS_SIGN == OPCODE_ADD, "OpCodeProviderImpl::OpCodeProviderImpl - need opcode mapping for OPCODE_PLUS_SIGN" );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "OpCodeProviderImpl::OpCodeProviderImpl - cannot receive formula opcode mapper" );
+ }
+}
+
+bool OpCodeProviderImpl::fillEntrySeq( OpCodeEntrySequence& orEntrySeq,
+ const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup )
+{
+ try
+ {
+ orEntrySeq = rxMapper->getAvailableMappings( css::sheet::FormulaLanguage::ODFF, nMapGroup );
+ return orEntrySeq.hasElements();
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+}
+
+bool OpCodeProviderImpl::fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq,
+ const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup )
+{
+ orTokenMap.clear();
+ if( fillEntrySeq( orEntrySeq, rxMapper, nMapGroup ) )
+ {
+ for( const FormulaOpCodeMapEntry& rEntry : std::as_const(orEntrySeq) )
+ orTokenMap[ rEntry.Name ] = rEntry.Token;
+ }
+ return orEntrySeq.hasElements();
+}
+
+bool OpCodeProviderImpl::fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const
+{
+ orIntFuncTokenMap.clear();
+ orExtFuncTokenMap.clear();
+ if( fillEntrySeq( orEntrySeq, rxMapper, css::sheet::FormulaMapGroup::FUNCTIONS ) )
+ {
+ for( const FormulaOpCodeMapEntry& rEntry : std::as_const(orEntrySeq) )
+ ((rEntry.Token.OpCode == OPCODE_EXTERNAL) ? orExtFuncTokenMap : orIntFuncTokenMap)[ rEntry.Name ] = rEntry.Token;
+ }
+ return orEntrySeq.hasElements();
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId )
+{
+ if( (0 <= nSpecialId) && (nSpecialId < rEntrySeq.getLength()) )
+ {
+ ornOpCode = rEntrySeq[ nSpecialId ].Token.OpCode;
+ return true;
+ }
+ OSL_FAIL( OString( OString::Concat("OpCodeProviderImpl::initOpCode - opcode for special offset ") +
+ OString::number( nSpecialId ) + " not found" ).getStr() );
+ return false;
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName )
+{
+ ApiTokenMap::const_iterator aIt = rTokenMap.find( rOdfName );
+ if( aIt != rTokenMap.end() )
+ {
+ ornOpCode = aIt->second.OpCode;
+ if( !rOoxName.isEmpty() )
+ {
+ FormulaOpCodeMapEntry aEntry;
+ aEntry.Name = rOoxName;
+ aEntry.Token.OpCode = ornOpCode;
+ maParserMap.push_back( aEntry );
+ }
+ return true;
+ }
+ OSL_FAIL( OStringBuffer( "OpCodeProviderImpl::initOpCode - opcode for \"" +
+ OUStringToOString( rOdfName, RTL_TEXTENCODING_ASCII_US ) +
+ "\" not found" ).getStr() );
+ return false;
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const char* pcOdfName, const char* pcOoxName )
+{
+ OUString aOoxName;
+ if( pcOoxName ) aOoxName = OUString::createFromAscii( pcOoxName );
+ return initOpCode( ornOpCode, rTokenMap, OUString::createFromAscii( pcOdfName ), aOoxName );
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName )
+{
+ OUString aOoxName;
+ if( cOoxName ) aOoxName = OUString( cOoxName );
+ return initOpCode( ornOpCode, rTokenMap, OUString( cOdfName ), aOoxName );
+}
+
+bool OpCodeProviderImpl::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap )
+{
+ bool bIsValid = false;
+ if( !orFuncInfo.maOdfFuncName.isEmpty() )
+ {
+ ApiTokenMap::const_iterator aIt = rFuncTokenMap.find( orFuncInfo.maOdfFuncName );
+ if( aIt != rFuncTokenMap.end() )
+ {
+ orFuncInfo.mnApiOpCode = aIt->second.OpCode;
+ bIsValid =
+ (orFuncInfo.mnApiOpCode >= 0) &&
+ (orFuncInfo.mnApiOpCode != OPCODE_UNKNOWN) &&
+ (orFuncInfo.mnApiOpCode != OPCODE_NONAME);
+ OSL_ENSURE( bIsValid,
+ OStringBuffer( "OpCodeProviderImpl::initFuncOpCode - no valid opcode for ODF function \""
+ + OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US )
+ + "\"" ).getStr() );
+
+ if( bIsValid && (orFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) )
+ {
+ bIsValid = (aIt->second.Data >>= orFuncInfo.maExtProgName) && !orFuncInfo.maExtProgName.isEmpty();
+ OSL_ENSURE( bIsValid,
+ OStringBuffer( "OpCodeProviderImpl::initFuncOpCode - no programmatical name for external function \""
+ + OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US )
+ + "\"" ).getStr() );
+ }
+
+ // add to parser map, if OOXML function name exists
+ if( bIsValid && !orFuncInfo.maOoxFuncName.isEmpty() )
+ {
+ // create the parser map entry
+ FormulaOpCodeMapEntry aEntry;
+ aEntry.Name = orFuncInfo.maOoxFuncName;
+ aEntry.Token = aIt->second;
+ maParserMap.push_back( aEntry );
+ }
+ }
+ else
+ {
+ // ignore entries for functions unknown by Calc *and* by Excel
+ bIsValid = orFuncInfo.maOoxFuncName.isEmpty();
+ SAL_WARN_IF( !bIsValid, "sc",
+ "OpCodeProviderImpl::initFuncOpCode - no opcode mapping for function ODF '" <<
+ orFuncInfo.maOdfFuncName << "' <-> OOXML '" << orFuncInfo.maOoxFuncName << "'");
+ }
+ }
+ else if( orFuncInfo.mnBiffFuncId == BIFF_FUNC_EXTERNCALL )
+ {
+ orFuncInfo.mnApiOpCode = OPCODE_EXTERNAL;
+ bIsValid = true;
+ }
+ else if( !orFuncInfo.maOoxFuncName.isEmpty() )
+ {
+ orFuncInfo.mnApiOpCode = OPCODE_BAD;
+ bIsValid = true;
+ }
+
+ if( !bIsValid || (orFuncInfo.mnApiOpCode == OPCODE_UNKNOWN) || (orFuncInfo.mnApiOpCode < 0) )
+ orFuncInfo.mnApiOpCode = OPCODE_NONAME;
+ return bIsValid;
+}
+
+bool OpCodeProviderImpl::initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap, const FunctionInfoVector& rFuncInfos )
+{
+ bool bIsValid = true;
+ for( const FunctionInfoRef& xFuncInfo : rFuncInfos )
+ {
+ // set API opcode from ODF function name
+ if (xFuncInfo->mbExternal)
+ bIsValid &= initFuncOpCode( *xFuncInfo, rExtFuncTokenMap );
+ if (xFuncInfo->mbInternal)
+ bIsValid &= initFuncOpCode( *xFuncInfo, rIntFuncTokenMap );
+ // insert the function info into the maps
+ if( (xFuncInfo->mnApiOpCode != OPCODE_NONAME) && (xFuncInfo->mnApiOpCode != OPCODE_BAD) )
+ {
+ if( (xFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && !xFuncInfo->maExtProgName.isEmpty() )
+ maExtProgFuncs[ xFuncInfo->maExtProgName ] = xFuncInfo;
+ else
+ maOpCodeFuncs[ xFuncInfo->mnApiOpCode ] = xFuncInfo;
+ }
+ }
+ return bIsValid;
+}
+
+OpCodeProvider::OpCodeProvider( const Reference< XMultiServiceFactory >& rxModelFactory,
+ bool bImportFilter ) :
+ FunctionProvider( bImportFilter ),
+ mxOpCodeImpl( std::make_shared<OpCodeProviderImpl>( getFuncs(), rxModelFactory ) )
+{
+}
+
+OpCodeProvider::~OpCodeProvider()
+{
+}
+
+const ApiOpCodes& OpCodeProvider::getOpCodes() const
+{
+ return *mxOpCodeImpl;
+}
+
+const FunctionInfo* OpCodeProvider::getFuncInfoFromApiToken( const ApiToken& rToken ) const
+{
+ const FunctionInfo* pFuncInfo = nullptr;
+ if( (rToken.OpCode == mxOpCodeImpl->OPCODE_EXTERNAL) && rToken.Data.has< OUString >() )
+ pFuncInfo = mxOpCodeImpl->maExtProgFuncs.get( rToken.Data.get< OUString >() ).get();
+ else if( (rToken.OpCode == mxOpCodeImpl->OPCODE_MACRO) && rToken.Data.has< OUString >() )
+ pFuncInfo = getFuncInfoFromMacroName( rToken.Data.get< OUString >() );
+ else if( (rToken.OpCode == mxOpCodeImpl->OPCODE_BAD) && rToken.Data.has< OUString >() )
+ pFuncInfo = getFuncInfoFromOoxFuncName( rToken.Data.get< OUString >() );
+ else
+ pFuncInfo = mxOpCodeImpl->maOpCodeFuncs.get( rToken.OpCode ).get();
+ return pFuncInfo;
+}
+
+Sequence< FormulaOpCodeMapEntry > OpCodeProvider::getOoxParserMap() const
+{
+ return comphelper::containerToSequence( mxOpCodeImpl->maParserMap );
+}
+
+// API formula parser wrapper =================================================
+
+ApiParserWrapper::ApiParserWrapper(
+ const Reference< XMultiServiceFactory >& rxModelFactory, const OpCodeProvider& rOpCodeProv ) :
+ OpCodeProvider( rOpCodeProv )
+{
+ if( rxModelFactory.is() ) try
+ {
+ mxParser.set( rxModelFactory->createInstance( "com.sun.star.sheet.FormulaParser" ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( mxParser.is(), "ApiParserWrapper::ApiParserWrapper - cannot create API formula parser object" );
+ maParserProps.set( mxParser );
+ maParserProps.setProperty( PROP_CompileEnglish, true );
+ maParserProps.setProperty( PROP_FormulaConvention, css::sheet::AddressConvention::XL_OOX );
+ maParserProps.setProperty( PROP_IgnoreLeadingSpaces, false );
+ maParserProps.setProperty( PROP_OpCodeMap, getOoxParserMap() );
+}
+
+ApiTokenSequence ApiParserWrapper::parseFormula( const OUString& rFormula, const ScAddress& rRefPos )
+{
+ ApiTokenSequence aTokenSeq;
+ if( mxParser.is() ) try
+ {
+ aTokenSeq = mxParser->parseFormula( rFormula,
+ CellAddress(rRefPos.Tab(), rRefPos.Col(), rRefPos.Row()) );
+ }
+ catch( Exception& )
+ {
+ }
+ return aTokenSeq;
+}
+
+// formula parser/printer base class for filters ==============================
+
+namespace {
+
+bool lclConvertToCellAddress( ScAddress& orAddress, const SingleReference& rSingleRef, sal_Int32 nForbiddenFlags, sal_Int32 nFilterBySheet )
+{
+ orAddress = ScAddress( rSingleRef.Column, rSingleRef.Row, rSingleRef.Sheet );
+ return
+ !getFlag( rSingleRef.Flags, nForbiddenFlags ) &&
+ ((nFilterBySheet < 0) || (nFilterBySheet == rSingleRef.Sheet));
+}
+
+bool lclConvertToCellRange( ScRange& orRange, const ComplexReference& rComplexRef, sal_Int32 nForbiddenFlags, sal_Int32 nFilterBySheet )
+{
+ orRange = ScRange( rComplexRef.Reference1.Column, rComplexRef.Reference1.Row, rComplexRef.Reference1.Sheet,
+ rComplexRef.Reference2.Column, rComplexRef.Reference2.Row, rComplexRef.Reference2.Sheet );
+ return
+ !getFlag( rComplexRef.Reference1.Flags, nForbiddenFlags ) &&
+ !getFlag( rComplexRef.Reference2.Flags, nForbiddenFlags ) &&
+ (rComplexRef.Reference1.Sheet == rComplexRef.Reference2.Sheet) &&
+ ((nFilterBySheet < 0) || (nFilterBySheet == rComplexRef.Reference1.Sheet));
+}
+
+enum TokenToRangeListState { STATE_REF, STATE_SEP, STATE_OPEN, STATE_CLOSE, STATE_ERROR };
+
+TokenToRangeListState lclProcessRef( ScRangeList& orRanges, const Any& rData, sal_Int32 nFilterBySheet )
+{
+ using namespace ::com::sun::star::sheet::ReferenceFlags;
+ const sal_Int32 FORBIDDEN_FLAGS_REL = COLUMN_DELETED | ROW_DELETED | SHEET_DELETED |
+ COLUMN_RELATIVE | ROW_RELATIVE | SHEET_RELATIVE | RELATIVE_NAME;
+
+ sal_Int32 nForbiddenFlags = FORBIDDEN_FLAGS_REL;
+ SingleReference aSingleRef;
+ if( rData >>= aSingleRef )
+ {
+ ScAddress aAddress;
+ // ignore invalid addresses (with #REF! errors), but do not stop parsing
+ if( lclConvertToCellAddress( aAddress, aSingleRef, nForbiddenFlags, nFilterBySheet ) )
+ orRanges.push_back( ScRange(aAddress, aAddress) );
+ return STATE_REF;
+ }
+ ComplexReference aComplexRef;
+ if( rData >>= aComplexRef )
+ {
+ ScRange aRange;
+ // ignore invalid ranges (with #REF! errors), but do not stop parsing
+ if( lclConvertToCellRange( aRange, aComplexRef, nForbiddenFlags, nFilterBySheet ) )
+ orRanges.push_back( aRange );
+ return STATE_REF;
+ }
+ return STATE_ERROR;
+}
+
+TokenToRangeListState lclProcessOpen( sal_Int32& ornParenLevel )
+{
+ ++ornParenLevel;
+ return STATE_OPEN;
+}
+
+TokenToRangeListState lclProcessClose( sal_Int32& ornParenLevel )
+{
+ --ornParenLevel;
+ return (ornParenLevel >= 0) ? STATE_CLOSE : STATE_ERROR;
+}
+
+} // namespace
+
+FormulaProcessorBase::FormulaProcessorBase( const WorkbookHelper& rHelper ) :
+ OpCodeProvider( rHelper.getBaseFilter().getModelFactory(), rHelper.getBaseFilter().isImportFilter() ),
+ ApiOpCodes( getOpCodes() ),
+ WorkbookHelper( rHelper )
+{
+}
+
+OUString FormulaProcessorBase::generateAddress2dString( const ScAddress& rAddress, bool bAbsolute )
+{
+ return generateAddress2dString( BinAddress( rAddress ), bAbsolute );
+}
+
+OUString FormulaProcessorBase::generateAddress2dString( const BinAddress& rAddress, bool bAbsolute )
+{
+ OUStringBuffer aBuffer;
+ // column
+ for( sal_Int32 nTemp = rAddress.mnCol; nTemp >= 0; nTemp = (nTemp / 26) - 1 )
+ aBuffer.insert( 0, sal_Unicode( 'A' + (nTemp % 26) ) );
+ if( bAbsolute )
+ aBuffer.insert( 0, '$' );
+ // row
+ if( bAbsolute )
+ aBuffer.append( '$' );
+ aBuffer.append( static_cast< sal_Int32 >( rAddress.mnRow + 1 ) );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString FormulaProcessorBase::generateApiArray( const Matrix< Any >& rMatrix )
+{
+ OSL_ENSURE( !rMatrix.empty(), "FormulaProcessorBase::generateApiArray - missing matrix values" );
+ OUStringBuffer aBuffer(( OUStringChar(API_TOKEN_ARRAY_OPEN) ));
+ for( size_t nRow = 0, nHeight = rMatrix.height(); nRow < nHeight; ++nRow )
+ {
+ if( nRow > 0 )
+ aBuffer.append( API_TOKEN_ARRAY_ROWSEP );
+ for( Matrix< Any >::const_iterator aBeg = rMatrix.row_begin( nRow ), aIt = aBeg, aEnd = rMatrix.row_end( nRow ); aIt != aEnd; ++aIt )
+ {
+ double fValue = 0.0;
+ OUString aString;
+ if( aIt != aBeg )
+ aBuffer.append( API_TOKEN_ARRAY_COLSEP );
+ if( *aIt >>= fValue )
+ aBuffer.append( fValue );
+ else if( *aIt >>= aString )
+ {
+ // generate Api String
+ aBuffer.append( "\"" + aString.replaceAll(u"\"", u"\"\"") + "\"" );
+ }
+ else
+ aBuffer.append( "\"\"" );
+ }
+ }
+ aBuffer.append( API_TOKEN_ARRAY_CLOSE );
+ return aBuffer.makeStringAndClear();
+}
+
+Any FormulaProcessorBase::extractReference( const ApiTokenSequence& rTokens ) const
+{
+ ApiTokenIterator aTokenIt( rTokens, OPCODE_SPACES );
+ if( aTokenIt.is() && (aTokenIt->OpCode == OPCODE_PUSH) )
+ {
+ Any aRefAny = aTokenIt->Data;
+ if( !(++aTokenIt).is() && (aRefAny.has< SingleReference >() || aRefAny.has< ComplexReference >()) )
+ return aRefAny;
+ }
+ return Any();
+}
+
+bool FormulaProcessorBase::extractCellRange( ScRange& orRange,
+ const ApiTokenSequence& rTokens ) const
+{
+ ScRangeList aRanges;
+ lclProcessRef( aRanges, extractReference( rTokens ), -1 );
+ if( !aRanges.empty() )
+ {
+ orRange = aRanges.front();
+ return true;
+ }
+ return false;
+}
+
+void FormulaProcessorBase::extractCellRangeList( ScRangeList& orRanges,
+ const ApiTokenSequence& rTokens, sal_Int32 nFilterBySheet ) const
+{
+ orRanges.RemoveAll();
+ TokenToRangeListState eState = STATE_OPEN;
+ sal_Int32 nParenLevel = 0;
+ for( ApiTokenIterator aIt( rTokens, OPCODE_SPACES ); aIt.is() && (eState != STATE_ERROR); ++aIt )
+ {
+ sal_Int32 nOpCode = aIt->OpCode;
+ switch( eState )
+ {
+ // #i107275# accept OPCODE_SEP and OPCODE_LIST as separator token
+ case STATE_REF:
+ if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ case STATE_SEP:
+ if( nOpCode == OPCODE_PUSH ) eState = lclProcessRef( orRanges, aIt->Data, nFilterBySheet );
+ else if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_OPEN ) eState = lclProcessOpen( nParenLevel );
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ case STATE_OPEN:
+ if( nOpCode == OPCODE_PUSH ) eState = lclProcessRef( orRanges, aIt->Data, nFilterBySheet );
+ else if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_OPEN ) eState = lclProcessOpen( nParenLevel );
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ case STATE_CLOSE:
+ if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ default:;
+ }
+ }
+
+ if( eState == STATE_ERROR )
+ orRanges.RemoveAll();
+ else
+ getAddressConverter().validateCellRangeList( orRanges, false );
+}
+
+bool FormulaProcessorBase::extractString( OUString& orString, const ApiTokenSequence& rTokens ) const
+{
+ ApiTokenIterator aTokenIt( rTokens, OPCODE_SPACES );
+ return aTokenIt.is() && (aTokenIt->OpCode == OPCODE_PUSH) && (aTokenIt->Data >>= orString) && !(++aTokenIt).is();
+}
+
+bool FormulaProcessorBase::extractSpecialTokenInfo( ApiSpecialTokenInfo& orTokenInfo, const ApiTokenSequence& rTokens ) const
+{
+ ApiTokenIterator aTokenIt( rTokens, OPCODE_SPACES );
+ return aTokenIt.is() && (aTokenIt->OpCode == OPCODE_BAD) && (aTokenIt->Data >>= orTokenInfo);
+}
+
+void FormulaProcessorBase::convertStringToStringList(
+ ApiTokenSequence& orTokens, sal_Unicode cStringSep, bool bTrimLeadingSpaces ) const
+{
+ OUString aString;
+ if( !extractString( aString, orTokens ) || aString.isEmpty() )
+ return;
+
+ ::std::vector< ApiToken > aNewTokens;
+ for( sal_Int32 nPos{ 0 }; nPos>=0; )
+ {
+ OUString aEntry = aString.getToken( 0, cStringSep, nPos );
+ if( bTrimLeadingSpaces )
+ {
+ sal_Int32 nStart = 0;
+ while( (nStart < aEntry.getLength()) && (aEntry[ nStart ] == ' ') ) ++nStart;
+ aEntry = aEntry.copy( nStart );
+ }
+ if( !aNewTokens.empty() )
+ aNewTokens.emplace_back( OPCODE_SEP, Any() );
+ aNewTokens.emplace_back( OPCODE_PUSH, Any( aEntry ) );
+ }
+ orTokens = comphelper::containerToSequence( aNewTokens );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/formulabuffer.cxx b/sc/source/filter/oox/formulabuffer.cxx
new file mode 100644
index 0000000000..fae50d9305
--- /dev/null
+++ b/sc/source/filter/oox/formulabuffer.cxx
@@ -0,0 +1,519 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <formulabuffer.hxx>
+#include <externallinkbuffer.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+
+#include <autonamecache.hxx>
+#include <tokenarray.hxx>
+#include <sharedformulagroups.hxx>
+#include <externalrefmgr.hxx>
+#include <tokenstringcontext.hxx>
+#include <o3tl/safeint.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/helper/progressbar.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <memory>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sheet;
+
+namespace oox::xls {
+
+namespace {
+
+/**
+ * Cache the token array for the last cell position in each column. We use
+ * one cache per sheet.
+ */
+class CachedTokenArray
+{
+public:
+ CachedTokenArray(const CachedTokenArray&) = delete;
+ const CachedTokenArray& operator=(const CachedTokenArray&) = delete;
+
+ struct Item
+ {
+ SCROW mnRow;
+ ScFormulaCell* mpCell;
+
+ Item(const Item&) = delete;
+ const Item& operator=(const Item&) = delete;
+
+ Item() : mnRow(-1), mpCell(nullptr) {}
+ };
+
+ explicit CachedTokenArray( const ScDocument& rDoc ) :
+ maCxt(rDoc, formula::FormulaGrammar::GRAM_OOXML) {}
+
+ Item* get( const ScAddress& rPos, std::u16string_view rFormula )
+ {
+ // Check if a token array is cached for this column.
+ ColCacheType::iterator it = maCache.find(rPos.Col());
+ if (it == maCache.end())
+ return nullptr;
+
+ Item& rCached = *it->second;
+ const ScTokenArray& rCode = *rCached.mpCell->GetCode();
+ OUString aPredicted = rCode.CreateString(maCxt, rPos);
+ if (rFormula == aPredicted)
+ return &rCached;
+
+ return nullptr;
+ }
+
+ void store( const ScAddress& rPos, ScFormulaCell* pCell )
+ {
+ ColCacheType::iterator it = maCache.find(rPos.Col());
+ if (it == maCache.end())
+ {
+ // Create an entry for this column.
+ std::pair<ColCacheType::iterator,bool> r =
+ maCache.emplace(rPos.Col(), std::make_unique<Item>());
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ it = r.first;
+ }
+
+ Item& rItem = *it->second;
+ rItem.mnRow = rPos.Row();
+ rItem.mpCell = pCell;
+ }
+
+private:
+ typedef std::unordered_map<SCCOL, std::unique_ptr<Item>> ColCacheType;
+ ColCacheType maCache;
+ sc::TokenStringContext maCxt;
+};
+
+void applySharedFormulas(
+ ScDocumentImport& rDoc,
+ SvNumberFormatter& rFormatter,
+ std::vector<FormulaBuffer::SharedFormulaEntry>& rSharedFormulas,
+ std::vector<FormulaBuffer::SharedFormulaDesc>& rCells,
+ WorkbookHelper& rWorkbookHelper)
+{
+ sc::SharedFormulaGroups aGroups;
+ {
+ // Process shared formulas first.
+ for (const FormulaBuffer::SharedFormulaEntry& rEntry : rSharedFormulas)
+ {
+ const ScAddress& aPos = rEntry.maAddress;
+ sal_Int32 nId = rEntry.mnSharedId;
+ const OUString& rTokenStr = rEntry.maTokenStr;
+
+ ScCompiler aComp(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML, true, false);
+ aComp.SetNumberFormatter(&rFormatter);
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(rTokenStr);
+ if (pArray)
+ {
+ aComp.CompileTokenArray(); // Generate RPN tokens.
+ aGroups.set(nId, std::move(pArray), aPos);
+ }
+ }
+ }
+
+ {
+ svl::SharedStringPool& rStrPool = rDoc.getDoc().GetSharedStringPool();
+ const bool bGeneratorKnownGood = rWorkbookHelper.isGeneratorKnownGood();
+ bool bHasCalculatedFormulaCells = rWorkbookHelper.hasCalculatedFormulaCells();
+ // Process formulas that use shared formulas.
+ for (const FormulaBuffer::SharedFormulaDesc& rDesc : rCells)
+ {
+ const ScAddress& aPos = rDesc.maAddress;
+ const sc::SharedFormulaGroupEntry* pEntry = aGroups.getEntry(rDesc.mnSharedId);
+ if (!pEntry)
+ continue;
+
+ const ScTokenArray* pArray = pEntry->getTokenArray();
+ assert(pArray);
+ const ScAddress& rOrigin = pEntry->getOrigin();
+ assert(rOrigin.IsValid());
+
+ ScFormulaCell* pCell;
+ // In case of shared-formula along a row, do not let
+ // these cells share the same token objects.
+ // If we do, any reference-updates on these cells
+ // (while editing) will mess things up. Pass the cloned array as a
+ // pointer and not as reference to avoid any further allocation.
+ if (rOrigin.Col() != aPos.Col())
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, pArray->Clone());
+ else
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, *pArray);
+
+ rDoc.setFormulaCell(aPos, pCell);
+ const bool bNeedNumberFormat = ((rDoc.getDoc().GetNumberFormat(
+ aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET) == 0);
+ if (bNeedNumberFormat)
+ pCell->SetNeedNumberFormat(true);
+
+ if (rDesc.maCellValue.isEmpty())
+ {
+ // No cached cell value. Mark it for re-calculation.
+ pCell->SetDirty();
+ // Recalc even if AutoCalc is disabled. Must be after
+ // SetDirty() as it also calls SetDirtyVar().
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE);
+ continue;
+ }
+
+ // Set cached formula results. For now, we only use boolean,
+ // numeric and string-formula results. Find out how to utilize
+ // cached results of other types.
+ switch (rDesc.mnValueType)
+ {
+ case XML_b:
+ // boolean value.
+ if (bNeedNumberFormat)
+ {
+ rDoc.getDoc().SetNumberFormat( aPos,
+ rDoc.getDoc().GetFormatTable()->GetStandardFormat( SvNumFormatType::LOGICAL));
+ }
+ if (rDesc.maCellValue == "1" || rDesc.maCellValue == "0")
+ pCell->SetResultDouble(rDesc.maCellValue == "1" ? 1.0 : 0.0);
+ else
+ {
+ // Recalc even if AutoCalc is disabled.
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE);
+ }
+ break;
+ case XML_n:
+ // numeric value.
+ {
+ const double fVal = rDesc.maCellValue.toDouble();
+ if (!bHasCalculatedFormulaCells && fVal != 0.0)
+ {
+ rWorkbookHelper.setCalculatedFormulaCells();
+ bHasCalculatedFormulaCells = true;
+ }
+ pCell->SetResultDouble(fVal);
+ /* TODO: is it on purpose that we never reset dirty here
+ * and thus recalculate anyway if cell was dirty? Or is it
+ * never dirty and therefore set dirty below otherwise? This
+ * is different from the non-shared case in
+ * applyCellFormulaValues(). */
+ }
+ break;
+ case XML_str:
+ if (bGeneratorKnownGood)
+ {
+ // See applyCellFormulaValues
+ svl::SharedString aSS = rStrPool.intern(rDesc.maCellValue);
+ pCell->SetResultToken(new formula::FormulaStringToken(std::move(aSS)));
+ // If we don't reset dirty, then e.g. disabling macros makes all cells
+ // that use macro functions to show #VALUE!
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ // Mark it for re-calculation.
+ pCell->SetDirty();
+ // Recalc even if AutoCalc is disabled. Must be after
+ // SetDirty() as it also calls SetDirtyVar().
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE);
+ }
+ }
+ }
+}
+
+void applyCellFormulas(
+ ScDocumentImport& rDoc, CachedTokenArray& rCache, SvNumberFormatter& rFormatter,
+ const Sequence<ExternalLinkInfo>& rExternalLinks,
+ const std::vector<FormulaBuffer::TokenAddressItem>& rCells )
+{
+ for (const FormulaBuffer::TokenAddressItem& rItem : rCells)
+ {
+ const ScAddress& aPos = rItem.maAddress;
+ CachedTokenArray::Item* p = rCache.get(aPos, rItem.maTokenStr);
+ if (p)
+ {
+ // Use the cached version to avoid re-compilation.
+
+ ScFormulaCell* pCell = nullptr;
+ if (p->mnRow + 1 == aPos.Row())
+ {
+ // Put them in the same formula group.
+ ScFormulaCell& rPrev = *p->mpCell;
+ ScFormulaCellGroupRef xGroup = rPrev.GetCellGroup();
+ if (!xGroup)
+ {
+ // Last cell is not grouped yet. Start a new group.
+ assert(rPrev.aPos.Row() == p->mnRow);
+ xGroup = rPrev.CreateCellGroup(1, false);
+ }
+ ++xGroup->mnLength;
+
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, xGroup);
+ }
+ else
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, p->mpCell->GetCode()->Clone());
+
+ rDoc.setFormulaCell(aPos, pCell);
+ if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
+ pCell->SetNeedNumberFormat(true);
+
+ // Update the cache.
+ p->mnRow = aPos.Row();
+ p->mpCell = pCell;
+ continue;
+ }
+
+ ScCompiler aCompiler(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML, true, false);
+ aCompiler.SetNumberFormatter(&rFormatter);
+ aCompiler.SetExternalLinks(rExternalLinks);
+ std::unique_ptr<ScTokenArray> pCode = aCompiler.CompileString(rItem.maTokenStr);
+ if (!pCode)
+ continue;
+
+ aCompiler.CompileTokenArray(); // Generate RPN tokens.
+
+ ScFormulaCell* pCell = new ScFormulaCell(rDoc.getDoc(), aPos, std::move(pCode));
+ rDoc.setFormulaCell(aPos, pCell);
+ if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
+ pCell->SetNeedNumberFormat(true);
+ rCache.store(aPos, pCell);
+ }
+}
+
+void applyArrayFormulas(
+ ScDocumentImport& rDoc, SvNumberFormatter& rFormatter,
+ const Sequence<ExternalLinkInfo>& rExternalLinks,
+ const std::vector<FormulaBuffer::TokenRangeAddressItem>& rArrays )
+{
+ for (const FormulaBuffer::TokenRangeAddressItem& rAddressItem : rArrays)
+ {
+ const ScAddress& aPos = rAddressItem.maTokenAndAddress.maAddress;
+
+ ScCompiler aComp(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML);
+ aComp.SetNumberFormatter(&rFormatter);
+ aComp.SetExternalLinks(rExternalLinks);
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(rAddressItem.maTokenAndAddress.maTokenStr));
+ if (pArray)
+ rDoc.setMatrixCells(rAddressItem.maRange, *pArray, formula::FormulaGrammar::GRAM_OOXML);
+ }
+}
+
+void applyCellFormulaValues(
+ ScDocumentImport& rDoc, const std::vector<FormulaBuffer::FormulaValue>& rVector, WorkbookHelper& rWorkbookHelper )
+{
+ svl::SharedStringPool& rStrPool = rDoc.getDoc().GetSharedStringPool();
+ const bool bGeneratorKnownGood = rWorkbookHelper.isGeneratorKnownGood();
+ bool bHasCalculatedFormulaCells = rWorkbookHelper.hasCalculatedFormulaCells();
+
+ for (const FormulaBuffer::FormulaValue& rValue : rVector)
+ {
+ const ScAddress& aCellPos = rValue.maAddress;
+ ScFormulaCell* pCell = rDoc.getDoc().GetFormulaCell(aCellPos);
+ const OUString& rValueStr = rValue.maValueStr;
+ if (!pCell)
+ continue;
+
+ switch (rValue.mnCellType)
+ {
+ case XML_n:
+ {
+ const double fVal = rValueStr.toDouble();
+ if (!bHasCalculatedFormulaCells && fVal != 0.0)
+ {
+ rWorkbookHelper.setCalculatedFormulaCells();
+ bHasCalculatedFormulaCells = true;
+ }
+ pCell->SetResultDouble(fVal);
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ }
+ break;
+ case XML_str:
+ // Excel uses t="str" for string results (per definition
+ // ECMA-376 18.18.11 ST_CellType (Cell Type) "Cell containing a
+ // formula string.", but that 't' Cell Data Type attribute, "an
+ // enumeration representing the cell's data type", is meant for
+ // the content of the <v> element). We follow that. Other
+ // applications might not and instead use t="str" for the cell
+ // content if formula. Setting an otherwise numeric result as
+ // string result fouls things up, set result strings only for
+ // documents claiming to be generated by a known good
+ // generator. See tdf#98481
+ if (bGeneratorKnownGood)
+ {
+ svl::SharedString aSS = rStrPool.intern(rValueStr);
+ pCell->SetResultToken(new formula::FormulaStringToken(std::move(aSS)));
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void processSheetFormulaCells(
+ ScDocumentImport& rDoc, FormulaBuffer::SheetItem& rItem, SvNumberFormatter& rFormatter,
+ const Sequence<ExternalLinkInfo>& rExternalLinks, WorkbookHelper& rWorkbookHelper )
+{
+ if (rItem.mpSharedFormulaEntries && rItem.mpSharedFormulaIDs)
+ applySharedFormulas(rDoc, rFormatter, *rItem.mpSharedFormulaEntries,
+ *rItem.mpSharedFormulaIDs, rWorkbookHelper);
+
+ if (rItem.mpCellFormulas)
+ {
+ CachedTokenArray aCache(rDoc.getDoc());
+ applyCellFormulas(rDoc, aCache, rFormatter, rExternalLinks, *rItem.mpCellFormulas);
+ }
+
+ if (rItem.mpArrayFormulas)
+ applyArrayFormulas(rDoc, rFormatter, rExternalLinks, *rItem.mpArrayFormulas);
+
+ if (rItem.mpCellFormulaValues)
+ applyCellFormulaValues(rDoc, *rItem.mpCellFormulaValues, rWorkbookHelper);
+}
+
+}
+
+FormulaBuffer::SharedFormulaEntry::SharedFormulaEntry(
+ const ScAddress& rAddr,
+ OUString aTokenStr, sal_Int32 nSharedId ) :
+ maAddress(rAddr), maTokenStr(std::move(aTokenStr)), mnSharedId(nSharedId) {}
+
+FormulaBuffer::SharedFormulaDesc::SharedFormulaDesc(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ OUString aCellValue, sal_Int32 nValueType ) :
+ maAddress(rAddr), maCellValue(std::move(aCellValue)), mnSharedId(nSharedId), mnValueType(nValueType) {}
+
+FormulaBuffer::SheetItem::SheetItem() :
+ mpCellFormulas(nullptr),
+ mpArrayFormulas(nullptr),
+ mpCellFormulaValues(nullptr),
+ mpSharedFormulaEntries(nullptr),
+ mpSharedFormulaIDs(nullptr) {}
+
+FormulaBuffer::FormulaBuffer( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper )
+{
+}
+
+void FormulaBuffer::SetSheetCount( SCTAB nSheets )
+{
+ maCellFormulas.resize( nSheets );
+ maCellArrayFormulas.resize( nSheets );
+ maSharedFormulas.resize( nSheets );
+ maSharedFormulaIds.resize( nSheets );
+ maCellFormulaValues.resize( nSheets );
+}
+
+void FormulaBuffer::finalizeImport()
+{
+ ISegmentProgressBarRef xFormulaBar = getProgressBar().createSegment( getProgressBar().getFreeLength() );
+
+ ScDocumentImport& rDoc = getDocImport();
+ rDoc.getDoc().SetAutoNameCache(std::make_unique<ScAutoNameCache>(rDoc.getDoc()));
+ ScExternalRefManager::ApiGuard aExtRefGuard(rDoc.getDoc());
+
+ SCTAB nTabCount = rDoc.getDoc().GetTableCount();
+
+ // Fetch all the formulas to process first.
+ std::vector<SheetItem> aSheetItems;
+ aSheetItems.reserve(nTabCount);
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ aSheetItems.push_back(getSheetItem(nTab));
+
+ for (SheetItem& rItem : aSheetItems)
+ processSheetFormulaCells(rDoc, rItem, *rDoc.getDoc().GetFormatTable(), getExternalLinks().getLinkInfos(),
+ *this);
+
+ // With formula results being set and not recalculated we need to
+ // force-trigger adding all linked external files to the LinkManager.
+ rDoc.getDoc().GetExternalRefManager()->addFilesToLinkManager();
+
+ rDoc.getDoc().SetAutoNameCache(nullptr);
+
+ xFormulaBar->setPosition( 1.0 );
+}
+
+FormulaBuffer::SheetItem FormulaBuffer::getSheetItem( SCTAB nTab )
+{
+ std::scoped_lock aGuard(maMtxData);
+
+ SheetItem aItem;
+
+ if( o3tl::make_unsigned(nTab) >= maCellFormulas.size() )
+ {
+ SAL_WARN( "sc", "Tab " << nTab << " out of bounds " << maCellFormulas.size() );
+ return aItem;
+ }
+
+ if( !maCellFormulas[ nTab ].empty() )
+ aItem.mpCellFormulas = &maCellFormulas[ nTab ];
+ if( !maCellArrayFormulas[ nTab ].empty() )
+ aItem.mpArrayFormulas = &maCellArrayFormulas[ nTab ];
+ if( !maCellFormulaValues[ nTab ].empty() )
+ aItem.mpCellFormulaValues = &maCellFormulaValues[ nTab ];
+ if( !maSharedFormulas[ nTab ].empty() )
+ aItem.mpSharedFormulaEntries = &maSharedFormulas[ nTab ];
+ if( !maSharedFormulaIds[ nTab ].empty() )
+ aItem.mpSharedFormulaIDs = &maSharedFormulaIds[ nTab ];
+
+ return aItem;
+}
+
+void FormulaBuffer::createSharedFormulaMapEntry(
+ const ScAddress& rAddress,
+ sal_Int32 nSharedId, const OUString& rTokens )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maSharedFormulas.size() );
+ std::vector<SharedFormulaEntry>& rSharedFormulas = maSharedFormulas[ rAddress.Tab() ];
+ SharedFormulaEntry aEntry(rAddress, rTokens, nSharedId);
+ rSharedFormulas.push_back( aEntry );
+}
+
+void FormulaBuffer::setCellFormula( const ScAddress& rAddress, const OUString& rTokenStr )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maCellFormulas.size() );
+ maCellFormulas[ rAddress.Tab() ].emplace_back( rTokenStr, rAddress );
+}
+
+void FormulaBuffer::setCellFormula(
+ const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rCellValue, sal_Int32 nValueType )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maSharedFormulaIds.size() );
+ maSharedFormulaIds[rAddress.Tab()].emplace_back(rAddress, nSharedId, rCellValue, nValueType);
+}
+
+void FormulaBuffer::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr )
+{
+
+ TokenAddressItem tokenPair( rTokenStr, rTokenAddress );
+ assert( rRangeAddress.aStart.Tab() >= 0 && o3tl::make_unsigned(rRangeAddress.aStart.Tab()) < maCellArrayFormulas.size() );
+ maCellArrayFormulas[ rRangeAddress.aStart.Tab() ].emplace_back( tokenPair, rRangeAddress );
+}
+
+void FormulaBuffer::setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maCellFormulaValues.size() );
+ FormulaValue aVal;
+ aVal.maAddress = rAddress;
+ aVal.maValueStr = rValueStr;
+ aVal.mnCellType = nCellType;
+ maCellFormulaValues[rAddress.Tab()].push_back(aVal);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/formulaparser.cxx b/sc/source/filter/oox/formulaparser.cxx
new file mode 100644
index 0000000000..b832fa9636
--- /dev/null
+++ b/sc/source/filter/oox/formulaparser.cxx
@@ -0,0 +1,1858 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formulaparser.hxx>
+
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/ExternalReference.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/SingleReference.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <defnamesbuffer.hxx>
+#include <externallinkbuffer.hxx>
+#include <tablebuffer.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::sheet::ReferenceFlags;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+// formula finalizer ==========================================================
+
+FormulaFinalizer::FormulaFinalizer( const OpCodeProvider& rOpCodeProv ) :
+ OpCodeProvider( rOpCodeProv ),
+ ApiOpCodes( getOpCodes() )
+{
+ maTokens.reserve( 0x2000 );
+}
+
+ApiTokenSequence FormulaFinalizer::finalizeTokenArray( const ApiTokenSequence& rTokens )
+{
+ maTokens.clear();
+ if( rTokens.hasElements() )
+ {
+ const ApiToken* pToken = rTokens.getConstArray();
+ processTokens( pToken, pToken + rTokens.getLength() );
+ }
+ return maTokens.toSequence();
+}
+
+const FunctionInfo* FormulaFinalizer::resolveBadFuncName( const OUString& ) const
+{
+ return nullptr;
+}
+
+OUString FormulaFinalizer::resolveDefinedName( sal_Int32 ) const
+{
+ return OUString();
+}
+
+const FunctionInfo* FormulaFinalizer::getFunctionInfo( ApiToken& orFuncToken )
+{
+ // first, try to find a regular function info from token op-code
+ if( const FunctionInfo* pRegFuncInfo = getFuncInfoFromApiToken( orFuncToken ) )
+ return pRegFuncInfo;
+
+ // try to recognize a function from an external library
+ if( (orFuncToken.OpCode == OPCODE_BAD) && orFuncToken.Data.has< OUString >() )
+ {
+ // virtual call to resolveBadFuncName()
+ if( const FunctionInfo* pLibFuncInfo = resolveBadFuncName( orFuncToken.Data.get< OUString >() ) )
+ {
+ // write function op-code to the OPCODE_BAD token
+ orFuncToken.OpCode = pLibFuncInfo->mnApiOpCode;
+ // if it is an external function, insert programmatic function name
+ if( (orFuncToken.OpCode == OPCODE_EXTERNAL) && (!pLibFuncInfo->maExtProgName.isEmpty()) )
+ orFuncToken.Data <<= pLibFuncInfo->maExtProgName;
+ else
+ orFuncToken.Data.clear(); // clear string from OPCODE_BAD
+ return pLibFuncInfo;
+ }
+ }
+
+ // no success - return null
+ return nullptr;
+}
+
+const FunctionInfo* FormulaFinalizer::getExternCallInfo( ApiToken& orFuncToken, const ApiToken& rECToken )
+{
+ // try to resolve the passed token to a supported sheet function
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromApiToken( rECToken ) )
+ {
+ orFuncToken.OpCode = pFuncInfo->mnApiOpCode;
+ // programmatic add-in function name
+ if( (pFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && !pFuncInfo->maExtProgName.isEmpty() )
+ orFuncToken.Data <<= pFuncInfo->maExtProgName;
+ // name of unsupported function, convert to OPCODE_BAD to preserve the name
+ else if( (pFuncInfo->mnApiOpCode == OPCODE_BAD) && !pFuncInfo->maOoxFuncName.isEmpty() )
+ orFuncToken.Data <<= pFuncInfo->maOoxFuncName;
+ return pFuncInfo;
+ }
+
+ // macro call or unknown function name, move data to function token
+ if( (rECToken.OpCode == OPCODE_MACRO) || (rECToken.OpCode == OPCODE_BAD) )
+ orFuncToken = rECToken;
+
+ // defined name used as function call, convert to OPCODE_BAD to preserve the name
+ if( (rECToken.OpCode == OPCODE_NAME) && rECToken.Data.has< sal_Int32 >() )
+ {
+ OUString aDefName = resolveDefinedName( rECToken.Data.get< sal_Int32 >() );
+ if( !aDefName.isEmpty() )
+ {
+ orFuncToken.OpCode = OPCODE_BAD;
+ orFuncToken.Data <<= aDefName;
+ }
+ }
+
+ return nullptr;
+}
+
+void FormulaFinalizer::processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd )
+{
+ while( pToken < pTokenEnd )
+ {
+ // push the current token into the vector
+ bool bValid = appendFinalToken( *pToken );
+ // try to process a function
+ if( const FunctionInfo* pFuncInfo = bValid ? getFunctionInfo( maTokens.back() ) : nullptr )
+ pToken = processParameters( *pFuncInfo, pToken + 1, pTokenEnd );
+ // otherwise, go to next token
+ else
+ ++pToken;
+ }
+}
+
+const ApiToken* FormulaFinalizer::processParameters(
+ const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd )
+{
+ // remember position of the token containing the function op-code
+ size_t nFuncNameIdx = maTokens.size() - 1;
+
+ // process a function, if an OPCODE_OPEN token is following
+ OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaFinalizer::processParameters - OPCODE_OPEN expected" );
+ if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN) )
+ {
+ // append the OPCODE_OPEN token to the vector
+ maTokens.append( OPCODE_OPEN );
+
+ // store positions of OPCODE_OPEN, parameter separators, and OPCODE_CLOSE
+ ParameterPosVector aParams;
+ pToken = findParameters( aParams, pToken, pTokenEnd );
+ OSL_ENSURE( aParams.size() >= 2, "FormulaFinalizer::processParameters - missing tokens" );
+ size_t nParamCount = aParams.size() - 1;
+
+ if( (nParamCount == 1) && isEmptyParameter( aParams[ 0 ] + 1, aParams[ 1 ] ) )
+ {
+ /* Empty pair of parentheses -> function call without parameters,
+ process parameter, there might be spaces between parentheses. */
+ processTokens( aParams[ 0 ] + 1, aParams[ 1 ] );
+ }
+ else
+ {
+ const FunctionInfo* pRealFuncInfo = &rFuncInfo;
+ ParameterPosVector::const_iterator aPosIt = aParams.begin();
+
+ /* Preprocess EXTERN.CALL functions. The actual function name is
+ contained as reference to a defined name in the first (hidden)
+ parameter. */
+ if( rFuncInfo.mnBiffFuncId == BIFF_FUNC_EXTERNCALL )
+ {
+ ApiToken& rFuncToken = maTokens[ nFuncNameIdx ];
+ rFuncToken.OpCode = OPCODE_NONAME;
+
+ // try to initialize function token from first parameter
+ if( const ApiToken* pECToken = getSingleToken( *aPosIt + 1, *(aPosIt + 1) ) )
+ if( const FunctionInfo* pECFuncInfo = getExternCallInfo( rFuncToken, *pECToken ) )
+ pRealFuncInfo = pECFuncInfo;
+
+ /* On success (something has been inserted into rFuncToken),
+ skip the first parameter. */
+ if( rFuncToken.OpCode != OPCODE_NONAME )
+ {
+ --nParamCount;
+ ++aPosIt;
+ }
+ }
+
+ // process all parameters
+ FunctionParamInfoIterator aParamInfoIt( *pRealFuncInfo );
+ size_t nLastValidSize = maTokens.size();
+ size_t nLastValidCount = 0;
+ for( size_t nParam = 0; nParam < nParamCount; ++nParam, ++aPosIt, ++aParamInfoIt )
+ {
+ // add embedded Calc-only parameters
+ if( aParamInfoIt.isCalcOnlyParam() )
+ {
+ appendCalcOnlyParameter( *pRealFuncInfo, nParam, nParamCount );
+ while( aParamInfoIt.isCalcOnlyParam() ) ++aParamInfoIt;
+ }
+
+ const ApiToken* pParamBegin = *aPosIt + 1;
+ const ApiToken* pParamEnd = *(aPosIt + 1);
+ bool bIsEmpty = isEmptyParameter( pParamBegin, pParamEnd );
+
+ if( !aParamInfoIt.isExcelOnlyParam() )
+ {
+ // handle empty parameters
+ if( bIsEmpty )
+ {
+ // append leading space tokens from original token array
+ while( (pParamBegin < pParamEnd) && (pParamBegin->OpCode == OPCODE_SPACES) )
+ maTokens.push_back( *pParamBegin++ );
+ // add default values for some empty parameters, or the OPCODE_MISSING token
+ appendEmptyParameter( *pRealFuncInfo, nParam );
+ // reset bIsEmpty flag, if something has been appended in appendEmptyParameter()
+ bIsEmpty = maTokens.back().OpCode == OPCODE_MISSING;
+ // skip OPCODE_MISSING token in the original token array
+ OSL_ENSURE( (pParamBegin == pParamEnd) || (pParamBegin->OpCode == OPCODE_MISSING), "FormulaFinalizer::processParameters - OPCODE_MISSING expected" );
+ if( pParamBegin < pParamEnd ) ++pParamBegin;
+ // append trailing space tokens from original token array
+ while( (pParamBegin < pParamEnd) && (pParamBegin->OpCode == OPCODE_SPACES) )
+ maTokens.push_back( *pParamBegin++ );
+ }
+ else
+ {
+ // if parameter is not empty, process all tokens of the parameter
+ processTokens( pParamBegin, pParamEnd );
+ }
+
+ // append parameter separator token
+ maTokens.append( OPCODE_SEP );
+ }
+
+ /* #84453# Update size of new token sequence with valid parameters
+ to be able to remove trailing optional empty parameters. */
+ if( !bIsEmpty || (nParam < pRealFuncInfo->mnMinParamCount) )
+ {
+ nLastValidSize = maTokens.size();
+ nLastValidCount = nParam + 1;
+ }
+ }
+
+ // #84453# remove trailing optional empty parameters
+ maTokens.resize( nLastValidSize );
+
+ // add trailing Calc-only parameters
+ if( aParamInfoIt.isCalcOnlyParam() )
+ appendCalcOnlyParameter( *pRealFuncInfo, nLastValidCount, nParamCount );
+
+ // add optional parameters that are required in Calc
+ appendRequiredParameters( *pRealFuncInfo, nLastValidCount );
+
+ // remove last parameter separator token
+ if( maTokens.back().OpCode == OPCODE_SEP )
+ maTokens.pop_back();
+ }
+
+ /* Append the OPCODE_CLOSE token to the vector, but only if there is
+ no OPCODE_BAD token at the end, this token already contains the
+ trailing closing parentheses. */
+ if( (pTokenEnd - 1)->OpCode != OPCODE_BAD )
+ maTokens.append( OPCODE_CLOSE );
+ }
+
+ /* Replace OPCODE_EXTERNAL with OPCODE_NONAME to get #NAME! error in cell,
+ if no matching add-in function was found. */
+ ApiToken& rFuncNameToken = maTokens[ nFuncNameIdx ];
+ if( (rFuncNameToken.OpCode == OPCODE_EXTERNAL) && !rFuncNameToken.Data.hasValue() )
+ rFuncNameToken.OpCode = OPCODE_NONAME;
+
+ return pToken;
+}
+
+bool FormulaFinalizer::isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_MISSING) ) ++pToken;
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ return pToken == pTokenEnd;
+}
+
+const ApiToken* FormulaFinalizer::getSingleToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ const ApiToken* pSingleToken = nullptr;
+ // skip leading whitespace tokens
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ // remember first non-whitespace token
+ if( pToken < pTokenEnd ) pSingleToken = pToken++;
+ // skip trailing whitespace tokens
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ // return null, if other non-whitespace tokens follow
+ return (pToken == pTokenEnd) ? pSingleToken : nullptr;
+}
+
+const ApiToken* FormulaFinalizer::skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ // skip tokens between OPCODE_OPEN and OPCODE_CLOSE
+ OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "skipParentheses - OPCODE_OPEN expected" );
+ ++pToken;
+ while( (pToken < pTokenEnd) && (pToken->OpCode != OPCODE_CLOSE) )
+ {
+ if( pToken->OpCode == OPCODE_OPEN )
+ pToken = skipParentheses( pToken, pTokenEnd );
+ else
+ ++pToken;
+ }
+ // skip the OPCODE_CLOSE token
+ OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == OPCODE_BAD), "skipParentheses - OPCODE_CLOSE expected" );
+ return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd;
+}
+
+const ApiToken* FormulaFinalizer::findParameters( ParameterPosVector& rParams,
+ const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ // push position of OPCODE_OPEN
+ OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaFinalizer::findParameters - OPCODE_OPEN expected" );
+ rParams.push_back( pToken++ );
+
+ // find positions of parameter separators
+ while( (pToken < pTokenEnd) && (pToken->OpCode != OPCODE_CLOSE) )
+ {
+ if( pToken->OpCode == OPCODE_OPEN )
+ pToken = skipParentheses( pToken, pTokenEnd );
+ else if( pToken->OpCode == OPCODE_SEP )
+ rParams.push_back( pToken++ );
+ else
+ ++pToken;
+ }
+
+ // push position of OPCODE_CLOSE
+ OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == OPCODE_BAD), "FormulaFinalizer::findParameters - OPCODE_CLOSE expected" );
+ rParams.push_back( pToken );
+ return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd;
+}
+
+void FormulaFinalizer::appendEmptyParameter( const FunctionInfo& rFuncInfo, size_t nParam )
+{
+ // remember old size of the token array
+ size_t nTokenArraySize = maTokens.size();
+
+ switch( rFuncInfo.mnBiff12FuncId )
+ {
+ case BIFF_FUNC_IF:
+ if( (nParam == 1) || (nParam == 2) )
+ maTokens.append< double >( OPCODE_PUSH, 0.0 );
+ break;
+ default:;
+ }
+
+ // if no token has been added, append an OPCODE_MISSING token
+ if( nTokenArraySize == maTokens.size() )
+ maTokens.append( OPCODE_MISSING );
+}
+
+void FormulaFinalizer::appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam, size_t nParamCount )
+{
+ switch( rFuncInfo.mnBiff12FuncId )
+ {
+ case BIFF_FUNC_FLOOR:
+ case BIFF_FUNC_CEILING:
+ if (nParam == 2 && nParamCount < 3)
+ {
+ maTokens.append< double >( OPCODE_PUSH, 1.0 );
+ maTokens.append( OPCODE_SEP );
+ }
+ break;
+ }
+}
+
+void FormulaFinalizer::appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount )
+{
+ switch( rFuncInfo.mnBiff12FuncId )
+ {
+ case BIFF_FUNC_WEEKNUM:
+ if( nParamCount == 1 )
+ {
+ maTokens.append< double >( OPCODE_PUSH, 1.0 );
+ maTokens.append( OPCODE_SEP );
+ }
+ break;
+ }
+}
+
+bool FormulaFinalizer::appendFinalToken( const ApiToken& rToken )
+{
+ // replace OPCODE_MACRO without macro name with #NAME? error code
+ bool bValid = (rToken.OpCode != OPCODE_MACRO) || rToken.Data.hasValue();
+ if( bValid )
+ {
+ maTokens.push_back( rToken );
+ }
+ else
+ {
+ maTokens.append( OPCODE_ARRAY_OPEN );
+ maTokens.append( OPCODE_PUSH, BiffHelper::calcDoubleFromError( BIFF_ERR_NAME ) );
+ maTokens.append( OPCODE_ARRAY_CLOSE );
+ }
+ return bValid;
+}
+
+// parser implementation base =================================================
+
+class FormulaParserImpl : public FormulaFinalizer, public WorkbookHelper
+{
+public:
+ explicit FormulaParserImpl( const FormulaParser& rParent );
+
+ /** Converts an OOXML formula string. */
+ virtual ApiTokenSequence importOoxFormula(
+ const ScAddress& rBaseAddress,
+ const OUString& rFormulaString );
+
+ /** Imports and converts a BIFF12 token array from the passed stream. */
+ virtual ApiTokenSequence importBiff12Formula(
+ const ScAddress& rBaseAddress,
+ FormulaType eType,
+ SequenceInputStream& rStrm );
+
+ /** Tries to resolve the passed ref-id to an OLE target URL. */
+ OUString resolveOleTarget( sal_Int32 nRefId, bool bUseRefSheets ) const;
+
+protected:
+ typedef ::std::pair< sal_Int32, bool > WhiteSpace;
+ typedef ::std::vector< WhiteSpace > WhiteSpaceVec;
+
+ /** Initializes the formula parser before importing a formula. */
+ void initializeImport( const ScAddress& rBaseAddress, FormulaType eType );
+ /** Finalizes the internal token storage after import. */
+ ApiTokenSequence finalizeImport();
+
+ // token array ------------------------------------------------------------
+
+ bool resetSpaces();
+ static void appendSpaces( WhiteSpaceVec& orSpaces, sal_Int32 nCount, bool bLineFeed );
+ void appendLeadingSpaces( sal_Int32 nCount, bool bLineFeed );
+ void appendOpeningSpaces( sal_Int32 nCount, bool bLineFeed );
+ void appendClosingSpaces( sal_Int32 nCount, bool bLineFeed );
+
+ size_t getFormulaSize() const;
+ Any& appendRawToken( sal_Int32 nOpCode );
+ Any& insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd );
+ size_t appendWhiteSpaceTokens( const WhiteSpaceVec* pSpaces );
+ size_t insertWhiteSpaceTokens( const WhiteSpaceVec* pSpaces, size_t nIndexFromEnd );
+
+ size_t getOperandSize( size_t nOpIndex ) const;
+ void pushOperandSize( size_t nSize );
+ size_t popOperandSize();
+
+ ApiToken& getOperandToken( size_t nOpIndex, size_t nTokenIndex );
+
+ bool pushOperandToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ template< typename Type >
+ bool pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ template< typename Type >
+ bool pushValueOperandToken( const Type& rValue )
+ { return pushValueOperandToken( rValue, OPCODE_PUSH, nullptr ); }
+ bool pushParenthesesOperandToken( const WhiteSpaceVec* pClosingSpaces = nullptr );
+ bool pushUnaryPreOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ bool pushUnaryPostOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ bool pushBinaryOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ bool pushParenthesesOperatorToken( const WhiteSpaceVec* pOpeningSpaces = nullptr, const WhiteSpaceVec* pClosingSpaces = nullptr );
+ bool pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces = nullptr, const WhiteSpaceVec* pClosingSpaces = nullptr );
+ bool pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces = nullptr, const WhiteSpaceVec* pClosingSpaces = nullptr );
+
+ bool pushOperand( sal_Int32 nOpCode );
+ template< typename Type >
+ bool pushValueOperand( const Type& rValue, sal_Int32 nOpCode );
+ template< typename Type >
+ bool pushValueOperand( const Type& rValue )
+ { return pushValueOperand( rValue, OPCODE_PUSH ); }
+ bool pushBoolOperand( bool bValue );
+ bool pushErrorOperand( double fEncodedError );
+ bool pushBiffBoolOperand( sal_uInt8 nValue );
+ bool pushBiffErrorOperand( sal_uInt8 nErrorCode );
+ bool pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ bool pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ template< typename Type >
+ bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const Type& rApiRef );
+ bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ bool pushEmbeddedRefOperand( const DefinedNameBase& rName, bool bPushBadToken );
+ bool pushDefinedNameOperand( const DefinedNameRef& rxDefName );
+ bool pushExternalFuncOperand( const FunctionInfo& rFuncInfo );
+ bool pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem );
+ bool pushExternalNameOperand( const ExternalNameRef& rxExtName, const ExternalLink& rExtLink );
+ bool pushSpecialTokenOperand( const BinAddress& rBaseAddr );
+
+ bool pushUnaryPreOperator( sal_Int32 nOpCode );
+ bool pushUnaryPostOperator( sal_Int32 nOpCode );
+ bool pushBinaryOperator( sal_Int32 nOpCode );
+ bool pushParenthesesOperator();
+ bool pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount );
+ bool pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount );
+
+private:
+ // reference conversion ---------------------------------------------------
+
+ void initReference2d( SingleReference& orApiRef ) const;
+ static void initReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet );
+ void convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const;
+
+private:
+ // finalize token sequence ------------------------------------------------
+
+ virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const override;
+ virtual OUString resolveDefinedName( sal_Int32 nTokenIndex ) const override;
+
+protected:
+ const sal_Int32 mnMaxApiCol; /// Maximum column index in own document.
+ const sal_Int32 mnMaxApiRow; /// Maximum row index in own document.
+ const sal_Int32 mnMaxXlsCol; /// Maximum column index in imported document.
+ const sal_Int32 mnMaxXlsRow; /// Maximum row index in imported document.
+
+ ScAddress maBaseAddr; /// Base address for relative references.
+ bool mbRelativeAsOffset; /// True = relative row/column index is (signed) offset, false = explicit index.
+ bool mb2dRefsAs3dRefs; /// True = convert all 2D references to 3D references in sheet specified by base address.
+ bool mbSpecialTokens; /// True = special handling for tExp and tTbl tokens, false = exit with error.
+
+private:
+ ApiTokenVector maTokenStorage; /// Raw unordered token storage.
+ std::vector<size_t> maTokenIndexes; /// Indexes into maTokenStorage.
+ std::vector<size_t> maOperandSizeStack; /// Stack with token sizes per operand.
+ WhiteSpaceVec maLeadingSpaces; /// List of whitespaces before next token.
+ WhiteSpaceVec maOpeningSpaces; /// List of whitespaces before opening parenthesis.
+ WhiteSpaceVec maClosingSpaces; /// List of whitespaces before closing parenthesis.
+};
+
+FormulaParserImpl::FormulaParserImpl( const FormulaParser& rParent ) :
+ FormulaFinalizer( rParent ),
+ WorkbookHelper( rParent ),
+ mnMaxApiCol( rParent.getAddressConverter().getMaxApiAddress().Col() ),
+ mnMaxApiRow( rParent.getAddressConverter().getMaxApiAddress().Row() ),
+ mnMaxXlsCol( rParent.getAddressConverter().getMaxXlsAddress().Col() ),
+ mnMaxXlsRow( rParent.getAddressConverter().getMaxXlsAddress().Row() ),
+ mbRelativeAsOffset( false ),
+ mb2dRefsAs3dRefs( false ),
+ mbSpecialTokens( false )
+{
+ // reserve enough space to make resize(), push_back() etc. cheap
+ maTokenStorage.reserve( 0x2000 );
+ maTokenIndexes.reserve( 0x2000 );
+ maOperandSizeStack.reserve( 256 );
+ maLeadingSpaces.reserve( 256 );
+ maOpeningSpaces.reserve( 256 );
+ maClosingSpaces.reserve( 256 );
+}
+
+ApiTokenSequence FormulaParserImpl::importOoxFormula( const ScAddress&, const OUString& )
+{
+ OSL_FAIL( "FormulaParserImpl::importOoxFormula - not implemented" );
+ return ApiTokenSequence();
+}
+
+ApiTokenSequence FormulaParserImpl::importBiff12Formula( const ScAddress&, FormulaType, SequenceInputStream& )
+{
+ SAL_WARN("sc.filter", "FormulaParserImpl::importBiff12Formula - not implemented" );
+ return ApiTokenSequence();
+}
+
+OUString FormulaParserImpl::resolveOleTarget( sal_Int32 nRefId, bool bUseRefSheets ) const
+{
+ const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId, bUseRefSheets ).get();
+ OSL_ENSURE( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::OLE), "FormulaParserImpl::resolveOleTarget - missing or wrong link" );
+ if( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::OLE) )
+ return getBaseFilter().getAbsoluteUrl( pExtLink->getTargetUrl() );
+ return OUString();
+}
+
+void FormulaParserImpl::initializeImport( const ScAddress& rBaseAddr, FormulaType eType )
+{
+ maBaseAddr = rBaseAddr;
+ mbRelativeAsOffset = mb2dRefsAs3dRefs = mbSpecialTokens = false;
+ switch( eType )
+ {
+ case FormulaType::Cell:
+ mbSpecialTokens = true;
+ break;
+ case FormulaType::Array:
+ break;
+ case FormulaType::SharedFormula:
+ mbRelativeAsOffset = true;
+ break;
+ case FormulaType::CondFormat:
+ mbRelativeAsOffset = true;
+ break;
+ case FormulaType::Validation:
+ mbRelativeAsOffset = true;
+ break;
+ }
+
+ maTokenStorage.clear();
+ maTokenIndexes.clear();
+ maOperandSizeStack.clear();
+}
+
+ApiTokenSequence FormulaParserImpl::finalizeImport()
+{
+ ApiTokenSequence aTokens( static_cast< sal_Int32 >( maTokenIndexes.size() ) );
+ if( aTokens.hasElements() )
+ {
+ ApiToken* pToken = aTokens.getArray();
+ for( const auto& tokenIndex : maTokenIndexes )
+ {
+ *pToken = maTokenStorage[ tokenIndex ];
+ ++pToken;
+ }
+ }
+ return finalizeTokenArray( aTokens );
+}
+
+// token array ----------------------------------------------------------------
+
+bool FormulaParserImpl::resetSpaces()
+{
+ maLeadingSpaces.clear();
+ maOpeningSpaces.clear();
+ maClosingSpaces.clear();
+ return true;
+}
+
+void FormulaParserImpl::appendSpaces( WhiteSpaceVec& orSpaces, sal_Int32 nCount, bool bLineFeed )
+{
+ OSL_ENSURE( nCount >= 0, "FormulaParserImpl::appendSpaces - negative count" );
+ if( nCount > 0 )
+ orSpaces.emplace_back( nCount, bLineFeed );
+}
+
+void FormulaParserImpl::appendLeadingSpaces( sal_Int32 nCount, bool bLineFeed )
+{
+ appendSpaces( maLeadingSpaces, nCount, bLineFeed );
+}
+
+void FormulaParserImpl::appendOpeningSpaces( sal_Int32 nCount, bool bLineFeed )
+{
+ appendSpaces( maOpeningSpaces, nCount, bLineFeed );
+}
+
+void FormulaParserImpl::appendClosingSpaces( sal_Int32 nCount, bool bLineFeed )
+{
+ appendSpaces( maClosingSpaces, nCount, bLineFeed );
+}
+
+size_t FormulaParserImpl::getFormulaSize() const
+{
+ return maTokenIndexes.size();
+}
+
+Any& FormulaParserImpl::appendRawToken( sal_Int32 nOpCode )
+{
+ maTokenIndexes.push_back( maTokenStorage.size() );
+ return maTokenStorage.append( nOpCode );
+}
+
+Any& FormulaParserImpl::insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd )
+{
+ maTokenIndexes.insert( maTokenIndexes.end() - nIndexFromEnd, maTokenStorage.size() );
+ return maTokenStorage.append( nOpCode );
+}
+
+size_t FormulaParserImpl::appendWhiteSpaceTokens( const WhiteSpaceVec* pSpaces )
+{
+ if( pSpaces )
+ for( const auto& rSpace : *pSpaces )
+ appendRawToken( OPCODE_SPACES ) <<= rSpace.first;
+ return pSpaces ? pSpaces->size() : 0;
+}
+
+size_t FormulaParserImpl::insertWhiteSpaceTokens( const WhiteSpaceVec* pSpaces, size_t nIndexFromEnd )
+{
+ if( pSpaces )
+ for( const auto& rSpace : *pSpaces )
+ insertRawToken( OPCODE_SPACES, nIndexFromEnd ) <<= rSpace.first;
+ return pSpaces ? pSpaces->size() : 0;
+}
+
+size_t FormulaParserImpl::getOperandSize( size_t nOpIndex ) const
+{
+ OSL_ENSURE( (nOpIndex < 1) && (!maOperandSizeStack.empty()),
+ "FormulaParserImpl::getOperandSize - invalid parameters" );
+ return maOperandSizeStack[ maOperandSizeStack.size() - 1 + nOpIndex ];
+}
+
+void FormulaParserImpl::pushOperandSize( size_t nSize )
+{
+ maOperandSizeStack.push_back( nSize );
+}
+
+size_t FormulaParserImpl::popOperandSize()
+{
+ OSL_ENSURE( !maOperandSizeStack.empty(), "FormulaParserImpl::popOperandSize - invalid call" );
+ size_t nOpSize = maOperandSizeStack.back();
+ maOperandSizeStack.pop_back();
+ return nOpSize;
+}
+
+ApiToken& FormulaParserImpl::getOperandToken( size_t nOpIndex, size_t nTokenIndex )
+{
+ SAL_WARN_IF( getOperandSize( nOpIndex ) <= nTokenIndex, "sc.filter",
+ "FormulaParserImpl::getOperandToken - invalid parameters" );
+ auto aIndexIt = maTokenIndexes.cend();
+ for( auto aEnd = maOperandSizeStack.cend(), aIt = aEnd - 1 + nOpIndex; aIt != aEnd; ++aIt )
+ aIndexIt -= *aIt;
+ return maTokenStorage[ *(aIndexIt + nTokenIndex) ];
+}
+
+bool FormulaParserImpl::pushOperandToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces );
+ appendRawToken( nOpCode );
+ pushOperandSize( nSpacesSize + 1 );
+ return true;
+}
+
+template< typename Type >
+bool FormulaParserImpl::pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces );
+ appendRawToken( nOpCode ) <<= rValue;
+ pushOperandSize( nSpacesSize + 1 );
+ return true;
+}
+
+bool FormulaParserImpl::pushParenthesesOperandToken( const WhiteSpaceVec* pClosingSpaces )
+{
+ size_t nSpacesSize = appendWhiteSpaceTokens( nullptr );
+ appendRawToken( OPCODE_OPEN );
+ nSpacesSize += appendWhiteSpaceTokens( pClosingSpaces );
+ appendRawToken( OPCODE_CLOSE );
+ pushOperandSize( nSpacesSize + 2 );
+ return true;
+}
+
+bool FormulaParserImpl::pushUnaryPreOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ bool bOk = !maOperandSizeStack.empty();
+ if( bOk )
+ {
+ size_t nOpSize = popOperandSize();
+ size_t nSpacesSize = insertWhiteSpaceTokens( pSpaces, nOpSize );
+ insertRawToken( nOpCode, nOpSize );
+ pushOperandSize( nOpSize + nSpacesSize + 1 );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushUnaryPostOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ bool bOk = !maOperandSizeStack.empty();
+ if( bOk )
+ {
+ size_t nOpSize = popOperandSize();
+ size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces );
+ appendRawToken( nOpCode );
+ pushOperandSize( nOpSize + nSpacesSize + 1 );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushBinaryOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ bool bOk = maOperandSizeStack.size() >= 2;
+ if( bOk )
+ {
+ size_t nOp2Size = popOperandSize();
+ size_t nOp1Size = popOperandSize();
+ size_t nSpacesSize = insertWhiteSpaceTokens( pSpaces, nOp2Size );
+ insertRawToken( nOpCode, nOp2Size );
+ pushOperandSize( nOp1Size + nSpacesSize + 1 + nOp2Size );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushParenthesesOperatorToken( const WhiteSpaceVec* pOpeningSpaces, const WhiteSpaceVec* pClosingSpaces )
+{
+ bool bOk = !maOperandSizeStack.empty();
+ if( bOk )
+ {
+ size_t nOpSize = popOperandSize();
+ size_t nSpacesSize = insertWhiteSpaceTokens( pOpeningSpaces, nOpSize );
+ insertRawToken( OPCODE_OPEN, nOpSize );
+ nSpacesSize += appendWhiteSpaceTokens( pClosingSpaces );
+ appendRawToken( OPCODE_CLOSE );
+ pushOperandSize( nOpSize + nSpacesSize + 2 );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces, const WhiteSpaceVec* pClosingSpaces )
+{
+ /* #i70925# if there are not enough tokens available on token stack, do
+ not exit with error, but reduce parameter count. */
+ nParamCount = ::std::min( maOperandSizeStack.size(), nParamCount );
+
+ // convert all parameters on stack to a single operand separated with OPCODE_SEP
+ bool bOk = true;
+ for( size_t nParam = 1; bOk && (nParam < nParamCount); ++nParam )
+ bOk = pushBinaryOperatorToken( OPCODE_SEP );
+
+ // add function parentheses and function name
+ return bOk &&
+ ((nParamCount > 0) ? pushParenthesesOperatorToken( nullptr, pClosingSpaces ) : pushParenthesesOperandToken( pClosingSpaces )) &&
+ pushUnaryPreOperatorToken( nOpCode, pLeadingSpaces );
+}
+
+bool FormulaParserImpl::pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces, const WhiteSpaceVec* pClosingSpaces )
+{
+ bool bOk = pushFunctionOperatorToken( rFuncInfo.mnApiOpCode, nParamCount, pLeadingSpaces, pClosingSpaces );
+ if( bOk )
+ {
+ // create an external add-in call for the passed built-in function
+ if( (rFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) && !rFuncInfo.maExtProgName.isEmpty() )
+ getOperandToken( 0, 0 ).Data <<= rFuncInfo.maExtProgName;
+ // create a bad token with unsupported function name
+ else if( (rFuncInfo.mnApiOpCode == OPCODE_BAD) && !rFuncInfo.maOoxFuncName.isEmpty() )
+ getOperandToken( 0, 0 ).Data <<= rFuncInfo.maOoxFuncName;
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushOperand( sal_Int32 nOpCode )
+{
+ return pushOperandToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+template< typename Type >
+bool FormulaParserImpl::pushValueOperand( const Type& rValue, sal_Int32 nOpCode )
+{
+ return pushValueOperandToken( rValue, nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushBoolOperand( bool bValue )
+{
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( bValue ? BIFF_FUNC_TRUE : BIFF_FUNC_FALSE ) )
+ return pushFunctionOperator( pFuncInfo->mnApiOpCode, 0 );
+ return pushValueOperand< double >( bValue ? 1.0 : 0.0 );
+}
+
+bool FormulaParserImpl::pushErrorOperand( double fEncodedError )
+{
+ // HACK: enclose all error codes into an 1x1 matrix
+ // start token array with opening brace and leading spaces
+ pushOperand( OPCODE_ARRAY_OPEN );
+ size_t nOpSize = popOperandSize();
+ size_t nOldArraySize = maTokenIndexes.size();
+ // push a double containing the Calc error code
+ appendRawToken( OPCODE_PUSH ) <<= fEncodedError;
+ // close token array and set resulting operand size
+ appendRawToken( OPCODE_ARRAY_CLOSE );
+ pushOperandSize( nOpSize + maTokenIndexes.size() - nOldArraySize );
+ return true;
+}
+
+bool FormulaParserImpl::pushBiffBoolOperand( sal_uInt8 nValue )
+{
+ return pushBoolOperand( nValue != BIFF_TOK_BOOL_FALSE );
+}
+
+bool FormulaParserImpl::pushBiffErrorOperand( sal_uInt8 nErrorCode )
+{
+ return pushErrorOperand( BiffHelper::calcDoubleFromError( nErrorCode ) );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ SingleReference aApiRef;
+ convertReference2d( aApiRef, rRef, bDeleted, bRelativeAsOffset );
+ return pushValueOperand( aApiRef );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ ComplexReference aApiRef;
+ convertReference2d( aApiRef, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset );
+ return pushValueOperand( aApiRef );
+}
+
+template< typename Type >
+bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const Type& rApiRef )
+{
+ if( rSheetRange.isExternal() )
+ {
+ ExternalReference aApiExtRef;
+ aApiExtRef.Index = rSheetRange.getDocLinkIndex();
+ aApiExtRef.Reference <<= rApiRef;
+ return pushValueOperand( aApiExtRef );
+ }
+ return pushValueOperand( rApiRef );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ if( rSheetRange.is3dRange() )
+ {
+ // single-cell-range over several sheets, needs to create a ComplexReference
+ ComplexReference aApiRef;
+ convertReference3d( aApiRef, rSheetRange, rRef, rRef, bDeleted, bRelativeAsOffset );
+ return pushReferenceOperand( rSheetRange, aApiRef );
+ }
+ SingleReference aApiRef;
+ convertReference3d( aApiRef, rSheetRange.getFirstSheet(), rSheetRange.isSameSheet(), rRef, bDeleted, bRelativeAsOffset );
+ return pushReferenceOperand( rSheetRange, aApiRef );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ ComplexReference aApiRef;
+ convertReference3d( aApiRef, rSheetRange, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset );
+ return pushReferenceOperand( rSheetRange, aApiRef );
+}
+
+bool FormulaParserImpl::pushEmbeddedRefOperand( const DefinedNameBase& rName, bool bPushBadToken )
+{
+ if( bPushBadToken && !rName.getModelName().isEmpty() && (rName.getModelName()[ 0 ] >= ' ') )
+ return pushValueOperand( rName.getModelName(), OPCODE_BAD );
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+}
+
+bool FormulaParserImpl::pushDefinedNameOperand( const DefinedNameRef& rxDefName )
+{
+ if( !rxDefName || rxDefName->getModelName().isEmpty() )
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+ if( rxDefName->isMacroFunction() )
+ return pushValueOperand( rxDefName->getModelName(), OPCODE_MACRO );
+ if( rxDefName->getTokenIndex() >= 0 )
+ return pushValueOperand( rxDefName->getTokenIndex(), OPCODE_NAME );
+ return pushEmbeddedRefOperand( *rxDefName, true );
+}
+
+bool FormulaParserImpl::pushExternalFuncOperand( const FunctionInfo& rFuncInfo )
+{
+ return (rFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) ?
+ pushValueOperand( rFuncInfo.maExtProgName, OPCODE_EXTERNAL ) :
+ pushOperand( rFuncInfo.mnApiOpCode );
+}
+
+bool FormulaParserImpl::pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem )
+{
+ // create the function call DDE("server";"topic";"item")
+ return
+ pushValueOperandToken( rDdeServer ) &&
+ pushValueOperandToken( rDdeTopic ) &&
+ pushValueOperandToken( rDdeItem ) &&
+ pushFunctionOperator( OPCODE_DDE, 3 );
+}
+
+bool FormulaParserImpl::pushExternalNameOperand( const ExternalNameRef& rxExtName, const ExternalLink& rExtLink )
+{
+ if( rxExtName ) switch( rExtLink.getLinkType() )
+ {
+ case ExternalLinkType::External:
+ return pushEmbeddedRefOperand( *rxExtName, false );
+
+ case ExternalLinkType::Library:
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( rxExtName->getUpcaseModelName() ) )
+ if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == rExtLink.getFuncLibraryType()) )
+ return pushExternalFuncOperand( *pFuncInfo );
+ break;
+
+ case ExternalLinkType::DDE:
+ {
+ OUString aDdeServer, aDdeTopic, aDdeItem;
+ if( rxExtName->getDdeLinkData( aDdeServer, aDdeTopic, aDdeItem ) )
+ return pushDdeLinkOperand( aDdeServer, aDdeTopic, aDdeItem );
+ }
+ break;
+
+ default:
+ OSL_ENSURE( rExtLink.getLinkType() != ExternalLinkType::Self, "FormulaParserImpl::pushExternalNameOperand - invalid call" );
+ }
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+}
+
+bool FormulaParserImpl::pushSpecialTokenOperand( const BinAddress& rBaseAddr )
+{
+ CellAddress aBaseAddr( maBaseAddr.Tab(), rBaseAddr.mnCol, rBaseAddr.mnRow );
+ ApiSpecialTokenInfo aTokenInfo( aBaseAddr, false );
+ return mbSpecialTokens && (getFormulaSize() == 0) && pushValueOperand( aTokenInfo, OPCODE_BAD );
+}
+
+bool FormulaParserImpl::pushUnaryPreOperator( sal_Int32 nOpCode )
+{
+ return pushUnaryPreOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushUnaryPostOperator( sal_Int32 nOpCode )
+{
+ return pushUnaryPostOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushBinaryOperator( sal_Int32 nOpCode )
+{
+ return pushBinaryOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushParenthesesOperator()
+{
+ return pushParenthesesOperatorToken( &maOpeningSpaces, &maClosingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount )
+{
+ return pushFunctionOperatorToken( nOpCode, nParamCount, &maLeadingSpaces, &maClosingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount )
+{
+ return pushFunctionOperatorToken( rFuncInfo, nParamCount, &maLeadingSpaces, &maClosingSpaces ) && resetSpaces();
+}
+
+// reference conversion -------------------------------------------------------
+
+void FormulaParserImpl::initReference2d( SingleReference& orApiRef ) const
+{
+ if( mb2dRefsAs3dRefs )
+ {
+ initReference3d( orApiRef, sal_Int32 (maBaseAddr.Tab() ), false );
+ }
+ else
+ {
+ orApiRef.Flags = SHEET_RELATIVE;
+ // #i10184# absolute sheet index needed for relative references in shared formulas
+ orApiRef.Sheet = sal_Int32( maBaseAddr.Tab() );
+ orApiRef.RelativeSheet = 0;
+ }
+}
+
+void FormulaParserImpl::initReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet )
+{
+ orApiRef.Flags = SHEET_3D;
+ if( nSheet < 0 )
+ {
+ orApiRef.Sheet = 0;
+ orApiRef.Flags |= SHEET_DELETED;
+ }
+ else if( bSameSheet )
+ {
+ OSL_ENSURE( nSheet == 0, "FormulaParserImpl::initReference3d - invalid sheet index" );
+ orApiRef.Flags |= SHEET_RELATIVE;
+ orApiRef.RelativeSheet = 0;
+ }
+ else
+ {
+ orApiRef.Sheet = nSheet;
+ }
+}
+
+void FormulaParserImpl::convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ if( bDeleted )
+ {
+ orApiRef.Column = 0;
+ orApiRef.Row = 0;
+ // no explicit information about whether row or column is deleted
+ orApiRef.Flags |= COLUMN_DELETED | ROW_DELETED;
+ }
+ else
+ {
+ // column/row indexes and flags
+ setFlag( orApiRef.Flags, COLUMN_RELATIVE, rRef.mbColRel );
+ setFlag( orApiRef.Flags, ROW_RELATIVE, rRef.mbRowRel );
+ (rRef.mbColRel ? orApiRef.RelativeColumn : orApiRef.Column) = rRef.mnCol;
+ (rRef.mbRowRel ? orApiRef.RelativeRow : orApiRef.Row) = rRef.mnRow;
+ // convert absolute indexes to relative offsets used in API
+ if( !bRelativeAsOffset )
+ {
+ if( rRef.mbColRel )
+ orApiRef.RelativeColumn -= sal_Int32( maBaseAddr.Col() );
+ if( rRef.mbRowRel )
+ orApiRef.RelativeRow -= maBaseAddr.Row();
+ }
+ }
+}
+
+void FormulaParserImpl::convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ convertReference( orApiRef.Reference1, rRef1, bDeleted, bRelativeAsOffset );
+ convertReference( orApiRef.Reference2, rRef2, bDeleted, bRelativeAsOffset );
+ /* Handle references to complete rows or columns (e.g. $1:$2 or C:D),
+ need to expand or shrink to limits of own document. */
+ if( !bDeleted && !rRef1.mbColRel && !rRef2.mbColRel && (orApiRef.Reference1.Column == 0) && (orApiRef.Reference2.Column == mnMaxXlsCol) )
+ orApiRef.Reference2.Column = mnMaxApiCol;
+ if( !bDeleted && !rRef1.mbRowRel && !rRef2.mbRowRel && (orApiRef.Reference1.Row == 0) && (orApiRef.Reference2.Row == mnMaxXlsRow) )
+ orApiRef.Reference2.Row = mnMaxApiRow;
+}
+
+void FormulaParserImpl::convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ initReference2d( orApiRef );
+ convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset );
+}
+
+void FormulaParserImpl::convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ initReference2d( orApiRef.Reference1 );
+ initReference2d( orApiRef.Reference2 );
+ convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset );
+ // remove sheet name from second part of reference
+ setFlag( orApiRef.Reference2.Flags, SHEET_3D, false );
+}
+
+void FormulaParserImpl::convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ initReference3d( orApiRef, nSheet, bSameSheet );
+ convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset );
+}
+
+void FormulaParserImpl::convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ bool bSameSheet = rSheetRange.isSameSheet();
+ initReference3d( orApiRef.Reference1, rSheetRange.getFirstSheet(), bSameSheet );
+ initReference3d( orApiRef.Reference2, rSheetRange.getLastSheet(), bSameSheet );
+ convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset );
+ // remove sheet name from second part of reference
+ setFlag( orApiRef.Reference2.Flags, SHEET_3D, rSheetRange.is3dRange() );
+}
+
+// finalize token sequence ----------------------------------------------------
+
+const FunctionInfo* FormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const
+{
+ /* Try to parse calls to library functions. The format of such a function
+ call is "[n]!funcname", n>0 being the link identifier of the function
+ library spreadsheet file. */
+ sal_Int32 nBracketOpen = rTokenData.indexOf( '[' );
+ sal_Int32 nBracketClose = rTokenData.indexOf( ']' );
+ sal_Int32 nExclamation = rTokenData.indexOf( '!' );
+ if( (0 == nBracketOpen) && (nBracketOpen + 1 < nBracketClose) && (nBracketClose + 1 == nExclamation) && (nExclamation + 1 < rTokenData.getLength()) )
+ {
+ sal_Int32 nRefId = o3tl::toInt32(rTokenData.subView( nBracketOpen + 1, nBracketClose - nBracketOpen - 1 ));
+ const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get();
+ if( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::Library) )
+ {
+ OUString aFuncName = rTokenData.copy( nExclamation + 1 ).toAsciiUpperCase();
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aFuncName ) )
+ if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == pExtLink->getFuncLibraryType()) )
+ return pFuncInfo;
+ }
+ }
+ return nullptr;
+}
+
+OUString FormulaParserImpl::resolveDefinedName( sal_Int32 nTokenIndex ) const
+{
+ if( const DefinedName* pDefName = getDefinedNames().getByTokenIndex( nTokenIndex ).get() )
+ return pDefName->getCalcName();
+ return OUString();
+}
+
+// OOXML/BIFF12 parser implementation =========================================
+
+namespace {
+
+class OoxFormulaParserImpl : public FormulaParserImpl
+{
+public:
+ explicit OoxFormulaParserImpl( const FormulaParser& rParent );
+
+ virtual ApiTokenSequence importOoxFormula(
+ const ScAddress& rBaseAddr,
+ const OUString& rFormulaString ) override;
+
+ virtual ApiTokenSequence importBiff12Formula(
+ const ScAddress& rBaseAddr,
+ FormulaType eType,
+ SequenceInputStream& rStrm ) override;
+
+private:
+ // import token contents and create API formula token ---------------------
+
+ bool importAttrToken( SequenceInputStream& rStrm );
+ bool importSpaceToken( SequenceInputStream& rStrm );
+ bool importTableToken( SequenceInputStream& rStrm );
+ bool importArrayToken( SequenceInputStream& rStrm );
+ bool importRefToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importAreaToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importRef3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importArea3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importMemAreaToken( SequenceInputStream& rStrm, bool bAddData );
+ bool importMemFuncToken( SequenceInputStream& rStrm );
+ bool importNameToken( SequenceInputStream& rStrm );
+ bool importNameXToken( SequenceInputStream& rStrm );
+ bool importFuncToken( SequenceInputStream& rStrm );
+ bool importFuncVarToken( SequenceInputStream& rStrm );
+ bool importExpToken( SequenceInputStream& rStrm );
+
+ LinkSheetRange readSheetRange( SequenceInputStream& rStrm );
+
+ void swapStreamPosition( SequenceInputStream& rStrm );
+ void skipMemAreaAddData( SequenceInputStream& rStrm );
+
+ // convert BIN token and push API operand or operator ---------------------
+
+ bool pushBiff12Name( sal_Int32 nNameId );
+ bool pushBiff12ExtName( sal_Int32 nRefId, sal_Int32 nNameId );
+ bool pushBiff12Function( sal_uInt16 nFuncId );
+ bool pushBiff12Function( sal_uInt16 nFuncId, sal_uInt8 nParamCount );
+
+private:
+ ApiParserWrapper maApiParser; /// Wrapper for the API formula parser object.
+ sal_Int64 mnAddDataPos; /// Current stream position for additional data (tExp, tArray, tMemArea).
+ bool mbNeedExtRefs; /// True = parser needs initialization of external reference info.
+};
+
+}
+
+OoxFormulaParserImpl::OoxFormulaParserImpl( const FormulaParser& rParent ) :
+ FormulaParserImpl( rParent ),
+ maApiParser( rParent.getBaseFilter().getModelFactory(), rParent ),
+ mnAddDataPos( 0 ),
+ mbNeedExtRefs( true )
+{
+}
+
+ApiTokenSequence OoxFormulaParserImpl::importOoxFormula( const ScAddress& rBaseAddr, const OUString& rFormulaString )
+{
+ if( mbNeedExtRefs )
+ {
+ maApiParser.getParserProperties().setProperty( PROP_ExternalLinks, getExternalLinks().getLinkInfos() );
+ mbNeedExtRefs = false;
+ }
+ return finalizeTokenArray( maApiParser.parseFormula( rFormulaString, rBaseAddr ) );
+}
+
+ApiTokenSequence OoxFormulaParserImpl::importBiff12Formula( const ScAddress& rBaseAddr, FormulaType eType, SequenceInputStream& rStrm )
+{
+ initializeImport( rBaseAddr, eType );
+
+ sal_Int32 nFmlaSize = rStrm.readInt32();
+ sal_Int64 nFmlaPos = rStrm.tell();
+ sal_Int64 nFmlaEndPos = nFmlaPos + nFmlaSize;
+
+ rStrm.seek( nFmlaEndPos );
+ sal_Int32 nAddDataSize = rStrm.readInt32();
+ mnAddDataPos = rStrm.tell();
+ sal_Int64 nAddDataEndPos = mnAddDataPos + nAddDataSize;
+ rStrm.seek( nFmlaPos );
+
+ bool bOk = (nFmlaSize >= 0) && (nAddDataSize >= 0);
+ bool bRelativeAsOffset = mbRelativeAsOffset;
+
+ while( bOk && !rStrm.isEof() && (rStrm.tell() < nFmlaEndPos) )
+ {
+ sal_uInt8 nTokenId;
+ nTokenId = rStrm.readuChar();
+ sal_uInt8 nTokenClass = nTokenId & BIFF_TOKCLASS_MASK;
+ sal_uInt8 nBaseId = nTokenId & BIFF_TOKID_MASK;
+
+ if( nTokenClass == BIFF_TOKCLASS_NONE )
+ {
+ // base tokens
+ switch( nBaseId )
+ {
+ case BIFF_TOKID_EXP: bOk = importExpToken( rStrm ); break;
+ case BIFF_TOKID_ADD: bOk = pushBinaryOperator( OPCODE_ADD ); break;
+ case BIFF_TOKID_SUB: bOk = pushBinaryOperator( OPCODE_SUB ); break;
+ case BIFF_TOKID_MUL: bOk = pushBinaryOperator( OPCODE_MULT ); break;
+ case BIFF_TOKID_DIV: bOk = pushBinaryOperator( OPCODE_DIV ); break;
+ case BIFF_TOKID_POWER: bOk = pushBinaryOperator( OPCODE_POWER ); break;
+ case BIFF_TOKID_CONCAT: bOk = pushBinaryOperator( OPCODE_CONCAT ); break;
+ case BIFF_TOKID_LT: bOk = pushBinaryOperator( OPCODE_LESS ); break;
+ case BIFF_TOKID_LE: bOk = pushBinaryOperator( OPCODE_LESS_EQUAL ); break;
+ case BIFF_TOKID_EQ: bOk = pushBinaryOperator( OPCODE_EQUAL ); break;
+ case BIFF_TOKID_GE: bOk = pushBinaryOperator( OPCODE_GREATER_EQUAL ); break;
+ case BIFF_TOKID_GT: bOk = pushBinaryOperator( OPCODE_GREATER ); break;
+ case BIFF_TOKID_NE: bOk = pushBinaryOperator( OPCODE_NOT_EQUAL ); break;
+ case BIFF_TOKID_ISECT: bOk = pushBinaryOperator( OPCODE_INTERSECT ); break;
+ case BIFF_TOKID_LIST: bOk = pushBinaryOperator( OPCODE_LIST ); break;
+ case BIFF_TOKID_RANGE: bOk = pushBinaryOperator( OPCODE_RANGE ); break;
+ case BIFF_TOKID_UPLUS: bOk = pushUnaryPreOperator( OPCODE_PLUS_SIGN ); break;
+ case BIFF_TOKID_UMINUS: bOk = pushUnaryPreOperator( OPCODE_MINUS_SIGN ); break;
+ case BIFF_TOKID_PERCENT: bOk = pushUnaryPostOperator( OPCODE_PERCENT ); break;
+ case BIFF_TOKID_PAREN: bOk = pushParenthesesOperator(); break;
+ case BIFF_TOKID_MISSARG: bOk = pushOperand( OPCODE_MISSING ); break;
+ case BIFF_TOKID_STR: bOk = pushValueOperand( BiffHelper::readString( rStrm, false ) ); break;
+ case BIFF_TOKID_NLR: bOk = importTableToken( rStrm ); break;
+ case BIFF_TOKID_ATTR: bOk = importAttrToken( rStrm ); break;
+ case BIFF_TOKID_ERR: bOk = pushBiffErrorOperand( rStrm.readuInt8() ); break;
+ case BIFF_TOKID_BOOL: bOk = pushBiffBoolOperand( rStrm.readuInt8() ); break;
+ case BIFF_TOKID_INT: bOk = pushValueOperand< double >( rStrm.readuInt16() ); break;
+ case BIFF_TOKID_NUM: bOk = pushValueOperand( rStrm.readDouble() ); break;
+ default: bOk = false;
+ }
+ }
+ else
+ {
+ // classified tokens
+ switch( nBaseId )
+ {
+ case BIFF_TOKID_ARRAY: bOk = importArrayToken( rStrm ); break;
+ case BIFF_TOKID_FUNC: bOk = importFuncToken( rStrm ); break;
+ case BIFF_TOKID_FUNCVAR: bOk = importFuncVarToken( rStrm ); break;
+ case BIFF_TOKID_NAME: bOk = importNameToken( rStrm ); break;
+ case BIFF_TOKID_REF: bOk = importRefToken( rStrm, false, false ); break;
+ case BIFF_TOKID_AREA: bOk = importAreaToken( rStrm, false, false ); break;
+ case BIFF_TOKID_MEMAREA: bOk = importMemAreaToken( rStrm, true ); break;
+ case BIFF_TOKID_MEMERR: bOk = importMemAreaToken( rStrm, false ); break;
+ case BIFF_TOKID_MEMNOMEM: bOk = importMemAreaToken( rStrm, false ); break;
+ case BIFF_TOKID_MEMFUNC: bOk = importMemFuncToken( rStrm ); break;
+ case BIFF_TOKID_REFERR: bOk = importRefToken( rStrm, true, false ); break;
+ case BIFF_TOKID_AREAERR: bOk = importAreaToken( rStrm, true, false ); break;
+ case BIFF_TOKID_REFN: bOk = importRefToken( rStrm, false, true ); break;
+ case BIFF_TOKID_AREAN: bOk = importAreaToken( rStrm, false, true ); break;
+ case BIFF_TOKID_MEMAREAN: bOk = importMemFuncToken( rStrm ); break;
+ case BIFF_TOKID_MEMNOMEMN: bOk = importMemFuncToken( rStrm ); break;
+ case BIFF_TOKID_NAMEX: bOk = importNameXToken( rStrm ); break;
+ case BIFF_TOKID_REF3D: bOk = importRef3dToken( rStrm, false, bRelativeAsOffset ); break;
+ case BIFF_TOKID_AREA3D: bOk = importArea3dToken( rStrm, false, bRelativeAsOffset ); break;
+ case BIFF_TOKID_REFERR3D: bOk = importRef3dToken( rStrm, true, bRelativeAsOffset ); break;
+ case BIFF_TOKID_AREAERR3D: bOk = importArea3dToken( rStrm, true, bRelativeAsOffset ); break;
+ default: bOk = false;
+ }
+ }
+ }
+
+ // build and finalize the token sequence
+ ApiTokenSequence aFinalTokens;
+ if( bOk && (rStrm.tell() == nFmlaEndPos) && (mnAddDataPos == nAddDataEndPos) )
+ aFinalTokens = finalizeImport();
+
+ // seek behind token array
+ if( (nFmlaSize >= 0) && (nAddDataSize >= 0) )
+ rStrm.seek( nAddDataEndPos );
+
+ // return the final token sequence
+ return aFinalTokens;
+}
+
+// import token contents and create API formula token -------------------------
+
+bool OoxFormulaParserImpl::importAttrToken( SequenceInputStream& rStrm )
+{
+ bool bOk = true;
+ sal_uInt8 nType;
+ nType = rStrm.readuChar();
+ // equal flags in all BIFFs
+ switch( nType )
+ {
+ case 0: // sometimes, tAttrSkip tokens miss the type flag
+ case BIFF_TOK_ATTR_VOLATILE:
+ case BIFF_TOK_ATTR_IF:
+ case BIFF_TOK_ATTR_SKIP:
+ case BIFF_TOK_ATTR_ASSIGN:
+ case BIFF_TOK_ATTR_IFERROR:
+ rStrm.skip( 2 );
+ break;
+ case BIFF_TOK_ATTR_CHOOSE:
+ rStrm.skip( 2 * rStrm.readuInt16() + 2 );
+ break;
+ case BIFF_TOK_ATTR_SUM:
+ rStrm.skip( 2 );
+ bOk = pushBiff12Function( BIFF_FUNC_SUM, 1 );
+ break;
+ case BIFF_TOK_ATTR_SPACE:
+ case BIFF_TOK_ATTR_SPACE_VOLATILE:
+ bOk = importSpaceToken( rStrm );
+ break;
+ default:
+ bOk = false;
+ }
+ return bOk;
+}
+
+bool OoxFormulaParserImpl::importSpaceToken( SequenceInputStream& rStrm )
+{
+ // equal constants in BIFF and OOX
+ sal_uInt8 nType, nCount;
+ nType = rStrm.readuChar();
+ nCount = rStrm.readuChar();
+ switch( nType )
+ {
+ case BIFF_TOK_ATTR_SPACE_SP:
+ appendLeadingSpaces( nCount, false );
+ break;
+ case BIFF_TOK_ATTR_SPACE_BR:
+ appendLeadingSpaces( nCount, true );
+ break;
+ case BIFF_TOK_ATTR_SPACE_SP_OPEN:
+ appendOpeningSpaces( nCount, false );
+ break;
+ case BIFF_TOK_ATTR_SPACE_BR_OPEN:
+ appendOpeningSpaces( nCount, true );
+ break;
+ case BIFF_TOK_ATTR_SPACE_SP_CLOSE:
+ appendClosingSpaces( nCount, false );
+ break;
+ case BIFF_TOK_ATTR_SPACE_BR_CLOSE:
+ appendClosingSpaces( nCount, true );
+ break;
+ }
+ return true;
+}
+
+bool OoxFormulaParserImpl::importTableToken( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags, nTableId, nCol1, nCol2;
+ rStrm.skip( 3 );
+ nFlags = rStrm.readuInt16();
+ nTableId = rStrm.readuInt16();
+ rStrm.skip( 2 );
+ nCol1 = rStrm.readuInt16();
+ nCol2 = rStrm.readuInt16();
+ TableRef xTable = getTables().getTable( nTableId );
+ sal_Int32 nTokenIndex = xTable ? xTable->getTokenIndex() : -1;
+ if( nTokenIndex >= 0 )
+ {
+ sal_Int32 nWidth = xTable->getWidth();
+ sal_Int32 nHeight = xTable->getHeight();
+ sal_Int32 nStartCol = 0;
+ sal_Int32 nEndCol = nWidth - 1;
+ sal_Int32 nStartRow = 0;
+ sal_Int32 nEndRow = nHeight - 1;
+ bool bFixedStartRow = true;
+ bool bFixedHeight = false;
+
+ bool bSingleCol = getFlag( nFlags, BIFF12_TOK_TABLE_COLUMN );
+ bool bColRange = getFlag( nFlags, BIFF12_TOK_TABLE_COLRANGE );
+ bool bValidRef = !bSingleCol || !bColRange;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - illegal combination of single column and column range" );
+ if( bValidRef )
+ {
+ if( bSingleCol )
+ nStartCol = nEndCol = nCol1;
+ else if( bColRange )
+ { nStartCol = nCol1; nEndCol = nCol2; }
+ bValidRef = (nStartCol <= nEndCol) && (nEndCol < nWidth);
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid column range" );
+ }
+
+ if( bValidRef )
+ {
+ bool bAllRows = getFlag( nFlags, BIFF12_TOK_TABLE_ALL );
+ bool bHeaderRows = getFlag( nFlags, BIFF12_TOK_TABLE_HEADERS );
+ bool bDataRows = getFlag( nFlags, BIFF12_TOK_TABLE_DATA );
+ bool bTotalsRows = getFlag( nFlags, BIFF12_TOK_TABLE_TOTALS );
+ bool bThisRow = getFlag( nFlags, BIFF12_TOK_TABLE_THISROW );
+
+ sal_Int32 nStartDataRow = xTable->getHeaderRows();
+ sal_Int32 nEndDataRow = nEndRow - xTable->getTotalsRows();
+ bValidRef = (nStartRow <= nStartDataRow) && (nStartDataRow <= nEndDataRow) && (nEndDataRow <= nEndRow);
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid data row range" );
+ if( bValidRef )
+ {
+ if( bAllRows )
+ {
+ bValidRef = !bHeaderRows && !bDataRows && !bTotalsRows && !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#All] table token" );
+ }
+ else if( bHeaderRows )
+ {
+ bValidRef = !bTotalsRows && !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Headers] table token" );
+ nEndRow = bDataRows ? nEndDataRow : (nStartDataRow - 1);
+ bFixedHeight = !bDataRows;
+ }
+ else if( bDataRows )
+ {
+ bValidRef = !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Data] table token" );
+ nStartRow = nStartDataRow;
+ if( !bTotalsRows ) nEndRow = nEndDataRow;
+ }
+ else if( bTotalsRows )
+ {
+ bValidRef = !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Totals] table token" );
+ nStartRow = nEndDataRow + 1;
+ bFixedStartRow = false;
+ bFixedHeight = !bDataRows;
+ }
+ else if( bThisRow )
+ {
+ nStartRow = nEndRow = maBaseAddr.Row() - xTable->getRange().aStart.Row();
+ bFixedHeight = true;
+ }
+ else
+ {
+ // nothing is the same as [#Data]
+ nStartRow = nStartDataRow;
+ nEndRow = nEndDataRow;
+ }
+ }
+ if( bValidRef )
+ bValidRef = (0 <= nStartRow) && (nStartRow <= nEndRow) && (nEndRow < nHeight);
+ }
+ if( bValidRef )
+ {
+ // push single database area token, if table token refers to entire table
+ if( (nStartCol == 0) && (nEndCol + 1 == nWidth) && (nStartRow == 0) && (nEndRow + 1 == nHeight) )
+ return pushValueOperand( nTokenIndex, OPCODE_DBAREA );
+ // create an OFFSET function call to refer to a subrange of the table
+ const FunctionInfo* pRowsInfo = getFuncInfoFromBiff12FuncId( BIFF_FUNC_ROWS );
+ const FunctionInfo* pColumnsInfo = getFuncInfoFromBiff12FuncId( BIFF_FUNC_COLUMNS );
+ return
+ pRowsInfo && pColumnsInfo &&
+ pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ (bFixedStartRow ?
+ pushValueOperandToken< double >( nStartRow ) :
+ (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ pushFunctionOperatorToken( *pRowsInfo, 1 ) &&
+ pushValueOperandToken< double >( nHeight - nStartRow ) &&
+ pushBinaryOperatorToken( OPCODE_SUB ))) &&
+ pushValueOperandToken< double >( nStartCol ) &&
+ (bFixedHeight ?
+ pushValueOperandToken< double >( nEndRow - nStartRow + 1 ) :
+ (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ pushFunctionOperatorToken( *pRowsInfo, 1 ) &&
+ (((nStartRow == 0) && (nEndRow + 1 == nHeight)) ||
+ (pushValueOperandToken< double >( nHeight - (nEndRow - nStartRow + 1) ) &&
+ pushBinaryOperatorToken( OPCODE_SUB ))))) &&
+ (((nStartCol == 0) && (nEndCol + 1 == nWidth)) ?
+ (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ pushFunctionOperatorToken( *pColumnsInfo, 1 )) :
+ pushValueOperandToken< double >( nEndCol - nStartCol + 1 )) &&
+ pushBiff12Function( BIFF_FUNC_OFFSET, 5 );
+ }
+ }
+ return pushBiffErrorOperand( BIFF_ERR_REF );
+}
+
+bool OoxFormulaParserImpl::importArrayToken( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 14 );
+
+ // start token array with opening brace and leading spaces
+ pushOperand( OPCODE_ARRAY_OPEN );
+ size_t nOpSize = popOperandSize();
+ size_t nOldArraySize = getFormulaSize();
+
+ // read array size
+ swapStreamPosition( rStrm );
+ sal_Int32 nRows = rStrm.readInt32();
+ sal_Int32 nCols = rStrm.readInt32();
+ OSL_ENSURE( (nCols > 0) && (nRows > 0), "OoxFormulaParserImpl::importArrayToken - empty array" );
+
+ // read array values and build token array
+ for( sal_Int32 nRow = 0; !rStrm.isEof() && (nRow < nRows); ++nRow )
+ {
+ if( nRow > 0 )
+ appendRawToken( OPCODE_ARRAY_ROWSEP );
+ for( sal_Int32 nCol = 0; !rStrm.isEof() && (nCol < nCols); ++nCol )
+ {
+ if( nCol > 0 )
+ appendRawToken( OPCODE_ARRAY_COLSEP );
+ switch( rStrm.readuInt8() )
+ {
+ case BIFF_TOK_ARRAY_DOUBLE:
+ appendRawToken( OPCODE_PUSH ) <<= rStrm.readDouble();
+ break;
+ case BIFF_TOK_ARRAY_STRING:
+ appendRawToken( OPCODE_PUSH ) <<= BiffHelper::readString( rStrm, false );
+ break;
+ case BIFF_TOK_ARRAY_BOOL:
+ appendRawToken( OPCODE_PUSH ) <<= static_cast< double >( (rStrm.readuInt8() == BIFF_TOK_BOOL_FALSE) ? 0.0 : 1.0 );
+ break;
+ case BIFF_TOK_ARRAY_ERROR:
+ appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( rStrm.readuInt8() );
+ rStrm.skip( 3 );
+ break;
+ default:
+ OSL_FAIL( "OoxFormulaParserImpl::importArrayToken - unknown data type" );
+ appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NA );
+ }
+ }
+ }
+ swapStreamPosition( rStrm );
+
+ // close token array and set resulting operand size
+ appendRawToken( OPCODE_ARRAY_CLOSE );
+ pushOperandSize( nOpSize + getFormulaSize() - nOldArraySize );
+ return true;
+}
+
+bool OoxFormulaParserImpl::importRefToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ BinSingleRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importAreaToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ BinComplexRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importRef3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ LinkSheetRange aSheetRange = readSheetRange( rStrm );
+ BinSingleRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importArea3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ LinkSheetRange aSheetRange = readSheetRange( rStrm );
+ BinComplexRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importMemAreaToken( SequenceInputStream& rStrm, bool bAddData )
+{
+ rStrm.skip( 6 );
+ if( bAddData )
+ skipMemAreaAddData( rStrm );
+ return true;
+}
+
+bool OoxFormulaParserImpl::importMemFuncToken( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 );
+ return true;
+}
+
+bool OoxFormulaParserImpl::importNameToken( SequenceInputStream& rStrm )
+{
+ return pushBiff12Name( rStrm.readInt32() );
+}
+
+bool OoxFormulaParserImpl::importNameXToken( SequenceInputStream& rStrm )
+{
+ sal_Int32 nRefId = rStrm.readInt16();
+ sal_Int32 nNameId = rStrm.readInt32();
+ return pushBiff12ExtName( nRefId, nNameId );
+}
+
+bool OoxFormulaParserImpl::importFuncToken( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFuncId;
+ nFuncId = rStrm.readuInt16();
+ return pushBiff12Function( nFuncId );
+}
+
+bool OoxFormulaParserImpl::importFuncVarToken( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nParamCount;
+ sal_uInt16 nFuncId;
+ nParamCount = rStrm.readuChar();
+ nFuncId = rStrm.readuInt16();
+ return pushBiff12Function( nFuncId, nParamCount );
+}
+
+bool OoxFormulaParserImpl::importExpToken( SequenceInputStream& rStrm )
+{
+ BinAddress aBaseAddr;
+ aBaseAddr.mnRow = rStrm.readInt32();
+ swapStreamPosition( rStrm );
+ aBaseAddr.mnCol = rStrm.readInt32();
+ swapStreamPosition( rStrm );
+ return pushSpecialTokenOperand( aBaseAddr );
+}
+
+LinkSheetRange OoxFormulaParserImpl::readSheetRange( SequenceInputStream& rStrm )
+{
+ return getExternalLinks().getSheetRange( rStrm.readInt16() );
+}
+
+void OoxFormulaParserImpl::swapStreamPosition( SequenceInputStream& rStrm )
+{
+ sal_Int64 nRecPos = rStrm.tell();
+ rStrm.seek( mnAddDataPos );
+ mnAddDataPos = nRecPos;
+}
+
+void OoxFormulaParserImpl::skipMemAreaAddData( SequenceInputStream& rStrm )
+{
+ swapStreamPosition( rStrm );
+ rStrm.skip( 16 * rStrm.readInt32() );
+ swapStreamPosition( rStrm );
+}
+
+// convert BIN token and push API operand or operator -------------------------
+
+bool OoxFormulaParserImpl::pushBiff12Name( sal_Int32 nNameId )
+{
+ // one-based in BIFF12 formulas
+ return pushDefinedNameOperand( getDefinedNames().getByIndex( nNameId - 1 ) );
+}
+
+bool OoxFormulaParserImpl::pushBiff12ExtName( sal_Int32 nRefId, sal_Int32 nNameId )
+{
+ if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() )
+ {
+ if( pExtLink->getLinkType() == ExternalLinkType::Self )
+ return pushBiff12Name( nNameId );
+ // external name indexes are one-based in BIFF12
+ ExternalNameRef xExtName = pExtLink->getNameByIndex( nNameId - 1 );
+ return pushExternalNameOperand( xExtName, *pExtLink );
+ }
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+}
+
+bool OoxFormulaParserImpl::pushBiff12Function( sal_uInt16 nFuncId )
+{
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( nFuncId ) )
+ if( pFuncInfo->mnMinParamCount == pFuncInfo->mnMaxParamCount )
+ return pushFunctionOperator( *pFuncInfo, pFuncInfo->mnMinParamCount );
+ return pushFunctionOperator( OPCODE_NONAME, 0 );
+}
+
+bool OoxFormulaParserImpl::pushBiff12Function( sal_uInt16 nFuncId, sal_uInt8 nParamCount )
+{
+ if( getFlag( nFuncId, BIFF_TOK_FUNCVAR_CMD ) )
+ nParamCount &= BIFF_TOK_FUNCVAR_COUNTMASK;
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( nFuncId ) )
+ return pushFunctionOperator( *pFuncInfo, nParamCount );
+ return pushFunctionOperator( OPCODE_NONAME, nParamCount );
+}
+
+namespace {
+
+/** Extracts the reference identifier and the remaining data from a formula in
+ the format '[RefID]Remaining'. */
+bool lclExtractRefId( sal_Int32& rnRefId, OUString& rRemainder, std::u16string_view aFormulaString )
+{
+ if( (aFormulaString.size() >= 4) && (aFormulaString[ 0 ] == '[') )
+ {
+ size_t nBracketClose = aFormulaString.find( ']', 1 );
+ if( nBracketClose != std::u16string_view::npos && nBracketClose >= 2 )
+ {
+ rnRefId = o3tl::toInt32(aFormulaString.substr( 1, nBracketClose - 1 ));
+ rRemainder = aFormulaString.substr( nBracketClose + 1 );
+ return !rRemainder.isEmpty();
+ }
+ }
+ return false;
+}
+
+}
+
+FormulaParser::FormulaParser( const WorkbookHelper& rHelper ) :
+ FormulaProcessorBase( rHelper )
+{
+ mxImpl.reset( new OoxFormulaParserImpl( *this ) );
+}
+
+FormulaParser::~FormulaParser()
+{
+}
+
+ApiTokenSequence FormulaParser::importFormula( const ScAddress& rBaseAddress, const OUString& rFormulaString ) const
+{
+ return mxImpl->importOoxFormula( rBaseAddress, rFormulaString );
+}
+
+ApiTokenSequence FormulaParser::importFormula( const ScAddress& rBaseAddress, FormulaType eType, SequenceInputStream& rStrm ) const
+{
+ return mxImpl->importBiff12Formula( rBaseAddress, eType, rStrm );
+}
+
+OUString FormulaParser::importOleTargetLink( std::u16string_view aFormulaString )
+{
+ sal_Int32 nRefId = -1;
+ OUString aRemainder;
+ if( lclExtractRefId( nRefId, aRemainder, aFormulaString ) && (aRemainder.getLength() >= 3) &&
+ (aRemainder[ 0 ] == '!') && (aRemainder[ 1 ] == '\'') && (aRemainder[ aRemainder.getLength() - 1 ] == '\'') )
+ return mxImpl->resolveOleTarget( nRefId, false );
+ return OUString();
+}
+
+OUString FormulaParser::importOleTargetLink( SequenceInputStream& rStrm )
+{
+ OUString aTargetLink;
+ sal_Int32 nFmlaSize = rStrm.readInt32();
+ sal_Int64 nFmlaEndPos = rStrm.tell() + ::std::max< sal_Int32 >( nFmlaSize, 0 );
+ if( (nFmlaSize == 7) && (rStrm.getRemaining() >= 7) )
+ {
+ sal_uInt8 nToken = rStrm.readuChar();
+ sal_Int16 nRefId = rStrm.readInt16();
+ rStrm.skip(4); //nNameId
+ if( nToken == (BIFF_TOKCLASS_VAL|BIFF_TOKID_NAMEX) )
+ aTargetLink = mxImpl->resolveOleTarget( nRefId, true );
+ }
+ rStrm.seek( nFmlaEndPos );
+ return aTargetLink;
+}
+
+OUString FormulaParser::importMacroName( std::u16string_view aFormulaString )
+{
+ /* Valid macros are either sheet macros or VBA macros. OOXML and all BIFF
+ documents store defined names for sheet macros, but OOXML documents do
+ not store any defined name for VBA macros (while BIFF documents do).
+ Sheet macros may be defined locally to a sheet, or globally to the
+ document. As a result, all of the following macro specifiers are valid:
+
+ 1) Macros located in the own document:
+ [0]!MySheetMacro (global sheet macro 'MySheetMacro')
+ Macro1!MyMacro (sheet-local sheet macro 'MyMacro')
+ [0]!MyVBAProc (VBA macro 'MyVBAProc')
+ [0]!Mod1.MyVBAProc (VBA macro 'MyVBAProc' from code module 'Mod1')
+
+ 2) Macros from an external document:
+ [2]!MySheetMacro (global external sheet macro 'MySheetMacro')
+ [2]Macro1!MyMacro (sheet-local external sheet macro 'MyMacro')
+ [2]!MyVBAProc (external VBA macro 'MyVBAProc')
+ [2]!Mod1.MyVBAProc (external VBA macro from code module 'Mod1')
+
+ This implementation is only interested in VBA macros from the own
+ document, ignoring the valid syntax 'Macro1!MyMacro' for sheet-local
+ sheet macros.
+ */
+ OUString aRemainder(aFormulaString);
+ if (aRemainder.indexOf('!') == -1)
+ return aRemainder;
+
+ sal_Int32 nRefId = -1;
+ if( lclExtractRefId( nRefId, aRemainder, aFormulaString ) && (aRemainder.getLength() > 1) && (aRemainder[ 0 ] == '!') )
+ {
+ /* In BIFF12 documents, the reference identifier is always the
+ one-based index of the external link as it is in OOXML documents
+ (it is not an index into the list of reference sheets as used in
+ cell formulas). Index 0 is an implicit placeholder for the own
+ document. In BIFF12 documents, the reference to the own document is
+ stored explicitly, mostly at the top of the list, so index 1 may
+ resolve to the own document too.
+ Passing 'false' to getExternalLink() specifies to ignore the
+ reference sheets list (if existing) and to access the list of
+ external links directly. */
+ const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId, false ).get();
+ OSL_ENSURE( pExtLink, "FormulaParser::importMacroName - missing link" );
+ // do not accept macros in external documents (not supported)
+ if( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::Self) )
+ {
+ // ignore sheet macros (defined name for VBA macros may not exist, see above)
+ OUString aMacroName = aRemainder.copy( 1 );
+ const DefinedName* pDefName = getDefinedNames().getByModelName( aMacroName ).get();
+ if( !pDefName || pDefName->isVBName() )
+ return aMacroName;
+ }
+ }
+ return OUString();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/numberformatsbuffer.cxx b/sc/source/filter/oox/numberformatsbuffer.cxx
new file mode 100644
index 0000000000..5fa701b3f1
--- /dev/null
+++ b/sc/source/filter/oox/numberformatsbuffer.cxx
@@ -0,0 +1,2098 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <numberformatsbuffer.hxx>
+#include <biffhelper.hxx>
+
+#include <com/sun/star/i18n/NumberFormatIndex.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <officecfg/Setup.hxx>
+#include <officecfg/System.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/ustrbuf.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <scitems.hxx>
+#include <document.hxx>
+#include <ftools.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+namespace {
+
+/** Stores the number format used in Calc for an Excel built-in number format. */
+struct BuiltinFormat
+{
+ sal_Int32 mnNumFmtId; /// Built-in number format index.
+ const char* mpcFmtCode; /// Format string, UTF-8, may be 0 (mnPredefId is used then).
+ sal_Int16 mnPredefId; /// Predefined format index, if mpcFmtCode is 0.
+ sal_Int32 mnReuseId; /// Use this format, if mpcFmtCode is 0 and mnPredefId is -1.
+};
+
+/** Defines a literal built-in number format. */
+#define NUMFMT_STRING( INDEX, FORMATCODE ) \
+ { INDEX, FORMATCODE, -1, -1 }
+
+/** Defines a built-in number format that maps to an own predefined format. */
+#define NUMFMT_PREDEF( INDEX, PREDEFINED ) \
+ { INDEX, nullptr, css::i18n::NumberFormatIndex::PREDEFINED, -1 }
+
+/** Defines a built-in number format that is the same as the specified in nReuseId. */
+#define NUMFMT_REUSE( INDEX, REUSED_INDEX ) \
+ { INDEX, nullptr, -1, REUSED_INDEX }
+
+/** Terminates a built-in number format table. */
+#define NUMFMT_ENDTABLE() \
+ { -1, nullptr, -1, -1 }
+
+/** Defines builtin date and time formats 14...22.
+ @param SYSTEMDATE Complete short system date (for formats 14 and 22).
+ @param DAY Day format (for formats 15 and 16).
+ @param DAYSEP Separator between day and month (for formats 15 and 16).
+ @param MONTH Month format (for formats 15...17).
+ @param MONTHSEP Separator between month and year (for formats 15 and 17).
+ @param YEAR Year format (for formats 15 and 17).
+ @param HOUR12 Hour format for 12-hour AM/PM formats (formats 18 and 19).
+ @param HOUR24 Hour format for 24-hour formats (formats 20...22). */
+#define NUMFMT_ALLDATETIMES( SYSTEMDATE, DAY, DAYSEP, MONTH, MONTHSEP, YEAR, HOUR12, HOUR24 ) \
+ NUMFMT_STRING( 14, SYSTEMDATE ), \
+ NUMFMT_STRING( 15, DAY DAYSEP MONTH MONTHSEP YEAR ), \
+ NUMFMT_STRING( 16, DAY DAYSEP MONTH ), \
+ NUMFMT_STRING( 17, MONTH MONTHSEP YEAR ), \
+ NUMFMT_STRING( 18, HOUR12 ":mm AM/PM" ), \
+ NUMFMT_STRING( 19, HOUR12 ":mm:ss AM/PM" ), \
+ NUMFMT_STRING( 20, HOUR24 ":mm" ), \
+ NUMFMT_STRING( 21, HOUR24 ":mm:ss" ), \
+ NUMFMT_STRING( 22, SYSTEMDATE " " HOUR24 ":mm" )
+
+/** Defines builtin time formats INDEX and INDEX+1 for CJK locales.
+ @param INDEX First number format index.
+ @param HOURFORMAT Hour format.
+ @param HOUR Hour symbol.
+ @param MINUTE Minute symbol.
+ @param SECOND Second symbol. */
+#define NUMFMT_TIME_CJK( INDEX, HOURFORMAT, HOUR, MINUTE, SECOND ) \
+ NUMFMT_STRING( INDEX + 0, HOURFORMAT "\"" HOUR "\"mm\"" MINUTE "\"" ), \
+ NUMFMT_STRING( INDEX + 1, HOURFORMAT "\"" HOUR "\"mm\"" MINUTE "\"ss\"" SECOND "\"" )
+
+/** Defines builtin time formats 32...35 for CJK locales.
+ @param HOUR12 Hour format for 12-hour AM/PM formats (formats 34 and 35).
+ @param HOUR24 Hour format for 24-hour formats (formats 32 and 33).
+ @param HOUR Hour symbol.
+ @param MINUTE Minute symbol.
+ @param SECOND Second symbol. */
+#define NUMFMT_ALLTIMES_CJK( HOUR12, HOUR24, HOUR, MINUTE, SECOND ) \
+ NUMFMT_TIME_CJK( 32, HOUR24, HOUR, MINUTE, SECOND ), \
+ NUMFMT_TIME_CJK( 34, "AM/PM" HOUR12, HOUR, MINUTE, SECOND )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "symbol, [minus], number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0;" MODIF SYMBOL SPACE "-#,##0" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0;" "[RED]" MODIF SYMBOL SPACE "-#,##0" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00;" MODIF SYMBOL SPACE "-#,##0.00" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00;" "[RED]" MODIF SYMBOL SPACE "-#,##0.00" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "symbol, [minus], number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_SYMBOL_MINUS_NUMBER( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_ " "* #,##0_ ;" "_ " "* -#,##0_ ;" "_ " "* \"-\"_ ;" "_ @_ " ), \
+ NUMFMT_STRING( INDEX + 1, "_ " SYMBOL SPACE "* #,##0_ ;" "_ " SYMBOL SPACE "* -#,##0_ ;" "_ " SYMBOL SPACE "* \"-\"_ ;" "_ @_ " ), \
+ NUMFMT_STRING( INDEX + 2, "_ " "* #,##0.00_ ;" "_ " "* -#,##0.00_ ;" "_ " "* \"-\"?\?_ ;" "_ @_ " ), \
+ NUMFMT_STRING( INDEX + 3, "_ " SYMBOL SPACE "* #,##0.00_ ;" "_ " SYMBOL SPACE "* -#,##0.00_ ;" "_ " SYMBOL SPACE "* \"-\"?\?_ ;" "_ @_ " )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "symbol, [minus], number".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_SYMBOL_MINUS_NUMBER( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "symbol, number, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0_-;" MODIF SYMBOL SPACE "#,##0-" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0_-;" "[RED]" MODIF SYMBOL SPACE "#,##0-" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00_-;" MODIF SYMBOL SPACE "#,##0.00-" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00_-;" "[RED]" MODIF SYMBOL SPACE "#,##0.00-" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "symbol, number, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_SYMBOL_NUMBER_MINUS( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-" "* #,##0_-;" "_-" "* #,##0-;" "_-" "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-" SYMBOL SPACE "* #,##0_-;" "_-" SYMBOL SPACE "* #,##0-;" "_-" SYMBOL SPACE "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-" "* #,##0.00_-;" "_-" "* #,##0.00-;" "_-" "* \"-\"?\?_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-" SYMBOL SPACE "* #,##0.00_-;" "_-" SYMBOL SPACE "* #,##0.00-;" "_-" SYMBOL SPACE "* \"-\"?\?_-;" "_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "symbol, number, [minus]".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_SYMBOL_NUMBER_MINUS( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "number, symbol, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between number and currency symbol.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL "_-;" MODIF "#,##0" SPACE SYMBOL "-" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL "_-;" "[RED]" MODIF "#,##0" SPACE SYMBOL "-" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL "_-;" MODIF "#,##0.00" SPACE SYMBOL "-" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL "_-;" "[RED]" MODIF "#,##0.00" SPACE SYMBOL "-" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "number, symbol, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ACCOUNTING_NUMBER_SYMBOL_MINUS( INDEX, SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-* #,##0" SPACE BLINDS "_-;_-* #,##0" SPACE BLINDS "-;_-* \"-\"" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-* #,##0" SPACE SYMBOL "_-;_-* #,##0" SPACE SYMBOL "-;_-* \"-\"" SPACE SYMBOL "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-* #,##0.00" SPACE BLINDS "_-;_-* #,##0.00" SPACE BLINDS "-;_-* \"-\"?\?" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-* #,##0.00" SPACE SYMBOL "_-;_-* #,##0.00" SPACE SYMBOL "-;_-* \"-\"?\?" SPACE SYMBOL "_-;_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "number, symbol, [minus]".
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ALLCURRENCIES_NUMBER_SYMBOL_MINUS( SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( 37, BLINDS, SPACE, "" ), \
+ NUMFMT_ACCOUNTING_NUMBER_SYMBOL_MINUS( 41, SYMBOL, BLINDS, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[minus], symbol, number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0;" MODIF "-" SYMBOL SPACE "#,##0" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0;" "[RED]" MODIF "-" SYMBOL SPACE "#,##0" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00;" MODIF "-" SYMBOL SPACE "#,##0.00" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00;" "[RED]" MODIF "-" SYMBOL SPACE "#,##0.00" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following order:
+ "[minus], symbol, number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-" "* #,##0_-;" "-" "* #,##0_-;" "_-" "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-" SYMBOL SPACE "* #,##0_-;" "-" SYMBOL SPACE "* #,##0_-;" "_-" SYMBOL SPACE "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-" "* #,##0.00_-;" "-" "* #,##0.00_-;" "_-" "* \"-\"?\?_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-" SYMBOL SPACE "* #,##0.00_-;" "-" SYMBOL SPACE "* #,##0.00_-;" "_-" SYMBOL SPACE "* \"-\"?\?_-;" "_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following order:
+ "[minus], symbol, number".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[minus], number, symbol".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between number and currency symbol.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL ";" MODIF "-#,##0" SPACE SYMBOL ), \
+ NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL ";" "[RED]" MODIF "-#,##0" SPACE SYMBOL ), \
+ NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL ";" MODIF "-#,##0.00" SPACE SYMBOL ), \
+ NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL ";" "[RED]" MODIF "-#,##0.00" SPACE SYMBOL )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "[minus], number, symbol".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ACCOUNTING_MINUS_NUMBER_SYMBOL( INDEX, SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-* #,##0" SPACE BLINDS "_-;-* #,##0" SPACE BLINDS "_-;_-* \"-\"" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-* #,##0" SPACE SYMBOL "_-;-* #,##0" SPACE SYMBOL "_-;_-* \"-\"" SPACE SYMBOL "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-* #,##0.00" SPACE BLINDS "_-;-* #,##0.00" SPACE BLINDS "_-;_-* \"-\"?\?" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-* #,##0.00" SPACE SYMBOL "_-;-* #,##0.00" SPACE SYMBOL "_-;_-* \"-\"?\?" SPACE SYMBOL "_-;_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "[minus], number, symbol".
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( 37, BLINDS, SPACE, "" ), \
+ NUMFMT_ACCOUNTING_MINUS_NUMBER_SYMBOL( 41, SYMBOL, BLINDS, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], symbol, number, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0_);" MODIF "(" SYMBOL SPACE "#,##0)" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0_);" "[RED]" MODIF "(" SYMBOL SPACE "#,##0)" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00_);" MODIF "(" SYMBOL SPACE "#,##0.00)" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00_);" "[RED]" MODIF "(" SYMBOL SPACE "#,##0.00)" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], symbol, number, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_OPEN_SYMBOL_NUMBER_CLOSE( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_(" "* #,##0_);" "_(" "* (#,##0);" "_(" "* \"-\"_);" "_(@_)" ), \
+ NUMFMT_STRING( INDEX + 1, "_(" SYMBOL SPACE "* #,##0_);" "_(" SYMBOL SPACE "* (#,##0);" "_(" SYMBOL SPACE "* \"-\"_);" "_(@_)" ), \
+ NUMFMT_STRING( INDEX + 2, "_(" "* #,##0.00_);" "_(" "* (#,##0.00);" "_(" "* \"-\"?\?_);" "_(@_)" ), \
+ NUMFMT_STRING( INDEX + 3, "_(" SYMBOL SPACE "* #,##0.00_);" "_(" SYMBOL SPACE "* (#,##0.00);" "_(" SYMBOL SPACE "* \"-\"?\?_);" "_(@_)" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "[opening parenthesis], symbol, number, [closing parenthesis].".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_OPEN_SYMBOL_NUMBER_CLOSE( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], number, symbol, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between number and currency symbol.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL "_);" MODIF "(#,##0" SPACE SYMBOL ")" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL "_);" "[RED]" MODIF "(#,##0" SPACE SYMBOL ")" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL "_);" MODIF "(#,##0.00" SPACE SYMBOL ")" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL "_);" "[RED]" MODIF "(#,##0.00" SPACE SYMBOL ")" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], number, symbol, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ACCOUNTING_OPEN_NUMBER_SYMBOL_CLOSE( INDEX, SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_ * #,##0_)" SPACE BLINDS "_ ;_ * (#,##0)" SPACE BLINDS "_ ;_ * \"-\"_)" SPACE BLINDS "_ ;_ @_ " ), \
+ NUMFMT_STRING( INDEX + 1, "_ * #,##0_)" SPACE SYMBOL "_ ;_ * (#,##0)" SPACE SYMBOL "_ ;_ * \"-\"_)" SPACE SYMBOL "_ ;_ @_ " ), \
+ NUMFMT_STRING( INDEX + 2, "_ * #,##0.00_)" SPACE BLINDS "_ ;_ * (#,##0.00)" SPACE BLINDS "_ ;_ * \"-\"?\?_)" SPACE BLINDS "_ ;_ @_ " ), \
+ NUMFMT_STRING( INDEX + 3, "_ * #,##0.00_)" SPACE SYMBOL "_ ;_ * (#,##0.00)" SPACE SYMBOL "_ ;_ * \"-\"?\?_)" SPACE SYMBOL "_ ;_ @_ " )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "[opening parenthesis], number, symbol, [closing parenthesis].".
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ALLCURRENCIES_OPEN_NUMBER_SYMBOL_CLOSE( SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( 37, BLINDS, SPACE, "" ), \
+ NUMFMT_ACCOUNTING_OPEN_NUMBER_SYMBOL_CLOSE( 41, SYMBOL, BLINDS, SPACE )
+
+// currency unit characters
+#define UTF8_BAHT "\340\270\277"
+#define UTF8_COLON "\342\202\241"
+#define UTF8_CURR_AR_AE "\330\257.\330\245."
+#define UTF8_CURR_AR_BH "\330\257.\330\250."
+#define UTF8_CURR_AR_DZ "\330\257.\330\254."
+#define UTF8_CURR_AR_EG "\330\254.\331\205."
+#define UTF8_CURR_AR_IQ "\330\257.\330\271."
+#define UTF8_CURR_AR_JO "\330\257.\330\247."
+#define UTF8_CURR_AR_KW "\330\257.\331\203."
+#define UTF8_CURR_AR_LB "\331\204.\331\204."
+#define UTF8_CURR_AR_LY "\330\257.\331\204."
+#define UTF8_CURR_AR_MA "\330\257.\331\205."
+#define UTF8_CURR_AR_OM "\330\261.\330\271."
+#define UTF8_CURR_AR_QA "\330\261.\331\202."
+#define UTF8_CURR_AR_SA "\330\261.\330\263."
+#define UTF8_CURR_AR_SY "\331\204.\330\263."
+#define UTF8_CURR_AR_TN "\330\257.\330\252."
+#define UTF8_CURR_AR_YE "\330\261.\331\212."
+#define UTF8_CURR_BN_IN "\340\246\237\340\246\276"
+#define UTF8_CURR_FA_IR "\330\261\331\212\330\247\331\204"
+#define UTF8_CURR_GU_IN "\340\252\260\340\253\202"
+#define UTF8_CURR_HI_IN "\340\244\260\340\245\201"
+#define UTF8_CURR_KN_IN "\340\262\260\340\263\202"
+#define UTF8_CURR_ML_IN "\340\264\225"
+#define UTF8_CURR_PA_IN "\340\250\260\340\251\201"
+#define UTF8_CURR_TA_IN "\340\256\260\340\257\202"
+#define UTF8_CURR_TE_IN "\340\260\260\340\261\202"
+#define UTF8_DONG "\342\202\253"
+#define UTF8_EURO "\342\202\254"
+#define UTF8_POUND_GB "\302\243"
+#define UTF8_RUFIYAA "\336\203"
+#define UTF8_SHEQEL "\342\202\252"
+#define UTF8_TUGRUG "\342\202\256"
+#define UTF8_WON "\342\202\251"
+#define UTF8_YEN_CN "\357\277\245"
+#define UTF8_YEN_JP "\302\245"
+
+// Unicode characters for currency units
+#define UTF8_CCARON_LC "\304\215"
+#define UTF8_LSTROKE_LC "\305\202"
+// Armenian
+#define UTF8_HY_DA_LC "\325\244"
+#define UTF8_HY_REH_LC "\326\200"
+// Cyrillic
+#define UTF8_CYR_G_LC "\320\263"
+#define UTF8_CYR_L_LC "\320\273"
+#define UTF8_CYR_M_LC "\320\274"
+#define UTF8_CYR_N_LC "\320\275"
+#define UTF8_CYR_O_LC "\320\276"
+#define UTF8_CYR_R_LC "\321\200"
+#define UTF8_CYR_S_LC "\321\201"
+#define UTF8_CYR_W_LC "\320\262"
+
+// Japanese/Chinese date/time characters
+#define UTF8_CJ_YEAR "\345\271\264"
+#define UTF8_CJ_MON "\346\234\210"
+#define UTF8_CJ_DAY "\346\227\245"
+#define UTF8_CJ_HOUR "\346\231\202"
+#define UTF8_CJ_MIN "\345\210\206"
+#define UTF8_CJ_SEC "\347\247\222"
+
+// Chinese Simplified date/time characters
+#define UTF8_CS_YEAR "\345\271\264"
+#define UTF8_CS_MON "\346\234\210"
+#define UTF8_CS_DAY "\346\227\245"
+#define UTF8_CS_HOUR "\346\227\266"
+#define UTF8_CS_MIN "\345\210\206"
+#define UTF8_CS_SEC "\347\247\222"
+
+// Korean date/time characters
+#define UTF8_KO_YEAR "\353\205\204"
+#define UTF8_KO_MON "\354\233\224"
+#define UTF8_KO_DAY "\354\235\274"
+#define UTF8_KO_HOUR "\354\213\234"
+#define UTF8_KO_MIN "\353\266\204"
+#define UTF8_KO_SEC "\354\264\210"
+
+/** Default number format table. Last parent of all other tables, used for unknown locales. */
+const BuiltinFormat spBuiltinFormats_BASE[] =
+{
+ // 0..13 numeric and currency formats
+ NUMFMT_PREDEF( 0, NUMBER_STANDARD ), // General
+ NUMFMT_PREDEF( 1, NUMBER_INT ), // 0
+ NUMFMT_PREDEF( 2, NUMBER_DEC2 ), // 0.00
+ NUMFMT_PREDEF( 3, NUMBER_1000INT ), // #,##0
+ NUMFMT_PREDEF( 4, NUMBER_1000DEC2 ), // #,##0.00
+ NUMFMT_PREDEF( 5, CURRENCY_1000INT ), // #,##0[symbol]
+ NUMFMT_PREDEF( 6, CURRENCY_1000INT_RED ), // #,##0[symbol];[RED]-#,##0[symbol]
+ NUMFMT_PREDEF( 7, CURRENCY_1000DEC2 ), // #,##0.00[symbol]
+ NUMFMT_PREDEF( 8, CURRENCY_1000DEC2_RED ), // #,##0.00[symbol];[RED]-#,##0.00[symbol]
+ NUMFMT_PREDEF( 9, PERCENT_INT ), // 0%
+ NUMFMT_PREDEF( 10, PERCENT_DEC2 ), // 0.00%
+ NUMFMT_PREDEF( 11, SCIENTIFIC_000E00 ), // 0.00E+00
+ NUMFMT_PREDEF( 12, FRACTION_1 ), // # ?/?
+ NUMFMT_PREDEF( 13, FRACTION_2 ), // # ??/??
+
+ // 14...22 date and time formats
+ NUMFMT_PREDEF( 14, DATE_SYS_DDMMYYYY ),
+ NUMFMT_PREDEF( 15, DATE_SYS_DMMMYY ),
+ NUMFMT_PREDEF( 16, DATE_SYS_DDMMM ),
+ NUMFMT_PREDEF( 17, DATE_SYS_MMYY ),
+ NUMFMT_PREDEF( 18, TIME_HHMMAMPM ),
+ NUMFMT_PREDEF( 19, TIME_HHMMSSAMPM ),
+ NUMFMT_PREDEF( 20, TIME_HHMM ),
+ NUMFMT_PREDEF( 21, TIME_HHMMSS ),
+ NUMFMT_PREDEF( 22, DATETIME_SYSTEM_SHORT_HHMM ),
+
+ // 23...36 international formats
+ NUMFMT_REUSE( 23, 0 ),
+ NUMFMT_REUSE( 24, 0 ),
+ NUMFMT_REUSE( 25, 0 ),
+ NUMFMT_REUSE( 26, 0 ),
+ NUMFMT_REUSE( 27, 14 ),
+ NUMFMT_REUSE( 28, 14 ),
+ NUMFMT_REUSE( 29, 14 ),
+ NUMFMT_REUSE( 30, 14 ),
+ NUMFMT_REUSE( 31, 14 ),
+ NUMFMT_REUSE( 32, 21 ),
+ NUMFMT_REUSE( 33, 21 ),
+ NUMFMT_REUSE( 34, 21 ),
+ NUMFMT_REUSE( 35, 21 ),
+ NUMFMT_REUSE( 36, 14 ),
+
+ // 37...44 accounting formats, defaults without currency symbol here
+ NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 37, "", "", "" ),
+ NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( 41, "", "" ),
+
+ // 45...49 more special formats
+ NUMFMT_STRING( 45, "mm:ss" ),
+ NUMFMT_STRING( 46, "[h]:mm:ss" ),
+ NUMFMT_STRING( 47, "mm:ss.0" ),
+ NUMFMT_STRING( 48, "##0.0E+0" ),
+ NUMFMT_PREDEF( 49, TEXT ),
+
+ // 50...81 international formats
+ NUMFMT_REUSE( 50, 14 ),
+ NUMFMT_REUSE( 51, 14 ),
+ NUMFMT_REUSE( 52, 14 ),
+ NUMFMT_REUSE( 53, 14 ),
+ NUMFMT_REUSE( 54, 14 ),
+ NUMFMT_REUSE( 55, 14 ),
+ NUMFMT_REUSE( 56, 14 ),
+ NUMFMT_REUSE( 57, 14 ),
+ NUMFMT_REUSE( 58, 14 ),
+ NUMFMT_REUSE( 59, 1 ),
+ NUMFMT_REUSE( 60, 2 ),
+ NUMFMT_REUSE( 61, 3 ),
+ NUMFMT_REUSE( 62, 4 ),
+ NUMFMT_REUSE( 63, 5 ),
+ NUMFMT_REUSE( 64, 6 ),
+ NUMFMT_REUSE( 65, 7 ),
+ NUMFMT_REUSE( 66, 8 ),
+ NUMFMT_REUSE( 67, 9 ),
+ NUMFMT_REUSE( 68, 10 ),
+ NUMFMT_REUSE( 69, 12 ),
+ NUMFMT_REUSE( 70, 13 ),
+ NUMFMT_REUSE( 71, 14 ),
+ NUMFMT_REUSE( 72, 14 ),
+ NUMFMT_REUSE( 73, 15 ),
+ NUMFMT_REUSE( 74, 16 ),
+ NUMFMT_REUSE( 75, 17 ),
+ NUMFMT_REUSE( 76, 20 ),
+ NUMFMT_REUSE( 77, 21 ),
+ NUMFMT_REUSE( 78, 22 ),
+ NUMFMT_REUSE( 79, 45 ),
+ NUMFMT_REUSE( 80, 46 ),
+ NUMFMT_REUSE( 81, 47 ),
+
+ // 82...163 not used, must not occur in a file (Excel may crash)
+
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, U.A.E. */
+const BuiltinFormat spBuiltinFormats_ar_AE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_AE "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Bahrain. */
+const BuiltinFormat spBuiltinFormats_ar_BH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_BH "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Algeria. */
+const BuiltinFormat spBuiltinFormats_ar_DZ[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_DZ "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Egypt. */
+const BuiltinFormat spBuiltinFormats_ar_EG[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_EG "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Iraq. */
+const BuiltinFormat spBuiltinFormats_ar_IQ[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_IQ "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Jordan. */
+const BuiltinFormat spBuiltinFormats_ar_JO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_JO "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Kuwait. */
+const BuiltinFormat spBuiltinFormats_ar_KW[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_KW "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Lebanon. */
+const BuiltinFormat spBuiltinFormats_ar_LB[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_LB "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Libya. */
+const BuiltinFormat spBuiltinFormats_ar_LY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_LY "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Morocco. */
+const BuiltinFormat spBuiltinFormats_ar_MA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_MA "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Oman. */
+const BuiltinFormat spBuiltinFormats_ar_OM[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_OM "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Qatar. */
+const BuiltinFormat spBuiltinFormats_ar_QA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_QA "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Saudi Arabia. */
+const BuiltinFormat spBuiltinFormats_ar_SA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_SA "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Syria. */
+const BuiltinFormat spBuiltinFormats_ar_SY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_SY "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Tunisia. */
+const BuiltinFormat spBuiltinFormats_ar_TN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_TN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Yemen. */
+const BuiltinFormat spBuiltinFormats_ar_YE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_YE "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Belarusian, Belarus. */
+const BuiltinFormat spBuiltinFormats_be_BY[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Bulgarian, Bulgaria. */
+const BuiltinFormat spBuiltinFormats_bg_BG[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.M.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_L_LC UTF8_CYR_W_LC "\"", "_" UTF8_CYR_L_LC "_" UTF8_CYR_W_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Bengali, India. */
+const BuiltinFormat spBuiltinFormats_bn_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_BN_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Czech, Czech Republic. */
+const BuiltinFormat spBuiltinFormats_cs_CZ[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"K" UTF8_CCARON_LC "\"", "_K_" UTF8_CCARON_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Danish, Denmark. */
+const BuiltinFormat spBuiltinFormats_da_DK[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Austria. */
+const BuiltinFormat spBuiltinFormats_de_AT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Switzerland. */
+const BuiltinFormat spBuiltinFormats_de_CH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Germany. */
+const BuiltinFormat spBuiltinFormats_de_DE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Liechtenstein. */
+const BuiltinFormat spBuiltinFormats_de_LI[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"CHF\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Luxembourg. */
+const BuiltinFormat spBuiltinFormats_de_LU[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Divehi, Maldives. */
+const BuiltinFormat spBuiltinFormats_div_MV[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_NUMBER_SYMBOL_MINUS( "\"" UTF8_RUFIYAA ".\"", "_" UTF8_RUFIYAA "_.", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Greek, Greece. */
+const BuiltinFormat spBuiltinFormats_el_GR[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Australia. */
+const BuiltinFormat spBuiltinFormats_en_AU[] =
+{
+ NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Belize. */
+const BuiltinFormat spBuiltinFormats_en_BZ[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"BZ$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Canada. */
+const BuiltinFormat spBuiltinFormats_en_CA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Caribbean. */
+const BuiltinFormat spBuiltinFormats_en_CB[] =
+{
+ NUMFMT_ALLDATETIMES( "MM/DD/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, United Kingdom. */
+const BuiltinFormat spBuiltinFormats_en_GB[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_POUND_GB, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Ireland. */
+const BuiltinFormat spBuiltinFormats_en_IE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Jamaica. */
+const BuiltinFormat spBuiltinFormats_en_JM[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"J$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, New Zealand. */
+const BuiltinFormat spBuiltinFormats_en_NZ[] =
+{
+ NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Philippines. */
+const BuiltinFormat spBuiltinFormats_en_PH[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Php\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Trinidad and Tobago. */
+const BuiltinFormat spBuiltinFormats_en_TT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"TT$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, USA. */
+const BuiltinFormat spBuiltinFormats_en_US[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, South Africa. */
+const BuiltinFormat spBuiltinFormats_en_ZA[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\\R", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Zimbabwe. */
+const BuiltinFormat spBuiltinFormats_en_ZW[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Z$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Argentina. */
+const BuiltinFormat spBuiltinFormats_es_AR[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Bolivia. */
+const BuiltinFormat spBuiltinFormats_es_BO[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"$b\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Chile. */
+const BuiltinFormat spBuiltinFormats_es_CL[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Colombia. */
+const BuiltinFormat spBuiltinFormats_es_CO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Costa Rica. */
+const BuiltinFormat spBuiltinFormats_es_CR[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( UTF8_COLON, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Dominican Republic. */
+const BuiltinFormat spBuiltinFormats_es_DO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"RD$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Ecuador. */
+const BuiltinFormat spBuiltinFormats_es_EC[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Spain. */
+const BuiltinFormat spBuiltinFormats_es_ES[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Guatemala. */
+const BuiltinFormat spBuiltinFormats_es_GT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\Q", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Honduras. */
+const BuiltinFormat spBuiltinFormats_es_HN[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"L.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Mexico. */
+const BuiltinFormat spBuiltinFormats_es_MX[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Nicaragua. */
+const BuiltinFormat spBuiltinFormats_es_NI[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"C$\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Panama. */
+const BuiltinFormat spBuiltinFormats_es_PA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"B/.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Peru. */
+const BuiltinFormat spBuiltinFormats_es_PE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"S/.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Puerto Rico. */
+const BuiltinFormat spBuiltinFormats_es_PR[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Paraguay. */
+const BuiltinFormat spBuiltinFormats_es_PY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Gs\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, El Salvador. */
+const BuiltinFormat spBuiltinFormats_es_SV[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Uruguay. */
+const BuiltinFormat spBuiltinFormats_es_UY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"$U\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Venezuela. */
+const BuiltinFormat spBuiltinFormats_es_VE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "Bs", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Estonian, Estonia. */
+const BuiltinFormat spBuiltinFormats_et_EE[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "D.MM.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr\"", "_k_r", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Farsi, Iran. */
+const BuiltinFormat spBuiltinFormats_fa_IR[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_FA_IR "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Finnish, Finland. */
+const BuiltinFormat spBuiltinFormats_fi_FI[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_STRING( 9, "0\\ %" ),
+ NUMFMT_STRING( 10, "0.00\\ %" ),
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Faroese, Faroe Islands. */
+const BuiltinFormat spBuiltinFormats_fo_FO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Belgium. */
+const BuiltinFormat spBuiltinFormats_fr_BE[] =
+{
+ NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Canada. */
+const BuiltinFormat spBuiltinFormats_fr_CA[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_NUMBER_SYMBOL_CLOSE( "$", "_$", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Switzerland. */
+const BuiltinFormat spBuiltinFormats_fr_CH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, France. */
+const BuiltinFormat spBuiltinFormats_fr_FR[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Luxembourg. */
+const BuiltinFormat spBuiltinFormats_fr_LU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Monaco. */
+const BuiltinFormat spBuiltinFormats_fr_MC[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Galizian, Spain. */
+const BuiltinFormat spBuiltinFormats_gl_ES[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Gujarati, India. */
+const BuiltinFormat spBuiltinFormats_gu_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_GU_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Hebrew, Israel. */
+const BuiltinFormat spBuiltinFormats_he_IL[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( UTF8_SHEQEL, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Hindi, India. */
+const BuiltinFormat spBuiltinFormats_hi_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_HI_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Croatian, Bosnia and Herzegowina. */
+const BuiltinFormat spBuiltinFormats_hr_BA[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"KM\"", "_K_M", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Croatian, Croatia. */
+const BuiltinFormat spBuiltinFormats_hr_HR[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kn\"", "_k_n", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Hungarian, Hungary. */
+const BuiltinFormat spBuiltinFormats_hu_HU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ // MMM is rendered differently in Calc and Excel (see #i41488#)
+ NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Ft\"", "_F_t", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Armenian, Armenia. */
+const BuiltinFormat spBuiltinFormats_hy_AM[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_HY_DA_LC UTF8_HY_REH_LC ".\"", "_" UTF8_HY_DA_LC "_" UTF8_HY_REH_LC "_.", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Indonesian, Indonesia. */
+const BuiltinFormat spBuiltinFormats_id_ID[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Rp\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Icelandic, Iceland. */
+const BuiltinFormat spBuiltinFormats_is_IS[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr.\"", "_k_r_.", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Italian, Switzerland. */
+const BuiltinFormat spBuiltinFormats_it_CH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Italian, Italy. */
+const BuiltinFormat spBuiltinFormats_it_IT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Georgian, Georgia. */
+const BuiltinFormat spBuiltinFormats_ka_GE[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Lari\"", "_L_a_r_i", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Kazakh, Kazakhstan. */
+const BuiltinFormat spBuiltinFormats_kk_KZ[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\\T", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Kannada, India. */
+const BuiltinFormat spBuiltinFormats_kn_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_KN_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Kyrgyz, Kyrgyzstan. */
+const BuiltinFormat spBuiltinFormats_ky_KG[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_S_LC UTF8_CYR_O_LC UTF8_CYR_M_LC "\"", "_" UTF8_CYR_S_LC "_" UTF8_CYR_O_LC "_" UTF8_CYR_M_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Lithuanian, Lithuania. */
+const BuiltinFormat spBuiltinFormats_lt_LT[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Lt\"", "_L_t", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Latvian, Latvia. */
+const BuiltinFormat spBuiltinFormats_lv_LV[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"Ls\"", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Malayalam, India. */
+const BuiltinFormat spBuiltinFormats_ml_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_ML_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Mongolian, Mongolia. */
+const BuiltinFormat spBuiltinFormats_mn_MN[] =
+{
+ NUMFMT_ALLDATETIMES( "YY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_TUGRUG, "_" UTF8_TUGRUG, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Malay, Brunei Darussalam. */
+const BuiltinFormat spBuiltinFormats_ms_BN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Malay, Malaysia. */
+const BuiltinFormat spBuiltinFormats_ms_MY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\R", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Maltese, Malta. */
+const BuiltinFormat spBuiltinFormats_mt_MT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"Lm\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Dutch, Belgium. */
+const BuiltinFormat spBuiltinFormats_nl_BE[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "D\\/MM\\/YYYY", "D", "\\/", "MMM", "\\/", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Dutch, Netherlands. */
+const BuiltinFormat spBuiltinFormats_nl_NL[] =
+{
+ NUMFMT_ALLDATETIMES( "D-M-YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Norwegian (Bokmal and Nynorsk), Norway. */
+const BuiltinFormat spBuiltinFormats_no_NO[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Punjabi, India. */
+const BuiltinFormat spBuiltinFormats_pa_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_PA_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Polish, Poland. */
+const BuiltinFormat spBuiltinFormats_pl_PL[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ // MMM is rendered differently in Calc and Excel (see #i72300#)
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"z" UTF8_LSTROKE_LC "\"", "_z_" UTF8_LSTROKE_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Portuguese, Brazil. */
+const BuiltinFormat spBuiltinFormats_pt_BR[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "/", "MMM", "/", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"R$\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Portuguese, Portugal. */
+const BuiltinFormat spBuiltinFormats_pt_PT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Romanian, Romania. */
+const BuiltinFormat spBuiltinFormats_ro_RO[] =
+{
+ // space character is group separator, literal spaces must be quoted (but see #i75367#)
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"lei\"", "_l_e_i", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Russian, Russian Federation. */
+const BuiltinFormat spBuiltinFormats_ru_RU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Slovak, Slovakia. */
+const BuiltinFormat spBuiltinFormats_sk_SK[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Sk\"", "_S_k", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Slovenian, Slovenia. */
+const BuiltinFormat spBuiltinFormats_sl_SI[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"SIT\"", "_S_I_T", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Swedish, Finland. */
+const BuiltinFormat spBuiltinFormats_sv_FI[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_STRING( 9, "0\\ %" ),
+ NUMFMT_STRING( 10, "0.00\\ %" ),
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Swedish, Sweden. */
+const BuiltinFormat spBuiltinFormats_sv_SE[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr\"", "_k_r", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Swahili, Tanzania. */
+const BuiltinFormat spBuiltinFormats_sw_TZ[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\S", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Tamil, India. */
+const BuiltinFormat spBuiltinFormats_ta_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_TA_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Telugu, India. */
+const BuiltinFormat spBuiltinFormats_te_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_TE_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Thai, Thailand. */
+const BuiltinFormat spBuiltinFormats_th_TH[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_BAHT, "" ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 63, UTF8_BAHT, "", "t" ),
+ NUMFMT_STRING( 59, "t0" ),
+ NUMFMT_STRING( 60, "t0.00" ),
+ NUMFMT_STRING( 61, "t#,##0" ),
+ NUMFMT_STRING( 62, "t#,##0.00" ),
+ NUMFMT_STRING( 67, "t0%" ),
+ NUMFMT_STRING( 68, "t0.00%" ),
+ NUMFMT_STRING( 69, "t# ?/?" ),
+ NUMFMT_STRING( 70, "t# ?\?/?\?" ),
+ NUMFMT_STRING( 71, "tD/M/EE" ),
+ NUMFMT_STRING( 72, "tD-MMM-E" ),
+ NUMFMT_STRING( 73, "tD-MMM" ),
+ NUMFMT_STRING( 74, "tMMM-E" ),
+ NUMFMT_STRING( 75, "th:mm" ),
+ NUMFMT_STRING( 76, "th:mm:ss" ),
+ NUMFMT_STRING( 77, "tD/M/EE h:mm" ),
+ NUMFMT_STRING( 78, "tmm:ss" ),
+ NUMFMT_STRING( 79, "t[h]:mm:ss" ),
+ NUMFMT_STRING( 80, "tmm:ss.0" ),
+ NUMFMT_STRING( 81, "D/M/E" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Turkish, Turkey. */
+const BuiltinFormat spBuiltinFormats_tr_TR[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"TL\"", "_T_L", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Tatar, Russian Federation. */
+const BuiltinFormat spBuiltinFormats_tt_RU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Ukrainian, Ukraine. */
+const BuiltinFormat spBuiltinFormats_uk_UA[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_G_LC UTF8_CYR_R_LC UTF8_CYR_N_LC ".\"", "_" UTF8_CYR_G_LC "_" UTF8_CYR_R_LC "_" UTF8_CYR_N_LC "_.", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Urdu, Pakistan. */
+const BuiltinFormat spBuiltinFormats_ur_PK[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"Rs\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Vietnamese, Viet Nam. */
+const BuiltinFormat spBuiltinFormats_vi_VN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_DONG, "_" UTF8_DONG, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+// CJK ------------------------------------------------------------------------
+
+/** Base table for CJK locales. */
+const BuiltinFormat spBuiltinFormats_CJK[] =
+{
+ NUMFMT_REUSE( 29, 28 ),
+ NUMFMT_REUSE( 36, 27 ),
+ NUMFMT_REUSE( 50, 27 ),
+ NUMFMT_REUSE( 51, 28 ),
+ NUMFMT_REUSE( 52, 34 ),
+ NUMFMT_REUSE( 53, 35 ),
+ NUMFMT_REUSE( 54, 28 ),
+ NUMFMT_REUSE( 55, 34 ),
+ NUMFMT_REUSE( 56, 35 ),
+ NUMFMT_REUSE( 57, 27 ),
+ NUMFMT_REUSE( 58, 28 ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Japanese, Japan. */
+const BuiltinFormat spBuiltinFormats_ja_JP[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_YEN_JP, "" ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "[$-411]GE.MM.DD" ),
+ NUMFMT_STRING( 28, "[$-411]GGGE\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_STRING( 30, "MM/DD/YY" ),
+ NUMFMT_STRING( 31, "YYYY\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_TIME_CJK( 32, "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_STRING( 34, "YYYY\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"" ),
+ NUMFMT_STRING( 35, "MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Korean, South Korea. */
+const BuiltinFormat spBuiltinFormats_ko_KR[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_WON, "" ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ),
+ NUMFMT_STRING( 28, "MM-DD" ),
+ NUMFMT_STRING( 30, "MM-DD-YY" ),
+ NUMFMT_STRING( 31, "YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ),
+ NUMFMT_TIME_CJK( 32, "h", UTF8_KO_HOUR, UTF8_KO_MIN, UTF8_KO_SEC ),
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_STRING( 34, "YYYY\\/MM\\/DD" ),
+ NUMFMT_REUSE( 35, 14 ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, China. */
+const BuiltinFormat spBuiltinFormats_zh_CN[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY-M-D", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( UTF8_YEN_CN, "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CS_HOUR, UTF8_CS_MIN, UTF8_CS_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"" ),
+ NUMFMT_STRING( 28, "M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ),
+ NUMFMT_STRING( 30, "M-D-YY" ),
+ NUMFMT_STRING( 31, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ),
+ NUMFMT_REUSE( 52, 27 ),
+ NUMFMT_REUSE( 53, 28 ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Hong Kong. */
+const BuiltinFormat spBuiltinFormats_zh_HK[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"HK$\"", "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ),
+ NUMFMT_STRING( 27, "[$-404]D/M/E" ),
+ NUMFMT_STRING( 28, "[$-404]D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"E\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"YYYY\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Macau. */
+const BuiltinFormat spBuiltinFormats_zh_MO[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\P", "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ),
+ NUMFMT_STRING( 27, "[$-404]D/M/E" ),
+ NUMFMT_STRING( 28, "[$-404]D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"E\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"YYYY\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Singapore. */
+const BuiltinFormat spBuiltinFormats_zh_SG[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CS_HOUR, UTF8_CS_MIN, UTF8_CS_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"" ),
+ NUMFMT_STRING( 28, "M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "D\"" UTF8_CS_DAY "\"M\"" UTF8_CS_MON "\"YYYY\"" UTF8_CS_YEAR "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Taiwan. */
+const BuiltinFormat spBuiltinFormats_zh_TW[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/M/D", "D", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ALLTIMES_CJK( "hh", "hh", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ),
+ NUMFMT_STRING( 27, "[$-404]E/M/D" ),
+ NUMFMT_STRING( 28, "[$-404]E\"" UTF8_CJ_YEAR "\"M\"" UTF8_CJ_MON "\"D\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "YYYY\"" UTF8_CJ_YEAR "\"M\"" UTF8_CJ_MON "\"D\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Specifies a built-in number format table for a specific locale. */
+struct BuiltinFormatTable
+{
+ const char* mpcLocale; /// The locale for this table.
+ const char* mpcParent; /// The locale of the parent table.
+ const BuiltinFormat* mpFormats; /// The number format table (may be 0, if equal to parent).
+};
+
+const BuiltinFormatTable spBuiltinFormatTables[] =
+{ // locale parent format table
+ { "*", "", spBuiltinFormats_BASE }, // Base table
+ { "af-ZA", "*", spBuiltinFormats_en_ZA }, // Afrikaans, South Africa
+ { "ar-AE", "*", spBuiltinFormats_ar_AE }, // Arabic, U.A.E.
+ { "ar-BH", "*", spBuiltinFormats_ar_BH }, // Arabic, Bahrain
+ { "ar-DZ", "*", spBuiltinFormats_ar_DZ }, // Arabic, Algeria
+ { "ar-EG", "*", spBuiltinFormats_ar_EG }, // Arabic, Egypt
+ { "ar-IQ", "*", spBuiltinFormats_ar_IQ }, // Arabic, Iraq
+ { "ar-JO", "*", spBuiltinFormats_ar_JO }, // Arabic, Jordan
+ { "ar-KW", "*", spBuiltinFormats_ar_KW }, // Arabic, Kuwait
+ { "ar-LB", "*", spBuiltinFormats_ar_LB }, // Arabic, Lebanon
+ { "ar-LY", "*", spBuiltinFormats_ar_LY }, // Arabic, Libya
+ { "ar-MA", "*", spBuiltinFormats_ar_MA }, // Arabic, Morocco
+ { "ar-OM", "*", spBuiltinFormats_ar_OM }, // Arabic, Oman
+ { "ar-QA", "*", spBuiltinFormats_ar_QA }, // Arabic, Qatar
+ { "ar-SA", "*", spBuiltinFormats_ar_SA }, // Arabic, Saudi Arabia
+ { "ar-SY", "*", spBuiltinFormats_ar_SY }, // Arabic, Syria
+ { "ar-TN", "*", spBuiltinFormats_ar_TN }, // Arabic, Tunisia
+ { "ar-YE", "*", spBuiltinFormats_ar_YE }, // Arabic, Yemen
+ { "be-BY", "*", spBuiltinFormats_be_BY }, // Belarusian, Belarus
+ { "bg-BG", "*", spBuiltinFormats_bg_BG }, // Bulgarian, Bulgaria
+ { "bn-IN", "*", spBuiltinFormats_bn_IN }, // Bengali, India
+ { "ca-ES", "*", spBuiltinFormats_es_ES }, // Catalan, Spain
+ { "cs-CZ", "*", spBuiltinFormats_cs_CZ }, // Czech, Czech Republic
+ { "cy-GB", "*", spBuiltinFormats_en_GB }, // Welsh, United Kingdom
+ { "da-DK", "*", spBuiltinFormats_da_DK }, // Danish, Denmark
+ { "de-AT", "*", spBuiltinFormats_de_AT }, // German, Austria
+ { "de-CH", "*", spBuiltinFormats_de_CH }, // German, Switzerland
+ { "de-DE", "*", spBuiltinFormats_de_DE }, // German, Germany
+ { "de-LI", "*", spBuiltinFormats_de_LI }, // German, Liechtenstein
+ { "de-LU", "*", spBuiltinFormats_de_LU }, // German, Luxembourg
+ { "div-MV", "*", spBuiltinFormats_div_MV }, // Divehi, Maldives
+ { "el-GR", "*", spBuiltinFormats_el_GR }, // Greek, Greece
+ { "en-AU", "*", spBuiltinFormats_en_AU }, // English, Australia
+ { "en-BZ", "*", spBuiltinFormats_en_BZ }, // English, Belize
+ { "en-CA", "*", spBuiltinFormats_en_CA }, // English, Canada
+ { "en-CB", "*", spBuiltinFormats_en_CB }, // English, Caribbean
+ { "en-GB", "*", spBuiltinFormats_en_GB }, // English, United Kingdom
+ { "en-IE", "*", spBuiltinFormats_en_IE }, // English, Ireland
+ { "en-JM", "*", spBuiltinFormats_en_JM }, // English, Jamaica
+ { "en-NZ", "*", spBuiltinFormats_en_NZ }, // English, New Zealand
+ { "en-PH", "*", spBuiltinFormats_en_PH }, // English, Philippines
+ { "en-TT", "*", spBuiltinFormats_en_TT }, // English, Trinidad and Tobago
+ { "en-US", "*", spBuiltinFormats_en_US }, // English, USA
+ { "en-ZA", "*", spBuiltinFormats_en_ZA }, // English, South Africa
+ { "en-ZW", "*", spBuiltinFormats_en_ZW }, // English, Zimbabwe
+ { "es-AR", "*", spBuiltinFormats_es_AR }, // Spanish, Argentina
+ { "es-BO", "*", spBuiltinFormats_es_BO }, // Spanish, Bolivia
+ { "es-CL", "*", spBuiltinFormats_es_CL }, // Spanish, Chile
+ { "es-CO", "*", spBuiltinFormats_es_CO }, // Spanish, Colombia
+ { "es-CR", "*", spBuiltinFormats_es_CR }, // Spanish, Costa Rica
+ { "es-DO", "*", spBuiltinFormats_es_DO }, // Spanish, Dominican Republic
+ { "es-EC", "*", spBuiltinFormats_es_EC }, // Spanish, Ecuador
+ { "es-ES", "*", spBuiltinFormats_es_ES }, // Spanish, Spain
+ { "es-GT", "*", spBuiltinFormats_es_GT }, // Spanish, Guatemala
+ { "es-HN", "*", spBuiltinFormats_es_HN }, // Spanish, Honduras
+ { "es-MX", "*", spBuiltinFormats_es_MX }, // Spanish, Mexico
+ { "es-NI", "*", spBuiltinFormats_es_NI }, // Spanish, Nicaragua
+ { "es-PA", "*", spBuiltinFormats_es_PA }, // Spanish, Panama
+ { "es-PE", "*", spBuiltinFormats_es_PE }, // Spanish, Peru
+ { "es-PR", "*", spBuiltinFormats_es_PR }, // Spanish, Puerto Rico
+ { "es-PY", "*", spBuiltinFormats_es_PY }, // Spanish, Paraguay
+ { "es-SV", "*", spBuiltinFormats_es_SV }, // Spanish, El Salvador
+ { "es-UY", "*", spBuiltinFormats_es_UY }, // Spanish, Uruguay
+ { "es-VE", "*", spBuiltinFormats_es_VE }, // Spanish, Venezuela
+ { "et-EE", "*", spBuiltinFormats_et_EE }, // Estonian, Estonia
+ { "fa-IR", "*", spBuiltinFormats_fa_IR }, // Farsi, Iran
+ { "fi-FI", "*", spBuiltinFormats_fi_FI }, // Finnish, Finland
+ { "fo-FO", "*", spBuiltinFormats_fo_FO }, // Faroese, Faroe Islands
+ { "fr-BE", "*", spBuiltinFormats_fr_BE }, // French, Belgium
+ { "fr-CA", "*", spBuiltinFormats_fr_CA }, // French, Canada
+ { "fr-CH", "*", spBuiltinFormats_fr_CH }, // French, Switzerland
+ { "fr-FR", "*", spBuiltinFormats_fr_FR }, // French, France
+ { "fr-LU", "*", spBuiltinFormats_fr_LU }, // French, Luxembourg
+ { "fr-MC", "*", spBuiltinFormats_fr_MC }, // French, Monaco
+ { "gl-ES", "*", spBuiltinFormats_gl_ES }, // Galizian, Spain
+ { "gu-IN", "*", spBuiltinFormats_gu_IN }, // Gujarati, India
+ { "he-IL", "*", spBuiltinFormats_he_IL }, // Hebrew, Israel
+ { "hi-IN", "*", spBuiltinFormats_hi_IN }, // Hindi, India
+ { "hr-BA", "*", spBuiltinFormats_hr_BA }, // Croatian, Bosnia and Herzegowina
+ { "hr-HR", "*", spBuiltinFormats_hr_HR }, // Croatian, Croatia
+ { "hu-HU", "*", spBuiltinFormats_hu_HU }, // Hungarian, Hungary
+ { "hy-AM", "*", spBuiltinFormats_hy_AM }, // Armenian, Armenia
+ { "id-ID", "*", spBuiltinFormats_id_ID }, // Indonesian, Indonesia
+ { "is-IS", "*", spBuiltinFormats_is_IS }, // Icelandic, Iceland
+ { "it-CH", "*", spBuiltinFormats_it_CH }, // Italian, Switzerland
+ { "it-IT", "*", spBuiltinFormats_it_IT }, // Italian, Italy
+ { "ka-GE", "*", spBuiltinFormats_ka_GE }, // Georgian, Georgia
+ { "kk-KZ", "*", spBuiltinFormats_kk_KZ }, // Kazakh, Kazakhstan
+ { "kn-IN", "*", spBuiltinFormats_kn_IN }, // Kannada, India
+ { "kok-IN", "*", spBuiltinFormats_hi_IN }, // Konkani, India
+ { "ky-KG", "*", spBuiltinFormats_ky_KG }, // Kyrgyz, Kyrgyzstan
+ { "lt-LT", "*", spBuiltinFormats_lt_LT }, // Lithuanian, Lithuania
+ { "lv-LV", "*", spBuiltinFormats_lv_LV }, // Latvian, Latvia
+ { "mi-NZ", "*", spBuiltinFormats_en_NZ }, // Maori, New Zealand
+ { "ml-IN", "*", spBuiltinFormats_ml_IN }, // Malayalam, India
+ { "mn-MN", "*", spBuiltinFormats_mn_MN }, // Mongolian, Mongolia
+ { "mr-IN", "*", spBuiltinFormats_hi_IN }, // Marathi, India
+ { "ms-BN", "*", spBuiltinFormats_ms_BN }, // Malay, Brunei Darussalam
+ { "ms-MY", "*", spBuiltinFormats_ms_MY }, // Malay, Malaysia
+ { "mt-MT", "*", spBuiltinFormats_mt_MT }, // Maltese, Malta
+ { "nb-NO", "*", spBuiltinFormats_no_NO }, // Norwegian Bokmal, Norway
+ { "nl-BE", "*", spBuiltinFormats_nl_BE }, // Dutch, Belgium
+ { "nl-NL", "*", spBuiltinFormats_nl_NL }, // Dutch, Netherlands
+ { "nn-NO", "*", spBuiltinFormats_no_NO }, // Norwegian Nynorsk, Norway
+ { "nso-ZA", "*", spBuiltinFormats_en_ZA }, // Northern Sotho, South Africa
+ { "pa-IN", "*", spBuiltinFormats_pa_IN }, // Punjabi, India
+ { "pl-PL", "*", spBuiltinFormats_pl_PL }, // Polish, Poland
+ { "pt-BR", "*", spBuiltinFormats_pt_BR }, // Portuguese, Brazil
+ { "pt-PT", "*", spBuiltinFormats_pt_PT }, // Portuguese, Portugal
+ { "qu-BO", "*", spBuiltinFormats_es_BO }, // Quechua, Bolivia
+ { "qu-EC", "*", spBuiltinFormats_es_EC }, // Quechua, Ecuador
+ { "qu-PE", "*", spBuiltinFormats_es_PE }, // Quechua, Peru
+ { "ro-RO", "*", spBuiltinFormats_ro_RO }, // Romanian, Romania
+ { "ru-RU", "*", spBuiltinFormats_ru_RU }, // Russian, Russian Federation
+ { "sa-IN", "*", spBuiltinFormats_hi_IN }, // Sanskrit, India
+ { "se-FI", "*", spBuiltinFormats_fi_FI }, // Sami, Finland
+ { "se-NO", "*", spBuiltinFormats_no_NO }, // Sami, Norway
+ { "se-SE", "*", spBuiltinFormats_sv_SE }, // Sami, Sweden
+ { "sk-SK", "*", spBuiltinFormats_sk_SK }, // Slovak, Slovakia
+ { "sl-SI", "*", spBuiltinFormats_sl_SI }, // Slovenian, Slovenia
+ { "sv-FI", "*", spBuiltinFormats_sv_FI }, // Swedish, Finland
+ { "sv-SE", "*", spBuiltinFormats_sv_SE }, // Swedish, Sweden
+ { "sw-TZ", "*", spBuiltinFormats_sw_TZ }, // Swahili, Tanzania
+ { "syr-SY", "*", spBuiltinFormats_ar_SY }, // Syriac, Syria
+ { "syr-TR", "*", spBuiltinFormats_tr_TR }, // Syriac, Turkey
+ { "ta-IN", "*", spBuiltinFormats_ta_IN }, // Tamil, India
+ { "te-IN", "*", spBuiltinFormats_te_IN }, // Telugu, India
+ { "th-TH", "*", spBuiltinFormats_th_TH }, // Thai, Thailand
+ { "tn-ZA", "*", spBuiltinFormats_en_ZA }, // Tswana, South Africa
+ { "tr-TR", "*", spBuiltinFormats_tr_TR }, // Turkish, Turkey
+ { "tt-RU", "*", spBuiltinFormats_tt_RU }, // Tatar, Russian Federation
+ { "uk-UA", "*", spBuiltinFormats_uk_UA }, // Ukrainian, Ukraine
+ { "ur-PK", "*", spBuiltinFormats_ur_PK }, // Urdu, Pakistan
+ { "vi-VN", "*", spBuiltinFormats_vi_VN }, // Vietnamese, Viet Nam
+ { "xh-ZA", "*", spBuiltinFormats_en_ZA }, // Xhosa, South Africa
+ { "zu-ZA", "*", spBuiltinFormats_en_ZA }, // Zulu, South Africa
+
+ { "*CJK", "*", spBuiltinFormats_CJK }, // CJK base table
+ { "ja-JP", "*CJK", spBuiltinFormats_ja_JP }, // Japanese, Japan
+ { "ko-KR", "*CJK", spBuiltinFormats_ko_KR }, // Korean, South Korea
+ { "zh-CN", "*CJK", spBuiltinFormats_zh_CN }, // Chinese, China
+ { "zh-HK", "*CJK", spBuiltinFormats_zh_HK }, // Chinese, Hong Kong
+ { "zh-MO", "*CJK", spBuiltinFormats_zh_MO }, // Chinese, Macau
+ { "zh-SG", "*CJK", spBuiltinFormats_zh_SG }, // Chinese, Singapore
+ { "zh-TW", "*CJK", spBuiltinFormats_zh_TW } // Chinese, Taiwan
+};
+
+} // namespace
+
+NumFmtModel::NumFmtModel() :
+ mnPredefId( -1 )
+{
+}
+
+ApiNumFmtData::ApiNumFmtData() :
+ mnIndex( 0 )
+{
+}
+
+namespace {
+
+sal_Int32 lclCreatePredefinedFormat( const Reference< XNumberFormats >& rxNumFmts,
+ sal_Int16 nPredefId, const Locale& rToLocale )
+{
+ sal_Int32 nIndex = 0;
+ try
+ {
+ Reference< XNumberFormatTypes > xNumFmtTypes( rxNumFmts, UNO_QUERY_THROW );
+ nIndex = (nPredefId >= 0) ?
+ xNumFmtTypes->getFormatIndex( nPredefId, rToLocale ) :
+ xNumFmtTypes->getStandardIndex( rToLocale );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( OStringBuffer( "lclCreatePredefinedFormat - cannot create predefined number format " ).
+ append( OString::number( nPredefId ) ).getStr() );
+ }
+ return nIndex;
+}
+
+sal_Int32 lclCreateFormat( const Reference< XNumberFormats >& rxNumFmts,
+ const OUString& rFmtCode, const Locale& rToLocale, const Locale& rFromLocale )
+{
+ sal_Int32 nIndex = 0;
+ try
+ {
+ nIndex = rxNumFmts->addNewConverted( rFmtCode, rFromLocale, rToLocale );
+ }
+ catch( Exception& )
+ {
+ // BIFF2-BIFF4 stores standard format explicitly in stream
+ if( rFmtCode.equalsIgnoreAsciiCase( "general" ) )
+ {
+ nIndex = lclCreatePredefinedFormat( rxNumFmts, 0, rToLocale );
+ }
+ else
+ {
+ // do not assert fractional number formats with fixed denominator
+ OSL_ENSURE( rFmtCode.startsWith( "#\\ ?/" ) ||
+ rFmtCode.startsWith( "#\\ ?\?/" ) ||
+ rFmtCode.startsWith( "#\\ ?\?\?/" ),
+ OStringBuffer( "lclCreateFormat - cannot create number format '"
+ + OUStringToOString( rFmtCode, osl_getThreadTextEncoding() )
+ + "\'" ).getStr() );
+ }
+ }
+ return nIndex;
+}
+
+/** Functor for converting an XML number format to an API number format index. */
+class NumberFormatFinalizer
+{
+public:
+ explicit NumberFormatFinalizer( const WorkbookHelper& rHelper );
+
+ void operator()( NumberFormat& rNumFmt ) const
+ { rNumFmt.finalizeImport( mxNumFmts, maEnUsLocale ); }
+
+private:
+ Reference< XNumberFormats > mxNumFmts;
+ Locale maEnUsLocale;
+};
+
+NumberFormatFinalizer::NumberFormatFinalizer( const WorkbookHelper& rHelper ) :
+ maEnUsLocale( "en", "US", OUString() )
+{
+ try
+ {
+ Reference< XNumberFormatsSupplier > xNumFmtsSupp( static_cast<cppu::OWeakObject*>(rHelper.getDocument().get()), UNO_QUERY_THROW );
+ mxNumFmts = xNumFmtsSupp->getNumberFormats();
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( mxNumFmts.is(), "NumberFormatFinalizer::NumberFormatFinalizer - cannot get number formats" );
+}
+
+sal_Int32 lclPosToken ( std::u16string_view sFormat, std::u16string_view sSearch, sal_Int32 nStartPos )
+{
+ sal_Int32 nLength = sFormat.size();
+ for ( sal_Int32 i = nStartPos; i < nLength && i >= 0 ; i++ )
+ {
+ size_t nFind = i;
+ switch(sFormat[i])
+ {
+ case '\"' : // skip text
+ nFind = sFormat.find('\"',i+1);
+ break;
+ case '[' : // skip condition
+ nFind = sFormat.find(']',i+1);
+ break;
+ default :
+ if ( o3tl::starts_with(sFormat.substr(i), sSearch) )
+ return i;
+ break;
+ }
+ if ( nFind == std::u16string_view::npos )
+ return -2;
+ i = nFind;
+ }
+ return -2;
+}
+
+} // namespace
+
+NumberFormat::NumberFormat( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void NumberFormat::setFormatCode( std::u16string_view aFmtCode )
+{
+ // Special case for fraction code '\ ?/?', it is passed to us in xml, the '\' is not
+ // an escape character but merely should be telling the formatter to display the next
+ // char in the format ( afaics it does that anyhow )
+ sal_Int32 nPosEscape = 0;
+ sal_Int32 nErase = 0;
+ sal_Int32 nLastIndex = sal_Int32(aFmtCode.size()) - 1;
+ OUStringBuffer sFormat(aFmtCode);
+
+ while ( ( nPosEscape = lclPosToken( aFmtCode, u"\\ ", nPosEscape ) ) > 0 )
+ {
+ sal_Int32 nPos = nPosEscape + 2;
+ while ( nPos < nLastIndex && ( aFmtCode[nPos] == '?' || aFmtCode[nPos] == '#' || aFmtCode[nPos] == '0' ) )
+ nPos++;
+ if ( nPos < nLastIndex && aFmtCode[nPos] == '/' )
+ {
+ sFormat.remove(nPosEscape - nErase, 1);
+ nErase ++;
+ } // tdf#81939 preserve other escape characters
+ nPosEscape = lclPosToken( aFmtCode, u";", nPosEscape ); // skip to next format
+ }
+ maModel.maFmtCode = sFormat.makeStringAndClear();
+}
+
+void NumberFormat::setFormatCode( const Locale& rLocale, const char* pcFmtCode )
+{
+ maModel.maLocale = rLocale;
+ maModel.maFmtCode = OStringToOUString( std::string_view( pcFmtCode ), RTL_TEXTENCODING_UTF8 );
+ maModel.mnPredefId = -1;
+}
+
+void NumberFormat::setPredefinedId( const Locale& rLocale, sal_Int16 nPredefId )
+{
+ maModel.maLocale = rLocale;
+ maModel.maFmtCode.clear();
+ maModel.mnPredefId = nPredefId;
+}
+
+void NumberFormat::finalizeImport( const Reference< XNumberFormats >& rxNumFmts, const Locale& rFromLocale )
+{
+ if( rxNumFmts.is() && !maModel.maFmtCode.isEmpty() )
+ maApiData.mnIndex = lclCreateFormat( rxNumFmts, maModel.maFmtCode, maModel.maLocale, rFromLocale );
+ else
+ maApiData.mnIndex = lclCreatePredefinedFormat( rxNumFmts, maModel.mnPredefId, maModel.maLocale );
+}
+
+sal_uInt32 NumberFormat::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ const ScDocument& rDoc = getScDocument();
+ static sal_uInt32 nDflt = rDoc.GetFormatTable()->GetStandardIndex( ScGlobal::eLnge );
+ sal_uInt32 nScNumFmt = nDflt;
+ if ( maApiData.mnIndex )
+ nScNumFmt = maApiData.mnIndex;
+
+ ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs );
+ if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, false ) == SfxItemState::SET )
+ ScGlobal::AddLanguage( rItemSet, *(rDoc.GetFormatTable()) );
+ else
+ nScNumFmt = 0;
+
+ return nScNumFmt;
+}
+
+NumberFormatsBuffer::NumberFormatsBuffer( const WorkbookHelper& rHelper )
+ : WorkbookHelper(rHelper)
+ , mnHighestId(0)
+{
+ // get the current locale
+ // try user-defined locale setting
+ maLocaleStr = officecfg::Setup::L10N::ooSetupSystemLocale::get();
+ // if set to "use system", get locale from system
+ if( maLocaleStr.isEmpty() )
+ maLocaleStr = officecfg::System::L10N::Locale::get();
+
+ // create built-in formats for current locale
+ insertBuiltinFormats();
+}
+
+NumberFormatRef NumberFormatsBuffer::createNumFmt( sal_uInt32 nNumFmtId, std::u16string_view aFmtCode )
+{
+ NumberFormatRef xNumFmt;
+ xNumFmt = std::make_shared<NumberFormat>( *this );
+ maNumFmts[ nNumFmtId ] = xNumFmt;
+ if ( nNumFmtId > mnHighestId )
+ mnHighestId = nNumFmtId;
+ xNumFmt->setFormatCode( aFmtCode );
+ return xNumFmt;
+}
+
+NumberFormatRef NumberFormatsBuffer::importNumFmt( const AttributeList& rAttribs )
+{
+ sal_Int32 nNumFmtId = rAttribs.getInteger( XML_numFmtId, -1 );
+ OUString aFmtCode = rAttribs.getXString( XML_formatCode, OUString() );
+ return createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void NumberFormatsBuffer::importNumFmt( SequenceInputStream& rStrm )
+{
+ sal_Int32 nNumFmtId = rStrm.readuInt16();
+ OUString aFmtCode = BiffHelper::readString( rStrm );
+ createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void NumberFormatsBuffer::finalizeImport()
+{
+ maNumFmts.forEach( NumberFormatFinalizer( *this ) );
+}
+
+sal_uInt32 NumberFormatsBuffer::fillToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const
+{
+ const NumberFormat* pNumFmt = maNumFmts.get(nNumFmtId).get();
+ if (!pNumFmt)
+ return 0;
+
+ return pNumFmt->fillToItemSet( rItemSet, bSkipPoolDefs);
+}
+
+void NumberFormatsBuffer::insertBuiltinFormats()
+{
+ // build a map containing pointers to all tables
+ typedef ::std::map< OUString, const BuiltinFormatTable* > BuiltinMap;
+ BuiltinMap aBuiltinMap;
+ for(auto const &rTable : spBuiltinFormatTables)
+ aBuiltinMap[ OUString::createFromAscii(rTable.mpcLocale) ] = &rTable;
+
+ // convert locale string to locale struct
+ Locale aSysLocale( LanguageTag::convertToLocale( maLocaleStr));
+
+ // build a list of table pointers for the current locale, with all parent tables
+ typedef ::std::vector< const BuiltinFormatTable* > BuiltinVec;
+ BuiltinVec aBuiltinVec;
+ BuiltinMap::const_iterator aMIt = aBuiltinMap.find( maLocaleStr ), aMEnd = aBuiltinMap.end();
+ OSL_ENSURE( aMIt != aMEnd,
+ OStringBuffer( "NumberFormatsBuffer::insertBuiltinFormats - locale '" +
+ OUStringToOString( maLocaleStr, RTL_TEXTENCODING_ASCII_US ) +
+ "' not supported (#i29949#)" ).getStr() );
+ // start with default table, if no table has been found
+ if( aMIt == aMEnd )
+ aMIt = aBuiltinMap.find( "*" );
+ OSL_ENSURE( aMIt != aMEnd, "NumberFormatsBuffer::insertBuiltinFormats - default map not found" );
+ // insert all tables into the vector
+ for( ; aMIt != aMEnd; aMIt = aBuiltinMap.find( OUString::createFromAscii( aMIt->second->mpcParent ) ) )
+ aBuiltinVec.push_back( aMIt->second );
+
+ // insert the default formats in the format map (in reverse order from default table to system locale)
+ std::map< sal_uInt32, sal_uInt32 > aReuseMap;
+ for( BuiltinVec::reverse_iterator aVIt = aBuiltinVec.rbegin(), aVEnd = aBuiltinVec.rend(); aVIt != aVEnd; ++aVIt )
+ {
+ // do not put the current system locale for default table
+ Locale aLocale;
+ if( (*aVIt)->mpcParent[ 0 ] != '\0' && OUString::createFromAscii((*aVIt)->mpcLocale) != maLocaleStr )
+ aLocale = aSysLocale;
+ for( const BuiltinFormat* pBuiltin = (*aVIt)->mpFormats; pBuiltin && (pBuiltin->mnNumFmtId >= 0); ++pBuiltin )
+ {
+ NumberFormatRef& rxNumFmt = maNumFmts[ pBuiltin->mnNumFmtId ];
+ rxNumFmt = std::make_shared<NumberFormat>( *this );
+
+ bool bReuse = false;
+ if( pBuiltin->mpcFmtCode )
+ rxNumFmt->setFormatCode( aLocale, pBuiltin->mpcFmtCode );
+ else if( pBuiltin->mnPredefId >= 0 )
+ rxNumFmt->setPredefinedId( aLocale, pBuiltin->mnPredefId );
+ else
+ bReuse = pBuiltin->mnReuseId >= 0;
+
+ if( bReuse )
+ aReuseMap[ pBuiltin->mnNumFmtId ] = pBuiltin->mnReuseId;
+ else
+ aReuseMap.erase( pBuiltin->mnNumFmtId );
+ }
+ }
+
+ // copy reused number formats
+ for( const auto& [rNumFmtId, rReuseId] : aReuseMap )
+ {
+ maNumFmts[ rNumFmtId ] = maNumFmts[ rReuseId ];
+ if ( rNumFmtId > mnHighestId )
+ mnHighestId = rNumFmtId;
+ }
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/ooxformulaparser.cxx b/sc/source/filter/oox/ooxformulaparser.cxx
new file mode 100644
index 0000000000..0a7c0d5243
--- /dev/null
+++ b/sc/source/filter/oox/ooxformulaparser.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 <ooxformulaparser.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <osl/diagnose.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <formulaparser.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+class OOXMLFormulaParserImpl : private FormulaFinalizer
+{
+public:
+ explicit OOXMLFormulaParserImpl( const Reference< XMultiServiceFactory >& rxModelFactory );
+
+ Sequence< FormulaToken > parseFormula( const OUString& rFormula, const ScAddress& rReferencePos );
+
+protected:
+ virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const override;
+
+private:
+ ApiParserWrapper maApiParser;
+};
+
+OOXMLFormulaParserImpl::OOXMLFormulaParserImpl( const Reference< XMultiServiceFactory >& rxModelFactory ) :
+ FormulaFinalizer( OpCodeProvider( rxModelFactory, true ) ),
+ maApiParser( rxModelFactory, *this )
+{
+}
+
+Sequence< FormulaToken > OOXMLFormulaParserImpl::parseFormula( const OUString& rFormula, const ScAddress& rReferencePos )
+{
+ return finalizeTokenArray( maApiParser.parseFormula( rFormula, rReferencePos ) );
+}
+
+const FunctionInfo* OOXMLFormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const
+{
+ /* Try to parse calls to library functions. The format of such a function
+ call is assumed to be
+ "'<path-to-office-install>\Library\<libname>'!<funcname>". */
+
+ // the string has to start with an apostroph (followed by the library URL)
+ if( (rTokenData.getLength() >= 6) && (rTokenData[ 0 ] == '\'') )
+ {
+ // library URL and function name are separated by an exclamation mark
+ sal_Int32 nExclamPos = rTokenData.lastIndexOf( '!' );
+ if( (1 < nExclamPos) && (nExclamPos + 1 < rTokenData.getLength()) && (rTokenData[ nExclamPos - 1 ] == '\'') )
+ {
+ // find the last backslash that separates library path and name
+ sal_Int32 nFileSep = rTokenData.lastIndexOf( '\\', nExclamPos - 2 );
+ if( nFileSep > 1 )
+ {
+ // find preceding backslash that separates the last directory name
+ sal_Int32 nDirSep = rTokenData.lastIndexOf( '\\', nFileSep - 1 );
+ // function library is located in a directory called 'library'
+ if( (nDirSep > 0) && rTokenData.matchIgnoreAsciiCase( "\\LIBRARY\\", nDirSep ) )
+ {
+ // try to find a function info for the function name
+ OUString aFuncName = rTokenData.copy( nExclamPos + 1 ).toAsciiUpperCase();
+ const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aFuncName );
+ if( pFuncInfo && (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) )
+ {
+ // check that the name of the library matches
+ OUString aLibName = rTokenData.copy( nFileSep + 1, nExclamPos - nFileSep - 2 );
+ if( pFuncInfo->meFuncLibType == getFuncLibTypeFromLibraryName( aLibName ) )
+ return pFuncInfo;
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+OOXMLFormulaParser::OOXMLFormulaParser()
+{
+}
+
+OOXMLFormulaParser::~OOXMLFormulaParser()
+{
+}
+
+// com.sun.star.lang.XServiceInfo interface -----------------------------------
+OUString SAL_CALL OOXMLFormulaParser::getImplementationName()
+{
+ return "com.sun.star.comp.oox.xls.FormulaParser";
+}
+
+sal_Bool SAL_CALL OOXMLFormulaParser::supportsService( const OUString& rService )
+{
+ return cppu::supportsService(this, rService);
+}
+
+Sequence< OUString > SAL_CALL OOXMLFormulaParser::getSupportedServiceNames()
+{
+ return { "com.sun.star.sheet.FilterFormulaParser" };
+}
+
+// com.sun.star.lang.XInitialization interface --------------------------------
+
+void SAL_CALL OOXMLFormulaParser::initialize( const Sequence< Any >& rArgs )
+{
+ OSL_ENSURE( rArgs.hasElements(), "OOXMLFormulaParser::initialize - missing arguments" );
+ if( !rArgs.hasElements() )
+ throw RuntimeException();
+ mxComponent.set( rArgs[ 0 ], UNO_QUERY_THROW );
+}
+
+// com.sun.star.sheet.XFilterFormulaParser interface --------------------------
+
+OUString SAL_CALL OOXMLFormulaParser::getSupportedNamespace()
+{
+ return "http://schemas.microsoft.com/office/excel/formula";
+}
+
+// com.sun.star.sheet.XFormulaParser interface --------------------------------
+
+Sequence< FormulaToken > SAL_CALL OOXMLFormulaParser::parseFormula(
+ const OUString& rFormula, const CellAddress& rReferencePos )
+{
+ if( !mxParserImpl )
+ {
+ Reference< XMultiServiceFactory > xModelFactory( mxComponent, UNO_QUERY_THROW );
+ mxParserImpl = std::make_shared<OOXMLFormulaParserImpl>( xModelFactory );
+ }
+ return mxParserImpl->parseFormula( rFormula,
+ ScAddress(rReferencePos.Column, rReferencePos.Row, rReferencePos.Sheet) );
+}
+
+OUString SAL_CALL OOXMLFormulaParser::printFormula(
+ const Sequence< FormulaToken >& /*rTokens*/, const CellAddress& /*rReferencePos*/ )
+{
+ // not implemented
+ throw RuntimeException();
+}
+
+} // namespace oox::xls
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_oox_xls_FormulaParser_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new oox::xls::OOXMLFormulaParser());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pagesettings.cxx b/sc/source/filter/oox/pagesettings.cxx
new file mode 100644
index 0000000000..825389d37a
--- /dev/null
+++ b/sc/source/filter/oox/pagesettings.cxx
@@ -0,0 +1,1065 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pagesettings.hxx>
+
+#include <algorithm>
+#include <set>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <osl/diagnose.h>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sax/tools/converter.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/graphichelper.hxx>
+#include <oox/helper/propertymap.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/core/relations.hxx>
+#include <stylesbuffer.hxx>
+#include <document.hxx>
+#include <biffhelper.hxx>
+#include <filter/msfilter/util.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::Relations;
+
+namespace {
+
+const double OOX_MARGIN_DEFAULT_LR = 0.748; /// Left/right default margin in inches.
+const double OOX_MARGIN_DEFAULT_TB = 0.984; /// Top/bottom default margin in inches.
+const double OOX_MARGIN_DEFAULT_HF = 0.512; /// Header/footer default margin in inches.
+
+const sal_uInt16 BIFF12_PRINTOPT_HORCENTER = 0x0001;
+const sal_uInt16 BIFF12_PRINTOPT_VERCENTER = 0x0002;
+const sal_uInt16 BIFF12_PRINTOPT_PRINTHEADING = 0x0004;
+const sal_uInt16 BIFF12_PRINTOPT_PRINTGRID = 0x0008;
+
+const sal_uInt16 BIFF12_HEADERFOOTER_DIFFEVEN = 0x0001;
+const sal_uInt16 BIFF12_HEADERFOOTER_DIFFFIRST = 0x0002;
+
+const sal_uInt16 BIFF12_PAGESETUP_INROWS = 0x0001;
+const sal_uInt16 BIFF12_PAGESETUP_LANDSCAPE = 0x0002;
+const sal_uInt16 BIFF12_PAGESETUP_INVALID = 0x0004;
+const sal_uInt16 BIFF12_PAGESETUP_BLACKWHITE = 0x0008;
+const sal_uInt16 BIFF12_PAGESETUP_DRAFTQUALITY = 0x0010;
+const sal_uInt16 BIFF12_PAGESETUP_PRINTNOTES = 0x0020;
+const sal_uInt16 BIFF12_PAGESETUP_DEFAULTORIENT = 0x0040;
+const sal_uInt16 BIFF12_PAGESETUP_USEFIRSTPAGE = 0x0080;
+const sal_uInt16 BIFF12_PAGESETUP_NOTES_END = 0x0100; // different to BIFF flag
+
+const sal_uInt16 BIFF12_CHARTPAGESETUP_LANDSCAPE = 0x0001;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_INVALID = 0x0002;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_BLACKWHITE = 0x0004;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_DEFAULTORIENT= 0x0008;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_USEFIRSTPAGE = 0x0010;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_DRAFTQUALITY = 0x0020;
+
+} // namespace
+
+PageSettingsModel::PageSettingsModel() :
+ mfLeftMargin( OOX_MARGIN_DEFAULT_LR ),
+ mfRightMargin( OOX_MARGIN_DEFAULT_LR ),
+ mfTopMargin( OOX_MARGIN_DEFAULT_TB ),
+ mfBottomMargin( OOX_MARGIN_DEFAULT_TB ),
+ mfHeaderMargin( OOX_MARGIN_DEFAULT_HF ),
+ mfFooterMargin( OOX_MARGIN_DEFAULT_HF ),
+ mnPaperSize( 1 ),
+ mnPaperWidth( 0 ),
+ mnPaperHeight( 0 ),
+ mnCopies( 1 ),
+ mnScale( 100 ),
+ mnFirstPage( 1 ),
+ mnFitToWidth( 1 ),
+ mnFitToHeight( 1 ),
+ mnHorPrintRes( 600 ),
+ mnVerPrintRes( 600 ),
+ mnOrientation( XML_default ),
+ mnPageOrder( XML_downThenOver ),
+ mnCellComments( XML_none ),
+ mnPrintErrors( XML_displayed ),
+ mbUseEvenHF( false ),
+ mbUseFirstHF( false ),
+ mbValidSettings( true ),
+ mbUseFirstPage( false ),
+ mbBlackWhite( false ),
+ mbDraftQuality( false ),
+ mbFitToPages( false ),
+ mbHorCenter( false ),
+ mbVerCenter( false ),
+ mbPrintGrid( false ),
+ mbPrintHeadings( false )
+{
+}
+
+void PageSettingsModel::setBiffPrintErrors( sal_uInt8 nPrintErrors )
+{
+ static const sal_Int32 spnErrorIds[] = { XML_displayed, XML_none, XML_dash, XML_NA };
+ mnPrintErrors = STATIC_ARRAY_SELECT( spnErrorIds, nPrintErrors, XML_none );
+}
+
+PageSettings::PageSettings( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void PageSettings::importPrintOptions( const AttributeList& rAttribs )
+{
+ maModel.mbHorCenter = rAttribs.getBool( XML_horizontalCentered, false );
+ maModel.mbVerCenter = rAttribs.getBool( XML_verticalCentered, false );
+ maModel.mbPrintGrid = rAttribs.getBool( XML_gridLines, false );
+ maModel.mbPrintHeadings = rAttribs.getBool( XML_headings, false );
+}
+
+void PageSettings::importPageMargins( const AttributeList& rAttribs )
+{
+ maModel.mfLeftMargin = rAttribs.getDouble( XML_left, OOX_MARGIN_DEFAULT_LR );
+ maModel.mfRightMargin = rAttribs.getDouble( XML_right, OOX_MARGIN_DEFAULT_LR );
+ maModel.mfTopMargin = rAttribs.getDouble( XML_top, OOX_MARGIN_DEFAULT_TB );
+ maModel.mfBottomMargin = rAttribs.getDouble( XML_bottom, OOX_MARGIN_DEFAULT_TB );
+ maModel.mfHeaderMargin = rAttribs.getDouble( XML_header, OOX_MARGIN_DEFAULT_HF );
+ maModel.mfFooterMargin = rAttribs.getDouble( XML_footer, OOX_MARGIN_DEFAULT_HF );
+}
+
+void PageSettings::importPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ OUString aStr;
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ maModel.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 );
+ aStr = rAttribs.getString ( XML_paperWidth, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperWidth, aStr);
+ aStr = rAttribs.getString ( XML_paperHeight, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperHeight, aStr );
+ maModel.mnCopies = rAttribs.getInteger( XML_copies, 1 );
+ maModel.mnScale = rAttribs.getInteger( XML_scale, 100 );
+ maModel.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 );
+ maModel.mnFitToWidth = rAttribs.getInteger( XML_fitToWidth, 1 );
+ maModel.mnFitToHeight = rAttribs.getInteger( XML_fitToHeight, 1 );
+ maModel.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 );
+ maModel.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 );
+ maModel.mnOrientation = rAttribs.getToken( XML_orientation, XML_default );
+ maModel.mnPageOrder = rAttribs.getToken( XML_pageOrder, XML_downThenOver );
+ maModel.mnCellComments = rAttribs.getToken( XML_cellComments, XML_none );
+ maModel.mnPrintErrors = rAttribs.getToken( XML_errors, XML_displayed );
+ maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
+ maModel.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false );
+ maModel.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false );
+ maModel.mbDraftQuality = rAttribs.getBool( XML_draft, false );
+}
+
+void PageSettings::importChartPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ OUString aStr;
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ maModel.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 );
+ aStr = rAttribs.getString ( XML_paperWidth, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperWidth, aStr );
+ aStr = rAttribs.getString ( XML_paperHeight, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperHeight, aStr );
+ maModel.mnCopies = rAttribs.getInteger( XML_copies, 1 );
+ maModel.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 );
+ maModel.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 );
+ maModel.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 );
+ maModel.mnOrientation = rAttribs.getToken( XML_orientation, XML_default );
+ maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
+ maModel.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false );
+ maModel.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false );
+ maModel.mbDraftQuality = rAttribs.getBool( XML_draft, false );
+}
+
+void PageSettings::importHeaderFooter( const AttributeList& rAttribs )
+{
+ maModel.mbUseEvenHF = rAttribs.getBool( XML_differentOddEven, false );
+ maModel.mbUseFirstHF = rAttribs.getBool( XML_differentFirst, false );
+}
+
+void PageSettings::importHeaderFooterCharacters( std::u16string_view rChars, sal_Int32 nElement )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( oddHeader ): maModel.maOddHeader += rChars; break;
+ case XLS_TOKEN( oddFooter ): maModel.maOddFooter += rChars; break;
+ case XLS_TOKEN( evenHeader ): maModel.maEvenHeader += rChars; break;
+ case XLS_TOKEN( evenFooter ): maModel.maEvenFooter += rChars; break;
+ case XLS_TOKEN( firstHeader ): maModel.maFirstHeader += rChars; break;
+ case XLS_TOKEN( firstFooter ): maModel.maFirstFooter += rChars; break;
+ }
+}
+
+void PageSettings::importPicture( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ importPictureData( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
+}
+
+void PageSettings::importPageMargins( SequenceInputStream& rStrm )
+{
+ maModel.mfLeftMargin = rStrm.readDouble();
+ maModel.mfRightMargin = rStrm.readDouble();
+ maModel.mfTopMargin = rStrm.readDouble();
+ maModel.mfBottomMargin = rStrm.readDouble();
+ maModel.mfHeaderMargin = rStrm.readDouble();
+ maModel.mfFooterMargin = rStrm.readDouble();
+}
+
+void PageSettings::importPrintOptions( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ maModel.mbHorCenter = getFlag( nFlags, BIFF12_PRINTOPT_HORCENTER );
+ maModel.mbVerCenter = getFlag( nFlags, BIFF12_PRINTOPT_VERCENTER );
+ maModel.mbPrintGrid = getFlag( nFlags, BIFF12_PRINTOPT_PRINTGRID );
+ maModel.mbPrintHeadings = getFlag( nFlags, BIFF12_PRINTOPT_PRINTHEADING );
+}
+
+void PageSettings::importPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ OUString aRelId;
+ sal_uInt16 nFlags;
+ maModel.mnPaperSize = rStrm.readInt32();
+ maModel.mnScale = rStrm.readInt32();
+ maModel.mnHorPrintRes = rStrm.readInt32();
+ maModel.mnVerPrintRes = rStrm.readInt32();
+ maModel.mnCopies = rStrm.readInt32();
+ maModel.mnFirstPage = rStrm.readInt32();
+ maModel.mnFitToWidth = rStrm.readInt32();
+ maModel.mnFitToHeight = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ rStrm >> aRelId;
+ maModel.setBiffPrintErrors( extractValue< sal_uInt8 >( nFlags, 9, 2 ) );
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( aRelId );
+ maModel.mnOrientation = getFlagValue( nFlags, BIFF12_PAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_PAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
+ maModel.mnPageOrder = getFlagValue( nFlags, BIFF12_PAGESETUP_INROWS, XML_overThenDown, XML_downThenOver );
+ maModel.mnCellComments = getFlagValue( nFlags, BIFF12_PAGESETUP_PRINTNOTES, getFlagValue( nFlags, BIFF12_PAGESETUP_NOTES_END, XML_atEnd, XML_asDisplayed ), XML_none );
+ maModel.mbValidSettings = !getFlag( nFlags, BIFF12_PAGESETUP_INVALID );
+ maModel.mbUseFirstPage = getFlag( nFlags, BIFF12_PAGESETUP_USEFIRSTPAGE );
+ maModel.mbBlackWhite = getFlag( nFlags, BIFF12_PAGESETUP_BLACKWHITE );
+ maModel.mbDraftQuality = getFlag( nFlags, BIFF12_PAGESETUP_DRAFTQUALITY );
+}
+
+void PageSettings::importChartPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ OUString aRelId;
+ sal_uInt16 nFirstPage, nFlags;
+ maModel.mnPaperSize = rStrm.readInt32();
+ maModel.mnHorPrintRes = rStrm.readInt32();
+ maModel.mnVerPrintRes = rStrm.readInt32();
+ maModel.mnCopies = rStrm.readInt32();
+ nFirstPage = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ rStrm >> aRelId;
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( aRelId );
+ maModel.mnFirstPage = nFirstPage; // 16-bit in CHARTPAGESETUP
+ maModel.mnOrientation = getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
+ maModel.mbValidSettings = !getFlag( nFlags, BIFF12_CHARTPAGESETUP_INVALID );
+ maModel.mbUseFirstPage = getFlag( nFlags, BIFF12_CHARTPAGESETUP_USEFIRSTPAGE );
+ maModel.mbBlackWhite = getFlag( nFlags, BIFF12_CHARTPAGESETUP_BLACKWHITE );
+ maModel.mbDraftQuality = getFlag( nFlags, BIFF12_CHARTPAGESETUP_DRAFTQUALITY );
+}
+
+void PageSettings::importHeaderFooter( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ rStrm >> maModel.maOddHeader >> maModel.maOddFooter
+ >> maModel.maEvenHeader >> maModel.maEvenFooter
+ >> maModel.maFirstHeader >> maModel.maFirstFooter;
+ maModel.mbUseEvenHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFEVEN );
+ maModel.mbUseFirstHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFFIRST );
+}
+
+void PageSettings::importPicture( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ importPictureData( rRelations, BiffHelper::readString( rStrm ) );
+}
+
+void PageSettings::setFitToPagesMode( bool bFitToPages )
+{
+ maModel.mbFitToPages = bFitToPages;
+}
+
+void PageSettings::finalizeImport()
+{
+ OUStringBuffer aStyleNameBuffer( "PageStyle_" );
+ Reference<container::XNamed> xSheetName(getSheet(), UNO_QUERY);
+ if( xSheetName.is() )
+ aStyleNameBuffer.append( xSheetName->getName() );
+ else
+ aStyleNameBuffer.append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) );
+ OUString aStyleName = aStyleNameBuffer.makeStringAndClear();
+
+ Reference<style::XStyle> xStyle = createStyleObject(aStyleName, true);
+ PropertySet aStyleProps( xStyle );
+ getPageSettingsConverter().writePageSettingsProperties( aStyleProps, maModel, getSheetType() );
+
+ // Set page style name to the sheet.
+ SCTAB nTab = getSheetIndex();
+ getScDocument().SetPageStyle(nTab, aStyleName);
+}
+
+void PageSettings::importPictureData( const Relations& rRelations, const OUString& rRelId )
+{
+ OUString aPicturePath = rRelations.getFragmentPathFromRelId(rRelId);
+ if (!aPicturePath.isEmpty())
+ {
+ maModel.mxGraphic = getBaseFilter().getGraphicHelper().importEmbeddedGraphic(aPicturePath);
+ }
+}
+
+namespace {
+
+enum HFPortionId
+{
+ HF_LEFT,
+ HF_CENTER,
+ HF_RIGHT,
+ HF_COUNT
+};
+
+struct HFPortionInfo
+{
+ Reference<text::XText> mxText; /// XText interface of this portion.
+ Reference<text::XTextCursor> mxStart; /// Start position of current text range for formatting.
+ Reference<text::XTextCursor> mxEnd; /// End position of current text range for formatting.
+ double mfTotalHeight; /// Sum of heights of previous lines in points.
+ double mfCurrHeight; /// Height of the current text line in points.
+
+ bool initialize( const Reference<text::XText>& rxText );
+};
+
+}
+
+bool HFPortionInfo::initialize( const Reference<text::XText>& rxText )
+{
+ mfTotalHeight = mfCurrHeight = 0.0;
+ mxText = rxText;
+ if( mxText.is() )
+ {
+ mxStart = mxText->createTextCursor();
+ mxEnd = mxText->createTextCursor();
+ }
+ bool bRet = mxText.is() && mxStart.is() && mxEnd.is();
+ OSL_ENSURE( bRet, "HFPortionInfo::initialize - missing interfaces" );
+ return bRet;
+}
+
+class HeaderFooterParser : public WorkbookHelper
+{
+public:
+ explicit HeaderFooterParser( const WorkbookHelper& rHelper );
+
+ /** Parses the passed string and creates the header/footer contents.
+ @returns The total height of the converted header or footer in points. */
+ double parse(
+ const Reference<sheet::XHeaderFooterContent>& rxContext,
+ const OUString& rData );
+
+private:
+ /** Returns the current edit engine text object. */
+ HFPortionInfo& getPortion() { return maPortions[ meCurrPortion ]; }
+ /** Returns the start cursor of the current text range. */
+ const Reference<text::XTextCursor>& getStartPos() { return getPortion().mxStart; }
+ /** Returns the end cursor of the current text range. */
+ const Reference<text::XTextCursor>& getEndPos() { return getPortion().mxEnd; }
+
+ /** Returns the current line height of the specified portion. */
+ double getCurrHeight( HFPortionId ePortion ) const;
+
+ /** Updates the current line height of the specified portion, using the current font size. */
+ void updateCurrHeight( HFPortionId ePortion );
+ /** Updates the current line height, using the current font size. */
+ void updateCurrHeight();
+
+ /** Sets the font attributes at the current selection. */
+ void setAttributes();
+ /** Appends and clears internal string buffer. */
+ void appendText();
+ /** Appends a line break and adjusts internal text height data. */
+ void appendLineBreak();
+
+ /** Creates a text field from the passed service name. */
+ Reference<text::XTextContent> createField( const OUString& rServiceName ) const;
+ /** Appends the passed text field. */
+ void appendField( const Reference<text::XTextContent>& rxContent );
+
+ /** Sets the passed font name if it is valid. */
+ void convertFontName( const OUString& rStyle );
+ /** Converts a font style given as string. */
+ void convertFontStyle( std::u16string_view aStyle );
+ /** Converts a font color given as string. */
+ void convertFontColor( std::u16string_view aColor );
+
+ /** Finalizes current portion: sets font attributes and updates text height data. */
+ void finalizePortion();
+ /** Changes current header/footer portion. */
+ void setNewPortion( HFPortionId ePortion );
+
+private:
+ typedef ::std::vector< HFPortionInfo > HFPortionInfoVec;
+
+ const std::set< OString > maBoldNames; /// All names for bold font style in lowercase UTF-8.
+ const std::set< OString > maItalicNames; /// All names for italic font style in lowercase UTF-8.
+ HFPortionInfoVec maPortions;
+ HFPortionId meCurrPortion; /// Identifier of current H/F portion.
+ OUStringBuffer maBuffer; /// Text data to append to current text range.
+ FontModel maFontModel; /// Font attributes of current text range.
+};
+
+namespace {
+
+// different names for bold font style (lowercase)
+const char* const sppcBoldNames[] =
+{
+ "bold",
+ "fett", // German 'bold'
+ "demibold",
+ "halbfett", // German 'demibold'
+ "black",
+ "heavy",
+ "f\303\251lk\303\266v\303\251r" // Hungarian 'bold'
+};
+
+// different names for italic font style (lowercase)
+const char* const sppcItalicNames[] =
+{
+ "italic",
+ "kursiv", // German 'italic'
+ "oblique",
+ "schr\303\204g", // German 'oblique' with uppercase A umlaut
+ "schr\303\244g", // German 'oblique' with lowercase A umlaut
+ "d\305\221lt" // Hungarian 'italic'
+};
+
+} // namespace
+
+constexpr OUStringLiteral gaPageNumberService( u"com.sun.star.text.TextField.PageNumber" );
+constexpr OUStringLiteral gaPageCountService( u"com.sun.star.text.TextField.PageCount" );
+constexpr OUStringLiteral gaSheetNameService( u"com.sun.star.text.TextField.SheetName" );
+constexpr OUString gaFileNameService( u"com.sun.star.text.TextField.FileName"_ustr );
+constexpr OUString gaDateTimeService( u"com.sun.star.text.TextField.DateTime"_ustr );
+
+HeaderFooterParser::HeaderFooterParser( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maBoldNames( sppcBoldNames, sppcBoldNames + SAL_N_ELEMENTS(sppcBoldNames) ),
+ maItalicNames( sppcItalicNames, sppcItalicNames + SAL_N_ELEMENTS(sppcItalicNames) ),
+ maPortions( static_cast< size_t >( HF_COUNT ) ),
+ meCurrPortion( HF_CENTER )
+{
+}
+
+double HeaderFooterParser::parse( const Reference<sheet::XHeaderFooterContent>& rxContext, const OUString& rData )
+{
+ if( !rxContext.is() || rData.isEmpty() ||
+ !maPortions[ HF_LEFT ].initialize( rxContext->getLeftText() ) ||
+ !maPortions[ HF_CENTER ].initialize( rxContext->getCenterText() ) ||
+ !maPortions[ HF_RIGHT ].initialize( rxContext->getRightText() ) )
+ return 0.0;
+
+ meCurrPortion = HF_CENTER;
+ maBuffer.setLength( 0 );
+ maFontModel = getStyles().getDefaultFontModel();
+ OUStringBuffer aFontName; // current font name
+ OUStringBuffer aFontStyle; // current font style
+ sal_Int32 nFontHeight = 0; // current font height
+
+ /** State of the parser. */
+ enum
+ {
+ STATE_TEXT, /// Literal text data.
+ STATE_TOKEN, /// Control token following a '&' character.
+ STATE_FONTNAME, /// Font name ('&' is followed by '"', reads until next '"' or ',').
+ STATE_FONTSTYLE, /// Font style name (font part after ',', reads until next '"').
+ STATE_FONTHEIGHT /// Font height ('&' is followed by num. digits, reads until non-digit).
+ }
+ eState = STATE_TEXT;
+
+ const sal_Unicode* pcChar = rData.getStr();
+ const sal_Unicode* pcEnd = pcChar + rData.getLength();
+ for( ; (pcChar != pcEnd) && (*pcChar != 0); ++pcChar )
+ {
+ sal_Unicode cChar = *pcChar;
+ switch( eState )
+ {
+ case STATE_TEXT:
+ {
+ switch( cChar )
+ {
+ case '&': // new token
+ appendText();
+ eState = STATE_TOKEN;
+ break;
+ case '\n': // line break
+ appendText();
+ appendLineBreak();
+ break;
+ default:
+ maBuffer.append( cChar );
+ }
+ }
+ break;
+
+ case STATE_TOKEN:
+ {
+ // default: back to text mode, may be changed in specific cases
+ eState = STATE_TEXT;
+ // ignore case of token codes
+ if( ('a' <= cChar) && (cChar <= 'z') )
+ cChar = (cChar - 'a') + 'A';
+ switch( cChar )
+ {
+ case '&': maBuffer.append( cChar ); break; // the '&' character
+
+ case 'L': setNewPortion( HF_LEFT ); break; // left portion
+ case 'C': setNewPortion( HF_CENTER ); break; // center portion
+ case 'R': setNewPortion( HF_RIGHT ); break; // right portion
+
+ case 'P': // page number
+ appendField( createField( gaPageNumberService ) );
+ break;
+ case 'N': // total page count
+ appendField( createField( gaPageCountService ) );
+ break;
+ case 'A': // current sheet name
+ appendField( createField( gaSheetNameService ) );
+ break;
+
+ case 'F': // file name
+ {
+ Reference<text::XTextContent> xContent = createField( gaFileNameService );
+ PropertySet aPropSet( xContent );
+ aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::NAME_AND_EXT );
+ appendField( xContent );
+ }
+ break;
+ case 'Z': // file path (without file name), OOXML, BIFF12, and BIFF8 only
+ {
+ Reference<text::XTextContent> xContent = createField( gaFileNameService );
+ PropertySet aPropSet( xContent );
+ // FilenameDisplayFormat::PATH not supported by Calc
+ aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::FULL );
+ appendField( xContent );
+ /* path only is not supported -- if we find a '&Z&F'
+ combination for path/name, skip the '&F' part */
+ if( (pcChar + 2 < pcEnd) && (pcChar[ 1 ] == '&') && ((pcChar[ 2 ] == 'f') || (pcChar[ 2 ] == 'F')) )
+ pcChar += 2;
+ }
+ break;
+ case 'D': // date
+ {
+ Reference<text::XTextContent> xContent = createField( gaDateTimeService );
+ PropertySet aPropSet( xContent );
+ aPropSet.setProperty( PROP_IsDate, true );
+ appendField( xContent );
+ }
+ break;
+ case 'T': // time
+ {
+ Reference<text::XTextContent> xContent = createField( gaDateTimeService );
+ PropertySet aPropSet( xContent );
+ aPropSet.setProperty( PROP_IsDate, false );
+ appendField( xContent );
+ }
+ break;
+
+ case 'B': // bold
+ setAttributes();
+ maFontModel.mbBold = !maFontModel.mbBold;
+ break;
+ case 'I': // italic
+ setAttributes();
+ maFontModel.mbItalic = !maFontModel.mbItalic;
+ break;
+ case 'U': // underline
+ setAttributes();
+ maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_single) ? XML_none : XML_single;
+ break;
+ case 'E': // double underline
+ setAttributes();
+ maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_double) ? XML_none : XML_double;
+ break;
+ case 'S': // strikeout
+ setAttributes();
+ maFontModel.mbStrikeout = !maFontModel.mbStrikeout;
+ break;
+ case 'X': // superscript
+ setAttributes();
+ maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_superscript) ? XML_baseline : XML_superscript;
+ break;
+ case 'Y': // subscript
+ setAttributes();
+ maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_subscript) ? XML_baseline : XML_subscript;
+ break;
+ case 'O': // outlined
+ setAttributes();
+ maFontModel.mbOutline = !maFontModel.mbOutline;
+ break;
+ case 'H': // shadow
+ setAttributes();
+ maFontModel.mbShadow = !maFontModel.mbShadow;
+ break;
+
+ case 'K': // text color (not in BIFF)
+ if( pcChar + 6 < pcEnd )
+ {
+ setAttributes();
+ // eat the following 6 characters
+ convertFontColor( std::u16string_view( pcChar + 1, 6 ) );
+ pcChar += 6;
+ }
+ break;
+
+ case '\"': // font name
+ aFontName.setLength( 0 );
+ aFontStyle.setLength( 0 );
+ eState = STATE_FONTNAME;
+ break;
+ default:
+ if( ('0' <= cChar) && (cChar <= '9') ) // font size
+ {
+ nFontHeight = cChar - '0';
+ eState = STATE_FONTHEIGHT;
+ }
+ }
+ }
+ break;
+
+ case STATE_FONTNAME:
+ {
+ switch( cChar )
+ {
+ case '\"':
+ setAttributes();
+ convertFontName( aFontName.makeStringAndClear() );
+ eState = STATE_TEXT;
+ break;
+ case ',':
+ eState = STATE_FONTSTYLE;
+ break;
+ default:
+ aFontName.append( cChar );
+ }
+ }
+ break;
+
+ case STATE_FONTSTYLE:
+ {
+ switch( cChar )
+ {
+ case '\"':
+ setAttributes();
+ convertFontName( aFontName.makeStringAndClear() );
+ convertFontStyle( aFontStyle );
+ aFontStyle.setLength(0);
+ eState = STATE_TEXT;
+ break;
+ default:
+ aFontStyle.append( cChar );
+ }
+ }
+ break;
+
+ case STATE_FONTHEIGHT:
+ {
+ if( ('0' <= cChar) && (cChar <= '9') )
+ {
+ if( nFontHeight >= 0 )
+ {
+ nFontHeight *= 10;
+ nFontHeight += (cChar - '0');
+ if( nFontHeight > 1000 )
+ nFontHeight = -1;
+ }
+ }
+ else
+ {
+ if( nFontHeight > 0 )
+ {
+ setAttributes();
+ maFontModel.mfHeight = nFontHeight;
+ }
+ --pcChar;
+ eState = STATE_TEXT;
+ }
+ }
+ break;
+ }
+ }
+
+ // finalize
+ finalizePortion();
+ maPortions[ HF_LEFT ].mfTotalHeight += getCurrHeight( HF_LEFT );
+ maPortions[ HF_CENTER ].mfTotalHeight += getCurrHeight( HF_CENTER );
+ maPortions[ HF_RIGHT ].mfTotalHeight += getCurrHeight( HF_RIGHT );
+
+ return ::std::max( maPortions[ HF_LEFT ].mfTotalHeight,
+ ::std::max( maPortions[ HF_CENTER ].mfTotalHeight, maPortions[ HF_RIGHT ].mfTotalHeight ) );
+}
+
+// private --------------------------------------------------------------------
+
+double HeaderFooterParser::getCurrHeight( HFPortionId ePortion ) const
+{
+ double fMaxHt = maPortions[ ePortion ].mfCurrHeight;
+ return (fMaxHt == 0.0) ? maFontModel.mfHeight : fMaxHt;
+}
+
+void HeaderFooterParser::updateCurrHeight( HFPortionId ePortion )
+{
+ double& rfMaxHt = maPortions[ ePortion ].mfCurrHeight;
+ rfMaxHt = ::std::max( rfMaxHt, maFontModel.mfHeight );
+}
+
+void HeaderFooterParser::updateCurrHeight()
+{
+ updateCurrHeight( meCurrPortion );
+}
+
+void HeaderFooterParser::setAttributes()
+{
+ Reference<text::XTextRange> xRange = getStartPos();
+ getEndPos()->gotoRange( xRange, false );
+ getEndPos()->gotoEnd( true );
+ if( !getEndPos()->isCollapsed() )
+ {
+ Font aFont( *this, maFontModel );
+ aFont.finalizeImport();
+ PropertySet aPropSet( getEndPos() );
+ aFont.writeToPropertySet( aPropSet );
+ getStartPos()->gotoEnd( false );
+ getEndPos()->gotoEnd( false );
+ }
+}
+
+void HeaderFooterParser::appendText()
+{
+ if( !maBuffer.isEmpty() )
+ {
+ getEndPos()->gotoEnd( false );
+ getEndPos()->setString( maBuffer.makeStringAndClear() );
+ updateCurrHeight();
+ }
+}
+
+void HeaderFooterParser::appendLineBreak()
+{
+ getEndPos()->gotoEnd( false );
+ getEndPos()->setString( OUString( '\n' ) );
+ getPortion().mfTotalHeight += getCurrHeight( meCurrPortion ); // add the current line height.
+ getPortion().mfCurrHeight = 0;
+}
+
+Reference<text::XTextContent> HeaderFooterParser::createField( const OUString& rServiceName ) const
+{
+ Reference<text::XTextContent> xContent;
+ try
+ {
+ xContent.set( getBaseFilter().getModelFactory()->createInstance( rServiceName ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( OStringBuffer( "HeaderFooterParser::createField - error while creating text field \""
+ + OUStringToOString( rServiceName, RTL_TEXTENCODING_ASCII_US )
+ + "\"" ).getStr() );
+ }
+ return xContent;
+}
+
+void HeaderFooterParser::appendField( const Reference<text::XTextContent>& rxContent )
+{
+ getEndPos()->gotoEnd( false );
+ try
+ {
+ Reference<text::XTextRange> xRange( getEndPos(), UNO_QUERY_THROW );
+ getPortion().mxText->insertTextContent( xRange, rxContent, false );
+ updateCurrHeight();
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void HeaderFooterParser::convertFontName( const OUString& rName )
+{
+ if( !rName.isEmpty() )
+ {
+ // single dash is document default font
+ if( (rName.getLength() == 1) && (rName[ 0 ] == '-') )
+ maFontModel.maName = getStyles().getDefaultFontModel().maName;
+ else
+ maFontModel.maName = rName;
+ }
+}
+
+void HeaderFooterParser::convertFontStyle( std::u16string_view rStyle )
+{
+ maFontModel.mbBold = maFontModel.mbItalic = false;
+ if (rStyle.empty())
+ return;
+ for( sal_Int32 nPos{ 0 }; nPos>=0; )
+ {
+ OString aToken = OUStringToOString( o3tl::getToken(rStyle, 0, ' ', nPos ), RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
+ if( !aToken.isEmpty() )
+ {
+ if( maBoldNames.count( aToken ) > 0 )
+ maFontModel.mbBold = true;
+ else if( maItalicNames.count( aToken ) > 0 )
+ maFontModel.mbItalic = true;
+ }
+ }
+}
+
+void HeaderFooterParser::convertFontColor( std::u16string_view aColor )
+{
+ OSL_ENSURE( aColor.size() == 6, "HeaderFooterParser::convertFontColor - invalid font color code" );
+ if( (aColor[ 2 ] == '+') || (aColor[ 2 ] == '-') )
+ // theme color: TTSNNN (TT = decimal theme index, S = +/-, NNN = decimal tint/shade in percent)
+ maFontModel.maColor.setTheme(
+ o3tl::toInt32(aColor.substr( 0, 2 )),
+ static_cast< double >( o3tl::toInt32(aColor.substr( 2 )) ) / 100.0 );
+ else
+ // RGB color: RRGGBB
+ maFontModel.maColor.setRgb( ::Color(ColorTransparency, o3tl::toUInt32( aColor, 16 )) );
+}
+
+void HeaderFooterParser::finalizePortion()
+{
+ appendText();
+ setAttributes();
+}
+
+void HeaderFooterParser::setNewPortion( HFPortionId ePortion )
+{
+ if( ePortion != meCurrPortion )
+ {
+ finalizePortion();
+ meCurrPortion = ePortion;
+ maFontModel = getStyles().getDefaultFontModel();
+ }
+}
+
+PageSettingsConverter::HFHelperData::HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId, sal_Int32 nFirstPropId ) :
+ mnLeftPropId( nLeftPropId ),
+ mnRightPropId( nRightPropId ),
+ mnFirstPropId( nFirstPropId ),
+ mnHeight( 0 ),
+ mnBodyDist( 0 ),
+ mbHasContent( false ),
+ mbShareOddEven( false ),
+ mbShareFirst( false ),
+ mbDynamicHeight( false )
+{
+}
+
+PageSettingsConverter::PageSettingsConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mxHFParser( new HeaderFooterParser( rHelper ) ),
+ maHeaderData( PROP_LeftPageHeaderContent, PROP_RightPageHeaderContent, PROP_FirstPageHeaderContent ),
+ maFooterData( PROP_LeftPageFooterContent, PROP_RightPageFooterContent, PROP_FirstPageFooterContent )
+{
+}
+
+PageSettingsConverter::~PageSettingsConverter()
+{
+}
+
+void PageSettingsConverter::writePageSettingsProperties(
+ PropertySet& rPropSet, const PageSettingsModel& rModel, WorksheetType eSheetType )
+{
+ // special handling for chart sheets
+ bool bChartSheet = eSheetType == WorksheetType::Chart;
+
+ // printout scaling
+ if( bChartSheet )
+ {
+ // always fit chart sheet to 1 page
+ rPropSet.setProperty< sal_Int16 >( PROP_ScaleToPages, 1 );
+ }
+ else if( rModel.mbFitToPages )
+ {
+ // fit to number of pages
+ rPropSet.setProperty( PROP_ScaleToPagesX, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToWidth, 0, 1000 ) );
+ rPropSet.setProperty( PROP_ScaleToPagesY, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToHeight, 0, 1000 ) );
+ }
+ else
+ {
+ // scale may be 0 which indicates uninitialized
+ sal_Int16 nScale = (rModel.mnScale > 0) ? getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnScale, 10, 400 ) : 100;
+ rPropSet.setProperty( PROP_PageScale, nScale );
+ }
+
+ // paper orientation
+ bool bLandscape = rModel.mnOrientation == XML_landscape;
+ // default orientation for current sheet type (chart sheets default to landscape)
+ if( bChartSheet && ( !rModel.mbValidSettings || (rModel.mnOrientation == XML_default) ) )
+ bLandscape = true;
+
+ // paper size
+ if( !rModel.mbValidSettings )
+ {
+ awt::Size aSize;
+ bool bValid = false;
+
+ if( 0 < rModel.mnPaperSize )
+ {
+ const msfilter::util::ApiPaperSize& rPaperSize = msfilter::util::PaperSizeConv::getApiSizeForMSPaperSizeIndex( rModel.mnPaperSize );
+ aSize = awt::Size( rPaperSize.mnWidth, rPaperSize.mnHeight );
+ bValid = ( rPaperSize.mnWidth != 0 && rPaperSize.mnHeight != 0 );
+ }
+ if( rModel.mnPaperWidth > 0 && rModel.mnPaperHeight > 0 )
+ {
+ aSize = awt::Size( rModel.mnPaperWidth, rModel.mnPaperHeight );
+ bValid = true;
+ }
+
+ if( bValid )
+ {
+ if( bLandscape )
+ ::std::swap( aSize.Width, aSize.Height );
+ rPropSet.setProperty( PROP_Size, aSize );
+ }
+ }
+
+ // header/footer
+ convertHeaderFooterData( rPropSet, maHeaderData, rModel.maOddHeader, rModel.maEvenHeader, rModel.maFirstHeader, rModel.mbUseEvenHF, rModel.mbUseFirstHF, rModel.mfTopMargin, rModel.mfHeaderMargin );
+ convertHeaderFooterData( rPropSet, maFooterData, rModel.maOddFooter, rModel.maEvenFooter, rModel.maFirstFooter, rModel.mbUseEvenHF, rModel.mbUseFirstHF, rModel.mfBottomMargin, rModel.mfFooterMargin );
+
+ // write all properties to property set
+ PropertyMap aPropMap;
+ aPropMap.setProperty( PROP_IsLandscape, bLandscape);
+ aPropMap.setProperty( PROP_FirstPageNumber, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mbUseFirstPage ? rModel.mnFirstPage : 0, 0, 9999 ));
+ aPropMap.setProperty( PROP_PrintDownFirst, (rModel.mnPageOrder == XML_downThenOver));
+ aPropMap.setProperty( PROP_PrintAnnotations, (rModel.mnCellComments == XML_asDisplayed));
+ aPropMap.setProperty( PROP_CenterHorizontally, rModel.mbHorCenter);
+ aPropMap.setProperty( PROP_CenterVertically, rModel.mbVerCenter);
+ aPropMap.setProperty( PROP_PrintGrid, (!bChartSheet && rModel.mbPrintGrid)); // no gridlines in chart sheets
+ aPropMap.setProperty( PROP_PrintHeaders, (!bChartSheet && rModel.mbPrintHeadings)); // no column/row headings in chart sheets
+ aPropMap.setProperty<sal_Int32>( PROP_LeftMargin, std::round(o3tl::convert( rModel.mfLeftMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ aPropMap.setProperty<sal_Int32>( PROP_RightMargin, std::round(o3tl::convert( rModel.mfRightMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ // #i23296# In Calc, "TopMargin" property is distance to top of header if enabled
+ aPropMap.setProperty<sal_Int32>( PROP_TopMargin, std::round(o3tl::convert( maHeaderData.mbHasContent ? rModel.mfHeaderMargin : rModel.mfTopMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ // #i23296# In Calc, "BottomMargin" property is distance to bottom of footer if enabled
+ aPropMap.setProperty<sal_Int32>( PROP_BottomMargin, std::round(o3tl::convert( maFooterData.mbHasContent ? rModel.mfFooterMargin : rModel.mfBottomMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ aPropMap.setProperty( PROP_HeaderIsOn, maHeaderData.mbHasContent);
+ aPropMap.setProperty( PROP_HeaderIsShared, maHeaderData.mbShareOddEven);
+ aPropMap.setProperty( PROP_FirstPageHeaderIsShared, maHeaderData.mbShareFirst);
+ aPropMap.setProperty( PROP_HeaderIsDynamicHeight, maHeaderData.mbDynamicHeight);
+ aPropMap.setProperty( PROP_HeaderHeight, maHeaderData.mnHeight);
+ aPropMap.setProperty( PROP_HeaderBodyDistance, maHeaderData.mnBodyDist);
+ aPropMap.setProperty( PROP_FooterIsOn, maFooterData.mbHasContent);
+ aPropMap.setProperty( PROP_FooterIsShared, maFooterData.mbShareOddEven);
+ aPropMap.setProperty( PROP_FirstPageFooterIsShared, maFooterData.mbShareFirst);
+ aPropMap.setProperty( PROP_FooterIsDynamicHeight, maFooterData.mbDynamicHeight);
+ aPropMap.setProperty( PROP_FooterHeight, maFooterData.mnHeight);
+ aPropMap.setProperty( PROP_FooterBodyDistance, maFooterData.mnBodyDist);
+ // background image
+ if (rModel.mxGraphic.is())
+ {
+ aPropMap.setProperty(PROP_BackGraphic, rModel.mxGraphic);
+ aPropMap.setProperty(PROP_BackGraphicLocation, css::style::GraphicLocation_TILED);
+ }
+
+ rPropSet.setProperties( aPropMap );
+}
+
+void PageSettingsConverter::convertHeaderFooterData(
+ PropertySet& rPropSet, HFHelperData& orHFData,
+ const OUString& rOddContent, const OUString& rEvenContent, const OUString& rFirstContent,
+ bool bUseEvenContent, bool bUseFirstContent,
+ double fPageMargin, double fContentMargin )
+{
+ bool bHasOddContent = !rOddContent.isEmpty();
+ bool bHasEvenContent = bUseEvenContent && !rEvenContent.isEmpty();
+ bool bHasFirstContent = bUseFirstContent && !rFirstContent.isEmpty();
+
+ sal_Int32 nOddHeight = bHasOddContent ? writeHeaderFooter( rPropSet, orHFData.mnRightPropId, rOddContent ) : 0;
+ sal_Int32 nEvenHeight = bHasEvenContent ? writeHeaderFooter( rPropSet, orHFData.mnLeftPropId, rEvenContent ) : 0;
+ sal_Int32 nFirstHeight = bHasFirstContent ? writeHeaderFooter( rPropSet, orHFData.mnFirstPropId, rFirstContent ) : 0;
+
+ orHFData.mnHeight = 750;
+ orHFData.mnBodyDist = 250;
+ orHFData.mbHasContent = bHasOddContent || bHasEvenContent || bHasFirstContent;
+ orHFData.mbShareOddEven = !bUseEvenContent;
+ orHFData.mbShareFirst = !bUseFirstContent;
+ orHFData.mbDynamicHeight = true;
+
+ if( !orHFData.mbHasContent )
+ return;
+
+ // use maximum height of odd/even/first header/footer
+ orHFData.mnHeight = ::std::max( ::std::max( nOddHeight, nEvenHeight ), nFirstHeight );
+ /* Calc contains distance between bottom of header and top of page
+ body in "HeaderBodyDistance" property, and distance between bottom
+ of page body and top of footer in "FooterBodyDistance" property */
+ orHFData.mnBodyDist = std::round(o3tl::convert( fPageMargin - fContentMargin, o3tl::Length::in, o3tl::Length::mm100 )) - orHFData.mnHeight;
+ /* #i23296# Distance less than 0 means, header or footer overlays page
+ body. As this is not possible in Calc, set fixed header or footer
+ height (crop header/footer) to get correct top position of page body. */
+ orHFData.mbDynamicHeight = orHFData.mnBodyDist >= 0;
+ /* "HeaderHeight" property is in fact distance from top of header to
+ top of page body (including "HeaderBodyDistance").
+ "FooterHeight" property is in fact distance from bottom of page
+ body to bottom of footer (including "FooterBodyDistance"). */
+ orHFData.mnHeight += orHFData.mnBodyDist;
+ // negative body distance not allowed
+ orHFData.mnBodyDist = ::std::max< sal_Int32 >( orHFData.mnBodyDist, 0 );
+}
+
+sal_Int32 PageSettingsConverter::writeHeaderFooter(
+ PropertySet& rPropSet, sal_Int32 nPropId, const OUString& rContent )
+{
+ OSL_ENSURE( !rContent.isEmpty(), "PageSettingsConverter::writeHeaderFooter - empty h/f string found" );
+ sal_Int32 nHeight = 0;
+ if( !rContent.isEmpty() )
+ {
+ Reference<sheet::XHeaderFooterContent> xHFContent(rPropSet.getAnyProperty(nPropId), UNO_QUERY);
+ if( xHFContent.is() )
+ {
+ double fTotalHeight = mxHFParser->parse( xHFContent, rContent );
+ rPropSet.setProperty( nPropId, xHFContent );
+ nHeight = std::round(o3tl::convert(fTotalHeight, o3tl::Length::pt, o3tl::Length::mm100));
+ }
+ }
+ return nHeight;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/patterncache.cxx b/sc/source/filter/oox/patterncache.cxx
new file mode 100644
index 0000000000..2431a36fed
--- /dev/null
+++ b/sc/source/filter/oox/patterncache.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 <patterncache.hxx>
+
+ScPatternCache::Entry::Entry()
+ : nXfId(-1)
+ , nNumFmtId(-1)
+ , pPattern(nullptr)
+{
+}
+
+ScPatternCache::ScPatternCache()
+ : nNextPos(0)
+{
+}
+
+ScPatternAttr* ScPatternCache::query(sal_Int32 nXfId, sal_Int32 nNumFmtId) const
+{
+ for (const auto& entry : maEntries)
+ {
+ if (entry.nXfId == nXfId && entry.nNumFmtId == nNumFmtId)
+ return entry.pPattern;
+ }
+
+ return nullptr;
+}
+
+void ScPatternCache::add(sal_Int32 nXfId, sal_Int32 nNumFmtId, ScPatternAttr* pPattern)
+{
+ Entry& rEntry = maEntries[nNextPos];
+ nNextPos = ((nNextPos + 1) % nPatternCacheSize);
+ rEntry.nXfId = nXfId;
+ rEntry.nNumFmtId = nNumFmtId;
+ rEntry.pPattern = pPattern;
+}
diff --git a/sc/source/filter/oox/pivotcachebuffer.cxx b/sc/source/filter/oox/pivotcachebuffer.cxx
new file mode 100644
index 0000000000..d8cf350bf9
--- /dev/null
+++ b/sc/source/filter/oox/pivotcachebuffer.cxx
@@ -0,0 +1,1233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivotcachebuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupInfo.hpp>
+#include <com/sun/star/sheet/XDataPilotFieldGrouping.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <defnamesbuffer.hxx>
+#include <pivotcachefragment.hxx>
+#include <sheetdatabuffer.hxx>
+#include <tablebuffer.hxx>
+#include <unitconverter.hxx>
+#include <worksheetbuffer.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <tools/datetime.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::Relations;
+
+namespace {
+
+const sal_uInt16 BIFF12_PCDFIELD_SERVERFIELD = 0x0001;
+const sal_uInt16 BIFF12_PCDFIELD_NOUNIQUEITEMS = 0x0002;
+const sal_uInt16 BIFF12_PCDFIELD_DATABASEFIELD = 0x0004;
+const sal_uInt16 BIFF12_PCDFIELD_HASCAPTION = 0x0008;
+const sal_uInt16 BIFF12_PCDFIELD_MEMBERPROPFIELD = 0x0010;
+const sal_uInt16 BIFF12_PCDFIELD_HASFORMULA = 0x0100;
+const sal_uInt16 BIFF12_PCDFIELD_HASPROPERTYNAME = 0x0200;
+
+const sal_uInt16 BIFF12_PCDFSITEMS_HASSEMIMIXED = 0x0001;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASNONDATE = 0x0002;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASDATE = 0x0004;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASSTRING = 0x0008;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASBLANK = 0x0010;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASMIXED = 0x0020;
+const sal_uInt16 BIFF12_PCDFSITEMS_ISNUMERIC = 0x0040;
+const sal_uInt16 BIFF12_PCDFSITEMS_ISINTEGER = 0x0080;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASLONGTEXT = 0x0200;
+
+const sal_uInt16 BIFF12_PCITEM_ARRAY_DOUBLE = 0x0001;
+const sal_uInt16 BIFF12_PCITEM_ARRAY_STRING = 0x0002;
+const sal_uInt16 BIFF12_PCITEM_ARRAY_ERROR = 0x0010;
+const sal_uInt16 BIFF12_PCITEM_ARRAY_DATE = 0x0020;
+
+const sal_uInt8 BIFF12_PCDFRANGEPR_AUTOSTART = 0x01;
+const sal_uInt8 BIFF12_PCDFRANGEPR_AUTOEND = 0x02;
+const sal_uInt8 BIFF12_PCDFRANGEPR_DATEGROUP = 0x04;
+
+const sal_uInt8 BIFF12_PCDEFINITION_SAVEDATA = 0x01;
+const sal_uInt8 BIFF12_PCDEFINITION_INVALID = 0x02;
+const sal_uInt8 BIFF12_PCDEFINITION_REFRESHONLOAD = 0x04;
+const sal_uInt8 BIFF12_PCDEFINITION_OPTIMIZEMEMORY = 0x08;
+const sal_uInt8 BIFF12_PCDEFINITION_ENABLEREFRESH = 0x10;
+const sal_uInt8 BIFF12_PCDEFINITION_BACKGROUNDQUERY = 0x20;
+const sal_uInt8 BIFF12_PCDEFINITION_UPGRADEONREFR = 0x40;
+const sal_uInt8 BIFF12_PCDEFINITION_TUPLECACHE = 0x80;
+
+const sal_uInt8 BIFF12_PCDEFINITION_HASUSERNAME = 0x01;
+const sal_uInt8 BIFF12_PCDEFINITION_HASRELID = 0x02;
+const sal_uInt8 BIFF12_PCDEFINITION_SUPPORTSUBQUERY = 0x04;
+const sal_uInt8 BIFF12_PCDEFINITION_SUPPORTDRILL = 0x08;
+
+const sal_uInt8 BIFF12_PCDWBSOURCE_HASRELID = 0x01;
+const sal_uInt8 BIFF12_PCDWBSOURCE_HASSHEET = 0x02;
+
+
+/** Adjusts the weird date format read from binary streams.
+
+ Dates before 1900-Mar-01 are stored including the non-existing leap day
+ 1900-02-29. tools::Time values (without date) are stored as times of day
+ 1900-Jan-00. Nothing has to be done when the workbook is stored in 1904
+ date mode (dates before 1904-Jan-01 will not occur in this case).
+ */
+void lclAdjustBinDateTime( css::util::DateTime& orDateTime )
+{
+ if( (orDateTime.Year == 1900) && (orDateTime.Month <= 2) )
+ {
+ OSL_ENSURE( (orDateTime.Month == 1) || ((orDateTime.Month == 2) && (orDateTime.Day > 0)), "lclAdjustBinDateTime - invalid date" );
+ switch( orDateTime.Month )
+ {
+ case 2: if( orDateTime.Day > 1 ) --orDateTime.Day; else { orDateTime.Day += 30; --orDateTime.Month; } break;
+ case 1: if( orDateTime.Day > 1 ) --orDateTime.Day; else { orDateTime.Day += 30; orDateTime.Month = 12; --orDateTime.Year; } break;
+ }
+ }
+}
+
+} // namespace
+
+PivotCacheItem::PivotCacheItem() :
+ mnType( XML_m ), mbUnused( false )
+{
+}
+
+void PivotCacheItem::readString( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getXString( XML_v, OUString() );
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readNumeric( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getDouble( XML_v, 0.0 );
+ mnType = XML_n;
+ mbUnused = rAttribs.getBool( XML_u, false );
+}
+
+void PivotCacheItem::readDate( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getDateTime( XML_v, css::util::DateTime() );
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getBool( XML_v, false );
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getXString( XML_v, OUString() );
+ mnType = XML_e;
+}
+
+void PivotCacheItem::readIndex( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getInteger( XML_v, -1 );
+ mnType = XML_x;
+}
+
+void PivotCacheItem::readString( SequenceInputStream& rStrm )
+{
+ maValue <<= BiffHelper::readString( rStrm );
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readDouble( SequenceInputStream& rStrm )
+{
+ maValue <<= rStrm.readDouble();
+ mnType = XML_n;
+}
+
+void PivotCacheItem::readDate( SequenceInputStream& rStrm )
+{
+ css::util::DateTime aDateTime;
+ aDateTime.Year = rStrm.readuInt16();
+ aDateTime.Month = rStrm.readuInt16();
+ aDateTime.Day = rStrm.readuInt8();
+ aDateTime.Hours = rStrm.readuInt8();
+ aDateTime.Minutes = rStrm.readuInt8();
+ aDateTime.Seconds = rStrm.readuInt8();
+ lclAdjustBinDateTime( aDateTime );
+ maValue <<= aDateTime;
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( SequenceInputStream& rStrm )
+{
+ maValue <<= (rStrm.readuInt8() != 0);
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError(SequenceInputStream& rStrm, const UnitConverter& rUnitConverter)
+{
+ maValue <<= rUnitConverter.calcErrorString(rStrm.readuInt8());
+ mnType = XML_e;
+}
+
+void PivotCacheItem::readIndex( SequenceInputStream& rStrm )
+{
+ maValue <<= rStrm.readInt32();
+ mnType = XML_x;
+}
+
+void PivotCacheItem::setStringValue( const OUString& sString )
+{
+ mnType = XML_s;
+ maValue <<= sString;
+}
+
+OUString PivotCacheItem::getName() const
+{
+ switch( mnType )
+ {
+ case XML_m: return OUString();
+ case XML_s: return maValue.get< OUString >();
+ case XML_n: return OUString::number( maValue.get< double >() ); // !TODO
+ case XML_i: return OUString::number( maValue.get< sal_Int32 >() );
+ case XML_d: return OUString(); // !TODO
+ case XML_b: return OUString::boolean( maValue.get< bool >() ); // !TODO
+ case XML_e: return OUString(); // !TODO
+ }
+ OSL_FAIL( "PivotCacheItem::getName - invalid data type" );
+ return OUString();
+}
+
+OUString PivotCacheItem::getFormattedName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const DateTime& rNullDate) const
+{
+ switch( mnType )
+ {
+ case XML_m: return OUString();
+ case XML_s: return maValue.get< OUString >();
+ case XML_n: return pObj->GetFormattedString(rSaveDim.GetName(), maValue.get<double>());
+ case XML_i: return pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(maValue.get< sal_Int32 >()));
+ case XML_b: return pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(maValue.get< bool >()));
+ case XML_d:
+ {
+ css::util::DateTime aDateTime(maValue.get< css::util::DateTime >());
+ if (aDateTime.Year == 0)
+ {
+ SAL_WARN("sc", "PivotCacheField::getFormattedName - invalid date");
+ return OUString();
+ }
+ return pObj->GetFormattedString(rSaveDim.GetName(), DateTime::Sub(aDateTime, rNullDate));
+ }
+ case XML_e: return maValue.get< OUString >();
+ }
+ OSL_FAIL( "PivotCacheItem::getFormattedName - invalid data type" );
+ return OUString();
+}
+
+PivotCacheItemList::PivotCacheItemList( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PivotCacheItemList::importItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ PivotCacheItem& rItem = createItem();
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): rItem.readString( rAttribs ); break;
+ case XLS_TOKEN( n ): rItem.readNumeric( rAttribs ); break;
+ case XLS_TOKEN( d ): rItem.readDate( rAttribs ); break;
+ case XLS_TOKEN( b ): rItem.readBool( rAttribs ); break;
+ case XLS_TOKEN( e ): rItem.readError( rAttribs ); break;
+ default: OSL_FAIL( "PivotCacheItemList::importItem - unknown element type" );
+ }
+}
+
+void PivotCacheItemList::importItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( nRecId == BIFF12_ID_PCITEM_ARRAY )
+ {
+ importArray( rStrm );
+ return;
+ }
+
+ PivotCacheItem& rItem = createItem();
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCITEM_MISSING:
+ case BIFF12_ID_PCITEMA_MISSING: break;
+ case BIFF12_ID_PCITEM_STRING:
+ case BIFF12_ID_PCITEMA_STRING: rItem.readString( rStrm ); break;
+ case BIFF12_ID_PCITEM_DOUBLE:
+ case BIFF12_ID_PCITEMA_DOUBLE: rItem.readDouble( rStrm ); break;
+ case BIFF12_ID_PCITEM_DATE:
+ case BIFF12_ID_PCITEMA_DATE: rItem.readDate( rStrm ); break;
+ case BIFF12_ID_PCITEM_BOOL:
+ case BIFF12_ID_PCITEMA_BOOL: rItem.readBool( rStrm ); break;
+ case BIFF12_ID_PCITEM_ERROR:
+ case BIFF12_ID_PCITEMA_ERROR: rItem.readError(rStrm, getUnitConverter()); break;
+ default: OSL_FAIL( "PivotCacheItemList::importItem - unknown record type" );
+ }
+}
+
+const PivotCacheItem* PivotCacheItemList::getCacheItem( sal_Int32 nItemIdx ) const
+{
+ return ContainerHelper::getVectorElement( maItems, nItemIdx );
+}
+
+void PivotCacheItemList::applyItemCaptions( const IdCaptionPairList& vCaptions )
+{
+ for( const auto& [rId, rCaption] : vCaptions )
+ {
+ if ( o3tl::make_unsigned( rId ) < maItems.size() )
+ maItems[ rId ].setStringValue( rCaption );
+ }
+}
+
+void PivotCacheItemList::getCacheItemNames( ::std::vector< OUString >& orItemNames ) const
+{
+ orItemNames.clear();
+ orItemNames.reserve( maItems.size() );
+ for( const auto& rItem : maItems )
+ orItemNames.push_back( rItem.getName() );
+}
+
+// private --------------------------------------------------------------------
+
+PivotCacheItem& PivotCacheItemList::createItem()
+{
+ maItems.emplace_back();
+ return maItems.back();
+}
+
+void PivotCacheItemList::importArray( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nType = rStrm.readuInt16();
+ sal_Int32 nCount = rStrm.readInt32();
+ for( sal_Int32 nIdx = 0; !rStrm.isEof() && (nIdx < nCount); ++nIdx )
+ {
+ switch( nType )
+ {
+ case BIFF12_PCITEM_ARRAY_DOUBLE: createItem().readDouble( rStrm ); break;
+ case BIFF12_PCITEM_ARRAY_STRING: createItem().readString( rStrm ); break;
+ case BIFF12_PCITEM_ARRAY_ERROR: createItem().readError(rStrm, getUnitConverter()); break;
+ case BIFF12_PCITEM_ARRAY_DATE: createItem().readDate( rStrm ); break;
+ default:
+ OSL_FAIL( "PivotCacheItemList::importArray - unknown data type" );
+ return;
+ }
+ }
+}
+
+PCFieldModel::PCFieldModel() :
+ mnNumFmtId( 0 ),
+ mnSqlType( 0 ),
+ mnHierarchy( 0 ),
+ mnLevel( 0 ),
+ mnMappingCount( 0 ),
+ mbDatabaseField( true ),
+ mbServerField( false ),
+ mbUniqueList( true ),
+ mbMemberPropField( false )
+{
+}
+
+PCSharedItemsModel::PCSharedItemsModel() :
+ mbHasSemiMixed( true ),
+ mbHasNonDate( true ),
+ mbHasDate( false ),
+ mbHasString( true ),
+ mbHasBlank( false ),
+ mbHasMixed( false ),
+ mbIsNumeric( false ),
+ mbIsInteger( false ),
+ mbHasLongText( false )
+{
+}
+
+PCFieldGroupModel::PCFieldGroupModel() :
+ mfStartValue( 0.0 ),
+ mfEndValue( 0.0 ),
+ mfInterval( 1.0 ),
+ mnParentField( -1 ),
+ mnBaseField( -1 ),
+ mnGroupBy( XML_range ),
+ mbRangeGroup( false ),
+ mbDateGroup( false ),
+ mbAutoStart( true ),
+ mbAutoEnd( true )
+{
+}
+
+void PCFieldGroupModel::setBiffGroupBy( sal_uInt8 nGroupBy )
+{
+ static const sal_Int32 spnGroupBy[] = { XML_range,
+ XML_seconds, XML_minutes, XML_hours, XML_days, XML_months, XML_quarters, XML_years };
+ mnGroupBy = STATIC_ARRAY_SELECT( spnGroupBy, nGroupBy, XML_range );
+}
+
+PivotCacheField::PivotCacheField( const WorkbookHelper& rHelper, bool bIsDatabaseField ) :
+ WorkbookHelper( rHelper ),
+ maSharedItems( rHelper ),
+ maGroupItems( rHelper )
+{
+ maFieldModel.mbDatabaseField = bIsDatabaseField;
+}
+
+void PivotCacheField::importCacheField( const AttributeList& rAttribs )
+{
+ maFieldModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maFieldModel.maCaption = rAttribs.getXString( XML_caption, OUString() );
+ maFieldModel.maPropertyName = rAttribs.getXString( XML_propertyName, OUString() );
+ maFieldModel.maFormula = rAttribs.getXString( XML_formula, OUString() );
+ maFieldModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maFieldModel.mnSqlType = rAttribs.getInteger( XML_sqlType, 0 );
+ maFieldModel.mnHierarchy = rAttribs.getInteger( XML_hierarchy, 0 );
+ maFieldModel.mnLevel = rAttribs.getInteger( XML_level, 0 );
+ maFieldModel.mnMappingCount = rAttribs.getInteger( XML_mappingCount, 0 );
+ maFieldModel.mbDatabaseField = rAttribs.getBool( XML_databaseField, true );
+ maFieldModel.mbServerField = rAttribs.getBool( XML_serverField, false );
+ maFieldModel.mbUniqueList = rAttribs.getBool( XML_uniqueList, true );
+ maFieldModel.mbMemberPropField = rAttribs.getBool( XML_memberPropertyField, false );
+}
+
+void PivotCacheField::importSharedItems( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( maSharedItems.empty(), "PivotCacheField::importSharedItems - multiple shared items elements" );
+ maSharedItemsModel.mbHasSemiMixed = rAttribs.getBool( XML_containsSemiMixedTypes, true );
+ maSharedItemsModel.mbHasNonDate = rAttribs.getBool( XML_containsNonDate, true );
+ maSharedItemsModel.mbHasDate = rAttribs.getBool( XML_containsDate, false );
+ maSharedItemsModel.mbHasString = rAttribs.getBool( XML_containsString, true );
+ maSharedItemsModel.mbHasBlank = rAttribs.getBool( XML_containsBlank, false );
+ maSharedItemsModel.mbHasMixed = rAttribs.getBool( XML_containsMixedTypes, false );
+ maSharedItemsModel.mbIsNumeric = rAttribs.getBool( XML_containsNumber, false );
+ maSharedItemsModel.mbIsInteger = rAttribs.getBool( XML_containsInteger, false );
+ maSharedItemsModel.mbHasLongText = rAttribs.getBool( XML_longText, false );
+}
+
+void PivotCacheField::importSharedItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ maSharedItems.importItem( nElement, rAttribs );
+}
+
+void PivotCacheField::importFieldGroup( const AttributeList& rAttribs )
+{
+ maFieldGroupModel.mnParentField = rAttribs.getInteger( XML_par, -1 );
+ maFieldGroupModel.mnBaseField = rAttribs.getInteger( XML_base, -1 );
+}
+
+void PivotCacheField::importRangePr( const AttributeList& rAttribs )
+{
+ maFieldGroupModel.maStartDate = rAttribs.getDateTime( XML_startDate, css::util::DateTime() );
+ maFieldGroupModel.maEndDate = rAttribs.getDateTime( XML_endDate, css::util::DateTime() );
+ maFieldGroupModel.mfStartValue = rAttribs.getDouble( XML_startNum, 0.0 );
+ maFieldGroupModel.mfEndValue = rAttribs.getDouble( XML_endNum, 0.0 );
+ maFieldGroupModel.mfInterval = rAttribs.getDouble( XML_groupInterval, 1.0 );
+ maFieldGroupModel.mnGroupBy = rAttribs.getToken( XML_groupBy, XML_range );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = maFieldGroupModel.mnGroupBy != XML_range;
+ maFieldGroupModel.mbAutoStart = rAttribs.getBool( XML_autoStart, true );
+ maFieldGroupModel.mbAutoEnd = rAttribs.getBool( XML_autoEnd, true );
+}
+
+void PivotCacheField::importDiscretePrItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ OSL_ENSURE( nElement == XLS_TOKEN( x ), "PivotCacheField::importDiscretePrItem - unexpected element" );
+ if( nElement == XLS_TOKEN( x ) )
+ maDiscreteItems.push_back( rAttribs.getInteger( XML_v, -1 ) );
+}
+
+void PivotCacheField::importGroupItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ maGroupItems.importItem( nElement, rAttribs );
+}
+
+void PivotCacheField::importPCDField( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ maFieldModel.mnNumFmtId = rStrm.readInt32();
+ maFieldModel.mnSqlType = rStrm.readInt16();
+ maFieldModel.mnHierarchy = rStrm.readInt32();
+ maFieldModel.mnLevel = rStrm.readInt32();
+ maFieldModel.mnMappingCount = rStrm.readInt32();
+ rStrm >> maFieldModel.maName;
+ if( getFlag( nFlags, BIFF12_PCDFIELD_HASCAPTION ) )
+ rStrm >> maFieldModel.maCaption;
+ if( getFlag( nFlags, BIFF12_PCDFIELD_HASFORMULA ) )
+ rStrm.skip( ::std::max< sal_Int32 >( rStrm.readInt32(), 0 ) );
+ if( maFieldModel.mnMappingCount > 0 )
+ rStrm.skip( ::std::max< sal_Int32 >( rStrm.readInt32(), 0 ) );
+ if( getFlag( nFlags, BIFF12_PCDFIELD_HASPROPERTYNAME ) )
+ rStrm >> maFieldModel.maPropertyName;
+
+ maFieldModel.mbDatabaseField = getFlag( nFlags, BIFF12_PCDFIELD_DATABASEFIELD );
+ maFieldModel.mbServerField = getFlag( nFlags, BIFF12_PCDFIELD_SERVERFIELD );
+ maFieldModel.mbUniqueList = !getFlag( nFlags, BIFF12_PCDFIELD_NOUNIQUEITEMS );
+ maFieldModel.mbMemberPropField = getFlag( nFlags, BIFF12_PCDFIELD_MEMBERPROPFIELD );
+}
+
+void PivotCacheField::importPCDFSharedItems( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ maSharedItemsModel.mbHasSemiMixed = getFlag( nFlags, BIFF12_PCDFSITEMS_HASSEMIMIXED );
+ maSharedItemsModel.mbHasNonDate = getFlag( nFlags, BIFF12_PCDFSITEMS_HASNONDATE );
+ maSharedItemsModel.mbHasDate = getFlag( nFlags, BIFF12_PCDFSITEMS_HASDATE );
+ maSharedItemsModel.mbHasString = getFlag( nFlags, BIFF12_PCDFSITEMS_HASSTRING );
+ maSharedItemsModel.mbHasBlank = getFlag( nFlags, BIFF12_PCDFSITEMS_HASBLANK );
+ maSharedItemsModel.mbHasMixed = getFlag( nFlags, BIFF12_PCDFSITEMS_HASMIXED );
+ maSharedItemsModel.mbIsNumeric = getFlag( nFlags, BIFF12_PCDFSITEMS_ISNUMERIC );
+ maSharedItemsModel.mbIsInteger = getFlag( nFlags, BIFF12_PCDFSITEMS_ISINTEGER );
+ maSharedItemsModel.mbHasLongText = getFlag( nFlags, BIFF12_PCDFSITEMS_HASLONGTEXT );
+}
+
+void PivotCacheField::importPCDFSharedItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ maSharedItems.importItem( nRecId, rStrm );
+}
+
+void PivotCacheField::importPCDFieldGroup( SequenceInputStream& rStrm )
+{
+ maFieldGroupModel.mnParentField = rStrm.readInt32();
+ maFieldGroupModel.mnBaseField = rStrm.readInt32();
+}
+
+void PivotCacheField::importPCDFRangePr( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nGroupBy, nFlags;
+ nGroupBy = rStrm.readuChar();
+ nFlags = rStrm.readuChar();
+ maFieldGroupModel.mfStartValue = rStrm.readDouble();
+ maFieldGroupModel.mfEndValue = rStrm.readDouble();
+ maFieldGroupModel.mfInterval = rStrm.readDouble();
+
+ maFieldGroupModel.setBiffGroupBy( nGroupBy );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = getFlag( nFlags, BIFF12_PCDFRANGEPR_DATEGROUP );
+ maFieldGroupModel.mbAutoStart = getFlag( nFlags, BIFF12_PCDFRANGEPR_AUTOSTART );
+ maFieldGroupModel.mbAutoEnd = getFlag( nFlags, BIFF12_PCDFRANGEPR_AUTOEND );
+
+ OSL_ENSURE( maFieldGroupModel.mbDateGroup == (maFieldGroupModel.mnGroupBy != XML_range), "PivotCacheField::importPCDFRangePr - wrong date flag" );
+ if( maFieldGroupModel.mbDateGroup )
+ {
+ maFieldGroupModel.maStartDate = getUnitConverter().calcDateTimeFromSerial( maFieldGroupModel.mfStartValue );
+ maFieldGroupModel.maEndDate = getUnitConverter().calcDateTimeFromSerial( maFieldGroupModel.mfEndValue );
+ }
+}
+
+void PivotCacheField::importPCDFDiscretePrItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( nRecId == BIFF12_ID_PCITEM_INDEX, "PivotCacheField::importPCDFDiscretePrItem - unexpected record" );
+ if( nRecId == BIFF12_ID_PCITEM_INDEX )
+ maDiscreteItems.push_back( rStrm.readInt32() );
+}
+
+void PivotCacheField::importPCDFGroupItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ maGroupItems.importItem( nRecId, rStrm );
+}
+
+const PivotCacheItem* PivotCacheField::getCacheItem( sal_Int32 nItemIdx ) const
+{
+ if( hasGroupItems() )
+ return maGroupItems.getCacheItem( nItemIdx );
+ if( hasSharedItems() )
+ return maSharedItems.getCacheItem( nItemIdx );
+ return nullptr;
+}
+
+void PivotCacheField::applyItemCaptions( const IdCaptionPairList& vCaptions )
+{
+ if( hasGroupItems() )
+ maGroupItems.applyItemCaptions( vCaptions );
+ if( hasSharedItems() )
+ maSharedItems.applyItemCaptions( vCaptions );
+}
+
+void PivotCacheField::getCacheItemNames( ::std::vector< OUString >& orItemNames ) const
+{
+ if( hasGroupItems() )
+ maGroupItems.getCacheItemNames( orItemNames );
+ else if( hasSharedItems() )
+ maSharedItems.getCacheItemNames( orItemNames );
+}
+
+const PivotCacheItemList& PivotCacheField::getCacheItems() const
+{
+ if( hasGroupItems() )
+ return maGroupItems;
+ return maSharedItems;
+}
+
+void PivotCacheField::convertNumericGrouping( const Reference< XDataPilotField >& rxDPField ) const
+{
+ OSL_ENSURE( hasGroupItems() && hasNumericGrouping(), "PivotCacheField::convertNumericGrouping - not a numeric group field" );
+ PropertySet aPropSet( rxDPField );
+ if( hasGroupItems() && hasNumericGrouping() && aPropSet.is() )
+ {
+ DataPilotFieldGroupInfo aGroupInfo;
+ aGroupInfo.HasAutoStart = maFieldGroupModel.mbAutoStart;
+ aGroupInfo.HasAutoEnd = maFieldGroupModel.mbAutoEnd;
+ aGroupInfo.HasDateValues = false;
+ aGroupInfo.Start = maFieldGroupModel.mfStartValue;
+ aGroupInfo.End = maFieldGroupModel.mfEndValue;
+ aGroupInfo.Step = maFieldGroupModel.mfInterval;
+ aGroupInfo.GroupBy = 0;
+ aPropSet.setProperty( PROP_GroupInfo, aGroupInfo );
+ }
+}
+
+OUString PivotCacheField::createDateGroupField( const Reference< XDataPilotField >& rxBaseDPField ) const
+{
+ OSL_ENSURE( hasGroupItems() && hasDateGrouping(), "PivotCacheField::createDateGroupField - not a numeric group field" );
+ Reference< XDataPilotField > xDPGroupField;
+ PropertySet aPropSet( rxBaseDPField );
+ if( hasGroupItems() && hasDateGrouping() && aPropSet.is() )
+ {
+ bool bDayRanges = (maFieldGroupModel.mnGroupBy == XML_days) && (maFieldGroupModel.mfInterval >= 2.0);
+
+ DataPilotFieldGroupInfo aGroupInfo;
+ aGroupInfo.HasAutoStart = maFieldGroupModel.mbAutoStart;
+ aGroupInfo.HasAutoEnd = maFieldGroupModel.mbAutoEnd;
+ aGroupInfo.HasDateValues = true;
+ aGroupInfo.Start = getUnitConverter().calcSerialFromDateTime( maFieldGroupModel.maStartDate );
+ aGroupInfo.End = getUnitConverter().calcSerialFromDateTime( maFieldGroupModel.maEndDate );
+ aGroupInfo.Step = bDayRanges ? maFieldGroupModel.mfInterval : 0.0;
+
+ using namespace ::com::sun::star::sheet::DataPilotFieldGroupBy;
+ switch( maFieldGroupModel.mnGroupBy )
+ {
+ case XML_years: aGroupInfo.GroupBy = YEARS; break;
+ case XML_quarters: aGroupInfo.GroupBy = QUARTERS; break;
+ case XML_months: aGroupInfo.GroupBy = MONTHS; break;
+ case XML_days: aGroupInfo.GroupBy = DAYS; break;
+ case XML_hours: aGroupInfo.GroupBy = HOURS; break;
+ case XML_minutes: aGroupInfo.GroupBy = MINUTES; break;
+ case XML_seconds: aGroupInfo.GroupBy = SECONDS; break;
+ default: OSL_FAIL( "PivotCacheField::convertRangeGrouping - unknown date/time interval" );
+ }
+
+ try
+ {
+ Reference< XDataPilotFieldGrouping > xDPGrouping( rxBaseDPField, UNO_QUERY_THROW );
+ xDPGroupField = xDPGrouping->createDateGroup( aGroupInfo );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ Reference< XNamed > xFieldName( xDPGroupField, UNO_QUERY );
+ return xFieldName.is() ? xFieldName->getName() : OUString();
+}
+
+OUString PivotCacheField::createParentGroupField( const Reference< XDataPilotField >& rxBaseDPField, const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames ) const
+{
+ SAL_WARN_IF( !hasGroupItems() || maDiscreteItems.empty(), "sc", "PivotCacheField::createParentGroupField - not a group field" );
+ SAL_WARN_IF( maDiscreteItems.size() != orItemNames.size(), "sc", "PivotCacheField::createParentGroupField - number of item names does not match grouping info" );
+ Reference< XDataPilotFieldGrouping > xDPGrouping( rxBaseDPField, UNO_QUERY );
+ if( !xDPGrouping.is() ) return OUString();
+
+ // map the group item indexes from maGroupItems to all item indexes from maDiscreteItems
+ std::vector< std::vector<sal_Int32> > aItemMap( maGroupItems.size() );
+ sal_Int32 nIndex = -1;
+ for( const auto& rDiscreteItem : maDiscreteItems )
+ {
+ ++nIndex;
+ if( std::vector<sal_Int32>* pItems = ContainerHelper::getVectorElementAccess( aItemMap, rDiscreteItem ) )
+ {
+ if ( const PivotCacheItem* pItem = rBaseCacheField.getCacheItems().getCacheItem( nIndex ) )
+ {
+ // Skip unspecified or unused entries or errors
+ if ( pItem->isUnused() || ( pItem->getType() == XML_m ) || ( pItem->getType() == XML_e ) )
+ continue;
+ }
+ pItems->push_back( nIndex );
+ }
+ }
+
+ // process all groups
+ Reference< XDataPilotField > xDPGroupField;
+ nIndex = 0;
+ for( const auto& rItems : aItemMap )
+ {
+ SAL_WARN_IF( rItems.empty(), "sc", "PivotCacheField::createParentGroupField - item/group should not be empty" );
+ if( !rItems.empty() )
+ {
+ /* Insert the names of the items that are part of this group. Calc
+ expects the names of the members of the field whose members are
+ grouped (which may be the names of groups too). Excel provides
+ the names of the base field items instead (no group names
+ involved). Therefore, the passed collection of current item
+ names as they are already grouped is used here to resolve the
+ item names. */
+ ::std::vector< OUString > aMembers;
+ for( auto i : rItems )
+ if( const PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, i ) )
+ if( ::std::find( aMembers.begin(), aMembers.end(), pName->maGroupName ) == aMembers.end() )
+ aMembers.push_back( pName->maGroupName );
+
+ /* Check again, that this is not just a group that is not grouped
+ further with other items. */
+ if( !aMembers.empty() ) try
+ {
+ // only the first call of createNameGroup() returns the new field
+ Reference< XDataPilotField > xDPNewField = xDPGrouping->createNameGroup( comphelper::containerToSequence( aMembers ) );
+ SAL_WARN_IF( xDPGroupField.is() == xDPNewField.is(), "sc", "PivotCacheField::createParentGroupField - missing group field" );
+ if( !xDPGroupField.is() )
+ xDPGroupField = xDPNewField;
+
+ // get current grouping info
+ DataPilotFieldGroupInfo aGroupInfo;
+ PropertySet aPropSet( xDPGroupField );
+ aPropSet.getProperty( aGroupInfo, PROP_GroupInfo );
+
+ /* Find the group object and the auto-generated group name.
+ The returned field contains all groups derived from the
+ previous field if that is grouped too. To find the correct
+ group, the first item used to create the group is searched.
+ Calc provides the original item names of the base field
+ when the group is queried for its members. Its does not
+ provide the names of members that are already groups in the
+ field used to create the new groups. (Is this a bug?)
+ Therefore, a name from the passed list of original item
+ names is used to find the correct group. */
+ OUString aFirstItem;
+ if( const PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, rItems.front() ) )
+ aFirstItem = pName->maOrigName;
+ Reference< XNamed > xGroupName;
+ OUString aAutoName;
+ Reference< XIndexAccess > xGroupsIA( aGroupInfo.Groups, UNO_QUERY_THROW );
+ for( sal_Int32 nIdx = 0, nCount = xGroupsIA->getCount(); (nIdx < nCount) && (aAutoName.isEmpty()); ++nIdx ) try
+ {
+ Reference< XNameAccess > xItemsNA( xGroupsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ if( xItemsNA->hasByName( aFirstItem ) )
+ {
+ xGroupName.set( xGroupsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ aAutoName = xGroupName->getName();
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "PivotCacheField::createParentGroupField" );
+ }
+ SAL_WARN_IF( aAutoName.isEmpty(), "sc", "PivotCacheField::createParentGroupField - cannot find auto-generated group name" );
+
+ // get the real group name from the list of group items
+ OUString aGroupName;
+ if( const PivotCacheItem* pGroupItem = maGroupItems.getCacheItem( nIndex ) )
+ aGroupName = pGroupItem->getName();
+ SAL_WARN_IF( aGroupName.isEmpty(), "sc", "PivotCacheField::createParentGroupField - cannot find group name" );
+ if( aGroupName.isEmpty() )
+ aGroupName = aAutoName;
+
+ if( xGroupName.is() && !aGroupName.isEmpty() )
+ {
+ // replace the auto-generated group name with the real name
+ if( aAutoName != aGroupName )
+ {
+ xGroupName->setName( aGroupName );
+ aPropSet.setProperty( PROP_GroupInfo, aGroupInfo );
+ }
+ // replace original item names in passed vector with group name
+ for( auto i : rItems )
+ if( PivotCacheGroupItem* pName = ContainerHelper::getVectorElementAccess( orItemNames, i ) )
+ pName->maGroupName = aGroupName;
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "PivotCacheField::createParentGroupField" );
+ }
+ }
+ ++nIndex;
+ }
+
+ Reference< XNamed > xFieldName( xDPGroupField, UNO_QUERY );
+ return xFieldName.is() ? xFieldName->getName() : OUString();
+}
+
+void PivotCacheField::writeSourceHeaderCell( const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ CellModel aModel;
+ aModel.maCellAddr = ScAddress( SCCOL( nCol ), SCROW( nRow ), rSheetHelper.getSheetIndex() );
+ rSheetHelper.getSheetData().setStringCell( aModel, maFieldModel.maName );
+}
+
+void PivotCacheField::writeSourceDataCell( const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem ) const
+{
+ bool bHasIndex = rItem.getType() == XML_x;
+ OSL_ENSURE( bHasIndex != maSharedItems.empty(), "PivotCacheField::writeSourceDataCell - shared items missing or not expected" );
+ if( bHasIndex )
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, rItem.getValue().get< sal_Int32 >() );
+ else
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, rItem );
+}
+
+void PivotCacheField::importPCRecordItem( SequenceInputStream& rStrm, const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ if( hasSharedItems() )
+ {
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, rStrm.readInt32() );
+ }
+ else
+ {
+ PivotCacheItem aItem;
+ if( maSharedItemsModel.mbIsNumeric )
+ aItem.readDouble( rStrm );
+ else if( maSharedItemsModel.mbHasDate && !maSharedItemsModel.mbHasString )
+ aItem.readDate( rStrm );
+ else
+ aItem.readString( rStrm );
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, aItem );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCacheField::writeItemToSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem )
+{
+ if( rItem.getType() == XML_m )
+ return;
+
+ CellModel aModel;
+ aModel.maCellAddr = ScAddress( SCCOL( nCol ), SCROW( nRow ), rSheetHelper.getSheetIndex() );
+ SheetDataBuffer& rSheetData = rSheetHelper.getSheetData();
+ switch( rItem.getType() )
+ {
+ case XML_s: rSheetData.setStringCell( aModel, rItem.getValue().get< OUString >() ); break;
+ case XML_n: rSheetData.setValueCell( aModel, rItem.getValue().get< double >() ); break;
+ case XML_i: rSheetData.setValueCell( aModel, rItem.getValue().get< sal_Int16 >() ); break;
+ case XML_d: rSheetData.setDateTimeCell( aModel, rItem.getValue().get< css::util::DateTime >() ); break;
+ case XML_b: rSheetData.setBooleanCell( aModel, rItem.getValue().get< bool >() ); break;
+ case XML_e: rSheetData.setErrorCell(aModel, rItem.getValue().get<OUString>()); break;
+ default: OSL_FAIL( "PivotCacheField::writeItemToSourceDataCell - unexpected item data type" );
+ }
+}
+
+void PivotCacheField::writeSharedItemToSourceDataCell(
+ const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nItemIdx ) const
+{
+ if( const PivotCacheItem* pCacheItem = maSharedItems.getCacheItem( nItemIdx ) )
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, *pCacheItem );
+}
+
+PCDefinitionModel::PCDefinitionModel() :
+ mfRefreshedDate( 0.0 ),
+ mnRecords( 0 ),
+ mnMissItemsLimit( 0 ),
+ mbInvalid( false ),
+ mbSaveData( true ),
+ mbRefreshOnLoad( false ),
+ mbOptimizeMemory( false ),
+ mbEnableRefresh( true ),
+ mbBackgroundQuery( false ),
+ mbUpgradeOnRefresh( false ),
+ mbTupleCache( false ),
+ mbSupportSubquery( false ),
+ mbSupportDrill( false )
+{
+}
+
+PCSourceModel::PCSourceModel() :
+ mnSourceType( XML_TOKEN_INVALID ),
+ mnConnectionId( 0 )
+{
+}
+
+PCWorksheetSourceModel::PCWorksheetSourceModel()
+{
+ maRange.SetInvalid();
+}
+
+PivotCache::PivotCache( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnCurrRow( -1 ),
+ mbValidSource( false ),
+ mbDummySheet( false )
+{
+}
+
+void PivotCache::importPivotCacheDefinition( const AttributeList& rAttribs )
+{
+ maDefModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ maDefModel.maRefreshedBy = rAttribs.getXString( XML_refreshedBy, OUString() );
+ maDefModel.mfRefreshedDate = rAttribs.getDouble( XML_refreshedDate, 0.0 );
+ maDefModel.mnRecords = rAttribs.getInteger( XML_recordCount, 0 );
+ maDefModel.mnMissItemsLimit = rAttribs.getInteger( XML_missingItemsLimit, 0 );
+ maDefModel.mbInvalid = rAttribs.getBool( XML_invalid, false );
+ maDefModel.mbSaveData = rAttribs.getBool( XML_saveData, true );
+ maDefModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maDefModel.mbOptimizeMemory = rAttribs.getBool( XML_optimizeMemory, false );
+ maDefModel.mbEnableRefresh = rAttribs.getBool( XML_enableRefresh, true );
+ maDefModel.mbBackgroundQuery = rAttribs.getBool( XML_backgroundQuery, false );
+ maDefModel.mbUpgradeOnRefresh = rAttribs.getBool( XML_upgradeOnRefresh, false );
+ maDefModel.mbTupleCache = rAttribs.getBool( XML_tupleCache, false );
+ maDefModel.mbSupportSubquery = rAttribs.getBool( XML_supportSubquery, false );
+ maDefModel.mbSupportDrill = rAttribs.getBool( XML_supportAdvancedDrill, false );
+}
+
+void PivotCache::importCacheSource( const AttributeList& rAttribs )
+{
+ maSourceModel.mnSourceType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maSourceModel.mnConnectionId = rAttribs.getInteger( XML_connectionId, 0 );
+}
+
+void PivotCache::importWorksheetSource( const AttributeList& rAttribs, const Relations& rRelations )
+{
+ maSheetSrcModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ maSheetSrcModel.maSheet = rAttribs.getXString( XML_sheet, OUString() );
+ maSheetSrcModel.maDefName = rAttribs.getXString( XML_name, OUString() );
+
+ // resolve URL of external document
+ maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId );
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ AddressConverter::convertToCellRangeUnchecked( maSheetSrcModel.maRange, rAttribs.getString( XML_ref, OUString() ), 0 );
+}
+
+void PivotCache::importPCDefinition( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags1, nFlags2;
+ rStrm.skip( 3 ); // create/refresh version id's
+ nFlags1 = rStrm.readuChar();
+ maDefModel.mnMissItemsLimit = rStrm.readInt32();
+ maDefModel.mfRefreshedDate = rStrm.readDouble();
+ nFlags2 = rStrm.readuChar();
+ maDefModel.mnRecords = rStrm.readInt32();
+ if( getFlag( nFlags2, BIFF12_PCDEFINITION_HASUSERNAME ) )
+ rStrm >> maDefModel.maRefreshedBy;
+ if( getFlag( nFlags2, BIFF12_PCDEFINITION_HASRELID ) )
+ rStrm >> maDefModel.maRelId;
+
+ maDefModel.mbInvalid = getFlag( nFlags1, BIFF12_PCDEFINITION_INVALID );
+ maDefModel.mbSaveData = getFlag( nFlags1, BIFF12_PCDEFINITION_SAVEDATA );
+ maDefModel.mbRefreshOnLoad = getFlag( nFlags1, BIFF12_PCDEFINITION_REFRESHONLOAD );
+ maDefModel.mbOptimizeMemory = getFlag( nFlags1, BIFF12_PCDEFINITION_OPTIMIZEMEMORY );
+ maDefModel.mbEnableRefresh = getFlag( nFlags1, BIFF12_PCDEFINITION_ENABLEREFRESH );
+ maDefModel.mbBackgroundQuery = getFlag( nFlags1, BIFF12_PCDEFINITION_BACKGROUNDQUERY );
+ maDefModel.mbUpgradeOnRefresh = getFlag( nFlags1, BIFF12_PCDEFINITION_UPGRADEONREFR );
+ maDefModel.mbTupleCache = getFlag( nFlags1, BIFF12_PCDEFINITION_TUPLECACHE );
+ maDefModel.mbSupportSubquery = getFlag( nFlags2, BIFF12_PCDEFINITION_SUPPORTSUBQUERY );
+ maDefModel.mbSupportDrill = getFlag( nFlags2, BIFF12_PCDEFINITION_SUPPORTDRILL );
+}
+
+void PivotCache::importPCDSource( SequenceInputStream& rStrm )
+{
+ sal_Int32 nSourceType;
+ nSourceType = rStrm.readInt32();
+ maSourceModel.mnConnectionId = rStrm.readInt32();
+ static const sal_Int32 spnSourceTypes[] = { XML_worksheet, XML_external, XML_consolidation, XML_scenario };
+ maSourceModel.mnSourceType = STATIC_ARRAY_SELECT( spnSourceTypes, nSourceType, XML_TOKEN_INVALID );
+}
+
+void PivotCache::importPCDSheetSource( SequenceInputStream& rStrm, const Relations& rRelations )
+{
+ sal_uInt8 nIsDefName, nIsBuiltinName, nFlags;
+ nIsDefName = rStrm.readuChar();
+ nIsBuiltinName = rStrm.readuChar();
+ nFlags = rStrm.readuChar();
+ if( getFlag( nFlags, BIFF12_PCDWBSOURCE_HASSHEET ) )
+ rStrm >> maSheetSrcModel.maSheet;
+ if( getFlag( nFlags, BIFF12_PCDWBSOURCE_HASRELID ) )
+ rStrm >> maSheetSrcModel.maRelId;
+
+ // read cell range or defined name
+ if( nIsDefName == 0 )
+ {
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ AddressConverter::convertToCellRangeUnchecked( maSheetSrcModel.maRange, aBinRange, 0 );
+ }
+ else
+ {
+ rStrm >> maSheetSrcModel.maDefName;
+ if( nIsBuiltinName != 0 )
+ maSheetSrcModel.maDefName = "_xlnm." + maSheetSrcModel.maDefName;
+ }
+
+ // resolve URL of external document
+ maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId );
+}
+
+PivotCacheField& PivotCache::createCacheField()
+{
+ PivotCacheFieldVector::value_type xCacheField = std::make_shared<PivotCacheField>( *this, true/*bIsDatabaseField*/ );
+ maFields.push_back( xCacheField );
+ return *xCacheField;
+}
+
+void PivotCache::finalizeImport()
+{
+ // collect all fields that are based on source data (needed to finalize source data below)
+ OSL_ENSURE( !maFields.empty(), "PivotCache::finalizeImport - no pivot cache fields found" );
+ for( PivotCacheFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt )
+ {
+ if( (*aIt)->isDatabaseField() )
+ {
+ OSL_ENSURE( (aIt == maFields.begin()) || (*(aIt - 1))->isDatabaseField(),
+ "PivotCache::finalizeImport - database field follows a calculated field" );
+ maDatabaseIndexes.push_back( static_cast< sal_Int32 >( maDatabaseFields.size() ) );
+ maDatabaseFields.push_back( *aIt );
+ }
+ else
+ {
+ maDatabaseIndexes.push_back( -1 );
+ }
+ }
+ OSL_ENSURE( !maDatabaseFields.empty(), "PivotCache::finalizeImport - no pivot cache source fields found" );
+
+ // finalize source data depending on source type
+ switch( maSourceModel.mnSourceType )
+ {
+ case XML_worksheet:
+ {
+ // decide whether an external document is used
+ bool bInternal = maTargetUrl.isEmpty() && maSheetSrcModel.maRelId.isEmpty();
+ bool bExternal = !maTargetUrl.isEmpty(); // relation ID may be empty, e.g. BIFF import
+ OSL_ENSURE( bInternal || bExternal, "PivotCache::finalizeImport - invalid external document URL" );
+ if( bInternal )
+ finalizeInternalSheetSource();
+ else if( bExternal )
+ finalizeExternalSheetSource();
+ }
+ break;
+
+ // currently, we only support worksheet data sources
+ case XML_external:
+ break;
+ case XML_consolidation:
+ break;
+ case XML_scenario:
+ break;
+ }
+}
+
+PivotCacheField* PivotCache::getCacheField( sal_Int32 nFieldIdx )
+{
+ return maFields.get( nFieldIdx ).get();
+}
+
+const PivotCacheField* PivotCache::getCacheField( sal_Int32 nFieldIdx ) const
+{
+ return maFields.get( nFieldIdx ).get();
+}
+
+sal_Int32 PivotCache::getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const
+{
+ return ContainerHelper::getVectorElement( maDatabaseIndexes, nFieldIdx, -1 );
+}
+
+void PivotCache::writeSourceHeaderCells( const WorksheetHelper& rSheetHelper ) const
+{
+ OSL_ENSURE( static_cast< size_t >( maSheetSrcModel.maRange.aEnd.Col() - maSheetSrcModel.maRange.aStart.Col() + 1 ) == maDatabaseFields.size(),
+ "PivotCache::writeSourceHeaderCells - source cell range width does not match number of source fields" );
+ SCCOL nCol = maSheetSrcModel.maRange.aStart.Col();
+ SCCOL nMaxCol = getAddressConverter().getMaxApiAddress().Col();
+ SCROW nRow = maSheetSrcModel.maRange.aStart.Row();
+ mnCurrRow = -1;
+ updateSourceDataRow( nRow );
+ for( const auto& rxDatabaseField : maDatabaseFields )
+ {
+ if (nCol > nMaxCol)
+ break;
+ rxDatabaseField->writeSourceHeaderCell( rSheetHelper, nCol, nRow );
+ ++nCol;
+ }
+}
+
+void PivotCache::writeSourceDataCell( const WorksheetHelper& rSheetHelper, sal_Int32 nColIdx, sal_Int32 nRowIdx, const PivotCacheItem& rItem ) const
+{
+ SCCOL nCol = maSheetSrcModel.maRange.aStart.Col() + nColIdx;
+ OSL_ENSURE( ( maSheetSrcModel.maRange.aStart.Col() <= nCol ) && ( nCol <= maSheetSrcModel.maRange.aEnd.Col() ), "PivotCache::writeSourceDataCell - invalid column index" );
+ SCROW nRow = maSheetSrcModel.maRange.aStart.Row() + nRowIdx;
+ OSL_ENSURE( ( maSheetSrcModel.maRange.aStart.Row() < nRow ) && ( nRow <= maSheetSrcModel.maRange.aEnd.Row() ), "PivotCache::writeSourceDataCell - invalid row index" );
+ updateSourceDataRow( nRow );
+ if( const PivotCacheField* pCacheField = maDatabaseFields.get( nColIdx ).get() )
+ pCacheField->writeSourceDataCell( rSheetHelper, nCol, nRow, rItem );
+}
+
+void PivotCache::importPCRecord( SequenceInputStream& rStrm, const WorksheetHelper& rSheetHelper, sal_Int32 nRowIdx ) const
+{
+ SCROW nRow = maSheetSrcModel.maRange.aStart.Row() + nRowIdx;
+ OSL_ENSURE( ( maSheetSrcModel.maRange.aStart.Row() < nRow ) && ( nRow <= maSheetSrcModel.maRange.aEnd.Row() ), "PivotCache::importPCRecord - invalid row index" );
+ SCCOL nCol = maSheetSrcModel.maRange.aStart.Col();
+ SCCOL nMaxCol = getAddressConverter().getMaxApiAddress().Col();
+ for( const auto& rxDatabaseField : maDatabaseFields )
+ {
+ if( rStrm.isEof() || (nCol > nMaxCol) )
+ break;
+ rxDatabaseField->importPCRecordItem( rStrm, rSheetHelper, nCol, nRow );
+ ++nCol;
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCache::finalizeInternalSheetSource()
+{
+ // resolve sheet name to sheet index
+ sal_Int16 nSheet = getWorksheets().getCalcSheetIndex( maSheetSrcModel.maSheet );
+
+ // if cache is based on a defined name or table, try to resolve to cell range
+ if( !maSheetSrcModel.maDefName.isEmpty() )
+ {
+ // local or global defined name
+ if( const DefinedName* pDefName = getDefinedNames().getByModelName( maSheetSrcModel.maDefName, nSheet ).get() )
+ {
+ mbValidSource = pDefName->getAbsoluteRange( maSheetSrcModel.maRange );
+ }
+ // table
+ else if( const Table* pTable = getTables().getTable( maSheetSrcModel.maDefName ).get() )
+ {
+ // get original range from table, but exclude the totals row(s)
+ maSheetSrcModel.maRange = pTable->getOriginalRange();
+ mbValidSource = (pTable->getHeight() - pTable->getTotalsRows()) > 1;
+ if( mbValidSource )
+ maSheetSrcModel.maRange.aEnd.SetRow( maSheetSrcModel.maRange.aEnd.Row() - pTable->getTotalsRows() );
+ }
+ }
+ // else try the cell range (if the sheet exists)
+ else if( nSheet >= 0 )
+ {
+ // insert sheet index into the range, range address will be checked below
+ maSheetSrcModel.maRange.aStart.SetTab( nSheet );
+ mbValidSource = true;
+ }
+ // else sheet has been deleted, generate the source data from cache
+ else if( !maSheetSrcModel.maSheet.isEmpty() )
+ {
+ prepareSourceDataSheet();
+ // return here to skip the source range check below
+ return;
+ }
+
+ // check range location, do not allow ranges that overflow the sheet partly
+ mbValidSource = mbValidSource &&
+ getAddressConverter().checkCellRange( maSheetSrcModel.maRange, false, true ) &&
+ ( maSheetSrcModel.maRange.aStart.Row() < maSheetSrcModel.maRange.aEnd.Row() );
+}
+
+void PivotCache::finalizeExternalSheetSource()
+{
+ /* If pivot cache is based on external sheet data, try to restore sheet
+ data from cache records. No support for external defined names or tables,
+ sheet name and path to cache records fragment (OOXML only) are required. */
+ bool bHasRelation = !maDefModel.maRelId.isEmpty();
+ if( bHasRelation && maSheetSrcModel.maDefName.isEmpty() && !maSheetSrcModel.maSheet.isEmpty() )
+ prepareSourceDataSheet();
+}
+
+void PivotCache::prepareSourceDataSheet()
+{
+ ScRange& rRange = maSheetSrcModel.maRange;
+ // data will be inserted in top-left cell, sheet index is still set to 0 (will be set below)
+ rRange.aEnd.SetCol( rRange.aEnd.Col() - rRange.aStart.Col() );
+ rRange.aStart.SetCol( 0 );
+ rRange.aEnd.SetRow( rRange.aEnd.Row() - rRange.aStart.Row() );
+ rRange.aStart.SetRow( 0 );
+ // check range location, do not allow ranges that overflow the sheet partly
+ if( getAddressConverter().checkCellRange( rRange, false, true ) )
+ {
+ maColSpans.insert( ValueRange( rRange.aStart.Col(), rRange.aEnd.Col() ) );
+ OUString aSheetName = "DPCache_" + maSheetSrcModel.maSheet;
+ rRange.aStart.SetTab( getWorksheets().insertEmptySheet( aSheetName ) );
+ mbValidSource = mbDummySheet = rRange.aStart.Tab() >= 0;
+ }
+}
+
+void PivotCache::updateSourceDataRow( sal_Int32 nRow ) const
+{
+ if( mnCurrRow != nRow )
+ {
+ mnCurrRow = nRow;
+ }
+}
+
+PivotCacheBuffer::PivotCacheBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PivotCacheBuffer::registerPivotCacheFragment( sal_Int32 nCacheId, const OUString& rFragmentPath )
+{
+ OSL_ENSURE( nCacheId >= 0, "PivotCacheBuffer::registerPivotCacheFragment - invalid pivot cache identifier" );
+ OSL_ENSURE( maFragmentPaths.count( nCacheId ) == 0, "PivotCacheBuffer::registerPivotCacheFragment - fragment path exists already" );
+ if( (nCacheId >= 0) && !rFragmentPath.isEmpty() )
+ maFragmentPaths[ nCacheId ] = rFragmentPath;
+}
+
+PivotCache* PivotCacheBuffer::importPivotCacheFragment( sal_Int32 nCacheId )
+{
+ /* OOXML/BIFF12 filter: On first call for the cache ID, the pivot
+ cache object is created and inserted into maCaches. Then, the cache
+ definition fragment is read and the cache is returned. On
+ subsequent calls, the created cache will be found in maCaches and
+ returned immediately. */
+ // try to find an imported pivot cache
+ if( PivotCache* pCache = maCaches.get( nCacheId ).get() )
+ return pCache;
+
+ // check if a fragment path exists for the passed cache identifier
+ FragmentPathMap::iterator aIt = maFragmentPaths.find( nCacheId );
+ if( aIt == maFragmentPaths.end() )
+ return nullptr;
+
+ /* Import the cache fragment. This may create a dummy data sheet
+ for external sheet sources. */
+ PivotCache& rCache = createPivotCache( nCacheId );
+ importOoxFragment( new PivotCacheDefinitionFragment( *this, aIt->second, rCache ) );
+ return &rCache;
+}
+
+PivotCache& PivotCacheBuffer::createPivotCache( sal_Int32 nCacheId )
+{
+ maCacheIds.push_back( nCacheId );
+ PivotCacheMap::mapped_type& rxCache = maCaches[ nCacheId ];
+ rxCache = std::make_shared<PivotCache>( *this );
+ return *rxCache;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivotcachefragment.cxx b/sc/source/filter/oox/pivotcachefragment.cxx
new file mode 100644
index 0000000000..f508c36cf8
--- /dev/null
+++ b/sc/source/filter/oox/pivotcachefragment.cxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivotcachefragment.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/token/namespaces.hxx>
+#include <biffhelper.hxx>
+#include <formulabuffer.hxx>
+#include <pivotcachebuffer.hxx>
+#include <worksheetbuffer.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+PivotCacheFieldContext::PivotCacheFieldContext( WorkbookFragmentBase& rFragment, PivotCacheField& rCacheField ) :
+ WorkbookContextBase( rFragment ),
+ mrCacheField( rCacheField )
+{
+}
+
+ContextHandlerRef PivotCacheFieldContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cacheField ):
+ if( nElement == XLS_TOKEN( sharedItems ) ) { mrCacheField.importSharedItems( rAttribs ); return this; }
+ if( nElement == XLS_TOKEN( fieldGroup ) ) { mrCacheField.importFieldGroup( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( fieldGroup ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( rangePr ): mrCacheField.importRangePr( rAttribs ); break;
+ case XLS_TOKEN( discretePr ): return this;
+ case XLS_TOKEN( groupItems ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( sharedItems ): mrCacheField.importSharedItem( nElement, rAttribs ); break;
+ case XLS_TOKEN( discretePr ): mrCacheField.importDiscretePrItem( nElement, rAttribs ); break;
+ case XLS_TOKEN( groupItems ): mrCacheField.importGroupItem( nElement, rAttribs ); break;
+ }
+ return nullptr;
+}
+
+void PivotCacheFieldContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrCacheField.importCacheField( rAttribs );
+}
+
+ContextHandlerRef PivotCacheFieldContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_PCDFIELD:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItems( rStrm ); return this;
+ case BIFF12_ID_PCDFIELDGROUP: mrCacheField.importPCDFieldGroup( rStrm ); return this;
+ }
+ break;
+
+ case BIFF12_ID_PCDFIELDGROUP:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCDFRANGEPR: mrCacheField.importPCDFRangePr( rStrm ); break;
+ case BIFF12_ID_PCDFDISCRETEPR: return this;
+ case BIFF12_ID_PCDFGROUPITEMS: return this;
+ }
+ break;
+
+ case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItem( nRecId, rStrm ); break;
+ case BIFF12_ID_PCDFDISCRETEPR: mrCacheField.importPCDFDiscretePrItem( nRecId, rStrm ); break;
+ case BIFF12_ID_PCDFGROUPITEMS: mrCacheField.importPCDFGroupItem( nRecId, rStrm ); break;
+ }
+ return nullptr;
+}
+
+void PivotCacheFieldContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrCacheField.importPCDField( rStrm );
+}
+
+PivotCacheDefinitionFragment::PivotCacheDefinitionFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, PivotCache& rPivotCache ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath ),
+ mrPivotCache( rPivotCache )
+{
+}
+
+ContextHandlerRef PivotCacheDefinitionFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( pivotCacheDefinition ) ) { mrPivotCache.importPivotCacheDefinition( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( pivotCacheDefinition ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( cacheSource ): mrPivotCache.importCacheSource( rAttribs ); return this;
+ case XLS_TOKEN( cacheFields ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( cacheSource ):
+ if( nElement == XLS_TOKEN( worksheetSource ) ) mrPivotCache.importWorksheetSource( rAttribs, getRelations() );
+ break;
+
+ case XLS_TOKEN( cacheFields ):
+ if( nElement == XLS_TOKEN( cacheField ) ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef PivotCacheDefinitionFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_PCDEFINITION ) { mrPivotCache.importPCDefinition( rStrm ); return this; }
+ break;
+
+ case BIFF12_ID_PCDEFINITION:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCDSOURCE: mrPivotCache.importPCDSource( rStrm ); return this;
+ case BIFF12_ID_PCDFIELDS: return this;
+ }
+ break;
+
+ case BIFF12_ID_PCDSOURCE:
+ if( nRecId == BIFF12_ID_PCDSHEETSOURCE ) mrPivotCache.importPCDSheetSource( rStrm, getRelations() );
+ break;
+
+ case BIFF12_ID_PCDFIELDS:
+ if( nRecId == BIFF12_ID_PCDFIELD ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* PivotCacheDefinitionFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_PCDEFINITION, BIFF12_ID_PCDEFINITION + 1 },
+ { BIFF12_ID_PCDFDISCRETEPR, BIFF12_ID_PCDFDISCRETEPR + 1 },
+ { BIFF12_ID_PCDFGROUPITEMS, BIFF12_ID_PCDFGROUPITEMS + 1 },
+ { BIFF12_ID_PCDFIELD, BIFF12_ID_PCDFIELD + 1 },
+ { BIFF12_ID_PCDFIELDGROUP, BIFF12_ID_PCDFIELDGROUP + 1 },
+ { BIFF12_ID_PCDFIELDS, BIFF12_ID_PCDFIELDS + 1 },
+ { BIFF12_ID_PCDFRANGEPR, BIFF12_ID_PCDFRANGEPR + 1 },
+ { BIFF12_ID_PCDFSHAREDITEMS, BIFF12_ID_PCDFSHAREDITEMS + 1 },
+ { BIFF12_ID_PCITEM_ARRAY, BIFF12_ID_PCITEM_ARRAY + 1 },
+ { BIFF12_ID_PCDSHEETSOURCE, BIFF12_ID_PCDSHEETSOURCE + 1 },
+ { BIFF12_ID_PCDSOURCE, BIFF12_ID_PCDSOURCE + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void PivotCacheDefinitionFragment::finalizeImport()
+{
+ // finalize the cache (check source range etc.)
+ mrPivotCache.finalizeImport();
+
+ // load the cache records, if the cache is based on a deleted or an external worksheet
+ if( mrPivotCache.isValidDataSource() && mrPivotCache.isBasedOnDummySheet() )
+ {
+ OUString aRecFragmentPath = getRelations().getFragmentPathFromRelId( mrPivotCache.getRecordsRelId() );
+ if( !aRecFragmentPath.isEmpty() )
+ {
+ SCTAB nSheet = mrPivotCache.getSourceRange().aStart.Tab();
+ WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), WorksheetType::Work, nSheet );
+ if( xSheetGlob )
+ importOoxFragment( new PivotCacheRecordsFragment( *xSheetGlob, aRecFragmentPath, mrPivotCache ) );
+ }
+ }
+}
+
+PivotCacheRecordsFragment::PivotCacheRecordsFragment( const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath, const PivotCache& rPivotCache ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrPivotCache( rPivotCache ),
+ mnColIdx( 0 ),
+ mnRowIdx( 0 ),
+ mbInRecord( false )
+{
+ sal_Int32 nSheetCount = rPivotCache.getWorksheets().getAllSheetCount();
+
+ // prepare sheet: insert column header names into top row
+ rPivotCache.writeSourceHeaderCells( *this );
+ // resize formula buffers since we've added a new dummy sheet
+ rHelper.getFormulaBuffer().SetSheetCount( nSheetCount );
+}
+
+ContextHandlerRef PivotCacheRecordsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( pivotCacheRecords ) ) return this;
+ break;
+
+ case XLS_TOKEN( pivotCacheRecords ):
+ if( nElement == XLS_TOKEN( r ) ) { startCacheRecord(); return this; }
+ break;
+
+ case XLS_TOKEN( r ):
+ {
+ PivotCacheItem aItem;
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): aItem.readString( rAttribs ); break;
+ case XLS_TOKEN( n ): aItem.readNumeric( rAttribs ); break;
+ case XLS_TOKEN( d ): aItem.readDate( rAttribs ); break;
+ case XLS_TOKEN( b ): aItem.readBool( rAttribs ); break;
+ case XLS_TOKEN( e ): aItem.readError( rAttribs ); break;
+ case XLS_TOKEN( x ): aItem.readIndex( rAttribs ); break;
+ default: OSL_FAIL( "OoxPivotCacheRecordsFragment::onCreateContext - unexpected element" );
+ }
+ mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem );
+ ++mnColIdx;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef PivotCacheRecordsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_PCRECORDS ) return this;
+ break;
+
+ case BIFF12_ID_PCRECORDS:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCRECORD: importPCRecord( rStrm ); break;
+ case BIFF12_ID_PCRECORDDT: startCacheRecord(); break;
+ default: importPCRecordItem( nRecId, rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* PivotCacheRecordsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_PCRECORDS, BIFF12_ID_PCRECORDS + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCacheRecordsFragment::startCacheRecord()
+{
+ mnColIdx = 0;
+ ++mnRowIdx;
+ mbInRecord = true;
+}
+
+void PivotCacheRecordsFragment::importPCRecord( SequenceInputStream& rStrm )
+{
+ startCacheRecord();
+ mrPivotCache.importPCRecord( rStrm, *this, mnRowIdx );
+ mbInRecord = false;
+}
+
+void PivotCacheRecordsFragment::importPCRecordItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( !mbInRecord )
+ return;
+
+ PivotCacheItem aItem;
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCITEM_MISSING: break;
+ case BIFF12_ID_PCITEM_STRING: aItem.readString( rStrm ); break;
+ case BIFF12_ID_PCITEM_DOUBLE: aItem.readDouble( rStrm ); break;
+ case BIFF12_ID_PCITEM_DATE: aItem.readDate( rStrm ); break;
+ case BIFF12_ID_PCITEM_BOOL: aItem.readBool( rStrm ); break;
+ case BIFF12_ID_PCITEM_ERROR: aItem.readError( rStrm, getUnitConverter() ); break;
+ case BIFF12_ID_PCITEM_INDEX: aItem.readIndex( rStrm ); break;
+ default: OSL_FAIL( "OoxPivotCacheRecordsFragment::importPCRecordItem - unexpected record" );
+ }
+ mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem );
+ ++mnColIdx;
+}
+
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivottablebuffer.cxx b/sc/source/filter/oox/pivottablebuffer.cxx
new file mode 100644
index 0000000000..252b4773cc
--- /dev/null
+++ b/sc/source/filter/oox/pivottablebuffer.cxx
@@ -0,0 +1,1493 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivottablebuffer.hxx>
+
+#include <set>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sheet/CellFlags.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/GeneralFunction.hpp>
+#include <com/sun/star/sheet/XDataPilotDataLayoutFieldSupplier.hpp>
+#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp>
+#include <com/sun/star/sheet/XSheetOperation.hpp>
+#include <com/sun/star/xml/sax/XFastAttributeList.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+#include <dapiuno.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <workbooksettings.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_Int32 OOX_PT_DATALAYOUTFIELD = -2; /// Placeholder index of data layout field.
+
+const sal_Int32 OOX_PT_PREVIOUS_ITEM = 0x001000FC; /// Calculation of data item result is based on previous item.
+const sal_Int32 OOX_PT_NEXT_ITEM = 0x001000FD; /// Calculation of data item result is based on next item.
+
+const sal_uInt32 BIFF12_PTFIELD_DATAFIELD = 0x00000008;
+const sal_uInt32 BIFF12_PTFIELD_DEFAULT = 0x00000100;
+const sal_uInt32 BIFF12_PTFIELD_SUM = 0x00000200;
+const sal_uInt32 BIFF12_PTFIELD_COUNTA = 0x00000400;
+const sal_uInt32 BIFF12_PTFIELD_AVERAGE = 0x00000800;
+const sal_uInt32 BIFF12_PTFIELD_MAX = 0x00001000;
+const sal_uInt32 BIFF12_PTFIELD_MIN = 0x00002000;
+const sal_uInt32 BIFF12_PTFIELD_PRODUCT = 0x00004000;
+const sal_uInt32 BIFF12_PTFIELD_COUNT = 0x00008000;
+const sal_uInt32 BIFF12_PTFIELD_STDDEV = 0x00010000;
+const sal_uInt32 BIFF12_PTFIELD_STDDEVP = 0x00020000;
+const sal_uInt32 BIFF12_PTFIELD_VAR = 0x00040000;
+const sal_uInt32 BIFF12_PTFIELD_VARP = 0x00080000;
+
+const sal_uInt32 BIFF12_PTFIELD_SHOWALL = 0x00000020;
+const sal_uInt32 BIFF12_PTFIELD_OUTLINE = 0x00000040;
+const sal_uInt32 BIFF12_PTFIELD_INSERTBLANKROW = 0x00000080;
+const sal_uInt32 BIFF12_PTFIELD_SUBTOTALTOP = 0x00000100;
+const sal_uInt32 BIFF12_PTFIELD_INSERTPAGEBREAK = 0x00000800;
+const sal_uInt32 BIFF12_PTFIELD_AUTOSORT = 0x00001000;
+const sal_uInt32 BIFF12_PTFIELD_SORTASCENDING = 0x00002000;
+const sal_uInt32 BIFF12_PTFIELD_AUTOSHOW = 0x00004000;
+const sal_uInt32 BIFF12_PTFIELD_AUTOSHOWTOP = 0x00008000;
+const sal_uInt32 BIFF12_PTFIELD_MULTIPAGEITEMS = 0x00080000;
+
+const sal_uInt16 BIFF12_PTFITEM_HIDDEN = 0x0001;
+const sal_uInt16 BIFF12_PTFITEM_HIDEDETAILS = 0x0002;
+
+const sal_uInt8 BIFF12_PTPAGEFIELD_HASNAME = 0x01;
+const sal_Int32 BIFF12_PTPAGEFIELD_MULTIITEMS = 0x001000FE;
+
+const sal_uInt16 BIFF12_PTFILTER_HASNAME = 0x0001;
+const sal_uInt16 BIFF12_PTFILTER_HASDESCRIPTION = 0x0002;
+const sal_uInt16 BIFF12_PTFILTER_HASSTRVALUE1 = 0x0004;
+const sal_uInt16 BIFF12_PTFILTER_HASSTRVALUE2 = 0x0008;
+
+const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01;
+const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02;
+
+const sal_uInt32 BIFF12_PTDEF_SHOWITEMS = 0x00000100;
+const sal_uInt32 BIFF12_PTDEF_DISABLEFIELDLIST = 0x00000400;
+const sal_uInt32 BIFF12_PTDEF_HIDECALCMEMBERS = 0x00001000;
+const sal_uInt32 BIFF12_PTDEF_WITHHIDDENTOTALS = 0x00002000;
+const sal_uInt32 BIFF12_PTDEF_HIDEDRILL = 0x00100000;
+const sal_uInt32 BIFF12_PTDEF_PRINTDRILL = 0x00200000;
+const sal_uInt32 BIFF12_PTDEF_HIDEHEADERS = 0x80000000;
+
+const sal_uInt32 BIFF12_PTDEF_SHOWEMPTYROW = 0x00000004;
+const sal_uInt32 BIFF12_PTDEF_SHOWEMPTYCOL = 0x00000008;
+const sal_uInt32 BIFF12_PTDEF_ENABLEDRILL = 0x00000020;
+const sal_uInt32 BIFF12_PTDEF_PRESERVEFORMATTING = 0x00000080;
+const sal_uInt32 BIFF12_PTDEF_USEAUTOFORMAT = 0x00000100;
+const sal_uInt32 BIFF12_PTDEF_SHOWERROR = 0x00000200;
+const sal_uInt32 BIFF12_PTDEF_SHOWMISSING = 0x00000400;
+const sal_uInt32 BIFF12_PTDEF_PAGEOVERTHENDOWN = 0x00000800;
+const sal_uInt32 BIFF12_PTDEF_SUBTOTALHIDDENITEMS = 0x00001000;
+const sal_uInt32 BIFF12_PTDEF_ROWGRANDTOTALS = 0x00002000;
+const sal_uInt32 BIFF12_PTDEF_COLGRANDTOTALS = 0x00004000;
+const sal_uInt32 BIFF12_PTDEF_FIELDPRINTTITLES = 0x00008000;
+const sal_uInt32 BIFF12_PTDEF_ITEMPRINTTITLES = 0x00020000;
+const sal_uInt32 BIFF12_PTDEF_MERGEITEM = 0x00040000;
+const sal_uInt32 BIFF12_PTDEF_HASDATACAPTION = 0x00080000;
+const sal_uInt32 BIFF12_PTDEF_HASGRANDTOTALCAPTION = 0x00100000;
+const sal_uInt32 BIFF12_PTDEF_HASPAGESTYLE = 0x00200000;
+const sal_uInt32 BIFF12_PTDEF_HASPIVOTTABLESTYLE = 0x00400000;
+const sal_uInt32 BIFF12_PTDEF_HASVACATEDSTYLE = 0x00800000;
+const sal_uInt32 BIFF12_PTDEF_APPLYNUMFMT = 0x01000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYFONT = 0x02000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYALIGNMENT = 0x04000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYBORDER = 0x08000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYFILL = 0x10000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYPROTECTION = 0x20000000;
+const sal_uInt32 BIFF12_PTDEF_HASTAG = 0x40000000;
+
+const sal_uInt32 BIFF12_PTDEF_NOERRORCAPTION = 0x00000040;
+const sal_uInt32 BIFF12_PTDEF_NOMISSINGCAPTION = 0x00000080;
+const sal_uInt32 BIFF12_PTDEF_HASROWHEADERCAPTION = 0x00000400;
+const sal_uInt32 BIFF12_PTDEF_HASCOLHEADERCAPTION = 0x00000800;
+const sal_uInt32 BIFF12_PTDEF_FIELDLISTSORTASC = 0x00001000;
+const sal_uInt32 BIFF12_PTDEF_NOCUSTOMLISTSORT = 0x00004000;
+
+const sal_uInt8 BIFF12_PTDEF_ROWAXIS = 1;
+const sal_uInt8 BIFF12_PTDEF_COLAXIS = 2;
+
+} // namespace
+
+PTFieldItemModel::PTFieldItemModel() :
+ mnCacheItem( -1 ),
+ mnType( XML_data ),
+ mbShowDetails( true ),
+ mbHidden( false )
+{
+}
+
+void PTFieldItemModel::setBiffType( sal_uInt16 nType )
+{
+ static const sal_Int32 spnTypes[] = { XML_data, XML_default,
+ XML_sum, XML_countA, XML_avg, XML_max, XML_min, XML_product, XML_count,
+ XML_stdDev, XML_stdDevP, XML_var, XML_varP, XML_grand, XML_blank };
+ mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_data );
+}
+
+PTFieldModel::PTFieldModel() :
+ mnAxis( XML_TOKEN_INVALID ),
+ mnNumFmtId( 0 ),
+ mnAutoShowItems( 10 ),
+ mnAutoShowRankBy( -1 ),
+ mnSortType( XML_manual ),
+ mnSortRefField( -1 ),
+ mnSortRefItem( -1 ),
+ mbDataField( false ),
+ mbDefaultSubtotal( true ),
+ mbSumSubtotal( false ),
+ mbCountASubtotal( false ),
+ mbAverageSubtotal( false ),
+ mbMaxSubtotal( false ),
+ mbMinSubtotal( false ),
+ mbProductSubtotal( false ),
+ mbCountSubtotal( false ),
+ mbStdDevSubtotal( false ),
+ mbStdDevPSubtotal( false ),
+ mbVarSubtotal( false ),
+ mbVarPSubtotal( false ),
+ mbShowAll( true ),
+ mbOutline( true ),
+ mbSubtotalTop( true ),
+ mbCompact( false ),
+ mbInsertBlankRow( false ),
+ mbInsertPageBreak( false ),
+ mbAutoShow( false ),
+ mbTopAutoShow( true ),
+ mbMultiPageItems( false )
+{
+}
+
+void PTFieldModel::setBiffAxis( sal_uInt8 nAxis )
+{
+ /* Weird. The axis field is organized as bit field, but only one of the
+ row/col/page flags are allowed at the same time and refer to the values
+ 'axisRow', 'axisCol', and 'axisPage' of the XML attribute
+ 'pivotField@axis'. Additionally, the fourth bit determines if the field
+ is a data field, which may appear combined with the row/col/page flags.
+ Therefore, this bit is unrelated to the 'axisValues' value of the
+ 'pivotField@axis' attribute, but refers to the 'pivotField@dataField'
+ boolean attribute. */
+ static const sal_Int32 spnAxisIds[] = { XML_TOKEN_INVALID, XML_axisRow, XML_axisCol, XML_TOKEN_INVALID, XML_axisPage };
+ mnAxis = STATIC_ARRAY_SELECT( spnAxisIds, nAxis, XML_TOKEN_INVALID );
+}
+
+PTPageFieldModel::PTPageFieldModel() :
+ mnField( -1 ),
+ mnItem( BIFF12_PTPAGEFIELD_MULTIITEMS )
+{
+}
+
+PTDataFieldModel::PTDataFieldModel() :
+ mnField( -1 ),
+ mnSubtotal( XML_sum ),
+ mnShowDataAs( XML_normal ),
+ mnBaseField( -1 ),
+ mnBaseItem( -1 ),
+ mnNumFmtId( 0 )
+{
+}
+
+void PTDataFieldModel::setBiffSubtotal( sal_Int32 nSubtotal )
+{
+ static const sal_Int32 spnSubtotals[] = { XML_sum, XML_count, XML_average, XML_max, XML_min, XML_product, XML_countNums, XML_stdDev, XML_stdDevp, XML_var, XML_varp };
+ mnSubtotal = STATIC_ARRAY_SELECT( spnSubtotals, nSubtotal, XML_TOKEN_INVALID );
+}
+
+void PTDataFieldModel::setBiffShowDataAs( sal_Int32 nShowDataAs )
+{
+ static const sal_Int32 spnShowDataAs[] = { XML_normal, XML_difference, XML_percent, XML_percentDiff, XML_runTotal, XML_percentOfRow, XML_percentOfCol, XML_percentOfTotal, XML_index };
+ mnShowDataAs = STATIC_ARRAY_SELECT( spnShowDataAs, nShowDataAs, XML_TOKEN_INVALID );
+}
+
+PivotTableField::PivotTableField( PivotTable& rPivotTable, sal_Int32 nFieldIndex ) :
+ WorkbookHelper( rPivotTable ),
+ mrPivotTable( rPivotTable ),
+ mnFieldIndex( nFieldIndex )
+{
+}
+
+void PivotTableField::importPivotField( const AttributeList& rAttribs )
+{
+ /* The documentation mentions a value 'axisValues' for the attribute
+ 'pivotField@axis'. But this value is not used to mark a data field, as
+ data fields may be inserted in one of the row/column/page dimensions at
+ the same time. Therefore, the boolean attribute 'pivotField@dataField'
+ is really used to mark data fields. */
+ maModel.mnAxis = rAttribs.getToken( XML_axis, XML_TOKEN_INVALID );
+ maModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maModel.mnAutoShowItems = rAttribs.getInteger( XML_itemPageCount, 10 );
+ maModel.mnAutoShowRankBy = rAttribs.getInteger( XML_rankBy, -1 );
+ maModel.mnSortType = rAttribs.getToken( XML_sortType, XML_manual );
+ maModel.mbDataField = rAttribs.getBool( XML_dataField, false );
+ maModel.mbDefaultSubtotal = rAttribs.getBool( XML_defaultSubtotal, true );
+ maModel.mbSumSubtotal = rAttribs.getBool( XML_sumSubtotal, false );
+ maModel.mbCountASubtotal = rAttribs.getBool( XML_countASubtotal, false );
+ maModel.mbAverageSubtotal = rAttribs.getBool( XML_avgSubtotal, false );
+ maModel.mbMaxSubtotal = rAttribs.getBool( XML_maxSubtotal, false );
+ maModel.mbMinSubtotal = rAttribs.getBool( XML_minSubtotal, false );
+ maModel.mbProductSubtotal = rAttribs.getBool( XML_productSubtotal, false );
+ maModel.mbCountSubtotal = rAttribs.getBool( XML_countSubtotal, false );
+ maModel.mbStdDevSubtotal = rAttribs.getBool( XML_stdDevSubtotal, false );
+ maModel.mbStdDevPSubtotal = rAttribs.getBool( XML_stdDevPSubtotal, false );
+ maModel.mbVarSubtotal = rAttribs.getBool( XML_varSubtotal, false );
+ maModel.mbVarPSubtotal = rAttribs.getBool( XML_varPSubtotal, false );
+ maModel.mbShowAll = rAttribs.getBool( XML_showAll, true );
+ maModel.mbOutline = rAttribs.getBool( XML_outline, true );
+ maModel.mbSubtotalTop = rAttribs.getBool( XML_subtotalTop, true );
+ maModel.mbCompact = maModel.mbSubtotalTop && maModel.mbOutline && rAttribs.getBool( XML_compact, true );
+ maModel.mbInsertBlankRow = rAttribs.getBool( XML_insertBlankRow, false );
+ maModel.mbInsertPageBreak = rAttribs.getBool( XML_insertPageBreak, false );
+ maModel.mbAutoShow = rAttribs.getBool( XML_autoShow, false );
+ maModel.mbTopAutoShow = rAttribs.getBool( XML_topAutoShow, true );
+ maModel.mbMultiPageItems = rAttribs.getBool( XML_multipleItemSelectionAllowed, false );
+}
+
+void PivotTableField::importItem( const AttributeList& rAttribs )
+{
+ PTFieldItemModel aModel;
+ aModel.mnCacheItem = rAttribs.getInteger( XML_x, -1 );
+ aModel.mnType = rAttribs.getToken( XML_t, XML_data );
+ aModel.mbShowDetails = rAttribs.getBool( XML_sd, true );
+ aModel.mbHidden = rAttribs.getBool( XML_h, false );
+ aModel.msCaption = rAttribs.getXString( XML_n, OUString() );
+ maItems.push_back( aModel );
+}
+
+void PivotTableField::importReference( const AttributeList& rAttribs )
+{
+ // field index is stored as unsigned integer
+ maModel.mnSortRefField = static_cast< sal_Int32 >( rAttribs.getUnsigned( XML_field, SAL_MAX_UINT32 ) );
+}
+
+void PivotTableField::importReferenceItem( const AttributeList& rAttribs )
+{
+ maModel.mnSortRefItem = rAttribs.getInteger( XML_v, -1 );
+}
+
+void PivotTableField::importPTField( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags1, nFlags2;
+ nFlags1 = rStrm.readuInt32();
+ maModel.mnNumFmtId = rStrm.readInt32();
+ nFlags2 = rStrm.readuInt32();
+ maModel.mnAutoShowItems = rStrm.readInt32();
+ maModel.mnAutoShowRankBy = rStrm.readInt32();
+
+ maModel.setBiffAxis( extractValue< sal_uInt8 >( nFlags1, 0, 3 ) );
+ maModel.mbDataField = getFlag( nFlags1, BIFF12_PTFIELD_DATAFIELD );
+ maModel.mbDefaultSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_DEFAULT );
+ maModel.mbSumSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_SUM );
+ maModel.mbCountASubtotal = getFlag( nFlags1, BIFF12_PTFIELD_COUNTA );
+ maModel.mbAverageSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_AVERAGE );
+ maModel.mbMaxSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_MAX );
+ maModel.mbMinSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_MIN );
+ maModel.mbProductSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_PRODUCT );
+ maModel.mbCountSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_COUNT );
+ maModel.mbStdDevSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_STDDEV );
+ maModel.mbStdDevPSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_STDDEVP );
+ maModel.mbVarSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_VAR );
+ maModel.mbVarPSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_VARP );
+
+ maModel.mbShowAll = getFlag( nFlags2, BIFF12_PTFIELD_SHOWALL );
+ maModel.mbOutline = getFlag( nFlags2, BIFF12_PTFIELD_OUTLINE );
+ maModel.mbSubtotalTop = getFlag( nFlags2, BIFF12_PTFIELD_SUBTOTALTOP );
+ maModel.mbInsertBlankRow = getFlag( nFlags2, BIFF12_PTFIELD_INSERTBLANKROW );
+ maModel.mbInsertPageBreak = getFlag( nFlags2, BIFF12_PTFIELD_INSERTPAGEBREAK );
+ maModel.mbAutoShow = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSHOW );
+ maModel.mbTopAutoShow = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSHOWTOP );
+ maModel.mbMultiPageItems = getFlag( nFlags2, BIFF12_PTFIELD_MULTIPAGEITEMS );
+
+ bool bAutoSort = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSORT );
+ bool bAscending = getFlag( nFlags2, BIFF12_PTFIELD_SORTASCENDING );
+ maModel.mnSortType = bAutoSort ? (bAscending ? XML_ascending : XML_descending) : XML_manual;
+}
+
+void PivotTableField::importPTFItem( SequenceInputStream& rStrm )
+{
+ PTFieldItemModel aModel;
+ sal_uInt8 nType;
+ sal_uInt16 nFlags;
+ nType = rStrm.readuChar();
+ nFlags = rStrm.readuInt16();
+ aModel.mnCacheItem = rStrm.readInt32();
+
+ aModel.setBiffType( nType );
+ aModel.mbShowDetails = !getFlag( nFlags, BIFF12_PTFITEM_HIDEDETAILS );
+ aModel.mbHidden = getFlag( nFlags, BIFF12_PTFITEM_HIDDEN );
+
+ maItems.push_back( aModel );
+}
+
+void PivotTableField::importPTReference( SequenceInputStream& rStrm )
+{
+ maModel.mnSortRefField = rStrm.readInt32();
+}
+
+void PivotTableField::importPTReferenceItem( SequenceInputStream& rStrm )
+{
+ maModel.mnSortRefItem = rStrm.readInt32();
+}
+
+void PivotTableField::finalizeImport( const Reference< XDataPilotDescriptor >& rxDPDesc )
+{
+ /* Process all fields based on source data, other fields (e.g. group
+ fields) are processed from here. PivotCache::getCacheDatabaseIndex()
+ returns -1 for all fields not based on source data. */
+ Reference< XDataPilotField > xDPField;
+ sal_Int32 nDatabaseIdx = mrPivotTable.getCacheDatabaseIndex( mnFieldIndex );
+ if( !((nDatabaseIdx >= 0) && rxDPDesc.is()) )
+ return;
+
+ try
+ {
+ // try to get the source field and its name from passed DataPilot descriptor
+ Reference< XIndexAccess > xDPFieldsIA( rxDPDesc->getDataPilotFields(), UNO_SET_THROW );
+ xDPField.set( xDPFieldsIA->getByIndex( nDatabaseIdx ), UNO_QUERY_THROW );
+ Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW );
+ maDPFieldName = xDPFieldName->getName();
+ OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeImport - no field name in source data found" );
+
+ // try to convert grouping settings
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ // numeric grouping is done inplace, no nested group fields will appear
+ if( pCacheField->hasNumericGrouping() )
+ {
+ pCacheField->convertNumericGrouping( xDPField );
+ }
+ else if( pCacheField->hasDateGrouping() )
+ {
+ // first date group settings are inplace
+ pCacheField->createDateGroupField( xDPField );
+ // create all nested group fields (if any)
+ mrPivotTable.finalizeDateGroupingImport( xDPField, mnFieldIndex );
+ }
+ else if( pCacheField->hasParentGrouping() )
+ {
+
+ // create a list of all item names, needed to map between original and group items
+ ::std::vector< OUString > aItems;
+ pCacheField->getCacheItemNames( aItems );
+ PivotCacheGroupItemVector aItemNames;
+ for( const auto& rItem : aItems )
+ aItemNames.emplace_back( rItem );
+ // create all nested group fields (if any)
+ mrPivotTable.finalizeParentGroupingImport( xDPField, *pCacheField, aItemNames );
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void PivotTableField::finalizeDateGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, sal_Int32 nBaseFieldIdx )
+{
+ if( maDPFieldName.isEmpty() ) // prevent endless loops if file format is broken
+ {
+ if( PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ if( !pCacheField->isDatabaseField() && pCacheField->hasDateGrouping() && (pCacheField->getGroupBaseField() == nBaseFieldIdx) )
+ {
+ maDPFieldName = pCacheField->createDateGroupField( rxBaseDPField );
+ pCacheField->setFinalGroupName(maDPFieldName);
+ OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeDateGroupingImport - cannot create date group field" );
+ }
+ }
+ }
+}
+
+void PivotTableField::finalizeParentGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames )
+{
+ if( !maDPFieldName.isEmpty() ) // prevent endless loops if file format is broken
+ return;
+
+ PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex );
+ if( !pCacheField )
+ return;
+
+ // data field can have user defined groupname captions, apply them
+ // if they do
+ IdCaptionPairList captionList;
+ for( const auto& rItem : maItems )
+ {
+ if ( rItem.mnType == XML_data && rItem.msCaption.getLength() )
+ captionList.emplace_back( rItem.mnCacheItem, rItem.msCaption );
+ }
+ if ( !captionList.empty() )
+ pCacheField->applyItemCaptions( captionList );
+
+ maDPFieldName = pCacheField->createParentGroupField( rxBaseDPField, rBaseCacheField, orItemNames );
+ pCacheField->setFinalGroupName(maDPFieldName);
+ // on success, try to create nested group fields
+ Reference< XDataPilotField > xDPField = mrPivotTable.getDataPilotField( maDPFieldName );
+ if( xDPField.is() )
+ mrPivotTable.finalizeParentGroupingImport( xDPField, *pCacheField, orItemNames );
+}
+
+void PivotTableField::finalizeImportBasedOnCache( const Reference< XDataPilotDescriptor >& rxDPDesc)
+{
+ /* Process all fields based on source data, other fields (e.g. group
+ fields) are processed based on cache fields.*/
+ Reference< XDataPilotField > xDPField;
+ sal_Int32 nDatabaseIdx = mrPivotTable.getCacheDatabaseIndex( mnFieldIndex );
+ if( (nDatabaseIdx >= 0) && rxDPDesc.is() ) try
+ {
+ // Try to get the source field and its name from passed DataPilot descriptor
+ Reference< XIndexAccess > xDPFieldsIA( rxDPDesc->getDataPilotFields(), UNO_SET_THROW );
+ xDPField.set( xDPFieldsIA->getByIndex( nDatabaseIdx ), UNO_QUERY_THROW );
+ Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW );
+ maDPFieldName = xDPFieldName->getName();
+ SAL_WARN_IF( maDPFieldName.isEmpty(), "sc.filter", "PivotTableField::finalizeImportBasedOnCache - no field name in source data found" );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // Use group names already generated for another table using the same group field.
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ if(!pCacheField->getFinalGroupName().isEmpty())
+ maDPFieldName = pCacheField->getFinalGroupName();
+ }
+}
+
+void PivotTableField::convertRowField()
+{
+ convertRowColPageField( XML_axisRow );
+}
+
+void PivotTableField::convertColField()
+{
+ convertRowColPageField( XML_axisCol );
+}
+
+void PivotTableField::convertHiddenField()
+{
+ convertRowColPageField( XML_TOKEN_INVALID );
+}
+
+void PivotTableField::convertPageField( const PTPageFieldModel& rPageField )
+{
+ OSL_ENSURE( rPageField.mnField == mnFieldIndex, "PivotTableField::convertPageField - wrong field index" );
+ // convert all settings common for row/column/page fields
+ Reference< XDataPilotField > xDPField = convertRowColPageField( XML_axisPage );
+
+ if( !xDPField.is() )
+ return;
+
+ PropertySet aPropSet( xDPField );
+
+ // find cache item used as 'selected page'
+ sal_Int32 nCacheItem = -1;
+ if( maModel.mbMultiPageItems )
+ {
+ // multiple items may be selected
+ OSL_ENSURE( rPageField.mnItem == BIFF12_PTPAGEFIELD_MULTIITEMS, "PivotTableField::convertPageField - unexpected cache item index" );
+ // try to find a single visible item
+ bool bHasMultiItems = false;
+ for( const auto& rItem : maItems )
+ {
+ if( (rItem.mnType == XML_data) && !rItem.mbHidden )
+ {
+ bHasMultiItems = nCacheItem >= 0;
+ nCacheItem = bHasMultiItems ? -1 : rItem.mnCacheItem;
+ }
+
+ if( bHasMultiItems )
+ break;
+ }
+ }
+ else
+ {
+ // single item may be selected
+ if( (0 <= rPageField.mnItem) && (o3tl::make_unsigned(rPageField.mnItem) < maItems.size()) )
+ nCacheItem = maItems[ rPageField.mnItem ].mnCacheItem;
+ }
+
+ if( nCacheItem < 0 )
+ return;
+
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ if( const PivotCacheItem* pSharedItem = pCacheField->getCacheItem( nCacheItem ) )
+ {
+ ScDPObject* pDPObj = mrPivotTable.getDPObject();
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(pCacheField->getName());
+ OUString aSelectedPage = pSharedItem->getFormattedName(*pDim, pDPObj, DateTime(getWorkbookSettings().getNullDate()));
+ aPropSet.setProperty( PROP_SelectedPage, aSelectedPage );
+ }
+ }
+}
+
+void PivotTableField::convertDataField( const PTDataFieldModel& rDataField )
+{
+ OSL_ENSURE( rDataField.mnField == mnFieldIndex, "PivotTableField::convertDataField - wrong field index" );
+ OSL_ENSURE( maModel.mbDataField, "PivotTableField::convertDataField - not a data field" );
+ Reference< XDataPilotField > xDPField = mrPivotTable.getDataPilotField( maDPFieldName );
+ if( !xDPField.is() )
+ return;
+
+ PropertySet aPropSet( xDPField );
+
+ // field orientation
+ aPropSet.setProperty( PROP_Orientation, DataPilotFieldOrientation_DATA );
+
+ if (!rDataField.maName.isEmpty())
+ aPropSet.setProperty(PROP_Name, rDataField.maName);
+
+ /* Field aggregation function. Documentation is a little bit confused
+ about which names to use for the count functions. The name 'count'
+ means 'count all', and 'countNum' means 'count numbers'. On the
+ other hand, for subtotals, 'countA' means 'count all', and 'count'
+ means 'count numbers' (see above). */
+ GeneralFunction eAggFunc = GeneralFunction_SUM;
+ switch( rDataField.mnSubtotal )
+ {
+ case XML_sum: eAggFunc = GeneralFunction_SUM; break;
+ case XML_count: eAggFunc = GeneralFunction_COUNT; break;
+ case XML_average: eAggFunc = GeneralFunction_AVERAGE; break;
+ case XML_max: eAggFunc = GeneralFunction_MAX; break;
+ case XML_min: eAggFunc = GeneralFunction_MIN; break;
+ case XML_product: eAggFunc = GeneralFunction_PRODUCT; break;
+ case XML_countNums: eAggFunc = GeneralFunction_COUNTNUMS; break;
+ case XML_stdDev: eAggFunc = GeneralFunction_STDEV; break;
+ case XML_stdDevp: eAggFunc = GeneralFunction_STDEVP; break;
+ case XML_var: eAggFunc = GeneralFunction_VAR; break;
+ case XML_varp: eAggFunc = GeneralFunction_VARP; break;
+ default: OSL_FAIL( "PivotTableField::convertDataField - unknown aggregation function" );
+ }
+ aPropSet.setProperty( PROP_Function, eAggFunc );
+
+ // field reference ('show data as')
+ DataPilotFieldReference aReference;
+ aReference.ReferenceType = DataPilotFieldReferenceType::NONE;
+ switch( rDataField.mnShowDataAs )
+ {
+ case XML_difference: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_DIFFERENCE; break;
+ case XML_percent: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_PERCENTAGE; break;
+ case XML_percentDiff: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE; break;
+ case XML_runTotal: aReference.ReferenceType = DataPilotFieldReferenceType::RUNNING_TOTAL; break;
+ case XML_percentOfRow: aReference.ReferenceType = DataPilotFieldReferenceType::ROW_PERCENTAGE; break;
+ case XML_percentOfCol: aReference.ReferenceType = DataPilotFieldReferenceType::COLUMN_PERCENTAGE; break;
+ case XML_percentOfTotal: aReference.ReferenceType = DataPilotFieldReferenceType::TOTAL_PERCENTAGE; break;
+ case XML_index: aReference.ReferenceType = DataPilotFieldReferenceType::INDEX; break;
+ }
+ if( aReference.ReferenceType == DataPilotFieldReferenceType::NONE )
+ return;
+
+ const PivotCacheField* pCacheField = mrPivotTable.getCacheField( rDataField.mnBaseField );
+ if( !pCacheField )
+ return;
+
+ aReference.ReferenceField = pCacheField->getName();
+ switch( rDataField.mnBaseItem )
+ {
+ case OOX_PT_PREVIOUS_ITEM:
+ aReference.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS;
+ break;
+ case OOX_PT_NEXT_ITEM:
+ aReference.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT;
+ break;
+ default:
+ aReference.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED;
+ if( const PivotCacheItem* pCacheItem = pCacheField->getCacheItem( rDataField.mnBaseItem ) )
+ aReference.ReferenceItemName = pCacheItem->getName();
+ }
+ aPropSet.setProperty( PROP_Reference, aReference );
+}
+
+// private --------------------------------------------------------------------
+
+Reference< XDataPilotField > PivotTableField::convertRowColPageField( sal_Int32 nAxis )
+{
+ bool bDataLayout = mnFieldIndex == OOX_PT_DATALAYOUTFIELD;
+ Reference< XDataPilotField > xDPField = bDataLayout ? mrPivotTable.getDataLayoutField() : mrPivotTable.getDataPilotField( maDPFieldName );
+ OSL_ENSURE( bDataLayout || (nAxis == maModel.mnAxis), "PivotTableField::convertRowColPageField - field axis mismatch" );
+
+ if( xDPField.is() )
+ {
+ // TODO: Use this to set properties directly, bypassing the slow uno layer.
+ ScDPObject* pDPObj = mrPivotTable.getDPObject();
+
+ PropertySet aPropSet( xDPField );
+
+ // field orientation
+ DataPilotFieldOrientation eFieldOrient = DataPilotFieldOrientation_HIDDEN;
+ switch( nAxis )
+ {
+ case XML_axisRow: eFieldOrient = DataPilotFieldOrientation_ROW; break;
+ case XML_axisCol: eFieldOrient = DataPilotFieldOrientation_COLUMN; break;
+ case XML_axisPage: eFieldOrient = DataPilotFieldOrientation_PAGE; break;
+ }
+ if( eFieldOrient != DataPilotFieldOrientation_HIDDEN )
+ aPropSet.setProperty( PROP_Orientation, eFieldOrient );
+
+ // all other settings not for the data layout field
+ if( !bDataLayout )
+ {
+ /* Field subtotal functions. Ignore the 'defaultSubtotal' flag, if
+ explicit functions are set. This is different behaviour between
+ XML (where 'defaultSubtotal' is set regardless of other
+ functions) and binary formats (where 'defaultSubtotal' is not
+ set if other functions are set). */
+ ::std::vector< GeneralFunction > aSubtotals;
+ /* Order of subtotals is fixed in Excel. Documentation is a little
+ bit confused about which names to use for the count functions.
+ For subtotals, 'countA' means 'count all', and 'count' means
+ 'count numbers'. On the other hand, for the data field
+ aggregation function, 'count' means 'count all', and 'countNum'
+ means 'count numbers' (see below). */
+ if( maModel.mbSumSubtotal ) aSubtotals.push_back( GeneralFunction_SUM );
+ if( maModel.mbCountASubtotal ) aSubtotals.push_back( GeneralFunction_COUNT );
+ if( maModel.mbAverageSubtotal ) aSubtotals.push_back( GeneralFunction_AVERAGE );
+ if( maModel.mbMaxSubtotal ) aSubtotals.push_back( GeneralFunction_MAX );
+ if( maModel.mbMinSubtotal ) aSubtotals.push_back( GeneralFunction_MIN );
+ if( maModel.mbProductSubtotal ) aSubtotals.push_back( GeneralFunction_PRODUCT );
+ if( maModel.mbCountSubtotal ) aSubtotals.push_back( GeneralFunction_COUNTNUMS );
+ if( maModel.mbStdDevSubtotal ) aSubtotals.push_back( GeneralFunction_STDEV );
+ if( maModel.mbStdDevPSubtotal ) aSubtotals.push_back( GeneralFunction_STDEVP );
+ if( maModel.mbVarSubtotal ) aSubtotals.push_back( GeneralFunction_VAR );
+ if( maModel.mbVarPSubtotal ) aSubtotals.push_back( GeneralFunction_VARP );
+ // if no function is set manually, check the 'defaultSubtotal' flag
+ if( aSubtotals.empty() && maModel.mbDefaultSubtotal )
+ aSubtotals.push_back( GeneralFunction_AUTO );
+ aPropSet.setProperty( PROP_Subtotals, comphelper::containerToSequence( aSubtotals ) );
+
+ // layout settings
+ DataPilotFieldLayoutInfo aLayoutInfo;
+ if (maModel.mbCompact)
+ {
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::COMPACT_LAYOUT;
+ }
+ else if (maModel.mbOutline)
+ {
+ if (maModel.mbSubtotalTop)
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ else
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ }
+ else
+ {
+ aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ }
+
+ aLayoutInfo.AddEmptyLines = maModel.mbInsertBlankRow;
+ aPropSet.setProperty( PROP_LayoutInfo, aLayoutInfo );
+ aPropSet.setProperty( PROP_ShowEmpty, maModel.mbShowAll );
+
+ // auto show (OOXML/BIFF12 only)
+ if( maModel.mbAutoShow )
+ {
+ DataPilotFieldAutoShowInfo aAutoShowInfo;
+ aAutoShowInfo.IsEnabled = true;
+ aAutoShowInfo.ShowItemsMode = maModel.mbTopAutoShow ? DataPilotFieldShowItemsMode::FROM_TOP : DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ aAutoShowInfo.ItemCount = maModel.mnAutoShowItems;
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheFieldOfDataField( maModel.mnAutoShowRankBy ) )
+ aAutoShowInfo.DataField = pCacheField->getName();
+ aPropSet.setProperty( PROP_AutoShowInfo, aAutoShowInfo );
+ }
+
+ // auto sort
+ DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.IsAscending = maModel.mnSortType == XML_ascending;
+ if( (maModel.mnSortType != XML_ascending) && (maModel.mnSortType != XML_descending) )
+ {
+ aSortInfo.Mode = DataPilotFieldSortMode::MANUAL;
+ }
+ else
+ {
+ const PivotCacheField* pCacheField = (maModel.mnSortRefField == OOX_PT_DATALAYOUTFIELD) ?
+ mrPivotTable.getCacheFieldOfDataField( maModel.mnSortRefItem ) : nullptr;
+ if( pCacheField )
+ {
+ aSortInfo.Mode = DataPilotFieldSortMode::DATA;
+ aSortInfo.Field = pCacheField->getName();
+ }
+ else
+ {
+ aSortInfo.Mode = DataPilotFieldSortMode::NAME;
+ }
+ }
+ aPropSet.setProperty( PROP_SortInfo, aSortInfo );
+
+ // item settings
+ if (const PivotCacheField* pCacheField = mrPivotTable.getCacheField(mnFieldIndex))
+ {
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(pCacheField->getName());
+ SAL_WARN_IF(!pDim, "sc.filter", "PivotTableField::convertRowColPageField - no Dimension found for: " << pCacheField->getName());
+
+ if (pDim) try
+ {
+ for( const auto& rItem : maItems )
+ {
+ if (rItem.mnType != XML_data)
+ continue;
+
+ const PivotCacheItem* pSharedItem = pCacheField->getCacheItem(rItem.mnCacheItem);
+ if (!pSharedItem)
+ continue;
+
+ try
+ {
+ ScDPSaveMember* pMem = pDim->GetMemberByName(pSharedItem->getFormattedName(*pDim, pDPObj, DateTime(getWorkbookSettings().getNullDate())));
+ pMem->SetShowDetails(rItem.mbShowDetails);
+ pMem->SetIsVisible(!rItem.mbHidden);
+ }
+ catch( Exception& )
+ {
+ // catch every failed container access to be able to process following items
+ }
+ }
+ }
+ catch (const Exception&) {}
+ }
+ }
+ }
+ return xDPField;
+}
+
+PTFilterModel::PTFilterModel() :
+ mfValue( 0.0 ),
+ mnField( -1 ),
+ mnMemPropField( -1 ),
+ mnType( XML_TOKEN_INVALID ),
+ mnEvalOrder( 0 ),
+ mnId( -1 ),
+ mnMeasureField( -1 ),
+ mnMeasureHier( -1 ),
+ mbTopFilter( true )
+{
+}
+
+PivotTableFilter::PivotTableFilter( const PivotTable& rPivotTable ) :
+ WorkbookHelper( rPivotTable ),
+ mrPivotTable( rPivotTable )
+{
+}
+
+void PivotTableFilter::importFilter( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maDescription = rAttribs.getXString( XML_description, OUString() );
+ maModel.maStrValue1 = rAttribs.getXString( XML_stringValue1, OUString() );
+ maModel.maStrValue2 = rAttribs.getXString( XML_stringValue2, OUString() );
+ maModel.mnField = rAttribs.getInteger( XML_fld, -1 );
+ maModel.mnMemPropField = rAttribs.getInteger( XML_mpFld, -1 );
+ maModel.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maModel.mnEvalOrder = rAttribs.getInteger( XML_evalOrder, 0 );
+ maModel.mnId = rAttribs.getInteger( XML_id, -1 );
+ maModel.mnMeasureField = rAttribs.getInteger( XML_iMeasureFld, -1 );
+ maModel.mnMeasureHier = rAttribs.getInteger( XML_iMeasureHier, -1 );
+}
+
+void PivotTableFilter::importTop10( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( rAttribs.getBool( XML_percent, false ) == (maModel.mnType == XML_percent),
+ "PivotTableFilter::importTop10 - unexpected value of percent attribute" );
+ maModel.mfValue = rAttribs.getDouble( XML_val, 0.0 );
+ maModel.mbTopFilter = rAttribs.getBool( XML_top, true );
+}
+
+void PivotTableFilter::importPTFilter( SequenceInputStream& rStrm )
+{
+ sal_Int32 nType;
+ sal_uInt16 nFlags;
+ maModel.mnField = rStrm.readInt32();
+ maModel.mnMemPropField = rStrm.readInt32();
+ nType = rStrm.readInt32();
+ rStrm.skip( 4 ); // unused
+ maModel.mnId = rStrm.readInt32();
+ maModel.mnMeasureField = rStrm.readInt32();
+ maModel.mnMeasureHier = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASNAME ) )
+ rStrm >> maModel.maName;
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASDESCRIPTION ) )
+ rStrm >> maModel.maDescription;
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASSTRVALUE1 ) )
+ rStrm >> maModel.maStrValue1;
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASSTRVALUE2 ) )
+ rStrm >> maModel.maStrValue2;
+
+ static const sal_Int32 spnTypes[] =
+ {
+ XML_unknown,
+ // data field top10 filter (1-3)
+ XML_count, XML_percent, XML_sum,
+ // caption filter (4-17)
+ XML_captionEqual, XML_captionNotEqual,
+ XML_captionBeginsWith, XML_captionNotBeginsWith, XML_captionEndsWith, XML_captionNotEndsWith,
+ XML_captionContains, XML_captionNotContains, XML_captionGreaterThan, XML_captionGreaterThanOrEqual,
+ XML_captionLessThan, XML_captionLessThanOrEqual, XML_captionBetween, XML_captionNotBetween,
+ // value filter (18-25)
+ XML_valueEqual, XML_valueNotEqual, XML_valueGreaterThan, XML_valueGreaterThanOrEqual,
+ XML_valueLessThan, XML_valueLessThanOrEqual, XML_valueBetween, XML_valueNotBetween,
+ // date filter (26-65)
+ XML_dateEqual, XML_dateOlderThan, XML_dateNewerThan, XML_dateBetween,
+ XML_tomorrow, XML_today, XML_yesterday, XML_nextWeek, XML_thisWeek, XML_lastWeek,
+ XML_nextMonth, XML_thisMonth, XML_lastMonth, XML_nextQuarter, XML_thisQuarter, XML_lastQuarter,
+ XML_nextYear, XML_thisYear, XML_lastYear, XML_yearToDate, XML_Q1, XML_Q2, XML_Q3, XML_Q4,
+ XML_M1, XML_M2, XML_M3, XML_M4, XML_M5, XML_M6, XML_M7, XML_M8, XML_M9, XML_M10, XML_M11, XML_M12,
+ XML_dateNotEqual, XML_dateOlderThanOrEqual, XML_dateNewerThanOrEqual, XML_dateNotBetween
+ };
+ maModel.mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID );
+}
+
+void PivotTableFilter::importTop10Filter( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags;
+ nFlags = rStrm.readuChar();
+ maModel.mfValue = rStrm.readDouble();
+
+ SAL_WARN_IF(
+ getFlag(nFlags, BIFF12_TOP10FILTER_PERCENT) != (maModel.mnType == XML_percent),
+ "sc.filter",
+ "PivotTableFilter::importTop10 - unexpected value of percent attribute");
+ maModel.mbTopFilter = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
+}
+
+void PivotTableFilter::finalizeImport()
+{
+ // only simple top10 filter supported
+ if( maModel.mnType != XML_count )
+ return;
+
+ PropertySet aPropSet( mrPivotTable.getDataPilotField( maModel.mnField ) );
+ if( aPropSet.is() )
+ {
+ DataPilotFieldAutoShowInfo aAutoShowInfo;
+ aAutoShowInfo.IsEnabled = true;
+ aAutoShowInfo.ShowItemsMode = maModel.mbTopFilter ? DataPilotFieldShowItemsMode::FROM_TOP : DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ aAutoShowInfo.ItemCount = getLimitedValue< sal_Int32, double >( maModel.mfValue, 0, SAL_MAX_INT32 );
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheFieldOfDataField( maModel.mnMeasureField ) )
+ aAutoShowInfo.DataField = pCacheField->getName();
+ aPropSet.setProperty( PROP_AutoShowInfo, aAutoShowInfo );
+ }
+}
+
+PTDefinitionModel::PTDefinitionModel() :
+ mnCacheId( -1 ),
+ mnDataPosition( 0 ),
+ mnPageWrap( 0 ),
+ mnIndent( 1 ),
+ mnChartFormat( 0 ),
+ mbDataOnRows( false ),
+ mbShowError( false ),
+ mbShowMissing( true ),
+ mbShowItems( true ),
+ mbDisableFieldList( false ),
+ mbShowCalcMembers( true ),
+ mbVisualTotals( true ),
+ mbShowDrill( true ),
+ mbPrintDrill( false ),
+ mbEnableDrill( true ),
+ mbPreserveFormatting( true ),
+ mbUseAutoFormat( false ),
+ mbPageOverThenDown( false ),
+ mbSubtotalHiddenItems( false ),
+ mbRowGrandTotals( true ),
+ mbColGrandTotals( true ),
+ mbFieldPrintTitles( false ),
+ mbItemPrintTitles( false ),
+ mbMergeItem( false ),
+ mbShowEmptyRow( false ),
+ mbShowEmptyCol( false ),
+ mbShowHeaders( true ),
+ mbFieldListSortAsc( false ),
+ mbCustomListSort( true )
+{
+}
+
+PTLocationModel::PTLocationModel() :
+ mnFirstHeaderRow( 0 ),
+ mnFirstDataRow( 0 ),
+ mnFirstDataCol( 0 ),
+ mnRowPageCount( 0 ),
+ mnColPageCount( 0 )
+{
+}
+
+PivotTable::PivotTable( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mpDPObject(nullptr),
+ maDataField( *this, OOX_PT_DATALAYOUTFIELD ),
+ mpPivotCache( nullptr )
+{
+}
+
+void PivotTable::importPivotTableDefinition( const AttributeList& rAttribs )
+{
+ maDefModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maDefModel.maDataCaption = rAttribs.getXString( XML_dataCaption , OUString() );
+ maDefModel.maGrandTotalCaption = rAttribs.getXString( XML_grandTotalCaption, OUString() );
+ maDefModel.maRowHeaderCaption = rAttribs.getXString( XML_rowHeaderCaption, OUString() );
+ maDefModel.maColHeaderCaption = rAttribs.getXString( XML_colHeaderCaption, OUString() );
+ maDefModel.maErrorCaption = rAttribs.getXString( XML_errorCaption, OUString() );
+ maDefModel.maMissingCaption = rAttribs.getXString( XML_missingCaption, OUString() );
+ maDefModel.maPageStyle = rAttribs.getXString( XML_pageStyle, OUString() );
+ maDefModel.maPivotTableStyle = rAttribs.getXString( XML_pivotTableStyle, OUString() );
+ maDefModel.maVacatedStyle = rAttribs.getXString( XML_vacatedStyle, OUString() );
+ maDefModel.maTag = rAttribs.getXString( XML_tag, OUString() );
+ maDefModel.mnCacheId = rAttribs.getInteger( XML_cacheId, -1 );
+ maDefModel.mnDataPosition = rAttribs.getInteger( XML_dataPosition, 0 );
+ maDefModel.mnPageWrap = rAttribs.getInteger( XML_pageWrap, 0 );
+ maDefModel.mnIndent = rAttribs.getInteger( XML_indent, 1 );
+ maDefModel.mnChartFormat = rAttribs.getInteger( XML_chartFormat, 0 );
+ maDefModel.mnAutoFormatId = rAttribs.getInteger( XML_autoFormatId, 0 );
+ maDefModel.mbDataOnRows = rAttribs.getBool( XML_dataOnRows, false );
+ maDefModel.mbShowError = rAttribs.getBool( XML_showError, false );
+ maDefModel.mbShowMissing = rAttribs.getBool( XML_showMissing, true );
+ maDefModel.mbShowItems = rAttribs.getBool( XML_showItems, true );
+ maDefModel.mbDisableFieldList = rAttribs.getBool( XML_disableFieldList, false );
+ maDefModel.mbShowCalcMembers = rAttribs.getBool( XML_showCalcMbrs, true );
+ maDefModel.mbVisualTotals = rAttribs.getBool( XML_visualTotals, true );
+ maDefModel.mbShowDrill = rAttribs.getBool( XML_showDrill, true );
+ maDefModel.mbPrintDrill = rAttribs.getBool( XML_printDrill, false );
+ maDefModel.mbEnableDrill = rAttribs.getBool( XML_enableDrill, true );
+ maDefModel.mbPreserveFormatting = rAttribs.getBool( XML_preserveFormatting, true );
+ maDefModel.mbUseAutoFormat = rAttribs.getBool( XML_useAutoFormatting, false );
+ maDefModel.mbPageOverThenDown = rAttribs.getBool( XML_pageOverThenDown, false );
+ maDefModel.mbSubtotalHiddenItems = rAttribs.getBool( XML_subtotalHiddenItems, false );
+ maDefModel.mbRowGrandTotals = rAttribs.getBool( XML_rowGrandTotals, true );
+ maDefModel.mbColGrandTotals = rAttribs.getBool( XML_colGrandTotals, true );
+ maDefModel.mbFieldPrintTitles = rAttribs.getBool( XML_fieldPrintTitles, false );
+ maDefModel.mbItemPrintTitles = rAttribs.getBool( XML_itemPrintTitles, false );
+ maDefModel.mbMergeItem = rAttribs.getBool( XML_mergeItem, false );
+ maDefModel.mbShowEmptyRow = rAttribs.getBool( XML_showEmptyRow, false );
+ maDefModel.mbShowEmptyCol = rAttribs.getBool( XML_showEmptyCol, false );
+ maDefModel.mbShowHeaders = rAttribs.getBool( XML_showHeaders, true );
+ maDefModel.mbFieldListSortAsc = rAttribs.getBool( XML_fieldListSortAscending, false );
+ maDefModel.mbCustomListSort = rAttribs.getBool( XML_customListSort, true );
+ maDefModel.mbApplyNumFmt = rAttribs.getBool( XML_applyNumberFormats, false );
+ maDefModel.mbApplyFont = rAttribs.getBool( XML_applyFontFormats, false );
+ maDefModel.mbApplyAlignment = rAttribs.getBool( XML_applyAlignmentFormats, false );
+ maDefModel.mbApplyBorder = rAttribs.getBool( XML_applyBorderFormats, false );
+ maDefModel.mbApplyFill = rAttribs.getBool( XML_applyPatternFormats, false );
+ // OOXML and BIFF12 documentation differ: OOXML mentions width/height, BIFF12 mentions protection
+ maDefModel.mbApplyProtection = rAttribs.getBool( XML_applyWidthHeightFormats, false );
+}
+
+void PivotTable::importLocation( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet );
+ maLocationModel.mnFirstHeaderRow = rAttribs.getInteger( XML_firstHeaderRow, 0 );
+ maLocationModel.mnFirstDataRow = rAttribs.getInteger( XML_firstDataRow, 0 );
+ maLocationModel.mnFirstDataCol = rAttribs.getInteger( XML_firstDataCol, 0 );
+ maLocationModel.mnRowPageCount = rAttribs.getInteger( XML_rowPageCount, 0 );
+ maLocationModel.mnColPageCount = rAttribs.getInteger( XML_colPageCount, 0 );
+}
+
+void PivotTable::importRowField( const AttributeList& rAttribs )
+{
+ importField( maRowFields, rAttribs );
+}
+
+void PivotTable::importColField( const AttributeList& rAttribs )
+{
+ importField( maColFields, rAttribs );
+}
+
+void PivotTable::importPageField( const AttributeList& rAttribs )
+{
+ PTPageFieldModel aModel;
+ aModel.maName = rAttribs.getXString( XML_name, OUString() );
+ aModel.mnField = rAttribs.getInteger( XML_fld, -1 );
+ // specification is wrong, XML_item is not the cache item, but the field item
+ aModel.mnItem = rAttribs.getInteger( XML_item, BIFF12_PTPAGEFIELD_MULTIITEMS );
+ maPageFields.push_back( aModel );
+}
+
+void PivotTable::importDataField( const AttributeList& rAttribs )
+{
+ PTDataFieldModel aModel;
+ aModel.maName = rAttribs.getXString( XML_name, OUString() );
+ aModel.mnField = rAttribs.getInteger( XML_fld, -1 );
+ aModel.mnSubtotal = rAttribs.getToken( XML_subtotal, XML_sum );
+ aModel.mnShowDataAs = rAttribs.getToken( XML_showDataAs, XML_normal );
+ aModel.mnBaseField = rAttribs.getInteger( XML_baseField, -1 );
+ aModel.mnBaseItem = rAttribs.getInteger( XML_baseItem, -1 );
+ aModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maDataFields.push_back( aModel );
+}
+
+void PivotTable::putToInteropGrabBag(const OUString& sName, const AttributeList& rAttribs)
+{
+ if (auto xFastAttributeList = rAttribs.getFastAttributeList())
+ {
+ // Store both known and unknown attribute sequences to the grab bag as is
+ css::uno::Sequence<css::xml::FastAttribute> aFast = xFastAttributeList->getFastAttributes();
+ css::uno::Sequence<css::xml::Attribute> aUnk = xFastAttributeList->getUnknownAttributes();
+ css::uno::Sequence<css::uno::Any> aVal{ css::uno::Any(aFast), css::uno::Any(aUnk) };
+ maInteropGrabBag[sName] <<= aVal;
+ }
+}
+
+void PivotTable::importPTDefinition( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags1, nFlags2, nFlags3;
+ sal_uInt8 nDataAxis;
+ nFlags1 = rStrm.readuInt32();
+ nFlags2 = rStrm.readuInt32();
+ nFlags3 = rStrm.readuInt32();
+ nDataAxis = rStrm.readuChar();
+ maDefModel.mnPageWrap = rStrm.readuInt8();
+ rStrm.skip( 2 ); // refresh versions
+ maDefModel.mnDataPosition = rStrm.readInt32();
+ maDefModel.mnAutoFormatId = rStrm.readuInt16();
+ rStrm.skip( 2 ); // unused
+ maDefModel.mnChartFormat = rStrm.readInt32();
+ maDefModel.mnCacheId = rStrm.readInt32();
+ rStrm >> maDefModel.maName;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASDATACAPTION ) )
+ rStrm >> maDefModel.maDataCaption;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASGRANDTOTALCAPTION ) )
+ rStrm >> maDefModel.maGrandTotalCaption;
+ if( !getFlag( nFlags3, BIFF12_PTDEF_NOERRORCAPTION ) ) // missing flag indicates existing string
+ rStrm >> maDefModel.maErrorCaption;
+ if( !getFlag( nFlags3, BIFF12_PTDEF_NOMISSINGCAPTION ) ) // missing flag indicates existing string
+ rStrm >> maDefModel.maMissingCaption;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASPAGESTYLE ) )
+ rStrm >> maDefModel.maPageStyle;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASPIVOTTABLESTYLE ) )
+ rStrm >> maDefModel.maPivotTableStyle;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASVACATEDSTYLE ) )
+ rStrm >> maDefModel.maVacatedStyle;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASTAG ) )
+ rStrm >> maDefModel.maTag;
+ if( getFlag( nFlags3, BIFF12_PTDEF_HASCOLHEADERCAPTION ) ) // TODO: right order (col/row)? spec is unclear
+ rStrm >> maDefModel.maColHeaderCaption;
+ if( getFlag( nFlags3, BIFF12_PTDEF_HASROWHEADERCAPTION ) )
+ rStrm >> maDefModel.maRowHeaderCaption;
+
+ SAL_WARN_IF(
+ (nDataAxis != BIFF12_PTDEF_ROWAXIS) && (nDataAxis != BIFF12_PTDEF_COLAXIS),
+ "sc.filter",
+ "PivotTable::importPTDefinition - unexpected axis position for data field");
+
+ maDefModel.mnIndent = extractValue< sal_uInt8 >( nFlags1, 24, 7 );
+ maDefModel.mbDataOnRows = nDataAxis == BIFF12_PTDEF_ROWAXIS;
+ maDefModel.mbShowError = getFlag( nFlags2, BIFF12_PTDEF_SHOWERROR );
+ maDefModel.mbShowMissing = getFlag( nFlags2, BIFF12_PTDEF_SHOWMISSING );
+ maDefModel.mbShowItems = getFlag( nFlags1, BIFF12_PTDEF_SHOWITEMS );
+ maDefModel.mbDisableFieldList = getFlag( nFlags1, BIFF12_PTDEF_DISABLEFIELDLIST );
+ maDefModel.mbShowCalcMembers = !getFlag( nFlags1, BIFF12_PTDEF_HIDECALCMEMBERS );
+ maDefModel.mbVisualTotals = !getFlag( nFlags1, BIFF12_PTDEF_WITHHIDDENTOTALS );
+ maDefModel.mbShowDrill = !getFlag( nFlags1, BIFF12_PTDEF_HIDEDRILL );
+ maDefModel.mbPrintDrill = getFlag( nFlags1, BIFF12_PTDEF_PRINTDRILL );
+ maDefModel.mbEnableDrill = getFlag( nFlags2, BIFF12_PTDEF_ENABLEDRILL );
+ maDefModel.mbPreserveFormatting = getFlag( nFlags2, BIFF12_PTDEF_PRESERVEFORMATTING );
+ maDefModel.mbUseAutoFormat = getFlag( nFlags2, BIFF12_PTDEF_USEAUTOFORMAT );
+ maDefModel.mbPageOverThenDown = getFlag( nFlags2, BIFF12_PTDEF_PAGEOVERTHENDOWN );
+ maDefModel.mbSubtotalHiddenItems = getFlag( nFlags2, BIFF12_PTDEF_SUBTOTALHIDDENITEMS );
+ maDefModel.mbRowGrandTotals = getFlag( nFlags2, BIFF12_PTDEF_ROWGRANDTOTALS );
+ maDefModel.mbColGrandTotals = getFlag( nFlags2, BIFF12_PTDEF_COLGRANDTOTALS );
+ maDefModel.mbFieldPrintTitles = getFlag( nFlags2, BIFF12_PTDEF_FIELDPRINTTITLES );
+ maDefModel.mbItemPrintTitles = getFlag( nFlags2, BIFF12_PTDEF_ITEMPRINTTITLES );
+ maDefModel.mbMergeItem = getFlag( nFlags2, BIFF12_PTDEF_MERGEITEM );
+ maDefModel.mbApplyNumFmt = getFlag( nFlags2, BIFF12_PTDEF_APPLYNUMFMT );
+ maDefModel.mbApplyFont = getFlag( nFlags2, BIFF12_PTDEF_APPLYFONT );
+ maDefModel.mbApplyAlignment = getFlag( nFlags2, BIFF12_PTDEF_APPLYALIGNMENT );
+ maDefModel.mbApplyBorder = getFlag( nFlags2, BIFF12_PTDEF_APPLYBORDER );
+ maDefModel.mbApplyFill = getFlag( nFlags2, BIFF12_PTDEF_APPLYFILL );
+ maDefModel.mbApplyProtection = getFlag( nFlags2, BIFF12_PTDEF_APPLYPROTECTION );
+ maDefModel.mbShowEmptyRow = getFlag( nFlags2, BIFF12_PTDEF_SHOWEMPTYROW );
+ maDefModel.mbShowEmptyCol = getFlag( nFlags2, BIFF12_PTDEF_SHOWEMPTYCOL );
+ maDefModel.mbShowHeaders = !getFlag( nFlags1, BIFF12_PTDEF_HIDEHEADERS );
+ maDefModel.mbFieldListSortAsc = getFlag( nFlags3, BIFF12_PTDEF_FIELDLISTSORTASC );
+ maDefModel.mbCustomListSort = !getFlag( nFlags3, BIFF12_PTDEF_NOCUSTOMLISTSORT );
+}
+
+void PivotTable::importPTLocation( SequenceInputStream& rStrm, sal_Int16 nSheet )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ maLocationModel.mnFirstHeaderRow = rStrm.readInt32();
+ maLocationModel.mnFirstDataRow = rStrm.readInt32();
+ maLocationModel.mnFirstDataCol = rStrm.readInt32();
+ maLocationModel.mnRowPageCount = rStrm.readInt32();
+ maLocationModel.mnColPageCount = rStrm.readInt32();
+ AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, aBinRange, nSheet );
+}
+
+void PivotTable::importPTRowFields( SequenceInputStream& rStrm )
+{
+ importFields( maRowFields, rStrm );
+}
+
+void PivotTable::importPTColFields( SequenceInputStream& rStrm )
+{
+ importFields( maColFields, rStrm );
+}
+
+void PivotTable::importPTPageField( SequenceInputStream& rStrm )
+{
+ PTPageFieldModel aModel;
+ sal_uInt8 nFlags;
+ aModel.mnField = rStrm.readInt32();
+ aModel.mnItem = rStrm.readInt32();
+ rStrm.skip( 4 ); // hierarchy
+ nFlags = rStrm.readuChar();
+ if( getFlag( nFlags, BIFF12_PTPAGEFIELD_HASNAME ) )
+ rStrm >> aModel.maName;
+ maPageFields.push_back( aModel );
+}
+
+void PivotTable::importPTDataField( SequenceInputStream& rStrm )
+{
+ PTDataFieldModel aModel;
+ sal_Int32 nSubtotal, nShowDataAs;
+ sal_uInt8 nHasName;
+ aModel.mnField = rStrm.readInt32( );
+ nSubtotal = rStrm.readInt32();
+ nShowDataAs = rStrm.readInt32();
+ aModel.mnBaseField = rStrm.readInt32();
+ aModel.mnBaseItem = rStrm.readInt32();
+ aModel.mnNumFmtId = rStrm.readInt32();
+ nHasName = rStrm.readuChar();
+ if( nHasName == 1 )
+ rStrm >> aModel.maName;
+ aModel.setBiffSubtotal( nSubtotal );
+ aModel.setBiffShowDataAs( nShowDataAs );
+ maDataFields.push_back( aModel );
+}
+
+PivotTableField& PivotTable::createTableField()
+{
+ sal_Int32 nFieldIndex = static_cast< sal_Int32 >( maFields.size() );
+ PivotTableFieldVector::value_type xTableField = std::make_shared<PivotTableField>( *this, nFieldIndex );
+ maFields.push_back( xTableField );
+ return *xTableField;
+}
+
+PivotTableFilter& PivotTable::createTableFilter()
+{
+ PivotTableFilterVector::value_type xTableFilter = std::make_shared<PivotTableFilter>( *this );
+ maFilters.push_back( xTableFilter );
+ return *xTableFilter;
+}
+
+void PivotTable::finalizeImport()
+{
+ if( !getAddressConverter().validateCellRange( maLocationModel.maRange, true, true ) )
+ return;
+
+ mpPivotCache = getPivotCaches().importPivotCacheFragment( maDefModel.mnCacheId );
+ if( !mpPivotCache || !mpPivotCache->isValidDataSource() || maDefModel.maName.isEmpty() )
+ return;
+
+ // clear destination area of the original pivot table
+ try
+ {
+ Reference< XSheetOperation > xSheetOp( getCellRangeFromDoc( maLocationModel.maRange ), UNO_QUERY_THROW );
+ using namespace ::com::sun::star::sheet::CellFlags;
+ xSheetOp->clearContents( VALUE | DATETIME | STRING | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED );
+ }
+ catch( Exception& )
+ {
+ }
+
+ try
+ {
+ // create a new data pilot descriptor based on the source data
+ Reference< XDataPilotTablesSupplier > xDPTablesSupp( getSheetFromDoc( maLocationModel.maRange.aStart.Tab() ), UNO_QUERY_THROW );
+ Reference< XDataPilotTables > xDPTables( xDPTablesSupp->getDataPilotTables(), UNO_SET_THROW );
+ mxDPDescriptor = static_cast<ScDataPilotDescriptorBase*>( xDPTables->createDataPilotDescriptor().get() );
+ ScRange aRange = mpPivotCache->getSourceRange();
+ CellRangeAddress aCellRangeAddress( aRange.aStart.Tab(),
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ mxDPDescriptor->setSourceRange( aCellRangeAddress );
+ mxDPDescriptor->setTag( maDefModel.maTag );
+
+ mpDPObject = mxDPDescriptor->GetDPObject();
+ if (!mpDPObject)
+ return;
+
+ // global data pilot properties
+ PropertySet aDescProp(( css::uno::Reference< css::beans::XPropertySet >(mxDPDescriptor) ));
+ aDescProp.setProperty( PROP_ColumnGrand, maDefModel.mbColGrandTotals );
+ aDescProp.setProperty( PROP_RowGrand, maDefModel.mbRowGrandTotals );
+ aDescProp.setProperty( PROP_ShowFilterButton, false );
+ aDescProp.setProperty( PROP_DrillDownOnDoubleClick, maDefModel.mbEnableDrill );
+
+ if (auto* pSaveData = mpDPObject->GetSaveData())
+ pSaveData->SetExpandCollapse(maDefModel.mbShowDrill);
+
+ // finalize all fields, this finds field names and creates grouping fields
+ finalizeFieldsImport();
+
+ // all row fields
+ for( const auto& rRowField : maRowFields )
+ if( PivotTableField* pField = getTableField( rRowField ) )
+ pField->convertRowField();
+
+ // all column fields
+ for( const auto& rColField : maColFields )
+ if( PivotTableField* pField = getTableField( rColField ) )
+ pField->convertColField();
+
+ // all page fields
+ for( const auto& rPageField : maPageFields )
+ if( PivotTableField* pField = getTableField( rPageField.mnField ) )
+ pField->convertPageField( rPageField );
+
+ // all hidden fields
+ ::std::set< sal_Int32 > aVisFields;
+ aVisFields.insert( maRowFields.begin(), maRowFields.end() );
+ aVisFields.insert( maColFields.begin(), maColFields.end() );
+ for( const auto& rPageField : maPageFields )
+ aVisFields.insert( rPageField.mnField );
+ sal_Int32 nIndex = 0;
+ for( auto& rxField : maFields )
+ {
+ if( aVisFields.count( nIndex ) == 0 )
+ rxField->convertHiddenField();
+ ++nIndex;
+ }
+
+ // all data fields
+ for( auto& rDataField : maDataFields )
+ {
+ if( const PivotCacheField* pCacheField = getCacheField( rDataField.mnField ) )
+ {
+ if ( pCacheField-> getGroupBaseField() != -1 )
+ rDataField.mnField = pCacheField-> getGroupBaseField();
+ }
+ if( PivotTableField* pField = getTableField( rDataField.mnField ) )
+ pField->convertDataField( rDataField );
+ }
+
+ // filters
+ maFilters.forEachMem( &PivotTableFilter::finalizeImport );
+
+ // calculate base position of table
+ CellAddress aPos( maLocationModel.maRange.aStart.Tab(), maLocationModel.maRange.aStart.Col(), maLocationModel.maRange.aStart.Row() );
+ /* If page fields exist, include them into the destination
+ area (they are excluded in Excel). Add an extra blank row. */
+ if( !maPageFields.empty() )
+ aPos.Row = ::std::max< sal_Int32 >( static_cast< sal_Int32 >( aPos.Row - maPageFields.size() - 1 ), 0 );
+
+ // save interop grab bag
+ mpDPObject->PutInteropGrabBag(std::move(maInteropGrabBag));
+
+ // insert the DataPilot table into the sheet
+ xDPTables->insertNewByName( maDefModel.maName, aPos, mxDPDescriptor );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "PivotTable::finalizeImport - exception while creating the DataPilot table" );
+ }
+}
+
+void PivotTable::finalizeFieldsImport()
+{
+ if (maFields.empty())
+ return;
+
+ /* Check whether group fields are already imported for another table
+ sharing the same groups. */
+ ScDPObject* pDPObj = getDPObject();
+ const ScDocument& rDoc = getDocImport().getDoc();
+ if (rDoc.HasPivotTable())
+ {
+ const ScDPCollection* pDPCollection = rDoc.GetDPCollection();
+ assert(pDPCollection != nullptr);
+ const ScDPDimensionSaveData* pGroups = nullptr;
+ bool bRefFound = pDPCollection->GetReferenceGroups(*pDPObj, &pGroups);
+ // Apply reference groups on this table.
+ if (bRefFound && pGroups && pGroups->HasGroupDimensions()) {
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if (pSaveData) {
+ pSaveData->SetDimensionData(pGroups);
+ pDPObj->ReloadGroupTableData();
+ maFields.forEachMem(&PivotTableField::finalizeImportBasedOnCache, ::std::cref(mxDPDescriptor));
+ return;
+ }
+
+ }
+ }
+ maFields.forEachMem(&PivotTableField::finalizeImport, ::std::cref(mxDPDescriptor));
+}
+
+void PivotTable::finalizeDateGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, sal_Int32 nBaseFieldIdx )
+{
+ // process all fields, there is no chaining information in the cache fields
+ maFields.forEachMem( &PivotTableField::finalizeDateGroupingImport, ::std::cref(rxBaseDPField), nBaseFieldIdx );
+}
+
+void PivotTable::finalizeParentGroupingImport( const Reference< XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames )
+{
+ // try to create parent group fields that group the items of the passed base field
+ if( PivotTableField* pParentTableField = maFields.get( rBaseCacheField.getParentGroupField() ).get() )
+ pParentTableField->finalizeParentGroupingImport( rxBaseDPField, rBaseCacheField, orItemNames );
+}
+
+Reference< XDataPilotField > PivotTable::getDataPilotField( const OUString& rFieldName ) const
+{
+ Reference< XDataPilotField > xDPField;
+ if( !rFieldName.isEmpty() && mxDPDescriptor.is() ) try
+ {
+ Reference< XNameAccess > xDPFieldsNA( mxDPDescriptor->getDataPilotFields(), UNO_QUERY_THROW );
+ xDPField.set( xDPFieldsNA->getByName( rFieldName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xDPField;
+}
+
+Reference< XDataPilotField > PivotTable::getDataPilotField( sal_Int32 nFieldIdx ) const
+{
+ Reference< XDataPilotField > xDPField;
+ if( const PivotTableField* pTableField = maFields.get( nFieldIdx ).get() )
+ xDPField = getDataPilotField( pTableField->getDPFieldName() );
+ return xDPField;
+}
+
+Reference< XDataPilotField > PivotTable::getDataLayoutField() const
+{
+ Reference< XDataPilotField > xDPField;
+ try
+ {
+ if (mxDPDescriptor)
+ xDPField = mxDPDescriptor->getDataLayoutField();
+ }
+ catch( Exception& )
+ {
+ }
+ return xDPField;
+}
+
+PivotCacheField* PivotTable::getCacheField( sal_Int32 nFieldIdx )
+{
+ return mpPivotCache ? mpPivotCache->getCacheField( nFieldIdx ) : nullptr;
+}
+
+const PivotCacheField* PivotTable::getCacheField( sal_Int32 nFieldIdx ) const
+{
+ return mpPivotCache ? mpPivotCache->getCacheField( nFieldIdx ) : nullptr;
+}
+
+const PivotCacheField* PivotTable::getCacheFieldOfDataField( sal_Int32 nDataItemIdx ) const
+{
+ const PTDataFieldModel* pDataField = ContainerHelper::getVectorElement( maDataFields, nDataItemIdx );
+ return pDataField ? getCacheField( pDataField->mnField ) : nullptr;
+}
+
+sal_Int32 PivotTable::getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const
+{
+ return mpPivotCache ? mpPivotCache->getCacheDatabaseIndex( nFieldIdx ) : -1;
+}
+
+// private --------------------------------------------------------------------
+
+PivotTableField* PivotTable::getTableField( sal_Int32 nFieldIdx )
+{
+ return (nFieldIdx == OOX_PT_DATALAYOUTFIELD) ? &maDataField : maFields.get( nFieldIdx ).get();
+}
+
+void PivotTable::importField( IndexVector& orFields, const AttributeList& rAttribs )
+{
+ orFields.push_back( rAttribs.getInteger( XML_x, -1 ) );
+}
+
+void PivotTable::importFields( IndexVector& orFields, SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( orFields.empty(), "PivotTable::importFields - multiple record instances" );
+ orFields.clear();
+ sal_Int32 nCount = rStrm.readInt32();
+ OSL_ENSURE( 4 * nCount == rStrm.getRemaining(), "PivotTable::importFields - invalid field count" );
+ nCount = static_cast< sal_Int32 >( rStrm.getRemaining() / 4 );
+ for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx )
+ orFields.push_back( rStrm.readInt32() );
+}
+
+PivotTableBuffer::PivotTableBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+PivotTable& PivotTableBuffer::createPivotTable()
+{
+ PivotTableVector::value_type xTable = std::make_shared<PivotTable>( *this );
+ maTables.push_back( xTable );
+ return *xTable;
+}
+
+void PivotTableBuffer::finalizeImport()
+{
+ if(maTables.empty())
+ return;
+
+ // Create formula groups. This needs to be done before pivot tables, because
+ // import may lead to calling ScDPObject::GetSource(), which calls ScDPObject::CreateObjects(),
+ // which will ensure all the cells are not dirty, causing recalculation even though
+ // ScDPObject::GetSource() doesn't need it. And the recalculation is slower without formula
+ // groups set up. Fixing that properly seems quite complex, given that do-everything approach
+ // of ScDPObject, so at least ensure the calculation is efficient.
+ ScDocument& rDoc = getDocImport().getDoc();
+ rDoc.RegroupFormulaCells( ScRange( 0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), rDoc.GetMaxTableNumber()));
+
+ maTables.forEachMem( &PivotTable::finalizeImport );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivottablefragment.cxx b/sc/source/filter/oox/pivottablefragment.cxx
new file mode 100644
index 0000000000..76d2abdcde
--- /dev/null
+++ b/sc/source/filter/oox/pivottablefragment.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivottablefragment.hxx>
+#include <pivottablebuffer.hxx>
+#include <biffhelper.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+PivotTableFieldContext::PivotTableFieldContext( WorksheetFragmentBase& rFragment, PivotTableField& rTableField ) :
+ WorksheetContextBase( rFragment ),
+ mrTableField( rTableField )
+{
+}
+
+ContextHandlerRef PivotTableFieldContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( pivotField ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( items ): return this;
+ case XLS_TOKEN( autoSortScope ): return this;
+ }
+ break;
+ case XLS_TOKEN( items ):
+ if( nElement == XLS_TOKEN( item ) ) mrTableField.importItem( rAttribs );
+ break;
+ case XLS_TOKEN( autoSortScope ):
+ if( nElement == XLS_TOKEN( pivotArea ) ) return this;
+ break;
+ case XLS_TOKEN( pivotArea ):
+ if( nElement == XLS_TOKEN( references ) ) return this;
+ break;
+ case XLS_TOKEN( references ):
+ if( nElement == XLS_TOKEN( reference ) ) { mrTableField.importReference( rAttribs ); return this; }
+ break;
+ case XLS_TOKEN( reference ):
+ if( nElement == XLS_TOKEN( x ) ) mrTableField.importReferenceItem( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFieldContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrTableField.importPivotField( rAttribs );
+}
+
+ContextHandlerRef PivotTableFieldContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_PTFIELD:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PTFITEMS: return this;
+ case BIFF12_ID_AUTOSORTSCOPE: return this;
+ }
+ break;
+ case BIFF12_ID_PTFITEMS:
+ if( nRecId == BIFF12_ID_PTFITEM ) mrTableField.importPTFItem( rStrm );
+ break;
+ case BIFF12_ID_AUTOSORTSCOPE:
+ if( nRecId == BIFF12_ID_PIVOTAREA ) return this;
+ break;
+ case BIFF12_ID_PIVOTAREA:
+ if( nRecId == BIFF12_ID_PTREFERENCES ) return this;
+ break;
+ case BIFF12_ID_PTREFERENCES:
+ if( nRecId == BIFF12_ID_PTREFERENCE ) { mrTableField.importPTReference( rStrm ); return this; }
+ break;
+ case BIFF12_ID_PTREFERENCE:
+ if( nRecId == BIFF12_ID_PTREFERENCEITEM ) mrTableField.importPTReferenceItem( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFieldContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrTableField.importPTField( rStrm );
+}
+
+PivotTableFilterContext::PivotTableFilterContext( WorksheetFragmentBase& rFragment, PivotTableFilter& rTableFilter ) :
+ WorksheetContextBase( rFragment ),
+ mrTableFilter( rTableFilter )
+{
+}
+
+ContextHandlerRef PivotTableFilterContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( filter ):
+ if( nElement == XLS_TOKEN( autoFilter ) ) return this;
+ break;
+ case XLS_TOKEN( autoFilter ):
+ if( nElement == XLS_TOKEN( filterColumn ) ) return this;
+ break;
+ case XLS_TOKEN( filterColumn ):
+ if( nElement == XLS_TOKEN( top10 ) ) mrTableFilter.importTop10( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFilterContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrTableFilter.importFilter( rAttribs );
+}
+
+ContextHandlerRef PivotTableFilterContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_PTFILTER:
+ if( nRecId == BIFF12_ID_AUTOFILTER ) return this;
+ break;
+ case BIFF12_ID_AUTOFILTER:
+ if( nRecId == BIFF12_ID_FILTERCOLUMN ) return this;
+ break;
+ case BIFF12_ID_FILTERCOLUMN:
+ if( nRecId == BIFF12_ID_TOP10FILTER ) mrTableFilter.importTop10Filter( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFilterContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrTableFilter.importPTFilter( rStrm );
+}
+
+PivotTableFragment::PivotTableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrPivotTable( getPivotTables().createPivotTable() )
+{
+}
+
+ContextHandlerRef PivotTableFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( pivotTableDefinition ) ) { mrPivotTable.importPivotTableDefinition( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( pivotTableDefinition ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( location ): mrPivotTable.importLocation( rAttribs, getSheetIndex() ); break;
+ case XLS_TOKEN( pivotFields ): return this;
+ case XLS_TOKEN( rowFields ): return this;
+ case XLS_TOKEN( colFields ): return this;
+ case XLS_TOKEN( pageFields ): return this;
+ case XLS_TOKEN( dataFields ): return this;
+ case XLS_TOKEN( filters ): return this;
+ case XLS_TOKEN(pivotTableStyleInfo):
+ mrPivotTable.putToInteropGrabBag("pivotTableStyleInfo", rAttribs);
+ break;
+ }
+ break;
+
+ case XLS_TOKEN( pivotFields ):
+ if( nElement == XLS_TOKEN( pivotField ) ) return new PivotTableFieldContext( *this, mrPivotTable.createTableField() );
+ break;
+ case XLS_TOKEN( rowFields ):
+ if( nElement == XLS_TOKEN( field ) ) mrPivotTable.importRowField( rAttribs );
+ break;
+ case XLS_TOKEN( colFields ):
+ if( nElement == XLS_TOKEN( field ) ) mrPivotTable.importColField( rAttribs );
+ break;
+ case XLS_TOKEN( pageFields ):
+ if( nElement == XLS_TOKEN( pageField ) ) mrPivotTable.importPageField( rAttribs );
+ break;
+ case XLS_TOKEN( dataFields ):
+ if( nElement == XLS_TOKEN( dataField ) ) mrPivotTable.importDataField( rAttribs );
+ break;
+ case XLS_TOKEN( filters ):
+ if( nElement == XLS_TOKEN( filter ) ) return new PivotTableFilterContext( *this, mrPivotTable.createTableFilter() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef PivotTableFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_PTDEFINITION ) { mrPivotTable.importPTDefinition( rStrm ); return this; }
+ break;
+
+ case BIFF12_ID_PTDEFINITION:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PTLOCATION: mrPivotTable.importPTLocation( rStrm, getSheetIndex() ); break;
+ case BIFF12_ID_PTFIELDS: return this;
+ case BIFF12_ID_PTROWFIELDS: mrPivotTable.importPTRowFields( rStrm ); break;
+ case BIFF12_ID_PTCOLFIELDS: mrPivotTable.importPTColFields( rStrm ); break;
+ case BIFF12_ID_PTPAGEFIELDS: return this;
+ case BIFF12_ID_PTDATAFIELDS: return this;
+ case BIFF12_ID_PTFILTERS: return this;
+ }
+ break;
+
+ case BIFF12_ID_PTFIELDS:
+ if( nRecId == BIFF12_ID_PTFIELD ) return new PivotTableFieldContext( *this, mrPivotTable.createTableField() );
+ break;
+ case BIFF12_ID_PTPAGEFIELDS:
+ if( nRecId == BIFF12_ID_PTPAGEFIELD ) mrPivotTable.importPTPageField( rStrm );
+ break;
+ case BIFF12_ID_PTDATAFIELDS:
+ if( nRecId == BIFF12_ID_PTDATAFIELD ) mrPivotTable.importPTDataField( rStrm );
+ break;
+ case BIFF12_ID_PTFILTERS:
+ if( nRecId == BIFF12_ID_PTFILTER ) return new PivotTableFilterContext( *this, mrPivotTable.createTableFilter() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* PivotTableFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 },
+ { BIFF12_ID_AUTOSORTSCOPE, BIFF12_ID_AUTOSORTSCOPE + 1 },
+ { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 },
+ { BIFF12_ID_PIVOTAREA, BIFF12_ID_PIVOTAREA + 1 },
+ { BIFF12_ID_PTCOLFIELDS, BIFF12_ID_PTCOLFIELDS + 1 },
+ { BIFF12_ID_PTDATAFIELD, BIFF12_ID_PTDATAFIELD + 1 },
+ { BIFF12_ID_PTDATAFIELDS, BIFF12_ID_PTDATAFIELDS + 1 },
+ { BIFF12_ID_PTDEFINITION, BIFF12_ID_PTDEFINITION + 35 },
+ { BIFF12_ID_PTFIELD, BIFF12_ID_PTFIELD + 1 },
+ { BIFF12_ID_PTFIELDS, BIFF12_ID_PTFIELDS + 1 },
+ { BIFF12_ID_PTFILTER, BIFF12_ID_PTFILTER + 1 },
+ { BIFF12_ID_PTFILTERS, BIFF12_ID_PTFILTERS + 1 },
+ { BIFF12_ID_PTFITEM, BIFF12_ID_PTFITEM - 1 },
+ { BIFF12_ID_PTFITEMS, BIFF12_ID_PTFITEMS + 1 },
+ { BIFF12_ID_PTLOCATION, BIFF12_ID_PTLOCATION - 1 },
+ { BIFF12_ID_PTPAGEFIELD, BIFF12_ID_PTPAGEFIELD + 1 },
+ { BIFF12_ID_PTPAGEFIELDS, BIFF12_ID_PTPAGEFIELDS + 1 },
+ { BIFF12_ID_PTREFERENCE, BIFF12_ID_PTREFERENCE + 1 },
+ { BIFF12_ID_PTREFERENCEITEM, BIFF12_ID_PTREFERENCEITEM + 1 },
+ { BIFF12_ID_PTREFERENCES, BIFF12_ID_PTREFERENCES + 1 },
+ { BIFF12_ID_PTROWFIELDS, BIFF12_ID_PTROWFIELDS + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/querytablebuffer.cxx b/sc/source/filter/oox/querytablebuffer.cxx
new file mode 100644
index 0000000000..1c5ee44648
--- /dev/null
+++ b/sc/source/filter/oox/querytablebuffer.cxx
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <querytablebuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/sheet/XAreaLink.hpp>
+#include <com/sun/star/sheet/XAreaLinks.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <connectionsbuffer.hxx>
+#include <defnamesbuffer.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt32 BIFF12_QUERYTABLE_HEADERS = 0x00000001;
+const sal_uInt32 BIFF12_QUERYTABLE_ROWNUMBERS = 0x00000002;
+const sal_uInt32 BIFF12_QUERYTABLE_DISABLEREFRESH = 0x00000004;
+const sal_uInt32 BIFF12_QUERYTABLE_BACKGROUND = 0x00000008;
+const sal_uInt32 BIFF12_QUERYTABLE_FIRSTBACKGROUND = 0x00000010;
+const sal_uInt32 BIFF12_QUERYTABLE_REFRESHONLOAD = 0x00000020;
+const sal_uInt32 BIFF12_QUERYTABLE_FILLFORMULAS = 0x00000100;
+const sal_uInt32 BIFF12_QUERYTABLE_SAVEDATA = 0x00000200;
+const sal_uInt32 BIFF12_QUERYTABLE_DISABLEEDIT = 0x00000400;
+const sal_uInt32 BIFF12_QUERYTABLE_PRESERVEFORMAT = 0x00000800;
+const sal_uInt32 BIFF12_QUERYTABLE_ADJUSTCOLWIDTH = 0x00001000;
+const sal_uInt32 BIFF12_QUERYTABLE_INTERMEDIATE = 0x00002000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYNUMFMT = 0x00004000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYFONT = 0x00008000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYALIGNMENT = 0x00010000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYBORDER = 0x00020000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYFILL = 0x00040000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYPROTECTION = 0x00080000;
+
+void lclAppendWebQueryTableName( OUStringBuffer& rTables, std::u16string_view rTableName )
+{
+ if( !rTableName.empty() )
+ {
+ if( !rTables.isEmpty() )
+ rTables.append( ';' );
+ rTables.append( OUString::Concat("HTML__") + rTableName );
+ }
+}
+
+void lclAppendWebQueryTableIndex( OUStringBuffer& rTables, sal_Int32 nTableIndex )
+{
+ if( nTableIndex > 0 )
+ {
+ if( !rTables.isEmpty() )
+ rTables.append( ';' );
+ rTables.append( "HTML_" + OUString::number( nTableIndex ) );
+ }
+}
+
+OUString lclBuildWebQueryTables( const WebPrModel::TablesVector& rTables )
+{
+ if( rTables.empty() )
+ return "HTML_tables";
+
+ OUStringBuffer aTables;
+ for( const auto& rTable : rTables )
+ {
+ if( rTable.has< OUString >() )
+ lclAppendWebQueryTableName( aTables, rTable.get< OUString >() );
+ else if( rTable.has< sal_Int32 >() )
+ lclAppendWebQueryTableIndex( aTables, rTable.get< sal_Int32 >() );
+ }
+ return aTables.makeStringAndClear();
+}
+
+Reference< XAreaLink > lclFindAreaLink(
+ const Reference< XAreaLinks >& rxAreaLinks, const ScAddress& rDestPos,
+ std::u16string_view rFileUrl, std::u16string_view rTables, std::u16string_view rFilterName, std::u16string_view rFilterOptions )
+{
+ try
+ {
+ Reference< XEnumerationAccess > xAreaLinksEA( rxAreaLinks, UNO_QUERY_THROW );
+ Reference< XEnumeration > xAreaLinksEnum( xAreaLinksEA->createEnumeration(), UNO_SET_THROW );
+ while( xAreaLinksEnum->hasMoreElements() )
+ {
+ Reference< XAreaLink > xAreaLink( xAreaLinksEnum->nextElement(), UNO_QUERY_THROW );
+ PropertySet aPropSet( xAreaLink );
+ CellRangeAddress aDestArea = xAreaLink->getDestArea();
+ OUString aString;
+ if( (rDestPos.Tab() == aDestArea.Sheet) && (rDestPos.Col() == aDestArea.StartColumn) && (rDestPos.Row() == aDestArea.StartRow) &&
+ (rTables == xAreaLink->getSourceArea()) &&
+ aPropSet.getProperty( aString, PROP_Url ) && (rFileUrl == aString) &&
+ aPropSet.getProperty( aString, PROP_Filter ) && (rFilterName == aString) &&
+ aPropSet.getProperty( aString, PROP_FilterOptions ) && (rFilterOptions == aString) )
+ return xAreaLink;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return Reference< XAreaLink >();
+}
+
+} // namespace
+
+QueryTableModel::QueryTableModel() :
+ mnConnId( -1 ),
+ mnGrowShrinkType( XML_insertDelete ),
+ mbHeaders( true ),
+ mbRowNumbers( false ),
+ mbDisableRefresh( false ),
+ mbBackground( true ),
+ mbFirstBackground( false ),
+ mbRefreshOnLoad( false ),
+ mbFillFormulas( false ),
+ mbRemoveDataOnSave( false ),
+ mbDisableEdit( false ),
+ mbPreserveFormat( true ),
+ mbAdjustColWidth( true ),
+ mbIntermediate( false )
+{
+}
+
+QueryTable::QueryTable( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void QueryTable::importQueryTable( const AttributeList& rAttribs )
+{
+ maModel.maDefName = rAttribs.getXString( XML_name, OUString() );
+ maModel.mnConnId = rAttribs.getInteger( XML_connectionId, -1 );
+ maModel.mnGrowShrinkType = rAttribs.getToken( XML_growShrinkType, XML_insertDelete );
+ maModel.mnAutoFormatId = rAttribs.getInteger( XML_autoFormatId, 0 );
+ maModel.mbHeaders = rAttribs.getBool( XML_headers, true );
+ maModel.mbRowNumbers = rAttribs.getBool( XML_rowNumbers, false );
+ maModel.mbDisableRefresh = rAttribs.getBool( XML_disableRefresh, false );
+ maModel.mbBackground = rAttribs.getBool( XML_backgroundRefresh, true );
+ maModel.mbFirstBackground = rAttribs.getBool( XML_firstBackgroundRefresh, false );
+ maModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maModel.mbFillFormulas = rAttribs.getBool( XML_fillFormulas, false );
+ maModel.mbRemoveDataOnSave = rAttribs.getBool( XML_removeDataOnSave, false );
+ maModel.mbDisableEdit = rAttribs.getBool( XML_disableEdit, false );
+ maModel.mbPreserveFormat = rAttribs.getBool( XML_preserveFormatting, true );
+ maModel.mbAdjustColWidth = rAttribs.getBool( XML_adjustColumnWidth, true );
+ maModel.mbIntermediate = rAttribs.getBool( XML_intermediate, false );
+ maModel.mbApplyNumFmt = rAttribs.getBool( XML_applyNumberFormats, false );
+ maModel.mbApplyFont = rAttribs.getBool( XML_applyFontFormats, false );
+ maModel.mbApplyAlignment = rAttribs.getBool( XML_applyAlignmentFormats, false );
+ maModel.mbApplyBorder = rAttribs.getBool( XML_applyBorderFormats, false );
+ maModel.mbApplyFill = rAttribs.getBool( XML_applyPatternFormats, false );
+ // OOXML and BIFF12 documentation differ: OOXML mentions width/height, BIFF12 mentions protection
+ maModel.mbApplyProtection = rAttribs.getBool( XML_applyWidthHeightFormats, false );
+}
+
+void QueryTable::importQueryTable( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags;
+ nFlags = rStrm.readuInt32();
+ maModel.mnAutoFormatId = rStrm.readuInt16();
+ maModel.mnConnId = rStrm.readInt32();
+ rStrm >> maModel.maDefName;
+
+ static const sal_Int32 spnGrowShrinkTypes[] = { XML_insertClear, XML_insertDelete, XML_overwriteClear };
+ maModel.mnGrowShrinkType = STATIC_ARRAY_SELECT( spnGrowShrinkTypes, extractValue< sal_uInt8 >( nFlags, 6, 2 ), XML_insertDelete );
+
+ maModel.mbHeaders = getFlag( nFlags, BIFF12_QUERYTABLE_HEADERS );
+ maModel.mbRowNumbers = getFlag( nFlags, BIFF12_QUERYTABLE_ROWNUMBERS );
+ maModel.mbDisableRefresh = getFlag( nFlags, BIFF12_QUERYTABLE_DISABLEREFRESH );
+ maModel.mbBackground = getFlag( nFlags, BIFF12_QUERYTABLE_BACKGROUND );
+ maModel.mbFirstBackground = getFlag( nFlags, BIFF12_QUERYTABLE_FIRSTBACKGROUND );
+ maModel.mbRefreshOnLoad = getFlag( nFlags, BIFF12_QUERYTABLE_REFRESHONLOAD );
+ maModel.mbFillFormulas = getFlag( nFlags, BIFF12_QUERYTABLE_FILLFORMULAS );
+ maModel.mbRemoveDataOnSave = !getFlag( nFlags, BIFF12_QUERYTABLE_SAVEDATA ); // flag negated in BIFF12
+ maModel.mbDisableEdit = getFlag( nFlags, BIFF12_QUERYTABLE_DISABLEEDIT );
+ maModel.mbPreserveFormat = getFlag( nFlags, BIFF12_QUERYTABLE_PRESERVEFORMAT );
+ maModel.mbAdjustColWidth = getFlag( nFlags, BIFF12_QUERYTABLE_ADJUSTCOLWIDTH );
+ maModel.mbIntermediate = getFlag( nFlags, BIFF12_QUERYTABLE_INTERMEDIATE );
+ maModel.mbApplyNumFmt = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYNUMFMT );
+ maModel.mbApplyFont = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYFONT );
+ maModel.mbApplyAlignment = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYALIGNMENT );
+ maModel.mbApplyBorder = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYBORDER );
+ maModel.mbApplyFill = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYFILL );
+ maModel.mbApplyProtection = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYPROTECTION );
+}
+
+void QueryTable::finalizeImport()
+{
+ ConnectionRef xConnection = getConnections().getConnection( maModel.mnConnId );
+ OSL_ENSURE( xConnection, "QueryTable::finalizeImport - missing connection object" );
+ if( !(xConnection && (xConnection->getConnectionType() == BIFF12_CONNECTION_HTML)) )
+ return;
+
+ // check that valid web query properties exist
+ const WebPrModel* pWebPr = xConnection->getModel().mxWebPr.get();
+ if( !pWebPr || pWebPr->mbXml )
+ return;
+
+ OUString aFileUrl = getBaseFilter().getAbsoluteUrl( pWebPr->maUrl );
+ if( aFileUrl.isEmpty() )
+ return;
+
+ // resolve destination cell range (stored as defined name containing the range)
+ OUString aDefName = maModel.maDefName.replace( ' ', '_' ).replace( '-', '_' );
+ DefinedNameRef xDefName = getDefinedNames().getByModelName( aDefName, getSheetIndex() );
+ OSL_ENSURE( xDefName, "QueryTable::finalizeImport - missing defined name" );
+ if( !xDefName )
+ return;
+
+ ScRange aDestRange;
+ bool bIsRange = xDefName->getAbsoluteRange( aDestRange ) && (aDestRange.aStart.Tab() == getSheetIndex());
+ OSL_ENSURE( bIsRange, "QueryTable::finalizeImport - defined name does not contain valid cell range" );
+ if( !(bIsRange && getAddressConverter().checkCellRange( aDestRange, false, true )) )
+ return;
+
+ // find tables mode: entire document, all tables, or specific tables
+ OUString aTables = pWebPr->mbHtmlTables ? lclBuildWebQueryTables( pWebPr->maTables ) : "HTML_all";
+ if( aTables.isEmpty() )
+ return;
+
+ try
+ {
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ Reference< XAreaLinks > xAreaLinks( aDocProps.getAnyProperty( PROP_AreaLinks ), UNO_QUERY_THROW );
+ CellAddress aDestPos( aDestRange.aStart.Tab(), aDestRange.aStart.Col(), aDestRange.aStart.Row() );
+ static constexpr OUString aFilterName = u"calc_HTML_WebQuery"_ustr;
+ xAreaLinks->insertAtPosition( aDestPos, aFileUrl, aTables, aFilterName, /*aFilterOptions*/"" );
+ // set refresh interval (convert minutes to seconds)
+ sal_Int32 nRefreshPeriod = xConnection->getModel().mnInterval * 60;
+ if( nRefreshPeriod > 0 )
+ {
+ PropertySet aPropSet( lclFindAreaLink( xAreaLinks, aDestRange.aStart, aFileUrl, aTables, aFilterName, /*aFilterOptions*/u"" ) );
+ aPropSet.setProperty( PROP_RefreshPeriod, nRefreshPeriod );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+QueryTableBuffer::QueryTableBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+QueryTable& QueryTableBuffer::createQueryTable()
+{
+ QueryTableVector::value_type xQueryTable = std::make_shared<QueryTable>( *this );
+ maQueryTables.push_back( xQueryTable );
+ return *xQueryTable;
+}
+
+void QueryTableBuffer::finalizeImport()
+{
+ maQueryTables.forEachMem( &QueryTable::finalizeImport );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/querytablefragment.cxx b/sc/source/filter/oox/querytablefragment.cxx
new file mode 100644
index 0000000000..a41bda46a1
--- /dev/null
+++ b/sc/source/filter/oox/querytablefragment.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 <querytablefragment.hxx>
+#include <querytablebuffer.hxx>
+#include <biffhelper.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+QueryTableFragment::QueryTableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrQueryTable( getQueryTables().createQueryTable() )
+{
+}
+
+ContextHandlerRef QueryTableFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( queryTable ) )
+ mrQueryTable.importQueryTable( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef QueryTableFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_QUERYTABLE )
+ mrQueryTable.importQueryTable( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* QueryTableFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_QUERYTABLE, BIFF12_ID_QUERYTABLE + 1 },
+ { BIFF12_ID_QUERYTABLEREFRESH, BIFF12_ID_QUERYTABLEREFRESH + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/revisionfragment.cxx b/sc/source/filter/oox/revisionfragment.cxx
new file mode 100644
index 0000000000..d8dc26c895
--- /dev/null
+++ b/sc/source/filter/oox/revisionfragment.cxx
@@ -0,0 +1,450 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <revisionfragment.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/core/fastparser.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <editeng/editobj.hxx>
+
+#include <chgtrack.hxx>
+#include <document.hxx>
+#include <compiler.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <chgviset.hxx>
+#include <richstringcontext.hxx>
+#include <tokenarray.hxx>
+
+#include <com/sun/star/util/DateTime.hpp>
+
+using namespace com::sun::star;
+
+namespace oox::xls {
+
+namespace {
+
+enum RevisionType
+{
+ REV_UNKNOWN = 0,
+ REV_CELLCHANGE,
+ REV_INSERTROW
+};
+
+/**
+ * For nc (new cell) or oc (old cell) elements under rcc (cell content
+ * revision).
+ */
+class RCCCellValueContext : public WorkbookContextBase
+{
+ sal_Int32 mnSheetIndex;
+ ScAddress& mrPos;
+ ScCellValue& mrCellValue;
+ sal_Int32 mnType;
+
+ RichStringRef mxRichString;
+
+public:
+ RCCCellValueContext(
+ RevisionLogFragment& rParent, sal_Int32 nSheetIndex, ScAddress& rPos, ScCellValue& rCellValue ) :
+ WorkbookContextBase(rParent),
+ mnSheetIndex(nSheetIndex),
+ mrPos(rPos),
+ mrCellValue(rCellValue),
+ mnType(-1) {}
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) override
+ {
+ if (nElement == XLS_TOKEN(is))
+ {
+ mxRichString = std::make_shared<RichString>();
+ return new RichStringContext(*this, mxRichString);
+ }
+
+ return this;
+ }
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(nc):
+ case XLS_TOKEN(oc):
+ importCell(rAttribs);
+ break;
+ default:
+ ;
+ }
+ }
+
+ virtual void onCharacters( const OUString& rChars ) override
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(v):
+ {
+ if (mnType == XML_n || mnType == XML_b)
+ mrCellValue.set(rChars.toDouble());
+ }
+ break;
+ case XLS_TOKEN(t):
+ {
+ if (mnType == XML_inlineStr)
+ {
+ ScDocument& rDoc = getScDocument();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ mrCellValue.set(rPool.intern(rChars));
+ }
+ }
+ break;
+ case XLS_TOKEN(f):
+ {
+ // formula string
+ ScDocument& rDoc = getScDocument();
+ ScCompiler aComp(rDoc, mrPos, formula::FormulaGrammar::GRAM_OOXML);
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(rChars);
+ if (!pArray)
+ break;
+
+ mrCellValue.set(new ScFormulaCell(rDoc, mrPos, std::move(pArray)));
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ virtual void onEndElement() override
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(nc):
+ case XLS_TOKEN(oc):
+ {
+ if (mrCellValue.isEmpty() && mxRichString)
+ {
+ // The value is a rich text string.
+ ScDocument& rDoc = getScDocument();
+ std::unique_ptr<EditTextObject> pTextObj = mxRichString->convert(rDoc.GetEditEngine(), nullptr);
+ if (pTextObj)
+ {
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ pTextObj->NormalizeString(rPool);
+ mrCellValue.set(std::move(pTextObj));
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+private:
+ void importCell( const AttributeList& rAttribs )
+ {
+ mnType = rAttribs.getToken(XML_t, XML_n);
+ OUString aRefStr = rAttribs.getString(XML_r, OUString());
+ if (!aRefStr.isEmpty())
+ {
+ mrPos.Parse(aRefStr, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
+ if (mnSheetIndex != -1)
+ mrPos.SetTab(mnSheetIndex-1);
+ }
+ }
+};
+
+struct RevisionMetadata
+{
+ OUString maUserName;
+ DateTime maDateTime;
+
+ RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
+ RevisionMetadata( const RevisionMetadata& r ) :
+ maUserName(r.maUserName), maDateTime(r.maDateTime) {}
+};
+
+}
+
+struct RevisionHeadersFragment::Impl
+{
+ std::map<OUString, RevisionMetadata> maRevData;
+
+ Impl() {}
+};
+
+RevisionHeadersFragment::RevisionHeadersFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase(rHelper, rFragmentPath),
+ mpImpl(new Impl) {}
+
+RevisionHeadersFragment::~RevisionHeadersFragment()
+{
+}
+
+oox::core::ContextHandlerRef RevisionHeadersFragment::onCreateContext(
+ sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+ return this;
+}
+
+void RevisionHeadersFragment::onStartElement( const AttributeList& rAttribs )
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(headers):
+ break;
+ case XLS_TOKEN(header):
+ importHeader(rAttribs);
+ break;
+ case XLS_TOKEN(sheetIdMap):
+ break;
+ case XLS_TOKEN(sheetId):
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionHeadersFragment::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(headers):
+ break;
+ case XLS_TOKEN(header):
+ break;
+ case XLS_TOKEN(sheetIdMap):
+ break;
+ case XLS_TOKEN(sheetId):
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionHeadersFragment::finalizeImport()
+{
+ ScDocument& rDoc = getScDocument();
+ std::unique_ptr<ScChangeTrack> pCT(new ScChangeTrack(rDoc));
+ OUString aSelfUser = pCT->GetUser(); // owner of this document.
+ pCT->SetUseFixDateTime(true);
+
+ const oox::core::Relations& rRels = getRelations();
+ for (const auto& [rRelId, rData] : mpImpl->maRevData)
+ {
+ OUString aPath = rRels.getFragmentPathFromRelId(rRelId);
+ if (aPath.isEmpty())
+ continue;
+
+ // Parse each revision log fragment.
+ pCT->SetUser(rData.maUserName);
+ pCT->SetFixDateTimeLocal(rData.maDateTime);
+ std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
+ rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
+ importOoxFragment(xFragment, *xParser);
+ }
+
+ pCT->SetUser(aSelfUser); // set the default user to the document owner.
+ pCT->SetUseFixDateTime(false);
+ rDoc.SetChangeTrack(std::move(pCT));
+
+ // Turn on visibility of tracked changes.
+ ScChangeViewSettings aSettings;
+ aSettings.SetShowChanges(true);
+ rDoc.SetChangeViewSettings(aSettings);
+}
+
+void RevisionHeadersFragment::importHeader( const AttributeList& rAttribs )
+{
+ OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
+ if (aRId.isEmpty())
+ // All bets are off if we don't have a relation ID.
+ return;
+
+ RevisionMetadata aMetadata;
+ OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
+ if (!aDateTimeStr.isEmpty())
+ {
+ util::DateTime aDateTime;
+ if (sax::Converter::parseDateTime(aDateTime, aDateTimeStr))
+ aMetadata.maDateTime = aDateTime;
+ else
+ SAL_WARN("sc.filter", "RevisionHeadersFragment: broken DateTime '" << aDateTimeStr << "'");
+ }
+
+ aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
+
+ mpImpl->maRevData.emplace(aRId, aMetadata);
+}
+
+struct RevisionLogFragment::Impl
+{
+ ScChangeTrack& mrChangeTrack;
+
+ sal_Int32 mnSheetIndex;
+
+ RevisionType meType;
+
+ // rcc
+ ScAddress maOldCellPos;
+ ScAddress maNewCellPos;
+ ScCellValue maOldCellValue;
+ ScCellValue maNewCellValue;
+
+ // rrc
+ ScRange maRange;
+
+ bool mbEndOfList;
+
+ explicit Impl( ScChangeTrack& rChangeTrack ) :
+ mrChangeTrack(rChangeTrack),
+ mnSheetIndex(-1),
+ meType(REV_UNKNOWN),
+ mbEndOfList(false) {}
+};
+
+RevisionLogFragment::RevisionLogFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
+ WorkbookFragmentBase(rHelper, rFragmentPath),
+ mpImpl(new Impl(rChangeTrack)) {}
+
+RevisionLogFragment::~RevisionLogFragment()
+{
+}
+
+oox::core::ContextHandlerRef RevisionLogFragment::onCreateContext(
+ sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch (nElement)
+ {
+ case XLS_TOKEN(nc):
+ return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
+ case XLS_TOKEN(oc):
+ return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
+ default:
+ ;
+ }
+ return this;
+}
+
+void RevisionLogFragment::onStartElement( const AttributeList& rAttribs )
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(rcc):
+ mpImpl->maNewCellPos.SetInvalid();
+ mpImpl->maOldCellPos.SetInvalid();
+ mpImpl->maNewCellValue.clear();
+ mpImpl->maOldCellValue.clear();
+ importRcc(rAttribs);
+ break;
+ case XLS_TOKEN(rrc):
+ importRrc(rAttribs);
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionLogFragment::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(rcc):
+ case XLS_TOKEN(rrc):
+ pushRevision();
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionLogFragment::finalizeImport() {}
+
+void RevisionLogFragment::importCommon( const AttributeList& rAttribs )
+{
+ mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
+}
+
+void RevisionLogFragment::importRcc( const AttributeList& rAttribs )
+{
+ importCommon(rAttribs);
+
+ mpImpl->meType = REV_CELLCHANGE;
+}
+
+void RevisionLogFragment::importRrc( const AttributeList& rAttribs )
+{
+ importCommon(rAttribs);
+
+ if (mpImpl->mnSheetIndex == -1)
+ // invalid sheet index, or sheet index not given.
+ return;
+
+ mpImpl->meType = REV_UNKNOWN;
+ sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
+ if (nAction == -1)
+ return;
+
+ OUString aRefStr = rAttribs.getString(XML_ref, OUString());
+ mpImpl->maRange.Parse(aRefStr, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
+ if (!mpImpl->maRange.IsValid())
+ return;
+
+ switch (nAction)
+ {
+ case XML_insertRow:
+ mpImpl->meType = REV_INSERTROW;
+ mpImpl->maRange.aEnd.SetCol(getScDocument().MaxCol());
+ mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
+ mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
+ break;
+ default:
+ // Unknown action type. Ignore it.
+ return;
+ }
+
+ mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
+}
+
+void RevisionLogFragment::pushRevision()
+{
+ switch (mpImpl->meType)
+ {
+ case REV_CELLCHANGE:
+ mpImpl->mrChangeTrack.AppendContentOnTheFly(
+ mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
+ break;
+ case REV_INSERTROW:
+ mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
+ break;
+ default:
+ ;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/richstring.cxx b/sc/source/filter/oox/richstring.cxx
new file mode 100644
index 0000000000..06db87e3c7
--- /dev/null
+++ b/sc/source/filter/oox/richstring.cxx
@@ -0,0 +1,499 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <richstring.hxx>
+#include <biffhelper.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <editeng/editobj.hxx>
+#include <osl/diagnose.h>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <editutil.hxx>
+
+#include <vcl/svapp.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt8 BIFF12_STRINGFLAG_FONTS = 0x01;
+const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS = 0x02;
+
+bool lclNeedsRichTextFormat( const oox::xls::Font* pFont )
+{
+ return pFont && pFont->needsRichTextFormat();
+}
+
+} // namespace
+
+RichStringPortion::RichStringPortion() :
+ mnFontId( -1 ),
+ mbConverted( false )
+{
+}
+
+void RichStringPortion::setText( const OUString& rText )
+{
+ maText = AttributeConversion::decodeXString(rText);
+}
+
+FontRef const & RichStringPortion::createFont(const WorkbookHelper& rHelper)
+{
+ mxFont = std::make_shared<Font>( rHelper, false );
+ return mxFont;
+}
+
+void RichStringPortion::setFontId( sal_Int32 nFontId )
+{
+ mnFontId = nFontId;
+}
+
+void RichStringPortion::finalizeImport(const WorkbookHelper& rHelper)
+{
+ if( mxFont )
+ mxFont->finalizeImport();
+ else if( mnFontId >= 0 )
+ mxFont = rHelper.getStyles().getFont( mnFontId );
+}
+
+void RichStringPortion::convert( const Reference< XText >& rxText, bool bReplace )
+{
+ if ( mbConverted )
+ return;
+
+ Reference< XTextRange > xRange;
+ if( bReplace )
+ xRange = rxText;
+ else
+ xRange = rxText->getEnd();
+ OSL_ENSURE( xRange.is(), "RichStringPortion::convert - cannot get text range interface" );
+
+ if( xRange.is() )
+ {
+ xRange->setString( maText );
+ if( mxFont )
+ {
+ PropertySet aPropSet( xRange );
+ mxFont->writeToPropertySet( aPropSet );
+ }
+ }
+
+ mbConverted = true;
+}
+
+void RichStringPortion::convert( ScEditEngineDefaulter& rEE, ESelection& rSelection, const oox::xls::Font* pFont )
+{
+ rSelection.nStartPos = rSelection.nEndPos;
+ rSelection.nStartPara = rSelection.nEndPara;
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+
+ const Font* pFontToUse = mxFont ? mxFont.get() : lclNeedsRichTextFormat( pFont ) ? pFont : nullptr;
+
+ if ( pFontToUse )
+ pFontToUse->fillToItemSet( aItemSet, true );
+
+ // #TODO need to manually adjust nEndPos ( and nEndPara ) to cater for any paragraphs
+ sal_Int32 nLastParaLoc = -1;
+ sal_Int32 nSearchIndex = maText.indexOf( '\n' );
+ sal_Int32 nParaOccurrence = 0;
+ while ( nSearchIndex != -1 )
+ {
+ nLastParaLoc = nSearchIndex;
+ ++nParaOccurrence;
+ rSelection.nEndPos = 0;
+ nSearchIndex = maText.indexOf( '\n', nSearchIndex + 1);
+ }
+
+ rSelection.nEndPara += nParaOccurrence;
+ if ( nLastParaLoc != -1 )
+ {
+ rSelection.nEndPos = maText.getLength() - 1 - nLastParaLoc;
+ }
+ else
+ {
+ rSelection.nEndPos = rSelection.nStartPos + maText.getLength();
+ }
+ rEE.QuickSetAttribs( aItemSet, rSelection );
+}
+
+void RichStringPortion::writeFontProperties( const Reference<XText>& rxText ) const
+{
+ PropertySet aPropSet(rxText);
+
+ if (mxFont)
+ mxFont->writeToPropertySet(aPropSet);
+}
+
+void FontPortionModel::read( SequenceInputStream& rStrm )
+{
+ mnPos = rStrm.readuInt16();
+ mnFontId = rStrm.readuInt16();
+}
+
+void FontPortionModelList::appendPortion( const FontPortionModel& rPortion )
+{
+ // #i33341# real life -- same character index may occur several times
+ OSL_ENSURE( mvModels.empty() || (mvModels.back().mnPos <= rPortion.mnPos), "FontPortionModelList::appendPortion - wrong char order" );
+ if( mvModels.empty() || (mvModels.back().mnPos < rPortion.mnPos) )
+ mvModels.push_back( rPortion );
+ else
+ mvModels.back().mnFontId = rPortion.mnFontId;
+}
+
+void FontPortionModelList::importPortions( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCount = rStrm.readInt32();
+ mvModels.clear();
+ if( nCount > 0 )
+ {
+ mvModels.reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 4 ) );
+ /* #i33341# real life -- same character index may occur several times
+ -> use appendPortion() to validate string position. */
+ FontPortionModel aPortion;
+ for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
+ {
+ aPortion.read( rStrm );
+ appendPortion( aPortion );
+ }
+ }
+}
+
+PhoneticDataModel::PhoneticDataModel() :
+ mnFontId( -1 ),
+ mnType( XML_fullwidthKatakana ),
+ mnAlignment( XML_left )
+{
+}
+
+void PhoneticDataModel::setBiffData( sal_Int32 nType, sal_Int32 nAlignment )
+{
+ static const sal_Int32 spnTypeIds[] = { XML_halfwidthKatakana, XML_fullwidthKatakana, XML_hiragana, XML_noConversion };
+ mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_fullwidthKatakana );
+
+ static const sal_Int32 spnAlignments[] = { XML_noControl, XML_left, XML_center, XML_distributed };
+ mnAlignment = STATIC_ARRAY_SELECT( spnAlignments, nAlignment, XML_left );
+}
+
+PhoneticSettings::PhoneticSettings( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PhoneticSettings::importPhoneticPr( const AttributeList& rAttribs )
+{
+ maModel.mnFontId = rAttribs.getInteger( XML_fontId, -1 );
+ maModel.mnType = rAttribs.getToken( XML_type, XML_fullwidthKatakana );
+ maModel.mnAlignment = rAttribs.getToken( XML_alignment, XML_left );
+}
+
+void PhoneticSettings::importPhoneticPr( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFontId;
+ sal_Int32 nType, nAlignment;
+ nFontId = rStrm.readuInt16();
+ nType = rStrm.readInt32();
+ nAlignment = rStrm.readInt32();
+ maModel.mnFontId = nFontId;
+ maModel.setBiffData( nType, nAlignment );
+}
+
+void PhoneticSettings::importStringData( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFontId, nFlags;
+ nFontId = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ maModel.mnFontId = nFontId;
+ maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
+}
+
+RichStringPhonetic::RichStringPhonetic() :
+ mnBasePos( -1 ),
+ mnBaseEnd( -1 )
+{
+}
+
+void RichStringPhonetic::setText( const OUString& rText )
+{
+ maText = rText;
+}
+
+void RichStringPhonetic::importPhoneticRun( const AttributeList& rAttribs )
+{
+ mnBasePos = rAttribs.getInteger( XML_sb, -1 );
+ mnBaseEnd = rAttribs.getInteger( XML_eb, -1 );
+}
+
+void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd )
+{
+ mnBasePos = nBasePos;
+ mnBaseEnd = nBaseEnd;
+}
+
+void PhoneticPortionModel::read( SequenceInputStream& rStrm )
+{
+ mnPos = rStrm.readuInt16();
+ mnBasePos = rStrm.readuInt16();
+ mnBaseLen = rStrm.readuInt16();
+}
+
+void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel& rPortion )
+{
+ // same character index may occur several times
+ OSL_ENSURE( mvModels.empty() || ((mvModels.back().mnPos <= rPortion.mnPos) &&
+ (mvModels.back().mnBasePos + mvModels.back().mnBaseLen <= rPortion.mnBasePos)),
+ "PhoneticPortionModelList::appendPortion - wrong char order" );
+ if( mvModels.empty() || (mvModels.back().mnPos < rPortion.mnPos) )
+ {
+ mvModels.push_back( rPortion );
+ }
+ else if( mvModels.back().mnPos == rPortion.mnPos )
+ {
+ mvModels.back().mnBasePos = rPortion.mnBasePos;
+ mvModels.back().mnBaseLen = rPortion.mnBaseLen;
+ }
+}
+
+void PhoneticPortionModelList::importPortions( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCount = rStrm.readInt32();
+ mvModels.clear();
+ if( nCount > 0 )
+ {
+ mvModels.reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 6 ) );
+ PhoneticPortionModel aPortion;
+ for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
+ {
+ aPortion.read( rStrm );
+ appendPortion( aPortion );
+ }
+ }
+}
+
+sal_Int32 RichString::importText(const AttributeList& rAttribs)
+{
+ setAttributes(rAttribs);
+
+ return createPortion();
+}
+
+sal_Int32 RichString::importRun()
+{
+ return createPortion();
+}
+
+void RichString::setAttributes(const AttributeList& rAttribs)
+{
+ auto aAttrSpace = rAttribs.getString(oox::NMSP_xml | oox::XML_space);
+ if (aAttrSpace && *aAttrSpace == "preserve")
+ mbPreserveSpace = true;
+}
+
+RichStringPhoneticRef RichString::importPhoneticRun( const AttributeList& rAttribs )
+{
+ RichStringPhoneticRef xPhonetic = createPhonetic();
+ xPhonetic->importPhoneticRun( rAttribs );
+ return xPhonetic;
+}
+
+void RichString::importPhoneticPr( const AttributeList& rAttribs, const WorkbookHelper& rHelper )
+{
+ if (!mxPhonSettings)
+ mxPhonSettings.reset(new PhoneticSettings(rHelper));
+ mxPhonSettings->importPhoneticPr( rAttribs );
+}
+
+void RichString::importString( SequenceInputStream& rStrm, bool bRich, const WorkbookHelper& rHelper )
+{
+ sal_uInt8 nFlags = bRich ? rStrm.readuInt8() : 0;
+ OUString aBaseText = BiffHelper::readString( rStrm );
+
+ if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_FONTS ) )
+ {
+ FontPortionModelList aPortions;
+ aPortions.importPortions( rStrm );
+ createTextPortions( aBaseText, aPortions );
+ }
+ else
+ {
+ getPortion(createPortion()).setText( aBaseText );
+ }
+
+ if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_PHONETICS ) )
+ {
+ OUString aPhoneticText = BiffHelper::readString( rStrm );
+ PhoneticPortionModelList aPortions;
+ aPortions.importPortions( rStrm );
+ if (!mxPhonSettings)
+ mxPhonSettings.reset(new PhoneticSettings(rHelper));
+ mxPhonSettings->importStringData( rStrm );
+ createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() );
+ }
+}
+
+void RichString::finalizeImport(const WorkbookHelper& rHelper)
+{
+ for (RichStringPortion& rPortion : maTextPortions)
+ rPortion.finalizeImport( rHelper );
+}
+
+bool RichString::extractPlainString( OUString& orString, const oox::xls::Font* pFirstPortionFont ) const
+{
+ if( !maPhonPortions.empty() )
+ return false;
+ if( maTextPortions.empty() )
+ {
+ orString.clear();
+ return true;
+ }
+ if( (maTextPortions.size() == 1) && !maTextPortions.front().hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont ) )
+ {
+ orString = maTextPortions.front().getText();
+ return orString.indexOf( '\x0A' ) < 0;
+ }
+ return false;
+}
+
+void RichString::convert( const Reference< XText >& rxText )
+{
+ if (maTextPortions.size() == 1)
+ {
+ // Set text directly to the cell when the string has only one portion.
+ // It's much faster this way.
+ const RichStringPortion& rPtn = maTextPortions.front();
+ rxText->setString(rPtn.getText());
+ rPtn.writeFontProperties(rxText);
+ return;
+ }
+
+ bool bReplaceOld = true;
+ for( auto& rTextPortion : maTextPortions )
+ {
+ rTextPortion.convert( rxText, bReplaceOld );
+ bReplaceOld = false; // do not replace first portion text with following portions
+ }
+}
+
+OUString RichString::getStringContent() const
+{
+ OUStringBuffer sString;
+ for( auto& rTextPortion : maTextPortions )
+ sString.append(rTextPortion.getText());
+ return sString.makeStringAndClear();
+}
+
+std::unique_ptr<EditTextObject> RichString::convert( ScEditEngineDefaulter& rEE, const oox::xls::Font* pFirstPortionFont )
+{
+ ESelection aSelection;
+
+ OUString sString(getStringContent());
+
+ // fdo#84370 - diving into editeng is not thread safe.
+ SolarMutexGuard aGuard;
+
+ rEE.SetTextCurrentDefaults(sString);
+
+ for( auto& rTextPortion : maTextPortions )
+ {
+ rTextPortion.convert( rEE, aSelection, pFirstPortionFont );
+ pFirstPortionFont = nullptr;
+ }
+
+ return rEE.CreateTextObject();
+}
+
+// private --------------------------------------------------------------------
+
+sal_Int32 RichString::createPortion()
+{
+ maTextPortions.emplace_back();
+ return maTextPortions.size() - 1;
+}
+
+RichStringPhoneticRef RichString::createPhonetic()
+{
+ RichStringPhoneticRef xPhonetic = std::make_shared<RichStringPhonetic>();
+ maPhonPortions.push_back( xPhonetic );
+ return xPhonetic;
+}
+
+void RichString::createTextPortions( std::u16string_view aText, FontPortionModelList& rPortions )
+{
+ maTextPortions.clear();
+ if( aText.empty() )
+ return;
+
+ sal_Int32 nStrLen = aText.size();
+ // add leading and trailing string position to ease the following loop
+ if( rPortions.empty() || (rPortions.front().mnPos > 0) )
+ rPortions.insert( rPortions.begin(), FontPortionModel( 0 ) );
+ if( rPortions.back().mnPos < nStrLen )
+ rPortions.push_back( FontPortionModel( nStrLen ) );
+
+ // create all string portions according to the font id vector
+ for( ::std::vector< FontPortionModel >::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
+ {
+ sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
+ if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
+ {
+ RichStringPortion& rPortion = getPortion(createPortion());
+ rPortion.setText( OUString(aText.substr( aIt->mnPos, nPortionLen )) );
+ rPortion.setFontId( aIt->mnFontId );
+ }
+ }
+}
+
+void RichString::createPhoneticPortions( std::u16string_view aText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen )
+{
+ maPhonPortions.clear();
+ if( aText.empty())
+ return;
+
+ sal_Int32 nStrLen = aText.size();
+ // no portions - assign phonetic text to entire base text
+ if( rPortions.empty() )
+ rPortions.push_back( PhoneticPortionModel( 0, 0, nBaseLen ) );
+ // add trailing string position to ease the following loop
+ if( rPortions.back().mnPos < nStrLen )
+ rPortions.push_back( PhoneticPortionModel( nStrLen, nBaseLen, 0 ) );
+
+ // create all phonetic portions according to the portions vector
+ for( ::std::vector< PhoneticPortionModel >::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
+ {
+ sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
+ if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
+ {
+ RichStringPhoneticRef xPhonetic = createPhonetic();
+ xPhonetic->setText( OUString(aText.substr( aIt->mnPos, nPortionLen )) );
+ xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen );
+ }
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/richstringcontext.cxx b/sc/source/filter/oox/richstringcontext.cxx
new file mode 100644
index 0000000000..0c83fff2e9
--- /dev/null
+++ b/sc/source/filter/oox/richstringcontext.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 <richstringcontext.hxx>
+
+#include <stylesfragment.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+ContextHandlerRef RichStringContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ {
+ switch( nElement )
+ {
+ case XLS_TOKEN( t ):
+ mnPortionIdx = mxString->importText(rAttribs);
+ return this; // collect text in onCharacters()
+ case XLS_TOKEN( r ):
+ mnPortionIdx = mxString->importRun();
+ return this;
+ case XLS_TOKEN( rPh ):
+ mxPhonetic = mxString->importPhoneticRun( rAttribs );
+ return this;
+ case XLS_TOKEN( phoneticPr ):
+ mxString->importPhoneticPr( rAttribs, *this );
+ break;
+ }
+ }
+ else switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( r ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( rPr ):
+ if( mnPortionIdx != -1 )
+ return new FontContext( *this, mxString->getPortion(mnPortionIdx).createFont(*this) );
+ break;
+
+ case XLS_TOKEN( t ):
+ mxString->setAttributes(rAttribs);
+ return this; // collect portion text in onCharacters()
+ }
+ break;
+
+ case XLS_TOKEN( rPh ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( t ):
+ mxString->setAttributes(rAttribs);
+ return this; // collect phonetic text in onCharacters()
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void RichStringContext::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( t ) ) ) switch( getParentElement() )
+ {
+ case XLS_TOKEN( rPh ):
+ if( mxPhonetic )
+ mxPhonetic->setText( rChars );
+ break;
+ default:
+ if( mnPortionIdx != -1 )
+ mxString->getPortion(mnPortionIdx).setText( rChars );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/scenariobuffer.cxx b/sc/source/filter/oox/scenariobuffer.cxx
new file mode 100644
index 0000000000..44ad528146
--- /dev/null
+++ b/sc/source/filter/oox/scenariobuffer.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 <scenariobuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XScenarios.hpp>
+#include <com/sun/star/sheet/XScenariosSupplier.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+ScenarioCellModel::ScenarioCellModel() :
+ mnNumFmtId( 0 ),
+ mbDeleted( false )
+{
+}
+
+ScenarioModel::ScenarioModel() :
+ mbLocked( false ),
+ mbHidden( false ),
+ mbActive( false )
+{
+}
+
+Scenario::Scenario( const WorkbookHelper& rHelper, sal_Int16 nSheet, bool bIsActive ) :
+ WorkbookHelper( rHelper ),
+ mnSheet( nSheet )
+{
+ maModel.mbActive = bIsActive;
+}
+
+void Scenario::importScenario( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maComment = rAttribs.getXString( XML_comment, OUString() );
+ maModel.maUser = rAttribs.getXString( XML_user, OUString() );
+ maModel.mbLocked = rAttribs.getBool( XML_locked, false );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+}
+
+void Scenario::importInputCells( const AttributeList& rAttribs )
+{
+ ScenarioCellModel aModel;
+ AddressConverter::convertToCellAddressUnchecked( aModel.maPos, rAttribs.getString( XML_r, OUString() ), mnSheet );
+ aModel.maValue = rAttribs.getXString( XML_val, OUString() );
+ aModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ aModel.mbDeleted = rAttribs.getBool( XML_deleted, false );
+ maCells.push_back( aModel );
+}
+
+void Scenario::importScenario( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 ); // cell count
+ // two longs instead of flag field
+ maModel.mbLocked = rStrm.readInt32() != 0;
+ maModel.mbHidden = rStrm.readInt32() != 0;
+ rStrm >> maModel.maName >> maModel.maComment >> maModel.maUser;
+}
+
+void Scenario::importInputCells( SequenceInputStream& rStrm )
+{
+ // TODO: where is the deleted flag?
+ ScenarioCellModel aModel;
+ BinAddress aPos;
+ rStrm >> aPos;
+ rStrm.skip( 8 );
+ aModel.mnNumFmtId = rStrm.readuInt16();
+ rStrm >> aModel.maValue;
+ AddressConverter::convertToCellAddressUnchecked( aModel.maPos, aPos, mnSheet );
+ maCells.push_back( aModel );
+}
+
+void Scenario::finalizeImport()
+{
+ AddressConverter& rAddrConv = getAddressConverter();
+ ScRangeList aRanges;
+ for( const auto& rCell : maCells )
+ if( !rCell.mbDeleted && rAddrConv.checkCellAddress( rCell.maPos, true ) )
+ aRanges.push_back( ScRange(rCell.maPos, rCell.maPos) );
+
+ if( aRanges.empty() || maModel.maName.isEmpty() )
+ return;
+
+ try
+ {
+ /* Find an unused name for the scenario (Calc stores scenario data in
+ hidden sheets named after the scenario following the base sheet). */
+ Reference< XNameAccess > xSheetsNA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ OUString aScenName = ContainerHelper::getUnusedName( xSheetsNA, maModel.maName, '_' );
+
+ // create the new scenario sheet
+ Reference< XScenariosSupplier > xScenariosSupp( getSheetFromDoc( mnSheet ), UNO_QUERY_THROW );
+ Reference< XScenarios > xScenarios( xScenariosSupp->getScenarios(), UNO_SET_THROW );
+ xScenarios->addNewByName( aScenName, AddressConverter::toApiSequence(aRanges), maModel.maComment );
+
+ // write scenario cell values
+ Reference< XSpreadsheet > xSheet( getSheetFromDoc( aScenName ), UNO_SET_THROW );
+ for( const auto& rCell : maCells )
+ {
+ if( !rCell.mbDeleted ) try
+ {
+ // use XCell::setFormula to auto-detect values and strings
+ Reference< XCell > xCell( xSheet->getCellByPosition( rCell.maPos.Col(), rCell.maPos.Row() ), UNO_SET_THROW );
+ xCell->setFormula( rCell.maValue );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ // scenario properties
+ PropertySet aPropSet( xScenarios->getByName( aScenName ) );
+ aPropSet.setProperty( PROP_IsActive, maModel.mbActive );
+ aPropSet.setProperty( PROP_CopyBack, false );
+ aPropSet.setProperty( PROP_CopyStyles, false );
+ aPropSet.setProperty( PROP_CopyFormulas, false );
+ aPropSet.setProperty( PROP_Protected, maModel.mbLocked );
+ // #112621# do not show/print scenario border
+ aPropSet.setProperty( PROP_ShowBorder, false );
+ aPropSet.setProperty( PROP_PrintBorder, false );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+SheetScenariosModel::SheetScenariosModel() :
+ mnCurrent( 0 ),
+ mnShown( 0 )
+{
+}
+
+SheetScenarios::SheetScenarios( const WorkbookHelper& rHelper, sal_Int16 nSheet ) :
+ WorkbookHelper( rHelper ),
+ mnSheet( nSheet )
+{
+}
+
+void SheetScenarios::importScenarios( const AttributeList& rAttribs )
+{
+ maModel.mnCurrent = rAttribs.getInteger( XML_current, 0 );
+ maModel.mnShown = rAttribs.getInteger( XML_show, 0 );
+}
+
+void SheetScenarios::importScenarios( SequenceInputStream& rStrm )
+{
+ maModel.mnCurrent = rStrm.readuInt16();
+ maModel.mnShown = rStrm.readuInt16();
+}
+
+Scenario& SheetScenarios::createScenario()
+{
+ bool bIsActive = maScenarios.size() == static_cast<sal_uInt32>(maModel.mnShown);
+ ScenarioVector::value_type xScenario = std::make_shared<Scenario>( *this, mnSheet, bIsActive );
+ maScenarios.push_back( xScenario );
+ return *xScenario;
+}
+
+void SheetScenarios::finalizeImport()
+{
+ maScenarios.forEachMem( &Scenario::finalizeImport );
+}
+
+ScenarioBuffer::ScenarioBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+SheetScenarios& ScenarioBuffer::createSheetScenarios( sal_Int16 nSheet )
+{
+ SheetScenariosMap::mapped_type& rxSheetScens = maSheetScenarios[ nSheet ];
+ if( !rxSheetScens )
+ rxSheetScens = std::make_shared<SheetScenarios>( *this, nSheet );
+ return *rxSheetScens;
+}
+
+void ScenarioBuffer::finalizeImport()
+{
+ maSheetScenarios.forEachMem( &SheetScenarios::finalizeImport );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/scenariocontext.cxx b/sc/source/filter/oox/scenariocontext.cxx
new file mode 100644
index 0000000000..2e192d4a3d
--- /dev/null
+++ b/sc/source/filter/oox/scenariocontext.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 <scenariocontext.hxx>
+#include <biffhelper.hxx>
+
+#include <scenariobuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+ScenarioContext::ScenarioContext( WorksheetContextBase& rParent, SheetScenarios& rSheetScenarios ) :
+ WorksheetContextBase( rParent ),
+ mrScenario( rSheetScenarios.createScenario() )
+{
+}
+
+ContextHandlerRef ScenarioContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( scenario ):
+ if( nElement == XLS_TOKEN( inputCells ) ) mrScenario.importInputCells( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenarioContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrScenario.importScenario( rAttribs );
+}
+
+ContextHandlerRef ScenarioContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_SCENARIO:
+ if( nRecId == BIFF12_ID_INPUTCELLS ) mrScenario.importInputCells( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenarioContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrScenario.importScenario( rStrm );
+}
+
+ScenariosContext::ScenariosContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment ),
+ mrSheetScenarios( getScenarios().createSheetScenarios( getSheetIndex() ) )
+{
+}
+
+ContextHandlerRef ScenariosContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( scenarios ):
+ if( nElement == XLS_TOKEN( scenario ) ) return new ScenarioContext( *this, mrSheetScenarios );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenariosContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrSheetScenarios.importScenarios( rAttribs );
+}
+
+ContextHandlerRef ScenariosContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_SCENARIOS:
+ if( nRecId == BIFF12_ID_SCENARIO ) return new ScenarioContext( *this, mrSheetScenarios );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenariosContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrSheetScenarios.importScenarios( rStrm );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sharedstringsbuffer.cxx b/sc/source/filter/oox/sharedstringsbuffer.cxx
new file mode 100644
index 0000000000..6198c8e130
--- /dev/null
+++ b/sc/source/filter/oox/sharedstringsbuffer.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 <sharedstringsbuffer.hxx>
+
+namespace oox::xls {
+
+SharedStringsBuffer::SharedStringsBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+RichStringRef SharedStringsBuffer::createRichString()
+{
+ RichStringRef xString = std::make_shared<RichString>();
+ maStrings.push_back( xString );
+ return xString;
+}
+
+void SharedStringsBuffer::finalizeImport()
+{
+ for (auto & rString : maStrings)
+ rString->finalizeImport(*this);
+}
+
+RichStringRef SharedStringsBuffer::getString( sal_Int32 nStringId ) const
+{
+ return maStrings.get( nStringId );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sharedstringsfragment.cxx b/sc/source/filter/oox/sharedstringsfragment.cxx
new file mode 100644
index 0000000000..841d2b1d3a
--- /dev/null
+++ b/sc/source/filter/oox/sharedstringsfragment.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 <sharedstringsfragment.hxx>
+#include <biffhelper.hxx>
+
+#include <richstringcontext.hxx>
+#include <sharedstringsbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+SharedStringsFragment::SharedStringsFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef SharedStringsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( sst ) )
+ return this;
+ break;
+
+ case XLS_TOKEN( sst ):
+ if( nElement == XLS_TOKEN( si ) )
+ return new RichStringContext( *this, getSharedStrings().createRichString() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef SharedStringsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_SST )
+ return this;
+ break;
+
+ case BIFF12_ID_SST:
+ if( nRecId == BIFF12_ID_SI )
+ getSharedStrings().createRichString()->importString( rStrm, true, *this );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* SharedStringsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_SST, BIFF12_ID_SST + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void SharedStringsFragment::finalizeImport()
+{
+ getSharedStrings().finalizeImport();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sheetdatabuffer.cxx b/sc/source/filter/oox/sheetdatabuffer.cxx
new file mode 100644
index 0000000000..98d50c2a05
--- /dev/null
+++ b/sc/source/filter/oox/sheetdatabuffer.cxx
@@ -0,0 +1,814 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sheetdatabuffer.hxx>
+#include <patterncache.hxx>
+
+#include <algorithm>
+#include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/table/XCell.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <editeng/boxitem.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <formulaparser.hxx>
+#include <sharedstringsbuffer.hxx>
+#include <unitconverter.hxx>
+#include <rangelst.hxx>
+#include <document.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+#include <paramisc.hxx>
+#include <patattr.hxx>
+#include <documentimport.hxx>
+#include <formulabuffer.hxx>
+#include <numformat.hxx>
+#include <sax/tools/converter.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+CellModel::CellModel() :
+ mnCellType( XML_TOKEN_INVALID ),
+ mnXfId( -1 ),
+ mbShowPhonetic( false )
+{
+}
+
+CellFormulaModel::CellFormulaModel() :
+ mnFormulaType( XML_TOKEN_INVALID ),
+ mnSharedId( -1 )
+{
+}
+
+bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
+{
+ return (maFormulaRef.aStart == rCellAddr );
+}
+
+bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
+{
+ return
+ (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
+ (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
+ (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
+}
+
+DataTableModel::DataTableModel() :
+ mb2dTable( false ),
+ mbRowTable( false ),
+ mbRef1Deleted( false ),
+ mbRef2Deleted( false )
+{
+}
+
+SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ mbPendingSharedFmla( false )
+{
+}
+
+void SheetDataBuffer::setBlankCell( const CellModel& rModel )
+{
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
+{
+ getDocImport().setNumericCell(rModel.maCellAddr, fValue);
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
+{
+ if (!rText.isEmpty())
+ getDocImport().setStringCell(rModel.maCellAddr, rText);
+
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
+{
+ OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" );
+ const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
+ const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
+ bool bSingleLine = pXf ? !pXf->getAlignment().getModel().mbWrapText : false;
+ OUString aText;
+ if( rxString->extractPlainString( aText, pFirstPortionFont ) )
+ {
+ setStringCell( rModel, aText );
+ }
+ else
+ {
+ putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont, bSingleLine );
+ setCellFormat( rModel );
+ }
+}
+
+void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
+{
+ RichStringRef xString = getSharedStrings().getString( nStringId );
+ if( xString )
+ setStringCell( rModel, xString );
+ else
+ setBlankCell( rModel );
+}
+
+void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
+{
+ // write serial date/time value into the cell
+ double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
+ setValueCell( rModel, fSerial );
+ // set appropriate number format
+ using namespace ::com::sun::star::util::NumberFormat;
+ sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
+ // set number format
+ try
+ {
+ Reference< XNumberFormatsSupplier > xNumFmtsSupp( static_cast<cppu::OWeakObject*>(getDocument().get()), UNO_QUERY_THROW );
+ Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
+ sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
+ PropertySet aPropSet( getCell( rModel.maCellAddr ) );
+ aPropSet.setProperty( PROP_NumberFormat, nIndex );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
+{
+ getFormulaBuffer().setCellFormula(
+ rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
+
+ // #108770# set 'Standard' number format for all Boolean cells
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
+{
+ // Using the formula compiler now we can simply pass on the error string.
+ getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
+{
+ setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
+}
+
+void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
+{
+ css::util::DateTime aDateTime;
+ if (!sax::Converter::parseDateTime( aDateTime, rDateString))
+ {
+ SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
+ // At least don't lose data.
+ setStringCell( rModel, rDateString);
+ return;
+ }
+
+ double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
+ setValueCell( rModel, fSerial);
+}
+
+void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
+{
+ BinAddress aAddr(rAddr);
+ maSharedFormulas[aAddr] = rTokens;
+ if( mbPendingSharedFmla )
+ setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
+}
+
+void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
+{
+ mbPendingSharedFmla = false;
+ ApiTokenSequence aTokens;
+
+ /* Detect special token passed as placeholder for array formulas, shared
+ formulas, and table operations. In BIFF, these formulas are represented
+ by a single tExp resp. tTbl token. If the formula parser finds these
+ tokens, it puts a single OPCODE_BAD token with the base address and
+ formula type into the token sequence. This information will be
+ extracted here, and in case of a shared formula, the shared formula
+ buffer will generate the resulting formula token array. */
+ ApiSpecialTokenInfo aTokenInfo;
+ if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
+ {
+ /* The second member of the token info is set to true, if the formula
+ represents a table operation, which will be skipped. In BIFF12 it
+ is not possible to distinguish array and shared formulas
+ (BIFF5/BIFF8 provide this information with a special flag in the
+ FORMULA record). */
+ if( !aTokenInfo.Second )
+ {
+ /* Construct the token array representing the shared formula. If
+ the returned sequence is empty, the definition of the shared
+ formula has not been loaded yet, or the cell is part of an
+ array formula. In this case, the cell will be remembered. After
+ reading the formula definition it will be retried to insert the
+ formula via retryPendingSharedFormulaCell(). */
+ ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
+ aTokens = resolveSharedFormula( aTokenAddr );
+ if( !aTokens.hasElements() )
+ {
+ maSharedFmlaAddr = rModel.maCellAddr;
+ maSharedBaseAddr = aTokenAddr;
+ mbPendingSharedFmla = true;
+ }
+ }
+ }
+ else
+ {
+ // simple formula, use the passed token array
+ aTokens = rTokens;
+ }
+
+ setCellFormula( rModel.maCellAddr, aTokens );
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
+{
+ /* Array formulas will be inserted later in finalizeImport(). This is
+ needed to not disturb collecting all the cells, which will be put into
+ the sheet in large blocks to increase performance. */
+ maArrayFormulas.emplace_back( rRange, rTokens );
+}
+
+void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
+{
+ /* Table operations will be inserted later in finalizeImport(). This is
+ needed to not disturb collecting all the cells, which will be put into
+ the sheet in large blocks to increase performance. */
+ maTableOperations.emplace_back( rRange, rModel );
+}
+
+void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
+{
+ // set row formatting
+ if( bCustomFormat )
+ {
+ // try to expand cached row range, if formatting is equal
+ if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
+ {
+
+ maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
+ maXfIdRowRange.set( nRow, nXfId );
+ }
+ }
+ else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
+ {
+ // finish last cached row range
+ maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
+ maXfIdRowRange.set( -1, -1 );
+ }
+}
+
+void SheetDataBuffer::setMergedRange( const ScRange& rRange )
+{
+ maMergedRanges.emplace_back( rRange );
+}
+
+typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
+
+static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
+{
+ Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
+ if ( !pXf1 )
+ return;
+
+ auto it = std::find_if(rMap.begin(), rMap.end(),
+ [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
+ if (rEntry.first.second != nFormatId)
+ return false;
+ Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
+ return *pXf1 == *pXf2;
+ });
+ if (it != rMap.end()) // already exists
+ {
+ // add ranges from the rangelist to the existing rangelist for the
+ // matching style ( should we check if they overlap ? )
+ it->second.insert(it->second.end(), rRangeList.begin(), rRangeList.end());
+ return;
+ }
+ rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
+}
+
+void SheetDataBuffer::addColXfStyles()
+{
+ std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
+ for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
+ {
+ addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
+ }
+ // gather all ranges that have the same style and apply them in bulk
+ // Collect data in unsorted vectors and sort them just once at the end
+ // instead of possibly slow repeated inserts.
+ TmpColStyles tmpStylesPerColumn;
+ for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
+ {
+ for (const ScRange & rAddress : rRanges)
+ {
+ RowRangeStyle aStyleRows;
+ aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
+ aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
+ aStyleRows.mnStartRow = rAddress.aStart.Row();
+ aStyleRows.mnEndRow = rAddress.aEnd.Row();
+ for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
+ tmpStylesPerColumn[ nCol ].push_back( aStyleRows );
+ }
+ }
+ for( auto& rowStyles : tmpStylesPerColumn )
+ {
+ TmpRowStyles& s = rowStyles.second;
+ std::sort( s.begin(), s.end(), StyleRowRangeComp());
+ s.erase( std::unique( s.begin(), s.end(),
+ [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
+ // Synthetize operator== from operator < . Do not create an actual operator==
+ // as operator< is somewhat specific (see StyleRowRangeComp).
+ { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
+ s.end());
+ // Broken documents may have overlapping ranges that cause problems, repeat once more.
+ if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
+ {
+ std::sort( s.begin(), s.end(), StyleRowRangeComp());
+ s.erase( std::unique( s.begin(), s.end(),
+ [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
+ { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
+ s.end());
+ }
+ maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( std::move( s ));
+ }
+}
+
+void SheetDataBuffer::addColXfStyleProcessRowRanges()
+{
+ // count the number of row-range-styles we have
+ AddressConverter& rAddrConv = getAddressConverter();
+ int cnt = 0;
+ for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
+ {
+ if ( nXfId == -1 ) // it's a dud skip it
+ continue;
+ cnt += rRowRangeList.size();
+ }
+ // pre-allocate space in the sorted_vector
+ for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
+ {
+ RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
+ rRowStyles.reserve(rRowStyles.size() + cnt);
+ }
+ const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
+ for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
+ {
+ RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
+ for ( auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
+ {
+ if ( nXfId == -1 ) // it's a dud skip it
+ continue;
+ // sort the row ranges, so we spend less time moving data around
+ // when we insert into aStyleRows
+ std::sort(rRowRangeList.begin(), rRowRangeList.end(),
+ [](const ValueRange& lhs, const ValueRange& rhs)
+ {
+ return lhs.mnFirst < rhs.mnFirst;
+ });
+ // get all row ranges for id
+ for ( const auto& rRange : rRowRangeList )
+ {
+ RowRangeStyle aStyleRows;
+ aStyleRows.mnNumFmt.first = nXfId;
+ aStyleRows.mnNumFmt.second = -1;
+
+ // Reset row range for each column
+ aStyleRows.mnStartRow = rRange.mnFirst;
+ aStyleRows.mnEndRow = rRange.mnLast;
+
+ // If aStyleRows includes rows already allocated to a style
+ // in rRowStyles, then we need to split it into parts.
+ // ( to occupy only rows that have no style definition)
+
+ // Start iterating at the first element that is not completely before aStyleRows
+ RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
+ bool bAddRange = true;
+ for ( ; rows_it != rRowStyles.end(); ++rows_it )
+ {
+ // Add the part of aStyleRows that does not overlap with r
+ if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
+ {
+ RowRangeStyle aSplit = aStyleRows;
+ aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
+ rows_it = rRowStyles.insert( aSplit ).first;
+ }
+
+ // Done if no part of aStyleRows extends beyond r
+ if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
+ {
+ bAddRange = false;
+ break;
+ }
+
+ // Cut off the part aStyleRows that was handled above
+ aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
+ }
+ if ( bAddRange )
+ rRowStyles.insert( aStyleRows );
+ }
+ }
+ }
+}
+
+void SheetDataBuffer::finalizeImport()
+{
+ ScDocumentImport& rDocImport = getDocImport();
+
+ SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
+ SCTAB nEndTabInvalidatedIters(0);
+
+ // create all array formulas
+ for( const auto& [rRange, rTokens] : maArrayFormulas )
+ {
+ finalizeArrayFormula(rRange, rTokens);
+
+ nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
+ nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
+ }
+
+ for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
+ rDocImport.invalidateBlockPositionSet(nTab);
+
+ // create all table operations
+ for( const auto& [rRange, rModel] : maTableOperations )
+ finalizeTableOperation( rRange, rModel );
+
+ // write default formatting of remaining row range
+ maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
+
+ addColXfStyles();
+
+ addColXfStyleProcessRowRanges();
+
+ ScDocument& rDoc = rDocImport.getDoc();
+ StylesBuffer& rStyles = getStyles();
+ ScDocumentImport::Attrs aPendingAttrParam;
+ SCCOL pendingColStart = -1;
+ SCCOL pendingColEnd = -1;
+ ScPatternCache aPatternCache;
+
+ for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
+ {
+ SCCOL nScCol = static_cast< SCCOL >( rCol );
+
+ // tdf#91567 Get pattern from the first row without AutoFilter
+ const ScPatternAttr* pDefPattern = nullptr;
+ bool bAutoFilter = true;
+ SCROW nScRow = 0;
+ while ( bAutoFilter && nScRow < rDoc.MaxRow() )
+ {
+ pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
+ if ( pDefPattern )
+ {
+ const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
+ bAutoFilter = pAttr->HasAutoFilter();
+ }
+ else
+ break;
+ nScRow++;
+ }
+ if ( !pDefPattern || nScRow == rDoc.MaxRow() )
+ pDefPattern = rDoc.GetDefPattern();
+
+ Xf::AttrList aAttrs(pDefPattern);
+ for ( const auto& rRowStyle : rRowStyles )
+ {
+ Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
+
+ if ( pXf )
+ pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow,
+ rRowStyle.mnNumFmt.first, rRowStyle.mnNumFmt.second, aPatternCache );
+ }
+ if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
+ {
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = rDoc.MaxRow();
+ aEntry.pPattern = pDefPattern;
+ rDoc.GetPool()->DirectPutItemInPool(*aEntry.pPattern);
+ aAttrs.maAttrs.push_back(aEntry);
+
+ if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
+ aAttrs.mbLatinNumFmtOnly = false;
+ }
+
+ ScDocumentImport::Attrs aAttrParam;
+ aAttrParam.mvData.swap(aAttrs.maAttrs);
+ aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
+
+ // Compress setting the attributes, set the same set in one call.
+ if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
+ ++pendingColEnd;
+ else
+ {
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+ pendingColStart = pendingColEnd = nScCol;
+ aPendingAttrParam = std::move( aAttrParam );
+ }
+ }
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+
+ // merge all cached merged ranges and update right/bottom cell borders
+ for( const auto& rMergedRange : maMergedRanges )
+ applyCellMerging( rMergedRange.maRange );
+ for( const auto& rCenterFillRange : maCenterFillRanges )
+ applyCellMerging( rCenterFillRange.maRange );
+}
+
+// private --------------------------------------------------------------------
+
+SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
+ maRowRange( -1 ),
+ mnXfId( -1 )
+{
+}
+
+void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
+{
+ maRowRange = ValueRange( nRow );
+ mnXfId = nXfId;
+}
+
+bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
+{
+ if( mnXfId == nXfId )
+ {
+ if( maRowRange.mnLast + 1 == nRow )
+ {
+ ++maRowRange.mnLast;
+ return true;
+ }
+ if( maRowRange.mnFirst == nRow + 1 )
+ {
+ --maRowRange.mnFirst;
+ return true;
+ }
+ }
+ return false;
+}
+
+SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
+ maRange( rRange ),
+ mnHorAlign( XML_TOKEN_INVALID )
+{
+}
+
+SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
+ maRange( rAddress, rAddress ),
+ mnHorAlign( nHorAlign )
+{
+}
+
+bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
+{
+ if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
+ (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
+ {
+ maRange.aEnd.IncCol();
+ return true;
+ }
+ return false;
+}
+
+void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
+{
+ if( rTokens.hasElements() )
+ {
+ putFormulaTokens( rCellAddr, rTokens );
+ }
+}
+
+
+ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
+{
+ BinAddress aAddr(rAddr);
+ ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
+ return aTokens;
+}
+
+void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
+{
+ Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
+ OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
+ if( xTokens.is() )
+ xTokens->setArrayTokens( rTokens );
+}
+
+void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
+{
+ if (rModel.mbRef1Deleted)
+ return;
+
+ if (rModel.maRef1.isEmpty())
+ return;
+
+ if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
+ return;
+
+ sal_Int16 nSheet = getSheetIndex();
+
+ ScAddress aRef1( 0, 0, 0 );
+ if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
+ return;
+
+ ScDocumentImport& rDoc = getDocImport();
+ ScTabOpParam aParam;
+
+ ScRange aScRange(rRange);
+
+ if (rModel.mb2dTable)
+ {
+ // Two-variable data table.
+ if (rModel.mbRef2Deleted)
+ return;
+
+ if (rModel.maRef2.isEmpty())
+ return;
+
+ ScAddress aRef2( 0, 0, 0 );
+ if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
+ return;
+
+ aParam.meMode = ScTabOpParam::Both;
+
+ aScRange.aStart.IncCol(-1);
+ aScRange.aStart.IncRow(-1);
+
+ aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
+ aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
+
+ // Ref1 is row input cell and Ref2 is column input cell.
+ aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
+ aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
+ rDoc.setTableOpCells(aScRange, aParam);
+
+ return;
+ }
+
+ // One-variable data table.
+
+ if (rModel.mbRowTable)
+ {
+ // One-variable row input cell (horizontal).
+ aParam.meMode = ScTabOpParam::Row;
+ aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
+ aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
+ aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
+ aScRange.aStart.IncRow(-1);
+ rDoc.setTableOpCells(aScRange, aParam);
+ }
+ else
+ {
+ // One-variable column input cell (vertical).
+ aParam.meMode = ScTabOpParam::Column;
+ aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
+ aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
+ aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
+ aScRange.aStart.IncCol(-1);
+ rDoc.setTableOpCells(aScRange, aParam);
+ }
+}
+
+void SheetDataBuffer::setCellFormat( const CellModel& rModel )
+{
+ if( rModel.mnXfId < 0 )
+ return;
+
+ ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
+ ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
+ /* The xlsx sheet data contains row wise information.
+ * It is sufficient to check if the row range size is one
+ */
+ if (!rRangeList.empty() &&
+ *pLastRange == rModel.maCellAddr)
+ ; // do nothing - this probably bad data
+ else if (!rRangeList.empty() &&
+ pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
+ pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
+ pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
+ pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
+ {
+ pLastRange->aEnd.IncCol(); // Expand Column
+ }
+ else
+ {
+ rRangeList.push_back(ScRange(rModel.maCellAddr));
+ pLastRange = &rRangeList.back();
+ }
+
+ if (rRangeList.size() > 1)
+ {
+ for (size_t i = rRangeList.size() - 1; i != 0; --i)
+ {
+ ScRange& rMergeRange = rRangeList[i - 1];
+ if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
+ break;
+
+ /* Try to merge this with the previous range */
+ if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
+ pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
+ pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
+ {
+ rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
+ rRangeList.Remove(rRangeList.size() - 1);
+ break;
+ }
+ else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
+ break; // Un-necessary to check with any other rows
+ }
+ }
+ // update merged ranges for 'center across selection' and 'fill'
+ const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
+ if( !pXf )
+ return;
+
+ sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
+ if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
+ {
+ /* start new merged range, if cell is not empty (#108781#),
+ or try to expand last range with empty cell */
+ if( rModel.mnCellType != XML_TOKEN_INVALID )
+ maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
+ else if( !maCenterFillRanges.empty() )
+ maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
+ }
+}
+
+static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
+{
+ SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
+ SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
+
+ const SvxBoxItem* pFromItem =
+ rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
+ const SvxBoxItem* pToItem =
+ rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
+
+ SvxBoxItem aNewItem( *pToItem );
+ aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
+ rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
+}
+
+void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
+{
+ bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
+ bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
+
+ const ScAddress& rStart = rRange.aStart;
+ const ScAddress& rEnd = rRange.aEnd;
+ ScDocument& rDoc = getScDocument();
+ // set correct right border
+ if( bMultiCol )
+ lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
+ // set correct lower border
+ if( bMultiRow )
+ lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
+ // do merge
+ if( bMultiCol || bMultiRow )
+ rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), getSheetIndex() );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sheetdatacontext.cxx b/sc/source/filter/oox/sheetdatacontext.cxx
new file mode 100644
index 0000000000..19ad0a2a34
--- /dev/null
+++ b/sc/source/filter/oox/sheetdatacontext.cxx
@@ -0,0 +1,587 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sheetdatacontext.hxx>
+
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <formulaparser.hxx>
+#include <richstringcontext.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+namespace {
+
+// record constants -----------------------------------------------------------
+
+const sal_uInt32 BIFF12_CELL_SHOWPHONETIC = 0x01000000;
+
+const sal_uInt8 BIFF12_DATATABLE_ROW = 0x01;
+const sal_uInt8 BIFF12_DATATABLE_2D = 0x02;
+const sal_uInt8 BIFF12_DATATABLE_REF1DEL = 0x04;
+const sal_uInt8 BIFF12_DATATABLE_REF2DEL = 0x08;
+
+const sal_uInt16 BIFF12_ROW_THICKTOP = 0x0001;
+const sal_uInt16 BIFF12_ROW_THICKBOTTOM = 0x0002;
+const sal_uInt16 BIFF12_ROW_COLLAPSED = 0x0800;
+const sal_uInt16 BIFF12_ROW_HIDDEN = 0x1000;
+const sal_uInt16 BIFF12_ROW_CUSTOMHEIGHT = 0x2000;
+const sal_uInt16 BIFF12_ROW_CUSTOMFORMAT = 0x4000;
+const sal_uInt8 BIFF12_ROW_SHOWPHONETIC = 0x01;
+
+} // namespace
+
+SheetDataContext::SheetDataContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment ),
+ mrAddressConv( rFragment.getAddressConverter() ),
+ mrSheetData( rFragment.getSheetData() ),
+ mnSheet( rFragment.getSheetIndex() ),
+ mbHasFormula( false ),
+ mbValidRange( false ),
+ mnRow( -1 ),
+ mnCol( -1 )
+{
+ SAL_INFO( "sc.filter", "start safe sheet data context - unlock" );
+ mxFormulaParser.reset(rFragment.createFormulaParser());
+}
+
+SheetDataContext::~SheetDataContext()
+{
+ SAL_INFO( "sc.filter", "end safe sheet data context - relock" );
+}
+
+ContextHandlerRef SheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( sheetData ):
+ if( nElement == XLS_TOKEN( row ) ) { importRow( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( row ):
+ // do not process cell elements with invalid (out-of-range) address
+ if( nElement == XLS_TOKEN( c ) && importCell( rAttribs ) )
+ return this;
+ break;
+
+ case XLS_TOKEN( c ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( is ):
+ mxInlineStr = std::make_shared<RichString>();
+ return new RichStringContext( *this, mxInlineStr );
+ case XLS_TOKEN( v ):
+ return this; // characters contain cell value
+ case XLS_TOKEN( f ):
+ importFormula( rAttribs );
+ return this; // characters contain formula string
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void SheetDataContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( v ):
+ maCellValue = rChars;
+ break;
+ case XLS_TOKEN( f ):
+ if( maFmlaData.mnFormulaType != XML_TOKEN_INVALID )
+ {
+ maFormulaStr = rChars;
+ }
+ break;
+ }
+}
+
+void SheetDataContext::onEndElement()
+{
+ if( getCurrentElement() != XLS_TOKEN( c ) )
+ return;
+
+ // try to create a formula cell
+ if( mbHasFormula ) switch( maFmlaData.mnFormulaType )
+ {
+ // will buffer formulas but need to
+ // a) need to set format first
+ // :/
+ case XML_normal:
+ setCellFormula( maCellData.maCellAddr, maFormulaStr );
+ mrSheetData.setCellFormat( maCellData );
+
+ // If a number cell has some preloaded value, stick it into the buffer
+ // but do this only for real cell formulas (not array, shared etc.)
+ if (!maCellValue.isEmpty())
+ setCellFormulaValue(maCellData.maCellAddr, maCellValue, maCellData.mnCellType);
+ break;
+
+ case XML_shared:
+ if( maFmlaData.mnSharedId >= 0 )
+ {
+ if( mbValidRange && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
+ createSharedFormulaMapEntry(maCellData.maCellAddr, maFmlaData.mnSharedId, maFormulaStr);
+
+ setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType);
+ mrSheetData.setCellFormat( maCellData );
+ }
+ else
+ // no success, set plain cell value and formatting below
+ mbHasFormula = false;
+ break;
+ case XML_array:
+ if( mbValidRange && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
+ {
+ setCellArrayFormula( maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr );
+ }
+ // set cell formatting, but do not set result as cell value
+ mrSheetData.setBlankCell( maCellData );
+ break;
+ case XML_dataTable:
+ if( mbValidRange )
+ mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
+ // set cell formatting, but do not set result as cell value
+ mrSheetData.setBlankCell( maCellData );
+ break;
+ default:
+ OSL_ENSURE( maFmlaData.mnFormulaType == XML_TOKEN_INVALID, "SheetDataContext::onEndElement - unknown formula type" );
+ mbHasFormula = false;
+ }
+
+ if( mbHasFormula )
+ return;
+
+ // no formula created: try to set the cell value
+ if( !maCellValue.isEmpty() ) switch( maCellData.mnCellType )
+ {
+ case XML_n:
+ mrSheetData.setValueCell( maCellData, maCellValue.toDouble() );
+ break;
+ case XML_b:
+ {
+ // Some generators may write true or false instead of 1 or 0.
+ /* XXX NOTE: PivotCacheItem::readBool() may suffer from this as
+ * well, but for now let's assume that software writing this
+ * here wrong won't write pivot caches at all.. */
+ bool bValue = (maCellValue.toDouble() != 0.0);
+ if (!bValue && maCellValue.equalsIgnoreAsciiCase(u"true"))
+ bValue = true;
+ mrSheetData.setBooleanCell( maCellData, bValue );
+ }
+ break;
+ case XML_e:
+ mrSheetData.setErrorCell( maCellData, maCellValue );
+ break;
+ case XML_str:
+ mrSheetData.setStringCell( maCellData, maCellValue );
+ break;
+ case XML_s:
+ mrSheetData.setStringCell( maCellData, maCellValue.toInt32() );
+ break;
+ case XML_d:
+ mrSheetData.setDateCell( maCellData, maCellValue );
+ break;
+ }
+ else if( (maCellData.mnCellType == XML_inlineStr) && mxInlineStr )
+ {
+ mxInlineStr->finalizeImport(*this);
+ mrSheetData.setStringCell( maCellData, mxInlineStr );
+ }
+ else
+ {
+ // empty cell, update cell type
+ maCellData.mnCellType = XML_TOKEN_INVALID;
+ mrSheetData.setBlankCell( maCellData );
+ }
+}
+
+ContextHandlerRef SheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_SHEETDATA:
+ if( nRecId == BIFF12_ID_ROW ) { importRow( rStrm ); return this; }
+ break;
+
+ case BIFF12_ID_ROW:
+ switch( nRecId )
+ {
+ case BIFF12_ID_ARRAY: importArray( rStrm ); break;
+ case BIFF12_ID_CELL_BOOL: importCellBool( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_BLANK: importCellBlank( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_DOUBLE: importCellDouble( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_ERROR: importCellError( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_RK: importCellRk( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_RSTRING: importCellRString( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_SI: importCellSi( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_STRING: importCellString( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_DATATABLE: importDataTable( rStrm ); break;
+ case BIFF12_ID_FORMULA_BOOL: importCellBool( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_FORMULA_DOUBLE: importCellDouble( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_FORMULA_ERROR: importCellError( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_FORMULA_STRING: importCellString( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_MULTCELL_BOOL: importCellBool( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_BLANK: importCellBlank( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_DOUBLE: importCellDouble( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_ERROR: importCellError( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_RK: importCellRk( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_RSTRING:importCellRString( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_SI: importCellSi( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_STRING: importCellString( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_SHAREDFMLA: importSharedFmla( rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+// private --------------------------------------------------------------------
+
+void SheetDataContext::importRow( const AttributeList& rAttribs )
+{
+ RowModel aModel;
+ sal_Int32 nRow = rAttribs.getInteger( XML_r, -1 ); // 1-based row index
+ if(nRow != -1)
+ {
+ aModel.mnRow = nRow;
+ mnRow = nRow-1; // to 0-based row index.
+ }
+ else
+ aModel.mnRow = (++mnRow + 1); // increment 0-based row index, to 1-based model row
+ mrAddressConv.checkRow( mnRow, true);
+ mnCol = -1;
+
+ aModel.mfHeight = rAttribs.getDouble( XML_ht, -1.0 );
+ aModel.mnXfId = rAttribs.getInteger(XML_s, 0); // default style index is 0
+ aModel.mnLevel = rAttribs.getInteger( XML_outlineLevel, 0 );
+ aModel.mbCustomHeight = rAttribs.getBool( XML_customHeight, false );
+ aModel.mbCustomFormat = rAttribs.getBool( XML_customFormat, false );
+ aModel.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
+ aModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+ aModel.mbCollapsed = rAttribs.getBool( XML_collapsed, false );
+ aModel.mbThickTop = rAttribs.getBool( XML_thickTop, false );
+ aModel.mbThickBottom = rAttribs.getBool( XML_thickBot, false );
+
+ if (aModel.mfHeight > 0 && getFilter().isMSODocument())
+ {
+ aModel.mfHeight -= fmod(aModel.mfHeight, 0.75); //round down to 0.75pt
+ }
+
+ // decode the column spans (space-separated list of colon-separated integer pairs)
+ OUString aColSpansText = rAttribs.getString( XML_spans, OUString() );
+ sal_Int32 nIndex = 0;
+ while( nIndex >= 0 )
+ {
+ std::u16string_view aColSpanToken = o3tl::getToken(aColSpansText, 0, ' ', nIndex );
+ size_t nSepPos = aColSpanToken.find( ':' );
+ if( (0 < nSepPos) && (nSepPos + 1 < aColSpanToken.size()) )
+ {
+ // OOXML uses 1-based integer column indexes, row model expects 0-based colspans
+ const sal_Int32 nCol1 = o3tl::toInt32(aColSpanToken.substr( 0, nSepPos )) - 1;
+ const bool bValid1 = mrAddressConv.checkCol( nCol1, true);
+ if (bValid1)
+ {
+ const sal_Int32 nCol2 = o3tl::toInt32(aColSpanToken.substr( nSepPos + 1 )) - 1;
+ mrAddressConv.checkCol( nCol2, true);
+ }
+ }
+ }
+
+ // set row properties in the current sheet
+ setRowModel( aModel );
+}
+
+bool SheetDataContext::importCell( const AttributeList& rAttribs )
+{
+ bool bValid = true;
+ std::string_view p = rAttribs.getView(XML_r);
+
+ if (p.empty())
+ {
+ ++mnCol;
+ ScAddress aAddress( mnCol, mnRow, mnSheet );
+ bValid = mrAddressConv.checkCellAddress( aAddress, true );
+ maCellData.maCellAddr = aAddress;
+ }
+ else
+ {
+ bValid = mrAddressConv.convertToCellAddress(maCellData.maCellAddr, p, mnSheet, true);
+ mnCol = maCellData.maCellAddr.Col();
+ }
+
+ if( bValid )
+ {
+ maCellData.mnCellType = rAttribs.getToken( XML_t, XML_n );
+ maCellData.mnXfId = rAttribs.getInteger( XML_s, -1 );
+ maCellData.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
+
+ // reset cell value, formula settings, and inline string
+ maCellValue.clear();
+ mxInlineStr.reset();
+ mbHasFormula = false;
+
+ // update used area of the sheet
+ extendUsedArea( maCellData.maCellAddr );
+ }
+ return bValid;
+}
+
+void SheetDataContext::importFormula( const AttributeList& rAttribs )
+{
+ mbHasFormula = true;
+ mbValidRange = mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, rAttribs.getString( XML_ref, OUString() ), mnSheet, true, true );
+
+ maFmlaData.mnFormulaType = rAttribs.getToken( XML_t, XML_normal );
+ maFmlaData.mnSharedId = rAttribs.getInteger( XML_si, -1 );
+
+ if( maFmlaData.mnFormulaType == XML_dataTable )
+ {
+ maTableData.maRef1 = rAttribs.getString( XML_r1, OUString() );
+ maTableData.maRef2 = rAttribs.getString( XML_r2, OUString() );
+ maTableData.mb2dTable = rAttribs.getBool( XML_dt2D, false );
+ maTableData.mbRowTable = rAttribs.getBool( XML_dtr, false );
+ maTableData.mbRef1Deleted = rAttribs.getBool( XML_del1, false );
+ maTableData.mbRef2Deleted = rAttribs.getBool( XML_del2, false );
+ }
+
+ maFormulaStr.clear();
+}
+
+void SheetDataContext::importRow( SequenceInputStream& rStrm )
+{
+ RowModel aModel;
+ sal_Int32 nSpanCount;
+ sal_uInt16 nHeight, nFlags1;
+ sal_uInt8 nFlags2;
+ maCurrPos.mnRow = rStrm.readInt32();
+ aModel.mnXfId = rStrm.readInt32();
+ nHeight = rStrm.readuInt16();
+ nFlags1 = rStrm.readuInt16();
+ nFlags2 = rStrm.readuChar();
+ nSpanCount = rStrm.readInt32();
+ maCurrPos.mnCol = 0;
+
+ mrAddressConv.checkRow( maCurrPos.mnRow, true);
+ // row index is 0-based in BIFF12, but RowModel expects 1-based
+ aModel.mnRow = maCurrPos.mnRow + 1;
+ // row height is in twips in BIFF12, convert to points
+ aModel.mfHeight = nHeight / 20.0;
+ aModel.mnLevel = extractValue< sal_Int32 >( nFlags1, 8, 3 );
+ aModel.mbCustomHeight = getFlag( nFlags1, BIFF12_ROW_CUSTOMHEIGHT );
+ aModel.mbCustomFormat = getFlag( nFlags1, BIFF12_ROW_CUSTOMFORMAT );
+ aModel.mbShowPhonetic = getFlag( nFlags2, BIFF12_ROW_SHOWPHONETIC );
+ aModel.mbHidden = getFlag( nFlags1, BIFF12_ROW_HIDDEN );
+ aModel.mbCollapsed = getFlag( nFlags1, BIFF12_ROW_COLLAPSED );
+ aModel.mbThickTop = getFlag( nFlags1, BIFF12_ROW_THICKTOP );
+ aModel.mbThickBottom = getFlag( nFlags1, BIFF12_ROW_THICKBOTTOM );
+
+ // read the column spans
+ for( sal_Int32 nSpanIdx = 0; (nSpanIdx < nSpanCount) && !rStrm.isEof(); ++nSpanIdx )
+ {
+ sal_Int32 nFirstCol, nLastCol;
+ nFirstCol = rStrm.readInt32();
+ mrAddressConv.checkCol( nFirstCol, true);
+ nLastCol = rStrm.readInt32();
+ mrAddressConv.checkCol( nLastCol, true);
+ }
+
+ // set row properties in the current sheet
+ setRowModel( aModel );
+}
+
+bool SheetDataContext::readCellHeader( SequenceInputStream& rStrm, CellType eCellType )
+{
+ switch( eCellType )
+ {
+ case CELLTYPE_VALUE:
+ case CELLTYPE_FORMULA: maCurrPos.mnCol = rStrm.readInt32(); break;
+ case CELLTYPE_MULTI: ++maCurrPos.mnCol; break;
+ }
+
+ sal_uInt32 nXfId = rStrm.readuInt32();
+
+ bool bValidAddr = mrAddressConv.convertToCellAddress( maCellData.maCellAddr, maCurrPos, mnSheet, true );
+ maCellData.mnXfId = extractValue< sal_Int32 >( nXfId, 0, 24 );
+ maCellData.mbShowPhonetic = getFlag( nXfId, BIFF12_CELL_SHOWPHONETIC );
+
+ // update used area of the sheet
+ if( bValidAddr )
+ extendUsedArea( maCellData.maCellAddr );
+ return bValidAddr;
+}
+
+ApiTokenSequence SheetDataContext::readCellFormula( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 );
+ return mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::Cell, rStrm );
+}
+
+bool SheetDataContext::readFormulaRef( SequenceInputStream& rStrm )
+{
+ BinRange aRange;
+ rStrm >> aRange;
+ return mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, aRange, mnSheet, true, true );
+}
+
+void SheetDataContext::importCellBool( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_b;
+ bool bValue = rStrm.readuInt8() != 0;
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setBooleanCell( maCellData, bValue );
+ }
+}
+
+void SheetDataContext::importCellBlank( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellBlank - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ mrSheetData.setBlankCell( maCellData );
+}
+
+void SheetDataContext::importCellDouble( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_n;
+ double fValue = rStrm.readDouble();
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setValueCell( maCellData, fValue );
+ }
+}
+
+void SheetDataContext::importCellError( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_e;
+ sal_uInt8 nErrorCode = rStrm.readuInt8();
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setErrorCell( maCellData, nErrorCode );
+ }
+}
+
+void SheetDataContext::importCellRk( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRk - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_n;
+ mrSheetData.setValueCell( maCellData, BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) );
+ }
+}
+
+void SheetDataContext::importCellRString( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRString - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_inlineStr;
+ RichStringRef xString = std::make_shared<RichString>();
+ xString->importString( rStrm, true, *this );
+ xString->finalizeImport( *this );
+ mrSheetData.setStringCell( maCellData, xString );
+ }
+}
+
+void SheetDataContext::importCellSi( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellSi - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_s;
+ mrSheetData.setStringCell( maCellData, rStrm.readInt32() );
+ }
+}
+
+void SheetDataContext::importCellString( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_inlineStr;
+ // always import the string, stream will point to formula afterwards, if existing
+ RichStringRef xString = std::make_shared<RichString>();
+ xString->importString( rStrm, false, *this );
+ xString->finalizeImport( *this );
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setStringCell( maCellData, xString );
+ }
+}
+
+void SheetDataContext::importArray( SequenceInputStream& rStrm )
+{
+ if( readFormulaRef( rStrm ) && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
+ {
+ rStrm.skip( 1 );
+ ApiTokenSequence aTokens = mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::Array, rStrm );
+ mrSheetData.createArrayFormula( maFmlaData.maFormulaRef, aTokens );
+ }
+}
+
+void SheetDataContext::importDataTable( SequenceInputStream& rStrm )
+{
+ if( !readFormulaRef( rStrm ) )
+ return;
+
+ BinAddress aRef1, aRef2;
+ sal_uInt8 nFlags;
+ rStrm >> aRef1 >> aRef2;
+ nFlags = rStrm.readuChar();
+ maTableData.maRef1 = FormulaProcessorBase::generateAddress2dString( aRef1, false );
+ maTableData.maRef2 = FormulaProcessorBase::generateAddress2dString( aRef2, false );
+ maTableData.mbRowTable = getFlag( nFlags, BIFF12_DATATABLE_ROW );
+ maTableData.mb2dTable = getFlag( nFlags, BIFF12_DATATABLE_2D );
+ maTableData.mbRef1Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF1DEL );
+ maTableData.mbRef2Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF2DEL );
+ mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
+}
+
+void SheetDataContext::importSharedFmla( SequenceInputStream& rStrm )
+{
+ if( readFormulaRef( rStrm ) && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
+ {
+ ApiTokenSequence aTokens = mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::SharedFormula, rStrm );
+ mrSheetData.createSharedFormula( maCellData.maCellAddr, aTokens );
+ }
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/stylesbuffer.cxx b/sc/source/filter/oox/stylesbuffer.cxx
new file mode 100644
index 0000000000..2310f324e2
--- /dev/null
+++ b/sc/source/filter/oox/stylesbuffer.cxx
@@ -0,0 +1,3115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stylesbuffer.hxx>
+#include <patterncache.hxx>
+
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/awt/FontFamily.hpp>
+#include <com/sun/star/awt/FontPitch.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontType.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/awt/XFont2.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <com/sun/star/table/CellVertJustify2.hpp>
+#include <com/sun/star/table/CellJustifyMethod.hpp>
+#include <editeng/justifyitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <tools/fontenum.hxx>
+#include <tools/UnitConversion.hxx>
+#include <utility>
+#include <vcl/unohelp.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertymap.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <themebuffer.hxx>
+#include <unitconverter.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <docpool.hxx>
+#include <ftools.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <documentimport.hxx>
+#include <patattr.hxx>
+#include <stlsheet.hxx>
+#include <biffhelper.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+
+namespace {
+
+// OOXML constants ------------------------------------------------------------
+
+// OOXML predefined color indexes (also used in BIFF3-BIFF8)
+const sal_Int32 OOX_COLOR_USEROFFSET = 0; /// First user defined color in palette (OOXML/BIFF12).
+
+// OOXML font family (also used in BIFF)
+const sal_Int32 OOX_FONTFAMILY_NONE = 0;
+const sal_Int32 OOX_FONTFAMILY_ROMAN = 1;
+const sal_Int32 OOX_FONTFAMILY_SWISS = 2;
+const sal_Int32 OOX_FONTFAMILY_MODERN = 3;
+const sal_Int32 OOX_FONTFAMILY_SCRIPT = 4;
+const sal_Int32 OOX_FONTFAMILY_DECORATIVE = 5;
+
+// OOXML cell text direction (also used in BIFF)
+const sal_Int32 OOX_XF_TEXTDIR_CONTEXT = 0;
+const sal_Int32 OOX_XF_TEXTDIR_LTR = 1;
+const sal_Int32 OOX_XF_TEXTDIR_RTL = 2;
+
+// OOXML cell rotation (also used in BIFF)
+const sal_Int32 OOX_XF_ROTATION_NONE = 0;
+const sal_Int32 OOX_XF_ROTATION_STACKED = 255;
+
+// OOXML cell indentation
+const sal_Int32 OOX_XF_INDENT_NONE = 0;
+
+// OOXML built-in cell styles (also used in BIFF)
+const sal_Int32 OOX_STYLE_NORMAL = 0; /// Default cell style.
+const sal_Int32 OOX_STYLE_ROWLEVEL = 1; /// RowLevel_x cell style.
+const sal_Int32 OOX_STYLE_COLLEVEL = 2; /// ColLevel_x cell style.
+
+// BIFF12 constants -----------------------------------------------------------
+
+// BIFF12 color types
+const sal_uInt8 BIFF12_COLOR_AUTO = 0;
+const sal_uInt8 BIFF12_COLOR_INDEXED = 1;
+const sal_uInt8 BIFF12_COLOR_RGB = 2;
+const sal_uInt8 BIFF12_COLOR_THEME = 3;
+
+// BIFF12 diagonal borders
+const sal_uInt8 BIFF12_BORDER_DIAG_TLBR = 0x01; /// Top-left to bottom-right.
+const sal_uInt8 BIFF12_BORDER_DIAG_BLTR = 0x02; /// Bottom-left to top-right.
+
+// BIFF12 gradient fill
+const sal_Int32 BIFF12_FILL_GRADIENT = 40;
+
+// BIFF12 XF flags
+const sal_uInt32 BIFF12_XF_WRAPTEXT = 0x00400000;
+const sal_uInt32 BIFF12_XF_JUSTLASTLINE = 0x00800000;
+const sal_uInt32 BIFF12_XF_SHRINK = 0x01000000;
+const sal_uInt32 BIFF12_XF_LOCKED = 0x10000000;
+const sal_uInt32 BIFF12_XF_HIDDEN = 0x20000000;
+
+// BIFF12 XF attribute used flags
+const sal_uInt16 BIFF12_XF_NUMFMT_USED = 0x0001;
+const sal_uInt16 BIFF12_XF_FONT_USED = 0x0002;
+const sal_uInt16 BIFF12_XF_ALIGN_USED = 0x0004;
+const sal_uInt16 BIFF12_XF_BORDER_USED = 0x0008;
+const sal_uInt16 BIFF12_XF_AREA_USED = 0x0010;
+const sal_uInt16 BIFF12_XF_PROT_USED = 0x0020;
+
+// BIFF12 DXF constants
+const sal_uInt16 BIFF12_DXF_FILL_PATTERN = 0;
+const sal_uInt16 BIFF12_DXF_FILL_FGCOLOR = 1;
+const sal_uInt16 BIFF12_DXF_FILL_BGCOLOR = 2;
+const sal_uInt16 BIFF12_DXF_FILL_GRADIENT = 3;
+const sal_uInt16 BIFF12_DXF_FILL_STOP = 4;
+const sal_uInt16 BIFF12_DXF_FONT_COLOR = 5;
+const sal_uInt16 BIFF12_DXF_BORDER_TOP = 6;
+const sal_uInt16 BIFF12_DXF_BORDER_BOTTOM = 7;
+const sal_uInt16 BIFF12_DXF_BORDER_LEFT = 8;
+const sal_uInt16 BIFF12_DXF_BORDER_RIGHT = 9;
+const sal_uInt16 BIFF12_DXF_FONT_NAME = 24;
+const sal_uInt16 BIFF12_DXF_FONT_WEIGHT = 25;
+const sal_uInt16 BIFF12_DXF_FONT_UNDERLINE = 26;
+const sal_uInt16 BIFF12_DXF_FONT_ESCAPEMENT = 27;
+const sal_uInt16 BIFF12_DXF_FONT_ITALIC = 28;
+const sal_uInt16 BIFF12_DXF_FONT_STRIKE = 29;
+const sal_uInt16 BIFF12_DXF_FONT_OUTLINE = 30;
+const sal_uInt16 BIFF12_DXF_FONT_SHADOW = 31;
+const sal_uInt16 BIFF12_DXF_FONT_HEIGHT = 36;
+const sal_uInt16 BIFF12_DXF_FONT_SCHEME = 37;
+const sal_uInt16 BIFF12_DXF_NUMFMT_CODE = 38;
+const sal_uInt16 BIFF12_DXF_NUMFMT_ID = 41;
+
+// BIFF12 CELLSTYLE flags
+const sal_uInt16 BIFF12_CELLSTYLE_BUILTIN = 0x0001;
+const sal_uInt16 BIFF12_CELLSTYLE_HIDDEN = 0x0002;
+const sal_uInt16 BIFF12_CELLSTYLE_CUSTOM = 0x0004;
+
+// BIFF constants -------------------------------------------------------------
+
+// BIFF font flags, also used in BIFF12
+const sal_uInt16 BIFF_FONTFLAG_ITALIC = 0x0002;
+const sal_uInt16 BIFF_FONTFLAG_STRIKEOUT = 0x0008;
+const sal_uInt16 BIFF_FONTFLAG_OUTLINE = 0x0010;
+const sal_uInt16 BIFF_FONTFLAG_SHADOW = 0x0020;
+
+// BIFF font weight
+const sal_uInt16 BIFF_FONTWEIGHT_BOLD = 450;
+
+// BIFF font underline, also used in BIFF12
+const sal_uInt8 BIFF_FONTUNDERL_NONE = 0;
+const sal_uInt8 BIFF_FONTUNDERL_SINGLE = 1;
+const sal_uInt8 BIFF_FONTUNDERL_DOUBLE = 2;
+const sal_uInt8 BIFF_FONTUNDERL_SINGLE_ACC = 33;
+const sal_uInt8 BIFF_FONTUNDERL_DOUBLE_ACC = 34;
+
+::Color lclReadRgbColor( BinaryInputStream& rStrm )
+{
+ sal_uInt8 nR, nG, nB, nA;
+ nR = rStrm.readuChar();
+ nG = rStrm.readuChar();
+ nB = rStrm.readuChar();
+ nA = rStrm.readuChar();
+ sal_Int32 nValue = nA;
+ nValue <<= 8;
+ nValue |= nR;
+ nValue <<= 8;
+ nValue |= nG;
+ nValue <<= 8;
+ nValue |= nB;
+ return ::Color(ColorTransparency, nValue);
+}
+
+} // namespace
+
+ExcelGraphicHelper::ExcelGraphicHelper( const WorkbookHelper& rHelper ) :
+ GraphicHelper( rHelper.getBaseFilter().getComponentContext(), rHelper.getBaseFilter().getTargetFrame(), rHelper.getBaseFilter().getStorage() ),
+ WorkbookHelper( rHelper )
+{
+}
+
+::Color ExcelGraphicHelper::getSchemeColor( sal_Int32 nToken ) const
+{
+ return getTheme().getColorByToken( nToken );
+}
+
+::Color ExcelGraphicHelper::getPaletteColor( sal_Int32 nPaletteIdx ) const
+{
+ return getStyles().getPaletteColor( nPaletteIdx );
+}
+
+void XlsColor::setAuto()
+{
+ clearTransformations();
+ setSchemeClr( XML_phClr );
+}
+
+void XlsColor::setRgb( ::Color nRgbValue, double fTint )
+{
+ clearTransformations();
+ setSrgbClr( sal_uInt32(nRgbValue) & 0xFFFFFF );
+ if (fTint != 0.0)
+ addExcelTintTransformation(fTint);
+}
+
+void XlsColor::setTheme( sal_Int32 nThemeIdx, double fTint )
+{
+ clearTransformations();
+ static const sal_Int32 spnColorTokens[] = {
+ XML_lt1, XML_dk1, XML_lt2, XML_dk2, XML_accent1, XML_accent2,
+ XML_accent3, XML_accent4, XML_accent5, XML_accent6, XML_hlink, XML_folHlink };
+ setSchemeClr( STATIC_ARRAY_SELECT( spnColorTokens, nThemeIdx, XML_TOKEN_INVALID ) );
+ if (fTint != 0.0)
+ addExcelTintTransformation( fTint );
+}
+
+void XlsColor::setIndexed( sal_Int32 nPaletteIdx, double fTint )
+{
+ clearTransformations();
+ setPaletteClr( nPaletteIdx );
+ if (fTint != 0.0)
+ addExcelTintTransformation(fTint);
+}
+
+void XlsColor::importColor( const AttributeList& rAttribs )
+{
+ // tdf#113271 The order of import color is very important in case of more than one color attributes was provided.
+ // This order (theme -> rgb -> indexed -> auto) is not documented and was gathered experimentally based on MS Excel 2013.
+ if (rAttribs.hasAttribute(XML_theme))
+ {
+ sal_Int32 nTheme = rAttribs.getInteger(XML_theme, -1);
+ double fTint = rAttribs.getDouble(XML_tint, 0.0);
+ setTheme(nTheme , fTint);
+ }
+ else if( rAttribs.hasAttribute( XML_rgb ) )
+ setRgb( ::Color(ColorTransparency, rAttribs.getIntegerHex( XML_rgb, sal_Int32(API_RGB_TRANSPARENT) ) ), rAttribs.getDouble( XML_tint, 0.0 ) );
+ else if( rAttribs.hasAttribute( XML_indexed ) )
+ setIndexed( rAttribs.getInteger( XML_indexed, -1 ), rAttribs.getDouble( XML_tint, 0.0 ) );
+ else if( rAttribs.getBool( XML_auto, false ) )
+ setAuto();
+ else
+ {
+ OSL_FAIL( "Color::importColor - unknown color type" );
+ setAuto();
+ }
+}
+
+void XlsColor::importColor( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags, nIndex;
+ sal_Int16 nTint;
+ nFlags = rStrm.readuChar();
+ nIndex = rStrm.readuChar();
+ nTint = rStrm.readInt16();
+
+ // scale tint from signed 16-bit to double range -1.0 ... 1.0
+ double fTint = nTint;
+ if( nTint < 0 )
+ fTint /= -SAL_MIN_INT16;
+ else if( nTint > 0 )
+ fTint /= SAL_MAX_INT16;
+
+ switch( extractValue< sal_uInt8 >( nFlags, 1, 7 ) )
+ {
+ case BIFF12_COLOR_AUTO:
+ setAuto();
+ rStrm.skip( 4 );
+ break;
+ case BIFF12_COLOR_INDEXED:
+ setIndexed( nIndex, fTint );
+ rStrm.skip( 4 );
+ break;
+ case BIFF12_COLOR_RGB:
+ setRgb( lclReadRgbColor( rStrm ), fTint );
+ break;
+ case BIFF12_COLOR_THEME:
+ setTheme( nIndex, fTint );
+ rStrm.skip( 4 );
+ break;
+ default:
+ OSL_FAIL( "Color::importColor - unknown color type" );
+ setAuto();
+ rStrm.skip( 4 );
+ }
+}
+
+void XlsColor::importColorId( SequenceInputStream& rStrm )
+{
+ setIndexed( rStrm.readInt32() );
+}
+
+SequenceInputStream& operator>>( SequenceInputStream& rStrm, XlsColor& orColor )
+{
+ orColor.importColor( rStrm );
+ return rStrm;
+}
+
+namespace {
+
+/** Standard EGA colors, bright. */
+#define PALETTE_EGA_COLORS_LIGHT \
+ ::Color(0x000000), ::Color(0xFFFFFF), ::Color(0xFF0000), ::Color(0x00FF00), ::Color(0x0000FF), ::Color(0xFFFF00), ::Color(0xFF00FF), ::Color(0x00FFFF)
+/** Standard EGA colors), dark. */
+#define PALETTE_EGA_COLORS_DARK \
+ ::Color(0x800000), ::Color(0x008000), ::Color(0x000080), ::Color(0x808000), ::Color(0x800080), ::Color(0x008080), ::Color(0xC0C0C0), ::Color(0x808080)
+
+/** Default color table for BIFF8/BIFF12/OOXML. */
+const ::Color spnDefColors8[] =
+{
+/* 0 */ PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ PALETTE_EGA_COLORS_DARK,
+/* 24 */ ::Color(0x9999FF), ::Color(0x993366), ::Color(0xFFFFCC), ::Color(0xCCFFFF), ::Color(0x660066), ::Color(0xFF8080), ::Color(0x0066CC), ::Color(0xCCCCFF),
+/* 32 */ ::Color(0x000080), ::Color(0xFF00FF), ::Color(0xFFFF00), ::Color(0x00FFFF), ::Color(0x800080), ::Color(0x800000), ::Color(0x008080), ::Color(0x0000FF),
+/* 40 */ ::Color(0x00CCFF), ::Color(0xCCFFFF), ::Color(0xCCFFCC), ::Color(0xFFFF99), ::Color(0x99CCFF), ::Color(0xFF99CC), ::Color(0xCC99FF), ::Color(0xFFCC99),
+/* 48 */ ::Color(0x3366FF), ::Color(0x33CCCC), ::Color(0x99CC00), ::Color(0xFFCC00), ::Color(0xFF9900), ::Color(0xFF6600), ::Color(0x666699), ::Color(0x969696),
+/* 56 */ ::Color(0x003366), ::Color(0x339966), ::Color(0x003300), ::Color(0x333300), ::Color(0x993300), ::Color(0x993366), ::Color(0x333399), ::Color(0x333333)
+};
+
+#undef PALETTE_EGA_COLORS_LIGHT
+#undef PALETTE_EGA_COLORS_DARK
+
+} // namespace
+
+ColorPalette::ColorPalette( const WorkbookHelper& rHelper )
+ : WorkbookHelper(rHelper)
+ , mnAppendIndex(0)
+{
+ // default colors
+ maColors.insert( maColors.begin(), spnDefColors8, spnDefColors8 + SAL_N_ELEMENTS(spnDefColors8) );
+ mnAppendIndex = OOX_COLOR_USEROFFSET;
+}
+
+void ColorPalette::importPaletteColor( const AttributeList& rAttribs )
+{
+ appendColor( ::Color(ColorTransparency, rAttribs.getIntegerHex( XML_rgb, sal_Int32(API_RGB_WHITE) ) ) );
+}
+
+void ColorPalette::importPaletteColor( SequenceInputStream& rStrm )
+{
+ ::Color nRgb = lclReadRgbColor( rStrm );
+ appendColor( nRgb );
+}
+
+::Color ColorPalette::getColor( sal_Int32 nPaletteIdx ) const
+{
+ ::Color nColor = API_RGB_TRANSPARENT;
+ if( const ::Color* pnPaletteColor = ContainerHelper::getVectorElement( maColors, nPaletteIdx ) )
+ {
+ nColor = *pnPaletteColor;
+ }
+ else switch( nPaletteIdx )
+ {
+ case OOX_COLOR_WINDOWTEXT3:
+ case OOX_COLOR_WINDOWTEXT:
+ case OOX_COLOR_CHWINDOWTEXT: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_windowText ); break;
+ case OOX_COLOR_WINDOWBACK3:
+ case OOX_COLOR_WINDOWBACK:
+ case OOX_COLOR_CHWINDOWBACK: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_window ); break;
+ case OOX_COLOR_BUTTONBACK: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_btnFace ); break;
+ case OOX_COLOR_CHBORDERAUTO: nColor = API_RGB_BLACK; /* really always black? */ break;
+ case OOX_COLOR_NOTEBACK: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_infoBk ); break;
+ case OOX_COLOR_NOTETEXT: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_infoText ); break;
+ case OOX_COLOR_FONTAUTO: nColor = API_RGB_TRANSPARENT; break;
+ default: OSL_FAIL( "ColorPalette::getColor - unknown color index" );
+ }
+ return nColor;
+}
+
+void ColorPalette::appendColor( ::Color nRGBValue )
+{
+ if( mnAppendIndex < maColors.size() )
+ maColors[ mnAppendIndex ] = nRGBValue;
+ else
+ maColors.push_back( nRGBValue );
+ ++mnAppendIndex;
+}
+
+namespace {
+
+void lclSetFontName( ApiScriptFontName& rFontName, const FontDescriptor& rFontDesc, bool bHasGlyphs )
+{
+ if( bHasGlyphs )
+ {
+ rFontName.maName = rFontDesc.Name;
+ rFontName.mnFamily = rFontDesc.Family;
+ // API font descriptor contains rtl_TextEncoding constants
+ rFontName.mnTextEnc = rFontDesc.CharSet;
+ }
+ else
+ {
+ rFontName = ApiScriptFontName();
+ }
+}
+
+} // namespace
+
+FontModel::FontModel() :
+ mnScheme( XML_none ),
+ mnFamily( OOX_FONTFAMILY_NONE ),
+ mnCharSet( WINDOWS_CHARSET_DEFAULT ),
+ mfHeight( 0.0 ),
+ mnUnderline( XML_none ),
+ mnEscapement( XML_baseline ),
+ mbBold( false ),
+ mbItalic( false ),
+ mbStrikeout( false ),
+ mbOutline( false ),
+ mbShadow( false )
+{
+}
+
+void FontModel::setBiff12Scheme( sal_uInt8 nScheme )
+{
+ static const sal_Int32 spnSchemes[] = { XML_none, XML_major, XML_minor };
+ mnScheme = STATIC_ARRAY_SELECT( spnSchemes, nScheme, XML_none );
+}
+
+void FontModel::setBiffHeight( sal_uInt16 nHeight )
+{
+ mfHeight = nHeight / 20.0; // convert twips to points
+}
+
+void FontModel::setBiffWeight( sal_uInt16 nWeight )
+{
+ mbBold = nWeight >= BIFF_FONTWEIGHT_BOLD;
+}
+
+void FontModel::setBiffUnderline( sal_uInt16 nUnderline )
+{
+ switch( nUnderline )
+ {
+ case BIFF_FONTUNDERL_NONE: mnUnderline = XML_none; break;
+ case BIFF_FONTUNDERL_SINGLE: mnUnderline = XML_single; break;
+ case BIFF_FONTUNDERL_DOUBLE: mnUnderline = XML_double; break;
+ case BIFF_FONTUNDERL_SINGLE_ACC: mnUnderline = XML_singleAccounting; break;
+ case BIFF_FONTUNDERL_DOUBLE_ACC: mnUnderline = XML_doubleAccounting; break;
+ default: mnUnderline = XML_none;
+ }
+}
+
+void FontModel::setBiffEscapement( sal_uInt16 nEscapement )
+{
+ static const sal_Int32 spnEscapes[] = { XML_baseline, XML_superscript, XML_subscript };
+ mnEscapement = STATIC_ARRAY_SELECT( spnEscapes, nEscapement, XML_baseline );
+}
+
+ApiFontUsedFlags::ApiFontUsedFlags( bool bAllUsed ) :
+ mbNameUsed( bAllUsed ),
+ mbColorUsed( bAllUsed ),
+ mbSchemeUsed( bAllUsed ),
+ mbHeightUsed( bAllUsed ),
+ mbUnderlineUsed( bAllUsed ),
+ mbEscapementUsed( bAllUsed ),
+ mbWeightUsed( bAllUsed ),
+ mbPostureUsed( bAllUsed ),
+ mbStrikeoutUsed( bAllUsed ),
+ mbOutlineUsed( bAllUsed ),
+ mbShadowUsed( bAllUsed )
+{
+}
+
+ApiScriptFontName::ApiScriptFontName() :
+ mnFamily( css::awt::FontFamily::DONTKNOW ),
+ mnTextEnc( RTL_TEXTENCODING_DONTKNOW )
+{
+}
+
+ApiFontData::ApiFontData() :
+ maDesc(
+ "Calibri",
+ 220, // height 11 points
+ 0,
+ OUString(),
+ css::awt::FontFamily::DONTKNOW,
+ RTL_TEXTENCODING_DONTKNOW,
+ css::awt::FontPitch::DONTKNOW,
+ 100.0,
+ css::awt::FontWeight::NORMAL,
+ css::awt::FontSlant_NONE,
+ css::awt::FontUnderline::NONE,
+ css::awt::FontStrikeout::NONE,
+ 0.0,
+ false,
+ false,
+ css::awt::FontType::DONTKNOW ),
+ mnColor( API_RGB_TRANSPARENT ),
+ mnEscapement( API_ESCAPE_NONE ),
+ mnEscapeHeight( API_ESCAPEHEIGHT_NONE ),
+ mbOutline( false ),
+ mbShadow( false )
+{
+ maLatinFont.maName = maDesc.Name;
+}
+
+Font::Font( const WorkbookHelper& rHelper, bool bDxf ) :
+ WorkbookHelper( rHelper ),
+ maModel( rHelper.getTheme().getDefaultFontModel() ),
+ maUsedFlags( !bDxf ),
+ mbDxf( bDxf )
+{
+}
+
+Font::Font( const WorkbookHelper& rHelper, FontModel aModel ) :
+ WorkbookHelper( rHelper ),
+ maModel(std::move( aModel )),
+ maUsedFlags( true ),
+ mbDxf( false )
+{
+}
+
+void Font::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ const FontModel& rDefModel = getTheme().getDefaultFontModel();
+ switch( nElement )
+ {
+ case XLS_TOKEN( name ): // when in <font> element
+ case XLS_TOKEN( rFont ): // when in <rPr> element
+ if( rAttribs.hasAttribute( XML_val ) )
+ {
+ maModel.maName = rAttribs.getXString( XML_val, OUString() );
+ maUsedFlags.mbNameUsed = true;
+ }
+ break;
+ case XLS_TOKEN( scheme ):
+ maModel.mnScheme = rAttribs.getToken( XML_val, rDefModel.mnScheme );
+ break;
+ case XLS_TOKEN( family ):
+ maModel.mnFamily = rAttribs.getInteger( XML_val, rDefModel.mnFamily );
+ break;
+ case XLS_TOKEN( charset ):
+ maModel.mnCharSet = rAttribs.getInteger( XML_val, rDefModel.mnCharSet );
+ break;
+ case XLS_TOKEN( sz ):
+ maModel.mfHeight = rAttribs.getDouble( XML_val, rDefModel.mfHeight );
+ maUsedFlags.mbHeightUsed = true;
+ break;
+ case XLS_TOKEN( color ):
+ maModel.maColor.importColor( rAttribs );
+ maUsedFlags.mbColorUsed = true;
+ break;
+ case XLS_TOKEN( u ):
+ maModel.mnUnderline = rAttribs.getToken( XML_val, XML_single );
+ maUsedFlags.mbUnderlineUsed = true;
+ break;
+ case XLS_TOKEN( vertAlign ):
+ maModel.mnEscapement = rAttribs.getToken( XML_val, XML_baseline );
+ maUsedFlags.mbEscapementUsed = true;
+ break;
+ case XLS_TOKEN( b ):
+ maModel.mbBold = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbWeightUsed = true;
+ break;
+ case XLS_TOKEN( i ):
+ maModel.mbItalic = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbPostureUsed = true;
+ break;
+ case XLS_TOKEN( strike ):
+ maModel.mbStrikeout = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbStrikeoutUsed = true;
+ break;
+ case XLS_TOKEN( outline ):
+ maModel.mbOutline = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbOutlineUsed = true;
+ break;
+ case XLS_TOKEN( shadow ):
+ maModel.mbShadow = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbShadowUsed = true;
+ break;
+ }
+}
+
+void Font::importFont( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( mbDxf, "sc", "Font::importFont - unexpected conditional formatting flag" );
+
+ sal_uInt16 nHeight, nFlags, nWeight, nEscapement;
+ sal_uInt8 nUnderline, nFamily, nCharSet, nScheme;
+ nHeight = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ nWeight = rStrm.readuInt16();
+ nEscapement = rStrm.readuInt16();
+ nUnderline = rStrm.readuChar();
+ nFamily = rStrm.readuChar();
+ nCharSet = rStrm.readuChar();
+ rStrm.skip( 1 );
+ rStrm >> maModel.maColor;
+ nScheme = rStrm.readuChar();
+ rStrm >> maModel.maName;
+
+ // equal constants in all BIFFs for weight, underline, and escapement
+ maModel.setBiff12Scheme( nScheme );
+ maModel.setBiffHeight( nHeight );
+ maModel.setBiffWeight( nWeight );
+ maModel.setBiffUnderline( nUnderline );
+ maModel.setBiffEscapement( nEscapement );
+ maModel.mnFamily = nFamily;
+ maModel.mnCharSet = nCharSet;
+ // equal flags in all BIFFs
+ maModel.mbItalic = getFlag( nFlags, BIFF_FONTFLAG_ITALIC );
+ maModel.mbStrikeout = getFlag( nFlags, BIFF_FONTFLAG_STRIKEOUT );
+ maModel.mbOutline = getFlag( nFlags, BIFF_FONTFLAG_OUTLINE );
+ maModel.mbShadow = getFlag( nFlags, BIFF_FONTFLAG_SHADOW );
+}
+
+void Font::importDxfName( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfName - missing conditional formatting flag" );
+ maModel.maName = BiffHelper::readString( rStrm, false );
+ maUsedFlags.mbColorUsed = true;
+}
+
+void Font::importDxfColor( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfColor - missing conditional formatting flag" );
+ rStrm >> maModel.maColor;
+ maUsedFlags.mbColorUsed = true;
+}
+
+void Font::importDxfScheme( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfScheme - missing conditional formatting flag" );
+ maModel.setBiff12Scheme( rStrm.readuInt8() );
+ maUsedFlags.mbSchemeUsed = true;
+}
+
+void Font::importDxfHeight( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfHeight - missing conditional formatting flag" );
+ maModel.setBiffHeight( rStrm.readuInt16() );
+ maUsedFlags.mbHeightUsed = true;
+}
+
+void Font::importDxfWeight( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfWeight - missing conditional formatting flag" );
+ maModel.setBiffWeight( rStrm.readuInt16() );
+ maUsedFlags.mbWeightUsed = true;
+}
+
+void Font::importDxfUnderline( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfUnderline - missing conditional formatting flag" );
+ maModel.setBiffUnderline( rStrm.readuInt16() );
+ maUsedFlags.mbUnderlineUsed = true;
+}
+
+void Font::importDxfEscapement( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfEscapement - missing conditional formatting flag" );
+ maModel.setBiffEscapement( rStrm.readuInt16() );
+ maUsedFlags.mbEscapementUsed = true;
+}
+
+void Font::importDxfFlag( sal_Int32 nElement, SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfFlag - missing conditional formatting flag" );
+ bool bFlag = rStrm.readuInt8() != 0;
+ switch( nElement )
+ {
+ case XML_i:
+ maModel.mbItalic = bFlag;
+ maUsedFlags.mbPostureUsed = true;
+ break;
+ case XML_strike:
+ maModel.mbStrikeout = bFlag;
+ maUsedFlags.mbStrikeoutUsed = true;
+ break;
+ case XML_outline:
+ maModel.mbOutline = bFlag;
+ maUsedFlags.mbOutlineUsed = true;
+ break;
+ case XML_shadow:
+ maModel.mbShadow = bFlag;
+ maUsedFlags.mbShadowUsed = true;
+ break;
+ default:
+ OSL_FAIL( "Font::importDxfFlag - unexpected element identifier" );
+ }
+}
+
+void Font::finalizeImport()
+{
+ // font name
+ maApiData.maDesc.Name = maModel.maName;
+
+ // font family
+ switch( maModel.mnFamily )
+ {
+ case OOX_FONTFAMILY_NONE: maApiData.maDesc.Family = css::awt::FontFamily::DONTKNOW; break;
+ case OOX_FONTFAMILY_ROMAN: maApiData.maDesc.Family = css::awt::FontFamily::ROMAN; break;
+ case OOX_FONTFAMILY_SWISS: maApiData.maDesc.Family = css::awt::FontFamily::SWISS; break;
+ case OOX_FONTFAMILY_MODERN: maApiData.maDesc.Family = css::awt::FontFamily::MODERN; break;
+ case OOX_FONTFAMILY_SCRIPT: maApiData.maDesc.Family = css::awt::FontFamily::SCRIPT; break;
+ case OOX_FONTFAMILY_DECORATIVE: maApiData.maDesc.Family = css::awt::FontFamily::DECORATIVE; break;
+ }
+
+ // character set (API font descriptor uses rtl_TextEncoding in member CharSet!)
+ if( (0 <= maModel.mnCharSet) && (maModel.mnCharSet <= SAL_MAX_UINT8) )
+ maApiData.maDesc.CharSet = static_cast< sal_Int16 >(
+ rtl_getTextEncodingFromWindowsCharset( static_cast< sal_uInt8 >( maModel.mnCharSet ) ) );
+
+ // color, height, weight, slant, strikeout, outline, shadow
+ maApiData.maComplexColor = maModel.maColor.createComplexColor(getBaseFilter().getGraphicHelper(), -1);
+ maApiData.mnColor = maModel.maColor.getColor( getBaseFilter().getGraphicHelper() );
+ maApiData.maDesc.Height = static_cast< sal_Int16 >( maModel.mfHeight * 20.0 );
+ maApiData.maDesc.Weight = maModel.mbBold ? css::awt::FontWeight::BOLD : css::awt::FontWeight::NORMAL;
+ maApiData.maDesc.Slant = maModel.mbItalic ? css::awt::FontSlant_ITALIC : css::awt::FontSlant_NONE;
+ maApiData.maDesc.Strikeout = maModel.mbStrikeout ? css::awt::FontStrikeout::SINGLE : css::awt::FontStrikeout::NONE;
+ maApiData.mbOutline = maModel.mbOutline;
+ maApiData.mbShadow = maModel.mbShadow;
+
+ // underline
+ switch( maModel.mnUnderline )
+ {
+ case XML_double: maApiData.maDesc.Underline = css::awt::FontUnderline::DOUBLE; break;
+ case XML_doubleAccounting: maApiData.maDesc.Underline = css::awt::FontUnderline::DOUBLE; break;
+ case XML_none: maApiData.maDesc.Underline = css::awt::FontUnderline::NONE; break;
+ case XML_single: maApiData.maDesc.Underline = css::awt::FontUnderline::SINGLE; break;
+ case XML_singleAccounting: maApiData.maDesc.Underline = css::awt::FontUnderline::SINGLE; break;
+ }
+
+ // escapement
+ switch( maModel.mnEscapement )
+ {
+ case XML_baseline:
+ maApiData.mnEscapement = API_ESCAPE_NONE;
+ maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_NONE;
+ break;
+ case XML_superscript:
+ maApiData.mnEscapement = API_ESCAPE_SUPERSCRIPT;
+ maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_DEFAULT;
+ break;
+ case XML_subscript:
+ maApiData.mnEscapement = API_ESCAPE_SUBSCRIPT;
+ maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_DEFAULT;
+ break;
+ }
+
+ // supported script types
+ if( !maUsedFlags.mbNameUsed )
+ return;
+
+ bool bHasAsian(false), bHasCmplx(false), bHasLatin(false);
+ FontClassificationMap& rFontClassificationCache = getFontClassificationCache();
+ if (auto found = rFontClassificationCache.find(maApiData.maDesc); found != rFontClassificationCache.end())
+ {
+ FontClassification eClassification = found->second;
+ bHasAsian = bool(eClassification & FontClassification::Asian);
+ bHasCmplx = bool(eClassification & FontClassification::Cmplx);
+ bHasLatin = bool(eClassification & FontClassification::Latin);
+ }
+ else
+ {
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY );
+ if( !xDevice.is() )
+ return;
+
+ Reference< XFont2 > xFont( xDevice->getFont( maApiData.maDesc ), UNO_QUERY );
+ if( !xFont.is() )
+ return;
+
+ // #91658# CJK fonts
+ bHasAsian =
+ xFont->hasGlyphs( OUString( u'\x3041' ) ) || // 3040-309F: Hiragana
+ xFont->hasGlyphs( OUString( u'\x30A1' ) ) || // 30A0-30FF: Katakana
+ xFont->hasGlyphs( OUString( u'\x3111' ) ) || // 3100-312F: Bopomofo
+ xFont->hasGlyphs( OUString( u'\x3131' ) ) || // 3130-318F: Hangul Compatibility Jamo
+ xFont->hasGlyphs( OUString( u'\x3301' ) ) || // 3300-33FF: CJK Compatibility
+ xFont->hasGlyphs( OUString( u'\x3401' ) ) || // 3400-4DBF: CJK Unified Ideographs Extension A
+ xFont->hasGlyphs( OUString( u'\x4E01' ) ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFont->hasGlyphs( OUString( u'\x7E01' ) ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFont->hasGlyphs( OUString( u'\xA001' ) ) || // A001-A48F: Yi Syllables
+ xFont->hasGlyphs( OUString( u'\xAC01' ) ) || // AC00-D7AF: Hangul Syllables
+ xFont->hasGlyphs( OUString( u'\xCC01' ) ) || // AC00-D7AF: Hangul Syllables
+ xFont->hasGlyphs( OUString( u'\xF901' ) ) || // F900-FAFF: CJK Compatibility Ideographs
+ xFont->hasGlyphs( OUString( u'\xFF71' ) ); // FF00-FFEF: Halfwidth/Fullwidth Forms
+ // #113783# CTL fonts
+ bHasCmplx =
+ xFont->hasGlyphs( OUString( u'\x05D1' ) ) || // 0590-05FF: Hebrew
+ xFont->hasGlyphs( OUString( u'\x0631' ) ) || // 0600-06FF: Arabic
+ xFont->hasGlyphs( OUString( u'\x0721' ) ) || // 0700-074F: Syriac
+ xFont->hasGlyphs( OUString( u'\x0911' ) ) || // 0900-0DFF: Indic scripts
+ xFont->hasGlyphs( OUString( u'\x0E01' ) ) || // 0E00-0E7F: Thai
+ xFont->hasGlyphs( OUString( u'\xFB21' ) ) || // FB1D-FB4F: Hebrew Presentation Forms
+ xFont->hasGlyphs( OUString( u'\xFB51' ) ) || // FB50-FDFF: Arabic Presentation Forms-A
+ xFont->hasGlyphs( OUString( u'\xFE71' ) ); // FE70-FEFF: Arabic Presentation Forms-B
+ // Western fonts
+ bHasLatin =
+ (!bHasAsian && !bHasCmplx) ||
+ xFont->hasGlyphs( OUString( 'A' ) );
+
+ FontClassification eClassification(FontClassification::None);
+ if (bHasAsian)
+ eClassification = eClassification | FontClassification::Asian;
+ if (bHasCmplx)
+ eClassification = eClassification | FontClassification::Cmplx;
+ if (bHasLatin)
+ eClassification = eClassification | FontClassification::Latin;
+ rFontClassificationCache.emplace(maApiData.maDesc, eClassification);
+ }
+
+ lclSetFontName( maApiData.maLatinFont, maApiData.maDesc, bHasLatin );
+ lclSetFontName( maApiData.maAsianFont, maApiData.maDesc, bHasAsian );
+ lclSetFontName( maApiData.maCmplxFont, maApiData.maDesc, bHasCmplx );
+}
+
+bool Font::needsRichTextFormat() const
+{
+ return maApiData.mnEscapement != API_ESCAPE_NONE;
+}
+
+static ::FontFamily lcl_getFontFamily( sal_Int32 nFamily )
+{
+ ::FontFamily eScFamily = FAMILY_DONTKNOW;
+ switch( nFamily )
+ {
+ case css::awt::FontFamily::DONTKNOW:
+ eScFamily = FAMILY_DONTKNOW;
+ break;
+ case css::awt::FontFamily::ROMAN:
+ eScFamily = FAMILY_ROMAN;
+ break;
+ case css::awt::FontFamily::SWISS:
+ eScFamily = FAMILY_SWISS;
+ break;
+ case css::awt::FontFamily::MODERN:
+ eScFamily = FAMILY_MODERN;
+ break;
+ case css::awt::FontFamily::SCRIPT:
+ eScFamily = FAMILY_SCRIPT;
+ break;
+ case css::awt::FontFamily::DECORATIVE:
+ eScFamily = FAMILY_DECORATIVE;
+ break;
+ }
+ return eScFamily;
+}
+
+void Font::fillToItemSet( SfxItemSet& rItemSet, bool bEditEngineText, bool bSkipPoolDefs ) const
+{
+ if ( maUsedFlags.mbNameUsed )
+ {
+ if( !maApiData.maLatinFont.maName.isEmpty() )
+ {
+ rtl_TextEncoding eFontEnc = maApiData.maLatinFont.mnTextEnc;
+ // taken from binary importer
+ rtl_TextEncoding eTempTextEnc = (bEditEngineText && (eFontEnc == getTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+
+ SvxFontItem aFontItem( lcl_getFontFamily( maApiData.maLatinFont.mnFamily ), maApiData.maLatinFont.maName, OUString(),
+ PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT );
+ ScfTools::PutItem( rItemSet, aFontItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTINFO) : ATTR_FONT, bSkipPoolDefs );
+ }
+ if( !maApiData.maAsianFont.maName.isEmpty() )
+ {
+ rtl_TextEncoding eFontEnc = maApiData.maAsianFont.mnTextEnc;
+ // taken from binary importer
+ rtl_TextEncoding eTempTextEnc = (bEditEngineText && (eFontEnc == getTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+ SvxFontItem aFontItem( lcl_getFontFamily( maApiData.maAsianFont.mnFamily ), maApiData.maAsianFont.maName, OUString(),
+ PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT );
+ ScfTools::PutItem( rItemSet, aFontItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTINFO_CJK) : ATTR_CJK_FONT, bSkipPoolDefs );
+ }
+ if( !maApiData.maCmplxFont.maName.isEmpty() )
+ {
+ rtl_TextEncoding eFontEnc = maApiData.maCmplxFont.mnTextEnc;
+ // taken from binary importer
+ rtl_TextEncoding eTempTextEnc = (bEditEngineText && (eFontEnc == getTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+ SvxFontItem aFontItem( lcl_getFontFamily( maApiData.maCmplxFont.mnFamily ), maApiData.maCmplxFont.maName, OUString(),
+ PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT );
+ ScfTools::PutItem( rItemSet, aFontItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTINFO_CTL) : ATTR_CTL_FONT, bSkipPoolDefs );
+ }
+ }
+ // font height
+ if( maUsedFlags.mbHeightUsed )
+ {
+ sal_Int32 nHeight = maApiData.maDesc.Height;
+ // do we use XclFontItemType::HeaderFooter ( or is it just relevant for the binary filter )
+ if( bEditEngineText/* && (eType != XclFontItemType::HeaderFooter) */) // do not convert header/footer height
+ nHeight = convertTwipToMm100(nHeight);
+ SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT );
+ ScfTools::PutItem( rItemSet, aHeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTHEIGHT) : ATTR_FONT_HEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aHeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTHEIGHT_CJK) : ATTR_CJK_FONT_HEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aHeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTHEIGHT_CTL) : ATTR_CTL_FONT_HEIGHT, bSkipPoolDefs );
+ }
+ // font weight
+ if( maUsedFlags.mbWeightUsed )
+ {
+ ::FontWeight fWeight = vcl::unohelper::ConvertFontWeight( maApiData.maDesc.Weight );
+ SvxWeightItem aWeightItem( fWeight, ATTR_FONT_WEIGHT );
+ ScfTools::PutItem( rItemSet, aWeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_WEIGHT) : ATTR_FONT_WEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aWeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_WEIGHT_CTL) : ATTR_CTL_FONT_WEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aWeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_WEIGHT_CJK) : ATTR_CJK_FONT_WEIGHT, bSkipPoolDefs );
+ }
+ // font posture
+ if( maUsedFlags.mbPostureUsed )
+ {
+ SvxPostureItem aPostItem( ( maApiData.maDesc.Slant == css::awt::FontSlant_ITALIC ) ? ITALIC_NORMAL : ITALIC_NONE, ATTR_FONT_POSTURE);
+ ScfTools::PutItem( rItemSet, aPostItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_ITALIC) : ATTR_FONT_POSTURE, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aPostItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_ITALIC_CJK) : ATTR_CJK_FONT_POSTURE, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aPostItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_ITALIC_CTL) : ATTR_CTL_FONT_POSTURE, bSkipPoolDefs );
+ }
+ // character color
+ if( maUsedFlags.mbColorUsed )
+ {
+ ScfTools::PutItem( rItemSet,SvxColorItem( maApiData.mnColor, maApiData.maComplexColor, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_COLOR) : ATTR_FONT_COLOR), bSkipPoolDefs );
+ }
+ // underline style
+ if( maUsedFlags.mbUnderlineUsed )
+ {
+ FontLineStyle eScUnderl;
+ if ( maApiData.maDesc.Underline == css::awt::FontUnderline::DOUBLE )
+ eScUnderl = LINESTYLE_DOUBLE;
+ else if ( maApiData.maDesc.Underline == css::awt::FontUnderline::SINGLE )
+ eScUnderl = LINESTYLE_SINGLE;
+ else
+ eScUnderl = LINESTYLE_NONE;
+ SvxUnderlineItem aUnderlItem( eScUnderl, ATTR_FONT_UNDERLINE );
+ ScfTools::PutItem( rItemSet, aUnderlItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_UNDERLINE) : ATTR_FONT_UNDERLINE, bSkipPoolDefs );
+ }
+ // strike out style
+ if( maUsedFlags.mbStrikeoutUsed )
+ {
+ ScfTools::PutItem( rItemSet, SvxCrossedOutItem( maModel.mbStrikeout ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_STRIKEOUT) : ATTR_FONT_CROSSEDOUT ), bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_STRIKEOUT) : ATTR_FONT_CROSSEDOUT, bSkipPoolDefs );
+ }
+
+ // outline style
+ if( maUsedFlags.mbOutlineUsed )
+ {
+ ScfTools::PutItem( rItemSet, SvxContourItem( maApiData.mbOutline, ATTR_FONT_CONTOUR ), bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_OUTLINE) : ATTR_FONT_CONTOUR, bSkipPoolDefs );
+ }
+
+ // shadow style
+ if( maUsedFlags.mbShadowUsed )
+ {
+ ScfTools::PutItem( rItemSet, SvxShadowedItem( maApiData.mbShadow, ATTR_FONT_SHADOWED ), bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_SHADOW) : ATTR_FONT_SHADOWED, bSkipPoolDefs );
+ }
+ if( !maUsedFlags.mbEscapementUsed )
+ return;
+
+ SvxEscapement eScEscapem = SvxEscapement::Off;
+ if ( maApiData.mnEscapement == API_ESCAPE_SUPERSCRIPT )
+ eScEscapem = SvxEscapement::Superscript;
+ else if ( maApiData.mnEscapement == API_ESCAPE_SUBSCRIPT )
+ eScEscapem = SvxEscapement::Subscript;
+ if( bEditEngineText )
+ {
+ // #TODO handle EscapementHeight
+ rItemSet.Put( SvxEscapementItem( eScEscapem, EE_CHAR_ESCAPEMENT ) );
+ }
+}
+
+void Font::writeToPropertyMap( PropertyMap& rPropMap ) const
+{
+ // font name properties
+ if( maUsedFlags.mbNameUsed )
+ {
+ if( !maApiData.maLatinFont.maName.isEmpty() )
+ {
+ rPropMap.setProperty( PROP_CharFontName, maApiData.maLatinFont.maName);
+ rPropMap.setProperty( PROP_CharFontFamily, maApiData.maLatinFont.mnFamily);
+ rPropMap.setProperty( PROP_CharFontCharSet, maApiData.maLatinFont.mnTextEnc);
+ }
+ if( !maApiData.maAsianFont.maName.isEmpty() )
+ {
+ rPropMap.setProperty( PROP_CharFontNameAsian, maApiData.maAsianFont.maName);
+ rPropMap.setProperty( PROP_CharFontFamilyAsian, maApiData.maAsianFont.mnFamily);
+ rPropMap.setProperty( PROP_CharFontCharSetAsian, maApiData.maAsianFont.mnTextEnc);
+ }
+ if( !maApiData.maCmplxFont.maName.isEmpty() )
+ {
+ rPropMap.setProperty( PROP_CharFontNameComplex, maApiData.maCmplxFont.maName);
+ rPropMap.setProperty( PROP_CharFontFamilyComplex, maApiData.maCmplxFont.mnFamily);
+ rPropMap.setProperty( PROP_CharFontCharSetComplex, maApiData.maCmplxFont.mnTextEnc);
+ }
+ }
+ // font height
+ if( maUsedFlags.mbHeightUsed )
+ {
+ float fHeight = static_cast< float >( maApiData.maDesc.Height / 20.0 ); // twips to points
+ rPropMap.setProperty( PROP_CharHeight, fHeight);
+ rPropMap.setProperty( PROP_CharHeightAsian, fHeight);
+ rPropMap.setProperty( PROP_CharHeightComplex, fHeight);
+ }
+ // font weight
+ if( maUsedFlags.mbWeightUsed )
+ {
+ float fWeight = maApiData.maDesc.Weight;
+ rPropMap.setProperty( PROP_CharWeight, fWeight);
+ rPropMap.setProperty( PROP_CharWeightAsian, fWeight);
+ rPropMap.setProperty( PROP_CharWeightComplex, fWeight);
+ }
+ // font posture
+ if( maUsedFlags.mbPostureUsed )
+ {
+ rPropMap.setProperty( PROP_CharPosture, maApiData.maDesc.Slant);
+ rPropMap.setProperty( PROP_CharPostureAsian, maApiData.maDesc.Slant);
+ rPropMap.setProperty( PROP_CharPostureComplex, maApiData.maDesc.Slant);
+ }
+ // character color
+ if( maUsedFlags.mbColorUsed )
+ rPropMap.setProperty( PROP_CharColor, maApiData.mnColor);
+ // underline style
+ if( maUsedFlags.mbUnderlineUsed )
+ rPropMap.setProperty( PROP_CharUnderline, maApiData.maDesc.Underline);
+ // strike out style
+ if( maUsedFlags.mbStrikeoutUsed )
+ rPropMap.setProperty( PROP_CharStrikeout, maApiData.maDesc.Strikeout);
+ // outline style
+ if( maUsedFlags.mbOutlineUsed )
+ rPropMap.setProperty( PROP_CharContoured, maApiData.mbOutline);
+ // shadow style
+ if( maUsedFlags.mbShadowUsed )
+ rPropMap.setProperty( PROP_CharShadowed, maApiData.mbShadow);
+ // escapement
+ if( maUsedFlags.mbEscapementUsed )
+ {
+ rPropMap.setProperty( PROP_CharEscapement, maApiData.mnEscapement);
+ rPropMap.setProperty( PROP_CharEscapementHeight, maApiData.mnEscapeHeight);
+ }
+}
+
+void Font::writeToPropertySet( PropertySet& rPropSet ) const
+{
+ PropertyMap aPropMap;
+ writeToPropertyMap( aPropMap );
+ rPropSet.setProperties( aPropMap );
+}
+
+AlignmentModel::AlignmentModel() :
+ mnHorAlign( XML_general ),
+ mnVerAlign( XML_bottom ),
+ mnTextDir( OOX_XF_TEXTDIR_CONTEXT ),
+ mnRotation( OOX_XF_ROTATION_NONE ),
+ mnIndent( OOX_XF_INDENT_NONE ),
+ mbWrapText( false ),
+ mbShrink( false ),
+ mbJustLastLine( false )
+{
+}
+
+void AlignmentModel::setBiffHorAlign( sal_uInt8 nHorAlign )
+{
+ static const sal_Int32 spnHorAligns[] = {
+ XML_general, XML_left, XML_center, XML_right,
+ XML_fill, XML_justify, XML_centerContinuous, XML_distributed };
+ mnHorAlign = STATIC_ARRAY_SELECT( spnHorAligns, nHorAlign, XML_general );
+}
+
+void AlignmentModel::setBiffVerAlign( sal_uInt8 nVerAlign )
+{
+ static const sal_Int32 spnVerAligns[] = {
+ XML_top, XML_center, XML_bottom, XML_justify, XML_distributed };
+ mnVerAlign = STATIC_ARRAY_SELECT( spnVerAligns, nVerAlign, XML_bottom );
+}
+
+ApiAlignmentData::ApiAlignmentData() :
+ meHorJustify( css::table::CellHoriJustify_STANDARD ),
+ mnHorJustifyMethod( css::table::CellJustifyMethod::AUTO ),
+ mnVerJustify( css::table::CellVertJustify2::STANDARD ),
+ mnVerJustifyMethod( css::table::CellJustifyMethod::AUTO ),
+ meOrientation( css::table::CellOrientation_STANDARD ),
+ mnRotation( 0 ),
+ mnWritingMode( css::text::WritingMode2::PAGE ),
+ mnIndent( 0 ),
+ mbWrapText( false ),
+ mbShrink( false )
+{
+}
+
+bool operator==( const ApiAlignmentData& rLeft, const ApiAlignmentData& rRight )
+{
+ return
+ (rLeft.meHorJustify == rRight.meHorJustify) &&
+ (rLeft.mnHorJustifyMethod == rRight.mnHorJustifyMethod) &&
+ (rLeft.mnVerJustify == rRight.mnVerJustify) &&
+ (rLeft.mnVerJustifyMethod == rRight.mnVerJustifyMethod) &&
+ (rLeft.meOrientation == rRight.meOrientation) &&
+ (rLeft.mnRotation == rRight.mnRotation) &&
+ (rLeft.mnWritingMode == rRight.mnWritingMode) &&
+ (rLeft.mnIndent == rRight.mnIndent) &&
+ (rLeft.mbWrapText == rRight.mbWrapText) &&
+ (rLeft.mbShrink == rRight.mbShrink);
+}
+
+Alignment::Alignment( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void Alignment::importAlignment( const AttributeList& rAttribs )
+{
+ maModel.mnVerAlign = rAttribs.getToken( XML_vertical, XML_bottom );
+ maModel.mnTextDir = rAttribs.getInteger( XML_readingOrder, OOX_XF_TEXTDIR_CONTEXT );
+ maModel.mnRotation = rAttribs.getInteger( XML_textRotation, OOX_XF_ROTATION_NONE );
+ sal_Int32 nDefaultHorAlign = XML_general;
+ if (maModel.mnRotation != OOX_XF_ROTATION_NONE)
+ {
+ if (maModel.mnRotation < 90 || maModel.mnRotation == 180)
+ {
+ nDefaultHorAlign = XML_left;
+ }
+ else
+ {
+ nDefaultHorAlign = XML_right;
+ }
+ }
+ maModel.mnHorAlign = rAttribs.getToken( XML_horizontal, nDefaultHorAlign );
+ maModel.mnIndent = rAttribs.getInteger( XML_indent, OOX_XF_INDENT_NONE );
+ maModel.mbWrapText = rAttribs.getBool( XML_wrapText, false );
+ maModel.mbShrink = rAttribs.getBool( XML_shrinkToFit, false );
+ maModel.mbJustLastLine = rAttribs.getBool( XML_justifyLastLine, false );
+}
+
+void Alignment::setBiff12Data( sal_uInt32 nFlags )
+{
+ maModel.setBiffHorAlign( extractValue< sal_uInt8 >( nFlags, 16, 3 ) );
+ maModel.setBiffVerAlign( extractValue< sal_uInt8 >( nFlags, 19, 3 ) );
+ maModel.mnTextDir = extractValue< sal_Int32 >( nFlags, 26, 2 );
+ maModel.mnRotation = extractValue< sal_Int32 >( nFlags, 0, 8 );
+ maModel.mnIndent = extractValue< sal_uInt8 >( nFlags, 8, 8 );
+ maModel.mbWrapText = getFlag( nFlags, BIFF12_XF_WRAPTEXT );
+ maModel.mbShrink = getFlag( nFlags, BIFF12_XF_SHRINK );
+ maModel.mbJustLastLine = getFlag( nFlags, BIFF12_XF_JUSTLASTLINE );
+}
+
+void Alignment::finalizeImport()
+{
+ // horizontal alignment
+ switch( maModel.mnHorAlign )
+ {
+ case XML_center: maApiData.meHorJustify = css::table::CellHoriJustify_CENTER; break;
+ case XML_centerContinuous: maApiData.meHorJustify = css::table::CellHoriJustify_CENTER; break;
+ case XML_distributed: maApiData.meHorJustify = css::table::CellHoriJustify_BLOCK; break;
+ case XML_fill: maApiData.meHorJustify = css::table::CellHoriJustify_REPEAT; break;
+ case XML_general: maApiData.meHorJustify = css::table::CellHoriJustify_STANDARD; break;
+ case XML_justify: maApiData.meHorJustify = css::table::CellHoriJustify_BLOCK; break;
+ case XML_left: maApiData.meHorJustify = css::table::CellHoriJustify_LEFT; break;
+ case XML_right: maApiData.meHorJustify = css::table::CellHoriJustify_RIGHT; break;
+ }
+
+ if (maModel.mnHorAlign == XML_distributed)
+ maApiData.mnHorJustifyMethod = css::table::CellJustifyMethod::DISTRIBUTE;
+
+ // vertical alignment
+ switch( maModel.mnVerAlign )
+ {
+ case XML_bottom: maApiData.mnVerJustify = css::table::CellVertJustify2::BOTTOM; break;
+ case XML_center: maApiData.mnVerJustify = css::table::CellVertJustify2::CENTER; break;
+ case XML_distributed: maApiData.mnVerJustify = css::table::CellVertJustify2::BLOCK; break;
+ case XML_justify: maApiData.mnVerJustify = css::table::CellVertJustify2::BLOCK; break;
+ case XML_top: maApiData.mnVerJustify = css::table::CellVertJustify2::TOP; break;
+ }
+
+ if (maModel.mnVerAlign == XML_distributed)
+ maApiData.mnVerJustifyMethod = css::table::CellJustifyMethod::DISTRIBUTE;
+
+ /* indentation: expressed as number of blocks of 3 space characters in
+ OOXML. */
+ UnitConverter& rUnitConverter = getUnitConverter();
+ // Note: indents are stored in twips
+ sal_Int32 nIndent = rUnitConverter.scaleValue( 3.0 * maModel.mnIndent, Unit::Space, Unit::Twip);
+ if( (0 <= nIndent) && (nIndent <= SAL_MAX_INT16) )
+ maApiData.mnIndent = static_cast< sal_Int16 >( nIndent );
+
+ // complex text direction
+ switch( maModel.mnTextDir )
+ {
+ case OOX_XF_TEXTDIR_CONTEXT: maApiData.mnWritingMode = css::text::WritingMode2::PAGE; break;
+ case OOX_XF_TEXTDIR_LTR: maApiData.mnWritingMode = css::text::WritingMode2::LR_TB; break;
+ case OOX_XF_TEXTDIR_RTL: maApiData.mnWritingMode = css::text::WritingMode2::RL_TB; break;
+ }
+
+ // rotation: 0-90 means 0 to 90 degrees ccw, 91-180 means 1 to 90 degrees cw, 255 means stacked
+ sal_Int32 nOoxRot = maModel.mnRotation;
+ maApiData.mnRotation = Degree100(((0 <= nOoxRot) && (nOoxRot <= 90)) ?
+ (100 * nOoxRot) :
+ (((91 <= nOoxRot) && (nOoxRot <= 180)) ? (100 * (450 - nOoxRot)) : 0));
+
+ // "Orientation" property used for character stacking
+ maApiData.meOrientation = (nOoxRot == OOX_XF_ROTATION_STACKED) ?
+ css::table::CellOrientation_STACKED : css::table::CellOrientation_STANDARD;
+
+ // alignment flags (#i84960 automatic line break, if vertically justified/distributed)
+ maApiData.mbWrapText = maModel.mbWrapText || (maModel.mnVerAlign == XML_distributed) || (maModel.mnVerAlign == XML_justify);
+ maApiData.mbShrink = maModel.mbShrink;
+
+}
+
+::SvxCellVerJustify Alignment::GetScVerAlign() const
+{
+ ::SvxCellVerJustify nVert = ::SvxCellVerJustify::Standard;
+ switch ( maApiData.mnVerJustify )
+ {
+ case css::table::CellVertJustify2::BOTTOM:
+ nVert = ::SvxCellVerJustify::Bottom;
+ break;
+ case css::table::CellVertJustify2::CENTER:
+ nVert = ::SvxCellVerJustify::Center;
+ break;
+ case css::table::CellVertJustify2::TOP:
+ nVert = ::SvxCellVerJustify::Top;
+ break;
+ case css::table::CellVertJustify2::BLOCK:
+ nVert = ::SvxCellVerJustify::Block;
+ break;
+ case css::table::CellVertJustify2::STANDARD:
+ default:
+ nVert = ::SvxCellVerJustify::Standard;
+ break;
+ }
+ return nVert;
+}
+
+::SvxCellHorJustify Alignment::GetScHorAlign() const
+{
+ ::SvxCellHorJustify nHori = ::SvxCellHorJustify::Standard;
+ switch( maApiData.meHorJustify )
+ {
+ case css::table::CellHoriJustify_LEFT:
+ nHori = ::SvxCellHorJustify::Left;
+ break;
+ case css::table::CellHoriJustify_CENTER:
+ nHori = ::SvxCellHorJustify::Center;
+ break;
+ case css::table::CellHoriJustify_RIGHT:
+ nHori = ::SvxCellHorJustify::Right;
+ break;
+ case css::table::CellHoriJustify_BLOCK:
+ nHori = ::SvxCellHorJustify::Block;
+ break;
+ case css::table::CellHoriJustify_REPEAT:
+ nHori = ::SvxCellHorJustify::Repeat;
+ break;
+ case css::table::CellHoriJustify_STANDARD:
+ default:
+ nHori = ::SvxCellHorJustify::Standard;
+ break;
+ }
+ return nHori;
+}
+
+SvxFrameDirection Alignment::GetScFrameDir() const
+{
+ SvxFrameDirection eFrameDir = SvxFrameDirection::Environment;
+ switch( maApiData.mnWritingMode )
+ {
+ case css::text::WritingMode2::PAGE:
+ eFrameDir = SvxFrameDirection::Environment;
+ break;
+ case css::text::WritingMode2::LR_TB:
+ eFrameDir = SvxFrameDirection::Horizontal_LR_TB;
+ break;
+ case css::text::WritingMode2::RL_TB:
+ eFrameDir = SvxFrameDirection::Horizontal_RL_TB;
+ break;
+ default:
+ OSL_FAIL( "GetScFrameDir - unknown CTL text direction" );
+ }
+ return eFrameDir;
+}
+
+void Alignment::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ // horizontal alignment
+ ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( ( maApiData.mnHorJustifyMethod == css::table::CellJustifyMethod::DISTRIBUTE ) ? ::SvxCellJustifyMethod::Distribute : ::SvxCellJustifyMethod::Auto, ATTR_HOR_JUSTIFY_METHOD ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs );
+ // vertical alignment
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( ( maApiData.mnVerJustifyMethod == css::table::CellJustifyMethod::DISTRIBUTE ) ? ::SvxCellJustifyMethod::Distribute : ::SvxCellJustifyMethod::Auto, ATTR_VER_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // CTL text direction
+ ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs );
+ // set an angle in the range from -90 to 90 degrees
+ ScfTools::PutItem( rItemSet, ScRotateValueItem( maApiData.mnRotation ), bSkipPoolDefs );
+ // Orientation
+ ScfTools::PutItem( rItemSet, ScVerticalStackCell( maApiData.meOrientation == css::table::CellOrientation_STACKED ), bSkipPoolDefs );
+ // indent
+ ScfTools::PutItem( rItemSet, ScIndentItem( maApiData.mnIndent ), bSkipPoolDefs );
+ // line wrap
+ ScfTools::PutItem( rItemSet, ScLineBreakCell( maApiData.mbWrapText ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, ScShrinkToFitCell( maApiData.mbShrink ), bSkipPoolDefs );
+}
+
+ProtectionModel::ProtectionModel() :
+ mbLocked( true ), // default in Excel and Calc
+ mbHidden( false )
+{
+}
+
+ApiProtectionData::ApiProtectionData() :
+ maCellProt( true, false, false, false )
+{
+}
+
+bool operator==( const ApiProtectionData& rLeft, const ApiProtectionData& rRight )
+{
+ return
+ (rLeft.maCellProt.IsLocked == rRight.maCellProt.IsLocked) &&
+ (rLeft.maCellProt.IsFormulaHidden == rRight.maCellProt.IsFormulaHidden) &&
+ (rLeft.maCellProt.IsHidden == rRight.maCellProt.IsHidden) &&
+ (rLeft.maCellProt.IsPrintHidden == rRight.maCellProt.IsPrintHidden);
+}
+
+Protection::Protection( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void Protection::importProtection( const AttributeList& rAttribs )
+{
+ maModel.mbLocked = rAttribs.getBool( XML_locked, true );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+}
+
+void Protection::setBiff12Data( sal_uInt32 nFlags )
+{
+ maModel.mbLocked = getFlag( nFlags, BIFF12_XF_LOCKED );
+ maModel.mbHidden = getFlag( nFlags, BIFF12_XF_HIDDEN );
+}
+
+void Protection::finalizeImport()
+{
+ maApiData.maCellProt.IsLocked = maModel.mbLocked;
+ maApiData.maCellProt.IsFormulaHidden = maModel.mbHidden;
+}
+
+void Protection::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ ScfTools::PutItem( rItemSet, ScProtectionAttr( maApiData.maCellProt.IsLocked, maApiData.maCellProt.IsFormulaHidden ), bSkipPoolDefs );
+}
+
+namespace {
+
+bool lcl_isBorder(const css::table::BorderLine& rBorder)
+{
+ return (rBorder.InnerLineWidth > 0) || (rBorder.OuterLineWidth > 0);
+}
+
+}
+
+BorderLineModel::BorderLineModel( bool bDxf ) :
+ mnStyle( XML_none ),
+ mbUsed( !bDxf )
+{
+ maColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+}
+
+void BorderLineModel::setBiffStyle( sal_Int32 nLineStyle )
+{
+ static const sal_Int32 spnStyleIds[] = {
+ XML_none, XML_thin, XML_medium, XML_dashed,
+ XML_dotted, XML_thick, XML_double, XML_hair,
+ XML_mediumDashed, XML_dashDot, XML_mediumDashDot, XML_dashDotDot,
+ XML_mediumDashDotDot, XML_slantDashDot };
+ mnStyle = STATIC_ARRAY_SELECT( spnStyleIds, nLineStyle, XML_none );
+}
+
+BorderModel::BorderModel( bool bDxf ) :
+ maLeft( bDxf ),
+ maRight( bDxf ),
+ maTop( bDxf ),
+ maBottom( bDxf ),
+ maDiagonal( bDxf ),
+ mbDiagTLtoBR( false ),
+ mbDiagBLtoTR( false )
+{
+}
+
+ApiBorderData::ApiBorderData() :
+ mbBorderUsed( false ),
+ mbDiagUsed( false )
+{
+}
+
+bool ApiBorderData::hasAnyOuterBorder() const
+{
+ return
+ ( lcl_isBorder( maTop ) && maTop.OuterLineWidth > 0 ) ||
+ ( lcl_isBorder( maBottom ) && maBottom.OuterLineWidth > 0 ) ||
+ ( lcl_isBorder( maLeft ) && maLeft.OuterLineWidth > 0 ) ||
+ ( lcl_isBorder( maRight ) && maRight.OuterLineWidth > 0 );
+}
+
+namespace {
+
+void lclSetBorderLineWidth( BorderLine& rBorderLine,
+ sal_Int16 nOuter, sal_Int16 nDist = API_LINE_NONE, sal_Int16 nInner = API_LINE_NONE )
+{
+ rBorderLine.OuterLineWidth = nOuter;
+ rBorderLine.LineDistance = nDist;
+ rBorderLine.InnerLineWidth = nInner;
+}
+
+} // namespace
+
+Border::Border( const WorkbookHelper& rHelper, bool bDxf ) :
+ WorkbookHelper( rHelper ),
+ maModel( bDxf ),
+ mbDxf( bDxf )
+{
+}
+
+void Border::importBorder( const AttributeList& rAttribs )
+{
+ maModel.mbDiagTLtoBR = rAttribs.getBool( XML_diagonalDown, false );
+ maModel.mbDiagBLtoTR = rAttribs.getBool( XML_diagonalUp, false );
+}
+
+void Border::importStyle( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( BorderLineModel* pBorderLine = getBorderLine( nElement ) )
+ {
+ pBorderLine->mnStyle = rAttribs.getToken( XML_style, XML_none );
+ pBorderLine->mbUsed = true;
+ }
+}
+
+void Border::importColor( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( BorderLineModel* pBorderLine = getBorderLine( nElement ) )
+ pBorderLine->maColor.importColor( rAttribs );
+}
+
+void Border::importBorder( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags = rStrm.readuInt8();
+ maModel.mbDiagTLtoBR = getFlag( nFlags, BIFF12_BORDER_DIAG_TLBR );
+ maModel.mbDiagBLtoTR = getFlag( nFlags, BIFF12_BORDER_DIAG_BLTR );
+ maModel.maTop.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maTop.maColor;
+ maModel.maBottom.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maBottom.maColor;
+ maModel.maLeft.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maLeft.maColor;
+ maModel.maRight.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maRight.maColor;
+ maModel.maDiagonal.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maDiagonal.maColor;
+}
+
+void Border::importDxfBorder( sal_Int32 nElement, SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Border::importDxfBorder - missing conditional formatting flag" );
+ if( BorderLineModel* pBorderLine = getBorderLine( nElement ) )
+ {
+ sal_uInt16 nStyle;
+ rStrm >> pBorderLine->maColor;
+ nStyle = rStrm.readuInt16();
+ pBorderLine->setBiffStyle( nStyle );
+ pBorderLine->mbUsed = true;
+ }
+}
+
+void Border::finalizeImport( bool bRTL )
+{
+ if ( bRTL )
+ {
+ BorderLineModel aTmp = maModel.maLeft;
+ maModel.maLeft = maModel.maRight;
+ maModel.maRight = aTmp;
+ }
+ maApiData.mbBorderUsed = maModel.maLeft.mbUsed || maModel.maRight.mbUsed || maModel.maTop.mbUsed || maModel.maBottom.mbUsed;
+ maApiData.mbDiagUsed = maModel.maDiagonal.mbUsed;
+
+ convertBorderLine( maApiData.maLeft, maModel.maLeft );
+ convertBorderLine( maApiData.maRight, maModel.maRight );
+ convertBorderLine( maApiData.maTop, maModel.maTop );
+ convertBorderLine( maApiData.maBottom, maModel.maBottom );
+
+ maApiData.maComplexColorLeft = maModel.maLeft.maColor.createComplexColor(getBaseFilter().getGraphicHelper(), -1);
+ maApiData.maComplexColorRight = maModel.maRight.maColor.createComplexColor(getBaseFilter().getGraphicHelper(), -1);
+ maApiData.maComplexColorTop = maModel.maTop.maColor.createComplexColor(getBaseFilter().getGraphicHelper(), -1);
+ maApiData.maComplexColorBottom = maModel.maBottom.maColor.createComplexColor(getBaseFilter().getGraphicHelper(), -1);
+
+ if( maModel.mbDiagTLtoBR )
+ convertBorderLine( maApiData.maTLtoBR, maModel.maDiagonal );
+ if( maModel.mbDiagBLtoTR )
+ convertBorderLine( maApiData.maBLtoTR, maModel.maDiagonal );
+}
+
+void Border::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ if( maApiData.mbBorderUsed )
+ {
+ SvxBoxItem aBoxItem( ATTR_BORDER );
+ editeng::SvxBorderLine aLine;
+
+ if (SvxBoxItem::LineToSvxLine(maApiData.maLeft, aLine, false))
+ {
+ aLine.setComplexColor(maApiData.maComplexColorLeft);
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maRight, aLine, false))
+ {
+ aLine.setComplexColor(maApiData.maComplexColorRight);
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maTop, aLine, false))
+ {
+ aLine.setComplexColor(maApiData.maComplexColorTop);
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::TOP );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maBottom, aLine, false))
+ {
+ aLine.setComplexColor(maApiData.maComplexColorBottom);
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ }
+ ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs );
+ }
+ if ( !maApiData.mbDiagUsed )
+ return;
+
+ SvxLineItem aTLBRItem( ATTR_BORDER_TLBR );
+ SvxLineItem aBLTRItem( ATTR_BORDER_BLTR );
+ ::editeng::SvxBorderLine aLine;
+ if (SvxBoxItem::LineToSvxLine(maApiData.maTLtoBR, aLine, false))
+ {
+ aTLBRItem.SetLine( &aLine );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maBLtoTR, aLine, false))
+ {
+ aBLTRItem.SetLine( &aLine );
+ }
+ ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs );
+}
+
+BorderLineModel* Border::getBorderLine( sal_Int32 nElement )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( left ): return &maModel.maLeft;
+ case XLS_TOKEN( start ): return &maModel.maLeft;
+ case XLS_TOKEN( right ): return &maModel.maRight;
+ case XLS_TOKEN( end ): return &maModel.maRight;
+ case XLS_TOKEN( top ): return &maModel.maTop;
+ case XLS_TOKEN( bottom ): return &maModel.maBottom;
+ case XLS_TOKEN( diagonal ): return &maModel.maDiagonal;
+ }
+ return nullptr;
+}
+
+bool Border::convertBorderLine( BorderLine2& rBorderLine, const BorderLineModel& rModel )
+{
+ // Document: sc/qa/unit/data/README.cellborders
+
+ rBorderLine.Color = sal_Int32(rModel.maColor.getColor( getBaseFilter().getGraphicHelper(), API_RGB_BLACK ));
+ switch( rModel.mnStyle )
+ {
+ case XML_dashDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT;
+ break;
+ case XML_dashDotDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT_DOT;
+ break;
+ case XML_dashed:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::FINE_DASHED;
+ break;
+ case XML_dotted:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::DOTTED;
+ break;
+ case XML_double:
+ lclSetBorderLineWidth( rBorderLine, 10, 15, 10 );
+ rBorderLine.LineStyle = BorderLineStyle::DOUBLE_THIN;
+ break;
+ case XML_hair: lclSetBorderLineWidth( rBorderLine, API_LINE_HAIR ); break;
+ case XML_medium: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break;
+ case XML_mediumDashDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT;
+ break;
+ case XML_mediumDashDotDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT_DOT;
+ break;
+ case XML_mediumDashed:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::DASHED;
+ break;
+ case XML_none: lclSetBorderLineWidth( rBorderLine, API_LINE_NONE ); break;
+ case XML_slantDashDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::FINE_DASHED;
+ break;
+ case XML_thick: lclSetBorderLineWidth( rBorderLine, API_LINE_THICK ); break;
+ case XML_thin: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break;
+ default: lclSetBorderLineWidth( rBorderLine, API_LINE_NONE ); break;
+ }
+ return rModel.mbUsed;
+}
+
+PatternFillModel::PatternFillModel( bool bDxf ) :
+ mnPattern( XML_none ),
+ mbPattColorUsed( !bDxf ),
+ mbFillColorUsed( !bDxf ),
+ mbPatternUsed( !bDxf )
+{
+ maPatternColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+ maFilterPatternColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+ maFillColor.setIndexed( OOX_COLOR_WINDOWBACK );
+}
+
+void PatternFillModel::setBiffPattern( sal_Int32 nPattern )
+{
+ static const sal_Int32 spnPatternIds[] = {
+ XML_none, XML_solid, XML_mediumGray, XML_darkGray,
+ XML_lightGray, XML_darkHorizontal, XML_darkVertical, XML_darkDown,
+ XML_darkUp, XML_darkGrid, XML_darkTrellis, XML_lightHorizontal,
+ XML_lightVertical, XML_lightDown, XML_lightUp, XML_lightGrid,
+ XML_lightTrellis, XML_gray125, XML_gray0625 };
+ mnPattern = STATIC_ARRAY_SELECT( spnPatternIds, nPattern, XML_none );
+}
+
+GradientFillModel::GradientFillModel() :
+ mnType( XML_linear ),
+ mfAngle( 0.0 ),
+ mfLeft( 0.0 ),
+ mfRight( 0.0 ),
+ mfTop( 0.0 ),
+ mfBottom( 0.0 )
+{
+}
+
+void GradientFillModel::readGradient( SequenceInputStream& rStrm )
+{
+ sal_Int32 nType;
+ nType = rStrm.readInt32();
+ mfAngle = rStrm.readDouble();
+ mfLeft = rStrm.readDouble();
+ mfRight = rStrm.readDouble();
+ mfTop = rStrm.readDouble();
+ mfBottom = rStrm.readDouble();
+ static const sal_Int32 spnTypes[] = { XML_linear, XML_path };
+ mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID );
+}
+
+void GradientFillModel::readGradientStop( SequenceInputStream& rStrm, bool bDxf )
+{
+ XlsColor aColor;
+ double fPosition;
+ if( bDxf )
+ {
+ rStrm.skip( 2 );
+ fPosition = rStrm.readDouble();
+ rStrm >> aColor;
+ }
+ else
+ {
+ rStrm >> aColor;
+ fPosition = rStrm.readDouble();
+ }
+ if( !rStrm.isEof() && (fPosition >= 0.0) )
+ maColors[ fPosition ] = aColor;
+}
+
+ApiSolidFillData::ApiSolidFillData() :
+ mnColor( API_RGB_TRANSPARENT ),
+ mnFilterColor( API_RGB_TRANSPARENT ),
+ mbTransparent( true ),
+ mbUsed( false )
+{
+}
+
+namespace {
+
+sal_Int32 lclGetMixedColorComp( sal_Int32 nPatt, sal_Int32 nFill, sal_Int32 nAlpha )
+{
+ return ((nPatt - nFill) * nAlpha) / 0x80 + nFill;
+}
+
+::Color lclGetMixedColor( ::Color nPattColor, ::Color nFillColor, sal_Int32 nAlpha )
+{
+ return ::Color(
+ lclGetMixedColorComp( nPattColor.GetRed(), nFillColor.GetRed(), nAlpha ),
+ lclGetMixedColorComp( nPattColor.GetGreen(), nFillColor.GetGreen(), nAlpha ),
+ lclGetMixedColorComp( nPattColor.GetBlue(), nFillColor.GetBlue(), nAlpha ) );
+}
+
+} // namespace
+
+Fill::Fill( const WorkbookHelper& rHelper, bool bDxf ) :
+ WorkbookHelper( rHelper ),
+ mbDxf( bDxf )
+{
+}
+
+void Fill::importPatternFill( const AttributeList& rAttribs )
+{
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->mnPattern = rAttribs.getToken( XML_patternType, XML_none );
+ if( mbDxf )
+ mxPatternModel->mbPatternUsed = rAttribs.hasAttribute( XML_patternType );
+}
+
+void Fill::importFgColor( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( mxPatternModel, "Fill::importFgColor - missing pattern data" );
+ if( mxPatternModel )
+ {
+ mxPatternModel->maPatternColor.importColor(rAttribs);
+ mxPatternModel->mbPattColorUsed = true;
+ }
+}
+
+void Fill::importBgColor( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( mxPatternModel, "Fill::importBgColor - missing pattern data" );
+ if( mxPatternModel )
+ {
+ mxPatternModel->maFillColor.importColor(rAttribs);
+ mxPatternModel->mbFillColorUsed = true;
+ }
+}
+
+void Fill::importGradientFill( const AttributeList& rAttribs )
+{
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ mxGradientModel->mnType = rAttribs.getToken( XML_type, XML_linear );
+ mxGradientModel->mfAngle = rAttribs.getDouble( XML_degree, 0.0 );
+ mxGradientModel->mfLeft = rAttribs.getDouble( XML_left, 0.0 );
+ mxGradientModel->mfRight = rAttribs.getDouble( XML_right, 0.0 );
+ mxGradientModel->mfTop = rAttribs.getDouble( XML_top, 0.0 );
+ mxGradientModel->mfBottom = rAttribs.getDouble( XML_bottom, 0.0 );
+}
+
+void Fill::importColor( const AttributeList& rAttribs, double fPosition )
+{
+ OSL_ENSURE( mxGradientModel, "Fill::importColor - missing gradient data" );
+ if( mxGradientModel && (fPosition >= 0.0) )
+ mxGradientModel->maColors[ fPosition ].importColor( rAttribs );
+}
+
+void Fill::importFill( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( mbDxf, "sc", "Fill::importFill - unexpected conditional formatting flag" );
+ sal_Int32 nPattern = rStrm.readInt32();
+ if( nPattern == BIFF12_FILL_GRADIENT )
+ {
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ sal_Int32 nStopCount;
+ rStrm.skip( 16 );
+ mxGradientModel->readGradient( rStrm );
+ nStopCount = rStrm.readInt32();
+ for( sal_Int32 nStop = 0; (nStop < nStopCount) && !rStrm.isEof(); ++nStop )
+ mxGradientModel->readGradientStop( rStrm, false );
+ }
+ else
+ {
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->setBiffPattern( nPattern );
+ rStrm >> mxPatternModel->maPatternColor >> mxPatternModel->maFillColor;
+ }
+}
+
+void Fill::importDxfPattern( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfPattern - missing conditional formatting flag" );
+ if( !mxPatternModel )
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->setBiffPattern( rStrm.readuInt8() );
+ mxPatternModel->mbPatternUsed = true;
+}
+
+void Fill::importDxfFgColor( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfFgColor - missing conditional formatting flag" );
+ if( !mxPatternModel )
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->maPatternColor.importColor( rStrm );
+ mxPatternModel->mbPattColorUsed = true;
+}
+
+void Fill::importDxfBgColor( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfBgColor - missing conditional formatting flag" );
+ if( !mxPatternModel )
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->maFillColor.importColor( rStrm );
+ mxPatternModel->mbFillColorUsed = true;
+}
+
+void Fill::importDxfGradient( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfGradient - missing conditional formatting flag" );
+ if( !mxGradientModel )
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ mxGradientModel->readGradient( rStrm );
+}
+
+void Fill::importDxfStop( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfStop - missing conditional formatting flag" );
+ if( !mxGradientModel )
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ mxGradientModel->readGradientStop( rStrm, true );
+}
+
+void Fill::finalizeImport()
+{
+ const GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+
+ if( mxPatternModel )
+ {
+ // finalize the OOXML data struct
+ PatternFillModel& rModel = *mxPatternModel;
+ if( mbDxf )
+ {
+ if( rModel.mbFillColorUsed && (!rModel.mbPatternUsed || (rModel.mnPattern == XML_solid)) )
+ {
+ rModel.maFilterPatternColor = rModel.maPatternColor;
+ rModel.maPatternColor = rModel.maFillColor;
+ rModel.mnPattern = XML_solid;
+ rModel.mbPattColorUsed = rModel.mbPatternUsed = true;
+ }
+ else if(
+ !rModel.mbFillColorUsed && !rModel.mbPattColorUsed &&
+ rModel.mbPatternUsed && rModel.mnPattern == XML_solid )
+ {
+ rModel.mbPatternUsed = false;
+ }
+ else
+ rModel.maFilterPatternColor = rModel.maPatternColor;
+ }
+
+ // convert to API fill settings
+ maApiData.mbUsed = rModel.mbPatternUsed;
+ if( rModel.mnPattern == XML_none )
+ {
+ maApiData.mnColor = API_RGB_TRANSPARENT;
+ maApiData.mbTransparent = true;
+ }
+ else
+ {
+ sal_Int32 nAlpha = 0x80;
+ switch( rModel.mnPattern )
+ {
+ case XML_darkDown: nAlpha = 0x40; break;
+ case XML_darkGray: nAlpha = 0x60; break;
+ case XML_darkGrid: nAlpha = 0x40; break;
+ case XML_darkHorizontal: nAlpha = 0x40; break;
+ case XML_darkTrellis: nAlpha = 0x60; break;
+ case XML_darkUp: nAlpha = 0x40; break;
+ case XML_darkVertical: nAlpha = 0x40; break;
+ case XML_gray0625: nAlpha = 0x08; break;
+ case XML_gray125: nAlpha = 0x10; break;
+ case XML_lightDown: nAlpha = 0x20; break;
+ case XML_lightGray: nAlpha = 0x20; break;
+ case XML_lightGrid: nAlpha = 0x38; break;
+ case XML_lightHorizontal: nAlpha = 0x20; break;
+ case XML_lightTrellis: nAlpha = 0x30; break;
+ case XML_lightUp: nAlpha = 0x20; break;
+ case XML_lightVertical: nAlpha = 0x20; break;
+ case XML_mediumGray: nAlpha = 0x40; break;
+ case XML_solid: nAlpha = 0x80; break;
+ }
+
+ ::Color nWinTextColor = rGraphicHelper.getSystemColor( XML_windowText );
+ ::Color nWinColor = rGraphicHelper.getSystemColor( XML_window );
+
+ if (!rModel.mbPattColorUsed)
+ {
+ rModel.maPatternColor.setAuto();
+ rModel.maFilterPatternColor.setAuto();
+ }
+ ::Color nPattColor = rModel.maPatternColor.getColor( rGraphicHelper, nWinTextColor );
+ ::Color nFiltPattColor = rModel.maFilterPatternColor.getColor( rGraphicHelper, nWinTextColor );
+
+ if( !rModel.mbFillColorUsed )
+ rModel.maFillColor.setAuto();
+ ::Color nFillColor = rModel.maFillColor.getColor( rGraphicHelper, nWinColor );
+
+ maApiData.mnColor = lclGetMixedColor( nPattColor, nFillColor, nAlpha );
+ maApiData.maComplexColor = rModel.maPatternColor.createComplexColor(rGraphicHelper, -1);
+ maApiData.mnFilterColor = lclGetMixedColor( nFiltPattColor, nFillColor, nAlpha );
+ maApiData.mbTransparent = false;
+ }
+ }
+ else if( mxGradientModel && !mxGradientModel->maColors.empty() )
+ {
+ GradientFillModel& rModel = *mxGradientModel;
+ maApiData.mbUsed = true; // no support for differential attributes
+ GradientFillModel::ColorMap::const_iterator aIt = rModel.maColors.begin();
+ OSL_ENSURE( !aIt->second.isAuto(), "Fill::finalizeImport - automatic gradient color" );
+ maApiData.mnColor = aIt->second.getColor( rGraphicHelper, API_RGB_WHITE );
+ if( ++aIt != rModel.maColors.end() )
+ {
+ OSL_ENSURE( !aIt->second.isAuto(), "Fill::finalizeImport - automatic gradient color" );
+ ::Color nEndColor = aIt->second.getColor( rGraphicHelper, API_RGB_WHITE );
+ maApiData.mnColor = lclGetMixedColor( maApiData.mnColor, nEndColor, 0x40 );
+ maApiData.mbTransparent = false;
+ }
+ }
+}
+
+void Fill::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ if( !maApiData.mbUsed )
+ return;
+
+ SvxBrushItem aBrushItem( ATTR_BACKGROUND );
+ if ( maApiData.mbTransparent )
+ {
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ aBrushItem.SetFiltColor( COL_TRANSPARENT );
+ }
+ else
+ {
+ aBrushItem.SetColor( maApiData.mnColor );
+ aBrushItem.setComplexColor(maApiData.maComplexColor);
+ aBrushItem.SetFiltColor( maApiData.mnFilterColor );
+ }
+ ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs );
+}
+
+XfModel::XfModel() :
+ mnStyleXfId( -1 ),
+ mnFontId( -1 ),
+ mnNumFmtId( -1 ),
+ mnBorderId( -1 ),
+ mnFillId( -1 ),
+ mbCellXf( true ),
+ mbFontUsed( false ),
+ mbNumFmtUsed( false ),
+ mbAlignUsed( false ),
+ mbProtUsed( false ),
+ mbBorderUsed( false ),
+ mbAreaUsed( false )
+{
+}
+
+Xf::AttrList::AttrList(const ScPatternAttr* pDefPattern):
+ mbLatinNumFmtOnly(true),
+ mpDefPattern(pDefPattern)
+{}
+
+Xf::Xf( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnScNumFmt(0),
+ maAlignment( rHelper ),
+ maProtection( rHelper ),
+ meRotationRef( css::table::CellVertJustify2::STANDARD ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void Xf::importXf( const AttributeList& rAttribs, bool bCellXf )
+{
+ maModel.mbCellXf = bCellXf;
+ // tdf#70565 Set proper default value to "0" of xfId attribute
+ // When xfId is not exist during .xlsx import
+ // it must have values set to "0".
+ // This doesn't impact spreadsheets created with MS Excel,
+ // as xfId attribute is always created during export to .xlsx
+ // Not setting "0" value is causing wrong .xlsx import by LibreOffice,
+ // for spreadsheets created by external applications (ex. SAP BI).
+ bool bApplyDefault;
+ if ( maModel.mbCellXf )
+ {
+ const sal_Int32 xfId = rAttribs.getInteger( XML_xfId, -1 );
+ // No xfId => no cellStyleXfs that could overwrite this on change, thus
+ // has to be applied.
+ bApplyDefault = (xfId < 0);
+ maModel.mnStyleXfId = std::max<sal_Int32>(0, xfId);
+ }
+ else
+ {
+ maModel.mnStyleXfId = rAttribs.getInteger( XML_xfId, -1 );
+ bApplyDefault = true;
+ }
+ maModel.mnFontId = rAttribs.getInteger( XML_fontId, -1 );
+ maModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, -1 );
+ maModel.mnBorderId = rAttribs.getInteger( XML_borderId, -1 );
+ maModel.mnFillId = rAttribs.getInteger( XML_fillId, -1 );
+
+ // Default value of the apply*** attributes is dependent on context:
+ // true in cellStyleXfs element, false in cellXfs element...
+ // But it's not as easy as it sounds, for docs see
+ // https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/59922f8b-0edc-4e93-a822-9f22254aec46
+ // and apparently in reality cellStyleXfs xf and cellXfs xf are not merged
+ // at all, see
+ // https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/bcf98682-e8d3-44b8-b8f8-0bf696878ba1
+ // "b. The standard states that both the cell style xf records and cell xf
+ // records must be read to understand the full set of formatting applied to
+ // a cell."
+ // "In Office, only the cell xf record defines the formatting applied to a cell."
+
+ // So for reading documents this is all crap and effectively xf records
+ // apply their explicit properties by default unless denied.
+ // bApplyDefault==false only for cellXf xf with xfId.
+
+ // For cellXf xf, mbAlignUsed and mbProtUsed will be set when actually
+ // importing the element.
+ maModel.mbAlignUsed = rAttribs.getBool( XML_applyAlignment, bApplyDefault);
+ maModel.mbProtUsed = rAttribs.getBool( XML_applyProtection, bApplyDefault);
+
+ maModel.mbFontUsed = rAttribs.getBool( XML_applyFont, bApplyDefault || maModel.mnFontId > 0);
+ maModel.mbNumFmtUsed = rAttribs.getBool( XML_applyNumberFormat, bApplyDefault || maModel.mnNumFmtId > 0);
+ maModel.mbBorderUsed = rAttribs.getBool( XML_applyBorder, bApplyDefault || maModel.mnBorderId > 0);
+ maModel.mbAreaUsed = rAttribs.getBool( XML_applyFill, bApplyDefault || maModel.mnFillId > 0);
+}
+
+void Xf::importAlignment( const AttributeList& rAttribs )
+{
+ maAlignment.importAlignment( rAttribs );
+ if (maModel.mbCellXf)
+ maModel.mbAlignUsed = true;
+}
+
+void Xf::importProtection( const AttributeList& rAttribs )
+{
+ maProtection.importProtection( rAttribs );
+ if (maModel.mbCellXf)
+ maModel.mbProtUsed = true;
+}
+
+void Xf::importXf( SequenceInputStream& rStrm, bool bCellXf )
+{
+ maModel.mbCellXf = bCellXf;
+ maModel.mnStyleXfId = rStrm.readuInt16();
+ maModel.mnNumFmtId = rStrm.readuInt16();
+ maModel.mnFontId = rStrm.readuInt16();
+ maModel.mnFillId = rStrm.readuInt16();
+ maModel.mnBorderId = rStrm.readuInt16();
+ sal_uInt32 nFlags = rStrm.readuInt32();
+ maAlignment.setBiff12Data( nFlags );
+ maProtection.setBiff12Data( nFlags );
+ // used flags, see comments in Xf::setBiffUsedFlags()
+ sal_uInt16 nUsedFlags = rStrm.readuInt16();
+ maModel.mbFontUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_FONT_USED );
+ maModel.mbNumFmtUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_NUMFMT_USED );
+ maModel.mbAlignUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_ALIGN_USED );
+ maModel.mbProtUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_PROT_USED );
+ maModel.mbBorderUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_BORDER_USED );
+ maModel.mbAreaUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_AREA_USED );
+}
+
+void Xf::finalizeImport()
+{
+ // alignment and protection
+ maAlignment.finalizeImport();
+ maProtection.finalizeImport();
+}
+
+FontRef Xf::getFont() const
+{
+ return getStyles().getFont( maModel.mnFontId );
+}
+
+void Xf::applyPatternToAttrList( AttrList& rAttrs, SCROW nRow1, SCROW nRow2, sal_Int32 nXfId, sal_Int32 nNumFmtId, ScPatternCache& rCache )
+{
+ ScPatternAttr* pCachedPattern = rCache.query(nXfId, nNumFmtId);
+ if (!pCachedPattern)
+ createPattern();
+
+ ScPatternAttr& rPat = pCachedPattern ? *pCachedPattern : *mpPattern;
+ ScDocumentImport& rDocImport = getDocImport();
+ ScDocument& rDoc = getScDocument();
+ if ( !pCachedPattern && isCellXf() )
+ {
+ StylesBuffer& rStyles = getStyles();
+ rStyles.createCellStyle( maModel.mnStyleXfId );
+
+ mpStyleSheet = rStyles.getCellStyleSheet( maModel.mnStyleXfId );
+ if ( mpStyleSheet )
+ {
+ //rDoc.ApplySelectionStyle( static_cast<ScStyleSheet&>(*mpStyleSheet), rMarkData );
+ rPat.SetStyleSheet(mpStyleSheet, false);
+ }
+ else
+ {
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if (pStylePool)
+ {
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rPat.SetStyleSheet( pStyleSheet, false );
+ }
+ }
+ }
+ if ( !pCachedPattern && nNumFmtId >= 0 )
+ {
+ ScPatternAttr aNumPat(rDoc.GetPool());
+ mnScNumFmt = getStyles().writeNumFmtToItemSet( aNumPat.GetItemSet(), nNumFmtId, false );
+ rPat.GetItemSet().Put(aNumPat.GetItemSet());
+ }
+
+ if (!pCachedPattern && !rDocImport.isLatinScript(mnScNumFmt))
+ rAttrs.mbLatinNumFmtOnly = false;
+
+ if (!pCachedPattern && !rPat.GetStyleName())
+ return;
+
+
+ // Check for a gap between the last entry and this one.
+ bool bHasGap = false;
+ if (rAttrs.maAttrs.empty() && nRow1 > 0)
+ // First attribute range doesn't start at row 0.
+ bHasGap = true;
+
+ if (!rAttrs.maAttrs.empty() && rAttrs.maAttrs.back().nEndRow + 1 < nRow1)
+ bHasGap = true;
+
+ if (bHasGap)
+ {
+ // Fill this gap with the default pattern.
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow1 - 1;
+ aEntry.pPattern = &rDoc.GetPool()->DirectPutItemInPool(*rAttrs.mpDefPattern);
+ rAttrs.maAttrs.push_back(aEntry);
+
+ // Check if the default pattern is 'General'.
+ if (!rDocImport.isLatinScript(*aEntry.pPattern))
+ rAttrs.mbLatinNumFmtOnly = false;
+ }
+
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow2;
+ aEntry.pPattern = &rDoc.GetPool()->DirectPutItemInPool(rPat);
+ // Put the allocated pattern to cache
+ if (!pCachedPattern)
+ rCache.add(nXfId, nNumFmtId, const_cast<ScPatternAttr*>(aEntry.pPattern));
+
+ rAttrs.maAttrs.push_back(aEntry);
+
+ if (!rDocImport.isLatinScript(*aEntry.pPattern))
+ rAttrs.mbLatinNumFmtOnly = false;
+}
+
+void Xf::writeToDoc( ScDocumentImport& rDoc, const ScRange& rRange )
+{
+ const StylesBuffer& rStyles = getStyles();
+
+ if (isCellXf())
+ {
+ // Cell style name.
+ OUString aStyleName = rStyles.createCellStyle(maModel.mnStyleXfId);
+
+ ScStyleSheet* pStyleSheet =
+ static_cast<ScStyleSheet*>(
+ rDoc.getDoc().GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ {
+ rDoc.getDoc().ApplyStyleAreaTab(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab(),
+ *pStyleSheet);
+ }
+ }
+
+ const ScPatternAttr& rAttr = createPattern();
+ rDoc.getDoc().ApplyPatternAreaTab(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab(), rAttr);
+}
+
+const ::ScPatternAttr&
+Xf::createPattern( bool bSkipPoolDefs )
+{
+ if( mpPattern )
+ return *mpPattern;
+ mpPattern.reset( new ::ScPatternAttr( getScDocument().GetPool() ) );
+ SfxItemSet& rItemSet = mpPattern->GetItemSet();
+ /* Enables the used flags, if the formatting attributes differ from the
+ style XF. In cell XFs Excel uses the cell attributes, if they differ
+ from the parent style XF (even if the used flag is switched off).
+ #109899# ...or if the respective flag is not set in parent style XF.
+ */
+ StylesBuffer& rStyles = getStyles();
+
+ const Xf* pStyleXf = isCellXf() ? rStyles.getStyleXf( maModel.mnStyleXfId ).get() : nullptr;
+ if( pStyleXf && !mpStyleSheet )
+ {
+ rStyles.createCellStyle( maModel.mnStyleXfId );
+ mpStyleSheet = rStyles.getCellStyleSheet( maModel.mnStyleXfId );
+ OSL_ENSURE( mpStyleSheet, "Xf::createPattern - no parentStyle created" );
+
+ const XfModel& rStyleData = pStyleXf->maModel;
+ if( !maModel.mbFontUsed )
+ maModel.mbFontUsed = !rStyleData.mbFontUsed || (maModel.mnFontId != rStyleData.mnFontId);
+ if( !maModel.mbNumFmtUsed )
+ maModel.mbNumFmtUsed = !rStyleData.mbNumFmtUsed || (maModel.mnNumFmtId != rStyleData.mnNumFmtId);
+ if( !maModel.mbAlignUsed )
+ maModel.mbAlignUsed = !rStyleData.mbAlignUsed || !(maAlignment.getApiData() == pStyleXf->maAlignment.getApiData());
+ if( !maModel.mbProtUsed )
+ maModel.mbProtUsed = !rStyleData.mbProtUsed || !(maProtection.getApiData() == pStyleXf->maProtection.getApiData());
+ if( !maModel.mbBorderUsed )
+ maModel.mbBorderUsed = !rStyleData.mbBorderUsed || !StylesBuffer::equalBorders( maModel.mnBorderId, rStyleData.mnBorderId );
+ if( !maModel.mbAreaUsed )
+ maModel.mbAreaUsed = !rStyleData.mbAreaUsed || !StylesBuffer::equalFills( maModel.mnFillId, rStyleData.mnFillId );
+ }
+ // cell protection
+ if( maModel.mbProtUsed )
+ {
+ maProtection.fillToItemSet( rItemSet, bSkipPoolDefs );
+ }
+
+ // font
+ if( maModel.mbFontUsed )
+ {
+ rStyles.writeFontToItemSet( rItemSet, maModel.mnFontId, bSkipPoolDefs );
+ }
+
+ // value format
+ if( maModel.mbNumFmtUsed )
+ {
+ mnScNumFmt = rStyles.writeNumFmtToItemSet( rItemSet, maModel.mnNumFmtId, bSkipPoolDefs );
+ }
+ // alignment
+ if( maModel.mbAlignUsed )
+ {
+ maAlignment.fillToItemSet( rItemSet, bSkipPoolDefs );
+ }
+
+ // border
+ if( maModel.mbBorderUsed )
+ {
+ rStyles.writeBorderToItemSet( rItemSet, maModel.mnBorderId, bSkipPoolDefs );
+ }
+
+ // area
+ if( maModel.mbAreaUsed )
+ {
+ rStyles.writeFillToItemSet( rItemSet, maModel.mnFillId, bSkipPoolDefs );
+ }
+
+ /* #i38709# Decide which rotation reference mode to use. If any outer
+ border line of the cell is set (either explicitly or via cell style),
+ and the cell contents are rotated, set rotation reference to bottom of
+ cell. This causes the borders to be painted rotated with the text. */
+ if( const Alignment* pAlignment = maModel.mbAlignUsed ? &maAlignment : (pStyleXf ? &pStyleXf->maAlignment : nullptr) )
+ {
+ SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD;
+ sal_Int32 nBorderId = maModel.mbBorderUsed ? maModel.mnBorderId : (pStyleXf ? pStyleXf->maModel.mnBorderId : -1);
+ if( const Border* pBorder = rStyles.getBorder( nBorderId ).get() )
+ {
+ if( (pAlignment->getApiData().mnRotation) && pBorder->getApiData().hasAnyOuterBorder() )
+ {
+ meRotationRef = css::table::CellVertJustify2::BOTTOM;
+ eRotateMode = SVX_ROTATE_MODE_BOTTOM;
+ }
+ }
+ ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs );
+ }
+
+ return *mpPattern;
+}
+
+Dxf::Dxf( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+FontRef const & Dxf::createFont( bool bAlwaysNew )
+{
+ if( bAlwaysNew || !mxFont )
+ mxFont = std::make_shared<Font>( *this, true );
+ return mxFont;
+}
+
+BorderRef const & Dxf::createBorder( bool bAlwaysNew )
+{
+ if( bAlwaysNew || !mxBorder )
+ mxBorder = std::make_shared<Border>( *this, true );
+ return mxBorder;
+}
+
+FillRef const & Dxf::createFill( bool bAlwaysNew )
+{
+ if( bAlwaysNew || !mxFill )
+ mxFill = std::make_shared<Fill>( *this, true );
+ return mxFill;
+}
+
+void Dxf::importNumFmt( const AttributeList& rAttribs )
+{
+ // don't propagate number formats defined in Dxf entries
+ // they can have the same id ( but different format codes ) as those
+ // defined globally earlier. We discard the id defined in XML_numFmtId
+ // and generate one ourselves ( this assumes that the normal numberformat
+ // import has already taken place )
+ sal_Int32 nNumFmtId = getStyles().nextFreeNumFmtId();
+ OUString aFmtCode = rAttribs.getXString( XML_formatCode, OUString() );
+ mxNumFmt = getStyles().createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void Dxf::importDxf( SequenceInputStream& rStrm )
+{
+ sal_Int32 nNumFmtId = -1;
+ OUString aFmtCode;
+ sal_uInt16 nRecCount;
+ rStrm.skip( 4 ); // flags
+ nRecCount = rStrm.readuInt16();
+ for( sal_uInt16 nRec = 0; !rStrm.isEof() && (nRec < nRecCount); ++nRec )
+ {
+ sal_uInt16 nSubRecId, nSubRecSize;
+ sal_Int64 nRecEnd = rStrm.tell();
+ nSubRecId = rStrm.readuInt16();
+ nSubRecSize = rStrm.readuInt16();
+ nRecEnd += nSubRecSize;
+ switch( nSubRecId )
+ {
+ case BIFF12_DXF_FILL_PATTERN: createFill( false )->importDxfPattern( rStrm ); break;
+ case BIFF12_DXF_FILL_FGCOLOR: createFill( false )->importDxfFgColor( rStrm ); break;
+ case BIFF12_DXF_FILL_BGCOLOR: createFill( false )->importDxfBgColor( rStrm ); break;
+ case BIFF12_DXF_FILL_GRADIENT: createFill( false )->importDxfGradient( rStrm ); break;
+ case BIFF12_DXF_FILL_STOP: createFill( false )->importDxfStop( rStrm ); break;
+ case BIFF12_DXF_FONT_COLOR: createFont( false )->importDxfColor( rStrm ); break;
+ case BIFF12_DXF_BORDER_TOP: createBorder( false )->importDxfBorder( XLS_TOKEN( top ), rStrm ); break;
+ case BIFF12_DXF_BORDER_BOTTOM: createBorder( false )->importDxfBorder( XLS_TOKEN( bottom ), rStrm ); break;
+ case BIFF12_DXF_BORDER_LEFT: createBorder( false )->importDxfBorder( XLS_TOKEN( left ), rStrm ); break;
+ case BIFF12_DXF_BORDER_RIGHT: createBorder( false )->importDxfBorder( XLS_TOKEN( right ), rStrm ); break;
+ case BIFF12_DXF_FONT_NAME: createFont( false )->importDxfName( rStrm ); break;
+ case BIFF12_DXF_FONT_WEIGHT: createFont( false )->importDxfWeight( rStrm ); break;
+ case BIFF12_DXF_FONT_UNDERLINE: createFont( false )->importDxfUnderline( rStrm ); break;
+ case BIFF12_DXF_FONT_ESCAPEMENT: createFont( false )->importDxfEscapement( rStrm ); break;
+ case BIFF12_DXF_FONT_ITALIC: createFont( false )->importDxfFlag( XML_i, rStrm ); break;
+ case BIFF12_DXF_FONT_STRIKE: createFont( false )->importDxfFlag( XML_strike, rStrm ); break;
+ case BIFF12_DXF_FONT_OUTLINE: createFont( false )->importDxfFlag( XML_outline, rStrm ); break;
+ case BIFF12_DXF_FONT_SHADOW: createFont( false )->importDxfFlag( XML_shadow, rStrm ); break;
+ case BIFF12_DXF_FONT_HEIGHT: createFont( false )->importDxfHeight( rStrm ); break;
+ case BIFF12_DXF_FONT_SCHEME: createFont( false )->importDxfScheme( rStrm ); break;
+ case BIFF12_DXF_NUMFMT_CODE: aFmtCode = BiffHelper::readString( rStrm, false ); break;
+ case BIFF12_DXF_NUMFMT_ID: nNumFmtId = rStrm.readuInt16(); break;
+ }
+ rStrm.seek( nRecEnd );
+ }
+ OSL_ENSURE( !rStrm.isEof() && (rStrm.getRemaining() == 0), "Dxf::importDxf - unexpected remaining data" );
+ mxNumFmt = getStyles().createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void Dxf::finalizeImport()
+{
+ if( mxFont )
+ mxFont->finalizeImport();
+ bool bRTL = false;
+ // number format already finalized by the number formats buffer
+ if( mxAlignment )
+ {
+ mxAlignment->finalizeImport();
+ // how do we detect RTL when text dir is OOX_XF_CONTEXT? ( seems you
+ // would need access to the cell content, which we don't here )
+ if ( mxAlignment->getModel().mnTextDir == OOX_XF_TEXTDIR_RTL )
+ bRTL = true;
+ }
+ if( mxProtection )
+ mxProtection->finalizeImport();
+ if( mxBorder )
+ {
+ mxBorder->finalizeImport( bRTL );
+ }
+ if( mxFill )
+ mxFill->finalizeImport();
+}
+
+void Dxf::fillToItemSet( SfxItemSet& rSet ) const
+{
+ if (mxFont)
+ mxFont->fillToItemSet(rSet, false);
+ if (mxNumFmt)
+ mxNumFmt->fillToItemSet(rSet);
+ if (mxAlignment)
+ mxAlignment->fillToItemSet(rSet);
+ if (mxProtection)
+ mxProtection->fillToItemSet(rSet);
+ if (mxBorder)
+ mxBorder->fillToItemSet(rSet);
+ if (mxFill)
+ mxFill->fillToItemSet(rSet);
+}
+
+namespace {
+
+const char* const sppcStyleNames[] =
+{
+ "Normal",
+ "RowLevel_", // outline level will be appended
+ "ColLevel_", // outline level will be appended
+ "Comma",
+ "Currency",
+ "Percent",
+ "Comma [0]", // new in BIFF4
+ "Currency [0]",
+ "Hyperlink", // new in BIFF8
+ "Followed Hyperlink",
+ "Note", // new in OOX
+ "Warning Text",
+ nullptr,
+ nullptr,
+ nullptr,
+ "Title",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3",
+ "Heading 4",
+ "Input",
+ "Output",
+ "Calculation",
+ "Check Cell",
+ "Linked Cell",
+ "Total",
+ "Good",
+ "Bad",
+ "Neutral",
+ "Accent1",
+ "20% - Accent1",
+ "40% - Accent1",
+ "60% - Accent1",
+ "Accent2",
+ "20% - Accent2",
+ "40% - Accent2",
+ "60% - Accent2",
+ "Accent3",
+ "20% - Accent3",
+ "40% - Accent3",
+ "60% - Accent3",
+ "Accent4",
+ "20% - Accent4",
+ "40% - Accent4",
+ "60% - Accent4",
+ "Accent5",
+ "20% - Accent5",
+ "40% - Accent5",
+ "60% - Accent5",
+ "Accent6",
+ "20% - Accent6",
+ "40% - Accent6",
+ "60% - Accent6",
+ "Explanatory Text"
+};
+const sal_Int32 snStyleNamesCount = static_cast< sal_Int32 >( SAL_N_ELEMENTS( sppcStyleNames ) );
+
+OUString lclGetBuiltinStyleName( sal_Int32 nBuiltinId, std::u16string_view rName, sal_Int32 nLevel = 0 )
+{
+ OSL_ENSURE( (0 <= nBuiltinId) && (nBuiltinId < snStyleNamesCount), "lclGetBuiltinStyleName - unknown built-in style" );
+ OUStringBuffer aStyleName("Excel Built-in ");
+ if( (0 <= nBuiltinId) && (nBuiltinId < snStyleNamesCount) && (sppcStyleNames[ nBuiltinId ] != nullptr) )
+ aStyleName.appendAscii( sppcStyleNames[ nBuiltinId ] );
+ else if( !rName.empty() )
+ aStyleName.append( rName );
+ else
+ aStyleName.append( nBuiltinId );
+ if( (nBuiltinId == OOX_STYLE_ROWLEVEL) || (nBuiltinId == OOX_STYLE_COLLEVEL) )
+ aStyleName.append( nLevel );
+ return aStyleName.makeStringAndClear();
+}
+
+OUString lclCreateStyleName( const CellStyleModel& rModel )
+{
+ return rModel.mbBuiltin ? lclGetBuiltinStyleName( rModel.mnBuiltinId, rModel.maName, rModel.mnLevel ) : rModel.maName;
+}
+
+} // namespace
+
+CellStyleModel::CellStyleModel() :
+ mnXfId( -1 ),
+ mnBuiltinId( -1 ),
+ mnLevel( 0 ),
+ mbBuiltin( false ),
+ mbCustom( false ),
+ mbHidden( false )
+{
+}
+
+bool CellStyleModel::isBuiltin() const
+{
+ return mbBuiltin && (mnBuiltinId >= 0);
+}
+
+bool CellStyleModel::isDefaultStyle() const
+{
+ return mbBuiltin && (mnBuiltinId == OOX_STYLE_NORMAL);
+}
+
+CellStyle::CellStyle( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbCreated( false ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void CellStyle::importCellStyle( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.mnXfId = rAttribs.getInteger( XML_xfId, -1 );
+ maModel.mnBuiltinId = rAttribs.getInteger( XML_builtinId, -1 );
+ maModel.mnLevel = rAttribs.getInteger( XML_iLevel, 0 );
+ maModel.mbBuiltin = rAttribs.hasAttribute( XML_builtinId );
+ maModel.mbCustom = rAttribs.getBool( XML_customBuiltin, false );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+}
+
+void CellStyle::importCellStyle( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ maModel.mnXfId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ maModel.mnBuiltinId = rStrm.readInt8();
+ maModel.mnLevel = rStrm.readInt8();
+ rStrm >> maModel.maName;
+ maModel.mbBuiltin = getFlag( nFlags, BIFF12_CELLSTYLE_BUILTIN );
+ maModel.mbCustom = getFlag( nFlags, BIFF12_CELLSTYLE_CUSTOM );
+ maModel.mbHidden = getFlag( nFlags, BIFF12_CELLSTYLE_HIDDEN );
+}
+
+void CellStyle::createCellStyle()
+{
+
+ // #i1624# #i1768# ignore unnamed user styles
+ bool bDefStyle = maModel.isDefaultStyle();
+ if( !mbCreated )
+ {
+ if ( bDefStyle && maFinalName.isEmpty() )
+ maFinalName = ScResId( STR_STYLENAME_STANDARD );
+ mbCreated = maFinalName.isEmpty();
+ }
+
+ if( mbCreated || mpStyleSheet )
+ return;
+
+ bool bCreatePattern = false;
+ Xf* pXF = getStyles().getStyleXf( maModel.mnXfId ).get();
+ ::ScDocument& rDoc = getScDocument();
+
+ if( bDefStyle )
+ {
+ // use existing "Default" style sheet
+ mpStyleSheet = static_cast< ScStyleSheet* >( rDoc.GetStyleSheetPool()->Find(
+ ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) );
+ OSL_ENSURE( mpStyleSheet, "CellStyle::createStyle - Default style not found" );
+ bCreatePattern = true;
+ }
+ else
+ {
+ mpStyleSheet = static_cast< ScStyleSheet* >( rDoc.GetStyleSheetPool()->Find( maFinalName, SfxStyleFamily::Para ) );
+ if( !mpStyleSheet )
+ {
+ mpStyleSheet = &static_cast< ScStyleSheet& >( rDoc.GetStyleSheetPool()->Make( maFinalName, SfxStyleFamily::Para, SfxStyleSearchBits::UserDefined ) );
+ bCreatePattern = true;
+ }
+ }
+
+ // bDefStyle==true omits default pool items in CreatePattern()
+ if( bCreatePattern && mpStyleSheet && pXF )
+ mpStyleSheet->GetItemSet().Put( pXF->createPattern( bDefStyle ).GetItemSet() );
+}
+
+void CellStyle::finalizeImport( const OUString& rFinalName )
+{
+ maFinalName = rFinalName;
+ if( !maModel.isBuiltin() || maModel.mbCustom )
+ createCellStyle();
+}
+
+CellStyleBuffer::CellStyleBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+CellStyleRef CellStyleBuffer::importCellStyle( const AttributeList& rAttribs )
+{
+ CellStyleRef xCellStyle = std::make_shared<CellStyle>( *this );
+ xCellStyle->importCellStyle( rAttribs );
+ insertCellStyle( xCellStyle );
+ return xCellStyle;
+}
+
+CellStyleRef CellStyleBuffer::importCellStyle( SequenceInputStream& rStrm )
+{
+ CellStyleRef xCellStyle = std::make_shared<CellStyle>( *this );
+ xCellStyle->importCellStyle( rStrm );
+ insertCellStyle( xCellStyle );
+ return xCellStyle;
+}
+
+void CellStyleBuffer::finalizeImport()
+{
+ // calculate final names of all styles
+ typedef RefMap< OUString, CellStyle, IgnoreCaseCompare > CellStyleNameMap;
+ CellStyleNameMap aCellStyles;
+ CellStyleVector aConflictNameStyles;
+
+ /* First, reserve style names that are built-in in Calc. This causes that
+ imported cell styles get different unused names and thus do not try to
+ overwrite these built-in styles. */
+ try
+ {
+ // unfortunately, com.sun.star.style.StyleFamily does not implement XEnumerationAccess...
+ Reference< XIndexAccess > xStyleFamilyIA( getCellStyleFamily(), UNO_QUERY_THROW );
+ for( sal_Int32 nIndex = 0, nCount = xStyleFamilyIA->getCount(); nIndex < nCount; ++nIndex )
+ {
+ Reference< XStyle > xStyle( xStyleFamilyIA->getByIndex( nIndex ), UNO_QUERY_THROW );
+ if( !xStyle->isUserDefined() )
+ {
+ // create an empty entry by using ::std::map<>::operator[]
+ aCellStyles[ xStyle->getName() ];
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ /* Calculate names of built-in styles. Store styles with reserved names
+ in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maBuiltinStyles )
+ {
+ const CellStyleModel& rModel = rxStyle->getModel();
+ if (rModel.isDefaultStyle())
+ continue;
+
+ OUString aStyleName = lclCreateStyleName( rModel );
+ /* If a builtin style entry already exists,
+ we just stick with the last definition and ignore
+ the preceding ones. */
+ aCellStyles[ aStyleName ] = rxStyle;
+ }
+
+ /* Calculate names of user defined styles. Store styles with reserved
+ names in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maUserStyles )
+ {
+ const CellStyleModel& rModel = rxStyle->getModel();
+ OUString aStyleName = lclCreateStyleName( rModel );
+ // #i1624# #i1768# ignore unnamed user styles
+ if( aStyleName.getLength() > 0 )
+ {
+ if( aCellStyles.find( aStyleName ) != aCellStyles.end() )
+ aConflictNameStyles.push_back( rxStyle );
+ else
+ aCellStyles[ aStyleName ] = rxStyle;
+ }
+ }
+
+ // find unused names for all styles with conflicting names
+ // having the index counter outside the loop prevents performance problems with opening some pathological documents (tdf#62095)
+ sal_Int32 nIndex = 0;
+ for( const auto& rxStyle : aConflictNameStyles )
+ {
+ const CellStyleModel& rModel = rxStyle->getModel();
+ OUString aStyleName = lclCreateStyleName( rModel );
+ OUString aUnusedName;
+ do
+ {
+ aUnusedName = aStyleName + OUStringChar(' ') + OUString::number( ++nIndex );
+ }
+ while( !aCellStyles.try_emplace( aUnusedName, rxStyle ).second );
+ }
+
+ // set final names and create user-defined and modified built-in cell styles
+ aCellStyles.forEachMemWithKey( &CellStyle::finalizeImport );
+}
+
+sal_Int32 CellStyleBuffer::getDefaultXfId() const
+{
+ return mxDefStyle ? mxDefStyle->getModel().mnXfId : -1;
+}
+
+OUString CellStyleBuffer::getDefaultStyleName() const
+{
+ return createCellStyle( mxDefStyle );
+}
+
+OUString CellStyleBuffer::createCellStyle( sal_Int32 nXfId ) const
+{
+ return createCellStyle( maStylesByXf.get( nXfId ) );
+}
+
+::ScStyleSheet* CellStyleBuffer::getCellStyleSheet( sal_Int32 nXfId ) const
+{
+ return getCellStyleSheet( maStylesByXf.get( nXfId ) );
+}
+
+// private --------------------------------------------------------------------
+
+void CellStyleBuffer::insertCellStyle( CellStyleRef const & xCellStyle )
+{
+ const CellStyleModel& rModel = xCellStyle->getModel();
+ if( rModel.mnXfId < 0 )
+ return;
+
+ // insert into the built-in map or user defined map
+ (rModel.isBuiltin() ? maBuiltinStyles : maUserStyles).push_back( xCellStyle );
+
+ // insert into the XF identifier map
+ OSL_ENSURE( maStylesByXf.count( rModel.mnXfId ) == 0, "CellStyleBuffer::insertCellStyle - multiple styles with equal XF identifier" );
+ maStylesByXf[ rModel.mnXfId ] = xCellStyle;
+
+ // remember default cell style
+ if( rModel.isDefaultStyle() )
+ mxDefStyle = xCellStyle;
+}
+
+::ScStyleSheet* CellStyleBuffer::getCellStyleSheet( const CellStyleRef& rxCellStyle )
+{
+ ::ScStyleSheet* pStyleSheet = nullptr;
+ if ( rxCellStyle )
+ pStyleSheet = rxCellStyle->getStyleSheet();
+ return pStyleSheet;
+}
+
+OUString CellStyleBuffer::createCellStyle( const CellStyleRef& rxCellStyle )
+{
+ if( rxCellStyle )
+ {
+ rxCellStyle->createCellStyle();
+ const OUString& rStyleName = rxCellStyle->getFinalStyleName();
+ if( !rStyleName.isEmpty() )
+ return rStyleName;
+ }
+ // on error: fallback to default style
+ return lclGetBuiltinStyleName( OOX_STYLE_NORMAL, u"" );
+}
+
+AutoFormatModel::AutoFormatModel() :
+ mnAutoFormatId( 0 ),
+ mbApplyNumFmt( false ),
+ mbApplyFont( false ),
+ mbApplyAlignment( false ),
+ mbApplyBorder( false ),
+ mbApplyFill( false ),
+ mbApplyProtection( false )
+{
+}
+
+StylesBuffer::StylesBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maPalette( rHelper ),
+ maNumFmts( rHelper ),
+ maCellStyles( rHelper )
+{
+}
+
+FontRef StylesBuffer::createFont()
+{
+ FontRef xFont = std::make_shared<Font>( *this, false );
+ maFonts.push_back( xFont );
+ return xFont;
+}
+
+NumberFormatRef StylesBuffer::createNumFmt( sal_Int32 nNumFmtId, std::u16string_view aFmtCode )
+{
+ return maNumFmts.createNumFmt( nNumFmtId, aFmtCode );
+}
+
+sal_Int32 StylesBuffer::nextFreeNumFmtId()
+{
+ return maNumFmts.nextFreeId();
+}
+
+BorderRef StylesBuffer::createBorder()
+{
+ BorderRef xBorder = std::make_shared<Border>( *this, false );
+ maBorders.push_back( xBorder );
+ return xBorder;
+}
+
+FillRef StylesBuffer::createFill()
+{
+ FillRef xFill = std::make_shared<Fill>( *this, false );
+ maFills.push_back( xFill );
+ return xFill;
+}
+
+XfRef StylesBuffer::createCellXf()
+{
+ XfRef xXf = std::make_shared<Xf>( *this );
+ maCellXfs.push_back( xXf );
+ return xXf;
+}
+
+XfRef StylesBuffer::createStyleXf()
+{
+ XfRef xXf = std::make_shared<Xf>( *this );
+ maStyleXfs.push_back( xXf );
+ return xXf;
+}
+
+DxfRef StylesBuffer::createDxf()
+{
+ DxfRef xDxf = std::make_shared<Dxf>( *this );
+ maDxfs.push_back( xDxf );
+ return xDxf;
+}
+
+DxfRef StylesBuffer::createExtDxf()
+{
+ DxfRef xDxf = std::make_shared<Dxf>( *this );
+ maExtDxfs.push_back( xDxf );
+ return xDxf;
+}
+
+void StylesBuffer::importPaletteColor( const AttributeList& rAttribs )
+{
+ maPalette.importPaletteColor( rAttribs );
+}
+
+NumberFormatRef StylesBuffer::importNumFmt( const AttributeList& rAttribs )
+{
+ return maNumFmts.importNumFmt( rAttribs );
+}
+
+CellStyleRef StylesBuffer::importCellStyle( const AttributeList& rAttribs )
+{
+ return maCellStyles.importCellStyle( rAttribs );
+}
+
+void StylesBuffer::importPaletteColor( SequenceInputStream& rStrm )
+{
+ maPalette.importPaletteColor( rStrm );
+}
+
+void StylesBuffer::importNumFmt( SequenceInputStream& rStrm )
+{
+ maNumFmts.importNumFmt( rStrm );
+}
+
+void StylesBuffer::importCellStyle( SequenceInputStream& rStrm )
+{
+ maCellStyles.importCellStyle( rStrm );
+}
+
+void StylesBuffer::finalizeImport()
+{
+ // fonts first, are needed to finalize unit converter and XFs below
+ maFonts.forEachMem( &Font::finalizeImport );
+ // finalize unit coefficients after default font is known
+ getUnitConverter().finalizeImport();
+ // number formats
+ maNumFmts.finalizeImport();
+ // borders and fills
+ // is there a document wide RTL setting that we
+ // would/could need to pass to finalizeImport here ?
+ maBorders.forEachMem( &Border::finalizeImport, false );
+ maFills.forEachMem( &Fill::finalizeImport );
+ // style XFs and cell XFs
+ maStyleXfs.forEachMem( &Xf::finalizeImport );
+ maCellXfs.forEachMem( &Xf::finalizeImport );
+ // built-in and user defined cell styles
+ maCellStyles.finalizeImport();
+ // differential formatting (for conditional formatting)
+ maDxfs.forEachMem( &Dxf::finalizeImport );
+}
+
+::Color StylesBuffer::getPaletteColor( sal_Int32 nPaletteIdx ) const
+{
+ return maPalette.getColor( nPaletteIdx );
+}
+
+FontRef StylesBuffer::getFont( sal_Int32 nFontId ) const
+{
+ return maFonts.get( nFontId );
+}
+
+BorderRef StylesBuffer::getBorder( sal_Int32 nBorderId ) const
+{
+ return maBorders.get( nBorderId );
+}
+
+XfRef StylesBuffer::getCellXf( sal_Int32 nXfId ) const
+{
+ return maCellXfs.get( nXfId );
+}
+
+XfRef StylesBuffer::getStyleXf( sal_Int32 nXfId ) const
+{
+ return maStyleXfs.get( nXfId );
+}
+
+FontRef StylesBuffer::getFontFromCellXf( sal_Int32 nXfId ) const
+{
+ FontRef xFont;
+ if( const Xf* pXf = getCellXf( nXfId ).get() )
+ xFont = pXf->getFont();
+ return xFont;
+}
+
+FontRef StylesBuffer::getDefaultFont() const
+{
+ FontRef xDefFont;
+ if( const Xf* pXf = getStyleXf( maCellStyles.getDefaultXfId() ).get() )
+ xDefFont = pXf->getFont();
+ // no font from styles - try first loaded font (e.g. BIFF2)
+ if( !xDefFont )
+ xDefFont = maFonts.get( 0 );
+ OSL_ENSURE( xDefFont, "StylesBuffer::getDefaultFont - no default font found" );
+ return xDefFont;
+}
+
+const FontModel& StylesBuffer::getDefaultFontModel() const
+{
+ FontRef xDefFont = getDefaultFont();
+ return xDefFont ? xDefFont->getModel() : getTheme().getDefaultFontModel();
+}
+
+bool StylesBuffer::equalBorders( sal_Int32 nBorderId1, sal_Int32 nBorderId2 )
+{
+ // in OOXML, borders are assumed to be unique
+ return nBorderId1 == nBorderId2;
+}
+
+bool StylesBuffer::equalFills( sal_Int32 nFillId1, sal_Int32 nFillId2 )
+{
+ // in OOXML, fills are assumed to be unique
+ return nFillId1 == nFillId2;
+}
+
+OUString StylesBuffer::getDefaultStyleName() const
+{
+ return maCellStyles.getDefaultStyleName();
+}
+
+OUString StylesBuffer::createCellStyle( sal_Int32 nXfId ) const
+{
+ return maCellStyles.createCellStyle( nXfId );
+}
+
+::ScStyleSheet* StylesBuffer::getCellStyleSheet( sal_Int32 nXfId ) const
+{
+ return maCellStyles.getCellStyleSheet( nXfId );
+}
+
+OUString StylesBuffer::createDxfStyle( sal_Int32 nDxfId ) const
+{
+ OUString& rStyleName = maDxfStyles[ nDxfId ];
+ if (!rStyleName.isEmpty())
+ return rStyleName;
+
+ if (Dxf* pDxf = maDxfs.get(nDxfId).get())
+ {
+ // FIXME: How can we know whether this dxf is for conditional formatting,
+ // not for color filter? Currently this style is created for each dxf
+ // (which might only be used by color filter)
+ rStyleName = "ConditionalStyle_" + OUString::number(nDxfId + 1);
+
+ // Create a cell style. This may overwrite an existing style if
+ // one with the same name exists.
+ ScStyleSheet& rStyleSheet = ScfTools::MakeCellStyleSheet(
+ *getScDocument().GetStyleSheetPool(), rStyleName, true);
+
+ rStyleSheet.ResetParent();
+ SfxItemSet& rStyleItemSet =
+ rStyleSheet.GetItemSet();
+
+ pDxf->fillToItemSet(rStyleItemSet);
+
+ }
+
+ // on error: fallback to default style
+ if (rStyleName.isEmpty())
+ rStyleName = maCellStyles.getDefaultStyleName();
+
+ return rStyleName;
+}
+
+OUString StylesBuffer::createExtDxfStyle( sal_Int32 nDxfId ) const
+{
+ OUString rStyleName;
+
+ if (Dxf* pDxf = maExtDxfs.get(nDxfId).get())
+ {
+ rStyleName = "ExtConditionalStyle_" + OUString::number(nDxfId + 1);
+
+ // Create a cell style. This may overwrite an existing style if
+ // one with the same name exists.
+ ScStyleSheet& rStyleSheet = ScfTools::MakeCellStyleSheet(
+ *getScDocument().GetStyleSheetPool(), rStyleName, true);
+
+ rStyleSheet.ResetParent();
+ SfxItemSet& rStyleItemSet =
+ rStyleSheet.GetItemSet();
+
+ pDxf->fillToItemSet(rStyleItemSet);
+ }
+
+ // on error: fallback to default style
+ if (rStyleName.isEmpty())
+ rStyleName = maCellStyles.getDefaultStyleName();
+
+ return rStyleName;
+}
+
+void StylesBuffer::writeFontToItemSet( SfxItemSet& rItemSet, sal_Int32 nFontId, bool bSkipPoolDefs ) const
+{
+ if( Font* pFont = maFonts.get( nFontId ).get() )
+ pFont->fillToItemSet( rItemSet, false, bSkipPoolDefs );
+}
+
+sal_uInt32 StylesBuffer::writeNumFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const
+{
+ return maNumFmts.fillToItemSet( rItemSet, nNumFmtId, bSkipPoolDefs );
+}
+
+void StylesBuffer::writeBorderToItemSet( SfxItemSet& rItemSet, sal_Int32 nBorderId, bool bSkipPoolDefs ) const
+{
+ if( Border* pBorder = maBorders.get( nBorderId ).get() )
+ pBorder->fillToItemSet( rItemSet, bSkipPoolDefs );
+}
+
+void StylesBuffer::writeFillToItemSet( SfxItemSet& rItemSet, sal_Int32 nFillId, bool bSkipPoolDefs ) const
+{
+ if( Fill* pFill = maFills.get( nFillId ).get() )
+ pFill->fillToItemSet( rItemSet, bSkipPoolDefs );
+}
+
+bool operator==( const XfModel& rXfModel1, const XfModel& rXfModel2 )
+{
+ return ( rXfModel1.mbCellXf == rXfModel2.mbCellXf &&
+ rXfModel1.mnStyleXfId == rXfModel2.mnStyleXfId &&
+ rXfModel1.mbFontUsed == rXfModel2.mbFontUsed &&
+ rXfModel1.mnFontId == rXfModel2.mnFontId &&
+ rXfModel1.mbNumFmtUsed == rXfModel2.mbNumFmtUsed &&
+ rXfModel1.mnNumFmtId == rXfModel2.mnNumFmtId &&
+ rXfModel1.mbAlignUsed == rXfModel2.mbAlignUsed &&
+ rXfModel1.mbBorderUsed == rXfModel2.mbBorderUsed &&
+ rXfModel1.mnBorderId == rXfModel2.mnBorderId &&
+ rXfModel1.mbAreaUsed == rXfModel2.mbAreaUsed &&
+ rXfModel1.mnFillId == rXfModel2.mnFillId &&
+ rXfModel1.mbProtUsed == rXfModel2.mbProtUsed );
+}
+
+bool operator==( const Xf& rXf1, const Xf& rXf2 )
+{
+ if ( rXf1.maModel == rXf2.maModel )
+ {
+ if ( rXf1.maModel.mbAlignUsed )
+ {
+ if ( !( rXf1.maAlignment.getApiData() == rXf2.maAlignment.getApiData() ) )
+ return false;
+ }
+ if ( rXf1.maModel.mbProtUsed )
+ {
+ if ( !( rXf1.maProtection.getApiData() == rXf2.maProtection.getApiData() ) )
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+void StylesBuffer::writeCellXfToDoc(
+ ScDocumentImport& rDoc, const ScRange& rRange, sal_Int32 nXfId ) const
+{
+ Xf* pXf = maCellXfs.get(nXfId).get();
+ if (!pXf)
+ return;
+
+ pXf->writeToDoc(rDoc, rRange);
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/stylesfragment.cxx b/sc/source/filter/oox/stylesfragment.cxx
new file mode 100644
index 0000000000..e6707704dc
--- /dev/null
+++ b/sc/source/filter/oox/stylesfragment.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stylesfragment.hxx>
+#include <biffhelper.hxx>
+
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+IndexedColorsContext::IndexedColorsContext( WorkbookFragmentBase& rFragment ) :
+ WorkbookContextBase( rFragment )
+{
+}
+
+ContextHandlerRef IndexedColorsContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( indexedColors ):
+ if( nElement == XLS_TOKEN( rgbColor ) ) getStyles().importPaletteColor( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef IndexedColorsContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_INDEXEDCOLORS:
+ if( nRecId == BIFF12_ID_RGBCOLOR ) getStyles().importPaletteColor( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef FontContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxFont )
+ mxFont->importAttribs( nElement, rAttribs );
+ return nullptr;
+}
+
+void BorderContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( mxBorder && (getCurrentElement() == XLS_TOKEN( border )) )
+ mxBorder->importBorder( rAttribs );
+}
+
+ContextHandlerRef BorderContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxBorder ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( border ):
+ mxBorder->importStyle( nElement, rAttribs );
+ return this;
+
+ default:
+ if( nElement == XLS_TOKEN( color ) )
+ mxBorder->importColor( getCurrentElement(), rAttribs );
+ }
+ return nullptr;
+}
+
+ContextHandlerRef FillContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxFill ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( fill ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( patternFill ): mxFill->importPatternFill( rAttribs ); return this;
+ case XLS_TOKEN( gradientFill ): mxFill->importGradientFill( rAttribs ); return this;
+ }
+ break;
+ case XLS_TOKEN( patternFill ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( fgColor ): mxFill->importFgColor( rAttribs ); break;
+ case XLS_TOKEN( bgColor ): mxFill->importBgColor( rAttribs ); break;
+ }
+ break;
+ case XLS_TOKEN( gradientFill ):
+ if( nElement == XLS_TOKEN( stop ) )
+ {
+ mfGradPos = rAttribs.getDouble( XML_position, -1.0 );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( stop ):
+ if( nElement == XLS_TOKEN( color ) )
+ mxFill->importColor( rAttribs, mfGradPos );
+ break;
+ }
+ return nullptr;
+}
+
+void XfContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( mxXf && (getCurrentElement() == XLS_TOKEN( xf )) )
+ mxXf->importXf( rAttribs, mbCellXf );
+}
+
+ContextHandlerRef XfContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxXf ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( xf ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( alignment ): mxXf->importAlignment( rAttribs ); break;
+ case XLS_TOKEN( protection ): mxXf->importProtection( rAttribs ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef DxfContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxDxf ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( dxf ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( font ): return new FontContext( *this, mxDxf->createFont() );
+ case XLS_TOKEN( border ): return new BorderContext( *this, mxDxf->createBorder() );
+ case XLS_TOKEN( fill ): return new FillContext( *this, mxDxf->createFill() );
+
+ case XLS_TOKEN( numFmt ): mxDxf->importNumFmt( rAttribs ); break;
+#if 0
+ case XLS_TOKEN( alignment ): mxDxf->importAlignment( rAttribs ); break;
+ case XLS_TOKEN( protection ): mxDxf->importProtection( rAttribs ); break;
+#endif
+ }
+ break;
+ }
+
+ if( mxExtDxf ) switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( dxf ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( font ): return new FontContext( *this, mxExtDxf->createFont() );
+ case XLS_TOKEN( border ): return new BorderContext( *this, mxExtDxf->createBorder() );
+ case XLS_TOKEN( fill ): return new FillContext( *this, mxExtDxf->createFill() );
+ case XLS_TOKEN( numFmt ): mxExtDxf->importNumFmt( rAttribs ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+StylesFragment::StylesFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef StylesFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( styleSheet ) ) return this;
+ break;
+
+ case XLS_TOKEN( styleSheet ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( colors ):
+ case XLS_TOKEN( numFmts ):
+ case XLS_TOKEN( fonts ):
+ case XLS_TOKEN( borders ):
+ case XLS_TOKEN( fills ):
+ case XLS_TOKEN( cellXfs ):
+ case XLS_TOKEN( cellStyleXfs ):
+ case XLS_TOKEN( dxfs ):
+ case XLS_TOKEN( cellStyles ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( colors ):
+ if( nElement == XLS_TOKEN( indexedColors ) ) return new IndexedColorsContext( *this );
+ break;
+ case XLS_TOKEN( numFmts ):
+ if( nElement == XLS_TOKEN( numFmt ) ) getStyles().importNumFmt( rAttribs );
+ break;
+ case XLS_TOKEN( fonts ):
+ if( nElement == XLS_TOKEN( font ) ) return new FontContext( *this, getStyles().createFont() );
+ break;
+ case XLS_TOKEN( borders ):
+ if( nElement == XLS_TOKEN( border ) ) return new BorderContext( *this, getStyles().createBorder() );
+ break;
+ case XLS_TOKEN( fills ):
+ if( nElement == XLS_TOKEN( fill ) ) return new FillContext( *this, getStyles().createFill() );
+ break;
+ case XLS_TOKEN( cellXfs ):
+ if( nElement == XLS_TOKEN( xf ) ) return new XfContext( *this, getStyles().createCellXf(), true );
+ break;
+ case XLS_TOKEN( cellStyleXfs ):
+ if( nElement == XLS_TOKEN( xf ) ) return new XfContext( *this, getStyles().createStyleXf(), false );
+ break;
+ case XLS_TOKEN( dxfs ):
+ if( nElement == XLS_TOKEN( dxf ) ) return new DxfContext( *this, getStyles().createDxf() );
+ break;
+ case XLS_TOKEN( cellStyles ):
+ if( nElement == XLS_TOKEN( cellStyle ) ) getStyles().importCellStyle( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef StylesFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_STYLESHEET ) return this;
+ break;
+
+ case BIFF12_ID_STYLESHEET:
+ switch( nRecId )
+ {
+ case BIFF12_ID_COLORS:
+ case BIFF12_ID_NUMFMTS:
+ case BIFF12_ID_FONTS:
+ case BIFF12_ID_BORDERS:
+ case BIFF12_ID_FILLS:
+ case BIFF12_ID_CELLXFS:
+ case BIFF12_ID_CELLSTYLEXFS:
+ case BIFF12_ID_DXFS:
+ case BIFF12_ID_CELLSTYLES: return this;
+ }
+ break;
+
+ case BIFF12_ID_COLORS:
+ if( nRecId == BIFF12_ID_INDEXEDCOLORS ) return new IndexedColorsContext( *this );
+ break;
+ case BIFF12_ID_NUMFMTS:
+ if( nRecId == BIFF12_ID_NUMFMT ) getStyles().importNumFmt( rStrm );
+ break;
+ case BIFF12_ID_FONTS:
+ if( nRecId == BIFF12_ID_FONT ) getStyles().createFont()->importFont( rStrm );
+ break;
+ case BIFF12_ID_BORDERS:
+ if( nRecId == BIFF12_ID_BORDER ) getStyles().createBorder()->importBorder( rStrm );
+ break;
+ case BIFF12_ID_FILLS:
+ if( nRecId == BIFF12_ID_FILL ) getStyles().createFill()->importFill( rStrm );
+ break;
+ case BIFF12_ID_CELLXFS:
+ if( nRecId == BIFF12_ID_XF ) getStyles().createCellXf()->importXf( rStrm, true );
+ break;
+ case BIFF12_ID_CELLSTYLEXFS:
+ if( nRecId == BIFF12_ID_XF ) getStyles().createStyleXf()->importXf( rStrm, false );
+ break;
+ case BIFF12_ID_DXFS:
+ if( nRecId == BIFF12_ID_DXF ) getStyles().createDxf()->importDxf( rStrm );
+ break;
+ case BIFF12_ID_CELLSTYLES:
+ if( nRecId == BIFF12_ID_CELLSTYLE ) getStyles().importCellStyle( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* StylesFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_BORDERS, BIFF12_ID_BORDERS + 1 },
+ { BIFF12_ID_CELLSTYLES, BIFF12_ID_CELLSTYLES + 1 },
+ { BIFF12_ID_CELLSTYLEXFS, BIFF12_ID_CELLSTYLEXFS + 1 },
+ { BIFF12_ID_CELLXFS, BIFF12_ID_CELLXFS + 1 },
+ { BIFF12_ID_COLORS, BIFF12_ID_COLORS + 1 },
+ { BIFF12_ID_DXFS, BIFF12_ID_DXFS + 1 },
+ { BIFF12_ID_FILLS, BIFF12_ID_FILLS + 1 },
+ { BIFF12_ID_FONTS, BIFF12_ID_FONTS + 1 },
+ { BIFF12_ID_INDEXEDCOLORS, BIFF12_ID_INDEXEDCOLORS + 1 },
+ { BIFF12_ID_MRUCOLORS, BIFF12_ID_MRUCOLORS + 1 },
+ { BIFF12_ID_NUMFMTS, BIFF12_ID_NUMFMTS + 1 },
+ { BIFF12_ID_STYLESHEET, BIFF12_ID_STYLESHEET + 1 },
+ { BIFF12_ID_TABLESTYLES, BIFF12_ID_TABLESTYLES + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void StylesFragment::finalizeImport()
+{
+ getStyles().finalizeImport();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablebuffer.cxx b/sc/source/filter/oox/tablebuffer.cxx
new file mode 100644
index 0000000000..dec0498f46
--- /dev/null
+++ b/sc/source/filter/oox/tablebuffer.cxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tablebuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <com/sun/star/sheet/XDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+TableModel::TableModel() :
+ mnId( -1 ),
+ mnType( XML_worksheet ),
+ mnHeaderRows( 1 ),
+ mnTotalsRows( 0 )
+{
+}
+
+Table::Table( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maAutoFilters( rHelper ),
+ maTableColumns( rHelper ),
+ mnTokenIndex( -1 )
+{
+}
+
+void Table::importTable( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet );
+ maModel.maProgName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maDisplayName = rAttribs.getXString( XML_displayName, OUString() );
+ maModel.mnId = rAttribs.getInteger( XML_id, -1 );
+ maModel.mnType = rAttribs.getToken( XML_tableType, XML_worksheet );
+ maModel.mnHeaderRows = rAttribs.getInteger( XML_headerRowCount, 1 );
+ maModel.mnTotalsRows = rAttribs.getInteger( XML_totalsRowCount, 0 );
+}
+
+void Table::importTable( SequenceInputStream& rStrm, sal_Int16 nSheet )
+{
+ BinRange aBinRange;
+ sal_Int32 nType;
+ rStrm >> aBinRange;
+ nType = rStrm.readInt32();
+ maModel.mnId = rStrm.readInt32();
+ maModel.mnHeaderRows = rStrm.readInt32();
+ maModel.mnTotalsRows = rStrm.readInt32();
+ rStrm.skip( 32 );
+ rStrm >> maModel.maProgName >> maModel.maDisplayName;
+
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, aBinRange, nSheet );
+ static const sal_Int32 spnTypes[] = { XML_worksheet, XML_TOKEN_INVALID, XML_TOKEN_INVALID, XML_queryTable };
+ maModel.mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID );
+}
+
+void Table::finalizeImport()
+{
+ // Create database range. Note that Excel 2007 and later names database
+ // ranges (or tables in their terminology) as Table1, Table2 etc. We need
+ // to import them as named db ranges because they may be referenced by
+ // name in formula expressions.
+ if( (maModel.mnId <= 0) || maModel.maDisplayName.isEmpty() )
+ return;
+
+ try
+ {
+ maDBRangeName = maModel.maDisplayName;
+
+ Reference< XDatabaseRange > xDatabaseRange(
+ createDatabaseRangeObject( maDBRangeName, maModel.maRange ), UNO_SET_THROW);
+ ::css::table::CellRangeAddress aAddressRange = xDatabaseRange->getDataArea();
+ maDestRange = ScRange( aAddressRange.StartColumn, aAddressRange.StartRow, aAddressRange.Sheet,
+ aAddressRange.EndColumn, aAddressRange.EndRow, aAddressRange.Sheet );
+
+ PropertySet aPropSet( xDatabaseRange );
+
+ // Default HasHeader is true at ScDBData.
+ if (maModel.mnHeaderRows != 1)
+ {
+ SAL_WARN_IF( maModel.mnHeaderRows > 1, "sc.filter",
+ "Table HeaderRows > 1 not supported: " << maModel.mnHeaderRows);
+ if (maModel.mnHeaderRows == 0)
+ aPropSet.setProperty( PROP_ContainsHeader, false);
+ }
+
+ if (maModel.mnTotalsRows > 0)
+ {
+ SAL_WARN_IF( maModel.mnTotalsRows > 1, "sc.filter",
+ "Table TotalsRows > 1 not supported: " << maModel.mnTotalsRows);
+ aPropSet.setProperty( PROP_TotalsRow, true);
+ }
+
+ // get formula token index of the database range
+ if( !aPropSet.getProperty( mnTokenIndex, PROP_TokenIndex ) )
+ mnTokenIndex = -1;
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "Table::finalizeImport - cannot create database range" );
+ }
+}
+
+void Table::applyAutoFilters()
+{
+ if( maDBRangeName.isEmpty() )
+ return;
+
+ try
+ {
+ // get the range ( maybe we should cache the xDatabaseRange from finalizeImport )
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ Reference< XDatabaseRanges > xDatabaseRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY_THROW );
+ Reference< XDatabaseRange > xDatabaseRange( xDatabaseRanges->getByName( maDBRangeName ), UNO_QUERY );
+ maAutoFilters.finalizeImport( xDatabaseRange, maModel.maRange.aStart.Tab() );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "Table::applyAutofilters - cannot create filter" );
+ }
+}
+
+void Table::applyTableColumns()
+{
+ maTableColumns.finalizeImport( findDatabaseRangeByIndex( mnTokenIndex ));
+}
+
+TableBuffer::TableBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+Table& TableBuffer::createTable()
+{
+ TableVector::value_type xTable = std::make_shared<Table>( *this );
+ maTables.push_back( xTable );
+ return *xTable;
+}
+
+void TableBuffer::finalizeImport()
+{
+ // map all tables by identifier and display name
+ for( const auto& rxTable : maTables )
+ insertTableToMaps( rxTable );
+ // finalize all valid tables
+ maIdTables.forEachMem( &Table::finalizeImport );
+}
+
+void TableBuffer::applyAutoFilters()
+{
+ maIdTables.forEachMem( &Table::applyAutoFilters );
+}
+
+void TableBuffer::applyTableColumns()
+{
+ maIdTables.forEachMem( &Table::applyTableColumns );
+}
+
+TableRef TableBuffer::getTable( sal_Int32 nTableId ) const
+{
+ return maIdTables.get( nTableId );
+}
+
+TableRef TableBuffer::getTable( const OUString& rDispName ) const
+{
+ return maNameTables.get( rDispName );
+}
+
+// private --------------------------------------------------------------------
+
+void TableBuffer::insertTableToMaps( const TableRef& rxTable )
+{
+ sal_Int32 nTableId = rxTable->getTableId();
+ const OUString& rDispName = rxTable->getDisplayName();
+ if( (nTableId > 0) && !rDispName.isEmpty() )
+ {
+ OSL_ENSURE( !maIdTables.has( nTableId ), "TableBuffer::insertTableToMaps - multiple table identifier" );
+ maIdTables[ nTableId ] = rxTable;
+ OSL_ENSURE( !maNameTables.has( rDispName ), "TableBuffer::insertTableToMaps - multiple table name" );
+ maNameTables[ rDispName ] = rxTable;
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx b/sc/source/filter/oox/tablecolumnsbuffer.cxx
new file mode 100644
index 0000000000..4baa7f1bde
--- /dev/null
+++ b/sc/source/filter/oox/tablecolumnsbuffer.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 <tablecolumnsbuffer.hxx>
+
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::xls {
+
+TableColumn::TableColumn( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnId( -1 ),
+ mnDataDxfId( -1 )
+{
+}
+
+void TableColumn::importTableColumn( const AttributeList& rAttribs )
+{
+ mnId = rAttribs.getInteger( XML_id, -1 );
+ maName = rAttribs.getString( XML_name, OUString() );
+ mnDataDxfId = rAttribs.getInteger( XML_dataDxfId, -1 );
+ if ( rAttribs.hasAttribute( XML_totalsRowFunction ) )
+ maColumnAttributes.maTotalsFunction = rAttribs.getStringDefaulted( XML_totalsRowFunction );
+}
+
+void TableColumn::importTableColumn( SequenceInputStream& /*rStrm*/ )
+{
+ /* XXX not implemented */
+ (void) mnId;
+}
+
+const OUString& TableColumn::getName() const
+{
+ return maName;
+}
+
+const TableColumnAttributes& TableColumn::getColumnAttributes() const
+{
+ return maColumnAttributes;
+}
+
+TableColumns::TableColumns( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnCount(0)
+{
+}
+
+void TableColumns::importTableColumns( const AttributeList& rAttribs )
+{
+ mnCount = rAttribs.getInteger( XML_count, 0 );
+}
+
+void TableColumns::importTableColumns( SequenceInputStream& /*rStrm*/ )
+{
+ /* XXX not implemented */
+ (void) mnCount;
+}
+
+TableColumn& TableColumns::createTableColumn()
+{
+ TableColumnVector::value_type xTableColumn = std::make_shared<TableColumn>( *this );
+ maTableColumnVector.push_back( xTableColumn );
+ return *xTableColumn;
+}
+
+bool TableColumns::finalizeImport( ScDBData* pDBData )
+{
+ SAL_WARN_IF( static_cast<size_t>(mnCount) != maTableColumnVector.size(), "sc.filter",
+ "TableColumns::finalizeImport - count attribute doesn't match number of tableColumn elements");
+ if ( pDBData )
+ {
+ /* TODO: use svl::SharedString for names */
+ ::std::vector< OUString > aNames( maTableColumnVector.size());
+ ::std::vector< TableColumnAttributes > aAttributesVector( maTableColumnVector.size() );
+ size_t i = 0;
+ for (const auto& rxTableColumn : maTableColumnVector)
+ {
+ aNames[i] = rxTableColumn->getName();
+ aAttributesVector[i] = rxTableColumn->getColumnAttributes();
+ ++i;
+ }
+ pDBData->SetTableColumnNames( std::move(aNames) );
+ pDBData->SetTableColumnAttributes( std::move(aAttributesVector) );
+ return true;
+ }
+ return false;
+}
+
+TableColumnsBuffer::TableColumnsBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+TableColumns& TableColumnsBuffer::createTableColumns()
+{
+ TableColumnsVector::value_type xTableColumns = std::make_shared<TableColumns>( *this );
+ maTableColumnsVector.push_back( xTableColumns );
+ return *xTableColumns;
+}
+
+void TableColumnsBuffer::finalizeImport( ScDBData* pDBData )
+{
+ TableColumns* pTableColumns = getActiveTableColumns();
+ if ( pTableColumns )
+ pTableColumns->finalizeImport( pDBData );
+}
+
+TableColumns* TableColumnsBuffer::getActiveTableColumns()
+{
+ // not more than one table columns descriptor per table
+ SAL_WARN_IF( maTableColumnsVector.size() > 1, "sc.filter",
+ "TableColumnsBuffer::getActiveTableColumns - too many table columns" );
+ // stick to the last imported table columns
+ return maTableColumnsVector.empty() ? nullptr : maTableColumnsVector.back().get();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablecolumnscontext.cxx b/sc/source/filter/oox/tablecolumnscontext.cxx
new file mode 100644
index 0000000000..270f544bd5
--- /dev/null
+++ b/sc/source/filter/oox/tablecolumnscontext.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 <tablecolumnscontext.hxx>
+
+#include <tablecolumnsbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+TableColumnContext::TableColumnContext( WorksheetContextBase& rParent, TableColumn& rTableColumn ) :
+ WorksheetContextBase( rParent ),
+ mrTableColumn( rTableColumn )
+{
+}
+
+ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+ /* no known child elements */
+ return nullptr;
+}
+
+void TableColumnContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrTableColumn.importTableColumn( rAttribs );
+}
+
+ContextHandlerRef TableColumnContext::onCreateRecordContext( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
+{
+ /* no known child elements */
+ return nullptr;
+}
+
+void TableColumnContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrTableColumn.importTableColumn( rStrm );
+}
+
+TableColumnsContext::TableColumnsContext( WorksheetFragmentBase& rFragment, TableColumns& rTableColumns ) :
+ WorksheetContextBase( rFragment ),
+ mrTableColumns( rTableColumns )
+{
+}
+
+ContextHandlerRef TableColumnsContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( (getCurrentElement() == XLS_TOKEN( tableColumns )) && (nElement == XLS_TOKEN( tableColumn )) )
+ return new TableColumnContext( *this, mrTableColumns.createTableColumn() );
+ return nullptr;
+}
+
+void TableColumnsContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrTableColumns.importTableColumns( rAttribs );
+}
+
+ContextHandlerRef TableColumnsContext::onCreateRecordContext( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
+{
+ /* XXX not implemented */
+#if 0
+ if( (getCurrentElement() == BIFF12_ID_TABLECOLUMNS) && (nRecId == BIFF12_ID_TABLECOLUMN) )
+ return new TableColumnContext( *this, mrTableColumns.createTableColumn() );
+#endif
+ return nullptr;
+}
+
+void TableColumnsContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrTableColumns.importTableColumns( rStrm );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablefragment.cxx b/sc/source/filter/oox/tablefragment.cxx
new file mode 100644
index 0000000000..99f3ede7a0
--- /dev/null
+++ b/sc/source/filter/oox/tablefragment.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 <tablefragment.hxx>
+#include <biffhelper.hxx>
+
+#include <autofiltercontext.hxx>
+#include <tablecolumnscontext.hxx>
+#include <tablebuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+TableFragment::TableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrTable( getTables().createTable() )
+{
+}
+
+ContextHandlerRef TableFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( table ) )
+ {
+ mrTable.importTable( rAttribs, getSheetIndex() );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( table ):
+ switch (nElement)
+ {
+ case XLS_TOKEN( autoFilter ):
+ return new AutoFilterContext( *this, mrTable.createAutoFilter() );
+ case XLS_TOKEN( tableColumns ):
+ return new TableColumnsContext( *this, mrTable.createTableColumns() );
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef TableFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_TABLE )
+ {
+ mrTable.importTable( rStrm, getSheetIndex() );
+ return this;
+ }
+ break;
+ case BIFF12_ID_TABLE:
+ if( nRecId == BIFF12_ID_AUTOFILTER )
+ return new AutoFilterContext( *this, mrTable.createAutoFilter() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* TableFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 },
+ { BIFF12_ID_CUSTOMFILTERS, BIFF12_ID_CUSTOMFILTERS + 1 },
+ { BIFF12_ID_DISCRETEFILTERS, BIFF12_ID_DISCRETEFILTERS + 1 },
+ { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 },
+ { BIFF12_ID_TABLE, BIFF12_ID_TABLE + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/themebuffer.cxx b/sc/source/filter/oox/themebuffer.cxx
new file mode 100644
index 0000000000..3c82a31610
--- /dev/null
+++ b/sc/source/filter/oox/themebuffer.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <themebuffer.hxx>
+
+#include <stylesbuffer.hxx>
+
+namespace oox::xls {
+
+
+ThemeBuffer::ThemeBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mxDefFontModel( new FontModel )
+{
+ //! TODO: locale dependent font name
+ mxDefFontModel->maName = "Cambria";
+ mxDefFontModel->mfHeight = 11.0;
+}
+
+ThemeBuffer::~ThemeBuffer()
+{
+}
+
+::Color ThemeBuffer::getColorByToken( sal_Int32 nToken ) const
+{
+ ::Color nColor = 0;
+ return getClrScheme().getColor( nToken, nColor ) ? nColor : API_RGB_TRANSPARENT;
+}
+
+::Color ThemeBuffer::getColorByIndex(size_t nIndex) const
+{
+ ::Color nColor = 0;
+ return getClrScheme().getColorByIndex(nIndex, nColor) ? nColor : API_RGB_TRANSPARENT;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/unitconverter.cxx b/sc/source/filter/oox/unitconverter.cxx
new file mode 100644
index 0000000000..dd4178eee8
--- /dev/null
+++ b/sc/source/filter/oox/unitconverter.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 <unitconverter.hxx>
+
+#include <com/sun/star/awt/DeviceInfo.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/awt/XFont.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <stylesbuffer.hxx>
+#include <biffhelper.hxx>
+#include <docuno.hxx>
+
+namespace com::sun::star::awt { struct FontDescriptor; }
+
+namespace oox::xls {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+/** Returns true, if the passed year is a leap year. */
+bool lclIsLeapYear( sal_Int32 nYear )
+{
+ return ((nYear % 4) == 0) && (((nYear % 100) != 0) || ((nYear % 400) == 0));
+}
+
+void lclSkipYearBlock( sal_Int32& ornDays, sal_Int16& ornYear, sal_Int32 nDaysInBlock, sal_Int32 nYearsPerBlock, sal_Int32 nMaxBlocks )
+{
+ sal_Int32 nBlocks = ::std::min< sal_Int32 >( ornDays / nDaysInBlock, nMaxBlocks );
+ ornYear = static_cast< sal_Int16 >( ornYear + nYearsPerBlock * nBlocks );
+ ornDays -= nBlocks * nDaysInBlock;
+}
+
+/** Returns the number of days before the passed date, starting from the null
+ date 0000-Jan-01, using standard leap year conventions. */
+sal_Int32 lclGetDays( const util::Date& rDate )
+{
+ // number of days in all full years before passed date including all leap days
+ sal_Int32 nDays = rDate.Year * 365 + ((rDate.Year + 3) / 4) - ((rDate.Year + 99) / 100) + ((rDate.Year + 399) / 400);
+ OSL_ENSURE( (1 <= rDate.Month) && (rDate.Month <= 12), "lclGetDays - invalid month" );
+ OSL_ENSURE( (1 <= rDate.Day) && (rDate.Day <= 31), "lclGetDays - invalid day" ); // yes, this is weak...
+ if( (1 <= rDate.Month) && (rDate.Month <= 12) )
+ {
+ // number of days at start of month jan feb mar apr may jun jul aug sep oct nov dec
+ static const sal_Int32 spnCumDays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ // add number of days in full months before passed date
+ nDays += spnCumDays[ rDate.Month - 1 ];
+ // add number of days from passed date (this adds one day too much)
+ nDays += rDate.Day;
+ /* Remove the one day added too much if there is no leap day before
+ the passed day in the passed year. This means: remove the day, if
+ we are in january or february (leap day not reached if existing),
+ or if the passed year is not a leap year. */
+ if( (rDate.Month < 3) || !lclIsLeapYear( rDate.Year ) )
+ --nDays;
+ }
+ return nDays;
+}
+
+} // namespace
+
+UnitConverter::UnitConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnNullDate( lclGetDays( util::Date( 30, 12, 1899 ) ) )
+{
+ // initialize constant and default coefficients
+ const DeviceInfo& rDeviceInfo = getBaseFilter().getGraphicHelper().getDeviceInfo();
+ maCoeffs[Unit::Twip] = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::emu);
+ maCoeffs[Unit::Emu] = 1;
+ maCoeffs[Unit::ScreenX] = o3tl::convert((rDeviceInfo.PixelPerMeterX > 0) ? (1000.0 / rDeviceInfo.PixelPerMeterX) : 0.5, o3tl::Length::mm, o3tl::Length::emu);
+ maCoeffs[Unit::ScreenY] = o3tl::convert((rDeviceInfo.PixelPerMeterY > 0) ? (1000.0 / rDeviceInfo.PixelPerMeterY) : 0.5, o3tl::Length::mm, o3tl::Length::emu);
+ maCoeffs[Unit::Digit] = o3tl::convert(2.0, o3tl::Length::mm, o3tl::Length::emu); // default: 1 digit = 2 mm
+ maCoeffs[Unit::Space] = o3tl::convert(1.0, o3tl::Length::mm, o3tl::Length::emu); // default 1 space = 1 mm
+
+ // error code maps
+ addErrorCode( BIFF_ERR_NULL, "#NULL!" );
+ addErrorCode( BIFF_ERR_DIV0, "#DIV/0!" );
+ addErrorCode( BIFF_ERR_VALUE, "#VALUE!" );
+ addErrorCode( BIFF_ERR_REF, "#REF!" );
+ addErrorCode( BIFF_ERR_NAME, "#NAME?" );
+ addErrorCode( BIFF_ERR_NUM, "#NUM!" );
+ addErrorCode( BIFF_ERR_NA, "#N/A" );
+}
+
+void UnitConverter::finalizeImport()
+{
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY );
+ if( !xDevice.is() )
+ return;
+
+ // get character widths from default font
+ const oox::xls::Font* pDefFont = getStyles().getDefaultFont().get();
+ if( !pDefFont )
+ return;
+
+ // XDevice expects pixels in font descriptor, but font contains twips
+ const FontDescriptor& aDesc = pDefFont->getFontDescriptor();
+ Reference< XFont > xFont = xDevice->getFont( aDesc );
+ if( !xFont.is() )
+ return;
+
+ // get maximum width of all digits
+ sal_Int64 nDigitWidth = 0;
+ for( sal_Unicode cChar = '0'; cChar <= '9'; ++cChar )
+ nDigitWidth = ::std::max(nDigitWidth, o3tl::convert(xFont->getCharWidth(cChar),
+ o3tl::Length::twip, o3tl::Length::emu));
+ if( nDigitWidth > 0 )
+ maCoeffs[ Unit::Digit ] = nDigitWidth;
+ // get width of space character
+ sal_Int64 nSpaceWidth
+ = o3tl::convert(xFont->getCharWidth(' '), o3tl::Length::twip, o3tl::Length::emu);
+ if( nSpaceWidth > 0 )
+ maCoeffs[ Unit::Space ] = nSpaceWidth;
+}
+
+void UnitConverter::finalizeNullDate( const util::Date& rNullDate )
+{
+ // convert the nulldate to number of days since 0000-Jan-01
+ mnNullDate = lclGetDays( rNullDate );
+}
+
+// conversion -----------------------------------------------------------------
+
+double UnitConverter::scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const
+{
+ return (eFromUnit == eToUnit) ? fValue : (fValue * getCoefficient( eFromUnit ) / getCoefficient( eToUnit ));
+}
+
+double UnitConverter::calcSerialFromDateTime( const util::DateTime& rDateTime ) const
+{
+ sal_Int32 nDays = lclGetDays( util::Date( rDateTime.Day, rDateTime.Month, rDateTime.Year ) ) - mnNullDate;
+ OSL_ENSURE( nDays >= 0, "UnitConverter::calcDateTimeSerial - invalid date" );
+ OSL_ENSURE( (rDateTime.Hours <= 23) && (rDateTime.Minutes <= 59) && (rDateTime.Seconds <= 59), "UnitConverter::calcDateTimeSerial - invalid time" );
+ return nDays + rDateTime.Hours / 24.0 + rDateTime.Minutes / 1440.0 + rDateTime.Seconds / 86400.0;
+}
+
+util::DateTime UnitConverter::calcDateTimeFromSerial( double fSerial ) const
+{
+ util::DateTime aDateTime( 0, 0, 0, 0, 1, 1, 0, false );
+ double fDays = 0.0;
+ double fTime = modf( fSerial, &fDays );
+
+ // calculate date from number of days with O(1) complexity
+ sal_Int32 nDays = getLimitedValue< sal_Int32, double >( fDays + mnNullDate, 0, 3652424 );
+ // skip year 0, assumed to be a leap year. By starting at year 1, leap years can be handled easily
+ if( nDays >= 366 ) { ++aDateTime.Year; nDays -= 366; }
+ // skip full blocks of 400, 100, 4 years, and remaining full years
+ lclSkipYearBlock( nDays, aDateTime.Year, 400 * 365 + 97, 400, 24 );
+ lclSkipYearBlock( nDays, aDateTime.Year, 100 * 365 + 24, 100, 3 );
+ lclSkipYearBlock( nDays, aDateTime.Year, 4 * 365 + 1, 4, 24 );
+ lclSkipYearBlock( nDays, aDateTime.Year, 365, 1, 3 );
+ // skip full months of current year
+ static const sal_Int32 spnDaysInMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ if( (nDays >= 59) && !lclIsLeapYear( aDateTime.Year ) ) ++nDays;
+ const sal_Int32* pnDaysInMonth = spnDaysInMonth;
+ while( *pnDaysInMonth >= nDays ) { ++aDateTime.Month; nDays -= *pnDaysInMonth; ++pnDaysInMonth; }
+ aDateTime.Day = static_cast< sal_uInt16 >( nDays + 1 );
+
+ // calculate time from fractional part of serial
+ sal_Int32 nTime = getLimitedValue< sal_Int32, double >( fTime * 86400, 0, 86399 );
+ aDateTime.Seconds = static_cast< sal_uInt16 >( nTime % 60 );
+ nTime /= 60;
+ aDateTime.Minutes = static_cast< sal_uInt16 >( nTime % 60 );
+ aDateTime.Hours = static_cast< sal_uInt16 >( nTime / 60 );
+
+ return aDateTime;
+}
+
+sal_uInt8 UnitConverter::calcBiffErrorCode( const OUString& rErrorCode ) const
+{
+ auto aIt = maOoxErrCodes.find( rErrorCode );
+ return (aIt == maOoxErrCodes.end()) ? BIFF_ERR_NA : aIt->second;
+}
+
+OUString UnitConverter::calcErrorString( sal_uInt8 nErrorCode ) const
+{
+ auto iFail( maOoxErrCodes.cend());
+ for (auto aIt( maOoxErrCodes.cbegin()); aIt != maOoxErrCodes.cend(); ++aIt)
+ {
+ if (aIt->second == nErrorCode)
+ return aIt->first;
+ if (aIt->second == BIFF_ERR_NA)
+ iFail = aIt;
+ }
+ assert(iFail != maOoxErrCodes.end()); // BIFF_ERR_NA really should be in the map...
+ return iFail != maOoxErrCodes.end() ? iFail->first : OUString();
+}
+
+void UnitConverter::addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode )
+{
+ maOoxErrCodes[ rErrorCode ] = nErrorCode;
+}
+
+double UnitConverter::getCoefficient( Unit eUnit ) const
+{
+ return maCoeffs[ eUnit ];
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/viewsettings.cxx b/sc/source/filter/oox/viewsettings.cxx
new file mode 100644
index 0000000000..2badbd80a4
--- /dev/null
+++ b/sc/source/filter/oox/viewsettings.cxx
@@ -0,0 +1,651 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <viewsettings.hxx>
+
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/document/NamedPropertyValues.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <osl/diagnose.h>
+#include <unotools/mediadescriptor.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertymap.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <workbooksettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <vcl/svapp.hxx>
+#include <docuno.hxx>
+
+namespace com::sun::star::container { class XNameContainer; }
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::FilterBase;
+
+namespace {
+
+const sal_Int32 OOX_BOOKVIEW_TABBARRATIO_DEF = 600; /// Default tabbar ratio.
+const sal_Int32 OOX_SHEETVIEW_NORMALZOOM_DEF = 100; /// Default zoom for normal view.
+const sal_Int32 OOX_SHEETVIEW_SHEETLAYZOOM_DEF = 60; /// Default zoom for pagebreak preview.
+
+const sal_uInt8 BIFF12_PANE_FROZEN = 0x01;
+const sal_uInt8 BIFF12_PANE_FROZENNOSPLIT = 0x02;
+
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWFORMULAS = 0x0002;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWGRID = 0x0004;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWHEADINGS = 0x0008;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWZEROS = 0x0010;
+const sal_uInt16 BIFF12_SHEETVIEW_RIGHTTOLEFT = 0x0020;
+const sal_uInt16 BIFF12_SHEETVIEW_SELECTED = 0x0040;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWOUTLINE = 0x0100;
+const sal_uInt16 BIFF12_SHEETVIEW_DEFGRIDCOLOR = 0x0200;
+
+const sal_uInt16 BIFF12_CHARTSHEETVIEW_SELECTED = 0x0001;
+const sal_uInt16 BIFF12_CHARTSHEETVIEW_ZOOMTOFIT = 0x0002;
+
+const sal_uInt8 BIFF12_WBVIEW_HIDDEN = 0x01;
+const sal_uInt8 BIFF12_WBVIEW_MINIMIZED = 0x02;
+const sal_uInt8 BIFF12_WBVIEW_SHOWHORSCROLL = 0x08;
+const sal_uInt8 BIFF12_WBVIEW_SHOWVERSCROLL = 0x10;
+const sal_uInt8 BIFF12_WBVIEW_SHOWTABBAR = 0x20;
+
+// Attention: view settings in Calc do not use com.sun.star.view.DocumentZoomType!
+const sal_Int16 API_ZOOMTYPE_PERCENT = 0; /// Zoom value in percent.
+
+const sal_Int32 API_ZOOMVALUE_MIN = 20; /// Minimum zoom in Calc.
+const sal_Int32 API_ZOOMVALUE_MAX = 400; /// Maximum zoom in Calc.
+
+// no predefined constants for split mode
+const sal_Int16 API_SPLITMODE_NONE = 0; /// No splits in window.
+const sal_Int16 API_SPLITMODE_SPLIT = 1; /// Window is split.
+const sal_Int16 API_SPLITMODE_FREEZE = 2; /// Window has frozen panes.
+
+// no predefined constants for pane identifiers
+const sal_Int16 API_SPLITPANE_TOPLEFT = 0; /// Top-left, or top pane.
+const sal_Int16 API_SPLITPANE_TOPRIGHT = 1; /// Top-right pane.
+const sal_Int16 API_SPLITPANE_BOTTOMLEFT = 2; /// Bottom-left, bottom, left, or single pane.
+const sal_Int16 API_SPLITPANE_BOTTOMRIGHT = 3; /// Bottom-right, or right pane.
+
+/** Returns the OOXML pane identifier from the passed BIFF pane id. */
+sal_Int32 lclGetOoxPaneId( sal_Int32 nBiffPaneId, sal_Int32 nDefaultPaneId )
+{
+ static const sal_Int32 spnPaneIds[] = { XML_bottomRight, XML_topRight, XML_bottomLeft, XML_topLeft };
+ return STATIC_ARRAY_SELECT( spnPaneIds, nBiffPaneId, nDefaultPaneId );
+}
+
+} // namespace
+
+PaneSelectionModel::PaneSelectionModel() :
+ mnActiveCellId( 0 )
+{
+}
+
+SheetViewModel::SheetViewModel() :
+ mnWorkbookViewId( 0 ),
+ mnViewType( XML_normal ),
+ mnActivePaneId( XML_topLeft ),
+ mnPaneState( XML_split ),
+ mfSplitX( 0.0 ),
+ mfSplitY( 0.0 ),
+ mnCurrentZoom( 0 ),
+ mnNormalZoom( 0 ),
+ mnSheetLayoutZoom( 0 ),
+ mnPageLayoutZoom( 0 ),
+ mbSelected( false ),
+ mbRightToLeft( false ),
+ mbDefGridColor( true ),
+ mbShowFormulas( false ),
+ mbShowGrid( true ),
+ mbShowHeadings( true ),
+ mbShowZeros( true ),
+ mbShowOutline( true ),
+ mbZoomToFit( false )
+{
+ maGridColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+}
+
+bool SheetViewModel::isPageBreakPreview() const
+{
+ return mnViewType == XML_pageBreakPreview;
+}
+
+sal_Int32 SheetViewModel::getNormalZoom() const
+{
+ const sal_Int32& rnZoom = isPageBreakPreview() ? mnNormalZoom : mnCurrentZoom;
+ sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_NORMALZOOM_DEF;
+ return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX );
+}
+
+sal_Int32 SheetViewModel::getPageBreakZoom() const
+{
+ const sal_Int32& rnZoom = isPageBreakPreview() ? mnCurrentZoom : mnSheetLayoutZoom;
+ sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_SHEETLAYZOOM_DEF;
+ return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX );
+}
+
+::Color SheetViewModel::getGridColor( const FilterBase& rFilter ) const
+{
+ return mbDefGridColor ? API_RGB_TRANSPARENT : maGridColor.getColor( rFilter.getGraphicHelper() );
+}
+
+const PaneSelectionModel* SheetViewModel::getActiveSelection() const
+{
+ return maPaneSelMap.get( mnActivePaneId ).get();
+}
+
+PaneSelectionModel& SheetViewModel::createPaneSelection( sal_Int32 nPaneId )
+{
+ PaneSelectionModelMap::mapped_type& rxPaneSel = maPaneSelMap[ nPaneId ];
+ if( !rxPaneSel )
+ rxPaneSel = std::make_shared<PaneSelectionModel>();
+ return *rxPaneSel;
+}
+
+SheetViewSettings::SheetViewSettings( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void SheetViewSettings::importSheetView( const AttributeList& rAttribs )
+{
+ SheetViewModel& rModel = *createSheetView();
+ rModel.maGridColor.setIndexed( rAttribs.getInteger( XML_colorId, OOX_COLOR_WINDOWTEXT ) );
+ rModel.maFirstPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell, OUString() ), getSheetIndex(), false );
+ rModel.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 );
+ rModel.mnViewType = rAttribs.getToken( XML_view, XML_normal );
+ rModel.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 );
+ rModel.mnNormalZoom = rAttribs.getInteger( XML_zoomScaleNormal, 0 );
+ rModel.mnSheetLayoutZoom = rAttribs.getInteger( XML_zoomScaleSheetLayoutView, 0 );
+ rModel.mnPageLayoutZoom = rAttribs.getInteger( XML_zoomScalePageLayoutView, 0 );
+ rModel.mbSelected = rAttribs.getBool( XML_tabSelected, false );
+ rModel.mbRightToLeft = rAttribs.getBool( XML_rightToLeft, false );
+ rModel.mbDefGridColor = rAttribs.getBool( XML_defaultGridColor, true );
+ rModel.mbShowFormulas = rAttribs.getBool( XML_showFormulas, false );
+ rModel.mbShowGrid = rAttribs.getBool( XML_showGridLines, true );
+ rModel.mbShowHeadings = rAttribs.getBool( XML_showRowColHeaders, true );
+ rModel.mbShowZeros = rAttribs.getBool( XML_showZeros, true );
+ rModel.mbShowOutline = rAttribs.getBool( XML_showOutlineSymbols, true );
+}
+
+void SheetViewSettings::importPane( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing sheet view model" );
+ if( !maSheetViews.empty() )
+ {
+ SheetViewModel& rModel = *maSheetViews.back();
+ rModel.maSecondPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell, OUString() ), getSheetIndex(), false );
+ rModel.mnActivePaneId = rAttribs.getToken( XML_activePane, XML_topLeft );
+ rModel.mnPaneState = rAttribs.getToken( XML_state, XML_split );
+ rModel.mfSplitX = rAttribs.getDouble( XML_xSplit, 0.0 );
+ rModel.mfSplitY = rAttribs.getDouble( XML_ySplit, 0.0 );
+ }
+}
+
+void SheetViewSettings::importSelection( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importSelection - missing sheet view model" );
+ if( !maSheetViews.empty() )
+ {
+ // pane this selection belongs to
+ sal_Int32 nPaneId = rAttribs.getToken( XML_pane, XML_topLeft );
+ PaneSelectionModel& rSelData = maSheetViews.back()->createPaneSelection( nPaneId );
+ // cursor position
+ rSelData.maActiveCell = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_activeCell, OUString() ), getSheetIndex(), false );
+ rSelData.mnActiveCellId = rAttribs.getInteger( XML_activeCellId, 0 );
+ // selection
+ rSelData.maSelection.RemoveAll();
+ getAddressConverter().convertToCellRangeList( rSelData.maSelection, rAttribs.getString( XML_sqref, OUString() ), getSheetIndex(), false );
+ }
+}
+
+void SheetViewSettings::importChartSheetView( const AttributeList& rAttribs )
+{
+ SheetViewModel& rModel = *createSheetView();
+ rModel.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 );
+ rModel.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 );
+ rModel.mbSelected = rAttribs.getBool( XML_tabSelected, false );
+ rModel.mbZoomToFit = rAttribs.getBool( XML_zoomToFit, false );
+}
+
+void SheetViewSettings::importSheetView( SequenceInputStream& rStrm )
+{
+ SheetViewModel& rModel = *createSheetView();
+ sal_uInt16 nFlags;
+ sal_Int32 nViewType;
+ BinAddress aFirstPos;
+ nFlags = rStrm.readuInt16();
+ nViewType = rStrm.readInt32();
+ rStrm >> aFirstPos;
+ rModel.maGridColor.importColorId( rStrm );
+ rModel.mnCurrentZoom = rStrm.readuInt16();
+ rModel.mnNormalZoom = rStrm.readuInt16();
+ rModel.mnSheetLayoutZoom = rStrm.readuInt16();
+ rModel.mnPageLayoutZoom = rStrm.readuInt16();
+ rModel.mnWorkbookViewId = rStrm.readInt32();
+
+ rModel.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false );
+ static const sal_Int32 spnViewTypes[] = { XML_normal, XML_pageBreakPreview, XML_pageLayout };
+ rModel.mnViewType = STATIC_ARRAY_SELECT( spnViewTypes, nViewType, XML_normal );
+ rModel.mbSelected = getFlag( nFlags, BIFF12_SHEETVIEW_SELECTED );
+ rModel.mbRightToLeft = getFlag( nFlags, BIFF12_SHEETVIEW_RIGHTTOLEFT );
+ rModel.mbDefGridColor = getFlag( nFlags, BIFF12_SHEETVIEW_DEFGRIDCOLOR );
+ rModel.mbShowFormulas = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWFORMULAS );
+ rModel.mbShowGrid = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWGRID );
+ rModel.mbShowHeadings = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWHEADINGS );
+ rModel.mbShowZeros = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWZEROS );
+ rModel.mbShowOutline = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWOUTLINE );
+}
+
+void SheetViewSettings::importPane( SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing sheet view model" );
+ if( maSheetViews.empty() )
+ return;
+
+ SheetViewModel& rModel = *maSheetViews.back();
+
+ BinAddress aSecondPos;
+ sal_Int32 nActivePaneId;
+ sal_uInt8 nFlags;
+ rModel.mfSplitX = rStrm.readDouble();
+ rModel.mfSplitY = rStrm.readDouble();
+ rStrm >> aSecondPos;
+ nActivePaneId = rStrm.readInt32();
+ nFlags = rStrm.readuChar();
+
+ rModel.maSecondPos = getAddressConverter().createValidCellAddress( aSecondPos, getSheetIndex(), false );
+ rModel.mnActivePaneId = lclGetOoxPaneId( nActivePaneId, XML_topLeft );
+ rModel.mnPaneState = getFlagValue( nFlags, BIFF12_PANE_FROZEN, getFlagValue( nFlags, BIFF12_PANE_FROZENNOSPLIT, XML_frozen, XML_frozenSplit ), XML_split );
+}
+
+void SheetViewSettings::importSelection( SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importSelection - missing sheet view model" );
+ if( maSheetViews.empty() )
+ return;
+
+ // pane this selection belongs to
+ sal_Int32 nPaneId = rStrm.readInt32();
+ PaneSelectionModel& rPaneSel = maSheetViews.back()->createPaneSelection( lclGetOoxPaneId( nPaneId, -1 ) );
+ // cursor position
+ BinAddress aActiveCell;
+ rStrm >> aActiveCell;
+ rPaneSel.mnActiveCellId = rStrm.readInt32();
+ rPaneSel.maActiveCell = getAddressConverter().createValidCellAddress( aActiveCell, getSheetIndex(), false );
+ // selection
+ BinRangeList aSelection;
+ rStrm >> aSelection;
+ rPaneSel.maSelection.RemoveAll();
+ getAddressConverter().convertToCellRangeList( rPaneSel.maSelection, aSelection, getSheetIndex(), false );
+}
+
+void SheetViewSettings::importChartSheetView( SequenceInputStream& rStrm )
+{
+ SheetViewModel& rModel = *createSheetView();
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ rModel.mnCurrentZoom = rStrm.readInt32();
+ rModel.mnWorkbookViewId = rStrm.readInt32();
+
+ rModel.mbSelected = getFlag( nFlags, BIFF12_CHARTSHEETVIEW_SELECTED );
+ rModel.mbZoomToFit = getFlag( nFlags, BIFF12_CHARTSHEETVIEW_ZOOMTOFIT );
+}
+
+void SheetViewSettings::finalizeImport()
+{
+ // force creation of sheet view model to get the Excel defaults
+ SheetViewModelRef xModel = maSheetViews.empty() ? createSheetView() : maSheetViews.front();
+
+ // #i59590# #158194# special handling for chart sheets (Excel ignores some settings in chart sheets)
+ if( getSheetType() == WorksheetType::Chart )
+ {
+ xModel->maPaneSelMap.clear();
+ xModel->maFirstPos = xModel->maSecondPos = ScAddress( SCCOL ( 0 ), SCROW ( 0 ), getSheetIndex() );
+ xModel->mnViewType = XML_normal;
+ xModel->mnActivePaneId = XML_topLeft;
+ xModel->mnPaneState = XML_split;
+ xModel->mfSplitX = xModel->mfSplitY = 0.0;
+ xModel->mbRightToLeft = false;
+ xModel->mbDefGridColor = true;
+ xModel->mbShowFormulas = false;
+ xModel->mbShowGrid = true;
+ xModel->mbShowHeadings = true;
+ xModel->mbShowZeros = true;
+ xModel->mbShowOutline = true;
+ }
+
+ // sheet selected (active sheet must be selected)
+ bool bSelected = xModel->mbSelected || (getSheetIndex() == getViewSettings().getActiveCalcSheet());
+ if ( bSelected )
+ {
+ // active tab/sheet cannot be hidden
+ // always force it to be displayed
+ PropertySet aPropSet( getSheet() );
+ aPropSet.setProperty( PROP_IsVisible, true );
+ }
+ // visible area and current cursor position (selection not supported via API)
+ ScAddress aFirstPos = xModel->maFirstPos;
+ const PaneSelectionModel* pPaneSel = xModel->getActiveSelection();
+ ScAddress aCursor = pPaneSel ? pPaneSel->maActiveCell : aFirstPos;
+
+ // freeze/split position default
+ sal_Int16 nHSplitMode = API_SPLITMODE_NONE;
+ sal_Int16 nVSplitMode = API_SPLITMODE_NONE;
+ sal_Int32 nHSplitPos = 0;
+ sal_Int32 nVSplitPos = 0;
+ // active pane default
+ sal_Int16 nActivePane = API_SPLITPANE_BOTTOMLEFT;
+
+ // freeze/split position
+ if( (xModel->mnPaneState == XML_frozen) || (xModel->mnPaneState == XML_frozenSplit) )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns in the
+ frozen area (rows/columns scrolled outside are not included),
+ Calc uses absolute position of first unfrozen row/column. */
+ const ScAddress& rMaxApiPos = getAddressConverter().getMaxApiAddress();
+ if( (xModel->mfSplitX >= 1.0) && ( xModel->maFirstPos.Col() + xModel->mfSplitX <= rMaxApiPos.Col() ) )
+ nHSplitPos = static_cast< sal_Int32 >( xModel->maFirstPos.Col() + xModel->mfSplitX );
+ nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE;
+ if( (xModel->mfSplitY >= 1.0) && ( xModel->maFirstPos.Row() + xModel->mfSplitY <= rMaxApiPos.Row() ) )
+ nVSplitPos = static_cast< sal_Int32 >( xModel->maFirstPos.Row() + xModel->mfSplitY );
+ nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE;
+ }
+ else if( xModel->mnPaneState == XML_split )
+ {
+ // split window: view settings API uses twips...
+ nHSplitPos = getLimitedValue< sal_Int32, double >( xModel->mfSplitX + 0.5, 0, SAL_MAX_INT32 );
+ nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE;
+ nVSplitPos = getLimitedValue< sal_Int32, double >( xModel->mfSplitY + 0.5, 0, SAL_MAX_INT32 );
+ nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE;
+ }
+
+ // active pane
+ switch( xModel->mnActivePaneId )
+ {
+ // no horizontal split -> always use left panes
+ // no vertical split -> always use *bottom* panes
+ case XML_topLeft:
+ nActivePane = (nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT;
+ break;
+ case XML_topRight:
+ nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ?
+ ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT) :
+ ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMRIGHT : API_SPLITPANE_TOPRIGHT);
+ break;
+ case XML_bottomLeft:
+ nActivePane = API_SPLITPANE_BOTTOMLEFT;
+ break;
+ case XML_bottomRight:
+ nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_BOTTOMRIGHT;
+ break;
+ }
+
+ // write the sheet view settings into the property sequence
+ PropertyMap aPropMap;
+ aPropMap.setProperty( PROP_TableSelected, bSelected);
+ aPropMap.setProperty( PROP_CursorPositionX, aCursor.Col() );
+ aPropMap.setProperty( PROP_CursorPositionY, aCursor.Row() );
+ aPropMap.setProperty( PROP_HorizontalSplitMode, nHSplitMode);
+ aPropMap.setProperty( PROP_VerticalSplitMode, nVSplitMode);
+ aPropMap.setProperty( PROP_HorizontalSplitPositionTwips, nHSplitPos);
+ aPropMap.setProperty( PROP_VerticalSplitPositionTwips, nVSplitPos);
+ aPropMap.setProperty( PROP_ActiveSplitRange, nActivePane);
+ aPropMap.setProperty( PROP_PositionLeft, aFirstPos.Col() );
+ aPropMap.setProperty( PROP_PositionTop, aFirstPos.Row() );
+ aPropMap.setProperty( PROP_PositionRight, xModel->maSecondPos.Col() );
+ aPropMap.setProperty( PROP_PositionBottom, ((nVSplitPos > 0) ? xModel->maSecondPos.Row() : xModel->maFirstPos.Row() ) );
+ aPropMap.setProperty( PROP_ZoomType, API_ZOOMTYPE_PERCENT);
+ aPropMap.setProperty( PROP_ZoomValue, static_cast< sal_Int16 >( xModel->getNormalZoom() ));
+ aPropMap.setProperty( PROP_PageViewZoomValue, static_cast< sal_Int16 >( xModel->getPageBreakZoom() ));
+ aPropMap.setProperty( PROP_GridColor, xModel->getGridColor( getBaseFilter() ));
+ aPropMap.setProperty( PROP_ShowPageBreakPreview, xModel->isPageBreakPreview());
+ aPropMap.setProperty( PROP_ShowFormulas, xModel->mbShowFormulas);
+ aPropMap.setProperty( PROP_ShowGrid, xModel->mbShowGrid);
+ aPropMap.setProperty( PROP_HasColumnRowHeaders, xModel->mbShowHeadings);
+ aPropMap.setProperty( PROP_ShowZeroValues, xModel->mbShowZeros);
+ aPropMap.setProperty( PROP_IsOutlineSymbolsSet, xModel->mbShowOutline);
+
+ // store sheet view settings in global view settings object
+ getViewSettings().setSheetViewSettings( getSheetIndex(), xModel, Any( aPropMap.makePropertyValueSequence() ) );
+}
+
+bool SheetViewSettings::isSheetRightToLeft() const
+{
+ return !maSheetViews.empty() && maSheetViews.front()->mbRightToLeft;
+}
+
+// private --------------------------------------------------------------------
+
+SheetViewModelRef SheetViewSettings::createSheetView()
+{
+ SheetViewModelRef xModel = std::make_shared<SheetViewModel>();
+ maSheetViews.push_back( xModel );
+ return xModel;
+}
+
+WorkbookViewModel::WorkbookViewModel() :
+ mnWinX( 0 ),
+ mnWinY( 0 ),
+ mnWinWidth( 0 ),
+ mnWinHeight( 0 ),
+ mnActiveSheet( 0 ),
+ mnFirstVisSheet( 0 ),
+ mnTabBarWidth( OOX_BOOKVIEW_TABBARRATIO_DEF ),
+ mnVisibility( XML_visible ),
+ mbShowTabBar( true ),
+ mbShowHorScroll( true ),
+ mbShowVerScroll( true ),
+ mbMinimized( false )
+{
+}
+
+ViewSettings::ViewSettings( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbValidOleSize( false )
+{
+}
+
+void ViewSettings::importWorkbookView( const AttributeList& rAttribs )
+{
+ WorkbookViewModel& rModel = createWorkbookView();
+ rModel.mnWinX = rAttribs.getInteger( XML_xWindow, 0 );
+ rModel.mnWinY = rAttribs.getInteger( XML_yWindow, 0 );
+ rModel.mnWinWidth = rAttribs.getInteger( XML_windowWidth, 0 );
+ rModel.mnWinHeight = rAttribs.getInteger( XML_windowHeight, 0 );
+ rModel.mnActiveSheet = rAttribs.getInteger( XML_activeTab, 0 );
+ rModel.mnFirstVisSheet = rAttribs.getInteger( XML_firstSheet, 0 );
+ rModel.mnTabBarWidth = rAttribs.getInteger( XML_tabRatio, 600 );
+ rModel.mnVisibility = rAttribs.getToken( XML_visibility, XML_visible );
+ rModel.mbShowTabBar = rAttribs.getBool( XML_showSheetTabs, true );
+ rModel.mbShowHorScroll = rAttribs.getBool( XML_showHorizontalScroll, true );
+ rModel.mbShowVerScroll = rAttribs.getBool( XML_showVerticalScroll, true );
+ rModel.mbMinimized = rAttribs.getBool( XML_minimized, false );
+}
+
+void ViewSettings::importOleSize( const AttributeList& rAttribs )
+{
+ OUString aRange = rAttribs.getString( XML_ref, OUString() );
+ mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aRange, 0, true, false );
+}
+
+void ViewSettings::importWorkbookView( SequenceInputStream& rStrm )
+{
+ WorkbookViewModel& rModel = createWorkbookView();
+ sal_uInt8 nFlags;
+ rModel.mnWinX = rStrm.readInt32();
+ rModel.mnWinY = rStrm.readInt32();
+ rModel.mnWinWidth = rStrm.readInt32();
+ rModel.mnWinHeight = rStrm.readInt32();
+ rModel.mnTabBarWidth = rStrm.readInt32();
+ rModel.mnFirstVisSheet = rStrm.readInt32();
+ rModel.mnActiveSheet = rStrm.readInt32();
+ nFlags = rStrm.readuChar();
+ rModel.mnVisibility = getFlagValue( nFlags, BIFF12_WBVIEW_HIDDEN, XML_hidden, XML_visible );
+ rModel.mbShowTabBar = getFlag( nFlags, BIFF12_WBVIEW_SHOWTABBAR );
+ rModel.mbShowHorScroll = getFlag( nFlags, BIFF12_WBVIEW_SHOWHORSCROLL );
+ rModel.mbShowVerScroll = getFlag( nFlags, BIFF12_WBVIEW_SHOWVERSCROLL );
+ rModel.mbMinimized = getFlag( nFlags, BIFF12_WBVIEW_MINIMIZED );
+}
+
+void ViewSettings::importOleSize( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aBinRange, 0, true, false );
+}
+
+void ViewSettings::setSheetViewSettings( sal_Int16 nSheet, const SheetViewModelRef& rxSheetView, const Any& rProperties )
+{
+ maSheetViews[ nSheet ] = rxSheetView;
+ maSheetProps[ nSheet ] = rProperties;
+}
+
+void ViewSettings::setSheetUsedArea( const ScRange& rUsedArea )
+{
+ assert( rUsedArea.IsValid() );
+ maSheetUsedAreas[ rUsedArea.aStart.Tab() ] = rUsedArea;
+}
+
+void ViewSettings::finalizeImport()
+{
+ const WorksheetBuffer& rWorksheets = getWorksheets();
+ if( rWorksheets.getWorksheetCount() <= 0 ) return;
+
+ // force creation of workbook view model to get the Excel defaults
+ const WorkbookViewModel& rModel = maBookViews.empty() ? createWorkbookView() : *maBookViews.front();
+
+ // show object mode is part of workbook settings
+ sal_Int16 nShowMode = getWorkbookSettings().getApiShowObjectMode();
+
+ // view settings for all sheets
+ Reference< XNameContainer > xSheetsNC = NamedPropertyValues::create( getBaseFilter().getComponentContext() );
+ if( !xSheetsNC.is() ) return;
+ for( const auto& [rWorksheet, rObj] : maSheetProps )
+ ContainerHelper::insertByName( xSheetsNC, rWorksheets.getCalcSheetName( rWorksheet ), rObj );
+
+ // use active sheet to set sheet properties that are document-global in Calc
+ sal_Int16 nActiveSheet = getActiveCalcSheet();
+ SheetViewModelRef& rxActiveSheetView = maSheetViews[ nActiveSheet ];
+ OSL_ENSURE( rxActiveSheetView, "ViewSettings::finalizeImport - missing active sheet view settings" );
+ if( !rxActiveSheetView )
+ rxActiveSheetView = std::make_shared<SheetViewModel>();
+
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xContainer = new comphelper::IndexedPropertyValuesContainer();
+ try
+ {
+ PropertyMap aPropMap;
+ aPropMap.setProperty( PROP_Tables, xSheetsNC);
+ aPropMap.setProperty( PROP_ActiveTable, rWorksheets.getCalcSheetName( nActiveSheet ));
+ aPropMap.setProperty( PROP_HasHorizontalScrollBar, rModel.mbShowHorScroll);
+ aPropMap.setProperty( PROP_HasVerticalScrollBar, rModel.mbShowVerScroll);
+ aPropMap.setProperty( PROP_HasSheetTabs, rModel.mbShowTabBar);
+ aPropMap.setProperty( PROP_RelativeHorizontalTabbarWidth, double( rModel.mnTabBarWidth / 1000.0 ));
+ aPropMap.setProperty( PROP_ShowObjects, nShowMode);
+ aPropMap.setProperty( PROP_ShowCharts, nShowMode);
+ aPropMap.setProperty( PROP_ShowDrawing, nShowMode);
+ aPropMap.setProperty( PROP_GridColor, rxActiveSheetView->getGridColor( getBaseFilter() ));
+ aPropMap.setProperty( PROP_ShowPageBreakPreview, rxActiveSheetView->isPageBreakPreview());
+ aPropMap.setProperty( PROP_ShowFormulas, rxActiveSheetView->mbShowFormulas);
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ // tdf#126541 sheet based grid visibility shouldn't overwrite the global grid visibility
+ aPropMap.setProperty(PROP_ShowGrid, true);
+ }
+ else
+ {
+ // tdf#142854 except for headless mode, otherwise we could get a regression here:
+ // The sheet based grid visibility (bShowGrid) is stored in view settings. Headless
+ // mode means not to export view setting, including sheet based grid visibility.
+ // As the old workaround, use global visibility to keep the losing sheet visibility.
+ // FIXME: this only works correctly if all sheets have the same grid visibility.
+ // The sheet based bShowGrid should be moved to another location, which is supported
+ // by the headless mode, too.
+ aPropMap.setProperty(PROP_ShowGrid, rxActiveSheetView->mbShowGrid);
+ }
+ aPropMap.setProperty( PROP_HasColumnRowHeaders, rxActiveSheetView->mbShowHeadings);
+ aPropMap.setProperty( PROP_ShowZeroValues, rxActiveSheetView->mbShowZeros);
+ aPropMap.setProperty( PROP_IsOutlineSymbolsSet, rxActiveSheetView->mbShowOutline);
+
+ xContainer->insertByIndex( 0, Any( aPropMap.makePropertyValueSequence() ) );
+ getDocument()->setViewData( xContainer );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ViewSettings::finalizeImport - cannot create document view settings" );
+ }
+
+ /* Set visible area to be used if this document is an embedded OLE object.
+ #i44077# If a new OLE object is inserted from file, there is no OLESIZE
+ record in the Excel file. In this case, use the used area calculated
+ from file contents (used cells and drawing objects). */
+ maOleSize.aStart.SetTab( nActiveSheet );
+ maOleSize.aEnd.SetTab( nActiveSheet );
+ const ScRange* pVisibleArea = mbValidOleSize ?
+ &maOleSize : ContainerHelper::getMapElement( maSheetUsedAreas, nActiveSheet );
+ if( !pVisibleArea )
+ return;
+
+ // calculate the visible area in units of 1/100 mm
+ PropertySet aRangeProp( getCellRangeFromDoc( *pVisibleArea ) );
+ css::awt::Point aPos;
+ css::awt::Size aSize;
+ if( aRangeProp.getProperty( aPos, PROP_Position ) && aRangeProp.getProperty( aSize, PROP_Size ) )
+ {
+ // set the visible area as sequence of long at the media descriptor
+ Sequence< sal_Int32 > aWinExtent{ aPos.X, aPos.Y,
+ aPos.X + aSize.Width, aPos.Y + aSize.Height };
+ getBaseFilter().getMediaDescriptor()[ "WinExtent" ] <<= aWinExtent;
+ }
+}
+
+sal_Int16 ViewSettings::getActiveCalcSheet() const
+{
+ return maBookViews.empty() ? 0 : ::std::max< sal_Int16 >( getWorksheets().getCalcSheetIndex( maBookViews.front()->mnActiveSheet ), 0 );
+}
+
+// private --------------------------------------------------------------------
+
+WorkbookViewModel& ViewSettings::createWorkbookView()
+{
+ WorkbookViewModelRef xModel = std::make_shared<WorkbookViewModel>();
+ maBookViews.push_back( xModel );
+ return *xModel;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbookfragment.cxx b/sc/source/filter/oox/workbookfragment.cxx
new file mode 100644
index 0000000000..97cc3ca9d1
--- /dev/null
+++ b/sc/source/filter/oox/workbookfragment.cxx
@@ -0,0 +1,669 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <workbookfragment.hxx>
+
+#include <oox/core/filterbase.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/drawingml/themefragmenthandler.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/progressbar.hxx>
+#include <oox/ole/olestorage.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+#include <chartsheetfragment.hxx>
+#include <connectionsfragment.hxx>
+#include <externallinkbuffer.hxx>
+#include <externallinkfragment.hxx>
+#include <formulabuffer.hxx>
+#include <pivotcachebuffer.hxx>
+#include <sharedstringsfragment.hxx>
+#include <revisionfragment.hxx>
+#include <stylesfragment.hxx>
+#include <tablebuffer.hxx>
+#include <themebuffer.hxx>
+#include <viewsettings.hxx>
+#include <workbooksettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <worksheethelper.hxx>
+#include <worksheetfragment.hxx>
+#include <extlstcontext.hxx>
+#include <documentimport.hxx>
+#include <biffhelper.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <calcconfig.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <formulaopt.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/weld.hxx>
+
+#include <oox/core/fastparser.hxx>
+#include <svx/svdpage.hxx>
+#include <comphelper/threadpool.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+#include <oox/ole/vbaproject.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+using ::oox::drawingml::ThemeFragmentHandler;
+
+namespace {
+
+const double PROGRESS_LENGTH_GLOBALS = 0.1; /// 10% of progress bar for globals import.
+
+} // namespace
+
+WorkbookFragment::WorkbookFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef WorkbookFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( workbook ) ) return this;
+ break;
+
+ case XLS_TOKEN( workbook ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheets ):
+ case XLS_TOKEN( bookViews ):
+ case XLS_TOKEN( externalReferences ):
+ case XLS_TOKEN( definedNames ):
+ case XLS_TOKEN( pivotCaches ): return this;
+
+ case XLS_TOKEN( fileSharing ): getWorkbookSettings().importFileSharing( rAttribs ); break;
+ case XLS_TOKEN( workbookPr ): getWorkbookSettings().importWorkbookPr( rAttribs ); break;
+ case XLS_TOKEN( calcPr ): getWorkbookSettings().importCalcPr( rAttribs ); break;
+ case XLS_TOKEN( oleSize ): getViewSettings().importOleSize( rAttribs ); break;
+
+ case XLS_TOKEN( extLst ): return new ExtLstGlobalWorkbookContext( *this );
+ }
+ break;
+
+ case XLS_TOKEN( sheets ):
+ if( nElement == XLS_TOKEN( sheet ) ) getWorksheets().importSheet( rAttribs );
+ break;
+ case XLS_TOKEN( bookViews ):
+ if( nElement == XLS_TOKEN( workbookView ) ) getViewSettings().importWorkbookView( rAttribs );
+ break;
+ case XLS_TOKEN( externalReferences ):
+ if( nElement == XLS_TOKEN( externalReference ) ) importExternalReference( rAttribs );
+ break;
+ case XLS_TOKEN( definedNames ):
+ if( nElement == XLS_TOKEN( definedName ) ) { importDefinedName( rAttribs ); return this; } // collect formula
+ break;
+ case XLS_TOKEN( pivotCaches ):
+ if( nElement == XLS_TOKEN( pivotCache ) ) importPivotCache( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void WorkbookFragment::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( definedName ) ) && mxCurrName )
+ mxCurrName->setFormula( rChars );
+}
+
+ContextHandlerRef WorkbookFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_WORKBOOK ) return this;
+ break;
+
+ case BIFF12_ID_WORKBOOK:
+ switch( nRecId )
+ {
+ case BIFF12_ID_SHEETS:
+ case BIFF12_ID_BOOKVIEWS:
+ case BIFF12_ID_EXTERNALREFS:
+ case BIFF12_ID_PIVOTCACHES: return this;
+
+ case BIFF12_ID_FILESHARING: getWorkbookSettings().importFileSharing( rStrm ); break;
+ case BIFF12_ID_WORKBOOKPR: getWorkbookSettings().importWorkbookPr( rStrm ); break;
+ case BIFF12_ID_CALCPR: getWorkbookSettings().importCalcPr( rStrm ); break;
+ case BIFF12_ID_OLESIZE: getViewSettings().importOleSize( rStrm ); break;
+ case BIFF12_ID_DEFINEDNAME: getDefinedNames().importDefinedName( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_SHEETS:
+ if( nRecId == BIFF12_ID_SHEET ) getWorksheets().importSheet( rStrm );
+ break;
+ case BIFF12_ID_BOOKVIEWS:
+ if( nRecId == BIFF12_ID_WORKBOOKVIEW ) getViewSettings().importWorkbookView( rStrm );
+ break;
+
+ case BIFF12_ID_EXTERNALREFS:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTERNALREF: importExternalRef( rStrm ); break;
+ case BIFF12_ID_EXTERNALSELF: getExternalLinks().importExternalSelf( rStrm ); break;
+ case BIFF12_ID_EXTERNALSAME: getExternalLinks().importExternalSame( rStrm ); break;
+ case BIFF12_ID_EXTERNALADDIN: getExternalLinks().importExternalAddin( rStrm ); break;
+ case BIFF12_ID_EXTERNALSHEETS: getExternalLinks().importExternalSheets( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_PIVOTCACHES:
+ if( nRecId == BIFF12_ID_PIVOTCACHE ) importPivotCache( rStrm );
+ }
+ return nullptr;
+}
+
+const RecordInfo* WorkbookFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_BOOKVIEWS, BIFF12_ID_BOOKVIEWS + 1 },
+ { BIFF12_ID_EXTERNALREFS, BIFF12_ID_EXTERNALREFS + 1 },
+ { BIFF12_ID_FUNCTIONGROUPS, BIFF12_ID_FUNCTIONGROUPS + 2 },
+ { BIFF12_ID_PIVOTCACHE, BIFF12_ID_PIVOTCACHE + 1 },
+ { BIFF12_ID_PIVOTCACHES, BIFF12_ID_PIVOTCACHES + 1 },
+ { BIFF12_ID_SHEETS, BIFF12_ID_SHEETS + 1 },
+ { BIFF12_ID_WORKBOOK, BIFF12_ID_WORKBOOK + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+namespace {
+
+typedef std::pair<WorksheetGlobalsRef, FragmentHandlerRef> SheetFragmentHandler;
+typedef std::vector<SheetFragmentHandler> SheetFragmentVector;
+
+class WorkerThread : public comphelper::ThreadTask
+{
+ sal_Int32 &mrSheetsLeft;
+ WorkbookFragment& mrWorkbookHandler;
+ rtl::Reference<FragmentHandler> mxHandler;
+
+public:
+ WorkerThread( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
+ WorkbookFragment& rWorkbookHandler,
+ rtl::Reference<FragmentHandler> xHandler,
+ sal_Int32 &rSheetsLeft ) :
+ comphelper::ThreadTask( pTag ),
+ mrSheetsLeft( rSheetsLeft ),
+ mrWorkbookHandler( rWorkbookHandler ),
+ mxHandler(std::move( xHandler ))
+ {
+ }
+
+ virtual void doWork() override
+ {
+ // We hold the solar mutex in all threads except for
+ // the small safe section of the inner loop in
+ // sheetdatacontext.cxx
+ SAL_INFO( "sc.filter", "start wait on solar" );
+ SolarMutexGuard aGuard;
+ SAL_INFO( "sc.filter", "got solar" );
+
+ std::unique_ptr<oox::core::FastParser> xParser(
+ oox::core::XmlFilterBase::createParser() );
+
+ SAL_INFO( "sc.filter", "start import" );
+ mrWorkbookHandler.importOoxFragment( mxHandler, *xParser );
+ SAL_INFO( "sc.filter", "end import, release solar" );
+ mrSheetsLeft--;
+ assert( mrSheetsLeft >= 0 );
+ if( mrSheetsLeft == 0 )
+ Application::EndYield();
+ }
+};
+
+class ProgressBarTimer : private Timer
+{
+ // FIXME: really we should unify all sheet loading
+ // progress reporting into something pleasant.
+ class ProgressWrapper : public ISegmentProgressBar
+ {
+ double mfPosition;
+ ISegmentProgressBarRef mxWrapped;
+ public:
+ explicit ProgressWrapper(ISegmentProgressBarRef xRef)
+ : mfPosition(0.0)
+ , mxWrapped(std::move(xRef))
+ {
+ }
+
+ // IProgressBar
+ virtual double getPosition() const override { return mfPosition; }
+ virtual void setPosition( double fPosition ) override { mfPosition = fPosition; }
+ // ISegmentProgressBar
+ virtual double getFreeLength() const override { return 0.0; }
+ virtual ISegmentProgressBarRef createSegment( double /* fLength */ ) override
+ {
+ return ISegmentProgressBarRef();
+ }
+ void UpdateBar()
+ {
+ mxWrapped->setPosition( mfPosition );
+ }
+ };
+ std::vector< ISegmentProgressBarRef > aSegments;
+public:
+ ProgressBarTimer() : Timer("sc ProgressBarTimer")
+ {
+ SetTimeout( 500 );
+ }
+ virtual ~ProgressBarTimer() override
+ {
+ aSegments.clear();
+ }
+ const ISegmentProgressBarRef& wrapProgress( const ISegmentProgressBarRef &xProgress )
+ {
+ aSegments.push_back( std::make_shared<ProgressWrapper>( xProgress ) );
+ return aSegments.back();
+ }
+ virtual void Invoke() override
+ {
+ for(std::shared_ptr<ISegmentProgressBar> & pSegment : aSegments)
+ static_cast< ProgressWrapper *>( pSegment.get() )->UpdateBar();
+ }
+};
+
+void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
+{
+ rWorkbookHandler.getDocImport().initForSheets();
+
+ // test sequential read in this mode
+ comphelper::ThreadPool &rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ sal_Int32 nSheetsLeft = 0;
+ ProgressBarTimer aProgressUpdater;
+ for( auto& [rxSheetGlob, rxFragment] : rSheets )
+ {
+ // getting at the WorksheetGlobals is rather unpleasant
+ IWorksheetProgress *pProgress = WorksheetHelper::getWorksheetInterface( rxSheetGlob );
+ pProgress->setCustomRowProgress(
+ aProgressUpdater.wrapProgress(
+ pProgress->getRowProgress() ) );
+ rSharedPool.pushTask( std::make_unique<WorkerThread>( pTag, rWorkbookHandler, rxFragment,
+ /* ref */ nSheetsLeft ) );
+ nSheetsLeft++;
+ }
+
+ // coverity[loop_top] - this isn't an infinite loop where nSheetsLeft gets decremented by the above threads
+ while( nSheetsLeft > 0 && !Application::IsQuit())
+ {
+ // This is a much more controlled re-enterancy hazard than
+ // allowing a yield deeper inside the filter code for progress
+ // bar updating.
+ Application::Yield();
+ }
+ rSharedPool.waitUntilDone(pTag);
+
+ // threads joined in ThreadPool destructor
+}
+
+}
+
+void WorkbookFragment::finalizeImport()
+{
+ ISegmentProgressBarRef xGlobalSegment = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS );
+
+ // read the theme substream
+ OUString aThemeFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"theme" );
+ auto& rOoxTheme = getTheme();
+
+ auto pTheme = rOoxTheme.oox::drawingml::Theme::getTheme(); // needed full name here because a conflict with WorkbookHelper and Theme in ThemeBuffer
+ if (!pTheme)
+ {
+ pTheme = std::make_shared<model::Theme>();
+ rOoxTheme.setTheme(pTheme);
+ }
+
+ if( !aThemeFragmentPath.isEmpty() )
+ importOoxFragment(new ThemeFragmentHandler(getFilter(), aThemeFragmentPath, rOoxTheme, *pTheme));
+ xGlobalSegment->setPosition( 0.25 );
+
+ // read the styles substream (requires finalized theme buffer)
+ OUString aStylesFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"styles" );
+ if( !aStylesFragmentPath.isEmpty() )
+ importOoxFragment( new StylesFragment( *this, aStylesFragmentPath ) );
+ xGlobalSegment->setPosition( 0.5 );
+
+ // read the shared string table substream (requires finalized styles buffer)
+ OUString aSstFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"sharedStrings" );
+ if( !aSstFragmentPath.isEmpty() )
+ if (!importOoxFragment( new SharedStringsFragment( *this, aSstFragmentPath ) ))
+ importOoxFragment(new SharedStringsFragment(*this, aSstFragmentPath.replaceFirst("sharedStrings","SharedStrings")));
+ xGlobalSegment->setPosition( 0.75 );
+
+ // read the connections substream
+ OUString aConnFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"connections" );
+ if( !aConnFragmentPath.isEmpty() )
+ importOoxFragment( new ConnectionsFragment( *this, aConnFragmentPath ) );
+ xGlobalSegment->setPosition( 1.0 );
+
+ /* Create fragments for all sheets, before importing them. Needed to do
+ some preprocessing in the fragment constructors, e.g. loading the table
+ fragments for all sheets that are needed before the cell formulas are
+ loaded. Additionally, the instances of the WorkbookGlobals structures
+ have to be stored for every sheet. */
+ SheetFragmentVector aSheetFragments;
+ std::vector<WorksheetHelper*> aHelpers;
+ WorksheetBuffer& rWorksheets = getWorksheets();
+ sal_Int32 nWorksheetCount = rWorksheets.getWorksheetCount();
+ for( sal_Int32 nWorksheet = 0; nWorksheet < nWorksheetCount; ++nWorksheet )
+ {
+ sal_Int16 nCalcSheet = rWorksheets.getCalcSheetIndex( nWorksheet );
+ const Relation* pRelation = getRelations().getRelationFromRelId( rWorksheets.getWorksheetRelId( nWorksheet ) );
+ if( (nCalcSheet >= 0) && pRelation )
+ {
+ // get fragment path of the sheet
+ OUString aFragmentPath = getFragmentPathFromRelation( *pRelation );
+ OSL_ENSURE( !aFragmentPath.isEmpty(), "WorkbookFragment::finalizeImport - cannot access sheet fragment" );
+ if( !aFragmentPath.isEmpty() )
+ {
+ // leave space for formula processing ( calculate the segments as
+ // if there is an extra sheet )
+ double fSegmentLength = getProgressBar().getFreeLength() / (nWorksheetCount - ( nWorksheet - 1) );
+ ISegmentProgressBarRef xSheetSegment = getProgressBar().createSegment( fSegmentLength );
+
+ // get the sheet type according to the relations type
+ WorksheetType eSheetType = WorksheetType::Empty;
+ if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "worksheet" ) ||
+ pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "worksheet" ))
+ eSheetType = WorksheetType::Work;
+ else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "chartsheet" ) ||
+ pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "chartsheet" ))
+ eSheetType = WorksheetType::Chart;
+ else if( (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlMacrosheet" )) ||
+ (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlIntlMacrosheet" )) )
+ eSheetType = WorksheetType::Macro;
+ else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "dialogsheet" ) ||
+ pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT(" dialogsheet" ))
+ eSheetType = WorksheetType::Dialog;
+ OSL_ENSURE( eSheetType != WorksheetType::Empty, "WorkbookFragment::finalizeImport - unknown sheet type" );
+ if( eSheetType != WorksheetType::Empty )
+ {
+ // create the WorksheetGlobals object
+ WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, xSheetSegment, eSheetType, nCalcSheet );
+ OSL_ENSURE( xSheetGlob, "WorkbookFragment::finalizeImport - missing sheet in document" );
+ if( xSheetGlob )
+ {
+ // create the sheet fragment handler
+ ::rtl::Reference< WorksheetFragmentBase > xFragment;
+ switch( eSheetType )
+ {
+ case WorksheetType::Work:
+ case WorksheetType::Macro:
+ case WorksheetType::Dialog:
+ xFragment.set( new WorksheetFragment( *xSheetGlob, aFragmentPath ) );
+ break;
+ case WorksheetType::Chart:
+ xFragment.set( new ChartsheetFragment( *xSheetGlob, aFragmentPath ) );
+ break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case WorksheetType::Empty:
+ break;
+ }
+
+ // insert the fragment into the map
+ if( xFragment.is() )
+ {
+ aSheetFragments.emplace_back( xSheetGlob, xFragment.get() );
+ aHelpers.push_back(xFragment.get());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // setup structure sizes for the number of sheets
+ getFormulaBuffer().SetSheetCount( nWorksheetCount );
+
+ // create all database ranges and defined names, in that order
+ getTables().finalizeImport();
+ getTables().applyTableColumns();
+ getDefinedNames().finalizeImport();
+ // open the VBA project storage
+ OUString aVbaFragmentPath = getFragmentPathFromFirstType( CREATE_MSOFFICE_RELATION_TYPE( "vbaProject" ) );
+ if( !aVbaFragmentPath.isEmpty() )
+ {
+ Reference< XInputStream > xInStrm = getBaseFilter().openInputStream( aVbaFragmentPath );
+ if( xInStrm.is() )
+ {
+ StorageRef xPrjStrg = std::make_shared<::oox::ole::OleStorage>( getBaseFilter().getComponentContext(), xInStrm, false );
+ setVbaProjectStorage( xPrjStrg );
+ getBaseFilter().getVbaProject().readVbaModules( *xPrjStrg );
+ }
+ }
+
+ // lock the model to prevent broadcasting, speeds up load a lot
+ getScDocument().InitDrawLayer();
+ auto pModel = getScDocument().GetDrawLayer();
+ bool bWasLocked = pModel->isLocked();
+ pModel->setLock(true);
+
+ // load all worksheets
+ importSheetFragments(*this, aSheetFragments);
+
+ if (pTheme && !pTheme->GetName().isEmpty())
+ {
+ pModel->setTheme(pTheme);
+ }
+
+ // assumes getTables().finalizeImport ( which creates the DatabaseRanges )
+ // has been called already
+ getTables().applyAutoFilters();
+
+ sal_Int16 nActiveSheet = getViewSettings().getActiveCalcSheet();
+ getWorksheets().finalizeImport( nActiveSheet );
+
+ // final conversions, e.g. calculation settings and view settings
+ finalizeWorkbookImport();
+
+ //stop preventing establishment of listeners as is done in
+ //ScDocShell::AfterXMLLoading() for ods
+ getScDocument().SetInsertingFromOtherDoc(false);
+
+ for( WorksheetHelper* pHelper : aHelpers )
+ {
+ pHelper->finalizeDrawingImport();
+ }
+
+ for( auto& [rxSheetGlob, rxFragment] : aSheetFragments )
+ {
+ // delete fragment object and WorkbookGlobals object, will free all allocated sheet buffers
+ rxFragment.clear();
+ rxSheetGlob.reset();
+ }
+
+
+ getDocImport().finalize();
+
+ recalcFormulaCells();
+
+ OUString aRevHeadersPath = getFragmentPathFromFirstType(CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
+ if (!aRevHeadersPath.isEmpty())
+ {
+ std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
+ rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionHeadersFragment(*this, aRevHeadersPath));
+ importOoxFragment(xFragment, *xParser);
+ }
+
+ // attach macros to registered objects now that all objects have been created.
+ getBaseFilter().getVbaProject().attachMacros();
+
+ pModel->setLock(bWasLocked);
+}
+
+namespace {
+
+ScDocShell& getDocShell(const ScDocument& rDoc)
+{
+ return static_cast<ScDocShell&>(*rDoc.GetDocumentShell());
+}
+
+class MessageWithCheck : public weld::MessageDialogController
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
+public:
+ MessageWithCheck(weld::Window *pParent, const OUString& rUIFile, const OUString& rDialogId)
+ : MessageDialogController(pParent, rUIFile, rDialogId, "ask")
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+ {
+ }
+ bool get_active() const { return m_xWarningOnBox->get_active(); }
+ void hide_ask() const { m_xWarningOnBox->set_visible(false); };
+};
+
+}
+
+void WorkbookFragment::recalcFormulaCells()
+{
+ // Recalculate formula cells.
+ ScDocument& rDoc = getScDocument();
+ ScDocShell& rDocSh = getDocShell(rDoc);
+ ScRecalcOptions nRecalcMode =
+ static_cast<ScRecalcOptions>(officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::get());
+ bool bHardRecalc = false;
+ if (nRecalcMode == RECALC_ASK)
+ {
+ if (rDoc.IsUserInteractionEnabled())
+ {
+ // Ask the user if full re-calculation is desired.
+ MessageWithCheck aQueryBox(ScDocShell::GetActiveDialogParent(), "modules/scalc/ui/recalcquerydialog.ui", "RecalcQueryDialog");
+ aQueryBox.set_primary_text(ScResId(STR_QUERY_FORMULA_RECALC_ONLOAD_XLS));
+ aQueryBox.set_default_response(RET_YES);
+
+ if ( officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::isReadOnly() )
+ aQueryBox.hide_ask();
+
+ bHardRecalc = aQueryBox.run() == RET_YES;
+
+ if (aQueryBox.get_active())
+ {
+ // Always perform selected action in the future.
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::set(sal_Int32(0), batch);
+ ScFormulaOptions aOpt = SC_MOD()->GetFormulaOptions();
+ aOpt.SetOOXMLRecalcOptions(bHardRecalc ? RECALC_ALWAYS : RECALC_NEVER);
+ /* XXX is this really supposed to set the ScModule options?
+ * Not the ScDocShell options? */
+ SC_MOD()->SetFormulaOptions(aOpt);
+
+ batch->commit();
+ }
+ }
+ }
+ else if (nRecalcMode == RECALC_ALWAYS)
+ bHardRecalc = true;
+ else if (!hasCalculatedFormulaCells())
+ {
+ // Did not encounter any other formula result than 0.0 (or none / no
+ // formula cells at all, in which case recalculation is almost a no-op)
+ // in a non-known-good-generator document.
+ bHardRecalc = true;
+ }
+
+ if (bHardRecalc)
+ rDocSh.DoHardRecalc();
+ else
+ {
+ getDocImport().broadcastRecalcAfterImport();
+ // Full ScDocument::CalcFormulaTree() of all dirty cells is not
+ // necessary here, the View will recalculate them in the visible area,
+ // or any other method accessing formula cell results.
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void WorkbookFragment::importExternalReference( const AttributeList& rAttribs )
+{
+ if( ExternalLink* pExtLink = getExternalLinks().importExternalReference( rAttribs ).get() )
+ importExternalLinkFragment( *pExtLink );
+}
+
+void WorkbookFragment::importDefinedName( const AttributeList& rAttribs )
+{
+ mxCurrName = getDefinedNames().importDefinedName( rAttribs );
+}
+
+void WorkbookFragment::importPivotCache( const AttributeList& rAttribs )
+{
+ sal_Int32 nCacheId = rAttribs.getInteger( XML_cacheId, -1 );
+ OUString aRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ importPivotCacheDefFragment( aRelId, nCacheId );
+}
+
+void WorkbookFragment::importExternalRef( SequenceInputStream& rStrm )
+{
+ if( ExternalLink* pExtLink = getExternalLinks().importExternalRef( rStrm ).get() )
+ importExternalLinkFragment( *pExtLink );
+}
+
+void WorkbookFragment::importPivotCache( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCacheId = rStrm.readInt32();
+ OUString aRelId = BiffHelper::readString( rStrm );
+ importPivotCacheDefFragment( aRelId, nCacheId );
+}
+
+void WorkbookFragment::importExternalLinkFragment( ExternalLink& rExtLink )
+{
+ OUString aFragmentPath = getFragmentPathFromRelId( rExtLink.getRelId() );
+ if( !aFragmentPath.isEmpty() )
+ importOoxFragment( new ExternalLinkFragment( *this, aFragmentPath, rExtLink ) );
+}
+
+void WorkbookFragment::importPivotCacheDefFragment( const OUString& rRelId, sal_Int32 nCacheId )
+{
+ // pivot caches will be imported on demand, here we just store the fragment path in the buffer
+ getPivotCaches().registerPivotCacheFragment( nCacheId, getFragmentPathFromRelId( rRelId ) );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbookhelper.cxx b/sc/source/filter/oox/workbookhelper.cxx
new file mode 100644
index 0000000000..855c7dab40
--- /dev/null
+++ b/sc/source/filter/oox/workbookhelper.cxx
@@ -0,0 +1,1063 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <workbookhelper.hxx>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/sheet/XDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/any.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <oox/helper/progressbar.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/ole/vbaproject.hxx>
+#include <oox/token/properties.hxx>
+#include <addressconverter.hxx>
+#include <connectionsbuffer.hxx>
+#include <defnamesbuffer.hxx>
+#include <excelchartconverter.hxx>
+#include <excelfilter.hxx>
+#include <externallinkbuffer.hxx>
+#include <formulaparser.hxx>
+#include <pagesettings.hxx>
+#include <pivotcachebuffer.hxx>
+#include <pivottablebuffer.hxx>
+#include <scenariobuffer.hxx>
+#include <sharedstringsbuffer.hxx>
+#include <stylesbuffer.hxx>
+#include <tablebuffer.hxx>
+#include <themebuffer.hxx>
+#include <unitconverter.hxx>
+#include <viewsettings.hxx>
+#include <workbooksettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <docuno.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <dbdata.hxx>
+#include <datauno.hxx>
+#include <globalnames.hxx>
+#include <documentimport.hxx>
+#include <drwlayer.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <formulabuffer.hxx>
+#include <editutil.hxx>
+#include <editeng/editstat.hxx>
+#include <unotools/charclass.hxx>
+#include <o3tl/string_view.hxx>
+#include <ViewSettingsSequenceDefines.hxx>
+
+#include <memory>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+using ::oox::core::FilterBase;
+using ::oox::core::FragmentHandler;
+using ::oox::core::XmlFilterBase;
+
+bool IgnoreCaseCompare::operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
+{
+ // TODO: compare with collator
+ return o3tl::compareToIgnoreAsciiCase(rName1, rName2 ) < 0;
+}
+
+class WorkbookGlobals
+{
+public:
+ // noncopyable ------------------------------------------------------------
+
+ WorkbookGlobals(const WorkbookGlobals&) = delete;
+ const WorkbookGlobals& operator=(const WorkbookGlobals&) = delete;
+
+ explicit WorkbookGlobals( ExcelFilter& rFilter );
+ ~WorkbookGlobals();
+
+ /** Returns true, if this helper refers to a valid document. */
+ bool isValid() const { return mxDoc.is(); }
+
+ // filter -----------------------------------------------------------------
+
+ /** Returns the base filter object (base class of all filters). */
+ FilterBase& getBaseFilter() const { return mrBaseFilter; }
+ /** Returns the filter progress bar. */
+ SegmentProgressBar& getProgressBar() const { return *mxProgressBar; }
+ /** Returns the VBA project storage. */
+ const StorageRef& getVbaProjectStorage() const { return mxVbaPrjStrg; }
+ /** Returns the index of the current Calc sheet, if filter currently processes a sheet. */
+ sal_Int16 getCurrentSheetIndex() const { return mnCurrSheet; }
+ /** Returns true when reading a file generated by a known good generator. */
+ bool isGeneratorKnownGood() const { return mbGeneratorKnownGood; }
+ /** Returns true if any formula cell appears to have a calculated value
+ not 0.0 nor "", or when reading a known good generator's file. */
+ bool hasCalculatedFormulaCells() const { return mbHasCalculatedFormulaCells; }
+ void setCalculatedFormulaCells() { mbHasCalculatedFormulaCells = true; }
+
+ /** Sets the VBA project storage used to import VBA source code and forms. */
+ void setVbaProjectStorage( const StorageRef& rxVbaPrjStrg ) { mxVbaPrjStrg = rxVbaPrjStrg; }
+ /** Sets the index of the current Calc sheet, if filter currently processes a sheet. */
+ void setCurrentSheetIndex( SCTAB nSheet ) { mnCurrSheet = nSheet; }
+
+ // document model ---------------------------------------------------------
+
+ ScEditEngineDefaulter& getEditEngine() const
+ {
+ return *mxEditEngine;
+ }
+
+ ScDocument& getScDocument() { return *mpDoc; }
+
+ ScDocumentImport& getDocImport();
+
+ /** Returns a reference to the source/target spreadsheet document model. */
+ const rtl::Reference< ScModelObj >& getDocument() const { return mxDoc; }
+ /** Returns the cell or page styles container from the Calc document. */
+ Reference< XNameContainer > getStyleFamily( bool bPageStyles ) const;
+ /** Returns the specified cell or page style from the Calc document. */
+ Reference< XStyle > getStyleObject( const OUString& rStyleName, bool bPageStyle ) const;
+ /** Creates and returns a defined name on-the-fly in the Calc document. */
+ WorkbookHelper::RangeDataRet createNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags);
+ /** Creates and returns a defined name on the-fly in the correct Calc sheet. */
+ WorkbookHelper::RangeDataRet createLocalNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab);
+ /** Creates and returns a database range on-the-fly in the Calc document. */
+ Reference< XDatabaseRange > createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr );
+ /** Creates and returns an unnamed database range on-the-fly in the Calc document. */
+ Reference< XDatabaseRange > createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr );
+ /** Finds the (already existing) database range of the given formula token index. */
+ ScDBData* findDatabaseRangeByIndex( sal_uInt16 nIndex );
+ /** Creates and returns a com.sun.star.style.Style object for cells or pages. */
+ Reference< XStyle > createStyleObject( OUString& orStyleName, bool bPageStyle );
+ /** Helper to switch chart data table - specifically for xlsx imports */
+ void useInternalChartDataTable( bool bInternal );
+
+ // buffers ----------------------------------------------------------------
+
+ FormulaBuffer& getFormulaBuffer() const { return *mxFormulaBuffer; }
+ /** Returns the global workbook settings object. */
+ WorkbookSettings& getWorkbookSettings() const { return *mxWorkbookSettings; }
+ /** Returns the workbook and sheet view settings object. */
+ ViewSettings& getViewSettings() const { return *mxViewSettings; }
+ /** Returns the worksheet buffer containing sheet names and properties. */
+ WorksheetBuffer& getWorksheets() const { return *mxWorksheets; }
+ /** Returns the office theme object read from the theme substorage. */
+ ThemeBuffer& getTheme() const { return *mxTheme; }
+ /** Returns all cell formatting objects read from the styles substream. */
+ StylesBuffer& getStyles() const { return *mxStyles; }
+ /** Returns the shared strings read from the shared strings substream. */
+ SharedStringsBuffer& getSharedStrings() const { return *mxSharedStrings; }
+ /** Returns the external links read from the external links substream. */
+ ExternalLinkBuffer& getExternalLinks() const { return *mxExtLinks; }
+ /** Returns the defined names read from the workbook globals. */
+ DefinedNamesBuffer& getDefinedNames() const { return *mxDefNames; }
+ /** Returns the tables collection (equivalent to Calc's database ranges). */
+ TableBuffer& getTables() const { return *mxTables; }
+ /** Returns the scenarios collection. */
+ ScenarioBuffer& getScenarios() const { return *mxScenarios; }
+ /** Returns the collection of external data connections. */
+ ConnectionsBuffer& getConnections() const { return *mxConnections; }
+ /** Returns the collection of pivot caches. */
+ PivotCacheBuffer& getPivotCaches() const { return *mxPivotCaches; }
+ /** Returns the collection of pivot tables. */
+ PivotTableBuffer& getPivotTables() { return *mxPivotTables; }
+ /** Shared cache of Font Classifications to avoid repeated lookups */
+ FontClassificationMap& getFontClassificationCache() { return maFontClassificationMap; }
+
+ // converters -------------------------------------------------------------
+
+ /** Returns a shared import formula parser. */
+ FormulaParser& getFormulaParser() const { return *mxFmlaParser; }
+ /** Returns an unshared import formula parser. */
+ FormulaParser* createFormulaParser() { return new FormulaParser(*this); }
+ /** Returns the measurement unit converter. */
+ UnitConverter& getUnitConverter() const { return *mxUnitConverter; }
+ /** Returns the converter for string to cell address/range conversion. */
+ AddressConverter& getAddressConverter() const { return *mxAddrConverter; }
+ /** Returns the chart object converter. */
+ oox::drawingml::chart::ChartConverter* getChartConverter() const { return mxChartConverter.get(); }
+ /** Returns the page/print settings converter. */
+ PageSettingsConverter& getPageSettingsConverter() const { return *mxPageSettConverter; }
+
+ // OOXML/BIFF12 specific --------------------------------------------------
+
+ /** Returns the base OOXML/BIFF12 filter object. */
+ XmlFilterBase& getOoxFilter() const { return *mpOoxFilter; }
+
+ // BIFF2-BIFF8 specific ---------------------------------------------------
+
+ /** Returns the text encoding used to import/export byte strings. */
+ rtl_TextEncoding getTextEncoding() const { return meTextEnc; }
+
+private:
+ /** Initializes some basic members and sets needed document properties. */
+ void initialize();
+ /** Finalizes the filter process (sets some needed document properties). */
+ void finalize();
+
+ void ImplDestroy();
+
+private:
+ typedef ::std::unique_ptr< ScEditEngineDefaulter > EditEngineDefaulterPtr;
+ typedef ::std::unique_ptr< FormulaBuffer > FormulaBufferPtr;
+ typedef ::std::unique_ptr< SegmentProgressBar > ProgressBarPtr;
+ typedef ::std::unique_ptr< WorkbookSettings > WorkbookSettPtr;
+ typedef ::std::unique_ptr< ViewSettings > ViewSettingsPtr;
+ typedef ::std::unique_ptr< WorksheetBuffer > WorksheetBfrPtr;
+ typedef ::std::shared_ptr< ThemeBuffer > ThemeBfrRef;
+ typedef ::std::unique_ptr< StylesBuffer > StylesBfrPtr;
+ typedef ::std::unique_ptr< SharedStringsBuffer > SharedStrBfrPtr;
+ typedef ::std::unique_ptr< ExternalLinkBuffer > ExtLinkBfrPtr;
+ typedef ::std::unique_ptr< DefinedNamesBuffer > DefNamesBfrPtr;
+ typedef ::std::unique_ptr< TableBuffer > TableBfrPtr;
+ typedef ::std::unique_ptr< ScenarioBuffer > ScenarioBfrPtr;
+ typedef ::std::unique_ptr< ConnectionsBuffer > ConnectionsBfrPtr;
+ typedef ::std::unique_ptr< PivotCacheBuffer > PivotCacheBfrPtr;
+ typedef ::std::unique_ptr< PivotTableBuffer > PivotTableBfrPtr;
+ typedef ::std::unique_ptr< FormulaParser > FormulaParserPtr;
+ typedef ::std::unique_ptr< UnitConverter > UnitConvPtr;
+ typedef ::std::unique_ptr< AddressConverter > AddressConvPtr;
+ typedef ::std::unique_ptr< oox::drawingml::chart::ChartConverter > ExcelChartConvPtr;
+ typedef ::std::unique_ptr< PageSettingsConverter > PageSettConvPtr;
+
+ OUString maCellStyles; /// Style family name for cell styles.
+ OUString maPageStyles; /// Style family name for page styles.
+ OUString maCellStyleServ; /// Service name for a cell style.
+ OUString maPageStyleServ; /// Service name for a page style.
+ rtl::Reference< ScModelObj > mxDoc; /// Document model (XSpreadsheetDocument)
+ FilterBase& mrBaseFilter; /// Base filter object.
+ ExcelFilter& mrExcelFilter; /// Base object for registration of this structure.
+ ProgressBarPtr mxProgressBar; /// The progress bar.
+ StorageRef mxVbaPrjStrg; /// Storage containing the VBA project.
+ sal_Int16 mnCurrSheet; /// Current sheet index in Calc document.
+ bool mbGeneratorKnownGood; /// Whether reading a file generated by Excel or Calc.
+ bool mbHasCalculatedFormulaCells; /// Assumed to have calculated formula cells.
+
+ // buffers
+ FormulaBufferPtr mxFormulaBuffer;
+ WorkbookSettPtr mxWorkbookSettings; /// Global workbook settings.
+ ViewSettingsPtr mxViewSettings; /// Workbook and sheet view settings.
+ WorksheetBfrPtr mxWorksheets; /// Sheet info buffer.
+ ThemeBfrRef mxTheme; /// Formatting theme from theme substream.
+ StylesBfrPtr mxStyles; /// All cell style objects from styles substream.
+ SharedStrBfrPtr mxSharedStrings; /// All strings from shared strings substream.
+ ExtLinkBfrPtr mxExtLinks; /// All external links.
+ DefNamesBfrPtr mxDefNames; /// All defined names.
+ TableBfrPtr mxTables; /// All tables (database ranges).
+ ScenarioBfrPtr mxScenarios; /// All scenarios.
+ ConnectionsBfrPtr mxConnections; /// All external data connections.
+ PivotCacheBfrPtr mxPivotCaches; /// All pivot caches in the document.
+ PivotTableBfrPtr mxPivotTables; /// All pivot tables in the document.
+ FontClassificationMap maFontClassificationMap; /// Shared to avoid repeated lookups
+
+ // converters
+ FormulaParserPtr mxFmlaParser; /// Import formula parser.
+ UnitConvPtr mxUnitConverter; /// General unit converter.
+ AddressConvPtr mxAddrConverter; /// Cell address and cell range address converter.
+ ExcelChartConvPtr mxChartConverter; /// Chart object converter.
+ PageSettConvPtr mxPageSettConverter; /// Page/print settings converter.
+
+ EditEngineDefaulterPtr mxEditEngine;
+
+ // OOXML/BIFF12 specific
+ XmlFilterBase* mpOoxFilter; /// Base OOXML/BIFF12 filter object.
+
+ // BIFF2-BIFF8 specific
+ rtl_TextEncoding meTextEnc; /// BIFF byte string text encoding.
+ ScDocument* mpDoc;
+ ScDocShell* mpDocShell;
+ std::unique_ptr<ScDocumentImport> mxDocImport;
+};
+
+WorkbookGlobals::WorkbookGlobals( ExcelFilter& rFilter ) :
+ mrBaseFilter( rFilter ),
+ mrExcelFilter( rFilter ),
+ mpOoxFilter( &rFilter ),
+ mpDoc(nullptr),
+ mpDocShell(nullptr)
+{
+ // register at the filter, needed for virtual callbacks (even during construction)
+ mrExcelFilter.registerWorkbookGlobals( *this );
+ initialize();
+}
+
+void WorkbookGlobals::ImplDestroy()
+{
+ finalize();
+ mrExcelFilter.unregisterWorkbookGlobals();
+}
+
+WorkbookGlobals::~WorkbookGlobals()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+ScDocumentImport& WorkbookGlobals::getDocImport()
+{
+ return *mxDocImport;
+}
+
+Reference< XNameContainer > WorkbookGlobals::getStyleFamily( bool bPageStyles ) const
+{
+ Reference< XNameContainer > xStylesNC;
+ try
+ {
+ Reference< XNameAccess > xFamiliesNA( mxDoc->getStyleFamilies(), UNO_SET_THROW );
+ xStylesNC.set( xFamiliesNA->getByName( bPageStyles ? maPageStyles : maCellStyles ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xStylesNC.is(), "WorkbookGlobals::getStyleFamily - cannot access style family" );
+ return xStylesNC;
+}
+
+Reference< XStyle > WorkbookGlobals::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const
+{
+ Reference< XStyle > xStyle;
+ try
+ {
+ Reference< XNameContainer > xStylesNC( getStyleFamily( bPageStyle ), UNO_SET_THROW );
+ xStyle.set( xStylesNC->getByName( rStyleName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xStyle.is(), "WorkbookGlobals::getStyleObject - cannot access style object" );
+ return xStyle;
+}
+
+namespace {
+
+WorkbookHelper::RangeDataRet lcl_addNewByName(ScDocument& rDoc, ScRangeName* pNames, const OUString& rName, sal_Int16 nIndex, sal_Int32 nUnoType)
+{
+ bool bDone = false;
+ ScRangeData::Type nNewType = ScRangeData::Type::Name;
+ if ( nUnoType & NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria;
+ if ( nUnoType & NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea;
+ if ( nUnoType & NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader;
+ if ( nUnoType & NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader;
+ if ( nUnoType & NamedRangeFlag::HIDDEN ) nNewType |= ScRangeData::Type::Hidden;
+ ScTokenArray aTokenArray(rDoc);
+ ScRangeData* pNew = new ScRangeData(rDoc, rName, aTokenArray, ScAddress(), nNewType);
+ pNew->GuessPosition();
+ if ( nIndex )
+ pNew->SetIndex( nIndex );
+ // create but not insert hidden FILTER_CRITERIA named ranges to ScRangeName
+ if (((nUnoType & NamedRangeFlag::HIDDEN) == NamedRangeFlag::HIDDEN)
+ && ((nUnoType & NamedRangeFlag::FILTER_CRITERIA) == NamedRangeFlag::FILTER_CRITERIA))
+ {
+ return WorkbookHelper::RangeDataRet(pNew, true);
+ }
+ if ( pNames->insert(pNew) )
+ bDone = true;
+ if (!bDone)
+ {
+ delete pNew;
+ throw RuntimeException();
+ }
+ return WorkbookHelper::RangeDataRet(pNew, false);
+}
+
+OUString findUnusedName( const ScRangeName* pRangeName, const OUString& rSuggestedName )
+{
+ OUString aNewName = rSuggestedName;
+ sal_Int32 nIndex = 0;
+ while(pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(aNewName)))
+ aNewName = rSuggestedName + OUStringChar('_') + OUString::number( nIndex++ );
+
+ return aNewName;
+}
+
+}
+
+WorkbookHelper::RangeDataRet WorkbookGlobals::createNamedRangeObject(
+ OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags)
+{
+ // create the name and insert it into the Calc document
+ WorkbookHelper::RangeDataRet aScRangeData(nullptr, false);
+ if( !orName.isEmpty() )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScRangeName* pNames = rDoc.GetRangeName();
+ // find an unused name
+ orName = findUnusedName( pNames, orName );
+ // create the named range
+ aScRangeData = lcl_addNewByName(rDoc, pNames, orName, nIndex, nNameFlags);
+ }
+ return aScRangeData;
+}
+
+WorkbookHelper::RangeDataRet WorkbookGlobals::createLocalNamedRangeObject(
+ OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab)
+{
+ // create the name and insert it into the Calc document
+ WorkbookHelper::RangeDataRet aScRangeData(nullptr, false);
+ if( !orName.isEmpty() )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScRangeName* pNames = rDoc.GetRangeName( nTab );
+ if(!pNames)
+ throw RuntimeException("invalid sheet index used");
+ // find an unused name
+ orName = findUnusedName( pNames, orName );
+ // create the named range
+ aScRangeData = lcl_addNewByName(rDoc, pNames, orName, nIndex, nNameFlags);
+ }
+ return aScRangeData;
+}
+
+Reference< XDatabaseRange > WorkbookGlobals::createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr )
+{
+ // validate cell range
+ ScRange aDestRange = rRangeAddr;
+ bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true );
+
+ // create database range and insert it into the Calc document
+ Reference< XDatabaseRange > xDatabaseRange;
+ if( bValidRange && !orName.isEmpty() ) try
+ {
+ // find an unused name
+ PropertySet aDocProps(( Reference< css::beans::XPropertySet >(mxDoc) ));
+ Reference< XDatabaseRanges > xDatabaseRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY_THROW );
+ orName = ContainerHelper::getUnusedName( xDatabaseRanges, orName, '_' );
+ // create the database range
+ CellRangeAddress aApiRange( aDestRange.aStart.Tab(), aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ aDestRange.aEnd.Col(), aDestRange.aEnd.Row() );
+ xDatabaseRanges->addNewByName( orName, aApiRange );
+ xDatabaseRange.set( xDatabaseRanges->getByName( orName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xDatabaseRange.is(), "WorkbookGlobals::createDatabaseRangeObject - cannot create database range" );
+ return xDatabaseRange;
+}
+
+Reference< XDatabaseRange > WorkbookGlobals::createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr )
+{
+ // validate cell range
+ ScRange aDestRange = rRangeAddr;
+ bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true );
+
+ // create database range and insert it into the Calc document
+ Reference< XDatabaseRange > xDatabaseRange;
+ if( bValidRange ) try
+ {
+ ScDocument& rDoc = getScDocument();
+ if( rDoc.GetTableCount() <= aDestRange.aStart.Tab() )
+ throw css::lang::IndexOutOfBoundsException();
+ std::unique_ptr<ScDBData> pNewDBData(new ScDBData( STR_DB_LOCAL_NONAME, aDestRange.aStart.Tab(),
+ aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ aDestRange.aEnd.Col(), aDestRange.aEnd.Row() ));
+ rDoc.SetAnonymousDBData( aDestRange.aStart.Tab() , std::move(pNewDBData) );
+ ScDocShell* pDocSh = rDoc.GetDocumentShell();
+ xDatabaseRange.set(new ScDatabaseRangeObj(pDocSh, aDestRange.aStart.Tab()));
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xDatabaseRange.is(), "WorkbookData::createDatabaseRangeObject - cannot create database range" );
+ return xDatabaseRange;
+}
+
+ScDBData* WorkbookGlobals::findDatabaseRangeByIndex( sal_uInt16 nIndex )
+{
+ ScDBCollection* pDBCollection = getScDocument().GetDBCollection();
+ if (!pDBCollection)
+ return nullptr;
+ return pDBCollection->getNamedDBs().findByIndex( nIndex );
+}
+
+Reference< XStyle > WorkbookGlobals::createStyleObject( OUString& orStyleName, bool bPageStyle )
+{
+ Reference< XStyle > xStyle;
+ try
+ {
+ Reference< XNameContainer > xStylesNC( getStyleFamily( bPageStyle ), UNO_SET_THROW );
+ xStyle.set( mrBaseFilter.getModelFactory()->createInstance( bPageStyle ? maPageStyleServ : maCellStyleServ ), UNO_QUERY_THROW );
+ orStyleName = ContainerHelper::insertByUnusedName( xStylesNC, orStyleName, ' ', Any( xStyle ) );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xStyle.is(), "WorkbookGlobals::createStyleObject - cannot create style" );
+ return xStyle;
+}
+
+void WorkbookGlobals::useInternalChartDataTable( bool bInternal )
+{
+ if( bInternal )
+ mxChartConverter.reset( new oox::drawingml::chart::ChartConverter() );
+ else
+ mxChartConverter.reset( new ExcelChartConverter( *this ) );
+}
+
+// BIFF specific --------------------------------------------------------------
+
+// private --------------------------------------------------------------------
+
+void WorkbookGlobals::initialize()
+{
+ maCellStyles = "CellStyles";
+ maPageStyles = "PageStyles";
+ maCellStyleServ = "com.sun.star.style.CellStyle";
+ maPageStyleServ = "com.sun.star.style.PageStyle";
+ mnCurrSheet = -1;
+ mbGeneratorKnownGood = false;
+ mbHasCalculatedFormulaCells = false;
+ meTextEnc = osl_getThreadTextEncoding();
+
+ // the spreadsheet document
+ mxDoc = &dynamic_cast<ScModelObj&>(*mrBaseFilter.getModel());
+
+ mpDocShell = static_cast<ScDocShell*>(mxDoc->GetEmbeddedObject());
+ if (mpDocShell)
+ mpDoc = &mpDocShell->GetDocument();
+
+ if (!mpDoc)
+ throw RuntimeException("Workbookhelper::getScDocument(): Failed to access ScDocument from model");
+
+ Reference< XDocumentProperties > xDocProps = mxDoc->getDocumentProperties();
+ const OUString aGenerator( xDocProps->getGenerator());
+
+ if (aGenerator.startsWithIgnoreAsciiCase("Microsoft"))
+ {
+ mbGeneratorKnownGood = true;
+ ScCalcConfig aCalcConfig = mpDoc->GetCalcConfig();
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 ) ;
+ mpDoc->SetCalcConfig(aCalcConfig);
+ }
+ else if (aGenerator.startsWithIgnoreAsciiCase("LibreOffice"))
+ {
+ mbGeneratorKnownGood = true;
+ }
+ mbHasCalculatedFormulaCells = mbGeneratorKnownGood;
+
+ mxDocImport.reset(new ScDocumentImport(*mpDoc));
+
+ mxFormulaBuffer.reset( new FormulaBuffer( *this ) );
+ mxWorkbookSettings.reset( new WorkbookSettings( *this ) );
+ mxViewSettings.reset( new ViewSettings( *this ) );
+ mxWorksheets.reset( new WorksheetBuffer( *this ) );
+ mxTheme = std::make_shared<ThemeBuffer>( *this );
+ mxStyles.reset( new StylesBuffer( *this ) );
+ mxSharedStrings.reset( new SharedStringsBuffer( *this ) );
+ mxExtLinks.reset( new ExternalLinkBuffer( *this ) );
+ mxDefNames.reset( new DefinedNamesBuffer( *this ) );
+ mxTables.reset( new TableBuffer( *this ) );
+ mxScenarios.reset( new ScenarioBuffer( *this ) );
+ mxConnections.reset( new ConnectionsBuffer( *this ) );
+ mxPivotCaches.reset( new PivotCacheBuffer( *this ) );
+ mxPivotTables.reset( new PivotTableBuffer( *this ) );
+
+ mxUnitConverter.reset( new UnitConverter( *this ) );
+ mxAddrConverter.reset( new AddressConverter( *this ) );
+ mxChartConverter.reset( new ExcelChartConverter( *this ) );
+ mxPageSettConverter.reset( new PageSettingsConverter( *this ) );
+
+ // initialise edit engine
+ ScDocument& rDoc = getScDocument();
+ mxEditEngine.reset( new ScEditEngineDefaulter( rDoc.GetEnginePool() ) );
+ mxEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mxEditEngine->SetEditTextObjectPool( rDoc.GetEditPool() );
+ mxEditEngine->SetUpdateLayout( false );
+ mxEditEngine->EnableUndo( false );
+ mxEditEngine->SetControlWord( mxEditEngine->GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+
+ // set some document properties needed during import
+ if( mrBaseFilter.isImportFilter() )
+ {
+ // enable editing read-only documents (e.g. from read-only files)
+ mpDoc->EnableChangeReadOnly(true);
+ // #i76026# disable Undo while loading the document
+ mpDoc->EnableUndo(false);
+ // #i79826# disable calculating automatic row height while loading the document
+ mpDoc->LockAdjustHeight();
+ // disable automatic update of linked sheets and DDE links
+ mpDoc->EnableExecuteLink(false);
+
+ mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), ScResId(STR_LOAD_DOC) ) );
+ mxFmlaParser.reset( createFormulaParser() );
+
+ //prevent unnecessary broadcasts and "half way listeners" as
+ //is done in ScDocShell::BeforeXMLLoading() for ods
+ mpDoc->SetInsertingFromOtherDoc(true);
+ }
+ else if( mrBaseFilter.isExportFilter() )
+ {
+ mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), ScResId(STR_SAVE_DOC) ) );
+ }
+}
+
+void WorkbookGlobals::finalize()
+{
+ // set some document properties needed after import
+ if( !mrBaseFilter.isImportFilter() )
+ return;
+
+ // #i74668# do not insert default sheets
+ mpDocShell->SetEmpty(false);
+ // enable automatic update of linked sheets and DDE links
+ mpDoc->EnableExecuteLink(true);
+ // #i79826# enable updating automatic row height after loading the document
+ mpDoc->UnlockAdjustHeight();
+ mpDocShell->UpdateAllRowHeights(/*bOnlyUsedRows=*/true);
+
+ // #i76026# enable Undo after loading the document
+ mpDoc->EnableUndo(true);
+
+ // disable editing read-only documents (e.g. from read-only files)
+ mpDoc->EnableChangeReadOnly(false);
+ // #111099# open forms in alive mode (has no effect, if no controls in document)
+ ScDrawLayer* pModel = mpDoc->GetDrawLayer();
+ if (pModel)
+ pModel->SetOpenInDesignMode(false);
+}
+
+
+WorkbookHelper::~WorkbookHelper()
+{
+}
+
+/*static*/ WorkbookGlobalsRef WorkbookHelper::constructGlobals( ExcelFilter& rFilter )
+{
+ WorkbookGlobalsRef xBookGlob = std::make_shared<WorkbookGlobals>( rFilter );
+ if( !xBookGlob->isValid() )
+ xBookGlob.reset();
+ return xBookGlob;
+}
+
+// filter ---------------------------------------------------------------------
+
+FilterBase& WorkbookHelper::getBaseFilter() const
+{
+ return mrBookGlob.getBaseFilter();
+}
+
+SegmentProgressBar& WorkbookHelper::getProgressBar() const
+{
+ return mrBookGlob.getProgressBar();
+}
+
+sal_Int16 WorkbookHelper::getCurrentSheetIndex() const
+{
+ return mrBookGlob.getCurrentSheetIndex();
+}
+
+bool WorkbookHelper::isGeneratorKnownGood() const
+{
+ return mrBookGlob.isGeneratorKnownGood();
+}
+
+bool WorkbookHelper::hasCalculatedFormulaCells() const
+{
+ return mrBookGlob.hasCalculatedFormulaCells();
+}
+
+void WorkbookHelper::setCalculatedFormulaCells()
+{
+ return mrBookGlob.setCalculatedFormulaCells();
+}
+
+void WorkbookHelper::setVbaProjectStorage( const StorageRef& rxVbaPrjStrg )
+{
+ mrBookGlob.setVbaProjectStorage( rxVbaPrjStrg );
+}
+
+void WorkbookHelper::setCurrentSheetIndex( SCTAB nSheet )
+{
+ mrBookGlob.setCurrentSheetIndex( nSheet );
+}
+
+void WorkbookHelper::finalizeWorkbookImport()
+{
+ // workbook settings, document and sheet view settings
+ mrBookGlob.getWorkbookSettings().finalizeImport();
+ mrBookGlob.getViewSettings().finalizeImport();
+
+ // Import the VBA project (after finalizing workbook settings which
+ // contains the workbook code name). Do it before processing formulas in
+ // order to correctly resolve VBA custom function names.
+ StorageRef xVbaPrjStrg = mrBookGlob.getVbaProjectStorage();
+ if( xVbaPrjStrg && xVbaPrjStrg->isStorage() )
+ getBaseFilter().getVbaProject().importModulesAndForms( *xVbaPrjStrg, getBaseFilter().getGraphicHelper() );
+
+ // need to import formulas before scenarios
+ mrBookGlob.getFormulaBuffer().finalizeImport();
+
+ // Insert all pivot tables. Must be done after loading all sheets and
+ // formulas, because data pilots expect existing source data on
+ // creation.
+ getPivotTables().finalizeImport();
+
+ /* Insert scenarios after all sheet processing is done, because new hidden
+ sheets are created for scenarios which would confuse code that relies
+ on certain sheet indexes. Must be done after pivot tables too. */
+ mrBookGlob.getScenarios().finalizeImport();
+
+ /* Set 'Default' page style to automatic page numbering (default is manual
+ number 1). Otherwise hidden sheets (e.g. for scenarios) which have
+ 'Default' page style will break automatic page numbering for following
+ sheets. Automatic numbering is set by passing the value 0. */
+ PropertySet aDefPageStyle( getStyleObject( "Default", true ) );
+ aDefPageStyle.setProperty< sal_Int16 >( PROP_FirstPageNumber, 0 );
+
+ // Has any string ref syntax been imported?
+ // If not, we need to take action
+ ScCalcConfig aCalcConfig = getScDocument().GetCalcConfig();
+
+ if ( !aCalcConfig.mbHasStringRefSyntax )
+ {
+ aCalcConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_A1_XL_A1;
+ getScDocument().SetCalcConfig(aCalcConfig);
+ }
+
+ // set selected sheet and positionleft/positiontop for OLE objects
+ rtl::Reference<ScModelObj> xViewDataSupplier(getDocument());
+ if (!xViewDataSupplier.is())
+ return;
+
+ Reference<XIndexAccess> xIndexAccess(xViewDataSupplier->getViewData());
+ if (!(xIndexAccess.is() && xIndexAccess->getCount() > 0))
+ return;
+
+ Sequence< PropertyValue > aSeq;
+ if (!(xIndexAccess->getByIndex(0) >>= aSeq))
+ return;
+
+ OUString sTabName;
+ Reference< XNameAccess > xSheetsNC;
+ for (const auto& rProp : std::as_const(aSeq))
+ {
+ OUString sName(rProp.Name);
+ if (sName == SC_ACTIVETABLE)
+ {
+ if(rProp.Value >>= sTabName)
+ {
+ SCTAB nTab(0);
+ if (getScDocument().GetTable(sTabName, nTab))
+ getScDocument().SetVisibleTab(nTab);
+ }
+ }
+ else if (sName == SC_TABLES)
+ {
+ rProp.Value >>= xSheetsNC;
+ }
+ }
+ if (!(xSheetsNC.is() && xSheetsNC->hasByName(sTabName)))
+ return;
+
+ Sequence<PropertyValue> aProperties;
+ Any aAny = xSheetsNC->getByName(sTabName);
+ if ( !(aAny >>= aProperties) )
+ return;
+
+ for (const auto& rProp : std::as_const(aProperties))
+ {
+ OUString sName(rProp.Name);
+ if (sName == SC_POSITIONLEFT)
+ {
+ SCCOL nPosLeft = *o3tl::doAccess<SCCOL>(rProp.Value);
+ getScDocument().SetPosLeft(nPosLeft);
+ }
+ else if (sName == SC_POSITIONTOP)
+ {
+ SCROW nPosTop = *o3tl::doAccess<SCROW>(rProp.Value);
+ getScDocument().SetPosTop(nPosTop);
+ }
+ }
+}
+
+// document model -------------------------------------------------------------
+
+ScDocument& WorkbookHelper::getScDocument()
+{
+ return mrBookGlob.getScDocument();
+}
+
+const ScDocument& WorkbookHelper::getScDocument() const
+{
+ return mrBookGlob.getScDocument();
+}
+
+ScDocumentImport& WorkbookHelper::getDocImport()
+{
+ return mrBookGlob.getDocImport();
+}
+
+const ScDocumentImport& WorkbookHelper::getDocImport() const
+{
+ return mrBookGlob.getDocImport();
+}
+
+ScEditEngineDefaulter& WorkbookHelper::getEditEngine() const
+{
+ return mrBookGlob.getEditEngine();
+}
+
+const rtl::Reference< ScModelObj > & WorkbookHelper::getDocument() const
+{
+ return mrBookGlob.getDocument();
+}
+
+Reference< XSpreadsheet > WorkbookHelper::getSheetFromDoc( sal_Int32 nSheet ) const
+{
+ Reference< XSpreadsheet > xSheet;
+ try
+ {
+ Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ xSheet.set( xSheetsIA->getByIndex( nSheet ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ }
+ return xSheet;
+}
+
+Reference< XSpreadsheet > WorkbookHelper::getSheetFromDoc( const OUString& rSheet ) const
+{
+ Reference< XSpreadsheet > xSheet;
+ try
+ {
+ Reference< XNameAccess > xSheetsNA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ xSheet.set( xSheetsNA->getByName( rSheet ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xSheet;
+}
+
+Reference< XCellRange > WorkbookHelper::getCellRangeFromDoc( const ScRange& rRange ) const
+{
+ Reference< XCellRange > xRange;
+ try
+ {
+ Reference< XSpreadsheet > xSheet( getSheetFromDoc( rRange.aStart.Tab() ), UNO_SET_THROW );
+ xRange = xSheet->getCellRangeByPosition( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRange;
+}
+
+Reference< XNameContainer > WorkbookHelper::getCellStyleFamily() const
+{
+ return mrBookGlob.getStyleFamily( false/*bPageStyles*/ );
+}
+
+Reference< XStyle > WorkbookHelper::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const
+{
+ return mrBookGlob.getStyleObject( rStyleName, bPageStyle );
+}
+
+WorkbookHelper::RangeDataRet WorkbookHelper::createNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags) const
+{
+ return mrBookGlob.createNamedRangeObject(orName, nIndex, nNameFlags);
+}
+
+WorkbookHelper::RangeDataRet WorkbookHelper::createLocalNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab) const
+{
+ return mrBookGlob.createLocalNamedRangeObject(orName, nIndex, nNameFlags, nTab);
+}
+
+Reference< XDatabaseRange > WorkbookHelper::createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr ) const
+{
+ return mrBookGlob.createDatabaseRangeObject( orName, rRangeAddr );
+}
+
+Reference< XDatabaseRange > WorkbookHelper::createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr ) const
+{
+ return mrBookGlob.createUnnamedDatabaseRangeObject( rRangeAddr );
+}
+
+ScDBData* WorkbookHelper::findDatabaseRangeByIndex( sal_uInt16 nIndex ) const
+{
+ return mrBookGlob.findDatabaseRangeByIndex( nIndex );
+}
+
+Reference< XStyle > WorkbookHelper::createStyleObject( OUString& orStyleName, bool bPageStyle ) const
+{
+ return mrBookGlob.createStyleObject( orStyleName, bPageStyle );
+}
+
+// buffers --------------------------------------------------------------------
+
+FormulaBuffer& WorkbookHelper::getFormulaBuffer() const
+{
+ return mrBookGlob.getFormulaBuffer();
+}
+
+WorkbookSettings& WorkbookHelper::getWorkbookSettings() const
+{
+ return mrBookGlob.getWorkbookSettings();
+}
+
+ViewSettings& WorkbookHelper::getViewSettings() const
+{
+ return mrBookGlob.getViewSettings();
+}
+
+WorksheetBuffer& WorkbookHelper::getWorksheets() const
+{
+ return mrBookGlob.getWorksheets();
+}
+
+ThemeBuffer& WorkbookHelper::getTheme() const
+{
+ return mrBookGlob.getTheme();
+}
+
+StylesBuffer& WorkbookHelper::getStyles() const
+{
+ return mrBookGlob.getStyles();
+}
+
+SharedStringsBuffer& WorkbookHelper::getSharedStrings() const
+{
+ return mrBookGlob.getSharedStrings();
+}
+
+ExternalLinkBuffer& WorkbookHelper::getExternalLinks() const
+{
+ return mrBookGlob.getExternalLinks();
+}
+
+DefinedNamesBuffer& WorkbookHelper::getDefinedNames() const
+{
+ return mrBookGlob.getDefinedNames();
+}
+
+TableBuffer& WorkbookHelper::getTables() const
+{
+ return mrBookGlob.getTables();
+}
+
+ScenarioBuffer& WorkbookHelper::getScenarios() const
+{
+ return mrBookGlob.getScenarios();
+}
+
+ConnectionsBuffer& WorkbookHelper::getConnections() const
+{
+ return mrBookGlob.getConnections();
+}
+
+PivotCacheBuffer& WorkbookHelper::getPivotCaches() const
+{
+ return mrBookGlob.getPivotCaches();
+}
+
+PivotTableBuffer& WorkbookHelper::getPivotTables() const
+{
+ return mrBookGlob.getPivotTables();
+}
+
+FontClassificationMap& WorkbookHelper::getFontClassificationCache() const
+{
+ return mrBookGlob.getFontClassificationCache();
+}
+
+// converters -----------------------------------------------------------------
+
+FormulaParser& WorkbookHelper::getFormulaParser() const
+{
+ return mrBookGlob.getFormulaParser();
+}
+
+FormulaParser* WorkbookHelper::createFormulaParser() const
+{
+ return mrBookGlob.createFormulaParser();
+}
+
+UnitConverter& WorkbookHelper::getUnitConverter() const
+{
+ return mrBookGlob.getUnitConverter();
+}
+
+AddressConverter& WorkbookHelper::getAddressConverter() const
+{
+ return mrBookGlob.getAddressConverter();
+}
+
+oox::drawingml::chart::ChartConverter* WorkbookHelper::getChartConverter() const
+{
+ return mrBookGlob.getChartConverter();
+}
+
+void WorkbookHelper::useInternalChartDataTable( bool bInternal )
+{
+ mrBookGlob.useInternalChartDataTable( bInternal );
+}
+
+PageSettingsConverter& WorkbookHelper::getPageSettingsConverter() const
+{
+ return mrBookGlob.getPageSettingsConverter();
+}
+
+// OOXML/BIFF12 specific ------------------------------------------------------
+
+XmlFilterBase& WorkbookHelper::getOoxFilter() const
+{
+ return mrBookGlob.getOoxFilter();
+}
+
+bool WorkbookHelper::importOoxFragment( const rtl::Reference<FragmentHandler>& rxHandler )
+{
+ return getOoxFilter().importFragment( rxHandler );
+}
+
+bool WorkbookHelper::importOoxFragment( const rtl::Reference<FragmentHandler>& rxHandler, oox::core::FastParser& rParser )
+{
+ return getOoxFilter().importFragment(rxHandler, rParser);
+}
+
+// BIFF specific --------------------------------------------------------------
+
+rtl_TextEncoding WorkbookHelper::getTextEncoding() const
+{
+ return mrBookGlob.getTextEncoding();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbooksettings.cxx b/sc/source/filter/oox/workbooksettings.cxx
new file mode 100644
index 0000000000..655368acf0
--- /dev/null
+++ b/sc/source/filter/oox/workbooksettings.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 <workbooksettings.hxx>
+
+#include <com/sun/star/sheet/XCalculatable.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <oox/core/binarycodec.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <unitconverter.hxx>
+#include <biffhelper.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+namespace {
+
+const sal_uInt32 BIFF12_WORKBOOKPR_DATE1904 = 0x00000001;
+const sal_uInt32 BIFF12_WORKBOOKPR_STRIPEXT = 0x00000080;
+
+const sal_uInt16 BIFF12_CALCPR_A1 = 0x0002;
+const sal_uInt16 BIFF12_CALCPR_ITERATE = 0x0004;
+const sal_uInt16 BIFF12_CALCPR_FULLPRECISION = 0x0008;
+const sal_uInt16 BIFF12_CALCPR_CALCCOMPLETED = 0x0010;
+const sal_uInt16 BIFF12_CALCPR_CALCONSAVE = 0x0020;
+const sal_uInt16 BIFF12_CALCPR_CONCURRENT = 0x0040;
+const sal_uInt16 BIFF12_CALCPR_MANUALPROC = 0x0080;
+
+// no predefined constants for show objects mode
+const sal_Int16 API_SHOWMODE_SHOW = 0; /// Show drawing objects.
+const sal_Int16 API_SHOWMODE_HIDE = 1; /// Hide drawing objects.
+const sal_Int16 API_SHOWMODE_PLACEHOLDER = 2; /// Show placeholders for drawing objects.
+
+} // namespace
+
+FileSharingModel::FileSharingModel() :
+ mnSpinCount( 0 ),
+ mnPasswordHash( 0 ),
+ mbRecommendReadOnly( false )
+{
+}
+
+WorkbookSettingsModel::WorkbookSettingsModel() :
+ mnShowObjectMode( XML_all ),
+ mnUpdateLinksMode( XML_userSet ),
+ mnDefaultThemeVer( -1 ),
+ mbDateMode1904( false ),
+ mbDateCompatibility ( false ),
+ mbSaveExtLinkValues( true )
+{
+}
+
+void WorkbookSettingsModel::setBiffObjectMode( sal_uInt16 nObjMode )
+{
+ static const sal_Int32 spnObjModes[] = { XML_all, XML_placeholders, XML_none };
+ mnShowObjectMode = STATIC_ARRAY_SELECT( spnObjModes, nObjMode, XML_all );
+}
+
+CalcSettingsModel::CalcSettingsModel() :
+ mfIterateDelta( 0.001 ),
+ mnCalcId( -1 ),
+ mnRefMode( XML_A1 ),
+ mnCalcMode( XML_auto ),
+ mnIterateCount( 100 ),
+ mnProcCount( -1 ),
+ mbCalcOnSave( true ),
+ mbCalcCompleted( true ),
+ mbFullPrecision( true ),
+ mbIterate( false ),
+ mbConcurrent( true )
+{
+}
+
+WorkbookSettings::WorkbookSettings( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void WorkbookSettings::importFileSharing( const AttributeList& rAttribs )
+{
+ maFileSharing.maUserName = rAttribs.getXString( XML_userName, OUString() );
+ maFileSharing.maAlgorithmName = rAttribs.getString( XML_algorithmName, OUString());
+ maFileSharing.maHashValue = rAttribs.getString( XML_hashValue, OUString());
+ maFileSharing.maSaltValue = rAttribs.getString( XML_saltValue, OUString());
+ maFileSharing.mnSpinCount = rAttribs.getUnsigned( XML_spinCount, 0);
+ maFileSharing.mnPasswordHash = oox::core::CodecHelper::getPasswordHash( rAttribs, XML_reservationPassword );
+ maFileSharing.mbRecommendReadOnly = rAttribs.getBool( XML_readOnlyRecommended, false );
+}
+
+void WorkbookSettings::importWorkbookPr( const AttributeList& rAttribs )
+{
+ maBookSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
+ maBookSettings.mnShowObjectMode = rAttribs.getToken( XML_showObjects, XML_all );
+ maBookSettings.mnUpdateLinksMode = rAttribs.getToken( XML_updateLinks, XML_userSet );
+ maBookSettings.mnDefaultThemeVer = rAttribs.getInteger( XML_defaultThemeVersion, -1 );
+ maBookSettings.mbSaveExtLinkValues = rAttribs.getBool( XML_saveExternalLinkValues, true );
+ setDateMode( rAttribs.getBool( XML_date1904, false ), rAttribs.getBool( XML_dateCompatibility, true ) );
+}
+
+void WorkbookSettings::importCalcPr( const AttributeList& rAttribs )
+{
+ maCalcSettings.mfIterateDelta = rAttribs.getDouble( XML_iterateDelta, 0.0001 );
+ maCalcSettings.mnCalcId = rAttribs.getInteger( XML_calcId, -1 );
+ maCalcSettings.mnRefMode = rAttribs.getToken( XML_refMode, XML_A1 );
+ maCalcSettings.mnCalcMode = rAttribs.getToken( XML_calcMode, XML_auto );
+ maCalcSettings.mnIterateCount = rAttribs.getInteger( XML_iterateCount, 100 );
+ maCalcSettings.mnProcCount = rAttribs.getInteger( XML_concurrentManualCount, -1 );
+ maCalcSettings.mbCalcOnSave = rAttribs.getBool( XML_calcOnSave, true );
+ maCalcSettings.mbCalcCompleted = rAttribs.getBool( XML_calcCompleted, true );
+ maCalcSettings.mbFullPrecision = rAttribs.getBool( XML_fullPrecision, true );
+ maCalcSettings.mbIterate = rAttribs.getBool( XML_iterate, false );
+ maCalcSettings.mbConcurrent = rAttribs.getBool( XML_concurrentCalc, true );
+}
+
+void WorkbookSettings::importFileSharing( SequenceInputStream& rStrm )
+{
+ maFileSharing.mbRecommendReadOnly = rStrm.readuInt16() != 0;
+ maFileSharing.mnPasswordHash = rStrm.readuInt16();
+ rStrm >> maFileSharing.maUserName;
+}
+
+void WorkbookSettings::importWorkbookPr( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags;
+ nFlags = rStrm.readuInt32();
+ maBookSettings.mnDefaultThemeVer = rStrm.readInt32();
+ rStrm >> maBookSettings.maCodeName;
+ maBookSettings.setBiffObjectMode( extractValue< sal_uInt16 >( nFlags, 13, 2 ) );
+ // set flag means: strip external link values
+ maBookSettings.mbSaveExtLinkValues = !getFlag( nFlags, BIFF12_WORKBOOKPR_STRIPEXT );
+ setDateMode( getFlag( nFlags, BIFF12_WORKBOOKPR_DATE1904 ) );
+}
+
+void WorkbookSettings::importCalcPr( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCalcMode, nProcCount;
+ sal_uInt16 nFlags;
+ maCalcSettings.mnCalcId = rStrm.readInt32();
+ nCalcMode = rStrm.readInt32();
+ maCalcSettings.mnIterateCount = rStrm.readInt32();
+ maCalcSettings.mfIterateDelta = rStrm.readDouble();
+ nProcCount = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+
+ static const sal_Int32 spnCalcModes[] = { XML_manual, XML_auto, XML_autoNoTable };
+ maCalcSettings.mnRefMode = getFlagValue( nFlags, BIFF12_CALCPR_A1, XML_A1, XML_R1C1 );
+ maCalcSettings.mnCalcMode = STATIC_ARRAY_SELECT( spnCalcModes, nCalcMode, XML_auto );
+ maCalcSettings.mnProcCount = getFlagValue< sal_Int32 >( nFlags, BIFF12_CALCPR_MANUALPROC, nProcCount, -1 );
+ maCalcSettings.mbCalcOnSave = getFlag( nFlags, BIFF12_CALCPR_CALCONSAVE );
+ maCalcSettings.mbCalcCompleted = getFlag( nFlags, BIFF12_CALCPR_CALCCOMPLETED );
+ maCalcSettings.mbFullPrecision = getFlag( nFlags, BIFF12_CALCPR_FULLPRECISION );
+ maCalcSettings.mbIterate = getFlag( nFlags, BIFF12_CALCPR_ITERATE );
+ maCalcSettings.mbConcurrent = getFlag( nFlags, BIFF12_CALCPR_CONCURRENT );
+}
+
+void WorkbookSettings::finalizeImport()
+{
+ // default settings
+ PropertySet aPropSet(( Reference< css::beans::XPropertySet >(getDocument()) ));
+ aPropSet.setProperty( PROP_IgnoreCase, true ); // always in Excel
+ aPropSet.setProperty( PROP_RegularExpressions, false ); // not supported in Excel
+ aPropSet.setProperty( PROP_Wildcards, true ); // always in Excel
+
+ // write protection
+ if (maFileSharing.mbRecommendReadOnly || (maFileSharing.mnPasswordHash != 0) ||
+ !maFileSharing.maHashValue.isEmpty()) try
+ {
+ getBaseFilter().getMediaDescriptor()[ "ReadOnly" ] <<= true;
+
+ Reference< XPropertySet > xDocumentSettings( getBaseFilter().getModelFactory()->createInstance(
+ "com.sun.star.document.Settings" ), UNO_QUERY_THROW );
+ PropertySet aSettingsProp( xDocumentSettings );
+
+ /* TODO: not setting read-only if only mnPasswordHash ('password'
+ * attribute) is present looks a bit silly, any reason for that?
+ * 'readOnlyRecommended' is defined as "indicates on open, whether the
+ * application alerts the user that the file be marked as read-only",
+ * which sounds silly in itself and seems not to be present if the
+ * 'password' attribute isn't present, but... */
+ if (maFileSharing.mbRecommendReadOnly || !maFileSharing.maHashValue.isEmpty())
+ aSettingsProp.setProperty( PROP_LoadReadonly, true );
+
+ if (!maFileSharing.maHashValue.isEmpty())
+ {
+ Sequence<PropertyValue> aResult{
+ comphelper::makePropertyValue("algorithm-name", maFileSharing.maAlgorithmName),
+ comphelper::makePropertyValue("salt", maFileSharing.maSaltValue),
+ comphelper::makePropertyValue("iteration-count", maFileSharing.mnSpinCount),
+ comphelper::makePropertyValue("hash", maFileSharing.maHashValue)
+ };
+ aSettingsProp.setProperty(PROP_ModifyPasswordInfo, aResult);
+ }
+
+ if( maFileSharing.mnPasswordHash != 0 )
+ aSettingsProp.setProperty( PROP_ModifyPasswordHash, static_cast< sal_Int32 >( maFileSharing.mnPasswordHash ) );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // calculation settings
+ css::util::Date aNullDate = getNullDate();
+
+ aPropSet.setProperty( PROP_NullDate, aNullDate );
+ aPropSet.setProperty( PROP_IsIterationEnabled, maCalcSettings.mbIterate );
+ aPropSet.setProperty( PROP_IterationCount, maCalcSettings.mnIterateCount );
+ aPropSet.setProperty( PROP_IterationEpsilon, maCalcSettings.mfIterateDelta );
+ aPropSet.setProperty( PROP_CalcAsShown, !maCalcSettings.mbFullPrecision );
+ aPropSet.setProperty( PROP_LookUpLabels, false );
+
+ Reference< XNumberFormatsSupplier > xNumFmtsSupp( static_cast<cppu::OWeakObject*>(getDocument().get()), UNO_QUERY );
+ if( xNumFmtsSupp.is() )
+ {
+ PropertySet aNumFmtProp( xNumFmtsSupp->getNumberFormatSettings() );
+ aNumFmtProp.setProperty( PROP_NullDate, aNullDate );
+ }
+
+ rtl::Reference< ScModelObj > xCalculatable( getDocument() );
+ if( xCalculatable.is() )
+ xCalculatable->enableAutomaticCalculation( (maCalcSettings.mnCalcMode == XML_auto) || (maCalcSettings.mnCalcMode == XML_autoNoTable) );
+
+ // VBA code name
+ aPropSet.setProperty( PROP_CodeName, maBookSettings.maCodeName );
+}
+
+sal_Int16 WorkbookSettings::getApiShowObjectMode() const
+{
+ switch( maBookSettings.mnShowObjectMode )
+ {
+ case XML_all: return API_SHOWMODE_SHOW;
+ case XML_none: return API_SHOWMODE_HIDE;
+ // #i80528# placeholders not supported anymore, but this is handled internally in Calc
+ case XML_placeholders: return API_SHOWMODE_PLACEHOLDER;
+ }
+ return API_SHOWMODE_SHOW;
+}
+
+css::util::Date const & WorkbookSettings::getNullDate() const
+{
+ static const css::util::Date saDate1900 ( 30, 12, 1899 );
+ static const css::util::Date saDate1904 ( 1, 1, 1904 );
+ static const css::util::Date saDateBackCompatibility1900( 31, 12, 1899 );
+
+ if( getOoxFilter().getVersion() == oox::core::ISOIEC_29500_2008 )
+ {
+ if( !maBookSettings.mbDateCompatibility )
+ return saDate1900;
+
+ return maBookSettings.mbDateMode1904 ? saDate1904 :
+ saDateBackCompatibility1900;
+ }
+
+ return maBookSettings.mbDateMode1904 ? saDate1904 : saDate1900;
+}
+
+void WorkbookSettings::setDateMode( bool bDateMode1904, bool bDateCompatibility )
+{
+ maBookSettings.mbDateMode1904 = bDateMode1904;
+ maBookSettings.mbDateCompatibility = bDateCompatibility;
+
+ getUnitConverter().finalizeNullDate( getNullDate() );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheetbuffer.cxx b/sc/source/filter/oox/worksheetbuffer.cxx
new file mode 100644
index 0000000000..52285a75d3
--- /dev/null
+++ b/sc/source/filter/oox/worksheetbuffer.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 <sal/config.h>
+
+#include <string_view>
+
+#include <worksheetbuffer.hxx>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <biffhelper.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docuno.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+SheetInfoModel::SheetInfoModel() :
+ mnSheetId( -1 ),
+ mnState( XML_visible )
+{
+}
+
+WorksheetBuffer::WorksheetBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void WorksheetBuffer::importSheet( const AttributeList& rAttribs )
+{
+ SheetInfoModel aModel;
+ aModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ aModel.maName = rAttribs.getXString( XML_name, OUString() );
+ aModel.mnSheetId = rAttribs.getInteger( XML_sheetId, -1 );
+ aModel.mnState = rAttribs.getToken( XML_state, XML_visible );
+ insertSheet( aModel );
+}
+
+void WorksheetBuffer::importSheet( SequenceInputStream& rStrm )
+{
+ sal_Int32 nState;
+ SheetInfoModel aModel;
+ nState = rStrm.readInt32();
+ aModel.mnSheetId = rStrm.readInt32();
+ rStrm >> aModel.maRelId >> aModel.maName;
+ static const sal_Int32 spnStates[] = { XML_visible, XML_hidden, XML_veryHidden };
+ aModel.mnState = STATIC_ARRAY_SELECT( spnStates, nState, XML_visible );
+ insertSheet( aModel );
+}
+
+sal_Int16 WorksheetBuffer::insertEmptySheet( const OUString& rPreferredName )
+{
+ IndexNamePair aIndexName = createSheet( rPreferredName, SAL_MAX_INT32 );
+ ScDocument& rDoc = getScDocument();
+
+ rDoc.SetVisible( aIndexName.first, false );
+ return aIndexName.first;
+}
+
+sal_Int32 WorksheetBuffer::getWorksheetCount() const
+{
+ return static_cast< sal_Int32 >( maSheetInfos.size() );
+}
+
+sal_Int32 WorksheetBuffer::getAllSheetCount() const
+{
+ const ScDocumentImport& rDoc = getDocImport();
+ return rDoc.getSheetCount();
+}
+
+OUString WorksheetBuffer::getWorksheetRelId( sal_Int32 nWorksheet ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfos.get( nWorksheet ).get();
+ return pSheetInfo ? pSheetInfo->maRelId : OUString();
+}
+
+sal_Int16 WorksheetBuffer::getCalcSheetIndex( sal_Int32 nWorksheet ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfos.get( nWorksheet ).get();
+ return pSheetInfo ? pSheetInfo->mnCalcSheet : -1;
+}
+
+OUString WorksheetBuffer::getCalcSheetName( sal_Int32 nWorksheet ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfos.get( nWorksheet ).get();
+ return pSheetInfo ? pSheetInfo->maCalcName : OUString();
+}
+
+void WorksheetBuffer::convertSheetNameRef( OUString& sSheetNameRef ) const
+{
+ if( !sSheetNameRef.startsWith("#") )
+ return;
+
+ sal_Int32 nSepPos = sSheetNameRef.lastIndexOf( '!' );
+ if( nSepPos <= 0 )
+ return;
+
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well. Hyperlink
+ // handler has to handle all, but prefer '#SheetName.A1' if
+ // possible.
+ if (nSepPos < sSheetNameRef.getLength() - 1)
+ {
+ ScRange aRange;
+ if ((aRange.ParseAny( sSheetNameRef.copy( nSepPos + 1 ), getScDocument(),
+ formula::FormulaGrammar::CONV_XL_R1C1) & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ sSheetNameRef = sSheetNameRef.replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ) );
+ }
+ // #i66592# convert sheet names that have been renamed on import
+ OUString aSheetName = sSheetNameRef.copy( 1, nSepPos - 1 );
+ OUString aCalcName = getCalcSheetName( aSheetName );
+ if( !aCalcName.isEmpty() )
+ sSheetNameRef = sSheetNameRef.replaceAt( 1, nSepPos - 1, aCalcName );
+}
+
+sal_Int16 WorksheetBuffer::getCalcSheetIndex( const OUString& rWorksheetName ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfosByName.get( rWorksheetName ).get();
+ return pSheetInfo ? pSheetInfo->mnCalcSheet : -1;
+}
+
+OUString WorksheetBuffer::getCalcSheetName( const OUString& rWorksheetName ) const
+{
+ if( const SheetInfo* pSheetInfo = maSheetInfosByName.get( rWorksheetName ).get() )
+ {
+ bool bIsQuoted = pSheetInfo->maName != rWorksheetName;
+ return bIsQuoted ? pSheetInfo->maCalcQuotedName : pSheetInfo->maCalcName;
+ }
+ return OUString();
+}
+
+// private --------------------------------------------------------------------
+
+namespace {
+
+OUString lclQuoteName( std::u16string_view rName )
+{
+ OUStringBuffer aBuffer( rName );
+ // duplicate all quote characters
+ for( sal_Int32 nPos = aBuffer.getLength() - 1; nPos >= 0; --nPos )
+ if( aBuffer[nPos] == '\'' )
+ aBuffer.insert( nPos, '\'' );
+ // add outer quotes and return
+ return aBuffer.insert( 0, '\'' ).append( '\'' ).makeStringAndClear();
+}
+
+} // namespace
+
+WorksheetBuffer::SheetInfo::SheetInfo( const SheetInfoModel& rModel, sal_Int16 nCalcSheet, const OUString& rCalcName ) :
+ SheetInfoModel( rModel ),
+ maCalcName( rCalcName ),
+ maCalcQuotedName( lclQuoteName( rCalcName ) ),
+ mnCalcSheet( nCalcSheet )
+{
+}
+
+WorksheetBuffer::IndexNamePair WorksheetBuffer::createSheet( const OUString& rPreferredName, sal_Int32 nSheetPos )
+{
+ //FIXME: Rewrite this block using ScDocument[Import] instead of UNO
+ try
+ {
+ Reference< XSpreadsheets > xSheets( getDocument()->getSheets(), UNO_SET_THROW );
+ Reference< XIndexAccess > xSheetsIA( xSheets, UNO_QUERY_THROW );
+ sal_Int16 nCalcSheet = -1;
+ OUString aSheetName = rPreferredName.isEmpty() ? ScResId(STR_TABLE_DEF) : rPreferredName;
+ if( nSheetPos < xSheetsIA->getCount() )
+ {
+ nCalcSheet = static_cast< sal_Int16 >( nSheetPos );
+ // existing sheet - try to rename
+ Reference< XNamed > xSheetName( xSheetsIA->getByIndex( nSheetPos ), UNO_QUERY_THROW );
+ if( xSheetName->getName() != aSheetName )
+ {
+ aSheetName = ContainerHelper::getUnusedName( xSheets, aSheetName, ' ' );
+ xSheetName->setName( aSheetName );
+ }
+ }
+ else
+ {
+ nCalcSheet = static_cast< sal_Int16 >( xSheetsIA->getCount() );
+ // new sheet - insert with unused name
+ aSheetName = ContainerHelper::getUnusedName( xSheets, aSheetName, ' ' );
+ xSheets->insertNewByName( aSheetName, nCalcSheet );
+ }
+
+ // return final sheet index if sheet exists
+ return IndexNamePair( nCalcSheet, aSheetName );
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL( "WorksheetBuffer::createSheet - cannot insert or rename worksheet" );
+ }
+ return IndexNamePair( -1, OUString() );
+}
+
+void WorksheetBuffer::insertSheet( const SheetInfoModel& rModel )
+{
+ sal_Int32 nWorksheet = static_cast< sal_Int32 >( maSheetInfos.size() );
+ IndexNamePair aIndexName = createSheet( rModel.maName, nWorksheet );
+ auto xSheetInfo = std::make_shared<SheetInfo>( rModel, aIndexName.first, aIndexName.second );
+ maSheetInfos.push_back( xSheetInfo );
+ maSheetInfosByName[ rModel.maName ] = xSheetInfo;
+ maSheetInfosByName[ lclQuoteName( rModel.maName ) ] = xSheetInfo;
+}
+
+void WorksheetBuffer::finalizeImport( sal_Int16 nActiveSheet )
+{
+ ScDocument& rDoc = getScDocument();
+
+ for ( const auto& aSheetInfo: maSheetInfos )
+ {
+ // make sure at least 1 sheet (the active one) is visible
+ if ( aSheetInfo->mnCalcSheet == nActiveSheet)
+ rDoc.SetVisible( aSheetInfo->mnCalcSheet, true );
+ else
+ rDoc.SetVisible( aSheetInfo->mnCalcSheet, aSheetInfo->mnState == XML_visible );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheetfragment.cxx b/sc/source/filter/oox/worksheetfragment.cxx
new file mode 100644
index 0000000000..30156058e7
--- /dev/null
+++ b/sc/source/filter/oox/worksheetfragment.cxx
@@ -0,0 +1,909 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <worksheetfragment.hxx>
+#include <formulaparser.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <autofilterbuffer.hxx>
+#include <autofiltercontext.hxx>
+#include <commentsfragment.hxx>
+#include <condformatcontext.hxx>
+#include <drawingfragment.hxx>
+#include <pagesettings.hxx>
+#include <pivottablefragment.hxx>
+#include <querytablefragment.hxx>
+#include <scenariocontext.hxx>
+#include <sheetdatabuffer.hxx>
+#include <sheetdatacontext.hxx>
+#include <tablefragment.hxx>
+#include <extlstcontext.hxx>
+#include <viewsettings.hxx>
+#include <worksheetsettings.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+namespace {
+
+const sal_uInt16 BIFF_COLINFO_HIDDEN = 0x0001;
+const sal_uInt16 BIFF_COLINFO_SHOWPHONETIC = 0x0008;
+const sal_uInt16 BIFF_COLINFO_COLLAPSED = 0x1000;
+
+const sal_uInt16 BIFF_DEFROW_CUSTOMHEIGHT = 0x0001;
+const sal_uInt16 BIFF_DEFROW_HIDDEN = 0x0002;
+const sal_uInt16 BIFF_DEFROW_THICKTOP = 0x0004;
+const sal_uInt16 BIFF_DEFROW_THICKBOTTOM = 0x0008;
+
+const sal_uInt32 BIFF_DATAVAL_STRINGLIST = 0x00000080;
+const sal_uInt32 BIFF_DATAVAL_ALLOWBLANK = 0x00000100;
+const sal_uInt32 BIFF_DATAVAL_NODROPDOWN = 0x00000200;
+const sal_uInt32 BIFF_DATAVAL_SHOWINPUT = 0x00040000;
+const sal_uInt32 BIFF_DATAVAL_SHOWERROR = 0x00080000;
+
+const sal_Int32 BIFF12_OLEOBJECT_ICON = 4;
+const sal_Int32 BIFF12_OLEOBJECT_ALWAYS = 1;
+const sal_uInt16 BIFF12_OLEOBJECT_LINKED = 0x0001;
+const sal_uInt16 BIFF12_OLEOBJECT_AUTOLOAD = 0x0002;
+
+} // namespace
+
+void DataValidationsContextBase::SetValidation( WorksheetHelper& rTarget )
+{
+ if (!mxValModel)
+ return;
+
+ rTarget.getAddressConverter().convertToCellRangeList(mxValModel->maRanges, maSqref, rTarget.getSheetIndex(), true);
+ mxValModel->msRef = maSqref;
+
+ mxValModel->maTokens1 = rTarget.getFormulaParser().importFormula(mxValModel->maRanges.GetTopLeftCorner(), maFormula1);
+ // process string list of a list validation (convert to list of string tokens)
+ if (mxValModel->mnType == XML_list)
+ rTarget.getFormulaParser().convertStringToStringList(mxValModel->maTokens1, ',', true);
+
+ mxValModel->maTokens2 = rTarget.getFormulaParser().importFormula(mxValModel->maRanges.GetTopLeftCorner(), maFormula2);
+
+ rTarget.setValidation(*mxValModel);
+ mxValModel.reset();
+}
+
+void DataValidationsContextBase::importDataValidation( const AttributeList& rAttribs )
+{
+ mxValModel.reset(new ValidationModel);
+ maFormula1.clear();
+ maFormula2.clear();
+ maSqref = rAttribs.getString(XML_sqref, OUString());
+ mxValModel->maInputTitle = rAttribs.getXString(XML_promptTitle, OUString());
+ mxValModel->maInputMessage = rAttribs.getXString(XML_prompt, OUString());
+ mxValModel->maErrorTitle = rAttribs.getXString(XML_errorTitle, OUString());
+ mxValModel->maErrorMessage = rAttribs.getXString(XML_error, OUString());
+ mxValModel->mnType = rAttribs.getToken(XML_type, XML_none);
+ mxValModel->mnOperator = rAttribs.getToken(XML_operator, XML_between);
+ mxValModel->mnErrorStyle = rAttribs.getToken(XML_errorStyle, XML_stop);
+ mxValModel->mbShowInputMsg = rAttribs.getBool(XML_showInputMessage, false);
+ mxValModel->mbShowErrorMsg = rAttribs.getBool(XML_showErrorMessage, false);
+ /* The attribute showDropDown@dataValidation is in fact a "suppress
+ dropdown" flag, as it was in the BIFF format! ECMA specification
+ and attribute name are plain wrong! */
+ mxValModel->mbNoDropDown = rAttribs.getBool(XML_showDropDown, false);
+ mxValModel->mbAllowBlank = rAttribs.getBool(XML_allowBlank, false);
+}
+
+void DataValidationsContextBase::importDataValidation( SequenceInputStream& rStrm, WorksheetHelper& rTarget )
+{
+ ValidationModel aModel;
+
+ sal_uInt32 nFlags;
+ BinRangeList aRanges;
+ nFlags = rStrm.readuInt32();
+ rStrm >> aRanges >> aModel.maErrorTitle >> aModel.maErrorMessage >> aModel.maInputTitle >> aModel.maInputMessage;
+
+ // equal flags in all BIFFs
+ aModel.setBiffType(extractValue< sal_uInt8 >(nFlags, 0, 4));
+ aModel.setBiffOperator(extractValue< sal_uInt8 >(nFlags, 20, 4));
+ aModel.setBiffErrorStyle(extractValue< sal_uInt8 >(nFlags, 4, 3));
+ aModel.mbAllowBlank = getFlag(nFlags, BIFF_DATAVAL_ALLOWBLANK);
+ aModel.mbNoDropDown = getFlag(nFlags, BIFF_DATAVAL_NODROPDOWN);
+ aModel.mbShowInputMsg = getFlag(nFlags, BIFF_DATAVAL_SHOWINPUT);
+ aModel.mbShowErrorMsg = getFlag(nFlags, BIFF_DATAVAL_SHOWERROR);
+
+ // cell range list
+ rTarget.getAddressConverter().convertToCellRangeList(aModel.maRanges, aRanges, rTarget.getSheetIndex(), true);
+
+ // condition formula(s)
+ FormulaParser& rParser = rTarget.getFormulaParser();
+ ScAddress aBaseAddr = aModel.maRanges.GetTopLeftCorner();
+ aModel.maTokens1 = rParser.importFormula(aBaseAddr, FormulaType::Validation, rStrm);
+ aModel.maTokens2 = rParser.importFormula(aBaseAddr, FormulaType::Validation, rStrm);
+ // process string list of a list validation (convert to list of string tokens)
+ if ((aModel.mnType == XML_list) && getFlag(nFlags, BIFF_DATAVAL_STRINGLIST))
+ rParser.convertStringToStringList(aModel.maTokens1, ',', true);
+
+ // set validation data
+ rTarget.setValidation(aModel);
+}
+
+DataValidationsContext::DataValidationsContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment )
+{
+}
+
+ContextHandlerRef DataValidationsContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElementWithMce() )
+ {
+ case XLS_TOKEN( dataValidations ):
+ if( nElement == XLS_TOKEN( dataValidation ) )
+ {
+ importDataValidation( rAttribs );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( dataValidation ):
+ switch( nElement )
+ {
+ case MCE_TOKEN( AlternateContent ):
+ case XLS_TOKEN( formula1 ):
+ case XLS_TOKEN( formula2 ):
+ return this; // collect formulas in onCharacters()
+ }
+ break;
+ case MCE_TOKEN( AlternateContent ):
+ switch( nElement )
+ {
+ case MCE_TOKEN( Choice ):
+ case MCE_TOKEN( Fallback ):
+ return this;
+ }
+ break;
+ case MCE_TOKEN( Choice ):
+ switch( nElement )
+ {
+ case X12AC_TOKEN( list ):
+ return this;
+ }
+ break;
+ case MCE_TOKEN( Fallback ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( formula1 ):
+ if (!isFormula1Set()) // only if more preferable choice was not used
+ return this; // collect formulas in onCharacters()
+ break;
+ case XLS_TOKEN( formula2 ):
+ if (!isFormula2Set()) // only if more preferable choice was not used
+ return this; // collect formulas in onCharacters()
+ break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+namespace {
+// Convert strings like 1,"2,3",4 to form "1","2,3","4"
+OUString NormalizeOoxList(std::u16string_view aList)
+{
+ OUStringBuffer aResult("\"");
+ bool bInsideQuotes = false;
+ const size_t nLen = aList.size();
+ for (size_t i = 0; i < nLen; ++i)
+ {
+ sal_Unicode ch = aList[i];
+
+ switch (ch)
+ {
+ case L'"':
+ bInsideQuotes = !bInsideQuotes;
+ break;
+ case L',':
+ if (!bInsideQuotes)
+ {
+ aResult.append("\",\"");
+ break;
+ }
+ [[fallthrough]];
+ default:
+ aResult.append(ch);
+ break;
+ }
+ }
+ return aResult.append('"').makeStringAndClear();
+}
+}
+
+void DataValidationsContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( formula1 ):
+ SetFormula1( rChars );
+ break;
+ case XLS_TOKEN( formula2 ):
+ SetFormula2( rChars );
+ break;
+ case X12AC_TOKEN( list ):
+ SetFormula1( NormalizeOoxList( rChars ) );
+ break;
+ }
+}
+
+void DataValidationsContext::onEndElement()
+{
+ if( getCurrentElementWithMce() == XLS_TOKEN( dataValidation ) )
+ {
+ SetValidation( *this );
+ }
+}
+
+ContextHandlerRef DataValidationsContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( nRecId == BIFF12_ID_DATAVALIDATION )
+ importDataValidation( rStrm, *this );
+ return nullptr;
+}
+
+ExtDataValidationsContext::ExtDataValidationsContext( WorksheetContextBase& rFragment ) :
+ WorksheetContextBase( rFragment ), mCurrFormula( 0 )
+{
+}
+
+ContextHandlerRef ExtDataValidationsContext::onCreateContext(sal_Int32 nElement, const AttributeList& rAttribs)
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( dataValidations ):
+ if ( nElement == XLS14_TOKEN( dataValidation ) )
+ {
+ importDataValidation( rAttribs );
+ return this;
+ }
+ break;
+ case XLS14_TOKEN( dataValidation ):
+ switch ( nElement )
+ {
+ case XLS14_TOKEN( formula1 ):
+ case XLS14_TOKEN( formula2 ):
+ mCurrFormula = nElement;
+ return this;
+ case XM_TOKEN( sqref ):
+ return this; // collect sqref in onCharacters()
+ }
+ break;
+ case XLS14_TOKEN( formula1 ):
+ case XLS14_TOKEN( formula2 ):
+ switch( nElement )
+ {
+ case XM_TOKEN( f ):
+ return this; // collect formulas in onCharacters()
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void ExtDataValidationsContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XM_TOKEN( f ):
+ switch( mCurrFormula )
+ {
+ case XLS14_TOKEN( formula1 ):
+ SetFormula1( rChars );
+ break;
+ case XLS14_TOKEN( formula2 ):
+ SetFormula2( rChars );
+ break;
+ }
+ break;
+ case XM_TOKEN( sqref ):
+ SetSqref( rChars );
+ break;
+ }
+}
+
+void ExtDataValidationsContext::onEndElement()
+{
+ if( isCurrentElement( XLS14_TOKEN( dataValidation ) ) )
+ {
+ SetValidation( *this );
+ }
+}
+
+WorksheetFragment::WorksheetFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath )
+{
+ // import data tables related to this worksheet
+ RelationsRef xTableRels = getRelations().getRelationsFromTypeFromOfficeDoc( u"table" );
+ for( const auto& rEntry : *xTableRels )
+ importOoxFragment( new TableFragment( *this, getFragmentPathFromRelation( rEntry.second ) ) );
+
+ // import comments related to this worksheet
+ OUString aCommentsFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"comments" );
+ if( !aCommentsFragmentPath.isEmpty() )
+ importOoxFragment( new CommentsFragment( *this, aCommentsFragmentPath ) );
+}
+
+ContextHandlerRef WorksheetFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT: switch( getSheetType() )
+ {
+ case WorksheetType::Work: return (nElement == XLS_TOKEN( worksheet )) ? this : nullptr;
+ case WorksheetType::Chart: return nullptr;
+ case WorksheetType::Macro: return (nElement == XM_TOKEN( macrosheet )) ? this : nullptr;
+ case WorksheetType::Dialog: return (nElement == XLS_TOKEN( dialogsheet )) ? this : nullptr;
+ case WorksheetType::Empty: return nullptr;
+ }
+ break;
+
+ case XLS_TOKEN( worksheet ):
+ case XM_TOKEN( macrosheet ):
+ case XLS_TOKEN( dialogsheet ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetData ): return new SheetDataContext( *this );
+ case XLS_TOKEN( conditionalFormatting ): return new CondFormatContext( *this );
+ case XLS_TOKEN( dataValidations ): return new DataValidationsContext( *this );
+ case XLS_TOKEN( autoFilter ): return new AutoFilterContext( *this, getAutoFilters().createAutoFilter() );
+ case XLS_TOKEN( scenarios ): return new ScenariosContext( *this );
+ case XLS_TOKEN( extLst ): return new ExtLstGlobalContext( *this );
+
+ case XLS_TOKEN( sheetViews ):
+ case XLS_TOKEN( cols ):
+ case XLS_TOKEN( mergeCells ):
+ case XLS_TOKEN( hyperlinks ):
+ case XLS_TOKEN( rowBreaks ):
+ case XLS_TOKEN( colBreaks ):
+ case XLS_TOKEN( oleObjects ):
+ case XLS_TOKEN( controls ): return this;
+
+ case XLS_TOKEN( sheetPr ): getWorksheetSettings().importSheetPr( rAttribs ); return this;
+ case XLS_TOKEN( dimension ): importDimension( rAttribs ); break;
+ case XLS_TOKEN( sheetFormatPr ): importSheetFormatPr( rAttribs ); break;
+ case XLS_TOKEN( sheetProtection ): getWorksheetSettings().importSheetProtection( rAttribs ); break;
+ case XLS_TOKEN( protectedRanges ):
+ // no attribs known (yet?)
+ return this;
+ case XLS_TOKEN( phoneticPr ): getWorksheetSettings().importPhoneticPr( rAttribs ); break;
+ case XLS_TOKEN( printOptions ): getPageSettings().importPrintOptions( rAttribs ); break;
+ case XLS_TOKEN( pageMargins ): getPageSettings().importPageMargins( rAttribs ); break;
+ case XLS_TOKEN( pageSetup ): getPageSettings().importPageSetup( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( headerFooter ): getPageSettings().importHeaderFooter( rAttribs ); return this;
+ case XLS_TOKEN( picture ): getPageSettings().importPicture( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( drawing ): importDrawing( rAttribs ); break;
+ case XLS_TOKEN( legacyDrawing ): importLegacyDrawing( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( sheetPr ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( tabColor ): getWorksheetSettings().importTabColor( rAttribs ); break;
+ case XLS_TOKEN( outlinePr ): getWorksheetSettings().importOutlinePr( rAttribs ); break;
+ case XLS_TOKEN( pageSetUpPr ): importPageSetUpPr( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( sheetViews ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetView ): getSheetViewSettings().importSheetView( rAttribs ); return this;
+ }
+ break;
+ case XLS_TOKEN( sheetView ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( pane ): getSheetViewSettings().importPane( rAttribs ); break;
+ case XLS_TOKEN( selection ): getSheetViewSettings().importSelection( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( cols ):
+ if( nElement == XLS_TOKEN( col ) ) importCol( rAttribs );
+ break;
+ case XLS_TOKEN( mergeCells ):
+ if( nElement == XLS_TOKEN( mergeCell ) ) importMergeCell( rAttribs );
+ break;
+ case XLS_TOKEN( hyperlinks ):
+ if( nElement == XLS_TOKEN( hyperlink ) ) importHyperlink( rAttribs );
+ break;
+ case XLS_TOKEN( rowBreaks ):
+ if( nElement == XLS_TOKEN( brk ) ) importBrk( rAttribs, true );
+ break;
+ case XLS_TOKEN( colBreaks ):
+ if( nElement == XLS_TOKEN( brk ) ) importBrk( rAttribs, false );
+ break;
+
+ case XLS_TOKEN( protectedRanges ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( protectedRange ): getWorksheetSettings().importProtectedRange( rAttribs ); return this;
+ }
+ break;
+
+ case XLS_TOKEN( headerFooter ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ): return this; // collect h/f contents in onCharacters()
+ }
+ break;
+ // Only process an oleObject or control if outside a mc:AlternateContent
+ // element OR if within a mc:Fallback. I suppose ideally we
+ // should process the stuff within 'mc:Choice'
+ case XLS_TOKEN( controls ):
+ case XLS_TOKEN( oleObjects ):
+ if( isMCEStateEmpty() || getMCEState() == MCE_STATE::Started )
+ {
+ if ( getCurrentElement() == XLS_TOKEN( oleObjects ) )
+ importOleObject( rAttribs );
+ else
+ importControl( rAttribs );
+ }
+ else if ( !isMCEStateEmpty() && getMCEState() == MCE_STATE::FoundChoice )
+ {
+ // reset the handling within 'Choice'
+ // this will force attempted handling in Fallback
+ setMCEState( MCE_STATE::Started );
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void WorksheetFragment::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ):
+ getPageSettings().importHeaderFooterCharacters( rChars, getCurrentElement() );
+ break;
+ }
+}
+
+ContextHandlerRef WorksheetFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_WORKSHEET ) return this;
+ break;
+
+ case BIFF12_ID_WORKSHEET:
+ switch( nRecId )
+ {
+ case BIFF12_ID_SHEETDATA: return new SheetDataContext( *this );
+ case BIFF12_ID_CONDFORMATTING: return new CondFormatContext( *this );
+ case BIFF12_ID_DATAVALIDATIONS: return new DataValidationsContext( *this );
+ case BIFF12_ID_AUTOFILTER: return new AutoFilterContext( *this, getAutoFilters().createAutoFilter() );
+ case BIFF12_ID_SCENARIOS: return new ScenariosContext( *this );
+
+ case BIFF12_ID_SHEETVIEWS:
+ case BIFF12_ID_COLS:
+ case BIFF12_ID_MERGECELLS:
+ case BIFF12_ID_ROWBREAKS:
+ case BIFF12_ID_COLBREAKS:
+ case BIFF12_ID_OLEOBJECTS:
+ case BIFF12_ID_CONTROLS: return this;
+
+ case BIFF12_ID_SHEETPR: getWorksheetSettings().importSheetPr( rStrm ); break;
+ case BIFF12_ID_DIMENSION: importDimension( rStrm ); break;
+ case BIFF12_ID_SHEETFORMATPR: importSheetFormatPr( rStrm ); break;
+ case BIFF12_ID_HYPERLINK: importHyperlink( rStrm ); break;
+ case BIFF12_ID_PAGEMARGINS: getPageSettings().importPageMargins( rStrm ); break;
+ case BIFF12_ID_PAGESETUP: getPageSettings().importPageSetup( getRelations(), rStrm ); break;
+ case BIFF12_ID_PRINTOPTIONS: getPageSettings().importPrintOptions( rStrm ); break;
+ case BIFF12_ID_HEADERFOOTER: getPageSettings().importHeaderFooter( rStrm ); break;
+ case BIFF12_ID_PICTURE: getPageSettings().importPicture( getRelations(), rStrm ); break;
+ case BIFF12_ID_SHEETPROTECTION: getWorksheetSettings().importSheetProtection( rStrm ); break;
+ case BIFF12_ID_PHONETICPR: getWorksheetSettings().importPhoneticPr( rStrm ); break;
+ case BIFF12_ID_DRAWING: importDrawing( rStrm ); break;
+ case BIFF12_ID_LEGACYDRAWING: importLegacyDrawing( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_SHEETVIEWS:
+ switch( nRecId )
+ {
+ case BIFF12_ID_SHEETVIEW: getSheetViewSettings().importSheetView( rStrm ); return this;
+ }
+ break;
+ case BIFF12_ID_SHEETVIEW:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PANE: getSheetViewSettings().importPane( rStrm ); break;
+ case BIFF12_ID_SELECTION: getSheetViewSettings().importSelection( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_COLS:
+ if( nRecId == BIFF12_ID_COL ) importCol( rStrm );
+ break;
+ case BIFF12_ID_MERGECELLS:
+ if( nRecId == BIFF12_ID_MERGECELL ) importMergeCell( rStrm );
+ break;
+ case BIFF12_ID_ROWBREAKS:
+ if( nRecId == BIFF12_ID_BRK ) importBrk( rStrm, true );
+ break;
+ case BIFF12_ID_COLBREAKS:
+ if( nRecId == BIFF12_ID_BRK ) importBrk( rStrm, false );
+ break;
+ case BIFF12_ID_OLEOBJECTS:
+ if( nRecId == BIFF12_ID_OLEOBJECT ) importOleObject( rStrm );
+ break;
+ case BIFF12_ID_CONTROLS:
+ if( nRecId == BIFF12_ID_CONTROL ) importControl( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* WorksheetFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 },
+ { BIFF12_ID_CFRULE, BIFF12_ID_CFRULE + 1 },
+ { BIFF12_ID_COLBREAKS, BIFF12_ID_COLBREAKS + 1 },
+ { BIFF12_ID_COLORSCALE, BIFF12_ID_COLORSCALE + 1 },
+ { BIFF12_ID_COLS, BIFF12_ID_COLS + 1 },
+ { BIFF12_ID_CONDFORMATTING, BIFF12_ID_CONDFORMATTING + 1 },
+ { BIFF12_ID_CONTROLS, BIFF12_ID_CONTROLS + 2 },
+ { BIFF12_ID_CUSTOMFILTERS, BIFF12_ID_CUSTOMFILTERS + 1 },
+ { BIFF12_ID_CUSTOMSHEETVIEW, BIFF12_ID_CUSTOMSHEETVIEW + 1 },
+ { BIFF12_ID_CUSTOMSHEETVIEWS, BIFF12_ID_CUSTOMSHEETVIEWS + 3 },
+ { BIFF12_ID_DATABAR, BIFF12_ID_DATABAR + 1 },
+ { BIFF12_ID_DATAVALIDATIONS, BIFF12_ID_DATAVALIDATIONS + 1 },
+ { BIFF12_ID_DISCRETEFILTERS, BIFF12_ID_DISCRETEFILTERS + 1 },
+ { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 },
+ { BIFF12_ID_HEADERFOOTER, BIFF12_ID_HEADERFOOTER + 1 },
+ { BIFF12_ID_ICONSET, BIFF12_ID_ICONSET + 1 },
+ { BIFF12_ID_MERGECELLS, BIFF12_ID_MERGECELLS + 1 },
+ { BIFF12_ID_OLEOBJECTS, BIFF12_ID_OLEOBJECTS + 2 },
+ { BIFF12_ID_ROW, -1 },
+ { BIFF12_ID_ROWBREAKS, BIFF12_ID_ROWBREAKS + 1 },
+ { BIFF12_ID_SCENARIO, BIFF12_ID_SCENARIO + 1 },
+ { BIFF12_ID_SCENARIOS, BIFF12_ID_SCENARIOS + 1 },
+ { BIFF12_ID_SHEETDATA, BIFF12_ID_SHEETDATA + 1 },
+ { BIFF12_ID_SHEETVIEW, BIFF12_ID_SHEETVIEW + 1 },
+ { BIFF12_ID_SHEETVIEWS, BIFF12_ID_SHEETVIEWS + 1 },
+ { BIFF12_ID_TABLEPARTS, BIFF12_ID_TABLEPARTS + 2 },
+ { BIFF12_ID_WORKSHEET, BIFF12_ID_WORKSHEET + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void WorksheetFragment::initializeImport()
+{
+ // initial processing in base class WorksheetHelper
+ initializeWorksheetImport();
+
+ // import query table fragments related to this worksheet
+ RelationsRef xQueryRels = getRelations().getRelationsFromTypeFromOfficeDoc( u"queryTable" );
+ for( const auto& rEntry : *xQueryRels )
+ importOoxFragment( new QueryTableFragment( *this, getFragmentPathFromRelation( rEntry.second ) ) );
+
+ // import pivot table fragments related to this worksheet
+ RelationsRef xPivotRels = getRelations().getRelationsFromTypeFromOfficeDoc( u"pivotTable" );
+ for( const auto& rEntry : *xPivotRels )
+ importOoxFragment( new PivotTableFragment( *this, getFragmentPathFromRelation( rEntry.second ) ) );
+}
+
+void WorksheetFragment::finalizeImport()
+{
+ // final processing in base class WorksheetHelper
+ finalizeWorksheetImport();
+}
+
+// private --------------------------------------------------------------------
+
+void WorksheetFragment::importPageSetUpPr( const AttributeList& rAttribs )
+{
+ // for whatever reason, this flag is still stored separated from the page settings
+ getPageSettings().setFitToPagesMode( rAttribs.getBool( XML_fitToPage, false ) );
+}
+
+void WorksheetFragment::importDimension( const AttributeList& rAttribs )
+{
+ ScRange aRange;
+ AddressConverter::convertToCellRangeUnchecked( aRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() );
+ /* OOXML stores the used area, if existing, or "A1" if the sheet is empty.
+ In case of "A1", the dimension at the WorksheetHelper object will not
+ be set. If the cell A1 exists, the used area will be updated while
+ importing the cell. */
+ if( (aRange.aEnd.Col() > 0) || (aRange.aEnd.Row() > 0) )
+ {
+ extendUsedArea( aRange );
+ }
+}
+
+void WorksheetFragment::importSheetFormatPr( const AttributeList& rAttribs )
+{
+ // default column settings
+ setBaseColumnWidth( rAttribs.getInteger( XML_baseColWidth, 8 ) );
+ setDefaultColumnWidth( rAttribs.getDouble( XML_defaultColWidth, 0.0 ) );
+ // default row settings
+
+ // We don't need to import:
+ // XML_outlineLevelRow
+ // XML_outlineLevelCol
+ // as it will be updated during export to OOXML
+ double fDefaultRowHeight = rAttribs.getDouble(XML_defaultRowHeight, 0.0);
+ if (getFilter().isMSODocument())
+ {
+ fDefaultRowHeight -= fmod(fDefaultRowHeight, 0.75); //round down to 0.75pt
+ }
+ setDefaultRowSettings(
+ fDefaultRowHeight,
+ rAttribs.getBool( XML_customHeight, false ),
+ rAttribs.getBool( XML_zeroHeight, false ),
+ rAttribs.getBool( XML_thickTop, false ),
+ rAttribs.getBool( XML_thickBottom, false ) );
+}
+
+void WorksheetFragment::importCol( const AttributeList& rAttribs )
+{
+ ColumnModel aModel;
+ aModel.maRange.mnFirst = rAttribs.getInteger( XML_min, -1 );
+ aModel.maRange.mnLast = rAttribs.getInteger( XML_max, -1 );
+ aModel.mfWidth = rAttribs.getDouble( XML_width, 0.0 );
+ aModel.mnXfId = rAttribs.getInteger( XML_style, -1 );
+ aModel.mnLevel = rAttribs.getInteger( XML_outlineLevel, 0 );
+ aModel.mbShowPhonetic = rAttribs.getBool( XML_phonetic, false );
+ aModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+ aModel.mbCollapsed = rAttribs.getBool( XML_collapsed, false );
+ // set column properties in the current sheet
+ setColumnModel( aModel );
+}
+
+void WorksheetFragment::importMergeCell( const AttributeList& rAttribs )
+{
+ ScRange aRange;
+ if( getAddressConverter().convertToCellRange( aRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex(), true, true ) )
+ getSheetData().setMergedRange( aRange );
+}
+
+void WorksheetFragment::importHyperlink( const AttributeList& rAttribs )
+{
+ HyperlinkModel aModel;
+ if( getAddressConverter().convertToCellRange( aModel.maRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex(), true, true ) )
+ {
+ aModel.maTarget = getRelations().getExternalTargetFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ aModel.maLocation = rAttribs.getXString( XML_location, OUString() );
+ aModel.maDisplay = rAttribs.getXString( XML_display, OUString() );
+ aModel.maTooltip = rAttribs.getXString( XML_tooltip, OUString() );
+ setHyperlink( aModel );
+ }
+}
+
+void WorksheetFragment::importBrk( const AttributeList& rAttribs, bool bRowBreak )
+{
+ PageBreakModel aModel;
+ aModel.mnColRow = rAttribs.getInteger( XML_id, 0 );
+ aModel.mnMin = rAttribs.getInteger( XML_min, aModel.mnColRow );
+ aModel.mnMax = rAttribs.getInteger( XML_max, aModel.mnColRow );
+ aModel.mbManual = rAttribs.getBool( XML_man, false );
+ setPageBreak( aModel, bRowBreak );
+}
+
+void WorksheetFragment::importDrawing( const AttributeList& rAttribs )
+{
+ setDrawingPath( getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ) );
+}
+
+void WorksheetFragment::importLegacyDrawing( const AttributeList& rAttribs )
+{
+ setVmlDrawingPath( getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ) );
+}
+
+void WorksheetFragment::importOleObject( const AttributeList& rAttribs )
+{
+ ::oox::vml::OleObjectInfo aInfo;
+ aInfo.setShapeId( rAttribs.getInteger( XML_shapeId, 0 ) );
+ OSL_ENSURE( rAttribs.hasAttribute( XML_link ) != rAttribs.hasAttribute( R_TOKEN( id ) ),
+ "WorksheetFragment::importOleObject - OLE object must be either linked or embedded" );
+ aInfo.mbLinked = rAttribs.hasAttribute( XML_link );
+ if( aInfo.mbLinked )
+ aInfo.maTargetLink = getFormulaParser().importOleTargetLink( rAttribs.getString( XML_link, OUString() ) );
+ else if( rAttribs.hasAttribute( R_TOKEN( id ) ) )
+ importEmbeddedOleData( aInfo.maEmbeddedData, rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ aInfo.maProgId = rAttribs.getString( XML_progId, OUString() );
+ aInfo.mbShowAsIcon = rAttribs.getToken( XML_dvAspect, XML_DVASPECT_CONTENT ) == XML_DVASPECT_ICON;
+ aInfo.mbAutoUpdate = rAttribs.getToken( XML_oleUpdate, XML_OLEUPDATE_ONCALL ) == XML_OLEUPDATE_ALWAYS;
+ aInfo.mbAutoLoad = rAttribs.getBool( XML_autoLoad, false );
+ getVmlDrawing().registerOleObject( aInfo );
+}
+
+void WorksheetFragment::importControl( const AttributeList& rAttribs )
+{
+ ::oox::vml::ControlInfo aInfo;
+ aInfo.setShapeId( rAttribs.getInteger( XML_shapeId, 0 ) );
+ aInfo.maFragmentPath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ aInfo.maName = rAttribs.getString( XML_name, OUString() );
+ getVmlDrawing().registerControl( aInfo );
+}
+
+void WorksheetFragment::importDimension( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ aBinRange.read( rStrm );
+ ScRange aRange;
+ AddressConverter::convertToCellRangeUnchecked( aRange, aBinRange, getSheetIndex() );
+ /* BIFF12 stores the used area, if existing, or "A1" if the sheet is
+ empty. In case of "A1", the dimension at the WorksheetHelper object
+ will not be set. If the cell A1 exists, the used area will be updated
+ while importing the cell. */
+ if( (aRange.aEnd.Col() > 0) || (aRange.aEnd.Row() > 0) )
+ extendUsedArea( aRange );
+}
+
+void WorksheetFragment::importSheetFormatPr( SequenceInputStream& rStrm )
+{
+ sal_Int32 nDefaultWidth;
+ sal_uInt16 nBaseWidth, nDefaultHeight, nFlags;
+ nDefaultWidth = rStrm.readInt32();
+ nBaseWidth = rStrm.readuInt16();
+ nDefaultHeight = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+
+ // base column with
+ setBaseColumnWidth( nBaseWidth );
+ // default width is stored as 1/256th of a character in BIFF12, convert to entire character
+ setDefaultColumnWidth( static_cast< double >( nDefaultWidth ) / 256.0 );
+ // row height is in twips in BIFF12, convert to points; equal flags in all BIFFs
+ setDefaultRowSettings(
+ nDefaultHeight / 20.0,
+ getFlag( nFlags, BIFF_DEFROW_CUSTOMHEIGHT ),
+ getFlag( nFlags, BIFF_DEFROW_HIDDEN ),
+ getFlag( nFlags, BIFF_DEFROW_THICKTOP ),
+ getFlag( nFlags, BIFF_DEFROW_THICKBOTTOM ) );
+}
+
+void WorksheetFragment::importCol( SequenceInputStream& rStrm )
+{
+ ColumnModel aModel;
+
+ sal_Int32 nWidth;
+ sal_uInt16 nFlags;
+ aModel.maRange.mnFirst = rStrm.readInt32();
+ aModel.maRange.mnLast = rStrm.readInt32();
+ nWidth = rStrm.readInt32();
+ aModel.mnXfId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+
+ // column indexes are 0-based in BIFF12, but ColumnModel expects 1-based
+ ++aModel.maRange.mnFirst;
+ ++aModel.maRange.mnLast;
+ // width is stored as 1/256th of a character in BIFF12, convert to entire character
+ aModel.mfWidth = static_cast< double >( nWidth ) / 256.0;
+ // equal flags in all BIFFs
+ aModel.mnLevel = extractValue< sal_Int32 >( nFlags, 8, 3 );
+ aModel.mbShowPhonetic = getFlag( nFlags, BIFF_COLINFO_SHOWPHONETIC );
+ aModel.mbHidden = getFlag( nFlags, BIFF_COLINFO_HIDDEN );
+ aModel.mbCollapsed = getFlag( nFlags, BIFF_COLINFO_COLLAPSED );
+ // set column properties in the current sheet
+ setColumnModel( aModel );
+}
+
+void WorksheetFragment::importMergeCell( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ ScRange aRange;
+ if( getAddressConverter().convertToCellRange( aRange, aBinRange, getSheetIndex(), true, true ) )
+ getSheetData().setMergedRange( aRange );
+}
+
+void WorksheetFragment::importHyperlink( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ HyperlinkModel aModel;
+ if( getAddressConverter().convertToCellRange( aModel.maRange, aBinRange, getSheetIndex(), true, true ) )
+ {
+ aModel.maTarget = getRelations().getExternalTargetFromRelId( BiffHelper::readString( rStrm ) );
+ rStrm >> aModel.maLocation >> aModel.maTooltip >> aModel.maDisplay;
+ setHyperlink( aModel );
+ }
+}
+
+void WorksheetFragment::importBrk( SequenceInputStream& rStrm, bool bRowBreak )
+{
+ PageBreakModel aModel;
+ sal_Int32 nManual;
+ aModel.mnColRow = rStrm.readInt32();
+ aModel.mnMin = rStrm.readInt32();
+ aModel.mnMax = rStrm.readInt32();
+ nManual = rStrm.readInt32();
+ aModel.mbManual = nManual != 0;
+ setPageBreak( aModel, bRowBreak );
+}
+
+void WorksheetFragment::importDrawing( SequenceInputStream& rStrm )
+{
+ setDrawingPath( getFragmentPathFromRelId( BiffHelper::readString( rStrm ) ) );
+}
+
+void WorksheetFragment::importLegacyDrawing( SequenceInputStream& rStrm )
+{
+ setVmlDrawingPath( getFragmentPathFromRelId( BiffHelper::readString( rStrm ) ) );
+}
+
+void WorksheetFragment::importOleObject( SequenceInputStream& rStrm )
+{
+ ::oox::vml::OleObjectInfo aInfo;
+ sal_Int32 nAspect, nUpdateMode, nShapeId;
+ sal_uInt16 nFlags;
+ nAspect = rStrm.readInt32();
+ nUpdateMode = rStrm.readInt32();
+ nShapeId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ rStrm >> aInfo.maProgId;
+ aInfo.mbLinked = getFlag( nFlags, BIFF12_OLEOBJECT_LINKED );
+ if( aInfo.mbLinked )
+ aInfo.maTargetLink = getFormulaParser().importOleTargetLink( rStrm );
+ else
+ importEmbeddedOleData( aInfo.maEmbeddedData, BiffHelper::readString( rStrm ) );
+ aInfo.setShapeId( nShapeId );
+ aInfo.mbShowAsIcon = nAspect == BIFF12_OLEOBJECT_ICON;
+ aInfo.mbAutoUpdate = nUpdateMode == BIFF12_OLEOBJECT_ALWAYS;
+ aInfo.mbAutoLoad = getFlag( nFlags, BIFF12_OLEOBJECT_AUTOLOAD );
+ getVmlDrawing().registerOleObject( aInfo );
+}
+
+void WorksheetFragment::importControl( SequenceInputStream& rStrm )
+{
+ ::oox::vml::ControlInfo aInfo;
+ aInfo.setShapeId( rStrm.readInt32() );
+ aInfo.maFragmentPath = getFragmentPathFromRelId( BiffHelper::readString( rStrm ) );
+ rStrm >> aInfo.maName;
+ getVmlDrawing().registerControl( aInfo );
+}
+
+void WorksheetFragment::importEmbeddedOleData( StreamDataSequence& orEmbeddedData, const OUString& rRelId )
+{
+ OUString aFragmentPath = getFragmentPathFromRelId( rRelId );
+ if( !aFragmentPath.isEmpty() )
+ getBaseFilter().importBinaryData( orEmbeddedData, aFragmentPath );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheethelper.cxx b/sc/source/filter/oox/worksheethelper.cxx
new file mode 100644
index 0000000000..495cc0ff78
--- /dev/null
+++ b/sc/source/filter/oox/worksheethelper.cxx
@@ -0,0 +1,1665 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <utility>
+#include <worksheethelper.hxx>
+
+#include <algorithm>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/sheet/ConditionOperator2.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/XCellAddressable.hpp>
+#include <com/sun/star/sheet/XMultiFormulaTokens.hpp>
+#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
+#include <com/sun/star/sheet/XSheetCondition2.hpp>
+#include <com/sun/star/sheet/XSheetOutline.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <autofilterbuffer.hxx>
+#include <commentsbuffer.hxx>
+#include <condformatbuffer.hxx>
+#include <document.hxx>
+#include <drawingfragment.hxx>
+#include <pagesettings.hxx>
+#include <querytablebuffer.hxx>
+#include <sheetdatabuffer.hxx>
+#include <stylesbuffer.hxx>
+#include <tokenuno.hxx>
+#include <unitconverter.hxx>
+#include <viewsettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <worksheetsettings.hxx>
+#include <formulabuffer.hxx>
+#include <scitems.hxx>
+#include <editutil.hxx>
+#include <tokenarray.hxx>
+#include <table.hxx>
+#include <tablebuffer.hxx>
+#include <documentimport.hxx>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+#include <cellvalue.hxx>
+#include <columnspanset.hxx>
+#include <dbdata.hxx>
+
+#include <svl/stritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <tools/gen.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+void lclUpdateProgressBar( const ISegmentProgressBarRef& rxProgressBar, double fPosition )
+{
+ if( rxProgressBar )
+ rxProgressBar->setPosition( fPosition );
+}
+
+// TODO Needed because input might be >32-bit (in 64-bit builds),
+// or a negative, already overflown value (in 32-bit builds)
+sal_Int32 lclClampToNonNegativeInt32( tools::Long aVal )
+{
+ if ( aVal > SAL_MAX_INT32 || aVal < 0 )
+ {
+ SAL_WARN( "sc.filter", "Overflow detected, " << aVal << " does not fit into sal_Int32, or is negative." );
+ return SAL_MAX_INT32;
+ }
+ return static_cast<sal_Int32>( aVal );
+}
+
+} // namespace
+
+ColumnModel::ColumnModel() :
+ maRange( -1 ),
+ mfWidth( 0.0 ),
+ mnXfId( -1 ),
+ mnLevel( 0 ),
+ mbShowPhonetic( false ),
+ mbHidden( false ),
+ mbCollapsed( false )
+{
+}
+
+bool ColumnModel::isMergeable( const ColumnModel& rModel ) const
+{
+ return
+ (maRange.mnFirst <= rModel.maRange.mnFirst) &&
+ (rModel.maRange.mnFirst <= maRange.mnLast + 1) &&
+ (mfWidth == rModel.mfWidth) &&
+ // ignore mnXfId, cell formatting is always set directly
+ (mnLevel == rModel.mnLevel) &&
+ (mbHidden == rModel.mbHidden) &&
+ (mbCollapsed == rModel.mbCollapsed);
+}
+
+RowModel::RowModel() :
+ mnRow( -1 ),
+ mfHeight( 0.0 ),
+ mnXfId( -1 ),
+ mnLevel( 0 ),
+ mbCustomHeight( false ),
+ mbCustomFormat( false ),
+ mbShowPhonetic( false ),
+ mbHidden( false ),
+ mbCollapsed( false ),
+ mbThickTop( false ),
+ mbThickBottom( false )
+{
+}
+
+bool RowModel::isMergeable( const RowModel& rModel ) const
+{
+ return
+ // ignore maColSpans - is handled separately in SheetDataBuffer class
+ (mfHeight == rModel.mfHeight) &&
+ // ignore mnXfId, mbCustomFormat, mbShowPhonetic - cell formatting is always set directly
+ (mnLevel == rModel.mnLevel) &&
+ (mbCustomHeight == rModel.mbCustomHeight) &&
+ (mbHidden == rModel.mbHidden) &&
+ (mbCollapsed == rModel.mbCollapsed);
+}
+
+PageBreakModel::PageBreakModel()
+ : mnColRow(0)
+ , mnMin(0)
+ , mnMax(0)
+ , mbManual(false)
+{
+}
+
+HyperlinkModel::HyperlinkModel()
+{
+}
+
+ValidationModel::ValidationModel() :
+ mnType( XML_none ),
+ mnOperator( XML_between ),
+ mnErrorStyle( XML_stop ),
+ mbShowInputMsg( false ),
+ mbShowErrorMsg( false ),
+ mbNoDropDown( false ),
+ mbAllowBlank( false )
+{
+}
+
+void ValidationModel::setBiffType( sal_uInt8 nType )
+{
+ static const sal_Int32 spnTypeIds[] = {
+ XML_none, XML_whole, XML_decimal, XML_list, XML_date, XML_time, XML_textLength, XML_custom };
+ mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_none );
+}
+
+void ValidationModel::setBiffOperator( sal_uInt8 nOperator )
+{
+ static const sal_Int32 spnOperators[] = {
+ XML_between, XML_notBetween, XML_equal, XML_notEqual,
+ XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+void ValidationModel::setBiffErrorStyle( sal_uInt8 nErrorStyle )
+{
+ static const sal_Int32 spnErrorStyles[] = { XML_stop, XML_warning, XML_information };
+ mnErrorStyle = STATIC_ARRAY_SELECT( spnErrorStyles, nErrorStyle, XML_stop );
+}
+
+class WorksheetGlobals : public WorkbookHelper, public IWorksheetProgress
+{
+public:
+ explicit WorksheetGlobals(
+ const WorkbookHelper& rHelper,
+ ISegmentProgressBarRef xProgressBar,
+ WorksheetType eSheetType,
+ SCTAB nSheet );
+
+ /** Returns true, if this helper refers to an existing Calc sheet. */
+ bool isValidSheet() const { return mxSheet.is(); }
+
+ /** Returns the type of this sheet. */
+ WorksheetType getSheetType() const { return meSheetType; }
+ /** Returns the index of the current sheet. */
+ SCTAB getSheetIndex() const { return maUsedArea.aStart.Tab(); }
+ /** Returns the XSpreadsheet interface of the current sheet. */
+ const Reference< XSpreadsheet >& getSheet() const { return mxSheet; }
+
+ /** Returns the XCell interface for the passed cell address. */
+ Reference< XCell > getCell( const ScAddress& rAddress ) const;
+ /** Returns the XCellRange interface for the passed cell range address. */
+ Reference< XCellRange > getCellRange( const ScRange& rRange ) const;
+ /** Returns the XSheetCellRanges interface for the passed cell range addresses. */
+ Reference< XSheetCellRanges > getCellRangeList( const ScRangeList& rRanges ) const;
+
+ /** Returns the XCellRange interface for a column. */
+ Reference< XCellRange > getColumn( sal_Int32 nCol ) const;
+ /** Returns the XCellRange interface for a row. */
+ Reference< XCellRange > getRow( sal_Int32 nRow ) const;
+
+ /** Returns the XDrawPage interface of the draw page of the current sheet. */
+ Reference< XDrawPage > getDrawPage() const;
+ /** Returns the size of the entire drawing page in 1/100 mm. */
+ const awt::Size& getDrawPageSize() const;
+
+ /** Returns the absolute position of the top-left corner of the cell in 1/100 mm. */
+ awt::Point getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const;
+
+ /** Returns the address of the cell that contains the passed point in 1/100 mm. */
+ ScAddress getCellAddressFromPosition( const awt::Point& rPosition ) const;
+ /** Returns the cell range address that contains the passed rectangle in 1/100 mm. */
+ ScRange getCellRangeFromRectangle( const awt::Rectangle& rRect ) const;
+
+ /** Returns the buffer for cell contents and cell formatting. */
+ SheetDataBuffer& getSheetData() { return maSheetData; }
+ /** Returns the conditional formatting in this sheet. */
+ CondFormatBuffer& getCondFormats() { return maCondFormats; }
+ /** Returns the buffer for all cell comments in this sheet. */
+ CommentsBuffer& getComments() { return maComments; }
+ /** Returns the auto filters for the sheet. */
+ AutoFilterBuffer& getAutoFilters() { return maAutoFilters; }
+ /** Returns the buffer for all web query tables in this sheet. */
+ QueryTableBuffer& getQueryTables() { return maQueryTables; }
+ /** Returns the worksheet settings object. */
+ WorksheetSettings& getWorksheetSettings() { return maSheetSett; }
+ /** Returns the page/print settings for this sheet. */
+ PageSettings& getPageSettings() { return maPageSett; }
+ /** Returns the view settings for this sheet. */
+ SheetViewSettings& getSheetViewSettings() { return maSheetViewSett; }
+ /** Returns the VML drawing page for this sheet (OOXML/BIFF12 only). */
+ VmlDrawing& getVmlDrawing() { return *mxVmlDrawing; }
+ /** returns the ExtLst entries that need to be filled */
+ ExtLst& getExtLst() { return maExtLst; }
+
+ /** Sets a column or row page break described in the passed struct. */
+ void setPageBreak( const PageBreakModel& rModel, bool bRowBreak );
+ /** Inserts the hyperlink URL into the spreadsheet. */
+ void setHyperlink( const HyperlinkModel& rModel );
+ /** Inserts the data validation settings into the spreadsheet. */
+ void setValidation( const ValidationModel& rModel );
+ /** Sets the path to the DrawingML fragment of this sheet. */
+ void setDrawingPath( const OUString& rDrawingPath );
+ /** Sets the path to the legacy VML drawing fragment of this sheet. */
+ void setVmlDrawingPath( const OUString& rVmlDrawingPath );
+
+ /** Extends the used area of this sheet by the passed cell position. */
+ void extendUsedArea( const ScAddress& rAddress );
+
+ /** Extends the used area of this sheet by the passed cell range. */
+ void extendUsedArea( const ScRange& rRange );
+ /** Extends the shape bounding box by the position and size of the passed rectangle. */
+ void extendShapeBoundingBox( const awt::Rectangle& rShapeRect );
+
+ /** Sets base width for all columns (without padding pixels). This value
+ is only used, if base width has not been set with setDefaultColumnWidth(). */
+ void setBaseColumnWidth( sal_Int32 nWidth );
+ /** Sets default width for all columns. This function overrides the base
+ width set with the setBaseColumnWidth() function. */
+ void setDefaultColumnWidth( double fWidth );
+ /** Sets column settings for a specific column range.
+ @descr Column default formatting is converted directly, other settings
+ are cached and converted in the finalizeImport() call. */
+ void setColumnModel( const ColumnModel& rModel );
+ /** Converts column default cell formatting. */
+ void convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId );
+
+ /** Sets default height and hidden state for all unused rows in the sheet. */
+ void setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom );
+ /** Sets row settings for a specific row.
+ @descr Row default formatting is converted directly, other settings
+ are cached and converted in the finalizeImport() call. */
+ void setRowModel( const RowModel& rModel );
+
+ /** Initial conversion before importing the worksheet. */
+ void initializeWorksheetImport();
+ /** Final conversion after importing the worksheet. */
+ void finalizeWorksheetImport();
+
+ void finalizeDrawingImport();
+
+ /// Allow the threaded importer to override our progress bar impl.
+ virtual ISegmentProgressBarRef getRowProgress() override
+ {
+ return mxRowProgress;
+ }
+ virtual void setCustomRowProgress( const ISegmentProgressBarRef &rxRowProgress ) override
+ {
+ mxRowProgress = rxRowProgress;
+ mbFastRowProgress = true;
+ }
+
+private:
+ typedef ::std::vector< sal_Int32 > OutlineLevelVec;
+ typedef ::std::pair< ColumnModel, sal_Int32 > ColumnModelRange;
+ typedef ::std::map< sal_Int32, ColumnModelRange > ColumnModelRangeMap;
+ typedef ::std::pair< RowModel, sal_Int32 > RowModelRange;
+ typedef ::std::map< sal_Int32, RowModelRange > RowModelRangeMap;
+
+ /** Inserts all imported hyperlinks into their cell ranges. */
+ void finalizeHyperlinkRanges();
+ /** Generates the final URL for the passed hyperlink. */
+ OUString getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const;
+ /** Inserts a hyperlinks into the specified cell. */
+ void insertHyperlink( const ScAddress& rAddress, const OUString& rUrl );
+
+ /** Inserts all imported data validations into their cell ranges. */
+ void finalizeValidationRanges() const;
+
+ /** Converts column properties for all columns in the sheet. */
+ void convertColumns();
+ /** Converts column properties. */
+ void convertColumns( OutlineLevelVec& orColLevels, const ValueRange& rColRange, const ColumnModel& rModel );
+
+ /** Converts row properties for all rows in the sheet. */
+ void convertRows(const std::vector<sc::ColRowSpan>& rSpans);
+ /** Converts row properties. */
+ void convertRows(OutlineLevelVec& orRowLevels, const ValueRange& rRowRange,
+ const RowModel& rModel,
+ const std::vector<sc::ColRowSpan>& rSpans,
+ double fDefHeight = -1.0);
+
+ /** Converts outline grouping for the passed column or row. */
+ void convertOutlines( OutlineLevelVec& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows );
+ /** Groups columns or rows for the given range. */
+ void groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapsed, bool bRows );
+
+ /** Imports the drawings of the sheet (DML, VML, DFF) and updates the used area. */
+ void finalizeDrawings();
+
+ /** Update the row import progress bar */
+ void UpdateRowProgress( const ScRange& rUsedArea, SCROW nRow );
+
+private:
+ typedef ::std::unique_ptr< VmlDrawing > VmlDrawingPtr;
+
+ const ScAddress& mrMaxApiPos; /// Reference to maximum Calc cell address from address converter.
+ ScRange maUsedArea; /// Used area of the sheet, and sheet index of the sheet.
+ ColumnModel maDefColModel; /// Default column formatting.
+ ColumnModelRangeMap maColModels; /// Ranges of columns sorted by first column index.
+ RowModel maDefRowModel; /// Default row formatting.
+ RowModelRangeMap maRowModels; /// Ranges of rows sorted by first row index.
+ std::vector< HyperlinkModel > maHyperlinks; /// Cell ranges containing hyperlinks.
+ std::vector< ValidationModel > maValidations; /// Cell ranges containing data validation settings.
+ SheetDataBuffer maSheetData; /// Buffer for cell contents and cell formatting.
+ CondFormatBuffer maCondFormats; /// Buffer for conditional formatting.
+ CommentsBuffer maComments; /// Buffer for all cell comments in this sheet.
+ AutoFilterBuffer maAutoFilters; /// Sheet auto filters (not associated to a table).
+ QueryTableBuffer maQueryTables; /// Buffer for all web query tables in this sheet.
+ WorksheetSettings maSheetSett; /// Global settings for this sheet.
+ PageSettings maPageSett; /// Page/print settings for this sheet.
+ SheetViewSettings maSheetViewSett; /// View settings for this sheet.
+ VmlDrawingPtr mxVmlDrawing; /// Collection of all VML shapes.
+ ExtLst maExtLst; /// List of extended elements
+ OUString maDrawingPath; /// Path to DrawingML fragment.
+ OUString maVmlDrawingPath; /// Path to legacy VML drawing fragment.
+ awt::Size maDrawPageSize; /// Current size of the drawing page in 1/100 mm.
+ awt::Rectangle maShapeBoundingBox; /// Bounding box for all shapes from all drawings.
+ ISegmentProgressBarRef mxProgressBar; /// Sheet progress bar.
+ bool mbFastRowProgress; /// Do we have a progress bar thread ?
+ ISegmentProgressBarRef mxRowProgress; /// Progress bar for row/cell processing.
+ ISegmentProgressBarRef mxFinalProgress; /// Progress bar for finalization.
+ WorksheetType meSheetType; /// Type of this sheet.
+ Reference< XSpreadsheet > mxSheet; /// Reference to the current sheet.
+ bool mbHasDefWidth; /// True = default column width is set from defaultColWidth attribute.
+};
+
+constexpr OUStringLiteral gaSheetCellRanges( u"com.sun.star.sheet.SheetCellRanges" ); /// Service name for a SheetCellRanges object.
+
+WorksheetGlobals::WorksheetGlobals( const WorkbookHelper& rHelper, ISegmentProgressBarRef xProgressBar, WorksheetType eSheetType, SCTAB nSheet ) :
+ WorkbookHelper( rHelper ),
+ mrMaxApiPos( rHelper.getAddressConverter().getMaxApiAddress() ),
+ maUsedArea( SCCOL_MAX, SCROW_MAX, nSheet, -1, -1, nSheet ), // Set start address to largest possible value, and end address to smallest
+ maSheetData( *this ),
+ maCondFormats( *this ),
+ maComments( *this ),
+ maAutoFilters( *this ),
+ maQueryTables( *this ),
+ maSheetSett( *this ),
+ maPageSett( *this ),
+ maSheetViewSett( *this ),
+ mxProgressBar(std::move( xProgressBar )),
+ mbFastRowProgress( false ),
+ meSheetType( eSheetType ),
+ mxSheet(getSheetFromDoc( nSheet )),
+ mbHasDefWidth( false )
+{
+ if( !mxSheet.is() )
+ maUsedArea.aStart.SetTab( -1 );
+
+ // default column settings (width and hidden state may be updated later)
+ maDefColModel.mfWidth = 8.5;
+ maDefColModel.mnXfId = -1;
+ maDefColModel.mnLevel = 0;
+ maDefColModel.mbHidden = false;
+ maDefColModel.mbCollapsed = false;
+
+ // default row settings (height and hidden state may be updated later)
+ maDefRowModel.mfHeight = 0.0;
+ maDefRowModel.mnXfId = -1;
+ maDefRowModel.mnLevel = 0;
+ maDefRowModel.mbCustomHeight = false;
+ maDefRowModel.mbCustomFormat = false;
+ maDefRowModel.mbShowPhonetic = false;
+ maDefRowModel.mbHidden = false;
+ maDefRowModel.mbCollapsed = false;
+
+ // buffers
+ mxVmlDrawing.reset( new VmlDrawing( *this ) );
+
+ // prepare progress bars
+ if( mxProgressBar )
+ {
+ mxRowProgress = mxProgressBar->createSegment( 0.5 );
+ mxFinalProgress = mxProgressBar->createSegment( 0.5 );
+ }
+}
+
+Reference< XCell > WorksheetGlobals::getCell( const ScAddress& rAddress ) const
+{
+ Reference< XCell > xCell;
+ if( mxSheet.is() ) try
+ {
+ xCell = mxSheet->getCellByPosition( rAddress.Col(), rAddress.Row() );
+ }
+ catch( Exception& )
+ {
+ }
+ return xCell;
+}
+
+Reference< XCellRange > WorksheetGlobals::getCellRange( const ScRange& rRange ) const
+{
+ Reference< XCellRange > xRange;
+ if( mxSheet.is() ) try
+ {
+ xRange = mxSheet->getCellRangeByPosition( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRange;
+}
+
+Reference< XSheetCellRanges > WorksheetGlobals::getCellRangeList( const ScRangeList& rRanges ) const
+{
+ Reference< XSheetCellRanges > xRanges;
+ if( mxSheet.is() && !rRanges.empty() ) try
+ {
+ xRanges.set( getBaseFilter().getModelFactory()->createInstance( gaSheetCellRanges ), UNO_QUERY_THROW );
+ Reference< XSheetCellRangeContainer > xRangeCont( xRanges, UNO_QUERY_THROW );
+ xRangeCont->addRangeAddresses( AddressConverter::toApiSequence(rRanges), false );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRanges;
+}
+
+Reference< XCellRange > WorksheetGlobals::getColumn( sal_Int32 nCol ) const
+{
+ Reference< XCellRange > xColumn;
+ try
+ {
+ Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
+ Reference< XTableColumns > xColumns( xColRowRange->getColumns(), UNO_SET_THROW );
+ xColumn.set( xColumns->getByIndex( nCol ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xColumn;
+}
+
+Reference< XCellRange > WorksheetGlobals::getRow( sal_Int32 nRow ) const
+{
+ Reference< XCellRange > xRow;
+ try
+ {
+ Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
+ Reference< XTableRows > xRows( xColRowRange->getRows(), UNO_SET_THROW );
+ xRow.set( xRows->getByIndex( nRow ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRow;
+}
+
+Reference< XDrawPage > WorksheetGlobals::getDrawPage() const
+{
+ Reference< XDrawPage > xDrawPage;
+ try
+ {
+ xDrawPage = Reference< XDrawPageSupplier >( mxSheet, UNO_QUERY_THROW )->getDrawPage();
+ }
+ catch( Exception& )
+ {
+ }
+ return xDrawPage;
+}
+
+const awt::Size& WorksheetGlobals::getDrawPageSize() const
+{
+ OSL_ENSURE( (maDrawPageSize.Width > 0) && (maDrawPageSize.Height > 0), "WorksheetGlobals::getDrawPageSize - called too early, size invalid" );
+ return maDrawPageSize;
+}
+
+awt::Point WorksheetGlobals::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ const tools::Rectangle aMMRect( getScDocument().GetMMRect( nCol, nRow, nCol, nRow, getSheetIndex() ) );
+ awt::Point aPoint( lclClampToNonNegativeInt32( aMMRect.Left() ),
+ lclClampToNonNegativeInt32( aMMRect.Top() ) );
+ return aPoint;
+}
+
+namespace {
+
+sal_Int32 lclGetMidAddr( sal_Int32 nBegAddr, sal_Int32 nEndAddr, sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
+{
+ // use sal_Int64 to prevent integer overflow
+ return nBegAddr + 1 + static_cast< sal_Int32 >( static_cast< sal_Int64 >( nEndAddr - nBegAddr - 2 ) * (nSearchPos - nBegPos) / (nEndPos - nBegPos) );
+}
+
+bool lclPrepareInterval( sal_Int32 nBegAddr, sal_Int32& rnMidAddr, sal_Int32 nEndAddr,
+ sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
+{
+ // searched position before nBegPos -> use nBegAddr
+ if( nSearchPos <= nBegPos )
+ {
+ rnMidAddr = nBegAddr;
+ return false;
+ }
+
+ // searched position after nEndPos, or begin next to end -> use nEndAddr
+ if( (nSearchPos >= nEndPos) || (nBegAddr + 1 >= nEndAddr) )
+ {
+ rnMidAddr = nEndAddr;
+ return false;
+ }
+
+ /* Otherwise find mid address according to position. lclGetMidAddr() will
+ return an address between nBegAddr and nEndAddr. */
+ rnMidAddr = lclGetMidAddr( nBegAddr, nEndAddr, nBegPos, nEndPos, nSearchPos );
+ return true;
+}
+
+bool lclUpdateInterval( sal_Int32& rnBegAddr, sal_Int32& rnMidAddr, sal_Int32& rnEndAddr,
+ sal_Int32& rnBegPos, sal_Int32 nMidPos, sal_Int32& rnEndPos, sal_Int32 nSearchPos )
+{
+ // nSearchPos < nMidPos: use the interval [begin,mid] in the next iteration
+ if( nSearchPos < nMidPos )
+ {
+ // if rnBegAddr is next to rnMidAddr, the latter is the column/row in question
+ if( rnBegAddr + 1 >= rnMidAddr )
+ return false;
+ // otherwise, set interval end to mid
+ rnEndPos = nMidPos;
+ rnEndAddr = rnMidAddr;
+ rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
+ return true;
+ }
+
+ // nSearchPos > nMidPos: use the interval [mid,end] in the next iteration
+ if( nSearchPos > nMidPos )
+ {
+ // if rnMidAddr is next to rnEndAddr, the latter is the column/row in question
+ if( rnMidAddr + 1 >= rnEndAddr )
+ {
+ rnMidAddr = rnEndAddr;
+ return false;
+ }
+ // otherwise, set interval start to mid
+ rnBegPos = nMidPos;
+ rnBegAddr = rnMidAddr;
+ rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
+ return true;
+ }
+
+ // nSearchPos == nMidPos: rnMidAddr is the column/row in question, do not loop anymore
+ return false;
+}
+
+} // namespace
+
+ScAddress WorksheetGlobals::getCellAddressFromPosition( const awt::Point& rPosition ) const
+{
+ // starting cell address and its position in drawing layer (top-left edge)
+ sal_Int32 nBegCol = 0;
+ sal_Int32 nBegRow = 0;
+ awt::Point aBegPos( 0, 0 );
+
+ // end cell address and its position in drawing layer (bottom-right edge)
+ sal_Int32 nEndCol = mrMaxApiPos.Col() + 1;
+ sal_Int32 nEndRow = mrMaxApiPos.Row() + 1;
+ awt::Point aEndPos( maDrawPageSize.Width, maDrawPageSize.Height );
+
+ // starting point for interval search
+ sal_Int32 nMidCol, nMidRow;
+ bool bLoopCols = lclPrepareInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aEndPos.X, rPosition.X );
+ bool bLoopRows = lclPrepareInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aEndPos.Y, rPosition.Y );
+ awt::Point aMidPos = getCellPosition( nMidCol, nMidRow );
+
+ /* The loop will find the column/row index of the cell right of/below
+ the cell containing the passed point, unless the point is located at
+ the top or left border of the containing cell. */
+ while( bLoopCols || bLoopRows )
+ {
+ bLoopCols = bLoopCols && lclUpdateInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aMidPos.X, aEndPos.X, rPosition.X );
+ bLoopRows = bLoopRows && lclUpdateInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aMidPos.Y, aEndPos.Y, rPosition.Y );
+ aMidPos = getCellPosition( nMidCol, nMidRow );
+ }
+
+ /* The cell left of/above the current search position contains the passed
+ point, unless the point is located on the top/left border of the cell,
+ or the last column/row of the sheet has been reached. */
+ if( aMidPos.X > rPosition.X ) --nMidCol;
+ if( aMidPos.Y > rPosition.Y ) --nMidRow;
+ return ScAddress( nMidCol, nMidRow, getSheetIndex() );
+}
+
+ScRange WorksheetGlobals::getCellRangeFromRectangle( const awt::Rectangle& rRect ) const
+{
+ ScAddress aStartAddr = getCellAddressFromPosition( awt::Point( rRect.X, rRect.Y ) );
+ awt::Point aBotRight( rRect.X + rRect.Width, rRect.Y + rRect.Height );
+ ScAddress aEndAddr = getCellAddressFromPosition( aBotRight );
+ bool bMultiCols = aStartAddr.Col() < aEndAddr.Col();
+ bool bMultiRows = aStartAddr.Row() < aEndAddr.Row();
+ if( bMultiCols || bMultiRows )
+ {
+ /* Reduce end position of the cell range to previous column or row, if
+ the rectangle ends exactly between two columns or rows. */
+ awt::Point aEndPos = getCellPosition( aEndAddr.Col(), aEndAddr.Row() );
+ if( bMultiCols && (aBotRight.X <= aEndPos.X) )
+ aEndAddr.IncCol(-1);
+ if( bMultiRows && (aBotRight.Y <= aEndPos.Y) )
+ aEndAddr.IncRow(-1);
+ }
+ return ScRange( aStartAddr.Col(), aStartAddr.Row(), getSheetIndex(),
+ aEndAddr.Col(), aEndAddr.Row(), getSheetIndex() );
+}
+
+void WorksheetGlobals::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
+{
+ if( rModel.mbManual && (rModel.mnColRow > 0) )
+ {
+ PropertySet aPropSet( bRowBreak ? getRow( rModel.mnColRow ) : getColumn( rModel.mnColRow ) );
+ aPropSet.setProperty( PROP_IsStartOfNewPage, true );
+ }
+}
+
+void WorksheetGlobals::setHyperlink( const HyperlinkModel& rModel )
+{
+ maHyperlinks.push_back( rModel );
+}
+
+void WorksheetGlobals::setValidation( const ValidationModel& rModel )
+{
+ maValidations.push_back( rModel );
+}
+
+void WorksheetGlobals::setDrawingPath( const OUString& rDrawingPath )
+{
+ maDrawingPath = rDrawingPath;
+}
+
+void WorksheetGlobals::setVmlDrawingPath( const OUString& rVmlDrawingPath )
+{
+ maVmlDrawingPath = rVmlDrawingPath;
+}
+
+void WorksheetGlobals::extendUsedArea( const ScAddress& rAddress )
+{
+ maUsedArea.aStart.SetCol( ::std::min( maUsedArea.aStart.Col(), rAddress.Col() ) );
+ maUsedArea.aStart.SetRow( ::std::min( maUsedArea.aStart.Row(), rAddress.Row() ) );
+ maUsedArea.aEnd.SetCol( ::std::max( maUsedArea.aEnd.Col(), rAddress.Col() ) );
+ maUsedArea.aEnd.SetRow( ::std::max( maUsedArea.aEnd.Row(), rAddress.Row() ) );
+}
+
+void WorksheetGlobals::extendUsedArea( const ScRange& rRange )
+{
+ extendUsedArea( rRange.aStart );
+ extendUsedArea( rRange.aEnd );
+}
+
+void WorksheetHelper::extendUsedArea( const ScRange& rRange )
+{
+ extendUsedArea( rRange.aStart );
+ extendUsedArea( rRange.aEnd );
+}
+
+void WorksheetGlobals::extendShapeBoundingBox( const awt::Rectangle& rShapeRect )
+{
+ if( (maShapeBoundingBox.Width == 0) && (maShapeBoundingBox.Height == 0) )
+ {
+ // width and height of maShapeBoundingBox are assumed to be zero on first cell
+ maShapeBoundingBox = rShapeRect;
+ }
+ else
+ {
+ sal_Int32 nEndX = ::std::max( maShapeBoundingBox.X + maShapeBoundingBox.Width, rShapeRect.X + rShapeRect.Width );
+ sal_Int32 nEndY = ::std::max( maShapeBoundingBox.Y + maShapeBoundingBox.Height, rShapeRect.Y + rShapeRect.Height );
+ maShapeBoundingBox.X = ::std::min( maShapeBoundingBox.X, rShapeRect.X );
+ maShapeBoundingBox.Y = ::std::min( maShapeBoundingBox.Y, rShapeRect.Y );
+ maShapeBoundingBox.Width = nEndX - maShapeBoundingBox.X;
+ maShapeBoundingBox.Height = nEndY - maShapeBoundingBox.Y;
+ }
+}
+
+void WorksheetGlobals::setBaseColumnWidth( sal_Int32 nWidth )
+{
+ // do not modify width, if setDefaultColumnWidth() has been used
+ if( !mbHasDefWidth && (nWidth > 0) )
+ {
+ // #i3006# add 5 pixels padding to the width
+ maDefColModel.mfWidth = nWidth + getUnitConverter().scaleValue( 5, Unit::ScreenX, Unit::Digit );
+ }
+}
+
+void WorksheetGlobals::setDefaultColumnWidth( double fWidth )
+{
+ // overrides a width set with setBaseColumnWidth()
+ if( fWidth > 0.0 )
+ {
+ maDefColModel.mfWidth = fWidth;
+ mbHasDefWidth = true;
+ }
+}
+
+void WorksheetGlobals::setColumnModel( const ColumnModel& rModel )
+{
+ // convert 1-based OOXML column indexes to 0-based API column indexes
+ sal_Int32 nFirstCol = rModel.maRange.mnFirst - 1;
+ sal_Int32 nLastCol = rModel.maRange.mnLast - 1;
+ if( !(getAddressConverter().checkCol( nFirstCol, true ) && (nFirstCol <= nLastCol)) )
+ return;
+
+ // Validate last column index.
+ // If last column is equal to last possible column, Excel adds one
+ // more. We do that also in XclExpColinfo::SaveXml() and for 1024 end
+ // up with 1025 instead, which would lead to excess columns in
+ // checkCol(). Cater for this oddity.
+ if (nLastCol == mrMaxApiPos.Col() + 1)
+ --nLastCol;
+ // This is totally fouled up. If we saved 1025 and the file is saved
+ // with Excel again, it increments the value to 1026.
+ else if (nLastCol == mrMaxApiPos.Col() + 2)
+ nLastCol -= 2;
+ // Excel may add a column range for the remaining columns (with
+ // <cols><col .../></cols>), even if not used or only used to grey out
+ // columns in page break view. Don't let that trigger overflow warning,
+ // so check for the last possible column. If there really is content in
+ // the range that should be caught anyway.
+ else if (nLastCol == getAddressConverter().getMaxXlsAddress().Col())
+ nLastCol = mrMaxApiPos.Col();
+ // User may have applied custom column widths to arbitrary excess
+ // columns. Ignore those and don't track as overflow columns (false).
+ // Effectively this does the same as the above cases, just keep them
+ // for explanation.
+ // Actual data present should trigger the overflow detection later.
+ else if( !getAddressConverter().checkCol( nLastCol, false ) )
+ nLastCol = mrMaxApiPos.Col();
+ // try to find entry in column model map that is able to merge with the passed model
+ bool bInsertModel = true;
+ if( !maColModels.empty() )
+ {
+ // find first column model range following nFirstCol (nFirstCol < aIt->first), or end of map
+ ColumnModelRangeMap::iterator aIt = maColModels.upper_bound( nFirstCol );
+ OSL_ENSURE( aIt == maColModels.end(), "WorksheetGlobals::setColModel - columns are unsorted" );
+ // if inserting before another column model, get last free column
+ OSL_ENSURE( (aIt == maColModels.end()) || (nLastCol < aIt->first), "WorksheetGlobals::setColModel - multiple models of the same column" );
+ if( aIt != maColModels.end() )
+ nLastCol = ::std::min( nLastCol, aIt->first - 1 );
+ if( aIt != maColModels.begin() )
+ {
+ // go to previous map element (which may be able to merge with the passed model)
+ --aIt;
+ // the usage of upper_bound() above ensures that aIt->first is less than or equal to nFirstCol now
+ sal_Int32& rnLastMapCol = aIt->second.second;
+ OSL_ENSURE( rnLastMapCol < nFirstCol, "WorksheetGlobals::setColModel - multiple models of the same column" );
+ nFirstCol = ::std::max( rnLastMapCol + 1, nFirstCol );
+ if( (rnLastMapCol + 1 == nFirstCol) && (nFirstCol <= nLastCol) && aIt->second.first.isMergeable( rModel ) )
+ {
+ // can merge with existing model, update last column index
+ rnLastMapCol = nLastCol;
+ bInsertModel = false;
+ }
+ }
+ }
+ if( nFirstCol <= nLastCol )
+ {
+ // insert the column model, if it has not been merged with another
+ if( bInsertModel )
+ maColModels[ nFirstCol ] = ColumnModelRange( rModel, nLastCol );
+ // set column formatting directly
+ convertColumnFormat( nFirstCol, nLastCol, rModel.mnXfId );
+ }
+}
+
+void WorksheetGlobals::convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId )
+{
+ ScRange aRange( nFirstCol, 0, getSheetIndex(), nLastCol, mrMaxApiPos.Row(), getSheetIndex() );
+ if( getAddressConverter().validateCellRange( aRange, true, false ) )
+ {
+ const StylesBuffer& rStyles = getStyles();
+
+ // Set cell styles via direct API - the preferred approach.
+ ScDocumentImport& rDoc = getDocImport();
+ rStyles.writeCellXfToDoc(rDoc, aRange, nXfId);
+ }
+}
+
+void WorksheetGlobals::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
+{
+ maDefRowModel.mfHeight = fHeight;
+ maDefRowModel.mbCustomHeight = bCustomHeight;
+ maDefRowModel.mbHidden = bHidden;
+ maDefRowModel.mbThickTop = bThickTop;
+ maDefRowModel.mbThickBottom = bThickBottom;
+}
+
+void WorksheetGlobals::setRowModel( const RowModel& rModel )
+{
+ // convert 1-based OOXML row index to 0-based API row index
+ sal_Int32 nRow = rModel.mnRow - 1;
+ if( getAddressConverter().checkRow( nRow, true ) )
+ {
+ // try to find entry in row model map that is able to merge with the passed model
+ bool bInsertModel = true;
+ bool bUnusedRow = true;
+ if( !maRowModels.empty() )
+ {
+ // find first row model range following nRow (nRow < aIt->first), or end of map
+ RowModelRangeMap::iterator aIt = maRowModels.upper_bound( nRow );
+ OSL_ENSURE( aIt == maRowModels.end(), "WorksheetGlobals::setRowModel - rows are unsorted" );
+ if( aIt != maRowModels.begin() )
+ {
+ // go to previous map element (which may be able to merge with the passed model)
+ --aIt;
+ // the usage of upper_bound() above ensures that aIt->first is less than or equal to nRow now
+ sal_Int32& rnLastMapRow = aIt->second.second;
+ bUnusedRow = rnLastMapRow < nRow;
+ OSL_ENSURE( bUnusedRow, "WorksheetGlobals::setRowModel - multiple models of the same row" );
+ if( (rnLastMapRow + 1 == nRow) && aIt->second.first.isMergeable( rModel ) )
+ {
+ // can merge with existing model, update last row index
+ ++rnLastMapRow;
+ bInsertModel = false;
+ }
+ }
+ }
+ if( bUnusedRow )
+ {
+ // insert the row model, if it has not been merged with another
+ if( bInsertModel )
+ maRowModels[ nRow ] = RowModelRange( rModel, nRow );
+ // set row formatting
+ maSheetData.setRowFormat( nRow, rModel.mnXfId, rModel.mbCustomFormat );
+ }
+ }
+
+ UpdateRowProgress( maUsedArea, nRow );
+}
+
+// This is called at a higher frequency inside the (threaded) inner loop.
+void WorksheetGlobals::UpdateRowProgress( const ScRange& rUsedArea, SCROW nRow )
+{
+ if (!mxRowProgress || nRow < rUsedArea.aStart.Row() || rUsedArea.aEnd.Row() < nRow)
+ return;
+
+ double fNewPos = static_cast<double>(nRow - rUsedArea.aStart.Row() + 1.0) / (rUsedArea.aEnd.Row() - rUsedArea.aStart.Row() + 1.0);
+
+ if (mbFastRowProgress)
+ mxRowProgress->setPosition(fNewPos);
+ else
+ {
+ double fCurPos = mxRowProgress->getPosition();
+ if (fCurPos < fNewPos && (fNewPos - fCurPos) > 0.3)
+ // Try not to re-draw progress bar too frequently.
+ mxRowProgress->setPosition(fNewPos);
+ }
+}
+
+void WorksheetGlobals::initializeWorksheetImport()
+{
+ // set default cell style for unused cells
+ ScDocumentImport& rDoc = getDocImport();
+
+ ScStyleSheet* pStyleSheet =
+ static_cast<ScStyleSheet*>(rDoc.getDoc().GetStyleSheetPool()->Find(
+ getStyles().getDefaultStyleName(), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rDoc.setCellStyleToSheet(getSheetIndex(), *pStyleSheet);
+
+ /* Remember the current sheet index in global data, needed by global
+ objects, e.g. the chart converter. */
+ setCurrentSheetIndex( getSheetIndex() );
+}
+
+void WorksheetGlobals::finalizeWorksheetImport()
+{
+ lclUpdateProgressBar( mxRowProgress, 1.0 );
+ maSheetData.finalizeImport();
+
+ getCondFormats().finalizeImport();
+ lclUpdateProgressBar( mxFinalProgress, 0.25 );
+ finalizeHyperlinkRanges();
+ finalizeValidationRanges();
+ maAutoFilters.finalizeImport( getSheetIndex() );
+ maQueryTables.finalizeImport();
+ maSheetSett.finalizeImport();
+ maPageSett.finalizeImport();
+ maSheetViewSett.finalizeImport();
+
+ lclUpdateProgressBar( mxFinalProgress, 0.5 );
+ convertColumns();
+
+ // tdf#99913 rows hidden by filter need extra flag
+ ScDocument& rDoc = getScDocument();
+ std::vector<sc::ColRowSpan> aSpans;
+ SCTAB nTab = getSheetIndex();
+
+ ScTable* pTable = rDoc.FetchTable(nTab);
+ if (pTable)
+ pTable->SetOptimalMinRowHeight(maDefRowModel.mfHeight * 20); // in TWIPS
+
+ ScDBData* pDBData = rDoc.GetAnonymousDBData(nTab);
+ if (pDBData && pDBData->HasAutoFilter())
+ {
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ if (!pDocColl->empty())
+ {
+ ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ if (rxDB->GetTab() == nTab && rxDB->HasAutoFilter())
+ {
+ ScRange aRange;
+ rxDB->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ }
+ }
+ convertRows(aSpans);
+ lclUpdateProgressBar( mxFinalProgress, 1.0 );
+}
+
+void WorksheetGlobals::finalizeDrawingImport()
+{
+ finalizeDrawings();
+
+ // forget current sheet index in global data
+ setCurrentSheetIndex( -1 );
+}
+
+// private --------------------------------------------------------------------
+
+void WorksheetGlobals::finalizeHyperlinkRanges()
+{
+ for (auto const& link : maHyperlinks)
+ {
+ OUString aUrl = getHyperlinkUrl(link);
+ // try to insert URL into each cell of the range
+ if( !aUrl.isEmpty() )
+ for( ScAddress aAddress(link.maRange.aStart.Col(), link.maRange.aStart.Row(), getSheetIndex() ); aAddress.Row() <= link.maRange.aEnd.Row(); aAddress.IncRow() )
+ for( aAddress.SetCol(link.maRange.aStart.Col()); aAddress.Col() <= link.maRange.aEnd.Col(); aAddress.IncCol() )
+ insertHyperlink( aAddress, aUrl );
+ }
+}
+
+OUString WorksheetGlobals::getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const
+{
+ OUStringBuffer aUrlBuffer;
+ if( !rHyperlink.maTarget.isEmpty() )
+ aUrlBuffer.append( getBaseFilter().getAbsoluteUrl( rHyperlink.maTarget ) );
+ if( !rHyperlink.maLocation.isEmpty() )
+ aUrlBuffer.append( "#" + rHyperlink.maLocation );
+ OUString aUrl = aUrlBuffer.makeStringAndClear();
+
+ if( aUrl.startsWith("#") )
+ {
+ sal_Int32 nSepPos = aUrl.lastIndexOf( '!' );
+ if( nSepPos > 0 )
+ {
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well. Hyperlink
+ // handler has to handle all, but prefer '#SheetName.A1' if
+ // possible.
+ if (nSepPos < aUrl.getLength() - 1)
+ {
+ ScRange aRange;
+ const ScDocumentImport& rDoc = getDocImport();
+ if ((aRange.ParseAny( aUrl.copy( nSepPos + 1 ), rDoc.getDoc(),
+ formula::FormulaGrammar::CONV_XL_R1C1)
+ & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ aUrl = aUrl.replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ) );
+ }
+ // #i66592# convert sheet names that have been renamed on import
+ OUString aSheetName = aUrl.copy( 1, nSepPos - 1 );
+ OUString aCalcName = getWorksheets().getCalcSheetName( aSheetName );
+ if( !aCalcName.isEmpty() )
+ aUrl = aUrl.replaceAt( 1, nSepPos - 1, aCalcName );
+ }
+ }
+
+ return aUrl;
+}
+
+void WorksheetGlobals::insertHyperlink( const ScAddress& rAddress, const OUString& rUrl )
+{
+ ScDocumentImport& rDoc = getDocImport();
+ ScRefCellValue aCell(rDoc.getDoc(), rAddress);
+
+ if (aCell.getType() == CELLTYPE_STRING || aCell.getType() == CELLTYPE_EDIT)
+ {
+ OUString aStr = aCell.getString(&rDoc.getDoc());
+ ScFieldEditEngine& rEE = rDoc.getDoc().GetEditEngine();
+ rEE.Clear();
+
+ SvxURLField aURLField(rUrl, aStr, SvxURLFormat::Repr);
+ SvxFieldItem aURLItem(aURLField, EE_FEATURE_FIELD);
+ rEE.QuickInsertField(aURLItem, ESelection());
+
+ rDoc.setEditCell(rAddress, rEE.CreateTextObject());
+ }
+ else
+ {
+ // Handle other cell types e.g. formulas ( and ? ) that have associated
+ // hyperlinks.
+ // Ideally all hyperlinks should be treated as below. For the moment,
+ // given the current absence of ods support lets just handle what we
+ // previously didn't handle the new way.
+ // Unfortunately we won't be able to preserve such hyperlinks when
+ // saving to ods. Note: when we are able to save such hyperlinks to ods
+ // we should handle *all* imported hyperlinks as below ( e.g. as cell
+ // attribute ) for better interoperability.
+
+ SfxStringItem aItem(ATTR_HYPERLINK, rUrl);
+ rDoc.getDoc().ApplyAttr(rAddress.Col(), rAddress.Row(), rAddress.Tab(), aItem);
+ }
+}
+
+void WorksheetGlobals::finalizeValidationRanges() const
+{
+ for (auto const& validation : maValidations)
+ {
+ PropertySet aPropSet( getCellRangeList(validation.maRanges) );
+
+ Reference< XPropertySet > xValidation( aPropSet.getAnyProperty( PROP_Validation ), UNO_QUERY );
+ if( xValidation.is() )
+ {
+ PropertySet aValProps( xValidation );
+
+ try
+ {
+ const OUString aToken = validation.msRef.getToken( 0, ' ' );
+
+ Reference<XSpreadsheet> xSheet = getSheetFromDoc( getCurrentSheetIndex() );
+ Reference<XCellRange> xDBCellRange;
+ Reference<XCell> xCell;
+ xDBCellRange = xSheet->getCellRangeByName( aToken );
+
+ xCell = xDBCellRange->getCellByPosition( 0, 0 );
+ Reference<XCellAddressable> xCellAddressable( xCell, UNO_QUERY_THROW );
+ CellAddress aFirstCell = xCellAddressable->getCellAddress();
+ Reference<XSheetCondition> xCondition( xValidation, UNO_QUERY_THROW );
+ xCondition->setSourcePosition( aFirstCell );
+ }
+ catch(const Exception&)
+ {
+ }
+
+ // convert validation type to API enum
+ ValidationType eType = ValidationType_ANY;
+ switch( validation.mnType )
+ {
+ case XML_custom: eType = ValidationType_CUSTOM; break;
+ case XML_date: eType = ValidationType_DATE; break;
+ case XML_decimal: eType = ValidationType_DECIMAL; break;
+ case XML_list: eType = ValidationType_LIST; break;
+ case XML_none: eType = ValidationType_ANY; break;
+ case XML_textLength: eType = ValidationType_TEXT_LEN; break;
+ case XML_time: eType = ValidationType_TIME; break;
+ case XML_whole: eType = ValidationType_WHOLE; break;
+ default: OSL_FAIL( "WorksheetData::finalizeValidationRanges - unknown validation type" );
+ }
+ aValProps.setProperty( PROP_Type, eType );
+
+ // convert error alert style to API enum
+ ValidationAlertStyle eAlertStyle = ValidationAlertStyle_STOP;
+ switch( validation.mnErrorStyle )
+ {
+ case XML_information: eAlertStyle = ValidationAlertStyle_INFO; break;
+ case XML_stop: eAlertStyle = ValidationAlertStyle_STOP; break;
+ case XML_warning: eAlertStyle = ValidationAlertStyle_WARNING; break;
+ default: OSL_FAIL( "WorksheetData::finalizeValidationRanges - unknown error style" );
+ }
+ aValProps.setProperty( PROP_ErrorAlertStyle, eAlertStyle );
+
+ // convert dropdown style to API visibility constants
+ sal_Int16 nVisibility = validation.mbNoDropDown ? TableValidationVisibility::INVISIBLE : TableValidationVisibility::UNSORTED;
+ aValProps.setProperty( PROP_ShowList, nVisibility );
+
+ // messages
+ aValProps.setProperty( PROP_ShowInputMessage, validation.mbShowInputMsg );
+ aValProps.setProperty( PROP_InputTitle, validation.maInputTitle );
+ aValProps.setProperty( PROP_InputMessage, validation.maInputMessage );
+ aValProps.setProperty( PROP_ShowErrorMessage, validation.mbShowErrorMsg );
+ aValProps.setProperty( PROP_ErrorTitle, validation.maErrorTitle );
+ aValProps.setProperty( PROP_ErrorMessage, validation.maErrorMessage );
+
+ // allow blank cells
+ aValProps.setProperty( PROP_IgnoreBlankCells, validation.mbAllowBlank );
+
+ try
+ {
+ // condition operator
+ Reference< XSheetCondition2 > xSheetCond( xValidation, UNO_QUERY_THROW );
+ if( eType == ValidationType_CUSTOM )
+ xSheetCond->setConditionOperator( ConditionOperator2::FORMULA );
+ else
+ xSheetCond->setConditionOperator( CondFormatBuffer::convertToApiOperator( validation.mnOperator ) );
+
+ // condition formulas
+ Reference< XMultiFormulaTokens > xTokens( xValidation, UNO_QUERY_THROW );
+ xTokens->setTokens( 0, validation.maTokens1 );
+ xTokens->setTokens( 1, validation.maTokens2 );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // write back validation settings to cell range(s)
+ aPropSet.setProperty( PROP_Validation, xValidation );
+ }
+ }
+}
+
+void WorksheetGlobals::convertColumns()
+{
+ sal_Int32 nNextCol = 0;
+ sal_Int32 nMaxCol = mrMaxApiPos.Col();
+ // stores first grouped column index for each level
+ OutlineLevelVec aColLevels;
+
+ for (auto const& colModel : maColModels)
+ {
+ // column indexes are stored 0-based in maColModels
+ ValueRange aColRange( ::std::max( colModel.first, nNextCol ), ::std::min( colModel.second.second, nMaxCol ) );
+ // process gap between two column models, use default column model
+ if( nNextCol < aColRange.mnFirst )
+ convertColumns( aColLevels, ValueRange( nNextCol, aColRange.mnFirst - 1 ), maDefColModel );
+ // process the column model
+ convertColumns( aColLevels, aColRange, colModel.second.first );
+ // cache next column to be processed
+ nNextCol = aColRange.mnLast + 1;
+ }
+
+ // remaining default columns to end of sheet
+ convertColumns( aColLevels, ValueRange( nNextCol, nMaxCol ), maDefColModel );
+ // close remaining column outlines spanning to end of sheet
+ convertOutlines( aColLevels, nMaxCol + 1, 0, false, false );
+}
+
+void WorksheetGlobals::convertColumns( OutlineLevelVec& orColLevels,
+ const ValueRange& rColRange, const ColumnModel& rModel )
+{
+ // column width: convert 'number of characters' to column width in twips
+ sal_Int32 nWidth = std::round(getUnitConverter().scaleValue( rModel.mfWidth, Unit::Digit, Unit::Twip ));
+
+ SCTAB nTab = getSheetIndex();
+ ScDocument& rDoc = getScDocument();
+ SCCOL nStartCol = rColRange.mnFirst;
+ SCCOL nEndCol = rColRange.mnLast;
+
+ if( nWidth > 0 )
+ {
+ // macro sheets have double width
+ if( meSheetType == WorksheetType::Macro )
+ nWidth *= 2;
+
+ for( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ rDoc.SetColWidthOnly(nCol, nTab, nWidth);
+ }
+ }
+
+ // hidden columns: TODO: #108683# hide columns later?
+ if( rModel.mbHidden )
+ {
+ rDoc.SetColHidden( nStartCol, nEndCol, nTab, true );
+ }
+
+ // outline settings for this column range
+ convertOutlines( orColLevels, rColRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, false );
+}
+
+void WorksheetGlobals::convertRows(const std::vector<sc::ColRowSpan>& rSpans)
+{
+ sal_Int32 nNextRow = 0;
+ sal_Int32 nMaxRow = mrMaxApiPos.Row();
+ // stores first grouped row index for each level
+ OutlineLevelVec aRowLevels;
+
+ for (auto const& rowModel : maRowModels)
+ {
+ // row indexes are stored 0-based in maRowModels
+ ValueRange aRowRange( ::std::max( rowModel.first, nNextRow ), ::std::min( rowModel.second.second, nMaxRow ) );
+ // process gap between two row models, use default row model
+ if( nNextRow < aRowRange.mnFirst )
+ convertRows(aRowLevels, ValueRange(nNextRow, aRowRange.mnFirst - 1), maDefRowModel,
+ rSpans);
+ // process the row model
+ convertRows(aRowLevels, aRowRange, rowModel.second.first, rSpans, maDefRowModel.mfHeight);
+ // cache next row to be processed
+ nNextRow = aRowRange.mnLast + 1;
+ }
+
+ // remaining default rows to end of sheet
+ convertRows(aRowLevels, ValueRange(nNextRow, nMaxRow), maDefRowModel, rSpans);
+ // close remaining row outlines spanning to end of sheet
+ convertOutlines( aRowLevels, nMaxRow + 1, 0, false, true );
+}
+
+void WorksheetGlobals::convertRows(OutlineLevelVec& orRowLevels, const ValueRange& rRowRange,
+ const RowModel& rModel,
+ const std::vector<sc::ColRowSpan>& rSpans, double fDefHeight)
+{
+ // row height: convert points to row height in twips
+ double fHeight = (rModel.mfHeight >= 0.0) ? rModel.mfHeight : fDefHeight;
+ sal_Int32 nHeight = std::round(o3tl::toTwips( fHeight, o3tl::Length::pt ));
+ SCROW nStartRow = rRowRange.mnFirst;
+ SCROW nEndRow = rRowRange.mnLast;
+ SCTAB nTab = getSheetIndex();
+ if( nHeight > 0 )
+ {
+ /* always import the row height, ensures better layout */
+ ScDocument& rDoc = getScDocument();
+ rDoc.SetRowHeightOnly(nStartRow, nEndRow, nTab, nHeight);
+ if(rModel.mbCustomHeight)
+ rDoc.SetManualHeight( nStartRow, nEndRow, nTab, true );
+ }
+
+ // hidden rows: TODO: #108683# hide rows later?
+ if( rModel.mbHidden )
+ {
+ ScDocument& rDoc = getScDocument();
+ rDoc.SetRowHidden( nStartRow, nEndRow, nTab, true );
+ for (const auto& rSpan : rSpans)
+ {
+ // tdf#99913 rows hidden by filter need extra flag
+ if (rSpan.mnStart <= nStartRow && nStartRow <= rSpan.mnEnd)
+ {
+ SCROW nLast = ::std::min(nEndRow, rSpan.mnEnd);
+ rDoc.SetRowFiltered(nStartRow, nLast, nTab, true);
+ break;
+ }
+ }
+ }
+
+ // outline settings for this row range
+ convertOutlines( orRowLevels, rRowRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, true );
+}
+
+void WorksheetGlobals::convertOutlines( OutlineLevelVec& orLevels,
+ sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows )
+{
+ /* It is ensured from caller functions, that this function is called
+ without any gaps between the processed column or row ranges. */
+
+ OSL_ENSURE( nLevel >= 0, "WorksheetGlobals::convertOutlines - negative outline level" );
+ nLevel = ::std::max< sal_Int32 >( nLevel, 0 );
+
+ sal_Int32 nSize = orLevels.size();
+ if( nSize < nLevel )
+ {
+ // Outline level increased. Push the begin column position.
+ orLevels.insert(orLevels.end(), nLevel - nSize, nColRow);
+ }
+ else if( nLevel < nSize )
+ {
+ // Outline level decreased. Pop them all out.
+ for( sal_Int32 nIndex = nLevel; nIndex < nSize; ++nIndex )
+ {
+ sal_Int32 nFirstInLevel = orLevels.back();
+ orLevels.pop_back();
+ groupColumnsOrRows( nFirstInLevel, nColRow - 1, bCollapsed, bRows );
+ bCollapsed = false; // collapse only once
+ }
+ }
+}
+
+void WorksheetGlobals::groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapse, bool bRows )
+{
+ try
+ {
+ Reference< XSheetOutline > xOutline( mxSheet, UNO_QUERY_THROW );
+ if( bRows )
+ {
+ CellRangeAddress aRange( getSheetIndex(), 0, nFirstColRow, 0, nLastColRow );
+ xOutline->group( aRange, TableOrientation_ROWS );
+ if( bCollapse )
+ xOutline->hideDetail( aRange );
+ }
+ else
+ {
+ CellRangeAddress aRange( getSheetIndex(), nFirstColRow, 0, nLastColRow, 0 );
+ xOutline->group( aRange, TableOrientation_COLUMNS );
+ if( bCollapse )
+ xOutline->hideDetail( aRange );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void WorksheetGlobals::finalizeDrawings()
+{
+ // calculate the current drawing page size (after rows/columns are imported)
+ const Size aPageSize( getScDocument().GetMMRect( 0, 0, mrMaxApiPos.Col(), mrMaxApiPos.Row(), getSheetIndex() ).GetSize() );
+ maDrawPageSize.Width = lclClampToNonNegativeInt32( aPageSize.Width() );
+ maDrawPageSize.Height = lclClampToNonNegativeInt32( aPageSize.Height() );
+
+ // import DML and VML
+ if( !maDrawingPath.isEmpty() )
+ importOoxFragment( new DrawingFragment( *this, maDrawingPath ) );
+ if( !maVmlDrawingPath.isEmpty() )
+ importOoxFragment( new VmlDrawingFragment( *this, maVmlDrawingPath ) );
+
+ // comments (after callout shapes have been imported from VML/DFF)
+ maComments.finalizeImport();
+
+ /* Extend used area of the sheet by cells covered with drawing objects.
+ Needed if the imported document is inserted as "OLE object from file"
+ and thus does not provide an OLE size property by itself. */
+ if( (maShapeBoundingBox.Width > 0) || (maShapeBoundingBox.Height > 0) )
+ {
+ ScRange aRange(getCellRangeFromRectangle(maShapeBoundingBox));
+ if (aRange.aStart.Col() < 0)
+ aRange.aStart.SetCol(0);
+ if (aRange.aStart.Row() < 0)
+ aRange.aStart.SetRow(0);
+ if (aRange.aEnd.Col() < 0)
+ aRange.aEnd.SetCol(0);
+ if (aRange.aEnd.Row() < 0)
+ aRange.aEnd.SetRow(0);
+ extendUsedArea(aRange);
+ }
+
+ // if no used area is set, default to A1
+ if( maUsedArea.aStart.Col() > maUsedArea.aEnd.Col() )
+ {
+ maUsedArea.aStart.SetCol( 0 );
+ maUsedArea.aEnd.SetCol( 0 );
+ }
+
+ if( maUsedArea.aStart.Row() > maUsedArea.aEnd.Row() )
+ {
+ maUsedArea.aStart.SetRow( 0 );
+ maUsedArea.aEnd.SetRow( 0 );
+ }
+
+ /* Register the used area of this sheet in global view settings. The
+ global view settings will set the visible area if this document is an
+ embedded OLE object. */
+ getViewSettings().setSheetUsedArea( maUsedArea );
+
+ /* #i103686# Set right-to-left sheet layout. Must be done after all
+ drawing shapes to simplify calculation of shape coordinates. */
+ if( maSheetViewSett.isSheetRightToLeft() )
+ {
+ PropertySet aPropSet( mxSheet );
+ aPropSet.setProperty( PROP_TableLayout, WritingMode2::RL_TB );
+ }
+}
+
+WorksheetHelper::WorksheetHelper( WorksheetGlobals& rSheetGlob ) :
+ WorkbookHelper( rSheetGlob ),
+ mrSheetGlob( rSheetGlob )
+{
+}
+
+ScDocument& WorksheetHelper::getScDocument()
+{
+ return getDocImport().getDoc();
+}
+
+/*static*/ WorksheetGlobalsRef WorksheetHelper::constructGlobals( const WorkbookHelper& rHelper,
+ const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, SCTAB nSheet )
+{
+ WorksheetGlobalsRef xSheetGlob = std::make_shared<WorksheetGlobals>( rHelper, rxProgressBar, eSheetType, nSheet );
+ if( !xSheetGlob->isValidSheet() )
+ xSheetGlob.reset();
+ return xSheetGlob;
+}
+
+/* static */ IWorksheetProgress *WorksheetHelper::getWorksheetInterface( const WorksheetGlobalsRef &xRef )
+{
+ return static_cast< IWorksheetProgress *>( xRef.get() );
+}
+
+WorksheetType WorksheetHelper::getSheetType() const
+{
+ return mrSheetGlob.getSheetType();
+}
+
+SCTAB WorksheetHelper::getSheetIndex() const
+{
+ return mrSheetGlob.getSheetIndex();
+}
+
+const Reference< XSpreadsheet >& WorksheetHelper::getSheet() const
+{
+ return mrSheetGlob.getSheet();
+}
+
+Reference< XCell > WorksheetHelper::getCell( const ScAddress& rAddress ) const
+{
+ return mrSheetGlob.getCell( rAddress );
+}
+
+Reference< XCellRange > WorksheetHelper::getCellRange( const ScRange& rRange ) const
+{
+ return mrSheetGlob.getCellRange( rRange );
+}
+
+Reference< XDrawPage > WorksheetHelper::getDrawPage() const
+{
+ return mrSheetGlob.getDrawPage();
+}
+
+awt::Point WorksheetHelper::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return mrSheetGlob.getCellPosition( nCol, nRow );
+}
+
+const awt::Size& WorksheetHelper::getDrawPageSize() const
+{
+ return mrSheetGlob.getDrawPageSize();
+}
+
+SheetDataBuffer& WorksheetHelper::getSheetData() const
+{
+ return mrSheetGlob.getSheetData();
+}
+
+CondFormatBuffer& WorksheetHelper::getCondFormats() const
+{
+ return mrSheetGlob.getCondFormats();
+}
+
+CommentsBuffer& WorksheetHelper::getComments() const
+{
+ return mrSheetGlob.getComments();
+}
+
+AutoFilterBuffer& WorksheetHelper::getAutoFilters() const
+{
+ return mrSheetGlob.getAutoFilters();
+}
+
+QueryTableBuffer& WorksheetHelper::getQueryTables() const
+{
+ return mrSheetGlob.getQueryTables();
+}
+
+WorksheetSettings& WorksheetHelper::getWorksheetSettings() const
+{
+ return mrSheetGlob.getWorksheetSettings();
+}
+
+PageSettings& WorksheetHelper::getPageSettings() const
+{
+ return mrSheetGlob.getPageSettings();
+}
+
+SheetViewSettings& WorksheetHelper::getSheetViewSettings() const
+{
+ return mrSheetGlob.getSheetViewSettings();
+}
+
+VmlDrawing& WorksheetHelper::getVmlDrawing() const
+{
+ return mrSheetGlob.getVmlDrawing();
+}
+
+ExtLst& WorksheetHelper::getExtLst() const
+{
+ return mrSheetGlob.getExtLst();
+}
+
+void WorksheetHelper::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
+{
+ mrSheetGlob.setPageBreak( rModel, bRowBreak );
+}
+
+void WorksheetHelper::setHyperlink( const HyperlinkModel& rModel )
+{
+ mrSheetGlob.setHyperlink( rModel );
+}
+
+void WorksheetHelper::setValidation( const ValidationModel& rModel )
+{
+ mrSheetGlob.setValidation( rModel );
+}
+
+void WorksheetHelper::setDrawingPath( const OUString& rDrawingPath )
+{
+ mrSheetGlob.setDrawingPath( rDrawingPath );
+}
+
+void WorksheetHelper::setVmlDrawingPath( const OUString& rVmlDrawingPath )
+{
+ mrSheetGlob.setVmlDrawingPath( rVmlDrawingPath );
+}
+
+void WorksheetHelper::extendUsedArea( const ScAddress& rAddress )
+{
+ mrSheetGlob.extendUsedArea( rAddress );
+}
+
+void WorksheetHelper::extendShapeBoundingBox( const awt::Rectangle& rShapeRect )
+{
+ mrSheetGlob.extendShapeBoundingBox( rShapeRect );
+}
+
+void WorksheetHelper::setBaseColumnWidth( sal_Int32 nWidth )
+{
+ mrSheetGlob.setBaseColumnWidth( nWidth );
+}
+
+void WorksheetHelper::setDefaultColumnWidth( double fWidth )
+{
+ mrSheetGlob.setDefaultColumnWidth( fWidth );
+}
+
+void WorksheetHelper::setColumnModel( const ColumnModel& rModel )
+{
+ mrSheetGlob.setColumnModel( rModel );
+}
+
+void WorksheetHelper::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
+{
+ mrSheetGlob.setDefaultRowSettings( fHeight, bCustomHeight, bHidden, bThickTop, bThickBottom );
+}
+
+void WorksheetHelper::setRowModel( const RowModel& rModel )
+{
+ mrSheetGlob.setRowModel( rModel );
+}
+
+void WorksheetHelper::setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType )
+{
+ getFormulaBuffer().setCellFormulaValue(rAddress, rValueStr, nCellType);
+}
+
+void WorksheetHelper::putRichString( const ScAddress& rAddress, RichString& rString, const oox::xls::Font* pFirstPortionFont, bool bSingleLine )
+{
+ ScEditEngineDefaulter& rEE = getEditEngine();
+
+ rEE.SetSingleLine(bSingleLine);
+
+ // The cell will own the text object instance returned from convert().
+ getDocImport().setEditCell(rAddress, rString.convert(rEE, pFirstPortionFont));
+
+ rEE.SetSingleLine(false);
+}
+
+void WorksheetHelper::putFormulaTokens( const ScAddress& rAddress, const ApiTokenSequence& rTokens )
+{
+ ScDocumentImport& rDoc = getDocImport();
+ std::unique_ptr<ScTokenArray> pTokenArray(new ScTokenArray(rDoc.getDoc()));
+ ScTokenConversion::ConvertToTokenArray(rDoc.getDoc(), *pTokenArray, rTokens);
+ rDoc.setFormulaCell(rAddress, std::move(pTokenArray));
+}
+
+void WorksheetHelper::initializeWorksheetImport()
+{
+ mrSheetGlob.initializeWorksheetImport();
+}
+
+void WorksheetHelper::finalizeWorksheetImport()
+{
+ mrSheetGlob.finalizeWorksheetImport();
+}
+
+void WorksheetHelper::finalizeDrawingImport()
+{
+ mrSheetGlob.finalizeDrawingImport();
+}
+
+void WorksheetHelper::setCellFormula( const ScAddress& rTokenAddress, const OUString& rTokenStr )
+{
+ getFormulaBuffer().setCellFormula( rTokenAddress, rTokenStr );
+}
+
+void WorksheetHelper::setCellFormula(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType )
+{
+ getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType);
+}
+
+void WorksheetHelper::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr )
+{
+ getFormulaBuffer().setCellArrayFormula( rRangeAddress, rTokenAddress, rTokenStr );
+}
+
+void WorksheetHelper::createSharedFormulaMapEntry(
+ const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rTokens )
+{
+ getFormulaBuffer().createSharedFormulaMapEntry(rAddress, nSharedId, rTokens);
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheetsettings.cxx b/sc/source/filter/oox/worksheetsettings.cxx
new file mode 100644
index 0000000000..988207aa90
--- /dev/null
+++ b/sc/source/filter/oox/worksheetsettings.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 <memory>
+#include <worksheetsettings.hxx>
+
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <oox/core/binarycodec.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <pagesettings.hxx>
+#include <tabprotection.hxx>
+#include <document.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt8 BIFF12_SHEETPR_FILTERMODE = 0x01;
+
+const sal_uInt16 BIFF_SHEETPR_APPLYSTYLES = 0x0020;
+const sal_uInt16 BIFF_SHEETPR_SYMBOLSBELOW = 0x0040;
+const sal_uInt16 BIFF_SHEETPR_SYMBOLSRIGHT = 0x0080;
+const sal_uInt16 BIFF_SHEETPR_FITTOPAGES = 0x0100;
+
+} // namespace
+
+SheetSettingsModel::SheetSettingsModel() :
+ mbFilterMode( false ),
+ mbApplyStyles( false ),
+ mbSummaryBelow( true ),
+ mbSummaryRight( true )
+{
+}
+
+SheetProtectionModel::SheetProtectionModel() :
+ mnSpinCount( 0 ),
+ mnPasswordHash( 0 ),
+ mbSheet( false ),
+ mbObjects( false ),
+ mbScenarios( false ),
+ mbFormatCells( true ),
+ mbFormatColumns( true ),
+ mbFormatRows( true ),
+ mbInsertColumns( true ),
+ mbInsertRows( true ),
+ mbInsertHyperlinks( true ),
+ mbDeleteColumns( true ),
+ mbDeleteRows( true ),
+ mbSelectLocked( false ),
+ mbSort( true ),
+ mbAutoFilter( true ),
+ mbPivotTables( true ),
+ mbSelectUnlocked( false )
+{
+}
+
+WorksheetSettings::WorksheetSettings( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ maPhoneticSett( rHelper )
+{
+}
+
+void WorksheetSettings::importSheetPr( const AttributeList& rAttribs )
+{
+ maSheetSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
+ maSheetSettings.mbFilterMode = rAttribs.getBool( XML_filterMode, false );
+}
+
+void WorksheetSettings::importChartSheetPr( const AttributeList& rAttribs )
+{
+ maSheetSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
+}
+
+void WorksheetSettings::importTabColor( const AttributeList& rAttribs )
+{
+ maSheetSettings.maTabColor.importColor( rAttribs );
+}
+
+void WorksheetSettings::importOutlinePr( const AttributeList& rAttribs )
+{
+ maSheetSettings.mbApplyStyles = rAttribs.getBool( XML_applyStyles, false );
+ maSheetSettings.mbSummaryBelow = rAttribs.getBool( XML_summaryBelow, true );
+ maSheetSettings.mbSummaryRight = rAttribs.getBool( XML_summaryRight, true );
+}
+
+void WorksheetSettings::importSheetProtection( const AttributeList& rAttribs )
+{
+ maSheetProt.maAlgorithmName = rAttribs.getString( XML_algorithmName, OUString());
+ maSheetProt.maHashValue = rAttribs.getString( XML_hashValue, OUString());
+ maSheetProt.maSaltValue = rAttribs.getString( XML_saltValue, OUString());
+ maSheetProt.mnSpinCount = rAttribs.getUnsigned( XML_spinCount, 0);
+ maSheetProt.mnPasswordHash = oox::core::CodecHelper::getPasswordHash( rAttribs, XML_password );
+ maSheetProt.mbSheet = rAttribs.getBool( XML_sheet, false );
+ maSheetProt.mbObjects = rAttribs.getBool( XML_objects, false );
+ maSheetProt.mbScenarios = rAttribs.getBool( XML_scenarios, false );
+ maSheetProt.mbFormatCells = rAttribs.getBool( XML_formatCells, true );
+ maSheetProt.mbFormatColumns = rAttribs.getBool( XML_formatColumns, true );
+ maSheetProt.mbFormatRows = rAttribs.getBool( XML_formatRows, true );
+ maSheetProt.mbInsertColumns = rAttribs.getBool( XML_insertColumns, true );
+ maSheetProt.mbInsertRows = rAttribs.getBool( XML_insertRows, true );
+ maSheetProt.mbInsertHyperlinks = rAttribs.getBool( XML_insertHyperlinks, true );
+ maSheetProt.mbDeleteColumns = rAttribs.getBool( XML_deleteColumns, true );
+ maSheetProt.mbDeleteRows = rAttribs.getBool( XML_deleteRows, true );
+ maSheetProt.mbSelectLocked = rAttribs.getBool( XML_selectLockedCells, false );
+ maSheetProt.mbSort = rAttribs.getBool( XML_sort, true );
+ maSheetProt.mbAutoFilter = rAttribs.getBool( XML_autoFilter, true );
+ maSheetProt.mbPivotTables = rAttribs.getBool( XML_pivotTables, true );
+ maSheetProt.mbSelectUnlocked = rAttribs.getBool( XML_selectUnlockedCells, false );
+}
+
+void WorksheetSettings::importProtectedRange( const AttributeList& rAttribs )
+{
+ ScEnhancedProtection aProt;
+ aProt.maTitle = rAttribs.getString( XML_name, OUString());
+ /* XXX ECMA-376/OOXML XMLSchema and ISO/IEC 29500 say 'securityDescriptor'
+ * would be an element, but Excel2013 stores it as attribute. */
+ aProt.maSecurityDescriptorXML = rAttribs.getString( XML_securityDescriptor, OUString());
+ /* XXX ECMA-376/OOXML or ISO/IEC 29500 do not even mention a 'password'
+ * attribute here (or anywhere else), but this is what Excel2013 writes,
+ * similar to BIFF, if the original file was a BIFF file. OOXML XMLschema
+ * and ISO/IEC 29500 instead define 'algorithmName', 'hashValue',
+ * 'saltValue' and 'spinCount' that are written if the protection was newly
+ * created. */
+ aProt.mnPasswordVerifier = rAttribs.getIntegerHex( XML_password, 0);
+ aProt.maPasswordHash.maAlgorithmName = rAttribs.getString( XML_algorithmName, OUString());
+ aProt.maPasswordHash.maHashValue = rAttribs.getString( XML_hashValue, OUString());
+ aProt.maPasswordHash.maSaltValue = rAttribs.getString( XML_saltValue, OUString());
+ aProt.maPasswordHash.mnSpinCount = rAttribs.getUnsigned( XML_spinCount, 0);
+ OUString aRefs( rAttribs.getString( XML_sqref, OUString()));
+ if (!aRefs.isEmpty())
+ {
+ std::unique_ptr<ScRangeList> xRangeList(new ScRangeList());
+ getAddressConverter().convertToCellRangeList( *xRangeList, aRefs, getSheetIndex(), true );
+ if (!xRangeList->empty())
+ {
+ aProt.maRangeList = xRangeList.release();
+ }
+ }
+ maSheetProt.maEnhancedProtections.push_back( aProt);
+}
+
+void WorksheetSettings::importChartProtection( const AttributeList& rAttribs )
+{
+ maSheetProt.mnPasswordHash = oox::core::CodecHelper::getPasswordHash( rAttribs, XML_password );
+ maSheetProt.mbSheet = rAttribs.getBool( XML_content, false );
+ maSheetProt.mbObjects = rAttribs.getBool( XML_objects, false );
+}
+
+void WorksheetSettings::importPhoneticPr( const AttributeList& rAttribs )
+{
+ maPhoneticSett.importPhoneticPr( rAttribs );
+}
+
+void WorksheetSettings::importSheetPr( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags1;
+ sal_uInt8 nFlags2;
+ nFlags1 = rStrm.readuInt16();
+ nFlags2 = rStrm.readuChar();
+ rStrm >> maSheetSettings.maTabColor;
+ rStrm.skip( 8 ); // sync anchor cell
+ rStrm >> maSheetSettings.maCodeName;
+ // sheet settings
+ maSheetSettings.mbFilterMode = getFlag( nFlags2, BIFF12_SHEETPR_FILTERMODE );
+ // outline settings, equal flags in all BIFFs
+ maSheetSettings.mbApplyStyles = getFlag( nFlags1, BIFF_SHEETPR_APPLYSTYLES );
+ maSheetSettings.mbSummaryRight = getFlag( nFlags1, BIFF_SHEETPR_SYMBOLSRIGHT );
+ maSheetSettings.mbSummaryBelow = getFlag( nFlags1, BIFF_SHEETPR_SYMBOLSBELOW );
+ /* Fit printout to width/height - for whatever reason, this flag is still
+ stored separated from the page settings */
+ getPageSettings().setFitToPagesMode( getFlag( nFlags1, BIFF_SHEETPR_FITTOPAGES ) );
+}
+
+void WorksheetSettings::importChartSheetPr( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 ); // flags, contains only the 'published' flag
+ rStrm >> maSheetSettings.maTabColor >> maSheetSettings.maCodeName;
+}
+
+void WorksheetSettings::importSheetProtection( SequenceInputStream& rStrm )
+{
+ maSheetProt.mnPasswordHash = rStrm.readuInt16();
+ // no flags field for all these boolean flags?!?
+ maSheetProt.mbSheet = rStrm.readInt32() != 0;
+ maSheetProt.mbObjects = rStrm.readInt32() == 0;
+ maSheetProt.mbScenarios = rStrm.readInt32() == 0;
+ maSheetProt.mbFormatCells = rStrm.readInt32() == 0;
+ maSheetProt.mbFormatColumns = rStrm.readInt32() == 0;
+ maSheetProt.mbFormatRows = rStrm.readInt32() == 0;
+ maSheetProt.mbInsertColumns = rStrm.readInt32() == 0;
+ maSheetProt.mbInsertRows = rStrm.readInt32() == 0;
+ maSheetProt.mbInsertHyperlinks = rStrm.readInt32() == 0;
+ maSheetProt.mbDeleteColumns = rStrm.readInt32() == 0;
+ maSheetProt.mbDeleteRows = rStrm.readInt32() == 0;
+ maSheetProt.mbSelectLocked = rStrm.readInt32() == 0;
+ maSheetProt.mbSort = rStrm.readInt32() == 0;
+ maSheetProt.mbAutoFilter = rStrm.readInt32() == 0;
+ maSheetProt.mbPivotTables = rStrm.readInt32() == 0;
+ maSheetProt.mbSelectUnlocked = rStrm.readInt32() == 0;
+}
+
+void WorksheetSettings::importChartProtection( SequenceInputStream& rStrm )
+{
+ maSheetProt.mnPasswordHash = rStrm.readuInt16();
+ // no flags field for all these boolean flags?!?
+ maSheetProt.mbSheet = rStrm.readInt32() != 0;
+ maSheetProt.mbObjects = rStrm.readInt32() != 0;
+}
+
+void WorksheetSettings::importPhoneticPr( SequenceInputStream& rStrm )
+{
+ maPhoneticSett.importPhoneticPr( rStrm );
+}
+
+void WorksheetSettings::finalizeImport()
+{
+ // sheet protection
+ if( maSheetProt.mbSheet )
+ {
+ ScTableProtection aProtect;
+ aProtect.setProtected(true);
+ aProtect.setPasswordHash( maSheetProt.maAlgorithmName, maSheetProt.maHashValue,
+ maSheetProt.maSaltValue, maSheetProt.mnSpinCount);
+ // Set the simple hash after the proper hash because setting the proper
+ // hash resets the simple hash, yet if the simple hash is present we
+ // may as well use it and more important want to keep it for saving the
+ // document again.
+ if (maSheetProt.mnPasswordHash)
+ {
+ Sequence<sal_Int8> aPass{
+ sal_Int8(maSheetProt.mnPasswordHash >> 8),
+ sal_Int8(maSheetProt.mnPasswordHash & 0xFF)};
+ aProtect.setPasswordHash(aPass, PASSHASH_XL);
+ }
+ aProtect.setOption( ScTableProtection::OBJECTS, !maSheetProt.mbObjects);
+ aProtect.setOption( ScTableProtection::SCENARIOS, !maSheetProt.mbScenarios );
+ aProtect.setOption( ScTableProtection::FORMAT_CELLS, !maSheetProt.mbFormatCells );
+ aProtect.setOption( ScTableProtection::FORMAT_COLUMNS, !maSheetProt.mbFormatColumns );
+ aProtect.setOption( ScTableProtection::FORMAT_ROWS, !maSheetProt.mbFormatRows );
+ aProtect.setOption( ScTableProtection::INSERT_COLUMNS, !maSheetProt.mbInsertColumns );
+ aProtect.setOption( ScTableProtection::INSERT_ROWS, !maSheetProt.mbInsertRows );
+ aProtect.setOption( ScTableProtection::INSERT_HYPERLINKS, !maSheetProt.mbInsertHyperlinks );
+ aProtect.setOption( ScTableProtection::DELETE_COLUMNS, !maSheetProt.mbDeleteColumns );
+ aProtect.setOption( ScTableProtection::DELETE_ROWS,!maSheetProt.mbDeleteRows );
+ aProtect.setOption( ScTableProtection::SELECT_LOCKED_CELLS, !maSheetProt.mbSelectLocked );
+ aProtect.setOption( ScTableProtection::SORT, !maSheetProt.mbSort );
+ aProtect.setOption( ScTableProtection::AUTOFILTER, !maSheetProt.mbAutoFilter );
+ aProtect.setOption( ScTableProtection::PIVOT_TABLES, !maSheetProt.mbPivotTables );
+ aProtect.setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, !maSheetProt.mbSelectUnlocked );
+
+ aProtect.setEnhancedProtection( std::vector(maSheetProt.maEnhancedProtections) );
+
+ getScDocument().SetTabProtection( getSheetIndex(), &aProtect );
+ }
+
+ // VBA code name
+ PropertySet aPropSet( getSheet() );
+ aPropSet.setProperty( PROP_CodeName, maSheetSettings.maCodeName );
+
+ // sheet tab color
+ if( !maSheetSettings.maTabColor.isAuto() )
+ {
+ ::Color nColor = maSheetSettings.maTabColor.getColor( getBaseFilter().getGraphicHelper() );
+ aPropSet.setProperty( PROP_TabColor, nColor );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/filterdetect.cxx b/sc/source/filter/orcus/filterdetect.cxx
new file mode 100644
index 0000000000..06f6015a8f
--- /dev/null
+++ b/sc/source/filter/orcus/filterdetect.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 <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <unotools/mediadescriptor.hxx>
+
+#include <tools/stream.hxx>
+
+#include <orcus/format_detection.hpp>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace {
+
+class OrcusFormatDetect : public ::cppu::WeakImplHelper<
+ css::document::XExtendedFilterDetection,
+ css::lang::XServiceInfo >
+{
+public:
+ explicit OrcusFormatDetect();
+
+ 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;
+
+ virtual OUString SAL_CALL
+ detect( css::uno::Sequence< css::beans::PropertyValue >& rMediaDescSeq ) override;
+
+private:
+};
+
+OrcusFormatDetect::OrcusFormatDetect()
+{
+}
+
+OUString OrcusFormatDetect::getImplementationName()
+{
+ return "com.sun.star.comp.sc.OrcusFilterDetect";
+}
+
+sal_Bool OrcusFormatDetect::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence<OUString> OrcusFormatDetect::getSupportedServiceNames()
+{
+ return {"com.sun.star.frame.ExtendedTypeDetection"};
+}
+
+OUString OrcusFormatDetect::detect(css::uno::Sequence<css::beans::PropertyValue>& rMediaDescSeq)
+{
+ utl::MediaDescriptor aMediaDescriptor( rMediaDescSeq );
+ bool bAborted = aMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ABORTED, false);
+ if (bAborted)
+ return OUString();
+
+ css::uno::Reference<css::io::XInputStream> xInputStream(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM], css::uno::UNO_QUERY );
+ SvMemoryStream aContent(xInputStream->available());
+
+ static const sal_Int32 nBytes = 4096;
+ css::uno::Sequence<sal_Int8> aSeq(nBytes);
+ bool bEnd = false;
+ while(!bEnd)
+ {
+ sal_Int32 nReadBytes = xInputStream->readBytes(aSeq, nBytes);
+ bEnd = (nReadBytes != nBytes);
+ aContent.WriteBytes(aSeq.getConstArray(), nReadBytes);
+ }
+
+ std::string_view aStream(static_cast<const char*>(aContent.GetData()), aContent.GetSize());
+ orcus::format_t eFormat = orcus::detect(aStream);
+
+ switch (eFormat)
+ {
+ case orcus::format_t::gnumeric:
+ return "Gnumeric XML";
+ case orcus::format_t::xls_xml:
+ return "calc_MS_Excel_2003_XML";
+ case orcus::format_t::parquet:
+ return "Apache Parquet";
+ default:
+ ;
+ }
+
+ return OUString();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_sc_OrcusFormatDetect_get_implementation(css::uno::XComponentContext* ,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new OrcusFormatDetect());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/interface.cxx b/sc/source/filter/orcus/interface.cxx
new file mode 100644
index 0000000000..b349016355
--- /dev/null
+++ b/sc/source/filter/orcus/interface.cxx
@@ -0,0 +1,2481 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <limits>
+#include <memory>
+#include <orcusinterface.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <globalnames.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <compiler.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/intitem.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <i18nlangtag/lang.h>
+#include <tools/fontenum.hxx>
+#include <sal/log.hxx>
+
+#include <stylesbuffer.hxx>
+#include <orcus/exception.hpp>
+#include <stylehelper.hxx>
+#include <utility>
+#include <unordered_map>
+#include <frozen/bits/defines.h>
+#include <frozen/bits/elsa_std.h>
+#include <frozen/unordered_map.h>
+
+using namespace com::sun::star;
+
+namespace os = orcus::spreadsheet;
+
+namespace {
+
+formula::FormulaGrammar::Grammar getCalcGrammarFromOrcus( os::formula_grammar_t grammar )
+{
+ formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ODFF;
+ switch(grammar)
+ {
+ case orcus::spreadsheet::formula_grammar_t::ods:
+ eGrammar = formula::FormulaGrammar::GRAM_ODFF;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::xlsx:
+ eGrammar = formula::FormulaGrammar::GRAM_OOXML;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::gnumeric:
+ eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_A1;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::xls_xml:
+ eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::unknown:
+ break;
+ }
+
+ return eGrammar;
+}
+
+}
+
+ScOrcusGlobalSettings::ScOrcusGlobalSettings(ScDocumentImport& rDoc)
+ : mrDoc(rDoc)
+ , meCalcGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+ , meOrcusGrammar(os::formula_grammar_t::unknown)
+ , mnTextEncoding(RTL_TEXTENCODING_UTF8)
+{
+}
+
+void ScOrcusGlobalSettings::set_origin_date(int year, int month, int day)
+{
+ mrDoc.setOriginDate(year, month, day);
+}
+
+void ScOrcusGlobalSettings::set_character_set(orcus::character_set_t cs)
+{
+ // Keep the entries sorted by the key.
+ static constexpr auto rules = frozen::make_unordered_map<orcus::character_set_t, rtl_TextEncoding>({
+ { orcus::character_set_t::big5, RTL_TEXTENCODING_BIG5 },
+ { orcus::character_set_t::euc_jp, RTL_TEXTENCODING_EUC_JP },
+ { orcus::character_set_t::euc_kr, RTL_TEXTENCODING_EUC_KR },
+ { orcus::character_set_t::gb2312, RTL_TEXTENCODING_GB_2312 },
+ { orcus::character_set_t::gbk, RTL_TEXTENCODING_GBK },
+ { orcus::character_set_t::iso_2022_cn, RTL_TEXTENCODING_ISO_2022_CN },
+ { orcus::character_set_t::iso_2022_cn_ext, RTL_TEXTENCODING_ISO_2022_CN },
+ { orcus::character_set_t::iso_2022_jp, RTL_TEXTENCODING_ISO_2022_JP },
+ { orcus::character_set_t::iso_2022_jp_2, RTL_TEXTENCODING_ISO_2022_JP },
+ { orcus::character_set_t::iso_8859_1, RTL_TEXTENCODING_ISO_8859_1 },
+ { orcus::character_set_t::iso_8859_14, RTL_TEXTENCODING_ISO_8859_14 },
+ { orcus::character_set_t::iso_8859_15, RTL_TEXTENCODING_ISO_8859_15 },
+ { orcus::character_set_t::iso_8859_1_windows_3_0_latin_1, RTL_TEXTENCODING_ISO_8859_1 },
+ { orcus::character_set_t::iso_8859_1_windows_3_1_latin_1, RTL_TEXTENCODING_ISO_8859_1 },
+ { orcus::character_set_t::iso_8859_2, RTL_TEXTENCODING_ISO_8859_2 },
+ { orcus::character_set_t::iso_8859_2_windows_latin_2, RTL_TEXTENCODING_ISO_8859_2 },
+ { orcus::character_set_t::iso_8859_3, RTL_TEXTENCODING_ISO_8859_3 },
+ { orcus::character_set_t::iso_8859_4, RTL_TEXTENCODING_ISO_8859_4 },
+ { orcus::character_set_t::iso_8859_5, RTL_TEXTENCODING_ISO_8859_5 },
+ { orcus::character_set_t::iso_8859_6, RTL_TEXTENCODING_ISO_8859_6 },
+ { orcus::character_set_t::iso_8859_6_e, RTL_TEXTENCODING_ISO_8859_6 },
+ { orcus::character_set_t::iso_8859_6_i, RTL_TEXTENCODING_ISO_8859_6 },
+ { orcus::character_set_t::iso_8859_7, RTL_TEXTENCODING_ISO_8859_7 },
+ { orcus::character_set_t::iso_8859_8, RTL_TEXTENCODING_ISO_8859_8 },
+ { orcus::character_set_t::iso_8859_8_e, RTL_TEXTENCODING_ISO_8859_8 },
+ { orcus::character_set_t::iso_8859_8_i, RTL_TEXTENCODING_ISO_8859_8 },
+ { orcus::character_set_t::iso_8859_9, RTL_TEXTENCODING_ISO_8859_9 },
+ { orcus::character_set_t::iso_8859_9_windows_latin_5, RTL_TEXTENCODING_ISO_8859_9 },
+ { orcus::character_set_t::jis_x0201, RTL_TEXTENCODING_JIS_X_0201 },
+ { orcus::character_set_t::jis_x0212_1990, RTL_TEXTENCODING_JIS_X_0212 },
+ { orcus::character_set_t::shift_jis, RTL_TEXTENCODING_SHIFT_JIS },
+ { orcus::character_set_t::us_ascii, RTL_TEXTENCODING_ASCII_US },
+ { orcus::character_set_t::utf_7, RTL_TEXTENCODING_UTF7 },
+ { orcus::character_set_t::utf_8, RTL_TEXTENCODING_UTF8 },
+ { orcus::character_set_t::windows_1250, RTL_TEXTENCODING_MS_1250 },
+ { orcus::character_set_t::windows_1251, RTL_TEXTENCODING_MS_1251 },
+ { orcus::character_set_t::windows_1252, RTL_TEXTENCODING_MS_1252 },
+ { orcus::character_set_t::windows_1253, RTL_TEXTENCODING_MS_1253 },
+ { orcus::character_set_t::windows_1254, RTL_TEXTENCODING_MS_1254 },
+ { orcus::character_set_t::windows_1255, RTL_TEXTENCODING_MS_1255 },
+ { orcus::character_set_t::windows_1256, RTL_TEXTENCODING_MS_1256 },
+ { orcus::character_set_t::windows_1257, RTL_TEXTENCODING_MS_1257 },
+ { orcus::character_set_t::windows_1258, RTL_TEXTENCODING_MS_1258 },
+ });
+
+ if (auto it = rules.find(cs); it != rules.end())
+ mnTextEncoding = it->second;
+}
+
+void ScOrcusGlobalSettings::set_default_formula_grammar(os::formula_grammar_t grammar)
+{
+ meCalcGrammar = getCalcGrammarFromOrcus(grammar);
+ meOrcusGrammar = grammar;
+}
+
+orcus::spreadsheet::formula_grammar_t ScOrcusGlobalSettings::get_default_formula_grammar() const
+{
+ return meOrcusGrammar;
+}
+
+ScOrcusRefResolver::ScOrcusRefResolver( const ScOrcusGlobalSettings& rGS ) :
+ mrGlobalSettings(rGS) {}
+
+os::src_address_t ScOrcusRefResolver::resolve_address(std::string_view address)
+{
+ OUString aStr(address.data(), address.size(), mrGlobalSettings.getTextEncoding());
+
+ ScAddress aAddr;
+ aAddr.Parse(aStr, mrGlobalSettings.getDoc().getDoc(),
+ formula::FormulaGrammar::extractRefConvention(
+ mrGlobalSettings.getCalcGrammar()));
+
+ if (!aAddr.IsValid())
+ {
+ std::ostringstream os;
+ os << "'" << address << "' is not a valid address expression.";
+ throw orcus::invalid_arg_error(os.str());
+ }
+
+ os::src_address_t ret;
+ ret.sheet = aAddr.Tab();
+ ret.column = aAddr.Col();
+ ret.row = aAddr.Row();
+
+ return ret;
+}
+
+os::src_range_t ScOrcusRefResolver::resolve_range(std::string_view range)
+{
+ OUString aStr(range.data(), range.size(), mrGlobalSettings.getTextEncoding());
+
+ ScRange aRange;
+ aRange.Parse(aStr, mrGlobalSettings.getDoc().getDoc(),
+ formula::FormulaGrammar::extractRefConvention(
+ mrGlobalSettings.getCalcGrammar()));
+
+ if (!aRange.IsValid())
+ {
+ std::ostringstream os;
+ os << "'" << range << "' is not a valid range expression.";
+ throw orcus::invalid_arg_error(os.str());
+ }
+
+ os::src_range_t ret;
+ ret.first.sheet = aRange.aStart.Tab();
+ ret.first.column = aRange.aStart.Col();
+ ret.first.row = aRange.aStart.Row();
+ ret.last.sheet = aRange.aEnd.Tab();
+ ret.last.column = aRange.aEnd.Col();
+ ret.last.row = aRange.aEnd.Row();
+
+ return ret;
+}
+
+ScOrcusNamedExpression::ScOrcusNamedExpression(
+ ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab ) :
+ mrDoc(rDoc), mrGlobalSettings(rGS), mnTab(nTab) {}
+
+void ScOrcusNamedExpression::reset()
+{
+ maBasePos.SetTab(0);
+ maBasePos.SetCol(0);
+ maBasePos.SetRow(0);
+ maName.clear();
+ maExpr.clear();
+}
+
+void ScOrcusNamedExpression::set_base_position(const orcus::spreadsheet::src_address_t& pos)
+{
+ maBasePos.SetTab(pos.sheet);
+ maBasePos.SetCol(pos.column);
+ maBasePos.SetRow(pos.row);
+}
+
+void ScOrcusNamedExpression::set_named_expression(std::string_view name, std::string_view expression)
+{
+ maName = OUString(name.data(), name.size(), mrGlobalSettings.getTextEncoding());
+ maExpr = OUString(expression.data(), expression.size(), mrGlobalSettings.getTextEncoding());
+}
+
+void ScOrcusNamedExpression::set_named_range(std::string_view /*name*/, std::string_view /*range*/)
+{
+ throw std::runtime_error("ScOrcusNamedExpression::set_named_range not implemented yet.");
+}
+
+void ScOrcusNamedExpression::commit()
+{
+ ScRangeName* pNames = mnTab >= 0 ? mrDoc.getDoc().GetRangeName(mnTab) : mrDoc.getDoc().GetRangeName();
+ if (!pNames)
+ return;
+
+ ScRangeData* pRange = new ScRangeData(
+ mrDoc.getDoc(), maName, maExpr, maBasePos, ScRangeData::Type::Name,
+ mrGlobalSettings.getCalcGrammar());
+
+ pNames->insert(pRange, false);
+
+ reset(); // make sure to reset the state for the next run.
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, Type eType)
+ : mfValue(std::numeric_limits<double>::quiet_NaN())
+ , maPos(rPos)
+ , meType(eType)
+ , mnIndex1(0)
+ , mnIndex2(0)
+ , meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+{
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, double fValue)
+ : mfValue(fValue)
+ , maPos(rPos)
+ , meType(Type::Numeric)
+ , mnIndex1(0)
+ , mnIndex2(0)
+ , meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+{
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, uint32_t nIndex)
+ : mfValue(std::numeric_limits<double>::quiet_NaN())
+ , maPos(rPos)
+ , meType(Type::String)
+ , mnIndex1(nIndex)
+ , mnIndex2(0)
+ , meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+{
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, OUString aFormula,
+ formula::FormulaGrammar::Grammar eGrammar)
+ : maStr1(std::move(aFormula))
+ , mfValue(std::numeric_limits<double>::quiet_NaN())
+ , maPos(rPos)
+ , meType(Type::Formula)
+ , mnIndex1(0)
+ , mnIndex2(0)
+ , meGrammar(eGrammar)
+{
+}
+
+ScOrcusFactory::ScOrcusFactory(ScDocument& rDoc, bool bSkipDefaultStyles) :
+ maDoc(rDoc),
+ maGlobalSettings(maDoc),
+ maRefResolver(maGlobalSettings),
+ maSharedStrings(*this),
+ maNamedExpressions(maDoc, maGlobalSettings),
+ maStyles(*this, bSkipDefaultStyles),
+ mnProgress(0) {}
+
+orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::append_sheet(
+ orcus::spreadsheet::sheet_t sheet_index, std::string_view sheet_name)
+{
+ OUString aTabName(sheet_name.data(), sheet_name.size(), maGlobalSettings.getTextEncoding());
+
+ if (sheet_index == 0)
+ {
+ // The calc document initializes with one sheet already present.
+ assert(maDoc.getSheetCount() == 1);
+ maDoc.setSheetName(0, aTabName);
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, 0, *this));
+ return maSheets.back().get();
+ }
+
+ if (!maDoc.appendSheet(aTabName))
+ return nullptr;
+
+ SCTAB nTab = maDoc.getSheetCount() - 1;
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this));
+ return maSheets.back().get();
+}
+
+namespace {
+
+class FindSheetByIndex
+{
+ SCTAB mnTab;
+public:
+ explicit FindSheetByIndex(SCTAB nTab) : mnTab(nTab) {}
+ bool operator() (const std::unique_ptr<ScOrcusSheet>& rSheet) const
+ {
+ return rSheet->getIndex() == mnTab;
+ }
+};
+
+}
+
+orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::get_sheet(std::string_view sheet_name)
+{
+ OUString aTabName(sheet_name.data(), sheet_name.size(), maGlobalSettings.getTextEncoding());
+ SCTAB nTab = maDoc.getSheetIndex(aTabName);
+ if (nTab < 0)
+ // Sheet by that name not found.
+ return nullptr;
+
+ // See if we already have an orcus sheet instance by that index.
+ std::vector< std::unique_ptr<ScOrcusSheet> >::iterator it =
+ std::find_if(maSheets.begin(), maSheets.end(), FindSheetByIndex(nTab));
+
+ if (it != maSheets.end())
+ // We already have one. Return it.
+ return it->get();
+
+ // Create a new orcus sheet instance for this.
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this));
+ return maSheets.back().get();
+}
+
+orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::get_sheet(orcus::spreadsheet::sheet_t sheet_index)
+{
+ SCTAB nTab = static_cast<SCTAB>(sheet_index);
+ // See if we already have an orcus sheet instance by that index.
+ std::vector< std::unique_ptr<ScOrcusSheet> >::iterator it =
+ std::find_if(maSheets.begin(), maSheets.end(), FindSheetByIndex(nTab));
+
+ if (it != maSheets.end())
+ // We already have one. Return it.
+ return it->get();
+
+ // Create a new orcus sheet instance for this.
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this));
+ return maSheets.back().get();
+}
+
+orcus::spreadsheet::iface::import_global_settings* ScOrcusFactory::get_global_settings()
+{
+ return &maGlobalSettings;
+}
+
+orcus::spreadsheet::iface::import_shared_strings* ScOrcusFactory::get_shared_strings()
+{
+ return &maSharedStrings;
+}
+
+orcus::spreadsheet::iface::import_named_expression* ScOrcusFactory::get_named_expression()
+{
+ return &maNamedExpressions;
+}
+
+orcus::spreadsheet::iface::import_styles* ScOrcusFactory::get_styles()
+{
+ return &maStyles;
+}
+
+os::iface::import_reference_resolver* ScOrcusFactory::get_reference_resolver(os::formula_ref_context_t cxt)
+{
+ switch (cxt)
+ {
+ case os::formula_ref_context_t::global:
+ return &maRefResolver;
+ case os::formula_ref_context_t::named_expression_base:
+ case os::formula_ref_context_t::named_range:
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+void ScOrcusFactory::finalize()
+{
+ auto toFormulaCell = [this]( const CellStoreToken& rToken ) -> std::unique_ptr<ScFormulaCell>
+ {
+ const ScOrcusSheet& rSheet = *maSheets.at(rToken.maPos.Tab());
+ const sc::SharedFormulaGroups& rSFG = rSheet.getSharedFormulaGroups();
+ const ScTokenArray* pArray = rSFG.get(rToken.mnIndex1);
+ if (!pArray)
+ return std::unique_ptr<ScFormulaCell>();
+
+ return std::make_unique<ScFormulaCell>(maDoc.getDoc(), rToken.maPos, *pArray);
+ };
+
+ int nCellCount = 0;
+
+ for (const CellStoreToken& rToken : maCellStoreTokens)
+ {
+ switch (rToken.meType)
+ {
+ case CellStoreToken::Type::Auto:
+ {
+ maDoc.setAutoInput(rToken.maPos, rToken.maStr1);
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::String:
+ {
+ if (rToken.mnIndex1 >= maStrings.size())
+ // String index out-of-bound! Something is up.
+ break;
+
+ const auto& s = maStrings[rToken.mnIndex1];
+ switch (s.index())
+ {
+ case 0: // OUString
+ maDoc.setStringCell(rToken.maPos, std::get<0>(s));
+ break;
+ case 1: // std::unique_ptr<EditTextObject>
+ maDoc.setEditCell(rToken.maPos, std::get<1>(s)->Clone());
+ break;
+ }
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::Numeric:
+ {
+ maDoc.setNumericCell(rToken.maPos, rToken.mfValue);
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::Formula:
+ {
+ maDoc.setFormulaCell(
+ rToken.maPos, rToken.maStr1, rToken.meGrammar);
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::FormulaWithResult:
+ {
+ if (std::isfinite(rToken.mfValue))
+ maDoc.setFormulaCell(rToken.maPos, rToken.maStr1, rToken.meGrammar, &rToken.mfValue);
+ else
+ maDoc.setFormulaCell(rToken.maPos, rToken.maStr1, rToken.meGrammar, rToken.maStr2);
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::SharedFormula:
+ {
+ std::unique_ptr<ScFormulaCell> pCell = toFormulaCell(rToken);
+ if (!pCell)
+ break;
+
+ maDoc.setFormulaCell(rToken.maPos, pCell.release());
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::SharedFormulaWithResult:
+ {
+ std::unique_ptr<ScFormulaCell> pCell = toFormulaCell(rToken);
+ if (!pCell)
+ break;
+
+ if (std::isfinite(rToken.mfValue))
+ pCell->SetResultDouble(rToken.mfValue);
+ else
+ pCell->SetHybridString(
+ maDoc.getDoc().GetSharedStringPool().intern(rToken.maStr2));
+
+ maDoc.setFormulaCell(rToken.maPos, pCell.release());
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::Matrix:
+ {
+ if (!rToken.mnIndex1 || !rToken.mnIndex2)
+ break;
+
+ ScRange aRange(rToken.maPos);
+ aRange.aEnd.IncCol(rToken.mnIndex1-1);
+ aRange.aEnd.IncRow(rToken.mnIndex2-1);
+
+ ScCompiler aComp(maDoc.getDoc(), aRange.aStart, rToken.meGrammar);
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(rToken.maStr1));
+ if (!pArray)
+ break;
+
+ maDoc.setMatrixCells(aRange, *pArray, rToken.meGrammar);
+ break;
+ }
+ case CellStoreToken::Type::FillDownCells:
+ {
+ if (!rToken.mnIndex1)
+ break;
+
+ maDoc.fillDownCells(rToken.maPos, rToken.mnIndex1);
+ break;
+ }
+ default:
+ ;
+ }
+
+ if (nCellCount == 100000)
+ {
+ incrementProgress();
+ nCellCount = 0;
+ }
+ }
+
+ if (mxStatusIndicator.is())
+ mxStatusIndicator->end();
+
+ maDoc.finalize();
+}
+
+ScDocumentImport& ScOrcusFactory::getDoc()
+{
+ return maDoc;
+}
+
+size_t ScOrcusFactory::appendString(const OUString& rStr)
+{
+ size_t nPos = maStrings.size();
+ maStrings.push_back(rStr);
+ maStringHash.emplace(rStr, nPos);
+
+ return nPos;
+}
+
+size_t ScOrcusFactory::addString(const OUString& rStr)
+{
+ // Add only if the string is not yet present in the string pool.
+ StringHashType::iterator it = maStringHash.find(rStr);
+ if (it != maStringHash.end())
+ return it->second;
+
+ return appendString(rStr);
+}
+
+std::size_t ScOrcusFactory::appendFormattedString(std::unique_ptr<EditTextObject> pEditText)
+{
+ std::size_t nPos = maStrings.size();
+ maStrings.push_back(std::move(pEditText));
+ return nPos;
+}
+
+const OUString* ScOrcusFactory::getString(size_t nIndex) const
+{
+ if (nIndex >= maStrings.size())
+ return nullptr;
+
+ const StringValueType& rStr = maStrings[nIndex];
+ if (rStr.index() != 0)
+ return nullptr;
+
+ return &std::get<OUString>(rStr);
+}
+
+void ScOrcusFactory::pushCellStoreAutoToken( const ScAddress& rPos, const OUString& rVal )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::Auto);
+ maCellStoreTokens.back().maStr1 = rVal;
+}
+
+void ScOrcusFactory::pushCellStoreToken( const ScAddress& rPos, uint32_t nStrIndex )
+{
+ maCellStoreTokens.emplace_back(rPos, nStrIndex);
+}
+
+void ScOrcusFactory::pushCellStoreToken( const ScAddress& rPos, double fValue )
+{
+ maCellStoreTokens.emplace_back(rPos, fValue);
+}
+
+void ScOrcusFactory::pushCellStoreToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar )
+{
+ maCellStoreTokens.emplace_back(rPos, rFormula, eGrammar);
+}
+
+void ScOrcusFactory::pushFillDownCellsToken( const ScAddress& rPos, uint32_t nFillSize )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::FillDownCells);
+ maCellStoreTokens.back().mnIndex1 = nFillSize;
+}
+
+void ScOrcusFactory::pushSharedFormulaToken( const ScAddress& rPos, uint32_t nIndex )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::SharedFormula);
+ maCellStoreTokens.back().mnIndex1 = nIndex;
+}
+
+void ScOrcusFactory::pushMatrixFormulaToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
+ uint32_t nRowRange, uint32_t nColRange )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::Matrix);
+ CellStoreToken& rT = maCellStoreTokens.back();
+ rT.maStr1 = rFormula;
+ rT.meGrammar = eGrammar;
+ rT.mnIndex1 = nColRange;
+ rT.mnIndex2 = nRowRange;
+}
+
+void ScOrcusFactory::pushFormulaResult( const ScAddress& rPos, double fValue )
+{
+ // Formula result is expected to be pushed immediately following the
+ // formula token it belongs.
+ if (maCellStoreTokens.empty())
+ return;
+
+ CellStoreToken& rToken = maCellStoreTokens.back();
+ if (rToken.maPos != rPos)
+ return;
+
+ switch (rToken.meType)
+ {
+ case CellStoreToken::Type::Formula:
+ rToken.meType = CellStoreToken::Type::FormulaWithResult;
+ break;
+ case CellStoreToken::Type::SharedFormula:
+ rToken.meType = CellStoreToken::Type::SharedFormulaWithResult;
+ break;
+ default:
+ return;
+ }
+
+ rToken.mfValue = fValue;
+}
+
+void ScOrcusFactory::pushFormulaResult( const ScAddress& rPos, const OUString& rValue )
+{
+ // Formula result is expected to be pushed immediately following the
+ // formula token it belongs.
+ if (maCellStoreTokens.empty())
+ return;
+
+ CellStoreToken& rToken = maCellStoreTokens.back();
+ if (rToken.maPos != rPos)
+ return;
+
+ switch (rToken.meType)
+ {
+ case CellStoreToken::Type::Formula:
+ rToken.meType = CellStoreToken::Type::FormulaWithResult;
+ break;
+ case CellStoreToken::Type::SharedFormula:
+ rToken.meType = CellStoreToken::Type::SharedFormulaWithResult;
+ break;
+ default:
+ return;
+ }
+
+ rToken.maStr2 = rValue;
+}
+
+void ScOrcusFactory::incrementProgress()
+{
+ if (!mxStatusIndicator.is())
+ // Status indicator object not set.
+ return;
+
+ // For now, we'll hard-code the progress range to be 100, and stops at 99
+ // in all cases.
+
+ if (!mnProgress)
+ mxStatusIndicator->start(ScResId(STR_LOAD_DOC), 100);
+
+ if (mnProgress == 99)
+ return;
+
+ ++mnProgress;
+ mxStatusIndicator->setValue(mnProgress);
+}
+
+void ScOrcusFactory::setStatusIndicator(const uno::Reference<task::XStatusIndicator>& rIndicator)
+{
+ mxStatusIndicator = rIndicator;
+}
+
+const ScOrcusGlobalSettings& ScOrcusFactory::getGlobalSettings() const
+{
+ return maGlobalSettings;
+}
+
+ScOrcusSheetProperties::ScOrcusSheetProperties(SCTAB nTab, ScDocumentImport& rDoc):
+ mrDoc(rDoc),
+ mnTab(nTab)
+{
+}
+
+ScOrcusSheetProperties::~ScOrcusSheetProperties()
+{
+}
+
+namespace {
+
+double translateToInternal(double nVal, orcus::length_unit_t unit)
+{
+ switch(unit)
+ {
+ case orcus::length_unit_t::inch:
+ return nVal * 72.0 * 20.0;
+ case orcus::length_unit_t::twip:
+ return nVal;
+ case orcus::length_unit_t::point:
+ return nVal * 20.0;
+ case orcus::length_unit_t::centimeter:
+ return nVal * 20.0 * 72.0 / 2.54;
+ case orcus::length_unit_t::unknown:
+ if (nVal != 0)
+ SAL_WARN("sc.orcus", "unknown unit");
+ break;
+ default:
+ break;
+ }
+ return nVal;
+}
+
+
+}
+
+void ScOrcusSheetProperties::set_column_width(os::col_t col, os::col_t col_span, double width, orcus::length_unit_t unit)
+{
+ double nNewWidth = translateToInternal(width, unit);
+
+ for (os::col_t offset = 0; offset < col_span; ++offset)
+ mrDoc.getDoc().SetColWidthOnly(col + offset, mnTab, nNewWidth);
+}
+
+void ScOrcusSheetProperties::set_column_hidden(os::col_t col, os::col_t col_span, bool hidden)
+{
+ if (hidden)
+ mrDoc.getDoc().SetColHidden(col, col + col_span - 1, mnTab, hidden);
+}
+
+void ScOrcusSheetProperties::set_row_height(os::row_t row, double height, orcus::length_unit_t unit)
+{
+ double nNewHeight = translateToInternal(height, unit);
+ mrDoc.getDoc().SetRowHeightOnly(row, row,mnTab, nNewHeight);
+}
+
+void ScOrcusSheetProperties::set_row_hidden(os::row_t row, bool hidden)
+{
+ if (hidden)
+ mrDoc.getDoc().SetRowHidden(row, row, mnTab, hidden);
+}
+
+void ScOrcusSheetProperties::set_merge_cell_range(const orcus::spreadsheet::range_t& range)
+{
+ mrDoc.setMergedCells(mnTab, range.first.column, range.first.row, range.last.column, range.last.row);
+}
+
+ScOrcusConditionalFormat::ScOrcusConditionalFormat(SCTAB nTab, ScDocument& rDoc):
+ mnTab(nTab),
+ mrDoc(rDoc),
+ mpCurrentFormat(new ScConditionalFormat(0, &mrDoc)),
+ meEntryType(ScFormatEntry::Type::Condition)
+{
+}
+
+ScOrcusConditionalFormat::~ScOrcusConditionalFormat()
+{
+}
+
+void ScOrcusConditionalFormat::set_color(os::color_elem_t /*alpha*/, os::color_elem_t /*red*/,
+ os::color_elem_t /*green*/, os::color_elem_t /*blue*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_color");
+}
+
+void ScOrcusConditionalFormat::set_condition_type(os::condition_type_t /*type*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Condition);
+ SAL_INFO("sc.orcus.condformat", "set_condition_type");
+}
+
+void ScOrcusConditionalFormat::set_formula(std::string_view /*formula*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_formula");
+}
+
+void ScOrcusConditionalFormat::set_date(os::condition_date_t /*date*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Date);
+ SAL_INFO("sc.orcus.condformat", "set_date");
+}
+
+void ScOrcusConditionalFormat::commit_condition()
+{
+ SAL_INFO("sc.orcus.condformat", "commit_condition");
+}
+
+void ScOrcusConditionalFormat::set_icon_name(std::string_view /*name*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Iconset);
+ SAL_INFO("sc.orcus.condformat", "set_icon_name");
+}
+
+void ScOrcusConditionalFormat::set_databar_gradient(bool /*gradient*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_gradient");
+}
+
+void ScOrcusConditionalFormat::set_databar_axis(os::databar_axis_t /*axis*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_axis");
+}
+
+void ScOrcusConditionalFormat::set_databar_color_positive(os::color_elem_t /*alpha*/, os::color_elem_t /*red*/,
+ os::color_elem_t /*green*/, os::color_elem_t /*blue*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_color_positive");
+}
+
+void ScOrcusConditionalFormat::set_databar_color_negative(os::color_elem_t /*alpha*/, os::color_elem_t /*red*/,
+ os::color_elem_t /*green*/, os::color_elem_t /*blue*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_color_negative");
+}
+
+void ScOrcusConditionalFormat::set_min_databar_length(double /*length*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_min_databar_length");
+}
+
+void ScOrcusConditionalFormat::set_max_databar_length(double /*length*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_max_databar_length");
+}
+
+void ScOrcusConditionalFormat::set_show_value(bool /*show*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_show_value");
+}
+
+void ScOrcusConditionalFormat::set_iconset_reverse(bool /*reverse*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Iconset);
+ SAL_INFO("sc.orcus.condformat", "set_iconset_reverse");
+}
+
+void ScOrcusConditionalFormat::set_xf_id(size_t /*xf*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_xf_id");
+}
+
+void ScOrcusConditionalFormat::set_operator(os::condition_operator_t /*condition_type*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_operator");
+}
+
+void ScOrcusConditionalFormat::set_type(os::conditional_format_t type)
+{
+ switch (type)
+ {
+ case os::conditional_format_t::condition:
+ case os::conditional_format_t::formula:
+ meEntryType = ScFormatEntry::Type::Condition;
+ // mpCurrentEntry.reset(new ScCondFormatEntry());
+ break;
+ case os::conditional_format_t::date:
+ break;
+ case os::conditional_format_t::colorscale:
+ break;
+ case os::conditional_format_t::databar:
+ break;
+ case os::conditional_format_t::iconset:
+ break;
+ default:
+ SAL_INFO("sc.orcus.condformat", "unknown conditional_format_t value");
+ break;
+ }
+ SAL_INFO("sc.orcus.condformat", "set_type");
+}
+
+void ScOrcusConditionalFormat::commit_entry()
+{
+ SAL_INFO("sc.orcus.condformat", "commit_entry");
+}
+
+void ScOrcusConditionalFormat::set_range(std::string_view /*range*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_range");
+}
+
+void ScOrcusConditionalFormat::set_range(os::row_t row_start, os::col_t col_start,
+ os::row_t row_end, os::col_t col_end)
+{
+ SAL_INFO("sc.orcus.condformat", "set_range");
+ ScRange aRange(col_start, row_start, mnTab, col_end, row_end, mnTab);
+ mpCurrentFormat->SetRange(aRange);
+}
+
+void ScOrcusConditionalFormat::commit_format()
+{
+ SAL_INFO("sc.orcus.condformat", "commit_format");
+ mpCurrentFormat.reset(new ScConditionalFormat(0, &mrDoc));
+}
+
+ScOrcusSheet::ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& rFactory) :
+ mrDoc(rDoc),
+ mnTab(nTab),
+ mrFactory(rFactory),
+ mrStyles(static_cast<ScOrcusStyles&>(*mrFactory.get_styles())),
+ maAutoFilter(rFactory.getGlobalSettings()),
+ maProperties(mnTab, mrDoc),
+ maConditionalFormat(mnTab, rDoc.getDoc()),
+ maNamedExpressions(rDoc, rFactory.getGlobalSettings(), nTab),
+ maFormula(*this),
+ maArrayFormula(*this),
+ mnCellCount(0)
+{
+}
+
+void ScOrcusFormula::reset()
+{
+ mnCol = -1;
+ mnRow = -1;
+ maFormula.clear();
+ meGrammar = formula::FormulaGrammar::GRAM_UNSPECIFIED;
+ mnSharedFormulaIndex = 0;
+ mbShared = false;
+ meResType = ResultType::NotSet;
+ maResult.clear();
+ mfResult = 0.0;
+}
+
+ScOrcusFormula::ScOrcusFormula( ScOrcusSheet& rSheet ) :
+ mrSheet(rSheet),
+ mnCol(-1),
+ mnRow(-1),
+ meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED),
+ mnSharedFormulaIndex(0),
+ mbShared(false),
+ meResType(ResultType::NotSet),
+ mfResult(0.0) {}
+
+ScOrcusFormula::~ScOrcusFormula() {}
+
+void ScOrcusFormula::set_position(os::row_t row, os::col_t col)
+{
+ mnCol = col;
+ mnRow = row;
+}
+
+void ScOrcusFormula::set_formula(os::formula_grammar_t grammar, std::string_view formula)
+{
+ maFormula = OUString(formula.data(), formula.size(), mrSheet.getFactory().getGlobalSettings().getTextEncoding());
+ meGrammar = getCalcGrammarFromOrcus(grammar);
+}
+
+void ScOrcusFormula::set_shared_formula_index(size_t index)
+{
+ mnSharedFormulaIndex = index;
+ mbShared = true;
+}
+
+void ScOrcusFormula::set_result_value(double value)
+{
+ meResType = ResultType::Value;
+ mfResult = value;
+}
+
+void ScOrcusFormula::set_result_string(std::string_view value)
+{
+ meResType = ResultType::String;
+ maResult = OUString(value.data(), value.size(), mrSheet.getFactory().getGlobalSettings().getTextEncoding());
+}
+
+void ScOrcusFormula::set_result_empty()
+{
+ meResType = ResultType::Empty;
+}
+
+void ScOrcusFormula::set_result_bool(bool value)
+{
+ meResType = ResultType::Value;
+ mfResult = value ? 1.0 : 0.0;
+}
+
+void ScOrcusFormula::commit()
+{
+ ScOrcusFactory& rFactory = mrSheet.getFactory();
+ sc::SharedFormulaGroups& rGroups = mrSheet.getSharedFormulaGroups();
+ ScAddress aPos(mnCol, mnRow, mrSheet.getIndex());
+
+ if (mbShared)
+ {
+ if (maFormula.isEmpty())
+ {
+ // shared formula that references existing formula token.
+ const ScTokenArray* pArray = rGroups.get(mnSharedFormulaIndex);
+ if (!pArray)
+ return;
+ }
+ else
+ {
+ // topmost shared formula with new formula token.
+
+ // Compile the formula expression into tokens.
+ ScCompiler aComp(mrSheet.getDoc().getDoc(), aPos, meGrammar);
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(maFormula);
+ if (!pArray)
+ // Tokenization failed.
+ return;
+
+ rGroups.set(mnSharedFormulaIndex, std::move(pArray));
+ }
+ rFactory.pushSharedFormulaToken(aPos, mnSharedFormulaIndex);
+ }
+ else
+ {
+ // non-shared formula
+ rFactory.pushCellStoreToken(aPos, maFormula, meGrammar);
+ }
+
+ switch (meResType)
+ {
+ case ResultType::String:
+ {
+ rFactory.pushFormulaResult(aPos, maResult);
+ break;
+ }
+ case ResultType::Value:
+ rFactory.pushFormulaResult(aPos, mfResult);
+ break;
+ default:
+ ;
+ }
+
+ mrSheet.cellInserted();
+}
+
+void ScOrcusArrayFormula::reset()
+{
+ mnCol = -1;
+ mnRow = -1;
+ mnColRange = 0;
+ mnRowRange = 0;
+
+ maFormula.clear();
+ meGrammar = formula::FormulaGrammar::GRAM_UNSPECIFIED;
+}
+
+ScOrcusArrayFormula::ScOrcusArrayFormula( ScOrcusSheet& rSheet ) :
+ mrSheet(rSheet),
+ mnCol(-1),
+ mnRow(-1),
+ mnColRange(0),
+ mnRowRange(0),
+ meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED) {}
+
+ScOrcusArrayFormula::~ScOrcusArrayFormula() {}
+
+void ScOrcusArrayFormula::set_range(const os::range_t& range)
+{
+ mnCol = range.first.column;
+ mnRow = range.first.row;
+
+ mnColRange = range.last.column - range.first.column + 1;
+ mnRowRange = range.last.row - range.first.column + 1;
+}
+
+void ScOrcusArrayFormula::set_formula(os::formula_grammar_t grammar, std::string_view formula)
+{
+ meGrammar = getCalcGrammarFromOrcus(grammar);
+ maFormula = OUString(formula.data(), formula.size(), mrSheet.getFactory().getGlobalSettings().getTextEncoding());
+}
+
+void ScOrcusArrayFormula::set_result_value(os::row_t /*row*/, os::col_t /*col*/, double /*value*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::set_result_string(os::row_t /*row*/, os::col_t /*col*/, std::string_view /*value*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::set_result_empty(os::row_t /*row*/, os::col_t /*col*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::set_result_bool(os::row_t /*row*/, os::col_t /*col*/, bool /*value*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::commit()
+{
+ ScAddress aPos(mnCol, mnRow, mrSheet.getIndex());
+ mrSheet.getFactory().pushMatrixFormulaToken(aPos, maFormula, meGrammar, mnRowRange, mnColRange);
+ mrSheet.cellInserted();
+}
+
+void ScOrcusSheet::cellInserted()
+{
+ ++mnCellCount;
+ if (mnCellCount == 100000)
+ {
+ mrFactory.incrementProgress();
+ mnCellCount = 0;
+ }
+}
+
+ScDocumentImport& ScOrcusSheet::getDoc()
+{
+ return mrDoc;
+}
+
+os::iface::import_auto_filter* ScOrcusSheet::get_auto_filter()
+{
+ return &maAutoFilter;
+}
+
+os::iface::import_table* ScOrcusSheet::get_table()
+{
+ return nullptr;
+}
+
+os::iface::import_sheet_properties* ScOrcusSheet::get_sheet_properties()
+{
+ return &maProperties;
+}
+
+os::iface::import_conditional_format* ScOrcusSheet::get_conditional_format()
+{
+ return &maConditionalFormat;
+}
+
+os::iface::import_named_expression* ScOrcusSheet::get_named_expression()
+{
+ return &maNamedExpressions;
+}
+
+os::iface::import_formula* ScOrcusSheet::get_formula()
+{
+ maFormula.reset();
+ return &maFormula;
+}
+
+os::iface::import_array_formula* ScOrcusSheet::get_array_formula()
+{
+ maArrayFormula.reset();
+ return &maArrayFormula;
+}
+
+void ScOrcusSheet::set_auto(os::row_t row, os::col_t col, std::string_view value)
+{
+ OUString aVal(value.data(), value.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ mrFactory.pushCellStoreAutoToken(ScAddress(col, row, mnTab), aVal);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_string(os::row_t row, os::col_t col, os::string_id_t sindex)
+{
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), sindex);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_value(os::row_t row, os::col_t col, double value)
+{
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), value);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_bool(os::row_t row, os::col_t col, bool value)
+{
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), value ? 1.0 : 0.0);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_date_time(
+ os::row_t row, os::col_t col, int year, int month, int day, int hour, int minute, double second)
+{
+ SvNumberFormatter* pFormatter = mrDoc.getDoc().GetFormatTable();
+
+ Date aDate(day, month, year);
+ sal_uInt32 nSec = floor(second);
+ sal_uInt32 nNanoSec = (second - nSec) * ::tools::Time::nanoSecPerSec;
+ tools::Time aTime(hour, minute, nSec, nNanoSec);
+ tools::Long nDateDiff = aDate - pFormatter->GetNullDate();
+
+ double fTime =
+ static_cast<double>(aTime.GetNanoSec()) / ::tools::Time::nanoSecPerSec +
+ aTime.GetSec() +
+ aTime.GetMin() * ::tools::Time::secondPerMinute +
+ aTime.GetHour() * ::tools::Time::secondPerHour;
+
+ fTime /= DATE_TIME_FACTOR;
+
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), nDateDiff + fTime);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_format(os::row_t row, os::col_t col, size_t xf_index)
+{
+ SAL_INFO("sc.orcus.style", "set format: " << xf_index);
+
+ ScPatternAttr aPattern(mrDoc.getDoc().GetPool());
+ mrStyles.applyXfToItemSet(aPattern.GetItemSet(), xf_index);
+ mrDoc.getDoc().ApplyPattern(col, row, mnTab, aPattern);
+}
+
+void ScOrcusSheet::set_format(os::row_t row_start, os::col_t col_start,
+ os::row_t row_end, os::col_t col_end, size_t xf_index)
+{
+ SAL_INFO("sc.orcus.style", "set format range: " << xf_index);
+ ScPatternAttr aPattern(mrDoc.getDoc().GetPool());
+ mrStyles.applyXfToItemSet(aPattern.GetItemSet(), xf_index);
+ mrDoc.getDoc().ApplyPatternAreaTab(col_start, row_start, col_end, row_end, mnTab, aPattern);
+}
+
+void ScOrcusSheet::set_column_format(
+ os::col_t col, os::col_t col_span, std::size_t xf_index)
+{
+ ScPatternAttr aPattern(mrDoc.getDoc().GetPool());
+ mrStyles.applyXfToItemSet(aPattern.GetItemSet(), xf_index);
+
+ mrDoc.getDoc().ApplyPatternAreaTab(
+ col, 0, col + col_span - 1, mrDoc.getDoc().MaxRow(), mnTab, aPattern);
+}
+
+void ScOrcusSheet::set_row_format(os::row_t row, std::size_t xf_index)
+{
+ ScPatternAttr aPattern(mrDoc.getDoc().GetPool());
+ mrStyles.applyXfToItemSet(aPattern.GetItemSet(), xf_index);
+
+ mrDoc.getDoc().ApplyPatternAreaTab(
+ 0, row, mrDoc.getDoc().MaxCol(), row, mnTab, aPattern);
+}
+
+orcus::spreadsheet::range_size_t ScOrcusSheet::get_sheet_size() const
+{
+ orcus::spreadsheet::range_size_t ret;
+ ret.rows = MAXROWCOUNT;
+ ret.columns = MAXCOLCOUNT;
+
+ return ret;
+}
+
+void ScOrcusSheet::fill_down_cells(os::row_t row, os::col_t col, os::row_t range_size)
+{
+ mrFactory.pushFillDownCellsToken(ScAddress(col, row, mnTab), range_size);
+ cellInserted();
+}
+
+const sc::SharedFormulaGroups& ScOrcusSheet::getSharedFormulaGroups() const
+{
+ return maFormulaGroups;
+}
+
+sc::SharedFormulaGroups& ScOrcusSheet::getSharedFormulaGroups()
+{
+ return maFormulaGroups;
+}
+
+ScOrcusFactory& ScOrcusSheet::getFactory()
+{
+ return mrFactory;
+}
+
+OUString ScOrcusSharedStrings::toOUString(std::string_view s)
+{
+ return {s.data(), sal_Int32(s.size()), mrFactory.getGlobalSettings().getTextEncoding()};
+}
+
+ScOrcusSharedStrings::ScOrcusSharedStrings(ScOrcusFactory& rFactory) :
+ mrFactory(rFactory),
+ mrEditEngine(rFactory.getDoc().getDoc().GetEditEngine()),
+ maCurFormat(mrEditEngine.GetEmptyItemSet())
+{
+ mrEditEngine.Clear();
+}
+
+size_t ScOrcusSharedStrings::append(std::string_view s)
+{
+ return mrFactory.appendString(toOUString(s));
+}
+
+size_t ScOrcusSharedStrings::add(std::string_view s)
+{
+ return mrFactory.addString(toOUString(s));
+}
+
+void ScOrcusSharedStrings::set_segment_font(size_t /*font_index*/)
+{
+}
+
+void ScOrcusSharedStrings::set_segment_bold(bool b)
+{
+ FontWeight eWeight = b ? WEIGHT_BOLD : WEIGHT_NORMAL;
+ maCurFormat.Put(SvxWeightItem(eWeight, EE_CHAR_WEIGHT));
+}
+
+void ScOrcusSharedStrings::set_segment_italic(bool b)
+{
+ FontItalic eItalic = b ? ITALIC_NORMAL : ITALIC_NONE;
+ maCurFormat.Put(SvxPostureItem(eItalic, EE_CHAR_ITALIC));
+}
+
+void ScOrcusSharedStrings::set_segment_font_name(std::string_view s)
+{
+ OUString aName = toOUString(s);
+ maCurFormat.Put(
+ SvxFontItem(
+ FAMILY_DONTKNOW, aName, aName, PITCH_DONTKNOW,
+ mrFactory.getGlobalSettings().getTextEncoding(),
+ EE_CHAR_FONTINFO
+ )
+ );
+}
+
+void ScOrcusSharedStrings::set_segment_font_size(double point)
+{
+ // points to 100th of millimeters
+ tools::Long nMM = o3tl::convert(point, o3tl::Length::pt, o3tl::Length::mm100);
+ maCurFormat.Put(SvxFontHeightItem(nMM, 100, EE_CHAR_FONTHEIGHT));
+}
+
+void ScOrcusSharedStrings::set_segment_font_color(
+ os::color_elem_t alpha, os::color_elem_t red, os::color_elem_t green, os::color_elem_t blue)
+{
+ Color aColor(ColorAlpha, alpha, red, green, blue);
+ maCurFormat.Put(SvxColorItem(aColor, EE_CHAR_COLOR));
+}
+
+void ScOrcusSharedStrings::append_segment(std::string_view s)
+{
+ sal_Int32 nPos = mrEditEngine.GetText().getLength();
+ ESelection aSel{0, nPos, 0, nPos}; // end of current text
+
+ OUString aStr = toOUString(s);
+ mrEditEngine.QuickInsertText(aStr, aSel);
+
+ aSel.nEndPos += aStr.getLength(); // expand the selection over the current segment
+ maFormatSegments.emplace_back(aSel, maCurFormat);
+ maCurFormat.ClearItem();
+}
+
+size_t ScOrcusSharedStrings::commit_segments()
+{
+ for (const auto& [rSel, rFormat] : maFormatSegments)
+ mrEditEngine.QuickSetAttribs(rFormat, rSel);
+
+ auto nPos = mrFactory.appendFormattedString(mrEditEngine.CreateTextObject());
+ mrEditEngine.Clear();
+ maFormatSegments.clear();
+ return nPos;
+}
+
+void ScOrcusFont::applyToItemSet( SfxItemSet& rSet ) const
+{
+ if (mbBold)
+ {
+ FontWeight eWeight = *mbBold ? WEIGHT_BOLD : WEIGHT_NORMAL;
+ rSet.Put(SvxWeightItem(eWeight, ATTR_FONT_WEIGHT));
+ }
+
+ if (mbBoldAsian)
+ {
+ FontWeight eWeight = *mbBoldAsian ? WEIGHT_BOLD : WEIGHT_NORMAL;
+ rSet.Put(SvxWeightItem(eWeight, ATTR_CJK_FONT_WEIGHT));
+ }
+
+ if (mbBoldComplex)
+ {
+ FontWeight eWeight = *mbBoldComplex ? WEIGHT_BOLD : WEIGHT_NORMAL;
+ rSet.Put(SvxWeightItem(eWeight, ATTR_CTL_FONT_WEIGHT));
+ }
+
+ if (mbItalic)
+ {
+ FontItalic eItalic = *mbItalic ? ITALIC_NORMAL : ITALIC_NONE;
+ rSet.Put(SvxPostureItem(eItalic, ATTR_FONT_POSTURE));
+ }
+
+ if (mbItalicAsian)
+ {
+ FontItalic eItalic = *mbItalicAsian ? ITALIC_NORMAL : ITALIC_NONE;
+ rSet.Put(SvxPostureItem(eItalic, ATTR_CJK_FONT_POSTURE));
+ }
+
+ if (mbItalicComplex)
+ {
+ FontItalic eItalic = *mbItalicComplex ? ITALIC_NORMAL : ITALIC_NONE;
+ rSet.Put(SvxPostureItem(eItalic, ATTR_CTL_FONT_POSTURE));
+ }
+
+ if (maColor)
+ rSet.Put( SvxColorItem(*maColor, ATTR_FONT_COLOR));
+
+ if (maName && !maName->isEmpty())
+ rSet.Put( SvxFontItem( FAMILY_DONTKNOW, *maName, *maName, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ));
+
+ if (maNameAsian && !maNameAsian->isEmpty())
+ rSet.Put( SvxFontItem( FAMILY_DONTKNOW, *maNameAsian, *maNameAsian, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, ATTR_CJK_FONT ));
+
+ if (maNameComplex && !maNameComplex->isEmpty())
+ rSet.Put( SvxFontItem( FAMILY_DONTKNOW, *maNameComplex, *maNameComplex, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, ATTR_CTL_FONT ));
+
+ if (mnSize)
+ {
+ double fSize = translateToInternal(*mnSize, orcus::length_unit_t::point);
+ rSet.Put(SvxFontHeightItem(fSize, 100, ATTR_FONT_HEIGHT));
+ }
+
+ if (mnSizeAsian)
+ {
+ double fSize = translateToInternal(*mnSizeAsian, orcus::length_unit_t::point);
+ rSet.Put(SvxFontHeightItem(fSize, 100, ATTR_CJK_FONT_HEIGHT));
+ }
+
+ if (mnSizeComplex)
+ {
+ double fSize = translateToInternal(*mnSizeComplex, orcus::length_unit_t::point);
+ rSet.Put(SvxFontHeightItem(fSize, 100, ATTR_CTL_FONT_HEIGHT));
+ }
+
+ if (meUnderline)
+ {
+ SvxUnderlineItem aUnderline(*meUnderline, ATTR_FONT_UNDERLINE);
+ if (maUnderlineColor)
+ // Separate color specified for the underline
+ aUnderline.SetColor(*maUnderlineColor);
+ else if (maColor)
+ // Use font color
+ aUnderline.SetColor(*maColor);
+ rSet.Put(aUnderline);
+ }
+
+ if (meStrikeout)
+ rSet.Put(SvxCrossedOutItem(*meStrikeout, ATTR_FONT_CROSSEDOUT));
+}
+
+void ScOrcusFill::applyToItemSet( SfxItemSet& rSet ) const
+{
+ if (!mePattern || !maFgColor)
+ return;
+
+ if (*mePattern == os::fill_pattern_t::solid)
+ rSet.Put(SvxBrushItem(*maFgColor, ATTR_BACKGROUND));
+}
+
+void ScOrcusBorder::applyToItemSet( SfxItemSet& rSet ) const
+{
+ auto getDirection = [](os::border_direction_t dir) -> SvxBoxItemLine
+ {
+ switch (dir)
+ {
+ case os::border_direction_t::right:
+ return SvxBoxItemLine::RIGHT;
+ case os::border_direction_t::left:
+ return SvxBoxItemLine::LEFT;
+ case os::border_direction_t::top:
+ return SvxBoxItemLine::TOP;
+ case os::border_direction_t::bottom:
+ return SvxBoxItemLine::BOTTOM;
+ default:
+ ;
+ }
+ return SvxBoxItemLine::RIGHT;
+ };
+
+ if (maBorders.empty())
+ return;
+
+ SvxBoxItem aBoxItem(ATTR_BORDER);
+ SvxLineItem aDiagonal_TLBR(ATTR_BORDER_TLBR);
+ SvxLineItem aDiagonal_BLTR(ATTR_BORDER_BLTR);
+
+ for (const auto& [dir, attrs] : maBorders)
+ {
+ SvxBoxItemLine eDir = getDirection(dir);
+
+ SvxBorderLineStyle eStyle = attrs.meStyle.value_or(SvxBorderLineStyle::SOLID);
+ Color aColor = attrs.maColor.value_or(COL_BLACK);
+ double nWidth = attrs.mnWidth.value_or(0.0);
+
+ switch (dir)
+ {
+ case os::border_direction_t::diagonal_tl_br:
+ {
+ editeng::SvxBorderLine aLine(&aColor, nWidth, eStyle);
+ aDiagonal_TLBR.SetLine(&aLine);
+ break;
+ }
+ case os::border_direction_t::diagonal_bl_tr:
+ {
+ editeng::SvxBorderLine aLine(&aColor, nWidth, eStyle);
+ aDiagonal_BLTR.SetLine(&aLine);
+ break;
+ }
+ default:
+ {
+ editeng::SvxBorderLine aLine(&aColor, nWidth, eStyle);
+ aBoxItem.SetLine(&aLine, eDir);
+ }
+ }
+ }
+
+ rSet.Put(aDiagonal_BLTR);
+ rSet.Put(aDiagonal_TLBR);
+ rSet.Put(aBoxItem);
+}
+
+void ScOrcusProtection::applyToItemSet( SfxItemSet& rSet ) const
+{
+ if (!mbLocked.has_value() && !mbHidden.has_value() && !mbPrintContent.has_value() && !mbFormulaHidden.has_value())
+ return;
+
+ bool bLocked = mbLocked.value_or(true); // defaults to true.
+ bool bHidden = mbHidden.value_or(false);
+ bool bFormulaHidden = mbFormulaHidden.value_or(false);
+ bool bPrintContent = mbPrintContent.value_or(false);
+ rSet.Put(ScProtectionAttr(bLocked, bFormulaHidden, bHidden, bPrintContent));
+}
+
+void ScOrcusNumberFormat::applyToItemSet( SfxItemSet& rSet, const ScDocument& rDoc ) const
+{
+ if (!maCode)
+ return;
+
+ sal_uInt32 nKey;
+ sal_Int32 nCheckPos;
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ OUString Code = *maCode; /* <-- Done because the SvNumberFormatter::PutEntry demands a non const NumFormat Code*/
+ SvNumFormatType type = SvNumFormatType::ALL;
+
+ pFormatter->PutEntry(Code, nCheckPos, type, nKey, LANGUAGE_ENGLISH_US);
+ if (!nCheckPos)
+ rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nKey));
+}
+
+ScOrcusXf::ScOrcusXf() :
+ mnFontId(0),
+ mnFillId(0),
+ mnBorderId(0),
+ mnProtectionId(0),
+ mnNumberFormatId(0),
+ mnStyleXf(0),
+ mbApplyAlignment(false),
+ mbWrapText(false),
+ mbShrinkToFit(false),
+ meHorAlignment(SvxCellHorJustify::Standard),
+ meVerAlignment(SvxCellVerJustify::Standard),
+ meHorAlignMethod(SvxCellJustifyMethod::Auto),
+ meVerAlignMethod(SvxCellJustifyMethod::Auto)
+{
+}
+
+ScOrcusCellStyle::ScOrcusCellStyle() :
+ maParentName(SC_STYLE_PROG_STANDARD),
+ mnXFId(0),
+ mnBuiltInId(0)
+{
+}
+
+ScOrcusImportFontStyle::ScOrcusImportFontStyle( ScOrcusFactory& rFactory, std::vector<ScOrcusFont>& rFonts ) :
+ mrFactory(rFactory),
+ mrFonts(rFonts)
+{
+}
+
+void ScOrcusImportFontStyle::reset()
+{
+ maCurrentFont = ScOrcusFont();
+}
+
+void ScOrcusImportFontStyle::set_bold(bool b)
+{
+ maCurrentFont.mbBold = b;
+}
+
+void ScOrcusImportFontStyle::set_bold_asian(bool b)
+{
+ maCurrentFont.mbBoldAsian = b;
+}
+
+void ScOrcusImportFontStyle::set_bold_complex(bool b)
+{
+ maCurrentFont.mbBoldComplex = b;
+}
+
+void ScOrcusImportFontStyle::set_italic(bool b)
+{
+ maCurrentFont.mbItalic = b;
+}
+
+void ScOrcusImportFontStyle::set_italic_asian(bool b)
+{
+ maCurrentFont.mbItalicAsian = b;
+}
+
+void ScOrcusImportFontStyle::set_italic_complex(bool b)
+{
+ maCurrentFont.mbItalicComplex = b;
+}
+
+void ScOrcusImportFontStyle::set_name(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentFont.maName = aName;
+}
+
+void ScOrcusImportFontStyle::set_name_asian(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentFont.maNameAsian = aName;
+}
+
+void ScOrcusImportFontStyle::set_name_complex(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentFont.maNameComplex = aName;
+}
+
+void ScOrcusImportFontStyle::set_size(double point)
+{
+ maCurrentFont.mnSize = point;
+}
+
+void ScOrcusImportFontStyle::set_size_asian(double point)
+{
+ maCurrentFont.mnSizeAsian = point;
+}
+
+void ScOrcusImportFontStyle::set_size_complex(double point)
+{
+ maCurrentFont.mnSizeComplex = point;
+}
+
+void ScOrcusImportFontStyle::set_underline(os::underline_t e)
+{
+ switch(e)
+ {
+ case os::underline_t::single_line:
+ case os::underline_t::single_accounting:
+ maCurrentFont.meUnderline = LINESTYLE_SINGLE;
+ break;
+ case os::underline_t::double_line:
+ case os::underline_t::double_accounting:
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLE;
+ break;
+ case os::underline_t::none:
+ maCurrentFont.meUnderline = LINESTYLE_NONE;
+ break;
+ case os::underline_t::dotted:
+ maCurrentFont.meUnderline = LINESTYLE_DOTTED;
+ break;
+ case os::underline_t::dash:
+ maCurrentFont.meUnderline = LINESTYLE_DASH;
+ break;
+ case os::underline_t::long_dash:
+ maCurrentFont.meUnderline = LINESTYLE_LONGDASH;
+ break;
+ case os::underline_t::dot_dash:
+ maCurrentFont.meUnderline = LINESTYLE_DASHDOT;
+ break;
+ case os::underline_t::dot_dot_dash:
+ maCurrentFont.meUnderline = LINESTYLE_DASHDOTDOT;
+ break;
+ case os::underline_t::wave:
+ maCurrentFont.meUnderline = LINESTYLE_WAVE;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusImportFontStyle::set_underline_width(os::underline_width_t e)
+{
+ if (e == os::underline_width_t::bold || e == os::underline_width_t::thick)
+ {
+ if (maCurrentFont.meUnderline)
+ {
+ switch (*maCurrentFont.meUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_SINGLE:
+ maCurrentFont.meUnderline = LINESTYLE_BOLD;
+ break;
+ case LINESTYLE_DOTTED:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDOTTED;
+ break;
+ case LINESTYLE_DASH:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDASH;
+ break;
+ case LINESTYLE_LONGDASH:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDLONGDASH;
+ break;
+ case LINESTYLE_DASHDOT:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDASHDOT;
+ break;
+ case LINESTYLE_DASHDOTDOT:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDASHDOTDOT;
+ break;
+ case LINESTYLE_WAVE:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDWAVE;
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ maCurrentFont.meUnderline = LINESTYLE_BOLD;
+ }
+}
+
+void ScOrcusImportFontStyle::set_underline_mode(os::underline_mode_t /*e*/)
+{
+}
+
+void ScOrcusImportFontStyle::set_underline_type(os::underline_type_t e )
+{
+ if (e == os::underline_type_t::double_type)
+ {
+ if (maCurrentFont.meUnderline)
+ {
+ switch (*maCurrentFont.meUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_SINGLE:
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLE;
+ break;
+ case LINESTYLE_WAVE:
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLEWAVE;
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLE;
+ }
+}
+
+void ScOrcusImportFontStyle::set_underline_color(
+ os::color_elem_t alpha, os::color_elem_t red, os::color_elem_t green, os::color_elem_t blue)
+{
+ maCurrentFont.maUnderlineColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusImportFontStyle::set_color(
+ os::color_elem_t alpha, os::color_elem_t red, os::color_elem_t green, os::color_elem_t blue)
+{
+ maCurrentFont.maColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusImportFontStyle::set_strikethrough_style(os::strikethrough_style_t /*s*/)
+{
+}
+
+void ScOrcusImportFontStyle::set_strikethrough_type(os::strikethrough_type_t s)
+{
+ if (maCurrentFont.meStrikeout)
+ {
+ if (*maCurrentFont.meStrikeout == STRIKEOUT_BOLD ||
+ *maCurrentFont.meStrikeout == STRIKEOUT_SLASH ||
+ *maCurrentFont.meStrikeout == STRIKEOUT_X)
+ return;
+ }
+
+ switch (s)
+ {
+ case os::strikethrough_type_t::unknown:
+ maCurrentFont.meStrikeout = STRIKEOUT_DONTKNOW;
+ break;
+ case os::strikethrough_type_t::none:
+ maCurrentFont.meStrikeout = STRIKEOUT_NONE;
+ break;
+ case os::strikethrough_type_t::single_type:
+ maCurrentFont.meStrikeout = STRIKEOUT_SINGLE;
+ break;
+ case os::strikethrough_type_t::double_type:
+ maCurrentFont.meStrikeout = STRIKEOUT_DOUBLE;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusImportFontStyle::set_strikethrough_width(os::strikethrough_width_t s)
+{
+ switch (s)
+ {
+ case os::strikethrough_width_t::bold:
+ maCurrentFont.meStrikeout = STRIKEOUT_BOLD;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusImportFontStyle::set_strikethrough_text(os::strikethrough_text_t s)
+{
+ switch (s)
+ {
+ case os::strikethrough_text_t::slash:
+ maCurrentFont.meStrikeout = STRIKEOUT_SLASH;
+ break;
+ case os::strikethrough_text_t::cross:
+ maCurrentFont.meStrikeout = STRIKEOUT_X;
+ break;
+ default:
+ ;
+ }
+}
+
+std::size_t ScOrcusImportFontStyle::commit()
+{
+ SAL_INFO("sc.orcus.style", "commit font");
+ mrFonts.push_back(maCurrentFont);
+ maCurrentFont = ScOrcusFont();
+ return mrFonts.size() - 1;
+}
+
+ScOrcusImportFillStyle::ScOrcusImportFillStyle( std::vector<ScOrcusFill>& rFills ) :
+ mrFills(rFills)
+{
+}
+
+void ScOrcusImportFillStyle::reset()
+{
+ maCurrentFill = ScOrcusFill();
+}
+
+void ScOrcusImportFillStyle::set_pattern_type(os::fill_pattern_t fp)
+{
+ maCurrentFill.mePattern = fp;
+}
+
+void ScOrcusImportFillStyle::set_fg_color(
+ os::color_elem_t alpha, os::color_elem_t red, os::color_elem_t green, os::color_elem_t blue)
+{
+ maCurrentFill.maFgColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusImportFillStyle::set_bg_color(
+ os::color_elem_t alpha, os::color_elem_t red, os::color_elem_t green, os::color_elem_t blue)
+{
+ maCurrentFill.maBgColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+std::size_t ScOrcusImportFillStyle::commit()
+{
+ SAL_INFO("sc.orcus.style", "commit fill");
+ mrFills.push_back(maCurrentFill);
+ maCurrentFill = ScOrcusFill();
+ return mrFills.size() - 1;
+}
+
+ScOrcusImportBorderStyle::ScOrcusImportBorderStyle( std::vector<ScOrcusBorder>& rBorders ) :
+ mrBorders(rBorders)
+{
+}
+
+void ScOrcusImportBorderStyle::set_style(
+ os::border_direction_t dir, os::border_style_t style)
+{
+ ScOrcusBorder::BorderLine& rBorderLine = maCurrentBorder.maBorders[dir];
+
+ switch (style)
+ {
+ case os::border_style_t::solid:
+ rBorderLine.meStyle = SvxBorderLineStyle::SOLID;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case os::border_style_t::hair:
+ rBorderLine.meStyle = SvxBorderLineStyle::SOLID;
+ rBorderLine.mnWidth = oox::xls::API_LINE_HAIR;
+ break;
+ case os::border_style_t::medium:
+ rBorderLine.meStyle = SvxBorderLineStyle::SOLID;
+ rBorderLine.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case os::border_style_t::thick:
+ rBorderLine.meStyle = SvxBorderLineStyle::SOLID;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THICK;
+ break;
+ case os::border_style_t::thin:
+ rBorderLine.meStyle = SvxBorderLineStyle::SOLID;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case os::border_style_t::dash_dot:
+ rBorderLine.meStyle = SvxBorderLineStyle::DASH_DOT;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case os::border_style_t::dash_dot_dot:
+ rBorderLine.meStyle = SvxBorderLineStyle::DASH_DOT_DOT;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case os::border_style_t::dashed:
+ rBorderLine.meStyle = SvxBorderLineStyle::DASHED;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case os::border_style_t::dotted:
+ rBorderLine.meStyle = SvxBorderLineStyle::DOTTED;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case os::border_style_t::double_border:
+ rBorderLine.meStyle = SvxBorderLineStyle::DOUBLE;
+ rBorderLine.mnWidth = oox::xls::API_LINE_THICK;
+ break;
+ case os::border_style_t::medium_dash_dot:
+ case os::border_style_t::slant_dash_dot:
+ rBorderLine.meStyle = SvxBorderLineStyle::DASH_DOT;
+ rBorderLine.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case os::border_style_t::medium_dash_dot_dot:
+ rBorderLine.meStyle = SvxBorderLineStyle::DASH_DOT_DOT;
+ rBorderLine.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case os::border_style_t::medium_dashed:
+ rBorderLine.meStyle = SvxBorderLineStyle::DASHED;
+ rBorderLine.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case os::border_style_t::unknown:
+ case os::border_style_t::none:
+ rBorderLine.mnWidth = oox::xls::API_LINE_NONE;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusImportBorderStyle::set_color(
+ os::border_direction_t dir, os::color_elem_t alpha, os::color_elem_t red,
+ os::color_elem_t green, os::color_elem_t blue)
+{
+ ScOrcusBorder::BorderLine& rBorderLine = maCurrentBorder.maBorders[dir];
+ rBorderLine.maColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusImportBorderStyle::reset()
+{
+ maCurrentBorder = ScOrcusBorder();
+}
+
+void ScOrcusImportBorderStyle::set_width(os::border_direction_t dir, double val, orcus::length_unit_t unit)
+{
+ ScOrcusBorder::BorderLine& rBorderLine = maCurrentBorder.maBorders[dir];
+ rBorderLine.mnWidth = translateToInternal(val, unit);
+}
+
+std::size_t ScOrcusImportBorderStyle::commit()
+{
+ SAL_INFO("sc.orcus.style", "commit border");
+ mrBorders.push_back(maCurrentBorder);
+ maCurrentBorder = ScOrcusBorder();
+ return mrBorders.size() - 1;
+}
+
+ScOrcusImportCellProtection::ScOrcusImportCellProtection( std::vector<ScOrcusProtection>& rProtections ) :
+ mrProtections(rProtections)
+{
+}
+
+void ScOrcusImportCellProtection::reset()
+{
+ maCurrentProtection = ScOrcusProtection();
+}
+
+void ScOrcusImportCellProtection::set_hidden(bool b)
+{
+ maCurrentProtection.mbHidden = b;
+}
+
+void ScOrcusImportCellProtection::set_locked(bool b)
+{
+ maCurrentProtection.mbLocked = b;
+}
+
+void ScOrcusImportCellProtection::set_print_content(bool b )
+{
+ maCurrentProtection.mbPrintContent = b;
+}
+
+void ScOrcusImportCellProtection::set_formula_hidden(bool b )
+{
+ maCurrentProtection.mbFormulaHidden = b;
+}
+
+std::size_t ScOrcusImportCellProtection::commit()
+{
+ SAL_INFO("sc.orcus.style", "commit cell protection");
+ mrProtections.push_back(maCurrentProtection);
+ maCurrentProtection = ScOrcusProtection();
+ return mrProtections.size() - 1;
+}
+
+ScOrcusImportNumberFormat::ScOrcusImportNumberFormat( ScOrcusFactory& rFactory, std::vector<ScOrcusNumberFormat>& rFormats ) :
+ mrFactory(rFactory), mrNumberFormats(rFormats)
+{
+}
+
+void ScOrcusImportNumberFormat::reset()
+{
+ maCurrentFormat = ScOrcusNumberFormat();
+}
+
+void ScOrcusImportNumberFormat::set_identifier(std::size_t /*id*/)
+{
+}
+
+void ScOrcusImportNumberFormat::set_code(std::string_view s)
+{
+ OUString aCode(s.data(), s.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentFormat.maCode = aCode;
+}
+
+std::size_t ScOrcusImportNumberFormat::commit()
+{
+ SAL_INFO("sc.orcus.style", "commit number format");
+ mrNumberFormats.push_back(maCurrentFormat);
+ maCurrentFormat = ScOrcusNumberFormat();
+ return mrNumberFormats.size() - 1;
+}
+
+ScOrucsImportCellStyle::ScOrucsImportCellStyle(
+ ScOrcusFactory& rFactory, ScOrcusStyles& rStyles, const std::vector<ScOrcusXf>& rXfs ) :
+ mrFactory(rFactory),
+ mrStyles(rStyles),
+ mrXfs(rXfs)
+{
+}
+
+void ScOrucsImportCellStyle::reset()
+{
+ maCurrentStyle = ScOrcusCellStyle();
+}
+
+void ScOrucsImportCellStyle::set_name(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentStyle.maName = aName;
+}
+
+void ScOrucsImportCellStyle::set_display_name(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentStyle.maDisplayName = aName;
+}
+
+void ScOrucsImportCellStyle::set_xf(size_t index)
+{
+ maCurrentStyle.mnXFId = index;
+}
+
+void ScOrucsImportCellStyle::set_builtin(size_t index)
+{
+ maCurrentStyle.mnBuiltInId = index;
+}
+
+void ScOrucsImportCellStyle::set_parent_name(std::string_view name)
+{
+ const OUString aParentName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentStyle.maParentName = aParentName;
+}
+
+void ScOrucsImportCellStyle::commit()
+{
+ SAL_INFO("sc.orcus.style", "commit cell style: " << maCurrentStyle.maName);
+ if (maCurrentStyle.mnXFId >= mrXfs.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid xf id for commit cell style");
+ return;
+ }
+
+ if (maCurrentStyle.mnXFId == 0)
+ return;
+
+ ScStyleSheetPool* pPool = mrFactory.getDoc().getDoc().GetStyleSheetPool();
+ SfxStyleSheetBase& rBase = pPool->Make(maCurrentStyle.maName, SfxStyleFamily::Para);
+ // Need to convert the parent name to localized UI name, see tdf#139205.
+ rBase.SetParent(
+ ScStyleNameConversion::ProgrammaticToDisplayName(
+ maCurrentStyle.maParentName, SfxStyleFamily::Para));
+
+ SfxItemSet& rSet = rBase.GetItemSet();
+ const ScOrcusXf& rXf = mrXfs[maCurrentStyle.mnXFId];
+ mrStyles.applyXfToItemSet(rSet, rXf);
+
+ maCurrentStyle = ScOrcusCellStyle();
+}
+
+void ScOrcusImportXf::reset( std::vector<ScOrcusXf>& rXfs )
+{
+ mpXfs = &rXfs;
+ maCurrentXf = ScOrcusXf();
+}
+
+void ScOrcusImportXf::set_font(std::size_t index)
+{
+ maCurrentXf.mnFontId = index;
+}
+
+void ScOrcusImportXf::set_fill(std::size_t index)
+{
+ maCurrentXf.mnFillId = index;
+}
+
+void ScOrcusImportXf::set_border(std::size_t index)
+{
+ maCurrentXf.mnBorderId = index;
+}
+
+void ScOrcusImportXf::set_protection(std::size_t index)
+{
+ maCurrentXf.mnProtectionId = index;
+}
+
+void ScOrcusImportXf::set_number_format(std::size_t index)
+{
+ maCurrentXf.mnNumberFormatId = index;
+}
+
+void ScOrcusImportXf::set_style_xf(std::size_t index)
+{
+ maCurrentXf.mnStyleXf = index;
+}
+
+void ScOrcusImportXf::set_apply_alignment(bool b)
+{
+ maCurrentXf.mbApplyAlignment = b;
+}
+
+void ScOrcusImportXf::set_horizontal_alignment(os::hor_alignment_t align)
+{
+ switch (align)
+ {
+ case os::hor_alignment_t::left:
+ maCurrentXf.meHorAlignment = SvxCellHorJustify::Left;
+ break;
+ case os::hor_alignment_t::right:
+ maCurrentXf.meHorAlignment = SvxCellHorJustify::Right;
+ break;
+ case os::hor_alignment_t::center:
+ maCurrentXf.meHorAlignment = SvxCellHorJustify::Center;
+ break;
+ case os::hor_alignment_t::justified:
+ maCurrentXf.meHorAlignment = SvxCellHorJustify::Block;
+ break;
+ case os::hor_alignment_t::distributed:
+ maCurrentXf.meHorAlignment = SvxCellHorJustify::Block;
+ maCurrentXf.meHorAlignMethod = SvxCellJustifyMethod::Distribute;
+ break;
+ case os::hor_alignment_t::unknown:
+ maCurrentXf.meHorAlignment = SvxCellHorJustify::Standard;
+ break;
+ default:
+ ;
+ }
+ maCurrentXf.mbApplyAlignment = true;
+}
+
+void ScOrcusImportXf::set_vertical_alignment(os::ver_alignment_t align)
+{
+ switch (align)
+ {
+ case os::ver_alignment_t::top:
+ maCurrentXf.meVerAlignment = SvxCellVerJustify::Top;
+ break;
+ case os::ver_alignment_t::bottom:
+ maCurrentXf.meVerAlignment = SvxCellVerJustify::Bottom;
+ break;
+ case os::ver_alignment_t::middle:
+ maCurrentXf.meVerAlignment = SvxCellVerJustify::Center;
+ break;
+ case os::ver_alignment_t::justified:
+ maCurrentXf.meVerAlignment = SvxCellVerJustify::Block;
+ break;
+ case os::ver_alignment_t::distributed:
+ maCurrentXf.meVerAlignment = SvxCellVerJustify::Block;
+ maCurrentXf.meVerAlignMethod = SvxCellJustifyMethod::Distribute;
+ break;
+ case os::ver_alignment_t::unknown:
+ maCurrentXf.meVerAlignment = SvxCellVerJustify::Standard;
+ break;
+ default:
+ ;
+ }
+ maCurrentXf.mbApplyAlignment = true;
+}
+
+void ScOrcusImportXf::set_wrap_text(bool b)
+{
+ maCurrentXf.mbWrapText = b;
+}
+
+void ScOrcusImportXf::set_shrink_to_fit(bool b)
+{
+ maCurrentXf.mbShrinkToFit = b;
+}
+
+std::size_t ScOrcusImportXf::commit()
+{
+ mpXfs->push_back(maCurrentXf);
+ return mpXfs->size() - 1;
+}
+
+
+ScOrcusStyles::ScOrcusStyles( ScOrcusFactory& rFactory, bool bSkipDefaultStyles ) :
+ mrFactory(rFactory),
+ maFontStyle(rFactory, maFonts),
+ maFillStyle(maFills),
+ maBorderStyle(maBorders),
+ maCellProtection(maProtections),
+ maNumberFormat(rFactory, maNumberFormats),
+ maCellStyle(rFactory, *this, maCellStyleXfs)
+{
+ ScDocument& rDoc = rFactory.getDoc().getDoc();
+ if (!bSkipDefaultStyles && !rDoc.GetStyleSheetPool()->HasStandardStyles())
+ rDoc.GetStyleSheetPool()->CreateStandardStyles();
+}
+
+/*
+namespace {
+
+std::ostream& operator<<(std::ostream& rStrm, const Color& rColor)
+{
+ rStrm << "Red: " << (int)rColor.GetRed() << ", Green: " << (int)rColor.GetGreen() << ", Blue: " << (int)rColor.GetBlue();
+ return rStrm;
+}
+
+}
+*/
+
+void ScOrcusStyles::applyXfToItemSet( SfxItemSet& rSet, const ScOrcusXf& rXf )
+{
+ size_t nFontId = rXf.mnFontId;
+ if (nFontId >= maFonts.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid font id");
+ return;
+ }
+
+ maFonts[nFontId].applyToItemSet(rSet);
+
+ size_t nFillId = rXf.mnFillId;
+ if (nFillId >= maFills.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid fill id");
+ return;
+ }
+
+ maFills[nFillId].applyToItemSet(rSet);
+
+ size_t nBorderId = rXf.mnBorderId;
+ if (nBorderId >= maBorders.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid border id");
+ return;
+ }
+ maBorders[nBorderId].applyToItemSet(rSet);
+
+ size_t nProtectionId = rXf.mnProtectionId;
+ if (nProtectionId >= maProtections.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid protection id");
+ return;
+ }
+
+ maProtections[nProtectionId].applyToItemSet(rSet);
+
+ size_t nNumberFormatId = rXf.mnNumberFormatId;
+ if (nNumberFormatId >= maNumberFormats.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid number format id");
+ return;
+ }
+ const ScOrcusNumberFormat& rFormat = maNumberFormats[nNumberFormatId];
+ rFormat.applyToItemSet(rSet, mrFactory.getDoc().getDoc());
+
+ if (rXf.mbApplyAlignment)
+ {
+ rSet.Put(SvxHorJustifyItem(rXf.meHorAlignment, ATTR_HOR_JUSTIFY));
+ rSet.Put(SvxVerJustifyItem(rXf.meVerAlignment, ATTR_VER_JUSTIFY));
+ rSet.Put(SvxJustifyMethodItem(rXf.meHorAlignMethod, ATTR_HOR_JUSTIFY_METHOD));
+ rSet.Put(SvxJustifyMethodItem(rXf.meVerAlignMethod, ATTR_VER_JUSTIFY_METHOD));
+ }
+
+ if (rXf.mbWrapText)
+ rSet.Put(ScLineBreakCell(*rXf.mbWrapText));
+
+ if (rXf.mbShrinkToFit)
+ rSet.Put(ScShrinkToFitCell(*rXf.mbShrinkToFit));
+}
+
+void ScOrcusStyles::applyXfToItemSet( SfxItemSet& rSet, std::size_t xfId )
+{
+ SAL_INFO("sc.orcus.style", "applyXfToitemSet: " << xfId);
+ if (maCellXfs.size() <= xfId)
+ {
+ SAL_WARN("sc.orcus.style", "invalid xf id");
+ return;
+ }
+
+ applyXfToItemSet(rSet, maCellXfs[xfId]);
+}
+
+os::iface::import_font_style* ScOrcusStyles::start_font_style()
+{
+ maFontStyle.reset();
+ return &maFontStyle;
+}
+
+os::iface::import_fill_style* ScOrcusStyles::start_fill_style()
+{
+ maFillStyle.reset();
+ return &maFillStyle;
+}
+
+os::iface::import_border_style* ScOrcusStyles::start_border_style()
+{
+ maBorderStyle.reset();
+ return &maBorderStyle;
+}
+
+os::iface::import_cell_protection* ScOrcusStyles::start_cell_protection()
+{
+ maCellProtection.reset();
+ return &maCellProtection;
+}
+
+os::iface::import_number_format* ScOrcusStyles::start_number_format()
+{
+ maNumberFormat.reset();
+ return &maNumberFormat;
+}
+
+os::iface::import_xf* ScOrcusStyles::start_xf(os::xf_category_t cat)
+{
+ switch (cat)
+ {
+ case os::xf_category_t::cell:
+ maXf.reset(maCellXfs);
+ break;
+ case os::xf_category_t::cell_style:
+ maXf.reset(maCellStyleXfs);
+ break;
+ case os::xf_category_t::differential:
+ maXf.reset(maCellDiffXfs);
+ break;
+ case os::xf_category_t::unknown:
+ SAL_WARN("sc.orcus.style", "unknown xf category");
+ return nullptr;
+ }
+
+ return &maXf;
+}
+
+os::iface::import_cell_style* ScOrcusStyles::start_cell_style()
+{
+ maCellStyle.reset();
+ return &maCellStyle;
+}
+
+void ScOrcusStyles::set_font_count(size_t /*n*/)
+{
+}
+
+void ScOrcusStyles::set_fill_count(size_t /*n*/)
+{
+}
+
+void ScOrcusStyles::set_border_count(size_t /*n*/)
+{
+}
+
+void ScOrcusStyles::set_number_format_count(size_t /*n*/)
+{
+}
+
+void ScOrcusStyles::set_xf_count(os::xf_category_t /*cat*/, size_t /*n*/)
+{
+}
+
+void ScOrcusStyles::set_cell_style_count(size_t /*n*/)
+{
+}
+
+// auto filter import
+
+ScOrcusAutoFilter::ScOrcusAutoFilter( const ScOrcusGlobalSettings& rGS ) :
+ mrGlobalSettings(rGS)
+{
+}
+
+ScOrcusAutoFilter::~ScOrcusAutoFilter()
+{
+}
+
+void ScOrcusAutoFilter::set_range(const os::range_t& range)
+{
+ maRange.aStart.SetRow(range.first.row);
+ maRange.aStart.SetCol(range.first.column);
+ maRange.aEnd.SetRow(range.last.row);
+ maRange.aEnd.SetCol(range.last.column);
+}
+
+void ScOrcusAutoFilter::set_column(os::col_t col)
+{
+ SAL_INFO("sc.orcus.autofilter", "set_column: " << col);
+}
+
+void ScOrcusAutoFilter::append_column_match_value(std::string_view value)
+{
+ OUString aString(value.data(), value.size(), mrGlobalSettings.getTextEncoding());
+ SAL_INFO("sc.orcus.autofilter", "append_column_match_value: " << aString);
+}
+
+void ScOrcusAutoFilter::commit_column()
+{
+ SAL_INFO("sc.orcus.autofilter", "commit column");
+}
+
+void ScOrcusAutoFilter::commit()
+{
+ SAL_INFO("sc.orcus.autofilter", "commit");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/orcusfiltersimpl.cxx b/sc/source/filter/orcus/orcusfiltersimpl.cxx
new file mode 100644
index 0000000000..2a13c761d5
--- /dev/null
+++ b/sc/source/filter/orcus/orcusfiltersimpl.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/.
+ */
+
+#include <orcusfiltersimpl.hxx>
+#include <orcusinterface.hxx>
+#include <tokenarray.hxx>
+
+#include <osl/thread.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/itemset.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <orcus/format_detection.hpp>
+#include <orcus/orcus_import_ods.hpp>
+#include <orcus/stream.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+using namespace com::sun::star;
+
+namespace
+{
+uno::Reference<task::XStatusIndicator> getStatusIndicator(const SfxMedium& rMedium)
+{
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ const SfxUnoAnyItem* pItem
+ = rMedium.GetItemSet().GetItem<SfxUnoAnyItem>(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ xStatusIndicator.set(pItem->GetValue(), uno::UNO_QUERY);
+ return xStatusIndicator;
+}
+
+bool loadFileContent(SfxMedium& rMedium, orcus::iface::import_filter& filter)
+{
+ SvStream* pStream = rMedium.GetInStream();
+ pStream->Seek(0);
+ static const size_t nReadBuffer = 1024 * 32;
+ OStringBuffer aBuffer((int(nReadBuffer)));
+ size_t nRead = 0;
+ do
+ {
+ char pData[nReadBuffer];
+ nRead = pStream->ReadBytes(pData, nReadBuffer);
+ aBuffer.append(pData, nRead);
+ } while (nRead == nReadBuffer);
+
+ try
+ {
+ filter.read_stream(aBuffer);
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("sc", "Unable to load file via orcus filter! " << e.what());
+ return false;
+ }
+
+ return true;
+}
+}
+
+ScOrcusFilters::ImportResult ScOrcusFiltersImpl::importByName(ScDocument& rDoc, SfxMedium& rMedium,
+ const OUString& rFilterName) const
+{
+ const std::unordered_map<OUString, orcus::format_t> aMap = {
+ { "Apache Parquet Spreadsheet", orcus::format_t::parquet },
+ { "Gnumeric Spreadsheet", orcus::format_t::gnumeric },
+ { "MS Excel 2003 XML Orcus", orcus::format_t::xls_xml },
+ { "csv", orcus::format_t::csv },
+ { "gnumeric", orcus::format_t::gnumeric },
+ { "ods", orcus::format_t::ods },
+ { "parquet", orcus::format_t::parquet },
+ { "xls-xml", orcus::format_t::xls_xml },
+ { "xlsx", orcus::format_t::xlsx },
+ };
+
+ if (auto it = aMap.find(rFilterName); it != aMap.end())
+ {
+ ScOrcusFactory aFactory(rDoc);
+ aFactory.setStatusIndicator(getStatusIndicator(rMedium));
+
+ auto filter = orcus::create_filter(it->second, &aFactory);
+ if (!filter)
+ return ImportResult::Failure;
+
+ bool res = loadFileContent(rMedium, *filter);
+ return res ? ImportResult::Success : ImportResult::Failure;
+ }
+
+ return ImportResult::NotSupported;
+}
+
+bool ScOrcusFiltersImpl::importODS_Styles(ScDocument& rDoc, OUString& aPath) const
+{
+ try
+ {
+#if defined _WIN32
+ OString aPath8 = OUStringToOString(aPath, RTL_TEXTENCODING_UTF8);
+#else
+ OString aPath8 = OUStringToOString(aPath, osl_getThreadTextEncoding());
+#endif
+ orcus::file_content content(aPath8);
+ ScOrcusFactory aFactory(rDoc);
+ ScOrcusStyles aStyles(aFactory);
+ orcus::import_ods::read_styles(content.str(), &aStyles);
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("sc", "Unable to load styles from xml file! " << e.what());
+ return false;
+ }
+
+ return true;
+}
+
+std::unique_ptr<ScOrcusXMLContext> ScOrcusFiltersImpl::createXMLContext(ScDocument& rDoc,
+ const OUString& rPath) const
+{
+ return std::make_unique<ScOrcusXMLContextImpl>(rDoc, rPath);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/xmlcontext.cxx b/sc/source/filter/orcus/xmlcontext.cxx
new file mode 100644
index 0000000000..1588fad342
--- /dev/null
+++ b/sc/source/filter/orcus/xmlcontext.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <orcusfiltersimpl.hxx>
+#include <orcusinterface.hxx>
+#include <orcusxml.hxx>
+#include <document.hxx>
+#include <tokenarray.hxx>
+
+#include <utility>
+#include <vcl/weld.hxx>
+#include <ucbhelper/content.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+
+#include <orcus/xml_structure_tree.hpp>
+#include <orcus/xml_namespace.hpp>
+#include <orcus/orcus_xml.hpp>
+#include <orcus/sax_parser_base.hpp>
+#include <orcus/stream.hpp>
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <string>
+#include <sstream>
+
+namespace com::sun::star::ucb { class XCommandEnvironment; }
+
+#define BUFFER_SIZE 4096
+
+using namespace com::sun::star;
+
+namespace {
+
+ScOrcusXMLTreeParam::EntryData& setUserDataToEntry(weld::TreeView& rControl,
+ const weld::TreeIter& rEntry, ScOrcusXMLTreeParam::UserDataStoreType& rStore, ScOrcusXMLTreeParam::EntryType eType)
+{
+ rStore.push_back(std::make_unique<ScOrcusXMLTreeParam::EntryData>(eType));
+ rControl.set_id(rEntry, weld::toId(rStore.back().get()));
+ return *rStore.back();
+}
+
+void setEntityNameToUserData(
+ ScOrcusXMLTreeParam::EntryData& rEntryData,
+ const orcus::xml_structure_tree::entity_name& entity, const orcus::xml_structure_tree::walker& walker)
+{
+ rEntryData.mnNamespaceID = walker.get_xmlns_index(entity.ns);
+}
+
+OUString toString(const orcus::xml_structure_tree::entity_name& entity, const orcus::xml_structure_tree::walker& walker)
+{
+ OUStringBuffer aBuf;
+ if (entity.ns)
+ {
+ // Namespace exists. Use the short version of the xml namespace name for display.
+ std::string aShortName = walker.get_xmlns_short_name(entity.ns);
+ aBuf.appendAscii(aShortName.c_str());
+ aBuf.append(':');
+ }
+ aBuf.append(OUString(entity.name.data(), entity.name.size(), RTL_TEXTENCODING_UTF8));
+ return aBuf.makeStringAndClear();
+}
+
+void populateTree(
+ weld::TreeView& rTreeCtrl, orcus::xml_structure_tree::walker& rWalker,
+ const orcus::xml_structure_tree::entity_name& rElemName, bool bRepeat,
+ const weld::TreeIter* pParent, ScOrcusXMLTreeParam& rParam)
+{
+ OUString sEntry(toString(rElemName, rWalker));
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeCtrl.make_iterator());
+ rTreeCtrl.insert(pParent, -1, &sEntry, nullptr, nullptr, nullptr, false, xEntry.get());
+ rTreeCtrl.set_image(*xEntry, rParam.maImgElementDefault, -1);
+
+ ScOrcusXMLTreeParam::EntryData& rEntryData = setUserDataToEntry(rTreeCtrl,
+ *xEntry, rParam.m_UserDataStore,
+ bRepeat ? ScOrcusXMLTreeParam::ElementRepeat : ScOrcusXMLTreeParam::ElementDefault);
+
+ setEntityNameToUserData(rEntryData, rElemName, rWalker);
+
+ if (bRepeat)
+ {
+ // Recurring elements use different icon.
+ rTreeCtrl.set_image(*xEntry, rParam.maImgElementRepeat, -1);
+ }
+
+ orcus::xml_structure_tree::entity_names_type aNames = rWalker.get_attributes();
+
+ // Insert attributes.
+ for (const orcus::xml_structure_tree::entity_name& rAttrName : aNames)
+ {
+ OUString sAttr(toString(rAttrName, rWalker));
+ std::unique_ptr<weld::TreeIter> xAttr(rTreeCtrl.make_iterator());
+ rTreeCtrl.insert(xEntry.get(), -1, &sAttr, nullptr, nullptr, nullptr, false, xAttr.get());
+
+ ScOrcusXMLTreeParam::EntryData& rAttrData =
+ setUserDataToEntry(rTreeCtrl, *xAttr, rParam.m_UserDataStore, ScOrcusXMLTreeParam::Attribute);
+ setEntityNameToUserData(rAttrData, rAttrName, rWalker);
+
+ rTreeCtrl.set_image(*xAttr, rParam.maImgAttribute, -1);
+ }
+
+ aNames = rWalker.get_children();
+
+ // Non-leaf if it has child elements, leaf otherwise.
+ rEntryData.mbLeafNode = aNames.empty();
+
+ // Insert child elements recursively.
+ for (const auto& rName : aNames)
+ {
+ orcus::xml_structure_tree::element aElem = rWalker.descend(rName);
+ populateTree(rTreeCtrl, rWalker, rName, aElem.repeat, xEntry.get(), rParam);
+ rWalker.ascend();
+ }
+}
+
+class TreeUpdateSwitch
+{
+ weld::TreeView& mrTreeCtrl;
+public:
+ explicit TreeUpdateSwitch(weld::TreeView& rTreeCtrl) : mrTreeCtrl(rTreeCtrl)
+ {
+ mrTreeCtrl.freeze();
+ }
+
+ ~TreeUpdateSwitch()
+ {
+ mrTreeCtrl.thaw();
+ }
+};
+
+void loadContentFromURL(const OUString& rURL, std::string& rStrm)
+{
+ ucbhelper::Content aContent(
+ rURL, uno::Reference<ucb::XCommandEnvironment>(), comphelper::getProcessComponentContext());
+ uno::Reference<io::XInputStream> xStrm = aContent.openStream();
+
+ std::ostringstream aStrmBuf;
+ uno::Sequence<sal_Int8> aBytes;
+ size_t nBytesRead = 0;
+ do
+ {
+ nBytesRead = xStrm->readBytes(aBytes, BUFFER_SIZE);
+ const sal_Int8* p = aBytes.getConstArray();
+ aStrmBuf << std::string(p, p + nBytesRead);
+ }
+ while (nBytesRead == BUFFER_SIZE);
+
+ rStrm = aStrmBuf.str();
+}
+
+}
+
+ScOrcusXMLContextImpl::ScOrcusXMLContextImpl(ScDocument& rDoc, OUString aPath) :
+ ScOrcusXMLContext(), mrDoc(rDoc), maPath(std::move(aPath)) {}
+
+ScOrcusXMLContextImpl::~ScOrcusXMLContextImpl() {}
+
+void ScOrcusXMLContextImpl::loadXMLStructure(weld::TreeView& rTreeCtrl, ScOrcusXMLTreeParam& rParam)
+{
+ rParam.m_UserDataStore.clear();
+
+ std::string aStrm;
+ loadContentFromURL(maPath, aStrm);
+
+ if (aStrm.empty())
+ return;
+
+ orcus::xmlns_context cxt = maNsRepo.create_context();
+ orcus::xml_structure_tree aXmlTree(cxt);
+ try
+ {
+ aXmlTree.parse(aStrm);
+
+ TreeUpdateSwitch aSwitch(rTreeCtrl);
+ rTreeCtrl.clear();
+
+ orcus::xml_structure_tree::walker aWalker = aXmlTree.get_walker();
+
+ // Root element.
+ orcus::xml_structure_tree::element aElem = aWalker.root();
+ populateTree(rTreeCtrl, aWalker, aElem.name, aElem.repeat, nullptr, rParam);
+ }
+ catch (const orcus::malformed_xml_error& e)
+ {
+ SAL_WARN("sc.orcus", "Malformed XML error: " << e.what());
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("sc.orcus", "parsing failed with an unknown error " << e.what());
+ }
+
+ rTreeCtrl.all_foreach([&rTreeCtrl](weld::TreeIter& rEntry){
+ rTreeCtrl.expand_row(rEntry);
+ return false;
+ });
+}
+
+void ScOrcusXMLContextImpl::importXML(const ScOrcusImportXMLParam& rParam)
+{
+ ScOrcusFactory aFactory(mrDoc, true);
+
+ OUString aSysPath;
+ if (osl::FileBase::getSystemPathFromFileURL(maPath, aSysPath) != osl::FileBase::E_None)
+ return;
+
+ OString aOSysPath = OUStringToOString(aSysPath, RTL_TEXTENCODING_UTF8);
+ const char* path = aOSysPath.getStr();
+
+ try
+ {
+ orcus::orcus_xml filter(maNsRepo, &aFactory, nullptr);
+
+ // Define all used namespaces.
+ for (std::size_t index : rParam.maNamespaces)
+ {
+ orcus::xmlns_id_t nsid = maNsRepo.get_identifier(index);
+ if (nsid == orcus::XMLNS_UNKNOWN_ID)
+ continue;
+
+ std::ostringstream os;
+ os << "ns" << index;
+ std::string alias = os.str();
+ filter.set_namespace_alias(alias, nsid);
+ }
+
+ // Set cell links.
+ for (const ScOrcusImportXMLParam::CellLink& rLink : rParam.maCellLinks)
+ {
+ OUString aTabName;
+ mrDoc.GetName(rLink.maPos.Tab(), aTabName);
+ filter.set_cell_link(
+ rLink.maPath,
+ aTabName.toUtf8(),
+ rLink.maPos.Row(), rLink.maPos.Col());
+ }
+
+ // Set range links.
+ for (const ScOrcusImportXMLParam::RangeLink& rLink : rParam.maRangeLinks)
+ {
+ OUString aTabName;
+ mrDoc.GetName(rLink.maPos.Tab(), aTabName);
+ filter.start_range(
+ aTabName.toUtf8(),
+ rLink.maPos.Row(), rLink.maPos.Col());
+
+ std::for_each(rLink.maFieldPaths.begin(), rLink.maFieldPaths.end(),
+ [&filter](const OString& rFieldPath)
+ {
+ filter.append_field_link(rFieldPath, std::string_view());
+ }
+ );
+
+ std::for_each(rLink.maRowGroups.begin(), rLink.maRowGroups.end(),
+ [&filter] (const OString& rRowGroup)
+ {
+ filter.set_range_row_group(rRowGroup);
+ }
+ );
+
+ filter.commit_range();
+ }
+
+ orcus::file_content content(path);
+ filter.read_stream(content.str());
+
+ aFactory.finalize();
+ }
+ catch (const std::exception&)
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/qpro/README b/sc/source/filter/qpro/README
new file mode 100644
index 0000000000..9f49f70576
--- /dev/null
+++ b/sc/source/filter/qpro/README
@@ -0,0 +1,4 @@
+Quattro Pro filter
+
+Old spec files can be found at:
+http://web.archive.org/web/20021020204833/http://www.corel.com/partners_developers/ds/co32sdk/docs/qp7/qpf1copy.htm
diff --git a/sc/source/filter/qpro/qpro.cxx b/sc/source/filter/qpro/qpro.cxx
new file mode 100644
index 0000000000..5ee451af9a
--- /dev/null
+++ b/sc/source/filter/qpro/qpro.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 <sal/log.hxx>
+
+#include <qproform.hxx>
+#include <qpro.hxx>
+#include <qprostyle.hxx>
+
+#include <scerrors.hxx>
+#include <ftools.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <tools/stream.hxx>
+#include <unotools/configmgr.hxx>
+#include <docoptio.hxx>
+#include <scdll.hxx>
+#include <memory>
+
+ErrCode ScQProReader::readSheet( SCTAB nTab, ScDocument& rDoc, ScQProStyle *pStyle )
+{
+ ErrCode eRet = ERRCODE_NONE;
+ sal_uInt8 nCol, nDummy;
+ sal_uInt16 nRow;
+ sal_uInt16 nStyle;
+ bool bEndOfSheet = false;
+
+ SAL_INFO("sc", "Read sheet " << nTab);
+
+ while( ERRCODE_NONE == eRet && !bEndOfSheet && nextRecord() )
+ {
+ switch( getId() )
+ {
+ case 0x000f:{ // Label cell
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadUChar( nDummy );
+ sal_uInt16 nLen = getLength();
+ if (!mpStream->good() || nLen < 7)
+ eRet = SCERR_IMPORT_FORMAT;
+ else
+ {
+ OUString aLabel(readString(nLen - 7));
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetTextCell(ScAddress(nCol,nRow,nTab), aLabel);
+ }
+ }
+ break;
+
+ case 0x00cb: // End of sheet
+ bEndOfSheet = true;
+ break;
+
+ case 0x000c: // Blank cell
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ break;
+
+ case 0x000d:{ // Integer cell
+ sal_Int16 nValue;
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadInt16( nValue );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetValue(ScAddress(nCol,nRow,nTab), static_cast<double>(nValue));
+ }
+ break;
+
+ case 0x000e:{ // Floating point cell
+ double nValue;
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadDouble( nValue );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetValue(ScAddress(nCol,nRow,nTab), nValue);
+ }
+ break;
+
+ case 0x0010:
+ {
+ // Formula cell
+ double nValue;
+ sal_uInt16 nState, nLen;
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadDouble( nValue ).ReadUInt16( nState ).ReadUInt16( nLen );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ ScAddress aAddr( nCol, nRow, nTab );
+ std::unique_ptr<ScTokenArray> pArray;
+
+ QProToSc aConv(*mpStream, rDoc.GetSharedStringPool(), aAddr);
+ if (ConvErr::OK != aConv.Convert( rDoc, pArray ))
+ eRet = SCERR_IMPORT_FORMAT;
+ else
+ {
+ ScFormulaCell* pFormula = new ScFormulaCell(rDoc, aAddr, std::move(pArray));
+ nStyle = nStyle >> 3;
+ pFormula->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetFormulaCell(ScAddress(nCol,nRow,nTab), pFormula);
+ }
+ }
+ break;
+ }
+ }
+ return eRet;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScImportQuattroPro(SvStream *pStream, ScDocument& rDoc)
+{
+ ScQProReader aReader(pStream);
+ ErrCode eRet = aReader.import(rDoc);
+ return eRet;
+}
+
+ScQProReader::ScQProReader(SvStream* pStream)
+ : mnId(0)
+ , mnLength(0)
+ , mnOffset(0)
+ , mpStream(pStream)
+ , mbEndOfFile(false)
+ , mnMaxTab(utl::ConfigManager::IsFuzzing() ? 128 : MAXTAB)
+{
+ if( mpStream )
+ {
+ mpStream->SetBufferSize( 65535 );
+ mpStream->SetStreamCharSet( RTL_TEXTENCODING_MS_1252 );
+ }
+}
+
+ScQProReader::~ScQProReader()
+{
+ if( mpStream )
+ mpStream->SetBufferSize( 0 );
+}
+
+ErrCode ScQProReader::parse(ScDocument& rDoc)
+{
+ ErrCode eRet = ERRCODE_NONE;
+ sal_uInt16 nVersion;
+ sal_uInt16 i = 1, j = 1;
+ SCTAB nTab = 0;
+ SetEof( false );
+
+ if( !recordsLeft() )
+ return SCERR_IMPORT_OPEN;
+
+ std::unique_ptr<ScQProStyle> pStyleElement( new ScQProStyle );
+
+ while( nextRecord() && eRet == ERRCODE_NONE)
+ {
+ switch( getId() )
+ {
+ case 0x0000: // Beginning of file
+ mpStream->ReadUInt16( nVersion );
+ break;
+
+ case 0x00ca: // Beginning of sheet
+ if (nTab <= mnMaxTab)
+ {
+ if( nTab < 26 )
+ {
+ OUString aName = OUStringChar( sal_Unicode('A' + nTab) );
+ if (!nTab)
+ rDoc.RenameTab( nTab, aName );
+ else
+ rDoc.InsertTab( nTab, aName );
+ }
+ eRet = readSheet( nTab, rDoc, pStyleElement.get() );
+ nTab++;
+ }
+ break;
+
+ case 0x0001: // End of file
+ SetEof( true );
+ break;
+
+ case 0x00ce:{ // Attribute cell
+ sal_uInt8 nFormat, nAlign, nFont;
+ sal_Int16 nColor;
+ mpStream->ReadUChar( nFormat ).ReadUChar( nAlign ).ReadInt16( nColor ).ReadUChar( nFont );
+ pStyleElement->setAlign( i, nAlign );
+ pStyleElement->setFont( i, nFont );
+ i++;
+ }
+ break;
+
+ case 0x00cf:{ // Font description
+ sal_uInt16 nPtSize, nFontAttr;
+ OUString aLabel;
+ mpStream->ReadUInt16( nPtSize ).ReadUInt16( nFontAttr );
+ pStyleElement->setFontRecord( j, nFontAttr, nPtSize );
+ sal_uInt16 nLen = getLength();
+ if (nLen >= 4)
+ aLabel = readString(nLen - 4);
+ else
+ eRet = SCERR_IMPORT_FORMAT;
+ pStyleElement->setFontType( j, aLabel );
+ j++;
+ }
+ break;
+ }
+ }
+ return eRet;
+}
+
+ErrCode ScQProReader::import( ScDocument& rDoc)
+{
+ ErrCode eRet = parse(rDoc);
+ rDoc.CalcAfterLoad();
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportQPW(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ aDocument.SetImportingXML(true);
+ aDocument.SetHardRecalcState(ScDocument::HardRecalcState::ETERNAL);
+
+ ScQProReader aReader(&rStream);
+ ErrCode eRet = aReader.parse(aDocument);
+ return eRet == ERRCODE_NONE;
+}
+
+bool ScQProReader::recordsLeft()
+{
+ return mpStream && mpStream->good();
+}
+
+bool ScQProReader::nextRecord()
+{
+ if( !recordsLeft() )
+ return false;
+
+ if( mbEndOfFile )
+ return false;
+
+ sal_uInt32 nPos = mpStream->Tell();
+ if( nPos != mnOffset + mnLength )
+ mpStream->Seek( mnOffset + mnLength );
+
+ mnLength = mnId = 0;
+ mpStream->ReadUInt16( mnId ).ReadUInt16( mnLength );
+
+ mnOffset = mpStream->Tell();
+#ifdef DEBUG_SC_QPRO
+ fprintf( stderr, "Read record 0x%x length 0x%x at offset 0x%x\n",
+ (unsigned)mnId, (unsigned)mnLength, (unsigned)mnOffset );
+
+#if 1 // rather verbose
+ int len = mnLength;
+ while (len > 0) {
+ int i, chunk = std::min(len, 16);
+ unsigned char data[16];
+ mpStream->Read( data, chunk );
+
+ for (i = 0; i < chunk; i++)
+ fprintf( stderr, "%.2x ", data[i] );
+ fprintf( stderr, "| " );
+ for (i = 0; i < chunk; i++)
+ fprintf( stderr, "%c", data[i] < 127 && data[i] > 30 ? data[i] : '.' );
+ fprintf( stderr, "\n" );
+
+ len -= chunk;
+ }
+ mpStream->Seek( mnOffset );
+#endif
+#endif
+ return true;
+}
+
+OUString ScQProReader::readString(sal_uInt16 nLength)
+{
+ return read_uInt8s_ToOUString(*mpStream, nLength, mpStream->GetStreamCharSet());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/qpro/qproform.cxx b/sc/source/filter/qpro/qproform.cxx
new file mode 100644
index 0000000000..9ece7da03f
--- /dev/null
+++ b/sc/source/filter/qpro/qproform.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+#include <qproform.hxx>
+#include <formel.hxx>
+#include <tokstack.hxx>
+
+void QProToSc::ReadSRD( const ScDocument& rDoc, ScSingleRefData& rSRD, sal_Int8 nPage, sal_Int8 nCol, sal_uInt16 nRelBit )
+{
+ sal_uInt16 nTmp = nRelBit & 0x1fff;
+ rSRD.InitAddress( ScAddress( nCol, (~nTmp + 1), 0 ) );
+ if( nRelBit & 0x4000 )
+ {
+ rSRD.SetRelCol(nCol);
+ }
+ else
+ {
+ rSRD.SetAbsCol(nCol);
+ }
+
+ if( nRelBit & 0x2000 )
+ {
+ SCROW nRelRow = static_cast<sal_Int16>(nTmp << 3); // This looks weird... Mistake?
+ nRelRow /= 8;
+ rSRD.SetRelRow(nRelRow);
+ }
+ else
+ {
+ rSRD.SetAbsRow(nTmp);
+ }
+ if( nRelBit & 0x8000 )
+ {
+ rSRD.SetRelTab(nPage);
+ }
+ else
+ {
+ rSRD.SetAbsTab(nPage);
+ }
+ if (rSRD.toAbs(rDoc, aEingPos).Tab() != aEingPos.Tab())
+ rSRD.SetFlag3D(true);
+}
+
+QProToSc::QProToSc( SvStream& rStream, svl::SharedStringPool& rSPool, const ScAddress& rRefPos ) :
+ ConverterBase(rSPool),
+ maIn( rStream )
+{
+ aEingPos = rRefPos;
+}
+
+void QProToSc::DoFunc( DefTokenId eOc, sal_uInt16 nArgs, const char* pExtString )
+{
+ TokenId eParam[ nBufSize ];
+ sal_Int32 nCount;
+ TokenId nPush;
+
+ bool bAddIn = false;
+
+ if( eOc == ocNoName )
+ {
+ bAddIn = true;
+ if( pExtString )
+ {
+ OString s = OString::Concat("QPRO_") + pExtString;
+ nPush = aPool.Store(eOc, OStringToOUString(s, maIn.GetStreamCharSet()));
+ aPool << nPush;
+ }
+ else
+ aPool << ocNoName;
+ }
+
+ if( nArgs < nBufSize )
+ {
+ for( nCount = 0; nCount < nArgs && aStack.HasMoreTokens() ; nCount++ )
+ aStack >> eParam[ nCount ];
+
+ if (nCount < nArgs)
+ // Adapt count to reality. All sort of binary crap is possible.
+ nArgs = static_cast<sal_uInt16>(nCount);
+ }
+ else
+ return;
+
+ switch( eOc )
+ {
+ case ocIndex:
+ nPush = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nPush;
+ IncToken( eParam[ 0 ] );
+ IncToken( eParam[ 1 ] );
+ break;
+
+ case ocIRR:
+ nPush = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nPush;
+ break;
+
+ case ocGetYear:
+ nPush = aPool.Store( 1900.0 );
+ aPool << ocOpen;
+ break;
+
+ default:
+ break;
+ }
+
+ if( !bAddIn )
+ aPool << eOc;
+
+ aPool << ocOpen;
+
+ if( nArgs> 0 )
+ {
+ if( eOc == ocRRI )
+ {
+ // There should be at least 3 arguments, but with binary crap may not...
+ SAL_WARN_IF( nArgs < 3, "sc.filter","QProToSc::DoFunc - ocRRI expects 3 parameters but got " << nArgs);
+ // Store first 3 parameters to pool in order 2,1,0
+ if (nArgs > 3)
+ nArgs = 3;
+ }
+ else if( eOc == ocIpmt )
+ {
+ // There should be at least 4 arguments, but with binary crap may not...
+ SAL_WARN_IF( nArgs < 4, "sc.filter","QProToSc::DoFunc - ocIpmt expects 4 parameters but got " << nArgs);
+ // Store first 4 parameters to pool in order 3,2,1,0
+ if (nArgs > 4)
+ nArgs = 4;
+ }
+
+ sal_Int16 nLast = nArgs - 1;
+ aPool << eParam[ nLast ];
+ for( nCount = nLast - 1 ; nCount >= 0 ; nCount-- )
+ {
+ aPool << ocSep << eParam[ nCount ];
+ }
+ }
+
+ if( eOc == ocGetYear )
+ aPool << ocClose << ocSub << nPush;
+ else if( eOc == ocFixed )
+ aPool << ocSep << ocTrue << ocOpen << ocClose;
+
+ aPool << ocClose;
+ aPool >> aStack;
+}
+
+void QProToSc::IncToken( TokenId &rParam )
+{
+ aPool << ocOpen << rParam << mnAddToken;
+ rParam = aPool.Store();
+}
+
+#define SAFEDEC_OR_RET(nRef, amt, ret) \
+do { \
+ if (nRef < amt)\
+ return ret; \
+ nRef-=amt; \
+} while(false)
+
+#define SAFEREAD_OR_BREAK( aStream, i, nRef, eRet, ret ) \
+ if (!aStream.good()) \
+ { \
+ i = nRef-1; /* will be incremented at end of while */ \
+ eRet = ret; \
+ break; /* switch */ \
+ }
+
+ConvErr QProToSc::Convert( const ScDocument& rDoc, std::unique_ptr<ScTokenArray>& pArray )
+{
+ sal_uInt8 nFmla[ nBufSize ] = {0};
+ sal_uInt8 nArgArray[ nBufSize ] = {0};
+ sal_Int8 nCol, nPage;
+ sal_uInt16 nIntCount = 0, nStringCount = 0, nFloatCount = 0, nDLLCount = 0, nArgCount = 0;
+ sal_uInt16 nIntArray[ nBufSize ] = {0};
+ OUString sStringArray[ nBufSize ];
+ sal_uInt16 nDLLArray[ nBufSize ] = {0};
+ sal_uInt16 nNote, nRelBits;
+ TokenId nPush;
+ ScComplexRefData aCRD;
+ ScSingleRefData aSRD;
+ QPRO_FUNC_TYPE eType;
+ DefTokenId eOc;
+ double nFloatArray[ nBufSize ] = {0};
+ const char* pExtString = nullptr;
+
+ aCRD.InitFlags();
+ aSRD.InitFlags();
+ sal_uInt16 nRef = 0;
+ maIn.ReadUInt16( nRef );
+
+ if( nRef < nBufSize )
+ {
+ for( sal_uInt16 i=0; i < nRef; i++)
+ {
+ maIn.ReadUChar( nFmla[i] );
+
+ if( nFmla[ i ] == 0x05 )
+ {
+ sal_uInt16 nInt = 0;
+ maIn.ReadUInt16( nInt );
+ nIntArray[ nIntCount ] = nInt;
+ SAFEDEC_OR_RET(nRef, 2, ConvErr::Count);
+ nIntCount++;
+ }
+
+ if( nFmla[ i ] == 0x00 )
+ {
+ double nFloat = 0;
+ maIn.ReadDouble( nFloat );
+ nFloatArray[ nFloatCount ] = nFloat;
+ SAFEDEC_OR_RET(nRef, 8, ConvErr::Count);
+ nFloatCount++;
+ }
+
+ if( nFmla[ i ] == 0x1a )
+ {
+ sal_uInt8 nArg = 0;
+ sal_uInt16 nDummy, nDLLId = 0;
+ maIn.ReadUChar( nArg ).ReadUInt16( nDummy ).ReadUInt16( nDLLId );
+ nArgArray[ nArgCount ] = nArg;
+ nDLLArray[ nDLLCount ] = nDLLId;
+ SAFEDEC_OR_RET(nRef, 5, ConvErr::Count);
+ nDLLCount++;
+ nArgCount++;
+ }
+ if( nFmla[ i ] == 0x06 )
+ {
+ OUString aTmp(::read_zeroTerminated_uInt8s_ToOUString(maIn, maIn.GetStreamCharSet()));
+ sStringArray[ nStringCount ] = aTmp;
+ nStringCount++;
+ SAFEDEC_OR_RET(nRef, aTmp.getLength() + 1, ConvErr::Count);
+ }
+ }
+ }
+ else
+ return ConvErr::Count;
+
+ sal_uInt16 i = 0;
+ nIntCount = 0;
+ nFloatCount = 0;
+ nDLLCount = 0;
+ nArgCount = 0;
+ nStringCount = 0;
+ ConvErr eRet = ConvErr::OK;
+
+ while( i < nRef && ( nFmla[ i ] != 0x03 ) )
+ {
+ eType = IndexToType( nFmla[ i ] );
+ eOc = IndexToToken( nFmla[ i ] );
+ if( eOc == ocNoName )
+ pExtString = getString( nFmla[ i ] );
+
+ switch( eType )
+ {
+ case FT_NotImpl:
+ DoFunc( ocNoName, 0, pExtString );
+ break;
+
+ case FT_FuncFix0:
+ DoFunc( eOc, 0, nullptr );
+ break;
+
+ case FT_FuncFix1:
+ DoFunc( eOc, 1, nullptr );
+ break;
+
+ case FT_FuncFix2:
+ DoFunc( eOc, 2, nullptr );
+ break;
+
+ case FT_FuncFix3:
+ DoFunc( eOc, 3, nullptr );
+ break;
+
+ case FT_FuncFix4:
+ DoFunc( eOc, 4, nullptr );
+ break;
+
+ case FT_FuncFix5:
+ DoFunc( eOc, 5, nullptr );
+ break;
+
+ case FT_FuncFix6:
+ DoFunc( eOc, 6, nullptr );
+ break;
+
+ case FT_DLL:{
+ eOc = IndexToDLLId( nDLLArray[ nDLLCount ] );
+ sal_uInt8 nPar = nArgArray[ nArgCount ];
+ DoFunc( eOc, nPar, nullptr );
+ nDLLCount++;
+ nArgCount++;
+ }
+ break;
+
+ case FT_Cref : // Single cell reference
+ maIn.ReadUInt16( nNote ).ReadSChar( nCol ).ReadSChar( nPage ).ReadUInt16( nRelBits );
+ SAFEREAD_OR_BREAK( maIn, i, nRef, eRet, ConvErr::Count);
+ ReadSRD( rDoc, aSRD, nPage, nCol, nRelBits );
+ aStack << aPool.Store( aSRD );
+ break;
+
+ case FT_Range: // Block reference
+ maIn.ReadUInt16( nNote ).ReadSChar( nCol ).ReadSChar( nPage ).ReadUInt16( nRelBits );
+ SAFEREAD_OR_BREAK( maIn, i, nRef, eRet, ConvErr::Count);
+ ReadSRD( rDoc, aCRD.Ref1, nPage, nCol, nRelBits );
+ maIn.ReadSChar( nCol ).ReadSChar( nPage ).ReadUInt16( nRelBits );
+ SAFEREAD_OR_BREAK( maIn, i, nRef, eRet, ConvErr::Count);
+ ReadSRD( rDoc, aCRD.Ref2, nPage, nCol, nRelBits );
+ // Sheet name of second corner is not displayed if identical
+ if (aCRD.Ref1.IsFlag3D() && aCRD.Ref1.Tab() == aCRD.Ref2.Tab() &&
+ aCRD.Ref1.IsTabRel() == aCRD.Ref2.IsTabRel())
+ aCRD.Ref2.SetFlag3D( false);
+ aStack << aPool.Store( aCRD );
+ break;
+
+ case FT_FuncVar:{ // Sum of a sequence of numbers
+ i++;
+ sal_uInt8 nArgs = nFmla[ i ];
+ DoFunc( eOc, nArgs, nullptr );
+ }
+ break;
+
+ case FT_Op: // operators
+ aStack >> nPush;
+ aPool << aStack << eOc << nPush;
+ aPool >> aStack;
+ break;
+
+ case FT_Braces:
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+
+ case FT_ConstInt:{
+ sal_uInt16 nVal;
+ nVal = nIntArray[ nIntCount ];
+ aStack << aPool.Store( static_cast<double>(nVal) );
+ nIntCount++;
+ }
+ break;
+
+ case FT_ConstFloat:{
+ double nVal;
+ nVal = nFloatArray[ nFloatCount ];
+ aStack << aPool.Store( nVal );
+ nFloatCount++;
+ }
+ break;
+
+ case FT_ConstString:{
+ OUString aLabel(sStringArray[ nStringCount ]);
+ aStack << aPool.Store( aLabel );
+ nStringCount++;
+ }
+ break;
+
+ case FT_Neg:
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+
+ case FT_NOP: // indicates invalid opcode.
+ case FT_Return: // indicates end of formula
+ break;
+ }
+ i++;
+ }
+ pArray = aPool.GetTokenArray(rDoc, aStack.Get());
+ return eRet;
+}
+
+const struct
+{
+ DefTokenId nToken;
+ QPRO_FUNC_TYPE nType;
+} aFuncMap[] = {
+ { ocPush, FT_ConstFloat },
+ { ocPush, FT_Cref },
+ { ocPush, FT_Range },
+ { ocPush, FT_Return },
+ { ocPush, FT_Braces },
+ { ocPush, FT_ConstInt },
+ { ocPush, FT_ConstString },
+ { ocPush, FT_NOP },
+ { ocNegSub, FT_Neg }, // 0x08
+ { ocAdd, FT_Op },
+ { ocSub, FT_Op },
+ { ocMul, FT_Op },
+ { ocDiv, FT_Op },
+ { ocPow, FT_Op },
+ { ocEqual, FT_Op },
+ { ocNotEqual, FT_Op },
+ { ocLessEqual, FT_Op }, // 0x10
+ { ocGreaterEqual, FT_Op },
+ { ocLess, FT_Op },
+ { ocGreater, FT_Op },
+ { ocAnd, FT_Op },
+ { ocOr, FT_Op },
+ { ocNot, FT_FuncFix1 },
+ { ocPush, FT_NOP }, // Unary plus
+ { ocAddress, FT_FuncFix4 }, // Address of
+ { ocNoName, FT_NotImpl }, // Halt function
+ { ocNoName, FT_DLL }, // DLL function
+ { ocNoName, FT_NOP }, // Extended operands
+ { ocNoName, FT_NOP }, // Extended operands
+ { ocNoName, FT_NOP }, // Reserved
+ { ocNoName, FT_NOP }, // Reserved
+ { ocNotAvail, FT_FuncFix0 }, // NA
+ { ocNoName, FT_FuncFix0 }, // Error // 0x20
+ { ocAbs, FT_FuncFix1 },
+ { ocInt, FT_FuncFix1 },
+ { ocSqrt, FT_FuncFix1 },
+ { ocLog10, FT_FuncFix1 },
+ { ocLn, FT_FuncFix1 },
+ { ocPi, FT_FuncFix0 },
+ { ocSin, FT_FuncFix1 },
+ { ocCos, FT_FuncFix1 },
+ { ocTan, FT_FuncFix1 },
+ { ocArcTan2, FT_FuncFix2 },
+ { ocArcTan, FT_FuncFix1 },
+ { ocArcSin, FT_FuncFix1 },
+ { ocArcCos, FT_FuncFix1 },
+ { ocExp, FT_FuncFix1 },
+ { ocMod, FT_FuncFix2 },
+ { ocChoose, FT_FuncVar }, // 0x30
+ { ocIsNA, FT_FuncFix1 },
+ { ocIsError, FT_FuncFix1 },
+ { ocFalse, FT_FuncFix0 },
+ { ocTrue, FT_FuncFix0 },
+ { ocRandom, FT_FuncFix0 },
+ { ocGetDate, FT_FuncFix3 },
+ { ocGetActTime, FT_FuncFix0 },
+ { ocNoName, FT_NotImpl }, // QPro Pmt
+ { ocNoName, FT_NotImpl }, // QPro Pv
+ { ocNoName, FT_NotImpl }, // QPro Fv
+ { ocIf, FT_FuncFix3 },
+ { ocGetDay, FT_FuncFix1 },
+ { ocGetMonth, FT_FuncFix1 },
+ { ocGetYear, FT_FuncFix1 },
+ { ocRound, FT_FuncFix2 },
+ { ocGetTime, FT_FuncFix3 }, // 0x40
+ { ocGetHour, FT_FuncFix1 },
+ { ocGetMin, FT_FuncFix1 },
+ { ocGetSec, FT_FuncFix1 },
+ { ocIsValue, FT_FuncFix1 },
+ { ocIsString, FT_FuncFix1 },
+ { ocLen, FT_FuncFix1 },
+ { ocValue, FT_FuncFix1 },
+ { ocFixed, FT_FuncFix2 },
+ { ocMid, FT_FuncFix3 },
+ { ocChar, FT_FuncFix1 },
+ { ocCode, FT_FuncFix1 },
+ { ocFind, FT_FuncFix3 },
+ { ocGetDateValue, FT_FuncFix1 },
+ { ocGetTimeValue, FT_FuncFix1 },
+ { ocNoName, FT_NotImpl },
+ { ocSum, FT_FuncVar }, // 0x50
+ { ocAverage, FT_FuncVar },
+ { ocCount, FT_FuncVar },
+ { ocMin, FT_FuncVar },
+ { ocMax, FT_FuncVar },
+ { ocVLookup, FT_FuncFix3 },
+ { ocNPV, FT_FuncFix2 },
+ { ocVar, FT_FuncVar },
+ { ocNormDist, FT_FuncVar },
+ { ocIRR, FT_FuncFix2 },
+ { ocHLookup, FT_FuncFix3 },
+ { ocDBSum, FT_FuncFix3 },
+ { ocDBAverage, FT_FuncFix3 },
+ { ocDBCount, FT_FuncFix3 },
+ { ocDBMin, FT_FuncFix3 },
+ { ocDBMax, FT_FuncFix3 },
+ { ocDBVar, FT_FuncFix3 }, // 0x60
+ { ocDBStdDev, FT_FuncFix3 },
+ { ocNoName, FT_NotImpl },
+ { ocColumns, FT_FuncFix1 },
+ { ocRows, FT_FuncFix1 },
+ { ocRept, FT_FuncFix2 },
+ { ocUpper, FT_FuncFix1 },
+ { ocLower, FT_FuncFix1 },
+ { ocLeft, FT_FuncFix2 },
+ { ocRight, FT_FuncFix2 },
+ { ocReplace, FT_FuncFix4 },
+ { ocProper, FT_FuncFix1 },
+ { ocCell, FT_FuncFix2 },
+ { ocTrim, FT_FuncFix1 },
+ { ocClean, FT_FuncFix1 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl }, // 0x70
+ { ocExact, FT_FuncFix2 },
+ { ocNoName, FT_NotImpl }, // Call()
+ { ocIndirect, FT_FuncFix1 },
+ { ocRRI, FT_FuncFix3 }, // Interest
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocSLN, FT_FuncFix3 },
+ { ocSYD, FT_FuncFix4 },
+ { ocDDB, FT_FuncFix4 },
+ { ocStDevP, FT_FuncVar },
+ { ocVarP, FT_FuncVar },
+ { ocDBStdDevP, FT_FuncVar },
+ { ocDBVarP, FT_FuncVar },
+ { ocPV, FT_FuncFix3 }, // QPro Pval
+ { ocPMT, FT_FuncFix5 }, // QPro Paymt
+ { ocFV, FT_FuncFix3 }, // QPro Fval // 0x80
+ { ocNper, FT_FuncFix5 },
+ { ocRate, FT_FuncFix5 },
+ { ocIpmt, FT_FuncFix4 },
+ { ocPpmt, FT_FuncFix6 },
+ { ocSumProduct, FT_FuncFix2 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocDeg, FT_FuncFix1 },
+ { ocRad, FT_FuncFix1 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocGetActDate, FT_FuncFix0 },
+ { ocNPV, FT_FuncFix2 },
+ { ocNoName, FT_NotImpl }, // 0x90
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NOP },
+ { ocNoName, FT_NOP }, // 147
+ { ocNoName, FT_NOP }, // 148
+ { ocNoName, FT_NOP }, // 149
+ { ocNoName, FT_NOP }, // 150
+ { ocNoName, FT_NOP }, // 151
+ { ocNoName, FT_NOP }, // 152
+ { ocNoName, FT_NOP }, // 153
+ { ocSheet, FT_FuncFix1 },
+ { ocNoName, FT_NOP }, // 155 - opcodes do not represent any function.
+ { ocNoName, FT_NOP }, // 156
+ { ocIndex, FT_FuncFix4 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl }, // Gives the property of the particular object
+ { ocNoName, FT_NotImpl }, // Dynamic Data Exchange Link // 0x100
+ { ocNoName, FT_NotImpl } // gives properties of DOS menus
+};
+
+const int nIndexCount = SAL_N_ELEMENTS( aFuncMap );
+
+DefTokenId QProToSc::IndexToToken( sal_uInt16 nIndex )
+{
+ if( nIndex < nIndexCount )
+ return aFuncMap[ nIndex ].nToken;
+ return ocNoName;
+}
+
+QPRO_FUNC_TYPE QProToSc::IndexToType( sal_uInt8 nIndex )
+{
+ if( nIndex < nIndexCount )
+ return aFuncMap[ nIndex ].nType;
+ return FT_NotImpl;
+}
+
+DefTokenId QProToSc::IndexToDLLId( sal_uInt16 nIndex )
+{
+ DefTokenId eId;
+ switch( nIndex )
+ {
+ case 0x0001:
+ eId = ocAveDev;
+ break;
+
+ case 0x0024:
+ eId = ocGCD;
+ break;
+
+ case 0x0025:
+ eId = ocLCM;
+ break;
+
+ case 0x0027:
+ eId = ocCeil;
+ break;
+
+ case 0x0028:
+ eId = ocEven;
+ break;
+
+ case 0x0022:
+ eId = ocFact;
+ break;
+
+ case 0x002a:
+ eId = ocFloor;
+ break;
+
+ case 0x002d:
+ eId = ocOdd;
+ break;
+
+ case 0x0006:
+ eId = ocBetaDist;
+ break;
+
+ case 0x0008:
+ eId = ocBetaInv;
+ break;
+
+ case 0x0010:
+ eId = ocCovar;
+ break;
+
+ case 0x000b:
+ eId = ocChiInv;
+ break;
+
+ case 0x003d:
+ eId = ocPDuration;
+ break;
+
+ case 0x0019:
+ eId = ocFInv;
+ break;
+
+ case 0x001a:
+ eId = ocFisher;
+ break;
+
+ case 0x001b:
+ eId = ocFisherInv;
+ break;
+
+ case 0x0030:
+ eId = ocMedian;
+ break;
+
+ default:
+ eId = ocNoName;
+ break;
+ }
+ return eId;
+}
+
+const char* QProToSc::getString( sal_uInt8 nIndex )
+{
+ const char* pExtString = nullptr;
+ switch( nIndex )
+ {
+ case 57:
+ pExtString = "Pv";
+ break;
+
+ case 58:
+ pExtString = "Fv";
+ break;
+
+ case 98:
+ pExtString = "Index2D";
+ break;
+
+ case 111:
+ pExtString = "S";
+ break;
+
+ case 112:
+ pExtString = "N";
+ break;
+
+ case 114:
+ pExtString = "CALL";
+ break;
+
+ case 117:
+ pExtString = "TERM";
+ break;
+
+ case 118:
+ pExtString = "CTERM";
+ break;
+
+ case 134:
+ pExtString = "MEMAVAIL";
+ break;
+
+ case 135:
+ pExtString = "MEMEMSAVAIL";
+ break;
+
+ case 136:
+ pExtString = "FILEEXISTS";
+ break;
+
+ case 137:
+ pExtString = "CURVALUE";
+ break;
+
+ case 140:
+ pExtString = "HEX";
+ break;
+
+ case 141:
+ pExtString = "NUM";
+ break;
+
+ case 145:
+ pExtString = "VERSION";
+ break;
+
+ case 157:
+ pExtString = "INDEX3D";
+ break;
+
+ case 158:
+ pExtString = "CELLINDEX3D";
+ break;
+
+ case 159:
+ pExtString = "PROPERTY";
+ break;
+
+ case 160:
+ pExtString = "DDE";
+ break;
+
+ case 161:
+ pExtString = "COMMAND";
+ break;
+
+ default:
+ pExtString = nullptr;
+ break;
+ }
+ return pExtString;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/qpro/qprostyle.cxx b/sc/source/filter/qpro/qprostyle.cxx
new file mode 100644
index 0000000000..a23187c477
--- /dev/null
+++ b/sc/source/filter/qpro/qprostyle.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <qprostyle.hxx>
+
+#include <scitems.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <attrib.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <document.hxx>
+
+void ScQProStyle::SetFormat( ScDocument *pDoc, sal_uInt8 nCol, sal_uInt16 nRow, SCTAB nTab, sal_uInt16 nStyle )
+{
+ if (nStyle >= maxsize)
+ return;
+
+ ScPatternAttr aPattern(pDoc->GetPool());
+ SfxItemSet& rItemSet = aPattern.GetItemSet();
+
+ sal_uInt8 nTmp = maAlign[ nStyle ];
+ sal_uInt8 nHor = ( nTmp & 0x07 );
+ sal_uInt8 nVer = ( nTmp & 0x18 );
+ sal_uInt8 nOrient = ( nTmp & 0x60 );
+
+ // Horizontal Alignment
+ SvxCellHorJustify eJustify = SvxCellHorJustify::Standard;
+ switch( nHor )
+ {
+ case 0x00:
+ eJustify = SvxCellHorJustify::Standard;
+ break;
+
+ case 0x01:
+ eJustify = SvxCellHorJustify::Left;
+ break;
+
+ case 0x02:
+ eJustify = SvxCellHorJustify::Center;
+ break;
+
+ case 0x03:
+ eJustify = SvxCellHorJustify::Right;
+ break;
+
+ case 0x04:
+ eJustify = SvxCellHorJustify::Block;
+ break;
+ }
+ rItemSet.Put( SvxHorJustifyItem( eJustify, ATTR_HOR_JUSTIFY ) );
+
+ // Vertical Alignment
+ SvxCellVerJustify eVerJustify = SvxCellVerJustify::Standard;
+ switch( nVer )
+ {
+ case 0x00:
+ eVerJustify = SvxCellVerJustify::Bottom;
+ break;
+
+ case 0x08:
+ eVerJustify = SvxCellVerJustify::Center;
+ break;
+
+ case 0x10:
+ eVerJustify = SvxCellVerJustify::Top;
+ break;
+ }
+
+ rItemSet.Put(SvxVerJustifyItem( eVerJustify, ATTR_VER_JUSTIFY ) );
+
+ // Orientation
+ SvxCellOrientation eOrient = SvxCellOrientation::Standard;
+ switch( nOrient )
+ {
+ case 0x20:
+ eOrient = SvxCellOrientation::TopBottom;
+ break;
+
+ }
+ rItemSet.Put( SvxOrientationItem( eOrient, TypedWhichId<SvxOrientationItem>(0)) );
+
+ // Wrap cell contents
+ if( nTmp & 0x80 )
+ {
+ ScLineBreakCell aWrapItem(true);
+ rItemSet.Put( aWrapItem );
+ }
+
+ // Font Attributes
+ sal_uInt16 nTmpFnt = maFontRecord[ maFont[ nStyle ] ];
+ bool bIsBold, bIsItalic, bIsUnderLine;
+
+ bIsBold = ( nTmpFnt & 0x0001 ) != 0;
+ bIsItalic = ( nTmpFnt & 0x0002 ) != 0;
+ bIsUnderLine = ( nTmpFnt & 0x0004 ) != 0;
+ //(nTmpFnt & 0x0020 ) for StrikeThrough
+
+ if( bIsBold )
+ rItemSet.Put( SvxWeightItem( WEIGHT_BOLD,ATTR_FONT_WEIGHT) );
+ if( bIsItalic )
+ rItemSet.Put( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ if( bIsUnderLine )
+ rItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
+
+ if (maFontHeight[ maFont [ nStyle ] ])
+ rItemSet.Put( SvxFontHeightItem( static_cast<sal_uLong>(20 * maFontHeight[ maFont[ nStyle ] ] ), 100, ATTR_FONT_HEIGHT ) );
+
+ OUString fntName = maFontType[ maFont[ nStyle ] ];
+ rItemSet.Put( SvxFontItem( FAMILY_SYSTEM, fntName, OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
+
+ pDoc->ApplyPattern( nCol, nRow, nTab, aPattern );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/eeimpars.cxx b/sc/source/filter/rtf/eeimpars.cxx
new file mode 100644
index 0000000000..7056230812
--- /dev/null
+++ b/sc/source/filter/rtf/eeimpars.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 <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/sfxhtml.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <officecfg/Office/Common.hxx>
+
+#include <eeimport.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <editutil.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <eeparser.hxx>
+#include <drwlayer.hxx>
+#include <rangenam.hxx>
+#include <progress.hxx>
+#include <stringutil.hxx>
+#include <rowheightcontext.hxx>
+#include <fuinsert.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <memory>
+
+ScEEImport::ScEEImport( ScDocument* pDocP, const ScRange& rRange ) :
+ maRange( rRange ),
+ mpDoc( pDocP )
+{
+ const ScPatternAttr* pPattern = mpDoc->GetPattern(
+ maRange.aStart.Col(), maRange.aStart.Row(), maRange.aStart.Tab() );
+ mpEngine.reset( new ScTabEditEngine(*pPattern, mpDoc->GetEditPool(), mpDoc, mpDoc->GetEditPool()) );
+ mpEngine->SetUpdateLayout( false );
+ mpEngine->EnableUndo( false );
+}
+
+ScEEImport::~ScEEImport()
+{
+ // Sequence important, or else we crash in some dtor!
+ // Is guaranteed as ScEEImport is base class
+}
+
+ErrCode ScEEImport::Read( SvStream& rStream, const OUString& rBaseURL )
+{
+ ErrCode nErr = mpParser->Read( rStream, rBaseURL );
+
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ mpParser->GetDimensions( nEndCol, nEndRow );
+ if ( nEndCol != 0 )
+ {
+ nEndCol += maRange.aStart.Col() - 1;
+ if ( nEndCol > mpDoc->MaxCol() )
+ nEndCol = mpDoc->MaxCol();
+ }
+ else
+ nEndCol = maRange.aStart.Col();
+ if ( nEndRow != 0 )
+ {
+ nEndRow += maRange.aStart.Row() - 1;
+ if ( nEndRow > mpDoc->MaxRow() )
+ nEndRow = mpDoc->MaxRow();
+ }
+ else
+ nEndRow = maRange.aStart.Row();
+ maRange.aEnd.Set( nEndCol, nEndRow, maRange.aStart.Tab() );
+
+ return nErr;
+}
+
+namespace
+{
+ bool IsValidSel(const ScTabEditEngine& rEngine, const ESelection& rSel)
+ {
+ const auto nParaCount = rEngine.GetParagraphCount();
+ return rSel.nStartPara < nParaCount && rSel.nEndPara < nParaCount;
+ }
+}
+
+void ScEEImport::WriteToDocument( bool bSizeColsRows, double nOutputFactor, SvNumberFormatter* pFormatter, bool bConvertDate,
+ bool bConvertScientific )
+{
+ std::unique_ptr<ScProgress> pProgress( new ScProgress( mpDoc->GetDocumentShell(),
+ ScResId( STR_LOAD_DOC ), mpParser->ListSize(), true ) );
+ sal_uLong nProgress = 0;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nTab;
+ SCROW nOverlapRowMax, nLastMergedRow;
+ SCCOL nMergeColAdd;
+ nStartCol = maRange.aStart.Col();
+ nStartRow = maRange.aStart.Row();
+ nTab = maRange.aStart.Tab();
+ nEndCol = maRange.aEnd.Col();
+ nEndRow = maRange.aEnd.Row();
+ nOverlapRowMax = 0;
+ nMergeColAdd = 0;
+ nLastMergedRow = SCROW_MAX;
+ bool bHasGraphics = false;
+ ScEEParseEntry* pE;
+ if (!pFormatter)
+ pFormatter = mpDoc->GetFormatTable();
+ bool bNumbersEnglishUS = false;
+ if (pFormatter->GetLanguage() == LANGUAGE_SYSTEM && !utl::ConfigManager::IsFuzzing())
+ {
+ // Automatic language option selected. Check for the global 'use US English' option.
+ bNumbersEnglishUS = officecfg::Office::Common::Filter::HTML::Import::NumbersEnglishUS::get();
+ }
+ ScDocumentPool* pDocPool = mpDoc->GetPool();
+ ScRangeName* pRangeNames = mpDoc->GetRangeName();
+ for ( size_t i = 0, n = mpParser->ListSize(); i < n; ++i )
+ {
+ pE = mpParser->ListEntry( i );
+ SCROW nRow = nStartRow + pE->nRow;
+ if ( nRow != nLastMergedRow )
+ nMergeColAdd = 0;
+ SCCOL nCol = nStartCol + pE->nCol + nMergeColAdd;
+ // Determine RowMerge
+ // Pure ColMerge and ColMerge of the first MergeRow already done during parsing
+ if (nRow <= nOverlapRowMax && mpDoc->ValidCol(nCol))
+ {
+ while ( nCol <= mpDoc->MaxCol() && mpDoc->HasAttrib( nCol, nRow, nTab,
+ nCol, nRow, nTab, HasAttrFlags::Overlapped ) )
+ {
+ nCol++;
+ nMergeColAdd++;
+ }
+ nLastMergedRow = nRow;
+ }
+ // Add for second run
+ pE->nCol = nCol;
+ pE->nRow = nRow;
+ if ( mpDoc->ValidCol(nCol) && mpDoc->ValidRow(nRow) )
+ {
+ SfxItemSet aSet = mpEngine->GetAttribs( pE->aSel );
+ // Remove default: we set left/right ourselves depending on Text or
+ // Number
+ // EditView.GetAttribs always returns complete Set filled with
+ // defaults
+ const SfxPoolItem& rItem = aSet.Get( EE_PARA_JUST );
+ if ( static_cast<const SvxAdjustItem&>(rItem).GetAdjust() == SvxAdjust::Left )
+ aSet.ClearItem( EE_PARA_JUST );
+
+ // Test whether simple String without mixed attributes
+ bool bSimple = ( pE->aSel.nStartPara == pE->aSel.nEndPara );
+ for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && bSimple; nId++)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ SfxItemState eState = aSet.GetItemState( nId, true, &pItem );
+ if (eState == SfxItemState::DONTCARE)
+ bSimple = false;
+ else if (eState == SfxItemState::SET)
+ {
+ if ( nId == EE_CHAR_ESCAPEMENT ) // Super-/Subscript always via EE
+ {
+ if ( static_cast<SvxEscapement>(static_cast<const SvxEscapementItem*>(pItem)->GetEnumValue())
+ != SvxEscapement::Off )
+ bSimple = false;
+ }
+ }
+ }
+ if ( bSimple )
+ { // Contains field commands?
+ SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ bSimple = false;
+ }
+
+ // HTML
+ OUString aValStr, aNumStr;
+ double fVal = 0.0;
+ sal_uInt32 nNumForm = 0;
+ LanguageType eNumLang = LANGUAGE_NONE;
+ if ( pE->pNumStr )
+ { // SDNUM needs to be if SDVAL
+ aNumStr = *pE->pNumStr;
+ if ( pE->pValStr )
+ aValStr = *pE->pValStr;
+ fVal = SfxHTMLParser::GetTableDataOptionsValNum(
+ nNumForm, eNumLang, aValStr, aNumStr, *pFormatter );
+ }
+
+ // Set attributes
+ auto pAttr = std::make_unique<ScPatternAttr>( pDocPool );
+ pAttr->GetFromEditItemSet( &aSet );
+ SfxItemSet* pAttrItemSet = &pAttr->GetItemSet();
+ if (!aNumStr.isEmpty())
+ {
+ pAttrItemSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumForm ) );
+ pAttrItemSet->Put( SvxLanguageItem( eNumLang, ATTR_LANGUAGE_FORMAT ) );
+ }
+ const SfxItemSet& rESet = pE->aItemSet;
+ if ( rESet.Count() )
+ {
+ const SfxPoolItem* pItem;
+ if ( rESet.GetItemState( ATTR_BACKGROUND, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_BORDER, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_SHADOW, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ // HTML
+ if ( rESet.GetItemState( ATTR_HOR_JUSTIFY, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_VER_JUSTIFY, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_LINEBREAK, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_FONT_COLOR, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_FONT_UNDERLINE, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ // HTML LATIN/CJK/CTL script type dependent
+ const SfxPoolItem* pFont;
+ if ( rESet.GetItemState( ATTR_FONT, false, &pFont) != SfxItemState::SET )
+ pFont = nullptr;
+ const SfxPoolItem* pHeight;
+ if ( rESet.GetItemState( ATTR_FONT_HEIGHT, false, &pHeight) != SfxItemState::SET )
+ pHeight = nullptr;
+ const SfxPoolItem* pWeight;
+ if ( rESet.GetItemState( ATTR_FONT_WEIGHT, false, &pWeight) != SfxItemState::SET )
+ pWeight = nullptr;
+ const SfxPoolItem* pPosture;
+ if ( rESet.GetItemState( ATTR_FONT_POSTURE, false, &pPosture) != SfxItemState::SET )
+ pPosture = nullptr;
+ // Number format
+ const SfxPoolItem* pNumFmt = nullptr;
+ if ( rESet.GetItemState(ATTR_VALUE_FORMAT, false, &pNumFmt) == SfxItemState::SET )
+ pAttrItemSet->Put(*pNumFmt);
+ if ( pFont || pHeight || pWeight || pPosture )
+ {
+ OUString aStr( mpEngine->GetText( pE->aSel ) );
+ SvtScriptType nScriptType = mpDoc->GetStringScriptType( aStr );
+ const SvtScriptType nScripts[3] = { SvtScriptType::LATIN,
+ SvtScriptType::ASIAN, SvtScriptType::COMPLEX };
+ for (SvtScriptType nScript : nScripts)
+ {
+ if ( nScriptType & nScript )
+ {
+ if ( pFont )
+ {
+ pAttrItemSet->Put( pFont->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT )) );
+ }
+ if ( pHeight )
+ {
+ pAttrItemSet->Put( pHeight->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT_HEIGHT )) );
+ }
+ if ( pWeight )
+ {
+ pAttrItemSet->Put( pWeight->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT_WEIGHT )) );
+ }
+ if ( pPosture )
+ {
+ pAttrItemSet->Put( pPosture->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT_POSTURE )) );
+ }
+ }
+ }
+ }
+ }
+ if ( pE->nColOverlap > 1 || pE->nRowOverlap > 1 )
+ { // Merged cells, with SfxItemSet.Put() is faster than
+ // with ScDocument.DoMerge() afterwards
+ ScMergeAttr aMerge( pE->nColOverlap, pE->nRowOverlap );
+ pAttrItemSet->Put( aMerge );
+ SCROW nRO = 0;
+ if ( pE->nColOverlap > 1 )
+ mpDoc->ApplyFlagsTab( nCol+1, nRow,
+ nCol + pE->nColOverlap - 1, nRow, nTab,
+ ScMF::Hor );
+ if ( pE->nRowOverlap > 1 )
+ {
+ nRO = nRow + pE->nRowOverlap - 1;
+ mpDoc->ApplyFlagsTab( nCol, nRow+1,
+ nCol, nRO , nTab,
+ ScMF::Ver );
+ if ( nRO > nOverlapRowMax )
+ nOverlapRowMax = nRO;
+ }
+ if ( pE->nColOverlap > 1 && pE->nRowOverlap > 1 )
+ mpDoc->ApplyFlagsTab( nCol+1, nRow+1,
+ nCol + pE->nColOverlap - 1, nRO, nTab,
+ ScMF::Hor | ScMF::Ver );
+ }
+ const ScStyleSheet* pStyleSheet =
+ mpDoc->GetPattern( nCol, nRow, nTab )->GetStyleSheet();
+ pAttr->SetStyleSheet( const_cast<ScStyleSheet*>(pStyleSheet) );
+ auto rAttrItemSet2 = mpDoc->SetPattern( nCol, nRow, nTab, std::move(pAttr) )->GetItemSet();
+
+ // Add data
+ if (bSimple)
+ {
+ ScSetStringParam aParam;
+ aParam.mpNumFormatter = pFormatter;
+ aParam.mbDetectNumberFormat = true;
+ aParam.meSetTextNumFormat = ScSetStringParam::SpecialNumberOnly;
+ aParam.mbHandleApostrophe = false;
+ aParam.mbCheckLinkFormula = true;
+
+ if (!aValStr.isEmpty())
+ mpDoc->SetValue( nCol, nRow, nTab, fVal );
+ else if ( !pE->aSel.HasRange() )
+ {
+ // maybe ALT text of IMG or similar
+ mpDoc->SetString( nCol, nRow, nTab, pE->aAltText, &aParam );
+ // If SelRange is completely empty, the succeeding text can be in the same paragraph!
+ }
+ else
+ {
+ OUString aStr;
+ if( pE->bEntirePara )
+ {
+ aStr = mpEngine->GetText( pE->aSel.nStartPara );
+ }
+ else
+ {
+ aStr = comphelper::string::strip(mpEngine->GetText(pE->aSel), ' ');
+ }
+
+ bool bTextFormat = false;
+
+ if (const SfxUInt32Item* pNumFmt = rAttrItemSet2.GetItemIfSet(ATTR_VALUE_FORMAT, false))
+ {
+ sal_uInt32 nNumFmt = pNumFmt->GetValue();
+ SvNumFormatType nType = pFormatter->GetType(nNumFmt);
+ if (nType == SvNumFormatType::TEXT)
+ // Format is set to Text.
+ bTextFormat = true;
+ }
+
+ // TODO: RTF import should follow the language tag,
+ // currently this follows the HTML options for both, HTML
+ // and RTF.
+ if (bNumbersEnglishUS)
+ {
+ pFormatter->ChangeIntl( LANGUAGE_ENGLISH_US);
+ sal_uInt32 nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
+ double fEnVal = 0.0;
+ if (pFormatter->IsNumberFormat( aStr, nIndex, fEnVal))
+ {
+ sal_uInt32 nNewIndex =
+ pFormatter->GetFormatForLanguageIfBuiltIn(
+ nIndex, LANGUAGE_SYSTEM);
+ OSL_ENSURE( nNewIndex != nIndex, "ScEEImport::WriteToDocument: NumbersEnglishUS not a built-in format?");
+ pFormatter->GetInputLineString( fEnVal, nNewIndex, aStr);
+ }
+ else
+ bTextFormat = true;
+ pFormatter->ChangeIntl( LANGUAGE_SYSTEM);
+ }
+
+ // #105460#, #i4180# String cells can't contain tabs or linebreaks
+ // -> replace with spaces
+ aStr = aStr.replaceAll( "\t", " " );
+ aStr = aStr.replaceAll( "\n", " " );
+
+ if (bTextFormat)
+ {
+ aParam.mbDetectNumberFormat = false;
+ aParam.mbDetectScientificNumberFormat = bConvertScientific;
+ aParam.meSetTextNumFormat = ScSetStringParam::Always;
+ }
+ else
+ {
+ aParam.mbDetectNumberFormat = bConvertDate;
+ aParam.mbDetectScientificNumberFormat = bConvertScientific;
+ }
+
+ mpDoc->SetString(nCol, nRow, nTab, aStr, &aParam);
+ }
+ }
+ else if (std::unique_ptr<EditTextObject> pTextObject = IsValidSel(*mpEngine, pE->aSel) ? mpEngine->CreateTextObject(pE->aSel) : nullptr)
+ {
+ // The cell will own the text object instance.
+ mpDoc->SetEditText(ScAddress(nCol,nRow,nTab), std::move(pTextObject));
+ }
+ if ( !pE->maImageList.empty() )
+ bHasGraphics |= GraphicSize( nCol, nRow, pE );
+ if ( pE->pName )
+ { // Anchor Name => RangeName
+ if (!pRangeNames->findByUpperName(ScGlobal::getCharClass().uppercase(*pE->pName)))
+ {
+ ScRangeData* pData = new ScRangeData( *mpDoc, *pE->pName,
+ ScAddress( nCol, nRow, nTab ) );
+ pRangeNames->insert( pData );
+ }
+ }
+ }
+ pProgress->SetStateOnPercent( ++nProgress );
+ }
+ if ( bSizeColsRows )
+ {
+ // Column widths
+ ColWidthsMap& rColWidths = mpParser->GetColWidths();
+ if ( !rColWidths.empty() )
+ {
+ nProgress = 0;
+ pProgress->SetState( nProgress, nEndCol - nStartCol + 1 );
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++ )
+ {
+ sal_uInt16 nWidth = 0;
+ ColWidthsMap::const_iterator it = rColWidths.find( nCol );
+ if ( it != rColWidths.end() )
+ nWidth = it->second;
+ if ( nWidth )
+ mpDoc->SetColWidth( nCol, nTab, nWidth );
+ pProgress->SetState( ++nProgress );
+ }
+ }
+ pProgress.reset(); // SetOptimalHeight has its own ProgressBar
+ // Adjust line height, base is 100% zoom
+ Fraction aZoom( 1, 1 );
+ // Factor is printer to display ratio
+ double nPPTX = ScGlobal::nScreenPPTX * static_cast<double>(aZoom) / nOutputFactor;
+ double nPPTY = ScGlobal::nScreenPPTY * static_cast<double>(aZoom);
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ sc::RowHeightContext aCxt(mpDoc->MaxRow(), nPPTX, nPPTY, aZoom, aZoom, pVirtDev);
+ aCxt.setExtraHeight(ScGlobal::nLastRowHeightExtra);
+ mpDoc->SetOptimalHeight(aCxt, 0, nEndRow, 0, true);
+
+ if ( !maRowHeights.empty() )
+ {
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; nRow++ )
+ {
+ RowHeightMap::const_iterator it = maRowHeights.find( nRow );
+ sal_uInt16 nHeight = it == maRowHeights.end() ? 0 : it->second;
+ if ( nHeight > mpDoc->GetRowHeight( nRow, nTab ) )
+ mpDoc->SetRowHeight( nRow, nTab, nHeight );
+ }
+ }
+ }
+ if ( !bHasGraphics )
+ return;
+
+ // Insert graphics
+ for ( size_t i = 0, nListSize = mpParser->ListSize(); i < nListSize; ++i )
+ {
+ pE = mpParser->ListEntry( i );
+ if ( !pE->maImageList.empty() )
+ {
+ SCCOL nCol = pE->nCol;
+ SCROW nRow = pE->nRow;
+ if ( mpDoc->ValidCol(nCol) && mpDoc->ValidRow(nRow) )
+ InsertGraphic( nCol, nRow, nTab, pE );
+ }
+ }
+}
+
+bool ScEEImport::GraphicSize( SCCOL nCol, SCROW nRow, ScEEParseEntry* pE )
+{
+ if ( pE->maImageList.empty() )
+ return false;
+ bool bHasGraphics = false;
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ tools::Long nWidth, nHeight;
+ nWidth = nHeight = 0;
+ char nDir = nHorizontal;
+ for (const std::unique_ptr<ScHTMLImage> & pImage : pE->maImageList)
+ {
+ ScHTMLImage* pI = pImage.get();
+ if ( pI->oGraphic )
+ bHasGraphics = true;
+ Size aSizePix = pI->aSize;
+ aSizePix.AdjustWidth(2 * pI->aSpace.X() );
+ aSizePix.AdjustHeight(2 * pI->aSpace.Y() );
+ Size aLogicSize = pDefaultDev->PixelToLogic( aSizePix, MapMode( MapUnit::MapTwip ) );
+ if ( nDir & nHorizontal )
+ nWidth += aLogicSize.Width();
+ else if ( nWidth < aLogicSize.Width() )
+ nWidth = aLogicSize.Width();
+ if ( nDir & nVertical )
+ nHeight += aLogicSize.Height();
+ else if ( nHeight < aLogicSize.Height() )
+ nHeight = aLogicSize.Height();
+ nDir = pI->nDir;
+ }
+ // Column widths
+ ColWidthsMap& rColWidths = mpParser->GetColWidths();
+ tools::Long nThisWidth = 0;
+ ColWidthsMap::const_iterator it = rColWidths.find( nCol );
+ if ( it != rColWidths.end() )
+ nThisWidth = it->second;
+ tools::Long nColWidths = nThisWidth;
+ SCCOL nColSpanCol = nCol + pE->nColOverlap;
+ for ( SCCOL nC = nCol + 1; nC < nColSpanCol; nC++ )
+ {
+ ColWidthsMap::const_iterator it2 = rColWidths.find( nC );
+ if ( it2 != rColWidths.end() )
+ nColWidths += it2->second;
+ }
+ if ( nWidth > nColWidths )
+ { // Only insert difference in first column
+ rColWidths[ nCol ] = nWidth - nColWidths + nThisWidth;
+ }
+ // Distribute line height difference between all affected lines
+ SCROW nRowSpan = pE->nRowOverlap;
+
+ assert(nRowSpan != 0);
+ if ( nRowSpan == 0 )
+ return bHasGraphics;
+
+ nHeight /= nRowSpan;
+
+ if ( nHeight == 0 )
+ nHeight = 1; // For definite comparison
+ for ( SCROW nR = nRow; nR < nRow + nRowSpan; nR++ )
+ {
+ RowHeightMap::const_iterator it2 = maRowHeights.find( nR );
+ tools::Long nRowHeight = it2 == maRowHeights.end() ? 0 : it2->second;
+ if ( nHeight > nRowHeight )
+ {
+ maRowHeights[ nR ] = nHeight;
+ }
+ }
+ return bHasGraphics;
+}
+
+void ScEEImport::InsertGraphic( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ ScEEParseEntry* pE )
+{
+ if ( pE->maImageList.empty() )
+ return ;
+ ScDrawLayer* pModel = mpDoc->GetDrawLayer();
+ if (!pModel)
+ {
+ mpDoc->InitDrawLayer();
+ pModel = mpDoc->GetDrawLayer();
+ }
+ SdrPage* pPage = pModel->GetPage( static_cast<sal_uInt16>(nTab) );
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+
+ Point aCellInsertPos(
+ o3tl::convert(mpDoc->GetColOffset(nCol, nTab), o3tl::Length::twip, o3tl::Length::mm100),
+ o3tl::convert(mpDoc->GetRowOffset(nRow, nTab), o3tl::Length::twip, o3tl::Length::mm100));
+
+ Point aInsertPos( aCellInsertPos );
+ Point aSpace;
+ Size aLogicSize;
+ char nDir = nHorizontal;
+ for (const std::unique_ptr<ScHTMLImage> & pImage : pE->maImageList)
+ {
+ ScHTMLImage* pI = pImage.get();
+ if ( nDir & nHorizontal )
+ { // Horizontal
+ aInsertPos.AdjustX(aLogicSize.Width() );
+ aInsertPos.AdjustX(aSpace.X() );
+ aInsertPos.setY( aCellInsertPos.Y() );
+ }
+ else
+ { // Vertical
+ aInsertPos.setX( aCellInsertPos.X() );
+ aInsertPos.AdjustY(aLogicSize.Height() );
+ aInsertPos.AdjustY(aSpace.Y() );
+ }
+ // Add offset of Spacing
+ aSpace = pDefaultDev->PixelToLogic( pI->aSpace, MapMode( MapUnit::Map100thMM ) );
+ aInsertPos += aSpace;
+
+ Size aSizePix = pI->aSize;
+ aLogicSize = pDefaultDev->PixelToLogic( aSizePix, MapMode( MapUnit::Map100thMM ) );
+
+ // Limit size
+ ::ScLimitSizeOnDrawPage( aLogicSize, aInsertPos, pPage->GetSize() );
+
+ if ( pI->oGraphic )
+ {
+ tools::Rectangle aRect ( aInsertPos, aLogicSize );
+ rtl::Reference<SdrGrafObj> pObj = new SdrGrafObj(
+ *pModel,
+ *pI->oGraphic,
+ aRect);
+
+ // calling SetGraphicLink here doesn't work
+ pObj->SetName( pI->aURL );
+
+ pPage->InsertObject( pObj.get() );
+
+ // SetGraphicLink has to be used after inserting the object,
+ // otherwise an empty graphic is swapped in and the contact stuff crashes.
+ // See #i37444#.
+ pObj->SetGraphicLink( pI->aURL );
+
+ pObj->SetLogicRect( aRect ); // Only after InsertObject!
+ }
+ nDir = pI->nDir;
+ }
+}
+
+ScEEParser::ScEEParser( EditEngine* pEditP ) :
+ pEdit( pEditP ),
+ pPool( EditEngine::CreatePool() ),
+ pDocPool( new ScDocumentPool ),
+ nRtfLastToken(0),
+ nColCnt(0),
+ nRowCnt(0),
+ nColMax(0),
+ nRowMax(0)
+{
+ // pPool is foisted on SvxRTFParser at RtfImportState::Start later on
+ pPool->SetSecondaryPool( pDocPool.get() );
+ pPool->FreezeIdRanges();
+ NewActEntry( nullptr );
+}
+
+ScEEParser::~ScEEParser()
+{
+ mxActEntry.reset();
+ maList.clear();
+
+ // Don't delete Pool until the lists have been deleted
+ pPool->SetSecondaryPool( nullptr );
+ pDocPool.clear();
+ pPool.clear();
+}
+
+void ScEEParser::NewActEntry( const ScEEParseEntry* pE )
+{ // New free-flying mxActEntry
+ mxActEntry = std::make_shared<ScEEParseEntry>(pPool.get());
+ mxActEntry->aSel.nStartPara = (pE ? pE->aSel.nEndPara + 1 : 0);
+ mxActEntry->aSel.nStartPos = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/expbase.cxx b/sc/source/filter/rtf/expbase.cxx
new file mode 100644
index 0000000000..d5461f1d69
--- /dev/null
+++ b/sc/source/filter/rtf/expbase.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 <expbase.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+
+ScExportBase::ScExportBase( SvStream& rStrmP, ScDocument* pDocP,
+ const ScRange& rRangeP )
+ :
+ rStrm( rStrmP ),
+ aRange( rRangeP ),
+ pDoc( pDocP ),
+ pFormatter( pDocP->GetFormatTable() )
+{
+}
+
+ScExportBase::~ScExportBase()
+{
+}
+
+bool ScExportBase::GetDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const
+{
+ pDoc->GetDataStart( nTab, nStartCol, nStartRow );
+ pDoc->GetPrintArea( nTab, nEndCol, nEndRow );
+ return TrimDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+bool ScExportBase::TrimDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const
+{
+ while ( nStartCol <= nEndCol && pDoc->ColHidden(nStartCol, nTab))
+ ++nStartCol;
+ while ( nStartCol <= nEndCol && pDoc->ColHidden(nEndCol, nTab))
+ --nEndCol;
+ nStartRow = pDoc->FirstVisibleRow(nStartRow, nEndRow, nTab);
+ nEndRow = pDoc->LastVisibleRow(nStartRow, nEndRow, nTab);
+ return nStartCol <= nEndCol && nStartRow <= nEndRow && nEndRow !=
+ ::std::numeric_limits<SCROW>::max();
+}
+
+bool ScExportBase::IsEmptyTable( SCTAB nTab ) const
+{
+ if ( !pDoc->HasTable( nTab ) || !pDoc->IsVisible( nTab ) )
+ return true;
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ return !GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+ScFieldEditEngine& ScExportBase::GetEditEngine() const
+{
+ if ( !pEditEngine )
+ const_cast<ScExportBase*>(this)->pEditEngine.reset( new ScFieldEditEngine(pDoc, pDoc->GetEditPool()) );
+ return *pEditEngine;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/rtfexp.cxx b/sc/source/filter/rtf/rtfexp.cxx
new file mode 100644
index 0000000000..7349aa6985
--- /dev/null
+++ b/sc/source/filter/rtf/rtfexp.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 <scitems.hxx>
+
+#include <rtl/tencinfo.h>
+#include <osl/thread.h>
+
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svtools/rtfout.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <tools/stream.hxx>
+
+#include <rtfexp.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <cellform.hxx>
+#include <editutil.hxx>
+#include <ftools.hxx>
+
+void ScFormatFilterPluginImpl::ScExportRTF( SvStream& rStrm, ScDocument* pDoc,
+ const ScRange& rRange, const rtl_TextEncoding /*eNach*/ )
+{
+ ScRTFExport aEx( rStrm, pDoc, rRange );
+ aEx.Write();
+}
+
+ScRTFExport::ScRTFExport( SvStream& rStrmP, ScDocument* pDocP, const ScRange& rRangeP )
+ :
+ ScExportBase( rStrmP, pDocP, rRangeP ),
+ m_pCellX( new sal_uLong[ pDoc->MaxCol()+2 ] )
+{
+}
+
+ScRTFExport::~ScRTFExport()
+{
+}
+
+void ScRTFExport::Write()
+{
+ rStrm.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_RTF );
+ rStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_ANSI ).WriteOString( SAL_NEWLINE_STRING );
+
+ m_aFontStrm.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_FONTTBL );
+
+ // Data
+ for ( SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); nTab++ )
+ {
+ if ( nTab > aRange.aStart.Tab() )
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_PAR );
+ WriteTab( nTab );
+ }
+
+ m_aFontStrm.WriteChar( '}' );
+ m_aFontStrm.Seek(0);
+ rStrm.WriteStream(m_aFontStrm);
+ m_aDocStrm.Seek(0);
+ rStrm.WriteStream(m_aDocStrm);
+ rStrm.WriteChar( '}' ).WriteOString( SAL_NEWLINE_STRING );
+}
+
+void ScRTFExport::WriteTab( SCTAB nTab )
+{
+ m_aDocStrm.WriteChar( '{' ).WriteOString( SAL_NEWLINE_STRING );
+ if ( pDoc->HasTable( nTab ) )
+ {
+ memset( &m_pCellX[0], 0, (pDoc->MaxCol()+2) * sizeof(sal_uLong) );
+ SCCOL nCol;
+ SCCOL nEndCol = aRange.aEnd.Col();
+ for ( nCol = aRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ m_pCellX[nCol+1] = m_pCellX[nCol] + pDoc->GetColWidth( nCol, nTab );
+ }
+
+ SCROW nEndRow = aRange.aEnd.Row();
+ for ( SCROW nRow = aRange.aStart.Row(); nRow <= nEndRow; nRow++ )
+ {
+ WriteRow( nTab, nRow );
+ }
+ }
+ m_aDocStrm.WriteChar( '}' ).WriteOString( SAL_NEWLINE_STRING );
+}
+
+void ScRTFExport::WriteRow( SCTAB nTab, SCROW nRow )
+{
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_TROWD ).WriteOString( OOO_STRING_SVTOOLS_RTF_TRGAPH ).WriteOString( "30" ).WriteOString( OOO_STRING_SVTOOLS_RTF_TRLEFT ).WriteOString( "-30" );
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_TRRH ).WriteOString( OString::number(pDoc->GetRowHeight(nRow, nTab)) );
+ SCCOL nCol;
+ SCCOL nEndCol = aRange.aEnd.Col();
+ for ( nCol = aRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
+ const ScMergeAttr& rMergeAttr = pAttr->GetItem( ATTR_MERGE );
+ const SvxVerJustifyItem& rVerJustifyItem= pAttr->GetItem( ATTR_VER_JUSTIFY );
+
+ const char* pChar;
+
+ if ( rMergeAttr.GetColMerge() != 0 )
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_CLMGF );
+ else
+ {
+ const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG );
+ if ( rMergeFlagAttr.IsHorOverlapped() )
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_CLMRG );
+ }
+
+ switch( rVerJustifyItem.GetValue() )
+ {
+ case SvxCellVerJustify::Top: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALT; break;
+ case SvxCellVerJustify::Center: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALC; break;
+ case SvxCellVerJustify::Bottom: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALB; break;
+ case SvxCellVerJustify::Standard: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALB; break; //! Bottom
+ default: pChar = nullptr; break;
+ }
+ if ( pChar )
+ m_aDocStrm.WriteOString( pChar );
+
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_CELLX ).WriteOString( OString::number(m_pCellX[nCol+1]) );
+ if ( (nCol & 0x0F) == 0x0F )
+ m_aDocStrm.WriteOString( SAL_NEWLINE_STRING ); // Do not let lines get too long
+ }
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_PARD ).WriteOString( OOO_STRING_SVTOOLS_RTF_PLAIN ).WriteOString( OOO_STRING_SVTOOLS_RTF_INTBL ).WriteOString( SAL_NEWLINE_STRING );
+
+ sal_uInt64 nStrmPos = m_aDocStrm.Tell();
+ for ( nCol = aRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ WriteCell( nTab, nRow, nCol );
+ if ( m_aDocStrm.Tell() - nStrmPos > 255 )
+ { // Do not let lines get too long
+ m_aDocStrm.WriteOString( SAL_NEWLINE_STRING );
+ nStrmPos = m_aDocStrm.Tell();
+ }
+ }
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_ROW ).WriteOString( SAL_NEWLINE_STRING );
+}
+
+void ScRTFExport::WriteFontTable(const SvxFontItem& rFontItem, int nIndex)
+{
+ m_aFontStrm.WriteChar( '{' );
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_F );
+ m_aFontStrm.WriteOString( OString::number(nIndex) );
+
+ FontFamily eFamily = rFontItem.GetFamily();
+ if (eFamily == FAMILY_DONTKNOW)
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FNIL );
+ else if (eFamily == FAMILY_DECORATIVE)
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FDECOR );
+ else if (eFamily == FAMILY_MODERN)
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FMODERN );
+ else if (eFamily == FAMILY_ROMAN)
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FROMAN );
+ else if (eFamily == FAMILY_SCRIPT)
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FSCRIPT );
+ else if (eFamily == FAMILY_SWISS)
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FSWISS );
+
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FPRQ );
+
+ sal_uInt16 nVal = 0;
+ FontPitch ePitch = rFontItem.GetPitch();
+ if ( ePitch == PITCH_FIXED )
+ nVal = 1;
+ else if ( ePitch == PITCH_VARIABLE )
+ nVal = 2;
+ m_aFontStrm.WriteOString( OString::number(nVal) );
+
+ rtl_TextEncoding eDestEnc = RTL_TEXTENCODING_MS_1252;
+ rtl_TextEncoding eChrSet = rFontItem.GetCharSet();
+ if (IsOpenSymbol(rFontItem.GetFamilyName()))
+ eChrSet = RTL_TEXTENCODING_UTF8;
+ else if( RTL_TEXTENCODING_DONTKNOW == eChrSet )
+ eChrSet = osl_getThreadTextEncoding();
+
+ m_aFontStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_FCHARSET );
+ m_aFontStrm.WriteOString( OString::number(rtl_getBestWindowsCharsetFromTextEncoding( eChrSet )) );
+
+ m_aFontStrm.WriteChar( ' ' );
+ RTFOutFuncs::Out_String( m_aFontStrm, rFontItem.GetFamilyName(), eDestEnc );
+ m_aFontStrm.WriteOString( ";}" );
+}
+
+int ScRTFExport::AddFont(const SvxFontItem& rFontItem)
+{
+ auto nRet = m_pFontTable.size();
+ auto itFont(m_pFontTable.find(rFontItem.GetFamilyName()));
+ if (itFont == m_pFontTable.end())
+ {
+ m_pFontTable[rFontItem.GetFamilyName()] = nRet;
+ WriteFontTable(rFontItem, nRet);
+ }
+ else
+ {
+ nRet = itFont->second;
+ }
+
+ return nRet;
+}
+
+void ScRTFExport::WriteCell( SCTAB nTab, SCROW nRow, SCCOL nCol )
+{
+ const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
+
+ const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG );
+ if ( rMergeFlagAttr.IsHorOverlapped() )
+ {
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_CELL );
+ return ;
+ }
+
+ bool bValueData = false;
+ OUString aContent;
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(*pDoc, aPos);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_NONE:
+ bValueData = false;
+ break;
+ case CELLTYPE_EDIT:
+ {
+ bValueData = false;
+ const EditTextObject* pObj = aCell.getEditText();
+ EditEngine& rEngine = GetEditEngine();
+ rEngine.SetText(*pObj);
+ aContent = rEngine.GetText(); // LineFeed in between paragraphs!
+ }
+ break;
+ default:
+ {
+ bValueData = pDoc->HasValueData(aPos);
+ sal_uInt32 nFormat = pAttr->GetNumberFormat(pFormatter);
+ const Color* pColor;
+ aContent = ScCellFormat::GetString(*pDoc, aPos, nFormat, &pColor, *pFormatter);
+ }
+ }
+
+ bool bResetAttr(false);
+
+ const SvxFontItem& rFontItem = pAttr->GetItem( ATTR_FONT );
+ const SvxHorJustifyItem& rHorJustifyItem = pAttr->GetItem( ATTR_HOR_JUSTIFY );
+ const SvxWeightItem& rWeightItem = pAttr->GetItem( ATTR_FONT_WEIGHT );
+ const SvxPostureItem& rPostureItem = pAttr->GetItem( ATTR_FONT_POSTURE );
+ const SvxUnderlineItem& rUnderlineItem = pAttr->GetItem( ATTR_FONT_UNDERLINE );
+
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_F )
+ .WriteOString( OString::number(AddFont(rFontItem)) );
+
+ const char* pChar;
+
+ switch( rHorJustifyItem.GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ pChar = (bValueData ? OOO_STRING_SVTOOLS_RTF_QR : OOO_STRING_SVTOOLS_RTF_QL);
+ break;
+ case SvxCellHorJustify::Center: pChar = OOO_STRING_SVTOOLS_RTF_QC; break;
+ case SvxCellHorJustify::Block: pChar = OOO_STRING_SVTOOLS_RTF_QJ; break;
+ case SvxCellHorJustify::Right: pChar = OOO_STRING_SVTOOLS_RTF_QR; break;
+ case SvxCellHorJustify::Left:
+ case SvxCellHorJustify::Repeat:
+ default: pChar = OOO_STRING_SVTOOLS_RTF_QL; break;
+ }
+ m_aDocStrm.WriteOString( pChar );
+
+ if ( rWeightItem.GetWeight() >= WEIGHT_BOLD )
+ { // bold
+ bResetAttr = true;
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_B );
+ }
+ if ( rPostureItem.GetPosture() != ITALIC_NONE )
+ { // italic
+ bResetAttr = true;
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_I );
+ }
+ if ( rUnderlineItem.GetLineStyle() != LINESTYLE_NONE )
+ { // underline
+ bResetAttr = true;
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_UL );
+ }
+
+ m_aDocStrm.WriteChar( ' ' );
+ RTFOutFuncs::Out_String( m_aDocStrm, aContent );
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_CELL );
+
+ if ( bResetAttr )
+ m_aDocStrm.WriteOString( OOO_STRING_SVTOOLS_RTF_PLAIN );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/rtfimp.cxx b/sc/source/filter/rtf/rtfimp.cxx
new file mode 100644
index 0000000000..fe3856dfaa
--- /dev/null
+++ b/sc/source/filter/rtf/rtfimp.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 <filter.hxx>
+#include <editutil.hxx>
+#include <rtfimp.hxx>
+#include <rtfparse.hxx>
+#include <ftools.hxx>
+
+ErrCode ScFormatFilterPluginImpl::ScImportRTF( SvStream &rStream, const OUString& rBaseURL, ScDocument *pDoc, ScRange& rRange )
+{
+ ScRTFImport aImp( pDoc, rRange );
+ ErrCode nErr = aImp.Read( rStream, rBaseURL );
+ ScRange aR = aImp.GetRange();
+ rRange.aEnd = aR.aEnd;
+ aImp.WriteToDocument();
+ return nErr;
+}
+
+std::unique_ptr<ScEEAbsImport> ScFormatFilterPluginImpl::CreateRTFImport( ScDocument* pDoc, const ScRange& rRange )
+{
+ return std::make_unique<ScRTFImport>( pDoc, rRange );
+}
+
+ScRTFImport::ScRTFImport( ScDocument* pDocP, const ScRange& rRange ) :
+ ScEEImport( pDocP, rRange )
+{
+ mpParser.reset(new ScRTFParser( mpEngine.get() ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/rtfparse.cxx b/sc/source/filter/rtf/rtfparse.cxx
new file mode 100644
index 0000000000..b2d2b8c252
--- /dev/null
+++ b/sc/source/filter/rtf/rtfparse.cxx
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/svxrtf.hxx>
+#include <svtools/rtftoken.h>
+#include <osl/diagnose.h>
+#include <svl/itempool.hxx>
+
+#include <rtfparse.hxx>
+
+#define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
+
+ScRTFParser::ScRTFParser( EditEngine* pEditP ) :
+ ScEEParser( pEditP ),
+ mnCurPos(0),
+ pActDefault( nullptr ),
+ pDefMerge( nullptr ),
+ nStartAdjust( sal_uLong(~0) ),
+ nLastWidth(0),
+ bNewDef( false )
+{
+ // RTF default FontSize 12Pt
+ tools::Long nMM = o3tl::convert(12, o3tl::Length::pt, o3tl::Length::mm100);
+
+ pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
+ // Free-flying pInsDefault
+ pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
+}
+
+ScRTFParser::~ScRTFParser()
+{
+ pInsDefault.reset();
+ maDefaultList.clear();
+}
+
+ErrCode ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
+{
+ Link<RtfImportInfo&,void> aOldLink = pEdit->GetRtfImportHdl();
+ pEdit->SetRtfImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
+ ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Rtf );
+ if ( nRtfLastToken == RTF_PAR )
+ {
+ if ( !maList.empty() )
+ {
+ auto& pE = maList.back();
+ if ( // Completely empty
+ ( pE->aSel.nStartPara == pE->aSel.nEndPara
+ && pE->aSel.nStartPos == pE->aSel.nEndPos
+ )
+ || // Empty paragraph
+ ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara
+ && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara )
+ && pE->aSel.nEndPos == 0
+ )
+ )
+ { // Don't take over the last paragraph
+ maList.pop_back();
+ }
+ }
+ }
+ ColAdjust();
+ pEdit->SetRtfImportHdl( aOldLink );
+ return nErr;
+}
+
+void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel )
+{
+ // Paragraph -2 strips the attached empty paragraph
+ pE->aSel.nEndPara = aSel.nEndPara - 2;
+ // Although it's called nEndPos, the last one is position + 1
+ pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 );
+}
+
+inline void ScRTFParser::NextRow()
+{
+ if ( nRowMax < ++nRowCnt )
+ nRowMax = nRowCnt;
+}
+
+bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
+{
+ ScRTFColTwips::const_iterator it = aColTwips.find( nTwips );
+ bool bFound = it != aColTwips.end();
+ sal_uInt16 nPos = it - aColTwips.begin();
+ *pCol = static_cast<SCCOL>(nPos);
+ if ( bFound )
+ return true;
+ sal_uInt16 nCount = aColTwips.size();
+ if ( !nCount )
+ return false;
+ SCCOL nCol = *pCol;
+ // nCol is insertion position; the next one higher up is there (or not)
+ if ( nCol < static_cast<SCCOL>(nCount) && ((aColTwips[nCol] - SC_RTFTWIPTOL) <= nTwips) )
+ return true;
+ // Not smaller than everything else? Then compare with the next lower one
+ else if ( nCol != 0 && ((aColTwips[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
+ {
+ (*pCol)--;
+ return true;
+ }
+ return false;
+}
+
+void ScRTFParser::ColAdjust()
+{
+ if ( nStartAdjust == sal_uLong(~0) )
+ return;
+
+ SCCOL nCol = 0;
+ for (size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++i)
+ {
+ auto& pE = maList[i];
+ if ( pE->nCol == 0 )
+ nCol = 0;
+ pE->nCol = nCol;
+ if ( pE->nColOverlap > 1 )
+ nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
+ else
+ {
+ SeekTwips( pE->nTwips, &nCol );
+ if ( ++nCol <= pE->nCol )
+ nCol = pE->nCol + 1; // Moved cell X
+ pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
+ }
+ if ( nCol > nColMax )
+ nColMax = nCol;
+ }
+ nStartAdjust = sal_uLong(~0);
+ aColTwips.clear();
+}
+
+IMPL_LINK( ScRTFParser, 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( pPool.get() );
+ pParser->SetPardMap(SID_ATTR_BRUSH, ATTR_BACKGROUND);
+ pParser->SetPardMap(SID_ATTR_BORDER_OUTER, ATTR_BORDER);
+ pParser->SetPardMap(SID_ATTR_BORDER_SHADOW, ATTR_SHADOW);
+ }
+ break;
+ case RtfImportState::End:
+ if ( rInfo.aSelection.nEndPos )
+ { // If still text: create last paragraph
+ pActDefault = nullptr;
+ rInfo.nToken = RTF_PAR;
+ // EditEngine did not attach an empty paragraph anymore
+ // which EntryEnd could strip
+ rInfo.aSelection.nEndPara++;
+ ProcToken( &rInfo );
+ }
+ break;
+ case RtfImportState::SetAttr:
+ break;
+ case RtfImportState::InsertText:
+ break;
+ case RtfImportState::InsertPara:
+ break;
+ default:
+ OSL_FAIL("unknown ImportInfo.eState");
+ }
+}
+
+// Bad behavior:
+// For RTF_INTBL or respectively at the start of the first RTF_CELL
+// after RTF_CELLX if there was no RTF_INTBL
+void ScRTFParser::NewCellRow()
+{
+ if ( bNewDef )
+ {
+ bNewDef = false;
+ // Not flush on the right? => new table
+ if ( nLastWidth && !maDefaultList.empty() )
+ {
+ const ScRTFCellDefault& rD = *maDefaultList.back();
+ if (rD.nTwips != nLastWidth)
+ {
+ SCCOL n1, n2;
+ if ( !( SeekTwips( nLastWidth, &n1 )
+ && SeekTwips( rD.nTwips, &n2 )
+ && n1 == n2
+ )
+ )
+ {
+ ColAdjust();
+ }
+ }
+ }
+ // Build up TwipCols only after nLastWidth comparison!
+ for (const std::unique_ptr<ScRTFCellDefault> & pCellDefault : maDefaultList)
+ {
+ const ScRTFCellDefault& rD = *pCellDefault;
+ SCCOL nCol;
+ if ( !SeekTwips(rD.nTwips, &nCol) )
+ aColTwips.insert( rD.nTwips );
+ }
+ }
+ pDefMerge = nullptr;
+ pActDefault = maDefaultList.empty() ? nullptr : maDefaultList[0].get();
+ mnCurPos = 0;
+ OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
+}
+
+/*
+ SW:
+ ~~~
+ [\par]
+ \trowd \cellx \cellx ...
+ \intbl \cell \cell ...
+ \row
+ [\par]
+ [\trowd \cellx \cellx ...]
+ \intbl \cell \cell ...
+ \row
+ [\par]
+
+ M$-Word:
+ ~~~~~~~~
+ [\par]
+ \trowd \cellx \cellx ...
+ \intbl \cell \cell ...
+ \intbl \row
+ [\par]
+ [\trowd \cellx \cellx ...]
+ \intbl \cell \cell ...
+ \intbl \row
+ [\par]
+
+ */
+
+void ScRTFParser::ProcToken( RtfImportInfo* pInfo )
+{
+ switch ( pInfo->nToken )
+ {
+ case RTF_TROWD: // denotes table row default, before RTF_CELLX
+ {
+ if (!maDefaultList.empty())
+ nLastWidth = maDefaultList.back()->nTwips;
+
+ nColCnt = 0;
+ if (pActDefault != pInsDefault.get())
+ pActDefault = nullptr;
+ maDefaultList.clear();
+ pDefMerge = nullptr;
+ nRtfLastToken = pInfo->nToken;
+ mnCurPos = 0;
+ }
+ break;
+ case RTF_CLMGF: // The first cell of cells to be merged
+ {
+ pDefMerge = pInsDefault.get();
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLMRG: // A cell to be merged with the preceding cell
+ {
+ if (!pDefMerge && !maDefaultList.empty())
+ {
+ pDefMerge = maDefaultList.back().get();
+ mnCurPos = maDefaultList.size() - 1;
+ }
+ OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
+ if ( pDefMerge ) // Else broken RTF
+ pDefMerge->nColOverlap++; // multiple successive ones possible
+ pInsDefault->nColOverlap = 0; // Flag: ignore these
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CELLX: // closes cell default
+ {
+ bNewDef = true;
+ pInsDefault->nCol = nColCnt;
+ pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
+ maDefaultList.push_back( std::move(pInsDefault) );
+ // New free-flying pInsDefault
+ pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
+ if ( ++nColCnt > nColMax )
+ nColMax = nColCnt;
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_INTBL: // before the first RTF_CELL
+ {
+ // Once over NextToken and once over UnknownAttrToken
+ // or e.g. \intbl ... \cell \pard \intbl ... \cell
+ if ( nRtfLastToken != RTF_INTBL && nRtfLastToken != RTF_CELL && nRtfLastToken != RTF_PAR )
+ {
+ NewCellRow();
+ nRtfLastToken = pInfo->nToken;
+ }
+ }
+ break;
+ case RTF_CELL: // denotes the end of a cell.
+ {
+ OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
+ if ( bNewDef || !pActDefault )
+ NewCellRow(); // before was no \intbl, bad behavior
+ // Broken RTF? Let's save what we can
+ if ( !pActDefault )
+ pActDefault = pInsDefault.get();
+ if ( pActDefault->nColOverlap > 0 )
+ { // Not merged with preceding
+ mxActEntry->nCol = pActDefault->nCol;
+ mxActEntry->nColOverlap = pActDefault->nColOverlap;
+ mxActEntry->nTwips = pActDefault->nTwips;
+ mxActEntry->nRow = nRowCnt;
+ mxActEntry->aItemSet.Set(pActDefault->aItemSet);
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+
+ if ( nStartAdjust == sal_uLong(~0) )
+ nStartAdjust = maList.size();
+ maList.push_back(mxActEntry);
+ NewActEntry(mxActEntry.get()); // New free-flying mxActEntry
+ }
+ else
+ { // Assign current Twips to MergeCell
+ if ( !maList.empty() )
+ {
+ auto& pE = maList.back();
+ pE->nTwips = pActDefault->nTwips;
+ }
+ // Adjust selection of free-flying mxActEntry
+ // Paragraph -1 due to separated text in EditEngine during parsing
+ mxActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1;
+ }
+
+ pActDefault = nullptr;
+ if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
+ pActDefault = maDefaultList[++mnCurPos].get();
+
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_ROW: // denotes the end of a row
+ {
+ NextRow();
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_PAR: // Paragraph
+ {
+ if ( !pActDefault )
+ { // text not in table
+ ColAdjust(); // close the processing table
+ mxActEntry->nCol = 0;
+ mxActEntry->nRow = nRowCnt;
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+ maList.push_back(mxActEntry);
+ NewActEntry(mxActEntry.get()); // new mxActEntry
+ NextRow();
+ }
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ default:
+ { // do not set nRtfLastToken
+ switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
+ {
+ case RTF_SHADINGDEF:
+ static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBackgroundAttr(
+ pInfo->nToken, pInsDefault->aItemSet, true );
+ break;
+ case RTF_BRDRDEF:
+ static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(
+ pInfo->nToken, pInsDefault->aItemSet, true );
+ break;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/XclExpChangeTrack.cxx b/sc/source/filter/xcl97/XclExpChangeTrack.cxx
new file mode 100644
index 0000000000..9c22af8a64
--- /dev/null
+++ b/sc/source/filter/xcl97/XclExpChangeTrack.cxx
@@ -0,0 +1,1729 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <numeric>
+#include <stdio.h>
+#include <sot/storage.hxx>
+#include <XclExpChangeTrack.hxx>
+#include <utility>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xltools.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <root.hxx>
+#include <tools/Guid.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <rtl/uuid.h>
+#include <svl/sharedstring.hxx>
+
+using namespace oox;
+
+static OString lcl_DateTimeToOString( const DateTime& rDateTime )
+{
+ char sBuf[ 200 ];
+ snprintf( sBuf, sizeof( sBuf ),
+ "%d-%02d-%02dT%02d:%02d:%02d.%09" SAL_PRIuUINT32 "Z",
+ rDateTime.GetYear(), rDateTime.GetMonth(), rDateTime.GetDay(),
+ rDateTime.GetHour(), rDateTime.GetMin(), rDateTime.GetSec(),
+ rDateTime.GetNanoSec() );
+ return sBuf;
+}
+
+// local functions
+
+static void lcl_WriteDateTime( XclExpStream& rStrm, const DateTime& rDateTime )
+{
+ rStrm.SetSliceSize( 7 );
+ rStrm << static_cast<sal_uInt16>(rDateTime.GetYear())
+ << static_cast<sal_uInt8>(rDateTime.GetMonth())
+ << static_cast<sal_uInt8>(rDateTime.GetDay())
+ << static_cast<sal_uInt8>(rDateTime.GetHour())
+ << static_cast<sal_uInt8>(rDateTime.GetMin())
+ << static_cast<sal_uInt8>(rDateTime.GetSec());
+ rStrm.SetSliceSize( 0 );
+}
+
+// write string and fill rest of <nLength> with zero bytes
+// <nLength> is without string header
+static void lcl_WriteFixedString( XclExpStream& rStrm, const XclExpString& rString, std::size_t nLength )
+{
+ std::size_t nStrBytes = rString.GetBufferSize();
+ OSL_ENSURE( nLength >= nStrBytes, "lcl_WriteFixedString - String too long" );
+ if( rString.Len() > 0 )
+ rStrm << rString;
+ if( nLength > nStrBytes )
+ rStrm.WriteZeroBytes( nLength - nStrBytes );
+}
+
+static void lcl_GenerateGUID( sal_uInt8* pGUID, bool& rValidGUID )
+{
+ rtl_createUuid( pGUID, rValidGUID ? pGUID : nullptr, false );
+ rValidGUID = true;
+}
+
+static void lcl_WriteGUID( XclExpStream& rStrm, const sal_uInt8* pGUID )
+{
+ rStrm.SetSliceSize( 16 );
+ for( std::size_t nIndex = 0; nIndex < 16; nIndex++ )
+ rStrm << pGUID[ nIndex ];
+ rStrm.SetSliceSize( 0 );
+}
+
+XclExpUserBView::XclExpUserBView( const OUString& rUsername, const sal_uInt8* pGUID ) :
+ sUsername( rUsername )
+{
+ memcpy( aGUID, pGUID, 16 );
+}
+
+void XclExpUserBView::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt32(0xFF078014)
+ << sal_uInt32(0x00000001);
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm.WriteZeroBytes( 8 );
+ rStrm << sal_uInt32(1200)
+ << sal_uInt32(1000)
+ << sal_uInt16(1000)
+ << sal_uInt16(0x0CF7)
+ << sal_uInt16(0x0000)
+ << sal_uInt16(0x0001)
+ << sal_uInt16(0x0000);
+ if( sUsername.Len() > 0 )
+ rStrm << sUsername;
+}
+
+sal_uInt16 XclExpUserBView::GetNum() const
+{
+ return 0x01A9;
+}
+
+std::size_t XclExpUserBView::GetLen() const
+{
+ return 50 + ((sUsername.Len() > 0) ? sUsername.GetSize() : 0);
+}
+
+XclExpUserBViewList::XclExpUserBViewList( const ScChangeTrack& rChangeTrack )
+{
+ sal_uInt8 aGUID[ 16 ];
+ bool bValidGUID = false;
+ const std::set<OUString>& rStrColl = rChangeTrack.GetUserCollection();
+ aViews.reserve(rStrColl.size());
+ for (const auto& rStr : rStrColl)
+ {
+ lcl_GenerateGUID( aGUID, bValidGUID );
+ aViews.emplace_back( rStr, aGUID );
+ }
+}
+
+XclExpUserBViewList::~XclExpUserBViewList()
+{
+}
+
+void XclExpUserBViewList::Save( XclExpStream& rStrm )
+{
+ for( XclExpUserBView& rView : aViews )
+ rView.Save( rStrm );
+}
+
+XclExpUsersViewBegin::XclExpUsersViewBegin( const sal_uInt8* pGUID, sal_uInt32 nTab ) :
+ nCurrTab( nTab )
+{
+ memcpy( aGUID, pGUID, 16 );
+}
+
+void XclExpUsersViewBegin::SaveCont( XclExpStream& rStrm )
+{
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm << nCurrTab
+ << sal_uInt32(100)
+ << sal_uInt32(64)
+ << sal_uInt32(3)
+ << sal_uInt32(0x0000003C)
+ << sal_uInt16(0)
+ << sal_uInt16(3)
+ << sal_uInt16(0)
+ << sal_uInt16(3)
+ << double(0)
+ << double(0)
+ << sal_Int16(-1)
+ << sal_Int16(-1);
+}
+
+sal_uInt16 XclExpUsersViewBegin::GetNum() const
+{
+ return 0x01AA;
+}
+
+std::size_t XclExpUsersViewBegin::GetLen() const
+{
+ return 64;
+}
+
+void XclExpUsersViewEnd::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0001);
+}
+
+sal_uInt16 XclExpUsersViewEnd::GetNum() const
+{
+ return 0x01AB;
+}
+
+std::size_t XclExpUsersViewEnd::GetLen() const
+{
+ return 2;
+}
+
+void XclExpChTr0x0191::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0000);
+}
+
+sal_uInt16 XclExpChTr0x0191::GetNum() const
+{
+ return 0x0191;
+}
+
+std::size_t XclExpChTr0x0191::GetLen() const
+{
+ return 2;
+}
+
+void XclExpChTr0x0198::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0006)
+ << sal_uInt16(0x0000);
+}
+
+sal_uInt16 XclExpChTr0x0198::GetNum() const
+{
+ return 0x0198;
+}
+
+std::size_t XclExpChTr0x0198::GetLen() const
+{
+ return 4;
+}
+
+void XclExpChTr0x0192::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( 0x0022 );
+ rStrm.WriteZeroBytes( 510 );
+}
+
+sal_uInt16 XclExpChTr0x0192::GetNum() const
+{
+ return 0x0192;
+}
+
+std::size_t XclExpChTr0x0192::GetLen() const
+{
+ return 512;
+}
+
+void XclExpChTr0x0197::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0000);
+}
+
+sal_uInt16 XclExpChTr0x0197::GetNum() const
+{
+ return 0x0197;
+}
+
+std::size_t XclExpChTr0x0197::GetLen() const
+{
+ return 2;
+}
+
+XclExpChTrEmpty::~XclExpChTrEmpty()
+{
+}
+
+sal_uInt16 XclExpChTrEmpty::GetNum() const
+{
+ return nRecNum;
+}
+
+std::size_t XclExpChTrEmpty::GetLen() const
+{
+ return 0;
+}
+
+XclExpChTr0x0195::~XclExpChTr0x0195()
+{
+}
+
+void XclExpChTr0x0195::SaveCont( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 162 );
+}
+
+sal_uInt16 XclExpChTr0x0195::GetNum() const
+{
+ return 0x0195;
+}
+
+std::size_t XclExpChTr0x0195::GetLen() const
+{
+ return 162;
+}
+
+XclExpChTr0x0194::~XclExpChTr0x0194()
+{
+}
+
+void XclExpChTr0x0194::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt32(0);
+ lcl_WriteDateTime( rStrm, aDateTime );
+ rStrm << sal_uInt8(0);
+ lcl_WriteFixedString( rStrm, sUsername, 147 );
+}
+
+sal_uInt16 XclExpChTr0x0194::GetNum() const
+{
+ return 0x0194;
+}
+
+std::size_t XclExpChTr0x0194::GetLen() const
+{
+ return 162;
+}
+
+XclExpChTrHeader::~XclExpChTrHeader()
+{
+}
+
+void XclExpChTrHeader::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0006)
+ << sal_uInt16(0x0000)
+ << sal_uInt16(0x000D);
+ lcl_WriteGUID( rStrm, aGUID );
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm << nCount
+ << sal_uInt16(0x0001)
+ << sal_uInt32(0x00000000)
+ << sal_uInt16(0x001E);
+}
+
+sal_uInt16 XclExpChTrHeader::GetNum() const
+{
+ return 0x0196;
+}
+
+std::size_t XclExpChTrHeader::GetLen() const
+{
+ return 50;
+}
+
+void XclExpChTrHeader::SaveXml( XclExpXmlStream& rRevisionHeadersStrm )
+{
+ sax_fastparser::FSHelperPtr pHeaders = rRevisionHeadersStrm.GetCurrentStream();
+ tools::Guid aGuid(aGUID);
+ rRevisionHeadersStrm.WriteAttributes(
+ XML_guid, aGuid.getString(),
+ XML_lastGuid, nullptr, // OOXTODO
+ XML_shared, nullptr, // OOXTODO
+ XML_diskRevisions, nullptr, // OOXTODO
+ XML_history, nullptr, // OOXTODO
+ XML_trackRevisions, nullptr, // OOXTODO
+ XML_exclusive, nullptr, // OOXTODO
+ XML_revisionId, nullptr, // OOXTODO
+ XML_version, nullptr, // OOXTODO
+ XML_keepChangeHistory, nullptr, // OOXTODO
+ XML_protected, nullptr, // OOXTODO
+ XML_preserveHistory, nullptr); // OOXTODO
+ pHeaders->write( ">" );
+}
+
+void XclExpXmlChTrHeaders::SetGUID( const sal_uInt8* pGUID )
+{
+ memcpy(maGUID, pGUID, 16);
+}
+
+void XclExpXmlChTrHeaders::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pHeaders = rStrm.GetCurrentStream();
+
+ pHeaders->write("<")->writeId(XML_headers);
+
+ tools::Guid aGuid(maGUID);
+ rStrm.WriteAttributes(
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)),
+ XML_guid, aGuid.getString(),
+ XML_lastGuid, nullptr, // OOXTODO
+ XML_shared, nullptr, // OOXTODO
+ XML_diskRevisions, nullptr, // OOXTODO
+ XML_history, nullptr, // OOXTODO
+ XML_trackRevisions, nullptr, // OOXTODO
+ XML_exclusive, nullptr, // OOXTODO
+ XML_revisionId, nullptr, // OOXTODO
+ XML_version, nullptr, // OOXTODO
+ XML_keepChangeHistory, nullptr, // OOXTODO
+ XML_protected, nullptr, // OOXTODO
+ XML_preserveHistory, nullptr); // OOXTODO
+
+ pHeaders->write(">");
+}
+
+XclExpXmlChTrHeader::XclExpXmlChTrHeader(
+ OUString aUserName, const DateTime& rDateTime, const sal_uInt8* pGUID,
+ sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf ) :
+ maUserName(std::move(aUserName)), maDateTime(rDateTime), mnLogNumber(nLogNumber),
+ mnMinAction(0), mnMaxAction(0)
+{
+ memcpy(maGUID, pGUID, 16);
+ if (rBuf.GetBufferCount())
+ {
+ maTabBuffer.resize(rBuf.GetBufferCount());
+ rBuf.GetBufferCopy(maTabBuffer.data());
+ }
+}
+
+void XclExpXmlChTrHeader::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pHeader = rStrm.GetCurrentStream();
+
+ pHeader->write("<")->writeId(XML_header);
+
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pRevLogStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/revisions/", "revisionLog", mnLogNumber),
+ XclXmlUtils::GetStreamName(nullptr, "revisionLog", mnLogNumber),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("revisionLog"),
+ &aRelId);
+
+ tools::Guid aGuid(maGUID);
+ rStrm.WriteAttributes(
+ XML_guid, aGuid.getString(),
+ XML_dateTime, lcl_DateTimeToOString(maDateTime),
+ XML_userName, maUserName,
+ FSNS(XML_r, XML_id), aRelId);
+
+ if (mnMinAction)
+ rStrm.WriteAttributes(XML_minRId, OUString::number(mnMinAction));
+
+ if (mnMaxAction)
+ rStrm.WriteAttributes(XML_maxRId, OUString::number(mnMaxAction));
+
+ if (!maTabBuffer.empty())
+ // next available sheet index.
+ rStrm.WriteAttributes(XML_maxSheetId, OUString::number(maTabBuffer.back()+1));
+
+ pHeader->write(">");
+
+ if (!maTabBuffer.empty())
+ {
+ // Write sheet index map.
+ size_t n = maTabBuffer.size();
+ pHeader->startElement(XML_sheetIdMap, XML_count, OString::number(n));
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ pHeader->singleElement(XML_sheetId, XML_val, OString::number(maTabBuffer[i]));
+ }
+ pHeader->endElement(XML_sheetIdMap);
+ }
+
+ // Write all revision logs in a separate stream.
+
+ rStrm.PushStream(pRevLogStrm);
+
+ pRevLogStrm->write("<")->writeId(XML_revisions);
+
+ rStrm.WriteAttributes(
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)));
+
+ pRevLogStrm->write(">");
+
+ for (const auto& rxAction : maActions)
+ {
+ rxAction->SaveXml(rStrm);
+ }
+
+ pRevLogStrm->write("</")->writeId(XML_revisions)->write(">");
+
+ rStrm.PopStream();
+
+ pHeader->write("</")->writeId(XML_header)->write(">");
+}
+
+void XclExpXmlChTrHeader::AppendAction( std::unique_ptr<XclExpChTrAction> pAction )
+{
+ sal_uInt32 nActionNum = pAction->GetActionNumber();
+ if (!mnMinAction || mnMinAction > nActionNum)
+ mnMinAction = nActionNum;
+
+ if (!mnMaxAction || mnMaxAction < nActionNum)
+ mnMaxAction = nActionNum;
+
+ maActions.push_back(std::move(pAction));
+}
+
+XclExpChTrInfo::XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime, const sal_uInt8* pGUID ) :
+ sUsername( rUsername ),
+ aDateTime( rDateTime )
+{
+ memcpy( aGUID, pGUID, 16 );
+}
+
+XclExpChTrInfo::~XclExpChTrInfo()
+{
+}
+
+void XclExpChTrInfo::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt32(0xFFFFFFFF)
+ << sal_uInt32(0x00000000)
+ << sal_uInt32(0x00000020)
+ << sal_uInt16(0xFFFF);
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm << sal_uInt16(0x04B0);
+ lcl_WriteFixedString( rStrm, sUsername, 113 );
+ lcl_WriteDateTime( rStrm, aDateTime );
+ rStrm << sal_uInt8(0x0000)
+ << sal_uInt16(0x0002);
+}
+
+sal_uInt16 XclExpChTrInfo::GetNum() const
+{
+ return 0x0138;
+}
+
+std::size_t XclExpChTrInfo::GetLen() const
+{
+ return 158;
+}
+
+XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( sal_uInt16 nCount ) :
+ nBufSize( nCount ),
+ nLastId( nCount )
+{
+ pBuffer.reset( new sal_uInt16[ nBufSize ] );
+ memset( pBuffer.get(), 0, sizeof(sal_uInt16) * nBufSize );
+ pLast = pBuffer.get() + nBufSize - 1;
+}
+
+XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( const XclExpChTrTabIdBuffer& rCopy ) :
+ nBufSize( rCopy.nBufSize ),
+ nLastId( rCopy.nLastId )
+{
+ pBuffer.reset( new sal_uInt16[ nBufSize ] );
+ memcpy( pBuffer.get(), rCopy.pBuffer.get(), sizeof(sal_uInt16) * nBufSize );
+ pLast = pBuffer.get() + nBufSize - 1;
+}
+
+XclExpChTrTabIdBuffer::~XclExpChTrTabIdBuffer()
+{
+}
+
+void XclExpChTrTabIdBuffer::InitFill( sal_uInt16 nIndex )
+{
+ OSL_ENSURE( nIndex < nLastId, "XclExpChTrTabIdBuffer::Insert - out of range" );
+
+ sal_uInt16 nFreeCount = 0;
+ for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ )
+ {
+ if( !*pElem )
+ nFreeCount++;
+ if( nFreeCount > nIndex )
+ {
+ *pElem = nLastId--;
+ return;
+ }
+ }
+}
+
+void XclExpChTrTabIdBuffer::InitFillup()
+{
+ sal_uInt16 nFreeCount = 1;
+ for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ )
+ if( !*pElem )
+ *pElem = nFreeCount++;
+ nLastId = nBufSize;
+}
+
+sal_uInt16 XclExpChTrTabIdBuffer::GetId( sal_uInt16 nIndex ) const
+{
+ assert(nIndex < nBufSize && "XclExpChTrTabIdBuffer::GetId - out of range");
+ return pBuffer[ nIndex ];
+}
+
+bool XclExpChTrTabIdBuffer::HasId( sal_uInt16 nIndex ) const
+{
+ return nIndex < nBufSize;
+}
+
+void XclExpChTrTabIdBuffer::Remove()
+{
+ OSL_ENSURE( pBuffer.get() <= pLast, "XclExpChTrTabIdBuffer::Remove - buffer empty" );
+ sal_uInt16* pElem = pBuffer.get();
+ while( (pElem <= pLast) && (*pElem != nLastId) )
+ pElem++;
+ while( pElem < pLast )
+ {
+ *pElem = *(pElem + 1);
+ pElem++;
+ }
+ pLast--;
+ nLastId--;
+}
+
+XclExpChTrTabId::XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer )
+ : nTabCount( rBuffer.GetBufferCount() )
+{
+ pBuffer.reset( new sal_uInt16[ nTabCount ] );
+ rBuffer.GetBufferCopy( pBuffer.get() );
+}
+
+XclExpChTrTabId::~XclExpChTrTabId()
+{
+ Clear();
+}
+
+void XclExpChTrTabId::Copy( const XclExpChTrTabIdBuffer& rBuffer )
+{
+ Clear();
+ nTabCount = rBuffer.GetBufferCount();
+ pBuffer.reset( new sal_uInt16[ nTabCount ] );
+ rBuffer.GetBufferCopy( pBuffer.get() );
+}
+
+void XclExpChTrTabId::SaveCont( XclExpStream& rStrm )
+{
+ rStrm.EnableEncryption();
+ if( pBuffer )
+ rStrm.Write(pBuffer.get(), nTabCount);
+ else
+ for( sal_uInt16 nIndex = 1; nIndex <= nTabCount; nIndex++ )
+ rStrm << nIndex;
+}
+
+sal_uInt16 XclExpChTrTabId::GetNum() const
+{
+ return 0x013D;
+}
+
+std::size_t XclExpChTrTabId::GetLen() const
+{
+ return nTabCount << 1;
+}
+
+// ! does not copy additional actions
+XclExpChTrAction::XclExpChTrAction( const XclExpChTrAction& rCopy ) :
+ ExcRecord( rCopy ),
+ sUsername( rCopy.sUsername ),
+ aDateTime( rCopy.aDateTime ),
+ nIndex( 0 ),
+ bAccepted( rCopy.bAccepted ),
+ rTabInfo( rCopy.rTabInfo ),
+ rIdBuffer( rCopy.rIdBuffer ),
+ nLength( rCopy.nLength ),
+ nOpCode( rCopy.nOpCode ),
+ bForceInfo( rCopy.bForceInfo )
+{
+}
+
+XclExpChTrAction::XclExpChTrAction(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ sal_uInt16 nNewOpCode ) :
+ sUsername( rAction.GetUser() ),
+ aDateTime( rAction.GetDateTime() ),
+ nIndex( 0 ),
+ bAccepted( rAction.IsAccepted() ),
+ rTabInfo( rRoot.GetTabInfo() ),
+ rIdBuffer( rTabIdBuffer ),
+ nLength( 0 ),
+ nOpCode( nNewOpCode ),
+ bForceInfo( false )
+{
+ aDateTime.SetSec( 0 );
+ aDateTime.SetNanoSec( 0 );
+}
+
+XclExpChTrAction::~XclExpChTrAction()
+{
+}
+
+void XclExpChTrAction::SetAddAction( XclExpChTrAction* pAction )
+{
+ if( pAddAction )
+ pAddAction->SetAddAction( pAction );
+ else
+ pAddAction.reset( pAction );
+}
+
+void XclExpChTrAction::AddDependentContents(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const ScChangeTrack& rChangeTrack )
+{
+ ScChangeActionMap aActionMap;
+
+ rChangeTrack.GetDependents( const_cast<ScChangeAction*>(&rAction), aActionMap );
+ for( const auto& rEntry : aActionMap )
+ if( rEntry.second->GetType() == SC_CAT_CONTENT )
+ SetAddAction( new XclExpChTrCellContent(
+ *static_cast<const ScChangeActionContent*>(rEntry.second), rRoot, rIdBuffer ) );
+}
+
+void XclExpChTrAction::SetIndex( sal_uInt32& rIndex )
+{
+ nIndex = rIndex++;
+}
+
+void XclExpChTrAction::SaveCont( XclExpStream& rStrm )
+{
+ OSL_ENSURE( nOpCode != EXC_CHTR_OP_UNKNOWN, "XclExpChTrAction::SaveCont - unknown action" );
+ rStrm << nLength
+ << nIndex
+ << nOpCode
+ << static_cast<sal_uInt16>(bAccepted ? EXC_CHTR_ACCEPT : EXC_CHTR_NOTHING);
+ SaveActionData( rStrm );
+}
+
+void XclExpChTrAction::PrepareSaveAction( XclExpStream& /*rStrm*/ ) const
+{
+}
+
+void XclExpChTrAction::CompleteSaveAction( XclExpStream& /*rStrm*/ ) const
+{
+}
+
+void XclExpChTrAction::Save( XclExpStream& rStrm )
+{
+ if (UsesDeletedTab())
+ {
+ SAL_WARN("sc", "XclExpChTrAction : unable to export position with tab of EXC_TAB_DELETED");
+ return;
+ }
+ PrepareSaveAction( rStrm );
+ ExcRecord::Save( rStrm );
+ if( pAddAction )
+ pAddAction->Save( rStrm );
+ CompleteSaveAction( rStrm );
+}
+
+std::size_t XclExpChTrAction::GetLen() const
+{
+ return GetHeaderByteCount() + GetActionByteCount();
+}
+
+XclExpChTrData::XclExpChTrData() :
+ mpFormulaCell( nullptr ),
+ fValue( 0.0 ),
+ nRKValue( 0 ),
+ nType( EXC_CHTR_TYPE_EMPTY ),
+ nSize( 0 )
+{
+}
+
+XclExpChTrData::~XclExpChTrData()
+{
+ Clear();
+}
+
+void XclExpChTrData::Clear()
+{
+ pString.reset();
+ mpFormulaCell = nullptr;
+ mxTokArr.reset();
+ maRefLog.clear();
+ fValue = 0.0;
+ nRKValue = 0;
+ nType = EXC_CHTR_TYPE_EMPTY;
+ nSize = 0;
+}
+
+void XclExpChTrData::WriteFormula( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer )
+{
+ OSL_ENSURE( mxTokArr && !mxTokArr->Empty(), "XclExpChTrData::Write - no formula" );
+ rStrm << *mxTokArr;
+
+ for( const auto& rLogEntry : maRefLog )
+ {
+ if( rLogEntry.mpUrl && rLogEntry.mpFirstTab )
+ {
+ rStrm << *rLogEntry.mpUrl << sal_uInt8(0x01) << *rLogEntry.mpFirstTab << sal_uInt8(0x02);
+ }
+ else
+ {
+ bool bSingleTab = rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab;
+ rStrm.SetSliceSize( bSingleTab ? 6 : 8 );
+ rStrm << sal_uInt8(0x01) << sal_uInt8(0x02) << sal_uInt8(0x00);
+ rStrm << rTabIdBuffer.GetId( rLogEntry.mnFirstXclTab );
+ if( bSingleTab )
+ rStrm << sal_uInt8(0x02);
+ else
+ rStrm << sal_uInt8(0x00) << rTabIdBuffer.GetId( rLogEntry.mnLastXclTab );
+ }
+ }
+ rStrm.SetSliceSize( 0 );
+ rStrm << sal_uInt8(0x00);
+}
+
+void XclExpChTrData::Write( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer )
+{
+ switch( nType )
+ {
+ case EXC_CHTR_TYPE_RK:
+ rStrm << nRKValue;
+ break;
+ case EXC_CHTR_TYPE_DOUBLE:
+ rStrm << fValue;
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ OSL_ENSURE( pString, "XclExpChTrData::Write - no string" );
+ rStrm << *pString;
+ break;
+ case EXC_CHTR_TYPE_FORMULA:
+ WriteFormula( rStrm, rTabIdBuffer );
+ break;
+ }
+}
+
+static bool lcl_IsDeletedTab(const XclExpChTrTabIdBuffer& rTabIdBuffer, sal_uInt16 nIndex)
+{
+ return !rTabIdBuffer.HasId(nIndex);
+}
+
+bool XclExpChTrData::UsesDeletedTab(const XclExpChTrTabIdBuffer& rTabIdBuffer) const
+{
+ if (nType != EXC_CHTR_TYPE_FORMULA)
+ return false;
+
+ for( const auto& rLogEntry : maRefLog )
+ {
+ if (rLogEntry.mpUrl && rLogEntry.mpFirstTab)
+ continue;
+ if (lcl_IsDeletedTab(rTabIdBuffer, rLogEntry.mnFirstXclTab))
+ return true;
+ bool bSingleTab = rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab;
+ if (!bSingleTab)
+ continue;
+ if (lcl_IsDeletedTab(rTabIdBuffer, rLogEntry.mnLastXclTab))
+ return true;
+ }
+ return false;
+}
+
+XclExpChTrCellContent::XclExpChTrCellContent(
+ const ScChangeActionContent& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_CELL ),
+ XclExpRoot( rRoot ),
+ aPosition( rAction.GetBigRange().MakeRange( rRoot.GetDoc()).aStart )
+{
+ sal_uInt32 nDummy32;
+ sal_uInt16 nDummy16;
+ GetCellData( rRoot, rAction.GetOldCell(), pOldData, nDummy32, nOldLength );
+ GetCellData( rRoot, rAction.GetNewCell(), pNewData, nLength, nDummy16 );
+}
+
+XclExpChTrCellContent::~XclExpChTrCellContent()
+{
+ pOldData.reset();
+ pNewData.reset();
+}
+
+void XclExpChTrCellContent::MakeEmptyChTrData( std::unique_ptr<XclExpChTrData>& rpData )
+{
+ if( rpData )
+ rpData->Clear();
+ else
+ rpData.reset( new XclExpChTrData );
+}
+
+void XclExpChTrCellContent::GetCellData(
+ const XclExpRoot& rRoot, const ScCellValue& rScCell,
+ std::unique_ptr<XclExpChTrData>& rpData, sal_uInt32& rXclLength1, sal_uInt16& rXclLength2 )
+{
+ MakeEmptyChTrData( rpData );
+ rXclLength1 = 0x0000003A;
+ rXclLength2 = 0x0000;
+
+ if (rScCell.isEmpty())
+ {
+ rpData.reset();
+ return;
+ }
+
+ switch (rScCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ rpData->fValue = rScCell.getDouble();
+ if( XclTools::GetRKFromDouble( rpData->nRKValue, rpData->fValue ) )
+ {
+ rpData->nType = EXC_CHTR_TYPE_RK;
+ rpData->nSize = 4;
+ rXclLength1 = 0x0000003E;
+ rXclLength2 = 0x0004;
+ }
+ else
+ {
+ rpData->nType = EXC_CHTR_TYPE_DOUBLE;
+ rpData->nSize = 8;
+ rXclLength1 = 0x00000042;
+ rXclLength2 = 0x0008;
+ }
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ OUString sCellStr;
+ if (rScCell.getType() == CELLTYPE_STRING)
+ {
+ sCellStr = rScCell.getSharedString()->getString();
+ rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
+ rRoot, sCellStr, nullptr);
+ }
+ else
+ {
+ XclExpHyperlinkHelper aLinkHelper( rRoot, aPosition );
+ if (rScCell.getEditText())
+ {
+ sCellStr = ScEditUtil::GetString(*rScCell.getEditText(), &GetDoc());
+ rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
+ rRoot, *rScCell.getEditText(), nullptr, aLinkHelper);
+ }
+ else
+ {
+ rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
+ rRoot, OUString(), nullptr);
+ }
+ }
+ rpData->pString.reset( new XclExpString( sCellStr, XclStrFlags::NONE, 32766 ) );
+ rpData->nType = EXC_CHTR_TYPE_STRING;
+ rpData->nSize = 3 + rpData->pString->GetSize();
+ rXclLength1 = 64 + (sCellStr.getLength() << 1);
+ rXclLength2 = 6 + static_cast<sal_uInt16>(sCellStr.getLength() << 1);
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ const ScFormulaCell* pFmlCell = rScCell.getFormula();
+ rpData->mpFormulaCell = pFmlCell;
+
+ const ScTokenArray* pTokenArray = pFmlCell->GetCode();
+ if( pTokenArray )
+ {
+ XclExpRefLog& rRefLog = rpData->maRefLog;
+ rpData->mxTokArr = GetFormulaCompiler().CreateFormula(
+ EXC_FMLATYPE_CELL, *pTokenArray, &pFmlCell->aPos, &rRefLog );
+ rpData->nType = EXC_CHTR_TYPE_FORMULA;
+ std::size_t nSize = std::accumulate(rRefLog.begin(), rRefLog.end(),
+ static_cast<std::size_t>(rpData->mxTokArr->GetSize() + 3),
+ [](const std::size_t& rSum, const XclExpRefLogEntry& rLogEntry) {
+ if( rLogEntry.mpUrl && rLogEntry.mpFirstTab )
+ return rSum + rLogEntry.mpUrl->GetSize() + rLogEntry.mpFirstTab->GetSize() + 2;
+ else
+ return rSum + ((rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab) ? 6 : 8);
+ });
+ rpData->nSize = ::std::min< std::size_t >( nSize, 0xFFFF );
+ rXclLength1 = 0x00000052;
+ rXclLength2 = 0x0018;
+ }
+ }
+ break;
+ default:;
+ }
+}
+
+bool XclExpChTrCellContent::UsesDeletedTab() const
+{
+ if (IsDeletedTab(aPosition.Tab()))
+ return true;
+ if (pOldData && pOldData->UsesDeletedTab(rIdBuffer))
+ return true;
+ return pNewData && pNewData->UsesDeletedTab(rIdBuffer);
+}
+
+void XclExpChTrCellContent::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aPosition.Tab() );
+ rStrm << static_cast<sal_uInt16>((pOldData ? (pOldData->nType << 3) : 0x0000) | (pNewData ? pNewData->nType : 0x0000))
+ << sal_uInt16(0x0000);
+ Write2DAddress( rStrm, aPosition );
+ rStrm << nOldLength
+ << sal_uInt32(0x00000000);
+ if( pOldData )
+ pOldData->Write( rStrm, rIdBuffer );
+ if( pNewData )
+ pNewData->Write( rStrm, rIdBuffer );
+}
+
+sal_uInt16 XclExpChTrCellContent::GetNum() const
+{
+ return 0x013B;
+}
+
+std::size_t XclExpChTrCellContent::GetActionByteCount() const
+{
+ std::size_t nLen = 16;
+ if( pOldData )
+ nLen += pOldData->nSize;
+ if( pNewData )
+ nLen += pNewData->nSize;
+ return nLen;
+}
+
+static const char* lcl_GetType( XclExpChTrData* pData )
+{
+ switch( pData->nType )
+ {
+ case EXC_CHTR_TYPE_RK:
+ case EXC_CHTR_TYPE_DOUBLE:
+ return "n";
+ case EXC_CHTR_TYPE_FORMULA:
+ {
+ ScFormulaCell* pFormulaCell = const_cast< ScFormulaCell* >( pData->mpFormulaCell );
+ const char* sType;
+ OUString sValue;
+ XclXmlUtils::GetFormulaTypeAndValue( *pFormulaCell, sType, sValue );
+ return sType;
+ }
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ return "inlineStr";
+ default:
+ break;
+ }
+ return "*unknown*";
+}
+
+static void lcl_WriteCell( XclExpXmlStream& rStrm, sal_Int32 nElement, const ScAddress& rPosition, XclExpChTrData* pData )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+
+ pStream->startElement(nElement,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rPosition),
+ XML_s, nullptr, // OOXTODO: not supported
+ XML_t, lcl_GetType(pData),
+ XML_cm, nullptr, // OOXTODO: not supported
+ XML_vm, nullptr, // OOXTODO: not supported
+ XML_ph, nullptr); // OOXTODO: not supported
+ switch( pData->nType )
+ {
+ case EXC_CHTR_TYPE_RK:
+ case EXC_CHTR_TYPE_DOUBLE:
+ pStream->startElement(XML_v);
+ pStream->write( pData->fValue );
+ pStream->endElement( XML_v );
+ break;
+ case EXC_CHTR_TYPE_FORMULA:
+ pStream->startElement( XML_f
+ // OOXTODO: other attributes? see XclExpFormulaCell::SaveXml()
+ );
+ pStream->writeEscaped( XclXmlUtils::ToOUString(
+ rStrm.GetRoot().GetCompileFormulaContext(),
+ pData->mpFormulaCell->aPos, pData->mpFormulaCell->GetCode()));
+ pStream->endElement( XML_f );
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ pStream->startElement(XML_is);
+ if( pData->mpFormattedString )
+ pData->mpFormattedString->WriteXml( rStrm );
+ else
+ pData->pString->WriteXml( rStrm );
+ pStream->endElement( XML_is );
+ break;
+ default:
+ // ignore
+ break;
+ }
+ pStream->endElement( nElement );
+}
+
+void XclExpChTrCellContent::SaveXml( XclExpXmlStream& rRevisionLogStrm )
+{
+ if (IsDeletedTab(aPosition.Tab()))
+ {
+ // seen on attempt to export tdf66241-1.ods to xlsx
+ SAL_WARN("sc", "XclExpChTrCellContent: unable to export position with tab of EXC_TAB_DELETED");
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
+ pStream->startElement( XML_rcc,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sId, OString::number(GetTabId(aPosition.Tab())),
+ XML_odxf, nullptr, // OOXTODO: not supported
+ XML_xfDxf, nullptr, // OOXTODO: not supported
+ XML_s, nullptr, // OOXTODO: not supported
+ XML_dxf, nullptr, // OOXTODO: not supported
+ XML_numFmtId, nullptr, // OOXTODO: not supported
+ XML_quotePrefix, nullptr, // OOXTODO: not supported
+ XML_oldQuotePrefix, nullptr, // OOXTODO: not supported
+ XML_ph, nullptr, // OOXTODO: not supported
+ XML_oldPh, nullptr, // OOXTODO: not supported
+ XML_endOfListFormulaUpdate, nullptr); // OOXTODO: not supported
+ if( pOldData )
+ {
+ lcl_WriteCell( rRevisionLogStrm, XML_oc, aPosition, pOldData.get() );
+ if (!pNewData)
+ {
+ pStream->singleElement(XML_nc, XML_r, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aPosition));
+ }
+ }
+ if( pNewData )
+ {
+ lcl_WriteCell( rRevisionLogStrm, XML_nc, aPosition, pNewData.get() );
+ }
+ // OOXTODO: XML_odxf, XML_ndxf, XML_extLst elements
+ pStream->endElement( XML_rcc );
+}
+
+XclExpChTrInsert::XclExpChTrInsert( const XclExpChTrInsert& rCopy ) :
+ XclExpChTrAction(rCopy),
+ mbEndOfList(rCopy.mbEndOfList),
+ aRange(rCopy.aRange) {}
+
+XclExpChTrInsert::XclExpChTrInsert(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer ),
+ mbEndOfList(false),
+ aRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc()) )
+{
+ nLength = 0x00000030;
+ switch( rAction.GetType() )
+ {
+ case SC_CAT_INSERT_COLS: nOpCode = EXC_CHTR_OP_INSCOL; break;
+ case SC_CAT_INSERT_ROWS:
+ {
+ const ScChangeActionIns& rIns = static_cast<const ScChangeActionIns&>(rAction);
+ mbEndOfList = rIns.IsEndOfList();
+ nOpCode = EXC_CHTR_OP_INSROW;
+ }
+ break;
+ case SC_CAT_DELETE_COLS: nOpCode = EXC_CHTR_OP_DELCOL; break;
+ case SC_CAT_DELETE_ROWS: nOpCode = EXC_CHTR_OP_DELROW; break;
+ default:
+ OSL_FAIL( "XclExpChTrInsert::XclExpChTrInsert - unknown action" );
+ }
+
+ if( nOpCode & EXC_CHTR_OP_COLFLAG )
+ {
+ aRange.aStart.SetRow( 0 );
+ aRange.aEnd.SetRow( rRoot.GetXclMaxPos().Row() );
+ }
+ else
+ {
+ aRange.aStart.SetCol( 0 );
+ aRange.aEnd.SetCol( rRoot.GetXclMaxPos().Col() );
+ }
+
+ if( nOpCode & EXC_CHTR_OP_DELFLAG )
+ {
+ SetAddAction( new XclExpChTr0x014A( *this ) );
+ AddDependentContents( rAction, rRoot, rChangeTrack );
+ }
+}
+
+XclExpChTrInsert::~XclExpChTrInsert()
+{
+}
+
+bool XclExpChTrInsert::UsesDeletedTab() const
+{
+ return IsDeletedTab(aRange.aStart.Tab());
+}
+
+void XclExpChTrInsert::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aRange.aStart.Tab() );
+ sal_uInt16 nFlagVal = mbEndOfList ? 0x0001 : 0x0000;
+ rStrm << nFlagVal;
+ Write2DRange( rStrm, aRange );
+ rStrm << sal_uInt32(0x00000000);
+}
+
+void XclExpChTrInsert::PrepareSaveAction( XclExpStream& rStrm ) const
+{
+ if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) )
+ XclExpChTrEmpty( 0x0150 ).Save( rStrm );
+}
+
+void XclExpChTrInsert::CompleteSaveAction( XclExpStream& rStrm ) const
+{
+ if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) )
+ XclExpChTrEmpty( 0x0151 ).Save( rStrm );
+}
+
+sal_uInt16 XclExpChTrInsert::GetNum() const
+{
+ return 0x0137;
+}
+
+std::size_t XclExpChTrInsert::GetActionByteCount() const
+{
+ return 16;
+}
+
+static const char* lcl_GetAction( sal_uInt16 nOpCode )
+{
+ switch( nOpCode )
+ {
+ case EXC_CHTR_OP_INSCOL: return "insertCol";
+ case EXC_CHTR_OP_INSROW: return "insertRow";
+ case EXC_CHTR_OP_DELCOL: return "deleteCol";
+ case EXC_CHTR_OP_DELROW: return "deleteRow";
+ default: return "*unknown*";
+ }
+}
+
+void XclExpChTrInsert::SaveXml( XclExpXmlStream& rRevisionLogStrm )
+{
+ if (IsDeletedTab(aRange.aStart.Tab()))
+ {
+ // seen on attempt to export tdf66241-1.ods to xlsx
+ SAL_WARN("sc", "XclExpChTrCellContent: unable to export position with tab of EXC_TAB_DELETED");
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
+ pStream->startElement( XML_rrc,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sId, OString::number(GetTabId(aRange.aStart.Tab())),
+ XML_eol, ToPsz10(mbEndOfList),
+ XML_ref, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aRange),
+ XML_action, lcl_GetAction( nOpCode ),
+ XML_edge, nullptr); // OOXTODO: ???
+
+ // OOXTODO: does this handle XML_rfmt, XML_undo?
+ XclExpChTrAction* pAction = GetAddAction();
+ while( pAction != nullptr )
+ {
+ pAction->SaveXml( rRevisionLogStrm );
+ pAction = pAction->GetAddAction();
+ }
+ pStream->endElement( XML_rrc );
+}
+
+XclExpChTrInsertTab::XclExpChTrInsertTab(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_INSTAB ),
+ XclExpRoot( rRoot ),
+ nTab( static_cast<SCTAB>(rAction.GetBigRange().aStart.Tab()) )
+{
+ nLength = 0x0000021C;
+ bForceInfo = true;
+}
+
+XclExpChTrInsertTab::~XclExpChTrInsertTab()
+{
+}
+
+bool XclExpChTrInsertTab::UsesDeletedTab() const
+{
+ return IsDeletedTab(nTab);
+}
+
+void XclExpChTrInsertTab::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, nTab );
+ rStrm << sal_uInt32( 0 );
+ lcl_WriteFixedString( rStrm, XclExpString( GetTabInfo().GetScTabName( nTab ) ), 127 );
+ lcl_WriteDateTime( rStrm, GetDateTime() );
+ rStrm.WriteZeroBytes( 133 );
+}
+
+sal_uInt16 XclExpChTrInsertTab::GetNum() const
+{
+ return 0x014D;
+}
+
+std::size_t XclExpChTrInsertTab::GetActionByteCount() const
+{
+ return 276;
+}
+
+void XclExpChTrInsertTab::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+ pStream->singleElement( XML_ris,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sheetId, OString::number(GetTabId(nTab)),
+ XML_name, GetTabInfo().GetScTabName(nTab).toUtf8(),
+ XML_sheetPosition, OString::number(nTab) );
+}
+
+XclExpChTrMoveRange::XclExpChTrMoveRange(
+ const ScChangeActionMove& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_MOVE ),
+ aDestRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc() ) )
+{
+ nLength = 0x00000042;
+ aSourceRange = aDestRange;
+ sal_Int32 nDCols, nDRows, nDTabs;
+ rAction.GetDelta( nDCols, nDRows, nDTabs );
+ aSourceRange.aStart.IncRow( static_cast<SCROW>(-nDRows) );
+ aSourceRange.aStart.IncCol( static_cast<SCCOL>(-nDCols) );
+ aSourceRange.aStart.IncTab( static_cast<SCTAB>(-nDTabs) );
+ aSourceRange.aEnd.IncRow( static_cast<SCROW>(-nDRows) );
+ aSourceRange.aEnd.IncCol( static_cast<SCCOL>(-nDCols) );
+ aSourceRange.aEnd.IncTab( static_cast<SCTAB>(-nDTabs) );
+ AddDependentContents( rAction, rRoot, rChangeTrack );
+}
+
+XclExpChTrMoveRange::~XclExpChTrMoveRange()
+{
+}
+
+bool XclExpChTrMoveRange::UsesDeletedTab() const
+{
+ return IsDeletedTab(aDestRange.aStart.Tab()) ||
+ IsDeletedTab(aSourceRange.aStart.Tab());
+}
+
+void XclExpChTrMoveRange::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aDestRange.aStart.Tab() );
+ Write2DRange( rStrm, aSourceRange );
+ Write2DRange( rStrm, aDestRange );
+ WriteTabId( rStrm, aSourceRange.aStart.Tab() );
+ rStrm << sal_uInt32(0x00000000);
+}
+
+void XclExpChTrMoveRange::PrepareSaveAction( XclExpStream& rStrm ) const
+{
+ XclExpChTrEmpty( 0x014E ).Save( rStrm );
+}
+
+void XclExpChTrMoveRange::CompleteSaveAction( XclExpStream& rStrm ) const
+{
+ XclExpChTrEmpty( 0x014F ).Save( rStrm );
+}
+
+sal_uInt16 XclExpChTrMoveRange::GetNum() const
+{
+ return 0x0140;
+}
+
+std::size_t XclExpChTrMoveRange::GetActionByteCount() const
+{
+ return 24;
+}
+
+void XclExpChTrMoveRange::SaveXml( XclExpXmlStream& rRevisionLogStrm )
+{
+ if (IsDeletedTab(aDestRange.aStart.Tab()) || IsDeletedTab(aSourceRange.aStart.Tab()))
+ {
+ // seen on attempt to export tdf66241-1.ods to xlsx
+ SAL_WARN("sc", "XclExpChTrCellContent: unable to export position with tab of EXC_TAB_DELETED");
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
+
+ pStream->startElement( XML_rm,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sheetId, OString::number(GetTabId(aDestRange.aStart.Tab())),
+ XML_source, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aSourceRange),
+ XML_destination, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aDestRange),
+ XML_sourceSheetId, OString::number(GetTabId(aSourceRange.aStart.Tab())) );
+ // OOXTODO: does this handle XML_rfmt, XML_undo?
+ XclExpChTrAction* pAction = GetAddAction();
+ while( pAction != nullptr )
+ {
+ pAction->SaveXml( rRevisionLogStrm );
+ pAction = pAction->GetAddAction();
+ }
+ pStream->endElement( XML_rm );
+}
+
+XclExpChTr0x014A::XclExpChTr0x014A( const XclExpChTrInsert& rAction ) :
+ XclExpChTrInsert( rAction )
+{
+ nLength = 0x00000026;
+ nOpCode = EXC_CHTR_OP_FORMAT;
+}
+
+XclExpChTr0x014A::~XclExpChTr0x014A()
+{
+}
+
+void XclExpChTr0x014A::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aRange.aStart.Tab() );
+ rStrm << sal_uInt16(0x0003)
+ << sal_uInt16(0x0001);
+ Write2DRange( rStrm, aRange );
+}
+
+sal_uInt16 XclExpChTr0x014A::GetNum() const
+{
+ return 0x014A;
+}
+
+std::size_t XclExpChTr0x014A::GetActionByteCount() const
+{
+ return 14;
+}
+
+void XclExpChTr0x014A::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+
+ pStream->startElement( XML_rfmt,
+ XML_sheetId, OString::number(GetTabId(aRange.aStart.Tab())),
+ XML_xfDxf, nullptr, // OOXTODO: not supported
+ XML_s, nullptr, // OOXTODO: style
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange),
+ XML_start, nullptr, // OOXTODO: for string changes
+ XML_length, nullptr); // OOXTODO: for string changes
+ // OOXTODO: XML_dxf, XML_extLst
+
+ pStream->endElement( XML_rfmt );
+}
+
+std::size_t ExcXmlRecord::GetLen() const
+{
+ return 0;
+}
+
+sal_uInt16 ExcXmlRecord::GetNum() const
+{
+ return 0;
+}
+
+void ExcXmlRecord::Save( XclExpStream& )
+{
+ // Do nothing; ignored for BIFF output.
+}
+
+namespace {
+
+class EndXmlElement : public ExcXmlRecord
+{
+ sal_Int32 mnElement;
+public:
+ explicit EndXmlElement( sal_Int32 nElement ) : mnElement( nElement) {}
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+}
+
+void EndXmlElement::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+ pStream->write("</")->writeId(mnElement)->write(">");
+}
+
+XclExpChangeTrack::XclExpChangeTrack( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ pTabIdBuffer( nullptr )
+{
+ OSL_ENSURE( GetOldRoot().pTabId, "XclExpChangeTrack::XclExpChangeTrack - root data incomplete" );
+ if( !GetOldRoot().pTabId )
+ return;
+
+ ScChangeTrack* pTempChangeTrack = CreateTempChangeTrack();
+ if (!pTempChangeTrack)
+ return;
+
+ pTabIdBuffer = new XclExpChTrTabIdBuffer( GetTabInfo().GetXclTabCount() );
+ maBuffers.push_back( std::unique_ptr<XclExpChTrTabIdBuffer>(pTabIdBuffer) );
+
+ // calculate final table order (tab id list)
+ const ScChangeAction* pScAction;
+ for( pScAction = pTempChangeTrack->GetLast(); pScAction; pScAction = pScAction->GetPrev() )
+ {
+ if( pScAction->GetType() == SC_CAT_INSERT_TABS )
+ {
+ SCTAB nScTab = static_cast< SCTAB >( pScAction->GetBigRange().aStart.Tab() );
+ pTabIdBuffer->InitFill( GetTabInfo().GetXclTab( nScTab ) );
+ }
+ }
+ pTabIdBuffer->InitFillup();
+ GetOldRoot().pTabId->Copy( *pTabIdBuffer );
+
+ // get actions in reverse order
+ pScAction = pTempChangeTrack->GetLast();
+ while( pScAction )
+ {
+ PushActionRecord( *pScAction );
+ const ScChangeAction* pPrevAction = pScAction->GetPrev();
+ pScAction = pPrevAction;
+ }
+
+ // build record list
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ {
+ XclExpChTrHeader* pHeader = new XclExpChTrHeader; // header record for last GUID
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pHeader) );
+ maRecList.push_back( std::unique_ptr<ExcRecord>( new XclExpChTr0x0195 ) );
+ maRecList.push_back( std::unique_ptr<ExcRecord>( new XclExpChTr0x0194( *pTempChangeTrack ) ) );
+
+ OUString sLastUsername;
+ DateTime aLastDateTime( DateTime::EMPTY );
+ sal_uInt32 nIndex = 1;
+ sal_uInt8 aGUID[ 16 ]; // GUID for action info records
+ bool bValidGUID = false;
+ while( !aActionStack.empty() )
+ {
+ XclExpChTrAction* pAction = aActionStack.top();
+ aActionStack.pop();
+
+ if( (nIndex == 1) || pAction->ForceInfoRecord() ||
+ (pAction->GetUsername() != sLastUsername) ||
+ (pAction->GetDateTime() != aLastDateTime) )
+ {
+ lcl_GenerateGUID( aGUID, bValidGUID );
+ sLastUsername = pAction->GetUsername();
+ aLastDateTime = pAction->GetDateTime();
+
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new XclExpChTrInfo(sLastUsername, aLastDateTime, aGUID)) );
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new XclExpChTrTabId(pAction->GetTabIdBuffer())) );
+ pHeader->SetGUID( aGUID );
+ }
+ pAction->SetIndex( nIndex );
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pAction) );
+ }
+
+ pHeader->SetGUID( aGUID );
+ pHeader->SetCount( nIndex - 1 );
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new ExcEof) );
+ }
+ else
+ {
+ XclExpXmlChTrHeaders* pHeaders = new XclExpXmlChTrHeaders;
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pHeaders));
+
+ OUString sLastUsername;
+ DateTime aLastDateTime(DateTime::EMPTY);
+ sal_uInt32 nIndex = 1;
+ sal_Int32 nLogNumber = 1;
+ XclExpXmlChTrHeader* pCurHeader = nullptr;
+ sal_uInt8 aGUID[ 16 ]; // GUID for action info records
+ bool bValidGUID = false;
+
+ while (!aActionStack.empty())
+ {
+ XclExpChTrAction* pAction = aActionStack.top();
+ aActionStack.pop();
+
+ if( (nIndex == 1) || pAction->ForceInfoRecord() ||
+ (pAction->GetUsername() != sLastUsername) ||
+ (pAction->GetDateTime() != aLastDateTime) )
+ {
+ lcl_GenerateGUID( aGUID, bValidGUID );
+ sLastUsername = pAction->GetUsername();
+ aLastDateTime = pAction->GetDateTime();
+
+ pCurHeader = new XclExpXmlChTrHeader(sLastUsername, aLastDateTime, aGUID, nLogNumber, pAction->GetTabIdBuffer());
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pCurHeader));
+ nLogNumber++;
+ pHeaders->SetGUID(aGUID);
+ }
+ pAction->SetIndex(nIndex);
+ pCurHeader->AppendAction(std::unique_ptr<XclExpChTrAction>(pAction));
+ }
+
+ pHeaders->SetGUID(aGUID);
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new EndXmlElement(XML_headers)));
+ }
+}
+
+XclExpChangeTrack::~XclExpChangeTrack()
+{
+ while( !aActionStack.empty() )
+ {
+ delete aActionStack.top();
+ aActionStack.pop();
+ }
+}
+
+ScChangeTrack* XclExpChangeTrack::CreateTempChangeTrack()
+{
+ // get original change track
+ ScChangeTrack* pOrigChangeTrack = GetDoc().GetChangeTrack();
+ OSL_ENSURE( pOrigChangeTrack, "XclExpChangeTrack::CreateTempChangeTrack - no change track data" );
+ if( !pOrigChangeTrack )
+ return nullptr;
+
+ assert(!xTempDoc);
+ // create empty document
+ xTempDoc.reset(new ScDocument);
+
+ // adjust table count
+ SCTAB nOrigCount = GetDoc().GetTableCount();
+ OUString sTabName;
+ for( sal_Int32 nIndex = 0; nIndex < nOrigCount; nIndex++ )
+ {
+ xTempDoc->CreateValidTabName(sTabName);
+ xTempDoc->InsertTab(SC_TAB_APPEND, sTabName);
+ }
+ OSL_ENSURE(nOrigCount == xTempDoc->GetTableCount(),
+ "XclExpChangeTrack::CreateTempChangeTrack - table count mismatch");
+ if(nOrigCount != xTempDoc->GetTableCount())
+ return nullptr;
+
+ return pOrigChangeTrack->Clone(xTempDoc.get());
+}
+
+void XclExpChangeTrack::PushActionRecord( const ScChangeAction& rAction )
+{
+ XclExpChTrAction* pXclAction = nullptr;
+ ScChangeTrack* pTempChangeTrack = xTempDoc->GetChangeTrack();
+ switch( rAction.GetType() )
+ {
+ case SC_CAT_CONTENT:
+ pXclAction = new XclExpChTrCellContent( static_cast<const ScChangeActionContent&>(rAction), GetRoot(), *pTabIdBuffer );
+ break;
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_COLS:
+ if (pTempChangeTrack)
+ pXclAction = new XclExpChTrInsert( rAction, GetRoot(), *pTabIdBuffer, *pTempChangeTrack );
+ break;
+ case SC_CAT_INSERT_TABS:
+ {
+ pXclAction = new XclExpChTrInsertTab( rAction, GetRoot(), *pTabIdBuffer );
+ XclExpChTrTabIdBuffer* pNewBuffer = new XclExpChTrTabIdBuffer( *pTabIdBuffer );
+ pNewBuffer->Remove();
+ maBuffers.push_back( std::unique_ptr<XclExpChTrTabIdBuffer>(pNewBuffer) );
+ pTabIdBuffer = pNewBuffer;
+ }
+ break;
+ case SC_CAT_MOVE:
+ if (pTempChangeTrack)
+ pXclAction = new XclExpChTrMoveRange( static_cast<const ScChangeActionMove&>(rAction), GetRoot(), *pTabIdBuffer, *pTempChangeTrack );
+ break;
+ default:;
+ }
+ if( pXclAction )
+ aActionStack.push( pXclAction );
+}
+
+bool XclExpChangeTrack::WriteUserNamesStream()
+{
+ bool bRet = false;
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( EXC_STREAM_USERNAMES );
+ OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::WriteUserNamesStream - no stream" );
+ if( xSvStrm.is() )
+ {
+ XclExpStream aXclStrm( *xSvStrm, GetRoot() );
+ XclExpChTr0x0191().Save( aXclStrm );
+ XclExpChTr0x0198().Save( aXclStrm );
+ XclExpChTr0x0192().Save( aXclStrm );
+ XclExpChTr0x0197().Save( aXclStrm );
+ xSvStrm->Commit();
+ bRet = true;
+ }
+ return bRet;
+}
+
+void XclExpChangeTrack::Write()
+{
+ if (maRecList.empty())
+ return;
+
+ if( !WriteUserNamesStream() )
+ return;
+
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( EXC_STREAM_REVLOG );
+ OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::Write - no stream" );
+ if( xSvStrm.is() )
+ {
+ XclExpStream aXclStrm( *xSvStrm, GetRoot(), EXC_MAXRECSIZE_BIFF8 + 8 );
+
+ for(const auto& rxRec : maRecList)
+ rxRec->Save(aXclStrm);
+
+ xSvStrm->Commit();
+ }
+}
+
+static void lcl_WriteUserNamesXml( XclExpXmlStream& rWorkbookStrm )
+{
+ sax_fastparser::FSHelperPtr pUserNames = rWorkbookStrm.CreateOutputStream(
+ "xl/revisions/userNames.xml",
+ u"revisions/userNames.xml",
+ rWorkbookStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("usernames"));
+ pUserNames->startElement( XML_users,
+ XML_xmlns, rWorkbookStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS( XML_xmlns, XML_r ), rWorkbookStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ XML_count, "0" );
+ // OOXTODO: XML_userinfo elements for each user editing the file
+ // Doesn't seem to be supported by .xls output either (based on
+ // contents of XclExpChangeTrack::WriteUserNamesStream()).
+ pUserNames->endElement( XML_users );
+}
+
+void XclExpChangeTrack::WriteXml( XclExpXmlStream& rWorkbookStrm )
+{
+ if (maRecList.empty())
+ return;
+
+ lcl_WriteUserNamesXml( rWorkbookStrm );
+
+ sax_fastparser::FSHelperPtr pRevisionHeaders = rWorkbookStrm.CreateOutputStream(
+ "xl/revisions/revisionHeaders.xml",
+ u"revisions/revisionHeaders.xml",
+ rWorkbookStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
+ // OOXTODO: XML_userinfo elements for each user editing the file
+ // Doesn't seem to be supported by .xls output either (based on
+ // contents of XclExpChangeTrack::WriteUserNamesStream()).
+ rWorkbookStrm.PushStream( pRevisionHeaders );
+
+ for (const auto& rxRec : maRecList)
+ rxRec->SaveXml(rWorkbookStrm);
+
+ rWorkbookStrm.PopStream();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/XclImpChangeTrack.cxx b/sc/source/filter/xcl97/XclImpChangeTrack.cxx
new file mode 100644
index 0000000000..6bbd4c2e18
--- /dev/null
+++ b/sc/source/filter/xcl97/XclImpChangeTrack.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <XclImpChangeTrack.hxx>
+#include <sot/storage.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <chgviset.hxx>
+#include <formulacell.hxx>
+#include <chgtrack.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <externalrefmgr.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <excdefs.hxx>
+
+
+XclImpChangeTrack::XclImpChangeTrack( const XclImpRoot& rRoot, const XclImpStream& rBookStrm ) :
+ XclImpRoot( rRoot ),
+ aRecHeader(),
+ nTabIdCount( 0 ),
+ bGlobExit( false ),
+ eNestedMode( nmBase )
+{
+ // Verify that the User Names stream exists before going any further. Excel adds both
+ // "Revision Log" and "User Names" streams when Change Tracking is active but the Revision log
+ // remains if Change Tracking is turned off.
+ tools::SvRef<SotStorageStream> xUserStrm = OpenStream( EXC_STREAM_USERNAMES );
+ if( !xUserStrm.is() )
+ return;
+
+ xInStrm = OpenStream( EXC_STREAM_REVLOG );
+ if( !xInStrm.is() )
+ return;
+
+ sal_uInt64 const nStreamLen = xInStrm->TellEnd();
+ if( (xInStrm->GetErrorCode() == ERRCODE_NONE) && (nStreamLen != STREAM_SEEK_TO_END) )
+ {
+ xInStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ pStrm.reset( new XclImpStream( *xInStrm, GetRoot() ) );
+ pStrm->CopyDecrypterFrom( rBookStrm );
+ pChangeTrack.reset(new ScChangeTrack( GetDoc() ));
+
+ sOldUsername = pChangeTrack->GetUser();
+ pChangeTrack->SetUseFixDateTime( true );
+
+ ReadRecords();
+ }
+}
+
+XclImpChangeTrack::~XclImpChangeTrack()
+{
+ pChangeTrack.reset();
+ pStrm.reset();
+}
+
+void XclImpChangeTrack::DoAcceptRejectAction( ScChangeAction* pAction )
+{
+ if( !pAction ) return;
+ switch( aRecHeader.nAccept )
+ {
+ case EXC_CHTR_ACCEPT:
+ pChangeTrack->Accept( pAction );
+ break;
+ case EXC_CHTR_REJECT:
+ break;
+ }
+}
+
+void XclImpChangeTrack::DoAcceptRejectAction( sal_uInt32 nFirst, sal_uInt32 nLast )
+{
+ for( sal_uInt32 nIndex = nFirst; nIndex <= nLast; nIndex++ )
+ DoAcceptRejectAction( pChangeTrack->GetAction( nIndex ) );
+}
+
+void XclImpChangeTrack::DoInsertRange( const ScRange& rRange, bool bEndOfList )
+{
+ sal_uInt32 nFirst = pChangeTrack->GetActionMax() + 1;
+ pChangeTrack->AppendInsert(rRange, bEndOfList);
+ sal_uInt32 nLast = pChangeTrack->GetActionMax();
+ DoAcceptRejectAction( nFirst, nLast );
+}
+
+void XclImpChangeTrack::DoDeleteRange( const ScRange& rRange )
+{
+ sal_uLong nFirst, nLast;
+ pChangeTrack->AppendDeleteRange( rRange, &GetDoc() , nFirst, nLast );
+ DoAcceptRejectAction( nFirst, nLast );
+}
+
+SCTAB XclImpChangeTrack::ReadTabNum()
+{
+ return static_cast<SCTAB>(GetTabInfo().GetCurrentIndex(
+ pStrm->ReaduInt16(), nTabIdCount ));
+}
+
+void XclImpChangeTrack::ReadDateTime( DateTime& rDateTime )
+{
+ sal_uInt16 nYear;
+ sal_uInt8 nMonth, nDay, nHour, nMin, nSec;
+
+ nYear = pStrm->ReaduInt16();
+ nMonth = pStrm->ReaduInt8();
+ nDay = pStrm->ReaduInt8();
+ nHour = pStrm->ReaduInt8();
+ nMin = pStrm->ReaduInt8();
+ nSec = pStrm->ReaduInt8();
+
+ rDateTime.SetYear( nYear );
+ rDateTime.SetMonth( nMonth );
+ rDateTime.SetDay( nDay );
+ rDateTime.SetHour( nHour );
+ rDateTime.SetMin( nMin );
+ rDateTime.SetSec( nSec );
+ rDateTime.SetNanoSec( 0 );
+}
+
+bool XclImpChangeTrack::CheckRecord( sal_uInt16 nOpCode )
+{
+ if( (nOpCode != EXC_CHTR_OP_UNKNOWN) && (aRecHeader.nOpCode != nOpCode) )
+ {
+ OSL_FAIL( "XclImpChangeTrack::CheckRecord - unknown action" );
+ return false;
+ }
+ return aRecHeader.nIndex != 0;
+}
+
+void XclImpChangeTrack::Read3DTabRefInfo( SCTAB& rFirstTab, SCTAB& rLastTab, ExcelToSc8::ExternalTabInfo& rExtInfo )
+{
+ if( LookAtuInt8() == 0x01 )
+ {
+ rExtInfo.mbExternal = false;
+ // internal ref - read tab num and return sc tab num (position in TABID list)
+ pStrm->Ignore( 3 );
+ rFirstTab = static_cast< SCTAB >( GetTabInfo().GetCurrentIndex( pStrm->ReaduInt16(), nTabIdCount ) );
+ sal_uInt8 nFillByte = pStrm->ReaduInt8();
+ rLastTab = (nFillByte == 0x00) ?
+ static_cast< SCTAB >( GetTabInfo().GetCurrentIndex( pStrm->ReaduInt16(), nTabIdCount ) ) : rFirstTab;
+ }
+ else
+ {
+ // external ref - read doc and tab name and find sc tab num
+ // - URL
+ OUString aEncUrl( pStrm->ReadUniString() );
+ OUString aUrl;
+ bool bSelf;
+ XclImpUrlHelper::DecodeUrl( aUrl, bSelf, GetRoot(), aEncUrl );
+ pStrm->Ignore( 1 );
+ // - sheet name, always separated from URL
+ OUString aTabName( pStrm->ReadUniString() );
+ pStrm->Ignore( 1 );
+
+ rExtInfo.mbExternal = true;
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ pRefMgr->convertToAbsName(aUrl);
+ rExtInfo.mnFileId = pRefMgr->getExternalFileId(aUrl);
+ rExtInfo.maTabName = aTabName;
+ rFirstTab = rLastTab = 0;
+ }
+}
+
+void XclImpChangeTrack::ReadFormula( std::unique_ptr<ScTokenArray>& rpTokenArray, const ScAddress& rPosition )
+{
+ sal_uInt16 nFmlSize = pStrm->ReaduInt16();
+
+ // create a memory stream and copy the formula to be able to read simultaneously
+ // the formula and the additional 3D tab ref data following the formula
+ // here we have to simulate an Excel record to be able to use an XclImpStream...
+ // 2do: remove the stream member from formula converter and add it as a parameter
+ // to the Convert() routine (to prevent the construction/destruction of the
+ // converter in each formula)
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( 0x0001 ).WriteUInt16( nFmlSize );
+ size_t nRead = pStrm->CopyToStream( aMemStrm, nFmlSize );
+
+ // survive reading invalid streams!
+ // if we can't read as many bytes as required just don't use them and
+ // assume that this part is broken
+ if(nRead != nFmlSize)
+ {
+ rpTokenArray = nullptr;
+ pStrm->Ignore(1);
+ return;
+ }
+
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ XclImpChTrFmlConverter aFmlConv( GetRoot(), *this );
+
+ // read the formula, 3D tab refs from extended data
+ std::unique_ptr<ScTokenArray> pArray;
+ aFmlConv.Reset( rPosition );
+ bool bOK = (aFmlConv.Convert( pArray, aFmlaStrm, nFmlSize, false ) == ConvErr::OK); // JEG : Check This
+ rpTokenArray = (bOK && pArray) ? std::move( pArray ) : nullptr;
+ pStrm->Ignore( 1 );
+}
+
+void XclImpChangeTrack::ReadCell(
+ ScCellValue& rCell, sal_uInt32& rFormat, sal_uInt16 nFlags, const ScAddress& rPosition )
+{
+ rCell.clear();
+ rFormat = 0;
+ switch( nFlags & EXC_CHTR_TYPE_MASK )
+ {
+ case EXC_CHTR_TYPE_EMPTY:
+ break;
+ case EXC_CHTR_TYPE_RK:
+ {
+ double fValue = XclTools::GetDoubleFromRK( pStrm->ReadInt32() );
+ if( pStrm->IsValid() )
+ {
+ rCell.set(fValue);
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_DOUBLE:
+ {
+ double fValue = pStrm->ReadDouble();
+ if( pStrm->IsValid() )
+ {
+ rCell.set(fValue);
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ {
+ OUString sString = pStrm->ReadUniString();
+ if( pStrm->IsValid() )
+ {
+ rCell.set(GetDoc().GetSharedStringPool().intern(sString));
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_BOOL:
+ {
+ double fValue = static_cast<double>(pStrm->ReaduInt16() != 0);
+ if( pStrm->IsValid() )
+ {
+ rCell.set(fValue);
+ rFormat = GetFormatter().GetStandardFormat( SvNumFormatType::LOGICAL, ScGlobal::eLnge );
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_FORMULA:
+ {
+ std::unique_ptr<ScTokenArray> pTokenArray;
+ ReadFormula( pTokenArray, rPosition );
+ if( pStrm->IsValid() && pTokenArray )
+ {
+ rCell.set(new ScFormulaCell(GetDoc(), rPosition, std::move(pTokenArray)));
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpChangeTrack::ReadCell - unknown data type" );
+ }
+}
+
+void XclImpChangeTrack::ReadChTrInsert()
+{
+ *pStrm >> aRecHeader;
+ if( !CheckRecord( EXC_CHTR_OP_UNKNOWN ) )
+ return;
+
+ if( (aRecHeader.nOpCode != EXC_CHTR_OP_INSROW) &&
+ (aRecHeader.nOpCode != EXC_CHTR_OP_INSCOL) &&
+ (aRecHeader.nOpCode != EXC_CHTR_OP_DELROW) &&
+ (aRecHeader.nOpCode != EXC_CHTR_OP_DELCOL) )
+ {
+ OSL_FAIL( "XclImpChangeTrack::ReadChTrInsert - unknown action" );
+ return;
+ }
+
+ ScRange aRange;
+ aRange.aStart.SetTab( ReadTabNum() );
+ aRange.aEnd.SetTab( aRange.aStart.Tab() );
+ sal_uInt16 nFlags = pStrm->ReaduInt16();
+ bool bEndOfList = (nFlags & 0x0001); // row auto-inserted at the bottom.
+ Read2DRange( aRange );
+
+ if( aRecHeader.nOpCode & EXC_CHTR_OP_COLFLAG )
+ aRange.aEnd.SetRow( GetDocImport().getDoc().MaxRow() );
+ else
+ aRange.aEnd.SetCol( GetDocImport().getDoc().MaxCol() );
+
+ bool bValid = pStrm->IsValid();
+ if( FoundNestedMode() )
+ ReadNestedRecords();
+
+ if( bValid )
+ {
+ if( aRecHeader.nOpCode & EXC_CHTR_OP_DELFLAG )
+ DoDeleteRange( aRange );
+ else
+ DoInsertRange(aRange, bEndOfList);
+ }
+}
+
+void XclImpChangeTrack::ReadChTrInfo()
+{
+ pStrm->DisableDecryption();
+ pStrm->Ignore( 32 );
+ OUString sUsername( pStrm->ReadUniString() );
+ if( !pStrm->IsValid() ) return;
+
+ if( !sUsername.isEmpty() )
+ pChangeTrack->SetUser( sUsername );
+ pStrm->Seek( 148 );
+ if( !pStrm->IsValid() ) return;
+
+ DateTime aDateTime( DateTime::EMPTY );
+ ReadDateTime( aDateTime );
+ if( pStrm->IsValid() )
+ pChangeTrack->SetFixDateTimeLocal( aDateTime );
+}
+
+void XclImpChangeTrack::ReadChTrCellContent()
+{
+ *pStrm >> aRecHeader;
+ if( !CheckRecord( EXC_CHTR_OP_CELL ) )
+ return;
+
+ ScAddress aPosition;
+ SCTAB nTab = ReadTabNum();
+ aPosition.SetTab( nTab );
+ sal_uInt16 nValueType;
+ nValueType = pStrm->ReaduInt16();
+ sal_uInt16 nOldValueType = (nValueType >> 3) & EXC_CHTR_TYPE_MASK;
+ sal_uInt16 nNewValueType = nValueType & EXC_CHTR_TYPE_MASK;
+ pStrm->Ignore( 2 );
+ Read2DAddress( aPosition );
+ sal_uInt16 nOldSize;
+ nOldSize = pStrm->ReaduInt16();
+ SAL_WARN_IF( (nOldSize == 0) != (nOldValueType == EXC_CHTR_TYPE_EMPTY),
+ "sc.filter",
+ "XclImpChangeTrack::ReadChTrCellContent - old value mismatch" );
+ pStrm->Ignore( 4 );
+ switch( nValueType & EXC_CHTR_TYPE_FORMATMASK )
+ {
+ case 0x0000: break;
+ case 0x1100: pStrm->Ignore( 16 ); break;
+ case 0x1300: pStrm->Ignore( 8 ); break;
+ default: OSL_FAIL( "XclImpChangeTrack::ReadChTrCellContent - unknown format info" );
+ }
+
+ ScCellValue aOldCell;
+ ScCellValue aNewCell;
+ sal_uInt32 nOldFormat;
+ sal_uInt32 nNewFormat;
+ ReadCell(aOldCell, nOldFormat, nOldValueType, aPosition);
+ ReadCell(aNewCell, nNewFormat, nNewValueType, aPosition);
+ if( !pStrm->IsValid() || (pStrm->GetRecLeft() > 0) )
+ {
+ OSL_FAIL( "XclImpChangeTrack::ReadChTrCellContent - bytes left, action ignored" );
+ aOldCell.clear();
+ aNewCell.clear();
+ }
+ else
+ {
+ ScChangeActionContent* pNewAction =
+ pChangeTrack->AppendContentOnTheFly(aPosition, aOldCell, aNewCell, nOldFormat, nNewFormat);
+ DoAcceptRejectAction( pNewAction );
+ }
+}
+
+void XclImpChangeTrack::ReadChTrTabId()
+{
+ if( nTabIdCount == 0 ) // read only 1st time, otherwise calculated by <ReadChTrInsertTab()>
+ nTabIdCount = static_cast< sal_uInt16 >( pStrm->GetRecLeft() >> 1 );
+}
+
+void XclImpChangeTrack::ReadChTrMoveRange()
+{
+ *pStrm >> aRecHeader;
+ if( !CheckRecord( EXC_CHTR_OP_MOVE ) )
+ return;
+
+ ScRange aSourceRange;
+ ScRange aDestRange;
+ aDestRange.aStart.SetTab( ReadTabNum() );
+ aDestRange.aEnd.SetTab( aDestRange.aStart.Tab() );
+ Read2DRange( aSourceRange );
+ Read2DRange( aDestRange );
+ aSourceRange.aStart.SetTab( ReadTabNum() );
+ aSourceRange.aEnd.SetTab( aSourceRange.aStart.Tab() );
+
+ bool bValid = pStrm->IsValid();
+ if( FoundNestedMode() )
+ ReadNestedRecords();
+
+ if( bValid )
+ {
+ pChangeTrack->AppendMove( aSourceRange, aDestRange, nullptr );
+ DoAcceptRejectAction( pChangeTrack->GetLast() );
+ }
+}
+
+void XclImpChangeTrack::ReadChTrInsertTab()
+{
+ *pStrm >> aRecHeader;
+ if( CheckRecord( EXC_CHTR_OP_INSTAB ) )
+ {
+ SCTAB nTab = ReadTabNum();
+ if( pStrm->IsValid() )
+ {
+ nTabIdCount++;
+ DoInsertRange(ScRange(0, 0, nTab, GetDocImport().getDoc().MaxCol(), GetDocImport().getDoc().MaxRow(), nTab), false);
+ }
+ }
+}
+
+void XclImpChangeTrack::InitNestedMode()
+{
+ OSL_ENSURE( eNestedMode == nmBase, "XclImpChangeTrack::InitNestedMode - unexpected nested mode" );
+ if( eNestedMode == nmBase )
+ eNestedMode = nmFound;
+}
+
+void XclImpChangeTrack::ReadNestedRecords()
+{
+ OSL_ENSURE( eNestedMode == nmFound, "XclImpChangeTrack::StartNestedMode - missing nested mode" );
+ if( eNestedMode == nmFound )
+ {
+ eNestedMode = nmNested;
+ ReadRecords();
+ }
+}
+
+bool XclImpChangeTrack::EndNestedMode()
+{
+ OSL_ENSURE( eNestedMode != nmBase, "XclImpChangeTrack::EndNestedMode - missing nested mode" );
+ bool bReturn = (eNestedMode == nmNested);
+ eNestedMode = nmBase;
+ return bReturn;
+}
+
+void XclImpChangeTrack::ReadRecords()
+{
+ bool bExitLoop = false;
+
+ while( !bExitLoop && !bGlobExit && pStrm->StartNextRecord() )
+ {
+ switch( pStrm->GetRecId() )
+ {
+ case 0x000A: bGlobExit = true; break;
+ case 0x0137: ReadChTrInsert(); break;
+ case 0x0138: ReadChTrInfo(); break;
+ case 0x013B: ReadChTrCellContent(); break;
+ case 0x013D: ReadChTrTabId(); break;
+ case 0x0140: ReadChTrMoveRange(); break;
+ case 0x014D: ReadChTrInsertTab(); break;
+ case 0x014E:
+ case 0x0150: InitNestedMode(); break;
+ case 0x014F:
+ case 0x0151: bExitLoop = EndNestedMode(); break;
+ }
+ }
+}
+
+void XclImpChangeTrack::Apply()
+{
+ if( pChangeTrack )
+ {
+ pChangeTrack->SetUser( sOldUsername );
+ pChangeTrack->SetUseFixDateTime( false );
+
+ GetDoc().SetChangeTrack( std::move(pChangeTrack) );
+
+ ScChangeViewSettings aSettings;
+ aSettings.SetShowChanges( true );
+ GetDoc().SetChangeViewSettings( aSettings );
+ }
+}
+
+
+XclImpChTrFmlConverter::XclImpChTrFmlConverter(
+ XclImpRoot& rRoot, XclImpChangeTrack& rXclChTr ) :
+ ExcelToSc8( rRoot ),
+ rChangeTrack( rXclChTr ) {}
+
+XclImpChTrFmlConverter::~XclImpChTrFmlConverter()
+{
+}
+
+// virtual, called from ExcToSc8::Convert()
+bool XclImpChTrFmlConverter::Read3DTabReference( sal_uInt16 /*nIxti*/, SCTAB& rFirstTab, SCTAB& rLastTab,
+ ExternalTabInfo& rExtInfo )
+{
+ rChangeTrack.Read3DTabRefInfo( rFirstTab, rLastTab, rExtInfo );
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/xcl97esc.cxx b/sc/source/filter/xcl97/xcl97esc.cxx
new file mode 100644
index 0000000000..43538de6c8
--- /dev/null
+++ b/sc/source/filter/xcl97/xcl97esc.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/embed/XClassifiedObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svx/sdasitm.hxx>
+#include <sfx2/docfile.hxx>
+#include <sal/log.hxx>
+
+#include <sot/exchange.hxx>
+#include <sot/storage.hxx>
+#include <xeescher.hxx>
+
+#include <drwlayer.hxx>
+#include <xecontent.hxx>
+#include <editeng/flditem.hxx>
+#include <userdat.hxx>
+#include <xcl97rec.hxx>
+#include <xcl97esc.hxx>
+#include <unotools/streamwrap.hxx>
+#include <oox/ole/olehelper.hxx>
+#include <sfx2/objsh.hxx>
+#include <docsh.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+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::container::XIndexAccess;
+using ::com::sun::star::embed::XClassifiedObject;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::form::XFormsSupplier;
+using ::com::sun::star::io::XOutputStream;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::script::XEventAttacherManager;
+
+XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot& rRoot )
+ : XclExpRoot(rRoot)
+ , mpPicStrm(nullptr)
+{
+ SetBaseURI( GetMedium().GetBaseURL( true ) );
+}
+
+SvStream* XclEscherExGlobal::ImplQueryPictureStream()
+{
+ moPicTempFile.emplace();
+ mpPicStrm = moPicTempFile->GetStream( StreamMode::READWRITE );
+ mpPicStrm->SetEndian( SvStreamEndian::LITTLE );
+ return mpPicStrm;
+}
+
+XclEscherEx::XclEscherEx( const XclExpRoot& rRoot, XclExpObjectManager& rObjMgr, SvStream& rStrm, const XclEscherEx* pParent ) :
+ EscherEx( pParent ? pParent->mxGlobal : std::make_shared<XclEscherExGlobal>( rRoot ), &rStrm ),
+ XclExpRoot( rRoot ),
+ mrObjMgr( rObjMgr ),
+ pCurrXclObj( nullptr ),
+ pTheClientData( new XclEscherClientData ),
+ pAdditionalText( nullptr ),
+ nAdditionalText( 0 ),
+ mnNextKey( 0 ),
+ mbIsRootDff( pParent == nullptr )
+{
+ InsertPersistOffset( mnNextKey, 0 );
+}
+
+XclEscherEx::~XclEscherEx()
+{
+ OSL_ENSURE( aStack.empty(), "~XclEscherEx: stack not empty" );
+ DeleteCurrAppData();
+ pTheClientData.reset();
+}
+
+sal_uInt32 XclEscherEx::InitNextDffFragment()
+{
+ /* Current value of mnNextKey will be used by caller to refer to the
+ starting point of the DFF fragment. The key exists already in the
+ PersistTable (has been inserted by c'tor of previous call of
+ InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
+ sal_uInt32 nPersistKey = mnNextKey;
+
+ /* Prepare the next key that is used by caller as end point of the DFF
+ fragment. Will be updated by caller when writing to the DFF stream,
+ using the UpdateDffFragmentEnd() function. This is needed to find DFF
+ data written by the SVX base class implementation without interaction,
+ e.g. the solver container that will be written after the last shape. */
+ ++mnNextKey;
+ InsertPersistOffset( mnNextKey, mpOutStrm->Tell() );
+
+ return nPersistKey;
+}
+
+void XclEscherEx::UpdateDffFragmentEnd()
+{
+ // update existing fragment key with new stream position
+ ReplacePersistOffset( mnNextKey, mpOutStrm->Tell() );
+}
+
+sal_uInt32 XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey )
+{
+ /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
+ is non-const due to tools/List usage. */
+ return GetPersistOffset( nFragmentKey );
+}
+
+sal_uInt32 XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey )
+{
+ /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
+ is non-const due to tools/List usage. */
+ return GetDffFragmentPos( nFragmentKey + 1 ) - GetDffFragmentPos( nFragmentKey );
+}
+
+bool XclEscherEx::HasPendingDffData()
+{
+ /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
+ is non-const due to tools/List usage. */
+ return GetDffFragmentPos( mnNextKey ) < GetStreamPos();
+}
+
+XclExpDffAnchorBase* XclEscherEx::CreateDffAnchor( const SdrObject& rSdrObj ) const
+{
+ // the object manager creates the correct anchor type according to context
+ XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
+ // pass the drawing object, that will calculate the anchor position
+ pAnchor->SetSdrObject( rSdrObj );
+ return pAnchor;
+}
+
+namespace {
+
+bool lcl_IsFontwork( const SdrObject* pObj )
+{
+ bool bIsFontwork = false;
+ if( pObj->GetObjIdentifier() == SdrObjKind::CustomShape )
+ {
+ static constexpr OUString aTextPath = u"TextPath"_ustr;
+ const SdrCustomShapeGeometryItem& rGeometryItem =
+ pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ if( const Any* pAny = rGeometryItem.GetPropertyValueByName( aTextPath, aTextPath ) )
+ *pAny >>= bIsFontwork;
+ }
+ return bIsFontwork;
+}
+
+} // namespace
+
+EscherExHostAppData* XclEscherEx::StartShape( const Reference< XShape >& rxShape, const tools::Rectangle* pChildAnchor )
+{
+ if ( nAdditionalText )
+ nAdditionalText++;
+ bool bInGroup = ( pCurrXclObj != nullptr );
+ if ( bInGroup )
+ { // stacked recursive group object
+ if ( !pCurrAppData->IsStackedGroup() )
+ { //! UpdateDffFragmentEnd only once
+ pCurrAppData->SetStackedGroup( true );
+ UpdateDffFragmentEnd();
+ }
+ }
+ aStack.push( std::make_pair( pCurrXclObj, std::move(pCurrAppData) ) );
+ pCurrAppData.reset( new XclEscherHostAppData );
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rxShape);
+ //added for exporting OCX control
+ sal_Int16 nMsCtlType = 0;
+ if ( !pObj )
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() ); // just what is it?!?
+ else
+ {
+ pCurrXclObj = nullptr;
+ SdrObjKind nObjType = pObj->GetObjIdentifier();
+
+ if( nObjType == SdrObjKind::OLE2 )
+ {
+ // no OLE objects in embedded drawings (chart shapes)
+ if( mbIsRootDff )
+ {
+ //! not-const because GetObjRef may load the OLE object
+ Reference < XClassifiedObject > xObj( static_cast<SdrOle2Obj*>(pObj)->GetObjRef() );
+ if ( xObj.is() )
+ {
+ SvGlobalName aObjClsId( xObj->getClassID() );
+ if ( SotExchange::IsChart( aObjClsId ) )
+ { // yes, it's a chart diagram
+ mrObjMgr.AddObj( std::make_unique<XclExpChartObj>( mrObjMgr, rxShape, pChildAnchor, &GetDoc() ) );
+ pCurrXclObj = nullptr; // no metafile or whatsoever
+ }
+ else // metafile and OLE object
+ pCurrXclObj = new XclObjOle( mrObjMgr, *static_cast<SdrOle2Obj*>(pObj) );
+ }
+ else // just a metafile
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );
+ }
+ else
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );
+ }
+ else if( nObjType == SdrObjKind::UNO )
+ {
+ //added for exporting OCX control
+ Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
+ Any aAny;
+ try
+ {
+ aAny = xPropSet->getPropertyValue("ControlTypeinMSO");
+ aAny >>= nMsCtlType;
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
+ }
+ if( nMsCtlType == 2 ) //OCX Form Control
+ pCurrXclObj = CreateOCXCtrlObj( rxShape, pChildAnchor ).release();
+ else //TBX Form Control
+ pCurrXclObj = CreateTBXCtrlObj( rxShape, pChildAnchor ).release();
+ if( !pCurrXclObj )
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() ); // just a metafile
+ }
+ else if( !ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ // ignore permanent note shapes
+ // #i12190# do not ignore callouts (do not filter by object type ID)
+ pCurrXclObj = ShapeInteractionHelper::CreateShapeObj( mrObjMgr, rxShape, &GetDoc() );
+ ShapeInteractionHelper::PopulateShapeInteractionInfo( mrObjMgr, rxShape, *pCurrAppData );
+ }
+ }
+ if ( pCurrXclObj )
+ {
+ if ( !mrObjMgr.AddObj( std::unique_ptr<XclObj>(pCurrXclObj) ) )
+ { // maximum count reached, object got deleted
+ pCurrXclObj = nullptr;
+ }
+ else
+ {
+ pCurrAppData->SetClientData( pTheClientData.get() );
+ if ( nAdditionalText == 0 )
+ {
+ if ( pObj )
+ {
+ if ( !bInGroup )
+ {
+ /* Create a dummy anchor carrying the flags. Real
+ coordinates are calculated later in virtual call of
+ WriteData(EscherEx&,const Rectangle&). */
+ XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
+ pAnchor->SetFlags( *pObj );
+ pCurrAppData->SetClientAnchor( pAnchor );
+ }
+ const SdrTextObj* pTextObj = DynCastSdrTextObj( pObj );
+ if( pTextObj && !lcl_IsFontwork( pTextObj ) && (pObj->GetObjIdentifier() != SdrObjKind::Caption) )
+ {
+ const OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
+ if( pParaObj )
+ pCurrAppData->SetClientTextbox(
+ new XclEscherClientTextbox( GetRoot(), *pTextObj, pCurrXclObj ) );
+ }
+ }
+ else
+ {
+ if ( !bInGroup )
+ pCurrAppData->SetClientAnchor( mrObjMgr.CreateDffAnchor() );
+ }
+ }
+ else if ( nAdditionalText == 3 )
+ {
+ if ( pAdditionalText )
+ {
+ pAdditionalText->SetXclObj( pCurrXclObj );
+ pCurrAppData->SetClientTextbox( pAdditionalText );
+ }
+ }
+ }
+ }
+ if(pObj)
+ {
+ //add for exporting OCX control
+ //for OCX control import from MS office file,we need keep the id value as MS office file.
+ //GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as obj id rule;
+ //but we trick it here.
+ SdrObjKind nObjType = pObj->GetObjIdentifier();
+ if( nObjType == SdrObjKind::UNO && pCurrXclObj )
+ {
+ Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
+ Any aAny;
+ try
+ {
+ aAny = xPropSet->getPropertyValue("ObjIDinMSO");
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
+ }
+ sal_uInt16 nObjIDinMSO = 0xFFFF;
+ aAny >>= nObjIDinMSO;
+ if( nObjIDinMSO != 0xFFFF && nMsCtlType == 2) //OCX
+ {
+ pCurrXclObj->SetId(nObjIDinMSO);
+ }
+ }
+ }
+ if ( !pCurrXclObj )
+ pCurrAppData->SetDontWriteShape( true );
+ return pCurrAppData.get();
+}
+
+void XclEscherEx::EndShape( sal_uInt16 nShapeType, sal_uInt32 nShapeID )
+{
+ // own escher data created? -> never delete such objects
+ bool bOwnEscher = pCurrXclObj && pCurrXclObj->IsOwnEscher();
+
+ // post process the current object - not for objects with own escher data
+ if( pCurrXclObj && !bOwnEscher )
+ {
+ // escher data of last shape not written? -> delete it from object list
+ if( nShapeID == 0 )
+ {
+ std::unique_ptr<XclObj> pLastObj = mrObjMgr.RemoveLastObj();
+ OSL_ENSURE( pLastObj.get() == pCurrXclObj, "XclEscherEx::EndShape - wrong object" );
+ pCurrXclObj = nullptr;
+ }
+
+ if( pCurrXclObj )
+ {
+ // set shape type
+ if ( pCurrAppData->IsStackedGroup() )
+ pCurrXclObj->SetEscherShapeTypeGroup();
+ else
+ {
+ pCurrXclObj->SetEscherShapeType( nShapeType );
+ UpdateDffFragmentEnd();
+ }
+ }
+ }
+
+ // get next object from stack
+ DeleteCurrAppData();
+ if (aStack.empty())
+ {
+ pCurrXclObj = nullptr;
+ pCurrAppData = nullptr;
+ }
+ else
+ {
+ pCurrXclObj = aStack.top().first;
+ pCurrAppData = std::move(aStack.top().second);
+ aStack.pop();
+ }
+ if( nAdditionalText == 3 )
+ nAdditionalText = 0;
+}
+
+EscherExHostAppData* XclEscherEx::EnterAdditionalTextGroup()
+{
+ nAdditionalText = 1;
+ pAdditionalText = static_cast<XclEscherClientTextbox*>( pCurrAppData->GetClientTextbox() );
+ pCurrAppData->SetClientTextbox( nullptr );
+ return pCurrAppData.get();
+}
+
+void XclEscherEx::EndDocument()
+{
+ if( mbIsRootDff )
+ Flush( static_cast< XclEscherExGlobal& >( *mxGlobal ).GetPictureStream() );
+
+ // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
+ mpOutStrm->Seek( 0 );
+}
+
+std::unique_ptr<XclExpOcxControlObj> XclEscherEx::CreateOCXCtrlObj( Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor )
+{
+ ::std::unique_ptr< XclExpOcxControlObj > xOcxCtrl;
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( xCtrlModel.is() )
+ {
+ // output stream
+ if( !mxCtlsStrm.is() )
+ mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
+ if( mxCtlsStrm.is() )
+ {
+ OUString aClassName;
+ sal_uInt32 nStrmStart = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() );
+
+ // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
+ Reference< XOutputStream > xOut( new utl::OSeekableOutputStreamWrapper( *mxCtlsStrm ) );
+ Reference< css::frame::XModel > xModel( GetDocShell() ? GetDocShell()->GetModel() : nullptr );
+ if( xModel.is() && xOut.is() && oox::ole::MSConvertOCXControls::WriteOCXExcelKludgeStream( xModel, xOut, xCtrlModel, xShape->getSize(), aClassName ) )
+ {
+ sal_uInt32 nStrmSize = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() - nStrmStart );
+ // adjust the class name to "Forms.***.1"
+ aClassName = "Forms." + aClassName + ".1";
+ xOcxCtrl.reset( new XclExpOcxControlObj( mrObjMgr, xShape, pChildAnchor, aClassName, nStrmStart, nStrmSize ) );
+ }
+ }
+ }
+ return xOcxCtrl;
+}
+
+std::unique_ptr<XclExpTbxControlObj> XclEscherEx::CreateTBXCtrlObj( Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor )
+{
+ ::std::unique_ptr< XclExpTbxControlObj > xTbxCtrl( new XclExpTbxControlObj( mrObjMgr, xShape, pChildAnchor ) );
+ if( xTbxCtrl->GetObjType() == EXC_OBJTYPE_UNKNOWN )
+ xTbxCtrl.reset();
+ else
+ {
+ // find attached macro
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ ConvertTbxMacro( *xTbxCtrl, xCtrlModel );
+ }
+ return xTbxCtrl;
+}
+
+void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj& rTbxCtrlObj, Reference< XControlModel > const & xCtrlModel )
+{
+ SdrPage* pSdrPage = GetSdrPage( GetCurrScTab() );
+ if( !(xCtrlModel.is() && GetDocShell() && pSdrPage) )
+ return;
+
+ try
+ {
+ Reference< XFormsSupplier > xFormsSupplier( pSdrPage->getUnoPage(), UNO_QUERY_THROW );
+ Reference< XIndexAccess > xFormsIA( xFormsSupplier->getForms(), UNO_QUERY_THROW );
+
+ // 1) try to find the index of the processed control in the form
+
+ Reference< XIndexAccess > xFormIA; // needed in step 2) below
+ sal_Int32 nFoundIdx = -1;
+
+ // search all existing forms in the draw page
+ for( sal_Int32 nFormIdx = 0, nFormCount = xFormsIA->getCount();
+ (nFoundIdx < 0) && (nFormIdx < nFormCount); ++nFormIdx )
+ {
+ // get the XIndexAccess interface of the form with index nFormIdx
+ if( xFormIA.set( xFormsIA->getByIndex( nFormIdx ), UNO_QUERY ) )
+ {
+ // search all elements (controls) of the current form by index
+ for( sal_Int32 nCtrlIdx = 0, nCtrlCount = xFormIA->getCount();
+ (nFoundIdx < 0) && (nCtrlIdx < nCtrlCount); ++nCtrlIdx )
+ {
+ // compare implementation pointers of the control models
+ Reference< XControlModel > xCurrModel( xFormIA->getByIndex( nCtrlIdx ), UNO_QUERY );
+ if( xCtrlModel.get() == xCurrModel.get() )
+ nFoundIdx = nCtrlIdx;
+ }
+ }
+ }
+
+ // 2) try to find an attached macro
+
+ if( xFormIA.is() && (nFoundIdx >= 0) )
+ {
+ Reference< XEventAttacherManager > xEventMgr( xFormIA, UNO_QUERY_THROW );
+ // loop over all events attached to the found control
+ const Sequence< ScriptEventDescriptor > aEventSeq( xEventMgr->getScriptEvents( nFoundIdx ) );
+ for( const auto& rEvent : aEventSeq )
+ {
+ // try to set the event data at the Excel control object, returns true on success
+ if (rTbxCtrlObj.SetMacroLink( rEvent ))
+ break;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void XclEscherEx::DeleteCurrAppData()
+{
+ if ( pCurrAppData )
+ {
+ delete pCurrAppData->GetClientAnchor();
+ delete pCurrAppData->GetClientTextbox();
+ delete pCurrAppData->GetInteractionInfo();
+ pCurrAppData.reset();
+ }
+}
+
+// --- class XclEscherClientData -------------------------------------
+
+void XclEscherClientData::WriteData( EscherEx& rEx ) const
+{ // actual data is in the following OBJ record
+ rEx.AddAtom( 0, ESCHER_ClientData );
+}
+
+// --- class XclEscherClientTextbox -------------------------------------
+
+XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot& rRoot,
+ const SdrTextObj& rObj, XclObj* pObj )
+ :
+ XclExpRoot( rRoot ),
+ rTextObj( rObj ),
+ pXclObj( pObj )
+{
+}
+
+void XclEscherClientTextbox::WriteData( EscherEx& /*rEx*/ ) const
+{
+ pXclObj->SetText( GetRoot(), rTextObj );
+}
+
+XclExpShapeObj*
+ShapeInteractionHelper::CreateShapeObj( XclExpObjectManager& rObjMgr, const Reference< XShape >& xShape, ScDocument* pDoc )
+{
+ return new XclExpShapeObj( rObjMgr, xShape, pDoc );
+}
+
+void ShapeInteractionHelper::PopulateShapeInteractionInfo(const XclExpObjectManager& rObjMgr,
+ const Reference<XShape>& xShape,
+ EscherExHostAppData& rHostAppData)
+{
+ try
+ {
+ SvMemoryStream* pMemStrm = nullptr;
+ OUString sHyperLink;
+ OUString sMacro;
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
+ if (pObj)
+ sHyperLink = pObj->getHyperlink();
+ if (ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo(pObj))
+ {
+ sMacro = pInfo->GetMacro();
+ }
+ if (!sHyperLink.isEmpty())
+ {
+ pMemStrm = new SvMemoryStream();
+ XclExpStream tmpStream(*pMemStrm, rObjMgr.GetRoot());
+ ScAddress dummyAddress;
+ SvxURLField aUrlField;
+ aUrlField.SetURL(sHyperLink);
+ XclExpHyperlink hExpHlink(rObjMgr.GetRoot(), aUrlField, dummyAddress);
+ hExpHlink.WriteEmbeddedData(tmpStream);
+ }
+ if (!sHyperLink.isEmpty() || !sMacro.isEmpty())
+ rHostAppData.SetInteractionInfo(new InteractionInfo(pMemStrm));
+ }
+ catch (Exception&)
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/xcl97rec.cxx b/sc/source/filter/xcl97/xcl97rec.cxx
new file mode 100644
index 0000000000..e9ae7bdeba
--- /dev/null
+++ b/sc/source/filter/xcl97/xcl97rec.cxx
@@ -0,0 +1,2028 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/sprintf.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/editobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <sot/storage.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/unoapi.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <tools/urlobj.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+#include <drwlayer.hxx>
+
+#include <root.hxx>
+#include <utility>
+#include <xcl97rec.hxx>
+#include <xcl97esc.hxx>
+#include <xeescher.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xlcontent.hxx>
+
+#include <unotools/fltrcfg.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <filter/msfilter/msoleexp.hxx>
+
+#include <unotools/localedatawrapper.hxx>
+
+#include <stdio.h>
+
+#include <document.hxx>
+#include <rangelst.hxx>
+#include <docoptio.hxx>
+#include <tabprotection.hxx>
+
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+
+#include <sax/fastattribs.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/shapes.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <detfunc.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::drawing::XShape;
+using ::oox::drawingml::ShapeExport;
+using ::oox::vml::VMLExport;
+using namespace oox;
+
+XclExpObjList::XclExpObjList( const XclExpRoot& rRoot, XclEscherEx& rEscherEx ) :
+ XclExpRoot( rRoot ),
+ mnScTab( rRoot.GetCurrScTab() ),
+ mrEscherEx( rEscherEx )
+{
+ pMsodrawingPerSheet.reset( new XclExpMsoDrawing( rEscherEx ) );
+ // open the DGCONTAINER and the patriarch group shape
+ mrEscherEx.OpenContainer( ESCHER_DgContainer );
+ tools::Rectangle aRect( 0, 0, 0, 0 );
+ mrEscherEx.EnterGroup( &aRect );
+ mrEscherEx.UpdateDffFragmentEnd();
+}
+
+XclExpObjList::~XclExpObjList()
+{
+ maObjs.clear();
+ pMsodrawingPerSheet.reset();
+ pSolverContainer.reset();
+}
+
+sal_uInt16 XclExpObjList::Add( std::unique_ptr<XclObj> pObj )
+{
+ OSL_ENSURE( maObjs.size() < 0xFFFF, "XclExpObjList::Add: too much for Xcl" );
+
+ size_t nSize = maObjs.size();
+
+ if ( nSize < 0xFFFF )
+ {
+ pObj->SetId( nSize+1 );
+ pObj->SetTab( mnScTab );
+ maObjs.push_back(std::move(pObj));
+ ++nSize;
+ }
+ else
+ {
+ nSize = 0;
+ }
+
+ return nSize;
+}
+
+std::unique_ptr<XclObj> XclExpObjList::pop_back ()
+{
+ auto ret = std::move(maObjs.back());
+ maObjs.pop_back();
+ return ret;
+}
+
+void XclExpObjList::EndSheet()
+{
+ // Is there still something in the stream? -> The solver container
+ if( mrEscherEx.HasPendingDffData() )
+ pSolverContainer.reset( new XclExpMsoDrawing( mrEscherEx ) );
+
+ // close the DGCONTAINER created by XclExpObjList ctor MSODRAWING
+ mrEscherEx.CloseContainer();
+}
+
+void XclExpObjList::Save( XclExpStream& rStrm )
+{
+ //! Escher must be written, even if there are no objects
+ pMsodrawingPerSheet->Save( rStrm );
+
+ for ( const auto& rxObj : maObjs )
+ rxObj->Save( rStrm );
+
+ if( pSolverContainer )
+ pSolverContainer->Save( rStrm );
+}
+
+namespace {
+
+bool IsFormControlObject( const XclObj *rObj )
+{
+ switch( rObj->GetObjType() )
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ case EXC_OBJTYPE_BUTTON:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsVmlObject( const XclObj *rObj )
+{
+ switch( rObj->GetObjType() )
+ {
+ case EXC_OBJTYPE_NOTE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+sal_Int32 GetVmlObjectCount( XclExpObjList& rList )
+{
+ return static_cast<sal_Int32>(std::count_if(rList.begin(), rList.end(),
+ [](const std::unique_ptr<XclObj>& rxObj) { return IsVmlObject( rxObj.get() ) || IsFormControlObject( rxObj.get() ); }));
+}
+
+bool IsValidObject( const XclObj& rObj )
+{
+ if (rObj.GetObjType() == EXC_OBJTYPE_CHART)
+ {
+ // Chart object. Make sure it's a valid chart object. We skip
+ // invalid chart objects from exporting to prevent Excel from
+ // complaining on load.
+
+ const XclExpChartObj& rChartObj = static_cast<const XclExpChartObj&>(rObj);
+ uno::Reference<chart2::XChartDocument> xChartDoc(rChartObj.GetChartDoc(), uno::UNO_QUERY);
+ if (!xChartDoc.is())
+ return false;
+
+ uno::Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram();
+ if (!xDiagram.is())
+ return false;
+
+ uno::Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(xDiagram, uno::UNO_QUERY);
+ if (!xCooSysContainer.is())
+ return false;
+
+ const uno::Sequence<uno::Reference<chart2::XCoordinateSystem>> xCooSysSeq = xCooSysContainer->getCoordinateSystems();
+ for (const auto& rCooSys : xCooSysSeq)
+ {
+ Reference<chart2::XChartTypeContainer> xChartTypeCont(rCooSys, uno::UNO_QUERY);
+ if (!xChartTypeCont.is())
+ return false;
+
+ uno::Sequence<uno::Reference<chart2::XChartType>> xChartTypeSeq = xChartTypeCont->getChartTypes();
+ if (!xChartTypeSeq.hasElements())
+ // No chart type. Not good.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void SaveDrawingMLObjects( XclExpObjList& rList, XclExpXmlStream& rStrm )
+{
+ std::vector<XclObj*> aList;
+ // do not add objects to the list that are in the group,
+ // because the group already contains them. For this, count
+ // the next skipped objects, i.e. objects of a group,
+ // including objects of its subgroups
+ size_t nSkipObj = 0;
+ for (const auto& rxObj : rList)
+ {
+ // FIXME: Can DrawingML objects be grouped with VML or not valid objects?
+ if (IsVmlObject(rxObj.get()) || !IsValidObject(*rxObj))
+ continue;
+
+ if (nSkipObj == 0)
+ aList.push_back(rxObj.get());
+ else
+ --nSkipObj;
+
+ XclObjAny* pObj = nullptr;
+ if (rxObj->GetObjType() == 0) // group (it can be a subgroup)
+ pObj = dynamic_cast<XclObjAny*>(rxObj.get());
+ if (pObj)
+ {
+ css::uno::Reference<css::drawing::XShapes> xShapes(pObj->GetShape(), UNO_QUERY);
+ if (xShapes)
+ {
+ // skip (also) the objects of this group
+ nSkipObj += xShapes->getCount();
+ }
+ }
+ }
+
+ if (aList.empty())
+ return;
+
+ sal_Int32 nDrawing = drawingml::DrawingML::getNewDrawingUniqueId();
+ OUString sId;
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "drawings/drawing", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "drawings/drawing", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.drawing+xml",
+ oox::getRelationship(Relationship::DRAWING),
+ &sId );
+
+ rStrm.GetCurrentStream()->singleElement(XML_drawing, FSNS(XML_r, XML_id), sId.toUtf8());
+
+ rStrm.PushStream( pDrawing );
+ pDrawing->startElement( FSNS( XML_xdr, XML_wsDr ),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)).toUtf8(),
+ FSNS(XML_xmlns, XML_a), rStrm.getNamespaceURL(OOX_NS(dml)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8() );
+
+ sal_Int32 nShapeId = 1000; // unique id of the shape inside one worksheet (not the whole document)
+ for (const auto& rpObj : aList)
+ {
+ // validate shapeId
+ if ( IsFormControlObject( rpObj ) )
+ {
+ XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rpObj);
+ if (pXclExpTbxControlObj)
+ {
+ pXclExpTbxControlObj->setShapeId(++nShapeId);
+ }
+ }
+
+ rpObj->SaveXml(rStrm);
+ }
+
+ pDrawing->endElement( FSNS( XML_xdr, XML_wsDr ) );
+
+ rStrm.PopStream();
+}
+
+void SaveFormControlObjects(XclExpObjList& rList, XclExpXmlStream& rStrm)
+{
+ bool hasControls = false;
+ for (const auto& rxObj : rList)
+ {
+ if (IsFormControlObject(rxObj.get()))
+ {
+ hasControls = true;
+ break;
+ }
+ }
+
+ if (!hasControls)
+ {
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)).toUtf8());
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+ rWorksheet->startElement(XML_controls);
+
+ for (const auto& rxObj : rList)
+ {
+ if (IsFormControlObject(rxObj.get()))
+ {
+ XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rxObj.get());
+ if (pXclExpTbxControlObj)
+ {
+ const OUString aIdFormControlPr = pXclExpTbxControlObj->SaveControlPropertiesXml(rStrm);
+ pXclExpTbxControlObj->SaveSheetXml(rStrm, aIdFormControlPr);
+ }
+ }
+ }
+
+ rWorksheet->endElement(XML_controls);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+}
+
+void SaveVmlObjects( XclExpObjList& rList, XclExpXmlStream& rStrm )
+{
+ if( GetVmlObjectCount( rList ) == 0 )
+ return;
+
+ sal_Int32 nDrawing = drawingml::DrawingML::getNewVMLUniqueId();
+ OUString sId;
+ sax_fastparser::FSHelperPtr pVmlDrawing = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "drawings/vmlDrawing", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "drawings/vmlDrawing", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.vmlDrawing",
+ oox::getRelationship(Relationship::VMLDRAWING),
+ &sId );
+
+ rStrm.GetCurrentStream()->singleElement(XML_legacyDrawing, FSNS(XML_r, XML_id), sId.toUtf8());
+
+ rStrm.PushStream( pVmlDrawing );
+ pVmlDrawing->write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
+ pVmlDrawing->startElement( XML_xml,
+ FSNS(XML_xmlns, XML_v), rStrm.getNamespaceURL(OOX_NS(vml)).toUtf8(),
+ FSNS(XML_xmlns, XML_o), rStrm.getNamespaceURL(OOX_NS(vmlOffice)).toUtf8(),
+ FSNS(XML_xmlns, XML_x), rStrm.getNamespaceURL(OOX_NS(vmlExcel)).toUtf8(),
+ FSNS(XML_xmlns, XML_w10), rStrm.getNamespaceURL(OOX_NS(vmlWord)).toUtf8() );
+
+ for ( const auto& rxObj : rList )
+ {
+ if (IsFormControlObject(rxObj.get()))
+ {
+ auto pFormControlObject = dynamic_cast<XclExpTbxControlObj*>(rxObj.get());
+ if (pFormControlObject)
+ {
+ pFormControlObject->SaveVml(rStrm);
+ }
+ }
+
+ if( !IsVmlObject( rxObj.get() ) )
+ continue;
+ rxObj->SaveXml( rStrm );
+ }
+
+ pVmlDrawing->endElement( XML_xml );
+
+ rStrm.PopStream();
+}
+
+}
+
+void XclExpObjList::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( pSolverContainer )
+ pSolverContainer->SaveXml( rStrm );
+
+ if( maObjs.empty())
+ return;
+
+ SaveDrawingMLObjects( *this, rStrm );
+ SaveVmlObjects( *this, rStrm );
+ SaveFormControlObjects( *this, rStrm );
+}
+
+// --- class XclObj --------------------------------------------------
+
+XclObj::XclObj( XclExpObjectManager& rObjMgr, sal_uInt16 nObjType, bool bOwnEscher ) :
+ XclExpRecord( EXC_ID_OBJ, 26 ),
+ mrEscherEx( rObjMgr.GetEscherEx() ),
+ mnObjType( nObjType ),
+ nObjId(0),
+ nGrbit( 0x6011 ), // AutoLine, AutoFill, Printable, Locked
+ mnScTab(0),
+ bFirstOnSheet( !rObjMgr.HasObj() ),
+ mbOwnEscher( bOwnEscher )
+{
+ //! first object continues the first MSODRAWING record
+ if ( bFirstOnSheet )
+ pMsodrawing = rObjMgr.GetMsodrawingPerSheet();
+ else
+ pMsodrawing = new XclExpMsoDrawing( mrEscherEx );
+}
+
+XclObj::~XclObj()
+{
+ if ( !bFirstOnSheet )
+ delete pMsodrawing;
+ pClientTextbox.reset();
+ pTxo.reset();
+}
+
+void XclObj::ImplWriteAnchor( const SdrObject* pSdrObj, const tools::Rectangle* pChildAnchor )
+{
+ if( pChildAnchor )
+ {
+ mrEscherEx.AddChildAnchor( *pChildAnchor );
+ }
+ else if( pSdrObj )
+ {
+ std::unique_ptr< XclExpDffAnchorBase > xDffAnchor( mrEscherEx.CreateDffAnchor( *pSdrObj ) );
+ xDffAnchor->WriteDffData( mrEscherEx );
+ }
+}
+
+void XclObj::SetEscherShapeType( sal_uInt16 nType )
+{
+//ToDo: what about the other defined or... types?
+ switch ( nType )
+ {
+ case ESCHER_ShpInst_Line :
+ mnObjType = EXC_OBJTYPE_LINE;
+ break;
+ case ESCHER_ShpInst_Rectangle :
+ case ESCHER_ShpInst_RoundRectangle :
+ mnObjType = EXC_OBJTYPE_RECTANGLE;
+ break;
+ case ESCHER_ShpInst_Ellipse :
+ mnObjType = EXC_OBJTYPE_OVAL;
+ break;
+ case ESCHER_ShpInst_Arc :
+ mnObjType = EXC_OBJTYPE_ARC;
+ break;
+ case ESCHER_ShpInst_TextBox :
+ mnObjType = EXC_OBJTYPE_TEXT;
+ break;
+ case ESCHER_ShpInst_PictureFrame :
+ mnObjType = EXC_OBJTYPE_PICTURE;
+ break;
+ default:
+ mnObjType = EXC_OBJTYPE_DRAWING;
+ }
+}
+
+void XclObj::SetText( const XclExpRoot& rRoot, const SdrTextObj& rObj )
+{
+ OSL_ENSURE( !pClientTextbox, "XclObj::SetText: already set" );
+ if ( !pClientTextbox )
+ {
+ mrEscherEx.UpdateDffFragmentEnd();
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+ pTxo.reset( new XclTxo( rRoot, rObj ) );
+ }
+}
+
+void XclObj::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mnObjType != EXC_OBJTYPE_UNKNOWN, "XclObj::WriteBody - unknown type" );
+
+ // create a substream to be able to create subrecords
+ SvMemoryStream aMemStrm;
+ std::optional< XclExpStream > pXclStrm( std::in_place, aMemStrm, rStrm.GetRoot() );
+
+ // write the ftCmo subrecord
+ pXclStrm->StartRecord( EXC_ID_OBJCMO, 18 );
+ *pXclStrm << mnObjType << nObjId << nGrbit;
+ pXclStrm->WriteZeroBytes( 12 );
+ pXclStrm->EndRecord();
+
+ // write other subrecords
+ WriteSubRecs( *pXclStrm );
+
+ // write the ftEnd subrecord
+ pXclStrm->StartRecord( EXC_ID_OBJEND, 0 );
+ pXclStrm->EndRecord();
+
+ // copy the data to the OBJ record
+ pXclStrm.reset();
+ aMemStrm.Seek( 0 );
+ rStrm.CopyFromStream( aMemStrm );
+}
+
+void XclObj::Save( XclExpStream& rStrm )
+{
+ // MSODRAWING record (msofbtSpContainer)
+ if ( !bFirstOnSheet )
+ pMsodrawing->Save( rStrm );
+
+ // OBJ
+ XclExpRecord::Save( rStrm );
+
+ // second MSODRAWING record and TXO and CONTINUE records
+ SaveTextRecs( rStrm );
+}
+
+void XclObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ if( mnObjType != EXC_OBJTYPE_NOTE )
+ return;
+
+ // FtNts subrecord
+ AddRecSize( 26 );
+ // ft, cb
+ rStrm << EXC_ID_OBJNTS << sal_uInt16(0x0016);
+ sal_uInt8 aGUID[16];
+ rtl_createUuid( aGUID, nullptr, false );
+ // guid
+ rStrm.SetSliceSize( 16 );
+ for( int i = 0; i < 16; i++ )
+ rStrm << aGUID[i];
+ rStrm.SetSliceSize( 0 );
+ // fSharedNote
+ rStrm << sal_uInt16(0);
+ // unused
+ rStrm.WriteZeroBytes( 4 );
+}
+
+void XclObj::SaveTextRecs( XclExpStream& rStrm )
+{
+ // MSODRAWING record (msofbtClientTextbox)
+ if ( pClientTextbox )
+ pClientTextbox->Save( rStrm );
+ // TXO and CONTINUE records
+ if ( pTxo )
+ pTxo->Save( rStrm );
+}
+
+// --- class XclObjComment ------------------------------------------
+
+// tdf#118662 static helper to allow single function access as friend in SdrCaptionObj
+void setSuppressGetBitmapFromXclObjComment(SdrCaptionObj* pSdrCaptionObj, bool bValue)
+{
+ if(nullptr != pSdrCaptionObj)
+ {
+ pSdrCaptionObj->setSuppressGetBitmap(bValue);
+ }
+}
+
+XclObjComment::XclObjComment( XclExpObjectManager& rObjMgr, const tools::Rectangle& rRect, const EditTextObject& rEditObj, SdrCaptionObj* pCaption, bool bVisible, const ScAddress& rAddress, const tools::Rectangle &rFrom, const tools::Rectangle &rTo ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_NOTE, true )
+ , maScPos( rAddress )
+ , mpCaption( pCaption )
+ , mbVisible( bVisible )
+ , maFrom ( rFrom )
+ , maTo ( rTo )
+{
+ // tdf#118662 due to no longer cloning the SdrCaptionObj an old 'hack' using the
+ // fact that no Graphics gets created when a SdrObject is not inserted in a SdrPage
+ // does not work anymore. In SvxShape::GetBitmap that info was used, and here the
+ // SdrCaptionObj was cloned for the only reason to have one not added to a SdrPage.
+ // To emulate old behaviour, use a boolean flag at the SdrCaptionObj.
+ setSuppressGetBitmapFromXclObjComment(mpCaption, true);
+
+ ProcessEscherObj( rObjMgr.GetRoot(), rRect, pCaption, bVisible);
+ // TXO
+ pTxo .reset(new XclTxo( rObjMgr.GetRoot(), rEditObj, pCaption ));
+}
+
+static void lcl_FillProps( EscherPropertyContainer& rPropOpt, SdrObject* pCaption, bool bVisible )
+{
+ if( pCaption )
+ {
+ Reference< XShape > aXShape = GetXShapeForSdrObject( pCaption );
+ Reference< XPropertySet > aXPropSet( aXShape, UNO_QUERY );
+ if( aXPropSet.is() )
+ {
+ rPropOpt.CreateFillProperties( aXPropSet, true);
+
+ rPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // undocumented
+ rPropOpt.AddOpt( 0x0158, 0x00000000 ); // undocumented
+
+ sal_uInt32 nValue = 0;
+ if( !rPropOpt.GetOpt( ESCHER_Prop_FitTextToShape, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+
+ // Maybe the colour is the same as the 'ToolTip' System colour, but the tooltip
+ // colour shouldn't have influence on the fill colour of the exported shape
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fillColor, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x08000050 );
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fillBackColor, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000050 );
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fNoFillHitTest, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 ); // bool field
+ if( !rPropOpt.GetOpt( ESCHER_Prop_shadowColor, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x00000000 );
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) ) // bool field
+ rPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // bool field
+ }
+ }
+
+ sal_uInt32 nFlags = 0x000A0000;
+ ::set_flag( nFlags, sal_uInt32(2), !bVisible );
+ rPropOpt.AddOpt( ESCHER_Prop_fPrint, nFlags ); // bool field
+}
+
+void XclObjComment::ProcessEscherObj( const XclExpRoot& rRoot, const tools::Rectangle& rRect, SdrObject* pCaption, const bool bVisible )
+{
+ EscherPropertyContainer aPropOpt;
+
+ lcl_FillProps( aPropOpt, pCaption, bVisible );
+
+ nGrbit = 0; // all off: AutoLine, AutoFill, Printable, Locked
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ XclExpDffNoteAnchor( rRoot, rRect ).WriteDffData( mrEscherEx );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ //! Be sure to construct the MSODRAWING ClientTextbox record _after_ the
+ //! base OBJ's MSODRAWING record Escher data is completed.
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+}
+
+XclObjComment::~XclObjComment()
+{
+ // tdf#118662 reset flag
+ setSuppressGetBitmapFromXclObjComment(mpCaption, false);
+}
+
+void XclObjComment::Save( XclExpStream& rStrm )
+{
+ // content of this record
+ XclObj::Save( rStrm );
+}
+
+namespace {
+
+class VmlCommentExporter : public VMLExport
+{
+ ScAddress maScPos;
+ SdrCaptionObj* mpCaption;
+ bool mbVisible;
+ tools::Rectangle maFrom;
+ tools::Rectangle maTo;
+
+public:
+ VmlCommentExporter ( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption, bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo );
+protected:
+ virtual void Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect ) override;
+ using VMLExport::StartShape;
+ virtual sal_Int32 StartShape() override;
+ using VMLExport::EndShape;
+ virtual void EndShape( sal_Int32 nShapeElement ) override;
+};
+
+}
+
+VmlCommentExporter::VmlCommentExporter( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption,
+ bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo )
+ : VMLExport( p )
+ , maScPos( aScPos )
+ , mpCaption( pCaption )
+ , mbVisible( bVisible )
+ , maFrom ( aFrom )
+ , maTo ( aTo )
+{
+}
+
+void VmlCommentExporter::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect )
+{
+ lcl_FillProps( rProps, mpCaption, mbVisible );
+ rProps.AddOpt( ESCHER_Prop_fHidden, sal_uInt32(mbVisible) ); // bool field
+
+ // shadow property value for comment ( set in lcl_FillProps [*] ) has been
+ // overwritten by new value ( 0x20000 ) in the generic part of the export
+ // ( see EscherPropertyContainer::CreateShadowProperties )
+ // Safer option here is to just force the needed value here for oox vml
+ // export alone ( and avoid potential problems with binary export )
+ // #TODO investigate value of ESCHER_Prop_fshadowObscured generally
+ // in binary export ( if indeed this value is good for binary export )
+ // we can change the heuristics and/or initialisation path and get
+ // rid of line below.
+ // [*] lcl_FillProps seems to be called twice when exporting to xlsx
+ // once from XclObjComment::ProcessEscherObj #TODO look into that also
+ rProps.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // force value for comments
+
+ VMLExport::Commit( rProps, rRect );
+}
+
+sal_Int32 VmlCommentExporter::StartShape()
+{
+ AddShapeAttribute( XML_type, "#_x0000_t202" );
+
+ sal_Int32 nId = VMLExport::StartShape();
+
+ return nId;
+}
+
+static const char* lcl_GetHorizAlignFromItemSetChar(const SfxItemSet& rItemSet)
+{
+ switch (rItemSet.Get(EE_PARA_JUST).GetAdjust())
+ {
+ case SvxAdjust::Center:
+ return "Center";
+ case SvxAdjust::Right:
+ return "Right";
+ case SvxAdjust::Block:
+ return "Justify";
+ default:
+ return "Left";
+ }
+}
+
+static const char* lcl_GetVertAlignFromItemSetChar( const SfxItemSet& rItemSet )
+{
+ switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() )
+ {
+ case SDRTEXTVERTADJUST_CENTER:
+ return "Center";
+ case SDRTEXTVERTADJUST_BOTTOM:
+ return "Bottom";
+ case SDRTEXTVERTADJUST_BLOCK:
+ return "Justify";
+ case SDRTEXTVERTADJUST_TOP:
+ default:
+ return "Top";
+ }
+}
+
+void VmlCommentExporter::EndShape( sal_Int32 nShapeElement )
+{
+ char pAnchor[100];
+ sax_fastparser::FSHelperPtr pVmlDrawing = GetFS();
+ snprintf( pAnchor, 100, "%" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64,
+ sal_Int64(maFrom.Left()), sal_Int64(maFrom.Top()), sal_Int64(maFrom.Right()), sal_Int64(maFrom.Bottom()),
+ sal_Int64(maTo.Left()), sal_Int64(maTo.Top()), sal_Int64(maTo.Right()), sal_Int64(maTo.Bottom()) );
+
+ // Getting comment text alignments
+ const char* pVertAlign = lcl_GetVertAlignFromItemSetChar(mpCaption->GetMergedItemSet());
+ const char* pHorizAlign = lcl_GetHorizAlignFromItemSetChar(mpCaption->GetMergedItemSet());
+
+ pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, "Note");
+ pVmlDrawing->singleElement(FSNS(XML_x, XML_MoveWithCells));
+ pVmlDrawing->singleElement(FSNS(XML_x, XML_SizeWithCells));
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Anchor ), pAnchor );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_AutoFill ), "False" );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextVAlign ), pVertAlign );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextHAlign ), pHorizAlign );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Row ), maScPos.Row() );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS(XML_x, XML_Column), sal_Int32(maScPos.Col()));
+ if(mbVisible)
+ pVmlDrawing->singleElement(FSNS(XML_x, XML_Visible));
+ pVmlDrawing->endElement( FSNS( XML_x, XML_ClientData ) );
+
+ VMLExport::EndShape( nShapeElement );
+}
+
+void XclObjComment::SaveXml( XclExpXmlStream& rStrm )
+{
+ VmlCommentExporter aCommentExporter( rStrm.GetCurrentStream(), maScPos, mpCaption, mbVisible, maFrom, maTo );
+ aCommentExporter.AddSdrObject( *mpCaption );
+}
+
+// --- class XclObjDropDown ------------------------------------------
+
+XclObjDropDown::XclObjDropDown( XclExpObjectManager& rObjMgr, const ScAddress& rPos, bool bFilt ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_DROPDOWN, true ),
+ bIsFiltered( bFilt )
+{
+ SetLocked( true );
+ SetPrintable( false );
+ SetAutoFill( true );
+ SetAutoLine( false );
+ nGrbit |= 0x0100; // undocumented
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00010000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x000A0000 ); // bool field
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ XclExpDffDropDownAnchor( rObjMgr.GetRoot(), rPos ).WriteDffData( mrEscherEx );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+
+ // old size + ftSbs + ftLbsData
+ AddRecSize( 24 + 20 );
+}
+
+XclObjDropDown::~XclObjDropDown()
+{
+}
+
+void XclObjDropDown::WriteSubRecs( XclExpStream& rStrm )
+{
+ // ftSbs subrecord - Scroll bars (dummy)
+ rStrm.StartRecord( EXC_ID_OBJSBS, 20 );
+ rStrm.WriteZeroBytes( 20 );
+ rStrm.EndRecord();
+
+ // ftLbsData subrecord - Listbox data
+ sal_uInt16 nDropDownFlags = 0;
+ ::insert_value( nDropDownFlags, EXC_OBJ_DROPDOWN_SIMPLE, 0, 2 );
+ ::set_flag( nDropDownFlags, EXC_OBJ_DROPDOWN_FILTERED, bIsFiltered );
+ rStrm.StartRecord( EXC_ID_OBJLBSDATA, 16 );
+ rStrm << sal_uInt32(0) << sal_uInt16(0) << sal_uInt16(0x0301) << sal_uInt16(0)
+ << nDropDownFlags << sal_uInt16( 20 ) << sal_uInt16( 130 );
+ rStrm.EndRecord();
+}
+
+// --- class XclTxo --------------------------------------------------
+
+static sal_uInt8 lcl_GetHorAlignFromItemSet( const SfxItemSet& rItemSet )
+{
+ sal_uInt8 nHorAlign = EXC_OBJ_HOR_LEFT;
+
+ switch( rItemSet.Get( EE_PARA_JUST ).GetAdjust() )
+ {
+ case SvxAdjust::Left: nHorAlign = EXC_OBJ_HOR_LEFT; break;
+ case SvxAdjust::Center: nHorAlign = EXC_OBJ_HOR_CENTER; break;
+ case SvxAdjust::Right: nHorAlign = EXC_OBJ_HOR_RIGHT; break;
+ case SvxAdjust::Block: nHorAlign = EXC_OBJ_HOR_JUSTIFY; break;
+ default:;
+ }
+ return nHorAlign;
+}
+
+static sal_uInt8 lcl_GetVerAlignFromItemSet( const SfxItemSet& rItemSet )
+{
+ sal_uInt8 nVerAlign = EXC_OBJ_VER_TOP;
+
+ switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() )
+ {
+ case SDRTEXTVERTADJUST_TOP: nVerAlign = EXC_OBJ_VER_TOP; break;
+ case SDRTEXTVERTADJUST_CENTER: nVerAlign = EXC_OBJ_VER_CENTER; break;
+ case SDRTEXTVERTADJUST_BOTTOM: nVerAlign = EXC_OBJ_VER_BOTTOM; break;
+ case SDRTEXTVERTADJUST_BLOCK: nVerAlign = EXC_OBJ_VER_JUSTIFY; break;
+ default:;
+ }
+ return nVerAlign;
+}
+
+XclTxo::XclTxo( const OUString& rString, sal_uInt16 nFontIx ) :
+ mpString( std::make_shared<XclExpString>( rString ) ),
+ mnRotation( EXC_OBJ_ORIENT_NONE ),
+ mnHorAlign( EXC_OBJ_HOR_LEFT ),
+ mnVerAlign( EXC_OBJ_VER_TOP )
+{
+ if( mpString->Len() )
+ {
+ // If there is text, Excel *needs* the 2nd CONTINUE record with at least two format runs
+ mpString->AppendFormat( 0, nFontIx );
+ mpString->AppendFormat( mpString->Len(), EXC_FONT_APP );
+ }
+}
+
+XclTxo::XclTxo( const XclExpRoot& rRoot, const SdrTextObj& rTextObj ) :
+ mpString( XclExpStringHelper::CreateString( rRoot, rTextObj ) ),
+ mnRotation( EXC_OBJ_ORIENT_NONE ),
+ mnHorAlign( EXC_OBJ_HOR_LEFT ),
+ mnVerAlign( EXC_OBJ_VER_TOP )
+{
+ // additional alignment and orientation items
+ const SfxItemSet& rItemSet = rTextObj.GetMergedItemSet();
+
+ // horizontal alignment
+ SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) );
+
+ // vertical alignment
+ SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) );
+
+ // rotation
+ Degree100 nAngle = rTextObj.GetRotateAngle();
+ if( (4500_deg100 < nAngle) && (nAngle < 13500_deg100) )
+ mnRotation = EXC_OBJ_ORIENT_90CCW;
+ else if( (22500_deg100 < nAngle) && (nAngle < 31500_deg100) )
+ mnRotation = EXC_OBJ_ORIENT_90CW;
+ else
+ mnRotation = EXC_OBJ_ORIENT_NONE;
+}
+
+XclTxo::XclTxo( const XclExpRoot& rRoot, const EditTextObject& rEditObj, SdrObject* pCaption ) :
+ mpString( XclExpStringHelper::CreateString( rRoot, rEditObj ) ),
+ mnRotation( EXC_OBJ_ORIENT_NONE ),
+ mnHorAlign( EXC_OBJ_HOR_LEFT ),
+ mnVerAlign( EXC_OBJ_VER_TOP )
+{
+ if(!pCaption)
+ return;
+
+ // Excel has one alignment per NoteObject while Calc supports
+ // one alignment per paragraph - use the first paragraph
+ // alignment (if set) as our overall alignment.
+ OUString aParaText( rEditObj.GetText( 0 ) );
+ if( !aParaText.isEmpty() )
+ {
+ const SfxItemSet& aSet( rEditObj.GetParaAttribs( 0));
+ if( const SvxAdjustItem* pItem = aSet.GetItemIfSet( EE_PARA_JUST ) )
+ {
+ SvxAdjust eEEAlign = pItem->GetAdjust();
+ pCaption->SetMergedItem( SvxAdjustItem( eEEAlign, EE_PARA_JUST ) );
+ }
+ }
+ const SfxItemSet& rItemSet = pCaption->GetMergedItemSet();
+
+ // horizontal alignment
+ SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) );
+
+ // vertical alignment
+ SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) );
+
+ // orientation alignment
+ const SvxWritingModeItem& rItem = rItemSet.Get( SDRATTR_TEXTDIRECTION );
+ if( rItem.GetValue() == css::text::WritingMode_TB_RL )
+ mnRotation = EXC_OBJ_ORIENT_90CW;
+}
+
+void XclTxo::SaveCont( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mpString, "XclTxo::SaveCont - missing string" );
+
+ // #i96858# do not save existing string formatting if text is empty
+ sal_uInt16 nRunLen = mpString->IsEmpty() ? 0 : (8 * mpString->GetFormatsCount());
+ // alignment
+ sal_uInt16 nFlags = 0;
+ ::insert_value( nFlags, mnHorAlign, 1, 3 );
+ ::insert_value( nFlags, mnVerAlign, 4, 3 );
+
+ rStrm << nFlags << mnRotation;
+ rStrm.WriteZeroBytes( 6 );
+ rStrm << mpString->Len() << nRunLen << sal_uInt32( 0 );
+}
+
+void XclTxo::Save( XclExpStream& rStrm )
+{
+ // Write the TXO part
+ ExcRecord::Save( rStrm );
+
+ // CONTINUE records are only written if there is some text
+ if( mpString->IsEmpty() )
+ return;
+
+ // CONTINUE for character array
+ rStrm.StartRecord( EXC_ID_CONT, mpString->GetBufferSize() + 1 );
+ rStrm << static_cast< sal_uInt8 >( mpString->GetFlagField() & EXC_STRF_16BIT ); // only Unicode flag
+ mpString->WriteBuffer( rStrm );
+ rStrm.EndRecord();
+
+ // CONTINUE for formatting runs
+ rStrm.StartRecord( EXC_ID_CONT, 8 * mpString->GetFormatsCount() );
+ const XclFormatRunVec& rFormats = mpString->GetFormats();
+ for( const auto& rFormat : rFormats )
+ rStrm << rFormat.mnChar << rFormat.mnFontIdx << sal_uInt32( 0 );
+ rStrm.EndRecord();
+}
+
+sal_uInt16 XclTxo::GetNum() const
+{
+ return EXC_ID_TXO;
+}
+
+std::size_t XclTxo::GetLen() const
+{
+ return 18;
+}
+
+// --- class XclObjOle -------------------------------------------
+
+XclObjOle::XclObjOle( XclExpObjectManager& rObjMgr, const SdrOle2Obj& rObj ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_PICTURE ),
+ rOleObj( rObj ),
+ pRootStorage( rObjMgr.GetRoot().GetRootStorage().get() )
+{
+}
+
+XclObjOle::~XclObjOle()
+{
+}
+
+void XclObjOle::WriteSubRecs( XclExpStream& rStrm )
+{
+ // write only as embedded, not linked
+ OUString aStorageName( "MBD" );
+ char aBuf[ sizeof(sal_uInt32) * 2 + 1 ];
+ // FIXME Eeek! Is this just a way to get a unique id?
+ sal_uInt32 nPictureId = sal_uInt32(reinterpret_cast<sal_uIntPtr>(this) >> 2);
+ o3tl::sprintf( aBuf, "%08X", static_cast< unsigned int >( nPictureId ) );
+ aStorageName += OUString::createFromAscii(aBuf);
+ tools::SvRef<SotStorage> xOleStg = pRootStorage->OpenSotStorage( aStorageName );
+ if( !xOleStg.is() )
+ return;
+
+ uno::Reference < embed::XEmbeddedObject > xObj( rOleObj.GetObjRef() );
+ if ( !xObj.is() )
+ return;
+
+ // set version to "old" version, because it must be
+ // saved in MS notation.
+ sal_uInt32 nFl = 0;
+ const SvtFilterOptions& rFltOpts = SvtFilterOptions::Get();
+ if( rFltOpts.IsMath2MathType() )
+ nFl |= OLE_STARMATH_2_MATHTYPE;
+
+ if( rFltOpts.IsWriter2WinWord() )
+ nFl |= OLE_STARWRITER_2_WINWORD;
+
+ if( rFltOpts.IsCalc2Excel() )
+ nFl |= OLE_STARCALC_2_EXCEL;
+
+ if( rFltOpts.IsImpress2PowerPoint() )
+ nFl |= OLE_STARIMPRESS_2_POWERPOINT;
+
+ SvxMSExportOLEObjects aOLEExpFilt( nFl );
+ aOLEExpFilt.ExportOLEObject( xObj, *xOleStg );
+
+ // OBJCF subrecord, undocumented as usual
+ rStrm.StartRecord( EXC_ID_OBJCF, 2 );
+ rStrm << sal_uInt16(0x0002);
+ rStrm.EndRecord();
+
+ // OBJFLAGS subrecord, undocumented as usual
+ rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 );
+ sal_uInt16 nFlags = EXC_OBJ_PIC_MANUALSIZE;
+ ::set_flag( nFlags, EXC_OBJ_PIC_SYMBOL, rOleObj.GetAspect() == embed::Aspects::MSOLE_ICON );
+ rStrm << nFlags;
+ rStrm.EndRecord();
+
+ // OBJPICTFMLA subrecord, undocumented as usual
+ XclExpString aName( xOleStg->GetUserName() );
+ sal_uInt16 nPadLen = static_cast<sal_uInt16>(aName.GetSize() & 0x01);
+ sal_uInt16 nFmlaLen = static_cast< sal_uInt16 >( 12 + aName.GetSize() + nPadLen );
+ sal_uInt16 nSubRecLen = nFmlaLen + 6;
+
+ rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nSubRecLen );
+ rStrm << nFmlaLen
+ << sal_uInt16( 5 ) << sal_uInt32( 0 ) << sal_uInt8( 2 )
+ << sal_uInt32( 0 ) << sal_uInt8( 3 )
+ << aName;
+ if( nPadLen )
+ rStrm << sal_uInt8( 0 ); // pad byte
+ rStrm << nPictureId;
+ rStrm.EndRecord();
+}
+
+void XclObjOle::Save( XclExpStream& rStrm )
+{
+ // content of this record
+ XclObj::Save( rStrm );
+}
+
+// --- class XclObjAny -------------------------------------------
+
+XclObjAny::XclObjAny( XclExpObjectManager& rObjMgr, const Reference< XShape >& rShape, ScDocument* pDoc )
+ : XclObj( rObjMgr, EXC_OBJTYPE_UNKNOWN )
+ , mxShape( rShape )
+ , mpDoc(pDoc)
+{
+}
+
+XclObjAny::~XclObjAny()
+{
+}
+
+void XclObjAny::WriteSubRecs( XclExpStream& rStrm )
+{
+ if( mnObjType == EXC_OBJTYPE_GROUP )
+ // ftGmo subrecord
+ rStrm << EXC_ID_OBJGMO << sal_uInt16(2) << sal_uInt16(0);
+}
+
+void XclObjAny::Save( XclExpStream& rStrm )
+{
+ if( mnObjType == EXC_OBJTYPE_GROUP )
+ // old size + ftGmo
+ AddRecSize( 6 );
+
+ // content of this record
+ XclObj::Save( rStrm );
+}
+
+// --- class ExcBof8_Base --------------------------------------------
+
+ExcBof8_Base::ExcBof8_Base()
+{
+ nVers = 0x0600;
+ nRupBuild = 0x0dbb;
+ nRupYear = 0x07cc;
+}
+
+void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const Reference< XShape >& rShape, SCTAB nTab )
+{
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ awt::Point aTopLeft = rShape->getPosition();
+ awt::Size aSize = rShape->getSize();
+
+ // There are a few cases where we must adjust these values
+ // Do not adjust objects, which have rotation incorporated into their points
+ // but report a rotation angle nevertheless.
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape);
+ if (pObj && pObj->GetObjIdentifier() != SdrObjKind::Line && pObj->GetObjIdentifier() != SdrObjKind::PolyLine
+ && pObj->GetObjIdentifier() != SdrObjKind::PathLine && pObj->GetObjIdentifier() != SdrObjKind::FreehandLine
+ && pObj->GetObjIdentifier() != SdrObjKind::PathPolyLine)
+ {
+ Degree100 nRotation = NormAngle36000(pObj->GetRotateAngle());
+ if (nRotation)
+ {
+ sal_Int16 nHalfWidth = aSize.Width / 2;
+ sal_Int16 nHalfHeight = aSize.Height / 2;
+
+ // Center of bounding box of the rotated shape
+ const auto aSnapRectCenter(pObj->GetSnapRect().Center());
+ aTopLeft.X = aSnapRectCenter.X() - nHalfWidth;
+ aTopLeft.Y = aSnapRectCenter.Y() - nHalfHeight;
+
+ // MSO changes the anchor positions at these angles and that does an extra 90 degrees
+ // rotation on our shapes, so we output it in such position that MSO
+ // can draw this shape correctly.
+ if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100) || (nRotation > 22500_deg100 && nRotation <= 31500_deg100))
+ {
+ aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth;
+ aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight;
+
+ std::swap(aSize.Width, aSize.Height);
+ }
+ }
+ }
+
+ tools::Rectangle aLocation( aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height );
+ ScRange aRange = rStrm.GetRoot().GetDoc().GetRange( nTab, aLocation );
+ tools::Rectangle aRangeRect = rStrm.GetRoot().GetDoc().GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col()-1, aRange.aEnd.Row()-1,
+ nTab );
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_from));
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aStart.Col()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Left() - aRangeRect.Left() ) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aStart.Row()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Top() - aRangeRect.Top() ) );
+ pDrawing->endElement( FSNS( XML_xdr, XML_from ) );
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_to));
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aEnd.Col()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Right() - aRangeRect.Right() ) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aEnd.Row()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Bottom() - aRangeRect.Bottom() ) );
+ pDrawing->endElement( FSNS( XML_xdr, XML_to ) );
+}
+
+void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const XclObjAny& rObj )
+{
+ WriteFromTo( rStrm, rObj.GetShape(), rObj.GetTab() );
+}
+
+static const char*
+GetEditAs( const XclObjAny& rObj )
+{
+ if( const SdrObject* pShape = EscherEx::GetSdrObject( rObj.GetShape() ) )
+ {
+ switch( ScDrawLayer::GetAnchorType( *pShape ) )
+ {
+ case SCA_CELL:
+ return "oneCell";
+ case SCA_CELL_RESIZE:
+ return "twoCell";
+ default:
+ case SCA_PAGE:
+ break; // absolute
+ }
+ }
+ return "absolute";
+}
+
+namespace {
+
+ScRefFlags parseRange(const OUString& rString, ScRange& rRange, const ScDocument& rDoc)
+{
+ // start with the address convention set in the document
+ formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ ScRefFlags nResult = rRange.Parse(rString, rDoc, eConv);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try the default calc address convention
+ nResult = rRange.Parse(rString, rDoc);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try excel a1
+ nResult = rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try r1c1
+ return rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+}
+
+ScRefFlags parseAddress(const OUString& rString, ScAddress& rAddress, const ScDocument& rDoc)
+{
+ // start with the address convention set in the document
+ formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ ScRefFlags nResult = rAddress.Parse(rString, rDoc, eConv);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try the default calc address convention
+ nResult = rAddress.Parse(rString, rDoc);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try excel a1
+ nResult = rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try r1c1
+ return rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+}
+
+void transformURL(const OUString& rOldURL, OUString& rNewURL, const ScDocument& rDoc)
+{
+ if (rOldURL.startsWith("#"))
+ {
+ // URL has to be decoded for escaped characters (%20)
+ OUString aURL = INetURLObject::decode( rOldURL,
+ INetURLObject::DecodeMechanism::WithCharset );
+ OUString aAddressString = aURL.copy(1);
+
+ ScRange aRange;
+ ScRefFlags nResult = parseRange(aAddressString, aRange, rDoc);
+ if ( nResult & ScRefFlags::VALID )
+ {
+ OUString aString = aRange.Format(rDoc, nResult, formula::FormulaGrammar::CONV_XL_OOX);
+ rNewURL = "#" + aString;
+ return;
+ }
+ else
+ {
+ ScAddress aAddress;
+ nResult = parseAddress(aAddressString, aAddress, rDoc);
+ if( nResult & ScRefFlags::VALID )
+ {
+ OUString aString = aAddress.Format(nResult, &rDoc, formula::FormulaGrammar::CONV_XL_OOX);
+ rNewURL = "#" + aString;
+ return;
+ }
+ }
+ }
+
+ rNewURL = rOldURL;
+}
+
+}
+
+ScURLTransformer::ScURLTransformer(ScDocument& rDoc)
+ : mrDoc(rDoc)
+{
+}
+
+OUString ScURLTransformer::getTransformedString(const OUString& rURL) const
+{
+ OUString aNewURL;
+ transformURL(rURL, aNewURL, mrDoc);
+ return aNewURL;
+}
+
+bool ScURLTransformer::isExternalURL(const OUString& rURL) const
+{
+ return !rURL.startsWith("#");
+}
+
+void XclObjAny::SaveXml( XclExpXmlStream& rStrm )
+{
+ // Do not output any of the detective shapes and validation circles.
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxShape);
+ if (pObject)
+ {
+ ScDocument& rDoc = rStrm.GetRoot().GetDoc();
+ ScDetectiveFunc aDetFunc(rDoc, mnScTab);
+ ScAddress aPosition;
+ ScRange aSourceRange;
+ bool bRedLine;
+ ScDetectiveObjType eObjType
+ = aDetFunc.GetDetectiveObjectType(pObject, mnScTab, aPosition, aSourceRange, bRedLine);
+
+ if (eObjType != SC_DETOBJ_NONE)
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ ShapeExport aDML(XML_xdr, pDrawing, nullptr, &rStrm, drawingml::DOCUMENT_XLSX);
+ auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc);
+ aDML.SetURLTranslator(pURLTransformer);
+
+ pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor
+ XML_editAs, GetEditAs( *this ) );
+ Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY );
+ if (xPropSet.is())
+ {
+ WriteFromTo( rStrm, *this );
+ aDML.WriteShape( mxShape );
+ }
+
+ pDrawing->singleElement( FSNS( XML_xdr, XML_clientData)
+ // OOXTODO: XML_fLocksWithSheet
+ // OOXTODO: XML_fPrintsWithSheet
+ );
+ pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) );
+}
+
+void ExcBof8_Base::SaveCont( XclExpStream& rStrm )
+{
+ rStrm.DisableEncryption();
+ rStrm << nVers << nDocType << nRupBuild << nRupYear
+ << sal_uInt32(0)/*nFileHistory*/
+ << sal_uInt32(0x06) /*nLowestBiffVer = Biff8*/;
+}
+
+sal_uInt16 ExcBof8_Base::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBof8_Base::GetLen() const
+{
+ return 16;
+}
+
+// --- class ExcBof8 -------------------------------------------------
+
+ExcBof8::ExcBof8()
+{
+ nDocType = 0x0010;
+}
+
+// --- class ExcBofW8 ------------------------------------------------
+
+ExcBofW8::ExcBofW8()
+{
+ nDocType = 0x0005;
+}
+
+// --- class ExcBundlesheet8 -----------------------------------------
+
+ExcBundlesheet8::ExcBundlesheet8( const RootData& rRootData, SCTAB _nTab ) :
+ ExcBundlesheetBase( rRootData, static_cast<sal_uInt16>(_nTab) ),
+ sUnicodeName( rRootData.pER->GetTabInfo().GetScTabName( _nTab ) )
+{
+}
+
+ExcBundlesheet8::ExcBundlesheet8( OUString aString ) :
+ sUnicodeName(std::move( aString ))
+{
+}
+
+void ExcBundlesheet8::SaveCont( XclExpStream& rStrm )
+{
+ m_nOwnPos = rStrm.GetSvStreamPos();
+ // write dummy position, real position comes later
+ rStrm.DisableEncryption();
+ rStrm << sal_uInt32(0);
+ rStrm.EnableEncryption();
+ rStrm << nGrbit << GetName();
+}
+
+std::size_t ExcBundlesheet8::GetLen() const
+{ // Text max 255 chars
+ return 8 + GetName().GetBufferSize();
+}
+
+void ExcBundlesheet8::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString sId;
+ rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", nTab+1),
+ XclXmlUtils::GetStreamName( nullptr, "worksheets/sheet", nTab+1),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
+ oox::getRelationship(Relationship::WORKSHEET),
+ &sId );
+
+ rStrm.GetCurrentStream()->singleElement( XML_sheet,
+ XML_name, sUnicodeName.toUtf8(),
+ XML_sheetId, OString::number( nTab+1 ),
+ XML_state, nGrbit == 0x0000 ? "visible" : "hidden",
+ FSNS( XML_r, XML_id ), sId.toUtf8() );
+}
+
+// --- class XclObproj -----------------------------------------------
+
+sal_uInt16 XclObproj::GetNum() const
+{
+ return 0x00D3;
+}
+
+std::size_t XclObproj::GetLen() const
+{
+ return 0;
+}
+
+// ---- class XclCodename --------------------------------------------
+
+XclCodename::XclCodename( const OUString& r ) : aName( r )
+{
+}
+
+void XclCodename::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << aName;
+}
+
+sal_uInt16 XclCodename::GetNum() const
+{
+ return 0x01BA;
+}
+
+std::size_t XclCodename::GetLen() const
+{
+ return aName.GetSize();
+}
+
+// ---- Scenarios ----------------------------------------------------
+
+ExcEScenarioCell::ExcEScenarioCell( sal_uInt16 nC, sal_uInt16 nR, const OUString& rTxt ) :
+ nCol( nC ),
+ nRow( nR ),
+ sText( rTxt, XclStrFlags::NONE, 255 )
+{
+}
+
+void ExcEScenarioCell::WriteAddress( XclExpStream& rStrm ) const
+{
+ rStrm << nRow << nCol;
+}
+
+void ExcEScenarioCell::WriteText( XclExpStream& rStrm ) const
+{
+ rStrm << sText;
+}
+
+void ExcEScenarioCell::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_inputCells,
+ // OOXTODO: XML_deleted,
+ // OOXTODO: XML_numFmtId,
+ XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), ScAddress( nCol, nRow, 0 ) ),
+ // OOXTODO: XML_undone,
+ XML_val, XclXmlUtils::ToOString( sText ) );
+}
+
+ExcEScenario::ExcEScenario( const XclExpRoot& rRoot, SCTAB nTab )
+{
+ OUString sTmpName;
+ OUString sTmpComm;
+ OUString aTmp;
+ Color aDummyCol;
+ ScScenarioFlags nFlags;
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ rDoc.GetName(nTab, aTmp);
+ sTmpName = aTmp;
+ sName.Assign( sTmpName, XclStrFlags::EightBitLength );
+ nRecLen = 8 + sName.GetBufferSize();
+
+ rDoc.GetScenarioData( nTab, aTmp, aDummyCol, nFlags );
+ sTmpComm = aTmp;
+ sComment.Assign( sTmpComm, XclStrFlags::NONE, 255 );
+ if( sComment.Len() )
+ nRecLen += sComment.GetSize();
+ bProtected = (nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE;
+
+ sUserName.Assign( rRoot.GetUserName(), XclStrFlags::NONE, 255 );
+ nRecLen += sUserName.GetSize();
+
+ const ScRangeList* pRList = rDoc.GetScenarioRanges( nTab );
+ if( !pRList )
+ return;
+
+ bool bContLoop = true;
+ SCROW nRow;
+ SCCOL nCol;
+ OUString sText;
+ double fVal;
+
+ for( size_t nRange = 0; (nRange < pRList->size()) && bContLoop; nRange++ )
+ {
+ const ScRange & rRange = (*pRList)[nRange];
+ for( nRow = rRange.aStart.Row(); (nRow <= rRange.aEnd.Row()) && bContLoop; nRow++ )
+ for( nCol = rRange.aStart.Col(); (nCol <= rRange.aEnd.Col()) && bContLoop; nCol++ )
+ {
+ if( rDoc.HasValueData( nCol, nRow, nTab ) )
+ {
+ fVal = rDoc.GetValue( nCol, nRow, nTab );
+ sText = ::rtl::math::doubleToUString( fVal,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0],
+ true );
+ }
+ else
+ sText = rDoc.GetString(nCol, nRow, nTab);
+ bContLoop = Append( static_cast<sal_uInt16>(nCol),
+ static_cast<sal_uInt16>(nRow), sText );
+ }
+ }
+}
+
+bool ExcEScenario::Append( sal_uInt16 nCol, sal_uInt16 nRow, const OUString& rTxt )
+{
+ if( aCells.size() == EXC_SCEN_MAXCELL )
+ return false;
+
+ ExcEScenarioCell aCell(nCol, nRow, rTxt);
+ aCells.push_back(aCell);
+ nRecLen += 6 + aCell.GetStringBytes(); // 4 bytes address, 2 bytes ifmt
+ return true;
+}
+
+void ExcEScenario::SaveCont( XclExpStream& rStrm )
+{
+ sal_uInt16 count = aCells.size();
+
+ rStrm << count // number of cells
+ << sal_uInt8(bProtected) // fProtection
+ << sal_uInt8(0) // fHidden
+ << static_cast<sal_uInt8>(sName.Len()) // length of scen name
+ << static_cast<sal_uInt8>(sComment.Len()) // length of comment
+ << static_cast<sal_uInt8>(sUserName.Len()); // length of user name
+ sName.WriteFlagField( rStrm );
+ sName.WriteBuffer( rStrm );
+
+ rStrm << sUserName;
+
+ if( sComment.Len() )
+ rStrm << sComment;
+
+ for( const auto& rCell : aCells )
+ rCell.WriteAddress( rStrm ); // pos of cell
+ for( const auto& rCell : aCells )
+ rCell.WriteText( rStrm ); // string content
+ rStrm.SetSliceSize( 2 );
+ rStrm.WriteZeroBytes( 2 * count ); // date format
+}
+
+sal_uInt16 ExcEScenario::GetNum() const
+{
+ return 0x00AF;
+}
+
+std::size_t ExcEScenario::GetLen() const
+{
+ return nRecLen;
+}
+
+void ExcEScenario::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_scenario,
+ XML_name, XclXmlUtils::ToOString( sName ),
+ XML_locked, ToPsz( bProtected ),
+ // OOXTODO: XML_hidden,
+ XML_count, OString::number( aCells.size() ),
+ XML_user, XESTRING_TO_PSZ( sUserName ),
+ XML_comment, XESTRING_TO_PSZ( sComment ) );
+
+ for( const auto& rCell : aCells )
+ rCell.SaveXml( rStrm );
+
+ rWorkbook->endElement( XML_scenario );
+}
+
+ExcEScenarioManager::ExcEScenarioManager( const XclExpRoot& rRoot, SCTAB nTab ) :
+ nActive( 0 )
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ if( rDoc.IsScenario( nTab ) )
+ return;
+
+ SCTAB nFirstTab = nTab + 1;
+ SCTAB nNewTab = nFirstTab;
+
+ while( rDoc.IsScenario( nNewTab ) )
+ {
+ aScenes.emplace_back( rRoot, nNewTab );
+
+ if( rDoc.IsActiveScenario( nNewTab ) )
+ nActive = static_cast<sal_uInt16>(nNewTab - nFirstTab);
+ nNewTab++;
+ }
+}
+
+ExcEScenarioManager::~ExcEScenarioManager()
+{
+}
+
+void ExcEScenarioManager::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(aScenes.size()) // number of scenarios
+ << nActive // active scen
+ << nActive // last displayed
+ << sal_uInt16(0); // reference areas
+}
+
+void ExcEScenarioManager::Save( XclExpStream& rStrm )
+{
+ if( !aScenes.empty() )
+ ExcRecord::Save( rStrm );
+
+ for( ExcEScenario& rScenario : aScenes )
+ rScenario.Save( rStrm );
+}
+
+void ExcEScenarioManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( aScenes.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_scenarios,
+ XML_current, OString::number( nActive ),
+ XML_show, OString::number( nActive )
+ // OOXTODO: XML_sqref
+ );
+
+ for( ExcEScenario& rScenario : aScenes )
+ rScenario.SaveXml( rStrm );
+
+ rWorkbook->endElement( XML_scenarios );
+}
+
+sal_uInt16 ExcEScenarioManager::GetNum() const
+{
+ return 0x00AE;
+}
+
+std::size_t ExcEScenarioManager::GetLen() const
+{
+ return 8;
+}
+
+namespace {
+
+struct XclExpTabProtectOption
+{
+ ScTableProtection::Option eOption;
+ sal_uInt16 nMask;
+};
+
+}
+
+XclExpSheetProtectOptions::XclExpSheetProtectOptions( const XclExpRoot& rRoot, SCTAB nTab ) :
+ XclExpRecord( 0x0867, 23 )
+{
+ static const XclExpTabProtectOption aTable[] =
+ {
+ { ScTableProtection::OBJECTS, 0x0001 },
+ { ScTableProtection::SCENARIOS, 0x0002 },
+ { ScTableProtection::FORMAT_CELLS, 0x0004 },
+ { ScTableProtection::FORMAT_COLUMNS, 0x0008 },
+ { ScTableProtection::FORMAT_ROWS, 0x0010 },
+ { ScTableProtection::INSERT_COLUMNS, 0x0020 },
+ { ScTableProtection::INSERT_ROWS, 0x0040 },
+ { ScTableProtection::INSERT_HYPERLINKS, 0x0080 },
+
+ { ScTableProtection::DELETE_COLUMNS, 0x0100 },
+ { ScTableProtection::DELETE_ROWS, 0x0200 },
+ { ScTableProtection::SELECT_LOCKED_CELLS, 0x0400 },
+ { ScTableProtection::SORT, 0x0800 },
+ { ScTableProtection::AUTOFILTER, 0x1000 },
+ { ScTableProtection::PIVOT_TABLES, 0x2000 },
+ { ScTableProtection::SELECT_UNLOCKED_CELLS, 0x4000 },
+
+ { ScTableProtection::NONE, 0x0000 }
+ };
+
+ mnOptions = 0x0000;
+ const ScTableProtection* pProtect = rRoot.GetDoc().GetTabProtection(nTab);
+ if (!pProtect)
+ return;
+
+ for (int i = 0; aTable[i].nMask != 0x0000; ++i)
+ {
+ if ( pProtect->isOptionEnabled(aTable[i].eOption) )
+ mnOptions |= aTable[i].nMask;
+ }
+}
+
+void XclExpSheetProtectOptions::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nBytes = 0x0867;
+ rStrm << nBytes;
+
+ for (int i = 0; i < 9; ++i)
+ rStrm << static_cast<unsigned char>(0);
+
+ nBytes = 0x0200;
+ rStrm << nBytes;
+ nBytes = 0x0100;
+ rStrm << nBytes;
+ nBytes = 0xFFFF;
+ rStrm << nBytes << nBytes;
+
+ rStrm << mnOptions;
+ nBytes = 0;
+ rStrm << nBytes;
+}
+
+XclExpSheetEnhancedProtection::XclExpSheetEnhancedProtection( const XclExpRoot& rRoot,
+ ScEnhancedProtection aProt ) :
+ XclExpRecord( 0x0868 ),
+ mrRoot( rRoot ),
+ maEnhancedProtection(std::move( aProt ))
+{
+}
+
+void XclExpSheetEnhancedProtection::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 const nRecordType = 0x0868;
+ rStrm << nRecordType; // frtHeader rt
+ rStrm.WriteZeroBytesToRecord(10); // frtHeader unused
+ rStrm << EXC_ISFPROTECTION; // isf
+ rStrm.WriteZeroBytesToRecord(5); // reserved1 (1 bytes) and reserved2 (4 bytes)
+
+ XclRangeList aRefs;
+ if (maEnhancedProtection.maRangeList.is())
+ mrRoot.GetAddressConverter().ConvertRangeList( aRefs, *maEnhancedProtection.maRangeList, false);
+ sal_uInt16 nCref = ulimit_cast<sal_uInt16>(aRefs.size());
+ rStrm << nCref; // cref
+ rStrm.WriteZeroBytesToRecord(6); // cbFeatData if EXC_ISFFEC2 (4 bytes) and reserved3 (2 bytes)
+ aRefs.Write( rStrm, true, nCref); // refs
+
+ // FeatProtection structure
+ rStrm << maEnhancedProtection.mnAreserved; // 1 bit A and 31 bits reserved
+ rStrm << maEnhancedProtection.mnPasswordVerifier; // wPassword
+ rStrm << XclExpString( maEnhancedProtection.maTitle); // stTitle
+ bool bSDContainer = ((maEnhancedProtection.mnAreserved & 0x00000001) == 0x00000001);
+ sal_uInt32 nCbSD = maEnhancedProtection.maSecurityDescriptor.size();
+ SAL_WARN_IF( bSDContainer && nCbSD < 20, "sc.filter",
+ "XclExpSheetEnhancedProtection A flag indicates container but cbSD < 20");
+ SAL_WARN_IF( !bSDContainer && nCbSD > 0, "sc.filter",
+ "XclExpSheetEnhancedProtection A flag indicates no container but cbSD > 0");
+ if (bSDContainer)
+ {
+ rStrm << nCbSD;
+ rStrm.Write( &maEnhancedProtection.maSecurityDescriptor.front(), nCbSD);
+ }
+}
+
+void XclCalccount::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nCount;
+}
+
+XclCalccount::XclCalccount( const ScDocument& rDoc )
+{
+ nCount = rDoc.GetDocOptions().GetIterCount();
+}
+
+sal_uInt16 XclCalccount::GetNum() const
+{
+ return 0x000C;
+}
+
+std::size_t XclCalccount::GetLen() const
+{
+ return 2;
+}
+
+void XclCalccount::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_iterateCount, OUString::number(nCount));
+}
+
+void XclIteration::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nIter;
+}
+
+XclIteration::XclIteration( const ScDocument& rDoc )
+{
+ nIter = rDoc.GetDocOptions().IsIter()? 1 : 0;
+}
+
+sal_uInt16 XclIteration::GetNum() const
+{
+ return 0x0011;
+}
+
+std::size_t XclIteration::GetLen() const
+{
+ return 2;
+}
+
+void XclIteration::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_iterate, ToPsz(nIter == 1));
+}
+
+void XclDelta::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << fDelta;
+}
+
+XclDelta::XclDelta( const ScDocument& rDoc )
+{
+ fDelta = rDoc.GetDocOptions().GetIterEps();
+}
+
+sal_uInt16 XclDelta::GetNum() const
+{
+ return 0x0010;
+}
+
+std::size_t XclDelta::GetLen() const
+{
+ return 8;
+}
+
+void XclDelta::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_iterateDelta, OUString::number(fDelta));
+}
+
+XclExpFileEncryption::XclExpFileEncryption( const XclExpRoot& rRoot ) :
+ XclExpRecord(0x002F, 54),
+ mrRoot(rRoot)
+{
+}
+
+XclExpFileEncryption::~XclExpFileEncryption()
+{
+}
+
+void XclExpFileEncryption::WriteBody( XclExpStream& rStrm )
+{
+ // 0x0000 - neither standard nor strong encryption
+ // 0x0001 - standard or strong encryption
+ rStrm << static_cast<sal_uInt16>(0x0001);
+
+ // 0x0000 - non standard encryption
+ // 0x0001 - standard encryption
+ sal_uInt16 nStdEnc = 0x0001;
+ rStrm << nStdEnc << nStdEnc;
+
+ sal_uInt8 pnDocId[16];
+ sal_uInt8 pnSalt[16];
+ sal_uInt8 pnSaltHash[16];
+ XclExpEncrypterRef xEnc = std::make_shared<XclExpBiff8Encrypter>(mrRoot);
+ xEnc->GetDocId(pnDocId);
+ xEnc->GetSalt(pnSalt);
+ xEnc->GetSaltDigest(pnSaltHash);
+
+ rStrm.Write(pnDocId, 16);
+ rStrm.Write(pnSalt, 16);
+ rStrm.Write(pnSaltHash, 16);
+
+ rStrm.SetEncrypter(xEnc);
+}
+
+XclExpInterfaceHdr::XclExpInterfaceHdr( sal_uInt16 nCodePage ) :
+ XclExpUInt16Record( EXC_ID_INTERFACEHDR, nCodePage )
+{
+}
+
+void XclExpInterfaceHdr::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.DisableEncryption();
+ rStrm << GetValue();
+}
+
+XclExpInterfaceEnd::XclExpInterfaceEnd() :
+ XclExpRecord(0x00E2, 0) {}
+
+XclExpInterfaceEnd::~XclExpInterfaceEnd() {}
+
+void XclExpInterfaceEnd::WriteBody( XclExpStream& rStrm )
+{
+ // Don't forget to re-enable encryption.
+ rStrm.EnableEncryption();
+}
+
+XclExpWriteAccess::XclExpWriteAccess() :
+ XclExpRecord(0x005C, 112)
+{
+}
+
+XclExpWriteAccess::~XclExpWriteAccess()
+{
+}
+
+void XclExpWriteAccess::WriteBody( XclExpStream& rStrm )
+{
+ static const sal_uInt8 aData[] = {
+ 0x04, 0x00, 0x00, 'C', 'a', 'l', 'c', 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+
+ for (std::size_t i = 0; i < sizeof(aData); ++i)
+ rStrm << aData[i];
+}
+
+XclExpFileSharing::XclExpFileSharing( const XclExpRoot& rRoot, sal_uInt16 nPasswordHash, bool bRecommendReadOnly ) :
+ XclExpRecord( EXC_ID_FILESHARING ),
+ mnPasswordHash( nPasswordHash ),
+ mbRecommendReadOnly( bRecommendReadOnly )
+{
+ if( rRoot.GetBiff() <= EXC_BIFF5 )
+ maUserName.AssignByte( rRoot.GetUserName(), rRoot.GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ maUserName.Assign( rRoot.GetUserName() );
+}
+
+void XclExpFileSharing::Save( XclExpStream& rStrm )
+{
+ if( (mnPasswordHash != 0) || mbRecommendReadOnly )
+ XclExpRecord::Save( rStrm );
+}
+
+void XclExpFileSharing::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( mbRecommendReadOnly ? 1 : 0 ) << mnPasswordHash << maUserName;
+}
+
+XclExpProt4Rev::XclExpProt4Rev() :
+ XclExpRecord(0x01AF, 2)
+{
+}
+
+XclExpProt4Rev::~XclExpProt4Rev()
+{
+}
+
+void XclExpProt4Rev::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(0x0000);
+}
+
+XclExpProt4RevPass::XclExpProt4RevPass() :
+ XclExpRecord(0x01BC, 2)
+{
+}
+
+XclExpProt4RevPass::~XclExpProt4RevPass()
+{
+}
+
+void XclExpProt4RevPass::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(0x0000);
+}
+
+const sal_uInt8 nDataRecalcId[] = {
+ 0xC1, 0x01, 0x00, 0x00, 0x54, 0x8D, 0x01, 0x00
+};
+
+XclExpRecalcId::XclExpRecalcId() :
+ XclExpDummyRecord(0x01C1, nDataRecalcId, sizeof(nDataRecalcId))
+{
+}
+
+const sal_uInt8 nDataBookExt[] = {
+ 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02
+};
+
+XclExpBookExt::XclExpBookExt() :
+ XclExpDummyRecord(0x0863, nDataBookExt, sizeof(nDataBookExt))
+{
+}
+
+XclRefmode::XclRefmode( const ScDocument& rDoc ) :
+ XclExpBoolRecord( 0x000F, rDoc.GetAddressConvention() != formula::FormulaGrammar::CONV_XL_R1C1 )
+{
+}
+
+void XclRefmode::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_refMode, GetBool() ? "A1" : "R1C1");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsExport.cxx b/sc/source/filter/xml/SparklineGroupsExport.cxx
new file mode 100644
index 0000000000..0a652c2256
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsExport.cxx
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "SparklineGroupsExport.hxx"
+#include "xmlexprt.hxx"
+#include <rangeutl.hxx>
+#include <SparklineList.hxx>
+#include <document.hxx>
+
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sax/tools/converter.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <xmloff/XMLComplexColorExport.hxx>
+
+using namespace css;
+using namespace xmloff::token;
+
+namespace sc
+{
+SparklineGroupsExport::SparklineGroupsExport(ScXMLExport& rExport, SCTAB nTable)
+ : m_rExport(rExport)
+ , m_nTable(nTable)
+{
+}
+
+void SparklineGroupsExport::insertColor(model::ComplexColor const& rComplexColor,
+ XMLTokenEnum eToken)
+{
+ if (rComplexColor.getType() == model::ColorType::Unused)
+ return;
+
+ OUStringBuffer aStringBuffer;
+ sax::Converter::convertColor(aStringBuffer, rComplexColor.getFinalColor());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, eToken, aStringBuffer.makeStringAndClear());
+}
+
+void SparklineGroupsExport::insertComplexColor(model::ComplexColor const& rComplexColor,
+ XMLTokenEnum eToken)
+{
+ if (!rComplexColor.isValidThemeType())
+ return;
+ XMLComplexColorExport aComplexColorExport(m_rExport);
+ aComplexColorExport.exportComplexColor(rComplexColor, XML_NAMESPACE_CALC_EXT, eToken);
+}
+
+void SparklineGroupsExport::insertBool(bool bValue, XMLTokenEnum eToken)
+{
+ if (bValue)
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, eToken, "true");
+}
+
+void SparklineGroupsExport::addSparklineAttributes(Sparkline const& rSparkline)
+{
+ auto const* pDocument = m_rExport.GetDocument();
+
+ {
+ OUString sAddressString;
+ ScAddress aAddress(rSparkline.getColumn(), rSparkline.getRow(), m_nTable);
+ ScRangeStringConverter::GetStringFromAddress(sAddressString, aAddress, pDocument,
+ formula::FormulaGrammar::CONV_OOO);
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CELL_ADDRESS, sAddressString);
+ }
+
+ {
+ OUString sDataRangeString;
+ ScRangeList const& rRangeList = rSparkline.getInputRange();
+ ScRangeStringConverter::GetStringFromRangeList(sDataRangeString, &rRangeList, pDocument,
+ formula::FormulaGrammar::CONV_OOO);
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATA_RANGE, sDataRangeString);
+ }
+}
+
+namespace
+{
+OUString convertSparklineType(sc::SparklineType eType)
+{
+ switch (eType)
+ {
+ case sc::SparklineType::Line:
+ return u"line"_ustr;
+ case sc::SparklineType::Column:
+ return u"column"_ustr;
+ case sc::SparklineType::Stacked:
+ return u"stacked"_ustr;
+ }
+ return u""_ustr;
+}
+
+OUString convertDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs eType)
+{
+ switch (eType)
+ {
+ case sc::DisplayEmptyCellsAs::Zero:
+ return u"zero"_ustr;
+ case sc::DisplayEmptyCellsAs::Gap:
+ return u"gap"_ustr;
+ case sc::DisplayEmptyCellsAs::Span:
+ return u"span"_ustr;
+ }
+ return u""_ustr;
+}
+
+OUString convertAxisType(sc::AxisType eType)
+{
+ switch (eType)
+ {
+ case sc::AxisType::Individual:
+ return u"individual"_ustr;
+ case sc::AxisType::Group:
+ return u"group"_ustr;
+ case sc::AxisType::Custom:
+ return u"custom"_ustr;
+ }
+ return u""_ustr;
+}
+
+} // end anonymous ns
+
+void SparklineGroupsExport::addSparklineGroupAttributes(SparklineAttributes const& rAttributes)
+{
+ OUString sType = convertSparklineType(rAttributes.getType());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, sType);
+
+ // Line Weight = Line Width in ODF
+
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_LINE_WIDTH,
+ OUString::number(rAttributes.getLineWeight()) + "pt");
+
+ insertBool(rAttributes.isDateAxis(), XML_DATE_AXIS);
+
+ OUString sDisplayEmptyCellsAs
+ = convertDisplayEmptyCellsAs(rAttributes.getDisplayEmptyCellsAs());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DISPLAY_EMPTY_CELLS_AS,
+ sDisplayEmptyCellsAs);
+
+ insertBool(rAttributes.isMarkers(), XML_MARKERS);
+ insertBool(rAttributes.isHigh(), XML_HIGH);
+ insertBool(rAttributes.isLow(), XML_LOW);
+ insertBool(rAttributes.isFirst(), XML_FIRST);
+ insertBool(rAttributes.isLast(), XML_LAST);
+ insertBool(rAttributes.isNegative(), XML_NEGATIVE);
+ insertBool(rAttributes.shouldDisplayXAxis(), XML_DISPLAY_X_AXIS);
+ insertBool(rAttributes.shouldDisplayHidden(), XML_DISPLAY_HIDDEN);
+
+ OUString sMinAxisType = convertAxisType(rAttributes.getMinAxisType());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MIN_AXIS_TYPE, sMinAxisType);
+
+ OUString sMaxAxisType = convertAxisType(rAttributes.getMaxAxisType());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MAX_AXIS_TYPE, sMaxAxisType);
+
+ insertBool(rAttributes.isRightToLeft(), XML_RIGHT_TO_LEFT);
+
+ if (rAttributes.getManualMax() && rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MANUAL_MAX,
+ OUString::number(*rAttributes.getManualMax()));
+
+ if (rAttributes.getManualMin() && rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MANUAL_MIN,
+ OUString::number(*rAttributes.getManualMin()));
+
+ insertColor(rAttributes.getColorSeries(), XML_COLOR_SERIES);
+ insertColor(rAttributes.getColorNegative(), XML_COLOR_NEGATIVE);
+ insertColor(rAttributes.getColorAxis(), XML_COLOR_AXIS);
+ insertColor(rAttributes.getColorMarkers(), XML_COLOR_MARKERS);
+ insertColor(rAttributes.getColorFirst(), XML_COLOR_FIRST);
+ insertColor(rAttributes.getColorLast(), XML_COLOR_LAST);
+ insertColor(rAttributes.getColorHigh(), XML_COLOR_HIGH);
+ insertColor(rAttributes.getColorLow(), XML_COLOR_LOW);
+}
+
+void SparklineGroupsExport::addSparklineGroup(
+ std::shared_ptr<SparklineGroup> const& pSparklineGroup,
+ std::vector<std::shared_ptr<Sparkline>> const& rSparklines)
+{
+ auto const& rAttributes = pSparklineGroup->getAttributes();
+
+ OUString sID = pSparklineGroup->getID().getOUString();
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_ID, sID);
+
+ addSparklineGroupAttributes(rAttributes);
+
+ SvXMLElementExport aElementSparklineGroup(m_rExport, XML_NAMESPACE_CALC_EXT,
+ XML_SPARKLINE_GROUP, true, true);
+
+ insertComplexColor(rAttributes.getColorSeries(), XML_SPARKLINE_SERIES_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorNegative(), XML_SPARKLINE_NEGATIVE_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorAxis(), XML_SPARKLINE_AXIS_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorMarkers(), XML_SPARKLINE_MARKERS_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorFirst(), XML_SPARKLINE_FIRST_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorLast(), XML_SPARKLINE_LAST_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorHigh(), XML_SPARKLINE_HIGH_COMPLEX_COLOR);
+ insertComplexColor(rAttributes.getColorLow(), XML_SPARKLINE_LOW_COMPLEX_COLOR);
+
+ SvXMLElementExport aElementSparklines(m_rExport, XML_NAMESPACE_CALC_EXT, XML_SPARKLINES, true,
+ true);
+
+ for (auto const& rSparkline : rSparklines)
+ {
+ addSparklineAttributes(*rSparkline);
+ SvXMLElementExport aElementSparkline(m_rExport, XML_NAMESPACE_CALC_EXT, XML_SPARKLINE, true,
+ true);
+ }
+}
+
+void SparklineGroupsExport::write()
+{
+ auto* pDocument = m_rExport.GetDocument();
+ if (sc::SparklineList* pSparklineList = pDocument->GetSparklineList(m_nTable))
+ {
+ auto const& aSparklineGroups = pSparklineList->getSparklineGroups();
+ if (!aSparklineGroups.empty())
+ {
+ SvXMLElementExport aElement(m_rExport, XML_NAMESPACE_CALC_EXT, XML_SPARKLINE_GROUPS,
+ true, true);
+
+ for (auto const& pSparklineGroup : aSparklineGroups)
+ {
+ auto const& aSparklines = pSparklineList->getSparklinesFor(pSparklineGroup);
+ addSparklineGroup(pSparklineGroup, aSparklines);
+ }
+ }
+ }
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsExport.hxx b/sc/source/filter/xml/SparklineGroupsExport.hxx
new file mode 100644
index 0000000000..a2ed4a8ef8
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsExport.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <tools/color.hxx>
+#include <xmloff/xmltoken.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+
+class ScXMLExport;
+namespace model
+{
+class ComplexColor;
+}
+
+namespace sc
+{
+/** Handle the export of sparkline groups and sparklines */
+class SparklineGroupsExport
+{
+ ScXMLExport& m_rExport;
+ SCTAB m_nTable;
+
+ void addSparklineGroupAttributes(sc::SparklineAttributes const& rAttributes);
+ void addSparklineGroup(std::shared_ptr<SparklineGroup> const& pSparklineGroup,
+ std::vector<std::shared_ptr<Sparkline>> const& rSparklines);
+ void addSparklineAttributes(Sparkline const& rSparkline);
+
+ void insertColor(model::ComplexColor const& rComplexColor, xmloff::token::XMLTokenEnum eToken);
+ void insertComplexColor(model::ComplexColor const& rComplexColor,
+ xmloff::token::XMLTokenEnum eToken);
+
+ void insertBool(bool bValue, xmloff::token::XMLTokenEnum eToken);
+
+public:
+ SparklineGroupsExport(ScXMLExport& rExport, SCTAB nTable);
+
+ void write();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsImportContext.cxx b/sc/source/filter/xml/SparklineGroupsImportContext.cxx
new file mode 100644
index 0000000000..4f85ae1108
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsImportContext.cxx
@@ -0,0 +1,405 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SparklineGroupsImportContext.hxx"
+
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/XMLComplexColorContext.hxx>
+
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+
+using namespace xmloff::token;
+using namespace css;
+
+namespace sc
+{
+SparklineGroupsImportContext::SparklineGroupsImportContext(ScXMLImport& rImport)
+ : ScXMLImportContext(rImport)
+{
+}
+
+namespace
+{
+sc::SparklineType parseSparklineType(std::string_view aString)
+{
+ if (aString == "column")
+ return sc::SparklineType::Column;
+ else if (aString == "stacked")
+ return sc::SparklineType::Stacked;
+ return sc::SparklineType::Line;
+}
+
+sc::DisplayEmptyCellsAs parseDisplayEmptyCellsAs(std::string_view aString)
+{
+ if (aString == "span")
+ return sc::DisplayEmptyCellsAs::Span;
+ else if (aString == "gap")
+ return sc::DisplayEmptyCellsAs::Gap;
+ return sc::DisplayEmptyCellsAs::Zero;
+}
+
+sc::AxisType parseAxisType(std::string_view aString)
+{
+ if (aString == "group")
+ return sc::AxisType::Group;
+ else if (aString == "custom")
+ return sc::AxisType::Custom;
+ return sc::AxisType::Individual;
+}
+
+} // end anonymous namespace
+
+void SparklineGroupsImportContext::fillSparklineGroupID(
+ uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_ID):
+ {
+ tools::Guid aGuid(rIter.toView());
+ m_pCurrentSparklineGroup->setID(aGuid);
+ break;
+ }
+ }
+ }
+}
+
+void SparklineGroupsImportContext::fillSparklineGroupAttributes(
+ uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ sc::SparklineAttributes& rAttributes = m_pCurrentSparklineGroup->getAttributes();
+
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ rAttributes.setType(parseSparklineType(rIter.toView()));
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_LINE_WIDTH):
+ {
+ OUString sLineWidth = rIter.toString();
+ double fLineWidth;
+ sal_Int16 const eSrcUnit
+ = ::sax::Converter::GetUnitFromString(sLineWidth, util::MeasureUnit::POINT);
+ ::sax::Converter::convertDouble(fLineWidth, sLineWidth, eSrcUnit,
+ util::MeasureUnit::POINT);
+ rAttributes.setLineWeight(fLineWidth);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DATE_AXIS):
+ {
+ rAttributes.setDateAxis(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DISPLAY_EMPTY_CELLS_AS):
+ {
+ auto eDisplayEmptyCellsAs = parseDisplayEmptyCellsAs(rIter.toView());
+ rAttributes.setDisplayEmptyCellsAs(eDisplayEmptyCellsAs);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MARKERS):
+ {
+ rAttributes.setMarkers(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_HIGH):
+ {
+ rAttributes.setHigh(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_LOW):
+ {
+ rAttributes.setLow(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_FIRST):
+ {
+ rAttributes.setFirst(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_LAST):
+ {
+ rAttributes.setLast(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_NEGATIVE):
+ {
+ rAttributes.setNegative(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DISPLAY_X_AXIS):
+ {
+ rAttributes.setDisplayXAxis(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DISPLAY_HIDDEN):
+ {
+ rAttributes.setDisplayHidden(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MIN_AXIS_TYPE):
+ {
+ rAttributes.setMinAxisType(parseAxisType(rIter.toView()));
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MAX_AXIS_TYPE):
+ {
+ rAttributes.setMaxAxisType(parseAxisType(rIter.toView()));
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_RIGHT_TO_LEFT):
+ {
+ rAttributes.setRightToLeft(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MANUAL_MAX):
+ {
+ rAttributes.setManualMax(rIter.toDouble());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MANUAL_MIN):
+ {
+ rAttributes.setManualMin(rIter.toDouble());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_SERIES):
+ {
+ maSeriesColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maSeriesColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_NEGATIVE):
+ {
+ maNegativeColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maNegativeColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_AXIS):
+ {
+ maAxisColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maAxisColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_MARKERS):
+ {
+ maMarkersColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maMarkersColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_FIRST):
+ {
+ maFirstColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maFirstColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_LAST):
+ {
+ maLastColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maLastColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_HIGH):
+ {
+ maHighColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maHighColor, rIter.toView());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_LOW):
+ {
+ maLowColor = COL_TRANSPARENT;
+ sax::Converter::convertColor(maLowColor, rIter.toView());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void SparklineGroupsImportContext::fillSparklineAttributes(
+ SparklineImportData& rImportData, uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ ScDocument* pDocument = GetScImport().GetDocument();
+
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_CELL_ADDRESS):
+ {
+ sal_Int32 nOffset = 0;
+ ScRangeStringConverter::GetAddressFromString(
+ rImportData.m_aAddress, rIter.toString(), *pDocument,
+ formula::FormulaGrammar::CONV_OOO, nOffset);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DATA_RANGE):
+ {
+ ScRangeStringConverter::GetRangeListFromString(rImportData.m_aDataRangeList,
+ rIter.toString(), *pDocument,
+ formula::FormulaGrammar::CONV_OOO);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL SparklineGroupsImportContext::createFastChildContext(
+ sal_Int32 nElement, uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ SvXMLImportContext* pContext = nullptr;
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_GROUP):
+ {
+ m_pCurrentSparklineGroup = std::make_shared<sc::SparklineGroup>();
+ maAxisComplexColor = model::ComplexColor();
+ maFirstComplexColor = model::ComplexColor();
+ maLastComplexColor = model::ComplexColor();
+ maHighComplexColor = model::ComplexColor();
+ maLowComplexColor = model::ComplexColor();
+ maSeriesComplexColor = model::ComplexColor();
+ maNegativeComplexColor = model::ComplexColor();
+ maMarkersComplexColor = model::ComplexColor();
+
+ fillSparklineGroupID(xAttrList);
+ fillSparklineGroupAttributes(xAttrList);
+ pContext = this;
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINES):
+ {
+ pContext = this;
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE):
+ {
+ SparklineImportData& rImportData = m_aCurrentSparklineDataList.emplace_back();
+ fillSparklineAttributes(rImportData, xAttrList);
+ pContext = this;
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_AXIS_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maAxisComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_FIRST_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maFirstComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_LAST_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maLastComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_HIGH_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maHighComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_LOW_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maLowComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_SERIES_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maSeriesComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_NEGATIVE_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maNegativeComplexColor, xAttrList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_MARKERS_COMPLEX_COLOR):
+ {
+ pContext = new XMLComplexColorContext(GetImport(), maMarkersComplexColor, xAttrList);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SparklineGroupsImportContext::insertSparklines()
+{
+ ScDocument* pDocument = GetScImport().GetDocument();
+ for (auto const& rSparklineImportData : m_aCurrentSparklineDataList)
+ {
+ auto* pSparkline
+ = pDocument->CreateSparkline(rSparklineImportData.m_aAddress, m_pCurrentSparklineGroup);
+ pSparkline->setInputRange(rSparklineImportData.m_aDataRangeList);
+ }
+}
+
+namespace
+{
+model::ComplexColor combineComplexColorAndColor(model::ComplexColor& rComplexColor, Color aColor)
+{
+ if (rComplexColor.getType() != model::ColorType::Unused)
+ rComplexColor.setFinalColor(aColor);
+ else if (aColor != COL_TRANSPARENT)
+ rComplexColor = model::ComplexColor::createRGB(aColor);
+ return rComplexColor;
+}
+} // end anonymous namespace
+
+void SAL_CALL SparklineGroupsImportContext::endFastElement(sal_Int32 nElement)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_GROUP):
+ {
+ sc::SparklineAttributes& rAttributes = m_pCurrentSparklineGroup->getAttributes();
+ {
+ rAttributes.setColorAxis(
+ combineComplexColorAndColor(maAxisComplexColor, maAxisColor));
+ rAttributes.setColorFirst(
+ combineComplexColorAndColor(maFirstComplexColor, maFirstColor));
+ rAttributes.setColorLast(
+ combineComplexColorAndColor(maLastComplexColor, maLastColor));
+ rAttributes.setColorHigh(
+ combineComplexColorAndColor(maHighComplexColor, maHighColor));
+ rAttributes.setColorLow(combineComplexColorAndColor(maLowComplexColor, maLowColor));
+ rAttributes.setColorSeries(
+ combineComplexColorAndColor(maSeriesComplexColor, maSeriesColor));
+ rAttributes.setColorNegative(
+ combineComplexColorAndColor(maNegativeComplexColor, maNegativeColor));
+ rAttributes.setColorMarkers(
+ combineComplexColorAndColor(maMarkersComplexColor, maMarkersColor));
+ }
+ insertSparklines();
+ m_pCurrentSparklineGroup.reset();
+ m_aCurrentSparklineDataList.clear();
+ break;
+ }
+ }
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsImportContext.hxx b/sc/source/filter/xml/SparklineGroupsImportContext.hxx
new file mode 100644
index 0000000000..197eca9ca0
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsImportContext.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/.
+ */
+
+#pragma once
+
+#include <memory>
+#include "importcontext.hxx"
+#include "xmlimprt.hxx"
+#include <address.hxx>
+#include <rangelst.hxx>
+#include <docmodel/color/ComplexColor.hxx>
+
+namespace sax_fastparser
+{
+class FastAttributeList;
+}
+
+namespace sc
+{
+class SparklineGroup;
+
+/** Transitional import data of a sparkline */
+struct SparklineImportData
+{
+ ScAddress m_aAddress;
+ ScRangeList m_aDataRangeList;
+};
+
+/** Handle the import of sparkline groups and sparklines */
+class SparklineGroupsImportContext : public ScXMLImportContext
+{
+private:
+ std::shared_ptr<sc::SparklineGroup> m_pCurrentSparklineGroup;
+ std::vector<SparklineImportData> m_aCurrentSparklineDataList;
+
+ model::ComplexColor maAxisComplexColor;
+ model::ComplexColor maFirstComplexColor;
+ model::ComplexColor maLastComplexColor;
+ model::ComplexColor maHighComplexColor;
+ model::ComplexColor maLowComplexColor;
+ model::ComplexColor maSeriesComplexColor;
+ model::ComplexColor maNegativeComplexColor;
+ model::ComplexColor maMarkersComplexColor;
+
+ Color maAxisColor = COL_TRANSPARENT;
+ Color maFirstColor = COL_TRANSPARENT;
+ Color maLastColor = COL_TRANSPARENT;
+ Color maHighColor = COL_TRANSPARENT;
+ Color maLowColor = COL_TRANSPARENT;
+ Color maSeriesColor = COL_TRANSPARENT;
+ Color maNegativeColor = COL_TRANSPARENT;
+ Color maMarkersColor = COL_TRANSPARENT;
+
+ void
+ fillSparklineGroupID(css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList);
+ void fillSparklineGroupAttributes(
+ css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList);
+ void fillSparklineAttributes(
+ SparklineImportData& rImportData,
+ css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList);
+
+ void insertSparklines();
+
+public:
+ SparklineGroupsImportContext(ScXMLImport& rImport);
+
+ css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCalculationSettingsContext.cxx b/sc/source/filter/xml/XMLCalculationSettingsContext.cxx
new file mode 100644
index 0000000000..645da6c597
--- /dev/null
+++ b/sc/source/filter/xml/XMLCalculationSettingsContext.cxx
@@ -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 .
+ */
+
+#include "XMLCalculationSettingsContext.hxx"
+#include "xmlimprt.hxx"
+#include <unonames.hxx>
+#include <docoptio.hxx>
+#include <document.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sax/tools/converter.hxx>
+#include <docuno.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLCalculationSettingsContext::ScXMLCalculationSettingsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ fIterationEpsilon(0.001),
+ nIterationCount(100),
+ nYear2000(1930),
+ eSearchType(utl::SearchParam::SearchType::Regexp),
+ bIsIterationEnabled(false),
+ bCalcAsShown(false),
+ bIgnoreCase(false),
+ bLookUpLabels(true),
+ bMatchWholeCell(true)
+{
+ aNullDate.Day = 30;
+ aNullDate.Month = 12;
+ aNullDate.Year = 1899;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ if( IsXMLToken( aIter, XML_FALSE ) )
+ bIgnoreCase = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_PRECISION_AS_SHOWN ):
+ if( IsXMLToken( aIter, XML_TRUE ) )
+ bCalcAsShown = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_SEARCH_CRITERIA_MUST_APPLY_TO_WHOLE_CELL ):
+ if( IsXMLToken( aIter, XML_FALSE ) )
+ bMatchWholeCell = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_AUTOMATIC_FIND_LABELS ):
+ if( IsXMLToken( aIter, XML_FALSE ) )
+ bLookUpLabels = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_NULL_YEAR ):
+ sal_Int32 nTemp;
+ ::sax::Converter::convertNumber( nTemp, aIter.toView() );
+ nYear2000 = static_cast<sal_uInt16>(nTemp);
+ break;
+ case XML_ELEMENT( TABLE, XML_USE_REGULAR_EXPRESSIONS ):
+ // Overwrite only the default (regex true) value, not wildcard.
+ if( eSearchType == utl::SearchParam::SearchType::Regexp && IsXMLToken( aIter, XML_FALSE ) )
+ eSearchType = utl::SearchParam::SearchType::Normal;
+ break;
+ case XML_ELEMENT( TABLE, XML_USE_WILDCARDS ):
+ if( IsXMLToken( aIter, XML_TRUE ) )
+ eSearchType = utl::SearchParam::SearchType::Wildcard;
+ break;
+ }
+ }
+}
+
+ScXMLCalculationSettingsContext::~ScXMLCalculationSettingsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCalculationSettingsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_NULL_DATE ))
+ pContext = new ScXMLNullDateContext(GetScImport(), pAttribList, this);
+ else if (nElement == XML_ELEMENT( TABLE, XML_ITERATION ))
+ pContext = new ScXMLIterationContext(GetScImport(), pAttribList, this);
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLCalculationSettingsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScModelObj* xPropertySet(GetScImport().GetScModel());
+ if (!xPropertySet)
+ return;
+
+ xPropertySet->setPropertyValue( SC_UNO_CALCASSHOWN, uno::Any(bCalcAsShown) );
+ xPropertySet->setPropertyValue( SC_UNO_IGNORECASE, uno::Any(bIgnoreCase) );
+ xPropertySet->setPropertyValue( SC_UNO_LOOKUPLABELS, uno::Any(bLookUpLabels) );
+ xPropertySet->setPropertyValue( SC_UNO_MATCHWHOLE, uno::Any(bMatchWholeCell) );
+ bool bWildcards, bRegex;
+ utl::SearchParam::ConvertToBool( eSearchType, bWildcards, bRegex);
+ xPropertySet->setPropertyValue( SC_UNO_REGEXENABLED, uno::Any(bRegex) );
+ xPropertySet->setPropertyValue( SC_UNO_WILDCARDSENABLED, uno::Any(bWildcards) );
+ xPropertySet->setPropertyValue( SC_UNO_ITERENABLED, uno::Any(bIsIterationEnabled) );
+ xPropertySet->setPropertyValue( SC_UNO_ITERCOUNT, uno::Any(nIterationCount) );
+ xPropertySet->setPropertyValue( SC_UNO_ITEREPSILON, uno::Any(fIterationEpsilon) );
+ xPropertySet->setPropertyValue( SC_UNO_NULLDATE, uno::Any(aNullDate) );
+ if (GetScImport().GetDocument())
+ {
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScDocOptions aDocOptions (GetScImport().GetDocument()->GetDocOptions());
+ aDocOptions.SetYear2000(nYear2000);
+ GetScImport().GetDocument()->SetDocOptions(aDocOptions);
+ }
+}
+
+ScXMLNullDateContext::ScXMLNullDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLCalculationSettingsContext* pCalcSet) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_DATE_VALUE ) ) );
+ if (aIter != rAttrList->end())
+ {
+ util::DateTime aDateTime;
+ if (::sax::Converter::parseDateTime(aDateTime, aIter.toView()))
+ {
+ util::Date aDate;
+ aDate.Day = aDateTime.Day;
+ aDate.Month = aDateTime.Month;
+ aDate.Year = aDateTime.Year;
+ pCalcSet->SetNullDate(aDate);
+ }
+ else
+ {
+ SAL_WARN("sc.filter","ignoring invalid NullDate '" << aIter.toView() << "'");
+ }
+ }
+}
+
+ScXMLNullDateContext::~ScXMLNullDateContext()
+{
+}
+
+ScXMLIterationContext::ScXMLIterationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLCalculationSettingsContext* pCalcSet) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_STATUS ):
+ if (IsXMLToken(aIter, XML_ENABLE))
+ pCalcSet->SetIterationStatus(true);
+ break;
+ case XML_ELEMENT( TABLE, XML_STEPS ):
+ pCalcSet->SetIterationCount(aIter.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_MAXIMUM_DIFFERENCE ):
+ pCalcSet->SetIterationEpsilon( aIter.toDouble() );
+ break;
+ }
+ }
+}
+
+ScXMLIterationContext::~ScXMLIterationContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCalculationSettingsContext.hxx b/sc/source/filter/xml/XMLCalculationSettingsContext.hxx
new file mode 100644
index 0000000000..45bcdfb6c0
--- /dev/null
+++ b/sc/source/filter/xml/XMLCalculationSettingsContext.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 .
+ */
+
+#pragma once
+
+#include <unotools/textsearch.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+
+class ScXMLCalculationSettingsContext : public ScXMLImportContext
+{
+ css::util::Date aNullDate;
+ double fIterationEpsilon;
+ sal_Int32 nIterationCount;
+ sal_uInt16 nYear2000;
+ utl::SearchParam::SearchType eSearchType;
+ bool bIsIterationEnabled;
+ bool bCalcAsShown;
+ bool bIgnoreCase;
+ bool bLookUpLabels;
+ bool bMatchWholeCell;
+
+public:
+ ScXMLCalculationSettingsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLCalculationSettingsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SetNullDate(const css::util::Date& aDate) { aNullDate = aDate; }
+ void SetIterationStatus(const bool bValue) { bIsIterationEnabled = bValue; }
+ void SetIterationCount(const sal_Int32 nValue) { nIterationCount = nValue; }
+ void SetIterationEpsilon(const double fValue) { fIterationEpsilon = fValue; }
+ virtual void SAL_CALL endFastElement( sal_Int32 Element ) override;
+};
+
+class ScXMLNullDateContext : public ScXMLImportContext
+{
+public:
+ ScXMLNullDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLCalculationSettingsContext* pCalcSet);
+
+ virtual ~ScXMLNullDateContext() override;
+};
+
+class ScXMLIterationContext : public ScXMLImportContext
+{
+public:
+ ScXMLIterationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLCalculationSettingsContext* pCalcSet);
+
+ virtual ~ScXMLIterationContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCellRangeSourceContext.cxx b/sc/source/filter/xml/XMLCellRangeSourceContext.cxx
new file mode 100644
index 0000000000..72bdc7b906
--- /dev/null
+++ b/sc/source/filter/xml/XMLCellRangeSourceContext.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 "XMLCellRangeSourceContext.hxx"
+
+#include <sax/tools/converter.hxx>
+
+#include "xmlimprt.hxx"
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScMyImpCellRangeSource::ScMyImpCellRangeSource() :
+ nColumns( 0 ),
+ nRows( 0 ),
+ nRefresh( 0 )
+{
+}
+
+ScXMLCellRangeSourceContext::ScXMLCellRangeSourceContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpCellRangeSource* pCellRangeSource ) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ pCellRangeSource->sSourceStr = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_NAME ):
+ pCellRangeSource->sFilterName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OPTIONS ):
+ pCellRangeSource->sFilterOptions = aIter.toString();
+ break;
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ pCellRangeSource->sURL = GetScImport().GetAbsoluteReference(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_LAST_COLUMN_SPANNED ):
+ {
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber( nValue, aIter.toView(), 1 ))
+ pCellRangeSource->nColumns = nValue;
+ else
+ pCellRangeSource->nColumns = 1;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_LAST_ROW_SPANNED ):
+ {
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber( nValue, aIter.toView(), 1 ))
+ pCellRangeSource->nRows = nValue;
+ else
+ pCellRangeSource->nRows = 1;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_REFRESH_DELAY ):
+ {
+ double fTime;
+ if (::sax::Converter::convertDuration( fTime, aIter.toView() ))
+ pCellRangeSource->nRefresh = std::max( static_cast<sal_Int32>(fTime * 86400.0), sal_Int32(0) );
+ }
+ break;
+ }
+ }
+}
+
+ScXMLCellRangeSourceContext::~ScXMLCellRangeSourceContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCellRangeSourceContext.hxx b/sc/source/filter/xml/XMLCellRangeSourceContext.hxx
new file mode 100644
index 0000000000..9b6fa18d1a
--- /dev/null
+++ b/sc/source/filter/xml/XMLCellRangeSourceContext.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 "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+
+struct ScMyImpCellRangeSource
+{
+ OUString sSourceStr;
+ OUString sFilterName;
+ OUString sFilterOptions;
+ OUString sURL;
+ sal_Int32 nColumns;
+ sal_Int32 nRows;
+ sal_Int32 nRefresh;
+
+ ScMyImpCellRangeSource();
+};
+
+class ScXMLCellRangeSourceContext : public ScXMLImportContext
+{
+public:
+ ScXMLCellRangeSourceContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpCellRangeSource* pCellRangeSource
+ );
+ virtual ~ScXMLCellRangeSourceContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx b/sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx
new file mode 100644
index 0000000000..1a3b22e6aa
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx
@@ -0,0 +1,687 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLChangeTrackingExportHelper.hxx"
+#include "xmlexprt.hxx"
+#include "XMLConverter.hxx"
+#include <document.hxx>
+#include <chgtrack.hxx>
+#include <formulacell.hxx>
+#include <textuno.hxx>
+#include <rangeutl.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstring.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScChangeTrackingExportHelper::ScChangeTrackingExportHelper(ScXMLExport& rTempExport)
+ : rExport(rTempExport),
+ pChangeTrack(nullptr)
+{
+ pChangeTrack = rExport.GetDocument() ? rExport.GetDocument()->GetChangeTrack() : nullptr;
+}
+
+ScChangeTrackingExportHelper::~ScChangeTrackingExportHelper()
+{
+}
+
+OUString ScChangeTrackingExportHelper::GetChangeID(const sal_uInt32 nActionNumber)
+{
+ return "ct" + OUString::number(nActionNumber);
+}
+
+void ScChangeTrackingExportHelper::GetAcceptanceState(const ScChangeAction* pAction)
+{
+ if (pAction->IsRejected())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ACCEPTANCE_STATE, XML_REJECTED);
+ else if (pAction->IsAccepted())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ACCEPTANCE_STATE, XML_ACCEPTED);
+}
+
+void ScChangeTrackingExportHelper::WriteBigRange(const ScBigRange& rBigRange, XMLTokenEnum aName)
+{
+ sal_Int64 nStartColumn;
+ sal_Int64 nEndColumn;
+ sal_Int64 nStartRow;
+ sal_Int64 nEndRow;
+ sal_Int64 nStartSheet;
+ sal_Int64 nEndSheet;
+ rBigRange.GetVars(nStartColumn, nStartRow, nStartSheet,
+ nEndColumn, nEndRow, nEndSheet);
+ if ((nStartColumn == nEndColumn) && (nStartRow == nEndRow) && (nStartSheet == nEndSheet))
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_COLUMN, OUString::number(nStartColumn));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ROW, OUString::number(nStartRow));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE, OUString::number(nStartSheet));
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_COLUMN, OUString::number(nStartColumn));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_ROW, OUString::number(nStartRow));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_TABLE, OUString::number(nStartSheet));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_COLUMN, OUString::number(nEndColumn));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_ROW, OUString::number(nEndRow));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_TABLE, OUString::number(nEndSheet));
+ }
+ SvXMLElementExport aBigRangeElem(rExport, XML_NAMESPACE_TABLE, aName, true, true);
+}
+
+void ScChangeTrackingExportHelper::WriteChangeInfo(const ScChangeAction* pAction)
+{
+ SvXMLElementExport aElemInfo (rExport, XML_NAMESPACE_OFFICE, XML_CHANGE_INFO, true, true);
+
+ {
+ SvXMLElementExport aCreatorElem( rExport, XML_NAMESPACE_DC,
+ XML_CREATOR, true,
+ false );
+ rExport.Characters(pAction->GetUser());
+ }
+
+ {
+ OUStringBuffer sDate;
+ ScXMLConverter::ConvertDateTimeToString(pAction->GetDateTimeUTC(), sDate);
+ SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC,
+ XML_DATE, true,
+ false );
+ rExport.Characters(sDate.makeStringAndClear());
+ }
+
+ const OUString& sComment(pAction->GetComment());
+ if (!sComment.isEmpty())
+ {
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ bool bPrevCharWasSpace(true);
+ rExport.GetTextParagraphExport()->exportCharacterData(sComment, bPrevCharWasSpace);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteGenerated(const ScChangeAction* pGeneratedAction)
+{
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nActionNumber(pGeneratedAction->GetActionNumber());
+ OSL_ENSURE(pChangeTrack->IsGenerated(nActionNumber), "a not generated action found");
+#endif
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_CELL_CONTENT_DELETION, true, true);
+ WriteBigRange(pGeneratedAction->GetBigRange(), XML_CELL_ADDRESS);
+ OUString sValue = static_cast<const ScChangeActionContent*>(pGeneratedAction)->GetNewString(rExport.GetDocument());
+ WriteCell(static_cast<const ScChangeActionContent*>(pGeneratedAction)->GetNewCell(), sValue);
+}
+
+void ScChangeTrackingExportHelper::WriteDeleted(const ScChangeAction* pDeletedAction)
+{
+ sal_uInt32 nActionNumber(pDeletedAction->GetActionNumber());
+ if (pDeletedAction->GetType() == SC_CAT_CONTENT)
+ {
+ const ScChangeActionContent* pContentAction = static_cast<const ScChangeActionContent*>(pDeletedAction);
+ if (pContentAction)
+ {
+ if (!pChangeTrack->IsGenerated(nActionNumber))
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(nActionNumber));
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_CELL_CONTENT_DELETION, true, true);
+ if (static_cast<const ScChangeActionContent*>(pDeletedAction)->IsTopContent() && pDeletedAction->IsDeletedIn())
+ {
+ OUString sValue = pContentAction->GetNewString(rExport.GetDocument());
+ WriteCell(pContentAction->GetNewCell(), sValue);
+ }
+ }
+ else
+ WriteGenerated(pContentAction);
+ }
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(nActionNumber));
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_DELETION, true, true);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteDepending(const ScChangeAction* pDependAction)
+{
+ sal_uInt32 nActionNumber(pDependAction->GetActionNumber());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(nActionNumber));
+
+ SvXMLElementExport aDependElem(rExport, XML_NAMESPACE_TABLE,
+ XML_DEPENDENCY, true, true);
+}
+
+void ScChangeTrackingExportHelper::WriteDependings(const ScChangeAction* pAction)
+{
+ if (pAction->HasDependent())
+ {
+ SvXMLElementExport aDependingsElem (rExport, XML_NAMESPACE_TABLE, XML_DEPENDENCIES, true, true);
+ const ScChangeActionLinkEntry* pEntry = pAction->GetFirstDependentEntry();
+ while (pEntry)
+ {
+ WriteDepending(pEntry->GetAction());
+ pEntry = pEntry->GetNext();
+ }
+ }
+ if (pAction->HasDeleted())
+ {
+ SvXMLElementExport aDependingsElem (rExport, XML_NAMESPACE_TABLE, XML_DELETIONS, true, true);
+ const ScChangeActionLinkEntry* pEntry = pAction->GetFirstDeletedEntry();
+ while (pEntry)
+ {
+ WriteDeleted(pEntry->GetAction());
+ pEntry = pEntry->GetNext();
+ }
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteEmptyCell()
+{
+ SvXMLElementExport aElemEmptyCell(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+}
+
+void ScChangeTrackingExportHelper::SetValueAttributes(const double& fValue, const OUString& sValue)
+{
+ bool bSetAttributes(false);
+ if (!sValue.isEmpty())
+ {
+ sal_uInt32 nIndex = 0;
+ double fTempValue = 0.0;
+ if (rExport.GetDocument() && rExport.GetDocument()->GetFormatTable()->IsNumberFormat(sValue, nIndex, fTempValue))
+ {
+ SvNumFormatType nType = rExport.GetDocument()->GetFormatTable()->GetType(nIndex);
+ if (nType & SvNumFormatType::DEFINED)
+ nType &= ~SvNumFormatType::DEFINED;
+ switch(nType)
+ {
+ case SvNumFormatType::DATE:
+ {
+ if ( rExport.GetMM100UnitConverter().setNullDate(rExport.GetModel()) )
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_DATE);
+ OUStringBuffer sBuffer;
+ rExport.GetMM100UnitConverter().convertDateTime(sBuffer, fTempValue);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DATE_VALUE, sBuffer.makeStringAndClear());
+ bSetAttributes = true;
+ }
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_TIME);
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDuration(sBuffer, fTempValue);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_TIME_VALUE, sBuffer.makeStringAndClear());
+ bSetAttributes = true;
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ if (!bSetAttributes)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDouble(sBuffer, fValue);
+ OUString sNumValue(sBuffer.makeStringAndClear());
+ if (!sNumValue.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, sNumValue);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteValueCell(const ScCellValue& rCell, const OUString& sValue)
+{
+ assert(rCell.getType() == CELLTYPE_VALUE);
+
+ SetValueAttributes(rCell.getDouble(), sValue);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+}
+
+void ScChangeTrackingExportHelper::WriteStringCell(const ScCellValue& rCell)
+{
+ assert(rCell.getType() == CELLTYPE_STRING);
+
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ if (!rCell.getSharedString()->isEmpty())
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ bool bPrevCharWasSpace(true);
+ rExport.GetTextParagraphExport()->exportCharacterData(rCell.getSharedString()->getString(), bPrevCharWasSpace);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteEditCell(const ScCellValue& rCell)
+{
+ assert(rCell.getType() == CELLTYPE_EDIT);
+
+ OUString sString;
+ if (rCell.getEditText())
+ sString = ScEditUtil::GetString(*rCell.getEditText(), rExport.GetDocument());
+
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ if (rCell.getEditText() && !sString.isEmpty())
+ {
+ if (!pEditTextObj)
+ pEditTextObj = new ScEditEngineTextObj();
+ pEditTextObj->SetText(*rCell.getEditText());
+ rExport.GetTextParagraphExport()->exportText(pEditTextObj, false, false);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteFormulaCell(const ScCellValue& rCell, const OUString& sValue)
+{
+ assert(rCell.getType() == CELLTYPE_FORMULA);
+
+ ScFormulaCell* pFormulaCell = rCell.getFormula();
+ OUString sAddress;
+ const ScDocument* pDoc = rExport.GetDocument();
+ ScRangeStringConverter::GetStringFromAddress(sAddress, pFormulaCell->aPos, pDoc, ::formula::FormulaGrammar::CONV_OOO);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_ADDRESS, sAddress);
+ const formula::FormulaGrammar::Grammar eGrammar = pDoc->GetStorageGrammar();
+ sal_uInt16 nNamespacePrefix = (eGrammar == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
+ OUString sFormula = pFormulaCell->GetFormula(eGrammar);
+ ScMatrixMode nMatrixFlag(pFormulaCell->GetMatrixFlag());
+ if (nMatrixFlag != ScMatrixMode::NONE)
+ {
+ if (nMatrixFlag == ScMatrixMode::Formula)
+ {
+ SCCOL nColumns;
+ SCROW nRows;
+ pFormulaCell->GetMatColsRows(nColumns, nRows);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED, OUString::number(nColumns));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED, OUString::number(nRows));
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MATRIX_COVERED, XML_TRUE);
+ }
+ OUString sMatrixFormula = sFormula.copy(1, sFormula.getLength() - 2);
+ OUString sQValue = rExport.GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sMatrixFormula, false );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue);
+ }
+ else
+ {
+ OUString sQValue = rExport.GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sFormula, false );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue);
+ }
+ if (pFormulaCell->IsValue())
+ {
+ SetValueAttributes(pFormulaCell->GetValue(), sValue);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ OUString sCellValue = pFormulaCell->GetString().getString();
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ if (!sCellValue.isEmpty())
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ bool bPrevCharWasSpace(true);
+ rExport.GetTextParagraphExport()->exportCharacterData(sCellValue, bPrevCharWasSpace);
+ }
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteCell(const ScCellValue& rCell, const OUString& sValue)
+{
+ if (rCell.isEmpty())
+ {
+ WriteEmptyCell();
+ return;
+ }
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ WriteValueCell(rCell, sValue);
+ break;
+ case CELLTYPE_STRING:
+ WriteStringCell(rCell);
+ break;
+ case CELLTYPE_EDIT:
+ WriteEditCell(rCell);
+ break;
+ case CELLTYPE_FORMULA:
+ WriteFormulaCell(rCell, sValue);
+ break;
+ default:
+ WriteEmptyCell();
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteContentChange(const ScChangeAction* pAction)
+{
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_CELL_CONTENT_CHANGE, true, true);
+ const ScChangeAction* pConstAction = pAction;
+ WriteBigRange(pConstAction->GetBigRange(), XML_CELL_ADDRESS);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+ {
+ const ScChangeActionContent* pPrevAction = static_cast<const ScChangeActionContent*>(pAction)->GetPrevContent();
+ if (pPrevAction)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pPrevAction->GetActionNumber()));
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_PREVIOUS, true, true);
+ OUString sValue = static_cast<const ScChangeActionContent*>(pAction)->GetOldString(rExport.GetDocument());
+ WriteCell(static_cast<const ScChangeActionContent*>(pAction)->GetOldCell(), sValue);
+ }
+}
+
+void ScChangeTrackingExportHelper::AddInsertionAttributes(const ScChangeAction* pConstAction)
+{
+ sal_Int64 nPosition(0);
+ sal_Int64 nCount(0);
+ sal_Int64 nStartPosition(0);
+ sal_Int64 nEndPosition(0);
+ sal_Int64 nStartColumn;
+ sal_Int64 nEndColumn;
+ sal_Int64 nStartRow;
+ sal_Int64 nEndRow;
+ sal_Int64 nStartSheet;
+ sal_Int64 nEndSheet;
+ const ScBigRange& rBigRange = pConstAction->GetBigRange();
+ rBigRange.GetVars(nStartColumn, nStartRow, nStartSheet,
+ nEndColumn, nEndRow, nEndSheet);
+ switch (pConstAction->GetType())
+ {
+ case SC_CAT_INSERT_COLS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_COLUMN);
+ nStartPosition = nStartColumn;
+ nEndPosition = nEndColumn;
+ }
+ break;
+ case SC_CAT_INSERT_ROWS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_ROW);
+ nStartPosition = nStartRow;
+ nEndPosition = nEndRow;
+ }
+ break;
+ case SC_CAT_INSERT_TABS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_TABLE);
+ nStartPosition = nStartSheet;
+ nEndPosition = nEndSheet;
+ }
+ break;
+ default :
+ {
+ OSL_FAIL("wrong insertion type");
+ }
+ break;
+ }
+ nPosition = nStartPosition;
+ nCount = nEndPosition - nStartPosition + 1;
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(nPosition));
+ OSL_ENSURE(nCount > 0, "wrong insertion count");
+ if (nCount > 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_COUNT, OUString::number(nCount));
+ }
+ if (pConstAction->GetType() != SC_CAT_INSERT_TABS)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE, OUString::number(nStartSheet));
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteInsertion(const ScChangeAction* pAction)
+{
+ AddInsertionAttributes(pAction);
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_INSERTION, true, true);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+}
+
+void ScChangeTrackingExportHelper::AddDeletionAttributes(const ScChangeActionDel* pDelAction)
+{
+ sal_Int32 nPosition(0);
+ const ScBigRange& rBigRange = pDelAction->GetBigRange();
+ sal_Int64 nStartColumn(0);
+ sal_Int64 nEndColumn(0);
+ sal_Int64 nStartRow(0);
+ sal_Int64 nEndRow(0);
+ sal_Int64 nStartSheet(0);
+ sal_Int64 nEndSheet(0);
+ rBigRange.GetVars(nStartColumn, nStartRow, nStartSheet,
+ nEndColumn, nEndRow, nEndSheet);
+ switch (pDelAction->GetType())
+ {
+ case SC_CAT_DELETE_COLS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_COLUMN);
+ nPosition = nStartColumn;
+ }
+ break;
+ case SC_CAT_DELETE_ROWS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_ROW);
+ nPosition = nStartRow;
+ }
+ break;
+ case SC_CAT_DELETE_TABS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_TABLE);
+ nPosition = nStartSheet;
+ }
+ break;
+ default :
+ {
+ OSL_FAIL("wrong deletion type");
+ }
+ break;
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(nPosition));
+ if (pDelAction->GetType() == SC_CAT_DELETE_TABS)
+ return;
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE, OUString::number(nStartSheet));
+ if (!pDelAction->IsMultiDelete() || pDelAction->GetDx() || pDelAction->GetDy())
+ return;
+
+ const ScChangeAction* p = pDelAction->GetNext();
+ sal_Int32 nSlavesCount (1);
+ while (p)
+ {
+ if (p->GetType() != pDelAction->GetType())
+ break;
+ else
+ {
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(p);
+ if ( (pDel->GetDx() > pDelAction->GetDx() || pDel->GetDy() > pDelAction->GetDy()) &&
+ pDel->GetBigRange() == pDelAction->GetBigRange() )
+ {
+ ++nSlavesCount;
+ p = p->GetNext();
+ }
+ else
+ break;
+ }
+ }
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MULTI_DELETION_SPANNED, OUString::number(nSlavesCount));
+}
+
+void ScChangeTrackingExportHelper::WriteCutOffs(const ScChangeActionDel* pAction)
+{
+ const ScChangeActionIns* pCutOffIns = pAction->GetCutOffInsert();
+ const ScChangeActionDelMoveEntry* pLinkMove = pAction->GetFirstMoveEntry();
+ if (!(pCutOffIns || pLinkMove))
+ return;
+
+ SvXMLElementExport aCutOffsElem (rExport, XML_NAMESPACE_TABLE, XML_CUT_OFFS, true, true);
+ if (pCutOffIns)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pCutOffIns->GetActionNumber()));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(pAction->GetCutOffCount()));
+ SvXMLElementExport aInsertCutOffElem (rExport, XML_NAMESPACE_TABLE, XML_INSERTION_CUT_OFF, true, true);
+ }
+ while (pLinkMove)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pLinkMove->GetAction()->GetActionNumber()));
+ if (pLinkMove->GetCutOffFrom() == pLinkMove->GetCutOffTo())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(pLinkMove->GetCutOffFrom()));
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_POSITION, OUString::number(pLinkMove->GetCutOffFrom()));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_POSITION, OUString::number(pLinkMove->GetCutOffTo()));
+ }
+ SvXMLElementExport aMoveCutOffElem (rExport, XML_NAMESPACE_TABLE, XML_MOVEMENT_CUT_OFF, true, true);
+ pLinkMove = pLinkMove->GetNext();
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteDeletion(ScChangeAction* pAction)
+{
+ ScChangeActionDel* pDelAction = static_cast<ScChangeActionDel*> (pAction);
+ AddDeletionAttributes(pDelAction);
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_DELETION, true, true);
+ WriteChangeInfo(pDelAction);
+ WriteDependings(pDelAction);
+ WriteCutOffs(pDelAction);
+}
+
+void ScChangeTrackingExportHelper::WriteMovement(const ScChangeAction* pAction)
+{
+ const ScChangeActionMove* pMoveAction = static_cast<const ScChangeActionMove*> (pAction);
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_MOVEMENT, true, true);
+ WriteBigRange(pMoveAction->GetFromRange(), XML_SOURCE_RANGE_ADDRESS);
+ WriteBigRange(pMoveAction->GetBigRange(), XML_TARGET_RANGE_ADDRESS);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+}
+
+void ScChangeTrackingExportHelper::WriteRejection(const ScChangeAction* pAction)
+{
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_REJECTION, true, true);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+}
+
+void ScChangeTrackingExportHelper::CollectCellAutoStyles(const ScCellValue& rCell)
+{
+ if (rCell.getType() != CELLTYPE_EDIT)
+ return;
+
+ if (!pEditTextObj)
+ pEditTextObj = new ScEditEngineTextObj();
+
+ pEditTextObj->SetText(*rCell.getEditText());
+ rExport.GetTextParagraphExport()->collectTextAutoStyles(pEditTextObj, false, false);
+}
+
+void ScChangeTrackingExportHelper::CollectActionAutoStyles(const ScChangeAction* pAction)
+{
+ if (pAction->GetType() != SC_CAT_CONTENT)
+ return;
+
+ if (pChangeTrack->IsGenerated(pAction->GetActionNumber()))
+ CollectCellAutoStyles(static_cast<const ScChangeActionContent*>(pAction)->GetNewCell());
+ else
+ {
+ CollectCellAutoStyles(static_cast<const ScChangeActionContent*>(pAction)->GetOldCell());
+ if (static_cast<const ScChangeActionContent*>(pAction)->IsTopContent() && pAction->IsDeletedIn())
+ CollectCellAutoStyles(static_cast<const ScChangeActionContent*>(pAction)->GetNewCell());
+ }
+}
+
+void ScChangeTrackingExportHelper::WorkWithChangeAction(ScChangeAction* pAction)
+{
+ if (pAction->GetType() == SC_CAT_NONE)
+ {
+ SAL_WARN("sc.filter", "WorkWithChangeAction: type is not writable");
+ return;
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pAction->GetActionNumber()));
+ GetAcceptanceState(pAction);
+ if (pAction->IsRejecting())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_REJECTING_CHANGE_ID, GetChangeID(pAction->GetRejectAction()));
+ if (pAction->GetType() == SC_CAT_CONTENT)
+ WriteContentChange(pAction);
+ else if (pAction->IsInsertType())
+ WriteInsertion(pAction);
+ else if (pAction->IsDeleteType())
+ WriteDeletion(pAction);
+ else if (pAction->GetType() == SC_CAT_MOVE)
+ WriteMovement(pAction);
+ else if (pAction->GetType() == SC_CAT_REJECT)
+ WriteRejection(pAction);
+ else
+ {
+ assert(false); // tdf#73335 this would create duplicate attributes
+ }
+ rExport.CheckAttrList();
+}
+
+void ScChangeTrackingExportHelper::CollectAutoStyles()
+{
+ if (!pChangeTrack)
+ return;
+
+ sal_uInt32 nCount (pChangeTrack->GetActionMax());
+ if (!nCount)
+ return;
+
+ ScChangeAction* pAction = pChangeTrack->GetFirst();
+ CollectActionAutoStyles(pAction);
+ ScChangeAction* pLastAction = pChangeTrack->GetLast();
+ while (pAction != pLastAction)
+ {
+ pAction = pAction->GetNext();
+ CollectActionAutoStyles(pAction);
+ }
+ pAction = pChangeTrack->GetFirstGenerated();
+ while (pAction)
+ {
+ CollectActionAutoStyles(pAction);
+ pAction = pAction->GetNext();
+ }
+}
+
+void ScChangeTrackingExportHelper::CollectAndWriteChanges()
+{
+ if (!pChangeTrack)
+ return;
+
+ SvXMLElementExport aCangeListElem(rExport, XML_NAMESPACE_TABLE, XML_TRACKED_CHANGES, true, true);
+ {
+ ScChangeAction* pAction = pChangeTrack->GetFirst();
+ if (pAction)
+ {
+ WorkWithChangeAction(pAction);
+ ScChangeAction* pLastAction = pChangeTrack->GetLast();
+ while (pAction != pLastAction)
+ {
+ pAction = pAction->GetNext();
+ WorkWithChangeAction(pAction);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx b/sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx
new file mode 100644
index 0000000000..be721de62c
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingExportHelper.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 .
+ */
+
+#pragma once
+
+#include <xmloff/xmltoken.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::text
+{
+class XText;
+}
+
+class ScChangeAction;
+class ScChangeTrack;
+class ScXMLExport;
+struct ScCellValue;
+class ScChangeActionDel;
+class ScBigRange;
+class ScEditEngineTextObj;
+
+class ScChangeTrackingExportHelper
+{
+ ScXMLExport& rExport;
+
+ ScChangeTrack* pChangeTrack;
+ rtl::Reference<ScEditEngineTextObj> pEditTextObj;
+
+ static OUString GetChangeID(const sal_uInt32 nActionNumber);
+ void GetAcceptanceState(const ScChangeAction* pAction);
+
+ void WriteBigRange(const ScBigRange& rBigRange, xmloff::token::XMLTokenEnum aName);
+ void WriteChangeInfo(const ScChangeAction* pAction);
+ void WriteGenerated(const ScChangeAction* pDependAction);
+ void WriteDeleted(const ScChangeAction* pDependAction);
+ void WriteDepending(const ScChangeAction* pDependAction);
+ void WriteDependings(const ScChangeAction* pAction);
+
+ void WriteEmptyCell();
+ void SetValueAttributes(const double& fValue, const OUString& sValue);
+ void WriteValueCell(const ScCellValue& rCell, const OUString& sValue);
+ void WriteStringCell(const ScCellValue& rCell);
+ void WriteEditCell(const ScCellValue& rCell);
+ void WriteFormulaCell(const ScCellValue& rCell, const OUString& sValue);
+ void WriteCell(const ScCellValue& rCell, const OUString& sValue);
+
+ void WriteContentChange(const ScChangeAction* pAction);
+ void AddInsertionAttributes(const ScChangeAction* pAction);
+ void WriteInsertion(const ScChangeAction* pAction);
+ void AddDeletionAttributes(const ScChangeActionDel* pAction);
+ void WriteCutOffs(const ScChangeActionDel* pAction);
+ void WriteDeletion(ScChangeAction* pAction);
+ void WriteMovement(const ScChangeAction* pAction);
+ void WriteRejection(const ScChangeAction* pAction);
+
+ void CollectCellAutoStyles(const ScCellValue& rCell);
+ void CollectActionAutoStyles(const ScChangeAction* pAction);
+ void WorkWithChangeAction(ScChangeAction* pAction);
+
+public:
+ explicit ScChangeTrackingExportHelper(ScXMLExport& rExport);
+ ~ScChangeTrackingExportHelper();
+
+ void CollectAutoStyles();
+ void CollectAndWriteChanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx b/sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx
new file mode 100644
index 0000000000..076ceda030
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx
@@ -0,0 +1,804 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLChangeTrackingImportHelper.hxx"
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <tools/datetime.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sax/tools/converter.hxx>
+#include <utility>
+
+constexpr OString SC_CHANGE_ID_PREFIX = "ct"_ostr;
+
+ScMyCellInfo::ScMyCellInfo(
+ ScCellValue aCell, OUString aFormulaAddress, OUString aFormula,
+ const formula::FormulaGrammar::Grammar eTempGrammar, OUString aInputString,
+ const double& rValue, const sal_uInt16 nTempType, const ScMatrixMode nTempMatrixFlag, const sal_Int32 nTempMatrixCols,
+ const sal_Int32 nTempMatrixRows ) :
+ maCell(std::move(aCell)),
+ sFormulaAddress(std::move(aFormulaAddress)),
+ sFormula(std::move(aFormula)),
+ sInputString(std::move(aInputString)),
+ fValue(rValue),
+ nMatrixCols(nTempMatrixCols),
+ nMatrixRows(nTempMatrixRows),
+ eGrammar( eTempGrammar),
+ nType(nTempType),
+ nMatrixFlag(nTempMatrixFlag)
+{
+}
+
+ScMyCellInfo::~ScMyCellInfo() {}
+
+const ScCellValue& ScMyCellInfo::CreateCell(ScDocument& rDoc)
+{
+ if (!maCell.isEmpty())
+ return maCell;
+
+ if (!sFormula.isEmpty() && !sFormulaAddress.isEmpty())
+ {
+ ScAddress aPos;
+ sal_Int32 nOffset(0);
+ ScRangeStringConverter::GetAddressFromString(aPos, sFormulaAddress, rDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset);
+ maCell.set(new ScFormulaCell(rDoc, aPos, sFormula, eGrammar, nMatrixFlag));
+ maCell.getFormula()->SetMatColsRows(static_cast<SCCOL>(nMatrixCols), static_cast<SCROW>(nMatrixRows));
+ }
+
+ if ((nType == css::util::NumberFormat::DATE || nType == css::util::NumberFormat::TIME) && sInputString.isEmpty())
+ {
+ sal_uInt32 nFormat(0);
+ if (nType == css::util::NumberFormat::DATE)
+ nFormat = rDoc.GetFormatTable()->GetStandardFormat( SvNumFormatType::DATE, ScGlobal::eLnge );
+ else if (nType == css::util::NumberFormat::TIME)
+ nFormat = rDoc.GetFormatTable()->GetStandardFormat( SvNumFormatType::TIME, ScGlobal::eLnge );
+ rDoc.GetFormatTable()->GetInputLineString(fValue, nFormat, sInputString);
+ }
+
+ return maCell;
+}
+
+ScMyBaseAction::ScMyBaseAction(const ScChangeActionType nTempActionType)
+ : nActionNumber(0),
+ nRejectingNumber(0),
+ nPreviousAction(0),
+ nActionType(nTempActionType),
+ nActionState(SC_CAS_VIRGIN)
+{
+}
+
+ScMyBaseAction::~ScMyBaseAction()
+{
+}
+
+ScMyInsAction::ScMyInsAction(const ScChangeActionType nActionTypeP)
+ : ScMyBaseAction(nActionTypeP)
+{
+}
+
+ScMyInsAction::~ScMyInsAction()
+{
+}
+
+ScMyDelAction::ScMyDelAction(const ScChangeActionType nActionTypeP)
+ : ScMyBaseAction(nActionTypeP),
+ nD(0)
+{
+}
+
+ScMyDelAction::~ScMyDelAction()
+{
+}
+
+ScMyMoveAction::ScMyMoveAction()
+ : ScMyBaseAction(SC_CAT_MOVE)
+{
+}
+
+ScMyMoveAction::~ScMyMoveAction()
+{
+}
+
+ScMyContentAction::ScMyContentAction()
+ : ScMyBaseAction(SC_CAT_CONTENT)
+{
+}
+
+ScMyContentAction::~ScMyContentAction()
+{
+}
+
+ScMyRejAction::ScMyRejAction()
+ : ScMyBaseAction(SC_CAT_REJECT)
+{
+}
+
+ScMyRejAction::~ScMyRejAction()
+{
+}
+
+ScXMLChangeTrackingImportHelper::ScXMLChangeTrackingImportHelper() :
+ pTrack(nullptr),
+ nMultiSpanned(0),
+ nMultiSpannedSlaveCount(0)
+{
+}
+
+ScXMLChangeTrackingImportHelper::~ScXMLChangeTrackingImportHelper()
+{
+}
+
+void ScXMLChangeTrackingImportHelper::StartChangeAction(const ScChangeActionType nActionType)
+{
+ OSL_ENSURE(!pCurrentAction, "a not inserted action");
+ switch (nActionType)
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ {
+ pCurrentAction = std::make_unique<ScMyInsAction>(nActionType);
+ }
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ pCurrentAction = std::make_unique<ScMyDelAction>(nActionType);
+ }
+ break;
+ case SC_CAT_MOVE:
+ {
+ pCurrentAction = std::make_unique<ScMyMoveAction>();
+ }
+ break;
+ case SC_CAT_CONTENT:
+ {
+ pCurrentAction = std::make_unique<ScMyContentAction>();
+ }
+ break;
+ case SC_CAT_REJECT:
+ {
+ pCurrentAction = std::make_unique<ScMyRejAction>();
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+sal_uInt32 ScXMLChangeTrackingImportHelper::GetIDFromString(std::string_view sID)
+{
+ sal_uInt32 nResult(0);
+ if (!sID.empty())
+ {
+ if (sID.substr(0, SC_CHANGE_ID_PREFIX.getLength()) == SC_CHANGE_ID_PREFIX)
+ {
+ sal_Int32 nValue;
+ ::sax::Converter::convertNumber(nValue, sID.substr(SC_CHANGE_ID_PREFIX.getLength()));
+ OSL_ENSURE(nValue > 0, "wrong change action ID");
+ nResult = nValue;
+ }
+ else
+ {
+ OSL_FAIL("wrong change action ID");
+ }
+ }
+ return nResult;
+}
+
+void ScXMLChangeTrackingImportHelper::SetActionInfo(const ScMyActionInfo& aInfo)
+{
+ pCurrentAction->aInfo = aInfo;
+ aUsers.insert(aInfo.sUser);
+}
+
+void ScXMLChangeTrackingImportHelper::SetPreviousChange(const sal_uInt32 nPreviousAction,
+ ScMyCellInfo* pCellInfo)
+{
+ OSL_ENSURE(pCurrentAction->nActionType == SC_CAT_CONTENT, "wrong action type");
+ ScMyContentAction* pAction = static_cast<ScMyContentAction*>(pCurrentAction.get());
+ pAction->nPreviousAction = nPreviousAction;
+ pAction->pCellInfo.reset( pCellInfo );
+}
+
+void ScXMLChangeTrackingImportHelper::SetPosition(const sal_Int32 nPosition, const sal_Int32 nCount, const sal_Int32 nTable)
+{
+ OSL_ENSURE(((pCurrentAction->nActionType != SC_CAT_MOVE) &&
+ (pCurrentAction->nActionType != SC_CAT_CONTENT) &&
+ (pCurrentAction->nActionType != SC_CAT_REJECT)), "wrong action type");
+ OSL_ENSURE(nCount > 0, "wrong count");
+ switch(pCurrentAction->nActionType)
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_DELETE_COLS:
+ {
+ pCurrentAction->aBigRange.Set(nPosition, ScBigRange::nRangeMin, nTable,
+ nPosition + nCount - 1, ScBigRange::nRangeMax, nTable);
+ }
+ break;
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_DELETE_ROWS:
+ {
+ pCurrentAction->aBigRange.Set(ScBigRange::nRangeMin, nPosition, nTable,
+ ScBigRange::nRangeMax, nPosition + nCount - 1, nTable);
+ }
+ break;
+ case SC_CAT_INSERT_TABS:
+ case SC_CAT_DELETE_TABS:
+ {
+ pCurrentAction->aBigRange.Set(ScBigRange::nRangeMin, ScBigRange::nRangeMin, nPosition,
+ ScBigRange::nRangeMax, ScBigRange::nRangeMax, nPosition + nCount - 1);
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::AddDeleted(const sal_uInt32 nID)
+{
+ pCurrentAction->aDeletedList.emplace_back( nID, nullptr );
+}
+
+void ScXMLChangeTrackingImportHelper::AddDeleted(const sal_uInt32 nID, std::unique_ptr<ScMyCellInfo> pCellInfo)
+{
+ pCurrentAction->aDeletedList.emplace_back( nID, std::move(pCellInfo) );
+}
+
+void ScXMLChangeTrackingImportHelper::SetMultiSpanned(const sal_Int16 nTempMultiSpanned)
+{
+ if (nTempMultiSpanned)
+ {
+ OSL_ENSURE(((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS)), "wrong action type");
+ nMultiSpanned = nTempMultiSpanned;
+ nMultiSpannedSlaveCount = 0;
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetInsertionCutOff(const sal_uInt32 nID, const sal_Int32 nPosition)
+{
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->moInsCutOff.emplace( nID, nPosition );
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::AddMoveCutOff(const sal_uInt32 nID, const sal_Int32 nStartPosition, const sal_Int32 nEndPosition)
+{
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->aMoveCutOffs.push_back(ScMyMoveCutOff(nID, nStartPosition, nEndPosition));
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetMoveRanges(const ScBigRange& aSourceRange, const ScBigRange& aTargetRange)
+{
+ if (pCurrentAction->nActionType == SC_CAT_MOVE)
+ {
+ static_cast<ScMyMoveAction*>(pCurrentAction.get())->pMoveRanges.reset( new ScMyMoveRanges(aSourceRange, aTargetRange) );
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::GetMultiSpannedRange()
+{
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ if (nMultiSpannedSlaveCount)
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->nD = nMultiSpannedSlaveCount;
+ }
+ ++nMultiSpannedSlaveCount;
+ if (nMultiSpannedSlaveCount >= nMultiSpanned)
+ {
+ nMultiSpanned = 0;
+ nMultiSpannedSlaveCount = 0;
+ }
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::AddGenerated(std::unique_ptr<ScMyCellInfo> pCellInfo, const ScBigRange& aBigRange)
+{
+ ScMyGenerated aGenerated { aBigRange, 0, std::move(pCellInfo) };
+ if (pCurrentAction->nActionType == SC_CAT_MOVE)
+ {
+ static_cast<ScMyMoveAction*>(pCurrentAction.get())->aGeneratedList.push_back(std::move(aGenerated));
+ }
+ else if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->aGeneratedList.push_back(std::move(aGenerated));
+ }
+ else
+ {
+ OSL_FAIL("try to insert a generated action to a wrong action");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::EndChangeAction()
+{
+ if (!pCurrentAction)
+ {
+ OSL_FAIL("no current action");
+ return;
+ }
+
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ GetMultiSpannedRange();
+
+ if (pCurrentAction->nActionNumber > 0)
+ aActions.push_back(std::move(pCurrentAction));
+ else
+ {
+ OSL_FAIL("no current action");
+ }
+
+ pCurrentAction = nullptr;
+}
+
+void ScXMLChangeTrackingImportHelper::ConvertInfo(const ScMyActionInfo& aInfo, OUString& rUser, DateTime& aDateTime)
+{
+ aDateTime = DateTime( aInfo.aDateTime);
+
+ // old files didn't store nanoseconds, enable again
+ if ( aInfo.aDateTime.NanoSeconds )
+ pTrack->SetTimeNanoSeconds( true );
+
+ const std::set<OUString>& rUsers = pTrack->GetUserCollection();
+ std::set<OUString>::const_iterator it = rUsers.find(aInfo.sUser);
+ if (it != rUsers.end())
+ {
+ // It's probably pointless to do this.
+ rUser = *it;
+ }
+ else
+ rUser = aInfo.sUser; // shouldn't happen
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateInsertAction(const ScMyInsAction* pAction)
+{
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionIns>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment, pAction->nActionType);
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateDeleteAction(const ScMyDelAction* pAction)
+{
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionDel>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment, pAction->nActionType, pAction->nD, pTrack);
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateMoveAction(const ScMyMoveAction* pAction)
+{
+ OSL_ENSURE(pAction->pMoveRanges, "no move ranges");
+ if (pAction->pMoveRanges)
+ {
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionMove>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->pMoveRanges->aTargetRange, aUser, aDateTime, sComment, pAction->pMoveRanges->aSourceRange , pTrack);
+ }
+ return nullptr;
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateRejectionAction(const ScMyRejAction* pAction)
+{
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionReject>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment);
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateContentAction(const ScMyContentAction* pAction, ScDocument& rDoc)
+{
+ ScCellValue aCell;
+ OUString sInputString;
+ if (pAction->pCellInfo)
+ {
+ aCell = pAction->pCellInfo->CreateCell(rDoc);
+ sInputString = pAction->pCellInfo->sInputString;
+ }
+
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionContent>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment, aCell, &rDoc, sInputString);
+}
+
+void ScXMLChangeTrackingImportHelper::CreateGeneratedActions(std::vector<ScMyGenerated>& rList, ScDocument& rDoc)
+{
+ for (ScMyGenerated & rGenerated : rList)
+ {
+ if (rGenerated.nID != 0)
+ continue;
+ if (!rGenerated.pCellInfo)
+ continue;
+ ScCellValue aCell = rGenerated.pCellInfo->CreateCell(rDoc);
+ if (aCell.isEmpty())
+ continue;
+ rGenerated.nID = pTrack->AddLoadedGenerated(aCell, rGenerated.aBigRange, rGenerated.pCellInfo->sInputString);
+ OSL_ENSURE(rGenerated.nID, "could not insert generated action");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetDeletionDependencies(ScMyDelAction* pAction, ScChangeActionDel* pDelAct)
+{
+ if (!pAction->aGeneratedList.empty())
+ {
+ OSL_ENSURE(((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS) ||
+ (pAction->nActionType == SC_CAT_DELETE_TABS)), "wrong action type");
+ if (pDelAct)
+ {
+ for (const ScMyGenerated & rGenerated : pAction->aGeneratedList)
+ {
+ OSL_ENSURE(rGenerated.nID, "a not inserted generated action");
+ pDelAct->SetDeletedInThis(rGenerated.nID, pTrack);
+ }
+ pAction->aGeneratedList.clear();
+ }
+ }
+ if (pAction->moInsCutOff)
+ {
+ OSL_ENSURE(((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS) ||
+ (pAction->nActionType == SC_CAT_DELETE_TABS)), "wrong action type");
+ ScChangeAction* pChangeAction = pTrack->GetAction(pAction->moInsCutOff->nID);
+ if (pChangeAction && pChangeAction->IsInsertType())
+ {
+ ScChangeActionIns* pInsAction = static_cast<ScChangeActionIns*>(pChangeAction);
+ if (pDelAct)
+ pDelAct->SetCutOffInsert(pInsAction, static_cast<sal_Int16>(pAction->moInsCutOff->nPosition));
+ }
+ else
+ {
+ OSL_FAIL("no cut off insert action");
+ }
+ }
+ if (pAction->aMoveCutOffs.empty())
+ return;
+
+ OSL_ENSURE(((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS) ||
+ (pAction->nActionType == SC_CAT_DELETE_TABS)), "wrong action type");
+ for (auto it = pAction->aMoveCutOffs.crbegin(); it != pAction->aMoveCutOffs.crend(); ++it)
+ {
+ const ScMyMoveCutOff & rCutOff = *it;
+ ScChangeAction* pChangeAction = pTrack->GetAction(rCutOff.nID);
+ if (pChangeAction && (pChangeAction->GetType() == SC_CAT_MOVE))
+ {
+ ScChangeActionMove* pMoveAction = static_cast<ScChangeActionMove*>(pChangeAction);
+ if (pDelAct)
+ pDelAct->AddCutOffMove(pMoveAction, static_cast<sal_Int16>(rCutOff.nStartPosition),
+ static_cast<sal_Int16>(rCutOff.nEndPosition));
+ }
+ else
+ {
+ OSL_FAIL("no cut off move action");
+ }
+ }
+ pAction->aMoveCutOffs.clear();
+}
+
+void ScXMLChangeTrackingImportHelper::SetMovementDependencies(ScMyMoveAction* pAction, ScChangeActionMove* pMoveAct)
+{
+ if (pAction->aGeneratedList.empty())
+ return;
+
+ if (pAction->nActionType == SC_CAT_MOVE)
+ {
+ if (pMoveAct)
+ {
+ for (const ScMyGenerated & rGenerated : pAction->aGeneratedList)
+ {
+ OSL_ENSURE(rGenerated.nID, "a not inserted generated action");
+ pMoveAct->SetDeletedInThis(rGenerated.nID, pTrack);
+ }
+ pAction->aGeneratedList.clear();
+ }
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetContentDependencies(const ScMyContentAction* pAction, ScChangeActionContent* pActContent, const ScDocument& rDoc)
+{
+ if (!pActContent || !pAction->nPreviousAction)
+ return;
+
+ OSL_ENSURE(pAction->nActionType == SC_CAT_CONTENT, "wrong action type");
+ ScChangeAction* pPrevAct = pTrack->GetAction(pAction->nPreviousAction);
+ if (!pPrevAct || pPrevAct->GetType() != SC_CAT_CONTENT)
+ return;
+
+ ScChangeActionContent* pPrevActContent = static_cast<ScChangeActionContent*>(pPrevAct);
+
+ pActContent->SetPrevContent(pPrevActContent);
+ pPrevActContent->SetNextContent(pActContent);
+ const ScCellValue& rOldCell = pActContent->GetOldCell();
+ if (rOldCell.isEmpty())
+ return;
+
+ pPrevActContent->SetNewCell(rOldCell, &rDoc, OUString());
+}
+
+void ScXMLChangeTrackingImportHelper::SetDependencies(ScMyBaseAction* pAction, ScDocument& rDoc)
+{
+ ScChangeAction* pAct = pTrack->GetAction(pAction->nActionNumber);
+ if (pAct)
+ {
+ if (!pAction->aDependencies.empty())
+ {
+ for (auto it = pAction->aDependencies.crbegin(); it != pAction->aDependencies.crend(); ++it)
+ pAct->AddDependent(*it, pTrack);
+ pAction->aDependencies.clear();
+ }
+ if (!pAction->aDeletedList.empty())
+ {
+ for (auto it = pAction->aDeletedList.crbegin(); it != pAction->aDeletedList.crend(); ++it)
+ {
+ const ScMyDeleted & rDeleted = *it;
+ pAct->SetDeletedInThis(rDeleted.nID, pTrack);
+ ScChangeAction* pDeletedAct = pTrack->GetAction(rDeleted.nID);
+ if ((pDeletedAct->GetType() == SC_CAT_CONTENT) && rDeleted.pCellInfo)
+ {
+ ScChangeActionContent* pContentAct = static_cast<ScChangeActionContent*>(pDeletedAct);
+ if (rDeleted.pCellInfo)
+ {
+ const ScCellValue& rCell = rDeleted.pCellInfo->CreateCell(rDoc);
+ if (!rCell.equalsWithoutFormat(pContentAct->GetNewCell()))
+ {
+ // #i40704# Don't overwrite SetNewCell result by calling SetNewValue,
+ // instead pass the input string to SetNewCell.
+ pContentAct->SetNewCell(rCell, &rDoc, rDeleted.pCellInfo->sInputString);
+ }
+ }
+ }
+ }
+ pAction->aDeletedList.clear();
+ }
+ if ((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS))
+ SetDeletionDependencies(static_cast<ScMyDelAction*>(pAction), static_cast<ScChangeActionDel*>(pAct));
+ else if (pAction->nActionType == SC_CAT_MOVE)
+ SetMovementDependencies(static_cast<ScMyMoveAction*>(pAction), static_cast<ScChangeActionMove*>(pAct));
+ else if (pAction->nActionType == SC_CAT_CONTENT)
+ SetContentDependencies(static_cast<ScMyContentAction*>(pAction), static_cast<ScChangeActionContent*>(pAct), rDoc);
+ }
+ else
+ {
+ OSL_FAIL("could not find the action");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetNewCell(const ScMyContentAction* pAction, ScDocument& rDoc)
+{
+ ScChangeAction* pChangeAction = pTrack->GetAction(pAction->nActionNumber);
+ if (!pChangeAction)
+ return;
+
+ assert(dynamic_cast<ScChangeActionContent*>(pChangeAction));
+ ScChangeActionContent* pChangeActionContent = static_cast<ScChangeActionContent*>(pChangeAction);
+ if (!pChangeActionContent->IsTopContent() || pChangeActionContent->IsDeletedIn())
+ return;
+
+ sal_Int64 nCol, nRow, nTab, nCol2, nRow2, nTab2;
+ pAction->aBigRange.GetVars(nCol, nRow, nTab, nCol2, nRow2, nTab2);
+ if ((nCol >= 0) && (nCol <= rDoc.MaxCol()) &&
+ (nRow >= 0) && (nRow <= rDoc.MaxRow()) &&
+ (nTab >= 0) && (nTab <= MAXTAB))
+ {
+ ScAddress aAddress (static_cast<SCCOL>(nCol),
+ static_cast<SCROW>(nRow),
+ static_cast<SCTAB>(nTab));
+ ScCellValue aCell;
+ aCell.assign(rDoc, aAddress);
+ if (!aCell.isEmpty())
+ {
+ ScCellValue aNewCell;
+ if (aCell.getType() != CELLTYPE_FORMULA)
+ {
+ aNewCell = aCell;
+ pChangeActionContent->SetNewCell(aNewCell, &rDoc, OUString());
+ pChangeActionContent->SetNewValue(aCell, &rDoc);
+ }
+ else
+ {
+ ScMatrixMode nMatrixFlag = aCell.getFormula()->GetMatrixFlag();
+ // With GRAM_ODFF reference detection is faster on compilation.
+ /* FIXME: new cell should be created with a clone
+ * of the token array instead. Any reason why this
+ * wasn't done? */
+ OUString sFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+
+ // #i87826# [Collaboration] Rejected move destroys formulas
+ // FIXME: adjust ScFormulaCell::GetFormula(), so that the right formula string
+ // is returned and no further string handling is necessary
+ OUString sFormula2;
+ if ( nMatrixFlag != ScMatrixMode::NONE )
+ {
+ sFormula2 = sFormula.copy( 2, sFormula.getLength() - 3 );
+ }
+ else
+ {
+ sFormula2 = sFormula.copy( 1 );
+ }
+
+ aNewCell.set(new ScFormulaCell(rDoc, aAddress, sFormula2,formula::FormulaGrammar::GRAM_ODFF, nMatrixFlag));
+ if (nMatrixFlag == ScMatrixMode::Formula)
+ {
+ SCCOL nCols;
+ SCROW nRows;
+ aCell.getFormula()->GetMatColsRows(nCols, nRows);
+ aNewCell.getFormula()->SetMatColsRows(nCols, nRows);
+ }
+ aNewCell.getFormula()->SetInChangeTrack(true);
+ pChangeActionContent->SetNewCell(aNewCell, &rDoc, OUString());
+ // #i40704# don't overwrite the formula string via SetNewValue()
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("wrong cell position");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::CreateChangeTrack(ScDocument* pDoc)
+{
+ if (!pDoc)
+ return;
+
+ pTrack = new ScChangeTrack(*pDoc, std::set(aUsers));
+ // old files didn't store nanoseconds, disable until encountered
+ pTrack->SetTimeNanoSeconds( false );
+
+ for (const auto & rAction : aActions)
+ {
+ std::unique_ptr<ScChangeAction> pAction;
+
+ switch (rAction->nActionType)
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ {
+ pAction = CreateInsertAction(static_cast<ScMyInsAction*>(rAction.get()));
+ }
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ ScMyDelAction* pDelAct = static_cast<ScMyDelAction*>(rAction.get());
+ pAction = CreateDeleteAction(pDelAct);
+ CreateGeneratedActions(pDelAct->aGeneratedList, *pDoc);
+ }
+ break;
+ case SC_CAT_MOVE:
+ {
+ ScMyMoveAction* pMovAct = static_cast<ScMyMoveAction*>(rAction.get());
+ pAction = CreateMoveAction(pMovAct);
+ CreateGeneratedActions(pMovAct->aGeneratedList, *pDoc);
+ }
+ break;
+ case SC_CAT_CONTENT:
+ {
+ pAction = CreateContentAction(static_cast<ScMyContentAction*>(rAction.get()), *pDoc);
+ }
+ break;
+ case SC_CAT_REJECT:
+ {
+ pAction = CreateRejectionAction(static_cast<ScMyRejAction*>(rAction.get()));
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (pAction)
+ pTrack->AppendLoaded(std::move(pAction));
+ else
+ {
+ OSL_FAIL("no action");
+ }
+ }
+ if (pTrack->GetLast())
+ pTrack->SetActionMax(pTrack->GetLast()->GetActionNumber());
+
+ auto aItr = aActions.begin();
+ while (aItr != aActions.end())
+ {
+ SetDependencies(aItr->get(), *pDoc);
+
+ if ((*aItr)->nActionType == SC_CAT_CONTENT)
+ ++aItr;
+ else
+ aItr = aActions.erase(aItr);
+ }
+
+ for (const auto& rxAction : aActions)
+ {
+ OSL_ENSURE(rxAction->nActionType == SC_CAT_CONTENT, "wrong action type");
+ SetNewCell(static_cast<ScMyContentAction*>(rxAction.get()), *pDoc);
+ }
+ aActions.clear();
+ if (aProtect.hasElements())
+ pTrack->SetProtection(aProtect);
+ else if (pDoc->GetChangeTrack() && pDoc->GetChangeTrack()->IsProtected())
+ pTrack->SetProtection(pDoc->GetChangeTrack()->GetProtection());
+
+ if ( pTrack->GetLast() )
+ pTrack->SetLastSavedActionNumber(pTrack->GetLast()->GetActionNumber());
+
+ pDoc->SetChangeTrack(std::unique_ptr<ScChangeTrack>(pTrack));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx b/sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx
new file mode 100644
index 0000000000..e9f2525e91
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <chgtrack.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+#include <utility>
+
+class ScDocument;
+class DateTime;
+enum class ScMatrixMode : sal_uInt8;
+
+struct ScMyActionInfo
+{
+ OUString sUser;
+ OUString sComment;
+ css::util::DateTime aDateTime;
+};
+
+struct ScMyCellInfo
+{
+ ScCellValue maCell;
+ OUString sFormulaAddress;
+ OUString sFormula;
+ OUString sInputString;
+ double fValue;
+ sal_Int32 nMatrixCols;
+ sal_Int32 nMatrixRows;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_uInt16 nType;
+ ScMatrixMode nMatrixFlag;
+
+ ScMyCellInfo(
+ ScCellValue aCell, OUString sFormulaAddress, OUString sFormula,
+ const formula::FormulaGrammar::Grammar eGrammar, OUString sInputString,
+ const double& fValue, const sal_uInt16 nType, const ScMatrixMode nMatrixFlag,
+ const sal_Int32 nMatrixCols, const sal_Int32 nMatrixRows );
+ ~ScMyCellInfo();
+
+ const ScCellValue& CreateCell( ScDocument& rDoc );
+};
+
+struct ScMyDeleted
+{
+ sal_uInt32 nID = 0;
+ std::unique_ptr<ScMyCellInfo> pCellInfo;
+
+ ScMyDeleted(sal_uInt32 id, std::unique_ptr<ScMyCellInfo> p) : nID(id), pCellInfo(std::move(p)) {}
+};
+
+struct ScMyGenerated
+{
+ ScBigRange aBigRange;
+ sal_uInt32 nID = 0;
+ std::unique_ptr<ScMyCellInfo> pCellInfo;
+
+ ScMyGenerated(ScBigRange range, sal_uInt32 id, std::unique_ptr<ScMyCellInfo> p)
+ : aBigRange(std::move(range)), nID(id), pCellInfo(std::move(p)) {}
+};
+
+struct ScMyInsertionCutOff
+{
+ sal_uInt32 nID;
+ sal_Int32 nPosition;
+
+ ScMyInsertionCutOff(const sal_uInt32 nTempID, const sal_Int32 nTempPosition) :
+ nID(nTempID), nPosition(nTempPosition) {}
+};
+
+struct ScMyMoveCutOff
+{
+ sal_uInt32 nID;
+ sal_Int32 nStartPosition;
+ sal_Int32 nEndPosition;
+
+ ScMyMoveCutOff(const sal_uInt32 nTempID, const sal_Int32 nStartPos, const sal_Int32 nEndPos) :
+ nID(nTempID), nStartPosition(nStartPos), nEndPosition(nEndPos) {}
+};
+
+struct ScMyMoveRanges
+{
+ ScBigRange aSourceRange;
+ ScBigRange aTargetRange;
+
+ ScMyMoveRanges(ScBigRange aSource, ScBigRange aTarget) :
+ aSourceRange(std::move(aSource)), aTargetRange(std::move(aTarget)) {}
+};
+
+struct ScMyBaseAction
+{
+ ScMyActionInfo aInfo;
+ ScBigRange aBigRange;
+ std::vector<sal_uInt32> aDependencies;
+ std::vector<ScMyDeleted> aDeletedList;
+ sal_uInt32 nActionNumber;
+ sal_uInt32 nRejectingNumber;
+ sal_uInt32 nPreviousAction;
+ ScChangeActionType nActionType;
+ ScChangeActionState nActionState;
+
+ explicit ScMyBaseAction(const ScChangeActionType nActionType);
+ virtual ~ScMyBaseAction();
+};
+
+struct ScMyInsAction : public ScMyBaseAction
+{
+ explicit ScMyInsAction(const ScChangeActionType nActionType);
+ virtual ~ScMyInsAction() override;
+};
+
+struct ScMyDelAction : public ScMyBaseAction
+{
+ std::vector<ScMyGenerated> aGeneratedList;
+ std::optional<ScMyInsertionCutOff> moInsCutOff;
+ std::vector<ScMyMoveCutOff> aMoveCutOffs;
+ sal_Int32 nD;
+
+ explicit ScMyDelAction(const ScChangeActionType nActionType);
+ virtual ~ScMyDelAction() override;
+};
+
+struct ScMyMoveAction : public ScMyBaseAction
+{
+ std::vector<ScMyGenerated> aGeneratedList;
+ std::unique_ptr<ScMyMoveRanges> pMoveRanges;
+
+ ScMyMoveAction();
+ virtual ~ScMyMoveAction() override;
+};
+
+struct ScMyContentAction : public ScMyBaseAction
+{
+ std::unique_ptr<ScMyCellInfo> pCellInfo;
+
+ ScMyContentAction();
+ virtual ~ScMyContentAction() override;
+};
+
+struct ScMyRejAction : public ScMyBaseAction
+{
+ ScMyRejAction();
+ virtual ~ScMyRejAction() override;
+};
+
+class ScXMLChangeTrackingImportHelper
+{
+ std::set<OUString> aUsers;
+ std::vector<std::unique_ptr<ScMyBaseAction>> aActions;
+ css::uno::Sequence<sal_Int8> aProtect;
+ ScChangeTrack* pTrack;
+ std::unique_ptr<ScMyBaseAction> pCurrentAction;
+ sal_Int16 nMultiSpanned;
+ sal_Int16 nMultiSpannedSlaveCount;
+
+private:
+ void ConvertInfo(const ScMyActionInfo& aInfo, OUString& rUser, DateTime& aDateTime);
+ std::unique_ptr<ScChangeAction> CreateInsertAction(const ScMyInsAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateDeleteAction(const ScMyDelAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateMoveAction(const ScMyMoveAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateRejectionAction(const ScMyRejAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateContentAction(const ScMyContentAction* pAction, ScDocument& rDoc);
+
+ void CreateGeneratedActions(std::vector<ScMyGenerated>& rList, ScDocument& rDoc);
+
+public:
+ ScXMLChangeTrackingImportHelper();
+ ~ScXMLChangeTrackingImportHelper();
+
+ void SetProtection(const css::uno::Sequence<sal_Int8>& rProtect) { aProtect = rProtect; }
+ void StartChangeAction(const ScChangeActionType nActionType);
+
+ static sal_uInt32 GetIDFromString(std::string_view sID);
+
+ void SetActionNumber(const sal_uInt32 nActionNumber) { pCurrentAction->nActionNumber = nActionNumber; }
+ void SetActionState(const ScChangeActionState nActionState) { pCurrentAction->nActionState = nActionState; }
+ void SetRejectingNumber(const sal_uInt32 nRejectingNumber) { pCurrentAction->nRejectingNumber = nRejectingNumber; }
+ void SetActionInfo(const ScMyActionInfo& aInfo);
+ void SetBigRange(const ScBigRange& aBigRange) { pCurrentAction->aBigRange = aBigRange; }
+ void SetPreviousChange(const sal_uInt32 nPreviousAction, ScMyCellInfo* pCellInfo);
+ void SetPosition(const sal_Int32 nPosition, const sal_Int32 nCount, const sal_Int32 nTable);
+ void AddDependence(const sal_uInt32 nID) { pCurrentAction->aDependencies.push_back(nID); }
+ void AddDeleted(const sal_uInt32 nID);
+ void AddDeleted(const sal_uInt32 nID, std::unique_ptr<ScMyCellInfo> pCellInfo);
+ void SetMultiSpanned(const sal_Int16 nMultiSpanned);
+ void SetInsertionCutOff(const sal_uInt32 nID, const sal_Int32 nPosition);
+ void AddMoveCutOff(const sal_uInt32 nID, const sal_Int32 nStartPosition, const sal_Int32 nEndPosition);
+ void SetMoveRanges(const ScBigRange& aSourceRange, const ScBigRange& aTargetRange);
+ void GetMultiSpannedRange();
+ void AddGenerated(std::unique_ptr<ScMyCellInfo> pCellInfo, const ScBigRange& aBigRange);
+
+ void EndChangeAction();
+
+ void SetDeletionDependencies(ScMyDelAction* pAction, ScChangeActionDel* pDelAct);
+ void SetMovementDependencies(ScMyMoveAction* pAction, ScChangeActionMove* pMoveAct);
+ void SetContentDependencies(const ScMyContentAction* pAction, ScChangeActionContent* pActContent, const ScDocument& rDoc);
+ void SetDependencies(ScMyBaseAction* pAction, ScDocument& rDoc);
+
+ void SetNewCell(const ScMyContentAction* pAction, ScDocument& rDoc);
+
+ void CreateChangeTrack(ScDocument* pDoc);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCodeNameProvider.cxx b/sc/source/filter/xml/XMLCodeNameProvider.cxx
new file mode 100644
index 0000000000..347f21d0f8
--- /dev/null
+++ b/sc/source/filter/xml/XMLCodeNameProvider.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 "XMLCodeNameProvider.hxx"
+#include <document.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace com::sun::star;
+
+bool XMLCodeNameProvider::_getCodeName( const uno::Any& aAny, OUString& rCodeName )
+{
+ uno::Sequence<beans::PropertyValue> aProps;
+ if( !(aAny >>= aProps) )
+ return false;
+
+ for( const auto& rProp : std::as_const(aProps) )
+ {
+ if( rProp.Name == "CodeName" )
+ {
+ OUString sCodeName;
+ if( rProp.Value >>= sCodeName )
+ {
+ rCodeName = sCodeName;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+constexpr OUString gsDocName( u"*doc*"_ustr );
+constexpr OUString gsCodeNameProp( u"CodeName"_ustr );
+
+XMLCodeNameProvider::XMLCodeNameProvider( ScDocument* pDoc ) :
+ mpDoc( pDoc )
+{
+}
+
+XMLCodeNameProvider::~XMLCodeNameProvider()
+{
+}
+
+sal_Bool SAL_CALL XMLCodeNameProvider::hasByName( const OUString& aName )
+{
+ if( aName == gsDocName )
+ return !mpDoc->GetCodeName().isEmpty();
+
+ SCTAB nCount = mpDoc->GetTableCount();
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ if( mpDoc->GetName( i, sSheetName ) && sSheetName == aName )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ return !sCodeName.isEmpty();
+ }
+ }
+
+ return false;
+}
+
+uno::Any SAL_CALL XMLCodeNameProvider::getByName( const OUString& aName )
+{
+ uno::Any aRet;
+ if( aName == gsDocName )
+ {
+ OUString sUCodeName( mpDoc->GetCodeName() );
+ aRet <<= uno::Sequence{ comphelper::makePropertyValue(gsCodeNameProp, sUCodeName) };
+ return aRet;
+ }
+
+ SCTAB nCount = mpDoc->GetTableCount();
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ if( mpDoc->GetName( i, sSheetName ) && sSheetName == aName )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ aRet <<= uno::Sequence{ comphelper::makePropertyValue(gsCodeNameProp, sCodeName) };
+ return aRet;
+ }
+ }
+
+ return aRet;
+}
+
+uno::Sequence< OUString > SAL_CALL XMLCodeNameProvider::getElementNames( )
+{
+ SCTAB nCount = mpDoc->GetTableCount() + 1;
+ std::vector< OUString > aNames;
+ aNames.reserve(nCount);
+
+ if( !mpDoc->GetCodeName().isEmpty() )
+ aNames.push_back(gsDocName);
+
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ if (!sCodeName.isEmpty())
+ {
+ if( mpDoc->GetName( i, sSheetName ) )
+ aNames.push_back(sSheetName);
+ }
+ }
+
+ return comphelper::containerToSequence(aNames);
+}
+
+uno::Type SAL_CALL XMLCodeNameProvider::getElementType( )
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL XMLCodeNameProvider::hasElements()
+{
+ if( !mpDoc->GetCodeName().isEmpty() )
+ return true;
+
+ SCTAB nCount = mpDoc->GetTableCount();
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ if (!sCodeName.isEmpty() && mpDoc->GetName(i, sSheetName))
+ return true;
+ }
+
+ return false;
+}
+
+void XMLCodeNameProvider::set( const uno::Reference< container::XNameAccess>& xNameAccess, ScDocument *pDoc )
+{
+ uno::Any aAny;
+ OUString sDocName("*doc*");
+ OUString sCodeName;
+ if( xNameAccess->hasByName( sDocName ) )
+ {
+ aAny = xNameAccess->getByName( sDocName );
+ if( _getCodeName( aAny, sCodeName ) )
+ pDoc->SetCodeName( sCodeName );
+ }
+
+ SCTAB nCount = pDoc->GetTableCount();
+ OUString sSheetName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ if( pDoc->GetName( i, sSheetName ) &&
+ xNameAccess->hasByName( sSheetName ) )
+ {
+ aAny = xNameAccess->getByName( sSheetName );
+ if( _getCodeName( aAny, sCodeName ) )
+ pDoc->SetCodeName( i, sCodeName );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCodeNameProvider.hxx b/sc/source/filter/xml/XMLCodeNameProvider.hxx
new file mode 100644
index 0000000000..79c0579576
--- /dev/null
+++ b/sc/source/filter/xml/XMLCodeNameProvider.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::container { class XNameAccess; }
+
+class ScDocument;
+
+class XMLCodeNameProvider : public ::cppu::WeakImplHelper< css::container::XNameAccess >
+{
+ ScDocument* mpDoc;
+
+ static bool _getCodeName( const css::uno::Any& aAny,
+ OUString& rCodeName );
+
+public:
+ explicit XMLCodeNameProvider(ScDocument* pDoc);
+ virtual ~XMLCodeNameProvider() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ static void set( const css::uno::Reference< css::container::XNameAccess>& xNameAccess, ScDocument *pDoc );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLColumnRowGroupExport.cxx b/sc/source/filter/xml/XMLColumnRowGroupExport.cxx
new file mode 100644
index 0000000000..55907616a6
--- /dev/null
+++ b/sc/source/filter/xml/XMLColumnRowGroupExport.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 "XMLColumnRowGroupExport.hxx"
+#include "xmlexprt.hxx"
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <algorithm>
+
+using namespace xmloff::token;
+
+ScMyColumnRowGroup::ScMyColumnRowGroup()
+ : nField(0)
+ , nLevel(0)
+ , bDisplay(false)
+{
+}
+
+bool ScMyColumnRowGroup::operator<(const ScMyColumnRowGroup& rGroup) const
+{
+ if (rGroup.nField > nField)
+ return true;
+ else
+ if (rGroup.nField == nField && rGroup.nLevel > nLevel)
+ return true;
+ else
+ return false;
+}
+
+ScMyOpenCloseColumnRowGroup::ScMyOpenCloseColumnRowGroup(ScXMLExport& rTempExport, sal_uInt32 nToken)
+ : rExport(rTempExport),
+ rName(rExport.GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TABLE, GetXMLToken(XMLTokenEnum(nToken))))
+{
+}
+
+ScMyOpenCloseColumnRowGroup::~ScMyOpenCloseColumnRowGroup()
+{
+}
+
+void ScMyOpenCloseColumnRowGroup::NewTable()
+{
+ aTableStart.clear();
+ aTableEnd.clear();
+}
+
+void ScMyOpenCloseColumnRowGroup::AddGroup(const ScMyColumnRowGroup& aGroup, const sal_Int32 nEndField)
+{
+ aTableStart.push_back(aGroup);
+ aTableEnd.push_back(nEndField);
+}
+
+bool ScMyOpenCloseColumnRowGroup::IsGroupStart(const sal_Int32 nField)
+{
+ bool bGroupStart(false);
+ if (!aTableStart.empty())
+ {
+ // when used to find repeated rows at the beginning of a group,
+ // aTableStart may contain entries before nField. They must be skipped here
+ // (they will be used for OpenGroups later in the right order).
+
+ ScMyColumnRowGroupVec::iterator aItr = std::find_if_not(aTableStart.begin(), aTableStart.end(),
+ [&nField](const ScMyColumnRowGroup& rGroup) { return rGroup.nField < nField; });
+ bGroupStart = (aItr != aTableStart.end()) && (aItr->nField == nField);
+ }
+ return bGroupStart;
+}
+
+void ScMyOpenCloseColumnRowGroup::OpenGroup(const ScMyColumnRowGroup& rGroup)
+{
+ if (!rGroup.bDisplay)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_FALSE);
+ rExport.StartElement( rName, true);
+}
+
+void ScMyOpenCloseColumnRowGroup::OpenGroups(const sal_Int32 nField)
+{
+ ScMyColumnRowGroupVec::iterator aItr(aTableStart.begin());
+ bool bReady(false);
+ while(!bReady && aItr != aTableStart.end())
+ {
+ if (aItr->nField == nField)
+ {
+ OpenGroup(*aItr);
+ aItr = aTableStart.erase(aItr);
+ }
+ else
+ bReady = true;
+ }
+}
+
+bool ScMyOpenCloseColumnRowGroup::IsGroupEnd(const sal_Int32 nField)
+{
+ return (!aTableEnd.empty()) && (aTableEnd.front() == nField);
+}
+
+void ScMyOpenCloseColumnRowGroup::CloseGroups(const sal_Int32 nField)
+{
+ ScMyFieldGroupVec::iterator aItr(aTableEnd.begin());
+ bool bReady(false);
+ while(!bReady && aItr != aTableEnd.end())
+ {
+ if (*aItr == nField)
+ {
+ rExport.EndElement( rName, true );
+ aItr = aTableEnd.erase(aItr);
+ }
+ else
+ bReady = true;
+ }
+}
+
+sal_Int32 ScMyOpenCloseColumnRowGroup::GetLast()
+{
+ sal_Int32 maximum(-1);
+ if (!aTableEnd.empty())
+ {
+ ScMyFieldGroupVec::iterator i(std::max_element(aTableEnd.begin(), aTableEnd.end()));
+ if (*i > maximum)
+ maximum = *i;
+ }
+ return maximum;
+}
+
+void ScMyOpenCloseColumnRowGroup::Sort()
+{
+ std::sort(aTableStart.begin(), aTableStart.end());
+ std::sort(aTableEnd.begin(), aTableEnd.end());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLColumnRowGroupExport.hxx b/sc/source/filter/xml/XMLColumnRowGroupExport.hxx
new file mode 100644
index 0000000000..3d1a2ac4f1
--- /dev/null
+++ b/sc/source/filter/xml/XMLColumnRowGroupExport.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+struct ScMyColumnRowGroup
+{
+ sal_Int32 nField;
+ sal_Int16 nLevel;
+ bool bDisplay;
+
+ ScMyColumnRowGroup();
+ bool operator< (const ScMyColumnRowGroup& rGroup) const;
+};
+
+typedef std::vector<ScMyColumnRowGroup> ScMyColumnRowGroupVec;
+typedef std::vector<sal_Int32> ScMyFieldGroupVec;
+
+class ScXMLExport;
+class ScMyOpenCloseColumnRowGroup
+{
+ ScXMLExport& rExport;
+ const OUString rName;
+ ScMyColumnRowGroupVec aTableStart;
+ ScMyFieldGroupVec aTableEnd;
+
+ void OpenGroup(const ScMyColumnRowGroup& rGroup);
+public:
+ ScMyOpenCloseColumnRowGroup(ScXMLExport& rExport, sal_uInt32 nToken);
+ ~ScMyOpenCloseColumnRowGroup();
+
+ void NewTable();
+ void AddGroup(const ScMyColumnRowGroup& aGroup, const sal_Int32 nEndField);
+ bool IsGroupStart(const sal_Int32 nField);
+ void OpenGroups(const sal_Int32 nField);
+ bool IsGroupEnd(const sal_Int32 nField);
+ void CloseGroups(const sal_Int32 nField);
+ sal_Int32 GetLast();
+ void Sort();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConsolidationContext.cxx b/sc/source/filter/xml/XMLConsolidationContext.cxx
new file mode 100644
index 0000000000..610a3e2759
--- /dev/null
+++ b/sc/source/filter/xml/XMLConsolidationContext.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 "XMLConsolidationContext.hxx"
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include "xmlimprt.hxx"
+#include "XMLConverter.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScXMLConsolidationContext::ScXMLConsolidationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ eFunction( SUBTOTAL_FUNC_NONE ),
+ bLinkToSource( false ),
+ bTargetAddr(false)
+{
+ rImport.LockSolarMutex();
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ eFunction = ScXMLConverter::GetSubTotalFuncFromString( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_CELL_RANGE_ADDRESSES ):
+ sSourceList = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_CELL_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ bTargetAddr = ScRangeStringConverter::GetAddressFromString(
+ aTargetAddr, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_USE_LABEL ):
+ sUseLabel = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_LINK_TO_SOURCE_DATA ):
+ bLinkToSource = IsXMLToken( aIter, XML_TRUE );
+ break;
+ }
+ }
+}
+
+ScXMLConsolidationContext::~ScXMLConsolidationContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+void SAL_CALL ScXMLConsolidationContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!bTargetAddr)
+ return;
+
+ std::unique_ptr<ScConsolidateParam> pConsParam(new ScConsolidateParam);
+ pConsParam->nCol = aTargetAddr.Col();
+ pConsParam->nRow = aTargetAddr.Row();
+ pConsParam->nTab = aTargetAddr.Tab();
+ pConsParam->eFunction = eFunction;
+
+ sal_uInt16 nCount = static_cast<sal_uInt16>(std::min( ScRangeStringConverter::GetTokenCount( sSourceList ), sal_Int32(0xFFFF) ));
+ if( nCount )
+ {
+ std::unique_ptr<ScArea[]> ppAreas(new ScArea[ nCount ]);
+ sal_Int32 nOffset = 0;
+ sal_uInt16 nIndex;
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ if ( !ScRangeStringConverter::GetAreaFromString(
+ ppAreas[ nIndex ], sSourceList, *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ) )
+ {
+ //! handle error
+ }
+ }
+
+ pConsParam->SetAreas( std::move(ppAreas), nCount );
+ }
+
+ pConsParam->bByCol = pConsParam->bByRow = false;
+ if( IsXMLToken(sUseLabel, XML_COLUMN ) )
+ pConsParam->bByCol = true;
+ else if( IsXMLToken( sUseLabel, XML_ROW ) )
+ pConsParam->bByRow = true;
+ else if( IsXMLToken( sUseLabel, XML_BOTH ) )
+ pConsParam->bByCol = pConsParam->bByRow = true;
+
+ pConsParam->bReferenceData = bLinkToSource;
+
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if( pDoc )
+ pDoc->SetConsolidateDlgData( std::move(pConsParam) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConsolidationContext.hxx b/sc/source/filter/xml/XMLConsolidationContext.hxx
new file mode 100644
index 0000000000..1843ce67e6
--- /dev/null
+++ b/sc/source/filter/xml/XMLConsolidationContext.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <global.hxx>
+#include <address.hxx>
+#include "importcontext.hxx"
+
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLConsolidationContext : public ScXMLImportContext
+{
+private:
+ OUString sSourceList;
+ OUString sUseLabel;
+ ScAddress aTargetAddr;
+ ScSubTotalFunc eFunction;
+ bool bLinkToSource;
+ bool bTargetAddr;
+
+public:
+ ScXMLConsolidationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList
+ );
+ virtual ~ScXMLConsolidationContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConverter.cxx b/sc/source/filter/xml/XMLConverter.cxx
new file mode 100644
index 0000000000..fd7dcb4728
--- /dev/null
+++ b/sc/source/filter/xml/XMLConverter.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 "XMLConverter.hxx"
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <tools/datetime.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <rangeutl.hxx>
+#include <docuno.hxx>
+#include <generalfunction.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScDocument* ScXMLConverter::GetScDocument( const uno::Reference< frame::XModel >& xModel )
+{
+ if (xModel.is())
+ {
+ ScModelObj* pDocObj = comphelper::getFromUnoTunnel<ScModelObj>( xModel );
+ return pDocObj ? pDocObj->GetDocument() : nullptr;
+ }
+ return nullptr;
+}
+
+sheet::GeneralFunction ScXMLConverter::GetFunctionFromString( std::u16string_view sFunction )
+{
+ if( IsXMLToken(sFunction, XML_SUM ) )
+ return sheet::GeneralFunction_SUM;
+ if( IsXMLToken(sFunction, XML_AUTO ) )
+ return sheet::GeneralFunction_AUTO;
+ if( IsXMLToken(sFunction, XML_COUNT ) )
+ return sheet::GeneralFunction_COUNT;
+ if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
+ return sheet::GeneralFunction_COUNTNUMS;
+ if( IsXMLToken(sFunction, XML_PRODUCT ) )
+ return sheet::GeneralFunction_PRODUCT;
+ if( IsXMLToken(sFunction, XML_AVERAGE ) )
+ return sheet::GeneralFunction_AVERAGE;
+ if( IsXMLToken(sFunction, XML_MAX ) )
+ return sheet::GeneralFunction_MAX;
+ if( IsXMLToken(sFunction, XML_MIN ) )
+ return sheet::GeneralFunction_MIN;
+ if( IsXMLToken(sFunction, XML_STDEV ) )
+ return sheet::GeneralFunction_STDEV;
+ if( IsXMLToken(sFunction, XML_STDEVP ) )
+ return sheet::GeneralFunction_STDEVP;
+ if( IsXMLToken(sFunction, XML_VAR ) )
+ return sheet::GeneralFunction_VAR;
+ if( IsXMLToken(sFunction, XML_VARP ) )
+ return sheet::GeneralFunction_VARP;
+ return sheet::GeneralFunction_NONE;
+}
+
+ScGeneralFunction ScXMLConverter::GetFunctionFromString2( std::u16string_view sFunction )
+{
+ if( IsXMLToken(sFunction, XML_SUM ) )
+ return ScGeneralFunction::SUM;
+ if( IsXMLToken(sFunction, XML_AUTO ) )
+ return ScGeneralFunction::AUTO;
+ if( IsXMLToken(sFunction, XML_COUNT ) )
+ return ScGeneralFunction::COUNT;
+ if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
+ return ScGeneralFunction::COUNTNUMS;
+ if( IsXMLToken(sFunction, XML_PRODUCT ) )
+ return ScGeneralFunction::PRODUCT;
+ if( IsXMLToken(sFunction, XML_AVERAGE ) )
+ return ScGeneralFunction::AVERAGE;
+ if( IsXMLToken(sFunction, XML_MEDIAN ) )
+ return ScGeneralFunction::MEDIAN;
+ if( IsXMLToken(sFunction, XML_MAX ) )
+ return ScGeneralFunction::MAX;
+ if( IsXMLToken(sFunction, XML_MIN ) )
+ return ScGeneralFunction::MIN;
+ if( IsXMLToken(sFunction, XML_STDEV ) )
+ return ScGeneralFunction::STDEV;
+ if( IsXMLToken(sFunction, XML_STDEVP ) )
+ return ScGeneralFunction::STDEVP;
+ if( IsXMLToken(sFunction, XML_VAR ) )
+ return ScGeneralFunction::VAR;
+ if( IsXMLToken(sFunction, XML_VARP ) )
+ return ScGeneralFunction::VARP;
+ return ScGeneralFunction::NONE;
+}
+
+ScSubTotalFunc ScXMLConverter::GetSubTotalFuncFromString( std::u16string_view sFunction )
+{
+ if( IsXMLToken(sFunction, XML_SUM ) )
+ return SUBTOTAL_FUNC_SUM;
+ if( IsXMLToken(sFunction, XML_COUNT ) )
+ return SUBTOTAL_FUNC_CNT;
+ if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
+ return SUBTOTAL_FUNC_CNT2;
+ if( IsXMLToken(sFunction, XML_PRODUCT ) )
+ return SUBTOTAL_FUNC_PROD;
+ if( IsXMLToken(sFunction, XML_AVERAGE ) )
+ return SUBTOTAL_FUNC_AVE;
+ if( IsXMLToken(sFunction, XML_MEDIAN ) )
+ return SUBTOTAL_FUNC_MED;
+ if( IsXMLToken(sFunction, XML_MAX ) )
+ return SUBTOTAL_FUNC_MAX;
+ if( IsXMLToken(sFunction, XML_MIN ) )
+ return SUBTOTAL_FUNC_MIN;
+ if( IsXMLToken(sFunction, XML_STDEV ) )
+ return SUBTOTAL_FUNC_STD;
+ if( IsXMLToken(sFunction, XML_STDEVP ) )
+ return SUBTOTAL_FUNC_STDP;
+ if( IsXMLToken(sFunction, XML_VAR ) )
+ return SUBTOTAL_FUNC_VAR;
+ if( IsXMLToken(sFunction, XML_VARP ) )
+ return SUBTOTAL_FUNC_VARP;
+ return SUBTOTAL_FUNC_NONE;
+}
+
+OUString ScXMLConverter::GetStringFromFunction(
+ sal_Int16 eFunction )
+{
+ OUString sFuncStr;
+ switch( eFunction )
+ {
+ case sheet::GeneralFunction2::AUTO: sFuncStr = GetXMLToken( XML_AUTO ); break;
+ case sheet::GeneralFunction2::AVERAGE: sFuncStr = GetXMLToken( XML_AVERAGE ); break;
+ case sheet::GeneralFunction2::MEDIAN: sFuncStr = GetXMLToken( XML_MEDIAN ); break;
+ case sheet::GeneralFunction2::COUNT: sFuncStr = GetXMLToken( XML_COUNT ); break;
+ case sheet::GeneralFunction2::COUNTNUMS: sFuncStr = GetXMLToken( XML_COUNTNUMS ); break;
+ case sheet::GeneralFunction2::MAX: sFuncStr = GetXMLToken( XML_MAX ); break;
+ case sheet::GeneralFunction2::MIN: sFuncStr = GetXMLToken( XML_MIN ); break;
+ case sheet::GeneralFunction2::NONE: sFuncStr = GetXMLToken( XML_NONE ); break;
+ case sheet::GeneralFunction2::PRODUCT: sFuncStr = GetXMLToken( XML_PRODUCT ); break;
+ case sheet::GeneralFunction2::STDEV: sFuncStr = GetXMLToken( XML_STDEV ); break;
+ case sheet::GeneralFunction2::STDEVP: sFuncStr = GetXMLToken( XML_STDEVP ); break;
+ case sheet::GeneralFunction2::SUM: sFuncStr = GetXMLToken( XML_SUM ); break;
+ case sheet::GeneralFunction2::VAR: sFuncStr = GetXMLToken( XML_VAR ); break;
+ case sheet::GeneralFunction2::VARP: sFuncStr = GetXMLToken( XML_VARP ); break;
+ default:
+ {
+ assert(false);
+ }
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sFuncStr, false );
+ return str;
+}
+
+OUString ScXMLConverter::GetStringFromFunction(
+ const ScSubTotalFunc eFunction )
+{
+ OUString sFuncStr;
+ switch( eFunction )
+ {
+ case SUBTOTAL_FUNC_AVE: sFuncStr = GetXMLToken( XML_AVERAGE ); break;
+ case SUBTOTAL_FUNC_MED: sFuncStr = GetXMLToken( XML_MEDIAN ); break;
+ case SUBTOTAL_FUNC_CNT: sFuncStr = GetXMLToken( XML_COUNT ); break;
+ case SUBTOTAL_FUNC_CNT2: sFuncStr = GetXMLToken( XML_COUNTNUMS ); break;
+ case SUBTOTAL_FUNC_MAX: sFuncStr = GetXMLToken( XML_MAX ); break;
+ case SUBTOTAL_FUNC_MIN: sFuncStr = GetXMLToken( XML_MIN ); break;
+ case SUBTOTAL_FUNC_NONE: sFuncStr = GetXMLToken( XML_NONE ); break;
+ case SUBTOTAL_FUNC_PROD: sFuncStr = GetXMLToken( XML_PRODUCT ); break;
+ case SUBTOTAL_FUNC_STD: sFuncStr = GetXMLToken( XML_STDEV ); break;
+ case SUBTOTAL_FUNC_STDP: sFuncStr = GetXMLToken( XML_STDEVP ); break;
+ case SUBTOTAL_FUNC_SUM: sFuncStr = GetXMLToken( XML_SUM ); break;
+ case SUBTOTAL_FUNC_SELECTION_COUNT: break;
+ // it is not needed as it is only a UI value and not document content
+
+ case SUBTOTAL_FUNC_VAR: sFuncStr = GetXMLToken( XML_VAR ); break;
+ case SUBTOTAL_FUNC_VARP: sFuncStr = GetXMLToken( XML_VARP ); break;
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sFuncStr, false );
+ return str;
+}
+
+sheet::DataPilotFieldOrientation ScXMLConverter::GetOrientationFromString(
+ std::u16string_view rString )
+{
+ if( IsXMLToken(rString, XML_COLUMN ) )
+ return sheet::DataPilotFieldOrientation_COLUMN;
+ if( IsXMLToken(rString, XML_ROW ) )
+ return sheet::DataPilotFieldOrientation_ROW;
+ if( IsXMLToken(rString, XML_PAGE ) )
+ return sheet::DataPilotFieldOrientation_PAGE;
+ if( IsXMLToken(rString, XML_DATA ) )
+ return sheet::DataPilotFieldOrientation_DATA;
+ return sheet::DataPilotFieldOrientation_HIDDEN;
+}
+
+OUString ScXMLConverter::GetStringFromOrientation(
+ const sheet::DataPilotFieldOrientation eOrientation )
+{
+ OUString sOrientStr;
+ switch( eOrientation )
+ {
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ sOrientStr = GetXMLToken( XML_HIDDEN );
+ break;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ sOrientStr = GetXMLToken( XML_COLUMN );
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ sOrientStr = GetXMLToken( XML_ROW );
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ sOrientStr = GetXMLToken( XML_PAGE );
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ sOrientStr = GetXMLToken( XML_DATA );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sOrientStr, false );
+ return str;
+}
+
+ScDetectiveObjType ScXMLConverter::GetDetObjTypeFromString( std::u16string_view rString )
+{
+ if( IsXMLToken(rString, XML_FROM_SAME_TABLE ) )
+ return SC_DETOBJ_ARROW;
+ if( IsXMLToken(rString, XML_FROM_ANOTHER_TABLE ) )
+ return SC_DETOBJ_FROMOTHERTAB;
+ if( IsXMLToken(rString, XML_TO_ANOTHER_TABLE ) )
+ return SC_DETOBJ_TOOTHERTAB;
+ return SC_DETOBJ_NONE;
+}
+
+bool ScXMLConverter::GetDetOpTypeFromString( ScDetOpType& rDetOpType, std::u16string_view rString )
+{
+ if( IsXMLToken(rString, XML_TRACE_DEPENDENTS ) )
+ rDetOpType = SCDETOP_ADDSUCC;
+ else if( IsXMLToken(rString, XML_TRACE_PRECEDENTS ) )
+ rDetOpType = SCDETOP_ADDPRED;
+ else if( IsXMLToken(rString, XML_TRACE_ERRORS ) )
+ rDetOpType = SCDETOP_ADDERROR;
+ else if( IsXMLToken(rString, XML_REMOVE_DEPENDENTS ) )
+ rDetOpType = SCDETOP_DELSUCC;
+ else if( IsXMLToken(rString, XML_REMOVE_PRECEDENTS ) )
+ rDetOpType = SCDETOP_DELPRED;
+ else
+ return false;
+ return true;
+}
+
+OUString ScXMLConverter::GetStringFromDetObjType(
+ const ScDetectiveObjType eObjType )
+{
+ OUString sTypeStr;
+ switch( eObjType )
+ {
+ case SC_DETOBJ_ARROW:
+ sTypeStr = GetXMLToken( XML_FROM_SAME_TABLE );
+ break;
+ case SC_DETOBJ_FROMOTHERTAB:
+ sTypeStr = GetXMLToken( XML_FROM_ANOTHER_TABLE );
+ break;
+ case SC_DETOBJ_TOOTHERTAB:
+ sTypeStr = GetXMLToken( XML_TO_ANOTHER_TABLE );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sTypeStr, false );
+ return str;
+}
+
+OUString ScXMLConverter::GetStringFromDetOpType(
+ const ScDetOpType eOpType )
+{
+ OUString sTypeStr;
+ switch( eOpType )
+ {
+ case SCDETOP_ADDSUCC:
+ sTypeStr = GetXMLToken( XML_TRACE_DEPENDENTS );
+ break;
+ case SCDETOP_ADDPRED:
+ sTypeStr = GetXMLToken( XML_TRACE_PRECEDENTS );
+ break;
+ case SCDETOP_ADDERROR:
+ sTypeStr = GetXMLToken( XML_TRACE_ERRORS );
+ break;
+ case SCDETOP_DELSUCC:
+ sTypeStr = GetXMLToken( XML_REMOVE_DEPENDENTS );
+ break;
+ case SCDETOP_DELPRED:
+ sTypeStr = GetXMLToken( XML_REMOVE_PRECEDENTS );
+ break;
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sTypeStr, false );
+ return str;
+}
+
+void ScXMLConverter::ConvertCellRangeAddress(OUString& sFormula)
+{
+ OUStringBuffer sBuffer(sFormula.getLength());
+ bool bInQuotationMarks(false);
+ sal_Unicode chPrevious('=');
+ const sal_Unicode* p = sFormula.getStr();
+ const sal_Unicode* const pStop = p + sFormula.getLength();
+ for ( ; p < pStop; ++p)
+ {
+ const sal_Unicode c = *p;
+ if (c == '\'')
+ bInQuotationMarks = !bInQuotationMarks;
+ if (bInQuotationMarks)
+ sBuffer.append(c);
+ else if ((c != '.') ||
+ !((chPrevious == ':') || (chPrevious == ' ') || (chPrevious == '=')))
+ sBuffer.append(c);
+ chPrevious = c;
+ }
+
+ sFormula = sBuffer.makeStringAndClear();
+}
+
+void ScXMLConverter::ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate)
+{
+ css::util::DateTime aAPIDateTime = aDateTime.GetUNODateTime();
+ ::sax::Converter::convertDateTime(sDate, aAPIDateTime, nullptr);
+}
+
+namespace {
+
+/** Enumerates different types of condition tokens. */
+enum ScXMLConditionTokenType
+{
+ XML_COND_TYPE_KEYWORD, /// Simple keyword without parentheses, e.g. 'and'.
+ XML_COND_TYPE_COMPARISON, /// Comparison rule, e.g. 'cell-content()<=2'.
+ XML_COND_TYPE_FUNCTION0, /// Function without parameters, e.g. 'cell-content-is-whole-number()'.
+ XML_COND_TYPE_FUNCTION1, /// Function with 1 parameter, e.g. 'is-true-formula(1+1=2)'.
+ XML_COND_TYPE_FUNCTION2 /// Function with 2 parameters, e.g. 'cell-content-is-between(1,2)'.
+};
+
+struct ScXMLConditionInfo
+{
+ ScXMLConditionToken meToken;
+ ScXMLConditionTokenType meType;
+ sheet::ValidationType meValidation;
+ sheet::ConditionOperator meOperator;
+ const char* mpcIdentifier;
+ sal_Int32 mnIdentLength;
+};
+
+const ScXMLConditionInfo spConditionInfos[] =
+{
+ { XML_COND_AND, XML_COND_TYPE_KEYWORD, sheet::ValidationType_ANY, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "and" ) },
+ { XML_COND_CELLCONTENT, XML_COND_TYPE_COMPARISON, sheet::ValidationType_ANY, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content" ) },
+ { XML_COND_ISBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_ANY, sheet::ConditionOperator_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-between" ) },
+ { XML_COND_ISNOTBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_ANY, sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-not-between" ) },
+ { XML_COND_ISWHOLENUMBER, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_WHOLE, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-whole-number" ) },
+ { XML_COND_ISDECIMALNUMBER, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_DECIMAL, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-decimal-number" ) },
+ { XML_COND_ISDATE, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_DATE, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-date" ) },
+ { XML_COND_ISTIME, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_TIME, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-time" ) },
+ { XML_COND_ISINLIST, XML_COND_TYPE_FUNCTION1, sheet::ValidationType_LIST, sheet::ConditionOperator_EQUAL, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-in-list" ) },
+ { XML_COND_TEXTLENGTH, XML_COND_TYPE_COMPARISON, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length" ) },
+ { XML_COND_TEXTLENGTH_ISBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-between" ) },
+ { XML_COND_TEXTLENGTH_ISNOTBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-not-between" ) },
+ { XML_COND_ISTRUEFORMULA, XML_COND_TYPE_FUNCTION1, sheet::ValidationType_CUSTOM, sheet::ConditionOperator_FORMULA, RTL_CONSTASCII_STRINGPARAM( "is-true-formula" ) }
+};
+
+void lclSkipWhitespace( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ while( (rpcString < pcEnd) && (*rpcString <= ' ') ) ++rpcString;
+}
+
+const ScXMLConditionInfo* lclGetConditionInfo( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ lclSkipWhitespace( rpcString, pcEnd );
+ /* Search the end of an identifier name; assuming that valid identifiers
+ consist of [a-z-] only. */
+ const sal_Unicode* pcIdStart = rpcString;
+ while( (rpcString < pcEnd) && (((*rpcString >= 'a') && (*rpcString <= 'z')) || (*rpcString == '-')) ) ++rpcString;
+ sal_Int32 nLength = static_cast< sal_Int32 >( rpcString - pcIdStart );
+
+ // search the table for an entry
+ if( nLength > 0 )
+ for(auto const &rInfo : spConditionInfos)
+ if((nLength == rInfo.mnIdentLength)
+ && (::rtl_ustr_ascii_shortenedCompare_WithLength(pcIdStart, nLength, rInfo.mpcIdentifier, nLength) == 0) )
+ return &rInfo;
+
+ return nullptr;
+}
+
+sheet::ConditionOperator lclGetConditionOperator( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ // check for double-char operators
+ if( (rpcString + 1 < pcEnd) && (rpcString[ 1 ] == '=') )
+ {
+ sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
+ switch( *rpcString )
+ {
+ case '!': eOperator = sheet::ConditionOperator_NOT_EQUAL; break;
+ case '<': eOperator = sheet::ConditionOperator_LESS_EQUAL; break;
+ case '>': eOperator = sheet::ConditionOperator_GREATER_EQUAL; break;
+ }
+ if( eOperator != sheet::ConditionOperator_NONE )
+ {
+ rpcString += 2;
+ return eOperator;
+ }
+ }
+
+ // check for single-char operators
+ if( rpcString < pcEnd )
+ {
+ sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
+ switch( *rpcString )
+ {
+ case '=': eOperator = sheet::ConditionOperator_EQUAL; break;
+ case '<': eOperator = sheet::ConditionOperator_LESS; break;
+ case '>': eOperator = sheet::ConditionOperator_GREATER; break;
+ }
+ if( eOperator != sheet::ConditionOperator_NONE )
+ {
+ ++rpcString;
+ return eOperator;
+ }
+ }
+
+ return sheet::ConditionOperator_NONE;
+}
+
+/** Skips a literal string in a formula expression.
+
+ @param rpcString
+ (in-out) On call, must point to the first character of the string
+ following the leading string delimiter character. On return, points to
+ the trailing string delimiter character if existing, otherwise to
+ pcEnd.
+
+ @param pcEnd
+ The end of the string to parse.
+
+ @param cQuoteChar
+ The string delimiter character enclosing the string.
+ */
+void lclSkipExpressionString( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cQuoteChar )
+{
+ if( rpcString < pcEnd )
+ {
+ sal_Int32 nLength = static_cast< sal_Int32 >( pcEnd - rpcString );
+ sal_Int32 nNextQuote = ::rtl_ustr_indexOfChar_WithLength( rpcString, nLength, cQuoteChar );
+ if( nNextQuote >= 0 )
+ rpcString += nNextQuote;
+ else
+ rpcString = pcEnd;
+ }
+}
+
+/** Skips a formula expression. Processes embedded parentheses, braces, and
+ literal strings.
+
+ @param rpcString
+ (in-out) On call, must point to the first character of the expression.
+ On return, points to the passed end character if existing, otherwise to
+ pcEnd.
+
+ @param pcEnd
+ The end of the string to parse.
+
+ @param cEndChar
+ The termination character following the expression.
+ */
+void lclSkipExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
+{
+ while( rpcString < pcEnd )
+ {
+ if( *rpcString == cEndChar )
+ return;
+ switch( *rpcString )
+ {
+ case '(': lclSkipExpression( ++rpcString, pcEnd, ')' ); break;
+ case '{': lclSkipExpression( ++rpcString, pcEnd, '}' ); break;
+ case '"': lclSkipExpressionString( ++rpcString, pcEnd, '"' ); break;
+ case '\'': lclSkipExpressionString( ++rpcString, pcEnd, '\'' ); break;
+ }
+ if( rpcString < pcEnd ) ++rpcString;
+ }
+}
+
+/** Extracts a formula expression. Processes embedded parentheses, braces, and
+ literal strings.
+
+ @param rpcString
+ (in-out) On call, must point to the first character of the expression.
+ On return, points *behind* the passed end character if existing,
+ otherwise to pcEnd.
+
+ @param pcEnd
+ The end of the string to parse.
+
+ @param cEndChar
+ The termination character following the expression.
+ */
+/** Tries to skip an empty pair of parentheses (which may contain whitespace
+ characters).
+
+ @return
+ True on success, rpcString points behind the closing parentheses then.
+ */
+bool lclSkipEmptyParentheses( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ if( (rpcString < pcEnd) && (*rpcString == '(') )
+ {
+ lclSkipWhitespace( ++rpcString, pcEnd );
+ if( (rpcString < pcEnd) && (*rpcString == ')') )
+ {
+ ++rpcString;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void ScXMLConditionHelper::parseCondition(
+ ScXMLConditionParseResult& rParseResult, const OUString& rAttribute, sal_Int32 nStartIndex )
+{
+ rParseResult.meToken = XML_COND_INVALID;
+ if( (nStartIndex < 0) || (nStartIndex >= rAttribute.getLength()) ) return;
+
+ // try to find an identifier
+ const sal_Unicode* pcBegin = rAttribute.getStr();
+ const sal_Unicode* pcString = pcBegin + nStartIndex;
+ const sal_Unicode* pcEnd = pcBegin + rAttribute.getLength();
+ const ScXMLConditionInfo* pCondInfo = lclGetConditionInfo( pcString, pcEnd );
+ if( !pCondInfo )
+ return;
+
+ // insert default values into parse result (may be changed below)
+ rParseResult.meValidation = pCondInfo->meValidation;
+ rParseResult.meOperator = pCondInfo->meOperator;
+ // continue parsing dependent on token type
+ switch( pCondInfo->meType )
+ {
+ case XML_COND_TYPE_KEYWORD:
+ // nothing specific has to follow, success
+ rParseResult.meToken = pCondInfo->meToken;
+ break;
+
+ case XML_COND_TYPE_COMPARISON:
+ // format is <condition>()<operator><expression>
+ if( lclSkipEmptyParentheses( pcString, pcEnd ) )
+ {
+ rParseResult.meOperator = lclGetConditionOperator( pcString, pcEnd );
+ if( rParseResult.meOperator != sheet::ConditionOperator_NONE )
+ {
+ lclSkipWhitespace( pcString, pcEnd );
+ if( pcString < pcEnd )
+ {
+ rParseResult.meToken = pCondInfo->meToken;
+ // comparison must be at end of attribute, remaining text is the formula
+ rParseResult.maOperand1 = OUString( pcString, static_cast< sal_Int32 >( pcEnd - pcString ) );
+ }
+ }
+ }
+ break;
+
+ case XML_COND_TYPE_FUNCTION0:
+ // format is <condition>()
+ if( lclSkipEmptyParentheses( pcString, pcEnd ) )
+ rParseResult.meToken = pCondInfo->meToken;
+ break;
+
+ case XML_COND_TYPE_FUNCTION1:
+ // format is <condition>(<expression>)
+ if( (pcString < pcEnd) && (*pcString == '(') )
+ {
+ rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ')' );
+ if( !rParseResult.maOperand1.isEmpty() )
+ rParseResult.meToken = pCondInfo->meToken;
+ }
+ break;
+
+ case XML_COND_TYPE_FUNCTION2:
+ // format is <condition>(<expression1>,<expression2>)
+ if( (pcString < pcEnd) && (*pcString == '(') )
+ {
+ rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ',' );
+ if( !rParseResult.maOperand1.isEmpty() )
+ {
+ rParseResult.maOperand2 = getExpression( pcString, pcEnd, ')' );
+ if( !rParseResult.maOperand2.isEmpty() )
+ rParseResult.meToken = pCondInfo->meToken;
+ }
+ }
+ break;
+ }
+ rParseResult.mnEndIndex = static_cast< sal_Int32 >( pcString - pcBegin );
+}
+
+OUString ScXMLConditionHelper::getExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
+{
+ OUString aExp;
+ const sal_Unicode* pcExpStart = rpcString;
+ lclSkipExpression( rpcString, pcEnd, cEndChar );
+ if( rpcString < pcEnd )
+ {
+ aExp = OUString( pcExpStart, static_cast< sal_Int32 >( rpcString - pcExpStart ) ).trim();
+ ++rpcString;
+ }
+ return aExp;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConverter.hxx b/sc/source/filter/xml/XMLConverter.hxx
new file mode 100644
index 0000000000..ad29b1f501
--- /dev/null
+++ b/sc/source/filter/xml/XMLConverter.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <global.hxx>
+#include <detfunc.hxx>
+#include <detdata.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/GeneralFunction.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+
+namespace com::sun::star::frame { class XModel; }
+
+class ScDocument;
+class DateTime;
+enum class ScGeneralFunction;
+
+class ScXMLConverter
+{
+public:
+ ScXMLConverter() = delete;
+
+// helper methods
+ static ScDocument* GetScDocument(
+ const css::uno::Reference< css::frame::XModel >& xModel );
+
+// IMPORT: GeneralFunction / ScSubTotalFunc
+ static css::sheet::GeneralFunction
+ GetFunctionFromString(
+ std::u16string_view rString );
+ static ScGeneralFunction
+ GetFunctionFromString2(
+ std::u16string_view rString );
+
+ static ScSubTotalFunc GetSubTotalFuncFromString(
+ std::u16string_view rString );
+
+// EXPORT: GeneralFunctio2 / ScSubTotalFunc
+ static OUString GetStringFromFunction(
+ const sal_Int16 eFunction );
+ static OUString GetStringFromFunction(
+ const ScSubTotalFunc eFunction );
+
+// IMPORT: DataPilotFieldOrientation
+ static css::sheet::DataPilotFieldOrientation
+ GetOrientationFromString(
+ std::u16string_view rString );
+
+// EXPORT: DataPilotFieldOrientation
+ static OUString GetStringFromOrientation(
+ const css::sheet::DataPilotFieldOrientation eOrientation );
+
+// IMPORT: Detective
+ static ScDetectiveObjType
+ GetDetObjTypeFromString(
+ std::u16string_view rString );
+ static bool GetDetOpTypeFromString(
+ ScDetOpType& rDetOpType,
+ std::u16string_view rString );
+
+// EXPORT: Detective
+ static OUString GetStringFromDetObjType(
+ const ScDetectiveObjType eObjType );
+ static OUString GetStringFromDetOpType(
+ const ScDetOpType eOpType );
+
+// IMPORT: Formulas
+ static void ConvertCellRangeAddress(
+ OUString& sFormula);
+// EXPORT: Core Date Time
+ static void ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate);
+
+};
+
+enum ScXMLConditionToken
+{
+ XML_COND_INVALID, /// Token not recognized.
+ XML_COND_AND, /// The 'and' token.
+ XML_COND_CELLCONTENT, /// The 'cell-content' token.
+ XML_COND_ISBETWEEN, /// The 'cell-content-is-between' token.
+ XML_COND_ISNOTBETWEEN, /// The 'cell-content-is-not-between' token.
+ XML_COND_ISWHOLENUMBER, /// The 'cell-content-is-whole-number' token.
+ XML_COND_ISDECIMALNUMBER, /// The 'cell-content-is-decimal-number' token.
+ XML_COND_ISDATE, /// The 'cell-content-is-date' token.
+ XML_COND_ISTIME, /// The 'cell-content-is-time' token.
+ XML_COND_ISINLIST, /// The 'cell-content-is-in-list' token.
+ XML_COND_TEXTLENGTH, /// The 'cell-content-text-length' token.
+ XML_COND_TEXTLENGTH_ISBETWEEN, /// The 'cell-content-text-length-is-between' token.
+ XML_COND_TEXTLENGTH_ISNOTBETWEEN, /// The 'cell-content-text-length-is-not-between' token.
+ XML_COND_ISTRUEFORMULA /// The 'is-true-formula' token.
+};
+
+/** Result of an attempt to parse a single condition in a 'condition' attribute
+ value of e.g. conditional formatting or data validation.
+ */
+struct ScXMLConditionParseResult
+{
+ ScXMLConditionToken meToken; /// The leading condition token.
+ css::sheet::ValidationType
+ meValidation; /// A data validation type if existing.
+ css::sheet::ConditionOperator
+ meOperator; /// A comparison operator if existing.
+ OUString maOperand1; /// First operand of the token or comparison value.
+ OUString maOperand2; /// Second operand of 'between' conditions.
+ sal_Int32 mnEndIndex; /// Index of first character following the condition.
+
+ ScXMLConditionParseResult()
+ : meToken(XML_COND_INVALID)
+ , meValidation(css::sheet::ValidationType_ANY)
+ , meOperator(css::sheet::ConditionOperator_NONE)
+ , mnEndIndex(-1)
+ {
+ }
+};
+
+namespace ScXMLConditionHelper
+{
+ /** Parses the next condition in a 'condition' attribute value of e.g.
+ conditional formatting or data validation.
+ */
+ void parseCondition(
+ ScXMLConditionParseResult& rParseResult,
+ const OUString& rAttribute,
+ sal_Int32 nStartIndex );
+
+ OUString getExpression(const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDDELinksContext.cxx b/sc/source/filter/xml/XMLDDELinksContext.cxx
new file mode 100644
index 0000000000..a44308da5d
--- /dev/null
+++ b/sc/source/filter/xml/XMLDDELinksContext.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 "XMLDDELinksContext.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+#include <scmatrix.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLDDELinksContext::ScXMLDDELinksContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // here are no attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLDDELinksContext::~ScXMLDDELinksContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDELinksContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if ( nElement == XML_ELEMENT( TABLE, XML_DDE_LINK) )
+ pContext = new ScXMLDDELinkContext(GetScImport());
+
+ return pContext;
+}
+
+ScXMLDDELinkContext::ScXMLDDELinkContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport ),
+ nPosition(-1),
+ nColumns(0),
+ nRows(0),
+ nMode(SC_DDE_DEFAULT)
+{
+ // here are no attributes
+}
+
+ScXMLDDELinkContext::~ScXMLDDELinkContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDELinkContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_DDE_SOURCE ):
+ pContext = new ScXMLDDESourceContext(GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ pContext = new ScXMLDDETableContext(GetScImport(), this);
+ break;
+ }
+
+ return pContext;
+}
+
+void ScXMLDDELinkContext::CreateDDELink()
+{
+ if (GetScImport().GetDocument() &&
+ !sApplication.isEmpty() &&
+ !sTopic.isEmpty() &&
+ !sItem.isEmpty())
+ {
+ GetScImport().GetDocument()->CreateDdeLink(sApplication, sTopic, sItem, nMode, ScMatrixRef());
+ size_t nPos;
+ if(GetScImport().GetDocument()->FindDdeLink(sApplication, sTopic, sItem, nMode, nPos))
+ nPosition = nPos;
+ else
+ {
+ nPosition = -1;
+ SAL_WARN("sc" , "DDE Link not inserted");
+ }
+ }
+}
+
+void ScXMLDDELinkContext::AddCellToRow(const ScDDELinkCell& aCell)
+{
+ aDDELinkRow.push_back(aCell);
+}
+
+void ScXMLDDELinkContext::AddRowsToTable(const sal_Int32 nRowsP)
+{
+ for (sal_Int32 i = 0; i < nRowsP; ++i)
+ aDDELinkTable.insert(aDDELinkTable.end(), aDDELinkRow.begin(), aDDELinkRow.end());
+ aDDELinkRow.clear();
+}
+
+void SAL_CALL ScXMLDDELinkContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (!(nPosition > -1 && nColumns && nRows))
+ return;
+
+ bool bSizeMatch = (static_cast<size_t>(nColumns * nRows) == aDDELinkTable.size());
+ OSL_ENSURE( bSizeMatch, "ScXMLDDELinkContext::EndElement: matrix dimension doesn't match cells count");
+ // Excel writes bad ODF in that it does not write the
+ // table:number-columns-repeated attribute of the
+ // <table:table-column> element, but apparently uses the number of
+ // <table:table-cell> elements within a <table:table-row> element to
+ // determine the column count instead. Be lenient ...
+ if (!bSizeMatch && nColumns == 1)
+ {
+ nColumns = aDDELinkTable.size() / nRows;
+ OSL_ENSURE( static_cast<size_t>(nColumns * nRows) == aDDELinkTable.size(),
+ "ScXMLDDELinkContext::EndElement: adapted matrix dimension doesn't match either");
+ }
+ ScMatrixRef pMatrix = new ScMatrix(static_cast<SCSIZE>(nColumns), static_cast<SCSIZE>(nRows), 0.0);
+ sal_Int32 nCol(0);
+ sal_Int32 nRow(-1);
+ sal_Int32 nIndex(0);
+
+ svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
+ for (const auto& rDDELinkCell : aDDELinkTable)
+ {
+ if (nIndex % nColumns == 0)
+ {
+ ++nRow;
+ nCol = 0;
+ }
+ else
+ ++nCol;
+
+ SCSIZE nScCol( static_cast< SCSIZE >( nCol ) );
+ SCSIZE nScRow( static_cast< SCSIZE >( nRow ) );
+ if( rDDELinkCell.bEmpty )
+ pMatrix->PutEmpty( nScCol, nScRow );
+ else if( rDDELinkCell.bString )
+ pMatrix->PutString(rPool.intern(rDDELinkCell.sValue), nScCol, nScRow);
+ else
+ pMatrix->PutDouble( rDDELinkCell.fValue, nScCol, nScRow );
+
+ ++nIndex;
+ }
+
+ GetScImport().GetDocument()->SetDdeLinkResultMatrix( static_cast< sal_uInt16 >( nPosition ), pMatrix );
+}
+
+ScXMLDDESourceContext::ScXMLDDESourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ pDDELink(pTempDDELink)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( OFFICE, XML_DDE_APPLICATION ):
+ pDDELink->SetApplication(aIter.toString());
+ break;
+ case XML_ELEMENT( OFFICE, XML_DDE_TOPIC ):
+ pDDELink->SetTopic(aIter.toString());
+ break;
+ case XML_ELEMENT( OFFICE, XML_DDE_ITEM ):
+ pDDELink->SetItem(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_CONVERSION_MODE ):
+ if (IsXMLToken(aIter, XML_INTO_ENGLISH_NUMBER))
+ pDDELink->SetMode(SC_DDE_ENGLISH);
+ else if (IsXMLToken(aIter, XML_KEEP_TEXT))
+ pDDELink->SetMode(SC_DDE_TEXT);
+ else
+ pDDELink->SetMode(SC_DDE_DEFAULT);
+ break;
+ }
+ }
+}
+
+ScXMLDDESourceContext::~ScXMLDDESourceContext()
+{
+}
+
+void SAL_CALL ScXMLDDESourceContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDDELink->CreateDDELink();
+}
+
+ScXMLDDETableContext::ScXMLDDETableContext( ScXMLImport& rImport,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ pDDELink(pTempDDELink)
+{
+ // here are no attributes
+}
+
+ScXMLDDETableContext::~ScXMLDDETableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDETableContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN ):
+ pContext = new ScXMLDDEColumnContext(GetScImport(), pAttribList, pDDELink);
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ pContext = new ScXMLDDERowContext(GetScImport(), pAttribList, pDDELink);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDDEColumnContext::ScXMLDDEColumnContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink) :
+ ScXMLImportContext( rImport )
+{
+ if ( rAttrList.is() )
+ {
+ sal_Int32 nCols(1);
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ) ) );
+ if (aIter != rAttrList->end())
+ nCols = aIter.toInt32();
+
+ pDDELink->AddColumns(nCols);
+ }
+}
+
+ScXMLDDEColumnContext::~ScXMLDDEColumnContext()
+{
+}
+
+ScXMLDDERowContext::ScXMLDDERowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ pDDELink(pTempDDELink),
+ nRows(1)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NUMBER_ROWS_REPEATED ) ) );
+ if (aIter != rAttrList->end())
+ nRows = aIter.toInt32();
+
+ pDDELink->AddRows(nRows);
+ }
+}
+
+ScXMLDDERowContext::~ScXMLDDERowContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDERowContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_TABLE_CELL ))
+ pContext = new ScXMLDDECellContext(GetScImport(), pAttribList, pDDELink);
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDDERowContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDDELink->AddRowsToTable(nRows);
+}
+
+ScXMLDDECellContext::ScXMLDDECellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ fValue(),
+ nCells(1),
+ bString(true),
+ bString2(true),
+ bEmpty(true),
+ pDDELink(pTempDDELink)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ if (IsXMLToken(aIter, XML_STRING))
+ bString = true;
+ else
+ bString = false;
+ break;
+ case XML_ELEMENT( OFFICE, XML_STRING_VALUE ):
+ sValue = aIter.toString();
+ bEmpty = false;
+ bString2 = true;
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ fValue = aIter.toDouble();
+ bEmpty = false;
+ bString2 = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ nCells = aIter.toInt32();
+ break;
+ }
+ }
+}
+
+ScXMLDDECellContext::~ScXMLDDECellContext()
+{
+}
+
+void SAL_CALL ScXMLDDECellContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ OSL_ENSURE(bString == bString2, "something wrong with this type");
+ ScDDELinkCell aCell;
+ aCell.sValue = sValue;
+ aCell.fValue = fValue;
+ aCell.bEmpty = bEmpty;
+ aCell.bString = bString2;
+ for(sal_Int32 i = 0; i < nCells; ++i)
+ pDDELink->AddCellToRow(aCell);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDDELinksContext.hxx b/sc/source/filter/xml/XMLDDELinksContext.hxx
new file mode 100644
index 0000000000..a599786203
--- /dev/null
+++ b/sc/source/filter/xml/XMLDDELinksContext.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+#include <vector>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLDDELinksContext : public ScXMLImportContext
+{
+public:
+ ScXMLDDELinksContext( ScXMLImport& rImport);
+
+ virtual ~ScXMLDDELinksContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+struct ScDDELinkCell
+{
+ OUString sValue;
+ double fValue;
+ bool bString;
+ bool bEmpty;
+};
+
+typedef std::vector<ScDDELinkCell> ScDDELinkCells;
+
+class ScXMLDDELinkContext : public ScXMLImportContext
+{
+ ScDDELinkCells aDDELinkTable;
+ ScDDELinkCells aDDELinkRow;
+ OUString sApplication;
+ OUString sTopic;
+ OUString sItem;
+ sal_Int32 nPosition;
+ sal_Int32 nColumns;
+ sal_Int32 nRows;
+ sal_uInt8 nMode;
+
+public:
+ ScXMLDDELinkContext( ScXMLImport& rImport);
+
+ virtual ~ScXMLDDELinkContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SetApplication(const OUString& sValue) { sApplication = sValue; }
+ void SetTopic(const OUString& sValue) { sTopic = sValue; }
+ void SetItem(const OUString& sValue) { sItem = sValue; }
+ void SetMode(const sal_uInt8 nValue) { nMode = nValue; }
+ void CreateDDELink();
+ void AddColumns(const sal_Int32 nValue) { nColumns += nValue; }
+ void AddRows(const sal_Int32 nValue) { nRows += nValue; }
+ void AddCellToRow(const ScDDELinkCell& aCell);
+ void AddRowsToTable(const sal_Int32 nRows);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDDESourceContext : public ScXMLImportContext
+{
+ ScXMLDDELinkContext* pDDELink;
+
+public:
+ ScXMLDDESourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDESourceContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDDETableContext : public ScXMLImportContext
+{
+ ScXMLDDELinkContext* pDDELink;
+
+public:
+ ScXMLDDETableContext( ScXMLImport& rImport,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDETableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDDEColumnContext : public ScXMLImportContext
+{
+public:
+ ScXMLDDEColumnContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDEColumnContext() override;
+};
+
+class ScXMLDDERowContext : public ScXMLImportContext
+{
+ ScXMLDDELinkContext* pDDELink;
+ sal_Int32 nRows;
+
+public:
+ ScXMLDDERowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDERowContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDDECellContext : public ScXMLImportContext
+{
+ OUString sValue;
+ double fValue;
+ sal_Int32 nCells;
+ bool bString;
+ bool bString2;
+ bool bEmpty;
+
+ ScXMLDDELinkContext* pDDELink;
+
+public:
+ ScXMLDDECellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDECellContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDetectiveContext.cxx b/sc/source/filter/xml/XMLDetectiveContext.cxx
new file mode 100644
index 0000000000..b6e3af33ad
--- /dev/null
+++ b/sc/source/filter/xml/XMLDetectiveContext.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 "XMLDetectiveContext.hxx"
+
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include "xmlimprt.hxx"
+#include "XMLConverter.hxx"
+#include <rangeutl.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScMyImpDetectiveObj::ScMyImpDetectiveObj() :
+ eObjType( SC_DETOBJ_NONE ),
+ bHasError( false )
+{
+}
+
+bool ScMyImpDetectiveOp::operator<(const ScMyImpDetectiveOp& rDetOp) const
+{
+ return (nIndex < rDetOp.nIndex);
+}
+
+void ScMyImpDetectiveOpArray::Sort()
+{
+ aDetectiveOpList.sort();
+}
+
+bool ScMyImpDetectiveOpArray::GetFirstOp( ScMyImpDetectiveOp& rDetOp )
+{
+ if( aDetectiveOpList.empty() )
+ return false;
+ ScMyImpDetectiveOpList::iterator aItr = aDetectiveOpList.begin();
+ rDetOp = *aItr;
+ aDetectiveOpList.erase( aItr );
+ return true;
+}
+
+ScXMLDetectiveContext::ScXMLDetectiveContext(
+ ScXMLImport& rImport,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec ) :
+ ScXMLImportContext( rImport ),
+ pDetectiveObjVec( pNewDetectiveObjVec )
+{
+}
+
+ScXMLDetectiveContext::~ScXMLDetectiveContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDetectiveContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_HIGHLIGHTED_RANGE ):
+ pContext = new ScXMLDetectiveHighlightedContext( GetScImport(), pAttribList, pDetectiveObjVec );
+ break;
+ case XML_ELEMENT( TABLE, XML_OPERATION ):
+ pContext = new ScXMLDetectiveOperationContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDetectiveHighlightedContext::ScXMLDetectiveHighlightedContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec ):
+ ScXMLImportContext( rImport ),
+ pDetectiveObjVec( pNewDetectiveObjVec ),
+ bValid( false )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ bValid = ScRangeStringConverter::GetRangeFromString( aDetectiveObj.aSourceRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DIRECTION ):
+ aDetectiveObj.eObjType = ScXMLConverter::GetDetObjTypeFromString( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTAINS_ERROR ):
+ aDetectiveObj.bHasError = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_MARKED_INVALID ):
+ {
+ if (IsXMLToken(aIter, XML_TRUE))
+ aDetectiveObj.eObjType = SC_DETOBJ_CIRCLE;
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDetectiveHighlightedContext::~ScXMLDetectiveHighlightedContext()
+{
+}
+
+void SAL_CALL ScXMLDetectiveHighlightedContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ switch( aDetectiveObj.eObjType )
+ {
+ case SC_DETOBJ_ARROW:
+ case SC_DETOBJ_TOOTHERTAB:
+ break;
+ case SC_DETOBJ_FROMOTHERTAB:
+ case SC_DETOBJ_CIRCLE:
+ bValid = true;
+ break;
+ default:
+ bValid = false;
+ }
+ if( bValid )
+ pDetectiveObjVec->push_back( aDetectiveObj );
+}
+
+ScXMLDetectiveOperationContext::ScXMLDetectiveOperationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ bHasType( false )
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ bHasType = ScXMLConverter::GetDetOpTypeFromString( aDetectiveOp.eOpType, aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_INDEX ):
+ {
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber( nValue, aIter.toView(), 0 ))
+ aDetectiveOp.nIndex = nValue;
+ }
+ break;
+ }
+ }
+ }
+ aDetectiveOp.aPosition = rImport.GetTables().GetCurrentCellPos();
+}
+
+ScXMLDetectiveOperationContext::~ScXMLDetectiveOperationContext()
+{
+}
+
+void SAL_CALL ScXMLDetectiveOperationContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if( bHasType && (aDetectiveOp.nIndex >= 0) )
+ GetScImport().GetDetectiveOpArray()->AddDetectiveOp( aDetectiveOp );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDetectiveContext.hxx b/sc/source/filter/xml/XMLDetectiveContext.hxx
new file mode 100644
index 0000000000..8b0a635374
--- /dev/null
+++ b/sc/source/filter/xml/XMLDetectiveContext.hxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <detfunc.hxx>
+#include <detdata.hxx>
+#include "importcontext.hxx"
+
+#include <list>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+struct ScMyImpDetectiveObj
+{
+ ScRange aSourceRange;
+ ScDetectiveObjType eObjType;
+ bool bHasError;
+
+ ScMyImpDetectiveObj();
+};
+
+typedef ::std::vector< ScMyImpDetectiveObj > ScMyImpDetectiveObjVec;
+
+struct ScMyImpDetectiveOp
+{
+ ScAddress aPosition;
+ ScDetOpType eOpType;
+ sal_Int32 nIndex;
+
+ ScMyImpDetectiveOp()
+ : eOpType(SCDETOP_ADDSUCC)
+ , nIndex(-1)
+ {
+ }
+
+ bool operator<(const ScMyImpDetectiveOp& rDetOp) const;
+};
+
+typedef ::std::list< ScMyImpDetectiveOp > ScMyImpDetectiveOpList;
+
+class ScMyImpDetectiveOpArray
+{
+private:
+ ScMyImpDetectiveOpList aDetectiveOpList;
+
+public:
+ ScMyImpDetectiveOpArray() :
+ aDetectiveOpList() {}
+
+ void AddDetectiveOp( const ScMyImpDetectiveOp& rDetOp )
+ { aDetectiveOpList.push_back( rDetOp ); }
+
+ void Sort();
+ bool GetFirstOp( ScMyImpDetectiveOp& rDetOp );
+};
+
+class ScXMLDetectiveContext : public ScXMLImportContext
+{
+private:
+ ScMyImpDetectiveObjVec* pDetectiveObjVec;
+
+public:
+ ScXMLDetectiveContext(
+ ScXMLImport& rImport,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec
+ );
+ virtual ~ScXMLDetectiveContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList
+ ) override;
+};
+
+class ScXMLDetectiveHighlightedContext : public ScXMLImportContext
+{
+private:
+ ScMyImpDetectiveObjVec* pDetectiveObjVec;
+ ScMyImpDetectiveObj aDetectiveObj;
+ bool bValid;
+
+public:
+ ScXMLDetectiveHighlightedContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec
+ );
+ virtual ~ScXMLDetectiveHighlightedContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDetectiveOperationContext : public ScXMLImportContext
+{
+private:
+ ScMyImpDetectiveOp aDetectiveOp;
+ bool bHasType;
+
+public:
+ ScXMLDetectiveOperationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList
+ );
+ virtual ~ScXMLDetectiveOperationContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLEmptyContext.cxx b/sc/source/filter/xml/XMLEmptyContext.cxx
new file mode 100644
index 0000000000..b2e85896ce
--- /dev/null
+++ b/sc/source/filter/xml/XMLEmptyContext.cxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLEmptyContext.hxx"
+#include "xmlimprt.hxx"
+
+ScXMLEmptyContext::ScXMLEmptyContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+}
+
+ScXMLEmptyContext::~ScXMLEmptyContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLEmptyContext::createFastChildContext( sal_Int32 /*nElement*/,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = new ScXMLEmptyContext( GetScImport() );
+
+ return pContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLEmptyContext.hxx b/sc/source/filter/xml/XMLEmptyContext.hxx
new file mode 100644
index 0000000000..af5e838a7c
--- /dev/null
+++ b/sc/source/filter/xml/XMLEmptyContext.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLEmptyContext : public ScXMLImportContext
+{
+public:
+ ScXMLEmptyContext(ScXMLImport& rImport);
+
+ virtual ~ScXMLEmptyContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDDELinks.cxx b/sc/source/filter/xml/XMLExportDDELinks.cxx
new file mode 100644
index 0000000000..795f47a668
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDDELinks.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 "XMLExportDDELinks.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sax/tools/converter.hxx>
+#include "xmlexprt.hxx"
+#include <unonames.hxx>
+#include <document.hxx>
+#include <scmatrix.hxx>
+#include <com/sun/star/sheet/XDDELink.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLExportDDELinks::ScXMLExportDDELinks(ScXMLExport& rTempExport)
+ : rExport(rTempExport)
+{
+}
+
+void ScXMLExportDDELinks::WriteCell(const ScMatrixValue& aVal, sal_Int32 nRepeat)
+{
+ bool bString = ScMatrix::IsNonValueType(aVal.nType);
+ bool bEmpty = ScMatrix::IsEmptyType(aVal.nType);
+
+ if (!bEmpty)
+ {
+ if (bString)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_STRING_VALUE, aVal.GetString().getString());
+ }
+ else
+ {
+ OUStringBuffer aBuf;
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
+ ::sax::Converter::convertDouble(aBuf, aVal.fVal);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, aBuf.makeStringAndClear());
+ }
+ }
+
+ if (nRepeat > 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, OUString::number(nRepeat));
+ }
+ SvXMLElementExport(rExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+}
+
+void ScXMLExportDDELinks::WriteTable(const sal_Int32 nPos)
+{
+ ScDocument* pDoc = rExport.GetDocument();
+ if (!pDoc)
+ return;
+
+ const ScMatrix* pMatrix = pDoc->GetDdeLinkResultMatrix(static_cast<sal_uInt16>(nPos));
+ if (!pMatrix)
+ return;
+
+ SCSIZE nCols, nRows;
+ pMatrix->GetDimensions(nCols, nRows);
+
+ SvXMLElementExport aTableElem(rExport, XML_NAMESPACE_TABLE, XML_TABLE, true, true);
+ if (nCols > 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, OUString::number(nCols));
+ }
+ {
+ SvXMLElementExport aElemCol(rExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true);
+ }
+
+ for (SCSIZE nRow = 0; nRow < nRows; ++nRow)
+ {
+ sal_Int32 nRepeat = 0;
+ ScMatrixValue aPrevVal;
+ SvXMLElementExport aElemRow(rExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+ for (SCSIZE nCol = 0; nCol < nCols; ++nCol, ++nRepeat)
+ {
+ ScMatrixValue aVal = pMatrix->Get(nCol, nRow);
+ if (nCol > 0 && aVal != aPrevVal)
+ {
+ // Cell value differs. Flush the cell content.
+ WriteCell(aPrevVal, nRepeat);
+ nRepeat = 0;
+ }
+ aPrevVal = aVal;
+ }
+
+ WriteCell(aPrevVal, nRepeat);
+ }
+}
+
+void ScXMLExportDDELinks::WriteDDELinks(const uno::Reference<sheet::XSpreadsheetDocument>& xSpreadDoc)
+{
+ uno::Reference <beans::XPropertySet> xPropertySet (xSpreadDoc, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex(xPropertySet->getPropertyValue(SC_UNO_DDELINKS), uno::UNO_QUERY);
+ if (!xIndex.is())
+ return;
+
+ sal_Int32 nCount = xIndex->getCount();
+ if (!nCount)
+ return;
+
+ SvXMLElementExport aElemDDEs(rExport, XML_NAMESPACE_TABLE, XML_DDE_LINKS, true, true);
+ for (sal_Int32 nDDELink = 0; nDDELink < nCount; ++nDDELink)
+ {
+ uno::Reference<sheet::XDDELink> xDDELink(xIndex->getByIndex(nDDELink), uno::UNO_QUERY);
+ if (xDDELink.is())
+ {
+ SvXMLElementExport aElemDDE(rExport, XML_NAMESPACE_TABLE, XML_DDE_LINK, true, true);
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION, xDDELink->getApplication());
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DDE_TOPIC, xDDELink->getTopic());
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DDE_ITEM, xDDELink->getItem());
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_AUTOMATIC_UPDATE, XML_TRUE);
+ sal_uInt8 nMode;
+ if (rExport.GetDocument() &&
+ rExport.GetDocument()->GetDdeLinkMode(nDDELink, nMode))
+ {
+ switch (nMode)
+ {
+ case SC_DDE_ENGLISH :
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONVERSION_MODE, XML_INTO_ENGLISH_NUMBER);
+ break;
+ case SC_DDE_TEXT :
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONVERSION_MODE, XML_KEEP_TEXT);
+ break;
+ }
+ }
+ SvXMLElementExport(rExport, XML_NAMESPACE_OFFICE, XML_DDE_SOURCE, true, true);
+ }
+ WriteTable(nDDELink);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDDELinks.hxx b/sc/source/filter/xml/XMLExportDDELinks.hxx
new file mode 100644
index 0000000000..560f495877
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDDELinks.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+namespace com::sun::star::uno { template <class interface_type> class Reference; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+
+class ScXMLExport;
+struct ScMatrixValue;
+
+class ScXMLExportDDELinks
+{
+ ScXMLExport& rExport;
+
+ void WriteCell(const ScMatrixValue& aVal, sal_Int32 nRepeat);
+ void WriteTable(const sal_Int32 nPos);
+public:
+ explicit ScXMLExportDDELinks(ScXMLExport& rExport);
+ void WriteDDELinks(const css::uno::Reference < css::sheet::XSpreadsheetDocument >& xSpreadDoc);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDataPilot.cxx b/sc/source/filter/xml/XMLExportDataPilot.cxx
new file mode 100644
index 0000000000..bd5f16d828
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDataPilot.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 "XMLExportDataPilot.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <sax/tools/converter.hxx>
+#include <rtl/math.hxx>
+#include <osl/diagnose.h>
+#include "xmlexprt.hxx"
+#include "XMLConverter.hxx"
+#include <document.hxx>
+#include <dpobject.hxx>
+#include <dociter.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <scitems.hxx>
+#include <dpsave.hxx>
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <dpdimsave.hxx>
+#include <dputil.hxx>
+#include <rangeutl.hxx>
+#include <queryentry.hxx>
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLExportDataPilot::ScXMLExportDataPilot(ScXMLExport& rTempExport)
+ : rExport(rTempExport),
+ pDoc( nullptr )
+{
+}
+
+OUString ScXMLExportDataPilot::getDPOperatorXML(
+ const ScQueryOp aFilterOperator, const utl::SearchParam::SearchType eSearchType)
+{
+ switch (aFilterOperator)
+ {
+ case SC_EQUAL :
+ {
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_MATCH);
+ else
+ return "=";
+ }
+ case SC_NOT_EQUAL :
+ {
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_NOMATCH);
+ else
+ return "!=";
+ }
+ case SC_BOTPERC :
+ return GetXMLToken(XML_BOTTOM_PERCENT);
+ case SC_BOTVAL :
+ return GetXMLToken(XML_BOTTOM_VALUES);
+ case SC_GREATER :
+ return ">";
+ case SC_GREATER_EQUAL :
+ return ">=";
+ case SC_LESS :
+ return "<";
+ case SC_LESS_EQUAL :
+ return "<=";
+ case SC_TOPPERC :
+ return GetXMLToken(XML_TOP_PERCENT);
+ case SC_TOPVAL :
+ return GetXMLToken(XML_TOP_VALUES);
+ default:
+ OSL_FAIL("This FilterOperator is not supported.");
+ }
+ return "=";
+}
+
+void ScXMLExportDataPilot::WriteDPCondition(const ScQueryEntry& aQueryEntry, bool bIsCaseSensitive,
+ utl::SearchParam::SearchType eSearchType)
+{
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(aQueryEntry.nField));
+ if (bIsCaseSensitive)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+ const ScQueryEntry::Item& rItem = aQueryEntry.GetQueryItem();
+ OUString aQueryStr = rItem.maString.getString();
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, aQueryStr);
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_NUMBER);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, aQueryStr);
+ }
+
+ if (aQueryEntry.IsQueryByEmpty())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, GetXMLToken(XML_TOKEN_EMPTY));
+ }
+ else if (aQueryEntry.IsQueryByNonEmpty())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, GetXMLToken(XML_NOEMPTY));
+ }
+ else
+ rExport.AddAttribute(
+ XML_NAMESPACE_TABLE, XML_OPERATOR,
+ getDPOperatorXML(aQueryEntry.eOp, eSearchType));
+
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
+}
+
+void ScXMLExportDataPilot::WriteDPFilter(const ScQueryParam& aQueryParam)
+{
+ SCSIZE nQueryEntryCount = aQueryParam.GetEntryCount();
+ if (nQueryEntryCount <= 0)
+ return;
+
+ bool bAnd(false);
+ bool bOr(false);
+ bool bHasEntries(true);
+ SCSIZE nEntries(0);
+ SCSIZE j;
+
+ for ( j = 0; (j < nQueryEntryCount) && bHasEntries; ++j)
+ {
+ ScQueryEntry aEntry = aQueryParam.GetEntry(j);
+ if (aEntry.bDoQuery)
+ {
+ if (nEntries > 0)
+ {
+ if (aEntry.eConnect == SC_AND)
+ bAnd = true;
+ else
+ bOr = true;
+ }
+ ++nEntries;
+ }
+ else
+ bHasEntries = false;
+ }
+ nQueryEntryCount = nEntries;
+ if (!nQueryEntryCount)
+ return;
+
+ if(!((aQueryParam.nCol1 == aQueryParam.nCol2) && (aQueryParam.nRow1 == aQueryParam.nRow2) &&
+ (static_cast<SCCOLROW>(aQueryParam.nCol1) == static_cast<SCCOLROW>(aQueryParam.nRow1)) &&
+ (aQueryParam.nCol1 == 0) && (aQueryParam.nTab == SCTAB_MAX)))
+ {
+ ScRange aConditionRange(aQueryParam.nCol1, aQueryParam.nRow1, aQueryParam.nTab,
+ aQueryParam.nCol2, aQueryParam.nRow2, aQueryParam.nTab);
+ OUString sConditionRange;
+ ScRangeStringConverter::GetStringFromRange( sConditionRange, aConditionRange, pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ if (!sConditionRange.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS, sConditionRange);
+ }
+ if (!aQueryParam.bDuplicate)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_DUPLICATES, XML_FALSE);
+ SvXMLElementExport aElemDPF(rExport, XML_NAMESPACE_TABLE, XML_FILTER, true, true);
+ rExport.CheckAttrList();
+ if (nQueryEntryCount == 1)
+ {
+ WriteDPCondition(aQueryParam.GetEntry(0), aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ else if (bOr && !bAnd)
+ {
+ SvXMLElementExport aElemOr(rExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ for (j = 0; j < nQueryEntryCount; ++j)
+ {
+ WriteDPCondition(aQueryParam.GetEntry(j), aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ else if (bAnd && !bOr)
+ {
+ SvXMLElementExport aElemAnd(rExport, XML_NAMESPACE_TABLE, XML_FILTER_AND, true, true);
+ for (j = 0; j < nQueryEntryCount; ++j)
+ {
+ WriteDPCondition(aQueryParam.GetEntry(j), aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ else
+ {
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ ScQueryEntry aPrevFilterField(aQueryParam.GetEntry(0));
+ ScQueryConnect aConnection = aQueryParam.GetEntry(1).eConnect;
+ bool bOpenAndElement;
+ OUString aName(rExport.GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TABLE, GetXMLToken(XML_FILTER_AND)));
+ if (aConnection == SC_AND)
+ {
+ rExport.StartElement( aName, true );
+ bOpenAndElement = true;
+ }
+ else
+ bOpenAndElement = false;
+ for (j = 1; j < nQueryEntryCount; ++j)
+ {
+ if (aConnection != aQueryParam.GetEntry(j).eConnect)
+ {
+ aConnection = aQueryParam.GetEntry(j).eConnect;
+ if (aQueryParam.GetEntry(j).eConnect == SC_AND)
+ {
+ rExport.StartElement( aName, true );
+ bOpenAndElement = true;
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ aPrevFilterField = aQueryParam.GetEntry(j);
+ if (j == nQueryEntryCount - 1)
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ rExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ }
+ else
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ aPrevFilterField = aQueryParam.GetEntry(j);
+ if (bOpenAndElement)
+ {
+ rExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ if (j == nQueryEntryCount - 1)
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ }
+ else
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ aPrevFilterField = aQueryParam.GetEntry(j);
+ if (j == nQueryEntryCount - 1)
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ }
+}
+
+void ScXMLExportDataPilot::WriteFieldReference(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldReference* pRef = pDim->GetReferenceValue();
+ if (pRef)
+ {
+ OUString sValueStr;
+ switch (pRef->ReferenceType)
+ {
+ case sheet::DataPilotFieldReferenceType::NONE :
+ sValueStr = GetXMLToken(XML_NONE);
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE :
+ sValueStr = GetXMLToken(XML_MEMBER_DIFFERENCE);
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_MEMBER_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE :
+ sValueStr = GetXMLToken(XML_MEMBER_PERCENTAGE_DIFFERENCE);
+ break;
+ case sheet::DataPilotFieldReferenceType::RUNNING_TOTAL :
+ sValueStr = GetXMLToken(XML_RUNNING_TOTAL);
+ break;
+ case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_ROW_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_COLUMN_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_TOTAL_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::INDEX :
+ sValueStr = GetXMLToken(XML_INDEX);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, sValueStr);
+
+ if (!pRef->ReferenceField.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NAME, pRef->ReferenceField);
+
+ if (pRef->ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_TYPE, XML_NAMED);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_NAME, pRef->ReferenceItemName);
+ }
+ else
+ {
+ sValueStr.clear();
+ switch(pRef->ReferenceItemType)
+ {
+ case sheet::DataPilotFieldReferenceItemType::PREVIOUS :
+ sValueStr = GetXMLToken(XML_PREVIOUS);
+ break;
+ case sheet::DataPilotFieldReferenceItemType::NEXT :
+ sValueStr = GetXMLToken(XML_NEXT);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_TYPE, sValueStr);
+ }
+ SvXMLElementExport aElemDPFR(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_FIELD_REFERENCE, true, true);
+ }
+ rExport.CheckAttrList();
+}
+
+void ScXMLExportDataPilot::WriteSortInfo(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldSortInfo* pSortInfo = pDim->GetSortInfo();
+ if (!pSortInfo)
+ return;
+
+ if (pSortInfo->IsAscending)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_ASCENDING);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_DESCENDING);
+
+ OUString sValueStr;
+ switch (pSortInfo->Mode)
+ {
+ case sheet::DataPilotFieldSortMode::NONE:
+ sValueStr = GetXMLToken(XML_NONE);
+ break;
+ case sheet::DataPilotFieldSortMode::MANUAL:
+ sValueStr = GetXMLToken(XML_MANUAL);
+ break;
+ case sheet::DataPilotFieldSortMode::NAME:
+ sValueStr = GetXMLToken(XML_NAME);
+ break;
+ case sheet::DataPilotFieldSortMode::DATA:
+ sValueStr = GetXMLToken(XML_DATA);
+ if (!pSortInfo->Field.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_FIELD, pSortInfo->Field);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SORT_MODE, sValueStr);
+ SvXMLElementExport aElemDPLSI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_SORT_INFO, true, true);
+}
+
+void ScXMLExportDataPilot::WriteAutoShowInfo(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldAutoShowInfo* pAutoInfo = pDim->GetAutoShowInfo();
+ if (!pAutoInfo)
+ return;
+
+ if (pAutoInfo->IsEnabled)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ENABLED, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ENABLED, XML_FALSE);
+
+ OUString sValueStr;
+ switch (pAutoInfo->ShowItemsMode)
+ {
+ case sheet::DataPilotFieldShowItemsMode::FROM_TOP:
+ sValueStr = GetXMLToken(XML_FROM_TOP);
+ break;
+ case sheet::DataPilotFieldShowItemsMode::FROM_BOTTOM:
+ sValueStr = GetXMLToken(XML_FROM_BOTTOM);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_MEMBER_MODE, sValueStr);
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_COUNT, OUString::number(pAutoInfo->ItemCount));
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_FIELD, pAutoInfo->DataField);
+
+ SvXMLElementExport aElemDPLAI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_DISPLAY_INFO, true, true);
+}
+
+void ScXMLExportDataPilot::WriteLayoutInfo(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldLayoutInfo* pLayoutInfo = pDim->GetLayoutInfo();
+ if (!pLayoutInfo)
+ return;
+
+ if (pLayoutInfo->AddEmptyLines)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ADD_EMPTY_LINES, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ADD_EMPTY_LINES, XML_FALSE);
+
+ OUString sValueStr;
+ switch (pLayoutInfo->LayoutMode)
+ {
+ case sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT:
+ sValueStr = GetXMLToken(XML_TABULAR_LAYOUT);
+ break;
+ case sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP:
+ sValueStr = GetXMLToken(XML_OUTLINE_SUBTOTALS_TOP);
+ break;
+ case sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
+ sValueStr = GetXMLToken(XML_OUTLINE_SUBTOTALS_BOTTOM);
+ break;
+ case sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT:
+ sValueStr = GetXMLToken(XML_TABULAR_LAYOUT);
+ break;
+ }
+
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_LAYOUT_MODE, sValueStr);
+
+ if (pLayoutInfo->LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT)
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_LAYOUT_MODE, GetXMLToken(XML_COMPACT_LAYOUT));
+
+ SvXMLElementExport aElemDPLLI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_LAYOUT_INFO, true, true);
+}
+
+void ScXMLExportDataPilot::WriteSubTotals(const ScDPSaveDimension* pDim)
+{
+ sal_Int32 nSubTotalCount = pDim->GetSubTotalsCount();
+ std::optional<OUString> pLayoutName;
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ // Export display names only for 1.2 extended or later.
+ pLayoutName = pDim->GetSubtotalName();
+
+ if (nSubTotalCount <= 0)
+ return;
+
+ SvXMLElementExport aElemSTs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_SUBTOTALS, true, true);
+ rExport.CheckAttrList();
+ for (sal_Int32 nSubTotal = 0; nSubTotal < nSubTotalCount; nSubTotal++)
+ {
+ sal_Int16 nFunc = static_cast<sal_Int16>(pDim->GetSubTotalFunc(nSubTotal));
+ OUString sFunction = ScXMLConverter::GetStringFromFunction(nFunc);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FUNCTION, sFunction);
+ if (pLayoutName && nFunc == sheet::GeneralFunction2::AUTO)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pLayoutName);
+ SvXMLElementExport aElemST(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_SUBTOTAL, true, true);
+ }
+}
+
+void ScXMLExportDataPilot::WriteMembers(const ScDPSaveDimension* pDim)
+{
+ const ScDPSaveDimension::MemberList &rMembers = pDim->GetMembers();
+ if (rMembers.empty())
+ return;
+
+ SvXMLElementExport aElemDPMs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_MEMBERS, true, true);
+ rExport.CheckAttrList();
+ for (const auto& rpMember : rMembers)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rpMember->GetName());
+
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ // Export display names only for ODF 1.2 extended or later.
+ const std::optional<OUString> & pLayoutName = rpMember->GetLayoutName();
+ if (pLayoutName)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pLayoutName);
+ }
+
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, rpMember->GetIsVisible());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, sBuffer.makeStringAndClear());
+ ::sax::Converter::convertBool(sBuffer, rpMember->GetShowDetails());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_DETAILS, sBuffer.makeStringAndClear());
+ SvXMLElementExport aElemDPM(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_MEMBER, true, true);
+ rExport.CheckAttrList();
+ }
+}
+
+void ScXMLExportDataPilot::WriteLevels(const ScDPSaveDimension* pDim)
+{
+ // #i114202# GetShowEmpty is only valid if HasShowEmpty is true.
+ if (pDim->HasShowEmpty())
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, pDim->GetShowEmpty());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_EMPTY, sBuffer.makeStringAndClear());
+ }
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, pDim->GetRepeatItemLabels());
+ rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_REPEAT_ITEM_LABELS, sBuffer.makeStringAndClear());
+ }
+ SvXMLElementExport aElemDPL(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_LEVEL, true, true);
+
+ WriteSubTotals(pDim);
+ WriteMembers(pDim);
+ WriteAutoShowInfo(pDim);
+ WriteSortInfo(pDim);
+ WriteLayoutInfo(pDim);
+ rExport.CheckAttrList();
+}
+
+void ScXMLExportDataPilot::WriteDatePart(sal_Int32 nPart)
+{
+ switch(nPart)
+ {
+ case css::sheet::DataPilotFieldGroupBy::SECONDS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_SECONDS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::MINUTES :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_MINUTES);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::HOURS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_HOURS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::DAYS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_DAYS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::MONTHS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_MONTHS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::QUARTERS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_QUARTERS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::YEARS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_YEARS);
+ }
+ break;
+ }
+}
+
+void ScXMLExportDataPilot::WriteNumGroupInfo(const ScDPNumGroupInfo& rGroupInfo)
+{
+ OSL_ENSURE(rGroupInfo.mbEnable, "group dimension should be enabled");
+ if (rGroupInfo.mbDateValues)
+ {
+ if (rGroupInfo.mbAutoStart)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_START, XML_AUTO);
+ else
+ {
+ OUStringBuffer sDate;
+ rExport.GetMM100UnitConverter().convertDateTime(sDate, rGroupInfo.mfStart);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_START, sDate.makeStringAndClear());
+ }
+ if (rGroupInfo.mbAutoEnd)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_END, XML_AUTO);
+ else
+ {
+ OUStringBuffer sDate;
+ rExport.GetMM100UnitConverter().convertDateTime(sDate, rGroupInfo.mfEnd);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_END, sDate.makeStringAndClear());
+ }
+ }
+ else
+ {
+ if (rGroupInfo.mbAutoStart)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START, XML_AUTO);
+ else
+ {
+ OUString sValue( ::rtl::math::doubleToUString( rGroupInfo.mfStart,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START, sValue);
+ }
+ if (rGroupInfo.mbAutoEnd)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END, XML_AUTO);
+ else
+ {
+ OUString sValue( ::rtl::math::doubleToUString( rGroupInfo.mfEnd,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END, sValue);
+ }
+ }
+ OUString sValue( ::rtl::math::doubleToUString( rGroupInfo.mfStep,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STEP, sValue);
+}
+
+void ScXMLExportDataPilot::WriteGroupDimAttributes(const ScDPSaveGroupDimension& rGroupDim)
+{
+ OUString aSrcFieldName = ScDPUtil::getSourceDimensionName(rGroupDim.GetSourceDimName());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SOURCE_FIELD_NAME, aSrcFieldName);
+ if (rGroupDim.GetDatePart())
+ {
+ WriteDatePart(rGroupDim.GetDatePart());
+ WriteNumGroupInfo(rGroupDim.GetDateInfo());
+ }
+}
+
+void ScXMLExportDataPilot::WriteNumGroupDim(const ScDPSaveNumGroupDimension& rNumGroupDim)
+{
+ if (rNumGroupDim.GetDatePart())
+ {
+ WriteDatePart(rNumGroupDim.GetDatePart());
+ WriteNumGroupInfo(rNumGroupDim.GetDateInfo());
+ }
+ else
+ {
+ WriteNumGroupInfo(rNumGroupDim.GetInfo());
+ }
+}
+
+void ScXMLExportDataPilot::WriteGroupDimElements(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData)
+{
+ const ScDPSaveGroupDimension* pGroupDim = nullptr;
+ const ScDPSaveNumGroupDimension* pNumGroupDim = nullptr;
+ if (pDimData)
+ {
+ pGroupDim = pDimData->GetNamedGroupDim(pDim->GetName());
+ pNumGroupDim = pDimData->GetNumGroupDim(pDim->GetName());
+ OSL_ENSURE((!pGroupDim || !pNumGroupDim), "there should be no NumGroup and Group at the same field");
+ if (pGroupDim)
+ WriteGroupDimAttributes(*pGroupDim);
+ else if (pNumGroupDim)
+ WriteNumGroupDim(*pNumGroupDim);
+ }
+ if (!(pGroupDim || pNumGroupDim))
+ return;
+
+ SvXMLElementExport aElemDPGs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_GROUPS, true, true);
+ if (!pGroupDim)
+ return;
+
+ if (pGroupDim->GetDatePart())
+ return;
+
+ sal_Int32 nCount = pGroupDim->GetGroupCount();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ const ScDPSaveGroupItem& rGroup = pGroupDim->GetGroupByIndex( i );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rGroup.GetGroupName());
+ SvXMLElementExport aElemDPG(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_GROUP, true, true);
+ sal_Int32 nElemCount = rGroup.GetElementCount();
+ for(sal_Int32 j = 0; j < nElemCount; ++j)
+ {
+ const OUString* pElem = rGroup.GetElementByIndex(j);
+ if (pElem)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, *pElem);
+ SvXMLElementExport aElemDPM(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_GROUP_MEMBER, true, true);
+ }
+ }
+ }
+}
+
+void ScXMLExportDataPilot::WriteDimension(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData)
+{
+ OUString aSrcDimName = ScDPUtil::getSourceDimensionName(pDim->GetName());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SOURCE_FIELD_NAME, aSrcDimName);
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ // Export display names only for ODF 1.2 extended or later.
+ const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
+ if (pLayoutName)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pLayoutName);
+ }
+
+ if (pDim->IsDataLayout())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IS_DATA_LAYOUT_FIELD, XML_TRUE);
+ sheet::DataPilotFieldOrientation eOrientation = pDim->GetOrientation();
+ OUString sValueStr = ScXMLConverter::GetStringFromOrientation(eOrientation);
+ if( !sValueStr.isEmpty() )
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORIENTATION, sValueStr );
+ if (pDim->GetUsedHierarchy() != 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_USED_HIERARCHY, OUString::number(pDim->GetUsedHierarchy()));
+ }
+ sValueStr = ScXMLConverter::GetStringFromFunction( static_cast<sal_Int16>(pDim->GetFunction()) );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FUNCTION, sValueStr);
+
+ if (eOrientation == sheet::DataPilotFieldOrientation_PAGE)
+ {
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_IGNORE_SELECTED_PAGE, "true");
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SELECTED_PAGE, pDim->GetCurrentPage());
+ }
+
+ SvXMLElementExport aElemDPF(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_FIELD, true, true);
+ WriteLevels(pDim);
+ WriteFieldReference(pDim);
+ if( pDim->GetOrientation() != sheet::DataPilotFieldOrientation_DATA )
+ WriteGroupDimElements(pDim, pDimData);
+}
+
+void ScXMLExportDataPilot::WriteDimensions(const ScDPSaveData* pDPSave)
+{
+ const ScDPSaveData::DimsType& rDimensions = pDPSave->GetDimensions();
+ for (auto const& iter : rDimensions)
+ {
+ WriteDimension(iter.get(),
+ pDPSave->GetExistingDimensionData());
+ }
+}
+
+void ScXMLExportDataPilot::WriteGrandTotal(::xmloff::token::XMLTokenEnum eOrient, bool bVisible, const std::optional<OUString> & pGrandTotal)
+{
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, bVisible ? XML_TRUE : XML_FALSE);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORIENTATION, eOrient);
+ if (pGrandTotal)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pGrandTotal);
+
+ SvXMLElementExport aElemGrandTotal(rExport, XML_NAMESPACE_TABLE_EXT, XML_DATA_PILOT_GRAND_TOTAL, true, true);
+}
+
+void ScXMLExportDataPilot::WriteDataPilots()
+{
+ pDoc = rExport.GetDocument();
+ if (!pDoc)
+ return;
+
+ ScDPCollection* pDPs = pDoc->GetDPCollection();
+ if (!pDPs)
+ return;
+
+ size_t nDPCount = pDPs->GetCount();
+ if (!nDPCount)
+ return;
+
+ SvXMLElementExport aElemDPs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_TABLES, true, true);
+ rExport.CheckAttrList();
+ for (size_t i = 0; i < nDPCount; ++i)
+ {
+ ScDPSaveData* pDPSave = (*pDPs)[i].GetSaveData();
+ if (!pDPSave)
+ continue;
+
+ ScRange aOutRange((*pDPs)[i].GetOutRange());
+ OUString sTargetRangeAddress;
+ ScRangeStringConverter::GetStringFromRange( sTargetRangeAddress, aOutRange, pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ ScDocAttrIterator aAttrItr(*pDoc, aOutRange.aStart.Tab(),
+ aOutRange.aStart.Col(), aOutRange.aStart.Row(),
+ aOutRange.aEnd.Col(), aOutRange.aEnd.Row());
+ SCCOL nCol;
+ SCROW nRow1, nRow2;
+ OUString sOUButtonList;
+ const ScPatternAttr* pAttr = aAttrItr.GetNext(nCol, nRow1, nRow2);
+ while (pAttr)
+ {
+ const ScMergeFlagAttr& rItem = pAttr->GetItem(ATTR_MERGE_FLAG);
+ if (rItem.HasPivotButton())
+ {
+ for (SCROW nButtonRow = nRow1; nButtonRow <= nRow2; ++nButtonRow)
+ {
+ ScAddress aButtonAddr(nCol, nButtonRow, aOutRange.aStart.Tab());
+ ScRangeStringConverter::GetStringFromAddress(
+ sOUButtonList, aButtonAddr, pDoc, ::formula::FormulaGrammar::CONV_OOO, ' ', true );
+ }
+ }
+ pAttr = aAttrItr.GetNext(nCol, nRow1, nRow2);
+ }
+ OUString sName((*pDPs)[i].GetName());
+ OUString sApplicationData((*pDPs)[i].GetTag());
+ bool bRowGrand = pDPSave->GetRowGrand();
+ bool bColumnGrand = pDPSave->GetColumnGrand();
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, sName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_APPLICATION_DATA, sApplicationData);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, sTargetRangeAddress);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BUTTONS, sOUButtonList);
+ if (!(bRowGrand && bColumnGrand))
+ {
+ if (bRowGrand)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GRAND_TOTAL, XML_ROW);
+ else if (bColumnGrand)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GRAND_TOTAL, XML_COLUMN);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GRAND_TOTAL, XML_NONE);
+ }
+ if (pDPSave->GetIgnoreEmptyRows())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IGNORE_EMPTY_ROWS, XML_TRUE);
+ if (pDPSave->GetRepeatIfEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IDENTIFY_CATEGORIES, XML_TRUE);
+ if (!pDPSave->GetFilterButton())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_FILTER_BUTTON, XML_FALSE);
+ if (!pDPSave->GetDrillDown())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DRILL_DOWN_ON_DOUBLE_CLICK, XML_FALSE);
+ if (pDPSave->GetExpandCollapse())
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_SHOW_DRILL_DOWN_BUTTONS, XML_TRUE);
+ if ((*pDPs)[i].GetHeaderLayout())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_HEADER_GRID_LAYOUT, XML_TRUE);
+
+ SvXMLElementExport aElemDP(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_TABLE, true, true);
+
+ // grand total elements.
+
+ const std::optional<OUString> & pGrandTotalName = pDPSave->GetGrandTotalName();
+ if (pGrandTotalName && rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ // Use the new data-pilot-grand-total element.
+ if (bRowGrand && bColumnGrand)
+ {
+ WriteGrandTotal(XML_BOTH, true, pGrandTotalName);
+ }
+ else
+ {
+ WriteGrandTotal(XML_ROW, bRowGrand, pGrandTotalName);
+ WriteGrandTotal(XML_COLUMN, bColumnGrand, pGrandTotalName);
+ }
+ }
+
+ rExport.CheckAttrList();
+ if ((*pDPs)[i].IsSheetData())
+ {
+ const ScSheetSourceDesc* pSheetSource = (*pDPs)[i].GetSheetDesc();
+
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ if (pSheetSource->HasRangeName())
+ { // ODF 1.3 OFFICE-3665
+ // FIXME this was wrongly exported to TABLE namespace since 2011
+ // so continue doing that in ODF 1.2 extended, for now
+ rExport.AddAttribute(
+ XML_NAMESPACE_TABLE, XML_NAME, pSheetSource->GetRangeName());
+ }
+ }
+
+ OUString sCellRangeAddress;
+ ScRangeStringConverter::GetStringFromRange(
+ sCellRangeAddress, pSheetSource->GetSourceRange(), pDoc,
+ ::formula::FormulaGrammar::CONV_OOO);
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sCellRangeAddress);
+ SvXMLElementExport aElemSCR(rExport, XML_NAMESPACE_TABLE, XML_SOURCE_CELL_RANGE, true, true);
+ rExport.CheckAttrList();
+ WriteDPFilter(pSheetSource->GetQueryParam());
+ }
+ else if ((*pDPs)[i].IsImportData())
+ {
+ const ScImportSourceDesc* pImpSource = (*pDPs)[i].GetImportSourceDesc();
+ switch (pImpSource->nType)
+ {
+ case sheet::DataImportMode_NONE : break;
+ case sheet::DataImportMode_QUERY :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, pImpSource->aDBName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_QUERY_NAME, pImpSource->aObject);
+ SvXMLElementExport aElemID(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_QUERY, true, true);
+ rExport.CheckAttrList();
+ }
+ break;
+ case sheet::DataImportMode_TABLE :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, pImpSource->aDBName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_TABLE_NAME, pImpSource->aObject);
+ SvXMLElementExport aElemID(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_TABLE, true, true);
+ rExport.CheckAttrList();
+ }
+ break;
+ case sheet::DataImportMode_SQL :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, pImpSource->aDBName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SQL_STATEMENT, pImpSource->aObject);
+ if (!pImpSource->bNative)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PARSE_SQL_STATEMENT, XML_TRUE);
+ SvXMLElementExport aElemID(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_SQL, true, true);
+ rExport.CheckAttrList();
+ }
+ break;
+ default: break;
+ }
+ }
+ else if ((*pDPs)[i].IsServiceData())
+ {
+ const ScDPServiceDesc* pServSource = (*pDPs)[i].GetDPServiceDesc();
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pServSource->aServiceName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SOURCE_NAME, pServSource->aParSource);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OBJECT_NAME, pServSource->aParName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_USER_NAME, pServSource->aParUser);
+ // #i111754# leave out password attribute as long as DataPilotSource doesn't specify the content
+ // rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PASSWORD, OUString(pServSource->aParPass));
+ SvXMLElementExport aElemSD(rExport, XML_NAMESPACE_TABLE, XML_SOURCE_SERVICE, true, true);
+ rExport.CheckAttrList();
+ }
+ WriteDimensions(pDPSave);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDataPilot.hxx b/sc/source/filter/xml/XMLExportDataPilot.hxx
new file mode 100644
index 0000000000..04717aa10e
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDataPilot.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <optional>
+
+#include <rtl/ustring.hxx>
+#include <global.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <unotools/textsearch.hxx>
+
+class ScXMLExport;
+class ScDocument;
+class ScDPSaveDimension;
+class ScDPSaveData;
+class ScDPDimensionSaveData;
+class ScDPSaveGroupDimension;
+class ScDPSaveNumGroupDimension;
+struct ScDPNumGroupInfo;
+struct ScQueryParam;
+struct ScQueryEntry;
+
+class ScXMLExportDataPilot
+{
+ ScXMLExport& rExport;
+ ScDocument* pDoc;
+
+ static OUString getDPOperatorXML(const ScQueryOp aFilterOperator, const utl::SearchParam::SearchType eSearchType);
+ void WriteDPCondition(const ScQueryEntry& aQueryEntry, bool bIsCaseSensitive,
+ utl::SearchParam::SearchType eSearchType);
+ void WriteDPFilter(const ScQueryParam& aQueryParam);
+
+ void WriteFieldReference(const ScDPSaveDimension* pDim);
+ void WriteSortInfo(const ScDPSaveDimension* pDim);
+ void WriteAutoShowInfo(const ScDPSaveDimension* pDim);
+ void WriteLayoutInfo(const ScDPSaveDimension* pDim);
+ void WriteSubTotals(const ScDPSaveDimension* pDim);
+ void WriteMembers(const ScDPSaveDimension* pDim);
+ void WriteLevels(const ScDPSaveDimension* pDim);
+ void WriteDatePart(sal_Int32 nPart);
+ void WriteNumGroupInfo(const ScDPNumGroupInfo& pGroupInfo);
+ void WriteGroupDimAttributes(const ScDPSaveGroupDimension& rGroupDim);
+ void WriteGroupDimElements(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData);
+ void WriteNumGroupDim(const ScDPSaveNumGroupDimension& rNumGroupDim);
+ void WriteDimension(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData);
+ void WriteDimensions(const ScDPSaveData* pDPSave);
+
+ void WriteGrandTotal(::xmloff::token::XMLTokenEnum eOrient, bool bVisible, const std::optional<OUString> & pGrandTotal);
+
+public:
+ explicit ScXMLExportDataPilot(ScXMLExport& rExport);
+ void WriteDataPilots();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
new file mode 100644
index 0000000000..e4307065bd
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "XMLExportDatabaseRanges.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <sax/tools/converter.hxx>
+#include "xmlexprt.hxx"
+#include "XMLExportIterator.hxx"
+#include "XMLConverter.hxx"
+#include <unonames.hxx>
+#include <dbdata.hxx>
+#include <document.hxx>
+#include <globalnames.hxx>
+#include "XMLExportSharedData.hxx"
+#include <rangeutl.hxx>
+#include <subtotalparam.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <sortparam.hxx>
+
+#include <svx/dataaccessdescriptor.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/XDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <comphelper/extract.hxx>
+#include <osl/diagnose.h>
+
+#include <map>
+
+//! not found in unonames.hxx
+constexpr OUString SC_USERLIST = u"UserList"_ustr;
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+void writeSort(ScXMLExport& mrExport, const ScSortParam& aParam, const ScRange& aRange, const ScDocument* mpDoc)
+{
+ // Count sort items first.
+ size_t nSortCount = 0;
+ for (; nSortCount < aParam.GetSortKeyCount(); ++nSortCount)
+ {
+ if (!aParam.maKeyState[nSortCount].bDoSort)
+ break;
+ }
+
+ if (!nSortCount)
+ // Nothing to export.
+ return;
+
+ ScAddress aOutPos(aParam.nDestCol, aParam.nDestRow, aParam.nDestTab);
+
+ if (!aParam.aDataAreaExtras.mbCellFormats)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BIND_STYLES_TO_CONTENT, XML_FALSE);
+
+ if (!aParam.bInplace)
+ {
+ OUString aStr;
+ ScRangeStringConverter::GetStringFromAddress(
+ aStr, aOutPos, mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aStr);
+ }
+
+ if (aParam.bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+
+ mrExport.AddLanguageTagAttributes( XML_NAMESPACE_TABLE, XML_NAMESPACE_TABLE, aParam.aCollatorLocale, false);
+ if (!aParam.aCollatorAlgorithm.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ALGORITHM, aParam.aCollatorAlgorithm);
+
+ SvXMLElementExport aElemS(mrExport, XML_NAMESPACE_TABLE, XML_SORT, true, true);
+
+ SCCOLROW nFieldStart = aParam.bByRow ? aRange.aStart.Col() : aRange.aStart.Row();
+
+ for (size_t i = 0; i < nSortCount; ++i)
+ {
+ // Convert field value from absolute to relative.
+ SCCOLROW nField = aParam.maKeyState[i].nField - nFieldStart;
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(nField));
+
+ if (!aParam.maKeyState[i].bAscending)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_DESCENDING);
+
+ if (aParam.bUserDef)
+ {
+ OUString aBuf = SC_USERLIST + OUString::number(static_cast<sal_Int32>(aParam.nUserIndex));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, aBuf);
+ }
+ else
+ {
+ // Right now we only support automatic field type. In the
+ // future we may support numeric or alphanumeric field type.
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_AUTOMATIC);
+ }
+
+ SvXMLElementExport aElemSb(mrExport, XML_NAMESPACE_TABLE, XML_SORT_BY, true, true);
+ }
+}
+
+ScXMLExportDatabaseRanges::ScXMLExportDatabaseRanges(ScXMLExport& rTempExport)
+ : rExport(rTempExport),
+ pDoc( nullptr )
+{
+}
+
+ScMyEmptyDatabaseRangesContainer ScXMLExportDatabaseRanges::GetEmptyDatabaseRanges()
+{
+ ScMyEmptyDatabaseRangesContainer aSkipRanges;
+ if (rExport.GetModel().is())
+ {
+ uno::Reference <beans::XPropertySet> xPropertySet (rExport.GetModel(), uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ uno::Reference <sheet::XDatabaseRanges> xDatabaseRanges(xPropertySet->getPropertyValue(SC_UNO_DATABASERNG), uno::UNO_QUERY);
+ rExport.CheckAttrList();
+ if (xDatabaseRanges.is())
+ {
+ const uno::Sequence <OUString> aRanges(xDatabaseRanges->getElementNames());
+ for (const OUString& sDatabaseRangeName : aRanges)
+ {
+ uno::Reference <sheet::XDatabaseRange> xDatabaseRange(xDatabaseRanges->getByName(sDatabaseRangeName), uno::UNO_QUERY);
+ if (xDatabaseRange.is())
+ {
+ uno::Reference <beans::XPropertySet> xDatabaseRangePropertySet (xDatabaseRange, uno::UNO_QUERY);
+ if (xDatabaseRangePropertySet.is() &&
+ ::cppu::any2bool(xDatabaseRangePropertySet->getPropertyValue(SC_UNONAME_STRIPDAT)))
+ {
+ const uno::Sequence <beans::PropertyValue> aImportProperties(xDatabaseRange->getImportDescriptor());
+ sheet::DataImportMode nSourceType = sheet::DataImportMode_NONE;
+ for (const auto& rProp : aImportProperties)
+ if ( rProp.Name == SC_UNONAME_SRCTYPE )
+ rProp.Value >>= nSourceType;
+ if (nSourceType != sheet::DataImportMode_NONE)
+ {
+ table::CellRangeAddress aArea = xDatabaseRange->getDataArea();
+ aSkipRanges.AddNewEmptyDatabaseRange(aArea);
+
+ // #105276#; set last row/column so default styles are collected
+ rExport.GetSharedData()->SetLastColumn(aArea.Sheet, aArea.EndColumn);
+ rExport.GetSharedData()->SetLastRow(aArea.Sheet, aArea.EndRow);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return aSkipRanges;
+}
+
+namespace {
+
+class WriteDatabaseRange
+{
+ ScXMLExport& mrExport;
+ ScDocument* mpDoc;
+ sal_Int32 mnCounter;
+ ScDBCollection::RangeType meRangeType;
+public:
+
+ WriteDatabaseRange(ScXMLExport& rExport, ScDocument* pDoc) :
+ mrExport(rExport), mpDoc(pDoc), mnCounter(0), meRangeType(ScDBCollection::GlobalNamed) {}
+
+ void setRangeType(ScDBCollection::RangeType eNew)
+ {
+ meRangeType = eNew;
+ }
+
+ void operator() (const ::std::pair<SCTAB, const ScDBData*>& r)
+ {
+ if (meRangeType != ScDBCollection::SheetAnonymous)
+ return;
+
+ // name
+ OUString aBuf = STR_DB_LOCAL_NONAME +
+ OUString::number(static_cast<sal_Int32>(r.first)); // appended number equals sheet index on import.
+
+ write(aBuf, *r.second);
+ }
+
+ void operator() (const ScDBData& rData)
+ {
+ if (meRangeType == ScDBCollection::GlobalAnonymous)
+ {
+ // name
+ OUString aBuf = STR_DB_GLOBAL_NONAME + OUString::number(++mnCounter); // 1-based, for entirely arbitrary reasons. The numbers are ignored on import.
+
+ write(aBuf, rData);
+ }
+ else if (meRangeType == ScDBCollection::GlobalNamed)
+ write(rData.GetName(), rData);
+ }
+
+ void operator() (std::unique_ptr<ScDBData> const& p)
+ {
+ return operator()(*p);
+ }
+
+private:
+ void write(const OUString& rName, const ScDBData& rData)
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rName);
+
+ // range
+ ScRange aRange;
+ rData.GetArea(aRange);
+ OUString aRangeStr;
+ ScRangeStringConverter::GetStringFromRange(
+ aRangeStr, aRange, mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aRangeStr);
+
+ // various boolean flags.
+ if (rData.HasImportSelection())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IS_SELECTION, XML_TRUE);
+ if (rData.HasAutoFilter())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_FILTER_BUTTONS, XML_TRUE);
+ if (rData.IsKeepFmt())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ON_UPDATE_KEEP_STYLES, XML_TRUE);
+ if (rData.IsDoSize())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ON_UPDATE_KEEP_SIZE, XML_FALSE);
+ if (rData.IsStripData())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_HAS_PERSISTENT_DATA, XML_FALSE);
+
+ ScQueryParam aQueryParam;
+ rData.GetQueryParam(aQueryParam);
+ if (!aQueryParam.bHasHeader)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONTAINS_HEADER, XML_FALSE);
+
+ ScSortParam aSortParam;
+ rData.GetSortParam(aSortParam);
+ if (!aSortParam.bByRow)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORIENTATION, XML_COLUMN);
+
+ sal_Int32 nRefreshDelaySeconds = rData.GetRefreshDelaySeconds();
+ if (nRefreshDelaySeconds)
+ {
+ OUStringBuffer aBuf;
+ ::sax::Converter::convertDuration(aBuf,
+ static_cast<double>(nRefreshDelaySeconds) / 86400.0);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, aBuf.makeStringAndClear());
+ }
+
+ SvXMLElementExport aElemDR(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_RANGE, true, true);
+
+ ScSortParam aParam;
+ rData.GetSortParam(aParam);
+
+ writeImport(rData);
+ writeFilter(rData);
+ writeSort(mrExport, aParam, aRange, mpDoc);
+ writeSubtotals(rData);
+ }
+
+ void writeImport(const ScDBData& rData)
+ {
+ ScImportParam aParam;
+ rData.GetImportParam(aParam);
+
+ OUString sDatabaseName;
+ OUString sConRes;
+
+ svx::ODataAccessDescriptor aDescriptor;
+ aDescriptor.setDataSource(aParam.aDBName);
+ if (aDescriptor.has(svx::DataAccessDescriptorProperty::DataSource))
+ {
+ sDatabaseName = aParam.aDBName;
+ }
+ else if (aDescriptor.has(svx::DataAccessDescriptorProperty::ConnectionResource))
+ {
+ sConRes = aParam.aDBName;
+ }
+
+ sheet::DataImportMode nSourceType = sheet::DataImportMode_NONE;
+ if (aParam.bImport)
+ {
+ if (aParam.bSql)
+ nSourceType = sheet::DataImportMode_SQL;
+ else if (aParam.nType == ScDbQuery)
+ nSourceType = sheet::DataImportMode_QUERY;
+ else
+ nSourceType = sheet::DataImportMode_TABLE;
+ }
+
+ switch (nSourceType)
+ {
+ case sheet::DataImportMode_NONE : break;
+ case sheet::DataImportMode_QUERY :
+ {
+ if (!sDatabaseName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, sDatabaseName);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_QUERY_NAME, aParam.aStatement);
+ SvXMLElementExport aElemID(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_QUERY, true, true);
+ if (!sConRes.isEmpty())
+ {
+ mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sConRes );
+ SvXMLElementExport aElemCR(mrExport, XML_NAMESPACE_FORM, XML_CONNECTION_RESOURCE, true, true);
+ }
+ }
+ break;
+ case sheet::DataImportMode_TABLE :
+ {
+ if (!sDatabaseName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, sDatabaseName);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_TABLE_NAME, aParam.aStatement);
+ SvXMLElementExport aElemID(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_TABLE, true, true);
+ if (!sConRes.isEmpty())
+ {
+ mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sConRes );
+ SvXMLElementExport aElemCR(mrExport, XML_NAMESPACE_FORM, XML_CONNECTION_RESOURCE, true, true);
+ }
+ }
+ break;
+ case sheet::DataImportMode_SQL :
+ {
+ if (!sDatabaseName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, sDatabaseName);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SQL_STATEMENT, aParam.aStatement);
+ if (!aParam.bNative)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PARSE_SQL_STATEMENT, XML_TRUE);
+ SvXMLElementExport aElemID(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_SQL, true, true);
+ if (!sConRes.isEmpty())
+ {
+ mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sConRes );
+ SvXMLElementExport aElemCR(mrExport, XML_NAMESPACE_FORM, XML_CONNECTION_RESOURCE, true, true);
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ static OUString getOperatorXML(const ScQueryEntry& rEntry, utl::SearchParam::SearchType eSearchType)
+ {
+ switch (rEntry.eOp)
+ {
+ case SC_BEGINS_WITH:
+ return GetXMLToken(XML_BEGINS_WITH);
+ case SC_BOTPERC:
+ return GetXMLToken(XML_BOTTOM_PERCENT);
+ case SC_BOTVAL:
+ return GetXMLToken(XML_BOTTOM_VALUES);
+ case SC_CONTAINS:
+ return GetXMLToken(XML_CONTAINS);
+ case SC_DOES_NOT_BEGIN_WITH:
+ return GetXMLToken(XML_DOES_NOT_BEGIN_WITH);
+ case SC_DOES_NOT_CONTAIN:
+ return GetXMLToken(XML_DOES_NOT_CONTAIN);
+ case SC_DOES_NOT_END_WITH:
+ return GetXMLToken(XML_DOES_NOT_END_WITH);
+ case SC_ENDS_WITH:
+ return GetXMLToken(XML_ENDS_WITH);
+ case SC_EQUAL:
+ {
+ if (rEntry.IsQueryByEmpty())
+ return GetXMLToken(XML_TOKEN_EMPTY);
+ else if (rEntry.IsQueryByNonEmpty())
+ return GetXMLToken(XML_NOEMPTY);
+
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_MATCH);
+ else
+ return "=";
+ }
+ case SC_GREATER:
+ return ">";
+ case SC_GREATER_EQUAL:
+ return ">=";
+ case SC_LESS:
+ return "<";
+ case SC_LESS_EQUAL:
+ return "<=";
+ case SC_NOT_EQUAL:
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_NOMATCH);
+ else
+ return "!=";
+ case SC_TOPPERC:
+ return GetXMLToken(XML_TOP_PERCENT);
+ case SC_TOPVAL:
+ return GetXMLToken(XML_TOP_VALUES);
+ default:
+ ;
+ }
+ return "=";
+ }
+
+ class WriteSetItem
+ {
+ ScXMLExport& mrExport;
+ public:
+ explicit WriteSetItem(ScXMLExport& r) : mrExport(r) {}
+ void operator() (const ScQueryEntry::Item& rItem) const
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ SvXMLElementExport aElem(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_SET_ITEM, true, true);
+ }
+ };
+
+ void writeCondition(const ScQueryEntry& rEntry, SCCOLROW nFieldStart, bool bCaseSens,
+ utl::SearchParam::SearchType eSearchType)
+ {
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ if (rItems.empty())
+ {
+ OSL_FAIL("Query entry has no items at all! It must have at least one!");
+ return;
+ }
+
+ if (rItems.size() == 1)
+ {
+ // Single item condition.
+ const ScQueryEntry::Item& rItem = rItems.front();
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
+ else if (rItem.meType == ScQueryEntry::ByDate)
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
+ else if (rItem.meType == ScQueryEntry::ByTextColor
+ || rItem.meType == ScQueryEntry::ByBackgroundColor)
+ {
+ if (mrExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ if (rItem.meType == ScQueryEntry::ByTextColor)
+ mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_TYPE, XML_TEXT_COLOR);
+ else
+ mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_TYPE,
+ XML_BACKGROUND_COLOR);
+ }
+
+ OUString colorValue;
+ if (rItem.maColor == COL_AUTO) // tdf#142965
+ {
+ colorValue = rItem.meType == ScQueryEntry::ByTextColor
+ ? GetXMLToken(XML_WINDOW_FONT_COLOR)
+ : GetXMLToken(XML_TRANSPARENT);
+ }
+ else
+ {
+ OUStringBuffer buffer;
+ sax::Converter::convertColor(buffer, rItem.maColor);
+ colorValue = buffer.makeStringAndClear();
+ }
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, colorValue);
+ }
+ else
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_NUMBER);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
+
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, getOperatorXML(rEntry, eSearchType));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(rEntry.nField - nFieldStart));
+ if (bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+ SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
+ }
+ else
+ {
+ // Multi-item condition.
+ assert( rItems.size() > 1 && "rItems should have more than 1 element");
+
+ // Store the 1st value for backward compatibility.
+ const ScQueryEntry::Item& rItem = rItems.front();
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, OUString("="));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(rEntry.nField - nFieldStart));
+ if (bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+ SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
+
+ std::for_each(rItems.begin(), rItems.end(), WriteSetItem(mrExport));
+ }
+ }
+
+ void writeFilter(const ScDBData& rData)
+ {
+ ScQueryParam aParam;
+ rData.GetQueryParam(aParam);
+ size_t nCount = 0;
+ for (size_t n = aParam.GetEntryCount(); nCount < n; ++nCount)
+ {
+ if (!aParam.GetEntry(nCount).bDoQuery)
+ break;
+ }
+
+ if (!nCount)
+ // No filter criteria to save. Bail out.
+ return;
+
+ if (!aParam.bInplace)
+ {
+ OUString aAddrStr;
+ ScRangeStringConverter::GetStringFromAddress(
+ aAddrStr, ScAddress(aParam.nDestCol, aParam.nDestRow, aParam.nDestTab), mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aAddrStr);
+ }
+
+ ScRange aAdvSource;
+ if (rData.GetAdvancedQuerySource(aAdvSource))
+ {
+ OUString aAddrStr;
+ ScRangeStringConverter::GetStringFromRange(
+ aAddrStr, aAdvSource, mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ if (!aAddrStr.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS, aAddrStr);
+ }
+
+ if (!aParam.bDuplicate)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_DUPLICATES, XML_FALSE);
+
+ SvXMLElementExport aElemF(mrExport, XML_NAMESPACE_TABLE, XML_FILTER, true, true);
+
+ bool bAnd = false;
+ bool bOr = false;
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.eConnect == SC_AND)
+ bAnd = true;
+ else
+ bOr = true;
+ }
+
+ // Note that export field index values are relative to the first field.
+ ScRange aRange;
+ rData.GetArea(aRange);
+ SCCOLROW nFieldStart = aParam.bByRow ? aRange.aStart.Col() : aRange.aStart.Row();
+
+ if (bOr && !bAnd)
+ {
+ SvXMLElementExport aElemOr(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ for (size_t i = 0; i < nCount; ++i)
+ writeCondition(aParam.GetEntry(i), nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ else if (bAnd && !bOr)
+ {
+ SvXMLElementExport aElemAnd(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_AND, true, true);
+ for (size_t i = 0; i < nCount; ++i)
+ writeCondition(aParam.GetEntry(i), nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ else if (nCount == 1)
+ {
+ writeCondition(aParam.GetEntry(0), nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ else
+ {
+ SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ ScQueryEntry aPrevEntry = aParam.GetEntry(0);
+ ScQueryConnect eConnect = aParam.GetEntry(1).eConnect;
+ bool bOpenAndElement = false;
+ OUString aName = mrExport.GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TABLE, GetXMLToken(XML_FILTER_AND));
+
+ if (eConnect == SC_AND)
+ {
+ mrExport.StartElement(aName, true);
+ bOpenAndElement = true;
+ }
+ else
+ bOpenAndElement = false;
+
+ for (size_t i = 1; i < nCount; ++i)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (eConnect != rEntry.eConnect)
+ {
+ eConnect = rEntry.eConnect;
+ if (rEntry.eConnect == SC_AND)
+ {
+ mrExport.StartElement(aName, true );
+ bOpenAndElement = true;
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ aPrevEntry = rEntry;
+ if (i == nCount - 1)
+ {
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ mrExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ }
+ else
+ {
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ aPrevEntry = rEntry;
+ if (bOpenAndElement)
+ {
+ mrExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ if (i == nCount - 1)
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ }
+ else
+ {
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ aPrevEntry = rEntry;
+ if (i == nCount - 1)
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ }
+ if(bOpenAndElement)
+ mrExport.EndElement(aName, true);
+ }
+ }
+
+ void writeSubtotals(const ScDBData& rData)
+ {
+ ScSubTotalParam aParam;
+ rData.GetSubTotalParam(aParam);
+
+ size_t nCount = 0;
+ for (; nCount < MAXSUBTOTAL; ++nCount)
+ {
+ if (!aParam.bGroupActive[nCount])
+ break;
+ }
+
+ if (!nCount)
+ return;
+
+ if (!aParam.bIncludePattern)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BIND_STYLES_TO_CONTENT, XML_FALSE);
+
+ if (aParam.bPagebreak)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PAGE_BREAKS_ON_GROUP_CHANGE, XML_TRUE);
+
+ if (aParam.bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+
+ SvXMLElementExport aElemSTRs(mrExport, XML_NAMESPACE_TABLE, XML_SUBTOTAL_RULES, true, true);
+
+ if (aParam.bDoSort)
+ {
+ if (!aParam.bAscending)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_DESCENDING);
+
+ if (aParam.bUserDef)
+ {
+ OUString aBuf = SC_USERLIST + OUString::number(static_cast<sal_Int32>(aParam.nUserIndex));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, aBuf);
+ }
+ SvXMLElementExport aElemSGs(mrExport, XML_NAMESPACE_TABLE, XML_SORT_GROUPS, true, true);
+ }
+
+ for (size_t i = 0; i < MAXSUBTOTAL; ++i)
+ {
+ if (!aParam.bGroupActive[i])
+ // We're done!
+ break;
+
+ sal_Int32 nFieldCol = static_cast<sal_Int32>(aParam.nField[i]);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUP_BY_FIELD_NUMBER, OUString::number(nFieldCol));
+ SvXMLElementExport aElemSTR(mrExport, XML_NAMESPACE_TABLE, XML_SUBTOTAL_RULE, true, true);
+
+ for (SCCOL j = 0, n = aParam.nSubTotals[i]; j < n; ++j)
+ {
+ sal_Int32 nCol = static_cast<sal_Int32>(aParam.pSubTotals[i][j]);
+ ScSubTotalFunc eFunc = aParam.pFunctions[i][j];
+
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(nCol));
+ OUString aFuncStr = ScXMLConverter::GetStringFromFunction(eFunc);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FUNCTION, aFuncStr);
+
+ SvXMLElementExport aElemSTF(mrExport, XML_NAMESPACE_TABLE, XML_SUBTOTAL_FIELD, true, true);
+ }
+ }
+ }
+};
+
+}
+
+void ScXMLExportDatabaseRanges::WriteDatabaseRanges()
+{
+ pDoc = rExport.GetDocument();
+ if (!pDoc)
+ return;
+
+ // Get sheet-local anonymous ranges.
+ SCTAB nTabCount = pDoc->GetTableCount();
+ std::map<SCTAB, const ScDBData*> aSheetDBs;
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ const ScDBData* p = pDoc->GetAnonymousDBData(i);
+ if (p)
+ aSheetDBs.emplace(i, p);
+ }
+
+ bool bHasRanges = !aSheetDBs.empty();
+
+ // See if we have global ranges.
+ ScDBCollection* pDBCollection = pDoc->GetDBCollection();
+ if (pDBCollection)
+ {
+ if (!pDBCollection->getNamedDBs().empty() || !pDBCollection->getAnonDBs().empty())
+ bHasRanges = true;
+ }
+
+ if (!bHasRanges)
+ // No ranges to export. Bail out.
+ return;
+
+ SvXMLElementExport aElemDRs(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_RANGES, true, true);
+
+ WriteDatabaseRange func(rExport, pDoc);
+
+ if (pDBCollection)
+ {
+ // Write global named ranges.
+ func.setRangeType(ScDBCollection::GlobalNamed);
+ const ScDBCollection::NamedDBs& rNamedDBs = pDBCollection->getNamedDBs();
+ ::std::for_each(rNamedDBs.begin(), rNamedDBs.end(), func);
+
+ // Add global anonymous DB ranges.
+ func.setRangeType(ScDBCollection::GlobalAnonymous);
+ const ScDBCollection::AnonDBs& rAnonDBs = pDBCollection->getAnonDBs();
+ ::std::for_each(rAnonDBs.begin(), rAnonDBs.end(), func);
+ }
+
+ // Write sheet-local ranges.
+ func.setRangeType(ScDBCollection::SheetAnonymous);
+ ::std::for_each(aSheetDBs.begin(), aSheetDBs.end(), func);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.hxx b/sc/source/filter/xml/XMLExportDatabaseRanges.hxx
new file mode 100644
index 0000000000..ed8f794975
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDatabaseRanges.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 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+
+struct ScSortParam;
+class ScXMLExport;
+class ScDocument;
+class ScMyEmptyDatabaseRangesContainer;
+
+void writeSort(ScXMLExport& mrExport, const ScSortParam& aParam, const ScRange& aRange,
+ const ScDocument* mpDoc);
+
+class ScXMLExportDatabaseRanges
+{
+ ScXMLExport& rExport;
+ ScDocument* pDoc;
+
+public:
+ explicit ScXMLExportDatabaseRanges(ScXMLExport& rExport);
+ ScMyEmptyDatabaseRangesContainer GetEmptyDatabaseRanges();
+ void WriteDatabaseRanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportIterator.cxx b/sc/source/filter/xml/XMLExportIterator.cxx
new file mode 100644
index 0000000000..bff7e84986
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportIterator.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 <sal/config.h>
+
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+
+#include "XMLExportIterator.hxx"
+#include <dociter.hxx>
+#include "xmlexprt.hxx"
+#include "XMLExportSharedData.hxx"
+#include "XMLStylesExportHelper.hxx"
+#include <document.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+ScMyIteratorBase::ScMyIteratorBase()
+{
+}
+
+ScMyIteratorBase::~ScMyIteratorBase()
+{
+}
+
+void ScMyIteratorBase::UpdateAddress( ScAddress& rCellAddress )
+{
+ ScAddress aNewAddr( rCellAddress );
+ if( GetFirstAddress( aNewAddr ) )
+ {
+ if( ( aNewAddr.Tab() == rCellAddress.Tab() ) &&
+ ( ( aNewAddr.Row() < rCellAddress.Row() ) ||
+ ( ( aNewAddr.Row() == rCellAddress.Row() ) && ( aNewAddr.Col() < rCellAddress.Col() ) ) ) )
+ rCellAddress = aNewAddr;
+ }
+}
+
+inline bool ScMyShape::operator<(const ScMyShape& aShape) const
+{
+ return aAddress.lessThanByRow( aShape.aAddress );
+}
+
+ScMyShapesContainer::ScMyShapesContainer()
+{
+}
+
+ScMyShapesContainer::~ScMyShapesContainer()
+{
+}
+
+void ScMyShapesContainer::AddNewShape( const ScMyShape& aShape )
+{
+ aShapeList.push_back(aShape);
+}
+
+bool ScMyShapesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aShapeList.empty() )
+ {
+ rCellAddress = aShapeList.begin()->aAddress;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyShapesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.aShapeList.clear();
+
+ ScMyShapeList::iterator aItr(aShapeList.begin());
+ ScMyShapeList::iterator aEndItr(aShapeList.end());
+ while( (aItr != aEndItr) && (aItr->aAddress == rMyCell.maCellAddress) )
+ {
+ rMyCell.aShapeList.push_back(*aItr);
+ aItr = aShapeList.erase(aItr);
+ }
+ rMyCell.bHasShape = !rMyCell.aShapeList.empty();
+}
+
+void ScMyShapesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyShapeList::iterator aItr = std::find_if_not(aShapeList.begin(), aShapeList.end(),
+ [&nSkip](const ScMyShape& rShape) { return rShape.aAddress.Tab() == nSkip; });
+ aShapeList.erase(aShapeList.begin(), aItr);
+}
+
+void ScMyShapesContainer::Sort()
+{
+ aShapeList.sort();
+}
+
+inline bool ScMyNoteShape::operator<(const ScMyNoteShape& aNote) const
+{
+ return aPos.lessThanByRow( aNote.aPos );
+}
+
+ScMyNoteShapesContainer::ScMyNoteShapesContainer()
+{
+}
+
+ScMyNoteShapesContainer::~ScMyNoteShapesContainer()
+{
+}
+
+void ScMyNoteShapesContainer::AddNewNote( const ScMyNoteShape& aNote )
+{
+ aNoteShapeList.push_back(aNote);
+}
+
+bool ScMyNoteShapesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable = rCellAddress.Tab();
+ if( !aNoteShapeList.empty() )
+ {
+ rCellAddress = aNoteShapeList.begin()->aPos;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyNoteShapesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ ScMyNoteShapeList::iterator aItr = std::find_if_not(aNoteShapeList.begin(), aNoteShapeList.end(),
+ [&rMyCell](const ScMyNoteShape& rNoteShape) { return rNoteShape.aPos == rMyCell.maCellAddress; });
+ aNoteShapeList.erase(aNoteShapeList.begin(), aItr);
+}
+
+void ScMyNoteShapesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyNoteShapeList::iterator aItr = std::find_if_not(aNoteShapeList.begin(), aNoteShapeList.end(),
+ [&nSkip](const ScMyNoteShape& rNoteShape) { return rNoteShape.aPos.Tab() == nSkip; });
+ aNoteShapeList.erase(aNoteShapeList.begin(), aItr);
+}
+
+void ScMyNoteShapesContainer::Sort()
+{
+ aNoteShapeList.sort();
+}
+
+inline bool ScMyMergedRange::operator<(const ScMyMergedRange& aRange) const
+{
+ return aCellRange.aStart.lessThanByRow( aRange.aCellRange.aStart );
+}
+
+ScMyMergedRangesContainer::ScMyMergedRangesContainer()
+{
+}
+
+ScMyMergedRangesContainer::~ScMyMergedRangesContainer()
+{
+}
+
+void ScMyMergedRangesContainer::AddRange(const ScRange& rMergedRange)
+{
+ SCROW nStartRow( rMergedRange.aStart.Row() );
+ SCROW nEndRow( rMergedRange.aEnd.Row() );
+
+ ScMyMergedRange aRange;
+ aRange.bIsFirst = true;
+
+ aRange.aCellRange = rMergedRange;
+
+ aRange.aCellRange.aEnd.SetRow( nStartRow );
+ aRange.nRows = nEndRow - nStartRow + 1;
+ aRangeList.push_back( aRange );
+
+ aRange.bIsFirst = false;
+ aRange.nRows = 0;
+ for( SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow )
+ {
+ aRange.aCellRange.aStart.SetRow( nRow );
+ aRange.aCellRange.aEnd.SetRow( nRow );
+ aRangeList.push_back(aRange);
+ }
+}
+
+bool ScMyMergedRangesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aRangeList.empty() )
+ {
+ rCellAddress = aRangeList.begin()->aCellRange.aStart;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyMergedRangesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.bIsMergedBase = rMyCell.bIsCovered = false;
+ ScMyMergedRangeList::iterator aItr(aRangeList.begin());
+ if( aItr == aRangeList.end() )
+ return;
+
+ if( aItr->aCellRange.aStart != rMyCell.aCellAddress )
+ return;
+
+ rMyCell.aMergeRange = aItr->aCellRange;
+ if (aItr->bIsFirst)
+ rMyCell.aMergeRange.aEnd.SetRow( rMyCell.aMergeRange.aStart.Row() + aItr->nRows - 1 );
+ rMyCell.bIsMergedBase = aItr->bIsFirst;
+ rMyCell.bIsCovered = !aItr->bIsFirst;
+ if( aItr->aCellRange.aStart.Col() < aItr->aCellRange.aEnd.Col() )
+ {
+ aItr->aCellRange.aStart.IncCol( 1 );
+ aItr->bIsFirst = false;
+ }
+ else
+ aRangeList.erase(aItr);
+}
+
+void ScMyMergedRangesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyMergedRangeList::iterator aItr = std::find_if_not(aRangeList.begin(), aRangeList.end(),
+ [&nSkip](const ScMyMergedRange& rRange) { return rRange.aCellRange.aStart.Tab() == nSkip; });
+ aRangeList.erase(aRangeList.begin(), aItr);
+}
+
+void ScMyMergedRangesContainer::Sort()
+{
+ aRangeList.sort();
+}
+
+bool ScMyAreaLink::Compare( const ScMyAreaLink& rAreaLink ) const
+{
+ return (GetRowCount() == rAreaLink.GetRowCount()) &&
+ (sFilter == rAreaLink.sFilter) &&
+ (sFilterOptions == rAreaLink.sFilterOptions) &&
+ (sURL == rAreaLink.sURL) &&
+ (sSourceStr == rAreaLink.sSourceStr);
+}
+
+inline bool ScMyAreaLink::operator<(const ScMyAreaLink& rAreaLink ) const
+{
+ return aDestRange.aStart.lessThanByRow( rAreaLink.aDestRange.aStart );
+}
+
+ScMyAreaLinksContainer::ScMyAreaLinksContainer()
+{
+}
+
+ScMyAreaLinksContainer::~ScMyAreaLinksContainer()
+{
+}
+
+bool ScMyAreaLinksContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aAreaLinkList.empty() )
+ {
+ rCellAddress = aAreaLinkList.begin()->aDestRange.aStart;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyAreaLinksContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.bHasAreaLink = false;
+ ScMyAreaLinkList::iterator aItr(aAreaLinkList.begin());
+ if( aItr == aAreaLinkList.end() )
+ return;
+
+ if( aItr->aDestRange.aStart != rMyCell.aCellAddress )
+ return;
+
+ rMyCell.bHasAreaLink = true;
+ rMyCell.aAreaLink = *aItr;
+ aItr = aAreaLinkList.erase( aItr );
+ bool bFound = true;
+ while (aItr != aAreaLinkList.end() && bFound)
+ {
+ if ( aItr->aDestRange.aStart == rMyCell.aCellAddress )
+ {
+ OSL_FAIL("more than one linked range on one cell");
+ aItr = aAreaLinkList.erase( aItr );
+ }
+ else
+ bFound = false;
+ }
+}
+
+void ScMyAreaLinksContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyAreaLinkList::iterator aItr = std::find_if_not(aAreaLinkList.begin(), aAreaLinkList.end(),
+ [&nSkip](const ScMyAreaLink& rAreaLink) { return rAreaLink.aDestRange.aStart.Tab() == nSkip; });
+ aAreaLinkList.erase(aAreaLinkList.begin(), aItr);
+}
+
+void ScMyAreaLinksContainer::Sort()
+{
+ aAreaLinkList.sort();
+}
+
+ScMyEmptyDatabaseRangesContainer::ScMyEmptyDatabaseRangesContainer()
+{
+}
+
+ScMyEmptyDatabaseRangesContainer::~ScMyEmptyDatabaseRangesContainer()
+{
+}
+
+void ScMyEmptyDatabaseRangesContainer::AddNewEmptyDatabaseRange(const table::CellRangeAddress& aCellRange)
+{
+ SCROW nStartRow(aCellRange.StartRow);
+ SCROW nEndRow(aCellRange.EndRow);
+ ScRange aRange( aCellRange.StartColumn, aCellRange.StartRow, aCellRange.Sheet,
+ aCellRange.EndColumn, aCellRange.EndRow, aCellRange.Sheet );
+ for( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ aRange.aStart.SetRow( nRow );
+ aRange.aEnd.SetRow( nRow );
+ aDatabaseList.push_back( aRange );
+ }
+}
+
+bool ScMyEmptyDatabaseRangesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aDatabaseList.empty() )
+ {
+ rCellAddress = aDatabaseList.begin()->aStart;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyEmptyDatabaseRangesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.bHasEmptyDatabase = false;
+ ScMyEmptyDatabaseRangeList::iterator aItr(aDatabaseList.begin());
+ if( aItr != aDatabaseList.end() )
+ {
+ if( aItr->aStart == rMyCell.aCellAddress )
+ {
+ rMyCell.bHasEmptyDatabase = true;
+ if( aItr->aStart.Col() < aItr->aEnd.Col() )
+ aItr->aStart.SetCol( aItr->aStart.Col() + 1 );
+ else
+ aDatabaseList.erase(aItr);
+ }
+ }
+}
+
+void ScMyEmptyDatabaseRangesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyEmptyDatabaseRangeList::iterator aItr = std::find_if_not(aDatabaseList.begin(), aDatabaseList.end(),
+ [&nSkip](const ScRange& rDatabase) { return rDatabase.aStart.Tab() == nSkip; });
+ aDatabaseList.erase(aDatabaseList.begin(), aItr);
+}
+
+void ScMyEmptyDatabaseRangesContainer::Sort()
+{
+ aDatabaseList.sort();
+}
+
+inline bool ScMyDetectiveObj::operator<( const ScMyDetectiveObj& rDetObj) const
+{
+ return aPosition.lessThanByRow( rDetObj.aPosition );
+}
+
+ScMyDetectiveObjContainer::ScMyDetectiveObjContainer()
+{
+}
+
+ScMyDetectiveObjContainer::~ScMyDetectiveObjContainer()
+{
+}
+
+void ScMyDetectiveObjContainer::AddObject( ScDetectiveObjType eObjType, const SCTAB nSheet,
+ const ScAddress& rPosition, const ScRange& rSourceRange,
+ bool bHasError )
+{
+ if( !((eObjType == SC_DETOBJ_ARROW) ||
+ (eObjType == SC_DETOBJ_FROMOTHERTAB) ||
+ (eObjType == SC_DETOBJ_TOOTHERTAB) ||
+ (eObjType == SC_DETOBJ_CIRCLE)) )
+ return;
+
+ ScMyDetectiveObj aDetObj;
+ aDetObj.eObjType = eObjType;
+ if( eObjType == SC_DETOBJ_TOOTHERTAB )
+ aDetObj.aPosition = rSourceRange.aStart;
+ else
+ aDetObj.aPosition = rPosition;
+ aDetObj.aSourceRange = rSourceRange;
+
+ // #111064#; take the sheet where the object is found and not the sheet given in the ranges, because they are not always true
+ if (eObjType != SC_DETOBJ_FROMOTHERTAB)
+ {
+ // if the ObjType == SC_DETOBJ_FROMOTHERTAB then the SourceRange is not used and so it has not to be tested and changed
+ OSL_ENSURE(aDetObj.aPosition.Tab() == aDetObj.aSourceRange.aStart.Tab(), "It seems to be possible to have different sheets");
+ aDetObj.aSourceRange.aStart.SetTab( nSheet );
+ aDetObj.aSourceRange.aEnd.SetTab( nSheet );
+ }
+ aDetObj.aPosition.SetTab( nSheet );
+
+ aDetObj.bHasError = bHasError;
+ aDetectiveObjList.push_back( aDetObj );
+}
+
+bool ScMyDetectiveObjContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aDetectiveObjList.empty() )
+ {
+ rCellAddress = aDetectiveObjList.begin()->aPosition;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyDetectiveObjContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.aDetectiveObjVec.clear();
+ ScMyDetectiveObjList::iterator aItr(aDetectiveObjList.begin());
+ ScMyDetectiveObjList::iterator aEndItr(aDetectiveObjList.end());
+ while( (aItr != aEndItr) && (aItr->aPosition == rMyCell.aCellAddress) )
+ {
+ rMyCell.aDetectiveObjVec.push_back( *aItr );
+ aItr = aDetectiveObjList.erase( aItr );
+ }
+ rMyCell.bHasDetectiveObj = (!rMyCell.aDetectiveObjVec.empty());
+}
+
+void ScMyDetectiveObjContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyDetectiveObjList::iterator aItr = std::find_if_not(aDetectiveObjList.begin(), aDetectiveObjList.end(),
+ [&nSkip](const ScMyDetectiveObj& rDetectiveObj) { return rDetectiveObj.aPosition.Tab() == nSkip; });
+ aDetectiveObjList.erase(aDetectiveObjList.begin(), aItr);
+}
+
+void ScMyDetectiveObjContainer::Sort()
+{
+ aDetectiveObjList.sort();
+}
+
+inline bool ScMyDetectiveOp::operator<( const ScMyDetectiveOp& rDetOp) const
+{
+ return aPosition.lessThanByRow( rDetOp.aPosition );
+}
+
+ScMyDetectiveOpContainer::ScMyDetectiveOpContainer()
+{
+}
+
+ScMyDetectiveOpContainer::~ScMyDetectiveOpContainer()
+{
+}
+
+void ScMyDetectiveOpContainer::AddOperation( ScDetOpType eOpType, const ScAddress& rPosition, sal_uInt32 nIndex )
+{
+ ScMyDetectiveOp aDetOp;
+ aDetOp.eOpType = eOpType;
+ aDetOp.aPosition = rPosition;
+ aDetOp.nIndex = nIndex;
+ aDetectiveOpList.push_back( aDetOp );
+}
+
+bool ScMyDetectiveOpContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aDetectiveOpList.empty() )
+ {
+ rCellAddress = aDetectiveOpList.begin()->aPosition;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyDetectiveOpContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.aDetectiveOpVec.clear();
+ ScMyDetectiveOpList::iterator aItr(aDetectiveOpList.begin());
+ ScMyDetectiveOpList::iterator aEndItr(aDetectiveOpList.end());
+ while( (aItr != aEndItr) && (aItr->aPosition == rMyCell.aCellAddress) )
+ {
+ rMyCell.aDetectiveOpVec.push_back( *aItr );
+ aItr = aDetectiveOpList.erase( aItr );
+ }
+ rMyCell.bHasDetectiveOp = (!rMyCell.aDetectiveOpVec.empty());
+}
+
+void ScMyDetectiveOpContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyDetectiveOpList::iterator aItr = std::find_if_not(aDetectiveOpList.begin(), aDetectiveOpList.end(),
+ [&nSkip](const ScMyDetectiveOp& rDetectiveOp) { return rDetectiveOp.aPosition.Tab() == nSkip; });
+ aDetectiveOpList.erase(aDetectiveOpList.begin(), aItr);
+}
+
+void ScMyDetectiveOpContainer::Sort()
+{
+ aDetectiveOpList.sort();
+}
+
+ScMyCell::ScMyCell() :
+ pNote(nullptr),
+ nValidationIndex(-1),
+ nStyleIndex(-1),
+ nNumberFormat(-1),
+ nType(table::CellContentType_EMPTY),
+ bIsAutoStyle( false ),
+ bHasShape( false ),
+ bIsMergedBase( false ),
+ bIsCovered( false ),
+ bHasAreaLink( false ),
+ bHasEmptyDatabase( false ),
+ bHasDetectiveObj( false ),
+ bHasDetectiveOp( false ),
+ bIsMatrixBase( false ),
+ bIsMatrixCovered( false ),
+ bHasAnnotation( false )
+{
+}
+
+ScMyNotEmptyCellsIterator::ScMyNotEmptyCellsIterator(ScXMLExport& rTempXMLExport)
+ : pShapes(nullptr),
+ pNoteShapes(nullptr),
+ pEmptyDatabaseRanges(nullptr),
+ pMergedRanges(nullptr),
+ pAreaLinks(nullptr),
+ pDetectiveObj(nullptr),
+ pDetectiveOp(nullptr),
+ rExport(rTempXMLExport),
+ nCellCol(0),
+ nCellRow(0),
+ nCurrentTable(SCTAB_MAX)
+{
+}
+
+ScMyNotEmptyCellsIterator::~ScMyNotEmptyCellsIterator()
+{
+ Clear();
+}
+
+void ScMyNotEmptyCellsIterator::Clear()
+{
+ mpCellItr.reset();
+ pShapes = nullptr;
+ pNoteShapes = nullptr;
+ pMergedRanges = nullptr;
+ pAreaLinks = nullptr;
+ pEmptyDatabaseRanges = nullptr;
+ pDetectiveObj = nullptr;
+ pDetectiveOp = nullptr;
+ nCurrentTable = SCTAB_MAX;
+}
+
+void ScMyNotEmptyCellsIterator::UpdateAddress( ScAddress& rAddress )
+{
+ if (mpCellItr->GetPos(nCellCol, nCellRow))
+ {
+ rAddress.SetCol( nCellCol );
+ rAddress.SetRow( nCellRow );
+ }
+}
+
+void ScMyNotEmptyCellsIterator::SetCellData( ScMyCell& rMyCell, const ScAddress& rAddress )
+{
+ rMyCell.maBaseCell.clear();
+ rMyCell.aCellAddress = rAddress;
+ rMyCell.maCellAddress = rMyCell.aCellAddress;
+
+ if( ( nCellCol == rAddress.Col() ) && ( nCellRow == rAddress.Row() ) )
+ {
+ const ScRefCellValue* pCell = mpCellItr->GetNext(nCellCol, nCellRow);
+ if (pCell)
+ rMyCell.maBaseCell = *pCell;
+ }
+
+ rMyCell.bIsMatrixCovered = false;
+ rMyCell.bIsMatrixBase = false;
+
+ switch (rMyCell.maBaseCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ rMyCell.nType = table::CellContentType_VALUE;
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ rMyCell.nType = table::CellContentType_TEXT;
+ break;
+ case CELLTYPE_FORMULA:
+ rMyCell.nType = table::CellContentType_FORMULA;
+ break;
+ default:
+ rMyCell.nType = table::CellContentType_EMPTY;
+ }
+
+ if (rMyCell.maBaseCell.getType() == CELLTYPE_FORMULA)
+ {
+ bool bIsMatrixBase = false;
+ if (rExport.IsMatrix(rMyCell.maCellAddress, rMyCell.aMatrixRange, bIsMatrixBase))
+ {
+ rMyCell.bIsMatrixBase = bIsMatrixBase;
+ rMyCell.bIsMatrixCovered = !bIsMatrixBase;
+ }
+ }
+}
+
+void ScMyNotEmptyCellsIterator::HasAnnotation(ScMyCell& aCell)
+{
+ aCell.bHasAnnotation = false;
+ ScPostIt* pNote = rExport.GetDocument()->GetNote(aCell.maCellAddress);
+
+ if(pNote)
+ {
+ aCell.bHasAnnotation = true;
+ aCell.pNote = pNote;
+ }
+}
+
+void ScMyNotEmptyCellsIterator::SetCurrentTable(const SCTAB nTable,
+ const uno::Reference<sheet::XSpreadsheet>& rxTable)
+{
+ aLastAddress.SetRow( 0 );
+ aLastAddress.SetCol( 0 );
+ aLastAddress.SetTab( nTable );
+ if (nCurrentTable == nTable)
+ return;
+
+ nCurrentTable = nTable;
+
+ mpCellItr.reset(
+ new ScHorizontalCellIterator(
+ *rExport.GetDocument(), nCurrentTable, 0, 0,
+ static_cast<SCCOL>(rExport.GetSharedData()->GetLastColumn(nCurrentTable)),
+ static_cast<SCROW>(rExport.GetSharedData()->GetLastRow(nCurrentTable))));
+
+ xTable.set(rxTable);
+ xCellRange.set(xTable);
+}
+
+void ScMyNotEmptyCellsIterator::SkipTable(SCTAB nSkip)
+{
+ // Skip entries for a sheet that is copied instead of saving normally.
+ // Cells are handled separately in SetCurrentTable.
+
+ if( pShapes )
+ pShapes->SkipTable(nSkip);
+ if( pNoteShapes )
+ pNoteShapes->SkipTable(nSkip);
+ if( pEmptyDatabaseRanges )
+ pEmptyDatabaseRanges->SkipTable(nSkip);
+ if( pMergedRanges )
+ pMergedRanges->SkipTable(nSkip);
+ if( pAreaLinks )
+ pAreaLinks->SkipTable(nSkip);
+ if( pDetectiveObj )
+ pDetectiveObj->SkipTable(nSkip);
+ if( pDetectiveOp )
+ pDetectiveOp->SkipTable(nSkip);
+}
+
+bool ScMyNotEmptyCellsIterator::GetNext(ScMyCell& aCell, ScFormatRangeStyles* pCellStyles)
+{
+ ScDocument* pDoc = rExport.GetDocument();
+ ScAddress aAddress( pDoc->MaxCol() + 1, pDoc->MaxRow() + 1, nCurrentTable );
+
+ UpdateAddress( aAddress );
+
+ if( pShapes )
+ pShapes->UpdateAddress( aAddress );
+ if( pNoteShapes )
+ pNoteShapes->UpdateAddress( aAddress );
+ if( pEmptyDatabaseRanges )
+ pEmptyDatabaseRanges->UpdateAddress( aAddress );
+ if( pMergedRanges )
+ pMergedRanges->UpdateAddress( aAddress );
+ if( pAreaLinks )
+ pAreaLinks->UpdateAddress( aAddress );
+ if( pDetectiveObj )
+ pDetectiveObj->UpdateAddress( aAddress );
+ if( pDetectiveOp )
+ pDetectiveOp->UpdateAddress( aAddress );
+
+ bool bFoundCell( ( aAddress.Col() <= pDoc->MaxCol() ) && ( aAddress.Row() <= pDoc->MaxRow() + 1 ) );
+ if( bFoundCell )
+ {
+ SetCellData( aCell, aAddress );
+ if( pShapes )
+ pShapes->SetCellData( aCell );
+ if( pNoteShapes )
+ pNoteShapes->SetCellData( aCell );
+ if( pEmptyDatabaseRanges )
+ pEmptyDatabaseRanges->SetCellData( aCell );
+ if( pMergedRanges )
+ pMergedRanges->SetCellData( aCell );
+ if( pAreaLinks )
+ pAreaLinks->SetCellData( aCell );
+ if( pDetectiveObj )
+ pDetectiveObj->SetCellData( aCell );
+ if( pDetectiveOp )
+ pDetectiveOp->SetCellData( aCell );
+
+ HasAnnotation( aCell );
+ bool bIsAutoStyle(false);
+ // Ranges before the previous cell are not needed by ExportFormatRanges anymore and can be removed
+ SCROW nRemoveBeforeRow = aLastAddress.Row();
+ aCell.nStyleIndex = pCellStyles->GetStyleNameIndex(aCell.maCellAddress.Tab(),
+ aCell.maCellAddress.Col(), aCell.maCellAddress.Row(),
+ bIsAutoStyle, aCell.nValidationIndex, aCell.nNumberFormat, nRemoveBeforeRow);
+ aLastAddress = aCell.aCellAddress;
+ aCell.bIsAutoStyle = bIsAutoStyle;
+
+ //#102799#; if the cell is in a DatabaseRange which should saved empty, the cell should have the type empty
+ if (aCell.bHasEmptyDatabase)
+ aCell.nType = table::CellContentType_EMPTY;
+ }
+ return bFoundCell;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportIterator.hxx b/sc/source/filter/xml/XMLExportIterator.hxx
new file mode 100644
index 0000000000..6690c92c62
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportIterator.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <list>
+#include <com/sun/star/table/CellContentType.hpp>
+#include <detfunc.hxx>
+#include <detdata.hxx>
+#include <cellvalue.hxx>
+
+#include <memory>
+
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::table { struct CellRangeAddress; }
+
+class ScPostIt;
+class ScHorizontalCellIterator;
+struct ScMyCell;
+class ScXMLExport;
+class ScFormatRangeStyles;
+
+class ScMyIteratorBase
+{
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) = 0;
+
+public:
+ ScMyIteratorBase();
+ virtual ~ScMyIteratorBase();
+
+ ScMyIteratorBase(ScMyIteratorBase const &) = default;
+ ScMyIteratorBase(ScMyIteratorBase &&) = default;
+ ScMyIteratorBase & operator =(ScMyIteratorBase const &) = default;
+ ScMyIteratorBase & operator =(ScMyIteratorBase &&) = default;
+
+ virtual void SetCellData( ScMyCell& rMyCell ) = 0;
+ virtual void Sort() = 0;
+
+ void UpdateAddress( ScAddress& rCellAddress );
+};
+
+struct ScMyShape
+{
+ ScAddress aAddress;
+ ScAddress aEndAddress;
+ sal_Int32 nEndX;
+ sal_Int32 nEndY;
+ css::uno::Reference<css::drawing::XShape> xShape;
+
+ bool operator<(const ScMyShape& aShape) const;
+};
+
+typedef std::list<ScMyShape> ScMyShapeList;
+
+class ScMyShapesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyShapeList aShapeList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyShapesContainer();
+ virtual ~ScMyShapesContainer() override;
+
+ using ScMyIteratorBase::UpdateAddress;
+ void AddNewShape(const ScMyShape& aShape);
+ const ScMyShapeList& GetShapes() const { return aShapeList; }
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyNoteShape
+{
+ css::uno::Reference<css::drawing::XShape> xShape;
+ ScAddress aPos;
+
+ bool operator<(const ScMyNoteShape& aNote) const;
+};
+
+typedef std::list<ScMyNoteShape> ScMyNoteShapeList;
+
+class ScMyNoteShapesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyNoteShapeList aNoteShapeList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyNoteShapesContainer();
+ virtual ~ScMyNoteShapesContainer() override;
+
+ using ScMyIteratorBase::UpdateAddress;
+ void AddNewNote(const ScMyNoteShape& aNote);
+ const ScMyNoteShapeList& GetNotes() const { return aNoteShapeList; }
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyMergedRange
+{
+ ScRange aCellRange;
+ sal_Int32 nRows;
+ bool bIsFirst;
+ bool operator<(const ScMyMergedRange& aRange) const;
+};
+
+typedef std::list<ScMyMergedRange> ScMyMergedRangeList;
+
+class ScMyMergedRangesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyMergedRangeList aRangeList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyMergedRangesContainer();
+ virtual ~ScMyMergedRangesContainer() override;
+ void AddRange(const ScRange& rMergedRange);
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override; // + remove doublets
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyAreaLink
+{
+ OUString sFilter;
+ OUString sFilterOptions;
+ OUString sURL;
+ OUString sSourceStr;
+ ScRange aDestRange;
+ sal_Int32 nRefreshDelaySeconds;
+
+ ScMyAreaLink() : nRefreshDelaySeconds( 0 ) {}
+
+ sal_Int32 GetColCount() const { return aDestRange.aEnd.Col() - aDestRange.aStart.Col() + 1; }
+ sal_Int32 GetRowCount() const { return aDestRange.aEnd.Row() - aDestRange.aStart.Row() + 1; }
+
+ bool Compare( const ScMyAreaLink& rAreaLink ) const;
+ bool operator<(const ScMyAreaLink& rAreaLink ) const;
+};
+
+typedef ::std::list< ScMyAreaLink > ScMyAreaLinkList;
+
+class ScMyAreaLinksContainer : public ScMyIteratorBase
+{
+private:
+ ScMyAreaLinkList aAreaLinkList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyAreaLinksContainer();
+ virtual ~ScMyAreaLinksContainer() override;
+
+ void AddNewAreaLink( const ScMyAreaLink& rAreaLink )
+ { aAreaLinkList.push_back( rAreaLink ); }
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+typedef std::list<ScRange> ScMyEmptyDatabaseRangeList;
+
+class ScMyEmptyDatabaseRangesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyEmptyDatabaseRangeList aDatabaseList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyEmptyDatabaseRangesContainer();
+ virtual ~ScMyEmptyDatabaseRangesContainer() override;
+
+ ScMyEmptyDatabaseRangesContainer(ScMyEmptyDatabaseRangesContainer const &) = default;
+ ScMyEmptyDatabaseRangesContainer(ScMyEmptyDatabaseRangesContainer &&) = default;
+ ScMyEmptyDatabaseRangesContainer & operator =(ScMyEmptyDatabaseRangesContainer const &) = default;
+ ScMyEmptyDatabaseRangesContainer & operator =(ScMyEmptyDatabaseRangesContainer &&) = default;
+
+ void AddNewEmptyDatabaseRange(const css::table::CellRangeAddress& aCellRangeAddress);
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyDetectiveObj
+{
+ ScAddress aPosition;
+ ScRange aSourceRange;
+ ScDetectiveObjType eObjType;
+ bool bHasError;
+ bool operator<(const ScMyDetectiveObj& rDetObj) const;
+};
+
+typedef ::std::list< ScMyDetectiveObj > ScMyDetectiveObjList;
+typedef ::std::vector< ScMyDetectiveObj > ScMyDetectiveObjVec;
+
+class ScMyDetectiveObjContainer : public ScMyIteratorBase
+{
+private:
+ ScMyDetectiveObjList aDetectiveObjList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyDetectiveObjContainer();
+ virtual ~ScMyDetectiveObjContainer() override;
+
+ void AddObject(
+ ScDetectiveObjType eObjType,
+ const SCTAB nSheet,
+ const ScAddress& rPosition,
+ const ScRange& rSourceRange,
+ bool bHasError );
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyDetectiveOp
+{
+ ScAddress aPosition;
+ ScDetOpType eOpType;
+ sal_Int32 nIndex;
+ bool operator<(const ScMyDetectiveOp& rDetOp) const;
+};
+
+typedef ::std::list< ScMyDetectiveOp > ScMyDetectiveOpList;
+typedef ::std::vector< ScMyDetectiveOp > ScMyDetectiveOpVec;
+
+class ScMyDetectiveOpContainer : public ScMyIteratorBase
+{
+private:
+ ScMyDetectiveOpList aDetectiveOpList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyDetectiveOpContainer();
+ virtual ~ScMyDetectiveOpContainer() override;
+
+ void AddOperation( ScDetOpType eOpType, const ScAddress& rPosition, sal_uInt32 nIndex );
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+// contains data to export for the current cell position
+struct ScMyCell
+{
+ ScAddress maCellAddress; /// Use this instead of the UNO one.
+
+ ScAddress aCellAddress;
+ ScRange aMergeRange;
+ ScRange aMatrixRange;
+
+ ScMyAreaLink aAreaLink;
+ ScMyShapeList aShapeList;
+ ScMyDetectiveObjVec aDetectiveObjVec;
+ ScMyDetectiveOpVec aDetectiveOpVec;
+
+ ScPostIt* pNote;
+
+ sal_Int32 nValidationIndex;
+ sal_Int32 nStyleIndex;
+ sal_Int32 nNumberFormat;
+ css::table::CellContentType nType;
+
+ ScRefCellValue maBaseCell;
+
+ bool bIsAutoStyle;
+
+ bool bHasShape;
+ bool bIsMergedBase;
+ bool bIsCovered;
+ bool bHasAreaLink;
+ bool bHasEmptyDatabase;
+ bool bHasDetectiveObj;
+ bool bHasDetectiveOp;
+
+ bool bIsMatrixBase;
+ bool bIsMatrixCovered;
+ bool bHasAnnotation;
+
+ ScMyCell();
+};
+
+class ScMyNotEmptyCellsIterator
+{
+ ScMyNotEmptyCellsIterator(const ScMyNotEmptyCellsIterator&) = delete;
+ const ScMyNotEmptyCellsIterator& operator=(const ScMyNotEmptyCellsIterator&) = delete;
+
+ css::uno::Reference<css::sheet::XSpreadsheet> xTable;
+ css::uno::Reference<css::table::XCellRange> xCellRange;
+ ScAddress aLastAddress;
+
+ ScMyShapesContainer* pShapes;
+ ScMyNoteShapesContainer* pNoteShapes;
+ ScMyEmptyDatabaseRangesContainer* pEmptyDatabaseRanges;
+ ScMyMergedRangesContainer* pMergedRanges;
+ ScMyAreaLinksContainer* pAreaLinks;
+ ScMyDetectiveObjContainer* pDetectiveObj;
+ ScMyDetectiveOpContainer* pDetectiveOp;
+
+ ScXMLExport& rExport;
+ std::unique_ptr<ScHorizontalCellIterator> mpCellItr;
+
+ SCCOL nCellCol;
+ SCROW nCellRow;
+ SCTAB nCurrentTable;
+
+ void UpdateAddress( ScAddress& rAddress );
+ void SetCellData( ScMyCell& rMyCell, const ScAddress& rAddress );
+
+ void HasAnnotation( ScMyCell& aCell );
+public:
+ explicit ScMyNotEmptyCellsIterator(ScXMLExport& rExport);
+ ~ScMyNotEmptyCellsIterator();
+
+ void Clear();
+
+ void SetShapes(ScMyShapesContainer* pNewShapes)
+ { pShapes = pNewShapes; }
+ void SetNoteShapes(ScMyNoteShapesContainer* pNewNoteShapes)
+ { pNoteShapes = pNewNoteShapes; }
+ void SetEmptyDatabaseRanges(ScMyEmptyDatabaseRangesContainer* pNewEmptyDatabaseRanges)
+ { pEmptyDatabaseRanges = pNewEmptyDatabaseRanges; }
+ void SetMergedRanges(ScMyMergedRangesContainer* pNewMergedRanges)
+ { pMergedRanges = pNewMergedRanges; }
+ void SetAreaLinks(ScMyAreaLinksContainer* pNewAreaLinks)
+ { pAreaLinks = pNewAreaLinks; }
+ void SetDetectiveObj(ScMyDetectiveObjContainer* pNewDetectiveObj)
+ { pDetectiveObj = pNewDetectiveObj; }
+ void SetDetectiveOp(ScMyDetectiveOpContainer* pNewDetectiveOp)
+ { pDetectiveOp = pNewDetectiveOp; }
+
+ void SetCurrentTable(const SCTAB nTable,
+ const css::uno::Reference<css::sheet::XSpreadsheet>& rxTable);
+ void SkipTable(SCTAB nSkip);
+
+ bool GetNext(ScMyCell& aCell, ScFormatRangeStyles* pCellStyles);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportSharedData.cxx b/sc/source/filter/xml/XMLExportSharedData.cxx
new file mode 100644
index 0000000000..b193beb9f9
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportSharedData.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLExportSharedData.hxx"
+#include "XMLExportIterator.hxx"
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+ScMySharedData::ScMySharedData(const sal_Int32 nTempTableCount) :
+ nLastColumns(nTempTableCount, 0),
+ nLastRows(nTempTableCount, 0),
+ pDetectiveObjContainer(new ScMyDetectiveObjContainer()),
+ nTableCount(nTempTableCount)
+{
+}
+
+ScMySharedData::~ScMySharedData()
+{
+ pShapesContainer.reset();
+ pTableShapes.reset();
+ pDrawPages.reset();
+ pDetectiveObjContainer.reset();
+ pNoteShapes.reset();
+}
+
+void ScMySharedData::SetLastColumn(const sal_Int32 nTable, const sal_Int32 nCol)
+{
+ if(nCol > nLastColumns[nTable]) nLastColumns[nTable] = nCol;
+}
+
+sal_Int32 ScMySharedData::GetLastColumn(const sal_Int32 nTable) const
+{
+ return nLastColumns[nTable];
+}
+
+void ScMySharedData::SetLastRow(const sal_Int32 nTable, const sal_Int32 nRow)
+{
+ if(nRow > nLastRows[nTable]) nLastRows[nTable] = nRow;
+}
+
+sal_Int32 ScMySharedData::GetLastRow(const sal_Int32 nTable) const
+{
+ return nLastRows[nTable];
+}
+
+void ScMySharedData::AddDrawPage(const ScMyDrawPage& aDrawPage, const sal_Int32 nTable)
+{
+ if (!pDrawPages)
+ pDrawPages.reset(new ScMyDrawPages(nTableCount, ScMyDrawPage()));
+ (*pDrawPages)[nTable] = aDrawPage;
+}
+
+void ScMySharedData::SetDrawPageHasForms(const sal_Int32 nTable, bool bHasForms)
+{
+ OSL_ENSURE(pDrawPages, "DrawPages not collected");
+ if (pDrawPages)
+ (*pDrawPages)[nTable].bHasForms = bHasForms;
+}
+
+uno::Reference<drawing::XDrawPage> ScMySharedData::GetDrawPage(const sal_Int32 nTable)
+{
+ OSL_ENSURE(pDrawPages, "DrawPages not collected");
+ if (pDrawPages)
+ return (*pDrawPages)[nTable].xDrawPage;
+ else
+ return uno::Reference<drawing::XDrawPage>();
+}
+
+bool ScMySharedData::HasForm(const sal_Int32 nTable, uno::Reference<drawing::XDrawPage>& xDrawPage)
+{
+ bool bResult(false);
+ if (pDrawPages)
+ {
+ if ((*pDrawPages)[nTable].bHasForms)
+ {
+ bResult = true;
+ xDrawPage = (*pDrawPages)[nTable].xDrawPage;
+ }
+ }
+ return bResult;
+}
+
+void ScMySharedData::AddNewShape(const ScMyShape& aMyShape)
+{
+ if (!pShapesContainer)
+ pShapesContainer.reset(new ScMyShapesContainer());
+ pShapesContainer->AddNewShape(aMyShape);
+}
+
+void ScMySharedData::SortShapesContainer()
+{
+ if (pShapesContainer)
+ pShapesContainer->Sort();
+}
+
+void ScMySharedData::AddTableShape(const sal_Int32 nTable, const uno::Reference<drawing::XShape>& xShape)
+{
+ if (!pTableShapes)
+ pTableShapes.reset(new ScMyTableShapes(nTableCount));
+ (*pTableShapes)[nTable].push_back(xShape);
+}
+
+void ScMySharedData::AddNoteObj(const uno::Reference<drawing::XShape>& xShape, const ScAddress& rPos)
+{
+ if (!pNoteShapes)
+ pNoteShapes.reset( new ScMyNoteShapesContainer() );
+ ScMyNoteShape aNote;
+ aNote.xShape = xShape;
+ aNote.aPos = rPos;
+ pNoteShapes->AddNewNote(aNote);
+}
+
+void ScMySharedData::SortNoteShapes()
+{
+ if (pNoteShapes)
+ pNoteShapes->Sort();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportSharedData.hxx b/sc/source/filter/xml/XMLExportSharedData.hxx
new file mode 100644
index 0000000000..7e9e05cc65
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportSharedData.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 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <vector>
+#include <list>
+#include <memory>
+
+namespace com::sun::star::drawing { class XDrawPage; }
+namespace com::sun::star::drawing { class XShape; }
+
+struct ScMyDrawPage
+{
+ css::uno::Reference<css::drawing::XDrawPage> xDrawPage;
+ bool bHasForms;
+
+ ScMyDrawPage() : bHasForms(false) {}
+};
+
+typedef std::list< css::uno::Reference<css::drawing::XShape> > ScMyTableXShapes;
+typedef std::vector<ScMyTableXShapes> ScMyTableShapes;
+typedef std::vector<ScMyDrawPage> ScMyDrawPages;
+
+class ScMyShapesContainer;
+class ScMyDetectiveObjContainer;
+struct ScMyShape;
+class ScMyNoteShapesContainer;
+
+class ScMySharedData
+{
+ std::vector<sal_Int32> nLastColumns;
+ std::vector<sal_Int32> nLastRows;
+ std::unique_ptr<ScMyTableShapes> pTableShapes;
+ std::unique_ptr<ScMyDrawPages> pDrawPages;
+ std::unique_ptr<ScMyShapesContainer> pShapesContainer;
+ std::unique_ptr<ScMyDetectiveObjContainer> pDetectiveObjContainer;
+ std::unique_ptr<ScMyNoteShapesContainer> pNoteShapes;
+ sal_Int32 nTableCount;
+public:
+ explicit ScMySharedData(const sal_Int32 nTableCount);
+ ~ScMySharedData();
+
+ void SetLastColumn(const sal_Int32 nTable, const sal_Int32 nCol);
+ void SetLastRow(const sal_Int32 nTable, const sal_Int32 nRow);
+ sal_Int32 GetLastColumn(const sal_Int32 nTable) const;
+ sal_Int32 GetLastRow(const sal_Int32 nTable) const;
+ void AddDrawPage(const ScMyDrawPage& aDrawPage, const sal_Int32 nTable);
+ void SetDrawPageHasForms(const sal_Int32 nTable, bool bHasForms);
+ css::uno::Reference<css::drawing::XDrawPage> GetDrawPage(const sal_Int32 nTable);
+ bool HasDrawPage() const { return pDrawPages != nullptr; }
+ bool HasForm(const sal_Int32 nTable, css::uno::Reference<css::drawing::XDrawPage>& xDrawPage);
+ void AddNewShape(const ScMyShape& aMyShape);
+ void SortShapesContainer();
+ ScMyShapesContainer* GetShapesContainer() { return pShapesContainer.get(); }
+ void AddTableShape(const sal_Int32 nTable, const css::uno::Reference<css::drawing::XShape>& xShape);
+ ScMyTableShapes* GetTableShapes() { return pTableShapes.get(); }
+ ScMyDetectiveObjContainer* GetDetectiveObjContainer() { return pDetectiveObjContainer.get(); }
+ void AddNoteObj(const css::uno::Reference<css::drawing::XShape>& xShape, const ScAddress& rPos);
+ void SortNoteShapes();
+ ScMyNoteShapesContainer* GetNoteShapes() { return pNoteShapes.get(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesExportHelper.cxx b/sc/source/filter/xml/XMLStylesExportHelper.cxx
new file mode 100644
index 0000000000..0708bfd6c2
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesExportHelper.cxx
@@ -0,0 +1,1066 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLStylesExportHelper.hxx"
+#include <tools/lineend.hxx>
+#include <unonames.hxx>
+#include "xmlexprt.hxx"
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/XMLEventExport.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <comphelper/extract.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <sfx2/app.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScMyValidation::ScMyValidation()
+ : aAlertStyle(sheet::ValidationAlertStyle_STOP),
+ aValidationType(sheet::ValidationType_ANY),
+ aOperator(sheet::ConditionOperator_NONE),
+ nShowList(0),
+ bShowErrorMessage(false),
+ bShowInputMessage(false),
+ bIgnoreBlanks(false)
+{
+}
+
+bool ScMyValidation::IsEqual(const ScMyValidation& aVal) const
+{
+ return aVal.bIgnoreBlanks == bIgnoreBlanks &&
+ aVal.bShowInputMessage == bShowInputMessage &&
+ aVal.bShowErrorMessage == bShowErrorMessage &&
+ aVal.aBaseCell == aBaseCell &&
+ aVal.aAlertStyle == aAlertStyle &&
+ aVal.aValidationType == aValidationType &&
+ aVal.aOperator == aOperator &&
+ aVal.sErrorTitle == sErrorTitle &&
+ aVal.sInputTitle == sInputTitle &&
+ aVal.sErrorMessage == sErrorMessage &&
+ aVal.sInputMessage == sInputMessage &&
+ aVal.sFormula1 == sFormula1 &&
+ aVal.sFormula2 == sFormula2;
+}
+
+ScMyValidationsContainer::ScMyValidationsContainer()
+{
+}
+
+ScMyValidationsContainer::~ScMyValidationsContainer()
+{
+}
+
+void ScMyValidationsContainer::AddValidation(const uno::Any& aTempAny,
+ sal_Int32& nValidationIndex)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(aTempAny, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ OUString sErrorMessage;
+ xPropertySet->getPropertyValue(SC_UNONAME_ERRMESS) >>= sErrorMessage;
+ OUString sErrorTitle;
+ xPropertySet->getPropertyValue(SC_UNONAME_ERRTITLE) >>= sErrorTitle;
+ OUString sInputMessage;
+ xPropertySet->getPropertyValue(SC_UNONAME_INPMESS) >>= sInputMessage;
+ OUString sInputTitle;
+ xPropertySet->getPropertyValue(SC_UNONAME_INPTITLE) >>= sInputTitle;
+ bool bShowErrorMessage = ::cppu::any2bool(xPropertySet->getPropertyValue(SC_UNONAME_SHOWERR));
+ bool bShowInputMessage = ::cppu::any2bool(xPropertySet->getPropertyValue(SC_UNONAME_SHOWINP));
+ sheet::ValidationType aValidationType;
+ xPropertySet->getPropertyValue(SC_UNONAME_TYPE) >>= aValidationType;
+ if (!bShowErrorMessage && !bShowInputMessage && aValidationType == sheet::ValidationType_ANY &&
+ sErrorMessage.isEmpty() && sErrorTitle.isEmpty() && sInputMessage.isEmpty() && sInputTitle.isEmpty())
+ return;
+
+ ScMyValidation aValidation;
+ aValidation.sErrorMessage = sErrorMessage;
+ aValidation.sErrorTitle = sErrorTitle;
+ aValidation.sInputMessage = sInputMessage;
+ aValidation.sInputTitle = sInputTitle;
+ aValidation.bShowErrorMessage = bShowErrorMessage;
+ aValidation.bShowInputMessage = bShowInputMessage;
+ aValidation.aValidationType = aValidationType;
+ aValidation.bIgnoreBlanks = ::cppu::any2bool(xPropertySet->getPropertyValue(SC_UNONAME_IGNOREBL));
+ xPropertySet->getPropertyValue(SC_UNONAME_SHOWLIST) >>= aValidation.nShowList;
+ xPropertySet->getPropertyValue(SC_UNONAME_ERRALSTY) >>= aValidation.aAlertStyle;
+ uno::Reference<sheet::XSheetCondition> xCondition(xPropertySet, uno::UNO_QUERY);
+ if (xCondition.is())
+ {
+ aValidation.sFormula1 = xCondition->getFormula1();
+ aValidation.sFormula2 = xCondition->getFormula2();
+ aValidation.aOperator = xCondition->getOperator();
+ table::CellAddress aCellAddress= xCondition->getSourcePosition();
+ aValidation.aBaseCell = ScAddress( static_cast<SCCOL>(aCellAddress.Column), static_cast<SCROW>(aCellAddress.Row), aCellAddress.Sheet );
+ }
+ //ScMyValidationRange aValidationRange;
+ bool bEqualFound(false);
+ sal_Int32 i(0);
+ sal_Int32 nCount(aValidationVec.size());
+ while (i < nCount && !bEqualFound)
+ {
+ bEqualFound = aValidationVec[i].IsEqual(aValidation);
+ if (!bEqualFound)
+ ++i;
+ }
+ if (bEqualFound)
+ nValidationIndex = i;
+ else
+ {
+ sal_Int32 nNameIndex(nCount + 1);
+ OUString sCount(OUString::number(nNameIndex));
+ aValidation.sName += "val";
+ aValidation.sName += sCount;
+ aValidationVec.push_back(aValidation);
+ nValidationIndex = nCount;
+ }
+}
+
+OUString ScMyValidationsContainer::GetCondition(ScXMLExport& rExport, const ScMyValidation& aValidation)
+{
+ /* ATTENTION! Should the condition to not write sheet::ValidationType_ANY
+ * ever be changed, adapt the conditional call of
+ * MarkUsedExternalReferences() in
+ * ScTableValidationObj::ScTableValidationObj() accordingly! */
+ OUString sCondition;
+ if (aValidation.aValidationType != sheet::ValidationType_ANY)
+ {
+ switch (aValidation.aValidationType)
+ {
+ //case sheet::ValidationType_CUSTOM
+ case sheet::ValidationType_DATE :
+ sCondition += "cell-content-is-date()";
+ break;
+ case sheet::ValidationType_DECIMAL :
+ sCondition += "cell-content-is-decimal-number()";
+ break;
+ case sheet::ValidationType_LIST :
+ sCondition += "cell-content-is-in-list(" + aValidation.sFormula1 + ")";
+ break;
+ case sheet::ValidationType_TEXT_LEN :
+ if (aValidation.aOperator != sheet::ConditionOperator_BETWEEN &&
+ aValidation.aOperator != sheet::ConditionOperator_NOT_BETWEEN)
+ sCondition += "cell-content-text-length()";
+ break;
+ case sheet::ValidationType_TIME :
+ sCondition += "cell-content-is-time()";
+ break;
+ case sheet::ValidationType_WHOLE :
+ sCondition += "cell-content-is-whole-number()";
+ break;
+ case sheet::ValidationType_CUSTOM :
+ sCondition += "is-true-formula(" + aValidation.sFormula1 + ")";
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ if (aValidation.aValidationType != sheet::ValidationType_LIST &&
+ aValidation.aValidationType != sheet::ValidationType_CUSTOM &&
+ (!aValidation.sFormula1.isEmpty() ||
+ ((aValidation.aOperator == sheet::ConditionOperator_BETWEEN ||
+ aValidation.aOperator == sheet::ConditionOperator_NOT_BETWEEN) &&
+ !aValidation.sFormula2.isEmpty())))
+ {
+ if (aValidation.aValidationType != sheet::ValidationType_TEXT_LEN)
+ sCondition += " and ";
+ if (aValidation.aOperator != sheet::ConditionOperator_BETWEEN &&
+ aValidation.aOperator != sheet::ConditionOperator_NOT_BETWEEN)
+ {
+ if (aValidation.aValidationType != sheet::ValidationType_TEXT_LEN)
+ sCondition += "cell-content()";
+ switch (aValidation.aOperator)
+ {
+ case sheet::ConditionOperator_EQUAL :
+ sCondition += "=";
+ break;
+ case sheet::ConditionOperator_GREATER :
+ sCondition += ">";
+ break;
+ case sheet::ConditionOperator_GREATER_EQUAL :
+ sCondition += ">=";
+ break;
+ case sheet::ConditionOperator_LESS :
+ sCondition += "<";
+ break;
+ case sheet::ConditionOperator_LESS_EQUAL :
+ sCondition += "<=";
+ break;
+ case sheet::ConditionOperator_NOT_EQUAL :
+ sCondition += "!=";
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ sCondition += aValidation.sFormula1;
+ }
+ else
+ {
+ if (aValidation.aValidationType == sheet::ValidationType_TEXT_LEN)
+ {
+ if (aValidation.aOperator == sheet::ConditionOperator_BETWEEN)
+ sCondition += "cell-content-text-length-is-between(";
+ else
+ sCondition += "cell-content-text-length-is-not-between(";
+ }
+ else
+ {
+ if (aValidation.aOperator == sheet::ConditionOperator_BETWEEN)
+ sCondition += "cell-content-is-between(";
+ else
+ sCondition += "cell-content-is-not-between(";
+ }
+ sCondition += aValidation.sFormula1 + "," + aValidation.sFormula2 + ")";
+ }
+ }
+ else
+ if (aValidation.aValidationType == sheet::ValidationType_TEXT_LEN)
+ sCondition.clear();
+ }
+ if (!sCondition.isEmpty())
+ {
+ const formula::FormulaGrammar::Grammar eGrammar = rExport.GetDocument()->GetStorageGrammar();
+ sal_uInt16 nNamespacePrefix = (eGrammar == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
+ sCondition = rExport.GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sCondition, false );
+ }
+
+ return sCondition;
+}
+
+OUString ScMyValidationsContainer::GetBaseCellAddress(const ScDocument* pDoc, const ScAddress& aCell)
+{
+ OUString sAddress;
+ ScRangeStringConverter::GetStringFromAddress( sAddress, aCell, pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ return sAddress;
+}
+
+void ScMyValidationsContainer::WriteMessage(ScXMLExport& rExport,
+ const OUString& sTitle, const OUString& sOUMessage,
+ const bool bShowMessage, const bool bIsHelpMessage)
+{
+ if (!sTitle.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TITLE, sTitle);
+ if (bShowMessage)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_FALSE);
+ std::unique_ptr<SvXMLElementExport> pMessage;
+ if (bIsHelpMessage)
+ pMessage.reset(new SvXMLElementExport(rExport, XML_NAMESPACE_TABLE, XML_HELP_MESSAGE, true, true));
+ else
+ pMessage.reset(new SvXMLElementExport(rExport, XML_NAMESPACE_TABLE, XML_ERROR_MESSAGE, true, true));
+ if (sOUMessage.isEmpty())
+ return;
+
+ sal_Int32 i(0);
+ OUStringBuffer sTemp;
+ OUString sText(convertLineEnd(sOUMessage, LINEEND_LF));
+ bool bPrevCharWasSpace(true);
+ while(i < sText.getLength())
+ {
+ if( sText[i] == '\n')
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ rExport.GetTextParagraphExport()->exportCharacterData(sTemp.makeStringAndClear(), bPrevCharWasSpace);
+ bPrevCharWasSpace = true; // reset for start of next paragraph
+ }
+ else
+ sTemp.append(sText[i]);
+ ++i;
+ }
+ if (!sTemp.isEmpty())
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ rExport.GetTextParagraphExport()->exportCharacterData(sTemp.makeStringAndClear(), bPrevCharWasSpace);
+ }
+}
+
+void ScMyValidationsContainer::WriteValidations(ScXMLExport& rExport)
+{
+ if (aValidationVec.empty())
+ return;
+
+ SvXMLElementExport aElemVs(rExport, XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATIONS, true, true);
+ for (const auto& rValidation : aValidationVec)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rValidation.sName);
+ OUString sCondition(GetCondition(rExport, rValidation));
+ if (!sCondition.isEmpty())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONDITION, sCondition);
+ if (rValidation.bIgnoreBlanks)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ALLOW_EMPTY_CELL, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ALLOW_EMPTY_CELL, XML_FALSE);
+ if (rValidation.aValidationType == sheet::ValidationType_LIST)
+ {
+ switch (rValidation.nShowList)
+ {
+ case sheet::TableValidationVisibility::INVISIBLE:
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_LIST, XML_NO);
+ break;
+ case sheet::TableValidationVisibility::UNSORTED:
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_LIST, XML_UNSORTED);
+ break;
+ case sheet::TableValidationVisibility::SORTEDASCENDING:
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_LIST, XML_SORT_ASCENDING);
+ break;
+ default:
+ OSL_FAIL("unknown ListType");
+ }
+ }
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BASE_CELL_ADDRESS, GetBaseCellAddress(rExport.GetDocument(), rValidation.aBaseCell));
+ SvXMLElementExport aElemV(rExport, XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION, true, true);
+ if (rValidation.bShowInputMessage || !rValidation.sInputMessage.isEmpty() || !rValidation.sInputTitle.isEmpty())
+ {
+ WriteMessage(rExport, rValidation.sInputTitle, rValidation.sInputMessage, rValidation.bShowInputMessage, true);
+ }
+ if (rValidation.bShowErrorMessage || !rValidation.sErrorMessage.isEmpty() || !rValidation.sErrorTitle.isEmpty())
+ {
+ switch (rValidation.aAlertStyle)
+ {
+ case sheet::ValidationAlertStyle_INFO :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MESSAGE_TYPE, XML_INFORMATION);
+ WriteMessage(rExport, rValidation.sErrorTitle, rValidation.sErrorMessage, rValidation.bShowErrorMessage, false);
+ }
+ break;
+ case sheet::ValidationAlertStyle_WARNING :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MESSAGE_TYPE, XML_WARNING);
+ WriteMessage(rExport, rValidation.sErrorTitle, rValidation.sErrorMessage, rValidation.bShowErrorMessage, false);
+ }
+ break;
+ case sheet::ValidationAlertStyle_STOP :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MESSAGE_TYPE, XML_STOP);
+ WriteMessage(rExport, rValidation.sErrorTitle, rValidation.sErrorMessage, rValidation.bShowErrorMessage, false);
+ }
+ break;
+ case sheet::ValidationAlertStyle_MACRO :
+ {
+ {
+ //rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, aItr->sErrorTitle);
+ if (rValidation.bShowErrorMessage)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_EXECUTE, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_EXECUTE, XML_FALSE);
+ SvXMLElementExport aEMElem(rExport, XML_NAMESPACE_TABLE, XML_ERROR_MACRO, true, true);
+ }
+ {
+ // #i47525# for a script URL the type and the property name for the URL
+ // are both "Script", for a simple macro name the type is "StarBasic"
+ // and the property name is "MacroName".
+ bool bScriptURL = SfxApplication::IsXScriptURL( rValidation.sErrorTitle );
+
+ static constexpr OUString sScript(u"Script"_ustr);
+ uno::Sequence<beans::PropertyValue> aSeq( comphelper::InitPropertySequence({
+ { "EventType", uno::Any(bScriptURL ? sScript : OUString("StarBasic")) },
+ { "Library", uno::Any(OUString()) },
+ { bScriptURL ? sScript : OUString("MacroName"), uno::Any(rValidation.sErrorTitle) }
+ }));
+ // 2) export the sequence
+ rExport.GetEventExport().ExportSingleEvent( aSeq, "OnError");
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+}
+
+const OUString& ScMyValidationsContainer::GetValidationName(const sal_Int32 nIndex)
+{
+ OSL_ENSURE( o3tl::make_unsigned(nIndex) < aValidationVec.size(), "out of range" );
+ return aValidationVec[nIndex].sName;
+}
+
+sal_Int32 ScMyDefaultStyles::GetStyleNameIndex(const ScFormatRangeStyles* pCellStyles,
+ const sal_Int32 nTable, const sal_Int32 nPos,
+ const sal_Int32 i, bool& bIsAutoStyle)
+{
+ return pCellStyles->GetStyleNameIndex(nTable, i, nPos, bIsAutoStyle);
+}
+
+void ScMyDefaultStyles::FillDefaultStyles(const sal_Int32 nTable,
+ const sal_Int32 nLastRow, const sal_Int32 nLastCol,
+ const ScFormatRangeStyles* pCellStyles, ScDocument* pDoc)
+{
+ maColDefaults.clear();
+ maColDefaults.resize(nLastCol + 1);
+ if (!pDoc)
+ return ;
+
+ SCTAB nTab = static_cast<SCTAB>(nTable);
+ pDoc->CreateColumnIfNotExists(nTab, nLastCol);
+ sal_Int32 nPos;
+ ScMyDefaultStyleList* pDefaults = &maColDefaults;
+ bool bPrevAutoStyle(false);
+ bool bIsAutoStyle;
+ sal_Int32 nPrevIndex(0);
+ sal_Int32 nRepeat(0);
+ for (sal_Int32 i = nLastCol; i >= 0; --i)
+ {
+ pDoc->GetColDefault(nTab, static_cast<SCCOL>(i), static_cast<SCROW>(nLastRow), nPos);
+ if (!nRepeat)
+ {
+ nPrevIndex = GetStyleNameIndex(pCellStyles, nTab, nPos, i, bPrevAutoStyle);
+ (*pDefaults)[i].nIndex = nPrevIndex;
+ (*pDefaults)[i].bIsAutoStyle = bPrevAutoStyle;
+ nRepeat = 1;
+ }
+ else
+ {
+ sal_Int32 nIndex = GetStyleNameIndex(pCellStyles, nTab, nPos, i, bIsAutoStyle);
+ if ((nIndex != nPrevIndex) || (bIsAutoStyle != bPrevAutoStyle))
+ {
+ nRepeat = 1;
+ nPrevIndex = GetStyleNameIndex(pCellStyles, nTab, nPos, i, bPrevAutoStyle);
+ (*pDefaults)[i].nIndex = nPrevIndex;
+ (*pDefaults)[i].bIsAutoStyle = bPrevAutoStyle;
+ }
+ else
+ {
+ (*pDefaults)[i].nIndex = nPrevIndex;
+ (*pDefaults)[i].bIsAutoStyle = bPrevAutoStyle;
+ ++nRepeat;
+ if (nRepeat > 1)
+ (*pDefaults)[i].nRepeat = nRepeat;
+ }
+ }
+ }
+}
+
+ScMyRowFormatRange::ScMyRowFormatRange()
+ : nStartColumn(0),
+ nRepeatColumns(0),
+ nRepeatRows(0),
+ nIndex(-1),
+ nValidationIndex(-1),
+ bIsAutoStyle(true)
+{
+}
+
+bool ScMyRowFormatRange::operator< (const ScMyRowFormatRange& rRange) const
+{
+ return (nStartColumn < rRange.nStartColumn);
+}
+
+ScRowFormatRanges::ScRowFormatRanges()
+ : pColDefaults(nullptr),
+ nSize(0)
+{
+}
+
+ScRowFormatRanges::ScRowFormatRanges(const ScRowFormatRanges* pRanges)
+ : aRowFormatRanges(pRanges->aRowFormatRanges),
+ pColDefaults(pRanges->pColDefaults),
+ nSize(pRanges->nSize)
+{
+}
+
+ScRowFormatRanges::~ScRowFormatRanges()
+{
+}
+
+void ScRowFormatRanges::Clear()
+{
+ aRowFormatRanges.clear();
+ nSize = 0;
+}
+
+void ScRowFormatRanges::AddRange(const sal_Int32 nPrevStartCol, const sal_Int32 nRepeat, const sal_Int32 nPrevIndex,
+ const bool bPrevAutoStyle, const ScMyRowFormatRange& rFormatRange)
+{
+ sal_Int32 nIndex(-1);
+ if ((nPrevIndex != rFormatRange.nIndex) ||
+ (bPrevAutoStyle != rFormatRange.bIsAutoStyle))
+ nIndex = rFormatRange.nIndex;
+
+ bool bInserted(false);
+ if (!aRowFormatRanges.empty())
+ {
+ ScMyRowFormatRange& rRange(aRowFormatRanges.back());
+ if ((nPrevStartCol == (rRange.nStartColumn + rRange.nRepeatColumns))
+ && (rRange.bIsAutoStyle == rFormatRange.bIsAutoStyle) && (rRange.nIndex == nIndex)
+ && (rRange.nValidationIndex == rFormatRange.nValidationIndex))
+ {
+ if (rFormatRange.nRepeatRows < rRange.nRepeatRows)
+ rRange.nRepeatRows = rFormatRange.nRepeatRows;
+ rRange.nRepeatColumns += nRepeat;
+ bInserted = true;
+ }
+ }
+ if (!bInserted)
+ {
+ ScMyRowFormatRange aRange;
+ aRange.nStartColumn = nPrevStartCol;
+ aRange.nRepeatColumns = nRepeat;
+ aRange.nRepeatRows = rFormatRange.nRepeatRows;
+ aRange.nValidationIndex = rFormatRange.nValidationIndex;
+ aRange.bIsAutoStyle = rFormatRange.bIsAutoStyle;
+ aRange.nIndex = nIndex;
+ aRowFormatRanges.push_back(aRange);
+ ++nSize;
+ }
+}
+
+void ScRowFormatRanges::AddRange(const ScMyRowFormatRange& rFormatRange)
+{
+ OSL_ENSURE(pColDefaults, "no column defaults");
+ if (!pColDefaults)
+ return;
+ sal_Int32 nPrevIndex = -1;
+ bool bPrevAutoStyle = true;
+
+ sal_uInt32 nPrevStartCol(rFormatRange.nStartColumn);
+ OSL_ENSURE( static_cast<size_t>(nPrevStartCol) < pColDefaults->size(), "nPrevStartCol out of bounds");
+ sal_uInt32 nRepeat;
+ if (static_cast<size_t>(nPrevStartCol) < pColDefaults->size())
+ {
+ nRepeat = (*pColDefaults)[nPrevStartCol].nRepeat;
+ nPrevIndex = (*pColDefaults)[nPrevStartCol].nIndex;
+ bPrevAutoStyle = (*pColDefaults)[nPrevStartCol].bIsAutoStyle;
+ }
+ else
+ {
+ /* Again, this is to prevent out-of-bounds accesses, so FIXME
+ * elsewhere! */
+ if (pColDefaults->empty())
+ {
+ nRepeat = 1;
+ nPrevIndex = -1;
+ bPrevAutoStyle = false;
+ }
+ else
+ {
+ nRepeat = (*pColDefaults)[pColDefaults->size()-1].nRepeat;
+ nPrevIndex = (*pColDefaults)[pColDefaults->size()-1].nIndex;
+ bPrevAutoStyle = (*pColDefaults)[pColDefaults->size()-1].bIsAutoStyle;
+ }
+ }
+ sal_uInt32 nEnd = nPrevStartCol + rFormatRange.nRepeatColumns;
+ for(sal_uInt32 i = nPrevStartCol + nRepeat; i < nEnd && i < pColDefaults->size(); i += (*pColDefaults)[i].nRepeat)
+ {
+ OSL_ENSURE(sal_uInt32(nPrevStartCol + nRepeat) <= nEnd, "something went wrong");
+ if ((nPrevIndex != (*pColDefaults)[i].nIndex) ||
+ (bPrevAutoStyle != (*pColDefaults)[i].bIsAutoStyle))
+ {
+ AddRange(nPrevStartCol, nRepeat, nPrevIndex, bPrevAutoStyle, rFormatRange);
+ nPrevStartCol = i;
+ nRepeat = (*pColDefaults)[i].nRepeat;
+ nPrevIndex = (*pColDefaults)[i].nIndex;
+ bPrevAutoStyle = (*pColDefaults)[i].bIsAutoStyle;
+ }
+ else
+ nRepeat += (*pColDefaults)[i].nRepeat;
+ }
+ if (sal_uInt32(nPrevStartCol + nRepeat) > nEnd)
+ nRepeat = nEnd - nPrevStartCol;
+ AddRange(nPrevStartCol, nRepeat, nPrevIndex, bPrevAutoStyle, rFormatRange);
+
+}
+
+bool ScRowFormatRanges::GetNext(ScMyRowFormatRange& aFormatRange)
+{
+ ScMyRowFormatRangesList::iterator aItr(aRowFormatRanges.begin());
+ if (aItr != aRowFormatRanges.end())
+ {
+ aFormatRange = *aItr;
+ aRowFormatRanges.erase(aItr);
+ --nSize;
+ return true;
+ }
+ return false;
+}
+
+sal_Int32 ScRowFormatRanges::GetMaxRows() const
+{
+ sal_Int32 nMaxRows(0);
+ if (!aRowFormatRanges.empty())
+ {
+ auto aItr = std::min_element(aRowFormatRanges.begin(), aRowFormatRanges.end(),
+ [](const ScMyRowFormatRange& a, const ScMyRowFormatRange& b) { return a.nRepeatRows < b.nRepeatRows; });
+ nMaxRows = (*aItr).nRepeatRows;
+ }
+ else
+ {
+ OSL_FAIL("no ranges found");
+ }
+ return nMaxRows;
+}
+
+void ScRowFormatRanges::Sort()
+{
+ aRowFormatRanges.sort();
+}
+
+ScMyFormatRange::ScMyFormatRange()
+ : nStyleNameIndex(-1)
+ , nValidationIndex(-1)
+ , nNumberFormat(0)
+ , bIsAutoStyle(true)
+{
+}
+
+bool ScMyFormatRange::operator<(const ScMyFormatRange& rRange) const
+{
+ if (aRangeAddress.StartRow < rRange.aRangeAddress.StartRow)
+ return true;
+ else
+ if (aRangeAddress.StartRow == rRange.aRangeAddress.StartRow)
+ return (aRangeAddress.StartColumn < rRange.aRangeAddress.StartColumn);
+ else
+ return false;
+}
+
+ScFormatRangeStyles::ScFormatRangeStyles()
+ : pColDefaults(nullptr)
+{
+}
+
+ScFormatRangeStyles::~ScFormatRangeStyles()
+{
+}
+
+void ScFormatRangeStyles::AddNewTable(const sal_Int32 nTable)
+{
+ sal_Int32 nSize = aTables.size() - 1;
+ if (nTable > nSize)
+ for (sal_Int32 i = nSize; i < nTable; ++i)
+ {
+ aTables.emplace_back();
+ }
+}
+
+bool ScFormatRangeStyles::AddStyleName(OUString const & rString, sal_Int32& rIndex, const bool bIsAutoStyle)
+{
+ if (bIsAutoStyle)
+ {
+ aAutoStyleNames.push_back(rString);
+ rIndex = aAutoStyleNames.size() - 1;
+ return true;
+ }
+ else
+ {
+ sal_Int32 nCount(aStyleNames.size());
+ bool bFound(false);
+ sal_Int32 i(nCount - 1);
+ while ((i >= 0) && (!bFound))
+ {
+ if (aStyleNames.at(i) == rString)
+ bFound = true;
+ else
+ i--;
+ }
+ if (bFound)
+ {
+ rIndex = i;
+ return false;
+ }
+ else
+ {
+ aStyleNames.push_back(rString);
+ rIndex = aStyleNames.size() - 1;
+ return true;
+ }
+ }
+}
+
+sal_Int32 ScFormatRangeStyles::GetIndexOfStyleName(std::u16string_view rString, std::u16string_view rPrefix, bool& bIsAutoStyle)
+{
+ sal_Int32 nPrefixLength(rPrefix.size());
+ std::u16string_view sTemp(rString.substr(nPrefixLength));
+ sal_Int32 nIndex(o3tl::toInt32(sTemp));
+ if (nIndex > 0 && o3tl::make_unsigned(nIndex-1) < aAutoStyleNames.size() && aAutoStyleNames.at(nIndex - 1) == rString)
+ {
+ bIsAutoStyle = true;
+ return nIndex - 1;
+ }
+ else
+ {
+ sal_Int32 i(0);
+ bool bFound(false);
+ while (!bFound && o3tl::make_unsigned(i) < aStyleNames.size())
+ {
+ if (aStyleNames[i] == rString)
+ bFound = true;
+ else
+ ++i;
+ }
+ if (bFound)
+ {
+ bIsAutoStyle = false;
+ return i;
+ }
+ else
+ {
+ i = 0;
+ while (!bFound && o3tl::make_unsigned(i) < aAutoStyleNames.size())
+ {
+ if (aAutoStyleNames[i] == rString)
+ bFound = true;
+ else
+ ++i;
+ }
+ if (bFound)
+ {
+ bIsAutoStyle = true;
+ return i;
+ }
+ else
+ return -1;
+ }
+ }
+}
+
+sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable,
+ const sal_Int32 nColumn, const sal_Int32 nRow, bool& bIsAutoStyle) const
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ bIsAutoStyle = false;
+ if (o3tl::make_unsigned(nTable) >= aTables.size())
+ return -1;
+ for (const ScMyFormatRange & rFormatRange : aTables[nTable])
+ {
+ if ((rFormatRange.aRangeAddress.StartColumn <= nColumn) &&
+ (rFormatRange.aRangeAddress.EndColumn >= nColumn) &&
+ (rFormatRange.aRangeAddress.StartRow <= nRow) &&
+ (rFormatRange.aRangeAddress.EndRow >= nRow))
+ {
+ bIsAutoStyle = rFormatRange.bIsAutoStyle;
+ return rFormatRange.nStyleNameIndex;
+ }
+ }
+ return -1;
+}
+
+sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
+ bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Int32 nRemoveBeforeRow)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ if (o3tl::make_unsigned(nTable) >= aTables.size())
+ return -1;
+ ScMyFormatRangeAddresses& rFormatRanges(aTables[nTable]);
+ ScMyFormatRangeAddresses::iterator aItr(rFormatRanges.begin());
+ ScMyFormatRangeAddresses::iterator aEndItr(rFormatRanges.end());
+ while (aItr != aEndItr)
+ {
+ if (((*aItr).aRangeAddress.StartColumn <= nColumn) &&
+ ((*aItr).aRangeAddress.EndColumn >= nColumn) &&
+ ((*aItr).aRangeAddress.StartRow <= nRow) &&
+ ((*aItr).aRangeAddress.EndRow >= nRow))
+ {
+ bIsAutoStyle = aItr->bIsAutoStyle;
+ nValidationIndex = aItr->nValidationIndex;
+ nNumberFormat = aItr->nNumberFormat;
+ OSL_ENSURE( o3tl::make_unsigned(nColumn) < pColDefaults->size(), "nColumn out of bounds");
+ if (o3tl::make_unsigned(nColumn) < pColDefaults->size() &&
+ ((*pColDefaults)[nColumn].nIndex != -1) &&
+ ((*pColDefaults)[nColumn].nIndex == (*aItr).nStyleNameIndex) &&
+ ((*pColDefaults)[nColumn].bIsAutoStyle == (*aItr).bIsAutoStyle))
+ return -1;
+ else
+ return (*aItr).nStyleNameIndex;
+ }
+ else
+ {
+ if ((*aItr).aRangeAddress.EndRow < nRemoveBeforeRow)
+ aItr = rFormatRanges.erase(aItr);
+ else
+ ++aItr;
+ }
+ }
+ return -1;
+}
+
+void ScFormatRangeStyles::GetFormatRanges(const sal_Int32 nStartColumn, const sal_Int32 nEndColumn, const sal_Int32 nRow,
+ const sal_Int32 nTable, ScRowFormatRanges* pRowFormatRanges)
+{
+ sal_Int32 nTotalColumns(nEndColumn - nStartColumn + 1);
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ ScMyFormatRangeAddresses& rFormatRanges(aTables[nTable]);
+ ScMyFormatRangeAddresses::iterator aItr(rFormatRanges.begin());
+ ScMyFormatRangeAddresses::iterator aEndItr(rFormatRanges.end());
+ sal_Int32 nColumns = 0;
+ while (aItr != aEndItr && nColumns < nTotalColumns)
+ {
+ if (((*aItr).aRangeAddress.StartRow <= nRow) &&
+ ((*aItr).aRangeAddress.EndRow >= nRow))
+ {
+ if ((((*aItr).aRangeAddress.StartColumn <= nStartColumn) &&
+ ((*aItr).aRangeAddress.EndColumn >= nStartColumn)) ||
+ (((*aItr).aRangeAddress.StartColumn <= nEndColumn) &&
+ ((*aItr).aRangeAddress.EndColumn >= nEndColumn)) ||
+ (((*aItr).aRangeAddress.StartColumn >= nStartColumn) &&
+ ((*aItr).aRangeAddress.EndColumn <= nEndColumn)))
+ {
+ ScMyRowFormatRange aRange;
+ aRange.nIndex = aItr->nStyleNameIndex;
+ aRange.nValidationIndex = aItr->nValidationIndex;
+ aRange.bIsAutoStyle = aItr->bIsAutoStyle;
+ if ((aItr->aRangeAddress.StartColumn < nStartColumn) &&
+ (aItr->aRangeAddress.EndColumn >= nStartColumn))
+ {
+ if (aItr->aRangeAddress.EndColumn >= nEndColumn)
+ aRange.nRepeatColumns = nTotalColumns;
+ else
+ aRange.nRepeatColumns = aItr->aRangeAddress.EndColumn - nStartColumn + 1;
+ aRange.nStartColumn = nStartColumn;
+ }
+ else if ((aItr->aRangeAddress.StartColumn >= nStartColumn) &&
+ (aItr->aRangeAddress.EndColumn <= nEndColumn))
+ {
+ aRange.nRepeatColumns = aItr->aRangeAddress.EndColumn - aItr->aRangeAddress.StartColumn + 1;
+ aRange.nStartColumn = aItr->aRangeAddress.StartColumn;
+ }
+ else if ((aItr->aRangeAddress.StartColumn >= nStartColumn) &&
+ (aItr->aRangeAddress.StartColumn <= nEndColumn) &&
+ (aItr->aRangeAddress.EndColumn > nEndColumn))
+ {
+ aRange.nRepeatColumns = nEndColumn - aItr->aRangeAddress.StartColumn + 1;
+ aRange.nStartColumn = aItr->aRangeAddress.StartColumn;
+ }
+ aRange.nRepeatRows = aItr->aRangeAddress.EndRow - nRow + 1;
+ pRowFormatRanges->AddRange(aRange);
+ nColumns += aRange.nRepeatColumns;
+ }
+ ++aItr;
+ }
+ else
+ if(aItr->aRangeAddress.EndRow < nRow)
+ aItr = rFormatRanges.erase(aItr);
+ else
+ ++aItr;
+ }
+ pRowFormatRanges->Sort();
+}
+
+void ScFormatRangeStyles::AddRangeStyleName(const table::CellRangeAddress& rCellRangeAddress,
+ const sal_Int32 nStringIndex, const bool bIsAutoStyle, const sal_Int32 nValidationIndex,
+ const sal_Int32 nNumberFormat)
+{
+ ScMyFormatRange aFormatRange;
+ aFormatRange.aRangeAddress = rCellRangeAddress;
+ aFormatRange.nStyleNameIndex = nStringIndex;
+ aFormatRange.nValidationIndex = nValidationIndex;
+ aFormatRange.nNumberFormat = nNumberFormat;
+ aFormatRange.bIsAutoStyle = bIsAutoStyle;
+ OSL_ENSURE(o3tl::make_unsigned(rCellRangeAddress.Sheet) < aTables.size(), "wrong table");
+ ScMyFormatRangeAddresses& rFormatRanges(aTables[rCellRangeAddress.Sheet]);
+ rFormatRanges.push_back(aFormatRange);
+}
+
+OUString & ScFormatRangeStyles::GetStyleNameByIndex(const sal_Int32 nIndex, const bool bIsAutoStyle)
+{
+ if (bIsAutoStyle)
+ return aAutoStyleNames[nIndex];
+ else
+ return aStyleNames[nIndex];
+}
+
+void ScFormatRangeStyles::Sort()
+{
+ for (auto & rTable : aTables)
+ rTable.sort();
+}
+
+ScColumnRowStylesBase::ScColumnRowStylesBase()
+{
+}
+
+ScColumnRowStylesBase::~ScColumnRowStylesBase()
+{
+}
+
+sal_Int32 ScColumnRowStylesBase::AddStyleName(const OUString & rString)
+{
+ aStyleNames.push_back(rString);
+ return aStyleNames.size() - 1;
+}
+
+sal_Int32 ScColumnRowStylesBase::GetIndexOfStyleName(std::u16string_view rString, std::u16string_view rPrefix)
+{
+ sal_Int32 nPrefixLength(rPrefix.size());
+ std::u16string_view sTemp(rString.substr(nPrefixLength));
+ sal_Int32 nIndex(o3tl::toInt32(sTemp));
+ if (nIndex > 0 && o3tl::make_unsigned(nIndex-1) < aStyleNames.size() && aStyleNames.at(nIndex - 1) == rString)
+ return nIndex - 1;
+ else
+ {
+ sal_Int32 i(0);
+ bool bFound(false);
+ while (!bFound && o3tl::make_unsigned(i) < aStyleNames.size())
+ {
+ if (aStyleNames.at(i) == rString)
+ bFound = true;
+ else
+ ++i;
+ }
+ if (bFound)
+ return i;
+ else
+ return -1;
+ }
+}
+
+OUString& ScColumnRowStylesBase::GetStyleNameByIndex(const sal_Int32 nIndex)
+{
+ return aStyleNames[nIndex];
+}
+
+ScColumnStyles::ScColumnStyles()
+{
+}
+
+ScColumnStyles::~ScColumnStyles()
+{
+}
+
+void ScColumnStyles::AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields)
+{
+ sal_Int32 nSize(aTables.size() - 1);
+ if (nTable > nSize)
+ for (sal_Int32 i = nSize; i < nTable; ++i)
+ {
+ ScMyColumnStyleVec aFieldsVec(nFields + 1, ScColumnStyle());
+ aTables.push_back(aFieldsVec);
+ }
+}
+
+sal_Int32 ScColumnStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField,
+ bool& bIsVisible)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ if (o3tl::make_unsigned(nField) < aTables[nTable].size())
+ {
+ bIsVisible = aTables[nTable][nField].bIsVisible;
+ return aTables[nTable][nField].nIndex;
+ }
+ else
+ {
+ bIsVisible = aTables[nTable][aTables[nTable].size() - 1].bIsVisible;
+ return aTables[nTable][aTables[nTable].size() - 1].nIndex;
+ }
+}
+
+void ScColumnStyles::AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField,
+ const sal_Int32 nStringIndex, const bool bIsVisible)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ OSL_ENSURE(aTables[nTable].size() >= o3tl::make_unsigned(nField), "wrong field");
+ ScColumnStyle aStyle;
+ aStyle.nIndex = nStringIndex;
+ aStyle.bIsVisible = bIsVisible;
+ if (aTables[nTable].size() == static_cast<sal_uInt32>(nField))
+ aTables[nTable].push_back(aStyle);
+ aTables[nTable][nField] = aStyle;
+}
+
+ScRowStyles::Cache::Cache() :
+ mnTable(-1), mnStart(-1), mnEnd(-1), mnStyle(-1) {}
+
+bool ScRowStyles::Cache::hasCache(sal_Int32 nTable, sal_Int32 nField) const
+{
+ return mnTable == nTable && mnStart <= nField && nField < mnEnd;
+}
+
+ScRowStyles::ScRowStyles()
+{
+}
+
+ScRowStyles::~ScRowStyles()
+{
+}
+
+void ScRowStyles::AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields)
+{
+ sal_Int32 nSize(aTables.size() - 1);
+ if (nTable > nSize)
+ for (sal_Int32 i = nSize; i < nTable; ++i)
+ {
+ aTables.push_back(std::make_unique<StylesType>(0, nFields+1, -1));
+ }
+}
+
+sal_Int32 ScRowStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ if (o3tl::make_unsigned(nTable) >= aTables.size())
+ return -1;
+
+ if (maCache.hasCache(nTable, nField))
+ // Cache hit !
+ return maCache.mnStyle;
+
+ StylesType& r = *aTables[nTable];
+ if (!r.is_tree_valid())
+ r.build_tree();
+ sal_Int32 nStyle(0);
+ sal_Int32 nStart(0), nEnd(0);
+ if (r.search_tree(nField, nStyle, &nStart, &nEnd).second)
+ {
+ // Cache this value for better performance.
+ maCache.mnTable = nTable;
+ maCache.mnStart = nStart;
+ maCache.mnEnd = nEnd;
+ maCache.mnStyle = nStyle;
+ return nStyle;
+ }
+
+ return -1;
+}
+
+void ScRowStyles::AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField,
+ const sal_Int32 nStringIndex)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ StylesType& r = *aTables[nTable];
+ r.insert_back(nField, nField+1, nStringIndex);
+}
+
+void ScRowStyles::AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nStartField,
+ const sal_Int32 nStringIndex, const sal_Int32 nEndField)
+{
+ OSL_ENSURE( nStartField <= nEndField, "bad field range");
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ StylesType& r = *aTables[nTable];
+ r.insert_back(nStartField, nEndField+1, nStringIndex);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesExportHelper.hxx b/sc/source/filter/xml/XMLStylesExportHelper.hxx
new file mode 100644
index 0000000000..9a8b9a240a
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesExportHelper.hxx
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <list>
+
+#include <address.hxx>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+
+#include <mdds/flat_segment_tree.hpp>
+
+class ScDocument;
+class ScXMLExport;
+
+struct ScMyValidation
+{
+ OUString sName;
+ OUString sErrorMessage;
+ OUString sErrorTitle;
+ OUString sInputMessage;
+ OUString sInputTitle;
+ OUString sFormula1;
+ OUString sFormula2;
+ ScAddress aBaseCell;
+ css::sheet::ValidationAlertStyle aAlertStyle;
+ css::sheet::ValidationType aValidationType;
+ css::sheet::ConditionOperator aOperator;
+ sal_Int16 nShowList;
+ bool bShowErrorMessage;
+ bool bShowInputMessage;
+ bool bIgnoreBlanks;
+
+ ScMyValidation();
+
+ bool IsEqual(const ScMyValidation& aVal) const;
+};
+
+class ScMyValidationsContainer
+{
+private:
+ std::vector<ScMyValidation> aValidationVec;
+public:
+ ScMyValidationsContainer();
+ ~ScMyValidationsContainer();
+ void AddValidation(const css::uno::Any& aAny,
+ sal_Int32& nValidationIndex);
+ static OUString GetCondition(ScXMLExport& rExport, const ScMyValidation& aValidation);
+ static OUString GetBaseCellAddress(const ScDocument* pDoc, const ScAddress& aCell);
+ static void WriteMessage(ScXMLExport& rExport,
+ const OUString& sTitle, const OUString& sMessage,
+ const bool bShowMessage, const bool bIsHelpMessage);
+ void WriteValidations(ScXMLExport& rExport);
+ const OUString& GetValidationName(const sal_Int32 nIndex);
+};
+
+struct ScMyDefaultStyle
+{
+ sal_Int32 nIndex;
+ sal_Int32 nRepeat;
+ bool bIsAutoStyle;
+
+ ScMyDefaultStyle() : nIndex(-1), nRepeat(1),
+ bIsAutoStyle(true) {}
+};
+
+typedef std::vector<ScMyDefaultStyle> ScMyDefaultStyleList;
+
+class ScFormatRangeStyles;
+
+class ScMyDefaultStyles
+{
+ ScMyDefaultStyleList maColDefaults;
+
+ static sal_Int32 GetStyleNameIndex(const ScFormatRangeStyles* pCellStyles,
+ const sal_Int32 nTable, const sal_Int32 nPos,
+ const sal_Int32 i, bool& bIsAutoStyle);
+public:
+
+ void FillDefaultStyles(const sal_Int32 nTable,
+ const sal_Int32 nLastRow, const sal_Int32 nLastCol,
+ const ScFormatRangeStyles* pCellStyles, ScDocument* pDoc);
+
+ const ScMyDefaultStyleList& GetColDefaults() const { return maColDefaults; }
+};
+
+struct ScMyRowFormatRange
+{
+ sal_Int32 nStartColumn;
+ sal_Int32 nRepeatColumns;
+ sal_Int32 nRepeatRows;
+ sal_Int32 nIndex;
+ sal_Int32 nValidationIndex;
+ bool bIsAutoStyle;
+
+ ScMyRowFormatRange();
+ bool operator<(const ScMyRowFormatRange& rRange) const;
+};
+
+class ScRowFormatRanges
+{
+ typedef std::list<ScMyRowFormatRange> ScMyRowFormatRangesList;
+ ScMyRowFormatRangesList aRowFormatRanges;
+ const ScMyDefaultStyleList* pColDefaults;
+ sal_uInt32 nSize;
+
+ void AddRange(const sal_Int32 nPrevStartCol, const sal_Int32 nRepeat, const sal_Int32 nPrevIndex,
+ const bool bPrevAutoStyle, const ScMyRowFormatRange& rFormatRange);
+
+public:
+ ScRowFormatRanges();
+ explicit ScRowFormatRanges(const ScRowFormatRanges* pRanges);
+ ~ScRowFormatRanges();
+
+ void SetColDefaults(const ScMyDefaultStyleList* pDefaults) { pColDefaults = pDefaults; }
+ void Clear();
+ void AddRange(const ScMyRowFormatRange& rFormatRange);
+ bool GetNext(ScMyRowFormatRange& rFormatRange);
+ sal_Int32 GetMaxRows() const;
+ sal_Int32 GetSize() const { return nSize;}
+ void Sort();
+};
+
+
+struct ScMyFormatRange
+{
+ css::table::CellRangeAddress aRangeAddress;
+ sal_Int32 nStyleNameIndex;
+ sal_Int32 nValidationIndex;
+ sal_Int32 nNumberFormat;
+ bool bIsAutoStyle;
+
+ ScMyFormatRange();
+ bool operator< (const ScMyFormatRange& rRange) const;
+};
+
+class ScFormatRangeStyles
+{
+ typedef std::list<ScMyFormatRange> ScMyFormatRangeAddresses;
+ typedef std::vector<ScMyFormatRangeAddresses> ScMyFormatRangeListVec;
+
+ ScMyFormatRangeListVec aTables;
+ std::vector<OUString> aStyleNames;
+ std::vector<OUString> aAutoStyleNames;
+ const ScMyDefaultStyleList* pColDefaults;
+
+public:
+ ScFormatRangeStyles();
+ ~ScFormatRangeStyles();
+
+ void SetColDefaults(const ScMyDefaultStyleList* pDefaults) { pColDefaults = pDefaults; }
+ void AddNewTable(const sal_Int32 nTable);
+ bool AddStyleName(const OUString& rString, sal_Int32& rIndex, const bool bIsAutoStyle = true);
+ sal_Int32 GetIndexOfStyleName(std::u16string_view rString, std::u16string_view rPrefix, bool& bIsAutoStyle);
+ // does not delete ranges
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
+ bool& bIsAutoStyle) const;
+ // deletes not necessary ranges if wanted
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
+ bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Int32 nRemoveBeforeRow);
+ void GetFormatRanges(const sal_Int32 nStartColumn, const sal_Int32 nEndColumn, const sal_Int32 nRow,
+ const sal_Int32 nTable, ScRowFormatRanges* pFormatRanges);
+ void AddRangeStyleName(const css::table::CellRangeAddress& rCellRangeAddress, const sal_Int32 nStringIndex,
+ const bool bIsAutoStyle, const sal_Int32 nValidationIndex, const sal_Int32 nNumberFormat);
+ OUString& GetStyleNameByIndex(const sal_Int32 nIndex, const bool bIsAutoStyle);
+ void Sort();
+};
+
+class ScColumnRowStylesBase
+{
+ std::vector<OUString> aStyleNames;
+
+public:
+ ScColumnRowStylesBase();
+ virtual ~ScColumnRowStylesBase();
+
+ virtual void AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields) = 0;
+ sal_Int32 AddStyleName(const OUString & rString);
+ sal_Int32 GetIndexOfStyleName(std::u16string_view rString, std::u16string_view rPrefix);
+ OUString& GetStyleNameByIndex(const sal_Int32 nIndex);
+};
+
+struct ScColumnStyle
+{
+ sal_Int32 nIndex;
+ bool bIsVisible;
+
+ ScColumnStyle() : nIndex(-1), bIsVisible(true) {}
+};
+
+class ScColumnStyles : public ScColumnRowStylesBase
+{
+ typedef std::vector<ScColumnStyle> ScMyColumnStyleVec;
+ typedef std::vector<ScMyColumnStyleVec> ScMyColumnVectorVec;
+ ScMyColumnVectorVec aTables;
+
+public:
+ ScColumnStyles();
+ virtual ~ScColumnStyles() override;
+
+ virtual void AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields) override;
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField,
+ bool& bIsVisible);
+ void AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField, const sal_Int32 nStringIndex, const bool bIsVisible);
+};
+
+class ScRowStyles : public ScColumnRowStylesBase
+{
+ typedef ::mdds::flat_segment_tree<sal_Int32, sal_Int32> StylesType;
+ std::vector<std::unique_ptr<StylesType> > aTables;
+ struct Cache
+ {
+ sal_Int32 mnTable;
+ sal_Int32 mnStart;
+ sal_Int32 mnEnd;
+ sal_Int32 mnStyle;
+ Cache();
+
+ bool hasCache(sal_Int32 nTable, sal_Int32 nField) const;
+ };
+ Cache maCache;
+
+public:
+ ScRowStyles();
+ virtual ~ScRowStyles() override;
+
+ virtual void AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields) override;
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField);
+ void AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField, const sal_Int32 nStringIndex);
+ void AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nStartField, const sal_Int32 nStringIndex, const sal_Int32 nEndField);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesImportHelper.cxx b/sc/source/filter/xml/XMLStylesImportHelper.cxx
new file mode 100644
index 0000000000..ed3a210fc3
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesImportHelper.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLStylesImportHelper.hxx"
+#include "xmlimprt.hxx"
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+void ScMyStyleNumberFormats::AddStyleNumberFormat(const OUString& rStyleName, const sal_Int32 nNumberFormat)
+{
+ aSet.insert(ScMyStyleNumberFormat(rStyleName, nNumberFormat));
+}
+
+sal_Int32 ScMyStyleNumberFormats::GetStyleNumberFormat(const OUString& rStyleName)
+{
+ ScMyStyleNumberFormat aStyleNumberFormat(rStyleName);
+ ScMyStyleNumberFormatSet::iterator aItr(aSet.find(aStyleNumberFormat));
+ if (aItr == aSet.end())
+ return -1;
+ else
+ return aItr->nNumberFormat;
+}
+
+ScMyStyleRanges::ScMyStyleRanges()
+{
+}
+
+ScMyStyleRanges::~ScMyStyleRanges()
+{
+}
+
+void ScMyStyleRanges::AddRange(const ScRange& rRange, const sal_Int16 nType)
+{
+ switch (nType)
+ {
+ case util::NumberFormat::NUMBER:
+ {
+ if (!mpNumberList)
+ mpNumberList = std::make_shared<ScRangeList>();
+ mpNumberList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::TEXT:
+ {
+ if (!mpTextList)
+ mpTextList = std::make_shared<ScRangeList>();
+ mpTextList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::TIME:
+ {
+ if (!mpTimeList)
+ mpTimeList = std::make_shared<ScRangeList>();
+ mpTimeList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::DATETIME:
+ {
+ if (!mpDateTimeList)
+ mpDateTimeList = std::make_shared<ScRangeList>();
+ mpDateTimeList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::PERCENT:
+ {
+ if (!mpPercentList)
+ mpPercentList = std::make_shared<ScRangeList>();
+ mpPercentList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::LOGICAL:
+ {
+ if (!mpLogicalList)
+ mpLogicalList = std::make_shared<ScRangeList>();
+ mpLogicalList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::UNDEFINED:
+ {
+ if (!mpUndefinedList)
+ mpUndefinedList = std::make_shared<ScRangeList>();
+ mpUndefinedList->AddAndPartialCombine(rRange);
+ }
+ break;
+ default:
+ {
+ OSL_FAIL("wrong type");
+ }
+ break;
+ }
+}
+
+void ScMyStyleRanges::AddCurrencyRange(const ScRange& rRange, const std::optional<OUString> & pCurrency)
+{
+ if (!pCurrencyList)
+ pCurrencyList.reset( new ScMyCurrencyStylesSet );
+ ScMyCurrencyStyle aStyle;
+ if (pCurrency)
+ aStyle.sCurrency = *pCurrency;
+ auto itPair = pCurrencyList->insert(aStyle);
+ itPair.first->mpRanges->AddAndPartialCombine(rRange);
+}
+
+void ScMyStyleRanges::InsertCol(const sal_Int32 nCol, const sal_Int32 nTab)
+{
+ if (mpTextList)
+ mpTextList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpNumberList)
+ mpNumberList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpTimeList)
+ mpTimeList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpDateTimeList)
+ mpDateTimeList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpPercentList)
+ mpPercentList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpLogicalList)
+ mpLogicalList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpUndefinedList)
+ mpUndefinedList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+
+ if (pCurrencyList)
+ {
+ for (auto& rCurrency : *pCurrencyList)
+ {
+ rCurrency.mpRanges->InsertCol(static_cast<SCCOL>(nCol), static_cast<SCTAB>(nTab));
+ }
+ }
+}
+
+void ScMyStyleRanges::SetStylesToRanges(const ScRangeList& rRanges,
+ const OUString* pStyleName, const sal_Int16 nCellType,
+ const OUString* pCurrency, ScXMLImport& rImport)
+{
+ rImport.SetStyleToRanges(rRanges, pStyleName, nCellType, pCurrency);
+}
+
+void ScMyStyleRanges::SetStylesToRanges(const OUString* pStyleName, ScXMLImport& rImport)
+{
+ if (mpNumberList)
+ {
+ SetStylesToRanges(*mpNumberList, pStyleName, util::NumberFormat::NUMBER, nullptr, rImport);
+ mpNumberList.reset();
+ }
+ if (mpTextList)
+ {
+ SetStylesToRanges(*mpTextList, pStyleName, util::NumberFormat::TEXT, nullptr, rImport);
+ mpTextList.reset();
+ }
+ if (mpTimeList)
+ {
+ SetStylesToRanges(*mpTimeList, pStyleName, util::NumberFormat::TIME, nullptr, rImport);
+ mpTimeList.reset();
+ }
+ if (mpDateTimeList)
+ {
+ SetStylesToRanges(*mpDateTimeList, pStyleName, util::NumberFormat::DATETIME, nullptr, rImport);
+ mpDateTimeList.reset();
+ }
+ if (mpPercentList)
+ {
+ SetStylesToRanges(*mpPercentList, pStyleName, util::NumberFormat::PERCENT, nullptr, rImport);
+ mpPercentList.reset();
+ }
+ if (mpLogicalList)
+ {
+ SetStylesToRanges(*mpLogicalList, pStyleName, util::NumberFormat::LOGICAL, nullptr, rImport);
+ mpLogicalList.reset();
+ }
+ if (mpUndefinedList)
+ {
+ SetStylesToRanges(*mpUndefinedList, pStyleName, util::NumberFormat::UNDEFINED, nullptr, rImport);
+ mpUndefinedList.reset();
+ }
+ if (pCurrencyList)
+ {
+ for (const auto& rCurrency : *pCurrencyList)
+ {
+ SetStylesToRanges(*rCurrency.mpRanges, pStyleName, util::NumberFormat::CURRENCY, &rCurrency.sCurrency, rImport);
+ }
+ }
+}
+
+ScMyStylesImportHelper::ScMyStylesImportHelper(ScXMLImport& rTempImport)
+ :
+ aRowDefaultStyle(aCellStyles.end()),
+ rImport(rTempImport),
+ nCellType(0),
+ nPrevCellType(0),
+ bPrevRangeAdded(true)
+{
+}
+
+ScMyStylesImportHelper::~ScMyStylesImportHelper()
+{
+}
+
+void ScMyStylesImportHelper::ResetAttributes()
+{
+ pPrevStyleName = std::move(pStyleName);
+ pPrevCurrency = std::move(pCurrency);
+ nPrevCellType = nCellType;
+ nCellType = 0;
+}
+
+ScMyStylesMap::iterator ScMyStylesImportHelper::GetIterator(const OUString & rStyleName)
+{
+ auto it = aCellStyles.find(rStyleName);
+ if (it == aCellStyles.end())
+ it = aCellStyles.emplace_hint(it, std::piecewise_construct,
+ std::forward_as_tuple(rStyleName), std::forward_as_tuple());
+ return it;
+}
+
+void ScMyStylesImportHelper::AddDefaultRange(const ScRange& rRange)
+{
+ OSL_ENSURE(aRowDefaultStyle != aCellStyles.end(), "no row default style");
+ if (aRowDefaultStyle->first.isEmpty())
+ {
+ SCCOL nStartCol(rRange.aStart.Col());
+ SCCOL nEndCol(rRange.aEnd.Col());
+ if (aColDefaultStyles.size() > sal::static_int_cast<sal_uInt32>(nStartCol))
+ {
+ ScMyStylesMap::iterator aPrevItr(aColDefaultStyles[nStartCol]);
+ for (SCCOL i = nStartCol + 1; (i <= nEndCol) && (i < sal::static_int_cast<SCCOL>(aColDefaultStyles.size())); ++i)
+ {
+ if (aPrevItr != aColDefaultStyles[i])
+ {
+ OSL_ENSURE(aPrevItr != aCellStyles.end(), "no column default style");
+ ScRange aRange(rRange);
+ aRange.aStart.SetCol(nStartCol);
+ aRange.aEnd.SetCol(i - 1);
+ pPrevStyleName = aPrevItr->first;
+ AddSingleRange(aRange);
+ nStartCol = i;
+ aPrevItr = aColDefaultStyles[i];
+ }
+ }
+ if (aPrevItr != aCellStyles.end())
+ {
+ ScRange aRange(rRange);
+ aRange.aStart.SetCol(nStartCol);
+ pPrevStyleName = aPrevItr->first;
+ AddSingleRange(aRange);
+ }
+ else
+ {
+ OSL_FAIL("no column default style");
+ }
+ }
+ else
+ {
+ OSL_FAIL("too many columns");
+ }
+ }
+ else
+ {
+ pPrevStyleName = aRowDefaultStyle->first;
+ AddSingleRange(rRange);
+ }
+}
+
+void ScMyStylesImportHelper::AddSingleRange(const ScRange& rRange)
+{
+ ScMyStylesMap::iterator aItr(GetIterator(*pPrevStyleName));
+ if (nPrevCellType != util::NumberFormat::CURRENCY)
+ aItr->second.AddRange(rRange, nPrevCellType);
+ else
+ aItr->second.AddCurrencyRange(rRange, pPrevCurrency);
+}
+
+void ScMyStylesImportHelper::AddRange()
+{
+ if (pPrevStyleName && !pPrevStyleName->isEmpty())
+ AddSingleRange(aPrevRange);
+ else
+ AddDefaultRange(aPrevRange);
+ ResetAttributes();
+}
+
+void ScMyStylesImportHelper::AddColumnStyle(const OUString& sStyleName, const sal_Int32 nColumn, const sal_Int32 nRepeat)
+{
+ OSL_ENSURE(static_cast<sal_uInt32>(nColumn) == aColDefaultStyles.size(), "some columns are absent");
+ ScMyStylesMap::iterator aItr(GetIterator(sStyleName));
+ aColDefaultStyles.reserve(std::max<size_t>(aColDefaultStyles.size() + nRepeat, aColDefaultStyles.size() * 2));
+ for (sal_Int32 i = 0; i < nRepeat; ++i)
+ aColDefaultStyles.push_back(aItr);
+}
+
+void ScMyStylesImportHelper::SetRowStyle(const OUString& sStyleName)
+{
+ aRowDefaultStyle = GetIterator(sStyleName);
+}
+
+void ScMyStylesImportHelper::SetAttributes(std::optional<OUString> pStyleNameP,
+ std::optional<OUString> pCurrencyP, const sal_Int16 nCellTypeP)
+{
+ pStyleName = std::move(pStyleNameP);
+ pCurrency = std::move(pCurrencyP);
+ nCellType = nCellTypeP;
+}
+
+void ScMyStylesImportHelper::AddRange(const ScRange& rRange)
+{
+ if (!bPrevRangeAdded)
+ {
+ bool bAddRange(false);
+ if (nCellType == nPrevCellType &&
+ pStyleName == pPrevStyleName &&
+ pCurrency == pPrevCurrency)
+ {
+ if (rRange.aStart.Row() == aPrevRange.aStart.Row())
+ {
+ if (rRange.aEnd.Row() == aPrevRange.aEnd.Row())
+ {
+ OSL_ENSURE(aPrevRange.aEnd.Col() + 1 == rRange.aStart.Col(), "something went wrong");
+ aPrevRange.aEnd.SetCol(rRange.aEnd.Col());
+ }
+ else
+ bAddRange = true;
+ }
+ else
+ {
+ if (rRange.aStart.Col() == aPrevRange.aStart.Col() &&
+ rRange.aEnd.Col() == aPrevRange.aEnd.Col())
+ {
+ OSL_ENSURE(aPrevRange.aEnd.Row() + 1 == rRange.aStart.Row(), "something went wrong");
+ aPrevRange.aEnd.SetRow(rRange.aEnd.Row());
+ }
+ else
+ bAddRange = true;
+ }
+ }
+ else
+ bAddRange = true;
+ if (bAddRange)
+ {
+ AddRange();
+ aPrevRange = rRange;
+ }
+ }
+ else
+ {
+ aPrevRange = rRange;
+ ResetAttributes();
+ bPrevRangeAdded = false;
+ }
+}
+
+void ScMyStylesImportHelper::AddCell(const ScAddress& rAddress)
+{
+ ScRange aScRange( rAddress, rAddress );
+ AddRange(aScRange);
+}
+
+void ScMyStylesImportHelper::InsertCol(const sal_Int32 nCol, const sal_Int32 nTab)
+{
+ ScXMLImport::MutexGuard aGuard(rImport);
+ for (auto& rCellStyle : aCellStyles)
+ {
+ rCellStyle.second.InsertCol(nCol, nTab);
+ }
+}
+
+void ScMyStylesImportHelper::EndTable()
+{
+ if (!bPrevRangeAdded)
+ {
+ AddRange();
+ bPrevRangeAdded = true;
+ }
+}
+
+void ScMyStylesImportHelper::SetStylesToRanges()
+{
+ for (auto& rCellStyle : aCellStyles)
+ {
+ rCellStyle.second.SetStylesToRanges(&rCellStyle.first, rImport);
+ }
+ aColDefaultStyles.clear();
+ aCellStyles.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesImportHelper.hxx b/sc/source/filter/xml/XMLStylesImportHelper.hxx
new file mode 100644
index 0000000000..13fc31a665
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesImportHelper.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rangelst.hxx>
+#include <rtl/ustring.hxx>
+
+#include <memory>
+#include <set>
+#include <map>
+#include <utility>
+#include <vector>
+#include <optional>
+
+class ScXMLImport;
+
+struct ScMyStyleNumberFormat
+{
+ OUString sStyleName;
+ sal_Int32 nNumberFormat;
+
+ explicit ScMyStyleNumberFormat(OUString aStyleName) :
+ sStyleName(std::move(aStyleName)), nNumberFormat(-1) {}
+ ScMyStyleNumberFormat(OUString aStyleName, const sal_Int32 nFormat) :
+ sStyleName(std::move(aStyleName)), nNumberFormat(nFormat) {}
+};
+
+struct LessStyleNumberFormat
+{
+ bool operator() (const ScMyStyleNumberFormat& rValue1, const ScMyStyleNumberFormat& rValue2) const
+ {
+ return rValue1.sStyleName < rValue2.sStyleName;
+ }
+};
+
+typedef std::set< ScMyStyleNumberFormat, LessStyleNumberFormat > ScMyStyleNumberFormatSet;
+
+class ScMyStyleNumberFormats
+{
+ ScMyStyleNumberFormatSet aSet;
+
+public:
+ void AddStyleNumberFormat(const OUString& rStyleName, const sal_Int32 nNumberFormat);
+ sal_Int32 GetStyleNumberFormat(const OUString& rStyleName);
+};
+
+struct ScMyCurrencyStyle
+{
+ OUString sCurrency;
+ std::shared_ptr<ScRangeList> mpRanges;
+
+ ScMyCurrencyStyle() :
+ mpRanges(std::make_shared<ScRangeList>())
+ {}
+};
+
+struct LessCurrencyStyle
+{
+ bool operator() (const ScMyCurrencyStyle& rValue1, const ScMyCurrencyStyle& rValue2) const
+ {
+ return rValue1.sCurrency < rValue2.sCurrency;
+ }
+};
+
+typedef std::set<ScMyCurrencyStyle, LessCurrencyStyle> ScMyCurrencyStylesSet;
+
+class ScMyStyleRanges
+{
+ std::shared_ptr<ScRangeList> mpTextList;
+ std::shared_ptr<ScRangeList> mpNumberList;
+ std::shared_ptr<ScRangeList> mpTimeList;
+ std::shared_ptr<ScRangeList> mpDateTimeList;
+ std::shared_ptr<ScRangeList> mpPercentList;
+ std::shared_ptr<ScRangeList> mpLogicalList;
+ std::shared_ptr<ScRangeList> mpUndefinedList;
+ std::unique_ptr<ScMyCurrencyStylesSet> pCurrencyList;
+
+ static void SetStylesToRanges(const ScRangeList& rList,
+ const OUString* pStyleName, const sal_Int16 nCellType,
+ const OUString* pCurrency, ScXMLImport& rImport);
+public:
+ ScMyStyleRanges();
+ ~ScMyStyleRanges();
+ void AddRange(const ScRange& rRange, const sal_Int16 nType);
+ void AddCurrencyRange(const ScRange& rRange, const std::optional<OUString> & pCurrency);
+ void InsertCol(const sal_Int32 nCol, const sal_Int32 nTab);
+ void SetStylesToRanges(const OUString* pStyleName, ScXMLImport& rImport);
+};
+
+/** map from style name to ScMyStyleRanges */
+typedef std::map<OUString, ScMyStyleRanges> ScMyStylesMap;
+
+class ScMyStylesImportHelper
+{
+ ScMyStylesMap aCellStyles;
+ std::vector<ScMyStylesMap::iterator> aColDefaultStyles;
+ ScMyStylesMap::iterator aRowDefaultStyle;
+ ScXMLImport& rImport;
+ std::optional<OUString>
+ pStyleName;
+ std::optional<OUString>
+ pPrevStyleName;
+ std::optional<OUString>
+ pCurrency;
+ std::optional<OUString>
+ pPrevCurrency;
+ ScRange aPrevRange;
+ sal_Int16 nCellType;
+ sal_Int16 nPrevCellType;
+ bool bPrevRangeAdded;
+
+ void ResetAttributes();
+ ScMyStylesMap::iterator GetIterator(const OUString & rStyleName);
+ void AddDefaultRange(const ScRange& rRange);
+ void AddSingleRange(const ScRange& rRange);
+ void AddRange();
+public:
+ explicit ScMyStylesImportHelper(ScXMLImport& rImport);
+ ~ScMyStylesImportHelper();
+ void AddColumnStyle(const OUString& rStyleName, const sal_Int32 nColumn, const sal_Int32 nRepeat);
+ void SetRowStyle(const OUString& rStyleName);
+ void SetAttributes(std::optional<OUString> pStyleName,
+ std::optional<OUString> pCurrency, const sal_Int16 nCellType);
+ void AddRange(const ScRange& rRange);
+ void AddCell(const ScAddress& rAddress);
+ void InsertCol(const sal_Int32 nCol, const sal_Int32 nTab); // a col is inserted before nCol
+ void EndTable();
+ void SetStylesToRanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableHeaderFooterContext.cxx b/sc/source/filter/xml/XMLTableHeaderFooterContext.cxx
new file mode 100644
index 0000000000..192f1cbf56
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableHeaderFooterContext.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlimp.hxx>
+#include "XMLTableHeaderFooterContext.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <comphelper/extract.hxx>
+#include <sal/log.hxx>
+
+#include <unonames.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::beans;
+using namespace xmloff::token;
+
+
+XMLTableHeaderFooterContext::XMLTableHeaderFooterContext( SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList,
+ const Reference < XPropertySet > & rPageStylePropSet,
+ bool bFooter, bool bLeft, bool bFirst ) :
+ SvXMLImportContext( rImport ),
+ xPropSet( rPageStylePropSet ),
+ bContainsLeft(false),
+ bContainsRight(false),
+ bContainsCenter(false)
+{
+ OUString sOn( bFooter ? SC_UNO_PAGE_FTRON : SC_UNO_PAGE_HDRON );
+ OUString sContent( bFooter ? SC_UNO_PAGE_RIGHTFTRCON : SC_UNO_PAGE_RIGHTHDRCON );
+ OUString sContentLeft( bFooter ? SC_UNO_PAGE_LEFTFTRCONT : SC_UNO_PAGE_LEFTHDRCONT );
+ OUString sContentFirst( bFooter ? SC_UNO_PAGE_FIRSTFTRCONT : SC_UNO_PAGE_FIRSTHDRCONT );
+ OUString sShareContent( bFooter ? SC_UNO_PAGE_FTRSHARED : SC_UNO_PAGE_HDRSHARED );
+ OUString sShareFirstContent( bFooter ? SC_UNO_PAGE_FIRSTFTRSHARED : SC_UNO_PAGE_FIRSTHDRSHARED );
+ bool bDisplay( true );
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ if( aIter.getToken() == XML_ELEMENT(STYLE, XML_DISPLAY) )
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ else
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ bool bOn(::cppu::any2bool(xPropSet->getPropertyValue( sOn )));
+ if( bLeft || bFirst )
+ {
+ const OUString sShare = bLeft ? sShareContent : sShareFirstContent;
+ if( bOn && bDisplay )
+ {
+ if( ::cppu::any2bool(xPropSet->getPropertyValue( sShare )) )
+ // Don't share headers any longer
+ xPropSet->setPropertyValue( sShare, uno::Any(false) );
+ }
+ else
+ {
+ if( !::cppu::any2bool(xPropSet->getPropertyValue( sShare )) )
+ // share headers
+ xPropSet->setPropertyValue( sShare, uno::Any(true) );
+ }
+ }
+ else
+ {
+ if ( bOn != bDisplay )
+ xPropSet->setPropertyValue( sOn, uno::Any(bDisplay) );
+ }
+ if (bLeft)
+ {
+ sCont = sContentLeft;
+ }
+ else if (bFirst)
+ {
+ sCont = sContentFirst;
+ xPropSet->setPropertyValue( sShareFirstContent, uno::Any(!bDisplay) );
+ }
+ else
+ {
+ sCont = sContent;
+ }
+ xPropSet->getPropertyValue( sCont ) >>= xHeaderFooterContent;
+}
+
+XMLTableHeaderFooterContext::~XMLTableHeaderFooterContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableHeaderFooterContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ if (xHeaderFooterContent.is())
+ {
+ uno::Reference < text::XText > xText;
+ switch (nElement)
+ {
+ case XML_ELEMENT(STYLE, XML_REGION_LEFT):
+ xText.set(xHeaderFooterContent->getLeftText());
+ bContainsLeft = true;
+ break;
+ case XML_ELEMENT(STYLE, XML_REGION_CENTER):
+ xText.set(xHeaderFooterContent->getCenterText());
+ bContainsCenter = true;
+ break;
+ case XML_ELEMENT(STYLE, XML_REGION_RIGHT):
+ xText.set(xHeaderFooterContent->getRightText());
+ bContainsRight = true;
+ break;
+ default: break;
+ }
+ if (xText.is())
+ {
+ xText->setString("");
+ uno::Reference < text::XTextCursor > xTempTextCursor(xText->createTextCursor());
+ return new XMLHeaderFooterRegionContext( GetImport(), xTempTextCursor);
+ }
+ }
+
+ if ( nElement == XML_ELEMENT(TEXT, XML_P) )
+ {
+ if (!xTextCursor.is())
+ {
+ if( xHeaderFooterContent.is() )
+ {
+ uno::Reference < text::XText > xText(xHeaderFooterContent->getCenterText());
+ xText->setString("");
+ xTextCursor.set(xText->createTextCursor());
+ xOldTextCursor.set(GetImport().GetTextImport()->GetCursor());
+ GetImport().GetTextImport()->SetCursor( xTextCursor );
+ bContainsCenter = true;
+ }
+ }
+ return
+ GetImport().GetTextImport()->CreateTextChildContext(GetImport(),
+ nElement,
+ xAttrList);
+ }
+
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return nullptr;
+}
+
+void XMLTableHeaderFooterContext::endFastElement(sal_Int32 )
+{
+ if( GetImport().GetTextImport()->GetCursor().is() )
+ {
+ //GetImport().GetTextImport()->GetCursor()->gotoEnd(sal_False);
+ if( GetImport().GetTextImport()->GetCursor()->goLeft( 1, true ) )
+ {
+ GetImport().GetTextImport()->GetText()->insertString(
+ GetImport().GetTextImport()->GetCursorAsRange(), "",
+ true );
+ }
+ GetImport().GetTextImport()->ResetCursor();
+ }
+ if (xOldTextCursor.is())
+ GetImport().GetTextImport()->SetCursor(xOldTextCursor);
+ if (xHeaderFooterContent.is())
+ {
+ if (!bContainsLeft)
+ xHeaderFooterContent->getLeftText()->setString("");
+ if (!bContainsCenter)
+ xHeaderFooterContent->getCenterText()->setString("");
+ if (!bContainsRight)
+ xHeaderFooterContent->getRightText()->setString("");
+
+ xPropSet->setPropertyValue( sCont, uno::Any(xHeaderFooterContent) );
+ }
+}
+
+
+XMLHeaderFooterRegionContext::XMLHeaderFooterRegionContext( SvXMLImport& rImport,
+ uno::Reference< text::XTextCursor >& xCursor ) :
+ SvXMLImportContext( rImport ),
+ xTextCursor ( xCursor )
+{
+ xOldTextCursor.set(GetImport().GetTextImport()->GetCursor());
+ GetImport().GetTextImport()->SetCursor( xTextCursor );
+}
+
+XMLHeaderFooterRegionContext::~XMLHeaderFooterRegionContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLHeaderFooterRegionContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if (nElement == XML_ELEMENT(TEXT, XML_P))
+ {
+ return GetImport().GetTextImport()->CreateTextChildContext(GetImport(),
+ nElement,
+ xAttrList);
+ }
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return pContext;
+}
+
+void XMLHeaderFooterRegionContext::endFastElement(sal_Int32 )
+{
+ if( GetImport().GetTextImport()->GetCursor().is() )
+ {
+ //GetImport().GetTextImport()->GetCursor()->gotoEnd(sal_False);
+ if( GetImport().GetTextImport()->GetCursor()->goLeft( 1, true ) )
+ {
+ GetImport().GetTextImport()->GetText()->insertString(
+ GetImport().GetTextImport()->GetCursorAsRange(), "",
+ true );
+ }
+ GetImport().GetTextImport()->ResetCursor();
+ }
+ if (xOldTextCursor.is())
+ GetImport().GetTextImport()->SetCursor(xOldTextCursor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableHeaderFooterContext.hxx b/sc/source/filter/xml/XMLTableHeaderFooterContext.hxx
new file mode 100644
index 0000000000..ccb6a154cc
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableHeaderFooterContext.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/xmlictxt.hxx>
+
+namespace com::sun::star {
+ namespace text { class XTextCursor; }
+ namespace beans { class XPropertySet; }
+}
+
+namespace com::sun::star::sheet { class XHeaderFooterContent; }
+
+class XMLTableHeaderFooterContext: public SvXMLImportContext
+{
+ css::uno::Reference< css::text::XTextCursor > xTextCursor;
+ css::uno::Reference< css::text::XTextCursor > xOldTextCursor;
+ css::uno::Reference< css::beans::XPropertySet > xPropSet;
+ css::uno::Reference< css::sheet::XHeaderFooterContent > xHeaderFooterContent;
+
+ OUString sCont;
+
+ bool bContainsLeft;
+ bool bContainsRight;
+ bool bContainsCenter;
+
+public:
+
+ XMLTableHeaderFooterContext( SvXMLImport& rImport, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ const css::uno::Reference< css::beans::XPropertySet > & rPageStylePropSet,
+ bool bFooter, bool bLft, bool bFirst );
+
+ virtual ~XMLTableHeaderFooterContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class XMLHeaderFooterRegionContext: public SvXMLImportContext
+{
+private:
+ css::uno::Reference< css::text::XTextCursor >& xTextCursor;
+ css::uno::Reference< css::text::XTextCursor > xOldTextCursor;
+
+public:
+
+ XMLHeaderFooterRegionContext( SvXMLImport& rImport,
+ css::uno::Reference< css::text::XTextCursor >& xCursor );
+
+ virtual ~XMLHeaderFooterRegionContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableMasterPageExport.cxx b/sc/source/filter/xml/XMLTableMasterPageExport.cxx
new file mode 100644
index 0000000000..61899e651e
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableMasterPageExport.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include "XMLTableMasterPageExport.hxx"
+#include <comphelper/extract.hxx>
+#include <rtl/ref.hxx>
+#include <osl/diagnose.h>
+
+#include <unonames.hxx>
+#include "xmlexprt.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::beans;
+using namespace xmloff::token;
+
+XMLTableMasterPageExport::XMLTableMasterPageExport( ScXMLExport& rExp ) :
+ XMLTextMasterPageExport ( rExp )
+{
+}
+
+XMLTableMasterPageExport::~XMLTableMasterPageExport()
+{
+}
+
+void XMLTableMasterPageExport::exportHeaderFooterContent(
+ const Reference< XText >& rText,
+ bool bAutoStyles, bool bProgress )
+{
+ OSL_ENSURE( rText.is(), "There is the text" );
+
+ if( bAutoStyles )
+ GetExport().GetTextParagraphExport()
+ ->collectTextAutoStyles( rText, bProgress, false );
+ else
+ {
+ GetExport().GetTextParagraphExport()->exportTextDeclarations( rText );
+ GetExport().GetTextParagraphExport()->exportText( rText, bProgress, false );
+ }
+}
+
+void XMLTableMasterPageExport::exportHeaderFooter(const css::uno::Reference < css::sheet::XHeaderFooterContent >& xHeaderFooter,
+ const XMLTokenEnum aName,
+ const bool bDisplay)
+{
+ if( !xHeaderFooter.is() )
+ return;
+
+ sal_uInt16 nNameSpace = XML_NAMESPACE_STYLE;
+ if (aName == XML_HEADER_FIRST || aName == XML_FOOTER_FIRST)
+ {
+ // Since ODF 1.3 OFFICE-3789 or 1.2-extended.
+ auto const nVersion(GetExport().getSaneDefaultVersion());
+ if (nVersion <= SvtSaveOptions::ODFSVER_012)
+ return;
+ if (nVersion < SvtSaveOptions::ODFSVER_013)
+ nNameSpace = XML_NAMESPACE_LO_EXT;
+ }
+
+ Reference < XText > xCenter(xHeaderFooter->getCenterText());
+ Reference < XText > xLeft(xHeaderFooter->getLeftText());
+ Reference < XText > xRight(xHeaderFooter->getRightText());
+ if (!(xCenter.is() && xLeft.is() && xRight.is()))
+ return;
+
+ OUString sCenter (xCenter->getString());
+ OUString sLeft (xLeft->getString());
+ OUString sRight (xRight->getString());
+
+ if( !bDisplay )
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE,
+ XML_DISPLAY, XML_FALSE );
+ SvXMLElementExport aElem( GetExport(), nNameSpace,
+ aName, true, true );
+ if (!sCenter.isEmpty() && sLeft.isEmpty() && sRight.isEmpty())
+ exportHeaderFooterContent( xCenter, false, false );
+ else
+ {
+ if (!sLeft.isEmpty())
+ {
+ SvXMLElementExport aSubElem( GetExport(), XML_NAMESPACE_STYLE,
+ XML_REGION_LEFT, true, true );
+ exportHeaderFooterContent( xLeft, false, false );
+ }
+ if (!sCenter.isEmpty())
+ {
+ SvXMLElementExport aSubElem( GetExport(), XML_NAMESPACE_STYLE,
+ XML_REGION_CENTER, true, true );
+ exportHeaderFooterContent( xCenter, false, false );
+ }
+ if (!sRight.isEmpty())
+ {
+ SvXMLElementExport aSubElem( GetExport(), XML_NAMESPACE_STYLE,
+ XML_REGION_RIGHT, true, true );
+ exportHeaderFooterContent( xRight, false, false );
+ }
+ }
+}
+
+void XMLTableMasterPageExport::exportMasterPageContent(
+ const Reference < XPropertySet > & rPropSet,
+ bool bAutoStyles )
+{
+ Reference < sheet::XHeaderFooterContent > xHeader(rPropSet->getPropertyValue( SC_UNO_PAGE_RIGHTHDRCON ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xHeaderLeft(rPropSet->getPropertyValue( SC_UNO_PAGE_LEFTHDRCONT ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xHeaderFirst(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTHDRCONT ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xFooter(rPropSet->getPropertyValue( SC_UNO_PAGE_RIGHTFTRCON ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xFooterLeft(rPropSet->getPropertyValue( SC_UNO_PAGE_LEFTFTRCONT ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xFooterFirst(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTFTRCONT ), uno::UNO_QUERY);
+
+ if( bAutoStyles )
+ {
+ if( xHeader.is() )
+ {
+ exportHeaderFooterContent( xHeader->getCenterText(), true, false );
+ exportHeaderFooterContent( xHeader->getLeftText(), true, false );
+ exportHeaderFooterContent( xHeader->getRightText(), true, false );
+ }
+ if( xHeaderLeft.is())
+ {
+ exportHeaderFooterContent( xHeaderLeft->getCenterText(), true, false );
+ exportHeaderFooterContent( xHeaderLeft->getLeftText(), true, false );
+ exportHeaderFooterContent( xHeaderLeft->getRightText(), true, false );
+ }
+ if( xHeaderFirst.is())
+ {
+ exportHeaderFooterContent( xHeaderFirst->getCenterText(), true, false );
+ exportHeaderFooterContent( xHeaderFirst->getLeftText(), true, false );
+ exportHeaderFooterContent( xHeaderFirst->getRightText(), true, false );
+ }
+ if( xFooter.is() )
+ {
+ exportHeaderFooterContent( xFooter->getCenterText(), true, false );
+ exportHeaderFooterContent( xFooter->getLeftText(), true, false );
+ exportHeaderFooterContent( xFooter->getRightText(), true, false );
+ }
+ if( xFooterLeft.is())
+ {
+ exportHeaderFooterContent( xFooterLeft->getCenterText(), true, false );
+ exportHeaderFooterContent( xFooterLeft->getLeftText(), true, false );
+ exportHeaderFooterContent( xFooterLeft->getRightText(), true, false );
+ }
+ if( xFooterFirst.is())
+ {
+ exportHeaderFooterContent( xFooterFirst->getCenterText(), true, false );
+ exportHeaderFooterContent( xFooterFirst->getLeftText(), true, false );
+ exportHeaderFooterContent( xFooterFirst->getRightText(), true, false );
+ }
+ }
+ else
+ {
+ bool bHeader(::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_HDRON )));
+
+ exportHeaderFooter(xHeader, XML_HEADER, bHeader );
+
+ bool bLeftHeader(!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_HDRSHARED )) && bHeader);
+
+ exportHeaderFooter( xHeaderLeft, XML_HEADER_LEFT, bLeftHeader );
+
+ bool bFirstHeader(!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTHDRSHARED )) && bHeader);
+
+ exportHeaderFooter( xHeaderFirst, XML_HEADER_FIRST, bFirstHeader );
+
+ bool bFooter(::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FTRON )));
+
+ exportHeaderFooter( xFooter, XML_FOOTER, bFooter );
+
+ bool bLeftFooter = (!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FTRSHARED )) && bFooter);
+
+ exportHeaderFooter( xFooterLeft, XML_FOOTER_LEFT, bLeftFooter );
+
+ bool bFirstFooter = (!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTFTRSHARED )) && bFooter);
+
+ exportHeaderFooter( xFooterFirst, XML_FOOTER_FIRST, bFirstFooter );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableMasterPageExport.hxx b/sc/source/filter/xml/XMLTableMasterPageExport.hxx
new file mode 100644
index 0000000000..2ae7a127e2
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableMasterPageExport.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/XMLTextMasterPageExport.hxx>
+
+namespace com::sun::star {
+ namespace text { class XText; }
+}
+
+namespace com::sun::star::sheet { class XHeaderFooterContent; }
+
+class ScXMLExport;
+
+class XMLTableMasterPageExport : public XMLTextMasterPageExport
+{
+ void exportHeaderFooter(const css::uno::Reference < css::sheet::XHeaderFooterContent >& xHeaderFooter,
+ const xmloff::token::XMLTokenEnum aName,
+ const bool bDisplay);
+
+protected:
+ virtual void exportHeaderFooterContent(
+ const css::uno::Reference< css::text::XText >& rText,
+ bool bAutoStyles, bool bProgress = true ) override;
+
+ virtual void exportMasterPageContent(
+ const css::uno::Reference< css::beans::XPropertySet > & rPropSet,
+ bool bAutoStyles ) override;
+
+public:
+ explicit XMLTableMasterPageExport( ScXMLExport& rExp );
+ virtual ~XMLTableMasterPageExport() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeImportHelper.cxx b/sc/source/filter/xml/XMLTableShapeImportHelper.cxx
new file mode 100644
index 0000000000..003b84a0e4
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeImportHelper.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 "XMLTableShapeImportHelper.hxx"
+#include "xmlimprt.hxx"
+#include <drwlayer.hxx>
+#include "xmlannoi.hxx"
+#include <rangeutl.hxx>
+#include <userdat.hxx>
+#include <docuno.hxx>
+#include <sheetdata.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+
+constexpr OUStringLiteral SC_LAYERID = u"LayerID";
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+XMLTableShapeImportHelper::XMLTableShapeImportHelper( ScXMLImport& rImp ) :
+ XMLShapeImportHelper(rImp, rImp.GetModel(), nullptr ),
+ pAnnotationContext(nullptr),
+ bOnTable(false)
+{
+}
+
+XMLTableShapeImportHelper::~XMLTableShapeImportHelper()
+{
+}
+
+void XMLTableShapeImportHelper::SetLayer(const uno::Reference<drawing::XShape>& rShape, SdrLayerID nLayerID, std::u16string_view sType)
+{
+ if ( sType == u"com.sun.star.drawing.ControlShape" )
+ nLayerID = SC_LAYER_CONTROLS;
+ if (nLayerID != SDRLAYER_NOTFOUND)
+ {
+ uno::Reference< beans::XPropertySet > xShapeProp( rShape, uno::UNO_QUERY );
+ if( xShapeProp.is() )
+ xShapeProp->setPropertyValue( SC_LAYERID, uno::Any(nLayerID.get()) );
+ }
+}
+
+// Attempt to find the topmost parent of the group, this is the one we apply
+// offsets to
+static uno::Reference< drawing::XShape > lcl_getTopLevelParent( const uno::Reference< drawing::XShape >& rShape )
+{
+ uno::Reference< container::XChild > xChild( rShape, uno::UNO_QUERY );
+ uno::Reference< drawing::XShape > xParent( xChild->getParent(), uno::UNO_QUERY );
+ if ( xParent.is() )
+ return lcl_getTopLevelParent( xParent );
+ return rShape;
+}
+
+void XMLTableShapeImportHelper::finishShape(
+ uno::Reference< drawing::XShape >& rShape,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ uno::Reference< drawing::XShapes >& rShapes )
+{
+ bool bNote = false;
+ XMLShapeImportHelper::finishShape( rShape, xAttrList, rShapes );
+ static_cast<ScXMLImport&>(mrImporter).LockSolarMutex();
+ ScMyTables& rTables = static_cast<ScXMLImport&>(mrImporter).GetTables();
+ if (rShapes == rTables.GetCurrentXShapes())
+ {
+ if (!pAnnotationContext)
+ {
+ ScDrawObjData aAnchor;
+ aAnchor.maStart = aStartCell;
+ awt::Point aStartPoint(rShape->getPosition());
+ aAnchor.maStartOffset = Point(aStartPoint.X, aStartPoint.Y);
+ aAnchor.mbResizeWithCell = false;
+
+ sal_Int32 nEndX(-1);
+ sal_Int32 nEndY(-1);
+ std::optional<OUString> xRangeList;
+ SdrLayerID nLayerID = SDRLAYER_NOTFOUND;
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ switch(aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_END_CELL_ADDRESS):
+ {
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = static_cast<ScXMLImport&>(mrImporter).GetDocument();
+ assert(pDoc);
+ ScRangeStringConverter::GetAddressFromString(aAnchor.maEnd, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset);
+ // When the cell end address is set, we let the shape resize with the cell
+ aAnchor.mbResizeWithCell = true;
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_END_X):
+ {
+ static_cast<ScXMLImport&>(mrImporter).
+ GetMM100UnitConverter().convertMeasureToCore(
+ nEndX, aIter.toView());
+ aAnchor.maEndOffset.setX( nEndX );
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_END_Y):
+ {
+ static_cast<ScXMLImport&>(mrImporter).
+ GetMM100UnitConverter().convertMeasureToCore(
+ nEndY, aIter.toView());
+ aAnchor.maEndOffset.setY( nEndY );
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_TABLE_BACKGROUND):
+ if (IsXMLToken(aIter, XML_TRUE))
+ nLayerID = SC_LAYER_BACK;
+ break;
+ case XML_ELEMENT(DRAW, XML_NOTIFY_ON_UPDATE_OF_RANGES):
+ xRangeList = aIter.toString();
+ break;
+ default: ;
+ }
+ }
+ SetLayer(rShape, nLayerID, rShape->getShapeType());
+
+ if (SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(rShape))
+ {
+ if (!bOnTable)
+ ScDrawLayer::SetCellAnchored(*pSdrObj, aAnchor);
+ else
+ ScDrawLayer::SetPageAnchored(*pSdrObj);
+ }
+
+ if (xRangeList)
+ {
+ // #i78086# If there are notification ranges, the ChartListener must be created
+ // also when anchored to the sheet
+ // -> call AddOLE with invalid cell position (checked in ScMyShapeResizer::ResizeShapes)
+
+ if (ScMyTables::IsOLE(rShape))
+ rTables.AddOLE(rShape, *xRangeList);
+ }
+ }
+ else // shape is annotation
+ {
+ // get the style names for stream copying
+ OUString aStyleName;
+ OUString aTextStyle;
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ const OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(DRAW, XML_STYLE_NAME):
+ aStyleName = sValue;
+ break;
+ case XML_ELEMENT(DRAW, XML_TEXT_STYLE_NAME):
+ aTextStyle = sValue;
+ break;
+ default:;
+ }
+ }
+
+ pAnnotationContext->SetShape(rShape, rShapes, aStyleName, aTextStyle);
+ bNote = true;
+ }
+ }
+ else //this are grouped shapes which should also get the layerid
+ {
+ uno::Reference< drawing::XShapes > xGroup( rShape, uno::UNO_QUERY );
+ // ignore the group ( within group ) object if it exists
+ if ( !bOnTable && !xGroup.is() )
+ {
+ // For cell anchored grouped shape we need to set the start
+ // position from the most top and left positioned shape(s) within
+ // the group
+ Point aStartPoint( rShape->getPosition().X,rShape->getPosition().Y );
+ uno::Reference< drawing::XShape > xChild( rShapes, uno::UNO_QUERY );
+ if (xChild)
+ {
+ if (SdrObject *pSdrObj = SdrObject::getSdrObjectFromXShape(lcl_getTopLevelParent(xChild)))
+ {
+ if ( ScDrawObjData* pAnchor = ScDrawLayer::GetObjData( pSdrObj ) )
+ {
+ if ( pAnchor->maStartOffset.getX() == 0 && pAnchor->maStartOffset.getY() == 0 )
+ pAnchor->maStartOffset = aStartPoint;
+ if ( aStartPoint.getX() < pAnchor->maStartOffset.getX() )
+ pAnchor->maStartOffset.setX( aStartPoint.getX() );
+ if ( aStartPoint.getY() < pAnchor->maStartOffset.getY() )
+ pAnchor->maStartOffset.setY( aStartPoint.getY() );
+ }
+ }
+ }
+ }
+ SdrLayerID nLayerID = SDRLAYER_NOTFOUND;
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ if (aIter.getToken() == XML_ELEMENT(TABLE, XML_TABLE_BACKGROUND))
+ {
+ if (IsXMLToken(aIter, XML_TRUE))
+ nLayerID = SC_LAYER_BACK;
+ break;
+ }
+ }
+ SetLayer(rShape, nLayerID, rShape->getShapeType());
+ }
+
+ if (!bNote)
+ {
+ // any shape other than a note prevents copying the sheet
+ ScSheetSaveData* pSheetData = static_cast<ScXMLImport&>(mrImporter).GetScModel()->GetSheetSaveData();
+ pSheetData->BlockSheet( rTables.GetCurrentSheet() );
+ }
+
+ static_cast<ScXMLImport&>(mrImporter).UnlockSolarMutex();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeImportHelper.hxx b/sc/source/filter/xml/XMLTableShapeImportHelper.hxx
new file mode 100644
index 0000000000..2f27233d30
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeImportHelper.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/shapeimport.hxx>
+#include <svx/svdtypes.hxx>
+#include <address.hxx>
+
+class ScXMLImport;
+class ScXMLAnnotationContext;
+
+class XMLTableShapeImportHelper : public XMLShapeImportHelper
+{
+ ScAddress aStartCell;
+ ScXMLAnnotationContext* pAnnotationContext;
+ bool bOnTable;
+
+public:
+
+ explicit XMLTableShapeImportHelper( ScXMLImport& rImp );
+ virtual ~XMLTableShapeImportHelper() override;
+
+ static void SetLayer(const css::uno::Reference<css::drawing::XShape>& rShape, SdrLayerID nLayerID, std::u16string_view sType);
+ virtual void finishShape(css::uno::Reference< css::drawing::XShape >& rShape,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ css::uno::Reference< css::drawing::XShapes >& rShapes) override;
+
+ void SetCell (const ScAddress& rAddress) { aStartCell = rAddress; }
+ void SetOnTable (const bool bTempOnTable) { bOnTable = bTempOnTable; }
+ void SetAnnotation(ScXMLAnnotationContext* pAnnotation) { pAnnotationContext = pAnnotation; }
+
+ ScXMLAnnotationContext* GetAnnotationContext() const { return pAnnotationContext; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeResizer.cxx b/sc/source/filter/xml/XMLTableShapeResizer.cxx
new file mode 100644
index 0000000000..4ce1e6a022
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeResizer.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 "XMLTableShapeResizer.hxx"
+#include <document.hxx>
+#include "xmlimprt.hxx"
+#include <chartlis.hxx>
+#include <rangeutl.hxx>
+#include <compiler.hxx>
+#include <reftokenhelper.hxx>
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+
+#include <memory>
+#include <vector>
+
+using namespace ::com::sun::star;
+using ::std::vector;
+
+ScMyOLEFixer::ScMyOLEFixer(ScXMLImport& rTempImport)
+ : rImport(rTempImport),
+ pCollection(nullptr)
+{
+}
+
+ScMyOLEFixer::~ScMyOLEFixer()
+{
+}
+
+bool ScMyOLEFixer::IsOLE(const uno::Reference< drawing::XShape >& rShape)
+{
+ return rShape->getShapeType() == "com.sun.star.drawing.OLE2Shape";
+}
+
+void ScMyOLEFixer::CreateChartListener(ScDocument& rDoc,
+ const OUString& rName,
+ std::u16string_view rRangeList)
+{
+ if (rRangeList.empty())
+ {
+ rDoc.AddOLEObjectToCollection(rName);
+ return;
+ }
+
+ OUString aRangeStr;
+ ScRangeStringConverter::GetStringFromXMLRangeString(aRangeStr, rRangeList, rDoc);
+ if (aRangeStr.isEmpty())
+ {
+ rDoc.AddOLEObjectToCollection(rName);
+ return;
+ }
+
+ if (!pCollection)
+ pCollection = rDoc.GetChartListenerCollection();
+
+ if (!pCollection)
+ return;
+
+ vector<ScTokenRef> aRefTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aRefTokens, aRangeStr, rDoc, cSep, rDoc.GetGrammar());
+ if (aRefTokens.empty())
+ return;
+
+ OUString sName = !rName.isEmpty() ? rName : pCollection->getUniqueName(u"OLEFixer ");
+ ScChartListener* pCL(new ScChartListener(sName, rDoc, std::move(aRefTokens)));
+
+ //for loading binary files e.g.
+ //if we have the flat filter we need to set the dirty flag thus the visible charts get repainted
+ //otherwise the charts keep their first visual representation which was created at a moment where the calc itself was not loaded completely and is therefore incorrect
+ if( (rImport.getImportFlags() & SvXMLImportFlags::ALL) == SvXMLImportFlags::ALL )
+ pCL->SetDirty( true );
+ else
+ {
+ // #i104899# If a formula cell is already dirty, further changes aren't propagated.
+ // This can happen easily now that row heights aren't updated for all sheets.
+ rDoc.InterpretDirtyCells( *pCL->GetRangeList() );
+ }
+
+ bool bSuccess = pCollection->insert(pCL);
+ assert(bSuccess && "failed to insert listener"); (void)bSuccess;
+ pCL->StartListeningTo();
+}
+
+void ScMyOLEFixer::AddOLE(const uno::Reference <drawing::XShape>& rShape,
+ const OUString &rRangeList)
+{
+ ScMyToFixupOLE aShape;
+ aShape.xShape.set(rShape);
+ aShape.sRangeList = rRangeList;
+ aShapes.push_back(aShape);
+}
+
+void ScMyOLEFixer::FixupOLEs()
+{
+ if (aShapes.empty() || !rImport.GetModel().is())
+ return;
+
+ OUString sPersistName ("PersistName");
+ ScDocument* pDoc(rImport.GetDocument());
+
+ ScXMLImport::MutexGuard aGuard(rImport);
+
+ for (auto const& shape : aShapes)
+ {
+ // #i78086# also call CreateChartListener for invalid position (anchored to sheet)
+ if (!IsOLE(shape.xShape))
+ OSL_FAIL("Only OLEs should be in here now");
+
+ if (IsOLE(shape.xShape))
+ {
+ uno::Reference < beans::XPropertySet > xShapeProps ( shape.xShape, uno::UNO_QUERY );
+ uno::Reference < beans::XPropertySetInfo > xShapeInfo(xShapeProps->getPropertySetInfo());
+
+ OUString sName;
+ if (pDoc && xShapeProps.is() && xShapeInfo.is() && xShapeInfo->hasPropertyByName(sPersistName) &&
+ (xShapeProps->getPropertyValue(sPersistName) >>= sName))
+ CreateChartListener(*pDoc, sName, shape.sRangeList);
+ }
+ }
+ aShapes.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeResizer.hxx b/sc/source/filter/xml/XMLTableShapeResizer.hxx
new file mode 100644
index 0000000000..5bd213def1
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeResizer.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 <com/sun/star/uno/Reference.hxx>
+#include <vector>
+
+namespace com::sun::star::drawing { class XShape; }
+
+class ScXMLImport;
+class ScChartListenerCollection;
+class ScDocument;
+
+struct ScMyToFixupOLE
+{
+ css::uno::Reference <css::drawing::XShape> xShape;
+ OUString sRangeList;
+};
+
+class ScMyOLEFixer
+{
+ ScXMLImport& rImport;
+ std::vector<ScMyToFixupOLE> aShapes;
+ ScChartListenerCollection* pCollection;
+
+ void CreateChartListener(ScDocument& rDoc,
+ const OUString& rName,
+ std::u16string_view rRangeList);
+public:
+ explicit ScMyOLEFixer(ScXMLImport& rImport);
+ ~ScMyOLEFixer();
+
+ static bool IsOLE(const css::uno::Reference< css::drawing::XShape >& rShape);
+ void AddOLE(const css::uno::Reference <css::drawing::XShape>& rShape,
+ const OUString &rRangeList);
+ void FixupOLEs();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapesContext.cxx b/sc/source/filter/xml/XMLTableShapesContext.cxx
new file mode 100644
index 0000000000..676bc560fa
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapesContext.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLTableShapesContext.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+#include "xmlimprt.hxx"
+
+using namespace com::sun::star;
+
+ScXMLTableShapesContext::ScXMLTableShapesContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // here are no attributes
+}
+
+ScXMLTableShapesContext::~ScXMLTableShapesContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > ScXMLTableShapesContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ ScXMLImport& rXMLImport(GetScImport());
+ uno::Reference<drawing::XShapes> xShapes (rXMLImport.GetTables().GetCurrentXShapes());
+ if (xShapes.is())
+ {
+ XMLTableShapeImportHelper* pTableShapeImport(static_cast<XMLTableShapeImportHelper*>(rXMLImport.GetShapeImport().get()));
+ pTableShapeImport->SetOnTable(true);
+ return XMLShapeImportHelper::CreateGroupChildContext(
+ rXMLImport, nElement, xAttrList, xShapes);
+ }
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapesContext.hxx b/sc/source/filter/xml/XMLTableShapesContext.hxx
new file mode 100644
index 0000000000..649fef106d
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapesContext.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLImport;
+
+class ScXMLTableShapesContext : public ScXMLImportContext
+{
+public:
+ ScXMLTableShapesContext( ScXMLImport& rImport );
+
+ virtual ~ScXMLTableShapesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableSourceContext.cxx b/sc/source/filter/xml/XMLTableSourceContext.cxx
new file mode 100644
index 0000000000..9f66f6f4a0
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableSourceContext.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 "XMLTableSourceContext.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+#include <docsh.hxx>
+#include "xmlsubti.hxx"
+#include <tablink.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sax/tools/converter.hxx>
+#include <com/sun/star/sheet/XSheetLinkable.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableSourceContext::ScXMLTableSourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nRefresh(0),
+ nMode(sheet::SheetLinkMode_NORMAL)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ sLink = GetScImport().GetAbsoluteReference(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_NAME ):
+ sTableName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_NAME):
+ sFilterName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OPTIONS ):
+ sFilterOptions = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_MODE ):
+ if (IsXMLToken(aIter, XML_COPY_RESULTS_ONLY))
+ nMode = sheet::SheetLinkMode_VALUE;
+ break;
+ case XML_ELEMENT( TABLE, XML_REFRESH_DELAY ):
+ double fTime;
+ if (::sax::Converter::convertDuration( fTime, aIter.toView() ))
+ nRefresh = std::max( static_cast<sal_Int32>(fTime * 86400.0), sal_Int32(0) );
+ break;
+ }
+ }
+}
+
+ScXMLTableSourceContext::~ScXMLTableSourceContext()
+{
+}
+
+void SAL_CALL ScXMLTableSourceContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (sLink.isEmpty())
+ return;
+
+ uno::Reference <sheet::XSheetLinkable> xLinkable (GetScImport().GetTables().GetCurrentXSheet(), uno::UNO_QUERY);
+ ScDocument* pDoc(GetScImport().GetDocument());
+ if (!(xLinkable.is() && pDoc))
+ return;
+
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ if (!pDoc->RenameTab( GetScImport().GetTables().GetCurrentSheet(),
+ GetScImport().GetTables().GetCurrentSheetName(), true/*bExternalDocument*/))
+ return;
+
+ sLink = ScGlobal::GetAbsDocName( sLink, pDoc->GetDocumentShell() );
+ if (sFilterName.isEmpty())
+ ScDocumentLoader::GetFilterName( sLink, sFilterName, sFilterOptions, false, false );
+
+ ScLinkMode nLinkMode = ScLinkMode::NONE;
+ if ( nMode == sheet::SheetLinkMode_NORMAL )
+ nLinkMode = ScLinkMode::NORMAL;
+ else if ( nMode == sheet::SheetLinkMode_VALUE )
+ nLinkMode = ScLinkMode::VALUE;
+
+ pDoc->SetLink( GetScImport().GetTables().GetCurrentSheet(),
+ nLinkMode, sLink, sFilterName, sFilterOptions,
+ sTableName, nRefresh );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableSourceContext.hxx b/sc/source/filter/xml/XMLTableSourceContext.hxx
new file mode 100644
index 0000000000..85bfb69296
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableSourceContext.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sheet/SheetLinkMode.hpp>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLTableSourceContext : public ScXMLImportContext
+{
+ OUString sLink;
+ OUString sTableName;
+ OUString sFilterName;
+ OUString sFilterOptions;
+ sal_Int32 nRefresh;
+ css::sheet::SheetLinkMode nMode;
+
+public:
+ ScXMLTableSourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableSourceContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTrackedChangesContext.cxx b/sc/source/filter/xml/XMLTrackedChangesContext.cxx
new file mode 100644
index 0000000000..e2e51ed314
--- /dev/null
+++ b/sc/source/filter/xml/XMLTrackedChangesContext.cxx
@@ -0,0 +1,1397 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 "XMLTrackedChangesContext.hxx"
+#include "XMLChangeTrackingImportHelper.hxx"
+#include "xmlimprt.hxx"
+#include "xmlconti.hxx"
+#include <formulacell.hxx>
+#include <textuno.hxx>
+#include <editutil.hxx>
+#include <document.hxx>
+#include <utility>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <comphelper/base64.hxx>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/text/ControlCharacter.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+namespace {
+
+class ScXMLChangeInfoContext : public ScXMLImportContext
+{
+ ScMyActionInfo aInfo;
+ OUStringBuffer sAuthorBuffer{32};
+ OUStringBuffer sDateTimeBuffer{32};
+ OUStringBuffer sCommentBuffer{64};
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ sal_uInt32 nParagraphCount;
+
+public:
+ ScXMLChangeInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLBigRangeContext : public ScXMLImportContext
+{
+public:
+ ScXMLBigRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScBigRange& rBigRange);
+};
+
+class ScXMLCellContentDeletionContext : public ScXMLImportContext
+{
+ OUString sFormulaAddress;
+ OUString sFormula;
+ OUString sFormulaNmsp;
+ OUString sInputString;
+ ScBigRange aBigRange;
+ double fValue;
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ ScCellValue maCell;
+ sal_uInt32 nID;
+ sal_Int32 nMatrixCols;
+ sal_Int32 nMatrixRows;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_uInt16 nType;
+ ScMatrixMode nMatrixFlag;
+
+public:
+ ScXMLCellContentDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDependenceContext : public ScXMLImportContext
+{
+public:
+ ScXMLDependenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+};
+
+class ScXMLDependingsContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLDependingsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLChangeDeletionContext : public ScXMLImportContext
+{
+public:
+ ScXMLChangeDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+};
+
+class ScXMLDeletionsContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLDeletionsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLChangeCellContext;
+
+class ScXMLChangeTextPContext : public ScXMLImportContext
+{
+ css::uno::Reference< css::xml::sax::XFastAttributeList> mxAttrList;
+ sal_Int32 mnElement;
+ OUStringBuffer sText;
+ ScXMLChangeCellContext* pChangeCellContext;
+ rtl::Reference<SvXMLImportContext>
+ pTextPContext;
+
+public:
+
+ ScXMLChangeTextPContext( ScXMLImport& rImport, sal_Int32 nElement,
+ css::uno::Reference<css::xml::sax::XFastAttributeList> xAttrList,
+ ScXMLChangeCellContext* pChangeCellContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class ScXMLChangeCellContext : public ScXMLImportContext
+{
+ ScCellValue& mrOldCell;
+
+ OUString sText;
+ OUString& rInputString;
+ rtl::Reference<ScEditEngineTextObj> mpEditTextObj;
+ double fValue;
+ sal_uInt16& rType;
+ bool bEmpty;
+ bool bFirstParagraph;
+ bool bString;
+ bool bFormula;
+
+public:
+ ScXMLChangeCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScCellValue& rOldCell, OUString& sAddress,
+ OUString& rFormula, OUString& rFormulaNmsp,
+ formula::FormulaGrammar::Grammar& rGrammar,
+ OUString& rInputString, double& fValue, sal_uInt16& nType,
+ ScMatrixMode& nMatrixFlag, sal_Int32& nMatrixCols, sal_Int32& nMatrixRows);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void CreateTextPContext(bool bIsNewParagraph);
+ bool IsEditCell() const { return mpEditTextObj.is(); }
+ void SetText(const OUString& sTempText) { sText = sTempText; }
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLPreviousContext : public ScXMLImportContext
+{
+ OUString sFormulaAddress;
+ OUString sFormula;
+ OUString sFormulaNmsp;
+ OUString sInputString;
+ double fValue;
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ ScCellValue maOldCell;
+ sal_uInt32 nID;
+ sal_Int32 nMatrixCols;
+ sal_Int32 nMatrixRows;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_uInt16 nType;
+ ScMatrixMode nMatrixFlag;
+
+public:
+ ScXMLPreviousContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLContentChangeContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ ScBigRange aBigRange;
+
+public:
+ ScXMLContentChangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLInsertionContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLInsertionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLInsertionCutOffContext : public ScXMLImportContext
+{
+public:
+ ScXMLInsertionCutOffContext( ScXMLImport& rImport,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+};
+
+class ScXMLMovementCutOffContext : public ScXMLImportContext
+{
+public:
+ ScXMLMovementCutOffContext( ScXMLImport& rImport,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+};
+
+class ScXMLCutOffsContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLCutOffsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDeletionContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLMovementContext : public ScXMLImportContext
+{
+ ScBigRange aSourceRange;
+ ScBigRange aTargetRange;
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLMovementContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLRejectionContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLRejectionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+}
+
+ScXMLTrackedChangesContext::ScXMLTrackedChangesContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ rImport.LockSolarMutex();
+
+ if ( !rAttrList.is() )
+ return;
+
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_PROTECTION_KEY ) ) );
+ if (aIter != rAttrList->end())
+ {
+ if( !aIter.isEmpty() )
+ {
+ uno::Sequence<sal_Int8> aPass;
+ ::comphelper::Base64::decode( aPass, aIter.toString() );
+ pChangeTrackingImportHelper->SetProtection(aPass);
+ }
+ }
+}
+
+ScXMLTrackedChangesContext::~ScXMLTrackedChangesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTrackedChangesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CELL_CONTENT_CHANGE ):
+ pContext = new ScXMLContentChangeContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_INSERTION ):
+ pContext = new ScXMLInsertionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETION ):
+ pContext = new ScXMLDeletionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_MOVEMENT ):
+ pContext = new ScXMLMovementContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTION ):
+ pContext = new ScXMLRejectionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLChangeInfoContext::ScXMLChangeInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ aInfo(),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper),
+ nParagraphCount(0)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ sal_Int32 nToken = aIter.getToken();
+ if ( nToken == XML_ELEMENT( OFFICE, XML_CHG_AUTHOR ) )
+ sAuthorBuffer = aIter.toString();
+ else if ( nToken == XML_ELEMENT( OFFICE, XML_CHG_DATE_TIME ) )
+ sDateTimeBuffer = aIter.toString();
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLChangeInfoContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if( nElement == XML_ELEMENT(DC, XML_CREATOR ) )
+ pContext = new ScXMLContentContext(GetScImport(), sAuthorBuffer);
+ else if( nElement == XML_ELEMENT(DC, XML_DATE ) )
+ pContext = new ScXMLContentContext(GetScImport(), sDateTimeBuffer);
+ else if (nElement == XML_ELEMENT(TEXT, XML_P))
+ {
+ if(nParagraphCount)
+ sCommentBuffer.append('\n');
+ ++nParagraphCount;
+ pContext = new ScXMLContentContext( GetScImport(), sCommentBuffer);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLChangeInfoContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ aInfo.sUser = sAuthorBuffer.makeStringAndClear();
+ if (!::sax::Converter::parseDateTime(aInfo.aDateTime, sDateTimeBuffer))
+ SAL_WARN("sc.filter", "ScXMLChangeInfoContext: broken DateTime '" << sDateTimeBuffer.toString() << "'");
+ sDateTimeBuffer.setLength(0);
+ aInfo.sComment = sCommentBuffer.makeStringAndClear();
+ pChangeTrackingImportHelper->SetActionInfo(aInfo);
+}
+
+ScXMLBigRangeContext::ScXMLBigRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScBigRange& rBigRange ) :
+ ScXMLImportContext( rImport )
+{
+ bool bColumn(false);
+ bool bRow(false);
+ bool bTable(false);
+ sal_Int32 nColumn(0);
+ sal_Int32 nRow(0);
+ sal_Int32 nTable(0);
+ sal_Int32 nStartColumn(0);
+ sal_Int32 nEndColumn(0);
+ sal_Int32 nStartRow(0);
+ sal_Int32 nEndRow(0);
+ sal_Int32 nStartTable(0);
+ sal_Int32 nEndTable(0);
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_COLUMN ):
+ nColumn = aIter.toInt32();
+ bColumn = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_ROW ):
+ nRow = aIter.toInt32();
+ bRow = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ nTable = aIter.toInt32();
+ bTable = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_START_COLUMN ):
+ nStartColumn = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_END_COLUMN ):
+ nEndColumn = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_START_ROW ):
+ nStartRow = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_END_ROW ):
+ nEndRow = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_START_TABLE ):
+ nStartTable = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_END_TABLE ):
+ nEndTable = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ if (bColumn)
+ nStartColumn = nEndColumn = nColumn;
+ if (bRow)
+ nStartRow = nEndRow = nRow;
+ if (bTable)
+ nStartTable = nEndTable = nTable;
+ rBigRange.Set(nStartColumn, nStartRow, nStartTable,
+ nEndColumn, nEndRow, nEndTable);
+}
+
+ScXMLCellContentDeletionContext::ScXMLCellContentDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper) :
+ ScXMLImportContext( rImport ),
+ fValue(0.0),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper),
+ nID(0),
+ nMatrixCols(0),
+ nMatrixRows(0),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nType(css::util::NumberFormat::ALL),
+ nMatrixFlag(ScMatrixMode::NONE)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellContentDeletionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CHANGE_TRACK_TABLE_CELL ):
+ pContext = new ScXMLChangeCellContext(GetScImport(), pAttribList,
+ maCell, sFormulaAddress, sFormula, sFormulaNmsp, eGrammar, sInputString, fValue, nType, nMatrixFlag, nMatrixCols, nMatrixRows );
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_ADDRESS ):
+ OSL_ENSURE(!nID, "a action with a ID should not contain a BigRange");
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aBigRange);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLCellContentDeletionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ std::unique_ptr<ScMyCellInfo> pCellInfo(new ScMyCellInfo(maCell, sFormulaAddress, sFormula, eGrammar, sInputString, fValue, nType,
+ nMatrixFlag, nMatrixCols, nMatrixRows));
+ if (nID)
+ pChangeTrackingImportHelper->AddDeleted(nID, std::move(pCellInfo));
+ else
+ pChangeTrackingImportHelper->AddGenerated(std::move(pCellInfo), aBigRange);
+}
+
+ScXMLDependenceContext::ScXMLDependenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString(aIter.toView());
+ }
+ pChangeTrackingImportHelper->AddDependence(nID);
+}
+
+ScXMLDependingsContext::ScXMLDependingsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ // here are no attributes
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDependingsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ // #i80033# read both old (dependence) and new (dependency) elements
+ if (nElement == XML_ELEMENT( TABLE, XML_DEPENDENCE ) ||
+ nElement == XML_ELEMENT( TABLE, XML_DEPENDENCY ))
+ {
+ pContext = new ScXMLDependenceContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ }
+
+ return pContext;
+}
+
+ScXMLChangeDeletionContext::ScXMLChangeDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+ pChangeTrackingImportHelper->AddDeleted(nID);
+}
+
+ScXMLDeletionsContext::ScXMLDeletionsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ // here are no attributes
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDeletionsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CHANGE_DELETION ):
+ pContext = new ScXMLChangeDeletionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_CONTENT_DELETION ):
+ pContext = new ScXMLCellContentDeletionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLChangeTextPContext::ScXMLChangeTextPContext( ScXMLImport& rImport,
+ sal_Int32 nElement,
+ css::uno::Reference<css::xml::sax::XFastAttributeList> xAttrList,
+ ScXMLChangeCellContext* pTempChangeCellContext) :
+ ScXMLImportContext( rImport ),
+ mxAttrList(std::move(xAttrList)),
+ mnElement(nElement),
+ pChangeCellContext(pTempChangeCellContext)
+{
+ // here are no attributes
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLChangeTextPContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext;
+ if (IsTokenInNamespace(mnElement, XML_NAMESPACE_TEXT)
+ && (nElement & TOKEN_MASK) == XML_S
+ && !pTextPContext)
+ {
+ sal_Int32 nRepeat(0);
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(mxAttrList) )
+ {
+ if (aIter.getToken() == XML_ELEMENT(TEXT, XML_C))
+ nRepeat = aIter.toInt32();
+ else
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ if (nRepeat)
+ for (sal_Int32 j = 0; j < nRepeat; ++j)
+ sText.append(' ');
+ else
+ sText.append(' ');
+ }
+ else
+ {
+ if (!pChangeCellContext->IsEditCell())
+ pChangeCellContext->CreateTextPContext(false);
+ bool bWasContext (true);
+ if (!pTextPContext)
+ {
+ bWasContext = false;
+ pTextPContext = GetScImport().GetTextImport()->CreateTextChildContext(
+ GetScImport(), mnElement, mxAttrList);
+ }
+ if (pTextPContext)
+ {
+ if (!bWasContext)
+ pTextPContext->characters(sText.makeStringAndClear());
+ xContext = pTextPContext->createFastChildContext(nElement, xAttrList);
+ }
+ }
+
+ return xContext;
+}
+
+void ScXMLChangeTextPContext::characters( const OUString& rChars )
+{
+ if (!pTextPContext)
+ sText.append(rChars);
+ else
+ pTextPContext->characters(rChars);
+}
+
+void ScXMLChangeTextPContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ if (!pTextPContext)
+ pChangeCellContext->SetText(sText.makeStringAndClear());
+}
+
+ScXMLChangeCellContext::ScXMLChangeCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScCellValue& rOldCell, OUString& rAddress,
+ OUString& rFormula, OUString& rFormulaNmsp,
+ formula::FormulaGrammar::Grammar& rGrammar,
+ OUString& rTempInputString, double& rDateTimeValue, sal_uInt16& nType,
+ ScMatrixMode& nMatrixFlag, sal_Int32& nMatrixCols, sal_Int32& nMatrixRows )
+ : ScXMLImportContext( rImport )
+ , mrOldCell(rOldCell)
+ , rInputString(rTempInputString)
+ , fValue(0.0)
+ , rType(nType)
+ , bEmpty(true)
+ , bFirstParagraph(true)
+ , bString(true)
+ , bFormula(false)
+{
+ bool bIsMatrix(false);
+ bool bIsCoveredMatrix(false);
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FORMULA ):
+ bEmpty = false;
+ GetScImport().ExtractFormulaNamespaceGrammar( rFormula, rFormulaNmsp, rGrammar, aIter.toString() );
+ bFormula = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_ADDRESS ):
+ rAddress = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_MATRIX_COVERED ):
+ bIsCoveredMatrix = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixCols = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixRows = aIter.toInt32();
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ if (IsXMLToken(aIter, XML_FLOAT))
+ bString = false;
+ else if (IsXMLToken(aIter, XML_DATE))
+ {
+ rType = css::util::NumberFormat::DATE;
+ bString = false;
+ }
+ else if (IsXMLToken(aIter, XML_TIME))
+ {
+ rType = css::util::NumberFormat::TIME;
+ bString = false;
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ fValue = aIter.toDouble();
+ bEmpty = false;
+ break;
+ case XML_ELEMENT( OFFICE, XML_DATE_VALUE ):
+ bEmpty = false;
+ if (GetScImport().GetMM100UnitConverter().setNullDate(GetScImport().GetModel()))
+ GetScImport().GetMM100UnitConverter().convertDateTime(rDateTimeValue, aIter.toView());
+ fValue = rDateTimeValue;
+ break;
+ case XML_ELEMENT( OFFICE, XML_TIME_VALUE ):
+ bEmpty = false;
+ ::sax::Converter::convertDuration(rDateTimeValue, aIter.toView());
+ fValue = rDateTimeValue;
+ }
+ }
+ }
+
+ if (bIsCoveredMatrix)
+ nMatrixFlag = ScMatrixMode::Reference;
+ else if (bIsMatrix && nMatrixRows && nMatrixCols)
+ nMatrixFlag = ScMatrixMode::Formula;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > ScXMLChangeCellContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if (nElement == XML_ELEMENT(TEXT, XML_P))
+ {
+ bEmpty = false;
+ if (bFirstParagraph)
+ {
+ pContext = new ScXMLChangeTextPContext(GetScImport(), nElement, xAttrList, this);
+ bFirstParagraph = false;
+ }
+ else
+ {
+ if (!mpEditTextObj.is())
+ CreateTextPContext(true);
+ pContext = GetScImport().GetTextImport()->CreateTextChildContext(
+ GetScImport(), nElement, xAttrList);
+ }
+ }
+ else
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+
+ return pContext;
+}
+
+void ScXMLChangeCellContext::CreateTextPContext(bool bIsNewParagraph)
+{
+ if (!GetScImport().GetDocument())
+ return;
+
+ mpEditTextObj = new ScEditEngineTextObj();
+ mpEditTextObj->GetEditEngine()->SetEditTextObjectPool(GetScImport().GetDocument()->GetEditPool());
+ uno::Reference <text::XText> xText(mpEditTextObj);
+ if (xText.is())
+ {
+ uno::Reference<text::XTextCursor> xTextCursor(xText->createTextCursor());
+ if (bIsNewParagraph)
+ {
+ xText->setString(sText);
+ xTextCursor->gotoEnd(false);
+ xText->insertControlCharacter(xTextCursor, text::ControlCharacter::PARAGRAPH_BREAK, false);
+ }
+ GetScImport().GetTextImport()->SetCursor(xTextCursor);
+ }
+}
+
+void SAL_CALL ScXMLChangeCellContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!bEmpty)
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (mpEditTextObj.is())
+ {
+ if (GetImport().GetTextImport()->GetCursor().is())
+ {
+ //GetImport().GetTextImport()->GetCursor()->gotoEnd(sal_False);
+ if( GetImport().GetTextImport()->GetCursor()->goLeft( 1, true ) )
+ {
+ GetImport().GetTextImport()->GetText()->insertString(
+ GetImport().GetTextImport()->GetCursorAsRange(), "",
+ true );
+ }
+ }
+
+ // The cell will own the text object instance.
+ mrOldCell.set(mpEditTextObj->CreateTextObject());
+ GetScImport().GetTextImport()->ResetCursor();
+ mpEditTextObj.clear();
+ }
+ else
+ {
+ if (!bFormula)
+ {
+ if (!sText.isEmpty() && bString)
+ {
+ mrOldCell.set(pDoc->GetSharedStringPool().intern(sText));
+ }
+ else
+ {
+ mrOldCell.set(fValue);
+ }
+ if (rType == css::util::NumberFormat::DATE || rType == css::util::NumberFormat::TIME)
+ rInputString = sText;
+ }
+ }
+ }
+ else
+ mrOldCell.clear();
+}
+
+ScXMLPreviousContext::ScXMLPreviousContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ fValue(0.0),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper),
+ nID(0),
+ nMatrixCols(0),
+ nMatrixRows(0),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nType(css::util::NumberFormat::ALL),
+ nMatrixFlag(ScMatrixMode::NONE)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLPreviousContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( TABLE, XML_CHANGE_TRACK_TABLE_CELL ) )
+ pContext = new ScXMLChangeCellContext(GetScImport(), pAttribList,
+ maOldCell, sFormulaAddress, sFormula, sFormulaNmsp, eGrammar, sInputString, fValue, nType, nMatrixFlag, nMatrixCols, nMatrixRows);
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLPreviousContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->SetPreviousChange(nID, new ScMyCellInfo(maOldCell, sFormulaAddress, sFormula, eGrammar, sInputString,
+ fValue, nType, nMatrixFlag, nMatrixCols, nMatrixRows));
+}
+
+ScXMLContentChangeContext::ScXMLContentChangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(SC_CAT_CONTENT);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentChangeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_ADDRESS ):
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aBigRange);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_PREVIOUS ):
+ pContext = new ScXMLPreviousContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLContentChangeContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->SetBigRange(aBigRange);
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLInsertionContext::ScXMLInsertionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ sal_Int32 nPosition(0);
+ sal_Int32 nCount(1);
+ sal_Int32 nTable(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+ ScChangeActionType nActionType(SC_CAT_INSERT_COLS);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TYPE ):
+ if (IsXMLToken( aIter, XML_ROW ))
+ nActionType = SC_CAT_INSERT_ROWS;
+ else if (IsXMLToken( aIter, XML_TABLE ))
+ nActionType = SC_CAT_INSERT_TABS;
+ break;
+ case XML_ELEMENT( TABLE, XML_POSITION ):
+ nPosition = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ nTable = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_COUNT ):
+ nCount = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(nActionType);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+ pChangeTrackingImportHelper->SetPosition(nPosition, nCount, nTable);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLInsertionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLInsertionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLInsertionCutOffContext::ScXMLInsertionCutOffContext( ScXMLImport& rImport,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ sal_Int32 nPosition(0);
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_ID):
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString(aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_POSITION):
+ ::sax::Converter::convertNumber(nPosition, aIter.toView());
+ break;
+ }
+ }
+ pChangeTrackingImportHelper->SetInsertionCutOff(nID, nPosition);
+}
+
+
+ScXMLMovementCutOffContext::ScXMLMovementCutOffContext( ScXMLImport& rImport,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ sal_Int32 nPosition(0);
+ sal_Int32 nStartPosition(0);
+ sal_Int32 nEndPosition(0);
+ bool bPosition(false);
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_ID):
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString(aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_POSITION):
+ bPosition = true;
+ ::sax::Converter::convertNumber(nPosition, aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_START_POSITION):
+ ::sax::Converter::convertNumber(nStartPosition, aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_END_POSITION):
+ ::sax::Converter::convertNumber(nEndPosition, aIter.toView());
+ break;
+ }
+ }
+ if (bPosition)
+ nStartPosition = nEndPosition = nPosition;
+ pChangeTrackingImportHelper->AddMoveCutOff(nID, nStartPosition, nEndPosition);
+}
+
+ScXMLCutOffsContext::ScXMLCutOffsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ // here are no attributes
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLCutOffsContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if (nElement == XML_ELEMENT(TABLE, XML_INSERTION_CUT_OFF))
+ pContext = new ScXMLInsertionCutOffContext(GetScImport(), xAttrList, pChangeTrackingImportHelper);
+ else if (nElement == XML_ELEMENT(TABLE, XML_MOVEMENT_CUT_OFF))
+ pContext = new ScXMLMovementCutOffContext(GetScImport(), xAttrList, pChangeTrackingImportHelper);
+
+ return pContext;
+}
+
+ScXMLDeletionContext::ScXMLDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ sal_Int32 nPosition(0);
+ sal_Int32 nMultiSpanned(0);
+ sal_Int32 nTable(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+ ScChangeActionType nActionType(SC_CAT_DELETE_COLS);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ sal_Int32 nToken = aIter.getToken();
+ switch (nToken)
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TYPE ):
+ if (IsXMLToken( aIter, XML_ROW ))
+ nActionType = SC_CAT_DELETE_ROWS;
+ else if (IsXMLToken( aIter, XML_TABLE ))
+ nActionType = SC_CAT_DELETE_TABS;
+ break;
+ case XML_ELEMENT( TABLE, XML_POSITION ):
+ nPosition = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ nTable = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_MULTI_DELETION_SPANNED ):
+ nMultiSpanned = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(nActionType);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+ pChangeTrackingImportHelper->SetPosition(nPosition, 1, nTable);
+ pChangeTrackingImportHelper->SetMultiSpanned(static_cast<sal_Int16>(nMultiSpanned));
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDeletionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CUT_OFFS ):
+ case XML_ELEMENT( TABLE, XML_CUT_OFFS2 ): // cut_offs
+ pContext = new ScXMLCutOffsContext(GetScImport(), pChangeTrackingImportHelper);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDeletionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLMovementContext::ScXMLMovementContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(SC_CAT_MOVE);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLMovementContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_RANGE_ADDRESS ):
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aSourceRange);
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aTargetRange);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLMovementContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->SetMoveRanges(aSourceRange, aTargetRange);
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLRejectionContext::ScXMLRejectionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(SC_CAT_MOVE);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLRejectionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLRejectionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTrackedChangesContext.hxx b/sc/source/filter/xml/XMLTrackedChangesContext.hxx
new file mode 100644
index 0000000000..5ed8570ad0
--- /dev/null
+++ b/sc/source/filter/xml/XMLTrackedChangesContext.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser
+{
+class FastAttributeList;
+}
+
+class ScXMLChangeTrackingImportHelper;
+
+class ScXMLTrackedChangesContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLTrackedChangesContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+ virtual ~ScXMLTrackedChangesContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/cachedattraccess.cxx b/sc/source/filter/xml/cachedattraccess.cxx
new file mode 100644
index 0000000000..b87fc014f3
--- /dev/null
+++ b/sc/source/filter/xml/cachedattraccess.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/.
+ */
+
+#include "cachedattraccess.hxx"
+#include <document.hxx>
+
+ScXMLCachedRowAttrAccess::Cache::Cache() :
+ mnTab(-1), mnRow1(-1), mnRow2(-1), mbValue(false) {}
+
+bool ScXMLCachedRowAttrAccess::Cache::hasCache(sal_Int32 nTab, sal_Int32 nRow) const
+{
+ return mnTab == nTab && mnRow1 <= nRow && nRow <= mnRow2;
+}
+
+ScXMLCachedRowAttrAccess::ScXMLCachedRowAttrAccess(ScDocument* pDoc) :
+ mpDoc(pDoc) {}
+
+bool ScXMLCachedRowAttrAccess::rowHidden(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow)
+{
+ if (!maHidden.hasCache(nTab, nRow))
+ {
+ SCROW nRow1, nRow2;
+ maHidden.mbValue = mpDoc->RowHidden(
+ static_cast<SCROW>(nRow), static_cast<SCTAB>(nTab), &nRow1, &nRow2);
+ maHidden.mnTab = nTab;
+ maHidden.mnRow1 = static_cast<sal_Int32>(nRow1);
+ maHidden.mnRow2 = static_cast<sal_Int32>(nRow2);
+ }
+
+ nEndRow = maHidden.mnRow2;
+ return maHidden.mbValue;
+}
+
+bool ScXMLCachedRowAttrAccess::rowFiltered(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow)
+{
+ if (!maFiltered.hasCache(nTab, nRow))
+ {
+ SCROW nRow1, nRow2;
+ maFiltered.mbValue = mpDoc->RowFiltered(
+ static_cast<SCROW>(nRow), static_cast<SCTAB>(nTab), &nRow1, &nRow2);
+ maFiltered.mnTab = nTab;
+ maFiltered.mnRow1 = static_cast<sal_Int32>(nRow1);
+ maFiltered.mnRow2 = static_cast<sal_Int32>(nRow2);
+ }
+ nEndRow = maFiltered.mnRow2;
+ return maFiltered.mbValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/cachedattraccess.hxx b/sc/source/filter/xml/cachedattraccess.hxx
new file mode 100644
index 0000000000..1af7c8b6b6
--- /dev/null
+++ b/sc/source/filter/xml/cachedattraccess.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+class ScDocument;
+
+/**
+ * Wrapper for accessing hidden and filtered row attributes. It caches last
+ * accessed values for a current range, to avoid fetching values for every
+ * single row.
+ */
+class ScXMLCachedRowAttrAccess
+{
+ struct Cache
+ {
+ sal_Int32 mnTab;
+ sal_Int32 mnRow1;
+ sal_Int32 mnRow2;
+ bool mbValue;
+ Cache();
+ bool hasCache(sal_Int32 nTab, sal_Int32 nRow) const;
+ };
+
+public:
+ explicit ScXMLCachedRowAttrAccess(ScDocument* pDoc);
+
+ bool rowHidden(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow);
+ bool rowFiltered(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow);
+private:
+ Cache maHidden;
+ Cache maFiltered;
+ ScDocument* mpDoc;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/celltextparacontext.cxx b/sc/source/filter/xml/celltextparacontext.cxx
new file mode 100644
index 0000000000..26293e7d8d
--- /dev/null
+++ b/sc/source/filter/xml/celltextparacontext.cxx
@@ -0,0 +1,454 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "celltextparacontext.hxx"
+#include "xmlimprt.hxx"
+#include "xmlcelli.hxx"
+
+#include <comphelper/string.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLCellTextParaContext::ScXMLCellTextParaContext(
+ ScXMLImport& rImport, ScXMLTableRowCellContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void SAL_CALL ScXMLCellTextParaContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!maContent.isEmpty())
+ mrParentCxt.PushParagraphSpan(maContent, OUString());
+
+ mrParentCxt.PushParagraphEnd();
+}
+
+void SAL_CALL ScXMLCellTextParaContext::characters( const OUString& rChars )
+{
+ maContent += rChars;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellTextParaContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ if (!maContent.isEmpty())
+ {
+ mrParentCxt.PushParagraphSpan(maContent, OUString());
+ maContent.clear();
+ }
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_S ):
+ return new ScXMLCellFieldSContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_SPAN ):
+ return new ScXMLCellTextSpanContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_SHEET_NAME ):
+ return new ScXMLCellFieldSheetNameContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_DATE ):
+ return new ScXMLCellFieldDateContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_TITLE ):
+ return new ScXMLCellFieldTitleContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_A ):
+ return new ScXMLCellFieldURLContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_RUBY ):
+ return new ScXMLCellTextRubyContext(GetScImport(), *this);
+ case XML_ELEMENT(TEXT, XML_TAB):
+ maContent += "\t";
+ break;
+ case XML_ELEMENT(TEXT, XML_LINE_BREAK):
+ maContent += "\x0a";
+ break;
+ case XML_ELEMENT(TEXT, XML_BOOKMARK):
+ case XML_ELEMENT(TEXT, XML_BOOKMARK_START):
+ case XML_ELEMENT(TEXT, XML_BOOKMARK_END):
+ // TODO: ooo95423-1 [file.ods] and tdf#116079-3 have these bookmarks.
+ // Is this valid, and how can we prevent losing these?
+ break;
+ default:
+ SAL_WARN("sc","unknown text element["<<nElement<<"]["<<SvXMLImport::getNameFromToken(nElement )<<"] lost");
+ }
+
+ return nullptr;
+}
+
+void ScXMLCellTextParaContext::PushSpan(std::u16string_view aSpan, const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphSpan(aSpan, rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldSheetName(const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphFieldSheetName(rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldDate(const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphFieldDate(rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldTitle(const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphFieldDocTitle(rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldURL(
+ const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame)
+{
+ mrParentCxt.PushParagraphFieldURL(rURL, rRep, rStyleName, rTargetFrame);
+}
+
+ScXMLCellTextSpanContext::ScXMLCellTextSpanContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void SAL_CALL ScXMLCellTextSpanContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_STYLE_NAME ):
+ maStyleName = aIter.toString();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellTextSpanContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ submitContentAndClear();
+}
+
+void SAL_CALL ScXMLCellTextSpanContext::characters( const OUString& rChars )
+{
+ maContent += rChars;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellTextSpanContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ submitContentAndClear();
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_SHEET_NAME ):
+ {
+ ScXMLCellFieldSheetNameContext* p = new ScXMLCellFieldSheetNameContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_DATE ):
+ {
+ ScXMLCellFieldDateContext* p = new ScXMLCellFieldDateContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_TITLE ):
+ {
+ ScXMLCellFieldTitleContext* p = new ScXMLCellFieldTitleContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_A ):
+ {
+ ScXMLCellFieldURLContext* p = new ScXMLCellFieldURLContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_S ):
+ {
+ ScXMLCellFieldSContext* p = new ScXMLCellFieldSContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ default:
+ ;
+ }
+
+ return nullptr;
+}
+
+void ScXMLCellTextSpanContext::submitContentAndClear()
+{
+ if (!maContent.isEmpty())
+ {
+ mrParentCxt.PushSpan(maContent, maStyleName);
+ maContent.clear();
+ }
+}
+
+ScXMLCellFieldSheetNameContext::ScXMLCellFieldSheetNameContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldSheetNameContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldSheetNameContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldSheetName(maStyleName);
+}
+
+ScXMLCellFieldDateContext::ScXMLCellFieldDateContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldDateContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldDateContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldDate(maStyleName);
+}
+
+ScXMLCellFieldTitleContext::ScXMLCellFieldTitleContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldTitleContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldTitleContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldTitle(maStyleName);
+}
+
+ScXMLCellFieldURLContext::ScXMLCellFieldURLContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldURLContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldURLContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ maURL = aIter.toString();
+ break;
+ case XML_ELEMENT( XLINK, XML_TYPE ):
+ // Ignored for now.
+ break;
+ case XML_ELEMENT( OFFICE, XML_TARGET_FRAME_NAME ):
+ maTargetFrame = aIter.toString();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellFieldURLContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldURL(maURL, maRep, maStyleName, maTargetFrame);
+}
+
+void SAL_CALL ScXMLCellFieldURLContext::characters( const OUString& rChars )
+{
+ maRep += rChars;
+}
+
+ScXMLCellFieldSContext::ScXMLCellFieldSContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent),
+ mnCount(1)
+{
+}
+
+void ScXMLCellFieldSContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldSContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_C ):
+ mnCount = aIter.toInt32();
+ if (mnCount <= 0)
+ mnCount = 1; // worth a warning?
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellFieldSContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (mnCount)
+ PushSpaces();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellFieldSContext::createFastChildContext(
+ sal_Int32 /*nElement*/, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ // <text:s> does not have child elements, but ...
+ if (mnCount)
+ {
+ PushSpaces();
+ mnCount = 0;
+ }
+
+ return nullptr;
+}
+
+void ScXMLCellFieldSContext::PushSpaces()
+{
+ if (mnCount > 0)
+ {
+ if (mnCount == 1)
+ mrParentCxt.PushSpan(u" ", maStyleName);
+ else
+ {
+ OUStringBuffer aBuf( mnCount);
+ comphelper::string::padToLength( aBuf, mnCount, ' ');
+ mrParentCxt.PushSpan( aBuf.makeStringAndClear(), maStyleName);
+ }
+ }
+}
+
+ScXMLCellTextRubyContext::ScXMLCellTextRubyContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void SAL_CALL ScXMLCellTextRubyContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_STYLE_NAME ):
+ // This is ruby style instead of text style.
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellTextRubyContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_RUBY_BASE ):
+ {
+ ScXMLCellRubyBaseContext* p = new ScXMLCellRubyBaseContext(GetScImport(), mrParentCxt);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_RUBY_TEXT ):
+ {
+ ScXMLCellRubyTextContext* p = new ScXMLCellRubyTextContext(GetScImport(), maRubyText, maRubyTextStyle);
+ return p;
+ }
+ default:
+ ;
+ }
+
+ return nullptr;
+}
+
+ScXMLCellRubyBaseContext::ScXMLCellRubyBaseContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLCellTextSpanContext( rImport, rParent),
+ mrParentCxt(rParent)
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellRubyBaseContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ submitContentAndClear();
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_SPAN ):
+ return new ScXMLCellTextSpanContext(GetScImport(), mrParentCxt);
+ default:
+ ;
+ }
+ return nullptr;
+}
+
+ScXMLCellRubyTextContext::ScXMLCellRubyTextContext(
+ ScXMLImport& rImport, OUString& rRubyText, OUString& rRubyTextStyle) :
+ ScXMLImportContext(rImport),
+ mrRubyText(rRubyText),
+ mrRubyTextStyle(rRubyTextStyle)
+{
+}
+
+void SAL_CALL ScXMLCellRubyTextContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_STYLE_NAME ):
+ mrRubyTextStyle = aIter.toString();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellRubyTextContext::characters( const OUString& rChars )
+{
+ mrRubyText+= rChars;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/celltextparacontext.hxx b/sc/source/filter/xml/celltextparacontext.hxx
new file mode 100644
index 0000000000..8a230c56f1
--- /dev/null
+++ b/sc/source/filter/xml/celltextparacontext.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLImport;
+class ScXMLTableRowCellContext;
+
+/**
+ * This context handles <text:p> element inside <table:table-cell>.
+ */
+class ScXMLCellTextParaContext : public ScXMLImportContext
+{
+ ScXMLTableRowCellContext& mrParentCxt;
+ OUString maContent;
+public:
+ ScXMLCellTextParaContext(ScXMLImport& rImport, ScXMLTableRowCellContext& rParent);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void PushSpan(std::u16string_view aSpan, const OUString& rStyleName);
+ void PushFieldSheetName(const OUString& rStyleName);
+ void PushFieldDate(const OUString& rStyleName);
+ void PushFieldTitle(const OUString& rStyleName);
+ void PushFieldURL(const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame);
+};
+
+/**
+ * This context handles <text:span> element inside <text:p>.
+ */
+class ScXMLCellTextSpanContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+ OUString maContent;
+public:
+ ScXMLCellTextSpanContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ void submitContentAndClear();
+};
+
+/**
+ * This context handles <text:sheet-name> element inside <text:p>.
+ */
+class ScXMLCellFieldSheetNameContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+public:
+ ScXMLCellFieldSheetNameContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/**
+ * This context handles <text:date> element inside <text:p>.
+ */
+class ScXMLCellFieldDateContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+public:
+ ScXMLCellFieldDateContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/**
+ * This context handles <text:title> element inside <text:p>.
+ */
+class ScXMLCellFieldTitleContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+public:
+ ScXMLCellFieldTitleContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/**
+ * This context handles <text:a> element inside <text:p> or <text:span>.
+ */
+class ScXMLCellFieldURLContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+ OUString maURL;
+ OUString maRep;
+ OUString maTargetFrame;
+public:
+ ScXMLCellFieldURLContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+};
+
+/**
+ * This context handles <text:s> element inside <text:p> or <text:span>.
+ */
+class ScXMLCellFieldSContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+ sal_Int32 mnCount;
+
+ void PushSpaces();
+public:
+ ScXMLCellFieldSContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/**
+ * This context handles <text:ruby> element inside <text:p>.
+ */
+class ScXMLCellTextRubyContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maRubyTextStyle;
+ OUString maRubyText;
+public:
+ ScXMLCellTextRubyContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/**
+ * This context handles <text:ruby-base> element inside <text:ruby>.
+ */
+class ScXMLCellRubyBaseContext : public ScXMLCellTextSpanContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+public:
+ ScXMLCellRubyBaseContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/**
+ * This context handles <text:ruby-text> element inside <text:ruby>.
+ */
+class ScXMLCellRubyTextContext : public ScXMLImportContext
+{
+ OUString& mrRubyText;
+ OUString& mrRubyTextStyle;
+public:
+ ScXMLCellRubyTextContext(ScXMLImport& rImport, OUString& rRubyText, OUString& rRubyTextStyle);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/datastreamimport.cxx b/sc/source/filter/xml/datastreamimport.cxx
new file mode 100644
index 0000000000..f749e6178f
--- /dev/null
+++ b/sc/source/filter/xml/datastreamimport.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/.
+ */
+
+#include "datastreamimport.hxx"
+#include "xmlimprt.hxx"
+
+#include <rangeutl.hxx>
+#include <importfilterdata.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <formula/grammar.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLDataStreamContext::ScXMLDataStreamContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext(rImport),
+ mbRefreshOnEmpty(false),
+ meInsertPos(sc::ImportPostProcessData::DataStream::InsertBottom)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch ( aIter.getToken() )
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ maURL = GetScImport().GetAbsoluteReference( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ sal_Int32 nOffset = 0;
+ if (!ScRangeStringConverter::GetRangeFromString(
+ maRange, aIter.toString(), *pDoc, formula::FormulaGrammar::CONV_OOO, nOffset))
+ maRange.SetInvalid();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_EMPTY_LINE_REFRESH ):
+ mbRefreshOnEmpty = IsXMLToken( aIter, XML_TRUE );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_INSERTION_POSITION ):
+ meInsertPos = IsXMLToken( aIter, XML_TOP ) ?
+ sc::ImportPostProcessData::DataStream::InsertTop :
+ sc::ImportPostProcessData::DataStream::InsertBottom;
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+ScXMLDataStreamContext::~ScXMLDataStreamContext() {}
+
+void SAL_CALL ScXMLDataStreamContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!maRange.IsValid())
+ // Range must be valid.
+ return;
+
+ sc::ImportPostProcessData* pData = GetScImport().GetPostProcessData();
+ if (!pData)
+ return;
+
+ pData->mpDataStream.reset(new sc::ImportPostProcessData::DataStream);
+ sc::ImportPostProcessData::DataStream& rData = *pData->mpDataStream;
+
+ rData.maURL = maURL;
+ rData.maRange = maRange;
+ rData.mbRefreshOnEmpty = mbRefreshOnEmpty;
+ rData.meInsertPos = meInsertPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/datastreamimport.hxx b/sc/source/filter/xml/datastreamimport.hxx
new file mode 100644
index 0000000000..de6135488f
--- /dev/null
+++ b/sc/source/filter/xml/datastreamimport.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+#include <importfilterdata.hxx>
+#include <address.hxx>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLDataStreamContext : public ScXMLImportContext
+{
+ OUString maURL;
+ ScRange maRange;
+ bool mbRefreshOnEmpty;
+ sc::ImportPostProcessData::DataStream::InsertPos meInsertPos;
+
+public:
+ ScXMLDataStreamContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLDataStreamContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/editattributemap.cxx b/sc/source/filter/xml/editattributemap.cxx
new file mode 100644
index 0000000000..4b30edf140
--- /dev/null
+++ b/sc/source/filter/xml/editattributemap.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/.
+ */
+
+#include "editattributemap.hxx"
+
+#include <editeng/eeitem.hxx>
+#include <editeng/memberids.h>
+#include <xmloff/xmlnamespace.hxx>
+
+ScXMLEditAttributeMap::Entry const aEntries[] = {
+
+ { XML_NAMESPACE_FO, "color", "CharColor", EE_CHAR_COLOR, 0 },
+ { XML_NAMESPACE_STYLE, "font-charset", "CharFontCharSet", EE_CHAR_FONTINFO, MID_FONT_CHAR_SET },
+ { XML_NAMESPACE_STYLE, "font-charset-asian", "CharFontCharSetAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_CHAR_SET },
+ { XML_NAMESPACE_STYLE, "font-charset-complex", "CharFontCharSetComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_CHAR_SET },
+ { XML_NAMESPACE_FO, "font-family", "CharFontName", EE_CHAR_FONTINFO, MID_FONT_FAMILY_NAME },
+ { XML_NAMESPACE_STYLE, "font-family-asian", "CharFontNameAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_FAMILY_NAME },
+ { XML_NAMESPACE_STYLE, "font-family-complex", "CharFontNameComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_FAMILY_NAME },
+ { XML_NAMESPACE_STYLE, "font-family-generic", "CharFontFamily", EE_CHAR_FONTINFO, MID_FONT_FAMILY },
+ { XML_NAMESPACE_STYLE, "font-family-generic-asian", "CharFontFamilyAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_FAMILY },
+ { XML_NAMESPACE_STYLE, "font-family-generic-complex", "CharFontFamilyComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_FAMILY },
+ { XML_NAMESPACE_STYLE, "font-pitch", "CharFontPitch", EE_CHAR_FONTINFO, MID_FONT_PITCH },
+ { XML_NAMESPACE_STYLE, "font-pitch-asian", "CharFontPitchAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_PITCH },
+ { XML_NAMESPACE_STYLE, "font-pitch-complex", "CharFontPitchComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_PITCH },
+ { XML_NAMESPACE_FO, "font-size", "CharHeight", EE_CHAR_FONTHEIGHT, MID_FONTHEIGHT },
+ { XML_NAMESPACE_STYLE, "font-size-asian", "CharHeightAsian", EE_CHAR_FONTHEIGHT_CJK, MID_FONTHEIGHT },
+ { XML_NAMESPACE_STYLE, "font-size-complex", "CharHeightComplex", EE_CHAR_FONTHEIGHT_CTL, MID_FONTHEIGHT },
+ { XML_NAMESPACE_FO, "font-style", "CharPosture", EE_CHAR_ITALIC, MID_POSTURE },
+ { XML_NAMESPACE_STYLE, "font-style-asian", "CharPostureAsian", EE_CHAR_ITALIC_CJK, MID_POSTURE },
+ { XML_NAMESPACE_STYLE, "font-style-complex", "CharPostureComplex", EE_CHAR_ITALIC_CTL, MID_POSTURE },
+ { XML_NAMESPACE_STYLE, "font-style-name", "CharFontStyleName", EE_CHAR_FONTINFO, MID_FONT_STYLE_NAME },
+ { XML_NAMESPACE_STYLE, "font-style-name-asian", "CharFontStyleNameAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_STYLE_NAME },
+ { XML_NAMESPACE_STYLE, "font-style-name-complex", "CharFontStyleNameComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_STYLE_NAME },
+ { XML_NAMESPACE_FO, "font-weight", "CharWeight", EE_CHAR_WEIGHT, MID_WEIGHT },
+ { XML_NAMESPACE_STYLE, "font-weight-asian", "CharWeightAsian", EE_CHAR_WEIGHT_CJK, MID_WEIGHT },
+ { XML_NAMESPACE_STYLE, "font-weight-complex", "CharWeightComplex", EE_CHAR_WEIGHT_CTL, MID_WEIGHT },
+ { XML_NAMESPACE_STYLE, "text-overline-width", "CharOverline", EE_CHAR_OVERLINE, MID_TL_STYLE },
+ { XML_NAMESPACE_STYLE, "text-overline-color", "CharOverlineColor", EE_CHAR_OVERLINE, MID_TL_COLOR },
+ { XML_NAMESPACE_STYLE, "text-overline-color", "CharOverlineHasColor", EE_CHAR_OVERLINE, MID_TL_HASCOLOR },
+ { XML_NAMESPACE_STYLE, "text-underline-width", "CharUnderline", EE_CHAR_UNDERLINE, MID_TL_STYLE },
+ { XML_NAMESPACE_STYLE, "text-underline-color", "CharUnderlineColor", EE_CHAR_UNDERLINE, MID_TL_COLOR },
+ { XML_NAMESPACE_STYLE, "text-underline-color", "CharUnderlineHasColor", EE_CHAR_UNDERLINE, MID_TL_HASCOLOR },
+ { XML_NAMESPACE_STYLE, "text-line-through-mode", "CharWordMode", EE_CHAR_WLM, 0 },
+ { XML_NAMESPACE_STYLE, "text-line-through-type", "CharStrikeout", EE_CHAR_STRIKEOUT, MID_CROSS_OUT },
+ { XML_NAMESPACE_STYLE, "font-relief", "CharRelief", EE_CHAR_RELIEF, MID_RELIEF },
+ { XML_NAMESPACE_STYLE, "text-outline", "CharContoured", EE_CHAR_OUTLINE, 0 },
+ { XML_NAMESPACE_FO, "text-shadow", "CharShadowed", EE_CHAR_SHADOW, 0 },
+ { XML_NAMESPACE_FO, "letter-spacing", "CharKerning", EE_CHAR_KERNING, 0 },
+ { XML_NAMESPACE_STYLE, "letter-kerning", "CharAutoKerning", EE_CHAR_PAIRKERNING, 0 },
+ { XML_NAMESPACE_STYLE, "text-scale", "CharScaleWidth", EE_CHAR_FONTWIDTH, 0 },
+ { XML_NAMESPACE_STYLE, "text-position", "CharEscapement", EE_CHAR_ESCAPEMENT, MID_ESC },
+ { XML_NAMESPACE_STYLE, "text-position", "CharEscapementHeight", EE_CHAR_ESCAPEMENT, MID_ESC_HEIGHT },
+ { XML_NAMESPACE_STYLE, "text-emphasize", "CharEmphasis", EE_CHAR_EMPHASISMARK, MID_EMPHASIS },
+ // The following 3 "country" entries are just placeholders for language,
+ // country, script and rfc-language-tag, which all map to CharLocale,
+ // EE_CHAR_LANGUAGE and MID_LANG_LOCALE and are handled individually.
+ { XML_NAMESPACE_FO, "country", "CharLocale", EE_CHAR_LANGUAGE, MID_LANG_LOCALE },
+ { XML_NAMESPACE_STYLE, "country-asian", "CharLocaleAsian", EE_CHAR_LANGUAGE_CJK, MID_LANG_LOCALE },
+ { XML_NAMESPACE_STYLE, "country-complex", "CharLocaleComplex", EE_CHAR_LANGUAGE_CTL, MID_LANG_LOCALE },
+};
+
+ScXMLEditAttributeMap::ScXMLEditAttributeMap()
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aEntries); ++i)
+ {
+ maAPIEntries.emplace(
+ OUString::createFromAscii(aEntries[i].mpAPIName), &aEntries[i]);
+
+ maItemIDEntries.emplace(aEntries[i].mnItemID, &aEntries[i]);
+ }
+}
+
+const ScXMLEditAttributeMap::Entry* ScXMLEditAttributeMap::getEntryByAPIName(const OUString& rAPIName) const
+{
+ StrToEntriesType::const_iterator it = maAPIEntries.find(rAPIName);
+ return it == maAPIEntries.end() ? nullptr : it->second;
+}
+
+const ScXMLEditAttributeMap::Entry* ScXMLEditAttributeMap::getEntryByItemID(sal_uInt16 nItemID) const
+{
+ IndexToEntriesType::const_iterator it = maItemIDEntries.find(nItemID);
+ return it == maItemIDEntries.end() ? nullptr : it->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/editattributemap.hxx b/sc/source/filter/xml/editattributemap.hxx
new file mode 100644
index 0000000000..7139121910
--- /dev/null
+++ b/sc/source/filter/xml/editattributemap.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+#include <unordered_map>
+
+/**
+ * Provide mapping from ODF text formatting styles to EditEngine's, for
+ * rich-text cell content import.
+ */
+class ScXMLEditAttributeMap
+{
+public:
+ struct Entry
+ {
+ sal_uInt16 nmXMLNS;
+ const char* mpXMLName;
+ const char* mpAPIName;
+ sal_uInt16 mnItemID;
+ sal_uInt8 mnFlag;
+ };
+
+ ScXMLEditAttributeMap();
+
+ const Entry* getEntryByAPIName(const OUString& rAPIName) const;
+ const Entry* getEntryByItemID(sal_uInt16 nItemID) const;
+
+private:
+ typedef std::unordered_map<OUString, const Entry*> StrToEntriesType;
+ typedef std::unordered_map<sal_uInt16, const Entry*> IndexToEntriesType;
+ StrToEntriesType maAPIEntries;
+ IndexToEntriesType maItemIDEntries;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/importcontext.cxx b/sc/source/filter/xml/importcontext.cxx
new file mode 100644
index 0000000000..ef493f23b4
--- /dev/null
+++ b/sc/source/filter/xml/importcontext.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/.
+ */
+
+#include "importcontext.hxx"
+#include "xmlimprt.hxx"
+
+ScXMLImportContext::ScXMLImportContext(SvXMLImport& rImport ) :
+ SvXMLImportContext( rImport )
+{
+}
+
+ScXMLImport& ScXMLImportContext::GetScImport()
+{
+ return static_cast<ScXMLImport&>(GetImport());
+}
+
+const ScXMLImport& ScXMLImportContext::GetScImport() const
+{
+ return static_cast<const ScXMLImport&>(GetImport());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/importcontext.hxx b/sc/source/filter/xml/importcontext.hxx
new file mode 100644
index 0000000000..d070433b2d
--- /dev/null
+++ b/sc/source/filter/xml/importcontext.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <xmloff/xmlictxt.hxx>
+
+class ScXMLImport;
+
+/**
+ * This class exists only to provide GetScImport() to its derived classes.
+ */
+class ScXMLImportContext : public SvXMLImportContext
+{
+public:
+ ScXMLImportContext(SvXMLImport& rImport);
+
+protected:
+ ScXMLImport& GetScImport();
+ const ScXMLImport& GetScImport() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/pivotsource.cxx b/sc/source/filter/xml/pivotsource.cxx
new file mode 100644
index 0000000000..15e868c646
--- /dev/null
+++ b/sc/source/filter/xml/pivotsource.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "pivotsource.hxx"
+
+#include <dpsave.hxx>
+
+#include <algorithm>
+
+namespace sc {
+
+PivotTableSources::SelectedPages::SelectedPages( ScDPObject* pObj, SelectedPagesType&& rSelected ) :
+ mpDP(pObj), maSelectedPages(std::move(rSelected)) {}
+
+PivotTableSources::SheetSource::SheetSource( ScDPObject* pObj, ScSheetSourceDesc aDesc ) :
+ mpDP(pObj), maDesc(std::move(aDesc)) {}
+
+PivotTableSources::DBSource::DBSource( ScDPObject* pObj, ScImportSourceDesc aDesc ) :
+ mpDP(pObj), maDesc(std::move(aDesc)) {}
+
+PivotTableSources::ServiceSource::ServiceSource( ScDPObject* pObj, ScDPServiceDesc aDesc ) :
+ mpDP(pObj), maDesc(std::move(aDesc)) {}
+
+PivotTableSources::PivotTableSources() {}
+
+void PivotTableSources::appendSheetSource( ScDPObject* pObj, const ScSheetSourceDesc& rDesc )
+{
+ maSheetSources.emplace_back(pObj, rDesc);
+}
+
+void PivotTableSources::appendDBSource( ScDPObject* pObj, const ScImportSourceDesc& rDesc )
+{
+ maDBSources.emplace_back(pObj, rDesc);
+}
+
+void PivotTableSources::appendServiceSource( ScDPObject* pObj, const ScDPServiceDesc& rDesc )
+{
+ maServiceSources.emplace_back(pObj, rDesc);
+}
+
+void PivotTableSources::appendSelectedPages( ScDPObject* pObj, SelectedPagesType&& rSelected )
+{
+ if (rSelected.empty())
+ return;
+
+ maSelectedPagesList.emplace_back(pObj, std::move(rSelected));
+}
+
+namespace {
+
+struct SelectedPageProcessor
+{
+ void operator() ( PivotTableSources::SelectedPages& rItem )
+ {
+ // Set selected pages after building all dimension members.
+ if (!rItem.mpDP)
+ return;
+
+ rItem.mpDP->BuildAllDimensionMembers();
+ ScDPSaveData* pSaveData = rItem.mpDP->GetSaveData();
+ if (!pSaveData)
+ return;
+
+ for (const auto& [rDimName, rSelected] : rItem.maSelectedPages)
+ {
+ ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName(rDimName);
+ if (!pDim)
+ continue;
+
+ pDim->SetCurrentPage(&rSelected);
+ }
+ }
+};
+
+struct PivotSheetDescSetter
+{
+ void operator() ( sc::PivotTableSources::SheetSource& rSrc )
+ {
+ ScDPObject* pObj = rSrc.mpDP;
+ pObj->SetSheetDesc(rSrc.maDesc);
+ }
+};
+
+struct PivotDBDescSetter
+{
+ void operator() ( sc::PivotTableSources::DBSource& rSrc )
+ {
+ ScDPObject* pObj = rSrc.mpDP;
+ pObj->SetImportDesc(rSrc.maDesc);
+ }
+};
+
+struct PivotServiceDataSetter
+{
+ void operator() ( sc::PivotTableSources::ServiceSource& rSrc )
+ {
+ ScDPObject* pObj = rSrc.mpDP;
+ pObj->SetServiceData(rSrc.maDesc);
+ }
+};
+
+}
+
+void PivotTableSources::process()
+{
+ std::for_each(maSheetSources.begin(), maSheetSources.end(), PivotSheetDescSetter());
+ std::for_each(maDBSources.begin(), maDBSources.end(), PivotDBDescSetter());
+ std::for_each(maServiceSources.begin(), maServiceSources.end(), PivotServiceDataSetter());
+ std::for_each(maSelectedPagesList.begin(), maSelectedPagesList.end(), SelectedPageProcessor());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/pivotsource.hxx b/sc/source/filter/xml/pivotsource.hxx
new file mode 100644
index 0000000000..e94fed8534
--- /dev/null
+++ b/sc/source/filter/xml/pivotsource.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/.
+ */
+
+#pragma once
+
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <dpobject.hxx>
+
+#include <unordered_map>
+#include <vector>
+
+namespace sc
+{
+/**
+ * Store pivot table data that need to be post-processed at the end of the
+ * import.
+ */
+struct PivotTableSources
+{
+ typedef std::unordered_map<OUString, OUString> SelectedPagesType;
+
+ struct SelectedPages
+ {
+ ScDPObject* mpDP;
+ SelectedPagesType maSelectedPages;
+
+ SelectedPages(ScDPObject* pObj, SelectedPagesType&& rSelected);
+ };
+
+ struct SheetSource
+ {
+ ScDPObject* mpDP;
+ ScSheetSourceDesc maDesc;
+
+ SheetSource(ScDPObject* pObj, ScSheetSourceDesc aDesc);
+ };
+
+ struct DBSource
+ {
+ ScDPObject* mpDP;
+ ScImportSourceDesc maDesc;
+
+ DBSource(ScDPObject* pObj, ScImportSourceDesc aDesc);
+ };
+
+ struct ServiceSource
+ {
+ ScDPObject* mpDP;
+ ScDPServiceDesc maDesc;
+
+ ServiceSource(ScDPObject* pObj, ScDPServiceDesc aDesc);
+ };
+
+ std::vector<SelectedPages> maSelectedPagesList;
+ std::vector<SheetSource> maSheetSources;
+ std::vector<DBSource> maDBSources;
+ std::vector<ServiceSource> maServiceSources;
+
+ PivotTableSources();
+
+ void appendSheetSource(ScDPObject* pObj, const ScSheetSourceDesc& rDesc);
+ void appendDBSource(ScDPObject* pObj, const ScImportSourceDesc& rDesc);
+ void appendServiceSource(ScDPObject* pObj, const ScDPServiceDesc& rDesc);
+
+ void appendSelectedPages(ScDPObject* pObj, SelectedPagesType&& rSelected);
+
+ void process();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/sheetdata.cxx b/sc/source/filter/xml/sheetdata.cxx
new file mode 100644
index 0000000000..4f62181e7d
--- /dev/null
+++ b/sc/source/filter/xml/sheetdata.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+#include <xmloff/families.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <algorithm>
+
+#include <sheetdata.hxx>
+
+ScSheetSaveData::ScSheetSaveData() :
+ mnStartTab( -1 ),
+ mnStartOffset( -1 ),
+ maPreviousNote( OUString(), OUString(), ScAddress::INITIALIZE_INVALID ),
+ mbInSupportedSave( false )
+{
+}
+
+ScSheetSaveData::~ScSheetSaveData()
+{
+}
+
+void ScSheetSaveData::AddCellStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maCellStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::AddColumnStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maColumnStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::AddRowStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maRowStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::AddTableStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maTableStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::HandleNoteStyles( const OUString& rStyleName, const OUString& rTextName, const ScAddress& rCellPos )
+{
+ // only consecutive duplicates (most common case) are filtered out here,
+ // the others are found when the styles are created
+
+ if ( rStyleName == maPreviousNote.maStyleName &&
+ rTextName == maPreviousNote.maTextStyle &&
+ rCellPos.Tab() == maPreviousNote.maCellPos.Tab() )
+ {
+ // already stored for the same sheet - ignore
+ return;
+ }
+
+ ScNoteStyleEntry aNewEntry( rStyleName, rTextName, rCellPos );
+ maPreviousNote = aNewEntry;
+ maNoteStyles.push_back( aNewEntry );
+}
+
+void ScSheetSaveData::AddNoteContentStyle( XmlStyleFamily nFamily, const OUString& rName, const ScAddress& rCellPos, const ESelection& rSelection )
+{
+ if ( nFamily == XmlStyleFamily::TEXT_PARAGRAPH )
+ maNoteParaStyles.emplace_back( rName, rCellPos, rSelection );
+ else
+ maNoteTextStyles.emplace_back( rName, rCellPos, rSelection );
+}
+
+void ScSheetSaveData::AddTextStyle( const OUString& rName, const ScAddress& rCellPos, const ESelection& rSelection )
+{
+ maTextStyles.emplace_back( rName, rCellPos, rSelection );
+}
+
+void ScSheetSaveData::BlockSheet( SCTAB nTab )
+{
+ if ( nTab >= static_cast<SCTAB>(maBlocked.size()) )
+ maBlocked.resize( nTab + 1, false ); // fill vector with "false" entries
+
+ maBlocked[nTab] = true;
+}
+
+bool ScSheetSaveData::IsSheetBlocked( SCTAB nTab ) const
+{
+ if ( nTab < static_cast<SCTAB>(maBlocked.size()) )
+ return maBlocked[nTab];
+ else
+ return false;
+}
+
+void ScSheetSaveData::AddStreamPos( SCTAB nTab, sal_Int64 nStartOffset, sal_Int64 nEndOffset )
+{
+ if ( nTab >= static_cast<SCTAB>(maStreamEntries.size()) )
+ maStreamEntries.resize( nTab + 1 );
+
+ maStreamEntries[nTab] = ScStreamEntry( nStartOffset, nEndOffset );
+}
+
+void ScSheetSaveData::StartStreamPos( SCTAB nTab, sal_Int64 nStartOffset )
+{
+ OSL_ENSURE( mnStartTab < 0, "StartStreamPos without EndStreamPos" );
+
+ mnStartTab = nTab;
+ mnStartOffset = nStartOffset;
+}
+
+void ScSheetSaveData::EndStreamPos( sal_Int64 nEndOffset )
+{
+ if ( mnStartTab >= 0 )
+ {
+ AddStreamPos( mnStartTab, mnStartOffset, nEndOffset );
+ mnStartTab = -1;
+ mnStartOffset = -1;
+ }
+}
+
+void ScSheetSaveData::GetStreamPos( SCTAB nTab, sal_Int64& rStartOffset, sal_Int64& rEndOffset ) const
+{
+ if ( nTab < static_cast<SCTAB>(maStreamEntries.size()) )
+ {
+ const ScStreamEntry& rEntry = maStreamEntries[nTab];
+ rStartOffset = rEntry.mnStartOffset;
+ rEndOffset = rEntry.mnEndOffset;
+ }
+ else
+ rStartOffset = rEndOffset = -1;
+}
+
+bool ScSheetSaveData::HasStreamPos( SCTAB nTab ) const
+{
+ sal_Int64 nStartOffset = -1;
+ sal_Int64 nEndOffset = -1;
+ GetStreamPos( nTab, nStartOffset, nEndOffset );
+ return ( nStartOffset >= 0 && nEndOffset >= 0 );
+}
+
+void ScSheetSaveData::ResetSaveEntries()
+{
+ maSaveEntries.clear();
+}
+
+void ScSheetSaveData::AddSavePos( SCTAB nTab, sal_Int64 nStartOffset, sal_Int64 nEndOffset )
+{
+ if ( nTab >= static_cast<SCTAB>(maSaveEntries.size()) )
+ maSaveEntries.resize( nTab + 1 );
+
+ maSaveEntries[nTab] = ScStreamEntry( nStartOffset, nEndOffset );
+}
+
+void ScSheetSaveData::UseSaveEntries()
+{
+ maStreamEntries = maSaveEntries;
+}
+
+void ScSheetSaveData::StoreInitialNamespaces( const SvXMLNamespaceMap& rNamespaces )
+{
+ // the initial namespaces are just removed from the list of loaded namespaces,
+ // so only an unordered_map of the prefixes is needed.
+
+ const NameSpaceHash& rNameHash = rNamespaces.GetAllEntries();
+ for (const auto& rEntry : rNameHash)
+ {
+ maInitialPrefixes.insert( rEntry.first );
+ }
+}
+
+void ScSheetSaveData::StoreLoadedNamespaces( const SvXMLNamespaceMap& rNamespaces )
+{
+ // store the loaded namespaces, so the prefixes in copied stream fragments remain valid
+
+ const NameSpaceHash& rNameHash = rNamespaces.GetAllEntries();
+ for (const auto& [rName, rEntry] : rNameHash)
+ {
+ // ignore the initial namespaces
+ if ( maInitialPrefixes.find( rName ) == maInitialPrefixes.end() )
+ {
+ maLoadedNamespaces.emplace_back( rEntry.m_sPrefix, rEntry.m_sName, rEntry.m_nKey );
+ }
+ }
+}
+
+static bool lcl_NameInHash( const NameSpaceHash& rNameHash, const OUString& rName )
+{
+ return std::any_of(rNameHash.begin(), rNameHash.end(),
+ [&rName](const NameSpaceHash::value_type& rEntry) { return rEntry.second.m_sName == rName; });
+}
+
+bool ScSheetSaveData::AddLoadedNamespaces( SvXMLNamespaceMap& rNamespaces ) const
+{
+ // Add the loaded namespaces to the name space map.
+
+ // look for conflicts
+ // (if the loaded namespaces were added first, this might not be necessary)
+ const NameSpaceHash& rNameHash = rNamespaces.GetAllEntries();
+ auto bConflict = std::any_of(maLoadedNamespaces.begin(), maLoadedNamespaces.end(),
+ [&rNameHash](const ScLoadedNamespaceEntry& rLoadedNamespace) {
+ NameSpaceHash::const_iterator aHashIter = rNameHash.find( rLoadedNamespace.maPrefix );
+
+ // same prefix, but different name: loaded namespaces can't be used
+ bool bNameConflict = (aHashIter != rNameHash.end()) && (aHashIter->second.m_sName != rLoadedNamespace.maName);
+
+ // a second prefix for the same name would confuse SvXMLNamespaceMap lookup,
+ // so this is also considered a conflict
+ bool bPrefixConflict = (aHashIter == rNameHash.end()) && lcl_NameInHash(rNameHash, rLoadedNamespace.maName);
+
+ return bNameConflict || bPrefixConflict;
+ });
+ if (bConflict)
+ return false;
+
+ // only if there were no conflicts, add the entries that aren't in the map already
+ // (the key is needed if the same namespace is added later within an element)
+ for (const auto& rLoadedNamespace : maLoadedNamespaces)
+ {
+ NameSpaceHash::const_iterator aHashIter = rNameHash.find( rLoadedNamespace.maPrefix );
+ if ( aHashIter == rNameHash.end() )
+ rNamespaces.Add( rLoadedNamespace.maPrefix, rLoadedNamespace.maName, rLoadedNamespace.mnKey );
+ }
+
+ return true; // success
+}
+
+void ScSheetSaveData::SetInSupportedSave( bool bSet )
+{
+ mbInSupportedSave = bSet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlannoi.cxx b/sc/source/filter/xml/xmlannoi.cxx
new file mode 100644
index 0000000000..f046ec3d17
--- /dev/null
+++ b/sc/source/filter/xml/xmlannoi.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlannoi.hxx"
+#include "xmlimprt.hxx"
+#include "xmlconti.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLAnnotationData::ScXMLAnnotationData() :
+ mbUseShapePos( false ),
+ mbShown( false )
+{
+}
+
+ScXMLAnnotationData::~ScXMLAnnotationData()
+{
+}
+
+ScXMLAnnotationContext::ScXMLAnnotationContext( ScXMLImport& rImport,
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLAnnotationData& rAnnotationData) :
+ ScXMLImportContext( rImport ),
+ mrAnnotationData( rAnnotationData )
+{
+ uno::Reference<drawing::XShapes> xLocalShapes (GetScImport().GetTables().GetCurrentXShapes());
+ if (xLocalShapes.is())
+ {
+ XMLTableShapeImportHelper* pTableShapeImport = static_cast<XMLTableShapeImportHelper*>(GetScImport().GetShapeImport().get());
+ pTableShapeImport->SetAnnotation(this);
+ pShapeContext.reset( XMLShapeImportHelper::CreateGroupChildContext(
+ GetScImport(), nElement, xAttrList, xLocalShapes, true) );
+ }
+
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ switch( aIter.getToken() )
+ {
+ case XML_ELEMENT(OFFICE, XML_AUTHOR):
+ {
+ maAuthorBuffer = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_CREATE_DATE):
+ {
+ maCreateDateBuffer = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_CREATE_DATE_STRING):
+ {
+ maCreateDateStringBuffer = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_DISPLAY):
+ {
+ mrAnnotationData.mbShown = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT(SVG, XML_X):
+ case XML_ELEMENT(SVG_COMPAT, XML_X):
+ {
+ mrAnnotationData.mbUseShapePos = true;
+ }
+ break;
+ case XML_ELEMENT(SVG, XML_Y):
+ case XML_ELEMENT(SVG_COMPAT, XML_Y):
+ {
+ mrAnnotationData.mbUseShapePos = true;
+ }
+ break;
+ default:
+ XMLOFF_INFO_UNKNOWN("xmloff", aIter);
+ }
+ }
+}
+
+ScXMLAnnotationContext::~ScXMLAnnotationContext()
+{
+}
+
+void ScXMLAnnotationContext::startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList)
+{
+ if (pShapeContext)
+ pShapeContext->startFastElement(nElement, xAttrList);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLAnnotationContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(DC, XML_CREATOR):
+ return new ScXMLContentContext(GetScImport(), maAuthorBuffer);
+ case XML_ELEMENT(DC, XML_DATE):
+ return new ScXMLContentContext(GetScImport(), maCreateDateBuffer);
+ case XML_ELEMENT(META, XML_DATE_STRING):
+ return new ScXMLContentContext(GetScImport(), maCreateDateStringBuffer);
+ }
+
+ if( pShapeContext )
+ {
+ auto p = pShapeContext->createFastChildContext(nElement, xAttrList);
+ if (p)
+ return p;
+ }
+
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return nullptr;
+}
+
+void ScXMLAnnotationContext::characters( const OUString& rChars )
+{
+ maTextBuffer.append(rChars);
+}
+
+void ScXMLAnnotationContext::endFastElement(sal_Int32 nElement)
+{
+ if (pShapeContext)
+ {
+ pShapeContext->endFastElement(nElement);
+ pShapeContext.reset();
+ }
+
+ mrAnnotationData.maAuthor = maAuthorBuffer.makeStringAndClear();
+ mrAnnotationData.maCreateDate = maCreateDateBuffer.makeStringAndClear();
+ if (mrAnnotationData.maCreateDate.isEmpty())
+ mrAnnotationData.maCreateDate = maCreateDateStringBuffer.makeStringAndClear();
+ mrAnnotationData.maSimpleText = maTextBuffer.makeStringAndClear();
+
+ XMLTableShapeImportHelper* pTableShapeImport = static_cast<XMLTableShapeImportHelper*>(GetScImport().GetShapeImport().get());
+ pTableShapeImport->SetAnnotation(nullptr);
+}
+
+void ScXMLAnnotationContext::SetShape( const uno::Reference< drawing::XShape >& rxShape, const uno::Reference< drawing::XShapes >& rxShapes,
+ const OUString& rStyleName, const OUString& rTextStyle )
+{
+ mrAnnotationData.mxShape = rxShape;
+ mrAnnotationData.mxShapes = rxShapes;
+ mrAnnotationData.maStyleName = rStyleName;
+ mrAnnotationData.maTextStyle = rTextStyle;
+}
+
+void ScXMLAnnotationContext::AddContentStyle( XmlStyleFamily nFamily, const OUString& rName, const ESelection& rSelection )
+{
+ mrAnnotationData.maContentStyles.emplace_back( nFamily, rName, rSelection );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlannoi.hxx b/sc/source/filter/xml/xmlannoi.hxx
new file mode 100644
index 0000000000..6f8c9dc492
--- /dev/null
+++ b/sc/source/filter/xml/xmlannoi.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <utility>
+#include <xmloff/xmlictxt.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <editeng/editdata.hxx>
+#include "importcontext.hxx"
+
+#include <vector>
+
+class ScXMLImport;
+enum class XmlStyleFamily;
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::drawing { class XShapes; }
+
+struct ScXMLAnnotationStyleEntry
+{
+ XmlStyleFamily mnFamily;
+ OUString maName;
+ ESelection maSelection;
+
+ ScXMLAnnotationStyleEntry( XmlStyleFamily nFam, OUString aNam, const ESelection& rSel ) :
+ mnFamily( nFam ),
+ maName(std::move( aNam )),
+ maSelection( rSel )
+ {
+ }
+};
+
+struct ScXMLAnnotationData
+{
+ css::uno::Reference< css::drawing::XShape >
+ mxShape;
+ css::uno::Reference< css::drawing::XShapes >
+ mxShapes;
+ OUString maAuthor;
+ OUString maCreateDate;
+ OUString maSimpleText;
+ OUString maStyleName;
+ OUString maTextStyle;
+ bool mbUseShapePos;
+ bool mbShown;
+ std::vector<ScXMLAnnotationStyleEntry> maContentStyles;
+
+ explicit ScXMLAnnotationData();
+ ~ScXMLAnnotationData();
+};
+
+class ScXMLAnnotationContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLAnnotationContext( ScXMLImport& rImport,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLAnnotationData& rAnnotationData);
+
+ virtual ~ScXMLAnnotationContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList) override;
+
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ void SetShape(
+ const css::uno::Reference< css::drawing::XShape >& rxShape,
+ const css::uno::Reference< css::drawing::XShapes >& rxShapes,
+ const OUString& rStyleName, const OUString& rTextStyle );
+
+ void AddContentStyle( XmlStyleFamily nFamily, const OUString& rName, const ESelection& rSelection );
+
+private:
+ ScXMLAnnotationData& mrAnnotationData;
+ OUStringBuffer maTextBuffer;
+ OUStringBuffer maAuthorBuffer;
+ OUStringBuffer maCreateDateBuffer;
+ OUStringBuffer maCreateDateStringBuffer;
+ std::unique_ptr<SvXMLImportContext> pShapeContext;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlbodyi.cxx b/sc/source/filter/xml/xmlbodyi.cxx
new file mode 100644
index 0000000000..0b75b50466
--- /dev/null
+++ b/sc/source/filter/xml/xmlbodyi.cxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <docuno.hxx>
+#include <sheetdata.hxx>
+
+#include "xmlbodyi.hxx"
+#include "xmltabi.hxx"
+#include "xmlnexpi.hxx"
+#include "xmldrani.hxx"
+#include "xmlimprt.hxx"
+#include "xmldpimp.hxx"
+#include "xmlcvali.hxx"
+#include "xmllabri.hxx"
+#include "xmlmappingi.hxx"
+#include "XMLConsolidationContext.hxx"
+#include "XMLDDELinksContext.hxx"
+#include "XMLCalculationSettingsContext.hxx"
+#include "XMLTrackedChangesContext.hxx"
+#include "XMLChangeTrackingImportHelper.hxx"
+#include "XMLEmptyContext.hxx"
+#include "XMLDetectiveContext.hxx"
+#include <scerrors.hxx>
+#include <tabprotection.hxx>
+#include "datastreamimport.hxx"
+#include <sax/fastattribs.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <rtl/math.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLBodyContext::ScXMLBodyContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ meHash1(PASSHASH_SHA1),
+ meHash2(PASSHASH_UNSPECIFIED),
+ bProtected(false),
+ bHadCalculationSettings(false),
+ pChangeTrackingImportHelper(nullptr)
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (pDoc)
+ {
+ // ODF 1.1 and earlier => GRAM_PODF; ODF 1.2 and later => GRAM_ODFF;
+ // no version => earlier than 1.2 => GRAM_PODF.
+ formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ODFF;
+ const OUString& aVer( rImport.GetODFVersion());
+ sal_Int32 nLen = aVer.getLength();
+ SAL_INFO("sc.filter", "ScXMLBodyContext ODFVersion: nLen: " << nLen << " str : " << aVer);
+ if (!nLen)
+ eGrammar = formula::FormulaGrammar::GRAM_PODF;
+ else
+ {
+ // In case there was a micro version, e.g. "1.2.3", this would
+ // still yield major.minor, but pParsedEnd (5th parameter, not
+ // passed here) would point before string end upon return.
+ double fVer = ::rtl::math::stringToDouble( aVer, '.', 0 );
+ if (fVer < 1.2)
+ eGrammar = formula::FormulaGrammar::GRAM_PODF;
+ }
+ pDoc->SetStorageGrammar( eGrammar);
+ }
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &it : *rAttrList)
+ {
+ sal_Int32 nToken = it.getToken();
+ if( IsTokenInNamespace(nToken, XML_NAMESPACE_TABLE) )
+ {
+ const sal_Int32 nLocalToken = nToken & TOKEN_MASK;
+ if( nLocalToken == XML_STRUCTURE_PROTECTED )
+ bProtected = IsXMLToken( it, XML_TRUE );
+ else if ( nLocalToken == XML_PROTECTION_KEY )
+ sPassword = it.toString();
+ else if ( nLocalToken == XML_PROTECTION_KEY_DIGEST_ALGORITHM )
+ meHash1 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ else if ( nLocalToken == XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 )
+ meHash2 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ }
+ else if ( nToken == XML_ELEMENT( LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 ) )
+ {
+ meHash2 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ }
+ }
+}
+
+ScXMLBodyContext::~ScXMLBodyContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLBodyContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ ScSheetSaveData* pSheetData = GetScImport().GetScModel()->GetSheetSaveData();
+ if ( pSheetData && pSheetData->HasStartPos() )
+ {
+ // stream part to copy ends before the next child element
+ sal_Int64 nEndOffset = GetScImport().GetByteOffset();
+ pSheetData->EndStreamPos( nEndOffset );
+ }
+
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TRACKED_CHANGES ):
+ pChangeTrackingImportHelper = GetScImport().GetChangeTrackingImportHelper();
+ if (pChangeTrackingImportHelper)
+ pContext = new ScXMLTrackedChangesContext( GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CALCULATION_SETTINGS ):
+ pContext = new ScXMLCalculationSettingsContext( GetScImport(), pAttribList );
+ bHadCalculationSettings = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATIONS ):
+ pContext = new ScXMLContentValidationsContext( GetScImport() );
+ break;
+ case XML_ELEMENT( TABLE, XML_LABEL_RANGES ):
+ pContext = new ScXMLLabelRangesContext( GetScImport() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ if (GetScImport().GetTables().GetCurrentSheet() >= MAXTAB)
+ {
+ GetScImport().SetRangeOverflowType(SCWARN_IMPORT_SHEET_OVERFLOW);
+ pContext = new ScXMLEmptyContext(GetScImport() );
+ }
+ else
+ {
+ pContext = new ScXMLTableContext( GetScImport(), pAttribList );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_NAMED_EXPRESSIONS ):
+ pContext = new ScXMLNamedExpressionsContext (
+ GetScImport(),
+ std::make_shared<ScXMLNamedExpressionsContext::GlobalInserter>(GetScImport()) );
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_RANGES ):
+ pContext = new ScXMLDatabaseRangesContext ( GetScImport() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_MAPPINGS ):
+ pContext = new ScXMLMappingsContext(GetScImport());
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_RANGE ):
+ pContext = new ScXMLDatabaseRangeContext ( GetScImport(),
+ pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_TABLES ):
+ pContext = new ScXMLDataPilotTablesContext ( GetScImport() );
+ break;
+ case XML_ELEMENT( TABLE, XML_CONSOLIDATION ):
+ pContext = new ScXMLConsolidationContext ( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_DDE_LINKS ):
+ pContext = new ScXMLDDELinksContext ( GetScImport() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_STREAM_SOURCE ):
+ pContext = new ScXMLDataStreamContext(GetScImport(), pAttribList);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLBodyContext::characters(const OUString &)
+{
+ ScSheetSaveData* pSheetData = GetScImport().GetScModel()->GetSheetSaveData();
+ if ( pSheetData && pSheetData->HasStartPos() )
+ {
+ // stream part to copy ends before any content (whitespace) within the spreadsheet element
+ sal_Int64 nEndOffset = GetScImport().GetByteOffset();
+ pSheetData->EndStreamPos( nEndOffset );
+ }
+ // otherwise ignore
+}
+
+void SAL_CALL ScXMLBodyContext::endFastElement(sal_Int32 nElement)
+{
+ ScSheetSaveData* pSheetData = GetScImport().GetScModel()->GetSheetSaveData();
+ if ( pSheetData && pSheetData->HasStartPos() )
+ {
+ // stream part to copy ends before the closing tag of spreadsheet element
+ sal_Int64 nEndOffset = GetScImport().GetByteOffset();
+ pSheetData->EndStreamPos( nEndOffset );
+ }
+
+ if ( pSheetData )
+ {
+ // store the loaded namespaces (for the office:spreadsheet element),
+ // so the prefixes in copied stream fragments remain valid
+ const SvXMLNamespaceMap& rNamespaces = GetImport().GetNamespaceMap();
+ pSheetData->StoreLoadedNamespaces( rNamespaces );
+ }
+
+ if (!bHadCalculationSettings)
+ {
+ // #111055#; set calculation settings defaults if there is no calculation settings element
+ rtl::Reference<ScXMLCalculationSettingsContext> pContext( new ScXMLCalculationSettingsContext(GetScImport(), nullptr) );
+ pContext->endFastElement( nElement );
+ }
+
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+
+ ScMyImpDetectiveOpArray* pDetOpArray = GetScImport().GetDetectiveOpArray();
+ ScDocument* pDoc = GetScImport().GetDocument();
+ ScMyImpDetectiveOp aDetOp;
+
+ if (!(pDoc && GetScImport().GetModel().is()))
+ return;
+
+ if (pDetOpArray)
+ {
+ pDetOpArray->Sort();
+ while( pDetOpArray->GetFirstOp( aDetOp ) )
+ {
+ ScDetOpData aOpData( aDetOp.aPosition, aDetOp.eOpType );
+ pDoc->AddDetectiveOperation( aOpData );
+ }
+ }
+
+ if (pChangeTrackingImportHelper)
+ pChangeTrackingImportHelper->CreateChangeTrack(pDoc);
+
+ // #i37959# handle document protection after the sheet settings
+ if (!bProtected)
+ return;
+
+ ScDocProtection aProtection;
+ aProtection.setProtected(true);
+
+ uno::Sequence<sal_Int8> aPass;
+ if (!sPassword.isEmpty())
+ {
+ ::comphelper::Base64::decode(aPass, sPassword);
+ aProtection.setPasswordHash(aPass, meHash1, meHash2);
+ }
+
+ pDoc->SetDocProtection(&aProtection);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlbodyi.hxx b/sc/source/filter/xml/xmlbodyi.hxx
new file mode 100644
index 0000000000..6db6780170
--- /dev/null
+++ b/sc/source/filter/xml/xmlbodyi.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 "importcontext.hxx"
+
+#include <tabprotection.hxx>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLChangeTrackingImportHelper;
+
+class ScXMLBodyContext : public ScXMLImportContext
+{
+ OUString sPassword;
+ ScPasswordHash meHash1;
+ ScPasswordHash meHash2;
+ bool bProtected;
+ bool bHadCalculationSettings;
+
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+
+ ScXMLBodyContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLBodyContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ virtual void SAL_CALL characters(const OUString & aChars) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcelli.cxx b/sc/source/filter/xml/xmlcelli.cxx
new file mode 100644
index 0000000000..6c739dc423
--- /dev/null
+++ b/sc/source/filter/xml/xmlcelli.cxx
@@ -0,0 +1,1532 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <limits>
+#include <memory>
+#include "xmlcelli.hxx"
+#include "xmlimprt.hxx"
+#include "xmlannoi.hxx"
+#include <global.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <postit.hxx>
+#include <sheetdata.hxx>
+#include <cellform.hxx>
+#include <validat.hxx>
+#include <patattr.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+
+#include "XMLTableShapeImportHelper.hxx"
+#include "XMLStylesImportHelper.hxx"
+#include "celltextparacontext.hxx"
+#include "XMLCellRangeSourceContext.hxx"
+
+#include <arealink.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <scerrors.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include "editattributemap.hxx"
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <stringutil.hxx>
+#include <documentimport.hxx>
+#include <externalrefmgr.hxx>
+
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/prstylei.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <com/sun/star/util/NumberFormat.hpp>
+
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <i18nlangtag/lang.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableRowCellContext::ParaFormat::ParaFormat(const ScEditEngineDefaulter& rEditEngine) :
+ maItemSet(rEditEngine.GetEmptyItemSet()) {}
+
+ScXMLTableRowCellContext::Field::Field(std::unique_ptr<SvxFieldData> pData) : mpData(std::move(pData)) {}
+
+ScXMLTableRowCellContext::Field::~Field()
+{
+}
+
+ScXMLTableRowCellContext::ScXMLTableRowCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempIsCovered,
+ const sal_Int32 nTempRepeatedRows ) :
+ ScXMLImportContext( rImport ),
+ mpEditEngine(GetScImport().GetEditEngine()),
+ mnCurParagraph(0),
+ fValue(std::numeric_limits<double>::quiet_NaN()),
+ nMergedRows(1),
+ nMatrixRows(0),
+ nRepeatedRows(nTempRepeatedRows),
+ nMergedCols(1),
+ nMatrixCols(0),
+ nColsRepeated(1),
+ rXMLImport(rImport),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nCellType(util::NumberFormat::TEXT),
+ bIsMerged(false),
+ bIsMatrix(false),
+ bIsCovered(bTempIsCovered),
+ bIsEmpty(true),
+ mbNewValueType(false),
+ mbErrorValue(false),
+ bSolarMutexLocked(false),
+ bFormulaTextResult(false),
+ mbPossibleErrorCell(false),
+ mbCheckWithCompilerForError(false),
+ mbEditEngineHasText(false),
+ mbHasFormatRuns(false),
+ mbHasStyle(false),
+ mbPossibleEmptyDisplay(false)
+{
+ rXMLImport.GetTables().AddColumn(bTempIsCovered);
+
+ std::optional<OUString> xStyleName;
+ std::optional<OUString> xCurrencySymbol;
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch ( it.getToken() )
+ {
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ xStyleName = it.toString();
+ mbHasStyle = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION_NAME ):
+ OSL_ENSURE(!maContentValidationName, "here should be only one Validation Name");
+ if (!it.isEmpty())
+ maContentValidationName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_ROWS_SPANNED ):
+ bIsMerged = true;
+ nMergedRows = static_cast<SCROW>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_SPANNED ):
+ bIsMerged = true;
+ nMergedCols = static_cast<SCCOL>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixCols = static_cast<SCCOL>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixRows = static_cast<SCROW>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ nColsRepeated = static_cast<SCCOL>(
+ std::min<sal_Int32>( rImport.GetDocument()->GetSheetLimits().GetMaxColCount(),
+ std::max( it.toInt32(), static_cast<sal_Int32>(1) ) ));
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
+ bIsEmpty = false;
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE_TYPE ):
+ if(it.isString( "error" ) )
+ mbErrorValue = true;
+ else
+ nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
+ bIsEmpty = false;
+ mbNewValueType = true;
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ fValue = it.toDouble();
+ bIsEmpty = false;
+
+ //if office:value="0", let's get the text:p in case this is
+ //a special case in HasSpecialCaseFormulaText(). If it
+ //turns out not to be a special case, we'll use the 0 value.
+ if(fValue == 0.0)
+ bFormulaTextResult = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_DATE_VALUE ):
+ {
+ if (!it.isEmpty() && rXMLImport.SetNullDateOnUnitConverter())
+ {
+ rXMLImport.GetMM100UnitConverter().convertDateTime(fValue, it.toView());
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_TIME_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ ::sax::Converter::convertDuration(fValue, it.toView());
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_STRING_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ OSL_ENSURE(!maStringValue, "here should be only one string value");
+ maStringValue = it.toString();
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE , XML_BOOLEAN_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ if ( IsXMLToken( it, XML_TRUE ) )
+ fValue = 1.0;
+ else if ( IsXMLToken( it, XML_FALSE ) )
+ fValue = 0.0;
+ else
+ fValue = it.toDouble();
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FORMULA ):
+ {
+ if (!it.isEmpty())
+ {
+ OSL_ENSURE(!maFormula, "here should be only one formula");
+ OUString aFormula, aFormulaNmsp;
+ rXMLImport.ExtractFormulaNamespaceGrammar( aFormula, aFormulaNmsp, eGrammar, it.toString() );
+ maFormula = FormulaWithNamespace(aFormula, aFormulaNmsp);
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_CURRENCY ):
+ xCurrencySymbol = it.toString();
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ if (maFormula)
+ {
+ if (nCellType == util::NumberFormat::TEXT)
+ bFormulaTextResult = true;
+ if(nCellType == util::NumberFormat::DATETIME)
+ nCellType = util::NumberFormat::UNDEFINED;
+ //if bIsEmpty is true at this point, then there is no office value.
+ //we must get the text:p (even if it is empty) in case this a special
+ //case in HasSpecialCaseFormulaText().
+ if(bIsEmpty)
+ bFormulaTextResult = true;
+ }
+ rXMLImport.GetStylesImportHelper()->SetAttributes(std::move(xStyleName), std::move(xCurrencySymbol), nCellType);
+}
+
+ScXMLTableRowCellContext::~ScXMLTableRowCellContext()
+{
+}
+
+void ScXMLTableRowCellContext::LockSolarMutex()
+{
+ if (!bSolarMutexLocked)
+ {
+ GetScImport().LockSolarMutex();
+ bSolarMutexLocked = true;
+ }
+}
+
+namespace {
+
+bool cellExists( const ScDocument& rDoc, const ScAddress& rCellPos )
+{
+ return( rCellPos.Col() >= 0 && rCellPos.Row() >= 0 &&
+ rCellPos.Col() <= rDoc.MaxCol() && rCellPos.Row() <= rDoc.MaxRow() );
+}
+
+}
+
+void ScXMLTableRowCellContext::PushParagraphSpan(std::u16string_view rSpan, const OUString& rStyleName)
+{
+ sal_Int32 nBegin = maParagraph.getLength();
+ sal_Int32 nEnd = nBegin + rSpan.size();
+ maParagraph.append(rSpan);
+
+ PushFormat(nBegin, nEnd, rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphField(std::unique_ptr<SvxFieldData> pData, const OUString& rStyleName)
+{
+ mbHasFormatRuns = true;
+ maFields.push_back(std::make_unique<Field>(std::move(pData)));
+ Field& rField = *maFields.back();
+
+ sal_Int32 nPos = maParagraph.getLength();
+ maParagraph.append('\1'); // Placeholder text for inserted field item.
+ rField.maSelection.nStartPara = mnCurParagraph;
+ rField.maSelection.nEndPara = mnCurParagraph;
+ rField.maSelection.nStartPos = nPos;
+ rField.maSelection.nEndPos = nPos+1;
+
+ PushFormat(nPos, nPos+1, rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushFormat(sal_Int32 nBegin, sal_Int32 nEnd, const OUString& rStyleName)
+{
+ if (rStyleName.isEmpty())
+ return;
+
+ // Get the style information from xmloff.
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetImport().GetTextImport()->GetTextImportPropertySetMapper()->getPropertySetMapper();
+ if (!xMapper.is())
+ // We can't do anything without the mapper.
+ return;
+
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+
+ SvXMLStylesContext* pAutoStyles = GetImport().GetAutoStyles();
+ if (!pAutoStyles)
+ return;
+
+ // Style name for text span corresponds with the name of an automatic style.
+ const XMLPropStyleContext* pStyle = dynamic_cast<const XMLPropStyleContext*>(
+ pAutoStyles->FindStyleChildContext(XmlStyleFamily::TEXT_TEXT, rStyleName));
+
+ if (!pStyle)
+ // No style by that name found.
+ return;
+
+ const std::vector<XMLPropertyState>& rProps = pStyle->GetProperties();
+ if (rProps.empty())
+ return;
+
+ const ScXMLEditAttributeMap& rEditAttrMap = GetScImport().GetEditAttributeMap();
+
+ mbHasFormatRuns = true;
+ maFormats.push_back(std::make_unique<ParaFormat>(*mpEditEngine));
+ ParaFormat& rFmt = *maFormats.back();
+ rFmt.maSelection.nStartPara = rFmt.maSelection.nEndPara = mnCurParagraph;
+ rFmt.maSelection.nStartPos = nBegin;
+ rFmt.maSelection.nEndPos = nEnd;
+
+ // Store the used text styles for export.
+ ScSheetSaveData* pSheetData = rXMLImport.GetScModel()->GetSheetSaveData();
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ pSheetData->AddTextStyle(rStyleName, aCellPos, rFmt.maSelection);
+
+ std::unique_ptr<SfxPoolItem> pPoolItem;
+ sal_uInt16 nLastItemID = EE_CHAR_END + 1;
+
+ for (const auto& rProp : rProps)
+ {
+ if (rProp.mnIndex == -1 || rProp.mnIndex >= nEntryCount)
+ continue;
+
+ const OUString& rName = xMapper->GetEntryAPIName(rProp.mnIndex);
+ const ScXMLEditAttributeMap::Entry* pEntry = rEditAttrMap.getEntryByAPIName(rName);
+ if (!pEntry)
+ continue;
+
+ if (nLastItemID != pEntry->mnItemID && pPoolItem)
+ {
+ // Flush the last item when the item ID changes.
+ rFmt.maItemSet.Put(std::move(pPoolItem));
+ }
+
+ switch (pEntry->mnItemID)
+ {
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ // Font properties need to be consolidated into a single item.
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxFontItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxWeightItem(WEIGHT_NORMAL, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxFontHeightItem(240, 100, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxPostureItem(ITALIC_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxUnderlineItem(LINESTYLE_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxOverlineItem(LINESTYLE_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxColorItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_WLM:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxWordLineModeItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCrossedOutItem(STRIKEOUT_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCharReliefItem(FontRelief::NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxContourItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxShadowedItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxKerningItem(0, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxAutoKernItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_FONTWIDTH:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCharScaleWidthItem(100, TypedWhichId<SvxCharScaleWidthItem>(pEntry->mnItemID)));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxEscapementItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxEmphasisMarkItem(FontEmphasisMark::NONE, TypedWhichId<SvxEmphasisMarkItem>(pEntry->mnItemID)));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxLanguageItem(LANGUAGE_DONTKNOW, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ default:
+ ;
+ }
+
+ nLastItemID = pEntry->mnItemID;
+ }
+
+ if (pPoolItem)
+ rFmt.maItemSet.Put(std::move(pPoolItem));
+}
+
+OUString ScXMLTableRowCellContext::GetFirstParagraph() const
+{
+ if (!maFirstParagraph)
+ return mpEditEngine->GetText(0);
+
+ return *maFirstParagraph;
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldDate(const OUString& rStyleName)
+{
+ PushParagraphField(std::make_unique<SvxDateField>(), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldSheetName(const OUString& rStyleName)
+{
+ SCTAB nTab = GetScImport().GetTables().GetCurrentCellPos().Tab();
+ PushParagraphField(std::make_unique<SvxTableField>(nTab), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldDocTitle(const OUString& rStyleName)
+{
+ PushParagraphField(std::make_unique<SvxFileField>(), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldURL(
+ const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame)
+{
+ OUString aAbsURL = GetScImport().GetAbsoluteReference(rURL);
+ std::unique_ptr<SvxURLField> pURLField(new SvxURLField(aAbsURL, rRep, SvxURLFormat::Repr));
+ pURLField->SetTargetFrame(rTargetFrame);
+ PushParagraphField(std::move(pURLField), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphEnd()
+{
+ // EditEngine always has at least one paragraph even when its content is empty.
+
+ if (mbEditEngineHasText)
+ {
+ if (maFirstParagraph)
+ {
+ // Flush the cached first paragraph first.
+ mpEditEngine->Clear();
+ mpEditEngine->SetTextCurrentDefaults(*maFirstParagraph);
+ maFirstParagraph.reset();
+ }
+ mpEditEngine->InsertParagraph(mpEditEngine->GetParagraphCount(), maParagraph.makeStringAndClear());
+ }
+ else if (mbHasFormatRuns || ScStringUtil::isMultiline(maParagraph))
+ {
+ mpEditEngine->Clear();
+ mpEditEngine->SetTextCurrentDefaults(maParagraph.makeStringAndClear());
+ mbEditEngineHasText = true;
+ }
+ else if (mnCurParagraph == 0)
+ {
+ maFirstParagraph = maParagraph.makeStringAndClear();
+ mbEditEngineHasText = true;
+ }
+
+ ++mnCurParagraph;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTableRowCellContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ bool bTextP(false);
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_P ):
+ {
+ bIsEmpty = false;
+ bTextP = true;
+
+ pContext = new ScXMLCellTextParaContext(rXMLImport, *this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUB_TABLE ):
+ {
+ SAL_WARN("sc", "ScXMLTableRowCellContext::createFastChildContext: subtables are not supported");
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DETECTIVE ):
+ {
+ bIsEmpty = false;
+ if (!pDetectiveObjVec)
+ pDetectiveObjVec.reset( new ScMyImpDetectiveObjVec );
+ pContext = new ScXMLDetectiveContext(
+ rXMLImport, pDetectiveObjVec.get() );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_SOURCE ):
+ {
+ bIsEmpty = false;
+ if (!pCellRangeSource)
+ pCellRangeSource.reset(new ScMyImpCellRangeSource());
+ pContext = new ScXMLCellRangeSourceContext(
+ rXMLImport, pAttribList, pCellRangeSource.get() );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_ANNOTATION):
+ {
+ bIsEmpty = false;
+ OSL_ENSURE(
+ !mxAnnotationData,
+ "ScXMLTableRowCellContext::CreateChildContext - multiple annotations in one cell");
+ mxAnnotationData.reset( new ScXMLAnnotationData );
+ pContext = new ScXMLAnnotationContext( rXMLImport, nElement,
+ xAttrList, *mxAnnotationData);
+ }
+ break;
+ }
+
+ if (!pContext && !bTextP)
+ {
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ uno::Reference<drawing::XShapes> xShapes (rXMLImport.GetTables().GetCurrentXShapes());
+ if (xShapes.is())
+ {
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if (aCellPos.Col() > pDoc->MaxCol())
+ aCellPos.SetCol(pDoc->MaxCol());
+ if (aCellPos.Row() > pDoc->MaxRow())
+ aCellPos.SetRow(pDoc->MaxRow());
+ XMLTableShapeImportHelper* pTableShapeImport =
+ static_cast< XMLTableShapeImportHelper* >( rXMLImport.GetShapeImport().get() );
+ pTableShapeImport->SetOnTable(false);
+ pTableShapeImport->SetCell(aCellPos);
+ pContext = XMLShapeImportHelper::CreateGroupChildContext(
+ rXMLImport, nElement, xAttrList, xShapes);
+ if (pContext)
+ {
+ bIsEmpty = false;
+ rXMLImport.ProgressBarIncrement();
+ }
+ }
+ }
+
+ return pContext;
+}
+
+void ScXMLTableRowCellContext::DoMerge( const ScAddress& rScAddress, const SCCOL nCols, const SCROW nRows )
+{
+ SCCOL mergeToCol = rScAddress.Col() + nCols;
+ SCROW mergeToRow = rScAddress.Row() + nRows;
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ bool bInBounds = rScAddress.Col() <= pDoc->MaxCol() && rScAddress.Row() <= pDoc->MaxRow() &&
+ mergeToCol <= pDoc->MaxCol() && mergeToRow <= pDoc->MaxRow();
+ if( bInBounds )
+ {
+ pDoc->DoMerge( rScAddress.Col(), rScAddress.Row(),
+ mergeToCol, mergeToRow, rScAddress.Tab() );
+ }
+}
+
+namespace {
+
+ScValidationMode validationTypeToMode( const sheet::ValidationType eVType )
+{
+ ScValidationMode eMode;
+ switch( eVType )
+ {
+ case sheet::ValidationType_WHOLE: eMode = SC_VALID_WHOLE; break;
+ case sheet::ValidationType_DECIMAL: eMode = SC_VALID_DECIMAL; break;
+ case sheet::ValidationType_DATE: eMode = SC_VALID_DATE; break;
+ case sheet::ValidationType_TIME: eMode = SC_VALID_TIME; break;
+ case sheet::ValidationType_TEXT_LEN: eMode = SC_VALID_TEXTLEN; break;
+ case sheet::ValidationType_LIST: eMode = SC_VALID_LIST; break;
+ case sheet::ValidationType_CUSTOM: eMode = SC_VALID_CUSTOM; break;
+ default: eMode = SC_VALID_ANY; break;
+ }
+ return eMode;
+}
+
+ScValidErrorStyle validAlertToValidError( const sheet::ValidationAlertStyle eVAlertStyle )
+{
+ ScValidErrorStyle eVErrStyle;
+ switch( eVAlertStyle )
+ {
+ case sheet::ValidationAlertStyle_STOP: eVErrStyle = SC_VALERR_STOP; break;
+ case sheet::ValidationAlertStyle_WARNING: eVErrStyle = SC_VALERR_WARNING; break;
+ case sheet::ValidationAlertStyle_MACRO: eVErrStyle = SC_VALERR_MACRO; break;
+ default: eVErrStyle = SC_VALERR_INFO; break;
+ //should INFO be the default? seems to be the most unobtrusive choice.
+ }
+ return eVErrStyle;
+}
+
+}
+
+void ScXMLTableRowCellContext::SetContentValidation( const ScRange& rScRange )
+{
+ if (!maContentValidationName)
+ return;
+
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScMyImportValidation aValidation;
+ aValidation.eGrammar1 = aValidation.eGrammar2 = pDoc->GetStorageGrammar();
+ if( !rXMLImport.GetValidation(*maContentValidationName, aValidation) )
+ return;
+
+ ScValidationData aScValidationData(
+ validationTypeToMode(aValidation.aValidationType),
+ ScConditionEntry::GetModeFromApi(aValidation.aOperator),
+ aValidation.sFormula1, aValidation.sFormula2, *pDoc, ScAddress(),
+ aValidation.sFormulaNmsp1, aValidation.sFormulaNmsp2,
+ aValidation.eGrammar1, aValidation.eGrammar2
+ );
+
+ aScValidationData.SetIgnoreBlank( aValidation.bIgnoreBlanks );
+ aScValidationData.SetListType( aValidation.nShowList );
+
+ // set strings for error / input even if disabled (and disable afterwards)
+ aScValidationData.SetInput( aValidation.sInputTitle, aValidation.sInputMessage );
+ if( !aValidation.bShowInputMessage )
+ aScValidationData.ResetInput();
+ aScValidationData.SetError( aValidation.sErrorTitle, aValidation.sErrorMessage, validAlertToValidError(aValidation.aAlertStyle) );
+ if( !aValidation.bShowErrorMessage )
+ aScValidationData.ResetError();
+
+ if( !aValidation.sBaseCellAddress.isEmpty() )
+ aScValidationData.SetSrcString( aValidation.sBaseCellAddress );
+
+ sal_uInt32 nIndex = pDoc->AddValidationEntry( aScValidationData );
+
+ ScPatternAttr aPattern( pDoc->GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nIndex ) );
+ if( rScRange.aStart == rScRange.aEnd ) //for a single cell
+ {
+ pDoc->ApplyPattern( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aStart.Tab(), aPattern );
+ }
+ else //for repeating cells
+ {
+ pDoc->ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aEnd.Col(), rScRange.aEnd.Row(),
+ rScRange.aStart.Tab(), aPattern );
+ }
+
+ // is the below still needed?
+ // For now, any sheet with validity is blocked from stream-copying.
+ // Later, the validation names could be stored along with the style names.
+ ScSheetSaveData* pSheetData = GetScImport().GetScModel()->GetSheetSaveData();
+ pSheetData->BlockSheet( GetScImport().GetTables().GetCurrentSheet() );
+}
+
+void ScXMLTableRowCellContext::SetContentValidation( const ScAddress& rCellPos )
+{
+ SetContentValidation( ScRange(rCellPos, rCellPos) );
+}
+
+void ScXMLTableRowCellContext::SetAnnotation(const ScAddress& rPos)
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if (!pDoc || !mxAnnotationData)
+ return;
+
+ LockSolarMutex();
+
+ ScPostIt* pNote = nullptr;
+
+ uno::Reference< drawing::XShapes > xShapes = rXMLImport.GetTables().GetCurrentXShapes();
+ sal_Int32 nOldShapeCount = xShapes.is() ? xShapes->getCount() : 0;
+
+ OSL_ENSURE( !mxAnnotationData->mxShape.is() || mxAnnotationData->mxShapes.is(),
+ "ScXMLTableRowCellContext::SetAnnotation - shape without drawing page" );
+ if( mxAnnotationData->mxShape.is() && mxAnnotationData->mxShapes.is() )
+ {
+ OSL_ENSURE( mxAnnotationData->mxShapes.get() == xShapes.get(), "ScXMLTableRowCellContext::SetAnnotation - different drawing pages" );
+
+ /* Don't attempt to get the style from the SdrObject,
+ as it might be a default assigned one. */
+ auto pStyle = rXMLImport.GetShapeImport()->GetAutoStylesContext()->FindStyleChildContext(
+ XmlStyleFamily::SD_GRAPHICS_ID, mxAnnotationData->maStyleName);
+ OUString aStyleName = pStyle ? pStyle->GetParentName() : mxAnnotationData->maStyleName;
+ assert(!rXMLImport.GetShapeImport()->GetAutoStylesContext()->FindStyleChildContext(
+ XmlStyleFamily::SD_GRAPHICS_ID, aStyleName));
+ aStyleName = rXMLImport.GetStyleDisplayName(XmlStyleFamily::SD_GRAPHICS_ID, aStyleName);
+
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxAnnotationData->mxShape);
+ OSL_ENSURE( pObject, "ScXMLTableRowCellContext::SetAnnotation - cannot get SdrObject from shape" );
+
+ /* Try to reuse the drawing object already created (but only if the
+ note is visible, and the object is a caption object). */
+ if( mxAnnotationData->mbShown && mxAnnotationData->mbUseShapePos && !comphelper::LibreOfficeKit::isActive())
+ {
+ if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
+ {
+ OSL_ENSURE( !pCaption->GetLogicRect().IsEmpty(), "ScXMLTableRowCellContext::SetAnnotation - invalid caption rectangle" );
+ // create the cell note with the caption object
+ pNote = ScNoteUtil::CreateNoteFromCaption( *pDoc, rPos, pCaption, !aStyleName.isEmpty() );
+ // forget pointer to object (do not create note again below)
+ pObject = nullptr;
+ }
+ }
+
+ // drawing object has not been used to create a note -> use shape data
+ if( pObject )
+ {
+ // rescue settings from drawing object before the shape is removed
+ SfxItemSet aItemSet( pObject->GetMergedItemSet() );
+ std::optional<OutlinerParaObject> pOutlinerObj;
+ if (auto p = pObject->GetOutlinerParaObject())
+ pOutlinerObj = *p;
+ tools::Rectangle aCaptionRect;
+ if( mxAnnotationData->mbUseShapePos )
+ aCaptionRect = pObject->GetLogicRect();
+ // remove the shape from the drawing page, this invalidates pObject
+ mxAnnotationData->mxShapes->remove( mxAnnotationData->mxShape );
+ pObject = nullptr;
+ // update current number of existing objects
+ if( xShapes.is() )
+ nOldShapeCount = xShapes->getCount();
+
+ // an outliner object is required (empty note captions not allowed)
+ if (pOutlinerObj)
+ {
+ // create cell note with all data from drawing object
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
+ std::move(aItemSet), aStyleName, *pOutlinerObj,
+ aCaptionRect, mxAnnotationData->mbShown );
+ }
+ else
+ {
+ pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
+ std::move(aItemSet), aStyleName, *pOutlinerObj,
+ aCaptionRect, false );
+ }
+
+ }
+ }
+ }
+ else if( !mxAnnotationData->maSimpleText.isEmpty() )
+ {
+ // create note from simple text
+ pNote = ScNoteUtil::CreateNoteFromString( *pDoc, rPos,
+ mxAnnotationData->maSimpleText, mxAnnotationData->mbShown, false );
+ }
+
+ // set author and date
+ if( pNote )
+ {
+ double fDate;
+ if (rXMLImport.GetMM100UnitConverter().convertDateTime(fDate, mxAnnotationData->maCreateDate))
+ {
+ SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
+ sal_uInt32 nfIndex = pNumForm->GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_SYSTEM );
+ OUString aDate;
+ const Color* pColor = nullptr;
+ pNumForm->GetOutputString( fDate, nfIndex, aDate, &pColor );
+ pNote->SetDate( aDate );
+ }
+ pNote->SetAuthor( mxAnnotationData->maAuthor );
+ }
+
+ // register a shape that has been newly created in the ScNoteUtil functions
+ if( xShapes.is() && (nOldShapeCount < xShapes->getCount()) )
+ {
+ uno::Reference< drawing::XShape > xShape;
+ rXMLImport.GetShapeImport()->shapeWithZIndexAdded( xShape, xShapes->getCount() );
+ }
+
+ // store the style names for stream copying
+ ScSheetSaveData* pSheetData = rXMLImport.GetScModel()->GetSheetSaveData();
+ pSheetData->HandleNoteStyles( mxAnnotationData->maStyleName, mxAnnotationData->maTextStyle, rPos );
+
+ for (const auto& rContentStyle : mxAnnotationData->maContentStyles)
+ {
+ pSheetData->AddNoteContentStyle( rContentStyle.mnFamily, rContentStyle.maName, rPos, rContentStyle.maSelection );
+ }
+}
+
+// core implementation
+void ScXMLTableRowCellContext::SetDetectiveObj( const ScAddress& rPosition )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( !pDoc || !cellExists(*pDoc, rPosition) || !pDetectiveObjVec || pDetectiveObjVec->empty() )
+ return;
+
+ LockSolarMutex();
+ ScDetectiveFunc aDetFunc( *pDoc, rPosition.Tab() );
+ uno::Reference<container::XIndexAccess> xShapesIndex = rXMLImport.GetTables().GetCurrentXShapes(); // make draw page
+ for(const auto& rDetectiveObj : *pDetectiveObjVec)
+ {
+ aDetFunc.InsertObject( rDetectiveObj.eObjType, rPosition, rDetectiveObj.aSourceRange, rDetectiveObj.bHasError );
+ if (xShapesIndex.is())
+ {
+ sal_Int32 nShapes = xShapesIndex->getCount();
+ uno::Reference < drawing::XShape > xShape;
+ rXMLImport.GetShapeImport()->shapeWithZIndexAdded(xShape, nShapes);
+ }
+ }
+}
+
+// core implementation
+void ScXMLTableRowCellContext::SetCellRangeSource( const ScAddress& rPosition )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( !pDoc || !cellExists(*pDoc, rPosition) || !pCellRangeSource || pCellRangeSource->sSourceStr.isEmpty() ||
+ pCellRangeSource->sFilterName.isEmpty() || pCellRangeSource->sURL.isEmpty() )
+ return;
+
+ LockSolarMutex();
+ ScRange aDestRange( rPosition.Col(), rPosition.Row(), rPosition.Tab(),
+ rPosition.Col() + static_cast<SCCOL>(pCellRangeSource->nColumns - 1),
+ rPosition.Row() + static_cast<SCROW>(pCellRangeSource->nRows - 1), rPosition.Tab() );
+ OUString sFilterName( pCellRangeSource->sFilterName );
+ OUString sSourceStr( pCellRangeSource->sSourceStr );
+ ScAreaLink* pLink = new ScAreaLink( pDoc->GetDocumentShell(), pCellRangeSource->sURL,
+ sFilterName, pCellRangeSource->sFilterOptions, sSourceStr, aDestRange, pCellRangeSource->nRefresh );
+ sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, pCellRangeSource->sURL, &sFilterName, &sSourceStr );
+}
+
+void ScXMLTableRowCellContext::SetFormulaCell(ScFormulaCell* pFCell) const
+{
+ if(!pFCell)
+ return;
+
+ bool bMayForceNumberformat = true;
+
+ if(mbErrorValue)
+ {
+ // don't do anything here
+ // we need to recalc anyway
+ }
+ else if( bFormulaTextResult && maStringValue )
+ {
+ if( !IsPossibleErrorString() )
+ {
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(*maStringValue));
+ pFCell->ResetDirty();
+ // A General format doesn't force any other format for a string
+ // result, don't attempt to recalculate this later.
+ bMayForceNumberformat = false;
+ }
+ }
+ else if (std::isfinite(fValue))
+ {
+ pFCell->SetHybridDouble(fValue);
+ if (mbPossibleEmptyDisplay && fValue == 0.0)
+ {
+ // Needs to be recalculated to propagate, otherwise would be
+ // propagated as empty string. So don't ResetDirty().
+ pFCell->SetHybridEmptyDisplayedAsString();
+ }
+ else
+ pFCell->ResetDirty();
+ }
+
+ if (bMayForceNumberformat)
+ // Re-calculate to get number format only when style is not set.
+ pFCell->SetNeedNumberFormat(!mbHasStyle);
+}
+
+void ScXMLTableRowCellContext::PutTextCell( const ScAddress& rCurrentPos,
+ const SCCOL nCurrentCol, const ::std::optional< OUString >& pOUText )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ bool bDoIncrement = true;
+ //matrix reference cells that contain text formula results;
+ //cell was already put in document, just need to set text here.
+ if( pDoc && rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
+ {
+ ScRefCellValue aCell(*pDoc, rCurrentPos);
+ bDoIncrement = aCell.getType() == CELLTYPE_FORMULA;
+ if ( bDoIncrement )
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ OUString aCellString;
+ if (maStringValue)
+ aCellString = *maStringValue;
+ else if (mbEditEngineHasText)
+ aCellString = GetFirstParagraph();
+ else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
+ aCellString = *pOUText;
+ else
+ bDoIncrement = false;
+
+ if(mbErrorValue)
+ bDoIncrement = false;
+
+ if(!aCellString.isEmpty())
+ {
+ if (bDoIncrement && !IsPossibleErrorString() && pFCell)
+ {
+ pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(aCellString));
+ pFCell->ResetDirty();
+ }
+ else
+ {
+ ScAddress aTopLeftMatrixCell;
+ if (pFCell && pFCell->GetMatrixOrigin(*pDoc, aTopLeftMatrixCell))
+ {
+ ScFormulaCell* pMatrixCell = pDoc->GetFormulaCell(aTopLeftMatrixCell);
+ if (pMatrixCell)
+ pMatrixCell->SetDirty();
+ }
+ else
+ SAL_WARN("sc", "matrix cell without matrix");
+ }
+ }
+ }
+ }
+ else //regular text cells
+ {
+ ScDocumentImport& rDoc = rXMLImport.GetDoc();
+ if (maStringValue)
+ {
+ rDoc.setStringCell(rCurrentPos, *maStringValue);
+ bDoIncrement = true;
+ }
+ else if (mbEditEngineHasText)
+ {
+ if (maFirstParagraph)
+ {
+ // This is a normal text without format runs.
+ rDoc.setStringCell(rCurrentPos, *maFirstParagraph);
+ }
+ else
+ {
+ // This text either has format runs, has field(s), or consists of multiple lines.
+ for (const auto& rxFormat : maFormats)
+ mpEditEngine->QuickSetAttribs(rxFormat->maItemSet, rxFormat->maSelection);
+
+ for (const auto& rxField : maFields)
+ mpEditEngine->QuickInsertField(SvxFieldItem(*rxField->mpData, EE_FEATURE_FIELD), rxField->maSelection);
+
+ // This edit engine uses the SfxItemPool instance returned
+ // from pDoc->GetEditPool() to create the text object, which
+ // is a prerequisite for using this constructor of ScEditCell.
+ rDoc.setEditCell(rCurrentPos, mpEditEngine->CreateTextObject());
+ }
+ bDoIncrement = true;
+ }
+ else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
+ {
+ rDoc.setStringCell(rCurrentPos, *pOUText);
+ bDoIncrement = true;
+ }
+ else
+ bDoIncrement = false;
+ }
+
+ // #i56027# This is about setting simple text, not edit cells,
+ // so ProgressBarIncrement must be called with bEditCell = FALSE.
+ // Formatted text that is put into the cell by the child context
+ // is handled in AddCellsToTable() (bIsEmpty is true then).
+ if (bDoIncrement)
+ rXMLImport.ProgressBarIncrement();
+}
+
+void ScXMLTableRowCellContext::PutValueCell( const ScAddress& rCurrentPos )
+{
+ //matrix reference cells that contain value formula results;
+ //cell was already put in document, just need to set value here.
+ if( rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
+ {
+ ScRefCellValue aCell(*rXMLImport.GetDocument(), rCurrentPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ SetFormulaCell(pFCell);
+ if (pFCell)
+ pFCell->SetNeedNumberFormat( true );
+ }
+ }
+ else //regular value cell
+ {
+ // fdo#62250 absent values are not NaN, set to 0.0
+ // PutValueCell() is called only for a known cell value type,
+ // bIsEmpty==false in all these cases, no sense to check it here.
+ if (!std::isfinite( fValue))
+ fValue = 0.0;
+
+ // #i62435# Initialize the value cell's script type if the default
+ // style's number format is latin-only. If the cell uses a different
+ // format, the script type will be reset when the style is applied.
+
+ rXMLImport.GetDoc().setNumericCell(rCurrentPos, fValue);
+ }
+ rXMLImport.ProgressBarIncrement();
+}
+
+namespace {
+
+bool isEmptyOrNote( const ScDocument* pDoc, const ScAddress& rCurrentPos )
+{
+ CellType eType = pDoc->GetCellType(rCurrentPos);
+ return (eType == CELLTYPE_NONE);
+}
+
+}
+
+void ScXMLTableRowCellContext::AddTextAndValueCell( const ScAddress& rCellPos,
+ const ::std::optional< OUString >& pOUText, ScAddress& rCurrentPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScMyTables& rTables = rXMLImport.GetTables();
+ bool bWasEmpty = bIsEmpty;
+ for (SCCOL i = 0; i < nColsRepeated; ++i)
+ {
+ rCurrentPos.SetCol( rCellPos.Col() + i );
+
+ // it makes no sense to import data after the last supported column
+ // fdo#58539 & gnome#627150
+ if(rCurrentPos.Col() > pDoc->MaxCol())
+ {
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ break;
+ }
+
+ if (i > 0)
+ rTables.AddColumn(false);
+ if (!bIsEmpty)
+ {
+ for (SCROW j = 0; j < nRepeatedRows; ++j)
+ {
+ rCurrentPos.SetRow( rCellPos.Row() + j );
+
+ // it makes no sense to import data after last supported row
+ // fdo#58539 & gnome#627150
+ if(rCurrentPos.Row() > pDoc->MaxRow())
+ {
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ break;
+ }
+
+ if( (rCurrentPos.Col() == 0) && (j > 0) )
+ {
+ rTables.AddRow();
+ rTables.AddColumn(false);
+ }
+ if( cellExists(*pDoc, rCurrentPos) )
+ {
+ if( !bIsCovered || isEmptyOrNote(pDoc, rCurrentPos) )
+ {
+ switch (nCellType)
+ {
+ case util::NumberFormat::TEXT:
+ {
+ PutTextCell( rCurrentPos, i, pOUText );
+ }
+ break;
+ case util::NumberFormat::NUMBER:
+ case util::NumberFormat::PERCENT:
+ case util::NumberFormat::CURRENCY:
+ case util::NumberFormat::TIME:
+ case util::NumberFormat::DATETIME:
+ case util::NumberFormat::LOGICAL:
+ {
+ PutValueCell( rCurrentPos );
+ }
+ break;
+ default:
+ {
+ OSL_FAIL("no cell type given");
+ }
+ break;
+ }
+ }
+
+ SetAnnotation( rCurrentPos );
+ SetDetectiveObj( rCurrentPos );
+ SetCellRangeSource( rCurrentPos );
+ }
+ else
+ {
+ if (!bWasEmpty || mxAnnotationData)
+ {
+ if (rCurrentPos.Row() > pDoc->MaxRow())
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ else
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((i == 0) && (rCellPos.Col() == 0))
+ {
+ for (sal_Int32 j = 1; j < nRepeatedRows; ++j)
+ {
+ rTables.AddRow();
+ rTables.AddColumn(false);
+ }
+ }
+ }
+ }
+}
+
+bool ScXMLTableRowCellContext::CellsAreRepeated() const
+{
+ return ( (nColsRepeated > 1) || (nRepeatedRows > 1) );
+}
+
+namespace {
+
+// from ScCellObj::GetOutputString_Imp(). all of it may not be necessary.
+OUString getOutputString( ScDocument* pDoc, const ScAddress& aCellPos )
+{
+ if (!pDoc)
+ return OUString();
+
+ ScRefCellValue aCell(*pDoc, aCellPos);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_NONE:
+ return OUString();
+ case CELLTYPE_EDIT:
+ {
+ // GetString on EditCell replaces linebreaks with spaces;
+ // however here we need line breaks
+ const EditTextObject* pData = aCell.getEditText();
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(*pData);
+ return rEngine.GetText();
+ // also don't format EditCells per NumberFormatter
+ }
+ break;
+ default:
+ {
+ // like in GetString for document (column)
+ const Color* pColor;
+ sal_uInt32 nNumFmt = pDoc->GetNumberFormat(aCellPos);
+ return ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pDoc->GetFormatTable(), *pDoc);
+ }
+ }
+}
+
+}
+
+void ScXMLTableRowCellContext::AddNonFormulaCell( const ScAddress& rCellPos )
+{
+ ::std::optional< OUString > pOUText;
+
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( nCellType == util::NumberFormat::TEXT )
+ {
+ if( !bIsEmpty && !maStringValue && !mbEditEngineHasText && cellExists(*pDoc, rCellPos) && CellsAreRepeated() )
+ pOUText = getOutputString(pDoc, rCellPos);
+
+ if (!mbEditEngineHasText && !pOUText && !maStringValue)
+ bIsEmpty = true;
+ }
+
+ ScAddress aCurrentPos( rCellPos );
+ if( mxAnnotationData || pDetectiveObjVec || pCellRangeSource ) // has special content
+ bIsEmpty = false;
+
+ AddTextAndValueCell( rCellPos, pOUText, aCurrentPos );
+
+ if( CellsAreRepeated() )
+ {
+ SCCOL nStartCol( std::min(rCellPos.Col(), pDoc->MaxCol()) );
+ SCROW nStartRow( std::min(rCellPos.Row(), pDoc->MaxRow()) );
+ SCCOL nEndCol( std::min<SCCOL>(rCellPos.Col() + nColsRepeated - 1, pDoc->MaxCol()) );
+ SCROW nEndRow( std::min(rCellPos.Row() + nRepeatedRows - 1, pDoc->MaxRow()) );
+ ScRange aScRange( nStartCol, nStartRow, rCellPos.Tab(), nEndCol, nEndRow, rCellPos.Tab() );
+ SetContentValidation( aScRange );
+ rXMLImport.GetStylesImportHelper()->AddRange( aScRange );
+ }
+ else if( cellExists(*pDoc, rCellPos) )
+ {
+ rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
+ SetContentValidation( rCellPos );
+ }
+}
+
+void ScXMLTableRowCellContext::PutFormulaCell( const ScAddress& rCellPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScDocumentImport& rDocImport = rXMLImport.GetDoc();
+
+ const OUString & aText = maFormula->first;
+
+ ScExternalRefManager::ApiGuard aExtRefGuard(*pDoc);
+
+ if ( aText.isEmpty() )
+ return;
+
+ // temporary formula string as string tokens
+ std::unique_ptr<ScTokenArray> pCode(new ScTokenArray(*pDoc));
+
+ // Check the special case of a single error constant without leading
+ // '=' and create an error formula cell without tokens.
+ FormulaError nError = GetScImport().GetFormulaErrorConstant(aText);
+ if (nError != FormulaError::NONE)
+ {
+ pCode->SetCodeError(nError);
+ }
+ else
+ {
+ // 5.2 and earlier wrote broken "Err:xxx" as formula to designate
+ // an error formula cell.
+ if (aText.startsWithIgnoreAsciiCase("Err:") && aText.getLength() <= 9 &&
+ ((nError =
+ GetScImport().GetFormulaErrorConstant( OUString::Concat("#ERR") + aText.subView(4) + "!")) != FormulaError::NONE))
+ {
+ pCode->SetCodeError(nError);
+ }
+ else
+ {
+ OUString aFormulaNmsp = maFormula->second;
+ if( eGrammar != formula::FormulaGrammar::GRAM_EXTERNAL )
+ aFormulaNmsp.clear();
+ pCode->AssignXMLString( aText, aFormulaNmsp );
+ rDocImport.getDoc().IncXMLImportedFormulaCount( aText.getLength() );
+ }
+ }
+
+ ScFormulaCell* pNewCell = new ScFormulaCell(*pDoc, rCellPos, std::move(pCode), eGrammar, ScMatrixMode::NONE);
+ SetFormulaCell(pNewCell);
+ rDocImport.setFormulaCell(rCellPos, pNewCell);
+}
+
+void ScXMLTableRowCellContext::AddFormulaCell( const ScAddress& rCellPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( cellExists(*pDoc, rCellPos) )
+ {
+ SetContentValidation( rCellPos );
+ SAL_WARN_IF((nColsRepeated != 1) || (nRepeatedRows != 1), "sc", "repeated cells with formula not possible now");
+ rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
+
+ //add matrix
+ if(bIsMatrix)
+ {
+ if (nMatrixCols > 0 && nMatrixRows > 0)
+ {
+ //matrix cells are put in the document, but we must set the
+ //value/text of each matrix cell later
+ rXMLImport.GetTables().AddMatrixRange(
+ rCellPos.Col(), rCellPos.Row(),
+ std::min<SCCOL>(rCellPos.Col() + nMatrixCols - 1, pDoc->MaxCol()),
+ std::min<SCROW>(rCellPos.Row() + nMatrixRows - 1, pDoc->MaxRow()),
+ maFormula->first, maFormula->second, eGrammar);
+
+ // Set the value/text of the top-left matrix position in its
+ // cached result. For import, we only need to set the correct
+ // matrix geometry and the value type of the top-left element.
+ ScFormulaCell* pFCell = pDoc->GetFormulaCell(rCellPos);
+ if (pFCell)
+ {
+ ScMatrixRef pMat(new ScMatrix(nMatrixCols, nMatrixRows));
+ if (bFormulaTextResult && maStringValue)
+ {
+ if (!IsPossibleErrorString())
+ {
+ pFCell->SetResultMatrix(
+ nMatrixCols, nMatrixRows, pMat, new formula::FormulaStringToken(
+ pDoc->GetSharedStringPool().intern( *maStringValue)));
+ pFCell->ResetDirty();
+ }
+ }
+ else if (std::isfinite(fValue))
+ {
+ pFCell->SetResultMatrix(
+ nMatrixCols, nMatrixRows, pMat, new formula::FormulaDoubleToken(fValue));
+ pFCell->ResetDirty();
+ }
+ }
+ }
+ }
+ else
+ PutFormulaCell( rCellPos );
+
+ SetAnnotation( rCellPos );
+ SetDetectiveObj( rCellPos );
+ SetCellRangeSource( rCellPos );
+ rXMLImport.ProgressBarIncrement();
+ }
+ else
+ {
+ if (rCellPos.Row() > pDoc->MaxRow())
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ else
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ }
+}
+
+//There are cases where a formula cell is exported with an office:value of 0 or
+//no office:value at all, but the formula cell will have a text:p value which
+//contains the intended formula result.
+//These cases include when a formula result:
+// - is blank
+// - has a constant error value beginning with "#" (such as "#VALUE!" or "#N/A")
+// - has an "Err:[###]" (where "[###]" is an error number)
+// Libreoffice 4.1+ with ODF1.2 extended write however calcext:value-type="error" in that case
+void ScXMLTableRowCellContext::HasSpecialCaseFormulaText()
+{
+ if (!mbEditEngineHasText)
+ return;
+
+ const OUString aStr = GetFirstParagraph();
+
+ if (mbNewValueType)
+ {
+ if (aStr.isEmpty())
+ mbPossibleEmptyDisplay = true;
+ return;
+ }
+
+ if (aStr.isEmpty())
+ {
+ mbPossibleErrorCell = true;
+ mbPossibleEmptyDisplay = true;
+ }
+ else if (aStr.startsWith("Err:"))
+ mbPossibleErrorCell = true;
+ else if (aStr.startsWith("#"))
+ mbCheckWithCompilerForError = true;
+}
+
+bool ScXMLTableRowCellContext::IsPossibleErrorString() const
+{
+ if(mbNewValueType)
+ return mbErrorValue;
+
+ return mbPossibleErrorCell || (mbCheckWithCompilerForError && maStringValue &&
+ GetScImport().GetFormulaErrorConstant(*maStringValue) != FormulaError::NONE);
+}
+
+void SAL_CALL ScXMLTableRowCellContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ HasSpecialCaseFormulaText();
+ if( bFormulaTextResult && (mbPossibleErrorCell || mbCheckWithCompilerForError) )
+ {
+ maStringValue = GetFirstParagraph();
+ }
+
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ if( aCellPos.Col() > 0 && nRepeatedRows > 1 )
+ aCellPos.SetRow( aCellPos.Row() - (nRepeatedRows - 1) );
+ if( bIsMerged )
+ DoMerge( aCellPos, nMergedCols - 1, nMergedRows - 1 );
+
+ if (maFormula)
+ AddFormulaCell(aCellPos);
+ else
+ AddNonFormulaCell(aCellPos);
+
+ //if LockSolarMutex got used, we presumably need to ensure an UnlockSolarMutex
+ if (bSolarMutexLocked)
+ {
+ GetScImport().UnlockSolarMutex();
+ bSolarMutexLocked = false;
+ }
+
+ bIsMerged = false;
+ nMergedCols = 1;
+ nMergedRows = 1;
+ nColsRepeated = 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcelli.hxx b/sc/source/filter/xml/xmlcelli.hxx
new file mode 100644
index 0000000000..73d839e692
--- /dev/null
+++ b/sc/source/filter/xml/xmlcelli.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 .
+ */
+#pragma once
+
+#include "XMLDetectiveContext.hxx"
+#include "importcontext.hxx"
+#include <formula/grammar.hxx>
+#include <svl/itemset.hxx>
+#include <editeng/editdata.hxx>
+
+#include <optional>
+#include <memory>
+#include <vector>
+
+class ScXMLImport;
+class ScFormulaCell;
+class ScEditEngineDefaulter;
+class SvxFieldData;
+struct ScXMLAnnotationData;
+struct ScMyImpCellRangeSource;
+
+class ScXMLTableRowCellContext : public ScXMLImportContext
+{
+ struct ParaFormat
+ {
+ SfxItemSet maItemSet;
+ ESelection maSelection;
+
+ explicit ParaFormat(const ScEditEngineDefaulter& rEditEngine);
+ };
+
+ struct Field
+ {
+ std::unique_ptr<SvxFieldData> mpData;
+ ESelection maSelection;
+
+ Field(const Field&) = delete;
+ const Field& operator=(const Field&) = delete;
+
+ explicit Field(std::unique_ptr<SvxFieldData> pData);
+ ~Field();
+ };
+
+ typedef std::vector<std::unique_ptr<Field> > FieldsType;
+ typedef std::pair<OUString, OUString> FormulaWithNamespace;
+
+ std::optional<FormulaWithNamespace> maFormula; /// table:formula attribute
+ std::optional<OUString> maStringValue; /// office:string-value attribute
+ std::optional<OUString> maContentValidationName;
+ std::optional<OUString> maFirstParagraph; /// unformatted first paragraph, for better performance.
+
+ ScEditEngineDefaulter* mpEditEngine;
+ OUStringBuffer maParagraph{32};
+ sal_Int32 mnCurParagraph;
+
+ std::vector<std::unique_ptr<ParaFormat> > maFormats;
+ FieldsType maFields;
+
+ std::unique_ptr< ScXMLAnnotationData > mxAnnotationData;
+ std::unique_ptr< ScMyImpDetectiveObjVec > pDetectiveObjVec;
+ std::unique_ptr< ScMyImpCellRangeSource > pCellRangeSource;
+ double fValue;
+ SCROW nMergedRows, nMatrixRows, nRepeatedRows;
+ SCCOL nMergedCols, nMatrixCols, nColsRepeated;
+ ScXMLImport& rXMLImport;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_Int16 nCellType;
+ bool bIsMerged;
+ bool bIsMatrix;
+ bool bIsCovered;
+ bool bIsEmpty;
+ bool mbNewValueType;
+ bool mbErrorValue;
+ bool bSolarMutexLocked;
+ bool bFormulaTextResult;
+ bool mbPossibleErrorCell;
+ bool mbCheckWithCompilerForError;
+ bool mbEditEngineHasText;
+ bool mbHasFormatRuns;
+ bool mbHasStyle;
+ bool mbPossibleEmptyDisplay;
+
+ void DoMerge(const ScAddress& rScCellPos, const SCCOL nCols, const SCROW nRows);
+
+ void SetContentValidation( const ScRange& rScRange );
+ void SetContentValidation( const ScAddress& rScCellPos );
+
+ void LockSolarMutex();
+
+ bool CellsAreRepeated() const;
+
+ void SetFormulaCell ( ScFormulaCell* pFCell ) const;
+ void PutTextCell ( const ScAddress& rScCurrentPos, const SCCOL nCurrentCol,
+ const ::std::optional< OUString >& pOUText );
+ void PutValueCell ( const ScAddress& rScCurrentPos );
+ void AddTextAndValueCell ( const ScAddress& rScCellPos,
+ const ::std::optional< OUString >& pOUText, ScAddress& rScCurrentPos );
+ void AddNonFormulaCell ( const ScAddress& rScCellPos );
+ void PutFormulaCell ( const ScAddress& rScCurrentPos );
+ void AddFormulaCell ( const ScAddress& rScCellPos );
+
+ void HasSpecialCaseFormulaText();
+
+ bool IsPossibleErrorString() const;
+
+ void PushParagraphField(std::unique_ptr<SvxFieldData> pData, const OUString& rStyleName);
+
+ void PushFormat(sal_Int32 nBegin, sal_Int32 nEnd, const OUString& rStyleName);
+
+ OUString GetFirstParagraph() const;
+
+public:
+
+ ScXMLTableRowCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bIsCovered, const sal_Int32 nRepeatedRows );
+
+ virtual ~ScXMLTableRowCellContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void PushParagraphSpan(std::u16string_view rSpan, const OUString& rStyleName);
+ void PushParagraphFieldDate(const OUString& rStyleName);
+ void PushParagraphFieldSheetName(const OUString& rStyleName);
+ void PushParagraphFieldDocTitle(const OUString& rStyleName);
+ void PushParagraphFieldURL(const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame);
+ void PushParagraphEnd();
+
+ void SetAnnotation( const ScAddress& rPosition );
+ void SetDetectiveObj( const ScAddress& rPosition );
+ void SetCellRangeSource( const ScAddress& rPosition );
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcoli.cxx b/sc/source/filter/xml/xmlcoli.cxx
new file mode 100644
index 0000000000..f835bddb7c
--- /dev/null
+++ b/sc/source/filter/xml/xmlcoli.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlcoli.hxx"
+#include "xmlimprt.hxx"
+#include "xmlstyli.hxx"
+#include <document.hxx>
+#include <docuno.hxx>
+#include <olinetab.hxx>
+#include <sheetdata.hxx>
+#include <unonames.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <comphelper/servicehelper.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableColContext::ScXMLTableColContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nColCount(1),
+ sVisibility(GetXMLToken(XML_VISIBLE))
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ {
+ nColCount = std::max<sal_Int32>(aIter.toInt32(), 1);
+ nColCount = std::min<sal_Int32>(nColCount, rImport.GetDocument()->GetSheetLimits().GetMaxColCount() );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ {
+ sStyleName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VISIBILITY ):
+ {
+ sVisibility = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DEFAULT_CELL_STYLE_NAME ):
+ {
+ sCellStyleName = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLTableColContext::~ScXMLTableColContext()
+{
+}
+
+void SAL_CALL ScXMLTableColContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScXMLImport& rXMLImport = GetScImport();
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ SCTAB nSheet = rXMLImport.GetTables().GetCurrentSheet();
+ sal_Int32 nCurrentColumn = rXMLImport.GetTables().GetCurrentColCount();
+ uno::Reference<sheet::XSpreadsheet> xSheet(rXMLImport.GetTables().GetCurrentXSheet());
+ if(xSheet.is())
+ {
+ sal_Int32 nLastColumn(nCurrentColumn + nColCount - 1);
+ if (nLastColumn > pDoc->MaxCol())
+ nLastColumn = pDoc->MaxCol();
+ if (nCurrentColumn > pDoc->MaxCol())
+ nCurrentColumn = pDoc->MaxCol();
+ uno::Reference<table::XColumnRowRange> xColumnRowRange (xSheet->getCellRangeByPosition(nCurrentColumn, 0, nLastColumn, 0), uno::UNO_QUERY);
+ if (xColumnRowRange.is())
+ {
+ uno::Reference <beans::XPropertySet> xColumnProperties(xColumnRowRange->getColumns(), uno::UNO_QUERY);
+ if (xColumnProperties.is())
+ {
+ if (!sStyleName.isEmpty())
+ {
+ XMLTableStylesContext *pStyles = static_cast<XMLTableStylesContext *>(rXMLImport.GetAutoStyles());
+ if ( pStyles )
+ {
+ XMLTableStyleContext* pStyle = const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_COLUMN, sStyleName, true)));
+ if (pStyle)
+ {
+ pStyle->FillPropertySet(xColumnProperties);
+
+ if ( nSheet != pStyle->GetLastSheet() )
+ {
+ ScSheetSaveData* pSheetData = rXMLImport.GetScModel()->GetSheetSaveData();
+ pSheetData->AddColumnStyle( sStyleName, ScAddress( static_cast<SCCOL>(nCurrentColumn), 0, nSheet ) );
+ pStyle->SetLastSheet(nSheet);
+ }
+ }
+ }
+ }
+ bool bValue(true);
+ if (!IsXMLToken(sVisibility, XML_VISIBLE))
+ bValue = false;
+ xColumnProperties->setPropertyValue(SC_UNONAME_CELLVIS, uno::Any(bValue));
+ }
+ }
+ }
+
+ // #i57915# ScXMLImport::SetStyleToRange can't handle empty style names.
+ // The default for a column if there is no attribute is the style "Default" (programmatic API name).
+ if ( sCellStyleName.isEmpty() )
+ sCellStyleName = "Default";
+
+ GetScImport().GetTables().AddColStyle(nColCount, sCellStyleName);
+}
+
+ScXMLTableColsContext::ScXMLTableColsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempHeader, const bool bTempGroup) :
+ ScXMLImportContext( rImport ),
+ nHeaderStartCol(0),
+ nHeaderEndCol(0),
+ nGroupStartCol(0),
+ nGroupEndCol(0),
+ bHeader(bTempHeader),
+ bGroup(bTempGroup),
+ bGroupDisplay(true)
+{
+ // don't have any attributes
+ if (bHeader)
+ nHeaderStartCol = rImport.GetTables().GetCurrentColCount();
+ else if (bGroup)
+ {
+ nGroupStartCol = rImport.GetTables().GetCurrentColCount();
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_DISPLAY ) ) );
+ if ( aIter != rAttrList->end() && IsXMLToken(aIter, XML_FALSE) )
+ bGroupDisplay = false;
+ }
+ }
+}
+
+ScXMLTableColsContext::~ScXMLTableColsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTableColsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN_GROUP ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN ):
+ pContext = new ScXMLTableColContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableColsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScXMLImport& rXMLImport = GetScImport();
+ if (bHeader)
+ {
+ nHeaderEndCol = rXMLImport.GetTables().GetCurrentColCount();
+ nHeaderEndCol--;
+ if (nHeaderStartCol <= nHeaderEndCol)
+ {
+ uno::Reference <sheet::XPrintAreas> xPrintAreas (rXMLImport.GetTables().GetCurrentXSheet(), uno::UNO_QUERY);
+ if (xPrintAreas.is())
+ {
+ if (!xPrintAreas->getPrintTitleColumns())
+ {
+ xPrintAreas->setPrintTitleColumns(true);
+ table::CellRangeAddress aColumnHeaderRange;
+ aColumnHeaderRange.StartColumn = nHeaderStartCol;
+ aColumnHeaderRange.EndColumn = nHeaderEndCol;
+ xPrintAreas->setTitleColumns(aColumnHeaderRange);
+ }
+ else
+ {
+ table::CellRangeAddress aColumnHeaderRange(xPrintAreas->getTitleColumns());
+ aColumnHeaderRange.EndColumn = nHeaderEndCol;
+ xPrintAreas->setTitleColumns(aColumnHeaderRange);
+ }
+ }
+ }
+ }
+ else if (bGroup)
+ {
+ SCTAB nSheet = rXMLImport.GetTables().GetCurrentSheet();
+ nGroupEndCol = rXMLImport.GetTables().GetCurrentColCount();
+ nGroupEndCol--;
+ if (nGroupStartCol <= nGroupEndCol)
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (pDoc)
+ {
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScOutlineTable* pOutlineTable = pDoc->GetOutlineTable(nSheet, true);
+ if (pOutlineTable)
+ {
+ ScOutlineArray& rColArray = pOutlineTable->GetColArray();
+ bool bResized;
+ rColArray.Insert(static_cast<SCCOL>(nGroupStartCol), static_cast<SCCOL>(nGroupEndCol), bResized, !bGroupDisplay);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcoli.hxx b/sc/source/filter/xml/xmlcoli.hxx
new file mode 100644
index 0000000000..204d9cd4bc
--- /dev/null
+++ b/sc/source/filter/xml/xmlcoli.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLTableColContext : public ScXMLImportContext
+{
+ sal_Int32 nColCount;
+ OUString sStyleName;
+ OUString sVisibility;
+ OUString sCellStyleName;
+
+public:
+
+ ScXMLTableColContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLTableColContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLTableColsContext : public ScXMLImportContext
+{
+ sal_Int32 nHeaderStartCol;
+ sal_Int32 nHeaderEndCol;
+ sal_Int32 nGroupStartCol;
+ sal_Int32 nGroupEndCol;
+ bool bHeader;
+ bool bGroup;
+ bool bGroupDisplay;
+
+public:
+
+ ScXMLTableColsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bHeader, const bool bGroup);
+
+ virtual ~ScXMLTableColsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcondformat.cxx b/sc/source/filter/xml/xmlcondformat.cxx
new file mode 100644
index 0000000000..23127414a1
--- /dev/null
+++ b/sc/source/filter/xml/xmlcondformat.cxx
@@ -0,0 +1,1007 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include "xmlcondformat.hxx"
+#include "xmlimprt.hxx"
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <sal/log.hxx>
+
+#include <compiler.hxx>
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <document.hxx>
+#include <sax/tools/converter.hxx>
+#include <rangelst.hxx>
+#include <rangeutl.hxx>
+#include "XMLConverter.hxx"
+#include <stylehelper.hxx>
+#include <tokenarray.hxx>
+
+using namespace xmloff::token;
+
+ScXMLConditionalFormatsContext::ScXMLConditionalFormatsContext( ScXMLImport& rImport ):
+ ScXMLImportContext( rImport )
+{
+ GetScImport().SetNewCondFormatData();
+ GetScImport().GetDocument()->SetCondFormList(new ScConditionalFormatList(), GetScImport().GetTables().GetCurrentSheet());
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionalFormatsContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_CONDITIONAL_FORMAT ):
+ pContext = new ScXMLConditionalFormatContext( GetScImport(), pAttribList, *this );
+ break;
+ }
+
+ return pContext;
+}
+
+IMPL_LINK(ScXMLConditionalFormatsContext, FormatDeletedHdl, ScConditionalFormat*, pFormat, void)
+{
+ std::erase_if(mvCondFormatData, [pFormat](CondFormatData& r){ return r.mpFormat == pFormat; });
+}
+
+void SAL_CALL ScXMLConditionalFormatsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ ScConditionalFormatList* pCondFormatList = pDoc->GetCondFormList(nTab);
+ bool bDeleted = !pCondFormatList->CheckAllEntries(LINK(this, ScXMLConditionalFormatsContext, FormatDeletedHdl));
+
+ SAL_WARN_IF(bDeleted, "sc", "conditional formats have been deleted because they contained empty range info");
+
+ for (const auto& i : mvCondFormatData)
+ {
+ pDoc->AddCondFormatData( i.mpFormat->GetRange(), i.mnTab, i.mpFormat->GetKey() );
+ }
+}
+
+ScXMLConditionalFormatContext::ScXMLConditionalFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLConditionalFormatsContext& rParent ):
+ ScXMLImportContext( rImport ),
+ mrParent( rParent )
+{
+ OUString sRange;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_TARGET_RANGE_ADDRESS ):
+ sRange = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ScRangeList aRangeList;
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ ScRangeStringConverter::GetRangeListFromString(aRangeList, sRange, *pDoc,
+ formula::FormulaGrammar::CONV_ODF);
+
+ mxFormat.reset(new ScConditionalFormat(0, pDoc));
+ mxFormat->SetRange(aRangeList);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionalFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_CONDITION ):
+ pContext = new ScXMLCondContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_COLOR_SCALE ):
+ pContext = new ScXMLColorScaleFormatContext( GetScImport(), mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_BAR ):
+ pContext = new ScXMLDataBarFormatContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_ICON_SET ):
+ pContext = new ScXMLIconSetFormatContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATE_IS ):
+ pContext = new ScXMLDateContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+static bool HasRelRefIgnoringSheet0Relative( ScDocument* pDoc, const ScTokenArray* pTokens, sal_uInt16 nRecursion = 0 )
+{
+ if (pTokens)
+ {
+ formula::FormulaTokenArrayPlainIterator aIter( *pTokens );
+ formula::FormulaToken* t;
+ for( t = aIter.Next(); t; t = aIter.Next() )
+ {
+ switch( t->GetType() )
+ {
+ case formula::svDoubleRef:
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ if ( rRef2.IsColRel() || rRef2.IsRowRel() || (rRef2.IsFlag3D() && rRef2.IsTabRel()) )
+ return true;
+ [[fallthrough]];
+ }
+
+ case formula::svSingleRef:
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() || rRef1.IsRowRel() || (rRef1.IsFlag3D() && rRef1.IsTabRel()) )
+ return true;
+ }
+ break;
+
+ case formula::svIndex:
+ {
+ if( t->GetOpCode() == ocName ) // DB areas always absolute
+ if( ScRangeData* pRangeData = pDoc->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()) )
+ if( (nRecursion < 42) && HasRelRefIgnoringSheet0Relative( pDoc, pRangeData->GetCode(), nRecursion + 1 ) )
+ return true;
+ }
+ break;
+
+ // #i34474# function result dependent on cell position
+ case formula::svByte:
+ {
+ switch( t->GetOpCode() )
+ {
+ case ocRow: // ROW() returns own row index
+ case ocColumn: // COLUMN() returns own column index
+ case ocSheet: // SHEET() returns own sheet index
+ case ocCell: // CELL() may return own cell address
+ return true;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+static bool HasOneSingleFullyRelativeReference( const ScTokenArray* pTokens, ScSingleRefData& rOffset )
+{
+ int nCount = 0;
+ if (pTokens)
+ {
+ formula::FormulaTokenArrayPlainIterator aIter( *pTokens );
+ formula::FormulaToken* t;
+ for( t = aIter.Next(); t; t = aIter.Next() )
+ {
+ switch( t->GetType() )
+ {
+ case formula::svSingleRef:
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() && rRef1.IsRowRel() && !rRef1.IsFlag3D() && rRef1.IsTabRel() )
+ {
+ nCount++;
+ if (nCount == 1)
+ {
+ rOffset = rRef1;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return nCount == 1;
+}
+
+void SAL_CALL ScXMLConditionalFormatContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ std::unique_ptr<ScConditionalFormat> pFormat(std::move(mxFormat));
+
+ bool bEligibleForCache = true;
+ bool bSingleRelativeReference = false;
+ ScSingleRefData aOffsetForSingleRelRef;
+ std::unique_ptr<ScTokenArray> pTokens;
+ for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx)
+ {
+ auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx);
+ auto pCondFormatEntry = dynamic_cast<const ScCondFormatEntry*>(pFormatEntry);
+
+ if (pCondFormatEntry == nullptr ||
+ (pCondFormatEntry->GetOperation() != ScConditionMode::Equal &&
+ pCondFormatEntry->GetOperation() != ScConditionMode::Direct))
+ {
+ bEligibleForCache = false;
+ break;
+ }
+
+ ScAddress aSrcPos;
+ OUString aSrcString = pCondFormatEntry->GetSrcString();
+ if ( !aSrcString.isEmpty() )
+ aSrcPos.Parse( aSrcString, *pDoc );
+ ScCompiler aComp( *pDoc, aSrcPos );
+ aComp.SetGrammar( formula::FormulaGrammar::GRAM_ODFF );
+ pTokens = aComp.CompileString( pCondFormatEntry->GetExpression(aSrcPos, 0), "" );
+ if (HasRelRefIgnoringSheet0Relative( pDoc, pTokens.get() ))
+ {
+ // In general not eligible, but some might be. We handle one very special case: When the
+ // conditional format has one entry, the reference position is the first cell of the
+ // range, and with a single fully relative reference in its expression. (Possibly these
+ // conditions could be loosened, but I am too tired to think on that right now.)
+ if (pFormat->size() == 1 &&
+ pFormat->GetRange().size() == 1 &&
+ pFormat->GetRange()[0].aStart == aSrcPos &&
+ HasOneSingleFullyRelativeReference( pTokens.get(), aOffsetForSingleRelRef ))
+ {
+ bSingleRelativeReference = true;
+ }
+ else
+ {
+ bEligibleForCache = false;
+ break;
+ }
+ }
+ }
+
+ if (bEligibleForCache)
+ {
+ for (auto& aCacheEntry : mrParent.maCache)
+ if (aCacheEntry.mnAge < SAL_MAX_INT64)
+ aCacheEntry.mnAge++;
+
+ for (auto& aCacheEntry : mrParent.maCache)
+ {
+ if (!aCacheEntry.mpFormat)
+ continue;
+
+ if (aCacheEntry.mpFormat->size() != pFormat->size())
+ continue;
+
+ // Check if the conditional format is identical to an existing one (but with different range) and can be shared
+ for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx)
+ {
+ auto pCacheFormatEntry = aCacheEntry.mpFormat->GetEntry(nFormatEntryIx);
+ auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx);
+ if (pCacheFormatEntry->GetType() != pFormatEntry->GetType() ||
+ pFormatEntry->GetType() != ScFormatEntry::Type::Condition)
+ break;
+
+ auto pCacheCondFormatEntry = static_cast<const ScCondFormatEntry*>(pCacheFormatEntry);
+ auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+
+ if (pCacheCondFormatEntry->GetStyle() != pCondFormatEntry->GetStyle())
+ break;
+
+ // Note That comparing the formulas of the ScConditionEntry at this stage is
+ // comparing just the *strings* of the formulas. For the bSingleRelativeReference
+ // case we compare the tokenized ("compiled") formulas.
+ if (bSingleRelativeReference)
+ {
+ if (aCacheEntry.mbSingleRelativeReference &&
+ pTokens->EqualTokens(aCacheEntry.mpTokens.get()))
+ ;
+ else
+ break;
+ }
+ else if (!pCacheCondFormatEntry->IsEqual(*pCondFormatEntry, /*bIgnoreSrcPos*/true))
+ {
+ break;
+ }
+ // If we get here on the last round through the for loop, we have a cache hit
+ if (nFormatEntryIx == pFormat->size() - 1)
+ {
+ // Mark cache entry as fresh, do necessary mangling of it and just return
+ aCacheEntry.mnAge = 0;
+ for (size_t k = 0; k < pFormat->GetRange().size(); ++k)
+ aCacheEntry.mpFormat->GetRangeList().Join(pFormat->GetRange()[k]);
+ return;
+ }
+ }
+ }
+ }
+
+ sal_uLong nIndex = pDoc->AddCondFormat(std::move(pFormat), nTab);
+ ScConditionalFormat* pInsertedFormat = pDoc->GetCondFormList(nTab)->GetFormat(nIndex);
+ assert(pInsertedFormat && pInsertedFormat->GetKey() == nIndex);
+
+ mrParent.mvCondFormatData.push_back( { pInsertedFormat, nTab } );
+
+ if (!bEligibleForCache)
+ return;
+
+ // Not found in cache, replace oldest cache entry
+ sal_Int64 nOldestAge = -1;
+ size_t nIndexOfOldest = 0;
+ for (auto& aCacheEntry : mrParent.maCache)
+ {
+ if (aCacheEntry.mnAge > nOldestAge)
+ {
+ nOldestAge = aCacheEntry.mnAge;
+ nIndexOfOldest = (&aCacheEntry - &mrParent.maCache.front());
+ }
+ }
+ mrParent.maCache[nIndexOfOldest].mpFormat = pInsertedFormat;
+ mrParent.maCache[nIndexOfOldest].mbSingleRelativeReference = bSingleRelativeReference;
+ mrParent.maCache[nIndexOfOldest].mpTokens = std::move(pTokens);
+ mrParent.maCache[nIndexOfOldest].mnAge = 0;
+}
+
+ScXMLConditionalFormatContext::~ScXMLConditionalFormatContext()
+{
+}
+
+ScXMLColorScaleFormatContext::ScXMLColorScaleFormatContext( ScXMLImport& rImport,
+ ScConditionalFormat* pFormat):
+ ScXMLImportContext( rImport ),
+ pColorScaleFormat(nullptr)
+{
+ pColorScaleFormat = new ScColorScaleFormat(GetScImport().GetDocument());
+ pFormat->AddEntry(pColorScaleFormat);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLColorScaleFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_COLOR_SCALE_ENTRY ):
+ pContext = new ScXMLColorScaleFormatEntryContext( GetScImport(), pAttribList, pColorScaleFormat );
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataBarFormatContext::ScXMLDataBarFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat):
+ ScXMLImportContext( rImport ),
+ mpFormatData(nullptr),
+ mpParent(pFormat),
+ mnIndex(0)
+{
+ OUString sPositiveColor;
+ OUString sNegativeColor;
+ OUString sGradient;
+ OUString sAxisPosition;
+ OUString sShowValue;
+ OUString sAxisColor;
+ OUString sMinLength;
+ OUString sMaxLength;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_POSITIVE_COLOR ):
+ sPositiveColor = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_GRADIENT ):
+ sGradient = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_NEGATIVE_COLOR ):
+ sNegativeColor = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_AXIS_POSITION ):
+ sAxisPosition = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_SHOW_VALUE ):
+ sShowValue = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_AXIS_COLOR ):
+ sAxisColor = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_MIN_LENGTH ):
+ sMinLength = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_MAX_LENGTH ):
+ sMaxLength = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ScDataBarFormat* pDataBarFormat = new ScDataBarFormat(rImport.GetDocument());
+ mpFormatData = new ScDataBarFormatData();
+ pDataBarFormat->SetDataBarData(mpFormatData);
+ if(!sGradient.isEmpty())
+ {
+ bool bGradient = true;
+ (void)sax::Converter::convertBool( bGradient, sGradient);
+ mpFormatData->mbGradient = bGradient;
+ }
+
+ if(!sPositiveColor.isEmpty())
+ {
+ sax::Converter::convertColor( mpFormatData->maPositiveColor, sPositiveColor );
+ }
+
+ if(!sNegativeColor.isEmpty())
+ {
+ // we might check here for 0xff0000 and don't write it
+ Color nColor;
+ sax::Converter::convertColor( nColor, sNegativeColor );
+ mpFormatData->mxNegativeColor = nColor;
+ }
+ else
+ mpFormatData->mbNeg = false;
+
+ if(!sAxisPosition.isEmpty())
+ {
+ if(sAxisPosition == "middle")
+ mpFormatData->meAxisPosition = databar::MIDDLE;
+ else if (sAxisPosition == "none")
+ mpFormatData->meAxisPosition = databar::NONE;
+ }
+
+ if(!sAxisColor.isEmpty())
+ {
+ sax::Converter::convertColor( mpFormatData->maAxisColor, sAxisColor );
+ }
+
+ if(!sShowValue.isEmpty())
+ {
+ bool bShowValue = true;
+ (void)sax::Converter::convertBool( bShowValue, sShowValue );
+ mpFormatData->mbOnlyBar = !bShowValue;
+ }
+
+ if (!sMinLength.isEmpty())
+ {
+ double nVal = sMinLength.toDouble();
+ mpFormatData->mnMinLength = nVal;
+ }
+
+ if (!sMaxLength.isEmpty())
+ {
+ double nVal = sMaxLength.toDouble();
+ if (nVal == 0.0)
+ nVal = 100.0;
+ mpFormatData->mnMaxLength = nVal;
+ }
+
+ pFormat->AddEntry(pDataBarFormat);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLDataBarFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_FORMATTING_ENTRY ):
+ case XML_ELEMENT( CALC_EXT, XML_DATA_BAR_ENTRY ):
+ {
+ ScColorScaleEntry* pEntry(nullptr);
+ pContext = new ScXMLFormattingEntryContext( GetScImport(), pAttribList, pEntry );
+ pEntry->SetRepaintCallback(mpParent);
+ if(mnIndex == 0)
+ {
+ mpFormatData->mpLowerLimit.reset(pEntry);
+ }
+ else if (mnIndex == 1)
+ {
+ mpFormatData->mpUpperLimit.reset(pEntry);
+ }
+ else
+ {
+ // data bars only support 2 entries
+ assert(false);
+ }
+ ++mnIndex;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLIconSetFormatContext::ScXMLIconSetFormatContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat):
+ ScXMLImportContext( rImport ),
+ mpParent(pFormat)
+{
+ OUString aIconSetType, sShowValue;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_ICON_SET_TYPE ):
+ aIconSetType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_SHOW_VALUE ):
+ sShowValue = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ const ScIconSetMap* pMap = ScIconSetFormat::g_IconSetMap;
+ ScIconSetType eType = IconSet_3Arrows;
+ for(; pMap->pName; ++pMap)
+ {
+ OUString aName = OUString::createFromAscii(pMap->pName);
+ if(aName ==aIconSetType)
+ {
+ eType = pMap->eType;
+ break;
+ }
+ }
+
+ ScIconSetFormat* pIconSetFormat = new ScIconSetFormat(GetScImport().GetDocument());
+ ScIconSetFormatData* pIconSetFormatData = new ScIconSetFormatData;
+
+ if(!sShowValue.isEmpty())
+ {
+ bool bShowValue = true;
+ (void)sax::Converter::convertBool( bShowValue, sShowValue );
+ pIconSetFormatData->mbShowValue = !bShowValue;
+ }
+
+ pIconSetFormatData->eIconSetType = eType;
+ pIconSetFormat->SetIconSetData(pIconSetFormatData);
+ pFormat->AddEntry(pIconSetFormat);
+
+ mpFormatData = pIconSetFormatData;
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLIconSetFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_FORMATTING_ENTRY ):
+ {
+ ScColorScaleEntry* pEntry(nullptr);
+ pContext = new ScXMLFormattingEntryContext( GetScImport(), pAttribList, pEntry );
+ mpFormatData->m_Entries.emplace_back(pEntry);
+ pEntry->SetRepaintCallback(mpParent);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+namespace {
+
+void GetConditionData(const OUString& rValue, ScConditionMode& eMode, OUString& rExpr1, OUString& rExpr2)
+{
+ if(rValue.startsWith("unique"))
+ {
+ eMode = ScConditionMode::NotDuplicate;
+ }
+ else if(rValue.startsWith("duplicate"))
+ {
+ eMode = ScConditionMode::Duplicate;
+ }
+ else if(rValue.startsWith("between"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 8;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ',');
+ rExpr2 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Between;
+ }
+ else if(rValue.startsWith("not-between"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 12;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ',');
+ rExpr2 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::NotBetween;
+ }
+ else if(rValue.startsWith("<="))
+ {
+ rExpr1 = rValue.copy(2);
+ eMode = ScConditionMode::EqLess;
+ }
+ else if(rValue.startsWith(">="))
+ {
+ rExpr1 = rValue.copy(2);
+ eMode = ScConditionMode::EqGreater;
+ }
+ else if(rValue.startsWith("!="))
+ {
+ rExpr1 = rValue.copy(2);
+ eMode = ScConditionMode::NotEqual;
+ }
+ else if(rValue.startsWith("<"))
+ {
+ rExpr1 = rValue.copy(1);
+ eMode = ScConditionMode::Less;
+ }
+ else if(rValue.startsWith("="))
+ {
+ rExpr1 = rValue.copy(1);
+ eMode = ScConditionMode::Equal;
+ }
+ else if(rValue.startsWith(">"))
+ {
+ rExpr1 = rValue.copy(1);
+ eMode = ScConditionMode::Greater;
+ }
+ else if(rValue.startsWith("formula-is"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 11;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Direct;
+ }
+ else if(rValue.startsWith("top-elements"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 13;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Top10;
+ }
+ else if(rValue.startsWith("bottom-elements"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 16;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Bottom10;
+ }
+ else if(rValue.startsWith("top-percent"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 12;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::TopPercent;
+ }
+ else if(rValue.startsWith("bottom-percent"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 15;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::BottomPercent;
+ }
+ else if(rValue.startsWith("above-average"))
+ {
+ eMode = ScConditionMode::AboveAverage;
+ }
+ else if(rValue.startsWith("below-average"))
+ {
+ eMode = ScConditionMode::BelowAverage;
+ }
+ else if(rValue.startsWith("above-equal-average"))
+ {
+ eMode = ScConditionMode::AboveEqualAverage;
+ }
+ else if(rValue.startsWith("below-equal-average"))
+ {
+ eMode = ScConditionMode::BelowEqualAverage;
+ }
+ else if(rValue.startsWith("is-error"))
+ {
+ eMode = ScConditionMode::Error;
+ }
+ else if(rValue.startsWith("is-no-error"))
+ {
+ eMode = ScConditionMode::NoError;
+ }
+ else if(rValue.startsWith("begins-with"))
+ {
+ eMode = ScConditionMode::BeginsWith;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 12;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else if(rValue.startsWith("ends-with"))
+ {
+ eMode = ScConditionMode::EndsWith;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 10;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else if(rValue.startsWith("contains-text"))
+ {
+ eMode = ScConditionMode::ContainsText;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 14;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else if(rValue.startsWith("not-contains-text"))
+ {
+ eMode = ScConditionMode::NotContainsText;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 18;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else
+ eMode = ScConditionMode::NONE;
+}
+
+}
+
+ScXMLCondContext::ScXMLCondContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat ):
+ ScXMLImportContext( rImport )
+{
+ OUString sExpression;
+ OUString sStyle;
+ OUString sAddress;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_VALUE ):
+ sExpression = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_APPLY_STYLE_NAME ):
+ sStyle = ScStyleNameConversion::ProgrammaticToDisplayName(aIter.toString(), SfxStyleFamily::Para );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_BASE_CELL_ADDRESS ):
+ sAddress = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ OUString aExpr1;
+ OUString aExpr2;
+ ScConditionMode eMode;
+ GetConditionData(sExpression, eMode, aExpr1, aExpr2);
+
+ ScCondFormatEntry* pFormatEntry = new ScCondFormatEntry(eMode, aExpr1, aExpr2, *GetScImport().GetDocument(), ScAddress(), sStyle,
+ OUString(), OUString(), formula::FormulaGrammar::GRAM_ODFF, formula::FormulaGrammar::GRAM_ODFF);
+ pFormatEntry->SetSrcString(sAddress);
+
+ pFormat->AddEntry(pFormatEntry);
+}
+
+namespace {
+
+void setColorEntryType(std::u16string_view rType, ScColorScaleEntry* pEntry, const OUString& rFormula,
+ ScXMLImport& rImport)
+{
+ if(rType == u"minimum")
+ pEntry->SetType(COLORSCALE_MIN);
+ else if(rType == u"maximum")
+ pEntry->SetType(COLORSCALE_MAX);
+ else if(rType == u"percentile")
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ else if(rType == u"percent")
+ pEntry->SetType(COLORSCALE_PERCENT);
+ else if(rType == u"formula")
+ {
+ pEntry->SetType(COLORSCALE_FORMULA);
+ //position does not matter, only table is important
+ pEntry->SetFormula(rFormula, *rImport.GetDocument(), ScAddress(0,0,rImport.GetTables().GetCurrentSheet()), formula::FormulaGrammar::GRAM_ODFF);
+ }
+ else if(rType == u"auto-minimum")
+ pEntry->SetType(COLORSCALE_AUTO);
+ else if(rType == u"auto-maximum")
+ pEntry->SetType(COLORSCALE_AUTO);
+}
+
+}
+
+ScXMLColorScaleFormatEntryContext::ScXMLColorScaleFormatEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleFormat* pFormat):
+ ScXMLImportContext( rImport )
+{
+ double nVal = 0;
+ Color aColor;
+
+ OUString sType;
+ OUString sVal;
+ OUString sColor;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_TYPE ):
+ sType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE ):
+ sVal = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_COLOR ):
+ sColor = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ sax::Converter::convertColor(aColor, sColor);
+
+ if(!sVal.isEmpty())
+ sax::Converter::convertDouble(nVal, sVal);
+
+ auto pFormatEntry = new ScColorScaleEntry(nVal, aColor);
+ setColorEntryType(sType, pFormatEntry, sVal, GetScImport());
+ pFormat->AddEntry(pFormatEntry);
+}
+
+ScXMLFormattingEntryContext::ScXMLFormattingEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleEntry*& pColorScaleEntry):
+ ScXMLImportContext( rImport )
+{
+ OUString sVal;
+ OUString sType;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_TYPE ):
+ sType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE ):
+ sVal = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ double nVal = 0;
+ if(!sVal.isEmpty())
+ sax::Converter::convertDouble(nVal, sVal);
+
+ pColorScaleEntry = new ScColorScaleEntry(nVal, Color());
+ setColorEntryType(sType, pColorScaleEntry, sVal, GetScImport());
+}
+
+namespace {
+
+condformat::ScCondFormatDateType getDateFromString(const OUString& rString)
+{
+ if(rString == "today")
+ return condformat::TODAY;
+ else if(rString == "yesterday")
+ return condformat::YESTERDAY;
+ else if(rString == "tomorrow")
+ return condformat::TOMORROW;
+ else if(rString == "last-7-days")
+ return condformat::LAST7DAYS;
+ else if(rString == "this-week")
+ return condformat::THISWEEK;
+ else if(rString == "last-week")
+ return condformat::LASTWEEK;
+ else if(rString == "next-week")
+ return condformat::NEXTWEEK;
+ else if(rString == "this-month")
+ return condformat::THISMONTH;
+ else if(rString == "last-month")
+ return condformat::LASTMONTH;
+ else if(rString == "next-month")
+ return condformat::NEXTMONTH;
+ else if(rString == "this-year")
+ return condformat::THISYEAR;
+ else if(rString == "last-year")
+ return condformat::LASTYEAR;
+ else if(rString == "next-year")
+ return condformat::NEXTYEAR;
+
+ SAL_WARN("sc", "unknown date type: " << rString);
+ return condformat::TODAY;
+}
+
+}
+
+ScXMLDateContext::ScXMLDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat ):
+ ScXMLImportContext( rImport )
+{
+ OUString sDateType, sStyle;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_DATE ):
+ sDateType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_STYLE ):
+ sStyle = ScStyleNameConversion::ProgrammaticToDisplayName(aIter.toString(), SfxStyleFamily::Para );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ScCondDateFormatEntry* pFormatEntry = new ScCondDateFormatEntry(GetScImport().GetDocument());
+ pFormatEntry->SetStyleName(sStyle);
+ pFormatEntry->SetDateType(getDateFromString(sDateType));
+ pFormat->AddEntry(pFormatEntry);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcondformat.hxx b/sc/source/filter/xml/xmlcondformat.hxx
new file mode 100644
index 0000000000..eaaf7440aa
--- /dev/null
+++ b/sc/source/filter/xml/xmlcondformat.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/.
+ */
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <tools/link.hxx>
+#include "importcontext.hxx"
+#include <tokenarray.hxx>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScColorScaleFormat;
+class ScColorScaleEntry;
+struct ScDataBarFormatData;
+class ScConditionalFormat;
+struct ScIconSetFormatData;
+
+class ScXMLConditionalFormatsContext : public ScXMLImportContext
+{
+private:
+ struct CacheEntry
+ {
+ ScConditionalFormat* mpFormat = nullptr;
+ bool mbSingleRelativeReference;
+ std::unique_ptr<const ScTokenArray> mpTokens;
+ sal_Int64 mnAge = SAL_MAX_INT64;
+ };
+
+ struct CondFormatData
+ {
+ ScConditionalFormat* mpFormat;
+ SCTAB mnTab;
+ };
+
+ DECL_LINK(FormatDeletedHdl, ScConditionalFormat*, void);
+
+public:
+ ScXMLConditionalFormatsContext( ScXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ std::array<CacheEntry, 4> maCache;
+
+ std::vector<CondFormatData> mvCondFormatData;
+};
+
+class ScXMLConditionalFormatContext : public ScXMLImportContext
+{
+public:
+ ScXMLConditionalFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLConditionalFormatsContext& rParent );
+
+ virtual ~ScXMLConditionalFormatContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+private:
+
+ std::unique_ptr<ScConditionalFormat> mxFormat;
+
+ ScXMLConditionalFormatsContext& mrParent;
+};
+
+class ScXMLColorScaleFormatContext : public ScXMLImportContext
+{
+public:
+ ScXMLColorScaleFormatContext( ScXMLImport& rImport,
+ ScConditionalFormat* pFormat);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+private:
+
+ ScColorScaleFormat* pColorScaleFormat;
+};
+
+class ScXMLDataBarFormatContext : public ScXMLImportContext
+{
+public:
+ ScXMLDataBarFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+private:
+
+ ScDataBarFormatData* mpFormatData;
+ ScConditionalFormat* mpParent;
+
+ sal_Int32 mnIndex;
+};
+
+class ScXMLIconSetFormatContext : public ScXMLImportContext
+{
+ ScIconSetFormatData* mpFormatData;
+ ScConditionalFormat* mpParent;
+public:
+
+ ScXMLIconSetFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLColorScaleFormatEntryContext : public ScXMLImportContext
+{
+public:
+ ScXMLColorScaleFormatEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleFormat* pFormat);
+};
+
+class ScXMLFormattingEntryContext : public ScXMLImportContext
+{
+public:
+ ScXMLFormattingEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleEntry*& pData);
+};
+
+class ScXMLCondContext : public ScXMLImportContext
+{
+public:
+ ScXMLCondContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+};
+
+class ScXMLDateContext : public ScXMLImportContext
+{
+public:
+ ScXMLDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlconti.cxx b/sc/source/filter/xml/xmlconti.cxx
new file mode 100644
index 0000000000..18adc82ffd
--- /dev/null
+++ b/sc/source/filter/xml/xmlconti.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 "xmlconti.hxx"
+#include "xmlimprt.hxx"
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace xmloff::token;
+
+ScXMLContentContext::ScXMLContentContext( ScXMLImport& rImport,
+ OUStringBuffer& sTempValue) :
+ ScXMLImportContext( rImport ),
+ sValue(sTempValue)
+{
+}
+
+ScXMLContentContext::~ScXMLContentContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLContentContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ if (nElement == XML_ELEMENT(TEXT, XML_S))
+ {
+ sal_Int32 nRepeat(0);
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ if (aIter.getToken() == XML_ELEMENT(TEXT, XML_C))
+ nRepeat = aIter.toInt32();
+ }
+ if (nRepeat)
+ for (sal_Int32 j = 0; j < nRepeat; ++j)
+ sValue.append(' ');
+ else
+ sValue.append(' ');
+ }
+
+ return new SvXMLImportContext( GetImport() );
+}
+
+void ScXMLContentContext::characters( const OUString& rChars )
+{
+ sValue.append(rChars);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlconti.hxx b/sc/source/filter/xml/xmlconti.hxx
new file mode 100644
index 0000000000..3324bf2010
--- /dev/null
+++ b/sc/source/filter/xml/xmlconti.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <rtl/ustrbuf.hxx>
+#include "importcontext.hxx"
+
+class ScXMLContentContext : public ScXMLImportContext
+{
+ OUStringBuffer& sValue;
+
+public:
+
+ ScXMLContentContext( ScXMLImport& rImport,
+ OUStringBuffer& sValue);
+
+ virtual ~ScXMLContentContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcvali.cxx b/sc/source/filter/xml/xmlcvali.cxx
new file mode 100644
index 0000000000..20b1f235fd
--- /dev/null
+++ b/sc/source/filter/xml/xmlcvali.cxx
@@ -0,0 +1,555 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlcvali.hxx"
+#include "xmlimprt.hxx"
+#include "xmlconti.hxx"
+#include <document.hxx>
+#include "XMLConverter.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/XMLEventsImportContext.hxx>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+using namespace ::formula;
+
+namespace {
+
+class ScXMLContentValidationContext : public ScXMLImportContext
+{
+ OUString sName;
+ OUString sHelpTitle;
+ OUString sHelpMessage;
+ OUString sErrorTitle;
+ OUString sErrorMessage;
+ OUString sErrorMessageType;
+ OUString sBaseCellAddress;
+ OUString sCondition;
+ sal_Int16 nShowList;
+ bool bAllowEmptyCell;
+ bool bDisplayHelp;
+ bool bDisplayError;
+
+ rtl::Reference<XMLEventsImportContext> xEventContext;
+
+ css::sheet::ValidationAlertStyle GetAlertStyle() const;
+ void SetFormula( OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
+ const OUString& rCondition, const OUString& rGlobNmsp, FormulaGrammar::Grammar eGlobGrammar, bool bHasNmsp ) const;
+ void GetCondition( ScMyImportValidation& rValidation ) const;
+
+public:
+
+ ScXMLContentValidationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetHelpMessage(const OUString& sTitle, const OUString& sMessage, const bool bDisplay);
+ void SetErrorMessage(const OUString& sTitle, const OUString& sMessage, const OUString& sMessageType, const bool bDisplay);
+ void SetErrorMacro(const bool bExecute);
+};
+
+class ScXMLHelpMessageContext : public ScXMLImportContext
+{
+ OUString sTitle;
+ OUStringBuffer sMessage;
+ sal_Int32 nParagraphCount;
+ bool bDisplay;
+
+ ScXMLContentValidationContext* pValidationContext;
+
+public:
+
+ ScXMLHelpMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pValidationContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLErrorMessageContext : public ScXMLImportContext
+{
+ OUString sTitle;
+ OUStringBuffer sMessage;
+ OUString sMessageType;
+ sal_Int32 nParagraphCount;
+ bool bDisplay;
+
+ ScXMLContentValidationContext* pValidationContext;
+
+public:
+
+ ScXMLErrorMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pValidationContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLErrorMacroContext : public ScXMLImportContext
+{
+ bool bExecute;
+ ScXMLContentValidationContext* pValidationContext;
+
+public:
+
+ ScXMLErrorMacroContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pValidationContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+}
+
+ScXMLContentValidationsContext::ScXMLContentValidationsContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // here are no attributes
+}
+
+ScXMLContentValidationsContext::~ScXMLContentValidationsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentValidationsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION ):
+ pContext = new ScXMLContentValidationContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLContentValidationContext::ScXMLContentValidationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nShowList(sheet::TableValidationVisibility::UNSORTED),
+ bAllowEmptyCell(true),
+ bDisplayHelp(false),
+ bDisplayError(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ sName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION ):
+ sCondition = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
+ sBaseCellAddress = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_ALLOW_EMPTY_CELL ):
+ if (IsXMLToken(aIter, XML_FALSE))
+ bAllowEmptyCell = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_LIST ):
+ if (IsXMLToken(aIter, XML_NO))
+ {
+ nShowList = sheet::TableValidationVisibility::INVISIBLE;
+ }
+ else if (IsXMLToken(aIter, XML_UNSORTED))
+ {
+ nShowList = sheet::TableValidationVisibility::UNSORTED;
+ }
+ else if (IsXMLToken(aIter, XML_SORT_ASCENDING))
+ {
+ nShowList = sheet::TableValidationVisibility::SORTEDASCENDING;
+ }
+ else if (IsXMLToken(aIter, XML_SORTED_ASCENDING))
+ {
+ // Read old wrong value, fdo#72548
+ nShowList = sheet::TableValidationVisibility::SORTEDASCENDING;
+ }
+ break;
+ }
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentValidationContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_HELP_MESSAGE ):
+ pContext = new ScXMLHelpMessageContext( GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_ERROR_MESSAGE ):
+ pContext = new ScXMLErrorMessageContext( GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_ERROR_MACRO ):
+ pContext = new ScXMLErrorMacroContext( GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS):
+ xEventContext = new XMLEventsImportContext( GetImport() );
+ pContext = xEventContext.get();
+ }
+
+ return pContext;
+}
+
+sheet::ValidationAlertStyle ScXMLContentValidationContext::GetAlertStyle() const
+{
+ if (IsXMLToken(sErrorMessageType, XML_MACRO))
+ return sheet::ValidationAlertStyle_MACRO;
+ if (IsXMLToken(sErrorMessageType, XML_STOP))
+ return sheet::ValidationAlertStyle_STOP;
+ if (IsXMLToken(sErrorMessageType, XML_WARNING))
+ return sheet::ValidationAlertStyle_WARNING;
+ if (IsXMLToken(sErrorMessageType, XML_INFORMATION))
+ return sheet::ValidationAlertStyle_INFO;
+ // default for unknown
+ return sheet::ValidationAlertStyle_STOP;
+}
+
+void ScXMLContentValidationContext::SetFormula( OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
+ const OUString& rCondition, const OUString& rGlobNmsp, FormulaGrammar::Grammar eGlobGrammar, bool bHasNmsp ) const
+{
+ reGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ if( bHasNmsp )
+ {
+ // the entire attribute contains a namespace: internal namespace not allowed
+ rFormula = rCondition;
+ rFormulaNmsp = rGlobNmsp;
+ reGrammar = eGlobGrammar;
+ }
+ else
+ {
+ // the attribute does not contain a namespace: try to find a namespace of an external grammar
+ GetScImport().ExtractFormulaNamespaceGrammar( rFormula, rFormulaNmsp, reGrammar, rCondition, true );
+ if( reGrammar != FormulaGrammar::GRAM_EXTERNAL )
+ reGrammar = eGlobGrammar;
+ }
+}
+
+void ScXMLContentValidationContext::GetCondition( ScMyImportValidation& rValidation ) const
+{
+ rValidation.aValidationType = sheet::ValidationType_ANY; // default if no condition is given
+ rValidation.aOperator = sheet::ConditionOperator_NONE;
+
+ if( sCondition.isEmpty() )
+ return;
+
+ // extract leading namespace from condition string
+ OUString aCondition, aConditionNmsp;
+ FormulaGrammar::Grammar eGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eGrammar, sCondition );
+ bool bHasNmsp = aCondition.getLength() < sCondition.getLength();
+
+ // parse a condition from the attribute string
+ ScXMLConditionParseResult aParseResult;
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, 0 );
+
+ /* Check the result. A valid value in aParseResult.meToken implies
+ that the other members of aParseResult are filled with valid data
+ for that token. */
+ bool bSecondaryPart = false;
+ switch( aParseResult.meToken )
+ {
+ case XML_COND_TEXTLENGTH: // condition is 'cell-content-text-length()<operator><expression>'
+ case XML_COND_TEXTLENGTH_ISBETWEEN: // condition is 'cell-content-text-length-is-between(<expression1>,<expression2>)'
+ case XML_COND_TEXTLENGTH_ISNOTBETWEEN: // condition is 'cell-content-text-length-is-not-between(<expression1>,<expression2>)'
+ case XML_COND_ISINLIST: // condition is 'cell-content-is-in-list(<expression>)'
+ case XML_COND_ISTRUEFORMULA: // condition is 'is-true-formula(<expression>)'
+ rValidation.aValidationType = aParseResult.meValidation;
+ rValidation.aOperator = aParseResult.meOperator;
+ break;
+
+ case XML_COND_ISWHOLENUMBER: // condition is 'cell-content-is-whole-number() and <condition>'
+ case XML_COND_ISDECIMALNUMBER: // condition is 'cell-content-is-decimal-number() and <condition>'
+ case XML_COND_ISDATE: // condition is 'cell-content-is-date() and <condition>'
+ case XML_COND_ISTIME: // condition is 'cell-content-is-time() and <condition>'
+ rValidation.aValidationType = aParseResult.meValidation;
+ bSecondaryPart = true;
+ break;
+
+ default:; // unacceptable or unknown condition
+ }
+
+ /* Parse the following 'and <condition>' part of some conditions. This
+ updates the members of aParseResult that will contain the operands
+ and comparison operator then. */
+ if( bSecondaryPart )
+ {
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, aParseResult.mnEndIndex );
+ if( aParseResult.meToken == XML_COND_AND )
+ {
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, aParseResult.mnEndIndex );
+ switch( aParseResult.meToken )
+ {
+ case XML_COND_CELLCONTENT: // condition is 'and cell-content()<operator><expression>'
+ case XML_COND_ISBETWEEN: // condition is 'and cell-content-is-between(<expression1>,<expression2>)'
+ case XML_COND_ISNOTBETWEEN: // condition is 'and cell-content-is-not-between(<expression1>,<expression2>)'
+ rValidation.aOperator = aParseResult.meOperator;
+ break;
+ default:; // unacceptable or unknown condition
+ }
+ }
+ }
+
+ // a validation type (date, integer) without a condition isn't possible
+ if( rValidation.aOperator == sheet::ConditionOperator_NONE )
+ rValidation.aValidationType = sheet::ValidationType_ANY;
+
+ // parse the formulas
+ if( rValidation.aValidationType != sheet::ValidationType_ANY )
+ {
+ SetFormula( rValidation.sFormula1, rValidation.sFormulaNmsp1, rValidation.eGrammar1,
+ aParseResult.maOperand1, aConditionNmsp, eGrammar, bHasNmsp );
+ SetFormula( rValidation.sFormula2, rValidation.sFormulaNmsp2, rValidation.eGrammar2,
+ aParseResult.maOperand2, aConditionNmsp, eGrammar, bHasNmsp );
+ }
+}
+
+void SAL_CALL ScXMLContentValidationContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ // #i36650# event-listeners element moved up one level
+ if (xEventContext.is())
+ {
+ uno::Sequence<beans::PropertyValue> aValues;
+ xEventContext->GetEventSequence( "OnError", aValues );
+
+ auto pValue = std::find_if(std::cbegin(aValues), std::cend(aValues),
+ [](const beans::PropertyValue& rValue) {
+ return rValue.Name == "MacroName" || rValue.Name == "Script"; });
+ if (pValue != std::cend(aValues))
+ pValue->Value >>= sErrorTitle;
+ }
+
+ ScMyImportValidation aValidation;
+ aValidation.eGrammar1 = aValidation.eGrammar2 = GetScImport().GetDocument()->GetStorageGrammar();
+ aValidation.sName = sName;
+ aValidation.sBaseCellAddress = sBaseCellAddress;
+ aValidation.sInputTitle = sHelpTitle;
+ aValidation.sInputMessage = sHelpMessage;
+ aValidation.sErrorTitle = sErrorTitle;
+ aValidation.sErrorMessage = sErrorMessage;
+ GetCondition( aValidation );
+ aValidation.aAlertStyle = GetAlertStyle();
+ aValidation.bShowErrorMessage = bDisplayError;
+ aValidation.bShowInputMessage = bDisplayHelp;
+ aValidation.bIgnoreBlanks = bAllowEmptyCell;
+ aValidation.nShowList = nShowList;
+ GetScImport().AddValidation(aValidation);
+}
+
+void ScXMLContentValidationContext::SetHelpMessage(const OUString& sTitle, const OUString& sMessage, const bool bDisplay)
+{
+ sHelpTitle = sTitle;
+ sHelpMessage = sMessage;
+ bDisplayHelp = bDisplay;
+}
+
+void ScXMLContentValidationContext::SetErrorMessage(const OUString& sTitle, const OUString& sMessage,
+ const OUString& sMessageType, const bool bDisplay)
+{
+ sErrorTitle = sTitle;
+ sErrorMessage = sMessage;
+ sErrorMessageType = sMessageType;
+ bDisplayError = bDisplay;
+}
+
+void ScXMLContentValidationContext::SetErrorMacro(const bool bExecute)
+{
+ sErrorMessageType = "macro";
+ bDisplayError = bExecute;
+}
+
+ScXMLHelpMessageContext::ScXMLHelpMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pTempValidationContext) :
+ ScXMLImportContext( rImport ),
+ nParagraphCount(0),
+ bDisplay(false)
+{
+ pValidationContext = pTempValidationContext;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TITLE ):
+ sTitle = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLHelpMessageContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT(TEXT, XML_P):
+ {
+ if(nParagraphCount)
+ sMessage.append('\n');
+ ++nParagraphCount;
+ pContext = new ScXMLContentContext( GetScImport(), sMessage );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLHelpMessageContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pValidationContext->SetHelpMessage(sTitle, sMessage.makeStringAndClear(), bDisplay);
+}
+
+ScXMLErrorMessageContext::ScXMLErrorMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pTempValidationContext) :
+ ScXMLImportContext( rImport ),
+ nParagraphCount(0),
+ bDisplay(false)
+{
+ pValidationContext = pTempValidationContext;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TITLE ):
+ sTitle = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_MESSAGE_TYPE ):
+ sMessageType = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLErrorMessageContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT(TEXT, XML_P):
+ {
+ if(nParagraphCount)
+ sMessage.append('\n');
+ ++nParagraphCount;
+ pContext = new ScXMLContentContext( GetScImport(), sMessage);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLErrorMessageContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pValidationContext->SetErrorMessage(sTitle, sMessage.makeStringAndClear(), sMessageType, bDisplay);
+}
+
+ScXMLErrorMacroContext::ScXMLErrorMacroContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pTempValidationContext) :
+ ScXMLImportContext( rImport ),
+ bExecute(false)
+{
+ pValidationContext = pTempValidationContext;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ break;
+ case XML_ELEMENT( TABLE, XML_EXECUTE ):
+ bExecute = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLErrorMacroContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if (nElement == XML_ELEMENT(SCRIPT, XML_EVENTS))
+ {
+ pContext = new XMLEventsImportContext(GetImport());
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLErrorMacroContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pValidationContext->SetErrorMacro( bExecute );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcvali.hxx b/sc/source/filter/xml/xmlcvali.hxx
new file mode 100644
index 0000000000..b3fee13be1
--- /dev/null
+++ b/sc/source/filter/xml/xmlcvali.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLContentValidationsContext : public ScXMLImportContext
+{
+public:
+ ScXMLContentValidationsContext(ScXMLImport& rImport);
+
+ virtual ~ScXMLContentValidationsContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldpimp.cxx b/sc/source/filter/xml/xmldpimp.cxx
new file mode 100644
index 0000000000..3d97756b7e
--- /dev/null
+++ b/sc/source/filter/xml/xmldpimp.cxx
@@ -0,0 +1,1562 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmldpimp.hxx"
+#include "xmlimprt.hxx"
+#include "xmlfilti.hxx"
+#include <document.hxx>
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <attrib.hxx>
+#include "XMLConverter.hxx"
+#include <dpdimsave.hxx>
+#include <rangeutl.hxx>
+#include <dpoutputgeometry.hxx>
+#include <generalfunction.hxx>
+
+#include "pivotsource.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+using ::com::sun::star::uno::Reference;
+
+ScXMLDataPilotTablesContext::ScXMLDataPilotTablesContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // has no Attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLDataPilotTablesContext::~ScXMLDataPilotTablesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotTablesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_TABLE ) :
+ {
+ pContext = new ScXMLDataPilotTableContext( GetScImport(), pAttribList );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotTableContext::GrandTotalItem::GrandTotalItem() :
+ mbVisible(true) {}
+
+ScXMLDataPilotTableContext::ScXMLDataPilotTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ pDoc(GetScImport().GetDocument()),
+ pDPSave(new ScDPSaveData()),
+ nSourceType(SQL),
+ mnRowFieldCount(0),
+ mnColFieldCount(0),
+ mnPageFieldCount(0),
+ mnDataFieldCount(0),
+ mnDataLayoutType(sheet::DataPilotFieldOrientation_HIDDEN),
+ bIsNative(true),
+ bIgnoreEmptyRows(false),
+ bIdentifyCategories(false),
+ bTargetRangeAddress(false),
+ bSourceCellRange(false),
+ bShowFilter(true),
+ bDrillDown(true),
+ bShowExpandCollapse(false),
+ bHeaderGridLayout(false),
+ bHasCompactField(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ {
+ sDataPilotTableName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_APPLICATION_DATA ):
+ {
+ sApplicationData = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_GRAND_TOTAL ):
+ {
+ if (IsXMLToken(aIter, XML_BOTH))
+ {
+ maRowGrandTotal.mbVisible = true;
+ maColGrandTotal.mbVisible = true;
+ }
+ else if (IsXMLToken(aIter, XML_ROW))
+ {
+ maRowGrandTotal.mbVisible = true;
+ maColGrandTotal.mbVisible = false;
+ }
+ else if (IsXMLToken(aIter, XML_COLUMN))
+ {
+ maRowGrandTotal.mbVisible = false;
+ maColGrandTotal.mbVisible = true;
+ }
+ else
+ {
+ maRowGrandTotal.mbVisible = false;
+ maColGrandTotal.mbVisible = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_IGNORE_EMPTY_ROWS ):
+ {
+ bIgnoreEmptyRows = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_IDENTIFY_CATEGORIES ):
+ {
+ bIdentifyCategories = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ assert(pDoc);
+ bTargetRangeAddress = ScRangeStringConverter::GetRangeFromString( aTargetRangeAddress, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_BUTTONS ):
+ {
+ sButtons = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SHOW_FILTER_BUTTON ):
+ {
+ bShowFilter = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DRILL_DOWN_ON_DOUBLE_CLICK ):
+ {
+ bDrillDown = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( LO_EXT, XML_SHOW_DRILL_DOWN_BUTTONS ):
+ {
+ bShowExpandCollapse = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_HEADER_GRID_LAYOUT ):
+ {
+ bHeaderGridLayout = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotTableContext::~ScXMLDataPilotTableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotTableContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_SQL ):
+ {
+ pContext = new ScXMLDPSourceSQLContext(GetScImport(), pAttribList, this);
+ nSourceType = SQL;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_TABLE ):
+ {
+ pContext = new ScXMLDPSourceTableContext(GetScImport(), pAttribList, this);
+ nSourceType = TABLE;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_QUERY ):
+ {
+ pContext = new ScXMLDPSourceQueryContext(GetScImport(), pAttribList, this);
+ nSourceType = QUERY;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_SERVICE ):
+ {
+ pContext = new ScXMLSourceServiceContext(GetScImport(), pAttribList, this);
+ nSourceType = SERVICE;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_GRAND_TOTAL ):
+ case XML_ELEMENT( TABLE_EXT, XML_DATA_PILOT_GRAND_TOTAL ):
+ {
+ pContext = new ScXMLDataPilotGrandTotalContext(GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_CELL_RANGE ):
+ {
+ pContext = new ScXMLSourceCellRangeContext(GetScImport(), pAttribList, this);
+ nSourceType = CELLRANGE;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_FIELD ):
+ pContext = new ScXMLDataPilotFieldContext(GetScImport(), pAttribList, this);
+ break;
+ }
+
+ return pContext;
+}
+
+namespace {
+
+const ScDPSaveDimension* getDimension(
+ const std::vector<const ScDPSaveDimension*>& rRowDims,
+ const std::vector<const ScDPSaveDimension*>& rColDims,
+ const std::vector<const ScDPSaveDimension*>& rPageDims,
+ ScDPOutputGeometry::FieldType eType, size_t nPos)
+{
+ switch (eType)
+ {
+ case ScDPOutputGeometry::Column:
+ {
+ if (rColDims.size() <= nPos)
+ return nullptr;
+
+ return rColDims[nPos];
+ }
+ case ScDPOutputGeometry::Row:
+ {
+ if (rRowDims.size() <= nPos)
+ return nullptr;
+
+ return rRowDims[nPos];
+ }
+ case ScDPOutputGeometry::Page:
+ {
+ if (rPageDims.size() <= nPos)
+ return nullptr;
+
+ return rPageDims[nPos];
+ }
+ case ScDPOutputGeometry::Data:
+ break;
+ case ScDPOutputGeometry::None:
+ break;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+ScDPOutputGeometry::FieldType toFieldType(sheet::DataPilotFieldOrientation nOrient)
+{
+ switch (nOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ return ScDPOutputGeometry::Column;
+ case sheet::DataPilotFieldOrientation_DATA:
+ return ScDPOutputGeometry::Data;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ return ScDPOutputGeometry::Page;
+ case sheet::DataPilotFieldOrientation_ROW:
+ return ScDPOutputGeometry::Row;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ break;
+ default:
+ break;
+ }
+ return ScDPOutputGeometry::None;
+}
+
+}
+
+void ScXMLDataPilotTableContext::SetButtons(ScDPObject* pDPObject)
+{
+ ScDPOutputGeometry aGeometry(aTargetRangeAddress, bShowFilter);
+ aGeometry.setColumnFieldCount(mnColFieldCount);
+ aGeometry.setRowFieldCount(mnRowFieldCount);
+ aGeometry.setPageFieldCount(mnPageFieldCount);
+ aGeometry.setDataFieldCount(mnDataFieldCount);
+ aGeometry.setDataLayoutType(toFieldType(mnDataLayoutType));
+ aGeometry.setHeaderLayout(bHeaderGridLayout);
+
+ std::vector<const ScDPSaveDimension*> aRowDims, aColDims, aPageDims;
+ pDPSave->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
+ pDPSave->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
+ pDPSave->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_PAGE, aPageDims);
+
+ OUString sAddress;
+ sal_Int32 nOffset = 0;
+ while( nOffset >= 0 )
+ {
+ ScRangeStringConverter::GetTokenByOffset( sAddress, sButtons, nOffset );
+ if( nOffset >= 0 )
+ {
+ ScAddress aScAddress;
+ sal_Int32 nAddrOffset(0);
+ if (pDoc && ScRangeStringConverter::GetAddressFromString( aScAddress, sAddress, *pDoc, ::formula::FormulaGrammar::CONV_OOO, nAddrOffset ))
+ {
+ std::pair<ScDPOutputGeometry::FieldType, size_t> aBtnType = aGeometry.getFieldButtonType(aScAddress);
+ const ScDPSaveDimension* pDim = getDimension(
+ aRowDims, aColDims, aPageDims, aBtnType.first, aBtnType.second);
+
+ bool bDimension = pDim != nullptr;
+ bool bDataLayout = pDim && pDim->IsDataLayout();
+ bool bHasHidden = pDim && pDim->HasInvisibleMember();
+ bool bPageDim = pDim && pDim->GetOrientation() == sheet::DataPilotFieldOrientation_PAGE;
+
+ if (bPageDim)
+ {
+ // Page dimension needs 2 buttons.
+
+ pDoc->ApplyFlagsTab(aScAddress.Col(), aScAddress.Row(), aScAddress.Col(), aScAddress.Row(), aScAddress.Tab(), ScMF::Button);
+
+ ScMF nMFlag = ScMF::ButtonPopup;
+ if (bHasHidden)
+ nMFlag |= ScMF::HiddenMember;
+ pDoc->ApplyFlagsTab(aScAddress.Col()+1, aScAddress.Row(), aScAddress.Col()+1, aScAddress.Row(), aScAddress.Tab(), nMFlag);
+ }
+ else
+ {
+ ScMF nMFlag = ScMF::Button;
+ if (bDataLayout)
+ {
+ // Data layout dimension only has a plain button with no popup.
+ }
+ else if (bDimension)
+ {
+ // Normal dimension has a popup arrow button.
+ if (bHasHidden)
+ nMFlag |= ScMF::HiddenMember;
+
+ nMFlag |= (bHasCompactField ? ScMF::ButtonPopup2 : ScMF::ButtonPopup);
+ }
+
+ pDoc->ApplyFlagsTab(aScAddress.Col(), aScAddress.Row(), aScAddress.Col(), aScAddress.Row(), aScAddress.Tab(), nMFlag);
+ }
+ }
+ }
+ }
+
+ pDPObject->RefreshAfterLoad();
+}
+
+void ScXMLDataPilotTableContext::SetSelectedPage( const OUString& rDimName, const OUString& rSelected )
+{
+ maSelectedPages.emplace(rDimName, rSelected);
+}
+
+void ScXMLDataPilotTableContext::AddDimension(ScDPSaveDimension* pDim)
+{
+ if (!pDPSave)
+ return;
+
+ if (pDim->IsDataLayout())
+ mnDataLayoutType = pDim->GetOrientation();
+
+ // if a dimension with that name has already been inserted,
+ // mark the new one as duplicate
+ if ( !pDim->IsDataLayout() &&
+ pDPSave->GetExistingDimensionByName(pDim->GetName()) )
+ pDim->SetDupFlag(true);
+
+ switch (pDim->GetOrientation())
+ {
+ case sheet::DataPilotFieldOrientation_ROW:
+ ++mnRowFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ ++mnColFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ ++mnPageFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ ++mnDataFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ break;
+ default:
+ break;
+ }
+
+ pDPSave->AddDimension(pDim);
+}
+
+void ScXMLDataPilotTableContext::AddGroupDim(const ScDPSaveNumGroupDimension& aNumGroupDim)
+{
+ if (!pDPDimSaveData)
+ pDPDimSaveData.reset( new ScDPDimensionSaveData );
+ pDPDimSaveData->AddNumGroupDimension(aNumGroupDim);
+}
+
+void ScXMLDataPilotTableContext::AddGroupDim(const ScDPSaveGroupDimension& aGroupDim)
+{
+ if (!pDPDimSaveData)
+ pDPDimSaveData.reset( new ScDPDimensionSaveData );
+ pDPDimSaveData->AddGroupDimension(aGroupDim);
+}
+
+void SAL_CALL ScXMLDataPilotTableContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!bTargetRangeAddress)
+ return;
+
+ std::unique_ptr<ScDPObject> pDPObject(new ScDPObject(pDoc));
+ pDPObject->SetName(sDataPilotTableName);
+ pDPObject->SetTag(sApplicationData);
+ pDPObject->SetOutRange(aTargetRangeAddress);
+ pDPObject->SetHeaderLayout(bHeaderGridLayout);
+
+ sc::PivotTableSources& rPivotSources = GetScImport().GetPivotTableSources();
+
+ switch (nSourceType)
+ {
+ case SQL :
+ {
+ ScImportSourceDesc aImportDesc(pDoc);
+ aImportDesc.aDBName = sDatabaseName;
+ aImportDesc.aObject = sSourceObject;
+ aImportDesc.nType = sheet::DataImportMode_SQL;
+ aImportDesc.bNative = bIsNative;
+ rPivotSources.appendDBSource(pDPObject.get(), aImportDesc);
+ }
+ break;
+ case TABLE :
+ {
+ ScImportSourceDesc aImportDesc(pDoc);
+ aImportDesc.aDBName = sDatabaseName;
+ aImportDesc.aObject = sSourceObject;
+ aImportDesc.nType = sheet::DataImportMode_TABLE;
+ rPivotSources.appendDBSource(pDPObject.get(), aImportDesc);
+ }
+ break;
+ case QUERY :
+ {
+ ScImportSourceDesc aImportDesc(pDoc);
+ aImportDesc.aDBName = sDatabaseName;
+ aImportDesc.aObject = sSourceObject;
+ aImportDesc.nType = sheet::DataImportMode_QUERY;
+ rPivotSources.appendDBSource(pDPObject.get(), aImportDesc);
+ }
+ break;
+ case SERVICE :
+ {
+ ScDPServiceDesc aServiceDesc(sServiceName, sServiceSourceName, sServiceSourceObject,
+ sServiceUsername, sServicePassword);
+ rPivotSources.appendServiceSource(pDPObject.get(), aServiceDesc);
+ }
+ break;
+ case CELLRANGE :
+ {
+ if (bSourceCellRange)
+ {
+ ScSheetSourceDesc aSheetDesc(pDoc);
+ if (!sSourceRangeName.isEmpty())
+ // Range name takes precedence.
+ aSheetDesc.SetRangeName(sSourceRangeName);
+ else
+ aSheetDesc.SetSourceRange(aSourceCellRangeAddress);
+ aSheetDesc.SetQueryParam(aSourceQueryParam);
+ rPivotSources.appendSheetSource(pDPObject.get(), aSheetDesc);
+ }
+ }
+ break;
+ }
+
+ rPivotSources.appendSelectedPages(pDPObject.get(), std::unordered_map(maSelectedPages));
+
+ pDPSave->SetRowGrand(maRowGrandTotal.mbVisible);
+ pDPSave->SetColumnGrand(maColGrandTotal.mbVisible);
+ if (!maRowGrandTotal.maDisplayName.isEmpty())
+ // TODO: Right now, we only support one grand total name for both
+ // column and row totals. Take the value from the row total for
+ // now.
+ pDPSave->SetGrandTotalName(maRowGrandTotal.maDisplayName);
+
+ pDPSave->SetIgnoreEmptyRows(bIgnoreEmptyRows);
+ pDPSave->SetRepeatIfEmpty(bIdentifyCategories);
+ pDPSave->SetFilterButton(bShowFilter);
+ pDPSave->SetDrillDown(bDrillDown);
+ pDPSave->SetExpandCollapse(bShowExpandCollapse);
+ if (pDPDimSaveData)
+ pDPSave->SetDimensionData(pDPDimSaveData.get());
+ pDPObject->SetSaveData(*pDPSave);
+
+ ScDPCollection* pDPCollection = pDoc->GetDPCollection();
+
+ // #i94570# Names have to be unique, or the tables can't be accessed by API.
+ if ( pDPCollection->GetByName(pDPObject->GetName()) )
+ pDPObject->SetName( OUString() ); // ignore the invalid name, create a new name in AfterXMLLoading
+
+ SetButtons(pDPObject.get());
+
+ pDPCollection->InsertNewTable(std::move(pDPObject));
+}
+
+void ScXMLDataPilotTableContext::SetGrandTotal(
+ XMLTokenEnum eOrientation, bool bVisible, const OUString& rDisplayName)
+{
+ switch (eOrientation)
+ {
+ case XML_BOTH:
+ maRowGrandTotal.mbVisible = bVisible;
+ maRowGrandTotal.maDisplayName = rDisplayName;
+ maColGrandTotal.mbVisible = bVisible;
+ maColGrandTotal.maDisplayName = rDisplayName;
+ break;
+ case XML_ROW:
+ maRowGrandTotal.mbVisible = bVisible;
+ maRowGrandTotal.maDisplayName = rDisplayName;
+ break;
+ case XML_COLUMN:
+ maColGrandTotal.mbVisible = bVisible;
+ maColGrandTotal.maDisplayName = rDisplayName;
+ break;
+ default:
+ break;
+ }
+}
+
+ScXMLDPSourceSQLContext::ScXMLDPSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ pDataPilotTable->SetDatabaseName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_SQL_STATEMENT ):
+ pDataPilotTable->SetSourceObject(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_PARSE_SQL_STATEMENT ):
+ pDataPilotTable->SetNative(!IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+}
+
+ScXMLDPSourceSQLContext::~ScXMLDPSourceSQLContext()
+{
+}
+
+ScXMLDPSourceTableContext::ScXMLDPSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ pDataPilotTable->SetDatabaseName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_NAME ):
+ case XML_ELEMENT( TABLE, XML_DATABASE_TABLE_NAME ):
+ pDataPilotTable->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLDPSourceTableContext::~ScXMLDPSourceTableContext()
+{
+}
+
+ScXMLDPSourceQueryContext::ScXMLDPSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ pDataPilotTable->SetDatabaseName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_QUERY_NAME ):
+ pDataPilotTable->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLDPSourceQueryContext::~ScXMLDPSourceQueryContext()
+{
+}
+
+ScXMLSourceServiceContext::ScXMLSourceServiceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ pDataPilotTable->SetServiceName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_NAME ):
+ pDataPilotTable->SetServiceSourceName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_OBJECT_NAME ):
+ pDataPilotTable->SetServiceSourceObject(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_USER_NAME ):
+ pDataPilotTable->SetServiceUsername(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_PASSWORD ):
+ pDataPilotTable->SetServicePassword(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLSourceServiceContext::~ScXMLSourceServiceContext()
+{
+}
+
+ScXMLDataPilotGrandTotalContext::ScXMLDataPilotGrandTotalContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTableContext ) :
+ ScXMLImportContext( rImport ),
+ mpTableContext(pTableContext),
+ meOrientation(NONE),
+ mbVisible(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ mbVisible = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ if (IsXMLToken(aIter, XML_BOTH))
+ meOrientation = BOTH;
+ else if (IsXMLToken(aIter, XML_ROW))
+ meOrientation = ROW;
+ else if (IsXMLToken(aIter, XML_COLUMN))
+ meOrientation = COLUMN;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ maDisplayName = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotGrandTotalContext::~ScXMLDataPilotGrandTotalContext()
+{
+}
+
+ void SAL_CALL ScXMLDataPilotGrandTotalContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ XMLTokenEnum eOrient = XML_NONE;
+ switch (meOrientation)
+ {
+ case BOTH:
+ eOrient = XML_BOTH;
+ break;
+ case ROW:
+ eOrient = XML_ROW;
+ break;
+ case COLUMN:
+ eOrient = XML_COLUMN;
+ break;
+ default:
+ break;
+ }
+ mpTableContext->SetGrandTotal(eOrient, mbVisible, maDisplayName);
+}
+
+ScXMLSourceCellRangeContext::ScXMLSourceCellRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTable) :
+ ScXMLImportContext( rImport ),
+ pDataPilotTable(pTempDataPilotTable)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_ADDRESS ):
+ {
+ ScRange aSourceRangeAddress;
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ if (ScRangeStringConverter::GetRangeFromString( aSourceRangeAddress, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ))
+ pDataPilotTable->SetSourceCellRangeAddress(aSourceRangeAddress);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ pDataPilotTable->SetSourceRangeName(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLSourceCellRangeContext::~ScXMLSourceCellRangeContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceCellRangeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER ):
+ pContext = new ScXMLDPFilterContext(GetScImport(), pAttribList, pDataPilotTable);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotFieldContext::ScXMLDataPilotFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTable) :
+ ScXMLImportContext( rImport ),
+ pDataPilotTable(pTempDataPilotTable),
+ fStart(0.0),
+ fEnd(0.0),
+ fStep(0.0),
+ nUsedHierarchy(1),
+ nGroupPart(0),
+ nFunction(ScGeneralFunction::NONE),
+ nOrientation(sheet::DataPilotFieldOrientation_HIDDEN),
+ bSelectedPage(false),
+ bIsGroupField(false),
+ bDateValue(false),
+ bAutoStart(false),
+ bAutoEnd(false),
+ mbHasHiddenMember(false)
+{
+ bool bHasName = false;
+ bool bDataLayout = false;
+ bool bIgnoreSelectedPage = false;
+ OUString aDisplayName;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_SOURCE_FIELD_NAME ):
+ sName = aIter.toString();
+ bHasName = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ aDisplayName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_IS_DATA_LAYOUT_FIELD ):
+ bDataLayout = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ nFunction = ScXMLConverter::GetFunctionFromString2( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ nOrientation = ScXMLConverter::GetOrientationFromString( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_SELECTED_PAGE ):
+ sSelectedPage = aIter.toString();
+ bSelectedPage = true;
+ break;
+ case XML_ELEMENT( LO_EXT, XML_IGNORE_SELECTED_PAGE ):
+ bIgnoreSelectedPage = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_USED_HIERARCHY ):
+ nUsedHierarchy = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ // use the new extension elements
+ if (bIgnoreSelectedPage)
+ bSelectedPage = false;
+
+ if (bHasName)
+ {
+ xDim.reset(new ScDPSaveDimension(sName, bDataLayout));
+ if (!aDisplayName.isEmpty())
+ xDim->SetLayoutName(aDisplayName);
+ }
+}
+
+ScXMLDataPilotFieldContext::~ScXMLDataPilotFieldContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotFieldContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_LEVEL ):
+ pContext = new ScXMLDataPilotLevelContext(GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_FIELD_REFERENCE ):
+ pContext = new ScXMLDataPilotFieldReferenceContext(GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_GROUPS ):
+ pContext = new ScXMLDataPilotGroupsContext(GetScImport(), pAttribList, this);
+ break;
+ }
+
+ return pContext;
+}
+
+void ScXMLDataPilotFieldContext::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
+{
+ if (xDim)
+ {
+ bool isVisible = pMember->GetIsVisible();
+ xDim->AddMember(std::move(pMember));
+ if (!isVisible)
+ // This member is hidden.
+ mbHasHiddenMember = true;
+ }
+}
+
+void ScXMLDataPilotFieldContext::SetSubTotalName(const OUString& rName)
+{
+ if (xDim)
+ xDim->SetSubtotalName(rName);
+}
+
+void ScXMLDataPilotFieldContext::SetLayoutInfo(const css::sheet::DataPilotFieldLayoutInfo& aInfo)
+{
+ if (xDim)
+ xDim->SetLayoutInfo(&aInfo);
+
+ if (pDataPilotTable && aInfo.LayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT)
+ pDataPilotTable->SetHasCompactField();
+}
+
+void ScXMLDataPilotFieldContext::AddGroup(::std::vector<OUString>&& rMembers, const OUString& rName)
+{
+ ScXMLDataPilotGroup aGroup;
+ aGroup.aMembers = std::move(rMembers);
+ aGroup.aName = rName;
+ aGroups.push_back(std::move(aGroup));
+}
+
+void SAL_CALL ScXMLDataPilotFieldContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!xDim)
+ return;
+
+ xDim->SetUsedHierarchy(nUsedHierarchy);
+ xDim->SetFunction(nFunction);
+ xDim->SetOrientation(nOrientation);
+ if (bSelectedPage)
+ {
+ pDataPilotTable->SetSelectedPage(xDim->GetName(), sSelectedPage);
+ }
+ pDataPilotTable->AddDimension(xDim.release());
+ if (!bIsGroupField)
+ return;
+
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = bDateValue;
+ aInfo.mbAutoStart = bAutoStart;
+ aInfo.mbAutoEnd = bAutoEnd;
+ aInfo.mfStart = fStart;
+ aInfo.mfEnd = fEnd;
+ aInfo.mfStep = fStep;
+ if (!sGroupSource.isEmpty())
+ {
+ ScDPSaveGroupDimension aGroupDim(sGroupSource, sName);
+ if (nGroupPart)
+ aGroupDim.SetDateInfo(aInfo, nGroupPart);
+ else
+ {
+ for (const auto& rGroup : aGroups)
+ {
+ ScDPSaveGroupItem aItem(rGroup.aName);
+ for (const auto& rMember : rGroup.aMembers)
+ {
+ aItem.AddElement(rMember);
+ }
+ aGroupDim.AddGroupItem(aItem);
+ }
+ }
+ pDataPilotTable->AddGroupDim(aGroupDim);
+ }
+ else //NumGroup
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim(sName, aInfo);
+ if (nGroupPart)
+ aNumGroupDim.SetDateInfo(aInfo, nGroupPart);
+ pDataPilotTable->AddGroupDim(aNumGroupDim);
+ }
+}
+
+ScXMLDataPilotFieldReferenceContext::ScXMLDataPilotFieldReferenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldReference aReference;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TYPE ):
+ {
+ if (IsXMLToken(aIter, XML_NONE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::NONE;
+ else if (IsXMLToken(aIter, XML_MEMBER_DIFFERENCE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE;
+ else if (IsXMLToken(aIter, XML_MEMBER_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_MEMBER_PERCENTAGE_DIFFERENCE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
+ else if (IsXMLToken(aIter, XML_RUNNING_TOTAL))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::RUNNING_TOTAL;
+ else if (IsXMLToken(aIter, XML_ROW_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_COLUMN_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_TOTAL_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_INDEX))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::INDEX;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FIELD_NAME ):
+ {
+ aReference.ReferenceField = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_MEMBER_TYPE ):
+ {
+ if (IsXMLToken(aIter, XML_NAMED))
+ aReference.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NAMED;
+ else if (IsXMLToken(aIter, XML_PREVIOUS))
+ aReference.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::PREVIOUS;
+ else if (IsXMLToken(aIter, XML_NEXT))
+ aReference.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NEXT;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_MEMBER_NAME ):
+ {
+ aReference.ReferenceItemName = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetFieldReference(aReference);
+}
+
+ScXMLDataPilotFieldReferenceContext::~ScXMLDataPilotFieldReferenceContext()
+{
+}
+
+ScXMLDataPilotLevelContext::ScXMLDataPilotLevelContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_SHOW_EMPTY ):
+ pDataPilotField->SetShowEmpty(IsXMLToken(aIter, XML_TRUE));
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_REPEAT_ITEM_LABELS ):
+ pDataPilotField->SetRepeatItemLabels(IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotLevelContext::~ScXMLDataPilotLevelContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotLevelContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_SUBTOTALS ):
+ pContext = new ScXMLDataPilotSubTotalsContext(GetScImport(), pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_MEMBERS ):
+ pContext = new ScXMLDataPilotMembersContext(GetScImport(), pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_DISPLAY_INFO ):
+ pContext = new ScXMLDataPilotDisplayInfoContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_SORT_INFO ):
+ pContext = new ScXMLDataPilotSortInfoContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_LAYOUT_INFO ):
+ pContext = new ScXMLDataPilotLayoutInfoContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotDisplayInfoContext::ScXMLDataPilotDisplayInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldAutoShowInfo aInfo;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ENABLED ):
+ if (IsXMLToken(aIter, XML_TRUE))
+ aInfo.IsEnabled = true;
+ else
+ aInfo.IsEnabled = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_MEMBER_MODE ):
+ if (IsXMLToken(aIter, XML_FROM_TOP))
+ aInfo.ShowItemsMode = sheet::DataPilotFieldShowItemsMode::FROM_TOP;
+ else if (IsXMLToken(aIter, XML_FROM_BOTTOM))
+ aInfo.ShowItemsMode = sheet::DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ break;
+ case XML_ELEMENT( TABLE, XML_MEMBER_COUNT ):
+ aInfo.ItemCount = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_FIELD ):
+ aInfo.DataField = aIter.toString();
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetAutoShowInfo(aInfo);
+}
+
+ScXMLDataPilotDisplayInfoContext::~ScXMLDataPilotDisplayInfoContext()
+{
+}
+
+ScXMLDataPilotSortInfoContext::ScXMLDataPilotSortInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldSortInfo aInfo;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ORDER ):
+ if (IsXMLToken(aIter, XML_ASCENDING))
+ aInfo.IsAscending = true;
+ else if (IsXMLToken(aIter, XML_DESCENDING))
+ aInfo.IsAscending = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_SORT_MODE ):
+ if (IsXMLToken(aIter, XML_NONE))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::NONE;
+ else if (IsXMLToken(aIter, XML_MANUAL))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
+ else if (IsXMLToken(aIter, XML_NAME))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
+ else if (IsXMLToken(aIter, XML_DATA))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::DATA;
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_FIELD ):
+ aInfo.Field = aIter.toString();
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetSortInfo(aInfo);
+}
+
+ScXMLDataPilotSortInfoContext::~ScXMLDataPilotSortInfoContext()
+{
+}
+
+ScXMLDataPilotLayoutInfoContext::ScXMLDataPilotLayoutInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldLayoutInfo aInfo;
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ADD_EMPTY_LINES ):
+ if (IsXMLToken(aIter, XML_TRUE))
+ aInfo.AddEmptyLines = true;
+ else
+ aInfo.AddEmptyLines = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_LAYOUT_MODE ):
+ case XML_ELEMENT( LO_EXT, XML_LAYOUT_MODE ):
+ // Ensure that loext:layout-mode="compact" is not overwritten by any
+ // value of table:layout-mode.
+ if (aInfo.LayoutMode != sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT)
+ {
+ if (IsXMLToken(aIter, XML_TABULAR_LAYOUT))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_TOP))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_BOTTOM))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ else if (IsXMLToken(aIter, XML_COMPACT_LAYOUT))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT;
+ }
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetLayoutInfo(aInfo);}
+
+ScXMLDataPilotLayoutInfoContext::~ScXMLDataPilotLayoutInfoContext()
+{
+}
+
+ScXMLDataPilotSubTotalsContext::ScXMLDataPilotSubTotalsContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+
+ // has no attributes
+}
+
+ScXMLDataPilotSubTotalsContext::~ScXMLDataPilotSubTotalsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotSubTotalsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_SUBTOTAL ):
+ pContext = new ScXMLDataPilotSubTotalContext(GetScImport(), pAttribList, this);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDataPilotSubTotalsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDataPilotField->SetSubTotals(std::vector(maFunctions));
+ if (!maDisplayName.isEmpty())
+ pDataPilotField->SetSubTotalName(maDisplayName);
+}
+
+void ScXMLDataPilotSubTotalsContext::AddFunction(ScGeneralFunction nFunction)
+{
+ maFunctions.push_back(nFunction);
+}
+
+void ScXMLDataPilotSubTotalsContext::SetDisplayName(const OUString& rName)
+{
+ maDisplayName = rName;
+}
+
+ScXMLDataPilotSubTotalContext::ScXMLDataPilotSubTotalContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotSubTotalsContext* pDataPilotSubTotals) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ pDataPilotSubTotals->AddFunction( ScXMLConverter::GetFunctionFromString2( aIter.toString() ) );
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ pDataPilotSubTotals->SetDisplayName(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotSubTotalContext::~ScXMLDataPilotSubTotalContext()
+{
+}
+
+ScXMLDataPilotMembersContext::ScXMLDataPilotMembersContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ // has no attributes
+}
+
+ScXMLDataPilotMembersContext::~ScXMLDataPilotMembersContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotMembersContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_MEMBER ):
+ pContext = new ScXMLDataPilotMemberContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotMemberContext::ScXMLDataPilotMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField),
+ bDisplay( true ),
+ bDisplayDetails( true ),
+ bHasName( false )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ sName = aIter.toString();
+ bHasName = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ maDisplayName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_SHOW_DETAILS ):
+ bDisplayDetails = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotMemberContext::~ScXMLDataPilotMemberContext()
+{
+}
+
+void SAL_CALL ScXMLDataPilotMemberContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (bHasName) // #i53407# don't check sName, empty name is allowed
+ {
+ std::unique_ptr<ScDPSaveMember> pMember(new ScDPSaveMember(sName));
+ if (!maDisplayName.isEmpty())
+ pMember->SetLayoutName(maDisplayName);
+ pMember->SetIsVisible(bDisplay);
+ pMember->SetShowDetails(bDisplayDetails);
+ pDataPilotField->AddMember(std::move(pMember));
+ }
+}
+
+ScXMLDataPilotGroupsContext::ScXMLDataPilotGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ OUString sGroupSource;
+ double fStart(0.0);
+ double fEnd(0.0);
+ double fStep(0.0);
+ sal_Int32 nGroupPart(0);
+ bool bDateValue(false);
+ bool bAutoStart(true);
+ bool bAutoEnd(true);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_SOURCE_FIELD_NAME :
+ {
+ sGroupSource = aIter.toString();
+ }
+ break;
+ case XML_DATE_START :
+ {
+ bDateValue = true;
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoStart = true;
+ else
+ {
+ GetScImport().GetMM100UnitConverter().convertDateTime(fStart, aIter.toView());
+ bAutoStart = false;
+ }
+ }
+ break;
+ case XML_DATE_END :
+ {
+ bDateValue = true;
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoEnd = true;
+ else
+ {
+ GetScImport().GetMM100UnitConverter().convertDateTime(fEnd, aIter.toView());
+ bAutoEnd = false;
+ }
+ }
+ break;
+ case XML_START :
+ {
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoStart = true;
+ else
+ {
+ fStart = aIter.toDouble();
+ bAutoStart = false;
+ }
+ }
+ break;
+ case XML_END :
+ {
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoEnd = true;
+ else
+ {
+ fEnd = aIter.toDouble();
+ bAutoEnd = false;
+ }
+ }
+ break;
+ case XML_STEP :
+ {
+ fStep = aIter.toDouble();
+ }
+ break;
+ case XML_GROUPED_BY :
+ {
+ if (IsXMLToken(aIter, XML_SECONDS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::SECONDS;
+ else if (IsXMLToken(aIter, XML_MINUTES))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::MINUTES;
+ else if (IsXMLToken(aIter, XML_HOURS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::HOURS;
+ else if (IsXMLToken(aIter, XML_DAYS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::DAYS;
+ else if (IsXMLToken(aIter, XML_MONTHS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::MONTHS;
+ else if (IsXMLToken(aIter, XML_QUARTERS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::QUARTERS;
+ else if (IsXMLToken(aIter, XML_YEARS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::YEARS;
+ }
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetGrouping(sGroupSource, fStart, fEnd, fStep, nGroupPart, bDateValue, bAutoStart, bAutoEnd);
+}
+
+ScXMLDataPilotGroupsContext::~ScXMLDataPilotGroupsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotGroupsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_DATA_PILOT_GROUP ))
+ {
+ pContext = new ScXMLDataPilotGroupContext(GetScImport(), pAttribList, pDataPilotField);
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotGroupContext::ScXMLDataPilotGroupContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NAME ) ) );
+ if (aIter != rAttrList->end())
+ sName = aIter.toString();
+ }
+}
+
+ScXMLDataPilotGroupContext::~ScXMLDataPilotGroupContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotGroupContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_DATA_PILOT_MEMBER ) ||
+ nElement == XML_ELEMENT( TABLE, XML_DATA_PILOT_GROUP_MEMBER ))
+ {
+ pContext = new ScXMLDataPilotGroupMemberContext(GetScImport(), pAttribList, this);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDataPilotGroupContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDataPilotField->AddGroup(std::vector(aMembers), sName);
+}
+
+ScXMLDataPilotGroupMemberContext::ScXMLDataPilotGroupMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotGroupContext* pTempDataPilotGroup) :
+ ScXMLImportContext( rImport ),
+ pDataPilotGroup(pTempDataPilotGroup)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NAME ) ) );
+ if (aIter != rAttrList->end())
+ sName = aIter.toString();
+ }
+}
+
+ScXMLDataPilotGroupMemberContext::~ScXMLDataPilotGroupMemberContext()
+{
+}
+
+void SAL_CALL ScXMLDataPilotGroupMemberContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sName.isEmpty())
+ pDataPilotGroup->AddMember(sName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldpimp.hxx b/sc/source/filter/xml/xmldpimp.hxx
new file mode 100644
index 0000000000..641a213287
--- /dev/null
+++ b/sc/source/filter/xml/xmldpimp.hxx
@@ -0,0 +1,473 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <xmloff/xmltoken.hxx>
+
+#include <dpsave.hxx>
+#include <queryparam.hxx>
+#include "importcontext.hxx"
+
+#include <unordered_map>
+
+namespace com::sun::star::sheet { struct DataPilotFieldAutoShowInfo; }
+namespace com::sun::star::sheet { struct DataPilotFieldLayoutInfo; }
+namespace com::sun::star::sheet { struct DataPilotFieldReference; }
+namespace com::sun::star::sheet { struct DataPilotFieldSortInfo; }
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScDPSaveNumGroupDimension;
+class ScDPSaveGroupDimension;
+class ScDPObject;
+
+enum ScMySourceType
+{
+ SQL,
+ TABLE,
+ QUERY,
+ SERVICE,
+ CELLRANGE
+};
+
+class ScXMLDataPilotTablesContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotTablesContext( ScXMLImport& rImport);
+
+ virtual ~ScXMLDataPilotTablesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotTableContext : public ScXMLImportContext
+{
+ typedef std::unordered_map<OUString, OUString> SelectedPagesType;
+
+ struct GrandTotalItem
+ {
+ OUString maDisplayName;
+ bool mbVisible;
+ GrandTotalItem();
+ };
+ ScDocument* pDoc;
+ std::unique_ptr<ScDPSaveData> pDPSave;
+ std::unique_ptr<ScDPDimensionSaveData> pDPDimSaveData;
+ GrandTotalItem maRowGrandTotal;
+ GrandTotalItem maColGrandTotal;
+ OUString sDataPilotTableName;
+ OUString sApplicationData;
+ OUString sDatabaseName;
+ OUString sSourceObject;
+ OUString sServiceName;
+ OUString sServiceSourceName;
+ OUString sServiceSourceObject;
+ OUString sServiceUsername;
+ OUString sServicePassword;
+ OUString sButtons;
+ OUString sSourceRangeName;
+ ScRange aSourceCellRangeAddress;
+ ScRange aTargetRangeAddress;
+ ScQueryParam aSourceQueryParam;
+ ScMySourceType nSourceType;
+ sal_uInt32 mnRowFieldCount;
+ sal_uInt32 mnColFieldCount;
+ sal_uInt32 mnPageFieldCount;
+ sal_uInt32 mnDataFieldCount;
+ css::sheet::DataPilotFieldOrientation
+ mnDataLayoutType;
+ bool bIsNative:1;
+ bool bIgnoreEmptyRows:1;
+ bool bIdentifyCategories:1;
+ bool bTargetRangeAddress:1;
+ bool bSourceCellRange:1;
+ bool bShowFilter:1;
+ bool bDrillDown:1;
+ bool bShowExpandCollapse:1;
+ bool bHeaderGridLayout:1;
+ bool bHasCompactField:1; // True = One or more fields have compact layout.
+
+ SelectedPagesType maSelectedPages;
+
+public:
+
+ ScXMLDataPilotTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLDataPilotTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetGrandTotal(::xmloff::token::XMLTokenEnum eOrientation, bool bVisible, const OUString& rDisplayName);
+ void SetDatabaseName(const OUString& sValue) { sDatabaseName = sValue; }
+ void SetSourceObject(const OUString& sValue) { sSourceObject = sValue; }
+ void SetNative(bool bValue) { bIsNative = bValue; }
+ void SetServiceName(const OUString& sValue) { sServiceName = sValue; }
+ void SetServiceSourceName(const OUString& sValue) { sServiceSourceName = sValue; }
+ void SetServiceSourceObject(const OUString& sValue) { sServiceSourceObject = sValue; }
+ void SetServiceUsername(const OUString& sValue) { sServiceUsername = sValue; }
+ void SetServicePassword(const OUString& sValue) { sServicePassword = sValue; }
+ void SetSourceRangeName(const OUString& sValue) { sSourceRangeName = sValue; bSourceCellRange = true; }
+ void SetSourceCellRangeAddress(const ScRange& aValue) { aSourceCellRangeAddress = aValue; bSourceCellRange = true; }
+ void SetSourceQueryParam(const ScQueryParam& aValue) { aSourceQueryParam = aValue; }
+ void AddDimension(ScDPSaveDimension* pDim);
+ void AddGroupDim(const ScDPSaveNumGroupDimension& aNumGroupDim);
+ void AddGroupDim(const ScDPSaveGroupDimension& aGroupDim);
+ void SetButtons(ScDPObject* pDPObject);
+ void SetSelectedPage( const OUString& rDimName, const OUString& rSelected );
+ void SetHasCompactField() { bHasCompactField = true; }
+};
+
+class ScXMLDPSourceSQLContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDPSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDPSourceSQLContext() override;
+};
+
+class ScXMLDPSourceTableContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDPSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDPSourceTableContext() override;
+};
+
+class ScXMLDPSourceQueryContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDPSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDPSourceQueryContext() override;
+};
+
+class ScXMLSourceServiceContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLSourceServiceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLSourceServiceContext() override;
+};
+
+class ScXMLDataPilotGrandTotalContext : public ScXMLImportContext
+{
+ enum Orientation { COLUMN, ROW, BOTH, NONE };
+
+ ScXMLDataPilotTableContext* mpTableContext;
+ OUString maDisplayName;
+ Orientation meOrientation;
+ bool mbVisible;
+
+public:
+ ScXMLDataPilotGrandTotalContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTableContext );
+
+ virtual ~ScXMLDataPilotGrandTotalContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLSourceCellRangeContext : public ScXMLImportContext
+{
+ ScXMLDataPilotTableContext* pDataPilotTable;
+
+public:
+
+ ScXMLSourceCellRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLSourceCellRangeContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+struct ScXMLDataPilotGroup
+{
+ ::std::vector<OUString> aMembers;
+ OUString aName;
+};
+
+class ScXMLDataPilotFieldContext : public ScXMLImportContext
+{
+ ScXMLDataPilotTableContext* pDataPilotTable;
+ std::unique_ptr<ScDPSaveDimension> xDim;
+
+ ::std::vector<ScXMLDataPilotGroup> aGroups;
+ OUString sGroupSource;
+ OUString sSelectedPage;
+ OUString sName;
+ double fStart;
+ double fEnd;
+ double fStep;
+ sal_Int32 nUsedHierarchy;
+ sal_Int32 nGroupPart;
+ ScGeneralFunction nFunction;
+ css::sheet::DataPilotFieldOrientation
+ nOrientation;
+ bool bSelectedPage:1;
+ bool bIsGroupField:1;
+ bool bDateValue:1;
+ bool bAutoStart:1;
+ bool bAutoEnd:1;
+ bool mbHasHiddenMember:1; // TODO: import to document core
+
+public:
+
+ ScXMLDataPilotFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDataPilotFieldContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetShowEmpty(const bool bValue) { if (xDim) xDim->SetShowEmpty(bValue); }
+ void SetRepeatItemLabels(const bool bSet) { if (xDim) xDim->SetRepeatItemLabels(bSet); }
+ void SetSubTotals(std::vector<ScGeneralFunction> && rFunctions) { if (xDim) xDim->SetSubTotals(std::move(rFunctions)); }
+ void AddMember(std::unique_ptr<ScDPSaveMember> pMember);
+ void SetSubTotalName(const OUString& rName);
+ void SetFieldReference(const css::sheet::DataPilotFieldReference& aRef) { if (xDim) xDim->SetReferenceValue(&aRef); }
+ void SetAutoShowInfo(const css::sheet::DataPilotFieldAutoShowInfo& aInfo) { if (xDim) xDim->SetAutoShowInfo(&aInfo); }
+ void SetSortInfo(const css::sheet::DataPilotFieldSortInfo& aInfo) { if (xDim) xDim->SetSortInfo(&aInfo); }
+ void SetLayoutInfo(const css::sheet::DataPilotFieldLayoutInfo& aInfo);
+ void SetGrouping(const OUString& rGroupSource, const double& rStart, const double& rEnd, const double& rStep,
+ sal_Int32 nPart, bool bDate, bool bAutoSt, bool bAutoE)
+ {
+ bIsGroupField = true;
+ sGroupSource = rGroupSource;
+ fStart = rStart;
+ fEnd = rEnd;
+ fStep = rStep;
+ nGroupPart = nPart;
+ bDateValue = bDate;
+ bAutoStart = bAutoSt;
+ bAutoEnd = bAutoE;
+ }
+ void AddGroup(::std::vector<OUString>&& rMembers, const OUString& rName);
+};
+
+class ScXMLDataPilotFieldReferenceContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotFieldReferenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotFieldReferenceContext() override;
+};
+
+class ScXMLDataPilotLevelContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+public:
+
+ ScXMLDataPilotLevelContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotLevelContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotDisplayInfoContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotDisplayInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotDisplayInfoContext() override;
+};
+
+class ScXMLDataPilotSortInfoContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotSortInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotSortInfoContext() override;
+};
+
+class ScXMLDataPilotLayoutInfoContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotLayoutInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotLayoutInfoContext() override;
+};
+
+class ScXMLDataPilotSubTotalsContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+ std::vector<ScGeneralFunction> maFunctions;
+ OUString maDisplayName;
+
+public:
+ ScXMLDataPilotSubTotalsContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotSubTotalsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ void AddFunction(ScGeneralFunction nFunction);
+ void SetDisplayName(const OUString& rName);
+};
+
+class ScXMLDataPilotSubTotalContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotSubTotalContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotSubTotalsContext* pDataPilotSubTotals);
+
+ virtual ~ScXMLDataPilotSubTotalContext() override;
+};
+
+class ScXMLDataPilotMembersContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+public:
+
+ ScXMLDataPilotMembersContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotMembersContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotMemberContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+ OUString sName;
+ OUString maDisplayName;
+ bool bDisplay;
+ bool bDisplayDetails;
+ bool bHasName;
+
+public:
+
+ ScXMLDataPilotMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotMemberContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDataPilotGroupsContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+public:
+
+ ScXMLDataPilotGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotGroupsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotGroupContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+ OUString sName;
+ ::std::vector<OUString> aMembers;
+
+public:
+
+ ScXMLDataPilotGroupContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotGroupContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void AddMember(const OUString& sMember) { aMembers.push_back(sMember); }
+};
+
+class ScXMLDataPilotGroupMemberContext : public ScXMLImportContext
+{
+ ScXMLDataPilotGroupContext* pDataPilotGroup;
+ OUString sName;
+
+public:
+
+ ScXMLDataPilotGroupMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotGroupContext* pDataPilotGroup);
+
+ virtual ~ScXMLDataPilotGroupMemberContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldrani.cxx b/sc/source/filter/xml/xmldrani.cxx
new file mode 100644
index 0000000000..5708c74d2f
--- /dev/null
+++ b/sc/source/filter/xml/xmldrani.cxx
@@ -0,0 +1,812 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmldrani.hxx"
+#include "xmlimprt.hxx"
+#include "xmlfilti.hxx"
+#include "xmlsorti.hxx"
+#include <document.hxx>
+#include <globalnames.hxx>
+#include <dbdata.hxx>
+#include <datauno.hxx>
+#include <attrib.hxx>
+#include <unonames.hxx>
+#include "XMLConverter.hxx"
+#include <rangeutl.hxx>
+#include <dputil.hxx>
+#include <sortparam.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <sax/tools/converter.hxx>
+
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/table/TableOrientation.hpp>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLDatabaseRangesContext::ScXMLDatabaseRangesContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // has no attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLDatabaseRangesContext::~ScXMLDatabaseRangesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDatabaseRangesContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_RANGE ):
+ {
+ pContext = new ScXMLDatabaseRangeContext( GetScImport(), pAttribList );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDatabaseRangeContext::ScXMLDatabaseRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ mpQueryParam(new ScQueryParam),
+ sDatabaseRangeName(STR_DB_LOCAL_NONAME),
+ nSourceType(sheet::DataImportMode_NONE),
+ nRefresh(0),
+ nSubTotalsUserListIndex(0),
+ mbValidRange(true),
+ bContainsSort(false),
+ bContainsSubTotal(false),
+ bNative(true),
+ bIsSelection(false),
+ bKeepFormats(false),
+ bMoveCells(false),
+ bStripData(false),
+ bAutoFilter(false),
+ bSubTotalsBindFormatsToContent(false),
+ bSubTotalsIsCaseSensitive(false),
+ bSubTotalsInsertPageBreaks(false),
+ bSubTotalsSortGroups(false),
+ bSubTotalsEnabledUserList(false),
+ bSubTotalsAscending(true),
+ bFilterConditionSourceRange(false),
+ bHasHeader(true),
+ bByRow(true),
+ meRangeType(ScDBCollection::GlobalNamed)
+{
+ if( rAttrList.is() )
+ {
+ for( auto &aIter : *rAttrList )
+ {
+ switch( aIter.getToken() )
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ {
+ sDatabaseRangeName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_IS_SELECTION ):
+ {
+ bIsSelection = IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ON_UPDATE_KEEP_STYLES ):
+ {
+ bKeepFormats = IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ON_UPDATE_KEEP_SIZE ):
+ {
+ bMoveCells = !IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_HAS_PERSISTENT_DATA ):
+ {
+ bStripData = !IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ {
+ bByRow = !IsXMLToken( aIter, XML_COLUMN );
+ mpQueryParam->bByRow = bByRow;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTAINS_HEADER ):
+ {
+ bHasHeader = IsXMLToken( aIter, XML_TRUE );
+ mpQueryParam->bHasHeader = bHasHeader;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_FILTER_BUTTONS ):
+ {
+ bAutoFilter = IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ sal_Int32 nOffset = 0;
+ if (!ScRangeStringConverter::GetRangeFromString(
+ maRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset))
+ mbValidRange = false;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_REFRESH_DELAY ):
+ {
+ double fTime;
+ if (::sax::Converter::convertDuration( fTime, aIter.toView() ))
+ nRefresh = std::max( static_cast<sal_Int32>(fTime * 86400.0), sal_Int32(0) );
+ }
+ break;
+ }
+ }
+ }
+
+ mpQueryParam->nTab = maRange.aStart.Tab();
+ mpQueryParam->nCol1 = maRange.aStart.Col();
+ mpQueryParam->nRow1 = maRange.aStart.Row();
+ mpQueryParam->nCol2 = maRange.aEnd.Col();
+ mpQueryParam->nRow2 = maRange.aEnd.Row();
+
+ if (sDatabaseRangeName.startsWith(STR_DB_LOCAL_NONAME))
+ meRangeType = ScDBCollection::SheetAnonymous;
+ else if (sDatabaseRangeName.startsWith(STR_DB_GLOBAL_NONAME))
+ meRangeType = ScDBCollection::GlobalAnonymous;
+}
+
+ScXMLDatabaseRangeContext::~ScXMLDatabaseRangeContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDatabaseRangeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_SQL ):
+ {
+ pContext = new ScXMLSourceSQLContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_TABLE ):
+ {
+ pContext = new ScXMLSourceTableContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_QUERY ):
+ {
+ pContext = new ScXMLSourceQueryContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER ):
+ {
+ pContext = new ScXMLFilterContext(
+ GetScImport(), pAttribList, *mpQueryParam, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SORT ):
+ {
+ bContainsSort = true;
+ pContext = new ScXMLSortContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUBTOTAL_RULES ):
+ {
+ bContainsSubTotal = true;
+ pContext = new ScXMLSubTotalRulesContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+std::unique_ptr<ScDBData> ScXMLDatabaseRangeContext::ConvertToDBData(const OUString& rName)
+{
+ if (!mbValidRange)
+ return nullptr;
+
+ ScDocument* pDoc = GetScImport().GetDocument();
+
+ ::std::unique_ptr<ScDBData> pData(
+ new ScDBData(rName, maRange.aStart.Tab(), maRange.aStart.Col(), maRange.aStart.Row(), maRange.aEnd.Col(), maRange.aEnd.Row(), bByRow, bHasHeader));
+
+ pData->SetAutoFilter(bAutoFilter);
+ pData->SetKeepFmt(bKeepFormats);
+ pData->SetDoSize(bMoveCells);
+ pData->SetStripData(bStripData);
+
+ pDoc->PrepareQuery(mpQueryParam->nTab, *mpQueryParam);
+
+ pData->SetQueryParam(*mpQueryParam);
+
+ if (bFilterConditionSourceRange)
+ {
+ pData->SetAdvancedQuerySource( &aFilterConditionSourceRangeAddress );
+ }
+
+ {
+ ScImportParam aParam;
+ aParam.bNative = bNative;
+ aParam.aDBName = sDatabaseName.isEmpty() ? sConnectionResource : sDatabaseName;
+ aParam.aStatement = sSourceObject;
+ switch (nSourceType)
+ {
+ case sheet::DataImportMode_NONE:
+ aParam.bImport = false;
+ break;
+ case sheet::DataImportMode_SQL:
+ aParam.bImport = true;
+ aParam.bSql = true;
+ break;
+ case sheet::DataImportMode_TABLE:
+ aParam.bImport = true;
+ aParam.bSql = false;
+ aParam.nType = ScDbTable;
+ break;
+ case sheet::DataImportMode_QUERY:
+ aParam.bImport = true;
+ aParam.bSql = false;
+ aParam.nType = ScDbQuery;
+ break;
+ default:
+ OSL_FAIL("Unknown data import mode");
+ aParam.bImport = false;
+ }
+ pData->SetImportParam(aParam);
+ }
+
+ if (bContainsSort)
+ {
+ size_t nOldSize = aSortSequence.getLength();
+ aSortSequence.realloc(nOldSize + 1);
+ beans::PropertyValue aProperty;
+ aProperty.Name = SC_UNONAME_ORIENT;
+ table::TableOrientation eOrient = mpQueryParam->bByRow ?
+ table::TableOrientation_ROWS : table::TableOrientation_COLUMNS;
+ aProperty.Value <<= eOrient;
+ aSortSequence.getArray()[nOldSize] = aProperty;
+ ScSortParam aParam;
+ ScSortDescriptor::FillSortParam(aParam, aSortSequence);
+
+ SCCOLROW nStartPos = aParam.bByRow ? maRange.aStart.Col() : maRange.aStart.Row();
+ for (size_t i = 0; i < aParam.GetSortKeyCount(); ++i)
+ {
+ if (!aParam.maKeyState[i].bDoSort)
+ break;
+ aParam.maKeyState[i].nField += nStartPos;
+ }
+
+ pData->SetSortParam(aParam);
+ }
+
+ if (bContainsSubTotal)
+ {
+ ScSubTotalParam aParam;
+ aParam.bIncludePattern = bSubTotalsBindFormatsToContent;
+ aParam.bUserDef = bSubTotalsEnabledUserList;
+ aParam.nUserIndex = nSubTotalsUserListIndex;
+ aParam.bPagebreak = bSubTotalsInsertPageBreaks;
+ aParam.bCaseSens = bSubTotalsIsCaseSensitive;
+ aParam.bDoSort = bSubTotalsSortGroups;
+ aParam.bAscending = bSubTotalsAscending;
+ size_t nPos = 0;
+ for (const auto& rSubTotalRule : aSubTotalRules)
+ {
+ if (nPos >= MAXSUBTOTAL)
+ break;
+
+ const uno::Sequence<sheet::SubTotalColumn>& rColumns = rSubTotalRule.aSubTotalColumns;
+ sal_Int32 nColCount = rColumns.getLength();
+ sal_Int16 nGroupColumn = rSubTotalRule.nSubTotalRuleGroupFieldNumber;
+ aParam.bGroupActive[nPos] = true;
+ aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn);
+
+ SCCOL nCount = static_cast<SCCOL>(nColCount);
+ aParam.nSubTotals[nPos] = nCount;
+ if (nCount != 0)
+ {
+ aParam.pSubTotals[nPos].reset(new SCCOL[nCount]);
+ aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]);
+
+ const sheet::SubTotalColumn* pAry = rColumns.getConstArray();
+ for (SCCOL i = 0; i < nCount; ++i)
+ {
+ aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column);
+ aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function));
+ }
+ }
+ else
+ {
+ aParam.pSubTotals[nPos].reset();
+ aParam.pFunctions[nPos].reset();
+ }
+ ++nPos;
+ }
+
+ pData->SetSubTotalParam(aParam);
+ }
+
+ if (pData->HasImportParam() && !pData->HasImportSelection())
+ {
+ pData->SetRefreshDelay(nRefresh);
+ pData->SetRefreshHandler(pDoc->GetDBCollection()->GetRefreshHandler());
+ pData->SetRefreshControl(&pDoc->GetRefreshTimerControlAddress());
+ }
+
+ return pData;
+}
+
+namespace {
+
+bool setAutoFilterFlags(ScDocument& rDoc, const ScDBData& rData)
+{
+ if (!rData.HasAutoFilter())
+ return false;
+
+ // Set autofilter flags so that the buttons get displayed.
+ ScRange aRange;
+ rData.GetArea(aRange);
+ rDoc.ApplyFlagsTab(
+ aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), ScMF::Auto);
+ return false;
+}
+
+}
+
+void SAL_CALL ScXMLDatabaseRangeContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (!pDoc)
+ return;
+
+ if (meRangeType == ScDBCollection::SheetAnonymous)
+ {
+ ::std::unique_ptr<ScDBData> pData(ConvertToDBData(STR_DB_LOCAL_NONAME));
+
+ if (pData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+
+ setAutoFilterFlags(*pDoc, *pData);
+ pDoc->SetAnonymousDBData(aRange.aStart.Tab(), std::move(pData));
+ }
+ return;
+ }
+ else if (meRangeType == ScDBCollection::GlobalAnonymous)
+ {
+ ::std::unique_ptr<ScDBData> pData(ConvertToDBData(STR_DB_GLOBAL_NONAME));
+
+ if (pData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+
+ if (setAutoFilterFlags(*pDoc, *pData))
+ pDoc->SetAnonymousDBData(aRange.aStart.Tab(), std::move(pData));
+ else
+ pDoc->GetDBCollection()->getAnonDBs().insert(pData.release());
+ }
+ return;
+ }
+ else if (meRangeType == ScDBCollection::GlobalNamed)
+ {
+ ::std::unique_ptr<ScDBData> pData(ConvertToDBData(sDatabaseRangeName));
+
+ if (pData)
+ {
+ setAutoFilterFlags(*pDoc, *pData);
+ (void)pDoc->GetDBCollection()->getNamedDBs().insert(std::move(pData));
+ }
+ }
+}
+
+ScXMLSourceSQLContext::ScXMLSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ sDBName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_SQL_STATEMENT ):
+ pDatabaseRangeContext->SetSourceObject(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_PARSE_SQL_STATEMENT ):
+ pDatabaseRangeContext->SetNative(IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+ }
+ pDatabaseRangeContext->SetSourceType(sheet::DataImportMode_SQL);
+}
+
+ScXMLSourceSQLContext::~ScXMLSourceSQLContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceSQLContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( FORM, XML_CONNECTION_RESOURCE ) && sDBName.isEmpty() )
+ {
+ pContext = new ScXMLConResContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSourceSQLContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sDBName.isEmpty())
+ pDatabaseRangeContext->SetDatabaseName(sDBName);
+}
+
+ScXMLSourceTableContext::ScXMLSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ sDBName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_NAME ):
+ case XML_ELEMENT( TABLE, XML_DATABASE_TABLE_NAME ):
+ pDatabaseRangeContext->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+ }
+ pDatabaseRangeContext->SetSourceType(sheet::DataImportMode_TABLE);
+}
+
+ScXMLSourceTableContext::~ScXMLSourceTableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceTableContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( FORM, XML_CONNECTION_RESOURCE ) && sDBName.isEmpty() )
+ {
+ pContext = new ScXMLConResContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSourceTableContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sDBName.isEmpty())
+ pDatabaseRangeContext->SetDatabaseName(sDBName);
+}
+
+ScXMLSourceQueryContext::ScXMLSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ sDBName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_QUERY_NAME ):
+ pDatabaseRangeContext->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+ }
+ pDatabaseRangeContext->SetSourceType(sheet::DataImportMode_QUERY);
+}
+
+ScXMLSourceQueryContext::~ScXMLSourceQueryContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceQueryContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( FORM, XML_CONNECTION_RESOURCE ) && sDBName.isEmpty() )
+ {
+ pContext = new ScXMLConResContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSourceQueryContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sDBName.isEmpty())
+ pDatabaseRangeContext->SetDatabaseName(sDBName);
+}
+
+ScXMLConResContext::ScXMLConResContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext) :
+ ScXMLImportContext( rImport )
+{
+ OUString sConRes;
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( XLINK, XML_HREF ) ) );
+ if (aIter != rAttrList->end())
+ sConRes = aIter.toString();
+ }
+ if (!sConRes.isEmpty())
+ pDatabaseRangeContext->SetConnectionResource(sConRes);
+}
+
+ScXMLConResContext::~ScXMLConResContext()
+{
+}
+
+ScXMLSubTotalRulesContext::ScXMLSubTotalRulesContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_BIND_STYLES_TO_CONTENT ):
+ pDatabaseRangeContext->SetSubTotalsBindFormatsToContent(IsXMLToken(aIter, XML_TRUE));
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ pDatabaseRangeContext->SetSubTotalsIsCaseSensitive(IsXMLToken(aIter, XML_TRUE));
+ break;
+ case XML_ELEMENT( TABLE, XML_PAGE_BREAKS_ON_GROUP_CHANGE ):
+ pDatabaseRangeContext->SetSubTotalsInsertPageBreaks(IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+}
+
+ScXMLSubTotalRulesContext::~ScXMLSubTotalRulesContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSubTotalRulesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_SORT_GROUPS ):
+ {
+ pContext = new ScXMLSortGroupsContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUBTOTAL_RULE ):
+ {
+ pContext = new ScXMLSubTotalRuleContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLSortGroupsContext::ScXMLSortGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext) :
+ ScXMLImportContext( rImport )
+{
+ pDatabaseRangeContext->SetSubTotalsSortGroups(true);
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ const OUString &sValue = aIter.toString();
+ if (sValue.getLength() > 8)
+ {
+ std::u16string_view sTemp = sValue.subView(0, 8);
+ if (sTemp == u"UserList")
+ {
+ pDatabaseRangeContext->SetSubTotalsEnabledUserList(true);
+ sTemp = sValue.subView(8);
+ pDatabaseRangeContext->SetSubTotalsUserListIndex(static_cast<sal_Int16>(o3tl::toInt32(sTemp)));
+ }
+ else
+ {
+ //if (IsXMLToken(aIter, XML_AUTOMATIC))
+ //aSortField.FieldType = util::SortFieldType_AUTOMATIC;
+ // is not supported by StarOffice
+ }
+ }
+ else
+ {
+ //if (IsXMLToken(aIter, XML_TEXT))
+ //aSortField.FieldType = util::SortFieldType_ALPHANUMERIC;
+ // is not supported by StarOffice
+ //else if (IsXMLToken(aIter, XML_NUMBER))
+ //aSortField.FieldType = util::SortFieldType_NUMERIC;
+ // is not supported by StarOffice
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ORDER ):
+ {
+ if (IsXMLToken(aIter, XML_ASCENDING))
+ pDatabaseRangeContext->SetSubTotalsAscending(true);
+ else
+ pDatabaseRangeContext->SetSubTotalsAscending(false);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSortGroupsContext::~ScXMLSortGroupsContext()
+{
+}
+
+ScXMLSubTotalRuleContext::ScXMLSubTotalRuleContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_GROUP_BY_FIELD_NUMBER ):
+ aSubTotalRule.nSubTotalRuleGroupFieldNumber = static_cast<sal_Int16>(aIter.toInt32());
+ break;
+ }
+ }
+ }
+}
+
+ScXMLSubTotalRuleContext::~ScXMLSubTotalRuleContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSubTotalRuleContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_SUBTOTAL_FIELD ):
+ {
+ pContext = new ScXMLSubTotalFieldContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSubTotalRuleContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (pDatabaseRangeContext)
+ pDatabaseRangeContext->AddSubTotalRule(aSubTotalRule);
+}
+
+ScXMLSubTotalFieldContext::ScXMLSubTotalFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSubTotalRuleContext* pTempSubTotalRuleContext) :
+ ScXMLImportContext( rImport ),
+ pSubTotalRuleContext(pTempSubTotalRuleContext)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ sFieldNumber = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ sFunction = aIter.toString();
+ break;
+ }
+ }
+}
+
+ScXMLSubTotalFieldContext::~ScXMLSubTotalFieldContext()
+{
+}
+
+void SAL_CALL ScXMLSubTotalFieldContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ sheet::SubTotalColumn aSubTotalColumn;
+ aSubTotalColumn.Column = sFieldNumber.toInt32();
+ aSubTotalColumn.Function = ScXMLConverter::GetFunctionFromString( sFunction );
+ pSubTotalRuleContext->AddSubTotalColumn(aSubTotalColumn);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldrani.hxx b/sc/source/filter/xml/xmldrani.hxx
new file mode 100644
index 0000000000..a35074117c
--- /dev/null
+++ b/sc/source/filter/xml/xmldrani.hxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/SubTotalColumn.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <dbdata.hxx>
+#include "importcontext.hxx"
+
+#include <memory>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+struct ScQueryParam;
+
+class ScXMLDatabaseRangesContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDatabaseRangesContext( ScXMLImport& rImport );
+
+ virtual ~ScXMLDatabaseRangesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+struct ScSubTotalRule
+{
+ sal_Int16 nSubTotalRuleGroupFieldNumber;
+ css::uno::Sequence <css::sheet::SubTotalColumn> aSubTotalColumns;
+};
+
+class ScXMLDatabaseRangeContext : public ScXMLImportContext
+{
+ std::unique_ptr<ScQueryParam> mpQueryParam;
+ ScRange maRange;
+ OUString sDatabaseRangeName;
+ OUString sConnectionResource;
+ OUString sDatabaseName;
+ OUString sSourceObject;
+ css::uno::Sequence <css::beans::PropertyValue> aSortSequence;
+ std::vector < ScSubTotalRule > aSubTotalRules;
+ ScRange aFilterConditionSourceRangeAddress;
+ css::sheet::DataImportMode nSourceType;
+ sal_Int32 nRefresh;
+ sal_Int16 nSubTotalsUserListIndex;
+ bool mbValidRange;
+ bool bContainsSort;
+ bool bContainsSubTotal;
+ bool bNative;
+ bool bIsSelection; // TODO: import to document core
+ bool bKeepFormats;
+ bool bMoveCells;
+ bool bStripData;
+ bool bAutoFilter;
+ bool bSubTotalsBindFormatsToContent;
+ bool bSubTotalsIsCaseSensitive;
+ bool bSubTotalsInsertPageBreaks;
+ bool bSubTotalsSortGroups;
+ bool bSubTotalsEnabledUserList;
+ bool bSubTotalsAscending;
+ bool bFilterConditionSourceRange;
+ bool bHasHeader;
+ bool bByRow;
+ ScDBCollection::RangeType meRangeType;
+
+ std::unique_ptr<ScDBData> ConvertToDBData(const OUString& rName);
+
+public:
+
+ ScXMLDatabaseRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLDatabaseRangeContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetDatabaseName(const OUString& sTempDatabaseName) { sDatabaseName = sTempDatabaseName; }
+ void SetConnectionResource(const OUString& sTempConRes) { sConnectionResource = sTempConRes; }
+ void SetSourceObject(const OUString& sTempSourceObject) { sSourceObject = sTempSourceObject; }
+ void SetSourceType(const css::sheet::DataImportMode nTempSourceType) { nSourceType = nTempSourceType; }
+ void SetNative(const bool bTempNative) { bNative = bTempNative; }
+ void SetSubTotalsBindFormatsToContent(const bool bTemp ) { bSubTotalsBindFormatsToContent = bTemp; }
+ void SetSubTotalsIsCaseSensitive(const bool bTemp) { bSubTotalsIsCaseSensitive = bTemp; }
+ void SetSubTotalsInsertPageBreaks(const bool bTemp) { bSubTotalsInsertPageBreaks = bTemp; }
+ void SetSubTotalsEnabledUserList(const bool bTemp) { bSubTotalsEnabledUserList = bTemp; }
+ void SetSubTotalsUserListIndex(const sal_Int16 nTemp) { nSubTotalsUserListIndex = nTemp; }
+ void SetSubTotalsAscending(const bool bTemp) { bSubTotalsAscending = bTemp; }
+ void SetSubTotalsSortGroups(const bool bTemp) { bSubTotalsSortGroups = bTemp; }
+ void AddSubTotalRule(const ScSubTotalRule& rRule) { aSubTotalRules.push_back(rRule); }
+ void SetSortSequence(const css::uno::Sequence <css::beans::PropertyValue>& aTempSortSequence) { aSortSequence = aTempSortSequence; }
+ void SetFilterConditionSourceRangeAddress(const ScRange& aRange) { aFilterConditionSourceRangeAddress = aRange;
+ bFilterConditionSourceRange = true; }
+};
+
+class ScXMLSourceSQLContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ OUString sDBName;
+
+public:
+
+ ScXMLSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSourceSQLContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLSourceTableContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ OUString sDBName;
+
+public:
+
+ ScXMLSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSourceTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLSourceQueryContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ OUString sDBName;
+
+public:
+
+ ScXMLSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSourceQueryContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLConResContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLConResContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLConResContext() override;
+};
+
+class ScXMLSubTotalRulesContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+
+public:
+
+ ScXMLSubTotalRulesContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSubTotalRulesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLSortGroupsContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLSortGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSortGroupsContext() override;
+};
+
+class ScXMLSubTotalRuleContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ ScSubTotalRule aSubTotalRule;
+
+public:
+
+ ScXMLSubTotalRuleContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSubTotalRuleContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void AddSubTotalColumn(const css::sheet::SubTotalColumn& rSubTotalColumn)
+ {
+ aSubTotalRule.aSubTotalColumns.realloc(aSubTotalRule.aSubTotalColumns.getLength() + 1);
+ aSubTotalRule.aSubTotalColumns.getArray()[aSubTotalRule.aSubTotalColumns.getLength() - 1] = rSubTotalColumn;
+ }
+};
+
+class ScXMLSubTotalFieldContext : public ScXMLImportContext
+{
+ ScXMLSubTotalRuleContext* pSubTotalRuleContext;
+ OUString sFieldNumber;
+ OUString sFunction;
+
+public:
+
+ ScXMLSubTotalFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSubTotalRuleContext* pSubTotalRuleContext);
+
+ virtual ~ScXMLSubTotalFieldContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx
new file mode 100644
index 0000000000..9da5da65b7
--- /dev/null
+++ b/sc/source/filter/xml/xmlexprt.cxx
@@ -0,0 +1,5488 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 "xmlexprt.hxx"
+#include "XMLConverter.hxx"
+#include "xmlstyle.hxx"
+#include <unonames.hxx>
+#include <document.hxx>
+#include <olinetab.hxx>
+#include <formulacell.hxx>
+#include <rangenam.hxx>
+#include "XMLTableMasterPageExport.hxx"
+#include <drwlayer.hxx>
+#include "XMLExportDataPilot.hxx"
+#include "XMLExportDatabaseRanges.hxx"
+#include "XMLExportDDELinks.hxx"
+#include "XMLExportIterator.hxx"
+#include "XMLColumnRowGroupExport.hxx"
+#include "XMLStylesExportHelper.hxx"
+#include "XMLChangeTrackingExportHelper.hxx"
+#include <sheetdata.hxx>
+#include <docoptio.hxx>
+#include "XMLExportSharedData.hxx"
+#include <chgviset.hxx>
+#include <docuno.hxx>
+#include <textuno.hxx>
+#include <chartlis.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+#include <userdat.hxx>
+#include <chgtrack.hxx>
+#include <rangeutl.hxx>
+#include <postit.hxx>
+#include <externalrefmgr.hxx>
+#include <editutil.hxx>
+#include <tabprotection.hxx>
+#include "cachedattraccess.hxx"
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <cellvalue.hxx>
+#include <stylehelper.hxx>
+#include <edittextiterator.hxx>
+#include "editattributemap.hxx"
+#include <arealink.hxx>
+#include <datastream.hxx>
+#include <documentlinkmgr.hxx>
+#include <tokenstringcontext.hxx>
+#include <cellform.hxx>
+#include <datamapper.hxx>
+#include <datatransformation.hxx>
+#include "SparklineGroupsExport.hxx"
+#include <SparklineList.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/numehelp.hxx>
+#include <xmloff/txtparae.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/section.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <formula/errorcodes.hxx>
+#include <xmloff/xmlerror.hxx>
+#include <xmloff/XMLEventExport.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <xmloff/table/XMLTableExport.hxx>
+
+#include <sax/tools/converter.hxx>
+#include <tools/fldunit.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <tools/color.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/extract.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdmodel.hxx>
+#include <vcl/svapp.hxx>
+#include <docmodel/theme/Theme.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/form/XFormsSupplier2.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/sheet/XUsedAreaCursor.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <com/sun/star/sheet/XUniqueCellFormatRangesSupplier.hpp>
+#include <com/sun/star/sheet/XLabelRange.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/sheet/XSheetCellCursor.hpp>
+#include <com/sun/star/sheet/XSheetCellRanges.hpp>
+#include <com/sun/star/sheet/XSheetLinkable.hpp>
+#include <com/sun/star/sheet/GlobalSheetSettings.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/util/XProtectable.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+
+#include "XMLCodeNameProvider.hxx"
+
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <memory>
+#include <vector>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <officecfg/Office/Common.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+
+
+//! not found in unonames.hxx
+constexpr OUString SC_LAYERID = u"LayerID"_ustr;
+
+#define SC_VIEWCHANGES_COUNT 13
+#define SC_SHOW_CHANGES 0
+#define SC_SHOW_ACCEPTED_CHANGES 1
+#define SC_SHOW_REJECTED_CHANGES 2
+#define SC_SHOW_CHANGES_BY_DATETIME 3
+#define SC_SHOW_CHANGES_BY_DATETIME_MODE 4
+#define SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME 5
+#define SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME 6
+#define SC_SHOW_CHANGES_BY_AUTHOR 7
+#define SC_SHOW_CHANGES_BY_AUTHOR_NAME 8
+#define SC_SHOW_CHANGES_BY_COMMENT 9
+#define SC_SHOW_CHANGES_BY_COMMENT_TEXT 10
+#define SC_SHOW_CHANGES_BY_RANGES 11
+#define SC_SHOW_CHANGES_BY_RANGES_LIST 12
+
+using namespace formula;
+using namespace com::sun::star;
+using namespace xmloff::token;
+using ::std::vector;
+using ::com::sun::star::uno::UNO_QUERY;
+
+namespace
+{
+OUString lcl_RangeSequenceToString(
+ const uno::Sequence< OUString > & rRanges,
+ const uno::Reference< chart2::data::XRangeXMLConversion > & xFormatConverter )
+{
+ OUStringBuffer aResult;
+ const sal_Int32 nMaxIndex( rRanges.getLength() - 1 );
+ const sal_Unicode cSep(' ');
+ for( sal_Int32 i=0; i<=nMaxIndex; ++i )
+ {
+ OUString aRange( rRanges[i] );
+ if( xFormatConverter.is())
+ aRange = xFormatConverter->convertRangeToXML( aRange );
+ aResult.append( aRange );
+ if( i < nMaxIndex )
+ aResult.append( cSep );
+ }
+ return aResult.makeStringAndClear();
+}
+
+OUString lcl_GetFormattedString(ScDocument* pDoc, const ScRefCellValue& rCell, const ScAddress& rAddr)
+{
+ // return text/edit cell string content, with line feeds in edit cells
+
+ if (!pDoc)
+ return OUString();
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ const Color* pColor;
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+
+ sal_uInt32 nFormat = pDoc->GetNumberFormat(rAddr);
+ return ScCellFormat::GetString(rCell, nFormat, &pColor, *pFormatter, *pDoc);
+ }
+ case CELLTYPE_EDIT:
+ {
+ const EditTextObject* pData = rCell.getEditText();
+ if (!pData)
+ return OUString();
+
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(*pData);
+ return rEngine.GetText();
+ }
+ break;
+ default:
+ ;
+ }
+
+ return OUString();
+}
+
+} // anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLExporter", SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLMetaExporter", SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLStylesExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLStylesExporter", SvXMLExportFlags::STYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLContentExporter", SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::CONTENT|SvXMLExportFlags::SCRIPTS|SvXMLExportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisExporter", SvXMLExportFlags::ALL|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisMetaExporter", SvXMLExportFlags::META|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisStylesExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisStylesExporter", SvXMLExportFlags::STYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisContentExporter", SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::CONTENT|SvXMLExportFlags::SCRIPTS|SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisSettingsExporter", SvXMLExportFlags::SETTINGS|SvXMLExportFlags::OASIS));
+}
+
+namespace {
+
+class ScXMLShapeExport : public XMLShapeExport
+{
+public:
+ explicit ScXMLShapeExport(SvXMLExport& rExp)
+ : XMLShapeExport(rExp,
+ // chain text attributes
+ XMLTextParagraphExport::CreateParaExtPropMapper(rExp))
+ {
+ }
+
+ /** is called before a shape element for the given XShape is exported */
+ virtual void onExport( const uno::Reference < drawing::XShape >& xShape ) override;
+};
+
+}
+
+void ScXMLShapeExport::onExport( const uno::Reference < drawing::XShape >& xShape )
+{
+ uno::Reference< beans::XPropertySet > xShapeProp( xShape, uno::UNO_QUERY );
+ if( xShapeProp.is() )
+ {
+ sal_Int16 nLayerID = 0;
+ if( (xShapeProp->getPropertyValue( SC_LAYERID ) >>= nLayerID) && (SdrLayerID(nLayerID) == SC_LAYER_BACK) )
+ GetExport().AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_BACKGROUND, XML_TRUE);
+ }
+}
+
+sal_Int16 ScXMLExport::GetMeasureUnit()
+{
+ css::uno::Reference<css::sheet::XGlobalSheetSettings> xProperties =
+ css::sheet::GlobalSheetSettings::create( comphelper::getProcessComponentContext() );
+ const FieldUnit eFieldUnit = static_cast<FieldUnit>(xProperties->getMetric());
+ return SvXMLUnitConverter::GetMeasureUnit(eFieldUnit);
+}
+
+ScXMLExport::ScXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlag)
+: SvXMLExport(
+ rContext, implementationName, GetMeasureUnit(), XML_SPREADSHEET, nExportFlag ),
+ pDoc(nullptr),
+ nSourceStreamPos(0),
+ pCurrentCell(nullptr),
+ nOpenRow(-1),
+ nProgressCount(0),
+ nCurrentTable(0),
+ bHasRowHeader(false),
+ bRowHeaderOpen(false)
+{
+ if (getExportFlags() & SvXMLExportFlags::CONTENT)
+ {
+ pGroupColumns.reset( new ScMyOpenCloseColumnRowGroup(*this, XML_TABLE_COLUMN_GROUP) );
+ pGroupRows.reset( new ScMyOpenCloseColumnRowGroup(*this, XML_TABLE_ROW_GROUP) );
+ pColumnStyles.reset( new ScColumnStyles() );
+ pRowStyles.reset( new ScRowStyles() );
+ pRowFormatRanges.reset( new ScRowFormatRanges() );
+ pMergedRangesContainer.reset( new ScMyMergedRangesContainer() );
+ pValidationsContainer.reset( new ScMyValidationsContainer() );
+ mpCellsItr.reset(new ScMyNotEmptyCellsIterator(*this));
+ pDefaults.reset( new ScMyDefaultStyles );
+ }
+ pCellStyles.reset( new ScFormatRangeStyles() );
+
+ // document is not set here - create ScChangeTrackingExportHelper later
+
+ xScPropHdlFactory = new XMLScPropHdlFactory;
+ xCellStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScCellStylesProperties, xScPropHdlFactory, true);
+ xColumnStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScColumnStylesProperties, xScPropHdlFactory, true);
+ xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScRowStylesProperties, xScPropHdlFactory, true);
+ xTableStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScTableStylesProperties, xScPropHdlFactory, true);
+ xCellStylesExportPropertySetMapper = new ScXMLCellExportPropertyMapper(xCellStylesPropertySetMapper);
+ xCellStylesExportPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaExtPropMapper(*this));
+ xColumnStylesExportPropertySetMapper = new ScXMLColumnExportPropertyMapper(xColumnStylesPropertySetMapper);
+ xRowStylesExportPropertySetMapper = new ScXMLRowExportPropertyMapper(xRowStylesPropertySetMapper);
+ xTableStylesExportPropertySetMapper = new ScXMLTableExportPropertyMapper(xTableStylesPropertySetMapper);
+
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_CELL, XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME,
+ xCellStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX);
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_COLUMN, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_NAME,
+ xColumnStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX);
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_ROW, XML_STYLE_FAMILY_TABLE_ROW_STYLES_NAME,
+ xRowStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX);
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_TABLE, XML_STYLE_FAMILY_TABLE_TABLE_STYLES_NAME,
+ xTableStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_TABLE_STYLES_PREFIX);
+
+ GetShapeExport(); // make sure the graphics styles family is added
+
+ if( !(getExportFlags() & (SvXMLExportFlags::STYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) )
+ return;
+
+ // This name is reserved for the external ref cache tables. This
+ // should not conflict with user-defined styles since this name is
+ // used for a table style which is not available in the UI.
+ sExternalRefTabStyleName = "ta_extref";
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_TABLE, sExternalRefTabStyleName);
+
+ sAttrName = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_NAME));
+ sAttrStyleName = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_STYLE_NAME));
+ sAttrColumnsRepeated = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_NUMBER_COLUMNS_REPEATED));
+ sAttrFormula = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_FORMULA));
+ sAttrStringValue = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_OFFICE, GetXMLToken(XML_STRING_VALUE));
+ sAttrValueType = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_OFFICE, GetXMLToken(XML_VALUE_TYPE));
+ sElemCell = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_CELL));
+ sElemCoveredCell = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_COVERED_TABLE_CELL));
+ sElemCol = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_COLUMN));
+ sElemRow = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_ROW));
+ sElemTab = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE));
+ sElemP = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
+}
+
+ScXMLExport::~ScXMLExport()
+{
+ pGroupColumns.reset();
+ pGroupRows.reset();
+ pColumnStyles.reset();
+ pRowStyles.reset();
+ pCellStyles.reset();
+ pRowFormatRanges.reset();
+ pMergedRangesContainer.reset();
+ pValidationsContainer.reset();
+ pChangeTrackingExportHelper.reset();
+ pDefaults.reset();
+ pNumberFormatAttributesExportHelper.reset();
+}
+
+void ScXMLExport::SetSourceStream( const uno::Reference<io::XInputStream>& xNewStream )
+{
+ xSourceStream = xNewStream;
+
+ if ( !xSourceStream.is() )
+ return;
+
+ // make sure it's a plain UTF-8 stream as written by OOo itself
+
+ const char pXmlHeader[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ sal_Int32 nLen = strlen(pXmlHeader);
+
+ uno::Sequence<sal_Int8> aFileStart(nLen);
+ sal_Int32 nRead = xSourceStream->readBytes( aFileStart, nLen );
+
+ if ( nRead != nLen || memcmp( aFileStart.getConstArray(), pXmlHeader, nLen ) != 0 )
+ {
+ // invalid - ignore stream, save normally
+ xSourceStream.clear();
+ }
+ else
+ {
+ // keep track of the bytes already read
+ nSourceStreamPos = nRead;
+
+ const ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetSheetSaveData();
+ if (pSheetData)
+ {
+ // add the loaded namespaces to the name space map
+
+ if ( !pSheetData->AddLoadedNamespaces( GetNamespaceMap_() ) )
+ {
+ // conflicts in the namespaces - ignore the stream, save normally
+ xSourceStream.clear();
+ }
+ }
+ }
+}
+
+sal_Int32 ScXMLExport::GetNumberFormatStyleIndex(sal_Int32 nNumFmt) const
+{
+ NumberFormatIndexMap::const_iterator itr = aNumFmtIndexMap.find(nNumFmt);
+ if (itr == aNumFmtIndexMap.end())
+ return -1;
+
+ return itr->second;
+}
+
+void ScXMLExport::CollectSharedData(SCTAB& nTableCount, sal_Int32& nShapesCount)
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc(GetModel(), uno::UNO_QUERY);
+ if (!xSpreadDoc.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex(xSpreadDoc->getSheets(), uno::UNO_QUERY);
+ if (!xIndex.is())
+ return;
+
+ nTableCount = xIndex->getCount();
+ if (!pSharedData)
+ pSharedData.reset(new ScMySharedData(nTableCount));
+
+ pCellStyles->AddNewTable(nTableCount - 1);
+
+ for (SCTAB nTable = 0; nTable < nTableCount; ++nTable)
+ {
+ nCurrentTable = sal::static_int_cast<sal_uInt16>(nTable);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ if (!xDrawPageSupplier.is())
+ continue;
+
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage());
+ ScMyDrawPage aDrawPage;
+ aDrawPage.bHasForms = false;
+ aDrawPage.xDrawPage.set(xDrawPage);
+ pSharedData->AddDrawPage(aDrawPage, nTable);
+ if (!xDrawPage.is())
+ continue;
+
+ sal_Int32 nShapes = xDrawPage->getCount();
+ for (sal_Int32 nShape = 0; nShape < nShapes; ++nShape)
+ {
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShape), uno::UNO_QUERY);
+ if (!xShape.is())
+ continue;
+
+ uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
+ if (!xShapeProp.is())
+ continue;
+
+ sal_Int16 nLayerID = 0;
+ bool bExtracted = xShapeProp->getPropertyValue(SC_LAYERID) >>= nLayerID;
+ if (!bExtracted)
+ continue;
+
+ if ((SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN))
+ {
+ CollectInternalShape(xShape);
+ continue;
+ }
+
+ ++nShapesCount;
+
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
+ if (!pSdrObj)
+ continue;
+
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetNonRotatedObjData(pSdrObj))
+ {
+ ScMyShape aMyShape;
+ aMyShape.aAddress = pAnchor->maStart;
+ SAL_WARN_IF(aMyShape.aAddress.Tab() != nTable, "sc", "not anchored to current sheet!");
+ aMyShape.aAddress.SetTab(nTable);
+ aMyShape.aEndAddress = pAnchor->maEnd;
+ aMyShape.aEndAddress.SetTab( nTable );
+ aMyShape.nEndX = pAnchor->maEndOffset.X();
+ aMyShape.nEndY = pAnchor->maEndOffset.Y();
+ aMyShape.xShape = xShape;
+ pSharedData->AddNewShape(aMyShape);
+ pSharedData->SetLastColumn(nTable, pAnchor->maStart.Col());
+ pSharedData->SetLastRow(nTable, pAnchor->maStart.Row());
+ }
+ else
+ pSharedData->AddTableShape(nTable, xShape);
+ }
+ }
+}
+
+void ScXMLExport::CollectShapesAutoStyles(SCTAB nTableCount)
+{
+ // #i84077# To avoid compiler warnings about uninitialized aShapeItr,
+ // it's initialized using this dummy list. The iterator contains shapes
+ // from all sheets, so it can't be declared inside the nTable loop where
+ // it is used.
+ ScMyShapeList aDummyInitList;
+
+ pSharedData->SortShapesContainer();
+ pSharedData->SortNoteShapes();
+ const ScMyShapeList* pShapeList(nullptr);
+ ScMyShapeList::const_iterator aShapeItr = aDummyInitList.end();
+ if (pSharedData->GetShapesContainer())
+ {
+ pShapeList = &pSharedData->GetShapesContainer()->GetShapes();
+ aShapeItr = pShapeList->begin();
+ }
+ if (pSharedData->HasDrawPage())
+ {
+ for (SCTAB nTable = 0; nTable < nTableCount; ++nTable)
+ {
+ uno::Reference<drawing::XDrawPage> xDrawPage(pSharedData->GetDrawPage(nTable));
+
+ if (xDrawPage.is())
+ {
+ GetShapeExport()->seekShapes(xDrawPage);
+ uno::Reference< form::XFormsSupplier2 > xFormsSupplier( xDrawPage, uno::UNO_QUERY );
+ if( xFormsSupplier.is() && xFormsSupplier->hasForms() )
+ {
+ GetFormExport()->examineForms(xDrawPage);
+ pSharedData->SetDrawPageHasForms(nTable, true);
+ }
+ ScMyTableShapes* pTableShapes(pSharedData->GetTableShapes());
+ if (pTableShapes)
+ {
+ for (const auto& rxShape : (*pTableShapes)[nTable])
+ {
+ GetShapeExport()->collectShapeAutoStyles(rxShape);
+ IncrementProgressBar(false);
+ }
+ }
+ if (pShapeList)
+ {
+ ScMyShapeList::const_iterator aEndItr(pShapeList->end());
+ while ( aShapeItr != aEndItr && ( aShapeItr->aAddress.Tab() == nTable ) )
+ {
+ GetShapeExport()->collectShapeAutoStyles(aShapeItr->xShape);
+ IncrementProgressBar(false);
+ ++aShapeItr;
+ }
+ }
+ if (pSharedData->GetNoteShapes())
+ {
+ const ScMyNoteShapeList& rNoteShapes = pSharedData->GetNoteShapes()->GetNotes();
+ for (const auto& rNoteShape : rNoteShapes)
+ {
+ if ( rNoteShape.aPos.Tab() == nTable )
+ GetShapeExport()->collectShapeAutoStyles(rNoteShape.xShape);
+ }
+ }
+ }
+ }
+ }
+ pSharedData->SortNoteShapes(); // sort twice, because some more shapes are added
+}
+
+void ScXMLExport::ExportMeta_()
+{
+ sal_Int32 nCellCount(pDoc ? pDoc->GetCellCount() : 0);
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ GetAutoStylePool()->ClearEntries();
+ CollectSharedData(nTableCount, nShapesCount);
+
+ uno::Sequence<beans::NamedValue> stats
+ {
+ { "TableCount", uno::Any(static_cast<sal_Int32>(nTableCount)) },
+ { "CellCount", uno::Any(nCellCount) },
+ { "ObjectCount", uno::Any(nShapesCount) }
+ };
+
+ // update document statistics at the model
+ uno::Reference<document::XDocumentPropertiesSupplier> xPropSup(GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xPropSup->getDocumentProperties());
+ if (xDocProps.is()) {
+ xDocProps->setDocumentStatistics(stats);
+ }
+
+ // export document properties
+ SvXMLExport::ExportMeta_();
+}
+
+void ScXMLExport::ExportFontDecls_()
+{
+ GetFontAutoStylePool(); // make sure the pool is created
+ SvXMLExport::ExportFontDecls_();
+}
+
+table::CellRangeAddress ScXMLExport::GetEndAddress(const uno::Reference<sheet::XSpreadsheet>& xTable)
+{
+ table::CellRangeAddress aCellAddress;
+ uno::Reference<sheet::XSheetCellCursor> xCursor(xTable->createCursor());
+ uno::Reference<sheet::XUsedAreaCursor> xUsedArea (xCursor, uno::UNO_QUERY);
+ uno::Reference<sheet::XCellRangeAddressable> xCellAddress (xCursor, uno::UNO_QUERY);
+ if (xUsedArea.is() && xCellAddress.is())
+ {
+ xUsedArea->gotoEndOfUsedArea(true);
+ aCellAddress = xCellAddress->getRangeAddress();
+ }
+ return aCellAddress;
+}
+
+void ScXMLExport::GetAreaLinks( ScMyAreaLinksContainer& rAreaLinks )
+{
+ if (pDoc->GetLinkManager())
+ {
+ const sfx2::SvBaseLinks& rLinks = pDoc->GetLinkManager()->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ ScAreaLink *pLink = dynamic_cast<ScAreaLink*>(rLink.get());
+ if (pLink)
+ {
+ ScMyAreaLink aAreaLink;
+ aAreaLink.aDestRange = pLink->GetDestArea();
+ aAreaLink.sSourceStr = pLink->GetSource();
+ aAreaLink.sFilter = pLink->GetFilter();
+ aAreaLink.sFilterOptions = pLink->GetOptions();
+ aAreaLink.sURL = pLink->GetFile();
+ aAreaLink.nRefreshDelaySeconds = pLink->GetRefreshDelaySeconds();
+ rAreaLinks.AddNewAreaLink( aAreaLink );
+ }
+ }
+ }
+ rAreaLinks.Sort();
+}
+
+// core implementation
+void ScXMLExport::GetDetectiveOpList( ScMyDetectiveOpContainer& rDetOp )
+{
+ if (!pDoc)
+ return;
+
+ ScDetOpList* pOpList(pDoc->GetDetOpList());
+ if( !pOpList )
+ return;
+
+ size_t nCount = pOpList->Count();
+ for (size_t nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ const ScDetOpData& rDetData = pOpList->GetObject( nIndex);
+ const ScAddress& rDetPos = rDetData.GetPos();
+ SCTAB nTab = rDetPos.Tab();
+ if ( nTab < pDoc->GetTableCount() )
+ {
+ rDetOp.AddOperation( rDetData.GetOperation(), rDetPos, static_cast<sal_uInt32>( nIndex) );
+
+ // cells with detective operations are written even if empty
+ pSharedData->SetLastColumn( nTab, rDetPos.Col() );
+ pSharedData->SetLastRow( nTab, rDetPos.Row() );
+ }
+ }
+ rDetOp.Sort();
+}
+
+void ScXMLExport::WriteSingleColumn(const sal_Int32 nRepeatColumns, const sal_Int32 nStyleIndex,
+ const sal_Int32 nIndex, const bool bIsAutoStyle, const bool bIsVisible)
+{
+ CheckAttrList();
+ // tdf#138466
+ if (nStyleIndex != -1)
+ AddAttribute(sAttrStyleName, pColumnStyles->GetStyleNameByIndex(nStyleIndex));
+ if (!bIsVisible)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_COLLAPSE);
+ if (nRepeatColumns > 1)
+ {
+ OUString sOUEndCol(OUString::number(nRepeatColumns));
+ AddAttribute(sAttrColumnsRepeated, sOUEndCol);
+ }
+ if (nIndex != -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DEFAULT_CELL_STYLE_NAME, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
+ SvXMLElementExport aElemC(*this, sElemCol, true, true);
+}
+
+void ScXMLExport::WriteColumn(const sal_Int32 nColumn, const sal_Int32 nRepeatColumns,
+ const sal_Int32 nStyleIndex, const bool bIsVisible)
+{
+ sal_Int32 nRepeat(1);
+ sal_Int32 nPrevIndex(pDefaults->GetColDefaults()[nColumn].nIndex);
+ bool bPrevAutoStyle(pDefaults->GetColDefaults()[nColumn].bIsAutoStyle);
+ for (sal_Int32 i = nColumn + 1; i < nColumn + nRepeatColumns; ++i)
+ {
+ if ((pDefaults->GetColDefaults()[i].nIndex != nPrevIndex) ||
+ (pDefaults->GetColDefaults()[i].bIsAutoStyle != bPrevAutoStyle))
+ {
+ WriteSingleColumn(nRepeat, nStyleIndex, nPrevIndex, bPrevAutoStyle, bIsVisible);
+ nPrevIndex = pDefaults->GetColDefaults()[i].nIndex;
+ bPrevAutoStyle = pDefaults->GetColDefaults()[i].bIsAutoStyle;
+ nRepeat = 1;
+ }
+ else
+ ++nRepeat;
+ }
+ WriteSingleColumn(nRepeat, nStyleIndex, nPrevIndex, bPrevAutoStyle, bIsVisible);
+}
+
+void ScXMLExport::OpenHeaderColumn()
+{
+ StartElement( XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true );
+}
+
+void ScXMLExport::CloseHeaderColumn()
+{
+ EndElement(XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true);
+}
+
+void ScXMLExport::ExportColumns(const sal_Int32 nTable, const ScRange& aColumnHeaderRange, const bool bHasColumnHeader)
+{
+ sal_Int32 nColsRepeated (1);
+ sal_Int32 nIndex;
+ sal_Int32 nPrevColumn(0);
+ bool bPrevIsVisible (true);
+ bool bWasHeader (false);
+ bool bIsClosed (true);
+ sal_Int32 nPrevIndex (-1);
+ sal_Int32 nColumn;
+ for (nColumn = 0; nColumn <= pSharedData->GetLastColumn(nTable); ++nColumn)
+ {
+ CheckAttrList();
+ bool bIsVisible(true);
+ nIndex = pColumnStyles->GetStyleNameIndex(nTable, nColumn, bIsVisible);
+
+ const bool bIsHeader = bHasColumnHeader && (aColumnHeaderRange.aStart.Col() <= nColumn) && (nColumn <= aColumnHeaderRange.aEnd.Col());
+ if (bIsHeader != bWasHeader)
+ {
+ if (bIsHeader)
+ {
+ if (nColumn > 0)
+ {
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ pGroupColumns->CloseGroups(nColumn - 1);
+ }
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ nPrevColumn = nColumn;
+ nColsRepeated = 1;
+ if(pGroupColumns->IsGroupStart(nColumn))
+ pGroupColumns->OpenGroups(nColumn);
+ OpenHeaderColumn();
+ bWasHeader = true;
+ bIsClosed = false;
+ }
+ else
+ {
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ CloseHeaderColumn();
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ pGroupColumns->CloseGroups(nColumn - 1);
+ if(pGroupColumns->IsGroupStart(nColumn))
+ pGroupColumns->OpenGroups(nColumn);
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ nPrevColumn = nColumn;
+ nColsRepeated = 1;
+ bWasHeader = false;
+ bIsClosed = true;
+ }
+ }
+ else if (nColumn == 0)
+ {
+ if (pGroupColumns->IsGroupStart(nColumn))
+ pGroupColumns->OpenGroups(nColumn);
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ }
+ else if ((bIsVisible == bPrevIsVisible) && (nIndex == nPrevIndex) &&
+ !pGroupColumns->IsGroupStart(nColumn) && !pGroupColumns->IsGroupEnd(nColumn - 1))
+ ++nColsRepeated;
+ else
+ {
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ {
+ if (bIsHeader)
+ CloseHeaderColumn();
+ pGroupColumns->CloseGroups(nColumn - 1);
+ if (bIsHeader)
+ OpenHeaderColumn();
+ }
+ if (pGroupColumns->IsGroupStart(nColumn))
+ {
+ if (bIsHeader)
+ CloseHeaderColumn();
+ pGroupColumns->OpenGroups(nColumn);
+ if (bIsHeader)
+ OpenHeaderColumn();
+ }
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ nPrevColumn = nColumn;
+ nColsRepeated = 1;
+ }
+ }
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ if (!bIsClosed)
+ CloseHeaderColumn();
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ pGroupColumns->CloseGroups(nColumn - 1);
+}
+
+void ScXMLExport::ExportExternalRefCacheStyles()
+{
+ sal_Int32 nEntryIndex = GetCellStylesPropertySetMapper()->FindEntryIndex(
+ "NumberFormat", XML_NAMESPACE_STYLE, u"data-style-name");
+
+ if (nEntryIndex < 0)
+ // No entry index for the number format is found.
+ return;
+
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ if (!pRefMgr->hasExternalData())
+ // No external reference data cached.
+ return;
+
+ // Export each unique number format used in the external ref cache.
+ vector<sal_uInt32> aNumFmts;
+ pRefMgr->getAllCachedNumberFormats(aNumFmts);
+ static constexpr OUString aDefaultStyle(u"Default"_ustr);
+ for (const auto& rNumFmt : aNumFmts)
+ {
+ sal_Int32 nNumFmt = static_cast<sal_Int32>(rNumFmt);
+
+ addDataStyle(nNumFmt);
+
+ uno::Any aVal;
+ aVal <<= nNumFmt;
+ vector<XMLPropertyState> aProps;
+ aVal <<= aDefaultStyle;
+ aProps.emplace_back(nEntryIndex, aVal);
+
+ OUString aName;
+ sal_Int32 nIndex;
+ if (GetAutoStylePool()->Add(aName, XmlStyleFamily::TABLE_CELL, aDefaultStyle, std::move(aProps)))
+ {
+ pCellStyles->AddStyleName(aName, nIndex);
+ }
+ else
+ {
+ bool bIsAuto;
+ nIndex = pCellStyles->GetIndexOfStyleName(
+ aName, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX, bIsAuto);
+ }
+
+ // store the number format to index mapping for later use.
+ aNumFmtIndexMap.emplace(nNumFmt, nIndex);
+ }
+}
+
+namespace {
+
+void handleFont(
+ SvXMLExport & rExport,
+ std::vector<XMLPropertyState>& rPropStates,
+ const SfxPoolItem* p, const rtl::Reference<XMLPropertySetMapper>& xMapper, std::u16string_view rXMLName )
+{
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+
+ // Apparently font info needs special handling.
+ const SvxFontItem* pItem = static_cast<const SvxFontItem*>(p);
+
+ sal_Int32 nIndexFontName = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, rXMLName, 0);
+
+ if (nIndexFontName == -1 || nIndexFontName >= nEntryCount)
+ return;
+
+ OUString const sFamilyName(pItem->GetFamilyName());
+ OUString const sStyleName(pItem->GetStyleName());
+ auto const nFamily(pItem->GetFamily());
+ auto const nPitch(pItem->GetPitch());
+ auto const eEnc(pItem->GetCharSet());
+ OUString const sName(rExport.GetFontAutoStylePool()->Find(
+ sFamilyName, sStyleName, nFamily, nPitch, eEnc));
+ if (sName.isEmpty())
+ {
+ assert(false); // fallback to fo:font-family etc. probably not needed
+ }
+
+ rPropStates.emplace_back(nIndexFontName, uno::Any(sName));
+}
+
+const SvxFieldData* toXMLPropertyStates(
+ SvXMLExport & rExport,
+ std::vector<XMLPropertyState>& rPropStates, const std::vector<const SfxPoolItem*>& rSecAttrs,
+ const rtl::Reference<XMLPropertySetMapper>& xMapper, const ScXMLEditAttributeMap& rAttrMap )
+{
+ const SvxFieldData* pField = nullptr;
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+ rPropStates.reserve(rSecAttrs.size());
+ for (const SfxPoolItem* p : rSecAttrs)
+ {
+ if (p->Which() == EE_FEATURE_FIELD)
+ {
+ pField = static_cast<const SvxFieldItem*>(p)->GetField();
+ continue;
+ }
+
+ const ScXMLEditAttributeMap::Entry* pEntry = rAttrMap.getEntryByItemID(p->Which());
+ if (!pEntry)
+ continue;
+
+ sal_Int32 nIndex = xMapper->GetEntryIndex(
+ pEntry->nmXMLNS, OUString::createFromAscii(pEntry->mpXMLName), 0);
+
+ if (nIndex == -1 || nIndex >= nEntryCount)
+ continue;
+
+ uno::Any aAny;
+ switch (p->Which())
+ {
+ case EE_CHAR_FONTINFO:
+ handleFont(rExport, rPropStates, p, xMapper, u"font-name");
+ break;
+ case EE_CHAR_FONTINFO_CJK:
+ handleFont(rExport, rPropStates, p, xMapper, u"font-name-asian");
+ break;
+ case EE_CHAR_FONTINFO_CTL:
+ handleFont(rExport, rPropStates, p, xMapper, u"font-name-complex");
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ if (!static_cast<const SvxWeightItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ if (!static_cast<const SvxFontHeightItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ if (!static_cast<const SvxPostureItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ // Underline attribute needs to export multiple entries.
+ sal_Int32 nIndexStyle = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-style", 0);
+ if (nIndexStyle == -1 || nIndexStyle > nEntryCount)
+ break;
+
+ sal_Int32 nIndexWidth = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-width", 0);
+ if (nIndexWidth == -1 || nIndexWidth > nEntryCount)
+ break;
+
+ sal_Int32 nIndexType = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-type", 0);
+ if (nIndexType == -1 || nIndexType > nEntryCount)
+ break;
+
+ sal_Int32 nIndexColor = xMapper->FindEntryIndex("CharUnderlineColor", XML_NAMESPACE_STYLE, u"text-underline-color");
+ if (nIndexColor == -1 || nIndexColor > nEntryCount)
+ break;
+
+ sal_Int32 nIndexHasColor = xMapper->FindEntryIndex("CharUnderlineHasColor", XML_NAMESPACE_STYLE, u"text-underline-color");
+ if (nIndexHasColor == -1 || nIndexHasColor > nEntryCount)
+ break;
+
+ const SvxUnderlineItem* pUL = static_cast<const SvxUnderlineItem*>(p);
+ pUL->QueryValue(aAny, MID_TL_STYLE);
+ rPropStates.emplace_back(nIndexStyle, aAny);
+ rPropStates.emplace_back(nIndexType, aAny);
+ rPropStates.emplace_back(nIndexWidth, aAny);
+
+ pUL->QueryValue(aAny, MID_TL_COLOR);
+ rPropStates.emplace_back(nIndexColor, aAny);
+
+ pUL->QueryValue(aAny, MID_TL_HASCOLOR);
+ rPropStates.emplace_back(nIndexHasColor, aAny);
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ // Same with overline. Do just as we do with underline attributes.
+ sal_Int32 nIndexStyle = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-style", 0);
+ if (nIndexStyle == -1 || nIndexStyle > nEntryCount)
+ break;
+
+ sal_Int32 nIndexWidth = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-width", 0);
+ if (nIndexWidth == -1 || nIndexWidth > nEntryCount)
+ break;
+
+ sal_Int32 nIndexType = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-type", 0);
+ if (nIndexType == -1 || nIndexType > nEntryCount)
+ break;
+
+ sal_Int32 nIndexColor = xMapper->FindEntryIndex("CharOverlineColor", XML_NAMESPACE_STYLE, u"text-overline-color");
+ if (nIndexColor == -1 || nIndexColor > nEntryCount)
+ break;
+
+ sal_Int32 nIndexHasColor = xMapper->FindEntryIndex("CharOverlineHasColor", XML_NAMESPACE_STYLE, u"text-overline-color");
+ if (nIndexHasColor == -1 || nIndexHasColor > nEntryCount)
+ break;
+
+ const SvxOverlineItem* pOL = static_cast<const SvxOverlineItem*>(p);
+ pOL->QueryValue(aAny, MID_TL_STYLE);
+ rPropStates.emplace_back(nIndexStyle, aAny);
+ rPropStates.emplace_back(nIndexType, aAny);
+ rPropStates.emplace_back(nIndexWidth, aAny);
+
+ pOL->QueryValue(aAny, MID_TL_COLOR);
+ rPropStates.emplace_back(nIndexColor, aAny);
+
+ pOL->QueryValue(aAny, MID_TL_HASCOLOR);
+ rPropStates.emplace_back(nIndexHasColor, aAny);
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ if (!static_cast<const SvxColorItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ ::Color nColor;
+ if ( aAny >>= nColor )
+ {
+ sal_Int32 nIndexColor = ( nColor == COL_AUTO ) ? xMapper->GetEntryIndex(
+ XML_NAMESPACE_STYLE, GetXMLToken( XML_USE_WINDOW_FONT_COLOR ), 0 ) : nIndex;
+ rPropStates.emplace_back( nIndexColor, aAny );
+ }
+ }
+ break;
+ case EE_CHAR_WLM:
+ {
+ if (!static_cast<const SvxWordLineModeItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ if (!static_cast<const SvxCrossedOutItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ if (!static_cast<const SvxCharReliefItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ if (!static_cast<const SvxContourItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ if (!static_cast<const SvxShadowedItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ if (!static_cast<const SvxKerningItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ if (!static_cast<const SvxAutoKernItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_FONTWIDTH:
+ {
+ if (!static_cast<const SvxCharScaleWidthItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ sal_Int32 nIndexEsc = xMapper->FindEntryIndex("CharEscapement", XML_NAMESPACE_STYLE, u"text-position");
+ if (nIndexEsc == -1 || nIndexEsc > nEntryCount)
+ break;
+
+ sal_Int32 nIndexEscHeight = xMapper->FindEntryIndex("CharEscapementHeight", XML_NAMESPACE_STYLE, u"text-position");
+ if (nIndexEscHeight == -1 || nIndexEscHeight > nEntryCount)
+ break;
+
+ const SvxEscapementItem* pEsc = static_cast<const SvxEscapementItem*>(p);
+
+ pEsc->QueryValue(aAny);
+ rPropStates.emplace_back(nIndexEsc, aAny);
+
+ pEsc->QueryValue(aAny, MID_ESC_HEIGHT);
+ rPropStates.emplace_back(nIndexEscHeight, aAny);
+
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ if (!static_cast<const SvxEmphasisMarkItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ if (!static_cast<const SvxLanguageItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ // Export multiple entries.
+ sal_Int32 nIndexLanguage, nIndexCountry, nIndexScript, nIndexTag;
+ switch (p->Which())
+ {
+ case EE_CHAR_LANGUAGE:
+ nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"language", 0);
+ nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"country", 0);
+ nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"script", 0);
+ nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag", 0);
+ break;
+ case EE_CHAR_LANGUAGE_CJK:
+ nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"language-asian", 0);
+ nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"country-asian", 0);
+ nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"script-asian", 0);
+ nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag-asian", 0);
+ break;
+ case EE_CHAR_LANGUAGE_CTL:
+ nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"language-complex", 0);
+ nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"country-complex", 0);
+ nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"script-complex", 0);
+ nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag-complex", 0);
+ break;
+ default:
+ nIndexLanguage = nIndexCountry = nIndexScript = nIndexTag = -1;
+ }
+ assert( nIndexLanguage >= 0 && nIndexCountry >= 0 && nIndexScript >= 0 && nIndexTag >= 0);
+ rPropStates.emplace_back( nIndexLanguage, aAny);
+ rPropStates.emplace_back( nIndexCountry, aAny);
+ rPropStates.emplace_back( nIndexScript, aAny);
+ rPropStates.emplace_back( nIndexTag, aAny);
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return pField;
+}
+
+}
+
+void ScXMLExport::ExportCellTextAutoStyles(sal_Int32 nTable)
+{
+ if (!ValidTab(nTable))
+ return;
+
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetTextParagraphExport()->GetTextPropMapper()->getPropertySetMapper();
+ rtl::Reference<SvXMLAutoStylePoolP> xStylePool = GetAutoStylePool();
+ const ScXMLEditAttributeMap& rAttrMap = GetEditAttributeMap();
+
+ sc::EditTextIterator aIter(*pDoc, nTable);
+ sal_Int32 nCellCount = 0;
+ for (const EditTextObject* pEdit = aIter.first(); pEdit; pEdit = aIter.next(), ++nCellCount)
+ {
+ std::vector<editeng::Section> aAttrs;
+ pEdit->GetAllSections(aAttrs);
+ if (aAttrs.empty())
+ continue;
+
+ for (const auto& rSec : aAttrs)
+ {
+ const std::vector<const SfxPoolItem*>& rSecAttrs = rSec.maAttributes;
+ if (rSecAttrs.empty())
+ // No formats applied to this section. Skip it.
+ continue;
+
+ std::vector<XMLPropertyState> aPropStates;
+ toXMLPropertyStates(*this, aPropStates, rSecAttrs, xMapper, rAttrMap);
+ if (!aPropStates.empty())
+ xStylePool->Add(XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
+ }
+ }
+
+ GetProgressBarHelper()->ChangeReference(GetProgressBarHelper()->GetReference() + nCellCount);
+}
+
+void ScXMLExport::WriteRowContent()
+{
+ ScMyRowFormatRange aRange;
+ sal_Int32 nIndex(-1);
+#if OSL_DEBUG_LEVEL > 0
+ sal_Int32 nPrevCol(0);
+#endif
+ sal_Int32 nCols(0);
+ sal_Int32 nPrevValidationIndex(-1);
+ bool bIsAutoStyle(true);
+ bool bIsFirst(true);
+ while (pRowFormatRanges->GetNext(aRange))
+ {
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE(bIsFirst || (!bIsFirst && (nPrevCol + nCols == aRange.nStartColumn)), "here are some columns missing");
+#endif
+ if (bIsFirst)
+ {
+ nIndex = aRange.nIndex;
+ nPrevValidationIndex = aRange.nValidationIndex;
+ bIsAutoStyle = aRange.bIsAutoStyle;
+ nCols = aRange.nRepeatColumns;
+ bIsFirst = false;
+#if OSL_DEBUG_LEVEL > 0
+ nPrevCol = aRange.nStartColumn;
+#endif
+ }
+ else
+ {
+ if (((aRange.nIndex == nIndex && aRange.bIsAutoStyle == bIsAutoStyle) ||
+ (aRange.nIndex == nIndex && nIndex == -1)) &&
+ nPrevValidationIndex == aRange.nValidationIndex)
+ nCols += aRange.nRepeatColumns;
+ else
+ {
+ if (nIndex != -1)
+ AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
+ if (nPrevValidationIndex > -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(nPrevValidationIndex));
+ if (nCols > 1)
+ {
+ AddAttribute(sAttrColumnsRepeated, OUString::number(nCols));
+ }
+ SvXMLElementExport aElemC(*this, sElemCell, true, true);
+ nIndex = aRange.nIndex;
+ bIsAutoStyle = aRange.bIsAutoStyle;
+ nCols = aRange.nRepeatColumns;
+ nPrevValidationIndex = aRange.nValidationIndex;
+#if OSL_DEBUG_LEVEL > 0
+ nPrevCol = aRange.nStartColumn;
+#endif
+ }
+ }
+ }
+ if (!bIsFirst)
+ {
+ if (nIndex != -1)
+ AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
+ if (nPrevValidationIndex > -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(nPrevValidationIndex));
+ if (nCols > 1)
+ {
+ AddAttribute(sAttrColumnsRepeated, OUString::number(nCols));
+ }
+ SvXMLElementExport aElemC(*this, sElemCell, true, true);
+ }
+}
+
+void ScXMLExport::WriteRowStartTag(
+ const sal_Int32 nIndex, const sal_Int32 nEqualRows,
+ bool bHidden, bool bFiltered)
+{
+ // tdf#143940
+ if (nIndex != -1)
+ AddAttribute(sAttrStyleName, pRowStyles->GetStyleNameByIndex(nIndex));
+ if (bHidden)
+ {
+ if (bFiltered)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_FILTER);
+ else
+ AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_COLLAPSE);
+ }
+ if (nEqualRows > 1)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, OUString::number(nEqualRows));
+ }
+
+ StartElement( sElemRow, true);
+}
+
+void ScXMLExport::OpenHeaderRows()
+{
+ StartElement( XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true);
+ bRowHeaderOpen = true;
+}
+
+void ScXMLExport::CloseHeaderRows()
+{
+ EndElement(XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true);
+}
+
+void ScXMLExport::OpenNewRow(
+ const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEqualRows,
+ bool bHidden, bool bFiltered)
+{
+ nOpenRow = nStartRow;
+ if (pGroupRows->IsGroupStart(nStartRow))
+ {
+ if (bHasRowHeader && bRowHeaderOpen)
+ CloseHeaderRows();
+ pGroupRows->OpenGroups(nStartRow);
+ if (bHasRowHeader && bRowHeaderOpen)
+ OpenHeaderRows();
+ }
+ if (bHasRowHeader && !bRowHeaderOpen && nStartRow >= aRowHeaderRange.aStart.Row() && nStartRow <= aRowHeaderRange.aEnd.Row())
+ {
+ if (nStartRow == aRowHeaderRange.aStart.Row())
+ OpenHeaderRows();
+ sal_Int32 nEquals;
+ if (aRowHeaderRange.aEnd.Row() < nStartRow + nEqualRows - 1)
+ nEquals = aRowHeaderRange.aEnd.Row() - nStartRow + 1;
+ else
+ nEquals = nEqualRows;
+ WriteRowStartTag(nIndex, nEquals, bHidden, bFiltered);
+ nOpenRow = nStartRow + nEquals - 1;
+ if (nEquals < nEqualRows)
+ {
+ CloseRow(nStartRow + nEquals - 1);
+ WriteRowStartTag(nIndex, nEqualRows - nEquals, bHidden, bFiltered);
+ nOpenRow = nStartRow + nEqualRows - 1;
+ }
+ }
+ else
+ WriteRowStartTag(nIndex, nEqualRows, bHidden, bFiltered);
+}
+
+void ScXMLExport::OpenAndCloseRow(
+ const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEqualRows,
+ bool bHidden, bool bFiltered)
+{
+ OpenNewRow(nIndex, nStartRow, nEqualRows, bHidden, bFiltered);
+ WriteRowContent();
+ CloseRow(nStartRow + nEqualRows - 1);
+ pRowFormatRanges->Clear();
+}
+
+void ScXMLExport::OpenRow(const sal_Int32 nTable, const sal_Int32 nStartRow, const sal_Int32 nRepeatRow, ScXMLCachedRowAttrAccess& rRowAttr)
+{
+ if (nRepeatRow > 1)
+ {
+ sal_Int32 nPrevIndex(0), nIndex;
+ bool bPrevHidden = false;
+ bool bPrevFiltered = false;
+ bool bHidden = false;
+ bool bFiltered = false;
+ sal_Int32 nEqualRows(1);
+ sal_Int32 nEndRow(nStartRow + nRepeatRow);
+ sal_Int32 nEndRowHidden = nStartRow - 1;
+ sal_Int32 nEndRowFiltered = nStartRow - 1;
+ sal_Int32 nRow;
+ for (nRow = nStartRow; nRow < nEndRow; ++nRow)
+ {
+ if (nRow == nStartRow)
+ {
+ nPrevIndex = pRowStyles->GetStyleNameIndex(nTable, nRow);
+ if (pDoc)
+ {
+ if (nRow > nEndRowHidden)
+ {
+ bPrevHidden = rRowAttr.rowHidden(nTable, nRow, nEndRowHidden);
+ bHidden = bPrevHidden;
+ }
+ if (nRow > nEndRowFiltered)
+ {
+ bPrevFiltered = rRowAttr.rowFiltered(nTable, nRow, nEndRowFiltered);
+ bFiltered = bPrevFiltered;
+ }
+ }
+
+ }
+ else
+ {
+ nIndex = pRowStyles->GetStyleNameIndex(nTable, nRow);
+ if (pDoc)
+ {
+ if (nRow > nEndRowHidden)
+ bHidden = rRowAttr.rowHidden(nTable, nRow, nEndRowHidden);
+ if (nRow > nEndRowFiltered)
+ bFiltered = rRowAttr.rowFiltered(nTable, nRow, nEndRowFiltered);
+ }
+ if (nIndex == nPrevIndex && bHidden == bPrevHidden && bFiltered == bPrevFiltered &&
+ !(bHasRowHeader && ((nRow == aRowHeaderRange.aStart.Row()) || (nRow - 1 == aRowHeaderRange.aEnd.Row()))) &&
+ !(pGroupRows->IsGroupStart(nRow)) &&
+ !(pGroupRows->IsGroupEnd(nRow - 1)))
+ ++nEqualRows;
+ else
+ {
+ assert(nPrevIndex >= 0 && "coverity#1438402");
+ ScRowFormatRanges* pTempRowFormatRanges = new ScRowFormatRanges(pRowFormatRanges.get());
+ OpenAndCloseRow(nPrevIndex, nRow - nEqualRows, nEqualRows, bPrevHidden, bPrevFiltered);
+ pRowFormatRanges.reset(pTempRowFormatRanges);
+ nEqualRows = 1;
+ nPrevIndex = nIndex;
+ bPrevHidden = bHidden;
+ bPrevFiltered = bFiltered;
+ }
+ }
+ }
+ assert(nPrevIndex >= 0 && "coverity#1438402");
+ OpenNewRow(nPrevIndex, nRow - nEqualRows, nEqualRows, bPrevHidden, bPrevFiltered);
+ }
+ else
+ {
+ sal_Int32 nIndex = pRowStyles->GetStyleNameIndex(nTable, nStartRow);
+ bool bHidden = false;
+ bool bFiltered = false;
+ if (pDoc)
+ {
+ sal_Int32 nEndRowHidden;
+ sal_Int32 nEndRowFiltered;
+ bHidden = rRowAttr.rowHidden(nTable, nStartRow, nEndRowHidden);
+ bFiltered = rRowAttr.rowFiltered(nTable, nStartRow, nEndRowFiltered);
+ }
+ assert(nIndex >= 0 && "coverity#1438402");
+ OpenNewRow(nIndex, nStartRow, 1, bHidden, bFiltered);
+ }
+ nOpenRow = nStartRow + nRepeatRow - 1;
+}
+
+void ScXMLExport::CloseRow(const sal_Int32 nRow)
+{
+ if (nOpenRow > -1)
+ {
+ EndElement(sElemRow, true);
+ if (bHasRowHeader && nRow == aRowHeaderRange.aEnd.Row())
+ {
+ CloseHeaderRows();
+ bRowHeaderOpen = false;
+ }
+ if (pGroupRows->IsGroupEnd(nRow))
+ {
+ if (bHasRowHeader && bRowHeaderOpen)
+ CloseHeaderRows();
+ pGroupRows->CloseGroups(nRow);
+ if (bHasRowHeader && bRowHeaderOpen)
+ OpenHeaderRows();
+ }
+ }
+ nOpenRow = -1;
+}
+
+void ScXMLExport::ExportFormatRanges(const sal_Int32 nStartCol, const sal_Int32 nStartRow,
+ const sal_Int32 nEndCol, const sal_Int32 nEndRow, const sal_Int32 nSheet)
+{
+ pRowFormatRanges->Clear();
+ ScXMLCachedRowAttrAccess aRowAttr(pDoc);
+ if (nStartRow == nEndRow)
+ {
+ pCellStyles->GetFormatRanges(nStartCol, nEndCol, nStartRow, nSheet, pRowFormatRanges.get());
+ if (nOpenRow == - 1)
+ OpenRow(nSheet, nStartRow, 1, aRowAttr);
+ WriteRowContent();
+ pRowFormatRanges->Clear();
+ }
+ else
+ {
+ if (nOpenRow > -1)
+ {
+ pCellStyles->GetFormatRanges(nStartCol, pSharedData->GetLastColumn(nSheet), nStartRow, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ CloseRow(nStartRow);
+ sal_Int32 nRows(1);
+ sal_Int32 nTotalRows(nEndRow - nStartRow + 1 - 1);
+ while (nRows < nTotalRows)
+ {
+ pRowFormatRanges->Clear();
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ sal_Int32 nMaxRows = pRowFormatRanges->GetMaxRows();
+ assert(nMaxRows && "ScXMLExport::ExportFormatRanges cannot make progress with zero rows, something went wrong");
+ if (!nMaxRows)
+ {
+ uno::Sequence<OUString> aEmptySeq;
+ SetError(XMLERROR_CANCEL|XMLERROR_FLAG_SEVERE, aEmptySeq);
+ break;
+ }
+ if (nMaxRows >= nTotalRows - nRows)
+ {
+ OpenRow(nSheet, nStartRow + nRows, nTotalRows - nRows, aRowAttr);
+ nRows += nTotalRows - nRows;
+ }
+ else
+ {
+ OpenRow(nSheet, nStartRow + nRows, nMaxRows, aRowAttr);
+ nRows += nMaxRows;
+ }
+ if (!pRowFormatRanges->GetSize())
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ CloseRow(nStartRow + nRows - 1);
+ }
+ if (nTotalRows == 1)
+ CloseRow(nStartRow);
+ OpenRow(nSheet, nEndRow, 1, aRowAttr);
+ pRowFormatRanges->Clear();
+ pCellStyles->GetFormatRanges(0, nEndCol, nEndRow, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ }
+ else
+ {
+ sal_Int32 nRows(0);
+ sal_Int32 nTotalRows(nEndRow - nStartRow + 1 - 1);
+ while (nRows < nTotalRows)
+ {
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ sal_Int32 nMaxRows = pRowFormatRanges->GetMaxRows();
+ OSL_ENSURE(nMaxRows, "something went wrong");
+ if (nMaxRows >= nTotalRows - nRows)
+ {
+ OpenRow(nSheet, nStartRow + nRows, nTotalRows - nRows, aRowAttr);
+ nRows += nTotalRows - nRows;
+ }
+ else
+ {
+ OpenRow(nSheet, nStartRow + nRows, nMaxRows, aRowAttr);
+ nRows += nMaxRows;
+ }
+ if (!pRowFormatRanges->GetSize())
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ CloseRow(nStartRow + nRows - 1);
+ }
+ OpenRow(nSheet, nEndRow, 1, aRowAttr);
+ pRowFormatRanges->Clear();
+ pCellStyles->GetFormatRanges(0, nEndCol, nEndRow, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ }
+ }
+}
+
+void ScXMLExport::GetColumnRowHeader(bool& rHasColumnHeader, ScRange& rColumnHeaderRange,
+ bool& rHasRowHeader, ScRange& rRowHeaderRange,
+ OUString& rPrintRanges) const
+{
+ uno::Reference <sheet::XPrintAreas> xPrintAreas (xCurrentTable, uno::UNO_QUERY);
+ if (!xPrintAreas.is())
+ return;
+
+ rHasRowHeader = xPrintAreas->getPrintTitleRows();
+ rHasColumnHeader = xPrintAreas->getPrintTitleColumns();
+ table::CellRangeAddress rTempRowHeaderRange = xPrintAreas->getTitleRows();
+ rRowHeaderRange = ScRange(rTempRowHeaderRange.StartColumn,
+ rTempRowHeaderRange.StartRow,
+ rTempRowHeaderRange.Sheet,
+ rTempRowHeaderRange.EndColumn,
+ rTempRowHeaderRange.EndRow,
+ rTempRowHeaderRange.Sheet);
+ table::CellRangeAddress rTempColumnHeaderRange = xPrintAreas->getTitleColumns();
+ rColumnHeaderRange = ScRange(rTempColumnHeaderRange.StartColumn,
+ rTempColumnHeaderRange.StartRow,
+ rTempColumnHeaderRange.Sheet,
+ rTempColumnHeaderRange.EndColumn,
+ rTempColumnHeaderRange.EndRow,
+ rTempColumnHeaderRange.Sheet);
+ uno::Sequence< table::CellRangeAddress > aRangeList( xPrintAreas->getPrintAreas() );
+ ScRangeStringConverter::GetStringFromRangeList( rPrintRanges, aRangeList, pDoc, FormulaGrammar::CONV_OOO );
+}
+
+void ScXMLExport::FillFieldGroup(ScOutlineArray* pFields, ScMyOpenCloseColumnRowGroup* pGroups)
+{
+ size_t nDepth = pFields->GetDepth();
+ for (size_t i = 0; i < nDepth; ++i)
+ {
+ size_t nFields = pFields->GetCount(i);
+ for (size_t j = 0; j < nFields; ++j)
+ {
+ ScMyColumnRowGroup aGroup;
+ const ScOutlineEntry* pEntry = pFields->GetEntry(i, j);
+ aGroup.nField = pEntry->GetStart();
+ aGroup.nLevel = static_cast<sal_Int16>(i);
+ aGroup.bDisplay = !(pEntry->IsHidden());
+ pGroups->AddGroup(aGroup, pEntry->GetEnd());
+ }
+ }
+ if (nDepth)
+ pGroups->Sort();
+}
+
+void ScXMLExport::FillColumnRowGroups()
+{
+ if (!pDoc)
+ return;
+
+ ScOutlineTable* pOutlineTable = pDoc->GetOutlineTable( static_cast<SCTAB>(nCurrentTable) );
+ if(pOutlineTable)
+ {
+ ScOutlineArray& rCols(pOutlineTable->GetColArray());
+ ScOutlineArray& rRows(pOutlineTable->GetRowArray());
+ FillFieldGroup(&rCols, pGroupColumns.get());
+ FillFieldGroup(&rRows, pGroupRows.get());
+ pSharedData->SetLastColumn(nCurrentTable, pGroupColumns->GetLast());
+ pSharedData->SetLastRow(nCurrentTable, pGroupRows->GetLast());
+ }
+}
+
+void ScXMLExport::SetBodyAttributes()
+{
+ if (!(pDoc && pDoc->IsDocProtected()))
+ return;
+
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STRUCTURE_PROTECTED, XML_TRUE);
+ OUStringBuffer aBuffer;
+ uno::Sequence<sal_Int8> aPassHash;
+ ScPasswordHash eHashUsed = PASSHASH_UNSPECIFIED;
+ const ScDocProtection* p = pDoc->GetDocProtection();
+ if (p)
+ {
+ if (p->hasPasswordHash(PASSHASH_SHA1))
+ {
+ aPassHash = p->getPasswordHash(PASSHASH_SHA1);
+ eHashUsed = PASSHASH_SHA1;
+ }
+ else if (p->hasPasswordHash(PASSHASH_SHA256))
+ {
+ aPassHash = p->getPasswordHash(PASSHASH_SHA256);
+ eHashUsed = PASSHASH_SHA256;
+ }
+ else if (p->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
+ {
+ aPassHash = p->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
+ eHashUsed = PASSHASH_XL;
+ }
+ }
+ ::comphelper::Base64::encode(aBuffer, aPassHash);
+ if (aBuffer.isEmpty())
+ return;
+
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY, aBuffer.makeStringAndClear());
+ if (getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
+ return;
+
+ if (eHashUsed == PASSHASH_XL)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_XL));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA1)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA256)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA256));
+ }
+}
+
+static bool lcl_CopyStreamElement( const uno::Reference< io::XInputStream >& xInput,
+ const uno::Reference< io::XOutputStream >& xOutput,
+ sal_Int64 nCount )
+{
+ const sal_Int32 nBufSize = 16*1024;
+ uno::Sequence<sal_Int8> aSequence(nBufSize);
+
+ sal_Int64 nRemaining = nCount;
+ bool bFirst = true;
+
+ while ( nRemaining > 0 )
+ {
+ sal_Int32 nRead = xInput->readBytes( aSequence, std::min( nRemaining, static_cast<sal_Int64>(nBufSize) ) );
+ if (bFirst)
+ {
+ // safety check: Make sure the copied part actually points to the start of an element
+ if ( nRead < 1 || aSequence[0] != static_cast<sal_Int8>('<') )
+ {
+ return false; // abort and set an error
+ }
+ bFirst = false;
+ }
+ if (nRead == nRemaining)
+ {
+ // safety check: Make sure the copied part also ends at the end of an element
+ if ( aSequence[nRead-1] != static_cast<sal_Int8>('>') )
+ {
+ return false; // abort and set an error
+ }
+ }
+
+ if ( nRead == nBufSize )
+ {
+ xOutput->writeBytes( aSequence );
+ nRemaining -= nRead;
+ }
+ else
+ {
+ if ( nRead > 0 )
+ {
+ uno::Sequence<sal_Int8> aTempBuf( aSequence.getConstArray(), nRead );
+ xOutput->writeBytes( aTempBuf );
+ }
+ nRemaining = 0;
+ }
+ }
+ return true; // successful
+}
+
+static void lcl_SkipBytesInBlocks( const uno::Reference< io::XInputStream >& xInput, sal_Int64 nBytesToSkip )
+{
+ // skipBytes in zip stream is implemented as reading.
+ // For now, split into several calls to avoid allocating a large buffer.
+ // Later, skipBytes should be changed.
+
+ const sal_Int64 nMaxSize = 32*1024;
+
+ if ( nBytesToSkip > 0 )
+ {
+ sal_Int64 nRemaining = nBytesToSkip;
+ while ( nRemaining > 0 )
+ {
+ sal_Int32 nSkip = std::min( nRemaining, nMaxSize );
+ xInput->skipBytes( nSkip );
+ nRemaining -= nSkip;
+ }
+ }
+}
+
+void ScXMLExport::CopySourceStream( sal_Int64 nStartOffset, sal_Int64 nEndOffset, sal_Int64& rNewStart, sal_Int64& rNewEnd )
+{
+ uno::Reference<xml::sax::XDocumentHandler> xHandler = GetDocHandler();
+ uno::Reference<io::XActiveDataSource> xDestSource( xHandler, uno::UNO_QUERY );
+ if ( !xDestSource.is() )
+ return;
+
+ uno::Reference<io::XOutputStream> xDestStream = xDestSource->getOutputStream();
+ uno::Reference<io::XSeekable> xDestSeek( xDestStream, uno::UNO_QUERY );
+ if ( !xDestSeek.is() )
+ return;
+
+ // temporary: set same stream again to clear buffer
+ xDestSource->setOutputStream( xDestStream );
+
+ if ( getExportFlags() & SvXMLExportFlags::PRETTY )
+ {
+ static constexpr OString aOutStr("\n "_ostr);
+ uno::Sequence<sal_Int8> aOutSeq( reinterpret_cast<sal_Int8 const *>(aOutStr.getStr()), aOutStr.getLength() );
+ xDestStream->writeBytes( aOutSeq );
+ }
+
+ rNewStart = xDestSeek->getPosition();
+
+ if ( nStartOffset > nSourceStreamPos )
+ lcl_SkipBytesInBlocks( xSourceStream, nStartOffset - nSourceStreamPos );
+
+ if ( !lcl_CopyStreamElement( xSourceStream, xDestStream, nEndOffset - nStartOffset ) )
+ {
+ // If copying went wrong, set an error.
+ // ScXMLImportWrapper then resets all stream flags, so the next save attempt will use normal saving.
+
+ uno::Sequence<OUString> aEmptySeq;
+ SetError(XMLERROR_CANCEL|XMLERROR_FLAG_SEVERE, aEmptySeq);
+ }
+ nSourceStreamPos = nEndOffset;
+
+ rNewEnd = xDestSeek->getPosition();
+}
+
+const ScXMLEditAttributeMap& ScXMLExport::GetEditAttributeMap() const
+{
+ if (!mpEditAttrMap)
+ mpEditAttrMap.reset(new ScXMLEditAttributeMap);
+ return *mpEditAttrMap;
+}
+
+void ScXMLExport::RegisterDefinedStyleNames( const uno::Reference< css::sheet::XSpreadsheetDocument > & xSpreadDoc )
+{
+ ScFormatSaveData* pFormatData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetFormatSaveData();
+ auto xAutoStylePool = GetAutoStylePool();
+ for (const auto& rFormatInfo : pFormatData->maIDToName)
+ {
+ xAutoStylePool->RegisterDefinedName(XmlStyleFamily::TABLE_CELL, rFormatInfo.second);
+ }
+}
+
+void ScXMLExport::ExportContent_()
+{
+ nCurrentTable = 0;
+ if (!pSharedData)
+ {
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ CollectSharedData(nTableCount, nShapesCount);
+ OSL_FAIL("no shared data set");
+ if (!pSharedData)
+ return;
+ }
+ ScXMLExportDatabaseRanges aExportDatabaseRanges(*this);
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if ( !xSpreadDoc.is() )
+ return;
+
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetSheetSaveData();
+ if (pSheetData)
+ pSheetData->ResetSaveEntries();
+
+ uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ //_GetNamespaceMap().ClearQNamesCache();
+ pChangeTrackingExportHelper->CollectAndWriteChanges();
+ WriteCalculationSettings(xSpreadDoc);
+ sal_Int32 nTableCount(xIndex->getCount());
+ ScMyAreaLinksContainer aAreaLinks;
+ GetAreaLinks( aAreaLinks );
+ ScMyEmptyDatabaseRangesContainer aEmptyRanges(aExportDatabaseRanges.GetEmptyDatabaseRanges());
+ ScMyDetectiveOpContainer aDetectiveOpContainer;
+ GetDetectiveOpList( aDetectiveOpContainer );
+
+ pCellStyles->Sort();
+ pMergedRangesContainer->Sort();
+ pSharedData->GetDetectiveObjContainer()->Sort();
+
+ mpCellsItr->Clear();
+ mpCellsItr->SetShapes( pSharedData->GetShapesContainer() );
+ mpCellsItr->SetNoteShapes( pSharedData->GetNoteShapes() );
+ mpCellsItr->SetMergedRanges( pMergedRangesContainer.get() );
+ mpCellsItr->SetAreaLinks( &aAreaLinks );
+ mpCellsItr->SetEmptyDatabaseRanges( &aEmptyRanges );
+ mpCellsItr->SetDetectiveObj( pSharedData->GetDetectiveObjContainer() );
+ mpCellsItr->SetDetectiveOp( &aDetectiveOpContainer );
+
+ if (nTableCount > 0)
+ pValidationsContainer->WriteValidations(*this);
+ WriteTheLabelRanges( xSpreadDoc );
+ for (sal_Int32 nTable = 0; nTable < nTableCount; ++nTable)
+ {
+ sal_Int64 nStartOffset = -1;
+ sal_Int64 nEndOffset = -1;
+ if (pSheetData && pDoc && pDoc->IsStreamValid(static_cast<SCTAB>(nTable)) && !pDoc->GetChangeTrack())
+ pSheetData->GetStreamPos( nTable, nStartOffset, nEndOffset );
+
+ if ( nStartOffset >= 0 && nEndOffset >= 0 && xSourceStream.is() )
+ {
+ sal_Int64 nNewStart = -1;
+ sal_Int64 nNewEnd = -1;
+ CopySourceStream( nStartOffset, nEndOffset, nNewStart, nNewEnd );
+
+ // store position of copied sheet in output
+ pSheetData->AddSavePos( nTable, nNewStart, nNewEnd );
+
+ // skip iterator entries for this sheet
+ mpCellsItr->SkipTable(static_cast<SCTAB>(nTable));
+ }
+ else
+ {
+ uno::Reference<sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ WriteTable(nTable, xTable);
+ }
+ IncrementProgressBar(false);
+ }
+ }
+ WriteExternalRefCaches();
+ WriteNamedExpressions();
+ WriteDataStream();
+ aExportDatabaseRanges.WriteDatabaseRanges();
+ WriteExternalDataMapping();
+ ScXMLExportDataPilot aExportDataPilot(*this);
+ aExportDataPilot.WriteDataPilots();
+ WriteConsolidation();
+ ScXMLExportDDELinks aExportDDELinks(*this);
+ aExportDDELinks.WriteDDELinks(xSpreadDoc);
+ IncrementProgressBar(true, 0);
+ GetProgressBarHelper()->SetValue(GetProgressBarHelper()->GetReference());
+}
+
+void ScXMLExport::ExportStyles_( bool bUsed )
+{
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if (xSpreadDoc.is())
+ RegisterDefinedStyleNames( xSpreadDoc);
+
+ if (!pSharedData)
+ {
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ CollectSharedData(nTableCount, nShapesCount);
+ }
+ rtl::Reference<XMLCellStyleExport> aStylesExp(new XMLCellStyleExport(*this, GetAutoStylePool().get()));
+ if (GetModel().is())
+ {
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (xMultiServiceFactory.is())
+ {
+ uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance("com.sun.star.sheet.Defaults"), uno::UNO_QUERY);
+ if (xProperties.is())
+ aStylesExp->exportDefaultStyle(xProperties, XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME, xCellStylesExportPropertySetMapper);
+ GetShapeExport()->ExportGraphicDefaults();
+ }
+ collectDataStyles(false);
+ }
+ exportDataStyles();
+
+ aStylesExp->exportStyleFamily("CellStyles",
+ XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME, xCellStylesExportPropertySetMapper, false, XmlStyleFamily::TABLE_CELL);
+
+ SvXMLExport::ExportStyles_(bUsed);
+
+ exportTheme();
+}
+
+void ScXMLExport::exportTheme()
+{
+ if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
+ return;
+
+ SdrModel* pModel = GetDocument()->GetDrawLayer();
+
+ if (!pModel)
+ return;
+
+ auto const& pTheme = pModel->getTheme();
+ if (!pTheme)
+ return;
+
+ ExportThemeElement(pTheme);
+}
+
+void ScXMLExport::AddStyleFromCells(const uno::Reference<beans::XPropertySet>& xProperties,
+ const uno::Reference<sheet::XSpreadsheet>& xTable,
+ sal_Int32 nTable, const OUString* pOldName)
+{
+ css::uno::Any aAny = xProperties->getPropertyValue("FormatID");
+ sal_uInt64 nKey = 0;
+ aAny >>= nKey;
+
+ //! pass xCellRanges instead
+ uno::Reference<sheet::XSheetCellRanges> xCellRanges( xProperties, uno::UNO_QUERY );
+
+ OUString sStyleName;
+ sal_Int32 nNumberFormat(-1);
+ sal_Int32 nValidationIndex(-1);
+ std::vector<XMLPropertyState> aPropStates(xCellStylesExportPropertySetMapper->Filter(*this, xProperties));
+ std::vector< XMLPropertyState >::iterator aItr(aPropStates.begin());
+ std::vector< XMLPropertyState >::iterator aEndItr(aPropStates.end());
+ sal_Int32 nCount(0);
+ while (aItr != aEndItr)
+ {
+ if (aItr->mnIndex != -1)
+ {
+ switch (xCellStylesPropertySetMapper->GetEntryContextId(aItr->mnIndex))
+ {
+ case CTF_SC_VALIDATION :
+ {
+ pValidationsContainer->AddValidation(aItr->maValue, nValidationIndex);
+ // this is not very slow, because it is most the last property or
+ // if it is not the last property it is the property before the last property,
+ // so in the worst case only one property has to be copied, but in the best case no
+ // property has to be copied
+ aItr = aPropStates.erase(aItr);
+ aEndItr = aPropStates.end(); // old aEndItr is invalidated!
+ }
+ break;
+ case CTF_SC_CELLSTYLE :
+ {
+ aItr->maValue >>= sStyleName;
+ aItr->mnIndex = -1;
+ ++aItr;
+ ++nCount;
+ }
+ break;
+ case CTF_SC_NUMBERFORMAT :
+ {
+ if (aItr->maValue >>= nNumberFormat)
+ addDataStyle(nNumberFormat);
+ ++aItr;
+ ++nCount;
+ }
+ break;
+ default:
+ {
+ ++aItr;
+ ++nCount;
+ }
+ break;
+ }
+ }
+ else
+ {
+ ++aItr;
+ ++nCount;
+ }
+ }
+ if (nCount == 1) // this is the CellStyle and should be removed if alone
+ aPropStates.clear();
+ if (nNumberFormat == -1)
+ xProperties->getPropertyValue(SC_UNONAME_NUMFMT) >>= nNumberFormat;
+ if (sStyleName.isEmpty())
+ return;
+
+ if (!aPropStates.empty())
+ {
+ sal_Int32 nIndex;
+ if (pOldName)
+ {
+ if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates)))
+ {
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_CELL, *pOldName);
+ // add to pCellStyles, so the name is found for normal sheets
+ pCellStyles->AddStyleName(*pOldName, nIndex);
+ }
+ }
+ else
+ {
+ OUString sName;
+ bool bAdded = false;
+ if (nKey)
+ {
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ ScFormatSaveData* pFormatData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetFormatSaveData();
+ auto itr = pFormatData->maIDToName.find(nKey);
+ if (itr != pFormatData->maIDToName.end())
+ {
+ sName = itr->second;
+ bAdded = GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TABLE_CELL, sStyleName, aPropStates);
+ if (bAdded)
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_CELL, sName);
+ }
+ }
+ bool bIsAutoStyle(true);
+ if (bAdded || GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates)))
+ {
+ pCellStyles->AddStyleName(sName, nIndex);
+ }
+ else
+ nIndex = pCellStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX, bIsAutoStyle);
+
+ const uno::Sequence<table::CellRangeAddress> aAddresses(xCellRanges->getRangeAddresses());
+ bool bGetMerge(true);
+ for (table::CellRangeAddress const & address : aAddresses)
+ {
+ pSharedData->SetLastColumn(nTable, address.EndColumn);
+ pSharedData->SetLastRow(nTable, address.EndRow);
+ pCellStyles->AddRangeStyleName(address, nIndex, bIsAutoStyle, nValidationIndex, nNumberFormat);
+ if (bGetMerge)
+ bGetMerge = GetMerged(&address, xTable);
+ }
+ }
+ }
+ else
+ {
+ OUString sEncodedStyleName(EncodeStyleName(sStyleName));
+ sal_Int32 nIndex(0);
+ pCellStyles->AddStyleName(sEncodedStyleName, nIndex, false);
+ if ( !pOldName )
+ {
+ const uno::Sequence<table::CellRangeAddress> aAddresses(xCellRanges->getRangeAddresses());
+ bool bGetMerge(true);
+ for (table::CellRangeAddress const & address : aAddresses)
+ {
+ if (bGetMerge)
+ bGetMerge = GetMerged(&address, xTable);
+ pCellStyles->AddRangeStyleName(address, nIndex, false, nValidationIndex, nNumberFormat);
+ if( sStyleName != "Default" || nValidationIndex != -1 )
+ {
+ pSharedData->SetLastColumn(nTable, address.EndColumn);
+ pSharedData->SetLastRow(nTable, address.EndRow);
+ }
+ }
+ }
+ }
+}
+
+void ScXMLExport::AddStyleFromColumn(const uno::Reference<beans::XPropertySet>& xColumnProperties,
+ const OUString* pOldName, sal_Int32& rIndex, bool& rIsVisible)
+{
+ std::vector<XMLPropertyState> aPropStates(xColumnStylesExportPropertySetMapper->Filter(*this, xColumnProperties));
+ if(aPropStates.empty())
+ return;
+
+ auto aItr = std::find_if(aPropStates.begin(), aPropStates.end(),
+ [this](const XMLPropertyState& rPropState) {
+ return xColumnStylesPropertySetMapper->GetEntryContextId(rPropState.mnIndex) == CTF_SC_ISVISIBLE; });
+ if (aItr != aPropStates.end())
+ {
+ aItr->maValue >>= rIsVisible;
+ }
+
+ const OUString sParent;
+ if (pOldName)
+ {
+ if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_COLUMN, sParent, std::move(aPropStates)))
+ {
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_COLUMN, *pOldName);
+ // add to pColumnStyles, so the name is found for normal sheets
+ rIndex = pColumnStyles->AddStyleName(*pOldName);
+ }
+ }
+ else
+ {
+ OUString sName;
+ if (GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_COLUMN, sParent, std::move(aPropStates)))
+ {
+ rIndex = pColumnStyles->AddStyleName(sName);
+ }
+ else
+ rIndex = pColumnStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX);
+ }
+}
+
+void ScXMLExport::AddStyleFromRow(const uno::Reference<beans::XPropertySet>& xRowProperties,
+ const OUString* pOldName, sal_Int32& rIndex)
+{
+ std::vector<XMLPropertyState> aPropStates(xRowStylesExportPropertySetMapper->Filter(*this, xRowProperties));
+ if(aPropStates.empty())
+ return;
+
+ const OUString sParent;
+ if (pOldName)
+ {
+ if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_ROW, sParent, std::move(aPropStates)))
+ {
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_ROW, *pOldName);
+ // add to pRowStyles, so the name is found for normal sheets
+ rIndex = pRowStyles->AddStyleName(*pOldName);
+ }
+ }
+ else
+ {
+ OUString sName;
+ if (GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_ROW, sParent, std::move(aPropStates)))
+ {
+ rIndex = pRowStyles->AddStyleName(sName);
+ }
+ else
+ rIndex = pRowStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX);
+ }
+}
+
+static uno::Any lcl_GetEnumerated( uno::Reference<container::XEnumerationAccess> const & xEnumAccess, sal_Int32 nIndex )
+{
+ uno::Any aRet;
+ uno::Reference<container::XEnumeration> xEnum( xEnumAccess->createEnumeration() );
+ try
+ {
+ sal_Int32 nSkip = nIndex;
+ while ( nSkip > 0 )
+ {
+ (void) xEnum->nextElement();
+ --nSkip;
+ }
+ aRet = xEnum->nextElement();
+ }
+ catch (container::NoSuchElementException&)
+ {
+ // leave aRet empty
+ }
+ return aRet;
+}
+
+void ScXMLExport::collectAutoStyles()
+{
+ SvXMLExport::collectAutoStyles();
+
+ if (mbAutoStylesCollected)
+ return;
+
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if (!xSpreadDoc.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if (!xIndex.is())
+ return;
+
+ if (getExportFlags() & SvXMLExportFlags::CONTENT)
+ {
+ // Reserve the loaded cell style names.
+ RegisterDefinedStyleNames( xSpreadDoc);
+
+ // re-create automatic styles with old names from stored data
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetSheetSaveData();
+ if (pSheetData && pDoc)
+ {
+ // formulas have to be calculated now, to detect changed results
+ // (during normal save, they will be calculated anyway)
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ if (pDoc->IsStreamValid(nTab))
+ pDoc->InterpretDirtyCells(ScRange(0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab));
+
+ // stored cell styles
+ const std::vector<ScCellStyleEntry>& rCellEntries = pSheetData->GetCellStyles();
+ for (const auto& rCellEntry : rCellEntries)
+ {
+ ScAddress aPos = rCellEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ uno::Reference <sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference <beans::XPropertySet> xProperties(
+ xTable->getCellByPosition( aPos.Col(), aPos.Row() ), uno::UNO_QUERY );
+
+ AddStyleFromCells(xProperties, xTable, nTable, &rCellEntry.maName);
+ }
+ }
+
+ // stored column styles
+ const std::vector<ScCellStyleEntry>& rColumnEntries = pSheetData->GetColumnStyles();
+ for (const auto& rColumnEntry : rColumnEntries)
+ {
+ ScAddress aPos = rColumnEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ uno::Reference<table::XColumnRowRange> xColumnRowRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference<table::XTableColumns> xTableColumns(xColumnRowRange->getColumns());
+ uno::Reference<beans::XPropertySet> xColumnProperties(xTableColumns->getByIndex( aPos.Col() ), uno::UNO_QUERY);
+
+ sal_Int32 nIndex(-1);
+ bool bIsVisible(true);
+ AddStyleFromColumn( xColumnProperties, &rColumnEntry.maName, nIndex, bIsVisible );
+ }
+ }
+
+ // stored row styles
+ const std::vector<ScCellStyleEntry>& rRowEntries = pSheetData->GetRowStyles();
+ for (const auto& rRowEntry : rRowEntries)
+ {
+ ScAddress aPos = rRowEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ uno::Reference<table::XColumnRowRange> xColumnRowRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference<table::XTableRows> xTableRows(xColumnRowRange->getRows());
+ uno::Reference<beans::XPropertySet> xRowProperties(xTableRows->getByIndex( aPos.Row() ), uno::UNO_QUERY);
+
+ sal_Int32 nIndex(-1);
+ AddStyleFromRow( xRowProperties, &rRowEntry.maName, nIndex );
+ }
+ }
+
+ // stored table styles
+ const std::vector<ScCellStyleEntry>& rTableEntries = pSheetData->GetTableStyles();
+ for (const auto& rTableEntry : rTableEntries)
+ {
+ ScAddress aPos = rTableEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ //! separate method AddStyleFromTable needed?
+ uno::Reference<beans::XPropertySet> xTableProperties(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ if (xTableProperties.is())
+ {
+ std::vector<XMLPropertyState> aPropStates(xTableStylesExportPropertySetMapper->Filter(*this, xTableProperties));
+ OUString sName( rTableEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TABLE_TABLE, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_TABLE, sName);
+ }
+ }
+ }
+
+ // stored styles for notes
+
+ rtl::Reference<SvXMLExportPropertyMapper> xShapeMapper = XMLShapeExport::CreateShapePropMapper( *this );
+
+ const std::vector<ScNoteStyleEntry>& rNoteEntries = pSheetData->GetNoteStyles();
+ for (const auto& rNoteEntry : rNoteEntries)
+ {
+ ScAddress aPos = rNoteEntry.maCellPos;
+ SCTAB nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( nTable );
+ if (bCopySheet)
+ {
+ //! separate method AddStyleFromNote needed?
+
+ ScPostIt* pNote = pDoc->GetNote(aPos);
+ OSL_ENSURE( pNote, "note not found" );
+ if (pNote)
+ {
+ SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
+ // all uno shapes are created anyway in CollectSharedData
+ uno::Reference<beans::XPropertySet> xShapeProperties( pDrawObj->getUnoShape(), uno::UNO_QUERY );
+ if (xShapeProperties.is())
+ {
+ if ( !rNoteEntry.maStyleName.isEmpty() )
+ {
+ std::vector<XMLPropertyState> aPropStates(xShapeMapper->Filter(*this, xShapeProperties));
+ OUString sName( rNoteEntry.maStyleName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::SD_GRAPHICS_ID, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::SD_GRAPHICS_ID, sName);
+ }
+ if ( !rNoteEntry.maTextStyle.isEmpty() )
+ {
+ std::vector<XMLPropertyState> aPropStates(
+ GetTextParagraphExport()->GetParagraphPropertyMapper()->Filter(*this, xShapeProperties));
+ OUString sName( rNoteEntry.maTextStyle );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_PARAGRAPH, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_PARAGRAPH, sName);
+ }
+ }
+ }
+ }
+ }
+
+ // note paragraph styles
+
+ rtl::Reference<SvXMLExportPropertyMapper> xParaPropMapper = GetTextParagraphExport()->GetParagraphPropertyMapper();
+
+ const std::vector<ScTextStyleEntry>& rNoteParaEntries = pSheetData->GetNoteParaStyles();
+ for (const auto& rNoteParaEntry : rNoteParaEntries)
+ {
+ ScAddress aPos = rNoteParaEntry.maCellPos;
+ SCTAB nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( nTable );
+ if (bCopySheet)
+ {
+ ScPostIt* pNote = pDoc->GetNote( aPos );
+ OSL_ENSURE( pNote, "note not found" );
+ if (pNote)
+ {
+ SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
+ uno::Reference<container::XEnumerationAccess> xCellText(pDrawObj->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xParaProp(
+ lcl_GetEnumerated( xCellText, rNoteParaEntry.maSelection.nStartPara ), uno::UNO_QUERY );
+ if ( xParaProp.is() )
+ {
+ std::vector<XMLPropertyState> aPropStates(xParaPropMapper->Filter(*this, xParaProp));
+ OUString sName( rNoteParaEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_PARAGRAPH, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_PARAGRAPH, sName);
+ }
+ }
+ }
+ }
+
+ // note text styles
+
+ rtl::Reference<SvXMLExportPropertyMapper> xTextPropMapper = XMLTextParagraphExport::CreateCharExtPropMapper( *this );
+
+ const std::vector<ScTextStyleEntry>& rNoteTextEntries = pSheetData->GetNoteTextStyles();
+ for (const auto& rNoteTextEntry : rNoteTextEntries)
+ {
+ ScAddress aPos = rNoteTextEntry.maCellPos;
+ SCTAB nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( nTable );
+ if (bCopySheet)
+ {
+ ScPostIt* pNote = pDoc->GetNote( aPos );
+ OSL_ENSURE( pNote, "note not found" );
+ if (pNote)
+ {
+ SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
+ uno::Reference<text::XSimpleText> xCellText(pDrawObj->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xCursorProp(xCellText->createTextCursor(), uno::UNO_QUERY);
+ ScDrawTextCursor* pCursor = comphelper::getFromUnoTunnel<ScDrawTextCursor>( xCursorProp );
+ if (pCursor)
+ {
+ pCursor->SetSelection( rNoteTextEntry.maSelection );
+
+ std::vector<XMLPropertyState> aPropStates(xTextPropMapper->Filter(*this, xCursorProp));
+ OUString sName( rNoteTextEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_TEXT, sName);
+ }
+ }
+ }
+ }
+
+ // stored text styles
+
+ // Calling createTextCursor fires up editeng, which is very slow, and often subsequent style entries
+ // refer to the same cell, so cache it.
+ ScAddress aPrevPos;
+ uno::Reference<beans::XPropertySet> xPrevCursorProp;
+ const std::vector<ScTextStyleEntry>& rTextEntries = pSheetData->GetTextStyles();
+ for (const auto& rTextEntry : rTextEntries)
+ {
+ ScAddress aPos = rTextEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (!bCopySheet)
+ continue;
+
+ //! separate method AddStyleFromText needed?
+ //! cache sheet object
+
+ uno::Reference<beans::XPropertySet> xCursorProp;
+ if (xPrevCursorProp && aPrevPos == aPos)
+ xCursorProp = xPrevCursorProp;
+ else
+ {
+ uno::Reference<table::XCellRange> xCellRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference<text::XSimpleText> xCellText(xCellRange->getCellByPosition(aPos.Col(), aPos.Row()), uno::UNO_QUERY);
+ xCursorProp.set(xCellText->createTextCursor(), uno::UNO_QUERY);
+ }
+ ScCellTextCursor* pCursor = comphelper::getFromUnoTunnel<ScCellTextCursor>( xCursorProp );
+ if (!pCursor)
+ continue;
+ pCursor->SetSelection( rTextEntry.maSelection );
+
+ std::vector<XMLPropertyState> aPropStates(xTextPropMapper->Filter(*this, xCursorProp));
+ OUString sName( rTextEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_TEXT, sName);
+ xPrevCursorProp = xCursorProp;
+ aPrevPos = aPos;
+ }
+ }
+
+ ExportExternalRefCacheStyles();
+
+ if (!pSharedData)
+ {
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ CollectSharedData(nTableCount, nShapesCount);
+ }
+ sal_Int32 nTableCount(xIndex->getCount());
+ pCellStyles->AddNewTable(nTableCount - 1);
+ CollectShapesAutoStyles(nTableCount);
+ for (sal_Int32 nTable = 0; nTable < nTableCount; ++nTable, IncrementProgressBar(false))
+ {
+ uno::Reference <sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ if (!xTable.is())
+ continue;
+
+ // table styles array must be complete, including copied tables - Add should find the stored style
+ uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
+ if (xTableProperties.is())
+ {
+ std::vector<XMLPropertyState> aPropStates(xTableStylesExportPropertySetMapper->Filter(*this, xTableProperties));
+ if(!aPropStates.empty())
+ {
+ OUString sName;
+ GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_TABLE, OUString(), std::move(aPropStates));
+ aTableStyles.push_back(sName);
+ }
+ }
+
+ // collect other auto-styles only for non-copied sheets
+ uno::Reference<sheet::XUniqueCellFormatRangesSupplier> xCellFormatRanges ( xTable, uno::UNO_QUERY );
+ if ( xCellFormatRanges.is() )
+ {
+ uno::Reference<container::XIndexAccess> xFormatRangesIndex(xCellFormatRanges->getUniqueCellFormatRanges());
+ if (xFormatRangesIndex.is())
+ {
+ sal_Int32 nFormatRangesCount(xFormatRangesIndex->getCount());
+ GetProgressBarHelper()->ChangeReference(GetProgressBarHelper()->GetReference() + nFormatRangesCount);
+ for (sal_Int32 nFormatRange = 0; nFormatRange < nFormatRangesCount; ++nFormatRange)
+ {
+ uno::Reference< sheet::XSheetCellRanges> xCellRanges(xFormatRangesIndex->getByIndex(nFormatRange), uno::UNO_QUERY);
+ if (xCellRanges.is())
+ {
+ uno::Reference <beans::XPropertySet> xProperties (xCellRanges, uno::UNO_QUERY);
+ if (xProperties.is())
+ {
+ AddStyleFromCells(xProperties, xTable, nTable, nullptr);
+ IncrementProgressBar(false);
+ }
+ }
+ }
+ }
+ }
+ uno::Reference<table::XColumnRowRange> xColumnRowRange (xTable, uno::UNO_QUERY);
+ if (xColumnRowRange.is() && pDoc)
+ {
+ pDoc->SyncColRowFlags();
+ uno::Reference<table::XTableColumns> xTableColumns(xColumnRowRange->getColumns());
+ if (xTableColumns.is())
+ {
+ sal_Int32 nColumns(pDoc->GetLastChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
+ pSharedData->SetLastColumn(nTable, nColumns);
+ table::CellRangeAddress aCellAddress(GetEndAddress(xTable));
+ if (aCellAddress.EndColumn > nColumns)
+ {
+ ++nColumns;
+ pColumnStyles->AddNewTable(nTable, aCellAddress.EndColumn);
+ }
+ else
+ pColumnStyles->AddNewTable(nTable, nColumns);
+ sal_Int32 nColumn = 0;
+ while (nColumn <= pDoc->MaxCol())
+ {
+ sal_Int32 nIndex(-1);
+ bool bIsVisible(true);
+ uno::Reference <beans::XPropertySet> xColumnProperties(xTableColumns->getByIndex(nColumn), uno::UNO_QUERY);
+ if (xColumnProperties.is())
+ {
+ AddStyleFromColumn( xColumnProperties, nullptr, nIndex, bIsVisible );
+ pColumnStyles->AddFieldStyleName(nTable, nColumn, nIndex, bIsVisible);
+ }
+ sal_Int32 nOld(nColumn);
+ nColumn = pDoc->GetNextDifferentChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable), static_cast<SCCOL>(nColumn));
+ for (sal_Int32 i = nOld + 1; i < nColumn; ++i)
+ pColumnStyles->AddFieldStyleName(nTable, i, nIndex, bIsVisible);
+ }
+ if (aCellAddress.EndColumn > nColumns)
+ {
+ bool bIsVisible(true);
+ sal_Int32 nIndex(pColumnStyles->GetStyleNameIndex(nTable, nColumns, bIsVisible));
+ for (sal_Int32 i = nColumns + 1; i <= aCellAddress.EndColumn; ++i)
+ pColumnStyles->AddFieldStyleName(nTable, i, nIndex, bIsVisible);
+ }
+ }
+ uno::Reference<table::XTableRows> xTableRows(xColumnRowRange->getRows());
+ if (xTableRows.is())
+ {
+ sal_Int32 nRows(pDoc->GetLastChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
+ pSharedData->SetLastRow(nTable, nRows);
+
+ pRowStyles->AddNewTable(nTable, pDoc->MaxRow());
+ sal_Int32 nRow = 0;
+ while (nRow <= pDoc->MaxRow())
+ {
+ sal_Int32 nIndex = 0;
+ uno::Reference <beans::XPropertySet> xRowProperties(xTableRows->getByIndex(nRow), uno::UNO_QUERY);
+ if(xRowProperties.is())
+ {
+ AddStyleFromRow( xRowProperties, nullptr, nIndex );
+ pRowStyles->AddFieldStyleName(nTable, nRow, nIndex);
+ }
+ sal_Int32 nOld(nRow);
+ nRow = pDoc->GetNextDifferentChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable), static_cast<SCROW>(nRow));
+ if (nRow > nOld + 1)
+ pRowStyles->AddFieldStyleName(nTable, nOld + 1, nIndex, nRow - 1);
+ }
+ }
+ }
+ ExportCellTextAutoStyles(nTable);
+ }
+
+ pChangeTrackingExportHelper->CollectAutoStyles();
+ }
+
+ if (getExportFlags() & SvXMLExportFlags::MASTERSTYLES)
+ // tdf#154445 - export all page styles even if they are not in use
+ GetPageExport()->collectAutoStyles(false);
+
+ mbAutoStylesCollected = true;
+}
+
+void ScXMLExport::ExportAutoStyles_()
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if (!xSpreadDoc.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if (!xIndex.is())
+ return;
+
+ collectAutoStyles();
+
+ if (getExportFlags() & SvXMLExportFlags::CONTENT)
+ {
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_COLUMN);
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_ROW);
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_TABLE);
+ exportAutoDataStyles();
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_CELL);
+
+ GetShapeExport()->exportAutoStyles();
+ GetFormExport()->exportAutoStyles( );
+
+ if (pDoc)
+ {
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ // #i100879# write the table style for cached tables only if there are cached tables
+ // (same logic as in ExportExternalRefCacheStyles)
+ if (pRefMgr->hasExternalData())
+ {
+ // Special table style for the external ref cache tables.
+ AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, sExternalRefTabStyleName);
+ AddAttribute(XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE);
+ SvXMLElementExport aElemStyle(*this, XML_NAMESPACE_STYLE, XML_STYLE, true, true);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_FALSE);
+ SvXMLElementExport aElemStyleTabProps(*this, XML_NAMESPACE_STYLE, XML_TABLE_PROPERTIES, true, true);
+ }
+ }
+ }
+
+ if (getExportFlags() & SvXMLExportFlags::MASTERSTYLES)
+ {
+ exportAutoDataStyles();
+ GetPageExport()->exportAutoStyles();
+ }
+
+ // #i30251#; only write Text Styles once
+
+ if ((getExportFlags() & SvXMLExportFlags::CONTENT) || (getExportFlags() & SvXMLExportFlags::MASTERSTYLES))
+ GetTextParagraphExport()->exportTextAutoStyles();
+}
+
+void ScXMLExport::ExportMasterStyles_()
+{
+ // tdf#154445 - export all page styles even if they are not in use
+ GetPageExport()->exportMasterStyles( false );
+}
+
+void ScXMLExport::CollectInternalShape( uno::Reference< drawing::XShape > const & xShape )
+{
+ // detective objects and notes
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape( xShape );
+ if( !pObject )
+ return;
+
+ // collect note caption objects from all layers (internal or hidden)
+ if( ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObject, static_cast< SCTAB >( nCurrentTable ) ) )
+ {
+ if(pDoc->GetNote(pCaptData->maStart))
+ {
+ pSharedData->AddNoteObj( xShape, pCaptData->maStart );
+
+ // #i60851# When the file is saved while editing a new note,
+ // the cell is still empty -> last column/row must be updated
+ OSL_ENSURE( pCaptData->maStart.Tab() == nCurrentTable, "invalid table in object data" );
+ pSharedData->SetLastColumn( nCurrentTable, pCaptData->maStart.Col() );
+ pSharedData->SetLastRow( nCurrentTable, pCaptData->maStart.Row() );
+ }
+ }
+ // other objects from internal layer only (detective)
+ else if( pObject->GetLayer() == SC_LAYER_INTERN )
+ {
+ ScDetectiveFunc aDetFunc( *pDoc, static_cast<SCTAB>(nCurrentTable) );
+ ScAddress aPosition;
+ ScRange aSourceRange;
+ bool bRedLine;
+ ScDetectiveObjType eObjType = aDetFunc.GetDetectiveObjectType(
+ pObject, nCurrentTable, aPosition, aSourceRange, bRedLine );
+ pSharedData->GetDetectiveObjContainer()->AddObject( eObjType, static_cast<SCTAB>(nCurrentTable), aPosition, aSourceRange, bRedLine );
+ }
+}
+
+bool ScXMLExport::GetMerged (const table::CellRangeAddress* pCellAddress,
+ const uno::Reference <sheet::XSpreadsheet>& xTable)
+{
+ bool bReady(false);
+ sal_Int32 nRow(pCellAddress->StartRow);
+ sal_Int32 nCol(pCellAddress->StartColumn);
+ sal_Int32 nEndRow(pCellAddress->EndRow);
+ sal_Int32 nEndCol(pCellAddress->EndColumn);
+ bool bRowInc(nEndRow > nRow);
+ while(!bReady && nRow <= nEndRow && nCol <= nEndCol)
+ {
+ uno::Reference<sheet::XSheetCellRange> xSheetCellRange(xTable->getCellRangeByPosition(nCol, nRow, nCol, nRow), uno::UNO_QUERY);
+ if (xSheetCellRange.is())
+ {
+ uno::Reference<sheet::XSheetCellCursor> xCursor(xTable->createCursorByRange(xSheetCellRange));
+ if(xCursor.is())
+ {
+ uno::Reference<sheet::XCellRangeAddressable> xCellAddress (xCursor, uno::UNO_QUERY);
+ xCursor->collapseToMergedArea();
+ table::CellRangeAddress aCellAddress2(xCellAddress->getRangeAddress());
+ ScRange aScRange( aCellAddress2.StartColumn, aCellAddress2.StartRow, aCellAddress2.Sheet,
+ aCellAddress2.EndColumn, aCellAddress2.EndRow, aCellAddress2.Sheet );
+
+ if ((aScRange.aEnd.Row() > nRow ||
+ aScRange.aEnd.Col() > nCol) &&
+ aScRange.aStart.Row() == nRow &&
+ aScRange.aStart.Col() == nCol)
+ {
+ pMergedRangesContainer->AddRange(aScRange);
+ pSharedData->SetLastColumn(aScRange.aEnd.Tab(), aScRange.aEnd.Col());
+ pSharedData->SetLastRow(aScRange.aEnd.Tab(), aScRange.aEnd.Row());
+ }
+ else
+ bReady = true;
+ }
+ }
+ if (!bReady)
+ {
+ if (bRowInc)
+ ++nRow;
+ else
+ ++nCol;
+ }
+ }
+ OSL_ENSURE(!(!bReady && nEndRow > nRow && nEndCol > nCol), "should not be possible");
+ return !bReady;
+}
+
+bool ScXMLExport::IsMatrix (const ScAddress& aCell,
+ ScRange& aCellAddress, bool& bIsFirst) const
+{
+ bIsFirst = false;
+
+ ScRange aMatrixRange;
+
+ if (pDoc && pDoc->GetMatrixFormulaRange(aCell, aMatrixRange))
+ {
+ aCellAddress = aMatrixRange;
+ if ((aCellAddress.aStart.Col() == aCell.Col() && aCellAddress.aStart.Row() == aCell.Row()) &&
+ (aCellAddress.aEnd.Col() > aCell.Col() || aCellAddress.aEnd.Row() > aCell.Row()))
+ {
+ bIsFirst = true;
+ return true;
+ }
+ else if (aCellAddress.aStart.Col() != aCell.Col() || aCellAddress.aStart.Row() != aCell.Row() ||
+ aCellAddress.aEnd.Col() != aCell.Col() || aCellAddress.aEnd.Row()!= aCell.Row())
+ return true;
+ else
+ {
+ bIsFirst = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScXMLExport::WriteTable(sal_Int32 nTable, const uno::Reference<sheet::XSpreadsheet>& xTable)
+{
+ if (!xTable.is())
+ return;
+
+ xCurrentTable.set(xTable);
+ uno::Reference<container::XNamed> xName (xTable, uno::UNO_QUERY );
+ if (!xName.is())
+ return;
+
+ nCurrentTable = sal::static_int_cast<sal_uInt16>( nTable );
+ OUString sOUTableName(xName->getName());
+ AddAttribute(sAttrName, sOUTableName);
+ AddAttribute(sAttrStyleName, aTableStyles[nTable]);
+
+ uno::Reference<util::XProtectable> xProtectable (xTable, uno::UNO_QUERY);
+ const ScTableProtection* pProtect = nullptr;
+ if (xProtectable.is() && xProtectable->isProtected())
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE);
+ if (pDoc)
+ {
+ pProtect = pDoc->GetTabProtection(nTable);
+ if (pProtect)
+ {
+ OUStringBuffer aBuffer;
+ ScPasswordHash eHashUsed = PASSHASH_UNSPECIFIED;
+ if (pProtect->hasPasswordHash(PASSHASH_SHA1))
+ {
+ ::comphelper::Base64::encode(aBuffer,
+ pProtect->getPasswordHash(PASSHASH_SHA1));
+ eHashUsed = PASSHASH_SHA1;
+ }
+ else if (pProtect->hasPasswordHash(PASSHASH_SHA256))
+ {
+ ::comphelper::Base64::encode(aBuffer,
+ pProtect->getPasswordHash(PASSHASH_SHA256));
+ eHashUsed = PASSHASH_SHA256;
+ }
+ else if (pProtect->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
+ {
+ // Double-hash this by SHA1 on top of the legacy xls hash.
+ uno::Sequence<sal_Int8> aHash = pProtect->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
+ ::comphelper::Base64::encode(aBuffer, aHash);
+ eHashUsed = PASSHASH_XL;
+ }
+ if (!aBuffer.isEmpty())
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY, aBuffer.makeStringAndClear());
+ if (getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+ {
+ if (eHashUsed == PASSHASH_XL)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_XL));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA1)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA256)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA256));
+ }
+ }
+ }
+ }
+ }
+ }
+ OUString sPrintRanges;
+ ScRange aColumnHeaderRange;
+ bool bHasColumnHeader;
+ GetColumnRowHeader(bHasColumnHeader, aColumnHeaderRange, bHasRowHeader, aRowHeaderRange, sPrintRanges);
+ if( !sPrintRanges.isEmpty() )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_PRINT_RANGES, sPrintRanges );
+ else if (pDoc && !pDoc->IsPrintEntireSheet(static_cast<SCTAB>(nTable)))
+ AddAttribute( XML_NAMESPACE_TABLE, XML_PRINT, XML_FALSE);
+ SvXMLElementExport aElemT(*this, sElemTab, true, true);
+
+ if (pProtect && pProtect->isProtected() && getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ if (pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_SELECT_PROTECTED_CELLS, XML_TRUE);
+ if (pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_SELECT_UNPROTECTED_CELLS, XML_TRUE);
+
+ if (pProtect->isOptionEnabled(ScTableProtection::INSERT_COLUMNS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_INSERT_COLUMNS, XML_TRUE);
+ if (pProtect->isOptionEnabled(ScTableProtection::INSERT_ROWS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_INSERT_ROWS, XML_TRUE);
+
+ if (pProtect->isOptionEnabled(ScTableProtection::DELETE_COLUMNS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_DELETE_COLUMNS, XML_TRUE);
+ if (pProtect->isOptionEnabled(ScTableProtection::DELETE_ROWS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_DELETE_ROWS, XML_TRUE);
+
+ OUString aElemName = GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_LO_EXT, GetXMLToken(XML_TABLE_PROTECTION));
+
+ SvXMLElementExport aElemProtected(*this, aElemName, true, true);
+ }
+
+ CheckAttrList();
+
+ if ( pDoc && pDoc->GetSheetEvents( static_cast<SCTAB>(nTable) ) &&
+ getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+ {
+ // store sheet events
+ uno::Reference<document::XEventsSupplier> xSupplier(xTable, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xEvents = xSupplier->getEvents();
+ GetEventExport().ExportExt( xEvents );
+ }
+
+ WriteTableSource();
+ WriteScenario();
+ uno::Reference<drawing::XDrawPage> xDrawPage;
+ if (pSharedData->HasForm(nTable, xDrawPage) && xDrawPage.is())
+ {
+ ::xmloff::OOfficeFormsExport aForms(*this);
+ GetFormExport()->exportForms( xDrawPage );
+ bool bRet(GetFormExport()->seekPage( xDrawPage ));
+ OSL_ENSURE( bRet, "OFormLayerXMLExport::seekPage failed!" );
+ }
+ if (pSharedData->HasDrawPage())
+ {
+ GetShapeExport()->seekShapes(pSharedData->GetDrawPage(nTable));
+ WriteTableShapes();
+ }
+ table::CellRangeAddress aRange(GetEndAddress(xTable));
+ pSharedData->SetLastColumn(nTable, aRange.EndColumn);
+ pSharedData->SetLastRow(nTable, aRange.EndRow);
+ mpCellsItr->SetCurrentTable(static_cast<SCTAB>(nTable), xCurrentTable);
+ pGroupColumns->NewTable();
+ pGroupRows->NewTable();
+ FillColumnRowGroups();
+ if (bHasColumnHeader)
+ pSharedData->SetLastColumn(nTable, aColumnHeaderRange.aEnd.Col());
+ bRowHeaderOpen = false;
+ if (bHasRowHeader)
+ pSharedData->SetLastRow(nTable, aRowHeaderRange.aEnd.Row());
+ pDefaults->FillDefaultStyles(nTable, pSharedData->GetLastRow(nTable),
+ pSharedData->GetLastColumn(nTable), pCellStyles.get(), pDoc);
+ pRowFormatRanges->SetColDefaults(&pDefaults->GetColDefaults());
+ pCellStyles->SetColDefaults(&pDefaults->GetColDefaults());
+ ExportColumns(nTable, aColumnHeaderRange, bHasColumnHeader);
+ bool bIsFirst(true);
+ sal_Int32 nEqualCells(0);
+ ScMyCell aCell;
+ ScMyCell aPrevCell;
+ while (mpCellsItr->GetNext(aCell, pCellStyles.get()))
+ {
+ if (bIsFirst)
+ {
+ ExportFormatRanges(0, 0, aCell.maCellAddress.Col()-1, aCell.maCellAddress.Row(), nTable);
+ aPrevCell = aCell;
+ bIsFirst = false;
+ }
+ else
+ {
+ if ((aPrevCell.maCellAddress.Row() == aCell.maCellAddress.Row()) &&
+ (aPrevCell.maCellAddress.Col() + nEqualCells + 1 == aCell.maCellAddress.Col()))
+ {
+ if(IsCellEqual(aPrevCell, aCell))
+ ++nEqualCells;
+ else
+ {
+ WriteCell(aPrevCell, nEqualCells);
+ nEqualCells = 0;
+ aPrevCell = aCell;
+ }
+ }
+ else
+ {
+ WriteCell(aPrevCell, nEqualCells);
+ ExportFormatRanges(aPrevCell.maCellAddress.Col() + nEqualCells + 1, aPrevCell.maCellAddress.Row(),
+ aCell.maCellAddress.Col()-1, aCell.maCellAddress.Row(), nTable);
+ nEqualCells = 0;
+ aPrevCell = aCell;
+ }
+ }
+ }
+ if (!bIsFirst)
+ {
+ WriteCell(aPrevCell, nEqualCells);
+ ExportFormatRanges(aPrevCell.maCellAddress.Col() + nEqualCells + 1, aPrevCell.maCellAddress.Row(),
+ pSharedData->GetLastColumn(nTable), pSharedData->GetLastRow(nTable), nTable);
+ }
+ else
+ ExportFormatRanges(0, 0, pSharedData->GetLastColumn(nTable), pSharedData->GetLastRow(nTable), nTable);
+
+ CloseRow(pSharedData->GetLastRow(nTable));
+
+ if (!pDoc)
+ return;
+
+ // Export sheet-local named ranges.
+ ScRangeName* pRangeName = pDoc->GetRangeName(nTable);
+ if (pRangeName && !pRangeName->empty())
+ {
+ WriteNamedRange(pRangeName);
+ }
+
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ //export new conditional format information
+ ExportConditionalFormat(nTable);
+ exportSparklineGroups(nTable);
+ }
+}
+
+namespace {
+
+void writeContent(
+ ScXMLExport& rExport, const OUString& rStyleName, const OUString& rContent, const SvxFieldData* pField )
+{
+ std::unique_ptr<SvXMLElementExport> pElem;
+ if (!rStyleName.isEmpty())
+ {
+ // Formatted section with automatic style.
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_STYLE_NAME, rStyleName);
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_SPAN));
+ pElem.reset(new SvXMLElementExport(rExport, aElemName, false, false));
+ }
+
+ if (pField)
+ {
+ // Write a field item.
+ OUString aFieldVal = ScEditUtil::GetCellFieldValue(*pField, rExport.GetDocument(), nullptr, nullptr);
+ switch (pField->GetClassId())
+ {
+ case text::textfield::Type::URL:
+ {
+ // <text:a xlink:href="url" xlink:type="simple">value</text:a>
+
+ const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
+ const OUString& aURL = pURLField->GetURL();
+ rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, rExport.GetRelativeReference(aURL));
+ rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, "simple");
+ const OUString& aTargetFrame = pURLField->GetTargetFrame();
+ if (!aTargetFrame.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, aTargetFrame);
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_A));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ case text::textfield::Type::DATE:
+ {
+ // <text:date style:data-style-name="N2" text:date-value="YYYY-MM-DD">value</text:date>
+
+ Date aDate(Date::SYSTEM);
+ OUStringBuffer aBuf;
+ sal_Int32 nVal = aDate.GetYear();
+ aBuf.append(OUString::number(nVal) + "-");
+ nVal = aDate.GetMonth();
+ if (nVal < 10)
+ aBuf.append('0');
+ aBuf.append(OUString::number(nVal) + "-");
+ nVal = aDate.GetDay();
+ if (nVal < 10)
+ aBuf.append('0');
+ aBuf.append(nVal);
+ rExport.AddAttribute(XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, "N2");
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_DATE_VALUE, aBuf.makeStringAndClear());
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_DATE));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ {
+ // <text:title>value</text:title>
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_TITLE));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ case text::textfield::Type::TABLE:
+ {
+ // <text:sheet-name>value</text:sheet-name>
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_SHEET_NAME));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ default:
+ rExport.Characters(aFieldVal);
+ }
+ }
+ else
+ rExport.Characters(rContent);
+}
+
+void flushParagraph(
+ ScXMLExport& rExport, std::u16string_view rParaText,
+ rtl::Reference<XMLPropertySetMapper> const & xMapper, rtl::Reference<SvXMLAutoStylePoolP> const & xStylePool,
+ const ScXMLEditAttributeMap& rAttrMap,
+ std::vector<editeng::Section>::const_iterator it, std::vector<editeng::Section>::const_iterator const & itEnd )
+{
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
+ SvXMLElementExport aElemP(rExport, aElemName, false, false);
+
+ for (; it != itEnd; ++it)
+ {
+ const editeng::Section& rSec = *it;
+
+ OUString aContent(rParaText.substr(rSec.mnStart, rSec.mnEnd - rSec.mnStart));
+
+ std::vector<XMLPropertyState> aPropStates;
+ const SvxFieldData* pField = toXMLPropertyStates(rExport, aPropStates, rSec.maAttributes, xMapper, rAttrMap);
+ OUString aStyleName = xStylePool->Find(XmlStyleFamily::TEXT_TEXT, OUString(), aPropStates);
+ if (aContent == "\x001" && !pField)
+ {
+ for (const SfxPoolItem* p : rSec.maAttributes)
+ {
+ if (p->Which() == EE_FEATURE_TAB)
+ {
+ SvXMLElementExport Tab(rExport, XML_NAMESPACE_TEXT, XML_TAB, false, false);
+ break;
+ }
+ }
+ }
+ else
+ writeContent(rExport, aStyleName, aContent, pField);
+ }
+}
+
+}
+
+void ScXMLExport::WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount)
+{
+ // nEqualCellCount is the number of additional cells
+ SetRepeatAttribute(nEqualCellCount, (aCell.nType != table::CellContentType_EMPTY));
+
+ if (aCell.nStyleIndex != -1)
+ AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(aCell.nStyleIndex, aCell.bIsAutoStyle));
+ if (aCell.nValidationIndex > -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(aCell.nValidationIndex));
+ const bool bIsFirstMatrixCell(aCell.bIsMatrixBase);
+ if (bIsFirstMatrixCell)
+ {
+ SCCOL nColumns( aCell.aMatrixRange.aEnd.Col() - aCell.aMatrixRange.aStart.Col() + 1 );
+ SCROW nRows( aCell.aMatrixRange.aEnd.Row() - aCell.aMatrixRange.aStart.Row() + 1 );
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED, OUString::number(nColumns));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED, OUString::number(nRows));
+ }
+ bool bIsEmpty(false);
+ switch (aCell.nType)
+ {
+ case table::CellContentType_EMPTY :
+ {
+ bIsEmpty = true;
+ }
+ break;
+ case table::CellContentType_VALUE :
+ {
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, aCell.maBaseCell.getDouble());
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, aCell.maBaseCell.getDouble(), false, XML_NAMESPACE_CALC_EXT, false);
+ }
+ break;
+ case table::CellContentType_TEXT :
+ {
+ OUString sFormattedString(lcl_GetFormattedString(pDoc, aCell.maBaseCell, aCell.maCellAddress));
+ OUString sCellString = aCell.maBaseCell.getString(pDoc);
+ bool bExportValue = sCellString.indexOf('\x001') == -1;
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ sCellString, sFormattedString, bExportValue);
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ sCellString, sFormattedString, false, XML_NAMESPACE_CALC_EXT);
+ }
+ break;
+ case table::CellContentType_FORMULA :
+ {
+ if (aCell.maBaseCell.getType() == CELLTYPE_FORMULA)
+ {
+ const bool bIsMatrix(bIsFirstMatrixCell || aCell.bIsMatrixCovered);
+ ScFormulaCell* pFormulaCell = aCell.maBaseCell.getFormula();
+ if (!bIsMatrix || bIsFirstMatrixCell)
+ {
+ if (!mpCompileFormulaCxt)
+ {
+ const formula::FormulaGrammar::Grammar eGrammar = pDoc->GetStorageGrammar();
+ mpCompileFormulaCxt.reset(new sc::CompileFormulaContext(*pDoc, eGrammar));
+ }
+
+ OUString aFormula = pFormulaCell->GetFormula(*mpCompileFormulaCxt);
+ sal_uInt16 nNamespacePrefix =
+ (mpCompileFormulaCxt->getGrammar() == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
+
+ if (!bIsMatrix)
+ {
+ AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey(nNamespacePrefix, aFormula, false));
+ }
+ else
+ {
+ AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey(nNamespacePrefix, aFormula.copy(1, aFormula.getLength()-2), false));
+ }
+ }
+ if (pFormulaCell->GetErrCode() != FormulaError::NONE)
+ {
+ AddAttribute(sAttrValueType, XML_STRING);
+ AddAttribute(sAttrStringValue, aCell.maBaseCell.getString(pDoc));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ //export calcext:value-type="error"
+ AddAttribute(XML_NAMESPACE_CALC_EXT,XML_VALUE_TYPE, OUString("error"));
+ }
+ }
+ else if (pFormulaCell->IsValue())
+ {
+ bool bIsStandard;
+ OUString sCurrency;
+ GetNumberFormatAttributesExportHelper()->GetCellType(aCell.nNumberFormat, sCurrency, bIsStandard);
+ if (pDoc)
+ {
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, pDoc->GetValue(aCell.maCellAddress));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, pDoc->GetValue(aCell.maCellAddress), false, XML_NAMESPACE_CALC_EXT, false );
+ }
+ }
+ }
+ else
+ {
+ if (!aCell.maBaseCell.getString(pDoc).isEmpty())
+ {
+ AddAttribute(sAttrValueType, XML_STRING);
+ AddAttribute(sAttrStringValue, aCell.maBaseCell.getString(pDoc));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT,XML_VALUE_TYPE, XML_STRING);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ OUString* pCellString(&sElemCell);
+ if (aCell.bIsCovered)
+ {
+ pCellString = &sElemCoveredCell;
+ }
+ else
+ {
+ if (aCell.bIsMergedBase)
+ {
+ SCCOL nColumns( aCell.aMergeRange.aEnd.Col() - aCell.aMergeRange.aStart.Col() + 1 );
+ SCROW nRows( aCell.aMergeRange.aEnd.Row() - aCell.aMergeRange.aStart.Row() + 1 );
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, OUString::number(nColumns));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, OUString::number(nRows));
+ }
+ }
+ SvXMLElementExport aElemC(*this, *pCellString, true, true);
+ CheckAttrList();
+ WriteAreaLink(aCell);
+ WriteAnnotation(aCell);
+ WriteDetective(aCell);
+
+ if (!bIsEmpty)
+ {
+ if (aCell.maBaseCell.getType() == CELLTYPE_EDIT)
+ {
+ WriteEditCell(aCell.maBaseCell.getEditText());
+ }
+ else if (aCell.maBaseCell.getType() == CELLTYPE_FORMULA && aCell.maBaseCell.getFormula()->IsMultilineResult())
+ {
+ WriteMultiLineFormulaResult(aCell.maBaseCell.getFormula());
+ }
+ else
+ {
+ SvXMLElementExport aElemP(*this, sElemP, true, false);
+
+ OUString aParaStr =
+ ScCellFormat::GetOutputString(*pDoc, aCell.maCellAddress, aCell.maBaseCell);
+
+ bool bPrevCharWasSpace = true;
+ GetTextParagraphExport()->exportCharacterData(aParaStr, bPrevCharWasSpace);
+ }
+ }
+ WriteShapes(aCell);
+ if (!bIsEmpty)
+ IncrementProgressBar(false);
+}
+
+void ScXMLExport::WriteEditCell(const EditTextObject* pText)
+{
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetTextParagraphExport()->GetTextPropMapper()->getPropertySetMapper();
+ rtl::Reference<SvXMLAutoStylePoolP> xStylePool = GetAutoStylePool();
+ const ScXMLEditAttributeMap& rAttrMap = GetEditAttributeMap();
+
+ // Get raw paragraph texts first.
+ std::vector<OUString> aParaTexts;
+ sal_Int32 nParaCount = pText->GetParagraphCount();
+ aParaTexts.reserve(nParaCount);
+ for (sal_Int32 i = 0; i < nParaCount; ++i)
+ aParaTexts.push_back(pText->GetText(i));
+
+ // Get all section data and iterate through them.
+ std::vector<editeng::Section> aAttrs;
+ pText->GetAllSections(aAttrs);
+ std::vector<editeng::Section>::const_iterator itSec = aAttrs.begin(), itSecEnd = aAttrs.end();
+ std::vector<editeng::Section>::const_iterator itPara = itSec;
+ sal_Int32 nCurPara = 0; // current paragraph
+ for (; itSec != itSecEnd; ++itSec)
+ {
+ const editeng::Section& rSec = *itSec;
+ if (nCurPara == rSec.mnParagraph)
+ // Still in the same paragraph.
+ continue;
+
+ // Start of a new paragraph. Flush the old paragraph.
+ flushParagraph(*this, aParaTexts[nCurPara], xMapper, xStylePool, rAttrMap, itPara, itSec);
+ nCurPara = rSec.mnParagraph;
+ itPara = itSec;
+ }
+
+ flushParagraph(*this, aParaTexts[nCurPara], xMapper, xStylePool, rAttrMap, itPara, itSecEnd);
+}
+
+void ScXMLExport::WriteMultiLineFormulaResult(const ScFormulaCell* pCell)
+{
+ OUString aElemName = GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
+
+ OUString aResStr = pCell->GetResultString().getString();
+ const sal_Unicode* p = aResStr.getStr();
+ const sal_Unicode* pEnd = p + static_cast<size_t>(aResStr.getLength());
+ const sal_Unicode* pPara = p; // paragraph head.
+ for (; p != pEnd; ++p)
+ {
+ if (*p != '\n')
+ continue;
+
+ // flush the paragraph.
+ OUString aContent;
+ if (*pPara == '\n')
+ ++pPara;
+ if (p > pPara)
+ aContent = OUString(pPara, p-pPara);
+
+ SvXMLElementExport aElem(*this, aElemName, false, false);
+ Characters(aContent);
+
+ pPara = p;
+ }
+
+ OUString aContent;
+ if (*pPara == '\n')
+ ++pPara;
+ if (pEnd > pPara)
+ aContent = OUString(pPara, pEnd-pPara);
+
+ SvXMLElementExport aElem(*this, aElemName, false, false);
+ Characters(aContent);
+}
+
+void ScXMLExport::ExportShape(const uno::Reference < drawing::XShape >& xShape, awt::Point* pPoint)
+{
+ uno::Reference < beans::XPropertySet > xShapeProps ( xShape, uno::UNO_QUERY );
+ bool bIsChart( false );
+ if (xShapeProps.is())
+ {
+ sal_Int32 nZOrder = 0;
+ if (xShapeProps->getPropertyValue("ZOrder") >>= nZOrder)
+ {
+ AddAttribute(XML_NAMESPACE_DRAW, XML_ZINDEX, OUString::number(nZOrder));
+ }
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xShapeProps->getPropertySetInfo();
+ OUString sPropCLSID ("CLSID");
+ if( xPropSetInfo->hasPropertyByName( sPropCLSID ) )
+ {
+ OUString sCLSID;
+ if (xShapeProps->getPropertyValue( sPropCLSID ) >>= sCLSID)
+ {
+ if ( sCLSID.equalsIgnoreAsciiCase(GetChartExport()->getChartCLSID()) )
+ {
+ // we have a chart
+ OUString sRanges;
+ if ( pDoc )
+ {
+ OUString aChartName;
+ xShapeProps->getPropertyValue( "PersistName" ) >>= aChartName;
+ ScChartListenerCollection* pCollection = pDoc->GetChartListenerCollection();
+ if (pCollection)
+ {
+ ScChartListener* pListener = pCollection->findByName(aChartName);
+ if (pListener)
+ {
+ const ScRangeListRef& rRangeList = pListener->GetRangeList();
+ if ( rRangeList.is() )
+ {
+ ScRangeStringConverter::GetStringFromRangeList( sRanges, rRangeList.get(), pDoc, FormulaGrammar::CONV_OOO );
+ if ( !sRanges.isEmpty() )
+ {
+ bIsChart = true;
+ rtl::Reference<comphelper::AttributeList> pAttrList = new comphelper::AttributeList();
+ pAttrList->AddAttribute(
+ GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_DRAW, GetXMLToken( XML_NOTIFY_ON_UPDATE_OF_RANGES ) ), sRanges );
+ GetShapeExport()->exportShape( xShape, SEF_DEFAULT, pPoint, pAttrList.get() );
+ }
+ }
+ }
+ }
+ }
+
+ if ( sRanges.isEmpty() )
+ {
+ uno::Reference< frame::XModel > xChartModel;
+ if( ( xShapeProps->getPropertyValue( "Model" ) >>= xChartModel ) &&
+ xChartModel.is())
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc( xChartModel, uno::UNO_QUERY );
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartModel, uno::UNO_QUERY );
+ if( xChartDoc.is() && xReceiver.is() &&
+ ! xChartDoc->hasInternalDataProvider())
+ {
+ // we have a chart that gets its data from Calc
+ bIsChart = true;
+ uno::Sequence< OUString > aRepresentations(
+ xReceiver->getUsedRangeRepresentations());
+ rtl::Reference<comphelper::AttributeList> pAttrList;
+ try
+ {
+ if (aRepresentations.hasElements())
+ {
+ // add the ranges used by the chart to the shape
+ // element to be able to start listening after
+ // load (when the chart is not yet loaded)
+ uno::Reference< chart2::data::XRangeXMLConversion > xRangeConverter( xChartDoc->getDataProvider(), uno::UNO_QUERY );
+ sRanges = lcl_RangeSequenceToString( aRepresentations, xRangeConverter );
+ pAttrList = new comphelper::AttributeList();
+ pAttrList->AddAttribute(
+ GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_DRAW, GetXMLToken(XML_NOTIFY_ON_UPDATE_OF_RANGES) ), sRanges );
+ }
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION("sc", "Exception in lcl_RangeSequenceToString - invalid range?");
+ }
+ GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint, pAttrList.get());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!bIsChart)
+ GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint);
+
+ IncrementProgressBar(false);
+}
+
+void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
+{
+ if( !(rMyCell.bHasShape && !rMyCell.aShapeList.empty() && pDoc) )
+ return;
+
+ // Reference point to turn absolute coordinates in reference point + offset. That happens in most
+ // cases in XMLShapeExport::ImpExportNewTrans_DecomposeAndRefPoint, which gets the absolute
+ // coordinates as translation from matrix in property "Transformation". For cell anchored shapes
+ // the reference point is left-top (in LTR mode) of that cell, which contains the shape.
+ tools::Rectangle aCellRectFull = pDoc->GetMMRect(
+ rMyCell.maCellAddress.Col(), rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Col(),
+ rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Tab(), false /*bHiddenAsZero*/);
+ awt::Point aPoint;
+ bool bNegativePage = pDoc->IsNegativePage(rMyCell.maCellAddress.Tab());
+ if (bNegativePage)
+ aPoint.X = aCellRectFull.Right();
+ else
+ aPoint.X = aCellRectFull.Left();
+ aPoint.Y = aCellRectFull.Top();
+
+ for (const auto& rShape : rMyCell.aShapeList)
+ {
+ // Skip the shape if requirements are not met. The tests should not fail, but allow
+ // shorter conditions in main part below.
+ if (!rShape.xShape.is())
+ continue;
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape.xShape);
+ if (!pObj)
+ continue;
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
+ if (!pObjData)
+ continue;
+ ScAddress aSnapStartAddress = pObjData->maStart;
+ if (!aSnapStartAddress.IsValid())
+ continue;
+
+ // The current object geometry is based on bHiddenAsZero=true, but ODF file format
+ // needs it as if there were no hidden rows or columns. We determine a fictive snap
+ // rectangle from the anchor as if all column/rows are shown. Then we move and resize
+ // (in case of "resize with cell") the object to meet this snap rectangle. We need to
+ // manipulate the object itself, because the used methods in xmloff do not evaluate the
+ // ObjData. This manipulation is only done temporarily for export. Thus we stash the geometry
+ // and restore it when export is done and we use NbcFoo methods.
+ bool bNeedsRestore = false;
+ std::unique_ptr<SdrObjGeoData> pGeoData = pObj->GetGeoData();
+
+ // Determine top point of fictive snap rectangle ('Full' rectangle).
+ SCTAB aTab(aSnapStartAddress.Tab());
+ SCCOL aCol(aSnapStartAddress.Col());
+ SCROW aRow(aSnapStartAddress.Row());
+ tools::Rectangle aFullStartCellRect
+ = pDoc->GetMMRect(aCol, aRow, aCol, aRow, aTab, false /*bHiddenAsZero*/);
+ // The reference corner for the offset is top-left in case of LTR and top-right for RTL.
+ Point aFullTopPoint;
+ if (bNegativePage)
+ aFullTopPoint.setX(aFullStartCellRect.Right() - pObjData->maStartOffset.X());
+ else
+ aFullTopPoint.setX(aFullStartCellRect.Left() + pObjData->maStartOffset.X());
+ aFullTopPoint.setY(aFullStartCellRect.Top() + pObjData->maStartOffset.Y());
+
+ // Compare actual top point and full top point and move object accordingly.
+ tools::Rectangle aOrigSnapRect(pObj->GetSnapRect());
+ Point aActualTopPoint = bNegativePage ? aOrigSnapRect.TopRight() : aOrigSnapRect.TopLeft();
+ if (aFullTopPoint != aActualTopPoint)
+ {
+ bNeedsRestore = true;
+ Point aMoveBy = aFullTopPoint - aActualTopPoint;
+ pObj->NbcMove(Size(aMoveBy.X(), aMoveBy.Y()));
+ }
+
+ ScAddress aSnapEndAddress = pObjData->maEnd;
+ // tdf#154005: We treat the combination of "To cell (resize with cell)" with 'size protected'
+ // as being "To cell".
+ if (pObjData->mbResizeWithCell && aSnapEndAddress.IsValid() && !pObj->IsResizeProtect())
+ {
+ // Object is anchored "To cell (resize with cell)". Compare size of actual snap rectangle
+ // and fictive full one. Resize object accordingly.
+ tools::Rectangle aActualSnapRect(pObj->GetSnapRect());
+ Point aSnapEndOffset(pObjData->maEndOffset);
+ aCol = aSnapEndAddress.Col();
+ aRow = aSnapEndAddress.Row();
+ tools::Rectangle aFullEndCellRect
+ = pDoc->GetMMRect(aCol, aRow, aCol, aRow, aTab, false /*bHiddenAsZero*/);
+ Point aFullBottomPoint;
+ if (bNegativePage)
+ aFullBottomPoint.setX(aFullEndCellRect.Right() - aSnapEndOffset.X());
+ else
+ aFullBottomPoint.setX(aFullEndCellRect.Left() + aSnapEndOffset.X());
+ aFullBottomPoint.setY(aFullEndCellRect.Top() + aSnapEndOffset.Y());
+ tools::Rectangle aFullSnapRect(aFullTopPoint, aFullBottomPoint);
+ aFullSnapRect.Normalize();
+
+ if (aFullSnapRect != aActualSnapRect)
+ {
+ bNeedsRestore = true;
+ Fraction aScaleWidth(aFullSnapRect.getOpenWidth(), aActualSnapRect.getOpenWidth());
+ if (!aScaleWidth.IsValid())
+ aScaleWidth = Fraction(1, 1);
+ Fraction aScaleHeight(aFullSnapRect.getOpenHeight(),
+ aActualSnapRect.getOpenHeight());
+ if (!aScaleHeight.IsValid())
+ aScaleHeight = Fraction(1, 1);
+ pObj->NbcResize(aFullTopPoint, aScaleWidth, aScaleHeight);
+ }
+ }
+
+ // The existence of an end address is equivalent to anchor mode "To Cell (resize with cell)".
+ // XML needs end address in regard of untransformed shape. Those are contained in rShape but
+ // could be received from NonRotatedObjData as well.
+ // tdf#154005: We treat the combination of "To Cell (resize with cell)" anchor with 'size
+ // protected' property as being "To cell" anchor.
+ if (pObjData->mbResizeWithCell && !pObj->IsResizeProtect())
+ {
+ OUString sEndAddress;
+ ScRangeStringConverter::GetStringFromAddress(sEndAddress, rShape.aEndAddress, pDoc,
+ FormulaGrammar::CONV_OOO);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_END_CELL_ADDRESS, sEndAddress);
+ OUStringBuffer sBuffer;
+ GetMM100UnitConverter().convertMeasureToXML(sBuffer, rShape.nEndX);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_END_X, sBuffer.makeStringAndClear());
+ GetMM100UnitConverter().convertMeasureToXML(sBuffer, rShape.nEndY);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_END_Y, sBuffer.makeStringAndClear());
+ }
+
+ // Correct above calculated reference point for these cases:
+ // a) For a RTL-sheet translate from matrix is not suitable, because the shape
+ // from xml (which is always LTR) is not mirrored to negative page but shifted.
+ // b) In case of horizontal mirrored, 'resize with cell' anchored custom shape, translate from
+ // matrix has wrong values. FixMe: Why is translate wrong?
+ if (bNegativePage
+ || (pObj->GetObjIdentifier() == SdrObjKind::CustomShape
+ && static_cast<SdrObjCustomShape*>(pObj)->IsMirroredX()
+ && pObjData->mbResizeWithCell))
+ {
+ // In these cases we set reference point so that the offset calculation in XML export
+ // (= matrix translate - reference point) results in maStartOffset.
+ ScDrawObjData* pNRObjData = ScDrawLayer::GetNonRotatedObjData(pObj);
+ if (pNRObjData)
+ {
+ awt::Point aMatrixTranslate = rShape.xShape->getPosition();
+ aPoint.X = aMatrixTranslate.X - pNRObjData->maStartOffset.X();
+ aPoint.Y = aMatrixTranslate.Y - pNRObjData->maStartOffset.Y();
+ }
+ }
+
+ ExportShape(rShape.xShape, &aPoint);
+
+ // Restore object geometry
+ if (bNeedsRestore && pGeoData)
+ pObj->SetGeoData(*pGeoData);
+ }
+}
+
+void ScXMLExport::WriteTableShapes()
+{
+ ScMyTableShapes* pTableShapes(pSharedData->GetTableShapes());
+ if (!pTableShapes || (*pTableShapes)[nCurrentTable].empty())
+ return;
+
+ OSL_ENSURE(pTableShapes->size() > static_cast<size_t>(nCurrentTable), "wrong Table");
+ SvXMLElementExport aShapesElem(*this, XML_NAMESPACE_TABLE, XML_SHAPES, true, false);
+ for (const auto& rxShape : (*pTableShapes)[nCurrentTable])
+ {
+ if (rxShape.is())
+ {
+ if (pDoc->IsNegativePage(static_cast<SCTAB>(nCurrentTable)))
+ {
+ // RTL-mirroring refers to snap rectangle, not to logic rectangle, therefore cannot use
+ // getPosition() and getSize(), but need property "FrameRect" from rxShape or
+ // GetSnapRect() from associated SdrObject.
+ uno::Reference<beans::XPropertySet> xShapeProp(rxShape, uno::UNO_QUERY);
+ awt::Rectangle aFrameRect;
+ if (xShapeProp.is() && (xShapeProp->getPropertyValue("FrameRect") >>= aFrameRect))
+ {
+ // file format uses shape in LTR mode. newLeft = - oldRight = - (oldLeft + width).
+ // newTranslate = oldTranslate - refPoint, oldTranslate from transformation matrix,
+ // calculated in XMLShapeExport::exportShape common for all modules.
+ // oldTranslate.X = oldLeft ==> refPoint.X = 2 * oldLeft + width
+ awt::Point aRefPoint;
+ aRefPoint.X = 2 * aFrameRect.X + aFrameRect.Width - 1;
+ aRefPoint.Y = 0;
+ ExportShape(rxShape, &aRefPoint);
+ }
+ // else should not happen
+ }
+ else
+ ExportShape(rxShape, nullptr);
+ }
+ }
+ (*pTableShapes)[nCurrentTable].clear();
+}
+
+void ScXMLExport::WriteAreaLink( const ScMyCell& rMyCell )
+{
+ if( !rMyCell.bHasAreaLink )
+ return;
+
+ const ScMyAreaLink& rAreaLink = rMyCell.aAreaLink;
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, rAreaLink.sSourceStr );
+ AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
+ AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(rAreaLink.sURL) );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_FILTER_NAME, rAreaLink.sFilter );
+ if( !rAreaLink.sFilterOptions.isEmpty() )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, rAreaLink.sFilterOptions );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LAST_COLUMN_SPANNED, OUString::number(rAreaLink.GetColCount()) );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LAST_ROW_SPANNED, OUString::number(rAreaLink.GetRowCount()) );
+ if( rAreaLink.nRefreshDelaySeconds )
+ {
+ OUStringBuffer sValue;
+ ::sax::Converter::convertDuration( sValue,
+ static_cast<double>(rAreaLink.nRefreshDelaySeconds) / 86400 );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, sValue.makeStringAndClear() );
+ }
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_CELL_RANGE_SOURCE, true, true );
+}
+
+void ScXMLExport::exportAnnotationMeta( const uno::Reference < drawing::XShape >& xShape)
+{
+ ScPostIt* pNote = pCurrentCell->pNote;
+
+ if (!pNote)
+ return;
+
+ // TODO : notes
+ //is it still useful, as this call back is only called from ScXMLExport::WriteAnnotation
+ // and should be in sync with pCurrentCell
+ SdrCaptionObj* pNoteCaption = pNote->GetOrCreateCaption(pCurrentCell->maCellAddress);
+ uno::Reference<drawing::XShape> xCurrentShape( pNoteCaption->getUnoShape(), uno::UNO_QUERY );
+ if (xCurrentShape.get()!=xShape.get())
+ return;
+
+ bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo) && !SvtSecurityOptions::IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo);
+
+ const OUString& sAuthor(pNote->GetAuthor());
+ if (!sAuthor.isEmpty())
+ {
+ SvXMLElementExport aCreatorElem( *this, XML_NAMESPACE_DC,
+ XML_CREATOR, true,
+ false );
+ Characters( bRemovePersonalInfo
+ ? "Author" + OUString::number(SvXMLExport::GetInfoID(sAuthor))
+ : sAuthor );
+ }
+
+ const OUString& aDate(bRemovePersonalInfo ? OUString("1970-01-01") : pNote->GetDate()); // Epoch time
+ if (pDoc)
+ {
+ SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
+ double fDate;
+ sal_uInt32 nfIndex = pNumForm->GetFormatIndex(NF_DATE_SYS_DDMMYYYY, LANGUAGE_SYSTEM);
+ if (pNumForm->IsNumberFormat(aDate, nfIndex, fDate))
+ {
+ OUStringBuffer sBuf;
+ GetMM100UnitConverter().convertDateTime(sBuf, fDate,true);
+ SvXMLElementExport aDateElem( *this, XML_NAMESPACE_DC,
+ XML_DATE, true,
+ false );
+ Characters(sBuf.makeStringAndClear());
+ }
+ else
+ {
+ SvXMLElementExport aDateElem( *this, XML_NAMESPACE_META,
+ XML_DATE_STRING, true,
+ false );
+ Characters(aDate);
+ }
+ }
+ else
+ {
+ SvXMLElementExport aDateElem( *this, XML_NAMESPACE_META,
+ XML_DATE_STRING, true,
+ false );
+ Characters(aDate);
+ }
+}
+
+void ScXMLExport::WriteAnnotation(const ScMyCell& rMyCell)
+{
+ ScPostIt* pNote = pDoc->GetNote(rMyCell.maCellAddress);
+ if (!pNote)
+ return;
+
+ if (pNote->IsCaptionShown())
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_DISPLAY, XML_TRUE);
+
+ pCurrentCell = &rMyCell;
+
+ SdrCaptionObj* pNoteCaption = pNote->GetOrCreateCaption(rMyCell.maCellAddress);
+ if (pNoteCaption)
+ {
+ uno::Reference<drawing::XShape> xShape( pNoteCaption->getUnoShape(), uno::UNO_QUERY );
+ if (xShape.is())
+ GetShapeExport()->exportShape(xShape, SEF_DEFAULT|XMLShapeExportFlags::ANNOTATION);
+ }
+
+ pCurrentCell = nullptr;
+}
+
+void ScXMLExport::WriteDetective( const ScMyCell& rMyCell )
+{
+ if( !(rMyCell.bHasDetectiveObj || rMyCell.bHasDetectiveOp) )
+ return;
+
+ const ScMyDetectiveObjVec& rObjVec = rMyCell.aDetectiveObjVec;
+ const ScMyDetectiveOpVec& rOpVec = rMyCell.aDetectiveOpVec;
+ sal_Int32 nObjCount(rObjVec.size());
+ sal_Int32 nOpCount(rOpVec.size());
+ if( !(nObjCount || nOpCount) )
+ return;
+
+ SvXMLElementExport aDetElem( *this, XML_NAMESPACE_TABLE, XML_DETECTIVE, true, true );
+ OUString sString;
+ for(const auto& rObj : rObjVec)
+ {
+ if (rObj.eObjType != SC_DETOBJ_CIRCLE)
+ {
+ if( (rObj.eObjType == SC_DETOBJ_ARROW) || (rObj.eObjType == SC_DETOBJ_TOOTHERTAB))
+ {
+ ScRangeStringConverter::GetStringFromRange( sString, rObj.aSourceRange, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sString );
+ }
+ sString = ScXMLConverter::GetStringFromDetObjType( rObj.eObjType );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_DIRECTION, sString );
+ if( rObj.bHasError )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_CONTAINS_ERROR, XML_TRUE );
+ }
+ else
+ AddAttribute( XML_NAMESPACE_TABLE, XML_MARKED_INVALID, XML_TRUE );
+ SvXMLElementExport aRangeElem( *this, XML_NAMESPACE_TABLE, XML_HIGHLIGHTED_RANGE, true, true );
+ }
+ for(const auto& rOp : rOpVec)
+ {
+ OUString sOpString = ScXMLConverter::GetStringFromDetOpType( rOp.eOpType );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, sOpString );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_INDEX, OUString::number(rOp.nIndex) );
+ SvXMLElementExport aRangeElem( *this, XML_NAMESPACE_TABLE, XML_OPERATION, true, true );
+ }
+}
+
+void ScXMLExport::SetRepeatAttribute(sal_Int32 nEqualCellCount, bool bIncProgress)
+{
+ // nEqualCellCount is additional cells, so the attribute value is nEqualCellCount+1
+ if (nEqualCellCount > 0)
+ {
+ sal_Int32 nTemp(nEqualCellCount + 1);
+ OUString sOUEqualCellCount(OUString::number(nTemp));
+ AddAttribute(sAttrColumnsRepeated, sOUEqualCellCount);
+ if (bIncProgress)
+ IncrementProgressBar(false, nEqualCellCount);
+ }
+}
+
+bool ScXMLExport::IsEditCell(const ScMyCell& rCell)
+{
+ return rCell.maBaseCell.getType() == CELLTYPE_EDIT;
+}
+
+bool ScXMLExport::IsCellEqual (const ScMyCell& aCell1, const ScMyCell& aCell2)
+{
+ bool bIsEqual = false;
+ if( !aCell1.bIsMergedBase && !aCell2.bIsMergedBase &&
+ aCell1.bIsCovered == aCell2.bIsCovered &&
+ !aCell1.bIsMatrixBase && !aCell2.bIsMatrixBase &&
+ aCell1.bIsMatrixCovered == aCell2.bIsMatrixCovered &&
+ aCell1.bHasAnnotation == aCell2.bHasAnnotation &&
+ !aCell1.bHasShape && !aCell2.bHasShape &&
+ aCell1.bHasAreaLink == aCell2.bHasAreaLink &&
+ !aCell1.bHasDetectiveObj && !aCell2.bHasDetectiveObj)
+ {
+ if( (aCell1.bHasAreaLink &&
+ (aCell1.aAreaLink.GetColCount() == 1) &&
+ (aCell2.aAreaLink.GetColCount() == 1) &&
+ aCell1.aAreaLink.Compare( aCell2.aAreaLink ) ) ||
+ !aCell1.bHasAreaLink )
+ {
+ if (!aCell1.bHasAnnotation)
+ {
+ if ((((aCell1.nStyleIndex == aCell2.nStyleIndex) && (aCell1.bIsAutoStyle == aCell2.bIsAutoStyle)) ||
+ ((aCell1.nStyleIndex == aCell2.nStyleIndex) && (aCell1.nStyleIndex == -1))) &&
+ aCell1.nValidationIndex == aCell2.nValidationIndex &&
+ aCell1.nType == aCell2.nType)
+ {
+ switch ( aCell1.nType )
+ {
+ case table::CellContentType_EMPTY :
+ {
+ bIsEqual = true;
+ }
+ break;
+ case table::CellContentType_VALUE :
+ {
+ // #i29101# number format may be different from column default styles,
+ // but can lead to different value types, so it must also be compared
+ bIsEqual = (aCell1.nNumberFormat == aCell2.nNumberFormat) &&
+ (aCell1.maBaseCell.getDouble() == aCell2.maBaseCell.getDouble());
+ }
+ break;
+ case table::CellContentType_TEXT :
+ {
+ if (IsEditCell(aCell1) || IsEditCell(aCell2))
+ bIsEqual = false;
+ else
+ {
+ bIsEqual = (aCell1.maBaseCell.getString(pDoc) == aCell2.maBaseCell.getString(pDoc));
+ }
+ }
+ break;
+ case table::CellContentType_FORMULA :
+ {
+ bIsEqual = false;
+ }
+ break;
+ default :
+ {
+ bIsEqual = false;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return bIsEqual;
+}
+
+void ScXMLExport::WriteCalculationSettings(const uno::Reference <sheet::XSpreadsheetDocument>& xSpreadDoc)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(xSpreadDoc, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ bool bCalcAsShown (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_CALCASSHOWN) ));
+ bool bIgnoreCase (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_IGNORECASE) ));
+ bool bLookUpLabels (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_LOOKUPLABELS) ));
+ bool bMatchWholeCell (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_MATCHWHOLE) ));
+ bool bUseRegularExpressions (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_REGEXENABLED) ));
+ bool bUseWildcards (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_WILDCARDSENABLED) ));
+ if (bUseWildcards && bUseRegularExpressions)
+ bUseRegularExpressions = false; // mutually exclusive, wildcards take precedence
+ bool bIsIterationEnabled (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_ITERENABLED) ));
+ sal_uInt16 nYear2000 (pDoc ? pDoc->GetDocOptions().GetYear2000() : 0);
+ sal_Int32 nIterationCount(100);
+ xPropertySet->getPropertyValue( SC_UNO_ITERCOUNT ) >>= nIterationCount;
+ double fIterationEpsilon = 0;
+ xPropertySet->getPropertyValue( SC_UNO_ITEREPSILON ) >>= fIterationEpsilon;
+ util::Date aNullDate;
+ xPropertySet->getPropertyValue( SC_UNO_NULLDATE ) >>= aNullDate;
+ if (!(bCalcAsShown || bIgnoreCase || !bLookUpLabels || !bMatchWholeCell || !bUseRegularExpressions ||
+ bUseWildcards ||
+ bIsIterationEnabled || nIterationCount != 100 || !::rtl::math::approxEqual(fIterationEpsilon, 0.001) ||
+ aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899 || nYear2000 != 1930))
+ return;
+
+ if (bIgnoreCase)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_FALSE);
+ if (bCalcAsShown)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PRECISION_AS_SHOWN, XML_TRUE);
+ if (!bMatchWholeCell)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_SEARCH_CRITERIA_MUST_APPLY_TO_WHOLE_CELL, XML_FALSE);
+ if (!bLookUpLabels)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_AUTOMATIC_FIND_LABELS, XML_FALSE);
+ if (!bUseRegularExpressions)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_USE_REGULAR_EXPRESSIONS, XML_FALSE);
+ if (bUseWildcards)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_USE_WILDCARDS, XML_TRUE);
+ if (nYear2000 != 1930)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NULL_YEAR, OUString::number(nYear2000));
+ }
+ SvXMLElementExport aCalcSettings(*this, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true);
+ {
+ if (aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899)
+ {
+ OUStringBuffer sDate;
+ SvXMLUnitConverter::convertDateTime(sDate, 0.0, aNullDate);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_VALUE, sDate.makeStringAndClear());
+ SvXMLElementExport aElemNullDate(*this, XML_NAMESPACE_TABLE, XML_NULL_DATE, true, true);
+ }
+ if (bIsIterationEnabled || nIterationCount != 100 || !::rtl::math::approxEqual(fIterationEpsilon, 0.001))
+ {
+ if (bIsIterationEnabled)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STATUS, XML_ENABLE);
+ if (nIterationCount != 100)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STEPS, OUString::number(nIterationCount));
+ }
+ if (!::rtl::math::approxEqual(fIterationEpsilon, 0.001))
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDouble(sBuffer,
+ fIterationEpsilon);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_MAXIMUM_DIFFERENCE, sBuffer.makeStringAndClear());
+ }
+ SvXMLElementExport aElemIteration(*this, XML_NAMESPACE_TABLE, XML_ITERATION, true, true);
+ }
+ }
+}
+
+void ScXMLExport::WriteTableSource()
+{
+ uno::Reference <sheet::XSheetLinkable> xLinkable (xCurrentTable, uno::UNO_QUERY);
+ if (!(xLinkable.is() && GetModel().is()))
+ return;
+
+ sheet::SheetLinkMode nMode (xLinkable->getLinkMode());
+ if (nMode == sheet::SheetLinkMode_NONE)
+ return;
+
+ OUString sLink (xLinkable->getLinkUrl());
+ uno::Reference <beans::XPropertySet> xProps (GetModel(), uno::UNO_QUERY);
+ if (!xProps.is())
+ return;
+
+ uno::Reference <container::XIndexAccess> xIndex(xProps->getPropertyValue(SC_UNO_SHEETLINKS), uno::UNO_QUERY);
+ if (!xIndex.is())
+ return;
+
+ sal_Int32 nCount(xIndex->getCount());
+ if (!nCount)
+ return;
+
+ bool bFound(false);
+ uno::Reference <beans::XPropertySet> xLinkProps;
+ for (sal_Int32 i = 0; (i < nCount) && !bFound; ++i)
+ {
+ xLinkProps.set(xIndex->getByIndex(i), uno::UNO_QUERY);
+ if (xLinkProps.is())
+ {
+ OUString sNewLink;
+ if (xLinkProps->getPropertyValue(SC_UNONAME_LINKURL) >>= sNewLink)
+ bFound = sLink == sNewLink;
+ }
+ }
+ if (!(bFound && xLinkProps.is()))
+ return;
+
+ OUString sFilter;
+ OUString sFilterOptions;
+ OUString sTableName (xLinkable->getLinkSheetName());
+ sal_Int32 nRefresh(0);
+ xLinkProps->getPropertyValue(SC_UNONAME_FILTER) >>= sFilter;
+ xLinkProps->getPropertyValue(SC_UNONAME_FILTOPT) >>= sFilterOptions;
+ xLinkProps->getPropertyValue(SC_UNONAME_REFDELAY) >>= nRefresh;
+ if (sLink.isEmpty())
+ return;
+
+ AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(sLink));
+ if (!sTableName.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_NAME, sTableName);
+ if (!sFilter.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_NAME, sFilter);
+ if (!sFilterOptions.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, sFilterOptions);
+ if (nMode != sheet::SheetLinkMode_NORMAL)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_MODE, XML_COPY_RESULTS_ONLY);
+ if( nRefresh )
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDuration( sBuffer,
+ static_cast<double>(nRefresh) / 86400 );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, sBuffer.makeStringAndClear() );
+ }
+ SvXMLElementExport aSourceElem(*this, XML_NAMESPACE_TABLE, XML_TABLE_SOURCE, true, true);
+}
+
+// core implementation
+void ScXMLExport::WriteScenario()
+{
+ if (!(pDoc && pDoc->IsScenario(static_cast<SCTAB>(nCurrentTable))))
+ return;
+
+ OUString sComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ pDoc->GetScenarioData(static_cast<SCTAB>(nCurrentTable), sComment, aColor, nFlags);
+ if (!(nFlags & ScScenarioFlags::ShowFrame))
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_BORDER, XML_FALSE);
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, aColor);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_BORDER_COLOR, aBuffer.makeStringAndClear());
+ if (!(nFlags & ScScenarioFlags::TwoWay))
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_BACK, XML_FALSE);
+ if (!(nFlags & ScScenarioFlags::Attrib))
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_STYLES, XML_FALSE);
+ if (nFlags & ScScenarioFlags::Value)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_FORMULAS, XML_FALSE);
+ if (nFlags & ScScenarioFlags::Protected)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE);
+ ::sax::Converter::convertBool(aBuffer,
+ pDoc->IsActiveScenario(static_cast<SCTAB>(nCurrentTable)));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_IS_ACTIVE, aBuffer.makeStringAndClear());
+ const ScRangeList* pRangeList = pDoc->GetScenarioRanges(static_cast<SCTAB>(nCurrentTable));
+ OUString sRangeListStr;
+ ScRangeStringConverter::GetStringFromRangeList( sRangeListStr, pRangeList, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute(XML_NAMESPACE_TABLE, XML_SCENARIO_RANGES, sRangeListStr);
+ if (!sComment.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COMMENT, sComment);
+ SvXMLElementExport aElem(*this, XML_NAMESPACE_TABLE, XML_SCENARIO, true, true);
+}
+
+void ScXMLExport::WriteTheLabelRanges( const uno::Reference< sheet::XSpreadsheetDocument >& xSpreadDoc )
+{
+ uno::Reference< beans::XPropertySet > xDocProp( xSpreadDoc, uno::UNO_QUERY );
+ if( !xDocProp.is() ) return;
+
+ sal_Int32 nCount(0);
+ uno::Reference< container::XIndexAccess > xColRangesIAccess(xDocProp->getPropertyValue( SC_UNO_COLLABELRNG ), uno::UNO_QUERY);
+ if( xColRangesIAccess.is() )
+ nCount += xColRangesIAccess->getCount();
+
+ uno::Reference< container::XIndexAccess > xRowRangesIAccess(xDocProp->getPropertyValue( SC_UNO_ROWLABELRNG ), uno::UNO_QUERY);
+ if( xRowRangesIAccess.is() )
+ nCount += xRowRangesIAccess->getCount();
+
+ if( nCount )
+ {
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_LABEL_RANGES, true, true );
+ WriteLabelRanges( xColRangesIAccess, true );
+ WriteLabelRanges( xRowRangesIAccess, false );
+ }
+}
+
+void ScXMLExport::WriteLabelRanges( const uno::Reference< container::XIndexAccess >& xRangesIAccess, bool bColumn )
+{
+ if( !xRangesIAccess.is() ) return;
+
+ sal_Int32 nCount(xRangesIAccess->getCount());
+ for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< sheet::XLabelRange > xRange(xRangesIAccess->getByIndex( nIndex ), uno::UNO_QUERY);
+ if( xRange.is() )
+ {
+ OUString sRangeStr;
+ table::CellRangeAddress aCellRange( xRange->getLabelArea() );
+ ScRangeStringConverter::GetStringFromRange( sRangeStr, aCellRange, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LABEL_CELL_RANGE_ADDRESS, sRangeStr );
+ aCellRange = xRange->getDataArea();
+ ScRangeStringConverter::GetStringFromRange( sRangeStr, aCellRange, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_DATA_CELL_RANGE_ADDRESS, sRangeStr );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_ORIENTATION, bColumn ? XML_COLUMN : XML_ROW );
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_LABEL_RANGE, true, true );
+ }
+ }
+}
+
+void ScXMLExport::WriteNamedExpressions()
+{
+ if (!pDoc)
+ return;
+ ScRangeName* pNamedRanges = pDoc->GetRangeName();
+ WriteNamedRange(pNamedRanges);
+}
+
+void ScXMLExport::WriteExternalDataMapping()
+{
+ if (!pDoc)
+ return;
+
+ if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
+ // Export this only for 1.2 extended and above.
+ return;
+
+ sc::ExternalDataMapper& rDataMapper = pDoc->GetExternalDataMapper();
+ auto& rDataSources = rDataMapper.getDataSources();
+
+ if (rDataSources.empty())
+ return;
+
+ SvXMLElementExport aMappings(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_MAPPINGS, true, true);
+ for (const auto& itr : rDataSources)
+ {
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, itr.getURL());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_PROVIDER, itr.getProvider());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATA_FREQUENCY, OUString::number(sc::ExternalDataSource::getUpdateFrequency()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_ID, itr.getID());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATABASE_NAME, itr.getDBName());
+
+ SvXMLElementExport aMapping(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_MAPPING, true, true);
+ // Add the data transformations
+ WriteExternalDataTransformations(itr.getDataTransformation());
+ }
+}
+
+void ScXMLExport::WriteExternalDataTransformations(const std::vector<std::shared_ptr<sc::DataTransformation>>& aDataTransformations)
+{
+ SvXMLElementExport aTransformations(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_TRANSFORMATIONS, true, true);
+ for (auto& itr : aDataTransformations)
+ {
+ sc::TransformationType aTransformationType = itr->getTransformationType();
+
+ switch(aTransformationType)
+ {
+ case sc::TransformationType::DELETE_TRANSFORMATION:
+ {
+ // Delete Columns Transformation
+ std::shared_ptr<sc::ColumnRemoveTransformation> aDeleteTransformation = std::dynamic_pointer_cast<sc::ColumnRemoveTransformation>(itr);
+ std::set<SCCOL> aColumns = aDeleteTransformation->getColumns();
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_REMOVE_TRANSFORMATION, true, true);
+ for(auto& col : aColumns)
+ {
+ // Add Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::SPLIT_TRANSFORMATION:
+ {
+ std::shared_ptr<sc::SplitColumnTransformation> aSplitTransformation = std::dynamic_pointer_cast<sc::SplitColumnTransformation>(itr);
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(aSplitTransformation->getColumn()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SEPARATOR, OUString::number(aSplitTransformation->getSeparator()));
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_SPLIT_TRANSFORMATION, true, true);
+ }
+ break;
+ case sc::TransformationType::MERGE_TRANSFORMATION:
+ {
+ // Merge Transformation
+ std::shared_ptr<sc::MergeColumnTransformation> aMergeTransformation = std::dynamic_pointer_cast<sc::MergeColumnTransformation>(itr);
+ std::set<SCCOL> aColumns = aMergeTransformation->getColumns();
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MERGE_STRING, aMergeTransformation->getMergeString());
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_MERGE_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::SORT_TRANSFORMATION:
+ {
+ // Sort Transformation
+ std::shared_ptr<sc::SortTransformation> aSortTransformation = std::dynamic_pointer_cast<sc::SortTransformation>(itr);
+ ScSortParam aSortParam = aSortTransformation->getSortParam();
+ const sc::DocumentLinkManager& rMgr = pDoc->GetDocLinkManager();
+ const sc::DataStream* pStrm = rMgr.getDataStream();
+ if (!pStrm)
+ // No data stream.
+ return;
+
+ // Streamed range
+ ScRange aRange = pStrm->GetRange();
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_SORT_TRANSFORMATION, true, true);
+
+ writeSort(*this, aSortParam, aRange, pDoc);
+ }
+ break;
+ case sc::TransformationType::TEXT_TRANSFORMATION:
+ {
+ // Text Transformation
+ std::shared_ptr<sc::TextTransformation> aTextTransformation = std::dynamic_pointer_cast<sc::TextTransformation>(itr);
+
+ sc::TEXT_TRANSFORM_TYPE aTextTransformType = aTextTransformation->getTextTransformationType();
+
+ switch ( aTextTransformType )
+ {
+ case sc::TEXT_TRANSFORM_TYPE::TO_LOWER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_LOWERCASE);
+ break;
+ case sc::TEXT_TRANSFORM_TYPE::TO_UPPER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_UPPERCASE);
+ break;
+ case sc::TEXT_TRANSFORM_TYPE::CAPITALIZE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_CAPITALIZE);
+ break;
+ case sc::TEXT_TRANSFORM_TYPE::TRIM:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_TRIM);
+ break;
+ }
+
+ std::set<SCCOL> aColumns = aTextTransformation->getColumns();
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_TEXT_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::AGGREGATE_FUNCTION:
+ {
+ // Aggregate Transformation
+ std::shared_ptr<sc::AggregateFunction> aAggregateFunction = std::dynamic_pointer_cast<sc::AggregateFunction>(itr);
+ std::set<SCCOL> aColumns = aAggregateFunction->getColumns();
+
+ sc::AGGREGATE_FUNCTION aAggregateType = aAggregateFunction->getAggregateType();
+
+ switch (aAggregateType)
+ {
+ case sc::AGGREGATE_FUNCTION::SUM:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SUM);
+ break;
+ case sc::AGGREGATE_FUNCTION::AVERAGE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_AVERAGE);
+ break;
+ case sc::AGGREGATE_FUNCTION::MIN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MIN);
+ break;
+ case sc::AGGREGATE_FUNCTION::MAX:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MAX);
+ break;
+ }
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT,XML_COLUMN_AGGREGATE_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::NUMBER_TRANSFORMATION:
+ {
+ // Number Transformation
+ std::shared_ptr<sc::NumberTransformation> aNumberTransformation = std::dynamic_pointer_cast<sc::NumberTransformation>(itr);
+
+ sc::NUMBER_TRANSFORM_TYPE aNumberTransformType = aNumberTransformation->getNumberTransformationType();
+
+ switch ( aNumberTransformType )
+ {
+ case sc::NUMBER_TRANSFORM_TYPE::ROUND:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::ROUND_UP:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND_UP);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND_DOWN);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ABS);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::LOG_E:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_LOG);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::LOG_10:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_LOG_10);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::CUBE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CUBE);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::SQUARE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SQUARE);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SQUARE_ROOT);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::EXPONENT:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_EXPONENTIAL);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::IS_EVEN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_EVEN);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::IS_ODD:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ODD);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::SIGN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SIGN);
+ break;
+ }
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_PRECISION, OUString::number(aNumberTransformation->getPrecision()));
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_NUMBER_TRANSFORMATION, true, true);
+
+ std::set<SCCOL> aColumns = aNumberTransformation->getColumn();
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::REMOVE_NULL_TRANSFORMATION:
+ {
+ // Replace Null Transformation
+ std::shared_ptr<sc::ReplaceNullTransformation> aReplaceNullTransformation = std::dynamic_pointer_cast<sc::ReplaceNullTransformation>(itr);
+ std::set<SCCOL> aColumns = aReplaceNullTransformation->getColumn();
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_REPLACE_STRING, aReplaceNullTransformation->getReplaceString());
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_REPLACENULL_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::DATETIME_TRANSFORMATION:
+ {
+ // Number Transformation
+ std::shared_ptr<sc::DateTimeTransformation> aDateTimeTransformation = std::dynamic_pointer_cast<sc::DateTimeTransformation>(itr);
+
+ sc::DATETIME_TRANSFORMATION_TYPE aDateTimeTransformationType = aDateTimeTransformation->getDateTimeTransformationType();
+
+ switch ( aDateTimeTransformationType )
+ {
+ case sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DATE_STRING);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::MONTH:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MONTH);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MONTH_NAME);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_MONTH);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_MONTH);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::DAY:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY_OF_WEEK);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY_OF_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::QUARTER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_QUARTER);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_QUARTER);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_QUARTER);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::TIME:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_TIME);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::HOUR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_HOUR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::MINUTE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MINUTE);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::SECOND:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SECONDS);
+ break;
+ }
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_DATETIME_TRANSFORMATION, true, true);
+
+ std::set<SCCOL> aColumns = aDateTimeTransformation->getColumn();
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ScXMLExport::WriteDataStream()
+{
+ if (!pDoc)
+ return;
+
+ if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
+ // Export this only in experimental mode.
+ return;
+
+ if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
+ // Export this only for 1.2 extended and above.
+ return;
+
+ const sc::DocumentLinkManager& rMgr = pDoc->GetDocLinkManager();
+ const sc::DataStream* pStrm = rMgr.getDataStream();
+ if (!pStrm)
+ // No data stream.
+ return;
+
+ // Source URL
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(pStrm->GetURL()));
+
+ // Streamed range
+ ScRange aRange = pStrm->GetRange();
+ OUString aRangeStr;
+ ScRangeStringConverter::GetStringFromRange(
+ aRangeStr, aRange, pDoc, formula::FormulaGrammar::CONV_OOO);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aRangeStr);
+
+ // Empty line refresh option.
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_EMPTY_LINE_REFRESH, pStrm->IsRefreshOnEmptyLine() ? XML_TRUE : XML_FALSE);
+
+ // New data insertion position. Either top of bottom. Default to bottom.
+ xmloff::token::XMLTokenEnum eInsertPosition = XML_BOTTOM;
+ if (pStrm->GetMove() == sc::DataStream::MOVE_DOWN)
+ eInsertPosition = XML_TOP;
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_INSERTION_POSITION, eInsertPosition);
+
+ SvXMLElementExport aElem(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_STREAM_SOURCE, true, true);
+}
+
+void ScXMLExport::WriteNamedRange(ScRangeName* pRangeName)
+{
+ //write a global or local ScRangeName
+ SvXMLElementExport aElemNEs(*this, XML_NAMESPACE_TABLE, XML_NAMED_EXPRESSIONS, true, true);
+ for (const auto& rxEntry : *pRangeName)
+ {
+ AddAttribute(sAttrName, rxEntry.second->GetName());
+
+ OUString sBaseCellAddress;
+ rxEntry.second->ValidateTabRefs();
+ ScRangeStringConverter::GetStringFromAddress( sBaseCellAddress, rxEntry.second->GetPos(), pDoc,
+ FormulaGrammar::CONV_OOO, ' ', false, ScRefFlags::ADDR_ABS_3D);
+ assert(!sBaseCellAddress.isEmpty());
+ AddAttribute(XML_NAMESPACE_TABLE, XML_BASE_CELL_ADDRESS, sBaseCellAddress);
+
+ OUString sTempSymbol(rxEntry.second->GetSymbol(pDoc->GetStorageGrammar()));
+ ScRange aRange;
+ if (rxEntry.second->IsReference(aRange))
+ {
+
+ OUString sContent(sTempSymbol.copy(1, sTempSymbol.getLength() -2 ));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sContent);
+
+ sal_Int32 nRangeType = rxEntry.second->GetUnoType();
+ OUStringBuffer sBufferRangeType;
+ if ((nRangeType & sheet::NamedRangeFlag::COLUMN_HEADER) == sheet::NamedRangeFlag::COLUMN_HEADER)
+ sBufferRangeType.append(GetXMLToken(XML_REPEAT_COLUMN));
+ if ((nRangeType & sheet::NamedRangeFlag::ROW_HEADER) == sheet::NamedRangeFlag::ROW_HEADER)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_REPEAT_ROW));
+ }
+ if ((nRangeType & sheet::NamedRangeFlag::FILTER_CRITERIA) == sheet::NamedRangeFlag::FILTER_CRITERIA)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_FILTER));
+ }
+ if ((nRangeType & sheet::NamedRangeFlag::PRINT_AREA) == sheet::NamedRangeFlag::PRINT_AREA)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_PRINT_RANGE));
+ }
+ if ((nRangeType & sheet::NamedRangeFlag::HIDDEN) == sheet::NamedRangeFlag::HIDDEN)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_HIDDEN));
+ }
+ OUString sRangeType = sBufferRangeType.makeStringAndClear();
+ if (!sRangeType.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_RANGE_USABLE_AS, sRangeType);
+ SvXMLElementExport aElemNR(*this, XML_NAMESPACE_TABLE, XML_NAMED_RANGE, true, true);
+
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_EXPRESSION, sTempSymbol);
+ SvXMLElementExport aElemNE(*this, XML_NAMESPACE_TABLE, XML_NAMED_EXPRESSION, true, true);
+ }
+ }
+}
+
+void ScXMLExport::exportSparklineGroups(SCTAB nTable)
+{
+ sc::SparklineGroupsExport aSparklineGroupExport(*this, nTable);
+ aSparklineGroupExport.write();
+}
+
+namespace {
+
+OUString getCondFormatEntryType(const ScColorScaleEntry& rEntry, bool bFirst = true)
+{
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_MIN:
+ return "minimum";
+ case COLORSCALE_MAX:
+ return "maximum";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_VALUE:
+ return "number";
+ case COLORSCALE_AUTO:
+ // only important for data bars
+ if(bFirst)
+ return "auto-minimum";
+ else
+ return "auto-maximum";
+ }
+ return OUString();
+}
+
+OUString getDateStringForType(condformat::ScCondFormatDateType eType)
+{
+ switch(eType)
+ {
+ case condformat::TODAY:
+ return "today";
+ case condformat::YESTERDAY:
+ return "yesterday";
+ case condformat::TOMORROW:
+ return "tomorrow";
+ case condformat::LAST7DAYS:
+ return "last-7-days";
+ case condformat::THISWEEK:
+ return "this-week";
+ case condformat::LASTWEEK:
+ return "last-week";
+ case condformat::NEXTWEEK:
+ return "next-week";
+ case condformat::THISMONTH:
+ return "this-month";
+ case condformat::LASTMONTH:
+ return "last-month";
+ case condformat::NEXTMONTH:
+ return "next-month";
+ case condformat::THISYEAR:
+ return "this-year";
+ case condformat::LASTYEAR:
+ return "last-year";
+ case condformat::NEXTYEAR:
+ return "next-year";
+ }
+
+ return OUString();
+}
+
+}
+
+void ScXMLExport::ExportConditionalFormat(SCTAB nTab)
+{
+ ScConditionalFormatList* pCondFormatList = pDoc->GetCondFormList(nTab);
+ if(!pCondFormatList)
+ return;
+
+ if (pCondFormatList->empty())
+ return;
+
+ SvXMLElementExport aElementCondFormats(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITIONAL_FORMATS, true, true);
+
+ for(const auto& rxCondFormat : *pCondFormatList)
+ {
+ OUString sRanges;
+ const ScRangeList& rRangeList = rxCondFormat->GetRange();
+ ScRangeStringConverter::GetStringFromRangeList( sRanges, &rRangeList, pDoc, formula::FormulaGrammar::CONV_OOO );
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TARGET_RANGE_ADDRESS, sRanges);
+ SvXMLElementExport aElementCondFormat(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITIONAL_FORMAT, true, true);
+ size_t nEntries = rxCondFormat->size();
+ for(size_t i = 0; i < nEntries; ++i)
+ {
+ const ScFormatEntry* pFormatEntry = rxCondFormat->GetEntry(i);
+ if(pFormatEntry->GetType()==ScFormatEntry::Type::Condition)
+ {
+ const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+ OUStringBuffer aCond;
+ ScAddress aPos = pEntry->GetSrcPos();
+ switch(pEntry->GetOperation())
+ {
+ case ScConditionMode::Equal:
+ aCond.append("=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::Less:
+ aCond.append("<" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::Greater:
+ aCond.append(">" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::EqLess:
+ aCond.append("<=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::EqGreater:
+ aCond.append(">=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::NotEqual:
+ aCond.append("!=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::Between:
+ aCond.append("between("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ","
+ + pEntry->GetExpression(aPos, 1, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::NotBetween:
+ aCond.append("not-between("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ","
+ + pEntry->GetExpression(aPos, 1, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::Duplicate:
+ aCond.append("duplicate");
+ break;
+ case ScConditionMode::NotDuplicate:
+ aCond.append("unique");
+ break;
+ case ScConditionMode::Direct:
+ aCond.append("formula-is("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::Top10:
+ aCond.append("top-elements("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::Bottom10:
+ aCond.append("bottom-elements("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::TopPercent:
+ aCond.append("top-percent("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::BottomPercent:
+ aCond.append("bottom-percent("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::AboveAverage:
+ aCond.append("above-average");
+ break;
+ case ScConditionMode::BelowAverage:
+ aCond.append("below-average");
+ break;
+ case ScConditionMode::AboveEqualAverage:
+ aCond.append("above-equal-average");
+ break;
+ case ScConditionMode::BelowEqualAverage:
+ aCond.append("below-equal-average");
+ break;
+ case ScConditionMode::Error:
+ aCond.append("is-error");
+ break;
+ case ScConditionMode::NoError:
+ aCond.append("is-no-error");
+ break;
+ case ScConditionMode::BeginsWith:
+ aCond.append("begins-with("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::EndsWith:
+ aCond.append("ends-with("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::ContainsText:
+ aCond.append("contains-text("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::NotContainsText:
+ aCond.append("not-contains-text("
+ + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
+ + ")");
+ break;
+ case ScConditionMode::NONE:
+ continue;
+ default:
+ SAL_WARN("sc", "unimplemented conditional format export");
+ }
+ OUString sStyle = ScStyleNameConversion::DisplayToProgrammaticName(pEntry->GetStyle(), SfxStyleFamily::Para);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_APPLY_STYLE_NAME, sStyle);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, aCond.makeStringAndClear());
+
+ OUString sBaseAddress;
+ ScRangeStringConverter::GetStringFromAddress( sBaseAddress, aPos, pDoc,formula::FormulaGrammar::CONV_ODF );
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_BASE_CELL_ADDRESS, sBaseAddress);
+ SvXMLElementExport aElementCondEntry(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITION, true, true);
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ {
+ SvXMLElementExport aElementColorScale(*this, XML_NAMESPACE_CALC_EXT, XML_COLOR_SCALE, true, true);
+ const ScColorScaleFormat& rColorScale = static_cast<const ScColorScaleFormat&>(*pFormatEntry);
+ for(const auto& rxItem : rColorScale)
+ {
+ if(rxItem->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = rxItem->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(rxItem->GetValue()));
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*rxItem));
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, rxItem->GetColor());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLOR, aBuffer.makeStringAndClear());
+ SvXMLElementExport aElementColorScaleEntry(*this, XML_NAMESPACE_CALC_EXT, XML_COLOR_SCALE_ENTRY, true, true);
+ }
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
+ {
+ const ScDataBarFormatData* pFormatData = static_cast<const ScDataBarFormat&>(*pFormatEntry).GetDataBarData();
+ if(!pFormatData->mbGradient)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_GRADIENT, XML_FALSE);
+ if(pFormatData->mbOnlyBar)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SHOW_VALUE, XML_FALSE);
+
+ if (pFormatData->mnMinLength != 0.0)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MIN_LENGTH, OUString::number(pFormatData->mnMinLength));
+
+ if (pFormatData->mnMaxLength != 0.0)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MAX_LENGTH, OUString::number(pFormatData->mnMaxLength));
+
+ if(pFormatData->mbNeg)
+ {
+ if(pFormatData->mxNegativeColor)
+ {
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, *pFormatData->mxNegativeColor);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_NEGATIVE_COLOR, aBuffer.makeStringAndClear());
+ }
+ else
+ {
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, COL_LIGHTRED);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_NEGATIVE_COLOR, aBuffer.makeStringAndClear());
+ }
+ }
+
+ if(pFormatData->meAxisPosition != databar::AUTOMATIC)
+ {
+ if(pFormatData->meAxisPosition == databar::NONE)
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_POSITION, OUString("none"));
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_POSITION, OUString("middle"));
+ }
+ }
+
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, pFormatData->maPositiveColor);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_POSITIVE_COLOR, aBuffer.makeStringAndClear());
+
+ aBuffer.truncate();
+ ::sax::Converter::convertColor(aBuffer, pFormatData->maAxisColor);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_COLOR, aBuffer.makeStringAndClear());
+ SvXMLElementExport aElementDataBar(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_BAR, true, true);
+
+ {
+ if(pFormatData->mpLowerLimit->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = pFormatData->mpLowerLimit->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(pFormatData->mpLowerLimit->GetValue()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*pFormatData->mpLowerLimit));
+ SvXMLElementExport aElementDataBarEntryLower(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
+ }
+
+ {
+ if(pFormatData->mpUpperLimit->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = pFormatData->mpUpperLimit->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(pFormatData->mpUpperLimit->GetValue()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*pFormatData->mpUpperLimit, false));
+ SvXMLElementExport aElementDataBarEntryUpper(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
+ }
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
+ OUString aIconSetName = OUString::createFromAscii(ScIconSetFormat::getIconSetName(rIconSet.GetIconSetData()->eIconSetType));
+ AddAttribute( XML_NAMESPACE_CALC_EXT, XML_ICON_SET_TYPE, aIconSetName );
+ if (rIconSet.GetIconSetData()->mbCustom)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM, OUString::boolean(true));
+
+ SvXMLElementExport aElementColorScale(*this, XML_NAMESPACE_CALC_EXT, XML_ICON_SET, true, true);
+
+ if (rIconSet.GetIconSetData()->mbCustom)
+ {
+ for (const auto& [rType, rIndex] : rIconSet.GetIconSetData()->maCustomVector)
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET_NAME, OUString::createFromAscii(ScIconSetFormat::getIconSetName(rType)));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET_INDEX, OUString::number(rIndex));
+ SvXMLElementExport aCustomIcon(*this, XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET, true, true);
+ }
+
+ }
+
+ if(!rIconSet.GetIconSetData()->mbShowValue)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SHOW_VALUE, XML_FALSE);
+ for (auto const& it : rIconSet)
+ {
+ if(it->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = it->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(it->GetValue()));
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*it));
+ SvXMLElementExport aElementColorScaleEntry(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
+ }
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
+ {
+ const ScCondDateFormatEntry& rDateFormat = static_cast<const ScCondDateFormatEntry&>(*pFormatEntry);
+ OUString aDateType = getDateStringForType(rDateFormat.GetDateType());
+ OUString aStyleName = ScStyleNameConversion::DisplayToProgrammaticName(rDateFormat.GetStyleName(), SfxStyleFamily::Para );
+ AddAttribute( XML_NAMESPACE_CALC_EXT, XML_STYLE, aStyleName);
+ AddAttribute( XML_NAMESPACE_CALC_EXT, XML_DATE, aDateType);
+ SvXMLElementExport aElementDateFormat(*this, XML_NAMESPACE_CALC_EXT, XML_DATE_IS, true, true);
+ }
+ }
+ }
+}
+
+void ScXMLExport::WriteExternalRefCaches()
+{
+ if (!pDoc)
+ return;
+
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ pRefMgr->resetSrcFileData(GetOrigFileName());
+ sal_uInt16 nCount = pRefMgr->getExternalFileCount();
+ for (sal_uInt16 nFileId = 0; nFileId < nCount; ++nFileId)
+ {
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ continue;
+
+ vector<OUString> aTabNames;
+ pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
+ if (aTabNames.empty())
+ continue;
+
+ for (const auto& rTabName : aTabNames)
+ {
+ ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, rTabName, false);
+ if (!pTable || !pTable->isReferenced())
+ continue;
+
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, "'" + *pUrl + "'#" + rTabName);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PRINT, GetXMLToken(XML_FALSE));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, sExternalRefTabStyleName);
+ SvXMLElementExport aElemTable(*this, XML_NAMESPACE_TABLE, XML_TABLE, true, true);
+ {
+ const ScExternalRefManager::SrcFileData* pExtFileData = pRefMgr->getExternalFileData(nFileId);
+ if (pExtFileData)
+ {
+ OUString aRelUrl;
+ if (!pExtFileData->maRelativeName.isEmpty())
+ aRelUrl = pExtFileData->maRelativeName;
+ else
+ aRelUrl = GetRelativeReference(pExtFileData->maRelativeName);
+ AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, aRelUrl);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_NAME, rTabName);
+ if (!pExtFileData->maFilterName.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_NAME, pExtFileData->maFilterName);
+ if (!pExtFileData->maFilterOptions.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, pExtFileData->maFilterOptions);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_MODE, XML_COPY_RESULTS_ONLY);
+ }
+ SvXMLElementExport aElemTableSource(*this, XML_NAMESPACE_TABLE, XML_TABLE_SOURCE, true, true);
+ }
+
+ // Determine maximum column count of used area, for repeated cells.
+ SCCOL nMaxColsUsed = 1; // assume that there is at least one cell somewhere...
+ vector<SCROW> aRows;
+ pTable->getAllRows(aRows);
+ for (SCROW nRow : aRows)
+ {
+ vector<SCCOL> aCols;
+ pTable->getAllCols(nRow, aCols);
+ if (!aCols.empty())
+ {
+ SCCOL nCol = aCols.back();
+ if (nMaxColsUsed <= nCol)
+ nMaxColsUsed = nCol + 1;
+ }
+ }
+
+ // Column definitions have to be present to make a valid file
+ {
+ if (nMaxColsUsed > 1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
+ OUString::number(nMaxColsUsed));
+ SvXMLElementExport aElemColumn(*this, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true);
+ }
+
+ // Write cache content for this table.
+ SCROW nLastRow = 0;
+ bool bFirstRow = true;
+ for (SCROW nRow : aRows)
+ {
+ if (bFirstRow)
+ {
+ if (nRow > 0)
+ {
+ if (nRow > 1)
+ {
+ OUString aVal = OUString::number(nRow);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nMaxColsUsed));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+ else
+ {
+ SCROW nRowGap = nRow - nLastRow;
+ if (nRowGap > 1)
+ {
+ if (nRowGap > 2)
+ {
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nRowGap-1));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nMaxColsUsed));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+ SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+
+ vector<SCCOL> aCols;
+ pTable->getAllCols(nRow, aCols);
+ SCCOL nLastCol = 0;
+ bool bFirstCol = true;
+ for (SCCOL nCol : aCols)
+ {
+ if (bFirstCol)
+ {
+ if (nCol > 0)
+ {
+ if (nCol > 1)
+ {
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nCol));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+ else
+ {
+ SCCOL nColGap = nCol - nLastCol;
+ if (nColGap > 1)
+ {
+ if (nColGap > 2)
+ {
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nColGap-1));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+
+ // Write out this cell.
+ sal_uInt32 nNumFmt = 0;
+ ScExternalRefCache::TokenRef pToken = pTable->getCell(nCol, nRow, &nNumFmt);
+ OUString aStrVal;
+ if (pToken)
+ {
+ sal_Int32 nIndex = GetNumberFormatStyleIndex(nNumFmt);
+ if (nIndex >= 0)
+ {
+ const OUString & aStyleName = pCellStyles->GetStyleNameByIndex(nIndex, true);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, aStyleName);
+ }
+
+ switch(pToken->GetType())
+ {
+ case svDouble:
+ {
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
+ aStrVal = OUString::number(pToken->GetDouble());
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, aStrVal);
+ }
+ break;
+ case svString:
+ {
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ aStrVal = pToken->GetString().getString();
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ SvXMLElementExport aElemText(*this, XML_NAMESPACE_TEXT, XML_P, true, false);
+ Characters(aStrVal);
+
+ nLastCol = nCol;
+ bFirstCol = false;
+ }
+ nLastRow = nRow;
+ bFirstRow = false;
+ }
+ }
+ }
+}
+
+// core implementation
+void ScXMLExport::WriteConsolidation()
+{
+ if (!pDoc)
+ return;
+
+ const ScConsolidateParam* pCons(pDoc->GetConsolidateDlgData());
+ if( !pCons )
+ return;
+
+ OUString sStrData = ScXMLConverter::GetStringFromFunction( pCons->eFunction );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_FUNCTION, sStrData );
+
+ sStrData.clear();
+ for( sal_Int32 nIndex = 0; nIndex < pCons->nDataAreaCount; ++nIndex )
+ ScRangeStringConverter::GetStringFromArea( sStrData, pCons->pDataAreas[ nIndex ], pDoc, FormulaGrammar::CONV_OOO, ' ', true );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_SOURCE_CELL_RANGE_ADDRESSES, sStrData );
+
+ ScRangeStringConverter::GetStringFromAddress( sStrData, ScAddress( pCons->nCol, pCons->nRow, pCons->nTab ), pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_TARGET_CELL_ADDRESS, sStrData );
+
+ if( pCons->bByCol && !pCons->bByRow )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_COLUMN );
+ else if( !pCons->bByCol && pCons->bByRow )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_ROW );
+ else if( pCons->bByCol && pCons->bByRow )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_BOTH );
+
+ if( pCons->bReferenceData )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LINK_TO_SOURCE_DATA, XML_TRUE );
+
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_CONSOLIDATION, true, true );
+}
+
+SvXMLAutoStylePoolP* ScXMLExport::CreateAutoStylePool()
+{
+ return new ScXMLAutoStylePoolP(*this);
+}
+
+XMLPageExport* ScXMLExport::CreatePageExport()
+{
+ return new XMLTableMasterPageExport( *this );
+}
+
+void ScXMLExport::GetChangeTrackViewSettings(uno::Sequence<beans::PropertyValue>& rProps)
+{
+ ScChangeViewSettings* pViewSettings(GetDocument() ? GetDocument()->GetChangeViewSettings() : nullptr);
+ if (!pViewSettings)
+ return;
+
+ sal_Int32 nChangePos(rProps.getLength());
+ rProps.realloc(nChangePos + 1);
+ beans::PropertyValue* pProps(rProps.getArray());
+
+ uno::Sequence<beans::PropertyValue> aChangeProps(SC_VIEWCHANGES_COUNT);
+ beans::PropertyValue* pChangeProps(aChangeProps.getArray());
+ pChangeProps[SC_SHOW_CHANGES].Name = "ShowChanges";
+ pChangeProps[SC_SHOW_CHANGES].Value <<= pViewSettings->ShowChanges();
+ pChangeProps[SC_SHOW_ACCEPTED_CHANGES].Name = "ShowAcceptedChanges";
+ pChangeProps[SC_SHOW_ACCEPTED_CHANGES].Value <<= pViewSettings->IsShowAccepted();
+ pChangeProps[SC_SHOW_REJECTED_CHANGES].Name = "ShowRejectedChanges";
+ pChangeProps[SC_SHOW_REJECTED_CHANGES].Value <<= pViewSettings->IsShowRejected();
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME].Name = "ShowChangesByDatetime";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME].Value <<= pViewSettings->HasDate();
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_MODE].Name = "ShowChangesByDatetimeMode";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_MODE].Value <<= static_cast<sal_Int16>(pViewSettings->GetTheDateMode());
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME].Name = "ShowChangesByDatetimeFirstDatetime";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME].Value <<= pViewSettings->GetTheFirstDateTime().GetUNODateTime();
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME].Name = "ShowChangesByDatetimeSecondDatetime";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME].Value <<= pViewSettings->GetTheLastDateTime().GetUNODateTime();
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR].Name = "ShowChangesByAuthor";
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR].Value <<= pViewSettings->HasAuthor();
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR_NAME].Name = "ShowChangesByAuthorName";
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR_NAME].Value <<= pViewSettings->GetTheAuthorToShow();
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT].Name = "ShowChangesByComment";
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT].Value <<= pViewSettings->HasComment();
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT_TEXT].Name = "ShowChangesByCommentText";
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT_TEXT].Value <<= pViewSettings->GetTheComment();
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES].Name = "ShowChangesByRanges";
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES].Value <<= pViewSettings->HasRange();
+ OUString sRangeList;
+ ScRangeStringConverter::GetStringFromRangeList(sRangeList, &(pViewSettings->GetTheRangeList()), GetDocument(), FormulaGrammar::CONV_OOO);
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES_LIST].Name = "ShowChangesByRangesList";
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES_LIST].Value <<= sRangeList;
+
+ pProps[nChangePos].Name = "TrackedChangesViewSettings";
+ pProps[nChangePos].Value <<= aChangeProps;
+}
+
+void ScXMLExport::GetViewSettings(uno::Sequence<beans::PropertyValue>& rProps)
+{
+ if (GetModel().is())
+ {
+ rProps.realloc(4);
+ beans::PropertyValue* pProps(rProps.getArray());
+ ScModelObj* pDocObj(comphelper::getFromUnoTunnel<ScModelObj>( GetModel() ));
+ if (pDocObj)
+ {
+ SfxObjectShell* pEmbeddedObj = pDocObj->GetEmbeddedObject();
+ if (pEmbeddedObj)
+ {
+ tools::Rectangle aRect(pEmbeddedObj->GetVisArea());
+ sal_uInt16 i(0);
+ pProps[i].Name = "VisibleAreaTop";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.Top());
+ pProps[++i].Name = "VisibleAreaLeft";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.Left());
+ pProps[++i].Name = "VisibleAreaWidth";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.getOpenWidth());
+ pProps[++i].Name = "VisibleAreaHeight";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.getOpenHeight());
+ }
+ }
+ }
+ GetChangeTrackViewSettings(rProps);
+}
+
+void ScXMLExport::GetConfigurationSettings(uno::Sequence<beans::PropertyValue>& rProps)
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (!xMultiServiceFactory.is())
+ return;
+
+ uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance("com.sun.star.comp.SpreadsheetSettings"), uno::UNO_QUERY);
+ if (xProperties.is())
+ SvXMLUnitConverter::convertPropertySet(rProps, xProperties);
+
+ sal_Int32 nPropsToAdd = 0;
+ OUStringBuffer aTrackedChangesKey;
+ if (GetDocument() && GetDocument()->GetChangeTrack() && GetDocument()->GetChangeTrack()->IsProtected())
+ {
+ ::comphelper::Base64::encode(aTrackedChangesKey,
+ GetDocument()->GetChangeTrack()->GetProtection());
+ if (!aTrackedChangesKey.isEmpty())
+ ++nPropsToAdd;
+ }
+
+ bool bVBACompat = false;
+ uno::Reference <container::XNameAccess> xCodeNameAccess;
+ OSL_ENSURE( pDoc, "ScXMLExport::GetConfigurationSettings - no ScDocument!" );
+ // tdf#71271 - add code names regardless of VBA compatibility mode
+ if (pDoc)
+ {
+ // VBA compatibility mode
+ if (bVBACompat = pDoc->IsInVBAMode(); bVBACompat)
+ ++nPropsToAdd;
+
+ // code names
+ xCodeNameAccess = new XMLCodeNameProvider( pDoc );
+ if( xCodeNameAccess->hasElements() )
+ ++nPropsToAdd;
+ else
+ xCodeNameAccess.clear();
+ }
+
+ if( nPropsToAdd <= 0 )
+ return;
+
+ sal_Int32 nCount(rProps.getLength());
+ rProps.realloc(nCount + nPropsToAdd);
+ auto pProps = rProps.getArray();
+ if (!aTrackedChangesKey.isEmpty())
+ {
+ pProps[nCount].Name = "TrackedChangesProtectionKey";
+ pProps[nCount].Value <<= aTrackedChangesKey.makeStringAndClear();
+ ++nCount;
+ }
+ if( bVBACompat )
+ {
+ pProps[nCount].Name = "VBACompatibilityMode";
+ pProps[nCount].Value <<= bVBACompat;
+ ++nCount;
+ }
+ if( xCodeNameAccess.is() )
+ {
+ pProps[nCount].Name = "ScriptConfiguration";
+ pProps[nCount].Value <<= xCodeNameAccess;
+ ++nCount;
+ }
+}
+
+XMLShapeExport* ScXMLExport::CreateShapeExport()
+{
+ return new ScXMLShapeExport(*this);
+}
+
+XMLNumberFormatAttributesExportHelper* ScXMLExport::GetNumberFormatAttributesExportHelper()
+{
+ if (!pNumberFormatAttributesExportHelper)
+ pNumberFormatAttributesExportHelper.reset(new XMLNumberFormatAttributesExportHelper(GetNumberFormatsSupplier(), *this ));
+ return pNumberFormatAttributesExportHelper.get();
+}
+
+void ScXMLExport::CollectUserDefinedNamespaces(const SfxItemPool* pPool, sal_uInt16 nAttrib)
+{
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(nAttrib))
+ {
+ const SvXMLAttrContainerItem *pUnknown(static_cast<const SvXMLAttrContainerItem *>(pItem));
+ if( pUnknown->GetAttrCount() > 0 )
+ {
+ sal_uInt16 nIdx(pUnknown->GetFirstNamespaceIndex());
+ while( USHRT_MAX != nIdx )
+ {
+ if( (XML_NAMESPACE_UNKNOWN_FLAG & nIdx) != 0 )
+ {
+ const OUString& rPrefix = pUnknown->GetPrefix( nIdx );
+ // Add namespace declaration for unknown attributes if
+ // there aren't existing ones for the prefix used by the
+ // attributes
+ GetNamespaceMap_().Add( rPrefix,
+ pUnknown->GetNamespace( nIdx ) );
+ }
+ nIdx = pUnknown->GetNextNamespaceIndex( nIdx );
+ }
+ }
+ }
+
+ // #i66550# needed for 'presentation:event-listener' element for URLs in shapes
+ GetNamespaceMap_().Add(
+ GetXMLToken( XML_NP_PRESENTATION ),
+ GetXMLToken( XML_N_PRESENTATION ),
+ XML_NAMESPACE_PRESENTATION );
+}
+
+void ScXMLExport::IncrementProgressBar(bool bFlush, sal_Int32 nInc)
+{
+ nProgressCount += nInc;
+ if (bFlush || nProgressCount > 100)
+ {
+ GetProgressBarHelper()->Increment(nProgressCount);
+ nProgressCount = 0;
+ }
+}
+
+ErrCode ScXMLExport::exportDoc( enum XMLTokenEnum eClass )
+{
+ if( getExportFlags() & (SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::STYLES|
+ SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT) )
+ {
+ if (GetDocument())
+ {
+ // if source doc was Excel then
+ uno::Reference< frame::XModel > xModel = GetModel();
+ if ( xModel.is() )
+ {
+ auto pFoundShell = comphelper::getFromUnoTunnel<SfxObjectShell>(xModel);
+ if ( pFoundShell && ooo::vba::isAlienExcelDoc( *pFoundShell ) )
+ {
+ xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScFromXLSRowStylesProperties, xScPropHdlFactory, true);
+ xRowStylesExportPropertySetMapper = new ScXMLRowExportPropertyMapper(xRowStylesPropertySetMapper);
+ GetAutoStylePool()->SetFamilyPropSetMapper( XmlStyleFamily::TABLE_ROW,
+ xRowStylesExportPropertySetMapper );
+ }
+ }
+ CollectUserDefinedNamespaces(GetDocument()->GetPool(), ATTR_USERDEF);
+ CollectUserDefinedNamespaces(GetDocument()->GetEditPool(), EE_PARA_XMLATTRIBS);
+ CollectUserDefinedNamespaces(GetDocument()->GetEditPool(), EE_CHAR_XMLATTRIBS);
+ ScDrawLayer* pDrawLayer = GetDocument()->GetDrawLayer();
+ if (pDrawLayer)
+ {
+ CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), EE_PARA_XMLATTRIBS);
+ CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), EE_CHAR_XMLATTRIBS);
+ CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), SDRATTR_XMLATTRIBUTES);
+ }
+
+ // sheet events use officeooo namespace
+ if( (getExportFlags() & SvXMLExportFlags::CONTENT) &&
+ getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+ {
+ bool bAnySheetEvents = false;
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ if (pDoc->GetSheetEvents(nTab))
+ bAnySheetEvents = true;
+ if (bAnySheetEvents)
+ GetNamespaceMap_().Add(
+ GetXMLToken( XML_NP_OFFICE_EXT ),
+ GetXMLToken( XML_N_OFFICE_EXT ),
+ XML_NAMESPACE_OFFICE_EXT );
+ }
+ }
+ }
+ return SvXMLExport::exportDoc( eClass );
+}
+
+// XExporter
+void SAL_CALL ScXMLExport::setSourceDocument( const uno::Reference<lang::XComponent>& xComponent )
+{
+ SolarMutexGuard aGuard;
+ SvXMLExport::setSourceDocument( xComponent );
+
+ pDoc = ScXMLConverter::GetScDocument( GetModel() );
+ OSL_ENSURE( pDoc, "ScXMLExport::setSourceDocument - no ScDocument!" );
+ if (!pDoc)
+ throw lang::IllegalArgumentException();
+
+ // create ScChangeTrackingExportHelper after document is known
+ pChangeTrackingExportHelper.reset(new ScChangeTrackingExportHelper(*this));
+
+ // Set the document's storage grammar corresponding to the ODF version that
+ // is to be written.
+ SvtSaveOptions::ODFSaneDefaultVersion meODFDefaultVersion = getSaneDefaultVersion();
+ switch (meODFDefaultVersion)
+ {
+ // ODF 1.0 and 1.1 use GRAM_PODF, everything later or unspecified GRAM_ODFF
+ case SvtSaveOptions::ODFSVER_010:
+ case SvtSaveOptions::ODFSVER_011:
+ pDoc->SetStorageGrammar( formula::FormulaGrammar::GRAM_PODF);
+ break;
+ default:
+ pDoc->SetStorageGrammar( formula::FormulaGrammar::GRAM_ODFF);
+ }
+}
+
+// XFilter
+sal_Bool SAL_CALL ScXMLExport::filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor )
+{
+ SolarMutexGuard aGuard;
+ if (pDoc)
+ pDoc->EnableIdle(false);
+ bool bReturn(SvXMLExport::filter(aDescriptor));
+ if (pDoc)
+ pDoc->EnableIdle(true);
+ return bReturn;
+}
+
+void SAL_CALL ScXMLExport::cancel()
+{
+ SolarMutexGuard aGuard;
+ if (pDoc)
+ pDoc->EnableIdle(true);
+ SvXMLExport::cancel();
+}
+
+// XInitialization
+void SAL_CALL ScXMLExport::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ SolarMutexGuard aGuard;
+ SvXMLExport::initialize(aArguments);
+}
+
+void ScXMLExport::DisposingModel()
+{
+ SvXMLExport::DisposingModel();
+ pDoc = nullptr;
+ xCurrentTable = nullptr;
+}
+
+void ScXMLExport::SetSharedData(std::unique_ptr<ScMySharedData> pTemp) { pSharedData = std::move(pTemp); }
+
+std::unique_ptr<ScMySharedData> ScXMLExport::ReleaseSharedData() { return std::move(pSharedData); }
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexprt.hxx b/sc/source/filter/xml/xmlexprt.hxx
new file mode 100644
index 0000000000..aaf490736a
--- /dev/null
+++ b/sc/source/filter/xml/xmlexprt.hxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/xmlexp.hxx>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <address.hxx>
+
+#include <memory>
+#include <unordered_map>
+
+
+namespace com::sun::star {
+ namespace beans { class XPropertySet; }
+}
+
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+
+namespace sc { class DataTransformation; }
+
+class ScOutlineArray;
+class SvXMLExportPropertyMapper;
+class ScMyMergedRangesContainer;
+class ScMyValidationsContainer;
+class ScMyNotEmptyCellsIterator;
+class ScChangeTrackingExportHelper;
+class ScColumnStyles;
+class ScRowStyles;
+class ScFormatRangeStyles;
+class ScRowFormatRanges;
+class ScMyOpenCloseColumnRowGroup;
+class ScMyAreaLinksContainer;
+class ScMyDetectiveOpContainer;
+struct ScMyCell;
+class ScDocument;
+class ScMySharedData;
+class ScMyDefaultStyles;
+class XMLNumberFormatAttributesExportHelper;
+class SfxItemPool;
+class ScXMLCachedRowAttrAccess;
+class ScRangeName;
+class ScXMLEditAttributeMap;
+class EditTextObject;
+class ScFormulaCell;
+
+namespace sc {
+
+class CompileFormulaContext;
+
+}
+
+
+class ScXMLExport : public SvXMLExport
+{
+ ScDocument* pDoc;
+ css::uno::Reference <css::sheet::XSpreadsheet> xCurrentTable;
+
+ css::uno::Reference<css::io::XInputStream> xSourceStream;
+ sal_Int64 nSourceStreamPos;
+
+ mutable std::unique_ptr<ScXMLEditAttributeMap> mpEditAttrMap;
+ std::unique_ptr<ScMyNotEmptyCellsIterator> mpCellsItr;
+ std::unique_ptr<sc::CompileFormulaContext> mpCompileFormulaCxt;
+ rtl::Reference < XMLPropertyHandlerFactory > xScPropHdlFactory;
+ rtl::Reference < XMLPropertySetMapper > xCellStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xColumnStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xRowStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xTableStylesPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xCellStylesExportPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xColumnStylesExportPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xRowStylesExportPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xTableStylesExportPropertySetMapper;
+ std::unique_ptr<XMLNumberFormatAttributesExportHelper> pNumberFormatAttributesExportHelper;
+ typedef std::unordered_map<sal_Int32, sal_Int32> NumberFormatIndexMap;
+ NumberFormatIndexMap aNumFmtIndexMap;
+ std::unique_ptr<ScMySharedData> pSharedData;
+ std::unique_ptr<ScColumnStyles> pColumnStyles;
+ std::unique_ptr<ScRowStyles> pRowStyles;
+ std::unique_ptr<ScFormatRangeStyles> pCellStyles;
+ std::unique_ptr<ScRowFormatRanges> pRowFormatRanges;
+ std::vector<OUString> aTableStyles;
+ ScRange aRowHeaderRange;
+ std::unique_ptr<ScMyOpenCloseColumnRowGroup> pGroupColumns;
+ std::unique_ptr<ScMyOpenCloseColumnRowGroup> pGroupRows;
+ std::unique_ptr<ScMyDefaultStyles> pDefaults;
+ const ScMyCell* pCurrentCell;
+
+ std::unique_ptr<ScMyMergedRangesContainer> pMergedRangesContainer;
+ std::unique_ptr<ScMyValidationsContainer> pValidationsContainer;
+ std::unique_ptr<ScChangeTrackingExportHelper> pChangeTrackingExportHelper;
+ OUString sExternalRefTabStyleName;
+ OUString sAttrName;
+ OUString sAttrStyleName;
+ OUString sAttrColumnsRepeated;
+ OUString sAttrFormula;
+ OUString sAttrValueType;
+ OUString sAttrStringValue;
+ OUString sElemCell;
+ OUString sElemCoveredCell;
+ OUString sElemCol;
+ OUString sElemRow;
+ OUString sElemTab;
+ OUString sElemP;
+ sal_Int32 nOpenRow;
+ sal_Int32 nProgressCount;
+ sal_uInt16 nCurrentTable;
+ bool bHasRowHeader;
+ bool bRowHeaderOpen;
+
+ sal_Int32 GetNumberFormatStyleIndex(sal_Int32 nNumFmt) const;
+ void CollectSharedData(SCTAB& nTableCount, sal_Int32& nShapesCount);
+ void CollectShapesAutoStyles(SCTAB nTableCount);
+ void RegisterDefinedStyleNames( const css::uno::Reference< css::sheet::XSpreadsheetDocument > & xSpreadDoc );
+ virtual void ExportFontDecls_() override;
+ virtual void ExportStyles_( bool bUsed ) override;
+ virtual void ExportAutoStyles_() override;
+ virtual void ExportMasterStyles_() override;
+ virtual void SetBodyAttributes() override;
+ virtual void ExportContent_() override;
+ virtual void ExportMeta_() override;
+
+ void CollectInternalShape( css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ static css::table::CellRangeAddress GetEndAddress(const css::uno::Reference<css::sheet::XSpreadsheet>& xTable);
+ void GetAreaLinks( ScMyAreaLinksContainer& rAreaLinks );
+ void GetDetectiveOpList( ScMyDetectiveOpContainer& rDetOp );
+ void WriteSingleColumn(const sal_Int32 nRepeatColumns, const sal_Int32 nStyleIndex,
+ const sal_Int32 nIndex, const bool bIsAutoStyle, const bool bIsVisible);
+ void WriteColumn(const sal_Int32 nColumn, const sal_Int32 nRepeatColumns,
+ const sal_Int32 nStyleIndex, const bool bIsVisible);
+ void OpenHeaderColumn();
+ void CloseHeaderColumn();
+ void ExportColumns(const sal_Int32 nTable, const ScRange& aColumnHeaderRange, const bool bHasColumnHeader);
+ void ExportExternalRefCacheStyles();
+ void ExportCellTextAutoStyles(sal_Int32 nTable);
+ void ExportFormatRanges(const sal_Int32 nStartCol, const sal_Int32 nStartRow,
+ const sal_Int32 nEndCol, const sal_Int32 nEndRow, const sal_Int32 nSheet);
+ void WriteRowContent();
+ void WriteRowStartTag(const sal_Int32 nIndex, const sal_Int32 nEmptyRows, bool bHidden, bool bFiltered);
+ void OpenHeaderRows();
+ void CloseHeaderRows();
+ void OpenNewRow(const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEmptyRows,
+ bool bHidden, bool bFiltered);
+ void OpenAndCloseRow(const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEmptyRows,
+ bool bHidden, bool bFiltered);
+ void OpenRow(const sal_Int32 nTable, const sal_Int32 nStartRow, const sal_Int32 nRepeatRow, ScXMLCachedRowAttrAccess& rRowAttr);
+ void CloseRow(const sal_Int32 nRow);
+ void GetColumnRowHeader(bool& bHasColumnHeader, ScRange& aColumnHeaderRange,
+ bool& bHasRowHeader, ScRange& aRowHeaderRange, OUString& rPrintRanges) const;
+ static void FillFieldGroup(ScOutlineArray* pFields, ScMyOpenCloseColumnRowGroup* pGroups);
+ void FillColumnRowGroups();
+
+ bool GetMerged (const css::table::CellRangeAddress* pCellRange,
+ const css::uno::Reference <css::sheet::XSpreadsheet>& xTable);
+
+ void WriteTable(sal_Int32 nTable, const css::uno::Reference< css::sheet::XSpreadsheet>& xTable);
+ void WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount);
+ void WriteEditCell(const EditTextObject* pText);
+ void WriteMultiLineFormulaResult(const ScFormulaCell* pCell);
+ void WriteAreaLink(const ScMyCell& rMyCell);
+ void WriteAnnotation(const ScMyCell& rMyCell);
+ void WriteDetective(const ScMyCell& rMyCell);
+ void ExportShape(const css::uno::Reference < css::drawing::XShape >& xShape, css::awt::Point* pPoint);
+ void WriteShapes(const ScMyCell& rMyCell);
+ void WriteTableShapes();
+ void SetRepeatAttribute(sal_Int32 nEqualCellCount, bool bIncProgress);
+
+ static bool IsEditCell(const ScMyCell& rCell);
+ bool IsCellEqual(const ScMyCell& aCell1, const ScMyCell& aCell2);
+
+ void WriteCalculationSettings(const css::uno::Reference <css::sheet::XSpreadsheetDocument>& xSpreadDoc);
+ void WriteTableSource();
+ void WriteScenario(); // core implementation
+ void WriteTheLabelRanges(const css::uno::Reference< css::sheet::XSpreadsheetDocument >& xSpreadDoc);
+ void WriteLabelRanges( const css::uno::Reference< css::container::XIndexAccess >& xRangesIAccess, bool bColumn );
+ void WriteNamedExpressions();
+ void WriteExternalDataMapping();
+ void WriteExternalDataTransformations(const std::vector<std::shared_ptr<sc::DataTransformation>>& aDataTransformations);
+ void WriteDataStream();
+ void WriteNamedRange(ScRangeName* pRangeName);
+ void exportSparklineGroups(SCTAB nTab);
+ void ExportConditionalFormat(SCTAB nTab);
+ void WriteExternalRefCaches();
+ void WriteConsolidation(); // core implementation
+
+ void CollectUserDefinedNamespaces(const SfxItemPool* pPool, sal_uInt16 nAttrib);
+
+ void AddStyleFromCells(
+ const css::uno::Reference< css::beans::XPropertySet >& xProperties,
+ const css::uno::Reference< css::sheet::XSpreadsheet >& xTable,
+ sal_Int32 nTable, const OUString* pOldName );
+ void AddStyleFromColumn(
+ const css::uno::Reference< css::beans::XPropertySet >& xColumnProperties,
+ const OUString* pOldName, sal_Int32& rIndex, bool& rIsVisible );
+ void AddStyleFromRow(
+ const css::uno::Reference< css::beans::XPropertySet >& xRowProperties,
+ const OUString* pOldName, sal_Int32& rIndex );
+
+ void IncrementProgressBar(bool bFlush, sal_Int32 nInc = 1);
+
+ void CopySourceStream( sal_Int64 nStartOffset, sal_Int64 nEndOffset, sal_Int64& rNewStart, sal_Int64& rNewEnd );
+
+ const ScXMLEditAttributeMap& GetEditAttributeMap() const;
+
+ void exportTheme();
+
+protected:
+ virtual SvXMLAutoStylePoolP* CreateAutoStylePool() override;
+ virtual XMLPageExport* CreatePageExport() override;
+ virtual XMLShapeExport* CreateShapeExport() override;
+ virtual XMLFontAutoStylePool* CreateFontAutoStylePool() override;
+public:
+ ScXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlag);
+
+ virtual ~ScXMLExport() override;
+
+ void collectAutoStyles() override;
+
+ static sal_Int16 GetMeasureUnit();
+ ScDocument* GetDocument() { return pDoc; }
+ const ScDocument* GetDocument() const { return pDoc; }
+ bool IsMatrix (const ScAddress& aCell,
+ ScRange& aCellAddress, bool& bIsFirst) const;
+
+ const rtl::Reference < XMLPropertySetMapper >& GetCellStylesPropertySetMapper() const { return xCellStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetTableStylesPropertySetMapper() const { return xTableStylesPropertySetMapper; }
+
+ void SetSourceStream( const css::uno::Reference<css::io::XInputStream>& xNewStream );
+
+ void GetChangeTrackViewSettings(css::uno::Sequence<css::beans::PropertyValue>& rProps);
+ virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& rProps) override;
+ virtual void GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& rProps) override;
+
+ virtual void exportAnnotationMeta( const css::uno::Reference < css::drawing::XShape >& xShape) override;
+
+ void SetSharedData(std::unique_ptr<ScMySharedData> pTemp);
+ ScMySharedData* GetSharedData() { return pSharedData.get(); }
+ std::unique_ptr<ScMySharedData> ReleaseSharedData();
+ XMLNumberFormatAttributesExportHelper* GetNumberFormatAttributesExportHelper();
+
+ // Export the document.
+ virtual ErrCode exportDoc( enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID ) override;
+
+ // XExporter
+ virtual void SAL_CALL setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override;
+
+ // XFilter
+ virtual sal_Bool SAL_CALL filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor ) override;
+ virtual void SAL_CALL cancel() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ virtual void DisposingModel() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexternaltabi.cxx b/sc/source/filter/xml/xmlexternaltabi.cxx
new file mode 100644
index 0000000000..7931440c12
--- /dev/null
+++ b/sc/source/filter/xml/xmlexternaltabi.cxx
@@ -0,0 +1,378 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlexternaltabi.hxx"
+#include "xmlimprt.hxx"
+#include "xmltabi.hxx"
+#include "xmlstyli.hxx"
+
+#include <document.hxx>
+#include <documentimport.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+
+#include <sax/tools/converter.hxx>
+
+#include <com/sun/star/util/NumberFormat.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+
+using ::com::sun::star::uno::Reference;
+
+ScXMLExternalRefTabSourceContext::ScXMLExternalRefTabSourceContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrExternalRefInfo(rRefInfo)
+{
+ using namespace ::xmloff::token;
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &it : *rAttrList)
+ {
+ sal_Int32 nAttrToken = it.getToken();
+ if ( nAttrToken == XML_ELEMENT( XLINK, XML_HREF ) )
+ maRelativeUrl = it.toString();
+ else if ( nAttrToken == XML_ELEMENT( TABLE, XML_TABLE_NAME ) )
+ // todo
+ ;
+ else if ( nAttrToken == XML_ELEMENT( TABLE, XML_FILTER_NAME ) )
+ maFilterName = it.toString();
+ else if ( nAttrToken == XML_ELEMENT( TABLE, XML_FILTER_OPTIONS ) )
+ maFilterOptions = it.toString();
+ }
+}
+
+ScXMLExternalRefTabSourceContext::~ScXMLExternalRefTabSourceContext()
+{
+}
+
+/**
+ * Make sure the URL is a valid relative URL, mainly to avoid storing
+ * absolute URL as relative URL by accident. For now, we only check the first
+ * three characters which are assumed to be always '../', because the relative
+ * URL for an external document is always in reference to the content.xml
+ * fragment of the original document.
+ */
+static bool lcl_isValidRelativeURL(const OUString& rUrl)
+{
+ sal_Int32 n = ::std::min( rUrl.getLength(), static_cast<sal_Int32>(3));
+ if (n < 3)
+ return false;
+ const sal_Unicode* p = rUrl.getStr();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ sal_Unicode c = p[i];
+ if (i < 2 && c != '.')
+ // the path must begin with '..'
+ return false;
+ else if (i == 2 && c != '/')
+ // a '/' path separator must follow
+ return false;
+ }
+ return true;
+}
+
+void SAL_CALL ScXMLExternalRefTabSourceContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (!pDoc)
+ return;
+
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ if (lcl_isValidRelativeURL(maRelativeUrl))
+ pRefMgr->setRelativeFileName(mrExternalRefInfo.mnFileId, maRelativeUrl);
+ pRefMgr->setFilterData(mrExternalRefInfo.mnFileId, maFilterName, maFilterOptions);
+}
+
+ScXMLExternalRefRowsContext::ScXMLExternalRefRowsContext(
+ ScXMLImport& rImport, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrExternalRefInfo(rRefInfo)
+{
+}
+
+ScXMLExternalRefRowsContext::~ScXMLExternalRefRowsContext()
+{
+}
+
+Reference< XFastContextHandler > SAL_CALL ScXMLExternalRefRowsContext::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList )
+{
+ // #i101319# row elements inside group, rows or header-rows
+ // are treated like row elements directly in the table element
+
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(TABLE, XML_TABLE_ROW_GROUP):
+ case XML_ELEMENT(TABLE, XML_TABLE_HEADER_ROWS):
+ case XML_ELEMENT(TABLE, XML_TABLE_ROWS):
+ return new ScXMLExternalRefRowsContext(
+ GetScImport(), mrExternalRefInfo);
+ case XML_ELEMENT(TABLE, XML_TABLE_ROW):
+ return new ScXMLExternalRefRowContext(
+ GetScImport(), pAttribList, mrExternalRefInfo);
+ default:
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ }
+ return nullptr;
+}
+
+ScXMLExternalRefRowContext::ScXMLExternalRefRowContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrScImport(rImport),
+ mrExternalRefInfo(rRefInfo),
+ mnRepeatRowCount(1)
+{
+ mrExternalRefInfo.mnCol = 0;
+
+ for (auto &it : *rAttrList)
+ {
+ switch (it.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_NUMBER_ROWS_REPEATED):
+ {
+ mnRepeatRowCount = std::max(it.toInt32(), static_cast<sal_Int32>(1));
+ }
+ break;
+ }
+ }
+}
+
+ScXMLExternalRefRowContext::~ScXMLExternalRefRowContext()
+{
+}
+
+Reference< XFastContextHandler > SAL_CALL ScXMLExternalRefRowContext::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList )
+{
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) || nElement == XML_ELEMENT(TABLE, XML_COVERED_TABLE_CELL))
+ return new ScXMLExternalRefCellContext(mrScImport, pAttribList, mrExternalRefInfo);
+
+ return nullptr;
+}
+
+void SAL_CALL ScXMLExternalRefRowContext::endFastElement( sal_Int32 /* nElement */ )
+{
+ ScExternalRefCache::TableTypeRef pTab = mrExternalRefInfo.mpCacheTable;
+
+ for (sal_Int32 i = 1; i < mnRepeatRowCount; ++i)
+ {
+ // Performance: duplicates of a non-existent row will still not exist.
+ // Don't find that out for every cell.
+ // External references often are a sparse matrix.
+ if (i == 1 && !pTab->hasRow( mrExternalRefInfo.mnRow))
+ {
+ mrExternalRefInfo.mnRow += mnRepeatRowCount;
+ return;
+ }
+
+ for (sal_Int32 j = 0; j < mrExternalRefInfo.mnCol; ++j)
+ {
+ ScExternalRefCache::TokenRef pToken = pTab->getCell(
+ static_cast<SCCOL>(j), static_cast<SCROW>(mrExternalRefInfo.mnRow));
+
+ if (pToken)
+ {
+ pTab->setCell(static_cast<SCCOL>(j),
+ static_cast<SCROW>(mrExternalRefInfo.mnRow+i), pToken);
+ }
+ }
+ }
+ mrExternalRefInfo.mnRow += mnRepeatRowCount;
+}
+
+ScXMLExternalRefCellContext::ScXMLExternalRefCellContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrScImport(rImport),
+ mrExternalRefInfo(rRefInfo),
+ mfCellValue(0.0),
+ mnRepeatCount(1),
+ mnNumberFormat(-1),
+ mnCellType(css::util::NumberFormat::UNDEFINED),
+ mbIsNumeric(false),
+ mbIsEmpty(true)
+{
+ using namespace ::xmloff::token;
+
+ for (auto &it : *rAttrList)
+ {
+ switch ( it.getToken() )
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ {
+ XMLTableStylesContext* pStyles = static_cast<XMLTableStylesContext*>(mrScImport.GetAutoStyles());
+ const XMLTableStyleContext* pStyle = static_cast<const XMLTableStyleContext*>(
+ pStyles->FindStyleChildContext(XmlStyleFamily::TABLE_CELL, it.toString(), true));
+ if (pStyle)
+ mnNumberFormat = const_cast<XMLTableStyleContext*>(pStyle)->GetNumberFormat();
+ }
+ break;
+ case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED):
+ {
+ mnRepeatCount = ::std::max( it.toInt32(), static_cast<sal_Int32>(1) );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
+ {
+ mnCellType = ScXMLImport::GetCellType( it.toCString(), it.getLength() );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ mfCellValue = it.toDouble();
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_DATE_VALUE):
+ {
+ if ( !it.isEmpty() && mrScImport.SetNullDateOnUnitConverter() )
+ {
+ mrScImport.GetMM100UnitConverter().convertDateTime( mfCellValue, it.toView() );
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_TIME_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ ::sax::Converter::convertDuration( mfCellValue, it.toView() );
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ maCellString = it.toString();
+ mbIsNumeric = false;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ mfCellValue = IsXMLToken( it, XML_TRUE ) ? 1.0 : 0.0;
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+ScXMLExternalRefCellContext::~ScXMLExternalRefCellContext()
+{
+}
+
+Reference< XFastContextHandler > SAL_CALL ScXMLExternalRefCellContext::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& /*xAttrList*/ )
+{
+ if (nElement == XML_ELEMENT(TEXT, XML_P))
+ return new ScXMLExternalRefCellTextContext(mrScImport, *this);
+
+ return nullptr;
+}
+
+void SAL_CALL ScXMLExternalRefCellContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!maCellString.isEmpty())
+ mbIsEmpty = false;
+
+ for (sal_Int32 i = 0; i < mnRepeatCount; ++i, ++mrExternalRefInfo.mnCol)
+ {
+ if (mbIsEmpty)
+ continue;
+
+ ScExternalRefCache::TokenRef aToken;
+ if (mbIsNumeric)
+ aToken.reset(new formula::FormulaDoubleToken(mfCellValue));
+ else
+ {
+ ScDocument& rDoc = mrScImport.GetDoc().getDoc();
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern(maCellString);
+ aToken.reset(new formula::FormulaStringToken(std::move(aSS)));
+ }
+
+ sal_uInt32 nNumFmt = mnNumberFormat >= 0 ? static_cast<sal_uInt32>(mnNumberFormat) : 0;
+ mrExternalRefInfo.mpCacheTable->setCell(
+ static_cast<SCCOL>(mrExternalRefInfo.mnCol),
+ static_cast<SCROW>(mrExternalRefInfo.mnRow),
+ aToken, nNumFmt);
+ }
+}
+
+void ScXMLExternalRefCellContext::SetCellString(const OUString& rStr)
+{
+ maCellString = rStr;
+}
+
+ScXMLExternalRefCellTextContext::ScXMLExternalRefCellTextContext(
+ ScXMLImport& rImport,
+ ScXMLExternalRefCellContext& rParent ) :
+ ScXMLImportContext( rImport ),
+ mrParent(rParent)
+{
+}
+
+ScXMLExternalRefCellTextContext::~ScXMLExternalRefCellTextContext()
+{
+}
+
+void SAL_CALL ScXMLExternalRefCellTextContext::characters( const OUString& rChars )
+{
+ maCellStrBuf.append( rChars );
+}
+
+void SAL_CALL ScXMLExternalRefCellTextContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParent.SetCellString( maCellStrBuf.makeStringAndClear() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexternaltabi.hxx b/sc/source/filter/xml/xmlexternaltabi.hxx
new file mode 100644
index 0000000000..94fa055745
--- /dev/null
+++ b/sc/source/filter/xml/xmlexternaltabi.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 .
+ */
+
+#pragma once
+
+#include <rtl/ustrbuf.hxx>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLImport;
+struct ScXMLExternalTabData;
+
+class ScXMLExternalRefTabSourceContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefTabSourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefTabSourceContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+private:
+ ScXMLExternalTabData& mrExternalRefInfo;
+
+ OUString maRelativeUrl;
+ OUString maFilterName;
+ OUString maFilterOptions;
+};
+
+class ScXMLExternalRefRowsContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefRowsContext( ScXMLImport& rImport,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefRowsContext() override;
+
+ virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ ScXMLExternalTabData& mrExternalRefInfo;
+};
+
+class ScXMLExternalRefRowContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefRowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefRowContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ ScXMLImport& mrScImport;
+ ScXMLExternalTabData& mrExternalRefInfo;
+ sal_Int32 mnRepeatRowCount;
+};
+
+class ScXMLExternalRefCellContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefCellContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SetCellString(const OUString& rStr);
+
+private:
+ ScXMLImport& mrScImport;
+ ScXMLExternalTabData& mrExternalRefInfo;
+ OUString maCellString;
+ double mfCellValue;
+ sal_Int32 mnRepeatCount;
+ sal_Int32 mnNumberFormat;
+ sal_Int16 mnCellType; // TODO: import to document core
+ bool mbIsNumeric;
+ bool mbIsEmpty;
+};
+
+class ScXMLExternalRefCellTextContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefCellTextContext( ScXMLImport& rImport,
+ ScXMLExternalRefCellContext& rParent );
+
+ virtual ~ScXMLExternalRefCellTextContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+private:
+ ScXMLExternalRefCellContext& mrParent;
+
+ OUStringBuffer maCellStrBuf;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlfilti.cxx b/sc/source/filter/xml/xmlfilti.cxx
new file mode 100644
index 0000000000..61f7cfe4a3
--- /dev/null
+++ b/sc/source/filter/xml/xmlfilti.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 "xmlfilti.hxx"
+#include "xmlimprt.hxx"
+#include "xmldrani.hxx"
+#include "xmldpimp.hxx"
+#include <rangeutl.hxx>
+#include <queryentry.hxx>
+#include <document.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+using ::com::sun::star::uno::Reference;
+
+ScXMLFilterContext::ConnStackItem::ConnStackItem(bool bOr) : mbOr(bOr), mnCondCount(0) {}
+
+ScXMLFilterContext::ScXMLFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pDatabaseRangeContext(pTempDatabaseRangeContext),
+ bSkipDuplicates(false),
+ bCopyOutputData(false),
+ bConditionSourceRange(false)
+{
+ ScDocument* pDoc(GetScImport().GetDocument());
+ assert(pDoc);
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScRange aScRange;
+ sal_Int32 nOffset(0);
+ if (ScRangeStringConverter::GetRangeFromString( aScRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ))
+ {
+ aOutputPosition = aScRange.aStart;
+ bCopyOutputData = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ if (ScRangeStringConverter::GetRangeFromString( aConditionSourceRangeAddress, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ) )
+ bConditionSourceRange = true;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE ):
+ {
+ // not supported by StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_DUPLICATES ):
+ {
+ bSkipDuplicates = !IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLFilterContext::~ScXMLFilterContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLFilterContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLAndContext(
+ GetScImport(), mrQueryParam, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ pContext = new ScXMLOrContext(
+ GetScImport(), mrQueryParam, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLConditionContext(
+ GetScImport(), nElement, pAttribList, mrQueryParam, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLFilterContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrQueryParam.bInplace = !bCopyOutputData;
+ mrQueryParam.bDuplicate = !bSkipDuplicates;
+
+ if (bCopyOutputData)
+ {
+ mrQueryParam.nDestCol = aOutputPosition.Col();
+ mrQueryParam.nDestRow = aOutputPosition.Row();
+ mrQueryParam.nDestTab = aOutputPosition.Tab();
+ }
+
+ if (bConditionSourceRange)
+ pDatabaseRangeContext->SetFilterConditionSourceRangeAddress(aConditionSourceRangeAddress);
+}
+
+void ScXMLFilterContext::OpenConnection(bool b)
+{
+ maConnStack.emplace_back(b);
+}
+
+void ScXMLFilterContext::CloseConnection()
+{
+ maConnStack.pop_back();
+}
+
+bool ScXMLFilterContext::GetConnection()
+{
+ // For condition items in each stack, the first one gets the connection of
+ // the last stack, while the rest of them get that of the current stack.
+
+ if (maConnStack.empty())
+ // This should never happen.
+ return true;
+
+ ConnStackItem& rItem = maConnStack.back();
+ if (rItem.mnCondCount)
+ // secondary item gets the current connection.
+ return rItem.mbOr;
+
+ // The next condition of this stack will get the current connection.
+ ++rItem.mnCondCount;
+
+ if (maConnStack.size() < 2)
+ // There is no last stack. Likely the first condition in the first
+ // stack whose connection is not used. Default in
+ // ScQueryEntry::eConnect is SC_AND, so return false (AND instead of
+ // OR) here. Otherwise, when saving the document again, we'd write a
+ // uselessly stacked
+ // <table:filter-or><table:filter-and>...</table:filter-and></table:filter-or>
+ // for two conditions connected with AND.
+ return false;
+
+ std::vector<ConnStackItem>::reverse_iterator itr = maConnStack.rbegin();
+ ++itr;
+ return itr->mbOr; // connection of the last stack.
+}
+
+ScXMLAndContext::ScXMLAndContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pFilterContext(pTempFilterContext)
+{
+ pFilterContext->OpenConnection(false);
+}
+
+ScXMLAndContext::~ScXMLAndContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLAndContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ // not supported in StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLConditionContext(
+ GetScImport(), nElement, pAttribList, mrQueryParam, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLAndContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLOrContext::ScXMLOrContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pFilterContext(pTempFilterContext)
+{
+ pFilterContext->OpenConnection(true);
+}
+
+ScXMLOrContext::~ScXMLOrContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLOrContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLAndContext(
+ GetScImport(), mrQueryParam, pFilterContext);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLConditionContext(
+ GetScImport(), nElement, pAttribList, mrQueryParam, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLOrContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLConditionContext::ScXMLConditionContext(
+ ScXMLImport& rImport, sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pFilterContext(pTempFilterContext),
+ sDataType(GetXMLToken(XML_TEXT)),
+ nField(0),
+ bIsCaseSensitive(false)
+{
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ {
+ nField = aIter.toInt32();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ {
+ bIsCaseSensitive = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ case XML_ELEMENT( LO_EXT, XML_DATA_TYPE ):
+ {
+ sDataType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VALUE ):
+ {
+ sConditionValue = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_OPERATOR ):
+ {
+ sOperator = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLConditionContext::~ScXMLConditionContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_SET_ITEM ):
+ {
+ pContext = new ScXMLSetItemContext(
+ GetScImport(), nElement, pAttribList, *this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void ScXMLConditionContext::GetOperator(
+ std::u16string_view aOpStr, ScQueryParam& rParam, ScQueryEntry& rEntry)
+{
+ rParam.eSearchType = utl::SearchParam::SearchType::Normal;
+ if (IsXMLToken(aOpStr, XML_MATCH))
+ {
+ rParam.eSearchType = utl::SearchParam::SearchType::Regexp;
+ rEntry.eOp = SC_EQUAL;
+ }
+ else if (IsXMLToken(aOpStr, XML_NOMATCH))
+ {
+ rParam.eSearchType = utl::SearchParam::SearchType::Regexp;
+ rEntry.eOp = SC_NOT_EQUAL;
+ }
+ else if (aOpStr == u"=")
+ rEntry.eOp = SC_EQUAL;
+ else if (aOpStr == u"!=")
+ rEntry.eOp = SC_NOT_EQUAL;
+ else if (IsXMLToken(aOpStr, XML_BOTTOM_PERCENT))
+ rEntry.eOp = SC_BOTPERC;
+ else if (IsXMLToken(aOpStr, XML_BOTTOM_VALUES))
+ rEntry.eOp = SC_BOTVAL;
+ else if (IsXMLToken(aOpStr, XML_TOKEN_EMPTY))
+ rEntry.SetQueryByEmpty();
+ else if (aOpStr == u">")
+ rEntry.eOp = SC_GREATER;
+ else if (aOpStr == u">=")
+ rEntry.eOp = SC_GREATER_EQUAL;
+ else if (aOpStr == u"<")
+ rEntry.eOp = SC_LESS;
+ else if (aOpStr == u"<=")
+ rEntry.eOp = SC_LESS_EQUAL;
+ else if (IsXMLToken(aOpStr, XML_NOEMPTY))
+ rEntry.SetQueryByNonEmpty();
+ else if (IsXMLToken(aOpStr, XML_TOP_PERCENT))
+ rEntry.eOp = SC_TOPPERC;
+ else if (IsXMLToken(aOpStr, XML_TOP_VALUES))
+ rEntry.eOp = SC_TOPVAL;
+ else if (IsXMLToken(aOpStr, XML_CONTAINS))
+ rEntry.eOp = SC_CONTAINS;
+ else if (IsXMLToken(aOpStr, XML_DOES_NOT_CONTAIN))
+ rEntry.eOp = SC_DOES_NOT_CONTAIN;
+ else if (IsXMLToken(aOpStr, XML_BEGINS_WITH))
+ rEntry.eOp = SC_BEGINS_WITH;
+ else if (IsXMLToken(aOpStr, XML_DOES_NOT_BEGIN_WITH))
+ rEntry.eOp = SC_DOES_NOT_BEGIN_WITH;
+ else if (IsXMLToken(aOpStr, XML_ENDS_WITH))
+ rEntry.eOp = SC_ENDS_WITH;
+ else if (IsXMLToken(aOpStr, XML_DOES_NOT_END_WITH))
+ rEntry.eOp = SC_DOES_NOT_END_WITH;
+}
+
+void ScXMLConditionContext::AddSetItem(const ScQueryEntry::Item& rItem)
+{
+ maQueryItems.push_back(rItem);
+}
+
+void SAL_CALL ScXMLConditionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScQueryEntry& rEntry = mrQueryParam.AppendEntry();
+
+ // We currently don't support per-condition case sensitivity.
+ mrQueryParam.bCaseSens = bIsCaseSensitive;
+
+ rEntry.bDoQuery = true;
+ rEntry.eConnect = pFilterContext->GetConnection() ? SC_OR : SC_AND;
+
+ GetOperator(sOperator, mrQueryParam, rEntry);
+ SCCOLROW nStartPos = mrQueryParam.bByRow ? mrQueryParam.nCol1 : mrQueryParam.nRow1;
+ rEntry.nField = o3tl::saturating_add(nField, nStartPos);
+
+ if (maQueryItems.empty())
+ {
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (IsXMLToken(sOperator, XML_TOKEN_EMPTY))
+ return;
+ if (IsXMLToken(sDataType, XML_NUMBER))
+ {
+ rItem.mfVal = sConditionValue.toDouble();
+ rItem.meType = ScQueryEntry::ByValue;
+ }
+ else if (IsXMLToken(sDataType, XML_TEXT_COLOR)
+ || IsXMLToken(sDataType, XML_BACKGROUND_COLOR))
+ {
+ rItem.meType = IsXMLToken(sDataType, XML_TEXT_COLOR) ? ScQueryEntry::ByTextColor
+ : ScQueryEntry::ByBackgroundColor;
+ if (IsXMLToken(sConditionValue, XML_TRANSPARENT)
+ || IsXMLToken(sConditionValue, XML_WINDOW_FONT_COLOR))
+ rItem.maColor = COL_AUTO;
+ else
+ sax::Converter::convertColor(rItem.maColor, sConditionValue);
+ }
+ else
+ {
+ svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
+ rItem.maString = rPool.intern(sConditionValue);
+ rItem.meType = ScQueryEntry::ByString;
+ }
+ }
+ else
+ rEntry.GetQueryItems().swap(maQueryItems);
+}
+
+ScXMLSetItemContext::ScXMLSetItemContext(
+ ScXMLImport& rImport, sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLConditionContext& rParent) :
+ ScXMLImportContext(rImport)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_VALUE ):
+ {
+ svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
+ ScQueryEntry::Item aItem;
+ aItem.maString = rPool.intern(aIter.toString());
+ aItem.meType = ScQueryEntry::ByString;
+ aItem.mfVal = 0.0;
+ rParent.AddSetItem(aItem);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSetItemContext::~ScXMLSetItemContext()
+{
+}
+
+ScXMLDPFilterContext::ScXMLDPFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTableContext) :
+ ScXMLImportContext( rImport ),
+ pDataPilotTable(pTempDataPilotTableContext),
+ eSearchType(utl::SearchParam::SearchType::Normal),
+ nFilterFieldCount(0),
+ bSkipDuplicates(false),
+ bIsCaseSensitive(false),
+ bConnectionOr(true),
+ bNextConnectionOr(true)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ // not supported
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS ):
+ {
+ // not supported
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE ):
+ {
+ // not supported by StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_DUPLICATES ):
+ {
+ bSkipDuplicates = !IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDPFilterContext::~ScXMLDPFilterContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDPFilterContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLDPAndContext( GetScImport(), this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ pContext = new ScXMLDPOrContext( GetScImport(), this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLDPConditionContext( GetScImport(), nElement, pAttribList, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDPFilterContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ aFilterFields.eSearchType = eSearchType;
+ aFilterFields.bCaseSens = bIsCaseSensitive;
+ aFilterFields.bDuplicate = !bSkipDuplicates;
+
+ pDataPilotTable->SetSourceQueryParam(aFilterFields);
+}
+
+void ScXMLDPFilterContext::AddFilterField (const ScQueryEntry& aFilterField)
+{
+ aFilterFields.Resize(nFilterFieldCount + 1);
+ ScQueryEntry& rEntry(aFilterFields.GetEntry(nFilterFieldCount));
+ rEntry = aFilterField;
+ rEntry.bDoQuery = true;
+ ++nFilterFieldCount;
+}
+
+ScXMLDPAndContext::ScXMLDPAndContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport )
+{
+ pFilterContext = pTempFilterContext;
+ pFilterContext->OpenConnection(false);
+}
+
+ScXMLDPAndContext::~ScXMLDPAndContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDPAndContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ // not supported in StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLDPConditionContext( GetScImport(), nElement, pAttribList, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDPAndContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLDPOrContext::ScXMLDPOrContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ pFilterContext(pTempFilterContext)
+{
+ pFilterContext->OpenConnection(true);
+}
+
+ScXMLDPOrContext::~ScXMLDPOrContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDPOrContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLDPAndContext( GetScImport(), pFilterContext);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLDPConditionContext( GetScImport(), nElement, pAttribList, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDPOrContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLDPConditionContext::ScXMLDPConditionContext( ScXMLImport& rImport,
+ sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDPFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ pFilterContext(pTempFilterContext),
+ sDataType(GetXMLToken(XML_TEXT)),
+ nField(0),
+ bIsCaseSensitive(false)
+{
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ {
+ nField = aIter.toInt32();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ {
+ bIsCaseSensitive = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ sDataType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VALUE ):
+ {
+ sConditionValue = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_OPERATOR ):
+ {
+ sOperator = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDPConditionContext::~ScXMLDPConditionContext()
+{
+}
+
+void ScXMLDPConditionContext::getOperatorXML(
+ std::u16string_view sTempOperator, ScQueryOp& aFilterOperator, utl::SearchParam::SearchType& rSearchType)
+{
+ rSearchType = utl::SearchParam::SearchType::Normal;
+ if (IsXMLToken(sTempOperator, XML_MATCH))
+ {
+ rSearchType = utl::SearchParam::SearchType::Regexp;
+ aFilterOperator = SC_EQUAL;
+ }
+ else if (IsXMLToken(sTempOperator, XML_NOMATCH))
+ {
+ rSearchType = utl::SearchParam::SearchType::Regexp;
+ aFilterOperator = SC_NOT_EQUAL;
+ }
+ else if (sTempOperator == u"=")
+ aFilterOperator = SC_EQUAL;
+ else if (sTempOperator == u"!=")
+ aFilterOperator = SC_NOT_EQUAL;
+ else if (IsXMLToken(sTempOperator, XML_BOTTOM_PERCENT))
+ aFilterOperator = SC_BOTPERC;
+ else if (IsXMLToken(sTempOperator, XML_BOTTOM_VALUES))
+ aFilterOperator = SC_BOTVAL;
+ else if (sTempOperator == u">")
+ aFilterOperator = SC_GREATER;
+ else if (sTempOperator == u">=")
+ aFilterOperator = SC_GREATER_EQUAL;
+ else if (sTempOperator == u"<")
+ aFilterOperator = SC_LESS;
+ else if (sTempOperator == u"<=")
+ aFilterOperator = SC_LESS_EQUAL;
+ else if (IsXMLToken(sTempOperator, XML_TOP_PERCENT))
+ aFilterOperator = SC_TOPPERC;
+ else if (IsXMLToken(sTempOperator, XML_TOP_VALUES))
+ aFilterOperator = SC_TOPVAL;
+}
+
+void SAL_CALL ScXMLDPConditionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScQueryEntry aFilterField;
+ aFilterField.nField = nField;
+ if (pFilterContext->GetConnection())
+ aFilterField.eConnect = SC_OR;
+ else
+ aFilterField.eConnect = SC_AND;
+ pFilterContext->SetIsCaseSensitive(bIsCaseSensitive);
+ if (IsXMLToken(sOperator, XML_TOKEN_EMPTY))
+ aFilterField.SetQueryByEmpty();
+ else if (IsXMLToken(sOperator, XML_NOEMPTY))
+ aFilterField.SetQueryByNonEmpty();
+ else
+ {
+ utl::SearchParam::SearchType eSearchType = utl::SearchParam::SearchType::Normal;
+ getOperatorXML(sOperator, aFilterField.eOp, eSearchType);
+ pFilterContext->SetSearchType(eSearchType);
+ ScQueryEntry::Item& rItem = aFilterField.GetQueryItem();
+ svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
+
+ if (IsXMLToken(sDataType, XML_NUMBER))
+ {
+ rItem.mfVal = sConditionValue.toDouble();
+ rItem.maString = rPool.intern(sConditionValue);
+ rItem.meType = ScQueryEntry::ByValue;
+ }
+ else
+ {
+ rItem.maString = rPool.intern(sConditionValue);
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.mfVal = 0.0;
+ }
+ }
+ pFilterContext->AddFilterField(aFilterField);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlfilti.hxx b/sc/source/filter/xml/xmlfilti.hxx
new file mode 100644
index 0000000000..02a2a9e6c8
--- /dev/null
+++ b/sc/source/filter/xml/xmlfilti.hxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+
+#include <stack>
+#include <vector>
+
+class ScXMLImport;
+class ScXMLDatabaseRangeContext;
+class ScXMLDataPilotTableContext;
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLFilterContext : public ScXMLImportContext
+{
+ struct ConnStackItem
+ {
+ bool mbOr;
+ int mnCondCount;
+ explicit ConnStackItem(bool bOr);
+ };
+ ScQueryParam& mrQueryParam;
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+
+ ScAddress aOutputPosition;
+ ScRange aConditionSourceRangeAddress;
+ bool bSkipDuplicates;
+ bool bCopyOutputData;
+ bool bConditionSourceRange;
+ std::vector<ConnStackItem> maConnStack;
+
+public:
+
+ ScXMLFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLFilterContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void OpenConnection(bool b);
+ void CloseConnection();
+ bool GetConnection();
+};
+
+class ScXMLAndContext : public ScXMLImportContext
+{
+ ScQueryParam& mrQueryParam;
+ ScXMLFilterContext* pFilterContext;
+
+public:
+
+ ScXMLAndContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLAndContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLOrContext : public ScXMLImportContext
+{
+ ScQueryParam& mrQueryParam;
+ ScXMLFilterContext* pFilterContext;
+
+public:
+
+ ScXMLOrContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLOrContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLConditionContext : public ScXMLImportContext
+{
+ ScQueryParam& mrQueryParam;
+ ScXMLFilterContext* pFilterContext;
+
+ ScQueryEntry::QueryItemsType maQueryItems;
+ OUString sDataType;
+ OUString sConditionValue;
+ OUString sOperator;
+ sal_Int32 nField;
+ bool bIsCaseSensitive;
+
+public:
+
+ ScXMLConditionContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLConditionContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ static void GetOperator(std::u16string_view aOpStr, ScQueryParam& rParam, ScQueryEntry& rEntry);
+ void AddSetItem(const ScQueryEntry::Item& rItem);
+};
+
+class ScXMLSetItemContext : public ScXMLImportContext
+{
+public:
+ ScXMLSetItemContext(ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLConditionContext& rParent);
+
+ virtual ~ScXMLSetItemContext() override;
+};
+
+// Datapilot (Core)
+
+class ScXMLDPFilterContext : public ScXMLImportContext
+{
+ ScXMLDataPilotTableContext* pDataPilotTable;
+
+ ScQueryParam aFilterFields;
+ utl::SearchParam::SearchType eSearchType;
+ sal_uInt8 nFilterFieldCount;
+ bool bSkipDuplicates:1;
+ bool bIsCaseSensitive:1;
+ bool bConnectionOr:1;
+ bool bNextConnectionOr:1;
+ ::std::stack<bool> aConnectionOrStack;
+
+public:
+
+ ScXMLDPFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTableContext);
+
+ virtual ~ScXMLDPFilterContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetIsCaseSensitive(const bool bTemp) { bIsCaseSensitive = bTemp; }
+ void SetSearchType(const utl::SearchParam::SearchType eTemp)
+ {
+ if (eSearchType == utl::SearchParam::SearchType::Normal)
+ eSearchType = eTemp;
+ }
+
+ void OpenConnection(const bool bVal)
+ {
+ bool bTemp = bConnectionOr;
+ bConnectionOr = bNextConnectionOr;
+ bNextConnectionOr = bVal;
+ aConnectionOrStack.push(bTemp);
+ }
+
+ void CloseConnection()
+ {
+ bool bTemp;
+ if (aConnectionOrStack.empty())
+ bTemp = false;
+ else
+ {
+ bTemp = aConnectionOrStack.top();
+ aConnectionOrStack.pop();
+ }
+ bConnectionOr = bTemp;
+ bNextConnectionOr = bTemp;
+ }
+
+ bool GetConnection() { bool bTemp = bConnectionOr; bConnectionOr = bNextConnectionOr; return bTemp; }
+ void AddFilterField (const ScQueryEntry& aFilterField);
+};
+
+class ScXMLDPAndContext : public ScXMLImportContext
+{
+ ScXMLDPFilterContext* pFilterContext;
+public:
+
+ ScXMLDPAndContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLDPAndContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDPOrContext : public ScXMLImportContext
+{
+ ScXMLDPFilterContext* pFilterContext;
+public:
+
+ ScXMLDPOrContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLDPOrContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDPConditionContext : public ScXMLImportContext
+{
+ ScXMLDPFilterContext* pFilterContext;
+
+ OUString sDataType;
+ OUString sConditionValue;
+ OUString sOperator;
+ sal_Int32 nField;
+ bool bIsCaseSensitive;
+
+public:
+
+ ScXMLDPConditionContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDPFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLDPConditionContext() override;
+
+ static void getOperatorXML(
+ std::u16string_view sTempOperator, ScQueryOp& aFilterOperator, utl::SearchParam::SearchType& rSearchType);
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlfonte.cxx b/sc/source/filter/xml/xmlfonte.cxx
new file mode 100644
index 0000000000..ab8e42f4ce
--- /dev/null
+++ b/sc/source/filter/xml/xmlfonte.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 <scitems.hxx>
+
+#include <editeng/eeitem.hxx>
+
+#include <xmloff/XMLFontAutoStylePool.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/editeng.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include "xmlexprt.hxx"
+#include <stlpool.hxx>
+#include <attrib.hxx>
+
+namespace {
+
+class ScXMLFontAutoStylePool_Impl: public XMLFontAutoStylePool
+{
+private:
+ // #i120077# remember owned pool
+ rtl::Reference<SfxItemPool> mpEditEnginePool;
+
+ void AddFontItems(const sal_uInt16* pWhichIds, sal_uInt8 nIdCount, const SfxItemPool* pItemPool, const bool bExportDefaults);
+
+public:
+ ScXMLFontAutoStylePool_Impl( ScXMLExport& rExport, bool bEmbedFonts);
+};
+
+}
+
+void ScXMLFontAutoStylePool_Impl::AddFontItems(const sal_uInt16* pWhichIds, sal_uInt8 nIdCount, const SfxItemPool* pItemPool, const bool bExportDefaults)
+{
+ for( sal_uInt16 i=0; i < nIdCount; ++i )
+ {
+ sal_uInt16 nWhichId(pWhichIds[i]);
+ if (bExportDefaults)
+ {
+ const SfxPoolItem* pItem = &pItemPool->GetDefaultItem(nWhichId);
+ const SvxFontItem *pFont(static_cast<const SvxFontItem *>(pItem));
+ Add( pFont->GetFamilyName(), pFont->GetStyleName(),
+ pFont->GetFamily(), pFont->GetPitch(),
+ pFont->GetCharSet() );
+ }
+ for (const SfxPoolItem* pItem : pItemPool->GetItemSurrogates( nWhichId ))
+ {
+ const SvxFontItem *pFont(static_cast<const SvxFontItem *>(pItem));
+ Add( pFont->GetFamilyName(), pFont->GetStyleName(),
+ pFont->GetFamily(), pFont->GetPitch(),
+ pFont->GetCharSet() );
+ }
+ }
+}
+
+ScXMLFontAutoStylePool_Impl::ScXMLFontAutoStylePool_Impl(ScXMLExport& rExportP, bool bEmbedFonts)
+ : XMLFontAutoStylePool(rExportP, bEmbedFonts)
+{
+ sal_uInt16 const aWhichIds[] { ATTR_FONT, ATTR_CJK_FONT,
+ ATTR_CTL_FONT };
+ sal_uInt16 const aEditWhichIds[] { EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK,
+ EE_CHAR_FONTINFO_CTL };
+ sal_uInt16 const aPageWhichIds[] { ATTR_PAGE_HEADERLEFT, ATTR_PAGE_FOOTERLEFT,
+ ATTR_PAGE_HEADERRIGHT, ATTR_PAGE_FOOTERRIGHT,
+ ATTR_PAGE_HEADERFIRST, ATTR_PAGE_FOOTERFIRST };
+
+ const SfxItemPool* pItemPool(rExportP.GetDocument()->GetPool());
+ AddFontItems(aWhichIds, 3, pItemPool, true);
+ const SfxItemPool* pEditPool(rExportP.GetDocument()->GetEditPool());
+ AddFontItems(aEditWhichIds, 3, pEditPool, false);
+
+ std::unique_ptr<SfxStyleSheetIterator> pItr = rExportP.GetDocument()->GetStyleSheetPool()->CreateIterator(SfxStyleFamily::Page);
+
+ m_bEmbedUsedOnly = rExportP.GetDocument()->IsEmbedUsedFontsOnly();
+ m_bEmbedLatinScript = rExportP.GetDocument()->IsEmbedFontScriptLatin();
+ m_bEmbedAsianScript = rExportP.GetDocument()->IsEmbedFontScriptAsian();
+ m_bEmbedComplexScript = rExportP.GetDocument()->IsEmbedFontScriptComplex();
+
+ if(!pItr)
+ return;
+
+ SfxStyleSheetBase* pStyle(pItr->First());
+
+ if(!pStyle)
+ return;
+
+ // #i120077# remember the SfxItemPool in member variable before usage. The
+ // local EditEngine will not take over ownership of the pool.
+ mpEditEnginePool = EditEngine::CreatePool();
+ EditEngine aEditEngine(mpEditEnginePool.get());
+
+ while (pStyle)
+ {
+ const SfxItemPool& rPagePool(pStyle->GetPool()->GetPool());
+
+ for (sal_uInt16 nPageWhichId : aPageWhichIds)
+ {
+ for (const SfxPoolItem* pItem : rPagePool.GetItemSurrogates( nPageWhichId ))
+ {
+ const ScPageHFItem* pPageItem = static_cast<const ScPageHFItem*>(pItem);
+ const EditTextObject* pLeftArea(pPageItem->GetLeftArea());
+ if (pLeftArea)
+ {
+ aEditEngine.SetText(*pLeftArea);
+ AddFontItems(aEditWhichIds, 3, mpEditEnginePool.get(), false);
+ }
+ const EditTextObject* pCenterArea(pPageItem->GetCenterArea());
+ if (pCenterArea)
+ {
+ aEditEngine.SetText(*pCenterArea);
+ AddFontItems(aEditWhichIds, 3, mpEditEnginePool.get(), false);
+ }
+ const EditTextObject* pRightArea(pPageItem->GetRightArea());
+ if (pRightArea)
+ {
+ aEditEngine.SetText(*pRightArea);
+ AddFontItems(aEditWhichIds, 3, mpEditEnginePool.get(), false);
+ }
+ }
+ }
+
+ pStyle = pItr->Next();
+ }
+}
+
+XMLFontAutoStylePool* ScXMLExport::CreateFontAutoStylePool()
+{
+ bool blockFontEmbedding = false;
+ // We write font info to both content.xml and styles.xml, but they are both
+ // written by different ScXMLExport instance, and would therefore write each
+ // font file twice without complicated checking for duplicates, so handle
+ // the embedding only in one of them.
+ if(!( getExportFlags() & SvXMLExportFlags::CONTENT ))
+ blockFontEmbedding = true;
+ if (!GetDocument()->IsEmbedFonts())
+ blockFontEmbedding = true;
+ return new ScXMLFontAutoStylePool_Impl( *this, !blockFontEmbedding );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlimprt.cxx b/sc/source/filter/xml/xmlimprt.cxx
new file mode 100644
index 0000000000..60fa7ba717
--- /dev/null
+++ b/sc/source/filter/xml/xmlimprt.cxx
@@ -0,0 +1,1768 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <svl/numformat.hxx>
+
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmlmetai.hxx>
+#include <sfx2/objsh.hxx>
+#include <unotools/streamwrap.hxx>
+#include <xmloff/xmlscripti.hxx>
+#include <xmloff/XMLFontStylesContext.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/numehelp.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlerror.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <svx/svdpage.hxx>
+
+#include <svl/languageoptions.hxx>
+#include <editeng/editstat.hxx>
+#include <formula/errorcodes.hxx>
+#include <vcl/svapp.hxx>
+
+#include <appluno.hxx>
+#include "xmlimprt.hxx"
+#include "importcontext.hxx"
+#include <document.hxx>
+#include <docsh.hxx>
+#include <docuno.hxx>
+#include "xmlbodyi.hxx"
+#include "xmlstyli.hxx"
+#include <ViewSettingsSequenceDefines.hxx>
+#include <userdat.hxx>
+
+#include <compiler.hxx>
+
+#include "XMLConverter.hxx"
+#include "XMLDetectiveContext.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+#include "XMLChangeTrackingImportHelper.hxx"
+#include <chgviset.hxx>
+#include "XMLStylesImportHelper.hxx"
+#include <sheetdata.hxx>
+#include <rangeutl.hxx>
+#include <formulaparserpool.hxx>
+#include <externalrefmgr.hxx>
+#include <editutil.hxx>
+#include "editattributemap.hxx"
+#include <documentimport.hxx>
+#include "pivotsource.hxx"
+#include <unonames.hxx>
+#include <numformat.hxx>
+#include <sizedev.hxx>
+#include <scdll.hxx>
+#include "xmlstyle.hxx"
+
+#include <comphelper/base64.hxx>
+#include <comphelper/extract.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/util/MalformedNumberFormatException.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/sheet/XLabelRanges.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
+#include <cellsuno.hxx>
+
+#include <memory>
+#include <utility>
+
+constexpr OUString SC_LOCALE = u"Locale"_ustr;
+constexpr OUStringLiteral SC_CURRENCYSYMBOL = u"CurrencySymbol";
+constexpr OUStringLiteral SC_REPEAT_ROW = u"repeat-row";
+constexpr OUStringLiteral SC_FILTER = u"filter";
+constexpr OUStringLiteral SC_PRINT_RANGE = u"print-range";
+constexpr OUStringLiteral SC_HIDDEN = u"hidden";
+
+using namespace com::sun::star;
+using namespace ::xmloff::token;
+using namespace ::formula;
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisImporter",
+ SvXMLImportFlags::ALL,
+ { "com.sun.star.comp.Calc.XMLOasisImporter" } ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisMetaImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisMetaImporter",
+ SvXMLImportFlags::META,
+ { "com.sun.star.comp.Calc.XMLOasisMetaImporter" } ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisStylesImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisStylesImporter",
+ SvXMLImportFlags::STYLES|SvXMLImportFlags::AUTOSTYLES|SvXMLImportFlags::MASTERSTYLES|SvXMLImportFlags::FONTDECLS,
+ { "com.sun.star.comp.Calc.XMLOasisStylesImporter" } ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisContentImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisContentImporter",
+ SvXMLImportFlags::AUTOSTYLES|SvXMLImportFlags::CONTENT|SvXMLImportFlags::SCRIPTS|SvXMLImportFlags::FONTDECLS,
+ uno::Sequence< OUString > { "com.sun.star.comp.Calc.XMLOasisContentImporter" }));
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisSettingsImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS,
+ { "com.sun.star.comp.Calc.XMLOasisSettingsImporter" } ));
+}
+
+namespace {
+
+// NB: virtually inherit so we can multiply inherit properly
+// in ScXMLFlatDocContext_Impl
+class ScXMLDocContext_Impl : public virtual SvXMLImportContext
+{
+protected:
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+public:
+ ScXMLDocContext_Impl( ScXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+}
+
+ScXMLDocContext_Impl::ScXMLDocContext_Impl( ScXMLImport& rImport ) :
+SvXMLImportContext( rImport )
+{
+}
+
+namespace {
+
+// context for flat file xml format
+class ScXMLFlatDocContext_Impl
+ : public ScXMLDocContext_Impl, public SvXMLMetaDocumentContext
+{
+public:
+
+ ScXMLFlatDocContext_Impl( ScXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+}
+
+ScXMLFlatDocContext_Impl::ScXMLFlatDocContext_Impl( ScXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps) :
+SvXMLImportContext(i_rImport),
+ScXMLDocContext_Impl(i_rImport),
+SvXMLMetaDocumentContext(i_rImport, i_xDocProps)
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLFlatDocContext_Impl::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if ( nElement == XML_ELEMENT( OFFICE, XML_META ) )
+ return SvXMLMetaDocumentContext::createFastChildContext( nElement, xAttrList );
+ else
+ return ScXMLDocContext_Impl::createFastChildContext( nElement, xAttrList );
+}
+
+namespace {
+
+class ScXMLBodyContext_Impl : public ScXMLImportContext
+{
+public:
+ ScXMLBodyContext_Impl( ScXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+}
+
+ScXMLBodyContext_Impl::ScXMLBodyContext_Impl( ScXMLImport& rImport ) :
+ScXMLImportContext( rImport )
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLBodyContext_Impl::createFastChildContext( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+ return GetScImport().CreateBodyContext( pAttribList );
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLDocContext_Impl::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( OFFICE, XML_BODY ):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::CONTENT)
+ pContext = new ScXMLBodyContext_Impl( GetScImport() );
+ break;
+ case XML_ELEMENT( OFFICE, XML_SCRIPTS ):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::SCRIPTS)
+ pContext = GetScImport().CreateScriptContext();
+ break;
+ case XML_ELEMENT( OFFICE, XML_SETTINGS ):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::SETTINGS)
+ pContext = new XMLDocumentSettingsContext(GetScImport());
+ break;
+ case XML_ELEMENT(OFFICE, XML_STYLES):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::STYLES)
+ pContext = GetScImport().CreateStylesContext( false);
+ break;
+ case XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::AUTOSTYLES)
+ pContext = GetScImport().CreateStylesContext( true);
+ break;
+ case XML_ELEMENT(OFFICE, XML_FONT_FACE_DECLS):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::FONTDECLS)
+ pContext = GetScImport().CreateFontDeclsContext();
+ break;
+ case XML_ELEMENT(OFFICE, XML_MASTER_STYLES):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::MASTERSTYLES)
+ pContext = new ScXMLMasterStylesContext( GetImport() );
+ break;
+ case XML_ELEMENT(OFFICE, XML_META):
+ SAL_INFO("sc", "XML_ELEMENT(OFFICE, XML_META): should not have come here, maybe document is invalid?");
+ break;
+ }
+
+ return pContext;
+}
+
+
+void ScXMLImport::SetPostProcessData( sc::ImportPostProcessData* p )
+{
+ mpPostProcessData = p;
+}
+
+sc::PivotTableSources& ScXMLImport::GetPivotTableSources()
+{
+ if (!mpPivotSources)
+ mpPivotSources.reset(new sc::PivotTableSources);
+
+ return *mpPivotSources;
+}
+
+SvXMLImportContext *ScXMLImport::CreateFastContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_STYLES ):
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_CONTENT ):
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_SETTINGS ):
+ pContext = new ScXMLDocContext_Impl( *this );
+ break;
+
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ):
+ pContext = CreateMetaContext(nElement);
+ break;
+
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT ):
+ {
+ // flat OpenDocument file format
+ pContext = new ScXMLFlatDocContext_Impl( *this, GetScModel()->getDocumentProperties());
+ break;
+ }
+
+ }
+
+ return pContext;
+}
+
+ScXMLImport::ScXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlag,
+ const css::uno::Sequence< OUString > & sSupportedServiceNames)
+: SvXMLImport( rContext, implementationName, nImportFlag, sSupportedServiceNames ),
+ pDoc( nullptr ),
+ mpPostProcessData(nullptr),
+ aTables(*this),
+ nSolarMutexLocked(0),
+ nProgressCount(0),
+ nPrevCellType(0),
+ bLoadDoc( true ),
+ bNullDateSetted(false),
+ bSelfImportingXMLSet(false),
+ mbLockSolarMutex(true),
+ mbImportStyles(true),
+ mbHasNewCondFormatData(false)
+{
+ pStylesImportHelper.reset(new ScMyStylesImportHelper(*this));
+
+ xScPropHdlFactory = new XMLScPropHdlFactory;
+ xCellStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScCellStylesProperties, xScPropHdlFactory, false);
+ xColumnStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScColumnStylesProperties, xScPropHdlFactory, false);
+ xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScRowStylesImportProperties, xScPropHdlFactory, false);
+ xTableStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScTableStylesImportProperties, xScPropHdlFactory, false);
+
+ // #i66550# needed for 'presentation:event-listener' element for URLs in shapes
+ GetNamespaceMap().Add(
+ GetXMLToken( XML_NP_PRESENTATION ),
+ GetXMLToken( XML_N_PRESENTATION ),
+ XML_NAMESPACE_PRESENTATION );
+}
+
+ScXMLImport::~ScXMLImport() noexcept
+{
+ pChangeTrackingImportHelper.reset();
+ pNumberFormatAttributesExportHelper.reset();
+ pStyleNumberFormats.reset();
+ pStylesImportHelper.reset();
+
+ m_aMyNamedExpressions.clear();
+ maMyLabelRanges.clear();
+ maValidations.clear();
+ pDetectiveOpArray.reset();
+
+ //call SvXMLImport dtor contents before deleting pSolarMutexGuard
+ cleanup();
+
+ moSolarMutexGuard.reset();
+}
+
+void ScXMLImport::initialize( const css::uno::Sequence<css::uno::Any>& aArguments )
+{
+ SvXMLImport::initialize(aArguments);
+
+ uno::Reference<beans::XPropertySet> xInfoSet = getImportInfo();
+ if (!xInfoSet.is())
+ return;
+
+ uno::Reference<beans::XPropertySetInfo> xInfoSetInfo = xInfoSet->getPropertySetInfo();
+ if (!xInfoSetInfo.is())
+ return;
+
+ if (xInfoSetInfo->hasPropertyByName(SC_UNO_ODS_LOCK_SOLAR_MUTEX))
+ xInfoSet->getPropertyValue(SC_UNO_ODS_LOCK_SOLAR_MUTEX) >>= mbLockSolarMutex;
+
+ if (xInfoSetInfo->hasPropertyByName(SC_UNO_ODS_IMPORT_STYLES))
+ xInfoSet->getPropertyValue(SC_UNO_ODS_IMPORT_STYLES) >>= mbImportStyles;
+}
+
+SvXMLImportContext *ScXMLImport::CreateFontDeclsContext()
+{
+ XMLFontStylesContext *pFSContext = new XMLFontStylesContext(
+ *this, osl_getThreadTextEncoding());
+ SetFontDecls(pFSContext);
+ SvXMLImportContext* pContext = pFSContext;
+ return pContext;
+}
+
+SvXMLImportContext *ScXMLImport::CreateStylesContext( bool bIsAutoStyle )
+{
+ SvXMLImportContext* pContext = new XMLTableStylesContext(
+ *this, bIsAutoStyle);
+
+ if (bIsAutoStyle)
+ SetAutoStyles(static_cast<SvXMLStylesContext*>(pContext));
+ else
+ SetStyles(static_cast<SvXMLStylesContext*>(pContext));
+
+ return pContext;
+}
+
+SvXMLImportContext *ScXMLImport::CreateBodyContext(const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+{
+ return new ScXMLBodyContext(*this, rAttrList);
+}
+
+SvXMLImportContext *ScXMLImport::CreateMetaContext(
+ const sal_Int32 /*nElement*/ )
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ if (getImportFlags() & SvXMLImportFlags::META)
+ {
+ uno::Reference<document::XDocumentProperties> const xDocProps(
+ (IsStylesOnlyMode()) ? nullptr : GetScModel()->getDocumentProperties());
+ pContext = new SvXMLMetaDocumentContext(*this, xDocProps);
+ }
+
+ return pContext;
+}
+
+SvXMLImportContext *ScXMLImport::CreateScriptContext()
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ if( !(IsStylesOnlyMode()) )
+ {
+ pContext = new XMLScriptContext( *this, GetModel() );
+ }
+
+ return pContext;
+}
+
+void ScXMLImport::SetStatistics(const uno::Sequence<beans::NamedValue> & i_rStats)
+{
+ static const char* s_stats[] =
+ { "TableCount", "CellCount", "ObjectCount", nullptr };
+
+ SvXMLImport::SetStatistics(i_rStats);
+
+ sal_uInt64 nCount(0);
+ for (const auto& rStat : i_rStats) {
+ for (const char** pStat = s_stats; *pStat != nullptr; ++pStat) {
+ if (rStat.Name.equalsAscii(*pStat)) {
+ sal_Int32 val = 0;
+ if (rStat.Value >>= val) {
+ nCount += val;
+ } else {
+ OSL_FAIL("ScXMLImport::SetStatistics: invalid entry");
+ }
+ }
+ }
+ }
+
+ if (nCount)
+ {
+ GetProgressBarHelper()->SetReference(nCount);
+ GetProgressBarHelper()->SetValue(0);
+ }
+}
+
+ScDocumentImport& ScXMLImport::GetDoc()
+{
+ return *mpDocImport;
+}
+
+sal_Int16 ScXMLImport::GetCellType(const char* rStrValue, const sal_Int32 nStrLength)
+{
+ sal_Int16 nCellType = util::NumberFormat::UNDEFINED;
+ if (rStrValue != nullptr)
+ {
+ switch (rStrValue[0])
+ {
+ case 'b':
+ if (nStrLength == 7 && !strcmp(rStrValue, "boolean"))
+ nCellType = util::NumberFormat::LOGICAL;
+ break;
+ case 'c':
+ if (nStrLength == 8 && !strcmp(rStrValue, "currency"))
+ nCellType = util::NumberFormat::CURRENCY;
+ break;
+ case 'd':
+ if (nStrLength == 4 && !strcmp(rStrValue, "date"))
+ nCellType = util::NumberFormat::DATETIME;
+ break;
+ case 'f':
+ if (nStrLength == 5 && !strcmp(rStrValue, "float"))
+ nCellType = util::NumberFormat::NUMBER;
+ break;
+ case 'p':
+ if (nStrLength == 10 && !strcmp(rStrValue, "percentage"))
+ nCellType = util::NumberFormat::PERCENT;
+ break;
+ case 's':
+ if (nStrLength == 6 && !strcmp(rStrValue, "string"))
+ nCellType = util::NumberFormat::TEXT;
+ break;
+ case 't':
+ if (nStrLength == 4 && !strcmp(rStrValue, "time"))
+ nCellType = util::NumberFormat::TIME;
+ break;
+ }
+ }
+
+ return nCellType;
+}
+
+XMLShapeImportHelper* ScXMLImport::CreateShapeImport()
+{
+ return new XMLTableShapeImportHelper(*this);
+}
+
+bool ScXMLImport::GetValidation(const OUString& sName, ScMyImportValidation& aValidation)
+{
+ auto aItr = std::find_if(maValidations.begin(), maValidations.end(),
+ [&sName](const ScMyImportValidation& rValidation) { return rValidation.sName == sName; });
+ if (aItr != maValidations.end())
+ {
+ // source position must be set as string,
+ // so sBaseCellAddress no longer has to be converted here
+ aValidation = *aItr;
+ return true;
+ }
+ return false;
+}
+
+void ScXMLImport::AddNamedExpression(SCTAB nTab, ScMyNamedExpression aNamedExp)
+{
+ SheetNamedExpMap::iterator itr = m_SheetNamedExpressions.find(nTab);
+ if (itr == m_SheetNamedExpressions.end())
+ {
+ // No chain exists for this sheet. Create one.
+ ::std::pair<SheetNamedExpMap::iterator, bool> r =
+ m_SheetNamedExpressions.insert(std::make_pair(nTab, ScMyNamedExpressions()));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ ScMyNamedExpressions& r = itr->second;
+ r.push_back(std::move(aNamedExp));
+}
+
+ScXMLChangeTrackingImportHelper* ScXMLImport::GetChangeTrackingImportHelper()
+{
+ if (!pChangeTrackingImportHelper)
+ pChangeTrackingImportHelper.reset(new ScXMLChangeTrackingImportHelper());
+ return pChangeTrackingImportHelper.get();
+}
+
+void ScXMLImport::InsertStyles()
+{
+ GetStyles()->CopyStylesToDoc(true);
+
+ // if content is going to be loaded with the same import, set bLatinDefaultStyle flag now
+ if ( getImportFlags() & SvXMLImportFlags::CONTENT )
+ ExamineDefaultStyle();
+}
+
+void ScXMLImport::ExamineDefaultStyle()
+{
+ if (pDoc)
+ {
+ // #i62435# after inserting the styles, check if the default style has a latin-script-only
+ // number format (then, value cells can be pre-initialized with western script type)
+
+ const ScPatternAttr* pDefPattern = pDoc->GetDefPattern();
+ if (pDefPattern && sc::NumFmtUtil::isLatinScript(*pDefPattern, *pDoc))
+ mpDocImport->setDefaultNumericScript(SvtScriptType::LATIN);
+ }
+}
+
+void ScXMLImport::SetChangeTrackingViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& rChangeProps)
+{
+ if (!pDoc)
+ return;
+
+ if (!rChangeProps.hasElements())
+ return;
+
+ ScXMLImport::MutexGuard aGuard(*this);
+ sal_Int16 nTemp16(0);
+ ScChangeViewSettings aViewSettings;
+ for (const auto& rChangeProp : rChangeProps)
+ {
+ OUString sName(rChangeProp.Name);
+ if (sName == "ShowChanges")
+ aViewSettings.SetShowChanges(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowAcceptedChanges")
+ aViewSettings.SetShowAccepted(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowRejectedChanges")
+ aViewSettings.SetShowRejected(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByDatetime")
+ aViewSettings.SetHasDate(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByDatetimeMode")
+ {
+ if (rChangeProp.Value >>= nTemp16)
+ aViewSettings.SetTheDateMode(static_cast<SvxRedlinDateMode>(nTemp16));
+ }
+ else if (sName == "ShowChangesByDatetimeFirstDatetime")
+ {
+ util::DateTime aDateTime;
+ if (rChangeProp.Value >>= aDateTime)
+ {
+ aViewSettings.SetTheFirstDateTime(::DateTime(aDateTime));
+ }
+ }
+ else if (sName == "ShowChangesByDatetimeSecondDatetime")
+ {
+ util::DateTime aDateTime;
+ if (rChangeProp.Value >>= aDateTime)
+ {
+ aViewSettings.SetTheLastDateTime(::DateTime(aDateTime));
+ }
+ }
+ else if (sName == "ShowChangesByAuthor")
+ aViewSettings.SetHasAuthor(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByAuthorName")
+ {
+ OUString sOUName;
+ if (rChangeProp.Value >>= sOUName)
+ {
+ aViewSettings.SetTheAuthorToShow(sOUName);
+ }
+ }
+ else if (sName == "ShowChangesByComment")
+ aViewSettings.SetHasComment(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByCommentText")
+ {
+ OUString sOUComment;
+ if (rChangeProp.Value >>= sOUComment)
+ {
+ aViewSettings.SetTheComment(sOUComment);
+ }
+ }
+ else if (sName == "ShowChangesByRanges")
+ aViewSettings.SetHasRange(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByRangesList")
+ {
+ OUString sRanges;
+ if ((rChangeProp.Value >>= sRanges) && !sRanges.isEmpty())
+ {
+ ScRangeList aRangeList;
+ ScRangeStringConverter::GetRangeListFromString(
+ aRangeList, sRanges, *pDoc, FormulaGrammar::CONV_OOO);
+ aViewSettings.SetTheRangeList(aRangeList);
+ }
+ }
+ }
+ pDoc->SetChangeViewSettings(aViewSettings);
+}
+
+void ScXMLImport::SetViewSettings(const uno::Sequence<beans::PropertyValue>& aViewProps)
+{
+ sal_Int32 nHeight(0);
+ sal_Int32 nLeft(0);
+ sal_Int32 nTop(0);
+ sal_Int32 nWidth(0);
+ for (const auto& rViewProp : aViewProps)
+ {
+ OUString sName(rViewProp.Name);
+ if (sName == "VisibleAreaHeight")
+ rViewProp.Value >>= nHeight;
+ else if (sName == "VisibleAreaLeft")
+ rViewProp.Value >>= nLeft;
+ else if (sName == "VisibleAreaTop")
+ rViewProp.Value >>= nTop;
+ else if (sName == "VisibleAreaWidth")
+ rViewProp.Value >>= nWidth;
+ else if (sName == "TrackedChangesViewSettings")
+ {
+ uno::Sequence<beans::PropertyValue> aChangeProps;
+ if(rViewProp.Value >>= aChangeProps)
+ SetChangeTrackingViewSettings(aChangeProps);
+ }
+ }
+ if (!(nHeight && nWidth && GetModel().is()))
+ return;
+
+ ScModelObj* pDocObj( GetScModel() );
+ if (!pDocObj)
+ return;
+
+ SfxObjectShell* pEmbeddedObj = pDocObj->GetEmbeddedObject();
+ if (pEmbeddedObj)
+ {
+ tools::Rectangle aRect{ nLeft, nTop };
+ aRect.setWidth( nWidth );
+ aRect.setHeight( nHeight );
+ pEmbeddedObj->SetVisArea(aRect);
+ }
+}
+
+void ScXMLImport::SetConfigurationSettings(const uno::Sequence<beans::PropertyValue>& aConfigProps)
+{
+ rtl::Reference<ScModelObj> xMultiServiceFactory(GetScModel());
+ if (!xMultiServiceFactory.is())
+ return;
+
+ sal_Int32 nCount(aConfigProps.getLength());
+ css::uno::Sequence<css::beans::PropertyValue> aFilteredProps(nCount);
+ auto pFilteredProps = aFilteredProps.getArray();
+ sal_Int32 nFilteredPropsLen = 0;
+ for (sal_Int32 i = nCount - 1; i >= 0; --i)
+ {
+ if (aConfigProps[i].Name == "TrackedChangesProtectionKey")
+ {
+ OUString sKey;
+ if (aConfigProps[i].Value >>= sKey)
+ {
+ uno::Sequence<sal_Int8> aPass;
+ ::comphelper::Base64::decode(aPass, sKey);
+ if (aPass.hasElements())
+ {
+ if (pDoc->GetChangeTrack())
+ pDoc->GetChangeTrack()->SetProtection(aPass);
+ else
+ {
+ std::set<OUString> aUsers;
+ std::unique_ptr<ScChangeTrack> pTrack( new ScChangeTrack(*pDoc, std::move(aUsers)) );
+ pTrack->SetProtection(aPass);
+ pDoc->SetChangeTrack(std::move(pTrack));
+ }
+ }
+ }
+ }
+ // store the following items for later use (after document is loaded)
+ else if ((aConfigProps[i].Name == "VBACompatibilityMode") || (aConfigProps[i].Name == "ScriptConfiguration"))
+ {
+ uno::Reference< beans::XPropertySet > xImportInfo = getImportInfo();
+ if (xImportInfo.is())
+ {
+ uno::Reference< beans::XPropertySetInfo > xPropertySetInfo = xImportInfo->getPropertySetInfo();
+ if (xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName(aConfigProps[i].Name))
+ xImportInfo->setPropertyValue( aConfigProps[i].Name, aConfigProps[i].Value );
+ }
+ }
+ if (aConfigProps[i].Name != "LinkUpdateMode")
+ {
+ pFilteredProps[nFilteredPropsLen++] = aConfigProps[i];
+ }
+ }
+ aFilteredProps.realloc(nFilteredPropsLen);
+ uno::Reference <uno::XInterface> xInterface = xMultiServiceFactory->createInstance("com.sun.star.comp.SpreadsheetSettings");
+ uno::Reference <beans::XPropertySet> xProperties(xInterface, uno::UNO_QUERY);
+ if (xProperties.is())
+ SvXMLUnitConverter::convertPropertySet(xProperties, aFilteredProps);
+}
+
+sal_Int32 ScXMLImport::SetCurrencySymbol(const sal_Int32 nKey, std::u16string_view rCurrency)
+{
+ uno::Reference <util::XNumberFormatsSupplier> xNumberFormatsSupplier(GetNumberFormatsSupplier());
+ if (xNumberFormatsSupplier.is())
+ {
+ uno::Reference <util::XNumberFormats> xLocalNumberFormats(xNumberFormatsSupplier->getNumberFormats());
+ if (xLocalNumberFormats.is())
+ {
+ OUString sFormatString;
+ try
+ {
+ uno::Reference <beans::XPropertySet> xProperties(xLocalNumberFormats->getByKey(nKey));
+ if (xProperties.is())
+ {
+ lang::Locale aLocale;
+ if (GetDocument() && (xProperties->getPropertyValue(SC_LOCALE) >>= aLocale))
+ {
+ {
+ ScXMLImport::MutexGuard aGuard(*this);
+ LocaleDataWrapper aLocaleData( comphelper::getProcessComponentContext(), LanguageTag( aLocale) );
+ sFormatString = "#" +
+ aLocaleData.getNumThousandSep() +
+ "##0" +
+ aLocaleData.getNumDecimalSep() +
+ "00 [$" +
+ rCurrency +
+ "]";
+ }
+ sal_Int32 nNewKey = xLocalNumberFormats->queryKey(sFormatString, aLocale, true);
+ if (nNewKey == -1)
+ nNewKey = xLocalNumberFormats->addNew(sFormatString, aLocale);
+ return nNewKey;
+ }
+ }
+ }
+ catch ( const util::MalformedNumberFormatException& rException )
+ {
+ OUString sErrorMessage ="Error in Formatstring " +
+ sFormatString + " at position " +
+ OUString::number(rException.CheckPos);
+ uno::Sequence<OUString> aSeq { sErrorMessage };
+ uno::Reference<xml::sax::XLocator> xLocator;
+ SetError(XMLERROR_API | XMLERROR_FLAG_ERROR, aSeq, rException.Message, xLocator);
+ }
+ }
+ }
+ return nKey;
+}
+
+bool ScXMLImport::IsCurrencySymbol(const sal_Int32 nNumberFormat, std::u16string_view sCurrentCurrency, std::u16string_view sBankSymbol)
+{
+ uno::Reference <util::XNumberFormatsSupplier> xNumberFormatsSupplier(GetNumberFormatsSupplier());
+ if (xNumberFormatsSupplier.is())
+ {
+ uno::Reference <util::XNumberFormats> xLocalNumberFormats(xNumberFormatsSupplier->getNumberFormats());
+ if (xLocalNumberFormats.is())
+ {
+ try
+ {
+ uno::Reference <beans::XPropertySet> xNumberPropertySet(xLocalNumberFormats->getByKey(nNumberFormat));
+ if (xNumberPropertySet.is())
+ {
+ OUString sTemp;
+ if ( xNumberPropertySet->getPropertyValue(SC_CURRENCYSYMBOL) >>= sTemp)
+ {
+ if (sCurrentCurrency == sTemp)
+ return true;
+ // A release that saved an unknown currency may have
+ // saved the currency symbol of the number format
+ // instead of an ISO code bank symbol. In another
+ // release we may have a match for that. In this case
+ // sCurrentCurrency is the ISO code obtained through
+ // XMLNumberFormatAttributesExportHelper::GetCellType()
+ // and sBankSymbol is the currency symbol.
+ if (sCurrentCurrency.size() == 3 && sBankSymbol == sTemp)
+ return true;
+ // #i61657# This may be a legacy currency symbol that changed in the meantime.
+ if (SvNumberFormatter::GetLegacyOnlyCurrencyEntry( sCurrentCurrency, sBankSymbol) != nullptr)
+ return true;
+ // In the rare case that sCurrentCurrency is not the
+ // currency symbol, but a matching ISO code
+ // abbreviation instead that was obtained through
+ // XMLNumberFormatAttributesExportHelper::GetCellType(),
+ // check with the number format's symbol. This happens,
+ // for example, in the es_BO locale, where a legacy
+ // B$,BOB matched B$->BOP, which leads to
+ // sCurrentCurrency being BOP, and the previous call
+ // with BOP,BOB didn't find an entry, but B$,BOB will.
+ return SvNumberFormatter::GetLegacyOnlyCurrencyEntry( sTemp, sBankSymbol) != nullptr;
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL("Numberformat not found");
+ }
+ }
+ }
+ return false;
+}
+
+void ScXMLImport::SetType(const uno::Reference <beans::XPropertySet>& rProperties,
+ sal_Int32& rNumberFormat,
+ const sal_Int16 nCellType,
+ std::u16string_view rCurrency)
+{
+ if (!mbImportStyles)
+ return;
+
+ if ((nCellType == util::NumberFormat::TEXT) || (nCellType == util::NumberFormat::UNDEFINED))
+ return;
+
+ if (rNumberFormat == -1)
+ rProperties->getPropertyValue( SC_UNONAME_NUMFMT ) >>= rNumberFormat;
+ OSL_ENSURE(rNumberFormat != -1, "no NumberFormat");
+ bool bIsStandard;
+ // sCurrentCurrency may be the ISO code abbreviation if the currency
+ // symbol matches such, or if no match found the symbol itself!
+ OUString sCurrentCurrency;
+ sal_Int32 nCurrentCellType(
+ GetNumberFormatAttributesExportHelper()->GetCellType(
+ rNumberFormat, sCurrentCurrency, bIsStandard) & ~util::NumberFormat::DEFINED);
+ // If the (numeric) cell type (number, currency, date, time, boolean)
+ // is different from the format type then for some combinations we may
+ // have to apply a format, e.g. in case the generator deduced format
+ // from type and did not apply a format but we don't keep a dedicated
+ // type internally. Specifically this is necessary if the cell type is
+ // not number but the format type is (i.e. General). Currency cells
+ // need extra attention, see calls of ScXMLImport::IsCurrencySymbol()
+ // and description within there and ScXMLImport::SetCurrencySymbol().
+ if ((nCellType != nCurrentCellType) &&
+ (nCellType != util::NumberFormat::NUMBER) &&
+ (bIsStandard || (nCellType == util::NumberFormat::CURRENCY)))
+ {
+ if (!xNumberFormats.is())
+ {
+ uno::Reference <util::XNumberFormatsSupplier> xNumberFormatsSupplier(GetNumberFormatsSupplier());
+ if (xNumberFormatsSupplier.is())
+ xNumberFormats.set(xNumberFormatsSupplier->getNumberFormats());
+ }
+ if (xNumberFormats.is())
+ {
+ try
+ {
+ uno::Reference < beans::XPropertySet> xNumberFormatProperties(xNumberFormats->getByKey(rNumberFormat));
+ if (xNumberFormatProperties.is())
+ {
+ if (nCellType != util::NumberFormat::CURRENCY)
+ {
+ lang::Locale aLocale;
+ if ( xNumberFormatProperties->getPropertyValue(SC_LOCALE) >>= aLocale )
+ {
+ if (!xNumberFormatTypes.is())
+ xNumberFormatTypes.set(uno::Reference <util::XNumberFormatTypes>(xNumberFormats, uno::UNO_QUERY));
+ rProperties->setPropertyValue( SC_UNONAME_NUMFMT, uno::Any(xNumberFormatTypes->getStandardFormat(nCellType, aLocale)) );
+ }
+ }
+ else if (!rCurrency.empty() && !sCurrentCurrency.isEmpty())
+ {
+ if (sCurrentCurrency != rCurrency)
+ if (!IsCurrencySymbol(rNumberFormat, sCurrentCurrency, rCurrency))
+ rProperties->setPropertyValue( SC_UNONAME_NUMFMT, uno::Any(SetCurrencySymbol(rNumberFormat, rCurrency)));
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL("Numberformat not found");
+ }
+ }
+ }
+ else
+ {
+ if ((nCellType == util::NumberFormat::CURRENCY) && !rCurrency.empty() && !sCurrentCurrency.isEmpty() &&
+ sCurrentCurrency != rCurrency && !IsCurrencySymbol(rNumberFormat, sCurrentCurrency, rCurrency))
+ rProperties->setPropertyValue( SC_UNONAME_NUMFMT, uno::Any(SetCurrencySymbol(rNumberFormat, rCurrency)));
+ }
+}
+
+void ScXMLImport::SetStyleToRanges()
+{
+ if (!mbImportStyles)
+ return;
+
+ if (!sPrevStyleName.isEmpty())
+ {
+ uno::Reference <beans::XPropertySet> xProperties (mxSheetCellRanges);
+ if (xProperties.is())
+ {
+ XMLTableStylesContext *pStyles(static_cast<XMLTableStylesContext *>(GetAutoStyles()));
+ XMLTableStyleContext* pStyle = nullptr;
+ if ( pStyles )
+ pStyle = const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_CELL, sPrevStyleName, true)));
+ if (pStyle)
+ {
+ pStyle->FillPropertySet(xProperties);
+ // here needs to be the cond format import method
+ sal_Int32 nNumberFormat(pStyle->GetNumberFormat());
+ SetType(xProperties, nNumberFormat, nPrevCellType, sPrevCurrency);
+
+ css::uno::Any aAny = xProperties->getPropertyValue("FormatID");
+ sal_uInt64 nKey = 0;
+ if ((aAny >>= nKey) && nKey)
+ {
+ ScFormatSaveData* pFormatSaveData = GetScModel()->GetFormatSaveData();
+ pFormatSaveData->maIDToName.insert(std::pair<sal_uInt64, OUString>(nKey, sPrevStyleName));
+ }
+
+ // store first cell of first range for each style, once per sheet
+ uno::Sequence<table::CellRangeAddress> aAddresses(mxSheetCellRanges->getRangeAddresses());
+ pStyle->ApplyCondFormat(aAddresses);
+ if ( aAddresses.hasElements() )
+ {
+ const table::CellRangeAddress& rRange = aAddresses[0];
+ if ( rRange.Sheet != pStyle->GetLastSheet() )
+ {
+ ScSheetSaveData* pSheetData = GetScModel()->GetSheetSaveData();
+ pSheetData->AddCellStyle( sPrevStyleName,
+ ScAddress( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), static_cast<SCTAB>(rRange.Sheet) ) );
+ pStyle->SetLastSheet(rRange.Sheet);
+ }
+ }
+ }
+ else
+ {
+ xProperties->setPropertyValue(SC_UNONAME_CELLSTYL, uno::Any(GetStyleDisplayName( XmlStyleFamily::TABLE_CELL, sPrevStyleName )));
+ sal_Int32 nNumberFormat(GetStyleNumberFormats()->GetStyleNumberFormat(sPrevStyleName));
+ bool bInsert(nNumberFormat == -1);
+ SetType(xProperties, nNumberFormat, nPrevCellType, sPrevCurrency);
+ if (bInsert)
+ GetStyleNumberFormats()->AddStyleNumberFormat(sPrevStyleName, nNumberFormat);
+ }
+ }
+ }
+ if (GetModel().is())
+ {
+ rtl::Reference<ScModelObj> xMultiServiceFactory(GetScModel());
+ mxSheetCellRanges = &dynamic_cast<ScCellRangesObj&>(
+ *xMultiServiceFactory->createInstance("com.sun.star.sheet.SheetCellRanges"));
+ }
+ OSL_ENSURE(mxSheetCellRanges.is(), "didn't get SheetCellRanges");
+}
+
+void ScXMLImport::SetStyleToRanges(const ScRangeList& rRanges, const OUString* pStyleName,
+ const sal_Int16 nCellType, const OUString* pCurrency)
+{
+ if (!mbImportStyles)
+ return;
+
+ if (sPrevStyleName.isEmpty())
+ {
+ nPrevCellType = nCellType;
+ if (pStyleName)
+ sPrevStyleName = *pStyleName;
+ if (pCurrency)
+ sPrevCurrency = *pCurrency;
+ else if (!sPrevCurrency.isEmpty())
+ sPrevCurrency.clear();
+ }
+ else if ((nCellType != nPrevCellType) ||
+ ((pStyleName && *pStyleName != sPrevStyleName) ||
+ (!pStyleName && !sPrevStyleName.isEmpty())) ||
+ ((pCurrency && *pCurrency != sPrevCurrency) ||
+ (!pCurrency && !sPrevCurrency.isEmpty())))
+ {
+ SetStyleToRanges();
+ nPrevCellType = nCellType;
+ if (pStyleName)
+ sPrevStyleName = *pStyleName;
+ else if(!sPrevStyleName.isEmpty())
+ sPrevStyleName.clear();
+ if (pCurrency)
+ sPrevCurrency = *pCurrency;
+ else if(!sPrevCurrency.isEmpty())
+ sPrevCurrency.clear();
+ }
+
+ if (!mxSheetCellRanges.is() && GetModel().is())
+ {
+ rtl::Reference<ScModelObj> xMultiServiceFactory(GetScModel());
+ mxSheetCellRanges = &dynamic_cast<ScCellRangesObj&>(*xMultiServiceFactory->createInstance("com.sun.star.sheet.SheetCellRanges"));
+ OSL_ENSURE(mxSheetCellRanges.is(), "didn't get SheetCellRanges");
+ }
+ mxSheetCellRanges->SetNewRanges(rRanges);
+}
+
+bool ScXMLImport::SetNullDateOnUnitConverter()
+{
+ if (!bNullDateSetted)
+ bNullDateSetted = GetMM100UnitConverter().setNullDate(GetModel());
+ OSL_ENSURE(bNullDateSetted, "could not set the null date");
+ return bNullDateSetted;
+}
+
+XMLNumberFormatAttributesExportHelper* ScXMLImport::GetNumberFormatAttributesExportHelper()
+{
+ if (!pNumberFormatAttributesExportHelper)
+ pNumberFormatAttributesExportHelper.reset(new XMLNumberFormatAttributesExportHelper(GetNumberFormatsSupplier()));
+ return pNumberFormatAttributesExportHelper.get();
+}
+
+ScMyStyleNumberFormats* ScXMLImport::GetStyleNumberFormats()
+{
+ if (!pStyleNumberFormats)
+ pStyleNumberFormats.reset(new ScMyStyleNumberFormats);
+ return pStyleNumberFormats.get();
+}
+
+void ScXMLImport::SetStylesToRangesFinished()
+{
+ SetStyleToRanges();
+ sPrevStyleName.clear();
+}
+
+// XImporter
+void SAL_CALL ScXMLImport::setTargetDocument( const css::uno::Reference< css::lang::XComponent >& xDoc )
+{
+ ScXMLImport::MutexGuard aGuard(*this);
+ SvXMLImport::setTargetDocument( xDoc );
+
+ uno::Reference<frame::XModel> xModel(xDoc, uno::UNO_QUERY);
+ pDoc = ScXMLConverter::GetScDocument( xModel );
+ OSL_ENSURE( pDoc, "ScXMLImport::setTargetDocument - no ScDocument!" );
+ if (!pDoc)
+ throw lang::IllegalArgumentException();
+
+ if (ScDocShell* pDocSh = pDoc->GetDocumentShell())
+ pDocSh->SetInitialLinkUpdate( pDocSh->GetMedium());
+
+ mpDocImport.reset(new ScDocumentImport(*pDoc));
+ mpComp.reset(new ScCompiler(*pDoc, ScAddress(), formula::FormulaGrammar::GRAM_ODFF));
+
+ uno::Reference<document::XActionLockable> xActionLockable(xDoc, uno::UNO_QUERY);
+ if (xActionLockable.is())
+ xActionLockable->addActionLock();
+}
+
+// css::xml::sax::XDocumentHandler
+void SAL_CALL ScXMLImport::startDocument()
+{
+ ScXMLImport::MutexGuard aGuard(*this);
+ SvXMLImport::startDocument();
+ if (pDoc && !pDoc->IsImportingXML())
+ {
+ GetScModel()->BeforeXMLLoading();
+ bSelfImportingXMLSet = true;
+ }
+
+ // if content and styles are loaded with separate imports,
+ // set bLatinDefaultStyle flag at the start of the content import
+ SvXMLImportFlags nFlags = getImportFlags();
+ if ( ( nFlags & SvXMLImportFlags::CONTENT ) && !( nFlags & SvXMLImportFlags::STYLES ) )
+ ExamineDefaultStyle();
+
+ if (getImportFlags() & SvXMLImportFlags::CONTENT)
+ {
+ if (GetModel().is())
+ {
+ // store initial namespaces, to find the ones that were added from the file later
+ ScSheetSaveData* pSheetData = GetScModel()->GetSheetSaveData();
+ const SvXMLNamespaceMap& rNamespaces = GetNamespaceMap();
+ pSheetData->StoreInitialNamespaces(rNamespaces);
+ }
+ }
+
+ uno::Reference< beans::XPropertySet > const xImportInfo( getImportInfo() );
+ uno::Reference< beans::XPropertySetInfo > const xPropertySetInfo(
+ xImportInfo.is() ? xImportInfo->getPropertySetInfo() : nullptr);
+ if (xPropertySetInfo.is())
+ {
+ static constexpr OUString sOrganizerMode(u"OrganizerMode"_ustr);
+ if (xPropertySetInfo->hasPropertyByName(sOrganizerMode))
+ {
+ bool bStyleOnly(false);
+ if (xImportInfo->getPropertyValue(sOrganizerMode) >>= bStyleOnly)
+ {
+ bLoadDoc = !bStyleOnly;
+ }
+ }
+ }
+
+ UnlockSolarMutex();
+}
+
+sal_Int32 ScXMLImport::GetRangeType(std::u16string_view sRangeType)
+{
+ sal_Int32 nRangeType(0);
+ OUStringBuffer sBuffer;
+ size_t i = 0;
+ while (i <= sRangeType.size())
+ {
+ if ((i == sRangeType.size()) || (sRangeType[i] == ' '))
+ {
+ OUString sTemp = sBuffer.makeStringAndClear();
+ if (sTemp == "repeat-column")
+ nRangeType |= sheet::NamedRangeFlag::COLUMN_HEADER;
+ else if (sTemp == SC_REPEAT_ROW)
+ nRangeType |= sheet::NamedRangeFlag::ROW_HEADER;
+ else if (sTemp == SC_FILTER)
+ nRangeType |= sheet::NamedRangeFlag::FILTER_CRITERIA;
+ else if (sTemp == SC_PRINT_RANGE)
+ nRangeType |= sheet::NamedRangeFlag::PRINT_AREA;
+ else if (sTemp == SC_HIDDEN)
+ nRangeType |= sheet::NamedRangeFlag::HIDDEN;
+ }
+ else if (i < sRangeType.size())
+ sBuffer.append(sRangeType[i]);
+ ++i;
+ }
+ return nRangeType;
+}
+
+void ScXMLImport::SetLabelRanges()
+{
+ if (maMyLabelRanges.empty())
+ return;
+
+ rtl::Reference<ScModelObj> xPropertySet (GetScModel());
+ if (!xPropertySet.is())
+ return;
+
+ uno::Any aColAny = xPropertySet->getPropertyValue(SC_UNO_COLLABELRNG);
+ uno::Any aRowAny = xPropertySet->getPropertyValue(SC_UNO_ROWLABELRNG);
+
+ uno::Reference< sheet::XLabelRanges > xColRanges;
+ uno::Reference< sheet::XLabelRanges > xRowRanges;
+
+ if ( !(( aColAny >>= xColRanges ) && ( aRowAny >>= xRowRanges )) )
+ return;
+
+ table::CellRangeAddress aLabelRange;
+ table::CellRangeAddress aDataRange;
+
+ for (const auto& rLabelRange : maMyLabelRanges)
+ {
+ sal_Int32 nOffset1(0);
+ sal_Int32 nOffset2(0);
+ FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO;
+
+ assert(pDoc);
+ if (ScRangeStringConverter::GetRangeFromString( aLabelRange, rLabelRange.sLabelRangeStr, *pDoc, eConv, nOffset1 ) &&
+ ScRangeStringConverter::GetRangeFromString( aDataRange, rLabelRange.sDataRangeStr, *pDoc, eConv, nOffset2 ))
+ {
+ if ( rLabelRange.bColumnOrientation )
+ xColRanges->addNew( aLabelRange, aDataRange );
+ else
+ xRowRanges->addNew( aLabelRange, aDataRange );
+ }
+ }
+
+ maMyLabelRanges.clear();
+}
+
+namespace {
+
+class RangeNameInserter
+{
+ ScDocument& mrDoc;
+ ScRangeName& mrRangeName;
+ SCTAB mnTab;
+
+public:
+ RangeNameInserter(ScDocument& rDoc, ScRangeName& rRangeName, SCTAB nTab) :
+ mrDoc(rDoc), mrRangeName(rRangeName), mnTab(nTab) {}
+
+ void operator() (const ScMyNamedExpression& p) const
+ {
+ using namespace formula;
+
+ const OUString& aType = p.sRangeType;
+ sal_uInt32 nUnoType = ScXMLImport::GetRangeType(aType);
+
+ ScRangeData::Type nNewType = ScRangeData::Type::Name;
+ if ( nUnoType & sheet::NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria;
+ if ( nUnoType & sheet::NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea;
+ if ( nUnoType & sheet::NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::HIDDEN ) nNewType |= ScRangeData::Type::Hidden;
+
+ // Insert a new name.
+ ScAddress aPos;
+ sal_Int32 nOffset = 0;
+ bool bSuccess = ScRangeStringConverter::GetAddressFromString(
+ aPos, p.sBaseCellAddress, mrDoc, FormulaGrammar::CONV_OOO, nOffset);
+
+ if (!bSuccess)
+ {
+ SAL_WARN("sc.filter", "No conversion from table:base-cell-address '" << p.sBaseCellAddress
+ << "' for name '" << p.sName << "' on sheet " << mnTab);
+ // Do not lose the defined name. Relative addressing in
+ // content/expression, if any, will be broken though.
+ // May had happened due to tdf#150312.
+ aPos.SetTab(mnTab < 0 ? 0 : mnTab);
+ bSuccess = true;
+ }
+
+ if (bSuccess)
+ {
+ OUString aContent = p.sContent;
+ if (!p.bIsExpression)
+ ScXMLConverter::ConvertCellRangeAddress(aContent);
+
+ ScRangeData* pData = new ScRangeData(
+ mrDoc, p.sName, aContent, aPos, nNewType, p.eGrammar);
+ mrRangeName.insert(pData);
+ }
+ }
+};
+
+}
+
+void ScXMLImport::SetNamedRanges()
+{
+ if (m_aMyNamedExpressions.empty())
+ return;
+
+ if (!pDoc)
+ return;
+
+ // Insert the namedRanges
+ ScRangeName* pRangeNames = pDoc->GetRangeName();
+ ::std::for_each(m_aMyNamedExpressions.begin(), m_aMyNamedExpressions.end(),
+ RangeNameInserter(*pDoc, *pRangeNames, -1));
+}
+
+void ScXMLImport::SetSheetNamedRanges()
+{
+ if (!pDoc)
+ return;
+
+ for (auto const& itr : m_SheetNamedExpressions)
+ {
+ const SCTAB nTab = itr.first;
+ ScRangeName* pRangeNames = pDoc->GetRangeName(nTab);
+ if (!pRangeNames)
+ continue;
+
+ const ScMyNamedExpressions& rNames = itr.second;
+ ::std::for_each(rNames.begin(), rNames.end(), RangeNameInserter(*pDoc, *pRangeNames, nTab));
+ }
+}
+
+void ScXMLImport::SetStringRefSyntaxIfMissing()
+{
+ if (!pDoc)
+ return;
+
+ ScCalcConfig aCalcConfig = pDoc->GetCalcConfig();
+
+ // Has any string ref syntax been imported?
+ // If not, we need to take action
+ if ( !aCalcConfig.mbHasStringRefSyntax )
+ {
+ aCalcConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_A1_XL_A1;
+ pDoc->SetCalcConfig(aCalcConfig);
+ }
+}
+
+void SAL_CALL ScXMLImport::endDocument()
+{
+ ScXMLImport::MutexGuard aGuard(*this);
+ if (getImportFlags() & SvXMLImportFlags::CONTENT)
+ {
+ if (GetModel().is())
+ {
+ mpDocImport->finalize();
+
+ rtl::Reference<ScModelObj> xViewDataSupplier(GetScModel());
+ uno::Reference<container::XIndexAccess> xIndexAccess(xViewDataSupplier->getViewData());
+ if (xIndexAccess.is() && xIndexAccess->getCount() > 0)
+ {
+ uno::Sequence< beans::PropertyValue > aSeq;
+ if (xIndexAccess->getByIndex(0) >>= aSeq)
+ {
+ for (const auto& rProp : std::as_const(aSeq))
+ {
+ OUString sName(rProp.Name);
+ if (sName == SC_ACTIVETABLE)
+ {
+ OUString sTabName;
+ if(rProp.Value >>= sTabName)
+ {
+ SCTAB nTab(0);
+ if (pDoc->GetTable(sTabName, nTab))
+ {
+ pDoc->SetVisibleTab(nTab);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ SetLabelRanges();
+ SetNamedRanges();
+ SetSheetNamedRanges();
+ SetStringRefSyntaxIfMissing();
+ if (mpPivotSources)
+ // Process pivot table sources after the named ranges have been set.
+ mpPivotSources->process();
+ }
+ GetProgressBarHelper()->End(); // make room for subsequent SfxProgressBars
+ if (pDoc)
+ {
+ pDoc->CompileXML();
+
+ // After CompileXML, links must be completely changed to the new URLs.
+ // Otherwise, hasExternalFile for API wouldn't work (#i116940#),
+ // and typing a new formula would create a second link with the same "real" file name.
+ if (pDoc->HasExternalRefManager())
+ pDoc->GetExternalRefManager()->updateAbsAfterLoad();
+ }
+
+ // If the stream contains cells outside of the current limits, the styles can't be re-created,
+ // so stream copying is disabled then.
+ if (pDoc && GetModel().is() && !pDoc->HasRangeOverflow())
+ {
+ // set "valid stream" flags after loading (before UpdateRowHeights, so changed formula results
+ // in UpdateRowHeights can already clear the flags again)
+ ScSheetSaveData* pSheetData = GetScModel()->GetSheetSaveData();
+
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ {
+ pDoc->SetDrawPageSize(nTab);
+ if (!pSheetData->IsSheetBlocked( nTab ))
+ pDoc->SetStreamValid( nTab, true );
+ }
+ }
+
+ // There are rows with optimal height which need to be updated
+ if (pDoc && !maRecalcRowRanges.empty())
+ {
+ bool bLockHeight = pDoc->IsAdjustHeightLocked();
+ if (bLockHeight)
+ {
+ pDoc->UnlockAdjustHeight();
+ }
+
+ ScSizeDeviceProvider aProv(pDoc->GetDocumentShell());
+ ScDocRowHeightUpdater aUpdater(*pDoc, aProv.GetDevice(), aProv.GetPPTX(), aProv.GetPPTY(), &maRecalcRowRanges);
+ aUpdater.update();
+
+ if (bLockHeight)
+ {
+ pDoc->LockAdjustHeight();
+ }
+ }
+
+ // Initialize and set position and size of objects
+ if (pDoc && pDoc->GetDrawLayer())
+ {
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ const SdrPage* pPage = pDrawLayer->GetPage(nTab);
+ if (!pPage)
+ continue;
+ bool bNegativePage = pDoc->IsNegativePage(nTab);
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ {
+ ScDrawObjData* pData
+ = ScDrawLayer::GetObjDataTab(pObj.get(), nTab);
+ // Existence of pData means, that it is a cell anchored object
+ if (pData)
+ {
+ // Finish and correct import based on full size (no hidden row/col) and LTR
+ pDrawLayer->InitializeCellAnchoredObj(pObj.get(), *pData);
+ // Adapt object to hidden row/col and RTL
+ pDrawLayer->RecalcPos(pObj.get(), *pData, bNegativePage,
+ true /*bUpdateNoteCaptionPos*/);
+ }
+ }
+ }
+ }
+
+ aTables.FixupOLEs();
+ }
+ if (GetScModel())
+ {
+ GetScModel()->removeActionLock();
+ }
+ SvXMLImport::endDocument();
+
+ if (pDoc)
+ {
+ pDoc->BroadcastUno(SfxHint(SfxHintId::ScClearCache));
+ }
+
+ if(pDoc && bSelfImportingXMLSet)
+ GetScModel()->AfterXMLLoading();
+}
+
+// XEventListener
+void ScXMLImport::DisposingModel()
+{
+ SvXMLImport::DisposingModel();
+ pDoc = nullptr;
+}
+
+ScXMLImport::MutexGuard::MutexGuard(ScXMLImport& rImport) :
+ mrImport(rImport)
+{
+ mrImport.LockSolarMutex();
+}
+
+ScXMLImport::MutexGuard::~MutexGuard()
+{
+ mrImport.UnlockSolarMutex();
+}
+
+void ScXMLImport::LockSolarMutex()
+{
+ // #i62677# When called from DocShell/Wrapper, the SolarMutex is already locked,
+ // so there's no need to allocate (and later delete) the SolarMutexGuard.
+ if (!mbLockSolarMutex)
+ {
+ DBG_TESTSOLARMUTEX();
+ return;
+ }
+
+ if (nSolarMutexLocked == 0)
+ {
+ OSL_ENSURE(!moSolarMutexGuard, "Solar Mutex is locked");
+ moSolarMutexGuard.emplace();
+ }
+ ++nSolarMutexLocked;
+}
+
+void ScXMLImport::UnlockSolarMutex()
+{
+ if (nSolarMutexLocked > 0)
+ {
+ nSolarMutexLocked--;
+ if (nSolarMutexLocked == 0)
+ {
+ OSL_ENSURE(moSolarMutexGuard, "Solar Mutex is always unlocked");
+ moSolarMutexGuard.reset();
+ }
+ }
+}
+
+sal_Int64 ScXMLImport::GetByteOffset() const
+{
+ sal_Int64 nOffset = -1;
+ uno::Reference<xml::sax::XLocator> xLocator = GetLocator();
+ uno::Reference<io::XSeekable> xSeek( xLocator, uno::UNO_QUERY ); //! should use different interface
+ if ( xSeek.is() )
+ nOffset = xSeek->getPosition();
+ return nOffset;
+}
+
+void ScXMLImport::SetRangeOverflowType(ErrCode nType)
+{
+ // #i31130# Overflow is stored in the document, because the ScXMLImport object
+ // isn't available in ScXMLImportWrapper::ImportFromComponent when using the
+ // OOo->Oasis transformation.
+
+ if ( pDoc )
+ pDoc->SetRangeOverflowType( nType );
+}
+
+void ScXMLImport::ProgressBarIncrement()
+{
+ nProgressCount++;
+ if (nProgressCount > 100)
+ {
+ GetProgressBarHelper()->Increment(nProgressCount);
+ nProgressCount = 0;
+ }
+}
+
+void ScXMLImport::ExtractFormulaNamespaceGrammar(
+ OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
+ const OUString& rAttrValue, bool bRestrictToExternalNmsp ) const
+{
+ // parse the attribute value, extract namespace ID, literal namespace, and formula string
+ rFormulaNmsp.clear();
+ sal_uInt16 nNsId = GetNamespaceMap().GetKeyByQName(rAttrValue, nullptr, &rFormula, &rFormulaNmsp, SvXMLNamespaceMap::QNameMode::AttrValue);
+
+ // check if we have an ODF formula namespace
+ if( !bRestrictToExternalNmsp ) switch( nNsId )
+ {
+ case XML_NAMESPACE_OOOC:
+ rFormulaNmsp.clear(); // remove namespace string for built-in grammar
+ reGrammar = FormulaGrammar::GRAM_PODF;
+ return;
+ case XML_NAMESPACE_OF:
+ rFormulaNmsp.clear(); // remove namespace string for built-in grammar
+ reGrammar = FormulaGrammar::GRAM_ODFF;
+ return;
+ }
+
+ /* Find default grammar for formulas without namespace. There may be
+ documents in the wild that stored no namespace in ODF 1.0/1.1. Use
+ GRAM_PODF then (old style ODF 1.0/1.1 formulas). The default for ODF
+ 1.2 and later without namespace is GRAM_ODFF (OpenFormula). */
+ FormulaGrammar::Grammar eDefaultGrammar =
+ (GetDocument()->GetStorageGrammar() == FormulaGrammar::GRAM_PODF) ?
+ FormulaGrammar::GRAM_PODF : FormulaGrammar::GRAM_ODFF;
+
+ /* Check if we have no namespace at all. The value XML_NAMESPACE_NONE
+ indicates that there is no colon. If the first character of the
+ attribute value is the equality sign, the value XML_NAMESPACE_UNKNOWN
+ indicates that there is a colon somewhere in the formula string. */
+ if( (nNsId == XML_NAMESPACE_NONE) || ((nNsId == XML_NAMESPACE_UNKNOWN) && (rAttrValue.toChar() == '=')) )
+ {
+ rFormula = rAttrValue; // return entire string as formula
+ reGrammar = eDefaultGrammar;
+ return;
+ }
+
+ /* Check if a namespace URL could be resolved from the attribute value.
+ Use that namespace only, if the Calc document knows an associated
+ external formula parser. This prevents that the range operator in
+ conjunction with defined names is confused as namespaces prefix, e.g.
+ in the expression 'table:A1' where 'table' is a named reference. */
+ if( ((nNsId & XML_NAMESPACE_UNKNOWN_FLAG) != 0) && !rFormulaNmsp.isEmpty() &&
+ GetDocument()->GetFormulaParserPool().hasFormulaParser( rFormulaNmsp ) )
+ {
+ reGrammar = FormulaGrammar::GRAM_EXTERNAL;
+ return;
+ }
+
+ /* All attempts failed (e.g. no namespace and no leading equality sign, or
+ an invalid namespace prefix), continue with the entire attribute value. */
+ rFormula = rAttrValue;
+ rFormulaNmsp.clear(); // remove any namespace string
+ reGrammar = eDefaultGrammar;
+}
+
+FormulaError ScXMLImport::GetFormulaErrorConstant( const OUString& rStr ) const
+{
+ if (!mpComp)
+ return FormulaError::NONE;
+
+ return mpComp->GetErrorConstant(rStr);
+}
+
+ScEditEngineDefaulter* ScXMLImport::GetEditEngine()
+{
+ if (!mpEditEngine)
+ {
+ mpEditEngine.reset(new ScEditEngineDefaulter(pDoc->GetEnginePool()));
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mpEditEngine->SetEditTextObjectPool(pDoc->GetEditPool());
+ mpEditEngine->SetUpdateLayout(false);
+ mpEditEngine->EnableUndo(false);
+ mpEditEngine->SetControlWord(mpEditEngine->GetControlWord() & ~EEControlBits::ALLOWBIGOBJS);
+ }
+ return mpEditEngine.get();
+}
+
+const ScXMLEditAttributeMap& ScXMLImport::GetEditAttributeMap() const
+{
+ if (!mpEditAttrMap)
+ mpEditAttrMap.reset(new ScXMLEditAttributeMap);
+ return *mpEditAttrMap;
+}
+
+void ScXMLImport::NotifyContainsEmbeddedFont()
+{
+ if (pDoc)
+ pDoc->SetEmbedFonts(true);
+}
+
+ScMyImpDetectiveOpArray* ScXMLImport::GetDetectiveOpArray()
+{
+ if (!pDetectiveOpArray)
+ pDetectiveOpArray.reset(new ScMyImpDetectiveOpArray());
+ return pDetectiveOpArray.get();
+}
+
+ScModelObj* ScXMLImport::GetScModel() const
+{
+ return static_cast<ScModelObj*>(GetModel().get());
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODS(SvStream &rStream)
+{
+ ScDLL::Init();
+
+ SfxObjectShellLock xDocSh(new ScDocShell);
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new ::utl::OSeekableInputStreamWrapper(rStream));
+ uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW);
+
+ css::uno::Sequence<OUString> aUserData
+ {
+ "com.sun.star.comp.filter.OdfFlatXml",
+ "",
+ "com.sun.star.comp.Calc.XMLOasisImporter",
+ "com.sun.star.comp.Calc.XMLOasisExporter",
+ "",
+ "",
+ "true"
+ };
+ uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence(
+ {
+ { "UserData", uno::Any(aUserData) },
+ }));
+ css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) };
+
+ uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW);
+ xInit->initialize(aOuterArgs);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "URL", uno::Any(OUString("private:stream")) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+ bool ret = xFilter->filter(aArgs);
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return ret;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportXLSX(SvStream &rStream)
+{
+ ScDLL::Init();
+
+ SfxObjectShellLock xDocSh(new ScDocShell);
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+
+ uno::Reference<document::XFilter> xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.oox.xls.ExcelFilter"), uno::UNO_QUERY_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "InputMode", uno::Any(true) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+ bool ret = false;
+ try
+ {
+ ret = xFilter->filter(aArgs);
+ }
+ catch (const css::io::IOException&)
+ {
+ }
+ catch (const css::lang::WrappedTargetRuntimeException&)
+ {
+ }
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlimprt.hxx b/sc/source/filter/xml/xmlimprt.hxx
new file mode 100644
index 0000000000..843e865360
--- /dev/null
+++ b/sc/source/filter/xml/xmlimprt.hxx
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/xmlimp.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include "xmlsubti.hxx"
+#include <formula/grammar.hxx>
+#include <vcl/svapp.hxx>
+#include <dociter.hxx>
+
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+
+#include <memory>
+#include <map>
+#include <vector>
+#include <list>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::sheet { class XSheetCellRangeContainer; }
+namespace com::sun::star::table { struct CellRangeAddress; }
+namespace com::sun::star::util { class XNumberFormatTypes; }
+namespace com::sun::star::util { class XNumberFormats; }
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScCompiler;
+class ErrCode;
+class ScMyStyleNumberFormats;
+class XMLNumberFormatAttributesExportHelper;
+class ScEditEngineDefaulter;
+class ScDocumentImport;
+class ScMyImpDetectiveOpArray;
+class SdrPage;
+class ScModelObj;
+
+namespace sc {
+struct ImportPostProcessData;
+struct PivotTableSources;
+class ScDrawObjData;
+}
+
+
+class SvXMLTokenMap;
+class XMLShapeImportHelper;
+class ScXMLChangeTrackingImportHelper;
+
+struct ScMyNamedExpression
+{
+ OUString sName;
+ OUString sContent;
+ OUString sContentNmsp;
+ OUString sBaseCellAddress;
+ OUString sRangeType;
+ formula::FormulaGrammar::Grammar eGrammar;
+ bool bIsExpression;
+};
+
+typedef ::std::list<ScMyNamedExpression> ScMyNamedExpressions;
+
+struct ScMyLabelRange
+{
+ OUString sLabelRangeStr;
+ OUString sDataRangeStr;
+ bool bColumnOrientation;
+};
+
+typedef std::list<ScMyLabelRange> ScMyLabelRanges;
+
+struct ScMyImportValidation
+{
+ OUString sName;
+ OUString sInputTitle;
+ OUString sInputMessage;
+ OUString sErrorTitle;
+ OUString sErrorMessage;
+ OUString sFormula1;
+ OUString sFormula2;
+ OUString sFormulaNmsp1;
+ OUString sFormulaNmsp2;
+ OUString sBaseCellAddress; // string is used directly
+ css::sheet::ValidationAlertStyle aAlertStyle;
+ css::sheet::ValidationType aValidationType;
+ css::sheet::ConditionOperator aOperator;
+ formula::FormulaGrammar::Grammar eGrammar1;
+ formula::FormulaGrammar::Grammar eGrammar2;
+ sal_Int16 nShowList;
+ bool bShowErrorMessage;
+ bool bShowInputMessage;
+ bool bIgnoreBlanks;
+};
+
+typedef std::vector<ScMyImportValidation> ScMyImportValidations;
+class ScMyStylesImportHelper;
+class ScXMLEditAttributeMap;
+class ScCellRangesObj;
+
+class ScXMLImport: public SvXMLImport
+{
+ ScXMLImport(const ScXMLImport&) = delete;
+ const ScXMLImport& operator=(const ScXMLImport&) = delete;
+
+ typedef ::std::map<SCTAB, ScMyNamedExpressions> SheetNamedExpMap;
+
+ ScDocument* pDoc;
+ std::unique_ptr<ScDocumentImport> mpDocImport;
+ std::unique_ptr<ScCompiler> mpComp; // For error-checking of cached string cell values.
+ std::unique_ptr<ScEditEngineDefaulter> mpEditEngine;
+ std::unique_ptr<sc::PivotTableSources> mpPivotSources;
+
+ mutable std::unique_ptr<ScXMLEditAttributeMap> mpEditAttrMap;
+ std::unique_ptr<ScXMLChangeTrackingImportHelper> pChangeTrackingImportHelper;
+ std::unique_ptr<ScMyStylesImportHelper> pStylesImportHelper;
+
+ rtl::Reference < XMLPropertyHandlerFactory > xScPropHdlFactory;
+ rtl::Reference < XMLPropertySetMapper > xCellStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xColumnStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xRowStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xTableStylesPropertySetMapper;
+
+ sc::ImportPostProcessData* mpPostProcessData; /// Lift cycle managed elsewhere, no need to delete.
+
+ ScMyTables aTables;
+
+ std::vector<ScDocRowHeightUpdater::TabRanges> maRecalcRowRanges;
+
+ ScMyNamedExpressions m_aMyNamedExpressions;
+ SheetNamedExpMap m_SheetNamedExpressions;
+
+ ScMyLabelRanges maMyLabelRanges;
+ ScMyImportValidations maValidations;
+ std::unique_ptr<ScMyImpDetectiveOpArray> pDetectiveOpArray;
+ std::optional<SolarMutexGuard> moSolarMutexGuard;
+
+ std::unique_ptr<XMLNumberFormatAttributesExportHelper> pNumberFormatAttributesExportHelper;
+ std::unique_ptr<ScMyStyleNumberFormats> pStyleNumberFormats;
+ css::uno::Reference <css::util::XNumberFormats> xNumberFormats;
+ css::uno::Reference <css::util::XNumberFormatTypes> xNumberFormatTypes;
+
+ rtl::Reference<ScCellRangesObj> mxSheetCellRanges; // css::sheet::XSheetCellRangeContainer
+
+ OUString sPrevStyleName;
+ OUString sPrevCurrency;
+ sal_uInt32 nSolarMutexLocked;
+ sal_Int32 nProgressCount;
+ sal_Int16 nPrevCellType;
+ bool bLoadDoc; // Load doc or styles only
+ bool bNullDateSetted;
+ bool bSelfImportingXMLSet;
+ bool mbLockSolarMutex;
+ bool mbImportStyles;
+ bool mbHasNewCondFormatData;
+
+protected:
+
+ // This method is called after the namespace map has been updated, but
+ // before a context for the current element has been pushed.
+ virtual SvXMLImportContext *CreateFastContext( sal_Int32 nElement,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual XMLShapeImportHelper* CreateShapeImport() override;
+
+public:
+ ScXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlag,
+ const css::uno::Sequence< OUString > & sSupportedServiceNames = {});
+
+ virtual ~ScXMLImport() noexcept override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence<css::uno::Any>& aArguments ) override;
+
+ // namespace office
+ // NB: in contrast to other CreateFooContexts, this particular one handles
+ // the root element (i.e. office:document-meta)
+ SvXMLImportContext *CreateMetaContext( sal_Int32 nElement );
+ SvXMLImportContext *CreateFontDeclsContext();
+ SvXMLImportContext *CreateScriptContext();
+ SvXMLImportContext *CreateStylesContext( bool bAutoStyles );
+
+ SvXMLImportContext *CreateBodyContext(
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual void SetStatistics( const css::uno::Sequence< css::beans::NamedValue> & i_rStats) override;
+
+ ScDocumentImport& GetDoc();
+
+ ScDocument* GetDocument() { return pDoc; }
+ const ScDocument* GetDocument() const { return pDoc; }
+
+ ScModelObj* GetScModel() const;
+
+ ScMyTables& GetTables() { return aTables; }
+
+ std::vector<ScDocRowHeightUpdater::TabRanges>& GetRecalcRowRanges() { return maRecalcRowRanges; }
+
+ bool IsStylesOnlyMode() const { return !bLoadDoc; }
+
+ static sal_Int16 GetCellType(const char* rStrValue, const sal_Int32 nStrLength);
+
+ const rtl::Reference < XMLPropertySetMapper >& GetCellStylesPropertySetMapper() const { return xCellStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetColumnStylesPropertySetMapper() const { return xColumnStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetRowStylesPropertySetMapper() const { return xRowStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetTableStylesPropertySetMapper() const { return xTableStylesPropertySetMapper; }
+
+ void SetPostProcessData( sc::ImportPostProcessData* p );
+ sc::ImportPostProcessData* GetPostProcessData() { return mpPostProcessData;}
+
+ sc::PivotTableSources& GetPivotTableSources();
+
+ void AddNamedExpression(ScMyNamedExpression aMyNamedExpression)
+ {
+ m_aMyNamedExpressions.push_back(std::move(aMyNamedExpression));
+ }
+
+ void AddNamedExpression(SCTAB nTab, ScMyNamedExpression aNamedExp);
+
+ void AddLabelRange(ScMyLabelRange aMyLabelRange)
+ {
+ maMyLabelRanges.push_back(std::move(aMyLabelRange));
+ }
+
+ void AddValidation(const ScMyImportValidation& rValidation) { maValidations.push_back(rValidation); }
+ bool GetValidation(const OUString& sName, ScMyImportValidation& aValidation);
+
+ ScMyImpDetectiveOpArray* GetDetectiveOpArray();
+
+ ScXMLChangeTrackingImportHelper* GetChangeTrackingImportHelper();
+ void InsertStyles();
+
+ void SetChangeTrackingViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& rChangeProps);
+ virtual void SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override;
+ virtual void SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>& aConfigProps) override;
+
+ ScMyStylesImportHelper* GetStylesImportHelper() { return pStylesImportHelper.get(); }
+ sal_Int32 SetCurrencySymbol(const sal_Int32 nKey, std::u16string_view rCurrency);
+ bool IsCurrencySymbol(const sal_Int32 nNumberFormat, std::u16string_view sCurrencySymbol, std::u16string_view sBankSymbol);
+ void SetType(const css::uno::Reference <css::beans::XPropertySet>& rProperties,
+ sal_Int32& rNumberFormat,
+ const sal_Int16 nCellType,
+ std::u16string_view rCurrency);
+
+ void ProgressBarIncrement();
+
+ void SetNewCondFormatData() { mbHasNewCondFormatData = true; }
+ bool HasNewCondFormatData() const { return mbHasNewCondFormatData; }
+
+private:
+ void SetStyleToRanges();
+
+ void ExamineDefaultStyle();
+public:
+ void SetStyleToRanges(const ScRangeList& rRanges, const OUString* pStyleName,
+ const sal_Int16 nCellType, const OUString* pCurrency);
+ bool SetNullDateOnUnitConverter();
+ XMLNumberFormatAttributesExportHelper* GetNumberFormatAttributesExportHelper();
+ ScMyStyleNumberFormats* GetStyleNumberFormats();
+
+ void SetStylesToRangesFinished();
+
+ // XImporter
+ virtual void SAL_CALL setTargetDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override;
+
+ virtual void SAL_CALL startDocument() override;
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void DisposingModel() override;
+
+ /**
+ * Use this class to manage solar mutex locking instead of calling
+ * LockSolarMutex() and UnlockSolarMutex() directly.
+ */
+ class MutexGuard
+ {
+ public:
+ explicit MutexGuard(ScXMLImport& rImport);
+ ~MutexGuard();
+ private:
+ ScXMLImport& mrImport;
+ };
+ void LockSolarMutex();
+ void UnlockSolarMutex();
+
+ sal_Int64 GetByteOffset() const;
+
+ void SetRangeOverflowType(ErrCode nType);
+
+ static sal_Int32 GetRangeType(std::u16string_view sRangeType);
+ void SetNamedRanges();
+ void SetSheetNamedRanges();
+ void SetLabelRanges();
+ void SetStringRefSyntaxIfMissing();
+
+ /** Extracts the formula string, the formula grammar namespace URL, and a
+ grammar enum value from the passed formula attribute value.
+
+ @param rFormula
+ (out-parameter) Returns the plain formula string with the leading
+ equality sign if existing.
+
+ @param rFormulaNmsp
+ (out-parameter) Returns the URL of the formula grammar namespace if
+ the attribute value contains the prefix of an unknown namespace.
+
+ @param reGrammar
+ (out-parameter) Returns the exact formula grammar if the formula
+ is in a supported ODF format (e.g. FormulaGrammar::GRAM_PODF for
+ ODF 1.0/1.1 formulas, or FormulaGrammar::GRAM_ODFF for ODF 1.2
+ formulas a.k.a. OpenFormula). Returns the default storage grammar,
+ if the attribute value does not contain a namespace prefix. Returns
+ the special value FormulaGrammar::GRAM_EXTERNAL, if an unknown
+ namespace could be extracted from the formula which will be
+ contained in the parameter rFormulaNmsp then.
+
+ @param rAttrValue
+ The value of the processed formula attribute.
+
+ @param bRestrictToExternalNmsp
+ If set to true, only namespaces of external formula grammars will
+ be recognized. Internal namespace prefixes (e.g. 'oooc:' or 'of:'
+ will be considered to be part of the formula, e.g. an expression
+ with range operator.
+ */
+ void ExtractFormulaNamespaceGrammar(
+ OUString& rFormula,
+ OUString& rFormulaNmsp,
+ ::formula::FormulaGrammar::Grammar& reGrammar,
+ const OUString& rAttrValue,
+ bool bRestrictToExternalNmsp = false ) const;
+
+ FormulaError GetFormulaErrorConstant( const OUString& rStr ) const;
+
+ ScEditEngineDefaulter* GetEditEngine();
+ const ScXMLEditAttributeMap& GetEditAttributeMap() const;
+ virtual void NotifyContainsEmbeddedFont() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmllabri.cxx b/sc/source/filter/xml/xmllabri.cxx
new file mode 100644
index 0000000000..e1289e4c1a
--- /dev/null
+++ b/sc/source/filter/xml/xmllabri.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 "xmllabri.hxx"
+#include <xmloff/xmltoken.hxx>
+#include "xmlimprt.hxx"
+#include <xmloff/xmlnamespace.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScXMLLabelRangesContext::ScXMLLabelRangesContext(
+ ScXMLImport& rImport ):
+ ScXMLImportContext( rImport )
+{
+ rImport.LockSolarMutex();
+}
+
+ScXMLLabelRangesContext::~ScXMLLabelRangesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLLabelRangesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_LABEL_RANGE ):
+ pContext = new ScXMLLabelRangeContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLLabelRangeContext::ScXMLLabelRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ bColumnOrientation( false )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_LABEL_CELL_RANGE_ADDRESS ):
+ sLabelRangeStr = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_CELL_RANGE_ADDRESS ):
+ sDataRangeStr = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ bColumnOrientation = IsXMLToken(aIter, XML_COLUMN );
+ break;
+ }
+ }
+}
+
+ScXMLLabelRangeContext::~ScXMLLabelRangeContext()
+{
+}
+
+void SAL_CALL ScXMLLabelRangeContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ // Label ranges must be stored as strings until all sheets are loaded
+ // (like named expressions).
+
+ ScMyLabelRange aLabelRange(
+ ScMyLabelRange{sLabelRangeStr, sDataRangeStr, bColumnOrientation});
+
+ GetScImport().AddLabelRange(std::move(aLabelRange));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmllabri.hxx b/sc/source/filter/xml/xmllabri.hxx
new file mode 100644
index 0000000000..c61897654f
--- /dev/null
+++ b/sc/source/filter/xml/xmllabri.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 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLLabelRangesContext : public ScXMLImportContext
+{
+public:
+ ScXMLLabelRangesContext(
+ ScXMLImport& rImport
+ );
+ virtual ~ScXMLLabelRangesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList
+ ) override;
+};
+
+class ScXMLLabelRangeContext : public ScXMLImportContext
+{
+private:
+ OUString sLabelRangeStr;
+ OUString sDataRangeStr;
+ bool bColumnOrientation;
+
+public:
+ ScXMLLabelRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList
+ );
+ virtual ~ScXMLLabelRangeContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlmappingi.cxx b/sc/source/filter/xml/xmlmappingi.cxx
new file mode 100644
index 0000000000..9e9b23e36a
--- /dev/null
+++ b/sc/source/filter/xml/xmlmappingi.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/.
+ */
+
+#include "xmlimprt.hxx"
+#include "xmlmappingi.hxx"
+#include "xmltransformationi.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <datamapper.hxx>
+#include <document.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLMappingsContext::ScXMLMappingsContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // has no attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLMappingsContext::~ScXMLMappingsContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLMappingsContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( CALC_EXT, XML_DATA_MAPPING ):
+ {
+ pContext = new ScXMLMappingContext( GetScImport(), pAttribList );
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_TRANSFORMATIONS):
+ {
+ pContext = new ScXMLTransformationsContext( GetScImport() );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLMappingContext::ScXMLMappingContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport )
+{
+ OUString aProvider;
+ OUString aID;
+ OUString aURL;
+ // OUString aFrequency;
+ OUString aDBName;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ {
+ aURL = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_PROVIDER ):
+ {
+ aProvider = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_ID ):
+ {
+ aID = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATABASE_NAME ):
+ {
+ aDBName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_FREQUENCY ):
+ {
+ }
+ break;
+ }
+ }
+ }
+
+ if (!aProvider.isEmpty())
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataMapper = pDoc->GetExternalDataMapper();
+ sc::ExternalDataSource aSource(aURL, aProvider, pDoc);
+ aSource.setID(aID);
+ aSource.setDBData(aDBName);
+ rDataMapper.insertDataSource(aSource);
+ }
+}
+
+ScXMLMappingContext::~ScXMLMappingContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataMapper = pDoc->GetExternalDataMapper();
+ auto& rDataSources = rDataMapper.getDataSources();
+ if(!rDataSources.empty())
+ rDataSources[0].refresh(pDoc, true);
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLMappingContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( CALC_EXT, XML_DATA_TRANSFORMATIONS):
+ {
+ pContext = new ScXMLTransformationsContext( GetScImport() );
+ }
+ break;
+ }
+
+ return pContext;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlmappingi.hxx b/sc/source/filter/xml/xmlmappingi.hxx
new file mode 100644
index 0000000000..cc38d40dbe
--- /dev/null
+++ b/sc/source/filter/xml/xmlmappingi.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/.
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLMappingsContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLMappingsContext( ScXMLImport& rImport );
+
+ virtual ~ScXMLMappingsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLMappingContext : public ScXMLImportContext
+{
+
+public:
+
+ ScXMLMappingContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLMappingContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlnexpi.cxx b/sc/source/filter/xml/xmlnexpi.cxx
new file mode 100644
index 0000000000..cf2878032b
--- /dev/null
+++ b/sc/source/filter/xml/xmlnexpi.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 "xmlnexpi.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLNamedExpressionsContext::GlobalInserter::GlobalInserter(ScXMLImport& rImport) : mrImport(rImport) {}
+
+void ScXMLNamedExpressionsContext::GlobalInserter::insert(ScMyNamedExpression aExp)
+{
+ mrImport.AddNamedExpression(std::move(aExp));
+}
+
+ScXMLNamedExpressionsContext::SheetLocalInserter::SheetLocalInserter(ScXMLImport& rImport, SCTAB nTab) :
+ mrImport(rImport), mnTab(nTab) {}
+
+void ScXMLNamedExpressionsContext::SheetLocalInserter::insert(ScMyNamedExpression aExp)
+{
+ mrImport.AddNamedExpression(mnTab, std::move(aExp));
+}
+
+ScXMLNamedExpressionsContext::ScXMLNamedExpressionsContext(
+ ScXMLImport& rImport,
+ std::shared_ptr<Inserter> pInserter ) :
+ ScXMLImportContext( rImport ),
+ mpInserter(std::move(pInserter))
+{
+ rImport.LockSolarMutex();
+}
+
+ScXMLNamedExpressionsContext::~ScXMLNamedExpressionsContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLNamedExpressionsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_NAMED_RANGE ):
+ pContext = new ScXMLNamedRangeContext(
+ GetScImport(), pAttribList, mpInserter.get() );
+ break;
+ case XML_ELEMENT( TABLE, XML_NAMED_EXPRESSION ):
+ pContext = new ScXMLNamedExpressionContext(
+ GetScImport(), pAttribList, mpInserter.get() );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLNamedRangeContext::ScXMLNamedRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter ) :
+ ScXMLImportContext( rImport )
+{
+ if (!pInserter)
+ return;
+
+ ScMyNamedExpression aNamedExpression;
+ // A simple table:cell-range-address is not a formula expression, stored
+ // without [] brackets but with dot, .A1
+ aNamedExpression.eGrammar = formula::FormulaGrammar::mergeToGrammar(
+ GetScImport().GetDocument()->GetStorageGrammar(),
+ formula::FormulaGrammar::CONV_OOO);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ aNamedExpression.sName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_ADDRESS ):
+ aNamedExpression.sContent = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
+ aNamedExpression.sBaseCellAddress = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_RANGE_USABLE_AS ):
+ aNamedExpression.sRangeType = aIter.toString();
+ break;
+ }
+ }
+ }
+ aNamedExpression.bIsExpression = false;
+ pInserter->insert(std::move(aNamedExpression));
+}
+
+ScXMLNamedRangeContext::~ScXMLNamedRangeContext()
+{
+}
+
+ScXMLNamedExpressionContext::ScXMLNamedExpressionContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter ) :
+ ScXMLImportContext( rImport )
+{
+ if (!pInserter)
+ return;
+
+ ScMyNamedExpression aNamedExpression;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ aNamedExpression.sName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_EXPRESSION ):
+ GetScImport().ExtractFormulaNamespaceGrammar(
+ aNamedExpression.sContent, aNamedExpression.sContentNmsp,
+ aNamedExpression.eGrammar, aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
+ aNamedExpression.sBaseCellAddress = aIter.toString();
+ break;
+ }
+ }
+ }
+ aNamedExpression.bIsExpression = true;
+ pInserter->insert(std::move(aNamedExpression));
+}
+
+ScXMLNamedExpressionContext::~ScXMLNamedExpressionContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlnexpi.hxx b/sc/source/filter/xml/xmlnexpi.hxx
new file mode 100644
index 0000000000..1d53f174c3
--- /dev/null
+++ b/sc/source/filter/xml/xmlnexpi.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <types.hxx>
+#include "importcontext.hxx"
+
+#include <memory>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+struct ScMyNamedExpression;
+
+class ScXMLNamedExpressionsContext : public ScXMLImportContext
+{
+public:
+
+ class Inserter
+ {
+ public:
+ virtual ~Inserter() {}
+ virtual void insert(ScMyNamedExpression aExp) = 0;
+ };
+
+ /**
+ * Global named expressions are inserted into ScXMLImport, which does the
+ * bulk insertion at the end of the import.
+ */
+ class GlobalInserter : public Inserter
+ {
+ public:
+ explicit GlobalInserter(ScXMLImport& rImport);
+ virtual void insert(ScMyNamedExpression aExp) override;
+ private:
+ ScXMLImport& mrImport;
+ };
+
+ /**
+ * Sheet local named expressions are inserted directly into ScRangeName
+ * instance of that sheet. TODO: the global ones should be inserted the
+ * same way for efficiency.
+ */
+ class SheetLocalInserter : public Inserter
+ {
+ public:
+ SheetLocalInserter(ScXMLImport& rImport, SCTAB nTab);
+ virtual void insert(ScMyNamedExpression aExp) override;
+ private:
+ ScXMLImport& mrImport;
+ SCTAB mnTab;
+ };
+
+ ScXMLNamedExpressionsContext(
+ ScXMLImport& rImport,
+ std::shared_ptr<Inserter> pInserter );
+
+ virtual ~ScXMLNamedExpressionsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ std::shared_ptr<Inserter> mpInserter;
+};
+
+class ScXMLNamedRangeContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLNamedRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter );
+
+ virtual ~ScXMLNamedRangeContext() override;
+};
+
+class ScXMLNamedExpressionContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLNamedExpressionContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter );
+
+ virtual ~ScXMLNamedExpressionContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlrowi.cxx b/sc/source/filter/xml/xmlrowi.cxx
new file mode 100644
index 0000000000..82f58d1f3c
--- /dev/null
+++ b/sc/source/filter/xml/xmlrowi.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 "xmlrowi.hxx"
+#include "xmlimprt.hxx"
+#include "xmlcelli.hxx"
+#include "xmlstyli.hxx"
+#include "xmlstyle.hxx"
+#include <document.hxx>
+#include <docuno.hxx>
+#include <olinetab.hxx>
+#include <sheetdata.hxx>
+#include <documentimport.hxx>
+#include <unonames.hxx>
+
+#include <comphelper/extract.hxx>
+#include <unotools/configmgr.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <sax/fastattribs.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <osl/diagnose.h>
+
+constexpr OUStringLiteral SC_ISFILTERED = u"IsFiltered";
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableRowContext::ScXMLTableRowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ sVisibility(GetXMLToken(XML_VISIBLE)),
+ nRepeatedRows(1),
+ bHasCell(false)
+{
+ OUString sCellStyleName;
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch (it.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ {
+ sStyleName = it.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VISIBILITY ):
+ {
+ sVisibility = it.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_ROWS_REPEATED ):
+ {
+ nRepeatedRows = std::max( it.toInt32(), sal_Int32(1) );
+ nRepeatedRows = std::min( nRepeatedRows, rImport.GetDocument()->GetSheetLimits().GetMaxRowCount() );
+ if (utl::ConfigManager::IsFuzzing())
+ nRepeatedRows = std::min(nRepeatedRows, sal_Int32(1024));
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DEFAULT_CELL_STYLE_NAME ):
+ {
+ sCellStyleName = it.toString();
+ }
+ break;
+ /*case XML_ELEMENT( TABLE, XML_USE_OPTIMAL_HEIGHT ):
+ {
+ sOptimalHeight = it.toString();
+ }
+ break;*/
+ }
+ }
+ }
+
+ GetScImport().GetTables().AddRow();
+ GetScImport().GetTables().SetRowStyle(sCellStyleName);
+}
+
+ScXMLTableRowContext::~ScXMLTableRowContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLTableRowContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_CELL ):
+// if( IsInsertCellPossible() )
+ {
+ bHasCell = true;
+ pContext = new ScXMLTableRowCellContext( GetScImport(),
+ pAttribList, false, static_cast<SCROW>(nRepeatedRows)
+ //this
+ );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_COVERED_TABLE_CELL ):
+// if( IsInsertCellPossible() )
+ {
+ bHasCell = true;
+ pContext = new ScXMLTableRowCellContext( GetScImport(),
+ pAttribList, true, static_cast<SCROW>(nRepeatedRows)
+ //this
+ );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableRowContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ ScXMLImport& rXMLImport(GetScImport());
+ ScDocument* pDoc(rXMLImport.GetDocument());
+ if (!bHasCell && nRepeatedRows > 1)
+ {
+ for (sal_Int32 i = 0; i < nRepeatedRows - 1; ++i) //one row is always added
+ GetScImport().GetTables().AddRow();
+ OSL_FAIL("it seems here is a nonvalid file; possible missing of table:table-cell element");
+ }
+ SCTAB nSheet = rXMLImport.GetTables().GetCurrentSheet();
+ sal_Int32 nCurrentRow(rXMLImport.GetTables().GetCurrentRow());
+ uno::Reference<sheet::XSpreadsheet> xSheet(rXMLImport.GetTables().GetCurrentXSheet());
+ if(!xSheet.is())
+ return;
+
+ sal_Int32 nFirstRow(nCurrentRow - nRepeatedRows + 1);
+ if (nFirstRow > pDoc->MaxRow())
+ nFirstRow = pDoc->MaxRow();
+ if (nCurrentRow > pDoc->MaxRow())
+ nCurrentRow = pDoc->MaxRow();
+ uno::Reference <table::XCellRange> xCellRange(xSheet->getCellRangeByPosition(0, nFirstRow, 0, nCurrentRow));
+ if (!xCellRange.is())
+ return;
+
+ uno::Reference<table::XColumnRowRange> xColumnRowRange (xCellRange, uno::UNO_QUERY);
+ if (!xColumnRowRange.is())
+ return;
+
+ uno::Reference <beans::XPropertySet> xRowProperties(xColumnRowRange->getRows(), uno::UNO_QUERY);
+ if (!xRowProperties.is())
+ return;
+
+ XMLTableStyleContext* ptmpStyle = nullptr;
+
+ if (!sStyleName.isEmpty())
+ {
+ XMLTableStylesContext *pStyles(static_cast<XMLTableStylesContext *>(rXMLImport.GetAutoStyles()));
+ if ( pStyles )
+ {
+ XMLTableStyleContext* pStyle(const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_ROW, sStyleName, true))));
+ if (pStyle)
+ {
+ pStyle->FillPropertySet(xRowProperties);
+
+ if ( nSheet != pStyle->GetLastSheet() )
+ {
+ ScSheetSaveData* pSheetData = rXMLImport.GetScModel()->GetSheetSaveData();
+ pSheetData->AddRowStyle( sStyleName, ScAddress( 0, static_cast<SCROW>(nFirstRow), nSheet ) );
+ pStyle->SetLastSheet(nSheet);
+ }
+
+ // for later checking of optimal row height
+ ptmpStyle = pStyle;
+ }
+ }
+ }
+ bool bVisible (true);
+ bool bFiltered (false);
+ if (IsXMLToken(sVisibility, XML_COLLAPSE))
+ {
+ bVisible = false;
+ }
+ else if (IsXMLToken(sVisibility, XML_FILTER))
+ {
+ bVisible = false;
+ bFiltered = true;
+ }
+ if (!bVisible)
+ {
+ rXMLImport.GetDoc().setRowsVisible(nSheet, nFirstRow, nCurrentRow, false);
+ }
+ if (bFiltered)
+ xRowProperties->setPropertyValue(SC_ISFILTERED, uno::Any(bFiltered));
+
+ uno::Any any = xRowProperties->getPropertyValue(SC_UNONAME_OHEIGHT);
+ bool bOptionalHeight = false;
+ any >>= bOptionalHeight;
+ if (bOptionalHeight)
+ {
+ // Save this row for later height update, only if we have no already optimal row heights
+ // If we have already optimal row heights, recalc only the first 200 row in case of optimal document loading
+ std::vector<ScDocRowHeightUpdater::TabRanges>& rRecalcRanges = rXMLImport.GetRecalcRowRanges();
+ while (static_cast<SCTAB>(rRecalcRanges.size()) <= nSheet)
+ {
+ rRecalcRanges.emplace_back(0, pDoc->MaxRow());
+ }
+ rRecalcRanges.at(nSheet).mnTab = nSheet;
+
+ // check that, we already have valid optimal row heights
+ if (nCurrentRow > 200 && ptmpStyle && !ptmpStyle->FindProperty(CTF_SC_ROWHEIGHT))
+ {
+ XMLPropertyState* pOptimalHeight = ptmpStyle->FindProperty(CTF_SC_ROWOPTIMALHEIGHT);
+ if (pOptimalHeight && ::cppu::any2bool(pOptimalHeight->maValue))
+ {
+ rRecalcRanges.at(nSheet).maRanges.setFalse(nFirstRow, nCurrentRow);
+ }
+ else
+ {
+ rRecalcRanges.at(nSheet).maRanges.setTrue(nFirstRow, nCurrentRow);
+ }
+ }
+ else
+ {
+ rRecalcRanges.at(nSheet).maRanges.setTrue(nFirstRow, nCurrentRow);
+ }
+ }
+}
+
+ScXMLTableRowsContext::ScXMLTableRowsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempHeader,
+ const bool bTempGroup ) :
+ ScXMLImportContext( rImport ),
+ nHeaderStartRow(0),
+ nGroupStartRow(0),
+ bHeader(bTempHeader),
+ bGroup(bTempGroup),
+ bGroupDisplay(true)
+{
+ // don't have any attributes
+ if (bHeader)
+ {
+ ScAddress aAddr = rImport.GetTables().GetCurrentCellPos();
+ nHeaderStartRow = aAddr.Row();
+ ++nHeaderStartRow;
+ }
+ else if (bGroup)
+ {
+ nGroupStartRow = rImport.GetTables().GetCurrentRow();
+ ++nGroupStartRow;
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_DISPLAY ) ) );
+ if (aIter != rAttrList->end())
+ bGroupDisplay = IsXMLToken( aIter, XML_TRUE );
+ }
+ }
+}
+
+ScXMLTableRowsContext::~ScXMLTableRowsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLTableRowsContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW_GROUP ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ pContext = new ScXMLTableRowContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableRowsContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ ScXMLImport& rXMLImport(GetScImport());
+ if (bHeader)
+ {
+ SCROW nHeaderEndRow = rXMLImport.GetTables().GetCurrentRow();
+ if (nHeaderStartRow <= nHeaderEndRow)
+ {
+ uno::Reference <sheet::XPrintAreas> xPrintAreas (rXMLImport.GetTables().GetCurrentXSheet(), uno::UNO_QUERY);
+ if (xPrintAreas.is())
+ {
+ if (!xPrintAreas->getPrintTitleRows())
+ {
+ xPrintAreas->setPrintTitleRows(true);
+ table::CellRangeAddress aRowHeaderRange;
+ aRowHeaderRange.StartRow = nHeaderStartRow;
+ aRowHeaderRange.EndRow = nHeaderEndRow;
+ xPrintAreas->setTitleRows(aRowHeaderRange);
+ }
+ else
+ {
+ table::CellRangeAddress aRowHeaderRange(xPrintAreas->getTitleRows());
+ aRowHeaderRange.EndRow = nHeaderEndRow;
+ xPrintAreas->setTitleRows(aRowHeaderRange);
+ }
+ }
+ }
+ }
+ else if (bGroup)
+ {
+ SCROW nGroupEndRow = rXMLImport.GetTables().GetCurrentRow();
+ SCTAB nSheet(rXMLImport.GetTables().GetCurrentSheet());
+ if (nGroupStartRow <= nGroupEndRow)
+ {
+ ScDocument* pDoc(GetScImport().GetDocument());
+ if (pDoc)
+ {
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScOutlineTable* pOutlineTable(pDoc->GetOutlineTable(nSheet, true));
+ ScOutlineArray& rRowArray(pOutlineTable->GetRowArray());
+ bool bResized;
+ rRowArray.Insert(nGroupStartRow, nGroupEndRow, bResized, !bGroupDisplay);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlrowi.hxx b/sc/source/filter/xml/xmlrowi.hxx
new file mode 100644
index 0000000000..981a74a597
--- /dev/null
+++ b/sc/source/filter/xml/xmlrowi.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <types.hxx>
+#include "importcontext.hxx"
+
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLTableRowContext : public ScXMLImportContext
+{
+ OUString sStyleName;
+ OUString sVisibility;
+ sal_Int32 nRepeatedRows;
+ bool bHasCell;
+
+public:
+
+ ScXMLTableRowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableRowContext() override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+class ScXMLTableRowsContext : public ScXMLImportContext
+{
+ SCROW nHeaderStartRow;
+ SCROW nGroupStartRow;
+ bool bHeader;
+ bool bGroup;
+ bool bGroupDisplay;
+
+public:
+
+ ScXMLTableRowsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bHeader, const bool bGroup);
+
+ virtual ~ScXMLTableRowsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsceni.cxx b/sc/source/filter/xml/xmlsceni.cxx
new file mode 100644
index 0000000000..7a1c1848ba
--- /dev/null
+++ b/sc/source/filter/xml/xmlsceni.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 <document.hxx>
+#include "xmlimprt.hxx"
+#include "xmlsceni.hxx"
+#include <attrib.hxx>
+#include <rangeutl.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <sax/tools/converter.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableScenarioContext::ScXMLTableScenarioContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ):
+ ScXMLImportContext( rImport ),
+ aBorderColor( COL_BLACK ),
+ bDisplayBorder( true ),
+ bCopyBack( true ),
+ bCopyStyles( true ),
+ bCopyFormulas( true ),
+ bIsActive( false ),
+ bProtected( false )
+{
+ rImport.LockSolarMutex();
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DISPLAY_BORDER ):
+ bDisplayBorder = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_BORDER_COLOR ):
+ ::sax::Converter::convertColor(aBorderColor, aIter.toView());
+ break;
+ case XML_ELEMENT( TABLE, XML_COPY_BACK ):
+ bCopyBack = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_COPY_STYLES ):
+ bCopyStyles = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_COPY_FORMULAS ):
+ bCopyFormulas = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_IS_ACTIVE ):
+ bIsActive = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_SCENARIO_RANGES ):
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ ScRangeStringConverter::GetRangeListFromString(
+ aScenarioRanges, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ break;
+ }
+ case XML_ELEMENT( TABLE, XML_COMMENT ):
+ sComment = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTED ):
+ bProtected = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+ScXMLTableScenarioContext::~ScXMLTableScenarioContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+void SAL_CALL ScXMLTableScenarioContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ SCTAB nCurrTable( GetScImport().GetTables().GetCurrentSheet() );
+ ScDocument* pDoc(GetScImport().GetDocument());
+ if (!pDoc)
+ return;
+
+ pDoc->SetScenario( nCurrTable, true );
+ ScScenarioFlags nFlags( ScScenarioFlags::NONE );
+ if( bDisplayBorder )
+ nFlags |= ScScenarioFlags::ShowFrame;
+ if( bCopyBack )
+ nFlags |= ScScenarioFlags::TwoWay;
+ if( bCopyStyles )
+ nFlags |= ScScenarioFlags::Attrib;
+ if( !bCopyFormulas )
+ nFlags |= ScScenarioFlags::Value;
+ if( bProtected )
+ nFlags |= ScScenarioFlags::Protected;
+ pDoc->SetScenarioData( nCurrTable, sComment, aBorderColor, nFlags );
+ for( size_t i = 0; i < aScenarioRanges.size(); ++i )
+ {
+ ScRange const & rRange = aScenarioRanges[ i ];
+ pDoc->ApplyFlagsTab( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), nCurrTable, ScMF::Scenario );
+ }
+ pDoc->SetActiveScenario( nCurrTable, bIsActive );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsceni.hxx b/sc/source/filter/xml/xmlsceni.hxx
new file mode 100644
index 0000000000..95f4b71809
--- /dev/null
+++ b/sc/source/filter/xml/xmlsceni.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <tools/color.hxx>
+#include <rangelst.hxx>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLImport;
+
+class ScXMLTableScenarioContext : public ScXMLImportContext
+{
+private:
+ OUString sComment;
+ Color aBorderColor;
+ ScRangeList aScenarioRanges;
+ bool bDisplayBorder;
+ bool bCopyBack;
+ bool bCopyStyles;
+ bool bCopyFormulas;
+ bool bIsActive;
+ bool bProtected;
+
+public:
+
+ ScXMLTableScenarioContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableScenarioContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsorti.cxx b/sc/source/filter/xml/xmlsorti.cxx
new file mode 100644
index 0000000000..a405d4a2c8
--- /dev/null
+++ b/sc/source/filter/xml/xmlsorti.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 "xmldrani.hxx"
+#include "xmlsorti.hxx"
+#include "xmlimprt.hxx"
+#include <convuno.hxx>
+#include <unonames.hxx>
+#include <rangeutl.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/util/SortField.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLSortContext::ScXMLSortContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext),
+ nUserListIndex(0),
+ bCopyOutputData(false),
+ bBindFormatsToContent(true),
+ bIsCaseSensitive(false),
+ bEnabledUserList(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_BIND_STYLES_TO_CONTENT ):
+ {
+ bBindFormatsToContent = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScRange aScRange;
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ if (ScRangeStringConverter::GetRangeFromString( aScRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ))
+ {
+ ScUnoConversion::FillApiAddress( aOutputPosition, aScRange.aStart );
+ bCopyOutputData = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ {
+ bIsCaseSensitive = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_RFC_LANGUAGE_TAG ):
+ {
+ maLanguageTagODF.maRfcLanguageTag = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_LANGUAGE ):
+ {
+ maLanguageTagODF.maLanguage = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SCRIPT ):
+ {
+ maLanguageTagODF.maScript = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_COUNTRY ):
+ {
+ maLanguageTagODF.maCountry = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ALGORITHM ):
+ {
+ sAlgorithm = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSortContext::~ScXMLSortContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSortContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_SORT_BY ):
+ {
+ pContext = new ScXMLSortByContext( GetScImport(), nElement, pAttribList, this );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSortContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ sal_Int32 nAlgoLength(sAlgorithm.getLength());
+ sal_uInt8 i (0);
+ if (!maLanguageTagODF.isEmpty())
+ ++i;
+ if (nAlgoLength)
+ ++i;
+ uno::Sequence <beans::PropertyValue> aSortDescriptor(7 + i);
+ auto pSortDescriptor = aSortDescriptor.getArray();
+ pSortDescriptor[0].Name = SC_UNONAME_BINDFMT;
+ pSortDescriptor[0].Value <<= bBindFormatsToContent;
+ pSortDescriptor[1].Name = SC_UNONAME_COPYOUT;
+ pSortDescriptor[1].Value <<= bCopyOutputData;
+ pSortDescriptor[2].Name = SC_UNONAME_ISCASE;
+ pSortDescriptor[2].Value <<= bIsCaseSensitive;
+ pSortDescriptor[3].Name = SC_UNONAME_ISULIST;
+ pSortDescriptor[3].Value <<= bEnabledUserList;
+ pSortDescriptor[4].Name = SC_UNONAME_OUTPOS;
+ pSortDescriptor[4].Value <<= aOutputPosition;
+ pSortDescriptor[5].Name = SC_UNONAME_UINDEX;
+ pSortDescriptor[5].Value <<= nUserListIndex;
+ pSortDescriptor[6].Name = SC_UNONAME_SORTFLD;
+ pSortDescriptor[6].Value <<= aSortFields;
+ if (!maLanguageTagODF.isEmpty())
+ {
+ pSortDescriptor[7].Name = SC_UNONAME_COLLLOC;
+ pSortDescriptor[7].Value <<= maLanguageTagODF.getLanguageTag().getLocale( false);
+ }
+ if (nAlgoLength)
+ {
+ pSortDescriptor[6 + i].Name = SC_UNONAME_COLLALG;
+ pSortDescriptor[6 + i].Value <<= sAlgorithm;
+ }
+ pDatabaseRangeContext->SetSortSequence(aSortDescriptor);
+}
+
+void ScXMLSortContext::AddSortField(std::u16string_view sFieldNumber, std::u16string_view sDataType, std::u16string_view sOrder)
+{
+ util::SortField aSortField;
+ aSortField.Field = o3tl::toInt32(sFieldNumber);
+ if (IsXMLToken(sOrder, XML_ASCENDING))
+ aSortField.SortAscending = true;
+ else
+ aSortField.SortAscending = false;
+ if (sDataType.size() > 8)
+ {
+ std::u16string_view sTemp = sDataType.substr(0, 8);
+ if (sTemp == u"UserList")
+ {
+ bEnabledUserList = true;
+ sTemp = sDataType.substr(8);
+ nUserListIndex = static_cast<sal_Int16>(o3tl::toInt32(sTemp));
+ }
+ else
+ {
+ if (IsXMLToken(sDataType, XML_AUTOMATIC))
+ aSortField.FieldType = util::SortFieldType_AUTOMATIC;
+ }
+ }
+ else
+ {
+ if (IsXMLToken(sDataType, XML_TEXT))
+ aSortField.FieldType = util::SortFieldType_ALPHANUMERIC;
+ else if (IsXMLToken(sDataType, XML_NUMBER))
+ aSortField.FieldType = util::SortFieldType_NUMERIC;
+ }
+ aSortFields.realloc(aSortFields.getLength() + 1);
+ aSortFields.getArray()[aSortFields.getLength() - 1] = aSortField;
+}
+
+ScXMLSortByContext::ScXMLSortByContext( ScXMLImport& rImport,
+ sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSortContext* pTempSortContext) :
+ ScXMLImportContext( rImport ),
+ pSortContext(pTempSortContext),
+ sDataType(GetXMLToken(XML_AUTOMATIC)),
+ sOrder(GetXMLToken(XML_ASCENDING))
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ {
+ sFieldNumber = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ sDataType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ORDER ):
+ {
+ sOrder = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSortByContext::~ScXMLSortByContext()
+{
+}
+
+void SAL_CALL ScXMLSortByContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pSortContext->AddSortField(sFieldNumber, sDataType, sOrder);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsorti.hxx b/sc/source/filter/xml/xmlsorti.hxx
new file mode 100644
index 0000000000..cc5e99e93d
--- /dev/null
+++ b/sc/source/filter/xml/xmlsorti.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 .
+ */
+#pragma once
+
+#include <xmloff/languagetagodf.hxx>
+#include <com/sun/star/table/CellAddress.hpp>
+
+#include "importcontext.hxx"
+
+namespace com::sun::star::util { struct SortField; }
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLImport;
+class ScXMLDatabaseRangeContext;
+
+class ScXMLSortContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+
+ css::uno::Sequence <css::util::SortField> aSortFields;
+ css::table::CellAddress aOutputPosition;
+ LanguageTagODF maLanguageTagODF;
+ OUString sAlgorithm;
+ sal_Int16 nUserListIndex;
+ bool bCopyOutputData;
+ bool bBindFormatsToContent;
+ bool bIsCaseSensitive;
+ bool bEnabledUserList;
+
+public:
+
+ ScXMLSortContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSortContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void AddSortField(std::u16string_view sFieldNumber, std::u16string_view sDataType, std::u16string_view sOrder);
+};
+
+class ScXMLSortByContext : public ScXMLImportContext
+{
+ ScXMLSortContext* pSortContext;
+
+ OUString sFieldNumber;
+ OUString sDataType;
+ OUString sOrder;
+
+public:
+
+ ScXMLSortByContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSortContext* pTempSortContext);
+
+ virtual ~ScXMLSortByContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyle.cxx b/sc/source/filter/xml/xmlstyle.cxx
new file mode 100644
index 0000000000..ddcdd54400
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyle.cxx
@@ -0,0 +1,1930 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlstyle.hxx"
+#include "xmlexprt.hxx"
+
+#include <rangeutl.hxx>
+#include <unonames.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltypes.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/contextid.hxx>
+#include <xmloff/txtprmap.hxx>
+#include <xmloff/XMLComplexColorHandler.hxx>
+#include <xmloff/XMLComplexColorExport.hxx>
+#include <docmodel/color/ComplexColor.hxx>
+#include <docmodel/uno/UnoComplexColor.hxx>
+#include <sax/tools/converter.hxx>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/CellVertJustify2.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellJustifyMethod.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/util/XComplexColor.hpp>
+#include <com/sun/star/sheet/XSheetConditionalEntry.hpp>
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <comphelper/extract.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+using namespace ::xmloff::token;
+using namespace ::formula;
+
+#define MAP(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_010, false }
+#define MAP_ODF13(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_013, false }
+// extensions import/export
+#define MAP_EXT(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, false }
+// extensions import only
+#define MAP_EXT_I(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, true }
+#define MAP_END() { nullptr }
+
+const XMLPropertyMapEntry aXMLScCellStylesProperties[] =
+{
+ MAP( SC_UNONAME_ASIANVERT, XML_NAMESPACE_STYLE, XML_GLYPH_ORIENTATION_VERTICAL, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTICAL, 0),
+ MAP( SC_UNONAME_BOTTBORDER, XML_NAMESPACE_FO, XML_BORDER_BOTTOM, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_BOTTOMBORDER ),
+ MAP( SC_UNONAME_BOTTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_BOTTOM, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_BOTTOMBORDERWIDTH ),
+ MAP_EXT(SC_UNONAME_BOTTOM_BORDER_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BORDER_BOTTOM_COMPLEX_COLOR, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_COMPLEX_COLOR | MID_FLAG_ELEMENT_ITEM, CTF_COMPLEX_COLOR),
+ MAP( SC_UNONAME_CELLBACK, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_COLORTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP_EXT( SC_UNONAME_CELL_BACKGROUND_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BACKGROUND_COMPLEX_COLOR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_COMPLEX_COLOR|MID_FLAG_ELEMENT_ITEM, CTF_COMPLEX_COLOR),
+ MAP( SC_UNONAME_CELLPRO, XML_NAMESPACE_STYLE, XML_CELL_PROTECT, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_CELLPROTECTION|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLPRO, XML_NAMESPACE_STYLE, XML_PRINT_CONTENT, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_PRINTCONTENT|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLSTYL, XML_NAMESPACE_STYLE, XML_STYLE, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_STRING, CTF_SC_CELLSTYLE ),
+ MAP( SC_UNONAME_CONDXML, XML_NAMESPACE_STYLE, XML_MAP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_IMPORT_MAP ),
+ MAP( SC_UNONAME_CONDXML, XML_NAMESPACE_STYLE, XML_MAP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_MAP ),
+ MAP( SC_UNONAME_DIAGONAL_BLTR, XML_NAMESPACE_STYLE, XML_DIAGONAL_BL_TR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_DIAGONALBLTR ),
+ MAP( SC_UNONAME_DIAGONAL_BLTR, XML_NAMESPACE_STYLE, XML_DIAGONAL_BL_TR_WIDTH, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALBLTRWIDTH ), // #i102690# for old files
+ MAP( SC_UNONAME_DIAGONAL_BLTR, XML_NAMESPACE_STYLE, XML_DIAGONAL_BL_TR_WIDTHS, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALBLTRWIDTHS ),
+ MAP( SC_UNONAME_DIAGONAL_TLBR, XML_NAMESPACE_STYLE, XML_DIAGONAL_TL_BR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_DIAGONALTLBR ),
+ MAP( SC_UNONAME_DIAGONAL_TLBR, XML_NAMESPACE_STYLE, XML_DIAGONAL_TL_BR_WIDTH, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALTLBRWIDTH ), // #i102690# for old files
+ MAP( SC_UNONAME_DIAGONAL_TLBR, XML_NAMESPACE_STYLE, XML_DIAGONAL_TL_BR_WIDTHS, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALTLBRWIDTHS ),
+ MAP( SC_UNONAME_CELLHJUS, XML_NAMESPACE_FO, XML_TEXT_ALIGN, XML_TYPE_PROP_PARAGRAPH|XML_SC_TYPE_HORIJUSTIFY|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLHJUS, XML_NAMESPACE_STYLE, XML_TEXT_ALIGN_SOURCE, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_HORIJUSTIFYSOURCE|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLHJUS, XML_NAMESPACE_STYLE, XML_REPEAT_CONTENT, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_HORIJUSTIFYREPEAT|MID_FLAG_MERGE_PROPERTY, 0 ),
+ // FIXME this was wrongly exported to STYLE namespace since 2013
+ MAP_EXT( SC_UNONAME_HYPERLINK, XML_NAMESPACE_STYLE, XML_HYPERLINK, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_STRING | MID_FLAG_ELEMENT_ITEM, CTF_SC_HYPERLINK ),
+ MAP_EXT( SC_UNONAME_HYPERLINK, XML_NAMESPACE_LO_EXT, XML_HYPERLINK, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_STRING | MID_FLAG_ELEMENT_ITEM, CTF_SC_HYPERLINK ),
+ MAP_EXT( SC_UNONAME_CELLHJUS_METHOD, XML_NAMESPACE_CSS3TEXT, XML_TEXT_JUSTIFY, XML_TYPE_PROP_PARAGRAPH|XML_SC_TYPE_HORIJUSTIFY_METHOD, 0 ),
+ MAP( SC_UNONAME_CELLTRAN, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_ISTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_WRAP, XML_NAMESPACE_FO, XML_WRAP_OPTION, XML_TYPE_PROP_TABLE_CELL|XML_SC_ISTEXTWRAPPED, 0 ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_FO, XML_BORDER, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_ALLBORDER ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_FO, XML_BORDER_LEFT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_LEFTBORDER ),
+ MAP_EXT(SC_UNONAME_LEFT_BORDER_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BORDER_LEFT_COMPLEX_COLOR, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_COMPLEX_COLOR | MID_FLAG_ELEMENT_ITEM, CTF_COMPLEX_COLOR),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_ALLBORDERWIDTH ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_LEFT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_LEFTBORDERWIDTH ),
+ MAP( SC_UNONAME_NUMFMT, XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_NUMBER|MID_FLAG_SPECIAL_ITEM, CTF_SC_NUMBERFORMAT),
+ MAP( SC_UNONAME_ORIENT, XML_NAMESPACE_STYLE, XML_DIRECTION, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_ORIENTATION, 0 ),
+ MAP( SC_UNONAME_PBMARGIN, XML_NAMESPACE_FO, XML_PADDING, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_ALLPADDING ),
+ MAP( SC_UNONAME_PBMARGIN, XML_NAMESPACE_FO, XML_PADDING_BOTTOM, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_BOTTOMPADDING ),
+ MAP( SC_UNONAME_PINDENT, XML_NAMESPACE_FO, XML_MARGIN_LEFT, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_MEASURE16, 0 ),
+ MAP( SC_UNONAME_PLMARGIN, XML_NAMESPACE_FO, XML_PADDING_LEFT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_LEFTPADDING ),
+ MAP( SC_UNONAME_PRMARGIN, XML_NAMESPACE_FO, XML_PADDING_RIGHT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_RIGHTPADDING ),
+ MAP( SC_UNONAME_PTMARGIN, XML_NAMESPACE_FO, XML_PADDING_TOP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_TOPPADDING ),
+ MAP( SC_UNONAME_RIGHTBORDER, XML_NAMESPACE_FO, XML_BORDER_RIGHT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_RIGHTBORDER ),
+ MAP( SC_UNONAME_RIGHTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_RIGHT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_RIGHTBORDERWIDTH ),
+ MAP_EXT(SC_UNONAME_RIGHT_BORDER_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BORDER_RIGHT_COMPLEX_COLOR, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_COMPLEX_COLOR | MID_FLAG_ELEMENT_ITEM, CTF_COMPLEX_COLOR),
+ MAP( SC_UNONAME_ROTANG, XML_NAMESPACE_STYLE, XML_ROTATION_ANGLE, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_ROTATEANGLE, 0 ),
+ MAP( SC_UNONAME_ROTREF, XML_NAMESPACE_STYLE, XML_ROTATION_ALIGN, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_ROTATEREFERENCE, 0),
+ MAP( SC_UNONAME_SHADOW, XML_NAMESPACE_STYLE, XML_SHADOW, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_TEXT_SHADOW, 0 ),
+ MAP( SC_UNONAME_SHRINK_TO_FIT, XML_NAMESPACE_STYLE, XML_SHRINK_TO_FIT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BOOL, 0 ),
+ MAP( SC_UNO_STANDARDDEC, XML_NAMESPACE_STYLE, XML_DECIMAL_PLACES, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_NUMBER16, 0 ),
+ MAP( SC_UNONAME_TOPBORDER, XML_NAMESPACE_FO, XML_BORDER_TOP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_TOPBORDER ),
+ MAP( SC_UNONAME_TOPBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_TOP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_TOPBORDERWIDTH ),
+ MAP_EXT(SC_UNONAME_TOP_BORDER_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BORDER_TOP_COMPLEX_COLOR, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_COMPLEX_COLOR | MID_FLAG_ELEMENT_ITEM, CTF_COMPLEX_COLOR),
+ MAP( SC_UNONAME_USERDEF, XML_NAMESPACE_TEXT, XML_XMLNS, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_ATTRIBUTE_CONTAINER | MID_FLAG_SPECIAL_ITEM, 0 ),
+ MAP( SC_UNONAME_VALIXML, XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BUILDIN_CMP_ONLY, CTF_SC_VALIDATION ),
+ MAP( SC_UNONAME_CELLVJUS, XML_NAMESPACE_STYLE, XML_VERTICAL_ALIGN, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTJUSTIFY, 0),
+ MAP_EXT_I( SC_UNONAME_CELLVJUS_METHOD, XML_NAMESPACE_STYLE, XML_VERTICAL_JUSTIFY, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTJUSTIFY_METHOD, 0 ), // proposed ODF 1.2+
+ MAP_EXT( SC_UNONAME_CELLVJUS_METHOD, XML_NAMESPACE_LO_EXT, XML_VERTICAL_JUSTIFY, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTJUSTIFY_METHOD, 0 ), // extension namespace
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScColumnStylesProperties[] =
+{
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_COLUMN|XML_SC_TYPE_BREAKBEFORE, 0),
+ MAP( SC_UNONAME_CELLVIS, XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TYPE_PROP_TABLE_COLUMN|XML_SC_TYPE_EQUAL|MID_FLAG_SPECIAL_ITEM, CTF_SC_ISVISIBLE ),
+ MAP( SC_UNONAME_CELLWID, XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH, XML_TYPE_PROP_TABLE_COLUMN|XML_TYPE_MEASURE, 0 ),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScRowStylesImportProperties[] =
+{
+ // #i57867# Include background color (CellBackColor/IsCellBackgroundTransparent) for import only.
+ // Import and export should use the same map, with MID_FLAG_NO_PROPERTY_EXPORT for the background entries,
+ // but this doesn't work at the moment because SvXMLImportPropertyMapper compares MID_FLAG_NO_PROPERTY to 0.
+ // If this is changed (not for 2.0.x), a single map can be used again.
+
+ MAP( SC_UNONAME_CELLBACK, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_COLORTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_CELLHGT, XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_MEASURE, CTF_SC_ROWHEIGHT),
+ MAP( SC_UNONAME_CELLTRAN, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_ISTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP_EXT( SC_UNONAME_CELL_BACKGROUND_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BACKGROUND_COMPLEX_COLOR, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_COMPLEX_COLOR, CTF_COMPLEX_COLOR ),
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_ROW|XML_SC_TYPE_BREAKBEFORE, CTF_SC_ROWBREAKBEFORE),
+ MAP( SC_UNONAME_OHEIGHT, XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_BOOL, CTF_SC_ROWOPTIMALHEIGHT),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScRowStylesProperties[] =
+{
+ MAP( SC_UNONAME_CELLHGT, XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_MEASURE, CTF_SC_ROWHEIGHT),
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_ROW|XML_SC_TYPE_BREAKBEFORE, CTF_SC_ROWBREAKBEFORE),
+ MAP( SC_UNONAME_OHEIGHT, XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_BOOL, CTF_SC_ROWOPTIMALHEIGHT),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScFromXLSRowStylesProperties[] =
+{
+ MAP( SC_UNONAME_CELLHGT, XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_MEASURE, CTF_SC_ROWHEIGHT),
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_ROW|XML_SC_TYPE_BREAKBEFORE, CTF_SC_ROWBREAKBEFORE),
+ MAP( SC_UNONAME_OHEIGHT, XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_BOOL_FALSE, CTF_SC_ROWOPTIMALHEIGHT),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScTableStylesImportProperties[] =
+{
+ // #i57869# Include background color (CellBackColor/IsCellBackgroundTransparent) for import only.
+ // Import and export should use the same map, with MID_FLAG_NO_PROPERTY_EXPORT for the background entries,
+ // but this doesn't work at the moment because SvXMLImportPropertyMapper compares MID_FLAG_NO_PROPERTY to 0.
+ // If this is changed (not for 2.0.x), a single map can be used again.
+
+ MAP( SC_UNONAME_CELLBACK, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_CELLTRAN, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_ISTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP_EXT( SC_UNONAME_CELL_BACKGROUND_COMPLEX_COLOR, XML_NAMESPACE_LO_EXT, XML_BACKGROUND_COMPLEX_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COMPLEX_COLOR, CTF_COMPLEX_COLOR ),
+ MAP( SC_UNONAME_CELLVIS, XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TYPE_PROP_TABLE|XML_TYPE_BOOL, 0 ),
+ MAP( SC_UNONAME_PAGESTL, XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, XML_TYPE_PROP_TABLE|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_MASTERPAGENAME ),
+ MAP( SC_UNONAME_TABLAYOUT, XML_NAMESPACE_STYLE, XML_WRITING_MODE, XML_TYPE_PROP_TABLE|XML_TYPE_TEXT_WRITING_MODE, 0 ),
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE_EXT, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScTableStylesProperties[] =
+{
+ MAP( SC_UNONAME_CELLVIS, XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TYPE_PROP_TABLE|XML_TYPE_BOOL, 0 ),
+ MAP( SC_UNONAME_PAGESTL, XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, XML_TYPE_PROP_TABLE|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_MASTERPAGENAME ),
+ MAP( SC_UNONAME_TABLAYOUT, XML_NAMESPACE_STYLE, XML_WRITING_MODE, XML_TYPE_PROP_TABLE|XML_TYPE_TEXT_WRITING_MODE, 0 ),
+ // ODF 1.3 OFFICE-2173
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE_EXT, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_END()
+};
+
+ScXMLCellExportPropertyMapper::ScXMLCellExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLCellExportPropertyMapper::~ScXMLCellExportPropertyMapper()
+{
+}
+
+void ScXMLCellExportPropertyMapper::ContextFilter(
+ bool bEnableFoFontFamily,
+ ::std::vector< XMLPropertyState >& rProperties,
+ const uno::Reference< beans::XPropertySet >& rPropSet ) const
+{
+ XMLPropertyState* pPadding = nullptr;
+ XMLPropertyState* pPadding_Bottom = nullptr;
+ XMLPropertyState* pPadding_Left = nullptr;
+ XMLPropertyState* pPadding_Right = nullptr;
+ XMLPropertyState* pPadding_Top = nullptr;
+
+ XMLPropertyState* pBorder = nullptr;
+ XMLPropertyState* pBorder_Bottom = nullptr;
+ XMLPropertyState* pBorder_Left = nullptr;
+ XMLPropertyState* pBorder_Right = nullptr;
+ XMLPropertyState* pBorder_Top = nullptr;
+ XMLPropertyState* pSWBorder = nullptr;
+ XMLPropertyState* pSWBorder_Bottom = nullptr;
+ XMLPropertyState* pSWBorder_Left = nullptr;
+ XMLPropertyState* pSWBorder_Right = nullptr;
+ XMLPropertyState* pSWBorder_Top = nullptr;
+
+ XMLPropertyState* pAllBorderWidthState = nullptr;
+ XMLPropertyState* pLeftBorderWidthState = nullptr;
+ XMLPropertyState* pRightBorderWidthState = nullptr;
+ XMLPropertyState* pTopBorderWidthState = nullptr;
+ XMLPropertyState* pBottomBorderWidthState = nullptr;
+ XMLPropertyState* pSWAllBorderWidthState = nullptr;
+ XMLPropertyState* pSWLeftBorderWidthState = nullptr;
+ XMLPropertyState* pSWRightBorderWidthState = nullptr;
+ XMLPropertyState* pSWTopBorderWidthState = nullptr;
+ XMLPropertyState* pSWBottomBorderWidthState = nullptr;
+ XMLPropertyState* pDiagonalTLBRWidthState = nullptr;
+ XMLPropertyState* pDiagonalBLTRWidthState = nullptr;
+
+ XMLPropertyState* pParaMarginLeft = nullptr;
+ XMLPropertyState* pParaMarginLeftRel = nullptr;
+ XMLPropertyState* pParaMarginRight = nullptr;
+ XMLPropertyState* pParaMarginRightRel = nullptr;
+ XMLPropertyState* pParaMarginTop = nullptr;
+ XMLPropertyState* pParaMarginTopRel = nullptr;
+ XMLPropertyState* pParaMarginBottom = nullptr;
+ XMLPropertyState* pParaMarginBottomRel = nullptr;
+
+ XMLPropertyState* pParaAdjust = nullptr;
+ XMLPropertyState* pParaAdjustLast = nullptr;
+
+ for( auto& rProperty : rProperties )
+ {
+ XMLPropertyState* propertyState = &rProperty;
+ if (propertyState->mnIndex != -1)
+ {
+ switch( getPropertySetMapper()->GetEntryContextId( propertyState->mnIndex ) )
+ {
+ case CTF_SC_ALLPADDING: pPadding = propertyState; break;
+ case CTF_SC_BOTTOMPADDING: pPadding_Bottom = propertyState; break;
+ case CTF_SC_LEFTPADDING: pPadding_Left = propertyState; break;
+ case CTF_SC_RIGHTPADDING: pPadding_Right = propertyState; break;
+ case CTF_SC_TOPPADDING: pPadding_Top = propertyState; break;
+ case CTF_SC_ALLBORDER: pBorder = propertyState; break;
+ case CTF_SC_LEFTBORDER: pBorder_Left = propertyState; break;
+ case CTF_SC_RIGHTBORDER: pBorder_Right = propertyState; break;
+ case CTF_SC_BOTTOMBORDER: pBorder_Bottom = propertyState; break;
+ case CTF_SC_TOPBORDER: pBorder_Top = propertyState; break;
+ case CTF_SC_ALLBORDERWIDTH: pAllBorderWidthState = propertyState; break;
+ case CTF_SC_LEFTBORDERWIDTH: pLeftBorderWidthState = propertyState; break;
+ case CTF_SC_RIGHTBORDERWIDTH: pRightBorderWidthState = propertyState; break;
+ case CTF_SC_TOPBORDERWIDTH: pTopBorderWidthState = propertyState; break;
+ case CTF_SC_BOTTOMBORDERWIDTH: pBottomBorderWidthState = propertyState; break;
+ case CTF_ALLBORDER: pSWBorder = propertyState; break;
+ case CTF_LEFTBORDER: pSWBorder_Left = propertyState; break;
+ case CTF_RIGHTBORDER: pSWBorder_Right = propertyState; break;
+ case CTF_BOTTOMBORDER: pSWBorder_Bottom = propertyState; break;
+ case CTF_TOPBORDER: pSWBorder_Top = propertyState; break;
+ case CTF_ALLBORDERWIDTH: pSWAllBorderWidthState = propertyState; break;
+ case CTF_LEFTBORDERWIDTH: pSWLeftBorderWidthState = propertyState; break;
+ case CTF_RIGHTBORDERWIDTH: pSWRightBorderWidthState = propertyState; break;
+ case CTF_TOPBORDERWIDTH: pSWTopBorderWidthState = propertyState; break;
+ case CTF_BOTTOMBORDERWIDTH: pSWBottomBorderWidthState = propertyState; break;
+ case CTF_SC_DIAGONALTLBR: break; //old diagonal line attribute names without "s" are only read, not written
+ case CTF_SC_DIAGONALTLBRWIDTH: pDiagonalTLBRWidthState = propertyState; break;
+ case CTF_SC_DIAGONALBLTR: break; //old diagonal line attribute names without "s" are only read, not written
+ case CTF_SC_DIAGONALBLTRWIDTH: pDiagonalBLTRWidthState = propertyState; break;
+ case CTF_SD_SHAPE_PARA_ADJUST: pParaAdjust = propertyState; break;
+ case CTF_PARA_ADJUSTLAST: pParaAdjustLast = propertyState; break;
+ case CTF_PARALEFTMARGIN: pParaMarginLeft = propertyState; break;
+ case CTF_PARALEFTMARGIN_REL: pParaMarginLeftRel = propertyState; break;
+ case CTF_PARARIGHTMARGIN: pParaMarginRight = propertyState; break;
+ case CTF_PARARIGHTMARGIN_REL: pParaMarginRightRel = propertyState; break;
+ case CTF_PARATOPMARGIN: pParaMarginTop = propertyState; break;
+ case CTF_PARATOPMARGIN_REL: pParaMarginTopRel = propertyState; break;
+ case CTF_PARABOTTOMMARGIN: pParaMarginBottom = propertyState; break;
+ case CTF_PARABOTTOMMARGIN_REL: pParaMarginBottomRel = propertyState; break;
+ }
+ }
+ }
+
+ if (pPadding && pPadding_Bottom && pPadding_Left && pPadding_Right && pPadding_Top)
+ {
+ sal_Int32 nBottom = 0, nTop = 0, nLeft = 0, nRight = 0;
+ if ((pPadding_Bottom->maValue >>= nBottom) &&
+ (pPadding_Left->maValue >>= nLeft) &&
+ (pPadding_Right->maValue >>= nRight) &&
+ (pPadding_Top->maValue >>= nTop))
+ {
+ if ((nBottom == nTop) && (nLeft == nRight) && (nTop == nLeft))
+ {
+ pPadding_Bottom->mnIndex = -1;
+ pPadding_Bottom->maValue.clear();
+ pPadding_Left->mnIndex = -1;
+ pPadding_Left->maValue.clear();
+ pPadding_Right->mnIndex = -1;
+ pPadding_Right->maValue.clear();
+ pPadding_Top->mnIndex = -1;
+ pPadding_Top->maValue.clear();
+ }
+ else
+ {
+ pPadding->mnIndex = -1;
+ pPadding->maValue.clear();
+ }
+ }
+ }
+ if( pBorder )
+ {
+ if( pBorder_Left && pBorder_Right && pBorder_Top && pBorder_Bottom )
+ {
+ table::BorderLine2 aLeft, aRight, aTop, aBottom;
+
+ pBorder_Left->maValue >>= aLeft;
+ pBorder_Right->maValue >>= aRight;
+ pBorder_Top->maValue >>= aTop;
+ pBorder_Bottom->maValue >>= aBottom;
+ if( aLeft.Color == aRight.Color && aLeft.InnerLineWidth == aRight.InnerLineWidth &&
+ aLeft.OuterLineWidth == aRight.OuterLineWidth && aLeft.LineDistance == aRight.LineDistance &&
+ aLeft.Color == aTop.Color && aLeft.InnerLineWidth == aTop.InnerLineWidth &&
+ aLeft.OuterLineWidth == aTop.OuterLineWidth && aLeft.LineDistance == aTop.LineDistance &&
+ aLeft.Color == aBottom.Color && aLeft.InnerLineWidth == aBottom.InnerLineWidth &&
+ aLeft.OuterLineWidth == aBottom.OuterLineWidth && aLeft.LineDistance == aBottom.LineDistance &&
+ aLeft.LineStyle == aRight.LineStyle && aLeft.LineStyle == aTop.LineStyle &&
+ aLeft.LineStyle == aBottom.LineStyle && aLeft.LineWidth == aRight.LineWidth &&
+ aLeft.LineWidth == aTop.LineWidth && aLeft.LineWidth == aBottom.LineWidth )
+ {
+ pBorder_Left->mnIndex = -1;
+ pBorder_Left->maValue.clear();
+ pBorder_Right->mnIndex = -1;
+ pBorder_Right->maValue.clear();
+ pBorder_Top->mnIndex = -1;
+ pBorder_Top->maValue.clear();
+ pBorder_Bottom->mnIndex = -1;
+ pBorder_Bottom->maValue.clear();
+ }
+ else
+ {
+ pBorder->mnIndex = -1;
+ pBorder->maValue.clear();
+ }
+ }
+ else
+ {
+ pBorder->mnIndex = -1;
+ pBorder->maValue.clear();
+ }
+ }
+ if( pAllBorderWidthState )
+ {
+ if( pLeftBorderWidthState && pRightBorderWidthState && pTopBorderWidthState && pBottomBorderWidthState )
+ {
+ table::BorderLine2 aLeft, aRight, aTop, aBottom;
+
+ pLeftBorderWidthState->maValue >>= aLeft;
+ pRightBorderWidthState->maValue >>= aRight;
+ pTopBorderWidthState->maValue >>= aTop;
+ pBottomBorderWidthState->maValue >>= aBottom;
+ if( aLeft.InnerLineWidth == aRight.InnerLineWidth && aLeft.OuterLineWidth == aRight.OuterLineWidth &&
+ aLeft.LineDistance == aRight.LineDistance && aLeft.InnerLineWidth == aTop.InnerLineWidth &&
+ aLeft.OuterLineWidth == aTop.OuterLineWidth && aLeft.LineDistance == aTop.LineDistance &&
+ aLeft.InnerLineWidth == aBottom.InnerLineWidth && aLeft.OuterLineWidth == aBottom.OuterLineWidth &&
+ aLeft.LineDistance == aBottom.LineDistance && aLeft.LineWidth == aRight.LineWidth &&
+ aLeft.LineWidth == aTop.LineWidth && aLeft.LineWidth == aBottom.LineWidth )
+ {
+ pLeftBorderWidthState->mnIndex = -1;
+ pLeftBorderWidthState->maValue.clear();
+ pRightBorderWidthState->mnIndex = -1;
+ pRightBorderWidthState->maValue.clear();
+ pTopBorderWidthState->mnIndex = -1;
+ pTopBorderWidthState->maValue.clear();
+ pBottomBorderWidthState->mnIndex = -1;
+ pBottomBorderWidthState->maValue.clear();
+ }
+ else
+ {
+ pAllBorderWidthState->mnIndex = -1;
+ pAllBorderWidthState->maValue.clear();
+ }
+ }
+ else
+ {
+ pAllBorderWidthState->mnIndex = -1;
+ pAllBorderWidthState->maValue.clear();
+ }
+ }
+
+ if (pParaAdjust)
+ {
+ pParaAdjust->mnIndex = -1;
+ pParaAdjust->maValue.clear();
+ }
+ if (pParaAdjustLast)
+ {
+ pParaAdjustLast->mnIndex = -1;
+ pParaAdjustLast->maValue.clear();
+ }
+ if (pSWBorder)
+ {
+ pSWBorder->mnIndex = -1;
+ pSWBorder->maValue.clear();
+ }
+ if (pSWBorder_Left)
+ {
+ pSWBorder_Left->mnIndex = -1;
+ pSWBorder_Left->maValue.clear();
+ }
+ if (pSWBorder_Right)
+ {
+ pSWBorder_Right->mnIndex = -1;
+ pSWBorder_Right->maValue.clear();
+ }
+ if (pSWBorder_Bottom)
+ {
+ pSWBorder_Bottom->mnIndex = -1;
+ pSWBorder_Bottom->maValue.clear();
+ }
+ if (pSWBorder_Top)
+ {
+ pSWBorder_Top->mnIndex = -1;
+ pSWBorder_Top->maValue.clear();
+ }
+ if (pSWAllBorderWidthState)
+ {
+ pSWAllBorderWidthState->mnIndex = -1;
+ pSWAllBorderWidthState->maValue.clear();
+ }
+ if (pSWLeftBorderWidthState)
+ {
+ pSWLeftBorderWidthState->mnIndex = -1;
+ pSWLeftBorderWidthState->maValue.clear();
+ }
+ if (pSWRightBorderWidthState)
+ {
+ pSWRightBorderWidthState->mnIndex = -1;
+ pSWRightBorderWidthState->maValue.clear();
+ }
+ if (pSWTopBorderWidthState)
+ {
+ pSWTopBorderWidthState->mnIndex = -1;
+ pSWTopBorderWidthState->maValue.clear();
+ }
+ if (pSWBottomBorderWidthState)
+ {
+ pSWBottomBorderWidthState->mnIndex = -1;
+ pSWBottomBorderWidthState->maValue.clear();
+ }
+
+ if (pParaMarginLeft)
+ {
+ pParaMarginLeft->mnIndex = -1;
+ pParaMarginLeft->maValue.clear();
+ }
+ if (pParaMarginLeftRel)
+ {
+ pParaMarginLeftRel->mnIndex = -1;
+ pParaMarginLeftRel->maValue.clear();
+ }
+ if (pParaMarginRight)
+ {
+ pParaMarginRight->mnIndex = -1;
+ pParaMarginRight->maValue.clear();
+ }
+ if (pParaMarginRightRel)
+ {
+ pParaMarginRightRel->mnIndex = -1;
+ pParaMarginRightRel->maValue.clear();
+ }
+ if (pParaMarginTop)
+ {
+ pParaMarginTop->mnIndex = -1;
+ pParaMarginTop->maValue.clear();
+ }
+ if (pParaMarginTopRel)
+ {
+ pParaMarginTopRel->mnIndex = -1;
+ pParaMarginTopRel->maValue.clear();
+ }
+ if (pParaMarginBottom)
+ {
+ pParaMarginBottom->mnIndex = -1;
+ pParaMarginBottom->maValue.clear();
+ }
+ if (pParaMarginBottomRel)
+ {
+ pParaMarginBottomRel->mnIndex = -1;
+ pParaMarginBottomRel->maValue.clear();
+ }
+
+ // #i102690# old diagonal line attribute names without "s" are only read, not written
+ if (pDiagonalTLBRWidthState)
+ {
+ pDiagonalTLBRWidthState->mnIndex = -1;
+ pDiagonalTLBRWidthState->maValue.clear();
+ }
+ if (pDiagonalBLTRWidthState)
+ {
+ pDiagonalBLTRWidthState->mnIndex = -1;
+ pDiagonalBLTRWidthState->maValue.clear();
+ }
+
+ SvXMLExportPropertyMapper::ContextFilter(bEnableFoFontFamily, rProperties, rPropSet);
+}
+
+/** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void ScXMLCellExportPropertyMapper::handleSpecialItem(
+ comphelper::AttributeList& /* rAttrList */,
+ const XMLPropertyState& /* rProperty */,
+ const SvXMLUnitConverter& /* rUnitConverter */,
+ const SvXMLNamespaceMap& /* rNamespaceMap */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */ ) const
+{
+ // the SpecialItem NumberFormat must not be handled by this method
+ // the SpecialItem ConditionlaFormat must not be handled by this method
+ // the SpecialItem CharBackColor must not be handled by this method
+}
+
+void ScXMLCellExportPropertyMapper::handleElementItem(
+ SvXMLExport& rExport,
+ const XMLPropertyState& rProperty,
+ SvXmlExportFlags /* nFlags */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */) const
+{
+ sal_uInt32 nContextId = getPropertySetMapper()->GetEntryContextId( rProperty.mnIndex );
+ switch (nContextId)
+ {
+ case CTF_SC_HYPERLINK:
+ {
+ OUString sURL;
+ if ((rProperty.maValue >>= sURL) && !sURL.isEmpty())
+ {
+ rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sURL );
+ rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE,
+ XML_SIMPLE );
+ sal_uInt32 nPropIndex = rProperty.mnIndex;
+ sal_uInt16 nPrefix = getPropertySetMapper()->GetEntryNameSpace( nPropIndex );
+ OUString sLocalName = getPropertySetMapper()->GetEntryXMLName( nPropIndex );
+ SvXMLElementExport aElem( rExport, nPrefix, sLocalName, true, true );
+ }
+ }
+ break;
+ case CTF_COMPLEX_COLOR:
+ {
+ XMLComplexColorExport aExport(rExport);
+ aExport.exportXML(rProperty.maValue,
+ getPropertySetMapper()->GetEntryNameSpace(rProperty.mnIndex),
+ getPropertySetMapper()->GetEntryXMLName(rProperty.mnIndex));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+ScXMLRowExportPropertyMapper::ScXMLRowExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLRowExportPropertyMapper::~ScXMLRowExportPropertyMapper()
+{
+}
+
+void ScXMLRowExportPropertyMapper::ContextFilter(
+ bool /* bEnableFoFontFamily */,
+ ::std::vector< XMLPropertyState >& /* rProperties */,
+ const uno::Reference< beans::XPropertySet >& /* rPropSet */ ) const
+{
+ //#108550#; don't filter the height, so other applications know the calculated height
+}
+
+ScXMLColumnExportPropertyMapper::ScXMLColumnExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLColumnExportPropertyMapper::~ScXMLColumnExportPropertyMapper()
+{
+}
+
+/** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void ScXMLColumnExportPropertyMapper::handleSpecialItem(
+ comphelper::AttributeList& /* rAttrList */,
+ const XMLPropertyState& /* rProperty */,
+ const SvXMLUnitConverter& /* rUnitConverter */,
+ const SvXMLNamespaceMap& /* rNamespaceMap */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */ ) const
+{
+ // the SpecialItem IsVisible must not be handled by this method
+}
+
+ScXMLTableExportPropertyMapper::ScXMLTableExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLTableExportPropertyMapper::~ScXMLTableExportPropertyMapper()
+{
+}
+
+/** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void ScXMLTableExportPropertyMapper::handleSpecialItem(
+ comphelper::AttributeList& /* rAttrList */,
+ const XMLPropertyState& /* rProperty */,
+ const SvXMLUnitConverter& /* rUnitConverter */,
+ const SvXMLNamespaceMap& /* rNamespaceMap */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */ ) const
+{
+ // the SpecialItem PageStyle must not be handled by this method
+}
+
+void ScXMLAutoStylePoolP::exportStyleAttributes(
+ comphelper::AttributeList& rAttrList,
+ XmlStyleFamily nFamily,
+ const ::std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const
+{
+ SvXMLAutoStylePoolP::exportStyleAttributes( rAttrList, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap );
+ if (nFamily == XmlStyleFamily::TABLE_CELL)
+ {
+ for(const auto& rProperty : rProperties)
+ {
+ rtl::Reference< XMLPropertySetMapper > aPropMapper(rScXMLExport.GetCellStylesPropertySetMapper());
+ sal_Int16 nContextID(aPropMapper->GetEntryContextId(rProperty.mnIndex));
+ switch (nContextID)
+ {
+ case CTF_SC_NUMBERFORMAT :
+ {
+ sal_Int32 nNumberFormat = 0;
+ if (rProperty.maValue >>= nNumberFormat)
+ {
+ OUString sAttrValue(rScXMLExport.getDataStyleName(nNumberFormat));
+ if (!sAttrValue.isEmpty())
+ {
+ GetExport().AddAttribute(
+ aPropMapper->GetEntryNameSpace(rProperty.mnIndex),
+ aPropMapper->GetEntryXMLName(rProperty.mnIndex),
+ sAttrValue );
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ else if (nFamily == XmlStyleFamily::TABLE_TABLE)
+ {
+ for(const auto& rProperty : rProperties)
+ {
+ rtl::Reference< XMLPropertySetMapper > aPropMapper(rScXMLExport.GetTableStylesPropertySetMapper());
+ sal_Int16 nContextID(aPropMapper->GetEntryContextId(rProperty.mnIndex));
+ switch (nContextID)
+ {
+ case CTF_SC_MASTERPAGENAME :
+ {
+ OUString sName;
+ if (rProperty.maValue >>= sName)
+ {
+ GetExport().AddAttribute(
+ aPropMapper->GetEntryNameSpace(rProperty.mnIndex),
+ aPropMapper->GetEntryXMLName(rProperty.mnIndex),
+ GetExport().EncodeStyleName( sName ));
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ScXMLAutoStylePoolP::exportStyleContent(
+ const css::uno::Reference< css::xml::sax::XDocumentHandler > & rHandler,
+ XmlStyleFamily nFamily,
+ const std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const
+{
+ SvXMLAutoStylePoolP::exportStyleContent( rHandler, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap );
+ if (nFamily != XmlStyleFamily::TABLE_CELL)
+ return;
+
+ for(const auto& rProperty : rProperties)
+ {
+ if (rProperty.mnIndex != -1)
+ {
+ sal_Int16 nContextID = rScXMLExport.GetCellStylesPropertySetMapper()->GetEntryContextId(rProperty.mnIndex);
+ switch (nContextID)
+ {
+ case CTF_SC_MAP :
+ {
+ uno::Reference<container::XIndexAccess> xIndex( rProperty.maValue, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ sal_Int32 nConditionCount(xIndex->getCount());
+ for (sal_Int32 nCondition = 0; nCondition < nConditionCount; ++nCondition)
+ {
+ uno::Reference <sheet::XSheetConditionalEntry> xSheetConditionalEntry(xIndex->getByIndex(nCondition), uno::UNO_QUERY);
+ if (xSheetConditionalEntry.is())
+ {
+ OUString sStyleName(xSheetConditionalEntry->getStyleName());
+ uno::Reference <sheet::XSheetCondition> xSheetCondition(xSheetConditionalEntry, uno::UNO_QUERY);
+ if (xSheetCondition.is())
+ {
+ sheet::ConditionOperator aOperator = xSheetCondition->getOperator();
+ if (aOperator != sheet::ConditionOperator_NONE)
+ {
+ if (aOperator == sheet::ConditionOperator_FORMULA)
+ {
+ OUString sCondition = "is-true-formula("
+ + xSheetCondition->getFormula1()
+ + ")";
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_CONDITION, sCondition);
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME, rScXMLExport.EncodeStyleName( sStyleName ));
+ OUString sOUBaseAddress;
+ ScDocument* pDoc = rScXMLExport.GetDocument();
+ ScRangeStringConverter::GetStringFromAddress( sOUBaseAddress,
+ xSheetCondition->getSourcePosition(), pDoc, FormulaGrammar::CONV_OOO );
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_BASE_CELL_ADDRESS, sOUBaseAddress);
+ SvXMLElementExport aMElem(rScXMLExport, XML_NAMESPACE_STYLE, XML_MAP, true, true);
+ }
+ else
+ {
+ OUString sCondition;
+ if (aOperator == sheet::ConditionOperator_BETWEEN ||
+ aOperator == sheet::ConditionOperator_NOT_BETWEEN)
+ {
+ if (aOperator == sheet::ConditionOperator_BETWEEN)
+ sCondition = "cell-content-is-between(";
+ else
+ sCondition = "cell-content-is-not-between(";
+ sCondition += xSheetCondition->getFormula1()
+ + ","
+ + xSheetCondition->getFormula2()
+ + ")";
+ }
+ else
+ {
+ sCondition = "cell-content()";
+ switch (aOperator)
+ {
+ case sheet::ConditionOperator_LESS:
+ sCondition += "<";
+ break;
+ case sheet::ConditionOperator_GREATER:
+ sCondition += ">";
+ break;
+ case sheet::ConditionOperator_LESS_EQUAL:
+ sCondition += "<=";
+ break;
+ case sheet::ConditionOperator_GREATER_EQUAL:
+ sCondition += ">=";
+ break;
+ case sheet::ConditionOperator_EQUAL:
+ sCondition += "=";
+ break;
+ case sheet::ConditionOperator_NOT_EQUAL:
+ sCondition += "!=";
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ sCondition += xSheetCondition->getFormula1();
+ }
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_CONDITION, sCondition);
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME, rScXMLExport.EncodeStyleName( sStyleName ));
+ OUString sOUBaseAddress;
+ ScRangeStringConverter::GetStringFromAddress( sOUBaseAddress,
+ xSheetCondition->getSourcePosition(), rScXMLExport.GetDocument(), FormulaGrammar::CONV_OOO );
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_BASE_CELL_ADDRESS, sOUBaseAddress);
+ SvXMLElementExport aMElem(rScXMLExport, XML_NAMESPACE_STYLE, XML_MAP, true, true);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+ScXMLAutoStylePoolP::ScXMLAutoStylePoolP(ScXMLExport& rTempScXMLExport):
+ SvXMLAutoStylePoolP(rTempScXMLExport),
+ rScXMLExport(rTempScXMLExport)
+{
+}
+
+ScXMLAutoStylePoolP::~ScXMLAutoStylePoolP()
+{
+}
+
+XMLScPropHdlFactory::XMLScPropHdlFactory()
+{
+}
+
+XMLScPropHdlFactory::~XMLScPropHdlFactory()
+{
+}
+
+const XMLPropertyHandler* XMLScPropHdlFactory::GetPropertyHandler( sal_Int32 nType ) const
+{
+ nType &= MID_FLAG_MASK;
+
+ XMLPropertyHandler* pHdl(const_cast<XMLPropertyHandler*>(XMLPropertyHandlerFactory::GetPropertyHandler( nType )));
+ if(!pHdl)
+ {
+ switch(nType)
+ {
+ case XML_SC_TYPE_CELLPROTECTION :
+ {
+ pHdl = new XmlScPropHdl_CellProtection;
+ }
+ break;
+ case XML_SC_TYPE_PRINTCONTENT :
+ {
+ pHdl = new XmlScPropHdl_PrintContent;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFY_METHOD:
+ case XML_SC_TYPE_VERTJUSTIFY_METHOD:
+ {
+ pHdl = new XmlScPropHdl_JustifyMethod;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFY :
+ {
+ pHdl = new XmlScPropHdl_HoriJustify;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFYSOURCE :
+ {
+ pHdl = new XmlScPropHdl_HoriJustifySource;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFYREPEAT :
+ {
+ pHdl = new XmlScPropHdl_HoriJustifyRepeat;
+ }
+ break;
+ case XML_SC_TYPE_ORIENTATION :
+ {
+ pHdl = new XmlScPropHdl_Orientation;
+ }
+ break;
+ case XML_SC_TYPE_ROTATEANGLE :
+ {
+ pHdl = new XmlScPropHdl_RotateAngle;
+ }
+ break;
+ case XML_SC_TYPE_ROTATEREFERENCE :
+ {
+ pHdl = new XmlScPropHdl_RotateReference;
+ }
+ break;
+ case XML_SC_TYPE_VERTJUSTIFY :
+ {
+ pHdl = new XmlScPropHdl_VertJustify;
+ }
+ break;
+ case XML_SC_TYPE_BREAKBEFORE :
+ {
+ pHdl = new XmlScPropHdl_BreakBefore;
+ }
+ break;
+ case XML_SC_ISTEXTWRAPPED :
+ {
+ pHdl = new XmlScPropHdl_IsTextWrapped;
+ }
+ break;
+ case XML_SC_TYPE_EQUAL :
+ {
+ pHdl = new XmlScPropHdl_IsEqual;
+ }
+ break;
+ case XML_SC_TYPE_VERTICAL :
+ {
+ pHdl = new XmlScPropHdl_Vertical;
+ }
+ break;
+ }
+
+ if(pHdl)
+ PutHdlCache(nType, pHdl);
+ }
+
+ return pHdl;
+}
+
+XmlScPropHdl_CellProtection::~XmlScPropHdl_CellProtection()
+{
+}
+
+bool XmlScPropHdl_CellProtection::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ util::CellProtection aCellProtection1, aCellProtection2;
+
+ if((r1 >>= aCellProtection1) && (r2 >>= aCellProtection2))
+ {
+ return ((aCellProtection1.IsHidden == aCellProtection2.IsHidden) &&
+ (aCellProtection1.IsLocked == aCellProtection2.IsLocked) &&
+ (aCellProtection1.IsFormulaHidden == aCellProtection2.IsFormulaHidden));
+ }
+ return false;
+}
+
+bool XmlScPropHdl_CellProtection::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ util::CellProtection aCellProtection;
+ bool bDefault(false);
+ if (!rValue.hasValue())
+ {
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = true;
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsPrintHidden = false;
+ bDefault = true;
+ }
+ if ((rValue >>= aCellProtection) || bDefault)
+ {
+ if (IsXMLToken(rStrImpValue, XML_NONE))
+ {
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = false;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_HIDDEN_AND_PROTECTED))
+ {
+ aCellProtection.IsFormulaHidden = true;
+ aCellProtection.IsHidden = true;
+ aCellProtection.IsLocked = true;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_PROTECTED))
+ {
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = true;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_FORMULA_HIDDEN))
+ {
+ aCellProtection.IsFormulaHidden = true;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = false;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else
+ {
+ sal_Int32 i(0);
+ while (i < rStrImpValue.getLength() && rStrImpValue[i] != ' ')
+ ++i;
+ OUString sFirst(rStrImpValue.copy(0, i));
+ OUString sSecond(rStrImpValue.copy(i + 1));
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = false;
+ if ((IsXMLToken(sFirst, XML_PROTECTED)) || (IsXMLToken(sSecond, XML_PROTECTED)))
+ aCellProtection.IsLocked = true;
+ if ((IsXMLToken(sFirst, XML_FORMULA_HIDDEN)) || (IsXMLToken(sSecond, XML_FORMULA_HIDDEN)))
+ aCellProtection.IsFormulaHidden = true;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_CellProtection::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+ util::CellProtection aCellProtection;
+
+ if(rValue >>= aCellProtection)
+ {
+ if (!(aCellProtection.IsFormulaHidden || aCellProtection.IsHidden || aCellProtection.IsLocked))
+ {
+ rStrExpValue = GetXMLToken(XML_NONE);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsHidden)
+ {
+ // #i105964# "Hide all" implies "Protected" in the UI, so it must be saved as "hidden-and-protected"
+ // even if "IsLocked" is not set in the CellProtection struct.
+ rStrExpValue = GetXMLToken(XML_HIDDEN_AND_PROTECTED);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsLocked && !aCellProtection.IsFormulaHidden)
+ {
+ rStrExpValue = GetXMLToken(XML_PROTECTED);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsFormulaHidden && !aCellProtection.IsLocked)
+ {
+ rStrExpValue = GetXMLToken(XML_FORMULA_HIDDEN);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsFormulaHidden && aCellProtection.IsLocked)
+ {
+ rStrExpValue = GetXMLToken(XML_PROTECTED);
+ rStrExpValue += " ";
+ rStrExpValue += GetXMLToken(XML_FORMULA_HIDDEN);
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_PrintContent::~XmlScPropHdl_PrintContent()
+{
+}
+
+bool XmlScPropHdl_PrintContent::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ util::CellProtection aCellProtection1, aCellProtection2;
+
+ if((r1 >>= aCellProtection1) && (r2 >>= aCellProtection2))
+ {
+ return (aCellProtection1.IsPrintHidden == aCellProtection2.IsPrintHidden);
+ }
+ return false;
+}
+
+bool XmlScPropHdl_PrintContent::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+ util::CellProtection aCellProtection;
+ bool bDefault(false);
+ if (!rValue.hasValue())
+ {
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = true;
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsPrintHidden = false;
+ bDefault = true;
+ }
+ if ((rValue >>= aCellProtection) || bDefault)
+ {
+ bool bValue(false);
+ if (::sax::Converter::convertBool(bValue, rStrImpValue))
+ {
+ aCellProtection.IsPrintHidden = !bValue;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_PrintContent::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ util::CellProtection aCellProtection;
+ if(rValue >>= aCellProtection)
+ {
+ OUStringBuffer sValue;
+ ::sax::Converter::convertBool(sValue, !aCellProtection.IsPrintHidden);
+ rStrExpValue = sValue.makeStringAndClear();
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_JustifyMethod::~XmlScPropHdl_JustifyMethod()
+{
+}
+
+bool XmlScPropHdl_JustifyMethod::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 nVal1(0), nVal2(0);
+
+ if((r1 >>= nVal1) && (r2 >>= nVal2))
+ return (nVal1 == nVal2);
+ return false;
+}
+
+bool XmlScPropHdl_JustifyMethod::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval = false;
+
+ sal_Int32 nValue = table::CellJustifyMethod::AUTO;
+ if (IsXMLToken(rStrImpValue, XML_AUTO))
+ {
+ nValue = table::CellJustifyMethod::AUTO;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_DISTRIBUTE))
+ {
+ nValue = table::CellJustifyMethod::DISTRIBUTE;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else
+ bRetval = true;
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_JustifyMethod::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal(0);
+ bool bRetval = false;
+
+ if (rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellJustifyMethod::AUTO:
+ {
+ rStrExpValue = GetXMLToken(XML_AUTO);
+ bRetval = true;
+ }
+ break;
+ case table::CellJustifyMethod::DISTRIBUTE:
+ {
+ rStrExpValue = GetXMLToken(XML_DISTRIBUTE);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return bRetval;
+}
+
+XmlScPropHdl_HoriJustify::~XmlScPropHdl_HoriJustify()
+{
+}
+
+bool XmlScPropHdl_HoriJustify::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellHoriJustify aHoriJustify1, aHoriJustify2;
+
+ if((r1 >>= aHoriJustify1) && (r2 >>= aHoriJustify2))
+ return (aHoriJustify1 == aHoriJustify2);
+ return false;
+}
+
+bool XmlScPropHdl_HoriJustify::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ table::CellHoriJustify nValue = table::CellHoriJustify_LEFT;
+ rValue >>= nValue;
+ if (nValue != table::CellHoriJustify_REPEAT)
+ {
+ if (IsXMLToken(rStrImpValue, XML_START))
+ {
+ nValue = table::CellHoriJustify_LEFT;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_END))
+ {
+ nValue = table::CellHoriJustify_RIGHT;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_CENTER))
+ {
+ nValue = table::CellHoriJustify_CENTER;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_JUSTIFY))
+ {
+ nValue = table::CellHoriJustify_BLOCK;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ }
+ else
+ bRetval = true;
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_HoriJustify::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellHoriJustify nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellHoriJustify_REPEAT:
+ case table::CellHoriJustify_LEFT:
+ {
+ rStrExpValue = GetXMLToken(XML_START);
+ bRetval = true;
+ }
+ break;
+ case table::CellHoriJustify_RIGHT:
+ {
+ rStrExpValue = GetXMLToken(XML_END);
+ bRetval = true;
+ }
+ break;
+ case table::CellHoriJustify_CENTER:
+ {
+ rStrExpValue = GetXMLToken(XML_CENTER);
+ bRetval = true;
+ }
+ break;
+ case table::CellHoriJustify_BLOCK:
+ {
+ rStrExpValue = GetXMLToken(XML_JUSTIFY);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_HoriJustifySource::~XmlScPropHdl_HoriJustifySource()
+{
+}
+
+bool XmlScPropHdl_HoriJustifySource::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellHoriJustify aHoriJustify1, aHoriJustify2;
+
+ if((r1 >>= aHoriJustify1) && (r2 >>= aHoriJustify2))
+ return (aHoriJustify1 == aHoriJustify2);
+ return false;
+}
+
+bool XmlScPropHdl_HoriJustifySource::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_FIX))
+ {
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_VALUE_TYPE))
+ {
+ rValue <<= table::CellHoriJustify_STANDARD;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_HoriJustifySource::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellHoriJustify nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ if (nVal == table::CellHoriJustify_STANDARD)
+ {
+ rStrExpValue = GetXMLToken(XML_VALUE_TYPE);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_FIX);
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_HoriJustifyRepeat::~XmlScPropHdl_HoriJustifyRepeat()
+{
+}
+
+bool XmlScPropHdl_HoriJustifyRepeat::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellHoriJustify aHoriJustify1, aHoriJustify2;
+
+ if((r1 >>= aHoriJustify1) && (r2 >>= aHoriJustify2))
+ return (aHoriJustify1 == aHoriJustify2);
+ return false;
+}
+
+bool XmlScPropHdl_HoriJustifyRepeat::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_FALSE))
+ {
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TRUE))
+ {
+ rValue <<= table::CellHoriJustify_REPEAT;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_HoriJustifyRepeat::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellHoriJustify nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ if (nVal == table::CellHoriJustify_REPEAT)
+ {
+ rStrExpValue = GetXMLToken(XML_TRUE);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_FALSE);
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_Orientation::~XmlScPropHdl_Orientation()
+{
+}
+
+bool XmlScPropHdl_Orientation::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellOrientation aOrientation1, aOrientation2;
+
+ if((r1 >>= aOrientation1) && (r2 >>= aOrientation2))
+ return (aOrientation1 == aOrientation2);
+ return false;
+}
+
+bool XmlScPropHdl_Orientation::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ table::CellOrientation nValue;
+ if (IsXMLToken(rStrImpValue, XML_LTR))
+ {
+ nValue = table::CellOrientation_STANDARD;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TTB))
+ {
+ nValue = table::CellOrientation_STACKED;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_Orientation::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellOrientation nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellOrientation_STACKED :
+ {
+ rStrExpValue = GetXMLToken(XML_TTB);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ rStrExpValue = GetXMLToken(XML_LTR);
+ bRetval = true;
+ }
+ break;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_RotateAngle::~XmlScPropHdl_RotateAngle()
+{
+}
+
+bool XmlScPropHdl_RotateAngle::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 aAngle1 = 0, aAngle2 = 0;
+
+ if((r1 >>= aAngle1) && (r2 >>= aAngle2))
+ return (aAngle1 == aAngle2);
+ return false;
+}
+
+bool XmlScPropHdl_RotateAngle::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber(nValue, rStrImpValue) && !o3tl::checked_multiply<sal_Int32>(nValue, 100, nValue))
+ {
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_RotateAngle::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal = 0;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ rStrExpValue = OUString::number(nVal / 100);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_RotateReference::~XmlScPropHdl_RotateReference()
+{
+}
+
+bool XmlScPropHdl_RotateReference::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 aReference1(0), aReference2(0);
+
+ if((r1 >>= aReference1) && (r2 >>= aReference2))
+ return (aReference1 == aReference2);
+ return false;
+}
+
+bool XmlScPropHdl_RotateReference::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ sal_Int32 nValue;
+ if (IsXMLToken(rStrImpValue, XML_NONE))
+ {
+ nValue = table::CellVertJustify2::STANDARD;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_BOTTOM))
+ {
+ nValue = table::CellVertJustify2::BOTTOM;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TOP))
+ {
+ nValue = table::CellVertJustify2::TOP;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_CENTER))
+ {
+ nValue = table::CellVertJustify2::CENTER;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_RotateReference::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal(0);
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellVertJustify2::BOTTOM :
+ {
+ rStrExpValue = GetXMLToken(XML_BOTTOM);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::CENTER :
+ {
+ rStrExpValue = GetXMLToken(XML_CENTER);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::STANDARD :
+ {
+ rStrExpValue = GetXMLToken(XML_NONE);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::TOP :
+ {
+ rStrExpValue = GetXMLToken(XML_TOP);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_VertJustify::~XmlScPropHdl_VertJustify()
+{
+}
+
+bool XmlScPropHdl_VertJustify::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 aReference1(0), aReference2(0);
+
+ if((r1 >>= aReference1) && (r2 >>= aReference2))
+ return (aReference1 == aReference2);
+ return false;
+}
+
+bool XmlScPropHdl_VertJustify::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ sal_Int32 nValue;
+ if (IsXMLToken(rStrImpValue, XML_AUTOMATIC))
+ {
+ nValue = table::CellVertJustify2::STANDARD;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_BOTTOM))
+ {
+ nValue = table::CellVertJustify2::BOTTOM;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TOP))
+ {
+ nValue = table::CellVertJustify2::TOP;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_MIDDLE))
+ {
+ nValue = table::CellVertJustify2::CENTER;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_JUSTIFY))
+ {
+ nValue = table::CellVertJustify2::BLOCK;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_VertJustify::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal(0);
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellVertJustify2::BOTTOM :
+ {
+ rStrExpValue = GetXMLToken(XML_BOTTOM);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::CENTER :
+ {
+ rStrExpValue = GetXMLToken(XML_MIDDLE);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::STANDARD :
+ {
+ rStrExpValue = GetXMLToken(XML_AUTOMATIC);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::TOP :
+ {
+ rStrExpValue = GetXMLToken(XML_TOP);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::BLOCK :
+ {
+ rStrExpValue = GetXMLToken(XML_JUSTIFY);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_BreakBefore::~XmlScPropHdl_BreakBefore()
+{
+}
+
+bool XmlScPropHdl_BreakBefore::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ bool aBreak1 = false, aBreak2 = false;
+
+ if((r1 >>= aBreak1) && (r2 >>= aBreak2))
+ return (aBreak1 == aBreak2);
+ return false;
+}
+
+bool XmlScPropHdl_BreakBefore::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ bool bValue;
+ if (IsXMLToken(rStrImpValue, XML_AUTO))
+ {
+ bValue = false;
+ rValue <<= bValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_PAGE))
+ {
+ bValue = true;
+ rValue <<= bValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_BreakBefore::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if(::cppu::any2bool(rValue))
+ {
+ rStrExpValue = GetXMLToken(XML_PAGE);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_AUTO);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_IsTextWrapped::~XmlScPropHdl_IsTextWrapped()
+{
+}
+
+bool XmlScPropHdl_IsTextWrapped::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ return (::cppu::any2bool(r1) == ::cppu::any2bool(r2));
+}
+
+bool XmlScPropHdl_IsTextWrapped::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_WRAP))
+ {
+ rValue <<= true;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_NO_WRAP))
+ {
+ rValue <<= false;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_IsTextWrapped::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (::cppu::any2bool(rValue))
+ {
+ rStrExpValue = GetXMLToken(XML_WRAP);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_NO_WRAP);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_IsEqual::importXML( const OUString& /* rStrImpValue */,
+ css::uno::Any& /* rValue */,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ OSL_FAIL("should never be called");
+ return false;
+}
+
+bool XmlScPropHdl_IsEqual::exportXML( OUString& /* rStrExpValue */,
+ const css::uno::Any& /* rValue */,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ OSL_FAIL("should never be called");
+ return false;
+}
+
+XmlScPropHdl_Vertical::~XmlScPropHdl_Vertical()
+{
+}
+
+bool XmlScPropHdl_Vertical::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ return (::cppu::any2bool(r1) == ::cppu::any2bool(r2));
+}
+
+bool XmlScPropHdl_Vertical::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_AUTO))
+ {
+ rValue <<= true;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_0))
+ {
+ rValue <<= false;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_Vertical::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (::cppu::any2bool(rValue))
+ {
+ rStrExpValue = GetXMLToken(XML_AUTO);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_0);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyle.hxx b/sc/source/filter/xml/xmlstyle.hxx
new file mode 100644
index 0000000000..ed7c569b38
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyle.hxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmlaustp.hxx>
+#include <xmloff/xmltypes.hxx>
+#include <xmloff/prhdlfac.hxx>
+#include <xmloff/xmlexppr.hxx>
+#include <xmloff/contextid.hxx>
+#include <xmloff/xmlprhdl.hxx>
+
+extern const XMLPropertyMapEntry aXMLScCellStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScColumnStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScRowStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScFromXLSRowStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScRowStylesImportProperties[];
+extern const XMLPropertyMapEntry aXMLScTableStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScTableStylesImportProperties[];
+
+//CellStyles
+#define XML_SC_TYPE_CELLPROTECTION (XML_SC_TYPES_START + 1)
+#define XML_SC_TYPE_PRINTCONTENT (XML_SC_TYPES_START + 2)
+#define XML_SC_TYPE_HORIJUSTIFY (XML_SC_TYPES_START + 3)
+#define XML_SC_TYPE_HORIJUSTIFY_METHOD (XML_SC_TYPES_START + 4)
+#define XML_SC_TYPE_HORIJUSTIFYSOURCE (XML_SC_TYPES_START + 5)
+#define XML_SC_TYPE_HORIJUSTIFYREPEAT (XML_SC_TYPES_START + 6)
+#define XML_SC_TYPE_ORIENTATION (XML_SC_TYPES_START + 7)
+#define XML_SC_TYPE_ROTATEANGLE (XML_SC_TYPES_START + 8)
+#define XML_SC_TYPE_ROTATEREFERENCE (XML_SC_TYPES_START + 9)
+#define XML_SC_TYPE_BORDERLEFT (XML_SC_TYPES_START + 10)
+#define XML_SC_TYPE_BORDERRIGHT (XML_SC_TYPES_START + 11)
+#define XML_SC_TYPE_BORDERTOP (XML_SC_TYPES_START + 12)
+#define XML_SC_TYPE_BORDERBOTTOM (XML_SC_TYPES_START + 13)
+#define XML_SC_TYPE_VERTJUSTIFY (XML_SC_TYPES_START + 14)
+#define XML_SC_TYPE_VERTJUSTIFY_METHOD (XML_SC_TYPES_START + 15)
+#define XML_SC_ISTEXTWRAPPED (XML_SC_TYPES_START + 16)
+#define XML_SC_TYPE_EQUAL (XML_SC_TYPES_START + 17)
+#define XML_SC_TYPE_VERTICAL (XML_SC_TYPES_START + 18)
+
+// CTF_SC_HORIJUSTIFY (XML_SC_CTF_START + 1)
+// CTF_SC_HORIJUSTIFY_SOURCE (XML_SC_CTF_START + 2)
+#define CTF_SC_ALLPADDING (XML_SC_CTF_START + 3)
+#define CTF_SC_BOTTOMPADDING (XML_SC_CTF_START + 4)
+#define CTF_SC_LEFTPADDING (XML_SC_CTF_START + 5)
+#define CTF_SC_RIGHTPADDING (XML_SC_CTF_START + 6)
+#define CTF_SC_TOPPADDING (XML_SC_CTF_START + 7)
+#define CTF_SC_ALLBORDER (XML_SC_CTF_START + 8)
+#define CTF_SC_LEFTBORDER (XML_SC_CTF_START + 9)
+#define CTF_SC_RIGHTBORDER (XML_SC_CTF_START + 10)
+#define CTF_SC_TOPBORDER (XML_SC_CTF_START + 11)
+#define CTF_SC_BOTTOMBORDER (XML_SC_CTF_START + 12)
+#define CTF_SC_ALLBORDERWIDTH (XML_SC_CTF_START + 13)
+#define CTF_SC_LEFTBORDERWIDTH (XML_SC_CTF_START + 14)
+#define CTF_SC_RIGHTBORDERWIDTH (XML_SC_CTF_START + 15)
+#define CTF_SC_TOPBORDERWIDTH (XML_SC_CTF_START + 16)
+#define CTF_SC_BOTTOMBORDERWIDTH (XML_SC_CTF_START + 17)
+#define CTF_SC_NUMBERFORMAT (XML_SC_CTF_START + 18)
+#define CTF_SC_MAP (XML_SC_CTF_START + 19)
+// CTF_SC_PARAINDENT (XML_SC_CTF_START + 20)
+// CTF_SC_OLDTEXTBACKGROUND (XML_SC_CTF_START + 21)
+#define CTF_SC_IMPORT_MAP (XML_SC_CTF_START + 22)
+#define CTF_SC_CELLSTYLE (XML_SC_CTF_START + 23)
+#define CTF_SC_VALIDATION (XML_SC_CTF_START + 24)
+#define CTF_SC_DIAGONALTLBR (XML_SC_CTF_START + 25)
+#define CTF_SC_DIAGONALTLBRWIDTH (XML_SC_CTF_START + 26)
+#define CTF_SC_DIAGONALBLTR (XML_SC_CTF_START + 27)
+#define CTF_SC_DIAGONALBLTRWIDTH (XML_SC_CTF_START + 28)
+#define CTF_SC_DIAGONALTLBRWIDTHS (XML_SC_CTF_START + 29)
+#define CTF_SC_DIAGONALBLTRWIDTHS (XML_SC_CTF_START + 30)
+
+#define CTF_SC_ROWHEIGHT (XML_SC_CTF_START + 50)
+#define CTF_SC_ROWOPTIMALHEIGHT (XML_SC_CTF_START + 51)
+#define CTF_SC_ROWBREAKBEFORE (XML_SC_CTF_START + 52)
+#define CTF_SC_ISVISIBLE (XML_SC_CTF_START + 53)
+
+#define CTF_SC_MASTERPAGENAME (XML_SC_CTF_START + 53)
+#define CTF_SC_HYPERLINK (XML_SC_CTF_START + 54)
+
+//ColumnStyles
+#define XML_SC_TYPE_BREAKBEFORE (XML_SC_TYPES_START + 50)
+
+class ScXMLExport;
+
+class ScXMLCellExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+protected:
+ /** Application-specific filter. By default do nothing. */
+ virtual void ContextFilter(
+ bool bEnableFoFontFamily,
+ ::std::vector< XMLPropertyState >& rProperties,
+ const css::uno::Reference<css::beans::XPropertySet >& rPropSet ) const override;
+public:
+ explicit ScXMLCellExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLCellExportPropertyMapper() override;
+ virtual void handleElementItem(
+ SvXMLExport& rExport,
+ const XMLPropertyState& rProperty,
+ SvXmlExportFlags nFlags,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+
+ /** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem(
+ comphelper::AttributeList& rAttrList,
+ const XMLPropertyState& rProperty,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+};
+
+class ScXMLRowExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+protected:
+ /** Application-specific filter. By default do nothing. */
+ virtual void ContextFilter(
+ bool bEnableFoFontFamily,
+ ::std::vector< XMLPropertyState >& rProperties,
+ const css::uno::Reference<css::beans::XPropertySet >& rPropSet ) const override;
+public:
+ explicit ScXMLRowExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLRowExportPropertyMapper() override;
+};
+
+class ScXMLColumnExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+public:
+ explicit ScXMLColumnExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLColumnExportPropertyMapper() override;
+
+ /** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem(
+ comphelper::AttributeList& rAttrList,
+ const XMLPropertyState& rProperty,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+};
+
+class ScXMLTableExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+protected:
+public:
+ explicit ScXMLTableExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLTableExportPropertyMapper() override;
+
+ /** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem(
+ comphelper::AttributeList& rAttrList,
+ const XMLPropertyState& rProperty,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+};
+
+class ScXMLAutoStylePoolP : public SvXMLAutoStylePoolP
+{
+ ScXMLExport& rScXMLExport;
+
+ virtual void exportStyleAttributes(
+ comphelper::AttributeList& rAttrList,
+ XmlStyleFamily nFamily,
+ const ::std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const override;
+
+ virtual void exportStyleContent(
+ const css::uno::Reference< css::xml::sax::XDocumentHandler > & rHandler,
+ XmlStyleFamily nFamily,
+ const ::std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const override;
+
+public:
+ explicit ScXMLAutoStylePoolP(ScXMLExport& rScXMLExport);
+ virtual ~ScXMLAutoStylePoolP() override;
+};
+
+class XMLScPropHdlFactory : public XMLPropertyHandlerFactory
+{
+public:
+ XMLScPropHdlFactory();
+ virtual ~XMLScPropHdlFactory() override;
+ virtual const XMLPropertyHandler* GetPropertyHandler( sal_Int32 nType ) const override;
+};
+
+class XmlScPropHdl_CellProtection : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_CellProtection() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_PrintContent : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_PrintContent() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_JustifyMethod : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_JustifyMethod() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_HoriJustify : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_HoriJustify() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_HoriJustifySource : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_HoriJustifySource() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_HoriJustifyRepeat : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_HoriJustifyRepeat() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_Orientation : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_Orientation() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_RotateAngle : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_RotateAngle() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_RotateReference : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_RotateReference() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_VertJustify : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_VertJustify() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_BreakBefore : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_BreakBefore() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_IsTextWrapped : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_IsTextWrapped() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_IsEqual : public XMLPropertyHandler
+{
+public:
+ virtual bool equals( const css::uno::Any& /* r1 */, const css::uno::Any& /* r2 */ ) const override { return true; }
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_Vertical : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_Vertical() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyli.cxx b/sc/source/filter/xml/xmlstyli.cxx
new file mode 100644
index 0000000000..7b5d8611a9
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyli.cxx
@@ -0,0 +1,1068 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlstyli.hxx"
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnumfi.hxx>
+#include <xmloff/XMLGraphicsDefaultStyle.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <comphelper/extract.hxx>
+#include <xmloff/xmlprcon.hxx>
+#include <xmloff/XMLComplexColorContext.hxx>
+#include "XMLTableHeaderFooterContext.hxx"
+#include "XMLConverter.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+#include <sheetdata.hxx>
+#include "xmlannoi.hxx"
+#include <textuno.hxx>
+#include <cellsuno.hxx>
+#include "xmlstyle.hxx"
+
+#include <docuno.hxx>
+#include <unonames.hxx>
+#include <document.hxx>
+#include <conditio.hxx>
+#include <rangelst.hxx>
+
+#include <xmloff/xmltypes.hxx>
+#include <xmloff/contextid.hxx>
+#include <xmloff/txtprmap.hxx>
+
+#define XML_LINE_LEFT 0
+#define XML_LINE_RIGHT 1
+#define XML_LINE_TOP 2
+#define XML_LINE_BOTTOM 3
+
+#define XML_LINE_TLBR 0
+#define XML_LINE_BLTR 1
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace xmloff::token;
+using namespace ::formula;
+
+using com::sun::star::uno::UNO_QUERY;
+ScXMLCellImportPropertyMapper::ScXMLCellImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImportP) :
+ SvXMLImportPropertyMapper( rMapper, rImportP )
+{
+}
+
+ScXMLCellImportPropertyMapper::~ScXMLCellImportPropertyMapper()
+{
+}
+
+void ScXMLCellImportPropertyMapper::finished(::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const
+{
+ static const sal_Int16 aPaddingCTF[4] = { CTF_SC_LEFTPADDING, CTF_SC_RIGHTPADDING,
+ CTF_SC_TOPPADDING, CTF_SC_BOTTOMPADDING };
+ static const sal_Int16 aBorderCTF[4] = { CTF_SC_LEFTBORDER, CTF_SC_RIGHTBORDER,
+ CTF_SC_TOPBORDER, CTF_SC_BOTTOMBORDER };
+
+ SvXMLImportPropertyMapper::finished(rProperties, nStartIndex, nEndIndex);
+ XMLPropertyState* pAllPaddingProperty(nullptr);
+ XMLPropertyState* pPadding[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pNewPadding[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pAllBorderProperty = nullptr;
+ XMLPropertyState* pBorders[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pNewBorders[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pAllBorderWidthProperty = nullptr;
+ XMLPropertyState* pBorderWidths[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pDiagBorders[2] = { nullptr };
+ XMLPropertyState* pOldDiagBorderWidths[2] = { nullptr }; // old attribute names without "s"
+ XMLPropertyState* pDiagBorderWidths[2] = { nullptr };
+
+ for (auto& rProperty : rProperties)
+ {
+ XMLPropertyState*property = &rProperty;
+ if (property->mnIndex != -1)
+ {
+ sal_Int16 nContextID = getPropertySetMapper()->GetEntryContextId(property->mnIndex);
+ switch (nContextID)
+ {
+ case CTF_SC_ALLPADDING : pAllPaddingProperty = property; break;
+ case CTF_SC_LEFTPADDING : pPadding[XML_LINE_LEFT] = property; break;
+ case CTF_SC_RIGHTPADDING : pPadding[XML_LINE_RIGHT] = property; break;
+ case CTF_SC_TOPPADDING : pPadding[XML_LINE_TOP] = property; break;
+ case CTF_SC_BOTTOMPADDING : pPadding[XML_LINE_BOTTOM] = property; break;
+ case CTF_SC_ALLBORDER : pAllBorderProperty = property; break;
+ case CTF_SC_LEFTBORDER : pBorders[XML_LINE_LEFT] = property; break;
+ case CTF_SC_RIGHTBORDER : pBorders[XML_LINE_RIGHT] = property; break;
+ case CTF_SC_TOPBORDER : pBorders[XML_LINE_TOP] = property; break;
+ case CTF_SC_BOTTOMBORDER : pBorders[XML_LINE_BOTTOM] = property; break;
+ case CTF_SC_ALLBORDERWIDTH : pAllBorderWidthProperty = property; break;
+ case CTF_SC_LEFTBORDERWIDTH : pBorderWidths[XML_LINE_LEFT] = property; break;
+ case CTF_SC_RIGHTBORDERWIDTH : pBorderWidths[XML_LINE_RIGHT] = property; break;
+ case CTF_SC_TOPBORDERWIDTH : pBorderWidths[XML_LINE_TOP] = property; break;
+ case CTF_SC_BOTTOMBORDERWIDTH : pBorderWidths[XML_LINE_BOTTOM] = property; break;
+ case CTF_SC_DIAGONALTLBR : pDiagBorders[XML_LINE_TLBR] = property; break;
+ case CTF_SC_DIAGONALBLTR : pDiagBorders[XML_LINE_BLTR] = property; break;
+ case CTF_SC_DIAGONALTLBRWIDTH : pOldDiagBorderWidths[XML_LINE_TLBR] = property; break;
+ case CTF_SC_DIAGONALTLBRWIDTHS : pDiagBorderWidths[XML_LINE_TLBR] = property; break;
+ case CTF_SC_DIAGONALBLTRWIDTH : pOldDiagBorderWidths[XML_LINE_BLTR] = property; break;
+ case CTF_SC_DIAGONALBLTRWIDTHS : pDiagBorderWidths[XML_LINE_BLTR] = property; break;
+ }
+ }
+ }
+ sal_uInt16 i;
+
+ // #i27594#; copy Value, but don't insert
+ if (pAllBorderWidthProperty)
+ pAllBorderWidthProperty->mnIndex = -1;
+ if (pAllBorderProperty)
+ pAllBorderProperty->mnIndex = -1;
+ if (pAllPaddingProperty)
+ pAllPaddingProperty->mnIndex = -1;
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (pAllPaddingProperty && !pPadding[i])
+ pNewPadding[i] = new XMLPropertyState(maPropMapper->FindEntryIndex(aPaddingCTF[i]), pAllPaddingProperty->maValue);
+ if (pAllBorderProperty && !pBorders[i])
+ {
+ pNewBorders[i] = new XMLPropertyState(maPropMapper->FindEntryIndex(aBorderCTF[i]), pAllBorderProperty->maValue);
+ pBorders[i] = pNewBorders[i];
+ }
+ if( !pBorderWidths[i] )
+ pBorderWidths[i] = pAllBorderWidthProperty;
+ else
+ pBorderWidths[i]->mnIndex = -1;
+ if( pBorders[i] )
+ {
+ table::BorderLine2 aBorderLine;
+ pBorders[i]->maValue >>= aBorderLine;
+ if( pBorderWidths[i] )
+ {
+ // Merge style:border-line-width values to fo:border values. Do
+ // not override fo:border line width or line style with an
+ // empty value!
+ table::BorderLine2 aBorderLineWidth;
+ pBorderWidths[i]->maValue >>= aBorderLineWidth;
+ aBorderLine.OuterLineWidth = aBorderLineWidth.OuterLineWidth;
+ aBorderLine.InnerLineWidth = aBorderLineWidth.InnerLineWidth;
+ aBorderLine.LineDistance = aBorderLineWidth.LineDistance;
+ pBorders[i]->maValue <<= aBorderLine;
+ }
+ }
+ }
+ for( i = 0; i < 2; ++i )
+ {
+ if( pDiagBorders[i] && ( pDiagBorderWidths[i] || pOldDiagBorderWidths[i] ) )
+ {
+ table::BorderLine2 aBorderLine;
+ pDiagBorders[i]->maValue >>= aBorderLine;
+ table::BorderLine2 aBorderLineWidth;
+ if (pDiagBorderWidths[i])
+ pDiagBorderWidths[i]->maValue >>= aBorderLineWidth; // prefer new attribute
+ else
+ pOldDiagBorderWidths[i]->maValue >>= aBorderLineWidth;
+ aBorderLine.OuterLineWidth = aBorderLineWidth.OuterLineWidth;
+ aBorderLine.InnerLineWidth = aBorderLineWidth.InnerLineWidth;
+ aBorderLine.LineDistance = aBorderLineWidth.LineDistance;
+ pDiagBorders[i]->maValue <<= aBorderLine;
+ if (pDiagBorderWidths[i])
+ pDiagBorderWidths[i]->mnIndex = -1;
+ if (pOldDiagBorderWidths[i])
+ pOldDiagBorderWidths[i]->mnIndex = -1; // reset mnIndex for old and new attribute if both are present
+ }
+ }
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (pNewPadding[i])
+ {
+ rProperties.push_back(*pNewPadding[i]);
+ delete pNewPadding[i];
+ }
+ if (pNewBorders[i])
+ {
+ rProperties.push_back(*pNewBorders[i]);
+ delete pNewBorders[i];
+ }
+ }
+}
+
+ScXMLRowImportPropertyMapper::ScXMLRowImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImportP) :
+ SvXMLImportPropertyMapper( rMapper, rImportP )
+{
+}
+
+ScXMLRowImportPropertyMapper::~ScXMLRowImportPropertyMapper()
+{
+}
+
+void ScXMLRowImportPropertyMapper::finished(::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const
+{
+ SvXMLImportPropertyMapper::finished(rProperties, nStartIndex, nEndIndex);
+ XMLPropertyState* pHeight(nullptr);
+ XMLPropertyState* pOptimalHeight(nullptr);
+ XMLPropertyState* pPageBreak(nullptr);
+ for (auto& rProperty : rProperties)
+ {
+ XMLPropertyState* property = &rProperty;
+ if (property->mnIndex != -1)
+ {
+ sal_Int16 nContextID = getPropertySetMapper()->GetEntryContextId(property->mnIndex);
+ switch (nContextID)
+ {
+ case CTF_SC_ROWHEIGHT : pHeight = property; break;
+ case CTF_SC_ROWOPTIMALHEIGHT : pOptimalHeight = property; break;
+ case CTF_SC_ROWBREAKBEFORE : pPageBreak = property; break;
+ }
+ }
+ }
+ if (pPageBreak)
+ {
+ if(!(::cppu::any2bool(pPageBreak->maValue)))
+ pPageBreak->mnIndex = -1;
+ }
+ if (pOptimalHeight)
+ {
+ if (::cppu::any2bool(pOptimalHeight->maValue))
+ {
+ if (pHeight)
+ {
+ // set the stored height, but keep "optimal" flag:
+ // pass the height value as OptimalHeight property (only allowed while loading!)
+ pOptimalHeight->maValue = pHeight->maValue;
+ pHeight->mnIndex = -1;
+ }
+ else
+ pOptimalHeight->mnIndex = -1;
+ }
+ }
+ else if (pHeight)
+ {
+ rProperties.emplace_back(maPropMapper->FindEntryIndex(CTF_SC_ROWOPTIMALHEIGHT), css::uno::Any(false));
+ }
+ // don't access pointers to rProperties elements after push_back!
+}
+
+namespace {
+
+class XMLTableCellPropsContext : public SvXMLPropertySetContext
+{
+ public:
+ XMLTableCellPropsContext(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ sal_uInt32 nFamily,
+ ::std::vector< XMLPropertyState > &rProps,
+ const rtl::Reference < SvXMLImportPropertyMapper > &rMap);
+
+ using SvXMLPropertySetContext::createFastChildContext;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ ::std::vector< XMLPropertyState > &rProperties,
+ const XMLPropertyState& rProp ) override;
+};
+
+}
+
+XMLTableCellPropsContext::XMLTableCellPropsContext(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ sal_uInt32 nFamily,
+ ::std::vector< XMLPropertyState > &rProps,
+ const rtl::Reference < SvXMLImportPropertyMapper > &rMap)
+ : SvXMLPropertySetContext( rImport, nElement, xAttrList, nFamily,
+ rProps, rMap )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableCellPropsContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ ::std::vector< XMLPropertyState > &rProperties,
+ const XMLPropertyState& rProperty)
+{
+ switch (mxMapper->getPropertySetMapper()->GetEntryContextId(rProperty.mnIndex))
+ {
+ case CTF_COMPLEX_COLOR:
+ {
+ return new XMLPropertyComplexColorContext(GetImport(), nElement, xAttrList, rProperty, rProperties);
+ }
+ break;
+ case CTF_SC_HYPERLINK:
+ {
+ OUString sURL;
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ if ( aIter.getToken() == XML_ELEMENT(XLINK, XML_HREF) )
+ sURL = aIter.toString();
+ else
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ if ( !sURL.isEmpty() )
+ {
+ XMLPropertyState aProp(rProperty);
+ aProp.maValue <<= sURL;
+ rProperties.push_back( aProp );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return SvXMLPropertySetContext::createFastChildContext(nElement, xAttrList, rProperties, rProperty);
+}
+
+namespace {
+
+class ScXMLMapContext : public SvXMLImportContext
+{
+ OUString msApplyStyle;
+ OUString msCondition;
+ OUString msBaseCell;
+
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+public:
+
+ ScXMLMapContext(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList );
+
+ ScCondFormatEntry* CreateConditionEntry();
+};
+
+}
+
+ScXMLMapContext::ScXMLMapContext(SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+ : SvXMLImportContext( rImport )
+{
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(STYLE, XML_CONDITION):
+ msCondition = sValue;
+ break;
+ case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME):
+ msApplyStyle = GetImport().GetStyleDisplayName( XmlStyleFamily::TABLE_CELL, sValue);
+ break;
+ case XML_ELEMENT(STYLE, XML_BASE_CELL_ADDRESS):
+ msBaseCell = sValue;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ }
+}
+
+ScCondFormatEntry* ScXMLMapContext::CreateConditionEntry()
+{
+ OUString aCondition, aConditionNmsp;
+ FormulaGrammar::Grammar eGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eGrammar, msCondition );
+ bool bHasNmsp = aCondition.getLength() < msCondition.getLength();
+
+ // parse a condition from the attribute string
+ ScXMLConditionParseResult aParseResult;
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, 0 );
+
+ if( !bHasNmsp )
+ {
+ // the attribute does not contain a namespace: try to find a namespace of an external grammar
+ FormulaGrammar::Grammar eNewGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eNewGrammar, aCondition, true );
+ if( eNewGrammar != FormulaGrammar::GRAM_EXTERNAL )
+ eGrammar = eNewGrammar;
+ }
+
+ ScConditionMode eMode = ScConditionEntry::GetModeFromApi(aParseResult.meOperator);
+ ScDocument* pDoc = GetScImport().GetDocument();
+
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, aParseResult.maOperand1, aParseResult.maOperand2, *pDoc, ScAddress(), msApplyStyle,
+ OUString(), OUString(), eGrammar, eGrammar);
+
+ pEntry->SetSrcString(msBaseCell);
+ return pEntry;
+}
+
+void XMLTableStyleContext::SetAttribute( sal_Int32 nElement,
+ const OUString& rValue )
+{
+ switch(nElement & TOKEN_MASK)
+ {
+ case XML_DATA_STYLE_NAME:
+ sDataStyleName = rValue;
+ break;
+ case XML_MASTER_PAGE_NAME:
+ sPageStyle = rValue;
+ break;
+ default:
+ XMLPropStyleContext::SetAttribute( nElement, rValue );
+ }
+}
+
+
+XMLTableStyleContext::XMLTableStyleContext( ScXMLImport& rImport,
+ XMLTableStylesContext& rStyles, XmlStyleFamily nFamily, bool bDefaultStyle ) :
+ XMLPropStyleContext( rImport, rStyles, nFamily, bDefaultStyle ),
+ pStyles(&rStyles),
+ nNumberFormat(-1),
+ nLastSheet(-1),
+ bParentSet(false),
+ mpCondFormat(nullptr),
+ mbDeleteCondFormat(true)
+{
+}
+
+XMLTableStyleContext::~XMLTableStyleContext()
+{
+ if(mbDeleteCondFormat)
+ delete mpCondFormat;
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableStyleContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ css::uno::Reference< css::xml::sax::XFastContextHandler > xContext;
+
+ if( nElement == XML_ELEMENT(STYLE, XML_MAP) )
+ {
+ if(!mpCondFormat)
+ mpCondFormat = new ScConditionalFormat( 0, GetScImport().GetDocument() );
+ ScXMLMapContext* pMapContext = new ScXMLMapContext(GetImport(), nElement, xAttrList);
+ xContext = pMapContext;
+ mpCondFormat->AddEntry(pMapContext->CreateConditionEntry());
+ }
+ else if ( nElement == XML_ELEMENT(STYLE, XML_TABLE_CELL_PROPERTIES) )
+ {
+ rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap =
+ GetStyles()->GetImportPropertyMapper(
+ GetFamily() );
+ if( xImpPrMap.is() )
+ xContext = new XMLTableCellPropsContext( GetImport(), nElement,
+ xAttrList,
+ XML_TYPE_PROP_TABLE_CELL,
+ GetProperties(),
+ xImpPrMap );
+ }
+
+ if (!xContext)
+ xContext = XMLPropStyleContext::createFastChildContext( nElement, xAttrList );
+ return xContext;
+}
+
+void XMLTableStyleContext::ApplyCondFormat( const uno::Sequence<table::CellRangeAddress>& xCellRanges )
+{
+ if(!mpCondFormat || GetScImport().HasNewCondFormatData())
+ return;
+
+ ScRangeList aRangeList;
+ for(const table::CellRangeAddress& aAddress : xCellRanges)
+ {
+ ScRange aRange( aAddress.StartColumn, aAddress.StartRow, aAddress.Sheet, aAddress.EndColumn, aAddress.EndRow, aAddress.Sheet );
+ aRangeList.Join( aRange );
+ }
+
+ ScDocument* pDoc = GetScImport().GetDocument();
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ ScConditionalFormatList* pFormatList = pDoc->GetCondFormList(nTab);
+ auto itr = std::find_if(pFormatList->begin(), pFormatList->end(),
+ [this](const std::unique_ptr<ScConditionalFormat>& rxFormat) { return rxFormat->EqualEntries(*mpCondFormat); });
+ if (itr != pFormatList->end())
+ {
+ ScRangeList& rRangeList = (*itr)->GetRangeList();
+ sal_uInt32 nCondId = (*itr)->GetKey();
+ size_t n = aRangeList.size();
+ for(size_t i = 0; i < n; ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ rRangeList.Join(rRange);
+ }
+
+ pDoc->AddCondFormatData( aRangeList, nTab, nCondId );
+ return;
+ }
+
+ if(mpCondFormat && mbDeleteCondFormat)
+ {
+ sal_uLong nIndex = pDoc->AddCondFormat(std::unique_ptr<ScConditionalFormat>(mpCondFormat), nTab );
+ mpCondFormat->SetKey(nIndex);
+ mpCondFormat->SetRange(aRangeList);
+
+ pDoc->AddCondFormatData( aRangeList, nTab, nIndex );
+ mbDeleteCondFormat = false;
+ }
+
+}
+
+void XMLTableStyleContext::FillPropertySet(
+ const uno::Reference< XPropertySet > & rPropSet )
+{
+ if (!IsDefaultStyle())
+ {
+ if (GetFamily() == XmlStyleFamily::TABLE_CELL)
+ {
+ if (!bParentSet)
+ {
+ AddProperty(CTF_SC_CELLSTYLE, uno::Any(GetImport().GetStyleDisplayName( XmlStyleFamily::TABLE_CELL, GetParentName() )));
+ bParentSet = true;
+ }
+ sal_Int32 nNumFmt = GetNumberFormat();
+ if (nNumFmt >= 0)
+ AddProperty(CTF_SC_NUMBERFORMAT, uno::Any(nNumFmt));
+ }
+ else if (GetFamily() == XmlStyleFamily::TABLE_TABLE)
+ {
+ if (!sPageStyle.isEmpty())
+ AddProperty(CTF_SC_MASTERPAGENAME, uno::Any(GetImport().GetStyleDisplayName( XmlStyleFamily::MASTER_PAGE, sPageStyle )));
+ }
+ }
+ XMLPropStyleContext::FillPropertySet(rPropSet);
+}
+
+void XMLTableStyleContext::SetDefaults()
+{
+ if ((GetFamily() == XmlStyleFamily::TABLE_CELL) && GetScImport().GetScModel())
+ {
+ rtl::Reference<ScModelObj> xMultiServiceFactory(GetScImport().GetScModel());
+ if (xMultiServiceFactory.is())
+ {
+ uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance("com.sun.star.sheet.Defaults"), uno::UNO_QUERY);
+ if (xProperties.is())
+ FillPropertySet(xProperties);
+ }
+ }
+}
+
+void XMLTableStyleContext::AddProperty(const sal_Int16 nContextID, const uno::Any& rValue)
+{
+ XMLPropertyState* property = FindProperty(nContextID);
+ if (property)
+ property->mnIndex = -1; // #i46996# remove old property, so it isn't double
+ sal_Int32 nIndex(pStyles->GetIndex(nContextID));
+ OSL_ENSURE(nIndex != -1, "Property not found in Map");
+ XMLPropertyState aPropState(nIndex, rValue);
+ GetProperties().push_back(aPropState); // has to be inserted in a sort order later
+}
+
+XMLPropertyState* XMLTableStyleContext::FindProperty(const sal_Int16 nContextID)
+{
+ XMLPropertyState* pRet = nullptr;
+ rtl::Reference < XMLPropertySetMapper > xPrMap;
+ rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap =
+ pStyles->GetImportPropertyMapper( GetFamily() );
+ OSL_ENSURE( xImpPrMap.is(), "There is the import prop mapper" );
+ if( xImpPrMap.is() )
+ xPrMap = xImpPrMap->getPropertySetMapper();
+ if( xPrMap.is() )
+ {
+ auto aIter = std::find_if(GetProperties().begin(), GetProperties().end(),
+ [&xPrMap, &nContextID](const XMLPropertyState& rProp) {
+ return rProp.mnIndex != -1 && xPrMap->GetEntryContextId(rProp.mnIndex) == nContextID;
+ });
+ if (aIter != GetProperties().end())
+ pRet = &(*aIter);
+ }
+ return pRet;
+}
+
+sal_Int32 XMLTableStyleContext::GetNumberFormat()
+{
+ if (nNumberFormat < 0 && !sDataStyleName.isEmpty())
+ {
+ const SvXMLNumFormatContext* pStyle = static_cast<const SvXMLNumFormatContext*>(
+ pStyles->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, sDataStyleName, true));
+
+ if (!pStyle)
+ {
+ XMLTableStylesContext* pMyStyles = static_cast<XMLTableStylesContext*>(GetScImport().GetStyles());
+ if (pMyStyles)
+ pStyle = static_cast<const SvXMLNumFormatContext*>(
+ pMyStyles->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, sDataStyleName, true));
+ else
+ {
+ OSL_FAIL("not possible to get style");
+ }
+ }
+ if (pStyle)
+ nNumberFormat = const_cast<SvXMLNumFormatContext*>(pStyle)->GetKey();
+ }
+ return nNumberFormat;
+}
+
+SvXMLStyleContext *XMLTableStylesContext::CreateStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pStyle;
+ // use own wrapper for text and paragraph, to record style usage
+ if (nFamily == XmlStyleFamily::TEXT_PARAGRAPH || nFamily == XmlStyleFamily::TEXT_TEXT)
+ pStyle = new ScCellTextStyleContext( GetImport(),*this, nFamily );
+ else if (nFamily == XmlStyleFamily::SD_GRAPHICS_ID)
+ pStyle = new ScShapeStyleContext( GetImport(), *this, nFamily );
+ else
+ pStyle = SvXMLStylesContext::CreateStyleStyleChildContext(
+ nFamily, nElement, xAttrList );
+
+ if (!pStyle)
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_CELL:
+ case XmlStyleFamily::TABLE_COLUMN:
+ case XmlStyleFamily::TABLE_ROW:
+ case XmlStyleFamily::TABLE_TABLE:
+ pStyle = new XMLTableStyleContext( GetScImport(), *this, nFamily );
+ break;
+ default: break;
+ }
+ }
+
+ return pStyle;
+}
+
+SvXMLStyleContext *XMLTableStylesContext::CreateDefaultStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pStyle(SvXMLStylesContext::CreateDefaultStyleStyleChildContext( nFamily, nElement,
+ xAttrList ));
+ if (!pStyle)
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_CELL:
+ pStyle = new XMLTableStyleContext( GetScImport(), *this, nFamily, true);
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ pStyle = new XMLGraphicsDefaultStyle( GetScImport(), *this);
+ break;
+ default: break;
+ }
+ }
+
+ return pStyle;
+}
+
+constexpr OUStringLiteral gsCellStyleServiceName(u"com.sun.star.style.CellStyle");
+constexpr OUStringLiteral gsGraphicStyleServiceName(u"com.sun.star.style.GraphicStyle");
+
+XMLTableStylesContext::XMLTableStylesContext( SvXMLImport& rImport,
+ const bool bTempAutoStyles )
+ : SvXMLStylesContext( rImport )
+ , nNumberFormatIndex(-1)
+ , nConditionalFormatIndex(-1)
+ , nCellStyleIndex(-1)
+ , nMasterPageNameIndex(-1)
+ , bAutoStyles(bTempAutoStyles)
+{
+}
+
+XMLTableStylesContext::~XMLTableStylesContext()
+{
+}
+
+void XMLTableStylesContext::endFastElement(sal_Int32 )
+{
+ if (bAutoStyles)
+ GetImport().GetTextImport()->SetAutoStyles( this );
+ else
+ GetScImport().InsertStyles();
+}
+
+rtl::Reference < SvXMLImportPropertyMapper >
+ XMLTableStylesContext::GetImportPropertyMapper(
+ XmlStyleFamily nFamily ) const
+{
+ rtl::Reference < SvXMLImportPropertyMapper > xMapper(SvXMLStylesContext::GetImportPropertyMapper(nFamily));
+
+ if (!xMapper.is())
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_CELL:
+ {
+ if( !xCellImpPropMapper.is() )
+ {
+ const_cast<XMLTableStylesContext *>(this)->xCellImpPropMapper =
+ new ScXMLCellImportPropertyMapper( GetScImport().GetCellStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xCellImpPropMapper->ChainImportMapper(XMLTextImportHelper::CreateParaExtPropMapper(const_cast<SvXMLImport&>(GetImport())));
+ }
+ xMapper = xCellImpPropMapper;
+ }
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ {
+ if( !xColumnImpPropMapper.is() )
+ const_cast<XMLTableStylesContext *>(this)->xColumnImpPropMapper =
+ new SvXMLImportPropertyMapper( GetScImport().GetColumnStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xMapper = xColumnImpPropMapper;
+ }
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ {
+ if( !xRowImpPropMapper.is() )
+ const_cast<XMLTableStylesContext *>(this)->xRowImpPropMapper =
+ new ScXMLRowImportPropertyMapper( GetScImport().GetRowStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xMapper = xRowImpPropMapper;
+ }
+ break;
+ case XmlStyleFamily::TABLE_TABLE:
+ {
+ if( !xTableImpPropMapper.is() )
+ const_cast<XMLTableStylesContext *>(this)->xTableImpPropMapper =
+ new SvXMLImportPropertyMapper( GetScImport().GetTableStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xMapper = xTableImpPropMapper;
+ }
+ break;
+ default: break;
+ }
+ }
+
+ return xMapper;
+}
+
+uno::Reference < XNameContainer >
+ XMLTableStylesContext::GetStylesContainer( XmlStyleFamily nFamily ) const
+{
+ uno::Reference < XNameContainer > xStyles(SvXMLStylesContext::GetStylesContainer(nFamily));
+ if (!xStyles.is())
+ {
+ OUString sName;
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_TABLE:
+ {
+ if( xTableStyles.is() )
+ xStyles.set(xTableStyles);
+ else
+ sName = "TableStyles";
+ }
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ {
+ if( xCellStyles.is() )
+ xStyles.set(xCellStyles);
+ else
+ sName = "CellStyles";
+ }
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ {
+ if( xColumnStyles.is() )
+ xStyles.set(xColumnStyles);
+ else
+ sName = "ColumnStyles";
+ }
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ {
+ if( xRowStyles.is() )
+ xStyles.set(xRowStyles);
+ else
+ sName = "RowStyles";
+ }
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ {
+ if( xGraphicStyles.is() )
+ xStyles.set(xGraphicStyles);
+ else
+ sName = "GraphicStyles";
+ }
+ break;
+ default: break;
+ }
+ if( !xStyles.is() && !sName.isEmpty() && GetScImport().GetModel().is() )
+ {
+ ScModelObj* xFamiliesSupp( GetScImport().GetScModel() );
+ uno::Reference< XNameAccess > xFamilies(xFamiliesSupp->getStyleFamilies());
+
+ try
+ {
+ xStyles.set(xFamilies->getByName( sName ), uno::UNO_QUERY);
+ }
+ catch ( uno::Exception& )
+ {
+ // #i97680# Named table/column/row styles aren't supported, getByName will throw an exception.
+ // For better interoperability, these styles should then be handled as automatic styles.
+ // For now, NULL is returned (and the style is ignored).
+ }
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_TABLE:
+ const_cast<XMLTableStylesContext *>(this)->xTableStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ const_cast<XMLTableStylesContext *>(this)->xCellStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ const_cast<XMLTableStylesContext *>(this)->xColumnStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ const_cast<XMLTableStylesContext *>(this)->xRowStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ const_cast<XMLTableStylesContext *>(this)->xGraphicStyles.set(xStyles);
+ break;
+ default: break;
+ }
+ }
+ }
+
+ return xStyles;
+}
+
+OUString XMLTableStylesContext::GetServiceName( XmlStyleFamily nFamily ) const
+{
+ OUString sServiceName(SvXMLStylesContext::GetServiceName(nFamily));
+ if (sServiceName.isEmpty())
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_COLUMN:
+ sServiceName = XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_NAME;
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ sServiceName = XML_STYLE_FAMILY_TABLE_ROW_STYLES_NAME;
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ sServiceName = gsCellStyleServiceName;
+ break;
+ case XmlStyleFamily::TABLE_TABLE:
+ sServiceName = XML_STYLE_FAMILY_TABLE_TABLE_STYLES_NAME;
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ sServiceName = gsGraphicStyleServiceName;
+ break;
+ default: break;
+ }
+ }
+ return sServiceName;
+}
+
+sal_Int32 XMLTableStylesContext::GetIndex(const sal_Int16 nContextID)
+{
+ if (nContextID == CTF_SC_CELLSTYLE)
+ {
+ if (nCellStyleIndex == -1)
+ nCellStyleIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_CELL)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nCellStyleIndex;
+ }
+ else if (nContextID == CTF_SC_NUMBERFORMAT)
+ {
+ if (nNumberFormatIndex == -1)
+ nNumberFormatIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_CELL)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nNumberFormatIndex;
+ }
+ else if (nContextID == CTF_SC_IMPORT_MAP)
+ {
+ if (nConditionalFormatIndex == -1)
+ nConditionalFormatIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_CELL)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nConditionalFormatIndex;
+ }
+ else if (nContextID == CTF_SC_MASTERPAGENAME)
+ {
+ if (nMasterPageNameIndex == -1)
+ nMasterPageNameIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_TABLE)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nMasterPageNameIndex;
+ }
+ else
+ return -1;
+}
+
+
+bool ScXMLMasterStylesContext::InsertStyleFamily( XmlStyleFamily ) const
+{
+ return true;
+}
+
+ScXMLMasterStylesContext::ScXMLMasterStylesContext( SvXMLImport& rImport ) :
+ SvXMLStylesContext( rImport )
+{
+}
+
+ScXMLMasterStylesContext::~ScXMLMasterStylesContext()
+{
+}
+
+SvXMLStyleContext *ScXMLMasterStylesContext::CreateStyleChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pContext(nullptr);
+
+ if( nElement == XML_ELEMENT(STYLE, XML_MASTER_PAGE) &&
+ InsertStyleFamily( XmlStyleFamily::MASTER_PAGE ) )
+ pContext = new ScMasterPageContext(
+ GetImport(), nElement, xAttrList,
+ !GetImport().GetTextImport()->IsInsertMode() );
+
+ // any other style will be ignored here!
+
+ return pContext;
+}
+
+SvXMLStyleContext *ScXMLMasterStylesContext::CreateStyleStyleChildContext(
+ XmlStyleFamily /* nFamily */,
+ sal_Int32 /* nElement */,
+ const uno::Reference< XFastAttributeList > & /* xAttrList */ )
+{
+ return nullptr;
+}
+
+void ScXMLMasterStylesContext::endFastElement(sal_Int32 )
+{
+ FinishStyles(true);
+}
+
+
+ScMasterPageContext::ScMasterPageContext( SvXMLImport& rImport,
+ sal_Int32 nElement,
+ const uno::Reference< XFastAttributeList > & xAttrList,
+ bool bOverwrite ) :
+ XMLTextMasterPageContext( rImport, nElement, xAttrList, bOverwrite ),
+ bContainsRightHeader(false),
+ bContainsRightFooter(false)
+{
+}
+
+ScMasterPageContext::~ScMasterPageContext()
+{
+}
+
+SvXMLImportContext *ScMasterPageContext::CreateHeaderFooterContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ const bool bFooter,
+ const bool bLeft,
+ const bool bFirst )
+{
+ if (!bLeft)
+ {
+ if (bFooter)
+ bContainsRightFooter = true;
+ else
+ bContainsRightHeader = true;
+ }
+ if (!xPropSet.is())
+ xPropSet.set(GetStyle(), UNO_QUERY );
+ return new XMLTableHeaderFooterContext( GetImport(),
+ nElement,
+ xAttrList,
+ xPropSet,
+ bFooter, bLeft, bFirst );
+}
+
+void ScMasterPageContext::ClearContent(const OUString& rContent)
+{
+ if (!xPropSet.is())
+ xPropSet.set(GetStyle(), UNO_QUERY );
+
+ if (xPropSet.is())
+ {
+ uno::Reference < sheet::XHeaderFooterContent > xHeaderFooterContent(xPropSet->getPropertyValue( rContent ), uno::UNO_QUERY);
+ if (xHeaderFooterContent.is())
+ {
+ xHeaderFooterContent->getLeftText()->setString("");
+ xHeaderFooterContent->getCenterText()->setString("");
+ xHeaderFooterContent->getRightText()->setString("");
+ xPropSet->setPropertyValue( rContent, uno::Any(xHeaderFooterContent) );
+ }
+ }
+}
+
+void ScMasterPageContext::Finish( bool bOverwrite )
+{
+ XMLTextMasterPageContext::Finish(bOverwrite);
+ if (!bContainsRightFooter)
+ ClearContent(SC_UNO_PAGE_RIGHTFTRCON);
+ if (!bContainsRightHeader)
+ ClearContent(SC_UNO_PAGE_RIGHTHDRCON);
+}
+
+ScCellTextStyleContext::ScCellTextStyleContext( SvXMLImport& rImport,
+ SvXMLStylesContext& rStyles, XmlStyleFamily nFamily ) :
+ XMLTextStyleContext( rImport, rStyles, nFamily, false/*bDefaultStyle*/ ),
+ nLastSheet(-1)
+{
+}
+
+ScCellTextStyleContext::~ScCellTextStyleContext()
+{
+}
+
+void ScCellTextStyleContext::FillPropertySet( const uno::Reference<beans::XPropertySet>& xPropSet )
+{
+ XMLTextStyleContext::FillPropertySet( xPropSet );
+
+ ScXMLImport& rXMLImport = GetScImport();
+
+ ScCellTextCursor* pCellImp = comphelper::getFromUnoTunnel<ScCellTextCursor>( xPropSet );
+ if (pCellImp)
+ {
+ ScAddress aPos = pCellImp->GetCellObj().GetPosition();
+ if ( aPos.Tab() != nLastSheet )
+ {
+ ESelection aSel = pCellImp->GetSelection();
+
+ ScSheetSaveData* pSheetData = GetScImport().GetScModel()->GetSheetSaveData();
+ pSheetData->AddTextStyle( GetName(), aPos, aSel );
+
+ nLastSheet = aPos.Tab();
+ }
+ }
+ else if ( rXMLImport.GetTables().GetCurrentSheet() != nLastSheet )
+ {
+ ScDrawTextCursor* pDrawImp = comphelper::getFromUnoTunnel<ScDrawTextCursor>( xPropSet );
+ if (pDrawImp)
+ {
+ XMLTableShapeImportHelper* pTableShapeImport = static_cast<XMLTableShapeImportHelper*>(GetScImport().GetShapeImport().get());
+ ScXMLAnnotationContext* pAnnotationContext = pTableShapeImport->GetAnnotationContext();
+ if (pAnnotationContext)
+ {
+ pAnnotationContext->AddContentStyle( GetFamily(), GetName(), pDrawImp->GetSelection() );
+ nLastSheet = rXMLImport.GetTables().GetCurrentSheet();
+ }
+ }
+
+ // if it's a different shape, BlockSheet is called from XMLTableShapeImportHelper::finishShape
+ // formatted text in page headers/footers can be ignored
+ }
+}
+
+void ScShapeStyleContext::Finish(bool bOverwrite)
+{
+ // set parent styles
+ XMLPropStyleContext::Finish(bOverwrite);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyli.hxx b/sc/source/filter/xml/xmlstyli.hxx
new file mode 100644
index 0000000000..e977b84e0e
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyli.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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <vector>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/prstylei.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <xmloff/XMLShapeStyleContext.hxx>
+#include <xmloff/XMLTextMasterPageContext.hxx>
+#include <xmloff/txtstyli.hxx>
+#include "xmlimprt.hxx"
+
+class ScConditionalFormat;
+
+class ScXMLCellImportPropertyMapper : public SvXMLImportPropertyMapper
+{
+protected:
+
+public:
+
+ ScXMLCellImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImport);
+ virtual ~ScXMLCellImportPropertyMapper() override;
+
+ /** This method is called when all attributes have been processed. It may be used to remove items that are incomplete */
+ virtual void finished(
+ ::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const override;
+};
+
+class ScXMLRowImportPropertyMapper : public SvXMLImportPropertyMapper
+{
+protected:
+
+public:
+
+ ScXMLRowImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImport);
+ virtual ~ScXMLRowImportPropertyMapper() override;
+
+ /** This method is called when all attributes have been processed. It may be used to remove items that are incomplete */
+ virtual void finished(
+ ::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const override;
+};
+
+class XMLTableStylesContext;
+
+class XMLTableStyleContext : public XMLPropStyleContext
+{
+ OUString sDataStyleName;
+ OUString sPageStyle;
+ XMLTableStylesContext* pStyles;
+ sal_Int32 nNumberFormat;
+ SCTAB nLastSheet;
+ bool bParentSet;
+ ScConditionalFormat* mpCondFormat;
+ bool mbDeleteCondFormat;
+
+ const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+protected:
+
+ virtual void SetAttribute( sal_Int32 nElement,
+ const OUString& rValue ) override;
+
+public:
+
+ XMLTableStyleContext( ScXMLImport& rImport,
+ XMLTableStylesContext& rStyles, XmlStyleFamily nFamily, bool bDefaultStyle = false );
+ virtual ~XMLTableStyleContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+ virtual void FillPropertySet(const css::uno::Reference<
+ css::beans::XPropertySet > & rPropSet ) override;
+
+ virtual void SetDefaults() override;
+
+ void AddProperty(sal_Int16 nContextID, const css::uno::Any& aValue);
+ XMLPropertyState* FindProperty(const sal_Int16 nContextID);
+
+ sal_Int32 GetNumberFormat();// { return nNumberFormat; }
+
+ SCTAB GetLastSheet() const { return nLastSheet; }
+ void SetLastSheet(SCTAB nNew) { nLastSheet = nNew; }
+
+ void ApplyCondFormat( const css::uno::Sequence<css::table::CellRangeAddress>& xCellRanges );
+
+private:
+ using XMLPropStyleContext::SetStyle;
+};
+
+class XMLTableStylesContext : public SvXMLStylesContext
+{
+ css::uno::Reference< css::container::XNameContainer > xCellStyles;
+ css::uno::Reference< css::container::XNameContainer > xColumnStyles;
+ css::uno::Reference< css::container::XNameContainer > xRowStyles;
+ css::uno::Reference< css::container::XNameContainer > xTableStyles;
+ css::uno::Reference< css::container::XNameContainer > xGraphicStyles;
+ sal_Int32 nNumberFormatIndex;
+ sal_Int32 nConditionalFormatIndex;
+ sal_Int32 nCellStyleIndex;
+ sal_Int32 nMasterPageNameIndex;
+ bool bAutoStyles;
+
+ rtl::Reference < SvXMLImportPropertyMapper > xCellImpPropMapper;
+ rtl::Reference < SvXMLImportPropertyMapper > xColumnImpPropMapper;
+ rtl::Reference < SvXMLImportPropertyMapper > xRowImpPropMapper;
+ rtl::Reference < SvXMLImportPropertyMapper > xTableImpPropMapper;
+
+ const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+protected:
+
+ // Create a style context.
+ using SvXMLStylesContext::CreateStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateStyleStyleChildContext(
+ XmlStyleFamily nFamily,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ using SvXMLStylesContext::CreateDefaultStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateDefaultStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+public:
+
+ XMLTableStylesContext( SvXMLImport& rImport, bool bAutoStyles );
+ virtual ~XMLTableStylesContext() override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ virtual rtl::Reference < SvXMLImportPropertyMapper > GetImportPropertyMapper(
+ XmlStyleFamily nFamily ) const override;
+ virtual css::uno::Reference< css::container::XNameContainer >
+ GetStylesContainer( XmlStyleFamily nFamily ) const override;
+ virtual OUString GetServiceName( XmlStyleFamily nFamily ) const override;
+
+ sal_Int32 GetIndex(const sal_Int16 nContextID);
+};
+
+class ScXMLMasterStylesContext : public SvXMLStylesContext
+{
+protected:
+ virtual SvXMLStyleContext *CreateStyleChildContext( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ using SvXMLStylesContext::CreateStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateStyleStyleChildContext( XmlStyleFamily nFamily,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override;
+
+public:
+
+ ScXMLMasterStylesContext( SvXMLImport& rImport );
+
+ virtual ~ScXMLMasterStylesContext() override;
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class ScMasterPageContext : public XMLTextMasterPageContext
+{
+ css::uno::Reference<css::beans::XPropertySet> xPropSet;
+ bool bContainsRightHeader;
+ bool bContainsRightFooter;
+
+ void ClearContent(const OUString& rContent);
+public:
+
+
+ ScMasterPageContext( SvXMLImport& rImport, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ bool bOverwrite );
+ virtual ~ScMasterPageContext() override;
+
+ virtual SvXMLImportContext *CreateHeaderFooterContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ const bool bFooter,
+ const bool bLeft,
+ const bool bFirst ) override;
+
+ virtual void Finish( bool bOverwrite ) override;
+};
+
+class ScCellTextStyleContext : public XMLTextStyleContext
+{
+ sal_Int32 nLastSheet;
+
+ const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+public:
+ ScCellTextStyleContext( SvXMLImport& rImport,
+ SvXMLStylesContext& rStyles, XmlStyleFamily nFamily );
+ virtual ~ScCellTextStyleContext() override;
+
+ // override FillPropertySet to store style information
+ virtual void FillPropertySet(
+ const css::uno::Reference< css::beans::XPropertySet > & rPropSet ) override;
+};
+
+class ScShapeStyleContext : public XMLShapeStyleContext
+{
+ using XMLShapeStyleContext::XMLShapeStyleContext;
+ void Finish(bool bOverwrite) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsubti.cxx b/sc/source/filter/xml/xmlsubti.cxx
new file mode 100644
index 0000000000..7e5083f2a3
--- /dev/null
+++ b/sc/source/filter/xml/xmlsubti.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlsubti.hxx"
+#include "xmlstyli.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+#include "XMLConverter.hxx"
+#include <docuno.hxx>
+#include "XMLStylesImportHelper.hxx"
+#include <sheetdata.hxx>
+#include <tabprotection.hxx>
+#include <tokenarray.hxx>
+#include <documentimport.hxx>
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+
+using namespace com::sun::star;
+
+ScXMLTabProtectionData::ScXMLTabProtectionData() :
+ meHash1(PASSHASH_SHA1),
+ meHash2(PASSHASH_UNSPECIFIED),
+ mbProtected(false),
+ mbSelectProtectedCells(true),
+ mbSelectUnprotectedCells(true),
+ mbInsertColumns(false),
+ mbInsertRows(false),
+ mbDeleteColumns(false),
+ mbDeleteRows(false)
+{
+}
+
+ScMyTables::ScMyTables(ScXMLImport& rTempImport)
+ : rImport(rTempImport),
+ aFixupOLEs(rTempImport),
+ maCurrentCellPos(ScAddress::INITIALIZE_INVALID),
+ nCurrentColCount(0),
+ nCurrentDrawPage( -1 ),
+ nCurrentXShapes( -1 )
+{
+}
+
+ScMyTables::~ScMyTables()
+{
+}
+
+namespace {
+
+uno::Reference<sheet::XSpreadsheet> getCurrentSheet(const uno::Reference<frame::XModel>& xModel, SCTAB nSheet)
+{
+ uno::Reference<sheet::XSpreadsheet> xSheet;
+ uno::Reference<sheet::XSpreadsheetDocument> xSpreadDoc(xModel, uno::UNO_QUERY);
+ if (!xSpreadDoc.is())
+ return xSheet;
+
+ uno::Reference <sheet::XSpreadsheets> xSheets(xSpreadDoc->getSheets());
+ if (!xSheets.is())
+ return xSheet;
+
+ uno::Reference <container::XIndexAccess> xIndex(xSheets, uno::UNO_QUERY);
+ if (!xIndex.is())
+ return xSheet;
+
+ xSheet.set(xIndex->getByIndex(nSheet), uno::UNO_QUERY);
+ return xSheet;
+}
+
+}
+
+void ScMyTables::NewSheet(const OUString& sTableName, const OUString& sStyleName,
+ const ScXMLTabProtectionData& rProtectData)
+{
+ if (!rImport.GetModel().is())
+ return;
+
+ nCurrentColCount = 0;
+ sCurrentSheetName = sTableName;
+ //reset cols and rows for new sheet, but increment tab
+ maCurrentCellPos.SetCol(-1);
+ maCurrentCellPos.SetRow(-1);
+ maCurrentCellPos.SetTab(maCurrentCellPos.Tab() + 1);
+
+ maProtectionData = rProtectData;
+ ScDocument *pDoc = ScXMLConverter::GetScDocument(rImport.GetModel());
+
+ // The document contains one sheet when created. So for the first
+ // sheet, we only need to set its name.
+ if (maCurrentCellPos.Tab() > 0)
+ pDoc->AppendTabOnLoad(sTableName);
+ else
+ pDoc->SetTabNameOnLoad(maCurrentCellPos.Tab(), sTableName);
+
+ xCurrentSheet = getCurrentSheet(rImport.GetModel(), maCurrentCellPos.Tab());
+ if (xCurrentSheet.is())
+ {
+ // We need to set the current cell range here regardless of
+ // presence of style name.
+ SetTableStyle(sStyleName);
+ }
+}
+
+void ScMyTables::SetTableStyle(const OUString& sStyleName)
+{
+ //these uno calls are a bit difficult to remove, XMLTableStyleContext::FillPropertySet uses
+ //SvXMLImportPropertyMapper::FillPropertySet
+ if ( sStyleName.isEmpty() )
+ return;
+
+ // #i57869# All table style properties for all sheets are now applied here,
+ // before importing the contents.
+ // This is needed for the background color.
+ // Sheet visibility has special handling in ScDocFunc::SetTableVisible to
+ // allow hiding the first sheet.
+ // RTL layout is only remembered, not actually applied, so the shapes can
+ // be loaded before mirroring.
+
+ if ( !xCurrentSheet.is() )
+ return;
+
+ uno::Reference <beans::XPropertySet> xProperties(xCurrentSheet, uno::UNO_QUERY);
+ if ( !xProperties.is() )
+ return;
+
+ XMLTableStylesContext *pStyles = static_cast<XMLTableStylesContext *>(rImport.GetAutoStyles());
+ if ( pStyles )
+ {
+ XMLTableStyleContext* pStyle = const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_TABLE, sStyleName, true)));
+ if ( pStyle )
+ {
+ pStyle->FillPropertySet(xProperties);
+
+ ScSheetSaveData* pSheetData = rImport.GetScModel()->GetSheetSaveData();
+ pSheetData->AddTableStyle( sStyleName, ScAddress( 0, 0, maCurrentCellPos.Tab() ) );
+ }
+ }
+}
+
+void ScMyTables::AddRow()
+{
+ maCurrentCellPos.SetRow(maCurrentCellPos.Row() + 1);
+ maCurrentCellPos.SetCol(-1); //reset columns for new row
+}
+
+void ScMyTables::SetRowStyle(const OUString& rCellStyleName)
+{
+ rImport.GetStylesImportHelper()->SetRowStyle(rCellStyleName);
+}
+
+void ScMyTables::AddColumn(bool bIsCovered)
+{
+ maCurrentCellPos.SetCol( maCurrentCellPos.Col() + 1 );
+ //here only need to set column style if this is the first row and
+ //the cell is not covered.
+ if(maCurrentCellPos.Row() == 0 && !bIsCovered)
+ rImport.GetStylesImportHelper()->InsertCol(maCurrentCellPos.Col(), maCurrentCellPos.Tab());
+}
+
+void ScMyTables::DeleteTable()
+{
+ ScXMLImport::MutexGuard aGuard(rImport);
+
+ rImport.GetStylesImportHelper()->SetStylesToRanges();
+ rImport.SetStylesToRangesFinished();
+
+ maMatrixRangeList.RemoveAll();
+
+ if (!(rImport.GetDocument() && maProtectionData.mbProtected))
+ return;
+
+ uno::Sequence<sal_Int8> aHash;
+ ::comphelper::Base64::decode(aHash, maProtectionData.maPassword);
+
+ ScTableProtection aProtect;
+ aProtect.setProtected(maProtectionData.mbProtected);
+ aProtect.setPasswordHash(aHash, maProtectionData.meHash1, maProtectionData.meHash2);
+ aProtect.setOption(ScTableProtection::SELECT_LOCKED_CELLS, maProtectionData.mbSelectProtectedCells);
+ aProtect.setOption(ScTableProtection::SELECT_UNLOCKED_CELLS, maProtectionData.mbSelectUnprotectedCells);
+ aProtect.setOption(ScTableProtection::INSERT_COLUMNS, maProtectionData.mbInsertColumns);
+ aProtect.setOption(ScTableProtection::INSERT_ROWS, maProtectionData.mbInsertRows);
+ aProtect.setOption(ScTableProtection::DELETE_COLUMNS, maProtectionData.mbDeleteColumns);
+ aProtect.setOption(ScTableProtection::DELETE_ROWS, maProtectionData.mbDeleteRows);
+ rImport.GetDocument()->SetTabProtection(maCurrentCellPos.Tab(), &aProtect);
+}
+
+void ScMyTables::AddColStyle(const sal_Int32 nRepeat, const OUString& rCellStyleName)
+{
+ rImport.GetStylesImportHelper()->AddColumnStyle(rCellStyleName, nCurrentColCount, nRepeat);
+ nCurrentColCount += nRepeat;
+ SAL_WARN_IF(nCurrentColCount > rImport.GetDocument()->GetSheetLimits().GetMaxColCount(),
+ "sc", "more columns than fit into SCCOL");
+ nCurrentColCount = std::min<sal_Int32>( nCurrentColCount, rImport.GetDocument()->GetSheetLimits().GetMaxColCount() );
+}
+
+uno::Reference< drawing::XDrawPage > const & ScMyTables::GetCurrentXDrawPage()
+{
+ if( (maCurrentCellPos.Tab() != nCurrentDrawPage) || !xDrawPage.is() )
+ {
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier( xCurrentSheet, uno::UNO_QUERY );
+ if( xDrawPageSupplier.is() )
+ xDrawPage.set(xDrawPageSupplier->getDrawPage());
+ nCurrentDrawPage = sal::static_int_cast<sal_Int16>(maCurrentCellPos.Tab());
+ }
+ return xDrawPage;
+}
+
+uno::Reference< drawing::XShapes > const & ScMyTables::GetCurrentXShapes()
+{
+ if( (maCurrentCellPos.Tab() != nCurrentXShapes) || !xShapes.is() )
+ {
+ xShapes = GetCurrentXDrawPage();
+ rImport.GetShapeImport()->startPage(xShapes);
+ rImport.GetShapeImport()->pushGroupForPostProcessing ( xShapes );
+ nCurrentXShapes = sal::static_int_cast<sal_Int16>(maCurrentCellPos.Tab());
+ }
+ return xShapes;
+}
+
+bool ScMyTables::HasDrawPage() const
+{
+ return (maCurrentCellPos.Tab() == nCurrentDrawPage) && xDrawPage.is();
+}
+
+bool ScMyTables::HasXShapes() const
+{
+ return (maCurrentCellPos.Tab() == nCurrentXShapes) && xShapes.is();
+}
+
+void ScMyTables::AddOLE(const uno::Reference <drawing::XShape>& rShape,
+ const OUString &rRangeList)
+{
+ aFixupOLEs.AddOLE(rShape, rRangeList);
+}
+
+void ScMyTables::AddMatrixRange(
+ const SCCOL nStartColumn, const SCROW nStartRow, const SCCOL nEndColumn, const SCROW nEndRow,
+ const OUString& rFormula, const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar)
+{
+ OSL_ENSURE(nEndRow >= nStartRow, "wrong row order");
+ OSL_ENSURE(nEndColumn >= nStartColumn, "wrong column order");
+ ScRange aScRange(
+ nStartColumn, nStartRow, maCurrentCellPos.Tab(),
+ nEndColumn, nEndRow, maCurrentCellPos.Tab()
+ );
+
+ maMatrixRangeList.push_back(aScRange);
+
+ ScDocumentImport& rDoc = rImport.GetDoc();
+ ScTokenArray aCode(rDoc.getDoc());
+ aCode.AssignXMLString( rFormula,
+ ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString()));
+ rDoc.setMatrixCells(aScRange, aCode, eGrammar);
+ rDoc.getDoc().IncXMLImportedFormulaCount( rFormula.getLength() );
+}
+
+bool ScMyTables::IsPartOfMatrix(const ScAddress& rScAddress) const
+{
+ if (!maMatrixRangeList.empty())
+ return maMatrixRangeList.Contains(rScAddress);
+ return false;
+}
+
+SCCOL ScMyTables::GetCurrentColCount() const
+{
+ return std::min<sal_Int32>(nCurrentColCount, rImport.GetDocument()->MaxCol());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsubti.hxx b/sc/source/filter/xml/xmlsubti.hxx
new file mode 100644
index 0000000000..b0c6e74572
--- /dev/null
+++ b/sc/source/filter/xml/xmlsubti.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "XMLTableShapeResizer.hxx"
+#include <formula/grammar.hxx>
+#include <tabprotection.hxx>
+#include <rangelst.hxx>
+
+namespace com::sun::star::drawing { class XDrawPage; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::drawing { class XShapes; }
+
+class ScXMLImport;
+
+struct ScXMLTabProtectionData
+{
+ OUString maPassword;
+ ScPasswordHash meHash1;
+ ScPasswordHash meHash2;
+ bool mbProtected;
+ bool mbSelectProtectedCells;
+ bool mbSelectUnprotectedCells;
+ bool mbInsertColumns;
+ bool mbInsertRows;
+ bool mbDeleteColumns;
+ bool mbDeleteRows;
+
+ ScXMLTabProtectionData();
+};
+
+class ScMyTables
+{
+private:
+ ScXMLImport& rImport;
+
+ ScMyOLEFixer aFixupOLEs;
+
+ css::uno::Reference< css::sheet::XSpreadsheet > xCurrentSheet;
+ css::uno::Reference< css::drawing::XDrawPage > xDrawPage;
+ css::uno::Reference < css::drawing::XShapes > xShapes;
+ OUString sCurrentSheetName;
+ ScAddress maCurrentCellPos;
+ ScRangeList maMatrixRangeList;
+ ScXMLTabProtectionData maProtectionData;
+ sal_Int32 nCurrentColCount;
+ sal_Int16 nCurrentDrawPage;
+ sal_Int16 nCurrentXShapes;
+
+ void SetTableStyle(const OUString& sStyleName);
+public:
+ explicit ScMyTables(ScXMLImport& rImport);
+ ~ScMyTables();
+ void NewSheet(const OUString& sTableName, const OUString& sStyleName,
+ const ScXMLTabProtectionData& rProtectData);
+ void AddRow();
+ void SetRowStyle(const OUString& rCellStyleName);
+ void AddColumn(bool bIsCovered);
+ void FixupOLEs() { aFixupOLEs.FixupOLEs(); }
+ static bool IsOLE(const css::uno::Reference< css::drawing::XShape >& rShape)
+ { return ScMyOLEFixer::IsOLE(rShape); }
+ void DeleteTable();
+ const ScAddress& GetCurrentCellPos() const { return maCurrentCellPos; };
+ void AddColStyle(const sal_Int32 nRepeat, const OUString& rCellStyleName);
+ ScXMLTabProtectionData& GetCurrentProtectionData() { return maProtectionData; }
+ const OUString& GetCurrentSheetName() const { return sCurrentSheetName; }
+ SCTAB GetCurrentSheet() const { return (maCurrentCellPos.Tab() >= 0) ? maCurrentCellPos.Tab() : 0; }
+ SCCOL GetCurrentColCount() const;
+ SCROW GetCurrentRow() const { return (maCurrentCellPos.Row() >= 0) ? maCurrentCellPos.Row() : 0; }
+ const css::uno::Reference< css::sheet::XSpreadsheet >&
+ GetCurrentXSheet() const { return xCurrentSheet; }
+ css::uno::Reference< css::drawing::XDrawPage > const &
+ GetCurrentXDrawPage();
+ css::uno::Reference< css::drawing::XShapes > const &
+ GetCurrentXShapes();
+ bool HasDrawPage() const;
+ bool HasXShapes() const;
+ void AddOLE(const css::uno::Reference <css::drawing::XShape>& rShape,
+ const OUString &rRangeList);
+
+ void AddMatrixRange( const SCCOL nStartColumn,
+ const SCROW nStartRow,
+ const SCCOL nEndColumn,
+ const SCROW nEndRow,
+ const OUString& rFormula,
+ const OUString& rFormulaNmsp,
+ const formula::FormulaGrammar::Grammar );
+ bool IsPartOfMatrix( const ScAddress& rScAddress) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltabi.cxx b/sc/source/filter/xml/xmltabi.cxx
new file mode 100644
index 0000000000..af4ba33d3c
--- /dev/null
+++ b/sc/source/filter/xml/xmltabi.cxx
@@ -0,0 +1,469 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmltabi.hxx"
+#include "xmlimprt.hxx"
+#include "xmlrowi.hxx"
+#include "xmlcoli.hxx"
+#include "xmlsceni.hxx"
+#include "xmlexternaltabi.hxx"
+#include "xmlnexpi.hxx"
+#include <document.hxx>
+#include <docuno.hxx>
+#include <olinetab.hxx>
+#include "XMLTableShapesContext.hxx"
+#include "XMLTableSourceContext.hxx"
+#include "XMLStylesImportHelper.hxx"
+#include <rangeutl.hxx>
+#include <externalrefmgr.hxx>
+#include <sheetdata.hxx>
+#include "xmlcondformat.hxx"
+#include "SparklineGroupsImportContext.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/XMLEventsImportContext.hxx>
+
+#include <tools/urlobj.hxx>
+#include <sax/fastattribs.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <comphelper/servicehelper.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+
+/**
+ * Determine whether this table is an external reference cache from its
+ * name. There is currently no way of determining whether a table is a
+ * regular table or an external reference cache other than examining the
+ * name itself. We should probably introduce a new boolean value for
+ * table:table element and use it instead of doing this, to make it more
+ * reliable and future-proof.
+ *
+ * @param rName
+ *
+ * @return
+ */
+static bool lcl_isExternalRefCache(const OUString& rName, OUString& rUrl, OUString& rExtTabName)
+{
+ // 'file:///path/to/file.ods'#MySheet
+ // 'file:///path/to/file.ods'#MySheet with space
+ // 'file:///path/to/file's.ods'#Sheet (Notice the quote in the file name.
+ // That's allowed.)
+
+ if ( rName.toChar() != '\'' ) // initial quote
+ return false;
+
+ // #i114504# Other schemes besides "file:" are also allowed.
+ // CompareProtocolScheme is quick, only looks at the start of the string.
+ INetProtocol eProt = INetURLObject::CompareProtocolScheme( rName.subView(1) );
+ if ( eProt == INetProtocol::NotValid )
+ return false;
+
+ OUString aPrefix = INetURLObject::GetScheme( eProt );
+ sal_Int32 nPrefLen = aPrefix.getLength();
+
+ OUStringBuffer aUrlBuf, aTabNameBuf;
+ aUrlBuf.append( aPrefix );
+ sal_Int32 n = rName.getLength();
+ const sal_Unicode* p = rName.getStr();
+
+ bool bInUrl = true;
+ sal_Unicode cPrev = 0;
+ for (sal_Int32 i = nPrefLen+1; i < n; ++i) // start the loop after quote and prefix
+ {
+ const sal_Unicode c = p[i];
+ if (bInUrl)
+ {
+ // parsing file URL
+ if (c == '#')
+ {
+ if (cPrev != '\'')
+ return false;
+
+ rUrl = aUrlBuf.makeStringAndClear();
+ rUrl = rUrl.copy(0, rUrl.getLength()-1); // remove the trailing single-quote.
+ bInUrl = false;
+ }
+ else
+ aUrlBuf.append(c);
+ }
+ else
+ // parsing sheet name.
+ aTabNameBuf.append(c);
+
+ cPrev = c;
+ }
+
+ if (bInUrl)
+ return false;
+
+ if (aTabNameBuf.isEmpty())
+ return false;
+
+ rExtTabName = aTabNameBuf.makeStringAndClear();
+
+ return true;
+}
+
+ScXMLExternalTabData::ScXMLExternalTabData() :
+ mnRow(0), mnCol(0), mnFileId(0)
+{
+}
+
+ScXMLTableContext::ScXMLTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nStartOffset(-1),
+ bStartFormPage(false),
+ bPrintEntireSheet(true)
+{
+ // get start offset in file (if available)
+ nStartOffset = GetScImport().GetByteOffset();
+
+ ScXMLTabProtectionData aProtectData;
+ OUString sName;
+ OUString sStyleName;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch (it.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ sName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ sStyleName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTED ):
+ aProtectData.mbProtected = IsXMLToken( it, XML_TRUE );
+ break;
+ case XML_ELEMENT( TABLE, XML_PRINT_RANGES ):
+ sPrintRanges = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTION_KEY ):
+ aProtectData.maPassword = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM ):
+ aProtectData.meHash1 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 ):
+ case XML_ELEMENT( LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 ):
+ aProtectData.meHash2 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_PRINT ):
+ {
+ if ( IsXMLToken( it, XML_FALSE) )
+ bPrintEntireSheet = false;
+ }
+ break;
+ }
+
+ }
+ }
+
+ OUString aExtUrl, aExtTabName;
+ if (lcl_isExternalRefCache(sName, aExtUrl, aExtTabName))
+ {
+ // This is an external ref cache table.
+ pExternalRefInfo.reset(new ScXMLExternalTabData);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (pDoc)
+ {
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ pExternalRefInfo->mnFileId = pRefMgr->getExternalFileId(aExtUrl);
+ pExternalRefInfo->mpCacheTable = pRefMgr->getCacheTable(pExternalRefInfo->mnFileId, aExtTabName, true,
+ nullptr, &aExtUrl);
+ pExternalRefInfo->mpCacheTable->setWholeTableCached();
+ }
+ }
+ else
+ {
+ // This is a regular table.
+ GetScImport().GetTables().NewSheet(sName, sStyleName, aProtectData);
+ }
+}
+
+ScXMLTableContext::~ScXMLTableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLTableContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (pExternalRefInfo)
+ {
+ // We only care about the table-row and table-source elements for
+ // external cache data.
+ switch ( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW_GROUP ):
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_ROWS ):
+ case XML_ELEMENT( TABLE, XML_TABLE_ROWS ):
+ // #i101319# don't discard rows in groups or header (repeat range)
+ return new ScXMLExternalRefRowsContext(
+ GetScImport(), *pExternalRefInfo);
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ return new ScXMLExternalRefRowContext(
+ GetScImport(), pAttribList, *pExternalRefInfo);
+ case XML_ELEMENT( TABLE, XML_TABLE_SOURCE ):
+ return new ScXMLExternalRefTabSourceContext(
+ GetScImport(), pAttribList, *pExternalRefInfo);
+ default:
+ ;
+ }
+ return nullptr;
+ }
+
+ SvXMLImportContext *pContext(nullptr);
+
+ switch ( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_NAMED_EXPRESSIONS ):
+ {
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ pContext = new ScXMLNamedExpressionsContext(
+ GetScImport(),
+ std::make_shared<ScXMLNamedExpressionsContext::SheetLocalInserter>(GetScImport(), nTab));
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN_GROUP ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN ):
+ pContext = new ScXMLTableColContext( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_PROTECTION ):
+ case XML_ELEMENT( LO_EXT, XML_TABLE_PROTECTION ):
+ case XML_ELEMENT( OFFICE_EXT, XML_TABLE_PROTECTION ):
+ pContext = new ScXMLTableProtectionContext( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW_GROUP ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ pContext = new ScXMLTableRowContext( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_SOURCE ):
+ pContext = new ScXMLTableSourceContext( GetScImport(), pAttribList);
+ break;
+ case XML_ELEMENT( TABLE, XML_SCENARIO ):
+ pContext = new ScXMLTableScenarioContext( GetScImport(), pAttribList);
+ break;
+ case XML_ELEMENT( TABLE, XML_SHAPES ):
+ pContext = new ScXMLTableShapesContext( GetScImport() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_CONDITIONAL_FORMATS ):
+ pContext = new ScXMLConditionalFormatsContext( GetScImport() );
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_GROUPS):
+ pContext = new sc::SparklineGroupsImportContext(GetScImport());
+ break;
+ case XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS):
+ case XML_ELEMENT(OFFICE_EXT, XML_EVENT_LISTENERS):
+ {
+ // use XEventsSupplier interface of the sheet
+ uno::Reference<document::XEventsSupplier> xSupplier( GetScImport().GetTables().GetCurrentXSheet(), uno::UNO_QUERY );
+ pContext = new XMLEventsImportContext( GetImport(), xSupplier );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_FORMS):
+ {
+ GetScImport().GetFormImport()->startPage(GetScImport().GetTables().GetCurrentXDrawPage());
+ bStartFormPage = true;
+ pContext = xmloff::OFormLayerXMLImport::createOfficeFormsContext( GetScImport() );
+ }
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ ScXMLImport::MutexGuard aMutexGuard(GetScImport());
+ ScXMLImport& rImport = GetScImport();
+ rImport.GetStylesImportHelper()->EndTable();
+ ScDocument* pDoc(rImport.GetDocument());
+ if (!pDoc)
+ return;
+
+ ScMyTables& rTables = rImport.GetTables();
+ SCTAB nCurTab = rTables.GetCurrentSheet();
+ // tdf#51022 process only print ranges of internal sheets
+ if (!pExternalRefInfo)
+ {
+ if (!sPrintRanges.isEmpty())
+ {
+ ScRangeList aRangeList;
+ ScRangeStringConverter::GetRangeListFromString(aRangeList, sPrintRanges, *pDoc, ::formula::FormulaGrammar::CONV_OOO);
+ size_t nCount = aRangeList.size();
+ for (size_t i = 0; i < nCount; i++)
+ {
+ pDoc->AddPrintRange(nCurTab, aRangeList[i]);
+ }
+ }
+ else if (!bPrintEntireSheet)
+ // Sheet has "print entire sheet" option by default. Remove it.
+ pDoc->ClearPrintRanges(nCurTab);
+ }
+
+ ScOutlineTable* pOutlineTable(pDoc->GetOutlineTable(nCurTab));
+ if (pOutlineTable)
+ {
+ ScOutlineArray& rColArray(pOutlineTable->GetColArray());
+ size_t nDepth = rColArray.GetDepth();
+ for (size_t i = 0; i < nDepth; ++i)
+ {
+ size_t nCount = rColArray.GetCount(i);
+ for (size_t j = 0; j < nCount; ++j)
+ {
+ const ScOutlineEntry* pEntry = rColArray.GetEntry(i, j);
+ if (pEntry->IsHidden())
+ rColArray.SetVisibleBelow(i, j, false);
+ }
+ }
+ ScOutlineArray& rRowArray(pOutlineTable->GetRowArray());
+ nDepth = rRowArray.GetDepth();
+ for (size_t i = 0; i < nDepth; ++i)
+ {
+ size_t nCount = rRowArray.GetCount(i);
+ for (size_t j = 0; j < nCount; ++j)
+ {
+ const ScOutlineEntry* pEntry = rRowArray.GetEntry(i, j);
+ if (pEntry->IsHidden())
+ rRowArray.SetVisibleBelow(i, j, false);
+ }
+ }
+ }
+ if (rTables.HasDrawPage())
+ {
+ if (rTables.HasXShapes())
+ {
+ rImport.GetShapeImport()->popGroupAndPostProcess();
+ uno::Reference < drawing::XShapes > xTempShapes(rTables.GetCurrentXShapes());
+ rImport.GetShapeImport()->endPage(xTempShapes);
+ }
+ if (bStartFormPage)
+ rImport.GetFormImport()->endPage();
+ }
+
+ rTables.DeleteTable();
+ rImport.ProgressBarIncrement();
+
+ // store stream positions
+ if (!pExternalRefInfo && nStartOffset >= 0 /* && nEndOffset >= 0 */)
+ {
+ ScSheetSaveData* pSheetData = rImport.GetScModel()->GetSheetSaveData();
+ SCTAB nTab = rTables.GetCurrentSheet();
+ // pSheetData->AddStreamPos( nTab, nStartOffset, nEndOffset );
+ pSheetData->StartStreamPos( nTab, nStartOffset );
+ }
+}
+
+ScXMLTableProtectionContext::ScXMLTableProtectionContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport )
+{
+ bool bSelectProtectedCells = false;
+ bool bSelectUnprotectedCells = false;
+ bool bInsertColumns = false;
+ bool bInsertRows = false;
+ bool bDeleteColumns = false;
+ bool bDeleteRows = false;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ sal_Int32 nToken = aIter.getToken();
+ switch (nToken)
+ {
+ case XML_ELEMENT( TABLE, XML_SELECT_PROTECTED_CELLS ):
+ case XML_ELEMENT( OFFICE_EXT, XML_SELECT_PROTECTED_CELLS ):
+ case XML_ELEMENT( LO_EXT, XML_SELECT_PROTECTED_CELLS ):
+ bSelectProtectedCells = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_SELECT_UNPROTECTED_CELLS ):
+ case XML_ELEMENT( OFFICE_EXT, XML_SELECT_UNPROTECTED_CELLS ):
+ case XML_ELEMENT( LO_EXT, XML_SELECT_UNPROTECTED_CELLS ):
+ bSelectUnprotectedCells = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_INSERT_COLUMNS ):
+ bInsertColumns = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_INSERT_ROWS ):
+ bInsertRows = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_DELETE_COLUMNS ):
+ bDeleteColumns = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_DELETE_ROWS ):
+ bDeleteRows = IsXMLToken(aIter, XML_TRUE);
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ }
+ }
+
+ ScXMLTabProtectionData& rProtectData = GetScImport().GetTables().GetCurrentProtectionData();
+ rProtectData.mbSelectProtectedCells = bSelectProtectedCells;
+ rProtectData.mbSelectUnprotectedCells = bSelectUnprotectedCells;
+ rProtectData.mbInsertColumns = bInsertColumns;
+ rProtectData.mbInsertRows = bInsertRows;
+ rProtectData.mbDeleteColumns = bDeleteColumns;
+ rProtectData.mbDeleteRows = bDeleteRows;
+}
+
+ScXMLTableProtectionContext::~ScXMLTableProtectionContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltabi.hxx b/sc/source/filter/xml/xmltabi.hxx
new file mode 100644
index 0000000000..92f5b1956a
--- /dev/null
+++ b/sc/source/filter/xml/xmltabi.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 <externalrefmgr.hxx>
+#include "importcontext.hxx"
+
+#include <memory>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+
+struct ScXMLExternalTabData
+{
+ ScExternalRefCache::TableTypeRef mpCacheTable;
+ sal_Int32 mnRow;
+ sal_Int32 mnCol;
+ sal_uInt16 mnFileId;
+
+ ScXMLExternalTabData();
+};
+
+class ScXMLTableContext : public ScXMLImportContext
+{
+ OUString sPrintRanges;
+ ::std::unique_ptr<ScXMLExternalTabData> pExternalRefInfo;
+ sal_Int64 nStartOffset;
+ bool bStartFormPage;
+ bool bPrintEntireSheet;
+
+public:
+
+ ScXMLTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class ScXMLTableProtectionContext : public ScXMLImportContext
+{
+public:
+ ScXMLTableProtectionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableProtectionContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltransformationi.cxx b/sc/source/filter/xml/xmltransformationi.cxx
new file mode 100644
index 0000000000..4c53f30f8e
--- /dev/null
+++ b/sc/source/filter/xml/xmltransformationi.cxx
@@ -0,0 +1,637 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "xmlimprt.hxx"
+#include "xmltransformationi.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <datamapper.hxx>
+#include <document.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTransformationsContext::ScXMLTransformationsContext(ScXMLImport& rImport)
+ : ScXMLImportContext(rImport)
+{
+}
+
+ScXMLTransformationsContext::~ScXMLTransformationsContext() {}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLTransformationsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList* pAttribList
+ = &sax_fastparser::castToFastAttributeList(xAttrList);
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_REMOVE_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnRemoveContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_SPLIT_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnSplitContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_MERGE_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnMergeContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_SORT_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnSortContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_TEXT_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnTextContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_AGGREGATE_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnAggregateContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_NUMBER_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnNumberContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_REPLACENULL_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnRemoveNullContext(GetScImport(), pAttribList);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLColumnRemoveContext::ScXMLColumnRemoveContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& /*rAttrList*/)
+ : ScXMLImportContext(rImport)
+{
+}
+
+ScXMLColumnRemoveContext::~ScXMLColumnRemoveContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::ColumnRemoveTransformation>(std::set(maColumns)));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnRemoveContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ sax_fastparser::FastAttributeList* pAttribList
+ = &sax_fastparser::castToFastAttributeList(xAttrList);
+
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList = pAttribList;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnSplitContext::ScXMLColumnSplitContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+{
+ SCCOL mnCol = 0;
+ OUString cSeparator;
+
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ mnCol = aIter.toInt32();
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SEPARATOR):
+ {
+ cSeparator = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (mnCol > 0)
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::SplitColumnTransformation>(mnCol, cSeparator.toChar()));
+ }
+ }
+}
+
+ScXMLColumnSplitContext::~ScXMLColumnSplitContext() {}
+
+ScXMLColumnMergeContext::ScXMLColumnMergeContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+{
+ if (!rAttrList.is())
+ return;
+
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_MERGE_STRING):
+ {
+ maMergeString = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLColumnMergeContext::~ScXMLColumnMergeContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::MergeColumnTransformation>(std::set(maColumns), maMergeString));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnMergeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnSortContext::ScXMLColumnSortContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& /*rAttrList*/)
+ : ScXMLImportContext(rImport)
+{
+}
+
+ScXMLColumnSortContext::~ScXMLColumnSortContext() {}
+
+/*
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnSortContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+
+}
+*/
+
+ScXMLColumnTextContext::ScXMLColumnTextContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::TEXT_TRANSFORM_TYPE::TO_LOWER)
+{
+ OUString aType;
+
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (!aType.isEmpty())
+ {
+ if (aType == "lowercase")
+ maType = sc::TEXT_TRANSFORM_TYPE::TO_LOWER;
+ else if (aType == "uppercase")
+ maType = sc::TEXT_TRANSFORM_TYPE::TO_UPPER;
+ else if (aType == "capitalize")
+ maType = sc::TEXT_TRANSFORM_TYPE::CAPITALIZE;
+ else if (aType == "trim")
+ maType = sc::TEXT_TRANSFORM_TYPE::TRIM;
+ }
+}
+
+ScXMLColumnTextContext::~ScXMLColumnTextContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::TextTransformation>(std::set(maColumns), maType));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnTextContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnAggregateContext::ScXMLColumnAggregateContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::AGGREGATE_FUNCTION::SUM)
+{
+ OUString aType;
+
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (!aType.isEmpty())
+ {
+ if (aType == "sum")
+ maType = sc::AGGREGATE_FUNCTION::SUM;
+ else if (aType == "average")
+ maType = sc::AGGREGATE_FUNCTION::AVERAGE;
+ else if (aType == "min")
+ maType = sc::AGGREGATE_FUNCTION::MIN;
+ else if (aType == "max")
+ maType = sc::AGGREGATE_FUNCTION::MAX;
+ }
+}
+
+ScXMLColumnAggregateContext::~ScXMLColumnAggregateContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::AggregateFunction>(std::set(maColumns), maType));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnAggregateContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnNumberContext::ScXMLColumnNumberContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::NUMBER_TRANSFORM_TYPE::ROUND)
+ , maPrecision(0)
+{
+ OUString aType;
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_PRECISION):
+ {
+ maPrecision = aIter.toInt32();
+ }
+ break;
+ }
+ }
+ }
+
+ if (aType.isEmpty())
+ return;
+
+ if (aType == "round")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ROUND;
+ else if (aType == "round-up")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ROUND_UP;
+ else if (aType == "round-down")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN;
+ else if (aType == "abs")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE;
+ else if (aType == "log")
+ maType = sc::NUMBER_TRANSFORM_TYPE::LOG_E;
+ else if (aType == "log-base-10")
+ maType = sc::NUMBER_TRANSFORM_TYPE::LOG_10;
+ else if (aType == "cube")
+ maType = sc::NUMBER_TRANSFORM_TYPE::CUBE;
+ else if (aType == "number-square")
+ maType = sc::NUMBER_TRANSFORM_TYPE::SQUARE;
+ else if (aType == "square-root")
+ maType = sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT;
+ else if (aType == "exponential")
+ maType = sc::NUMBER_TRANSFORM_TYPE::EXPONENT;
+ else if (aType == "even")
+ maType = sc::NUMBER_TRANSFORM_TYPE::IS_EVEN;
+ else if (aType == "odd")
+ maType = sc::NUMBER_TRANSFORM_TYPE::IS_ODD;
+ else if (aType == "sign")
+ maType = sc::NUMBER_TRANSFORM_TYPE::SIGN;
+}
+
+ScXMLColumnNumberContext::~ScXMLColumnNumberContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::NumberTransformation>(std::set(maColumns), maType, maPrecision));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnNumberContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnRemoveNullContext::ScXMLColumnRemoveNullContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+{
+ if (!rAttrList.is())
+ return;
+
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_REPLACE_STRING):
+ {
+ maReplaceString = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLColumnRemoveNullContext::~ScXMLColumnRemoveNullContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::ReplaceNullTransformation>(std::set(maColumns), maReplaceString));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnRemoveNullContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLDateTimeContext::ScXMLDateTimeContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING)
+{
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (aType.isEmpty())
+ return;
+
+ if (aType == "date-string")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING;
+ else if (aType == "year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::YEAR;
+ else if (aType == "start-of-year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR;
+ else if (aType == "end-of-year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR;
+ else if (aType == "month")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::MONTH;
+ else if (aType == "month-name")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME;
+ else if (aType == "start-of-month")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH;
+ else if (aType == "end-of-month")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH;
+ else if (aType == "day")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DAY;
+ else if (aType == "day-of-week")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK;
+ else if (aType == "day-of-year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR;
+ else if (aType == "quarter")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::QUARTER;
+ else if (aType == "start-of-quarter")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER;
+ else if (aType == "end-of-quarter")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER;
+ else if (aType == "time")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::TIME;
+ else if (aType == "hour")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::HOUR;
+ else if (aType == "minute")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::MINUTE;
+ else if (aType == "seconds")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::SECOND;
+}
+
+ScXMLDateTimeContext::~ScXMLDateTimeContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::DateTimeTransformation>(std::set(maColumns), maType));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SAL_CALL ScXMLDateTimeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltransformationi.hxx b/sc/source/filter/xml/xmltransformationi.hxx
new file mode 100644
index 0000000000..9ee7066209
--- /dev/null
+++ b/sc/source/filter/xml/xmltransformationi.hxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <datatransformation.hxx>
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser
+{
+class FastAttributeList;
+}
+
+class ScXMLTransformationsContext : public ScXMLImportContext
+{
+public:
+ ScXMLTransformationsContext(ScXMLImport& rImport);
+
+ virtual ~ScXMLTransformationsContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnRemoveContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+
+public:
+ ScXMLColumnRemoveContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnRemoveContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnSplitContext : public ScXMLImportContext
+{
+public:
+ ScXMLColumnSplitContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnSplitContext() override;
+};
+
+class ScXMLColumnMergeContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ OUString maMergeString;
+
+public:
+ ScXMLColumnMergeContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnMergeContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnSortContext : public ScXMLImportContext
+{
+public:
+ ScXMLColumnSortContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnSortContext() override;
+ /*
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+ */
+};
+
+class ScXMLColumnTextContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ sc::TEXT_TRANSFORM_TYPE maType;
+
+public:
+ ScXMLColumnTextContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnTextContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnAggregateContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ sc::AGGREGATE_FUNCTION maType;
+
+public:
+ ScXMLColumnAggregateContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnAggregateContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnNumberContext : public ScXMLImportContext
+{
+ sc::NUMBER_TRANSFORM_TYPE maType;
+ int maPrecision;
+ std::set<SCCOL> maColumns;
+
+public:
+ ScXMLColumnNumberContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnNumberContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnRemoveNullContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ OUString maReplaceString;
+
+public:
+ ScXMLColumnRemoveNullContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnRemoveNullContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLDateTimeContext : public ScXMLImportContext
+{
+ OUString aType;
+ sc::DATETIME_TRANSFORMATION_TYPE maType;
+ std::set<SCCOL> maColumns;
+
+public:
+ ScXMLDateTimeContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLDateTimeContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlwrap.cxx b/sc/source/filter/xml/xmlwrap.cxx
new file mode 100644
index 0000000000..9794af9593
--- /dev/null
+++ b/sc/source/filter/xml/xmlwrap.cxx
@@ -0,0 +1,996 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <utility>
+#include <vcl/errinf.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sot/storage.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/unreachable.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/xmlgrhlp.hxx>
+#include <svtools/sfxecode.hxx>
+#include <sfx2/frame.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <sfx2/DocumentMetadataAccess.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <xmloff/shapeexport.hxx>
+#include <svx/xmleohlp.hxx>
+#include <sal/log.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <document.hxx>
+#include <xmlwrap.hxx>
+#include "xmlimprt.hxx"
+#include "xmlexprt.hxx"
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scerrors.hxx>
+#include "XMLExportSharedData.hxx"
+#include <docuno.hxx>
+#include <drwlayer.hxx>
+#include <sheetdata.hxx>
+#include "XMLCodeNameProvider.hxx"
+#include <docsh.hxx>
+#include <unonames.hxx>
+
+using namespace com::sun::star;
+using namespace css::uno;
+
+ScXMLImportWrapper::ScXMLImportWrapper( ScDocShell& rDocSh, SfxMedium* pM, uno::Reference < embed::XStorage > xStor ) :
+ mrDocShell(rDocSh),
+ rDoc(rDocSh.GetDocument()),
+ pMedium(pM),
+ xStorage(std::move(xStor))
+{
+ OSL_ENSURE( pMedium || xStorage.is(), "ScXMLImportWrapper: Medium or Storage must be set" );
+}
+
+uno::Reference <task::XStatusIndicator> ScXMLImportWrapper::GetStatusIndicator() const
+{
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ if (pMedium)
+ {
+ const SfxUnoAnyItem* pItem = pMedium->GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ xStatusIndicator.set(pItem->GetValue(), uno::UNO_QUERY);
+ }
+ return xStatusIndicator;
+}
+
+ErrCodeMsg ScXMLImportWrapper::ImportFromComponent(const uno::Reference<uno::XComponentContext>& xContext,
+ const uno::Reference<frame::XModel>& xModel,
+ xml::sax::InputSource& aParserInput,
+ const OUString& sComponentName, const OUString& sDocName,
+ const uno::Sequence<uno::Any>& aArgs,
+ bool bMustBeSuccessful)
+{
+ uno::Reference < io::XStream > xDocStream;
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetStorage();
+
+ bool bEncrypted = false;
+ OUString sStream(sDocName);
+ if( xStorage.is() )
+ {
+ try
+ {
+ if ( xStorage->hasByName(sDocName) && xStorage->isStreamElement( sDocName) )
+ xDocStream = xStorage->openStreamElement( sDocName, embed::ElementModes::READ );
+ else
+ return ERRCODE_NONE;
+
+ aParserInput.aInputStream = xDocStream->getInputStream();
+ uno::Reference < beans::XPropertySet > xSet( xDocStream, uno::UNO_QUERY );
+
+ uno::Any aAny = xSet->getPropertyValue("Encrypted");
+ aAny >>= bEncrypted;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch( const packages::zip::ZipIOException& )
+ {
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch( const uno::Exception& )
+ {
+ return SCERR_IMPORT_UNKNOWN;
+ }
+ }
+ else
+ return SCERR_IMPORT_UNKNOWN;
+
+ // set Base URL
+ uno::Reference< beans::XPropertySet > xInfoSet;
+ if( aArgs.hasElements() )
+ aArgs.getConstArray()[0] >>= xInfoSet;
+ OSL_ENSURE( xInfoSet.is(), "missing property set" );
+ if( xInfoSet.is() )
+ {
+ xInfoSet->setPropertyValue( "StreamName", uno::Any( sStream ) );
+ }
+
+ ErrCodeMsg nReturn = ERRCODE_NONE;
+ rDoc.SetRangeOverflowType(ERRCODE_NONE); // is modified by the importer if limits are exceeded
+
+ uno::Reference<XInterface> xImportInterface =
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ sComponentName, aArgs, xContext );
+ SAL_WARN_IF( !xImportInterface, "sc", "can't get Calc importer " << sComponentName );
+ uno::Reference<document::XImporter> xImporter( xImportInterface, uno::UNO_QUERY );
+ if (xImporter.is())
+ xImporter->setTargetDocument( xModel );
+
+ ScXMLImport* pImporterImpl = dynamic_cast<ScXMLImport*>(xImporter.get());
+ if (pImporterImpl)
+ pImporterImpl->SetPostProcessData(&maPostProcessData);
+
+ // connect parser and filter
+ try
+ {
+ // xImportInterface is either ScXMLImport or an XMLTransformer subclass.
+ // ScXMLImport implements XFastParser, but XMLTransformer only implements XExtendedDocumentHandler
+
+ uno::Reference< xml::sax::XFastParser > xFastParser(xImportInterface, uno::UNO_QUERY);
+ if (xFastParser)
+ xFastParser->parseStream( aParserInput );
+ else
+ {
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xContext);
+ uno::Reference<css::xml::sax::XDocumentHandler> xDocumentHandler(xImportInterface, uno::UNO_QUERY);
+ xParser->setDocumentHandler( xDocumentHandler );
+ xParser->parseStream( aParserInput );
+ }
+ }
+ catch( const xml::sax::SAXParseException& r )
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ // sax parser sends wrapped exceptions,
+ // try to find the original one
+ xml::sax::SAXException aSaxEx = *static_cast<xml::sax::SAXException const *>(&r);
+ bool bTryChild = true;
+
+ while( bTryChild )
+ {
+ xml::sax::SAXException aTmp;
+ if ( aSaxEx.WrappedException >>= aTmp )
+ aSaxEx = aTmp;
+ else
+ bTryChild = false;
+ }
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( aSaxEx.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+ else if( bEncrypted )
+ nReturn = ERRCODE_SFX_WRONGPASSWORD;
+ else
+ {
+ SAL_WARN("sc.filter", "SAX parse exception caught while importing: " << exceptionToString(ex));
+
+ OUString sErr = OUString::number( r.LineNumber ) +
+ "," +
+ OUString::number( r.ColumnNumber );
+
+ if( !sDocName.isEmpty() )
+ {
+ nReturn = ErrCodeMsg(
+ (bMustBeSuccessful ? SCERR_IMPORT_FILE_ROWCOL
+ : SCWARN_IMPORT_FILE_ROWCOL),
+ sDocName, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+ else
+ {
+ OSL_ENSURE( bMustBeSuccessful, "Warnings are not supported" );
+ nReturn = ErrCodeMsg( SCERR_IMPORT_FORMAT_ROWCOL, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+ }
+ }
+ catch( const xml::sax::SAXException& r )
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( r.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+ else if( bEncrypted )
+ nReturn = ERRCODE_SFX_WRONGPASSWORD;
+ else
+ {
+ SAL_WARN("sc.filter", "SAX exception caught while importing: " << exceptionToString(ex));
+
+ nReturn = SCERR_IMPORT_FORMAT;
+ }
+ }
+ catch( const packages::zip::ZipIOException& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.filter", "Zip exception caught while importing");
+
+ nReturn = ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.filter", "IO exception caught while importing");
+
+ nReturn = SCERR_IMPORT_OPEN;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.filter", "uno exception caught while importing");
+
+ nReturn = SCERR_IMPORT_UNKNOWN;
+ }
+
+ // #i31130# Can't use getImplementation here to get the ScXMLImport from xDocHandler,
+ // because when OOo 1.x files are loaded, xDocHandler is the OOo2OasisTransformer.
+ // So the overflow warning ErrorCode is now stored in the document.
+ // Export works differently, there getImplementation still works.
+
+ if (rDoc.HasRangeOverflow() && !nReturn)
+ nReturn = rDoc.GetRangeOverflowType();
+
+ // success!
+ return nReturn;
+}
+
+bool ScXMLImportWrapper::Import( ImportFlags nMode, ErrCodeMsg& rError )
+{
+ uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ if (pMedium)
+ aParserInput.sSystemId = pMedium->GetName();
+
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetStorage();
+
+ // get filter
+ uno::Reference<frame::XModel> xModel = mrDocShell.GetModel();
+
+ /** property map for export info set */
+ static comphelper::PropertyMapEntry const aImportInfoMap[] =
+ {
+ { OUString("ProgressRange"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressMax"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressCurrent"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("NumberStyles"), 0, cppu::UnoType<container::XNameAccess>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("PrivateData"), 0, cppu::UnoType<uno::XInterface>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BuildId"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("VBACompatibilityMode"), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("ScriptConfiguration"), 0, cppu::UnoType<container::XNameAccess>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("OrganizerMode"), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { SC_UNO_ODS_LOCK_SOLAR_MUTEX, 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { SC_UNO_ODS_IMPORT_STYLES, 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aImportInfoMap ) ) );
+
+ // No need to lock solar mutex when calling from the wrapper.
+ xInfoSet->setPropertyValue(SC_UNO_ODS_LOCK_SOLAR_MUTEX, uno::Any(false));
+
+ // ---- get BuildId from parent container if available
+
+ uno::Reference< container::XChild > xChild( xModel, uno::UNO_QUERY );
+ if( xChild.is() )
+ {
+ uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY );
+ if( xParentSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() );
+ OUString sPropName("BuildId" );
+ if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) )
+ {
+ xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) );
+ }
+ }
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator = GetStatusIndicator();
+ if (xStatusIndicator.is())
+ {
+ sal_Int32 nProgressRange(1000000);
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ xInfoSet->setPropertyValue("ProgressRange", uno::Any(nProgressRange));
+ }
+
+ // Set base URI
+ OSL_ENSURE( pMedium, "There is no medium to get MediaDescriptor from!" );
+ OUString aBaseURL = pMedium ? pMedium->GetBaseURL() : OUString();
+ // needed for relative URLs, but in clipboard copy/paste there may be none
+ SAL_INFO_IF(aBaseURL.isEmpty(), "sc.filter", "ScXMLImportWrapper: no base URL");
+ OUString sPropName("BaseURI");
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aBaseURL ) );
+
+ // TODO/LATER: do not do it for embedded links
+ OUString aName;
+ if (SfxObjectCreateMode::EMBEDDED == mrDocShell.GetCreateMode())
+ {
+ if ( pMedium )
+ {
+ const SfxStringItem* pDocHierarchItem =
+ pMedium->GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME);
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+ else
+ aName = "dummyObjectName";
+
+ if( !aName.isEmpty() )
+ {
+ sPropName = "StreamRelPath";
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aName ) );
+ }
+ }
+
+ if (mrDocShell.GetCreateMode() == SfxObjectCreateMode::ORGANIZER)
+ xInfoSet->setPropertyValue("OrganizerMode", uno::Any(true));
+
+ xInfoSet->setPropertyValue( "SourceStorage", uno::Any( xStorage ) );
+
+ bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
+
+ if ((nMode & ImportFlags::Metadata) && bOasis)
+ {
+ // RDF metadata: ODF >= 1.2
+ try
+ {
+ const uno::Reference< rdf::XDocumentMetadataAccess > xDMA(
+ xModel, uno::UNO_QUERY_THROW );
+ const uno::Reference< rdf::XURI > xBaseURI(
+ ::sfx2::createBaseURI( xContext, xModel, aBaseURL, aName ) );
+ uno::Reference<task::XInteractionHandler> xHandler =
+ mrDocShell.GetMedium()->GetInteractionHandler();
+ xDMA->loadMetadataFromStorage( xStorage, xBaseURI, xHandler );
+ }
+ catch ( const lang::WrappedTargetException & e)
+ {
+ ucb::InteractiveAugmentedIOException iaioe;
+ if ( e.TargetException >>= iaioe )
+ {
+ rError = SCERR_IMPORT_UNKNOWN;
+ }
+ else
+ {
+ rError = SCWARN_IMPORT_FEATURES_LOST;
+ }
+ }
+ catch ( const uno::Exception &)
+ {
+ rError = SCWARN_IMPORT_FEATURES_LOST;
+ }
+ }
+
+ // #i103539#: always read meta.xml for generator
+ ErrCodeMsg nMetaRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Metadata)
+ {
+ uno::Sequence<uno::Any> aMetaArgs { Any(xInfoSet) };
+
+ SAL_INFO( "sc.filter", "meta import start" );
+
+ nMetaRetval = ImportFromComponent(
+ xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisMetaImporter")
+ : OUString("com.sun.star.comp.Calc.XMLMetaImporter"),
+ "meta.xml", aMetaArgs, false);
+
+ SAL_INFO( "sc.filter", "meta import end" );
+ }
+
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+
+ uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ if( xStorage.is() )
+ {
+ xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Read );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create(xStorage, mrDocShell, SvXMLEmbeddedObjectHelperMode::Read);
+ xObjectResolver = xObjectHelper.get();
+ }
+ uno::Sequence<uno::Any> aStylesArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xObjectResolver)
+ };
+
+ ErrCodeMsg nSettingsRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Settings)
+ {
+ // Settings must be loaded first because of the printer setting,
+ // which is needed in the page styles (paper tray).
+
+ uno::Sequence<uno::Any> aSettingsArgs { Any(xInfoSet) };
+
+ SAL_INFO( "sc.filter", "settings import start" );
+
+ nSettingsRetval = ImportFromComponent(
+ xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisSettingsImporter")
+ : OUString("com.sun.star.comp.Calc.XMLSettingsImporter"),
+ "settings.xml", aSettingsArgs, false);
+
+ SAL_INFO( "sc.filter", "settings import end" );
+ }
+
+ ErrCodeMsg nStylesRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Styles)
+ {
+ SAL_INFO( "sc.filter", "styles import start" );
+
+ nStylesRetval = ImportFromComponent(xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisStylesImporter")
+ : OUString("com.sun.star.comp.Calc.XMLStylesImporter"),
+ "styles.xml",
+ aStylesArgs, true);
+
+ SAL_INFO( "sc.filter", "styles import end" );
+ }
+
+ ErrCodeMsg nDocRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Content)
+ {
+ if (mrDocShell.GetCreateMode() == SfxObjectCreateMode::INTERNAL)
+ // We only need to import content for external link cache document.
+ xInfoSet->setPropertyValue(SC_UNO_ODS_IMPORT_STYLES, uno::Any(false));
+
+ uno::Sequence<uno::Any> aDocArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xObjectResolver)
+ };
+
+ SAL_INFO( "sc.filter", "content import start" );
+
+ nDocRetval = ImportFromComponent(xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisContentImporter")
+ : OUString("com.sun.star.comp.Calc.XMLContentImporter"),
+ "content.xml",
+ aDocArgs,
+ true);
+
+ SAL_INFO( "sc.filter", "content import end" );
+ }
+ if( xGraphicHelper.is() )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+
+ if( xObjectHelper.is() )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+
+ bool bRet = false;
+ if (nDocRetval)
+ {
+ rError = nDocRetval;
+ if (nDocRetval == SCWARN_IMPORT_RANGE_OVERFLOW ||
+ nDocRetval == SCWARN_IMPORT_ROW_OVERFLOW ||
+ nDocRetval == SCWARN_IMPORT_COLUMN_OVERFLOW ||
+ nDocRetval == SCWARN_IMPORT_SHEET_OVERFLOW)
+ bRet = true;
+ }
+ else if (nStylesRetval)
+ rError = nStylesRetval;
+ else if (nMetaRetval)
+ rError = nMetaRetval;
+ else if (nSettingsRetval)
+ rError = nSettingsRetval;
+ else
+ bRet = true;
+
+ ::svx::DropUnusedNamedItems(xModel);
+
+ // set BuildId on XModel for later OLE object loading
+ if( xInfoSet.is() )
+ {
+ uno::Reference< beans::XPropertySet > xModelSet( xModel, uno::UNO_QUERY );
+ if( xModelSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() );
+ OUString sBuildPropName("BuildId" );
+ if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sBuildPropName) )
+ {
+ xModelSet->setPropertyValue( sBuildPropName, xInfoSet->getPropertyValue(sBuildPropName) );
+ }
+ }
+
+ // Set Code Names
+ uno::Any aAny = xInfoSet->getPropertyValue("ScriptConfiguration");
+ uno::Reference <container::XNameAccess> xCodeNameAccess;
+ if( aAny >>= xCodeNameAccess )
+ XMLCodeNameProvider::set( xCodeNameAccess, &rDoc );
+
+ // VBA compatibility
+ bool bVBACompat = false;
+ if ( (xInfoSet->getPropertyValue("VBACompatibilityMode") >>= bVBACompat) && bVBACompat )
+ {
+ /* Set library container to VBA compatibility mode, this
+ forces loading the Basic project, which in turn creates the
+ VBA Globals object and does all related initialization. */
+ if ( xModelSet.is() ) try
+ {
+ uno::Reference< script::vba::XVBACompatibility > xVBACompat( xModelSet->getPropertyValue(
+ "BasicLibraries" ), uno::UNO_QUERY_THROW );
+ xVBACompat->setVBACompatibilityMode( true );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+
+ // Don't test bStylesRetval and bMetaRetval, because it could be an older file which not contain such streams
+ return bRet;//!bStylesOnly ? bDocRetval : bStylesRetval;
+}
+
+static bool lcl_HasValidStream(const ScDocument& rDoc)
+{
+ ScDocShell* pObjSh = rDoc.GetDocumentShell();
+ if ( pObjSh->IsDocShared() )
+ return false; // never copy stream from shared file
+
+ // don't read remote file again
+ // (could instead re-use medium directly in that case)
+ SfxMedium* pSrcMed = rDoc.GetDocumentShell()->GetMedium();
+ if ( !pSrcMed || pSrcMed->IsRemote() )
+ return false;
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ if (rDoc.IsStreamValid(nTab))
+ return true;
+ return false;
+}
+
+bool ScXMLImportWrapper::ExportToComponent(const uno::Reference<uno::XComponentContext>& xContext,
+ const uno::Reference<frame::XModel>& xModel, const uno::Reference<xml::sax::XWriter>& xWriter,
+ const uno::Sequence<beans::PropertyValue>& aDescriptor, const OUString& sName,
+ const OUString& sMediaType, const OUString& sComponentName,
+ const uno::Sequence<uno::Any>& aArgs, std::unique_ptr<ScMySharedData>& pSharedData)
+{
+ bool bRet(false);
+ uno::Reference<io::XOutputStream> xOut;
+ uno::Reference<io::XStream> xStream;
+
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetOutputStorage();
+
+ if( xStorage.is() )
+ {
+ // #96807#; trunc stream before use, because it could be an existing stream
+ // and the new content could be shorter than the old content. In this case
+ // would not all be over written by the new content and the xml file
+ // would not be valid.
+ xStream = xStorage->openStreamElement( sName, embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ if (xSet.is())
+ {
+ xSet->setPropertyValue("MediaType", uno::Any(sMediaType));
+
+ // advise storage impl to use common encryption
+ xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", uno::Any(true) );
+ }
+
+ xOut = xStream->getOutputStream();
+ }
+
+ // set Base URL
+ uno::Reference< beans::XPropertySet > xInfoSet;
+ if( aArgs.hasElements() )
+ aArgs.getConstArray()[0] >>= xInfoSet;
+ OSL_ENSURE( xInfoSet.is(), "missing property set" );
+ if( xInfoSet.is() )
+ {
+ xInfoSet->setPropertyValue( "StreamName", uno::Any( sName ) );
+ }
+
+ xWriter->setOutputStream( xOut );
+
+ uno::Reference<document::XFilter> xFilter(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ sComponentName , aArgs, xContext ),
+ uno::UNO_QUERY );
+ OSL_ENSURE( xFilter.is(), "can't get exporter" );
+ uno::Reference<document::XExporter> xExporter( xFilter, uno::UNO_QUERY );
+ if (xExporter.is())
+ xExporter->setSourceDocument( xModel );
+
+ if ( xFilter.is() )
+ {
+ ScXMLExport* pExport = static_cast<ScXMLExport*>(dynamic_cast<SvXMLExport*>(xFilter.get()));
+ assert(pExport && "can only succeed");
+ pExport->SetSharedData(std::move(pSharedData));
+
+ // if there are sheets to copy, get the source stream
+ if ( sName == "content.xml" && lcl_HasValidStream(rDoc) && ( pExport->getExportFlags() & SvXMLExportFlags::OASIS ) )
+ {
+ // old stream is still in this file's storage - open read-only
+
+ // #i106854# use the document's storage directly, without a temporary SfxMedium
+ uno::Reference<embed::XStorage> xTmpStorage = rDoc.GetDocumentShell()->GetStorage();
+ uno::Reference<io::XStream> xSrcStream;
+ uno::Reference<io::XInputStream> xSrcInput;
+
+ // #i108978# If an embedded object is saved and no events are notified, don't use the stream
+ // because without the ...DONE events, stream positions aren't updated.
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xModel)->GetSheetSaveData();
+ if (pSheetData && pSheetData->IsInSupportedSave())
+ {
+ try
+ {
+ if (xTmpStorage.is())
+ xSrcStream = xTmpStorage->openStreamElement( sName, embed::ElementModes::READ );
+ if (xSrcStream.is())
+ xSrcInput = xSrcStream->getInputStream();
+ }
+ catch ( const uno::Exception&)
+ {
+ // stream not available (for example, password protected) - save normally (xSrcInput is null)
+ }
+ }
+
+ pExport->SetSourceStream( xSrcInput );
+ bRet = xFilter->filter( aDescriptor );
+ pExport->SetSourceStream( uno::Reference<io::XInputStream>() );
+
+ // If there was an error, reset all stream flags, so the next save attempt will use normal saving.
+ // #i110692# For embedded objects, the stream may be unavailable for one save operation (m_pAntiImpl)
+ // and become available again later. But after saving normally once, the stream positions aren't
+ // valid anymore, so the flags also have to be reset if the stream wasn't available.
+ if ( !bRet || !xSrcInput.is() )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ rDoc.SetStreamValid(nTab, false);
+ }
+ }
+ else
+ bRet = xFilter->filter( aDescriptor );
+
+ pSharedData = pExport->ReleaseSharedData();
+ }
+
+ return bRet;
+}
+
+bool ScXMLImportWrapper::Export(bool bStylesOnly)
+{
+ // Prevent all broadcasting and repaints and notification of accessibility
+ // during mass creation of captions, which is a major bottleneck and not
+ // needed during Save.
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ bool bOldLock = bool();
+ if (pDrawLayer)
+ {
+ bOldLock = pDrawLayer->isLocked();
+ pDrawLayer->setLock(true);
+ }
+
+ rDoc.CreateAllNoteCaptions();
+
+ if (pDrawLayer)
+ pDrawLayer->setLock(bOldLock);
+
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ uno::Reference<xml::sax::XWriter> xWriter = xml::sax::Writer::create(xContext);
+
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetOutputStorage();
+
+ OUString sFileName;
+ if (pMedium)
+ sFileName = pMedium->GetName();
+ ScDocShell* pObjSh = rDoc.GetDocumentShell();
+ uno::Sequence<beans::PropertyValue> aDescriptor( comphelper::InitPropertySequence({
+ { "FileName", uno::Any(sFileName) }
+ }));
+
+ /** property map for export info set */
+ static comphelper::PropertyMapEntry const aExportInfoMap[] =
+ {
+ { OUString("ProgressRange"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressMax"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressCurrent"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("WrittenNumberStyles"), 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("UsePrettyPrinting"), 0, ::cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleNames"), 0, cppu::UnoType<uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleFamilies"), 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("TargetStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aExportInfoMap ) ) );
+
+ if ( pObjSh && xStorage.is() )
+ {
+ uno::Reference<frame::XModel> xModel(pObjSh->GetModel());
+ // sorting wants to create undo actions
+ assert(SfxObjectCreateMode::STANDARD != pObjSh->GetCreateMode() || rDoc.GetDrawLayer()->IsUndoEnabled());
+ uno::Reference<drawing::XDrawPagesSupplier> const xDPS(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> const xDPs(xDPS->getDrawPages());
+ assert(xDPs.is());
+ for (auto i = xDPs->getCount(); 0 < i; )
+ {
+ --i;
+ uno::Reference<drawing::XShapes> const xDP(xDPs->getByIndex(i), uno::UNO_QUERY);
+ assert(xDP.is());
+ xmloff::FixZOrder(xDP,
+ [](uno::Reference<beans::XPropertySet> const& xShape)
+ {
+ sal_Int16 nLayerID(0);
+ xShape->getPropertyValue("LayerID") >>= nLayerID;
+ switch (nLayerID)
+ {
+ case sal_Int16(SC_LAYER_FRONT):
+ return 1;
+ case sal_Int16(SC_LAYER_BACK):
+ return 0;
+ case sal_Int16(SC_LAYER_INTERN):
+ return 2;
+ case sal_Int16(SC_LAYER_CONTROLS):
+ return 3;
+ case sal_Int16(SC_LAYER_HIDDEN):
+ return 1; // treat as equivalent to front
+ default:
+ O3TL_UNREACHABLE;
+ }
+ });
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator(GetStatusIndicator());
+ sal_Int32 nProgressRange(1000000);
+ if(xStatusIndicator.is())
+ xStatusIndicator->start(ScResId(STR_SAVE_DOC), nProgressRange);
+ xInfoSet->setPropertyValue("ProgressRange", uno::Any(nProgressRange));
+
+ bool bUsePrettyPrinting = officecfg::Office::Common::Save::Document::PrettyPrinting::get();
+ xInfoSet->setPropertyValue("UsePrettyPrinting", uno::Any(bUsePrettyPrinting));
+
+ xInfoSet->setPropertyValue( "TargetStorage", uno::Any( xStorage ) );
+
+ OSL_ENSURE( pMedium, "There is no medium to get MediaDescriptor from!" );
+ OUString aBaseURL = pMedium ? pMedium->GetBaseURL( true ) : OUString();
+ OUString sPropName("BaseURI");
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aBaseURL ) );
+
+ // TODO/LATER: do not do it for embedded links
+ if( SfxObjectCreateMode::EMBEDDED == pObjSh->GetCreateMode() )
+ {
+ OUString aName("dummyObjectName");
+ if ( pMedium )
+ {
+ const SfxStringItem* pDocHierarchItem =
+ pMedium->GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME);
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if( !aName.isEmpty() )
+ {
+ sPropName = "StreamRelPath";
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aName ) );
+ }
+ }
+
+ OUString sTextMediaType("text/xml");
+ bool bMetaRet(pObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED);
+ bool bStylesRet (false);
+ bool bDocRet(false);
+ bool bSettingsRet(false);
+ std::unique_ptr<ScMySharedData> pSharedData;
+
+ bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
+
+ // RDF metadata: ODF >= 1.2
+ if ( !bStylesOnly && bOasis )
+ {
+ const uno::Reference< beans::XPropertySet > xPropSet( xStorage, uno::UNO_QUERY_THROW );
+ try
+ {
+ OUString aVersion;
+ if (( xPropSet->getPropertyValue("Version") >>= aVersion )
+ && aVersion != ODFVER_010_TEXT
+ && aVersion != ODFVER_011_TEXT )
+ {
+ const uno::Reference< rdf::XDocumentMetadataAccess > xDMA(
+ xModel, uno::UNO_QUERY_THROW );
+ xDMA->storeMetadataToStorage( xStorage );
+ }
+ }
+ catch ( const beans::UnknownPropertyException &)
+ {
+ }
+ catch ( const uno::Exception &)
+ {
+ }
+ }
+
+ // meta export
+ if (!bStylesOnly && !bMetaRet)
+ {
+ uno::Sequence<uno::Any> aMetaArgs
+ {
+ Any(xInfoSet),
+ Any(xWriter),
+ Any(xStatusIndicator)
+ };
+
+ SAL_INFO( "sc.filter", "meta export start" );
+
+ bMetaRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "meta.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisMetaExporter")
+ : OUString("com.sun.star.comp.Calc.XMLMetaExporter"),
+ aMetaArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "meta export end" );
+ }
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ if( xStorage.is() )
+ {
+ xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Write );
+ xGraphicStorageHandler = xGraphicHelper.get();
+ }
+
+ auto xObjectHelper = SvXMLEmbeddedObjectHelper::Create(
+ xStorage, *pObjSh, SvXMLEmbeddedObjectHelperMode::Write);
+ uno::Reference<document::XEmbeddedObjectResolver> xObjectResolver(xObjectHelper);
+
+ // styles export
+
+ {
+ uno::Sequence<uno::Any> aStylesArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xWriter),
+ Any(xObjectResolver)
+ };
+
+ SAL_INFO( "sc.filter", "styles export start" );
+
+ bStylesRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "styles.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisStylesExporter")
+ : OUString("com.sun.star.comp.Calc.XMLStylesExporter"),
+ aStylesArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "styles export end" );
+ }
+
+ // content export
+
+ if (!bStylesOnly)
+ {
+ uno::Sequence<uno::Any> aDocArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xWriter),
+ Any(xObjectResolver)
+ };
+
+ SAL_INFO( "sc.filter", "content export start" );
+
+ bDocRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "content.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisContentExporter")
+ : OUString("com.sun.star.comp.Calc.XMLContentExporter"),
+ aDocArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "content export end" );
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+
+ if( xObjectHelper )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+
+ // settings export
+
+ if (!bStylesOnly)
+ {
+ uno::Sequence<uno::Any> aSettingsArgs
+ {
+ Any(xInfoSet),
+ Any(xWriter),
+ Any(xStatusIndicator)
+ };
+
+ SAL_INFO( "sc.filter", "settings export start" );
+
+ bSettingsRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "settings.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisSettingsExporter")
+ : OUString("com.sun.star.comp.Calc.XMLSettingsExporter"),
+ aSettingsArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "settings export end" );
+ }
+
+ pSharedData.reset();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return bStylesRet && ((!bStylesOnly && bDocRet && bMetaRet && bSettingsRet) || bStylesOnly);
+ }
+
+ // later: give string descriptor as parameter for doc type
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibilityHints.cxx b/sc/source/ui/Accessibility/AccessibilityHints.cxx
new file mode 100644
index 0000000000..733311f721
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibilityHints.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibilityHints.hxx>
+
+using namespace ::com::sun::star;
+
+// ScAccWinFocusLostHint - the current window lost its focus (to another application, view or document)
+
+ScAccWinFocusLostHint::~ScAccWinFocusLostHint()
+{
+}
+
+// ScAccWinFocusGotHint - the window got the focus (from another application, view or document)
+
+ScAccWinFocusGotHint::~ScAccWinFocusGotHint()
+{
+}
+
+// ScAccGridWinFocusLostHint - the current grid window lost its focus (to another application, view or document)
+
+ScAccGridWinFocusLostHint::ScAccGridWinFocusLostHint(ScSplitPos eOld )
+ :
+ ScAccWinFocusLostHint(),
+ eOldGridWin(eOld)
+{
+}
+
+ScAccGridWinFocusLostHint::~ScAccGridWinFocusLostHint()
+{
+}
+
+// ScAccGridWinFocusGotHint - the grid window got the focus (from another application, view or document)
+
+ScAccGridWinFocusGotHint::ScAccGridWinFocusGotHint(ScSplitPos eNew )
+ :
+ ScAccWinFocusGotHint(),
+ eNewGridWin(eNew)
+{
+}
+
+ScAccGridWinFocusGotHint::~ScAccGridWinFocusGotHint()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleCell.cxx b/sc/source/ui/Accessibility/AccessibleCell.cxx
new file mode 100644
index 0000000000..3ac2fdba30
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleCell.cxx
@@ -0,0 +1,615 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <string_view>
+
+#include <sal/config.h>
+
+#include <AccessibleCell.hxx>
+#include <scitems.hxx>
+#include <AccessibleText.hxx>
+#include <AccessibleDocument.hxx>
+#include <tabvwsh.hxx>
+#include <comphelper/sequence.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <editsrc.hxx>
+#include <dociter.hxx>
+#include <markdata.hxx>
+#include <cellvalue.hxx>
+#include <formulaiter.hxx>
+#include <validat.hxx>
+
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <editeng/brushitem.hxx>
+#include <vcl/svapp.hxx>
+
+#include <AccessibleSpreadsheet.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+rtl::Reference<ScAccessibleCell> ScAccessibleCell::create(
+ const uno::Reference<XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex,
+ ScSplitPos eSplitPos,
+ ScAccessibleDocument* pAccDoc)
+{
+ rtl::Reference<ScAccessibleCell> x(new ScAccessibleCell(
+ rxParent, pViewShell, rCellAddress, nIndex, eSplitPos, pAccDoc));
+ x->Init();
+ return x;
+}
+
+ScAccessibleCell::ScAccessibleCell(
+ const uno::Reference<XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex,
+ ScSplitPos eSplitPos,
+ ScAccessibleDocument* pAccDoc)
+ :
+ ScAccessibleCellBase(rxParent, GetDocument(pViewShell), rCellAddress, nIndex),
+ ::accessibility::AccessibleStaticTextBase(CreateEditSource(pViewShell, rCellAddress, eSplitPos)),
+ mpViewShell(pViewShell),
+ mpAccDoc(pAccDoc),
+ meSplitPos(eSplitPos)
+{
+ if (pViewShell)
+ pViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessibleCell::~ScAccessibleCell()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void ScAccessibleCell::Init()
+{
+ ScAccessibleCellBase::Init();
+
+ SetEventSource(this);
+}
+
+void SAL_CALL ScAccessibleCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ // dispose in AccessibleStaticTextBase
+ Dispose();
+
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpAccDoc = nullptr;
+
+ ScAccessibleCellBase::disposing();
+}
+
+ //===== XInterface =====================================================
+
+IMPLEMENT_FORWARD_XINTERFACE3( ScAccessibleCell, ScAccessibleCellBase, AccessibleStaticTextBase, ScAccessibleCellAttributeImpl )
+
+ //===== XTypeProvider ===================================================
+
+css::uno::Sequence< css::uno::Type > SAL_CALL ScAccessibleCell::getTypes()
+{
+ return ::comphelper::concatSequences(
+ ScAccessibleCellBase::getTypes(),
+ AccessibleStaticTextBase::getTypes(),
+ ScAccessibleCellAttributeImpl::getTypes()
+ );
+}
+IMPLEMENT_GET_IMPLEMENTATION_ID( ScAccessibleCell )
+
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleCell::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ return AccessibleStaticTextBase::getAccessibleAtPoint(rPoint);
+}
+
+void SAL_CALL ScAccessibleCell::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is() && mpViewShell)
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ {
+ xAccessibleComponent->grabFocus();
+ mpViewShell->SetCursor(maCellAddress.Col(), maCellAddress.Row());
+ }
+ }
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleCell::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aCellRect(GetBoundingBox());
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return aCellRect;
+}
+
+tools::Rectangle ScAccessibleCell::GetBoundingBox() const
+{
+ tools::Rectangle aCellRect;
+ if (mpViewShell)
+ {
+ tools::Long nSizeX, nSizeY;
+ mpViewShell->GetViewData().GetMergeSizePixel(
+ maCellAddress.Col(), maCellAddress.Row(), nSizeX, nSizeY);
+ aCellRect.SetSize(Size(nSizeX, nSizeY));
+ aCellRect.SetPos(mpViewShell->GetViewData().GetScrPos(maCellAddress.Col(), maCellAddress.Row(), meSplitPos, true));
+
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ {
+ tools::Rectangle aRect(pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow()));
+ aRect.Move(-aRect.Left(), -aRect.Top());
+ aCellRect = aRect.Intersection(aCellRect);
+ }
+
+ /* #i19430# Gnopernicus reads text partly if it sticks out of the cell
+ boundaries. This leads to wrong results in cases where the cell
+ text is rotated, because rotation is not taken into account when
+ calculating the visible part of the text. In these cases we will
+ simply expand the cell size to the width of the unrotated text. */
+ if (mpDoc)
+ {
+ const ScRotateValueItem* pItem = mpDoc->GetAttr( maCellAddress, ATTR_ROTATE_VALUE );
+ if( pItem && (pItem->GetValue() != 0_deg100) )
+ {
+ tools::Rectangle aParaRect = GetParagraphBoundingBox();
+ if( !aParaRect.IsEmpty() && (aCellRect.GetWidth() < aParaRect.GetWidth()) )
+ aCellRect.SetSize( Size( aParaRect.GetWidth(), aCellRect.GetHeight() ) );
+ }
+ }
+ }
+ if (aCellRect.IsEmpty())
+ aCellRect.SetPos(Point(-1, -1));
+ return aCellRect;
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL
+ ScAccessibleCell::getAccessibleChildCount()
+{
+ return AccessibleStaticTextBase::getAccessibleChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessibleCell::getAccessibleChild(sal_Int64 nIndex)
+{
+ return AccessibleStaticTextBase::getAccessibleChild(nIndex);
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ if (IsFocused())
+ nStateSet |= AccessibleStateType::FOCUSED;
+
+ if (IsFormulaMode())
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ if (IsOpaque())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if (IsSelected())
+ nStateSet |= AccessibleStateType::SELECTED;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ return nStateSet;
+ }
+ if (IsEditable(nParentStates))
+ {
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::RESIZABLE;
+ }
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if (IsOpaque())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if (IsSelected())
+ nStateSet |= AccessibleStateType::SELECTED;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ ScAccessibleCell::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ if (mpAccDoc)
+ pRelationSet = mpAccDoc->GetRelationSet(&maCellAddress);
+ if (!pRelationSet)
+ pRelationSet = new utl::AccessibleRelationSetHelper();
+ FillDependents(pRelationSet.get());
+ FillPrecedents(pRelationSet.get());
+ return pRelationSet;
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleCell::getImplementationName()
+{
+ return "ScAccessibleCell";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleCell::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.sheet.AccessibleCell" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+ //==== internal =========================================================
+
+bool ScAccessibleCell::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+bool ScAccessibleCell::IsEditable(sal_Int64 nParentStates)
+{
+ bool bEditable(true);
+ if ( !(nParentStates & AccessibleStateType::EDITABLE) &&
+ mpDoc)
+ {
+ // here I have to test whether the protection of the table should influence this cell.
+ const ScProtectionAttr* pItem = mpDoc->GetAttr(maCellAddress, ATTR_PROTECTION);
+ if (pItem)
+ bEditable = !pItem->GetProtection();
+ }
+ return bEditable;
+}
+
+bool ScAccessibleCell::IsOpaque() const
+{
+ // test whether there is a background color
+ bool bOpaque(true);
+ if (mpDoc)
+ {
+ const SvxBrushItem* pItem = mpDoc->GetAttr(maCellAddress, ATTR_BACKGROUND);
+ if (pItem)
+ bOpaque = pItem->GetColor() != COL_TRANSPARENT;
+ }
+ return bOpaque;
+}
+
+bool ScAccessibleCell::IsFocused() const
+{
+ if (mpViewShell && mpViewShell->GetViewData().GetCurPos() == maCellAddress)
+ return mpViewShell->GetActiveWin()->HasFocus();
+
+ return false;
+}
+
+bool ScAccessibleCell::IsSelected()
+{
+ if (IsFormulaMode())
+ {
+ const ScAccessibleSpreadsheet *pSheet =static_cast<const ScAccessibleSpreadsheet*>(mxParent.get());
+ if (pSheet)
+ {
+ return pSheet->IsScAddrFormulaSel(maCellAddress);
+ }
+ return false;
+ }
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsCellMarked(maCellAddress.Col(), maCellAddress.Row());
+ }
+ return bResult;
+}
+
+ScDocument* ScAccessibleCell::GetDocument(ScTabViewShell* pViewShell)
+{
+ ScDocument* pDoc = nullptr;
+ if (pViewShell)
+ pDoc = &pViewShell->GetViewData().GetDocument();
+ return pDoc;
+}
+
+::std::unique_ptr< SvxEditSource > ScAccessibleCell::CreateEditSource(ScTabViewShell* pViewShell, ScAddress aCell, ScSplitPos eSplitPos)
+{
+ if (IsFormulaMode())
+ {
+ return ::std::unique_ptr< SvxEditSource >();
+ }
+ ::std::unique_ptr< SvxEditSource > pEditSource (new ScAccessibilityEditSource(std::make_unique<ScAccessibleCellTextData>(pViewShell, aCell, eSplitPos, this)));
+
+ return pEditSource;
+}
+
+void ScAccessibleCell::FillDependents(utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ if (!mpDoc)
+ return;
+
+ ScRange aRange(0, 0, maCellAddress.Tab(), mpDoc->MaxCol(), mpDoc->MaxRow(), maCellAddress.Tab());
+ ScCellIterator aCellIter(*mpDoc, aRange);
+
+ for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
+ {
+ if (aCellIter.getType() == CELLTYPE_FORMULA)
+ {
+ bool bFound = false;
+ ScDetectiveRefIter aIter(*mpDoc, aCellIter.getFormulaCell());
+ ScRange aRef;
+ while ( !bFound && aIter.GetNextRef( aRef ) )
+ {
+ if (aRef.Contains(maCellAddress))
+ bFound = true;
+ }
+ if (bFound)
+ AddRelation(aCellIter.GetPos(), AccessibleRelationType::CONTROLLER_FOR, pRelationSet);
+ }
+ }
+}
+
+void ScAccessibleCell::FillPrecedents(utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ if (!mpDoc)
+ return;
+
+ ScRefCellValue aCell(*mpDoc, maCellAddress);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pCell = aCell.getFormula();
+ ScDetectiveRefIter aIter(*mpDoc, pCell);
+ ScRange aRef;
+ while ( aIter.GetNextRef( aRef ) )
+ {
+ AddRelation( aRef, AccessibleRelationType::CONTROLLED_BY, pRelationSet);
+ }
+ }
+}
+
+void ScAccessibleCell::AddRelation(const ScAddress& rCell,
+ const sal_uInt16 aRelationType,
+ utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ AddRelation(ScRange(rCell, rCell), aRelationType, pRelationSet);
+}
+
+void ScAccessibleCell::AddRelation(const ScRange& rRange,
+ const sal_uInt16 aRelationType,
+ utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ uno::Reference < XAccessibleTable > xTable ( getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY );
+ if (!xTable.is())
+ return;
+
+ const sal_uInt32 nCount(static_cast<sal_uInt32>(rRange.aEnd.Col() -
+ rRange.aStart.Col() + 1) * (rRange.aEnd.Row() -
+ rRange.aStart.Row() + 1));
+
+ // tdf#157299 avoid handling a large amount of cells for performance reasons
+ if (nCount > 1000)
+ {
+ SAL_WARN("sc", "ScAccessibleCell::AddRelation: Not setting relations "
+ "for cell range with more than 1000 cells for performance reasons.");
+ return;
+ }
+
+ uno::Sequence < uno::Reference < uno::XInterface > > aTargetSet( nCount );
+ uno::Reference < uno::XInterface >* pTargetSet = aTargetSet.getArray();
+ sal_uInt32 nPos(0);
+ for (sal_uInt32 nRow = rRange.aStart.Row(); nRow <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Row()); ++nRow)
+ {
+ for (sal_uInt32 nCol = rRange.aStart.Col(); nCol <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Col()); ++nCol)
+ {
+ pTargetSet[nPos] = xTable->getAccessibleCellAt(nRow, nCol);
+ ++nPos;
+ }
+ }
+ OSL_ENSURE(nCount == nPos, "something went wrong");
+ AccessibleRelation aRelation;
+ aRelation.RelationType = aRelationType;
+ aRelation.TargetSet = aTargetSet;
+ pRelationSet->AddRelation(aRelation);
+}
+
+static OUString ReplaceFourChar(const OUString& oldOUString)
+{
+ return oldOUString.replaceAll(u"\\", u"\\\\")
+ .replaceAll(u";", u"\\;")
+ .replaceAll(u"=", u"\\=")
+ .replaceAll(u",", u"\\,")
+ .replaceAll(u":", u"\\:");
+}
+
+uno::Any SAL_CALL ScAccessibleCell::getExtendedAttributes()
+{
+ SolarMutexGuard aGuard;
+
+ // report row and column index text via attributes as specified in ARIA which map
+ // to attributes of the same name for AT-SPI2, IAccessible2, UIA
+ // https://www.w3.org/TR/core-aam-1.2/#ariaRowIndexText
+ // https://www.w3.org/TR/core-aam-1.2/#ariaColIndexText
+ const OUString sRowIndexText = maCellAddress.Format(ScRefFlags::ROW_VALID);
+ const OUString sColIndexText = maCellAddress.Format(ScRefFlags::COL_VALID);
+ OUString sAttributes = "rowindextext:" + sRowIndexText + ";colindextext:" + sColIndexText + ";";
+
+ if (mpViewShell)
+ {
+ OUString strFor = mpViewShell->GetFormula(maCellAddress) ;
+ if (!strFor.isEmpty())
+ {
+ strFor = strFor.copy(1);
+ strFor = ReplaceFourChar(strFor);
+ }
+ strFor = "Formula:" + strFor +
+ ";Note:" +
+ ReplaceFourChar(GetAllDisplayNote()) + ";" +
+ getShadowAttrs() + //the string returned contains the spliter ";"
+ getBorderAttrs();//the string returned contains the spliter ";"
+ //end of cell attributes
+ if( mpDoc )
+ {
+ strFor += "isdropdown:";
+ if( IsDropdown() )
+ strFor += "true";
+ else
+ strFor += "false";
+ strFor += ";";
+ }
+ sAttributes += strFor ;
+ }
+
+ return uno::Any(sAttributes);
+}
+
+// cell has its own ParaIndent property, so when calling character attributes on cell, the ParaIndent should replace the ParaLeftMargin if its value is not zero.
+uno::Sequence< beans::PropertyValue > SAL_CALL ScAccessibleCell::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< beans::PropertyValue > aAttribs = AccessibleStaticTextBase::getCharacterAttributes( nIndex, aRequestedAttributes );
+
+ sal_uInt16 nParaIndent = mpDoc->GetAttr( maCellAddress, ATTR_INDENT )->GetValue();
+ if (nParaIndent > 0)
+ {
+ auto [begin, end] = asNonConstRange(aAttribs);
+ auto pAttrib = std::find_if(begin, end,
+ [](const beans::PropertyValue& rAttrib) { return "ParaLeftMargin" == rAttrib.Name; });
+ if (pAttrib != end)
+ pAttrib->Value <<= nParaIndent;
+ }
+ return aAttribs;
+}
+
+bool ScAccessibleCell::IsFormulaMode()
+{
+ ScAccessibleSpreadsheet* pSheet = static_cast<ScAccessibleSpreadsheet*>(mxParent.get());
+ if (pSheet)
+ {
+ return pSheet->IsFormulaMode();
+ }
+ return false;
+}
+
+bool ScAccessibleCell::IsDropdown() const
+{
+ sal_uInt16 nPosX = maCellAddress.Col();
+ sal_uInt16 nPosY = sal_uInt16(maCellAddress.Row());
+ sal_uInt16 nTab = maCellAddress.Tab();
+ sal_uInt32 nValidation = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = mpDoc->GetValidationEntry( nValidation );
+ if( pData && pData->HasSelectionList() )
+ return true;
+ }
+ const ScMergeFlagAttr* pAttr = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_MERGE_FLAG );
+ if( pAttr->HasAutoFilter() )
+ {
+ return true;
+ }
+ else
+ {
+ sal_uInt16 nTabCount = mpDoc->GetTableCount();
+ if ( nTab+1<nTabCount && mpDoc->IsScenario(nTab+1) && !mpDoc->IsScenario(nTab) )
+ {
+ SCTAB i;
+ ScMarkData aMarks(mpDoc->GetSheetLimits());
+ for (i=nTab+1; i<nTabCount && mpDoc->IsScenario(i); i++)
+ mpDoc->MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
+ ScRangeList aRanges;
+ aMarks.FillRangeListWithMarks( &aRanges, false );
+ bool bHasScenario;
+ SCTAB nRangeCount = aRanges.size();
+ for (i=0; i<nRangeCount; i++)
+ {
+ ScRange aRange = aRanges[i];
+ mpDoc->ExtendTotalMerge( aRange );
+ bool bTextBelow = ( aRange.aStart.Row() == 0 );
+ // MT IA2: Not used: sal_Bool bIsInScen = sal_False;
+ if ( bTextBelow )
+ {
+ bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aEnd.Row() == nPosY-1);
+ }
+ else
+ {
+ bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aStart.Row() == nPosY+1);
+ }
+ if( bHasScenario ) return true;
+ }
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleCellBase.cxx b/sc/source/ui/Accessibility/AccessibleCellBase.cxx
new file mode 100644
index 0000000000..d8b84fabaf
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleCellBase.cxx
@@ -0,0 +1,587 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleCellBase.hxx>
+#include <document.hxx>
+#include <docfunc.hxx>
+#include <docsh.hxx>
+#include <strings.hxx>
+#include <unonames.hxx>
+#include <detfunc.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSheetAnnotationAnchor.hpp>
+#include <com/sun/star/text/XSimpleText.hpp>
+#include <com/sun/star/table/BorderLine.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <comphelper/sequence.hxx>
+#include <sfx2/objsh.hxx>
+#include <vcl/svapp.hxx>
+
+#include <float.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+#define DEFAULT_LINE_WIDTH 2
+
+//===== internal ============================================================
+
+ScAccessibleCellBase::ScAccessibleCellBase(
+ const uno::Reference<XAccessible>& rxParent,
+ ScDocument* pDoc,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex)
+ :
+ ScAccessibleContextBase(rxParent, AccessibleRole::TABLE_CELL),
+ maCellAddress(rCellAddress),
+ mpDoc(pDoc),
+ mnIndex(nIndex)
+{
+}
+
+ScAccessibleCellBase::~ScAccessibleCellBase()
+{
+}
+
+ //===== XAccessibleComponent ============================================
+
+bool ScAccessibleCellBase::isVisible()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ // test whether the cell is hidden (column/row - hidden/filtered)
+ bool bVisible(true);
+ if (mpDoc)
+ {
+ bool bColHidden = mpDoc->ColHidden(maCellAddress.Col(), maCellAddress.Tab());
+ bool bRowHidden = mpDoc->RowHidden(maCellAddress.Row(), maCellAddress.Tab());
+ bool bColFiltered = mpDoc->ColFiltered(maCellAddress.Col(), maCellAddress.Tab());
+ bool bRowFiltered = mpDoc->RowFiltered(maCellAddress.Row(), maCellAddress.Tab());
+
+ if (bColHidden || bColFiltered || bRowHidden || bRowFiltered)
+ bVisible = false;
+ }
+ return bVisible;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCellBase::getForeground()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int32 nColor(0);
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_CCOLOR);
+ aAny >>= nColor;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nColor;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCellBase::getBackground()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int32 nColor(0);
+
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_CELLBACK);
+ aAny >>= nColor;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nColor;
+}
+
+ //===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessibleCellBase::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessibleCellBaseImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessibleCellBase::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessibleCellBase::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64
+ ScAccessibleCellBase::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return mnIndex;
+}
+
+OUString
+ ScAccessibleCellBase::createAccessibleDescription()
+{
+ return STR_ACC_CELL_DESCR;
+}
+
+OUString
+ ScAccessibleCellBase::createAccessibleName()
+{
+ // Document not needed, because only the cell address, but not the tablename is needed
+ // always us OOO notation
+ return maCellAddress.Format(ScRefFlags::VALID);
+}
+
+ //===== XAccessibleValue ================================================
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getCurrentValue()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Any aAny;
+ if (mpDoc)
+ {
+ aAny <<= mpDoc->GetValue(maCellAddress);
+ }
+ return aAny;
+}
+
+sal_Bool SAL_CALL
+ ScAccessibleCellBase::setCurrentValue( const uno::Any& aNumber )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ double fValue = 0;
+ bool bResult = false;
+ if((aNumber >>= fValue) && mpDoc && mpDoc->GetDocumentShell())
+ {
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ if (IsEditable(nParentStates))
+ {
+ ScDocShell* pDocShell = mpDoc->GetDocumentShell();
+ bResult = pDocShell->GetDocFunc().SetValueCell(maCellAddress, fValue, false);
+ }
+ }
+ return bResult;
+}
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getMaximumValue( )
+{
+ return uno::Any(DBL_MAX);
+}
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getMinimumValue( )
+{
+ return uno::Any(-DBL_MAX);
+}
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getMinimumIncrement( )
+{
+ return uno::Any();
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleCellBase::getImplementationName()
+{
+ return "ScAccessibleCellBase";
+}
+
+ //===== XTypeProvider ===================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessibleCellBase::getTypes()
+{
+ return comphelper::concatSequences(ScAccessibleCellBaseImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleCellBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+bool ScAccessibleCellBase::IsEditable(sal_Int64 nParentStates)
+{
+ bool bEditable = nParentStates & AccessibleStateType::EDITABLE;
+ return bEditable;
+}
+
+OUString ScAccessibleCellBase::GetNote() const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ OUString sNote;
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference <sheet::XSheetAnnotationAnchor> xAnnotationAnchor ( xCell, uno::UNO_QUERY);
+ if(xAnnotationAnchor.is())
+ {
+ uno::Reference <sheet::XSheetAnnotation> xSheetAnnotation = xAnnotationAnchor->getAnnotation();
+ if (xSheetAnnotation.is())
+ {
+ uno::Reference <text::XSimpleText> xText (xSheetAnnotation, uno::UNO_QUERY);
+ if (xText.is())
+ {
+ sNote = xText->getString();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return sNote;
+}
+
+OUString ScAccessibleCellBase::getShadowAttrs() const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ table::ShadowFormat aShadowFmt;
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_SHADOW);
+ aAny >>= aShadowFmt;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ //construct shadow attributes string
+ OUString sShadowAttrs("Shadow:");
+ OUString sInnerSplit(",");
+ OUString sOuterSplit(";");
+ sal_Int32 nLocationVal = 0;
+ switch( aShadowFmt.Location )
+ {
+ case table::ShadowLocation_TOP_LEFT:
+ nLocationVal = 1;
+ break;
+ case table::ShadowLocation_TOP_RIGHT:
+ nLocationVal = 2;
+ break;
+ case table::ShadowLocation_BOTTOM_LEFT:
+ nLocationVal = 3;
+ break;
+ case table::ShadowLocation_BOTTOM_RIGHT:
+ nLocationVal = 4;
+ break;
+ default:
+ break;
+ }
+ //if there is no shadow property for the cell
+ if ( nLocationVal == 0 )
+ {
+ sShadowAttrs += sOuterSplit;
+ return sShadowAttrs;
+ }
+ //else return all the shadow properties
+ sShadowAttrs += "Location=" +
+ OUString::number( nLocationVal ) +
+ sInnerSplit +
+ "ShadowWidth=" +
+ OUString::number( static_cast<sal_Int32>(aShadowFmt.ShadowWidth) ) +
+ sInnerSplit +
+ "IsTransparent=" +
+ OUString::number( static_cast<int>(aShadowFmt.IsTransparent) ) +
+ sInnerSplit +
+ "Color=" +
+ OUString::number( aShadowFmt.Color ) +
+ sOuterSplit;
+ return sShadowAttrs;
+}
+
+OUString ScAccessibleCellBase::getBorderAttrs()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ table::BorderLine aTopBorder;
+ table::BorderLine aBottomBorder;
+ table::BorderLine aLeftBorder;
+ table::BorderLine aRightBorder;
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_TOPBORDER);
+ aAny >>= aTopBorder;
+ aAny = xCellProps->getPropertyValue(SC_UNONAME_BOTTBORDER);
+ aAny >>= aBottomBorder;
+ aAny = xCellProps->getPropertyValue(SC_UNONAME_LEFTBORDER);
+ aAny >>= aLeftBorder;
+ aAny = xCellProps->getPropertyValue(SC_UNONAME_RIGHTBORDER);
+ aAny >>= aRightBorder;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Color aColor;
+ bool bIn = mpDoc && mpDoc->IsCellInChangeTrack(maCellAddress,&aColor);
+ if (bIn)
+ {
+ aTopBorder.Color = sal_Int32(aColor);
+ aBottomBorder.Color = sal_Int32(aColor);
+ aLeftBorder.Color = sal_Int32(aColor);
+ aRightBorder.Color = sal_Int32(aColor);
+ aTopBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ aBottomBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ aLeftBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ aRightBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ }
+
+ //construct border attributes string
+ OUString sBorderAttrs;
+ OUString sInnerSplit(",");
+ OUString sOuterSplit(";");
+ //top border
+ //if top of the cell has no border
+ if ( aTopBorder.InnerLineWidth == 0 && aTopBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "TopBorder:;";
+ }
+ else//add all the border properties to the return string.
+ {
+ sBorderAttrs += "TopBorder:Color=" +
+ OUString::number( aTopBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aTopBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aTopBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aTopBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ //bottom border
+ if ( aBottomBorder.InnerLineWidth == 0 && aBottomBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "BottomBorder:;";
+ }
+ else
+ {
+ sBorderAttrs += "BottomBorder:Color=" +
+ OUString::number( aBottomBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aBottomBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aBottomBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aBottomBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ //left border
+ if ( aLeftBorder.InnerLineWidth == 0 && aLeftBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "LeftBorder:;";
+ }
+ else
+ {
+ sBorderAttrs += "LeftBorder:Color=" +
+ OUString::number( aLeftBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aLeftBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aLeftBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aLeftBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ //right border
+ if ( aRightBorder.InnerLineWidth == 0 && aRightBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "RightBorder:;";
+ }
+ else
+ {
+ sBorderAttrs += "RightBorder:Color=" +
+ OUString::number( aRightBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aRightBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aRightBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aRightBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ return sBorderAttrs;
+}
+//end of cell attributes
+
+OUString ScAccessibleCellBase::GetAllDisplayNote() const
+{
+ OUString strNote;
+ OUString strTrackText;
+ if (mpDoc)
+ {
+ bool bLeftedge = false;
+ mpDoc->GetCellChangeTrackNote(maCellAddress,strTrackText,bLeftedge);
+ }
+ if (!strTrackText.isEmpty())
+ {
+ ScDetectiveFunc::AppendChangTrackNoteSeparator(strTrackText);
+ strNote = strTrackText;
+ }
+ strNote += GetNote();
+ return strNote;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleContextBase.cxx b/sc/source/ui/Accessibility/AccessibleContextBase.cxx
new file mode 100644
index 0000000000..59f2f39903
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleContextBase.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 <AccessibleContextBase.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
+#include <tools/gen.hxx>
+#include <tools/color.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <svl/hint.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <utility>
+#include <vcl/unohelp.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+/**
+ The listener is an internal class to prevent reference-counting cycles and therefore memory leaks.
+*/
+typedef cppu::WeakComponentImplHelper<
+ css::accessibility::XAccessibleEventListener
+ > ScAccessibleContextBaseEventListenerWeakImpl;
+class ScAccessibleContextBase::ScAccessibleContextBaseEventListener : public cppu::BaseMutex, public ScAccessibleContextBaseEventListenerWeakImpl
+{
+public:
+ ScAccessibleContextBaseEventListener(ScAccessibleContextBase& rBase)
+ : ScAccessibleContextBaseEventListenerWeakImpl(m_aMutex), mrBase(rBase) {}
+
+ using WeakComponentImplHelperBase::disposing;
+
+ ///===== XAccessibleEventListener ========================================
+
+ virtual void SAL_CALL disposing( const lang::EventObject& rSource ) override
+ {
+ SolarMutexGuard aGuard;
+ if (rSource.Source == mrBase.mxParent)
+ dispose();
+ }
+
+ virtual void SAL_CALL
+ notifyEvent(
+ const css::accessibility::AccessibleEventObject& /*aEvent*/ ) override {}
+private:
+ ScAccessibleContextBase& mrBase;
+};
+
+
+ScAccessibleContextBase::ScAccessibleContextBase(
+ uno::Reference<XAccessible> xParent,
+ const sal_Int16 aRole)
+ :
+ ScAccessibleContextBaseWeakImpl(m_aMutex),
+ mxParent(std::move(xParent)),
+ mnClientId(0),
+ maRole(aRole)
+{
+}
+
+ScAccessibleContextBase::~ScAccessibleContextBase()
+{
+ if (!IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void ScAccessibleContextBase::Init()
+{
+ // hold reference to make sure that the destructor is not called
+ uno::Reference< XAccessibleContext > xKeepAlive(this);
+
+ if (mxParent.is())
+ {
+ uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ {
+ if (!mxEventListener)
+ mxEventListener = new ScAccessibleContextBaseEventListener(*this);
+ xBroadcaster->addAccessibleEventListener(mxEventListener);
+ }
+ }
+ msName = createAccessibleName();
+ msDescription = createAccessibleDescription();
+}
+
+void SAL_CALL ScAccessibleContextBase::disposing()
+{
+ SolarMutexGuard aGuard;
+// CommitDefunc(); not necessary and should not be send, because it cost a lot of time
+
+ // hold reference to make sure that the destructor is not called
+ uno::Reference< XAccessibleContext > xKeepAlive(this);
+
+ if ( mnClientId )
+ {
+ sal_Int32 nTemClientId(mnClientId);
+ mnClientId = 0;
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nTemClientId, *this );
+ }
+
+ if (mxParent.is())
+ {
+ uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xBroadcaster && mxEventListener)
+ xBroadcaster->removeAccessibleEventListener(mxEventListener);
+ mxParent = nullptr;
+ }
+}
+
+
+//===== SfxListener =====================================================
+
+void ScAccessibleContextBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ // it seems the Broadcaster is dying, since the view is dying
+ dispose();
+ }
+}
+
+//===== XAccessible =========================================================
+
+uno::Reference< XAccessibleContext> SAL_CALL
+ ScAccessibleContextBase::getAccessibleContext()
+{
+ return this;
+}
+
+//===== XAccessibleComponent ================================================
+
+sal_Bool SAL_CALL ScAccessibleContextBase::containsPoint(const awt::Point& rPoint )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return tools::Rectangle (Point(), GetBoundingBox().GetSize()).Contains(VCLPoint(rPoint));
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleContextBase::getAccessibleAtPoint(
+ const awt::Point& /* rPoint */ )
+{
+ OSL_FAIL("not implemented");
+ return uno::Reference<XAccessible>();
+}
+
+awt::Rectangle SAL_CALL ScAccessibleContextBase::getBounds( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTRectangle(GetBoundingBox());
+}
+
+awt::Point SAL_CALL ScAccessibleContextBase::getLocation( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTPoint(GetBoundingBox().TopLeft());
+}
+
+awt::Point SAL_CALL ScAccessibleContextBase::getLocationOnScreen( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTPoint(GetBoundingBoxOnScreen().TopLeft());
+}
+
+awt::Size SAL_CALL ScAccessibleContextBase::getSize( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTSize(GetBoundingBox().GetSize());
+}
+
+bool ScAccessibleContextBase::isShowing( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ bool bShowing(false);
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleComponent> xParentComponent (mxParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ tools::Rectangle aParentBounds(VCLRectangle(xParentComponent->getBounds()));
+ tools::Rectangle aBounds(VCLRectangle(getBounds()));
+ bShowing = aBounds.Overlaps(aParentBounds);
+ }
+ }
+ return bShowing;
+}
+
+bool ScAccessibleContextBase::isVisible()
+{
+ return true;
+}
+
+void SAL_CALL ScAccessibleContextBase::grabFocus( )
+{
+ OSL_FAIL("not implemented");
+}
+
+sal_Int32 SAL_CALL ScAccessibleContextBase::getForeground( )
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL ScAccessibleContextBase::getBackground( )
+{
+ return sal_Int32(COL_WHITE);
+}
+
+//===== XAccessibleContext ==================================================
+
+sal_Int64 SAL_CALL ScAccessibleContextBase::getAccessibleChildCount()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return 0;
+}
+
+uno::Reference<XAccessible> SAL_CALL
+ ScAccessibleContextBase::getAccessibleChild(sal_Int64 /* nIndex */)
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return uno::Reference<XAccessible>();
+}
+
+uno::Reference<XAccessible> SAL_CALL
+ ScAccessibleContextBase::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleContextBase::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ // Use a simple but slow solution for now. Optimize later.
+ // Return -1 to indicate that this object's parent does not know about the
+ // object.
+ sal_Int64 nIndex(-1);
+
+ // Iterate over all the parent's children and search for this object.
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext (
+ mxParent->getAccessibleContext());
+ if (xParentContext.is())
+ {
+ sal_Int64 nChildCount = xParentContext->getAccessibleChildCount();
+ for (sal_Int64 i=0; i<nChildCount; ++i)
+ {
+ uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
+ if (xChild.is() && xChild.get() == this)
+ nIndex = i;
+ }
+ }
+ }
+
+ return nIndex;
+}
+
+sal_Int16 SAL_CALL
+ ScAccessibleContextBase::getAccessibleRole()
+{
+ return maRole;
+}
+
+OUString SAL_CALL
+ ScAccessibleContextBase::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (msDescription.isEmpty())
+ {
+ OUString sDescription(createAccessibleDescription());
+
+ if (msDescription != sDescription)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= msDescription;
+ aEvent.NewValue <<= sDescription;
+
+ msDescription = sDescription;
+
+ CommitChange(aEvent);
+ }
+ }
+ return msDescription;
+}
+
+OUString SAL_CALL
+ ScAccessibleContextBase::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (msName.isEmpty())
+ {
+ OUString sName(createAccessibleName());
+ OSL_ENSURE(!sName.isEmpty(), "We should give always a name.");
+
+ if (msName != sName)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= msName;
+ aEvent.NewValue <<= sName;
+
+ msName = sName;
+
+ CommitChange(aEvent);
+ }
+ }
+ return msName;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ ScAccessibleContextBase::getAccessibleRelationSet()
+{
+ return new utl::AccessibleRelationSetHelper();
+}
+
+sal_Int64 SAL_CALL ScAccessibleContextBase::getAccessibleStateSet()
+{
+ return 0;
+}
+
+lang::Locale SAL_CALL
+ ScAccessibleContextBase::getLocale()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext (
+ mxParent->getAccessibleContext());
+ if (xParentContext.is())
+ return xParentContext->getLocale ();
+ }
+
+ // No locale and no parent. Therefore throw exception to indicate this
+ // cluelessness.
+ throw IllegalAccessibleComponentStateException ();
+}
+
+ //===== XAccessibleEventBroadcaster =====================================
+
+void SAL_CALL
+ ScAccessibleContextBase::addAccessibleEventListener(
+ const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ if (xListener.is())
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!IsDefunc())
+ {
+ if (!mnClientId)
+ mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener );
+ }
+ }
+}
+
+void SAL_CALL
+ ScAccessibleContextBase::removeAccessibleEventListener(
+ const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ if (!xListener.is())
+ return;
+
+ SolarMutexGuard aGuard;
+ if (IsDefunc() || !mnClientId)
+ return;
+
+ 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;
+ }
+}
+
+// XServiceInfo
+OUString SAL_CALL ScAccessibleContextBase::getImplementationName()
+{
+ return "ScAccessibleContextBase";
+}
+
+sal_Bool SAL_CALL ScAccessibleContextBase::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleContextBase::getSupportedServiceNames()
+{
+ return {"com.sun.star.accessibility.Accessible",
+ "com.sun.star.accessibility.AccessibleContext"};
+}
+
+//===== internal ============================================================
+
+OUString
+ ScAccessibleContextBase::createAccessibleDescription()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return OUString();
+}
+
+OUString ScAccessibleContextBase::createAccessibleName()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return OUString();
+}
+
+void ScAccessibleContextBase::CommitChange(const AccessibleEventObject& rEvent) const
+{
+ if (mnClientId)
+ comphelper::AccessibleEventNotifier::addEvent( mnClientId, rEvent );
+}
+
+void ScAccessibleContextBase::CommitFocusGained() const
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
+ aEvent.NewValue <<= AccessibleStateType::FOCUSED;
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleContextBase::CommitFocusLost() const
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
+ aEvent.OldValue <<= AccessibleStateType::FOCUSED;
+
+ CommitChange(aEvent);
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleContextBase::GetBoundingBoxOnScreen() const
+{
+ OSL_FAIL("not implemented");
+ return AbsoluteScreenPixelRectangle();
+}
+
+tools::Rectangle ScAccessibleContextBase::GetBoundingBox() const
+{
+ OSL_FAIL("not implemented");
+ return tools::Rectangle();
+}
+
+void ScAccessibleContextBase::IsObjectValid() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ throw lang::DisposedException();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleCsvControl.cxx b/sc/source/ui/Accessibility/AccessibleCsvControl.cxx
new file mode 100644
index 0000000000..c7050d7777
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleCsvControl.cxx
@@ -0,0 +1,1405 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <utility>
+
+#include <AccessibleCsvControl.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <scitems.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/langitem.hxx>
+#include <csvtablebox.hxx>
+#include <csvcontrol.hxx>
+#include <csvruler.hxx>
+#include <csvgrid.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <scmod.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/string_view.hxx>
+
+using ::utl::AccessibleRelationSetHelper;
+using ::accessibility::AccessibleStaticTextBase;
+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 ::com::sun::star::uno::XInterface;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using ::com::sun::star::beans::PropertyValue;
+using namespace ::com::sun::star::accessibility;
+
+const sal_Unicode cRulerDot = '.';
+const sal_Unicode cRulerLine = '|';
+
+const sal_Int32 CSV_LINE_HEADER = CSV_POS_INVALID;
+const sal_uInt32 CSV_COLUMN_HEADER = CSV_COLUMN_INVALID;
+
+ScAccessibleCsvControl::ScAccessibleCsvControl(ScCsvControl& rControl)
+ : mpControl(&rControl)
+{
+}
+
+ScAccessibleCsvControl::~ScAccessibleCsvControl()
+{
+ ensureDisposed();
+}
+
+void SAL_CALL ScAccessibleCsvControl::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpControl = nullptr;
+ comphelper::OAccessibleComponentHelper::disposing();
+}
+
+// XAccessibleComponent -------------------------------------------------------
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvControl::getAccessibleAtPoint( const css::awt::Point& /* rPoint */ )
+{
+ ensureAlive();
+ return nullptr;
+}
+
+void SAL_CALL ScAccessibleCsvControl::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ implGetControl().GrabFocus();
+}
+
+// events ---------------------------------------------------------------------
+
+void ScAccessibleCsvControl::SendFocusEvent( bool bFocused )
+{
+ Any aOldAny, aNewAny;
+ if (bFocused)
+ aNewAny <<= AccessibleStateType::FOCUSED;
+ else
+ aOldAny <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny);
+}
+
+void ScAccessibleCsvControl::SendCaretEvent()
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendCaretEvent - Illegal call" );
+}
+
+void ScAccessibleCsvControl::SendVisibleEvent()
+{
+ NotifyAccessibleEvent(AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any());
+}
+
+void ScAccessibleCsvControl::SendSelectionEvent()
+{
+ NotifyAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, Any(), Any());
+}
+
+void ScAccessibleCsvControl::SendTableUpdateEvent( sal_uInt32 /* nFirstColumn */, sal_uInt32 /* nLastColumn */, bool /* bAllRows */ )
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendTableUpdateEvent - Illegal call" );
+}
+
+void ScAccessibleCsvControl::SendInsertColumnEvent( sal_uInt32 /* nFirstColumn */, sal_uInt32 /* nLastColumn */ )
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendInsertColumnEvent - Illegal call" );
+}
+
+void ScAccessibleCsvControl::SendRemoveColumnEvent( sal_uInt32 /* nFirstColumn */, sal_uInt32 /* nLastColumn */ )
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendRemoveColumnEvent - Illegal call" );
+}
+
+// helpers --------------------------------------------------------------------
+
+css::awt::Rectangle ScAccessibleCsvControl::implGetBounds()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ Size aOutSize(implGetControl().GetOutputSizePixel());
+ return css::awt::Rectangle(0, 0, aOutSize.Width(), aOutSize.Height());
+}
+
+ScCsvControl& ScAccessibleCsvControl::implGetControl() const
+{
+ assert(mpControl && "ScAccessibleCsvControl::implGetControl - missing control");
+ return *mpControl;
+}
+
+sal_Int64 ScAccessibleCsvControl::implCreateStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+ if (isAlive())
+ {
+ const ScCsvControl& rCtrl = implGetControl();
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if( rCtrl.IsEnabled() )
+ nStateSet |= AccessibleStateType::ENABLED;
+ if( rCtrl.IsReallyVisible() )
+ nStateSet |= AccessibleStateType::SHOWING;
+ if( rCtrl.IsVisible() )
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+ return nStateSet;
+}
+
+// Ruler ======================================================================
+
+/** Converts a ruler cursor position to API text index. */
+static sal_Int32 lcl_GetApiPos( sal_Int32 nRulerPos )
+{
+ sal_Int32 nApiPos = nRulerPos;
+ sal_Int32 nStart = (nRulerPos - 1) / 10;
+ sal_Int32 nExp = 1;
+ while( nStart >= nExp )
+ {
+ nApiPos += nStart - nExp + 1;
+ nExp *= 10;
+ }
+ return ::std::max( nApiPos, static_cast<sal_Int32>(0) );
+}
+
+/** Converts an API text index to a ruler cursor position. */
+static sal_Int32 lcl_GetRulerPos( sal_Int32 nApiPos )
+{
+ sal_Int32 nDiv = 10;
+ sal_Int32 nExp = 10;
+ sal_Int32 nRulerPos = 0;
+ sal_Int32 nApiBase = 0;
+ sal_Int32 nApiLimit = 10;
+ while( nApiPos >= nApiLimit )
+ {
+ ++nDiv;
+ nRulerPos = nExp;
+ nExp *= 10;
+ nApiBase = nApiLimit;
+ nApiLimit = lcl_GetApiPos( nExp );
+ }
+ sal_Int32 nRelPos = nApiPos - nApiBase;
+ return nRulerPos + nRelPos / nDiv * 10 + ::std::max<sal_Int32>( nRelPos % nDiv - nDiv + 10, 0 );
+}
+
+/** Expands the sequence's size and returns the base index of the new inserted elements. */
+static sal_Int32 lcl_ExpandSequence( Sequence< PropertyValue >& rSeq, sal_Int32 nExp )
+{
+ OSL_ENSURE( nExp > 0, "lcl_ExpandSequence - invalid value" );
+ rSeq.realloc( rSeq.getLength() + nExp );
+ return rSeq.getLength() - nExp;
+}
+
+/** Fills the property value rVal with the specified name and value from the item. */
+static void lcl_FillProperty( PropertyValue& rVal, const OUString& rPropName, const SfxPoolItem& rItem, sal_uInt8 nMID )
+{
+ rVal.Name = rPropName;
+ rItem.QueryValue( rVal.Value, nMID );
+}
+
+/** Fills the sequence with all font attributes of rFont. */
+static void lcl_FillFontAttributes( Sequence< PropertyValue >& rSeq, const vcl::Font& rFont )
+{
+ SvxFontItem aFontItem( rFont.GetFamilyType(), rFont.GetFamilyName(), rFont.GetStyleName(), rFont.GetPitch(), rFont.GetCharSet(), ATTR_FONT );
+ SvxFontHeightItem aHeightItem( rFont.GetFontSize().Height(), 100, ATTR_FONT_HEIGHT );
+ SvxLanguageItem aLangItem( rFont.GetLanguage(), ATTR_FONT_LANGUAGE );
+
+ sal_Int32 nIndex = lcl_ExpandSequence( rSeq, 7 );
+ auto pSeq = rSeq.getArray();
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontName", aFontItem, MID_FONT_FAMILY_NAME );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontFamily", aFontItem, MID_FONT_FAMILY );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontStyleName", aFontItem, MID_FONT_STYLE_NAME );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontCharSet", aFontItem, MID_FONT_PITCH );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontPitch", aFontItem, MID_FONT_CHAR_SET );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharHeight", aHeightItem, MID_FONTHEIGHT );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharLocale", aLangItem, MID_LANG_LOCALE );
+}
+
+ScAccessibleCsvRuler::ScAccessibleCsvRuler(ScCsvRuler& rRuler)
+ : ImplInheritanceHelper(rRuler)
+{
+ constructStringBuffer();
+}
+
+ScAccessibleCsvRuler::~ScAccessibleCsvRuler()
+{
+ ensureDisposed();
+}
+
+// XAccessibleComponent -----------------------------------------------------
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getForeground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetLabelTextColor());
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetFaceColor());
+}
+
+// XAccessibleContext ---------------------------------------------------------
+
+sal_Int64 SAL_CALL ScAccessibleCsvRuler::getAccessibleChildCount()
+{
+ ensureAlive();
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvRuler::getAccessibleChild( sal_Int64 /* nIndex */ )
+{
+ ensureAlive();
+ throw IndexOutOfBoundsException();
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvRuler::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ rtl::Reference<AccessibleRelationSetHelper> pRelationSet = new AccessibleRelationSetHelper();
+
+ ScCsvRuler& rRuler = implGetRuler();
+ ScCsvTableBox* pTableBox = rRuler.GetTableBox();
+ ScCsvGrid& rGrid = pTableBox->GetGrid();
+
+ css::uno::Reference<css::accessibility::XAccessible> xAccObj(static_cast<ScAccessibleCsvGrid*>(rGrid.GetAccessible()));
+ if( xAccObj.is() )
+ {
+ Sequence< Reference< XInterface > > aSeq{ xAccObj };
+ pRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::CONTROLLER_FOR, aSeq ) );
+ }
+
+ return pRelationSet;
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvRuler::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = implCreateStateSet();
+ if( isAlive() )
+ {
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ nStateSet |= AccessibleStateType::SINGLE_LINE;
+ if( implGetRuler().HasFocus() )
+ nStateSet |= AccessibleStateType::FOCUSED;
+ }
+ return nStateSet;
+}
+
+// XAccessibleText ------------------------------------------------------------
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getCaretPosition()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return lcl_GetApiPos( implGetRuler().GetRulerCursorPos() );
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::setCaretPosition( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nIndex );
+ ScCsvRuler& rRuler = implGetRuler();
+ sal_Int32 nOldCursor = rRuler.GetRulerCursorPos();
+ rRuler.Execute( CSVCMD_MOVERULERCURSOR, lcl_GetRulerPos( nIndex ) );
+ return rRuler.GetRulerCursorPos() != nOldCursor;
+}
+
+sal_Unicode SAL_CALL ScAccessibleCsvRuler::getCharacter( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nIndex );
+ return maBuffer[nIndex];
+}
+
+Sequence< PropertyValue > SAL_CALL ScAccessibleCsvRuler::getCharacterAttributes( sal_Int32 nIndex,
+ const css::uno::Sequence< OUString >& /* aRequestedAttributes */ )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+ Sequence< PropertyValue > aSeq;
+ lcl_FillFontAttributes( aSeq, implGetRuler().GetDrawingArea()->get_ref_device().GetFont() );
+ return aSeq;
+}
+
+css::awt::Rectangle SAL_CALL ScAccessibleCsvRuler::getCharacterBounds( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+ ScCsvRuler& rRuler = implGetRuler();
+ Point aPos( rRuler.GetX( lcl_GetRulerPos( nIndex ) ) - rRuler.GetCharWidth() / 2, 0 );
+ css::awt::Rectangle aRect( aPos.X(), aPos.Y(), rRuler.GetCharWidth(), rRuler.GetOutputSizePixel().Height() );
+ // do not return rectangle out of window
+ sal_Int32 nWidth = rRuler.GetOutputSizePixel().Width();
+ if( aRect.X >= nWidth )
+ throw IndexOutOfBoundsException();
+ if( aRect.X + aRect.Width > nWidth )
+ aRect.Width = nWidth - aRect.X;
+ return aRect;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getCharacterCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetTextLength();
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getIndexAtPoint( const css::awt::Point& rPoint )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ScCsvRuler& rRuler = implGetRuler();
+ // use object's coordinate system, convert to API position
+ return lcl_GetApiPos( ::std::clamp( rRuler.GetPosFromX( rPoint.X ), sal_Int32(0), rRuler.GetPosCount() ) );
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getSelectedText()
+{
+ ensureAlive();
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getSelectionStart()
+{
+ ensureAlive();
+ return -1;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getSelectionEnd()
+{
+ ensureAlive();
+ return -1;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::setSelection( sal_Int32 /* nStartIndex */, sal_Int32 /* nEndIndex */ )
+{
+ ensureAlive();
+ return false;
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getText()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return OUString(maBuffer.subView( 0, implGetTextLength() ));
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidRange( nStartIndex, nEndIndex );
+ return OUString( maBuffer.getStr() + nStartIndex, nEndIndex - nStartIndex );
+}
+
+TextSegment SAL_CALL ScAccessibleCsvRuler::getTextAtIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ if( (nIndex == implGetTextLength()) && (nTextType != AccessibleTextType::LINE) )
+ return aResult;
+
+ ensureValidIndex( nIndex );
+
+ OUStringBuffer aResultText; // will be assigned to aResult.SegmentText below
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nIndex );
+
+ switch( nTextType )
+ {
+ // single character
+ case AccessibleTextType::CHARACTER:
+ {
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex;
+ o3tl::iterateCodePoints(maBuffer, &aResult.SegmentEnd);
+ for (; nIndex < aResult.SegmentEnd; nIndex++)
+ aResultText.append(maBuffer[nIndex]);
+ }
+ break;
+
+ // entire number or single dot/line
+ case AccessibleTextType::WORD:
+ case AccessibleTextType::GLYPH:
+ aResult.SegmentStart = nIndex;
+ if( nRulerPos % 10 )
+ aResultText.append(maBuffer[nIndex]);
+ else
+ aResultText.append( nRulerPos ); // string representation of sal_Int32!!!
+ break;
+
+ // entire text
+ case AccessibleTextType::SENTENCE:
+ case AccessibleTextType::PARAGRAPH:
+ case AccessibleTextType::LINE:
+ aResult.SegmentStart = 0;
+ aResultText.append( maBuffer.getStr(), implGetTextLength() );
+ break;
+
+ // equal-formatted text
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nFirstIndex = implGetFirstEqualFormatted( nIndex );
+ sal_Int32 nLastIndex = implGetLastEqualFormatted( nIndex );
+ aResult.SegmentStart = nFirstIndex;
+ aResultText.append( maBuffer.getStr() + nFirstIndex, nLastIndex - nFirstIndex + 1 );
+ }
+ break;
+
+ default:
+ throw RuntimeException();
+ }
+
+ aResult.SegmentText = aResultText.makeStringAndClear();
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ return aResult;
+}
+
+TextSegment SAL_CALL ScAccessibleCsvRuler::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nIndex );
+
+ switch( nTextType )
+ {
+ // single character
+ case AccessibleTextType::CHARACTER:
+ if( nIndex > 0 )
+ {
+ o3tl::iterateCodePoints(maBuffer, &nIndex, -1);
+ aResult = getTextAtIndex(nIndex, nTextType);
+ }
+ // else empty
+ break;
+
+ // entire number or single dot/line
+ case AccessibleTextType::WORD:
+ case AccessibleTextType::GLYPH:
+ if( nRulerPos > 0 )
+ aResult = getTextAtIndex( lcl_GetApiPos( nRulerPos - 1 ), nTextType );
+ // else empty
+ break;
+
+ // entire text
+ case AccessibleTextType::SENTENCE:
+ case AccessibleTextType::PARAGRAPH:
+ case AccessibleTextType::LINE:
+ // empty
+ break;
+
+ // equal-formatted text
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nFirstIndex = implGetFirstEqualFormatted( nIndex );
+ if( nFirstIndex > 0 )
+ aResult = getTextAtIndex( nFirstIndex - 1, nTextType );
+ // else empty
+ }
+ break;
+
+ default:
+ throw RuntimeException();
+ }
+ return aResult;
+}
+
+TextSegment SAL_CALL ScAccessibleCsvRuler::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nIndex );
+ sal_Int32 nLastValid = implGetTextLength();
+
+ switch( nTextType )
+ {
+ // single character
+ case AccessibleTextType::CHARACTER:
+ if( nIndex < nLastValid )
+ {
+ o3tl::iterateCodePoints(maBuffer, &nIndex);
+ aResult = getTextAtIndex(nIndex, nTextType);
+ }
+ // else empty
+ break;
+
+ // entire number or single dot/line
+ case AccessibleTextType::WORD:
+ case AccessibleTextType::GLYPH:
+ if( nRulerPos < implGetRuler().GetPosCount() )
+ aResult = getTextAtIndex( lcl_GetApiPos( nRulerPos + 1 ), nTextType );
+ // else empty
+ break;
+
+ // entire text
+ case AccessibleTextType::SENTENCE:
+ case AccessibleTextType::PARAGRAPH:
+ case AccessibleTextType::LINE:
+ // empty
+ break;
+
+ // equal-formatted text
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nLastIndex = implGetLastEqualFormatted( nIndex );
+ if( nLastIndex < nLastValid )
+ aResult = getTextAtIndex( nLastIndex + 1, nTextType );
+ // else empty
+ }
+ break;
+
+ default:
+ throw RuntimeException();
+ }
+ return aResult;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::copyText( sal_Int32 /* nStartIndex */, sal_Int32 /* nEndIndex */ )
+{
+ ensureAlive();
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::scrollSubstringTo( sal_Int32 /* nStartIndex */, sal_Int32/* nEndIndex */, AccessibleScrollType /* aScrollType */ )
+{
+ return false;
+}
+
+// events ---------------------------------------------------------------------
+
+void ScAccessibleCsvRuler::SendCaretEvent()
+{
+ sal_Int32 nPos = implGetRuler().GetRulerCursorPos();
+ if (nPos != CSV_POS_INVALID)
+ {
+ Any aOldValue, aNewValue;
+ aNewValue <<= nPos;
+ NotifyAccessibleEvent( AccessibleEventId::CARET_CHANGED, aOldValue, aNewValue );
+ }
+}
+
+// helpers --------------------------------------------------------------------
+
+OUString SAL_CALL ScAccessibleCsvRuler::getAccessibleName()
+{
+ return ScResId( STR_ACC_CSVRULER_NAME );
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getAccessibleDescription()
+{
+ return ScResId( STR_ACC_CSVRULER_DESCR );
+}
+
+void ScAccessibleCsvRuler::ensureValidIndex( sal_Int32 nIndex ) const
+{
+ if( (nIndex < 0) || (nIndex >= implGetTextLength()) )
+ throw IndexOutOfBoundsException();
+}
+
+void ScAccessibleCsvRuler::ensureValidIndexWithEnd( sal_Int32 nIndex ) const
+{
+ if( (nIndex < 0) || (nIndex > implGetTextLength()) )
+ throw IndexOutOfBoundsException();
+}
+
+void ScAccessibleCsvRuler::ensureValidRange( sal_Int32& rnStartIndex, sal_Int32& rnEndIndex ) const
+{
+ if( rnStartIndex > rnEndIndex )
+ ::std::swap( rnStartIndex, rnEndIndex );
+ if( (rnStartIndex < 0) || (rnEndIndex > implGetTextLength()) )
+ throw IndexOutOfBoundsException();
+}
+
+ScCsvRuler& ScAccessibleCsvRuler::implGetRuler() const
+{
+ return static_cast< ScCsvRuler& >( implGetControl() );
+}
+
+void ScAccessibleCsvRuler::constructStringBuffer()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ // extend existing string buffer to new ruler size
+ sal_Int32 nRulerCount = implGetRuler().GetPosCount();
+ sal_Int32 nRulerPos = lcl_GetRulerPos( maBuffer.getLength() );
+ for( ; nRulerPos <= nRulerCount; ++nRulerPos ) // include last position
+ {
+ switch( nRulerPos % 10 )
+ {
+ case 0: maBuffer.append( nRulerPos ); break;
+ case 5: maBuffer.append( cRulerLine ); break;
+ default: maBuffer.append( cRulerDot );
+ }
+ }
+}
+
+sal_Int32 ScAccessibleCsvRuler::implGetTextLength() const
+{
+ return lcl_GetApiPos( implGetRuler().GetPosCount() + 1 );
+}
+
+bool ScAccessibleCsvRuler::implHasSplit( sal_Int32 nApiPos )
+{
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nApiPos );
+ return implGetRuler().HasSplit( nRulerPos ) && (nApiPos == lcl_GetApiPos( nRulerPos ));
+}
+
+sal_Int32 ScAccessibleCsvRuler::implGetFirstEqualFormatted( sal_Int32 nApiPos )
+{
+ bool bSplit = implHasSplit( nApiPos );
+ while( (nApiPos > 0) && (implHasSplit( nApiPos - 1 ) == bSplit) )
+ --nApiPos;
+ return nApiPos;
+}
+
+sal_Int32 ScAccessibleCsvRuler::implGetLastEqualFormatted( sal_Int32 nApiPos )
+{
+ bool bSplit = implHasSplit( nApiPos );
+ sal_Int32 nLength = implGetTextLength();
+ while( (nApiPos < nLength - 1) && (implHasSplit( nApiPos + 1 ) == bSplit) )
+ ++nApiPos;
+ return nApiPos;
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SAL_CALL ScAccessibleCsvRuler::getAccessibleParent()
+{
+ return implGetControl().GetDrawingArea()->get_accessible_parent();
+}
+
+// Grid =======================================================================
+
+/** Converts a grid columnm index to an API column index. */
+static sal_Int32 lcl_GetApiColumn( sal_uInt32 nGridColumn )
+{
+ return (nGridColumn != CSV_COLUMN_HEADER) ? static_cast< sal_Int32 >( nGridColumn + 1 ) : 0;
+}
+
+/** Converts an API columnm index to a ScCsvGrid column index. */
+static sal_uInt32 lcl_GetGridColumn( sal_Int32 nApiColumn )
+{
+ return (nApiColumn > 0) ? static_cast< sal_uInt32 >( nApiColumn - 1 ) : CSV_COLUMN_HEADER;
+}
+
+ScAccessibleCsvGrid::ScAccessibleCsvGrid(ScCsvGrid& rGrid)
+ : ImplInheritanceHelper(rGrid)
+{
+}
+
+ScAccessibleCsvGrid::~ScAccessibleCsvGrid()
+{
+ ensureDisposed();
+}
+
+void ScAccessibleCsvGrid::disposing()
+{
+ SolarMutexGuard aGuard;
+ for (auto& rEntry : maAccessibleChildren)
+ rEntry.second->dispose();
+ maAccessibleChildren.clear();
+ ScAccessibleCsvControl::disposing();
+}
+
+// XAccessibleComponent -------------------------------------------------------
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleAtPoint( const css::awt::Point& rPoint )
+{
+ Reference< XAccessible > xRet;
+ if( containsPoint( rPoint ) )
+ {
+ SolarMutexGuard aGuard;
+ ensureAlive();
+
+ const ScCsvGrid& rGrid = implGetGrid();
+ // #102679#; use <= instead of <, because the offset is the size and not the point
+ sal_Int32 nColumn = ((rGrid.GetFirstX() <= rPoint.X) && (rPoint.X <= rGrid.GetLastX())) ?
+ lcl_GetApiColumn( rGrid.GetColumnFromX( rPoint.X ) ) : 0;
+ sal_Int32 nRow = (rPoint.Y >= rGrid.GetHdrHeight()) ?
+ (rGrid.GetLineFromY( rPoint.Y ) - rGrid.GetFirstVisLine() + 1) : 0;
+ xRet = getAccessibleCell(nRow, nColumn);
+ }
+ return xRet;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getForeground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetButtonTextColor());
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+// XAccessibleContext ---------------------------------------------------------
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetCellCount();
+}
+
+Reference<XAccessible> ScAccessibleCsvGrid::getAccessibleCell(sal_Int32 nRow, sal_Int32 nColumn)
+{
+ sal_Int64 nIndex = implGetIndex(nRow, nColumn);
+
+ XAccessibleSet::iterator aI = maAccessibleChildren.lower_bound(nIndex);
+ if (aI != maAccessibleChildren.end() && !(maAccessibleChildren.key_comp()(nIndex, aI->first)))
+ {
+ // key already exists
+ return aI->second;
+ }
+ // key does not exist
+ rtl::Reference<ScAccessibleCsvCell> xNew = implCreateCellObj(nRow, nColumn);
+ maAccessibleChildren.insert(aI, XAccessibleSet::value_type(nIndex, xNew));
+ return xNew;
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nIndex );
+
+ return getAccessibleCell(implGetRow(nIndex), implGetColumn(nIndex));
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvGrid::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ rtl::Reference<AccessibleRelationSetHelper> pRelationSet = new AccessibleRelationSetHelper();
+
+ ScCsvGrid& rGrid = implGetGrid();
+ ScCsvTableBox* pTableBox = rGrid.GetTableBox();
+ ScCsvRuler& rRuler = pTableBox->GetRuler();
+
+ if (rRuler.IsVisible())
+ {
+ css::uno::Reference<css::accessibility::XAccessible> xAccObj(static_cast<ScAccessibleCsvGrid*>(rRuler.GetAccessible()));
+ if( xAccObj.is() )
+ {
+ Sequence< Reference< XInterface > > aSeq{ xAccObj };
+ pRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::CONTROLLED_BY, aSeq ) );
+ }
+ }
+
+ return pRelationSet;
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = implCreateStateSet();
+ if( isAlive() )
+ {
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ if( implGetGrid().HasFocus() )
+ nStateSet |= AccessibleStateType::FOCUSED;
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+ return nStateSet;
+}
+
+// XAccessibleTable -----------------------------------------------------------
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleRowCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetRowCount();
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetColumnCount();
+}
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( nRow, 0 );
+ return implGetCellText( nRow, 0 );
+}
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( 0, nColumn );
+ return implGetCellText( 0, nColumn );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return 1;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return 1;
+}
+
+Reference< XAccessibleTable > SAL_CALL ScAccessibleCsvGrid::getAccessibleRowHeaders()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+Reference< XAccessibleTable > SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnHeaders()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+Sequence< sal_Int32 > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleRows()
+{
+ ensureAlive();
+ return Sequence< sal_Int32 >();
+}
+
+Sequence< sal_Int32 > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleColumns()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+
+ ScCsvGrid& rGrid = implGetGrid();
+ Sequence< sal_Int32 > aSeq( implGetColumnCount() );
+ auto pSeq = aSeq.getArray();
+
+ sal_Int32 nSeqIx = 0;
+ sal_uInt32 nColIx = rGrid.GetFirstSelected();
+ for( ; nColIx != CSV_COLUMN_INVALID; ++nSeqIx, nColIx = rGrid.GetNextSelected( nColIx ) )
+ pSeq[ nSeqIx ] = lcl_GetApiColumn( nColIx );
+
+ aSeq.realloc( nSeqIx );
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleRowSelected( sal_Int32 /* nRow */ )
+{
+ ensureAlive();
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nColumn );
+ return implIsColumnSelected( nColumn );
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return getAccessibleCell(nRow, nColumn);
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleCaption()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleSummary()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleSelected( sal_Int32 /* nRow */, sal_Int32 nColumn )
+{
+ return isAccessibleColumnSelected( nColumn );
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return implGetIndex( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ return implGetRow( nChildIndex );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ return implGetColumn( nChildIndex );
+}
+
+// XAccessibleSelection -------------------------------------------------------
+
+void SAL_CALL ScAccessibleCsvGrid::selectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ sal_Int32 nColumn = implGetColumn( nChildIndex );
+ if( nChildIndex == 0 )
+ implGetGrid().SelectAll();
+ else
+ implSelectColumn( nColumn, true );
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ sal_Int32 nColumn = implGetColumn( nChildIndex );
+ return implIsColumnSelected( nColumn );
+}
+
+void SAL_CALL ScAccessibleCsvGrid::clearAccessibleSelection()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ implGetGrid().SelectAll( false );
+}
+
+void SAL_CALL ScAccessibleCsvGrid::selectAllAccessibleChildren()
+{
+ selectAccessibleChild( 0 );
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return static_cast<sal_Int64>(implGetRowCount()) * static_cast<sal_Int64>(implGetSelColumnCount());
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ sal_Int32 nColumns = implGetSelColumnCount();
+ if( nColumns == 0 )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nRow = nSelectedChildIndex / nColumns;
+ sal_Int32 nColumn = implGetSelColumn( nSelectedChildIndex % nColumns );
+ return getAccessibleCellAt( nRow, nColumn );
+}
+
+void SAL_CALL ScAccessibleCsvGrid::deselectAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex(nSelectedChildIndex);
+ sal_Int32 nColumns = implGetSelColumnCount();
+ if( nColumns == 0 )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nColumn = implGetSelColumn( nSelectedChildIndex % nColumns );
+ ensureValidPosition( nSelectedChildIndex / nColumns, nColumn );
+ if( nColumn > 0 )
+ implSelectColumn( nColumn, false );
+}
+
+// events ---------------------------------------------------------------------
+
+void ScAccessibleCsvGrid::SendFocusEvent( bool bFocused )
+{
+ ScAccessibleCsvControl::SendFocusEvent( bFocused );
+ Any aOldAny, aNewAny;
+ (bFocused ? aNewAny : aOldAny) <<=
+ getAccessibleCellAt( 0, lcl_GetApiColumn( implGetGrid().GetFocusColumn() ) );
+ NotifyAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny);
+}
+
+void ScAccessibleCsvGrid::SendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows )
+{
+ if( nFirstColumn <= nLastColumn )
+ {
+ AccessibleTableModelChange aModelChange(
+ AccessibleTableModelChangeType::UPDATE, 0, bAllRows ? implGetRowCount() - 1 : 0,
+ lcl_GetApiColumn( nFirstColumn ), lcl_GetApiColumn( nLastColumn ) );
+ Any aOldAny, aNewAny;
+ aNewAny <<= aModelChange;
+ NotifyAccessibleEvent(AccessibleEventId::TABLE_MODEL_CHANGED, aOldAny, aNewAny);
+ }
+}
+
+void ScAccessibleCsvGrid::SendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( nFirstColumn <= nLastColumn )
+ {
+ AccessibleTableModelChange aModelChange(
+ AccessibleTableModelChangeType::COLUMNS_INSERTED, -1, -1,
+ lcl_GetApiColumn( nFirstColumn ), lcl_GetApiColumn( nLastColumn ) );
+ Any aOldAny, aNewAny;
+ aNewAny <<= aModelChange;
+ NotifyAccessibleEvent(AccessibleEventId::TABLE_MODEL_CHANGED, aOldAny, aNewAny);
+ }
+}
+
+void ScAccessibleCsvGrid::SendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( nFirstColumn <= nLastColumn )
+ {
+ AccessibleTableModelChange aModelChange(
+ AccessibleTableModelChangeType::COLUMNS_REMOVED, -1, -1,
+ lcl_GetApiColumn( nFirstColumn ), lcl_GetApiColumn( nLastColumn ) );
+ Any aOldAny, aNewAny;
+ aNewAny <<= aModelChange;
+ NotifyAccessibleEvent(AccessibleEventId::TABLE_MODEL_CHANGED, aOldAny, aNewAny);
+ }
+}
+
+// helpers --------------------------------------------------------------------
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleName()
+{
+ return ScResId( STR_ACC_CSVGRID_NAME );
+}
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleDescription()
+{
+ return ScResId( STR_ACC_CSVGRID_DESCR );
+}
+
+void ScAccessibleCsvGrid::ensureValidIndex( sal_Int64 nIndex ) const
+{
+ if( (nIndex < 0) || (nIndex >= implGetCellCount()) )
+ throw IndexOutOfBoundsException();
+}
+
+void ScAccessibleCsvGrid::ensureValidPosition( sal_Int32 nRow, sal_Int32 nColumn ) const
+{
+ if( (nRow < 0) || (nRow >= implGetRowCount()) || (nColumn < 0) || (nColumn >= implGetColumnCount()) )
+ throw IndexOutOfBoundsException();
+}
+
+ScCsvGrid& ScAccessibleCsvGrid::implGetGrid() const
+{
+ return static_cast< ScCsvGrid& >( implGetControl() );
+}
+
+bool ScAccessibleCsvGrid::implIsColumnSelected( sal_Int32 nColumn ) const
+{
+ return (nColumn > 0) && implGetGrid().IsSelected( lcl_GetGridColumn( nColumn ) );
+}
+
+void ScAccessibleCsvGrid::implSelectColumn( sal_Int32 nColumn, bool bSelect )
+{
+ if( nColumn > 0 )
+ implGetGrid().Select( lcl_GetGridColumn( nColumn ), bSelect );
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetRowCount() const
+{
+ return static_cast< sal_Int32 >( implGetGrid().GetLastVisLine() - implGetGrid().GetFirstVisLine() + 2 );
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetColumnCount() const
+{
+ return static_cast< sal_Int32 >( implGetGrid().GetColumnCount() + 1 );
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetSelColumnCount() const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ sal_Int32 nCount = 0;
+ for( sal_uInt32 nColIx = rGrid.GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = rGrid.GetNextSelected( nColIx ) )
+ ++nCount;
+ return nCount;
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetSelColumn( sal_Int32 nSelColumn ) const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ sal_Int32 nColumn = 0;
+ for( sal_uInt32 nColIx = rGrid.GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = rGrid.GetNextSelected( nColIx ) )
+ {
+ if( nColumn == nSelColumn )
+ return static_cast< sal_Int32 >( nColIx + 1 );
+ ++nColumn;
+ }
+ return 0;
+}
+
+OUString ScAccessibleCsvGrid::implGetCellText( sal_Int32 nRow, sal_Int32 nColumn ) const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ sal_Int32 nLine = nRow + rGrid.GetFirstVisLine() - 1;
+ OUString aCellStr;
+ if( (nColumn > 0) && (nRow > 0) )
+ aCellStr = rGrid.GetCellText( lcl_GetGridColumn( nColumn ), nLine );
+ else if( nRow > 0 )
+ aCellStr = OUString::number( nLine + 1 );
+ else if( nColumn > 0 )
+ aCellStr = rGrid.GetColumnTypeName( lcl_GetGridColumn( nColumn ) );
+ return aCellStr;
+}
+
+rtl::Reference<ScAccessibleCsvCell> ScAccessibleCsvGrid::implCreateCellObj( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return new ScAccessibleCsvCell(implGetGrid(), implGetCellText(nRow, nColumn), nRow, nColumn);
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SAL_CALL ScAccessibleCsvGrid::getAccessibleParent()
+{
+ return implGetControl().GetDrawingArea()->get_accessible_parent();
+}
+
+ScAccessibleCsvCell::ScAccessibleCsvCell(
+ ScCsvGrid& rGrid,
+ OUString aCellText,
+ sal_Int32 nRow, sal_Int32 nColumn ) :
+ ImplInheritanceHelper( rGrid ),
+ AccessibleStaticTextBase( SvxEditSourcePtr() ),
+ maCellText(std::move( aCellText )),
+ mnLine( nRow ? (nRow + rGrid.GetFirstVisLine() - 1) : CSV_LINE_HEADER ),
+ mnColumn( lcl_GetGridColumn( nColumn ) ),
+ mnIndex( nRow * (rGrid.GetColumnCount() + 1) + nColumn )
+{
+ SetEditSource( implCreateEditSource() );
+}
+
+ScAccessibleCsvCell::~ScAccessibleCsvCell()
+{
+}
+
+void SAL_CALL ScAccessibleCsvCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ SetEditSource( SvxEditSourcePtr() );
+ ScAccessibleCsvControl::disposing();
+}
+
+// XAccessibleComponent -------------------------------------------------------
+
+void SAL_CALL ScAccessibleCsvCell::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ScCsvGrid& rGrid = implGetGrid();
+ rGrid.Execute( CSVCMD_MOVEGRIDCURSOR, rGrid.GetColumnPos( mnColumn ) );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvCell::getForeground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetButtonTextColor());
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvCell::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+// XAccessibleContext -----------------------------------------------------
+
+sal_Int64 SAL_CALL ScAccessibleCsvCell::getAccessibleChildCount()
+{
+ return AccessibleStaticTextBase::getAccessibleChildCount();
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvCell::getAccessibleChild( sal_Int64 nIndex )
+{
+ return AccessibleStaticTextBase::getAccessibleChild( nIndex );
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvCell::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return mnIndex;
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvCell::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return new AccessibleRelationSetHelper();
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = implCreateStateSet();
+ if( isAlive() )
+ {
+ const ScCsvGrid& rGrid = implGetGrid();
+ nStateSet |= AccessibleStateType::SINGLE_LINE;
+ if( mnColumn != CSV_COLUMN_HEADER )
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if( rGrid.HasFocus() && (rGrid.GetFocusColumn() == mnColumn) && (mnLine == CSV_LINE_HEADER) )
+ nStateSet |= AccessibleStateType::ACTIVE;
+ if( rGrid.IsSelected( mnColumn ) )
+ nStateSet |= AccessibleStateType::SELECTED;
+ }
+ return nStateSet;
+}
+
+// XInterface -----------------------------------------------------------------
+
+IMPLEMENT_FORWARD_XINTERFACE2( ScAccessibleCsvCell, ImplInheritanceHelper, AccessibleStaticTextBase )
+
+// XTypeProvider --------------------------------------------------------------
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( ScAccessibleCsvCell, ImplInheritanceHelper, AccessibleStaticTextBase )
+
+// helpers --------------------------------------------------------------------
+
+OUString SAL_CALL ScAccessibleCsvCell::getAccessibleName()
+{
+ return maCellText;
+}
+
+OUString SAL_CALL ScAccessibleCsvCell::getAccessibleDescription()
+{
+ return OUString();
+}
+
+ScCsvGrid& ScAccessibleCsvCell::implGetGrid() const
+{
+ return static_cast< ScCsvGrid& >( implGetControl() );
+}
+
+Point ScAccessibleCsvCell::implGetRealPos() const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ return Point(
+ (mnColumn == CSV_COLUMN_HEADER) ? rGrid.GetHdrX() : rGrid.GetColumnX( mnColumn ),
+ (mnLine == CSV_LINE_HEADER) ? 0 : rGrid.GetY( mnLine ) );
+}
+
+sal_uInt32 ScAccessibleCsvCell::implCalcPixelWidth(sal_uInt32 nChars) const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ return rGrid.GetCharWidth() * nChars;
+}
+
+Size ScAccessibleCsvCell::implGetRealSize() const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ return Size(
+ (mnColumn == CSV_COLUMN_HEADER) ? rGrid.GetHdrWidth() : implCalcPixelWidth( rGrid.GetColumnWidth( mnColumn ) ),
+ (mnLine == CSV_LINE_HEADER) ? rGrid.GetHdrHeight() : rGrid.GetLineHeight() );
+}
+
+css::awt::Rectangle ScAccessibleCsvCell::implGetBounds()
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ tools::Rectangle aClipRect( Point( 0, 0 ), rGrid.GetOutputSizePixel() );
+ if( mnColumn != CSV_COLUMN_HEADER )
+ {
+ aClipRect.SetLeft( rGrid.GetFirstX() );
+ aClipRect.SetRight( rGrid.GetLastX() );
+ }
+ if( mnLine != CSV_LINE_HEADER )
+ aClipRect.SetTop( rGrid.GetHdrHeight() );
+
+ tools::Rectangle aRect( implGetRealPos(), implGetRealSize() );
+ aRect.Intersection( aClipRect );
+ if( aRect.IsEmpty() )
+ aRect.SetSize( Size( -1, -1 ) );
+
+ return css::awt::Rectangle(aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight());
+}
+
+::std::unique_ptr< SvxEditSource > ScAccessibleCsvCell::implCreateEditSource()
+{
+ ScCsvGrid& rGrid = implGetGrid();
+
+ ::std::unique_ptr< SvxEditSource > pEditSource( new ScAccessibilityEditSource( std::make_unique<ScAccessibleCsvTextData>(&rGrid.GetDrawingArea()->get_ref_device(), rGrid.GetEditEngine(), maCellText, implGetRealSize()) ) );
+ return pEditSource;
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SAL_CALL ScAccessibleCsvCell::getAccessibleParent()
+{
+ ScCsvGrid& rGrid = implGetGrid();
+
+ ScAccessibleCsvGrid* pAcc = static_cast<ScAccessibleCsvGrid*>(rGrid.GetAccessible());
+
+ return pAcc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleDocument.cxx b/sc/source/ui/Accessibility/AccessibleDocument.cxx
new file mode 100644
index 0000000000..c309f1b1fe
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleDocument.cxx
@@ -0,0 +1,2222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleDocument.hxx>
+#include <AccessibleSpreadsheet.hxx>
+#include <tabvwsh.hxx>
+#include <AccessibilityHints.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <DrawModelBroadcaster.hxx>
+#include <drawview.hxx>
+#include <gridwin.hxx>
+#include <AccessibleEditObject.hxx>
+#include <userdat.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <markdata.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/drawing/ShapeCollection.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <tools/gen.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <comphelper/sequence.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/docfile.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <sfx2/objsh.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <algorithm>
+
+#include <scmod.hxx>
+
+#ifdef indices
+#undef indices
+#endif
+
+#ifdef extents
+#undef extents
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+ //===== internal ========================================================
+
+namespace {
+
+struct ScAccessibleShapeData
+{
+ ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_);
+ ~ScAccessibleShapeData();
+ mutable rtl::Reference< ::accessibility::AccessibleShape > pAccShape;
+ mutable std::optional<ScAddress> xRelationCell; // if it is NULL this shape is anchored on the table
+ css::uno::Reference< css::drawing::XShape > xShape;
+ mutable bool bSelected;
+ bool bSelectable;
+ // cache these to make the sorting cheaper
+ std::optional<sal_Int16> mxLayerID;
+ std::optional<sal_Int32> mxZOrder;
+};
+
+}
+
+ScAccessibleShapeData::ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_)
+ : xShape(std::move(xShape_)),
+ bSelected(false), bSelectable(true)
+{
+ static constexpr OUStringLiteral gsLayerId = u"LayerID";
+ static constexpr OUStringLiteral gsZOrder = u"ZOrder";
+ uno::Reference< beans::XPropertySet> xProps(xShape, uno::UNO_QUERY);
+ if (xProps.is())
+ {
+ uno::Any aAny = xProps->getPropertyValue(gsLayerId);
+ sal_Int16 nLayerID;
+ if (aAny >>= nLayerID)
+ mxLayerID = nLayerID;
+ sal_Int32 nZOrder;
+ aAny = xProps->getPropertyValue(gsZOrder);
+ if (aAny >>= nZOrder)
+ mxZOrder = nZOrder;
+ }
+}
+
+ScAccessibleShapeData::~ScAccessibleShapeData()
+{
+ if (pAccShape.is())
+ {
+ pAccShape->dispose();
+ }
+}
+
+namespace {
+
+struct ScShapeDataLess
+{
+ static void ConvertLayerId(sal_Int16& rLayerID) // changes the number of the LayerId so it the accessibility order
+ {
+ // note: MSVC 2017 ICE's if this is written as "switch" so use "if"
+ if (SC_LAYER_FRONT.get() == rLayerID)
+ {
+ rLayerID = 1;
+ }
+ else if (SC_LAYER_BACK.get() == rLayerID)
+ {
+ rLayerID = 0;
+ }
+ else if (SC_LAYER_INTERN.get() == rLayerID)
+ {
+ rLayerID = 2;
+ }
+ else if (SC_LAYER_CONTROLS.get() == rLayerID)
+ {
+ rLayerID = 3;
+ }
+ }
+ static bool LessThanSheet(const ScAccessibleShapeData* pData)
+ {
+ bool bResult(false);
+ if (pData->mxLayerID)
+ {
+ if (SdrLayerID(*pData->mxLayerID) == SC_LAYER_BACK)
+ bResult = true;
+ }
+ return bResult;
+ }
+ bool operator()(const ScAccessibleShapeData* pData1, const ScAccessibleShapeData* pData2) const
+ {
+ bool bResult(false);
+ if (pData1 && pData2)
+ {
+ if( pData1->mxLayerID && pData2->mxLayerID )
+ {
+ sal_Int16 nLayerID1 = *pData1->mxLayerID;
+ sal_Int16 nLayerID2 = *pData2->mxLayerID;
+ if (nLayerID1 == nLayerID2)
+ {
+ if ( pData1->mxZOrder && pData2->mxZOrder )
+ bResult = (*pData1->mxZOrder < *pData2->mxZOrder);
+ }
+ else
+ {
+ ConvertLayerId(nLayerID1);
+ ConvertLayerId(nLayerID2);
+ bResult = (nLayerID1 < nLayerID2);
+ }
+ }
+ }
+ else if (pData1 && !pData2)
+ bResult = LessThanSheet(pData1);
+ else if (!pData1 && pData2)
+ bResult = !LessThanSheet(pData2);
+ else
+ bResult = false;
+ return bResult;
+ }
+};
+
+}
+
+class ScChildrenShapes : public SfxListener,
+ public ::accessibility::IAccessibleParent
+{
+public:
+ ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos);
+ virtual ~ScChildrenShapes() override;
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== IAccessibleParent ===============================================
+
+ virtual bool ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long _nIndex,
+ const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo
+ ) override;
+
+ virtual ::accessibility::AccessibleControlShape* GetAccControlShapeFromModel
+ (css::beans::XPropertySet* pSet) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible>
+ GetAccessibleCaption (const css::uno::Reference<css::drawing::XShape>& xShape) override;
+ ///===== Internal ========================================================
+ void SetDrawBroadcaster();
+
+ sal_Int32 GetCount() const;
+ uno::Reference< XAccessible > Get(const ScAccessibleShapeData* pData) const;
+ uno::Reference< XAccessible > Get(sal_Int32 nIndex) const;
+ uno::Reference< XAccessible > GetAt(const awt::Point& rPoint) const;
+
+ // gets the index of the shape starting on 0 (without the index of the table)
+ // returns the selected shape
+ bool IsSelected(sal_Int32 nIndex,
+ css::uno::Reference<css::drawing::XShape>& rShape) const;
+
+ bool SelectionChanged();
+
+ void Select(sal_Int32 nIndex);
+ void DeselectAll(); // deselect also the table
+ void SelectAll();
+ sal_Int32 GetSelectedCount() const;
+ uno::Reference< XAccessible > GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const;
+ void Deselect(sal_Int32 nChildIndex);
+
+ SdrPage* GetDrawPage() const;
+
+ rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAddress* pAddress) const;
+
+ void VisAreaChanged() const;
+private:
+ typedef std::vector<ScAccessibleShapeData*> SortedShapes;
+ typedef std::unordered_map<css::uno::Reference< css::drawing::XShape >, ScAccessibleShapeData*> ShapesMap;
+
+ mutable SortedShapes maZOrderedShapes; // a null pointer represents the sheet in the correct order
+ mutable ShapesMap maShapesMap;
+ mutable bool mbShapesNeedSorting; // set if maZOrderedShapes needs sorting
+
+ mutable ::accessibility::AccessibleShapeTreeInfo maShapeTreeInfo;
+ mutable css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier;
+ mutable sal_uInt32 mnShapesSelected;
+ ScTabViewShell* mpViewShell;
+ ScAccessibleDocument* mpAccessibleDocument;
+ ScSplitPos meSplitPos;
+
+ void FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const;
+ bool FindSelectedShapesChanges(const css::uno::Reference<css::drawing::XShapes>& xShapes) const;
+
+ std::optional<ScAddress> GetAnchor(const uno::Reference<drawing::XShape>& xShape) const;
+ rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAccessibleShapeData* pData) const;
+ void SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const;
+ void AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const;
+ void RemoveShape(const uno::Reference<drawing::XShape>& xShape) const;
+
+ bool FindShape(const uno::Reference<drawing::XShape>& xShape, SortedShapes::iterator& rItr) const;
+
+ static sal_Int8 Compare(const ScAccessibleShapeData* pData1,
+ const ScAccessibleShapeData* pData2);
+};
+
+ScChildrenShapes::ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
+ :
+ mbShapesNeedSorting(false),
+ mnShapesSelected(0),
+ mpViewShell(pViewShell),
+ mpAccessibleDocument(pAccessibleDocument),
+ meSplitPos(eSplitPos)
+{
+ if (mpViewShell)
+ {
+ SfxViewFrame& rViewFrame = mpViewShell->GetViewFrame();
+ xSelectionSupplier = uno::Reference<view::XSelectionSupplier>(rViewFrame.GetFrame().GetController(), uno::UNO_QUERY);
+ if (xSelectionSupplier.is())
+ {
+ xSelectionSupplier->addSelectionChangeListener(mpAccessibleDocument);
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ mnShapesSelected = xShapes->getCount();
+ }
+ }
+
+ maZOrderedShapes.push_back(nullptr); // add an element which represents the table
+
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (mnShapesSelected)
+ {
+ //set flag on every selected shape
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ FindSelectedShapesChanges(xShapes);
+ }
+ if (!pViewShell)
+ return;
+
+ ScViewData& rViewData = pViewShell->GetViewData();
+ SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ {
+ StartListening(*pDrawBC);
+
+ maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
+ maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
+ maShapeTreeInfo.SetController(nullptr);
+ maShapeTreeInfo.SetWindow(pViewShell->GetWindowByPos(meSplitPos));
+ maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
+ }
+}
+
+ScChildrenShapes::~ScChildrenShapes()
+{
+ for (ScAccessibleShapeData* pShapeData : maZOrderedShapes)
+ delete pShapeData;
+ if (mpViewShell)
+ {
+ SfxBroadcaster* pDrawBC = mpViewShell->GetViewData().GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ EndListening(*pDrawBC);
+ }
+ if (mpAccessibleDocument && xSelectionSupplier.is())
+ xSelectionSupplier->removeSelectionChangeListener(mpAccessibleDocument);
+}
+
+void ScChildrenShapes::SetDrawBroadcaster()
+{
+ if (!mpViewShell)
+ return;
+
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ {
+ StartListening(*pDrawBC, DuplicateHandling::Prevent);
+
+ maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
+ maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
+ maShapeTreeInfo.SetController(nullptr);
+ maShapeTreeInfo.SetWindow(mpViewShell->GetWindowByPos(meSplitPos));
+ maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
+ }
+}
+
+void ScChildrenShapes::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+
+ SdrObject* pObj = const_cast<SdrObject*>(pSdrHint->GetObject());
+ if (!(pObj && /*(pObj->GetLayer() != SC_LAYER_INTERN) && */(pObj->getSdrPageFromSdrObject() == GetDrawPage()) &&
+ (pObj->getSdrPageFromSdrObject() == pObj->getParentSdrObjListFromSdrObject())) ) //only do something if the object lies direct on the page
+ return;
+
+ switch (pSdrHint->GetKind())
+ {
+ case SdrHintKind::ObjectChange : // object changed
+ {
+ uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ {
+ mbShapesNeedSorting = true; // sort, because the z index or layer could be changed
+ auto it = maShapesMap.find(xShape);
+ if (it != maShapesMap.end())
+ SetAnchor(xShape, it->second);
+ }
+ }
+ break;
+ case SdrHintKind::ObjectInserted : // new drawing object inserted
+ {
+ uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ AddShape(xShape, true);
+ }
+ break;
+ case SdrHintKind::ObjectRemoved : // Removed drawing object from list
+ {
+ uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ RemoveShape(xShape);
+ }
+ break;
+ default :
+ {
+ // other events are not interesting
+ }
+ break;
+ }
+}
+
+bool ScChildrenShapes::ReplaceChild (::accessibility::AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long /*_nIndex*/, const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo)
+{
+ // create the new child
+ rtl::Reference< ::accessibility::AccessibleShape > pReplacement(::accessibility::ShapeTypeHandler::Instance().CreateAccessibleObject (
+ ::accessibility::AccessibleShapeInfo ( _rxShape, pCurrentChild->getAccessibleParent(), this ),
+ _rShapeTreeInfo
+ ));
+
+ bool bResult(false);
+ if (pReplacement.is())
+ {
+ OSL_ENSURE(pCurrentChild->GetXShape().get() == pReplacement->GetXShape().get(), "XShape changes and should be inserted sorted");
+ auto it = maShapesMap.find(pCurrentChild->GetXShape());
+ if (it != maShapesMap.end() && it->second->pAccShape.is())
+ {
+ OSL_ENSURE(it->second->pAccShape == pCurrentChild, "wrong child found");
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(pCurrentChild);
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
+
+ pCurrentChild->dispose();
+ }
+
+ // Init after above possible pCurrentChild->dispose so we don't trigger the assert
+ // ScDrawModelBroadcaster::addShapeEventListener of duplicate listeners
+ pReplacement->Init();
+
+ if (it != maShapesMap.end())
+ {
+ it->second->pAccShape = pReplacement;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.NewValue <<= uno::Reference<XAccessible>(pReplacement);
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // child is new - event
+ bResult = true;
+ }
+ }
+ return bResult;
+}
+
+::accessibility::AccessibleControlShape * ScChildrenShapes::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ GetCount(); // populate
+ for (ScAccessibleShapeData* pShape : maZOrderedShapes)
+ {
+ if (pShape)
+ {
+ rtl::Reference< ::accessibility::AccessibleShape > pAccShape(pShape->pAccShape);
+ if (pAccShape.is() && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
+ {
+ ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape.get());
+ if (pCtlAccShape && pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ }
+ }
+ return nullptr;
+}
+
+css::uno::Reference < css::accessibility::XAccessible >
+ScChildrenShapes::GetAccessibleCaption (const css::uno::Reference < css::drawing::XShape>& xShape)
+{
+ GetCount(); // populate
+ auto it = maShapesMap.find(xShape);
+ if (it == maShapesMap.end())
+ return nullptr;
+ ScAccessibleShapeData* pShape = it->second;
+ css::uno::Reference< css::accessibility::XAccessible > xNewChild( pShape->pAccShape );
+ if(xNewChild)
+ return xNewChild;
+ return nullptr;
+}
+
+sal_Int32 ScChildrenShapes::GetCount() const
+{
+ SdrPage* pDrawPage = GetDrawPage();
+ if (pDrawPage && (maZOrderedShapes.size() == 1)) // the table is always in
+ {
+ size_t nSdrObjCount = pDrawPage->GetObjCount();
+ maZOrderedShapes.reserve(nSdrObjCount + 1); // the table is always in
+ for (const rtl::Reference<SdrObject>& pObj : *pDrawPage)
+ {
+ uno::Reference< drawing::XShape > xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ AddShape(xShape, false); //inserts in the correct order
+ }
+ }
+ return maZOrderedShapes.size();
+}
+
+uno::Reference< XAccessible > ScChildrenShapes::Get(const ScAccessibleShapeData* pData) const
+{
+ if (!pData)
+ return nullptr;
+
+ if (!pData->pAccShape.is())
+ {
+ ::accessibility::ShapeTypeHandler& rShapeHandler = ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(pData->xShape, mpAccessibleDocument, const_cast<ScChildrenShapes*>(this));
+ pData->pAccShape = rShapeHandler.CreateAccessibleObject(
+ aShapeInfo, maShapeTreeInfo);
+ if (pData->pAccShape.is())
+ {
+ pData->pAccShape->Init();
+ if (pData->bSelected)
+ pData->pAccShape->SetState(AccessibleStateType::SELECTED);
+ if (!pData->bSelectable)
+ pData->pAccShape->ResetState(AccessibleStateType::SELECTABLE);
+ pData->pAccShape->SetRelationSet(GetRelationSet(pData));
+ }
+ }
+ return pData->pAccShape;
+ }
+
+uno::Reference< XAccessible > ScChildrenShapes::Get(sal_Int32 nIndex) const
+{
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ if (o3tl::make_unsigned(nIndex) >= maZOrderedShapes.size())
+ return nullptr;
+
+ return Get(maZOrderedShapes[nIndex]);
+}
+
+uno::Reference< XAccessible > ScChildrenShapes::GetAt(const awt::Point& rPoint) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ if(mpViewShell)
+ {
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ sal_Int32 i(maZOrderedShapes.size() - 1);
+ bool bFound(false);
+ while (!bFound && i >= 0)
+ {
+ ScAccessibleShapeData* pShape = maZOrderedShapes[i];
+ if (pShape)
+ {
+ if (!pShape->pAccShape.is())
+ Get(pShape);
+
+ if (pShape->pAccShape.is())
+ {
+ Point aPoint(VCLPoint(rPoint));
+ aPoint -= VCLRectangle(pShape->pAccShape->getBounds()).TopLeft();
+ if (pShape->pAccShape->containsPoint(AWTPoint(aPoint)))
+ {
+ xAccessible = pShape->pAccShape.get();
+ bFound = true;
+ }
+ }
+ else
+ {
+ OSL_FAIL("I should have an accessible shape now!");
+ }
+ }
+ else
+ bFound = true; // this is the sheet and it lies before the rest of the shapes which are background shapes
+
+ --i;
+ }
+ }
+ return xAccessible;
+}
+
+bool ScChildrenShapes::IsSelected(sal_Int32 nIndex,
+ uno::Reference<drawing::XShape>& rShape) const
+{
+ bool bResult (false);
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ if (!maZOrderedShapes[nIndex])
+ return false;
+
+ bResult = maZOrderedShapes[nIndex]->bSelected;
+ rShape = maZOrderedShapes[nIndex]->xShape;
+
+#if OSL_DEBUG_LEVEL > 0 // test whether it is truly selected by a slower method
+ uno::Reference< drawing::XShape > xReturnShape;
+ bool bDebugResult(false);
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+
+ if (xShapes.is())
+ {
+ sal_Int32 nCount(xShapes->getCount());
+ if (nCount)
+ {
+ uno::Reference< drawing::XShape > xShape;
+ uno::Reference< drawing::XShape > xIndexShape = maZOrderedShapes[nIndex]->xShape;
+ sal_Int32 i(0);
+ while (!bDebugResult && (i < nCount))
+ {
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is() && (xIndexShape.get() == xShape.get()))
+ {
+ bDebugResult = true;
+ xReturnShape = xShape;
+ }
+ else
+ ++i;
+ }
+ }
+ }
+ OSL_ENSURE((bResult == bDebugResult) && ((bResult && (rShape.get() == xReturnShape.get())) || !bResult), "found the wrong shape or result");
+#endif
+
+ return bResult;
+}
+
+bool ScChildrenShapes::SelectionChanged()
+{
+ bool bResult(false);
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+
+ bResult = FindSelectedShapesChanges(xShapes);
+
+ return bResult;
+}
+
+void ScChildrenShapes::Select(sal_Int32 nIndex)
+{
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ if (!maZOrderedShapes[nIndex])
+ return;
+
+ uno::Reference<drawing::XShape> xShape;
+ if (IsSelected(nIndex, xShape) || !maZOrderedShapes[nIndex]->bSelectable)
+ return;
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+
+ if (!xShapes.is())
+ xShapes = drawing::ShapeCollection::create(
+ comphelper::getProcessComponentContext());
+
+ xShapes->add(maZOrderedShapes[nIndex]->xShape);
+
+ try
+ {
+ xSelectionSupplier->select(uno::Any(xShapes));
+ maZOrderedShapes[nIndex]->bSelected = true;
+ if (maZOrderedShapes[nIndex]->pAccShape.is())
+ maZOrderedShapes[nIndex]->pAccShape->SetState(AccessibleStateType::SELECTED);
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ }
+}
+
+void ScChildrenShapes::DeselectAll()
+{
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ bool bSomethingSelected(true);
+ try
+ {
+ xSelectionSupplier->select(uno::Any()); //deselects all
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ OSL_FAIL("nothing selected before");
+ bSomethingSelected = false;
+ }
+
+ if (bSomethingSelected)
+ for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
+ if (pAccShapeData)
+ {
+ pAccShapeData->bSelected = false;
+ if (pAccShapeData->pAccShape.is())
+ pAccShapeData->pAccShape->ResetState(AccessibleStateType::SELECTED);
+ }
+};
+
+
+void ScChildrenShapes::SelectAll()
+{
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (maZOrderedShapes.size() <= 1)
+ return;
+
+ uno::Reference<drawing::XShapes> xShapes = drawing::ShapeCollection::create(
+ comphelper::getProcessComponentContext());
+
+ try
+ {
+ for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
+ {
+ if (pAccShapeData && pAccShapeData->bSelectable)
+ {
+ pAccShapeData->bSelected = true;
+ if (pAccShapeData->pAccShape.is())
+ pAccShapeData->pAccShape->SetState(AccessibleStateType::SELECTED);
+ if (xShapes.is())
+ xShapes->add(pAccShapeData->xShape);
+ }
+ }
+ xSelectionSupplier->select(uno::Any(xShapes));
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ SelectionChanged(); // find all selected shapes and set the flags
+ }
+}
+
+void ScChildrenShapes::FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const
+{
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ {
+ sal_uInt32 nCount(xShapes->getCount());
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ uno::Reference<drawing::XShape> xShape;
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is())
+ rShapes.push_back(xShape);
+ }
+ }
+}
+
+sal_Int32 ScChildrenShapes::GetSelectedCount() const
+{
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ std::vector < uno::Reference < drawing::XShape > > aShapes;
+ FillShapes(aShapes);
+
+ return aShapes.size();
+}
+
+uno::Reference< XAccessible > ScChildrenShapes::GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const
+{
+ uno::Reference< XAccessible > xAccessible;
+
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with shapes
+
+ if (!bTabSelected)
+ {
+ std::vector < uno::Reference < drawing::XShape > > aShapes;
+ FillShapes(aShapes);
+
+ if (nSelectedChildIndex < 0 || o3tl::make_unsigned(nSelectedChildIndex) >= aShapes.size())
+ return xAccessible;
+
+ SortedShapes::iterator aItr;
+ if (FindShape(aShapes[nSelectedChildIndex], aItr))
+ xAccessible = Get(*aItr);
+ }
+ else
+ {
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+ for(const auto& rpShape : maZOrderedShapes)
+ {
+ if (!rpShape || rpShape->bSelected)
+ {
+ if (nSelectedChildIndex == 0)
+ {
+ if (rpShape)
+ xAccessible = rpShape->pAccShape.get();
+ break;
+ }
+ else
+ --nSelectedChildIndex;
+ }
+ }
+ }
+
+ return xAccessible;
+}
+
+void ScChildrenShapes::Deselect(sal_Int32 nChildIndex)
+{
+ uno::Reference<drawing::XShape> xShape;
+ if (!IsSelected(nChildIndex, xShape)) // returns false if it is the sheet
+ return;
+
+ if (!xShape.is())
+ return;
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ xShapes->remove(xShape);
+
+ try
+ {
+ xSelectionSupplier->select(uno::Any(xShapes));
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ OSL_FAIL("something not selectable");
+ }
+
+ maZOrderedShapes[nChildIndex]->bSelected = false;
+ if (maZOrderedShapes[nChildIndex]->pAccShape.is())
+ maZOrderedShapes[nChildIndex]->pAccShape->ResetState(AccessibleStateType::SELECTED);
+}
+
+SdrPage* ScChildrenShapes::GetDrawPage() const
+{
+ SCTAB nTab(mpAccessibleDocument->getVisibleTable());
+ SdrPage* pDrawPage = nullptr;
+ if (mpViewShell)
+ {
+ ScDocument& rDoc = mpViewShell->GetViewData().GetDocument();
+ if (ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer())
+ {
+ if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
+ pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
+ }
+ }
+ return pDrawPage;
+}
+
+rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAddress* pAddress) const
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
+ {
+ if (pAccShapeData &&
+ ((!pAccShapeData->xRelationCell && !pAddress) ||
+ (pAccShapeData->xRelationCell && pAddress && (*(pAccShapeData->xRelationCell) == *pAddress))))
+ {
+ if (!pRelationSet)
+ pRelationSet = new utl::AccessibleRelationSetHelper();
+
+ AccessibleRelation aRelation;
+ aRelation.TargetSet = { Get(pAccShapeData) };
+ aRelation.RelationType = AccessibleRelationType::CONTROLLER_FOR;
+
+ pRelationSet->AddRelation(aRelation);
+ }
+ }
+ return pRelationSet;
+}
+
+bool ScChildrenShapes::FindSelectedShapesChanges(const uno::Reference<drawing::XShapes>& xShapes) const
+{
+ bool bResult(false);
+ SortedShapes aShapesList;
+ if (xShapes.is())
+ {
+ mnShapesSelected = xShapes->getCount();
+ for (sal_uInt32 i = 0; i < mnShapesSelected; ++i)
+ {
+ uno::Reference< drawing::XShape > xShape;
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is())
+ {
+ ScAccessibleShapeData* pShapeData = new ScAccessibleShapeData(xShape);
+ aShapesList.push_back(pShapeData);
+ }
+ }
+ }
+ else
+ mnShapesSelected = 0;
+ SdrObject *pFocusedObj = nullptr;
+ if( mnShapesSelected == 1 && aShapesList.size() == 1)
+ {
+ pFocusedObj = SdrObject::getSdrObjectFromXShape(aShapesList[0]->xShape);
+ }
+ std::sort(aShapesList.begin(), aShapesList.end(), ScShapeDataLess());
+ SortedShapes vecSelectedShapeAdd;
+ SortedShapes vecSelectedShapeRemove;
+ bool bHasSelect=false;
+ SortedShapes::iterator aXShapesItr(aShapesList.begin());
+ SortedShapes::const_iterator aXShapesEndItr(aShapesList.end());
+ SortedShapes::iterator aDataItr(maZOrderedShapes.begin());
+ SortedShapes::const_iterator aDataEndItr(maZOrderedShapes.end());
+ SortedShapes::const_iterator aFocusedItr = aDataEndItr;
+ while(aDataItr != aDataEndItr)
+ {
+ if (*aDataItr) // is it really a shape or only the sheet
+ {
+ sal_Int8 nComp(0);
+ if (aXShapesItr == aXShapesEndItr)
+ nComp = -1; // simulate that the Shape is lower, so the selection state will be removed
+ else
+ nComp = Compare(*aDataItr, *aXShapesItr);
+ if (nComp == 0)
+ {
+ if (!(*aDataItr)->bSelected)
+ {
+ (*aDataItr)->bSelected = true;
+ if ((*aDataItr)->pAccShape.is())
+ {
+ (*aDataItr)->pAccShape->SetState(AccessibleStateType::SELECTED);
+ (*aDataItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
+ bResult = true;
+ vecSelectedShapeAdd.push_back(*aDataItr);
+ }
+ aFocusedItr = aDataItr;
+ }
+ else
+ {
+ bHasSelect = true;
+ }
+ ++aDataItr;
+ ++aXShapesItr;
+ }
+ else if (nComp < 0)
+ {
+ if ((*aDataItr)->bSelected)
+ {
+ (*aDataItr)->bSelected = false;
+ if ((*aDataItr)->pAccShape.is())
+ {
+ (*aDataItr)->pAccShape->ResetState(AccessibleStateType::SELECTED);
+ (*aDataItr)->pAccShape->ResetState(AccessibleStateType::FOCUSED);
+ bResult = true;
+ vecSelectedShapeRemove.push_back(*aDataItr);
+ }
+ }
+ ++aDataItr;
+ }
+ else
+ {
+ OSL_FAIL("here is a selected shape which is not in the childlist");
+ ++aXShapesItr;
+ --mnShapesSelected;
+ }
+ }
+ else
+ ++aDataItr;
+ }
+ bool bWinFocus=false;
+ if (mpViewShell)
+ {
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ {
+ bWinFocus = pWin->HasFocus();
+ }
+ }
+ const SdrMarkList* pMarkList = nullptr;
+ SdrObject* pMarkedObj = nullptr;
+ bool bIsFocuseMarked = true;
+ if( mpViewShell && mnShapesSelected == 1 && bWinFocus)
+ {
+ ScDrawView* pScDrawView = mpViewShell->GetViewData().GetScDrawView();
+ if( pScDrawView )
+ {
+ if( pScDrawView->GetMarkedObjectList().GetMarkCount() == 1 )
+ {
+ pMarkList = &(pScDrawView->GetMarkedObjectList());
+ pMarkedObj = pMarkList->GetMark(0)->GetMarkedSdrObj();
+ uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
+ if( aFocusedItr != aDataEndItr &&
+ (*aFocusedItr)->xShape.is() &&
+ xMarkedXShape.is() &&
+ (*aFocusedItr)->xShape != xMarkedXShape )
+ bIsFocuseMarked = false;
+ }
+ }
+ }
+ //if ((aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1))
+ if ( bIsFocuseMarked && (aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1) && bWinFocus)
+ {
+ (*aFocusedItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
+ }
+ else if( pFocusedObj && bWinFocus && pMarkList && pMarkList->GetMarkCount() == 1 && mnShapesSelected == 1 )
+ {
+ if( pMarkedObj )
+ {
+ uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
+ SdrObject* pUpObj = pMarkedObj->getParentSdrObjectFromSdrObject();
+
+ if( pMarkedObj == pFocusedObj && pUpObj )
+ {
+ uno::Reference< drawing::XShape > xUpGroupXShape (pUpObj->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference < XAccessible > xAccGroupShape =
+ const_cast<ScChildrenShapes*>(this)->GetAccessibleCaption( xUpGroupXShape );
+ if( xAccGroupShape.is() )
+ {
+ ::accessibility::AccessibleShape* pAccGroupShape =
+ static_cast< ::accessibility::AccessibleShape* >(xAccGroupShape.get());
+ if( pAccGroupShape )
+ {
+ sal_Int64 nCount = pAccGroupShape->getAccessibleChildCount();
+ for( sal_Int64 i = 0; i < nCount; i++ )
+ {
+ uno::Reference<XAccessible> xAccShape = pAccGroupShape->getAccessibleChild(i);
+ if (xAccShape.is())
+ {
+ ::accessibility::AccessibleShape* pChildAccShape = static_cast< ::accessibility::AccessibleShape* >(xAccShape.get());
+ uno::Reference< drawing::XShape > xChildShape = pChildAccShape->GetXShape();
+ if (xChildShape == xMarkedXShape)
+ {
+ pChildAccShape->SetState(AccessibleStateType::FOCUSED);
+ }
+ else
+ {
+ pChildAccShape->ResetState(AccessibleStateType::FOCUSED);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (vecSelectedShapeAdd.size() >= 10 )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
+ mpAccessibleDocument->CommitChange(aEvent);
+ }
+ else
+ {
+ for (const auto& rpShape : vecSelectedShapeAdd)
+ {
+ AccessibleEventObject aEvent;
+ if (bHasSelect)
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ }
+ else
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ }
+ aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
+ uno::Reference< XAccessible > xChild( rpShape->pAccShape );
+ aEvent.NewValue <<= xChild;
+ mpAccessibleDocument->CommitChange(aEvent);
+ }
+ }
+ for (const auto& rpShape : vecSelectedShapeRemove)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
+ uno::Reference< XAccessible > xChild( rpShape->pAccShape );
+ aEvent.NewValue <<= xChild;
+ mpAccessibleDocument->CommitChange(aEvent);
+ }
+ for(ScAccessibleShapeData*& pShapeData : aShapesList)
+ {
+ delete pShapeData;
+ pShapeData = nullptr;
+ }
+ return bResult;
+}
+
+std::optional<ScAddress> ScChildrenShapes::GetAnchor(const uno::Reference<drawing::XShape>& xShape) const
+{
+ if (mpViewShell)
+ {
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
+ uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
+ if (pSdrObj && xShapeProp.is())
+ {
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pSdrObj))
+ return std::optional<ScAddress>(pAnchor->maStart);
+ }
+ }
+
+ return std::optional<ScAddress>();
+}
+
+rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAccessibleShapeData* pData) const
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper();
+
+ if (pData && mpAccessibleDocument)
+ {
+ uno::Reference<XAccessible> xAccessible = mpAccessibleDocument->GetAccessibleSpreadsheet(); // should be the current table
+ if (pData->xRelationCell && xAccessible.is())
+ {
+ sal_Int32 nRow = pData->xRelationCell->Row();
+ sal_Int32 nColumn = pData->xRelationCell->Col();
+ bool bPositionUnset = nRow == -1 && nColumn == -1;
+ if (!bPositionUnset)
+ {
+ uno::Reference<XAccessibleTable> xAccTable(xAccessible->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccTable.is())
+ xAccessible = xAccTable->getAccessibleCellAt(nRow, nColumn);
+ }
+ }
+ AccessibleRelation aRelation;
+ aRelation.TargetSet = { xAccessible };
+ aRelation.RelationType = AccessibleRelationType::CONTROLLED_BY;
+ pRelationSet->AddRelation(aRelation);
+ }
+
+ return pRelationSet;
+}
+
+void ScChildrenShapes::SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const
+{
+ if (pData)
+ {
+ std::optional<ScAddress> xAddress = GetAnchor(xShape);
+ if ((xAddress && pData->xRelationCell && (*xAddress != *(pData->xRelationCell))) ||
+ (!xAddress && pData->xRelationCell) || (xAddress && !pData->xRelationCell))
+ {
+ pData->xRelationCell = xAddress;
+ if (pData->pAccShape.is())
+ pData->pAccShape->SetRelationSet(GetRelationSet(pData));
+ }
+ }
+}
+
+void ScChildrenShapes::AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const
+{
+ assert( maShapesMap.find(xShape) == maShapesMap.end());
+
+ ScAccessibleShapeData* pShape = new ScAccessibleShapeData(xShape);
+ maZOrderedShapes.push_back(pShape);
+ mbShapesNeedSorting = true;
+ maShapesMap[xShape] = pShape;
+ SetAnchor(xShape, pShape);
+
+ uno::Reference< beans::XPropertySet > xShapeProp(xShape, uno::UNO_QUERY);
+ if (xShapeProp.is())
+ {
+ uno::Any aPropAny = xShapeProp->getPropertyValue("LayerID");
+ sal_Int16 nLayerID = 0;
+ if( aPropAny >>= nLayerID )
+ {
+ if( (SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN) )
+ pShape->bSelectable = false;
+ else
+ pShape->bSelectable = true;
+ }
+ }
+
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ uno::Reference<container::XEnumerationAccess> xEnumAcc(xShapes, uno::UNO_QUERY);
+ if (xEnumAcc.is())
+ {
+ uno::Reference<container::XEnumeration> xEnum = xEnumAcc->createEnumeration();
+ if (xEnum.is())
+ {
+ uno::Reference<drawing::XShape> xSelectedShape;
+ bool bFound(false);
+ while (!bFound && xEnum->hasMoreElements())
+ {
+ xEnum->nextElement() >>= xSelectedShape;
+ if (xShape.is() && (xShape.get() == xSelectedShape.get()))
+ {
+ pShape->bSelected = true;
+ bFound = true;
+ }
+ }
+ }
+ }
+ if (mpAccessibleDocument && bCommitChange)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.NewValue <<= Get(pShape);
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // new child - event
+ }
+}
+
+void ScChildrenShapes::RemoveShape(const uno::Reference<drawing::XShape>& xShape) const
+{
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+ SortedShapes::iterator aItr;
+ if (FindShape(xShape, aItr))
+ {
+ if (mpAccessibleDocument)
+ {
+ uno::Reference<XAccessible> xOldAccessible (Get(*aItr));
+
+ delete *aItr;
+ maShapesMap.erase((*aItr)->xShape);
+ maZOrderedShapes.erase(aItr);
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.OldValue <<= xOldAccessible;
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
+ }
+ else
+ {
+ delete *aItr;
+ maShapesMap.erase((*aItr)->xShape);
+ maZOrderedShapes.erase(aItr);
+ }
+ }
+ else
+ {
+ OSL_FAIL("shape was not in internal list");
+ }
+}
+
+bool ScChildrenShapes::FindShape(const uno::Reference<drawing::XShape>& xShape, ScChildrenShapes::SortedShapes::iterator& rItr) const
+{
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+ bool bResult(false);
+ ScAccessibleShapeData aShape(xShape);
+ rItr = std::lower_bound(maZOrderedShapes.begin(), maZOrderedShapes.end(), &aShape, ScShapeDataLess());
+ if ((rItr != maZOrderedShapes.end()) && (*rItr != nullptr) && ((*rItr)->xShape.get() == xShape.get()))
+ bResult = true; // if the shape is found
+
+#if OSL_DEBUG_LEVEL > 0 // test whether it finds truly the correct shape (perhaps it is not really sorted)
+ SortedShapes::iterator aDebugItr = std::find_if(maZOrderedShapes.begin(), maZOrderedShapes.end(),
+ [&xShape](const ScAccessibleShapeData* pShape) { return pShape && (pShape->xShape.get() == xShape.get()); });
+ bool bResult2 = (aDebugItr != maZOrderedShapes.end());
+ OSL_ENSURE((bResult == bResult2) && ((bResult && (rItr == aDebugItr)) || !bResult), "wrong Shape found");
+#endif
+ return bResult;
+}
+
+sal_Int8 ScChildrenShapes::Compare(const ScAccessibleShapeData* pData1,
+ const ScAccessibleShapeData* pData2)
+{
+ ScShapeDataLess aLess;
+
+ bool bResult1(aLess(pData1, pData2));
+ bool bResult2(aLess(pData2, pData1));
+
+ sal_Int8 nResult(0);
+ if (!bResult1 && bResult2)
+ nResult = 1;
+ else if (bResult1 && !bResult2)
+ nResult = -1;
+
+ return nResult;
+}
+
+void ScChildrenShapes::VisAreaChanged() const
+{
+ for (const ScAccessibleShapeData* pAccShapeData: maZOrderedShapes)
+ if (pAccShapeData && pAccShapeData->pAccShape.is())
+ pAccShapeData->pAccShape->ViewForwarderChanged();
+}
+
+ScAccessibleDocument::ScAccessibleDocument(
+ const uno::Reference<XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ ScSplitPos eSplitPos)
+ : ScAccessibleDocumentBase(rxParent),
+ mpViewShell(pViewShell),
+ meSplitPos(eSplitPos),
+ mbCompleteSheetSelected(false)
+{
+ maVisArea = GetVisibleArea_Impl();
+}
+
+void ScAccessibleDocument::PreInit()
+{
+ if (!mpViewShell)
+ return;
+
+ mpViewShell->AddAccessibilityObject(*this);
+ vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
+ if( pWin )
+ {
+ pWin->AddChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
+ sal_uInt16 nCount = pWin->GetChildCount();
+ for( sal_uInt16 i=0; i < nCount; ++i )
+ {
+ vcl::Window *pChildWin = pWin->GetChild( i );
+ if( pChildWin &&
+ AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ AddChild( pChildWin->GetAccessible(), false );
+ }
+ }
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ if (rViewData.HasEditView(meSplitPos))
+ {
+ uno::Reference<XAccessible> xAcc = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
+ mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(), GetCurrentCellDescription(),
+ ScAccessibleEditObject::CellInEditMode);
+ AddChild(xAcc, false);
+ }
+}
+
+void ScAccessibleDocument::Init()
+{
+ if(!mpChildrenShapes)
+ mpChildrenShapes.reset( new ScChildrenShapes(this, mpViewShell, meSplitPos) );
+}
+
+ScAccessibleDocument::~ScAccessibleDocument()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessibleDocument::disposing()
+{
+ SolarMutexGuard aGuard;
+ FreeAccessibleSpreadsheet();
+ if (mpViewShell)
+ {
+ vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
+ if( pWin )
+ pWin->RemoveChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
+
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpChildrenShapes.reset();
+
+ ScAccessibleDocumentBase::disposing();
+}
+
+void SAL_CALL ScAccessibleDocument::disposing( const lang::EventObject& /* Source */ )
+{
+ disposing();
+}
+
+ //===== SfxListener =====================================================
+
+IMPL_LINK( ScAccessibleDocument, WindowChildEventListener, VclWindowEvent&, rEvent, void )
+{
+ OSL_ENSURE( rEvent.GetWindow(), "Window???" );
+ switch ( rEvent.GetId() )
+ {
+ case VclEventId::WindowShow: // send create on show for direct accessible children
+ {
+ vcl::Window* pChildWin = static_cast < vcl::Window * >( rEvent.GetData() );
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ AddChild( pChildWin->GetAccessible(), true );
+ }
+ }
+ break;
+ case VclEventId::WindowHide: // send destroy on hide for direct accessible children
+ {
+ vcl::Window* pChildWin = static_cast < vcl::Window * >( rEvent.GetData() );
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ RemoveChild( pChildWin->GetAccessible(), true );
+ }
+ }
+ break;
+ default: break;
+ }
+}
+
+void ScAccessibleDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (auto pFocusLostHint = dynamic_cast<const ScAccGridWinFocusLostHint*>(&rHint) )
+ {
+ if (pFocusLostHint->GetOldGridWin() == meSplitPos)
+ {
+ if (mxTempAcc.is() && mpTempAccEdit)
+ mpTempAccEdit->LostFocus();
+ else if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->LostFocus();
+ else
+ CommitFocusLost();
+ }
+ }
+ else if (auto pFocusGotHint = dynamic_cast<const ScAccGridWinFocusGotHint*>(&rHint) )
+ {
+ if (pFocusGotHint->GetNewGridWin() == meSplitPos)
+ {
+ uno::Reference<XAccessible> xAccessible;
+ if (mpChildrenShapes)
+ {
+ bool bTabMarked(IsTableSelected());
+ xAccessible = mpChildrenShapes->GetSelected(0, bTabMarked);
+ }
+ if( xAccessible.is() )
+ {
+ uno::Any aNewValue;
+ aNewValue<<=AccessibleStateType::FOCUSED;
+ static_cast< ::accessibility::AccessibleShape* >(xAccessible.get())->
+ CommitChange(AccessibleEventId::STATE_CHANGED,
+ aNewValue,
+ uno::Any(), -1 );
+ }
+ else
+ {
+ if (mxTempAcc.is() && mpTempAccEdit)
+ mpTempAccEdit->GotFocus();
+ else if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->GotFocus();
+ else
+ CommitFocusGained();
+ }
+ }
+ }
+ else
+ {
+ // only notify if child exist, otherwise it is not necessary
+ if ((rHint.GetId() == SfxHintId::ScAccTableChanged) &&
+ mpAccessibleSpreadsheet.is())
+ {
+ FreeAccessibleSpreadsheet();
+
+ // Shapes / form controls after reload not accessible, rebuild the
+ // mpChildrenShapes variable.
+ mpChildrenShapes.reset( new ScChildrenShapes( this, mpViewShell, meSplitPos ) );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::INVALIDATE_ALL_CHILDREN;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent); // all children changed
+
+ if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->FireFirstCellFocus();
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccMakeDrawLayer)
+ {
+ if (mpChildrenShapes)
+ mpChildrenShapes->SetDrawBroadcaster();
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccEnterEditMode) // this event comes only on creating edit field of a cell
+ {
+ if (mpViewShell->GetViewData().GetEditActivePart() == meSplitPos)
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ const EditEngine* pEditEng = rViewData.GetEditView(meSplitPos)->GetEditEngine();
+ if (pEditEng && pEditEng->IsUpdateLayout())
+ {
+ mpTempAccEdit = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
+ mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(),
+ ScResId(STR_ACC_EDITLINE_DESCR), ScAccessibleEditObject::CellInEditMode);
+ uno::Reference<XAccessible> xAcc = mpTempAccEdit;
+
+ AddChild(xAcc, true);
+
+ if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->LostFocus();
+ else
+ CommitFocusLost();
+
+ mpTempAccEdit->GotFocus();
+ }
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccLeaveEditMode)
+ {
+ if (mxTempAcc.is())
+ {
+ if (mpTempAccEdit)
+ {
+ mpTempAccEdit->LostFocus();
+ }
+ RemoveChild(mxTempAcc, true);
+ if (mpTempAccEdit)
+ {
+ // tdf#125982 a11y use-after-free of editengine by
+ // ScAccessibleEditObjectTextData living past the
+ // the editengine of the editview passed in above
+ // in ScAccEnterEditMode
+ mpTempAccEdit->dispose();
+ mpTempAccEdit = nullptr;
+ }
+ if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
+ mpAccessibleSpreadsheet->GotFocus();
+ else if( mpViewShell && mpViewShell->IsActive())
+ CommitFocusGained();
+ }
+ }
+ else if ((rHint.GetId() == SfxHintId::ScAccVisAreaChanged) || (rHint.GetId() == SfxHintId::ScAccWindowResized))
+ {
+ tools::Rectangle aOldVisArea(maVisArea);
+ maVisArea = GetVisibleArea_Impl();
+
+ if (maVisArea != aOldVisArea)
+ {
+ if (maVisArea.GetSize() != aOldVisArea.GetSize())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+
+ if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->BoundingBoxChanged();
+ if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
+ mpAccessibleSpreadsheet->FireFirstCellFocus();
+ }
+ else if (mpAccessibleSpreadsheet.is())
+ {
+ mpAccessibleSpreadsheet->VisAreaChanged();
+ }
+ if (mpChildrenShapes)
+ mpChildrenShapes->VisAreaChanged();
+ }
+ }
+ }
+
+ ScAccessibleDocumentBase::Notify(rBC, rHint);
+}
+
+void SAL_CALL ScAccessibleDocument::selectionChanged( const lang::EventObject& /* aEvent */ )
+{
+ bool bSelectionChanged(false);
+ if (mpAccessibleSpreadsheet.is())
+ {
+ bool bOldSelected(mbCompleteSheetSelected);
+ mbCompleteSheetSelected = IsTableSelected();
+ if (bOldSelected != mbCompleteSheetSelected)
+ {
+ mpAccessibleSpreadsheet->CompleteSelectionChanged(mbCompleteSheetSelected);
+ bSelectionChanged = true;
+ }
+ }
+
+ if (mpChildrenShapes && mpChildrenShapes->SelectionChanged())
+ bSelectionChanged = true;
+
+ if (bSelectionChanged)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+ }
+}
+
+ //===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessibleDocument::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessibleDocumentImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessibleDocument::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessibleDocument::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleDocument::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xAccessible;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mpChildrenShapes)
+ xAccessible = mpChildrenShapes->GetAt(rPoint);
+ if(!xAccessible.is())
+ {
+ if (mxTempAcc.is())
+ {
+ uno::Reference< XAccessibleContext > xCont(mxTempAcc->getAccessibleContext());
+ uno::Reference< XAccessibleComponent > xComp(xCont, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ tools::Rectangle aBound(VCLRectangle(xComp->getBounds()));
+ if (aBound.Contains(VCLPoint(rPoint)))
+ xAccessible = mxTempAcc;
+ }
+ }
+ if (!xAccessible.is())
+ xAccessible = GetAccessibleSpreadsheet();
+ }
+ }
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleDocument::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!getAccessibleParent().is())
+ return;
+
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ {
+ xAccessibleComponent->grabFocus();
+ // grab only focus if it does not have the focus and it is not hidden
+ if (mpViewShell &&
+ (mpViewShell->GetViewData().GetActivePart() != meSplitPos) &&
+ mpViewShell->GetWindowByPos(meSplitPos)->IsVisible())
+ {
+ mpViewShell->ActivatePart(meSplitPos);
+ }
+ }
+}
+
+ //===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+sal_Int64 SAL_CALL
+ ScAccessibleDocument::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int64 nCount(1);
+ if (mpChildrenShapes)
+ nCount = mpChildrenShapes->GetCount(); // returns the count of the shapes inclusive the table
+
+ if (mxTempAcc.is())
+ ++nCount;
+
+ return nCount;
+}
+
+ /// Return the specified child or NULL if index is invalid.
+uno::Reference<XAccessible> SAL_CALL
+ ScAccessibleDocument::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference<XAccessible> xAccessible;
+ if (nIndex >= 0)
+ {
+ sal_Int64 nCount(1);
+ if (mpChildrenShapes)
+ {
+ xAccessible = mpChildrenShapes->Get(nIndex); // returns NULL if it is the table or out of range
+ nCount = mpChildrenShapes->GetCount(); //there is always a table
+ }
+ if (!xAccessible.is())
+ {
+ if (nIndex < nCount)
+ xAccessible = GetAccessibleSpreadsheet();
+ else if (nIndex == nCount && mxTempAcc.is())
+ xAccessible = mxTempAcc;
+ }
+ }
+
+ if (!xAccessible.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+ /// Return the set of current states.
+sal_Int64 SAL_CALL
+ ScAccessibleDocument::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+OUString SAL_CALL
+ ScAccessibleDocument::getAccessibleName()
+{
+ SolarMutexGuard g;
+
+ OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
+ ScDocument* pScDoc = GetDocument();
+ if (!pScDoc)
+ return aName;
+
+ ScDocShell* pObjSh = pScDoc->GetDocumentShell();
+ if (!pObjSh)
+ return aName;
+
+ OUString aFileName;
+ SfxMedium* pMed = pObjSh->GetMedium();
+ if (pMed)
+ aFileName = pMed->GetName();
+
+ if (aFileName.isEmpty())
+ aFileName = pObjSh->GetTitle(SFX_TITLE_APINAME);
+
+ if (!aFileName.isEmpty())
+ {
+ OUString aReadOnly;
+ if (pObjSh->IsReadOnly())
+ aReadOnly = ScResId(STR_ACC_DOC_SPREADSHEET_READONLY);
+
+ aName = aFileName + aReadOnly + " - " + aName;
+ }
+ return aName;
+}
+
+///===== XAccessibleSelection ===========================================
+
+void SAL_CALL
+ ScAccessibleDocument::selectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (!(mpChildrenShapes && mpViewShell))
+ return;
+
+ sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
+ if (mxTempAcc.is())
+ ++nCount;
+ if (nChildIndex < 0 || nChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
+ if (xAccessible.is())
+ {
+ bool bWasTableSelected(IsTableSelected());
+ mpChildrenShapes->Select(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
+ if (bWasTableSelected)
+ mpViewShell->SelectAll();
+ }
+ else
+ {
+ mpViewShell->SelectAll();
+ }
+}
+
+sal_Bool SAL_CALL
+ ScAccessibleDocument::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ bool bResult(false);
+
+ if (mpChildrenShapes)
+ {
+ sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
+ if (mxTempAcc.is())
+ ++nCount;
+ if (nChildIndex < 0 || nChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
+ if (xAccessible.is())
+ {
+ uno::Reference<drawing::XShape> xShape;
+ bResult = mpChildrenShapes->IsSelected(nChildIndex, xShape); // throws no lang::IndexOutOfBoundsException if Index is too high
+ }
+ else
+ {
+ if (mxTempAcc.is() && nChildIndex == nCount)
+ bResult = true;
+ else
+ bResult = IsTableSelected();
+ }
+ }
+ return bResult;
+}
+
+void SAL_CALL
+ ScAccessibleDocument::clearAccessibleSelection( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (mpChildrenShapes)
+ mpChildrenShapes->DeselectAll(); //deselects all (also the table)
+}
+
+void SAL_CALL
+ ScAccessibleDocument::selectAllAccessibleChildren( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (mpChildrenShapes)
+ mpChildrenShapes->SelectAll();
+
+ // select table after shapes, because while selecting shapes the table will be deselected
+ if (mpViewShell)
+ {
+ mpViewShell->SelectAll();
+ }
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleDocument::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int64 nCount(0);
+
+ if (mpChildrenShapes)
+ nCount = mpChildrenShapes->GetSelectedCount();
+
+ if (IsTableSelected())
+ ++nCount;
+
+ if (mxTempAcc.is())
+ ++nCount;
+
+ return nCount;
+}
+
+uno::Reference<XAccessible > SAL_CALL
+ ScAccessibleDocument::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference<XAccessible> xAccessible;
+ if (mpChildrenShapes)
+ {
+ sal_Int64 nCount(getSelectedAccessibleChildCount()); //all shapes and the table
+ if (nSelectedChildIndex < 0 || nSelectedChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ bool bTabMarked(IsTableSelected());
+
+ if (mpChildrenShapes)
+ xAccessible = mpChildrenShapes->GetSelected(nSelectedChildIndex, bTabMarked); // throws no lang::IndexOutOfBoundsException if Index is too high
+ if (mxTempAcc.is() && nSelectedChildIndex == nCount - 1)
+ xAccessible = mxTempAcc;
+ else if (bTabMarked)
+ xAccessible = GetAccessibleSpreadsheet();
+ }
+
+ OSL_ENSURE(xAccessible.is(), "here should always be an accessible object or an exception thrown");
+
+ return xAccessible;
+}
+
+void SAL_CALL
+ ScAccessibleDocument::deselectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (!(mpChildrenShapes && mpViewShell))
+ return;
+
+ sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
+ if (mxTempAcc.is())
+ ++nCount;
+ if (nChildIndex < 0 || nChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ bool bTabMarked(IsTableSelected());
+
+ uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
+ if (xAccessible.is())
+ {
+ mpChildrenShapes->Deselect(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
+ if (bTabMarked)
+ mpViewShell->SelectAll(); // select the table again
+ }
+ else if (bTabMarked)
+ mpViewShell->Unmark();
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL
+ ScAccessibleDocument::getImplementationName()
+{
+ return "ScAccessibleDocument";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleDocument::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheetDocumentView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessibleDocument::getTypes()
+{
+ return comphelper::concatSequences(ScAccessibleDocumentImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleDocument::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+///===== IAccessibleViewForwarder ========================================
+
+tools::Rectangle ScAccessibleDocument::GetVisibleArea_Impl() const
+{
+ tools::Rectangle aVisRect(GetBoundingBox());
+
+ if (mpViewShell)
+ {
+ Point aPoint(mpViewShell->GetViewData().GetPixPos(meSplitPos)); // returns a negative Point
+ aPoint.setX(-aPoint.getX());
+ aPoint.setY(-aPoint.getY());
+ aVisRect.SetPos(aPoint);
+
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ aVisRect = pWin->PixelToLogic(aVisRect, pWin->GetDrawMapMode());
+ }
+
+ return aVisRect;
+}
+
+tools::Rectangle ScAccessibleDocument::GetVisibleArea() const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return maVisArea;
+}
+
+Point ScAccessibleDocument::LogicToPixel (const Point& rPoint) const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ Point aPoint;
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ {
+ aPoint = pWin->LogicToPixel(rPoint, pWin->GetDrawMapMode());
+ aPoint += Point(pWin->GetWindowExtentsAbsolute().TopLeft());
+ }
+ return aPoint;
+}
+
+Size ScAccessibleDocument::LogicToPixel (const Size& rSize) const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ Size aSize;
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ aSize = pWin->LogicToPixel(rSize, pWin->GetDrawMapMode());
+ return aSize;
+}
+
+ //===== internal ========================================================
+
+rtl::Reference<utl::AccessibleRelationSetHelper> ScAccessibleDocument::GetRelationSet(const ScAddress* pAddress) const
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ if (mpChildrenShapes)
+ pRelationSet = mpChildrenShapes->GetRelationSet(pAddress);
+ return pRelationSet;
+}
+
+OUString
+ ScAccessibleDocument::createAccessibleDescription()
+{
+ return STR_ACC_DOC_DESCR;
+}
+
+OUString
+ ScAccessibleDocument::createAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ OUString sName = ScResId(STR_ACC_DOC_NAME);
+ sal_Int32 nNumber(sal_Int32(meSplitPos) + 1);
+ sName += OUString::number(nNumber);
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleDocument::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsAbsolute();
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessibleDocument::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
+ }
+ return aRect;
+}
+
+SCTAB ScAccessibleDocument::getVisibleTable() const
+{
+ SCTAB nVisibleTable(0);
+ if (mpViewShell)
+ nVisibleTable = mpViewShell->GetViewData().GetTabNo();
+ return nVisibleTable;
+}
+
+uno::Reference < XAccessible >
+ ScAccessibleDocument::GetAccessibleSpreadsheet()
+{
+ if (!mpAccessibleSpreadsheet.is() && mpViewShell)
+ {
+ mpAccessibleSpreadsheet = new ScAccessibleSpreadsheet(this, mpViewShell, getVisibleTable(), meSplitPos);
+ mpAccessibleSpreadsheet->Init();
+ mbCompleteSheetSelected = IsTableSelected();
+ }
+ return mpAccessibleSpreadsheet;
+}
+
+void ScAccessibleDocument::FreeAccessibleSpreadsheet()
+{
+ if (mpAccessibleSpreadsheet.is())
+ {
+ mpAccessibleSpreadsheet->dispose();
+ mpAccessibleSpreadsheet.clear();
+ }
+}
+
+bool ScAccessibleDocument::IsTableSelected() const
+{
+ bool bResult (false);
+ if(mpViewShell)
+ {
+ SCTAB nTab(getVisibleTable());
+ //#103800#; use a copy of MarkData
+ ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
+ ScDocument* pDoc = GetDocument();
+ if (aMarkData.IsAllMarked( ScRange( 0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab)))
+ bResult = true;
+ }
+ return bResult;
+}
+
+bool ScAccessibleDocument::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessibleDocument::AddChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
+{
+ OSL_ENSURE(!mxTempAcc.is(), "this object should be removed before");
+ if (xAcc.is())
+ {
+ mxTempAcc = xAcc;
+ if( bFireEvent )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext>(this);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= mxTempAcc;
+ aEvent.IndexHint = getAccessibleChildCount() - 1;
+ CommitChange( aEvent );
+ }
+ }
+}
+
+void ScAccessibleDocument::RemoveChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
+{
+ OSL_ENSURE(mxTempAcc.is(), "this object should be added before");
+ if (!xAcc.is())
+ return;
+
+ OSL_ENSURE(xAcc.get() == mxTempAcc.get(), "only the same object should be removed");
+ if( bFireEvent )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext>(this);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= mxTempAcc;
+ aEvent.IndexHint = -1;
+ CommitChange( aEvent );
+ }
+ mxTempAcc = nullptr;
+}
+
+OUString ScAccessibleDocument::GetCurrentCellName() const
+{
+ OUString sName(ScResId(STR_ACC_CELL_NAME));
+ if (mpViewShell)
+ {
+ // Document not needed, because only the cell address, but not the tablename is needed
+ OUString sAddress(mpViewShell->GetViewData().GetCurPos().Format(ScRefFlags::VALID));
+ sName = sName.replaceFirst("%1", sAddress);
+ }
+ return sName;
+}
+
+OUString ScAccessibleDocument::GetCurrentCellDescription()
+{
+ return OUString();
+}
+
+ScDocument *ScAccessibleDocument::GetDocument() const
+{
+ return mpViewShell ? &mpViewShell->GetViewData().GetDocument() : nullptr;
+}
+
+ScAddress ScAccessibleDocument::GetCurCellAddress() const
+{
+ return mpViewShell ? mpViewShell->GetViewData().GetCurPos() : ScAddress();
+}
+
+uno::Any SAL_CALL ScAccessibleDocument::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ uno::Any anyAttribute;
+
+ sal_uInt16 sheetIndex;
+ OUString sSheetName;
+ sheetIndex = getVisibleTable();
+ if(GetDocument()==nullptr)
+ return anyAttribute;
+ GetDocument()->GetName(sheetIndex,sSheetName);
+ OUString sValue = "page-name:" + sSheetName +
+ ";page-number:" + OUString::number(sheetIndex+1) +
+ ";total-pages:" + OUString::number(GetDocument()->GetTableCount()) + ";";
+ anyAttribute <<= sValue;
+ return anyAttribute;
+}
+
+sal_Int32 SAL_CALL ScAccessibleDocument::getForeground( )
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL ScAccessibleDocument::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleDocumentBase.cxx b/sc/source/ui/Accessibility/AccessibleDocumentBase.cxx
new file mode 100644
index 0000000000..78da26006f
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleDocumentBase.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 <AccessibleDocumentBase.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ========================================================
+
+ScAccessibleDocumentBase::ScAccessibleDocumentBase(const uno::Reference<XAccessible>& rxParent)
+ : ScAccessibleContextBase(rxParent, AccessibleRole::DOCUMENT_SPREADSHEET)
+{
+}
+
+ScAccessibleDocumentBase::~ScAccessibleDocumentBase() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx
new file mode 100644
index 0000000000..3161e2ba32
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx
@@ -0,0 +1,1569 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleDocumentPagePreview.hxx>
+#include <AccessiblePreviewTable.hxx>
+#include <AccessiblePageHeader.hxx>
+#include <AccessibilityHints.hxx>
+#include <AccessibleText.hxx>
+#include <document.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <drwlayer.hxx>
+#include <editsrc.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <preview.hxx>
+#include <postit.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <tools/gen.hxx>
+#include <svx/fmview.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+typedef std::vector< uno::Reference< XAccessible > > ScXAccVector;
+
+namespace {
+
+struct ScAccNote
+{
+ OUString maNoteText;
+ tools::Rectangle maRect;
+ ScAddress maNoteCell;
+ ::accessibility::AccessibleTextHelper* mpTextHelper;
+ sal_Int32 mnParaCount;
+ bool mbMarkNote;
+
+ ScAccNote()
+ : mpTextHelper(nullptr)
+ , mnParaCount(0)
+ , mbMarkNote(false)
+ {
+ }
+};
+
+}
+
+class ScNotesChildren
+{
+public:
+ ScNotesChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc);
+ ~ScNotesChildren();
+ void Init(const tools::Rectangle& rVisRect, sal_Int32 nOffset);
+
+ sal_Int32 GetChildrenCount() const { return mnParagraphs;}
+ uno::Reference<XAccessible> GetChild(sal_Int32 nIndex) const;
+ uno::Reference<XAccessible> GetAt(const awt::Point& rPoint) const;
+
+ void DataChanged(const tools::Rectangle& rVisRect);
+
+private:
+ ScPreviewShell* mpViewShell;
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ typedef std::vector<ScAccNote> ScAccNotes;
+ mutable ScAccNotes maNotes;
+ mutable ScAccNotes maMarks;
+ sal_Int32 mnParagraphs;
+ sal_Int32 mnOffset;
+
+ ::accessibility::AccessibleTextHelper* CreateTextHelper(const OUString& rString, const tools::Rectangle& rVisRect, const ScAddress& aCellPos, bool bMarkNote, sal_Int32 nChildOffset) const;
+ sal_Int32 AddNotes(const ScPreviewLocationData& rData, const tools::Rectangle& rVisRect, bool bMark, ScAccNotes& rNotes);
+
+ static sal_Int8 CompareCell(const ScAddress& aCell1, const ScAddress& aCell2);
+ static void CollectChildren(const ScAccNote& rNote, ScXAccVector& rVector);
+ sal_Int32 CheckChanges(const ScPreviewLocationData& rData, const tools::Rectangle& rVisRect,
+ bool bMark, ScAccNotes& rOldNotes, ScAccNotes& rNewNotes,
+ ScXAccVector& rOldParas, ScXAccVector& rNewParas);
+
+ inline ScDocument* GetDocument() const;
+};
+
+ScNotesChildren::ScNotesChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc)
+ : mpViewShell(pViewShell),
+ mpAccDoc(pAccDoc),
+ mnParagraphs(0),
+ mnOffset(0)
+{
+}
+
+ScNotesChildren::~ScNotesChildren()
+{
+ for (auto & i : maNotes)
+ if (i.mpTextHelper)
+ {
+ delete i.mpTextHelper;
+ i.mpTextHelper = nullptr;
+ }
+ for (auto & i : maMarks)
+ if (i.mpTextHelper)
+ {
+ delete i.mpTextHelper;
+ i.mpTextHelper = nullptr;
+ }
+}
+
+::accessibility::AccessibleTextHelper* ScNotesChildren::CreateTextHelper(const OUString& rString, const tools::Rectangle& rVisRect, const ScAddress& aCellPos, bool bMarkNote, sal_Int32 nChildOffset) const
+{
+ ::accessibility::AccessibleTextHelper* pTextHelper = new ::accessibility::AccessibleTextHelper(std::make_unique<ScAccessibilityEditSource>(std::make_unique<ScAccessibleNoteTextData>(mpViewShell, rString, aCellPos, bMarkNote)));
+ pTextHelper->SetEventSource(mpAccDoc);
+ pTextHelper->SetStartIndex(nChildOffset);
+ pTextHelper->SetOffset(rVisRect.TopLeft());
+
+ return pTextHelper;
+}
+
+sal_Int32 ScNotesChildren::AddNotes(const ScPreviewLocationData& rData, const tools::Rectangle& rVisRect, bool bMark, ScAccNotes& rNotes)
+{
+ sal_Int32 nCount = rData.GetNoteCountInRange(rVisRect, bMark);
+
+ rNotes.reserve(nCount);
+
+ sal_Int32 nParagraphs(0);
+ ScDocument* pDoc = GetDocument();
+ if (pDoc)
+ {
+ ScAccNote aNote;
+ aNote.mbMarkNote = bMark;
+ if (bMark)
+ aNote.mnParaCount = 1;
+ for (sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ if (rData.GetNoteInRange(rVisRect, nIndex, bMark, aNote.maNoteCell, aNote.maRect))
+ {
+ if (bMark)
+ {
+ // Document not needed, because only the cell address, but not the tablename is needed
+ aNote.maNoteText = aNote.maNoteCell.Format(ScRefFlags::VALID);
+ }
+ else
+ {
+ if( ScPostIt* pNote = pDoc->GetNote( aNote.maNoteCell ) )
+ aNote.maNoteText = pNote->GetText();
+ aNote.mpTextHelper = CreateTextHelper(aNote.maNoteText, aNote.maRect, aNote.maNoteCell, aNote.mbMarkNote, nParagraphs + mnOffset);
+ if (aNote.mpTextHelper)
+ aNote.mnParaCount = aNote.mpTextHelper->GetChildCount();
+ }
+ nParagraphs += aNote.mnParaCount;
+ rNotes.push_back(aNote);
+ }
+ }
+ }
+ return nParagraphs;
+}
+
+void ScNotesChildren::Init(const tools::Rectangle& rVisRect, sal_Int32 nOffset)
+{
+ if (mpViewShell && !mnParagraphs)
+ {
+ mnOffset = nOffset;
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+
+ mnParagraphs = AddNotes(rData, rVisRect, false, maMarks);
+ mnParagraphs += AddNotes(rData, rVisRect, true, maNotes);
+ }
+}
+
+namespace {
+
+struct ScParaFound
+{
+ sal_Int32 mnIndex;
+ explicit ScParaFound(sal_Int32 nIndex) : mnIndex(nIndex) {}
+ bool operator() (const ScAccNote& rNote)
+ {
+ bool bResult(false);
+ if (rNote.mnParaCount > mnIndex)
+ bResult = true;
+ else
+ mnIndex -= rNote.mnParaCount;
+ return bResult;
+ }
+};
+
+}
+
+uno::Reference<XAccessible> ScNotesChildren::GetChild(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+
+ if (nIndex < mnParagraphs)
+ {
+ if (nIndex < static_cast<sal_Int32>(maMarks.size()))
+ {
+ ScAccNotes::iterator aEndItr = maMarks.end();
+ ScParaFound aParaFound(nIndex);
+ ScAccNotes::iterator aItr = std::find_if(maMarks.begin(), aEndItr, aParaFound);
+ if (aItr != aEndItr)
+ {
+ OSL_ENSURE((aItr->maNoteCell == maMarks[nIndex].maNoteCell) && (aItr->mbMarkNote == maMarks[nIndex].mbMarkNote), "wrong note found");
+ if (!aItr->mpTextHelper)
+ aItr->mpTextHelper = CreateTextHelper(maMarks[nIndex].maNoteText, maMarks[nIndex].maRect, maMarks[nIndex].maNoteCell, maMarks[nIndex].mbMarkNote, nIndex + mnOffset); // the marks are the first and every mark has only one paragraph
+ xAccessible = aItr->mpTextHelper->GetChild(aParaFound.mnIndex + aItr->mpTextHelper->GetStartIndex());
+ }
+ else
+ {
+ OSL_FAIL("wrong note found");
+ }
+ }
+ else
+ {
+ nIndex -= maMarks.size();
+ ScAccNotes::iterator aEndItr = maNotes.end();
+ ScParaFound aParaFound(nIndex);
+ ScAccNotes::iterator aItr = std::find_if(maNotes.begin(), aEndItr, aParaFound);
+ if (aEndItr != aItr)
+ {
+ if (!aItr->mpTextHelper)
+ aItr->mpTextHelper = CreateTextHelper(aItr->maNoteText, aItr->maRect, aItr->maNoteCell, aItr->mbMarkNote, (nIndex - aParaFound.mnIndex) + mnOffset + maMarks.size());
+ xAccessible = aItr->mpTextHelper->GetChild(aParaFound.mnIndex + aItr->mpTextHelper->GetStartIndex());
+ }
+ }
+ }
+
+ return xAccessible;
+}
+
+namespace {
+
+struct ScPointFound
+{
+ tools::Rectangle maPoint;
+ sal_Int32 mnParagraphs;
+ explicit ScPointFound(const Point& rPoint) : maPoint(rPoint, Size(0, 0)), mnParagraphs(0) {}
+ bool operator() (const ScAccNote& rNote)
+ {
+ bool bResult(false);
+ if (maPoint.Contains(rNote.maRect))
+ bResult = true;
+ else
+ mnParagraphs += rNote.mnParaCount;
+ return bResult;
+ }
+};
+
+}
+
+uno::Reference<XAccessible> ScNotesChildren::GetAt(const awt::Point& rPoint) const
+{
+ uno::Reference<XAccessible> xAccessible;
+
+ ScPointFound aPointFound(Point(rPoint.X, rPoint.Y));
+
+ ScAccNotes::iterator aEndItr = maMarks.end();
+ ScAccNotes::iterator aItr = std::find_if(maMarks.begin(), aEndItr, aPointFound);
+ if (aEndItr == aItr)
+ {
+ aEndItr = maNotes.end();
+ aItr = std::find_if(maNotes.begin(), aEndItr, aPointFound);
+ }
+ if (aEndItr != aItr)
+ {
+ if (!aItr->mpTextHelper)
+ aItr->mpTextHelper = CreateTextHelper(aItr->maNoteText, aItr->maRect, aItr->maNoteCell, aItr->mbMarkNote, aPointFound.mnParagraphs + mnOffset);
+ xAccessible = aItr->mpTextHelper->GetAt(rPoint);
+ }
+
+ return xAccessible;
+}
+
+sal_Int8 ScNotesChildren::CompareCell(const ScAddress& aCell1, const ScAddress& aCell2)
+{
+ OSL_ENSURE(aCell1.Tab() == aCell2.Tab(), "the notes should be on the same table");
+ sal_Int8 nResult(0);
+ if (aCell1 != aCell2)
+ {
+ if (aCell1.Row() == aCell2.Row())
+ nResult = (aCell1.Col() < aCell2.Col()) ? -1 : 1;
+ else
+ nResult = (aCell1.Row() < aCell2.Row()) ? -1 : 1;
+ }
+ return nResult;
+}
+
+void ScNotesChildren::CollectChildren(const ScAccNote& rNote, ScXAccVector& rVector)
+{
+ if (rNote.mpTextHelper)
+ for (sal_Int32 i = 0; i < rNote.mnParaCount; ++i)
+ rVector.push_back(rNote.mpTextHelper->GetChild(i + rNote.mpTextHelper->GetStartIndex()));
+}
+
+sal_Int32 ScNotesChildren::CheckChanges(const ScPreviewLocationData& rData,
+ const tools::Rectangle& rVisRect, bool bMark, ScAccNotes& rOldNotes,
+ ScAccNotes& rNewNotes, ScXAccVector& rOldParas, ScXAccVector& rNewParas)
+{
+ sal_Int32 nCount = rData.GetNoteCountInRange(rVisRect, bMark);
+
+ rNewNotes.reserve(nCount);
+
+ sal_Int32 nParagraphs(0);
+ ScDocument* pDoc = GetDocument();
+ if (pDoc)
+ {
+ ScAccNote aNote;
+ aNote.mbMarkNote = bMark;
+ if (bMark)
+ aNote.mnParaCount = 1;
+ ScAccNotes::iterator aItr = rOldNotes.begin();
+ ScAccNotes::iterator aEndItr = rOldNotes.end();
+ bool bAddNote(false);
+ for (sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ if (rData.GetNoteInRange(rVisRect, nIndex, bMark, aNote.maNoteCell, aNote.maRect))
+ {
+ if (bMark)
+ {
+ // Document not needed, because only the cell address, but not the tablename is needed
+ aNote.maNoteText = aNote.maNoteCell.Format(ScRefFlags::VALID);
+ }
+ else
+ {
+ if( ScPostIt* pNote = pDoc->GetNote( aNote.maNoteCell ) )
+ aNote.maNoteText = pNote->GetText();
+ }
+
+ sal_Int8 nCompare(-1); // if there are no more old children it is always a new one
+ if (aItr != aEndItr)
+ nCompare = CompareCell(aNote.maNoteCell, aItr->maNoteCell);
+ if (nCompare == 0)
+ {
+ if (aNote.maNoteText == aItr->maNoteText)
+ {
+ aNote.mpTextHelper = aItr->mpTextHelper;
+ if (aNote.maRect != aItr->maRect) // set new VisArea
+ {
+ aNote.mpTextHelper->SetOffset(aNote.maRect.TopLeft());
+ aNote.mpTextHelper->UpdateChildren();
+ //OSL_ENSURE(aItr->maRect.GetSize() == aNote.maRect.GetSize(), "size should be the same, because the text is not changed");
+ // could be changed, because only a part of the note is visible
+ }
+ }
+ else
+ {
+ aNote.mpTextHelper = CreateTextHelper(aNote.maNoteText, aNote.maRect, aNote.maNoteCell, aNote.mbMarkNote, nParagraphs + mnOffset);
+ if (aNote.mpTextHelper)
+ aNote.mnParaCount = aNote.mpTextHelper->GetChildCount();
+ // collect removed children
+ CollectChildren(*aItr, rOldParas);
+ delete aItr->mpTextHelper;
+ aItr->mpTextHelper = nullptr;;
+ // collect new children
+ CollectChildren(aNote, rNewParas);
+ }
+ bAddNote = true;
+ // not necessary, because this branch should not be reached if it is the end
+ //if (aItr != aEndItr)
+ ++aItr;
+ }
+ else if (nCompare < 0)
+ {
+ aNote.mpTextHelper = CreateTextHelper(aNote.maNoteText, aNote.maRect, aNote.maNoteCell, aNote.mbMarkNote, nParagraphs + mnOffset);
+ if (aNote.mpTextHelper)
+ aNote.mnParaCount = aNote.mpTextHelper->GetChildCount();
+ // collect new children
+ CollectChildren(aNote, rNewParas);
+ bAddNote = true;
+ }
+ else
+ {
+ // collect removed children
+ CollectChildren(*aItr, rOldParas);
+ delete aItr->mpTextHelper;
+ aItr->mpTextHelper = nullptr;
+
+ // no note to add
+ // not necessary, because this branch should not be reached if it is the end
+ //if (aItr != aEndItr)
+ ++aItr;
+ }
+ if (bAddNote)
+ {
+ nParagraphs += aNote.mnParaCount;
+ rNewNotes.push_back(aNote);
+ bAddNote = false;
+ }
+ }
+ }
+ }
+ return nParagraphs;
+}
+
+namespace {
+
+struct ScChildGone
+{
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ explicit ScChildGone(ScAccessibleDocumentPagePreview* pAccDoc) : mpAccDoc(pAccDoc) {}
+ void operator() (const uno::Reference<XAccessible>& xAccessible) const
+ {
+ if (mpAccDoc)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccDoc);
+ aEvent.OldValue <<= xAccessible;
+ aEvent.IndexHint = -1;
+
+ mpAccDoc->CommitChange(aEvent); // gone child - event
+ }
+ }
+};
+
+struct ScChildNew
+{
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ explicit ScChildNew(ScAccessibleDocumentPagePreview* pAccDoc) : mpAccDoc(pAccDoc) {}
+ void operator() (const uno::Reference<XAccessible>& xAccessible) const
+ {
+ if (mpAccDoc)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccDoc);
+ aEvent.NewValue <<= xAccessible;
+ aEvent.IndexHint = -1;
+
+ mpAccDoc->CommitChange(aEvent); // new child - event
+ }
+ }
+};
+
+}
+
+void ScNotesChildren::DataChanged(const tools::Rectangle& rVisRect)
+{
+ if (!(mpViewShell && mpAccDoc))
+ return;
+
+ ScXAccVector aNewParas;
+ ScXAccVector aOldParas;
+ ScAccNotes aNewMarks;
+ mnParagraphs = CheckChanges(mpViewShell->GetLocationData(), rVisRect, true, maMarks, aNewMarks, aOldParas, aNewParas);
+ maMarks = aNewMarks;
+ ScAccNotes aNewNotes;
+ mnParagraphs += CheckChanges(mpViewShell->GetLocationData(), rVisRect, false, maNotes, aNewNotes, aOldParas, aNewParas);
+ maNotes = aNewNotes;
+
+ std::for_each(aOldParas.begin(), aOldParas.end(), ScChildGone(mpAccDoc));
+ std::for_each(aNewParas.begin(), aNewParas.end(), ScChildNew(mpAccDoc));
+}
+
+inline ScDocument* ScNotesChildren::GetDocument() const
+{
+ ScDocument* pDoc = nullptr;
+ if (mpViewShell)
+ pDoc = &mpViewShell->GetDocument();
+ return pDoc;
+}
+
+namespace {
+
+class ScIAccessibleViewForwarder : public ::accessibility::IAccessibleViewForwarder
+{
+public:
+ ScIAccessibleViewForwarder();
+ ScIAccessibleViewForwarder(ScPreviewShell* pViewShell,
+ ScAccessibleDocumentPagePreview* pAccDoc,
+ const MapMode& aMapMode);
+
+ ///===== IAccessibleViewForwarder ========================================
+
+ virtual tools::Rectangle GetVisibleArea() const override;
+ virtual Point LogicToPixel (const Point& rPoint) const override;
+ virtual Size LogicToPixel (const Size& rSize) const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ MapMode maMapMode;
+};
+
+}
+
+ScIAccessibleViewForwarder::ScIAccessibleViewForwarder()
+ : mpViewShell(nullptr), mpAccDoc(nullptr)
+{
+}
+
+ScIAccessibleViewForwarder::ScIAccessibleViewForwarder(ScPreviewShell* pViewShell,
+ ScAccessibleDocumentPagePreview* pAccDoc,
+ const MapMode& aMapMode)
+ : mpViewShell(pViewShell),
+ mpAccDoc(pAccDoc),
+ maMapMode(aMapMode)
+{
+}
+
+///===== IAccessibleViewForwarder ========================================
+
+tools::Rectangle ScIAccessibleViewForwarder::GetVisibleArea() const
+{
+ SolarMutexGuard aGuard;
+ tools::Rectangle aVisRect;
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ {
+ aVisRect.SetSize(pWin->GetOutputSizePixel());
+ aVisRect.SetPos(Point(0, 0));
+
+ aVisRect = pWin->PixelToLogic(aVisRect, maMapMode);
+ }
+
+ return aVisRect;
+}
+
+Point ScIAccessibleViewForwarder::LogicToPixel (const Point& rPoint) const
+{
+ SolarMutexGuard aGuard;
+ Point aPoint;
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin && mpAccDoc)
+ {
+ tools::Rectangle aRect(mpAccDoc->GetBoundingBoxOnScreen());
+ aPoint = pWin->LogicToPixel(rPoint, maMapMode) + aRect.TopLeft();
+ }
+
+ return aPoint;
+}
+
+Size ScIAccessibleViewForwarder::LogicToPixel (const Size& rSize) const
+{
+ SolarMutexGuard aGuard;
+ Size aSize;
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ aSize = pWin->LogicToPixel(rSize, maMapMode);
+ return aSize;
+}
+
+namespace {
+
+struct ScShapeChild
+{
+ ScShapeChild()
+ : mnRangeId(0)
+ {
+ }
+ ScShapeChild(ScShapeChild const &) = delete;
+ ScShapeChild(ScShapeChild &&) = default;
+ ~ScShapeChild();
+ ScShapeChild & operator =(ScShapeChild const &) = delete;
+ ScShapeChild & operator =(ScShapeChild && other) {
+ std::swap(mpAccShape, other.mpAccShape);
+ mxShape = std::move(other.mxShape);
+ mnRangeId = other.mnRangeId;
+ return *this;
+ }
+
+ mutable rtl::Reference< ::accessibility::AccessibleShape > mpAccShape;
+ css::uno::Reference< css::drawing::XShape > mxShape;
+ sal_Int32 mnRangeId;
+};
+
+}
+
+ScShapeChild::~ScShapeChild()
+{
+ if (mpAccShape.is())
+ {
+ mpAccShape->dispose();
+ }
+}
+
+namespace {
+
+struct ScShapeChildLess
+{
+ bool operator()(const ScShapeChild& rChild1, const ScShapeChild& rChild2) const
+ {
+ bool bResult(false);
+ if (rChild1.mxShape.is() && rChild2.mxShape.is())
+ bResult = (rChild1.mxShape.get() < rChild2.mxShape.get());
+ return bResult;
+ }
+};
+
+}
+
+typedef std::vector<ScShapeChild> ScShapeChildVec;
+
+namespace {
+
+struct ScShapeRange
+{
+ ScShapeRange() = default;
+ ScShapeRange(ScShapeRange const &) = delete;
+ ScShapeRange(ScShapeRange &&) = default;
+ ScShapeRange & operator =(ScShapeRange const &) = delete;
+ ScShapeRange & operator =(ScShapeRange &&) = default;
+
+ ScShapeChildVec maBackShapes;
+ ScShapeChildVec maForeShapes; // inclusive internal shapes
+ ScShapeChildVec maControls;
+ ScIAccessibleViewForwarder maViewForwarder;
+};
+
+}
+
+typedef std::vector<ScShapeRange> ScShapeRangeVec;
+
+class ScShapeChildren : public ::accessibility::IAccessibleParent
+{
+public:
+ ScShapeChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc);
+
+ ///===== IAccessibleParent ==============================================
+
+ virtual bool ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long _nIndex,
+ const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo
+ ) override;
+
+ ///===== Internal ========================================================
+
+ void Init();
+
+ sal_Int32 GetBackShapeCount() const;
+ uno::Reference<XAccessible> GetBackShape(sal_Int32 nIndex) const;
+ sal_Int32 GetForeShapeCount() const;
+ uno::Reference<XAccessible> GetForeShape(sal_Int32 nIndex) const;
+ sal_Int32 GetControlCount() const;
+ uno::Reference<XAccessible> GetControl(sal_Int32 nIndex) const;
+ uno::Reference<XAccessible> GetForegroundShapeAt(const awt::Point& rPoint) const; // inclusive controls
+ uno::Reference<XAccessible> GetBackgroundShapeAt(const awt::Point& rPoint) const;
+
+ void DataChanged();
+ void VisAreaChanged() const;
+
+private:
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ ScPreviewShell* mpViewShell;
+ ScShapeRangeVec maShapeRanges;
+
+ void FindChanged(ScShapeChildVec& aOld, ScShapeChildVec& aNew) const;
+ void FindChanged(ScShapeRange& aOld, ScShapeRange& aNew) const;
+ ::accessibility::AccessibleShape* GetAccShape(const ScShapeChild& rShape) const;
+ ::accessibility::AccessibleShape* GetAccShape(const ScShapeChildVec& rShapes, sal_Int32 nIndex) const;
+ void FillShapes(const tools::Rectangle& aPixelPaintRect, const MapMode& aMapMode, sal_uInt8 nRangeId);
+
+// void AddShape(const uno::Reference<drawing::XShape>& xShape, SdrLayerID aLayerID);
+// void RemoveShape(const uno::Reference<drawing::XShape>& xShape, SdrLayerID aLayerID);
+ SdrPage* GetDrawPage() const;
+};
+
+ScShapeChildren::ScShapeChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc)
+ :
+ mpAccDoc(pAccDoc),
+ mpViewShell(pViewShell),
+ maShapeRanges(SC_PREVIEW_MAXRANGES)
+{
+}
+
+void ScShapeChildren::FindChanged(ScShapeChildVec& rOld, ScShapeChildVec& rNew) const
+{
+ ScShapeChildVec::iterator aOldItr = rOld.begin();
+ ScShapeChildVec::iterator aOldEnd = rOld.end();
+ ScShapeChildVec::const_iterator aNewItr = rNew.begin();
+ ScShapeChildVec::const_iterator aNewEnd = rNew.end();
+ uno::Reference<XAccessible> xAcc;
+ while ((aNewItr != aNewEnd) && (aOldItr != aOldEnd))
+ {
+ if (aNewItr->mxShape.get() == aOldItr->mxShape.get())
+ {
+ ++aOldItr;
+ ++aNewItr;
+ }
+ else if (aNewItr->mxShape.get() < aOldItr->mxShape.get())
+ {
+ xAcc = GetAccShape(*aNewItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aNewItr;
+ }
+ else
+ {
+ xAcc = GetAccShape(*aOldItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aOldItr;
+ }
+ }
+ while (aOldItr != aOldEnd)
+ {
+ xAcc = GetAccShape(*aOldItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aOldItr;
+ }
+ while (aNewItr != aNewEnd)
+ {
+ xAcc = GetAccShape(*aNewItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aNewItr;
+ }
+}
+
+void ScShapeChildren::FindChanged(ScShapeRange& rOld, ScShapeRange& rNew) const
+{
+ FindChanged(rOld.maBackShapes, rNew.maBackShapes);
+ FindChanged(rOld.maForeShapes, rNew.maForeShapes);
+ FindChanged(rOld.maControls, rNew.maControls);
+}
+
+void ScShapeChildren::DataChanged()
+{
+ ScShapeRangeVec aOldShapeRanges(std::move(maShapeRanges));
+ maShapeRanges.clear();
+ maShapeRanges.resize(SC_PREVIEW_MAXRANGES);
+ Init();
+ for (sal_Int32 i = 0; i < SC_PREVIEW_MAXRANGES; ++i)
+ {
+ FindChanged(aOldShapeRanges[i], maShapeRanges[i]);
+ }
+}
+
+namespace
+{
+ struct ScVisAreaChanged
+ {
+ void operator() (const ScShapeChild& rAccShapeData) const
+ {
+ if (rAccShapeData.mpAccShape.is())
+ {
+ rAccShapeData.mpAccShape->ViewForwarderChanged();
+ }
+ }
+ };
+}
+
+void ScShapeChildren::VisAreaChanged() const
+{
+ for (auto const& shape : maShapeRanges)
+ {
+ ScVisAreaChanged aVisAreaChanged;
+ std::for_each(shape.maBackShapes.begin(), shape.maBackShapes.end(), aVisAreaChanged);
+ std::for_each(shape.maControls.begin(), shape.maControls.end(), aVisAreaChanged);
+ std::for_each(shape.maForeShapes.begin(), shape.maForeShapes.end(), aVisAreaChanged);
+ }
+}
+
+ ///===== IAccessibleParent ==============================================
+
+bool ScShapeChildren::ReplaceChild (::accessibility::AccessibleShape* /* pCurrentChild */,
+ const css::uno::Reference< css::drawing::XShape >& /* _rxShape */,
+ const tools::Long /* _nIndex */, const ::accessibility::AccessibleShapeTreeInfo& /* _rShapeTreeInfo */)
+{
+ OSL_FAIL("should not be called in the page preview");
+ return false;
+}
+
+ ///===== Internal ========================================================
+
+void ScShapeChildren::Init()
+{
+ if(!mpViewShell)
+ return;
+
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ MapMode aMapMode;
+ tools::Rectangle aPixelPaintRect;
+ sal_uInt8 nRangeId;
+ sal_uInt16 nCount(rData.GetDrawRanges());
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ rData.GetDrawRange(i, aPixelPaintRect, aMapMode, nRangeId);
+ FillShapes(aPixelPaintRect, aMapMode, nRangeId);
+ }
+}
+
+sal_Int32 ScShapeChildren::GetBackShapeCount() const
+{
+ sal_Int32 nCount(0);
+ for (auto const& shape : maShapeRanges)
+ nCount += shape.maBackShapes.size();
+ return nCount;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetBackShape(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ for (const auto& rShapeRange : maShapeRanges)
+ {
+ sal_Int32 nCount(rShapeRange.maBackShapes.size());
+ if(nIndex < nCount)
+ xAccessible = GetAccShape(rShapeRange.maBackShapes, nIndex);
+ nIndex -= nCount;
+ if (xAccessible.is())
+ break;
+ }
+
+ if (nIndex >= 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+sal_Int32 ScShapeChildren::GetForeShapeCount() const
+{
+ sal_Int32 nCount(0);
+ for (auto const& shape : maShapeRanges)
+ nCount += shape.maForeShapes.size();
+ return nCount;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetForeShape(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ for (const auto& rShapeRange : maShapeRanges)
+ {
+ sal_Int32 nCount(rShapeRange.maForeShapes.size());
+ if(nIndex < nCount)
+ xAccessible = GetAccShape(rShapeRange.maForeShapes, nIndex);
+ nIndex -= nCount;
+ if (xAccessible.is())
+ break;
+ }
+
+ if (nIndex >= 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+sal_Int32 ScShapeChildren::GetControlCount() const
+{
+ sal_Int32 nCount(0);
+ for (auto const& shape : maShapeRanges)
+ nCount += shape.maControls.size();
+ return nCount;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetControl(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ for (const auto& rShapeRange : maShapeRanges)
+ {
+ sal_Int32 nCount(rShapeRange.maControls.size());
+ if(nIndex < nCount)
+ xAccessible = GetAccShape(rShapeRange.maControls, nIndex);
+ nIndex -= nCount;
+ if (xAccessible.is())
+ break;
+ }
+
+ if (nIndex >= 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+namespace {
+
+struct ScShapePointFound
+{
+ Point maPoint;
+ explicit ScShapePointFound(const awt::Point& rPoint) : maPoint(VCLPoint(rPoint)) {}
+ bool operator() (const ScShapeChild& rShape)
+ {
+ bool bResult(false);
+ if (VCLRectangle(rShape.mpAccShape->getBounds()).Contains(maPoint))
+ bResult = true;
+ return bResult;
+ }
+};
+
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetForegroundShapeAt(const awt::Point& rPoint) const //inclusive Controls
+{
+ uno::Reference<XAccessible> xAcc;
+
+ for(const auto& rShapeRange : maShapeRanges)
+ {
+ ScShapeChildVec::const_iterator aFindItr = std::find_if(rShapeRange.maForeShapes.begin(), rShapeRange.maForeShapes.end(), ScShapePointFound(rPoint));
+ if (aFindItr != rShapeRange.maForeShapes.end())
+ xAcc = GetAccShape(*aFindItr);
+ else
+ {
+ ScShapeChildVec::const_iterator aCtrlItr = std::find_if(rShapeRange.maControls.begin(), rShapeRange.maControls.end(), ScShapePointFound(rPoint));
+ if (aCtrlItr != rShapeRange.maControls.end())
+ xAcc = GetAccShape(*aCtrlItr);
+ }
+
+ if (xAcc.is())
+ break;
+ }
+
+ return xAcc;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetBackgroundShapeAt(const awt::Point& rPoint) const
+{
+ uno::Reference<XAccessible> xAcc;
+
+ for(const auto& rShapeRange : maShapeRanges)
+ {
+ ScShapeChildVec::const_iterator aFindItr = std::find_if(rShapeRange.maBackShapes.begin(), rShapeRange.maBackShapes.end(), ScShapePointFound(rPoint));
+ if (aFindItr != rShapeRange.maBackShapes.end())
+ xAcc = GetAccShape(*aFindItr);
+ if (xAcc.is())
+ break;
+ }
+
+ return xAcc;
+}
+
+::accessibility::AccessibleShape* ScShapeChildren::GetAccShape(const ScShapeChild& rShape) const
+{
+ if (!rShape.mpAccShape.is())
+ {
+ ::accessibility::ShapeTypeHandler& rShapeHandler = ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(rShape.mxShape, mpAccDoc);
+
+ if (mpViewShell)
+ {
+ ::accessibility::AccessibleShapeTreeInfo aShapeTreeInfo;
+ aShapeTreeInfo.SetSdrView(mpViewShell->GetPreview()->GetDrawView());
+ aShapeTreeInfo.SetController(nullptr);
+ aShapeTreeInfo.SetWindow(mpViewShell->GetWindow());
+ aShapeTreeInfo.SetViewForwarder(&(maShapeRanges[rShape.mnRangeId].maViewForwarder));
+ rShape.mpAccShape = rShapeHandler.CreateAccessibleObject(aShapeInfo, aShapeTreeInfo);
+ if (rShape.mpAccShape.is())
+ {
+ rShape.mpAccShape->Init();
+ }
+ }
+ }
+ return rShape.mpAccShape.get();
+}
+
+::accessibility::AccessibleShape* ScShapeChildren::GetAccShape(const ScShapeChildVec& rShapes, sal_Int32 nIndex) const
+{
+ return GetAccShape(rShapes[nIndex]);
+}
+
+void ScShapeChildren::FillShapes(const tools::Rectangle& aPixelPaintRect, const MapMode& aMapMode, sal_uInt8 nRangeId)
+{
+ OSL_ENSURE(nRangeId < maShapeRanges.size(), "this is not a valid range for draw objects");
+ SdrPage* pPage = GetDrawPage();
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (!(pPage && pWin))
+ return;
+
+ bool bForeAdded(false);
+ bool bBackAdded(false);
+ bool bControlAdded(false);
+ tools::Rectangle aClippedPixelPaintRect(aPixelPaintRect);
+ if (mpAccDoc)
+ {
+ tools::Rectangle aRect2(Point(0,0), mpAccDoc->GetBoundingBoxOnScreen().GetSize());
+ aClippedPixelPaintRect = aPixelPaintRect.GetIntersection(aRect2);
+ }
+ ScIAccessibleViewForwarder aViewForwarder(mpViewShell, mpAccDoc, aMapMode);
+ maShapeRanges[nRangeId].maViewForwarder = aViewForwarder;
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ {
+ uno::Reference< drawing::XShape > xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ {
+ tools::Rectangle aRect(pWin->LogicToPixel(
+ tools::Rectangle(VCLPoint(xShape->getPosition()), VCLSize(xShape->getSize())), aMapMode));
+ if(!aClippedPixelPaintRect.GetIntersection(aRect).IsEmpty())
+ {
+ ScShapeChild aShape;
+ aShape.mxShape = xShape;
+ aShape.mnRangeId = nRangeId;
+ if (pObj->GetLayer().anyOf(SC_LAYER_INTERN, SC_LAYER_FRONT))
+ {
+ maShapeRanges[nRangeId].maForeShapes.push_back(std::move(aShape));
+ bForeAdded = true;
+ }
+ else if (pObj->GetLayer() == SC_LAYER_BACK)
+ {
+ maShapeRanges[nRangeId].maBackShapes.push_back(std::move(aShape));
+ bBackAdded = true;
+ }
+ else if (pObj->GetLayer() == SC_LAYER_CONTROLS)
+ {
+ maShapeRanges[nRangeId].maControls.push_back(std::move(aShape));
+ bControlAdded = true;
+ }
+ else
+ {
+ OSL_FAIL("I don't know this layer.");
+ }
+ }
+ }
+ }
+ if (bForeAdded)
+ std::sort(maShapeRanges[nRangeId].maForeShapes.begin(), maShapeRanges[nRangeId].maForeShapes.end(),ScShapeChildLess());
+ if (bBackAdded)
+ std::sort(maShapeRanges[nRangeId].maBackShapes.begin(), maShapeRanges[nRangeId].maBackShapes.end(),ScShapeChildLess());
+ if (bControlAdded)
+ std::sort(maShapeRanges[nRangeId].maControls.begin(), maShapeRanges[nRangeId].maControls.end(),ScShapeChildLess());
+}
+
+SdrPage* ScShapeChildren::GetDrawPage() const
+{
+ SCTAB nTab( mpViewShell->GetLocationData().GetPrintTab() );
+ SdrPage* pDrawPage = nullptr;
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ if (rDoc.GetDrawLayer())
+ {
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
+ pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
+ }
+ return pDrawPage;
+}
+
+namespace {
+
+struct ScPagePreviewCountData
+{
+ // order is background shapes, header, table or notes, footer, foreground shapes, controls
+
+ tools::Rectangle aVisRect;
+ tools::Long nBackShapes;
+ tools::Long nHeaders;
+ tools::Long nTables;
+ tools::Long nNoteParagraphs;
+ tools::Long nFooters;
+ tools::Long nForeShapes;
+ tools::Long nControls;
+
+ ScPagePreviewCountData( const ScPreviewLocationData& rData, const vcl::Window* pSizeWindow,
+ const ScNotesChildren* pNotesChildren, const ScShapeChildren* pShapeChildren );
+
+ tools::Long GetTotal() const
+ {
+ return nBackShapes + nHeaders + nTables + nNoteParagraphs + nFooters + nForeShapes + nControls;
+ }
+};
+
+}
+
+ScPagePreviewCountData::ScPagePreviewCountData( const ScPreviewLocationData& rData,
+ const vcl::Window* pSizeWindow, const ScNotesChildren* pNotesChildren,
+ const ScShapeChildren* pShapeChildren) :
+ nBackShapes( 0 ),
+ nHeaders( 0 ),
+ nTables( 0 ),
+ nNoteParagraphs( 0 ),
+ nFooters( 0 ),
+ nForeShapes( 0 ),
+ nControls( 0 )
+{
+ Size aOutputSize;
+ if ( pSizeWindow )
+ aOutputSize = pSizeWindow->GetOutputSizePixel();
+ aVisRect = tools::Rectangle( Point(), aOutputSize );
+
+ tools::Rectangle aObjRect;
+
+ if ( rData.GetHeaderPosition( aObjRect ) && aObjRect.Overlaps( aVisRect ) )
+ nHeaders = 1;
+
+ if ( rData.GetFooterPosition( aObjRect ) && aObjRect.Overlaps( aVisRect ) )
+ nFooters = 1;
+
+ if ( rData.HasCellsInRange( aVisRect ) )
+ nTables = 1;
+
+ //! shapes...
+ nBackShapes = pShapeChildren->GetBackShapeCount();
+ nForeShapes = pShapeChildren->GetForeShapeCount();
+ nControls = pShapeChildren->GetControlCount();
+
+ // there are only notes if there is no table
+ if (nTables == 0)
+ nNoteParagraphs = pNotesChildren->GetChildrenCount();
+}
+
+//===== internal ========================================================
+
+ScAccessibleDocumentPagePreview::ScAccessibleDocumentPagePreview(
+ const uno::Reference<XAccessible>& rxParent, ScPreviewShell* pViewShell ) :
+ ScAccessibleDocumentBase(rxParent),
+ mpViewShell(pViewShell)
+{
+ if (pViewShell)
+ pViewShell->AddAccessibilityObject(*this);
+
+}
+
+ScAccessibleDocumentPagePreview::~ScAccessibleDocumentPagePreview()
+{
+ if (!ScAccessibleDocumentBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessibleDocumentPagePreview::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpTable.clear();
+ mpHeader.clear();
+ mpFooter.clear();
+
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ // no need to Dispose the AccessibleTextHelper,
+ // as long as mpNotesChildren are destructed here
+ mpNotesChildren.reset();
+
+ mpShapeChildren.reset();
+
+ ScAccessibleDocumentBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessibleDocumentPagePreview::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScAccWinFocusLostHint*>(&rHint) )
+ {
+ CommitFocusLost();
+ }
+ else if ( dynamic_cast<const ScAccWinFocusGotHint*>(&rHint) )
+ {
+ CommitFocusGained();
+ }
+ else
+ {
+ // only notify if child exist, otherwise it is not necessary
+ if (rHint.GetId() == SfxHintId::ScDataChanged)
+ {
+ if (mpTable.is()) // if there is no table there is nothing to notify, because no one recognizes the change
+ {
+ {
+ uno::Reference<XAccessible> xAcc = mpTable;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ CommitChange(aEvent);
+ }
+
+ mpTable->dispose();
+ mpTable.clear();
+ }
+
+ Size aOutputSize;
+ vcl::Window* pSizeWindow = mpViewShell->GetWindow();
+ if ( pSizeWindow )
+ aOutputSize = pSizeWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ GetNotesChildren()->DataChanged(aVisRect);
+
+ GetShapeChildren()->DataChanged();
+
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if (aCount.nTables > 0)
+ {
+ //! order is background shapes, header, table or notes, footer, foreground shapes, controls
+ sal_Int32 nIndex (aCount.nBackShapes + aCount.nHeaders);
+
+ mpTable = new ScAccessiblePreviewTable( this, mpViewShell, nIndex );
+ mpTable->Init();
+
+ {
+ uno::Reference<XAccessible> xAcc = mpTable;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ CommitChange(aEvent);
+ }
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ Size aOutputSize;
+ vcl::Window* pSizeWindow = mpViewShell->GetWindow();
+ if ( pSizeWindow )
+ aOutputSize = pSizeWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ GetNotesChildren()->DataChanged(aVisRect);
+
+ GetShapeChildren()->VisAreaChanged();
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+ }
+ ScAccessibleDocumentBase::Notify(rBC, rHint);
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xAccessible;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if ( mpViewShell )
+ {
+ xAccessible = GetShapeChildren()->GetForegroundShapeAt(rPoint);
+ if (!xAccessible.is())
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if ( !mpTable.is() && (aCount.nTables > 0) )
+ {
+ //! order is background shapes, header, table or notes, footer, foreground shapes, controls
+ sal_Int32 nIndex (aCount.nBackShapes + aCount.nHeaders);
+
+ mpTable = new ScAccessiblePreviewTable( this, mpViewShell, nIndex );
+ mpTable->Init();
+ }
+ if (mpTable.is() && VCLRectangle(mpTable->getBounds()).Contains(VCLPoint(rPoint)))
+ xAccessible = mpTable.get();
+ }
+ if (!xAccessible.is())
+ xAccessible = GetNotesChildren()->GetAt(rPoint);
+ if (!xAccessible.is())
+ {
+ if (!mpHeader.is() || !mpFooter.is())
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if (!mpHeader.is())
+ {
+ mpHeader = new ScAccessiblePageHeader( this, mpViewShell, true, aCount.nBackShapes + aCount.nHeaders - 1);
+ }
+ if (!mpFooter.is())
+ {
+ mpFooter = new ScAccessiblePageHeader( this, mpViewShell, false, aCount.nBackShapes + aCount.nHeaders + aCount.nTables + aCount.nNoteParagraphs + aCount.nFooters - 1 );
+ }
+ }
+
+ Point aPoint(VCLPoint(rPoint));
+
+ if (VCLRectangle(mpHeader->getBounds()).Contains(aPoint))
+ xAccessible = mpHeader.get();
+ else if (VCLRectangle(mpFooter->getBounds()).Contains(aPoint))
+ xAccessible = mpFooter.get();
+ }
+ if (!xAccessible.is())
+ xAccessible = GetShapeChildren()->GetBackgroundShapeAt(rPoint);
+ }
+ }
+
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleDocumentPagePreview::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ {
+ // just grab the focus for the window
+ xAccessibleComponent->grabFocus();
+ }
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ sal_Int64 nRet = 0;
+ if ( mpViewShell )
+ {
+ ScPagePreviewCountData aCount( mpViewShell->GetLocationData(), mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+ nRet = aCount.GetTotal();
+ }
+
+ return nRet;
+}
+
+uno::Reference<XAccessible> SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference<XAccessible> xAccessible;
+
+ if ( mpViewShell )
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if ( nIndex < aCount.nBackShapes )
+ {
+ xAccessible = GetShapeChildren()->GetBackShape(nIndex);
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders )
+ {
+ if ( !mpHeader.is() )
+ {
+ mpHeader = new ScAccessiblePageHeader( this, mpViewShell, true, nIndex );
+ }
+
+ xAccessible = mpHeader.get();
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders + aCount.nTables )
+ {
+ if ( !mpTable.is() )
+ {
+ mpTable = new ScAccessiblePreviewTable( this, mpViewShell, nIndex );
+ mpTable->Init();
+ }
+ xAccessible = mpTable.get();
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders + aCount.nNoteParagraphs )
+ {
+ xAccessible = GetNotesChildren()->GetChild(nIndex - aCount.nBackShapes - aCount.nHeaders);
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders + aCount.nTables + aCount.nNoteParagraphs + aCount.nFooters )
+ {
+ if ( !mpFooter.is() )
+ {
+ mpFooter = new ScAccessiblePageHeader( this, mpViewShell, false, nIndex );
+ }
+ xAccessible = mpFooter.get();
+ }
+ else
+ {
+ sal_Int64 nIdx(nIndex - (aCount.nBackShapes + aCount.nHeaders + aCount.nTables + aCount.nNoteParagraphs + aCount.nFooters));
+ if (nIdx < aCount.nForeShapes)
+ xAccessible = GetShapeChildren()->GetForeShape(nIdx);
+ else
+ xAccessible = GetShapeChildren()->GetControl(nIdx - aCount.nForeShapes);
+ }
+ }
+
+ if ( !xAccessible.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+ /// Return the set of current states.
+sal_Int64 SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ // never editable
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleDocumentPagePreview::getImplementationName()
+{
+ return "ScAccessibleDocumentPagePreview";
+}
+
+uno::Sequence< OUString> SAL_CALL ScAccessibleDocumentPagePreview::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheetPageView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleDocumentPagePreview::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//===== internal ========================================================
+
+OUString ScAccessibleDocumentPagePreview::createAccessibleDescription()
+{
+ return STR_ACC_PREVIEWDOC_DESCR;
+}
+
+OUString ScAccessibleDocumentPagePreview::createAccessibleName()
+{
+ OUString sName = ScResId(STR_ACC_PREVIEWDOC_NAME);
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleDocumentPagePreview::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsAbsolute();
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessibleDocumentPagePreview::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
+ }
+ return aRect;
+}
+
+bool ScAccessibleDocumentPagePreview::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+ScNotesChildren* ScAccessibleDocumentPagePreview::GetNotesChildren()
+{
+ if (!mpNotesChildren && mpViewShell)
+ {
+ mpNotesChildren.reset( new ScNotesChildren(mpViewShell, this) );
+
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ //! order is background shapes, header, table or notes, footer, foreground shapes, controls
+ mpNotesChildren->Init(aCount.aVisRect, aCount.nBackShapes + aCount.nHeaders);
+ }
+ return mpNotesChildren.get();
+}
+
+ScShapeChildren* ScAccessibleDocumentPagePreview::GetShapeChildren()
+{
+ if (!mpShapeChildren && mpViewShell)
+ {
+ mpShapeChildren.reset( new ScShapeChildren(mpViewShell, this) );
+ mpShapeChildren->Init();
+ }
+
+ return mpShapeChildren.get();
+}
+
+OUString ScAccessibleDocumentPagePreview::getAccessibleName()
+{
+ SolarMutexGuard g;
+
+ OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
+ ScDocument& rScDoc = mpViewShell->GetDocument();
+
+ ScDocShell* pObjSh = rScDoc.GetDocumentShell();
+ if (!pObjSh)
+ return aName;
+
+ OUString aFileName;
+ SfxMedium* pMed = pObjSh->GetMedium();
+ if (pMed)
+ aFileName = pMed->GetName();
+
+ if (aFileName.isEmpty())
+ aFileName = pObjSh->GetTitle(SFX_TITLE_APINAME);
+
+ if (!aFileName.isEmpty())
+ {
+ aName = aFileName + " - " + aName + ScResId(STR_ACC_DOC_PREVIEW_SUFFIX);
+
+ }
+
+ return aName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleEditObject.cxx b/sc/source/ui/Accessibility/AccessibleEditObject.cxx
new file mode 100644
index 0000000000..7e58af04ef
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleEditObject.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 <memory>
+#include <utility>
+
+#include <AccessibleEditObject.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <svx/AccessibleTextHelper.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <svx/svdmodel.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sfx2/objsh.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <unonames.hxx>
+#include <document.hxx>
+#include <AccessibleDocument.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessibleEditObject::ScAccessibleEditObject(
+ const uno::Reference<XAccessible>& rxParent,
+ EditView* pEditView, vcl::Window* pWin, const OUString& rName,
+ const OUString& rDescription, EditObjectType eObjectType)
+ : ScAccessibleContextBase(rxParent, AccessibleRole::TEXT_FRAME)
+ , mpEditView(pEditView)
+ , mpWindow(pWin)
+ , mpTextWnd(nullptr)
+ , meObjectType(eObjectType)
+ , mbHasFocus(false)
+ , m_pScDoc(nullptr)
+{
+ InitAcc(rxParent, pEditView, rName, rDescription);
+}
+
+ScAccessibleEditObject::ScAccessibleEditObject(EditObjectType eObjectType)
+ : ScAccessibleContextBase(nullptr, AccessibleRole::TEXT_FRAME)
+ , mpEditView(nullptr)
+ , mpWindow(nullptr)
+ , mpTextWnd(nullptr)
+ , meObjectType(eObjectType)
+ , mbHasFocus(false)
+ , m_pScDoc(nullptr)
+{
+}
+
+void ScAccessibleEditObject::InitAcc(
+ const uno::Reference<XAccessible>& rxParent,
+ EditView* pEditView,
+ const OUString& rName,
+ const OUString& rDescription)
+{
+ SetParent(rxParent);
+ mpEditView = pEditView;
+
+ CreateTextHelper();
+ SetName(rName);
+ SetDescription(rDescription);
+ if( meObjectType == CellInEditMode)
+ {
+ const ScAccessibleDocument *pAccDoc = static_cast<ScAccessibleDocument*>(rxParent.get());
+ if (pAccDoc)
+ {
+ m_pScDoc = pAccDoc->GetDocument();
+ m_curCellAddress =pAccDoc->GetCurCellAddress();
+ }
+ }
+}
+
+ScAccessibleEditObject::~ScAccessibleEditObject()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessibleEditObject::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpTextHelper.reset();
+
+ ScAccessibleContextBase::disposing();
+}
+
+void ScAccessibleEditObject::LostFocus()
+{
+ mbHasFocus = false;
+ if (mpTextHelper)
+ mpTextHelper->SetFocus(false);
+ CommitFocusLost();
+}
+
+void ScAccessibleEditObject::GotFocus()
+{
+ mbHasFocus = true;
+ CommitFocusGained();
+ if (mpTextHelper)
+ mpTextHelper->SetFocus();
+}
+
+//===== XInterface ==========================================================
+
+css::uno::Any SAL_CALL
+ ScAccessibleEditObject::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = ScAccessibleContextBase::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast< css::accessibility::XAccessibleSelection* >(this)
+ );
+ return aReturn;
+}
+void SAL_CALL
+ ScAccessibleEditObject::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire ();
+}
+void SAL_CALL
+ ScAccessibleEditObject::release()
+ noexcept
+{
+ ScAccessibleContextBase::release ();
+}
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleEditObject::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ CreateTextHelper();
+
+ xRet = mpTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleEditObject::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aScreenBounds;
+
+ if ( mpWindow )
+ {
+ if ( meObjectType == CellInEditMode )
+ {
+ if ( mpEditView && mpEditView->GetEditEngine() )
+ {
+ MapMode aMapMode( mpEditView->GetEditEngine()->GetRefMapMode() );
+ tools::Rectangle aScreenBoundsLog = mpWindow->LogicToPixel( mpEditView->GetOutputArea(), aMapMode );
+ Point aCellLoc = aScreenBoundsLog.TopLeft();
+ AbsoluteScreenPixelRectangle aWindowRect = mpWindow->GetWindowExtentsAbsolute();
+ AbsoluteScreenPixelPoint aWindowLoc = aWindowRect.TopLeft();
+ AbsoluteScreenPixelPoint aPos( aCellLoc.getX() + aWindowLoc.getX(), aCellLoc.getY() + aWindowLoc.getY() );
+ aScreenBounds = AbsoluteScreenPixelRectangle( aPos, aScreenBoundsLog.GetSize() );
+ }
+ }
+ else
+ {
+ aScreenBounds = mpWindow->GetWindowExtentsAbsolute();
+ }
+ }
+
+ return aScreenBounds;
+}
+
+tools::Rectangle ScAccessibleEditObject::GetBoundingBox() const
+{
+ tools::Rectangle aBounds( GetBoundingBoxOnScreen() );
+
+ if ( mpWindow )
+ {
+ uno::Reference< XAccessible > xThis( mpWindow->GetAccessible() );
+ if ( xThis.is() )
+ {
+ uno::Reference< XAccessibleContext > xContext( xThis->getAccessibleContext() );
+ if ( xContext.is() )
+ {
+ uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() );
+ if ( xParent.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY );
+ if ( xParentComponent.is() )
+ {
+ Point aScreenLoc = aBounds.TopLeft();
+ awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
+ Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y );
+ aBounds.SetPos( aPos );
+ }
+ }
+ }
+ }
+ }
+
+ return aBounds;
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL
+ ScAccessibleEditObject::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ CreateTextHelper();
+ return mpTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessibleEditObject::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ CreateTextHelper();
+ return mpTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessibleEditObject::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ // all states are const, because this object exists only in one state
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::SENSITIVE;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+OUString
+ ScAccessibleEditObject::createAccessibleDescription()
+{
+// OSL_FAIL("Should never be called, because is set in the constructor.")
+ return OUString();
+}
+
+OUString
+ ScAccessibleEditObject::createAccessibleName()
+{
+ OSL_FAIL("Should never be called, because is set in the constructor.");
+ return OUString();
+}
+
+ ///===== XAccessibleEventBroadcaster =====================================
+
+void SAL_CALL
+ ScAccessibleEditObject::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ CreateTextHelper();
+
+ mpTextHelper->AddEventListener(xListener);
+
+ ScAccessibleContextBase::addAccessibleEventListener(xListener);
+}
+
+void SAL_CALL
+ ScAccessibleEditObject::removeAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ CreateTextHelper();
+
+ mpTextHelper->RemoveEventListener(xListener);
+
+ ScAccessibleContextBase::removeAccessibleEventListener(xListener);
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleEditObject::getImplementationName()
+{
+ return "ScAccessibleEditObject";
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleEditObject::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+ //==== internal =========================================================
+
+bool ScAccessibleEditObject::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+OutputDevice* ScAccessibleEditObject::GetOutputDeviceForView()
+{
+ return mpWindow->GetOutDev();
+}
+
+void ScAccessibleEditObject::CreateTextHelper()
+{
+ if (mpTextHelper)
+ return;
+
+ ::std::unique_ptr < ScAccessibleTextData > pAccessibleTextData;
+ if (meObjectType == CellInEditMode || meObjectType == EditControl)
+ {
+ pAccessibleTextData.reset
+ (new ScAccessibleEditObjectTextData(mpEditView, GetOutputDeviceForView()));
+ }
+ else
+ {
+ pAccessibleTextData.reset
+ (new ScAccessibleEditLineTextData(nullptr, GetOutputDeviceForView(), mpTextWnd));
+ }
+
+ std::unique_ptr<ScAccessibilityEditSource> pEditSrc =
+ std::make_unique<ScAccessibilityEditSource>(std::move(pAccessibleTextData));
+
+ mpTextHelper = std::make_unique<::accessibility::AccessibleTextHelper>(std::move(pEditSrc));
+ mpTextHelper->SetEventSource(this);
+
+ const ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl && pInputHdl->IsEditMode() )
+ {
+ mpTextHelper->SetFocus();
+ }
+ else
+ {
+ mpTextHelper->SetFocus(mbHasFocus);
+ }
+
+ // #i54814# activate cell in edit mode
+ if( meObjectType == CellInEditMode )
+ {
+ // do not activate cell object, if top edit line is active
+ if( pInputHdl && !pInputHdl->IsTopMode() )
+ {
+ SdrHint aHint( SdrHintKind::BeginEdit );
+ mpTextHelper->GetEditSource().GetBroadcaster().Broadcast( aHint );
+ }
+ }
+}
+
+sal_Int32 SAL_CALL ScAccessibleEditObject::getForeground( )
+{
+ return GetFgBgColor(SC_UNONAME_CCOLOR);
+}
+
+sal_Int32 SAL_CALL ScAccessibleEditObject::getBackground( )
+{
+ return GetFgBgColor(SC_UNONAME_CELLBACK);
+}
+
+sal_Int32 ScAccessibleEditObject::GetFgBgColor( const OUString &strPropColor)
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nColor(0);
+ if (m_pScDoc)
+ {
+ ScDocShell* pObjSh = m_pScDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(m_curCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(m_curCellAddress.Col(), m_curCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(strPropColor);
+ aAny >>= nColor;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nColor;
+}
+//===== XAccessibleSelection ============================================
+
+void SAL_CALL ScAccessibleEditObject::selectAccessibleChild( sal_Int64 )
+{
+}
+
+sal_Bool SAL_CALL ScAccessibleEditObject::isAccessibleChildSelected( sal_Int64 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;
+ }
+ }
+ }
+ return false;
+}
+
+void SAL_CALL ScAccessibleEditObject::clearAccessibleSelection( )
+{
+}
+
+void SAL_CALL ScAccessibleEditObject::selectAllAccessibleChildren( )
+{
+}
+
+sal_Int64 SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChildCount()
+{
+ sal_Int64 nCount = 0;
+ sal_Int64 TotalCount = getAccessibleChildCount();
+ for( sal_Int64 i = 0; i < TotalCount; i++ )
+ if( isAccessibleChildSelected(i) ) nCount++;
+ return nCount;
+}
+
+uno::Reference<XAccessible> SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ if ( nSelectedChildIndex < 0 || nSelectedChildIndex > getSelectedAccessibleChildCount() )
+ throw IndexOutOfBoundsException();
+
+ for (sal_Int64 i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ )
+ if( isAccessibleChildSelected(i1) )
+ {
+ if( i2 == nSelectedChildIndex )
+ return getAccessibleChild( i1 );
+ i2++;
+ }
+ return uno::Reference<XAccessible>();
+}
+
+void SAL_CALL ScAccessibleEditObject::deselectAccessibleChild(sal_Int64)
+{
+}
+
+uno::Reference< XAccessibleRelationSet > ScAccessibleEditObject::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+ vcl::Window* pWindow = mpWindow;
+ rtl::Reference<utl::AccessibleRelationSetHelper> rRelationSet = new utl::AccessibleRelationSetHelper;
+ if ( pWindow )
+ {
+ vcl::Window *pLabeledBy = pWindow->GetAccessibleRelationLabeledBy();
+ if ( pLabeledBy && pLabeledBy != pWindow )
+ {
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabeledBy->GetAccessible() };
+ rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) );
+ }
+ vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf();
+ if ( pMemberOf && pMemberOf != pWindow )
+ {
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pMemberOf->GetAccessible() };
+ rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) );
+ }
+ return rRelationSet;
+ }
+ return uno::Reference< XAccessibleRelationSet >();
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleEditControlObject::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aScreenBounds;
+
+ if (m_pController && m_pController->GetDrawingArea())
+ {
+ aScreenBounds = AbsoluteScreenPixelRectangle(m_pController->GetDrawingArea()->get_accessible_location_on_screen(),
+ m_pController->GetOutputSizePixel());
+ }
+
+ return aScreenBounds;
+}
+
+tools::Rectangle ScAccessibleEditControlObject::GetBoundingBox() const
+{
+ tools::Rectangle aBounds( GetBoundingBoxOnScreen() );
+
+ uno::Reference< XAccessibleContext > xContext(const_cast<ScAccessibleEditControlObject*>(this)->getAccessibleContext());
+ if ( xContext.is() )
+ {
+ uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() );
+ if ( xParent.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY );
+ if ( xParentComponent.is() )
+ {
+ Point aScreenLoc = aBounds.TopLeft();
+ awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
+ Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y );
+ aBounds.SetPos( aPos );
+ }
+ }
+ }
+
+ return aBounds;
+}
+
+void SAL_CALL ScAccessibleEditControlObject::disposing()
+{
+ ScAccessibleEditObject::disposing();
+ m_pController = nullptr;
+}
+
+uno::Reference< XAccessibleRelationSet > ScAccessibleEditControlObject::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pController || !m_pController->GetDrawingArea())
+ return uno::Reference< XAccessibleRelationSet >();
+ return m_pController->GetDrawingArea()->get_accessible_relation_set();
+}
+
+OutputDevice* ScAccessibleEditControlObject::GetOutputDeviceForView()
+{
+ if (!m_pController || !m_pController->GetDrawingArea())
+ return nullptr;
+ return &m_pController->GetDrawingArea()->get_ref_device();
+}
+
+ScAccessibleEditLineObject::ScAccessibleEditLineObject(ScTextWnd* pTextWnd)
+ : ScAccessibleEditControlObject(pTextWnd, ScAccessibleEditObject::EditLine)
+{
+ // tdf#141769 set this early so its always available, even before the on-demand
+ // editview is created
+ mpTextWnd = pTextWnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePageHeader.cxx b/sc/source/ui/Accessibility/AccessiblePageHeader.cxx
new file mode 100644
index 0000000000..a79017276b
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePageHeader.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 <AccessiblePageHeader.hxx>
+#include <AccessiblePageHeaderArea.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <vcl/window.hxx>
+#include <svl/hint.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/style.hxx>
+#include <editeng/editobj.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+const sal_uInt8 MAX_AREAS = 3;
+
+ScAccessiblePageHeader::ScAccessiblePageHeader( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, bool bHeader, sal_Int32 nIndex ) :
+ScAccessibleContextBase( rxParent, bHeader ? AccessibleRole::HEADER : AccessibleRole::FOOTER ),
+ mpViewShell( pViewShell ),
+ mnIndex( nIndex ),
+ mbHeader( bHeader ),
+ maAreas(MAX_AREAS, rtl::Reference<ScAccessiblePageHeaderArea>()),
+ mnChildCount(-1)
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePageHeader::~ScAccessiblePageHeader()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePageHeader::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ for (auto & i : maAreas)
+ {
+ if (i.is())
+ {
+ i->dispose();
+ i.clear();
+ }
+ }
+
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePageHeader::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ // only notify if child exist, otherwise it is not necessary
+ if (rHint.GetId() == SfxHintId::ScDataChanged)
+ {
+ std::vector<rtl::Reference<ScAccessiblePageHeaderArea>> aOldAreas(maAreas);
+ mnChildCount = -1;
+ getAccessibleChildCount();
+ for (sal_uInt8 i = 0; i < MAX_AREAS; ++i)
+ {
+ if ((aOldAreas[i].is() && maAreas[i].is() && !ScGlobal::EETextObjEqual(aOldAreas[i]->GetEditTextObject(), maAreas[i]->GetEditTextObject())) ||
+ (aOldAreas[i].is() && !maAreas[i].is()) || (!aOldAreas[i].is() && maAreas[i].is()))
+ {
+ if (aOldAreas[i].is() && aOldAreas[i]->GetEditTextObject())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(aOldAreas[i]);
+ aEvent.IndexHint = -1;
+
+ CommitChange(aEvent); // child gone - event
+ aOldAreas[i]->dispose();
+ }
+ if (maAreas[i].is() && maAreas[i]->GetEditTextObject())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= uno::Reference<XAccessible>(maAreas[i]);
+ aEvent.IndexHint = -1;
+
+ CommitChange(aEvent); // new child - event
+ }
+ }
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeader::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ uno::Reference<XAccessible> xRet;
+
+ if (containsPoint(aPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ sal_Int64 nCount(getAccessibleChildCount()); // fill the areas
+
+ if (nCount)
+ {
+ // return the first with content, because they have all the same Bounding Box
+ sal_uInt8 i(0);
+ while(!xRet.is() && i < MAX_AREAS)
+ {
+ if (maAreas[i].is())
+ xRet = maAreas[i].get();
+ else
+ ++i;
+ }
+ }
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePageHeader::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePageHeader::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if((mnChildCount < 0) && mpViewShell)
+ {
+ mnChildCount = 0;
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ // find out how many regions (left,center, right) are with content
+
+ SfxStyleSheetBase* pStyle = rDoc.GetStyleSheetPool()->Find(rDoc.GetPageStyle(mpViewShell->GetLocationData().GetPrintTab()), SfxStyleFamily::Page);
+ if (pStyle)
+ {
+ sal_uInt16 nPageWhichId(0);
+ if (mbHeader)
+ nPageWhichId = mpViewShell->GetLocationData().IsHeaderLeft() ? ATTR_PAGE_HEADERLEFT : ATTR_PAGE_HEADERRIGHT;
+ else
+ nPageWhichId = mpViewShell->GetLocationData().IsFooterLeft() ? ATTR_PAGE_FOOTERLEFT : ATTR_PAGE_FOOTERRIGHT;
+
+ const ScPageHFItem& rPageItem = static_cast<const ScPageHFItem&>(pStyle->GetItemSet().Get(nPageWhichId));
+ AddChild(rPageItem.GetLeftArea(), 0, SvxAdjust::Left);
+ AddChild(rPageItem.GetCenterArea(), 1, SvxAdjust::Center);
+ AddChild(rPageItem.GetRightArea(), 2, SvxAdjust::Right);
+ }
+ }
+
+ return mnChildCount;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeader::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ uno::Reference<XAccessible> xRet;
+
+ if(mnChildCount < 0)
+ getAccessibleChildCount();
+
+ if (nIndex >= 0)
+ for (const auto& rxArea : maAreas)
+ {
+ if (rxArea.is())
+ {
+ if (nIndex == 0)
+ {
+ xRet = rxArea.get();
+ break;
+ }
+ else
+ --nIndex;
+ }
+ }
+
+ if ( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePageHeader::getAccessibleIndexInParent()
+{
+ return mnIndex;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePageHeader::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePageHeader::getImplementationName()
+{
+ return "ScAccessiblePageHeader";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePageHeader::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.text.AccessibleHeaderFooterView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//==== internal =========================================================
+
+OUString ScAccessiblePageHeader::createAccessibleDescription()
+{
+ OUString sDesc(mbHeader ? STR_ACC_HEADER_DESCR : STR_ACC_FOOTER_DESCR);
+ return sDesc.replaceFirst("%1", ScResId(SCSTR_UNKNOWN));
+}
+
+OUString ScAccessiblePageHeader::createAccessibleName()
+{
+ OUString sName(ScResId(mbHeader ? STR_ACC_HEADER_NAME : STR_ACC_FOOTER_NAME));
+ return sName.replaceFirst("%1", ScResId(SCSTR_UNKNOWN));
+}
+
+AbsoluteScreenPixelRectangle ScAccessiblePageHeader::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect(GetBoundingBox());
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePageHeader::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ if ( mbHeader )
+ rData.GetHeaderPosition( aRect );
+ else
+ rData.GetFooterPosition( aRect );
+
+ // the Rectangle could contain negative coordinates so it should be clipped
+ tools::Rectangle aClipRect(Point(0, 0), aRect.GetSize());
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ aClipRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
+ aRect = aClipRect.GetIntersection(aRect);
+ }
+ if (aRect.IsEmpty())
+ aRect.SetSize(Size(-1, -1));
+
+ return aRect;
+}
+
+bool ScAccessiblePageHeader::IsDefunc( sal_Int64 nParentStates )
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessiblePageHeader::AddChild(const EditTextObject* pArea, sal_uInt32 nIndex, SvxAdjust eAdjust)
+{
+ if (pArea && (!pArea->GetText(0).isEmpty() || (pArea->GetParagraphCount() > 1)))
+ {
+ if (maAreas[nIndex].is())
+ {
+ if (!ScGlobal::EETextObjEqual(maAreas[nIndex]->GetEditTextObject(), pArea))
+ {
+ maAreas[nIndex] = new ScAccessiblePageHeaderArea(this, mpViewShell, pArea, eAdjust);
+ }
+ }
+ else
+ {
+ maAreas[nIndex] = new ScAccessiblePageHeaderArea(this, mpViewShell, pArea, eAdjust);
+ }
+ ++mnChildCount;
+ }
+ else
+ {
+ maAreas[nIndex].clear();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx b/sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx
new file mode 100644
index 0000000000..ec1fbf3add
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/gen.hxx>
+#include <AccessiblePageHeaderArea.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <prevwsh.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <comphelper/sequence.hxx>
+#include <editeng/editobj.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+ //===== internal ========================================================
+
+ScAccessiblePageHeaderArea::ScAccessiblePageHeaderArea(
+ const uno::Reference<XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const EditTextObject* pEditObj,
+ SvxAdjust eAdjust)
+ : ScAccessibleContextBase(rxParent, AccessibleRole::TEXT),
+ mpEditObj(pEditObj->Clone()),
+ mpViewShell(pViewShell),
+ meAdjust(eAdjust)
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePageHeaderArea::~ScAccessiblePageHeaderArea()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePageHeaderArea::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpTextHelper.reset();
+ mpEditObj.reset();
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePageHeaderArea::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ // only notify if child exist, otherwise it is not necessary
+ if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ if (mpTextHelper)
+ mpTextHelper->UpdateChildren();
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeaderArea::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if(!mpTextHelper)
+ CreateTextHelper();
+
+ xRet = mpTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL
+ ScAccessiblePageHeaderArea::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessiblePageHeaderArea::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessiblePageHeaderArea::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc())
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+// XServiceInfo
+
+OUString SAL_CALL
+ ScAccessiblePageHeaderArea::getImplementationName()
+{
+ return "ScAccessiblePageHeaderArea";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessiblePageHeaderArea::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.sheet.AccessiblePageHeaderFooterAreasView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessiblePageHeaderArea::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//===== internal ==============================================================
+OUString ScAccessiblePageHeaderArea::createAccessibleDescription()
+{
+ OUString sDesc;
+ switch (meAdjust)
+ {
+ case SvxAdjust::Left :
+ sDesc = STR_ACC_LEFTAREA_DESCR;
+ break;
+ case SvxAdjust::Right:
+ sDesc = STR_ACC_RIGHTAREA_DESCR;
+ break;
+ case SvxAdjust::Center:
+ sDesc = STR_ACC_CENTERAREA_DESCR;
+ break;
+ default:
+ OSL_FAIL("wrong adjustment found");
+ }
+
+ return sDesc;
+}
+
+OUString ScAccessiblePageHeaderArea::createAccessibleName()
+{
+ OUString sName;
+ switch (meAdjust)
+ {
+ case SvxAdjust::Left :
+ sName = ScResId(STR_ACC_LEFTAREA_NAME);
+ break;
+ case SvxAdjust::Right:
+ sName = ScResId(STR_ACC_RIGHTAREA_NAME);
+ break;
+ case SvxAdjust::Center:
+ sName = ScResId(STR_ACC_CENTERAREA_NAME);
+ break;
+ default:
+ OSL_FAIL("wrong adjustment found");
+ }
+
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessiblePageHeaderArea::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xContext = mxParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xComp(xContext, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ // has the same size and position on screen like the parent
+ aRect = AbsoluteScreenPixelRectangle(
+ AbsoluteScreenPixelPoint(VCLPoint(xComp->getLocationOnScreen())),
+ AbsoluteScreenPixelSize(VCLRectangle(xComp->getBounds()).GetSize()));
+ }
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessiblePageHeaderArea::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xContext = mxParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xComp(xContext, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ // has the same size and position on screen like the parent and so the pos is (0, 0)
+ tools::Rectangle aNewRect(Point(0, 0), VCLRectangle(xComp->getBounds()).GetSize());
+ aRect = aNewRect;
+ }
+ }
+
+ return aRect;
+}
+
+void ScAccessiblePageHeaderArea::CreateTextHelper()
+{
+ if (!mpTextHelper)
+ {
+ mpTextHelper.reset( new ::accessibility::AccessibleTextHelper(
+ std::make_unique<ScAccessibilityEditSource>(
+ std::make_unique<ScAccessibleHeaderTextData>(
+ mpViewShell, mpEditObj.get(), meAdjust))) );
+ mpTextHelper->SetEventSource(this);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePreviewCell.cxx b/sc/source/ui/Accessibility/AccessiblePreviewCell.cxx
new file mode 100644
index 0000000000..3c4d334305
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePreviewCell.cxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <scitems.hxx>
+#include <tools/gen.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <AccessiblePreviewCell.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <document.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <editeng/brushitem.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessiblePreviewCell::ScAccessiblePreviewCell( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int32 nIndex ) :
+ ScAccessibleCellBase( rxParent, ( pViewShell ? &pViewShell->GetDocument() : nullptr ), rCellAddress, nIndex ),
+ mpViewShell( pViewShell )
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePreviewCell::~ScAccessiblePreviewCell()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePreviewCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ mpTextHelper.reset();
+
+ ScAccessibleCellBase::disposing();
+}
+
+void ScAccessiblePreviewCell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ if (mpTextHelper)
+ mpTextHelper->UpdateChildren();
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewCell::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if(!mpTextHelper)
+ CreateTextHelper();
+
+ xRet = mpTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePreviewCell::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePreviewCell::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewCell::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ if (IsOpaque())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ // MANAGES_DESCENDANTS (for paragraphs)
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePreviewCell::getImplementationName()
+{
+ return "ScAccessiblePreviewCell";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePreviewCell::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.table.AccessibleCellView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessiblePreviewCell::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//==== internal =========================================================
+
+AbsoluteScreenPixelRectangle ScAccessiblePreviewCell::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect;
+ if (mpViewShell)
+ {
+ mpViewShell->GetLocationData().GetCellPosition( maCellAddress, aCellRect );
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePreviewCell::GetBoundingBox() const
+{
+ tools::Rectangle aCellRect;
+ if (mpViewShell)
+ {
+ mpViewShell->GetLocationData().GetCellPosition( maCellAddress, aCellRect );
+ uno::Reference<XAccessible> xAccParent = const_cast<ScAccessiblePreviewCell*>(this)->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference<XAccessibleContext> xAccParentContext = xAccParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xAccParentComp (xAccParentContext, uno::UNO_QUERY);
+ if (xAccParentComp.is())
+ {
+ tools::Rectangle aParentRect (VCLRectangle(xAccParentComp->getBounds()));
+ aCellRect.Move(-aParentRect.Left(), -aParentRect.Top());
+ }
+ }
+ }
+ return aCellRect;
+}
+
+bool ScAccessiblePreviewCell::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+bool ScAccessiblePreviewCell::IsEditable(sal_Int64 /* nParentStates */)
+{
+ return false;
+}
+
+bool ScAccessiblePreviewCell::IsOpaque() const
+{
+ // test whether there is a background color
+ //! could be moved to ScAccessibleCellBase
+
+ bool bOpaque(true);
+ if (mpDoc)
+ {
+ const SvxBrushItem* pItem = mpDoc->GetAttr(maCellAddress, ATTR_BACKGROUND);
+ if (pItem)
+ bOpaque = pItem->GetColor() != COL_TRANSPARENT;
+ }
+ return bOpaque;
+}
+
+void ScAccessiblePreviewCell::CreateTextHelper()
+{
+ if (mpTextHelper)
+ return;
+
+ mpTextHelper.reset( new ::accessibility::AccessibleTextHelper(
+ std::make_unique<ScAccessibilityEditSource>(
+ std::make_unique<ScAccessiblePreviewCellTextData>(
+ mpViewShell, maCellAddress))) );
+ mpTextHelper->SetEventSource( this );
+
+ // paragraphs in preview are transient
+ mpTextHelper->SetAdditionalChildStates( AccessibleStateType::TRANSIENT );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx b/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx
new file mode 100644
index 0000000000..d772db890a
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/gen.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <AccessiblePreviewHeaderCell.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <strings.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/hint.hxx>
+#include <toolkit/helper/convert.hxx>
+
+#ifdef indices
+#undef indices
+#endif
+
+#ifdef extents
+#undef extents
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessiblePreviewHeaderCell::ScAccessiblePreviewHeaderCell( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const ScAddress& rCellPos, bool bIsColHdr, bool bIsRowHdr,
+ sal_Int32 nIndex ) :
+ ScAccessibleContextBase( rxParent, AccessibleRole::TABLE_CELL ),
+ mpViewShell( pViewShell ),
+ mnIndex( nIndex ),
+ maCellPos( rCellPos ),
+ mbColumnHeader( bIsColHdr ),
+ mbRowHeader( bIsRowHdr )
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePreviewHeaderCell::~ScAccessiblePreviewHeaderCell()
+{
+ if (mpViewShell)
+ mpViewShell->RemoveAccessibilityObject(*this);
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ mpTableInfo.reset();
+
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePreviewHeaderCell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ const SfxHintId nId = rHint.GetId();
+ if (nId == SfxHintId::ScAccVisAreaChanged)
+ {
+ if (mxTextHelper)
+ mxTextHelper->UpdateChildren();
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ // column / row layout may change with any document change,
+ // so it must be invalidated
+ mpTableInfo.reset();
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessiblePreviewHeaderCellImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+//===== XAccessibleValue ================================================
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getCurrentValue()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ double fValue(0.0);
+ if (mbColumnHeader)
+ fValue = maCellPos.Col();
+ else
+ fValue = maCellPos.Row();
+
+ return uno::Any(fValue);
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewHeaderCell::setCurrentValue( const uno::Any& /* aNumber */ )
+{
+ // it is not possible to set a value
+ return false;
+}
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getMaximumValue()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ double fValue(0.0);
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ if (mbColumnHeader)
+ fValue = rDoc.MaxCol();
+ else
+ fValue = rDoc.MaxRow();
+ return uno::Any(fValue);
+}
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getMinimumValue()
+{
+ return uno::Any(0.0);
+}
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getMinimumIncrement()
+{
+ // value can't be changed, s. 'setCurrentValue'
+ return uno::Any();
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if(!mxTextHelper)
+ CreateTextHelper();
+
+ xRet = mxTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mxTextHelper)
+ CreateTextHelper();
+ return mxTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mxTextHelper)
+ CreateTextHelper();
+ return mxTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleIndexInParent()
+{
+ return mnIndex;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePreviewHeaderCell::getImplementationName()
+{
+ return "ScAccessiblePreviewHeaderCell";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePreviewHeaderCell::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.table.AccessibleCellView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessiblePreviewHeaderCell::getTypes()
+{
+ return comphelper::concatSequences(ScAccessiblePreviewHeaderCellImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessiblePreviewHeaderCell::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//==== internal =========================================================
+
+AbsoluteScreenPixelRectangle ScAccessiblePreviewHeaderCell::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect;
+
+ FillTableInfo();
+
+ if (mpTableInfo)
+ {
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[maCellPos.Col()];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[maCellPos.Row()];
+
+ aCellRect = tools::Rectangle( rColInfo.nPixelStart, rRowInfo.nPixelStart, rColInfo.nPixelEnd, rRowInfo.nPixelEnd );
+ }
+
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePreviewHeaderCell::GetBoundingBox() const
+{
+ FillTableInfo();
+
+ if (mpTableInfo)
+ {
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[maCellPos.Col()];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[maCellPos.Row()];
+
+ tools::Rectangle aCellRect( rColInfo.nPixelStart, rRowInfo.nPixelStart, rColInfo.nPixelEnd, rRowInfo.nPixelEnd );
+ uno::Reference<XAccessible> xAccParent = const_cast<ScAccessiblePreviewHeaderCell*>(this)->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference<XAccessibleContext> xAccParentContext = xAccParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xAccParentComp (xAccParentContext, uno::UNO_QUERY);
+ if (xAccParentComp.is())
+ {
+ tools::Rectangle aParentRect (VCLRectangle(xAccParentComp->getBounds()));
+ aCellRect.Move(-aParentRect.Left(), -aParentRect.Top());
+ }
+ }
+ return aCellRect;
+ }
+ return tools::Rectangle();
+}
+
+OUString ScAccessiblePreviewHeaderCell::createAccessibleDescription()
+{
+ return STR_ACC_HEADERCELL_DESCR;
+}
+
+OUString ScAccessiblePreviewHeaderCell::createAccessibleName()
+{
+ OUString sName = STR_ACC_HEADERCELL_NAME;
+
+ if ( mbColumnHeader )
+ {
+ if ( mbRowHeader )
+ {
+ //! name for corner cell?
+
+// sName = "Column/Row Header";
+ }
+ else
+ {
+ // name of column header
+ sName += ScColToAlpha( maCellPos.Col() );
+ }
+ }
+ else
+ {
+ // name of row header
+ sName += OUString::number( maCellPos.Row() + 1 );
+ }
+
+ return sName;
+}
+
+bool ScAccessiblePreviewHeaderCell::IsDefunc( sal_Int64 nParentStates )
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessiblePreviewHeaderCell::CreateTextHelper()
+{
+ if (!mxTextHelper)
+ {
+ mxTextHelper.reset( new ::accessibility::AccessibleTextHelper(
+ std::make_unique<ScAccessibilityEditSource>(
+ std::make_unique<ScAccessiblePreviewHeaderCellTextData>(
+ mpViewShell, getAccessibleName(), maCellPos,
+ mbColumnHeader, mbRowHeader))) );
+ mxTextHelper->SetEventSource(this);
+ }
+}
+
+void ScAccessiblePreviewHeaderCell::FillTableInfo() const
+{
+ if ( mpViewShell && !mpTableInfo )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+
+ mpTableInfo.reset( new ScPreviewTableInfo );
+ mpViewShell->GetLocationData().GetTableInfo( aVisRect, *mpTableInfo );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePreviewTable.cxx b/sc/source/ui/Accessibility/AccessiblePreviewTable.cxx
new file mode 100644
index 0000000000..925ad1075c
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePreviewTable.cxx
@@ -0,0 +1,640 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <AccessiblePreviewTable.hxx>
+#include <AccessiblePreviewCell.hxx>
+#include <AccessiblePreviewHeaderCell.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <attrib.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/hint.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessiblePreviewTable::ScAccessiblePreviewTable( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, sal_Int32 nIndex ) :
+ ScAccessibleContextBase( rxParent, AccessibleRole::TABLE ),
+ mpViewShell( pViewShell ),
+ mnIndex( nIndex )
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePreviewTable::~ScAccessiblePreviewTable()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePreviewTable::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ mpTableInfo.reset();
+
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePreviewTable::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::DataChanged )
+ {
+ // column / row layout may change with any document change,
+ // so it must be invalidated
+ mpTableInfo.reset();
+ }
+ else if (nId == SfxHintId::ScAccVisAreaChanged)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessiblePreviewTable::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessiblePreviewTableImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessiblePreviewTable::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessiblePreviewTable::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+//===== XAccessibleTable ================================================
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRowCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nRet = 0;
+ if ( mpTableInfo )
+ nRet = mpTableInfo->GetRows();
+ return nRet;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nRet = 0;
+ if ( mpTableInfo )
+ nRet = mpTableInfo->GetCols();
+ return nRet;
+}
+
+OUString SAL_CALL ScAccessiblePreviewTable::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nRow < 0 || (mpTableInfo && nRow >= mpTableInfo->GetRows()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return OUString();
+}
+
+OUString SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nColumn < 0 || (mpTableInfo && nColumn >= mpTableInfo->GetCols()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nRows = 1;
+ if ( !mpViewShell || !mpTableInfo || nColumn < 0 || nRow < 0 ||
+ nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[nColumn];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[nRow];
+
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ {
+ // header cells only span a single cell
+ }
+ else
+ {
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ const ScMergeAttr* pItem = rDoc.GetAttr(
+ static_cast<SCCOL>(rColInfo.nDocIndex), static_cast<SCROW>(rRowInfo.nDocIndex), mpTableInfo->GetTab(), ATTR_MERGE );
+ if ( pItem && pItem->GetRowMerge() > 0 )
+ nRows = pItem->GetRowMerge();
+ }
+
+ return nRows;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nColumns = 1;
+ if ( !mpViewShell || !mpTableInfo || nColumn < 0 || nRow < 0 ||
+ nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[nColumn];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[nRow];
+
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ {
+ // header cells only span a single cell
+ }
+ else
+ {
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ const ScMergeAttr* pItem = rDoc.GetAttr(
+ static_cast<SCCOL>(rColInfo.nDocIndex), static_cast<SCROW>(rRowInfo.nDocIndex), mpTableInfo->GetTab(), ATTR_MERGE );
+ if ( pItem && pItem->GetColMerge() > 0 )
+ nColumns = pItem->GetColMerge();
+ }
+
+ return nColumns;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessiblePreviewTable::getAccessibleRowHeaders()
+{
+ //! missing
+ return nullptr;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnHeaders()
+{
+ //! missing
+ return nullptr;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessiblePreviewTable::getSelectedAccessibleRows()
+{
+ // in the page preview, there is no selection
+ return {};
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessiblePreviewTable::getSelectedAccessibleColumns()
+{
+ // in the page preview, there is no selection
+ return {};
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewTable::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ // in the page preview, there is no selection
+
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nRow < 0 || (mpTableInfo && nRow >= mpTableInfo->GetRows()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewTable::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ // in the page preview, there is no selection
+
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nColumn < 0 || (mpTableInfo && nColumn >= mpTableInfo->GetCols()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return false;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ uno::Reference<XAccessible> xRet;
+ if ( mpTableInfo && nColumn >= 0 && nRow >= 0 && nColumn < mpTableInfo->GetCols() && nRow < mpTableInfo->GetRows() )
+ {
+ // index iterates horizontally
+ tools::Long nNewIndex = nRow * mpTableInfo->GetCols() + nColumn;
+
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[nColumn];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[nRow];
+
+ ScAddress aCellPos( static_cast<SCCOL>(rColInfo.nDocIndex), static_cast<SCROW>(rRowInfo.nDocIndex), mpTableInfo->GetTab() );
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ {
+ const bool bRotatedColHeader = rRowInfo.bIsHeader;
+ const bool bRotatedRowHeader = rColInfo.bIsHeader;
+ rtl::Reference<ScAccessiblePreviewHeaderCell> pHeaderCell(new ScAccessiblePreviewHeaderCell(this, mpViewShell, aCellPos,
+ bRotatedColHeader, bRotatedRowHeader, nNewIndex));
+ xRet = pHeaderCell.get();
+ pHeaderCell->Init();
+ }
+ else
+ {
+ rtl::Reference<ScAccessiblePreviewCell> pCell(new ScAccessiblePreviewCell( this, mpViewShell, aCellPos, nNewIndex ));
+ xRet = pCell.get();
+ pCell->Init();
+ }
+ }
+
+ if ( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleCaption()
+{
+ //! missing
+ return nullptr;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleSummary()
+{
+ //! missing
+ return nullptr;
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewTable::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ // in the page preview, there is no selection
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nColumn < 0 || nRow < 0 || nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ // index iterates horizontally
+ return false;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nColumn < 0 || nRow < 0 || nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ // index iterates horizontally
+ sal_Int64 nRet = static_cast<sal_Int64>(nRow) * static_cast<sal_Int64>(mpTableInfo->GetCols()) + nColumn;
+ return nRet;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nChildIndex < 0 || nChildIndex >= static_cast<sal_Int64>(mpTableInfo->GetRows()) * mpTableInfo->GetCols() )
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nRow = nChildIndex / mpTableInfo->GetCols();
+ return nRow;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nChildIndex < 0 || nChildIndex >= static_cast<sal_Int64>(mpTableInfo->GetRows()) * mpTableInfo->GetCols() )
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nCol = nChildIndex % static_cast<sal_Int32>(mpTableInfo->GetCols());
+ return nCol;
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(aPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( mpTableInfo )
+ {
+ SCCOL nCols = mpTableInfo->GetCols();
+ SCROW nRows = mpTableInfo->GetRows();
+ const ScPreviewColRowInfo* pColInfo = mpTableInfo->GetColInfo();
+ const ScPreviewColRowInfo* pRowInfo = mpTableInfo->GetRowInfo();
+
+ tools::Rectangle aScreenRect(GetBoundingBox());
+
+ awt::Point aMovedPoint = aPoint;
+ aMovedPoint.X += aScreenRect.Left();
+ aMovedPoint.Y += aScreenRect.Top();
+
+ if ( nCols > 0 && nRows > 0 && aMovedPoint.X >= pColInfo[0].nPixelStart && aMovedPoint.Y >= pRowInfo[0].nPixelStart )
+ {
+ SCCOL nColIndex = 0;
+ while ( nColIndex < nCols && aMovedPoint.X > pColInfo[nColIndex].nPixelEnd )
+ ++nColIndex;
+ SCROW nRowIndex = 0;
+ while ( nRowIndex < nRows && aMovedPoint.Y > pRowInfo[nRowIndex].nPixelEnd )
+ ++nRowIndex;
+ if ( nColIndex < nCols && nRowIndex < nRows )
+ {
+ try
+ {
+ xRet = getAccessibleCellAt( nRowIndex, nColIndex );
+ }
+ catch (uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePreviewTable::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ tools::Long nRet = 0;
+ if ( mpTableInfo )
+ nRet = static_cast<sal_Int64>(mpTableInfo->GetCols()) * mpTableInfo->GetRows();
+ return nRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ uno::Reference<XAccessible> xRet;
+ if ( mpTableInfo )
+ {
+ sal_Int32 nColumns = mpTableInfo->GetCols();
+ if ( nColumns > 0 )
+ {
+ // nCol, nRow are within the visible table, not the document
+ sal_Int32 nCol = nIndex % nColumns;
+ sal_Int64 nRow = nIndex / nColumns;
+ assert(nRow <= std::numeric_limits<sal_Int32>::max());
+ xRet = getAccessibleCellAt( nRow, nCol );
+ }
+ }
+
+ if ( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleIndexInParent()
+{
+ return mnIndex;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePreviewTable::getImplementationName()
+{
+ return "ScAccessiblePreviewTable";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePreviewTable::getSupportedServiceNames()
+{
+ uno::Sequence< OUString > aSequence = ScAccessibleContextBase::getSupportedServiceNames();
+ sal_Int32 nOldSize(aSequence.getLength());
+ aSequence.realloc(nOldSize + 1);
+
+ aSequence.getArray()[nOldSize] = "com.sun.star.table.AccessibleTableView";
+
+ return aSequence;
+}
+
+//===== XTypeProvider ===================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessiblePreviewTable::getTypes()
+{
+ return comphelper::concatSequences(ScAccessiblePreviewTableImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScAccessiblePreviewTable::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//==== internal =========================================================
+
+OUString ScAccessiblePreviewTable::createAccessibleDescription()
+{
+ return STR_ACC_TABLE_DESCR;
+}
+
+OUString ScAccessiblePreviewTable::createAccessibleName()
+{
+ OUString sName(ScResId(STR_ACC_TABLE_NAME));
+
+ if (mpViewShell)
+ {
+ FillTableInfo();
+
+ if ( mpTableInfo )
+ {
+ OUString sCoreName;
+ if (mpViewShell->GetDocument().GetName( mpTableInfo->GetTab(), sCoreName ))
+ sName = sName.replaceFirst("%1", sCoreName);
+ }
+ }
+
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessiblePreviewTable::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect(GetBoundingBox());
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePreviewTable::GetBoundingBox() const
+{
+ FillTableInfo();
+
+ tools::Rectangle aRect;
+ if ( mpTableInfo )
+ {
+ SCCOL nColumns = mpTableInfo->GetCols();
+ SCROW nRows = mpTableInfo->GetRows();
+ if ( nColumns > 0 && nRows > 0 )
+ {
+ const ScPreviewColRowInfo* pColInfo = mpTableInfo->GetColInfo();
+ const ScPreviewColRowInfo* pRowInfo = mpTableInfo->GetRowInfo();
+
+ aRect = tools::Rectangle( pColInfo[0].nPixelStart,
+ pRowInfo[0].nPixelStart,
+ pColInfo[nColumns-1].nPixelEnd,
+ pRowInfo[nRows-1].nPixelEnd );
+ }
+ }
+ return aRect;
+}
+
+bool ScAccessiblePreviewTable::IsDefunc( sal_Int64 nParentStates )
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessiblePreviewTable::FillTableInfo() const
+{
+ if ( mpViewShell && !mpTableInfo )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+
+ mpTableInfo.reset( new ScPreviewTableInfo );
+ mpViewShell->GetLocationData().GetTableInfo( aVisRect, *mpTableInfo );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx
new file mode 100644
index 0000000000..7aaa7237cc
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx
@@ -0,0 +1,1669 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleSpreadsheet.hxx>
+#include <AccessibleCell.hxx>
+#include <AccessibleDocument.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <hints.hxx>
+#include <scmod.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <sal/log.hxx>
+#include <tools/gen.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/svapp.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+#include <algorithm>
+#include <cstdlib>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+static bool CompMinCol(const std::pair<sal_uInt16,sal_uInt16> & pc1,const std::pair<sal_uInt16,sal_uInt16> &pc2)
+{
+ return pc1.first < pc2.first;
+}
+
+ScMyAddress ScAccessibleSpreadsheet::CalcScAddressFromRangeList(ScRangeList *pMarkedRanges,sal_Int32 nSelectedChildIndex)
+{
+ if (pMarkedRanges->size() <= 1)
+ {
+ ScRange const & rRange = pMarkedRanges->front();
+ // MT IA2: Not used.
+ // const int nRowNum = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ const int nColNum = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ const int nCurCol = nSelectedChildIndex % nColNum;
+ const int nCurRow = (nSelectedChildIndex - nCurCol)/nColNum;
+ return ScMyAddress(static_cast<SCCOL>(rRange.aStart.Col() + nCurCol), rRange.aStart.Row() + nCurRow, maActiveCell.Tab());
+ }
+ else
+ {
+ ScDocument* pDoc= GetDocument(mpViewShell);
+ sal_Int32 nMinRow = pDoc->MaxRow();
+ sal_Int32 nMaxRow = 0;
+ std::vector<ScRange> aRanges;
+ size_t nSize = pMarkedRanges->size();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ ScRange const & rRange = (*pMarkedRanges)[i];
+ if (rRange.aStart.Tab() != rRange.aEnd.Tab())
+ {
+ if ((maActiveCell.Tab() >= rRange.aStart.Tab()) ||
+ maActiveCell.Tab() <= rRange.aEnd.Tab())
+ {
+ aRanges.push_back(rRange);
+ nMinRow = std::min(rRange.aStart.Row(),nMinRow);
+ nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow);
+ }
+ else
+ SAL_WARN("sc", "Range of wrong table");
+ }
+ else if(rRange.aStart.Tab() == maActiveCell.Tab())
+ {
+ aRanges.push_back(rRange);
+ nMinRow = std::min(rRange.aStart.Row(),nMinRow);
+ nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow);
+ }
+ else
+ SAL_WARN("sc", "Range of wrong table");
+ }
+ int nCurrentIndex = 0 ;
+ for(sal_Int32 row = nMinRow ; row <= nMaxRow ; ++row)
+ {
+ std::vector<std::pair<SCCOL, SCCOL>> aVecCol;
+ for (ScRange const & r : aRanges)
+ {
+ if ( row >= r.aStart.Row() && row <= r.aEnd.Row())
+ {
+ aVecCol.emplace_back(r.aStart.Col(), r.aEnd.Col());
+ }
+ }
+ std::sort(aVecCol.begin(), aVecCol.end(), CompMinCol);
+ for (const std::pair<SCCOL, SCCOL> &pairCol : aVecCol)
+ {
+ SCCOL nCol = pairCol.second - pairCol.first + 1;
+ if (nCol + nCurrentIndex > nSelectedChildIndex)
+ {
+ return ScMyAddress(static_cast<SCCOL>(pairCol.first + nSelectedChildIndex - nCurrentIndex), row, maActiveCell.Tab());
+ }
+ nCurrentIndex += nCol;
+ }
+ }
+ }
+ return ScMyAddress(0,0,maActiveCell.Tab());
+}
+
+bool ScAccessibleSpreadsheet::CalcScRangeDifferenceMax(const ScRange & rSrc, const ScRange & rDest, int nMax,
+ std::vector<ScMyAddress> &vecRet, int &nSize)
+{
+ //Src Must be :Src > Dest
+ if (rDest.Contains(rSrc))
+ {//Here is Src In Dest,Src <= Dest
+ return false;
+ }
+ if (!rDest.Intersects(rSrc))
+ {
+ int nCellCount = sal_uInt32(rDest.aEnd.Col() - rDest.aStart.Col() + 1)
+ * sal_uInt32(rDest.aEnd.Row() - rDest.aStart.Row() + 1)
+ * sal_uInt32(rDest.aEnd.Tab() - rDest.aStart.Tab() + 1);
+ if (nCellCount + nSize > nMax)
+ {
+ return true;
+ }
+ else if(nCellCount > 0)
+ {
+ for (sal_Int32 row = rDest.aStart.Row(); row <= rDest.aEnd.Row();++row)
+ {
+ for (sal_uInt16 col = rDest.aStart.Col(); col <= rDest.aEnd.Col();++col)
+ {
+ vecRet.emplace_back(col,row,rDest.aStart.Tab());
+ }
+ }
+ }
+ return false;
+ }
+ sal_Int32 nMinRow = rSrc.aStart.Row();
+ sal_Int32 nMaxRow = rSrc.aEnd.Row();
+ for (; nMinRow <= nMaxRow ; ++nMinRow,--nMaxRow)
+ {
+ for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col)
+ {
+ if (nSize > nMax)
+ {
+ return true;
+ }
+ ScMyAddress cell(col,nMinRow,rSrc.aStart.Tab());
+ if(!rDest.Contains(cell))
+ {//In Src ,Not In Dest
+ vecRet.push_back(cell);
+ ++nSize;
+ }
+ }
+ if (nMinRow != nMaxRow)
+ {
+ for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col)
+ {
+ if (nSize > nMax)
+ {
+ return true;
+ }
+ ScMyAddress cell(col,nMaxRow,rSrc.aStart.Tab());
+ if(!rDest.Contains(cell))
+ {//In Src ,Not In Dest
+ vecRet.push_back(cell);
+ ++nSize;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+//In Src , Not in Dest
+bool ScAccessibleSpreadsheet::CalcScRangeListDifferenceMax(ScRangeList *pSrc, ScRangeList *pDest,
+ int nMax, std::vector<ScMyAddress> &vecRet)
+{
+ if (pSrc == nullptr || pDest == nullptr)
+ {
+ return false;
+ }
+ int nSize =0;
+ if (pDest->GetCellCount() == 0)//if the Dest Rang List is empty
+ {
+ if (pSrc->GetCellCount() > o3tl::make_unsigned(nMax))//if the Src Cell count is greater than nMax
+ {
+ return true;
+ }
+ //now the cell count is less than nMax
+ vecRet.reserve(10);
+ size_t nSrcSize = pSrc->size();
+ for (size_t i = 0; i < nSrcSize; ++i)
+ {
+ ScRange const & rRange = (*pSrc)[i];
+ for (sal_Int32 row = rRange.aStart.Row(); row <= rRange.aEnd.Row();++row)
+ {
+ for (sal_uInt16 col = rRange.aStart.Col(); col <= rRange.aEnd.Col();++col)
+ {
+ vecRet.emplace_back(col,row, rRange.aStart.Tab());
+ }
+ }
+ }
+ return false;
+ }
+ //the Dest Rang List is not empty
+ vecRet.reserve(10);
+ size_t nSizeSrc = pSrc->size();
+ for (size_t i = 0; i < nSizeSrc; ++i)
+ {
+ ScRange const & rRange = (*pSrc)[i];
+ size_t nSizeDest = pDest->size();
+ for (size_t j = 0; j < nSizeDest; ++j)
+ {
+ ScRange const & rRangeDest = (*pDest)[j];
+ if (CalcScRangeDifferenceMax(rRange,rRangeDest,nMax,vecRet,nSize))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//===== internal ============================================================
+
+// FIXME: really unclear why we have an ScAccessibleTableBase with
+// only this single sub-class
+ScAccessibleSpreadsheet::ScAccessibleSpreadsheet(
+ ScAccessibleDocument* pAccDoc,
+ ScTabViewShell* pViewShell,
+ SCTAB nTab,
+ ScSplitPos eSplitPos)
+ :
+ ScAccessibleTableBase( pAccDoc, GetDocument(pViewShell), ScRange( 0, 0, nTab, GetDocument(pViewShell)->MaxCol(), GetDocument(pViewShell)->MaxRow(), nTab)),
+ mbIsSpreadsheet( true ),
+ m_bFormulaMode( false ),
+ m_bFormulaLastMode( false ),
+ m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0)
+{
+ ConstructScAccessibleSpreadsheet( pAccDoc, pViewShell, nTab, eSplitPos );
+}
+
+ScAccessibleSpreadsheet::ScAccessibleSpreadsheet(
+ ScAccessibleSpreadsheet& rParent, const ScRange& rRange ) :
+ ScAccessibleTableBase( rParent.mpAccDoc, rParent.mpDoc, rRange),
+ mbIsSpreadsheet( false ),
+ m_bFormulaMode( false ),
+ m_bFormulaLastMode( false ),
+ m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0)
+{
+ ConstructScAccessibleSpreadsheet( rParent.mpAccDoc, rParent.mpViewShell, rParent.mnTab, rParent.meSplitPos );
+}
+
+ScAccessibleSpreadsheet::~ScAccessibleSpreadsheet()
+{
+ mpMarkedRanges.reset();
+ if (mpViewShell)
+ mpViewShell->RemoveAccessibilityObject(*this);
+}
+
+void ScAccessibleSpreadsheet::ConstructScAccessibleSpreadsheet(
+ ScAccessibleDocument* pAccDoc,
+ ScTabViewShell* pViewShell,
+ SCTAB nTab,
+ ScSplitPos eSplitPos)
+{
+ mpViewShell = pViewShell;
+ mpMarkedRanges = nullptr;
+ mpAccDoc = pAccDoc;
+ mpAccCell.clear();
+ meSplitPos = eSplitPos;
+ mnTab = nTab;
+ mbDelIns = false;
+ mbIsFocusSend = false;
+ if (!mpViewShell)
+ return;
+
+ mpViewShell->AddAccessibilityObject(*this);
+
+ const ScViewData& rViewData = mpViewShell->GetViewData();
+ maActiveCell = rViewData.GetCurPos();
+ mpAccCell = GetAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col());
+ ScDocument* pScDoc= GetDocument(mpViewShell);
+ if (pScDoc)
+ {
+ pScDoc->GetName( maActiveCell.Tab(), m_strOldTabName );
+ }
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpAccCell.clear();
+
+ ScAccessibleTableBase::disposing();
+}
+
+void ScAccessibleSpreadsheet::CompleteSelectionChanged(bool bNewState)
+{
+ if (IsFormulaMode())
+ {
+ return ;
+ }
+ mpMarkedRanges.reset();
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ if (bNewState)
+ aEvent.NewValue <<= AccessibleStateType::SELECTED;
+ else
+ aEvent.OldValue <<= AccessibleStateType::SELECTED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::LostFocus()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell);
+
+ CommitChange(aEvent);
+
+ CommitFocusLost();
+}
+
+void ScAccessibleSpreadsheet::GotFocus()
+{
+ CommitFocusGained();
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ uno::Reference< XAccessible > xNew;
+ if (IsFormulaMode())
+ {
+ if (!m_pAccFormulaCell.is() || !m_bFormulaLastMode)
+ {
+ ScAddress aFormulaAddr;
+ if(!GetFormulaCurrentFocusCell(aFormulaAddr))
+ {
+ return;
+ }
+ m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(),aFormulaAddr.Col());
+ }
+ xNew = m_pAccFormulaCell.get();
+ }
+ else
+ {
+ if(mpAccCell->GetCellAddress() == maActiveCell)
+ {
+ xNew = mpAccCell.get();
+ }
+ else
+ {
+ CommitFocusCell(maActiveCell);
+ return ;
+ }
+ }
+ aEvent.NewValue <<= xNew;
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::BoundingBoxChanged()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::VisAreaChanged()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+}
+
+ //===== SfxListener =====================================================
+
+void ScAccessibleSpreadsheet::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ if (pRefHint->GetMode() == URM_INSDEL && pRefHint->GetDz() == 0) //test whether table is inserted or deleted
+ {
+ if (((pRefHint->GetRange().aStart.Col() == maRange.aStart.Col()) &&
+ (pRefHint->GetRange().aEnd.Col() == maRange.aEnd.Col())) ||
+ ((pRefHint->GetRange().aStart.Row() == maRange.aStart.Row()) &&
+ (pRefHint->GetRange().aEnd.Row() == maRange.aEnd.Row())))
+ {
+ // ignore next SfxHintId::ScDataChanged notification
+ mbDelIns = true;
+
+ SCROW nFirstRow = -1;
+ SCROW nLastRow = -1;
+ SCCOL nFirstCol = -1;
+ SCCOL nLastCol = -1;
+
+ sal_Int16 nId(0);
+ SCCOL nX(pRefHint->GetDx());
+ SCROW nY(pRefHint->GetDy());
+ ScRange aRange(pRefHint->GetRange());
+ if ((nX < 0) || (nY < 0))
+ {
+ assert(!((nX < 0) && (nY < 0)) && "should not be possible to remove row and column at the same time");
+
+ // Range in the update hint is the range after the removed rows/columns;
+ // calculate indices for the removed ones from that
+ if (nX < 0)
+ {
+ nId = AccessibleTableModelChangeType::COLUMNS_REMOVED;
+ nFirstCol = aRange.aStart.Col() + nX;
+ nLastCol = aRange.aStart.Col() - 1;
+ }
+ else
+ {
+ nId = AccessibleTableModelChangeType::ROWS_REMOVED;
+ nFirstRow = aRange.aStart.Row() + nY;
+ nLastRow = aRange.aStart.Row() - 1;
+ }
+ }
+ else if ((nX > 0) || (nY > 0))
+ {
+ assert(!((nX > 0) && (nY > 0)) && "should not be possible to add row and column at the same time");
+
+ // Range in the update hint is from first inserted row/column to last one in spreadsheet;
+ // calculate indices for the inserted ones from that
+ if (nX > 0)
+ {
+ nId = AccessibleTableModelChangeType::COLUMNS_INSERTED;
+ nFirstCol = aRange.aStart.Col();
+ nLastCol = aRange.aStart.Col() + nX - 1;
+ }
+ else
+ {
+ nId = AccessibleTableModelChangeType::ROWS_INSERTED;
+ nFirstRow = aRange.aStart.Row();
+ nLastRow = aRange.aStart.Row() + nY -1;
+ }
+ }
+ else
+ {
+ assert(false && "is it a deletion or an insertion?");
+ }
+
+ CommitTableModelChange(nFirstRow, nFirstCol, nLastRow, nLastCol, nId);
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell);
+
+ CommitChange(aEvent);
+ }
+ }
+ }
+ else
+ {
+ if (rHint.GetId() == SfxHintId::ScAccCursorChanged)
+ {
+ if (mpViewShell)
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+
+ m_bFormulaMode = rViewData.IsRefMode() || SC_MOD()->IsFormulaMode();
+ if ( m_bFormulaMode )
+ {
+ NotifyRefMode();
+ m_bFormulaLastMode = true;
+ return;
+ }
+ if (m_bFormulaLastMode)
+ {//Last Notify Mode Is Formula Mode.
+ m_vecFormulaLastMyAddr.clear();
+ RemoveFormulaSelection(true);
+ m_pAccFormulaCell.clear();
+ //Remove All Selection
+ }
+ m_bFormulaLastMode = m_bFormulaMode;
+
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ ScAddress aNewCell = rViewData.GetCurPos();
+ if(aNewCell.Tab() != maActiveCell.Tab())
+ {
+ aEvent.EventId = AccessibleEventId::PAGE_CHANGED;
+ auto pAccParent = getAccessibleParent();
+ ScAccessibleDocument *pAccDoc =
+ static_cast<ScAccessibleDocument*>(pAccParent.get());
+ if(pAccDoc)
+ {
+ pAccDoc->CommitChange(aEvent);
+ }
+ }
+ bool bNewPosCell = (aNewCell != maActiveCell) || mpViewShell->GetForceFocusOnCurCell(); // #i123629#
+ bool bNewPosCellFocus=false;
+ if ( bNewPosCell && IsFocused() && aNewCell.Tab() == maActiveCell.Tab() )
+ {//single Focus
+ bNewPosCellFocus=true;
+ }
+ ScMarkData &refScMarkData = rViewData.GetMarkData();
+ // MT IA2: Not used
+ // int nSelCount = refScMarkData.GetSelectCount();
+ bool bIsMark =refScMarkData.IsMarked();
+ bool bIsMultMark = refScMarkData.IsMultiMarked();
+ bool bNewMarked = refScMarkData.GetTableSelect(aNewCell.Tab()) && ( bIsMark || bIsMultMark );
+// sal_Bool bNewCellSelected = isAccessibleSelected(aNewCell.Row(), aNewCell.Col());
+ sal_uInt16 nTab = rViewData.GetTabNo();
+ const ScRange& aMarkRange = refScMarkData.GetMarkArea();
+ aEvent.OldValue.clear();
+ ScDocument* pDoc= GetDocument(mpViewShell);
+ //Mark All
+ if ( !bNewPosCellFocus &&
+ (bNewMarked || bIsMark || bIsMultMark ) &&
+ aMarkRange == ScRange( 0,0,nTab, pDoc->MaxCol(),pDoc->MaxRow(),nTab ) )
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ return ;
+ }
+ if (!mpMarkedRanges)
+ {
+ mpMarkedRanges.reset(new ScRangeList());
+ }
+ refScMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), true);
+
+ //For Whole Col Row
+ bool bWholeRow = std::abs(aMarkRange.aStart.Row() - aMarkRange.aEnd.Row()) == pDoc->MaxRow() ;
+ bool bWholeCol = ::abs(aMarkRange.aStart.Col() - aMarkRange.aEnd.Col()) == pDoc->MaxCol() ;
+ if ((bNewMarked || bIsMark || bIsMultMark ) && (bWholeCol || bWholeRow))
+ {
+ if ( aMarkRange != m_aLastWithInMarkRange )
+ {
+ RemoveSelection(refScMarkData);
+ if(bNewPosCell)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ bool bLastIsWholeColRow =
+ (std::abs(m_aLastWithInMarkRange.aStart.Row() - m_aLastWithInMarkRange.aEnd.Row()) == pDoc->MaxRow() && bWholeRow) ||
+ (::abs(m_aLastWithInMarkRange.aStart.Col() - m_aLastWithInMarkRange.aEnd.Col()) == pDoc->MaxCol() && bWholeCol);
+ bool bSelSmaller=
+ bLastIsWholeColRow &&
+ !aMarkRange.Contains(m_aLastWithInMarkRange) &&
+ aMarkRange.Intersects(m_aLastWithInMarkRange);
+ if( !bSelSmaller )
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ }
+ m_aLastWithInMarkRange = aMarkRange;
+ }
+ return ;
+ }
+ m_aLastWithInMarkRange = aMarkRange;
+ int nNewMarkCount = mpMarkedRanges->GetCellCount();
+ bool bSendSingle= (0 == nNewMarkCount) && bNewPosCell;
+ if (bSendSingle)
+ {
+ RemoveSelection(refScMarkData);
+ if(bNewPosCellFocus)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ uno::Reference< XAccessible > xChild ;
+ if (bNewPosCellFocus)
+ {
+ xChild = mpAccCell.get();
+ }
+ else
+ {
+ mpAccCell = GetAccessibleCellAt(aNewCell.Row(),aNewCell.Col());
+ xChild = mpAccCell.get();
+
+ maActiveCell = aNewCell;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
+ aEvent.NewValue <<= xChild;
+ aEvent.OldValue <<= uno::Reference< XAccessible >();
+ CommitChange(aEvent);
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ OSL_ASSERT(m_mapSelectionSend.count(aNewCell) == 0 );
+ m_mapSelectionSend.emplace(aNewCell,xChild);
+
+ }
+ else
+ {
+ ScRange aDelRange;
+ bool bIsDel = rViewData.GetDelMark( aDelRange );
+ if ( (!bIsDel || aMarkRange != aDelRange) &&
+ bNewMarked &&
+ nNewMarkCount > 0 &&
+ m_LastMarkedRanges != *mpMarkedRanges )
+ {
+ RemoveSelection(refScMarkData);
+ if(bNewPosCellFocus)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ std::vector<ScMyAddress> vecNew;
+ if(CalcScRangeListDifferenceMax(mpMarkedRanges.get(), &m_LastMarkedRanges,10,vecNew))
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ }
+ else
+ {
+ for(const auto& rAddr : vecNew)
+ {
+ uno::Reference< XAccessible > xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col());
+ if (!(bNewPosCellFocus && rAddr == aNewCell) )
+ {
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ m_mapSelectionSend.emplace(rAddr,xChild);
+ }
+ }
+ }
+ }
+ if (bNewPosCellFocus && maActiveCell != aNewCell)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ m_LastMarkedRanges = *mpMarkedRanges;
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScDataChanged)
+ {
+ if (!mbDelIns)
+ CommitTableModelChange(maRange.aStart.Row(), maRange.aStart.Col(), maRange.aEnd.Row(), maRange.aEnd.Col(), AccessibleTableModelChangeType::UPDATE);
+ else
+ mbDelIns = false;
+ if (mpViewShell)
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ ScAddress aNewCell = rViewData.GetCurPos();
+ if( maActiveCell == aNewCell)
+ {
+ ScDocument* pScDoc= GetDocument(mpViewShell);
+ if (pScDoc)
+ {
+ OUString valStr(pScDoc->GetString(aNewCell.Col(),aNewCell.Row(),aNewCell.Tab()));
+ if(m_strCurCellValue != valStr)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VALUE_CHANGED;
+ mpAccCell->CommitChange(aEvent);
+ m_strCurCellValue=valStr;
+ }
+ OUString tabName;
+ pScDoc->GetName( maActiveCell.Tab(), tabName );
+ if( m_strOldTabName != tabName )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ OUString sOldName(ScResId(STR_ACC_TABLE_NAME));
+ sOldName = sOldName.replaceFirst("%1", m_strOldTabName);
+ aEvent.OldValue <<= sOldName;
+ OUString sNewName(ScResId(STR_ACC_TABLE_NAME));
+ sNewName = sNewName.replaceFirst("%1", tabName);
+ aEvent.NewValue <<= sNewName;
+ CommitChange( aEvent );
+ m_strOldTabName = tabName;
+ }
+ }
+ }
+ }
+ }
+ // commented out, because to use a ModelChangeEvent is not the right way
+ // at the moment there is no way, but the Java/Gnome Api should be extended sometime
+/* if (mpViewShell)
+ {
+ Rectangle aNewVisCells(GetVisCells(GetVisArea(mpViewShell, meSplitPos)));
+
+ Rectangle aNewPos(aNewVisCells);
+
+ if (aNewVisCells.Overlaps(maVisCells))
+ aNewPos.Union(maVisCells);
+ else
+ CommitTableModelChange(maVisCells.Top(), maVisCells.Left(), maVisCells.Bottom(), maVisCells.Right(), AccessibleTableModelChangeType::UPDATE);
+
+ maVisCells = aNewVisCells;
+
+ CommitTableModelChange(aNewPos.Top(), aNewPos.Left(), aNewPos.Bottom(), aNewPos.Right(), AccessibleTableModelChangeType::UPDATE);
+ }
+ }*/
+ }
+
+ ScAccessibleTableBase::Notify(rBC, rHint);
+}
+
+void ScAccessibleSpreadsheet::RemoveSelection(const ScMarkData &refScMarkData)
+{
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ MAP_ADDR_XACC::iterator miRemove = m_mapSelectionSend.begin();
+ while (miRemove != m_mapSelectionSend.end())
+ {
+ if (refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row(),true) ||
+ refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row()) )
+ {
+ ++miRemove;
+ continue;
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ aEvent.NewValue <<= miRemove->second;
+ CommitChange(aEvent);
+ miRemove = m_mapSelectionSend.erase(miRemove);
+ }
+}
+void ScAccessibleSpreadsheet::CommitFocusCell(const ScAddress &aNewCell)
+{
+ OSL_ASSERT(!IsFormulaMode());
+ if(IsFormulaMode())
+ {
+ return ;
+ }
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell);
+ mpAccCell.clear();
+ mpAccCell = GetAccessibleCellAt(aNewCell.Row(), aNewCell.Col());
+ aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell);
+ maActiveCell = aNewCell;
+ ScDocument* pScDoc= GetDocument(mpViewShell);
+ if (pScDoc)
+ {
+ m_strCurCellValue = pScDoc->GetString(maActiveCell.Col(),maActiveCell.Row(),maActiveCell.Tab());
+ }
+ CommitChange(aEvent);
+}
+
+//===== XAccessibleTable ================================================
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleRowHeaders( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ if( mpDoc && mbIsSpreadsheet )
+ {
+ if( std::optional<ScRange> oRowRange = mpDoc->GetRepeatRowRange( mnTab ) )
+ {
+ SCROW nStart = oRowRange->aStart.Row();
+ SCROW nEnd = oRowRange->aEnd.Row();
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxRow()) )
+ xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( 0, nStart, mnTab, pDoc->MaxCol(), nEnd, mnTab ) ) );
+ }
+ }
+ return xAccessibleTable;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleColumnHeaders( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ if( mpDoc && mbIsSpreadsheet )
+ {
+ if( std::optional<ScRange> oColRange = mpDoc->GetRepeatColRange( mnTab ) )
+ {
+ SCCOL nStart = oColRange->aStart.Col();
+ SCCOL nEnd = oColRange->aEnd.Col();
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxCol()) )
+ xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( nStart, 0, mnTab, nEnd, pDoc->MaxRow(), mnTab ) ) );
+ }
+ }
+ return xAccessibleTable;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleRows( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Sequence<sal_Int32> aSequence;
+ if (IsFormulaMode())
+ {
+ return aSequence;
+ }
+ if (mpViewShell)
+ {
+ aSequence.realloc(maRange.aEnd.Row() - maRange.aStart.Row() + 1);
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ sal_Int32* pSequence = aSequence.getArray();
+ sal_Int32 nCount(0);
+ for (SCROW i = maRange.aStart.Row(); i <= maRange.aEnd.Row(); ++i)
+ {
+ if (rMarkdata.IsRowMarked(i))
+ {
+ pSequence[nCount] = i;
+ ++nCount;
+ }
+ }
+ aSequence.realloc(nCount);
+ }
+ else
+ aSequence.realloc(0);
+ return aSequence;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleColumns( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Sequence<sal_Int32> aSequence;
+ if (IsFormulaMode() || !mpViewShell)
+ return aSequence;
+
+ aSequence.realloc(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+ sal_Int32* pSequence = aSequence.getArray();
+ sal_Int32 nCount(0);
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ for (SCCOL i = maRange.aStart.Col(); i <= maRange.aEnd.Col(); ++i)
+ {
+ if (rMarkdata.IsColumnMarked(i))
+ {
+ pSequence[nCount] = i;
+ ++nCount;
+ }
+ }
+ aSequence.realloc(nCount);
+ return aSequence;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ if ((nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsRowMarked(static_cast<SCROW>(nRow));
+ }
+ return bResult;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsColumnMarked(static_cast<SCCOL>(nColumn));
+ }
+ return bResult;
+}
+
+rtl::Reference<ScAccessibleCell> ScAccessibleSpreadsheet::GetAccessibleCellAt(sal_Int32 nRow, sal_Int32 nColumn)
+{
+ if (IsFormulaMode())
+ {
+ ScAddress aCellAddress(static_cast<SCCOL>(nColumn), nRow, mpViewShell->GetViewData().GetTabNo());
+ if ((aCellAddress == m_aFormulaActiveCell) && m_pAccFormulaCell.is())
+ {
+ return m_pAccFormulaCell;
+ }
+ else
+ {
+ return ScAccessibleCell::create(this, mpViewShell, aCellAddress, GetAccessibleIndexFormula(nRow, nColumn), meSplitPos, mpAccDoc);
+ }
+ }
+ else
+ {
+ ScAddress aCellAddress(static_cast<SCCOL>(maRange.aStart.Col() + nColumn),
+ static_cast<SCROW>(maRange.aStart.Row() + nRow), maRange.aStart.Tab());
+ if ((aCellAddress == maActiveCell) && mpAccCell.is())
+ {
+ return mpAccCell;
+ }
+ else
+ {
+ return ScAccessibleCell::create(this, mpViewShell, aCellAddress, getAccessibleIndex(nRow, nColumn), meSplitPos, mpAccDoc);
+ }
+ }
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!IsFormulaMode())
+ {
+ if (nRow > (maRange.aEnd.Row() - maRange.aStart.Row()) ||
+ nRow < 0 ||
+ nColumn > (maRange.aEnd.Col() - maRange.aStart.Col()) ||
+ nColumn < 0)
+ throw lang::IndexOutOfBoundsException();
+ }
+ rtl::Reference<ScAccessibleCell> pAccessibleCell = GetAccessibleCellAt(nRow, nColumn);
+ return pAccessibleCell;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (IsFormulaMode())
+ {
+ ScAddress addr(static_cast<SCCOL>(nColumn), nRow, 0);
+ return IsScAddrFormulaSel(addr);
+ }
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
+ (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsCellMarked(static_cast<SCCOL>(nColumn), static_cast<SCROW>(nRow));
+ }
+ return bResult;
+}
+
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleAtPoint(const awt::Point& rPoint)
+{
+ uno::Reference< XAccessible > xAccessible;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mpViewShell)
+ {
+ SCCOL nX;
+ SCROW nY;
+ mpViewShell->GetViewData().GetPosFromPixel( rPoint.X, rPoint.Y, meSplitPos, nX, nY);
+ try {
+ xAccessible = getAccessibleCellAt(nY, nX);
+ }
+ catch(const css::lang::IndexOutOfBoundsException &)
+ {
+ return nullptr;
+ }
+ }
+ }
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::grabFocus( )
+{
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getForeground( )
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+ //===== XAccessibleContext ==============================================
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL ScAccessibleSpreadsheet::getAccessibleRelationSet()
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ if(mpAccDoc)
+ pRelationSet = mpAccDoc->GetRelationSet(nullptr);
+ if (pRelationSet)
+ return pRelationSet;
+ return new utl::AccessibleRelationSetHelper();
+}
+
+sal_Int64 SAL_CALL ScAccessibleSpreadsheet::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ if (IsEditable())
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if (IsFocused())
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if (IsCompleteSheetSelected())
+ nStateSet |= AccessibleStateType::SELECTED;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+ ///===== XAccessibleSelection ===========================================
+
+void SAL_CALL ScAccessibleSpreadsheet::selectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ if (mpViewShell)
+ {
+ sal_Int32 nCol(getAccessibleColumn(nChildIndex));
+ sal_Int32 nRow(getAccessibleRow(nChildIndex));
+
+ SelectCell(nRow, nCol, false);
+ }
+}
+
+void SAL_CALL
+ ScAccessibleSpreadsheet::clearAccessibleSelection( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mpViewShell && !IsFormulaMode())
+ mpViewShell->Unmark();
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::selectAllAccessibleChildren( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpViewShell)
+ return;
+
+ if (IsFormulaMode())
+ {
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ mpViewShell->InitRefMode( 0, 0, rViewData.GetTabNo(), SC_REFTYPE_REF );
+ rViewData.SetRefStart(0, 0, rViewData.GetTabNo());
+ rViewData.SetRefEnd(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo());
+ mpViewShell->UpdateRef(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo());
+ }
+ else
+ mpViewShell->SelectAll();
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleSpreadsheet::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int64 nResult(0);
+ if (mpViewShell)
+ {
+ if (IsFormulaMode())
+ {
+ nResult = static_cast<sal_Int64>(GetRowAll()) * static_cast<sal_Int64>(GetColAll());
+ }
+ else
+ {
+ if (!mpMarkedRanges)
+ {
+ mpMarkedRanges.reset(new ScRangeList());
+ ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
+ aMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), false);
+ }
+ // is possible, because there shouldn't be overlapped ranges in it
+ if (mpMarkedRanges)
+ nResult = mpMarkedRanges->GetCellCount();
+ }
+ }
+ return nResult;
+}
+
+uno::Reference<XAccessible > SAL_CALL
+ ScAccessibleSpreadsheet::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference < XAccessible > xAccessible;
+ if (IsFormulaMode())
+ {
+ if(CheckChildIndex(nSelectedChildIndex))
+ {
+ ScAddress addr = GetChildIndexAddress(nSelectedChildIndex);
+ xAccessible = getAccessibleCellAt(addr.Row(), addr.Col());
+ }
+ return xAccessible;
+ }
+ if (mpViewShell)
+ {
+ if (!mpMarkedRanges)
+ {
+ mpMarkedRanges.reset(new ScRangeList());
+ mpViewShell->GetViewData().GetMarkData().FillRangeListWithMarks(mpMarkedRanges.get(), false);
+ }
+ if (mpMarkedRanges)
+ {
+ if ((nSelectedChildIndex < 0) ||
+ (mpMarkedRanges->GetCellCount() <= o3tl::make_unsigned(nSelectedChildIndex)))
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+ ScMyAddress addr = CalcScAddressFromRangeList(mpMarkedRanges.get(),nSelectedChildIndex);
+ auto it = m_mapSelectionSend.find(addr);
+ if( it != m_mapSelectionSend.end() )
+ xAccessible = it->second;
+ else
+ xAccessible = getAccessibleCellAt(addr.Row(), addr.Col());
+ }
+ }
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::deselectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ if (!mpViewShell)
+ return;
+
+ sal_Int32 nCol(getAccessibleColumn(nChildIndex));
+ sal_Int32 nRow(getAccessibleRow(nChildIndex));
+
+ if (IsFormulaMode())
+ {
+ if(IsScAddrFormulaSel(
+ ScAddress(static_cast<SCCOL>(nCol), nRow,mpViewShell->GetViewData().GetTabNo()))
+ )
+ {
+ SelectCell(nRow, nCol, true);
+ }
+ return ;
+ }
+ if (mpViewShell->GetViewData().GetMarkData().IsCellMarked(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow)))
+ SelectCell(nRow, nCol, true);
+}
+
+void ScAccessibleSpreadsheet::SelectCell(sal_Int32 nRow, sal_Int32 nCol, bool bDeselect)
+{
+ if (IsFormulaMode())
+ {
+ if (bDeselect)
+ {//??
+ return;
+ }
+ else
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+
+ mpViewShell->InitRefMode( static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo(), SC_REFTYPE_REF );
+ mpViewShell->UpdateRef(static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo());
+ }
+ return ;
+ }
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), maRange.aStart.Tab(), bDeselect );
+
+ mpViewShell->SelectionChanged();
+}
+
+/*
+void ScAccessibleSpreadsheet::CreateSortedMarkedCells()
+{
+ mpSortedMarkedCells = new std::vector<ScMyAddress>();
+ mpSortedMarkedCells->reserve(mpMarkedRanges->GetCellCount());
+ for ( size_t i = 0, ListSize = mpMarkedRanges->size(); i < ListSize; ++i )
+ {
+ ScRange* pRange = (*mpMarkedRanges)[i];
+ if (pRange->aStart.Tab() != pRange->aEnd.Tab())
+ {
+ if ((maActiveCell.Tab() >= pRange->aStart.Tab()) ||
+ maActiveCell.Tab() <= pRange->aEnd.Tab())
+ {
+ ScRange aRange(*pRange);
+ aRange.aStart.SetTab(maActiveCell.Tab());
+ aRange.aEnd.SetTab(maActiveCell.Tab());
+ AddMarkedRange(aRange);
+ }
+ else
+ {
+ OSL_FAIL("Range of wrong table");
+ }
+ }
+ else if(pRange->aStart.Tab() == maActiveCell.Tab())
+ AddMarkedRange(*pRange);
+ else
+ {
+ OSL_FAIL("Range of wrong table");
+ }
+ }
+ std::sort(mpSortedMarkedCells->begin(), mpSortedMarkedCells->end());
+}
+
+void ScAccessibleSpreadsheet::AddMarkedRange(const ScRange& rRange)
+{
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ ScMyAddress aCell(nCol, nRow, maActiveCell.Tab());
+ mpSortedMarkedCells->push_back(aCell);
+ }
+ }
+}*/
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleSpreadsheet::getImplementationName()
+{
+ return "ScAccessibleSpreadsheet";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleSpreadsheet::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheet" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleSpreadsheet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+///===== XAccessibleEventBroadcaster =====================================
+
+void SAL_CALL ScAccessibleSpreadsheet::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ ScAccessibleTableBase::addAccessibleEventListener(xListener);
+
+}
+
+//==== internal =========================================================
+
+AbsoluteScreenPixelRectangle ScAccessibleSpreadsheet::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsAbsolute();
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessibleSpreadsheet::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ //#101986#; extends to the same window, because the parent is the document and it has the same window
+ aRect = pWindow->GetWindowExtentsRelative(*pWindow);
+ }
+ return aRect;
+}
+
+bool ScAccessibleSpreadsheet::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+bool ScAccessibleSpreadsheet::IsEditable()
+{
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+ bool bProtected(false);
+ if (mpDoc && mpDoc->IsTabProtected(maRange.aStart.Tab()))
+ bProtected = true;
+ return !bProtected;
+}
+
+bool ScAccessibleSpreadsheet::IsFocused()
+{
+ bool bFocused(false);
+ if (mpViewShell)
+ {
+ if (mpViewShell->GetViewData().GetActivePart() == meSplitPos)
+ bFocused = mpViewShell->GetActiveWin()->HasFocus();
+ }
+ return bFocused;
+}
+
+bool ScAccessibleSpreadsheet::IsCompleteSheetSelected()
+{
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ bool bResult(false);
+ if(mpViewShell)
+ {
+ //#103800#; use a copy of MarkData
+ ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
+ if (aMarkData.IsAllMarked(maRange))
+ bResult = true;
+ }
+ return bResult;
+}
+
+ScDocument* ScAccessibleSpreadsheet::GetDocument(ScTabViewShell* pViewShell)
+{
+ ScDocument* pDoc = nullptr;
+ if (pViewShell)
+ pDoc = &pViewShell->GetViewData().GetDocument();
+ return pDoc;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true );
+ mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true );
+ mpViewShell->SelectionChanged();
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true );
+ mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true );
+ mpViewShell->SelectionChanged();
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true, true );
+ mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true );
+ mpViewShell->SelectionChanged();
+ mpViewShell->DoneBlockMode( true );
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true, false, true );
+ mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true );
+ mpViewShell->SelectionChanged();
+ mpViewShell->DoneBlockMode( true );
+ return true;
+}
+
+void ScAccessibleSpreadsheet::FireFirstCellFocus()
+{
+ if (IsFormulaMode())
+ {
+ return ;
+ }
+ if (mbIsFocusSend)
+ {
+ return ;
+ }
+ mbIsFocusSend = true;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ aEvent.NewValue <<= getAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col());
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::NotifyRefMode()
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ if (!rViewData.IsRefMode())
+ // Not in reference mode. Bail out.
+ return;
+
+ sal_uInt16 nRefStartX = rViewData.GetRefStartX();
+ sal_Int32 nRefStartY = rViewData.GetRefStartY();
+ sal_uInt16 nRefEndX = rViewData.GetRefEndX();
+ sal_Int32 nRefEndY = rViewData.GetRefEndY();
+ ScAddress aFormulaAddr;
+ if(!GetFormulaCurrentFocusCell(aFormulaAddr))
+ {
+ return ;
+ }
+ if (m_aFormulaActiveCell != aFormulaAddr)
+ {//New Focus
+ m_nMinX =std::min(nRefStartX,nRefEndX);
+ m_nMaxX =std::max(nRefStartX,nRefEndX);
+ m_nMinY = std::min(nRefStartY,nRefEndY);
+ m_nMaxY = std::max(nRefStartY,nRefEndY);
+ RemoveFormulaSelection();
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.OldValue <<= uno::Reference<XAccessible>(m_pAccFormulaCell);
+ m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(), aFormulaAddr.Col());
+ uno::Reference< XAccessible > xNew = m_pAccFormulaCell;
+ aEvent.NewValue <<= xNew;
+ CommitChange(aEvent);
+ if (nRefStartX == nRefEndX && nRefStartY == nRefEndY)
+ {//Selection Single
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ aEvent.NewValue <<= xNew;
+ CommitChange(aEvent);
+ m_mapFormulaSelectionSend.emplace(aFormulaAddr,xNew);
+ m_vecFormulaLastMyAddr.clear();
+ m_vecFormulaLastMyAddr.emplace_back(aFormulaAddr);
+ }
+ else
+ {
+ std::vector<ScMyAddress> vecCurSel;
+ int nCurSize = (m_nMaxX - m_nMinX +1)*(m_nMaxY - m_nMinY +1) ;
+ vecCurSel.reserve(nCurSize);
+ for (sal_uInt16 x = m_nMinX ; x <= m_nMaxX ; ++x)
+ {
+ for (sal_Int32 y = m_nMinY ; y <= m_nMaxY ; ++y)
+ {
+ ScMyAddress aAddr(x,y,0);
+ vecCurSel.push_back(aAddr);
+ }
+ }
+ std::sort(vecCurSel.begin(), vecCurSel.end());
+ std::vector<ScMyAddress> vecNew;
+ std::set_difference(vecCurSel.begin(),vecCurSel.end(),
+ m_vecFormulaLastMyAddr.begin(),m_vecFormulaLastMyAddr.end(),
+ std::back_insert_iterator(vecNew));
+ int nNewSize = vecNew.size();
+ if ( nNewSize > 10 )
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ }
+ else
+ {
+ for(const auto& rAddr : vecNew)
+ {
+ uno::Reference< XAccessible > xChild;
+ if (rAddr == aFormulaAddr)
+ {
+ xChild = m_pAccFormulaCell.get();
+ }
+ else
+ {
+ xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col());
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ m_mapFormulaSelectionSend.emplace(rAddr,xChild);
+ }
+ }
+ m_vecFormulaLastMyAddr.swap(vecCurSel);
+ }
+ }
+ m_aFormulaActiveCell = aFormulaAddr;
+}
+
+void ScAccessibleSpreadsheet::RemoveFormulaSelection(bool bRemoveAll )
+{
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ MAP_ADDR_XACC::iterator miRemove = m_mapFormulaSelectionSend.begin();
+ while (miRemove != m_mapFormulaSelectionSend.end())
+ {
+ if( !bRemoveAll && IsScAddrFormulaSel(miRemove->first) )
+ {
+ ++miRemove;
+ continue;
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ aEvent.NewValue <<= miRemove->second;
+ CommitChange(aEvent);
+ miRemove = m_mapFormulaSelectionSend.erase(miRemove);
+ }
+}
+
+bool ScAccessibleSpreadsheet::IsScAddrFormulaSel(const ScAddress &addr) const
+{
+ return addr.Col() >= m_nMinX && addr.Col() <= m_nMaxX &&
+ addr.Row() >= m_nMinY && addr.Row() <= m_nMaxY &&
+ addr.Tab() == mpViewShell->GetViewData().GetTabNo();
+}
+
+bool ScAccessibleSpreadsheet::CheckChildIndex(sal_Int64 nIndex) const
+{
+ sal_Int64 nMaxIndex = static_cast<sal_Int64>(m_nMaxX - m_nMinX +1) * static_cast<sal_Int64>(m_nMaxY - m_nMinY +1) -1 ;
+ return nIndex <= nMaxIndex && nIndex >= 0 ;
+}
+
+ScAddress ScAccessibleSpreadsheet::GetChildIndexAddress(sal_Int64 nIndex) const
+{
+ sal_Int64 nRowAll = GetRowAll();
+ sal_Int64 nColAll = GetColAll();
+ if (nIndex < 0 || nIndex >= nRowAll * nColAll)
+ {
+ return ScAddress();
+ }
+ return ScAddress(
+ static_cast<SCCOL>((nIndex - nIndex % nRowAll) / nRowAll + + m_nMinX),
+ nIndex % nRowAll + m_nMinY,
+ mpViewShell->GetViewData().GetTabNo()
+ );
+}
+
+sal_Int64 ScAccessibleSpreadsheet::GetAccessibleIndexFormula( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_uInt16 nColRelative = sal_uInt16(nColumn) - GetColAll();
+ sal_Int32 nRowRelative = nRow - GetRowAll();
+ if (nRow < 0 || nColumn < 0 || nRowRelative >= GetRowAll() || nColRelative >= GetColAll() )
+ {
+ return -1;
+ }
+ return static_cast<sal_Int64>(GetRowAll()) * static_cast<sal_Int64>(nRowRelative) + nColRelative;
+}
+
+bool ScAccessibleSpreadsheet::IsFormulaMode()
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ m_bFormulaMode = rViewData.IsRefMode() || SC_MOD()->IsFormulaMode();
+ return m_bFormulaMode ;
+}
+
+bool ScAccessibleSpreadsheet::GetFormulaCurrentFocusCell(ScAddress &addr)
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ sal_uInt16 nRefX=0;
+ sal_Int32 nRefY=0;
+ if(m_bFormulaLastMode)
+ {
+ nRefX=rViewData.GetRefEndX();
+ nRefY=rViewData.GetRefEndY();
+ }
+ else
+ {
+ nRefX=rViewData.GetRefStartX();
+ nRefY=rViewData.GetRefStartY();
+ }
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ if( /* Always true: nRefX >= 0 && */ nRefX <= pDoc->MaxCol() && nRefY >= 0 && nRefY <= pDoc->MaxRow())
+ {
+ addr = ScAddress(nRefX,nRefY,rViewData.GetTabNo());
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleTableBase.cxx b/sc/source/ui/Accessibility/AccessibleTableBase.cxx
new file mode 100644
index 0000000000..5ce54fad56
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleTableBase.cxx
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleTableBase.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <table.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessibleTableBase::ScAccessibleTableBase(
+ const uno::Reference<XAccessible>& rxParent,
+ ScDocument* pDoc,
+ const ScRange& rRange)
+ :
+ ScAccessibleContextBase (rxParent, AccessibleRole::TABLE),
+ maRange(rRange),
+ mpDoc(pDoc)
+{
+}
+
+ScAccessibleTableBase::~ScAccessibleTableBase()
+{
+}
+
+void SAL_CALL ScAccessibleTableBase::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpDoc = nullptr;
+
+ ScAccessibleContextBase::disposing();
+}
+
+ //===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessibleTableBase::queryInterface( uno::Type const & rType )
+{
+ if ( rType == cppu::UnoType<XAccessibleTableSelection>::get())
+ {
+ return uno::Any(uno::Reference<XAccessibleTableSelection>(this));
+ }
+ else
+ {
+ uno::Any aAny (ScAccessibleTableBaseImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+ }
+}
+
+void SAL_CALL ScAccessibleTableBase::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessibleTableBase::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+ //===== XAccessibleTable ================================================
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRowCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return maRange.aEnd.Row() - maRange.aStart.Row() + 1;
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleColumnCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return maRange.aEnd.Col() - maRange.aStart.Col() + 1;
+}
+
+OUString SAL_CALL ScAccessibleTableBase::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ OSL_FAIL("Here should be an implementation to fill the description");
+
+ if ((nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ //setAccessibleRowDescription(nRow, xAccessible); // to remember the created Description
+ return OUString();
+}
+
+OUString SAL_CALL ScAccessibleTableBase::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ OSL_FAIL("Here should be an implementation to fill the description");
+
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ //setAccessibleColumnDescription(nColumn, xAccessible); // to remember the created Description
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
+ (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nCount(1); // the same cell
+ nRow += maRange.aStart.Row();
+ nColumn += maRange.aStart.Col();
+
+ if (mpDoc)
+ {
+ ScTable* pTab = mpDoc->FetchTable(maRange.aStart.Tab());
+ if (pTab)
+ {
+ SCROW nStartRow = static_cast<SCROW>(nRow);
+ SCROW nEndRow = nStartRow;
+ SCCOL nStartCol = static_cast<SCCOL>(nColumn);
+ SCCOL nEndCol = nStartCol;
+ if (pTab->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, false))
+ {
+ if (nEndRow > nStartRow)
+ nCount = nEndRow - nStartRow + 1;
+ }
+ }
+ }
+
+ return nCount;
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
+ (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nCount(1); // the same cell
+ nRow += maRange.aStart.Row();
+ nColumn += maRange.aStart.Col();
+
+ if (mpDoc)
+ {
+ ScTable* pTab = mpDoc->FetchTable(maRange.aStart.Tab());
+ if (pTab)
+ {
+ SCROW nStartRow = static_cast<SCROW>(nRow);
+ SCROW nEndRow = nStartRow;
+ SCCOL nStartCol = static_cast<SCCOL>(nColumn);
+ SCCOL nEndCol = nStartCol;
+ if (pTab->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, false))
+ {
+ if (nEndCol > nStartCol)
+ nCount = nEndCol - nStartCol + 1;
+ }
+ }
+ }
+
+ return nCount;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleTableBase::getAccessibleRowHeaders( )
+{
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ OSL_FAIL("Here should be an implementation to fill the row headers");
+
+ //CommitChange
+ return xAccessibleTable;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleTableBase::getAccessibleColumnHeaders( )
+{
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ OSL_FAIL("Here should be an implementation to fill the column headers");
+
+ //CommitChange
+ return xAccessibleTable;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleTableBase::getSelectedAccessibleRows( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Sequence< sal_Int32 > aSequence;
+ return aSequence;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleTableBase::getSelectedAccessibleColumns( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Sequence< sal_Int32 > aSequence;
+ return aSequence;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::isAccessibleRowSelected( sal_Int32 /* nRow */ )
+{
+ OSL_FAIL("not implemented yet");
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::isAccessibleColumnSelected( sal_Int32 /* nColumn */ )
+{
+ OSL_FAIL("not implemented yet");
+ return false;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleTableBase::getAccessibleCellAt( sal_Int32 /* nRow */, sal_Int32 /* nColumn */ )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Reference< XAccessible > xAccessible;
+ return xAccessible;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleTableBase::getAccessibleCaption( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Reference< XAccessible > xAccessible;
+ return xAccessible;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleTableBase::getAccessibleSummary( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Reference< XAccessible > xAccessible;
+ return xAccessible;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::isAccessibleSelected( sal_Int32 /* nRow */, sal_Int32 /* nColumn */ )
+{
+ OSL_FAIL("not implemented yet");
+ return false;
+}
+
+// ===== XAccessibleExtendedTable ========================================
+
+sal_Int64 SAL_CALL ScAccessibleTableBase::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nRow > (maRange.aEnd.Row() - maRange.aStart.Row()) ||
+ nRow < 0 ||
+ nColumn > (maRange.aEnd.Col() - maRange.aStart.Col()) ||
+ nColumn < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ nRow -= maRange.aStart.Row();
+ nColumn -= maRange.aStart.Col();
+ return (static_cast<sal_Int64>(nRow) * static_cast<sal_Int64>(maRange.aEnd.Col() + 1)) + nColumn;
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nChildIndex >= getAccessibleChildCount() || nChildIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return nChildIndex / (maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nChildIndex >= getAccessibleChildCount() || nChildIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return nChildIndex % static_cast<sal_Int32>(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+}
+
+// ===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessibleTableBase::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ // FIXME: representing rows & columns this way is a plain and simple madness.
+ // this needs a radical re-think.
+ sal_Int64 nMax = static_cast<sal_Int64>(maRange.aEnd.Row() - maRange.aStart.Row() + 1) *
+ static_cast<sal_Int64>(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+ if (nMax < 0)
+ return 0;
+ return nMax;
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessibleTableBase::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nIndex >= getAccessibleChildCount() || nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ // FIXME: representing rows & columns this way is a plain and simple madness.
+ // this needs a radical re-think.
+
+ sal_Int32 nRow(0);
+ sal_Int32 nColumn(0);
+ sal_Int32 nTemp(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+ nRow = nIndex / nTemp;
+ nColumn = nIndex % nTemp;
+ return getAccessibleCellAt(nRow, nColumn);
+}
+
+OUString
+ ScAccessibleTableBase::createAccessibleDescription()
+{
+ return STR_ACC_TABLE_DESCR;
+}
+
+OUString ScAccessibleTableBase::createAccessibleName()
+{
+ OUString sName(ScResId(STR_ACC_TABLE_NAME));
+ OUString sCoreName;
+ if (mpDoc && mpDoc->GetName( maRange.aStart.Tab(), sCoreName ))
+ sName = sName.replaceFirst("%1", sCoreName);
+ return sName;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ ScAccessibleTableBase::getAccessibleRelationSet()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return uno::Reference<XAccessibleRelationSet>();
+}
+
+sal_Int64 SAL_CALL ScAccessibleTableBase::getAccessibleStateSet()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return 0;
+}
+
+ ///===== XAccessibleSelection ===========================================
+
+void SAL_CALL ScAccessibleTableBase::selectAccessibleChild( sal_Int64 /* nChildIndex */ )
+{
+}
+
+sal_Bool SAL_CALL
+ ScAccessibleTableBase::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ // I don't need to guard, because the called functions have a guard
+ if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+ return isAccessibleSelected(getAccessibleRow(nChildIndex), getAccessibleColumn(nChildIndex));
+}
+
+void SAL_CALL
+ ScAccessibleTableBase::clearAccessibleSelection( )
+{
+}
+
+void SAL_CALL ScAccessibleTableBase::selectAllAccessibleChildren()
+{
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleTableBase::getSelectedAccessibleChildCount( )
+{
+ return 0;
+}
+
+uno::Reference<XAccessible > SAL_CALL
+ ScAccessibleTableBase::getSelectedAccessibleChild( sal_Int64 /* nSelectedChildIndex */ )
+{
+ uno::Reference < XAccessible > xAccessible;
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleTableBase::deselectAccessibleChild( sal_Int64 /* nSelectedChildIndex */ )
+{
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleTableBase::getImplementationName()
+{
+ return "ScAccessibleTableBase";
+}
+
+ //===== XTypeProvider ===================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessibleTableBase::getTypes()
+{
+ return comphelper::concatSequences(ScAccessibleTableBaseImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleTableBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void ScAccessibleTableBase::CommitTableModelChange(sal_Int32 nStartRow, sal_Int32 nStartCol, sal_Int32 nEndRow, sal_Int32 nEndCol, sal_uInt16 nId)
+{
+ AccessibleTableModelChange aModelChange;
+ aModelChange.FirstRow = nStartRow;
+ aModelChange.FirstColumn = nStartCol;
+ aModelChange.LastRow = nEndRow;
+ aModelChange.LastColumn = nEndCol;
+ aModelChange.Type = nId;
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TABLE_MODEL_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= aModelChange;
+
+ CommitChange(aEvent);
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::selectRow( sal_Int32 )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::selectColumn( sal_Int32 )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::unselectRow( sal_Int32 )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::unselectColumn( sal_Int32 )
+{
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleText.cxx b/sc/source/ui/Accessibility/AccessibleText.cxx
new file mode 100644
index 0000000000..3f7ff57f8f
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleText.cxx
@@ -0,0 +1,1386 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <memory>
+#include <AccessibleText.hxx>
+#include <AccessibleCell.hxx>
+#include <attrib.hxx>
+#include <tabvwsh.hxx>
+#include <editutil.hxx>
+#include <document.hxx>
+#include <scmod.hxx>
+#include <prevwsh.hxx>
+#include <docsh.hxx>
+#include <prevloc.hxx>
+#include <patattr.hxx>
+#include <inputwin.hxx>
+#include <editeng/unofored.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/unoedhlp.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/algitem.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+class ScViewForwarder : public SvxViewForwarder
+{
+ ScTabViewShell* mpViewShell;
+ ScSplitPos meSplitPos;
+public:
+ ScViewForwarder(ScTabViewShell* pViewShell, ScSplitPos eSplitPos);
+
+ 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;
+
+ void SetInvalid();
+};
+
+ScViewForwarder::ScViewForwarder(ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
+ :
+ mpViewShell(pViewShell),
+ meSplitPos(eSplitPos)
+{
+}
+
+bool ScViewForwarder::IsValid() const
+{
+ return mpViewShell != nullptr;
+}
+
+Point ScViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ return pWindow->LogicToPixel( rPoint, rMapMode );
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ return pWindow->PixelToLogic( rPoint, rMapMode );
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+void ScViewForwarder::SetInvalid()
+{
+ mpViewShell = nullptr;
+}
+
+class ScEditObjectViewForwarder : public SvxViewForwarder
+{
+ VclPtr<OutputDevice> mpWindow;
+ // #i49561# EditView needed for access to its visible area.
+ const EditView* mpEditView;
+public:
+ ScEditObjectViewForwarder( OutputDevice* pWindow,
+ const EditView* _pEditView);
+
+ 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;
+
+ void SetInvalid();
+};
+
+ScEditObjectViewForwarder::ScEditObjectViewForwarder( OutputDevice* pWindow,
+ const EditView* _pEditView )
+ : mpWindow(pWindow)
+ , mpEditView( _pEditView )
+{
+}
+
+bool ScEditObjectViewForwarder::IsValid() const
+{
+ return (mpWindow != nullptr);
+}
+
+Point ScEditObjectViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ {
+ // #i49561# - consider offset of the visible area
+ // of the EditView before converting point to pixel.
+ Point aPoint( rPoint );
+ if ( mpEditView )
+ {
+ tools::Rectangle aEditViewVisArea( mpEditView->GetVisArea() );
+ aPoint += aEditViewVisArea.TopLeft();
+ }
+ return mpWindow->LogicToPixel( aPoint, rMapMode );
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScEditObjectViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ {
+ // #i49561# - consider offset of the visible area
+ // of the EditView after converting point to logic.
+ Point aPoint( mpWindow->PixelToLogic( rPoint, rMapMode ) );
+ if ( mpEditView )
+ {
+ tools::Rectangle aEditViewVisArea( mpEditView->GetVisArea() );
+ aPoint -= aEditViewVisArea.TopLeft();
+ }
+ return aPoint;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+void ScEditObjectViewForwarder::SetInvalid()
+{
+ mpWindow = nullptr;
+}
+
+class ScPreviewViewForwarder : public SvxViewForwarder
+{
+protected:
+ ScPreviewShell* mpViewShell;
+public:
+ explicit ScPreviewViewForwarder(ScPreviewShell* pViewShell);
+
+ 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;
+
+ void SetInvalid();
+};
+
+ScPreviewViewForwarder::ScPreviewViewForwarder(ScPreviewShell* pViewShell)
+ : mpViewShell(pViewShell)
+{
+}
+
+bool ScPreviewViewForwarder::IsValid() const
+{
+ return mpViewShell != nullptr;
+}
+
+Point ScPreviewViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ MapMode aMapMode(pWindow->GetMapMode().GetMapUnit());
+ Point aPoint2( OutputDevice::LogicToLogic( rPoint, rMapMode, aMapMode) );
+ return pWindow->LogicToPixel(aPoint2);
+ }
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScPreviewViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ MapMode aMapMode(pWindow->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint1( pWindow->PixelToLogic( rPoint ) );
+ Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
+ MapMode(aMapMode.GetMapUnit()),
+ rMapMode ) );
+ return aPoint2;
+ }
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+void ScPreviewViewForwarder::SetInvalid()
+{
+ mpViewShell = nullptr;
+}
+
+namespace {
+
+class ScPreviewHeaderFooterViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewHeaderFooterViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewHeaderFooterViewForwarder::ScPreviewHeaderFooterViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+namespace {
+
+class ScPreviewCellViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewCellViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewCellViewForwarder::ScPreviewCellViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+namespace {
+
+class ScPreviewHeaderCellViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewHeaderCellViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewHeaderCellViewForwarder::ScPreviewHeaderCellViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+namespace {
+
+class ScPreviewNoteViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewNoteViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewNoteViewForwarder::ScPreviewNoteViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+class ScEditViewForwarder : public SvxEditViewForwarder
+{
+ EditView* mpEditView;
+ VclPtr<OutputDevice> mpWindow;
+public:
+ ScEditViewForwarder(EditView* pEditView, OutputDevice* pWin);
+
+ 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;
+
+ void SetInvalid();
+};
+
+ScEditViewForwarder::ScEditViewForwarder(EditView* pEditView, OutputDevice* pWin)
+ : mpEditView(pEditView)
+ , mpWindow(pWin)
+{
+}
+
+bool ScEditViewForwarder::IsValid() const
+{
+ return mpWindow && mpEditView;
+}
+
+Point ScEditViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ return mpWindow->LogicToPixel( rPoint, rMapMode );
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScEditViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ return mpWindow->PixelToLogic( rPoint, rMapMode );
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+bool ScEditViewForwarder::GetSelection( ESelection& rSelection ) const
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ rSelection = mpEditView->GetSelection();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::SetSelection( const ESelection& rSelection )
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->SetSelection(rSelection);
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::Copy()
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->Copy();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::Cut()
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->Cut();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::Paste()
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->Paste();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+void ScEditViewForwarder::SetInvalid()
+{
+ mpWindow = nullptr;
+ mpEditView = nullptr;
+}
+
+// ScAccessibleCellTextData: shared data between sub objects of an accessible cell text object
+
+ScAccessibleCellTextData::ScAccessibleCellTextData(ScTabViewShell* pViewShell,
+ const ScAddress& rP, ScSplitPos eSplitPos, ScAccessibleCell* pAccCell)
+ : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
+ mpViewShell(pViewShell),
+ meSplitPos(eSplitPos),
+ mpAccessibleCell( pAccCell )
+{
+}
+
+ScAccessibleCellTextData::~ScAccessibleCellTextData()
+{
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+}
+
+void ScAccessibleCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr; // invalid now
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleCellBaseTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessibleCellTextData::Clone() const
+{
+ return new ScAccessibleCellTextData( mpViewShell, aCellPos, meSplitPos, mpAccessibleCell );
+}
+
+SvxTextForwarder* ScAccessibleCellTextData::GetTextForwarder()
+{
+ ScCellTextData::GetTextForwarder(); // creates Forwarder and EditEngine
+
+ if ( pDocShell && pEditEngine && mpViewShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ tools::Long nSizeX, nSizeY;
+ mpViewShell->GetViewData().GetMergeSizePixel(
+ aCellPos.Col(), aCellPos.Row(), nSizeX, nSizeY);
+
+ Size aSize(nSizeX, nSizeY);
+
+ // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
+ tools::Long nIndent = 0;
+ const SvxHorJustifyItem* pHorJustifyItem = rDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
+ SvxCellHorJustify eHorJust = pHorJustifyItem ? pHorJustifyItem->GetValue() : SvxCellHorJustify::Standard;
+ if ( eHorJust == SvxCellHorJustify::Left )
+ {
+ const ScIndentItem* pIndentItem = rDoc.GetAttr( aCellPos, ATTR_INDENT );
+ if ( pIndentItem )
+ {
+ nIndent = static_cast< tools::Long >( pIndentItem->GetValue() );
+ }
+ }
+
+ const SvxMarginItem* pMarginItem = rDoc.GetAttr( aCellPos, ATTR_MARGIN );
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+ tools::Long nLeftM = ( pMarginItem ? static_cast< tools::Long >( ( pMarginItem->GetLeftMargin() + nIndent ) * nPPTX ) : 0 );
+ tools::Long nTopM = ( pMarginItem ? static_cast< tools::Long >( pMarginItem->GetTopMargin() * nPPTY ) : 0 );
+ tools::Long nRightM = ( pMarginItem ? static_cast< tools::Long >( pMarginItem->GetRightMargin() * nPPTX ) : 0 );
+ tools::Long nBottomM = ( pMarginItem ? static_cast< tools::Long >( pMarginItem->GetBottomMargin() * nPPTY ) : 0 );
+ tools::Long nWidth = aSize.getWidth() - nLeftM - nRightM;
+ aSize.setWidth( nWidth );
+ aSize.setHeight( aSize.getHeight() - nTopM - nBottomM );
+
+ vcl::Window* pWin = mpViewShell->GetWindowByPos( meSplitPos );
+ if ( pWin )
+ {
+ aSize = pWin->PixelToLogic( aSize, pEditEngine->GetRefMapMode() );
+ }
+
+ /* #i19430# Gnopernicus reads text partly if it sticks out of the cell
+ boundaries. This leads to wrong results in cases where the cell text
+ is rotated, because rotation is not taken into account when calcu-
+ lating the visible part of the text. In these cases we will expand
+ the cell size passed as paper size to the edit engine. The function
+ accessibility::AccessibleStaticTextBase::GetParagraphBoundingBox()
+ (see svx/source/accessibility/AccessibleStaticTextBase.cxx) will
+ return the size of the complete text then, which is used to expand
+ the cell bounding box in ScAccessibleCell::GetBoundingBox()
+ (see sc/source/ui/Accessibility/AccessibleCell.cxx). */
+ const ScRotateValueItem* pItem = rDoc.GetAttr( aCellPos, ATTR_ROTATE_VALUE );
+ if( pItem && (pItem->GetValue() != 0_deg100) )
+ {
+ pEditEngine->SetPaperSize( Size( LONG_MAX, aSize.getHeight() ) );
+ tools::Long nTxtWidth = static_cast< tools::Long >( pEditEngine->CalcTextWidth() );
+ aSize.setWidth( std::max( aSize.getWidth(), nTxtWidth + 2 ) );
+ }
+ else
+ {
+ // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
+ const ScLineBreakCell* pLineBreakItem = rDoc.GetAttr( aCellPos, ATTR_LINEBREAK );
+ bool bLineBreak = ( pLineBreakItem && pLineBreakItem->GetValue() );
+ if ( !bLineBreak )
+ {
+ tools::Long nTxtWidth = static_cast< tools::Long >( pEditEngine->CalcTextWidth() );
+ aSize.setWidth( ::std::max( aSize.getWidth(), nTxtWidth ) );
+ }
+ }
+
+ pEditEngine->SetPaperSize( aSize );
+
+ // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
+ if ( eHorJust == SvxCellHorJustify::Standard && rDoc.HasValueData( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab() ) )
+ {
+ pEditEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+ }
+
+ Size aTextSize;
+ if ( pWin )
+ {
+ aTextSize = pWin->LogicToPixel( Size( pEditEngine->CalcTextWidth(), pEditEngine->GetTextHeight() ), pEditEngine->GetRefMapMode() );
+ }
+ tools::Long nTextWidth = aTextSize.Width();
+ tools::Long nTextHeight = aTextSize.Height();
+
+ tools::Long nOffsetX = nLeftM;
+ tools::Long nDiffX = nTextWidth - nWidth;
+ if ( nDiffX > 0 )
+ {
+ switch ( eHorJust )
+ {
+ case SvxCellHorJustify::Right:
+ {
+ nOffsetX -= nDiffX;
+ }
+ break;
+ case SvxCellHorJustify::Center:
+ {
+ nOffsetX -= nDiffX / 2;
+ }
+ break;
+ default:
+ {
+ }
+ break;
+ }
+ }
+
+ tools::Long nOffsetY = 0;
+ const SvxVerJustifyItem* pVerJustifyItem = rDoc.GetAttr( aCellPos, ATTR_VER_JUSTIFY );
+ SvxCellVerJustify eVerJust = ( pVerJustifyItem ? pVerJustifyItem->GetValue() : SvxCellVerJustify::Standard );
+ switch ( eVerJust )
+ {
+ case SvxCellVerJustify::Standard:
+ case SvxCellVerJustify::Bottom:
+ {
+ nOffsetY = nSizeY - nBottomM - nTextHeight;
+ }
+ break;
+ case SvxCellVerJustify::Center:
+ {
+ nOffsetY = ( nSizeY - nTopM - nBottomM - nTextHeight ) / 2 + nTopM;
+ }
+ break;
+ default:
+ {
+ nOffsetY = nTopM;
+ }
+ break;
+ }
+
+ if ( mpAccessibleCell )
+ {
+ mpAccessibleCell->SetOffset( Point( nOffsetX, nOffsetY ) );
+ }
+
+ pEditEngine->SetNotifyHdl( LINK(this, ScAccessibleCellTextData, NotifyHdl) );
+ }
+
+ return pForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleCellTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ mpViewForwarder.reset(new ScViewForwarder(mpViewShell, meSplitPos));
+ return mpViewForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleCellTextData::GetEditViewForwarder( bool /* bCreate */ )
+{
+ //#102219#; there should no EditViewForwarder be, because the cell is now readonly in this interface
+ return nullptr;
+}
+
+IMPL_LINK(ScAccessibleTextData, NotifyHdl, EENotify&, aNotify, void)
+{
+ ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &aNotify );
+
+ if (aHint)
+ GetBroadcaster().Broadcast(*aHint);
+}
+
+ScDocShell* ScAccessibleCellTextData::GetDocShell(ScTabViewShell* pViewShell)
+{
+ ScDocShell* pDocSh = nullptr;
+ if (pViewShell)
+ pDocSh = pViewShell->GetViewData().GetDocShell();
+ return pDocSh;
+}
+
+ScAccessibleEditObjectTextData::ScAccessibleEditObjectTextData(EditView* pEditView, OutputDevice* pWin, bool isClone)
+ :
+ mpEditView(pEditView),
+ mpEditEngine(pEditView ? pEditView->GetEditEngine() : nullptr),
+ mpWindow(pWin)
+{
+ // If the object is cloned, do NOT add notify hdl.
+ mbIsCloned = isClone;
+ if (mpEditEngine && !mbIsCloned)
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
+}
+
+ScAccessibleEditObjectTextData::~ScAccessibleEditObjectTextData()
+{
+ // If the object is cloned, do NOT set notify hdl.
+ if (mpEditEngine && !mbIsCloned)
+ mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+ mpEditViewForwarder.reset();
+ mpForwarder.reset();
+}
+
+void ScAccessibleEditObjectTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpWindow = nullptr;
+ mpEditView = nullptr;
+ mpEditEngine = nullptr;
+ mpForwarder.reset();
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ if (mpEditViewForwarder)
+ mpEditViewForwarder->SetInvalid();
+ }
+ ScAccessibleTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessibleEditObjectTextData::Clone() const
+{
+ // Add para to indicate the object is cloned
+ return new ScAccessibleEditObjectTextData(mpEditView, mpWindow, true);
+}
+
+SvxTextForwarder* ScAccessibleEditObjectTextData::GetTextForwarder()
+{
+ if ((!mpForwarder && mpEditView) || (mpEditEngine && !mpEditEngine->GetNotifyHdl().IsSet()))
+ {
+ if (!mpEditEngine)
+ mpEditEngine = mpEditView->GetEditEngine();
+ // If the object is cloned, do NOT add notify hdl.
+ if (mpEditEngine && !mpEditEngine->GetNotifyHdl().IsSet()&&!mbIsCloned)
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
+ if(!mpForwarder)
+ mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
+ }
+ return mpForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleEditObjectTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ {
+ // i#49561 Get right-aligned cell content to be read by screenreader.
+ mpViewForwarder.reset(new ScEditObjectViewForwarder( mpWindow, mpEditView ));
+ }
+ return mpViewForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleEditObjectTextData::GetEditViewForwarder( bool bCreate )
+{
+ if (!mpEditViewForwarder && mpEditView)
+ mpEditViewForwarder.reset(new ScEditViewForwarder(mpEditView, mpWindow));
+ if (bCreate)
+ {
+ if (!mpEditView && mpEditViewForwarder)
+ {
+ mpEditViewForwarder.reset();
+ }
+ }
+ return mpEditViewForwarder.get();
+}
+
+IMPL_LINK(ScAccessibleEditObjectTextData, NotifyHdl, EENotify&, rNotify, void)
+{
+ ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &rNotify );
+
+ if (aHint)
+ GetBroadcaster().Broadcast(*aHint);
+}
+
+ScAccessibleEditLineTextData::ScAccessibleEditLineTextData(EditView* pEditView,
+ OutputDevice* pWin,
+ ScTextWnd* pTxtWnd)
+ : ScAccessibleEditObjectTextData(pEditView, pWin)
+ , mpTxtWnd(pTxtWnd)
+ , mbEditEngineCreated(false)
+{
+ if (mpTxtWnd)
+ mpTxtWnd->InsertAccessibleTextData( *this );
+}
+
+ScAccessibleEditLineTextData::~ScAccessibleEditLineTextData()
+{
+ if (mpTxtWnd)
+ mpTxtWnd->RemoveAccessibleTextData( *this );
+
+ if (mbEditEngineCreated && mpEditEngine)
+ {
+ delete mpEditEngine;
+ mpEditEngine = nullptr; // don't access in ScAccessibleEditObjectTextData dtor!
+ }
+ else if (mpTxtWnd && mpTxtWnd->HasEditView() && mpTxtWnd->GetEditView()->GetEditEngine())
+ {
+ // the NotifyHdl also has to be removed from the ScTextWnd's EditEngine
+ // (it's set in ScAccessibleEditLineTextData::GetTextForwarder, and mpEditEngine
+ // is reset there)
+ mpTxtWnd->GetEditView()->GetEditEngine()->SetNotifyHdl(Link<EENotify&,void>());
+ }
+}
+
+void ScAccessibleEditLineTextData::Dispose()
+{
+ if (mpTxtWnd)
+ mpTxtWnd->RemoveAccessibleTextData( *this );
+
+ ResetEditMode();
+ mpWindow = nullptr;
+ mpTxtWnd = nullptr;
+}
+
+ScAccessibleTextData* ScAccessibleEditLineTextData::Clone() const
+{
+ return new ScAccessibleEditLineTextData(mpEditView, mpWindow, mpTxtWnd);
+}
+
+SvxTextForwarder* ScAccessibleEditLineTextData::GetTextForwarder()
+{
+ if (mpTxtWnd)
+ {
+ if (mpTxtWnd->HasEditView())
+ {
+ mpEditView = mpTxtWnd->GetEditView();
+
+ if (mbEditEngineCreated && mpEditEngine)
+ ResetEditMode();
+ mbEditEngineCreated = false;
+
+ mpEditView = mpTxtWnd->GetEditView();
+ ScAccessibleEditObjectTextData::GetTextForwarder(); // fill the mpForwarder
+ mpEditEngine = nullptr;
+ }
+ else
+ {
+ mpEditView = nullptr;
+
+ if (mpEditEngine && !mbEditEngineCreated)
+ ResetEditMode();
+ if (!mpEditEngine)
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ mpEditEngine = new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true);
+ mbEditEngineCreated = true;
+ mpEditEngine->EnableUndo( false );
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
+
+ mpEditEngine->SetText(mpTxtWnd->GetTextString());
+
+#if 0
+ Size aSize(pTxtWnd->GetSizePixel());
+ aSize = pTxtWnd->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+#else
+ OutputDevice& rDevice = mpTxtWnd->GetDrawingArea()->get_ref_device();
+ Size aSize(rDevice.GetOutputSizePixel());
+ aSize = rDevice.PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+#endif
+
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
+ }
+ }
+ }
+ return mpForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleEditLineTextData::GetEditViewForwarder( bool bCreate )
+{
+ if (mpTxtWnd)
+ {
+ if (!mpTxtWnd->HasEditView() && bCreate)
+ {
+ if ( !mpTxtWnd->IsInputActive() )
+ {
+ mpTxtWnd->StartEditEngine();
+ mpTxtWnd->GrabFocus();
+
+ mpEditView = mpTxtWnd->GetEditView();
+ }
+ }
+ }
+
+ return ScAccessibleEditObjectTextData::GetEditViewForwarder(bCreate);
+}
+
+void ScAccessibleEditLineTextData::ResetEditMode()
+{
+ if (mbEditEngineCreated && mpEditEngine)
+ delete mpEditEngine;
+ else if (mpTxtWnd && mpTxtWnd->HasEditView() && mpTxtWnd->GetEditView()->GetEditEngine())
+ mpTxtWnd->GetEditView()->GetEditEngine()->SetNotifyHdl(Link<EENotify&,void>());
+ mpEditEngine = nullptr;
+
+ mpForwarder.reset();
+ mpEditViewForwarder.reset();
+ mpViewForwarder.reset();
+ mbEditEngineCreated = false;
+}
+
+void ScAccessibleEditLineTextData::TextChanged()
+{
+ if (mbEditEngineCreated && mpEditEngine)
+ {
+ if (mpTxtWnd)
+ mpEditEngine->SetText(mpTxtWnd->GetTextString());
+ }
+}
+
+void ScAccessibleEditLineTextData::StartEdit()
+{
+ ResetEditMode();
+ mpEditView = nullptr;
+
+ // send SdrHintKind::BeginEdit
+ SdrHint aHint(SdrHintKind::BeginEdit);
+ GetBroadcaster().Broadcast( aHint );
+}
+
+void ScAccessibleEditLineTextData::EndEdit()
+{
+ // send SdrHintKind::EndEdit
+ SdrHint aHint(SdrHintKind::EndEdit);
+ GetBroadcaster().Broadcast( aHint );
+
+ ResetEditMode();
+ mpEditView = nullptr;
+}
+
+// ScAccessiblePreviewCellTextData: shared data between sub objects of an accessible cell text object
+
+ScAccessiblePreviewCellTextData::ScAccessiblePreviewCellTextData(ScPreviewShell* pViewShell,
+ const ScAddress& rP)
+ : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
+ mpViewShell(pViewShell)
+{
+}
+
+ScAccessiblePreviewCellTextData::~ScAccessiblePreviewCellTextData()
+{
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+}
+
+void ScAccessiblePreviewCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr; // invalid now
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleCellBaseTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessiblePreviewCellTextData::Clone() const
+{
+ return new ScAccessiblePreviewCellTextData(mpViewShell, aCellPos);
+}
+
+SvxTextForwarder* ScAccessiblePreviewCellTextData::GetTextForwarder()
+{
+ bool bEditEngineBefore(pEditEngine != nullptr);
+
+ ScCellTextData::GetTextForwarder(); // creates Forwarder and EditEngine
+
+ if (!bEditEngineBefore && pEditEngine)
+ {
+ Size aSize(mpViewShell->GetLocationData().GetCellOutputRect(aCellPos).GetSize());
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ aSize = pWin->PixelToLogic(aSize, pEditEngine->GetRefMapMode());
+ pEditEngine->SetPaperSize(aSize);
+ }
+
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl( LINK(this, ScAccessiblePreviewCellTextData, NotifyHdl) );
+
+ return pForwarder.get();
+}
+
+SvxViewForwarder* ScAccessiblePreviewCellTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ mpViewForwarder.reset(new ScPreviewCellViewForwarder(mpViewShell));
+ return mpViewForwarder.get();
+}
+
+ScDocShell* ScAccessiblePreviewCellTextData::GetDocShell(ScPreviewShell* pViewShell)
+{
+ ScDocShell* pDocSh = nullptr;
+ if (pViewShell)
+ pDocSh = pViewShell->GetDocument().GetDocumentShell();
+ return pDocSh;
+}
+
+// ScAccessiblePreviewHeaderCellTextData: shared data between sub objects of an accessible cell text object
+
+ScAccessiblePreviewHeaderCellTextData::ScAccessiblePreviewHeaderCellTextData(ScPreviewShell* pViewShell,
+ OUString aText, const ScAddress& rP, bool bColHeader, bool bRowHeader)
+ : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
+ mpViewShell(pViewShell),
+ maText(std::move(aText)),
+ mbColHeader(bColHeader),
+ mbRowHeader(bRowHeader)
+{
+}
+
+ScAccessiblePreviewHeaderCellTextData::~ScAccessiblePreviewHeaderCellTextData()
+{
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+}
+
+void ScAccessiblePreviewHeaderCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr; // invalid now
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleCellBaseTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessiblePreviewHeaderCellTextData::Clone() const
+{
+ return new ScAccessiblePreviewHeaderCellTextData(mpViewShell, maText, aCellPos, mbColHeader, mbRowHeader);
+}
+
+SvxTextForwarder* ScAccessiblePreviewHeaderCellTextData::GetTextForwarder()
+{
+ if (!pEditEngine)
+ {
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pEditEngine = rDoc.CreateFieldEditEngine();
+ }
+ else
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) );
+ }
+ pEditEngine->EnableUndo( false );
+ if (pDocShell)
+ pEditEngine->SetRefDevice(pDocShell->GetRefDevice());
+ else
+ pEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) );
+ }
+
+ if (bDataValid)
+ return pForwarder.get();
+
+ if (!maText.isEmpty())
+ {
+ if ( mpViewShell )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ Size aSize(mpViewShell->GetLocationData().GetHeaderCellOutputRect(aVisRect, aCellPos, mbColHeader).GetSize());
+ if (pWindow)
+ aSize = pWindow->PixelToLogic(aSize, pEditEngine->GetRefMapMode());
+ pEditEngine->SetPaperSize(aSize);
+ }
+ pEditEngine->SetTextCurrentDefaults( maText );
+ }
+
+ bDataValid = true;
+
+ pEditEngine->SetNotifyHdl( LINK(this, ScAccessiblePreviewHeaderCellTextData, NotifyHdl) );
+
+ return pForwarder.get();
+}
+
+SvxViewForwarder* ScAccessiblePreviewHeaderCellTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ mpViewForwarder.reset(new ScPreviewHeaderCellViewForwarder(mpViewShell));
+ return mpViewForwarder.get();
+}
+
+ScDocShell* ScAccessiblePreviewHeaderCellTextData::GetDocShell(ScPreviewShell* pViewShell)
+{
+ ScDocShell* pDocSh = nullptr;
+ if (pViewShell)
+ pDocSh = pViewShell->GetDocument().GetDocumentShell();
+ return pDocSh;
+}
+
+ScAccessibleHeaderTextData::ScAccessibleHeaderTextData(ScPreviewShell* pViewShell,
+ const EditTextObject* pEditObj, SvxAdjust eAdjust)
+ :
+ mpViewShell(pViewShell),
+ mpDocSh(nullptr),
+ mpEditObj(pEditObj),
+ mbDataValid(false),
+ meAdjust(eAdjust)
+{
+ if (pViewShell)
+ mpDocSh = pViewShell->GetDocument().GetDocumentShell();
+ if (mpDocSh)
+ mpDocSh->GetDocument().AddUnoObject(*this);
+}
+
+ScAccessibleHeaderTextData::~ScAccessibleHeaderTextData()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ if (mpDocSh)
+ mpDocSh->GetDocument().RemoveUnoObject(*this);
+ if (mpEditEngine)
+ mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpEditEngine.reset();
+ mpForwarder.reset();
+}
+
+ScAccessibleTextData* ScAccessibleHeaderTextData::Clone() const
+{
+ return new ScAccessibleHeaderTextData(mpViewShell, mpEditObj, meAdjust);
+}
+
+void ScAccessibleHeaderTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr;// invalid now
+ mpDocSh = nullptr;
+ if (mxViewForwarder)
+ mxViewForwarder->SetInvalid();
+ }
+}
+
+SvxTextForwarder* ScAccessibleHeaderTextData::GetTextForwarder()
+{
+ if (!mpEditEngine)
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ std::unique_ptr<ScHeaderEditEngine> pHdrEngine(new ScHeaderEditEngine( pEnginePool.get() ));
+
+ pHdrEngine->EnableUndo( false );
+ pHdrEngine->SetRefMapMode(MapMode(MapUnit::MapTwip));
+
+ // default font must be set, independently of document
+ // -> use global pool from module
+
+ SfxItemSet aDefaults( pHdrEngine->GetEmptyItemSet() );
+ const ScPatternAttr& rPattern = SC_MOD()->GetPool().GetDefaultItem(ATTR_PATTERN);
+ rPattern.FillEditItemSet( &aDefaults );
+ // FillEditItemSet adjusts font height to 1/100th mm,
+ // but for header/footer twips is needed, as in the PatternAttr:
+ aDefaults.Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ aDefaults.Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ aDefaults.Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ aDefaults.Put( SvxAdjustItem( meAdjust, EE_PARA_JUST ) );
+ pHdrEngine->SetDefaults( aDefaults );
+
+ ScHeaderFieldData aData;
+ if (mpViewShell)
+ mpViewShell->FillFieldData(aData);
+ else
+ ScHeaderFooterTextObj::FillDummyFieldData( aData );
+ pHdrEngine->SetData( aData );
+
+ mpEditEngine = std::move(pHdrEngine);
+ mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
+ }
+
+ if (mbDataValid)
+ return mpForwarder.get();
+
+ if ( mpViewShell )
+ {
+ tools::Rectangle aVisRect;
+ mpViewShell->GetLocationData().GetHeaderPosition(aVisRect);
+ Size aSize(aVisRect.GetSize());
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ aSize = pWin->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+ }
+ if (mpEditObj)
+ mpEditEngine->SetTextCurrentDefaults(*mpEditObj);
+
+ mbDataValid = true;
+ return mpForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleHeaderTextData::GetViewForwarder()
+{
+ if (!mxViewForwarder)
+ mxViewForwarder = std::make_unique<ScPreviewHeaderFooterViewForwarder>(mpViewShell);
+ return mxViewForwarder.get();
+}
+
+ScAccessibleNoteTextData::ScAccessibleNoteTextData(ScPreviewShell* pViewShell,
+ OUString sText, const ScAddress& aCellPos, bool bMarkNote)
+ :
+ mpViewShell(pViewShell),
+ mpDocSh(nullptr),
+ msText(std::move(sText)),
+ maCellPos(aCellPos),
+ mbMarkNote(bMarkNote),
+ mbDataValid(false)
+{
+ if (pViewShell)
+ mpDocSh = pViewShell->GetDocument().GetDocumentShell();
+ if (mpDocSh)
+ mpDocSh->GetDocument().AddUnoObject(*this);
+}
+
+ScAccessibleNoteTextData::~ScAccessibleNoteTextData()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ if (mpDocSh)
+ mpDocSh->GetDocument().RemoveUnoObject(*this);
+ if (mpEditEngine)
+ mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpEditEngine.reset();
+ mpForwarder.reset();
+}
+
+ScAccessibleTextData* ScAccessibleNoteTextData::Clone() const
+{
+ return new ScAccessibleNoteTextData(mpViewShell, msText, maCellPos, mbMarkNote);
+}
+
+void ScAccessibleNoteTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr;// invalid now
+ mpDocSh = nullptr;
+ if (mxViewForwarder)
+ mxViewForwarder->SetInvalid();
+ }
+}
+
+SvxTextForwarder* ScAccessibleNoteTextData::GetTextForwarder()
+{
+ if (!mpEditEngine)
+ {
+ if ( mpDocSh )
+ {
+ ScDocument& rDoc = mpDocSh->GetDocument();
+ mpEditEngine = rDoc.CreateFieldEditEngine();
+ }
+ else
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ mpEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) );
+ }
+ mpEditEngine->EnableUndo( false );
+ if (mpDocSh)
+ mpEditEngine->SetRefDevice(mpDocSh->GetRefDevice());
+ else
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mpForwarder.reset( new SvxEditEngineForwarder(*mpEditEngine) );
+ }
+
+ if (mbDataValid)
+ return mpForwarder.get();
+
+ if (!msText.isEmpty())
+ {
+
+ if ( mpViewShell )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ Size aSize(mpViewShell->GetLocationData().GetNoteInRangeOutputRect(aVisRect, mbMarkNote, maCellPos).GetSize());
+ if (pWindow)
+ aSize = pWindow->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+ }
+ mpEditEngine->SetTextCurrentDefaults( msText );
+ }
+
+ mbDataValid = true;
+
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleNoteTextData, NotifyHdl) );
+
+ return mpForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleNoteTextData::GetViewForwarder()
+{
+ if (!mxViewForwarder)
+ mxViewForwarder = std::make_unique<ScPreviewNoteViewForwarder>(mpViewShell);
+ return mxViewForwarder.get();
+}
+
+// CSV import =================================================================
+
+class ScCsvViewForwarder : public SvxViewForwarder
+{
+ VclPtr<OutputDevice> mpWindow;
+
+public:
+ explicit ScCsvViewForwarder( OutputDevice* pWindow );
+
+ 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;
+
+ void SetInvalid();
+};
+
+ScCsvViewForwarder::ScCsvViewForwarder( OutputDevice* pWindow ) :
+ mpWindow( pWindow )
+{
+}
+
+bool ScCsvViewForwarder::IsValid() const
+{
+ return mpWindow != nullptr;
+}
+
+Point ScCsvViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if( !mpWindow ) return Point();
+ return mpWindow->LogicToPixel( rPoint, rMapMode );
+}
+
+Point ScCsvViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if( !mpWindow ) return Point();
+ return mpWindow->PixelToLogic( rPoint, rMapMode );
+}
+
+void ScCsvViewForwarder::SetInvalid()
+{
+ mpWindow = nullptr;
+}
+
+ScAccessibleCsvTextData::ScAccessibleCsvTextData(
+ OutputDevice* pWindow, EditEngine* pEditEngine,
+ OUString aCellText, const Size& rCellSize ) :
+ mpWindow( pWindow ),
+ mpEditEngine( pEditEngine ),
+ maCellText(std::move( aCellText )),
+ maCellSize( rCellSize )
+{
+}
+
+ScAccessibleCsvTextData::~ScAccessibleCsvTextData()
+{
+}
+
+void ScAccessibleCsvTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpWindow = nullptr;
+ mpEditEngine = nullptr;
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleTextData::Notify( rBC, rHint );
+}
+
+ScAccessibleTextData* ScAccessibleCsvTextData::Clone() const
+{
+ return new ScAccessibleCsvTextData( mpWindow, mpEditEngine, maCellText, maCellSize );
+}
+
+SvxTextForwarder* ScAccessibleCsvTextData::GetTextForwarder()
+{
+ if( mpEditEngine )
+ {
+ mpEditEngine->SetPaperSize( maCellSize );
+ mpEditEngine->SetText( maCellText );
+ if( !mpTextForwarder )
+ mpTextForwarder.reset( new SvxEditEngineForwarder( *mpEditEngine ) );
+ }
+ else
+ mpTextForwarder.reset();
+ return mpTextForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleCsvTextData::GetViewForwarder()
+{
+ if( !mpViewForwarder )
+ mpViewForwarder.reset( new ScCsvViewForwarder( mpWindow ) );
+ return mpViewForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleCsvTextData::GetEditViewForwarder( bool /* bCreate */ )
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/DrawModelBroadcaster.cxx b/sc/source/ui/Accessibility/DrawModelBroadcaster.cxx
new file mode 100644
index 0000000000..c181656fe1
--- /dev/null
+++ b/sc/source/ui/Accessibility/DrawModelBroadcaster.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 <DrawModelBroadcaster.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/unomod.hxx>
+#include <svx/svdobj.hxx>
+
+using namespace ::com::sun::star;
+
+ScDrawModelBroadcaster::ScDrawModelBroadcaster( SdrModel *pDrawModel ) :
+ mpDrawModel( pDrawModel )
+{
+ if (mpDrawModel)
+ StartListening( *mpDrawModel );
+}
+
+ScDrawModelBroadcaster::~ScDrawModelBroadcaster()
+{
+ if (mpDrawModel)
+ EndListening( *mpDrawModel );
+}
+
+void SAL_CALL ScDrawModelBroadcaster::addEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ std::unique_lock aGuard(maListenerMutex);
+ maEventListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL ScDrawModelBroadcaster::removeEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ std::unique_lock aGuard(maListenerMutex);
+ maEventListeners.removeInterface( aGuard, xListener );
+}
+
+void SAL_CALL ScDrawModelBroadcaster::addShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ assert(xShape.is() && "no shape?");
+ std::scoped_lock aGuard(maListenerMutex);
+ auto rv = maShapeListeners.emplace(xShape, xListener);
+ assert(rv.second && "duplicate listener?");
+ (void)rv;
+}
+
+void SAL_CALL ScDrawModelBroadcaster::removeShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ std::scoped_lock aGuard(maListenerMutex);
+ auto it = maShapeListeners.find(xShape);
+ if (it != maShapeListeners.end())
+ {
+ assert(it->second == xListener && "removing wrong listener?");
+ (void)xListener;
+ maShapeListeners.erase(it);
+ }
+}
+
+void ScDrawModelBroadcaster::Notify( SfxBroadcaster&,
+ const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+
+ document::EventObject aEvent;
+ if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) )
+ return;
+
+ std::unique_lock aGuard(maListenerMutex);
+ maEventListeners.forEach(aGuard,
+ [&aEvent](const css::uno::Reference<document::XEventListener>& xListener)
+ {
+ xListener->notifyEvent(aEvent);
+ }
+ );
+
+ // right now, we're only handling the specific event necessary to fix this performance problem
+ if (pSdrHint->GetKind() == SdrHintKind::ObjectChange)
+ {
+ auto pSdrObject = const_cast<SdrObject*>(pSdrHint->GetObject());
+ uno::Reference<drawing::XShape> xShape(pSdrObject->getUnoShape(), uno::UNO_QUERY);
+ auto it = maShapeListeners.find(xShape);
+ if (it != maShapeListeners.end())
+ it->second->notifyShapeEvent(aEvent);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/AnalysisOfVarianceDialog.cxx b/sc/source/ui/StatisticsDialogs/AnalysisOfVarianceDialog.cxx
new file mode 100644
index 0000000000..6f1fccd9cb
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/AnalysisOfVarianceDialog.cxx
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+#include <string_view>
+
+#include <rangelst.hxx>
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <AnalysisOfVarianceDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+namespace
+{
+
+struct StatisticCalculation {
+ TranslateId aLabelId;
+ const char* aFormula;
+ const char* aResultRangeName;
+};
+
+StatisticCalculation const lclBasicStatistics[] =
+{
+ { STR_ANOVA_LABEL_GROUPS, nullptr, nullptr },
+ { STRID_CALC_COUNT, "=COUNT(%RANGE%)", "COUNT_RANGE" },
+ { STRID_CALC_SUM, "=SUM(%RANGE%)", "SUM_RANGE" },
+ { STRID_CALC_MEAN, "=AVERAGE(%RANGE%)", "MEAN_RANGE" },
+ { STRID_CALC_VARIANCE, "=VAR(%RANGE%)", "VAR_RANGE" },
+ { {}, nullptr, nullptr }
+};
+
+const TranslateId lclAnovaLabels[] =
+{
+ STR_ANOVA_LABEL_SOURCE_OF_VARIATION,
+ STR_ANOVA_LABEL_SS,
+ STR_ANOVA_LABEL_DF,
+ STR_ANOVA_LABEL_MS,
+ STR_ANOVA_LABEL_F,
+ STR_ANOVA_LABEL_P_VALUE,
+ STR_ANOVA_LABEL_F_CRITICAL,
+ {}
+};
+
+constexpr OUString strWildcardRange = u"%RANGE%"_ustr;
+
+OUString lclCreateMultiParameterFormula(
+ ScRangeList& aRangeList, const OUString& aFormulaTemplate,
+ std::u16string_view aWildcard, const ScDocument& rDocument,
+ const ScAddress::Details& aAddressDetails)
+{
+ OUStringBuffer aResult;
+ for (size_t i = 0; i < aRangeList.size(); i++)
+ {
+ OUString aRangeString(aRangeList[i].Format(rDocument, ScRefFlags::RANGE_ABS_3D, aAddressDetails));
+ OUString aFormulaString = aFormulaTemplate.replaceAll(aWildcard, aRangeString);
+ aResult.append(aFormulaString);
+ if(i != aRangeList.size() - 1) // Not Last
+ aResult.append(";");
+ }
+ return aResult.makeStringAndClear();
+}
+
+void lclMakeSubRangesList(ScRangeList& rRangeList, const ScRange& rInputRange, ScStatisticsInputOutputDialog::GroupedBy aGroupedBy)
+{
+ std::unique_ptr<DataRangeIterator> pIterator;
+ if (aGroupedBy == ScStatisticsInputOutputDialog::BY_COLUMN)
+ pIterator.reset(new DataRangeByColumnIterator(rInputRange));
+ else
+ pIterator.reset(new DataRangeByRowIterator(rInputRange));
+
+ for( ; pIterator->hasNext(); pIterator->next() )
+ {
+ ScRange aRange = pIterator->get();
+ rRangeList.push_back(aRange);
+ }
+}
+
+}
+
+ScAnalysisOfVarianceDialog::ScAnalysisOfVarianceDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData )
+ : ScStatisticsInputOutputDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/analysisofvariancedialog.ui",
+ "AnalysisOfVarianceDialog")
+ , meFactor(SINGLE_FACTOR)
+ , mxAlphaField(m_xBuilder->weld_spin_button("alpha-spin"))
+ , mxSingleFactorRadio(m_xBuilder->weld_radio_button("radio-single-factor"))
+ , mxTwoFactorRadio(m_xBuilder->weld_radio_button("radio-two-factor"))
+ , mxRowsPerSampleField(m_xBuilder->weld_spin_button("rows-per-sample-spin"))
+{
+ mxSingleFactorRadio->connect_toggled( LINK( this, ScAnalysisOfVarianceDialog, FactorChanged ) );
+ mxTwoFactorRadio->connect_toggled( LINK( this, ScAnalysisOfVarianceDialog, FactorChanged ) );
+
+ mxSingleFactorRadio->set_active(true);
+ mxTwoFactorRadio->set_active(false);
+
+ FactorChanged();
+}
+
+ScAnalysisOfVarianceDialog::~ScAnalysisOfVarianceDialog()
+{
+}
+
+void ScAnalysisOfVarianceDialog::Close()
+{
+ DoClose( ScAnalysisOfVarianceDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScAnalysisOfVarianceDialog::GetUndoNameId()
+{
+ return STR_ANALYSIS_OF_VARIANCE_UNDO_NAME;
+}
+
+IMPL_LINK_NOARG( ScAnalysisOfVarianceDialog, FactorChanged, weld::Toggleable&, void )
+{
+ FactorChanged();
+}
+
+void ScAnalysisOfVarianceDialog::FactorChanged()
+{
+ if (mxSingleFactorRadio->get_active())
+ {
+ mxGroupByRowsRadio->set_sensitive(true);
+ mxGroupByColumnsRadio->set_sensitive(true);
+ mxRowsPerSampleField->set_sensitive(false);
+ meFactor = SINGLE_FACTOR;
+ }
+ else if (mxTwoFactorRadio->get_active())
+ {
+ mxGroupByRowsRadio->set_sensitive(false);
+ mxGroupByColumnsRadio->set_sensitive(false);
+ mxRowsPerSampleField->set_sensitive(false); // Rows per sample not yet implemented
+ meFactor = TWO_FACTOR;
+ }
+}
+
+void ScAnalysisOfVarianceDialog::RowColumn(ScRangeList& rRangeList, AddressWalkerWriter& aOutput, FormulaTemplate& aTemplate,
+ const OUString& sFormula, GroupedBy aGroupedBy, ScRange* pResultRange)
+{
+ if (pResultRange != nullptr)
+ pResultRange->aStart = aOutput.current();
+ if (!sFormula.isEmpty())
+ {
+ for (size_t i = 0; i < rRangeList.size(); i++)
+ {
+ ScRange const & rRange = rRangeList[i];
+ aTemplate.setTemplate(sFormula);
+ aTemplate.applyRange(strWildcardRange, rRange);
+ aOutput.writeFormula(aTemplate.getTemplate());
+ if (pResultRange != nullptr)
+ pResultRange->aEnd = aOutput.current();
+ aOutput.nextRow();
+ }
+ }
+ else
+ {
+ TranslateId pLabelId = (aGroupedBy == BY_COLUMN) ? STR_COLUMN_LABEL_TEMPLATE : STR_ROW_LABEL_TEMPLATE;
+ OUString aLabelTemplate(ScResId(pLabelId));
+
+ for (size_t i = 0; i < rRangeList.size(); i++)
+ {
+ aTemplate.setTemplate(aLabelTemplate);
+ aTemplate.applyNumber(u"%NUMBER%", i + 1);
+ aOutput.writeString(aTemplate.getTemplate());
+ if (pResultRange != nullptr)
+ pResultRange->aEnd = aOutput.current();
+ aOutput.nextRow();
+ }
+ }
+}
+
+void ScAnalysisOfVarianceDialog::AnovaSingleFactor(AddressWalkerWriter& output, FormulaTemplate& aTemplate)
+{
+ output.writeBoldString(ScResId(STR_ANOVA_SINGLE_FACTOR_LABEL));
+ output.newLine();
+
+ double aAlphaValue = mxAlphaField->get_value() / 100.0;
+ output.writeString(ScResId(STR_LABEL_ALPHA));
+ output.nextColumn();
+ output.writeValue(aAlphaValue);
+ aTemplate.autoReplaceAddress("%ALPHA%", output.current());
+ output.newLine();
+ output.newLine();
+
+ // Write labels
+ for(sal_Int32 i = 0; lclBasicStatistics[i].aLabelId; i++)
+ {
+ output.writeString(ScResId(lclBasicStatistics[i].aLabelId));
+ output.nextColumn();
+ }
+ output.newLine();
+
+ // Collect aRangeList
+ ScRangeList aRangeList;
+ lclMakeSubRangesList(aRangeList, mInputRange, mGroupedBy);
+
+ output.push();
+
+ // Write values
+ for(sal_Int32 i = 0; lclBasicStatistics[i].aLabelId; i++)
+ {
+ output.resetRow();
+ ScRange aResultRange;
+ OUString sFormula = OUString::createFromAscii(lclBasicStatistics[i].aFormula);
+ RowColumn(aRangeList, output, aTemplate, sFormula, mGroupedBy, &aResultRange);
+ output.nextColumn();
+ if (lclBasicStatistics[i].aResultRangeName != nullptr)
+ {
+ OUString sResultRangeName = OUString::createFromAscii(lclBasicStatistics[i].aResultRangeName);
+ aTemplate.autoReplaceRange("%" + sResultRangeName + "%", aResultRange);
+ }
+ }
+
+ output.nextRow(); // Blank row
+
+ // Write ANOVA labels
+ output.resetColumn();
+ for(sal_Int32 i = 0; lclAnovaLabels[i]; i++)
+ {
+ output.writeString(ScResId(lclAnovaLabels[i]));
+ output.nextColumn();
+ }
+ output.nextRow();
+
+ aTemplate.autoReplaceRange("%FIRST_COLUMN%", aRangeList[0]);
+
+ // Between Groups
+ {
+ // Label
+ output.resetColumn();
+ output.writeString(ScResId(STR_ANOVA_LABEL_BETWEEN_GROUPS));
+ output.nextColumn();
+
+ // Sum of Squares
+ aTemplate.setTemplate("=SUMPRODUCT(%SUM_RANGE%;%MEAN_RANGE%)-SUM(%SUM_RANGE%)^2/SUM(%COUNT_RANGE%)");
+ aTemplate.autoReplaceAddress("%BETWEEN_SS%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=COUNT(%SUM_RANGE%)-1");
+ aTemplate.autoReplaceAddress("%BETWEEN_DF%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // MS
+ aTemplate.setTemplate("=%BETWEEN_SS% / %BETWEEN_DF%");
+ aTemplate.autoReplaceAddress("%BETWEEN_MS%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // F
+ aTemplate.setTemplate("=%BETWEEN_MS% / %WITHIN_MS%");
+ aTemplate.applyAddress(u"%WITHIN_MS%", output.current(-1, 1));
+ aTemplate.autoReplaceAddress("%F_VAL%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // P-value
+ aTemplate.setTemplate("=FDIST(%F_VAL%; %BETWEEN_DF%; %WITHIN_DF%");
+ aTemplate.applyAddress(u"%WITHIN_DF%", output.current(-3, 1));
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // F critical
+ aTemplate.setTemplate("=FINV(%ALPHA%; %BETWEEN_DF%; %WITHIN_DF%");
+ aTemplate.applyAddress(u"%WITHIN_DF%", output.current(-4, 1));
+ output.writeFormula(aTemplate.getTemplate());
+ }
+ output.nextRow();
+
+ // Within Groups
+ {
+ // Label
+ output.resetColumn();
+ output.writeString(ScResId(STR_ANOVA_LABEL_WITHIN_GROUPS));
+ output.nextColumn();
+
+ // Sum of Squares
+ OUString aSSPart = lclCreateMultiParameterFormula(aRangeList, "DEVSQ(%RANGE%)", strWildcardRange, mDocument, mAddressDetails);
+ aTemplate.setTemplate("=SUM(%RANGE%)");
+ aTemplate.applyString(strWildcardRange, aSSPart);
+ aTemplate.autoReplaceAddress("%WITHIN_SS%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=SUM(%COUNT_RANGE%)-COUNT(%COUNT_RANGE%)");
+ aTemplate.autoReplaceAddress("%WITHIN_DF%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // MS
+ aTemplate.setTemplate("=%WITHIN_SS% / %WITHIN_DF%");
+ output.writeFormula(aTemplate.getTemplate());
+ }
+ output.nextRow();
+
+ // Total
+ {
+ // Label
+ output.resetColumn();
+ output.writeString(ScResId(STR_ANOVA_LABEL_TOTAL));
+ output.nextColumn();
+
+ // Sum of Squares
+ aTemplate.setTemplate("=DEVSQ(%RANGE_LIST%)");
+ aTemplate.applyRangeList(u"%RANGE_LIST%", aRangeList, ';');
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=SUM(%COUNT_RANGE%) - 1");
+ output.writeFormula(aTemplate.getTemplate());
+ }
+ output.nextRow();
+}
+
+void ScAnalysisOfVarianceDialog::AnovaTwoFactor(AddressWalkerWriter& output, FormulaTemplate& aTemplate)
+{
+ output.writeBoldString(ScResId(STR_ANOVA_TWO_FACTOR_LABEL));
+ output.newLine();
+
+ double aAlphaValue = mxAlphaField->get_value() / 100.0;
+ output.writeString("Alpha");
+ output.nextColumn();
+ output.writeValue(aAlphaValue);
+ aTemplate.autoReplaceAddress("%ALPHA%", output.current());
+ output.newLine();
+ output.newLine();
+
+ // Write labels
+ for(sal_Int32 i = 0; lclBasicStatistics[i].aLabelId; i++)
+ {
+ output.writeString(ScResId(lclBasicStatistics[i].aLabelId));
+ output.nextColumn();
+ }
+ output.newLine();
+
+ ScRangeList aColumnRangeList;
+ ScRangeList aRowRangeList;
+
+ lclMakeSubRangesList(aColumnRangeList, mInputRange, BY_COLUMN);
+ lclMakeSubRangesList(aRowRangeList, mInputRange, BY_ROW);
+
+ // Write ColumnX values
+ output.push();
+ for(sal_Int32 i = 0; lclBasicStatistics[i].aLabelId; i++)
+ {
+ output.resetRow();
+ ScRange aResultRange;
+ OUString sFormula = OUString::createFromAscii(lclBasicStatistics[i].aFormula);
+ RowColumn(aColumnRangeList, output, aTemplate, sFormula, BY_COLUMN, &aResultRange);
+ if (lclBasicStatistics[i].aResultRangeName != nullptr)
+ {
+ OUString sResultRangeName = OUString::createFromAscii(lclBasicStatistics[i].aResultRangeName);
+ aTemplate.autoReplaceRange("%" + sResultRangeName + "_COLUMN%", aResultRange);
+ }
+ output.nextColumn();
+ }
+ output.newLine();
+
+ // Write RowX values
+ output.push();
+ for(sal_Int32 i = 0; lclBasicStatistics[i].aLabelId; i++)
+ {
+ output.resetRow();
+ ScRange aResultRange;
+ OUString sFormula = OUString::createFromAscii(lclBasicStatistics[i].aFormula);
+ RowColumn(aRowRangeList, output, aTemplate, sFormula, BY_ROW, &aResultRange);
+
+ if (lclBasicStatistics[i].aResultRangeName != nullptr)
+ {
+ OUString sResultRangeName = OUString::createFromAscii(lclBasicStatistics[i].aResultRangeName);
+ aTemplate.autoReplaceRange("%" + sResultRangeName + "_ROW%", aResultRange);
+ }
+ output.nextColumn();
+ }
+ output.newLine();
+
+ // Write ANOVA labels
+ for(sal_Int32 i = 0; lclAnovaLabels[i]; i++)
+ {
+ output.writeString(ScResId(lclAnovaLabels[i]));
+ output.nextColumn();
+ }
+ output.nextRow();
+
+ // Setup auto-replace strings
+ aTemplate.autoReplaceRange(strWildcardRange, mInputRange);
+ aTemplate.autoReplaceRange("%FIRST_COLUMN%", aColumnRangeList[0]);
+ aTemplate.autoReplaceRange("%FIRST_ROW%", aRowRangeList[0]);
+
+ // Rows
+ {
+ // Label
+ output.resetColumn();
+ output.writeString("Rows");
+ output.nextColumn();
+
+ // Sum of Squares
+ aTemplate.setTemplate("=SUMPRODUCT(%SUM_RANGE_ROW%;%MEAN_RANGE_ROW%) - SUM(%RANGE%)^2 / COUNT(%RANGE%)");
+ aTemplate.autoReplaceAddress("%ROW_SS%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=MAX(%COUNT_RANGE_COLUMN%) - 1");
+ aTemplate.autoReplaceAddress("%ROW_DF%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // MS
+ aTemplate.setTemplate("=%ROW_SS% / %ROW_DF%");
+ aTemplate.autoReplaceAddress("%MS_ROW%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // F
+ aTemplate.setTemplate("=%MS_ROW% / %MS_ERROR%");
+ aTemplate.applyAddress(u"%MS_ERROR%", output.current(-1, 2));
+ aTemplate.autoReplaceAddress("%F_ROW%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // P-value
+ aTemplate.setTemplate("=FDIST(%F_ROW%; %ROW_DF%; %ERROR_DF%");
+ aTemplate.applyAddress(u"%ERROR_DF%", output.current(-3, 2));
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // F critical
+ aTemplate.setTemplate("=FINV(%ALPHA%; %ROW_DF%; %ERROR_DF%");
+ aTemplate.applyAddress(u"%ERROR_DF%", output.current(-4, 2));
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+ }
+ output.nextRow();
+
+ // Columns
+ {
+ // Label
+ output.resetColumn();
+ output.writeString("Columns");
+ output.nextColumn();
+
+ // Sum of Squares
+ aTemplate.setTemplate("=SUMPRODUCT(%SUM_RANGE_COLUMN%;%MEAN_RANGE_COLUMN%) - SUM(%RANGE%)^2 / COUNT(%RANGE%)");
+ aTemplate.autoReplaceAddress("%COLUMN_SS%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=MAX(%COUNT_RANGE_ROW%) - 1");
+ aTemplate.autoReplaceAddress("%COLUMN_DF%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // MS
+ aTemplate.setTemplate("=%COLUMN_SS% / %COLUMN_DF%");
+ aTemplate.autoReplaceAddress("%MS_COLUMN%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // F
+ aTemplate.setTemplate("=%MS_COLUMN% / %MS_ERROR%");
+ aTemplate.applyAddress(u"%MS_ERROR%", output.current(-1, 1));
+ aTemplate.autoReplaceAddress("%F_COLUMN%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // P-value
+ aTemplate.setTemplate("=FDIST(%F_COLUMN%; %COLUMN_DF%; %ERROR_DF%");
+ aTemplate.applyAddress(u"%ERROR_DF%", output.current(-3, 1));
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // F critical
+ aTemplate.setTemplate("=FINV(%ALPHA%; %COLUMN_DF%; %ERROR_DF%");
+ aTemplate.applyAddress(u"%ERROR_DF%", output.current(-4, 1));
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+ }
+ output.nextRow();
+
+ // Error
+ {
+ // Label
+ output.resetColumn();
+ output.writeString("Error");
+ output.nextColumn();
+
+ // Sum of Squares
+ aTemplate.setTemplate("=SUMSQ(%RANGE%)+SUM(%RANGE%)^2/COUNT(%RANGE%) - (SUMPRODUCT(%SUM_RANGE_ROW%;%MEAN_RANGE_ROW%) + SUMPRODUCT(%SUM_RANGE_COLUMN%;%MEAN_RANGE_COLUMN%))");
+ aTemplate.autoReplaceAddress("%ERROR_SS%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=%TOTAL_DF% - %ROW_DF% - %COLUMN_DF%");
+ aTemplate.applyAddress(u"%TOTAL_DF%", output.current(0,1));
+ aTemplate.autoReplaceAddress("%ERROR_DF%", output.current());
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // MS
+ aTemplate.setTemplate("=%ERROR_SS% / %ERROR_DF%");
+ output.writeFormula(aTemplate.getTemplate());
+ }
+ output.nextRow();
+
+ // Total
+ {
+ // Label
+ output.resetColumn();
+ output.writeString("Total");
+ output.nextColumn();
+
+ // Sum of Squares
+ aTemplate.setTemplate("=SUM(%ROW_SS%;%COLUMN_SS%;%ERROR_SS%)");
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+
+ // Degree of freedom
+ aTemplate.setTemplate("=COUNT(%RANGE%)-1");
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextColumn();
+ }
+}
+
+ScRange ScAnalysisOfVarianceDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter output(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ if (meFactor == SINGLE_FACTOR)
+ {
+ AnovaSingleFactor(output, aTemplate);
+ }
+ else if (meFactor == TWO_FACTOR)
+ {
+ AnovaTwoFactor(output, aTemplate);
+ }
+
+ return ScRange(output.mMinimumAddress, output.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/ChiSquareTestDialog.cxx b/sc/source/ui/StatisticsDialogs/ChiSquareTestDialog.cxx
new file mode 100644
index 0000000000..cfcf53699d
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/ChiSquareTestDialog.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/.
+ *
+ */
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <ChiSquareTestDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScChiSquareTestDialog::ScChiSquareTestDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScStatisticsInputOutputDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/chisquaretestdialog.ui", "ChiSquareTestDialog")
+{
+ m_xDialog->set_title(ScResId(STR_CHI_SQUARE_TEST));
+}
+
+ScChiSquareTestDialog::~ScChiSquareTestDialog()
+{}
+
+void ScChiSquareTestDialog::Close()
+{
+ DoClose(ScChiSquareTestDialogWrapper::GetChildWindowId());
+}
+
+TranslateId ScChiSquareTestDialog::GetUndoNameId()
+{
+ return STR_CHI_SQUARE_TEST;
+}
+
+ScRange ScChiSquareTestDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ aTemplate.autoReplaceRange("%RANGE%", mInputRange);
+
+ aOutput.writeBoldString(ScResId(STR_CHI_SQUARE_TEST));
+ aOutput.newLine();
+
+ // Alpha
+ aOutput.writeString(ScResId(STR_LABEL_ALPHA));
+ aOutput.nextColumn();
+ aOutput.writeValue(0.05);
+ aTemplate.autoReplaceAddress("%ALPHA%", aOutput.current());
+ aOutput.newLine();
+
+ // DF
+ aOutput.writeString(ScResId(STR_DEGREES_OF_FREEDOM_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=(COLUMNS(%RANGE%) - 1) * (ROWS(%RANGE%) - 1)");
+ aTemplate.autoReplaceAddress("%DEGREES_OF_FREEDOM%", aOutput.current());
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // p Value
+ aOutput.writeString(ScResId(STR_P_VALUE_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=CHITEST(%RANGE%; MMULT(MMULT(%RANGE%;TRANSPOSE(IF(COLUMN(%RANGE%))));MMULT(TRANSPOSE(IF(ROW(%RANGE%)));%RANGE%)) / SUM(%RANGE%))");
+ aTemplate.autoReplaceAddress("%P_VALUE%", aOutput.current());
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // Test Statistic
+ aOutput.writeString(ScResId(STR_TEST_STATISTIC_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=CHIINV(%P_VALUE%; %DEGREES_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // Critical value
+ aOutput.writeString(ScResId(STR_CRITICAL_VALUE_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=CHIINV(%ALPHA%; %DEGREES_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ return ScRange(aOutput.mMinimumAddress, aOutput.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/CorrelationDialog.cxx b/sc/source/ui/StatisticsDialogs/CorrelationDialog.cxx
new file mode 100644
index 0000000000..7e9a233726
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/CorrelationDialog.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <reffact.hxx>
+#include <CorrelationDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScCorrelationDialog::ScCorrelationDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScMatrixComparisonGenerator(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/correlationdialog.ui", "CorrelationDialog")
+{}
+
+void ScCorrelationDialog::Close()
+{
+ DoClose(ScCorrelationDialogWrapper::GetChildWindowId());
+}
+
+OUString ScCorrelationDialog::getLabel()
+{
+ return ScResId(STR_CORRELATION_LABEL);
+}
+
+OUString ScCorrelationDialog::getTemplate()
+{
+ return "=CORREL(%VAR1%; %VAR2%)";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/CovarianceDialog.cxx b/sc/source/ui/StatisticsDialogs/CovarianceDialog.cxx
new file mode 100644
index 0000000000..b2849d316c
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/CovarianceDialog.cxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <reffact.hxx>
+#include <CovarianceDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScCovarianceDialog::ScCovarianceDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScMatrixComparisonGenerator(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/covariancedialog.ui", "CovarianceDialog")
+{}
+
+TranslateId ScCovarianceDialog::GetUndoNameId()
+{
+ return STR_COVARIANCE_UNDO_NAME;
+}
+
+void ScCovarianceDialog::Close()
+{
+ DoClose( ScCovarianceDialogWrapper::GetChildWindowId() );
+}
+
+OUString ScCovarianceDialog::getLabel()
+{
+ return ScResId(STR_COVARIANCE_LABEL);
+}
+
+OUString ScCovarianceDialog::getTemplate()
+{
+ return "=COVAR(%VAR1%; %VAR2%)";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/DescriptiveStatisticsDialog.cxx b/sc/source/ui/StatisticsDialogs/DescriptiveStatisticsDialog.cxx
new file mode 100644
index 0000000000..0924278c50
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/DescriptiveStatisticsDialog.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/.
+ *
+ */
+
+#include <memory>
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <DescriptiveStatisticsDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+namespace
+{
+
+struct StatisticCalculation {
+ TranslateId aCalculationNameId;
+ const char* aFormula;
+};
+
+const StatisticCalculation lclCalcDefinitions[] =
+{
+ { STRID_CALC_MEAN, "=AVERAGE(%RANGE%)" },
+ { STRID_CALC_STD_ERROR, "=SQRT(VAR(%RANGE%)/COUNT(%RANGE%))"},
+ { STRID_CALC_MODE, "=MODE(%RANGE%)"},
+ { STRID_CALC_MEDIAN, "=MEDIAN(%RANGE%)"},
+ { STRID_CALC_FIRST_QUARTILE, "=QUARTILE(%RANGE%; 1)" },
+ { STRID_CALC_THIRD_QUARTILE, "=QUARTILE(%RANGE%; 3)" },
+ { STRID_CALC_VARIANCE, "=VAR(%RANGE%)"},
+ { STRID_CALC_STD_DEVIATION, "=STDEV(%RANGE%)"},
+ { STRID_CALC_KURTOSIS, "=KURT(%RANGE%)"},
+ { STRID_CALC_SKEWNESS, "=SKEW(%RANGE%)"},
+ { STRID_CALC_RANGE, "=MAX(%RANGE%)-MIN(%RANGE%)"},
+ { STRID_CALC_MIN, "=MIN(%RANGE%)"},
+ { STRID_CALC_MAX, "=MAX(%RANGE%)"},
+ { STRID_CALC_SUM, "=SUM(%RANGE%)"},
+ { STRID_CALC_COUNT, "=COUNT(%RANGE%)" },
+ { {}, nullptr }
+};
+
+}
+
+ScDescriptiveStatisticsDialog::ScDescriptiveStatisticsDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScStatisticsInputOutputDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/descriptivestatisticsdialog.ui",
+ "DescriptiveStatisticsDialog")
+{}
+
+ScDescriptiveStatisticsDialog::~ScDescriptiveStatisticsDialog()
+{}
+
+void ScDescriptiveStatisticsDialog::Close()
+{
+ DoClose( ScDescriptiveStatisticsDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScDescriptiveStatisticsDialog::GetUndoNameId()
+{
+ return STR_DESCRIPTIVE_STATISTICS_UNDO_NAME;
+}
+
+ScRange ScDescriptiveStatisticsDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ std::unique_ptr<DataRangeIterator> pIterator;
+ if (mGroupedBy == BY_COLUMN)
+ pIterator.reset(new DataRangeByColumnIterator(mInputRange));
+ else
+ pIterator.reset(new DataRangeByRowIterator(mInputRange));
+
+ aOutput.nextColumn();
+
+ // Use explicit sheet name in case the input and output are on different sheets.
+ bool b3DAddress = mInputRange.aStart.Tab() != mOutputAddress.Tab();
+
+ // Write column/row labels
+ for( ; pIterator->hasNext(); pIterator->next() )
+ {
+ // tdf#128018 - add column/row labels to the output
+ OUString aColRowLabel = mDocument.GetString(pIterator->get().aStart);
+ if (aColRowLabel.isEmpty())
+ {
+ if (mGroupedBy == BY_COLUMN)
+ aTemplate.setTemplate(ScResId(STR_COLUMN_LABEL_TEMPLATE));
+ else
+ aTemplate.setTemplate(ScResId(STR_ROW_LABEL_TEMPLATE));
+
+ aTemplate.applyNumber(u"%NUMBER%", pIterator->index() + 1);
+ aOutput.writeBoldString(aTemplate.getTemplate());
+ }
+ else
+ {
+ aOutput.writeBoldString(aColRowLabel);
+ }
+ aOutput.nextColumn();
+ }
+ aOutput.nextRow();
+ aOutput.resetColumn();
+ aOutput.push();
+
+ // Write calculation labels
+ for(sal_Int32 i = 0; lclCalcDefinitions[i].aFormula != nullptr; i++)
+ {
+ OUString aLabel(ScResId(lclCalcDefinitions[i].aCalculationNameId));
+ aOutput.writeString(aLabel);
+ aOutput.nextRow();
+ }
+ aOutput.nextColumn();
+
+ pIterator->reset();
+
+ for( ; pIterator->hasNext(); pIterator->next() )
+ {
+ aOutput.resetRow();
+
+ for(sal_Int32 i = 0; lclCalcDefinitions[i].aFormula != nullptr; i++)
+ {
+ aTemplate.setTemplate(lclCalcDefinitions[i].aFormula);
+ aTemplate.applyRange(u"%RANGE%", pIterator->get(), b3DAddress);
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextRow();
+ }
+ aOutput.nextColumn();
+ }
+
+ return ScRange(aOutput.mMinimumAddress, aOutput.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/ExponentialSmoothingDialog.cxx b/sc/source/ui/StatisticsDialogs/ExponentialSmoothingDialog.cxx
new file mode 100644
index 0000000000..1a87f5beb3
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/ExponentialSmoothingDialog.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <ExponentialSmoothingDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScExponentialSmoothingDialog::ScExponentialSmoothingDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData )
+ : ScStatisticsInputOutputDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/exponentialsmoothingdialog.ui",
+ "ExponentialSmoothingDialog")
+ , mxSmoothingFactor(m_xBuilder->weld_spin_button("smoothing-factor-spin"))
+{
+}
+
+ScExponentialSmoothingDialog::~ScExponentialSmoothingDialog()
+{
+}
+
+void ScExponentialSmoothingDialog::Close()
+{
+ DoClose( ScExponentialSmoothingDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScExponentialSmoothingDialog::GetUndoNameId()
+{
+ return STR_EXPONENTIAL_SMOOTHING_UNDO_NAME;
+}
+
+ScRange ScExponentialSmoothingDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter output(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ // Smoothing factor
+ double aSmoothingFactor = mxSmoothingFactor->get_value() / 100.0;
+
+ // Alpha
+ output.writeBoldString(ScResId(STR_LABEL_ALPHA));
+ output.nextRow();
+
+ // Alpha Value
+ ScAddress aSmoothingFactorAddress = output.current();
+ output.writeValue(aSmoothingFactor);
+ output.nextRow();
+
+ // Exponential Smoothing
+ output.push();
+
+ std::unique_ptr<DataRangeIterator> pIterator;
+ if (mGroupedBy == BY_COLUMN)
+ pIterator.reset(new DataRangeByColumnIterator(mInputRange));
+ else
+ pIterator.reset(new DataRangeByRowIterator(mInputRange));
+
+ for( ; pIterator->hasNext(); pIterator->next() )
+ {
+ output.resetRow();
+
+ ScRange aCurrentRange = pIterator->get();
+
+ // Write column label
+ if (mGroupedBy == BY_COLUMN)
+ aTemplate.setTemplate(ScResId(STR_COLUMN_LABEL_TEMPLATE));
+ else
+ aTemplate.setTemplate(ScResId(STR_ROW_LABEL_TEMPLATE));
+ aTemplate.applyNumber(u"%NUMBER%", pIterator->index() + 1);
+ output.writeBoldString(aTemplate.getTemplate());
+ output.nextRow();
+
+ // Initial value
+ if ((false))
+ {
+ aTemplate.setTemplate("=AVERAGE(%RANGE%)");
+ aTemplate.applyRange(u"%RANGE%", aCurrentRange);
+ output.writeFormula(aTemplate.getTemplate());
+ }
+ else
+ {
+ aTemplate.setTemplate("=%VAR%");
+ aTemplate.applyAddress(u"%VAR%", aCurrentRange.aStart);
+ output.writeFormula(aTemplate.getTemplate());
+ }
+
+ output.nextRow();
+
+ DataCellIterator aDataCellIterator = pIterator->iterateCells();
+
+ for (; aDataCellIterator.hasNext(); aDataCellIterator.next())
+ {
+ aTemplate.setTemplate("=%VALUE% * %PREVIOUS_INPUT% + (1 - %VALUE%) * %PREVIOUS_OUTPUT%");
+ aTemplate.applyAddress(u"%PREVIOUS_INPUT%", aDataCellIterator.get());
+ aTemplate.applyAddress(u"%PREVIOUS_OUTPUT%", output.current(0, -1));
+ aTemplate.applyAddress(u"%VALUE%", aSmoothingFactorAddress);
+
+ output.writeFormula(aTemplate.getTemplate());
+ output.nextRow();
+ }
+ output.nextColumn();
+ }
+
+ return ScRange (output.mMinimumAddress, output.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/FTestDialog.cxx b/sc/source/ui/StatisticsDialogs/FTestDialog.cxx
new file mode 100644
index 0000000000..76b2bade62
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/FTestDialog.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <FTestDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScFTestDialog::ScFTestDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScStatisticsTwoVariableDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/ttestdialog.ui", "TTestDialog" )
+{
+ m_xDialog->set_title(ScResId(STR_FTEST));
+}
+
+ScFTestDialog::~ScFTestDialog()
+{}
+
+void ScFTestDialog::Close()
+{
+ DoClose( ScFTestDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScFTestDialog::GetUndoNameId()
+{
+ return STR_FTEST_UNDO_NAME;
+}
+
+ScRange ScFTestDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ std::unique_ptr<DataRangeIterator> pVariable1Iterator;
+ if (mGroupedBy == BY_COLUMN)
+ pVariable1Iterator.reset(new DataRangeByColumnIterator(mVariable1Range));
+ else
+ pVariable1Iterator.reset(new DataRangeByRowIterator(mVariable1Range));
+
+ std::unique_ptr<DataRangeIterator> pVariable2Iterator;
+ if (mGroupedBy == BY_COLUMN)
+ pVariable2Iterator.reset(new DataRangeByColumnIterator(mVariable2Range));
+ else
+ pVariable2Iterator.reset(new DataRangeByRowIterator(mVariable2Range));
+
+ aTemplate.autoReplaceRange("%VARIABLE1_RANGE%", pVariable1Iterator->get());
+ aTemplate.autoReplaceRange("%VARIABLE2_RANGE%", pVariable2Iterator->get());
+
+ aOutput.writeBoldString(ScResId(STR_FTEST_UNDO_NAME));
+ aOutput.newLine();
+
+ // Alpha
+ aOutput.writeString(ScResId(STR_LABEL_ALPHA));
+ aOutput.nextColumn();
+ aOutput.writeValue(0.05);
+ aTemplate.autoReplaceAddress("%ALPHA%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.nextColumn();
+ aOutput.writeBoldString(ScResId(STR_VARIABLE_1_LABEL));
+ aOutput.nextColumn();
+ aOutput.writeBoldString(ScResId(STR_VARIABLE_2_LABEL));
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STRID_CALC_MEAN));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STRID_CALC_VARIANCE));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=VAR(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIABLE1_VARIANCE%", aOutput.current());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=VAR(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIABLE2_VARIANCE%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_OBSERVATIONS_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=COUNT(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIABLE1_OBSERVATIONS%", aOutput.current());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=COUNT(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIABLE2_OBSERVATIONS%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_ANOVA_LABEL_DF));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=%VARIABLE1_OBSERVATIONS% - 1");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIABLE1_DEGREE_OF_FREEDOM%", aOutput.current());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=%VARIABLE2_OBSERVATIONS% - 1");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIABLE2_DEGREE_OF_FREEDOM%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_ANOVA_LABEL_F));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=%VARIABLE1_VARIANCE% / %VARIABLE2_VARIANCE%");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%F_VALUE%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_FTEST_P_RIGHT_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=FDIST(%F_VALUE%; %VARIABLE1_DEGREE_OF_FREEDOM%; %VARIABLE2_DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%P_RIGHT_TAIL_VALUE%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_FTEST_F_CRITICAL_RIGHT_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=FINV(%ALPHA%; %VARIABLE1_DEGREE_OF_FREEDOM%; %VARIABLE2_DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_FTEST_P_LEFT_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=1 - %P_RIGHT_TAIL_VALUE%");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%P_LEFT_TAIL_VALUE%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_FTEST_F_CRITICAL_LEFT_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=FINV(1-%ALPHA%; %VARIABLE1_DEGREE_OF_FREEDOM%; %VARIABLE2_DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_FTEST_P_TWO_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=2*MIN(%P_RIGHT_TAIL_VALUE%; %P_LEFT_TAIL_VALUE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STR_FTEST_F_CRITICAL_TWO_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=FINV(1-(%ALPHA%/2); %VARIABLE1_DEGREE_OF_FREEDOM%; %VARIABLE2_DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=FINV(%ALPHA%/2; %VARIABLE1_DEGREE_OF_FREEDOM%; %VARIABLE2_DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+
+ return ScRange(aOutput.mMinimumAddress, aOutput.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/FourierAnalysisDialog.cxx b/sc/source/ui/StatisticsDialogs/FourierAnalysisDialog.cxx
new file mode 100644
index 0000000000..5a1e192ef3
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/FourierAnalysisDialog.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/.
+ *
+ */
+
+#include <docsh.hxx>
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <FourierAnalysisDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <o3tl/safeint.hxx>
+
+ScFourierAnalysisDialog::ScFourierAnalysisDialog(SfxBindings* pSfxBindings,
+ SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData)
+ : ScStatisticsInputOutputDialog(pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/fourieranalysisdialog.ui",
+ "FourierAnalysisDialog")
+ , maLabelAddr(ScAddress::INITIALIZE_INVALID)
+ , maActualInputRange(ScAddress::INITIALIZE_INVALID)
+ , mnLen(0)
+ , mfMinMag(0.0)
+ , mbUse3DAddresses(false)
+ , mbGroupedByColumn(true)
+ , mbWithLabels(false)
+ , mbInverse(false)
+ , mbPolar(false)
+ , mxWithLabelsCheckBox(m_xBuilder->weld_check_button("withlabels-check"))
+ , mxInverseCheckBox(m_xBuilder->weld_check_button("inverse-check"))
+ , mxPolarCheckBox(m_xBuilder->weld_check_button("polar-check"))
+ , mxMinMagnitudeField(m_xBuilder->weld_spin_button("minmagnitude-spin"))
+ , mxErrorMessage(m_xBuilder->weld_label("error-message"))
+{
+ m_xDialog->set_title(ScResId(STR_FOURIER_ANALYSIS));
+
+ mxWithLabelsCheckBox->connect_toggled(LINK(this, ScFourierAnalysisDialog, CheckBoxHdl));
+}
+
+ScFourierAnalysisDialog::~ScFourierAnalysisDialog() {}
+
+void ScFourierAnalysisDialog::Close()
+{
+ DoClose(ScFourierAnalysisDialogWrapper::GetChildWindowId());
+}
+
+TranslateId ScFourierAnalysisDialog::GetUndoNameId() { return STR_FOURIER_ANALYSIS_UNDO_NAME; }
+
+ScRange ScFourierAnalysisDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ getOptions();
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar(
+ formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+ aTemplate.autoReplaceUses3D(mbUse3DAddresses);
+
+ aOutput.writeBoldString(mbInverse ? ScResId(STR_INVERSE_FOURIER_TRANSFORM)
+ : ScResId(STR_FOURIER_TRANSFORM));
+ aOutput.newLine();
+ OUString aLabel;
+ getDataLabel(aLabel);
+ if (aLabel.startsWith("="))
+ aOutput.writeFormula(aLabel);
+ else
+ aOutput.writeString(aLabel);
+
+ aOutput.newLine();
+ // Components header
+ if (!mbPolar)
+ {
+ aOutput.writeString(ScResId(STR_REAL_PART));
+ aOutput.nextColumn();
+ aOutput.writeString(ScResId(STR_IMAGINARY_PART));
+ }
+ else
+ {
+ aOutput.writeString(ScResId(STR_MAGNITUDE_PART));
+ aOutput.nextColumn();
+ aOutput.writeString(ScResId(STR_PHASE_PART));
+ }
+
+ aOutput.newLine();
+ aTemplate.autoReplaceRange("%INPUTRANGE%", maActualInputRange);
+
+ OUString aFormula;
+ genFormula(aFormula);
+
+ aTemplate.setTemplate(aFormula);
+ aOutput.writeMatrixFormula(aTemplate.getTemplate(), 2, mnLen);
+
+ return ScRange(aOutput.mMinimumAddress, aOutput.mMaximumAddress);
+}
+
+bool ScFourierAnalysisDialog::InputRangesValid()
+{
+ if (!mInputRange.IsValid())
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_INVALID_INPUT_RANGE));
+ return false;
+ }
+
+ if (!mOutputAddress.IsValid())
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_INVALID_OUTPUT_ADDR));
+ return false;
+ }
+
+ mInputRange.PutInOrder();
+
+ mbGroupedByColumn = mGroupedBy == BY_COLUMN;
+ mbWithLabels = mxWithLabelsCheckBox->get_active();
+
+ mbUse3DAddresses = mInputRange.aStart.Tab() != mOutputAddress.Tab();
+
+ SCSIZE nRows = mInputRange.aEnd.Row() - mInputRange.aStart.Row() + 1;
+ SCSIZE nCols = mInputRange.aEnd.Col() - mInputRange.aStart.Col() + 1;
+
+ SCSIZE nLen = mbGroupedByColumn ? nRows : nCols;
+ SCSIZE nComponents = mbGroupedByColumn ? nCols : nRows;
+
+ if (nComponents > 2)
+ {
+ OUString aMsg = mbGroupedByColumn ? ScResId(STR_MESSAGE_INVALID_NUMCOLS)
+ : ScResId(STR_MESSAGE_INVALID_NUMROWS);
+ mxErrorMessage->set_label(aMsg);
+ return false;
+ }
+
+ if (mbWithLabels && nLen < 2)
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_NODATA_IN_RANGE));
+ return false;
+ }
+
+ // Include space for writing the title, label and Real/Imaginary/Magnitude/Phase heading.
+ SCSIZE nLastOutputRow = mOutputAddress.Row() + nLen + 2;
+ if (mbWithLabels)
+ --nLastOutputRow;
+
+ if (nLastOutputRow > o3tl::make_unsigned(mDocument.MaxRow()))
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_OUTPUT_TOO_LONG));
+ return false;
+ }
+
+ ScAddress aActualStart(mInputRange.aStart);
+
+ if (mbWithLabels)
+ {
+ if (mbGroupedByColumn)
+ aActualStart.IncRow();
+ else
+ aActualStart.IncCol();
+
+ if (nComponents == 1)
+ maLabelAddr = mInputRange.aStart;
+ else
+ mbWithLabels = false;
+
+ mnLen = nLen - 1;
+ }
+ else
+ {
+ mnLen = nLen;
+ }
+
+ maActualInputRange = ScRange(aActualStart, mInputRange.aEnd);
+ mxErrorMessage->set_label("");
+
+ return true;
+}
+
+void ScFourierAnalysisDialog::getOptions()
+{
+ mbInverse = mxInverseCheckBox->get_active();
+ mbPolar = mxPolarCheckBox->get_active();
+
+ sal_Int32 nDeciBels = static_cast<sal_Int32>(mxMinMagnitudeField->get_value());
+ if (nDeciBels <= -150)
+ mfMinMag = 0.0;
+ else
+ mfMinMag = pow(10.0, static_cast<double>(nDeciBels) / 10.0);
+}
+
+void ScFourierAnalysisDialog::getDataLabel(OUString& rLabel)
+{
+ if (mbWithLabels)
+ {
+ rLabel = "="
+ + maLabelAddr.Format(mbUse3DAddresses ? ScRefFlags::ADDR_ABS_3D
+ : ScRefFlags::ADDR_ABS,
+ &mDocument, mAddressDetails);
+
+ return;
+ }
+
+ OUString aDataSrc(mInputRange.Format(
+ mDocument, mbUse3DAddresses ? ScRefFlags::RANGE_ABS_3D : ScRefFlags::RANGE_ABS,
+ mAddressDetails));
+
+ rLabel = ScResId(STR_INPUT_DATA_RANGE) + " : " + aDataSrc;
+ return;
+}
+
+void ScFourierAnalysisDialog::genFormula(OUString& rFormula)
+{
+ static constexpr OUString aSep(u";"_ustr);
+
+ if (!mbPolar)
+ {
+ rFormula = "FOURIER(%INPUTRANGE%;" + OUString::boolean(mbGroupedByColumn) + aSep
+ + OUString::boolean(mbInverse) + ")";
+ return;
+ }
+
+ rFormula = "FOURIER(%INPUTRANGE%;" + OUString::boolean(mbGroupedByColumn) + aSep
+ + OUString::boolean(mbInverse) + ";true;" + OUString::number(mfMinMag) + ")";
+}
+
+IMPL_LINK_NOARG(ScFourierAnalysisDialog, CheckBoxHdl, weld::Toggleable&, void)
+{
+ ValidateDialogInput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/MatrixComparisonGenerator.cxx b/sc/source/ui/StatisticsDialogs/MatrixComparisonGenerator.cxx
new file mode 100644
index 0000000000..f2059ff822
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/MatrixComparisonGenerator.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/.
+ *
+ */
+
+#include <rangelst.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <MatrixComparisonGenerator.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+namespace
+{
+ void lclWriteCorrelationFormulas(
+ AddressWalkerWriter& aOutput, FormulaTemplate& aTemplate,
+ const ScRangeList& aRangeList, const OUString& aTemplateString)
+ {
+ for (size_t i = 0; i < aRangeList.size(); i++)
+ {
+ aOutput.resetRow();
+ for (size_t j = 0; j < aRangeList.size(); j++)
+ {
+ if (j >= i)
+ {
+ aTemplate.setTemplate(aTemplateString);
+ aTemplate.applyRange(u"%VAR1%", aRangeList[i]);
+ aTemplate.applyRange(u"%VAR2%", aRangeList[j]);
+ aOutput.writeFormula(aTemplate.getTemplate());
+ }
+ aOutput.nextRow();
+ }
+ aOutput.nextColumn();
+ }
+ }
+}
+
+ScMatrixComparisonGenerator::ScMatrixComparisonGenerator(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData,
+ const OUString& rUiXmlDescription,
+ const OUString& rID)
+ : ScStatisticsInputOutputDialog(pSfxBindings, pChildWindow, pParent, rViewData, rUiXmlDescription, rID)
+{}
+
+ScMatrixComparisonGenerator::~ScMatrixComparisonGenerator()
+{}
+
+TranslateId ScMatrixComparisonGenerator::GetUndoNameId()
+{
+ return STR_CORRELATION_UNDO_NAME;
+}
+
+ScRange ScMatrixComparisonGenerator::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter output(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ SCTAB inTab = mInputRange.aStart.Tab();
+
+ ScRangeList aRangeList = (mGroupedBy == BY_COLUMN) ?
+ MakeColumnRangeList(inTab, mInputRange.aStart, mInputRange.aEnd) :
+ MakeRowRangeList(inTab, mInputRange.aStart, mInputRange.aEnd);
+
+ // labels
+ output.writeString(getLabel());
+ output.nextColumn();
+
+ static constexpr OUString strWildcardNumber(u"%NUMBER%"_ustr);
+
+ // write labels to columns
+ for (size_t i = 0; i < aRangeList.size(); i++)
+ {
+ if (mGroupedBy == BY_COLUMN)
+ aTemplate.setTemplate(ScResId(STR_COLUMN_LABEL_TEMPLATE));
+ else
+ aTemplate.setTemplate(ScResId(STR_ROW_LABEL_TEMPLATE));
+
+ aTemplate.applyNumber(strWildcardNumber, i + 1);
+ output.writeString(aTemplate.getTemplate());
+ output.nextColumn();
+ }
+
+ // write labels to rows
+ output.resetColumn();
+ output.nextRow();
+ for (size_t i = 0; i < aRangeList.size(); i++)
+ {
+ if (mGroupedBy == BY_COLUMN)
+ aTemplate.setTemplate(ScResId(STR_COLUMN_LABEL_TEMPLATE));
+ else
+ aTemplate.setTemplate(ScResId(STR_ROW_LABEL_TEMPLATE));
+
+ aTemplate.applyNumber(strWildcardNumber, i + 1);
+ output.writeString(aTemplate.getTemplate());
+ output.nextRow();
+ }
+
+ // write correlation formulas
+ output.reset();
+ output.push(1, 1);
+
+ lclWriteCorrelationFormulas(output, aTemplate, aRangeList, getTemplate());
+
+ return ScRange(output.mMinimumAddress, output.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx b/sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx
new file mode 100644
index 0000000000..9d990ed5a4
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/MovingAverageDialog.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/.
+ *
+ */
+
+#include <memory>
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <MovingAverageDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScMovingAverageDialog::ScMovingAverageDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData )
+ : ScStatisticsInputOutputDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/movingaveragedialog.ui",
+ "MovingAverageDialog")
+ , mxTrimRangeCheck(m_xBuilder->weld_check_button("trimrange-check"))
+ , mxIntervalSpin(m_xBuilder->weld_spin_button("interval-spin"))
+{
+}
+
+ScMovingAverageDialog::~ScMovingAverageDialog()
+{
+}
+
+void ScMovingAverageDialog::Close()
+{
+ DoClose( ScMovingAverageDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScMovingAverageDialog::GetUndoNameId()
+{
+ return STR_MOVING_AVERAGE_UNDO_NAME;
+}
+
+ScRange ScMovingAverageDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter output(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ if (mxTrimRangeCheck->get_active())
+ mDocument.GetDataAreaSubrange(mInputRange);
+
+ std::unique_ptr<DataRangeIterator> pIterator;
+ if (mGroupedBy == BY_COLUMN)
+ pIterator.reset(new DataRangeByColumnIterator(mInputRange));
+ else
+ pIterator.reset(new DataRangeByRowIterator(mInputRange));
+
+ sal_Int32 aIntervalSize = mxIntervalSpin->get_value();
+ const bool aCentral = true; //to-do add support to change this to the dialog
+
+ for( ; pIterator->hasNext(); pIterator->next() )
+ {
+ output.resetRow();
+
+ // Write label
+ if (mGroupedBy == BY_COLUMN)
+ aTemplate.setTemplate(ScResId(STR_COLUMN_LABEL_TEMPLATE));
+ else
+ aTemplate.setTemplate(ScResId(STR_ROW_LABEL_TEMPLATE));
+
+ aTemplate.applyNumber(u"%NUMBER%", pIterator->index() + 1);
+ output.writeBoldString(aTemplate.getTemplate());
+ output.nextRow();
+
+ DataCellIterator aDataCellIterator = pIterator->iterateCells();
+ std::vector<OUString> aFormulas;
+
+ for (; aDataCellIterator.hasNext(); aDataCellIterator.next())
+ {
+ ScAddress aIntervalStart;
+ ScAddress aIntervalEnd;
+
+ if (aCentral)
+ {
+ sal_Int32 aHalf = aIntervalSize / 2;
+ sal_Int32 aHalfRemainder = aIntervalSize % 2;
+ aIntervalStart = aDataCellIterator.getRelative(-aHalf);
+ aIntervalEnd = aDataCellIterator.getRelative(aHalf - 1 + aHalfRemainder);
+ }
+ else
+ {
+ aIntervalStart = aDataCellIterator.getRelative(-aIntervalSize);
+ aIntervalEnd = aDataCellIterator.getRelative(0);
+ }
+
+ if(aIntervalStart.IsValid() && aIntervalEnd.IsValid())
+ {
+ aTemplate.setTemplate("=AVERAGE(%RANGE%)");
+ aTemplate.applyRange(u"%RANGE%", ScRange(aIntervalStart, aIntervalEnd));
+ aFormulas.push_back(aTemplate.getTemplate());
+ }
+ else
+ {
+ aFormulas.push_back("=#N/A");
+ }
+ }
+
+ output.writeFormulas(aFormulas);
+ output.nextColumn();
+ }
+ return ScRange(output.mMinimumAddress, output.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx b/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx
new file mode 100644
index 0000000000..38894160ef
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx
@@ -0,0 +1,500 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <svl/undo.hxx>
+#include <rtl/math.hxx>
+#include <osl/time.h>
+
+#include <rangelst.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <reffact.hxx>
+#include <docfunc.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+#include <random>
+
+#include <RandomNumberGeneratorDialog.hxx>
+
+namespace
+{
+
+const sal_Int64 DIST_UNIFORM = 0;
+const sal_Int64 DIST_NORMAL = 1;
+const sal_Int64 DIST_CAUCHY = 2;
+const sal_Int64 DIST_BERNOULLI = 3;
+const sal_Int64 DIST_BINOMIAL = 4;
+const sal_Int64 DIST_CHI_SQUARED = 5;
+const sal_Int64 DIST_GEOMETRIC = 6;
+const sal_Int64 DIST_NEGATIVE_BINOMIAL = 7;
+const sal_Int64 DIST_UNIFORM_INTEGER = 8;
+const sal_Int64 DIST_POISSON = 9;
+
+const sal_Int64 PRECISION = 10000;
+const sal_Int64 DIGITS = 4;
+
+}
+
+ScRandomNumberGeneratorDialog::ScRandomNumberGeneratorDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent,
+ "modules/scalc/ui/randomnumbergenerator.ui",
+ "RandomNumberGeneratorDialog")
+ , mrViewData(rViewData)
+ , mrDoc(rViewData.GetDocument())
+ , mbDialogLostFocus(false)
+ , mxInputRangeText(m_xBuilder->weld_label("cell-range-label"))
+ , mxInputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("cell-range-edit")))
+ , mxInputRangeButton(new formula::RefButton(m_xBuilder->weld_button("cell-range-button")))
+ , mxDistributionCombo(m_xBuilder->weld_combo_box("distribution-combo"))
+ , mxParameter1Text(m_xBuilder->weld_label("parameter1-label"))
+ , mxParameter1Value(m_xBuilder->weld_spin_button("parameter1-spin"))
+ , mxParameter2Text(m_xBuilder->weld_label("parameter2-label"))
+ , mxParameter2Value(m_xBuilder->weld_spin_button("parameter2-spin"))
+ , mxSeed(m_xBuilder->weld_spin_button("seed-spin"))
+ , mxEnableSeed(m_xBuilder->weld_check_button("enable-seed-check"))
+ , mxDecimalPlaces(m_xBuilder->weld_spin_button("decimal-places-spin"))
+ , mxEnableRounding(m_xBuilder->weld_check_button("enable-rounding-check"))
+ , mxButtonApply(m_xBuilder->weld_button("apply"))
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonClose(m_xBuilder->weld_button("close"))
+{
+ mxInputRangeEdit->SetReferences(this, mxInputRangeText.get());
+ mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
+
+ Init();
+ GetRangeFromSelection();
+}
+
+ScRandomNumberGeneratorDialog::~ScRandomNumberGeneratorDialog()
+{
+}
+
+void ScRandomNumberGeneratorDialog::Init()
+{
+ mxButtonOk->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, OkClicked ) );
+ mxButtonClose->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, CloseClicked ) );
+ mxButtonApply->connect_clicked( LINK( this, ScRandomNumberGeneratorDialog, ApplyClicked ) );
+
+ mxInputRangeEdit->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog, GetEditFocusHandler ));
+ mxInputRangeButton->SetGetFocusHdl(LINK( this, ScRandomNumberGeneratorDialog, GetButtonFocusHandler ));
+
+ mxInputRangeEdit->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog, LoseEditFocusHandler ));
+ mxInputRangeButton->SetLoseFocusHdl (LINK( this, ScRandomNumberGeneratorDialog, LoseButtonFocusHandler ));
+
+ mxInputRangeEdit->SetModifyHdl( LINK( this, ScRandomNumberGeneratorDialog, InputRangeModified ));
+ mxParameter1Value->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog, Parameter1ValueModified ));
+ mxParameter2Value->connect_value_changed( LINK( this, ScRandomNumberGeneratorDialog, Parameter2ValueModified ));
+
+ mxDistributionCombo->connect_changed( LINK( this, ScRandomNumberGeneratorDialog, DistributionChanged ));
+
+ mxEnableSeed->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog, CheckChanged ));
+ mxEnableRounding->connect_toggled( LINK( this, ScRandomNumberGeneratorDialog, CheckChanged ));
+
+ DistributionChanged(*mxDistributionCombo);
+ CheckChanged(*mxEnableSeed);
+}
+
+void ScRandomNumberGeneratorDialog::GetRangeFromSelection()
+{
+ mrViewData.GetSimpleArea(maInputRange);
+ OUString aCurrentString(maInputRange.Format(mrDoc, ScRefFlags::RANGE_ABS_3D, mrDoc.GetAddressConvention()));
+ mxInputRangeEdit->SetText( aCurrentString );
+}
+
+void ScRandomNumberGeneratorDialog::SetActive()
+{
+ if ( mbDialogLostFocus )
+ {
+ mbDialogLostFocus = false;
+ if( mxInputRangeEdit )
+ mxInputRangeEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScRandomNumberGeneratorDialog::Close()
+{
+ DoClose( ScRandomNumberGeneratorDialogWrapper::GetChildWindowId() );
+}
+
+void ScRandomNumberGeneratorDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDoc )
+{
+ if (!mxInputRangeEdit->GetWidget()->get_sensitive())
+ return;
+
+ if ( rReferenceRange.aStart != rReferenceRange.aEnd )
+ RefInputStart(mxInputRangeEdit.get());
+
+ maInputRange = rReferenceRange;
+
+ OUString aReferenceString(maInputRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ mxInputRangeEdit->SetRefString( aReferenceString );
+
+ mxButtonApply->set_sensitive(true);
+ mxButtonOk->set_sensitive(true);
+}
+
+void ScRandomNumberGeneratorDialog::SelectGeneratorAndGenerateNumbers()
+{
+ if (!maInputRange.IsValid())
+ return;
+
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+
+ sal_uInt32 seedValue;
+
+ if( mxEnableSeed->get_active() )
+ {
+ seedValue = mxSeed->get_value();
+ }
+ else
+ {
+ TimeValue now;
+ osl_getSystemTime(&now);
+ seedValue = now.Nanosec;
+ }
+
+ std::mt19937 seed(seedValue);
+
+ sal_Int64 parameterInteger1 = mxParameter1Value->get_value();
+ sal_Int64 parameterInteger2 = mxParameter2Value->get_value();
+
+ double parameter1 = parameterInteger1 / static_cast<double>(PRECISION);
+ double parameter2 = parameterInteger2 / static_cast<double>(PRECISION);
+
+ std::optional<sal_Int8> aDecimalPlaces;
+ if (mxEnableRounding->get_active())
+ {
+ aDecimalPlaces = static_cast<sal_Int8>(mxDecimalPlaces->get_value());
+ }
+
+ switch(aSelectedId)
+ {
+ case DIST_UNIFORM:
+ {
+ std::uniform_real_distribution<> distribution(parameter1, parameter2);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_UNIFORM_REAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_UNIFORM_INTEGER:
+ {
+ std::uniform_int_distribution<sal_Int64> distribution(parameterInteger1, parameterInteger2);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_UNIFORM_INTEGER, aDecimalPlaces);
+ break;
+ }
+ case DIST_NORMAL:
+ {
+ std::normal_distribution<> distribution(parameter1, parameter2);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_NORMAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_CAUCHY:
+ {
+ std::cauchy_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_CAUCHY, aDecimalPlaces);
+ break;
+ }
+ case DIST_BERNOULLI:
+ {
+ std::bernoulli_distribution distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_BERNOULLI, aDecimalPlaces);
+ break;
+ }
+ case DIST_BINOMIAL:
+ {
+ std::binomial_distribution<> distribution(parameterInteger2, parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_BINOMIAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_CHI_SQUARED:
+ {
+ std::chi_squared_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_CHI_SQUARED, aDecimalPlaces);
+ break;
+ }
+ case DIST_GEOMETRIC:
+ {
+ std::geometric_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_GEOMETRIC, aDecimalPlaces);
+ break;
+ }
+ case DIST_NEGATIVE_BINOMIAL:
+ {
+ std::negative_binomial_distribution<> distribution(parameterInteger2, parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_NEGATIVE_BINOMIAL, aDecimalPlaces);
+ break;
+ }
+ case DIST_POISSON:
+ {
+ std::poisson_distribution<> distribution(parameter1);
+ auto rng = std::bind(distribution, seed);
+ GenerateNumbers(rng, STR_DISTRIBUTION_POISSON, aDecimalPlaces);
+ break;
+ }
+ }
+}
+
+template<class RNG>
+void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG& randomGenerator, TranslateId pDistributionStringId, std::optional<sal_Int8> aDecimalPlaces)
+{
+ OUString aUndo = ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE);
+ OUString aDistributionName = ScResId(pDistributionStringId);
+ aUndo = aUndo.replaceAll("%1", aDistributionName);
+
+ ScDocShell* pDocShell = mrViewData.GetDocShell();
+ SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ SCROW nRowStart = maInputRange.aStart.Row();
+ SCROW nRowEnd = maInputRange.aEnd.Row();
+ SCCOL nColStart = maInputRange.aStart.Col();
+ SCCOL nColEnd = maInputRange.aEnd.Col();
+ SCTAB nTabStart = maInputRange.aStart.Tab();
+ SCTAB nTabEnd = maInputRange.aEnd.Tab();
+
+ std::vector<double> aVals;
+ aVals.reserve(nRowEnd - nRowStart + 1);
+
+ for (SCROW nTab = nTabStart; nTab <= nTabEnd; ++nTab)
+ {
+ for (SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol)
+ {
+ aVals.clear();
+
+ ScAddress aPos(nCol, nRowStart, nTab);
+ for (SCROW nRow = nRowStart; nRow <= nRowEnd; ++nRow)
+ {
+
+ if (aDecimalPlaces)
+ aVals.push_back(rtl::math::round(randomGenerator(), *aDecimalPlaces));
+ else
+ aVals.push_back(randomGenerator());
+ }
+
+ pDocShell->GetDocFunc().SetValueCells(aPos, aVals, true);
+ }
+ }
+
+ pUndoManager->LeaveListAction();
+
+ pDocShell->PostPaint( maInputRange, PaintPartFlags::Grid );
+}
+
+IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog, OkClicked, weld::Button&, void )
+{
+ ApplyClicked(*mxButtonApply);
+ CloseClicked(*mxButtonClose);
+}
+
+IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog, ApplyClicked, weld::Button&, void )
+{
+ SelectGeneratorAndGenerateNumbers();
+}
+
+IMPL_LINK_NOARG( ScRandomNumberGeneratorDialog, CloseClicked, weld::Button&, void )
+{
+ response(RET_CLOSE);
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, GetEditFocusHandler, formula::RefEdit&, void)
+{
+ mxInputRangeEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, GetButtonFocusHandler, formula::RefButton&, void)
+{
+ mxInputRangeEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, InputRangeModified, formula::RefEdit&, void)
+{
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxInputRangeEdit->GetText(), mrDoc);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ maInputRange = *pRange;
+ mxButtonApply->set_sensitive(true);
+ mxButtonOk->set_sensitive(true);
+ // Highlight the resulting range.
+ mxInputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ maInputRange = ScRange( ScAddress::INITIALIZE_INVALID);
+ mxButtonApply->set_sensitive(false);
+ mxButtonOk->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, Parameter1ValueModified, weld::SpinButton&, void)
+{
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+ if (aSelectedId == DIST_UNIFORM ||
+ aSelectedId == DIST_UNIFORM_INTEGER)
+ {
+ sal_Int64 min = mxParameter1Value->get_value();
+ sal_Int64 max = mxParameter2Value->get_value();
+ if(min > max)
+ {
+ mxParameter2Value->set_value(min);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, Parameter2ValueModified, weld::SpinButton&, void)
+{
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+ if (aSelectedId == DIST_UNIFORM ||
+ aSelectedId == DIST_UNIFORM_INTEGER)
+ {
+ sal_Int64 min = mxParameter1Value->get_value();
+ sal_Int64 max = mxParameter2Value->get_value();
+ if(min > max)
+ {
+ mxParameter1Value->set_value(max);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, CheckChanged, weld::Toggleable&, void)
+{
+ mxSeed->set_sensitive(mxEnableSeed->get_active());
+ mxDecimalPlaces->set_sensitive(mxEnableRounding->get_active());
+}
+
+IMPL_LINK_NOARG(ScRandomNumberGeneratorDialog, DistributionChanged, weld::ComboBox&, void)
+{
+ sal_Int64 aSelectedId = mxDistributionCombo->get_active_id().toInt64();
+
+ mxParameter1Value->set_range(SAL_MIN_INT32, SAL_MAX_INT32);
+ mxParameter2Value->set_range(SAL_MIN_INT32, SAL_MAX_INT32);
+
+ mxParameter1Value->set_digits(DIGITS);
+ mxParameter1Value->set_increments(PRECISION, PRECISION * 10);
+
+ mxParameter2Value->set_digits(DIGITS);
+ mxParameter2Value->set_increments(PRECISION, PRECISION * 10);
+
+ switch(aSelectedId)
+ {
+ case DIST_UNIFORM:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM));
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM));
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_UNIFORM_INTEGER:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MINIMUM));
+ mxParameter1Value->set_digits(0);
+ mxParameter1Value->set_increments(1, 10);
+
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_MAXIMUM));
+ mxParameter2Value->set_digits(0);
+ mxParameter2Value->set_increments(1, 10);
+
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_NORMAL:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MEAN));
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_DEVIATION));
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_CAUCHY:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_MEDIAN));
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_SIGMA));
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_BERNOULLI:
+ case DIST_GEOMETRIC:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY));
+ mxParameter1Value->set_range(0, PRECISION);
+ mxParameter1Value->set_increments(1000, 10000);
+
+ mxParameter2Text->hide();
+ mxParameter2Value->hide();
+ break;
+ }
+ case DIST_BINOMIAL:
+ case DIST_NEGATIVE_BINOMIAL:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_PROBABILITY));
+ mxParameter1Value->set_range(0, PRECISION);
+ mxParameter1Value->set_increments(1000, 10000);
+
+ mxParameter2Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NUMBER_OF_TRIALS));
+ mxParameter2Value->set_digits(0);
+ mxParameter2Value->set_increments(1, 10);
+ mxParameter2Value->set_min(0);
+
+ mxParameter2Text->show();
+ mxParameter2Value->show();
+ break;
+ }
+ case DIST_CHI_SQUARED:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_STANDARD_NU_VALUE));
+
+ mxParameter2Text->hide();
+ mxParameter2Value->hide();
+ break;
+ }
+ case DIST_POISSON:
+ {
+ mxParameter1Text->set_label(ScResId(STR_RNG_PARAMETER_MEAN));
+ mxParameter1Value->set_value(PRECISION);
+ mxParameter1Value->set_increments(1000, 10000);
+ mxParameter1Value->set_min(1000);
+ mxParameter2Text->hide();
+ mxParameter2Value->hide();
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/RegressionDialog.cxx b/sc/source/ui/StatisticsDialogs/RegressionDialog.cxx
new file mode 100644
index 0000000000..547866cf01
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/RegressionDialog.cxx
@@ -0,0 +1,695 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <document.hxx>
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <RegressionDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+/*
+ Some regression basics
+ ----------------------
+
+ 1. Linear regression fits using data, a linear function between the dependent variable and the independent variable(s).
+ The basic form of this function is :-
+
+ y = b + m_1*x_1 + m_2*x_2 + ... + m_k*x_k
+
+ where y is the dependent variable
+ x_1, x_2, ..., x_k are the k independent variables
+ b is the intercept
+ m_1, m_2, ..., m_k are the slopes corresponding to the variables x_1, x_2, ..., x_k respectively.
+
+
+ This equation for n observations can be compactly written using matrices as :-
+
+ y = X*A
+
+ where y is the n dimensional column vector containing dependent variable observations.
+ where X is matrix of shape n*(k+1) where a row looks like [ 1 x_1 x_2 ... x_k ]
+ A is the k+1 dimensional column vector [ b m_1 m_2 ... m_k ]
+
+ Calc formula LINEST(Y_array ; X_array) can be used to compute all entries in "A" along with many other statistics.
+
+
+ 2. Logarithmic regression is basically used to find a linear function between the dependent variable and
+ the natural logarithm of the independent variable(s).
+ So the basic form of this functions is :-
+
+ y = b + m_1*ln(x_1) + m_2*ln(x_2) + ... + m_k*ln(x_k)
+
+ This can be again written in a compact matrix form for n observations.
+
+ y = ln(X)*A
+
+ where y is the n dimensional column vector containing dependent variable observations.
+ where X is matrix of shape n*(k+1) where a row looks like [ e x_1 x_2 ... x_k ]
+ A is the k+1 dimensional column vector [ b m_1 m_2 ... m_k ]
+
+ To estimate A, we use the formula =LINEST(Y_array ; LN(X_array))
+
+
+ 3. Power regression is used to fit the following model :-
+
+ y = b * (x_1 ^ m_1) * (x_2 ^ m_2) * ... * (x_k ^ m_k)
+
+ To reduce this to a linear function(so that we can still use LINEST()), we take natural logarithm on both sides
+
+ ln(y) = c + m_1*ln(x_1) + m_2*ln(x_2) + ... + m_k*ln(x_k) ; where c = ln(b)
+
+
+ This again can be written compactly in matrix form as :-
+
+ ln(y) = ln(X)*A
+
+ where y is the n dimensional column vector containing dependent variable observations.
+ where X is matrix of shape n*(k+1) where a row looks like [ e x_1 x_2 ... x_k ]
+ A is the k+1 dimensional column vector [ c m_1 m_2 ... m_k ]
+
+ To estimate A, we use the formula =LINEST(LN(Y_array) ; LN(X_array))
+
+ Once we get A, to get back y from x's we use the formula :-
+
+ y = exp( ln(X)*A )
+
+
+
+ Some references for computing confidence interval for the regression coefficients :-
+
+ [1] https://en.wikipedia.org/wiki/Student%27s_t-test#Slope_of_a_regression_line
+ [2] https://en.wikipedia.org/wiki/Simple_linear_regression#Normality_assumption
+ [3] https://onlinecourses.science.psu.edu/stat414/node/280
+
+ */
+
+namespace
+{
+ enum class ScRegType {
+ LINEAR,
+ LOGARITHMIC,
+ POWER
+ };
+
+ const TranslateId constRegressionModel[] =
+ {
+ STR_LABEL_LINEAR,
+ STR_LABEL_LOGARITHMIC,
+ STR_LABEL_POWER
+ };
+
+ OUString constTemplateLINEST[] =
+ {
+ "=LINEST(%VARIABLE2_RANGE% ; %VARIABLE1_RANGE% ; %CALC_INTERCEPT% ; TRUE)",
+ "=LINEST(%VARIABLE2_RANGE% ; LN(%VARIABLE1_RANGE%) ; %CALC_INTERCEPT% ; TRUE)",
+ "=LINEST(LN(%VARIABLE2_RANGE%) ; LN(%VARIABLE1_RANGE%) ; %CALC_INTERCEPT% ; TRUE)"
+ };
+
+ OUString constRegressionFormula[] =
+ {
+ "=MMULT(%XDATAMATRIX_RANGE% ; %SLOPES_RANGE%) + %INTERCEPT_ADDR%",
+ "=MMULT(LN(%XDATAMATRIX_RANGE%) ; %SLOPES_RANGE%) + %INTERCEPT_ADDR%",
+ "=EXP(MMULT(LN(%XDATAMATRIX_RANGE%) ; %SLOPES_RANGE%) + %INTERCEPT_ADDR%)"
+ };
+
+} // end anonymous namespace
+
+static size_t lcl_GetNumRowsColsInRange(const ScRange& rRange, bool bRows)
+{
+ if (bRows)
+ return rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+
+ return rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+}
+
+ScRegressionDialog::ScRegressionDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData )
+ : ScStatisticsTwoVariableDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/regressiondialog.ui", "RegressionDialog")
+ , mbUnivariate(true)
+ , mnNumIndependentVars(1)
+ , mnNumObservations(0)
+ , mbUse3DAddresses(false)
+ , mbCalcIntercept(true)
+ , mxWithLabelsCheckBox(m_xBuilder->weld_check_button("withlabels-check"))
+ , mxLinearRadioButton(m_xBuilder->weld_radio_button("linear-radio"))
+ , mxLogarithmicRadioButton(m_xBuilder->weld_radio_button("logarithmic-radio"))
+ , mxErrorMessage(m_xBuilder->weld_label("error-message"))
+ , mxConfidenceLevelField(m_xBuilder->weld_spin_button("confidencelevel-spin"))
+ , mxCalcResidualsCheckBox(m_xBuilder->weld_check_button("calcresiduals-check"))
+ , mxNoInterceptCheckBox(m_xBuilder->weld_check_button("nointercept-check"))
+{
+ mxWithLabelsCheckBox->connect_toggled(LINK(this, ScRegressionDialog, CheckBoxHdl));
+ mxConfidenceLevelField->connect_value_changed(LINK(this, ScRegressionDialog, NumericFieldHdl));
+}
+
+ScRegressionDialog::~ScRegressionDialog()
+{
+}
+
+void ScRegressionDialog::Close()
+{
+ DoClose(ScRegressionDialogWrapper::GetChildWindowId());
+}
+
+TranslateId ScRegressionDialog::GetUndoNameId()
+{
+ return STR_REGRESSION_UNDO_NAME;
+}
+
+ScRange ScRegressionDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+ aTemplate.autoReplaceUses3D(mbUse3DAddresses);
+ mbCalcIntercept = !mxNoInterceptCheckBox->get_active();
+
+ // max col of our output should account for
+ // 1. constant term column,
+ // 2. mnNumIndependentVars columns
+ // 3. Actual Y column
+ // 4. Predicted Y column
+ // 5. Residual Column
+ SCCOL nOutputMaxCol = mOutputAddress.Col() + mnNumIndependentVars + 3;
+
+ ScRange aXDataRange(GetDataRange(mVariable1Range));
+ ScRange aYDataRange(GetDataRange(mVariable2Range));
+
+ aTemplate.autoReplaceRange("%VARIABLE1_RANGE%", aXDataRange);
+ aTemplate.autoReplaceRange("%VARIABLE2_RANGE%", aYDataRange);
+ size_t nRegressionIndex = GetRegressionTypeIndex();
+ ScRegType eRegType = static_cast<ScRegType>(nRegressionIndex);
+ bool bTakeLogX = eRegType == ScRegType::LOGARITHMIC || eRegType == ScRegType::POWER;
+
+ WriteRawRegressionResults(aOutput, aTemplate, nRegressionIndex);
+ WriteRegressionStatistics(aOutput, aTemplate);
+ WriteRegressionANOVAResults(aOutput, aTemplate);
+ WriteRegressionEstimatesWithCI(aOutput, aTemplate, bTakeLogX);
+ if (mxCalcResidualsCheckBox->get_active())
+ WritePredictionsWithResiduals(aOutput, aTemplate, nRegressionIndex);
+
+ ScAddress aMaxAddress(aOutput.mMaximumAddress);
+ aMaxAddress.SetCol(std::max(aMaxAddress.Col(), nOutputMaxCol));
+ return ScRange(aOutput.mMinimumAddress, aMaxAddress);
+}
+
+bool ScRegressionDialog::InputRangesValid()
+{
+ if (!mVariable1Range.IsValid())
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_XINVALID_RANGE));
+ return false;
+ }
+
+ if (!mVariable2Range.IsValid())
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_YINVALID_RANGE));
+ return false;
+ }
+
+ if (!mOutputAddress.IsValid())
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_INVALID_OUTPUT_ADDR));
+ return false;
+ }
+
+ {
+ double fConfidenceLevel = mxConfidenceLevelField->get_value();
+ if ( fConfidenceLevel <= 0.0 || fConfidenceLevel >= 100.0 )
+ {
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_INVALID_CONFIDENCE_LEVEL));
+ return false;
+ }
+ }
+
+ mVariable1Range.PutInOrder();
+ mVariable2Range.PutInOrder();
+
+ bool bGroupedByColumn = mGroupedBy == BY_COLUMN;
+
+ bool bYHasSingleDim = (
+ (bGroupedByColumn &&
+ mVariable2Range.aStart.Col() == mVariable2Range.aEnd.Col()) ||
+ (!bGroupedByColumn &&
+ mVariable2Range.aStart.Row() == mVariable2Range.aEnd.Row()));
+
+ if (!bYHasSingleDim)
+ {
+ if (bGroupedByColumn)
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_YVARIABLE_MULTI_COLUMN));
+ else
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_YVARIABLE_MULTI_ROW));
+ return false;
+ }
+
+ bool bWithLabels = mxWithLabelsCheckBox->get_active();
+
+ size_t nYObs = lcl_GetNumRowsColsInRange(mVariable2Range, bGroupedByColumn);
+ size_t nNumXVars = lcl_GetNumRowsColsInRange(mVariable1Range, !bGroupedByColumn);
+ mbUnivariate = nNumXVars == 1;
+ // Observation count mismatch check
+ if (lcl_GetNumRowsColsInRange(mVariable1Range, bGroupedByColumn) != nYObs)
+ {
+ if (mbUnivariate)
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_UNIVARIATE_NUMOBS_MISMATCH));
+ else
+ mxErrorMessage->set_label(ScResId(STR_MESSAGE_MULTIVARIATE_NUMOBS_MISMATCH));
+ return false;
+ }
+
+ mnNumIndependentVars = nNumXVars;
+ mnNumObservations = bWithLabels ? nYObs - 1 : nYObs;
+
+ mbUse3DAddresses = mVariable1Range.aStart.Tab() != mOutputAddress.Tab() ||
+ mVariable2Range.aStart.Tab() != mOutputAddress.Tab();
+
+ mxErrorMessage->set_label("");
+
+ return true;
+}
+
+size_t ScRegressionDialog::GetRegressionTypeIndex() const
+{
+ if (mxLinearRadioButton->get_active())
+ return 0;
+ if (mxLogarithmicRadioButton->get_active())
+ return 1;
+ return 2;
+}
+
+ScRange ScRegressionDialog::GetDataRange(const ScRange& rRange)
+{
+ if (!mxWithLabelsCheckBox->get_active())
+ return rRange;
+
+ ScRange aDataRange(rRange);
+ if (mGroupedBy == BY_COLUMN)
+ aDataRange.aStart.IncRow(1);
+ else
+ aDataRange.aStart.IncCol(1);
+
+ return aDataRange;
+}
+
+OUString ScRegressionDialog::GetVariableNameFormula(bool bXVar, size_t nIndex, bool bWithLog)
+{
+ if (bXVar && nIndex == 0)
+ return "=\"" + ScResId(STR_LABEL_INTERCEPT) + "\"";
+
+ if (mxWithLabelsCheckBox->get_active())
+ {
+ ScAddress aAddr(bXVar ? mVariable1Range.aStart : mVariable2Range.aStart);
+ if (mGroupedBy == BY_COLUMN)
+ aAddr.IncCol(nIndex - 1);
+ else
+ aAddr.IncRow(nIndex - 1);
+
+ ScRefFlags eAddrFlag = mbUse3DAddresses ? ScRefFlags::ADDR_ABS_3D : ScRefFlags::ADDR_ABS;
+ return bWithLog ? OUString("=CONCAT(\"LN(\";" +
+ aAddr.Format(eAddrFlag, &mDocument, mDocument.GetAddressConvention()) + ";\")\")") :
+ OUString("=" + aAddr.Format(eAddrFlag, &mDocument, mDocument.GetAddressConvention()));
+ }
+
+ OUString aDefaultVarName;
+
+ if (bXVar)
+ aDefaultVarName = "X" + OUString::number(nIndex);
+ else
+ aDefaultVarName = "Y";
+
+ return bWithLog ? OUString("=\"LN(" + aDefaultVarName + ")\"") :
+ OUString("=\"" + aDefaultVarName + "\"");
+}
+
+OUString ScRegressionDialog::GetXVariableNameFormula(size_t nIndex, bool bWithLog)
+{
+ assert(nIndex <= mnNumIndependentVars);
+ return GetVariableNameFormula(true, nIndex, bWithLog);
+}
+
+OUString ScRegressionDialog::GetYVariableNameFormula(bool bWithLog)
+{
+ return GetVariableNameFormula(false, 1, bWithLog);
+}
+
+void ScRegressionDialog::WriteRawRegressionResults(AddressWalkerWriter& rOutput, FormulaTemplate& rTemplate,
+ size_t nRegressionIndex)
+{
+ rOutput.writeBoldString(ScResId(STR_REGRESSION));
+ rOutput.newLine();
+ // REGRESSION MODEL
+ rOutput.writeString(ScResId(STR_LABEL_REGRESSION_MODEL));
+ rOutput.nextColumn();
+ rOutput.writeString(ScResId(constRegressionModel[nRegressionIndex]));
+ rOutput.newLine();
+ rOutput.newLine();
+
+ rOutput.writeString(ScResId(STR_LINEST_RAW_OUTPUT_TITLE));
+ rOutput.newLine();
+ rOutput.push();
+
+ rTemplate.setTemplate(constTemplateLINEST[nRegressionIndex].
+ replaceFirst("%CALC_INTERCEPT%",
+ mbCalcIntercept ? std::u16string_view(u"TRUE") : std::u16string_view(u"FALSE")));
+ rOutput.writeMatrixFormula(rTemplate.getTemplate(), 1 + mnNumIndependentVars, 5);
+ // Add LINEST result components to template
+ // 1. Add ranges for coefficients and standard errors for indep. vars and the intercept.
+ // Note that these two are in the reverse order(m_n, m_n-1, ..., m_1, b) w.r.t what we expect.
+ rTemplate.autoReplaceRange("%COEFFICIENTS_REV_RANGE%", ScRange(rOutput.current(), rOutput.current(mnNumIndependentVars)));
+ rTemplate.autoReplaceRange("%SERRORSX_REV_RANGE%", ScRange(rOutput.current(0, 1), rOutput.current(mnNumIndependentVars, 1)));
+
+ // 2. Add R-squared and standard error for y estimate.
+ rTemplate.autoReplaceAddress("%RSQUARED_ADDR%", rOutput.current(0, 2));
+ rTemplate.autoReplaceAddress("%SERRORY_ADDR%", rOutput.current(1, 2));
+
+ // 3. Add F statistic and degrees of freedom
+ rTemplate.autoReplaceAddress("%FSTATISTIC_ADDR%", rOutput.current(0, 3));
+ rTemplate.autoReplaceAddress("%DoFRESID_ADDR%", rOutput.current(1, 3));
+
+ // 4. Add regression sum of squares and residual sum of squares
+ rTemplate.autoReplaceAddress("%SSREG_ADDR%", rOutput.current(0, 4));
+ rTemplate.autoReplaceAddress("%SSRESID_ADDR%", rOutput.current(1, 4));
+
+ rOutput.push(0, 4);
+ rOutput.newLine();
+}
+
+void ScRegressionDialog::WriteRegressionStatistics(AddressWalkerWriter& rOutput, FormulaTemplate& rTemplate)
+{
+ rOutput.newLine();
+ rOutput.writeString(ScResId(STR_LABEL_REGRESSION_STATISTICS));
+ rOutput.newLine();
+
+ const TranslateId aMeasureNames[] =
+ {
+ STR_LABEL_RSQUARED,
+ STRID_CALC_STD_ERROR,
+ STR_LABEL_XVARIABLES_COUNT,
+ STR_OBSERVATIONS_LABEL,
+ STR_LABEL_ADJUSTED_RSQUARED
+ };
+
+ OUString aMeasureFormulas[] =
+ {
+ "=%RSQUARED_ADDR%",
+ "=%SERRORY_ADDR%",
+ "=" + OUString::number(mnNumIndependentVars),
+ "=" + OUString::number(mnNumObservations),
+ OUString::Concat(
+ "=1 - (1 - %RSQUARED_ADDR%)*(%NUMOBS_ADDR% - 1)/(%NUMOBS_ADDR% - %NUMXVARS_ADDR%") +
+ (mbCalcIntercept ? std::u16string_view(u" - 1)") : std::u16string_view(u")"))
+ };
+
+ rTemplate.autoReplaceAddress("%NUMXVARS_ADDR%", rOutput.current(1, 2));
+ rTemplate.autoReplaceAddress("%NUMOBS_ADDR%", rOutput.current(1, 3));
+
+ for (size_t nIdx = 0; nIdx < SAL_N_ELEMENTS(aMeasureNames); ++nIdx)
+ {
+ rOutput.writeString(ScResId(aMeasureNames[nIdx]));
+ rOutput.nextColumn();
+ rTemplate.setTemplate(aMeasureFormulas[nIdx]);
+ rOutput.writeFormula(rTemplate.getTemplate());
+ rOutput.newLine();
+ }
+}
+
+void ScRegressionDialog::WriteRegressionANOVAResults(AddressWalkerWriter& rOutput, FormulaTemplate& rTemplate)
+{
+ rOutput.newLine();
+ rOutput.writeString(ScResId(STR_LABEL_ANOVA));
+ rOutput.newLine();
+
+ const size_t nColsInTable = 6;
+ const size_t nRowsInTable = 4;
+ OUString aTable[nRowsInTable][nColsInTable] =
+ {
+ {
+ "",
+ ScResId(STR_ANOVA_LABEL_DF),
+ ScResId(STR_ANOVA_LABEL_SS),
+ ScResId(STR_ANOVA_LABEL_MS),
+ ScResId(STR_ANOVA_LABEL_F),
+ ScResId(STR_ANOVA_LABEL_SIGNIFICANCE_F)
+ },
+ {
+ ScResId(STR_REGRESSION),
+ "=%NUMXVARS_ADDR%",
+ "=%SSREG_ADDR%",
+ "=%SSREG_ADDR% / %DoFREG_ADDR%",
+ "=%FSTATISTIC_ADDR%",
+ "=FDIST(%FSTATISTIC_ADDR% ; %DoFREG_ADDR% ; %DoFRESID_ADDR%)"
+ },
+ {
+ ScResId(STR_LABEL_RESIDUAL),
+ "=%DoFRESID_ADDR%",
+ "=%SSRESID_ADDR%",
+ "=%SSRESID_ADDR% / %DoFRESID_ADDR%",
+ "",
+ ""
+ },
+ {
+ ScResId(STR_ANOVA_LABEL_TOTAL),
+ "=%DoFREG_ADDR% + %DoFRESID_ADDR%",
+ "=%SSREG_ADDR% + %SSRESID_ADDR%",
+ "",
+ "",
+ ""
+ }
+ };
+
+ rTemplate.autoReplaceAddress("%DoFREG_ADDR%", rOutput.current(1, 1));
+
+ // Cell getter lambda
+ std::function<CellValueGetter> aCellGetterFunc = [&aTable](size_t nRowIdx, size_t nColIdx) -> const OUString&
+ {
+ return aTable[nRowIdx][nColIdx];
+ };
+
+ // Cell writer lambda
+ std::function<CellWriter> aCellWriterFunc = [&rOutput, &rTemplate]
+ (const OUString& rContent, size_t /*nRowIdx*/, size_t /*nColIdx*/)
+ {
+ if (!rContent.isEmpty())
+ {
+ if (rContent.startsWith("="))
+ {
+ rTemplate.setTemplate(rContent);
+ rOutput.writeFormula(rTemplate.getTemplate());
+ }
+ else
+ rOutput.writeString(rContent);
+ }
+ };
+
+ WriteTable(aCellGetterFunc, nRowsInTable, nColsInTable, rOutput, aCellWriterFunc);
+
+ // User given confidence level
+ rOutput.newLine();
+ rOutput.writeString(ScResId(STR_LABEL_CONFIDENCE_LEVEL));
+ rOutput.nextColumn();
+ rOutput.writeValue(mxConfidenceLevelField->get_value() / 100.0);
+ rTemplate.autoReplaceAddress("%CONFIDENCE_LEVEL_ADDR%", rOutput.current());
+ rOutput.newLine();
+}
+
+// Write slopes, intercept, their standard errors, t-statistics, p-value, confidence intervals
+void ScRegressionDialog::WriteRegressionEstimatesWithCI(AddressWalkerWriter& rOutput, FormulaTemplate& rTemplate,
+ bool bTakeLogX)
+{
+ rOutput.newLine();
+ ScAddress aEnd( rOutput.current(0, 1 + mnNumIndependentVars));
+ ScRefFlags eAddrFlag = mbUse3DAddresses ? ScRefFlags::ADDR_ABS_3D : ScRefFlags::ADDR_ABS;
+ aEnd.IncCol();
+ const OUString aCoeffAddr( aEnd.Format( eAddrFlag, &mDocument, mDocument.GetAddressConvention()));
+ aEnd.IncCol();
+ const OUString aStErrAddr( aEnd.Format( eAddrFlag, &mDocument, mDocument.GetAddressConvention()));
+
+ // Coefficients & Std.Errors ranges (column vectors) in this table (yet to populate).
+ rTemplate.autoReplaceRange("%COEFFICIENTS_RANGE%",
+ ScRange(rOutput.current(1, 1),
+ rOutput.current(1, 1 + mnNumIndependentVars)));
+ rTemplate.autoReplaceRange("%SLOPES_RANGE%", // Excludes the intercept
+ ScRange(rOutput.current(1, 2),
+ rOutput.current(1, 1 + mnNumIndependentVars)));
+ rTemplate.autoReplaceAddress("%INTERCEPT_ADDR%", rOutput.current(1, 1));
+ rTemplate.autoReplaceRange("%SERRORSX_RANGE%",
+ ScRange(rOutput.current(2, 1),
+ rOutput.current(2, 1 + mnNumIndependentVars)));
+ // t-Statistics range in this table (yet to populate)
+ rTemplate.autoReplaceRange("%TSTAT_RANGE%",
+ ScRange(rOutput.current(3, 1),
+ rOutput.current(3, 1 + mnNumIndependentVars)));
+
+ const size_t nColsInTable = 7;
+ const size_t nRowsInTable = 2;
+ OUString aTable[nRowsInTable][nColsInTable] =
+ {
+ {
+ "",
+ ScResId(STR_LABEL_COEFFICIENTS),
+ ScResId(STRID_CALC_STD_ERROR),
+ ScResId(STR_LABEL_TSTATISTIC),
+ ScResId(STR_P_VALUE_LABEL),
+
+ "=CONCAT(\"" + ScResId(STR_LABEL_LOWER) +
+ " \" ; INT(%CONFIDENCE_LEVEL_ADDR% * 100) ; \"%\")",
+
+ "=CONCAT(\"" + ScResId(STR_LABEL_UPPER) +
+ " \" ; INT(%CONFIDENCE_LEVEL_ADDR% * 100) ; \"%\")",
+ },
+
+ // Following are matrix formulas of size numcols = 1, numrows = (mnNumIndependentVars + 1)
+ {
+ "",
+ // This puts the coefficients in the reverse order compared to that in LINEST output.
+ "=INDEX(%COEFFICIENTS_REV_RANGE%; 1 ; ROW(" + aCoeffAddr + ")+1 - ROW())",
+ // This puts the standard errors in the reverse order compared to that in LINEST output.
+ "=INDEX(%SERRORSX_REV_RANGE%; 1 ; ROW(" + aStErrAddr + ")+1 - ROW())",
+ // t-Statistic
+ "=%COEFFICIENTS_RANGE% / %SERRORSX_RANGE%",
+ // p-Value
+ "=TDIST(ABS(%TSTAT_RANGE%) ; %DoFRESID_ADDR% ; 2 )",
+ // Lower limit of confidence interval
+ "=%COEFFICIENTS_RANGE% - %SERRORSX_RANGE% * "
+ "TINV(1 - %CONFIDENCE_LEVEL_ADDR% ; %DoFRESID_ADDR%)",
+ // Upper limit of confidence interval
+ "=%COEFFICIENTS_RANGE% + %SERRORSX_RANGE% * "
+ "TINV(1 - %CONFIDENCE_LEVEL_ADDR% ; %DoFRESID_ADDR%)"
+ }
+ };
+
+ // Cell getter lambda
+ std::function<CellValueGetter> aCellGetterFunc = [&aTable](size_t nRowIdx, size_t nColIdx) -> const OUString&
+ {
+ return aTable[nRowIdx][nColIdx];
+ };
+
+ // Cell writer lambda
+ size_t nNumIndependentVars = mnNumIndependentVars;
+ std::function<CellWriter> aCellWriterFunc = [&rOutput, &rTemplate, nNumIndependentVars]
+ (const OUString& rContent, size_t nRowIdx, size_t /*nColIdx*/)
+ {
+ if (!rContent.isEmpty())
+ {
+ if (rContent.startsWith("="))
+ {
+ rTemplate.setTemplate(rContent);
+ if (nRowIdx == 0)
+ rOutput.writeFormula(rTemplate.getTemplate());
+ else
+ rOutput.writeMatrixFormula(rTemplate.getTemplate(), 1, 1 + nNumIndependentVars);
+ }
+ else
+ rOutput.writeString(rContent);
+ }
+ };
+
+ WriteTable(aCellGetterFunc, nRowsInTable, nColsInTable, rOutput, aCellWriterFunc);
+
+ // Go back to the second row and first column of the table to
+ // fill the names of variables + intercept
+ rOutput.push(0, -1);
+
+ for (size_t nXvarIdx = 0; nXvarIdx <= mnNumIndependentVars; ++nXvarIdx)
+ {
+ rOutput.writeFormula(GetXVariableNameFormula(nXvarIdx, bTakeLogX));
+ rOutput.newLine();
+ }
+
+}
+
+// Re-write all observations in group-by column mode with predictions and residuals
+void ScRegressionDialog::WritePredictionsWithResiduals(AddressWalkerWriter& rOutput, FormulaTemplate& rTemplate,
+ size_t nRegressionIndex)
+{
+ bool bGroupedByColumn = mGroupedBy == BY_COLUMN;
+ rOutput.newLine();
+ rOutput.push();
+
+ // Range of X variables with rows as observations and columns as variables.
+ ScRange aDataMatrixRange(rOutput.current(0, 1), rOutput.current(mnNumIndependentVars - 1, mnNumObservations));
+ rTemplate.autoReplaceRange("%XDATAMATRIX_RANGE%", aDataMatrixRange);
+
+ // Write X variable names
+ for (size_t nXvarIdx = 1; nXvarIdx <= mnNumIndependentVars; ++nXvarIdx)
+ {
+ // Here we write the X variables without any transformation(LN)
+ rOutput.writeFormula(GetXVariableNameFormula(nXvarIdx, false));
+ rOutput.nextColumn();
+ }
+ rOutput.reset();
+
+ // Write the X data matrix
+ rOutput.nextRow();
+ OUString aDataMatrixFormula = bGroupedByColumn ? OUString("=%VARIABLE1_RANGE%") : OUString("=TRANSPOSE(%VARIABLE1_RANGE%)");
+ rTemplate.setTemplate(aDataMatrixFormula);
+ rOutput.writeMatrixFormula(rTemplate.getTemplate(), mnNumIndependentVars, mnNumObservations);
+
+ // Write predicted values
+ rOutput.push(mnNumIndependentVars, -1);
+ rOutput.writeString(ScResId(STR_LABEL_PREDICTEDY));
+ rOutput.nextRow();
+ rTemplate.setTemplate(constRegressionFormula[nRegressionIndex]);
+ rOutput.writeMatrixFormula(rTemplate.getTemplate(), 1, mnNumObservations);
+ rTemplate.autoReplaceRange("%PREDICTEDY_RANGE%", ScRange(rOutput.current(), rOutput.current(0, mnNumObservations - 1)));
+
+ // Write actual Y
+ rOutput.push(1, -1);
+ rOutput.writeFormula(GetYVariableNameFormula(false));
+ rOutput.nextRow();
+ OUString aYVectorFormula = bGroupedByColumn ? OUString("=%VARIABLE2_RANGE%") : OUString("=TRANSPOSE(%VARIABLE2_RANGE%)");
+ rTemplate.setTemplate(aYVectorFormula);
+ rOutput.writeMatrixFormula(rTemplate.getTemplate(), 1, mnNumObservations);
+ rTemplate.autoReplaceRange("%ACTUALY_RANGE%", ScRange(rOutput.current(), rOutput.current(0, mnNumObservations - 1)));
+
+ // Write residual
+ rOutput.push(1, -1);
+ rOutput.writeString(ScResId(STR_LABEL_RESIDUAL));
+ rOutput.nextRow();
+ rTemplate.setTemplate("=%ACTUALY_RANGE% - %PREDICTEDY_RANGE%");
+ rOutput.writeMatrixFormula(rTemplate.getTemplate(), 1, mnNumObservations);
+}
+
+// Generic table writer
+void ScRegressionDialog::WriteTable(const std::function<CellValueGetter>& rCellGetter,
+ size_t nRowsInTable, size_t nColsInTable,
+ AddressWalkerWriter& rOutput,
+ const std::function<CellWriter>& rFunc)
+{
+ for (size_t nRowIdx = 0; nRowIdx < nRowsInTable; ++nRowIdx)
+ {
+ for (size_t nColIdx = 0; nColIdx < nColsInTable; ++nColIdx)
+ {
+ rFunc(rCellGetter(nRowIdx, nColIdx), nRowIdx, nColIdx);
+ rOutput.nextColumn();
+ }
+ rOutput.newLine();
+ }
+}
+
+IMPL_LINK_NOARG(ScRegressionDialog, CheckBoxHdl, weld::Toggleable&, void)
+{
+ ValidateDialogInput();
+}
+
+IMPL_LINK_NOARG(ScRegressionDialog, NumericFieldHdl, weld::SpinButton&, void)
+{
+ ValidateDialogInput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/SamplingDialog.cxx b/sc/source/ui/StatisticsDialogs/SamplingDialog.cxx
new file mode 100644
index 0000000000..0667fd6a31
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/SamplingDialog.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/.
+ *
+ */
+
+#include <svl/undo.hxx>
+#include <comphelper/random.hxx>
+#include <rangelst.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <reffact.hxx>
+#include <docfunc.hxx>
+#include <SamplingDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScSamplingDialog::ScSamplingDialog(SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent,
+ "modules/scalc/ui/samplingdialog.ui", "SamplingDialog")
+ , mpActiveEdit(nullptr)
+ , mViewData(rViewData)
+ , mDocument(rViewData.GetDocument())
+ , mInputRange(ScAddress::INITIALIZE_INVALID)
+ , mAddressDetails(mDocument.GetAddressConvention(), 0, 0)
+ , mOutputAddress(ScAddress::INITIALIZE_INVALID)
+ , mCurrentAddress(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo())
+ , mnLastSampleSizeValue(1)
+ , mnLastPeriodValue(1)
+ , mDialogLostFocus(false)
+ , mxInputRangeLabel(m_xBuilder->weld_label("input-range-label"))
+ , mxInputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("input-range-edit")))
+ , mxInputRangeButton(new formula::RefButton(m_xBuilder->weld_button("input-range-button")))
+ , mxOutputRangeLabel(m_xBuilder->weld_label("output-range-label"))
+ , mxOutputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("output-range-edit")))
+ , mxOutputRangeButton(new formula::RefButton(m_xBuilder->weld_button("output-range-button")))
+ , mxSampleSize(m_xBuilder->weld_spin_button("sample-size-spin"))
+ , mxPeriod(m_xBuilder->weld_spin_button("period-spin"))
+ , mxRandomMethodRadio(m_xBuilder->weld_radio_button("random-method-radio"))
+ , mxWithReplacement(m_xBuilder->weld_check_button("with-replacement"))
+ , mxKeepOrder(m_xBuilder->weld_check_button("keep-order"))
+ , mxPeriodicMethodRadio(m_xBuilder->weld_radio_button("periodic-method-radio"))
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+{
+ mxInputRangeEdit->SetReferences(this, mxInputRangeLabel.get());
+ mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
+
+ mxOutputRangeEdit->SetReferences(this, mxOutputRangeLabel.get());
+ mxOutputRangeButton->SetReferences(this, mxOutputRangeEdit.get());
+
+ Init();
+ GetRangeFromSelection();
+}
+
+ScSamplingDialog::~ScSamplingDialog()
+{
+}
+
+void ScSamplingDialog::Init()
+{
+ mxButtonCancel->connect_clicked( LINK( this, ScSamplingDialog, ButtonClicked ) );
+ mxButtonOk->connect_clicked( LINK( this, ScSamplingDialog, ButtonClicked ) );
+ mxButtonOk->set_sensitive(false);
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScSamplingDialog, GetEditFocusHandler );
+ mxInputRangeEdit->SetGetFocusHdl( aEditLink );
+ mxOutputRangeEdit->SetGetFocusHdl( aEditLink );
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScSamplingDialog, GetButtonFocusHandler );
+ mxInputRangeButton->SetGetFocusHdl( aButtonLink );
+ mxOutputRangeButton->SetGetFocusHdl( aButtonLink );
+
+ aEditLink = LINK( this, ScSamplingDialog, LoseEditFocusHandler );
+ mxInputRangeEdit->SetLoseFocusHdl( aEditLink );
+ mxOutputRangeEdit->SetLoseFocusHdl( aEditLink );
+ aButtonLink = LINK( this, ScSamplingDialog, LoseButtonFocusHandler );
+ mxInputRangeButton->SetLoseFocusHdl( aButtonLink );
+ mxOutputRangeButton->SetLoseFocusHdl( aButtonLink );
+
+ Link<formula::RefEdit&,void> aLink2 = LINK( this, ScSamplingDialog, RefInputModifyHandler);
+ mxInputRangeEdit->SetModifyHdl( aLink2);
+ mxOutputRangeEdit->SetModifyHdl( aLink2);
+
+ mxSampleSize->connect_value_changed( LINK( this, ScSamplingDialog, SamplingSizeValueModified ));
+ mxSampleSize->set_range(1, SAL_MAX_INT32);
+ mxPeriod->connect_value_changed( LINK( this, ScSamplingDialog, PeriodValueModified ));
+ mxPeriod->set_range(1, SAL_MAX_INT32);
+
+ mxPeriodicMethodRadio->connect_toggled( LINK( this, ScSamplingDialog, ToggleSamplingMethod ) );
+ mxRandomMethodRadio->connect_toggled( LINK( this, ScSamplingDialog, ToggleSamplingMethod ) );
+
+ mxWithReplacement->connect_toggled( LINK( this, ScSamplingDialog, CheckHdl));
+ mxKeepOrder->connect_toggled( LINK( this, ScSamplingDialog, CheckHdl));
+
+ mxOutputRangeEdit->GrabFocus();
+ mxPeriodicMethodRadio->set_active(true);
+
+ ToggleSamplingMethod();
+}
+
+void ScSamplingDialog::GetRangeFromSelection()
+{
+ mViewData.GetSimpleArea(mInputRange);
+ OUString aCurrentString(mInputRange.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails));
+ mxInputRangeEdit->SetText(aCurrentString);
+}
+
+void ScSamplingDialog::SetActive()
+{
+ if ( mDialogLostFocus )
+ {
+ mDialogLostFocus = false;
+ if( mpActiveEdit )
+ mpActiveEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScSamplingDialog::Close()
+{
+ DoClose( ScSamplingDialogWrapper::GetChildWindowId() );
+}
+
+void ScSamplingDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDocument )
+{
+ if ( mpActiveEdit )
+ {
+ if ( rReferenceRange.aStart != rReferenceRange.aEnd )
+ RefInputStart( mpActiveEdit );
+
+ OUString aReferenceString;
+
+ if ( mpActiveEdit == mxInputRangeEdit.get() )
+ {
+ mInputRange = rReferenceRange;
+ aReferenceString = mInputRange.Format(rDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxInputRangeEdit->SetRefString( aReferenceString );
+
+ LimitSampleSizeAndPeriod();
+ }
+ else if ( mpActiveEdit == mxOutputRangeEdit.get() )
+ {
+ mOutputAddress = rReferenceRange.aStart;
+
+ ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
+ ScRefFlags::ADDR_ABS :
+ ScRefFlags::ADDR_ABS_3D;
+ aReferenceString = mOutputAddress.Format(nFormat, &rDocument, rDocument.GetAddressConvention());
+ mxOutputRangeEdit->SetRefString( aReferenceString );
+
+ // Change sampling size according to output range selection
+ sal_Int64 aSelectedSampleSize = rReferenceRange.aEnd.Row() - rReferenceRange.aStart.Row() + 1;
+ if (aSelectedSampleSize > 1)
+ mxSampleSize->set_value(aSelectedSampleSize);
+ SamplingSizeValueModified(*mxSampleSize);
+ }
+ }
+
+ // Enable OK if both, input range and output address are set.
+ // Disable if at least one is invalid.
+ mxButtonOk->set_sensitive(mInputRange.IsValid() && mOutputAddress.IsValid());
+}
+
+ScRange ScSamplingDialog::PerformPeriodicSampling(ScDocShell* pDocShell)
+{
+ ScAddress aStart = mInputRange.aStart;
+ ScAddress aEnd = mInputRange.aEnd;
+
+ SCTAB outTab = mOutputAddress.Tab();
+ SCROW outRow = mOutputAddress.Row();
+
+ sal_Int64 aPeriod = mxPeriod->get_value();
+
+ for (SCROW inTab = aStart.Tab(); inTab <= aEnd.Tab(); inTab++)
+ {
+ SCCOL outCol = mOutputAddress.Col();
+ for (SCCOL inCol = aStart.Col(); inCol <= aEnd.Col(); inCol++)
+ {
+ sal_Int64 i = 0;
+ outRow = mOutputAddress.Row();
+ for (SCROW inRow = aStart.Row(); inRow <= aEnd.Row(); inRow++)
+ {
+ assert(aPeriod && "div-by-zero");
+ if (i % aPeriod == aPeriod - 1 ) // Sample the last of period
+ {
+ double aValue = mDocument.GetValue(ScAddress(inCol, inRow, inTab));
+ pDocShell->GetDocFunc().SetValueCell(ScAddress(outCol, outRow, outTab), aValue, true);
+ outRow++;
+ }
+ i++;
+ }
+ outCol++;
+ }
+ outTab++;
+ }
+
+ return ScRange(mOutputAddress, ScAddress(outTab, outRow, outTab) );
+}
+
+ScRange ScSamplingDialog::PerformRandomSampling(ScDocShell* pDocShell)
+{
+ ScAddress aStart = mInputRange.aStart;
+ ScAddress aEnd = mInputRange.aEnd;
+
+ SCTAB outTab = mOutputAddress.Tab();
+ SCROW outRow = mOutputAddress.Row();
+
+ const sal_Int64 nSampleSize = mxSampleSize->get_value();
+
+ // This implementation groups by columns. Other options could be grouping
+ // by rows or area.
+ const sal_Int64 nPopulationSize = aEnd.Row() - aStart.Row() + 1;
+
+ const bool bWithReplacement = mxWithReplacement->get_sensitive() && mxWithReplacement->get_active();
+
+ // WOR (WithOutReplacement) can't draw more than population. Catch that in
+ // the caller.
+ assert( bWithReplacement || nSampleSize <= nPopulationSize);
+ if (!bWithReplacement && nSampleSize > nPopulationSize)
+ // Would enter an endless loop below, bail out.
+ return ScRange( mOutputAddress);
+
+ for (SCROW inTab = aStart.Tab(); inTab <= aEnd.Tab(); inTab++)
+ {
+ SCCOL outCol = mOutputAddress.Col();
+ for (SCCOL inCol = aStart.Col(); inCol <= aEnd.Col(); inCol++)
+ {
+ outRow = mOutputAddress.Row();
+ std::vector<bool> vUsed( nPopulationSize, false);
+
+ while ((outRow - mOutputAddress.Row()) < nSampleSize)
+ {
+ // [a,b] *both* inclusive
+ SCROW nRandom = comphelper::rng::uniform_int_distribution( aStart.Row(), aEnd.Row());
+
+ if (!bWithReplacement)
+ {
+ nRandom -= aStart.Row();
+ if (vUsed[nRandom])
+ {
+ // Find a nearest one, preferring forwards.
+ // Again: it's essential that the loop is entered only
+ // if nSampleSize<=nPopulationSize, which is checked
+ // above.
+ SCROW nBack = nRandom;
+ SCROW nForw = nRandom;
+ do
+ {
+ if (nForw < nPopulationSize - 1 && !vUsed[++nForw])
+ {
+ nRandom = nForw;
+ break;
+ }
+ if (nBack > 0 && !vUsed[--nBack])
+ {
+ nRandom = nBack;
+ break;
+ }
+ }
+ while (true);
+ }
+ vUsed[nRandom] = true;
+ nRandom += aStart.Row();
+ }
+
+ const double fValue = mDocument.GetValue( ScAddress(inCol, nRandom, inTab) );
+ pDocShell->GetDocFunc().SetValueCell(ScAddress(outCol, outRow, outTab), fValue, true);
+ outRow++;
+ }
+ outCol++;
+ }
+ outTab++;
+ }
+
+ return ScRange(mOutputAddress, ScAddress(outTab, outRow, outTab) );
+}
+
+ScRange ScSamplingDialog::PerformRandomSamplingKeepOrder(ScDocShell* pDocShell)
+{
+ ScAddress aStart = mInputRange.aStart;
+ ScAddress aEnd = mInputRange.aEnd;
+
+ SCTAB outTab = mOutputAddress.Tab();
+ SCROW outRow = mOutputAddress.Row();
+
+ SCROW inRow;
+
+ sal_Int64 aSampleSize = mxSampleSize->get_value();
+
+ for (SCROW inTab = aStart.Tab(); inTab <= aEnd.Tab(); inTab++)
+ {
+ SCCOL outCol = mOutputAddress.Col();
+ for (SCCOL inCol = aStart.Col(); inCol <= aEnd.Col(); inCol++)
+ {
+ SCROW aPopulationSize = (aEnd.Row() - aStart.Row()) + 1;
+
+ outRow = mOutputAddress.Row();
+ inRow = aStart.Row();
+
+ while ((outRow - mOutputAddress.Row()) < aSampleSize)
+ {
+ double aRandomValue = comphelper::rng::uniform_real_distribution();
+
+ if ( (aPopulationSize - (inRow - aStart.Row())) * aRandomValue >= aSampleSize - (outRow - mOutputAddress.Row()) )
+ {
+ inRow++;
+ }
+ else
+ {
+ double aValue = mDocument.GetValue( ScAddress(inCol, inRow, inTab) );
+ pDocShell->GetDocFunc().SetValueCell(ScAddress(outCol, outRow, outTab), aValue, true);
+ inRow++;
+ outRow++;
+ }
+ }
+ outCol++;
+ }
+ outTab++;
+ }
+
+ return ScRange(mOutputAddress, ScAddress(outTab, outRow, outTab) );
+}
+
+void ScSamplingDialog::PerformSampling()
+{
+ OUString aUndo(ScResId(STR_SAMPLING_UNDO_NAME));
+ ScDocShell* pDocShell = mViewData.GetDocShell();
+ SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
+
+ ScRange aModifiedRange;
+
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, mViewData.GetViewShell()->GetViewShellId() );
+
+ if (mxRandomMethodRadio->get_active())
+ {
+ if (mxKeepOrder->get_sensitive() && mxKeepOrder->get_active())
+ aModifiedRange = PerformRandomSamplingKeepOrder(pDocShell);
+ else
+ aModifiedRange = PerformRandomSampling(pDocShell);
+ }
+ else if (mxPeriodicMethodRadio->get_active())
+ {
+ aModifiedRange = PerformPeriodicSampling(pDocShell);
+ }
+
+ pUndoManager->LeaveListAction();
+ pDocShell->PostPaint(aModifiedRange, PaintPartFlags::Grid);
+}
+
+sal_Int64 ScSamplingDialog::GetPopulationSize() const
+{
+ return mInputRange.IsValid() ? mInputRange.aEnd.Row() - mInputRange.aStart.Row() + 1 : 0;
+}
+
+void ScSamplingDialog::LimitSampleSizeAndPeriod()
+{
+ // Limit sample size (for WOR methods) and period if population is smaller
+ // than last known value. When enlarging the input population range the
+ // values will be adjusted up to the last known value again.
+ const sal_Int64 nPopulationSize = GetPopulationSize();
+ if (nPopulationSize <= mnLastSampleSizeValue && !mxWithReplacement->get_active())
+ mxSampleSize->set_value( nPopulationSize);
+ if (nPopulationSize <= mnLastPeriodValue)
+ mxPeriod->set_value( nPopulationSize);
+}
+
+IMPL_LINK_NOARG(ScSamplingDialog, SamplingSizeValueModified, weld::SpinButton&, void)
+{
+ if (!mxWithReplacement->get_active())
+ {
+ // For all WOR methods limit sample size to population size.
+ const sal_Int64 nPopulationSize = GetPopulationSize();
+ if (mxSampleSize->get_value() > nPopulationSize)
+ mxSampleSize->set_value(nPopulationSize);
+ }
+ mnLastSampleSizeValue = mxSampleSize->get_value();
+}
+
+IMPL_LINK_NOARG(ScSamplingDialog, PeriodValueModified, weld::SpinButton&, void)
+{
+ // Limit period to population size.
+ const sal_Int64 nPopulationSize = GetPopulationSize();
+ if (mxPeriod->get_value() > nPopulationSize)
+ mxPeriod->set_value(nPopulationSize);
+ mnLastPeriodValue = mxPeriod->get_value();
+}
+
+IMPL_LINK( ScSamplingDialog, GetEditFocusHandler, formula::RefEdit&, rCtrl, void )
+{
+ if (&rCtrl == mxInputRangeEdit.get())
+ mpActiveEdit = mxInputRangeEdit.get();
+ else if (&rCtrl == mxOutputRangeEdit.get())
+ mpActiveEdit = mxOutputRangeEdit.get();
+ else
+ mpActiveEdit = nullptr;
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(ScSamplingDialog, GetButtonFocusHandler, formula::RefButton&, rCtrl, void)
+{
+ if (&rCtrl == mxInputRangeButton.get())
+ mpActiveEdit = mxInputRangeEdit.get();
+ else if (&rCtrl == mxOutputRangeButton.get())
+ mpActiveEdit = mxOutputRangeEdit.get();
+ else
+ mpActiveEdit = nullptr;
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+
+IMPL_LINK(ScSamplingDialog, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (&rButton == mxButtonOk.get())
+ {
+ PerformSampling();
+ response(RET_OK);
+ }
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScSamplingDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScSamplingDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScSamplingDialog, ToggleSamplingMethod, weld::Toggleable&, void)
+{
+ ToggleSamplingMethod();
+}
+
+void ScSamplingDialog::ToggleSamplingMethod()
+{
+ if (mxRandomMethodRadio->get_active())
+ {
+ mxPeriod->set_sensitive(false);
+ mxSampleSize->set_sensitive(true);
+ mxWithReplacement->set_sensitive(true);
+ mxKeepOrder->set_sensitive(true);
+ }
+ else if (mxPeriodicMethodRadio->get_active())
+ {
+ // WOR keeping order.
+ mxPeriod->set_sensitive(true);
+ mxSampleSize->set_sensitive(false);
+ mxWithReplacement->set_active(false);
+ mxWithReplacement->set_sensitive(false);
+ mxKeepOrder->set_active(true);
+ mxKeepOrder->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(ScSamplingDialog, CheckHdl, weld::Toggleable&, rBtn, void)
+{
+ // Keep both checkboxes enabled so user can easily switch between the three
+ // possible combinations (one or the other or none), just uncheck the other
+ // one if one is checked. Otherwise the other checkbox would had to be
+ // disabled until user unchecks the enabled one again, which would force
+ // user to two clicks to switch.
+ if (&rBtn == mxWithReplacement.get())
+ {
+ if (mxWithReplacement->get_active())
+ {
+ // For WR can't keep order.
+ mxKeepOrder->set_active(false);
+ }
+ else
+ {
+ // For WOR limit sample size to population size.
+ SamplingSizeValueModified(*mxSampleSize);
+ }
+ }
+ else if (&rBtn == mxKeepOrder.get())
+ {
+ if (mxKeepOrder->get_active())
+ {
+ // Keep order is always WOR.
+ mxWithReplacement->set_active(false);
+ SamplingSizeValueModified(*mxSampleSize);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScSamplingDialog, RefInputModifyHandler, formula::RefEdit&, void)
+{
+ if ( mpActiveEdit )
+ {
+ if ( mpActiveEdit == mxInputRangeEdit.get() )
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxInputRangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mInputRange = *pRange;
+ // Highlight the resulting range.
+ mxInputRangeEdit->StartUpdateData();
+
+ LimitSampleSizeAndPeriod();
+ }
+ else
+ {
+ mInputRange = ScRange( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ else if ( mpActiveEdit == mxOutputRangeEdit.get() )
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxOutputRangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mOutputAddress = pRange->aStart;
+
+ // Crop output range to top left address for Edit field.
+ if (pRange->aStart != pRange->aEnd)
+ {
+ ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
+ ScRefFlags::ADDR_ABS :
+ ScRefFlags::ADDR_ABS_3D;
+ OUString aReferenceString = mOutputAddress.Format(nFormat, &mDocument, mDocument.GetAddressConvention());
+ mxOutputRangeEdit->SetRefString( aReferenceString );
+ }
+
+ // Change sampling size according to output range selection
+ sal_Int64 aSelectedSampleSize = pRange->aEnd.Row() - pRange->aStart.Row() + 1;
+ if (aSelectedSampleSize > 1)
+ mxSampleSize->set_value(aSelectedSampleSize);
+ SamplingSizeValueModified(*mxSampleSize);
+
+ // Highlight the resulting range.
+ mxOutputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ mOutputAddress = ScAddress( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ }
+
+ // Enable OK if both, input range and output address are set.
+ mxButtonOk->set_sensitive(mInputRange.IsValid() && mOutputAddress.IsValid());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/StatisticsInputOutputDialog.cxx b/sc/source/ui/StatisticsDialogs/StatisticsInputOutputDialog.cxx
new file mode 100644
index 0000000000..15521dd1e1
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/StatisticsInputOutputDialog.cxx
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <svl/undo.hxx>
+
+#include <rangelst.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+
+#include <StatisticsInputOutputDialog.hxx>
+
+ScRangeList ScStatisticsInputOutputDialog::MakeColumnRangeList(SCTAB aTab, ScAddress const & aStart, ScAddress const & aEnd)
+{
+ ScRangeList aRangeList;
+ for (SCCOL inCol = aStart.Col(); inCol <= aEnd.Col(); inCol++)
+ {
+ ScRange aColumnRange (
+ ScAddress(inCol, aStart.Row(), aTab),
+ ScAddress(inCol, aEnd.Row(), aTab) );
+
+ aRangeList.push_back(aColumnRange);
+ }
+ return aRangeList;
+}
+
+ScRangeList ScStatisticsInputOutputDialog::MakeRowRangeList(SCTAB aTab, ScAddress const & aStart, ScAddress const & aEnd)
+{
+ ScRangeList aRangeList;
+ for (SCROW inRow = aStart.Row(); inRow <= aEnd.Row(); inRow++)
+ {
+ ScRange aRowRange (
+ ScAddress(aStart.Col(), inRow, aTab),
+ ScAddress(aEnd.Col(), inRow, aTab) );
+
+ aRangeList.push_back(aRowRange);
+ }
+ return aRangeList;
+}
+
+ScStatisticsInputOutputDialog::ScStatisticsInputOutputDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData, const OUString& rUIXMLDescription, const OUString& rID)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent, rUIXMLDescription, rID)
+ , mxInputRangeLabel(m_xBuilder->weld_label("input-range-label"))
+ , mxInputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("input-range-edit")))
+ , mxInputRangeButton(new formula::RefButton(m_xBuilder->weld_button("input-range-button")))
+ , mxOutputRangeLabel(m_xBuilder->weld_label("output-range-label"))
+ , mxOutputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("output-range-edit")))
+ , mxOutputRangeButton(new formula::RefButton(m_xBuilder->weld_button("output-range-button")))
+ , mxGroupByColumnsRadio(m_xBuilder->weld_radio_button("groupedby-columns-radio"))
+ , mxGroupByRowsRadio(m_xBuilder->weld_radio_button("groupedby-rows-radio"))
+ , mViewData(rViewData)
+ , mDocument(rViewData.GetDocument())
+ , mInputRange(ScAddress::INITIALIZE_INVALID)
+ , mAddressDetails(mDocument.GetAddressConvention(), 0, 0)
+ , mOutputAddress(ScAddress::INITIALIZE_INVALID)
+ , mGroupedBy(BY_COLUMN)
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+ , mpActiveEdit(nullptr)
+ , mCurrentAddress(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo())
+ , mDialogLostFocus(false)
+{
+ mxInputRangeEdit->SetReferences(this, mxInputRangeLabel.get());
+ mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
+
+ mxOutputRangeEdit->SetReferences(this, mxOutputRangeLabel.get());
+ mxOutputRangeButton->SetReferences(this, mxOutputRangeEdit.get());
+
+ Init();
+ GetRangeFromSelection();
+}
+
+ScStatisticsInputOutputDialog::~ScStatisticsInputOutputDialog()
+{
+}
+
+void ScStatisticsInputOutputDialog::Init()
+{
+ mxButtonCancel->connect_clicked( LINK( this, ScStatisticsInputOutputDialog, ButtonClicked ) );
+ mxButtonOk->connect_clicked( LINK( this, ScStatisticsInputOutputDialog, ButtonClicked ) );
+ mxButtonOk->set_sensitive(false);
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScStatisticsInputOutputDialog, GetEditFocusHandler );
+ mxInputRangeEdit->SetGetFocusHdl( aEditLink );
+ mxOutputRangeEdit->SetGetFocusHdl( aEditLink );
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScStatisticsInputOutputDialog, GetButtonFocusHandler );
+ mxInputRangeButton->SetGetFocusHdl( aButtonLink );
+ mxOutputRangeButton->SetGetFocusHdl( aButtonLink );
+
+ aEditLink = LINK( this, ScStatisticsInputOutputDialog, LoseEditFocusHandler );
+ mxInputRangeEdit->SetLoseFocusHdl( aEditLink );
+ mxOutputRangeEdit->SetLoseFocusHdl( aEditLink );
+ aButtonLink = LINK( this, ScStatisticsInputOutputDialog, LoseButtonFocusHandler );
+ mxInputRangeButton->SetLoseFocusHdl( aButtonLink );
+ mxOutputRangeButton->SetLoseFocusHdl( aButtonLink );
+
+ Link<formula::RefEdit&,void> aLink2 = LINK( this, ScStatisticsInputOutputDialog, RefInputModifyHandler);
+ mxInputRangeEdit->SetModifyHdl( aLink2);
+ mxOutputRangeEdit->SetModifyHdl( aLink2);
+
+ mxOutputRangeEdit->GrabFocus();
+
+ mxGroupByColumnsRadio->connect_toggled( LINK( this, ScStatisticsInputOutputDialog, GroupByChanged ) );
+ mxGroupByRowsRadio->connect_toggled( LINK( this, ScStatisticsInputOutputDialog, GroupByChanged ) );
+
+ mxGroupByColumnsRadio->set_active(true);
+ mxGroupByRowsRadio->set_active(false);
+}
+
+void ScStatisticsInputOutputDialog::GetRangeFromSelection()
+{
+ mViewData.GetSimpleArea(mInputRange);
+ OUString aCurrentString(mInputRange.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails));
+ mxInputRangeEdit->SetText(aCurrentString);
+}
+
+void ScStatisticsInputOutputDialog::SetActive()
+{
+ if ( mDialogLostFocus )
+ {
+ mDialogLostFocus = false;
+ if( mpActiveEdit )
+ mpActiveEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScStatisticsInputOutputDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDocument )
+{
+ if ( mpActiveEdit )
+ {
+ if ( rReferenceRange.aStart != rReferenceRange.aEnd )
+ RefInputStart( mpActiveEdit );
+
+ OUString aReferenceString;
+
+ if (mpActiveEdit == mxInputRangeEdit.get())
+ {
+ mInputRange = rReferenceRange;
+ aReferenceString = mInputRange.Format(rDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxInputRangeEdit->SetRefString( aReferenceString );
+ }
+ else if (mpActiveEdit == mxOutputRangeEdit.get())
+ {
+ mOutputAddress = rReferenceRange.aStart;
+
+ ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
+ ScRefFlags::ADDR_ABS :
+ ScRefFlags::ADDR_ABS_3D;
+ aReferenceString = mOutputAddress.Format(nFormat, &rDocument, rDocument.GetAddressConvention());
+ mxOutputRangeEdit->SetRefString( aReferenceString );
+ }
+ }
+
+ ValidateDialogInput();
+}
+
+IMPL_LINK( ScStatisticsInputOutputDialog, ButtonClicked, weld::Button&, rButton, void )
+{
+ if (&rButton == mxButtonOk.get())
+ {
+ CalculateInputAndWriteToOutput();
+ response(RET_OK);
+ }
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScStatisticsInputOutputDialog, GetEditFocusHandler, formula::RefEdit&, rCtrl, void)
+{
+ mpActiveEdit = nullptr;
+
+ if (&rCtrl == mxInputRangeEdit.get())
+ mpActiveEdit = mxInputRangeEdit.get();
+ if (&rCtrl == mxOutputRangeEdit.get())
+ mpActiveEdit = mxOutputRangeEdit.get();
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(ScStatisticsInputOutputDialog, GetButtonFocusHandler, formula::RefButton&, rCtrl, void)
+{
+ mpActiveEdit = nullptr;
+
+ if (&rCtrl == mxInputRangeButton.get())
+ mpActiveEdit = mxInputRangeEdit.get();
+ else if (&rCtrl == mxOutputRangeButton.get())
+ mpActiveEdit = mxOutputRangeEdit.get();
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScStatisticsInputOutputDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScStatisticsInputOutputDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG( ScStatisticsInputOutputDialog, GroupByChanged, weld::Toggleable&, void )
+{
+ if (mxGroupByColumnsRadio->get_active())
+ mGroupedBy = BY_COLUMN;
+ else if (mxGroupByRowsRadio->get_active())
+ mGroupedBy = BY_ROW;
+
+ ValidateDialogInput();
+}
+
+IMPL_LINK_NOARG( ScStatisticsInputOutputDialog, RefInputModifyHandler, formula::RefEdit&, void )
+{
+ if ( mpActiveEdit )
+ {
+ if (mpActiveEdit == mxInputRangeEdit.get())
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxInputRangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mInputRange = *pRange;
+ // Highlight the resulting range.
+ mxInputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ mInputRange = ScRange( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ else if (mpActiveEdit == mxOutputRangeEdit.get())
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxOutputRangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mOutputAddress = pRange->aStart;
+
+ // Crop output range to top left address for Edit field.
+ if (pRange->aStart != pRange->aEnd)
+ {
+ ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
+ ScRefFlags::ADDR_ABS :
+ ScRefFlags::ADDR_ABS_3D;
+ OUString aReferenceString = mOutputAddress.Format(nFormat, &mDocument, mDocument.GetAddressConvention());
+ mxOutputRangeEdit->SetRefString( aReferenceString );
+ }
+
+ // Highlight the resulting range.
+ mxOutputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ mOutputAddress = ScAddress( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ }
+
+ ValidateDialogInput();
+}
+
+void ScStatisticsInputOutputDialog::CalculateInputAndWriteToOutput()
+{
+ OUString aUndo(ScResId(GetUndoNameId()));
+ ScDocShell* pDocShell = mViewData.GetDocShell();
+ SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, mViewData.GetViewShell()->GetViewShellId() );
+
+ ScRange aOutputRange = ApplyOutput(pDocShell);
+
+ pUndoManager->LeaveListAction();
+ pDocShell->PostPaint( aOutputRange, PaintPartFlags::Grid );
+}
+
+bool ScStatisticsInputOutputDialog::InputRangesValid()
+{
+ return mInputRange.IsValid() && mOutputAddress.IsValid();
+}
+
+void ScStatisticsInputOutputDialog::ValidateDialogInput()
+{
+ // Enable OK button if all inputs are ok.
+ mxButtonOk->set_sensitive(InputRangesValid());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/StatisticsTwoVariableDialog.cxx b/sc/source/ui/StatisticsDialogs/StatisticsTwoVariableDialog.cxx
new file mode 100644
index 0000000000..8c45f4b2df
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/StatisticsTwoVariableDialog.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/.
+ *
+ */
+
+#include <svl/undo.hxx>
+
+#include <rangelst.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+
+#include <StatisticsTwoVariableDialog.hxx>
+
+ScStatisticsTwoVariableDialog::ScStatisticsTwoVariableDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData, const OUString& rUIXMLDescription, const OUString& rID)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent, rUIXMLDescription, rID)
+ , mxVariable1RangeLabel(m_xBuilder->weld_label("variable1-range-label"))
+ , mxVariable1RangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("variable1-range-edit")))
+ , mxVariable1RangeButton(new formula::RefButton(m_xBuilder->weld_button("variable1-range-button")))
+ , mxVariable2RangeLabel(m_xBuilder->weld_label("variable2-range-label"))
+ , mxVariable2RangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("variable2-range-edit")))
+ , mxVariable2RangeButton(new formula::RefButton(m_xBuilder->weld_button("variable2-range-button")))
+ , mxOutputRangeLabel(m_xBuilder->weld_label("output-range-label"))
+ , mxOutputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("output-range-edit")))
+ , mxOutputRangeButton(new formula::RefButton(m_xBuilder->weld_button("output-range-button")))
+ , mViewData(rViewData)
+ , mDocument(rViewData.GetDocument())
+ , mVariable1Range(ScAddress::INITIALIZE_INVALID)
+ , mVariable2Range(ScAddress::INITIALIZE_INVALID)
+ , mAddressDetails(mDocument.GetAddressConvention(), 0, 0 )
+ , mOutputAddress(ScAddress::INITIALIZE_INVALID)
+ , mGroupedBy(BY_COLUMN)
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+ , mxGroupByColumnsRadio(m_xBuilder->weld_radio_button("groupedby-columns-radio"))
+ , mxGroupByRowsRadio(m_xBuilder->weld_radio_button("groupedby-rows-radio"))
+ , mpActiveEdit(nullptr)
+ , mCurrentAddress(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() )
+ , mDialogLostFocus(false)
+{
+ mxVariable1RangeEdit->SetReferences(this, mxVariable1RangeLabel.get());
+ mxVariable1RangeButton->SetReferences(this, mxVariable1RangeEdit.get());
+
+ mxVariable2RangeEdit->SetReferences(this, mxVariable2RangeLabel.get());
+ mxVariable2RangeButton->SetReferences(this, mxVariable2RangeEdit.get());
+
+ mxOutputRangeEdit->SetReferences(this, mxOutputRangeLabel.get());
+ mxOutputRangeButton->SetReferences(this, mxOutputRangeEdit.get());
+
+ Init();
+ GetRangeFromSelection();
+}
+
+ScStatisticsTwoVariableDialog::~ScStatisticsTwoVariableDialog()
+{
+}
+
+void ScStatisticsTwoVariableDialog::Init()
+{
+ mxButtonCancel->connect_clicked( LINK( this, ScStatisticsTwoVariableDialog, ButtonClicked ) );
+ mxButtonOk->connect_clicked( LINK( this, ScStatisticsTwoVariableDialog, ButtonClicked ) );
+ mxButtonOk->set_sensitive(false);
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScStatisticsTwoVariableDialog, GetEditFocusHandler );
+ mxVariable1RangeEdit->SetGetFocusHdl( aEditLink );
+ mxVariable2RangeEdit->SetGetFocusHdl( aEditLink );
+ mxOutputRangeEdit->SetGetFocusHdl( aEditLink );
+
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScStatisticsTwoVariableDialog, GetButtonFocusHandler );
+ mxVariable1RangeButton->SetGetFocusHdl( aButtonLink );
+ mxVariable2RangeButton->SetGetFocusHdl( aButtonLink );
+ mxOutputRangeButton->SetGetFocusHdl( aButtonLink );
+
+ aEditLink = LINK( this, ScStatisticsTwoVariableDialog, LoseEditFocusHandler );
+ mxVariable1RangeEdit->SetLoseFocusHdl( aEditLink );
+ mxVariable2RangeEdit->SetLoseFocusHdl( aEditLink );
+ mxOutputRangeEdit->SetLoseFocusHdl( aEditLink );
+
+ aButtonLink = LINK( this, ScStatisticsTwoVariableDialog, LoseButtonFocusHandler );
+ mxVariable1RangeButton->SetLoseFocusHdl( aButtonLink );
+ mxVariable2RangeButton->SetLoseFocusHdl( aButtonLink );
+ mxOutputRangeButton->SetLoseFocusHdl( aButtonLink );
+
+ Link<formula::RefEdit&,void> aLink2 = LINK( this, ScStatisticsTwoVariableDialog, RefInputModifyHandler);
+ mxVariable1RangeEdit->SetModifyHdl( aLink2);
+ mxVariable2RangeEdit->SetModifyHdl( aLink2);
+ mxOutputRangeEdit->SetModifyHdl( aLink2);
+
+ mxOutputRangeEdit->GrabFocus();
+
+ mxGroupByColumnsRadio->connect_toggled( LINK( this, ScStatisticsTwoVariableDialog, GroupByChanged ) );
+ mxGroupByRowsRadio->connect_toggled( LINK( this, ScStatisticsTwoVariableDialog, GroupByChanged ) );
+
+ mxGroupByColumnsRadio->set_active(true);
+ mxGroupByRowsRadio->set_active(false);
+}
+
+void ScStatisticsTwoVariableDialog::GetRangeFromSelection()
+{
+ OUString aCurrentString;
+
+ ScRange aCurrentRange;
+ mViewData.GetSimpleArea(aCurrentRange);
+
+ if (aCurrentRange.aEnd.Col() - aCurrentRange.aStart.Col() == 1)
+ {
+ mVariable1Range = aCurrentRange;
+ mVariable1Range.aEnd.SetCol(mVariable1Range.aStart.Col());
+ aCurrentString = mVariable1Range.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxVariable1RangeEdit->SetText(aCurrentString);
+
+ mVariable2Range = aCurrentRange;
+ mVariable2Range.aStart.SetCol(mVariable2Range.aEnd.Col());
+ aCurrentString = mVariable2Range.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxVariable2RangeEdit->SetText(aCurrentString);
+ }
+ else
+ {
+ mVariable1Range = aCurrentRange;
+ aCurrentString = mVariable1Range.Format(mDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxVariable1RangeEdit->SetText(aCurrentString);
+ }
+}
+
+void ScStatisticsTwoVariableDialog::SetActive()
+{
+ if ( mDialogLostFocus )
+ {
+ mDialogLostFocus = false;
+ if( mpActiveEdit )
+ mpActiveEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScStatisticsTwoVariableDialog::SetReference( const ScRange& rReferenceRange, ScDocument& rDocument )
+{
+ if ( mpActiveEdit != nullptr )
+ {
+ if ( rReferenceRange.aStart != rReferenceRange.aEnd )
+ RefInputStart( mpActiveEdit );
+
+ OUString aReferenceString;
+
+ if ( mpActiveEdit == mxVariable1RangeEdit.get() )
+ {
+ mVariable1Range = rReferenceRange;
+ aReferenceString = mVariable1Range.Format(rDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxVariable1RangeEdit->SetRefString(aReferenceString);
+ }
+ else if ( mpActiveEdit == mxVariable2RangeEdit.get() )
+ {
+ mVariable2Range = rReferenceRange;
+ aReferenceString = mVariable2Range.Format(rDocument, ScRefFlags::RANGE_ABS_3D, mAddressDetails);
+ mxVariable2RangeEdit->SetRefString(aReferenceString);
+ }
+ else if ( mpActiveEdit == mxOutputRangeEdit.get() )
+ {
+ mOutputAddress = rReferenceRange.aStart;
+
+ ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
+ ScRefFlags::ADDR_ABS :
+ ScRefFlags::ADDR_ABS_3D;
+ aReferenceString = mOutputAddress.Format(nFormat, &rDocument, rDocument.GetAddressConvention());
+ mxOutputRangeEdit->SetRefString( aReferenceString );
+ }
+ }
+
+ ValidateDialogInput();
+}
+
+IMPL_LINK( ScStatisticsTwoVariableDialog, ButtonClicked, weld::Button&, rButton, void )
+{
+ if (&rButton == mxButtonOk.get())
+ {
+ CalculateInputAndWriteToOutput();
+ response(RET_OK);
+ }
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScStatisticsTwoVariableDialog, GetEditFocusHandler, formula::RefEdit&, rCtrl, void)
+{
+ mpActiveEdit = nullptr;
+ if (&rCtrl == mxVariable1RangeEdit.get())
+ {
+ mpActiveEdit = mxVariable1RangeEdit.get();
+ }
+ else if (&rCtrl == mxVariable2RangeEdit.get())
+ {
+ mpActiveEdit = mxVariable2RangeEdit.get();
+ }
+ else if (&rCtrl == mxOutputRangeEdit.get())
+ {
+ mpActiveEdit = mxOutputRangeEdit.get();
+ }
+
+ if( mpActiveEdit )
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK( ScStatisticsTwoVariableDialog, GetButtonFocusHandler, formula::RefButton&, rCtrl, void )
+{
+ mpActiveEdit = nullptr;
+ if (&rCtrl == mxVariable1RangeButton.get())
+ {
+ mpActiveEdit = mxVariable1RangeEdit.get();
+ }
+ else if (&rCtrl == mxVariable2RangeButton.get())
+ {
+ mpActiveEdit = mxVariable2RangeEdit.get();
+ }
+ else if (&rCtrl == mxOutputRangeButton.get())
+ {
+ mpActiveEdit = mxOutputRangeEdit.get();
+ }
+
+ if( mpActiveEdit )
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG( ScStatisticsTwoVariableDialog, LoseEditFocusHandler, formula::RefEdit&, void )
+{
+ mDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG( ScStatisticsTwoVariableDialog, LoseButtonFocusHandler, formula::RefButton&, void )
+{
+ mDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScStatisticsTwoVariableDialog, GroupByChanged, weld::Toggleable&, void)
+{
+ if (mxGroupByColumnsRadio->get_active())
+ mGroupedBy = BY_COLUMN;
+ else if (mxGroupByRowsRadio->get_active())
+ mGroupedBy = BY_ROW;
+
+ ValidateDialogInput();
+}
+
+IMPL_LINK_NOARG( ScStatisticsTwoVariableDialog, RefInputModifyHandler, formula::RefEdit&, void )
+{
+ if ( mpActiveEdit )
+ {
+ if (mpActiveEdit == mxVariable1RangeEdit.get())
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxVariable1RangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mVariable1Range = *pRange;
+ // Highlight the resulting range.
+ mxVariable1RangeEdit->StartUpdateData();
+ }
+ else
+ {
+ mVariable1Range = ScRange( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ else if ( mpActiveEdit == mxVariable2RangeEdit.get() )
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxVariable2RangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mVariable2Range = *pRange;
+ // Highlight the resulting range.
+ mxVariable2RangeEdit->StartUpdateData();
+ }
+ else
+ {
+ mVariable2Range = ScRange( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ else if ( mpActiveEdit == mxOutputRangeEdit.get() )
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames( aRangeList, mxOutputRangeEdit->GetText(), mDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ mOutputAddress = pRange->aStart;
+
+ // Crop output range to top left address for Edit field.
+ if (pRange->aStart != pRange->aEnd)
+ {
+ ScRefFlags nFormat = ( mOutputAddress.Tab() == mCurrentAddress.Tab() ) ?
+ ScRefFlags::ADDR_ABS :
+ ScRefFlags::ADDR_ABS_3D;
+ OUString aReferenceString = mOutputAddress.Format(nFormat, &mDocument, mDocument.GetAddressConvention());
+ mxOutputRangeEdit->SetRefString( aReferenceString );
+ }
+
+ // Highlight the resulting range.
+ mxOutputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ mOutputAddress = ScAddress( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ }
+
+ ValidateDialogInput();
+}
+
+void ScStatisticsTwoVariableDialog::CalculateInputAndWriteToOutput()
+{
+ OUString aUndo(ScResId(GetUndoNameId()));
+ ScDocShell* pDocShell = mViewData.GetDocShell();
+ SfxUndoManager* pUndoManager = pDocShell->GetUndoManager();
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, mViewData.GetViewShell()->GetViewShellId() );
+
+ ScRange aOutputRange = ApplyOutput(pDocShell);
+
+ pUndoManager->LeaveListAction();
+ pDocShell->PostPaint( aOutputRange, PaintPartFlags::Grid );
+}
+
+bool ScStatisticsTwoVariableDialog::InputRangesValid()
+{
+ return mVariable1Range.IsValid() && mVariable2Range.IsValid() && mOutputAddress.IsValid();
+}
+
+void ScStatisticsTwoVariableDialog::ValidateDialogInput()
+{
+ // Enable OK button if all inputs are ok.
+ mxButtonOk->set_sensitive(InputRangesValid());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/TTestDialog.cxx b/sc/source/ui/StatisticsDialogs/TTestDialog.cxx
new file mode 100644
index 0000000000..864d4ac4f6
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/TTestDialog.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <TTestDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScTTestDialog::ScTTestDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScStatisticsTwoVariableDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/ttestdialog.ui", "TTestDialog")
+{
+ m_xDialog->set_title(ScResId(STR_TTEST));
+}
+
+ScTTestDialog::~ScTTestDialog()
+{}
+
+void ScTTestDialog::Close()
+{
+ DoClose( ScTTestDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScTTestDialog::GetUndoNameId()
+{
+ return STR_TTEST_UNDO_NAME;
+}
+
+ScRange ScTTestDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ std::unique_ptr<DataRangeIterator> pVariable1Iterator;
+ if (mGroupedBy == BY_COLUMN)
+ pVariable1Iterator.reset(new DataRangeByColumnIterator(mVariable1Range));
+ else
+ pVariable1Iterator.reset(new DataRangeByRowIterator(mVariable1Range));
+
+ std::unique_ptr<DataRangeIterator> pVariable2Iterator;
+ if (mGroupedBy == BY_COLUMN)
+ pVariable2Iterator.reset(new DataRangeByColumnIterator(mVariable2Range));
+ else
+ pVariable2Iterator.reset(new DataRangeByRowIterator(mVariable2Range));
+
+ aTemplate.autoReplaceRange("%VARIABLE1_RANGE%", pVariable1Iterator->get());
+ aTemplate.autoReplaceRange("%VARIABLE2_RANGE%", pVariable2Iterator->get());
+
+ aOutput.writeBoldString(ScResId(STR_TTEST_UNDO_NAME));
+ aOutput.newLine();
+
+ // Alpha
+ aOutput.writeString(ScResId(STR_LABEL_ALPHA));
+ aOutput.nextColumn();
+ aOutput.writeValue(0.05);
+ aTemplate.autoReplaceAddress("%ALPHA%", aOutput.current());
+ aOutput.newLine();
+
+ // Hypothesized mean difference
+ aOutput.writeString(ScResId(STR_HYPOTHESIZED_MEAN_DIFFERENCE_LABEL));
+ aOutput.nextColumn();
+ aOutput.writeValue(0);
+ aTemplate.autoReplaceAddress("%HYPOTHESIZED_MEAN_DIFFERENCE%", aOutput.current());
+ aOutput.newLine();
+
+ aOutput.nextColumn();
+ aOutput.writeBoldString(ScResId(STR_VARIABLE_1_LABEL));
+ aOutput.nextColumn();
+ aOutput.writeBoldString(ScResId(STR_VARIABLE_2_LABEL));
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STRID_CALC_MEAN));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ aOutput.writeString(ScResId(STRID_CALC_VARIANCE));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=VAR(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=VAR(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // Observations
+ aOutput.writeString(ScResId(STR_OBSERVATIONS_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=COUNT(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=COUNT(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // Pearson Correlation
+ aOutput.writeString(ScResId(STR_TTEST_PEARSON_CORRELATION));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=CORREL(%VARIABLE1_RANGE%;%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // Observed mean difference
+ aOutput.writeString(ScResId(STR_OBSERVED_MEAN_DIFFERENCE_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(IF(ISODD(IF(ISNUMBER(%VARIABLE1_RANGE%); 1; 0) * IF(ISNUMBER(%VARIABLE2_RANGE%); 1; 0)); %VARIABLE1_RANGE% - %VARIABLE2_RANGE%; \"NA\"))");
+ aOutput.writeMatrixFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%OBSERVED_MEAN_DIFFERENCE%", aOutput.current());
+ aOutput.newLine();
+
+ // Variance of the Differences
+ aOutput.writeString(ScResId(STR_TTEST_VARIANCE_OF_THE_DIFFERENCES));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=VAR(IF(ISODD(IF(ISNUMBER(%VARIABLE1_RANGE%); 1; 0) * IF(ISNUMBER(%VARIABLE2_RANGE%); 1; 0)); %VARIABLE1_RANGE% - %VARIABLE2_RANGE%; \"NA\"))");
+ aOutput.writeMatrixFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%VARIANCE_OF_DIFFERENCES%", aOutput.current());
+ aOutput.newLine();
+
+ // df
+ aOutput.writeString(ScResId(STR_ANOVA_LABEL_DF));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=SUM(IF(ISNUMBER(%VARIABLE1_RANGE%); 1; 0) * IF(ISNUMBER(%VARIABLE2_RANGE%); 1; 0)) - 1");
+ aOutput.writeMatrixFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%DEGREE_OF_FREEDOM%", aOutput.current());
+ aOutput.newLine();
+
+ // t stat
+ aOutput.writeString(ScResId(STR_TTEST_T_STAT));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=(%OBSERVED_MEAN_DIFFERENCE% - %HYPOTHESIZED_MEAN_DIFFERENCE%) / (%VARIANCE_OF_DIFFERENCES% / ( %DEGREE_OF_FREEDOM% + 1)) ^ 0.5");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%T_STAT%", aOutput.current());
+ aOutput.newLine();
+
+ // P one-tail
+ aOutput.writeString(ScResId(STR_TTEST_P_ONE_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=TDIST(ABS(%T_STAT%); %DEGREE_OF_FREEDOM%; 1)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // T critical one-tail
+ aOutput.writeString(ScResId(STR_TTEST_T_CRITICAL_ONE_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=TINV(2*%ALPHA%; %DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // P two-tail
+ aOutput.writeString(ScResId(STR_TTEST_P_TWO_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=TDIST(ABS(%T_STAT%); %DEGREE_OF_FREEDOM%; 2)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // T critical two-tail
+ aOutput.writeString(ScResId(STR_TTEST_T_CRITICAL_TWO_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=TINV(%ALPHA%; %DEGREE_OF_FREEDOM%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+
+ return ScRange(aOutput.mMinimumAddress, aOutput.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx b/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx
new file mode 100644
index 0000000000..be84311285
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx
@@ -0,0 +1,386 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <editeng/editobj.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editutil.hxx>
+
+#include <TableFillingAndNavigationTools.hxx>
+#include <formulacell.hxx>
+#include <docfunc.hxx>
+#include <docsh.hxx>
+
+FormulaTemplate::FormulaTemplate(ScDocument* pDoc)
+ : mpDoc(pDoc)
+ , mbUse3D(true)
+{}
+
+void FormulaTemplate::setTemplate(const OUString& aTemplate)
+{
+ mTemplate = aTemplate;
+}
+
+void FormulaTemplate::setTemplate(const char* aTemplate)
+{
+ mTemplate = OUString::createFromAscii(aTemplate);
+}
+
+const OUString& FormulaTemplate::getTemplate()
+{
+ for (const auto& [rVariable, rRange] : mRangeReplacementMap)
+ {
+ applyRange(rVariable, rRange, mbUse3D);
+ }
+ for (const auto& [rVariable, rAddress] : mAddressReplacementMap)
+ {
+ applyAddress(rVariable, rAddress, mbUse3D);
+ }
+ return mTemplate;
+}
+
+void FormulaTemplate::autoReplaceRange(const OUString& aVariable, const ScRange& rRange)
+{
+ mRangeReplacementMap[aVariable] = rRange;
+}
+
+void FormulaTemplate::autoReplaceAddress(const OUString& aVariable, ScAddress const & aAddress)
+{
+
+ mAddressReplacementMap[aVariable] = aAddress;
+}
+
+void FormulaTemplate::applyRange(std::u16string_view aVariable, const ScRange& aRange, bool b3D)
+{
+ ScRefFlags nFlag = b3D ? ScRefFlags::RANGE_ABS_3D : ScRefFlags::RANGE_ABS;
+ OUString aString = aRange.Format(*mpDoc, nFlag, mpDoc->GetAddressConvention());
+ mTemplate = mTemplate.replaceAll(aVariable, aString);
+}
+
+void FormulaTemplate::applyRangeList(std::u16string_view aVariable, const ScRangeList& aRangeList, sal_Unicode cDelimiter)
+{
+ OUString aString;
+ aRangeList.Format(aString, ScRefFlags::RANGE_ABS_3D, *mpDoc, mpDoc->GetAddressConvention(), cDelimiter);
+ mTemplate = mTemplate.replaceAll(aVariable, aString);
+}
+
+void FormulaTemplate::applyAddress(std::u16string_view aVariable, const ScAddress& aAddress, bool b3D)
+{
+ ScRefFlags nFlag = b3D ? ScRefFlags::ADDR_ABS_3D : ScRefFlags::ADDR_ABS;
+ OUString aString = aAddress.Format(nFlag, mpDoc, mpDoc->GetAddressConvention());
+ mTemplate = mTemplate.replaceAll(aVariable, aString);
+}
+
+void FormulaTemplate::applyString(std::u16string_view aVariable, std::u16string_view aValue)
+{
+ mTemplate = mTemplate.replaceAll(aVariable, aValue);
+}
+
+void FormulaTemplate::applyNumber(std::u16string_view aVariable, sal_Int32 aValue)
+{
+ mTemplate = mTemplate.replaceAll(aVariable, OUString::number(aValue));
+}
+
+AddressWalker::AddressWalker(const ScAddress& aInitialAddress) :
+ mCurrentAddress(aInitialAddress),
+ mMinimumAddress(aInitialAddress),
+ mMaximumAddress(aInitialAddress)
+{
+ mAddressStack.push_back(mCurrentAddress);
+}
+
+void AddressWalker::resetColumn()
+{
+ mCurrentAddress.SetCol(mAddressStack.back().Col());
+}
+
+void AddressWalker::resetRow()
+{
+ mCurrentAddress.SetRow(mAddressStack.back().Row());
+}
+
+void AddressWalker::reset()
+{
+ mCurrentAddress = mAddressStack.back();
+}
+
+void AddressWalker::newLine()
+{
+ resetColumn();
+ nextRow();
+}
+
+ScAddress AddressWalker::current(SCCOL aRelCol, SCROW aRelRow, SCTAB aRelTab)
+{
+ return ScAddress(
+ mCurrentAddress.Col() + aRelCol,
+ mCurrentAddress.Row() + aRelRow,
+ mCurrentAddress.Tab() + aRelTab);
+}
+
+void AddressWalker::nextColumn()
+{
+ mCurrentAddress.IncCol();
+
+ if(mMaximumAddress.Col() < mCurrentAddress.Col())
+ mMaximumAddress.SetCol(mCurrentAddress.Col());
+}
+
+void AddressWalker::nextRow()
+{
+ mCurrentAddress.IncRow();
+ if(mMaximumAddress.Row() < mCurrentAddress.Row())
+ mMaximumAddress.SetRow(mCurrentAddress.Row());
+}
+
+void AddressWalker::push(SCCOL aRelativeCol, SCROW aRelativeRow, SCTAB aRelativeTab)
+{
+ mCurrentAddress = current(aRelativeCol, aRelativeRow, aRelativeTab);
+ mAddressStack.push_back(mCurrentAddress);
+}
+
+AddressWalkerWriter::AddressWalkerWriter(const ScAddress& aInitialAddress, ScDocShell* pDocShell, ScDocument& rDocument,
+ formula::FormulaGrammar::Grammar eGrammar ) :
+ AddressWalker(aInitialAddress),
+ mpDocShell(pDocShell),
+ mrDocument(rDocument),
+ meGrammar(eGrammar)
+{}
+
+void AddressWalkerWriter::writeFormula(const OUString& aFormula)
+{
+ mpDocShell->GetDocFunc().SetFormulaCell(mCurrentAddress,
+ new ScFormulaCell(mrDocument, mCurrentAddress, aFormula, meGrammar), true);
+}
+
+void AddressWalkerWriter::writeFormulas(const std::vector<OUString>& rFormulas)
+{
+ size_t nLength = rFormulas.size();
+ if (!nLength)
+ return;
+
+ const size_t nMaxLen = mpDocShell->GetDocument().MaxRow() - mCurrentAddress.Row() + 1;
+ // If not done already, trim the length to fit.
+ if (nLength > nMaxLen)
+ nLength = nMaxLen;
+
+ std::vector<ScFormulaCell*> aFormulaCells(nLength);
+ ScAddress aAddr(mCurrentAddress);
+ for (size_t nIdx = 0; nIdx < nLength; ++nIdx)
+ {
+ aFormulaCells[nIdx] = new ScFormulaCell(mrDocument, aAddr, rFormulas[nIdx], meGrammar);
+ aAddr.IncRow(1);
+ }
+
+ mpDocShell->GetDocFunc().SetFormulaCells(mCurrentAddress, aFormulaCells, true);
+}
+
+void AddressWalkerWriter::writeMatrixFormula(const OUString& aFormula, SCCOL nCols, SCROW nRows)
+{
+ ScRange aRange;
+ aRange.aStart = mCurrentAddress;
+ aRange.aEnd = mCurrentAddress;
+ if (nCols > 1)
+ aRange.aEnd.IncCol(nCols - 1);
+ if (nRows > 1)
+ aRange.aEnd.IncRow(nRows - 1);
+ mpDocShell->GetDocFunc().EnterMatrix(aRange, nullptr, nullptr, aFormula, false, false, OUString(), meGrammar );
+}
+
+void AddressWalkerWriter::writeString(const OUString& aString)
+{
+ mpDocShell->GetDocFunc().SetStringCell(mCurrentAddress, aString, true);
+}
+
+void AddressWalkerWriter::writeString(const char* aCharArray)
+{
+ writeString(OUString::createFromAscii(aCharArray));
+}
+
+void AddressWalkerWriter::writeBoldString(const OUString& aString)
+{
+ ScFieldEditEngine& rEngine = mrDocument.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aString);
+ SfxItemSet aItemSet = rEngine.GetEmptyItemSet();
+ SvxWeightItem aWeight(WEIGHT_BOLD, EE_CHAR_WEIGHT);
+ aItemSet.Put(aWeight);
+ rEngine.QuickSetAttribs(aItemSet, ESelection(0, 0, 0, aString.getLength()) );
+ std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject());
+ mpDocShell->GetDocFunc().SetEditCell(mCurrentAddress, *pEditText, true);
+}
+
+void AddressWalkerWriter::writeValue(double aValue)
+{
+ mpDocShell->GetDocFunc().SetValueCell(mCurrentAddress, aValue, true);
+}
+
+// DataCellIterator
+
+DataCellIterator::DataCellIterator(const ScRange& aInputRange, bool aByColumn)
+ : mInputRange(aInputRange)
+ , mByColumn(aByColumn)
+ , mCol(0)
+ , mRow(0)
+{
+ if(aByColumn)
+ mCol = aInputRange.aStart.Col();
+ else
+ mRow = aInputRange.aStart.Row();
+}
+
+bool DataCellIterator::hasNext() const
+{
+ if(mByColumn)
+ return mCol <= mInputRange.aEnd.Col();
+ else
+ return mRow <= mInputRange.aEnd.Row();
+}
+
+void DataCellIterator::next()
+{
+ if(mByColumn)
+ mCol++;
+ else
+ mRow++;
+}
+
+ScAddress DataCellIterator::get()
+{
+ return getRelative(0);
+}
+
+ScAddress DataCellIterator::getRelative(int aDelta)
+{
+ if(mByColumn)
+ {
+ SCCOL aNewColumn = mCol + aDelta;
+ if(aNewColumn < mInputRange.aStart.Col() || aNewColumn > mInputRange.aEnd.Col())
+ {
+ ScAddress aResult;
+ aResult.SetInvalid();
+ return aResult;
+ }
+ return ScAddress(aNewColumn, mInputRange.aStart.Row(), mInputRange.aStart.Tab());
+ }
+ else
+ {
+ SCROW aNewRow = mRow + aDelta;
+ if(aNewRow < mInputRange.aStart.Row() || aNewRow > mInputRange.aEnd.Row())
+ {
+ ScAddress aResult;
+ aResult.SetInvalid();
+ return aResult;
+ }
+ return ScAddress(mInputRange.aStart.Col(), aNewRow, mInputRange.aStart.Tab());
+ }
+}
+
+// DataRangeIterator
+
+DataRangeIterator::DataRangeIterator(const ScRange& aInputRange) :
+ mInputRange(aInputRange),
+ mIndex(0)
+{}
+
+DataRangeIterator::~DataRangeIterator()
+{}
+
+sal_Int32 DataRangeIterator::index()
+{
+ return mIndex;
+}
+
+// DataRangeByColumnIterator
+
+DataRangeByColumnIterator::DataRangeByColumnIterator(const ScRange& aInputRange)
+ : DataRangeIterator(aInputRange)
+ , mCol(aInputRange.aStart.Col())
+{}
+
+bool DataRangeByColumnIterator::hasNext()
+{
+ return mCol <= mInputRange.aEnd.Col();
+}
+
+void DataRangeByColumnIterator::next()
+{
+ mCol++;
+ mIndex++;
+}
+
+ScRange DataRangeByColumnIterator::get()
+{
+ return ScRange(
+ ScAddress(mCol, mInputRange.aStart.Row(), mInputRange.aStart.Tab()),
+ ScAddress(mCol, mInputRange.aEnd.Row(), mInputRange.aEnd.Tab())
+ );
+}
+
+size_t DataRangeByColumnIterator::size()
+{
+ return mInputRange.aEnd.Row() - mInputRange.aStart.Row() + 1;
+}
+
+void DataRangeByColumnIterator::reset()
+{
+ mCol = mInputRange.aStart.Col();
+}
+
+DataCellIterator DataRangeByColumnIterator::iterateCells()
+{
+ return DataCellIterator(get(), false);
+}
+
+// DataRangeByRowIterator
+
+DataRangeByRowIterator::DataRangeByRowIterator(const ScRange& aInputRange)
+ : DataRangeIterator(aInputRange)
+ , mRow(aInputRange.aStart.Row())
+{}
+
+bool DataRangeByRowIterator::hasNext()
+{
+ return mRow <= mInputRange.aEnd.Row();
+}
+
+void DataRangeByRowIterator::next()
+{
+ mRow++;
+ mIndex++;
+}
+
+ScRange DataRangeByRowIterator::get()
+{
+ return ScRange(
+ ScAddress(mInputRange.aStart.Col(), mRow, mInputRange.aStart.Tab()),
+ ScAddress(mInputRange.aEnd.Col(), mRow, mInputRange.aEnd.Tab())
+ );
+}
+
+size_t DataRangeByRowIterator::size()
+{
+ return mInputRange.aEnd.Col() - mInputRange.aStart.Col() + 1;
+}
+
+void DataRangeByRowIterator::reset()
+{
+ mRow = mInputRange.aStart.Row();
+}
+
+DataCellIterator DataRangeByRowIterator::iterateCells()
+{
+ return DataCellIterator(get(), true);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/StatisticsDialogs/ZTestDialog.cxx b/sc/source/ui/StatisticsDialogs/ZTestDialog.cxx
new file mode 100644
index 0000000000..a1731fa8f0
--- /dev/null
+++ b/sc/source/ui/StatisticsDialogs/ZTestDialog.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/.
+ *
+ */
+
+#include <memory>
+
+#include <reffact.hxx>
+#include <TableFillingAndNavigationTools.hxx>
+#include <ZTestDialog.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScZTestDialog::ScZTestDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData ) :
+ ScStatisticsTwoVariableDialog(
+ pSfxBindings, pChildWindow, pParent, rViewData,
+ "modules/scalc/ui/ztestdialog.ui", "ZTestDialog")
+{
+ m_xDialog->set_title(ScResId(STR_ZTEST));
+}
+
+ScZTestDialog::~ScZTestDialog()
+{}
+
+void ScZTestDialog::Close()
+{
+ DoClose( ScZTestDialogWrapper::GetChildWindowId() );
+}
+
+TranslateId ScZTestDialog::GetUndoNameId()
+{
+ return STR_ZTEST_UNDO_NAME;
+}
+
+ScRange ScZTestDialog::ApplyOutput(ScDocShell* pDocShell)
+{
+ AddressWalkerWriter aOutput(mOutputAddress, pDocShell, mDocument,
+ formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
+ FormulaTemplate aTemplate(&mDocument);
+
+ std::unique_ptr<DataRangeIterator> pVariable1Iterator;
+ if (mGroupedBy == BY_COLUMN)
+ pVariable1Iterator.reset(new DataRangeByColumnIterator(mVariable1Range));
+ else
+ pVariable1Iterator.reset(new DataRangeByRowIterator(mVariable1Range));
+
+ std::unique_ptr<DataRangeIterator> pVariable2Iterator;
+ if (mGroupedBy == BY_COLUMN)
+ pVariable2Iterator.reset(new DataRangeByColumnIterator(mVariable2Range));
+ else
+ pVariable2Iterator.reset(new DataRangeByRowIterator(mVariable2Range));
+
+ aTemplate.autoReplaceRange("%VARIABLE1_RANGE%", pVariable1Iterator->get());
+ aTemplate.autoReplaceRange("%VARIABLE2_RANGE%", pVariable2Iterator->get());
+
+ aOutput.writeBoldString(ScResId(STR_ZTEST));
+ aOutput.newLine();
+
+ // Alpha
+ aOutput.writeString(ScResId(STR_LABEL_ALPHA));
+ aOutput.nextColumn();
+ aOutput.writeValue(0.05);
+ aTemplate.autoReplaceAddress("%ALPHA%", aOutput.current());
+ aOutput.newLine();
+
+ // Hypothesized mean difference
+ aOutput.writeString(ScResId(STR_HYPOTHESIZED_MEAN_DIFFERENCE_LABEL));
+ aOutput.nextColumn();
+ aOutput.writeValue(0);
+ aTemplate.autoReplaceAddress("%HYPOTHESIZED_MEAN_DIFFERENCE%", aOutput.current());
+ aOutput.newLine();
+
+ // Variable Label
+ aOutput.nextColumn();
+ aOutput.writeBoldString(ScResId(STR_VARIABLE_1_LABEL));
+ aOutput.nextColumn();
+ aOutput.writeBoldString(ScResId(STR_VARIABLE_2_LABEL));
+ aOutput.newLine();
+
+ // Known Variance
+ aOutput.writeString(ScResId(STR_ZTEST_KNOWN_VARIANCE));
+ aOutput.nextColumn();
+ aOutput.writeValue(0);
+ aTemplate.autoReplaceAddress("%KNOWN_VARIANCE_VARIABLE1%", aOutput.current());
+ aOutput.nextColumn();
+ aOutput.writeValue(0);
+ aTemplate.autoReplaceAddress("%KNOWN_VARIANCE_VARIABLE2%", aOutput.current());
+ aOutput.newLine();
+
+ // Mean
+ aOutput.writeString(ScResId(STRID_CALC_MEAN));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(%VARIABLE1_RANGE%)");
+ aTemplate.autoReplaceAddress("%MEAN_VARIABLE1%", aOutput.current());
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=AVERAGE(%VARIABLE2_RANGE%)");
+ aTemplate.autoReplaceAddress("%MEAN_VARIABLE2%", aOutput.current());
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // Observations
+ aOutput.writeString(ScResId(STR_OBSERVATIONS_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=COUNT(%VARIABLE1_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%OBSERVATION_VARIABLE1%", aOutput.current());
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=COUNT(%VARIABLE2_RANGE%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%OBSERVATION_VARIABLE2%", aOutput.current());
+ aOutput.newLine();
+
+ // Observed mean difference
+ aOutput.writeString(ScResId(STR_OBSERVED_MEAN_DIFFERENCE_LABEL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=%MEAN_VARIABLE1% - %MEAN_VARIABLE2%");
+ aOutput.writeMatrixFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%OBSERVED_MEAN_DIFFERENCE%", aOutput.current());
+ aOutput.newLine();
+
+ // z
+ aOutput.writeString(ScResId(STR_ZTEST_Z_VALUE));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=(%OBSERVED_MEAN_DIFFERENCE% - %HYPOTHESIZED_MEAN_DIFFERENCE%) / SQRT( %KNOWN_VARIANCE_VARIABLE1% / %OBSERVATION_VARIABLE1% + %KNOWN_VARIANCE_VARIABLE2% / %OBSERVATION_VARIABLE2% )");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aTemplate.autoReplaceAddress("%Z_STAT%", aOutput.current());
+ aOutput.newLine();
+
+ // P one-tail
+ aOutput.writeString(ScResId(STR_ZTEST_P_ONE_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=1 - NORMSDIST(ABS(%Z_STAT%))");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // z critical one-tail
+ aOutput.writeString(ScResId(STR_ZTEST_Z_CRITICAL_ONE_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=-NORMSINV(%ALPHA%)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // P two-tail
+ aOutput.writeString(ScResId(STR_ZTEST_P_TWO_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=2 * NORMSDIST(-ABS(%Z_STAT%))");
+ aOutput.writeFormula(aTemplate.getTemplate());
+ aOutput.newLine();
+
+ // z critical two-tail
+ aOutput.writeString(ScResId(STR_ZTEST_Z_CRITICAL_TWO_TAIL));
+ aOutput.nextColumn();
+ aTemplate.setTemplate("=-NORMSINV(%ALPHA%/2)");
+ aOutput.writeFormula(aTemplate.getTemplate());
+
+ return ScRange(aOutput.mMinimumAddress, aOutput.mMaximumAddress);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/client.cxx b/sc/source/ui/app/client.cxx
new file mode 100644
index 0000000000..5c44bc077e
--- /dev/null
+++ b/sc/source/ui/app/client.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 <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdoole2.hxx>
+
+#include <client.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+
+using namespace com::sun::star;
+
+ScClient::ScClient( ScTabViewShell* pViewShell, vcl::Window* pDraw, SdrModel* pSdrModel, const SdrOle2Obj* pObj ) :
+ SfxInPlaceClient( pViewShell, pDraw, pObj->GetAspect() ),
+ pModel( pSdrModel )
+{
+ SetObject( pObj->GetObjRef() );
+}
+
+ScClient::~ScClient()
+{
+}
+
+SdrOle2Obj* ScClient::GetDrawObj()
+{
+ uno::Reference < embed::XEmbeddedObject > xObj = GetObject();
+ SdrOle2Obj* pOle2Obj = nullptr;
+ OUString aName = GetViewShell()->GetObjectShell()->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xObj );
+
+ sal_uInt16 nPages = pModel->GetPageCount();
+ for (sal_uInt16 nPNr=0; nPNr<nPages && !pOle2Obj; nPNr++)
+ {
+ SdrPage* pPage = pModel->GetPage(nPNr);
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !pOle2Obj)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ // name from InfoObject is PersistName
+ if ( static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == aName )
+ pOle2Obj = static_cast<SdrOle2Obj*>(pObject);
+ }
+ pObject = aIter.Next();
+ }
+ }
+ return pOle2Obj;
+}
+
+void ScClient::RequestNewObjectArea( tools::Rectangle& aLogicRect )
+{
+ SfxViewShell* pSfxViewSh = GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSfxViewSh );
+ if (!pViewSh)
+ {
+ OSL_FAIL("Wrong ViewShell");
+ return;
+ }
+
+ tools::Rectangle aOldRect = GetObjArea();
+ SdrOle2Obj* pDrawObj = GetDrawObj();
+ if ( pDrawObj )
+ {
+ if ( pDrawObj->IsResizeProtect() )
+ aLogicRect.SetSize( aOldRect.GetSize() );
+
+ if ( pDrawObj->IsMoveProtect() )
+ aLogicRect.SetPos( aOldRect.TopLeft() );
+ }
+
+ sal_uInt16 nTab = pViewSh->GetViewData().GetTabNo();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
+ if ( !(pPage && aLogicRect != aOldRect) )
+ return;
+
+ Point aPos;
+ Size aSize = pPage->GetSize();
+ if ( aSize.Width() < 0 )
+ {
+ aPos.setX( aSize.Width() + 1 ); // negative
+ aSize.setWidth( -aSize.Width() ); // positive
+ }
+ tools::Rectangle aPageRect( aPos, aSize );
+
+ if (aLogicRect.Right() > aPageRect.Right())
+ {
+ tools::Long nDiff = aLogicRect.Right() - aPageRect.Right();
+ aLogicRect.AdjustLeft( -nDiff );
+ aLogicRect.AdjustRight( -nDiff );
+ }
+ if (aLogicRect.Bottom() > aPageRect.Bottom())
+ {
+ tools::Long nDiff = aLogicRect.Bottom() - aPageRect.Bottom();
+ aLogicRect.AdjustTop( -nDiff );
+ aLogicRect.AdjustBottom( -nDiff );
+ }
+
+ if (aLogicRect.Left() < aPageRect.Left())
+ {
+ tools::Long nDiff = aLogicRect.Left() - aPageRect.Left();
+ aLogicRect.AdjustRight( -nDiff );
+ aLogicRect.AdjustLeft( -nDiff );
+ }
+ if (aLogicRect.Top() < aPageRect.Top())
+ {
+ tools::Long nDiff = aLogicRect.Top() - aPageRect.Top();
+ aLogicRect.AdjustBottom( -nDiff );
+ aLogicRect.AdjustTop( -nDiff );
+ }
+}
+
+void ScClient::ObjectAreaChanged()
+{
+ SfxViewShell* pSfxViewSh = GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSfxViewSh );
+ if (!pViewSh)
+ {
+ OSL_FAIL("Wrong ViewShell");
+ return;
+ }
+
+ // Take over position and size into document
+ SdrOle2Obj* pDrawObj = GetDrawObj();
+ if (!pDrawObj)
+ return;
+
+ tools::Rectangle aNewRectangle(GetScaledObjArea());
+
+ // #i118524# if sheared/rotated, center to non-rotated LogicRect
+ pDrawObj->setSuppressSetVisAreaSize(true);
+
+ if(pDrawObj->GetGeoStat().m_nRotationAngle || pDrawObj->GetGeoStat().m_nShearAngle)
+ {
+ pDrawObj->SetLogicRect( aNewRectangle );
+
+ const tools::Rectangle& rBoundRect = pDrawObj->GetCurrentBoundRect();
+ const Point aDelta(aNewRectangle.Center() - rBoundRect.Center());
+
+ aNewRectangle.Move(aDelta.X(), aDelta.Y());
+ }
+
+ pDrawObj->SetLogicRect( aNewRectangle );
+ pDrawObj->setSuppressSetVisAreaSize(false);
+
+ // set document modified (SdrModel::SetChanged is not used)
+ pViewSh->GetViewData().GetDocShell()->SetDrawModified();
+ pViewSh->ScrollToObject(pDrawObj);
+}
+
+void ScClient::ViewChanged()
+{
+ if ( GetAspect() == embed::Aspects::MSOLE_ICON )
+ {
+ // the iconified object seems not to need such a scaling handling
+ // since the replacement image and the size a completely controlled by the container
+ // TODO/LATER: when the icon exchange is implemented the scaling handling might be required again here
+
+ return;
+ }
+
+ uno::Reference < embed::XEmbeddedObject > xObj = GetObject();
+ if (!xObj.is())
+ return;
+
+ // TODO/LEAN: working with Visual Area can switch object to running state
+ awt::Size aSz;
+ try {
+ aSz = xObj->getVisualAreaSize( GetAspect() );
+ } catch (const uno::Exception&) {
+ TOOLS_WARN_EXCEPTION("sc", "The visual area size must be available!");
+ return; // leave it unchanged on failure
+ }
+
+ MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( GetAspect() ) );
+ Size aVisSize = OutputDevice::LogicToLogic(Size(aSz.Width, aSz.Height), MapMode(aMapUnit), MapMode(MapUnit::Map100thMM));
+
+ // Take over position and size into document
+ SdrOle2Obj* pDrawObj = GetDrawObj();
+ if (!pDrawObj)
+ return;
+
+ if (!IsObjectInPlaceActive())
+ {
+ pDrawObj->ActionChanged();
+ return;
+ }
+
+ tools::Rectangle aLogicRect = pDrawObj->GetLogicRect();
+ Fraction aFractX = GetScaleWidth() * aVisSize.Width();
+ Fraction aFractY = GetScaleHeight() * aVisSize.Height();
+ aVisSize = Size( static_cast<tools::Long>(aFractX), static_cast<tools::Long>(aFractY) ); // Scaled for Draw model
+
+ // pClientData->SetObjArea before pDrawObj->SetLogicRect, so that we don't
+ // calculate wrong scalings:
+ //Rectangle aObjArea = aLogicRect;
+ //aObjArea.SetSize( aVisSize ); // Document size from the server
+ //SetObjArea( aObjArea );
+
+ SfxViewShell* pSfxViewSh = GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSfxViewSh );
+ if ( pViewSh )
+ {
+ vcl::Window* pWin = pViewSh->GetActiveWin();
+ if ( pWin->LogicToPixel( aVisSize ) != pWin->LogicToPixel( aLogicRect.GetSize() ) )
+ {
+ aLogicRect.SetSize( aVisSize );
+ pDrawObj->SetLogicRect( aLogicRect );
+
+ // set document modified (SdrModel::SetChanged is not used)
+ pViewSh->GetViewData().GetDocShell()->SetDrawModified();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/drwtrans.cxx b/sc/source/ui/app/drwtrans.cxx
new file mode 100644
index 0000000000..8dae3e5f33
--- /dev/null
+++ b/sc/source/ui/app/drwtrans.cxx
@@ -0,0 +1,734 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <unotools/streamwrap.hxx>
+
+#include <svx/unomodel.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <svtools/embedtransfer.hxx>
+#include <sot/storage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <sfx2/docfile.hxx>
+#include <svl/itempool.hxx>
+#include <svl/urlbmk.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+
+#include <drwtrans.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <drawview.hxx>
+#include <utility>
+#include <viewdata.hxx>
+#include <scmod.hxx>
+#include <dragdata.hxx>
+#include <stlpool.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+
+#include <editeng/eeitem.hxx>
+
+#include <editeng/fhgtitem.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star;
+
+constexpr sal_uInt32 SCDRAWTRANS_TYPE_EMBOBJ = 1;
+constexpr sal_uInt32 SCDRAWTRANS_TYPE_DRAWMODEL = 2;
+constexpr sal_uInt32 SCDRAWTRANS_TYPE_DOCUMENT = 3;
+
+ScDrawTransferObj::ScDrawTransferObj( std::unique_ptr<SdrModel> pClipModel, ScDocShell* pContainerShell,
+ TransferableObjectDescriptor aDesc ) :
+ m_pModel( std::move(pClipModel) ),
+ m_aObjDesc(std::move( aDesc )),
+ m_bGraphic( false ),
+ m_bGrIsBit( false ),
+ m_bOleObj( false ),
+ m_nDragSourceFlags( ScDragSrc::Undefined ),
+ m_bDragWasInternal( false ),
+ maShellID(SfxObjectShell::CreateShellID(pContainerShell))
+{
+
+ // check what kind of objects are contained
+
+ SdrPage* pPage = m_pModel->GetPage(0);
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ if (pObject && !aIter.Next()) // exactly one object?
+ {
+
+ // OLE object
+
+ SdrObjKind nSdrObjKind = pObject->GetObjIdentifier();
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ // if object has no persistence it must be copied as a part of document
+ try
+ {
+ uno::Reference< embed::XEmbedPersist > xPersObj( static_cast<SdrOle2Obj*>(pObject)->GetObjRef(), uno::UNO_QUERY );
+ if ( xPersObj.is() && xPersObj->hasEntry() )
+ m_bOleObj = true;
+ }
+ catch( uno::Exception& )
+ {}
+ // aOleData is initialized later
+ }
+
+ // Graphic object
+
+ if (nSdrObjKind == SdrObjKind::Graphic)
+ {
+ m_bGraphic = true;
+ if ( static_cast<SdrGrafObj*>(pObject)->GetGraphic().GetType() == GraphicType::Bitmap )
+ m_bGrIsBit = true;
+ }
+
+ // URL button
+
+ SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObject );
+ if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor())
+ {
+ const uno::Reference<awt::XControlModel>& xControlModel = pUnoCtrl->GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "uno control without model" );
+ if ( xControlModel.is() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo();
+
+ OUString sPropButtonType( "ButtonType" );
+
+ if(xInfo->hasPropertyByName( sPropButtonType ))
+ {
+ uno::Any aAny = xPropSet->getPropertyValue( sPropButtonType );
+ form::FormButtonType eTmp;
+ if ( (aAny >>= eTmp) && eTmp == form::FormButtonType_URL )
+ {
+ // URL
+ OUString sPropTargetURL( "TargetURL" );
+ if(xInfo->hasPropertyByName( sPropTargetURL ))
+ {
+ aAny = xPropSet->getPropertyValue( sPropTargetURL );
+ OUString sTmp;
+ if ( (aAny >>= sTmp) && !sTmp.isEmpty() )
+ {
+ OUString aUrl = sTmp;
+ OUString aAbs = aUrl;
+ if (pContainerShell)
+ {
+ const SfxMedium* pMedium = pContainerShell->GetMedium();
+ if (pMedium)
+ {
+ bool bWasAbs = true;
+ aAbs = pMedium->GetURLObject().smartRel2Abs( aUrl, bWasAbs ).
+ GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ // full path as stored INetBookmark must be encoded
+ }
+ }
+
+ // Label
+ OUString aLabel;
+ OUString sPropLabel( "Label" );
+ if(xInfo->hasPropertyByName( sPropLabel ))
+ {
+ aAny = xPropSet->getPropertyValue( sPropLabel );
+ if ( (aAny >>= sTmp) && !sTmp.isEmpty() )
+ {
+ aLabel = sTmp;
+ }
+ }
+ m_oBookmark.emplace( aAbs, aLabel );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // get size for object descriptor
+
+ // #i71538# use complete SdrViews
+ // SdrExchangeView aView(pModel);
+ SdrView aView(*m_pModel);
+ SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel().GetPage(0));
+ aView.MarkAllObj(pPv);
+ m_aSrcSize = aView.GetAllMarkedRect().GetSize();
+
+ if ( m_bOleObj ) // single OLE object
+ {
+ SdrOle2Obj* pObj = GetSingleObject();
+ if ( pObj && pObj->GetObjRef().is() )
+ SvEmbedTransferHelper::FillTransferableObjectDescriptor( m_aObjDesc, pObj->GetObjRef(), pObj->GetGraphic(), pObj->GetAspect() );
+ }
+
+ m_aObjDesc.maSize = m_aSrcSize;
+ PrepareOLE( m_aObjDesc );
+
+ // remember a unique ID of the source document
+
+ if ( pContainerShell )
+ {
+ ScDocument& rDoc = pContainerShell->GetDocument();
+ if ( pPage )
+ {
+ ScChartHelper::FillProtectedChartRangesVector( m_aProtectedChartRangesVector, rDoc, pPage );
+ }
+ }
+}
+
+ScDrawTransferObj::~ScDrawTransferObj()
+{
+ SolarMutexGuard aSolarGuard;
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod && pScMod->GetDragData().pDrawTransfer == this)
+ {
+ OSL_FAIL("ScDrawTransferObj wasn't released");
+ pScMod->ResetDragObject();
+ }
+
+ m_aOleData = TransferableDataHelper(); // clear before releasing the mutex
+ m_aDocShellRef.clear();
+
+ m_pModel.reset();
+ m_aDrawPersistRef.clear(); // after the model
+
+ m_oBookmark.reset();
+ m_pDragSourceView.reset();
+}
+
+ScDrawTransferObj* ScDrawTransferObj::GetOwnClipboard(const uno::Reference<datatransfer::XTransferable2>& xTransferable)
+{
+ return dynamic_cast<ScDrawTransferObj*>(xTransferable.get());
+}
+
+static bool lcl_HasOnlyControls( SdrModel* pModel )
+{
+ bool bOnlyControls = false; // default if there are no objects
+
+ if ( pModel )
+ {
+ SdrPage* pPage = pModel->GetPage(0);
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObj = aIter.Next();
+ if ( pObj )
+ {
+ bOnlyControls = true; // only set if there are any objects at all
+ while ( pObj )
+ {
+ if (dynamic_cast<const SdrUnoObj*>( pObj) == nullptr)
+ {
+ bOnlyControls = false;
+ break;
+ }
+ pObj = aIter.Next();
+ }
+ }
+ }
+ }
+
+ return bOnlyControls;
+}
+
+void ScDrawTransferObj::AddSupportedFormats()
+{
+ if ( m_bGrIsBit ) // single bitmap graphic
+ {
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::SVXB );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ }
+ else if ( m_bGraphic ) // other graphic
+ {
+ // #i25616#
+ AddFormat( SotClipboardFormatId::DRAWING );
+
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::SVXB );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ }
+ else if ( m_oBookmark ) // url button
+ {
+// AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::SOLK );
+ AddFormat( SotClipboardFormatId::STRING );
+ AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR );
+ AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK );
+ AddFormat( SotClipboardFormatId::DRAWING );
+ }
+ else if ( m_bOleObj ) // single OLE object
+ {
+ AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+
+ CreateOLEData();
+
+ if ( m_aOleData.GetTransferable().is() )
+ {
+ // get format list from object snapshot
+ // (this must be after inserting the default formats!)
+
+ DataFlavorExVector aVector( m_aOleData.GetDataFlavorExVector() );
+
+ for( const auto& rItem : aVector )
+ AddFormat( rItem );
+ }
+ }
+ else // any drawing objects
+ {
+ AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::DRAWING );
+
+ // leave out bitmap and metafile if there are only controls
+ if ( !lcl_HasOnlyControls( m_pModel.get() ) )
+ {
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ }
+ }
+
+// if( pImageMap )
+// AddFormat( SotClipboardFormatId::SVIM );
+}
+
+bool ScDrawTransferObj::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc )
+{
+ bool bOK = false;
+ SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
+
+ if ( m_bOleObj && nFormat != SotClipboardFormatId::GDIMETAFILE )
+ {
+ CreateOLEData();
+
+ if( m_aOleData.GetTransferable().is() && m_aOleData.HasFormat( rFlavor ) )
+ {
+ bOK = SetAny( m_aOleData.GetAny(rFlavor, rDestDoc) );
+
+ return bOK;
+ }
+ }
+
+ if( HasFormat( nFormat ) )
+ {
+ if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR )
+ {
+ bOK = SetTransferableObjectDescriptor( m_aObjDesc );
+ }
+ else if ( nFormat == SotClipboardFormatId::DRAWING )
+ {
+ SdrView aView(*m_pModel);
+ SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel().GetPage(0));
+ aView.MarkAllObj( pPv );
+ auto pNewModel = aView.CreateMarkedObjModel();
+ bOK = SetObject( pNewModel.get(), SCDRAWTRANS_TYPE_DRAWMODEL, rFlavor );
+ }
+ else if ( nFormat == SotClipboardFormatId::BITMAP
+ || nFormat == SotClipboardFormatId::PNG
+ || nFormat == SotClipboardFormatId::GDIMETAFILE )
+ {
+ // #i71538# use complete SdrViews
+ // SdrExchangeView aView( pModel );
+ SdrView aView(*m_pModel);
+ SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel().GetPage(0));
+ OSL_ENSURE( pPv, "pPv not there..." );
+ aView.MarkAllObj( pPv );
+ if ( nFormat == SotClipboardFormatId::GDIMETAFILE )
+ bOK = SetGDIMetaFile( aView.GetMarkedObjMetaFile(true) );
+ else
+ bOK = SetBitmapEx( aView.GetMarkedObjBitmapEx(true), rFlavor );
+ }
+ else if ( nFormat == SotClipboardFormatId::SVXB )
+ {
+ // only enabled for single graphics object
+
+ SdrPage* pPage = m_pModel->GetPage(0);
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ if (pObject && pObject->GetObjIdentifier() == SdrObjKind::Graphic)
+ {
+ SdrGrafObj* pGraphObj = static_cast<SdrGrafObj*>(pObject);
+ bOK = SetGraphic( pGraphObj->GetGraphic() );
+ }
+ }
+ }
+ else if ( nFormat == SotClipboardFormatId::EMBED_SOURCE )
+ {
+ if ( m_bOleObj ) // single OLE object
+ {
+ SdrOle2Obj* pObj = GetSingleObject();
+ if ( pObj && pObj->GetObjRef().is() )
+ {
+ bOK = SetObject( pObj->GetObjRef().get(), SCDRAWTRANS_TYPE_EMBOBJ, rFlavor );
+ }
+ }
+ else // create object from contents
+ {
+ //TODO/LATER: needs new Format, because now single OLE and "this" are different
+ InitDocShell(); // set aDocShellRef
+
+ SfxObjectShell* pEmbObj = m_aDocShellRef.get();
+ bOK = SetObject( pEmbObj, SCDRAWTRANS_TYPE_DOCUMENT, rFlavor );
+ }
+ }
+ else if( m_oBookmark )
+ {
+ bOK = SetINetBookmark( *m_oBookmark, rFlavor );
+ }
+ }
+ return bOK;
+}
+
+bool ScDrawTransferObj::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId,
+ const css::datatransfer::DataFlavor& /* rFlavor */ )
+{
+ // called from SetObject, put data into stream
+
+ bool bRet = false;
+ switch (nUserObjectId)
+ {
+ case SCDRAWTRANS_TYPE_DRAWMODEL:
+ {
+ SdrModel* pDrawModel = static_cast<SdrModel*>(pUserObject);
+ pDrawModel->BurnInStyleSheetAttributes();
+ rxOStm->SetBufferSize( 0xff00 );
+
+ // for the changed pool defaults from drawing layer pool set those
+ // attributes as hard attributes to preserve them for saving
+ const SfxItemPool& rItemPool = pDrawModel->GetItemPool();
+ const SvxFontHeightItem& rDefaultFontHeight = rItemPool.GetDefaultItem(EE_CHAR_FONTHEIGHT);
+
+ // SW should have no MasterPages
+ OSL_ENSURE(0 == pDrawModel->GetMasterPageCount(), "SW with MasterPages (!)");
+
+ for(sal_uInt16 a(0); a < pDrawModel->GetPageCount(); a++)
+ {
+ const SdrPage* pPage(pDrawModel->GetPage(a));
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj = aIter.Next();
+ const SvxFontHeightItem& rItem = pObj->GetMergedItem(EE_CHAR_FONTHEIGHT);
+
+ if(rItem.GetHeight() == rDefaultFontHeight.GetHeight())
+ {
+ pObj->SetMergedItem(rDefaultFontHeight);
+ }
+ }
+ }
+
+ {
+ css::uno::Reference<css::io::XOutputStream> xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) );
+ SvxDrawingLayerExport( pDrawModel, xDocOut );
+ }
+
+ bRet = ( rxOStm->GetError() == ERRCODE_NONE );
+ }
+ break;
+
+ case SCDRAWTRANS_TYPE_EMBOBJ:
+ {
+ // impl. for "single OLE"
+ embed::XEmbeddedObject* pEmbObj = static_cast<embed::XEmbeddedObject*>(pUserObject);
+
+ ::utl::TempFileFast aTempFile;
+ SvStream* pTempStream = aTempFile.GetStream(StreamMode::READWRITE);
+ uno::Reference< embed::XStorage > xWorkStore =
+ ::comphelper::OStorageHelper::GetStorageFromStream( new utl::OStreamWrapper(*pTempStream) );
+
+ uno::Reference < embed::XEmbedPersist > xPers( static_cast<embed::XVisualObject*>(pEmbObj), uno::UNO_QUERY );
+ if ( xPers.is() )
+ {
+ try
+ {
+ uno::Sequence < beans::PropertyValue > aSeq;
+ OUString aDummyName("Dummy");
+ xPers->storeToEntry( xWorkStore, aDummyName, aSeq, aSeq );
+ if ( xWorkStore->isStreamElement( aDummyName ) )
+ {
+ uno::Reference < io::XOutputStream > xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) );
+ uno::Reference < io::XStream > xNewStream = xWorkStore->openStreamElement( aDummyName, embed::ElementModes::READ );
+ ::comphelper::OStorageHelper::CopyInputToOutput( xNewStream->getInputStream(), xDocOut );
+ }
+ else
+ {
+ uno::Reference < io::XStream > xDocStr( new utl::OStreamWrapper( *rxOStm ) );
+ uno::Reference< embed::XStorage > xDocStg = ::comphelper::OStorageHelper::GetStorageFromStream( xDocStr );
+ uno::Reference < embed::XStorage > xNewStg = xWorkStore->openStorageElement( aDummyName, embed::ElementModes::READ );
+ xNewStg->copyToStorage( xDocStg );
+ uno::Reference < embed::XTransactedObject > xTrans( xDocStg, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ break;
+ }
+ case SCDRAWTRANS_TYPE_DOCUMENT:
+ {
+ // impl. for "DocShell"
+ SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pUserObject);
+
+ try
+ {
+ ::utl::TempFileFast aTempFile;
+ SvStream* pTempStream = aTempFile.GetStream(StreamMode::READWRITE);
+ uno::Reference< embed::XStorage > xWorkStore =
+ ::comphelper::OStorageHelper::GetStorageFromStream( new utl::OStreamWrapper(*pTempStream) );
+
+ // write document storage
+ pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false );
+
+ // mba: no relative URLs for clipboard!
+ SfxMedium aMedium( xWorkStore, OUString() );
+ pEmbObj->DoSaveObjectAs( aMedium, false );
+ pEmbObj->DoSaveCompleted();
+
+ uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+
+ rxOStm->SetBufferSize( 0xff00 );
+ rxOStm->WriteStream( *pTempStream );
+
+ xWorkStore->dispose();
+ xWorkStore.clear();
+ }
+ catch ( uno::Exception& )
+ {}
+
+ bRet = ( rxOStm->GetError() == ERRCODE_NONE );
+ }
+ break;
+
+ default:
+ OSL_FAIL("unknown object id");
+ }
+ return bRet;
+}
+
+void ScDrawTransferObj::DragFinished( sal_Int8 nDropAction )
+{
+ if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) )
+ {
+ // move: delete source objects
+
+ if ( m_pDragSourceView )
+ m_pDragSourceView->DeleteMarked();
+ }
+
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod->GetDragData().pDrawTransfer == this )
+ pScMod->ResetDragObject();
+
+ m_pDragSourceView.reset();
+
+ TransferDataContainer::DragFinished( nDropAction );
+}
+
+void ScDrawTransferObj::SetDrawPersist( const SfxObjectShellRef& rRef )
+{
+ m_aDrawPersistRef = rRef;
+}
+
+static void lcl_InitMarks( SdrMarkView& rDest, const SdrMarkView& rSource, SCTAB nTab )
+{
+ rDest.ShowSdrPage(rDest.GetModel().GetPage(nTab));
+ SdrPageView* pDestPV = rDest.GetSdrPageView();
+ OSL_ENSURE(pDestPV,"PageView ?");
+
+ const SdrMarkList& rMarkList = rSource.GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrMark* pMark = rMarkList.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ rDest.MarkObj(pObj, pDestPV);
+ }
+}
+
+void ScDrawTransferObj::SetDragSource( const ScDrawView* pView )
+{
+ m_pDragSourceView.reset(new SdrView(pView->getSdrModelFromSdrView())); // TTTT pView should be reference
+ lcl_InitMarks( *m_pDragSourceView, *pView, pView->GetTab() );
+
+ //! add as listener with document, delete pDragSourceView if document gone
+}
+
+void ScDrawTransferObj::SetDragSourceObj( SdrObject& rObj, SCTAB nTab )
+{
+ m_pDragSourceView.reset(new SdrView(rObj.getSdrModelFromSdrObject()));
+ m_pDragSourceView->ShowSdrPage(m_pDragSourceView->GetModel().GetPage(nTab));
+ SdrPageView* pPV = m_pDragSourceView->GetSdrPageView();
+ m_pDragSourceView->MarkObj(&rObj, pPV); // TTTT MarkObj should take SdrObject&
+
+ //! add as listener with document, delete pDragSourceView if document gone
+}
+
+void ScDrawTransferObj::SetDragSourceFlags(ScDragSrc nFlags)
+{
+ m_nDragSourceFlags = nFlags;
+}
+
+void ScDrawTransferObj::SetDragWasInternal()
+{
+ m_bDragWasInternal = true;
+}
+
+const OUString& ScDrawTransferObj::GetShellID() const
+{
+ return maShellID;
+}
+
+SdrOle2Obj* ScDrawTransferObj::GetSingleObject()
+{
+ // if single OLE object was copied, get its object
+
+ SdrPage* pPage = m_pModel->GetPage(0);
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ if (pObject && pObject->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ return static_cast<SdrOle2Obj*>(pObject);
+ }
+ }
+
+ return nullptr;
+}
+
+void ScDrawTransferObj::CreateOLEData()
+{
+ if (m_aOleData.GetTransferable().is())
+ // Already created.
+ return;
+
+ SdrOle2Obj* pObj = GetSingleObject();
+ if (!pObj || !pObj->GetObjRef().is())
+ // No OLE object present.
+ return;
+
+ rtl::Reference<SvEmbedTransferHelper> pEmbedTransfer =
+ new SvEmbedTransferHelper(
+ pObj->GetObjRef(), pObj->GetGraphic(), pObj->GetAspect());
+
+ pEmbedTransfer->SetParentShellID(maShellID);
+
+ m_aOleData = TransferableDataHelper(pEmbedTransfer);
+}
+
+// initialize aDocShellRef with a live document from the ClipDoc
+
+void ScDrawTransferObj::InitDocShell()
+{
+ if ( m_aDocShellRef.is() )
+ return;
+
+ ScDocShell* pDocSh = new ScDocShell;
+ m_aDocShellRef = pDocSh; // ref must be there before InitNew
+
+ pDocSh->DoInitNew();
+
+ ScDocument& rDestDoc = pDocSh->GetDocument();
+ rDestDoc.InitDrawLayer( pDocSh );
+
+ auto pPool = rDestDoc.GetStyleSheetPool();
+ pPool->CopyStyleFrom(m_pModel->GetStyleSheetPool(), ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Frame);
+ pPool->CopyUsedGraphicStylesFrom(m_pModel->GetStyleSheetPool());
+
+ SdrModel* pDestModel = rDestDoc.GetDrawLayer();
+ // #i71538# use complete SdrViews
+ // SdrExchangeView aDestView( pDestModel );
+ SdrView aDestView(*pDestModel);
+ aDestView.ShowSdrPage(aDestView.GetModel().GetPage(0));
+ aDestView.Paste(
+ *m_pModel,
+ Point(m_aSrcSize.Width()/2, m_aSrcSize.Height()/2),
+ nullptr, SdrInsertFlags::NONE);
+
+ // put objects to right layer (see ScViewFunc::PasteDataFormat for SotClipboardFormatId::DRAWING)
+
+ SdrPage* pPage = pDestModel->GetPage(0);
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pObject) != nullptr )
+ pObject->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pObject->NbcSetLayer(SC_LAYER_FRONT);
+ pObject = aIter.Next();
+ }
+ }
+
+ tools::Rectangle aDestArea( Point(), m_aSrcSize );
+ pDocSh->SetVisArea( aDestArea );
+
+ ScViewOptions aViewOpt( rDestDoc.GetViewOptions() );
+ aViewOpt.SetOption( VOPT_GRID, false );
+ rDestDoc.SetViewOptions( aViewOpt );
+
+ ScViewData aViewData( *pDocSh, nullptr );
+ aViewData.SetTabNo( 0 );
+ aViewData.SetScreen( aDestArea );
+ aViewData.SetCurX( 0 );
+ aViewData.SetCurY( 0 );
+ pDocSh->UpdateOle(aViewData, true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/inputhdl.cxx b/sc/source/ui/app/inputhdl.cxx
new file mode 100644
index 0000000000..809ba8520e
--- /dev/null
+++ b/sc/source/ui/app/inputhdl.cxx
@@ -0,0 +1,4733 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <iterator>
+#include <memory>
+#include <string_view>
+
+#include <inputhdl.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <sfx2/app.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <formula/errorcodes.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/svxacorr.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/misspellrange.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/svxids.hrc>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <utility>
+#include <vcl/help.hxx>
+#include <vcl/jsdialog/executor.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/formulahelper.hxx>
+#include <formula/funcvarargs.h>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <inputwin.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <formulaopt.hxx>
+#include <uiitems.hxx>
+#include <global.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <patattr.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <editutil.hxx>
+#include <appoptio.hxx>
+#include <docoptio.hxx>
+#include <validat.hxx>
+#include <rfindlst.hxx>
+#include <inputopt.hxx>
+#include <simpleformulacalc.hxx>
+#include <compiler.hxx>
+#include <editable.hxx>
+#include <funcdesc.hxx>
+#include <markdata.hxx>
+#include <tokenarray.hxx>
+#include <gridwin.hxx>
+#include <output.hxx>
+#include <fillinfo.hxx>
+
+// Maximum Ranges in RangeFinder
+#define RANGEFIND_MAX 128
+
+using namespace formula;
+
+namespace {
+
+ScTypedCaseStrSet::const_iterator findText(
+ const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
+ const OUString& rStart, OUString& rResult, bool bBack)
+{
+ auto lIsMatch = [&rStart](const ScTypedStrData& rData) {
+ return (rData.GetStringType() != ScTypedStrData::Value) && ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()); };
+
+ if (bBack) // Backwards
+ {
+ ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend();
+ if (itPos != rDataSet.end())
+ {
+ size_t nPos = std::distance(rDataSet.begin(), itPos);
+ size_t nRPos = rDataSet.size() - 1 - nPos;
+ std::advance(it, nRPos);
+ ++it;
+ }
+
+ it = std::find_if(it, itEnd, lIsMatch);
+ if (it != itEnd)
+ {
+ rResult = it->GetString();
+ return (++it).base(); // convert the reverse iterator back to iterator.
+ }
+ }
+ else // Forwards
+ {
+ ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end();
+ if (itPos != itEnd)
+ {
+ it = std::next(itPos);
+ }
+
+ it = std::find_if(it, itEnd, lIsMatch);
+ if (it != itEnd)
+ {
+ rResult = it->GetString();
+ return it;
+ }
+ }
+
+ return rDataSet.end(); // no matching text found
+}
+
+OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString)
+{
+ auto it = std::find_if(rDataSet.begin(), rDataSet.end(),
+ [&rString](const ScTypedStrData& rData) {
+ return (rData.GetStringType() != ScTypedStrData::Value)
+ && ScGlobal::GetTransliteration().isEqual(rData.GetString(), rString);
+ });
+ if (it != rDataSet.end())
+ return it->GetString();
+ return rString;
+}
+
+// This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or
+// in the reverse direction, whose origin is specified by nRingOrigin.
+sal_Int32 getLongestCommonPrefixLength(const std::vector<OUString>& rResults, std::u16string_view aUserEntry, sal_Int32 nRingOrigin)
+{
+ sal_Int32 nResults = rResults.size();
+ if (!nResults)
+ return 0;
+
+ if (nResults == 1)
+ return rResults[0].getLength();
+
+ sal_Int32 nMinLen = aUserEntry.size();
+ sal_Int32 nLastIdx = nRingOrigin ? nRingOrigin - 1 : nResults - 1;
+ const OUString& rFirst = rResults[nRingOrigin];
+ const OUString& rLast = rResults[nLastIdx];
+ const sal_Int32 nMaxLen = std::min(rFirst.getLength(), rLast.getLength());
+
+ for (sal_Int32 nLen = nMaxLen; nLen > nMinLen; --nLen)
+ {
+ if (ScGlobal::GetTransliteration().isMatch(rFirst.copy(0, nLen), rLast))
+ return nLen;
+ }
+
+ return nMinLen;
+}
+
+ScTypedCaseStrSet::const_iterator findTextAll(
+ const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
+ const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, sal_Int32* pLongestPrefixLen = nullptr)
+{
+ rResultVec.clear(); // clear contents
+
+ if (!rDataSet.size())
+ return rDataSet.end();
+
+ sal_Int32 nRingOrigin = 0;
+ size_t nCount = 0;
+ ScTypedCaseStrSet::const_iterator retit;
+ if ( bBack ) // Backwards
+ {
+ ScTypedCaseStrSet::const_reverse_iterator it, itEnd;
+ if ( itPos == rDataSet.end() )
+ {
+ it = rDataSet.rend();
+ --it;
+ itEnd = it;
+ }
+ else
+ {
+ it = rDataSet.rbegin();
+ size_t nPos = std::distance(rDataSet.begin(), itPos);
+ size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1
+ std::advance(it, nRPos);
+ if ( it == rDataSet.rend() )
+ it = rDataSet.rbegin();
+ itEnd = it;
+ }
+ bool bFirstTime = true;
+
+ while ( it != itEnd || bFirstTime )
+ {
+ ++it;
+ if ( it == rDataSet.rend() ) // go to the first if reach the end
+ {
+ it = rDataSet.rbegin();
+ nRingOrigin = nCount;
+ }
+
+ if ( bFirstTime )
+ bFirstTime = false;
+ const ScTypedStrData& rData = *it;
+ if ( rData.GetStringType() == ScTypedStrData::Value )
+ // skip values
+ continue;
+
+ if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) )
+ // not a match
+ continue;
+
+ rResultVec.push_back(rData.GetString()); // set the match data
+ if ( nCount == 0 ) // convert the reverse iterator back to iterator.
+ {
+ // actually we want to do "retit = it;".
+ retit = rDataSet.begin();
+ size_t nRPos = std::distance(rDataSet.rbegin(), it);
+ size_t nPos = rDataSet.size() - 1 - nRPos;
+ std::advance(retit, nPos);
+ }
+ ++nCount;
+ }
+ }
+ else // Forwards
+ {
+ ScTypedCaseStrSet::const_iterator it, itEnd;
+ it = itPos;
+ if ( it == rDataSet.end() )
+ it = --rDataSet.end();
+ itEnd = it;
+ bool bFirstTime = true;
+
+ while ( it != itEnd || bFirstTime )
+ {
+ ++it;
+ if ( it == rDataSet.end() ) // go to the first if reach the end
+ {
+ it = rDataSet.begin();
+ nRingOrigin = nCount;
+ }
+
+ if ( bFirstTime )
+ bFirstTime = false;
+ const ScTypedStrData& rData = *it;
+ if ( rData.GetStringType() == ScTypedStrData::Value )
+ // skip values
+ continue;
+
+ if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) )
+ // not a match
+ continue;
+
+ rResultVec.push_back(rData.GetString()); // set the match data
+ if ( nCount == 0 )
+ retit = it; // remember first match iterator
+ ++nCount;
+ }
+ }
+
+ if (pLongestPrefixLen)
+ {
+ if (nRingOrigin >= static_cast<sal_Int32>(nCount))
+ {
+ // All matches were picked when rDataSet was read in one direction.
+ nRingOrigin = 0;
+ }
+ // rResultsVec is a sorted ring with nRingOrigin "origin".
+ // The direction of sorting is not important for getLongestCommonPrefixLength.
+ *pLongestPrefixLen = getLongestCommonPrefixLength(rResultVec, rStart, nRingOrigin);
+ }
+
+ if ( nCount > 0 ) // at least one function has matched
+ return retit;
+ return rDataSet.end(); // no matching text found
+}
+
+}
+
+void ScInputHandler::SendReferenceMarks( const SfxViewShell* pViewShell,
+ const std::vector<ReferenceMark>& rReferenceMarks )
+{
+ if ( !pViewShell )
+ return;
+
+ bool bSend = false;
+
+ std::stringstream ss;
+
+ ss << "{ \"marks\": [ ";
+
+ for ( size_t i = 0; i < rReferenceMarks.size(); i++ )
+ {
+ if ( rReferenceMarks[i].Is() )
+ {
+ if ( bSend )
+ ss << ", ";
+
+ ss << "{ \"rectangle\": \""
+ << rReferenceMarks[i].nX << ", "
+ << rReferenceMarks[i].nY << ", "
+ << rReferenceMarks[i].nWidth << ", "
+ << rReferenceMarks[i].nHeight << "\", "
+ "\"color\": \"" << rReferenceMarks[i].aColor.AsRGBHexString() << "\", "
+ "\"part\": \"" << rReferenceMarks[i].nTab << "\" } ";
+
+ bSend = true;
+ }
+ }
+
+ ss << " ] }";
+
+ OString aPayload( ss.str() );
+ pViewShell->libreOfficeKitViewCallback(
+ LOK_CALLBACK_REFERENCE_MARKS, aPayload );
+}
+
+static inline void incPos( const sal_Unicode c, sal_Int32& rPos, ESelection& rSel )
+{
+ ++rPos;
+ if (c == '\n')
+ {
+ ++rSel.nEndPara;
+ rSel.nEndPos = 0;
+ }
+ else
+ {
+ ++rSel.nEndPos;
+ }
+}
+
+void ScInputHandler::InitRangeFinder( const OUString& rFormula )
+{
+ DeleteRangeFinder();
+ if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() )
+ return;
+ ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const sal_Unicode cSheetSep = rDoc.GetSheetSeparator();
+
+ OUString aDelimiters = ScEditUtil::ModifyDelimiters(" !~%\"\t\n");
+ // delimiters (in addition to ScEditUtil): only characters that are
+ // allowed in formulas next to references and the quotation mark (so
+ // string constants can be skipped)
+
+ sal_Int32 nColon = aDelimiters.indexOf( ':' );
+ if ( nColon != -1 )
+ aDelimiters = aDelimiters.replaceAt( nColon, 1, u""); // Delimiter without colon
+ sal_Int32 nDot = aDelimiters.indexOf(cSheetSep);
+ if ( nDot != -1 )
+ aDelimiters = aDelimiters.replaceAt( nDot, 1 , u""); // Delimiter without dot
+
+ const sal_Unicode* pChar = rFormula.getStr();
+ sal_Int32 nLen = rFormula.getLength();
+ sal_Int32 nPos = 0;
+ sal_Int32 nStart = 0;
+ ESelection aSel;
+ sal_uInt16 nCount = 0;
+ ScRange aRange;
+ while ( nPos < nLen && nCount < RANGEFIND_MAX )
+ {
+ // Skip separator
+ while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) )
+ {
+ if ( pChar[nPos] == '"' ) // String
+ {
+ incPos( pChar[nPos], nPos, aSel);
+ while (nPos<nLen && pChar[nPos] != '"') // Skip until end
+ incPos( pChar[nPos], nPos, aSel);
+ }
+ incPos( pChar[nPos], nPos, aSel); // Separator or closing quote
+ }
+
+ // Text between separators. We only consider within one line/paragraph.
+ aSel.nStartPara = aSel.nEndPara;
+ aSel.nStartPos = aSel.nEndPos;
+ nStart = nPos;
+handle_r1c1:
+ {
+ bool bSingleQuoted = false;
+ while (nPos < nLen)
+ {
+ // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1"
+ // Literal single quotes in sheet names are masked by another single quote
+ if (pChar[nPos] == '\'')
+ {
+ bSingleQuoted = !bSingleQuoted;
+ }
+ else if (!bSingleQuoted) // Get everything in single quotes, including separators
+ {
+ if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos]))
+ break;
+ }
+ incPos( pChar[nPos], nPos, aSel);
+ }
+ }
+
+ // for R1C1 '-' in R[-]... or C[-]... are not delimiters
+ // Nothing heroic here to ensure that there are '[]' around a negative
+ // integer. we need to clean up this code.
+ if( nPos < nLen && nPos > 0 &&
+ '-' == pChar[nPos] && '[' == pChar[nPos-1] &&
+ formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() )
+ {
+ incPos( pChar[nPos], nPos, aSel);
+ goto handle_r1c1;
+ }
+
+ if ( nPos > nStart )
+ {
+ OUString aTest = rFormula.copy( nStart, nPos-nStart );
+ const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
+ ScRefFlags nFlags = aRange.ParseAny( aTest, rDoc, aAddrDetails );
+ if ( nFlags & ScRefFlags::VALID )
+ {
+ // Set tables if not specified
+ if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
+ aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() );
+ if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
+ aRange.aEnd.SetTab( aRange.aStart.Tab() );
+
+ if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) ==
+ ScRefFlags::ZERO )
+ {
+ // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
+ // so Format doesn't output a double ref because of different flags.
+ ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS);
+ applyStartToEndFlags(nFlags, nAbsFlags);
+ }
+
+ if (!nCount)
+ {
+ mpEditEngine->SetUpdateLayout( false );
+ pRangeFindList.reset(new ScRangeFindList( pDocSh->GetTitle() ));
+ }
+
+ Color nColor = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, aSel));
+
+ SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
+ aSet.Put( SvxColorItem( nColor, EE_CHAR_COLOR ) );
+ mpEditEngine->QuickSetAttribs( aSet, aSel );
+ ++nCount;
+ }
+ }
+
+ // Do not skip last separator; could be a quote (?)
+ }
+
+ UpdateLokReferenceMarks();
+
+ if (nCount)
+ {
+ mpEditEngine->SetUpdateLayout( true );
+
+ pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) );
+ }
+}
+
+ReferenceMark ScInputHandler::GetReferenceMark( const ScViewData& rViewData, ScDocShell* pDocSh,
+ tools::Long nX1, tools::Long nX2, tools::Long nY1, tools::Long nY2,
+ tools::Long nTab, const Color& rColor )
+{
+ ScSplitPos eWhich = rViewData.GetActivePart();
+
+ // This method is LOK specific.
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ SCCOL nCol1 = nX1, nCol2 = nX2;
+ SCROW nRow1 = nY1, nRow2 = nY2;
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ PutInOrder(nCol1, nCol2);
+ PutInOrder(nRow1, nRow2);
+
+ if (nCol1 == nCol2 && nRow1 == nRow2)
+ rDoc.ExtendMerge(nCol1, nRow1, nCol2, nRow2, nTab);
+ else if (rDoc.HasAttrib(nCol2, nRow2, nTab, HasAttrFlags::Merged))
+ rDoc.ExtendMerge(nCol2, nRow2, nCol2, nRow2, nTab);
+
+ Point aTopLeft = rViewData.GetPrintTwipsPos(nCol1, nRow1);
+ Point aBottomRight = rViewData.GetPrintTwipsPos(nCol2 + 1, nRow2 + 1);
+ tools::Long nSizeX = aBottomRight.X() - aTopLeft.X() - 1;
+ tools::Long nSizeY = aBottomRight.Y() - aTopLeft.Y() - 1;
+
+ return ReferenceMark(aTopLeft.X(), aTopLeft.Y(), nSizeX, nSizeY, nTab, rColor);
+ }
+
+ Point aScrPos = rViewData.GetScrPos( nX1, nY1, eWhich );
+ tools::Long nScrX = aScrPos.X();
+ tools::Long nScrY = aScrPos.Y();
+
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+
+ Fraction aZoomX = rViewData.GetZoomX();
+ Fraction aZoomY = rViewData.GetZoomY();
+
+ ScTableInfo aTabInfo;
+ pDocSh->GetDocument().FillInfo( aTabInfo, nX1, nY1, nX2, nY2,
+ nTab, nPPTX, nPPTY, false, false );
+
+ ScOutputData aOutputData( nullptr, OUTTYPE_WINDOW, aTabInfo,
+ &( pDocSh->GetDocument() ), nTab,
+ nScrX, nScrY,
+ nX1, nY1, nX2, nY2,
+ nPPTX, nPPTY,
+ &aZoomX, &aZoomY );
+
+ return aOutputData.FillReferenceMark( nX1, nY1, nX2, nY2,
+ rColor );
+}
+
+void ScInputHandler::UpdateLokReferenceMarks()
+{
+ if ( !comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScTabViewShell* pShell = pActiveViewSh ? pActiveViewSh
+ : dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+
+ if (!pShell)
+ return;
+
+ ScViewData& rViewData = pShell->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScRangeFindList* pRangeFinder = GetRangeFindList();
+
+ if ( !pRangeFinder && !rViewData.IsRefMode() )
+ return;
+
+ sal_uInt16 nAdditionalMarks = 0;
+ std::vector<ReferenceMark> aReferenceMarks( 1 );
+
+ if ( rViewData.IsRefMode() )
+ {
+ nAdditionalMarks = 1;
+
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aRefColor( rColorCfg.GetColorValue( svtools::CALCREFERENCE ).nColor );
+ tools::Long nX1 = rViewData.GetRefStartX();
+ tools::Long nX2 = rViewData.GetRefEndX();
+ tools::Long nY1 = rViewData.GetRefStartY();
+ tools::Long nY2 = rViewData.GetRefEndY();
+ tools::Long nTab = rViewData.GetRefStartZ();
+
+ if (rViewData.GetRefEndZ() == rViewData.GetTabNo())
+ nTab = rViewData.GetRefEndZ();
+
+ PutInOrder(nX1, nX2);
+ PutInOrder(nY1, nY2);
+
+ aReferenceMarks[0] = ScInputHandler::GetReferenceMark( rViewData, pDocSh,
+ nX1, nX2, nY1, nY2,
+ nTab, aRefColor );
+ }
+
+ sal_uInt16 nCount = pRangeFinder ?
+ ( static_cast<sal_uInt16>( pRangeFinder->Count() ) + nAdditionalMarks ) : nAdditionalMarks;
+ aReferenceMarks.resize( nCount );
+
+ if ( nCount && pRangeFinder && !pRangeFinder->IsHidden() &&
+ pRangeFinder->GetDocName() == pDocSh->GetTitle() )
+ {
+ for (sal_uInt16 i = 0; i < nCount - nAdditionalMarks; i++)
+ {
+ ScRangeFindData& rData = pRangeFinder->GetObject( i );
+ ScRange aRef = rData.aRef;
+ aRef.PutInOrder();
+
+ tools::Long nX1 = aRef.aStart.Col();
+ tools::Long nX2 = aRef.aEnd.Col();
+ tools::Long nY1 = aRef.aStart.Row();
+ tools::Long nY2 = aRef.aEnd.Row();
+ tools::Long nTab = aRef.aStart.Tab();
+
+ aReferenceMarks[i + nAdditionalMarks] = ScInputHandler::GetReferenceMark( rViewData, pDocSh,
+ nX1, nX2, nY1, nY2,
+ nTab, rData.nColor );
+
+ ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
+ }
+ }
+ else if ( nCount )
+ {
+ ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
+ }
+ else
+ {
+ // Clear
+ aReferenceMarks.clear();
+ ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
+ }
+}
+
+void ScInputHandler::SetDocumentDisposing( bool b )
+{
+ mbDocumentDisposing = b;
+}
+
+static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel )
+{
+ if ( !pView )
+ return;
+
+ ESelection aOldSel = pView->GetSelection();
+ if (aOldSel.HasRange())
+ pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos,
+ aOldSel.nEndPara, aOldSel.nEndPos ) );
+
+ EditEngine* pEngine = pView->GetEditEngine();
+ pEngine->QuickInsertText( rNewStr, rOldSel );
+
+ // Dummy InsertText for Update and Paint
+ // To do that we need to cancel the selection from above (before QuickInsertText)
+ pView->InsertText( OUString() );
+
+ const sal_Int32 nPara = pEngine->GetParagraphCount() - 1;
+ const sal_Int32 nLen = pEngine->GetTextLen(nPara);
+ ESelection aSel( nPara, nLen, nPara, nLen );
+ pView->SetSelection( aSel ); // Set cursor to the end
+}
+
+void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew )
+{
+ ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh;
+ if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() )
+ {
+ ScRangeFindData& rData = pRangeFindList->GetObject( nIndex );
+ Color nNewColor = pRangeFindList->FindColor( rNew, nIndex );
+
+ ScRange aJustified = rNew;
+ aJustified.PutInOrder(); // Always display Ref in the Formula the right way
+ ScDocument& rDoc = pDocView->GetViewData().GetDocument();
+ const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
+ OUString aNewStr(aJustified.Format(rDoc, rData.nFlags, aAddrDetails));
+ SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
+
+ DataChanging();
+
+ lcl_Replace( pTopView, aNewStr, rData.maSel );
+ lcl_Replace( pTableView, aNewStr, rData.maSel );
+
+ // We are within one paragraph.
+ const sal_Int32 nDiff = aNewStr.getLength() - (rData.maSel.nEndPos - rData.maSel.nStartPos);
+ rData.maSel.nEndPos += nDiff;
+
+ aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) );
+ mpEditEngine->QuickSetAttribs( aSet, rData.maSel );
+
+ bInRangeUpdate = true;
+ DataChanged();
+ bInRangeUpdate = false;
+
+ rData.aRef = rNew;
+ rData.nColor = nNewColor;
+
+ if (nDiff)
+ {
+ const size_t nCount = pRangeFindList->Count();
+ for (size_t i = nIndex + 1; i < nCount; ++i)
+ {
+ ScRangeFindData& rNext = pRangeFindList->GetObject( i );
+ if (rNext.maSel.nStartPara != rData.maSel.nStartPara)
+ break;
+
+ rNext.maSel.nStartPos += nDiff;
+ rNext.maSel.nEndPos += nDiff;
+ }
+ }
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ pActiveView->ShowCursor( false );
+ }
+ else
+ {
+ OSL_FAIL("UpdateRange: we're missing something");
+ }
+}
+
+void ScInputHandler::DeleteRangeFinder()
+{
+ ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh;
+ if ( pRangeFindList && pPaintView )
+ {
+ ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
+ pRangeFindList->SetHidden(true);
+ pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) ); // Steal
+ pRangeFindList.reset();
+ }
+}
+
+static OUString GetEditText(const EditEngine* pEng)
+{
+ return ScEditUtil::GetMultilineString(*pEng);
+}
+
+static void lcl_RemoveTabs(OUString& rStr)
+{
+ rStr = rStr.replace('\t', ' ');
+}
+
+static void lcl_RemoveLineEnd(OUString& rStr)
+{
+ rStr = convertLineEnd(rStr, LINEEND_LF);
+ rStr = rStr.replace('\n', ' ');
+}
+
+static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos )
+{
+ int nDir;
+ sal_Unicode c1, c2 = 0;
+ c1 = rStr[nPos];
+ switch ( c1 )
+ {
+ case '(' :
+ c2 = ')';
+ nDir = 1;
+ break;
+ case ')' :
+ c2 = '(';
+ nDir = -1;
+ break;
+ case '<' :
+ c2 = '>';
+ nDir = 1;
+ break;
+ case '>' :
+ c2 = '<';
+ nDir = -1;
+ break;
+ case '{' :
+ c2 = '}';
+ nDir = 1;
+ break;
+ case '}' :
+ c2 = '{';
+ nDir = -1;
+ break;
+ case '[' :
+ c2 = ']';
+ nDir = 1;
+ break;
+ case ']' :
+ c2 = '[';
+ nDir = -1;
+ break;
+ default:
+ nDir = 0;
+ }
+ if ( !nDir )
+ return -1;
+ sal_Int32 nLen = rStr.getLength();
+ const sal_Unicode* p0 = rStr.getStr();
+ const sal_Unicode* p;
+ const sal_Unicode* p1;
+ sal_uInt16 nQuotes = 0;
+ if ( nPos < nLen / 2 )
+ {
+ p = p0;
+ p1 = p0 + nPos;
+ }
+ else
+ {
+ p = p0 + nPos;
+ p1 = p0 + nLen;
+ }
+ while ( p < p1 )
+ {
+ if ( *p++ == '\"' )
+ nQuotes++;
+ }
+ // Odd number of quotes that we find ourselves in a string
+ bool bLookInString = ((nQuotes % 2) != 0);
+ bool bInString = bLookInString;
+ p = p0 + nPos;
+ p1 = (nDir < 0 ? p0 : p0 + nLen) ;
+ sal_uInt16 nLevel = 1;
+ while ( p != p1 && nLevel )
+ {
+ p += nDir;
+ if ( *p == '\"' )
+ {
+ bInString = !bInString;
+ if ( bLookInString && !bInString )
+ p = p1; // That's it then
+ }
+ else if ( bInString == bLookInString )
+ {
+ if ( *p == c1 )
+ nLevel++;
+ else if ( *p == c2 )
+ nLevel--;
+ }
+ }
+ if ( nLevel )
+ return -1;
+ return static_cast<sal_Int32>(p - p0);
+}
+
+ScInputHandler::ScInputHandler()
+ : pInputWin( nullptr ),
+ pTableView( nullptr ),
+ pTopView( nullptr ),
+ pTipVisibleParent( nullptr ),
+ nTipVisible( nullptr ),
+ pTipVisibleSecParent( nullptr ),
+ nTipVisibleSec( nullptr ),
+ nFormSelStart( 0 ),
+ nFormSelEnd( 0 ),
+ nCellPercentFormatDecSep( 0 ),
+ nAutoPar( 0 ),
+ eMode( SC_INPUT_NONE ),
+ bUseTab( false ),
+ bTextValid( true ),
+ bModified( false ),
+ bSelIsRef( false ),
+ bFormulaMode( false ),
+ bInRangeUpdate( false ),
+ bParenthesisShown( false ),
+ bCreatingFuncView( false ),
+ bInEnterHandler( false ),
+ bCommandErrorShown( false ),
+ bInOwnChange( false ),
+ bProtected( false ),
+ bLastIsSymbol( false ),
+ mbDocumentDisposing(false),
+ mbPartialPrefix(false),
+ mbEditingExistingContent(false),
+ nValidation( 0 ),
+ eAttrAdjust( SvxCellHorJustify::Standard ),
+ aScaleX( 1,1 ),
+ aScaleY( 1,1 ),
+ pRefViewSh( nullptr ),
+ pLastPattern( nullptr )
+{
+ // The InputHandler is constructed with the view, so SfxViewShell::Current
+ // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
+ pActiveViewSh = nullptr;
+
+ // Bindings (only still used for Invalidate) are retrieved if needed on demand
+
+ pDelayTimer.reset( new Timer( "ScInputHandlerDelay timer" ) );
+ pDelayTimer->SetTimeout( 500 ); // 500 ms delay
+ pDelayTimer->SetInvokeHandler( LINK( this, ScInputHandler, DelayTimer ) );
+}
+
+ScInputHandler::~ScInputHandler()
+{
+ // If this is the application InputHandler, the dtor is called after SfxApplication::Main,
+ // thus we can't rely on any Sfx functions
+ if (!mbDocumentDisposing) // inplace
+ EnterHandler(); // Finish input
+
+ if (SC_MOD()->GetRefInputHdl() == this)
+ SC_MOD()->SetRefInputHdl(nullptr);
+
+ if ( pInputWin && pInputWin->GetInputHandler() == this )
+ pInputWin->SetInputHandler( nullptr );
+}
+
+void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY )
+{
+ if ( rX != aScaleX || rY != aScaleY )
+ {
+ aScaleX = rX;
+ aScaleY = rY;
+ if (mpEditEngine)
+ {
+ MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
+ mpEditEngine->SetRefMapMode( aMode );
+ }
+ }
+}
+
+void ScInputHandler::UpdateRefDevice()
+{
+ if (!mpEditEngine)
+ return;
+
+ bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
+ bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame().GetFrame().IsInPlace();
+ EEControlBits nCtrl = mpEditEngine->GetControlWord();
+ if ( bTextWysiwyg || bInPlace )
+ nCtrl |= EEControlBits::FORMAT100; // EditEngine default: always format for 100%
+ else
+ nCtrl &= ~EEControlBits::FORMAT100; // when formatting for screen, use the actual MapMode
+ mpEditEngine->SetControlWord( nCtrl );
+ if ( bTextWysiwyg && pActiveViewSh )
+ mpEditEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument().GetPrinter() );
+ else
+ mpEditEngine->SetRefDevice( nullptr );
+
+ MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
+ mpEditEngine->SetRefMapMode( aMode );
+
+ // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
+ // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
+ if ( !( bTextWysiwyg && pActiveViewSh ) )
+ {
+ mpEditEngine->GetRefDevice()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ }
+}
+
+void ScInputHandler::ImplCreateEditEngine()
+{
+ if ( mpEditEngine )
+ return;
+
+ // we cannot create a properly initialised EditEngine until we have a document
+ assert( pActiveViewSh );
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
+ mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
+ mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) );
+ UpdateRefDevice(); // also sets MapMode
+ mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) );
+ pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) );
+
+ mpEditEngine->SetControlWord( mpEditEngine->GetControlWord() | EEControlBits::AUTOCORRECT );
+ mpEditEngine->SetReplaceLeadingSingleQuotationMark( false );
+ mpEditEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) );
+}
+
+void ScInputHandler::UpdateAutoCorrFlag()
+{
+ EEControlBits nCntrl = mpEditEngine->GetControlWord();
+ EEControlBits nOld = nCntrl;
+
+ // Don't use pLastPattern here (may be invalid because of AutoStyle)
+ bool bDisable = bLastIsSymbol || bFormulaMode;
+ if ( bDisable )
+ nCntrl &= ~EEControlBits::AUTOCORRECT;
+ else
+ nCntrl |= EEControlBits::AUTOCORRECT;
+
+ if ( nCntrl != nOld )
+ mpEditEngine->SetControlWord(nCntrl);
+}
+
+void ScInputHandler::UpdateSpellSettings( bool bFromStartTab )
+{
+ if ( !pActiveViewSh )
+ return;
+
+ ScViewData& rViewData = pActiveViewSh->GetViewData();
+ bool bOnlineSpell = rViewData.GetDocument().GetDocOptions().IsAutoSpell();
+
+ // SetDefaultLanguage is independent of the language attributes,
+ // ScGlobal::GetEditDefaultLanguage is always used.
+ // It must be set every time in case the office language was changed.
+
+ mpEditEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
+
+ // if called for changed options, update flags only if already editing
+ // if called from StartTable, always update flags
+
+ if ( bFromStartTab || eMode != SC_INPUT_NONE )
+ {
+ EEControlBits nCntrl = mpEditEngine->GetControlWord();
+ EEControlBits nOld = nCntrl;
+ if( bOnlineSpell )
+ nCntrl |= EEControlBits::ONLINESPELLING;
+ else
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
+ if ( pLastPattern && pLastPattern->IsSymbolFont() )
+ nCntrl &= ~EEControlBits::AUTOCORRECT;
+ else
+ nCntrl |= EEControlBits::AUTOCORRECT;
+ if ( nCntrl != nOld )
+ mpEditEngine->SetControlWord(nCntrl);
+
+ ScDocument& rDoc = rViewData.GetDocument();
+ rDoc.ApplyAsianEditSettings( *mpEditEngine );
+ mpEditEngine->SetDefaultHorizontalTextDirection(
+ rDoc.GetEditTextDirection( rViewData.GetTabNo() ) );
+ mpEditEngine->SetFirstWordCapitalization( false );
+ }
+
+ // Language is set separately, so the speller is needed only if online spelling is active
+ if ( bOnlineSpell ) {
+ css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
+ mpEditEngine->SetSpeller( xXSpellChecker1 );
+ }
+
+ bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue();
+ if ( bHyphen ) {
+ css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
+ mpEditEngine->SetHyphenator( xXHyphenator );
+ }
+}
+
+// Function/Range names etc. as Tip help
+
+// The other types are defined in ScDocument::GetFormulaEntries
+void ScInputHandler::GetFormulaData()
+{
+ if ( !pActiveViewSh )
+ return;
+
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
+
+ if ( pFormulaData )
+ pFormulaData->clear();
+ else
+ {
+ pFormulaData.reset( new ScTypedCaseStrSet );
+ }
+
+ if( pFormulaDataPara )
+ pFormulaDataPara->clear();
+ else
+ pFormulaDataPara.reset( new ScTypedCaseStrSet );
+
+ const OUString aParenthesesReplacement( cParenthesesReplacement);
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ const sal_uInt32 nListCount = pFuncList->GetCount();
+ const InputHandlerFunctionNames& rFunctionNames = ScGlobal::GetInputHandlerFunctionNames();
+ *pFormulaData = rFunctionNames.maFunctionData;
+ *pFormulaDataPara = rFunctionNames.maFunctionDataPara;
+ maFormulaChar = rFunctionNames.maFunctionChar;
+
+ // Increase suggestion priority of MRU formulas
+ const ScAppOptions& rOpt = SC_MOD()->GetAppOptions();
+ const sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount();
+ const sal_uInt16* pMRUList = rOpt.GetLRUFuncList();
+ for (sal_uInt16 i = 0; i < nMRUCount; i++)
+ {
+ const sal_uInt16 nId = pMRUList[i];
+ for (sal_uInt32 j = 0; j < nListCount; j++)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction(j);
+ if (pDesc->nFIndex == nId && pDesc->mxFuncName)
+ {
+ const OUString aEntry = *pDesc->mxFuncName + aParenthesesReplacement;;
+ const ScTypedStrData aData(aEntry, 0.0, 0.0, ScTypedStrData::Standard);
+ auto it = pFormulaData->find(aData);
+ if (it != pFormulaData->end())
+ pFormulaData->erase(it);
+ pFormulaData->insert(ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::MRU));
+ break; // Stop searching
+ }
+ }
+ }
+ miAutoPosFormula = pFormulaData->end();
+
+ // tdf#142031 - collect all the characters for the formula suggestion auto input
+ ScTypedCaseStrSet aStrSet;
+ rDoc.GetFormulaEntries( aStrSet );
+ for (auto iter = aStrSet.begin(); iter != aStrSet.end(); ++iter)
+ {
+ const OUString aFuncName = ScGlobal::getCharClass().uppercase((*iter).GetString());
+ // fdo#75264 fill maFormulaChar with all characters used in formula names
+ for (sal_Int32 j = 0; j < aFuncName.getLength(); j++)
+ maFormulaChar.insert(aFuncName[j]);
+ }
+ pFormulaData->insert(aStrSet.begin(), aStrSet.end());
+ pFormulaDataPara->insert(aStrSet.begin(), aStrSet.end());
+}
+
+IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void )
+{
+ if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
+ || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus)
+ HideTip();
+}
+
+IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void )
+{
+ if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
+ || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus)
+ HideTipBelow();
+}
+
+void ScInputHandler::HideTip()
+{
+ if ( nTipVisible )
+ {
+ pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
+ Help::HidePopover(pTipVisibleParent, nTipVisible );
+ nTipVisible = nullptr;
+ pTipVisibleParent = nullptr;
+ }
+ aManualTip.clear();
+}
+void ScInputHandler::HideTipBelow()
+{
+ if ( nTipVisibleSec )
+ {
+ pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
+ Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec);
+ nTipVisibleSec = nullptr;
+ pTipVisibleSecParent = nullptr;
+ }
+ aManualTip.clear();
+}
+
+namespace
+{
+
+bool lcl_hasSingleToken(std::u16string_view s, sal_Unicode c)
+{
+ return !s.empty() && s.find(c) == std::u16string_view::npos;
+}
+
+}
+
+void ScInputHandler::ShowArgumentsTip( OUString& rSelText )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ return;
+ }
+
+ if ( !pActiveViewSh )
+ return;
+
+ ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ const sal_Unicode cSheetSep = pDocSh->GetDocument().GetSheetSeparator();
+ FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
+ bool bFound = false;
+ while( !bFound )
+ {
+ rSelText += ")";
+ sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 );
+ if( nLeftParentPos != -1 )
+ {
+ sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true);
+ const IFunctionDescription* ppFDesc;
+ ::std::vector< OUString> aArgs;
+ if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
+ {
+ if( !ppFDesc->getFunctionName().isEmpty() )
+ {
+ sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 );
+ sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount());
+ OUString aFuncName( ppFDesc->getFunctionName() + "(");
+ OUString aNew;
+ ScTypedCaseStrSet::const_iterator it =
+ findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false);
+ if (it != pFormulaDataPara->end())
+ {
+ bool bFlag = false;
+ sal_uInt16 nActive = 0;
+ for( sal_uInt16 i=0; i < nArgs; i++ )
+ {
+ sal_Int32 nLength = aArgs[i].getLength();
+ if( nArgPos <= rSelText.getLength()-1 )
+ {
+ nActive = i+1;
+ bFlag = true;
+ }
+ nArgPos+=nLength+1;
+ }
+ if( bFlag )
+ {
+ sal_Int32 nStartPosition = 0;
+ sal_Int32 nEndPosition = 0;
+
+ if( lcl_hasSingleToken(aNew, cSep) )
+ {
+ for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
+ {
+ sal_Unicode cNext = aNew[i];
+ if( cNext == '(' )
+ {
+ nStartPosition = i+1;
+ }
+ }
+ }
+ else if( lcl_hasSingleToken(aNew, cSheetSep) )
+ {
+ sal_uInt16 nCount = 0;
+ for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
+ {
+ sal_Unicode cNext = aNew[i];
+ if( cNext == '(' )
+ {
+ nStartPosition = i+1;
+ }
+ else if( cNext == cSep )
+ {
+ nCount ++;
+ nEndPosition = i;
+ if( nCount == nActive )
+ {
+ break;
+ }
+ nStartPosition = nEndPosition+1;
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nCount = 0;
+ for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
+ {
+ sal_Unicode cNext = aNew[i];
+ if( cNext == '(' )
+ {
+ nStartPosition = i+1;
+ }
+ else if( cNext == cSep )
+ {
+ nCount ++;
+ nEndPosition = i;
+ if( nCount == nActive )
+ {
+ break;
+ }
+ nStartPosition = nEndPosition+1;
+ }
+ else if( cNext == cSheetSep )
+ {
+ continue;
+ }
+ }
+ }
+
+ if (nStartPosition > 0)
+ {
+ nArgs = ppFDesc->getParameterCount();
+ sal_Int16 nVarArgsSet = 0;
+ if ( nArgs >= PAIRED_VAR_ARGS )
+ {
+ nVarArgsSet = 2;
+ nArgs -= PAIRED_VAR_ARGS - nVarArgsSet;
+ }
+ else if ( nArgs >= VAR_ARGS )
+ {
+ nVarArgsSet = 1;
+ nArgs -= VAR_ARGS - nVarArgsSet;
+ }
+ if ( nVarArgsSet > 0 && nActive > nArgs )
+ nActive = nArgs - (nActive - nArgs) % nVarArgsSet;
+ aNew = OUString::Concat(aNew.subView(0, nStartPosition)) +
+ u"\x25BA" +
+ aNew.subView(nStartPosition) +
+ " : " +
+ ppFDesc->getParameterDescription(nActive-1);
+ if (eMode != SC_INPUT_TOP)
+ {
+ ShowTipBelow( aNew );
+ }
+ else
+ {
+ ShowTip(aNew);
+ }
+ bFound = true;
+ }
+ }
+ else
+ {
+ ShowTipBelow( aNew );
+ bFound = true;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+void ScInputHandler::ShowTipCursor()
+{
+ HideTip();
+ HideTipBelow();
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+
+ /* TODO: MLFORMULA: this should work also with multi-line formulas. */
+ if ( !(bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1) )
+ return;
+
+ OUString aParagraph = mpEditEngine->GetText( 0 );
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+
+ if ( aParagraph.getLength() < aSel.nEndPos )
+ return;
+
+ if ( aSel.nEndPos > 0 )
+ {
+ OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
+
+ ShowArgumentsTip( aSelText );
+ }
+}
+
+void ScInputHandler::ShowTip( const OUString& rText )
+{
+ // aManualTip needs to be set afterwards from outside
+
+ HideTip();
+ HideTipBelow();
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if (!pActiveView)
+ return;
+
+ Point aPos;
+ if (pInputWin && pInputWin->GetEditView() == pActiveView)
+ {
+ pTipVisibleParent = pInputWin->GetEditWindow();
+ aPos = pInputWin->GetCursorScreenPixelPos();
+ }
+ else
+ {
+ pTipVisibleParent = pActiveView->GetWindow();
+ if (vcl::Cursor* pCur = pActiveView->GetCursor())
+ aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() );
+ aPos = pTipVisibleParent->OutputToScreenPixel( aPos );
+ }
+
+ tools::Rectangle aRect( aPos, aPos );
+ QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
+ nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign);
+ pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
+}
+
+void ScInputHandler::ShowTipBelow( const OUString& rText )
+{
+ HideTipBelow();
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if ( !pActiveView )
+ return;
+
+ Point aPos;
+ if (pInputWin && pInputWin->GetEditView() == pActiveView)
+ {
+ pTipVisibleSecParent = pInputWin->GetEditWindow();
+ aPos = pInputWin->GetCursorScreenPixelPos(true);
+ }
+ else
+ {
+ pTipVisibleSecParent = pActiveView->GetWindow();
+ if (vcl::Cursor* pCur = pActiveView->GetCursor())
+ {
+ Point aLogicPos = pCur->GetPos();
+ aLogicPos.AdjustY(pCur->GetHeight() );
+ aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos );
+ }
+ aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos );
+ }
+
+ tools::Rectangle aRect( aPos, aPos );
+ QuickHelpFlags const nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer;
+ nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign);
+ pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
+}
+
+bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult )
+{
+ if ( aStart.isEmpty() )
+ return false;
+
+ aStart = ScGlobal::getCharClass().uppercase( aStart );
+ sal_Int32 nPos = aStart.getLength() - 1;
+ sal_Unicode c = aStart[ nPos ];
+ // fdo#75264 use maFormulaChar to check if characters are used in function names
+ ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c );
+ if ( p == maFormulaChar.end() )
+ return false; // last character is not part of any function name, quit
+
+ ::std::vector<sal_Unicode> aTemp { c };
+ for(sal_Int32 i = nPos - 1; i >= 0; --i)
+ {
+ c = aStart[ i ];
+ p = maFormulaChar.find( c );
+
+ if (p == maFormulaChar.end())
+ break;
+
+ aTemp.push_back( c );
+ }
+
+ ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin();
+ aResult = OUString( *rIt++ );
+ while ( rIt != aTemp.rend() )
+ aResult += OUStringChar( *rIt++ );
+
+ return true;
+}
+
+namespace {
+ /// Rid ourselves of unwanted " quoted json characters.
+ OString escapeJSON(const OUString &aStr)
+ {
+ OUString aEscaped = aStr;
+ aEscaped = aEscaped.replaceAll("\n", " ");
+ aEscaped = aEscaped.replaceAll("\"", "'");
+ return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8);
+ }
+}
+
+void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec )
+{
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (rFuncStrVec.size() && pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ auto aPos = pFormulaData->begin();
+ sal_uInt32 nCurIndex = std::distance(aPos, miAutoPosFormula);
+ const sal_uInt32 nSize = pFormulaData->size();
+
+ OUString aFuncNameStr;
+ OUString aDescFuncNameStr;
+ OStringBuffer aPayload("[ ");
+ for (const OUString& rFunc : rFuncStrVec)
+ {
+ if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
+ {
+ aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
+ }
+ else
+ {
+ aFuncNameStr = rFunc;
+ }
+
+ FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
+ aDescFuncNameStr = aFuncNameStr + "()";
+ sal_Int32 nNextFStart = 0;
+ const IFunctionDescription* ppFDesc;
+ ::std::vector< OUString > aArgs;
+ OUString eqPlusFuncName = "=" + aDescFuncNameStr;
+ if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
+ {
+ if ( !ppFDesc->getFunctionName().isEmpty() )
+ {
+ aPayload.append("{"
+ "\"index\": "
+ + OString::number(static_cast<sal_Int64>(nCurIndex))
+ + ", "
+ "\"signature\": \""
+ + escapeJSON(ppFDesc->getSignature())
+ + "\", "
+ "\"description\": \""
+ + escapeJSON(ppFDesc->getDescription())
+ + "\"}, ");
+ }
+ }
+ ++nCurIndex;
+ if (nCurIndex == nSize)
+ nCurIndex = 0;
+ }
+ sal_Int32 nLen = aPayload.getLength();
+ aPayload[nLen - 2] = ' ';
+ aPayload[nLen - 1] = ']';
+
+ OString s = aPayload.makeStringAndClear();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s);
+ }
+ // not tunnel tooltips in the lok case
+ return;
+ }
+
+ OUStringBuffer aTipStr;
+ OUString aFuncNameStr;
+ OUString aDescFuncNameStr;
+ ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin();
+ sal_Int32 nMaxFindNumber = 3;
+ sal_Int32 nRemainFindNumber = nMaxFindNumber;
+ for ( ; itStr != rFuncStrVec.end(); ++itStr )
+ {
+ const OUString& rFunc = *itStr;
+ if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
+ {
+ aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
+ }
+ else
+ {
+ aFuncNameStr = rFunc;
+ }
+ if ( itStr == rFuncStrVec.begin() )
+ {
+ aTipStr = "[";
+ aDescFuncNameStr = aFuncNameStr + "()";
+ }
+ else
+ {
+ aTipStr.append(", ");
+ }
+ aTipStr.append(aFuncNameStr);
+ if ( itStr == rFuncStrVec.begin() )
+ aTipStr.append("]");
+ if ( --nRemainFindNumber <= 0 )
+ break;
+ }
+ sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber;
+ if ( nRemainFindNumber == 0 && nRemainNumber > 0 )
+ {
+ OUString aMessage( ScResId( STR_FUNCTIONS_FOUND ) );
+ aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber));
+ aMessage = aMessage.replaceFirst("%1", aTipStr);
+ aTipStr = aMessage;
+ }
+ FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
+ sal_Int32 nNextFStart = 0;
+ const IFunctionDescription* ppFDesc;
+ ::std::vector< OUString > aArgs;
+ OUString eqPlusFuncName = "=" + aDescFuncNameStr;
+ if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
+ {
+ if ( !ppFDesc->getFunctionName().isEmpty() )
+ {
+ aTipStr.append(" : " + ppFDesc->getDescription());
+ }
+ }
+ ShowTip( aTipStr.makeStringAndClear() );
+}
+
+void ScInputHandler::UseFormulaData()
+{
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+
+ /* TODO: MLFORMULA: this should work also with multi-line formulas. */
+ if ( !(pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1) )
+ return;
+
+ OUString aParagraph = mpEditEngine->GetText( 0 );
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+
+ // Due to differences between table and input cell (e.g clipboard with line breaks),
+ // the selection may not be in line with the EditEngine anymore.
+ // Just return without any indication as to why.
+ if ( aSel.nEndPos > aParagraph.getLength() )
+ return;
+
+ if ( aParagraph.getLength() > aSel.nEndPos &&
+ ( ScGlobal::getCharClass().isLetterNumeric( aParagraph, aSel.nEndPos ) ||
+ aParagraph[ aSel.nEndPos ] == '_' ||
+ aParagraph[ aSel.nEndPos ] == '.' ||
+ aParagraph[ aSel.nEndPos ] == '$' ) )
+ return;
+
+ // Is the cursor at the end of a word?
+ if ( aSel.nEndPos <= 0 )
+ return;
+
+ OUString aSelText( aParagraph.copy( 0, aSel.nEndPos ));
+
+ OUString aText;
+ if ( GetFuncName( aSelText, aText ) )
+ {
+ // function name is incomplete:
+ // show matching functions name as tip above cell
+ ::std::vector<OUString> aNewVec;
+ miAutoPosFormula = pFormulaData->end();
+ miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false);
+ if (miAutoPosFormula != pFormulaData->end())
+ {
+ // check if partial function name is not between quotes
+ sal_Unicode cBetweenQuotes = 0;
+ for ( int n = 0; n < aSelText.getLength(); n++ )
+ {
+ if (cBetweenQuotes)
+ {
+ if (aSelText[n] == cBetweenQuotes)
+ cBetweenQuotes = 0;
+ }
+ else if ( aSelText[ n ] == '"' )
+ cBetweenQuotes = '"';
+ else if ( aSelText[ n ] == '\'' )
+ cBetweenQuotes = '\'';
+ }
+ if ( cBetweenQuotes )
+ return; // we're between quotes
+
+ ShowFuncList(aNewVec);
+ aAutoSearch = aText;
+ }
+ return;
+ }
+
+ // function name is complete:
+ // show tip below the cell with function name and arguments of function
+ ShowArgumentsTip( aSelText );
+}
+
+void ScInputHandler::NextFormulaEntry( bool bBack )
+{
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if ( pActiveView && pFormulaData )
+ {
+ ::std::vector<OUString> aNewVec;
+ ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack);
+ if (itNew != pFormulaData->end())
+ {
+ miAutoPosFormula = itNew;
+ ShowFuncList( aNewVec );
+ }
+ }
+
+ // For Tab we always call HideCursor first
+ if (pActiveView)
+ pActiveView->ShowCursor();
+}
+
+namespace {
+
+void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted )
+{
+ if (!pView)
+ return;
+
+ ESelection aSel = pView->GetSelection();
+
+ bool bNoInitialLetter = false;
+ OUString aOld = pView->GetEditEngine()->GetText(0);
+ // in case we want just insert a function and not completing
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ ESelection aSelRange = aSel;
+ --aSelRange.nStartPos;
+ --aSelRange.nEndPos;
+ pView->SetSelection(aSelRange);
+ pView->SelectCurrentWord();
+
+ if ( aOld == "=" )
+ {
+ bNoInitialLetter = true;
+ aSelRange.nStartPos = 1;
+ aSelRange.nEndPos = 1;
+ pView->SetSelection(aSelRange);
+ }
+ else if ( pView->GetSelected().startsWith("()") )
+ {
+ bNoInitialLetter = true;
+ ++aSelRange.nStartPos;
+ ++aSelRange.nEndPos;
+ pView->SetSelection(aSelRange);
+ }
+ }
+
+ if(!bNoInitialLetter)
+ {
+ const sal_Int32 nMinLen = std::max(aSel.nEndPos - aSel.nStartPos, sal_Int32(1));
+ // Since transliteration service is used to test for match, the replaced string could be
+ // longer than rInsert, so in order to find longest match before the cursor, test whole
+ // string from start to current cursor position (don't limit to length of rInsert)
+ // Disclaimer: I really don't know if a match longer than rInsert is actually possible,
+ // so the above is based on assumptions how "transliteration" might possibly work. If
+ // it's in fact impossible, an optimization would be useful to limit aSel.nStartPos to
+ // std::max(sal_Int32(0), aSel.nEndPos - rInsert.getLength()).
+ aSel.nStartPos = 0;
+ pView->SetSelection(aSel);
+ const OUString aAll = pView->GetSelected();
+ OUString aMatch;
+ for (sal_Int32 n = aAll.getLength(); n >= nMinLen && aMatch.isEmpty(); --n)
+ {
+ const OUString aTest = aAll.copy(aAll.getLength() - n); // n trailing chars
+ if (ScGlobal::GetTransliteration().isMatch(aTest, rInsert))
+ aMatch = aTest; // Found => break the loop
+ }
+
+ aSel.nStartPos = aSel.nEndPos - aMatch.getLength();
+ pView->SetSelection(aSel);
+ }
+
+ OUString aInsStr = rInsert;
+ sal_Int32 nInsLen = aInsStr.getLength();
+ bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '('
+ && aInsStr[nInsLen-1] == ')' );
+ if ( bDoParen )
+ {
+ // Do not insert parentheses after function names if there already are some
+ // (e.g. if the function name was edited).
+ ESelection aWordSel = pView->GetSelection();
+
+ // aWordSel.EndPos points one behind string if word at end
+ if (aWordSel.nEndPos < aOld.getLength())
+ {
+ sal_Unicode cNext = aOld[aWordSel.nEndPos];
+ if ( cNext == '(' )
+ {
+ bDoParen = false;
+ aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses
+ }
+ }
+ }
+
+ pView->InsertText( aInsStr );
+
+ if ( bDoParen ) // Put cursor between parentheses
+ {
+ aSel = pView->GetSelection();
+ --aSel.nStartPos;
+ --aSel.nEndPos;
+ pView->SetSelection(aSel);
+
+ rParInserted = true;
+ }
+}
+
+}
+
+void ScInputHandler::PasteFunctionData()
+{
+ if (pFormulaData && miAutoPosFormula != pFormulaData->end())
+ {
+ const ScTypedStrData& rData = *miAutoPosFormula;
+ OUString aInsert = rData.GetString();
+ if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement)
+ aInsert = OUString::Concat(aInsert.subView( 0, aInsert.getLength()-1)) + "()";
+ bool bParInserted = false;
+
+ DataChanging(); // Cannot be new
+ completeFunction( pTopView, aInsert, bParInserted );
+ completeFunction( pTableView, aInsert, bParInserted );
+ DataChanged();
+ ShowTipCursor();
+
+ if (bParInserted)
+ AutoParAdded();
+ }
+
+ HideTip();
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if (comphelper::LibreOfficeKit::isActive() && pTopView && pInputWin)
+ pInputWin->TextGrabFocus();
+ if (pActiveView)
+ pActiveView->ShowCursor();
+}
+
+void ScInputHandler::LOKPasteFunctionData(const OUString& rFunctionName)
+{
+ // in case we have no top view try to create it
+ if (!pTopView && pInputWin)
+ {
+ ScInputMode eCurMode = eMode;
+ SetMode(SC_INPUT_TOP);
+ if (!pTopView)
+ SetMode(eCurMode);
+ }
+
+ EditView* pEditView = pTopView ? pTopView : pTableView;
+
+ if (!pActiveViewSh || !pEditView)
+ return;
+
+ bool bEdit = false;
+ OUString aFormula;
+ const EditEngine* pEditEngine = pEditView->GetEditEngine();
+ if (pEditEngine)
+ {
+ aFormula = pEditEngine->GetText(0);
+ /* TODO: LOK: are you sure you want '+' and '-' let start formulas with
+ * function names? That was meant for "data typist" numeric keyboard
+ * input. */
+ bEdit = aFormula.getLength() > 1 && (aFormula[0] == '=' || aFormula[0] == '+' || aFormula[0] == '-');
+ }
+
+ if ( !bEdit )
+ {
+ OUString aNewFormula('=');
+ if ( aFormula.startsWith("=") )
+ aNewFormula = aFormula;
+
+ InputReplaceSelection( aNewFormula );
+ }
+
+ if (pFormulaData)
+ {
+ OUString aNew;
+ ScTypedCaseStrSet::const_iterator aPos = findText(*pFormulaData, pFormulaData->begin(), rFunctionName, aNew, /* backward = */false);
+
+ if (aPos != pFormulaData->end())
+ {
+ miAutoPosFormula = aPos;
+ PasteFunctionData();
+ }
+ }
+}
+
+void ScTabViewShell::LOKSendFormulabarUpdate(EditView* pActiveView,
+ const OUString& rText,
+ const ESelection& rSelection)
+{
+ OUString aSelection;
+ if (pActiveView)
+ {
+ aSelection = OUString::number(pActiveView->GetPosWithField(0, rSelection.nStartPos)) + ";" +
+ OUString::number(pActiveView->GetPosWithField(0, rSelection.nEndPos)) + ";" +
+ OUString::number(rSelection.nStartPara) + ";" + OUString::number(rSelection.nEndPara);
+ }
+ else
+ {
+ aSelection = OUString::number(rSelection.nStartPos) + ";" + OUString::number(rSelection.nEndPos) + ";" +
+ OUString::number(rSelection.nStartPara) + ";" + OUString::number(rSelection.nEndPara);
+ }
+
+ sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(this);
+
+ // We can get three updates per keystroke, StartExtTextInput, ExtTextInput and PostExtTextInput
+ // Skip duplicate updates. Be conservative and don't skip duplicates that are 5+ seconds
+ // apart.
+ std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
+ if (maSendFormulabarUpdate.m_nShellId == nCurrentShellId &&
+ maSendFormulabarUpdate.m_aText == rText &&
+ maSendFormulabarUpdate.m_aSelection == aSelection &&
+ std::chrono::duration_cast<std::chrono::seconds>(
+ now - maSendFormulabarUpdate.m_nTimeStamp) < std::chrono::seconds(5))
+ {
+ return;
+ }
+
+ maSendFormulabarUpdate.m_nShellId = nCurrentShellId;
+ maSendFormulabarUpdate.m_aText = rText;
+ maSendFormulabarUpdate.m_aSelection = aSelection;
+ maSendFormulabarUpdate.m_nTimeStamp = now;
+ maSendFormulabarUpdate.Send();
+}
+
+void ScTabViewShell::SendFormulabarUpdate::Send()
+{
+ std::unique_ptr<jsdialog::ActionDataMap> pData = std::make_unique<jsdialog::ActionDataMap>();
+ (*pData)["action_type"_ostr] = "setText";
+ (*pData)["text"_ostr] = m_aText;
+ (*pData)["selection"_ostr] = m_aSelection;
+ OUString sWindowId = OUString::number(m_nShellId) + "formulabar";
+ jsdialog::SendAction(sWindowId, "sc_input_window", std::move(pData));
+}
+
+// Calculate selection and display as tip help
+static OUString lcl_Calculate( const OUString& rFormula, ScDocument& rDoc, const ScAddress &rPos )
+{
+ //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
+ // Quotation marks for Strings are only inserted here.
+
+ if(rFormula.isEmpty())
+ return OUString();
+
+ std::optional<ScSimpleFormulaCalculator> pCalc( std::in_place, rDoc, rPos, rFormula, false );
+
+ // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
+ // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
+ bool bColRowName = pCalc->HasColRowName();
+ if ( bColRowName )
+ {
+ // ColRowName in RPN code?
+ if ( pCalc->GetCode()->GetCodeLen() <= 1 )
+ { // ==1: Single one is as a Parameter always a Range
+ // ==0: It might be one, if ...
+ OUString aBraced = "(" + rFormula + ")";
+ pCalc.emplace( rDoc, rPos, aBraced, false );
+ }
+ else
+ bColRowName = false;
+ }
+
+ FormulaError nErrCode = pCalc->GetErrCode();
+ if ( nErrCode != FormulaError::NONE )
+ return ScGlobal::GetErrorString(nErrCode);
+
+ SvNumberFormatter& aFormatter = *rDoc.GetFormatTable();
+ OUString aValue;
+ if ( pCalc->IsValue() )
+ {
+ double n = pCalc->GetValue();
+ sal_uInt32 nFormat = aFormatter.GetStandardFormat( n, 0,
+ pCalc->GetFormatType(), ScGlobal::eLnge );
+ aFormatter.GetInputLineString( n, nFormat, aValue );
+ //! display OutputString but insert InputLineString
+ }
+ else
+ {
+ OUString aStr = pCalc->GetString().getString();
+ sal_uInt32 nFormat = aFormatter.GetStandardFormat(
+ pCalc->GetFormatType(), ScGlobal::eLnge);
+ {
+ const Color* pColor;
+ aFormatter.GetOutputString( aStr, nFormat,
+ aValue, &pColor );
+ }
+
+ aValue = "\"" + aValue + "\"";
+ //! Escape quotation marks in String??
+ }
+
+ ScRange aTestRange;
+ if ( bColRowName || (aTestRange.Parse(rFormula, rDoc) & ScRefFlags::VALID) )
+ aValue += " ...";
+
+ return aValue;
+}
+
+void ScInputHandler::FormulaPreview()
+{
+ OUString aValue;
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if ( pActiveView && pActiveViewSh )
+ {
+ OUString aPart = pActiveView->GetSelected();
+ if (aPart.isEmpty())
+ aPart = mpEditEngine->GetText(0);
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
+ aValue = lcl_Calculate( aPart, rDoc, aCursorPos );
+ }
+
+ if (!aValue.isEmpty())
+ {
+ ShowTip( aValue ); // Display as QuickHelp
+ aManualTip = aValue; // Set after ShowTip
+ if (pFormulaData)
+ miAutoPosFormula = pFormulaData->end();
+ if (pColumnData)
+ miAutoPosColumn = pColumnData->end();
+ }
+}
+
+void ScInputHandler::PasteManualTip()
+{
+ // Three dots at the end -> Range reference -> do not insert
+ // FIXME: Once we have matrix constants, we can change this
+ sal_Int32 nTipLen = aManualTip.getLength();
+ sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen));
+ if ( nTipLen && ( nTipLen < 3 || aManualTip.subView( nTipLen2-3 ) != u"..." ) )
+ {
+ DataChanging(); // Cannot be new
+
+ OUString aInsert = aManualTip;
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if (!pActiveView->HasSelection())
+ {
+ // Nothing selected -> select everything
+ sal_Int32 nOldLen = mpEditEngine->GetTextLen(0);
+ ESelection aAllSel( 0, 0, 0, nOldLen );
+ if ( pTopView )
+ pTopView->SetSelection( aAllSel );
+ if ( pTableView )
+ pTableView->SetSelection( aAllSel );
+ }
+
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+ OSL_ENSURE( !aSel.nStartPara && !aSel.nEndPara, "Too many paragraphs in Formula" );
+ if ( !aSel.nStartPos ) // Selection from the start?
+ {
+ if ( aSel.nEndPos == mpEditEngine->GetTextLen(0) )
+ {
+ // Everything selected -> skip quotation marks
+ if ( aInsert[0] == '"' )
+ aInsert = aInsert.copy(1);
+ sal_Int32 nInsLen = aInsert.getLength();
+ if ( aInsert.endsWith("\"") )
+ aInsert = aInsert.copy( 0, nInsLen-1 );
+ }
+ else if ( aSel.nEndPos )
+ {
+ // Not everything selected -> do not overwrite equality sign
+ //FIXME: Even double equality signs??
+ aSel.nStartPos = 1;
+ if ( pTopView )
+ pTopView->SetSelection( aSel );
+ if ( pTableView )
+ pTableView->SetSelection( aSel );
+ }
+ }
+ if ( pTopView )
+ pTopView->InsertText( aInsert, true );
+ if ( pTableView )
+ pTableView->InsertText( aInsert, true );
+
+ DataChanged();
+ }
+
+ HideTip();
+}
+
+void ScInputHandler::ResetAutoPar()
+{
+ nAutoPar = 0;
+}
+
+void ScInputHandler::AutoParAdded()
+{
+ ++nAutoPar; // Closing parenthesis can be overwritten
+}
+
+bool ScInputHandler::CursorAtClosingPar()
+{
+ // Test if the cursor is before a closing parenthesis
+ // Selection from SetReference has been removed before
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode )
+ {
+ ESelection aSel = pActiveView->GetSelection();
+ sal_Int32 nPos = aSel.nStartPos;
+ OUString aFormula = mpEditEngine->GetText(0);
+ if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' )
+ return true;
+ }
+ return false;
+}
+
+void ScInputHandler::SkipClosingPar()
+{
+ // this is called when a ')' is typed and the cursor is before a ')'
+ // that can be overwritten -> just set the cursor behind the ')'
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if (pActiveView)
+ {
+ ESelection aSel = pActiveView->GetSelection();
+ ++aSel.nStartPos;
+ ++aSel.nEndPos;
+
+ // this is in a formula (only one paragraph), so the selection
+ // can be used directly for the TopView
+
+ if ( pTopView )
+ pTopView->SetSelection( aSel );
+ if ( pTableView )
+ pTableView->SetSelection( aSel );
+ }
+
+ OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong");
+ --nAutoPar;
+}
+
+// Auto input
+
+void ScInputHandler::GetColData()
+{
+ if ( !pActiveViewSh )
+ return;
+
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
+
+ if ( pColumnData )
+ pColumnData->clear();
+ else
+ pColumnData.reset( new ScTypedCaseStrSet );
+
+ std::vector<ScTypedStrData> aEntries;
+ rDoc.GetDataEntries(
+ aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries);
+ if (!aEntries.empty())
+ pColumnData->insert(aEntries.begin(), aEntries.end());
+
+ miAutoPosColumn = pColumnData->end();
+}
+
+void ScInputHandler::UseColData() // When typing
+{
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if ( !(pActiveView && pColumnData) )
+ return;
+
+ // Only change when cursor is at the end
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+
+ sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
+ if ( aSel.nEndPara+1 != nParCnt )
+ return;
+
+ sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara );
+ if ( aSel.nEndPos != nParLen )
+ return;
+
+ OUString aText = GetEditText(mpEditEngine.get());
+ if (aText.isEmpty())
+ return;
+
+ std::vector< OUString > aResultVec;
+ OUString aNew;
+ sal_Int32 nLongestPrefixLen = 0;
+ miAutoPosColumn = pColumnData->end();
+ mbPartialPrefix = false;
+ miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, &nLongestPrefixLen);
+
+ if (nLongestPrefixLen <= 0 || aResultVec.empty())
+ return;
+
+ if (aResultVec.size() > 1)
+ {
+ mbPartialPrefix = true;
+ bUseTab = true; // Allow Ctrl (+ Shift + ) + TAB cycling.
+ miAutoPosColumn = pColumnData->end();
+
+ // Display the rest of longest common prefix as suggestion.
+ aNew = aResultVec[0].copy(0, nLongestPrefixLen);
+ }
+ else
+ {
+ aNew = aResultVec[0];
+ }
+
+ // Strings can contain line endings (e.g. due to dBase import),
+ // which would result in multiple paragraphs here, which is not desirable.
+ //! Then GetExactMatch doesn't work either
+ lcl_RemoveLineEnd( aNew );
+
+ // Keep paragraph, just append the rest
+ //! Exact replacement in EnterHandler !!!
+ // One Space between paragraphs:
+ sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1;
+ OUString aIns = aNew.copy(nEdLen);
+
+ // Selection must be "backwards", so the cursor stays behind the last
+ // typed character
+ ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.getLength(),
+ aSel.nEndPara, aSel.nEndPos );
+
+ // When editing in input line, apply to both edit views
+ if ( pTableView )
+ {
+ pTableView->InsertText( aIns );
+ pTableView->SetSelection( aSelection );
+ }
+ if ( pTopView )
+ {
+ pTopView->InsertText( aIns );
+ pTopView->SetSelection( aSelection );
+ }
+
+ aAutoSearch = aText; // To keep searching - nAutoPos is set
+}
+
+void ScInputHandler::NextAutoEntry( bool bBack )
+{
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if ( pActiveView && pColumnData )
+ {
+ if (!aAutoSearch.isEmpty())
+ {
+ // Is the selection still valid (could be changed via the mouse)?
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+ sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
+ if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara )
+ {
+ OUString aText = GetEditText(mpEditEngine.get());
+ sal_Int32 nSelLen = aSel.nEndPos - aSel.nStartPos;
+ sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara );
+ if ( aSel.nEndPos == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen )
+ {
+ OUString aNew;
+ ScTypedCaseStrSet::const_iterator itNew =
+ findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack);
+
+ if (itNew != pColumnData->end())
+ {
+ // match found!
+ miAutoPosColumn = itNew;
+ bInOwnChange = true; // disable ModifyHdl (reset below)
+ mbPartialPrefix = false;
+
+ lcl_RemoveLineEnd( aNew );
+ OUString aIns = aNew.copy(aAutoSearch.getLength());
+
+ // when editing in input line, apply to both edit views
+ if ( pTableView )
+ {
+ pTableView->DeleteSelected();
+ pTableView->InsertText( aIns );
+ pTableView->SetSelection( ESelection(
+ aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
+ aSel.nEndPara, aSel.nStartPos ) );
+ }
+ if ( pTopView )
+ {
+ pTopView->DeleteSelected();
+ pTopView->InsertText( aIns );
+ pTopView->SetSelection( ESelection(
+ aSel.nEndPara, aSel.nStartPos + aIns.getLength(),
+ aSel.nEndPara, aSel.nStartPos ) );
+ }
+
+ bInOwnChange = false;
+ }
+ }
+ }
+ }
+ }
+
+ // For Tab, HideCursor was always called first
+ if (pActiveView)
+ pActiveView->ShowCursor();
+}
+
+// Highlight parentheses
+void ScInputHandler::UpdateParenthesis()
+{
+ // Find parentheses
+ //TODO: Can we disable parentheses highlighting per parentheses?
+ bool bFound = false;
+ if ( bFormulaMode && eMode != SC_INPUT_TOP )
+ {
+ if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom
+ {
+ ESelection aSel = pTableView->GetSelection();
+ if (aSel.nStartPos)
+ {
+ // Examine character left to the cursor
+ sal_Int32 nPos = aSel.nStartPos - 1;
+ OUString aFormula = mpEditEngine->GetText(aSel.nStartPara);
+ sal_Unicode c = aFormula[nPos];
+ if ( c == '(' || c == ')' )
+ {
+ // Note this matches only within one paragraph.
+ sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos );
+ if ( nOther != -1 )
+ {
+ SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
+ aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
+
+ //! Distinguish if cell is already highlighted!!!!
+ if (bParenthesisShown)
+ {
+ // Remove old highlighting
+ sal_Int32 nCount = mpEditEngine->GetParagraphCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
+ }
+
+ ESelection aSelThis( aSel.nStartPara, nPos, aSel.nStartPara, nPos+1);
+ mpEditEngine->QuickSetAttribs( aSet, aSelThis );
+ ESelection aSelOther( aSel.nStartPara, nOther, aSel.nStartPara, nOther+1);
+ mpEditEngine->QuickSetAttribs( aSet, aSelOther );
+
+ // Dummy InsertText for Update and Paint (selection is empty)
+ pTableView->InsertText( OUString() );
+
+ bFound = true;
+ }
+ }
+ }
+
+ // mark parenthesis right of cursor if it will be overwritten (nAutoPar)
+ // with different color (COL_LIGHTBLUE) ??
+ }
+ }
+
+ // Remove old highlighting, if no new one is set
+ if ( bParenthesisShown && !bFound && pTableView )
+ {
+ sal_Int32 nCount = mpEditEngine->GetParagraphCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
+ }
+
+ bParenthesisShown = bFound;
+}
+
+void ScInputHandler::ViewShellGone(const ScTabViewShell* pViewSh) // Executed synchronously!
+{
+ if ( pViewSh == pActiveViewSh )
+ {
+ pLastState.reset();
+ pLastPattern = nullptr;
+ }
+
+ if ( pViewSh == pRefViewSh )
+ {
+ //! The input from the EnterHandler does not arrive anymore
+ // We end the EditMode anyways
+ EnterHandler();
+ bFormulaMode = false;
+ pRefViewSh = nullptr;
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
+ SC_MOD()->SetRefInputHdl(nullptr);
+ if (pInputWin)
+ pInputWin->SetFormulaMode(false);
+ UpdateAutoCorrFlag();
+ }
+
+ pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+
+ if ( pActiveViewSh && pActiveViewSh == pViewSh )
+ {
+ OSL_FAIL("pActiveViewSh is gone");
+ pActiveViewSh = nullptr;
+ }
+
+ if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
+ UpdateRefDevice(); // Don't keep old document's printer as RefDevice
+}
+
+void ScInputHandler::UpdateActiveView()
+{
+ ImplCreateEditEngine();
+
+ // #i20588# Don't rely on focus to find the active edit view. Instead, the
+ // active pane at the start of editing is now stored (GetEditActivePart).
+ // GetActiveWin (the currently active pane) fails for ref input across the
+ // panes of a split view.
+
+ vcl::Window* pShellWin = pActiveViewSh ?
+ pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) :
+ nullptr;
+
+ sal_uInt16 nCount = mpEditEngine->GetViewCount();
+ if (nCount > 0)
+ {
+ pTableView = mpEditEngine->GetView();
+ for (sal_uInt16 i=1; i<nCount; i++)
+ {
+ EditView* pThis = mpEditEngine->GetView(i);
+ vcl::Window* pWin = pThis->GetWindow();
+ if ( pWin==pShellWin )
+ pTableView = pThis;
+ }
+ }
+ else
+ pTableView = nullptr;
+
+ // setup the pTableView editeng for tiled rendering to get cursor and selections
+ if (pTableView && pActiveViewSh)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ pTableView->RegisterViewShell(pActiveViewSh);
+ }
+ }
+
+ if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE))
+ {
+ // tdf#71409: Always create the edit engine instance for the input
+ // window, in order to properly manage accessibility events.
+ pTopView = pInputWin->GetEditView();
+ if (eMode != SC_INPUT_TOP)
+ pTopView = nullptr;
+ }
+ else
+ pTopView = nullptr;
+}
+
+void ScInputHandler::SetInputWindow( ScInputWindow* pNew )
+{
+ pInputWin = pNew;
+}
+
+void ScInputHandler::StopInputWinEngine( bool bAll )
+{
+ if (pInputWin && !pInputWin->isDisposed())
+ pInputWin->StopEditEngine( bAll );
+
+ pTopView = nullptr; // invalid now
+}
+
+EditView* ScInputHandler::GetActiveView()
+{
+ UpdateActiveView();
+ return pTopView ? pTopView : pTableView;
+}
+
+void ScInputHandler::ForgetLastPattern()
+{
+ pLastPattern = nullptr;
+ if ( !pLastState && pActiveViewSh )
+ pActiveViewSh->UpdateInputHandler( true ); // Get status again
+ else
+ NotifyChange( pLastState.get(), true );
+}
+
+void ScInputHandler::UpdateAdjust( sal_Unicode cTyped )
+{
+ SvxAdjust eSvxAdjust;
+ switch (eAttrAdjust)
+ {
+ case SvxCellHorJustify::Standard:
+ {
+ bool bNumber = false;
+ if (cTyped) // Restarted
+ bNumber = (cTyped>='0' && cTyped<='9'); // Only ciphers are numbers
+ else if ( pActiveViewSh )
+ {
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
+ bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE );
+ }
+ eSvxAdjust = bNumber ? SvxAdjust::Right : SvxAdjust::Left;
+ }
+ break;
+ case SvxCellHorJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ case SvxCellHorJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellHorJustify::Right:
+ eSvxAdjust = SvxAdjust::Right;
+ break;
+ default: // SvxCellHorJustify::Left
+ eSvxAdjust = SvxAdjust::Left;
+ break;
+ }
+
+ bool bAsianVertical = pLastPattern &&
+ pLastPattern->GetItem( ATTR_STACKED ).GetValue() &&
+ pLastPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue();
+ if ( bAsianVertical )
+ {
+ // Always edit at top of cell -> LEFT when editing vertically
+ eSvxAdjust = SvxAdjust::Left;
+ }
+
+ pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
+ mpEditEngine->SetDefaults( *pEditDefaults );
+
+ if ( pActiveViewSh )
+ {
+ pActiveViewSh->GetViewData().SetEditAdjust( eSvxAdjust );
+ }
+ mpEditEngine->SetVertical( bAsianVertical );
+}
+
+void ScInputHandler::RemoveAdjust()
+{
+ // Delete hard alignment attributes
+ bool bUndo = mpEditEngine->IsUndoEnabled();
+ if ( bUndo )
+ mpEditEngine->EnableUndo( false );
+
+ // Non-default paragraph attributes (e.g. from clipboard)
+ // must be turned into character attributes
+ mpEditEngine->RemoveParaAttribs();
+
+ if ( bUndo )
+ mpEditEngine->EnableUndo( true );
+
+}
+
+void ScInputHandler::RemoveRangeFinder()
+{
+ // Delete pRangeFindList and colors
+ mpEditEngine->SetUpdateLayout(false);
+ sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted
+ for (sal_Int32 i=0; i<nCount; i++)
+ mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR );
+ mpEditEngine->SetUpdateLayout(true);
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ pActiveView->ShowCursor( false );
+
+ DeleteRangeFinder(); // Deletes the list and the labels on the table
+}
+
+bool ScInputHandler::StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated,
+ ScEditEngineDefaulter* pTopEngine )
+{
+ bool bNewTable = false;
+
+ if (bModified)
+ return false;
+
+ if (pActiveViewSh)
+ {
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
+
+ if (!rDoc.ValidCol(aCursorPos.Col()))
+ return false;
+
+ ImplCreateEditEngine();
+ UpdateActiveView();
+ SyncViews();
+
+
+ const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData();
+ ScEditableTester aTester;
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ aTester.TestSelection( rDoc, rMark );
+ else
+ aTester.TestSelectedBlock(
+ rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark );
+
+ bool bStartInputMode = true;
+
+ if (!aTester.IsEditable())
+ {
+ bProtected = true;
+ // We allow read-only input mode activation regardless
+ // whether it's part of an array or not or whether explicit cell
+ // activation is requested (double-click or F2) or a click in input
+ // line.
+ bool bShowError = (!bInputActivated || !aTester.GetMessageId() || aTester.GetMessageId() != STR_PROTECTIONERR) &&
+ !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly();
+ if (bShowError)
+ {
+ eMode = SC_INPUT_NONE;
+ StopInputWinEngine( true );
+ UpdateFormulaMode();
+ if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) )
+ {
+ // Prevent repeated error messages for the same cell from command events
+ // (for keyboard events, multiple messages are wanted).
+ // Set the flag before showing the error message because the command handler
+ // for the next IME command may be called when showing the dialog.
+ if ( bFromCommand )
+ bCommandErrorShown = true;
+
+ pActiveViewSh->GetActiveWin()->GrabFocus();
+ pActiveViewSh->ErrorMessage(aTester.GetMessageId());
+ }
+ bStartInputMode = false;
+ }
+ }
+
+ if (bStartInputMode)
+ {
+ // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
+ mpEditEngine->SetUpdateLayout( false );
+
+ // Take over attributes in EditEngine
+ const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(),
+ aCursorPos.Row(),
+ aCursorPos.Tab() );
+ if (!SfxPoolItem::areSame(pPattern, pLastPattern))
+ {
+ // Percent format?
+ const SfxItemSet& rAttrSet = pPattern->GetItemSet();
+
+ if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nFormat = pItem->GetValue();
+ if (SvNumFormatType::PERCENT == rDoc.GetFormatTable()->GetType( nFormat ))
+ nCellPercentFormatDecSep = rDoc.GetFormatTable()->GetFormatDecimalSep( nFormat).toChar();
+ else
+ nCellPercentFormatDecSep = 0;
+ }
+ else
+ nCellPercentFormatDecSep = 0; // Default: no percent
+
+ // Validity specified?
+ if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALIDDATA ) )
+ nValidation = pItem->GetValue();
+ else
+ nValidation = 0;
+
+ // EditEngine Defaults
+ // In no case SetParaAttribs, because the EditEngine might already
+ // be filled (for Edit cells).
+ // SetParaAttribs would change the content.
+
+ //! The SetDefaults is now (since MUST/src602
+ //! EditEngine changes) implemented as a SetParaAttribs.
+ //! Any problems?
+
+ pPattern->FillEditItemSet( pEditDefaults.get() );
+ mpEditEngine->SetDefaults( *pEditDefaults );
+ pLastPattern = pPattern;
+ bLastIsSymbol = pPattern->IsSymbolFont();
+
+ // Background color must be known for automatic font color.
+ // For transparent cell background, the document background color must be used.
+
+ Color aBackCol = pPattern->GetItem( ATTR_BACKGROUND ).GetColor();
+ ScModule* pScMod = SC_MOD();
+ if ( aBackCol.IsTransparent() ||
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ mpEditEngine->SetBackgroundColor( aBackCol );
+
+ // Adjustment
+ eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
+ if ( eAttrAdjust == SvxCellHorJustify::Repeat &&
+ pPattern->GetItem(ATTR_LINEBREAK).GetValue() )
+ {
+ // #i31843# "repeat" with "line breaks" is treated as default alignment
+ eAttrAdjust = SvxCellHorJustify::Standard;
+ }
+ }
+
+ if (pTopEngine)
+ {
+ // Necessary to sync SvxAutoCorrect behavior. This has to be
+ // done before InitRangeFinder() below.
+ MergeLanguageAttributes( *pTopEngine);
+ }
+
+ // UpdateSpellSettings enables online spelling if needed
+ // -> also call if attributes are unchanged
+ UpdateSpellSettings( true ); // uses pLastPattern
+
+ // Fill EditEngine
+ OUString aStr;
+ if (bTextValid)
+ {
+ mpEditEngine->SetTextCurrentDefaults(aCurrentText);
+ aStr = aCurrentText;
+ bTextValid = false;
+ aCurrentText.clear();
+ }
+ else
+ aStr = GetEditText(mpEditEngine.get());
+
+ // cTyped!=0 is overtyping, not editing.
+ mbEditingExistingContent = !cTyped && !aStr.isEmpty();
+
+ if (aStr.startsWith("{=") && aStr.endsWith("}") ) // Matrix formula?
+ {
+ aStr = aStr.copy(1, aStr.getLength() -2);
+ mpEditEngine->SetTextCurrentDefaults(aStr);
+ if ( pInputWin )
+ pInputWin->SetTextString(aStr, true);
+ }
+
+ UpdateAdjust( cTyped );
+
+ if ( SC_MOD()->GetAppOptions().GetAutoComplete() )
+ GetColData();
+
+ if (!cTyped && !bCreatingFuncView && StartsLikeFormula(aStr))
+ InitRangeFinder(aStr); // Formula is being edited -> RangeFinder
+
+ bNewTable = true; // -> PostEditView Call
+ }
+ }
+
+ if (!bProtected && pInputWin)
+ pInputWin->SetOkCancelMode();
+
+ return bNewTable;
+}
+
+void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const
+{
+ const SfxItemSet& rSrcSet = mpEditEngine->GetDefaults();
+ rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE ));
+ rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CJK ));
+ rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CTL ));
+}
+
+static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel )
+{
+ OSL_ENSURE( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" );
+
+ EditEngine* pEngine = pEditView->GetEditEngine();
+ sal_Int32 nCount = pEngine->GetParagraphCount();
+ if (nCount > 1)
+ {
+ sal_Int32 nParLen = pEngine->GetTextLen(rSel.nStartPara);
+ while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount)
+ {
+ rSel.nStartPos -= nParLen + 1; // Including space from line break
+ nParLen = pEngine->GetTextLen(++rSel.nStartPara);
+ }
+
+ nParLen = pEngine->GetTextLen(rSel.nEndPara);
+ while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount)
+ {
+ rSel.nEndPos -= nParLen + 1; // Including space from line break
+ nParLen = pEngine->GetTextLen(++rSel.nEndPara);
+ }
+ }
+
+ ESelection aSel = pEditView->GetSelection();
+
+ if ( rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara
+ || rSel.nStartPos != aSel.nStartPos || rSel.nEndPos != aSel.nEndPos )
+ pEditView->SetSelection( rSel );
+}
+
+void ScInputHandler::SyncViews( const EditView* pSourceView )
+{
+ if (pSourceView)
+ {
+ bool bSelectionForTopView = false;
+ if (pTopView && pTopView != pSourceView)
+ bSelectionForTopView = true;
+ bool bSelectionForTableView = false;
+ if (pTableView && pTableView != pSourceView)
+ bSelectionForTableView = true;
+ if (bSelectionForTopView || bSelectionForTableView)
+ {
+ ESelection aSel(pSourceView->GetSelection());
+ if (bSelectionForTopView)
+ pTopView->SetSelection(aSel);
+ if (bSelectionForTableView)
+ lcl_SetTopSelection(pTableView, aSel);
+ }
+ }
+ // Only sync selection from topView if we are actually editing there
+ else if (pTopView && pTableView)
+ {
+ ESelection aSel(pTopView->GetSelection());
+ lcl_SetTopSelection( pTableView, aSel );
+ }
+}
+
+IMPL_LINK_NOARG(ScInputHandler, ModifyHdl, LinkParamNone*, void)
+{
+ if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) &&
+ mpEditEngine && mpEditEngine->IsUpdateLayout() && pInputWin )
+ {
+ // Update input line from ModifyHdl for changes that are not
+ // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
+ OUString aText(ScEditUtil::GetMultilineString(*mpEditEngine));
+ lcl_RemoveTabs(aText);
+ pInputWin->SetTextString(aText, true);
+ }
+}
+
+/**
+ * @return true means new view created
+ */
+bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand )
+{
+ if (pActiveViewSh)
+ pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
+ bInOwnChange = true; // disable ModifyHdl (reset in DataChanged)
+
+ if ( eMode == SC_INPUT_NONE )
+ return StartTable( cTyped, bFromCommand, false, nullptr );
+ else
+ return false;
+}
+
+void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified )
+{
+ ImplCreateEditEngine();
+
+ if (eMode==SC_INPUT_NONE)
+ eMode = SC_INPUT_TYPE;
+
+ if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify )
+ {
+ // table EditEngine is formatted below, input line needs formatting after paste
+ // #i20282# not when called from the input line's modify handler
+ pTopView->GetEditEngine()->QuickFormatDoc( true );
+
+ // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
+ // can't safely access the EditEngine's current view, so the cursor has to be
+ // shown again here.
+ pTopView->ShowCursor();
+ }
+
+ if (bSetModified)
+ bModified = true;
+ bSelIsRef = false;
+
+ if ( pRangeFindList && !bInRangeUpdate )
+ RemoveRangeFinder(); // Delete attributes and labels
+
+ UpdateParenthesis(); // Highlight parentheses anew
+
+ const bool bUpdateKit = comphelper::LibreOfficeKit::isActive() && pActiveViewSh && pInputWin;
+
+ if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE)
+ {
+ OUString aText;
+ if (pInputWin)
+ aText = ScEditUtil::GetMultilineString(*mpEditEngine);
+ else
+ aText = GetEditText(mpEditEngine.get());
+ lcl_RemoveTabs(aText);
+
+ if (pInputWin)
+ {
+ // If we will end up updating LoKit at the end, we can skip it here
+ pInputWin->SetTextString(aText, !bUpdateKit);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (pActiveViewSh)
+ {
+ // TODO: deprecated?
+ pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8());
+ }
+ }
+ }
+
+ // If the cursor is before the end of a paragraph, parts are being pushed to
+ // the right (independently from the eMode) -> Adapt View!
+ // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
+ //
+ // First make sure the status handler is called now if the cursor
+ // is outside the visible area
+ mpEditEngine->QuickFormatDoc();
+
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ ESelection aSel;
+ if (pActiveView && pActiveViewSh)
+ {
+ ScViewData& rViewData = pActiveViewSh->GetViewData();
+
+ bool bNeedGrow = ( rViewData.GetEditAdjust() != SvxAdjust::Left ); // Always right-aligned
+ if (!bNeedGrow)
+ {
+ // Cursor before the end?
+ aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+ bNeedGrow = ( aSel.nEndPos != mpEditEngine->GetTextLen(aSel.nEndPara) );
+ }
+ if (!bNeedGrow)
+ {
+ bNeedGrow = rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() );
+ }
+ if (bNeedGrow)
+ {
+ // Adjust inplace view
+ rViewData.EditGrowY();
+ rViewData.EditGrowX();
+ }
+ }
+
+ if (bUpdateKit)
+ {
+ UpdateActiveView();
+ if (pActiveView)
+ aSel = pActiveView->GetSelection();
+
+ pActiveViewSh->LOKSendFormulabarUpdate(pActiveView,
+ ScEditUtil::GetMultilineString(*mpEditEngine),
+ aSel);
+ }
+
+ UpdateFormulaMode();
+ bTextValid = false; // Changes only in the EditEngine
+ bInOwnChange = false;
+}
+
+bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr ) const
+{
+ // For new input '+' and '-' may start the dreaded "lazy data typist"
+ // formula input, editing existing formula content can only start with '='.
+ return !rStr.empty() && (rStr[0] == '=' || (!mbEditingExistingContent && (rStr[0] == '+' || rStr[0] == '-')));
+}
+
+void ScInputHandler::UpdateFormulaMode()
+{
+ SfxApplication* pSfxApp = SfxGetpApp();
+
+ bool bIsFormula = !bProtected;
+ if (bIsFormula)
+ {
+ const OUString& rText = mpEditEngine->GetText(0);
+ bIsFormula = StartsLikeFormula(rText);
+ }
+
+ if ( bIsFormula )
+ {
+ if (!bFormulaMode)
+ {
+ bFormulaMode = true;
+ pRefViewSh = pActiveViewSh;
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
+ ScModule* pMod = SC_MOD();
+ pMod->SetRefInputHdl(this);
+ if (pInputWin)
+ pInputWin->SetFormulaMode(true);
+
+ // in LOK, we always need to perform the GetFormulaData() call so
+ // that the formula insertion works
+ if (comphelper::LibreOfficeKit::isActive() || pMod->GetAppOptions().GetAutoComplete())
+ GetFormulaData();
+
+ UpdateParenthesis();
+ UpdateAutoCorrFlag();
+ }
+ }
+ else // Deactivate
+ {
+ if (bFormulaMode)
+ {
+ ShowRefFrame();
+ bFormulaMode = false;
+ pRefViewSh = nullptr;
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
+ SC_MOD()->SetRefInputHdl(nullptr);
+ if (pInputWin)
+ pInputWin->SetFormulaMode(false);
+ UpdateAutoCorrFlag();
+ }
+ }
+}
+
+void ScInputHandler::ShowRefFrame()
+{
+ // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
+ // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
+ // A local variable is used instead.
+ ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( !(pRefViewSh && pRefViewSh != pVisibleSh) )
+ return;
+
+ bool bFound = false;
+ SfxViewFrame& rRefFrame = pRefViewSh->GetViewFrame();
+ SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst();
+ while ( pOneFrame && !bFound )
+ {
+ if ( pOneFrame == &rRefFrame )
+ bFound = true;
+ pOneFrame = SfxViewFrame::GetNext( *pOneFrame );
+ }
+
+ if (bFound)
+ {
+ // We count on Activate working synchronously here
+ // (pActiveViewSh is set while doing so)
+ pRefViewSh->SetActive(); // Appear and SetViewFrame
+
+ // pLastState is set correctly in the NotifyChange from the Activate
+ }
+ else
+ {
+ OSL_FAIL("ViewFrame for reference input is not here anymore");
+ }
+}
+
+void ScInputHandler::RemoveSelection()
+{
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if (!pActiveView)
+ return;
+
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.nStartPara = aSel.nEndPara;
+ aSel.nStartPos = aSel.nEndPos;
+ if (pTableView)
+ pTableView->SetSelection( aSel );
+ if (pTopView)
+ pTopView->SetSelection( aSel );
+}
+
+void ScInputHandler::InvalidateAttribs()
+{
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (!pViewFrm)
+ return;
+
+ SfxBindings& rBindings = pViewFrm->GetBindings();
+
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+
+ rBindings.Invalidate( SID_HYPERLINK_GETLINK );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+
+ rBindings.Invalidate( SID_SAVEDOC );
+ rBindings.Invalidate( SID_DOC_MODIFIED );
+}
+
+// --------------- public methods --------------------------------------------
+
+void ScInputHandler::SetMode( ScInputMode eNewMode, const OUString* pInitText, ScEditEngineDefaulter* pTopEngine )
+{
+ if ( eMode == eNewMode )
+ return;
+
+ ImplCreateEditEngine();
+
+ if (bProtected)
+ {
+ eMode = SC_INPUT_NONE;
+ StopInputWinEngine( true );
+ if (pActiveViewSh)
+ pActiveViewSh->GetActiveWin()->GrabFocus();
+ return;
+ }
+
+ if (eNewMode != SC_INPUT_NONE && pActiveViewSh)
+ // Disable paste mode when edit mode starts.
+ pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
+
+ bInOwnChange = true; // disable ModifyHdl (reset below)
+
+ ScInputMode eOldMode = eMode;
+ eMode = eNewMode;
+ if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode)
+ StopInputWinEngine( false );
+
+ if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE)
+ {
+ if (eOldMode == SC_INPUT_NONE) // not if switching between modes
+ {
+ if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine))
+ {
+ if (pActiveViewSh)
+ pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
+ }
+ }
+
+ if (pInitText)
+ {
+ mpEditEngine->SetTextCurrentDefaults(*pInitText);
+ bModified = true;
+ }
+
+ sal_Int32 nPara = mpEditEngine->GetParagraphCount()-1;
+ sal_Int32 nLen = mpEditEngine->GetText(nPara).getLength();
+ sal_uInt16 nCount = mpEditEngine->GetViewCount();
+
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP )
+ {
+ // Keep Selection
+ }
+ else
+ {
+ mpEditEngine->GetView(i)->
+ SetSelection( ESelection( nPara, nLen, nPara, nLen ) );
+ }
+ mpEditEngine->GetView(i)->ShowCursor(false);
+ }
+ }
+
+ UpdateActiveView();
+ if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE)
+ {
+ if (pTableView)
+ pTableView->SetEditEngineUpdateLayout(true);
+ }
+ else
+ {
+ if (pTopView)
+ pTopView->SetEditEngineUpdateLayout(true);
+ }
+
+ if (eNewMode != eOldMode)
+ UpdateFormulaMode();
+
+ bInOwnChange = false;
+}
+
+/**
+ * @return true if rString only contains digits (no autocorrect then)
+ */
+static bool lcl_IsNumber(std::u16string_view aString)
+{
+ size_t nLen = aString.size();
+ for (size_t i=0; i<nLen; i++)
+ {
+ sal_Unicode c = aString[i];
+ if ( c < '0' || c > '9' )
+ return false;
+ }
+ return true;
+}
+
+static void lcl_SelectionToEnd( EditView* pView )
+{
+ if ( pView )
+ {
+ EditEngine* pEngine = pView->GetEditEngine();
+ sal_Int32 nParCnt = pEngine->GetParagraphCount();
+ if ( nParCnt == 0 )
+ nParCnt = 1;
+ ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end
+ pView->SetSelection( aSel );
+ }
+}
+
+void ScInputHandler::EnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK )
+{
+ if (!mbDocumentDisposing && comphelper::LibreOfficeKit::isActive()
+ && pActiveViewSh != SfxViewShell::Current())
+ return;
+
+ if (!pActiveViewSh)
+ return;
+
+ // Macro calls for validity can cause a lot of problems, so inhibit
+ // nested calls of EnterHandler().
+ if (bInEnterHandler) return;
+ bInEnterHandler = true;
+ bInOwnChange = true; // disable ModifyHdl (reset below)
+ mbPartialPrefix = false;
+
+ ImplCreateEditEngine();
+
+ bool bMatrix = ( nBlockMode == ScEnterMode::MATRIX );
+
+ SfxApplication* pSfxApp = SfxGetpApp();
+ std::unique_ptr<EditTextObject> pObject;
+ std::unique_ptr<ScPatternAttr> pCellAttrs;
+ bool bForget = false; // Remove due to validity?
+
+ OUString aString = GetEditText(mpEditEngine.get());
+ OUString aPreAutoCorrectString(aString);
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString))
+ {
+ if (pColumnData && miAutoPosColumn != pColumnData->end())
+ {
+ // #i47125# If AutoInput appended something, do the final AutoCorrect
+ // with the cursor at the end of the input.
+ lcl_SelectionToEnd(pTopView);
+ lcl_SelectionToEnd(pTableView);
+ }
+
+ vcl::Window* pFrameWin = pActiveViewSh->GetFrameWin();
+
+ if (pTopView)
+ pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
+ if (pTableView)
+ pTableView->CompleteAutoCorrect(pFrameWin);
+ aString = GetEditText(mpEditEngine.get());
+ }
+ lcl_RemoveTabs(aString);
+ lcl_RemoveTabs(aPreAutoCorrectString);
+
+ if (aString.indexOf('\n') != -1)
+ {
+ // Cell contains line breaks, enable wrapping
+ ScLineBreakCell aBreakItem(true);
+ pActiveViewSh->ApplyAttr(aBreakItem);
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ pViewFrm->GetBindings().Invalidate(SID_ATTR_ALIGN_LINEBREAK);
+ }
+
+ // Test if valid (always with simple string)
+ if (bModified && nValidation)
+ {
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
+ const ScValidationData* pData = rDoc.GetValidationEntry( nValidation );
+ if (pData && pData->HasErrMsg())
+ {
+ // #i67990# don't use pLastPattern in EnterHandler
+ const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
+
+ bool bOk;
+
+ if (pData->GetDataMode() == SC_VALID_CUSTOM)
+ {
+ bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos, ScValidationData::CustomValidationPrivateAccess() );
+ }
+ else
+ {
+ bOk = pData->IsDataValid( aString, *pPattern, aCursorPos );
+ }
+
+ if (!bOk)
+ {
+ pActiveViewSh->StopMarking(); // (the InfoBox consumes the MouseButtonUp)
+
+ // tdf#125917 Release the grab that a current mouse-down event being handled
+ // by ScTabView has put on the mouse via its SelectionEngine.
+ // Otherwise the warning box cannot interact with the mouse
+ if (ScTabView* pView = pActiveViewSh->GetViewData().GetView())
+ {
+ if (ScViewSelectionEngine* pSelEngine = pView->GetSelEngine())
+ pSelEngine->ReleaseMouse();
+ }
+
+ if (bBeforeSavingInLOK)
+ {
+ // Invalid entry but not applied to the document model.
+ // Exit to complete the "save", leaving the edit view as it is
+ // for the user to continue after save.
+ bInOwnChange = false;
+ bInEnterHandler = false;
+ return;
+ }
+
+ if (pData->DoError(pActiveViewSh->GetFrameWeld(), aString, aCursorPos))
+ bForget = true; // Do not take over input
+ }
+ }
+ }
+
+ // Check for input into DataPilot table
+ if ( bModified && !bForget )
+ {
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
+ if ( pDPObj )
+ {
+ // Any input within the DataPilot table is either a valid renaming
+ // or an invalid action - normal cell input is always aborted
+ pActiveViewSh->DataPilotInput( aCursorPos, aString );
+ bForget = true;
+ }
+ }
+
+ std::vector<editeng::MisspellRanges> aMisspellRanges;
+ // UpdateLayout must be true during CompleteOnlineSpelling
+ const bool bUpdateLayout = mpEditEngine->SetUpdateLayout( true );
+ mpEditEngine->CompleteOnlineSpelling();
+ bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors();
+ if ( bSpellErrors )
+ {
+ // #i3820# If the spell checker flags numerical input as error,
+ // it still has to be treated as number, not EditEngine object.
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
+ // #i67990# don't use pLastPattern in EnterHandler
+ const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
+ if (pPattern)
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ // without conditional format, as in ScColumn::SetString
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
+ double nVal;
+ if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) )
+ {
+ bSpellErrors = false; // ignore the spelling errors
+ }
+ }
+ }
+
+ // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
+ // SetUpdateLayout must come after CompleteOnlineSpelling.
+ // The view is hidden in any case below (Broadcast).
+ mpEditEngine->SetUpdateLayout( false );
+
+ if ( bModified && !bForget ) // What is being entered (text/object)?
+ {
+ sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
+ if ( nParCnt == 0 )
+ nParCnt = 1;
+
+ bool bUniformAttribs = true;
+ SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0));
+ for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara)
+ {
+ SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara));
+ if (!(aPara1Attribs == aPara2Attribs))
+ {
+ // Paragraph format different from that of the 1st paragraph.
+ bUniformAttribs = false;
+ break;
+ }
+ }
+
+ ESelection aSel( 0, 0, nParCnt-1, mpEditEngine->GetTextLen(nParCnt-1) );
+ SfxItemSet aOldAttribs = mpEditEngine->GetAttribs( aSel );
+ const SfxPoolItem* pItem = nullptr;
+
+ // Find common (cell) attributes before RemoveAdjust
+ if ( bUniformAttribs )
+ {
+ std::optional<SfxItemSet> pCommonAttrs;
+ for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++)
+ {
+ SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
+ if ( eState == SfxItemState::SET &&
+ nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING &&
+ nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS &&
+ *pItem != pEditDefaults->Get(nId) )
+ {
+ if ( !pCommonAttrs )
+ pCommonAttrs.emplace( mpEditEngine->GetEmptyItemSet() );
+ pCommonAttrs->Put( *pItem );
+ }
+ }
+
+ if ( pCommonAttrs )
+ {
+ ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
+ pCellAttrs = std::make_unique<ScPatternAttr>(rDoc.GetPool());
+ pCellAttrs->GetFromEditItemSet( &*pCommonAttrs );
+ }
+ }
+
+ // Clear ParaAttribs (including adjustment)
+ RemoveAdjust();
+
+ bool bAttrib = false; // Formatting present?
+
+ // check if EditObject is needed
+ if (nParCnt > 1)
+ bAttrib = true;
+ else
+ {
+ for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++)
+ {
+ SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
+ if (eState == SfxItemState::DONTCARE)
+ bAttrib = true;
+ else if (eState == SfxItemState::SET)
+ {
+ // Keep same items in EditEngine as in ScEditAttrTester
+ if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING ||
+ nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS )
+ {
+ if ( *pItem != pEditDefaults->Get(nId) )
+ bAttrib = true;
+ }
+ }
+ }
+
+ // Contains fields?
+ SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ bAttrib = true;
+
+ // Not converted characters?
+ SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false );
+ if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET )
+ bAttrib = true;
+
+ // Always recognize formulas as formulas
+ // We still need the preceding test due to cell attributes
+ }
+
+ if (bSpellErrors)
+ mpEditEngine->GetAllMisspellRanges(aMisspellRanges);
+
+ if (bMatrix)
+ bAttrib = false;
+
+ if (bAttrib)
+ {
+ mpEditEngine->ClearSpellErrors();
+ pObject = mpEditEngine->CreateTextObject();
+ }
+ else if (SC_MOD()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case
+ {
+ // Perform case-matching only when the typed text is partial.
+ if (pColumnData && aAutoSearch.getLength() < aString.getLength())
+ aString = getExactMatch(*pColumnData, aString);
+ }
+ }
+
+ // Don't rely on ShowRefFrame switching the active view synchronously
+ // execute the function directly on the correct view's bindings instead
+ // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
+ ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
+
+ if (bFormulaMode)
+ {
+ ShowRefFrame();
+
+ if (pExecuteSh)
+ {
+ pExecuteSh->SetTabNo(aCursorPos.Tab());
+ pExecuteSh->ActiveGrabFocus();
+ }
+
+ bFormulaMode = false;
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
+ SC_MOD()->SetRefInputHdl(nullptr);
+ if (pInputWin)
+ pInputWin->SetFormulaMode(false);
+ UpdateAutoCorrFlag();
+ }
+ pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
+ DeleteRangeFinder();
+ ResetAutoPar();
+
+ bool bOldMod = bModified;
+
+ bModified = false;
+ bSelIsRef = false;
+ eMode = SC_INPUT_NONE;
+ StopInputWinEngine(true);
+
+ // Text input (through number formats) or ApplySelectionPattern modify
+ // the cell's attributes, so pLastPattern is no longer valid
+ pLastPattern = nullptr;
+
+ if (bOldMod && !bProtected && !bForget)
+ {
+ bool bInsertPreCorrectedString = true;
+ // No typographic quotes in formulas
+ if (aString.startsWith("="))
+ {
+ SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ if ( pAuto )
+ {
+ bInsertPreCorrectedString = false;
+ OUString aReplace(pAuto->GetStartDoubleQuote());
+ if( aReplace.isEmpty() )
+ aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkStart();
+ if( aReplace != "\"" )
+ aString = aString.replaceAll( aReplace, "\"" );
+
+ aReplace = OUString(pAuto->GetEndDoubleQuote());
+ if( aReplace.isEmpty() )
+ aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkEnd();
+ if( aReplace != "\"" )
+ aString = aString.replaceAll( aReplace, "\"" );
+
+ aReplace = OUString(pAuto->GetStartSingleQuote());
+ if( aReplace.isEmpty() )
+ aReplace = ScGlobal::getLocaleData().getQuotationMarkStart();
+ if( aReplace != "'" )
+ aString = aString.replaceAll( aReplace, "'" );
+
+ aReplace = OUString(pAuto->GetEndSingleQuote());
+ if( aReplace.isEmpty() )
+ aReplace = ScGlobal::getLocaleData().getQuotationMarkEnd();
+ if( aReplace != "'" )
+ aString = aString.replaceAll( aReplace, "'");
+ }
+ }
+
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint ) );
+
+ if ( pExecuteSh )
+ {
+ SfxBindings& rBindings = pExecuteSh->GetViewFrame().GetBindings();
+
+ sal_uInt16 nId = FID_INPUTLINE_ENTER;
+ if ( nBlockMode == ScEnterMode::BLOCK )
+ nId = FID_INPUTLINE_BLOCK;
+ else if ( nBlockMode == ScEnterMode::MATRIX )
+ nId = FID_INPUTLINE_MATRIX;
+
+ const SfxPoolItem* aArgs[2];
+ aArgs[1] = nullptr;
+
+ if ( bInsertPreCorrectedString && aString != aPreAutoCorrectString )
+ {
+ ScInputStatusItem aItem(FID_INPUTLINE_STATUS,
+ aCursorPos, aCursorPos, aCursorPos,
+ aPreAutoCorrectString, pObject.get());
+ aArgs[0] = &aItem;
+ rBindings.Execute(nId, aArgs);
+ }
+
+ ScInputStatusItem aItemCorrected(FID_INPUTLINE_STATUS,
+ aCursorPos, aCursorPos, aCursorPos,
+ aString, pObject.get());
+ if ( !aMisspellRanges.empty() )
+ aItemCorrected.SetMisspellRanges(&aMisspellRanges);
+
+ aArgs[0] = &aItemCorrected;
+ rBindings.Execute(nId, aArgs);
+ }
+
+ pLastState.reset(); // pLastState still contains the old text
+ }
+ else
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
+
+ if ( bOldMod && pExecuteSh && pCellAttrs && !bForget )
+ {
+ // Combine with input?
+ pExecuteSh->ApplySelectionPattern( *pCellAttrs, true );
+ pExecuteSh->AdjustBlockHeight();
+ }
+
+ HideTip();
+ HideTipBelow();
+
+ nFormSelStart = nFormSelEnd = 0;
+ aFormText.clear();
+
+ mbEditingExistingContent = false;
+ bInOwnChange = false;
+ bInEnterHandler = false;
+ if (bUpdateLayout)
+ mpEditEngine->SetUpdateLayout( true );
+}
+
+void ScInputHandler::CancelHandler()
+{
+ bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot
+
+ ImplCreateEditEngine();
+
+ bModified = false;
+ mbPartialPrefix = false;
+ mbEditingExistingContent = false;
+
+ // Don't rely on ShowRefFrame switching the active view synchronously
+ // execute the function directly on the correct view's bindings instead
+ // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
+ ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
+
+ if (bFormulaMode)
+ {
+ ShowRefFrame();
+ if (pExecuteSh)
+ {
+ pExecuteSh->SetTabNo(aCursorPos.Tab());
+ pExecuteSh->ActiveGrabFocus();
+ }
+ bFormulaMode = false;
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
+ SC_MOD()->SetRefInputHdl(nullptr);
+ if (pInputWin)
+ pInputWin->SetFormulaMode(false);
+ UpdateAutoCorrFlag();
+ }
+ pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
+ DeleteRangeFinder();
+ ResetAutoPar();
+
+ eMode = SC_INPUT_NONE;
+ StopInputWinEngine( true );
+ SCCOL nMaxCol(MAXCOL);
+ if (pExecuteSh)
+ {
+ pExecuteSh->StopEditShell();
+ nMaxCol = pExecuteSh->GetViewData().GetDocument().MaxCol();
+ }
+
+ aCursorPos.Set(nMaxCol+1,0,0); // Invalid flag
+ mpEditEngine->SetTextCurrentDefaults(OUString());
+
+ if ( !pLastState && pExecuteSh )
+ pExecuteSh->UpdateInputHandler( true ); // Update status again
+ else
+ NotifyChange( pLastState.get(), true );
+
+ nFormSelStart = nFormSelEnd = 0;
+ aFormText.clear();
+
+ bInOwnChange = false;
+
+ if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh )
+ {
+ // Clear
+ std::vector<ReferenceMark> aReferenceMarks;
+ ScInputHandler::SendReferenceMarks( pActiveViewSh, aReferenceMarks );
+ }
+}
+
+bool ScInputHandler::IsModalMode( const SfxObjectShell* pDocSh )
+{
+ // References to unnamed document; that doesn't work
+ return bFormulaMode && pRefViewSh
+ && pRefViewSh->GetViewData().GetDocument().GetDocumentShell() != pDocSh
+ && !pDocSh->HasName();
+}
+
+void ScInputHandler::AddRefEntry()
+{
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ UpdateActiveView();
+ if (!pTableView && !pTopView)
+ return; // E.g. FillMode
+
+ DataChanging(); // Cannot be new
+
+ RemoveSelection();
+ OUString aText = GetEditText(mpEditEngine.get());
+ sal_Unicode cLastChar = 0;
+ sal_Int32 nPos = aText.getLength() - 1;
+ while (nPos >= 0) //checking space
+ {
+ cLastChar = aText[nPos];
+ if (cLastChar != ' ')
+ break;
+ --nPos;
+ }
+
+ bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '=');
+ if (bAppendSeparator)
+ {
+ if (pTableView)
+ pTableView->InsertText( OUString(cSep) );
+ if (pTopView)
+ pTopView->InsertText( OUString(cSep) );
+ }
+
+ DataChanged();
+}
+
+void ScInputHandler::SetReference( const ScRange& rRef, const ScDocument& rDoc )
+{
+ HideTip();
+
+ const ScDocument* pThisDoc = nullptr;
+ if (pRefViewSh)
+ pThisDoc = &pRefViewSh->GetViewData().GetDocument();
+ bool bOtherDoc = (pThisDoc != &rDoc);
+ if (bOtherDoc && !rDoc.GetDocumentShell()->HasName())
+ {
+ // References to unnamed document; that doesn't work
+ // SetReference should not be called, then
+ return;
+ }
+ if (!pThisDoc)
+ pThisDoc = &rDoc;
+
+ UpdateActiveView();
+ if (!pTableView && !pTopView)
+ return; // E.g. FillMode
+
+ // Never overwrite the "="!
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ ESelection aSel = pActiveView->GetSelection();
+ aSel.Adjust();
+ if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 )
+ return;
+
+ DataChanging(); // Cannot be new
+
+ // Turn around selection if backwards.
+ if (pTableView)
+ {
+ ESelection aTabSel = pTableView->GetSelection();
+ if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara)
+ {
+ aTabSel.Adjust();
+ pTableView->SetSelection(aTabSel);
+ }
+ }
+ if (pTopView)
+ {
+ ESelection aTopSel = pTopView->GetSelection();
+ if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara)
+ {
+ aTopSel.Adjust();
+ pTopView->SetSelection(aTopSel);
+ }
+ }
+
+ // Create string from reference, in the syntax of the document being edited.
+ OUString aRefStr;
+ const ScAddress::Details aAddrDetails( *pThisDoc, aCursorPos );
+ if (bOtherDoc)
+ {
+ // Reference to other document
+ OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
+
+ // Always 3D and absolute.
+ OUString aTmp(rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails));
+
+ ScDocShell* pObjSh = rDoc.GetDocumentShell();
+ // #i75893# convert escaped URL of the document to something user friendly
+ OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+
+ switch(aAddrDetails.eConv)
+ {
+ case formula::FormulaGrammar::CONV_XL_A1 :
+ case formula::FormulaGrammar::CONV_XL_OOX :
+ case formula::FormulaGrammar::CONV_XL_R1C1 :
+ aRefStr = "[\'" + aFileName + "']";
+ break;
+ case formula::FormulaGrammar::CONV_OOO :
+ default:
+ aRefStr = "\'" + aFileName + "'#";
+ break;
+ }
+ aRefStr += aTmp;
+ }
+ else
+ {
+ if ( rRef.aStart.Tab() != aCursorPos.Tab() ||
+ rRef.aStart.Tab() != rRef.aEnd.Tab() )
+ // pointer-selected => absolute sheet reference
+ aRefStr = rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails);
+ else
+ aRefStr = rRef.Format(rDoc, ScRefFlags::VALID, aAddrDetails);
+ }
+ bool bLOKShowSelect = true;
+ if(comphelper::LibreOfficeKit::isActive() && pRefViewSh->GetViewData().GetRefTabNo() != pRefViewSh->GetViewData().GetTabNo())
+ bLOKShowSelect = false;
+
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ pTableView->InsertText( aRefStr, true, bLOKShowSelect );
+ if (pTopView)
+ pTopView->InsertText( aRefStr, true, bLOKShowSelect );
+
+ DataChanged();
+ }
+
+ bSelIsRef = true;
+}
+
+void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar )
+{
+ if ( eMode == SC_INPUT_NONE )
+ {
+ OSL_FAIL("InsertFunction, not during input mode");
+ return;
+ }
+
+ UpdateActiveView();
+ if (!pTableView && !pTopView)
+ return; // E.g. FillMode
+
+ DataChanging(); // Cannot be new
+
+ OUString aText = rFuncName;
+ if (bAddPar)
+ aText += "()";
+
+ if (pTableView)
+ {
+ pTableView->InsertText( aText );
+ if (bAddPar)
+ {
+ ESelection aSel = pTableView->GetSelection();
+ --aSel.nStartPos;
+ --aSel.nEndPos;
+ pTableView->SetSelection(aSel);
+ }
+ }
+ if (pTopView)
+ {
+ pTopView->InsertText( aText );
+ if (bAddPar)
+ {
+ ESelection aSel = pTopView->GetSelection();
+ --aSel.nStartPos;
+ --aSel.nEndPos;
+ pTopView->SetSelection(aSel);
+ }
+ }
+
+ DataChanged();
+
+ if (bAddPar)
+ AutoParAdded();
+}
+
+void ScInputHandler::ClearText()
+{
+ if ( eMode == SC_INPUT_NONE )
+ {
+ OSL_FAIL("ClearText, not during input mode");
+ return;
+ }
+
+ UpdateActiveView();
+ if (!pTableView && !pTopView)
+ return; // E.g. FillMode
+
+ DataChanging(); // Cannot be new
+
+ if (pTableView)
+ {
+ pTableView->GetEditEngine()->SetText( "" );
+ pTableView->SetSelection( ESelection(0,0, 0,0) );
+ }
+ if (pTopView)
+ {
+ pTopView->GetEditEngine()->SetText( "" );
+ pTopView->SetSelection( ESelection(0,0, 0,0) );
+ }
+
+ DataChanged();
+}
+
+bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ )
+{
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ sal_uInt16 nModi = aCode.GetModifier();
+ bool bShift = aCode.IsShift();
+ bool bControl = aCode.IsMod1();
+ bool bAlt = aCode.IsMod2();
+ sal_uInt16 nCode = aCode.GetCode();
+ sal_Unicode nChar = rKEvt.GetCharCode();
+
+ if (bAlt && !bControl && nCode != KEY_RETURN)
+ // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
+ return false;
+
+ // There is a partial autocomplete suggestion.
+ // Allow its completion with right arrow key (without modifiers).
+ if (mbPartialPrefix && nCode == KEY_RIGHT && !bControl && !bShift && !bAlt &&
+ (pTopView || pTableView))
+ {
+ if (pTopView)
+ pTopView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
+ if (pTableView)
+ pTableView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
+
+ mbPartialPrefix = false;
+
+ // Indicate that this event has been consumed and ScTabViewShell should not act on this.
+ return true;
+ }
+
+ if (!bControl && nCode == KEY_TAB)
+ {
+ // Normal TAB moves the cursor right.
+ EnterHandler();
+
+ if (pActiveViewSh)
+ pActiveViewSh->FindNextUnprot( bShift, true );
+ return true;
+ }
+
+ bool bInputLine = ( eMode==SC_INPUT_TOP );
+
+ bool bUsed = false;
+ bool bSkip = false;
+ bool bDoEnter = false;
+
+ switch ( nCode )
+ {
+ case KEY_RETURN:
+ // New line when in the input line and Shift/Ctrl-Enter is pressed,
+ // or when in a cell and Ctrl-Enter is pressed.
+ if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift))
+ {
+ bDoEnter = true;
+ }
+ else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end())
+ {
+ PasteFunctionData();
+ bUsed = true;
+ }
+ else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() )
+ {
+ PasteManualTip();
+ bUsed = true;
+ }
+ else
+ {
+ ScEnterMode nMode = ScEnterMode::NORMAL;
+ if ( bShift && bControl )
+ nMode = ScEnterMode::MATRIX;
+ else if ( bAlt )
+ nMode = ScEnterMode::BLOCK;
+ EnterHandler( nMode );
+
+ if (pActiveViewSh)
+ pActiveViewSh->MoveCursorEnter( bShift && !bControl );
+
+ bUsed = true;
+ }
+ break;
+ case KEY_TAB:
+ if (bControl && !bAlt)
+ {
+ if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end())
+ {
+ // Iterate
+ NextFormulaEntry( bShift );
+ bUsed = true;
+ }
+ else if (pColumnData && bUseTab)
+ {
+ // Iterate through AutoInput entries
+ NextAutoEntry( bShift );
+ bUsed = true;
+ }
+ }
+ break;
+ case KEY_ESCAPE:
+ if ( nTipVisible )
+ {
+ HideTip();
+ bUsed = true;
+ }
+ else if( nTipVisibleSec )
+ {
+ HideTipBelow();
+ bUsed = true;
+ }
+ else if (eMode != SC_INPUT_NONE)
+ {
+ CancelHandler();
+ bUsed = true;
+ }
+ else
+ bSkip = true;
+ break;
+ case KEY_F2:
+ if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE )
+ {
+ eMode = SC_INPUT_TYPE;
+ bUsed = true;
+ }
+ break;
+ }
+
+ // Only execute cursor keys if already in EditMode
+ // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
+ bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt);
+ bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys
+ if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) ||
+ ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) )
+ {
+ HideTip();
+ HideTipBelow();
+
+ if (bSelIsRef)
+ {
+ RemoveSelection();
+ bSelIsRef = false;
+ }
+
+ UpdateActiveView();
+ bool bNewView = DataChanging( nChar );
+
+ if (bProtected) // Protected cell?
+ bUsed = true; // Don't forward KeyEvent
+ else // Changes allowed
+ {
+ if (bNewView ) // Create anew
+ {
+ if (pActiveViewSh)
+ pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
+ UpdateActiveView();
+ if (eMode==SC_INPUT_NONE)
+ if (pTableView || pTopView)
+ {
+ OUString aStrLoP;
+
+ if (bStartEdit && nCellPercentFormatDecSep != 0 &&
+ ((nChar >= '0' && nChar <= '9') || nChar == '-' || nChar == nCellPercentFormatDecSep))
+ {
+ aStrLoP = "%";
+ }
+
+ if (pTableView)
+ {
+ pTableView->GetEditEngine()->SetText( aStrLoP );
+ if ( !aStrLoP.isEmpty() )
+ pTableView->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
+
+ // Don't call SetSelection if the string is empty anyway,
+ // to avoid breaking the bInitial handling in ScViewData::EditGrowY
+ }
+ if (pTopView)
+ {
+ pTopView->GetEditEngine()->SetText( aStrLoP );
+ if ( !aStrLoP.isEmpty() )
+ pTopView->SetSelection( ESelection(0,0, 0,0) ); // before the '%'
+ }
+ }
+ SyncViews();
+ }
+
+ if (pTableView || pTopView)
+ {
+ if (bDoEnter)
+ {
+ if (pTableView)
+ if( pTableView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
+ bUsed = true;
+ if (pTopView)
+ if( pTopView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
+ bUsed = true;
+ }
+ else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() )
+ {
+ SkipClosingPar();
+ bUsed = true;
+ }
+ else
+ {
+ if (pTableView)
+ {
+ if (pTopView)
+ pTableView->SetControlWord(pTableView->GetControlWord() | EVControlBits::SINGLELINEPASTE);
+
+ vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
+ if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) )
+ bUsed = true;
+
+ pTableView->SetControlWord(pTableView->GetControlWord() & ~EVControlBits::SINGLELINEPASTE);
+ }
+ if (pTopView)
+ {
+ if ( bUsed && rKEvt.GetKeyCode().GetFunction() == KeyFuncType::CUT )
+ pTopView->DeleteSelected();
+ else if ( pTopView->PostKeyEvent( rKEvt ) )
+ bUsed = true;
+ }
+ }
+
+ // AutoInput:
+ if ( bUsed && SC_MOD()->GetAppOptions().GetAutoComplete() )
+ {
+ bUseTab = false;
+ if (pFormulaData)
+ miAutoPosFormula = pFormulaData->end(); // do not search further
+ if (pColumnData)
+ miAutoPosColumn = pColumnData->end();
+
+ KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
+ if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete'
+ KeyFuncType::CUT != eFunc) // and no 'CTRL-X'
+ {
+ if (bFormulaMode)
+ UseFormulaData();
+ else
+ UseColData();
+ }
+ }
+
+ // When the selection is changed manually or an opening parenthesis
+ // is typed, stop overwriting parentheses
+ if ( bUsed && nChar == '(' )
+ ResetAutoPar();
+
+ if ( KEY_INSERT == nCode )
+ {
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
+ }
+ if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) )
+ {
+ ShowTipCursor();
+ }
+ if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE )
+ {
+ UseFormulaData();
+ }
+
+ }
+
+ // #i114511# don't count cursor keys as modification
+ bool bSetModified = !bCursorKey;
+ DataChanged(false, bSetModified); // also calls UpdateParenthesis()
+
+ // In the LOK case, we want to set the document modified state
+ // right away at the start of the edit, so that the content is
+ // saved even when the user leaves the document before hitting
+ // Enter
+ if (comphelper::LibreOfficeKit::isActive() && bSetModified && pActiveViewSh && !pActiveViewSh->GetViewData().GetDocShell()->IsModified())
+ pActiveViewSh->GetViewData().GetDocShell()->SetModified();
+
+ InvalidateAttribs(); //! in DataChanged?
+ }
+ }
+
+ if (pTopView && eMode != SC_INPUT_NONE)
+ SyncViews();
+
+ return bUsed;
+}
+
+OUString ScInputHandler::GetSurroundingText()
+{
+ if (eMode != SC_INPUT_NONE)
+ {
+ UpdateActiveView();
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ return pTableView->GetSurroundingText();
+ else if (pTopView) // call only once
+ return pTopView->GetSurroundingText();
+ }
+ }
+ return OUString();
+}
+
+Selection ScInputHandler::GetSurroundingTextSelection()
+{
+ if (eMode != SC_INPUT_NONE)
+ {
+ UpdateActiveView();
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ return pTableView->GetSurroundingTextSelection();
+ else if (pTopView) // call only once
+ return pTopView->GetSurroundingTextSelection();
+ }
+ }
+ return Selection(0, 0);
+}
+
+bool ScInputHandler::DeleteSurroundingText(const Selection& rSelection)
+{
+ if (eMode != SC_INPUT_NONE)
+ {
+ UpdateActiveView();
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ return pTableView->DeleteSurroundingText(rSelection);
+ else if (pTopView) // call only once
+ return pTopView->DeleteSurroundingText(rSelection);
+ }
+ }
+ return false;
+}
+
+void ScInputHandler::InputCommand( const CommandEvent& rCEvt )
+{
+ if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
+ {
+ // For CommandEventId::CursorPos, do as little as possible, because
+ // with remote VCL, even a ShowCursor will generate another event.
+ if ( eMode != SC_INPUT_NONE )
+ {
+ UpdateActiveView();
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ pTableView->Command( rCEvt );
+ else if (pTopView) // call only once
+ pTopView->Command( rCEvt );
+ }
+ }
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
+ {
+ if ( eMode != SC_INPUT_NONE )
+ {
+ UpdateActiveView();
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ pTableView->Command( rCEvt );
+ else if (pTopView) // call only once
+ pTopView->Command( rCEvt );
+ }
+ }
+ }
+ else
+ {
+ HideTip();
+ HideTipBelow();
+
+ if ( bSelIsRef )
+ {
+ RemoveSelection();
+ bSelIsRef = false;
+ }
+
+ UpdateActiveView();
+ bool bNewView = DataChanging( 0, true );
+
+ if (!bProtected) // changes allowed
+ {
+ if (bNewView) // create new edit view
+ {
+ if (pActiveViewSh)
+ pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
+ UpdateActiveView();
+ if (eMode==SC_INPUT_NONE)
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ {
+ pTableView->GetEditEngine()->SetText( "" );
+ pTableView->SetSelection( ESelection(0,0, 0,0) );
+ }
+ if (pTopView)
+ {
+ pTopView->GetEditEngine()->SetText( "" );
+ pTopView->SetSelection( ESelection(0,0, 0,0) );
+ }
+ }
+ SyncViews();
+ }
+
+ if (pTableView || pTopView)
+ {
+ if (pTableView)
+ pTableView->Command( rCEvt );
+ if (pTopView)
+ pTopView->Command( rCEvt );
+
+ if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
+ {
+ // AutoInput after ext text input
+
+ if (pFormulaData)
+ miAutoPosFormula = pFormulaData->end();
+ if (pColumnData)
+ miAutoPosColumn = pColumnData->end();
+
+ if (bFormulaMode)
+ UseFormulaData();
+ else
+ UseColData();
+ }
+ }
+
+ DataChanged(); // calls UpdateParenthesis()
+ InvalidateAttribs(); //! in DataChanged ?
+ }
+
+ if (pTopView && eMode != SC_INPUT_NONE)
+ SyncViews();
+ }
+}
+
+void ScInputHandler::NotifyChange( const ScInputHdlState* pState,
+ bool bForce, ScTabViewShell* pSourceSh,
+ bool bStopEditing)
+{
+ // If the call originates from a macro call in the EnterHandler,
+ // return immediately and don't mess up the status
+ if (bInEnterHandler)
+ return;
+
+ bool bRepeat = (pState == pLastState.get());
+ if (!bRepeat && pState && pLastState)
+ bRepeat = (*pState == *pLastState);
+ if (bRepeat && !bForce)
+ return;
+
+ bInOwnChange = true; // disable ModifyHdl (reset below)
+
+ if ( pState && !pLastState ) // Enable again
+ bForce = true;
+
+ bool bHadObject = pLastState && pLastState->GetEditData();
+
+ //! Before EditEngine gets eventually created (so it gets the right pools)
+ if ( pSourceSh )
+ pActiveViewSh = pSourceSh;
+ else
+ pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+
+ if (pActiveViewSh)
+ ImplCreateEditEngine();
+
+ if ( pState != pLastState.get() )
+ {
+ pLastState.reset( pState ? new ScInputHdlState( *pState ) : nullptr);
+ }
+
+ if ( pState && pActiveViewSh )
+ {
+ ScModule* pScMod = SC_MOD();
+
+ ScTabViewShell* pScTabViewShell = dynamic_cast<ScTabViewShell*>(pScMod->GetViewShell());
+
+ // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
+ // FormEditData, if we're switching from Help to Calc:
+ if ( !bFormulaMode && !pScMod->IsFormulaMode() &&
+ ( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) )
+ {
+ bool bIgnore = false;
+ if ( bModified )
+ {
+ if (pState->GetPos() != aCursorPos)
+ {
+ if (!bProtected)
+ EnterHandler();
+ }
+ else
+ bIgnore = true;
+ }
+
+ if ( !bIgnore )
+ {
+ const ScAddress& rSPos = pState->GetStartPos();
+ const ScAddress& rEPos = pState->GetEndPos();
+ const EditTextObject* pData = pState->GetEditData();
+ OUString aString = pState->GetString();
+ bool bTxtMod = false;
+ ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ aCursorPos = pState->GetPos();
+
+ if ( pData )
+ bTxtMod = true;
+ else if ( bHadObject )
+ bTxtMod = true;
+ else if ( bTextValid )
+ bTxtMod = ( aString != aCurrentText );
+ else
+ bTxtMod = ( aString != GetEditText(mpEditEngine.get()) );
+
+ if ( bTxtMod || bForce )
+ {
+ if (pData)
+ {
+ mpEditEngine->SetTextCurrentDefaults( *pData );
+ if (pInputWin)
+ aString = ScEditUtil::GetMultilineString(*mpEditEngine);
+ else
+ aString = GetEditText(mpEditEngine.get());
+ lcl_RemoveTabs(aString);
+ bTextValid = false;
+ aCurrentText.clear();
+ }
+ else
+ {
+ aCurrentText = aString;
+ bTextValid = true; //! To begin with remember as a string
+ }
+
+ const bool bUpdateKit = comphelper::LibreOfficeKit::isActive() && pActiveViewSh;
+
+ if (pInputWin)
+ {
+ // If we will end up updating LoKit after this, we can skip it here
+ pInputWin->SetTextString(aString, !bUpdateKit);
+ }
+
+ if (bUpdateKit)
+ {
+ UpdateActiveView();
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection();
+
+ // if we switched content completely - don't send huge numbers
+ if (aSel.nStartPara == EE_PARA_NOT_FOUND)
+ aSel.nStartPara = 0;
+
+ if (aSel.nEndPara == EE_PARA_NOT_FOUND)
+ aSel.nEndPara = 0;
+
+ pActiveViewSh->LOKSendFormulabarUpdate(pActiveView, aString, aSel);
+ // TODO: deprecated?
+ pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8());
+ }
+ }
+
+ if ( pInputWin || comphelper::LibreOfficeKit::isActive()) // Named range input
+ {
+ OUString aPosStr;
+ bool bSheetLocal = false;
+ const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
+
+ // Is the range a name?
+ //! Find by Timer?
+ if ( pActiveViewSh )
+ pActiveViewSh->GetViewData().GetDocument().
+ GetRangeAtBlock( ScRange( rSPos, rEPos ), aPosStr, &bSheetLocal);
+
+ if ( aPosStr.isEmpty() ) // Not a name -> format
+ {
+ ScRefFlags nFlags = ScRefFlags::ZERO;
+ if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 )
+ nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS;
+ if ( rSPos != rEPos )
+ {
+ ScRange r(rSPos, rEPos);
+ applyStartToEndFlags(nFlags);
+ aPosStr = r.Format(rDoc, ScRefFlags::VALID | nFlags, aAddrDetails);
+ }
+ else
+ aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
+ }
+ else if (bSheetLocal)
+ {
+ OUString aName;
+ if (rDoc.GetName( rSPos.Tab(), aName))
+ aPosStr = ScPosWnd::createLocalRangeName( aPosStr, aName);
+ }
+
+ if (pInputWin)
+ {
+ // Disable the accessible VALUE_CHANGE event
+ bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(false);
+ pInputWin->SetAccessibilityEventsSuppressed(true);
+ pInputWin->SetPosString(aPosStr);
+ pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed);
+ pInputWin->SetSumAssignMode();
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
+ pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8());
+ }
+
+ if (bStopEditing) {
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
+
+ // As long as the content is not edited, turn off online spelling.
+ // Online spelling is turned back on in StartTable, after setting
+ // the right language from cell attributes.
+
+ EEControlBits nCntrl = mpEditEngine->GetControlWord();
+ if ( nCntrl & EEControlBits::ONLINESPELLING )
+ mpEditEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING );
+ }
+
+ bModified = false;
+ bSelIsRef = false;
+ bProtected = false;
+ bCommandErrorShown = false;
+ }
+ }
+
+ if ( pInputWin)
+ {
+ // Do not enable if RefDialog is open
+ if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen())
+ {
+ if ( !pInputWin->IsEnabled())
+ {
+ pDelayTimer->Stop();
+ pInputWin->Enable();
+ }
+ }
+ else if(pScMod->IsRefDialogOpen())
+ { // Because every document has its own InputWin,
+ // we should start Timer again, because the input line may
+ // still be active
+ if ( !pDelayTimer->IsActive() )
+ pDelayTimer->Start();
+ }
+ }
+ }
+ else // !pState || !pActiveViewSh
+ {
+ if ( !pDelayTimer->IsActive() )
+ pDelayTimer->Start();
+ }
+
+ HideTip();
+ HideTipBelow();
+ bInOwnChange = false;
+}
+
+void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust )
+{
+ eAttrAdjust = eJust;
+ UpdateAdjust( 0 );
+}
+
+void ScInputHandler::ResetDelayTimer()
+{
+ if( pDelayTimer->IsActive() )
+ {
+ pDelayTimer->Stop();
+ if ( pInputWin )
+ pInputWin->Enable();
+ }
+}
+
+IMPL_LINK_NOARG( ScInputHandler, DelayTimer, Timer*, void )
+{
+ if ( !(nullptr == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen()))
+ return;
+
+ //! New method at ScModule to query if function autopilot is open
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
+ {
+ if ( pInputWin)
+ {
+ pInputWin->EnableButtons( false );
+ pInputWin->Disable();
+ }
+ }
+ else if ( !bFormulaMode ) // Keep formula e.g. for help
+ {
+ bInOwnChange = true; // disable ModifyHdl (reset below)
+
+ pActiveViewSh = nullptr;
+ mpEditEngine->SetTextCurrentDefaults( OUString() );
+ if ( pInputWin )
+ {
+ pInputWin->SetPosString( OUString() );
+ pInputWin->SetTextString(OUString(), true);
+ pInputWin->Disable();
+ }
+
+ bInOwnChange = false;
+ }
+}
+
+void ScInputHandler::InputSelection( const EditView* pView )
+{
+ SyncViews( pView );
+ ShowTipCursor();
+ UpdateParenthesis(); // Selection changed -> update parentheses highlighting
+
+ // When the selection is changed manually, stop overwriting parentheses
+ ResetAutoPar();
+
+ if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
+ {
+ EditView* pActiveView = pTopView ? pTopView : pTableView;
+ ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection();
+ pActiveViewSh->LOKSendFormulabarUpdate(pActiveView, GetEditString(), aSel);
+ }
+}
+
+void ScInputHandler::InputChanged( const EditView* pView, bool bFromNotify )
+{
+ if ( !pView )
+ return;
+
+ UpdateActiveView();
+
+ // #i20282# DataChanged needs to know if this is from the input line's modify handler
+ bool bFromTopNotify = ( bFromNotify && pView == pTopView );
+
+ bool bNewView = DataChanging(); //FIXME: Is this at all possible?
+ aCurrentText = pView->GetEditEngine()->GetText(); // Also remember the string
+ mpEditEngine->SetTextCurrentDefaults( aCurrentText );
+ DataChanged( bFromTopNotify );
+ bTextValid = true; // Is set to false in DataChanged
+
+ if ( pActiveViewSh )
+ {
+ ScViewData& rViewData = pActiveViewSh->GetViewData();
+ if ( bNewView )
+ rViewData.GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos );
+
+ rViewData.EditGrowY();
+ rViewData.EditGrowX();
+ }
+
+ SyncViews( pView );
+}
+
+const OUString& ScInputHandler::GetEditString()
+{
+ if (mpEditEngine)
+ {
+ aCurrentText = mpEditEngine->GetText(); // Always new from Engine
+ bTextValid = true;
+ }
+
+ return aCurrentText;
+}
+
+Size ScInputHandler::GetTextSize()
+{
+ Size aSize;
+ if ( mpEditEngine )
+ aSize = Size( mpEditEngine->CalcTextWidth(), mpEditEngine->GetTextHeight() );
+
+ return aSize;
+}
+
+bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine )
+{
+ bool bRet = false;
+ if (mpEditEngine)
+ {
+ // Contains field?
+ sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
+ SfxItemSet aSet = mpEditEngine->GetAttribs( ESelection(0,0,nParCnt,0) );
+ SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ {
+ // Copy content
+ std::unique_ptr<EditTextObject> pObj = mpEditEngine->CreateTextObject();
+ rDestEngine.SetTextCurrentDefaults(*pObj);
+ pObj.reset();
+
+ // Delete attributes
+ for (sal_Int32 i=0; i<nParCnt; i++)
+ rDestEngine.RemoveCharAttribs( i );
+
+ // Combine paragraphs
+ while ( nParCnt > 1 )
+ {
+ sal_Int32 nLen = rDestEngine.GetTextLen( 0 );
+ ESelection aSel( 0,nLen, 1,0 );
+ rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space
+ --nParCnt;
+ }
+
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+/**
+ * Methods for FunctionAutoPilot:
+ * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
+ */
+void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd )
+{
+ rStart = nFormSelStart;
+ rEnd = nFormSelEnd;
+}
+
+EditView* ScInputHandler::GetFuncEditView()
+{
+ UpdateActiveView(); // Due to pTableView
+
+ EditView* pView = nullptr;
+ if ( pInputWin )
+ {
+ pInputWin->MakeDialogEditView();
+ pView = pInputWin->GetEditView();
+ }
+ else
+ {
+ if ( eMode != SC_INPUT_TABLE )
+ {
+ bCreatingFuncView = true; // Don't display RangeFinder
+ SetMode( SC_INPUT_TABLE );
+ bCreatingFuncView = false;
+ if ( pTableView )
+ pTableView->GetEditEngine()->SetText( OUString() );
+ }
+ pView = pTableView;
+ }
+
+ return pView;
+}
+
+void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd )
+{
+ if ( nStart <= nEnd )
+ {
+ nFormSelStart = nStart;
+ nFormSelEnd = nEnd;
+ }
+ else
+ {
+ nFormSelEnd = nStart;
+ nFormSelStart = nEnd;
+ }
+
+ EditView* pView = GetFuncEditView();
+ if (pView)
+ pView->SetSelection( ESelection(0,nStart, 0,nEnd) );
+
+ bModified = true;
+}
+
+void ScInputHandler::InputReplaceSelection( std::u16string_view aStr )
+{
+ if (!pRefViewSh)
+ pRefViewSh = pActiveViewSh;
+
+ OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken...");
+
+ sal_Int32 nOldLen = nFormSelEnd - nFormSelStart;
+ sal_Int32 nNewLen = aStr.size();
+
+ OUStringBuffer aBuf(aFormText);
+ if (nOldLen)
+ aBuf.remove(nFormSelStart, nOldLen);
+ if (nNewLen)
+ aBuf.insert(nFormSelStart, aStr);
+
+ aFormText = aBuf.makeStringAndClear();
+
+ nFormSelEnd = nFormSelStart + nNewLen;
+
+ EditView* pView = GetFuncEditView();
+ if (pView)
+ {
+ pView->SetEditEngineUpdateLayout( false );
+ pView->GetEditEngine()->SetText( aFormText );
+ pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) );
+ pView->SetEditEngineUpdateLayout( true );
+ }
+ bModified = true;
+}
+
+void ScInputHandler::InputTurnOffWinEngine()
+{
+ bInOwnChange = true; // disable ModifyHdl (reset below)
+
+ eMode = SC_INPUT_NONE;
+ /* TODO: it would be better if there was some way to reset the input bar
+ * engine instead of deleting and having it recreate through
+ * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
+ * fdo#72278 without reintroducing fdo#69971. */
+ StopInputWinEngine(true);
+
+ bInOwnChange = false;
+}
+
+/**
+ * ScInputHdlState
+ */
+ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos,
+ const ScAddress& rStartPos,
+ const ScAddress& rEndPos,
+ OUString _aString,
+ const EditTextObject* pData )
+ : aCursorPos ( rCurPos ),
+ aStartPos ( rStartPos ),
+ aEndPos ( rEndPos ),
+ aString (std::move( _aString )),
+ pEditData ( pData ? pData->Clone() : nullptr )
+{
+}
+
+ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy )
+{
+ *this = rCpy;
+}
+
+ScInputHdlState::~ScInputHdlState()
+{
+}
+
+bool ScInputHdlState::operator==( const ScInputHdlState& r ) const
+{
+ return ( (aStartPos == r.aStartPos)
+ && (aEndPos == r.aEndPos)
+ && (aCursorPos == r.aCursorPos)
+ && (aString == r.aString)
+ && ScGlobal::EETextObjEqual( pEditData.get(), r.pEditData.get() ) );
+}
+
+ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r )
+{
+ if (this != &r)
+ {
+ aCursorPos = r.aCursorPos;
+ aStartPos = r.aStartPos;
+ aEndPos = r.aEndPos;
+ aString = r.aString;
+ pEditData.reset();
+ if (r.pEditData)
+ pEditData = r.pEditData->Clone();
+ }
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/inputwin.cxx b/sc/source/ui/app/inputwin.cxx
new file mode 100644
index 0000000000..7f0cf742b0
--- /dev/null
+++ b/sc/source/ui/app/inputwin.cxx
@@ -0,0 +1,2762 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <algorithm>
+#include <string_view>
+
+#include <editeng/eeitem.hxx>
+
+#include <sfx2/app.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/langitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/event.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <unotools/charclass.hxx>
+
+#include <inputwin.hxx>
+#include <scmod.hxx>
+#include <global.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <globstr.hrc>
+#include <bitmaps.hlst>
+#include <reffact.hxx>
+#include <editutil.hxx>
+#include <inputhdl.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <appoptio.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <docfunc.hxx>
+#include <funcdesc.hxx>
+#include <editeng/fontitem.hxx>
+#include <AccessibleEditObject.hxx>
+#include <AccessibleText.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <helpids.h>
+#include <output.hxx>
+
+namespace com::sun::star::accessibility { class XAccessible; }
+
+const tools::Long THESIZE = 1000000; // Should be more than enough!
+const tools::Long INPUTLINE_INSET_MARGIN = 2; // Space between border and interior widgets of input line
+const tools::Long LEFT_OFFSET = 5; // Left offset of input line
+//TODO const long BUTTON_OFFSET = 2; // Space between input line and button to expand/collapse
+const tools::Long INPUTWIN_MULTILINES = 6; // Initial number of lines within multiline dropdown
+const tools::Long TOOLBOX_WINDOW_HEIGHT = 22; // Height of toolbox window in pixels - TODO: The same on all systems?
+const tools::Long POSITION_COMBOBOX_WIDTH = 18; // Width of position combobox in characters
+const int RESIZE_HOTSPOT_HEIGHT = 4;
+
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::UNO_QUERY;
+
+using com::sun::star::frame::XLayoutManager;
+using com::sun::star::beans::XPropertySet;
+
+namespace {
+
+constexpr ToolBoxItemId SID_INPUT_FUNCTION (SC_VIEW_START + 47);
+constexpr ToolBoxItemId SID_INPUT_SUM (SC_VIEW_START + 48);
+constexpr ToolBoxItemId SID_INPUT_EQUAL (SC_VIEW_START + 49);
+constexpr ToolBoxItemId SID_INPUT_CANCEL (SC_VIEW_START + 50);
+constexpr ToolBoxItemId SID_INPUT_OK (SC_VIEW_START + 51);
+
+enum ScNameInputType
+{
+ SC_NAME_INPUT_CELL,
+ SC_NAME_INPUT_RANGE,
+ SC_NAME_INPUT_NAMEDRANGE_LOCAL,
+ SC_NAME_INPUT_NAMEDRANGE_GLOBAL,
+ SC_NAME_INPUT_DATABASE,
+ SC_NAME_INPUT_ROW,
+ SC_NAME_INPUT_SHEET,
+ SC_NAME_INPUT_DEFINE,
+ SC_NAME_INPUT_BAD_NAME,
+ SC_NAME_INPUT_BAD_SELECTION,
+ SC_MANAGE_NAMES
+};
+
+}
+
+SFX_IMPL_CHILDWINDOW_WITHID(ScInputWindowWrapper,FID_INPUTLINE_STATUS)
+
+ScInputWindowWrapper::ScInputWindowWrapper( vcl::Window* pParentP,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* /* pInfo */ )
+ : SfxChildWindow( pParentP, nId )
+{
+ VclPtr<ScInputWindow> pWin = VclPtr<ScInputWindow>::Create( pParentP, pBindings );
+ SetWindow( pWin );
+
+ pWin->Show();
+
+ pWin->SetSizePixel( pWin->CalcWindowSizePixel() );
+
+ SetAlignment(SfxChildAlignment::LOWESTTOP);
+ pBindings->Invalidate( FID_TOGGLEINPUTLINE );
+}
+
+/**
+ * GetInfo is disposed of if there's a SFX_IMPL_TOOLBOX!
+ */
+SfxChildWinInfo ScInputWindowWrapper::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ return aInfo;
+}
+
+
+static VclPtr<ScInputBarGroup> lcl_chooseRuntimeImpl( vcl::Window* pParent, const SfxBindings* pBind )
+{
+ ScTabViewShell* pViewSh = nullptr;
+ SfxDispatcher* pDisp = pBind->GetDispatcher();
+ if ( pDisp )
+ {
+ SfxViewFrame* pViewFrm = pDisp->GetFrame();
+ if ( pViewFrm )
+ pViewSh = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() );
+ }
+
+ return VclPtr<ScInputBarGroup>::Create( pParent, pViewSh );
+}
+
+ScInputWindow::ScInputWindow( vcl::Window* pParent, const SfxBindings* pBind ) :
+ // With WB_CLIPCHILDREN otherwise we get flickering
+ ToolBox ( pParent, WinBits(WB_CLIPCHILDREN | WB_BORDER | WB_NOSHADOW) ),
+ aWndPos ( !comphelper::LibreOfficeKit::isActive() ? VclPtr<ScPosWnd>::Create(this) : nullptr ),
+ mxTextWindow ( lcl_chooseRuntimeImpl( this, pBind ) ),
+ pInputHdl ( nullptr ),
+ mpViewShell ( nullptr ),
+ mnMaxY (0),
+ mnStandardItemHeight(0),
+ bIsOkCancelMode ( false ),
+ bInResize ( false )
+{
+ // #i73615# don't rely on SfxViewShell::Current while constructing the input line
+ // (also for GetInputHdl below)
+ ScTabViewShell* pViewSh = nullptr;
+ SfxDispatcher* pDisp = pBind->GetDispatcher();
+ if ( pDisp )
+ {
+ SfxViewFrame* pViewFrm = pDisp->GetFrame();
+ if ( pViewFrm )
+ pViewSh = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() );
+ }
+ OSL_ENSURE( pViewSh, "no view shell for input window" );
+
+ mpViewShell = pViewSh;
+
+ // Position window, 3 buttons, input window
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ InsertWindow (ToolBoxItemId(1), aWndPos.get(), ToolBoxItemBits::NONE, 0);
+ InsertSeparator (1);
+ InsertItem (SID_INPUT_FUNCTION, Image(StockImage::Yes, RID_BMP_INPUT_FUNCTION), ToolBoxItemBits::NONE, 2);
+ }
+
+ const bool bIsLOKMobilePhone = mpViewShell && mpViewShell->isLOKMobilePhone();
+
+ // sigma and equal buttons
+ if (!bIsLOKMobilePhone)
+ {
+ InsertItem (SID_INPUT_SUM, Image(StockImage::Yes, RID_BMP_INPUT_SUM), ToolBoxItemBits::DROPDOWN, 3);
+ InsertItem (SID_INPUT_EQUAL, Image(StockImage::Yes, RID_BMP_INPUT_EQUAL), ToolBoxItemBits::NONE, 4);
+ InsertItem (SID_INPUT_CANCEL, Image(StockImage::Yes, RID_BMP_INPUT_CANCEL), ToolBoxItemBits::NONE, 5);
+ InsertItem (SID_INPUT_OK, Image(StockImage::Yes, RID_BMP_INPUT_OK), ToolBoxItemBits::NONE, 6);
+ }
+
+ InsertWindow (ToolBoxItemId(7), mxTextWindow.get(), ToolBoxItemBits::NONE, 7);
+ SetDropdownClickHdl( LINK( this, ScInputWindow, DropdownClickHdl ));
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ aWndPos ->SetQuickHelpText(ScResId(SCSTR_QHELP_POSWND));
+ aWndPos ->SetHelpId (HID_INSWIN_POS);
+
+ mxTextWindow->SetQuickHelpText(ScResId(SCSTR_QHELP_INPUTWND));
+ mxTextWindow->SetHelpId (HID_INSWIN_INPUT);
+
+ // No SetHelpText: the helptexts come from the Help
+ SetItemText (SID_INPUT_FUNCTION, ScResId(SCSTR_QHELP_BTNCALC));
+ SetHelpId (SID_INPUT_FUNCTION, HID_INSWIN_CALC);
+ }
+
+ // sigma and equal buttons
+ if (!bIsLOKMobilePhone)
+ {
+ SetHelpId (SID_INPUT_SUM, HID_INSWIN_SUMME);
+ SetHelpId (SID_INPUT_EQUAL, HID_INSWIN_FUNC);
+ SetHelpId (SID_INPUT_CANCEL, HID_INSWIN_CANCEL);
+ SetHelpId (SID_INPUT_OK, HID_INSWIN_OK);
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ SetItemText ( SID_INPUT_SUM, ScResId( SCSTR_QHELP_BTNSUM ) );
+ SetItemText ( SID_INPUT_EQUAL, ScResId( SCSTR_QHELP_BTNEQUAL ) );
+ SetItemText ( SID_INPUT_CANCEL, ScResId( SCSTR_QHELP_BTNCANCEL ) );
+ SetItemText ( SID_INPUT_OK, ScResId( SCSTR_QHELP_BTNOK ) );
+ }
+
+ EnableItem( SID_INPUT_CANCEL, false );
+ EnableItem( SID_INPUT_OK, false );
+
+ HideItem( SID_INPUT_CANCEL );
+ HideItem( SID_INPUT_OK );
+
+ mnStandardItemHeight = GetItemRect(SID_INPUT_SUM).GetHeight();
+ }
+
+ SetHelpId( HID_SC_INPUTWIN ); // For the whole input row
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ aWndPos ->Show();
+ mxTextWindow->Show();
+
+ pInputHdl = SC_MOD()->GetInputHdl( pViewSh, false ); // use own handler even if ref-handler is set
+ if (pInputHdl)
+ pInputHdl->SetInputWindow( this );
+
+ if (pInputHdl && !pInputHdl->GetFormString().isEmpty())
+ {
+ // Switch over while the Function AutoPilot is active
+ // -> show content of the Function AutoPilot again
+ // Also show selection (remember at the InputHdl)
+ mxTextWindow->SetTextString(pInputHdl->GetFormString(), true);
+ }
+ else if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ // If the input row was hidden while editing (e.g. when editing a formula
+ // and then switching to another document or the help), display the text
+ // we just edited from the InputHandler
+ mxTextWindow->SetTextString(pInputHdl->GetEditString(), true); // Display text
+ if ( pInputHdl->IsTopMode() )
+ pInputHdl->SetMode( SC_INPUT_TABLE ); // Focus ends up at the bottom anyways
+ }
+ else if (pViewSh)
+ {
+ // Don't stop editing in LOK a remote user might be editing.
+ const bool bStopEditing = !comphelper::LibreOfficeKit::isActive();
+ pViewSh->UpdateInputHandler(true, bStopEditing); // Absolutely necessary update
+ }
+
+ SetToolbarLayoutMode( ToolBoxLayoutMode::Locked );
+
+ SetAccessibleName(ScResId(STR_ACC_TOOLBAR_FORMULA));
+}
+
+ScInputWindow::~ScInputWindow()
+{
+ disposeOnce();
+}
+
+void ScInputWindow::dispose()
+{
+ bool bDown = !ScGlobal::oSysLocale; // after Clear?
+
+ // if any view's input handler has a pointer to this input window, reset it
+ // (may be several ones, #74522#)
+ // member pInputHdl is not used here
+
+ if ( !bDown )
+ {
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh )
+ {
+ ScInputHandler* pHdl = static_cast<ScTabViewShell*>(pSh)->GetInputHandler();
+ if ( pHdl && pHdl->GetInputWindow() == this )
+ {
+ pHdl->SetInputWindow( nullptr );
+ pHdl->StopInputWinEngine( false ); // reset pTopView pointer
+ }
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (GetLOKNotifier())
+ ReleaseLOKNotifier();
+ }
+
+ mxTextWindow.disposeAndClear();
+ aWndPos.disposeAndClear();
+
+ ToolBox::dispose();
+}
+
+void ScInputWindow::SetInputHandler( ScInputHandler* pNew )
+{
+ // Is called in the Activate of the View ...
+ if ( pNew != pInputHdl )
+ {
+ // On Reload (last version) the pInputHdl is the InputHandler of the old, deleted
+ // ViewShell: so don't touch it here!
+ pInputHdl = pNew;
+ if (pInputHdl)
+ pInputHdl->SetInputWindow( this );
+ }
+}
+
+void ScInputWindow::Select()
+{
+ ScModule* pScMod = SC_MOD();
+ ToolBox::Select();
+
+ ToolBoxItemId curItemId = GetCurItemId();
+ if (curItemId == SID_INPUT_FUNCTION)
+ {
+ //! new method at ScModule to query if function autopilot is open
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if ( pViewFrm && ( comphelper::LibreOfficeKit::isActive() || !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) )
+ {
+ pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+
+ // The Toolbox will be disabled anyways, so we don't need to switch here,
+ // regardless whether it succeeded or not!
+// SetOkCancelMode();
+ }
+ }
+ else if (curItemId == SID_INPUT_CANCEL)
+ {
+ pScMod->InputCancelHandler();
+ SetSumAssignMode();
+ }
+ else if (curItemId == SID_INPUT_OK)
+ {
+ pScMod->InputEnterHandler();
+ SetSumAssignMode();
+ mxTextWindow->Invalidate(); // Or else the Selection remains
+ }
+ else if (curItemId == SID_INPUT_SUM)
+ {
+ bool bRangeFinder = false;
+ bool bSubTotal = false;
+ AutoSum(bRangeFinder, bSubTotal, ocSum);
+ }
+ else if (curItemId == SID_INPUT_EQUAL)
+ {
+ StartFormula();
+ }
+}
+
+void ScInputWindow::StartFormula()
+{
+ ScModule* pScMod = SC_MOD();
+ mxTextWindow->StartEditEngine();
+ if ( pScMod->IsEditMode() ) // Isn't if e.g. protected
+ {
+ mxTextWindow->StartEditEngine();
+
+ sal_Int32 nStartPos = 1;
+ sal_Int32 nEndPos = 1;
+
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( pViewSh )
+ {
+ const OUString& rString = mxTextWindow->GetTextString();
+ const sal_Int32 nLen = rString.getLength();
+
+ ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
+ CellType eCellType = rDoc.GetCellType( pViewSh->GetViewData().GetCurPos() );
+ switch ( eCellType )
+ {
+ case CELLTYPE_VALUE:
+ {
+ nEndPos = nLen + 1;
+ mxTextWindow->SetTextString("=" + rString, true);
+ break;
+ }
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ nStartPos = 0;
+ nEndPos = nLen;
+ break;
+ case CELLTYPE_FORMULA:
+ nEndPos = nLen;
+ break;
+ default:
+ mxTextWindow->SetTextString("=", true);
+ break;
+ }
+ }
+
+ EditView* pView = mxTextWindow->GetEditView();
+ if (pView)
+ {
+ sal_Int32 nStartPara = 0, nEndPara = 0;
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ TextGrabFocus();
+ if (pViewSh && !pViewSh->isLOKDesktop())
+ {
+ nStartPara = nEndPara = pView->GetEditEngine()->GetParagraphCount() ?
+ (pView->GetEditEngine()->GetParagraphCount() - 1) : 0;
+ nStartPos = nEndPos = pView->GetEditEngine()->GetTextLen(nStartPara);
+ }
+ }
+ pView->SetSelection(ESelection(nStartPara, nStartPos, nEndPara, nEndPos));
+ pScMod->InputChanged(pView);
+ SetOkCancelMode();
+ pView->SetEditEngineUpdateLayout(true);
+ }
+ }
+}
+
+void ScInputWindow::PixelInvalidate(const tools::Rectangle* pRectangle)
+{
+ if (comphelper::LibreOfficeKit::isDialogPainting() || !comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (pRectangle)
+ {
+ tools::Rectangle aRect(*pRectangle);
+ aRect.Move(-GetOutOffXPixel(), -GetOutOffYPixel());
+ Window::PixelInvalidate(&aRect);
+ }
+ else
+ {
+ Window::PixelInvalidate(nullptr);
+ }
+}
+
+void ScInputWindow::SetSizePixel( const Size& rNewSize )
+{
+ const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier();
+ if (pNotifier)
+ {
+ if (vcl::Window* pFrameWindowImpl = GetParent())
+ {
+ if (vcl::Window* pWorkWindow = pFrameWindowImpl->GetParent())
+ {
+ if (vcl::Window* pImplBorderWindow = pWorkWindow->GetParent())
+ {
+ Size aSize = pImplBorderWindow->GetSizePixel();
+ aSize.setWidth(rNewSize.getWidth());
+ pImplBorderWindow->SetSizePixel(aSize);
+ }
+ }
+ }
+ }
+
+ ToolBox::SetSizePixel(rNewSize);
+}
+
+void ScInputWindow::Resize()
+{
+ ToolBox::Resize();
+
+ Size aStartSize = GetSizePixel();
+ Size aSize = aStartSize;
+
+ auto nLines = mxTextWindow->GetNumLines();
+ //(-10) to allow margin between sidebar and formulabar
+ tools::Long margin = (comphelper::LibreOfficeKit::isActive()) ? 10 : 0;
+ Size aTextWindowSize(aSize.Width() - mxTextWindow->GetPosPixel().X() - LEFT_OFFSET - margin,
+ mxTextWindow->GetPixelHeightForLines(nLines));
+ mxTextWindow->SetSizePixel(aTextWindowSize);
+
+ int nTopOffset = 0;
+ if (nLines > 1)
+ {
+ // Initially there is 1 line and the edit is vertically centered in the toolbar
+ // Later, if expanded then the vertical position of the edit will remain at
+ // that initial position, so when calculating the overall size of the expanded
+ // toolbar we have to include that initial offset in order to not make
+ // the edit overlap the RESIZE_HOTSPOT_HEIGHT area so that dragging to resize
+ // is still possible.
+ int nNormalHeight = mxTextWindow->GetPixelHeightForLines(1);
+ int nInitialTopMargin = (mnStandardItemHeight - nNormalHeight) / 2;
+ if (nInitialTopMargin > 0)
+ nTopOffset = nInitialTopMargin;
+ }
+
+ // add empty space of RESIZE_HOTSPOT_HEIGHT so resize is possible when hovering there
+ aSize.setHeight(CalcWindowSizePixel().Height() + RESIZE_HOTSPOT_HEIGHT + nTopOffset);
+
+ if (aStartSize != aSize)
+ SetSizePixel(aSize);
+
+ Invalidate();
+}
+
+void ScInputWindow::NotifyLOKClient()
+{
+ if (comphelper::LibreOfficeKit::isActive() && !GetLOKNotifier() && mpViewShell)
+ SetLOKNotifier(mpViewShell);
+}
+
+void ScInputWindow::SetFuncString( const OUString& rString, bool bDoEdit )
+{
+ //! new method at ScModule to query if function autopilot is open
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
+ mxTextWindow->StartEditEngine();
+
+ ScModule* pScMod = SC_MOD();
+ if ( !pScMod->IsEditMode() )
+ return;
+
+ if ( bDoEdit )
+ mxTextWindow->TextGrabFocus();
+ mxTextWindow->SetTextString(rString, true);
+ EditView* pView = mxTextWindow->GetEditView();
+ if (!pView)
+ return;
+
+ sal_Int32 nLen = rString.getLength();
+
+ if ( nLen > 0 )
+ {
+ nLen--;
+ pView->SetSelection( ESelection( 0, nLen, 0, nLen ) );
+ }
+
+ pScMod->InputChanged(pView);
+ if ( bDoEdit )
+ SetOkCancelMode(); // Not the case if immediately followed by Enter/Cancel
+
+ pView->SetEditEngineUpdateLayout(true);
+}
+
+void ScInputWindow::SetPosString( const OUString& rStr )
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ aWndPos->SetPos( rStr );
+}
+
+void ScInputWindow::SetTextString( const OUString& rString, bool bKitUpdate )
+{
+ if (rString.getLength() <= 32767)
+ mxTextWindow->SetTextString(rString, bKitUpdate);
+ else
+ mxTextWindow->SetTextString(rString.copy(0, 32767), bKitUpdate);
+}
+
+void ScInputWindow::SetOkCancelMode()
+{
+ //! new method at ScModule to query if function autopilot is open
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
+
+ if (bIsOkCancelMode)
+ return;
+
+ EnableItem ( SID_INPUT_SUM, false );
+ EnableItem ( SID_INPUT_EQUAL, false );
+ HideItem ( SID_INPUT_SUM );
+ HideItem ( SID_INPUT_EQUAL );
+
+ ShowItem ( SID_INPUT_CANCEL, true );
+ ShowItem ( SID_INPUT_OK, true );
+ EnableItem ( SID_INPUT_CANCEL, true );
+ EnableItem ( SID_INPUT_OK, true );
+
+ bIsOkCancelMode = true;
+}
+
+void ScInputWindow::SetSumAssignMode()
+{
+ //! new method at ScModule to query if function autopilot is open
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
+
+ if (!bIsOkCancelMode)
+ return;
+
+ EnableItem ( SID_INPUT_CANCEL, false );
+ EnableItem ( SID_INPUT_OK, false );
+ HideItem ( SID_INPUT_CANCEL );
+ HideItem ( SID_INPUT_OK );
+
+ ShowItem ( SID_INPUT_SUM, true );
+ ShowItem ( SID_INPUT_EQUAL, true );
+ EnableItem ( SID_INPUT_SUM, true );
+ EnableItem ( SID_INPUT_EQUAL, true );
+
+ bIsOkCancelMode = false;
+
+ SetFormulaMode(false); // No editing -> no formula
+}
+
+void ScInputWindow::SetFormulaMode( bool bSet )
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ aWndPos->SetFormulaMode(bSet);
+ mxTextWindow->SetFormulaMode(bSet);
+}
+
+bool ScInputWindow::IsInputActive()
+{
+ return mxTextWindow->IsInputActive();
+}
+
+EditView* ScInputWindow::GetEditView()
+{
+ return mxTextWindow->GetEditView();
+}
+
+vcl::Window* ScInputWindow::GetEditWindow()
+{
+ return mxTextWindow;
+}
+
+Point ScInputWindow::GetCursorScreenPixelPos(bool bBelow)
+{
+ return mxTextWindow->GetCursorScreenPixelPos(bBelow);
+}
+
+void ScInputWindow::MakeDialogEditView()
+{
+ mxTextWindow->MakeDialogEditView();
+}
+
+void ScInputWindow::StopEditEngine( bool bAll )
+{
+ mxTextWindow->StopEditEngine( bAll );
+}
+
+void ScInputWindow::TextGrabFocus()
+{
+ mxTextWindow->TextGrabFocus();
+}
+
+void ScInputWindow::TextInvalidate()
+{
+ mxTextWindow->Invalidate();
+}
+
+void ScInputWindow::SwitchToTextWin()
+{
+ // used for shift-ctrl-F2
+
+ mxTextWindow->StartEditEngine();
+ if ( SC_MOD()->IsEditMode() )
+ {
+ mxTextWindow->TextGrabFocus();
+ EditView* pView = mxTextWindow->GetEditView();
+ if (pView)
+ {
+ sal_Int32 nPara = pView->GetEditEngine()->GetParagraphCount() ? ( pView->GetEditEngine()->GetParagraphCount() - 1 ) : 0;
+ sal_Int32 nLen = pView->GetEditEngine()->GetTextLen( nPara );
+ ESelection aSel( nPara, nLen, nPara, nLen );
+ pView->SetSelection( aSel ); // set cursor to end of text
+ }
+ }
+}
+
+void ScInputWindow::PosGrabFocus()
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ aWndPos->GrabFocus();
+}
+
+void ScInputWindow::EnableButtons( bool bEnable )
+{
+ // when enabling buttons, always also enable the input window itself
+ if ( bEnable && !IsEnabled() )
+ Enable();
+
+ EnableItem( SID_INPUT_FUNCTION, bEnable );
+ EnableItem( bIsOkCancelMode ? SID_INPUT_CANCEL : SID_INPUT_SUM, bEnable );
+ EnableItem( bIsOkCancelMode ? SID_INPUT_OK : SID_INPUT_EQUAL, bEnable );
+// Invalidate();
+}
+
+void ScInputWindow::NumLinesChanged()
+{
+ mxTextWindow->NumLinesChanged();
+}
+
+void ScInputWindow::StateChanged( StateChangedType nType )
+{
+ ToolBox::StateChanged( nType );
+
+ if ( nType == StateChangedType::InitShow ) Resize();
+}
+
+void ScInputWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( rDCEvt.GetType() == DataChangedEventType::SETTINGS && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ // update item images
+ SetItemImage(SID_INPUT_FUNCTION, Image(StockImage::Yes, RID_BMP_INPUT_FUNCTION));
+ if ( bIsOkCancelMode )
+ {
+ SetItemImage(SID_INPUT_CANCEL, Image(StockImage::Yes, RID_BMP_INPUT_CANCEL));
+ SetItemImage(SID_INPUT_OK, Image(StockImage::Yes, RID_BMP_INPUT_OK));
+ }
+ else
+ {
+ SetItemImage(SID_INPUT_SUM, Image(StockImage::Yes, RID_BMP_INPUT_SUM));
+ SetItemImage(SID_INPUT_EQUAL, Image(StockImage::Yes, RID_BMP_INPUT_EQUAL));
+ }
+ }
+
+ ToolBox::DataChanged( rDCEvt );
+}
+
+bool ScInputWindow::IsPointerAtResizePos()
+{
+ return GetOutputSizePixel().Height() - GetPointerPosPixel().Y() <= RESIZE_HOTSPOT_HEIGHT;
+}
+
+void ScInputWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ Point aPosPixel = GetPointerPosPixel();
+
+ ScInputBarGroup* pGroupBar = mxTextWindow.get();
+
+ if (bInResize || IsPointerAtResizePos())
+ SetPointer(PointerStyle::WindowSSize);
+ else
+ SetPointer(PointerStyle::Arrow);
+
+ if (bInResize)
+ {
+ // detect direction
+ tools::Long nResizeThreshold = tools::Long(TOOLBOX_WINDOW_HEIGHT * 0.7);
+ bool bResetPointerPos = false;
+
+ // Detect attempt to expand toolbar too much
+ if (aPosPixel.Y() >= mnMaxY)
+ {
+ bResetPointerPos = true;
+ aPosPixel.setY( mnMaxY );
+ } // or expanding down
+ else if (GetOutputSizePixel().Height() - aPosPixel.Y() < -nResizeThreshold)
+ {
+ pGroupBar->IncrementVerticalSize();
+ bResetPointerPos = true;
+ } // or shrinking up
+ else if ((GetOutputSizePixel().Height() - aPosPixel.Y()) > nResizeThreshold)
+ {
+ bResetPointerPos = true;
+ pGroupBar->DecrementVerticalSize();
+ }
+
+ if (bResetPointerPos)
+ {
+ aPosPixel.setY( GetOutputSizePixel().Height() );
+ SetPointerPosPixel(aPosPixel);
+ }
+ }
+
+ ToolBox::MouseMove(rMEvt);
+}
+
+void ScInputWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (rMEvt.IsLeft())
+ {
+ if (IsPointerAtResizePos())
+ {
+ // Don't leave the mouse pointer leave *this* window
+ CaptureMouse();
+ bInResize = true;
+
+ // find the height of the gridwin, we don't want to be
+ // able to expand the toolbar too far so we need to
+ // calculate an upper limit
+ // I'd prefer to leave at least a single column header and a
+ // row but I don't know how to get that value in pixels.
+ // Use TOOLBOX_WINDOW_HEIGHT for the moment
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ mnMaxY = GetOutputSizePixel().Height() + (pViewSh->GetGridHeight(SC_SPLIT_TOP)
+ + pViewSh->GetGridHeight(SC_SPLIT_BOTTOM)) - TOOLBOX_WINDOW_HEIGHT;
+ }
+ }
+
+ ToolBox::MouseButtonDown( rMEvt );
+}
+void ScInputWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ ReleaseMouse();
+ if ( rMEvt.IsLeft() )
+ {
+ bInResize = false;
+ mnMaxY = 0;
+ }
+
+ ToolBox::MouseButtonUp( rMEvt );
+}
+
+void ScInputWindow::AutoSum( bool& bRangeFinder, bool& bSubTotal, OpCode eCode )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( !pViewSh )
+ return;
+
+ const OUString aFormula = pViewSh->DoAutoSum(bRangeFinder, bSubTotal, eCode);
+ if ( aFormula.isEmpty() )
+ return;
+
+ SetFuncString( aFormula );
+ const sal_Int32 aOpen = aFormula.indexOf('(');
+ const sal_Int32 aLen = aFormula.getLength();
+ if (!(bRangeFinder && pScMod->IsEditMode()))
+ return;
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
+ if ( !pHdl )
+ return;
+
+ pHdl->InitRangeFinder( aFormula );
+
+ //! SetSelection at the InputHandler?
+ //! Set bSelIsRef?
+ if ( aOpen != -1 && aLen > aOpen )
+ {
+ ESelection aSel( 0, aOpen + (bSubTotal ? 3 : 1), 0, aLen-1 );
+ EditView* pTableView = pHdl->GetTableView();
+ if ( pTableView )
+ pTableView->SetSelection( aSel );
+ EditView* pTopView = pHdl->GetTopView();
+ if ( pTopView )
+ pTopView->SetSelection( aSel );
+ }
+}
+
+ScInputBarGroup::ScInputBarGroup(vcl::Window* pParent, ScTabViewShell* pViewSh)
+ : InterimItemWindow(pParent, "modules/scalc/ui/inputbar.ui", "InputBar", true, reinterpret_cast<sal_uInt64>(pViewSh))
+ , mxBackground(m_xBuilder->weld_container("background"))
+ , mxTextWndGroup(new ScTextWndGroup(*this, pViewSh))
+ , mxButtonUp(m_xBuilder->weld_button("up"))
+ , mxButtonDown(m_xBuilder->weld_button("down"))
+{
+ InitControlBase(m_xContainer.get());
+
+ SetPaintTransparent(false);
+ SetBackgrounds();
+
+ mxButtonUp->connect_clicked(LINK(this, ScInputBarGroup, ClickHdl));
+ mxButtonDown->connect_clicked(LINK(this, ScInputBarGroup, ClickHdl));
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ mxButtonUp->set_tooltip_text(ScResId( SCSTR_QHELP_COLLAPSE_FORMULA));
+ mxButtonDown->set_tooltip_text(ScResId(SCSTR_QHELP_EXPAND_FORMULA));
+ }
+
+ int nHeight = mxTextWndGroup->GetPixelHeightForLines(1);
+ mxButtonUp->set_size_request(-1, nHeight);
+ mxButtonDown->set_size_request(-1, nHeight);
+
+ // disable the multiline toggle on the mobile phones
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (!comphelper::LibreOfficeKit::isActive() || !(pViewShell && pViewShell->isLOKMobilePhone()))
+ mxButtonDown->show();
+
+ // tdf#154042 Use an initial height of one row so the Toolbar positions
+ // this in the same place regardless of how many rows it eventually shows
+ Size aSize(GetSizePixel().Width(), nHeight);
+ SetSizePixel(aSize);
+}
+
+void ScInputBarGroup::SetBackgrounds()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ SetBackground(rStyleSettings.GetFaceColor());
+ // match to bg used in ScTextWnd::SetDrawingArea to the margin area is drawn with the
+ // same desired bg
+ mxBackground->set_background(rStyleSettings.GetWindowColor());
+}
+
+void ScInputBarGroup::DataChanged(const DataChangedEvent& rDCEvt)
+{
+ InterimItemWindow::DataChanged(rDCEvt);
+ if ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
+ {
+ SetBackgrounds();
+ Invalidate();
+ }
+}
+
+Point ScInputBarGroup::GetCursorScreenPixelPos(bool bBelow)
+{
+ return mxTextWndGroup->GetCursorScreenPixelPos(bBelow);
+}
+
+ScInputBarGroup::~ScInputBarGroup()
+{
+ disposeOnce();
+}
+
+void ScInputBarGroup::dispose()
+{
+ mxTextWndGroup.reset();
+ mxButtonUp.reset();
+ mxButtonDown.reset();
+ mxBackground.reset();
+ InterimItemWindow::dispose();
+}
+
+void ScInputBarGroup::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
+{
+ mxTextWndGroup->InsertAccessibleTextData(rTextData);
+}
+
+void ScInputBarGroup::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
+{
+ mxTextWndGroup->RemoveAccessibleTextData(rTextData);
+}
+
+const OUString& ScInputBarGroup::GetTextString() const
+{
+ return mxTextWndGroup->GetTextString();
+}
+
+void ScInputBarGroup::SetTextString(const OUString& rString, bool bKitUpdate)
+{
+ mxTextWndGroup->SetTextString(rString, bKitUpdate);
+}
+
+void ScInputBarGroup::Resize()
+{
+ mxTextWndGroup->SetScrollPolicy();
+ InterimItemWindow::Resize();
+}
+
+void ScInputBarGroup::StopEditEngine(bool bAll)
+{
+ mxTextWndGroup->StopEditEngine(bAll);
+}
+
+void ScInputBarGroup::StartEditEngine()
+{
+ mxTextWndGroup->StartEditEngine();
+}
+
+void ScInputBarGroup::MakeDialogEditView()
+{
+ mxTextWndGroup->MakeDialogEditView();
+}
+
+EditView* ScInputBarGroup::GetEditView() const
+{
+ return mxTextWndGroup->GetEditView();
+}
+
+bool ScInputBarGroup::HasEditView() const
+{
+ return mxTextWndGroup->HasEditView();
+}
+
+bool ScInputBarGroup::IsInputActive()
+{
+ return mxTextWndGroup->IsInputActive();
+}
+
+void ScInputBarGroup::SetFormulaMode(bool bSet)
+{
+ mxTextWndGroup->SetFormulaMode(bSet);
+}
+
+void ScInputBarGroup::IncrementVerticalSize()
+{
+ mxTextWndGroup->SetNumLines(mxTextWndGroup->GetNumLines() + 1);
+ TriggerToolboxLayout();
+}
+
+void ScInputBarGroup::DecrementVerticalSize()
+{
+ if (mxTextWndGroup->GetNumLines() > 1)
+ {
+ mxTextWndGroup->SetNumLines(mxTextWndGroup->GetNumLines() - 1);
+ TriggerToolboxLayout();
+ }
+}
+
+void ScInputWindow::MenuHdl(std::u16string_view command)
+{
+ if (command.empty())
+ return;
+
+ bool bSubTotal = false;
+ bool bRangeFinder = false;
+ OpCode eCode = ocSum;
+ if ( command == u"sum" )
+ {
+ eCode = ocSum;
+ }
+ else if ( command == u"average" )
+ {
+ eCode = ocAverage;
+ }
+ else if ( command == u"max" )
+ {
+ eCode = ocMax;
+ }
+ else if ( command == u"min" )
+ {
+ eCode = ocMin;
+ }
+ else if ( command == u"count" )
+ {
+ eCode = ocCount;
+ }
+ else if ( command == u"counta" )
+ {
+ eCode = ocCount2;
+ }
+ else if ( command == u"product" )
+ {
+ eCode = ocProduct;
+ }
+ else if (command == u"stdev")
+ {
+ eCode = ocStDev;
+ }
+ else if (command == u"stdevp")
+ {
+ eCode = ocStDevP;
+ }
+ else if (command == u"var")
+ {
+ eCode = ocVar;
+ }
+ else if (command == u"varp")
+ {
+ eCode = ocVarP;
+ }
+
+ AutoSum( bRangeFinder, bSubTotal, eCode );
+}
+
+IMPL_LINK_NOARG(ScInputWindow, DropdownClickHdl, ToolBox *, void)
+{
+ ToolBoxItemId nCurID = GetCurItemId();
+ EndSelection();
+
+ if (nCurID == SID_INPUT_SUM)
+ {
+ tools::Rectangle aRect(GetItemRect(SID_INPUT_SUM));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/scalc/ui/autosum.ui"));
+ std::unique_ptr<weld::Menu> xPopMenu(xBuilder->weld_menu("menu"));
+ MenuHdl(xPopMenu->popup_at_rect(pPopupParent, aRect));
+ }
+}
+
+IMPL_LINK_NOARG(ScInputBarGroup, ClickHdl, weld::Button&, void)
+{
+ if (mxTextWndGroup->GetNumLines() > 1)
+ mxTextWndGroup->SetNumLines(1);
+ else
+ mxTextWndGroup->SetNumLines(mxTextWndGroup->GetLastNumExpandedLines());
+
+ NumLinesChanged();
+}
+
+void ScInputBarGroup::NumLinesChanged()
+{
+ if (mxTextWndGroup->GetNumLines() > 1)
+ {
+ mxButtonDown->hide();
+ mxButtonUp->show();
+ mxTextWndGroup->SetLastNumExpandedLines(mxTextWndGroup->GetNumLines());
+ }
+ else
+ {
+ mxButtonUp->hide();
+ mxButtonDown->show();
+ }
+ TriggerToolboxLayout();
+
+ // Restore focus to input line(s) if necessary
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if ( pHdl && pHdl->IsTopMode() )
+ mxTextWndGroup->TextGrabFocus();
+}
+
+void ScInputBarGroup::TriggerToolboxLayout()
+{
+ // layout changes are expensive and un-necessary.
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ vcl::Window *w=GetParent();
+ ScInputWindow &rParent = dynamic_cast<ScInputWindow&>(*w);
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+
+ if ( !pViewFrm )
+ return;
+
+ Reference< css::beans::XPropertySet > xPropSet( pViewFrm->GetFrame().GetFrameInterface(), UNO_QUERY );
+ Reference< css::frame::XLayoutManager > xLayoutManager;
+
+ if ( xPropSet.is() )
+ {
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ }
+
+ if ( !xLayoutManager.is() )
+ return;
+
+ xLayoutManager->lock();
+ DataChangedEvent aFakeUpdate( DataChangedEventType::SETTINGS, nullptr, AllSettingsFlags::STYLE );
+
+ // this basically will trigger the repositioning of the
+ // items in the toolbar from ImplFormat ( which is controlled by
+ // mnWinHeight ) which in turn is updated in ImplCalcItem which is
+ // controlled by mbCalc. Additionally the ImplFormat above is
+ // controlled via mbFormat. It seems the easiest way to get these
+ // booleans set is to send in the fake event below.
+ rParent.DataChanged( aFakeUpdate);
+
+ // highest item in toolbar will have been calculated via the
+ // event above. Call resize on InputBar to pick up the height
+ // change
+ rParent.Resize();
+
+ // unlock relayouts the toolbars in the 4 quadrants
+ xLayoutManager->unlock();
+}
+
+void ScInputBarGroup::TextGrabFocus()
+{
+ mxTextWndGroup->TextGrabFocus();
+}
+
+constexpr tools::Long gnBorderWidth = (INPUTLINE_INSET_MARGIN + 1) * 2;
+constexpr tools::Long gnBorderHeight = INPUTLINE_INSET_MARGIN + 1;
+
+ScTextWndGroup::ScTextWndGroup(ScInputBarGroup& rParent, ScTabViewShell* pViewSh)
+ : mxTextWnd(new ScTextWnd(*this, pViewSh))
+ , mxScrollWin(rParent.GetBuilder().weld_scrolled_window("scrolledwindow", true))
+ , mxTextWndWin(new weld::CustomWeld(rParent.GetBuilder(), "sc_input_window", *mxTextWnd))
+ , mrParent(rParent)
+{
+ mxScrollWin->connect_vadjustment_changed(LINK(this, ScTextWndGroup, Impl_ScrollHdl));
+ if (ScTabViewShell* pActiveViewShell = comphelper::LibreOfficeKit::isActive() ?
+ dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()) : nullptr)
+ {
+ pActiveViewShell->LOKSendFormulabarUpdate(nullptr, "", ESelection());
+ }
+}
+
+Point ScTextWndGroup::GetCursorScreenPixelPos(bool bBelow)
+{
+ Point aPos;
+ if (!HasEditView())
+ return aPos;
+ EditView* pEditView = GetEditView();
+ vcl::Cursor* pCur = pEditView->GetCursor();
+ if (!pCur)
+ return aPos;
+ Point aLogicPos = pCur->GetPos();
+ if (bBelow)
+ aLogicPos.AdjustY(pCur->GetHeight());
+ aPos = GetEditViewDevice().LogicToPixel(aLogicPos);
+ bool bRTL = mrParent.IsRTLEnabled();
+ if (bRTL)
+ aPos.setX(mxTextWnd->GetOutputSizePixel().Width() - aPos.X() + gnBorderWidth);
+ else
+ aPos.AdjustX(gnBorderWidth + 1);
+
+ return mrParent.OutputToScreenPixel(aPos);
+}
+
+ScTextWndGroup::~ScTextWndGroup()
+{
+}
+
+void ScTextWndGroup::InsertAccessibleTextData(ScAccessibleEditLineTextData& rTextData)
+{
+ mxTextWnd->InsertAccessibleTextData(rTextData);
+}
+
+EditView* ScTextWndGroup::GetEditView() const
+{
+ return mxTextWnd->GetEditView();
+}
+
+const OutputDevice& ScTextWndGroup::GetEditViewDevice() const
+{
+ return mxTextWnd->GetEditViewDevice();
+}
+
+tools::Long ScTextWndGroup::GetLastNumExpandedLines() const
+{
+ return mxTextWnd->GetLastNumExpandedLines();
+}
+
+void ScTextWndGroup::SetLastNumExpandedLines(tools::Long nLastExpandedLines)
+{
+ mxTextWnd->SetLastNumExpandedLines(nLastExpandedLines);
+}
+
+tools::Long ScTextWndGroup::GetNumLines() const
+{
+ return mxTextWnd->GetNumLines();
+}
+
+int ScTextWndGroup::GetPixelHeightForLines(tools::Long nLines)
+{
+ return mxTextWnd->GetPixelHeightForLines(nLines) + 2 * gnBorderHeight;
+}
+
+weld::ScrolledWindow& ScTextWndGroup::GetScrollWin()
+{
+ return *mxScrollWin;
+}
+
+const OUString& ScTextWndGroup::GetTextString() const
+{
+ return mxTextWnd->GetTextString();
+}
+
+bool ScTextWndGroup::HasEditView() const
+{
+ return mxTextWnd->HasEditView();
+}
+
+bool ScTextWndGroup::IsInputActive()
+{
+ return mxTextWnd->IsInputActive();
+}
+
+void ScTextWndGroup::MakeDialogEditView()
+{
+ mxTextWnd->MakeDialogEditView();
+}
+
+void ScTextWndGroup::RemoveAccessibleTextData(ScAccessibleEditLineTextData& rTextData)
+{
+ mxTextWnd->RemoveAccessibleTextData(rTextData);
+}
+
+void ScTextWndGroup::SetScrollPolicy()
+{
+ if (mxTextWnd->GetNumLines() > 2)
+ mxScrollWin->set_vpolicy(VclPolicyType::ALWAYS);
+ else
+ mxScrollWin->set_vpolicy(VclPolicyType::NEVER);
+}
+
+void ScTextWndGroup::SetNumLines(tools::Long nLines)
+{
+ mxTextWnd->SetNumLines(nLines);
+}
+
+void ScTextWndGroup::SetFormulaMode(bool bSet)
+{
+ mxTextWnd->SetFormulaMode(bSet);
+}
+
+void ScTextWndGroup::SetTextString(const OUString& rString, bool bKitUpdate)
+{
+ mxTextWnd->SetTextString(rString, bKitUpdate);
+}
+
+void ScTextWndGroup::StartEditEngine()
+{
+ mxTextWnd->StartEditEngine();
+}
+
+void ScTextWndGroup::StopEditEngine(bool bAll)
+{
+ mxTextWnd->StopEditEngine( bAll );
+}
+
+void ScTextWndGroup::TextGrabFocus()
+{
+ mxTextWnd->TextGrabFocus();
+}
+
+IMPL_LINK_NOARG(ScTextWndGroup, Impl_ScrollHdl, weld::ScrolledWindow&, void)
+{
+ mxTextWnd->DoScroll();
+}
+
+void ScTextWnd::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Color aBgColor = rStyleSettings.GetWindowColor();
+ rRenderContext.SetBackground(aBgColor);
+
+ // tdf#137713 we rely on GetEditView creating it if it doesn't already exist so
+ // GetEditView() must be called unconditionally
+ if (EditView* pView = GetEditView())
+ {
+ if (mbInvalidate)
+ {
+ pView->Invalidate();
+ mbInvalidate = false;
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && m_xEditEngine)
+ {
+ // EditEngine/EditView works in twips logical coordinates, so set the device map-mode to twips before painting
+ // and use twips version of the painting area 'rRect'.
+ // Document zoom should not be included in this conversion.
+ tools::Rectangle aLogicRect = OutputDevice::LogicToLogic(rRect, MapMode(MapUnit::MapPixel), MapMode(MapUnit::MapTwip));
+ MapMode aOriginalMode = rRenderContext.GetMapMode();
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
+ WeldEditView::Paint(rRenderContext, aLogicRect);
+ rRenderContext.SetMapMode(aOriginalMode);
+ }
+ else
+ WeldEditView::Paint(rRenderContext, rRect);
+}
+
+EditView* ScTextWnd::GetEditView() const
+{
+ if ( !m_xEditView )
+ const_cast<ScTextWnd&>(*this).InitEditEngine();
+ return m_xEditView.get();
+}
+
+bool ScTextWnd::HasEditView() const { return m_xEditView != nullptr; }
+
+const OutputDevice& ScTextWnd::GetEditViewDevice() const
+{
+ return EditViewOutputDevice();
+}
+
+int ScTextWnd::GetPixelHeightForLines(tools::Long nLines)
+{
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ return rDevice.LogicToPixel(Size(0, nLines * rDevice.GetTextHeight())).Height() + 1;
+}
+
+tools::Long ScTextWnd::GetNumLines() const
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ return rViewData.GetFormulaBarLines();
+}
+
+void ScTextWnd::SetNumLines(tools::Long nLines)
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ rViewData.SetFormulaBarLines(nLines);
+ if ( nLines > 1 )
+ {
+ // SetFormulaBarLines sanitizes the height, so get the sanitized value
+ mnLastExpandedLines = rViewData.GetFormulaBarLines();
+ Resize();
+ }
+}
+
+void ScTextWnd::Resize()
+{
+ if (m_xEditView)
+ {
+ Size aOutputSize = GetOutputSizePixel();
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ tools::Rectangle aOutputArea = rDevice.PixelToLogic( tools::Rectangle( Point(), aOutputSize ));
+ m_xEditView->SetOutputArea( aOutputArea );
+
+ // Don't leave an empty area at the bottom if we can move the text down.
+ tools::Long nMaxVisAreaTop = m_xEditEngine->GetTextHeight() - aOutputArea.GetHeight();
+ if (m_xEditView->GetVisArea().Top() > nMaxVisAreaTop)
+ {
+ m_xEditView->Scroll(0, m_xEditView->GetVisArea().Top() - nMaxVisAreaTop);
+ }
+
+ m_xEditEngine->SetPaperSize( rDevice.PixelToLogic( Size( aOutputSize.Width(), 10000 ) ) );
+ }
+
+ // skip WeldEditView's Resize();
+ weld::CustomWidgetController::Resize();
+
+ SetScrollBarRange();
+}
+
+int ScTextWnd::GetEditEngTxtHeight() const
+{
+ return m_xEditView ? m_xEditView->GetEditEngine()->GetTextHeight() : 0;
+}
+
+void ScTextWnd::SetScrollBarRange()
+{
+ if (!m_xEditView)
+ return;
+
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ Size aOutputSize = rDevice.GetOutputSize();
+
+ int nUpper = GetEditEngTxtHeight();
+ int nCurrentDocPos = m_xEditView->GetVisArea().Top();
+ int nStepIncrement = GetTextHeight();
+ int nPageIncrement = aOutputSize.Height();
+ int nPageSize = aOutputSize.Height();
+
+ /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has
+ effectively...
+
+ lower = gtk_adjustment_get_lower
+ upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
+
+ and requires that upper > lower or the deceleration animation never ends
+ */
+ nPageSize = std::min(nPageSize, nUpper);
+
+ weld::ScrolledWindow& rVBar = mrGroupBar.GetScrollWin();
+ rVBar.vadjustment_configure(nCurrentDocPos, 0, nUpper,
+ nStepIncrement, nPageIncrement, nPageSize);
+}
+
+void ScTextWnd::DoScroll()
+{
+ if (m_xEditView)
+ {
+ weld::ScrolledWindow& rVBar = mrGroupBar.GetScrollWin();
+ auto currentDocPos = m_xEditView->GetVisArea().Top();
+ auto nDiff = currentDocPos - rVBar.vadjustment_get_value();
+ // we expect SetScrollBarRange callback to be triggered by Scroll
+ // to set where we ended up
+ m_xEditView->Scroll(0, nDiff);
+ }
+}
+
+void ScTextWnd::StartEditEngine()
+{
+ // Don't activate if we're a modal dialog ourselves (Doc-modal dialog)
+ SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if ( pObjSh && pObjSh->IsInModalMode() )
+ return;
+
+ if ( !m_xEditView || !m_xEditEngine )
+ {
+ InitEditEngine();
+ }
+
+ ScInputHandler* pHdl = mpViewShell->GetInputHandler();
+ if (pHdl)
+ pHdl->SetMode(SC_INPUT_TOP, nullptr, static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get()));
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
+}
+
+static void lcl_ExtendEditFontAttribs( SfxItemSet& rSet )
+{
+ const SfxPoolItem& rFontItem = rSet.Get( EE_CHAR_FONTINFO );
+ std::unique_ptr<SfxPoolItem> pNewItem(rFontItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_FONTINFO_CJK);
+ rSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_FONTINFO_CTL);
+ rSet.Put( *pNewItem );
+ const SfxPoolItem& rHeightItem = rSet.Get( EE_CHAR_FONTHEIGHT );
+ pNewItem.reset(rHeightItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_FONTHEIGHT_CJK);
+ rSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_FONTHEIGHT_CTL);
+ rSet.Put( *pNewItem );
+ const SfxPoolItem& rWeightItem = rSet.Get( EE_CHAR_WEIGHT );
+ pNewItem.reset(rWeightItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK);
+ rSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL);
+ rSet.Put( *pNewItem );
+ const SfxPoolItem& rItalicItem = rSet.Get( EE_CHAR_ITALIC );
+ pNewItem.reset(rItalicItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_ITALIC_CJK);
+ rSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_ITALIC_CTL);
+ rSet.Put( *pNewItem );
+ const SfxPoolItem& rLangItem = rSet.Get( EE_CHAR_LANGUAGE );
+ pNewItem.reset(rLangItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK);
+ rSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL);
+ rSet.Put( *pNewItem );
+}
+
+static void lcl_ModifyRTLDefaults( SfxItemSet& rSet )
+{
+ rSet.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+
+ // always using rtl writing direction would break formulas
+ //rSet.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) );
+
+ // PaperSize width is limited to USHRT_MAX in RTL mode (because of EditEngine's
+ // sal_uInt16 values in EditLine), so the text may be wrapped and line spacing must be
+ // increased to not see the beginning of the next line.
+ SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL );
+ aItem.SetPropLineSpace( 200 );
+ rSet.Put( aItem );
+}
+
+static void lcl_ModifyRTLVisArea( EditView* pEditView )
+{
+ tools::Rectangle aVisArea = pEditView->GetVisArea();
+ Size aPaper = pEditView->GetEditEngine()->GetPaperSize();
+ tools::Long nDiff = aPaper.Width() - aVisArea.Right();
+ aVisArea.AdjustLeft(nDiff );
+ aVisArea.AdjustRight(nDiff );
+ pEditView->SetVisArea(aVisArea);
+}
+
+void ScTextWnd::InitEditEngine()
+{
+ std::unique_ptr<ScFieldEditEngine> pNew;
+ ScDocShell* pDocSh = nullptr;
+ if ( mpViewShell )
+ {
+ pDocSh = mpViewShell->GetViewData().GetDocShell();
+ ScDocument& rDoc = mpViewShell->GetViewData().GetDocument();
+ pNew = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
+ }
+ else
+ pNew = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), nullptr, true);
+ pNew->SetExecuteURL( false );
+ m_xEditEngine = std::move(pNew);
+
+ Size barSize = GetOutputSizePixel();
+ m_xEditEngine->SetUpdateLayout( false );
+ m_xEditEngine->SetPaperSize( GetDrawingArea()->get_ref_device().PixelToLogic(Size(barSize.Width(),10000)) );
+ m_xEditEngine->SetWordDelimiters(
+ ScEditUtil::ModifyDelimiters( m_xEditEngine->GetWordDelimiters() ) );
+ m_xEditEngine->SetReplaceLeadingSingleQuotationMark( false );
+
+ UpdateAutoCorrFlag();
+
+ {
+ auto pSet = std::make_unique<SfxItemSet>( m_xEditEngine->GetEmptyItemSet() );
+ EditEngine::SetFontInfoInItemSet( *pSet, aTextFont );
+ lcl_ExtendEditFontAttribs( *pSet );
+ // turn off script spacing to match DrawText output
+ pSet->Put( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) );
+ if ( bIsRTL )
+ lcl_ModifyRTLDefaults( *pSet );
+ static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetDefaults( std::move(pSet) );
+ }
+
+ // If the Cell contains URLFields, they need to be taken over into the entry row,
+ // or else the position is not correct anymore
+ bool bFilled = false;
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if ( pHdl ) //! Test if it's the right InputHdl?
+ bFilled = pHdl->GetTextAndFields(static_cast<ScEditEngineDefaulter&>(*m_xEditEngine));
+
+ m_xEditEngine->SetUpdateLayout( true );
+
+ // aString is the truth ...
+ if (bFilled && m_xEditEngine->GetText() == aString)
+ Invalidate(); // Repaint for (filled) Field
+ else
+ static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetTextCurrentDefaults(aString); // At least the right text then
+
+ m_xEditView = std::make_unique<EditView>(m_xEditEngine.get(), nullptr);
+
+ // we get cursor, selection etc. messages from the VCL/window layer
+ // otherwise these are injected into the document causing confusion.
+ m_xEditView->SuppressLOKMessages(true);
+
+ m_xEditView->setEditViewCallbacks(this);
+ m_xEditView->SetInsertMode(bIsInsertMode);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Color aBgColor = rStyleSettings.GetWindowColor();
+ m_xEditView->SetBackgroundColor(aBgColor);
+
+ if (pAcc)
+ {
+ pAcc->InitAcc(nullptr, m_xEditView.get(),
+ ScResId(STR_ACC_EDITLINE_NAME),
+ ScResId(STR_ACC_EDITLINE_DESCR));
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xEditView->RegisterViewShell(mpViewShell);
+
+ // Text from Clipboard is taken over as ASCII in a single row
+ EVControlBits n = m_xEditView->GetControlWord();
+ m_xEditView->SetControlWord( n | EVControlBits::SINGLELINEPASTE );
+
+ m_xEditEngine->InsertView( m_xEditView.get(), EE_APPEND );
+
+ Resize();
+
+ if ( bIsRTL )
+ lcl_ModifyRTLVisArea( m_xEditView.get() );
+
+ m_xEditEngine->SetModifyHdl(LINK(this, ScTextWnd, ModifyHdl));
+ m_xEditEngine->SetStatusEventHdl(LINK(this, ScTextWnd, EditStatusHdl));
+
+ if (!maAccTextDatas.empty())
+ maAccTextDatas.back()->StartEdit();
+
+ // as long as EditEngine and DrawText sometimes differ for CTL text,
+ // repaint now to have the EditEngine's version visible
+ if (pDocSh)
+ {
+ ScDocument& rDoc = pDocSh->GetDocument(); // any document
+ SvtScriptType nScript = rDoc.GetStringScriptType( aString );
+ if ( nScript & SvtScriptType::COMPLEX )
+ Invalidate();
+ }
+}
+
+ScTextWnd::ScTextWnd(ScTextWndGroup& rParent, ScTabViewShell* pViewSh) :
+ bIsRTL(AllSettings::GetLayoutRTL()),
+ bIsInsertMode(true),
+ bFormulaMode (false),
+ bInputMode (false),
+ mpViewShell(pViewSh),
+ mrGroupBar(rParent),
+ mnLastExpandedLines(INPUTWIN_MULTILINES),
+ mbInvalidate(false)
+{
+}
+
+ScTextWnd::~ScTextWnd()
+{
+ while (!maAccTextDatas.empty()) {
+ maAccTextDatas.back()->Dispose();
+ }
+}
+
+bool ScTextWnd::MouseMove( const MouseEvent& rMEvt )
+{
+ return m_xEditView && m_xEditView->MouseMove(rMEvt);
+}
+
+bool ScTextWnd::CanFocus() const
+{
+ return SC_MOD()->IsEditMode();
+}
+
+void ScTextWnd::UpdateFocus()
+{
+ if (!HasFocus())
+ {
+ StartEditEngine();
+ if (CanFocus())
+ TextGrabFocus();
+ }
+}
+
+bool ScTextWnd::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ UpdateFocus();
+
+ bool bClickOnSelection = false;
+ if (m_xEditView)
+ {
+ m_xEditView->SetEditEngineUpdateLayout( true );
+ bClickOnSelection = m_xEditView->IsSelectionAtPoint(rMEvt.GetPosPixel());
+ }
+ if (!bClickOnSelection)
+ {
+ rtl::Reference<TransferDataContainer> xTransferable(new TransferDataContainer);
+ GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_NONE);
+ }
+ else
+ {
+ rtl::Reference<TransferDataContainer> xTransferable(m_xHelper);
+ GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_COPY);
+ }
+ return WeldEditView::MouseButtonDown(rMEvt);
+}
+
+bool ScTextWnd::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ bool bRet = WeldEditView::MouseButtonUp(rMEvt);
+ if (bRet)
+ {
+ if ( rMEvt.IsMiddle() &&
+ Application::GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
+ {
+ // EditView may have pasted from selection
+ SC_MOD()->InputChanged( m_xEditView.get() );
+ }
+ else
+ SC_MOD()->InputSelection( m_xEditView.get() );
+ }
+ return bRet;
+}
+
+bool ScTextWnd::Command( const CommandEvent& rCEvt )
+{
+ bool bConsumed = false;
+
+ bInputMode = true;
+ CommandEventId nCommand = rCEvt.GetCommand();
+ if (m_xEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pStartViewSh = ScTabViewShell::GetActiveViewShell();
+
+ // don't modify the font defaults here - the right defaults are
+ // already set in StartEditEngine when the EditEngine is created
+
+ // Prevent that the EditView is lost when switching between Views
+ pScMod->SetInEditCommand( true );
+ m_xEditView->Command( rCEvt );
+ pScMod->SetInEditCommand( false );
+
+ // CommandEventId::StartDrag does not mean by far that the content was actually changed,
+ // so don't trigger an InputChanged.
+ //! Detect if dragged with Move or forbid Drag&Move somehow
+
+ if ( nCommand == CommandEventId::StartDrag )
+ {
+ // Is dragged onto another View?
+ ScTabViewShell* pEndViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pEndViewSh != pStartViewSh && pStartViewSh != nullptr )
+ {
+ ScViewData& rViewData = pStartViewSh->GetViewData();
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pStartViewSh );
+ if ( pHdl && rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ pHdl->CancelHandler();
+ rViewData.GetView()->ShowCursor(); // Missing for KillEditView, due to being inactive
+ }
+ }
+ }
+ else if ( nCommand == CommandEventId::EndExtTextInput )
+ {
+ if (bFormulaMode)
+ {
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->InputCommand(rCEvt);
+ }
+ }
+ else if ( nCommand == CommandEventId::CursorPos )
+ {
+ // don't call InputChanged for CommandEventId::CursorPos
+ }
+ else if ( nCommand == CommandEventId::InputLanguageChange )
+ {
+ // #i55929# Font and font size state depends on input language if nothing is selected,
+ // so the slots have to be invalidated when the input language is changed.
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ {
+ SfxBindings& rBindings = pViewFrm->GetBindings();
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ }
+ else if ( nCommand == CommandEventId::ContextMenu )
+ {
+ bConsumed = true;
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ {
+ Point aPos = rCEvt.GetMousePosPixel();
+ if (!rCEvt.IsMouseEvent())
+ {
+ Size aSize = GetOutputSizePixel();
+ aPos = Point(aSize.Width() / 2, aSize.Height() / 2);
+ }
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ UpdateFocus();
+ pViewFrm->GetDispatcher()->ExecutePopup("formulabar", &mrGroupBar.GetVclParent(), &aPos);
+ }
+ }
+ else if ( nCommand == CommandEventId::Wheel )
+ {
+ //don't call InputChanged for CommandEventId::Wheel
+ }
+ else if ( nCommand == CommandEventId::GestureSwipe )
+ {
+ //don't call InputChanged for CommandEventId::GestureSwipe
+ }
+ else if ( nCommand == CommandEventId::GestureLongPress )
+ {
+ //don't call InputChanged for CommandEventId::GestureLongPress
+ }
+ else if ( nCommand == CommandEventId::ModKeyChange )
+ {
+ //pass alt press/release to parent impl
+ }
+ else
+ SC_MOD()->InputChanged( m_xEditView.get() );
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && nCommand == CommandEventId::CursorPos )
+ {
+ // LOK uses this to setup caret position because drawingarea is replaced
+ // with text input field, it sends logical caret position (start, end) not pixels
+
+ StartEditEngine();
+ TextGrabFocus();
+
+ if (!m_xEditView)
+ return true;
+
+ // information about paragraph is in additional data
+ // information about position in a paragraph in a Mouse Pos
+ // see vcl/jsdialog/executor.cxx "textselection" event
+ const Point* pParaPoint = static_cast<const Point*>(rCEvt.GetEventData());
+ Point aSelectionStartEnd = rCEvt.GetMousePosPixel();
+
+ sal_Int32 nParaStart, nParaEnd, nPosStart, nPosEnd;
+
+ nParaStart = pParaPoint ? pParaPoint->X() : 0;
+ nParaEnd = pParaPoint ? pParaPoint->Y() : 0;
+ nPosStart = m_xEditView->GetPosNoField(nParaStart, aSelectionStartEnd.X());
+ nPosEnd = m_xEditView->GetPosNoField(nParaEnd, aSelectionStartEnd.Y());
+
+ m_xEditView->SetSelection(ESelection(nParaStart, nPosStart, nParaEnd, nPosEnd));
+ SC_MOD()->InputSelection( m_xEditView.get() );
+
+ bConsumed = true;
+ }
+
+ bInputMode = false;
+
+ return bConsumed;
+}
+
+bool ScTextWnd::StartDrag()
+{
+ // tdf#145248 don't start a drag if actively selecting
+ if (m_xEditView && !m_xEditEngine->IsInSelectionMode())
+ {
+ OUString sSelection = m_xEditView->GetSelected();
+ m_xHelper->SetData(sSelection);
+ return sSelection.isEmpty();
+ }
+ return true;
+}
+
+bool ScTextWnd::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bUsed = true;
+ bInputMode = true;
+ if (!SC_MOD()->InputKeyEvent( rKEvt ))
+ {
+ bUsed = false;
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ bUsed = pViewSh->SfxKeyInput(rKEvt); // Only accelerators, no input
+ }
+ bInputMode = false;
+ return bUsed;
+}
+
+void ScTextWnd::GetFocus()
+{
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ pViewSh->SetFormShellAtTop( false ); // focus in input line -> FormShell no longer on top
+ WeldEditView::GetFocus();
+}
+
+void ScTextWnd::SetFormulaMode( bool bSet )
+{
+ if ( bSet != bFormulaMode )
+ {
+ bFormulaMode = bSet;
+ UpdateAutoCorrFlag();
+ }
+}
+
+void ScTextWnd::UpdateAutoCorrFlag()
+{
+ if (m_xEditEngine)
+ {
+ EEControlBits nControl = m_xEditEngine->GetControlWord();
+ EEControlBits nOld = nControl;
+ if ( bFormulaMode )
+ nControl &= ~EEControlBits::AUTOCORRECT; // No AutoCorrect in Formulas
+ else
+ nControl |= EEControlBits::AUTOCORRECT; // Else do enable it
+
+ if ( nControl != nOld )
+ m_xEditEngine->SetControlWord( nControl );
+ }
+}
+
+void ScTextWnd::EditViewScrollStateChange()
+{
+ // editengine height has changed or editview scroll pos has changed
+ SetScrollBarRange();
+}
+
+IMPL_LINK_NOARG(ScTextWnd, ModifyHdl, LinkParamNone*, void)
+{
+ if (m_xEditView && !bInputMode)
+ {
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+
+ // Use the InputHandler's InOwnChange flag to prevent calling InputChanged
+ // while an InputHandler method is modifying the EditEngine content
+
+ if ( pHdl && !pHdl->IsInOwnChange() )
+ pHdl->InputChanged( m_xEditView.get(), true ); // #i20282# InputChanged must know if called from modify handler
+ }
+}
+
+IMPL_LINK_NOARG(ScTextWnd, EditStatusHdl, EditStatus&, void)
+{
+ SetScrollBarRange();
+ DoScroll();
+ Invalidate();
+}
+
+void ScTextWnd::StopEditEngine( bool bAll )
+{
+ if (!m_xEditEngine)
+ return;
+
+ if (m_xEditView)
+ {
+ if (!maAccTextDatas.empty())
+ maAccTextDatas.back()->EndEdit();
+
+ ScModule* pScMod = SC_MOD();
+
+ if (!bAll)
+ pScMod->InputSelection( m_xEditView.get() );
+ aString = m_xEditEngine->GetText();
+ bIsInsertMode = m_xEditView->IsInsertMode();
+ bool bSelection = m_xEditView->HasSelection();
+ m_xEditEngine->SetStatusEventHdl(Link<EditStatus&, void>());
+ m_xEditEngine->SetModifyHdl(Link<LinkParamNone*,void>());
+ m_xEditView.reset();
+ m_xEditEngine.reset();
+
+ ScInputHandler* pHdl = mpViewShell->GetInputHandler();
+
+ if (pHdl && pHdl->IsEditMode() && !bAll)
+ pHdl->SetMode(SC_INPUT_TABLE);
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
+
+ if (bSelection)
+ Invalidate(); // So that the Selection is not left there
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Clear
+ std::vector<ReferenceMark> aReferenceMarks;
+ ScInputHandler::SendReferenceMarks( mpViewShell, aReferenceMarks );
+ }
+}
+
+static sal_Int32 findFirstNonMatchingChar(const OUString& rStr1, const OUString& rStr2)
+{
+ // Search the string for unmatching chars
+ const sal_Unicode* pStr1 = rStr1.getStr();
+ const sal_Unicode* pStr2 = rStr2.getStr();
+ sal_Int32 i = 0;
+ while ( i < rStr1.getLength() )
+ {
+ // Abort on the first unmatching char
+ if ( *pStr1 != *pStr2 )
+ return i;
+ ++pStr1;
+ ++pStr2;
+ ++i;
+ }
+
+ return i;
+}
+
+void ScTextWnd::SetTextString( const OUString& rNewString, bool bKitUpdate )
+{
+ // Ideally it would be best to create on demand the EditEngine/EditView here, but... for
+ // the initialisation scenario where a cell is first clicked on we end up with the text in the
+ // inputbar window scrolled to the bottom if we do that here ( because the tableview and topview
+ // are synced I guess ).
+ // should fix that I suppose :-/ need to look a bit further into that
+ mbInvalidate = true; // ensure next Paint ( that uses editengine ) call will call Invalidate first
+
+ if ( rNewString != aString )
+ {
+ bInputMode = true;
+
+ // Find position of the change, only paint the rest
+ if (!m_xEditEngine)
+ {
+ bool bPaintAll = GetNumLines() > 1 || bIsRTL;
+ if (!bPaintAll)
+ {
+ // test if CTL script type is involved
+ SvtScriptType nOldScript = SvtScriptType::NONE;
+ SvtScriptType nNewScript = SvtScriptType::NONE;
+ SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if ( auto pDocShell = dynamic_cast<ScDocShell*>( pObjSh) )
+ {
+ // any document can be used (used only for its break iterator)
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nOldScript = rDoc.GetStringScriptType( aString );
+ nNewScript = rDoc.GetStringScriptType( rNewString );
+ }
+ bPaintAll = ( nOldScript & SvtScriptType::COMPLEX ) || ( nNewScript & SvtScriptType::COMPLEX );
+ }
+
+ if ( bPaintAll )
+ {
+ // In multiline mode, or if CTL is involved, the whole text has to be redrawn
+ Invalidate();
+ }
+ else
+ {
+ tools::Long nTextSize = 0;
+ sal_Int32 nDifPos;
+ if (rNewString.getLength() > aString.getLength())
+ nDifPos = findFirstNonMatchingChar(rNewString, aString);
+ else
+ nDifPos = findFirstNonMatchingChar(aString, rNewString);
+
+ tools::Long nSize1 = GetTextWidth(aString);
+ tools::Long nSize2 = GetTextWidth(rNewString);
+ if ( nSize1>0 && nSize2>0 )
+ nTextSize = std::max( nSize1, nSize2 );
+ else
+ nTextSize = GetOutputSizePixel().Width(); // Overflow
+
+ Point aLogicStart = GetDrawingArea()->get_ref_device().PixelToLogic(Point(0,0));
+ tools::Long nStartPos = aLogicStart.X();
+ tools::Long nInvPos = nStartPos;
+ if (nDifPos)
+ nInvPos += GetTextWidth(aString.copy(0,nDifPos));
+
+ Invalidate(tools::Rectangle(nInvPos, 0, nStartPos+nTextSize, GetOutputSizePixel().Height() - 1));
+ }
+ }
+ else
+ {
+ static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetTextCurrentDefaults(rNewString);
+ }
+
+ aString = rNewString;
+
+ if (!maAccTextDatas.empty())
+ maAccTextDatas.back()->TextChanged();
+
+ bInputMode = false;
+ }
+
+ if (ScTabViewShell* pActiveViewShell = bKitUpdate && comphelper::LibreOfficeKit::isActive() ?
+ dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()) : nullptr)
+ {
+ ESelection aSel = m_xEditView ? m_xEditView->GetSelection() : ESelection();
+ pActiveViewShell->LOKSendFormulabarUpdate(m_xEditView.get(), rNewString, aSel);
+ }
+
+ SetScrollBarRange();
+ DoScroll();
+}
+
+const OUString& ScTextWnd::GetTextString() const
+{
+ return aString;
+}
+
+bool ScTextWnd::IsInputActive()
+{
+ return HasFocus();
+}
+
+void ScTextWnd::MakeDialogEditView()
+{
+ if ( m_xEditView ) return;
+
+ std::unique_ptr<ScFieldEditEngine> pNew;
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ {
+ ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
+ pNew = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool());
+ }
+ else
+ pNew = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), nullptr, true);
+ pNew->SetExecuteURL( false );
+ m_xEditEngine = std::move(pNew);
+
+ const bool bPrevUpdateLayout = m_xEditEngine->SetUpdateLayout( false );
+ m_xEditEngine->SetWordDelimiters( m_xEditEngine->GetWordDelimiters() + "=" );
+ m_xEditEngine->SetPaperSize( Size( bIsRTL ? USHRT_MAX : THESIZE, 300 ) );
+
+ auto pSet = std::make_unique<SfxItemSet>( m_xEditEngine->GetEmptyItemSet() );
+ EditEngine::SetFontInfoInItemSet( *pSet, aTextFont );
+ lcl_ExtendEditFontAttribs( *pSet );
+ if ( bIsRTL )
+ lcl_ModifyRTLDefaults( *pSet );
+ static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetDefaults( std::move(pSet) );
+ m_xEditEngine->SetUpdateLayout( bPrevUpdateLayout );
+
+ m_xEditView = std::make_unique<EditView>(m_xEditEngine.get(), nullptr);
+ m_xEditView->setEditViewCallbacks(this);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Color aBgColor = rStyleSettings.GetWindowColor();
+ m_xEditView->SetBackgroundColor(aBgColor);
+
+ if (pAcc)
+ {
+ pAcc->InitAcc(nullptr, m_xEditView.get(),
+ ScResId(STR_ACC_EDITLINE_NAME),
+ ScResId(STR_ACC_EDITLINE_DESCR));
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xEditView->RegisterViewShell(mpViewShell);
+ m_xEditEngine->InsertView( m_xEditView.get(), EE_APPEND );
+
+ Resize();
+
+ if ( bIsRTL )
+ lcl_ModifyRTLVisArea( m_xEditView.get() );
+
+ if (!maAccTextDatas.empty())
+ maAccTextDatas.back()->StartEdit();
+}
+
+void ScTextWnd::ImplInitSettings()
+{
+ bIsRTL = AllSettings::GetLayoutRTL();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ Color aBgColor= rStyleSettings.GetWindowColor();
+ Color aTxtColor= rStyleSettings.GetWindowTextColor();
+
+ aTextFont.SetFillColor ( aBgColor );
+ aTextFont.SetColor (aTxtColor);
+ Invalidate();
+}
+
+void ScTextWnd::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ // bypass WeldEditView::SetDrawingArea
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ // set cursor
+ pDrawingArea->set_cursor(PointerStyle::Text);
+
+ // initialize dnd, deliberately just a simple string so
+ // we don't transfer the happenstance formatting in
+ // the input line
+ m_xHelper.set(new svt::OStringTransferable(OUString()));
+ rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
+ SetDragDataTransferable(xHelper, DND_ACTION_COPY);
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ pDrawingArea->set_margin_start(gnBorderWidth);
+ pDrawingArea->set_margin_end(gnBorderWidth);
+ // leave 1 for the width of the scrolledwindow border
+ pDrawingArea->set_margin_top(gnBorderHeight - 1);
+ pDrawingArea->set_margin_bottom(gnBorderHeight - 1);
+
+ // always use application font, so a font with cjk chars can be installed
+ vcl::Font aAppFont = Application::GetSettings().GetStyleSettings().GetAppFont();
+ weld::SetPointFont(rDevice, aAppFont);
+
+ aTextFont = rDevice.GetFont();
+ Size aFontSize = aTextFont.GetFontSize();
+ aTextFont.SetFontSize(rDevice.PixelToLogic(aFontSize, MapMode(MapUnit::MapTwip)));
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ Color aBgColor = rStyleSettings.GetWindowColor();
+ Color aTxtColor = rStyleSettings.GetWindowTextColor();
+
+ aTextFont.SetTransparent(true);
+ aTextFont.SetFillColor(aBgColor);
+ aTextFont.SetColor(aTxtColor);
+ aTextFont.SetWeight(WEIGHT_NORMAL);
+
+ Size aSize(1, GetPixelHeightForLines(1));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+
+ rDevice.SetBackground(aBgColor);
+ rDevice.SetLineColor(COL_BLACK);
+ rDevice.SetMapMode(MapMode(MapUnit::MapTwip));
+ rDevice.SetFont(aTextFont);
+
+ EnableRTL(false); // EditEngine can't be used with VCL EnableRTL
+}
+
+css::uno::Reference< css::accessibility::XAccessible > ScTextWnd::CreateAccessible()
+{
+ pAcc = new ScAccessibleEditLineObject(this);
+ return pAcc;
+}
+
+void ScTextWnd::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
+{
+ OSL_ENSURE( ::std::find( maAccTextDatas.begin(), maAccTextDatas.end(), &rTextData ) == maAccTextDatas.end(),
+ "ScTextWnd::InsertAccessibleTextData - passed object already registered" );
+ maAccTextDatas.push_back( &rTextData );
+}
+
+void ScTextWnd::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
+{
+ AccTextDataVector::iterator aEnd = maAccTextDatas.end();
+ AccTextDataVector::iterator aIt = ::std::find( maAccTextDatas.begin(), aEnd, &rTextData );
+ OSL_ENSURE( aIt != aEnd, "ScTextWnd::RemoveAccessibleTextData - passed object not registered" );
+ if( aIt != aEnd )
+ maAccTextDatas.erase( aIt );
+}
+
+void ScTextWnd::StyleUpdated()
+{
+ ImplInitSettings();
+ CustomWidgetController::Invalidate();
+}
+
+void ScTextWnd::TextGrabFocus()
+{
+ GrabFocus();
+}
+
+// Position window
+ScPosWnd::ScPosWnd(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "modules/scalc/ui/posbox.ui", "PosBox")
+ , m_xWidget(m_xBuilder->weld_combo_box("pos_window"))
+ , m_nAsyncGetFocusId(nullptr)
+ , nTipVisible(nullptr)
+ , bFormulaMode(false)
+{
+ InitControlBase(m_xWidget.get());
+
+ // Use calculation according to tdf#132338 to align combobox width to width of fontname combobox within formatting toolbar;
+ // formatting toolbar is placed above formulabar when using multiple toolbars typically
+
+ m_xWidget->set_entry_width_chars(1);
+ Size aSize(LogicToPixel(Size(POSITION_COMBOBOX_WIDTH * 4, 0), MapMode(MapUnit::MapAppFont)));
+ m_xWidget->set_size_request(aSize.Width(), -1);
+ SetSizePixel(m_xContainer->get_preferred_size());
+
+ FillRangeNames();
+
+ StartListening( *SfxGetpApp() ); // For Navigator rangename updates
+
+ m_xWidget->connect_key_press(LINK(this, ScPosWnd, KeyInputHdl));
+ m_xWidget->connect_entry_activate(LINK(this, ScPosWnd, ActivateHdl));
+ m_xWidget->connect_changed(LINK(this, ScPosWnd, ModifyHdl));
+ m_xWidget->connect_focus_in(LINK(this, ScPosWnd, FocusInHdl));
+ m_xWidget->connect_focus_out(LINK(this, ScPosWnd, FocusOutHdl));
+}
+
+ScPosWnd::~ScPosWnd()
+{
+ disposeOnce();
+}
+
+void ScPosWnd::dispose()
+{
+ EndListening( *SfxGetpApp() );
+
+ HideTip();
+
+ if (m_nAsyncGetFocusId)
+ {
+ Application::RemoveUserEvent(m_nAsyncGetFocusId);
+ m_nAsyncGetFocusId = nullptr;
+ }
+ m_xWidget.reset();
+
+ InterimItemWindow::dispose();
+}
+
+void ScPosWnd::SetFormulaMode( bool bSet )
+{
+ if ( bSet != bFormulaMode )
+ {
+ bFormulaMode = bSet;
+
+ if ( bSet )
+ FillFunctions();
+ else
+ FillRangeNames();
+
+ HideTip();
+ }
+}
+
+void ScPosWnd::SetPos( const OUString& rPosStr )
+{
+ if ( aPosStr != rPosStr )
+ {
+ aPosStr = rPosStr;
+ m_xWidget->set_entry_text(aPosStr);
+ }
+}
+
+// static
+OUString ScPosWnd::createLocalRangeName(std::u16string_view rName, std::u16string_view rTableName)
+{
+ return OUString::Concat(rName) + " (" + rTableName + ")";
+}
+
+void ScPosWnd::FillRangeNames()
+{
+ m_xWidget->clear();
+ m_xWidget->freeze();
+
+ SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if ( auto pDocShell = dynamic_cast<ScDocShell*>( pObjSh) )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ m_xWidget->append_text(ScResId(STR_MANAGE_NAMES));
+ m_xWidget->append_separator("separator");
+
+ ScRange aDummy;
+ std::set<OUString> aSet;
+ ScRangeName* pRangeNames = rDoc.GetRangeName();
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (rEntry.second->IsValidReference(aDummy))
+ aSet.insert(rEntry.second->GetName());
+ }
+ for (SCTAB i = 0; i < rDoc.GetTableCount(); ++i)
+ {
+ ScRangeName* pLocalRangeName = rDoc.GetRangeName(i);
+ if (pLocalRangeName && !pLocalRangeName->empty())
+ {
+ OUString aTableName;
+ rDoc.GetName(i, aTableName);
+ for (const auto& rEntry : *pLocalRangeName)
+ {
+ if (rEntry.second->IsValidReference(aDummy))
+ aSet.insert(createLocalRangeName(rEntry.second->GetName(), aTableName));
+ }
+ }
+ }
+
+ for (const auto& rItem : aSet)
+ {
+ m_xWidget->append_text(rItem);
+ }
+ }
+ m_xWidget->thaw();
+ m_xWidget->set_entry_text(aPosStr);
+}
+
+void ScPosWnd::FillFunctions()
+{
+ m_xWidget->clear();
+ m_xWidget->freeze();
+
+ OUString aFirstName;
+ const ScAppOptions& rOpt = SC_MOD()->GetAppOptions();
+ sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount();
+ const sal_uInt16* pMRUList = rOpt.GetLRUFuncList();
+ if (pMRUList)
+ {
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ sal_uInt32 nListCount = pFuncList->GetCount();
+ for (sal_uInt16 i=0; i<nMRUCount; i++)
+ {
+ sal_uInt16 nId = pMRUList[i];
+ for (sal_uInt32 j=0; j<nListCount; j++)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction( j );
+ if ( pDesc->nFIndex == nId && pDesc->mxFuncName )
+ {
+ m_xWidget->append_text(*pDesc->mxFuncName);
+ if (aFirstName.isEmpty())
+ aFirstName = *pDesc->mxFuncName;
+ break; // Stop searching
+ }
+ }
+ }
+ }
+
+ //! Re-add entry "Other..." for Function AutoPilot if it can work with text that
+ // has been entered so far
+
+ // m_xWidget->append_text(ScResId(STR_FUNCTIONLIST_MORE));
+
+ m_xWidget->thaw();
+ m_xWidget->set_entry_text(aFirstName);
+}
+
+void ScPosWnd::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( bFormulaMode )
+ return;
+
+ const SfxHintId nHintId = rHint.GetId();
+ // Does the list of range names need updating?
+ if (nHintId == SfxHintId::ThisIsAnSfxEventHint)
+ {
+ SfxEventHintId nEventId = static_cast<const SfxEventHint&>(rHint).GetEventId();
+ if ( nEventId == SfxEventHintId::ActivateDoc )
+ FillRangeNames();
+ }
+ else
+ {
+ if (nHintId == SfxHintId::ScAreasChanged || nHintId == SfxHintId::ScNavigatorUpdateAll)
+ FillRangeNames();
+ }
+}
+
+void ScPosWnd::HideTip()
+{
+ if (nTipVisible)
+ {
+ Help::HidePopover(this, nTipVisible);
+ nTipVisible = nullptr;
+ }
+}
+
+static ScNameInputType lcl_GetInputType( const OUString& rText )
+{
+ ScNameInputType eRet = SC_NAME_INPUT_BAD_NAME; // the more general error
+
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ ScAddress::Details aDetails( rDoc.GetAddressConvention());
+
+ // test in same order as in SID_CURRENTCELL execute
+
+ ScRange aRange;
+ ScAddress aAddress;
+ SCTAB nNameTab;
+ sal_Int32 nNumeric;
+
+ // From the context we know that when testing for a range name
+ // sheet-local scope names have " (sheetname)" appended and global
+ // names don't and can't contain ')', so we can force one or the other.
+ const RutlNameScope eNameScope =
+ ((!rText.isEmpty() && rText[rText.getLength()-1] == ')') ? RUTL_NAMES_LOCAL : RUTL_NAMES_GLOBAL);
+
+ if (rText == ScResId(STR_MANAGE_NAMES))
+ eRet = SC_MANAGE_NAMES;
+ else if ( aRange.Parse( rText, rDoc, aDetails ) & ScRefFlags::VALID )
+ eRet = SC_NAME_INPUT_RANGE;
+ else if ( aAddress.Parse( rText, rDoc, aDetails ) & ScRefFlags::VALID )
+ eRet = SC_NAME_INPUT_CELL;
+ else if ( ScRangeUtil::MakeRangeFromName( rText, rDoc, nTab, aRange, eNameScope, aDetails ) )
+ {
+ eRet = ((eNameScope == RUTL_NAMES_LOCAL) ? SC_NAME_INPUT_NAMEDRANGE_LOCAL :
+ SC_NAME_INPUT_NAMEDRANGE_GLOBAL);
+ }
+ else if ( ScRangeUtil::MakeRangeFromName( rText, rDoc, nTab, aRange, RUTL_DBASE, aDetails ) )
+ eRet = SC_NAME_INPUT_DATABASE;
+ else if ( comphelper::string::isdigitAsciiString( rText ) &&
+ ( nNumeric = rText.toInt32() ) > 0 && nNumeric <= rDoc.MaxRow()+1 )
+ eRet = SC_NAME_INPUT_ROW;
+ else if ( rDoc.GetTable( rText, nNameTab ) )
+ eRet = SC_NAME_INPUT_SHEET;
+ else if (ScRangeData::IsNameValid(rText, rDoc)
+ == ScRangeData::IsNameValidType::NAME_VALID) // nothing found, create new range?
+ {
+ if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ eRet = SC_NAME_INPUT_DEFINE;
+ else
+ eRet = SC_NAME_INPUT_BAD_SELECTION;
+ }
+ else
+ eRet = SC_NAME_INPUT_BAD_NAME;
+ }
+
+ return eRet;
+}
+
+IMPL_LINK_NOARG(ScPosWnd, ModifyHdl, weld::ComboBox&, void)
+{
+ HideTip();
+
+ if (m_xWidget->changed_by_direct_pick())
+ {
+ DoEnter();
+ return;
+ }
+
+ if (bFormulaMode)
+ return;
+
+ // determine the action that would be taken for the current input
+
+ ScNameInputType eType = lcl_GetInputType(m_xWidget->get_active_text()); // uses current view
+ TranslateId pStrId;
+ switch ( eType )
+ {
+ case SC_NAME_INPUT_CELL:
+ pStrId = STR_NAME_INPUT_CELL;
+ break;
+ case SC_NAME_INPUT_RANGE:
+ case SC_NAME_INPUT_NAMEDRANGE_LOCAL:
+ case SC_NAME_INPUT_NAMEDRANGE_GLOBAL:
+ pStrId = STR_NAME_INPUT_RANGE; // named range or range reference
+ break;
+ case SC_NAME_INPUT_DATABASE:
+ pStrId = STR_NAME_INPUT_DBRANGE;
+ break;
+ case SC_NAME_INPUT_ROW:
+ pStrId = STR_NAME_INPUT_ROW;
+ break;
+ case SC_NAME_INPUT_SHEET:
+ pStrId = STR_NAME_INPUT_SHEET;
+ break;
+ case SC_NAME_INPUT_DEFINE:
+ pStrId = STR_NAME_INPUT_DEFINE;
+ break;
+ default:
+ // other cases (error): no tip help
+ break;
+ }
+
+ if (!pStrId)
+ return;
+
+ // show the help tip at the text cursor position
+ Point aPos;
+ vcl::Cursor* pCur = GetCursor();
+ if (pCur)
+ aPos = LogicToPixel( pCur->GetPos() );
+ aPos = OutputToScreenPixel( aPos );
+ tools::Rectangle aRect( aPos, aPos );
+
+ OUString aText = ScResId(pStrId);
+ QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
+ nTipVisible = Help::ShowPopover(this, aRect, aText, nAlign);
+}
+
+void ScPosWnd::DoEnter()
+{
+ bool bOpenManageNamesDialog = false;
+ OUString aText = m_xWidget->get_active_text();
+ if ( !aText.isEmpty() )
+ {
+ if ( bFormulaMode )
+ {
+ ScModule* pScMod = SC_MOD();
+ if ( aText == ScResId(STR_FUNCTIONLIST_MORE) )
+ {
+ // Function AutoPilot
+ //! Continue working with the text entered so far
+
+ //! new method at ScModule to query if function autopilot is open
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if ( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
+ pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ }
+ else
+ {
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
+ if (pHdl)
+ pHdl->InsertFunction( aText );
+ }
+ }
+ else
+ {
+ // depending on the input, select something or create a new named range
+
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScNameInputType eType = lcl_GetInputType( aText );
+ if ( eType == SC_NAME_INPUT_BAD_NAME || eType == SC_NAME_INPUT_BAD_SELECTION )
+ {
+ TranslateId pId = (eType == SC_NAME_INPUT_BAD_NAME) ? STR_NAME_ERROR_NAME : STR_NAME_ERROR_SELECTION;
+ pViewSh->ErrorMessage(pId);
+ }
+ else if ( eType == SC_NAME_INPUT_DEFINE )
+ {
+ ScRangeName* pNames = rDoc.GetRangeName();
+ ScRange aSelection;
+ if ( pNames && !pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aText)) &&
+ (rViewData.GetSimpleArea( aSelection ) == SC_MARK_SIMPLE) )
+ {
+ ScRangeName aNewRanges( *pNames );
+ ScAddress aCursor( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+ OUString aContent(aSelection.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ ScRangeData* pNew = new ScRangeData( rDoc, aText, aContent, aCursor );
+ if ( aNewRanges.insert(pNew) )
+ {
+ pDocShell->GetDocFunc().ModifyRangeNames( aNewRanges );
+ pViewSh->UpdateInputHandler(true);
+ }
+ }
+ }
+ else if (eType == SC_MANAGE_NAMES)
+ {
+ // dialog is only set below after calling 'ReleaseFocus_Impl' to ensure it gets focus
+ bOpenManageNamesDialog = true;
+ }
+ else
+ {
+ bool bForceGlobalName = false;
+ // for all selection types, execute the SID_CURRENTCELL slot.
+ if (eType == SC_NAME_INPUT_CELL || eType == SC_NAME_INPUT_RANGE)
+ {
+ // Note that SID_CURRENTCELL always expects address to
+ // be in Calc A1 format. Convert the text.
+ ScRange aRange(0,0, rViewData.GetTabNo());
+ aRange.ParseAny(aText, rDoc, rDoc.GetAddressConvention());
+ aText = aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, ::formula::FormulaGrammar::CONV_OOO);
+ }
+ else if (eType == SC_NAME_INPUT_NAMEDRANGE_GLOBAL)
+ {
+ bForceGlobalName = true;
+ }
+
+ SfxStringItem aPosItem( SID_CURRENTCELL, aText );
+ SfxBoolItem aUnmarkItem( FN_PARAM_1, true ); // remove existing selection
+ // FN_PARAM_2 reserved for AlignToCursor
+ SfxBoolItem aForceGlobalName( FN_PARAM_3, bForceGlobalName );
+
+ pViewSh->GetViewData().GetDispatcher().ExecuteList( SID_CURRENTCELL,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aPosItem, &aUnmarkItem, &aForceGlobalName });
+ }
+ }
+ }
+ }
+ else
+ m_xWidget->set_entry_text(aPosStr);
+
+ ReleaseFocus_Impl();
+
+ if (bOpenManageNamesDialog)
+ {
+ const sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId();
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ assert(pViewSh);
+ SfxViewFrame& rViewFrm = pViewSh->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+ }
+}
+
+IMPL_LINK_NOARG(ScPosWnd, ActivateHdl, weld::ComboBox&, bool)
+{
+ DoEnter();
+ return true;
+}
+
+IMPL_LINK(ScPosWnd, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled = true;
+
+ switch (rKEvt.GetKeyCode().GetCode())
+ {
+ case KEY_RETURN:
+ bHandled = ActivateHdl(*m_xWidget);
+ break;
+ case KEY_ESCAPE:
+ if (nTipVisible)
+ {
+ // escape when the tip help is shown: only hide the tip
+ HideTip();
+ }
+ else
+ {
+ if (!bFormulaMode)
+ m_xWidget->set_entry_text(aPosStr);
+ ReleaseFocus_Impl();
+ }
+ break;
+ default:
+ bHandled = false;
+ break;
+ }
+
+ return bHandled || ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK_NOARG(ScPosWnd, OnAsyncGetFocus, void*, void)
+{
+ m_nAsyncGetFocusId = nullptr;
+ m_xWidget->select_entry_region(0, -1);
+}
+
+IMPL_LINK_NOARG(ScPosWnd, FocusInHdl, weld::Widget&, void)
+{
+ if (m_nAsyncGetFocusId)
+ return;
+ // do it async to defeat entry in combobox having its own ideas about the focus
+ m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, ScPosWnd, OnAsyncGetFocus));
+}
+
+IMPL_LINK_NOARG(ScPosWnd, FocusOutHdl, weld::Widget&, void)
+{
+ if (m_nAsyncGetFocusId)
+ {
+ Application::RemoveUserEvent(m_nAsyncGetFocusId);
+ m_nAsyncGetFocusId = nullptr;
+ }
+
+ HideTip();
+}
+
+void ScPosWnd::ReleaseFocus_Impl()
+{
+ HideTip();
+
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( dynamic_cast<ScTabViewShell*>( pCurSh ) );
+ if ( pHdl && pHdl->IsTopMode() )
+ {
+ // Focus back in input row?
+ ScInputWindow* pInputWin = pHdl->GetInputWindow();
+ if (pInputWin)
+ {
+ pInputWin->TextGrabFocus();
+ return;
+ }
+ }
+
+ // Set focus to active View
+ if ( pCurSh )
+ {
+ vcl::Window* pShellWnd = pCurSh->GetWindow();
+
+ if ( pShellWnd )
+ pShellWnd->GrabFocus();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/lnktrans.cxx b/sc/source/ui/app/lnktrans.cxx
new file mode 100644
index 0000000000..1ff48e7fe7
--- /dev/null
+++ b/sc/source/ui/app/lnktrans.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 <svl/urlbmk.hxx>
+
+#include <lnktrans.hxx>
+#include <scmod.hxx>
+
+using namespace com::sun::star;
+
+ScLinkTransferObj::ScLinkTransferObj()
+{
+}
+
+ScLinkTransferObj::~ScLinkTransferObj()
+{
+}
+
+void ScLinkTransferObj::SetLinkURL( const OUString& rURL, const OUString& rText )
+{
+ aLinkURL = rURL;
+ aLinkText = rText;
+}
+
+void ScLinkTransferObj::AddSupportedFormats()
+{
+ if ( !aLinkURL.isEmpty() )
+ {
+ // TransferableHelper::SetINetBookmark formats
+
+ AddFormat( SotClipboardFormatId::SOLK );
+ AddFormat( SotClipboardFormatId::STRING );
+ AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR );
+ AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK );
+ AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::FILECONTENT );
+ }
+}
+
+bool ScLinkTransferObj::GetData(
+ const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+{
+ bool bOK = false;
+ if ( !aLinkURL.isEmpty() )
+ {
+ INetBookmark aBmk( aLinkURL, aLinkText );
+ bOK = SetINetBookmark( aBmk, rFlavor );
+ }
+ return bOK;
+}
+
+void ScLinkTransferObj::DragFinished( sal_Int8 nDropAction )
+{
+ ScModule* pScMod = SC_MOD();
+ pScMod->ResetDragObject();
+
+ TransferDataContainer::DragFinished( nDropAction );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/msgpool.cxx b/sc/source/ui/app/msgpool.cxx
new file mode 100644
index 0000000000..227dbce456
--- /dev/null
+++ b/sc/source/ui/app/msgpool.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <sc.hrc>
+#include <docpool.hxx>
+#include <msgpool.hxx>
+
+SfxItemInfo const aMsgItemInfos[] =
+{
+ // _nSID, _bNeedsPoolRegistration, _bShareable
+ { 0, false, true }, // SCITEM_STRING
+ { 0, false, true }, // SCITEM_SEARCHDATA - stop using this!
+ { SID_SORT, false, true }, // SCITEM_SORTDATA
+ { SID_QUERY, false, true }, // SCITEM_QUERYDATA
+ { SID_SUBTOTALS, false, true }, // SCITEM_SUBTDATA
+ { SID_CONSOLIDATE, false, true }, // SCITEM_CONSOLIDATEDATA
+ { SID_PIVOT_TABLE, false, true }, // SCITEM_PIVOTDATA
+ { SID_SOLVE, false, true }, // SCITEM_SOLVEDATA
+ { SID_SCUSERLISTS, false, true }, // SCITEM_USERLIST
+ { 0, true, false } // SCITEM_CONDFORMATDLGDATA
+};
+
+ScMessagePool::ScMessagePool()
+ : SfxItemPool ( "ScMessagePool",
+ MSGPOOL_START, MSGPOOL_END,
+ aMsgItemInfos, nullptr ),
+
+ aGlobalStringItem ( SfxStringItem ( SCITEM_STRING, OUString() ) ),
+ aGlobalSearchItem ( SvxSearchItem ( SCITEM_SEARCHDATA ) ),
+ aGlobalSortItem ( ScSortItem ( SCITEM_SORTDATA, nullptr ) ),
+ aGlobalQueryItem ( ScQueryItem ( SCITEM_QUERYDATA, nullptr, nullptr ) ),
+ aGlobalSubTotalItem ( ScSubTotalItem ( SCITEM_SUBTDATA, nullptr, nullptr ) ),
+ aGlobalConsolidateItem ( ScConsolidateItem ( SCITEM_CONSOLIDATEDATA, nullptr ) ),
+ aGlobalPivotItem ( ScPivotItem ( SCITEM_PIVOTDATA, nullptr, nullptr, false ) ),
+ aGlobalSolveItem ( ScSolveItem ( SCITEM_SOLVEDATA, nullptr ) ),
+ aGlobalUserListItem ( ScUserListItem ( SCITEM_USERLIST ) ),
+ aCondFormatDlgItem ( ScCondFormatDlgItem ( nullptr, -1, false ) ),
+
+ mvPoolDefaults(MSGPOOL_END - MSGPOOL_START + 1),
+ pDocPool(new ScDocumentPool)
+{
+ mvPoolDefaults[SCITEM_STRING - MSGPOOL_START] = &aGlobalStringItem;
+ mvPoolDefaults[SCITEM_SEARCHDATA - MSGPOOL_START] = &aGlobalSearchItem;
+ mvPoolDefaults[SCITEM_SORTDATA - MSGPOOL_START] = &aGlobalSortItem;
+ mvPoolDefaults[SCITEM_QUERYDATA - MSGPOOL_START] = &aGlobalQueryItem;
+ mvPoolDefaults[SCITEM_SUBTDATA - MSGPOOL_START] = &aGlobalSubTotalItem;
+ mvPoolDefaults[SCITEM_CONSOLIDATEDATA - MSGPOOL_START] = &aGlobalConsolidateItem;
+ mvPoolDefaults[SCITEM_PIVOTDATA - MSGPOOL_START] = &aGlobalPivotItem;
+ mvPoolDefaults[SCITEM_SOLVEDATA - MSGPOOL_START] = &aGlobalSolveItem;
+ mvPoolDefaults[SCITEM_USERLIST - MSGPOOL_START] = &aGlobalUserListItem;
+ mvPoolDefaults[SCITEM_CONDFORMATDLGDATA - MSGPOOL_START] = &aCondFormatDlgItem;
+
+ SetDefaults( &mvPoolDefaults );
+
+ SetSecondaryPool( pDocPool.get() );
+}
+
+ScMessagePool::~ScMessagePool()
+{
+ Delete();
+ SetSecondaryPool( nullptr ); // before deleting defaults (accesses defaults)
+
+ for ( sal_uInt16 i=0; i <= MSGPOOL_END-MSGPOOL_START; i++ )
+ ClearRefCount( *mvPoolDefaults[i] );
+}
+
+MapUnit ScMessagePool::GetMetric( sal_uInt16 nWhich ) const
+{
+ // Own attributes: Twips, everything else 1/100 mm
+ if ( nWhich >= ATTR_STARTINDEX && nWhich <= ATTR_ENDINDEX )
+ return MapUnit::MapTwip;
+ else
+ return MapUnit::Map100thMM;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/rfindlst.cxx b/sc/source/ui/app/rfindlst.cxx
new file mode 100644
index 0000000000..ba17bf006e
--- /dev/null
+++ b/sc/source/ui/app/rfindlst.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 <rfindlst.hxx>
+#include <tools/debug.hxx>
+#include <utility>
+
+#define SC_RANGECOLORS 8
+
+const Color aColNames[SC_RANGECOLORS] =
+ { COL_LIGHTBLUE, COL_LIGHTRED, COL_LIGHTMAGENTA, COL_GREEN,
+ COL_BLUE, COL_RED, COL_MAGENTA, COL_BROWN };
+
+ScRangeFindList::ScRangeFindList(OUString aName) :
+ aDocName(std::move( aName )),
+ bHidden( false ),
+ nIndexColor( 0 )
+{
+}
+
+Color ScRangeFindList::Insert( const ScRangeFindData &rNew )
+{
+ auto it = std::find_if(maEntries.begin(), maEntries.end(),
+ [&rNew](const ScRangeFindData& rEntry) { return rEntry.aRef == rNew.aRef; });
+ ScRangeFindData insertData(rNew);
+ insertData.nColor = ( it != maEntries.end() ? it->nColor :
+ ScRangeFindList::GetColorName( maEntries.size() ) );
+ maEntries.push_back(insertData);
+ nIndexColor = maEntries.size() - 1;
+ return insertData.nColor;
+}
+
+Color ScRangeFindList::GetColorName( const size_t nIndex )
+{
+ return aColNames[nIndex % SC_RANGECOLORS];
+}
+
+Color ScRangeFindList::FindColor( const ScRange& rRef, const size_t nIndex )
+{
+ sal_Int32 nOldCntr = 0;
+ sal_Int32 nNewCntr = 0;
+ Color nOldColor(0);
+ Color nNewColor(0);
+
+ DBG_ASSERT( (nIndex < maEntries.size()), "nIndex out of range!" );
+
+ nOldColor = maEntries[nIndex].nColor;
+ nNewColor = ScRangeFindList::GetColorName( nIndex );
+
+ std::vector<ScRangeFindData>::iterator it=maEntries.begin();
+ for( ;it!=maEntries.end(); ++it)
+ {
+ if(it->aRef == rRef)
+ break;
+
+ if (it->nColor == nOldColor )
+ nOldCntr++;
+
+ if (it->nColor == nNewColor )
+ nNewCntr++;
+ }
+
+ if ( it != maEntries.end() )
+ return it->nColor;
+
+ if ( nOldCntr == 1 )
+ return nOldColor;
+
+ if ( nNewCntr > 0 )
+ return ScRangeFindList::GetColorName( ++nIndexColor );
+
+ return nNewColor;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/scdll.cxx b/sc/source/ui/app/scdll.cxx
new file mode 100644
index 0000000000..f2fbe8d74c
--- /dev/null
+++ b/sc/source/ui/app/scdll.cxx
@@ -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 .
+ */
+
+#include <config_features.h>
+
+#include <svx/fmobjfac.hxx>
+#include <svx/objfac3d.hxx>
+
+#include <comphelper/lok.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
+#include <avmedia/mediatoolbox.hxx>
+#include <NumberFormatControl.hxx>
+
+#include <unotools/resmgr.hxx>
+
+#include <scmod.hxx>
+#include <scresid.hxx>
+#include <sc.hrc>
+
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <prevwsh.hxx>
+#include <drawsh.hxx>
+#include <drformsh.hxx>
+#include <drtxtob.hxx>
+#include <editsh.hxx>
+#include <pivotsh.hxx>
+#include <auditsh.hxx>
+#include <cellsh.hxx>
+#include <oleobjsh.hxx>
+#include <chartsh.hxx>
+#include <graphsh.hxx>
+#include <mediash.hxx>
+#include <pgbrksh.hxx>
+#include <scdll.hxx>
+#include <SparklineShell.hxx>
+
+#include <appoptio.hxx>
+#include <searchresults.hxx>
+
+// Controls
+
+#include <svx/tbxctl.hxx>
+#include <svx/fillctrl.hxx>
+#include <svx/linectrl.hxx>
+//#include <svx/tbcontrl.hxx>
+#include <svx/selctrl.hxx>
+#include <svx/insctrl.hxx>
+#include <svx/zoomctrl.hxx>
+#include <svx/modctrl.hxx>
+#include <svx/pszctrl.hxx>
+#include <svx/grafctrl.hxx>
+#include <svx/clipboardctl.hxx>
+#include <svx/formatpaintbrushctrl.hxx>
+#include <tbzoomsliderctrl.hxx>
+#include <svx/zoomsliderctrl.hxx>
+
+#include <svx/xmlsecctrl.hxx>
+// Child windows
+#include <reffact.hxx>
+#include <navipi.hxx>
+#include <inputwin.hxx>
+#include <spelldialog.hxx>
+#include <svx/fontwork.hxx>
+#include <svx/srchdlg.hxx>
+#include <svx/hyperdlg.hxx>
+#include <svx/imapdlg.hxx>
+
+#include <filter.hxx>
+#include <scabstdlg.hxx>
+
+OUString ScResId(TranslateId aId)
+{
+ return Translate::get(aId, SC_MOD()->GetResLocale());
+}
+
+OUString ScResId(TranslateNId aContextSingularPlural, int nCardinality)
+{
+ return Translate::nget(aContextSingularPlural, nCardinality, SC_MOD()->GetResLocale());
+}
+
+void ScDLL::Init()
+{
+ if ( SfxApplication::GetModule(SfxToolsModule::Calc) ) // Module already active
+ return;
+
+ auto pUniqueModule = std::make_unique<ScModule>(&ScDocShell::Factory());
+ ScModule* pMod = pUniqueModule.get();
+ SfxApplication::SetModule(SfxToolsModule::Calc, std::move(pUniqueModule));
+
+ ScDocShell::Factory().SetDocumentServiceName( "com.sun.star.sheet.SpreadsheetDocument" );
+
+ // Not until the ResManager is initialized
+ // The AppOptions must be initialized not until after ScGlobal::Init
+ ScGlobal::Init();
+
+ // register your view-factories here
+ ScTabViewShell ::RegisterFactory(SFX_INTERFACE_SFXAPP);
+ ScPreviewShell ::RegisterFactory(SFX_INTERFACE_SFXDOCSH);
+
+ // register your shell-interfaces here
+ ScModule ::RegisterInterface(pMod);
+ ScDocShell ::RegisterInterface(pMod);
+ ScTabViewShell ::RegisterInterface(pMod);
+ ScPreviewShell ::RegisterInterface(pMod);
+ ScDrawShell ::RegisterInterface(pMod);
+ ScDrawFormShell ::RegisterInterface(pMod);
+ ScDrawTextObjectBar ::RegisterInterface(pMod);
+ ScEditShell ::RegisterInterface(pMod);
+ ScPivotShell ::RegisterInterface(pMod);
+ sc::SparklineShell ::RegisterInterface(pMod);
+ ScAuditingShell ::RegisterInterface(pMod);
+ ScFormatShell ::RegisterInterface(pMod);
+ ScCellShell ::RegisterInterface(pMod);
+ ScOleObjectShell ::RegisterInterface(pMod);
+ ScChartShell ::RegisterInterface(pMod);
+ ScGraphicShell ::RegisterInterface(pMod);
+ ScMediaShell ::RegisterInterface(pMod);
+ ScPageBreakShell ::RegisterInterface(pMod);
+
+ // Own Controller
+ ScZoomSliderControl ::RegisterControl(SID_PREVIEW_SCALINGFACTOR, pMod);
+
+ // SvxToolboxController
+ SvxTbxCtlDraw ::RegisterControl(SID_INSERT_DRAW, pMod);
+ SvxFillToolBoxControl ::RegisterControl(0, pMod);
+ SvxLineWidthToolBoxControl ::RegisterControl(0, pMod);
+ SvxClipBoardControl ::RegisterControl(SID_PASTE, pMod );
+ SvxClipBoardControl ::RegisterControl(SID_PASTE_UNFORMATTED, pMod );
+ svx::FormatPaintBrushToolBoxControl::RegisterControl(SID_FORMATPAINTBRUSH, pMod );
+ sc::ScNumberFormatControl ::RegisterControl(SID_NUMBER_TYPE_FORMAT, pMod );
+
+ SvxGrafModeToolBoxControl ::RegisterControl(SID_ATTR_GRAF_MODE, pMod);
+ SvxGrafRedToolBoxControl ::RegisterControl(SID_ATTR_GRAF_RED, pMod);
+ SvxGrafGreenToolBoxControl ::RegisterControl(SID_ATTR_GRAF_GREEN, pMod);
+ SvxGrafBlueToolBoxControl ::RegisterControl(SID_ATTR_GRAF_BLUE, pMod);
+ SvxGrafLuminanceToolBoxControl ::RegisterControl(SID_ATTR_GRAF_LUMINANCE, pMod);
+ SvxGrafContrastToolBoxControl ::RegisterControl(SID_ATTR_GRAF_CONTRAST, pMod);
+ SvxGrafGammaToolBoxControl ::RegisterControl(SID_ATTR_GRAF_GAMMA, pMod);
+ SvxGrafTransparenceToolBoxControl::RegisterControl(SID_ATTR_GRAF_TRANSPARENCE, pMod);
+
+ // Media Controller
+#if HAVE_FEATURE_AVMEDIA
+ ::avmedia::MediaToolBoxControl::RegisterControl( SID_AVMEDIA_TOOLBOX, pMod );
+#endif
+
+ // Common SFX Controller
+ sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pMod);
+ DevelopmentToolChildWindow::RegisterChildWindow(false, pMod);
+
+ // SvxStatusBar Controller
+ SvxInsertStatusBarControl ::RegisterControl(SID_ATTR_INSERT, pMod);
+ SvxSelectionModeControl ::RegisterControl(SID_STATUS_SELMODE, pMod);
+ SvxZoomStatusBarControl ::RegisterControl(SID_ATTR_ZOOM, pMod);
+ SvxZoomSliderControl ::RegisterControl(SID_ATTR_ZOOMSLIDER, pMod);
+ SvxModifyControl ::RegisterControl(SID_DOC_MODIFIED, pMod);
+ XmlSecStatusBarControl ::RegisterControl( SID_SIGNATURE, pMod );
+
+ SvxPosSizeStatusBarControl ::RegisterControl(SID_ATTR_SIZE, pMod);
+
+ // Child Windows
+
+ ScInputWindowWrapper ::RegisterChildWindow(true, pMod, SfxChildWindowFlags::TASK|SfxChildWindowFlags::FORCEDOCK);
+ ScSolverDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScOptSolverDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScXMLSourceDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScNameDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScNameDefDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScPivotLayoutWrapper ::RegisterChildWindow(false, pMod);
+ ScTabOpDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScFilterDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScSpecialFilterDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScDbNameDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScConsolidateDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScPrintAreasDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScColRowNameRangesDlgWrapper::RegisterChildWindow(false, pMod);
+ ScFormulaDlgWrapper ::RegisterChildWindow(false, pMod);
+
+ ScRandomNumberGeneratorDialogWrapper::RegisterChildWindow(false, pMod);
+ ScSamplingDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScDescriptiveStatisticsDialogWrapper::RegisterChildWindow(false, pMod);
+ ScAnalysisOfVarianceDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScCorrelationDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScCovarianceDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScExponentialSmoothingDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScMovingAverageDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScRegressionDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScTTestDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScFTestDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScZTestDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScChiSquareTestDialogWrapper ::RegisterChildWindow(false, pMod);
+ ScFourierAnalysisDialogWrapper ::RegisterChildWindow(false, pMod);
+ sc::SparklineDialogWrapper ::RegisterChildWindow(false, pMod);
+ sc::SparklineDataRangeDialogWrapper ::RegisterChildWindow(false, pMod);
+ sc::ConditionalFormatEasyDialogWrapper ::RegisterChildWindow(false, pMod);
+
+ // Redlining Window
+ ScAcceptChgDlgWrapper ::RegisterChildWindow(false, pMod);
+ ScSimpleRefDlgWrapper ::RegisterChildWindow(false, pMod, SfxChildWindowFlags::ALWAYSAVAILABLE|SfxChildWindowFlags::NEVERHIDE );
+ ScHighlightChgDlgWrapper ::RegisterChildWindow(false, pMod);
+
+ SvxSearchDialogWrapper ::RegisterChildWindow(false, pMod);
+ SvxHlinkDlgWrapper ::RegisterChildWindow(false, pMod);
+ SvxFontWorkChildWindow ::RegisterChildWindow(false, pMod);
+ SvxIMapDlgChildWindow ::RegisterChildWindow(false, pMod);
+ ScSpellDialogChildWindow::RegisterChildWindow(
+ false, pMod, comphelper::LibreOfficeKit::isActive() ? SfxChildWindowFlags::NEVERCLONE
+ : SfxChildWindowFlags::NONE);
+
+ ScValidityRefChildWin::RegisterChildWindow(false, pMod);
+ sc::SearchResultsDlgWrapper::RegisterChildWindow(false, pMod);
+ ScCondFormatDlgWrapper::RegisterChildWindow(false, pMod);
+
+ ScNavigatorWrapper::RegisterChildWindow(false, pMod, SfxChildWindowFlags::NEVERHIDE);
+
+ // Add 3DObject Factory
+ E3dObjFactory();
+
+ // Add css::form::component::FormObject Factory
+ FmFormObjFactory();
+
+ pMod->PutItem( SfxUInt16Item( SID_ATTR_METRIC, sal::static_int_cast<sal_uInt16>(pMod->GetAppOptions().GetAppMetric()) ) );
+
+ // StarOne Services are now handled in the registry
+}
+
+#ifndef DISABLE_DYNLOADING
+
+extern "C" SAL_DLLPUBLIC_EXPORT
+void lok_preload_hook()
+{
+ // scfilt
+ ScFormatFilter::Get();
+ // scui
+ ScAbstractDialogFactory::Create();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/scmod.cxx b/sc/source/ui/app/scmod.cxx
new file mode 100644
index 0000000000..3fa3a7849b
--- /dev/null
+++ b/sc/source/ui/app/scmod.cxx
@@ -0,0 +1,2354 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/ui/dialogs/XSLTFilterDialog.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <scitems.hxx>
+#include <sfx2/app.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/outliner.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objface.hxx>
+
+#include <IAnyRefDialog.hxx>
+
+#include <svtools/ehdl.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <unotools/useroptions.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/printer.hxx>
+#include <editeng/langitem.hxx>
+#include <svtools/colorcfg.hxx>
+
+#include <svl/whiter.hxx>
+#include <svx/dialogs.hrc>
+#include <svl/inethist.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svxerr.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <editeng/unolingu.hxx>
+#include <unotools/lingucfg.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/linguistic2/XThesaurus.hpp>
+#include <ooo/vba/XSinkCaller.hpp>
+
+#include <scmod.hxx>
+#include <global.hxx>
+#include <viewopti.hxx>
+#include <docoptio.hxx>
+#include <appoptio.hxx>
+#include <defaultsoptions.hxx>
+#include <formulaopt.hxx>
+#include <inputopt.hxx>
+#include <printopt.hxx>
+#include <navicfg.hxx>
+#include <addincfg.hxx>
+#include <tabvwsh.hxx>
+#include <prevwsh.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <uiitems.hxx>
+#include <sc.hrc>
+#include <scerrors.hrc>
+#include <scstyles.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <bitmaps.hlst>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <msgpool.hxx>
+#include <detfunc.hxx>
+#include <preview.hxx>
+#include <dragdata.hxx>
+#include <markdata.hxx>
+#include <transobj.hxx>
+#include <funcdesc.hxx>
+
+#define ShellClass_ScModule
+#include <scslots.hxx>
+
+#include <scabstdlg.hxx>
+#include <formula/errorcodes.hxx>
+#include <documentlinkmgr.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <sfx2/lokhelper.hxx>
+
+#define SC_IDLE_MIN 150
+#define SC_IDLE_MAX 3000
+#define SC_IDLE_STEP 75
+#define SC_IDLE_COUNT 50
+
+static sal_uInt16 nIdleCount = 0;
+
+SFX_IMPL_INTERFACE(ScModule, SfxShell)
+
+void ScModule::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer,
+ ToolbarId::Objectbar_App);
+
+ GetStaticInterface()->RegisterStatusBar(StatusBarId::CalcStatusBar);
+}
+
+ScModule::ScModule( SfxObjectFactory* pFact ) :
+ SfxModule("sc"_ostr, {pFact}),
+ m_aIdleTimer("sc ScModule IdleTimer"),
+ m_pDragData(new ScDragData),
+ m_pSelTransfer( nullptr ),
+ m_pRefInputHandler( nullptr ),
+ m_nCurRefDlgId( 0 ),
+ m_bIsWaterCan( false ),
+ m_bIsInEditCommand( false ),
+ m_bIsInExecuteDrop( false ),
+ m_bIsInSharedDocLoading( false ),
+ m_bIsInSharedDocSaving( false )
+{
+ // The ResManager (DLL data) is not yet initialized in the ctor!
+ SetName("StarCalc"); // for Basic
+
+ ResetDragObject();
+
+ // InputHandler does not need to be created
+
+ // Create ErrorHandler - was in Init()
+ // Between OfficeApplication::Init and ScGlobal::Init
+ SvxErrorHandler::ensure();
+ m_pErrorHdl.reset( new SfxErrorHandler(RID_ERRHDLSC,
+ ErrCodeArea::Sc,
+ ErrCodeArea::Sc,
+ GetResLocale()) );
+
+ m_aIdleTimer.SetTimeout(SC_IDLE_MIN);
+ m_aIdleTimer.SetInvokeHandler( LINK( this, ScModule, IdleHandler ) );
+ m_aIdleTimer.Start();
+
+ m_pMessagePool = new ScMessagePool;
+ m_pMessagePool->FreezeIdRanges();
+ SetPool( m_pMessagePool.get() );
+ ScGlobal::InitTextHeight( m_pMessagePool.get() );
+
+ StartListening( *SfxGetpApp() ); // for SfxHintId::Deinitializing
+
+ // Initialize the color config
+ GetColorConfig();
+}
+
+ScModule::~ScModule()
+{
+ OSL_ENSURE( !m_pSelTransfer, "Selection Transfer object not deleted" );
+
+ // InputHandler does not need to be deleted (there's none in the App anymore)
+
+ m_pMessagePool.clear();
+
+ m_pDragData.reset();
+ m_pErrorHdl.reset();
+
+ ScGlobal::Clear(); // Also calls ScDocumentPool::DeleteVersionMaps();
+
+ DeleteCfg(); // Called from Exit()
+}
+
+void ScModule::ConfigurationChanged(utl::ConfigurationBroadcaster* p, ConfigurationHints eHints)
+{
+ if ( p == m_pColorConfig.get() || p == m_pAccessOptions.get() )
+ {
+ // Test if detective objects have to be updated with new colors
+ // (if the detective colors haven't been used yet, there's nothing to update)
+ if ( ScDetectiveFunc::IsColorsInitialized() )
+ {
+ const svtools::ColorConfig& rColors = GetColorConfig();
+ bool bArrows =
+ ( ScDetectiveFunc::GetArrowColor() != rColors.GetColorValue(svtools::CALCDETECTIVE).nColor ||
+ ScDetectiveFunc::GetErrorColor() != rColors.GetColorValue(svtools::CALCDETECTIVEERROR).nColor );
+ bool bComments =
+ ( ScDetectiveFunc::GetCommentColor() != rColors.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor );
+ if ( bArrows || bComments )
+ {
+ ScDetectiveFunc::InitializeColors(); // get the new colors
+
+ // update detective objects in all open documents
+ SfxObjectShell* pObjSh = SfxObjectShell::GetFirst();
+ while ( pObjSh )
+ {
+ if ( auto pDocSh = dynamic_cast<ScDocShell * >(pObjSh) )
+ {
+ if ( bArrows )
+ ScDetectiveFunc( pDocSh->GetDocument(), 0 ).UpdateAllArrowColors();
+ if ( bComments )
+ ScDetectiveFunc::UpdateAllComments( pDocSh->GetDocument() );
+ }
+ pObjSh = SfxObjectShell::GetNext( *pObjSh );
+ }
+ }
+ }
+
+ bool bSkipInvalidate = false;
+
+ const bool bKit = comphelper::LibreOfficeKit::isActive();
+ if (bKit && p == m_pColorConfig.get())
+ {
+ SfxViewShell* pSfxViewShell = SfxViewShell::Current();
+ ScTabViewShell* pViewShell = dynamic_cast<ScTabViewShell*>(pSfxViewShell);
+
+ if (pViewShell)
+ {
+ ScViewData& pViewData = pViewShell->GetViewData();
+ ScViewOptions aViewOptions = pViewData.GetOptions();
+ Color aFillColor(m_pColorConfig->GetColorValue(svtools::DOCCOLOR).nColor);
+ aViewOptions.SetDocColor(aFillColor);
+ aViewOptions.SetColorSchemeName(svtools::ColorConfig::GetCurrentSchemeName());
+ const bool bChanged(aViewOptions != pViewData.GetOptions());
+ if (bChanged)
+ pViewData.SetOptions(aViewOptions);
+ ScModelObj* pScModelObj = comphelper::getFromUnoTunnel<ScModelObj>(SfxObjectShell::Current()->GetModel());
+ SfxLokHelper::notifyViewRenderState(SfxViewShell::Current(), pScModelObj);
+ // In Online, the document color is the one used for the background, contrary to
+ // Writer and Draw that use the application background color.
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR,
+ aFillColor.AsRGBHexString().toUtf8());
+
+ // if nothing changed, and the hint was OnlyCurrentDocumentColorScheme we can skip invalidate
+ bSkipInvalidate = !bChanged && eHints == ConfigurationHints::OnlyCurrentDocumentColorScheme;
+ }
+ }
+
+ //invalidate only the current view in tiled rendering mode, or all views otherwise
+ SfxViewShell* pViewShell = bKit ? SfxViewShell::Current() : SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(pViewShell))
+ {
+ if (!bSkipInvalidate)
+ {
+ pViewSh->PaintGrid();
+ pViewSh->PaintTop();
+ pViewSh->PaintLeft();
+ pViewSh->PaintExtras();
+ }
+
+ ScInputHandler* pHdl = pViewSh->GetInputHandler();
+ if ( pHdl )
+ pHdl->ForgetLastPattern(); // EditEngine BackgroundColor may change
+ }
+ else if ( dynamic_cast<const ScPreviewShell*>( pViewShell) != nullptr )
+ {
+ vcl::Window* pWin = pViewShell->GetWindow();
+ if (pWin)
+ pWin->Invalidate();
+ }
+ if (bKit)
+ break;
+ pViewShell = SfxViewShell::GetNext( *pViewShell );
+ }
+ }
+ else if ( p == m_pCTLOptions.get() )
+ {
+ // for all documents: set digit language for printer, recalc output factor, update row heights
+ SfxObjectShell* pObjSh = SfxObjectShell::GetFirst();
+ while ( pObjSh )
+ {
+ if ( auto pDocSh = dynamic_cast<ScDocShell *>(pObjSh) )
+ {
+ OutputDevice* pPrinter = pDocSh->GetPrinter();
+ if ( pPrinter )
+ pPrinter->SetDigitLanguage( GetOptDigitLanguage() );
+
+ pDocSh->CalcOutputFactor();
+
+ SCTAB nTabCount = pDocSh->GetDocument().GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ pDocSh->AdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab );
+ }
+ pObjSh = SfxObjectShell::GetNext( *pObjSh );
+ }
+
+ // for all views (table and preview): update digit language
+ SfxViewShell* pSh = SfxViewShell::GetFirst();
+ while ( pSh )
+ {
+ if (ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(pSh))
+ {
+ // set ref-device for EditEngine (re-evaluates digit settings)
+ ScInputHandler* pHdl = GetInputHdl(pViewSh);
+ if (pHdl)
+ pHdl->UpdateRefDevice();
+
+ pViewSh->DigitLanguageChanged();
+ pViewSh->PaintGrid();
+ }
+ else if (ScPreviewShell* pPreviewSh = dynamic_cast<ScPreviewShell*>(pSh))
+ {
+ ScPreview* pPreview = pPreviewSh->GetPreview();
+
+ pPreview->GetOutDev()->SetDigitLanguage( GetOptDigitLanguage() );
+ pPreview->Invalidate();
+ }
+
+ pSh = SfxViewShell::GetNext( *pSh );
+ }
+ }
+}
+
+void ScModule::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Deinitializing )
+ {
+ // ConfigItems must be removed before ConfigManager
+ DeleteCfg();
+ }
+}
+
+void ScModule::DeleteCfg()
+{
+ m_pViewCfg.reset(); // Saving happens automatically before Exit()
+ m_pDocCfg.reset();
+ m_pAppCfg.reset();
+ m_pDefaultsCfg.reset();
+ m_pFormulaCfg.reset();
+ m_pInputCfg.reset();
+ m_pPrintCfg.reset();
+ m_pNavipiCfg.reset();
+ m_pAddInCfg.reset();
+
+ if ( m_pColorConfig )
+ {
+ m_pColorConfig->RemoveListener(this);
+ m_pColorConfig.reset();
+ }
+ if ( m_pAccessOptions )
+ {
+ m_pAccessOptions->RemoveListener(this);
+ m_pAccessOptions.reset();
+ }
+ if ( m_pCTLOptions )
+ {
+ m_pCTLOptions->RemoveListener(this);
+ m_pCTLOptions.reset();
+ }
+ m_pUserOptions.reset();
+}
+
+// Moved here from the App
+
+void ScModule::Execute( SfxRequest& rReq )
+{
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ SfxBindings* pBindings = pViewFrm ? &pViewFrm->GetBindings() : nullptr;
+
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ switch ( nSlot )
+ {
+ case SID_CHOOSE_DESIGN:
+ SfxApplication::CallAppBasic( "Template.Samples.ShowStyles" );
+ break;
+ case SID_EURO_CONVERTER:
+ SfxApplication::CallAppBasic( "Euro.ConvertRun.Main" );
+ break;
+ case SID_AUTOSPELL_CHECK:
+ {
+ bool bSet;
+ const SfxPoolItem* pItem;
+ if (pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ))
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ else if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ else
+ { // Toggle
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ if ( pDocSh )
+ bSet = !pDocSh->GetDocument().GetDocOptions().IsAutoSpell();
+ else
+ bSet = !GetDocOptions().IsAutoSpell();
+ }
+
+ SfxItemSetFixed<SID_AUTOSPELL_CHECK, SID_AUTOSPELL_CHECK> aSet( GetPool() );
+ aSet.Put( SfxBoolItem( SID_AUTOSPELL_CHECK, bSet ) );
+ ModifyOptions( aSet );
+ rReq.Done();
+ }
+ break;
+
+ case SID_ATTR_METRIC:
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) )
+ {
+ FieldUnit eUnit = static_cast<FieldUnit>(static_cast<const SfxUInt16Item*>(pItem)->GetValue());
+ switch( eUnit )
+ {
+ case FieldUnit::MM: // Just the units that are also in the dialog
+ case FieldUnit::CM:
+ case FieldUnit::INCH:
+ case FieldUnit::PICA:
+ case FieldUnit::POINT:
+ {
+ PutItem( *pItem );
+ ScAppOptions aNewOpts( GetAppOptions() );
+ aNewOpts.SetAppMetric( eUnit );
+ SetAppOptions( aNewOpts );
+ rReq.Done();
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_AUTOCOMPLETE:
+ {
+ ScAppOptions aNewOpts( GetAppOptions() );
+ bool bNew = !aNewOpts.GetAutoComplete();
+ aNewOpts.SetAutoComplete( bNew );
+ SetAppOptions( aNewOpts );
+ if (pBindings)
+ pBindings->Invalidate( FID_AUTOCOMPLETE );
+ rReq.Done();
+ }
+ break;
+
+ case SID_DETECTIVE_AUTO:
+ {
+ ScAppOptions aNewOpts( GetAppOptions() );
+ bool bNew = !aNewOpts.GetDetectiveAuto();
+ const SfxBoolItem* pAuto = rReq.GetArg<SfxBoolItem>(SID_DETECTIVE_AUTO);
+ if ( pAuto )
+ bNew = pAuto->GetValue();
+
+ aNewOpts.SetDetectiveAuto( bNew );
+ SetAppOptions( aNewOpts );
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_AUTO );
+ rReq.AppendItem( SfxBoolItem( SID_DETECTIVE_AUTO, bNew ) );
+ rReq.Done();
+ }
+ break;
+
+ case SID_PSZ_FUNCTION:
+ if (pReqArgs)
+ {
+ const SfxUInt32Item & rItem = pReqArgs->Get(SID_PSZ_FUNCTION);
+
+ ScAppOptions aNewOpts( GetAppOptions() );
+ aNewOpts.SetStatusFunc( rItem.GetValue() );
+ SetAppOptions( aNewOpts );
+
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_TABLE_CELL );
+ pBindings->Update( SID_TABLE_CELL ); // Immediately
+
+ pBindings->Invalidate( SID_PSZ_FUNCTION );
+ pBindings->Update( SID_PSZ_FUNCTION );
+ // If the menu is opened again immediately
+ }
+ }
+ break;
+
+ case SID_ATTR_LANGUAGE:
+ case SID_ATTR_CHAR_CJK_LANGUAGE:
+ case SID_ATTR_CHAR_CTL_LANGUAGE:
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( GetPool().GetWhich(nSlot), true, &pItem ) )
+ {
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ LanguageType eNewLang = static_cast<const SvxLanguageItem*>(pItem)->GetLanguage();
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ LanguageType eOld = ( nSlot == SID_ATTR_CHAR_CJK_LANGUAGE ) ? eCjk :
+ ( ( nSlot == SID_ATTR_CHAR_CTL_LANGUAGE ) ? eCtl : eLatin );
+ if ( eNewLang != eOld )
+ {
+ if ( nSlot == SID_ATTR_CHAR_CJK_LANGUAGE )
+ eCjk = eNewLang;
+ else if ( nSlot == SID_ATTR_CHAR_CTL_LANGUAGE )
+ eCtl = eNewLang;
+ else
+ eLatin = eNewLang;
+
+ rDoc.SetLanguage( eLatin, eCjk, eCtl );
+
+ ScInputHandler* pInputHandler = GetInputHdl();
+ if ( pInputHandler )
+ pInputHandler->UpdateSpellSettings(); // EditEngine flags
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( pViewSh )
+ pViewSh->UpdateDrawTextOutliner(); // EditEngine flags
+
+ pDocSh->SetDocumentModified();
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_FOCUS_POSWND:
+ {
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ {
+ ScInputWindow* pWin = pHdl->GetInputWindow();
+ if (pWin)
+ pWin->PosGrabFocus();
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_OPEN_XML_FILTERSETTINGS:
+ {
+ try
+ {
+ css::uno::Reference < css::ui::dialogs::XExecutableDialog > xDialog = css::ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext());
+ xDialog->execute();
+ }
+ catch( css::uno::RuntimeException& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sc.ui");
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "ScApplication: Unknown Message." );
+ break;
+ }
+}
+
+void ScModule::GetState( SfxItemSet& rSet )
+{
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ bool bTabView = pDocSh && (pDocSh->GetBestViewShell() != nullptr);
+
+ SfxWhichIter aIter(rSet);
+ for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
+ {
+ if (!bTabView)
+ {
+ // Not in the normal calc view shell (most likely in preview shell). Disable all actions.
+ rSet.DisableItem(nWhich);
+ continue;
+ }
+
+ switch ( nWhich )
+ {
+ case FID_AUTOCOMPLETE:
+ rSet.Put( SfxBoolItem( nWhich, GetAppOptions().GetAutoComplete() ) );
+ break;
+ case SID_DETECTIVE_AUTO:
+ rSet.Put( SfxBoolItem( nWhich, GetAppOptions().GetDetectiveAuto() ) );
+ break;
+ case SID_PSZ_FUNCTION:
+ rSet.Put( SfxUInt32Item( nWhich, GetAppOptions().GetStatusFunc() ) );
+ break;
+ case SID_ATTR_METRIC:
+ rSet.Put( SfxUInt16Item( nWhich, sal::static_int_cast<sal_uInt16>(GetAppOptions().GetAppMetric()) ) );
+ break;
+ case SID_AUTOSPELL_CHECK:
+ rSet.Put( SfxBoolItem( nWhich, pDocSh->GetDocument().GetDocOptions().IsAutoSpell()) );
+ break;
+ case SID_ATTR_LANGUAGE:
+ case ATTR_CJK_FONT_LANGUAGE: // WID for SID_ATTR_CHAR_CJK_LANGUAGE
+ case ATTR_CTL_FONT_LANGUAGE: // WID for SID_ATTR_CHAR_CTL_LANGUAGE
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ pDocSh->GetDocument().GetLanguage( eLatin, eCjk, eCtl );
+ LanguageType eLang = ( nWhich == ATTR_CJK_FONT_LANGUAGE ) ? eCjk :
+ ( ( nWhich == ATTR_CTL_FONT_LANGUAGE ) ? eCtl : eLatin );
+ rSet.Put( SvxLanguageItem( eLang, nWhich ) );
+ }
+ break;
+ }
+ }
+}
+
+void ScModule::HideDisabledSlots( SfxItemSet& rSet )
+{
+ if( SfxViewFrame* pViewFrm = SfxViewFrame::Current() )
+ {
+ SfxBindings& rBindings = pViewFrm->GetBindings();
+ SfxWhichIter aIter( rSet );
+ for( sal_uInt16 nWhich = aIter.FirstWhich(); nWhich != 0; nWhich = aIter.NextWhich() )
+ {
+ ScViewUtil::HideDisabledSlot( rSet, rBindings, nWhich );
+ // always disable the slots
+ rSet.DisableItem( nWhich );
+ }
+ }
+}
+
+void ScModule::ResetDragObject()
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->ResetDragObject();
+ }
+ else
+ {
+ m_pDragData->pCellTransfer = nullptr;
+ m_pDragData->pDrawTransfer = nullptr;
+ m_pDragData->pJumpLocalDoc = nullptr;
+ m_pDragData->aLinkDoc.clear();
+ m_pDragData->aLinkTable.clear();
+ m_pDragData->aLinkArea.clear();
+ m_pDragData->aJumpTarget.clear();
+ m_pDragData->aJumpText.clear();
+ }
+}
+
+const ScDragData& ScModule::GetDragData() const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ assert(pViewShell);
+ return pViewShell->GetDragData();
+ }
+ else
+ return *m_pDragData;
+}
+
+void ScModule::SetDragObject( ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetDragObject(pCellObj, pDrawObj);
+ }
+ else
+ {
+ ResetDragObject();
+ m_pDragData->pCellTransfer = pCellObj;
+ m_pDragData->pDrawTransfer = pDrawObj;
+ }
+}
+
+void ScModule::SetDragLink(
+ const OUString& rDoc, const OUString& rTab, const OUString& rArea )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetDragLink(rDoc, rTab, rArea);
+ }
+ else
+ {
+ ResetDragObject();
+ m_pDragData->aLinkDoc = rDoc;
+ m_pDragData->aLinkTable = rTab;
+ m_pDragData->aLinkArea = rArea;
+ }
+}
+
+void ScModule::SetDragJump(
+ ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetDragJump(pLocalDoc, rTarget, rText);
+ }
+ else
+ {
+ ResetDragObject();
+
+ m_pDragData->pJumpLocalDoc = pLocalDoc;
+ m_pDragData->aJumpTarget = rTarget;
+ m_pDragData->aJumpText = rText;
+ }
+}
+
+ScDocument* ScModule::GetClipDoc()
+{
+ // called from document
+ SfxViewFrame* pViewFrame = nullptr;
+ ScTabViewShell* pViewShell = nullptr;
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable;
+
+ if ((pViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current())))
+ xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin()));
+ else if ((pViewShell = dynamic_cast<ScTabViewShell*>(SfxViewShell::GetFirst())))
+ xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin()));
+ else if ((pViewFrame = SfxViewFrame::GetFirst()))
+ {
+ css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard =
+ pViewFrame->GetWindow().GetClipboard();
+ xTransferable.set(xClipboard.is() ? xClipboard->getContents() : nullptr, css::uno::UNO_QUERY);
+ }
+
+ const ScTransferObj* pObj = ScTransferObj::GetOwnClipboard(xTransferable);
+ if (pObj)
+ {
+ ScDocument* pDoc = pObj->GetDocument();
+ assert((!pDoc || pDoc->IsClipboard()) && "Document is not clipboard, how can that be?");
+ return pDoc;
+ }
+
+ return nullptr;
+}
+
+void ScModule::SetSelectionTransfer( ScSelectionTransferObj* pNew )
+{
+ m_pSelTransfer = pNew;
+}
+
+void ScModule::SetViewOptions( const ScViewOptions& rOpt )
+{
+ if ( !m_pViewCfg )
+ m_pViewCfg.reset(new ScViewCfg);
+
+ m_pViewCfg->SetOptions( rOpt );
+}
+
+const ScViewOptions& ScModule::GetViewOptions()
+{
+ if ( !m_pViewCfg )
+ m_pViewCfg.reset( new ScViewCfg );
+
+ return *m_pViewCfg;
+}
+
+void ScModule::SetDocOptions( const ScDocOptions& rOpt )
+{
+ if ( !m_pDocCfg )
+ m_pDocCfg.reset( new ScDocCfg );
+
+ m_pDocCfg->SetOptions( rOpt );
+}
+
+const ScDocOptions& ScModule::GetDocOptions()
+{
+ if ( !m_pDocCfg )
+ m_pDocCfg.reset( new ScDocCfg );
+
+ return *m_pDocCfg;
+}
+
+void ScModule::InsertEntryToLRUList(sal_uInt16 nFIndex)
+{
+ if(nFIndex == 0)
+ return;
+
+ const ScAppOptions& rAppOpt = GetAppOptions();
+ sal_uInt16 nLRUFuncCount = std::min( rAppOpt.GetLRUFuncListCount(), sal_uInt16(LRU_MAX) );
+ sal_uInt16* pLRUListIds = rAppOpt.GetLRUFuncList();
+
+ sal_uInt16 aIdxList[LRU_MAX];
+ sal_uInt16 n = 0;
+ bool bFound = false;
+
+ while ((n < LRU_MAX) && n<nLRUFuncCount) // Iterate through old list
+ {
+ if (!bFound && (pLRUListIds[n]== nFIndex))
+ bFound = true; // First hit!
+ else if (bFound)
+ aIdxList[n ] = pLRUListIds[n]; // Copy after hit
+ else if ((n+1) < LRU_MAX)
+ aIdxList[n+1] = pLRUListIds[n]; // Move before hit
+ n++;
+ }
+ if (!bFound && (n < LRU_MAX)) // Entry not found?
+ n++; // One more
+ aIdxList[0] = nFIndex; // Current on Top
+
+ ScAppOptions aNewOpts(rAppOpt); // Let App know
+ aNewOpts.SetLRUFuncList(aIdxList, n);
+ SetAppOptions(aNewOpts);
+}
+
+void ScModule::SetAppOptions( const ScAppOptions& rOpt )
+{
+ if ( !m_pAppCfg )
+ m_pAppCfg.reset( new ScAppCfg );
+
+ m_pAppCfg->SetOptions( rOpt );
+}
+
+void global_InitAppOptions()
+{
+ SC_MOD()->GetAppOptions();
+}
+
+const ScAppOptions& ScModule::GetAppOptions()
+{
+ if ( !m_pAppCfg )
+ m_pAppCfg.reset( new ScAppCfg );
+
+ return m_pAppCfg->GetOptions();
+}
+
+void ScModule::SetDefaultsOptions( const ScDefaultsOptions& rOpt )
+{
+ if ( !m_pDefaultsCfg )
+ m_pDefaultsCfg.reset( new ScDefaultsCfg );
+
+ m_pDefaultsCfg->SetOptions( rOpt );
+}
+
+const ScDefaultsOptions& ScModule::GetDefaultsOptions()
+{
+ if ( !m_pDefaultsCfg )
+ m_pDefaultsCfg.reset( new ScDefaultsCfg );
+
+ return *m_pDefaultsCfg;
+}
+
+void ScModule::SetFormulaOptions( const ScFormulaOptions& rOpt )
+{
+ if ( !m_pFormulaCfg )
+ m_pFormulaCfg.reset( new ScFormulaCfg );
+
+ m_pFormulaCfg->SetOptions( rOpt );
+}
+
+const ScFormulaOptions& ScModule::GetFormulaOptions()
+{
+ if ( !m_pFormulaCfg )
+ m_pFormulaCfg.reset( new ScFormulaCfg );
+
+ return *m_pFormulaCfg;
+}
+
+void ScModule::SetInputOptions( const ScInputOptions& rOpt )
+{
+ if ( !m_pInputCfg )
+ m_pInputCfg.reset( new ScInputCfg );
+
+ m_pInputCfg->SetOptions( rOpt );
+}
+
+const ScInputOptions& ScModule::GetInputOptions()
+{
+ if ( !m_pInputCfg )
+ m_pInputCfg.reset( new ScInputCfg );
+
+ return m_pInputCfg->GetOptions();
+}
+
+void ScModule::SetPrintOptions( const ScPrintOptions& rOpt )
+{
+ if ( !m_pPrintCfg )
+ m_pPrintCfg.reset( new ScPrintCfg );
+
+ m_pPrintCfg->SetOptions( rOpt );
+}
+
+const ScPrintOptions& ScModule::GetPrintOptions()
+{
+ if ( !m_pPrintCfg )
+ m_pPrintCfg.reset( new ScPrintCfg );
+
+ return m_pPrintCfg->GetOptions();
+}
+
+ScNavipiCfg& ScModule::GetNavipiCfg()
+{
+ if ( !m_pNavipiCfg )
+ m_pNavipiCfg.reset( new ScNavipiCfg );
+
+ return *m_pNavipiCfg;
+}
+
+ScAddInCfg& ScModule::GetAddInCfg()
+{
+ if ( !m_pAddInCfg )
+ m_pAddInCfg.reset( new ScAddInCfg );
+
+ return *m_pAddInCfg;
+}
+
+svtools::ColorConfig& ScModule::GetColorConfig()
+{
+ if ( !m_pColorConfig )
+ {
+ m_pColorConfig.reset( new svtools::ColorConfig );
+ m_pColorConfig->AddListener(this);
+ }
+
+ return *m_pColorConfig;
+}
+
+SvtUserOptions& ScModule::GetUserOptions()
+{
+ if( !m_pUserOptions )
+ {
+ m_pUserOptions.reset( new SvtUserOptions );
+ }
+ return *m_pUserOptions;
+}
+
+LanguageType ScModule::GetOptDigitLanguage()
+{
+ SvtCTLOptions::TextNumerals eNumerals = SvtCTLOptions::GetCTLTextNumerals();
+ return ( eNumerals == SvtCTLOptions::NUMERALS_ARABIC ) ? LANGUAGE_ENGLISH_US :
+ ( eNumerals == SvtCTLOptions::NUMERALS_HINDI) ? LANGUAGE_ARABIC_SAUDI_ARABIA :
+ LANGUAGE_SYSTEM;
+}
+
+/**
+ * Options
+ *
+ * Items from Calc options dialog and SID_AUTOSPELL_CHECK
+ */
+void ScModule::ModifyOptions( const SfxItemSet& rOptSet )
+{
+ LanguageType nOldSpellLang, nOldCjkLang, nOldCtlLang;
+ bool bOldAutoSpell;
+ GetSpellSettings( nOldSpellLang, nOldCjkLang, nOldCtlLang, bOldAutoSpell );
+
+ if (!m_pAppCfg)
+ GetAppOptions();
+ OSL_ENSURE( m_pAppCfg, "AppOptions not initialised :-(" );
+
+ if (!m_pInputCfg)
+ GetInputOptions();
+ OSL_ENSURE( m_pInputCfg, "InputOptions not initialised :-(" );
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ SfxBindings* pBindings = pViewFrm ? &pViewFrm->GetBindings() : nullptr;
+
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ ScDocument* pDoc = pDocSh ? &pDocSh->GetDocument() : nullptr;
+ bool bRepaint = false;
+ bool bUpdateMarks = false;
+ bool bUpdateRefDev = false;
+ bool bCalcAll = false;
+ bool bSaveAppOptions = false;
+ bool bSaveInputOptions = false;
+ bool bCompileErrorCells = false;
+
+ // SfxGetpApp()->SetOptions( rOptSet );
+
+ ScAppOptions aAppOptions = m_pAppCfg->GetOptions();
+
+ // No more linguistics
+ if (const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_ATTR_METRIC))
+ {
+ PutItem( *pItem );
+ aAppOptions.SetAppMetric( static_cast<FieldUnit>(pItem->GetValue()) );
+ bSaveAppOptions = true;
+ }
+
+ if (const ScUserListItem* pItem = rOptSet.GetItemIfSet(SCITEM_USERLIST))
+ {
+ ScGlobal::SetUserList( pItem->GetUserList() );
+ bSaveAppOptions = true;
+ }
+
+ if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_SYNCZOOM))
+ {
+ aAppOptions.SetSynchronizeZoom( pItem->GetValue() );
+ bSaveAppOptions = true;
+ }
+
+ if (const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_KEY_BINDING_COMPAT))
+ {
+ sal_uInt16 nVal = pItem->GetValue();
+ ScOptionsUtil::KeyBindingType eOld = aAppOptions.GetKeyBindingType();
+ ScOptionsUtil::KeyBindingType eNew = static_cast<ScOptionsUtil::KeyBindingType>(nVal);
+ if (eOld != eNew)
+ {
+ aAppOptions.SetKeyBindingType(eNew);
+ bSaveAppOptions = true;
+ ScDocShell::ResetKeyBindings(eNew);
+ }
+ }
+
+ if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_LINKS))
+ {
+ aAppOptions.SetLinksInsertedLikeMSExcel(pItem->GetValue());
+ bSaveAppOptions = true;
+ }
+
+ // DefaultsOptions
+ if (const ScTpDefaultsItem* pItem = rOptSet.GetItemIfSet(SID_SCDEFAULTSOPTIONS))
+ {
+ const ScDefaultsOptions& rOpt = pItem->GetDefaultsOptions();
+ SetDefaultsOptions( rOpt );
+ }
+
+ // FormulaOptions
+ if (const ScTpFormulaItem* pItem = rOptSet.GetItemIfSet(SID_SCFORMULAOPTIONS))
+ {
+ const ScFormulaOptions& rOpt = pItem->GetFormulaOptions();
+
+ if (!m_pFormulaCfg || (*m_pFormulaCfg != rOpt))
+ // Formula options have changed. Repaint the column headers.
+ bRepaint = true;
+
+ if (m_pFormulaCfg && m_pFormulaCfg->GetUseEnglishFuncName() != rOpt.GetUseEnglishFuncName())
+ {
+ // Re-compile formula cells with error as the error may have been
+ // caused by unresolved function names.
+ bCompileErrorCells = true;
+ }
+
+ // Recalc for interpreter options changes.
+ if (m_pFormulaCfg && m_pFormulaCfg->GetCalcConfig() != rOpt.GetCalcConfig())
+ bCalcAll = true;
+
+ if ( pDocSh )
+ {
+ pDocSh->SetFormulaOptions( rOpt );
+ pDocSh->SetDocumentModified();
+ }
+
+ // ScDocShell::SetFormulaOptions() may check for changed settings, so
+ // set the new options here after that has been called.
+ if (!bCalcAll || rOpt.GetWriteCalcConfig())
+ {
+ // CalcConfig is new, didn't change or is global, simply set all.
+ SetFormulaOptions( rOpt );
+ }
+ else
+ {
+ // If "only for current document" was checked, reset those affected
+ // by that setting to previous values.
+ ScFormulaOptions aNewOpt( rOpt);
+ aNewOpt.GetCalcConfig().MergeDocumentSpecific( m_pFormulaCfg->GetCalcConfig());
+ SetFormulaOptions( aNewOpt);
+ }
+ }
+
+ // ViewOptions
+ if (const ScTpViewItem* pItem = rOptSet.GetItemIfSet(SID_SCVIEWOPTIONS))
+ {
+ const ScViewOptions& rNewOpt = pItem->GetViewOptions();
+
+ if ( pViewSh )
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ const ScViewOptions& rOldOpt = rViewData.GetOptions();
+
+ bool bAnchorList = ( rOldOpt.GetOption( VOPT_ANCHOR ) !=
+ rNewOpt.GetOption( VOPT_ANCHOR ) );
+
+ if ( rOldOpt != rNewOpt )
+ {
+ rViewData.SetOptions( rNewOpt ); // Changes rOldOpt
+ rViewData.GetDocument().SetViewOptions( rNewOpt );
+ if (pDocSh)
+ pDocSh->SetDocumentModified();
+ bRepaint = true;
+ }
+ if ( bAnchorList )
+ pViewSh->UpdateAnchorHandles();
+ }
+ SetViewOptions( rNewOpt );
+ if (pBindings)
+ {
+ pBindings->Invalidate(SID_HELPLINES_MOVE);
+ }
+ }
+
+ // GridOptions
+ // Evaluate after ViewOptions, as GridOptions is a member of ViewOptions
+ if ( const SvxGridItem* pItem = rOptSet.GetItemIfSet(SID_ATTR_GRID_OPTIONS) )
+ {
+ ScGridOptions aNewGridOpt( *pItem );
+
+ if ( pViewSh )
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScViewOptions aNewViewOpt( rViewData.GetOptions() );
+ const ScGridOptions& rOldGridOpt = aNewViewOpt.GetGridOptions();
+
+ if ( rOldGridOpt != aNewGridOpt )
+ {
+ aNewViewOpt.SetGridOptions( aNewGridOpt );
+ rViewData.SetOptions( aNewViewOpt );
+ rViewData.GetDocument().SetViewOptions( aNewViewOpt );
+ if (pDocSh)
+ pDocSh->SetDocumentModified();
+ bRepaint = true;
+ }
+ }
+ ScViewOptions aNewViewOpt ( GetViewOptions() );
+ aNewViewOpt.SetGridOptions( aNewGridOpt );
+ SetViewOptions( aNewViewOpt );
+ if (pBindings)
+ {
+ pBindings->Invalidate(SID_GRID_VISIBLE);
+ pBindings->Invalidate(SID_GRID_USE);
+ }
+ }
+
+ // DocOptions
+ if ( const ScTpCalcItem* pItem = rOptSet.GetItemIfSet(SID_SCDOCOPTIONS) )
+ {
+ const ScDocOptions& rNewOpt = pItem->GetDocOptions();
+
+ if ( pDoc )
+ {
+ const ScDocOptions& rOldOpt = pDoc->GetDocOptions();
+
+ bRepaint = ( bRepaint || ( rOldOpt != rNewOpt ) );
+ bCalcAll = bRepaint &&
+ ( rOldOpt.IsIter() != rNewOpt.IsIter()
+ || rOldOpt.GetIterCount() != rNewOpt.GetIterCount()
+ || rOldOpt.GetIterEps() != rNewOpt.GetIterEps()
+ || rOldOpt.IsIgnoreCase() != rNewOpt.IsIgnoreCase()
+ || rOldOpt.IsCalcAsShown() != rNewOpt.IsCalcAsShown()
+ || (rNewOpt.IsCalcAsShown() &&
+ rOldOpt.GetStdPrecision() != rNewOpt.GetStdPrecision())
+ || rOldOpt.IsMatchWholeCell() != rNewOpt.IsMatchWholeCell()
+ || rOldOpt.GetYear2000() != rNewOpt.GetYear2000()
+ || rOldOpt.IsFormulaRegexEnabled() != rNewOpt.IsFormulaRegexEnabled()
+ || rOldOpt.IsFormulaWildcardsEnabled() != rNewOpt.IsFormulaWildcardsEnabled()
+ );
+ pDoc->SetDocOptions( rNewOpt );
+ pDocSh->SetDocumentModified();
+ }
+ SetDocOptions( rNewOpt );
+ }
+
+ // Set TabDistance after the actual DocOptions
+ if ( const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_ATTR_DEFTABSTOP) )
+ {
+ sal_uInt16 nTabDist = pItem->GetValue();
+ ScDocOptions aOpt(GetDocOptions());
+ aOpt.SetTabDistance(nTabDist);
+ SetDocOptions( aOpt );
+
+ if ( pDoc )
+ {
+ ScDocOptions aDocOpt(pDoc->GetDocOptions());
+ aDocOpt.SetTabDistance(nTabDist);
+ pDoc->SetDocOptions( aDocOpt );
+ pDocSh->SetDocumentModified();
+ if(pDoc->GetDrawLayer())
+ pDoc->GetDrawLayer()->SetDefaultTabulator(nTabDist);
+ }
+ }
+
+ // AutoSpell after the DocOptions (due to being a member)
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_AUTOSPELL_CHECK) ) // At DocOptions
+ {
+ bool bDoAutoSpell = pItem->GetValue();
+
+ if (pDoc)
+ {
+ ScDocOptions aNewOpt = pDoc->GetDocOptions();
+ if ( aNewOpt.IsAutoSpell() != bDoAutoSpell )
+ {
+ aNewOpt.SetAutoSpell( bDoAutoSpell );
+ pDoc->SetDocOptions( aNewOpt );
+
+ if (pViewSh)
+ pViewSh->EnableAutoSpell(bDoAutoSpell);
+
+ bRepaint = true; // Because HideAutoSpell might be invalid
+ //TODO: Paint all Views?
+ }
+ }
+
+ if ( bOldAutoSpell != bDoAutoSpell )
+ SetAutoSpellProperty( bDoAutoSpell );
+ if ( pDocSh )
+ pDocSh->PostPaintGridAll(); // Due to marks
+ ScInputHandler* pInputHandler = GetInputHdl();
+ if ( pInputHandler )
+ pInputHandler->UpdateSpellSettings(); // EditEngine flags
+ if ( pViewSh )
+ pViewSh->UpdateDrawTextOutliner(); // EditEngine flags
+
+ if (pBindings)
+ pBindings->Invalidate( SID_AUTOSPELL_CHECK );
+ }
+
+ // InputOptions
+ ScInputOptions aInputOptions = m_pInputCfg->GetOptions();
+ if ( const SfxUInt16Item* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_SELECTIONPOS) )
+ {
+ aInputOptions.SetMoveDir( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_SELECTION) )
+ {
+ aInputOptions.SetMoveSelection( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_EDITMODE) )
+ {
+ aInputOptions.SetEnterEdit( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_FMT_EXPAND) )
+ {
+ aInputOptions.SetExtendFormat( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_RANGEFINDER) )
+ {
+ aInputOptions.SetRangeFinder( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_REF_EXPAND) )
+ {
+ aInputOptions.SetExpandRefs( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+ if (const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_OPT_SORT_REF_UPDATE))
+ {
+ aInputOptions.SetSortRefUpdate( pItem->GetValue());
+ bSaveInputOptions = true;
+ }
+
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_MARK_HEADER) )
+ {
+ aInputOptions.SetMarkHeader( pItem->GetValue() );
+ bSaveInputOptions = true;
+ bUpdateMarks = true;
+ }
+ if ( const SfxBoolItem* pItem = rOptSet.GetItemIfSet(SID_SC_INPUT_TEXTWYSIWYG) )
+ {
+ bool bNew = pItem->GetValue();
+ if ( bNew != aInputOptions.GetTextWysiwyg() )
+ {
+ aInputOptions.SetTextWysiwyg( bNew );
+ bSaveInputOptions = true;
+ bUpdateRefDev = true;
+ }
+ }
+ if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_REPLCELLSWARN ) )
+ {
+ aInputOptions.SetReplaceCellsWarn( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+
+ if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_LEGACY_CELL_SELECTION ) )
+ {
+ aInputOptions.SetLegacyCellSelection( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+
+ if( const SfxBoolItem* pItem = rOptSet.GetItemIfSet( SID_SC_INPUT_ENTER_PASTE_MODE ) )
+ {
+ aInputOptions.SetEnterPasteMode( pItem->GetValue() );
+ bSaveInputOptions = true;
+ }
+
+ // PrintOptions
+ if ( const ScTpPrintItem* pItem = rOptSet.GetItemIfSet(SID_SCPRINTOPTIONS) )
+ {
+ const ScPrintOptions& rNewOpt = pItem->GetPrintOptions();
+ SetPrintOptions( rNewOpt );
+
+ // broadcast causes all previews to recalc page numbers
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScPrintOptions ) );
+ }
+
+ if ( bSaveAppOptions )
+ m_pAppCfg->SetOptions(aAppOptions);
+
+ if ( bSaveInputOptions )
+ m_pInputCfg->SetOptions(aInputOptions);
+
+ // Kick off recalculation?
+ if (pDoc && bCompileErrorCells)
+ {
+ // Re-compile cells with name error, and recalc if at least one cell
+ // has been re-compiled. In the future we may want to find a way to
+ // recalc only those that are affected.
+ if (pDoc->CompileErrorCells(FormulaError::NoName))
+ bCalcAll = true;
+ }
+
+ if ( pDoc && bCalcAll )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+ pDoc->CalcAll();
+ if ( pViewSh )
+ pViewSh->UpdateCharts( true );
+ else
+ ScDBFunc::DoUpdateCharts( ScAddress(), *pDoc, true );
+ if (pBindings)
+ pBindings->Invalidate( SID_ATTR_SIZE ); //SvxPosSize StatusControl Update
+ }
+
+ if ( pViewSh && bUpdateMarks )
+ pViewSh->UpdateAutoFillMark();
+
+ // Repaint View?
+ if ( pViewSh && bRepaint )
+ {
+ pViewSh->UpdateFixPos();
+ pViewSh->PaintGrid();
+ pViewSh->PaintTop();
+ pViewSh->PaintLeft();
+ pViewSh->PaintExtras();
+ pViewSh->InvalidateBorder();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_TOGGLEHEADERS ); // -> Checks in menu
+ pBindings->Invalidate( FID_TOGGLESYNTAX );
+ }
+ }
+
+ // update ref device (for all documents)
+ if ( !bUpdateRefDev )
+ return;
+
+ // for all documents: recalc output factor, update row heights
+ SfxObjectShell* pObjSh = SfxObjectShell::GetFirst();
+ while ( pObjSh )
+ {
+ if ( auto pOneDocSh = dynamic_cast<ScDocShell *>(pObjSh) )
+ {
+ pOneDocSh->CalcOutputFactor();
+ SCTAB nTabCount = pOneDocSh->GetDocument().GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ pOneDocSh->AdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab );
+ }
+ pObjSh = SfxObjectShell::GetNext( *pObjSh );
+ }
+
+ // for all (tab-) views:
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh )
+ {
+ ScTabViewShell* pOneViewSh = static_cast<ScTabViewShell*>(pSh);
+
+ // set ref-device for EditEngine
+ ScInputHandler* pHdl = GetInputHdl(pOneViewSh);
+ if (pHdl)
+ pHdl->UpdateRefDevice();
+
+ // update view scale
+ ScViewData& rViewData = pOneViewSh->GetViewData();
+ pOneViewSh->SetZoom( rViewData.GetZoomX(), rViewData.GetZoomY(), false );
+
+ // repaint
+ pOneViewSh->PaintGrid();
+ pOneViewSh->PaintTop();
+ pOneViewSh->PaintLeft();
+
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+}
+
+/**
+ * Input-Handler
+ */
+ScInputHandler* ScModule::GetInputHdl( ScTabViewShell* pViewSh, bool bUseRef )
+{
+ if ( !comphelper::LibreOfficeKit::isActive() && m_pRefInputHandler && bUseRef )
+ return m_pRefInputHandler;
+
+ ScInputHandler* pHdl = nullptr;
+ if ( !pViewSh )
+ {
+ // in case a UIActive embedded object has no ViewShell (UNO component)
+ // the own calc view shell will be set as current, but no handling should happen
+ ScTabViewShell* pCurViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( pCurViewSh && !pCurViewSh->GetUIActiveClient() )
+ pViewSh = pCurViewSh;
+ }
+
+ if ( pViewSh )
+ pHdl = pViewSh->GetInputHandler(); // Viewshell always has one, from now on
+
+ // If no ViewShell passed or active, we can get NULL
+ OSL_ENSURE( pHdl || !pViewSh, "GetInputHdl: no InputHandler found!" );
+ return pHdl;
+}
+
+void ScModule::ViewShellChanged(bool bStopEditing /*=true*/)
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ ScTabViewShell* pShell = ScTabViewShell::GetActiveViewShell();
+ if ( pShell && pHdl )
+ pShell->UpdateInputHandler(false, bStopEditing);
+}
+
+void ScModule::SetInputMode( ScInputMode eMode, const OUString* pInitText )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->SetMode(eMode, pInitText);
+}
+
+bool ScModule::IsEditMode()
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ return pHdl && pHdl->IsEditMode();
+}
+
+bool ScModule::IsInputMode()
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ return pHdl && pHdl->IsInputMode();
+}
+
+bool ScModule::InputKeyEvent( const KeyEvent& rKEvt, bool bStartEdit )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ return pHdl && pHdl->KeyInput( rKEvt, bStartEdit );
+}
+
+void ScModule::InputEnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK )
+{
+ if ( !SfxGetpApp()->IsDowning() ) // Not when quitting the program
+ {
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->EnterHandler( nBlockMode, bBeforeSavingInLOK );
+ }
+}
+
+void ScModule::InputCancelHandler()
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->CancelHandler();
+}
+
+void ScModule::InputSelection( const EditView* pView )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->InputSelection( pView );
+}
+
+void ScModule::InputChanged( const EditView* pView )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->InputChanged( pView, false );
+}
+
+void ScModule::ViewShellGone( const ScTabViewShell* pViewSh )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->ViewShellGone( pViewSh );
+}
+
+void ScModule::SetRefInputHdl( ScInputHandler* pNew )
+{
+ m_pRefInputHandler = pNew;
+}
+
+void ScModule::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->InputGetSelection( rStart, rEnd );
+}
+
+void ScModule::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->InputSetSelection( nStart, nEnd );
+}
+
+void ScModule::InputReplaceSelection( std::u16string_view aStr )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->InputReplaceSelection( aStr );
+}
+
+void ScModule::InputTurnOffWinEngine()
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->InputTurnOffWinEngine();
+}
+
+void ScModule::ActivateInputWindow( const OUString* pStrFormula, bool bMatrix )
+{
+ ScInputHandler* pHdl = GetInputHdl();
+ if ( !pHdl )
+ return;
+
+ ScInputWindow* pWin = pHdl->GetInputWindow();
+ if ( pStrFormula )
+ {
+ // Take over formula
+ if ( pWin )
+ {
+ pWin->SetFuncString( *pStrFormula, false );
+ // SetSumAssignMode due to sal_False not necessary
+ }
+ ScEnterMode nMode = bMatrix ? ScEnterMode::MATRIX : ScEnterMode::NORMAL;
+ pHdl->EnterHandler( nMode );
+
+ // Without Invalidate the selection remains active, if the formula has not changed
+ if (pWin)
+ pWin->TextInvalidate();
+ }
+ else
+ {
+ // Cancel
+ if ( pWin )
+ {
+ pWin->SetFuncString( OUString(), false );
+ // SetSumAssignMode due to sal_False no necessary
+ }
+ pHdl->CancelHandler();
+ }
+}
+
+/**
+ * Reference dialogs
+ */
+void ScModule::SetRefDialog( sal_uInt16 nId, bool bVis, SfxViewFrame* pViewFrm )
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+ if ( !(m_nCurRefDlgId == 0 || ( nId == m_nCurRefDlgId && !bVis )
+ || ( comphelper::LibreOfficeKit::isActive() )) )
+ return;
+
+ if ( !pViewFrm )
+ pViewFrm = SfxViewFrame::Current();
+
+ // bindings update causes problems with update of stylist if
+ // current style family has changed
+ //if ( pViewFrm )
+ // pViewFrm->GetBindings().Update(); // to avoid trouble in LockDispatcher
+
+ // before SetChildWindow
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ if ( bVis )
+ m_nCurRefDlgId = nId;
+ }
+ else
+ {
+ m_nCurRefDlgId = bVis ? nId : 0;
+ }
+
+ if ( pViewFrm )
+ {
+ // store the dialog id also in the view shell
+ SfxViewShell* pViewSh = pViewFrm->GetViewShell();
+ if (ScTabViewShell* pTabViewSh = dynamic_cast<ScTabViewShell*>(pViewSh))
+ pTabViewSh->SetCurRefDlgId(m_nCurRefDlgId);
+ else
+ {
+ // no ScTabViewShell - possible for example from a Basic macro
+ bVis = false;
+ m_nCurRefDlgId = 0; // don't set nCurRefDlgId if no dialog is created
+ }
+
+ pViewFrm->SetChildWindow( nId, bVis );
+ }
+
+ SfxApplication* pSfxApp = SfxGetpApp();
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
+}
+
+static SfxChildWindow* lcl_GetChildWinFromCurrentView( sal_uInt16 nId )
+{
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+
+ // #i46999# current view frame can be null (for example, when closing help)
+ return pViewFrm ? pViewFrm->GetChildWindow( nId ) : nullptr;
+}
+
+static SfxChildWindow* lcl_GetChildWinFromAnyView( sal_uInt16 nId )
+{
+ // First, try the current view
+ SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( nId );
+ if ( pChildWnd )
+ return pChildWnd; // found in the current view
+
+ // if not found there, get the child window from any open view
+ // it can be open only in one view because nCurRefDlgId is global
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst();
+ while ( pViewFrm )
+ {
+ pChildWnd = pViewFrm->GetChildWindow( nId );
+ if ( pChildWnd )
+ return pChildWnd; // found in any view
+
+ pViewFrm = SfxViewFrame::GetNext( *pViewFrm );
+ }
+
+ return nullptr; // none found
+}
+
+bool ScModule::IsModalMode(SfxObjectShell* pDocSh)
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+ bool bIsModal = false;
+
+ if ( m_nCurRefDlgId )
+ {
+ SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId );
+ if ( pChildWnd )
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ assert(pRefDlg);
+ bIsModal = pChildWnd->IsVisible() && pRefDlg &&
+ !( pRefDlg->IsRefInputMode() && pRefDlg->IsDocAllowed(pDocSh) );
+ }
+ }
+ else if ( pDocSh && comphelper::LibreOfficeKit::isActive() )
+ {
+ // m_nCurRefDlgId is not deglobalized so it can be set by other view
+ // in LOK case when no ChildWindow for this view was detected -> fallback
+ ScInputHandler* pHdl = GetInputHdl();
+ if ( pHdl )
+ bIsModal = pHdl->IsModalMode(pDocSh);
+ }
+ }
+ else if (pDocSh)
+ {
+ ScInputHandler* pHdl = GetInputHdl();
+ if ( pHdl )
+ bIsModal = pHdl->IsModalMode(pDocSh);
+ }
+
+ return bIsModal;
+}
+
+bool ScModule::IsTableLocked()
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+ bool bLocked = false;
+
+ // Up until now just for ScAnyRefDlg
+ if ( m_nCurRefDlgId )
+ {
+ SfxChildWindow* pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId );
+ if ( pChildWnd )
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ assert(pRefDlg);
+ if (pRefDlg)
+ bLocked = pRefDlg->IsTableLocked();
+ }
+ }
+ else if (!comphelper::LibreOfficeKit::isActive())
+ bLocked = true; // for other views, see IsModalMode
+ }
+
+ // We can't stop LOK clients from switching part, and getting out of sync.
+ assert(!bLocked || !comphelper::LibreOfficeKit::isActive());
+
+ return bLocked;
+}
+
+bool ScModule::IsRefDialogOpen()
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+ bool bIsOpen = false;
+
+ if ( m_nCurRefDlgId )
+ {
+ SfxChildWindow* pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId );
+ if ( pChildWnd )
+ bIsOpen = pChildWnd->IsVisible();
+ }
+
+ return bIsOpen;
+}
+
+bool ScModule::IsFormulaMode()
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+ bool bIsFormula = false;
+
+ if ( m_nCurRefDlgId )
+ {
+ SfxChildWindow* pChildWnd = nullptr;
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId );
+ else
+ pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId );
+
+ if ( pChildWnd )
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ assert(pRefDlg);
+ bIsFormula = pChildWnd->IsVisible() && pRefDlg && pRefDlg->IsRefInputMode();
+ }
+ }
+ else if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ // m_nCurRefDlgId is not deglobalized so it can be set by other view
+ // in LOK case when no ChildWindow for this view was detected -> fallback
+ ScInputHandler* pHdl = GetInputHdl();
+ if ( pHdl )
+ bIsFormula = pHdl->IsFormulaMode();
+ }
+ }
+ else
+ {
+ ScInputHandler* pHdl = GetInputHdl();
+ if ( pHdl )
+ bIsFormula = pHdl->IsFormulaMode();
+ }
+
+ if (m_bIsInEditCommand)
+ bIsFormula = true;
+
+ return bIsFormula;
+}
+
+static void lcl_MarkedTabs( const ScMarkData& rMark, SCTAB& rStartTab, SCTAB& rEndTab )
+{
+ if (rMark.GetSelectCount() > 1)
+ {
+ rEndTab = rMark.GetLastSelected();
+ rStartTab = rMark.GetFirstSelected();
+ }
+}
+
+void ScModule::SetReference( const ScRange& rRef, ScDocument& rDoc,
+ const ScMarkData* pMarkData )
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+
+ // In RefDialogs we also trigger the ZoomIn, if the Ref's Start and End are different
+ ScRange aNew = rRef;
+ aNew.PutInOrder(); // Always in the right direction
+
+ if( m_nCurRefDlgId )
+ {
+ SfxChildWindow* pChildWnd = nullptr;
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId );
+ else
+ pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId );
+
+ OSL_ENSURE( pChildWnd, "NoChildWin" );
+ if ( pChildWnd )
+ {
+ if ( m_nCurRefDlgId == SID_OPENDLG_CONSOLIDATE && pMarkData )
+ {
+ SCTAB nStartTab = aNew.aStart.Tab();
+ SCTAB nEndTab = aNew.aEnd.Tab();
+ lcl_MarkedTabs( *pMarkData, nStartTab, nEndTab );
+ aNew.aStart.SetTab(nStartTab);
+ aNew.aEnd.SetTab(nEndTab);
+ }
+
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ assert(pRefDlg);
+ if(pRefDlg)
+ {
+ // hide the (color) selection now instead of later from LoseFocus,
+ // don't abort the ref input that causes this call (bDoneRefMode = sal_False)
+ pRefDlg->HideReference( false );
+ pRefDlg->SetReference( aNew, rDoc );
+ }
+ }
+ }
+ else if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ // m_nCurRefDlgId is not deglobalized so it can be set by other view
+ // in LOK case when no ChildWindow for this view was detected -> fallback
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->SetReference( aNew, rDoc );
+ }
+ }
+ else
+ {
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->SetReference( aNew, rDoc );
+ else
+ {
+ OSL_FAIL("SetReference without receiver");
+ }
+ }
+}
+
+/**
+ * Multiple selection
+ */
+void ScModule::AddRefEntry()
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+ if ( m_nCurRefDlgId )
+ {
+ SfxChildWindow* pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId );
+ OSL_ENSURE( pChildWnd, "NoChildWin" );
+ if ( pChildWnd )
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ assert(pRefDlg);
+ if (pRefDlg)
+ {
+ pRefDlg->AddRefEntry();
+ }
+ }
+ }
+ }
+ else
+ {
+ ScInputHandler* pHdl = GetInputHdl();
+ if (pHdl)
+ pHdl->AddRefEntry();
+ }
+}
+
+void ScModule::EndReference()
+{
+ //TODO: Move reference dialog handling to view
+ // Just keep function autopilot here for references to other documents
+
+ // We also annul the ZoomIn again in RefDialogs
+
+ //FIXME: ShowRefFrame at InputHdl, if the Function AutoPilot is open?
+ if ( !m_nCurRefDlgId )
+ return;
+
+ SfxChildWindow* pChildWnd = nullptr;
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ pChildWnd = lcl_GetChildWinFromCurrentView( m_nCurRefDlgId );
+ else
+ pChildWnd = lcl_GetChildWinFromAnyView( m_nCurRefDlgId );
+
+ OSL_ENSURE( pChildWnd, "NoChildWin" );
+ if ( pChildWnd )
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ assert(pRefDlg);
+ if(pRefDlg)
+ {
+ pRefDlg->SetActive();
+ }
+ }
+ }
+}
+
+/**
+ * Idle/OnlineSpelling
+ */
+void ScModule::AnythingChanged()
+{
+ sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout();
+ if ( nOldTime != SC_IDLE_MIN )
+ m_aIdleTimer.SetTimeout( SC_IDLE_MIN );
+
+ nIdleCount = 0;
+}
+
+static void lcl_CheckNeedsRepaint( const ScDocShell* pDocShell )
+{
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell );
+ while ( pFrame )
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ if ( pViewSh )
+ pViewSh->CheckNeedsRepaint();
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell );
+ }
+}
+
+IMPL_LINK_NOARG(ScModule, IdleHandler, Timer *, void)
+{
+ if ( Application::AnyInput( VclInputFlags::MOUSE | VclInputFlags::KEYBOARD ) )
+ {
+ m_aIdleTimer.Start(); // Timeout unchanged
+ return;
+ }
+
+ bool bMore = false;
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(SfxObjectShell::Current());
+
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ sc::DocumentLinkManager& rLinkMgr = rDoc.GetDocLinkManager();
+ bool bLinks = rLinkMgr.idleCheckLinks();
+ bool bWidth = rDoc.IdleCalcTextWidth();
+
+ bMore = bLinks || bWidth; // Still something at all?
+
+ // While calculating a Basic formula, a paint event may have occurred,
+ // so check the bNeedsRepaint flags for this document's views
+ if (bWidth)
+ lcl_CheckNeedsRepaint( pDocSh );
+ }
+
+
+ sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout();
+ sal_uInt64 nNewTime = nOldTime;
+ if ( bMore )
+ {
+ nNewTime = SC_IDLE_MIN;
+ nIdleCount = 0;
+ }
+ else
+ {
+ // Set SC_IDLE_COUNT to initial Timeout - increase afterwards
+ if ( nIdleCount < SC_IDLE_COUNT )
+ ++nIdleCount;
+ else
+ {
+ nNewTime += SC_IDLE_STEP;
+ if ( nNewTime > SC_IDLE_MAX )
+ nNewTime = SC_IDLE_MAX;
+ }
+ }
+ if ( nNewTime != nOldTime )
+ m_aIdleTimer.SetTimeout( nNewTime );
+
+
+ m_aIdleTimer.Start();
+}
+
+/**
+ * Virtual methods for the OptionsDialog
+ */
+std::optional<SfxItemSet> ScModule::CreateItemSet( sal_uInt16 nId )
+{
+ std::optional<SfxItemSet> pRet;
+ if(SID_SC_EDITOPTIONS == nId)
+ {
+ pRet.emplace(
+ GetPool(),
+ svl::Items<
+ // TP_USERLISTS:
+ SCITEM_USERLIST, SCITEM_USERLIST,
+ // TP_GRID:
+ SID_ATTR_GRID_OPTIONS, SID_ATTR_GRID_OPTIONS,
+ SID_ATTR_METRIC, SID_ATTR_METRIC,
+ SID_ATTR_DEFTABSTOP, SID_ATTR_DEFTABSTOP,
+ // TP_INPUT:
+ SID_SC_INPUT_LEGACY_CELL_SELECTION, SID_SC_OPT_SORT_REF_UPDATE,
+ // TP_FORMULA, TP_DEFAULTS:
+ SID_SCFORMULAOPTIONS, SID_SCDEFAULTSOPTIONS,
+ // TP_VIEW, TP_CALC:
+ SID_SCVIEWOPTIONS, SID_SCDOCOPTIONS,
+ // TP_INPUT:
+ SID_SC_INPUT_ENTER_PASTE_MODE, SID_SC_INPUT_ENTER_PASTE_MODE,
+ // TP_PRINT:
+ SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS,
+ // TP_INPUT:
+ SID_SC_INPUT_SELECTION, SID_SC_INPUT_MARK_HEADER,
+ SID_SC_INPUT_TEXTWYSIWYG, SID_SC_INPUT_TEXTWYSIWYG,
+ SID_SC_INPUT_REPLCELLSWARN, SID_SC_INPUT_REPLCELLSWARN,
+ // TP_VIEW:
+ SID_SC_OPT_SYNCZOOM, SID_SC_OPT_KEY_BINDING_COMPAT,
+ SID_SC_OPT_LINKS, SID_SC_OPT_LINKS>);
+
+ const ScAppOptions& rAppOpt = GetAppOptions();
+
+ ScDocShell* pDocSh = dynamic_cast< ScDocShell *>( SfxObjectShell::Current() );
+ ScDocOptions aCalcOpt = pDocSh
+ ? pDocSh->GetDocument().GetDocOptions()
+ : GetDocOptions();
+
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() );
+ ScViewOptions aViewOpt = pViewSh
+ ? pViewSh->GetViewData().GetOptions()
+ : GetViewOptions();
+
+ ScUserListItem aULItem( SCITEM_USERLIST );
+ ScUserList* pUL = ScGlobal::GetUserList();
+
+ // SfxGetpApp()->GetOptions( aSet );
+
+ pRet->Put( SfxUInt16Item( SID_ATTR_METRIC,
+ sal::static_int_cast<sal_uInt16>(rAppOpt.GetAppMetric()) ) );
+
+ // TP_CALC
+ pRet->Put( SfxUInt16Item( SID_ATTR_DEFTABSTOP,
+ aCalcOpt.GetTabDistance()));
+ pRet->Put( ScTpCalcItem( SID_SCDOCOPTIONS, aCalcOpt ) );
+
+ // TP_VIEW
+ pRet->Put( ScTpViewItem( aViewOpt ) );
+ pRet->Put( SfxBoolItem( SID_SC_OPT_SYNCZOOM, rAppOpt.GetSynchronizeZoom() ) );
+
+ // TP_INPUT
+ const ScInputOptions& rInpOpt = GetInputOptions();
+ pRet->Put( SfxUInt16Item( SID_SC_INPUT_SELECTIONPOS,
+ rInpOpt.GetMoveDir() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_SELECTION,
+ rInpOpt.GetMoveSelection() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_EDITMODE,
+ rInpOpt.GetEnterEdit() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_FMT_EXPAND,
+ rInpOpt.GetExtendFormat() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_RANGEFINDER,
+ rInpOpt.GetRangeFinder() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_REF_EXPAND,
+ rInpOpt.GetExpandRefs() ) );
+ pRet->Put( SfxBoolItem(SID_SC_OPT_SORT_REF_UPDATE, rInpOpt.GetSortRefUpdate()));
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_MARK_HEADER,
+ rInpOpt.GetMarkHeader() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_TEXTWYSIWYG,
+ rInpOpt.GetTextWysiwyg() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_REPLCELLSWARN,
+ rInpOpt.GetReplaceCellsWarn() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_LEGACY_CELL_SELECTION,
+ rInpOpt.GetLegacyCellSelection() ) );
+ pRet->Put( SfxBoolItem( SID_SC_INPUT_ENTER_PASTE_MODE,
+ rInpOpt.GetEnterPasteMode() ) );
+
+ // RID_SC_TP_PRINT
+ pRet->Put( ScTpPrintItem( GetPrintOptions() ) );
+
+ // TP_GRID
+ pRet->Put( aViewOpt.CreateGridItem() );
+
+ // TP_USERLISTS
+ if ( pUL )
+ {
+ aULItem.SetUserList( *pUL );
+ pRet->Put(aULItem);
+ }
+
+ // TP_COMPATIBILITY
+ pRet->Put( SfxUInt16Item( SID_SC_OPT_KEY_BINDING_COMPAT,
+ rAppOpt.GetKeyBindingType() ) );
+ pRet->Put( SfxBoolItem( SID_SC_OPT_LINKS, rAppOpt.GetLinksInsertedLikeMSExcel()));
+
+ // TP_DEFAULTS
+ pRet->Put( ScTpDefaultsItem( GetDefaultsOptions() ) );
+
+ // TP_FORMULA
+ ScFormulaOptions aOptions = GetFormulaOptions();
+ if (pDocSh)
+ {
+ ScCalcConfig aConfig( aOptions.GetCalcConfig());
+ aConfig.MergeDocumentSpecific( pDocSh->GetDocument().GetCalcConfig());
+ aOptions.SetCalcConfig( aConfig);
+ }
+ pRet->Put( ScTpFormulaItem( std::move(aOptions) ) );
+ }
+ return pRet;
+}
+
+void ScModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet )
+{
+ if(SID_SC_EDITOPTIONS == nId)
+ {
+ ModifyOptions( rSet );
+ }
+}
+
+std::unique_ptr<SfxTabPage> ScModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet )
+{
+ std::unique_ptr<SfxTabPage> xRet;
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ switch(nId)
+ {
+ case SID_SC_TP_LAYOUT:
+ {
+ ::CreateTabPage ScTpLayoutOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_LAYOUT);
+ if (ScTpLayoutOptionsCreate)
+ xRet = (*ScTpLayoutOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case SID_SC_TP_CONTENT:
+ {
+ ::CreateTabPage ScTpContentOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CONTENT);
+ if (ScTpContentOptionsCreate)
+ xRet = (*ScTpContentOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case SID_SC_TP_GRID:
+ xRet = SvxGridTabPage::Create(pPage, pController, rSet);
+ break;
+ case SID_SC_TP_USERLISTS:
+ {
+ ::CreateTabPage ScTpUserListsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_USERLISTS);
+ if (ScTpUserListsCreate)
+ xRet = (*ScTpUserListsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case SID_SC_TP_CALC:
+ {
+ ::CreateTabPage ScTpCalcOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CALC);
+ if (ScTpCalcOptionsCreate)
+ xRet = (*ScTpCalcOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case SID_SC_TP_FORMULA:
+ {
+ ::CreateTabPage ScTpFormulaOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_FORMULA);
+ if (ScTpFormulaOptionsCreate)
+ xRet = (*ScTpFormulaOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case SID_SC_TP_COMPATIBILITY:
+ {
+ ::CreateTabPage ScTpCompatOptionsCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_COMPATIBILITY);
+ if (ScTpCompatOptionsCreate)
+ xRet = (*ScTpCompatOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case SID_SC_TP_CHANGES:
+ {
+ ::CreateTabPage ScRedlineOptionsTabPageCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_CHANGES);
+ if (ScRedlineOptionsTabPageCreate)
+ xRet =(*ScRedlineOptionsTabPageCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case RID_SC_TP_PRINT:
+ {
+ ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
+ if (ScTpPrintOptionsCreate)
+ xRet = (*ScTpPrintOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ case RID_SC_TP_DEFAULTS:
+ {
+ ::CreateTabPage ScTpDefaultsOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_DEFAULTS);
+ if (ScTpDefaultsOptionsCreate)
+ xRet = (*ScTpDefaultsOptionsCreate)(pPage, pController, &rSet);
+ break;
+ }
+ }
+
+ OSL_ENSURE( xRet, "ScModule::CreateTabPage(): no valid ID for TabPage!" );
+
+ return xRet;
+}
+
+IMPL_LINK( ScModule, CalcFieldValueHdl, EditFieldInfo*, pInfo, void )
+{
+ //TODO: Merge with ScFieldEditEngine!
+ if (!pInfo)
+ return;
+
+ const SvxFieldItem& rField = pInfo->GetField();
+ const SvxFieldData* pField = rField.GetField();
+
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ // URLField
+ const OUString& aURL = pURLField->GetURL();
+
+ switch ( pURLField->GetFormat() )
+ {
+ case SvxURLFormat::AppDefault: //TODO: Settable in the App?
+ case SvxURLFormat::Repr:
+ {
+ pInfo->SetRepresentation( pURLField->GetRepresentation() );
+ }
+ break;
+
+ case SvxURLFormat::Url:
+ {
+ pInfo->SetRepresentation( aURL );
+ }
+ break;
+ }
+
+ svtools::ColorConfigEntry eEntry =
+ INetURLHistory::GetOrCreate()->QueryUrl( aURL ) ? svtools::LINKSVISITED : svtools::LINKS;
+ pInfo->SetTextColor( GetColorConfig().GetColorValue(eEntry).nColor );
+ }
+ else
+ {
+ OSL_FAIL("Unknown Field");
+ pInfo->SetRepresentation(OUString('?'));
+ }
+}
+
+void ScModule::RegisterRefController(sal_uInt16 nSlotId, std::shared_ptr<SfxDialogController>& rWnd, weld::Window* pWndAncestor)
+{
+ std::vector<std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>> & rlRefWindow = m_mapRefController[nSlotId];
+
+ if (std::none_of(rlRefWindow.begin(), rlRefWindow.end(),
+ [rWnd](const std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>& rCandidate)
+ {
+ return rCandidate.first.get() == rWnd.get();
+ }))
+ {
+ rlRefWindow.emplace_back(rWnd, pWndAncestor);
+ }
+}
+
+void ScModule::UnregisterRefController(sal_uInt16 nSlotId, const std::shared_ptr<SfxDialogController>& rWnd)
+{
+ auto iSlot = m_mapRefController.find( nSlotId );
+
+ if( iSlot == m_mapRefController.end() )
+ return;
+
+ std::vector<std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>> & rlRefWindow = iSlot->second;
+
+ auto i = std::find_if(rlRefWindow.begin(), rlRefWindow.end(),
+ [rWnd](const std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>& rCandidate)
+ {
+ return rCandidate.first.get() == rWnd.get();
+ });
+
+ if( i == rlRefWindow.end() )
+ return;
+
+ rlRefWindow.erase( i );
+
+ if( rlRefWindow.empty() )
+ m_mapRefController.erase( nSlotId );
+}
+
+std::shared_ptr<SfxDialogController> ScModule::Find1RefWindow(sal_uInt16 nSlotId, const weld::Window *pWndAncestor)
+{
+ if (!pWndAncestor)
+ return nullptr;
+
+ auto iSlot = m_mapRefController.find( nSlotId );
+
+ if( iSlot == m_mapRefController.end() )
+ return nullptr;
+
+ std::vector<std::pair<std::shared_ptr<SfxDialogController>, weld::Window*>> & rlRefWindow = iSlot->second;
+
+ for (auto const& refWindow : rlRefWindow)
+ if ( refWindow.second == pWndAncestor )
+ return refWindow.first;
+
+ return nullptr;
+}
+
+using namespace com::sun::star;
+
+constexpr OUStringLiteral LINGUPROP_AUTOSPELL = u"IsSpellAuto";
+
+void ScModule::GetSpellSettings( LanguageType& rDefLang, LanguageType& rCjkLang, LanguageType& rCtlLang,
+ bool& rAutoSpell )
+{
+ // use SvtLinguConfig instead of service LinguProperties to avoid
+ // loading the linguistic component
+ SvtLinguConfig aConfig;
+
+ SvtLinguOptions aOptions;
+ aConfig.GetOptions( aOptions );
+
+ rDefLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage, css::i18n::ScriptType::LATIN);
+ rCjkLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN);
+ rCtlLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX);
+ rAutoSpell = aOptions.bIsSpellAuto;
+}
+
+void ScModule::SetAutoSpellProperty( bool bSet )
+{
+ // use SvtLinguConfig instead of service LinguProperties to avoid
+ // loading the linguistic component
+ SvtLinguConfig aConfig;
+
+ aConfig.SetProperty( LINGUPROP_AUTOSPELL, uno::Any(bSet) );
+}
+
+bool ScModule::HasThesaurusLanguage( LanguageType nLang )
+{
+ if ( nLang == LANGUAGE_NONE )
+ return false;
+
+ bool bHasLang = false;
+ try
+ {
+ uno::Reference< linguistic2::XThesaurus > xThes(LinguMgr::GetThesaurus());
+ if ( xThes.is() )
+ bHasLang = xThes->hasLocale( LanguageTag::convertToLocale( nLang ) );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL("Error in Thesaurus");
+ }
+
+ return bHasLang;
+}
+
+std::optional<SfxStyleFamilies> ScModule::CreateStyleFamilies()
+{
+ SfxStyleFamilies aStyleFamilies;
+
+ aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Para,
+ ScResId(STR_STYLE_FAMILY_CELL),
+ BMP_STYLES_FAMILY_CELL,
+ RID_CELLSTYLEFAMILY, SC_MOD()->GetResLocale()));
+
+ aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Page,
+ ScResId(STR_STYLE_FAMILY_PAGE),
+ BMP_STYLES_FAMILY_PAGE,
+ RID_PAGESTYLEFAMILY, SC_MOD()->GetResLocale()));
+
+ aStyleFamilies.emplace_back(SfxStyleFamilyItem(SfxStyleFamily::Frame,
+ ScResId(STR_STYLE_FAMILY_GRAPHICS),
+ BMP_STYLES_FAMILY_GRAPHICS,
+ RID_GRAPHICSTYLEFAMILY, SC_MOD()->GetResLocale()));
+
+ return aStyleFamilies;
+}
+
+void ScModule::RegisterAutomationApplicationEventsCaller(css::uno::Reference< ooo::vba::XSinkCaller > const& xCaller)
+{
+ mxAutomationApplicationEventsCaller = xCaller;
+}
+
+void ScModule::CallAutomationApplicationEventSinks(const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments)
+{
+ if (mxAutomationApplicationEventsCaller.is())
+ mxAutomationApplicationEventsCaller->CallSinks(Method, Arguments);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/seltrans.cxx b/sc/source/ui/app/seltrans.cxx
new file mode 100644
index 0000000000..7122afab9b
--- /dev/null
+++ b/sc/source/ui/app/seltrans.cxx
@@ -0,0 +1,429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+
+#include <tools/urlobj.hxx>
+#include <sfx2/docfile.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdouno.hxx>
+#include <osl/diagnose.h>
+
+#include <seltrans.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <scmod.hxx>
+#include <dbfunc.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include <markdata.hxx>
+
+using namespace com::sun::star;
+
+static bool lcl_IsURLButton( SdrObject* pObject )
+{
+ bool bRet = false;
+
+ SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObject );
+ if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor())
+ {
+ const uno::Reference<awt::XControlModel>& xControlModel = pUnoCtrl->GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "uno control without model" );
+ if ( xControlModel.is() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo();
+
+ OUString sPropButtonType( "ButtonType" );
+ if(xInfo->hasPropertyByName( sPropButtonType ))
+ {
+ uno::Any aAny = xPropSet->getPropertyValue( sPropButtonType );
+ form::FormButtonType eTmp;
+ if ( (aAny >>= eTmp) && eTmp == form::FormButtonType_URL )
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+rtl::Reference<ScSelectionTransferObj> ScSelectionTransferObj::CreateFromView( ScTabView* pView )
+{
+ rtl::Reference<ScSelectionTransferObj> pRet;
+
+ try
+ {
+ if ( pView )
+ {
+ ScSelectionTransferMode eMode = SC_SELTRANS_INVALID;
+
+ SdrView* pSdrView = pView->GetScDrawView();
+ if ( pSdrView )
+ {
+ // handle selection on drawing layer
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+ if ( nMarkCount )
+ {
+ if ( nMarkCount == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+
+ if ( nSdrObjKind == SdrObjKind::Graphic )
+ {
+ if ( static_cast<SdrGrafObj*>(pObj)->GetGraphic().GetType() == GraphicType::Bitmap )
+ eMode = SC_SELTRANS_DRAW_BITMAP;
+ else
+ eMode = SC_SELTRANS_DRAW_GRAPHIC;
+ }
+ else if ( nSdrObjKind == SdrObjKind::OLE2 )
+ eMode = SC_SELTRANS_DRAW_OLE;
+ else if ( lcl_IsURLButton( pObj ) )
+ eMode = SC_SELTRANS_DRAW_BOOKMARK;
+ }
+
+ if ( eMode == SC_SELTRANS_INVALID )
+ eMode = SC_SELTRANS_DRAW_OTHER; // something selected but no special selection
+ }
+ }
+ if ( eMode == SC_SELTRANS_INVALID ) // no drawing object selected
+ {
+ ScViewData& rViewData = pView->GetViewData();
+ const ScMarkData& rMark = rViewData.GetMarkData();
+ // allow MultiMarked because GetSimpleArea may be able to merge into a simple range
+ // (GetSimpleArea modifies a local copy of MarkData)
+ // Also allow simple filtered area.
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ ScRange aRange;
+ ScMarkType eMarkType = rViewData.GetSimpleArea( aRange );
+ if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
+ {
+ // only for "real" selection, cursor alone isn't used
+ if ( aRange.aStart == aRange.aEnd )
+ eMode = SC_SELTRANS_CELL;
+ else
+ eMode = SC_SELTRANS_CELLS;
+ }
+ }
+ }
+
+ if ( eMode != SC_SELTRANS_INVALID )
+ pRet = new ScSelectionTransferObj( pView, eMode );
+ }
+ }
+ catch (...)
+ {
+ }
+
+ return pRet;
+}
+
+ScSelectionTransferObj::ScSelectionTransferObj( ScTabView* pSource, ScSelectionTransferMode eNewMode ) :
+ pView( pSource ),
+ eMode( eNewMode )
+{
+ //! store range for StillValid
+}
+
+ScSelectionTransferObj::~ScSelectionTransferObj()
+{
+ ScModule* pScMod = SC_MOD();
+ if (pScMod && pScMod->GetSelectionTransfer() == this)
+ {
+ // this is reached when the object wasn't really copied to the selection
+ // (CopyToSelection has no effect under Windows)
+
+ ForgetView();
+ pScMod->SetSelectionTransfer( nullptr );
+ }
+
+ OSL_ENSURE( !pView, "ScSelectionTransferObj dtor: ForgetView not called" );
+}
+
+void ScSelectionTransferObj::ForgetView()
+{
+ pView = nullptr;
+ eMode = SC_SELTRANS_INVALID;
+
+ mxCellData.clear();
+ mxDrawData.clear();
+}
+
+void ScSelectionTransferObj::AddSupportedFormats()
+{
+ // AddSupportedFormats must work without actually creating the
+ // "real" transfer object
+
+ switch (eMode)
+ {
+ case SC_SELTRANS_CELL:
+ case SC_SELTRANS_CELLS:
+ // same formats as in ScTransferObj::AddSupportedFormats
+ AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ AddFormat( SotClipboardFormatId::HTML );
+ AddFormat( SotClipboardFormatId::SYLK );
+ AddFormat( SotClipboardFormatId::LINK );
+ AddFormat( SotClipboardFormatId::DIF );
+ AddFormat( SotClipboardFormatId::STRING );
+ AddFormat( SotClipboardFormatId::STRING_TSVC );
+ AddFormat( SotClipboardFormatId::RTF );
+ AddFormat( SotClipboardFormatId::RICHTEXT );
+ if ( eMode == SC_SELTRANS_CELL )
+ {
+ AddFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT );
+ }
+ break;
+
+ // different graphic formats as in ScDrawTransferObj::AddSupportedFormats:
+
+ case SC_SELTRANS_DRAW_BITMAP:
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::SVXB );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ break;
+
+ case SC_SELTRANS_DRAW_GRAPHIC:
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::SVXB );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ break;
+
+ case SC_SELTRANS_DRAW_BOOKMARK:
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::SOLK );
+ AddFormat( SotClipboardFormatId::STRING );
+ AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR );
+ AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK );
+ AddFormat( SotClipboardFormatId::DRAWING );
+ break;
+
+ case SC_SELTRANS_DRAW_OLE:
+ AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ break;
+
+ case SC_SELTRANS_DRAW_OTHER:
+ // other drawing objects
+ AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::DRAWING );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScSelectionTransferObj::CreateCellData()
+{
+ OSL_ENSURE( !mxCellData.is(), "CreateCellData twice" );
+ if ( pView )
+ {
+ ScViewData& rViewData = pView->GetViewData();
+ ScMarkData aNewMark( rViewData.GetMarkData() ); // use local copy for MarkToSimple
+ aNewMark.MarkToSimple();
+
+ // similar to ScViewFunctionSet::BeginDrag
+ if ( aNewMark.IsMarked() && !aNewMark.IsMultiMarked() )
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ const ScRange& aSelRange = aNewMark.GetMarkArea();
+ ScDocShellRef aDragShellRef;
+ if ( pDocSh->GetDocument().HasOLEObjectsInArea( aSelRange, &aNewMark ) )
+ {
+ aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aDragShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ // bApi = sal_True -> no error messages
+ // #i18364# bStopEdit = sal_False -> don't end edit mode
+ // (this may be called from pasting into the edit line)
+ bool bCopied = rViewData.GetView()->CopyToClip( pClipDoc.get(), false, true, true, false );
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ if ( bCopied )
+ {
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+
+ // SetDragHandlePos is not used - there is no mouse position
+ //? pTransferObj->SetVisibleTab( nTab );
+
+ SfxObjectShellRef aPersistRef( aDragShellRef.get() );
+ pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive
+
+ pTransferObj->SetDragSource( pDocSh, aNewMark );
+
+ mxCellData = pTransferObj;
+ }
+ }
+ }
+ OSL_ENSURE( mxCellData.is(), "can't create CellData" );
+}
+
+void ScSelectionTransferObj::CreateDrawData()
+{
+ OSL_ENSURE( !mxDrawData.is(), "CreateDrawData twice" );
+ if ( pView )
+ {
+ // similar to ScDrawView::BeginDrag
+
+ ScDrawView* pDrawView = pView->GetScDrawView();
+ if ( pDrawView )
+ {
+ bool bAnyOle, bOneOle;
+ const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList();
+ ScDrawView::CheckOle( rMarkList, bAnyOle, bOneOle );
+
+ ScDocShellRef aDragShellRef;
+ if (bAnyOle)
+ {
+ aDragShellRef = new ScDocShell; // Without Ref the DocShell does not live
+ aDragShellRef->DoInitNew();
+ }
+
+ ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
+ std::unique_ptr<SdrModel> pModel(pDrawView->CreateMarkedObjModel());
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ ScViewData& rViewData = pView->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) );
+
+ SfxObjectShellRef aPersistRef( aDragShellRef.get() );
+ pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive
+ pTransferObj->SetDragSource( pDrawView ); // copies selection
+
+ mxDrawData = pTransferObj;
+ }
+ }
+ OSL_ENSURE( mxDrawData.is(), "can't create DrawData" );
+}
+
+ScTransferObj* ScSelectionTransferObj::GetCellData()
+{
+ if ( !mxCellData.is() && ( eMode == SC_SELTRANS_CELL || eMode == SC_SELTRANS_CELLS ) )
+ CreateCellData();
+ return mxCellData.get();
+}
+
+ScDrawTransferObj* ScSelectionTransferObj::GetDrawData()
+{
+ if ( !mxDrawData.is() && ( eMode == SC_SELTRANS_DRAW_BITMAP || eMode == SC_SELTRANS_DRAW_GRAPHIC ||
+ eMode == SC_SELTRANS_DRAW_BOOKMARK || eMode == SC_SELTRANS_DRAW_OLE ||
+ eMode == SC_SELTRANS_DRAW_OTHER ) )
+ CreateDrawData();
+ return mxDrawData.get();
+}
+
+bool ScSelectionTransferObj::GetData(
+ const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc )
+{
+ bool bOK = false;
+
+ uno::Reference<datatransfer::XTransferable> xSource;
+ switch (eMode)
+ {
+ case SC_SELTRANS_CELL:
+ case SC_SELTRANS_CELLS:
+ xSource = GetCellData();
+ break;
+ case SC_SELTRANS_DRAW_BITMAP:
+ case SC_SELTRANS_DRAW_GRAPHIC:
+ case SC_SELTRANS_DRAW_BOOKMARK:
+ case SC_SELTRANS_DRAW_OLE:
+ case SC_SELTRANS_DRAW_OTHER:
+ xSource = GetDrawData();
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if ( xSource.is() )
+ {
+ TransferableDataHelper aHelper( xSource );
+ uno::Any aAny = aHelper.GetAny(rFlavor, rDestDoc);
+ bOK = SetAny( aAny );
+ }
+
+ return bOK;
+}
+
+void ScSelectionTransferObj::ObjectReleased()
+{
+ // called when another selection is set from outside
+
+ ForgetView();
+
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod->GetSelectionTransfer() == this )
+ pScMod->SetSelectionTransfer( nullptr );
+
+ TransferableHelper::ObjectReleased();
+}
+
+sal_Bool SAL_CALL ScSelectionTransferObj::isComplex()
+{
+ switch (eMode)
+ {
+ case SC_SELTRANS_CELL:
+ case SC_SELTRANS_CELLS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/transobj.cxx b/sc/source/ui/app/transobj.cxx
new file mode 100644
index 0000000000..6a1ef6a046
--- /dev/null
+++ b/sc/source/ui/app/transobj.cxx
@@ -0,0 +1,903 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sot/storage.hxx>
+#include <utility>
+#include <vcl/gdimtf.hxx>
+#include <vcl/jobset.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <transobj.hxx>
+#include <patattr.hxx>
+#include <cellvalue.hxx>
+#include <cellform.hxx>
+#include <document.hxx>
+#include <viewopti.hxx>
+#include <editutil.hxx>
+#include <impex.hxx>
+#include <formulacell.hxx>
+#include <printfun.hxx>
+#include <docfunc.hxx>
+#include <scmod.hxx>
+#include <dragdata.hxx>
+#include <sortparam.hxx>
+#include <tabvwsh.hxx>
+
+#include <editeng/paperinf.hxx>
+#include <editeng/sizeitem.hxx>
+#include <formula/errorcodes.hxx>
+#include <docsh.hxx>
+#include <markdata.hxx>
+#include <stlpool.hxx>
+#include <viewdata.hxx>
+#include <dociter.hxx>
+#include <cellsuno.hxx>
+#include <stringutil.hxx>
+#include <formulaiter.hxx>
+
+using namespace com::sun::star;
+
+constexpr sal_uInt32 SCTRANS_TYPE_IMPEX = 1;
+constexpr sal_uInt32 SCTRANS_TYPE_EDIT_RTF = 2;
+constexpr sal_uInt32 SCTRANS_TYPE_EDIT_BIN = 3;
+constexpr sal_uInt32 SCTRANS_TYPE_EMBOBJ = 4;
+constexpr sal_uInt32 SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT = 5;
+
+void ScTransferObj::GetAreaSize( const ScDocument& rDoc, SCTAB nTab1, SCTAB nTab2, SCROW& nRow, SCCOL& nCol )
+{
+ SCCOL nMaxCol = 0;
+ SCROW nMaxRow = 0;
+ for( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
+ {
+ SCCOL nLastCol = 0;
+ SCROW nLastRow = 0;
+ // GetPrintArea instead of GetCellArea - include drawing objects
+ if( rDoc.GetPrintArea( nTab, nLastCol, nLastRow ) )
+ {
+ if( nLastCol > nMaxCol )
+ nMaxCol = nLastCol;
+ if( nLastRow > nMaxRow )
+ nMaxRow = nLastRow;
+ }
+ }
+ nRow = nMaxRow;
+ nCol = nMaxCol;
+}
+
+void ScTransferObj::PaintToDev( OutputDevice* pDev, ScDocument& rDoc, double nPrintFactor,
+ const ScRange& rBlock )
+{
+ tools::Rectangle aBound( Point(), pDev->GetOutputSize() ); //! use size from clip area?
+
+ ScViewData aViewData(rDoc);
+
+ aViewData.SetTabNo( rBlock.aEnd.Tab() );
+ aViewData.SetScreen( rBlock.aStart.Col(), rBlock.aStart.Row(),
+ rBlock.aEnd.Col(), rBlock.aEnd.Row() );
+
+ ScPrintFunc::DrawToDev( rDoc, pDev, nPrintFactor, aBound, &aViewData, false/*bMetaFile*/ );
+}
+
+ScTransferObj::ScTransferObj( const std::shared_ptr<ScDocument>& pClipDoc, TransferableObjectDescriptor aDesc ) :
+ m_pDoc( pClipDoc ),
+ m_nNonFiltered(0),
+ m_aObjDesc(std::move( aDesc )),
+ m_nDragHandleX( 0 ),
+ m_nDragHandleY( 0 ),
+ m_nSourceCursorX( m_pDoc->MaxCol() + 1 ),
+ m_nSourceCursorY( m_pDoc->MaxRow() + 1 ),
+ m_nDragSourceFlags( ScDragSrc::Undefined ),
+ m_bDragWasInternal( false ),
+ m_bUsedForLink( false ),
+ m_bUseInApi( false )
+{
+ OSL_ENSURE(m_pDoc->IsClipboard(), "wrong document");
+
+ // get aBlock from clipboard doc
+
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ m_pDoc->GetClipStart( nCol1, nRow1 );
+ m_pDoc->GetClipArea( nCol2, nRow2, true ); // real source area - include filtered rows
+ nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nCol1 );
+ nRow2 = sal::static_int_cast<SCROW>( nRow2 + nRow1 );
+
+ SCCOL nDummy;
+ m_pDoc->GetClipArea( nDummy, m_nNonFiltered, false );
+ m_bHasFiltered = (m_nNonFiltered < (nRow2 - nRow1));
+ ++m_nNonFiltered; // to get count instead of diff
+
+ SCTAB nTab1=0;
+ SCTAB nTab2=0;
+ bool bFirst = true;
+ for (SCTAB i=0; i< m_pDoc->GetTableCount(); i++)
+ if (m_pDoc->HasTable(i))
+ {
+ if (bFirst)
+ nTab1 = i;
+ nTab2 = i;
+ bFirst = false;
+ }
+ OSL_ENSURE(!bFirst, "no sheet selected");
+
+ // only limit to used cells if whole sheet was marked
+ // (so empty cell areas can be copied)
+ if ( nCol2>=m_pDoc->MaxCol() && nRow2>=m_pDoc->MaxRow() )
+ {
+ SCROW nMaxRow;
+ SCCOL nMaxCol;
+ GetAreaSize( *m_pDoc, nTab1, nTab2, nMaxRow, nMaxCol );
+ if( nMaxRow < nRow2 )
+ nRow2 = nMaxRow;
+ if( nMaxCol < nCol2 )
+ nCol2 = nMaxCol;
+ }
+
+ m_aBlock = ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ m_nVisibleTab = nTab1; // valid table as default
+
+ tools::Rectangle aMMRect = m_pDoc->GetMMRect( nCol1,nRow1, nCol2,nRow2, nTab1 );
+ m_aObjDesc.maSize = aMMRect.GetSize();
+ PrepareOLE( m_aObjDesc );
+}
+
+ScTransferObj::~ScTransferObj()
+{
+ SolarMutexGuard aSolarGuard;
+
+ bool bIsDisposing = comphelper::LibreOfficeKit::isActive() && !ScTabViewShell::GetActiveViewShell();
+ ScModule* pScMod = SC_MOD();
+ if (pScMod && !bIsDisposing && pScMod->GetDragData().pCellTransfer == this)
+ {
+ OSL_FAIL("ScTransferObj wasn't released");
+ pScMod->ResetDragObject();
+ }
+
+ m_pDoc.reset(); // ScTransferObj is owner of clipboard document
+
+ m_aDocShellRef.clear(); // before releasing the mutex
+
+ m_aDrawPersistRef.clear(); // after the model
+
+}
+
+ScTransferObj* ScTransferObj::GetOwnClipboard(const uno::Reference<datatransfer::XTransferable2>& xTransferable)
+{
+ return dynamic_cast<ScTransferObj*>(xTransferable.get());
+}
+
+void ScTransferObj::AddSupportedFormats()
+{
+ // same formats as in ScSelectionTransferObj::AddSupportedFormats
+ AddFormat( SotClipboardFormatId::EMBED_SOURCE );
+ AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ AddFormat( SotClipboardFormatId::PNG );
+ AddFormat( SotClipboardFormatId::BITMAP );
+
+ // ScImportExport formats
+ AddFormat( SotClipboardFormatId::HTML );
+ AddFormat( SotClipboardFormatId::SYLK );
+ AddFormat( SotClipboardFormatId::LINK );
+ AddFormat( SotClipboardFormatId::DIF );
+ AddFormat( SotClipboardFormatId::STRING );
+ AddFormat( SotClipboardFormatId::STRING_TSVC );
+
+ AddFormat( SotClipboardFormatId::RTF );
+ AddFormat( SotClipboardFormatId::RICHTEXT );
+ if ( m_aBlock.aStart == m_aBlock.aEnd )
+ {
+ AddFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT );
+ }
+}
+
+static ScRange lcl_reduceBlock(const ScDocument& rDoc, ScRange aReducedBlock, bool bIncludeVisual = false)
+{
+ if ((aReducedBlock.aEnd.Col() == rDoc.MaxCol() || aReducedBlock.aEnd.Row() == rDoc.MaxRow()) &&
+ aReducedBlock.aStart.Tab() == aReducedBlock.aEnd.Tab())
+ {
+ // Shrink the block here so we don't waste time creating huge
+ // output when whole columns or rows are selected.
+
+ SCCOL nPrintAreaEndCol = 0;
+ SCROW nPrintAreaEndRow = 0;
+ if (bIncludeVisual)
+ rDoc.GetPrintArea( aReducedBlock.aStart.Tab(), nPrintAreaEndCol, nPrintAreaEndRow, true );
+
+ // Shrink the area to allow pasting to external applications.
+ // Shrink to real data area for HTML, RTF and RICHTEXT, but include
+ // all objects and top-left area for BITMAP and PNG.
+ SCCOL nStartCol = aReducedBlock.aStart.Col();
+ SCROW nStartRow = aReducedBlock.aStart.Row();
+ SCCOL nEndCol = aReducedBlock.aEnd.Col();
+ SCROW nEndRow = aReducedBlock.aEnd.Row();
+
+ if (bIncludeVisual)
+ {
+ ScDataAreaExtras aDataAreaExtras;
+ aDataAreaExtras.mbCellNotes = true;
+ aDataAreaExtras.mbCellDrawObjects = true;
+ bool bShrunk = false;
+ rDoc.ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow,
+ false, true /*bStickyTopRow*/, true /*bStickyLeftCol*/, &aDataAreaExtras);
+ aDataAreaExtras.GetOverallRange( nStartCol, nStartRow, nEndCol, nEndRow, ScDataAreaExtras::Clip::None);
+ }
+ else
+ {
+ bool bShrunk = false;
+ rDoc.ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow,
+ false, false /*bStickyTopRow*/, false /*bStickyLeftCol*/);
+ }
+
+ if ( nPrintAreaEndRow > nEndRow )
+ nEndRow = nPrintAreaEndRow;
+
+ if ( nPrintAreaEndCol > nEndCol )
+ nEndCol = nPrintAreaEndCol;
+
+ aReducedBlock = ScRange(nStartCol, nStartRow, aReducedBlock.aStart.Tab(), nEndCol, nEndRow, aReducedBlock.aEnd.Tab());
+ }
+ return aReducedBlock;
+}
+
+bool ScTransferObj::GetData( const datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+{
+ SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
+ bool bOK = false;
+
+ if( HasFormat( nFormat ) )
+ {
+ ScRange aReducedBlock = m_aBlock;
+
+ bool bReduceBlockFormat =
+ nFormat == SotClipboardFormatId::HTML
+ || nFormat == SotClipboardFormatId::RTF
+ || nFormat == SotClipboardFormatId::RICHTEXT
+ || nFormat == SotClipboardFormatId::BITMAP
+ || nFormat == SotClipboardFormatId::PNG;
+
+ const bool bIncludeVisual = (nFormat == SotClipboardFormatId::BITMAP ||
+ nFormat == SotClipboardFormatId::PNG);
+
+ if (bReduceBlockFormat)
+ aReducedBlock = lcl_reduceBlock(*m_pDoc, m_aBlock, bIncludeVisual);
+
+ if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR )
+ {
+ bOK = SetTransferableObjectDescriptor( m_aObjDesc );
+ }
+ else if ( ( nFormat == SotClipboardFormatId::RTF || nFormat == SotClipboardFormatId::RICHTEXT ||
+ nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) && m_aBlock.aStart == m_aBlock.aEnd )
+ {
+ // RTF from a single cell is handled by EditEngine
+
+ SCCOL nCol = m_aBlock.aStart.Col();
+ SCROW nRow = m_aBlock.aStart.Row();
+ SCTAB nTab = m_aBlock.aStart.Tab();
+ ScAddress aPos(nCol, nRow, nTab);
+
+ const ScPatternAttr* pPattern = m_pDoc->GetPattern( nCol, nRow, nTab );
+ ScTabEditEngine aEngine( *pPattern, m_pDoc->GetEditPool(), m_pDoc.get() );
+ ScRefCellValue aCell(*m_pDoc, aPos);
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ const EditTextObject* pObj = aCell.getEditText();
+ aEngine.SetTextCurrentDefaults(*pObj);
+ }
+ else
+ {
+ SvNumberFormatter* pFormatter = m_pDoc->GetFormatTable();
+ sal_uInt32 nNumFmt = pPattern->GetNumberFormat(pFormatter);
+ const Color* pColor;
+ OUString aText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, *m_pDoc);
+ if (!aText.isEmpty())
+ aEngine.SetTextCurrentDefaults(aText);
+ }
+
+ bOK = SetObject( &aEngine,
+ ((nFormat == SotClipboardFormatId::RTF) ? SCTRANS_TYPE_EDIT_RTF :
+ ((nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT) ?
+ SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT : SCTRANS_TYPE_EDIT_BIN)),
+ rFlavor );
+ }
+ else if ( ScImportExport::IsFormatSupported( nFormat ) || nFormat == SotClipboardFormatId::RTF
+ || nFormat == SotClipboardFormatId::RICHTEXT )
+ {
+ // if this transfer object was used to create a DDE link, filtered rows
+ // have to be included for subsequent calls (to be consistent with link data)
+ if ( nFormat == SotClipboardFormatId::LINK )
+ m_bUsedForLink = true;
+
+ bool bIncludeFiltered = m_pDoc->IsCutMode() || m_bUsedForLink;
+
+ ScImportExport aObj( *m_pDoc, aReducedBlock );
+ // Plain text ("Unformatted text") may contain embedded tabs and
+ // line breaks but is not enclosed in quotes. Which makes it
+ // unsuitable for multiple cells, especially if one of them is
+ // multi-line, but otherwise is expected behavior for plain text.
+ // For multiple cells replace embedded line breaks (and tabs) with
+ // space character, otherwise pasting would yield odd results.
+ /* XXX: it's debatable whether this is actually expected, but
+ * there's no way to satisfy all possible requirements when
+ * copy/pasting unformatted text. */
+ const bool bPlainMulti = (nFormat == SotClipboardFormatId::STRING &&
+ aReducedBlock.aStart != aReducedBlock.aEnd);
+ // Add quotes only for STRING_TSVC.
+ /* TODO: a possible future STRING_TSV should not contain embedded
+ * line breaks nor tab (separator) characters and not be quoted.
+ * A possible STRING_CSV should. */
+ ScExportTextOptions aTextOptions( ScExportTextOptions::None, 0,
+ (nFormat == SotClipboardFormatId::STRING_TSVC));
+ if ( bPlainMulti || m_bUsedForLink )
+ {
+ // For a DDE link or plain text multiple cells, convert line
+ // breaks and separators to space.
+ aTextOptions.meNewlineConversion = ScExportTextOptions::ToSpace;
+ aTextOptions.mcSeparatorConvertTo = ' ';
+ aTextOptions.mbAddQuotes = false;
+ }
+ aObj.SetExportTextOptions(aTextOptions);
+ aObj.SetFormulas( m_pDoc->GetViewOptions().GetOption( VOPT_FORMULAS ) );
+ aObj.SetIncludeFiltered( bIncludeFiltered );
+
+ // DataType depends on format type:
+
+ if ( rFlavor.DataType.equals( ::cppu::UnoType<OUString>::get() ) )
+ {
+ OUString aString;
+ if ( aObj.ExportString( aString, nFormat ) )
+ bOK = SetString( aString );
+ }
+ else if ( rFlavor.DataType.equals( cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) )
+ {
+ // SetObject converts a stream into an Int8-Sequence
+ bOK = SetObject( &aObj, SCTRANS_TYPE_IMPEX, rFlavor );
+ }
+ else
+ {
+ OSL_FAIL("unknown DataType");
+ }
+ }
+ else if ( nFormat == SotClipboardFormatId::BITMAP || nFormat == SotClipboardFormatId::PNG )
+ {
+ tools::Rectangle aMMRect = m_pDoc->GetMMRect( aReducedBlock.aStart.Col(), aReducedBlock.aStart.Row(),
+ aReducedBlock.aEnd.Col(), aReducedBlock.aEnd.Row(),
+ aReducedBlock.aStart.Tab() );
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ pVirtDev->SetOutputSizePixel(pVirtDev->LogicToPixel(aMMRect.GetSize(), MapMode(MapUnit::Map100thMM)));
+
+ PaintToDev( pVirtDev, *m_pDoc, 1.0, aReducedBlock );
+
+ pVirtDev->SetMapMode( MapMode( MapUnit::MapPixel ) );
+ BitmapEx aBmp = pVirtDev->GetBitmapEx( Point(), pVirtDev->GetOutputSize() );
+ bOK = SetBitmapEx( aBmp, rFlavor );
+ }
+ else if ( nFormat == SotClipboardFormatId::GDIMETAFILE )
+ {
+ // #i123405# Do not limit visual size calculation for metafile creation.
+ // It seems unlikely that removing the limitation causes problems since
+ // metafile creation means that no real pixel device in the needed size is
+ // created.
+ InitDocShell(false);
+
+ SfxObjectShell* pEmbObj = m_aDocShellRef.get();
+
+ // like SvEmbeddedTransfer::GetData:
+ GDIMetaFile aMtf;
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ MapMode aMapMode( pEmbObj->GetMapUnit() );
+ tools::Rectangle aVisArea( pEmbObj->GetVisArea( ASPECT_CONTENT ) );
+
+ pVDev->EnableOutput( false );
+ pVDev->SetMapMode( aMapMode );
+ aMtf.SetPrefSize( aVisArea.GetSize() );
+ aMtf.SetPrefMapMode( aMapMode );
+ aMtf.Record( pVDev );
+
+ pEmbObj->DoDraw( pVDev, Point(), aVisArea.GetSize(), JobSetup() );
+
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ bOK = SetGDIMetaFile( aMtf );
+ }
+ else if ( nFormat == SotClipboardFormatId::EMBED_SOURCE )
+ {
+ //TODO/LATER: differentiate between formats?!
+ // #i123405# Do limit visual size calculation to PageSize
+ InitDocShell(true); // set aDocShellRef
+
+ SfxObjectShell* pEmbObj = m_aDocShellRef.get();
+ bOK = SetObject( pEmbObj, SCTRANS_TYPE_EMBOBJ, rFlavor );
+ }
+ }
+ return bOK;
+}
+
+bool ScTransferObj::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId,
+ const datatransfer::DataFlavor& rFlavor )
+{
+ // called from SetObject, put data into stream
+
+ bool bRet = false;
+ switch (nUserObjectId)
+ {
+ case SCTRANS_TYPE_IMPEX:
+ {
+ ScImportExport* pImpEx = static_cast<ScImportExport*>(pUserObject);
+
+ SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
+ // mba: no BaseURL for data exchange
+ if ( pImpEx->ExportStream( *rxOStm, OUString(), nFormat ) )
+ bRet = ( rxOStm->GetError() == ERRCODE_NONE );
+ }
+ break;
+
+ case SCTRANS_TYPE_EDIT_RTF:
+ case SCTRANS_TYPE_EDIT_BIN:
+ {
+ ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject);
+ if ( nUserObjectId == SCTRANS_TYPE_EDIT_RTF )
+ {
+ pEngine->Write( *rxOStm, EETextFormat::Rtf );
+ bRet = ( rxOStm->GetError() == ERRCODE_NONE );
+ }
+ else
+ {
+ // can't use Write for EditEngine format because that would
+ // write old format without support for unicode characters.
+ // Get the data from the EditEngine's transferable instead.
+
+ sal_Int32 nParCnt = pEngine->GetParagraphCount();
+ if ( nParCnt == 0 )
+ nParCnt = 1;
+ ESelection aSel( 0, 0, nParCnt-1, pEngine->GetTextLen(nParCnt-1) );
+
+ uno::Reference<datatransfer::XTransferable> xEditTrans = pEngine->CreateTransferable( aSel );
+ TransferableDataHelper aEditHelper( xEditTrans );
+
+ bRet = aEditHelper.GetSotStorageStream( rFlavor, rxOStm );
+ }
+ }
+ break;
+
+ case SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT:
+ {
+ ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject);
+ pEngine->Write(*rxOStm, EETextFormat::Xml);
+ bRet = (rxOStm->GetError() == ERRCODE_NONE);
+ }
+ break;
+
+ case SCTRANS_TYPE_EMBOBJ:
+ {
+ // TODO/MBA: testing
+ SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pUserObject);
+ ::utl::TempFileFast aTempFile;
+ SvStream* pTempStream = aTempFile.GetStream(StreamMode::READWRITE);
+ uno::Reference< embed::XStorage > xWorkStore =
+ ::comphelper::OStorageHelper::GetStorageFromStream( new utl::OStreamWrapper(*pTempStream) );
+
+ // write document storage
+ pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false );
+
+ // mba: no relative URLs for clipboard!
+ SfxMedium aMedium( xWorkStore, OUString() );
+ pEmbObj->DoSaveObjectAs( aMedium, false );
+ pEmbObj->DoSaveCompleted();
+
+ uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY );
+ if ( xTransact.is() )
+ xTransact->commit();
+
+ rxOStm->SetBufferSize( 0xff00 );
+ rxOStm->WriteStream( *pTempStream );
+
+ bRet = true;
+
+ xWorkStore->dispose();
+ xWorkStore.clear();
+ }
+ break;
+
+ default:
+ OSL_FAIL("unknown object id");
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL ScTransferObj::isComplex()
+{
+ ScRange aReduced = lcl_reduceBlock(*m_pDoc, m_aBlock);
+ size_t nCells = (aReduced.aEnd.Col() - aReduced.aStart.Col() + 1) *
+ (aReduced.aEnd.Row() - aReduced.aStart.Row() + 1) *
+ (aReduced.aEnd.Tab() - aReduced.aStart.Tab() + 1);
+ return nCells > 1000;
+}
+
+void ScTransferObj::DragFinished( sal_Int8 nDropAction )
+{
+ if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) )
+ {
+ // move: delete source data
+ ScDocShell* pSourceSh = GetSourceDocShell();
+ if (pSourceSh)
+ {
+ ScMarkData aMarkData = GetSourceMarkData();
+ // external drag&drop doesn't copy objects, so they also aren't deleted:
+ // bApi=TRUE, don't show error messages from drag&drop
+ pSourceSh->GetDocFunc().DeleteContents( aMarkData, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, true, true );
+ }
+ }
+
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod && pScMod->GetDragData().pCellTransfer == this )
+ pScMod->ResetDragObject();
+
+ m_xDragSourceRanges = nullptr; // don't keep source after dropping
+
+ TransferDataContainer::DragFinished( nDropAction );
+}
+
+void ScTransferObj::SetDragHandlePos( SCCOL nX, SCROW nY )
+{
+ m_nDragHandleX = nX;
+ m_nDragHandleY = nY;
+}
+
+void ScTransferObj::SetSourceCursorPos( SCCOL nX, SCROW nY )
+{
+ m_nSourceCursorX = nX;
+ m_nSourceCursorY = nY;
+}
+
+bool ScTransferObj::WasSourceCursorInSelection() const
+{
+ return
+ m_nSourceCursorX >= m_aBlock.aStart.Col() && m_nSourceCursorX <= m_aBlock.aEnd.Col() &&
+ m_nSourceCursorY >= m_aBlock.aStart.Row() && m_nSourceCursorY <= m_aBlock.aEnd.Row();
+}
+
+void ScTransferObj::SetVisibleTab( SCTAB nNew )
+{
+ m_nVisibleTab = nNew;
+}
+
+void ScTransferObj::SetDrawPersist( const SfxObjectShellRef& rRef )
+{
+ m_aDrawPersistRef = rRef;
+}
+
+void ScTransferObj::SetDragSource( ScDocShell* pSourceShell, const ScMarkData& rMark )
+{
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ m_xDragSourceRanges = new ScCellRangesObj( pSourceShell, aRanges );
+}
+
+void ScTransferObj::SetDragSourceFlags(ScDragSrc nFlags)
+{
+ m_nDragSourceFlags = nFlags;
+}
+
+void ScTransferObj::SetDragWasInternal()
+{
+ m_bDragWasInternal = true;
+}
+
+void ScTransferObj::SetUseInApi( bool bSet )
+{
+ m_bUseInApi = bSet;
+}
+
+ScDocument* ScTransferObj::GetSourceDocument()
+{
+ ScDocShell* pSourceDocSh = GetSourceDocShell();
+ if (pSourceDocSh)
+ return &pSourceDocSh->GetDocument();
+ return nullptr;
+}
+
+ScDocShell* ScTransferObj::GetSourceDocShell()
+{
+ if (m_xDragSourceRanges)
+ return m_xDragSourceRanges->GetDocShell();
+
+ return nullptr; // none set
+}
+
+ScMarkData ScTransferObj::GetSourceMarkData() const
+{
+ ScMarkData aMarkData(m_pDoc->GetSheetLimits());
+ if (m_xDragSourceRanges)
+ {
+ const ScRangeList& rRanges = m_xDragSourceRanges->GetRangeList();
+ aMarkData.MarkFromRangeList( rRanges, false );
+ }
+ return aMarkData;
+}
+
+// initialize aDocShellRef with a live document from the ClipDoc
+
+// #i123405# added parameter to allow size calculation without limitation
+// to PageSize, e.g. used for Metafile creation for clipboard.
+
+void ScTransferObj::InitDocShell(bool bLimitToPageSize)
+{
+ if ( m_aDocShellRef.is() )
+ return;
+
+ ScDocShell* pDocSh = new ScDocShell;
+ m_aDocShellRef = pDocSh; // ref must be there before InitNew
+
+ pDocSh->DoInitNew();
+
+ ScDocument& rDestDoc = pDocSh->GetDocument();
+ ScMarkData aDestMark(rDestDoc.GetSheetLimits());
+ aDestMark.SelectTable( 0, true );
+
+ rDestDoc.SetDocOptions( m_pDoc->GetDocOptions() ); // #i42666#
+
+ OUString aTabName;
+ m_pDoc->GetName( m_aBlock.aStart.Tab(), aTabName );
+ rDestDoc.RenameTab( 0, aTabName );
+
+ pDocSh->MakeDrawLayer();
+
+ rDestDoc.CopyStdStylesFrom(*m_pDoc);
+
+ SCCOL nStartX = m_aBlock.aStart.Col();
+ SCROW nStartY = m_aBlock.aStart.Row();
+ SCCOL nEndX = m_aBlock.aEnd.Col();
+ SCROW nEndY = m_aBlock.aEnd.Row();
+
+ // widths / heights
+ // (must be copied before CopyFromClip, for drawing objects)
+
+ SCCOL nCol;
+ SCTAB nSrcTab = m_aBlock.aStart.Tab();
+ rDestDoc.SetLayoutRTL(0, m_pDoc->IsLayoutRTL(nSrcTab));
+ for (nCol=nStartX; nCol<=nEndX; nCol++)
+ if ( m_pDoc->ColHidden(nCol, nSrcTab) )
+ rDestDoc.ShowCol( nCol, 0, false );
+ else
+ rDestDoc.SetColWidth( nCol, 0, m_pDoc->GetColWidth( nCol, nSrcTab ) );
+
+ if (nStartY > 0)
+ {
+ // Set manual height for all previous rows so we can ensure
+ // that visible area will not change due to autoheight
+ rDestDoc.SetManualHeight(0, nStartY - 1, 0, true);
+ }
+ for (SCROW nRow = nStartY; nRow <= nEndY; ++nRow)
+ {
+ if ( m_pDoc->RowHidden(nRow, nSrcTab) )
+ rDestDoc.ShowRow( nRow, 0, false );
+ else
+ {
+ rDestDoc.SetRowHeight( nRow, 0, m_pDoc->GetOriginalHeight( nRow, nSrcTab ) );
+
+ // if height was set manually, that flag has to be copied, too
+ bool bManual = m_pDoc->IsManualRowHeight(nRow, nSrcTab);
+ rDestDoc.SetManualHeight(nRow, nRow, 0, bManual);
+ }
+ }
+
+ // cell range is copied to the original position, but on the first sheet
+ // -> bCutMode must be set
+ // pDoc is always a Clipboard-document
+
+ ScRange aDestRange( nStartX,nStartY,0, nEndX,nEndY,0 );
+ bool bWasCut = m_pDoc->IsCutMode();
+ if (!bWasCut)
+ m_pDoc->SetClipArea( aDestRange, true ); // Cut
+ rDestDoc.CopyFromClip( aDestRange, aDestMark, InsertDeleteFlags::ALL, nullptr, m_pDoc.get(), false );
+ m_pDoc->SetClipArea( aDestRange, bWasCut );
+
+ StripRefs(*m_pDoc, nStartX,nStartY, nEndX,nEndY, rDestDoc);
+
+ ScRange aMergeRange = aDestRange;
+ rDestDoc.ExtendMerge( aMergeRange, true );
+
+ m_pDoc->CopyDdeLinks( rDestDoc ); // copy values of DDE Links
+
+ // page format (grid etc) and page size (maximum size for ole object)
+
+ Size aPaperSize = SvxPaperInfo::GetPaperSize( PAPER_A4 ); // Twips
+ ScStyleSheetPool* pStylePool = m_pDoc->GetStyleSheetPool();
+ OUString aStyleName = m_pDoc->GetPageStyle( m_aBlock.aStart.Tab() );
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page );
+ if (pStyleSheet)
+ {
+ const SfxItemSet& rSourceSet = pStyleSheet->GetItemSet();
+ aPaperSize = rSourceSet.Get(ATTR_PAGE_SIZE).GetSize();
+
+ // CopyStyleFrom copies SetItems with correct pool
+ ScStyleSheetPool* pDestPool = rDestDoc.GetStyleSheetPool();
+ pDestPool->CopyStyleFrom( pStylePool, aStyleName, SfxStyleFamily::Page );
+ }
+
+ ScViewData aViewData( *pDocSh, nullptr );
+ aViewData.SetScreen( nStartX,nStartY, nEndX,nEndY );
+ aViewData.SetCurX( nStartX );
+ aViewData.SetCurY( nStartY );
+
+ rDestDoc.SetViewOptions( m_pDoc->GetViewOptions() );
+
+ // Size
+ //! get while copying sizes
+
+ tools::Long nPosX = 0;
+ tools::Long nPosY = 0;
+
+ for (nCol=0; nCol<nStartX; nCol++)
+ nPosX += rDestDoc.GetColWidth( nCol, 0 );
+ nPosY += rDestDoc.GetRowHeight( 0, nStartY-1, 0 );
+ nPosX = o3tl::convert(nPosX, o3tl::Length::twip, o3tl::Length::mm100);
+ nPosY = o3tl::convert(nPosY, o3tl::Length::twip, o3tl::Length::mm100);
+
+ aPaperSize.setWidth( aPaperSize.Width() * 2 ); // limit OLE object to double of page size
+ aPaperSize.setHeight( aPaperSize.Height() * 2 );
+
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ for (nCol=nStartX; nCol<=nEndX; nCol++)
+ {
+ tools::Long nAdd = rDestDoc.GetColWidth( nCol, 0 );
+ if ( bLimitToPageSize && nSizeX+nAdd > aPaperSize.Width() && nSizeX ) // above limit?
+ break;
+ nSizeX += nAdd;
+ }
+ for (SCROW nRow=nStartY; nRow<=nEndY; nRow++)
+ {
+ tools::Long nAdd = rDestDoc.GetRowHeight( nRow, 0 );
+ if ( bLimitToPageSize && nSizeY+nAdd > aPaperSize.Height() && nSizeY ) // above limit?
+ break;
+ nSizeY += nAdd;
+ }
+ nSizeX = o3tl::convert(nSizeX, o3tl::Length::twip, o3tl::Length::mm100);
+ nSizeY = o3tl::convert(nSizeY, o3tl::Length::twip, o3tl::Length::mm100);
+
+// pDocSh->SetVisAreaSize( Size(nSizeX,nSizeY) );
+
+ tools::Rectangle aNewArea( Point(nPosX,nPosY), Size(nSizeX,nSizeY) );
+ //TODO/LATER: why twice?!
+ //pDocSh->SvInPlaceObject::SetVisArea( aNewArea );
+ pDocSh->SetVisArea( aNewArea );
+
+ pDocSh->UpdateOle(aViewData, true);
+
+ //! SetDocumentModified?
+ if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
+ rDestDoc.UpdateChartListenerCollection();
+}
+
+SfxObjectShell* ScTransferObj::SetDrawClipDoc( bool bAnyOle, const std::shared_ptr<ScDocument>& pDoc )
+{
+ // update ScGlobal::xDrawClipDocShellRef
+
+ ScGlobal::xDrawClipDocShellRef.clear();
+ if (bAnyOle)
+ {
+ ScGlobal::xDrawClipDocShellRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS, pDoc); // there must be a ref
+ ScGlobal::xDrawClipDocShellRef->DoInitNew();
+ }
+
+ return ScGlobal::xDrawClipDocShellRef.get();
+}
+
+void ScTransferObj::StripRefs( ScDocument& rDoc,
+ SCCOL nStartX, SCROW nStartY, SCCOL nEndX, SCROW nEndY,
+ ScDocument& rDestDoc )
+{
+ // In a clipboard doc the data don't have to be on the first sheet
+
+ SCTAB nSrcTab = 0;
+ while (nSrcTab < rDoc.GetTableCount() && !rDoc.HasTable(nSrcTab))
+ ++nSrcTab;
+ SCTAB nDestTab = 0;
+ while (nDestTab < rDestDoc.GetTableCount() && !rDestDoc.HasTable(nDestTab))
+ ++nDestTab;
+
+ if (!rDoc.HasTable(nSrcTab) || !rDestDoc.HasTable(nDestTab))
+ {
+ OSL_FAIL("Sheet not found in ScTransferObj::StripRefs");
+ return;
+ }
+
+ ScRange aRef;
+
+ ScCellIterator aIter( rDoc, ScRange(nStartX, nStartY, nSrcTab, nEndX, nEndY, nSrcTab) );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = aIter.getFormulaCell();
+ bool bOut = false;
+ ScDetectiveRefIter aRefIter( rDoc, pFCell );
+ while ( !bOut && aRefIter.GetNextRef( aRef ) )
+ {
+ if ( aRef.aStart.Tab() != nSrcTab || aRef.aEnd.Tab() != nSrcTab ||
+ aRef.aStart.Col() < nStartX || aRef.aEnd.Col() > nEndX ||
+ aRef.aStart.Row() < nStartY || aRef.aEnd.Row() > nEndY )
+ bOut = true;
+ }
+ if (bOut)
+ {
+ SCCOL nCol = aIter.GetPos().Col();
+ SCROW nRow = aIter.GetPos().Row();
+
+ FormulaError nErrCode = pFCell->GetErrCode();
+ ScAddress aPos(nCol, nRow, nDestTab);
+ if (nErrCode != FormulaError::NONE)
+ {
+ if ( rDestDoc.GetAttr( nCol,nRow,nDestTab, ATTR_HOR_JUSTIFY)->GetValue() ==
+ SvxCellHorJustify::Standard )
+ rDestDoc.ApplyAttr( nCol,nRow,nDestTab,
+ SvxHorJustifyItem(SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY) );
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDestDoc.SetString(aPos, ScGlobal::GetErrorString(nErrCode), &aParam);
+ }
+ else if (pFCell->IsValue())
+ {
+ rDestDoc.SetValue(aPos, pFCell->GetValue());
+ }
+ else
+ {
+ OUString aStr = pFCell->GetString().getString();
+ if ( pFCell->IsMultilineResult() )
+ {
+ ScFieldEditEngine& rEngine = rDestDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aStr);
+ rDestDoc.SetEditText(ScAddress(nCol,nRow,nDestTab), rEngine.CreateTextObject());
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDestDoc.SetString(aPos, aStr, &aParam);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/typemap.cxx b/sc/source/ui/app/typemap.cxx
new file mode 100644
index 0000000000..0719a4ed66
--- /dev/null
+++ b/sc/source/ui/app/typemap.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 <mid.h>
+#include <editeng/memberids.h>
+#include <svx/unomid.hxx>
+
+#include <sfx2/msg.hxx>
+#include <svl/slstitm.hxx>
+#include <editeng/fontitem.hxx>
+#include <svx/hlnkitem.hxx>
+#include <svl/srchitem.hxx>
+#include <svx/postattr.hxx>
+#include <svx/statusitem.hxx>
+#include <editeng/postitem.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <svl/ptitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/algitem.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xtextit0.hxx>
+#include <svx/xftadit.hxx>
+#include <svx/xftdiit.hxx>
+#include <svx/xftstit.hxx>
+#include <svx/xftmrit.hxx>
+#include <svx/xftouit.hxx>
+#include <svx/xftshit.hxx>
+#include <svx/xftshcit.hxx>
+#include <svx/xftshxy.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <svx/drawitem.hxx>
+#include <svl/ilstitem.hxx>
+#include <svl/globalnameitem.hxx>
+#include <svx/chrtitem.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/galleryitem.hxx>
+#include <svx/sdooitm.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <sfx2/frame.hxx>
+#include <attrib.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/sdmetitm.hxx>
+
+#define avmedia_MediaItem ::avmedia::MediaItem
+
+#ifdef DISABLE_DYNLOADING
+/* Avoid clash with the ones from svx/source/form/typemap.cxx */
+#define aSfxBoolItem_Impl sc_source_ui_appl_typemap_aSfxBoolItem_Impl
+#define aSfxInt32Item_Impl sc_source_ui_appl_typemap_aSfxInt32Item_Impl
+#define aSfxStringItem_Impl sc_source_ui_appl_typemap_aSfxStringItem_Impl
+#define aSfxUInt16Item_Impl sc_source_ui_appl_typemap_aSfxUInt16Item_Impl
+#define aSfxUInt32Item_Impl sc_source_ui_appl_typemap_aSfxUInt32Item_Impl
+#define aSfxVoidItem_Impl sc_source_ui_appl_typemap_aSfxVoidItem_Impl
+#define aSvxCharReliefItem_Impl sc_source_ui_appl_typemap_aSvxCharReliefItem_Impl
+#define aSvxClipboardFormatItem_Impl sc_source_ui_appl_typemap_aSvxClipboardFormatItem_Impl
+#define aSvxColorItem_Impl sc_source_ui_appl_typemap_aSvxColorItem_Impl
+#define aSvxContourItem_Impl sc_source_ui_appl_typemap_aSvxContourItem_Impl
+#define aSvxCrossedOutItem_Impl sc_source_ui_appl_typemap_aSvxCrossedOutItem_Impl
+#define aSvxFontHeightItem_Impl sc_source_ui_appl_typemap_aSvxFontHeightItem_Impl
+#define aSvxFontItem_Impl sc_source_ui_appl_typemap_aSvxFontItem_Impl
+#define aSvxLanguageItem_Impl sc_source_ui_appl_typemap_aSvxLanguageItem_Impl
+#define aSvxPostureItem_Impl sc_source_ui_appl_typemap_aSvxPostureItem_Impl
+#define aSvxShadowedItem_Impl sc_source_ui_appl_typemap_aSvxShadowedItem_Impl
+#define aSvxUnderlineItem_Impl sc_source_ui_appl_typemap_aSvxUnderlineItem_Impl
+#define aSvxOverlineItem_Impl sc_source_ui_appl_typemap_aSvxOverlineItem_Impl
+#define aSvxWeightItem_Impl sc_source_ui_appl_typemap_aSvxWeightItem_Impl
+#endif
+
+#define SFX_TYPEMAP
+#include <scslots.hxx>
+
+#ifdef DISABLE_DYNLOADING
+#undef aSfxBoolItem_Impl
+#undef aSfxInt32Item_Impl
+#undef aSfxStringItem_Impl
+#undef aSfxUInt16Item_Impl
+#undef aSfxUInt32Item_Impl
+#undef aSfxVoidItem_Impl
+#undef aSvxCharReliefItem_Impl
+#undef aSvxClipboardFormatItem_Impl
+#undef aSvxColorItem_Impl
+#undef aSvxContourItem_Impl
+#undef aSvxCrossedOutItem_Impl
+#undef aSvxFontHeightItem_Impl
+#undef aSvxFontItem_Impl
+#undef aSvxLanguageItem_Impl
+#undef aSvxPostureItem_Impl
+#undef aSvxShadowedItem_Impl
+#undef aSvxTextLineItem_Impl
+#undef aSvxWeightItem_Impl
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/app/uiitems.cxx b/sc/source/ui/app/uiitems.cxx
new file mode 100644
index 0000000000..ca3062c9fe
--- /dev/null
+++ b/sc/source/ui/app/uiitems.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 <uiitems.hxx>
+
+#include <userlist.hxx>
+#include <dpsave.hxx>
+#include <queryparam.hxx>
+
+#include <osl/diagnose.h>
+#include <editeng/editobj.hxx>
+#include <utility>
+
+/**
+ * Status update for entry field
+ */
+ScInputStatusItem::ScInputStatusItem(
+ sal_uInt16 nWhichP, const ScAddress& rCurPos, const ScAddress& rStartPos,
+ const ScAddress& rEndPos, OUString _aString, const EditTextObject* pData ) :
+ SfxPoolItem ( nWhichP ),
+ aCursorPos ( rCurPos ),
+ aStartPos ( rStartPos ),
+ aEndPos ( rEndPos ),
+ aString (std::move( _aString )),
+ pEditData ( pData ? pData->Clone() : nullptr ),
+ mpMisspellRanges(nullptr)
+{
+}
+
+ScInputStatusItem::ScInputStatusItem( const ScInputStatusItem& rItem ) :
+ SfxPoolItem ( rItem ),
+ aCursorPos ( rItem.aCursorPos ),
+ aStartPos ( rItem.aStartPos ),
+ aEndPos ( rItem.aEndPos ),
+ aString ( rItem.aString ),
+ pEditData ( rItem.pEditData ? rItem.pEditData->Clone() : nullptr ),
+ mpMisspellRanges(rItem.mpMisspellRanges)
+{
+}
+
+ScInputStatusItem::~ScInputStatusItem()
+{
+}
+
+bool ScInputStatusItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ return (aStartPos == static_cast<const ScInputStatusItem&>(rItem).aStartPos)
+ && (aEndPos == static_cast<const ScInputStatusItem&>(rItem).aEndPos)
+ && (aCursorPos == static_cast<const ScInputStatusItem&>(rItem).aCursorPos)
+ && (aString == static_cast<const ScInputStatusItem&>(rItem).aString);
+ //TODO: Compare Edit data!
+}
+
+ScInputStatusItem* ScInputStatusItem::Clone( SfxItemPool * ) const
+{
+ return new ScInputStatusItem( *this );
+}
+
+void ScInputStatusItem::SetMisspellRanges( const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ mpMisspellRanges = pRanges;
+}
+
+// ScPaintHint was moved to hints.cxx
+
+/**
+ * Adapt Views when inserting/deleting a table
+ */
+ScTablesHint::ScTablesHint(sal_uInt16 nNewId, SCTAB nTable1, SCTAB nTable2) :
+ nId( nNewId ),
+ nTab1( nTable1 ),
+ nTab2( nTable2 )
+{
+}
+
+ScTablesHint::~ScTablesHint()
+{
+}
+
+ScIndexHint::ScIndexHint(SfxHintId nNewId, sal_uInt16 nIdx) :
+ SfxHint( nNewId ),
+ nIndex( nIdx )
+{
+}
+
+ScIndexHint::~ScIndexHint()
+{
+}
+
+/**
+ * Create new EditView for Cursorposition
+ */
+ScEditViewHint::ScEditViewHint( ScEditEngineDefaulter* pEngine, const ScAddress& rCurPos ) :
+ pEditEngine( pEngine ),
+ aCursorPos( rCurPos )
+{
+}
+
+ScEditViewHint::~ScEditViewHint()
+{
+}
+
+/**
+ * Data for the sorting dialog
+ */
+ScSortItem::ScSortItem( sal_uInt16 nWhichP,
+ ScViewData* ptrViewData,
+ const ScSortParam* pSortData ) :
+ SfxPoolItem ( nWhichP ),
+ pViewData ( ptrViewData )
+{
+ if ( pSortData ) theSortData = *pSortData;
+}
+
+ScSortItem::ScSortItem( sal_uInt16 nWhichP,
+ const ScSortParam* pSortData ) :
+ SfxPoolItem ( nWhichP ),
+ pViewData ( nullptr )
+{
+ if ( pSortData ) theSortData = *pSortData;
+}
+
+bool ScSortItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScSortItem& rOther = static_cast<const ScSortItem&>(rItem);
+
+ return ( (pViewData == rOther.pViewData)
+ && (theSortData == rOther.theSortData) );
+}
+
+ScSortItem* ScSortItem::Clone( SfxItemPool * ) const
+{
+ return new ScSortItem( *this );
+}
+
+bool ScSortItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /* nMemberUd */ ) const
+{
+ // Return empty value as there is no useful conversion
+ rVal = css::uno::Any();
+ return true;
+}
+
+/**
+ * Data for the Filter dialog
+ */
+ScQueryItem::ScQueryItem( sal_uInt16 nWhichP,
+ ScViewData* ptrViewData,
+ const ScQueryParam* pQueryData ) :
+ SfxPoolItem ( nWhichP ),
+ pViewData ( ptrViewData ),
+ bIsAdvanced ( false )
+{
+ if (pQueryData)
+ mpQueryData.reset(new ScQueryParam(*pQueryData));
+ else
+ mpQueryData.reset(new ScQueryParam);
+}
+
+ScQueryItem::ScQueryItem( sal_uInt16 nWhichP,
+ const ScQueryParam* pQueryData ) :
+ SfxPoolItem ( nWhichP ),
+ pViewData ( nullptr ),
+ bIsAdvanced ( false )
+{
+ if (pQueryData)
+ mpQueryData.reset(new ScQueryParam(*pQueryData));
+ else
+ mpQueryData.reset(new ScQueryParam);
+}
+
+ScQueryItem::ScQueryItem( const ScQueryItem& rItem ) :
+ SfxPoolItem ( rItem ),
+ mpQueryData(new ScQueryParam(*rItem.mpQueryData)),
+ pViewData ( rItem.pViewData ),
+ aAdvSource ( rItem.aAdvSource ),
+ bIsAdvanced ( rItem.bIsAdvanced )
+{
+}
+
+ScQueryItem::~ScQueryItem()
+{
+}
+
+void ScQueryItem::SetAdvancedQuerySource(const ScRange* pSource)
+{
+ if (pSource)
+ {
+ aAdvSource = *pSource;
+ bIsAdvanced = true;
+ }
+ else
+ bIsAdvanced = false;
+}
+
+const ScQueryParam& ScQueryItem::GetQueryData() const
+{
+ return *mpQueryData;
+}
+
+bool ScQueryItem::GetAdvancedQuerySource(ScRange& rSource) const
+{
+ rSource = aAdvSource;
+ return bIsAdvanced;
+}
+
+bool ScQueryItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(rItem);
+
+ return ( (pViewData == rQueryItem.pViewData)
+ && (bIsAdvanced == rQueryItem.bIsAdvanced)
+ && (aAdvSource == rQueryItem.aAdvSource)
+ && (*mpQueryData == *rQueryItem.mpQueryData) );
+}
+
+ScQueryItem* ScQueryItem::Clone( SfxItemPool * ) const
+{
+ return new ScQueryItem( *this );
+}
+
+/**
+ * Data for the SubTotal dialog
+ */
+ScSubTotalItem::ScSubTotalItem( sal_uInt16 nWhichP,
+ ScViewData* ptrViewData,
+ const ScSubTotalParam* pSubTotalData ) :
+ SfxPoolItem ( nWhichP ),
+ pViewData ( ptrViewData )
+{
+ if ( pSubTotalData ) theSubTotalData = *pSubTotalData;
+}
+
+bool ScSubTotalItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScSubTotalItem& rSTItem = static_cast<const ScSubTotalItem&>(rItem);
+
+ return ( (pViewData == rSTItem.pViewData)
+ && (theSubTotalData == rSTItem.theSubTotalData) );
+}
+
+ScSubTotalItem* ScSubTotalItem::Clone( SfxItemPool * ) const
+{
+ return new ScSubTotalItem( *this );
+}
+
+bool ScSubTotalItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /* nMemberUd */ ) const
+{
+ // Return empty value as there is no useful conversion
+ rVal = css::uno::Any();
+ return true;
+}
+
+/**
+ * Transporter for the UserLIst dialog
+ */
+ScUserListItem::ScUserListItem( sal_uInt16 nWhichP )
+ : SfxPoolItem ( nWhichP )
+{
+}
+
+ScUserListItem::ScUserListItem( const ScUserListItem& rItem )
+ : SfxPoolItem ( rItem )
+{
+ if ( rItem.pUserList )
+ pUserList.reset( new ScUserList( *(rItem.pUserList) ) );
+}
+
+ScUserListItem::~ScUserListItem()
+{
+}
+
+bool ScUserListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScUserListItem& r = static_cast<const ScUserListItem&>(rItem);
+ bool bEqual = false;
+
+ if ( !pUserList || !r.pUserList )
+ bEqual = ( !pUserList && !r.pUserList );
+ else
+ bEqual = ( *pUserList == *(r.pUserList) );
+
+ return bEqual;
+}
+
+ScUserListItem* ScUserListItem::Clone( SfxItemPool * ) const
+{
+ return new ScUserListItem( *this );
+}
+
+void ScUserListItem::SetUserList( const ScUserList& rUserList )
+{
+ pUserList.reset( new ScUserList( rUserList ) );
+}
+
+/**
+ * Data for the Consolidate dialog
+ */
+ScConsolidateItem::ScConsolidateItem(
+ sal_uInt16 nWhichP,
+ const ScConsolidateParam* pConsolidateData ) :
+ SfxPoolItem ( nWhichP )
+{
+ if ( pConsolidateData ) theConsData = *pConsolidateData;
+}
+
+bool ScConsolidateItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScConsolidateItem& rCItem = static_cast<const ScConsolidateItem&>(rItem);
+
+ return ( theConsData == rCItem.theConsData);
+}
+
+ScConsolidateItem* ScConsolidateItem::Clone( SfxItemPool * ) const
+{
+ return new ScConsolidateItem( *this );
+}
+
+/**
+ * Data for the Pivot dialog
+ */
+ScPivotItem::ScPivotItem( sal_uInt16 nWhichP, const ScDPSaveData* pData,
+ const ScRange* pRange, bool bNew ) :
+ SfxPoolItem ( nWhichP )
+{
+ // pSaveData must always exist
+ if ( pData )
+ pSaveData.reset( new ScDPSaveData(*pData) );
+ else
+ pSaveData.reset( new ScDPSaveData );
+ if ( pRange ) aDestRange = *pRange;
+ bNewSheet = bNew;
+}
+
+ScPivotItem::ScPivotItem( const ScPivotItem& rItem ) :
+ SfxPoolItem ( rItem ),
+ aDestRange ( rItem.aDestRange ),
+ bNewSheet ( rItem.bNewSheet )
+{
+ assert(rItem.pSaveData && "pSaveData");
+ pSaveData.reset( new ScDPSaveData(*rItem.pSaveData) );
+}
+
+ScPivotItem::~ScPivotItem()
+{
+}
+
+bool ScPivotItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScPivotItem& rPItem = static_cast<const ScPivotItem&>(rItem);
+ OSL_ENSURE( pSaveData && rPItem.pSaveData, "pSaveData" );
+ return ( *pSaveData == *rPItem.pSaveData &&
+ aDestRange == rPItem.aDestRange &&
+ bNewSheet == rPItem.bNewSheet );
+}
+
+ScPivotItem* ScPivotItem::Clone( SfxItemPool * ) const
+{
+ return new ScPivotItem( *this );
+}
+
+/**
+ * Data for the Solver dialog
+ */
+ScSolveItem::ScSolveItem( sal_uInt16 nWhichP,
+ const ScSolveParam* pSolveData )
+ : SfxPoolItem ( nWhichP )
+{
+ if ( pSolveData ) theSolveData = *pSolveData;
+}
+
+bool ScSolveItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScSolveItem& rPItem = static_cast<const ScSolveItem&>(rItem);
+
+ return ( theSolveData == rPItem.theSolveData );
+}
+
+ScSolveItem* ScSolveItem::Clone( SfxItemPool * ) const
+{
+ return new ScSolveItem( *this );
+}
+
+/**
+ * Data for the TabOp dialog
+ */
+ScTabOpItem::ScTabOpItem( sal_uInt16 nWhichP,
+ const ScTabOpParam* pTabOpData )
+ : SfxPoolItem ( nWhichP )
+{
+ if ( pTabOpData ) theTabOpData = *pTabOpData;
+}
+
+bool ScTabOpItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const ScTabOpItem& rPItem = static_cast<const ScTabOpItem&>(rItem);
+
+ return ( theTabOpData == rPItem.theTabOpData );
+}
+
+ScTabOpItem* ScTabOpItem::Clone( SfxItemPool * ) const
+{
+ return new ScTabOpItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/attrdlg/attrdlg.cxx b/sc/source/ui/attrdlg/attrdlg.cxx
new file mode 100644
index 0000000000..faa08bf3ca
--- /dev/null
+++ b/sc/source/ui/attrdlg/attrdlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/tabdlg.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <svl/cjkoptions.hxx>
+
+#include <tabpages.hxx>
+#include <attrdlg.hxx>
+#include <svx/dialogs.hrc>
+#include <editeng/editids.hrc>
+#include <editeng/flstitem.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+
+ScAttrDlg::ScAttrDlg(weld::Window* pParent, const SfxItemSet* pCellAttrs)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/formatcellsdialog.ui",
+ "FormatCellsDialog", pCellAttrs)
+{
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUMBERFORMAT ), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "numbers", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUMBERFORMAT ), nullptr );
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "font", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), nullptr );
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "fonteffects", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), nullptr );
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_ALIGNMENT ), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "alignment", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_ALIGNMENT ), nullptr );
+
+ if (SvtCJKOptions::IsAsianTypographyEnabled())
+ {
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PARA_ASIAN), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "asiantypography", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PARA_ASIAN), nullptr );
+ }
+ else
+ RemoveTabPage( "asiantypography" );
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), nullptr );
+ OSL_ENSURE(pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), "GetTabPageCreatorFunc fail!");
+ AddTabPage( "background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr );
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ AddTabPage( "cellprotection" , ScTabPageProtection::Create, nullptr );
+ else
+ RemoveTabPage( "cellprotection" );
+}
+
+ScAttrDlg::~ScAttrDlg()
+{
+}
+
+void ScAttrDlg::PageCreated(const OUString& rPageId, SfxTabPage& rTabPage)
+{
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ if (rPageId == "numbers")
+ {
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "font" && pDocSh)
+ {
+ const SfxPoolItem* pInfoItem = pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST );
+ SAL_WARN_IF(!pInfoItem, "sc.ui", "we should have a FontListItem normally here");
+ if (pInfoItem)
+ {
+ aSet.Put (SvxFontListItem(static_cast<const SvxFontListItem*>(pInfoItem)->GetFontList(), SID_ATTR_CHAR_FONTLIST ));
+ rTabPage.PageCreated(aSet);
+ }
+ }
+ else if (rPageId == "background")
+ {
+ rTabPage.PageCreated(aSet);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/attrdlg/scabstdlg.cxx b/sc/source/ui/attrdlg/scabstdlg.cxx
new file mode 100644
index 0000000000..a8a457c5c9
--- /dev/null
+++ b/sc/source/ui/attrdlg/scabstdlg.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 <scabstdlg.hxx>
+
+#include <osl/module.hxx>
+#include <tools/svlibrary.h>
+
+typedef ScAbstractDialogFactory* (*ScFuncPtrCreateDialogFactory)();
+
+#ifndef DISABLE_DYNLOADING
+
+extern "C" { static void thisModule() {} }
+
+#else
+
+extern "C" ScAbstractDialogFactory* ScCreateDialogFactory();
+
+#endif
+
+ScAbstractDialogFactory* ScAbstractDialogFactory::Create()
+{
+ ScFuncPtrCreateDialogFactory fp = nullptr;
+#ifndef DISABLE_DYNLOADING
+ static ::osl::Module aDialogLibrary;
+
+ if ( aDialogLibrary.is() || aDialogLibrary.loadRelative( &thisModule, SVLIBRARY("scui"),
+ SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY ) )
+ fp = reinterpret_cast<ScAbstractDialogFactory* (SAL_CALL*)()>(
+ aDialogLibrary.getFunctionSymbol( "ScCreateDialogFactory" ));
+#else
+ fp = ScCreateDialogFactory;
+#endif
+ if ( fp )
+ return fp();
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/attrdlg/scdlgfact.cxx b/sc/source/ui/attrdlg/scdlgfact.cxx
new file mode 100644
index 0000000000..050b6d9577
--- /dev/null
+++ b/sc/source/ui/attrdlg/scdlgfact.cxx
@@ -0,0 +1,1397 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include "scdlgfact.hxx"
+
+#include <scuiasciiopt.hxx>
+#include <scuiautofmt.hxx>
+#include <corodlg.hxx>
+#include <dapidata.hxx>
+#include <dapitype.hxx>
+#include <delcldlg.hxx>
+#include <delcodlg.hxx>
+#include <filldlg.hxx>
+#include <groupdlg.hxx>
+#include <inscldlg.hxx>
+#include <inscodlg.hxx>
+#include <instbdlg.hxx>
+#include <lbseldlg.hxx>
+#include <linkarea.hxx>
+#include <mtrindlg.hxx>
+#include <mvtabdlg.hxx>
+#include <namecrea.hxx>
+#include <namepast.hxx>
+#include <pfiltdlg.hxx>
+#include <pvfundlg.hxx>
+#include <dpgroupdlg.hxx>
+#include <scendlg.hxx>
+#include <shtabdlg.hxx>
+#include <strindlg.hxx>
+#include <tabbgcolordlg.hxx>
+#include <scuiimoptdlg.hxx>
+#include <attrdlg.hxx>
+#include <hfedtdlg.hxx>
+#include <styledlg.hxx>
+#include <subtdlg.hxx>
+#include <textdlgs.hxx>
+#include <sortdlg.hxx>
+#include <textimportoptions.hxx>
+#include <opredlin.hxx>
+#include <tpcalc.hxx>
+#include <tpprint.hxx>
+#include <tpstat.hxx>
+#include <tpusrlst.hxx>
+#include <tpview.hxx>
+#include <tpformula.hxx>
+#include <datafdlg.hxx>
+#include <tpcompatibility.hxx>
+#include <tpdefaults.hxx>
+#include <condformatmgr.hxx>
+#include <scres.hrc>
+#include <svx/dialogs.hrc>
+#include <sfx2/sfxdlg.hxx>
+#include <conditio.hxx>
+
+#include <vcl/virdev.hxx>
+
+short AbstractScImportAsciiDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScImportAsciiDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScAutoFormatDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScColRowLabelDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScCondFormatManagerDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScCondFormatManagerDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScDataPilotDatabaseDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScDataPilotDatabaseDlg_Impl::StartExecuteAsync(AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScDataPilotSourceTypeDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScDataPilotSourceTypeDlg_Impl::StartExecuteAsync(AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScDataPilotServiceDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScDataPilotServiceDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScDeleteCellDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScDeleteCellDlg_Impl::StartExecuteAsync(AsyncContext& rCtx)
+{
+ return ScDeleteCellDlg::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+//for dataform
+short AbstractScDataFormDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+BitmapEx AbstractScDataFormDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScDataFormDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+short AbstractScDeleteContentsDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScFillSeriesDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScGroupDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScInsertCellDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScInsertCellDlg_Impl::StartExecuteAsync(AsyncContext& rCtx)
+{
+ return ScInsertCellDlg::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScInsertContentsDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScInsertTableDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScSelEntryDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScMetricInputDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScMetricInputDlg_Impl::StartExecuteAsync(AsyncContext& rCtx)
+{
+ return ScMetricInputDlg::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScMoveTableDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+BitmapEx AbstractScMoveTableDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScMoveTableDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+short AbstractScNameCreateDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScNamePasteDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScPivotFilterDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScDPFunctionDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScDPFunctionDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScDPSubtotalDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScDPSubtotalDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScDPNumGroupDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScDPDateGroupDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScDPShowDetailDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScNewScenarioDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScShowTabDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScShowTabDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScGoToTabDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool AbstractScGoToTabDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+short AbstractScSortWarningDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScTabBgColorDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScImportOptionsDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+short AbstractScTextImportOptionsDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+AbstractScLinkedAreaDlg_Impl::~AbstractScLinkedAreaDlg_Impl()
+{
+}
+
+short AbstractScLinkedAreaDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+void AbstractScImportAsciiDlg_Impl::GetOptions( ScAsciiOptions& rOpt )
+{
+ m_xDlg->GetOptions( rOpt );
+}
+
+void AbstractScImportAsciiDlg_Impl::SaveParameters()
+{
+ m_xDlg->SaveParameters();
+}
+
+BitmapEx AbstractScImportAsciiDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScImportAsciiDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+sal_uInt16 AbstractScAutoFormatDlg_Impl::GetIndex() const
+{
+ return m_xDlg->GetIndex();
+}
+
+OUString AbstractScAutoFormatDlg_Impl::GetCurrFormatName()
+{
+ return m_xDlg->GetCurrFormatName();
+}
+
+bool AbstractScColRowLabelDlg_Impl::IsCol()
+{
+ return m_xDlg->IsCol();
+}
+
+bool AbstractScColRowLabelDlg_Impl::IsRow()
+{
+ return m_xDlg->IsRow();
+}
+
+BitmapEx AbstractScColRowLabelDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScColRowLabelDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+void AbstractScDataPilotDatabaseDlg_Impl::GetValues( ScImportSourceDesc& rDesc )
+{
+ m_xDlg->GetValues(rDesc);
+}
+
+BitmapEx AbstractScDataPilotDatabaseDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScDataPilotDatabaseDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+bool AbstractScDataPilotSourceTypeDlg_Impl::IsDatabase() const
+{
+ return m_xDlg->IsDatabase();
+}
+
+bool AbstractScDataPilotSourceTypeDlg_Impl::IsExternal() const
+{
+ return m_xDlg->IsExternal();
+}
+
+bool AbstractScDataPilotSourceTypeDlg_Impl::IsNamedRange() const
+{
+ return m_xDlg->IsNamedRange();
+}
+
+OUString AbstractScDataPilotSourceTypeDlg_Impl::GetSelectedNamedRange() const
+{
+ return m_xDlg->GetSelectedNamedRange();
+}
+
+void AbstractScDataPilotSourceTypeDlg_Impl::AppendNamedRange(const OUString& rName)
+{
+ m_xDlg->AppendNamedRange(rName);
+}
+
+BitmapEx AbstractScDataPilotSourceTypeDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScDataPilotSourceTypeDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+OUString AbstractScDataPilotServiceDlg_Impl::GetServiceName() const
+{
+ return m_xDlg->GetServiceName();
+}
+
+OUString AbstractScDataPilotServiceDlg_Impl::GetParSource() const
+{
+ return m_xDlg->GetParSource();
+}
+
+OUString AbstractScDataPilotServiceDlg_Impl::GetParName() const
+{
+ return m_xDlg->GetParName();
+}
+
+OUString AbstractScDataPilotServiceDlg_Impl::GetParUser() const
+{
+ return m_xDlg->GetParUser();
+}
+
+OUString AbstractScDataPilotServiceDlg_Impl::GetParPass() const
+{
+ return m_xDlg->GetParPass();
+}
+
+DelCellCmd AbstractScDeleteCellDlg_Impl::GetDelCellCmd() const
+{
+ return m_xDlg->GetDelCellCmd();
+}
+
+BitmapEx AbstractScDeleteCellDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScDeleteCellDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+void AbstractScDeleteContentsDlg_Impl::DisableObjects()
+{
+ m_xDlg->DisableObjects();
+}
+
+InsertDeleteFlags AbstractScDeleteContentsDlg_Impl::GetDelContentsCmdBits() const
+{
+ return m_xDlg->GetDelContentsCmdBits();
+}
+
+BitmapEx AbstractScDeleteContentsDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScDeleteContentsDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+FillDir AbstractScFillSeriesDlg_Impl::GetFillDir() const
+{
+ return m_xDlg->GetFillDir();
+}
+
+FillCmd AbstractScFillSeriesDlg_Impl::GetFillCmd() const
+{
+ return m_xDlg->GetFillCmd();
+}
+
+FillDateCmd AbstractScFillSeriesDlg_Impl::GetFillDateCmd() const
+{
+ return m_xDlg->GetFillDateCmd();
+}
+
+double AbstractScFillSeriesDlg_Impl::GetStart() const
+{
+ return m_xDlg->GetStart();
+}
+
+double AbstractScFillSeriesDlg_Impl::GetStep() const
+{
+ return m_xDlg->GetStep();
+}
+
+double AbstractScFillSeriesDlg_Impl::GetMax() const
+{
+ return m_xDlg->GetMax();
+}
+
+OUString AbstractScFillSeriesDlg_Impl::GetStartStr() const
+{
+ return m_xDlg->GetStartStr();
+}
+
+void AbstractScFillSeriesDlg_Impl::SetEdStartValEnabled(bool bFlag)
+{
+ m_xDlg->SetEdStartValEnabled(bFlag);
+}
+
+bool AbstractScGroupDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+bool AbstractScGroupDlg_Impl::GetColsChecked() const
+{
+ return m_xDlg->GetColsChecked();
+}
+
+InsCellCmd AbstractScInsertCellDlg_Impl::GetInsCellCmd() const
+{
+ return m_xDlg->GetInsCellCmd();
+}
+
+InsertDeleteFlags AbstractScInsertContentsDlg_Impl::GetInsContentsCmdBits() const
+{
+ return m_xDlg->GetInsContentsCmdBits();
+}
+
+ScPasteFunc AbstractScInsertContentsDlg_Impl::GetFormulaCmdBits() const
+{
+ return m_xDlg->GetFormulaCmdBits();
+}
+
+bool AbstractScInsertContentsDlg_Impl::IsSkipEmptyCells() const
+{
+ return m_xDlg->IsSkipEmptyCells();
+}
+
+bool AbstractScInsertContentsDlg_Impl::IsLink() const
+{
+ return m_xDlg->IsLink();
+}
+
+void AbstractScInsertContentsDlg_Impl::SetFillMode( bool bSet )
+{
+ m_xDlg->SetFillMode( bSet );
+}
+
+void AbstractScInsertContentsDlg_Impl::SetOtherDoc( bool bSet )
+{
+ m_xDlg->SetOtherDoc( bSet );
+}
+
+bool AbstractScInsertContentsDlg_Impl::IsTranspose() const
+{
+ return m_xDlg->IsTranspose();
+}
+
+void AbstractScInsertContentsDlg_Impl::SetChangeTrack( bool bSet )
+{
+ m_xDlg->SetChangeTrack( bSet );
+}
+
+void AbstractScInsertContentsDlg_Impl::SetCellShiftDisabled( CellShiftDisabledFlags nDisable )
+{
+ m_xDlg->SetCellShiftDisabled( nDisable );
+}
+
+InsCellCmd AbstractScInsertContentsDlg_Impl::GetMoveMode()
+{
+ return m_xDlg->GetMoveMode();
+}
+
+BitmapEx AbstractScInsertContentsDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScInsertContentsDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+bool AbstractScInsertTableDlg_Impl::GetTablesFromFile()
+{
+ return m_xDlg->GetTablesFromFile();
+}
+
+bool AbstractScInsertTableDlg_Impl::GetTablesAsLink()
+{
+ return m_xDlg->GetTablesAsLink();
+}
+
+const OUString* AbstractScInsertTableDlg_Impl::GetFirstTable( sal_uInt16* pN )
+{
+ return m_xDlg->GetFirstTable( pN );
+}
+
+ScDocShell* AbstractScInsertTableDlg_Impl::GetDocShellTables()
+{
+ return m_xDlg->GetDocShellTables();
+}
+
+bool AbstractScInsertTableDlg_Impl::IsTableBefore()
+{
+ return m_xDlg->IsTableBefore();
+}
+
+sal_uInt16 AbstractScInsertTableDlg_Impl::GetTableCount()
+{
+ return m_xDlg->GetTableCount();
+}
+
+const OUString* AbstractScInsertTableDlg_Impl::GetNextTable( sal_uInt16* pN )
+{
+ return m_xDlg->GetNextTable( pN );
+}
+
+BitmapEx AbstractScInsertTableDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScInsertTableDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+OUString AbstractScSelEntryDlg_Impl::GetSelectedEntry() const
+{
+ return m_xDlg->GetSelectedEntry();
+}
+
+void AbstractScLinkedAreaDlg_Impl::InitFromOldLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, const OUString& rSource,
+ sal_Int32 nRefreshDelaySeconds )
+{
+ m_xDlg->InitFromOldLink( rFile, rFilter, rOptions, rSource, nRefreshDelaySeconds);
+}
+
+OUString AbstractScLinkedAreaDlg_Impl::GetURL()
+{
+ return m_xDlg->GetURL();
+}
+
+OUString AbstractScLinkedAreaDlg_Impl::GetFilter()
+{
+ return m_xDlg->GetFilter();
+}
+
+OUString AbstractScLinkedAreaDlg_Impl::GetOptions()
+{
+ return m_xDlg->GetOptions();
+}
+
+OUString AbstractScLinkedAreaDlg_Impl::GetSource()
+{
+ return m_xDlg->GetSource();
+}
+
+sal_Int32 AbstractScLinkedAreaDlg_Impl::GetRefreshDelaySeconds()
+{
+ return m_xDlg->GetRefreshDelaySeconds();
+}
+
+std::unique_ptr<ScConditionalFormatList> AbstractScCondFormatManagerDlg_Impl::GetConditionalFormatList()
+{
+ return m_xDlg->GetConditionalFormatList();
+}
+
+bool AbstractScCondFormatManagerDlg_Impl::CondFormatsChanged() const
+{
+ return m_xDlg->CondFormatsChanged();
+}
+
+void AbstractScCondFormatManagerDlg_Impl::SetModified()
+{
+ return m_xDlg->SetModified();
+}
+
+ScConditionalFormat* AbstractScCondFormatManagerDlg_Impl::GetCondFormatSelected()
+{
+ return m_xDlg->GetCondFormatSelected();
+}
+
+int AbstractScMetricInputDlg_Impl::GetInputValue() const
+{
+ return m_xDlg->GetInputValue();
+}
+
+sal_uInt16 AbstractScMoveTableDlg_Impl::GetSelectedDocument() const
+{
+ return m_xDlg->GetSelectedDocument();
+}
+
+sal_uInt16 AbstractScMoveTableDlg_Impl::GetSelectedTable() const
+{
+ return m_xDlg->GetSelectedTable();
+}
+
+bool AbstractScMoveTableDlg_Impl::GetCopyTable() const
+{
+ return m_xDlg->GetCopyTable();
+}
+
+bool AbstractScMoveTableDlg_Impl::GetRenameTable() const
+{
+ return m_xDlg->GetRenameTable();
+}
+
+void AbstractScMoveTableDlg_Impl::GetTabNameString( OUString& rString ) const
+{
+ m_xDlg->GetTabNameString( rString );
+}
+
+void AbstractScMoveTableDlg_Impl::SetForceCopyTable()
+{
+ return m_xDlg->SetForceCopyTable();
+}
+
+void AbstractScMoveTableDlg_Impl::EnableRenameTable(bool bFlag)
+{
+ return m_xDlg->EnableRenameTable( bFlag);
+}
+
+CreateNameFlags AbstractScNameCreateDlg_Impl::GetFlags() const
+{
+ return m_xDlg->GetFlags();
+}
+
+BitmapEx AbstractScNameCreateDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScNameCreateDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+std::vector<OUString> AbstractScNamePasteDlg_Impl::GetSelectedNames() const
+{
+ return m_xDlg->GetSelectedNames();
+}
+
+const ScQueryItem& AbstractScPivotFilterDlg_Impl::GetOutputItem()
+{
+ return m_xDlg->GetOutputItem();
+}
+
+PivotFunc AbstractScDPFunctionDlg_Impl::GetFuncMask() const
+{
+ return m_xDlg->GetFuncMask();
+}
+
+void AbstractScDPFunctionDlg_Impl::Response(int nResponse)
+{
+ m_xDlg->response(nResponse);
+}
+
+css::sheet::DataPilotFieldReference AbstractScDPFunctionDlg_Impl::GetFieldRef() const
+{
+ return m_xDlg->GetFieldRef();
+}
+
+PivotFunc AbstractScDPSubtotalDlg_Impl::GetFuncMask() const
+{
+ return m_xDlg->GetFuncMask();
+}
+
+void AbstractScDPSubtotalDlg_Impl::FillLabelData( ScDPLabelData& rLabelData ) const
+{
+ m_xDlg->FillLabelData( rLabelData );
+}
+
+void AbstractScDPSubtotalDlg_Impl::Response(int nResponse)
+{
+ m_xDlg->response(nResponse);
+}
+
+ScDPNumGroupInfo AbstractScDPNumGroupDlg_Impl::GetGroupInfo() const
+{
+ return m_xDlg->GetGroupInfo();
+}
+
+ScDPNumGroupInfo AbstractScDPDateGroupDlg_Impl::GetGroupInfo() const
+{
+ return m_xDlg->GetGroupInfo();
+}
+
+sal_Int32 AbstractScDPDateGroupDlg_Impl::GetDatePart() const
+{
+ return m_xDlg->GetDatePart();
+}
+
+OUString AbstractScDPShowDetailDlg_Impl::GetDimensionName() const
+{
+ return m_xDlg->GetDimensionName();
+}
+
+void AbstractScNewScenarioDlg_Impl::SetScenarioData(
+ const OUString& rName, const OUString& rComment, const Color& rColor, ScScenarioFlags nFlags )
+{
+ m_xDlg->SetScenarioData(rName, rComment, rColor, nFlags);
+}
+
+void AbstractScNewScenarioDlg_Impl::GetScenarioData(
+ OUString& rName, OUString& rComment, Color& rColor, ScScenarioFlags& rFlags ) const
+{
+ m_xDlg->GetScenarioData(rName, rComment, rColor, rFlags);
+}
+
+void AbstractScShowTabDlg_Impl::Insert( const OUString& rString, bool bSelected )
+{
+ m_xDlg->Insert(rString, bSelected);
+}
+
+void AbstractScShowTabDlg_Impl::SetDescription(
+ const OUString& rTitle, const OUString& rFixedText,
+ const OUString& sDlgHelpId, const OUString& sLbHelpId )
+{
+ m_xDlg->SetDescription( rTitle, rFixedText, sDlgHelpId, sLbHelpId );
+}
+
+std::vector<sal_Int32> AbstractScShowTabDlg_Impl::GetSelectedRows() const
+{
+ return m_xDlg->GetSelectedRows();
+}
+
+OUString AbstractScShowTabDlg_Impl::GetEntry(sal_Int32 nPos) const
+{
+ return m_xDlg->GetEntry(nPos);
+}
+
+void AbstractScGoToTabDlg_Impl::Insert( const OUString& rString, bool bSelected )
+{
+ m_xDlg->Insert(rString, bSelected);
+}
+
+void AbstractScGoToTabDlg_Impl::SetDescription(
+ const OUString& rTitle, const OUString& rEntryLabel, const OUString& rListLabel,
+ const OUString& rDlgHelpId, const OUString& rEnHelpId, const OUString& rLbHelpId )
+{
+ m_xDlg->SetDescription( rTitle, rEntryLabel, rListLabel, rDlgHelpId, rEnHelpId, rLbHelpId );
+}
+
+OUString AbstractScGoToTabDlg_Impl::GetSelectedEntry() const
+{
+ return m_xDlg->GetSelectedEntry();
+}
+
+short AbstractScStringInputDlg_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+OUString AbstractScStringInputDlg_Impl::GetInputString() const
+{
+ return m_xDlg->GetInputString();
+}
+
+BitmapEx AbstractScStringInputDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScStringInputDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+void AbstractScTabBgColorDlg_Impl::GetSelectedColor( Color& rColor ) const
+{
+ m_xDlg->GetSelectedColor( rColor );
+}
+
+BitmapEx AbstractScTabBgColorDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScTabBgColorDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+void AbstractScImportOptionsDlg_Impl::GetImportOptions( ScImportOptions& rOptions ) const
+{
+ m_xDlg->GetImportOptions(rOptions);
+}
+
+void AbstractScImportOptionsDlg_Impl::SaveImportOptions() const
+{
+ m_xDlg->SaveImportOptions();
+}
+
+LanguageType AbstractScTextImportOptionsDlg_Impl::GetLanguageType() const
+{
+ return m_xDlg->getLanguageType();
+}
+
+bool AbstractScTextImportOptionsDlg_Impl::IsDateConversionSet() const
+{
+ return m_xDlg->isDateConversionSet();
+}
+
+bool AbstractScTextImportOptionsDlg_Impl::IsScientificConversionSet() const
+{
+ return m_xDlg->isScientificConversionSet();
+}
+
+bool AbstractScTextImportOptionsDlg_Impl::IsKeepAskingSet() const
+{
+ return m_xDlg->isKeepAskingSet();
+}
+
+BitmapEx AbstractScTextImportOptionsDlg_Impl::createScreenshot() const
+{
+ VclPtr<VirtualDevice> xDialogSurface(m_xDlg->getDialog()->screenshot());
+ return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
+}
+
+OUString AbstractScTextImportOptionsDlg_Impl::GetScreenshotId() const
+{
+ return m_xDlg->get_help_id();
+}
+
+short ScAbstractTabController_Impl::Execute()
+{
+ return m_xDlg->run();
+}
+
+bool ScAbstractTabController_Impl::StartExecuteAsync(AsyncContext &rCtx)
+{
+ return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+void ScAbstractTabController_Impl::SetCurPageId( const OUString &rName )
+{
+ m_xDlg->SetCurPageId( rName );
+}
+
+const SfxItemSet* ScAbstractTabController_Impl::GetOutputItemSet() const
+{
+ return m_xDlg->GetOutputItemSet();
+}
+
+WhichRangesContainer ScAbstractTabController_Impl::GetInputRanges(const SfxItemPool& pItem )
+{
+ return m_xDlg->GetInputRanges( pItem );
+}
+
+void ScAbstractTabController_Impl::SetInputSet( const SfxItemSet* pInSet )
+{
+ m_xDlg->SetInputSet( pInSet );
+}
+
+//From class Window.
+void ScAbstractTabController_Impl::SetText( const OUString& rStr )
+{
+ m_xDlg->set_title(rStr);
+}
+
+std::vector<OUString> ScAbstractTabController_Impl::getAllPageUIXMLDescriptions() const
+{
+ return m_xDlg->getAllPageUIXMLDescriptions();
+}
+
+bool ScAbstractTabController_Impl::selectPageByUIXMLDescription(const OUString& rUIXMLDescription)
+{
+ return m_xDlg->selectPageByUIXMLDescription(rUIXMLDescription);
+}
+
+BitmapEx ScAbstractTabController_Impl::createScreenshot() const
+{
+ return m_xDlg->createScreenshot();
+}
+
+OUString ScAbstractTabController_Impl::GetScreenshotId() const
+{
+ return m_xDlg->GetScreenshotId();
+}
+
+bool ScAsyncTabController_Impl::StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx)
+{
+ return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn);
+}
+
+void ScAsyncTabController_Impl::SetCurPageId( const OUString &rName )
+{
+ m_xDlg->SetCurPageId( rName );
+}
+
+const SfxItemSet* ScAsyncTabController_Impl::GetOutputItemSet() const
+{
+ return m_xDlg->GetOutputItemSet();
+}
+
+// =========================Factories for createdialog ===================
+VclPtr<AbstractScImportAsciiDlg> ScAbstractDialogFactory_Impl::CreateScImportAsciiDlg(weld::Window* pParent,
+ const OUString& aDatName,
+ SvStream* pInStream, ScImportAsciiCall eCall)
+{
+ return VclPtr<AbstractScImportAsciiDlg_Impl>::Create(std::make_shared<ScImportAsciiDlg>(pParent, aDatName,pInStream, eCall));
+}
+
+VclPtr<AbstractScTextImportOptionsDlg> ScAbstractDialogFactory_Impl::CreateScTextImportOptionsDlg(weld::Window* pParent)
+{
+ return VclPtr<AbstractScTextImportOptionsDlg_Impl>::Create(std::make_unique<ScTextImportOptionsDlg>(pParent));
+}
+
+VclPtr<AbstractScAutoFormatDlg> ScAbstractDialogFactory_Impl::CreateScAutoFormatDlg(weld::Window* pParent,
+ ScAutoFormat* pAutoFormat,
+ const ScAutoFormatData* pSelFormatData,
+ ScViewData& rViewData)
+{
+ return VclPtr<AbstractScAutoFormatDlg_Impl>::Create(std::make_unique<ScAutoFormatDlg>(pParent, pAutoFormat, pSelFormatData, rViewData));
+}
+
+VclPtr<AbstractScColRowLabelDlg> ScAbstractDialogFactory_Impl::CreateScColRowLabelDlg(weld::Window* pParent,
+ bool bCol, bool bRow)
+{
+ return VclPtr<AbstractScColRowLabelDlg_Impl>::Create(std::make_unique<ScColRowLabelDlg>(pParent, bCol, bRow));
+}
+
+VclPtr<AbstractScSortWarningDlg> ScAbstractDialogFactory_Impl::CreateScSortWarningDlg(weld::Window* pParent, const OUString& rExtendText, const OUString& rCurrentText)
+{
+ return VclPtr<AbstractScSortWarningDlg_Impl>::Create(std::make_unique<ScSortWarningDlg>(pParent, rExtendText, rCurrentText));
+}
+
+VclPtr<AbstractScCondFormatManagerDlg> ScAbstractDialogFactory_Impl::CreateScCondFormatMgrDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList )
+{
+ return VclPtr<AbstractScCondFormatManagerDlg_Impl>::Create(std::make_shared<ScCondFormatManagerDlg>(pParent, rDoc, pFormatList));
+}
+
+VclPtr<AbstractScDataPilotDatabaseDlg> ScAbstractDialogFactory_Impl::CreateScDataPilotDatabaseDlg(weld::Window* pParent)
+{
+ return VclPtr<AbstractScDataPilotDatabaseDlg_Impl>::Create(std::make_shared<ScDataPilotDatabaseDlg>(pParent));
+}
+
+VclPtr<AbstractScDataPilotSourceTypeDlg> ScAbstractDialogFactory_Impl::CreateScDataPilotSourceTypeDlg(
+ weld::Window* pParent, bool bEnableExternal)
+{
+ return VclPtr<AbstractScDataPilotSourceTypeDlg_Impl>::Create(std::make_shared<ScDataPilotSourceTypeDlg>(pParent, bEnableExternal));
+}
+
+VclPtr<AbstractScDataPilotServiceDlg> ScAbstractDialogFactory_Impl::CreateScDataPilotServiceDlg(weld::Window* pParent,
+ const std::vector<OUString>& rServices)
+{
+ return VclPtr<AbstractScDataPilotServiceDlg_Impl>::Create(std::make_shared<ScDataPilotServiceDlg>(pParent, rServices));
+}
+
+VclPtr<AbstractScDeleteCellDlg> ScAbstractDialogFactory_Impl::CreateScDeleteCellDlg(weld::Window* pParent,
+ bool bDisallowCellMove)
+{
+ return VclPtr<AbstractScDeleteCellDlg_Impl>::Create(std::make_unique<ScDeleteCellDlg>(pParent, bDisallowCellMove));
+}
+
+VclPtr<AbstractScDataFormDlg> ScAbstractDialogFactory_Impl::CreateScDataFormDlg(weld::Window* pParent,
+ ScTabViewShell* pTabViewShell)
+{
+ return VclPtr<AbstractScDataFormDlg_Impl>::Create(std::make_unique<ScDataFormDlg>(pParent, pTabViewShell));
+}
+
+VclPtr<AbstractScDeleteContentsDlg> ScAbstractDialogFactory_Impl::CreateScDeleteContentsDlg(weld::Window* pParent)
+{
+ return VclPtr<AbstractScDeleteContentsDlg_Impl>::Create(std::make_unique<ScDeleteContentsDlg>(pParent));
+}
+
+VclPtr<AbstractScFillSeriesDlg> ScAbstractDialogFactory_Impl::CreateScFillSeriesDlg(weld::Window* pParent,
+ ScDocument& rDocument,
+ FillDir eFillDir,
+ FillCmd eFillCmd,
+ FillDateCmd eFillDateCmd,
+ const OUString& aStartStr,
+ double fStep,
+ double fMax,
+ const SCSIZE nSelectHeight,
+ const SCSIZE nSelectWidth,
+ sal_uInt16 nPossDir)
+{
+ return VclPtr<AbstractScFillSeriesDlg_Impl>::Create(std::make_unique<ScFillSeriesDlg>(pParent, rDocument,eFillDir, eFillCmd,eFillDateCmd, aStartStr,fStep,fMax,nSelectHeight,nSelectWidth,nPossDir));
+}
+
+VclPtr<AbstractScGroupDlg> ScAbstractDialogFactory_Impl::CreateAbstractScGroupDlg(weld::Window* pParent, bool bUnGroup)
+{
+ return VclPtr<AbstractScGroupDlg_Impl>::Create(std::make_shared<ScGroupDlg>(pParent, bUnGroup, true/*bRows*/));
+}
+
+VclPtr<AbstractScInsertCellDlg> ScAbstractDialogFactory_Impl::CreateScInsertCellDlg(weld::Window* pParent,
+ bool bDisallowCellMove)
+{
+ return VclPtr<AbstractScInsertCellDlg_Impl>::Create(std::make_unique<ScInsertCellDlg>(pParent, bDisallowCellMove));
+}
+
+VclPtr<AbstractScInsertContentsDlg> ScAbstractDialogFactory_Impl::CreateScInsertContentsDlg(weld::Window* pParent,
+ const OUString* pStrTitle)
+{
+ return VclPtr<AbstractScInsertContentsDlg_Impl>::Create(std::make_unique<ScInsertContentsDlg>(pParent, pStrTitle));
+}
+
+VclPtr<AbstractScInsertTableDlg> ScAbstractDialogFactory_Impl::CreateScInsertTableDlg(weld::Window* pParent, ScViewData& rViewData,
+ SCTAB nTabCount, bool bFromFile)
+{
+ return VclPtr<AbstractScInsertTableDlg_Impl>::Create(std::make_unique<ScInsertTableDlg>(pParent, rViewData,nTabCount, bFromFile));
+}
+
+VclPtr<AbstractScSelEntryDlg> ScAbstractDialogFactory_Impl::CreateScSelEntryDlg(weld::Window* pParent,
+ const std::vector<OUString> &rEntryList)
+{
+ return VclPtr<AbstractScSelEntryDlg_Impl>::Create(std::make_unique<ScSelEntryDlg>(pParent, rEntryList));
+}
+
+VclPtr<AbstractScLinkedAreaDlg> ScAbstractDialogFactory_Impl::CreateScLinkedAreaDlg(weld::Widget* pParent)
+{
+ return VclPtr<AbstractScLinkedAreaDlg_Impl>::Create(std::make_unique<ScLinkedAreaDlg>(pParent));
+}
+
+VclPtr<AbstractScMetricInputDlg> ScAbstractDialogFactory_Impl::CreateScMetricInputDlg(weld::Window* pParent,
+ const OUString& sDialogName,
+ tools::Long nCurrent,
+ tools::Long nDefault,
+ FieldUnit eFUnit,
+ sal_uInt16 nDecimals,
+ tools::Long nMaximum ,
+ tools::Long nMinimum )
+{
+ return VclPtr<AbstractScMetricInputDlg_Impl>::Create(std::make_shared<ScMetricInputDlg>(pParent, sDialogName, nCurrent ,nDefault, eFUnit,
+ nDecimals, nMaximum , nMinimum));
+}
+
+VclPtr<AbstractScMoveTableDlg> ScAbstractDialogFactory_Impl::CreateScMoveTableDlg(weld::Window* pParent,
+ const OUString& rDefault)
+{
+ return VclPtr<AbstractScMoveTableDlg_Impl>::Create(std::make_unique<ScMoveTableDlg>(pParent, rDefault));
+}
+
+VclPtr<AbstractScNameCreateDlg> ScAbstractDialogFactory_Impl::CreateScNameCreateDlg(weld::Window * pParent, CreateNameFlags nFlags)
+{
+ return VclPtr<AbstractScNameCreateDlg_Impl>::Create(std::make_unique<ScNameCreateDlg>(pParent, nFlags));
+}
+
+VclPtr<AbstractScNamePasteDlg> ScAbstractDialogFactory_Impl::CreateScNamePasteDlg(weld::Window * pParent, ScDocShell* pShell)
+{
+ return VclPtr<AbstractScNamePasteDlg_Impl>::Create(std::make_unique<ScNamePasteDlg>(pParent, pShell));
+}
+
+VclPtr<AbstractScPivotFilterDlg> ScAbstractDialogFactory_Impl::CreateScPivotFilterDlg(weld::Window* pParent,
+ const SfxItemSet& rArgSet, sal_uInt16 nSourceTab)
+{
+ return VclPtr<AbstractScPivotFilterDlg_Impl>::Create(std::make_unique<ScPivotFilterDlg>(pParent, rArgSet, nSourceTab));
+}
+
+VclPtr<AbstractScDPFunctionDlg> ScAbstractDialogFactory_Impl::CreateScDPFunctionDlg(weld::Widget* pParent,
+ const ScDPLabelDataVector& rLabelVec,
+ const ScDPLabelData& rLabelData,
+ const ScPivotFuncData& rFuncData)
+{
+ return VclPtr<AbstractScDPFunctionDlg_Impl>::Create(std::make_shared<ScDPFunctionDlg>(pParent, rLabelVec, rLabelData, rFuncData));
+}
+
+VclPtr<AbstractScDPSubtotalDlg> ScAbstractDialogFactory_Impl::CreateScDPSubtotalDlg(weld::Widget* pParent,
+ ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData,
+ const ScPivotFuncData& rFuncData,
+ const ScDPNameVec& rDataFields)
+{
+ return VclPtr<AbstractScDPSubtotalDlg_Impl>::Create(std::make_shared<ScDPSubtotalDlg>(pParent, rDPObj, rLabelData, rFuncData, rDataFields, true/*bEnableLayout*/));
+}
+
+VclPtr<AbstractScDPNumGroupDlg> ScAbstractDialogFactory_Impl::CreateScDPNumGroupDlg(weld::Window* pParent, const ScDPNumGroupInfo& rInfo)
+{
+ return VclPtr<AbstractScDPNumGroupDlg_Impl>::Create(std::make_unique<ScDPNumGroupDlg>(pParent, rInfo));
+}
+
+VclPtr<AbstractScDPDateGroupDlg> ScAbstractDialogFactory_Impl::CreateScDPDateGroupDlg(
+ weld::Window* pParent, const ScDPNumGroupInfo& rInfo, sal_Int32 nDatePart, const Date& rNullDate)
+{
+ return VclPtr<AbstractScDPDateGroupDlg_Impl>::Create(std::make_unique<ScDPDateGroupDlg>(pParent, rInfo, nDatePart, rNullDate));
+}
+
+VclPtr<AbstractScDPShowDetailDlg> ScAbstractDialogFactory_Impl::CreateScDPShowDetailDlg (
+ weld::Window* pParent, ScDPObject& rDPObj, css::sheet::DataPilotFieldOrientation nOrient )
+{
+ return VclPtr<AbstractScDPShowDetailDlg_Impl>::Create(std::make_unique<ScDPShowDetailDlg>(pParent, rDPObj, nOrient));
+}
+
+VclPtr<AbstractScNewScenarioDlg> ScAbstractDialogFactory_Impl::CreateScNewScenarioDlg(weld::Window* pParent, const OUString& rName,
+ bool bEdit, bool bSheetProtected)
+{
+ return VclPtr<AbstractScNewScenarioDlg_Impl>::Create(std::make_unique<ScNewScenarioDlg>(pParent, rName, bEdit, bSheetProtected));
+}
+
+VclPtr<AbstractScShowTabDlg> ScAbstractDialogFactory_Impl::CreateScShowTabDlg(weld::Window* pParent)
+{
+ return VclPtr<AbstractScShowTabDlg_Impl>::Create(std::make_shared<ScShowTabDlg>(pParent));
+}
+
+VclPtr<AbstractScGoToTabDlg> ScAbstractDialogFactory_Impl::CreateScGoToTabDlg(weld::Window* pParent)
+{
+ return VclPtr<AbstractScGoToTabDlg_Impl>::Create(std::make_shared<ScGoToTabDlg>(pParent));
+}
+
+VclPtr<AbstractScStringInputDlg> ScAbstractDialogFactory_Impl::CreateScStringInputDlg(weld::Window* pParent,
+ const OUString& rTitle, const OUString& rEditTitle, const OUString& rDefault, const OUString& rHelpId,
+ const OUString& rEditHelpId)
+{
+ return VclPtr<AbstractScStringInputDlg_Impl>::Create(std::make_unique<ScStringInputDlg>(pParent, rTitle, rEditTitle,
+ rDefault, rHelpId, rEditHelpId));
+}
+
+VclPtr<AbstractScTabBgColorDlg> ScAbstractDialogFactory_Impl::CreateScTabBgColorDlg(
+ weld::Window* pParent,
+ const OUString& rTitle,
+ const OUString& rTabBgColorNoColorText,
+ const Color& rDefaultColor)
+{
+ return VclPtr<AbstractScTabBgColorDlg_Impl>::Create(std::make_unique<ScTabBgColorDlg>(pParent, rTitle, rTabBgColorNoColorText, rDefaultColor));
+}
+
+VclPtr<AbstractScImportOptionsDlg> ScAbstractDialogFactory_Impl::CreateScImportOptionsDlg(weld::Window* pParent,
+ bool bAscii,
+ const ScImportOptions* pOptions,
+ const OUString* pStrTitle,
+ bool bOnlyDbtoolsEncodings,
+ bool bImport)
+{
+ return VclPtr<AbstractScImportOptionsDlg_Impl>::Create(std::make_unique<ScImportOptionsDlg>(pParent, bAscii, pOptions, pStrTitle, true/*bMultiByte*/, bOnlyDbtoolsEncodings, bImport));
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScAttrDlg(weld::Window* pParent, const SfxItemSet* pCellAttrs)
+{
+ return VclPtr<ScAbstractTabController_Impl>::Create(std::make_shared<ScAttrDlg>(pParent, pCellAttrs));
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScHFEditDlg( weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ const OUString& rPageStyle,
+ sal_uInt16 nResId )
+{
+ std::shared_ptr<SfxTabDialogController> xDlg;
+
+ switch (nResId)
+ {
+ case RID_SCDLG_HFED_HEADER:
+ case RID_SCDLG_HFEDIT_HEADER:
+ xDlg = std::make_shared<ScHFEditHeaderDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFED_FOOTER:
+ case RID_SCDLG_HFEDIT_FOOTER:
+ xDlg = std::make_shared<ScHFEditFooterDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_SHAREDFIRSTHEADER:
+ xDlg = std::make_shared<ScHFEditSharedFirstHeaderDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_SHAREDLEFTHEADER:
+ xDlg = std::make_shared<ScHFEditSharedLeftHeaderDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_SHAREDFIRSTFOOTER:
+ xDlg = std::make_shared<ScHFEditSharedFirstFooterDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_SHAREDLEFTFOOTER:
+ xDlg = std::make_shared<ScHFEditSharedLeftFooterDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_LEFTHEADER:
+ xDlg = std::make_shared<ScHFEditLeftHeaderDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_RIGHTHEADER:
+ xDlg = std::make_shared<ScHFEditRightHeaderDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_LEFTFOOTER:
+ xDlg = std::make_shared<ScHFEditLeftFooterDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_RIGHTFOOTER:
+ xDlg = std::make_shared<ScHFEditRightFooterDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_SHDR:
+ xDlg = std::make_shared<ScHFEditSharedHeaderDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_SFTR:
+ xDlg = std::make_shared<ScHFEditSharedFooterDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ case RID_SCDLG_HFEDIT_ALL:
+ xDlg = std::make_shared<ScHFEditAllDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ default:
+ case RID_SCDLG_HFEDIT:
+ xDlg = std::make_shared<ScHFEditActiveDlg>(pParent, rCoreSet, rPageStyle);
+ break;
+ }
+
+ return xDlg ? VclPtr<ScAbstractTabController_Impl>::Create(std::move(xDlg)) : nullptr;
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ bool bPage)
+{
+ return VclPtr<ScAbstractTabController_Impl>::Create(std::make_shared<ScStyleDlg>(pParent, rStyleBase, bPage));
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScDrawStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ SdrView* pView)
+{
+ return VclPtr<ScAbstractTabController_Impl>::Create(std::make_shared<ScDrawStyleDlg>(pParent, rStyleBase, pView));
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScSubTotalDlg(weld::Window* pParent, const SfxItemSet& rArgSet)
+{
+ return VclPtr<ScAbstractTabController_Impl>::Create(std::make_shared<ScSubTotalDlg>(pParent, rArgSet));
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScCharDlg(
+ weld::Window* pParent, const SfxItemSet* pAttr, const SfxObjectShell* pDocShell, bool bDrawText)
+{
+ return VclPtr<ScAbstractTabController_Impl>::Create(std::make_shared<ScCharDlg>(pParent, pAttr, pDocShell, bDrawText));
+}
+
+VclPtr<SfxAbstractTabDialog> ScAbstractDialogFactory_Impl::CreateScParagraphDlg(
+ weld::Window* pParent, const SfxItemSet* pAttr)
+{
+ return VclPtr<ScAbstractTabController_Impl>::Create(std::make_shared<ScParagraphDlg>(pParent, pAttr));
+}
+
+std::shared_ptr<ScAsyncTabController> ScAbstractDialogFactory_Impl::CreateScSortDlg(weld::Window* pParent, const SfxItemSet* pArgSet)
+{
+ return std::make_shared<ScAsyncTabController_Impl>(std::make_shared<ScSortDlg>(pParent, pArgSet));
+}
+
+//------------------ Factories for TabPages--------------------
+CreateTabPage ScAbstractDialogFactory_Impl::GetTabPageCreatorFunc( sal_uInt16 nId )
+{
+ switch (nId)
+ {
+ case SID_SC_TP_CHANGES:
+ return ScRedlineOptionsTabPage::Create;
+ case SID_SC_TP_CALC:
+ return ScTpCalcOptions::Create;
+ case SID_SC_TP_FORMULA:
+ return ScTpFormulaOptions::Create;
+ case SID_SC_TP_COMPATIBILITY:
+ return ScTpCompatOptions::Create;
+ case RID_SC_TP_DEFAULTS:
+ return ScTpDefaultsOptions::Create;
+ case RID_SC_TP_PRINT:
+ return ScTpPrintOptions::Create;
+ case SID_SC_TP_STAT:
+ return ScDocStatPage::Create;
+ case SID_SC_TP_USERLISTS:
+ return ScTpUserLists::Create;
+ case SID_SC_TP_CONTENT:
+ return ScTpContentOptions::Create;
+ case SID_SC_TP_LAYOUT:
+ return ScTpLayoutOptions::Create;
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/attrdlg/scdlgfact.hxx b/sc/source/ui/attrdlg/scdlgfact.hxx
new file mode 100644
index 0000000000..5de1ca3783
--- /dev/null
+++ b/sc/source/ui/attrdlg/scdlgfact.hxx
@@ -0,0 +1,814 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <scabstdlg.hxx>
+#include <sfx2/sfxdlg.hxx>
+
+#include <corodlg.hxx>
+#include <condformatmgr.hxx>
+#include <dapitype.hxx>
+#include <dapidata.hxx>
+#include <datafdlg.hxx>
+#include <delcodlg.hxx>
+#include <delcldlg.hxx>
+#include <dpgroupdlg.hxx>
+#include <filldlg.hxx>
+#include <gototabdlg.hxx>
+#include <groupdlg.hxx>
+#include <linkarea.hxx>
+#include <lbseldlg.hxx>
+#include <inscldlg.hxx>
+#include <instbdlg.hxx>
+#include <inscodlg.hxx>
+#include <mtrindlg.hxx>
+#include <mvtabdlg.hxx>
+#include <namecrea.hxx>
+#include <namepast.hxx>
+#include <pfiltdlg.hxx>
+#include <pvfundlg.hxx>
+#include <shtabdlg.hxx>
+#include <scendlg.hxx>
+#include <scuiasciiopt.hxx>
+#include <scuiautofmt.hxx>
+#include <scuiimoptdlg.hxx>
+#include <sortdlg.hxx>
+#include <strindlg.hxx>
+#include <tabbgcolordlg.hxx>
+#include <textimportoptions.hxx>
+
+class AbstractScImportAsciiDlg_Impl : public AbstractScImportAsciiDlg
+{
+ std::shared_ptr<ScImportAsciiDlg> m_xDlg;
+public:
+ explicit AbstractScImportAsciiDlg_Impl(std::shared_ptr<ScImportAsciiDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual void GetOptions( ScAsciiOptions& rOpt ) override;
+ virtual void SaveParameters() override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScAutoFormatDlg_Impl : public AbstractScAutoFormatDlg
+{
+ std::unique_ptr<ScAutoFormatDlg> m_xDlg;
+public:
+ explicit AbstractScAutoFormatDlg_Impl(std::unique_ptr<ScAutoFormatDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual sal_uInt16 GetIndex() const override;
+ virtual OUString GetCurrFormatName() override;
+};
+
+class AbstractScColRowLabelDlg_Impl : public AbstractScColRowLabelDlg
+{
+ std::unique_ptr<ScColRowLabelDlg> m_xDlg;
+public:
+ explicit AbstractScColRowLabelDlg_Impl(std::unique_ptr<ScColRowLabelDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool IsCol() override;
+ virtual bool IsRow() override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScCondFormatManagerDlg_Impl : public AbstractScCondFormatManagerDlg
+{
+ std::shared_ptr<ScCondFormatManagerDlg> m_xDlg;
+public:
+ explicit AbstractScCondFormatManagerDlg_Impl(std::shared_ptr<ScCondFormatManagerDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual std::unique_ptr<ScConditionalFormatList> GetConditionalFormatList() override;
+ virtual bool CondFormatsChanged() const override;
+ virtual void SetModified() override;
+ virtual ScConditionalFormat* GetCondFormatSelected() override;
+};
+
+class AbstractScDataPilotDatabaseDlg_Impl :public AbstractScDataPilotDatabaseDlg
+{
+ std::shared_ptr<ScDataPilotDatabaseDlg> m_xDlg;
+public:
+ explicit AbstractScDataPilotDatabaseDlg_Impl(std::shared_ptr<ScDataPilotDatabaseDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(AsyncContext &) override;
+ virtual void GetValues( ScImportSourceDesc& rDesc ) override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScDataPilotSourceTypeDlg_Impl :public AbstractScDataPilotSourceTypeDlg
+{
+ std::shared_ptr<ScDataPilotSourceTypeDlg> m_xDlg;
+public:
+ explicit AbstractScDataPilotSourceTypeDlg_Impl(std::shared_ptr<ScDataPilotSourceTypeDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(AsyncContext &) override;
+ virtual bool IsDatabase() const override;
+ virtual bool IsExternal() const override;
+ virtual bool IsNamedRange() const override;
+ virtual OUString GetSelectedNamedRange() const override;
+ virtual void AppendNamedRange(const OUString& rName) override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScDataPilotServiceDlg_Impl : public AbstractScDataPilotServiceDlg
+{
+ std::shared_ptr<ScDataPilotServiceDlg> m_xDlg;
+public:
+ explicit AbstractScDataPilotServiceDlg_Impl(std::shared_ptr<ScDataPilotServiceDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(AsyncContext &) override;
+ virtual OUString GetServiceName() const override;
+ virtual OUString GetParSource() const override;
+ virtual OUString GetParName() const override;
+ virtual OUString GetParUser() const override;
+ virtual OUString GetParPass() const override;
+};
+
+class AbstractScDeleteCellDlg_Impl : public AbstractScDeleteCellDlg
+{
+ std::shared_ptr<ScDeleteCellDlg> m_xDlg;
+public:
+ explicit AbstractScDeleteCellDlg_Impl(std::unique_ptr<ScDeleteCellDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(AsyncContext& rCtx) override;
+ virtual DelCellCmd GetDelCellCmd() const override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+//for dataform
+class AbstractScDataFormDlg_Impl : public AbstractScDataFormDlg
+{
+ std::unique_ptr<ScDataFormDlg> m_xDlg;
+public:
+ explicit AbstractScDataFormDlg_Impl(std::unique_ptr<ScDataFormDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScDeleteContentsDlg_Impl : public AbstractScDeleteContentsDlg
+{
+ std::unique_ptr<ScDeleteContentsDlg> m_xDlg;
+public:
+ explicit AbstractScDeleteContentsDlg_Impl(std::unique_ptr<ScDeleteContentsDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual void DisableObjects() override;
+ virtual InsertDeleteFlags GetDelContentsCmdBits() const override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScFillSeriesDlg_Impl:public AbstractScFillSeriesDlg
+{
+ std::unique_ptr<ScFillSeriesDlg> m_xDlg;
+public:
+ explicit AbstractScFillSeriesDlg_Impl(std::unique_ptr<ScFillSeriesDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual FillDir GetFillDir() const override;
+ virtual FillCmd GetFillCmd() const override;
+ virtual FillDateCmd GetFillDateCmd() const override;
+ virtual double GetStart() const override;
+ virtual double GetStep() const override;
+ virtual double GetMax() const override;
+ virtual OUString GetStartStr() const override;
+ virtual void SetEdStartValEnabled(bool bFlag) override;
+};
+
+class AbstractScGroupDlg_Impl : public AbstractScGroupDlg
+{
+ std::shared_ptr<ScGroupDlg> m_xDlg;
+public:
+ explicit AbstractScGroupDlg_Impl(std::shared_ptr<ScGroupDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual bool GetColsChecked() const override;
+};
+
+class AbstractScInsertCellDlg_Impl : public AbstractScInsertCellDlg
+{
+ std::shared_ptr<ScInsertCellDlg> m_xDlg;
+public:
+ explicit AbstractScInsertCellDlg_Impl(std::unique_ptr<ScInsertCellDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual InsCellCmd GetInsCellCmd() const override ;
+};
+
+class AbstractScInsertContentsDlg_Impl : public AbstractScInsertContentsDlg
+{
+ std::unique_ptr<ScInsertContentsDlg> m_xDlg;
+public:
+ explicit AbstractScInsertContentsDlg_Impl(std::unique_ptr<ScInsertContentsDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual InsertDeleteFlags GetInsContentsCmdBits() const override;
+ virtual ScPasteFunc GetFormulaCmdBits() const override;
+ virtual bool IsSkipEmptyCells() const override;
+ virtual bool IsLink() const override;
+ virtual void SetFillMode( bool bSet ) override;
+ virtual void SetOtherDoc( bool bSet ) override;
+ virtual bool IsTranspose() const override;
+ virtual void SetChangeTrack( bool bSet ) override;
+ virtual void SetCellShiftDisabled( CellShiftDisabledFlags nDisable ) override;
+ virtual InsCellCmd GetMoveMode() override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScInsertTableDlg_Impl : public AbstractScInsertTableDlg
+{
+ std::unique_ptr<ScInsertTableDlg> m_xDlg;
+public:
+ explicit AbstractScInsertTableDlg_Impl(std::unique_ptr<ScInsertTableDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool GetTablesFromFile() override;
+ virtual bool GetTablesAsLink() override;
+ virtual const OUString* GetFirstTable( sal_uInt16* pN = nullptr ) override;
+ virtual ScDocShell* GetDocShellTables() override;
+ virtual bool IsTableBefore() override;
+ virtual sal_uInt16 GetTableCount() override;
+ virtual const OUString* GetNextTable( sal_uInt16* pN ) override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScSelEntryDlg_Impl : public AbstractScSelEntryDlg
+{
+ std::unique_ptr<ScSelEntryDlg> m_xDlg;
+public:
+ explicit AbstractScSelEntryDlg_Impl(std::unique_ptr<ScSelEntryDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual OUString GetSelectedEntry() const override;
+};
+
+class AbstractScLinkedAreaDlg_Impl : public AbstractScLinkedAreaDlg
+{
+ std::unique_ptr<ScLinkedAreaDlg> m_xDlg;
+public:
+ explicit AbstractScLinkedAreaDlg_Impl(std::unique_ptr<ScLinkedAreaDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual ~AbstractScLinkedAreaDlg_Impl() override;
+ virtual short Execute() override;
+ virtual void InitFromOldLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, const OUString& rSource,
+ sal_Int32 nRefreshDelaySeconds ) override;
+ virtual OUString GetURL() override;
+ virtual OUString GetFilter() override; // may be empty
+ virtual OUString GetOptions() override; // filter options
+ virtual OUString GetSource() override; // separated by ";"
+ virtual sal_Int32 GetRefreshDelaySeconds() override; // 0 if disabled
+};
+
+class AbstractScMetricInputDlg_Impl : public AbstractScMetricInputDlg
+{
+ std::shared_ptr<ScMetricInputDlg> m_xDlg;
+public:
+ explicit AbstractScMetricInputDlg_Impl(std::shared_ptr<ScMetricInputDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(AsyncContext& rCtx) override;
+ virtual int GetInputValue() const override;
+};
+
+class AbstractScMoveTableDlg_Impl : public AbstractScMoveTableDlg
+{
+ std::unique_ptr<ScMoveTableDlg> m_xDlg;
+public:
+ explicit AbstractScMoveTableDlg_Impl(std::unique_ptr<ScMoveTableDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual sal_uInt16 GetSelectedDocument () const override;
+ virtual sal_uInt16 GetSelectedTable () const override;
+ virtual bool GetCopyTable () const override;
+ virtual bool GetRenameTable () const override;
+ virtual void GetTabNameString( OUString& rString ) const override;
+ virtual void SetForceCopyTable () override;
+ virtual void EnableRenameTable (bool bFlag) override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScNameCreateDlg_Impl : public AbstractScNameCreateDlg
+{
+ std::unique_ptr<ScNameCreateDlg> m_xDlg;
+public:
+ explicit AbstractScNameCreateDlg_Impl(std::unique_ptr<ScNameCreateDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual CreateNameFlags GetFlags() const override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScNamePasteDlg_Impl : public AbstractScNamePasteDlg
+{
+ std::unique_ptr<ScNamePasteDlg> m_xDlg;
+public:
+ explicit AbstractScNamePasteDlg_Impl(std::unique_ptr<ScNamePasteDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual std::vector<OUString> GetSelectedNames() const override;
+};
+
+class AbstractScPivotFilterDlg_Impl : public AbstractScPivotFilterDlg
+{
+ std::unique_ptr<ScPivotFilterDlg> m_xDlg;
+public:
+ explicit AbstractScPivotFilterDlg_Impl(std::unique_ptr<ScPivotFilterDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual const ScQueryItem& GetOutputItem() override;
+};
+
+class AbstractScDPFunctionDlg_Impl : public AbstractScDPFunctionDlg
+{
+ std::shared_ptr<ScDPFunctionDlg> m_xDlg;
+public:
+ explicit AbstractScDPFunctionDlg_Impl(std::shared_ptr<ScDPFunctionDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual PivotFunc GetFuncMask() const override;
+ virtual css::sheet::DataPilotFieldReference GetFieldRef() const override;
+ virtual void Response(int nResponse) override;
+};
+
+class AbstractScDPSubtotalDlg_Impl : public AbstractScDPSubtotalDlg
+{
+ std::shared_ptr<ScDPSubtotalDlg> m_xDlg;
+public:
+ explicit AbstractScDPSubtotalDlg_Impl(std::shared_ptr<ScDPSubtotalDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual PivotFunc GetFuncMask() const override;
+ virtual void FillLabelData( ScDPLabelData& rLabelData ) const override;
+ virtual void Response(int nResponse) override;
+};
+
+class AbstractScDPNumGroupDlg_Impl : public AbstractScDPNumGroupDlg
+{
+ std::unique_ptr<ScDPNumGroupDlg> m_xDlg;
+public:
+ explicit AbstractScDPNumGroupDlg_Impl(std::unique_ptr<ScDPNumGroupDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual ScDPNumGroupInfo GetGroupInfo() const override;
+};
+
+class AbstractScDPDateGroupDlg_Impl : public AbstractScDPDateGroupDlg
+{
+ std::unique_ptr<ScDPDateGroupDlg> m_xDlg;
+public:
+ explicit AbstractScDPDateGroupDlg_Impl(std::unique_ptr<ScDPDateGroupDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual ScDPNumGroupInfo GetGroupInfo() const override;
+ virtual sal_Int32 GetDatePart() const override;
+};
+
+class AbstractScDPShowDetailDlg_Impl : public AbstractScDPShowDetailDlg
+{
+ std::unique_ptr<ScDPShowDetailDlg> m_xDlg;
+public:
+ explicit AbstractScDPShowDetailDlg_Impl(std::unique_ptr<ScDPShowDetailDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual OUString GetDimensionName() const override;
+};
+
+class AbstractScNewScenarioDlg_Impl : public AbstractScNewScenarioDlg
+{
+ std::unique_ptr<ScNewScenarioDlg> m_xDlg;
+public:
+ explicit AbstractScNewScenarioDlg_Impl(std::unique_ptr<ScNewScenarioDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+
+ virtual void SetScenarioData( const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags ) override;
+
+ virtual void GetScenarioData( OUString& rName, OUString& rComment,
+ Color& rColor, ScScenarioFlags& rFlags ) const override;
+};
+
+class AbstractScShowTabDlg_Impl : public AbstractScShowTabDlg
+{
+ std::shared_ptr<ScShowTabDlg> m_xDlg;
+public:
+ explicit AbstractScShowTabDlg_Impl(std::shared_ptr<ScShowTabDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual void Insert( const OUString& rString, bool bSelected ) override;
+ virtual void SetDescription(const OUString& rTitle, const OUString& rFixedText, const OUString& sDlgHelpId, const OUString& sLbHelpId) override;
+ virtual OUString GetEntry(sal_Int32 nPos) const override;
+ virtual std::vector<sal_Int32> GetSelectedRows() const override;
+};
+
+class AbstractScGoToTabDlg_Impl : public AbstractScGoToTabDlg
+{
+ std::shared_ptr<ScGoToTabDlg> m_xDlg;
+public:
+ explicit AbstractScGoToTabDlg_Impl(std::shared_ptr<ScGoToTabDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual void Insert( const OUString& rString, bool bSelected ) override;
+ virtual void SetDescription(const OUString& rTitle, const OUString& rEntryLabel, const OUString& rListLabel,
+ const OUString& rDlgHelpId, const OUString& rEnHelpId, const OUString& rLbHelpId) override;
+ virtual OUString GetSelectedEntry() const override;
+};
+
+class AbstractScSortWarningDlg_Impl : public AbstractScSortWarningDlg
+{
+ std::unique_ptr<ScSortWarningDlg> m_xDlg;
+public:
+ explicit AbstractScSortWarningDlg_Impl(std::unique_ptr<ScSortWarningDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+};
+
+class AbstractScStringInputDlg_Impl : public AbstractScStringInputDlg
+{
+ std::unique_ptr<ScStringInputDlg> m_xDlg;
+public:
+ explicit AbstractScStringInputDlg_Impl(std::unique_ptr<ScStringInputDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual OUString GetInputString() const override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScTabBgColorDlg_Impl : public AbstractScTabBgColorDlg
+{
+ std::unique_ptr<ScTabBgColorDlg> m_xDlg;
+public:
+ explicit AbstractScTabBgColorDlg_Impl(std::unique_ptr<ScTabBgColorDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual void GetSelectedColor( Color& rColor ) const override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class AbstractScImportOptionsDlg_Impl : public AbstractScImportOptionsDlg
+{
+ std::unique_ptr<ScImportOptionsDlg> m_xDlg;
+public:
+ explicit AbstractScImportOptionsDlg_Impl(std::unique_ptr<ScImportOptionsDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual void GetImportOptions( ScImportOptions& rOptions ) const override;
+ virtual void SaveImportOptions() const override;
+};
+
+class AbstractScTextImportOptionsDlg_Impl : public AbstractScTextImportOptionsDlg
+{
+ std::unique_ptr<ScTextImportOptionsDlg> m_xDlg;
+public:
+ explicit AbstractScTextImportOptionsDlg_Impl(std::unique_ptr<ScTextImportOptionsDlg> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual LanguageType GetLanguageType() const override;
+ virtual bool IsDateConversionSet() const override;
+ virtual bool IsScientificConversionSet() const override;
+ virtual bool IsKeepAskingSet() const override;
+
+ // screenshotting
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class ScAbstractTabController_Impl : public SfxAbstractTabDialog
+{
+ std::shared_ptr<SfxTabDialogController> m_xDlg;
+public:
+ explicit ScAbstractTabController_Impl(std::shared_ptr<SfxTabDialogController> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual short Execute() override;
+ virtual bool StartExecuteAsync(AsyncContext &rCtx) override;
+ virtual void SetCurPageId( const OUString &rName ) override;
+ virtual const SfxItemSet* GetOutputItemSet() const override;
+ virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override;
+ virtual void SetInputSet( const SfxItemSet* pInSet ) override;
+ virtual void SetText( const OUString& rStr ) override;
+
+ // screenshotting
+ virtual std::vector<OUString> getAllPageUIXMLDescriptions() const override;
+ virtual bool selectPageByUIXMLDescription(const OUString& rUIXMLDescription) override;
+ virtual BitmapEx createScreenshot() const override;
+ virtual OUString GetScreenshotId() const override;
+};
+
+class ScAsyncTabController_Impl : public ScAsyncTabController
+{
+ std::shared_ptr<SfxTabDialogController> m_xDlg;
+public:
+ explicit ScAsyncTabController_Impl(std::shared_ptr<SfxTabDialogController> p)
+ : m_xDlg(std::move(p))
+ {
+ }
+ virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override;
+ virtual const SfxItemSet* GetOutputItemSet() const override;
+ virtual void SetCurPageId( const OUString &rName ) override;
+};
+
+//AbstractDialogFactory_Impl implementations
+class ScAbstractDialogFactory_Impl : public ScAbstractDialogFactory
+{
+
+public:
+ virtual ~ScAbstractDialogFactory_Impl() {}
+
+ virtual VclPtr<AbstractScImportAsciiDlg> CreateScImportAsciiDlg(weld::Window* pParent,
+ const OUString& aDatName,
+ SvStream* pInStream,
+ ScImportAsciiCall eCall) override;
+
+ virtual VclPtr<AbstractScTextImportOptionsDlg> CreateScTextImportOptionsDlg(weld::Window* pParent) override;
+
+ virtual VclPtr<AbstractScAutoFormatDlg> CreateScAutoFormatDlg(weld::Window* pParent,
+ ScAutoFormat* pAutoFormat,
+ const ScAutoFormatData* pSelFormatData,
+ ScViewData& rViewData) override;
+ virtual VclPtr<AbstractScColRowLabelDlg> CreateScColRowLabelDlg (weld::Window* pParent,
+ bool bCol,
+ bool bRow) override;
+
+ virtual VclPtr<AbstractScSortWarningDlg> CreateScSortWarningDlg(weld::Window* pParent, const OUString& rExtendText, const OUString& rCurrentText ) override;
+
+ virtual VclPtr<AbstractScCondFormatManagerDlg> CreateScCondFormatMgrDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList ) override;
+
+ virtual VclPtr<AbstractScDataPilotDatabaseDlg> CreateScDataPilotDatabaseDlg(weld::Window* pParent) override;
+
+ virtual VclPtr<AbstractScDataPilotSourceTypeDlg> CreateScDataPilotSourceTypeDlg(weld::Window* pParent,
+ bool bEnableExternal) override;
+
+ virtual VclPtr<AbstractScDataPilotServiceDlg> CreateScDataPilotServiceDlg(weld::Window* pParent,
+ const std::vector<OUString>& rServices) override;
+ virtual VclPtr<AbstractScDeleteCellDlg> CreateScDeleteCellDlg(weld::Window* pParent, bool bDisallowCellMove ) override;
+
+ //for dataform
+ virtual VclPtr<AbstractScDataFormDlg> CreateScDataFormDlg(weld::Window* pParent, ScTabViewShell* pTabViewShell) override;
+
+ virtual VclPtr<AbstractScDeleteContentsDlg> CreateScDeleteContentsDlg(weld::Window* pParent) override;
+
+ virtual VclPtr<AbstractScFillSeriesDlg> CreateScFillSeriesDlg(weld::Window* pParent,
+ ScDocument& rDocument,
+ FillDir eFillDir,
+ FillCmd eFillCmd,
+ FillDateCmd eFillDateCmd,
+ const OUString& aStartStr,
+ double fStep,
+ double fMax,
+ SCSIZE nSelectHeight,
+ SCSIZE nSelectWidth,
+ sal_uInt16 nPossDir) override;
+ virtual VclPtr<AbstractScGroupDlg> CreateAbstractScGroupDlg(weld::Window* pParent, bool bUnGroup = false) override;
+
+ virtual VclPtr<AbstractScInsertCellDlg> CreateScInsertCellDlg(weld::Window* pParent,
+ bool bDisallowCellMove) override;
+
+ virtual VclPtr<AbstractScInsertContentsDlg> CreateScInsertContentsDlg(weld::Window* pParent,
+ const OUString* pStrTitle = nullptr) override;
+
+ virtual VclPtr<AbstractScInsertTableDlg> CreateScInsertTableDlg(weld::Window* pParent, ScViewData& rViewData,
+ SCTAB nTabCount, bool bFromFile) override;
+
+ virtual VclPtr<AbstractScSelEntryDlg> CreateScSelEntryDlg(weld::Window* pParent, const std::vector<OUString> &rEntryList) override;
+
+ virtual VclPtr<AbstractScLinkedAreaDlg> CreateScLinkedAreaDlg(weld::Widget* pParent) override;
+
+ virtual VclPtr<AbstractScMetricInputDlg> CreateScMetricInputDlg(weld::Window* pParent,
+ const OUString& sDialogName,
+ tools::Long nCurrent,
+ tools::Long nDefault,
+ FieldUnit eFUnit,
+ sal_uInt16 nDecimals,
+ tools::Long nMaximum,
+ tools::Long nMinimum = 0 ) override;
+
+ virtual VclPtr<AbstractScMoveTableDlg> CreateScMoveTableDlg(weld::Window * pParent,
+ const OUString& rDefault) override;
+
+ virtual VclPtr<AbstractScNameCreateDlg> CreateScNameCreateDlg(weld::Window * pParent,
+ CreateNameFlags nFlags) override;
+
+ virtual VclPtr<AbstractScNamePasteDlg> CreateScNamePasteDlg(weld::Window * pParent, ScDocShell* pShell) override;
+
+ virtual VclPtr<AbstractScPivotFilterDlg> CreateScPivotFilterDlg(weld::Window* pParent, const SfxItemSet& rArgSet,
+ sal_uInt16 nSourceTab) override;
+
+ virtual VclPtr<AbstractScDPFunctionDlg> CreateScDPFunctionDlg(weld::Widget* pParent,
+ const ScDPLabelDataVector& rLabelVec,
+ const ScDPLabelData& rLabelData,
+ const ScPivotFuncData& rFuncData ) override;
+
+ virtual VclPtr<AbstractScDPSubtotalDlg> CreateScDPSubtotalDlg(weld::Widget* pParent,
+ ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData,
+ const ScPivotFuncData& rFuncData,
+ const ScDPNameVec& rDataFields ) override;
+
+ virtual VclPtr<AbstractScDPNumGroupDlg> CreateScDPNumGroupDlg(weld::Window* pParent,
+ const ScDPNumGroupInfo& rInfo) override;
+
+ virtual VclPtr<AbstractScDPDateGroupDlg> CreateScDPDateGroupDlg(weld::Window* pParent,
+ const ScDPNumGroupInfo& rInfo,
+ sal_Int32 nDatePart,
+ const Date& rNullDate) override;
+
+ virtual VclPtr<AbstractScDPShowDetailDlg> CreateScDPShowDetailDlg(weld::Window* pParent,
+ ScDPObject& rDPObj,
+ css::sheet::DataPilotFieldOrientation nOrient) override;
+
+ virtual VclPtr<AbstractScNewScenarioDlg> CreateScNewScenarioDlg(weld::Window* pParent, const OUString& rName,
+ bool bEdit, bool bSheetProtected) override;
+ virtual VclPtr<AbstractScShowTabDlg> CreateScShowTabDlg(weld::Window* pParent) override;
+ virtual VclPtr<AbstractScGoToTabDlg> CreateScGoToTabDlg(weld::Window* pParent) override;
+
+ virtual VclPtr<AbstractScStringInputDlg> CreateScStringInputDlg(weld::Window* pParent,
+ const OUString& rTitle,
+ const OUString& rEditTitle,
+ const OUString& rDefault,
+ const OUString& rHelpId,
+ const OUString& rEditHelpId) override;
+
+ virtual VclPtr<AbstractScTabBgColorDlg> CreateScTabBgColorDlg(weld::Window* pParent,
+ const OUString& rTitle, //Dialog Title
+ const OUString& rTabBgColorNoColorText, //Label for no tab color
+ const Color& rDefaultColor) override; //Currently selected Color
+
+ virtual VclPtr<AbstractScImportOptionsDlg> CreateScImportOptionsDlg(weld::Window* pParent, bool bAscii,
+ const ScImportOptions* pOptions,
+ const OUString* pStrTitle,
+ bool bOnlyDbtoolsEncodings,
+ bool bImport = true) override;
+
+ virtual VclPtr<SfxAbstractTabDialog> CreateScAttrDlg(weld::Window* pParent,
+ const SfxItemSet* pCellAttrs) override;
+
+ virtual VclPtr<SfxAbstractTabDialog> CreateScHFEditDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ const OUString& rPageStyle,
+ sal_uInt16 nResId ) override;
+
+ virtual VclPtr<SfxAbstractTabDialog> CreateScStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ bool bPage) override;
+
+ virtual VclPtr<SfxAbstractTabDialog> CreateScDrawStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ SdrView* pView) override;
+
+ virtual VclPtr<SfxAbstractTabDialog> CreateScSubTotalDlg(weld::Window* pParent,
+ const SfxItemSet& rArgSet) override;
+ virtual VclPtr<SfxAbstractTabDialog> CreateScCharDlg(weld::Window* pParent,
+ const SfxItemSet* pAttr, const SfxObjectShell* pDocShell, bool bDrawText) override;
+
+ virtual VclPtr<SfxAbstractTabDialog> CreateScParagraphDlg(weld::Window* pParent,
+ const SfxItemSet* pAttr) override;
+
+ virtual std::shared_ptr<ScAsyncTabController> CreateScSortDlg(weld::Window* pParent, const SfxItemSet* pArgSet) override;
+
+ // For TabPage
+ virtual CreateTabPage GetTabPageCreatorFunc( sal_uInt16 nId ) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/attrdlg/scuiexp.cxx b/sc/source/ui/attrdlg/scuiexp.cxx
new file mode 100644
index 0000000000..4351539a90
--- /dev/null
+++ b/sc/source/ui/attrdlg/scuiexp.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include "scdlgfact.hxx"
+#include <sal/types.h>
+
+extern "C" {
+SAL_DLLPUBLIC_EXPORT ScAbstractDialogFactory* ScCreateDialogFactory()
+{
+ static ScAbstractDialogFactory_Impl aFactory;
+ return &aFactory;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/attrdlg/tabpages.cxx b/sc/source/ui/attrdlg/tabpages.cxx
new file mode 100644
index 0000000000..af0ed1d6ab
--- /dev/null
+++ b/sc/source/ui/attrdlg/tabpages.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <attrib.hxx>
+#include <sc.hrc>
+
+#include <tabpages.hxx>
+#include <osl/diagnose.h>
+
+const WhichRangesContainer ScTabPageProtection::pProtectionRanges(
+ svl::Items<SID_SCATTR_PROTECTION, SID_SCATTR_PROTECTION>);
+
+// Zellschutz-Tabpage:
+
+ScTabPageProtection::ScTabPageProtection(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/cellprotectionpage.ui", "CellProtectionPage", &rCoreAttrs)
+ , m_xBtnHideCell(m_xBuilder->weld_check_button("checkHideAll"))
+ , m_xBtnProtect(m_xBuilder->weld_check_button("checkProtected"))
+ , m_xBtnHideFormula(m_xBuilder->weld_check_button("checkHideFormula"))
+ , m_xBtnHidePrint(m_xBuilder->weld_check_button("checkHidePrinting"))
+{
+ // This Page need ExchangeSupport
+ SetExchangeSupport();
+
+ // States will be set in Reset
+ bTriEnabled = bDontCare = bProtect = bHideForm = bHideCell = bHidePrint = false;
+
+ m_xBtnProtect->connect_toggled(LINK(this, ScTabPageProtection, ProtectClickHdl));
+ m_xBtnHideCell->connect_toggled(LINK(this, ScTabPageProtection, HideCellClickHdl));
+ m_xBtnHideFormula->connect_toggled(LINK(this, ScTabPageProtection, HideFormulaClickHdl));
+ m_xBtnHidePrint->connect_toggled(LINK(this, ScTabPageProtection, HidePrintClickHdl));
+}
+
+ScTabPageProtection::~ScTabPageProtection()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTabPageProtection::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet)
+{
+ return std::make_unique<ScTabPageProtection>(pPage, pController, *rAttrSet);
+}
+
+void ScTabPageProtection::Reset( const SfxItemSet* rCoreAttrs )
+{
+ // Initialize variables
+
+ sal_uInt16 nWhich = GetWhich( SID_SCATTR_PROTECTION );
+ const ScProtectionAttr* pProtAttr = nullptr;
+ SfxItemState eItemState = rCoreAttrs->GetItemState( nWhich, false,
+ reinterpret_cast<const SfxPoolItem**>(&pProtAttr) );
+
+ // Is this a Default-Item?
+ if ( eItemState == SfxItemState::DEFAULT )
+ pProtAttr = static_cast<const ScProtectionAttr*>(&(rCoreAttrs->Get(nWhich)));
+ // At SfxItemState::DONTCARE let to 0
+
+ bTriEnabled = ( pProtAttr == nullptr ); // TriState, when DontCare
+ bDontCare = bTriEnabled;
+ if (bTriEnabled)
+ {
+ // Defaults which appear when a TriState will be clicked away:
+ // (because everything combined is an attribute, and also only
+ // everything combined as DontCare can be available - #38543#)
+
+ bProtect = true;
+ bHideForm = bHideCell = bHidePrint = false;
+ }
+ else
+ {
+ bProtect = pProtAttr->GetProtection();
+ bHideCell = pProtAttr->GetHideCell();
+ bHideForm = pProtAttr->GetHideFormula();
+ bHidePrint = pProtAttr->GetHidePrint();
+ }
+
+ aHideCellState.bTriStateEnabled = bTriEnabled;
+ aProtectState.bTriStateEnabled = bTriEnabled;
+ aHideFormulaState.bTriStateEnabled = bTriEnabled;
+ aHidePrintState.bTriStateEnabled = bTriEnabled;
+
+ UpdateButtons();
+}
+
+bool ScTabPageProtection::FillItemSet( SfxItemSet* rCoreAttrs )
+{
+ bool bAttrsChanged = false;
+ sal_uInt16 nWhich = GetWhich( SID_SCATTR_PROTECTION );
+ const SfxPoolItem* pOldItem = GetOldItem( *rCoreAttrs, SID_SCATTR_PROTECTION );
+ const SfxItemSet& rOldSet = GetItemSet();
+ SfxItemState eItemState = rOldSet.GetItemState( nWhich, false );
+ ScProtectionAttr aProtAttr;
+
+ if ( !bDontCare )
+ {
+ aProtAttr.SetProtection( bProtect );
+ aProtAttr.SetHideCell( bHideCell );
+ aProtAttr.SetHideFormula( bHideForm );
+ aProtAttr.SetHidePrint( bHidePrint );
+
+ if ( bTriEnabled )
+ bAttrsChanged = true; // DontCare -> properly value
+ else
+ bAttrsChanged = !pOldItem || aProtAttr != *static_cast<const ScProtectionAttr*>(pOldItem);
+ }
+
+ if ( bAttrsChanged )
+ rCoreAttrs->Put( aProtAttr );
+ else if ( eItemState == SfxItemState::DEFAULT )
+ rCoreAttrs->ClearItem( nWhich );
+
+ return bAttrsChanged;
+}
+
+DeactivateRC ScTabPageProtection::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+IMPL_LINK(ScTabPageProtection, ProtectClickHdl, weld::Toggleable&, rBox, void)
+{
+ aProtectState.ButtonToggled(rBox);
+ ButtonClick(rBox);
+}
+
+IMPL_LINK(ScTabPageProtection, HideCellClickHdl, weld::Toggleable&, rBox, void)
+{
+ aHideCellState.ButtonToggled(rBox);
+ ButtonClick(rBox);
+}
+
+IMPL_LINK(ScTabPageProtection, HideFormulaClickHdl, weld::Toggleable&, rBox, void)
+{
+ aHideFormulaState.ButtonToggled(rBox);
+ ButtonClick(rBox);
+}
+
+IMPL_LINK(ScTabPageProtection, HidePrintClickHdl, weld::Toggleable&, rBox, void)
+{
+ aHidePrintState.ButtonToggled(rBox);
+ ButtonClick(rBox);
+}
+
+void ScTabPageProtection::ButtonClick(const weld::Toggleable& rBox)
+{
+ TriState eState = rBox.get_state();
+ if (eState == TRISTATE_INDET)
+ bDontCare = true; // everything combined at DontCare
+ else
+ {
+ bDontCare = false; // DontCare from everywhere
+ bool bOn = eState == TRISTATE_TRUE; // from a selected value
+
+ if (&rBox == m_xBtnProtect.get())
+ bProtect = bOn;
+ else if (&rBox == m_xBtnHideCell.get())
+ bHideCell = bOn;
+ else if (&rBox == m_xBtnHideFormula.get())
+ bHideForm = bOn;
+ else if (&rBox == m_xBtnHidePrint.get())
+ bHidePrint = bOn;
+ else
+ {
+ OSL_FAIL("Wrong button");
+ }
+ }
+
+ UpdateButtons(); // TriState and Logic-Enable
+}
+
+void ScTabPageProtection::UpdateButtons()
+{
+ if (bDontCare)
+ {
+ m_xBtnProtect->set_state(TRISTATE_INDET);
+ m_xBtnHideCell->set_state(TRISTATE_INDET);
+ m_xBtnHideFormula->set_state(TRISTATE_INDET);
+ m_xBtnHidePrint->set_state(TRISTATE_INDET);
+ }
+ else
+ {
+ m_xBtnProtect->set_state(bProtect ? TRISTATE_TRUE : TRISTATE_FALSE);
+ m_xBtnHideCell->set_state(bHideCell ? TRISTATE_TRUE : TRISTATE_FALSE);
+ m_xBtnHideFormula->set_state(bHideForm ? TRISTATE_TRUE : TRISTATE_FALSE);
+ m_xBtnHidePrint->set_state(bHidePrint ? TRISTATE_TRUE : TRISTATE_FALSE);
+ }
+
+ aHideCellState.eState = m_xBtnHideCell->get_state();
+ aProtectState.eState = m_xBtnProtect->get_state();
+ aHideFormulaState.eState = m_xBtnHideFormula->get_state();
+ aHidePrintState.eState = m_xBtnHidePrint->get_state();
+
+ bool bEnable = (m_xBtnHideCell->get_state() != TRISTATE_TRUE);
+ {
+ m_xBtnProtect->set_sensitive(bEnable);
+ m_xBtnHideFormula->set_sensitive(bEnable);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/cctrl/cbnumberformat.cxx b/sc/source/ui/cctrl/cbnumberformat.cxx
new file mode 100644
index 0000000000..760d6a7f9c
--- /dev/null
+++ b/sc/source/ui/cctrl/cbnumberformat.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 <cbnumberformat.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <sc.hrc>
+
+ScNumberFormat::ScNumberFormat(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "modules/scalc/ui/numberbox.ui", "NumberBox", true,
+ reinterpret_cast<sal_uInt64>(SfxViewShell::Current()))
+ , m_xWidget(m_xBuilder->weld_combo_box("numbertype"))
+{
+ m_xWidget->append_text(ScResId(STR_GENERAL));
+ m_xWidget->append_text(ScResId(STR_NUMBER));
+ m_xWidget->append_text(ScResId(STR_PERCENT));
+ m_xWidget->append_text(ScResId(STR_CURRENCY));
+ m_xWidget->append_text(ScResId(STR_DATE));
+ m_xWidget->append_text(ScResId(STR_TIME));
+ m_xWidget->append_text(ScResId(STR_SCIENTIFIC));
+ m_xWidget->append_text(ScResId(STR_FRACTION));
+ m_xWidget->append_text(ScResId(STR_BOOLEAN_VALUE));
+ m_xWidget->append_text(ScResId(STR_TEXT));
+
+ m_xWidget->connect_changed(LINK(this, ScNumberFormat, NumFormatSelectHdl));
+ m_xWidget->connect_key_press(LINK(this, ScNumberFormat, KeyInputHdl));
+
+ SetSizePixel(m_xWidget->get_preferred_size());
+}
+
+void ScNumberFormat::dispose()
+{
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+ScNumberFormat::~ScNumberFormat() { disposeOnce(); }
+
+void ScNumberFormat::GetFocus()
+{
+ if (m_xWidget)
+ m_xWidget->grab_focus();
+ InterimItemWindow::GetFocus();
+}
+
+IMPL_STATIC_LINK(ScNumberFormat, NumFormatSelectHdl, weld::ComboBox&, rBox, void)
+{
+ auto* pCurSh = SfxViewFrame::Current();
+ if (!pCurSh)
+ return;
+
+ SfxDispatcher* pDisp = pCurSh->GetBindings().GetDispatcher();
+ if (pDisp)
+ {
+ const sal_Int32 nVal = rBox.get_active();
+ SfxUInt16Item aItem(SID_NUMBER_TYPE_FORMAT, nVal);
+ pDisp->ExecuteList(SID_NUMBER_TYPE_FORMAT, SfxCallMode::RECORD, { &aItem });
+
+ pCurSh->GetWindow().GrabFocus();
+ }
+}
+
+IMPL_LINK(ScNumberFormat, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/cctrl/cbuttonw.cxx b/sc/source/ui/cctrl/cbuttonw.cxx
new file mode 100644
index 0000000000..b7f99f7318
--- /dev/null
+++ b/sc/source/ui/cctrl/cbuttonw.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/lok.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <cbutton.hxx>
+
+
+ScDDComboBoxButton::ScDDComboBoxButton( OutputDevice* pOutputDevice )
+ : pOut( pOutputDevice )
+{
+ SetOptSizePixel();
+}
+
+ScDDComboBoxButton::~ScDDComboBoxButton()
+{
+}
+
+void ScDDComboBoxButton::SetOutputDevice( OutputDevice* pOutputDevice )
+{
+ pOut = pOutputDevice;
+}
+
+void ScDDComboBoxButton::SetOptSizePixel()
+{
+ aBtnSize = pOut->LogicToPixel(Size(8, 11), MapMode(MapUnit::MapAppFont));
+ aBtnSize.setWidth( std::max(aBtnSize.Width(), static_cast<tools::Long>(pOut->GetSettings().GetStyleSettings().GetScrollBarSize())) );
+}
+
+void ScDDComboBoxButton::Draw( const Point& rAt,
+ const Size& rSize )
+{
+ if ( rSize.IsEmpty() )
+ return;
+
+ // save old state
+ bool bHadFill = pOut->IsFillColor();
+ Color aOldFill = pOut->GetFillColor();
+ bool bHadLine = pOut->IsLineColor();
+ Color aOldLine = pOut->GetLineColor();
+ bool bOldEnable = pOut->IsMapModeEnabled();
+
+ tools::Rectangle aBtnRect( rAt, rSize );
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ pOut->EnableMapMode(false);
+
+ DecorationView aDecoView( pOut);
+
+ tools::Rectangle aInnerRect=aDecoView.DrawButton( aBtnRect, DrawButtonFlags::Default );
+
+ aInnerRect.AdjustLeft(1 );
+ aInnerRect.AdjustTop(1 );
+ aInnerRect.AdjustRight( -1 );
+ aInnerRect.AdjustBottom( -1 );
+
+ Size aInnerSize = aInnerRect.GetSize();
+ Point aInnerCenter = aInnerRect.Center();
+
+ aInnerRect.SetTop( aInnerCenter.Y() - (aInnerSize.Width()>>1) );
+ aInnerRect.SetBottom( aInnerCenter.Y() + (aInnerSize.Width()>>1) );
+
+ ImpDrawArrow( aInnerRect );
+
+ // restore old state
+ pOut->EnableMapMode( bOldEnable );
+ if (bHadLine)
+ pOut->SetLineColor(aOldLine);
+ else
+ pOut->SetLineColor();
+ if (bHadFill)
+ pOut->SetFillColor(aOldFill);
+ else
+ pOut->SetFillColor();
+}
+
+void ScDDComboBoxButton::ImpDrawArrow( const tools::Rectangle& rRect )
+{
+ // no need to save old line and fill color here (is restored after the call)
+
+ tools::Rectangle aPixRect = rRect;
+ Point aCenter = aPixRect.Center();
+ Size aSize = aPixRect.GetSize();
+
+ Size aSize3;
+ aSize3.setWidth( aSize.Width() >> 1 );
+ aSize3.setHeight( aSize.Height() >> 1 );
+
+ Size aSize4;
+ aSize4.setWidth( aSize.Width() >> 2 );
+ aSize4.setHeight( aSize.Height() >> 2 );
+
+ tools::Rectangle aTempRect = aPixRect;
+
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ Color aColor( rSett.GetButtonTextColor() );
+ pOut->SetFillColor( aColor );
+ pOut->SetLineColor( aColor );
+
+ aTempRect.SetLeft( aCenter.X() - aSize4.Width() );
+ aTempRect.SetRight( aCenter.X() + aSize4.Width() );
+ aTempRect.SetTop( aCenter.Y() - aSize3.Height() );
+ aTempRect.SetBottom( aCenter.Y() - 1 );
+
+ pOut->DrawRect( aTempRect );
+
+ Point aPos1( aCenter.X()-aSize3.Width(), aCenter.Y() );
+ Point aPos2( aCenter.X()+aSize3.Width(), aCenter.Y() );
+ while( aPos1.X() <= aPos2.X() )
+ {
+ pOut->DrawLine( aPos1, aPos2 );
+ aPos1.AdjustX( 1 ); aPos2.AdjustX( -1 );
+ aPos1.AdjustY( 1 ); aPos2.AdjustY( 1 );
+ }
+
+ pOut->DrawLine( Point( aCenter.X() - aSize3.Width(), aPos1.Y()+1 ),
+ Point( aCenter.X() + aSize3.Width(), aPos1.Y()+1 ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx
new file mode 100644
index 0000000000..92e7096fc2
--- /dev/null
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -0,0 +1,1832 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <checklistmenu.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <rtl/math.hxx>
+#include <unotools/charclass.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <tools/json_writer.hxx>
+#include <svl/numformat.hxx>
+
+#include <document.hxx>
+#include <viewdata.hxx>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+ScCheckListMenuControl::MenuItemData::MenuItemData()
+ : mbEnabled(true)
+{
+}
+
+ScCheckListMenuControl::SubMenuItemData::SubMenuItemData(ScCheckListMenuControl* pParent)
+ : maTimer("sc SubMenuItemData maTimer")
+ , mpSubMenu(nullptr)
+ , mnMenuPos(MENU_NOT_SELECTED)
+ , mpParent(pParent)
+{
+ maTimer.SetInvokeHandler(LINK(this, ScCheckListMenuControl::SubMenuItemData, TimeoutHdl));
+ maTimer.SetTimeout(Application::GetSettings().GetMouseSettings().GetMenuDelay());
+}
+
+void ScCheckListMenuControl::SubMenuItemData::reset()
+{
+ mpSubMenu = nullptr;
+ mnMenuPos = MENU_NOT_SELECTED;
+ maTimer.Stop();
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl::SubMenuItemData, TimeoutHdl, Timer *, void)
+{
+ mpParent->handleMenuTimeout(this);
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, RowActivatedHdl, weld::TreeView&, bool)
+{
+ executeMenuItem(mxMenu->get_selected_index());
+ return true;
+}
+
+IMPL_LINK(ScCheckListMenuControl, MenuKeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ switch (rKeyCode.GetCode())
+ {
+ case KEY_RIGHT:
+ {
+ if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
+ break;
+
+ const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
+ if (!rMenu.mxSubMenuWin)
+ break;
+
+ executeMenuItem(mnSelectedMenu);
+ }
+ }
+
+ return false;
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, SelectHdl, weld::TreeView&, void)
+{
+ sal_uInt32 nSelectedMenu = MENU_NOT_SELECTED;
+ if (!mxMenu->get_selected(mxScratchIter.get()))
+ {
+ // reselect current item if its submenu is up and the launching item
+ // became unselected by mouse moving out of the top level menu
+ if (mnSelectedMenu < maMenuItems.size() &&
+ maMenuItems[mnSelectedMenu].mxSubMenuWin &&
+ maMenuItems[mnSelectedMenu].mxSubMenuWin->IsVisible())
+ {
+ mxMenu->select(mnSelectedMenu);
+ return;
+ }
+ }
+ else
+ nSelectedMenu = mxMenu->get_iter_index_in_parent(*mxScratchIter);
+
+ setSelectedMenuItem(nSelectedMenu);
+}
+
+void ScCheckListMenuControl::addMenuItem(const OUString& rText, Action* pAction)
+{
+ MenuItemData aItem;
+ aItem.mbEnabled = true;
+ aItem.mxAction.reset(pAction);
+ maMenuItems.emplace_back(std::move(aItem));
+
+ mxMenu->show();
+ mxMenu->append_text(rText);
+ mxMenu->set_image(mxMenu->n_children() - 1, css::uno::Reference<css::graphic::XGraphic>(), 1);
+}
+
+void ScCheckListMenuControl::addSeparator()
+{
+ MenuItemData aItem;
+ maMenuItems.emplace_back(std::move(aItem));
+
+ mxMenu->append_separator("separator" + OUString::number(maMenuItems.size()));
+}
+
+IMPL_LINK(ScCheckListMenuControl, TreeSizeAllocHdl, const Size&, rSize, void)
+{
+ if (maAllocatedSize == rSize)
+ return;
+ maAllocatedSize = rSize;
+ SetDropdownPos();
+ if (!mnAsyncSetDropdownPosId && Application::GetToolkitName().startsWith("gtk"))
+ {
+ // for gtk retry again later in case it didn't work (wayland)
+ mnAsyncSetDropdownPosId = Application::PostUserEvent(LINK(this, ScCheckListMenuControl, SetDropdownPosHdl));
+ }
+}
+
+void ScCheckListMenuControl::SetDropdownPos()
+{
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(maAllocatedSize.Width() - (mxMenu->get_text_height() * 3) / 4 - 6)
+ };
+ mxMenu->set_column_fixed_widths(aWidths);
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, SetDropdownPosHdl, void*, void)
+{
+ mnAsyncSetDropdownPosId = nullptr;
+ SetDropdownPos();
+ mxMenu->queue_resize();
+}
+
+void ScCheckListMenuControl::CreateDropDown()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ // tdf#151820 The color used for the arrow head depends on the background color
+ Color aBackgroundColor = rStyleSettings.GetWindowColor();
+ Color aSpinColor;
+ if (aBackgroundColor.IsDark())
+ aSpinColor = rStyleSettings.GetLightColor();
+ else
+ aSpinColor = rStyleSettings.GetDarkShadowColor();
+
+ int nWidth = (mxMenu->get_text_height() * 3) / 4;
+ mxDropDown->SetOutputSizePixel(Size(nWidth, nWidth), /*bErase*/true, /*bAlphaMaskTransparent*/true);
+ DecorationView aDecoView(mxDropDown.get());
+ aDecoView.DrawSymbol(tools::Rectangle(Point(0, 0), Size(nWidth, nWidth)),
+ SymbolType::SPIN_RIGHT, aSpinColor,
+ DrawSymbolFlags::NONE);
+}
+
+ScListSubMenuControl* ScCheckListMenuControl::addSubMenuItem(const OUString& rText, bool bEnabled, bool bColorMenu)
+{
+ MenuItemData aItem;
+ aItem.mbEnabled = bEnabled;
+
+ aItem.mxSubMenuWin.reset(new ScListSubMenuControl(mxMenu.get(), *this, bColorMenu));
+ maMenuItems.emplace_back(std::move(aItem));
+
+ mxMenu->show();
+ mxMenu->append_text(rText);
+ mxMenu->set_image(mxMenu->n_children() - 1, *mxDropDown, 1);
+ return maMenuItems.back().mxSubMenuWin.get();
+}
+
+void ScCheckListMenuControl::executeMenuItem(size_t nPos)
+{
+ if (nPos >= maMenuItems.size())
+ return;
+
+ const MenuItemData& rMenu = maMenuItems[nPos];
+ if (rMenu.mxSubMenuWin)
+ {
+ if (rMenu.mbEnabled)
+ {
+ maOpenTimer.mnMenuPos = nPos;
+ maOpenTimer.mpSubMenu = rMenu.mxSubMenuWin.get();
+ launchSubMenu();
+ }
+ return;
+ }
+
+ if (!maMenuItems[nPos].mxAction)
+ // no action is defined.
+ return;
+
+ const bool bClosePopup = maMenuItems[nPos].mxAction->execute();
+ if (bClosePopup)
+ terminateAllPopupMenus();
+}
+
+void ScCheckListMenuControl::setSelectedMenuItem(size_t nPos)
+{
+ if (mnSelectedMenu == nPos)
+ // nothing to do.
+ return;
+
+ selectMenuItem(nPos, /*bSubMenuTimer*/true);
+}
+
+void ScCheckListMenuControl::handleMenuTimeout(const SubMenuItemData* pTimer)
+{
+ if (pTimer == &maOpenTimer)
+ {
+ // Close any open submenu immediately.
+ if (maCloseTimer.mpSubMenu)
+ {
+ maCloseTimer.mpSubMenu->EndPopupMode();
+ maCloseTimer.mpSubMenu = nullptr;
+ maCloseTimer.maTimer.Stop();
+ }
+
+ launchSubMenu();
+ }
+ else if (pTimer == &maCloseTimer)
+ {
+ // end submenu.
+ if (maCloseTimer.mpSubMenu)
+ {
+ maCloseTimer.mpSubMenu->EndPopupMode();
+ maCloseTimer.mpSubMenu = nullptr;
+
+ // EndPopup sends a user event, and we want this focus to be set after that has done its conflicting focus-setting work
+ if (!mnAsyncPostPopdownId)
+ mnAsyncPostPopdownId = Application::PostUserEvent(LINK(this, ScCheckListMenuControl, PostPopdownHdl));
+ }
+ }
+}
+
+void ScCheckListMenuControl::queueLaunchSubMenu(size_t nPos, ScListSubMenuControl* pMenu)
+{
+ if (!pMenu)
+ return;
+
+ // Set the submenu on launch queue.
+ if (maOpenTimer.mpSubMenu)
+ {
+ if (maOpenTimer.mpSubMenu != pMenu)
+ {
+ // new submenu is being requested.
+ queueCloseSubMenu();
+ }
+ else
+ {
+ if (pMenu == maCloseTimer.mpSubMenu)
+ maCloseTimer.reset();
+ }
+ }
+
+ maOpenTimer.mpSubMenu = pMenu;
+ maOpenTimer.mnMenuPos = nPos;
+ if (comphelper::LibreOfficeKit::isActive())
+ maOpenTimer.maTimer.Invoke();
+ else
+ maOpenTimer.maTimer.Start();
+}
+
+void ScCheckListMenuControl::queueCloseSubMenu()
+{
+ if (!maOpenTimer.mpSubMenu)
+ // There is no submenu to close.
+ return;
+
+ // Stop any submenu on queue for opening.
+ maOpenTimer.maTimer.Stop();
+
+ // Flush any pending close so it doesn't get skipped
+ if (maCloseTimer.mpSubMenu)
+ {
+ maCloseTimer.mpSubMenu->EndPopupMode();
+ }
+
+ maCloseTimer.mpSubMenu = maOpenTimer.mpSubMenu;
+ maCloseTimer.mnMenuPos = maOpenTimer.mnMenuPos;
+ maOpenTimer.mpSubMenu = nullptr;
+ maOpenTimer.mnMenuPos = MENU_NOT_SELECTED;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ maCloseTimer.maTimer.Invoke();
+ else
+ maCloseTimer.maTimer.Start();
+}
+
+tools::Rectangle ScCheckListMenuControl::GetSubMenuParentRect()
+{
+ if (!mxMenu->get_selected(mxScratchIter.get()))
+ return tools::Rectangle();
+ return mxMenu->get_row_area(*mxScratchIter);
+}
+
+void ScCheckListMenuControl::launchSubMenu()
+{
+ ScListSubMenuControl* pSubMenu = maOpenTimer.mpSubMenu;
+ if (!pSubMenu)
+ return;
+
+ if (!mxMenu->get_selected(mxScratchIter.get()))
+ return;
+
+ tools::Rectangle aRect = GetSubMenuParentRect();
+ pSubMenu->StartPopupMode(mxMenu.get(), aRect);
+
+ mxMenu->select(*mxScratchIter);
+ pSubMenu->GrabFocus();
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, PostPopdownHdl, void*, void)
+{
+ mnAsyncPostPopdownId = nullptr;
+ mxMenu->grab_focus();
+}
+
+IMPL_LINK(ScCheckListMenuControl, MouseEnterHdl, const MouseEvent&, rMEvt, bool)
+{
+ if (!rMEvt.IsEnterWindow())
+ return false;
+ selectMenuItem(MENU_NOT_SELECTED, true);
+ return false;
+}
+
+void ScCheckListMenuControl::endSubMenu(ScListSubMenuControl& rSubMenu)
+{
+ rSubMenu.EndPopupMode();
+ maOpenTimer.reset();
+
+ // EndPopup sends a user event, and we want this focus to be set after that has done its conflicting focus-setting work
+ if (!mnAsyncPostPopdownId)
+ mnAsyncPostPopdownId = Application::PostUserEvent(LINK(this, ScCheckListMenuControl, PostPopdownHdl));
+
+ size_t nMenuPos = getSubMenuPos(&rSubMenu);
+ if (nMenuPos != MENU_NOT_SELECTED)
+ {
+ mnSelectedMenu = nMenuPos;
+ mxMenu->select(mnSelectedMenu);
+ }
+}
+
+void ScCheckListMenuControl::addFields(const std::vector<OUString>& aFields)
+{
+ if (!mbIsMultiField)
+ return;
+
+ mxFieldsCombo->clear();
+
+ for (auto& aField: aFields)
+ mxFieldsCombo->append_text(aField);
+
+ mxFieldsCombo->set_active(0);
+}
+
+tools::Long ScCheckListMenuControl::getField()
+{
+ if (!mbIsMultiField)
+ return -1;
+
+ return mxFieldsCombo->get_active();
+}
+
+void ScCheckListMenuControl::selectMenuItem(size_t nPos, bool bSubMenuTimer)
+{
+ mxMenu->select(nPos == MENU_NOT_SELECTED ? -1 : nPos);
+ mnSelectedMenu = nPos;
+
+ if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
+ {
+ queueCloseSubMenu();
+ return;
+ }
+
+ if (!maMenuItems[nPos].mbEnabled)
+ {
+ queueCloseSubMenu();
+ return;
+ }
+
+ if (bSubMenuTimer)
+ {
+ if (maMenuItems[nPos].mxSubMenuWin && mxMenu->changed_by_hover())
+ {
+ ScListSubMenuControl* pSubMenu = maMenuItems[nPos].mxSubMenuWin.get();
+ queueLaunchSubMenu(nPos, pSubMenu);
+ }
+ else
+ queueCloseSubMenu();
+ }
+}
+
+void ScCheckListMenuControl::clearSelectedMenuItem()
+{
+ selectMenuItem(MENU_NOT_SELECTED, false);
+}
+
+size_t ScCheckListMenuControl::getSubMenuPos(const ScListSubMenuControl* pSubMenu)
+{
+ size_t n = maMenuItems.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (maMenuItems[i].mxSubMenuWin.get() == pSubMenu)
+ return i;
+ }
+ return MENU_NOT_SELECTED;
+}
+
+void ScCheckListMenuControl::setSubMenuFocused(const ScListSubMenuControl* pSubMenu)
+{
+ maCloseTimer.reset();
+ size_t nMenuPos = getSubMenuPos(pSubMenu);
+ if (mnSelectedMenu != nMenuPos)
+ {
+ mnSelectedMenu = nMenuPos;
+ mxMenu->select(mnSelectedMenu);
+ }
+}
+
+void ScCheckListMenuControl::EndPopupMode()
+{
+ if (!mbIsPoppedUp)
+ return;
+ mxPopover->connect_closed(Link<weld::Popover&, void>());
+ mxPopover->popdown();
+ PopupModeEndHdl(*mxPopover);
+ assert(mbIsPoppedUp == false);
+}
+
+void ScCheckListMenuControl::StartPopupMode(weld::Widget* pParent, const tools::Rectangle& rRect)
+{
+ mxPopover->connect_closed(LINK(this, ScCheckListMenuControl, PopupModeEndHdl));
+ mbIsPoppedUp = true;
+ mxPopover->popup_at_rect(pParent, rRect);
+ GrabFocus();
+}
+
+void ScCheckListMenuControl::terminateAllPopupMenus()
+{
+ EndPopupMode();
+}
+
+ScCheckListMenuControl::Config::Config() :
+ mbAllowEmptySet(true), mbRTL(false)
+{
+}
+
+ScCheckListMember::ScCheckListMember()
+ : mnValue(0.0)
+ , mbVisible(true)
+ , mbHiddenByOtherFilter(false)
+ , mbDate(false)
+ , mbLeaf(false)
+ , mbValue(false)
+ , meDatePartType(YEAR)
+{
+}
+
+// the value of border-width of FilterDropDown
+constexpr int nBorderWidth = 4;
+// number of rows visible in checklist
+constexpr int nCheckListVisibleRows = 9;
+// number of rows visible in colorlist
+constexpr int nColorListVisibleRows = 9;
+
+ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData& rViewData,
+ bool bHasDates, int nWidth, bool bIsMultiField)
+ : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterdropdown.ui"))
+ , mxPopover(mxBuilder->weld_popover("FilterDropDown"))
+ , mxContainer(mxBuilder->weld_container("container"))
+ , mxMenu(mxBuilder->weld_tree_view("menu"))
+ , mxScratchIter(mxMenu->make_iterator())
+ , mxNonMenu(mxBuilder->weld_widget("nonmenu"))
+ , mxFieldsComboLabel(mxBuilder->weld_label("select_field_label"))
+ , mxFieldsCombo(mxBuilder->weld_combo_box("multi_field_combo"))
+ , mxEdSearch(mxBuilder->weld_entry("search_edit"))
+ , mxBox(mxBuilder->weld_widget("box"))
+ , mxListChecks(mxBuilder->weld_tree_view("check_list_box"))
+ , mxTreeChecks(mxBuilder->weld_tree_view("check_tree_box"))
+ , mxChkToggleAll(mxBuilder->weld_check_button("toggle_all"))
+ , mxBtnSelectSingle(mxBuilder->weld_button("select_current"))
+ , mxBtnUnselectSingle(mxBuilder->weld_button("unselect_current"))
+ , mxButtonBox(mxBuilder->weld_box("buttonbox"))
+ , mxBtnOk(mxBuilder->weld_button("ok"))
+ , mxBtnCancel(mxBuilder->weld_button("cancel"))
+ , mxContextMenu(mxBuilder->weld_menu("contextmenu"))
+ , mxDropDown(mxMenu->create_virtual_device())
+ , mnCheckWidthReq(-1)
+ , mnWndWidth(0)
+ , mnCheckListVisibleRows(nCheckListVisibleRows)
+ , mePrevToggleAllState(TRISTATE_INDET)
+ , mnSelectedMenu(MENU_NOT_SELECTED)
+ , mrViewData(rViewData)
+ , mnAsyncPostPopdownId(nullptr)
+ , mnAsyncSetDropdownPosId(nullptr)
+ , mbHasDates(bHasDates)
+ , mbIsPoppedUp(false)
+ , maOpenTimer(this)
+ , maCloseTimer(this)
+ , maSearchEditTimer("ScCheckListMenuControl maSearchEditTimer")
+ , mbIsMultiField(bIsMultiField)
+{
+ mxTreeChecks->set_clicks_to_toggle(1);
+ mxListChecks->set_clicks_to_toggle(1);
+
+ mxNonMenu->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxEdSearch->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxListChecks->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxTreeChecks->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxListChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, CommandHdl));
+ mxTreeChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, CommandHdl));
+ mxChkToggleAll->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxBtnSelectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxBtnUnselectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxBtnOk->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+ mxBtnCancel->connect_mouse_move(LINK(this, ScCheckListMenuControl, MouseEnterHdl));
+
+ /*
+ tdf#136559 If we have no dates we don't need a tree
+ structure, just a list. GtkListStore can be then
+ used which is much faster than a GtkTreeStore, so
+ with no dates switch to the treeview which uses the
+ faster GtkListStore
+ */
+ if (mbHasDates)
+ mpChecks = mxTreeChecks.get();
+ else
+ {
+ mxTreeChecks->hide();
+ mxListChecks->show();
+ mpChecks = mxListChecks.get();
+ }
+
+ int nChecksHeight = mxTreeChecks->get_height_rows(mnCheckListVisibleRows);
+ if (nWidth != -1)
+ {
+ mnCheckWidthReq = nWidth - nBorderWidth * 2 - 4;
+ mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+ mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+ }
+
+ // sort ok/cancel into native order, if this was a dialog they would be auto-sorted, but this
+ // popup isn't a true dialog
+ mxButtonBox->sort_native_button_order();
+
+ mxTreeChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
+ mxListChecks->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ mxBox->show();
+ if (mbIsMultiField)
+ {
+ mxFieldsComboLabel->show();
+ mxFieldsCombo->show();
+ }
+ else
+ {
+ mxFieldsComboLabel->hide();
+ mxFieldsCombo->hide();
+ }
+ mxEdSearch->show();
+ mxButtonBox->show();
+
+ mxMenu->connect_row_activated(LINK(this, ScCheckListMenuControl, RowActivatedHdl));
+ mxMenu->connect_changed(LINK(this, ScCheckListMenuControl, SelectHdl));
+ mxMenu->connect_key_press(LINK(this, ScCheckListMenuControl, MenuKeyInputHdl));
+
+ mxBtnOk->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
+ mxBtnCancel->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
+ if (mbIsMultiField)
+ mxFieldsCombo->connect_changed(LINK(this, ScCheckListMenuControl, ComboChangedHdl));
+ mxEdSearch->connect_changed(LINK(this, ScCheckListMenuControl, EdModifyHdl));
+ mxEdSearch->connect_activate(LINK(this, ScCheckListMenuControl, EdActivateHdl));
+ mxTreeChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
+ mxTreeChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl));
+ mxListChecks->connect_toggled(LINK(this, ScCheckListMenuControl, CheckHdl));
+ mxListChecks->connect_key_press(LINK(this, ScCheckListMenuControl, KeyInputHdl));
+ mxChkToggleAll->connect_toggled(LINK(this, ScCheckListMenuControl, TriStateHdl));
+ mxBtnSelectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
+ mxBtnUnselectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, ButtonHdl));
+
+ CreateDropDown();
+ mxMenu->connect_size_allocate(LINK(this, ScCheckListMenuControl, TreeSizeAllocHdl));
+
+ // determine what width the checklist will end up with
+ mnCheckWidthReq = mxContainer->get_preferred_size().Width();
+ // make that size fixed now, we can now use mnCheckWidthReq to speed up
+ // bulk_insert_for_each
+ mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+ mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+
+ maSearchEditTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
+ maSearchEditTimer.SetInvokeHandler(LINK(this, ScCheckListMenuControl, SearchEditTimeoutHdl));
+}
+
+void ScCheckListMenuControl::GrabFocus()
+{
+ if (mxEdSearch->get_visible())
+ mxEdSearch->grab_focus();
+ else
+ {
+ mxMenu->set_cursor(0);
+ mxMenu->grab_focus();
+ }
+}
+
+void ScCheckListMenuControl::DropPendingEvents()
+{
+ if (mnAsyncPostPopdownId)
+ {
+ Application::RemoveUserEvent(mnAsyncPostPopdownId);
+ mnAsyncPostPopdownId = nullptr;
+ }
+ if (mnAsyncSetDropdownPosId)
+ {
+ Application::RemoveUserEvent(mnAsyncSetDropdownPosId);
+ mnAsyncSetDropdownPosId = nullptr;
+ }
+}
+
+ScCheckListMenuControl::~ScCheckListMenuControl()
+{
+ maSearchEditTimer.Stop();
+ EndPopupMode();
+ for (auto& rMenuItem : maMenuItems)
+ rMenuItem.mxSubMenuWin.reset();
+ DropPendingEvents();
+}
+
+void ScCheckListMenuControl::prepWindow()
+{
+ mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height() + 2);
+ mnSelectedMenu = MENU_NOT_SELECTED;
+ if (mxMenu->n_children())
+ {
+ mxMenu->set_cursor(0);
+ mxMenu->unselect_all();
+ }
+
+ mnWndWidth = mxContainer->get_preferred_size().Width() + nBorderWidth * 2 + 4;
+}
+
+void ScCheckListMenuControl::setAllMemberState(bool bSet)
+{
+ mpChecks->all_foreach([this, bSet](weld::TreeIter& rEntry){
+ if (mpChecks->get_sensitive(rEntry, 0))
+ mpChecks->set_toggle(rEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE);
+ return false;
+ });
+
+ if (!maConfig.mbAllowEmptySet)
+ {
+ // We need to have at least one member selected.
+ mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0);
+ }
+}
+
+void ScCheckListMenuControl::selectCurrentMemberOnly(bool bSet)
+{
+ setAllMemberState(!bSet);
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
+ if (!mpChecks->get_cursor(xEntry.get()))
+ return;
+ mpChecks->set_toggle(*xEntry, bSet ? TRISTATE_TRUE : TRISTATE_FALSE);
+}
+
+IMPL_LINK(ScCheckListMenuControl, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ mxContextMenu->set_sensitive("less", mnCheckListVisibleRows > 4);
+ mxContextMenu->set_sensitive("more", mnCheckListVisibleRows < 42);
+
+ OUString sCommand = mxContextMenu->popup_at_rect(mpChecks, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
+ if (sCommand.isEmpty())
+ return true;
+
+ if (sCommand == "more")
+ ++mnCheckListVisibleRows;
+ else if (sCommand == "less")
+ --mnCheckListVisibleRows;
+ ResizeToRequest();
+
+ return true;
+}
+
+void ScCheckListMenuControl::ResizeToRequest()
+{
+ int nChecksHeight = mxTreeChecks->get_height_rows(mnCheckListVisibleRows);
+ mxTreeChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+ mxListChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+ mxPopover->resize_to_request();
+}
+
+IMPL_LINK(ScCheckListMenuControl, ButtonHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnOk.get())
+ close(true);
+ else if (&rBtn == mxBtnCancel.get())
+ close(false);
+ else if (&rBtn == mxBtnSelectSingle.get() || &rBtn == mxBtnUnselectSingle.get())
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
+ bool bEntry = mpChecks->get_cursor(xEntry.get());
+ if (!bEntry)
+ xEntry.reset();
+ if (bEntry && mpChecks->get_sensitive(*xEntry, 0))
+ {
+ selectCurrentMemberOnly(&rBtn == mxBtnSelectSingle.get());
+ Check(xEntry.get());
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, TriStateHdl, weld::Toggleable&, void)
+{
+ switch (mePrevToggleAllState)
+ {
+ case TRISTATE_FALSE:
+ mxChkToggleAll->set_state(TRISTATE_TRUE);
+ setAllMemberState(true);
+ break;
+ case TRISTATE_TRUE:
+ mxChkToggleAll->set_state(TRISTATE_FALSE);
+ setAllMemberState(false);
+ break;
+ case TRISTATE_INDET:
+ default:
+ mxChkToggleAll->set_state(TRISTATE_TRUE);
+ setAllMemberState(true);
+ break;
+ }
+
+ mePrevToggleAllState = mxChkToggleAll->get_state();
+}
+
+namespace
+{
+ void insertMember(weld::TreeView& rView, const weld::TreeIter& rIter, const ScCheckListMember& rMember, bool bChecked)
+ {
+ OUString aLabel = rMember.maName;
+ if (aLabel.isEmpty())
+ aLabel = ScResId(STR_EMPTYDATA);
+ rView.set_toggle(rIter, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
+ rView.set_text(rIter, aLabel, 0);
+ rView.set_sensitive(rIter, !rMember.mbHiddenByOtherFilter);
+ }
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, ComboChangedHdl, weld::ComboBox&, void)
+{
+ if (mbIsMultiField && mxFieldChangedAction)
+ mxFieldChangedAction->execute();
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, SearchEditTimeoutHdl, Timer*, void)
+{
+ OUString aSearchText = mxEdSearch->get_text();
+ aSearchText = ScGlobal::getCharClass().lowercase( aSearchText );
+ bool bSearchTextEmpty = aSearchText.isEmpty();
+ size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
+ [](const ScCheckListMember& rLMem) { return !rLMem.mbHiddenByOtherFilter; });
+ size_t nSelCount = 0;
+
+ // This branch is the general case, the other is an optimized variant of
+ // this one where we can take advantage of knowing we have no hierarchy
+ if (mbHasDates)
+ {
+ mpChecks->freeze();
+
+ bool bSomeDateDeletes = false;
+
+ for (size_t i = 0; i < nEnableMember; ++i)
+ {
+ bool bIsDate = maMembers[i].mbDate;
+ bool bPartialMatch = false;
+
+ OUString aLabelDisp = maMembers[i].maName;
+ if ( aLabelDisp.isEmpty() )
+ aLabelDisp = ScResId( STR_EMPTYDATA );
+
+ if ( !bSearchTextEmpty )
+ {
+ if ( !bIsDate )
+ bPartialMatch = ( ScGlobal::getCharClass().lowercase( aLabelDisp ).indexOf( aSearchText ) != -1 );
+ else if ( maMembers[i].meDatePartType == ScCheckListMember::DAY ) // Match with both numerical and text version of month
+ bPartialMatch = (ScGlobal::getCharClass().lowercase( OUString(
+ maMembers[i].maRealName + maMembers[i].maDateParts[1] )).indexOf( aSearchText ) != -1);
+ else
+ continue;
+ }
+ else if ( bIsDate && maMembers[i].meDatePartType != ScCheckListMember::DAY )
+ continue;
+
+ if ( bSearchTextEmpty )
+ {
+ auto xLeaf = ShowCheckEntry(aLabelDisp, maMembers[i], true, maMembers[i].mbVisible);
+ updateMemberParents(xLeaf.get(), i);
+ if ( maMembers[i].mbVisible )
+ ++nSelCount;
+ continue;
+ }
+
+ if ( bPartialMatch )
+ {
+ auto xLeaf = ShowCheckEntry(aLabelDisp, maMembers[i]);
+ updateMemberParents(xLeaf.get(), i);
+ ++nSelCount;
+ }
+ else
+ {
+ ShowCheckEntry(aLabelDisp, maMembers[i], false, false);
+ if( bIsDate )
+ bSomeDateDeletes = true;
+ }
+ }
+
+ if ( bSomeDateDeletes )
+ {
+ for (size_t i = 0; i < nEnableMember; ++i)
+ {
+ if (!maMembers[i].mbDate)
+ continue;
+ if (maMembers[i].meDatePartType != ScCheckListMember::DAY)
+ continue;
+ updateMemberParents(nullptr, i);
+ }
+ }
+
+ mpChecks->thaw();
+ }
+ else
+ {
+ mpChecks->freeze();
+
+ // when there are a lot of rows, it is cheaper to simply clear the tree and either
+ // re-initialise or just insert the filtered lines
+ mpChecks->clear();
+
+ mpChecks->thaw();
+
+ if (bSearchTextEmpty)
+ nSelCount = initMembers();
+ else
+ {
+ std::vector<size_t> aShownIndexes;
+
+ for (size_t i = 0; i < nEnableMember; ++i)
+ {
+ assert(!maMembers[i].mbDate);
+
+ OUString aLabelDisp = maMembers[i].maName;
+ if ( aLabelDisp.isEmpty() )
+ aLabelDisp = ScResId( STR_EMPTYDATA );
+
+ bool bPartialMatch = ScGlobal::getCharClass().lowercase( aLabelDisp ).indexOf( aSearchText ) != -1;
+
+ if (!bPartialMatch)
+ continue;
+
+ aShownIndexes.push_back(i);
+ }
+
+ std::vector<int> aFixedWidths { mnCheckWidthReq };
+ // tdf#122419 insert in the fastest order, this might be backwards.
+ mpChecks->bulk_insert_for_each(aShownIndexes.size(), [this, &aShownIndexes, &nSelCount](weld::TreeIter& rIter, int i) {
+ size_t nIndex = aShownIndexes[i];
+ insertMember(*mpChecks, rIter, maMembers[nIndex], true);
+ ++nSelCount;
+ }, nullptr, &aFixedWidths);
+ }
+ }
+
+ if ( nSelCount == nEnableMember )
+ mxChkToggleAll->set_state( TRISTATE_TRUE );
+ else if ( nSelCount == 0 )
+ mxChkToggleAll->set_state( TRISTATE_FALSE );
+ else
+ mxChkToggleAll->set_state( TRISTATE_INDET );
+
+ if ( !maConfig.mbAllowEmptySet )
+ {
+ const bool bEmptySet( nSelCount == 0 );
+ mpChecks->set_sensitive(!bEmptySet);
+ mxChkToggleAll->set_sensitive(!bEmptySet);
+ mxBtnSelectSingle->set_sensitive(!bEmptySet);
+ mxBtnUnselectSingle->set_sensitive(!bEmptySet);
+ mxBtnOk->set_sensitive(!bEmptySet);
+ }
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, EdModifyHdl, weld::Entry&, void)
+{
+ maSearchEditTimer.Start();
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, EdActivateHdl, weld::Entry&, bool)
+{
+ if (mxBtnOk->get_sensitive())
+ close(true);
+ return true;
+}
+
+IMPL_LINK( ScCheckListMenuControl, CheckHdl, const weld::TreeView::iter_col&, rRowCol, void )
+{
+ Check(&rRowCol.first);
+}
+
+void ScCheckListMenuControl::Check(const weld::TreeIter* pEntry)
+{
+ if (pEntry)
+ CheckEntry(*pEntry, mpChecks->get_toggle(*pEntry) == TRISTATE_TRUE);
+ size_t nNumChecked = GetCheckedEntryCount();
+ size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
+ [](const ScCheckListMember& rLMem) { return !rLMem.mbHiddenByOtherFilter; });
+ if (nNumChecked == nEnableMember)
+ // all members visible
+ mxChkToggleAll->set_state(TRISTATE_TRUE);
+ else if (nNumChecked == 0)
+ // no members visible
+ mxChkToggleAll->set_state(TRISTATE_FALSE);
+ else
+ mxChkToggleAll->set_state(TRISTATE_INDET);
+
+ if (!maConfig.mbAllowEmptySet)
+ // We need to have at least one member selected.
+ mxBtnOk->set_sensitive(nNumChecked != 0);
+
+ mePrevToggleAllState = mxChkToggleAll->get_state();
+}
+
+void ScCheckListMenuControl::updateMemberParents(const weld::TreeIter* pLeaf, size_t nIdx)
+{
+ if ( !maMembers[nIdx].mbDate || maMembers[nIdx].meDatePartType != ScCheckListMember::DAY )
+ return;
+
+ OUString aYearName = maMembers[nIdx].maDateParts[0];
+ OUString aMonthName = maMembers[nIdx].maDateParts[1];
+ auto aItr = maYearMonthMap.find(aYearName + aMonthName);
+
+ if ( pLeaf )
+ {
+ std::unique_ptr<weld::TreeIter> xYearEntry;
+ std::unique_ptr<weld::TreeIter> xMonthEntry = mpChecks->make_iterator(pLeaf);
+ if (!mpChecks->iter_parent(*xMonthEntry))
+ xMonthEntry.reset();
+ else
+ {
+ xYearEntry = mpChecks->make_iterator(xMonthEntry.get());
+ if (!mpChecks->iter_parent(*xYearEntry))
+ xYearEntry.reset();
+ }
+
+ maMembers[nIdx].mxParent = std::move(xMonthEntry);
+ if ( aItr != maYearMonthMap.end() )
+ {
+ size_t nMonthIdx = aItr->second;
+ maMembers[nMonthIdx].mxParent = std::move(xYearEntry);
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName);
+ if (aItr != maYearMonthMap.end() && !xYearEntry)
+ {
+ size_t nMonthIdx = aItr->second;
+ maMembers[nMonthIdx].mxParent.reset();
+ maMembers[nIdx].mxParent.reset();
+ }
+ else if (xYearEntry && !FindEntry(xYearEntry.get(), aMonthName))
+ maMembers[nIdx].mxParent.reset();
+ }
+}
+
+void ScCheckListMenuControl::setMemberSize(size_t n)
+{
+ maMembers.reserve(n);
+}
+
+void ScCheckListMenuControl::addDateMember(const OUString& rsName, double nVal, bool bVisible, bool bHiddenByOtherFilter)
+{
+ SvNumberFormatter* pFormatter = mrViewData.GetDocument().GetFormatTable();
+
+ // Convert the numeric date value to a date object.
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays(rtl::math::approxFloor(nVal));
+
+ sal_Int16 nYear = aDate.GetYear();
+ sal_uInt16 nMonth = aDate.GetMonth();
+ sal_uInt16 nDay = aDate.GetDay();
+
+ // Get the localized month name list.
+ CalendarWrapper& rCalendar = ScGlobal::GetCalendar();
+ uno::Sequence<i18n::CalendarItem2> aMonths = rCalendar.getMonths();
+ if (aMonths.getLength() < nMonth)
+ return;
+
+ OUString aYearName = OUString::number(nYear);
+ OUString aMonthName = aMonths[nMonth-1].FullName;
+ OUString aDayName = OUString::number(nDay);
+
+ if ( aDayName.getLength() == 1 )
+ aDayName = "0" + aDayName;
+
+ mpChecks->freeze();
+
+ std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, aYearName);
+ if (!xYearEntry)
+ {
+ xYearEntry = mpChecks->make_iterator();
+ mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get());
+ mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE);
+ mpChecks->set_text(*xYearEntry, aYearName, 0);
+ mpChecks->set_sensitive(*xYearEntry, !bHiddenByOtherFilter);
+ ScCheckListMember aMemYear;
+ aMemYear.maName = aYearName;
+ aMemYear.maRealName = rsName;
+ aMemYear.mbDate = true;
+ aMemYear.mbLeaf = false;
+ aMemYear.mbVisible = bVisible;
+ aMemYear.mbHiddenByOtherFilter = bHiddenByOtherFilter;
+ aMemYear.mxParent.reset();
+ aMemYear.meDatePartType = ScCheckListMember::YEAR;
+ maMembers.emplace_back(std::move(aMemYear));
+ }
+
+ std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), aMonthName);
+ if (!xMonthEntry)
+ {
+ xMonthEntry = mpChecks->make_iterator();
+ mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get());
+ mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE);
+ mpChecks->set_text(*xMonthEntry, aMonthName, 0);
+ mpChecks->set_sensitive(*xMonthEntry, !bHiddenByOtherFilter);
+ ScCheckListMember aMemMonth;
+ aMemMonth.maName = aMonthName;
+ aMemMonth.maRealName = rsName;
+ aMemMonth.mbDate = true;
+ aMemMonth.mbLeaf = false;
+ aMemMonth.mbVisible = bVisible;
+ aMemMonth.mbHiddenByOtherFilter = bHiddenByOtherFilter;
+ aMemMonth.mxParent = std::move(xYearEntry);
+ aMemMonth.meDatePartType = ScCheckListMember::MONTH;
+ maMembers.emplace_back(std::move(aMemMonth));
+ maYearMonthMap[aYearName + aMonthName] = maMembers.size() - 1;
+ }
+
+ std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), aDayName);
+ if (!xDayEntry)
+ {
+ xDayEntry = mpChecks->make_iterator();
+ mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get());
+ mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE);
+ mpChecks->set_text(*xDayEntry, aDayName, 0);
+ mpChecks->set_sensitive(*xDayEntry, !bHiddenByOtherFilter);
+ ScCheckListMember aMemDay;
+ aMemDay.maName = aDayName;
+ aMemDay.maRealName = rsName;
+ aMemDay.maDateParts.resize(2);
+ aMemDay.maDateParts[0] = aYearName;
+ aMemDay.maDateParts[1] = aMonthName;
+ aMemDay.mbDate = true;
+ aMemDay.mbLeaf = true;
+ aMemDay.mbVisible = bVisible;
+ aMemDay.mbHiddenByOtherFilter = bHiddenByOtherFilter;
+ aMemDay.mxParent = std::move(xMonthEntry);
+ aMemDay.meDatePartType = ScCheckListMember::DAY;
+ maMembers.emplace_back(std::move(aMemDay));
+ }
+
+ mpChecks->thaw();
+}
+
+void ScCheckListMenuControl::addMember(const OUString& rName, const double nVal, bool bVisible, bool bHiddenByOtherFilter, bool bValue)
+{
+ ScCheckListMember aMember;
+ // tdf#46062 - indicate hidden whitespaces using quotes
+ aMember.maName = o3tl::trim(rName) != rName ? "\"" + rName + "\"" : rName;
+ aMember.maRealName = rName;
+ aMember.mnValue = nVal;
+ aMember.mbDate = false;
+ aMember.mbLeaf = true;
+ aMember.mbValue = bValue;
+ aMember.mbVisible = bVisible;
+ aMember.mbHiddenByOtherFilter = bHiddenByOtherFilter;
+ aMember.mxParent.reset();
+ maMembers.emplace_back(std::move(aMember));
+}
+
+void ScCheckListMenuControl::clearMembers()
+{
+ maMembers.clear();
+
+ mpChecks->freeze();
+ mpChecks->clear();
+ mpChecks->thaw();
+}
+
+std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::FindEntry(const weld::TreeIter* pParent, std::u16string_view sNode)
+{
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(pParent);
+ bool bEntry = pParent ? mpChecks->iter_children(*xEntry) : mpChecks->get_iter_first(*xEntry);
+ while (bEntry)
+ {
+ if (sNode == mpChecks->get_text(*xEntry, 0))
+ return xEntry;
+ bEntry = mpChecks->iter_next_sibling(*xEntry);
+ }
+ return nullptr;
+}
+
+void ScCheckListMenuControl::GetRecursiveChecked(const weld::TreeIter* pEntry, std::unordered_set<OUString>& vOut,
+ OUString& rLabel)
+{
+ if (mpChecks->get_toggle(*pEntry) != TRISTATE_TRUE)
+ return;
+
+ // We have to hash parents and children together.
+ // Per convention for easy access in getResult()
+ // "child;parent;grandparent" while descending.
+ if (rLabel.isEmpty())
+ rLabel = mpChecks->get_text(*pEntry, 0);
+ else
+ rLabel = mpChecks->get_text(*pEntry, 0) + ";" + rLabel;
+
+ // Prerequisite: the selection mechanism guarantees that if a child is
+ // selected then also the parent is selected, so we only have to
+ // inspect the children in case the parent is selected.
+ if (!mpChecks->iter_has_child(*pEntry))
+ return;
+
+ std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(pEntry));
+ bool bChild = mpChecks->iter_children(*xChild);
+ while (bChild)
+ {
+ OUString aLabel = rLabel;
+ GetRecursiveChecked(xChild.get(), vOut, aLabel);
+ if (!aLabel.isEmpty() && aLabel != rLabel)
+ vOut.insert(aLabel);
+ bChild = mpChecks->iter_next_sibling(*xChild);
+ }
+ // Let the caller not add the parent alone.
+ rLabel.clear();
+}
+
+std::unordered_set<OUString> ScCheckListMenuControl::GetAllChecked()
+{
+ std::unordered_set<OUString> vResults(0);
+
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
+ bool bEntry = mpChecks->get_iter_first(*xEntry);
+ while (bEntry)
+ {
+ OUString aLabel;
+ GetRecursiveChecked(xEntry.get(), vResults, aLabel);
+ if (!aLabel.isEmpty())
+ vResults.insert(aLabel);
+ bEntry = mpChecks->iter_next_sibling(*xEntry);
+ }
+
+ return vResults;
+}
+
+bool ScCheckListMenuControl::IsChecked(std::u16string_view sName, const weld::TreeIter* pParent)
+{
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName);
+ return xEntry && mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
+}
+
+void ScCheckListMenuControl::CheckEntry(std::u16string_view sName, const weld::TreeIter* pParent, bool bCheck)
+{
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pParent, sName);
+ if (xEntry)
+ CheckEntry(*xEntry, bCheck);
+}
+
+// Recursively check all children of rParent
+void ScCheckListMenuControl::CheckAllChildren(const weld::TreeIter& rParent, bool bCheck)
+{
+ mpChecks->set_toggle(rParent, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator(&rParent);
+ bool bEntry = mpChecks->iter_children(*xEntry);
+ while (bEntry)
+ {
+ CheckAllChildren(*xEntry, bCheck);
+ bEntry = mpChecks->iter_next_sibling(*xEntry);
+ }
+}
+
+void ScCheckListMenuControl::CheckEntry(const weld::TreeIter& rParent, bool bCheck)
+{
+ // recursively check all items below rParent
+ CheckAllChildren(rParent, bCheck);
+ // checking rParent can affect ancestors, e.g. if ancestor is unchecked and rParent is
+ // now checked then the ancestor needs to be checked also
+ if (!mpChecks->get_iter_depth(rParent))
+ return;
+
+ std::unique_ptr<weld::TreeIter> xAncestor(mpChecks->make_iterator(&rParent));
+ bool bAncestor = mpChecks->iter_parent(*xAncestor);
+ while (bAncestor)
+ {
+ // if any first level children checked then ancestor
+ // needs to be checked, similarly if no first level children
+ // checked then ancestor needs to be unchecked
+ std::unique_ptr<weld::TreeIter> xChild(mpChecks->make_iterator(xAncestor.get()));
+ bool bChild = mpChecks->iter_children(*xChild);
+ bool bChildChecked = false;
+
+ while (bChild)
+ {
+ if (mpChecks->get_toggle(*xChild) == TRISTATE_TRUE)
+ {
+ bChildChecked = true;
+ break;
+ }
+ bChild = mpChecks->iter_next_sibling(*xChild);
+ }
+ mpChecks->set_toggle(*xAncestor, bChildChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
+ bAncestor = mpChecks->iter_parent(*xAncestor);
+ }
+}
+
+std::unique_ptr<weld::TreeIter> ScCheckListMenuControl::ShowCheckEntry(const OUString& sName, ScCheckListMember& rMember, bool bShow, bool bCheck)
+{
+ std::unique_ptr<weld::TreeIter> xEntry;
+ if (!rMember.mbDate || rMember.mxParent)
+ xEntry = FindEntry(rMember.mxParent.get(), sName);
+
+ if ( bShow )
+ {
+ if (!xEntry)
+ {
+ if (rMember.mbDate)
+ {
+ if (rMember.maDateParts.empty())
+ return nullptr;
+
+ std::unique_ptr<weld::TreeIter> xYearEntry = FindEntry(nullptr, rMember.maDateParts[0]);
+ if (!xYearEntry)
+ {
+ xYearEntry = mpChecks->make_iterator();
+ mpChecks->insert(nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, xYearEntry.get());
+ mpChecks->set_toggle(*xYearEntry, TRISTATE_FALSE);
+ mpChecks->set_text(*xYearEntry, rMember.maDateParts[0], 0);
+ }
+ std::unique_ptr<weld::TreeIter> xMonthEntry = FindEntry(xYearEntry.get(), rMember.maDateParts[1]);
+ if (!xMonthEntry)
+ {
+ xMonthEntry = mpChecks->make_iterator();
+ mpChecks->insert(xYearEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xMonthEntry.get());
+ mpChecks->set_toggle(*xMonthEntry, TRISTATE_FALSE);
+ mpChecks->set_text(*xMonthEntry, rMember.maDateParts[1], 0);
+ }
+ std::unique_ptr<weld::TreeIter> xDayEntry = FindEntry(xMonthEntry.get(), rMember.maName);
+ if (!xDayEntry)
+ {
+ xDayEntry = mpChecks->make_iterator();
+ mpChecks->insert(xMonthEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xDayEntry.get());
+ mpChecks->set_toggle(*xDayEntry, TRISTATE_FALSE);
+ mpChecks->set_text(*xDayEntry, rMember.maName, 0);
+ }
+ return xDayEntry; // Return leaf node
+ }
+
+ xEntry = mpChecks->make_iterator();
+ mpChecks->append(xEntry.get());
+ mpChecks->set_toggle(*xEntry, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
+ mpChecks->set_text(*xEntry, sName, 0);
+ }
+ else
+ CheckEntry(*xEntry, bCheck);
+ }
+ else if (xEntry)
+ {
+ mpChecks->remove(*xEntry);
+ if (rMember.mxParent)
+ {
+ std::unique_ptr<weld::TreeIter> xParent(mpChecks->make_iterator(rMember.mxParent.get()));
+ while (xParent && !mpChecks->iter_has_child(*xParent))
+ {
+ std::unique_ptr<weld::TreeIter> xTmp(mpChecks->make_iterator(xParent.get()));
+ if (!mpChecks->iter_parent(*xParent))
+ xParent.reset();
+ mpChecks->remove(*xTmp);
+ }
+ }
+ }
+ return nullptr;
+}
+
+int ScCheckListMenuControl::GetCheckedEntryCount() const
+{
+ int nRet = 0;
+
+ mpChecks->all_foreach([this, &nRet](weld::TreeIter& rEntry){
+ if (mpChecks->get_toggle(rEntry) == TRISTATE_TRUE)
+ ++nRet;
+ return false;
+ });
+
+ return nRet;
+}
+
+IMPL_LINK(ScCheckListMenuControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKey = rKEvt.GetKeyCode();
+
+ if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
+ bool bEntry = mpChecks->get_cursor(xEntry.get());
+ if (bEntry && mpChecks->get_sensitive(*xEntry, 0))
+ {
+ bool bOldCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
+ CheckEntry(*xEntry, !bOldCheck);
+ bool bNewCheck = mpChecks->get_toggle(*xEntry) == TRISTATE_TRUE;
+ if (bOldCheck != bNewCheck)
+ Check(xEntry.get());
+ }
+ return true;
+ }
+
+ return false;
+}
+
+size_t ScCheckListMenuControl::initMembers(int nMaxMemberWidth)
+{
+ size_t n = maMembers.size();
+ size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
+ [](const ScCheckListMember& rLMem) { return !rLMem.mbHiddenByOtherFilter; });
+ size_t nVisMemCount = 0;
+
+ if (nMaxMemberWidth == -1)
+ nMaxMemberWidth = mnCheckWidthReq;
+
+ if (!mpChecks->n_children() && !mbHasDates)
+ {
+ std::vector<int> aFixedWidths { nMaxMemberWidth };
+ // tdf#134038 insert in the fastest order, this might be backwards so only do it for
+ // the !mbHasDates case where no entry depends on another to exist before getting
+ // inserted. We cannot retain pre-existing treeview content, only clear and fill it.
+ mpChecks->bulk_insert_for_each(n, [this, &nVisMemCount](weld::TreeIter& rIter, int i) {
+ assert(!maMembers[i].mbDate);
+ insertMember(*mpChecks, rIter, maMembers[i], maMembers[i].mbVisible);
+ if (maMembers[i].mbVisible)
+ ++nVisMemCount;
+ }, nullptr, &aFixedWidths);
+ }
+ else
+ {
+ mpChecks->freeze();
+
+ std::unique_ptr<weld::TreeIter> xEntry = mpChecks->make_iterator();
+ std::vector<std::unique_ptr<weld::TreeIter>> aExpandRows;
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (maMembers[i].mbDate)
+ {
+ CheckEntry(maMembers[i].maName, maMembers[i].mxParent.get(), maMembers[i].mbVisible);
+ // Expand first node of checked dates
+ if (!maMembers[i].mxParent && IsChecked(maMembers[i].maName, maMembers[i].mxParent.get()))
+ {
+ std::unique_ptr<weld::TreeIter> xDateEntry = FindEntry(nullptr, maMembers[i].maName);
+ if (xDateEntry)
+ aExpandRows.emplace_back(std::move(xDateEntry));
+ }
+ }
+ else
+ {
+ mpChecks->append(xEntry.get());
+ insertMember(*mpChecks, *xEntry, maMembers[i], maMembers[i].mbVisible);
+ }
+
+ if (maMembers[i].mbVisible)
+ ++nVisMemCount;
+ }
+
+ mpChecks->thaw();
+
+ for (const auto& rRow : aExpandRows)
+ mpChecks->expand_row(*rRow);
+ }
+
+ if (nVisMemCount == nEnableMember)
+ {
+ // all members visible
+ mxChkToggleAll->set_state(TRISTATE_TRUE);
+ mePrevToggleAllState = TRISTATE_TRUE;
+ }
+ else if (nVisMemCount == 0)
+ {
+ // no members visible
+ mxChkToggleAll->set_state(TRISTATE_FALSE);
+ mePrevToggleAllState = TRISTATE_FALSE;
+ }
+ else
+ {
+ mxChkToggleAll->set_state(TRISTATE_INDET);
+ mePrevToggleAllState = TRISTATE_INDET;
+ }
+
+ if (nVisMemCount)
+ mpChecks->set_cursor(0);
+
+ return nVisMemCount;
+}
+
+void ScCheckListMenuControl::setConfig(const Config& rConfig)
+{
+ maConfig = rConfig;
+}
+
+bool ScCheckListMenuControl::isAllSelected() const
+{
+ return mxChkToggleAll->get_state() == TRISTATE_TRUE;
+}
+
+void ScCheckListMenuControl::getResult(ResultType& rResult)
+{
+ ResultType aResult;
+ std::unordered_set<OUString> vCheckeds = GetAllChecked();
+ size_t n = maMembers.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ if ( maMembers[i].mbLeaf )
+ {
+ OUStringBuffer aLabel(maMembers[i].maName);
+ if (aLabel.isEmpty())
+ aLabel = ScResId(STR_EMPTYDATA);
+
+ /* TODO: performance-wise this looks suspicious, concatenating to
+ * do the lookup for each leaf item seems wasteful. */
+ // Checked labels are in the form "child;parent;grandparent".
+ if (maMembers[i].mxParent)
+ {
+ std::unique_ptr<weld::TreeIter> xIter(mpChecks->make_iterator(maMembers[i].mxParent.get()));
+ do
+ {
+ aLabel.append(";" + mpChecks->get_text(*xIter));
+ }
+ while (mpChecks->iter_parent(*xIter));
+ }
+
+ bool bState = vCheckeds.find(aLabel.makeStringAndClear()) != vCheckeds.end();
+
+ ResultEntry aResultEntry;
+ aResultEntry.bValid = bState && !maMembers[i].mbHiddenByOtherFilter;
+ aResultEntry.aName = maMembers[i].maRealName;
+ aResultEntry.nValue = maMembers[i].mnValue;
+ aResultEntry.bDate = maMembers[i].mbDate;
+ aResultEntry.bValue = maMembers[i].mbValue;
+ aResult.insert(aResultEntry);
+ }
+ }
+ rResult.swap(aResult);
+}
+
+void ScCheckListMenuControl::launch(weld::Widget* pWidget, const tools::Rectangle& rRect)
+{
+ prepWindow();
+ if (!maConfig.mbAllowEmptySet)
+ // We need to have at least one member selected.
+ mxBtnOk->set_sensitive(GetCheckedEntryCount() != 0);
+
+ tools::Rectangle aRect(rRect);
+ if (maConfig.mbRTL)
+ {
+ // In RTL mode, the logical "left" is visual "right".
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ tools::Long nLeft = aRect.Left() - aRect.GetWidth();
+ aRect.SetLeft( nLeft );
+ }
+ else
+ {
+ // in LOK mode, rRect is in document pixel coordinates, so width has to be added
+ // to place the popup next to the (visual) left aligned button.
+ aRect.Move(aRect.GetWidth(), 0);
+ }
+ }
+ else if (mnWndWidth < aRect.GetWidth())
+ {
+ // Target rectangle (i.e. cell width) is wider than the window.
+ // Simulate right-aligned launch by modifying the target rectangle
+ // size.
+ tools::Long nDiff = aRect.GetWidth() - mnWndWidth;
+ aRect.AdjustLeft(nDiff );
+ }
+
+ StartPopupMode(pWidget, aRect);
+}
+
+void ScCheckListMenuControl::close(bool bOK)
+{
+ if (bOK && mxOKAction)
+ mxOKAction->execute();
+ EndPopupMode();
+}
+
+void ScCheckListMenuControl::setExtendedData(std::unique_ptr<ExtendedData> p)
+{
+ mxExtendedData = std::move(p);
+}
+
+ScCheckListMenuControl::ExtendedData* ScCheckListMenuControl::getExtendedData()
+{
+ return mxExtendedData.get();
+}
+
+void ScCheckListMenuControl::setOKAction(Action* p)
+{
+ mxOKAction.reset(p);
+}
+
+void ScCheckListMenuControl::setPopupEndAction(Action* p)
+{
+ mxPopupEndAction.reset(p);
+}
+
+void ScCheckListMenuControl::setFieldChangedAction(Action* p)
+{
+ mxFieldChangedAction.reset(p);
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, PopupModeEndHdl, weld::Popover&, void)
+{
+ mbIsPoppedUp = false;
+ clearSelectedMenuItem();
+ if (mxPopupEndAction)
+ mxPopupEndAction->execute();
+
+ DropPendingEvents();
+}
+
+int ScCheckListMenuControl::GetTextWidth(const OUString& rsName) const
+{
+ return mxDropDown->GetTextWidth(rsName);
+}
+
+int ScCheckListMenuControl::IncreaseWindowWidthToFitText(int nMaxTextWidth)
+{
+ int nBorder = nBorderWidth * 2 + 4;
+ int nNewWidth = nMaxTextWidth - nBorder;
+ if (nNewWidth > mnCheckWidthReq)
+ {
+ mnCheckWidthReq = nNewWidth;
+ int nChecksHeight = mpChecks->get_height_rows(nCheckListVisibleRows);
+ mpChecks->set_size_request(mnCheckWidthReq, nChecksHeight);
+ }
+ return mnCheckWidthReq + nBorder;
+}
+
+ScListSubMenuControl::ScListSubMenuControl(weld::Widget* pParent, ScCheckListMenuControl& rParentControl, bool bColorMenu)
+ : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filtersubdropdown.ui"))
+ , mxPopover(mxBuilder->weld_popover("FilterSubDropDown"))
+ , mxContainer(mxBuilder->weld_container("container"))
+ , mxMenu(mxBuilder->weld_tree_view("menu"))
+ , mxBackColorMenu(mxBuilder->weld_tree_view("background"))
+ , mxTextColorMenu(mxBuilder->weld_tree_view("textcolor"))
+ , mxScratchIter(mxMenu->make_iterator())
+ , mrParentControl(rParentControl)
+ , mnBackColorMenuPrefHeight(-1)
+ , mnTextColorMenuPrefHeight(-1)
+ , mbColorMenu(bColorMenu)
+{
+ mxMenu->hide();
+ mxBackColorMenu->hide();
+ mxTextColorMenu->hide();
+
+ if (!bColorMenu)
+ {
+ SetupMenu(*mxMenu);
+ mxMenu->show();
+ }
+ else
+ {
+ mxBackColorMenu->set_clicks_to_toggle(1);
+ mxBackColorMenu->enable_toggle_buttons(weld::ColumnToggleType::Radio);
+ mxBackColorMenu->connect_changed(LINK(this, ScListSubMenuControl, ColorSelChangedHdl));
+ mxTextColorMenu->set_clicks_to_toggle(1);
+ mxTextColorMenu->enable_toggle_buttons(weld::ColumnToggleType::Radio);
+ mxTextColorMenu->connect_changed(LINK(this, ScListSubMenuControl, ColorSelChangedHdl));
+ SetupMenu(*mxBackColorMenu);
+ SetupMenu(*mxTextColorMenu);
+ }
+}
+
+void ScListSubMenuControl::SetupMenu(weld::TreeView& rMenu)
+{
+ rMenu.connect_row_activated(LINK(this, ScListSubMenuControl, RowActivatedHdl));
+ rMenu.connect_key_press(LINK(this, ScListSubMenuControl, MenuKeyInputHdl));
+}
+
+void ScListSubMenuControl::StartPopupMode(weld::Widget* pParent, const tools::Rectangle& rRect)
+{
+ if (mxPopupStartAction)
+ mxPopupStartAction->execute();
+
+ mxPopover->popup_at_rect(pParent, rRect, weld::Placement::End);
+
+ weld::TreeView& rFirstMenu = mbColorMenu ? *mxBackColorMenu : *mxMenu;
+ rFirstMenu.set_cursor(0);
+ rFirstMenu.select(0);
+
+ mrParentControl.setSubMenuFocused(this);
+}
+
+void ScListSubMenuControl::EndPopupMode()
+{
+ mxPopover->popdown();
+}
+
+void ScListSubMenuControl::GrabFocus()
+{
+ weld::TreeView& rFirstMenu = mbColorMenu ? *mxBackColorMenu : *mxMenu;
+ rFirstMenu.grab_focus();
+}
+
+bool ScListSubMenuControl::IsVisible() const
+{
+ return mxPopover->get_visible();
+}
+
+void ScListSubMenuControl::resizeToFitMenuItems()
+{
+ if (!mbColorMenu)
+ mxMenu->set_size_request(-1, mxMenu->get_preferred_size().Height());
+ else
+ {
+ int nBackColorMenuPrefHeight = mnBackColorMenuPrefHeight;
+ if (nBackColorMenuPrefHeight == -1)
+ nBackColorMenuPrefHeight = mxBackColorMenu->get_preferred_size().Height();
+ mxBackColorMenu->set_size_request(-1, nBackColorMenuPrefHeight);
+ int nTextColorMenuPrefHeight = mnTextColorMenuPrefHeight;
+ if (nTextColorMenuPrefHeight == -1)
+ nTextColorMenuPrefHeight = mxTextColorMenu->get_preferred_size().Height();
+ mxTextColorMenu->set_size_request(-1, nTextColorMenuPrefHeight);
+ }
+}
+
+void ScListSubMenuControl::addItem(ScCheckListMenuControl::Action* pAction)
+{
+ ScCheckListMenuControl::MenuItemData aItem;
+ aItem.mbEnabled = true;
+ aItem.mxAction.reset(pAction);
+ maMenuItems.emplace_back(std::move(aItem));
+}
+
+void ScListSubMenuControl::addMenuItem(const OUString& rText, ScCheckListMenuControl::Action* pAction)
+{
+ addItem(pAction);
+ mxMenu->append(weld::toId(pAction), rText);
+}
+
+void ScListSubMenuControl::addMenuColorItem(const OUString& rText, bool bActive, VirtualDevice& rImage,
+ int nMenu, ScCheckListMenuControl::Action* pAction)
+{
+ addItem(pAction);
+
+ weld::TreeView& rColorMenu = nMenu == 0 ? *mxBackColorMenu : *mxTextColorMenu;
+ rColorMenu.show();
+
+ OUString sId = weld::toId(pAction);
+ rColorMenu.insert(nullptr, -1, &rText, &sId, nullptr, nullptr, false, mxScratchIter.get());
+ rColorMenu.set_toggle(*mxScratchIter, bActive ? TRISTATE_TRUE : TRISTATE_FALSE);
+ rColorMenu.set_image(*mxScratchIter, rImage);
+
+ if (mnTextColorMenuPrefHeight == -1 &&
+ &rColorMenu == mxTextColorMenu.get() &&
+ mxTextColorMenu->n_children() == nColorListVisibleRows)
+ {
+ mnTextColorMenuPrefHeight = mxTextColorMenu->get_preferred_size().Height();
+ }
+
+ if (mnBackColorMenuPrefHeight == -1 &&
+ &rColorMenu == mxBackColorMenu.get() &&
+ mxBackColorMenu->n_children() == nColorListVisibleRows)
+ {
+ mnBackColorMenuPrefHeight = mxBackColorMenu->get_preferred_size().Height();
+ }
+}
+
+void ScListSubMenuControl::addSeparator()
+{
+ ScCheckListMenuControl::MenuItemData aItem;
+ maMenuItems.emplace_back(std::move(aItem));
+
+ mxMenu->append_separator("separator" + OUString::number(maMenuItems.size()));
+}
+
+void ScListSubMenuControl::clearMenuItems()
+{
+ maMenuItems.clear();
+ mxMenu->clear();
+ mxBackColorMenu->clear();
+ mnBackColorMenuPrefHeight = -1;
+ mxTextColorMenu->clear();
+ mnTextColorMenuPrefHeight = -1;
+}
+
+IMPL_LINK(ScListSubMenuControl, MenuKeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bConsumed = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ switch (rKeyCode.GetCode())
+ {
+ case KEY_ESCAPE:
+ case KEY_LEFT:
+ {
+ mrParentControl.endSubMenu(*this);
+ bConsumed = true;
+ break;
+ }
+ case KEY_SPACE:
+ case KEY_RETURN:
+ {
+ weld::TreeView& rMenu = !mbColorMenu ? *mxMenu :
+ (mxBackColorMenu->has_focus() ? *mxBackColorMenu : *mxTextColorMenu);
+ // don't toggle checkbutton, go straight to activating entry
+ bConsumed = RowActivatedHdl(rMenu);
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if (mxTextColorMenu->get_visible() &&
+ mxBackColorMenu->has_focus() &&
+ mxBackColorMenu->get_selected_index() == mxBackColorMenu->n_children() - 1)
+ {
+ mxBackColorMenu->unselect_all();
+ mxTextColorMenu->select(0);
+ mxTextColorMenu->set_cursor(0);
+ mxTextColorMenu->grab_focus();
+ bConsumed = true;
+ }
+ break;
+ }
+ case KEY_UP:
+ {
+ if (mxBackColorMenu->get_visible() &&
+ mxTextColorMenu->has_focus() &&
+ mxTextColorMenu->get_selected_index() == 0)
+ {
+ mxTextColorMenu->unselect_all();
+ int nIndex = mxBackColorMenu->n_children() - 1;
+ mxBackColorMenu->select(nIndex);
+ mxBackColorMenu->set_cursor(nIndex);
+ mxBackColorMenu->grab_focus();
+ bConsumed = true;
+ }
+ break;
+ }
+ }
+
+ return bConsumed;
+}
+
+IMPL_LINK(ScListSubMenuControl, ColorSelChangedHdl, weld::TreeView&, rMenu, void)
+{
+ if (rMenu.get_selected_index() == -1)
+ return;
+ if (&rMenu != mxTextColorMenu.get())
+ mxTextColorMenu->unselect_all();
+ else
+ mxBackColorMenu->unselect_all();
+ rMenu.grab_focus();
+}
+
+IMPL_LINK(ScListSubMenuControl, RowActivatedHdl, weld::TreeView&, rMenu, bool)
+{
+ executeMenuItem(weld::fromId<ScCheckListMenuControl::Action*>(rMenu.get_selected_id()));
+ return true;
+}
+
+void ScListSubMenuControl::executeMenuItem(ScCheckListMenuControl::Action* pAction)
+{
+ // if no action is defined.
+ if (!pAction)
+ return;
+
+ const bool bClosePopup = pAction->execute();
+ if (bClosePopup)
+ terminateAllPopupMenus();
+}
+
+void ScListSubMenuControl::setPopupStartAction(ScCheckListMenuControl::Action* p)
+{
+ mxPopupStartAction.reset(p);
+}
+
+void ScListSubMenuControl::terminateAllPopupMenus()
+{
+ EndPopupMode();
+ mrParentControl.terminateAllPopupMenus();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/cctrl/dpcontrol.cxx b/sc/source/ui/cctrl/dpcontrol.cxx
new file mode 100644
index 0000000000..cbb1aaa456
--- /dev/null
+++ b/sc/source/ui/cctrl/dpcontrol.cxx
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpcontrol.hxx>
+
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/lok.hxx>
+#include <scitems.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <svtools/colorcfg.hxx>
+
+ScDPFieldButton::ScDPFieldButton(OutputDevice* pOutDev, const StyleSettings* pStyle, const Fraction* pZoomY, ScDocument* pDoc) :
+ mpDoc(pDoc),
+ mpOutDev(pOutDev),
+ mpStyle(pStyle),
+ mnToggleIndent(0),
+ mbBaseButton(true),
+ mbPopupButton(false),
+ mbPopupButtonMulti(false),
+ mbToggleButton(false),
+ mbToggleCollapse(false),
+ mbHasHiddenMember(false),
+ mbPopupPressed(false),
+ mbPopupLeft(false)
+{
+ if (pZoomY)
+ maZoomY = *pZoomY;
+ else
+ maZoomY = Fraction(1, 1);
+}
+
+ScDPFieldButton::~ScDPFieldButton()
+{
+}
+
+void ScDPFieldButton::setText(const OUString& rText)
+{
+ maText = rText;
+}
+
+void ScDPFieldButton::setBoundingBox(const Point& rPos, const Size& rSize, bool bLayoutRTL)
+{
+ maPos = rPos;
+ maSize = rSize;
+ if (bLayoutRTL)
+ {
+ // rPos is the logical-left position, adjust maPos to visual-left (inside the cell border)
+ maPos.AdjustX( -(maSize.Width() - 1) );
+ }
+}
+
+void ScDPFieldButton::setDrawBaseButton(bool b)
+{
+ mbBaseButton = b;
+}
+
+void ScDPFieldButton::setDrawPopupButton(bool b)
+{
+ mbPopupButton = b;
+}
+
+void ScDPFieldButton::setDrawPopupButtonMulti(bool b)
+{
+ mbPopupButtonMulti = b;
+}
+
+void ScDPFieldButton::setDrawToggleButton(bool b, bool bCollapse, sal_Int32 nIndent)
+{
+ mbToggleButton = b;
+ mbToggleCollapse = bCollapse;
+ mnToggleIndent = nIndent;
+}
+
+void ScDPFieldButton::setHasHiddenMember(bool b)
+{
+ mbHasHiddenMember = b;
+}
+
+void ScDPFieldButton::setPopupPressed(bool b)
+{
+ mbPopupPressed = b;
+}
+
+void ScDPFieldButton::setPopupLeft(bool b)
+{
+ mbPopupLeft = b;
+}
+
+void ScDPFieldButton::draw()
+{
+ bool bOldMapEnabled = mpOutDev->IsMapModeEnabled();
+
+ if (mpOutDev->GetMapMode().GetMapUnit() != MapUnit::MapPixel)
+ mpOutDev->EnableMapMode(false);
+
+ if (mbBaseButton)
+ {
+ // Background
+ tools::Rectangle aRect(maPos, maSize);
+ mpOutDev->SetLineColor(mpStyle->GetFaceColor());
+ mpOutDev->SetFillColor(mpStyle->GetFaceColor());
+ mpOutDev->DrawRect(aRect);
+
+ // Border lines
+ mpOutDev->SetLineColor(mpStyle->GetLightColor());
+ mpOutDev->DrawLine(maPos, Point(maPos.X(), maPos.Y()+maSize.Height()-1));
+ mpOutDev->DrawLine(maPos, Point(maPos.X()+maSize.Width()-1, maPos.Y()));
+
+ mpOutDev->SetLineColor(mpStyle->GetShadowColor());
+ mpOutDev->DrawLine(Point(maPos.X(), maPos.Y()+maSize.Height()-1),
+ Point(maPos.X()+maSize.Width()-1, maPos.Y()+maSize.Height()-1));
+ mpOutDev->DrawLine(Point(maPos.X()+maSize.Width()-1, maPos.Y()),
+ Point(maPos.X()+maSize.Width()-1, maPos.Y()+maSize.Height()-1));
+
+ // Field name.
+ // Get the font and size the same way as in scenario selection (lcl_DrawOneFrame in gridwin4.cxx)
+ vcl::Font aTextFont( mpStyle->GetAppFont() );
+ if ( mpDoc )
+ {
+ // use ScPatternAttr::GetFont only for font size
+ vcl::Font aAttrFont;
+ mpDoc->GetPool()->GetDefaultItem(ATTR_PATTERN).fillFontOnly(aAttrFont, mpOutDev, &maZoomY);
+ aTextFont.SetFontSize(aAttrFont.GetFontSize());
+ }
+ mpOutDev->SetFont(aTextFont);
+ mpOutDev->SetTextColor(mpStyle->GetButtonTextColor());
+
+ Point aTextPos = maPos;
+ tools::Long nTHeight = mpOutDev->GetTextHeight();
+ aTextPos.setX(maPos.getX() + 2); // 2 = Margin
+ aTextPos.setY(maPos.getY() + (maSize.Height()-nTHeight)/2);
+
+ mpOutDev->Push(vcl::PushFlags::CLIPREGION);
+ mpOutDev->IntersectClipRegion(aRect);
+ mpOutDev->DrawText(aTextPos, maText);
+ mpOutDev->Pop();
+ }
+
+ if (mbPopupButton || mbPopupButtonMulti)
+ drawPopupButton();
+
+ if (mbToggleButton)
+ drawToggleButton();
+
+ mpOutDev->EnableMapMode(bOldMapEnabled);
+}
+
+void ScDPFieldButton::getPopupBoundingBox(Point& rPos, Size& rSize) const
+{
+ float fScaleFactor = mpOutDev->GetDPIScaleFactor();
+
+ tools::Long nMaxSize = 18 * fScaleFactor; // Button max size in either dimension
+
+ tools::Long nW = std::min(maSize.getWidth() / 2, nMaxSize);
+ tools::Long nH = std::min(maSize.getHeight(), nMaxSize);
+
+ double fZoom = static_cast<double>(maZoomY) > 1.0 ? static_cast<double>(maZoomY) : 1.0;
+ if (fZoom > 1.0)
+ {
+ nW = fZoom * (nW - 1);
+ nH = fZoom * (nH - 1);
+ }
+
+ // #i114944# AutoFilter button is left-aligned in RTL.
+ // DataPilot button is always right-aligned for now, so text output isn't affected.
+ if (mbPopupLeft)
+ rPos.setX(maPos.getX());
+ else
+ rPos.setX(maPos.getX() + maSize.getWidth() - nW);
+
+ rPos.setY(maPos.getY() + maSize.getHeight() - nH);
+ rSize.setWidth(nW);
+ rSize.setHeight(nH);
+}
+
+void ScDPFieldButton::getToggleBoundingBox(Point& rPos, Size& rSize) const
+{
+ const float fScaleFactor = mpOutDev->GetDPIScaleFactor();
+
+ tools::Long nMaxSize = 13 * fScaleFactor; // Button max size in either dimension
+ tools::Long nMargin = 3 * fScaleFactor;
+
+ tools::Long nIndent = fScaleFactor * o3tl::convert(mnToggleIndent, o3tl::Length::twip, o3tl::Length::px);
+ tools::Long nW = std::min(maSize.getWidth() / 2, nMaxSize);
+ tools::Long nH = std::min(maSize.getHeight(), nMaxSize);
+ nIndent = std::min(nIndent, maSize.getWidth());
+
+ double fZoom = static_cast<double>(maZoomY) > 1.0 ? static_cast<double>(maZoomY) : 1.0;
+ if (fZoom > 1.0)
+ {
+ nW = fZoom * (nW - 1);
+ nH = fZoom * (nH - 1);
+ nIndent = fZoom * (nIndent -1);
+ nMargin = fZoom * (nMargin - 1);
+ }
+
+ // FIXME: RTL case ?
+ rPos.setX(maPos.getX() + nIndent - nW + nMargin);
+ rPos.setY(maPos.getY() + maSize.getHeight() / 2 - nH / 2 + nMargin);
+ rSize.setWidth(nW - nMargin - 1);
+ rSize.setHeight(nH - nMargin - 1);
+}
+
+void ScDPFieldButton::drawPopupButton()
+{
+ Point aPos;
+ Size aSize;
+ getPopupBoundingBox(aPos, aSize);
+
+ float fScaleFactor = mpOutDev->GetDPIScaleFactor();
+
+ // Button background color
+ Color aFaceColor = mpStyle->GetFaceColor();
+ Color aBackgroundColor
+ = mbHasHiddenMember ? mpStyle->GetHighlightColor()
+ : mbPopupPressed ? mpStyle->GetShadowColor() : aFaceColor;
+
+ // Button line color
+ mpOutDev->SetLineColor(mpStyle->GetLabelTextColor());
+ // If the document background is light and face color is dark, use ShadowColor instead
+ Color aDocColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ if (aDocColor.IsBright() && aFaceColor.IsDark())
+ mpOutDev->SetLineColor(mpStyle->GetShadowColor());
+
+ mpOutDev->SetFillColor(aBackgroundColor);
+ mpOutDev->DrawRect(tools::Rectangle(aPos, aSize));
+
+ // the arrowhead
+ Color aArrowColor = mbHasHiddenMember ? mpStyle->GetHighlightTextColor() : mpStyle->GetButtonTextColor();
+ // FIXME: HACK: The following DrawPolygon draws twice in lok rtl mode for some reason.
+ // => one at the correct location with fill (possibly no outline)
+ // => and the other at an x offset with outline and without fill
+ // eg. Replacing this with a DrawRect() does not have any such problems.
+ comphelper::LibreOfficeKit::isActive() ? mpOutDev->SetLineColor() : mpOutDev->SetLineColor(aArrowColor);
+ mpOutDev->SetFillColor(aArrowColor);
+
+ Point aCenter(aPos.X() + (aSize.Width() / 2), aPos.Y() + (aSize.Height() / 2));
+
+ Size aArrowSize(4 * fScaleFactor, 2 * fScaleFactor);
+
+ tools::Polygon aPoly(3);
+ aPoly.SetPoint(Point(aCenter.X() - aArrowSize.Width(), aCenter.Y() - aArrowSize.Height()), 0);
+ aPoly.SetPoint(Point(aCenter.X() + aArrowSize.Width(), aCenter.Y() - aArrowSize.Height()), 1);
+ aPoly.SetPoint(Point(aCenter.X(), aCenter.Y() + aArrowSize.Height()), 2);
+ mpOutDev->DrawPolygon(aPoly);
+
+ if (mbHasHiddenMember)
+ {
+ // tiny little box to display in presence of hidden member(s).
+ Point aBoxPos(aPos.X() + aSize.Width() - 5 * fScaleFactor, aPos.Y() + aSize.Height() - 5 * fScaleFactor);
+ Size aBoxSize(3 * fScaleFactor, 3 * fScaleFactor);
+ mpOutDev->DrawRect(tools::Rectangle(aBoxPos, aBoxSize));
+ }
+}
+
+void ScDPFieldButton::drawToggleButton()
+{
+ Point aPos;
+ Size aSize;
+ getToggleBoundingBox(aPos, aSize);
+
+ // Background & outer black border
+ mpOutDev->SetLineColor(COL_BLACK);
+ mpOutDev->SetFillColor();
+ mpOutDev->DrawRect(tools::Rectangle(aPos, aSize));
+
+ Point aCenter(aPos.X() + aSize.getWidth() / 2, aPos.Y() + aSize.getHeight() / 2);
+
+ mpOutDev->DrawLine(
+ Point(aPos.X() + 2, aCenter.Y()),
+ Point(aPos.X() + aSize.getWidth() - 2, aCenter.Y()));
+
+ if (!mbToggleCollapse)
+ {
+ mpOutDev->DrawLine(
+ Point(aCenter.X(), aPos.Y() + 2),
+ Point(aCenter.X(), aPos.Y() + aSize.getHeight() - 2));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/cctrl/editfield.cxx b/sc/source/ui/cctrl/editfield.cxx
new file mode 100644
index 0000000000..fd9d1e6b0b
--- /dev/null
+++ b/sc/source/ui/cctrl/editfield.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 .
+ */
+
+#ifdef SC_DLLIMPLEMENTATION
+#undef SC_DLLIMPLEMENTATION
+#endif
+#include <editfield.hxx>
+#include <comphelper/string.hxx>
+#include <rtl/math.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <global.hxx>
+
+namespace {
+
+sal_Unicode lclGetDecSep()
+{
+ return ScGlobal::getLocaleData().getNumDecimalSep()[0];
+}
+
+} // namespace
+
+ScDoubleField::ScDoubleField(std::unique_ptr<weld::Entry> xEntry)
+ : m_xEntry(std::move(xEntry))
+{
+}
+
+bool ScDoubleField::GetValue( double& rfValue ) const
+{
+ OUString aStr(comphelper::string::strip(m_xEntry->get_text(), ' '));
+ bool bOk = !aStr.isEmpty();
+ if( bOk )
+ {
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nEnd;
+ rfValue = ScGlobal::getLocaleData().stringToDouble( aStr, true, &eStatus, &nEnd );
+ bOk = (eStatus == rtl_math_ConversionStatus_Ok) && (nEnd == aStr.getLength() );
+ }
+ return bOk;
+}
+
+void ScDoubleField::SetValue( double fValue, sal_Int32 nDecPlaces )
+{
+ m_xEntry->set_text( ::rtl::math::doubleToUString( fValue, rtl_math_StringFormat_G,
+ nDecPlaces, lclGetDecSep(), true/*bEraseTrailingDecZeros*/ ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/cctrl/tbzoomsliderctrl.cxx b/sc/source/ui/cctrl/tbzoomsliderctrl.cxx
new file mode 100644
index 0000000000..4ec776de10
--- /dev/null
+++ b/sc/source/ui/cctrl/tbzoomsliderctrl.cxx
@@ -0,0 +1,458 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <tbzoomsliderctrl.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <utility>
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/event.hxx>
+#include <vcl/image.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <iterator>
+#include <set>
+#include <bitmaps.hlst>
+
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+// class ScZoomSliderControl ---------------------------------------
+
+SFX_IMPL_TOOLBOX_CONTROL( ScZoomSliderControl, SvxZoomSliderItem );
+
+ScZoomSliderControl::ScZoomSliderControl(
+ sal_uInt16 nSlotId,
+ ToolBoxItemId nId,
+ ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.Invalidate();
+}
+
+ScZoomSliderControl::~ScZoomSliderControl()
+{
+
+}
+
+void ScZoomSliderControl::StateChangedAtToolBoxControl( sal_uInt16 /*nSID*/, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox& rTbx = GetToolBox();
+ ScZoomSliderWnd* pBox = static_cast<ScZoomSliderWnd*>(rTbx.GetItemWindow( nId ));
+ OSL_ENSURE( pBox ,"Control not found!" );
+
+ if (SfxItemState::DEFAULT != eState || SfxItemState::DISABLED == eState)
+ {
+ SvxZoomSliderItem aZoomSliderItem( 100 );
+ pBox->Disable();
+ pBox->UpdateFromItem( &aZoomSliderItem );
+ }
+ else
+ {
+ pBox->Enable();
+ OSL_ENSURE( dynamic_cast<const SvxZoomSliderItem*>( pState) != nullptr, "invalid item type" );
+ const SvxZoomSliderItem* pZoomSliderItem = dynamic_cast< const SvxZoomSliderItem* >( pState );
+
+ OSL_ENSURE( pZoomSliderItem, "Sc::ScZoomSliderControl::StateChanged(), wrong item type!" );
+ if( pZoomSliderItem )
+ pBox->UpdateFromItem( pZoomSliderItem );
+ }
+}
+
+VclPtr<InterimItemWindow> ScZoomSliderControl::CreateItemWindow( vcl::Window *pParent )
+{
+ // #i98000# Don't try to get a value via SfxViewFrame::Current here.
+ // The view's value is always notified via StateChanged later.
+ VclPtrInstance<ScZoomSliderWnd> xSlider( pParent,
+ css::uno::Reference< css::frame::XDispatchProvider >( m_xFrame->getController(),
+ css::uno::UNO_QUERY ), 100 );
+ return xSlider;
+}
+
+constexpr sal_uInt16 gnSliderCenter(100);
+
+const tools::Long nButtonWidth = 10;
+const tools::Long nButtonHeight = 10;
+const tools::Long nIncDecWidth = 11;
+const tools::Long nIncDecHeight = 11;
+const tools::Long nSliderHeight = 2;
+const tools::Long nSliderWidth = 4;
+const tools::Long nSnappingHeight = 4;
+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
+
+sal_uInt16 ScZoomSlider::Offset2Zoom( tools::Long nOffset ) const
+{
+ Size aSliderWindowSize = GetOutputSizePixel();
+ const tools::Long nControlWidth = aSliderWindowSize.Width();
+ sal_uInt16 nRet = 0;
+
+ if( nOffset < nSliderXOffset )
+ return mnMinZoom;
+ if( nOffset > nControlWidth - nSliderXOffset )
+ return mnMaxZoom;
+
+ // check for snapping points:
+ auto aSnappingPointIter = std::find_if(maSnappingPointOffsets.begin(), maSnappingPointOffsets.end(),
+ [nOffset](const tools::Long nCurrent) { return std::abs(nCurrent - nOffset) < nSnappingEpsilon; });
+ if (aSnappingPointIter != maSnappingPointOffsets.end())
+ {
+ nOffset = *aSnappingPointIter;
+ auto nCount = static_cast<sal_uInt16>(std::distance(maSnappingPointOffsets.begin(), aSnappingPointIter));
+ nRet = maSnappingPointZooms[ nCount ];
+ }
+
+ if( 0 == nRet )
+ {
+ if( nOffset < nControlWidth / 2 )
+ {
+ // first half of slider
+ const tools::Long nFirstHalfRange = gnSliderCenter - mnMinZoom;
+ const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
+ const tools::Long nZoomPerSliderPixel = (1000 * nFirstHalfRange) / nHalfSliderWidth;
+ const tools::Long nOffsetToSliderLeft = nOffset - nSliderXOffset;
+ nRet = mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
+ }
+ else
+ {
+ // second half of slider
+ const tools::Long nSecondHalfRange = mnMaxZoom - gnSliderCenter;
+ const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
+ const tools::Long nZoomPerSliderPixel = 1000 * nSecondHalfRange / nHalfSliderWidth;
+ const tools::Long nOffsetToSliderCenter = nOffset - nControlWidth/2;
+ nRet = gnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
+ }
+ }
+
+ if( nRet < mnMinZoom )
+ return mnMinZoom;
+
+ else if( nRet > mnMaxZoom )
+ return mnMaxZoom;
+
+ return nRet;
+}
+
+tools::Long ScZoomSlider::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
+{
+ Size aSliderWindowSize = GetOutputSizePixel();
+ const tools::Long nControlWidth = aSliderWindowSize.Width();
+ tools::Long nRect = nSliderXOffset;
+
+ const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
+ if( nCurrentZoom <= gnSliderCenter )
+ {
+ nCurrentZoom = nCurrentZoom - mnMinZoom;
+ const tools::Long nFirstHalfRange = gnSliderCenter - mnMinZoom;
+ const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nFirstHalfRange;
+ const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
+ nRect += nOffset;
+ }
+ else
+ {
+ nCurrentZoom = nCurrentZoom - gnSliderCenter;
+ const tools::Long nSecondHalfRange = mnMaxZoom - gnSliderCenter;
+ const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nSecondHalfRange;
+ const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
+ nRect += nHalfSliderWidth + nOffset;
+ }
+ return nRect;
+}
+
+ScZoomSliderWnd::ScZoomSliderWnd( vcl::Window* pParent,
+ const css::uno::Reference< css::frame::XDispatchProvider >& rDispatchProvider,
+ sal_uInt16 nCurrentZoom ):
+ InterimItemWindow(pParent, "modules/scalc/ui/zoombox.ui", "ZoomBox"),
+ mxWidget(new ScZoomSlider(rDispatchProvider, nCurrentZoom)),
+ mxWeld(new weld::CustomWeld(*m_xBuilder, "zoom", *mxWidget))
+{
+ Size aLogicalSize( 115, 40 );
+ Size aSliderSize = LogicToPixel(aLogicalSize, MapMode(MapUnit::Map10thMM));
+ Size aPreferredSize(aSliderSize.Width() * nSliderWidth-1, aSliderSize.Height() + nSliderHeight);
+ mxWidget->GetDrawingArea()->set_size_request(aPreferredSize.Width(), aPreferredSize.Height());
+ mxWidget->SetOutputSizePixel(aPreferredSize);
+ SetSizePixel(aPreferredSize);
+}
+
+ScZoomSliderWnd::~ScZoomSliderWnd()
+{
+ disposeOnce();
+}
+
+void ScZoomSliderWnd::dispose()
+{
+ mxWeld.reset();
+ mxWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+ScZoomSlider::ScZoomSlider(css::uno::Reference< css::frame::XDispatchProvider> xDispatchProvider,
+ sal_uInt16 nCurrentZoom)
+ : mnCurrentZoom( nCurrentZoom ),
+ mnMinZoom( 10 ),
+ mnMaxZoom( 400 ),
+ mbOmitPaint( false ),
+ m_xDispatchProvider(std::move(xDispatchProvider))
+{
+ maSliderButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERBUTTON);
+ maIncreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERINCREASE);
+ maDecreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERDECREASE);
+}
+
+
+bool ScZoomSlider::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ Size aSliderWindowSize = GetOutputSizePixel();
+
+ const Point aPoint = rMEvt.GetPosPixel();
+
+ const tools::Long nButtonLeftOffset = ( nSliderXOffset - nIncDecWidth )/2;
+ const tools::Long nButtonRightOffset = ( nSliderXOffset + nIncDecWidth )/2;
+
+ const tools::Long nOldZoom = mnCurrentZoom;
+
+ // click to - button
+ if ( aPoint.X() >= nButtonLeftOffset && aPoint.X() <= nButtonRightOffset )
+ {
+ mnCurrentZoom = mnCurrentZoom - 5;
+ }
+ // click to + button
+ else if ( aPoint.X() >= aSliderWindowSize.Width() - nSliderXOffset + nButtonLeftOffset &&
+ aPoint.X() <= aSliderWindowSize.Width() - nSliderXOffset + nButtonRightOffset )
+ {
+ mnCurrentZoom = mnCurrentZoom + 5;
+ }
+ else if( aPoint.X() >= nSliderXOffset && aPoint.X() <= aSliderWindowSize.Width() - nSliderXOffset )
+ {
+ mnCurrentZoom = Offset2Zoom( aPoint.X() );
+ }
+
+ if( mnCurrentZoom < mnMinZoom )
+ mnCurrentZoom = mnMinZoom;
+ else if( mnCurrentZoom > mnMaxZoom )
+ mnCurrentZoom = mnMaxZoom;
+
+ if( nOldZoom == mnCurrentZoom )
+ return true;
+
+ tools::Rectangle aRect( Point( 0, 0 ), aSliderWindowSize );
+
+ Invalidate(aRect);
+ mbOmitPaint = true;
+
+ SvxZoomSliderItem aZoomSliderItem( mnCurrentZoom );
+
+ css::uno::Any a;
+ aZoomSliderItem.QueryValue( a );
+
+ css::uno::Sequence aArgs{ comphelper::makePropertyValue("ScalingFactor", a) };
+
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:ScalingFactor", aArgs );
+
+ mbOmitPaint = false;
+
+ return true;
+}
+
+bool ScZoomSlider::MouseMove( const MouseEvent& rMEvt )
+{
+ Size aSliderWindowSize = GetOutputSizePixel();
+ const tools::Long nControlWidth = aSliderWindowSize.Width();
+ const short nButtons = rMEvt.GetButtons();
+
+ // check mouse move with button pressed
+ if ( 1 == nButtons )
+ {
+ const Point aPoint = rMEvt.GetPosPixel();
+
+ if ( aPoint.X() >= nSliderXOffset && aPoint.X() <= nControlWidth - nSliderXOffset )
+ {
+ mnCurrentZoom = Offset2Zoom( aPoint.X() );
+
+ tools::Rectangle aRect(Point(0, 0), aSliderWindowSize);
+ Invalidate(aRect);
+
+ mbOmitPaint = true; // optimization: paint before executing command,
+
+ // commit state change
+ SvxZoomSliderItem aZoomSliderItem( mnCurrentZoom );
+
+ css::uno::Any a;
+ aZoomSliderItem.QueryValue( a );
+
+ css::uno::Sequence aArgs{ comphelper::makePropertyValue("ScalingFactor", a) };
+
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:ScalingFactor", aArgs );
+
+ mbOmitPaint = false;
+ }
+ }
+
+ return false;
+}
+
+void ScZoomSliderWnd::UpdateFromItem( const SvxZoomSliderItem* pZoomSliderItem )
+{
+ mxWidget->UpdateFromItem(pZoomSliderItem);
+}
+
+void ScZoomSlider::UpdateFromItem(const SvxZoomSliderItem* pZoomSliderItem)
+{
+ if( pZoomSliderItem )
+ {
+ mnCurrentZoom = pZoomSliderItem->GetValue();
+ mnMinZoom = pZoomSliderItem->GetMinZoom();
+ mnMaxZoom = pZoomSliderItem->GetMaxZoom();
+
+ OSL_ENSURE( mnMinZoom <= mnCurrentZoom &&
+ mnMinZoom < gnSliderCenter &&
+ mnMaxZoom >= mnCurrentZoom &&
+ mnMaxZoom > gnSliderCenter,
+ "Looks like the zoom slider item is corrupted" );
+ const css::uno::Sequence < sal_Int32 >& rSnappingPoints = pZoomSliderItem->GetSnappingPoints();
+ maSnappingPointOffsets.clear();
+ maSnappingPointZooms.clear();
+
+ // get all snapping points:
+ std::set< sal_uInt16 > aTmpSnappingPoints;
+ std::transform(rSnappingPoints.begin(), rSnappingPoints.end(), std::inserter(aTmpSnappingPoints, aTmpSnappingPoints.end()),
+ [](const sal_Int32 nSnappingPoint) -> sal_uInt16 { return 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 )
+ {
+ maSnappingPointOffsets.push_back( nCurrentOffset );
+ maSnappingPointZooms.push_back( nCurrent );
+ nLastOffset = nCurrentOffset;
+ }
+ }
+ }
+
+ Size aSliderWindowSize = GetOutputSizePixel();
+ tools::Rectangle aRect(Point(0, 0), aSliderWindowSize);
+
+ if ( !mbOmitPaint )
+ Invalidate(aRect);
+}
+
+void ScZoomSlider::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ DoPaint(rRenderContext);
+}
+
+void ScZoomSlider::DoPaint(vcl::RenderContext& rRenderContext)
+{
+ if (mbOmitPaint)
+ return;
+
+ Size aSliderWindowSize(GetOutputSizePixel());
+ tools::Rectangle aRect(Point(0, 0), aSliderWindowSize);
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev(rRenderContext);
+ pVDev->SetOutputSizePixel(aSliderWindowSize);
+
+ tools::Rectangle aSlider = aRect;
+
+ aSlider.AdjustTop((aSliderWindowSize.Height() - nSliderHeight) / 2 - 1 );
+ aSlider.SetBottom( aSlider.Top() + nSliderHeight );
+ aSlider.AdjustLeft(nSliderXOffset );
+ aSlider.AdjustRight( -nSliderXOffset );
+
+ tools::Rectangle aFirstLine(aSlider);
+ aFirstLine.SetBottom( aFirstLine.Top() );
+
+ tools::Rectangle aSecondLine(aSlider);
+ aSecondLine.SetTop( aSecondLine.Bottom() );
+
+ tools::Rectangle aLeft(aSlider);
+ aLeft.SetRight( aLeft.Left() );
+
+ tools::Rectangle aRight(aSlider);
+ aRight.SetLeft( aRight.Right() );
+
+ // draw VirtualDevice's background color
+ Color aStartColor = rRenderContext.GetSettings().GetStyleSettings().GetFaceColor();
+ Color aEndColor = rRenderContext.GetSettings().GetStyleSettings().GetFaceColor();
+
+ if (aEndColor.IsDark())
+ aStartColor = aEndColor;
+
+ Gradient aGradient;
+ aGradient.SetAngle(0_deg10);
+ aGradient.SetStyle(css::awt::GradientStyle_LINEAR);
+
+ aGradient.SetStartColor(aStartColor);
+ aGradient.SetEndColor(aEndColor);
+ pVDev->DrawGradient(aRect, aGradient);
+
+ // draw slider
+ pVDev->SetLineColor(COL_WHITE);
+ pVDev->DrawRect(aSecondLine);
+ pVDev->DrawRect(aRight);
+
+ pVDev->SetLineColor(COL_GRAY);
+ pVDev->DrawRect(aFirstLine);
+ pVDev->DrawRect(aLeft);
+
+ // draw snapping points:
+ for (const auto& rSnappingPointOffset : maSnappingPointOffsets)
+ {
+ pVDev->SetLineColor(COL_GRAY);
+ tools::Rectangle aSnapping(aRect);
+ aSnapping.SetBottom( aSlider.Top() );
+ aSnapping.SetTop( aSnapping.Bottom() - nSnappingHeight );
+ aSnapping.AdjustLeft(rSnappingPointOffset );
+ aSnapping.SetRight( aSnapping.Left() );
+ pVDev->DrawRect(aSnapping);
+
+ aSnapping.AdjustTop(nSnappingHeight + nSliderHeight );
+ aSnapping.AdjustBottom(nSnappingHeight + nSliderHeight );
+ pVDev->DrawRect(aSnapping);
+ }
+
+ // draw slider button
+ Point aImagePoint = aRect.TopLeft();
+ aImagePoint.AdjustX(Zoom2Offset(mnCurrentZoom) );
+ aImagePoint.AdjustX( -(nButtonWidth / 2) );
+ aImagePoint.AdjustY( (aSliderWindowSize.Height() - nButtonHeight) / 2 );
+ pVDev->DrawImage(aImagePoint, maSliderButton);
+
+ // draw decrease button
+ aImagePoint = aRect.TopLeft();
+ aImagePoint.AdjustX((nSliderXOffset - nIncDecWidth) / 2 );
+ aImagePoint.AdjustY((aSliderWindowSize.Height() - nIncDecHeight) / 2 );
+ pVDev->DrawImage(aImagePoint, maDecreaseButton);
+
+ // draw increase button
+ aImagePoint.setX( aRect.Left() + aSliderWindowSize.Width() - nIncDecWidth - (nSliderXOffset - nIncDecWidth) / 2 );
+ pVDev->DrawImage(aImagePoint, maIncreaseButton);
+
+ rRenderContext.DrawOutDev(Point(0, 0), aSliderWindowSize, Point(0, 0), aSliderWindowSize, *pVDev);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/condformat/colorformat.cxx b/sc/source/ui/condformat/colorformat.cxx
new file mode 100644
index 0000000000..d97f53fe68
--- /dev/null
+++ b/sc/source/ui/condformat/colorformat.cxx
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <colorformat.hxx>
+#include <colorscale.hxx>
+
+#include <document.hxx>
+
+#include <svl/numformat.hxx>
+#include <svx/colorbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+namespace {
+
+void SetType(const ScColorScaleEntry* pEntry, weld::ComboBox& rLstBox)
+{
+ rLstBox.set_active(pEntry->GetType());
+}
+
+void GetType(const weld::ComboBox& rLstBox, const weld::Entry& rEd, ScColorScaleEntry* pEntry, SvNumberFormatter* pNumberFormatter,
+ ScDocument* pDoc, const ScAddress& rPos )
+{
+ double nVal = 0;
+ sal_uInt32 nIndex = 0;
+ pEntry->SetType(static_cast<ScColorScaleEntryType>(rLstBox.get_active()));
+ switch (rLstBox.get_active())
+ {
+ case COLORSCALE_AUTO:
+ case COLORSCALE_MIN:
+ case COLORSCALE_MAX:
+ break;
+ case COLORSCALE_PERCENTILE:
+ case COLORSCALE_VALUE:
+ case COLORSCALE_PERCENT:
+ (void)pNumberFormatter->IsNumberFormat( rEd.get_text(), nIndex, nVal );
+ pEntry->SetValue(nVal);
+ break;
+ case COLORSCALE_FORMULA:
+ pEntry->SetFormula(rEd.get_text(), *pDoc, rPos);
+ break;
+ }
+}
+
+OUString convertNumberToString(double nVal, const ScDocument* pDoc)
+{
+ SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
+ OUString aText;
+ pNumberFormatter->GetInputLineString(nVal, 0, aText);
+ return aText;
+}
+
+void SetValue( const ScDocument* pDoc, const ScColorScaleEntry* pEntry, weld::Entry& rEdit)
+{
+ if(pEntry->GetType() == COLORSCALE_FORMULA)
+ rEdit.set_text(pEntry->GetFormula(formula::FormulaGrammar::GRAM_DEFAULT));
+ else if(pEntry->GetType() != COLORSCALE_MIN && pEntry->GetType() != COLORSCALE_MAX)
+ rEdit.set_text(convertNumberToString(pEntry->GetValue(), pDoc));
+ else
+ rEdit.set_sensitive(false);
+}
+
+}
+
+ScDataBarSettingsDlg::ScDataBarSettingsDlg(weld::Window* pParent, const ScDataBarFormatData& rData, ScDocument* pDoc, const ScAddress& rPos)
+ : GenericDialogController(pParent, "modules/scalc/ui/databaroptions.ui", "DataBarOptions")
+ , mpNumberFormatter(pDoc->GetFormatTable())
+ , mpDoc(pDoc)
+ , maPos(rPos)
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxLbPos(new ColorListBox(m_xBuilder->weld_menu_button("positive_colour"), [this]{ return m_xDialog.get(); }))
+ , mxLbNeg(new ColorListBox(m_xBuilder->weld_menu_button("negative_colour"), [this]{ return m_xDialog.get(); }))
+ , mxLbAxisCol(new ColorListBox(m_xBuilder->weld_menu_button("axis_colour"), [this]{ return m_xDialog.get(); }))
+ , mxLbFillType(m_xBuilder->weld_combo_box("fill_type"))
+ , mxLbTypeMin(m_xBuilder->weld_combo_box("min"))
+ , mxLbTypeMax(m_xBuilder->weld_combo_box("max"))
+ , mxLbAxisPos(m_xBuilder->weld_combo_box("axis_pos"))
+ , mxEdMin(m_xBuilder->weld_entry("min_value"))
+ , mxEdMax(m_xBuilder->weld_entry("max_value"))
+ , mxLenMin(m_xBuilder->weld_entry("min_length"))
+ , mxLenMax(m_xBuilder->weld_entry("max_length"))
+ , mxCbOnlyBar(m_xBuilder->weld_check_button("only_bar"))
+ , mxStrSameValueFT(m_xBuilder->weld_label("str_same_value"))
+{
+ maStrWarnSameValue = mxStrSameValueFT->get_label();
+
+ Init();
+
+ mxLbPos->SelectEntry(rData.maPositiveColor);
+ mxLbFillType->set_active( rData.mbGradient ? 1 : 0 );
+ if (rData.mxNegativeColor)
+ mxLbNeg->SelectEntry(*rData.mxNegativeColor);
+
+ switch (rData.meAxisPosition)
+ {
+ case databar::NONE:
+ mxLbAxisPos->set_active(2);
+ break;
+ case databar::AUTOMATIC:
+ mxLbAxisPos->set_active(0);
+ break;
+ case databar::MIDDLE:
+ mxLbAxisPos->set_active(1);
+ break;
+ }
+ ::SetType(rData.mpLowerLimit.get(), *mxLbTypeMin);
+ ::SetType(rData.mpUpperLimit.get(), *mxLbTypeMax);
+ SetValue(mpDoc, rData.mpLowerLimit.get(), *mxEdMin);
+ SetValue(mpDoc, rData.mpUpperLimit.get(), *mxEdMax);
+ mxLenMin->set_text(convertNumberToString(rData.mnMinLength, mpDoc));
+ mxLenMax->set_text(convertNumberToString(rData.mnMaxLength, mpDoc));
+ mxLbAxisCol->SelectEntry(rData.maAxisColor);
+ mxCbOnlyBar->set_active(rData.mbOnlyBar);
+
+ TypeSelectHdl(*mxLbTypeMin);
+ PosSelectHdl(*mxLbTypeMin);
+}
+
+ScDataBarSettingsDlg::~ScDataBarSettingsDlg()
+{
+}
+
+void ScDataBarSettingsDlg::Init()
+{
+ mxLbNeg->SelectEntry(COL_LIGHTRED);
+ mxLbAxisCol->SelectEntry(COL_BLACK);
+ mxLbPos->SelectEntry(0x2a6099);
+ mxBtnOk->connect_clicked( LINK( this, ScDataBarSettingsDlg, OkBtnHdl ) );
+
+ mxLbTypeMin->connect_changed( LINK( this, ScDataBarSettingsDlg, TypeSelectHdl ) );
+ mxLbTypeMax->connect_changed( LINK( this, ScDataBarSettingsDlg, TypeSelectHdl ) );
+ mxLbAxisPos->connect_changed( LINK( this, ScDataBarSettingsDlg, PosSelectHdl ) );
+
+}
+
+namespace {
+
+void GetAxesPosition(ScDataBarFormatData* pData, const weld::ComboBox& rLbox)
+{
+ switch (rLbox.get_active())
+ {
+ case 0:
+ pData->meAxisPosition = databar::AUTOMATIC;
+ break;
+ case 1:
+ pData->meAxisPosition = databar::MIDDLE;
+ break;
+ case 2:
+ pData->meAxisPosition = databar::NONE;
+ break;
+ }
+}
+
+void SetBarLength(ScDataBarFormatData* pData, const OUString& minStr, const OUString& maxStr, SvNumberFormatter* mpNumberFormatter)
+{
+ double nMinValue = 0;
+ sal_uInt32 nIndex = 0;
+ (void)mpNumberFormatter->IsNumberFormat(minStr, nIndex, nMinValue);
+ nIndex = 0;
+ double nMaxValue = 0;
+ (void)mpNumberFormatter->IsNumberFormat(maxStr, nIndex, nMaxValue);
+ pData->mnMinLength = nMinValue;
+ pData->mnMaxLength = nMaxValue;
+}
+
+}
+
+ScDataBarFormatData* ScDataBarSettingsDlg::GetData()
+{
+ ScDataBarFormatData* pData = new ScDataBarFormatData();
+ pData->maPositiveColor = mxLbPos->GetSelectEntryColor();
+ pData->mxNegativeColor = mxLbNeg->GetSelectEntryColor();
+ pData->mbGradient = ( mxLbFillType->get_active() == 1 );
+ pData->mpUpperLimit.reset(new ScColorScaleEntry());
+ pData->mpLowerLimit.reset(new ScColorScaleEntry());
+ pData->maAxisColor = mxLbAxisCol->GetSelectEntryColor();
+ pData->mbOnlyBar = mxCbOnlyBar->get_active();
+
+ ::GetType(*mxLbTypeMin, *mxEdMin, pData->mpLowerLimit.get(), mpNumberFormatter, mpDoc, maPos);
+ ::GetType(*mxLbTypeMax, *mxEdMax, pData->mpUpperLimit.get(), mpNumberFormatter, mpDoc, maPos);
+ GetAxesPosition(pData, *mxLbAxisPos);
+ SetBarLength(pData, mxLenMin->get_text(), mxLenMax->get_text(), mpNumberFormatter);
+
+ return pData;
+}
+
+IMPL_LINK_NOARG(ScDataBarSettingsDlg, OkBtnHdl, weld::Button&, void)
+{
+ //check that min < max
+ bool bWarn = false;
+ int nSelectMin = mxLbTypeMin->get_active();
+ if( nSelectMin == COLORSCALE_MAX )
+ bWarn = true;
+ int nSelectMax = mxLbTypeMax->get_active();
+ if( nSelectMax == COLORSCALE_MIN )
+ bWarn = true;
+ if(!bWarn) // databar length checks
+ {
+ OUString aMinString = mxLenMin->get_text();
+ OUString aMaxString = mxLenMax->get_text();
+ double nMinValue = 0;
+ sal_uInt32 nIndex = 0;
+ (void)mpNumberFormatter->IsNumberFormat(aMinString, nIndex, nMinValue);
+ nIndex = 0;
+ double nMaxValue = 0;
+ (void)mpNumberFormatter->IsNumberFormat(aMaxString, nIndex, nMaxValue);
+ if(rtl::math::approxEqual(nMinValue, nMaxValue) || nMinValue > nMaxValue || nMaxValue > 100 || nMinValue < 0)
+ bWarn = true;
+ }
+ if (!bWarn && mxLbTypeMin->get_active() == mxLbTypeMax->get_active())
+ {
+
+ if(nSelectMax != COLORSCALE_FORMULA && nSelectMax != COLORSCALE_AUTO)
+ {
+ OUString aMinString = mxEdMin->get_text();
+ OUString aMaxString = mxEdMax->get_text();
+ double nMinValue = 0;
+ sal_uInt32 nIndex = 0;
+ (void)mpNumberFormatter->IsNumberFormat(aMinString, nIndex, nMinValue);
+ nIndex = 0;
+ double nMaxValue = 0;
+ (void)mpNumberFormatter->IsNumberFormat(aMaxString, nIndex, nMaxValue);
+ if(rtl::math::approxEqual(nMinValue, nMaxValue) || nMinValue > nMaxValue)
+ bWarn = true;
+ }
+ }
+
+ if(bWarn)
+ {
+ //show warning message and don't close
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ maStrWarnSameValue));
+ xWarn->run();
+ }
+ else
+ {
+ m_xDialog->response(RET_OK);
+ }
+}
+
+IMPL_LINK_NOARG(ScDataBarSettingsDlg, TypeSelectHdl, weld::ComboBox&, void)
+{
+ int nSelectMin = mxLbTypeMin->get_active();
+ if( nSelectMin <= COLORSCALE_MAX)
+ mxEdMin->set_sensitive(false);
+ else
+ {
+ mxEdMin->set_sensitive(true);
+ if(mxEdMin->get_text().isEmpty())
+ {
+ if(nSelectMin == COLORSCALE_PERCENTILE || nSelectMin == COLORSCALE_PERCENT)
+ mxEdMin->set_text(OUString::number(50));
+ else
+ mxEdMin->set_text(OUString::number(0));
+ }
+ }
+
+ int nSelectMax = mxLbTypeMax->get_active();
+ if (nSelectMax <= COLORSCALE_MAX)
+ mxEdMax->set_sensitive(false);
+ else
+ {
+ mxEdMax->set_sensitive(true);
+ if (mxEdMax->get_text().isEmpty())
+ {
+ if(nSelectMax == COLORSCALE_PERCENTILE || nSelectMax == COLORSCALE_PERCENT)
+ mxEdMax->set_text(OUString::number(50));
+ else
+ mxEdMax->set_text(OUString::number(0));
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScDataBarSettingsDlg, PosSelectHdl, weld::ComboBox&, void)
+{
+ int axisPos = mxLbAxisPos->get_active();
+ if(axisPos != 2 && axisPos != 1) // disable if axis vertical position is automatic
+ {
+ mxLenMin->set_sensitive(false);
+ mxLenMax->set_sensitive(false);
+ }
+ else
+ {
+ mxLenMin->set_sensitive(true);
+ mxLenMax->set_sensitive(true);
+ if(mxLenMin->get_text().isEmpty())
+ {
+ mxLenMin->set_text(OUString::number(0));
+ mxLenMax->set_text(OUString::number(100));
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/ui/condformat/condformatdlg.cxx b/sc/source/ui/condformat/condformatdlg.cxx
new file mode 100644
index 0000000000..2a1b724596
--- /dev/null
+++ b/sc/source/ui/condformat/condformatdlg.cxx
@@ -0,0 +1,709 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <condformatdlg.hxx>
+
+#include <sfx2/dispatch.hxx>
+#include <vcl/svapp.hxx>
+
+#include <anyrefdg.hxx>
+#include <document.hxx>
+#include <conditio.hxx>
+#include <tabvwsh.hxx>
+#include <colorscale.hxx>
+#include <reffact.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <condformatdlgentry.hxx>
+#include <condformatdlgitem.hxx>
+
+ScCondFormatList::ScCondFormatList(ScCondFormatDlg* pDialogParent,
+ std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Container> xGrid)
+ : mxScrollWindow(std::move(xWindow))
+ , mxGrid(std::move(xGrid))
+ , mbFrozen(false)
+ , mbNewEntry(false)
+ , mpDoc(nullptr)
+ , mpDialogParent(pDialogParent)
+{
+ mxScrollWindow->set_size_request(mxScrollWindow->get_approximate_digit_width() * 85,
+ mxScrollWindow->get_text_height() * 23);
+ mxGrid->set_stack_background();
+}
+
+weld::Window* ScCondFormatList::GetFrameWeld()
+{
+ return mpDialogParent->getDialog();
+}
+
+ScCondFormatList::~ScCondFormatList()
+{
+ Freeze();
+}
+
+void ScCondFormatList::init(ScDocument& rDoc,
+ const ScConditionalFormat* pFormat, const ScRangeList& rRanges,
+ const ScAddress& rPos, condformat::dialog::ScCondFormatDialogType eType)
+{
+ mpDoc = &rDoc;
+ maPos = rPos;
+ maRanges = rRanges;
+
+ Freeze();
+
+ if(pFormat)
+ {
+ size_t nCount = pFormat->size();
+ for (size_t nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ const ScFormatEntry* pEntry = pFormat->GetEntry(nIndex);
+ switch(pEntry->GetType())
+ {
+ case ScFormatEntry::Type::Condition:
+ case ScFormatEntry::Type::ExtCondition:
+ {
+ const ScCondFormatEntry* pConditionEntry = static_cast<const ScCondFormatEntry*>( pEntry );
+ if(pConditionEntry->GetOperation() != ScConditionMode::Direct)
+ maEntries.emplace_back(new ScConditionFrmtEntry( this, mpDoc, mpDialogParent, maPos, pConditionEntry ) );
+ else
+ maEntries.emplace_back(new ScFormulaFrmtEntry( this, mpDoc, mpDialogParent, maPos, pConditionEntry ) );
+
+ }
+ break;
+ case ScFormatEntry::Type::Colorscale:
+ {
+ const ScColorScaleFormat* pColorScale = static_cast<const ScColorScaleFormat*>( pEntry );
+ if( pColorScale->size() == 2 )
+ maEntries.emplace_back(new ScColorScale2FrmtEntry( this, mpDoc, maPos, pColorScale ) );
+ else
+ maEntries.emplace_back(new ScColorScale3FrmtEntry( this, mpDoc, maPos, pColorScale ) );
+ }
+ break;
+ case ScFormatEntry::Type::Databar:
+ maEntries.emplace_back(new ScDataBarFrmtEntry( this, mpDoc, maPos, static_cast<const ScDataBarFormat*>( pEntry ) ) );
+ break;
+ case ScFormatEntry::Type::Iconset:
+ maEntries.emplace_back(new ScIconSetFrmtEntry( this, mpDoc, maPos, static_cast<const ScIconSetFormat*>( pEntry ) ) );
+ break;
+ case ScFormatEntry::Type::Date:
+ maEntries.emplace_back(new ScDateFrmtEntry( this, mpDoc, static_cast<const ScCondDateFormatEntry*>( pEntry ) ) );
+ break;
+ }
+ }
+ if(nCount)
+ EntrySelectHdl(*maEntries[0]);
+ }
+ else
+ {
+ switch(eType)
+ {
+ case condformat::dialog::CONDITION:
+ maEntries.emplace_back(new ScConditionFrmtEntry( this, mpDoc, mpDialogParent, maPos ));
+ break;
+ case condformat::dialog::COLORSCALE:
+ maEntries.emplace_back(new ScColorScale3FrmtEntry( this, mpDoc, maPos ));
+ break;
+ case condformat::dialog::DATABAR:
+ maEntries.emplace_back(new ScDataBarFrmtEntry( this, mpDoc, maPos ));
+ break;
+ case condformat::dialog::ICONSET:
+ maEntries.emplace_back(new ScIconSetFrmtEntry( this, mpDoc, maPos ));
+ break;
+ case condformat::dialog::DATE:
+ maEntries.emplace_back(new ScDateFrmtEntry( this, mpDoc ));
+ break;
+ case condformat::dialog::NONE:
+ break;
+ }
+ mbNewEntry = true;
+ }
+ Thaw();
+ RecalcAll();
+ if (!maEntries.empty())
+ {
+ (*maEntries.begin())->SetActive();
+ mpDialogParent->OnSelectionChange(0, maEntries.size());
+ }
+
+ RecalcAll();
+}
+
+void ScCondFormatList::SetRange(const ScRangeList& rRange)
+{
+ maRanges = rRange;
+}
+
+std::unique_ptr<ScConditionalFormat> ScCondFormatList::GetConditionalFormat() const
+{
+ if(maEntries.empty())
+ return nullptr;
+
+ std::unique_ptr<ScConditionalFormat> pFormat(new ScConditionalFormat(0, mpDoc));
+ pFormat->SetRange(maRanges);
+
+ for(auto & rEntry: maEntries)
+ {
+ // tdf#119178: Sometimes initial apply-to range (the one this dialog
+ // was opened with) is different from the final apply-to range
+ // (as edited by the user)
+
+ // If this format entry is new, take top-left corner of the final range
+ // and use it to create the initial entry (token array therein, if applicable)
+ if (mbNewEntry)
+ rEntry->SetPos(maRanges.GetTopLeftCorner());
+ // else do nothing: setting new position when editing recompiles formulas
+ // in entries and nobody wants that
+
+ ScFormatEntry* pEntry = rEntry->GetEntry();
+ if(pEntry)
+ pFormat->AddEntry(pEntry);
+ }
+
+ return pFormat;
+}
+
+void ScCondFormatList::RecalcAll()
+{
+ if (mbFrozen)
+ return;
+
+ int nWheelScroll = SAL_MAX_INT32;
+
+ sal_Int32 nIndex = 1;
+ for (const auto& item : maEntries)
+ {
+ if (!item)
+ continue;
+ item->SetIndex(nIndex);
+ item->set_grid_top_attach(nIndex - 1);
+ nWheelScroll = std::min(nWheelScroll, item->get_preferred_height());
+ ++nIndex;
+ }
+
+ if (nWheelScroll != SAL_MAX_INT32)
+ {
+ // tdf#118482 set a scroll step of the height of a collapsed entry
+ mxScrollWindow->vadjustment_set_step_increment(nWheelScroll);
+ }
+}
+
+IMPL_LINK(ScCondFormatList, ColFormatTypeHdl, weld::ComboBox&, rBox, void)
+{
+ Application::PostUserEvent(LINK(this, ScCondFormatList, AfterColFormatTypeHdl), &rBox);
+}
+
+IMPL_LINK(ScCondFormatList, AfterColFormatTypeHdl, void*, p, void)
+{
+ weld::ComboBox* pBox = static_cast<weld::ComboBox*>(p);
+ EntryContainer::iterator itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [](const std::unique_ptr<ScCondFrmtEntry>& widget) { return widget->IsSelected(); });
+ if(itr == maEntries.end())
+ return;
+
+ sal_Int32 nPos = pBox->get_active();
+ switch(nPos)
+ {
+ case 0:
+ if((*itr)->GetType() == condformat::entry::COLORSCALE2)
+ return;
+
+ Freeze();
+ itr->reset(new ScColorScale2FrmtEntry(this, mpDoc, maPos));
+ break;
+ case 1:
+ if((*itr)->GetType() == condformat::entry::COLORSCALE3)
+ return;
+
+ Freeze();
+ itr->reset(new ScColorScale3FrmtEntry(this, mpDoc, maPos));
+ break;
+ case 2:
+ if((*itr)->GetType() == condformat::entry::DATABAR)
+ return;
+
+ Freeze();
+ itr->reset(new ScDataBarFrmtEntry(this, mpDoc, maPos));
+ break;
+ case 3:
+ if((*itr)->GetType() == condformat::entry::ICONSET)
+ return;
+
+ Freeze();
+ itr->reset(new ScIconSetFrmtEntry(this, mpDoc, maPos));
+ break;
+ default:
+ break;
+ }
+ mpDialogParent->InvalidateRefData();
+ (*itr)->SetActive();
+ Thaw();
+ RecalcAll();
+}
+
+IMPL_LINK(ScCondFormatList, TypeListHdl, weld::ComboBox&, rBox, void)
+{
+ //Resolves: fdo#79021 At this point we are still inside the ListBox Select.
+ //If we call maEntries.replace here then the pBox will be deleted before it
+ //has finished Select and will crash on accessing its deleted this. So Post
+ //to do the real work after the Select has completed
+ Application::PostUserEvent(LINK(this, ScCondFormatList, AfterTypeListHdl), &rBox);
+}
+
+IMPL_LINK(ScCondFormatList, AfterTypeListHdl, void*, p, void)
+{
+ weld::ComboBox* pBox = static_cast<weld::ComboBox*>(p);
+ EntryContainer::iterator itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [](const std::unique_ptr<ScCondFrmtEntry>& widget) { return widget->IsSelected(); });
+ if(itr == maEntries.end())
+ return;
+
+ sal_Int32 nPos = pBox->get_active();
+ switch(nPos)
+ {
+ case 0:
+ switch((*itr)->GetType())
+ {
+ case condformat::entry::FORMULA:
+ case condformat::entry::CONDITION:
+ case condformat::entry::DATE:
+ break;
+ case condformat::entry::COLORSCALE2:
+ case condformat::entry::COLORSCALE3:
+ case condformat::entry::DATABAR:
+ case condformat::entry::ICONSET:
+ return;
+ }
+ Freeze();
+ itr->reset(new ScColorScale3FrmtEntry(this, mpDoc, maPos));
+ mpDialogParent->InvalidateRefData();
+ (*itr)->SetActive();
+ break;
+ case 1:
+ if((*itr)->GetType() == condformat::entry::CONDITION)
+ return;
+
+ Freeze();
+ itr->reset(new ScConditionFrmtEntry(this, mpDoc, mpDialogParent, maPos));
+ mpDialogParent->InvalidateRefData();
+ (*itr)->SetActive();
+ break;
+ case 2:
+ if((*itr)->GetType() == condformat::entry::FORMULA)
+ return;
+
+ Freeze();
+ itr->reset(new ScFormulaFrmtEntry(this, mpDoc, mpDialogParent, maPos));
+ mpDialogParent->InvalidateRefData();
+ (*itr)->SetActive();
+ break;
+ case 3:
+ if((*itr)->GetType() == condformat::entry::DATE)
+ return;
+
+ Freeze();
+ itr->reset(new ScDateFrmtEntry( this, mpDoc ));
+ mpDialogParent->InvalidateRefData();
+ (*itr)->SetActive();
+ break;
+
+ }
+ Thaw();
+ RecalcAll();
+}
+
+IMPL_LINK_NOARG( ScCondFormatList, AddBtnHdl, weld::Button&, void )
+{
+ Freeze();
+ maEntries.emplace_back(new ScConditionFrmtEntry(this, mpDoc, mpDialogParent, maPos));
+ for(auto& rxEntry : maEntries)
+ {
+ rxEntry->SetInactive();
+ }
+ mpDialogParent->InvalidateRefData();
+ maEntries.back()->SetActive();
+ mpDialogParent->OnSelectionChange(maEntries.size() - 1, maEntries.size());
+ Thaw();
+ RecalcAll();
+}
+
+IMPL_LINK_NOARG( ScCondFormatList, RemoveBtnHdl, weld::Button&, void )
+{
+ Freeze();
+ auto itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [](const std::unique_ptr<ScCondFrmtEntry>& widget) { return widget->IsSelected(); });
+ if (itr != maEntries.end())
+ {
+ maEntries.erase(itr);
+ }
+ mpDialogParent->InvalidateRefData();
+ mpDialogParent->OnSelectionChange(0, maEntries.size(), false);
+ Thaw();
+ RecalcAll();
+}
+
+IMPL_LINK_NOARG(ScCondFormatList, UpBtnHdl, weld::Button&, void)
+{
+ Freeze();
+ size_t index = 0;
+ for (size_t i = 0; i < maEntries.size(); i++)
+ {
+ auto& widget = maEntries[i];
+ if (widget->IsSelected() && i > 0)
+ {
+ std::swap(maEntries[i], maEntries[i - 1]);
+ index = i - 1;
+ break;
+ }
+ }
+ mpDialogParent->InvalidateRefData();
+ mpDialogParent->OnSelectionChange(index, maEntries.size());
+ Thaw();
+ RecalcAll();
+}
+
+IMPL_LINK_NOARG(ScCondFormatList, DownBtnHdl, weld::Button&, void)
+{
+ Freeze();
+ size_t index = 0;
+ for (size_t i = 0; i < maEntries.size(); i++)
+ {
+ auto& widget = maEntries[i];
+ if (widget->IsSelected())
+ {
+ index = i;
+ if (i < maEntries.size()-1)
+ {
+ std::swap(maEntries[i], maEntries[i + 1]);
+ index = i + 1;
+ break;
+ }
+ }
+ }
+ mpDialogParent->InvalidateRefData();
+ mpDialogParent->OnSelectionChange(index, maEntries.size());
+ Thaw();
+ RecalcAll();
+}
+
+IMPL_LINK( ScCondFormatList, EntrySelectHdl, ScCondFrmtEntry&, rEntry, void )
+{
+ if(rEntry.IsSelected())
+ return;
+
+ Freeze();
+ size_t index = 0;
+ for(size_t i = 0; i < maEntries.size(); i++)
+ {
+ if (maEntries[i].get() == &rEntry)
+ {
+ index = i;
+ }
+ maEntries[i]->SetInactive();
+ }
+ mpDialogParent->InvalidateRefData();
+ mpDialogParent->OnSelectionChange(index, maEntries.size());
+ rEntry.SetActive();
+ Thaw();
+ RecalcAll();
+}
+
+ScCondFormatDlg::ScCondFormatDlg(SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData* pViewData,
+ const ScCondFormatDlgItem* pItem)
+ : ScAnyRefDlgController(pB, pCW, pParent,
+ (SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone())?OUString("modules/scalc/ui/conditionalformatdialogmobile.ui"):OUString("modules/scalc/ui/conditionalformatdialog.ui"),
+ "ConditionalFormatDialog")
+ , mpViewData(pViewData)
+ , mpDlgItem(pItem->Clone())
+ , mpLastEdit(nullptr)
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxBtnAdd(m_xBuilder->weld_button("add"))
+ , mxBtnRemove(m_xBuilder->weld_button("delete"))
+ , mxBtnUp(m_xBuilder->weld_button("up"))
+ , mxBtnDown(m_xBuilder->weld_button("down"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mxFtRange(m_xBuilder->weld_label("ftassign"))
+ , mxEdRange(new formula::RefEdit(m_xBuilder->weld_entry("edassign")))
+ , mxRbRange(new formula::RefButton(m_xBuilder->weld_button("rbassign")))
+ , mxCondFormList(new ScCondFormatList(this, m_xBuilder->weld_scrolled_window("listwindow"),
+ m_xBuilder->weld_container("list")))
+{
+ mxEdRange->SetReferences(this, mxFtRange.get());
+ mxRbRange->SetReferences(this, mxEdRange.get());
+
+ ScConditionalFormat* pFormat = nullptr;
+ mnKey = mpDlgItem->GetIndex();
+ if (mpDlgItem->IsManaged() && mpDlgItem->GetConditionalFormatList())
+ {
+ pFormat = mpDlgItem->GetConditionalFormatList()->GetFormat(mnKey);
+ }
+ else if (!mpDlgItem->IsManaged())
+ {
+ ScDocument& rDoc = mpViewData->GetDocument();
+ pFormat = rDoc.GetCondFormList(mpViewData->GetTabNo())->GetFormat ( mnKey );
+ }
+
+ ScRangeList aRange;
+ if (pFormat)
+ {
+ aRange = pFormat->GetRange();
+ }
+ else
+ {
+ // this is for adding a new entry
+ mpViewData->GetMarkData().FillRangeListWithMarks(&aRange, false);
+ if(aRange.empty())
+ {
+ ScAddress aPos(mpViewData->GetCurX(), mpViewData->GetCurY(), mpViewData->GetTabNo());
+ aRange.push_back(ScRange(aPos));
+ }
+ mnKey = 0;
+ }
+ maPos = aRange.GetTopLeftCorner();
+
+ mxCondFormList->init(mpViewData->GetDocument(), pFormat, aRange, maPos, mpDlgItem->GetDialogType());
+
+ mxBtnOk->connect_clicked(LINK(this, ScCondFormatDlg, BtnPressedHdl ) );
+ mxBtnAdd->connect_clicked( LINK( mxCondFormList.get(), ScCondFormatList, AddBtnHdl ) );
+ mxBtnRemove->connect_clicked( LINK( mxCondFormList.get(), ScCondFormatList, RemoveBtnHdl ) );
+ mxBtnUp->connect_clicked(LINK(mxCondFormList.get(), ScCondFormatList, UpBtnHdl));
+ mxBtnDown->connect_clicked(LINK(mxCondFormList.get(), ScCondFormatList, DownBtnHdl));
+ mxBtnCancel->connect_clicked( LINK(this, ScCondFormatDlg, BtnPressedHdl ) );
+ mxEdRange->SetModifyHdl( LINK( this, ScCondFormatDlg, EdRangeModifyHdl ) );
+ mxEdRange->SetGetFocusHdl( LINK( this, ScCondFormatDlg, RangeGetFocusHdl ) );
+
+ OUString aRangeString;
+ const ScDocument& rDoc = pViewData->GetDocument();
+ aRange.Format(aRangeString, ScRefFlags::VALID, rDoc, rDoc.GetAddressConvention());
+ mxEdRange->SetText(aRangeString);
+
+ msBaseTitle = m_xDialog->get_title();
+ updateTitle();
+}
+
+void ScCondFormatDlg::updateTitle()
+{
+ OUString aTitle = msBaseTitle + " " + mxEdRange->GetText();
+
+ m_xDialog->set_title(aTitle);
+}
+
+ScCondFormatDlg::~ScCondFormatDlg()
+{
+}
+
+void ScCondFormatDlg::SetActive()
+{
+ if(mpLastEdit)
+ mpLastEdit->GrabFocus();
+ else
+ mxEdRange->GrabFocus();
+
+ RefInputDone();
+}
+
+void ScCondFormatDlg::RefInputDone( bool bForced )
+{
+ ScAnyRefDlgController::RefInputDone(bForced);
+
+ // ScAnyRefModalDlg::RefInputDone resets the title back
+ // to its original state.
+ // I.e. if we open the dialog normally, and then click into the sheet
+ // to modify the selection, the title is updated such that the range
+ // is only a single cell (e.g. $A$1), after which the dialog switches
+ // into the RefInput mode. During the RefInput mode the title is updated
+ // as expected, however at the end RefInputDone overwrites the title
+ // with the initial (now incorrect) single cell range. Hence we correct
+ // it here.
+ updateTitle();
+}
+
+bool ScCondFormatDlg::IsTableLocked() const
+{
+ return !mpLastEdit || mpLastEdit == mxEdRange.get();
+}
+
+bool ScCondFormatDlg::IsRefInputMode() const
+{
+ return mxEdRange->GetWidget()->get_sensitive();
+}
+
+void ScCondFormatDlg::SetReference(const ScRange& rRef, ScDocument&)
+{
+ formula::RefEdit* pEdit = mpLastEdit;
+ if (!mpLastEdit)
+ pEdit = mxEdRange.get();
+
+ if (!pEdit->GetWidget()->get_sensitive())
+ return;
+
+ if(rRef.aStart != rRef.aEnd)
+ RefInputStart(pEdit);
+
+ ScRefFlags nFlags;
+ if (mpLastEdit && mpLastEdit != mxEdRange.get())
+ nFlags = ScRefFlags::RANGE_ABS_3D;
+ else
+ nFlags = ScRefFlags::RANGE_ABS;
+
+ const ScDocument& rDoc = mpViewData->GetDocument();
+ OUString aRefStr(rRef.Format(rDoc, nFlags,
+ ScAddress::Details(rDoc.GetAddressConvention(), 0, 0)));
+ if (pEdit != mxEdRange.get())
+ {
+ Selection sel = pEdit->GetSelection();
+ sel.Normalize(); // in case of RTL selection
+ sel.Max() = sel.Min() + aRefStr.getLength();
+ pEdit->GetWidget()->replace_selection(aRefStr);
+ pEdit->SetSelection(sel); // to replace it again with next drag event
+ }
+ else
+ pEdit->SetRefString( aRefStr );
+ updateTitle();
+}
+
+std::unique_ptr<ScConditionalFormat> ScCondFormatDlg::GetConditionalFormat() const
+{
+ OUString aRangeStr = mxEdRange->GetText();
+ if(aRangeStr.isEmpty())
+ return nullptr;
+
+ ScRangeList aRange;
+ ScRefFlags nFlags = aRange.Parse(aRangeStr, mpViewData->GetDocument(),
+ mpViewData->GetDocument().GetAddressConvention(), maPos.Tab());
+ mxCondFormList->SetRange(aRange);
+ std::unique_ptr<ScConditionalFormat> pFormat = mxCondFormList->GetConditionalFormat();
+
+ if((nFlags & ScRefFlags::VALID) && !aRange.empty() && pFormat)
+ pFormat->SetRange(aRange);
+ else
+ pFormat.reset();
+
+ return pFormat;
+}
+
+void ScCondFormatDlg::InvalidateRefData()
+{
+ mpLastEdit = nullptr;
+}
+
+// Close the Conditional Format Dialog
+//
+void ScCondFormatDlg::Close()
+{
+ DoClose( ScCondFormatDlgWrapper::GetChildWindowId() );
+}
+
+// Occurs when the Conditional Format Dialog the OK button is pressed.
+//
+void ScCondFormatDlg::OkPressed()
+{
+ std::unique_ptr<ScConditionalFormat> pFormat = GetConditionalFormat();
+
+ if (!mpDlgItem->IsManaged())
+ {
+ if(pFormat)
+ {
+ auto& rRangeList = pFormat->GetRange();
+ mpViewData->GetDocShell()->GetDocFunc().ReplaceConditionalFormat(mnKey,
+ std::move(pFormat), maPos.Tab(), rRangeList);
+ }
+ else
+ mpViewData->GetDocShell()->GetDocFunc().ReplaceConditionalFormat(mnKey,
+ nullptr, maPos.Tab(), ScRangeList());
+ }
+ else
+ {
+ ScConditionalFormatList* pList = mpDlgItem->GetConditionalFormatList();
+ sal_uInt32 nKey = mnKey;
+ if (mnKey == 0)
+ {
+ nKey = pList->getMaxKey() + 1;
+ }
+
+ pList->erase(nKey);
+ if (pFormat)
+ {
+ pFormat->SetKey(nKey);
+ pList->InsertNew(std::move(pFormat));
+ }
+ mpViewData->GetViewShell()->GetPool().DirectPutItemInPool(*mpDlgItem);
+
+ SetDispatcherLock( false );
+ // Queue message to open Conditional Format Manager Dialog
+ GetBindings().GetDispatcher()->Execute( SID_OPENDLG_CONDFRMT_MANAGER,
+ SfxCallMode::ASYNCHRON );
+ }
+ m_xDialog->response(RET_OK);
+}
+
+// Occurs when the Conditional Format Dialog is cancelled.
+//
+void ScCondFormatDlg::CancelPressed()
+{
+ if ( mpDlgItem->IsManaged() )
+ {
+ mpViewData->GetViewShell()->GetPool().DirectPutItemInPool(*mpDlgItem);
+ SetDispatcherLock( false );
+ // Queue message to open Conditional Format Manager Dialog
+ GetBindings().GetDispatcher()->Execute( SID_OPENDLG_CONDFRMT_MANAGER,
+ SfxCallMode::ASYNCHRON );
+ }
+ m_xDialog->response(RET_CANCEL);
+}
+
+void ScCondFormatDlg::OnSelectionChange(size_t nIndex, size_t nSize, bool bSelected)
+{
+ if (nSize <= 1 || !bSelected)
+ {
+ mxBtnUp->set_sensitive(false);
+ mxBtnDown->set_sensitive(false);
+ }
+ else
+ {
+ mxBtnUp->set_sensitive(nIndex != 0);
+ mxBtnDown->set_sensitive(nIndex < nSize - 1);
+ }
+}
+
+IMPL_LINK(ScCondFormatDlg, EdRangeModifyHdl, formula::RefEdit&, rEdit, void)
+{
+ OUString aRangeStr = rEdit.GetText();
+ ScRangeList aRange;
+ ScRefFlags nFlags = aRange.Parse(aRangeStr, mpViewData->GetDocument(),
+ mpViewData->GetDocument().GetAddressConvention());
+ if(nFlags & ScRefFlags::VALID)
+ {
+ rEdit.GetWidget()->set_message_type(weld::EntryMessageType::Normal);
+ mxBtnOk->set_sensitive(true);
+ }
+ else
+ {
+ rEdit.GetWidget()->set_message_type(weld::EntryMessageType::Error);
+ mxBtnOk->set_sensitive(false);
+ }
+
+ updateTitle();
+}
+
+IMPL_LINK(ScCondFormatDlg, RangeGetFocusHdl, formula::RefEdit&, rControl, void)
+{
+ mpLastEdit = &rControl;
+}
+
+IMPL_LINK( ScCondFormatDlg, BtnPressedHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnOk.get())
+ OkPressed();
+ else if (&rBtn == mxBtnCancel.get())
+ CancelPressed();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/condformat/condformatdlgentry.cxx b/sc/source/ui/condformat/condformatdlgentry.cxx
new file mode 100644
index 0000000000..ade0cede7c
--- /dev/null
+++ b/sc/source/ui/condformat/condformatdlgentry.cxx
@@ -0,0 +1,1538 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <condformatdlg.hxx>
+#include <condformatdlgentry.hxx>
+#include <conditio.hxx>
+#include <compiler.hxx>
+#include <colorscale.hxx>
+#include <condformathelper.hxx>
+
+#include <document.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <svl/style.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/frame.hxx>
+#include <svl/stritem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svx/colorbox.hxx>
+#include <vcl/svapp.hxx>
+#include <formula/token.hxx>
+#include <formula/errorcodes.hxx>
+#include <tokenarray.hxx>
+#include <stlpool.hxx>
+#include <tabvwsh.hxx>
+#include <unotools/charclass.hxx>
+
+#include <colorformat.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <set>
+
+// set the widget width to something to override their auto-width calc and
+// force them to take a 1/3 of the available space
+#define CommonWidgetWidth 10
+
+ScCondFrmtEntry::ScCondFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos)
+ : mpParent(pParent)
+ , mxBuilder(Application::CreateBuilder(pParent->GetContainer(), (SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone())?OUString("modules/scalc/ui/conditionalentrymobile.ui"):OUString("modules/scalc/ui/conditionalentry.ui")))
+ , mxBorder(mxBuilder->weld_widget("border"))
+ , mxGrid(mxBuilder->weld_container("grid"))
+ , mxFtCondNr(mxBuilder->weld_label("number"))
+ , mxFtCondition(mxBuilder->weld_label("condition"))
+ , mbActive(false)
+ , maStrCondition(ScResId(SCSTR_CONDITION))
+ , mxLbType(mxBuilder->weld_combo_box("type"))
+ , mpDoc(pDoc)
+ , maPos(rPos)
+{
+ mxLbType->set_size_request(CommonWidgetWidth, -1);
+ mxLbType->connect_changed(LINK(pParent, ScCondFormatList, TypeListHdl));
+ mxGrid->connect_mouse_press(LINK(this, ScCondFrmtEntry, EntrySelectHdl));
+ maClickHdl = LINK( pParent, ScCondFormatList, EntrySelectHdl );
+
+ Show();
+}
+
+ScCondFrmtEntry::~ScCondFrmtEntry()
+{
+ mpParent->GetContainer()->move(mxBorder.get(), nullptr);
+}
+
+IMPL_LINK_NOARG(ScCondFrmtEntry, EntrySelectHdl, const MouseEvent&, bool)
+{
+ maClickHdl.Call(*this);
+ return false;
+}
+
+void ScCondFrmtEntry::SetIndex(sal_Int32 nIndex)
+{
+ OUString sLabel = maStrCondition + OUString::number(nIndex);
+ mxFtCondNr->set_label(sLabel);
+
+ // tdf#124412: uitest
+ mxFtCondition->set_buildable_name(sLabel);
+}
+
+void ScCondFrmtEntry::Select()
+{
+ mxFtCondition->set_label(OUString());
+ mxFtCondition->hide();
+ mxLbType->show();
+ mbActive = true;
+}
+
+void ScCondFrmtEntry::Deselect()
+{
+ OUString aCondText = GetExpressionString();
+ mxFtCondition->set_label(aCondText);
+ mxFtCondition->show();
+ mxLbType->hide();
+ mbActive = false;
+}
+
+//condition
+
+namespace {
+
+void FillStyleListBox( const ScDocument* pDoc, weld::ComboBox& rLbStyle )
+{
+ std::set<OUString> aStyleNames;
+ SfxStyleSheetIterator aStyleIter( pDoc->GetStyleSheetPool(), SfxStyleFamily::Para );
+ for ( SfxStyleSheetBase* pStyle = aStyleIter.First(); pStyle; pStyle = aStyleIter.Next() )
+ {
+ aStyleNames.insert(pStyle->GetName());
+ }
+ for(const auto& rStyleName : aStyleNames)
+ {
+ rLbStyle.append_text(rStyleName);
+ }
+}
+
+}
+
+const ScConditionMode ScConditionFrmtEntry::mpEntryToCond[ScConditionFrmtEntry::NUM_COND_ENTRIES] = {
+ ScConditionMode::Equal,
+ ScConditionMode::Less,
+ ScConditionMode::Greater,
+ ScConditionMode::EqLess,
+ ScConditionMode::EqGreater,
+ ScConditionMode::NotEqual,
+ ScConditionMode::Between,
+ ScConditionMode::NotBetween,
+ ScConditionMode::Duplicate,
+ ScConditionMode::NotDuplicate,
+ ScConditionMode::Top10,
+ ScConditionMode::Bottom10,
+ ScConditionMode::TopPercent,
+ ScConditionMode::BottomPercent,
+ ScConditionMode::AboveAverage,
+ ScConditionMode::BelowAverage,
+ ScConditionMode::AboveEqualAverage,
+ ScConditionMode::BelowEqualAverage,
+ ScConditionMode::Error,
+ ScConditionMode::NoError,
+ ScConditionMode::BeginsWith,
+ ScConditionMode::EndsWith,
+ ScConditionMode::ContainsText,
+ ScConditionMode::NotContainsText
+};
+
+ScConditionFrmtEntry::ScConditionFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, ScCondFormatDlg* pDialogParent,
+ const ScAddress& rPos, const ScCondFormatEntry* pFormatEntry)
+ : ScCondFrmtEntry(pParent, pDoc, rPos)
+ , mxLbCondType(mxBuilder->weld_combo_box("typeis"))
+ , mxEdVal1(new formula::RefEdit(mxBuilder->weld_entry("val1")))
+ , mxEdVal2(new formula::RefEdit(mxBuilder->weld_entry("val2")))
+ , mxFtVal(mxBuilder->weld_label("valueft"))
+ , mxFtStyle(mxBuilder->weld_label("styleft"))
+ , mxLbStyle(mxBuilder->weld_combo_box("style"))
+ , mxWdPreviewWin(mxBuilder->weld_widget("previewwin"))
+ , mxWdPreview(new weld::CustomWeld(*mxBuilder, "preview", maWdPreview))
+ , mbIsInStyleCreate(false)
+{
+ mxLbCondType->set_size_request(CommonWidgetWidth, -1);
+ mxLbType->set_size_request(CommonWidgetWidth, -1);
+ mxWdPreview->set_size_request(-1, mxLbStyle->get_preferred_size().Height());
+
+ mxLbType->set_active(1);
+
+ Init(pDialogParent);
+
+ StartListening(*pDoc->GetStyleSheetPool(), DuplicateHandling::Prevent);
+
+ if(pFormatEntry)
+ {
+ mxLbStyle->set_active_text(pFormatEntry->GetStyle());
+ StyleSelectHdl(*mxLbStyle);
+ ScConditionMode eMode = pFormatEntry->GetOperation();
+
+ mxLbCondType->set_active(ConditionModeToEntryPos(eMode));
+
+ switch(GetNumberEditFields(eMode))
+ {
+ case 0:
+ mxEdVal1->GetWidget()->hide();
+ mxEdVal2->GetWidget()->hide();
+ break;
+ case 1:
+ mxEdVal1->GetWidget()->show();
+ mxEdVal1->SetText(pFormatEntry->GetExpression(maPos, 0));
+ mxEdVal2->GetWidget()->hide();
+ OnEdChanged(*mxEdVal1);
+ break;
+ case 2:
+ mxEdVal1->GetWidget()->show();
+ mxEdVal1->SetText(pFormatEntry->GetExpression(maPos, 0));
+ OnEdChanged(*mxEdVal1);
+ mxEdVal2->GetWidget()->show();
+ mxEdVal2->SetText(pFormatEntry->GetExpression(maPos, 1));
+ OnEdChanged(*mxEdVal2);
+ break;
+ }
+ }
+ else
+ {
+ mxLbCondType->set_active(0);
+ mxEdVal2->GetWidget()->hide();
+ mxLbStyle->set_active(1);
+ }
+}
+
+ScConditionFrmtEntry::~ScConditionFrmtEntry()
+{
+}
+
+void ScConditionFrmtEntry::Init(ScCondFormatDlg* pDialogParent)
+{
+ mxEdVal1->SetGetFocusHdl( LINK( pDialogParent, ScCondFormatDlg, RangeGetFocusHdl ) );
+ mxEdVal2->SetGetFocusHdl( LINK( pDialogParent, ScCondFormatDlg, RangeGetFocusHdl ) );
+
+ mxEdVal1->SetModifyHdl( LINK( this, ScConditionFrmtEntry, OnEdChanged ) );
+ mxEdVal2->SetModifyHdl( LINK( this, ScConditionFrmtEntry, OnEdChanged ) );
+
+ FillStyleListBox( mpDoc, *mxLbStyle );
+ mxLbStyle->connect_changed( LINK( this, ScConditionFrmtEntry, StyleSelectHdl ) );
+
+ mxLbCondType->connect_changed( LINK( this, ScConditionFrmtEntry, ConditionTypeSelectHdl ) );
+}
+
+ScFormatEntry* ScConditionFrmtEntry::createConditionEntry() const
+{
+ ScConditionMode eMode = EntryPosToConditionMode(mxLbCondType->get_active());
+ OUString aExpr1 = mxEdVal1->GetText();
+ OUString aExpr2;
+ if (GetNumberEditFields(eMode) == 2)
+ {
+ aExpr2 = mxEdVal2->GetText();
+ if (aExpr2.isEmpty())
+ {
+ return nullptr;
+ }
+ }
+
+ ScFormatEntry* pEntry = new ScCondFormatEntry(eMode, aExpr1, aExpr2, *mpDoc, maPos, mxLbStyle->get_active_text());
+ return pEntry;
+}
+
+IMPL_LINK(ScConditionFrmtEntry, OnEdChanged, formula::RefEdit&, rRefEdit, void)
+{
+ weld::Entry& rEdit = *rRefEdit.GetWidget();
+ OUString aFormula = rEdit.get_text();
+
+ if( aFormula.isEmpty() )
+ {
+ mxFtVal->set_label(ScResId(STR_ENTER_VALUE));
+ return;
+ }
+
+ ScCompiler aComp( *mpDoc, maPos, mpDoc->GetGrammar() );
+ aComp.SetExtendedErrorDetection( ScCompiler::ExtendedErrorDetection::EXTENDED_ERROR_DETECTION_NAME_BREAK);
+ std::unique_ptr<ScTokenArray> ta(aComp.CompileString(aFormula));
+
+ // Error, warn the user if it is not an unknown name.
+ if (ta->GetCodeError() != FormulaError::NoName && (ta->GetCodeError() != FormulaError::NONE || ta->GetLen() == 0))
+ {
+ rEdit.set_message_type(weld::EntryMessageType::Error);
+ mxFtVal->set_label(ScResId(STR_VALID_DEFERROR));
+ return;
+ }
+
+ // Unrecognized name, warn the user; i.e. happens when starting to type and
+ // will go away once a valid name is completed.
+ if (ta->GetCodeError() == FormulaError::NoName)
+ {
+ rEdit.set_message_type(weld::EntryMessageType::Warning);
+ mxFtVal->set_label(ScResId(STR_UNQUOTED_STRING));
+ return;
+ }
+
+ // Generate RPN to detect further errors.
+ if (ta->GetLen() > 0)
+ aComp.CompileTokenArray();
+ // Error, warn the user.
+ if (ta->GetCodeError() != FormulaError::NONE || (ta->GetCodeLen() == 0))
+ {
+ rEdit.set_message_type(weld::EntryMessageType::Error);
+ mxFtVal->set_label(ScResId(STR_VALID_DEFERROR));
+ return;
+ }
+
+ rEdit.set_message_type(weld::EntryMessageType::Normal);
+ mxFtVal->set_label("");
+}
+
+void ScConditionFrmtEntry::Select()
+{
+ mxFtVal->show();
+ ScCondFrmtEntry::Select();
+}
+
+void ScConditionFrmtEntry::Deselect()
+{
+ mxFtVal->hide();
+ ScCondFrmtEntry::Deselect();
+}
+
+sal_Int32 ScConditionFrmtEntry::ConditionModeToEntryPos( ScConditionMode eMode )
+{
+ for ( sal_Int32 i = 0; i < NUM_COND_ENTRIES; ++i )
+ {
+ if (mpEntryToCond[i] == eMode)
+ {
+ return i;
+ }
+ }
+ assert(false); // should never get here
+ return 0;
+}
+
+ScConditionMode ScConditionFrmtEntry::EntryPosToConditionMode( sal_Int32 aEntryPos )
+{
+ assert( 0 <= aEntryPos && aEntryPos < NUM_COND_ENTRIES );
+ return mpEntryToCond[aEntryPos];
+}
+
+sal_Int32 ScConditionFrmtEntry::GetNumberEditFields( ScConditionMode eMode )
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ case ScConditionMode::Less:
+ case ScConditionMode::Greater:
+ case ScConditionMode::EqLess:
+ case ScConditionMode::EqGreater:
+ case ScConditionMode::NotEqual:
+ case ScConditionMode::Top10:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::BottomPercent:
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ case ScConditionMode::Error:
+ case ScConditionMode::NoError:
+ return 1;
+ case ScConditionMode::AboveAverage:
+ case ScConditionMode::BelowAverage:
+ case ScConditionMode::AboveEqualAverage:
+ case ScConditionMode::BelowEqualAverage:
+ case ScConditionMode::Duplicate:
+ case ScConditionMode::NotDuplicate:
+ return 0;
+ case ScConditionMode::Between:
+ case ScConditionMode::NotBetween:
+ return 2;
+ default:
+ assert(false); // should never get here
+ return 0;
+ }
+}
+
+OUString ScConditionFrmtEntry::GetExpressionString()
+{
+ return ScCondFormatHelper::GetExpression(CONDITION, mxLbCondType->get_active(), mxEdVal1->GetText(), mxEdVal2->GetText());
+}
+
+ScFormatEntry* ScConditionFrmtEntry::GetEntry() const
+{
+ return createConditionEntry();
+}
+
+void ScConditionFrmtEntry::SetActive()
+{
+ ScConditionMode eMode = EntryPosToConditionMode(mxLbCondType->get_active());
+ mxLbCondType->show();
+ switch(GetNumberEditFields(eMode))
+ {
+ case 1:
+ mxEdVal1->GetWidget()->show();
+ break;
+ case 2:
+ mxEdVal1->GetWidget()->show();
+ mxEdVal2->GetWidget()->show();
+ break;
+ }
+ mxFtStyle->show();
+ mxLbStyle->show();
+ mxWdPreviewWin->show();
+
+ Select();
+}
+
+void ScConditionFrmtEntry::SetInactive()
+{
+ mxLbCondType->hide();
+ mxEdVal1->GetWidget()->hide();
+ mxEdVal2->GetWidget()->hide();
+ mxFtStyle->hide();
+ mxLbStyle->hide();
+ mxWdPreviewWin->hide();
+
+ Deselect();
+}
+
+namespace {
+
+void UpdateStyleList(weld::ComboBox& rLbStyle, const ScDocument* pDoc)
+{
+ OUString aSelectedStyle = rLbStyle.get_active_text();
+ for (sal_Int32 i = rLbStyle.get_count(); i > 1; --i)
+ rLbStyle.remove(i - 1);
+ FillStyleListBox(pDoc, rLbStyle);
+ rLbStyle.set_active_text(aSelectedStyle);
+}
+
+}
+
+void ScConditionFrmtEntry::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::StyleSheetModified)
+ {
+ if(!mbIsInStyleCreate)
+ UpdateStyleList(*mxLbStyle, mpDoc);
+ }
+}
+
+namespace {
+
+void StyleSelect(weld::Window* pDialogParent, weld::ComboBox& rLbStyle, const ScDocument* pDoc, SvxFontPrevWindow& rWdPreview)
+{
+ if (rLbStyle.get_active() == 0)
+ {
+ // call new style dialog
+ SfxUInt16Item aFamilyItem( SID_STYLE_FAMILY, sal_uInt16(SfxStyleFamily::Para) );
+ SfxStringItem aRefItem( SID_STYLE_REFERENCE, ScResId(STR_STYLENAME_STANDARD) );
+ css::uno::Any aAny(pDialogParent->GetXWindow());
+ SfxUnoAnyItem aDialogParent( SID_DIALOG_PARENT, aAny );
+
+ // unlock the dispatcher so SID_STYLE_NEW can be executed
+ // (SetDispatcherLock would affect all Calc documents)
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SfxDispatcher* pDisp = pViewShell->GetDispatcher();
+ bool bLocked = pDisp->IsLocked();
+ if (bLocked)
+ pDisp->Lock(false);
+
+ // Execute the "new style" slot, complete with undo and all necessary updates.
+ // The return value (SfxUInt16Item) is ignored, look for new styles instead.
+ pDisp->ExecuteList(SID_STYLE_NEW,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aFamilyItem, &aRefItem }, { &aDialogParent });
+
+ if (bLocked)
+ pDisp->Lock(true);
+
+ // Find the new style and add it into the style list boxes
+ SfxStyleSheetIterator aStyleIter( pDoc->GetStyleSheetPool(), SfxStyleFamily::Para );
+ bool bFound = false;
+ for ( SfxStyleSheetBase* pStyle = aStyleIter.First(); pStyle && !bFound; pStyle = aStyleIter.Next() )
+ {
+ const OUString& aName = pStyle->GetName();
+ if (rLbStyle.find_text(aName) == -1) // all lists contain the same entries
+ {
+ for( sal_Int32 i = 1, n = rLbStyle.get_count(); i <= n && !bFound; ++i)
+ {
+ OUString aStyleName = ScGlobal::getCharClass().uppercase(rLbStyle.get_text(i));
+ if( i == n )
+ {
+ rLbStyle.append_text(aName);
+ rLbStyle.set_active_text(aName);
+ bFound = true;
+ }
+ else if( aStyleName > ScGlobal::getCharClass().uppercase(aName) )
+ {
+ rLbStyle.insert_text(i, aName);
+ rLbStyle.set_active_text(aName);
+ bFound = true;
+ }
+ }
+ }
+ }
+ }
+
+ OUString aStyleName = rLbStyle.get_active_text();
+ SfxStyleSheetBase* pStyleSheet = pDoc->GetStyleSheetPool()->Find( aStyleName, SfxStyleFamily::Para );
+ if(pStyleSheet)
+ {
+ const SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ rWdPreview.SetFromItemSet(rSet, false);
+ }
+}
+
+}
+
+IMPL_LINK_NOARG(ScConditionFrmtEntry, StyleSelectHdl, weld::ComboBox&, void)
+{
+ mbIsInStyleCreate = true;
+ StyleSelect(mpParent->GetFrameWeld(), *mxLbStyle, mpDoc, maWdPreview);
+ mbIsInStyleCreate = false;
+}
+
+// formula
+
+ScFormulaFrmtEntry::ScFormulaFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, ScCondFormatDlg* pDialogParent, const ScAddress& rPos, const ScCondFormatEntry* pFormat)
+ : ScCondFrmtEntry(pParent, pDoc, rPos)
+ , mxFtStyle(mxBuilder->weld_label("styleft"))
+ , mxLbStyle(mxBuilder->weld_combo_box("style"))
+ , mxWdPreviewWin(mxBuilder->weld_widget("previewwin"))
+ , mxWdPreview(new weld::CustomWeld(*mxBuilder, "preview", maWdPreview))
+ , mxEdFormula(new formula::RefEdit(mxBuilder->weld_entry("formula")))
+{
+ mxLbType->set_size_request(CommonWidgetWidth, -1);
+ mxWdPreview->set_size_request(-1, mxLbStyle->get_preferred_size().Height());
+
+ Init(pDialogParent);
+
+ mxLbType->set_active(2);
+
+ if(pFormat)
+ {
+ mxEdFormula->SetText(pFormat->GetExpression(rPos, 0, 0, pDoc->GetGrammar()));
+ mxLbStyle->set_active_text(pFormat->GetStyle());
+ }
+ else
+ {
+ mxLbStyle->set_active(1);
+ }
+
+ StyleSelectHdl(*mxLbStyle);
+}
+
+ScFormulaFrmtEntry::~ScFormulaFrmtEntry()
+{
+}
+
+void ScFormulaFrmtEntry::Init(ScCondFormatDlg* pDialogParent)
+{
+ mxEdFormula->SetGetFocusHdl( LINK( pDialogParent, ScCondFormatDlg, RangeGetFocusHdl ) );
+
+ FillStyleListBox( mpDoc, *mxLbStyle );
+ mxLbStyle->connect_changed( LINK( this, ScFormulaFrmtEntry, StyleSelectHdl ) );
+}
+
+IMPL_LINK_NOARG(ScFormulaFrmtEntry, StyleSelectHdl, weld::ComboBox&, void)
+{
+ StyleSelect(mpParent->GetFrameWeld(), *mxLbStyle, mpDoc, maWdPreview);
+}
+
+ScFormatEntry* ScFormulaFrmtEntry::createFormulaEntry() const
+{
+ OUString aFormula = mxEdFormula->GetText();
+ if(aFormula.isEmpty())
+ return nullptr;
+
+ ScFormatEntry* pEntry = new ScCondFormatEntry(ScConditionMode::Direct, aFormula, OUString(), *mpDoc, maPos, mxLbStyle->get_active_text());
+ return pEntry;
+}
+
+ScFormatEntry* ScFormulaFrmtEntry::GetEntry() const
+{
+ return createFormulaEntry();
+}
+
+OUString ScFormulaFrmtEntry::GetExpressionString()
+{
+ return ScCondFormatHelper::GetExpression(FORMULA, 0, mxEdFormula->GetText());
+}
+
+void ScFormulaFrmtEntry::SetActive()
+{
+ mxWdPreviewWin->show();
+ mxFtStyle->show();
+ mxLbStyle->show();
+ mxEdFormula->GetWidget()->show();
+
+ Select();
+}
+
+void ScFormulaFrmtEntry::SetInactive()
+{
+ mxWdPreviewWin->hide();
+ mxFtStyle->hide();
+ mxLbStyle->hide();
+ mxEdFormula->GetWidget()->hide();
+
+ Deselect();
+}
+
+//color scale
+
+namespace {
+
+OUString convertNumberToString(double nVal, const ScDocument* pDoc)
+{
+ SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
+ OUString aText;
+ pNumberFormatter->GetInputLineString(nVal, 0, aText);
+ return aText;
+}
+
+const struct
+{
+ ScColorScaleEntryType eType;
+ const char* sId;
+} TypeIdMap[] = {
+ { COLORSCALE_AUTO, "auto" },
+ { COLORSCALE_MIN, "min" },
+ { COLORSCALE_MAX, "max" },
+ { COLORSCALE_PERCENTILE, "percentil" },
+ { COLORSCALE_VALUE, "value" },
+ { COLORSCALE_PERCENT, "percent" },
+ { COLORSCALE_FORMULA, "formula" },
+};
+
+ScColorScaleEntryType getTypeForId(std::u16string_view sId)
+{
+ for (auto& r : TypeIdMap)
+ {
+ if (o3tl::equalsAscii(sId, r.sId))
+ return r.eType;
+ }
+ assert(false); // The id is not in TypeIdMap - something not in sync?
+ return COLORSCALE_AUTO; // invalid id - use default
+}
+
+// Item ids are imported from .ui into OUString* and are referenced by entry data.
+// See commit 83cefb5ceb4428d61a5b9fae80d1e673131e9bfe
+
+ScColorScaleEntryType getSelectedType(const weld::ComboBox& rListBox)
+{
+ return getTypeForId(rListBox.get_active_id());
+}
+
+sal_Int32 getEntryPos(const weld::ComboBox& rListBox, ScColorScaleEntryType eType)
+{
+ const sal_Int32 nSize = rListBox.get_count();
+ for (sal_Int32 i = 0; i < nSize; ++i)
+ {
+ if (getTypeForId(rListBox.get_id(i)) == eType)
+ return i;
+ }
+ return -1;
+}
+
+void selectType(weld::ComboBox& rListBox, ScColorScaleEntryType eType)
+{
+ const sal_Int32 nPos = getEntryPos(rListBox, eType);
+ if (nPos >= 0)
+ rListBox.set_active(nPos);
+}
+
+void removeType(weld::ComboBox& rListBox, ScColorScaleEntryType eType)
+{
+ const sal_Int32 nPos = getEntryPos(rListBox, eType);
+ if (nPos >= 0)
+ rListBox.remove(nPos);
+}
+
+void SetColorScaleEntryTypes( const ScColorScaleEntry& rEntry, weld::ComboBox& rLbType, weld::Entry& rEdit, ColorListBox& rLbCol, const ScDocument* pDoc )
+{
+ // entry Automatic is not available for color scales
+ assert(rEntry.GetType() > COLORSCALE_AUTO);
+ selectType(rLbType, rEntry.GetType());
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_MIN:
+ case COLORSCALE_MAX:
+ break;
+ case COLORSCALE_PERCENTILE:
+ case COLORSCALE_VALUE:
+ case COLORSCALE_PERCENT:
+ {
+ double nVal = rEntry.GetValue();
+ rEdit.set_text(convertNumberToString(nVal, pDoc));
+ }
+ break;
+ case COLORSCALE_FORMULA:
+ rEdit.set_text(rEntry.GetFormula(formula::FormulaGrammar::GRAM_DEFAULT));
+ break;
+ case COLORSCALE_AUTO:
+ abort();
+ break;
+ }
+ rLbCol.SelectEntry(rEntry.GetColor());
+}
+
+void SetColorScaleEntry(ScColorScaleEntry* pEntry, const weld::ComboBox& rType, const weld::Entry& rValue,
+ ScDocument* pDoc, const ScAddress& rPos)
+{
+ ScColorScaleEntryType eType = getSelectedType(rType);
+
+ pEntry->SetType(eType);
+ switch (eType)
+ {
+ case COLORSCALE_AUTO:
+ case COLORSCALE_MIN:
+ case COLORSCALE_MAX:
+ break;
+ case COLORSCALE_PERCENTILE:
+ case COLORSCALE_VALUE:
+ case COLORSCALE_PERCENT:
+ {
+ sal_uInt32 nIndex = 0;
+ double nVal = 0;
+ SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
+ (void)pNumberFormatter->IsNumberFormat(rValue.get_text(), nIndex, nVal);
+ pEntry->SetValue(nVal);
+ }
+ break;
+ case COLORSCALE_FORMULA:
+ pEntry->SetFormula(rValue.get_text(), *pDoc, rPos);
+ break;
+ default:
+ break;
+ }
+}
+
+ScColorScaleEntry* createColorScaleEntry( const weld::ComboBox& rType, const ColorListBox& rColor, const weld::Entry& rValue, ScDocument* pDoc, const ScAddress& rPos )
+{
+ ScColorScaleEntry* pEntry = new ScColorScaleEntry();
+
+ SetColorScaleEntry(pEntry, rType, rValue, pDoc, rPos);
+ Color aColor = rColor.GetSelectEntryColor();
+ pEntry->SetColor(aColor);
+ return pEntry;
+}
+
+}
+
+ScColorScale2FrmtEntry::ScColorScale2FrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScColorScaleFormat* pFormat)
+ : ScCondFrmtEntry(pParent, pDoc, rPos)
+ , mxLbColorFormat(mxBuilder->weld_combo_box("colorformat"))
+ , mxLbEntryTypeMin(mxBuilder->weld_combo_box("colscalemin"))
+ , mxLbEntryTypeMax(mxBuilder->weld_combo_box("colscalemax"))
+ , mxEdMin(mxBuilder->weld_entry("edcolscalemin"))
+ , mxEdMax(mxBuilder->weld_entry("edcolscalemax"))
+ , mxLbColMin(new ColorListBox(mxBuilder->weld_menu_button("lbcolmin"), [this]{ return mpParent->GetFrameWeld(); }))
+ , mxLbColMax(new ColorListBox(mxBuilder->weld_menu_button("lbcolmax"), [this]{ return mpParent->GetFrameWeld(); }))
+ , mxFtMin(mxBuilder->weld_label("Label_minimum"))
+ , mxFtMax(mxBuilder->weld_label("Label_maximum"))
+{
+ mxLbColorFormat->set_size_request(CommonWidgetWidth, -1);
+ mxLbEntryTypeMin->set_size_request(CommonWidgetWidth, -1);
+ mxLbEntryTypeMax->set_size_request(CommonWidgetWidth, -1);
+ mxLbColMin->get_widget().set_size_request(CommonWidgetWidth, -1);
+ mxLbColMax->get_widget().set_size_request(CommonWidgetWidth, -1);
+
+ mxFtMin->show();
+ mxFtMax->show();
+
+ // remove the automatic entry from color scales
+ removeType(*mxLbEntryTypeMin, COLORSCALE_AUTO);
+ removeType(*mxLbEntryTypeMax, COLORSCALE_AUTO);
+ // "min" selector doesn't need "max" entry, and vice versa
+ removeType(*mxLbEntryTypeMin, COLORSCALE_MAX);
+ removeType(*mxLbEntryTypeMax, COLORSCALE_MIN);
+
+ mxLbType->set_active(0);
+ mxLbColorFormat->set_active(0);
+ Init();
+ if(pFormat)
+ {
+ ScColorScaleEntries::const_iterator itr = pFormat->begin();
+ SetColorScaleEntryTypes(*itr[0], *mxLbEntryTypeMin, *mxEdMin, *mxLbColMin, pDoc);
+ ++itr;
+ SetColorScaleEntryTypes(*itr[0], *mxLbEntryTypeMax, *mxEdMax, *mxLbColMax, pDoc);
+ }
+ else
+ {
+ selectType(*mxLbEntryTypeMin, COLORSCALE_MIN);
+ selectType(*mxLbEntryTypeMax, COLORSCALE_MAX);
+ }
+
+ mxLbColorFormat->connect_changed( LINK( pParent, ScCondFormatList, ColFormatTypeHdl ) );
+
+ EntryTypeHdl(*mxLbEntryTypeMin);
+ EntryTypeHdl(*mxLbEntryTypeMax);
+}
+
+ScColorScale2FrmtEntry::~ScColorScale2FrmtEntry()
+{
+}
+
+void ScColorScale2FrmtEntry::Init()
+{
+ mxLbEntryTypeMin->connect_changed( LINK( this, ScColorScale2FrmtEntry, EntryTypeHdl ) );
+ mxLbEntryTypeMax->connect_changed( LINK( this, ScColorScale2FrmtEntry, EntryTypeHdl ) );
+ mxLbColMin->SelectEntry(Color(0xffff6d)); // Light Yellow 2
+ mxLbColMax->SelectEntry(Color(0x77bc65)); // Light Green 2
+}
+
+ScFormatEntry* ScColorScale2FrmtEntry::createColorscaleEntry() const
+{
+ ScColorScaleFormat* pColorScale = new ScColorScaleFormat(mpDoc);
+ pColorScale->AddEntry(createColorScaleEntry(*mxLbEntryTypeMin, *mxLbColMin, *mxEdMin, mpDoc, maPos));
+ pColorScale->AddEntry(createColorScaleEntry(*mxLbEntryTypeMax, *mxLbColMax, *mxEdMax, mpDoc, maPos));
+ return pColorScale;
+}
+
+OUString ScColorScale2FrmtEntry::GetExpressionString()
+{
+ return ScCondFormatHelper::GetExpression( COLORSCALE, 0 );
+}
+
+ScFormatEntry* ScColorScale2FrmtEntry::GetEntry() const
+{
+ return createColorscaleEntry();
+}
+
+void ScColorScale2FrmtEntry::SetActive()
+{
+ mxLbColorFormat->show();
+
+ mxLbEntryTypeMin->show();
+ mxLbEntryTypeMax->show();
+
+ mxEdMin->show();
+ mxEdMax->show();
+
+ mxLbColMin->show();
+ mxLbColMax->show();
+
+ Select();
+}
+
+void ScColorScale2FrmtEntry::SetInactive()
+{
+ mxLbColorFormat->hide();
+
+ mxLbEntryTypeMin->hide();
+ mxLbEntryTypeMax->hide();
+
+ mxEdMin->hide();
+ mxEdMax->hide();
+
+ mxLbColMin->hide();
+ mxLbColMax->hide();
+
+ Deselect();
+}
+
+IMPL_LINK( ScColorScale2FrmtEntry, EntryTypeHdl, weld::ComboBox&, rBox, void )
+{
+ weld::Entry* pEd = nullptr;
+ if (&rBox == mxLbEntryTypeMin.get())
+ pEd = mxEdMin.get();
+ else if (&rBox == mxLbEntryTypeMax.get())
+ pEd = mxEdMax.get();
+
+ if (!pEd)
+ return;
+
+ bool bEnableEdit = true;
+ if (getSelectedType(rBox) <= COLORSCALE_MAX)
+ {
+ bEnableEdit = false;
+ }
+
+ if (bEnableEdit)
+ pEd->set_sensitive(true);
+ else
+ pEd->set_sensitive(false);
+}
+
+ScColorScale3FrmtEntry::ScColorScale3FrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScColorScaleFormat* pFormat)
+ : ScCondFrmtEntry(pParent, pDoc, rPos)
+ , mxLbColorFormat(mxBuilder->weld_combo_box("colorformat"))
+ , mxLbEntryTypeMin(mxBuilder->weld_combo_box("colscalemin"))
+ , mxLbEntryTypeMiddle(mxBuilder->weld_combo_box("colscalemiddle"))
+ , mxLbEntryTypeMax(mxBuilder->weld_combo_box("colscalemax"))
+ , mxEdMin(mxBuilder->weld_entry("edcolscalemin"))
+ , mxEdMiddle(mxBuilder->weld_entry("edcolscalemiddle"))
+ , mxEdMax(mxBuilder->weld_entry("edcolscalemax"))
+ , mxLbColMin(new ColorListBox(mxBuilder->weld_menu_button("lbcolmin"), [this]{ return mpParent->GetFrameWeld(); }))
+ , mxLbColMiddle(new ColorListBox(mxBuilder->weld_menu_button("lbcolmiddle"), [this]{ return mpParent->GetFrameWeld(); }))
+ , mxLbColMax(new ColorListBox(mxBuilder->weld_menu_button("lbcolmax"), [this]{ return mpParent->GetFrameWeld(); }))
+ , mxFtMin(mxBuilder->weld_label("Label_minimum"))
+ , mxFtMax(mxBuilder->weld_label("Label_maximum"))
+{
+ mxLbColorFormat->set_size_request(CommonWidgetWidth, -1);
+ mxLbEntryTypeMin->set_size_request(CommonWidgetWidth, -1);
+ mxLbEntryTypeMiddle->set_size_request(CommonWidgetWidth, -1);
+ mxLbEntryTypeMax->set_size_request(CommonWidgetWidth, -1);
+ mxLbColMin->get_widget().set_size_request(CommonWidgetWidth, -1);
+ mxLbColMiddle->get_widget().set_size_request(CommonWidgetWidth, -1);
+ mxLbColMax->get_widget().set_size_request(CommonWidgetWidth, -1);
+ mxFtMin->show();
+ mxFtMax->show();
+
+ // remove the automatic entry from color scales
+ removeType(*mxLbEntryTypeMin, COLORSCALE_AUTO);
+ removeType(*mxLbEntryTypeMiddle, COLORSCALE_AUTO);
+ removeType(*mxLbEntryTypeMax, COLORSCALE_AUTO);
+ // "min" selector doesn't need "max" entry, and vice versa
+ removeType(*mxLbEntryTypeMin, COLORSCALE_MAX);
+ removeType(*mxLbEntryTypeMax, COLORSCALE_MIN);
+ mxLbColorFormat->set_active(1);
+
+ Init();
+ mxLbType->set_active(0);
+ if(pFormat)
+ {
+ ScColorScaleEntries::const_iterator itr = pFormat->begin();
+ SetColorScaleEntryTypes(*itr[0], *mxLbEntryTypeMin, *mxEdMin, *mxLbColMin, pDoc);
+ assert(pFormat->size() == 3);
+ ++itr;
+ SetColorScaleEntryTypes(*itr[0], *mxLbEntryTypeMiddle, *mxEdMiddle, *mxLbColMiddle, pDoc);
+ ++itr;
+ SetColorScaleEntryTypes(*itr[0], *mxLbEntryTypeMax, *mxEdMax, *mxLbColMax, pDoc);
+ }
+ else
+ {
+ mxLbColorFormat->set_active(1);
+ selectType(*mxLbEntryTypeMin, COLORSCALE_MIN);
+ selectType(*mxLbEntryTypeMiddle, COLORSCALE_PERCENTILE);
+ selectType(*mxLbEntryTypeMax, COLORSCALE_MAX);
+ mxEdMiddle->set_text(OUString::number(50));
+ }
+
+ mxLbColorFormat->connect_changed( LINK( pParent, ScCondFormatList, ColFormatTypeHdl ) );
+ EntryTypeHdl(*mxLbEntryTypeMin);
+ EntryTypeHdl(*mxLbEntryTypeMiddle);
+ EntryTypeHdl(*mxLbEntryTypeMax);
+}
+
+ScColorScale3FrmtEntry::~ScColorScale3FrmtEntry()
+{
+}
+
+void ScColorScale3FrmtEntry::Init()
+{
+ mxLbEntryTypeMin->connect_changed( LINK( this, ScColorScale3FrmtEntry, EntryTypeHdl ) );
+ mxLbEntryTypeMax->connect_changed( LINK( this, ScColorScale3FrmtEntry, EntryTypeHdl ) );
+ mxLbEntryTypeMiddle->connect_changed( LINK( this, ScColorScale3FrmtEntry, EntryTypeHdl ) );
+ mxLbColMin->SelectEntry(COL_LIGHTRED);
+ mxLbColMiddle->SelectEntry(COL_YELLOW);
+ mxLbColMax->SelectEntry(Color(0x00a933));
+}
+
+ScFormatEntry* ScColorScale3FrmtEntry::createColorscaleEntry() const
+{
+ ScColorScaleFormat* pColorScale = new ScColorScaleFormat(mpDoc);
+ pColorScale->AddEntry(createColorScaleEntry(*mxLbEntryTypeMin, *mxLbColMin, *mxEdMin, mpDoc, maPos));
+ if (mxLbColorFormat->get_active() == 1)
+ pColorScale->AddEntry(createColorScaleEntry(*mxLbEntryTypeMiddle, *mxLbColMiddle, *mxEdMiddle, mpDoc, maPos));
+ pColorScale->AddEntry(createColorScaleEntry(*mxLbEntryTypeMax, *mxLbColMax, *mxEdMax, mpDoc, maPos));
+ return pColorScale;
+}
+
+OUString ScColorScale3FrmtEntry::GetExpressionString()
+{
+ return ScCondFormatHelper::GetExpression( COLORSCALE, 0 );
+}
+
+ScFormatEntry* ScColorScale3FrmtEntry::GetEntry() const
+{
+ return createColorscaleEntry();
+}
+
+void ScColorScale3FrmtEntry::SetActive()
+{
+ mxLbColorFormat->show();
+ mxLbEntryTypeMin->show();
+ mxLbEntryTypeMiddle->show();
+ mxLbEntryTypeMax->show();
+
+ mxEdMin->show();
+ mxEdMiddle->show();
+ mxEdMax->show();
+
+ mxLbColMin->show();
+ mxLbColMiddle->show();
+ mxLbColMax->show();
+
+ Select();
+}
+
+void ScColorScale3FrmtEntry::SetInactive()
+{
+ mxLbColorFormat->hide();
+
+ mxLbEntryTypeMin->hide();
+ mxLbEntryTypeMiddle->hide();
+ mxLbEntryTypeMax->hide();
+
+ mxEdMin->hide();
+ mxEdMiddle->hide();
+ mxEdMax->hide();
+
+ mxLbColMin->hide();
+ mxLbColMiddle->hide();
+ mxLbColMax->hide();
+
+ Deselect();
+}
+
+IMPL_LINK( ScColorScale3FrmtEntry, EntryTypeHdl, weld::ComboBox&, rBox, void )
+{
+ weld::Entry* pEd = nullptr;
+ if(&rBox == mxLbEntryTypeMin.get())
+ pEd = mxEdMin.get();
+ else if(&rBox == mxLbEntryTypeMiddle.get())
+ pEd = mxEdMiddle.get();
+ else if(&rBox == mxLbEntryTypeMax.get())
+ pEd = mxEdMax.get();
+
+ if (!pEd)
+ return;
+
+ bool bEnableEdit = true;
+ if (getSelectedType(rBox) <= COLORSCALE_MAX)
+ {
+ bEnableEdit = false;
+ }
+
+ if(bEnableEdit)
+ pEd->set_sensitive(true);
+ else
+ pEd->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScConditionFrmtEntry, ConditionTypeSelectHdl, weld::ComboBox&, void)
+{
+ sal_Int32 nSelectPos = mxLbCondType->get_active();
+ ScConditionMode eMode = EntryPosToConditionMode(nSelectPos);
+ switch(GetNumberEditFields(eMode))
+ {
+ case 0:
+ mxEdVal1->GetWidget()->hide();
+ mxEdVal2->GetWidget()->hide();
+ mxFtVal->hide();
+ break;
+ case 1:
+ mxEdVal1->GetWidget()->show();
+ mxEdVal2->GetWidget()->hide();
+ mxFtVal->show();
+ break;
+ case 2:
+ mxEdVal1->GetWidget()->show();
+ mxEdVal2->GetWidget()->show();
+ mxFtVal->show();
+ break;
+ }
+}
+
+//databar
+
+namespace {
+
+void SetDataBarEntryTypes( const ScColorScaleEntry& rEntry, weld::ComboBox& rLbType, weld::Entry& rEdit, const ScDocument* pDoc )
+{
+ selectType(rLbType, rEntry.GetType());
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_AUTO:
+ case COLORSCALE_MIN:
+ case COLORSCALE_MAX:
+ break;
+ case COLORSCALE_VALUE:
+ case COLORSCALE_PERCENT:
+ case COLORSCALE_PERCENTILE:
+ {
+ double nVal = rEntry.GetValue();
+ SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
+ OUString aText;
+ pNumberFormatter->GetInputLineString(nVal, 0, aText);
+ rEdit.set_text(aText);
+ }
+ break;
+ case COLORSCALE_FORMULA:
+ rEdit.set_text(rEntry.GetFormula(formula::FormulaGrammar::GRAM_DEFAULT));
+ break;
+ }
+}
+
+}
+
+ScDataBarFrmtEntry::ScDataBarFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScDataBarFormat* pFormat)
+ : ScCondFrmtEntry(pParent, pDoc, rPos)
+ , mxLbColorFormat(mxBuilder->weld_combo_box("colorformat"))
+ , mxLbDataBarMinType(mxBuilder->weld_combo_box("colscalemin"))
+ , mxLbDataBarMaxType(mxBuilder->weld_combo_box("colscalemax"))
+ , mxEdDataBarMin(mxBuilder->weld_entry("edcolscalemin"))
+ , mxEdDataBarMax(mxBuilder->weld_entry("edcolscalemax"))
+ , mxBtOptions(mxBuilder->weld_button("options"))
+ , mxFtMin(mxBuilder->weld_label("Label_minimum"))
+ , mxFtMax(mxBuilder->weld_label("Label_maximum"))
+{
+ mxLbColorFormat->set_size_request(CommonWidgetWidth, -1);
+ mxLbDataBarMinType->set_size_request(CommonWidgetWidth, -1);
+ mxLbDataBarMaxType->set_size_request(CommonWidgetWidth, -1);
+
+ // "min" selector doesn't need "max" entry, and vice versa
+ removeType(*mxLbDataBarMinType, COLORSCALE_MAX);
+ removeType(*mxLbDataBarMaxType, COLORSCALE_MIN);
+
+ mxFtMin->show();
+ mxFtMax->show();
+
+ mxLbColorFormat->set_active(2);
+ mxLbType->set_active(0);
+ if(pFormat)
+ {
+ mpDataBarData.reset(new ScDataBarFormatData(*pFormat->GetDataBarData()));
+ SetDataBarEntryTypes(*mpDataBarData->mpLowerLimit, *mxLbDataBarMinType, *mxEdDataBarMin, pDoc);
+ SetDataBarEntryTypes(*mpDataBarData->mpUpperLimit, *mxLbDataBarMaxType, *mxEdDataBarMax, pDoc);
+ DataBarTypeSelectHdl(*mxLbDataBarMinType);
+ }
+ else
+ {
+ selectType(*mxLbDataBarMinType, COLORSCALE_AUTO);
+ selectType(*mxLbDataBarMaxType, COLORSCALE_AUTO);
+ DataBarTypeSelectHdl(*mxLbDataBarMinType);
+ }
+ Init();
+
+ mxLbColorFormat->connect_changed( LINK( pParent, ScCondFormatList, ColFormatTypeHdl ) );
+}
+
+ScDataBarFrmtEntry::~ScDataBarFrmtEntry()
+{
+}
+
+ScFormatEntry* ScDataBarFrmtEntry::GetEntry() const
+{
+ return createDatabarEntry();
+}
+
+void ScDataBarFrmtEntry::Init()
+{
+ mxLbDataBarMinType->connect_changed( LINK( this, ScDataBarFrmtEntry, DataBarTypeSelectHdl ) );
+ mxLbDataBarMaxType->connect_changed( LINK( this, ScDataBarFrmtEntry, DataBarTypeSelectHdl ) );
+
+ mxBtOptions->connect_clicked( LINK( this, ScDataBarFrmtEntry, OptionBtnHdl ) );
+
+ if(!mpDataBarData)
+ {
+ mpDataBarData.reset(new ScDataBarFormatData());
+ mpDataBarData->mpUpperLimit.reset(new ScColorScaleEntry());
+ mpDataBarData->mpLowerLimit.reset(new ScColorScaleEntry());
+ mpDataBarData->mpLowerLimit->SetType(COLORSCALE_AUTO);
+ mpDataBarData->mpUpperLimit->SetType(COLORSCALE_AUTO);
+ mpDataBarData->maPositiveColor = 0x2a6099;
+ }
+}
+
+ScFormatEntry* ScDataBarFrmtEntry::createDatabarEntry() const
+{
+ SetColorScaleEntry(mpDataBarData->mpLowerLimit.get(), *mxLbDataBarMinType,
+ *mxEdDataBarMin, mpDoc, maPos);
+ SetColorScaleEntry(mpDataBarData->mpUpperLimit.get(), *mxLbDataBarMaxType,
+ *mxEdDataBarMax, mpDoc, maPos);
+ ScDataBarFormat* pDataBar = new ScDataBarFormat(mpDoc);
+ pDataBar->SetDataBarData(new ScDataBarFormatData(*mpDataBarData));
+ return pDataBar;
+}
+
+OUString ScDataBarFrmtEntry::GetExpressionString()
+{
+ return ScCondFormatHelper::GetExpression( DATABAR, 0 );
+}
+
+void ScDataBarFrmtEntry::SetActive()
+{
+ mxLbColorFormat->show();
+
+ mxLbDataBarMinType->show();
+ mxLbDataBarMaxType->show();
+ mxEdDataBarMin->show();
+ mxEdDataBarMax->show();
+ mxBtOptions->show();
+
+ Select();
+}
+
+void ScDataBarFrmtEntry::SetInactive()
+{
+ mxLbColorFormat->hide();
+
+ mxLbDataBarMinType->hide();
+ mxLbDataBarMaxType->hide();
+ mxEdDataBarMin->hide();
+ mxEdDataBarMax->hide();
+ mxBtOptions->hide();
+
+ Deselect();
+}
+
+IMPL_LINK_NOARG( ScDataBarFrmtEntry, DataBarTypeSelectHdl, weld::ComboBox&, void )
+{
+ if (getSelectedType(*mxLbDataBarMinType) <= COLORSCALE_MAX)
+ mxEdDataBarMin->set_sensitive(false);
+ else
+ mxEdDataBarMin->set_sensitive(true);
+
+ if (getSelectedType(*mxLbDataBarMaxType) <= COLORSCALE_MAX)
+ mxEdDataBarMax->set_sensitive(false);
+ else
+ mxEdDataBarMax->set_sensitive(true);
+}
+
+IMPL_LINK_NOARG( ScDataBarFrmtEntry, OptionBtnHdl, weld::Button&, void )
+{
+ SetColorScaleEntry(mpDataBarData->mpLowerLimit.get(), *mxLbDataBarMinType,
+ *mxEdDataBarMin, mpDoc, maPos);
+ SetColorScaleEntry(mpDataBarData->mpUpperLimit.get(), *mxLbDataBarMaxType,
+ *mxEdDataBarMax, mpDoc, maPos);
+ ScDataBarSettingsDlg aDlg(mpParent->GetFrameWeld(), *mpDataBarData, mpDoc, maPos);
+ if (aDlg.run() == RET_OK)
+ {
+ mpDataBarData.reset(aDlg.GetData());
+ SetDataBarEntryTypes(*mpDataBarData->mpLowerLimit, *mxLbDataBarMinType, *mxEdDataBarMin, mpDoc);
+ SetDataBarEntryTypes(*mpDataBarData->mpUpperLimit, *mxLbDataBarMaxType, *mxEdDataBarMax, mpDoc);
+ DataBarTypeSelectHdl(*mxLbDataBarMinType);
+ }
+}
+
+ScDateFrmtEntry::ScDateFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScCondDateFormatEntry* pFormat)
+ : ScCondFrmtEntry(pParent, pDoc, ScAddress())
+ , mxLbDateEntry(mxBuilder->weld_combo_box("datetype"))
+ , mxFtStyle(mxBuilder->weld_label("styleft"))
+ , mxLbStyle(mxBuilder->weld_combo_box("style"))
+ , mxWdPreviewWin(mxBuilder->weld_widget("previewwin"))
+ , mxWdPreview(new weld::CustomWeld(*mxBuilder, "preview", maWdPreview))
+ , mbIsInStyleCreate(false)
+{
+ mxLbDateEntry->set_size_request(CommonWidgetWidth, -1);
+ mxLbStyle->set_size_request(CommonWidgetWidth, -1);
+
+ mxWdPreview->set_size_request(mxLbStyle->get_preferred_size().Height(), -1);
+
+ Init();
+
+ StartListening(*pDoc->GetStyleSheetPool(), DuplicateHandling::Prevent);
+
+ if(pFormat)
+ {
+ sal_Int32 nPos = static_cast<sal_Int32>(pFormat->GetDateType());
+ mxLbDateEntry->set_active(nPos);
+
+ mxLbStyle->set_active_text(pFormat->GetStyleName());
+ }
+
+ StyleSelectHdl(*mxLbStyle);
+}
+
+ScDateFrmtEntry::~ScDateFrmtEntry()
+{
+}
+
+void ScDateFrmtEntry::Init()
+{
+ mxLbDateEntry->set_active(0);
+ mxLbType->set_active(3);
+
+ FillStyleListBox( mpDoc, *mxLbStyle );
+ mxLbStyle->connect_changed( LINK( this, ScDateFrmtEntry, StyleSelectHdl ) );
+ mxLbStyle->set_active(1);
+}
+
+void ScDateFrmtEntry::SetActive()
+{
+ mxLbDateEntry->show();
+ mxFtStyle->show();
+ mxWdPreviewWin->show();
+ mxLbStyle->show();
+
+ Select();
+}
+
+void ScDateFrmtEntry::SetInactive()
+{
+ mxLbDateEntry->hide();
+ mxFtStyle->hide();
+ mxWdPreviewWin->hide();
+ mxLbStyle->hide();
+
+ Deselect();
+}
+
+void ScDateFrmtEntry::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if(rHint.GetId() == SfxHintId::StyleSheetModified)
+ {
+ if(!mbIsInStyleCreate)
+ UpdateStyleList(*mxLbStyle, mpDoc);
+ }
+}
+
+ScFormatEntry* ScDateFrmtEntry::GetEntry() const
+{
+ ScCondDateFormatEntry* pNewEntry = new ScCondDateFormatEntry(mpDoc);
+ condformat::ScCondFormatDateType eType = static_cast<condformat::ScCondFormatDateType>(mxLbDateEntry->get_active());
+ pNewEntry->SetDateType(eType);
+ pNewEntry->SetStyleName(mxLbStyle->get_active_text());
+ return pNewEntry;
+}
+
+OUString ScDateFrmtEntry::GetExpressionString()
+{
+ // tdf#124412 - set actual condition for an inactive date entry
+ return ScCondFormatHelper::GetExpression(DATE, mxLbDateEntry->get_active());
+}
+
+IMPL_LINK_NOARG( ScDateFrmtEntry, StyleSelectHdl, weld::ComboBox&, void )
+{
+ mbIsInStyleCreate = true;
+ StyleSelect(mpParent->GetFrameWeld(), *mxLbStyle, mpDoc, maWdPreview);
+ mbIsInStyleCreate = false;
+}
+
+class ScIconSetFrmtDataEntry
+{
+protected:
+ std::unique_ptr<weld::Builder> mxBuilder;
+private:
+ std::unique_ptr<weld::Container> mxGrid;
+ std::unique_ptr<weld::Image> mxImgIcon;
+ std::unique_ptr<weld::Label> mxFtEntry;
+ std::unique_ptr<weld::Entry> mxEdEntry;
+ std::unique_ptr<weld::ComboBox> mxLbEntryType;
+ weld::Container* mpContainer;
+
+public:
+ ScIconSetFrmtDataEntry(weld::Container* pParent, ScIconSetType eType, const ScDocument* pDoc,
+ sal_Int32 i, const ScColorScaleEntry* pEntry = nullptr);
+ ~ScIconSetFrmtDataEntry();
+ void Show() { mxGrid->show(); }
+ void Hide() { mxGrid->hide(); }
+ void set_grid_top_attach(int nTop)
+ {
+ mxGrid->set_grid_left_attach(0);
+ mxGrid->set_grid_top_attach(nTop);
+ }
+
+ ScColorScaleEntry* CreateEntry(ScDocument& rDoc, const ScAddress& rPos) const;
+
+ void SetFirstEntry();
+};
+
+ScIconSetFrmtDataEntry::ScIconSetFrmtDataEntry(weld::Container* pParent, ScIconSetType eType, const ScDocument* pDoc, sal_Int32 i, const ScColorScaleEntry* pEntry)
+ : mxBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/conditionaliconset.ui"))
+ , mxGrid(mxBuilder->weld_container("ConditionalIconSet"))
+ , mxImgIcon(mxBuilder->weld_image("icon"))
+ , mxFtEntry(mxBuilder->weld_label("label"))
+ , mxEdEntry(mxBuilder->weld_entry("entry"))
+ , mxLbEntryType(mxBuilder->weld_combo_box("listbox"))
+ , mpContainer(pParent)
+{
+ mxImgIcon->set_from_icon_name(ScIconSetFormat::getIconName(eType, i));
+ if(pEntry)
+ {
+ switch(pEntry->GetType())
+ {
+ case COLORSCALE_VALUE:
+ mxLbEntryType->set_active(0);
+ mxEdEntry->set_text(convertNumberToString(pEntry->GetValue(), pDoc));
+ break;
+ case COLORSCALE_PERCENTILE:
+ mxLbEntryType->set_active(2);
+ mxEdEntry->set_text(convertNumberToString(pEntry->GetValue(), pDoc));
+ break;
+ case COLORSCALE_PERCENT:
+ mxLbEntryType->set_active(1);
+ mxEdEntry->set_text(convertNumberToString(pEntry->GetValue(), pDoc));
+ break;
+ case COLORSCALE_FORMULA:
+ mxLbEntryType->set_active(3);
+ mxEdEntry->set_text(pEntry->GetFormula(formula::FormulaGrammar::GRAM_DEFAULT));
+ break;
+ default:
+ assert(false);
+ }
+ }
+ else
+ {
+ mxLbEntryType->set_active(1);
+ }
+}
+
+ScIconSetFrmtDataEntry::~ScIconSetFrmtDataEntry()
+{
+ mpContainer->move(mxGrid.get(), nullptr);
+}
+
+ScColorScaleEntry* ScIconSetFrmtDataEntry::CreateEntry(ScDocument& rDoc, const ScAddress& rPos) const
+{
+ sal_Int32 nPos = mxLbEntryType->get_active();
+ OUString aText = mxEdEntry->get_text();
+ ScColorScaleEntry* pEntry = new ScColorScaleEntry();
+
+ sal_uInt32 nIndex = 0;
+ double nVal = 0;
+ SvNumberFormatter* pNumberFormatter = rDoc.GetFormatTable();
+ (void)pNumberFormatter->IsNumberFormat(aText, nIndex, nVal);
+ pEntry->SetValue(nVal);
+
+ switch(nPos)
+ {
+ case 0:
+ pEntry->SetType(COLORSCALE_VALUE);
+ break;
+ case 1:
+ pEntry->SetType(COLORSCALE_PERCENT);
+ break;
+ case 2:
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ break;
+ case 3:
+ pEntry->SetType(COLORSCALE_FORMULA);
+ pEntry->SetFormula(aText, rDoc, rPos, rDoc.GetGrammar());
+ break;
+ default:
+ assert(false);
+ }
+
+ return pEntry;
+}
+
+void ScIconSetFrmtDataEntry::SetFirstEntry()
+{
+ mxEdEntry->hide();
+ mxLbEntryType->hide();
+ mxFtEntry->hide();
+ mxEdEntry->set_text("0");
+ mxLbEntryType->set_active(1);
+}
+
+ScIconSetFrmtEntry::ScIconSetFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScIconSetFormat* pFormat)
+ : ScCondFrmtEntry(pParent, pDoc, rPos)
+ , mxLbColorFormat(mxBuilder->weld_combo_box("colorformat"))
+ , mxLbIconSetType(mxBuilder->weld_combo_box("iconsettype"))
+ , mxIconParent(mxBuilder->weld_container("iconparent"))
+{
+ mxLbColorFormat->set_size_request(CommonWidgetWidth, -1);
+ mxLbIconSetType->set_size_request(CommonWidgetWidth, -1);
+
+ Init();
+ mxLbColorFormat->connect_changed(LINK(pParent, ScCondFormatList, ColFormatTypeHdl));
+
+ if(pFormat)
+ {
+ const ScIconSetFormatData* pIconSetFormatData = pFormat->GetIconSetData();
+ ScIconSetType eType = pIconSetFormatData->eIconSetType;
+ sal_Int32 nType = static_cast<sal_Int32>(eType);
+ mxLbIconSetType->set_active(nType);
+
+ for (size_t i = 0, n = pIconSetFormatData->m_Entries.size();
+ i < n; ++i)
+ {
+ maEntries.emplace_back(new ScIconSetFrmtDataEntry(
+ mxIconParent.get(), eType, pDoc, i, pIconSetFormatData->m_Entries[i].get()));
+ maEntries[i]->set_grid_top_attach(i);
+ }
+ maEntries[0]->SetFirstEntry();
+ }
+ else
+ IconSetTypeHdl(*mxLbIconSetType);
+}
+
+ScIconSetFrmtEntry::~ScIconSetFrmtEntry()
+{
+}
+
+void ScIconSetFrmtEntry::Init()
+{
+ mxLbColorFormat->set_active(3);
+ mxLbType->set_active(0);
+ mxLbIconSetType->set_active(0);
+
+ mxLbIconSetType->connect_changed(LINK(this, ScIconSetFrmtEntry, IconSetTypeHdl));
+}
+
+IMPL_LINK_NOARG( ScIconSetFrmtEntry, IconSetTypeHdl, weld::ComboBox&, void )
+{
+ const ScIconSetMap* pMap = ScIconSetFormat::g_IconSetMap;
+
+ sal_Int32 nPos = mxLbIconSetType->get_active();
+ sal_uInt32 nElements = pMap[nPos].nElements;
+
+ maEntries.clear();
+
+ for(size_t i = 0; i < nElements; ++i)
+ {
+ maEntries.emplace_back(new ScIconSetFrmtDataEntry(mxIconParent.get(), static_cast<ScIconSetType>(nPos), mpDoc, i));
+ maEntries[i]->set_grid_top_attach(i);
+ maEntries[i]->Show();
+ }
+ maEntries[0]->SetFirstEntry();
+}
+
+OUString ScIconSetFrmtEntry::GetExpressionString()
+{
+ return ScCondFormatHelper::GetExpression(ICONSET, 0);
+}
+
+void ScIconSetFrmtEntry::SetActive()
+{
+ mxLbColorFormat->show();
+ mxLbIconSetType->show();
+ for(auto& rxEntry : maEntries)
+ {
+ rxEntry->Show();
+ }
+
+ Select();
+}
+
+void ScIconSetFrmtEntry::SetInactive()
+{
+ mxLbColorFormat->hide();
+ mxLbIconSetType->hide();
+ for(auto& rxEntry : maEntries)
+ {
+ rxEntry->Hide();
+ }
+
+ Deselect();
+}
+
+ScFormatEntry* ScIconSetFrmtEntry::GetEntry() const
+{
+ ScIconSetFormat* pFormat = new ScIconSetFormat(mpDoc);
+
+ ScIconSetFormatData* pData = new ScIconSetFormatData;
+ pData->eIconSetType = static_cast<ScIconSetType>(mxLbIconSetType->get_active());
+ for(const auto& rxEntry : maEntries)
+ {
+ pData->m_Entries.emplace_back(rxEntry->CreateEntry(*mpDoc, maPos));
+ }
+ pFormat->SetIconSetData(pData);
+
+ return pFormat;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/condformat/condformatdlgitem.cxx b/sc/source/ui/condformat/condformatdlgitem.cxx
new file mode 100644
index 0000000000..b0bf511c3b
--- /dev/null
+++ b/sc/source/ui/condformat/condformatdlgitem.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/.
+ */
+
+
+#include <utility>
+
+#include <scitems.hxx>
+#include <condformatdlgitem.hxx>
+
+ScCondFormatDlgItem::ScCondFormatDlgItem(std::shared_ptr<ScConditionalFormatList> pCondFormats,
+ sal_Int32 nItem, bool bManaged):
+ SfxPoolItem(SCITEM_CONDFORMATDLGDATA),
+ mpCondFormats(std::move(pCondFormats)),
+ mnItem(nItem),
+ meDialogType(condformat::dialog::CONDITION),
+ mbManaged(bManaged)
+{
+}
+
+ScCondFormatDlgItem::~ScCondFormatDlgItem()
+{
+}
+
+bool ScCondFormatDlgItem::operator==(const SfxPoolItem& rItem) const
+{
+ assert(SfxPoolItem::operator==(rItem)); (void)rItem;
+ return false;
+}
+
+ScCondFormatDlgItem* ScCondFormatDlgItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new ScCondFormatDlgItem(*this);
+}
+
+bool ScCondFormatDlgItem::IsManaged() const
+{
+ return mbManaged;
+}
+
+condformat::dialog::ScCondFormatDialogType ScCondFormatDlgItem::GetDialogType() const
+{
+ return meDialogType;
+}
+
+sal_Int32 ScCondFormatDlgItem::GetIndex() const
+{
+ return mnItem;
+}
+
+ScConditionalFormatList* ScCondFormatDlgItem::GetConditionalFormatList()
+{
+ return mpCondFormats.get();
+}
+
+void ScCondFormatDlgItem::SetDialogType(condformat::dialog::ScCondFormatDialogType eType)
+{
+ meDialogType = eType;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/condformat/condformateasydlg.cxx b/sc/source/ui/condformat/condformateasydlg.cxx
new file mode 100644
index 0000000000..a368ec310c
--- /dev/null
+++ b/sc/source/ui/condformat/condformateasydlg.cxx
@@ -0,0 +1,230 @@
+#include <docfunc.hxx>
+#include <condformateasydlg.hxx>
+#include <stlpool.hxx>
+#include <viewdata.hxx>
+#include <reffact.hxx>
+#include <scresid.hxx>
+#include <svl/style.hxx>
+#include <strings.hrc>
+
+namespace
+{
+void FillStyleListBox(const ScDocument* pDocument, weld::ComboBox& rCombo)
+{
+ std::set<OUString> aStyleNames;
+ SfxStyleSheetIterator aStyleIter(pDocument->GetStyleSheetPool(), SfxStyleFamily::Para);
+ for (SfxStyleSheetBase* pStyle = aStyleIter.First(); pStyle; pStyle = aStyleIter.Next())
+ {
+ aStyleNames.insert(pStyle->GetName());
+ }
+ for (const auto& rStyleName : aStyleNames)
+ {
+ rCombo.append_text(rStyleName);
+ }
+}
+
+void UpdateStyleList(const ScDocument* pDocument, weld::ComboBox& rCombo)
+{
+ OUString sSelectedStyle = rCombo.get_active_text();
+ for (sal_Int32 i = rCombo.get_count(); i > 1; --i)
+ rCombo.remove(i - 1);
+ FillStyleListBox(pDocument, rCombo);
+ rCombo.set_active_text(sSelectedStyle);
+}
+}
+
+namespace sc
+{
+void ConditionalFormatEasyDialog::SetDescription(std::u16string_view rCondition)
+{
+ mxDescription->set_label(mxDescription->get_label().replaceAll("%1", rCondition));
+}
+
+ConditionalFormatEasyDialog::ConditionalFormatEasyDialog(SfxBindings* pBindings,
+ SfxChildWindow* pChildWindow,
+ weld::Window* pParent,
+ ScViewData* pViewData)
+ : ScAnyRefDlgController(pBindings, pChildWindow, pParent,
+ "modules/scalc/ui/conditionaleasydialog.ui", "CondFormatEasyDlg")
+ , mpViewData(pViewData)
+ , mpDocument(&mpViewData->GetDocument())
+ , mxNumberEntry(m_xBuilder->weld_entry("entryNumber"))
+ , mxNumberEntry2(m_xBuilder->weld_entry("entryNumber2"))
+ , mxRangeEntry(new formula::RefEdit(m_xBuilder->weld_entry("entryRange")))
+ , mxButtonRangeEdit(new formula::RefButton(m_xBuilder->weld_button("rbassign")))
+ , mxStyles(m_xBuilder->weld_combo_box("themeCombo"))
+ , mxDescription(m_xBuilder->weld_label("description"))
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+{
+ mxButtonRangeEdit->SetReferences(this, mxRangeEntry.get());
+ const ScConditionMode* pCurrentMode
+ = pViewData->GetDocument().GetEasyConditionalFormatDialogData();
+ if (!pCurrentMode)
+ {
+ SAL_WARN(
+ "sc",
+ "Condition mode not set for easy conditional format dialog, this should not happen");
+ meMode = ScConditionMode::Greater;
+ }
+ else
+ {
+ meMode = *pCurrentMode;
+ }
+ mxNumberEntry2->hide();
+ switch (meMode)
+ {
+ case ScConditionMode::Equal:
+ SetDescription(ScResId(STR_CONDITION_EQUAL));
+ break;
+ case ScConditionMode::Less:
+ SetDescription(ScResId(STR_CONDITION_LESS));
+ break;
+ case ScConditionMode::Greater:
+ SetDescription(ScResId(STR_CONDITION_GREATER));
+ break;
+ case ScConditionMode::EqLess:
+ SetDescription(ScResId(STR_CONDITION_EQLESS));
+ break;
+ case ScConditionMode::EqGreater:
+ SetDescription(ScResId(STR_CONDITION_EQGREATER));
+ break;
+ case ScConditionMode::NotEqual:
+ SetDescription(ScResId(STR_CONDITION_NOT_EQUAL));
+ break;
+ case ScConditionMode::Between:
+ SetDescription(ScResId(STR_CONDITION_BETWEEN));
+ mxNumberEntry2->show();
+ break;
+ // NotBetween
+ // Duplicate
+ // NotDuplicate
+ // Direct
+ // Top10
+ // Bottom10
+ // TopPercent
+ // BottomPercent
+ // AboveAverage
+ // BelowAverage
+ // AboveEqualAverage
+ // BelowEqualAverage
+ case ScConditionMode::Error:
+ SetDescription(ScResId(STR_CONDITION_ERROR));
+ break;
+ case ScConditionMode::NoError:
+ SetDescription(ScResId(STR_CONDITION_NOERROR));
+ break;
+ // BeginsWith
+ // EndsWith
+ case ScConditionMode::ContainsText:
+ SetDescription(ScResId(STR_CONDITION_CONTAINS_TEXT));
+ break;
+ case ScConditionMode::NotContainsText:
+ SetDescription(ScResId(STR_CONDITION_NOT_CONTAINS_TEXT));
+ break;
+ default:
+ SAL_WARN("sc",
+ "ConditionalFormatEasyDialog::ConditionalFormatEasyDialog: invalid format");
+ break;
+ }
+
+ mxButtonOk->connect_clicked(LINK(this, ConditionalFormatEasyDialog, ButtonPressed));
+ mxButtonCancel->connect_clicked(LINK(this, ConditionalFormatEasyDialog, ButtonPressed));
+
+ ScRangeList aRange;
+ mpViewData->GetMarkData().FillRangeListWithMarks(&aRange, false);
+ if (aRange.empty())
+ {
+ ScAddress aPosition(mpViewData->GetCurX(), mpViewData->GetCurY(), mpViewData->GetTabNo());
+ aRange.push_back(ScRange(aPosition));
+ }
+ maPosition = aRange.GetTopLeftCorner();
+
+ OUString sRangeString;
+ aRange.Format(sRangeString, ScRefFlags::VALID, *mpDocument, mpDocument->GetAddressConvention());
+ mxRangeEntry->SetText(sRangeString);
+
+ StartListening(*mpDocument->GetStyleSheetPool(), DuplicateHandling::Prevent);
+ FillStyleListBox(mpDocument, *mxStyles);
+
+ mxStyles->set_active(1);
+}
+
+ConditionalFormatEasyDialog::~ConditionalFormatEasyDialog() {}
+
+void ConditionalFormatEasyDialog::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::StyleSheetModified)
+ UpdateStyleList(mpDocument, *mxStyles);
+}
+
+void ConditionalFormatEasyDialog::SetReference(const ScRange& rRange, ScDocument&)
+{
+ formula::RefEdit* pEdit = mxRangeEntry.get();
+ if (rRange.aStart != rRange.aEnd)
+ RefInputStart(pEdit);
+
+ ScRefFlags nFlags = ScRefFlags::RANGE_ABS;
+ const ScDocument& rDoc = mpViewData->GetDocument();
+ OUString sRange(
+ rRange.Format(rDoc, nFlags, ScAddress::Details(mpDocument->GetAddressConvention(), 0, 0)));
+ pEdit->SetRefString(sRange);
+ maPosition = rRange.aStart;
+}
+
+void ConditionalFormatEasyDialog::SetActive()
+{
+ mxRangeEntry->GrabFocus();
+ RefInputDone();
+}
+
+void ConditionalFormatEasyDialog::Close()
+{
+ DoClose(ConditionalFormatEasyDialogWrapper::GetChildWindowId());
+}
+
+IMPL_LINK(ConditionalFormatEasyDialog, ButtonPressed, weld::Button&, rButton, void)
+{
+ if (&rButton == mxButtonOk.get())
+ {
+ std::unique_ptr<ScConditionalFormat> pFormat(new ScConditionalFormat(0, mpDocument));
+
+ OUString sExpression1 = mxNumberEntry->get_text();
+ OUString sExpression2 = mxNumberEntry2->get_text();
+
+ switch (meMode)
+ {
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ sExpression1 = "\"" + sExpression1 + "\"";
+ sExpression2 = "\"" + sExpression2 + "\"";
+ break;
+ default:
+ break;
+ }
+
+ ScFormatEntry* pEntry
+ = new ScCondFormatEntry(meMode, sExpression1, sExpression2, *mpDocument, maPosition,
+ mxStyles->get_active_text());
+
+ ScRangeList aRange;
+ ScRefFlags nFlags
+ = aRange.Parse(mxRangeEntry->GetText(), mpViewData->GetDocument(),
+ mpViewData->GetDocument().GetAddressConvention(), maPosition.Tab());
+ if ((nFlags & ScRefFlags::VALID) && !aRange.empty())
+ {
+ pFormat->AddEntry(pEntry);
+ pFormat->SetRange(aRange);
+ auto& rRangeList = pFormat->GetRange();
+ mpViewData->GetDocShell()->GetDocFunc().ReplaceConditionalFormat(
+ 0, std::move(pFormat), maPosition.Tab(), rRangeList);
+ }
+ m_xDialog->response(RET_OK);
+ }
+ else if (&rButton == mxButtonCancel.get())
+ m_xDialog->response(RET_CANCEL);
+}
+
+} // namespace sc
diff --git a/sc/source/ui/condformat/condformathelper.cxx b/sc/source/ui/condformat/condformathelper.cxx
new file mode 100644
index 0000000000..6d1f1b1b3a
--- /dev/null
+++ b/sc/source/ui/condformat/condformathelper.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <condformathelper.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <conditio.hxx>
+
+namespace {
+
+OUString getTextForType(ScCondFormatEntryType eType)
+{
+ switch(eType)
+ {
+ case CONDITION:
+ return ScResId(STR_COND_CONDITION);
+ case COLORSCALE:
+ return ScResId(STR_COND_COLORSCALE);
+ case DATABAR:
+ return ScResId(STR_COND_DATABAR);
+ case FORMULA:
+ return ScResId(STR_COND_FORMULA);
+ case ICONSET:
+ return ScResId(STR_COND_ICONSET);
+ case DATE:
+ return ScResId(STR_COND_DATE);
+ default:
+ break;
+ }
+
+ return OUString();
+}
+
+OUString getExpression(sal_Int32 nIndex)
+{
+ switch(nIndex)
+ {
+ case 0:
+ return "=";
+ case 1:
+ return "<";
+ case 2:
+ return ">";
+ case 3:
+ return "<=";
+ case 4:
+ return ">=";
+ case 5:
+ return "!=";
+ case 6:
+ return ScResId(STR_COND_BETWEEN);
+ case 7:
+ return ScResId(STR_COND_NOTBETWEEN);
+ case 8:
+ return ScResId(STR_COND_DUPLICATE);
+ case 9:
+ return ScResId(STR_COND_UNIQUE);
+
+ case 11:
+ return ScResId(STR_COND_TOP10);
+ case 12:
+ return ScResId(STR_COND_BOTTOM10);
+ case 13:
+ return ScResId(STR_COND_TOP_PERCENT);
+ case 14:
+ return ScResId(STR_COND_BOTTOM_PERCENT);
+ case 15:
+ return ScResId(STR_COND_ABOVE_AVERAGE);
+ case 16:
+ return ScResId(STR_COND_BELOW_AVERAGE);
+ case 17:
+ return ScResId(STR_COND_ABOVE_EQUAL_AVERAGE);
+ case 18:
+ return ScResId(STR_COND_BELOW_EQUAL_AVERAGE);
+ case 19:
+ return ScResId(STR_COND_ERROR);
+ case 20:
+ return ScResId(STR_COND_NOERROR);
+ case 21:
+ return ScResId(STR_COND_BEGINS_WITH);
+ case 22:
+ return ScResId(STR_COND_ENDS_WITH);
+ case 23:
+ return ScResId(STR_COND_CONTAINS);
+ case 24:
+ return ScResId(STR_COND_NOT_CONTAINS);
+
+ case 10:
+ assert(false);
+ }
+ return OUString();
+}
+
+OUString getDateString(sal_Int32 nIndex)
+{
+ const TranslateId aCondStrs[] =
+ {
+ STR_COND_TODAY,
+ STR_COND_YESTERDAY,
+ STR_COND_TOMORROW,
+ STR_COND_LAST7DAYS,
+ STR_COND_THISWEEK,
+ STR_COND_LASTWEEK,
+ STR_COND_NEXTWEEK,
+ STR_COND_THISMONTH,
+ STR_COND_LASTMONTH,
+ STR_COND_NEXTMONTH,
+ STR_COND_THISYEAR,
+ STR_COND_LASTYEAR,
+ STR_COND_NEXTYEAR
+ };
+
+ if (nIndex >= 0 && o3tl::make_unsigned(nIndex) < SAL_N_ELEMENTS(aCondStrs))
+ return ScResId(aCondStrs[nIndex]);
+ assert(false);
+ return OUString();
+}
+
+}
+
+OUString ScCondFormatHelper::GetExpression(const ScConditionalFormat& rFormat, const ScAddress& rPos)
+{
+ OUStringBuffer aBuffer;
+ if(!rFormat.IsEmpty())
+ {
+ switch(rFormat.GetEntry(0)->GetType())
+ {
+ case ScFormatEntry::Type::Condition:
+ case ScFormatEntry::Type::ExtCondition:
+ {
+ const ScConditionEntry* pEntry = static_cast<const ScConditionEntry*>(rFormat.GetEntry(0));
+ ScConditionMode eMode = pEntry->GetOperation();
+ if(eMode == ScConditionMode::Direct)
+ {
+ aBuffer.append(getTextForType(FORMULA)
+ + " "
+ + pEntry->GetExpression(rPos, 0));
+ }
+ else
+ {
+ aBuffer.append(getTextForType(CONDITION)
+ + " "
+ + getExpression(static_cast<sal_Int32>(eMode))
+ + " ");
+ if(eMode == ScConditionMode::Between || eMode == ScConditionMode::NotBetween)
+ {
+ aBuffer.append(pEntry->GetExpression(rPos, 0)
+ + " "
+ + ScResId(STR_COND_AND)
+ + " "
+ + pEntry->GetExpression(rPos, 1));
+ }
+ else if(eMode <= ScConditionMode::NotEqual || eMode >= ScConditionMode::BeginsWith)
+ {
+ aBuffer.append(pEntry->GetExpression(rPos, 0));
+ }
+ }
+ }
+
+ break;
+ case ScFormatEntry::Type::Databar:
+ aBuffer.append(getTextForType(DATABAR));
+ break;
+ case ScFormatEntry::Type::Colorscale:
+ aBuffer.append(getTextForType(COLORSCALE));
+ break;
+ case ScFormatEntry::Type::Iconset:
+ aBuffer.append(getTextForType(ICONSET));
+ break;
+ case ScFormatEntry::Type::Date:
+ {
+ sal_Int32 nDateEntry = static_cast<sal_Int32>(static_cast<const ScCondDateFormatEntry*>(rFormat.GetEntry(0))->GetDateType());
+ aBuffer.append(getTextForType(DATE)
+ + " "
+ + getDateString(nDateEntry));
+ }
+ break;
+ }
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+OUString ScCondFormatHelper::GetExpression( ScCondFormatEntryType eType, sal_Int32 nIndex,
+ std::u16string_view aStr1, std::u16string_view aStr2 )
+{
+ OUStringBuffer aBuffer(getTextForType(eType) + " ");
+ if(eType == CONDITION)
+ {
+ // workaround missing FORMULA option in the conditions case
+ // FORMULA is handled later
+ if(nIndex > 9)
+ ++nIndex;
+ aBuffer.append(getExpression(nIndex));
+ if(nIndex <= 7 || nIndex >= 19)
+ {
+ aBuffer.append(OUString::Concat(" ") + aStr1);
+ if(nIndex == 6 || nIndex == 7)
+ {
+ aBuffer.append(" " + ScResId(STR_COND_AND) + " " + aStr2);
+ }
+ }
+ }
+ else if(eType == FORMULA)
+ {
+ aBuffer.append(OUString::Concat(" ") + aStr1);
+ }
+ else if(eType == DATE)
+ {
+ aBuffer.append(getDateString(nIndex));
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/condformat/condformatmgr.cxx b/sc/source/ui/condformat/condformatmgr.cxx
new file mode 100644
index 0000000000..31e16ab8c0
--- /dev/null
+++ b/sc/source/ui/condformat/condformatmgr.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 <condformatmgr.hxx>
+#include <condformathelper.hxx>
+#include <condformatdlg.hxx>
+#include <document.hxx>
+#include <conditio.hxx>
+#include <o3tl/safeint.hxx>
+#include <unotools/viewoptions.hxx>
+
+ScCondFormatManagerWindow::ScCondFormatManagerWindow(weld::TreeView& rTreeView,
+ ScDocument& rDoc, ScConditionalFormatList* pFormatList)
+ : mrTreeView(rTreeView)
+ , mrDoc(rDoc)
+ , mpFormatList(pFormatList)
+{
+ mrTreeView.set_size_request(mrTreeView.get_approximate_digit_width() * 70,
+ mrTreeView.get_height_rows(20));
+ setColSizes();
+
+ Init();
+ mrTreeView.set_selection_mode(SelectionMode::Multiple);
+ mrTreeView.make_sorted();
+}
+
+void ScCondFormatManagerWindow::Init()
+{
+ mrTreeView.freeze();
+
+ if (mpFormatList)
+ {
+ int nRow = 0;
+ OUString sRangeStr;
+ for(const auto& rItem : *mpFormatList)
+ {
+ const ScRangeList& aRange = rItem->GetRange();
+ aRange.Format(sRangeStr, ScRefFlags::VALID, mrDoc, mrDoc.GetAddressConvention());
+ mrTreeView.append(OUString::number(rItem->GetKey()), sRangeStr);
+ mrTreeView.set_text(nRow, ScCondFormatHelper::GetExpression(*rItem, aRange.GetTopLeftCorner()), 1);
+ ++nRow;
+ }
+ }
+
+ mrTreeView.thaw();
+
+ if (mpFormatList && !mpFormatList->empty())
+ mrTreeView.select(0);
+}
+
+void ScCondFormatManagerWindow::DeleteSelection()
+{
+ auto aSelectedRows = mrTreeView.get_selected_rows();
+ std::sort(aSelectedRows.begin(), aSelectedRows.end());
+ for (auto it = aSelectedRows.rbegin(); it != aSelectedRows.rend(); ++it)
+ {
+ sal_Int32 nIndex = mrTreeView.get_id(*it).toInt32();
+ mpFormatList->erase(nIndex);
+ mrTreeView.remove(*it);
+ }
+}
+
+ScConditionalFormat* ScCondFormatManagerWindow::GetSelection()
+{
+ int nEntry = mrTreeView.get_selected_index();
+ if (nEntry == -1)
+ return nullptr;
+
+ sal_Int32 nIndex = mrTreeView.get_id(nEntry).toInt32();
+ return mpFormatList->GetFormat(nIndex);
+}
+
+void ScCondFormatManagerWindow::setColSizes()
+{
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(mrTreeView.get_size_request().Width() / 2)
+ };
+ mrTreeView.set_column_fixed_widths(aWidths);
+}
+
+ScCondFormatManagerDlg::ScCondFormatManagerDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList)
+ : GenericDialogController(pParent, "modules/scalc/ui/condformatmanager.ui", "CondFormatManager")
+ , m_bModified(false)
+ , m_xFormatList( pFormatList ? new ScConditionalFormatList(*pFormatList) : nullptr)
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("remove"))
+ , m_xBtnEdit(m_xBuilder->weld_button("edit"))
+ , m_xTreeView(m_xBuilder->weld_tree_view("CONTAINER"))
+ , m_xCtrlManager(new ScCondFormatManagerWindow(*m_xTreeView, rDoc, m_xFormatList.get()))
+{
+ m_xBtnRemove->connect_clicked(LINK(this, ScCondFormatManagerDlg, RemoveBtnHdl));
+ m_xBtnEdit->connect_clicked(LINK(this, ScCondFormatManagerDlg, EditBtnClickHdl));
+ m_xBtnAdd->connect_clicked(LINK(this, ScCondFormatManagerDlg, AddBtnHdl));
+ m_xTreeView->connect_row_activated(LINK(this, ScCondFormatManagerDlg, EditBtnHdl));
+
+ SvtViewOptions aDlgOpt(EViewType::Dialog, "CondFormatDialog");
+ if (aDlgOpt.Exists())
+ m_xDialog->set_window_state(aDlgOpt.GetWindowState());
+
+ UpdateButtonSensitivity();
+}
+
+ScCondFormatManagerDlg::~ScCondFormatManagerDlg()
+{
+ // tdf#101285 - Remember position of dialog
+ SvtViewOptions aDlgOpt(EViewType::Dialog, "CondFormatDialog");
+ aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::Pos));
+}
+
+std::unique_ptr<ScConditionalFormatList> ScCondFormatManagerDlg::GetConditionalFormatList()
+{
+ return std::move(m_xFormatList);
+}
+
+void ScCondFormatManagerDlg::UpdateButtonSensitivity()
+{
+ bool bNewSensitivity = !m_xFormatList->empty();
+ m_xBtnRemove->set_sensitive(bNewSensitivity);
+ m_xBtnEdit->set_sensitive(bNewSensitivity);
+}
+
+// Get the current conditional format selected.
+//
+ScConditionalFormat* ScCondFormatManagerDlg::GetCondFormatSelected()
+{
+ return m_xCtrlManager->GetSelection();
+}
+
+IMPL_LINK_NOARG(ScCondFormatManagerDlg, RemoveBtnHdl, weld::Button&, void)
+{
+ m_xCtrlManager->DeleteSelection();
+ m_bModified = true;
+ UpdateButtonSensitivity();
+}
+
+IMPL_LINK_NOARG(ScCondFormatManagerDlg, EditBtnClickHdl, weld::Button&, void)
+{
+ EditBtnHdl(*m_xTreeView);
+}
+
+IMPL_LINK_NOARG(ScCondFormatManagerDlg, EditBtnHdl, weld::TreeView&, bool)
+{
+ ScConditionalFormat* pFormat = m_xCtrlManager->GetSelection();
+
+ if (!pFormat)
+ return true;
+
+ m_bModified = true;
+ m_xDialog->response( DLG_RET_EDIT );
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ScCondFormatManagerDlg, AddBtnHdl, weld::Button&, void)
+{
+ m_bModified = true;
+ m_xDialog->response( DLG_RET_ADD );
+}
+
+void ScCondFormatManagerDlg::SetModified()
+{
+ m_bModified = true;
+ UpdateButtonSensitivity();
+}
+
+bool ScCondFormatManagerDlg::CondFormatsChanged() const
+{
+ return m_bModified;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/csvdataprovider.cxx b/sc/source/ui/dataprovider/csvdataprovider.cxx
new file mode 100644
index 0000000000..11051aab3f
--- /dev/null
+++ b/sc/source/ui/dataprovider/csvdataprovider.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/.
+ */
+
+#include <dataprovider.hxx>
+#include <datatransformation.hxx>
+#include <datamapper.hxx>
+#include <stringutil.hxx>
+
+#include <tools/stream.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <docsh.hxx>
+#include <orcus/csv_parser.hpp>
+
+namespace {
+
+class CSVHandler
+{
+ ScDocument* mpDoc;
+ SCCOL mnCol;
+ SCROW mnRow;
+
+public:
+ CSVHandler(ScDocument* pDoc) :
+ mpDoc(pDoc), mnCol(0), mnRow(0)
+ {
+ }
+
+ static void begin_parse() {}
+ static void end_parse() {}
+ static void begin_row() {}
+ void end_row()
+ {
+ ++mnRow;
+ mnCol = 0;
+ }
+
+ void cell(std::string_view s, bool /*transient*/)
+ {
+ if (mnCol > mpDoc->MaxCol())
+ return;
+
+ double mfValue = 0.0;
+ if (ScStringUtil::parseSimpleNumber(s.data(), s.size(), '.', ',', mfValue))
+ {
+ mpDoc->SetValue(mnCol, mnRow, 0, mfValue);
+ }
+ else
+ {
+ mpDoc->SetString(mnCol, mnRow, 0, OStringToOUString(s, RTL_TEXTENCODING_UTF8));
+ }
+
+ ++mnCol;
+ }
+};
+
+}
+
+namespace sc {
+CSVFetchThread::CSVFetchThread(
+ ScDocument& rDoc, OUString aURL, std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rDataTransformations)
+ : Thread("CSV Fetch Thread")
+ , mrDocument(rDoc)
+ , maURL(std::move(aURL))
+ , mbTerminate(false)
+ , maDataTransformations(std::move(rDataTransformations))
+ , maImportFinishedHdl(std::move(aImportFinishedHdl))
+{
+ maConfig.delimiters.push_back(',');
+ maConfig.text_qualifier = '"';
+}
+
+CSVFetchThread::~CSVFetchThread()
+{
+}
+
+bool CSVFetchThread::IsRequestedTerminate()
+{
+ return mbTerminate.load();
+}
+
+void CSVFetchThread::RequestTerminate()
+{
+ mbTerminate.store(true);
+}
+
+void CSVFetchThread::EndThread()
+{
+ RequestTerminate();
+}
+
+void CSVFetchThread::execute()
+{
+ OStringBuffer aBuffer(64000);
+ DataProvider::FetchStreamFromURL(maURL, aBuffer);
+ if (mbTerminate)
+ return;
+
+ CSVHandler aHdl(&mrDocument);
+ orcus::csv_parser<CSVHandler> parser(aBuffer, aHdl, maConfig);
+ parser.parse();
+
+ for (const auto& itr : maDataTransformations)
+ {
+ itr->Transform(mrDocument);
+ }
+
+ SolarMutexGuard aGuard;
+ maImportFinishedHdl();
+}
+
+CSVDataProvider::CSVDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource):
+ DataProvider(rDataSource),
+ mpDocument(pDoc)
+{
+}
+
+CSVDataProvider::~CSVDataProvider()
+{
+ if (mxCSVFetchThread.is())
+ {
+ SolarMutexReleaser aReleaser;
+ mxCSVFetchThread->join();
+ }
+}
+
+void CSVDataProvider::Import()
+{
+ // already importing data
+ if (mpDoc)
+ return;
+
+ mpDoc.reset(new ScDocument(SCDOCMODE_CLIP));
+ mpDoc->ResetClip(mpDocument, SCTAB(0));
+ mxCSVFetchThread = new CSVFetchThread(*mpDoc, mrDataSource.getURL(), std::bind(&CSVDataProvider::ImportFinished, this), std::vector(mrDataSource.getDataTransformation()));
+ mxCSVFetchThread->launch();
+
+ if (mbDeterministic)
+ {
+ SolarMutexReleaser aReleaser;
+ mxCSVFetchThread->join();
+ }
+}
+
+void CSVDataProvider::ImportFinished()
+{
+ mrDataSource.getDBManager()->WriteToDoc(*mpDoc);
+ mpDoc.reset();
+ Refresh();
+}
+
+void CSVDataProvider::Refresh()
+{
+ ScDocShell* pDocShell = mpDocument->GetDocumentShell();
+ if (pDocShell)
+ pDocShell->SetDocumentModified();
+}
+
+const OUString& CSVDataProvider::GetURL() const
+{
+ return mrDataSource.getURL();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/dataprovider.cxx b/sc/source/ui/dataprovider/dataprovider.cxx
new file mode 100644
index 0000000000..dced70cca0
--- /dev/null
+++ b/sc/source/ui/dataprovider/dataprovider.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/.
+ */
+
+#include <dataprovider.hxx>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <o3tl/string_view.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <tools/stream.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include "htmldataprovider.hxx"
+#include "xmldataprovider.hxx"
+#include "sqldataprovider.hxx"
+#include <datamapper.hxx>
+#include <dbdata.hxx>
+#include <docsh.hxx>
+#include <utility>
+
+using namespace com::sun::star;
+
+namespace sc {
+
+std::unique_ptr<SvStream> DataProvider::FetchStreamFromURL(const OUString& rURL, OStringBuffer& rBuffer)
+{
+ try
+ {
+ uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() );
+
+ uno::Reference< io::XInputStream > xStream = xFileAccess->openFileRead( rURL );
+
+ const sal_Int32 BUF_LEN = 8000;
+ uno::Sequence< sal_Int8 > buffer( BUF_LEN );
+
+ sal_Int32 nRead = 0;
+ while ( ( nRead = xStream->readBytes( buffer, BUF_LEN ) ) == BUF_LEN )
+ {
+ rBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
+ }
+
+ if ( nRead > 0 )
+ {
+ rBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
+ }
+
+ xStream->closeInput();
+
+ SvStream* pStream = new SvMemoryStream(const_cast<char*>(rBuffer.getStr()), rBuffer.getLength(), StreamMode::READ);
+ return std::unique_ptr<SvStream>(pStream);
+ }
+ catch(...)
+ {
+ rBuffer.setLength(0);
+ return nullptr;
+ }
+}
+
+ExternalDataSource::ExternalDataSource(OUString aURL,
+ OUString aProvider, ScDocument* pDoc)
+ : maURL(std::move(aURL))
+ , maProvider(std::move(aProvider))
+ , mpDoc(pDoc)
+{
+}
+
+void ExternalDataSource::setID(const OUString& rID)
+{
+ maID = rID;
+}
+
+void ExternalDataSource::setXMLImportParam(const ScOrcusImportXMLParam& rParam)
+{
+ maParam = rParam;
+}
+
+
+
+void ExternalDataSource::setURL(const OUString& rURL)
+{
+ maURL = rURL;
+}
+
+void ExternalDataSource::setProvider(const OUString& rProvider)
+{
+ maProvider = rProvider;
+ mpDataProvider.reset();
+}
+
+const OUString& ExternalDataSource::getURL() const
+{
+ return maURL;
+}
+
+const OUString& ExternalDataSource::getProvider() const
+{
+ return maProvider;
+}
+
+const OUString& ExternalDataSource::getID() const
+{
+ return maID;
+}
+
+const ScOrcusImportXMLParam& ExternalDataSource::getXMLImportParam() const
+{
+ return maParam;
+}
+
+OUString ExternalDataSource::getDBName() const
+{
+ if (mpDBDataManager)
+ {
+ ScDBData* pDBData = mpDBDataManager->getDBData();
+ if (pDBData)
+ return pDBData->GetName();
+ }
+ return OUString();
+}
+
+void ExternalDataSource::setDBData(const OUString& rDBName)
+{
+ if (!mpDBDataManager)
+ {
+ mpDBDataManager = std::make_shared<ScDBDataManager>(rDBName, mpDoc);
+ }
+ else
+ {
+ mpDBDataManager->SetDatabase(rDBName);
+ }
+}
+
+double ExternalDataSource::getUpdateFrequency()
+{
+ return 0;
+}
+
+ScDBDataManager* ExternalDataSource::getDBManager()
+{
+ return mpDBDataManager.get();
+}
+
+void ExternalDataSource::refresh(ScDocument* pDoc, bool bDeterministic)
+{
+ // no DB data available
+ if (!mpDBDataManager)
+ return;
+
+ // if no data provider exists, try to create one
+ if (!mpDataProvider)
+ mpDataProvider = DataProviderFactory::getDataProvider(pDoc, *this);
+
+ // if we still have not been able to create one, we can not refresh the data
+ if (!mpDataProvider)
+ return;
+
+ if (bDeterministic)
+ mpDataProvider->setDeterministic();
+
+ mpDataProvider->Import();
+}
+
+void ExternalDataSource::AddDataTransformation(
+ const std::shared_ptr<sc::DataTransformation>& mpDataTransformation)
+{
+ maDataTransformations.push_back(mpDataTransformation);
+}
+
+const std::vector<std::shared_ptr<sc::DataTransformation>>& ExternalDataSource::getDataTransformation() const
+{
+ return maDataTransformations;
+}
+
+ExternalDataMapper::ExternalDataMapper(ScDocument& /*rDoc*/)
+ //mrDoc(rDoc)
+{
+}
+
+ExternalDataMapper::~ExternalDataMapper()
+{
+}
+
+void ExternalDataMapper::insertDataSource(const sc::ExternalDataSource& rSource)
+{
+ maDataSources.push_back(rSource);
+}
+
+const std::vector<sc::ExternalDataSource>& ExternalDataMapper::getDataSources() const
+{
+ return maDataSources;
+}
+
+std::vector<sc::ExternalDataSource>& ExternalDataMapper::getDataSources()
+{
+ return maDataSources;
+}
+
+DataProvider::DataProvider(sc::ExternalDataSource& rDataSource):
+ mbDeterministic(false),
+ mrDataSource(rDataSource)
+{
+}
+
+void DataProvider::setDeterministic()
+{
+ mbDeterministic = true;
+}
+
+DataProvider::~DataProvider()
+{
+}
+
+void ScDBDataManager::WriteToDoc(ScDocument& rDoc)
+{
+ // first apply all data transformations
+
+ bool bShrunk = false;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = rDoc.MaxCol();
+ SCROW nEndRow = rDoc.MaxRow();
+ rDoc.ShrinkToUsedDataArea(bShrunk, 0, nStartCol, nStartRow, nEndCol, nEndRow, false, true, true);
+ ScRange aClipRange(nStartCol, nStartRow, 0, nEndCol, nEndRow, 0);
+ rDoc.SetClipArea(aClipRange);
+
+ ScRange aDestRange;
+ getDBData()->GetArea(aDestRange);
+ SCCOL nColSize = std::min<SCCOL>(aDestRange.aEnd.Col() - aDestRange.aStart.Col(), nEndCol);
+ aDestRange.aEnd.SetCol(aDestRange.aStart.Col() + nColSize);
+
+ SCROW nRowSize = std::min<SCROW>(aDestRange.aEnd.Row() - aDestRange.aStart.Row(), nEndRow);
+ aDestRange.aEnd.SetRow(aDestRange.aStart.Row() + nRowSize);
+
+ ScMarkData aMark(mpDoc->GetSheetLimits());
+ aMark.SelectTable(0, true);
+ mpDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &rDoc);
+ ScDocShell* pDocShell = mpDoc->GetDocumentShell();
+ if (pDocShell)
+ pDocShell->PostPaint(aDestRange, PaintPartFlags::All);
+}
+
+ScDBDataManager::ScDBDataManager(OUString aDBName, ScDocument* pDoc):
+ maDBName(std::move(aDBName)),
+ mpDoc(pDoc)
+{
+}
+
+ScDBDataManager::~ScDBDataManager()
+{
+}
+
+void ScDBDataManager::SetDatabase(const OUString& rDBName)
+{
+ maDBName = rDBName;
+}
+
+ScDBData* ScDBDataManager::getDBData()
+{
+ ScDBData* pDBData = mpDoc->GetDBCollection()->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(maDBName));
+ return pDBData;
+}
+
+bool DataProviderFactory::isInternalDataProvider(std::u16string_view rProvider)
+{
+ return o3tl::starts_with(rProvider, u"org.libreoffice.calc");
+}
+
+std::shared_ptr<DataProvider> DataProviderFactory::getDataProvider(ScDocument* pDoc,
+ sc::ExternalDataSource& rDataSource)
+{
+ const OUString& rDataProvider = rDataSource.getProvider();
+ bool bInternal = DataProviderFactory::isInternalDataProvider(rDataProvider);
+ if (bInternal)
+ {
+ if (rDataProvider == "org.libreoffice.calc.csv")
+ return std::make_shared<CSVDataProvider>(pDoc, rDataSource);
+ else if (rDataProvider == "org.libreoffice.calc.html")
+ return std::make_shared<HTMLDataProvider>(pDoc, rDataSource);
+ else if (rDataProvider == "org.libreoffice.calc.xml")
+ return std::make_shared<XMLDataProvider>(pDoc, rDataSource);
+ else if (rDataProvider == "org.libreoffice.calc.sql")
+ return std::make_shared<SQLDataProvider>(pDoc, rDataSource);
+ }
+ else
+ {
+ SAL_WARN("sc", "no external data provider supported yet");
+ return std::shared_ptr<DataProvider>();
+ }
+
+ return std::shared_ptr<DataProvider>();
+}
+
+std::vector<OUString> DataProviderFactory::getDataProviders()
+{
+ std::vector<OUString> aDataProviders;
+ aDataProviders.emplace_back("org.libreoffice.calc.csv");
+ aDataProviders.emplace_back("org.libreoffice.calc.html");
+ aDataProviders.emplace_back("org.libreoffice.calc.xml");
+ aDataProviders.emplace_back("org.libreoffice.calc.sql");
+
+ return aDataProviders;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/datatransformation.cxx b/sc/source/ui/dataprovider/datatransformation.cxx
new file mode 100644
index 0000000000..9fa4c483a4
--- /dev/null
+++ b/sc/source/ui/dataprovider/datatransformation.cxx
@@ -0,0 +1,1276 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <datatransformation.hxx>
+#include <limits>
+#include <document.hxx>
+#include <rtl/math.hxx>
+#include <cmath>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <unotools/charclass.hxx>
+#include <utility>
+
+namespace {
+
+Date getDate(double nDateTime, const SvNumberFormatter* pFormatter)
+{
+ Date aDate = pFormatter->GetNullDate();
+ aDate.AddDays(static_cast<sal_Int32>(::rtl::math::approxFloor(nDateTime)));
+ return aDate;
+}
+}
+
+namespace sc {
+DataTransformation::~DataTransformation()
+{
+}
+
+SCROW DataTransformation::getLastRow(const ScDocument& rDoc, SCCOL nCol)
+{
+ SCROW nEndRow = rDoc.MaxRow();
+
+ return rDoc.GetLastDataRow(0, nCol, nCol, nEndRow);
+}
+
+ColumnRemoveTransformation::ColumnRemoveTransformation(std::set<SCCOL>&& rColumns):
+ maColumns(std::move(rColumns))
+{
+}
+
+ColumnRemoveTransformation::~ColumnRemoveTransformation()
+{
+}
+
+void ColumnRemoveTransformation::Transform(ScDocument& rDoc) const
+{
+ sal_Int32 nIncrementIndex = 0;
+ for (auto& rCol : maColumns)
+ {
+ rDoc.DeleteCol(0, 0, rDoc.MaxRow(), 0, rCol - nIncrementIndex, 1);
+ nIncrementIndex++;
+ }
+}
+
+TransformationType ColumnRemoveTransformation::getTransformationType() const
+{
+ return TransformationType::DELETE_TRANSFORMATION;
+}
+
+const std::set<SCCOL> & ColumnRemoveTransformation::getColumns() const
+{
+ return maColumns;
+}
+
+SplitColumnTransformation::SplitColumnTransformation(SCCOL nCol, sal_Unicode cSeparator):
+ mnCol(nCol),
+ mcSeparator(cSeparator)
+{
+}
+
+void SplitColumnTransformation::Transform(ScDocument& rDoc) const
+{
+ if (mnCol == -1)
+ return;
+
+ rDoc.InsertCol(0, 0, rDoc.MaxRow(), 0, mnCol + 1, 1);
+
+ SCROW nEndRow = getLastRow(rDoc, mnCol);
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(mnCol, nRow, 0);
+ if (eType == CELLTYPE_STRING)
+ {
+ OUString aStr = rDoc.GetString(mnCol, nRow, 0);
+ sal_Int32 nIndex = aStr.indexOf(mcSeparator);
+ if (nIndex != -1)
+ {
+ rDoc.SetString(mnCol + 1, nRow, 0, aStr.copy(nIndex + 1));
+ rDoc.SetString(mnCol, nRow, 0, aStr.copy(0, nIndex));
+ }
+ }
+ }
+}
+
+TransformationType SplitColumnTransformation::getTransformationType() const
+{
+ return TransformationType::SPLIT_TRANSFORMATION;
+}
+
+SCCOL SplitColumnTransformation::getColumn() const
+{
+ return mnCol;
+}
+
+sal_Unicode SplitColumnTransformation::getSeparator() const
+{
+ return mcSeparator;
+}
+
+MergeColumnTransformation::MergeColumnTransformation( std::set<SCCOL>&& rColumns, OUString aMergeString):
+ maColumns(std::move(rColumns)),
+ maMergeString(std::move(aMergeString))
+{
+}
+
+void MergeColumnTransformation::Transform(ScDocument& rDoc) const
+{
+ if (maColumns.empty())
+ return;
+
+ SCROW nMaxRow = 0;
+ for (auto& itr : maColumns)
+ {
+ nMaxRow = getLastRow(rDoc, itr);
+ }
+ assert(nMaxRow != -1);
+
+ SCCOL nTargetCol = *maColumns.begin();
+
+
+ for (SCROW nRow = 0; nRow <= nMaxRow; ++nRow)
+ {
+ OUStringBuffer aStr(rDoc.GetString(nTargetCol, nRow, 0));
+ for (auto& itr : maColumns)
+ {
+ if (itr != nTargetCol)
+ {
+ aStr.append(maMergeString + rDoc.GetString(itr, nRow, 0));
+ }
+ }
+ rDoc.SetString(nTargetCol, nRow, 0, aStr.makeStringAndClear());
+ }
+
+ for (auto& itr : maColumns)
+ {
+ if (itr == nTargetCol)
+ continue;
+
+ rDoc.DeleteCol(0, 0, rDoc.MaxRow(), 0, itr, 1);
+ }
+}
+
+TransformationType MergeColumnTransformation::getTransformationType() const
+{
+ return TransformationType::MERGE_TRANSFORMATION;
+}
+
+const OUString & MergeColumnTransformation::getMergeString() const
+{
+ return maMergeString;
+}
+
+const std::set<SCCOL> & MergeColumnTransformation::getColumns() const
+{
+ return maColumns;
+}
+
+SortTransformation::SortTransformation(const ScSortParam& rSortParam):
+ maSortParam(rSortParam)
+{
+}
+
+void SortTransformation::Transform(ScDocument& rDoc) const
+{
+ rDoc.Sort(0, maSortParam, false, false, nullptr, nullptr);
+}
+
+TransformationType SortTransformation::getTransformationType() const
+{
+ return TransformationType::SORT_TRANSFORMATION;
+}
+
+const ScSortParam & SortTransformation::getSortParam() const
+{
+ return maSortParam;
+}
+
+TextTransformation::TextTransformation( std::set<SCCOL>&& nCol, const TEXT_TRANSFORM_TYPE rType):
+ mnCol(std::move(nCol)),
+ maType(rType)
+{
+}
+
+void TextTransformation::Transform(ScDocument& rDoc) const
+{
+ SCROW nEndRow = 0;
+ for(auto& rCol : mnCol)
+ {
+ nEndRow = getLastRow(rDoc, rCol);
+ }
+ assert(nEndRow != -1);
+
+ for(auto& rCol : mnCol)
+ {
+ switch (maType)
+ {
+ case TEXT_TRANSFORM_TYPE::TO_LOWER:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_STRING)
+ {
+ OUString aStr = rDoc.GetString(rCol, nRow, 0);
+ rDoc.SetString(rCol, nRow, 0, ScGlobal::getCharClass().lowercase(aStr));
+ }
+ }
+ }
+ break;
+ case TEXT_TRANSFORM_TYPE::TO_UPPER:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_STRING)
+ {
+ OUString aStr = rDoc.GetString(rCol, nRow, 0);
+ rDoc.SetString(rCol, nRow, 0, ScGlobal::getCharClass().uppercase(aStr));
+ }
+ }
+ }
+ break;
+ case TEXT_TRANSFORM_TYPE::CAPITALIZE:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_STRING)
+ {
+ OUString aStr = rDoc.GetString(rCol, nRow, 0);
+
+ sal_Int32 length = aStr.getLength();
+
+ if(length != 0)
+ aStr = aStr.replaceAt(0, 1, ScGlobal::getCharClass().uppercase(OUString(aStr[0])));
+
+ for (sal_Int32 i = 1; i < length; i++){
+ if (aStr[i-1] == sal_Unicode(U' '))
+ {
+ aStr = aStr.replaceAt(i, 1, ScGlobal::getCharClass().uppercase(OUString(aStr[i])));
+ }
+ else
+ {
+ aStr = aStr.replaceAt(i, 1, ScGlobal::getCharClass().lowercase(OUString(aStr[i])));
+ }
+ }
+ rDoc.SetString(rCol, nRow, 0, aStr);
+ }
+ }
+ }
+ break;
+ case TEXT_TRANSFORM_TYPE::TRIM:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_STRING)
+ {
+ OUString aStr = rDoc.GetString(rCol, nRow, 0);
+ rDoc.SetString(rCol, nRow, 0, aStr.trim());
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+TransformationType TextTransformation::getTransformationType() const
+{
+ return TransformationType::TEXT_TRANSFORMATION;
+}
+
+TEXT_TRANSFORM_TYPE TextTransformation::getTextTransformationType() const
+{
+ return maType;
+}
+
+const std::set<SCCOL>& TextTransformation::getColumns() const
+{
+ return mnCol;
+}
+
+AggregateFunction::AggregateFunction(std::set<SCCOL>&& rColumns, const AGGREGATE_FUNCTION rType):
+ maColumns(std::move(rColumns)),
+ maType(rType)
+{
+}
+
+void AggregateFunction::Transform(ScDocument& rDoc) const
+{
+ SCROW nEndRow = 0;
+ for (auto& itr : maColumns)
+ {
+ nEndRow = getLastRow(rDoc, itr);
+ }
+ assert(nEndRow != -1);
+
+ for (auto& rCol : maColumns)
+ {
+ switch (maType)
+ {
+ case AGGREGATE_FUNCTION::SUM:
+ {
+ double nSum = 0;
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ nSum += nVal;
+ }
+ }
+ rDoc.SetValue(rCol, nEndRow + 1, 0, nSum);
+ }
+ break;
+ case AGGREGATE_FUNCTION::AVERAGE:
+ {
+ double nSum = 0;
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ nSum += nVal;
+ }
+ }
+
+ double nAvg = nSum / (nEndRow + 1);
+ rDoc.SetValue(rCol, nEndRow + 1, 0, nAvg);
+ }
+ break;
+ case AGGREGATE_FUNCTION::MIN:
+ {
+ double nMin = std::numeric_limits<double>::max();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if(nVal < nMin)
+ nMin = nVal;
+ }
+ }
+ rDoc.SetValue(rCol, nEndRow + 1, 0, nMin);
+ }
+ break;
+ case AGGREGATE_FUNCTION::MAX:
+ {
+ double nMax = std::numeric_limits<double>::lowest();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if(nMax < nVal)
+ nMax = nVal;
+ }
+ }
+ rDoc.SetValue(rCol, nEndRow + 1, 0, nMax);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+TransformationType AggregateFunction::getTransformationType() const
+{
+ return TransformationType::AGGREGATE_FUNCTION;
+}
+
+AGGREGATE_FUNCTION AggregateFunction::getAggregateType() const
+{
+ return maType;
+}
+
+const std::set<SCCOL>& AggregateFunction::getColumns() const
+{
+ return maColumns;
+}
+
+NumberTransformation::NumberTransformation(std::set<SCCOL>&& nCol,
+ const NUMBER_TRANSFORM_TYPE rType)
+ : mnCol(std::move(nCol))
+ , maType(rType)
+ , maPrecision(-1)
+{
+}
+
+NumberTransformation::NumberTransformation(std::set<SCCOL>&& nCol,
+ const NUMBER_TRANSFORM_TYPE rType, int nPrecision)
+ : mnCol(std::move(nCol))
+ , maType(rType)
+ , maPrecision(nPrecision)
+{
+}
+
+void NumberTransformation::Transform(ScDocument& rDoc) const
+{
+ SCROW nEndRow = 0;
+ for(auto& rCol : mnCol)
+ {
+ nEndRow = getLastRow(rDoc, rCol);
+ }
+ assert(nEndRow != -1);
+
+ for(auto& rCol : mnCol)
+ {
+ switch (maType)
+ {
+ case NUMBER_TRANSFORM_TYPE::ROUND:
+ {
+ if(maPrecision > -1)
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, rtl::math::round(nVal, maPrecision));
+ }
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::ROUND_UP:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, rtl::math::approxCeil(nVal));
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::ROUND_DOWN:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, rtl::math::approxFloor(nVal));
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::ABSOLUTE:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if(std::signbit(nVal))
+ rDoc.SetValue(rCol, nRow, 0, -1 * nVal);
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::LOG_E:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if (nVal > 0)
+ {
+ rDoc.SetValue(rCol, nRow, 0, std::log1p(nVal-1));
+ }
+ else
+ {
+ rDoc.SetString(rCol, nRow, 0, OUString());
+ }
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::LOG_10:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if (nVal > 0)
+ {
+ rDoc.SetValue(rCol, nRow, 0, log10(nVal));
+ }
+ else
+ {
+ rDoc.SetString(rCol, nRow, 0, OUString());
+ }
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::CUBE:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, nVal * nVal * nVal);
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::SQUARE:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, nVal * nVal);
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::SQUARE_ROOT:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if (!std::signbit(nVal))
+ {
+ rDoc.SetValue(rCol, nRow, 0, sqrt(nVal));
+ }
+ else
+ {
+ rDoc.SetString(rCol, nRow, 0, OUString());
+ }
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::IS_EVEN:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if (fmod(nVal, 1) == 0 && fmod(nVal, 2) == 0)
+ rDoc.SetValue(rCol, nRow, 0, 1);
+ else
+ rDoc.SetValue(rCol, nRow, 0, 0);
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::IS_ODD:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if (fmod(nVal, 1) == 0 && fmod(nVal, 2) != 0)
+ rDoc.SetValue(rCol, nRow, 0, 1);
+ else
+ rDoc.SetValue(rCol, nRow, 0, 0);
+ }
+ }
+ }
+ break;
+ case NUMBER_TRANSFORM_TYPE::SIGN:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ if (nVal > 0)
+ rDoc.SetValue(rCol, nRow, 0, 1);
+ else if (nVal < 0)
+ rDoc.SetValue(rCol, nRow, 0, -1);
+ else
+ rDoc.SetValue(rCol, nRow, 0, 0);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+TransformationType NumberTransformation::getTransformationType() const
+{
+ return TransformationType::NUMBER_TRANSFORMATION;
+}
+
+NUMBER_TRANSFORM_TYPE NumberTransformation::getNumberTransformationType() const
+{
+ return maType;
+}
+
+int NumberTransformation::getPrecision() const
+{
+ return maPrecision;
+}
+
+const std::set<SCCOL>& NumberTransformation::getColumn() const
+{
+ return mnCol;
+}
+
+ReplaceNullTransformation::ReplaceNullTransformation(std::set<SCCOL>&& nCol,
+ OUString sReplaceWith)
+ : mnCol(std::move(nCol))
+ , msReplaceWith(std::move(sReplaceWith))
+{
+}
+
+void ReplaceNullTransformation::Transform(ScDocument& rDoc) const
+{
+ if (mnCol.empty())
+ return;
+
+ for(auto& rCol : mnCol)
+ {
+ SCROW nEndRow = getLastRow(rDoc, rCol);
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_NONE)
+ {
+ // OUString aStr = rDoc.GetString(rCol, nRow, 0);
+ // if (aStr == "" || aStr.isEmpty())
+ rDoc.SetString(rCol, nRow, 0, msReplaceWith);
+ }
+ }
+ }
+
+}
+
+const std::set<SCCOL>& ReplaceNullTransformation::getColumn() const
+{
+ return mnCol;
+}
+
+const OUString& ReplaceNullTransformation::getReplaceString() const
+{
+ return msReplaceWith;
+}
+
+TransformationType ReplaceNullTransformation::getTransformationType() const
+{
+ return TransformationType::REMOVE_NULL_TRANSFORMATION;
+}
+
+DateTimeTransformation::DateTimeTransformation(std::set<SCCOL>&& nCol,
+ const DATETIME_TRANSFORMATION_TYPE rType)
+ : mnCol(std::move(nCol))
+ , maType(rType)
+{
+}
+
+void DateTimeTransformation::Transform(ScDocument& rDoc) const
+{
+ SCROW nEndRow = 0;
+ for(auto& rCol : mnCol)
+ {
+ nEndRow = getLastRow(rDoc, rCol);
+ }
+ assert(nEndRow != -1);
+
+ for(auto& rCol : mnCol)
+ {
+ switch (maType)
+ {
+ case DATETIME_TRANSFORMATION_TYPE::DATE_STRING:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ ScAddress aAddress(rCol, nRow, 0);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::YEAR:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ rDoc.SetValue(rCol, nRow, 0, aDate.GetYear());
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ aDate.SetDay(1);
+ aDate.SetMonth(1);
+ nVal = aDate - pFormatter->GetNullDate();
+ ScAddress aAddress(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ aDate.SetMonth(12);
+ aDate.SetDay(31);
+ nVal = aDate - pFormatter->GetNullDate();
+ ScAddress aAddress(rCol, nRow, 0);
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::MONTH:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ rDoc.SetValue(rCol, nRow, 0, aDate.GetMonth());
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::MONTH_NAME:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ const Color* pColor = nullptr;
+ OUString aResult;
+ pFormatter->GetPreviewStringGuess("MMMM", nVal, aResult, &pColor, eLanguage);
+ rDoc.SetString(rCol, nRow, 0, aResult);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ ScAddress aAddress(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ aDate.SetDay(1);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ ScAddress aAddress(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ aDate.SetDay(aDate.GetDaysInMonth());
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::DAY:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ rDoc.SetValue(rCol, nRow, 0, aDate.GetDay());
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ rDoc.SetValue(rCol, nRow, 0, aDate.GetDayOfWeek());
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ rDoc.SetValue(rCol, nRow, 0, aDate.GetDayOfYear());
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::QUARTER:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+
+ int nMonth = aDate.GetMonth();
+
+ if(nMonth >= 1 && nMonth <=3)
+ rDoc.SetValue(rCol, nRow, 0, 1);
+
+ else if(nMonth >= 4 && nMonth <=6)
+ rDoc.SetValue(rCol, nRow, 0, 2);
+
+ else if(nMonth >= 7 && nMonth <=9)
+ rDoc.SetValue(rCol, nRow, 0, 3);
+
+ else if(nMonth >= 10 && nMonth <=12)
+ rDoc.SetValue(rCol, nRow, 0, 4);
+ else
+ rDoc.SetValue(rCol, nRow, 0, -1);
+
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ ScAddress aAddress(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+
+ int nMonth = aDate.GetMonth();
+
+ if(nMonth >= 1 && nMonth <=3)
+ {
+ aDate.SetDay(1);
+ aDate.SetMonth(1);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ else if(nMonth >= 4 && nMonth <=6)
+ {
+ aDate.SetDay(1);
+ aDate.SetMonth(4);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ else if(nMonth >= 7 && nMonth <=9)
+ {
+ aDate.SetDay(1);
+ aDate.SetMonth(7);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ else if(nMonth >= 10 && nMonth <=12)
+ {
+ aDate.SetDay(1);
+ aDate.SetMonth(10);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ else
+ rDoc.SetValue(rCol, nRow, 0, -1);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::DATE, eLanguage );
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ ScAddress aAddress(rCol, nRow, 0);
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ Date aDate = getDate(nVal, pFormatter);
+ int nMonth = aDate.GetMonth();
+
+ if(nMonth >= 1 && nMonth <=3)
+ {
+ aDate.SetDay(31);
+ aDate.SetMonth(3);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+
+ else if(nMonth >= 4 && nMonth <=6)
+ {
+ aDate.SetDay(30);
+ aDate.SetMonth(6);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+
+ else if(nMonth >= 7 && nMonth <=9)
+ {
+ aDate.SetDay(30);
+ aDate.SetMonth(9);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+
+ else if(nMonth >= 10 && nMonth <=12)
+ {
+ aDate.SetDay(31);
+ aDate.SetMonth(12);
+ nVal = aDate - pFormatter->GetNullDate();
+ rDoc.SetValue(rCol, nRow, 0, nVal);
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ else
+ rDoc.SetValue(rCol, nRow, 0, -1);
+
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::TIME:
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ sal_uInt32 nFormat = pFormatter->GetStandardFormat(SvNumFormatType::TIME, eLanguage);
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ ScAddress aAddress(rCol, nRow, 0);
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ rDoc.SetNumberFormat(aAddress, nFormat);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::HOUR:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( nVal, nHour, nMinute, nSecond, fFractionOfSecond, 0);
+ rDoc.SetValue(rCol, nRow, 0, nHour);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::MINUTE:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( nVal, nHour, nMinute, nSecond, fFractionOfSecond, 0);
+ rDoc.SetValue(rCol, nRow, 0, nMinute);
+ }
+ }
+ }
+ break;
+ case DATETIME_TRANSFORMATION_TYPE::SECOND:
+ {
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(rCol, nRow, 0);
+ if (eType == CELLTYPE_VALUE)
+ {
+ double nVal = rDoc.GetValue(rCol, nRow, 0);
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( nVal, nHour, nMinute, nSecond, fFractionOfSecond, 0);
+ rDoc.SetValue(rCol, nRow, 0, nSecond);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+TransformationType DateTimeTransformation::getTransformationType() const
+{
+ return TransformationType::DATETIME_TRANSFORMATION;
+}
+
+DATETIME_TRANSFORMATION_TYPE DateTimeTransformation::getDateTimeTransformationType() const
+{
+ return maType;
+}
+
+const std::set<SCCOL>& DateTimeTransformation::getColumn() const
+{
+ return mnCol;
+}
+
+FindReplaceTransformation::FindReplaceTransformation(SCCOL nCol, OUString aFindString, OUString aReplaceString)
+ : mnCol(nCol)
+ , maFindString(std::move(aFindString))
+ , maReplaceString(std::move(aReplaceString))
+{
+}
+
+void FindReplaceTransformation::Transform(ScDocument& rDoc) const
+{
+ if (mnCol == -1)
+ return;
+
+ SCROW nEndRow = getLastRow(rDoc, mnCol);
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(mnCol, nRow, 0);
+ if (eType != CELLTYPE_NONE)
+ {
+ OUString aStr = rDoc.GetString(mnCol, nRow, 0);
+ if (aStr == maFindString)
+ rDoc.SetString(mnCol, nRow, 0, maReplaceString);
+ }
+ }
+}
+
+TransformationType FindReplaceTransformation::getTransformationType() const
+{
+ return TransformationType::FINDREPLACE_TRANSFORMATION;
+}
+
+SCCOL FindReplaceTransformation::getColumn() const
+{
+ return mnCol;
+}
+
+const OUString& FindReplaceTransformation::getFindString() const
+{
+ return maFindString;
+}
+
+const OUString& FindReplaceTransformation::getReplaceString() const
+{
+ return maReplaceString;
+}
+
+DeleteRowTransformation::DeleteRowTransformation(SCCOL nCol, OUString aFindString)
+ : mnCol(nCol)
+ , maFindString(std::move(aFindString))
+{
+}
+
+void DeleteRowTransformation::Transform(ScDocument& rDoc) const
+{
+ sal_Int32 nIncrementIndex = 0;
+ if (mnCol == -1)
+ return;
+
+ SCROW nEndRow = getLastRow(rDoc, mnCol);
+ for (SCROW nRow = 0; nRow <= nEndRow; ++nRow)
+ {
+ CellType eType = rDoc.GetCellType(mnCol, nRow - nIncrementIndex, 0);
+ if (eType != CELLTYPE_NONE)
+ {
+ OUString aStr = rDoc.GetString(mnCol, nRow - nIncrementIndex, 0);
+ if (aStr == maFindString)
+ {
+ rDoc.DeleteRow(0, 0, rDoc.MaxCol(), 0, nRow - nIncrementIndex, 1);
+ nIncrementIndex++;
+ }
+ }
+ }
+}
+
+TransformationType DeleteRowTransformation::getTransformationType() const
+{
+ return TransformationType::DELETEROW_TRANSFORMATION;
+}
+
+SCCOL DeleteRowTransformation::getColumn() const
+{
+ return mnCol;
+}
+
+const OUString& DeleteRowTransformation::getFindString() const
+{
+ return maFindString;
+}
+
+SwapRowsTransformation::SwapRowsTransformation(SCROW mRow, SCROW nRow)
+ : mxRow(mRow)
+ , nxRow(nRow)
+{
+}
+
+void SwapRowsTransformation::Transform(ScDocument& rDoc) const
+{
+ if (mxRow == -1 || nxRow == -1)
+ return;
+
+ for (SCCOL nCol = 0; nCol <= rDoc.MaxCol(); ++nCol)
+ {
+ CellType aType = rDoc.GetCellType(nCol, mxRow, 0);
+ if (aType == CELLTYPE_STRING)
+ {
+ OUString aStr = rDoc.GetString(nCol, mxRow, 0);
+ OUString bStr = rDoc.GetString(nCol, nxRow, 0);
+ rDoc.SetString(nCol, mxRow, 0, bStr);
+ rDoc.SetString(nCol, nxRow, 0, aStr);
+ }
+ else if (aType == CELLTYPE_VALUE)
+ {
+ double aVal = rDoc.GetValue(nCol, mxRow, 0);
+ double bVal = rDoc.GetValue(nCol, nxRow, 0);
+ rDoc.SetValue(nCol, mxRow, 0, bVal);
+ rDoc.SetValue(nCol, nxRow, 0, aVal);
+ }
+ }
+}
+
+TransformationType SwapRowsTransformation::getTransformationType() const
+{
+ return TransformationType::SWAPROWS_TRANSFORMATION;
+}
+
+SCROW SwapRowsTransformation::getFirstRow() const
+{
+ return mxRow;
+}
+
+SCROW SwapRowsTransformation::getSecondRow() const
+{
+ return nxRow;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/htmldataprovider.cxx b/sc/source/ui/dataprovider/htmldataprovider.cxx
new file mode 100644
index 0000000000..3f3300d320
--- /dev/null
+++ b/sc/source/ui/dataprovider/htmldataprovider.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "htmldataprovider.hxx"
+#include <datamapper.hxx>
+#include <datatransformation.hxx>
+#include <salhelper/thread.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <tools/stream.hxx>
+
+#include <libxml/HTMLparser.h>
+
+#include <libxml/xpath.h>
+
+#include <comphelper/string.hxx>
+
+namespace sc {
+
+class HTMLFetchThread : public salhelper::Thread
+{
+ ScDocument& mrDocument;
+ OUString maURL;
+ OUString maID;
+ const std::vector<std::shared_ptr<sc::DataTransformation>> maDataTransformations;
+ std::function<void()> maImportFinishedHdl;
+
+ void handleTable(xmlNodePtr pTable);
+ void handleRow(xmlNodePtr pRow, SCROW nRow);
+ void skipHeadBody(xmlNodePtr pSkip, SCROW& rRow);
+ void handleCell(xmlNodePtr pCell, SCROW nRow, SCCOL nCol);
+
+public:
+ HTMLFetchThread(ScDocument& rDoc, const OUString&, const OUString& rID, std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rTransformations);
+
+ virtual void execute() override;
+};
+
+HTMLFetchThread::HTMLFetchThread(
+ ScDocument& rDoc, const OUString& rURL, const OUString& rID,
+ std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rTransformations)
+ : salhelper::Thread("HTML Fetch Thread")
+ , mrDocument(rDoc)
+ , maURL(rURL)
+ , maID(rID)
+ , maDataTransformations(std::move(rTransformations))
+ , maImportFinishedHdl(std::move(aImportFinishedHdl))
+{
+}
+
+namespace {
+
+OString toString(const xmlChar* pStr)
+{
+ return OString(reinterpret_cast<const char*>(pStr), xmlStrlen(pStr));
+}
+
+OUString trim_string(const OUString& aStr)
+{
+ OUString aOldString;
+ OUString aString = aStr;
+ do
+ {
+ aOldString = aString;
+ aString = comphelper::string::strip(aString, ' ');
+ aString = comphelper::string::strip(aString, '\n');
+ aString = comphelper::string::strip(aString, '\r');
+ aString = comphelper::string::strip(aString, '\t');
+ }
+ while (aOldString != aString);
+
+ return aString;
+}
+
+OUString get_node_str(xmlNodePtr pNode)
+{
+ OUStringBuffer aStr;
+ for (xmlNodePtr cur_node = pNode->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_TEXT_NODE)
+ {
+ OUString aString = OStringToOUString(toString(cur_node->content), RTL_TEXTENCODING_UTF8);
+ aStr.append(trim_string(aString));
+ }
+ else if (cur_node->type == XML_ELEMENT_NODE)
+ {
+ aStr.append(get_node_str(cur_node));
+ }
+ }
+
+ return aStr.makeStringAndClear();
+}
+
+}
+
+void HTMLFetchThread::handleCell(xmlNodePtr pCellNode, SCROW nRow, SCCOL nCol)
+{
+ OUStringBuffer aStr;
+ for (xmlNodePtr cur_node = pCellNode->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_TEXT_NODE)
+ {
+ OUString aString = OStringToOUString(toString(cur_node->content), RTL_TEXTENCODING_UTF8);
+ aStr.append(trim_string(aString));
+ }
+ else if (cur_node->type == XML_ELEMENT_NODE)
+ {
+ aStr.append(get_node_str(cur_node));
+ }
+ }
+
+ if (!aStr.isEmpty())
+ {
+ OUString aCellStr = aStr.makeStringAndClear();
+ mrDocument.SetString(nCol, nRow, 0, aCellStr);
+ }
+}
+
+void HTMLFetchThread::handleRow(xmlNodePtr pRowNode, SCROW nRow)
+{
+ sal_Int32 nCol = 0;
+ for (xmlNodePtr cur_node = pRowNode->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE)
+ {
+ OString aNodeName = toString(cur_node->name);
+ if (aNodeName == "td" || aNodeName == "th")
+ {
+ handleCell(cur_node, nRow, nCol);
+ ++nCol;
+ }
+ }
+ }
+}
+
+void HTMLFetchThread::skipHeadBody(xmlNodePtr pSkipElement, SCROW& rRow)
+{
+ for (xmlNodePtr cur_node = pSkipElement->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE)
+ {
+ OString aNodeName = toString(cur_node->name);
+ if (aNodeName == "tr")
+ {
+ handleRow(cur_node, rRow);
+ ++rRow;
+ }
+
+ }
+ }
+}
+
+void HTMLFetchThread::handleTable(xmlNodePtr pTable)
+{
+ sal_Int32 nRow = 0;
+ for (xmlNodePtr cur_node = pTable->children; cur_node; cur_node = cur_node->next)
+ {
+ if (cur_node->type == XML_ELEMENT_NODE)
+ {
+ OString aNodeName = toString(cur_node->name);
+ if (aNodeName == "tr")
+ {
+ handleRow(cur_node, nRow);
+ ++nRow;
+ }
+ else if (aNodeName == "thead" || aNodeName == "tbody")
+ {
+ skipHeadBody(cur_node, nRow);
+ }
+ }
+ }
+}
+
+void HTMLFetchThread::execute()
+{
+ OStringBuffer aBuffer(64000);
+ DataProvider::FetchStreamFromURL(maURL, aBuffer);
+
+ if (aBuffer.isEmpty())
+ return;
+
+ htmlDocPtr pHtmlPtr = htmlParseDoc(reinterpret_cast<xmlChar*>(const_cast<char*>(aBuffer.getStr())), nullptr);
+
+ OString aID = OUStringToOString(maID, RTL_TEXTENCODING_UTF8);
+ xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pHtmlPtr);
+ xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aID.getStr()), pXmlXpathCtx);
+
+ if (!pXmlXpathObj)
+ {
+ xmlXPathFreeContext(pXmlXpathCtx);
+ return;
+ }
+ xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
+
+ if (!pXmlNodes)
+ {
+ xmlXPathFreeNodeSetList(pXmlXpathObj);
+ xmlXPathFreeContext(pXmlXpathCtx);
+ return;
+ }
+
+ if (pXmlNodes->nodeNr == 0)
+ {
+ xmlXPathFreeNodeSet(pXmlNodes);
+ xmlXPathFreeNodeSetList(pXmlXpathObj);
+ xmlXPathFreeContext(pXmlXpathCtx);
+ return;
+ }
+
+ xmlNodePtr pNode = pXmlNodes->nodeTab[0];
+ handleTable(pNode);
+
+ xmlXPathFreeNodeSet(pXmlNodes);
+ xmlXPathFreeNodeSetList(pXmlXpathObj);
+ xmlXPathFreeContext(pXmlXpathCtx);
+
+ for (auto& itr : maDataTransformations)
+ {
+ itr->Transform(mrDocument);
+ }
+
+ SolarMutexGuard aGuard;
+ maImportFinishedHdl();
+}
+
+HTMLDataProvider::HTMLDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource):
+ DataProvider(rDataSource),
+ mpDocument(pDoc)
+{
+}
+
+HTMLDataProvider::~HTMLDataProvider()
+{
+ if (mxHTMLFetchThread.is())
+ {
+ SolarMutexReleaser aReleaser;
+ mxHTMLFetchThread->join();
+ }
+}
+
+void HTMLDataProvider::Import()
+{
+ // already importing data
+ if (mpDoc)
+ return;
+
+ mpDoc.reset(new ScDocument(SCDOCMODE_CLIP));
+ mpDoc->ResetClip(mpDocument, SCTAB(0));
+ mxHTMLFetchThread = new HTMLFetchThread(*mpDoc, mrDataSource.getURL(), mrDataSource.getID(),
+ std::bind(&HTMLDataProvider::ImportFinished, this), std::vector(mrDataSource.getDataTransformation()));
+ mxHTMLFetchThread->launch();
+
+ if (mbDeterministic)
+ {
+ SolarMutexReleaser aReleaser;
+ mxHTMLFetchThread->join();
+ }
+}
+
+void HTMLDataProvider::ImportFinished()
+{
+ mrDataSource.getDBManager()->WriteToDoc(*mpDoc);
+}
+
+const OUString& HTMLDataProvider::GetURL() const
+{
+ return mrDataSource.getURL();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/htmldataprovider.hxx b/sc/source/ui/dataprovider/htmldataprovider.hxx
new file mode 100644
index 0000000000..f978ebdd15
--- /dev/null
+++ b/sc/source/ui/dataprovider/htmldataprovider.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <dataprovider.hxx>
+
+namespace sc
+{
+class HTMLFetchThread;
+
+class HTMLDataProvider : public DataProvider
+{
+private:
+ ScDocument* mpDocument;
+ rtl::Reference<HTMLFetchThread> mxHTMLFetchThread;
+
+ ScDocumentUniquePtr mpDoc;
+
+public:
+ HTMLDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource);
+ virtual ~HTMLDataProvider() override;
+
+ virtual void Import() override;
+
+ virtual const OUString& GetURL() const override;
+
+ void ImportFinished();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/sqldataprovider.cxx b/sc/source/ui/dataprovider/sqldataprovider.cxx
new file mode 100644
index 0000000000..9aed1996c6
--- /dev/null
+++ b/sc/source/ui/dataprovider/sqldataprovider.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/.
+ */
+
+#include "sqldataprovider.hxx"
+#include <datatransformation.hxx>
+#include <salhelper/thread.hxx>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaData.hpp>
+#include <dbdocutl.hxx>
+#include <datamapper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace css;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+
+namespace sc
+{
+class SQLFetchThread : public salhelper::Thread
+{
+ ScDocument& mrDocument;
+ OUString maID;
+ const std::vector<std::shared_ptr<sc::DataTransformation>> maDataTransformations;
+ std::function<void()> maImportFinishedHdl;
+
+public:
+ SQLFetchThread(ScDocument& rDoc, const OUString& rID, std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rTransformations);
+
+ virtual void execute() override;
+};
+
+SQLFetchThread::SQLFetchThread(
+ ScDocument& rDoc, const OUString& rID, std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rTransformations)
+ : salhelper::Thread("SQL Fetch Thread")
+ , mrDocument(rDoc)
+ , maID(rID)
+ , maDataTransformations(std::move(rTransformations))
+ , maImportFinishedHdl(aImportFinishedHdl)
+{
+}
+
+void SQLFetchThread::execute()
+{
+ sal_Int32 nIndex = maID.indexOf("@");
+ if (nIndex == -1)
+ return;
+
+ OUString aTable = maID.copy(0, nIndex);
+ OUString aDatabase = maID.copy(nIndex + 1);
+
+ try
+ {
+ uno::Reference<sdb::XDatabaseContext> xContext
+ = sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
+ uno::Any aSourceAny = xContext->getByName(aDatabase);
+
+ uno::Reference<sdb::XCompletedConnection> xSource(aSourceAny, uno::UNO_QUERY);
+ if (!xSource.is())
+ return;
+
+ uno::Reference<task::XInteractionHandler> xHandler(
+ task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(),
+ nullptr),
+ uno::UNO_QUERY_THROW);
+
+ uno::Reference<sdbc::XConnection> xConnection = xSource->connectWithCompletion(xHandler);
+
+ uno::Reference<sdbc::XStatement> xStatement = xConnection->createStatement();
+
+ uno::Reference<sdbc::XResultSet> xResult
+ = xStatement->executeQuery("SELECT * FROM " + aTable);
+
+ if (xResult.is())
+ {
+ Reference<sdbc::XResultSetMetaDataSupplier> xMetaDataSupplier(xResult, UNO_QUERY);
+
+ Reference<sdbc::XResultSetMetaData> xMetaData = xMetaDataSupplier->getMetaData();
+
+ Reference<XRow> xRow(xResult, UNO_QUERY);
+
+ SCCOL nColCount = static_cast<SCCOL>(xMetaData->getColumnCount());
+
+ while (xResult->next())
+ {
+ SCROW nRow = static_cast<SCROW>(xResult->getRow());
+
+ for (SCCOL nCol = 0; nCol < nColCount; nCol++)
+ {
+ ScDatabaseDocUtil::PutData(mrDocument, nCol, nRow - 1, 0, xRow, nCol + 1,
+ xMetaData->getColumnType(nCol + 1), false);
+ }
+ }
+ }
+ }
+ catch (uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sc", "exception in database");
+ }
+
+ for (auto& itr : maDataTransformations)
+ {
+ itr->Transform(mrDocument);
+ }
+
+ SolarMutexGuard aGuard;
+ maImportFinishedHdl();
+}
+
+SQLDataProvider::SQLDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource)
+ : DataProvider(rDataSource)
+ , mpDocument(pDoc)
+{
+}
+
+SQLDataProvider::~SQLDataProvider()
+{
+ if (mxSQLFetchThread.is())
+ {
+ SolarMutexReleaser aReleaser;
+ mxSQLFetchThread->join();
+ }
+}
+
+void SQLDataProvider::Import()
+{
+ // already importing data
+ if (mpDoc)
+ return;
+
+ mpDoc.reset(new ScDocument(SCDOCMODE_CLIP));
+ mpDoc->ResetClip(mpDocument, SCTAB(0));
+ mxSQLFetchThread = new SQLFetchThread(*mpDoc, mrDataSource.getID(),
+ std::bind(&SQLDataProvider::ImportFinished, this),
+ std::vector(mrDataSource.getDataTransformation()));
+ mxSQLFetchThread->launch();
+
+ if (mbDeterministic)
+ {
+ SolarMutexReleaser aReleaser;
+ mxSQLFetchThread->join();
+ }
+}
+
+void SQLDataProvider::ImportFinished()
+{
+ mrDataSource.getDBManager()->WriteToDoc(*mpDoc);
+ mxSQLFetchThread.clear();
+ mpDoc.reset();
+}
+
+const OUString& SQLDataProvider::GetURL() const { return mrDataSource.getURL(); }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/sqldataprovider.hxx b/sc/source/ui/dataprovider/sqldataprovider.hxx
new file mode 100644
index 0000000000..fbbca76012
--- /dev/null
+++ b/sc/source/ui/dataprovider/sqldataprovider.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <dataprovider.hxx>
+
+namespace sc
+{
+class SQLFetchThread;
+
+class SQLDataProvider : public DataProvider
+{
+private:
+ ScDocument* mpDocument;
+ rtl::Reference<SQLFetchThread> mxSQLFetchThread;
+
+ ScDocumentUniquePtr mpDoc;
+
+public:
+ SQLDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource);
+ virtual ~SQLDataProvider() override;
+
+ virtual void Import() override;
+
+ virtual const OUString& GetURL() const override;
+
+ void ImportFinished();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/xmldataprovider.cxx b/sc/source/ui/dataprovider/xmldataprovider.cxx
new file mode 100644
index 0000000000..464382ecef
--- /dev/null
+++ b/sc/source/ui/dataprovider/xmldataprovider.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "xmldataprovider.hxx"
+#include <datatransformation.hxx>
+#include <salhelper/thread.hxx>
+#include <filter.hxx>
+#include <document.hxx>
+#include <datamapper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <orcusfilters.hxx>
+
+using namespace com::sun::star;
+
+namespace sc
+{
+class XMLFetchThread : public salhelper::Thread
+{
+ ScDocument& mrDocument;
+ OUString maURL;
+ OUString maID;
+ ScOrcusImportXMLParam maParam;
+ std::unique_ptr<ScOrcusXMLContext> mpXMLContext;
+ const std::vector<std::shared_ptr<sc::DataTransformation>> maDataTransformations;
+ std::function<void()> maImportFinishedHdl;
+
+public:
+ XMLFetchThread(ScDocument& rDoc, const OUString&, const ScOrcusImportXMLParam& rParam,
+ const OUString& rID, std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rTransformations);
+ virtual void execute() override;
+};
+
+XMLFetchThread::XMLFetchThread(
+ ScDocument& rDoc, const OUString& rURL, const ScOrcusImportXMLParam& rParam,
+ const OUString& rID, std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& rTransformations)
+ : salhelper::Thread("XML Fetch Thread")
+ , mrDocument(rDoc)
+ , maURL(rURL)
+ , maID(rID)
+ , maParam(rParam)
+ , maDataTransformations(std::move(rTransformations))
+ , maImportFinishedHdl(std::move(aImportFinishedHdl))
+{
+}
+
+void XMLFetchThread::execute()
+{
+ ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
+ if (!pOrcus)
+ return;
+
+ mpXMLContext = pOrcus->createXMLContext(mrDocument, maURL);
+ if (!mpXMLContext)
+ return;
+
+ if (!maID.isEmpty())
+ {
+ ScOrcusImportXMLParam::RangeLink aRangeLink;
+ aRangeLink.maPos = ScAddress(0, 0, 0);
+ aRangeLink.maFieldPaths.push_back(OUStringToOString(maID, RTL_TEXTENCODING_UTF8));
+ maParam.maRangeLinks.clear();
+ maParam.maRangeLinks.push_back(aRangeLink);
+ }
+ // Do the import.
+ SolarMutexGuard aGuard;
+ mpXMLContext->importXML(maParam);
+
+ for (auto& itr : maDataTransformations)
+ {
+ itr->Transform(mrDocument);
+ }
+
+ maImportFinishedHdl();
+}
+
+XMLDataProvider::XMLDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource)
+ : DataProvider(rDataSource)
+ , mpDocument(pDoc)
+{
+}
+
+XMLDataProvider::~XMLDataProvider()
+{
+ if (mxXMLFetchThread.is())
+ {
+ SolarMutexReleaser aReleaser;
+ mxXMLFetchThread->join();
+ }
+}
+
+void XMLDataProvider::Import()
+{
+ // already importing data
+ if (mpDoc)
+ return;
+
+ mpDoc.reset(new ScDocument(SCDOCMODE_CLIP));
+ mpDoc->ResetClip(mpDocument, SCTAB(0));
+ mxXMLFetchThread = new XMLFetchThread(*mpDoc, mrDataSource.getURL(),
+ mrDataSource.getXMLImportParam(), mrDataSource.getID(),
+ std::bind(&XMLDataProvider::ImportFinished, this),
+ std::vector(mrDataSource.getDataTransformation()));
+ mxXMLFetchThread->launch();
+
+ if (mbDeterministic)
+ {
+ SolarMutexReleaser aReleaser;
+ mxXMLFetchThread->join();
+ }
+}
+
+void XMLDataProvider::ImportFinished() { mrDataSource.getDBManager()->WriteToDoc(*mpDoc); }
+
+const OUString& XMLDataProvider::GetURL() const { return mrDataSource.getURL(); }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dataprovider/xmldataprovider.hxx b/sc/source/ui/dataprovider/xmldataprovider.hxx
new file mode 100644
index 0000000000..7be3f95c37
--- /dev/null
+++ b/sc/source/ui/dataprovider/xmldataprovider.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/.
+ */
+
+#pragma once
+
+#include <dataprovider.hxx>
+
+namespace sc
+{
+class XMLFetchThread;
+
+class XMLDataProvider : public DataProvider
+{
+private:
+ ScDocument* mpDocument;
+ rtl::Reference<XMLFetchThread> mxXMLFetchThread;
+ ScDocumentUniquePtr mpDoc;
+
+public:
+ XMLDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource);
+ virtual ~XMLDataProvider() override;
+
+ virtual void Import() override;
+
+ virtual const OUString& GetURL() const override;
+
+ void ImportFinished();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutDialog.cxx b/sc/source/ui/dbgui/PivotLayoutDialog.cxx
new file mode 100644
index 0000000000..de1f6b3b6f
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutDialog.cxx
@@ -0,0 +1,722 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ */
+
+#include <PivotLayoutTreeList.hxx>
+#include <PivotLayoutDialog.hxx>
+#include <reffact.hxx>
+
+#include <rangeutl.hxx>
+#include <uiitems.hxx>
+#include <dputil.hxx>
+#include <dbdocfun.hxx>
+#include <dpsave.hxx>
+#include <dpshttab.hxx>
+#include <scmod.hxx>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+
+using namespace css::uno;
+using namespace css::sheet;
+
+ScItemValue::ScItemValue(OUString aName, SCCOL nColumn, PivotFunc nFunctionMask) :
+ maName(std::move(aName)),
+ maFunctionData(nColumn, nFunctionMask),
+ mpOriginalItemValue(this)
+{}
+
+ScItemValue::ScItemValue(const ScItemValue* pInputItemValue) :
+ maName(pInputItemValue->maName),
+ maFunctionData(pInputItemValue->maFunctionData),
+ mpOriginalItemValue(this)
+{}
+
+ScItemValue::~ScItemValue()
+{}
+
+namespace
+{
+
+ScRange lclGetRangeForNamedRange(OUString const & aName, const ScDocument& rDocument)
+{
+ ScRange aInvalidRange(ScAddress::INITIALIZE_INVALID);
+ ScRangeName* pRangeName = rDocument.GetRangeName();
+ if (pRangeName == nullptr)
+ return aInvalidRange;
+
+ const ScRangeData* pData = pRangeName->findByUpperName(aName.toAsciiUpperCase());
+ if (pData == nullptr)
+ return aInvalidRange;
+
+ ScRange aRange;
+ if (pData->IsReference(aRange))
+ return aRange;
+
+ return aInvalidRange;
+}
+
+}
+
+ScPivotLayoutDialog::ScPivotLayoutDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow, weld::Window* pParent,
+ ScViewData* pViewData, const ScDPObject* pPivotTableObject, bool bNewPivotTable)
+ : ScAnyRefDlgController(pSfxBindings, pChildWindow, pParent, "modules/scalc/ui/pivottablelayoutdialog.ui", "PivotTableLayout")
+ , maPivotTableObject(*pPivotTableObject)
+ , mpPreviouslyFocusedListBox(nullptr)
+ , mpViewData(pViewData)
+ , mrDocument(pViewData->GetDocument())
+ , mbNewPivotTable(bNewPivotTable)
+ , maAddressDetails(mrDocument.GetAddressConvention(), 0, 0)
+ , mbDialogLostFocus(false)
+ , mpActiveEdit(nullptr)
+ , mxListBoxField(new ScPivotLayoutTreeListLabel(m_xBuilder->weld_tree_view("listbox-fields")))
+ , mxListBoxPage(new ScPivotLayoutTreeList(m_xBuilder->weld_tree_view("listbox-page")))
+ , mxListBoxColumn(new ScPivotLayoutTreeList(m_xBuilder->weld_tree_view("listbox-column")))
+ , mxListBoxRow(new ScPivotLayoutTreeList(m_xBuilder->weld_tree_view("listbox-row")))
+ , mxListBoxData(new ScPivotLayoutTreeListData(m_xBuilder->weld_tree_view("listbox-data")))
+ , mxCheckIgnoreEmptyRows(m_xBuilder->weld_check_button("check-ignore-empty-rows"))
+ , mxCheckTotalColumns(m_xBuilder->weld_check_button("check-total-columns"))
+ , mxCheckAddFilter(m_xBuilder->weld_check_button("check-add-filter"))
+ , mxCheckIdentifyCategories(m_xBuilder->weld_check_button("check-identify-categories"))
+ , mxCheckTotalRows(m_xBuilder->weld_check_button("check-total-rows"))
+ , mxCheckDrillToDetail(m_xBuilder->weld_check_button("check-drill-to-details"))
+ , mxCheckExpandCollapse(m_xBuilder->weld_check_button("check-show-expand-collapse"))
+ , mxSourceRadioNamedRange(m_xBuilder->weld_radio_button("source-radio-named-range"))
+ , mxSourceRadioSelection(m_xBuilder->weld_radio_button("source-radio-selection"))
+ , mxSourceListBox(m_xBuilder->weld_combo_box("source-list"))
+ , mxSourceEdit(new formula::RefEdit(m_xBuilder->weld_entry("source-edit")))
+ , mxSourceButton(new formula::RefButton(m_xBuilder->weld_button("source-button")))
+ , mxDestinationRadioNewSheet(m_xBuilder->weld_radio_button("destination-radio-new-sheet"))
+ , mxDestinationRadioNamedRange(m_xBuilder->weld_radio_button("destination-radio-named-range"))
+ , mxDestinationRadioSelection(m_xBuilder->weld_radio_button("destination-radio-selection"))
+ , mxDestinationListBox(m_xBuilder->weld_combo_box("destination-list"))
+ , mxDestinationEdit(new formula::RefEdit(m_xBuilder->weld_entry("destination-edit")))
+ , mxDestinationButton(new formula::RefButton(m_xBuilder->weld_button("destination-button")))
+ , mxBtnOK(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mxSourceFrame(m_xBuilder->weld_frame("frame2"))
+ , mxSourceLabel(mxSourceFrame->weld_label_widget())
+ , mxDestFrame(m_xBuilder->weld_frame("frame1"))
+ , mxDestLabel(mxDestFrame->weld_label_widget())
+{
+ // Source UI
+ Link<weld::Toggleable&,void> aLink2 = LINK(this, ScPivotLayoutDialog, ToggleSource);
+ mxSourceRadioNamedRange->connect_toggled(aLink2);
+ mxSourceRadioSelection->connect_toggled(aLink2);
+
+ mxSourceEdit->SetReferences(this, mxSourceLabel.get());
+ mxSourceButton->SetReferences(this, mxSourceEdit.get());
+
+ Link<formula::RefEdit&,void> aEditLink = LINK(this, ScPivotLayoutDialog, GetEditFocusHandler);
+ mxDestinationEdit->SetGetFocusHdl(aEditLink);
+ mxSourceEdit->SetGetFocusHdl(aEditLink);
+
+ aEditLink = LINK(this, ScPivotLayoutDialog, LoseEditFocusHandler);
+ mxDestinationEdit->SetLoseFocusHdl(aEditLink);
+ mxSourceEdit->SetLoseFocusHdl(aEditLink);
+
+ mxSourceEdit->SetModifyHdl(LINK(this, ScPivotLayoutDialog, SourceEditModified));
+ mxSourceListBox->connect_changed(LINK(this, ScPivotLayoutDialog, SourceListSelected));
+
+ // Destination UI
+ aLink2 = LINK(this, ScPivotLayoutDialog, ToggleDestination);
+ mxDestinationRadioNewSheet->connect_toggled(aLink2);
+ mxDestinationRadioNamedRange->connect_toggled(aLink2);
+ mxDestinationRadioSelection->connect_toggled(aLink2);
+
+ mxDestinationEdit->SetReferences(this, mxDestLabel.get());
+ mxDestinationButton->SetReferences(this, mxDestinationEdit.get());
+
+ Link<formula::RefButton&,void> aButtonLink = LINK(this, ScPivotLayoutDialog, GetButtonFocusHandler);
+ mxSourceButton->SetGetFocusHdl(aButtonLink);
+ mxDestinationButton->SetGetFocusHdl(aButtonLink);
+
+ aButtonLink = LINK(this, ScPivotLayoutDialog, LoseButtonFocusHandler);
+ mxSourceButton->SetLoseFocusHdl(aButtonLink);
+ mxDestinationButton->SetLoseFocusHdl(aButtonLink);
+
+ // Buttons
+ mxBtnCancel->connect_clicked(LINK(this, ScPivotLayoutDialog, CancelClicked));
+ mxBtnOK->connect_clicked(LINK(this, ScPivotLayoutDialog, OKClicked));
+
+ // Initialize Data
+ maPivotTableObject.FillOldParam(maPivotParameters);
+ maPivotTableObject.FillLabelData(maPivotParameters);
+
+ mxListBoxField->Setup (this);
+ mxListBoxPage->Setup (this, ScPivotLayoutTreeList::PAGE_LIST);
+ mxListBoxColumn->Setup(this, ScPivotLayoutTreeList::COLUMN_LIST);
+ mxListBoxRow->Setup (this, ScPivotLayoutTreeList::ROW_LIST);
+ mxListBoxData->Setup (this);
+
+ FillValuesToListBoxes();
+
+ // Initialize Options
+ const ScDPSaveData* pSaveData = maPivotTableObject.GetSaveData();
+ if (pSaveData == nullptr)
+ {
+ mxCheckAddFilter->set_active(false);
+ mxCheckDrillToDetail->set_active(false);
+ mxCheckExpandCollapse->set_active(false);
+ }
+ else
+ {
+ mxCheckAddFilter->set_active(pSaveData->GetFilterButton());
+ mxCheckDrillToDetail->set_active(pSaveData->GetDrillDown());
+ mxCheckExpandCollapse->set_active(pSaveData->GetExpandCollapse());
+ }
+
+ mxCheckIgnoreEmptyRows->set_active(maPivotParameters.bIgnoreEmptyRows);
+ mxCheckIdentifyCategories->set_active(maPivotParameters.bDetectCategories);
+ mxCheckTotalColumns->set_active(maPivotParameters.bMakeTotalCol);
+ mxCheckTotalRows->set_active(maPivotParameters.bMakeTotalRow);
+
+ SetupSource();
+ SetupDestination();
+}
+
+ScPivotLayoutDialog::~ScPivotLayoutDialog()
+{
+}
+
+void ScPivotLayoutDialog::SetupSource()
+{
+ mxSourceListBox->clear();
+
+ ScRange aSourceRange;
+ OUString sSourceNamedRangeName;
+
+ if (maPivotTableObject.GetSheetDesc())
+ {
+ const ScSheetSourceDesc* pSheetSourceDesc = maPivotTableObject.GetSheetDesc();
+ aSourceRange = pSheetSourceDesc->GetSourceRange();
+
+ if(!aSourceRange.IsValid())
+ {
+ // Source is probably a DB Range
+ mxSourceRadioNamedRange->set_sensitive(false);
+ mxSourceRadioSelection->set_sensitive(false);
+ ToggleSource();
+ return;
+ }
+ else
+ {
+ OUString aSourceRangeName = aSourceRange.Format(mrDocument, ScRefFlags::RANGE_ABS_3D, maAddressDetails);
+ mxSourceEdit->SetText(aSourceRangeName);
+ }
+ }
+ else
+ {
+ mxSourceRadioNamedRange->set_sensitive(false);
+ mxSourceRadioSelection->set_sensitive(false);
+ ToggleSource();
+ return;
+ }
+
+ // Setup Named Ranges
+ bool bIsNamedRange = false;
+
+ ScAreaNameIterator aIterator(mrDocument);
+ OUString aEachName;
+ ScRange aEachRange;
+
+ while (aIterator.Next(aEachName, aEachRange))
+ {
+ if (!aIterator.WasDBName())
+ {
+ mxSourceListBox->append_text(aEachName);
+ if (aEachRange == aSourceRange)
+ {
+ sSourceNamedRangeName = aEachName;
+ bIsNamedRange = true;
+ }
+ }
+ }
+
+ bool bSourceBoxHasEntries = mxSourceListBox->get_count() > 0;
+
+ if (bIsNamedRange)
+ {
+ mxSourceListBox->set_active_text(sSourceNamedRangeName);
+ mxSourceRadioNamedRange->set_active(true);
+ }
+ else
+ {
+ // If entries - select first entry
+ mxSourceListBox->set_active(bSourceBoxHasEntries ? 0 : -1);
+ mxSourceRadioSelection->set_active(true);
+ }
+
+ // If no entries disable the radio button.
+ if (!bSourceBoxHasEntries)
+ mxSourceRadioNamedRange->set_sensitive(false);
+
+ ToggleSource();
+}
+
+void ScPivotLayoutDialog::SetupDestination()
+{
+ mxDestinationListBox->clear();
+
+ // Fill up named ranges
+ ScAreaNameIterator aIterator(mrDocument);
+ OUString aName;
+ ScRange aRange;
+
+ while (aIterator.Next(aName, aRange))
+ {
+ if (!aIterator.WasDBName())
+ {
+ mxDestinationListBox->append_text(aName);
+ }
+ }
+
+ // If entries - select first entry, otherwise disable the radio button.
+ if (mxDestinationListBox->get_count() > 0)
+ mxDestinationListBox->set_active(0);
+ else
+ mxDestinationRadioNamedRange->set_sensitive(false);
+
+ //
+ if (mbNewPivotTable)
+ {
+ mxDestinationRadioNewSheet->set_active(true);
+ }
+ else
+ {
+ if (maPivotParameters.nTab != MAXTAB + 1)
+ {
+ ScAddress aAddress(maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ OUString aAddressString = aAddress.Format(ScRefFlags::ADDR_ABS_3D, &mrDocument, maAddressDetails);
+ mxDestinationEdit->SetText(aAddressString);
+ mxDestinationRadioSelection->set_active(true);
+ }
+ }
+
+ ToggleDestination();
+}
+
+void ScPivotLayoutDialog::FillValuesToListBoxes()
+{
+ mxListBoxField->FillLabelFields(maPivotParameters.maLabelArray);
+ mxListBoxData->FillDataField(maPivotParameters.maDataFields);
+ mxListBoxColumn->FillFields(maPivotParameters.maColFields);
+ mxListBoxRow->FillFields(maPivotParameters.maRowFields);
+ mxListBoxPage->FillFields(maPivotParameters.maPageFields);
+}
+
+void ScPivotLayoutDialog::SetActive()
+{
+ if (mbDialogLostFocus)
+ {
+ mbDialogLostFocus = false;
+ if(mpActiveEdit != nullptr)
+ {
+ mpActiveEdit->GrabFocus();
+ if (mpActiveEdit == mxSourceEdit.get())
+ UpdateSourceRange();
+ }
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+
+ RefInputDone();
+}
+
+void ScPivotLayoutDialog::SetReference(const ScRange& rReferenceRange, ScDocument& rDocument)
+{
+ if (!mbDialogLostFocus)
+ return;
+
+ if (mpActiveEdit == nullptr)
+ return;
+
+ if (rReferenceRange.aStart != rReferenceRange.aEnd)
+ RefInputStart(mpActiveEdit);
+
+ OUString aReferenceString = rReferenceRange.Format(rDocument, ScRefFlags::RANGE_ABS_3D, maAddressDetails);
+
+ if (mpActiveEdit == mxSourceEdit.get())
+ {
+ mxSourceEdit->SetRefString(aReferenceString);
+ }
+ else if (mpActiveEdit == mxDestinationEdit.get())
+ {
+ mxDestinationEdit->SetRefString(aReferenceString);
+ }
+}
+
+bool ScPivotLayoutDialog::IsRefInputMode() const
+{
+ return mbDialogLostFocus;
+}
+
+void ScPivotLayoutDialog::ItemInserted(const ScItemValue* pItemValue, ScPivotLayoutTreeList::SvPivotTreeListType eType)
+{
+ if (pItemValue == nullptr)
+ return;
+
+ switch (eType)
+ {
+ case ScPivotLayoutTreeList::ROW_LIST:
+ case ScPivotLayoutTreeList::COLUMN_LIST:
+ case ScPivotLayoutTreeList::PAGE_LIST:
+ {
+ mxListBoxRow->RemoveEntryForItem(pItemValue);
+ mxListBoxColumn->RemoveEntryForItem(pItemValue);
+ mxListBoxPage->RemoveEntryForItem(pItemValue);
+ }
+ break;
+ case ScPivotLayoutTreeList::LABEL_LIST:
+ {
+ mxListBoxRow->RemoveEntryForItem(pItemValue);
+ mxListBoxColumn->RemoveEntryForItem(pItemValue);
+ mxListBoxPage->RemoveEntryForItem(pItemValue);
+ mxListBoxData->RemoveEntryForItem(pItemValue);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void ScPivotLayoutDialog::UpdateSourceRange()
+{
+ if (!maPivotTableObject.GetSheetDesc())
+ return;
+
+ ScSheetSourceDesc aSourceSheet = *maPivotTableObject.GetSheetDesc();
+
+ if (mxSourceRadioNamedRange->get_active())
+ {
+ OUString aEntryString = mxSourceListBox->get_active_text();
+ ScRange aSourceRange = lclGetRangeForNamedRange(aEntryString, mrDocument);
+ if (!aSourceRange.IsValid() || aSourceSheet.GetSourceRange() == aSourceRange)
+ return;
+ aSourceSheet.SetRangeName(aEntryString);
+ }
+ else if (mxSourceRadioSelection->get_active())
+ {
+ OUString aSourceString = mxSourceEdit->GetText();
+ ScRange aSourceRange;
+ ScRefFlags nResult = aSourceRange.Parse(aSourceString, mrDocument, maAddressDetails);
+
+ bool bIsValid = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID; // aSourceString is valid
+
+ mxSourceEdit->SetRefValid(true);
+
+ if (bIsValid)
+ {
+ ScRefAddress aStart;
+ ScRefAddress aEnd;
+
+ ConvertDoubleRef(mrDocument, aSourceString, 1, aStart, aEnd, maAddressDetails);
+ aSourceRange.aStart = aStart.GetAddress();
+ aSourceRange.aEnd = aEnd.GetAddress();
+ }
+ else
+ {
+ aSourceRange = lclGetRangeForNamedRange(aSourceString, mrDocument);
+ }
+
+ if (!aSourceRange.IsValid())
+ {
+ mxSourceEdit->SetRefValid(false);
+ return;
+ }
+
+ if (aSourceSheet.GetSourceRange() == aSourceRange)
+ return;
+
+ aSourceSheet.SetSourceRange(aSourceRange);
+ if (aSourceSheet.CheckSourceRange())
+ {
+ mxSourceEdit->SetRefValid(false);
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ maPivotTableObject.SetSheetDesc(aSourceSheet);
+ maPivotTableObject.FillOldParam(maPivotParameters);
+ maPivotTableObject.FillLabelData(maPivotParameters);
+
+ FillValuesToListBoxes();
+}
+
+void ScPivotLayoutDialog::ApplyChanges()
+{
+ ScDPSaveData aSaveData;
+ ApplySaveData(aSaveData);
+ ApplyLabelData(aSaveData);
+
+ ScDPObject *pOldDPObj = mrDocument.GetDPAtCursor( maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ ScRange aDestinationRange;
+ bool bToNewSheet = false;
+
+ if (!GetDestination(aDestinationRange, bToNewSheet))
+ return;
+
+ SetDispatcherLock(false);
+ SwitchToDocument();
+
+ sal_uInt16 nWhichPivot = SC_MOD()->GetPool().GetWhich(SID_PIVOT_TABLE);
+ ScPivotItem aPivotItem(nWhichPivot, &aSaveData, &aDestinationRange, bToNewSheet);
+ mpViewData->GetViewShell()->SetDialogDPObject(std::make_unique<ScDPObject>(maPivotTableObject));
+
+
+ SfxDispatcher* pDispatcher = GetBindings().GetDispatcher();
+ SfxCallMode const nCallMode = SfxCallMode::SLOT | SfxCallMode::RECORD;
+ const SfxPoolItemHolder aResult(pDispatcher->ExecuteList(SID_PIVOT_TABLE,
+ nCallMode, { &aPivotItem }));
+
+ if (nullptr != aResult.getItem())
+ {
+ // existing pivot table might have moved to a new range or a new sheet
+ if ( pOldDPObj != nullptr )
+ {
+ const ScRange& rOldRange = pOldDPObj->GetOutRange();
+
+ ScDPObject *pDPObj = nullptr;
+ // FIXME: if the new range overlaps with the old one, the table actually doesn't move
+ // and shouldn't therefore be deleted
+ if ( ( ( rOldRange != aDestinationRange ) && !rOldRange.Contains( aDestinationRange ) )
+ || bToNewSheet )
+ {
+ pDPObj = mrDocument.GetDPAtCursor( maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ }
+ if (pDPObj)
+ {
+ ScDBDocFunc aFunc( *(mpViewData->GetDocShell() ));
+ aFunc.RemovePivotTable( *pDPObj, true, false);
+ mpViewData->GetView()->CursorPosChanged();
+ }
+ }
+ return;
+ }
+
+ SetDispatcherLock(true);
+}
+
+void ScPivotLayoutDialog::ApplySaveData(ScDPSaveData& rSaveData)
+{
+ rSaveData.SetIgnoreEmptyRows(mxCheckIgnoreEmptyRows->get_active());
+ rSaveData.SetRepeatIfEmpty(mxCheckIdentifyCategories->get_active());
+ rSaveData.SetColumnGrand(mxCheckTotalColumns->get_active());
+ rSaveData.SetRowGrand(mxCheckTotalRows->get_active());
+ rSaveData.SetFilterButton(mxCheckAddFilter->get_active());
+ rSaveData.SetDrillDown(mxCheckDrillToDetail->get_active());
+ rSaveData.SetExpandCollapse(mxCheckExpandCollapse->get_active());
+
+ Reference<XDimensionsSupplier> xSource = maPivotTableObject.GetSource();
+
+ ScPivotFieldVector aPageFieldVector;
+ mxListBoxPage->PushEntriesToPivotFieldVector(aPageFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aPageFieldVector, DataPilotFieldOrientation_PAGE,
+ xSource, maPivotParameters.maLabelArray);
+
+ ScPivotFieldVector aColFieldVector;
+ mxListBoxColumn->PushEntriesToPivotFieldVector(aColFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aColFieldVector, DataPilotFieldOrientation_COLUMN,
+ xSource, maPivotParameters.maLabelArray);
+
+ ScPivotFieldVector aRowFieldVector;
+ mxListBoxRow->PushEntriesToPivotFieldVector(aRowFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aRowFieldVector, DataPilotFieldOrientation_ROW,
+ xSource, maPivotParameters.maLabelArray);
+
+ ScPivotFieldVector aDataFieldVector;
+ mxListBoxData->PushEntriesToPivotFieldVector(aDataFieldVector);
+ ScDPObject::ConvertOrientation(rSaveData, aDataFieldVector, DataPilotFieldOrientation_DATA,
+ xSource, maPivotParameters.maLabelArray,
+ &aColFieldVector, &aRowFieldVector, &aPageFieldVector);
+}
+
+void ScPivotLayoutDialog::ApplyLabelData(const ScDPSaveData& rSaveData)
+{
+ ScDPLabelDataVector& rLabelDataVector = GetLabelDataVector();
+
+ for (std::unique_ptr<ScDPLabelData> const & pLabelData : rLabelDataVector)
+ {
+ OUString aUnoName = ScDPUtil::createDuplicateDimensionName(pLabelData->maName, pLabelData->mnDupCount);
+ ScDPSaveDimension* pSaveDimensions = rSaveData.GetExistingDimensionByName(aUnoName);
+
+ if (pSaveDimensions == nullptr)
+ continue;
+
+ pSaveDimensions->SetUsedHierarchy(pLabelData->mnUsedHier);
+ pSaveDimensions->SetShowEmpty(pLabelData->mbShowAll);
+ pSaveDimensions->SetRepeatItemLabels(pLabelData->mbRepeatItemLabels);
+ pSaveDimensions->SetSortInfo(&pLabelData->maSortInfo);
+ pSaveDimensions->SetLayoutInfo(&pLabelData->maLayoutInfo);
+ pSaveDimensions->SetAutoShowInfo(&pLabelData->maShowInfo);
+
+ bool bManualSort = (pLabelData->maSortInfo.Mode == DataPilotFieldSortMode::MANUAL);
+
+ for (ScDPLabelData::Member const & rLabelMember : pLabelData->maMembers)
+ {
+ ScDPSaveMember* pMember = pSaveDimensions->GetMemberByName(rLabelMember.maName);
+
+ if (bManualSort || !rLabelMember.mbVisible || !rLabelMember.mbShowDetails)
+ {
+ pMember->SetIsVisible(rLabelMember.mbVisible);
+ pMember->SetShowDetails(rLabelMember.mbShowDetails);
+ }
+ }
+ }
+}
+
+bool ScPivotLayoutDialog::GetDestination(ScRange& aDestinationRange, bool& bToNewSheet)
+{
+ bToNewSheet = false;
+
+ if (mxDestinationRadioNamedRange->get_active())
+ {
+ OUString aName = mxDestinationListBox->get_active_text();
+ aDestinationRange = lclGetRangeForNamedRange(aName, mrDocument);
+ if (!aDestinationRange.IsValid())
+ return false;
+ }
+ else if (mxDestinationRadioSelection->get_active())
+ {
+ ScAddress aAddress;
+ aAddress.Parse(mxDestinationEdit->GetText(), mrDocument, maAddressDetails);
+ aDestinationRange = ScRange(aAddress);
+ }
+ else
+ {
+ bToNewSheet = true;
+ aDestinationRange = ScRange(maPivotParameters.nCol, maPivotParameters.nRow, maPivotParameters.nTab);
+ }
+ return true;
+}
+
+ScItemValue* ScPivotLayoutDialog::GetItem(SCCOL nColumn)
+{
+ return mxListBoxField->GetItem(nColumn);
+}
+
+bool ScPivotLayoutDialog::IsDataElement(SCCOL nColumn)
+{
+ return mxListBoxField->IsDataElement(nColumn);
+}
+
+ScDPLabelData& ScPivotLayoutDialog::GetLabelData(SCCOL nColumn)
+{
+ return *maPivotParameters.maLabelArray[nColumn];
+}
+
+void ScPivotLayoutDialog::PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames)
+{
+ mxListBoxData->PushDataFieldNames(rDataFieldNames);
+}
+
+void ScPivotLayoutDialog::Close()
+{
+ DoClose(ScPivotLayoutWrapper::GetChildWindowId());
+ SfxDialogController::Close();
+}
+
+IMPL_LINK_NOARG( ScPivotLayoutDialog, OKClicked, weld::Button&, void )
+{
+ /* tdf#137726 hide so it's not a candidate to be parent of any error
+ messages that may appear because this dialog is going to disappear on
+ response(RET_OK) and the error dialog is not run in its own event loop
+ but instead async */
+ m_xDialog->hide();
+
+ ApplyChanges();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG( ScPivotLayoutDialog, CancelClicked, weld::Button&, void )
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK(ScPivotLayoutDialog, GetEditFocusHandler, formula::RefEdit&, rCtrl, void)
+{
+ mpActiveEdit = &rCtrl;
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(ScPivotLayoutDialog, GetButtonFocusHandler, formula::RefButton&, rCtrl, void)
+{
+ mpActiveEdit = nullptr;
+
+ if (&rCtrl == mxSourceButton.get())
+ mpActiveEdit = mxSourceEdit.get();
+ else if (&rCtrl == mxDestinationButton.get())
+ mpActiveEdit = mxDestinationEdit.get();
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, SourceListSelected, weld::ComboBox&, void)
+{
+ UpdateSourceRange();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, SourceEditModified, formula::RefEdit&, void)
+{
+ UpdateSourceRange();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, ToggleSource, weld::Toggleable&, void)
+{
+ ToggleSource();
+}
+
+void ScPivotLayoutDialog::ToggleSource()
+{
+ bool bNamedRange = mxSourceRadioNamedRange->get_active();
+ bool bSelection = mxSourceRadioSelection->get_active();
+ mxSourceListBox->set_sensitive(bNamedRange);
+ mxSourceButton->GetWidget()->set_sensitive(bSelection);
+ mxSourceEdit->GetWidget()->set_sensitive(bSelection);
+ UpdateSourceRange();
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutDialog, ToggleDestination, weld::Toggleable&, void)
+{
+ ToggleDestination();
+}
+
+void ScPivotLayoutDialog::ToggleDestination()
+{
+ bool bNamedRange = mxDestinationRadioNamedRange->get_active();
+ bool bSelection = mxDestinationRadioSelection->get_active();
+ mxDestinationListBox->set_sensitive(bNamedRange);
+ mxDestinationButton->GetWidget()->set_sensitive(bSelection);
+ mxDestinationEdit->GetWidget()->set_sensitive(bSelection);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeList.cxx b/sc/source/ui/dbgui/PivotLayoutTreeList.cxx
new file mode 100644
index 0000000000..c173d7ca7d
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeList.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:
+ */
+
+#include <memory>
+#include <PivotLayoutTreeList.hxx>
+#include <PivotLayoutDialog.hxx>
+
+#include <vcl/event.hxx>
+#include <pivot.hxx>
+
+ScPivotLayoutTreeList::ScPivotLayoutTreeList(std::unique_ptr<weld::TreeView> xControl)
+ : ScPivotLayoutTreeListBase(std::move(xControl))
+{
+ mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeList, KeyInputHdl));
+ mxControl->connect_row_activated(LINK(this, ScPivotLayoutTreeList, DoubleClickHdl));
+}
+
+ScPivotLayoutTreeList::~ScPivotLayoutTreeList()
+{
+ if (mpSubtotalDlg)
+ {
+ mpSubtotalDlg->Response(RET_CANCEL);
+ mpSubtotalDlg.clear();
+ }
+}
+
+void ScPivotLayoutTreeList::Setup(ScPivotLayoutDialog* pParent, SvPivotTreeListType eType)
+{
+ mpParent = pParent;
+ meType = eType;
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeList, DoubleClickHdl, weld::TreeView&, bool)
+{
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry == -1)
+ return true;
+
+ ScItemValue* pCurrentItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(nEntry));
+ ScPivotFuncData& rCurrentFunctionData = pCurrentItemValue->maFunctionData;
+ SCCOL nCurrentColumn = rCurrentFunctionData.mnCol;
+
+ if (mpParent->IsDataElement(nCurrentColumn))
+ return true;
+
+ ScDPLabelData& rCurrentLabelData = mpParent->GetLabelData(nCurrentColumn);
+
+ ScAbstractDialogFactory* pFactory = ScAbstractDialogFactory::Create();
+
+ maDataFieldNames.clear();
+ mpParent->PushDataFieldNames(maDataFieldNames);
+
+ mpSubtotalDlg = pFactory->CreateScDPSubtotalDlg(mxControl.get(), mpParent->maPivotTableObject,
+ rCurrentLabelData, rCurrentFunctionData,
+ maDataFieldNames);
+
+ mpSubtotalDlg->StartExecuteAsync([this, pCurrentItemValue, nCurrentColumn](int nResult) {
+ if (nResult == RET_OK)
+ {
+ mpSubtotalDlg->FillLabelData(mpParent->GetLabelData(nCurrentColumn));
+ pCurrentItemValue->maFunctionData.mnFuncMask = mpSubtotalDlg->GetFuncMask();
+ }
+
+ mpSubtotalDlg.disposeAndClear();
+ });
+
+ return true;
+}
+
+void ScPivotLayoutTreeList::FillFields(ScPivotFieldVector& rFieldVector)
+{
+ mxControl->clear();
+ maItemValues.clear();
+
+ for (const ScPivotField& rField : rFieldVector)
+ {
+ OUString aLabel = mpParent->GetItem(rField.nCol)->maName;
+ ScItemValue* pItemValue = new ScItemValue(aLabel, rField.nCol, rField.nFuncMask);
+ maItemValues.push_back(std::unique_ptr<ScItemValue>(pItemValue));
+ OUString sId(weld::toId(pItemValue));
+ mxControl->append(sId, pItemValue->maName);
+ }
+}
+
+void ScPivotLayoutTreeList::InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget)
+{
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(rSource.get_selected_id());
+ ScItemValue* pOriginalItemValue = pItemValue->mpOriginalItemValue;
+
+ // Don't allow to add "Data" element to page fields
+ if (meType == PAGE_LIST && mpParent->IsDataElement(pItemValue->maFunctionData.mnCol))
+ return;
+
+ mpParent->ItemInserted(pOriginalItemValue, meType);
+
+ InsertEntryForItem(pOriginalItemValue, nTarget);
+}
+
+void ScPivotLayoutTreeList::InsertEntryForItem(const ScItemValue* pItemValue, int nPosition)
+{
+ ScItemValue* pListItemValue = new ScItemValue(pItemValue);
+ maItemValues.push_back(std::unique_ptr<ScItemValue>(pListItemValue));
+ OUString sName = pListItemValue->maName;
+ OUString sId(weld::toId(pListItemValue));
+ mxControl->insert(nullptr, nPosition, &sName, &sId, nullptr, nullptr, false, nullptr);
+}
+
+IMPL_LINK(ScPivotLayoutTreeList, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ if (nCode == KEY_DELETE)
+ {
+ const int nEntry = mxControl->get_cursor_index();
+ if (nEntry != -1)
+ mxControl->remove(nEntry);
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
new file mode 100644
index 0000000000..45af29a4f1
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ */
+
+#include <PivotLayoutTreeListBase.hxx>
+#include <PivotLayoutDialog.hxx>
+
+ScPivotLayoutTreeListBase::ScPivotLayoutTreeListBase(std::unique_ptr<weld::TreeView> xControl, SvPivotTreeListType eType)
+ : mxControl(std::move(xControl))
+ , maDropTargetHelper(*this)
+ , meType(eType)
+ , mpParent(nullptr)
+{
+ mxControl->connect_focus_in(LINK(this, ScPivotLayoutTreeListBase, GetFocusHdl));
+ mxControl->connect_mnemonic_activate(LINK(this, ScPivotLayoutTreeListBase, MnemonicActivateHdl));
+ mxControl->connect_focus_out(LINK(this, ScPivotLayoutTreeListBase, LoseFocusHdl));
+}
+
+ScPivotLayoutTreeListBase::~ScPivotLayoutTreeListBase()
+{
+}
+
+void ScPivotLayoutTreeListBase::Setup(ScPivotLayoutDialog* pParent)
+{
+ mpParent = pParent;
+}
+
+ScPivotLayoutTreeDropTarget::ScPivotLayoutTreeDropTarget(ScPivotLayoutTreeListBase& rTreeView)
+ : DropTargetHelper(rTreeView.get_widget().get_drop_target())
+ , m_rTreeView(rTreeView)
+{
+}
+
+sal_Int8 ScPivotLayoutTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ // to enable the autoscroll when we're close to the edges
+ weld::TreeView& rWidget = m_rTreeView.get_widget();
+ rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
+ return DND_ACTION_MOVE;
+}
+
+sal_Int8 ScPivotLayoutTreeDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ weld::TreeView& rWidget = m_rTreeView.get_widget();
+ weld::TreeView* pSource = rWidget.get_drag_source();
+ if (!pSource)
+ return DND_ACTION_NONE;
+
+ std::unique_ptr<weld::TreeIter> xTarget(rWidget.make_iterator());
+ int nTargetPos = -1;
+ if (rWidget.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
+ nTargetPos = rWidget.get_iter_index_in_parent(*xTarget);
+ m_rTreeView.InsertEntryForSourceTarget(*pSource, nTargetPos);
+ rWidget.unset_drag_dest_row();
+ return DND_ACTION_NONE;
+}
+
+void ScPivotLayoutTreeListBase::PushEntriesToPivotFieldVector(ScPivotFieldVector& rVector)
+{
+ std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator());
+ if (!mxControl->get_iter_first(*xEachEntry))
+ return;
+ do
+ {
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry));
+ ScPivotFuncData& rFunctionData = pItemValue->maFunctionData;
+
+ ScPivotField aField;
+ aField.nCol = rFunctionData.mnCol;
+ aField.mnOriginalDim = rFunctionData.mnOriginalDim;
+ aField.nFuncMask = rFunctionData.mnFuncMask;
+ aField.mnDupCount = rFunctionData.mnDupCount;
+ aField.maFieldRef = rFunctionData.maFieldRef;
+ rVector.push_back(aField);
+ } while (mxControl->iter_next(*xEachEntry));
+}
+
+void ScPivotLayoutTreeListBase::InsertEntryForSourceTarget(weld::TreeView& /*pSource*/, int /*nTarget*/)
+{
+}
+
+void ScPivotLayoutTreeListBase::RemoveEntryForItem(const ScItemValue* pItemValue)
+{
+ OUString sId(weld::toId(pItemValue));
+ int nPos = mxControl->find_id(sId);
+ if (nPos == -1)
+ return;
+ mxControl->remove(nPos);
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListBase, GetFocusHdl, weld::Widget&, void)
+{
+ if (!mpParent)
+ return;
+ mpParent->mpPreviouslyFocusedListBox = this;
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListBase, MnemonicActivateHdl, weld::Widget&, bool)
+{
+ if (!mpParent || !mpParent->mpPreviouslyFocusedListBox)
+ return false;
+ weld::TreeView& rSource = mpParent->mpPreviouslyFocusedListBox->get_widget();
+ int nEntry = rSource.get_cursor_index();
+ if (nEntry != -1)
+ InsertEntryForSourceTarget(rSource, -1);
+ return true;
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListBase, LoseFocusHdl, weld::Widget&, void)
+{
+ if (!mpParent)
+ return;
+ mpParent->mpPreviouslyFocusedListBox = nullptr;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListData.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListData.cxx
new file mode 100644
index 0000000000..7455613274
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListData.cxx
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ */
+
+#include <memory>
+#include <string_view>
+
+#include <PivotLayoutTreeListData.hxx>
+#include <PivotLayoutDialog.hxx>
+
+#include <vcl/event.hxx>
+#include <pivot.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+namespace
+{
+
+OUString lclGetFunctionMaskName(const PivotFunc nFunctionMask)
+{
+ TranslateId pStrId;
+ switch (nFunctionMask)
+ {
+ case PivotFunc::Sum: pStrId = STR_FUN_TEXT_SUM; break;
+ case PivotFunc::Count: pStrId = STR_FUN_TEXT_COUNT; break;
+ case PivotFunc::Average: pStrId = STR_FUN_TEXT_AVG; break;
+ case PivotFunc::Median: pStrId = STR_FUN_TEXT_MEDIAN; break;
+ case PivotFunc::Max: pStrId = STR_FUN_TEXT_MAX; break;
+ case PivotFunc::Min: pStrId = STR_FUN_TEXT_MIN; break;
+ case PivotFunc::Product: pStrId = STR_FUN_TEXT_PRODUCT; break;
+ case PivotFunc::CountNum: pStrId = STR_FUN_TEXT_COUNT; break;
+ case PivotFunc::StdDev: pStrId = STR_FUN_TEXT_STDDEV; break;
+ case PivotFunc::StdDevP: pStrId = STR_FUN_TEXT_STDDEV; break;
+ case PivotFunc::StdVar: pStrId = STR_FUN_TEXT_VAR; break;
+ case PivotFunc::StdVarP: pStrId = STR_FUN_TEXT_VAR; break;
+ default:
+ assert(false);
+ break;
+ }
+ if (pStrId)
+ return ScResId(pStrId);
+ else
+ return OUString();
+}
+
+OUString lclCreateDataItemName(const PivotFunc nFunctionMask, std::u16string_view rName, const sal_uInt8 nDuplicationCount)
+{
+ OUString aBuffer = lclGetFunctionMaskName(nFunctionMask) + " - " + rName;
+ if(nDuplicationCount > 0)
+ {
+ aBuffer += " " + OUString::number(nDuplicationCount);
+ }
+ return aBuffer;
+}
+
+} // anonymous namespace
+
+ScPivotLayoutTreeListData::ScPivotLayoutTreeListData(std::unique_ptr<weld::TreeView> xControl)
+ : ScPivotLayoutTreeListBase(std::move(xControl))
+{
+ mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeListData, KeyInputHdl));
+ mxControl->connect_row_activated(LINK(this, ScPivotLayoutTreeListData, DoubleClickHdl));
+}
+
+ScPivotLayoutTreeListData::~ScPivotLayoutTreeListData()
+{
+ if (mpFunctionDlg)
+ {
+ mpFunctionDlg->Response(RET_CANCEL);
+ mpFunctionDlg.clear();
+ }
+}
+
+IMPL_LINK_NOARG(ScPivotLayoutTreeListData, DoubleClickHdl, weld::TreeView&, bool)
+{
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry == -1)
+ return true;
+
+ ScItemValue* pCurrentItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(nEntry));
+ ScPivotFuncData& rCurrentFunctionData = pCurrentItemValue->maFunctionData;
+
+ SCCOL nCurrentColumn = rCurrentFunctionData.mnCol;
+ ScDPLabelData& rCurrentLabelData = mpParent->GetLabelData(nCurrentColumn);
+
+ ScAbstractDialogFactory* pFactory = ScAbstractDialogFactory::Create();
+
+ mpFunctionDlg = pFactory->CreateScDPFunctionDlg(mxControl.get(), mpParent->GetLabelDataVector(), rCurrentLabelData, rCurrentFunctionData);
+
+ mpFunctionDlg->StartExecuteAsync([this, pCurrentItemValue, nEntry](int nResult) mutable {
+ if (nResult == RET_OK)
+ {
+ ScPivotFuncData& rFunctionData = pCurrentItemValue->maFunctionData;
+ rFunctionData.mnFuncMask = mpFunctionDlg->GetFuncMask();
+ ScDPLabelData& rLabelData = mpParent->GetLabelData(rFunctionData.mnCol);
+ rLabelData.mnFuncMask = mpFunctionDlg->GetFuncMask();
+
+ rFunctionData.maFieldRef = mpFunctionDlg->GetFieldRef();
+
+ ScDPLabelData& rDFData = mpParent->GetLabelData(rFunctionData.mnCol);
+
+ AdjustDuplicateCount(pCurrentItemValue);
+
+ OUString sDataItemName = lclCreateDataItemName(
+ rFunctionData.mnFuncMask,
+ rDFData.maName,
+ rFunctionData.mnDupCount);
+
+ mxControl->set_text(nEntry, sDataItemName);
+ }
+
+ mpFunctionDlg->disposeOnce();
+ });
+
+ return true;
+}
+
+void ScPivotLayoutTreeListData::FillDataField(ScPivotFieldVector& rDataFields)
+{
+ mxControl->clear();
+ maDataItemValues.clear();
+
+ for (const ScPivotField& rField : rDataFields)
+ {
+ if (rField.nCol == PIVOT_DATA_FIELD)
+ continue;
+
+ SCCOL nColumn;
+ if (rField.mnOriginalDim >= 0)
+ nColumn = rField.mnOriginalDim;
+ else
+ nColumn = rField.nCol;
+
+ ScItemValue* pOriginalItemValue = mpParent->GetItem(nColumn);
+ ScItemValue* pItemValue = new ScItemValue(pOriginalItemValue->maName, nColumn, rField.nFuncMask);
+
+ pItemValue->mpOriginalItemValue = pOriginalItemValue;
+ pItemValue->maFunctionData.mnOriginalDim = rField.mnOriginalDim;
+ pItemValue->maFunctionData.maFieldRef = rField.maFieldRef;
+
+ AdjustDuplicateCount(pItemValue);
+ OUString sDataItemName = lclCreateDataItemName(pItemValue->maFunctionData.mnFuncMask,
+ pItemValue->maName,
+ pItemValue->maFunctionData.mnDupCount);
+
+ maDataItemValues.push_back(std::unique_ptr<ScItemValue>(pItemValue));
+ OUString sId(weld::toId(pItemValue));
+ mxControl->append(sId, sDataItemName);
+ }
+}
+
+void ScPivotLayoutTreeListData::PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames)
+{
+ std::unique_ptr<weld::TreeIter> xLoopEntry(mxControl->make_iterator());
+ if (!mxControl->get_iter_first(*xLoopEntry))
+ return;
+
+ do
+ {
+ ScItemValue* pEachItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xLoopEntry));
+ SCCOL nColumn = pEachItemValue->maFunctionData.mnCol;
+
+ ScDPLabelData& rLabelData = mpParent->GetLabelData(nColumn);
+
+ if (rLabelData.maName.isEmpty())
+ continue;
+
+ OUString sLayoutName = rLabelData.maLayoutName;
+ if (sLayoutName.isEmpty())
+ {
+ sLayoutName = lclCreateDataItemName(
+ pEachItemValue->maFunctionData.mnFuncMask,
+ pEachItemValue->maName,
+ pEachItemValue->maFunctionData.mnDupCount);
+ }
+
+ rDataFieldNames.emplace_back(rLabelData.maName, sLayoutName, rLabelData.mnDupCount);
+ } while (mxControl->iter_next(*xLoopEntry));
+}
+
+void ScPivotLayoutTreeListData::InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget)
+{
+ if (rSource.count_selected_rows() <=0)
+ return;
+
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(rSource.get_selected_id());
+
+ if (mpParent->IsDataElement(pItemValue->maFunctionData.mnCol))
+ return;
+
+ if (&rSource == mxControl.get())
+ {
+ OUString sText = mxControl->get_selected_text();
+ OUString sId(weld::toId(pItemValue));
+ mxControl->remove_id(sId);
+ mxControl->insert(nullptr, nTarget, &sText, &sId, nullptr, nullptr, false, nullptr);
+ }
+ else
+ {
+ InsertEntryForItem(pItemValue->mpOriginalItemValue, nTarget);
+ }
+}
+
+void ScPivotLayoutTreeListData::InsertEntryForItem(ScItemValue* pItemValue, int nPosition)
+{
+ ScItemValue* pDataItemValue = new ScItemValue(pItemValue);
+ pDataItemValue->mpOriginalItemValue = pItemValue;
+ maDataItemValues.push_back(std::unique_ptr<ScItemValue>(pDataItemValue));
+
+ ScPivotFuncData& rFunctionData = pDataItemValue->maFunctionData;
+
+ if (rFunctionData.mnFuncMask == PivotFunc::NONE ||
+ rFunctionData.mnFuncMask == PivotFunc::Auto)
+ {
+ rFunctionData.mnFuncMask = PivotFunc::Sum;
+ }
+
+ AdjustDuplicateCount(pDataItemValue);
+
+ OUString sDataName = lclCreateDataItemName(
+ rFunctionData.mnFuncMask,
+ pDataItemValue->maName,
+ rFunctionData.mnDupCount);
+
+ OUString sId(weld::toId(pDataItemValue));
+ mxControl->insert(nullptr, nPosition, &sDataName, &sId, nullptr, nullptr, false, nullptr);
+}
+
+void ScPivotLayoutTreeListData::AdjustDuplicateCount(ScItemValue* pInputItemValue)
+{
+ ScPivotFuncData& rInputFunctionData = pInputItemValue->maFunctionData;
+
+ bool bFoundDuplicate = false;
+
+ rInputFunctionData.mnDupCount = 0;
+ sal_uInt8 nMaxDuplicateCount = 0;
+
+ std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator());
+ if (!mxControl->get_iter_first(*xEachEntry))
+ return;
+ do
+ {
+ ScItemValue* pItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry));
+ if (pItemValue == pInputItemValue)
+ continue;
+
+ ScPivotFuncData& rFunctionData = pItemValue->maFunctionData;
+
+ if (rFunctionData.mnCol == rInputFunctionData.mnCol &&
+ rFunctionData.mnFuncMask == rInputFunctionData.mnFuncMask)
+ {
+ bFoundDuplicate = true;
+ if(rFunctionData.mnDupCount > nMaxDuplicateCount)
+ nMaxDuplicateCount = rFunctionData.mnDupCount;
+ }
+ } while (mxControl->iter_next(*xEachEntry));
+
+ if(bFoundDuplicate)
+ {
+ rInputFunctionData.mnDupCount = nMaxDuplicateCount + 1;
+ }
+}
+
+IMPL_LINK(ScPivotLayoutTreeListData, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ if (nCode == KEY_DELETE)
+ {
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry != -1)
+ mxControl->remove(nEntry);
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx
new file mode 100644
index 0000000000..e4a2276dac
--- /dev/null
+++ b/sc/source/ui/dbgui/PivotLayoutTreeListLabel.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:
+ */
+
+#include <memory>
+#include <PivotLayoutTreeListLabel.hxx>
+#include <PivotLayoutDialog.hxx>
+
+#include <vcl/event.hxx>
+#include <pivot.hxx>
+
+ScPivotLayoutTreeListLabel::ScPivotLayoutTreeListLabel(std::unique_ptr<weld::TreeView> xControl)
+ : ScPivotLayoutTreeListBase(std::move(xControl), LABEL_LIST)
+ , maDataItem(0)
+{
+ mxControl->connect_key_press(LINK(this, ScPivotLayoutTreeListLabel, KeyInputHdl));
+}
+
+ScPivotLayoutTreeListLabel::~ScPivotLayoutTreeListLabel()
+{}
+
+void ScPivotLayoutTreeListLabel::FillLabelFields(ScDPLabelDataVector& rLabelVector)
+{
+ mxControl->clear();
+ maItemValues.clear();
+
+ for (std::unique_ptr<ScDPLabelData> const & pLabelData : rLabelVector)
+ {
+ ScItemValue* pValue = new ScItemValue(pLabelData->maName, pLabelData->mnCol, pLabelData->mnFuncMask);
+ maItemValues.push_back(std::unique_ptr<ScItemValue>(pValue));
+ if (pLabelData->mbDataLayout)
+ {
+ maDataItem = maItemValues.size() - 1;
+ }
+
+ if (pLabelData->mnOriginalDim < 0 && !pLabelData->mbDataLayout)
+ {
+ mxControl->append(weld::toId(pValue), pLabelData->maName);
+ }
+ }
+}
+
+void ScPivotLayoutTreeListLabel::InsertEntryForSourceTarget(weld::TreeView& rSource, int /*nTarget*/)
+{
+ if (&rSource == mxControl.get())
+ return;
+ rSource.remove(rSource.get_selected_index());
+}
+
+bool ScPivotLayoutTreeListLabel::IsDataElement(SCCOL nColumn)
+{
+ return (nColumn == PIVOT_DATA_FIELD || nColumn == maDataItem);
+}
+
+ScItemValue* ScPivotLayoutTreeListLabel::GetItem(SCCOL nColumn)
+{
+ if (nColumn == PIVOT_DATA_FIELD)
+ return maItemValues[maDataItem].get();
+ return maItemValues[nColumn].get();
+}
+
+IMPL_LINK(ScPivotLayoutTreeListLabel, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ sal_uInt16 nCode = aCode.GetCode();
+
+ if (nCode == KEY_DELETE)
+ {
+ int nEntry = mxControl->get_cursor_index();
+ if (nEntry != -1)
+ mxControl->remove(nEntry);
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/asciiopt.cxx b/sc/source/ui/dbgui/asciiopt.cxx
new file mode 100644
index 0000000000..c9a4d881ba
--- /dev/null
+++ b/sc/source/ui/dbgui/asciiopt.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 <global.hxx>
+#include <asciiopt.hxx>
+#include <comphelper/string.hxx>
+#include <osl/thread.h>
+#include <o3tl/string_view.hxx>
+
+constexpr std::u16string_view pStrFix = u"FIX";
+constexpr std::u16string_view pStrMrg = u"MRG";
+
+ScAsciiOptions::ScAsciiOptions() :
+ bFixedLen ( false ),
+ aFieldSeps ( OUString(';') ),
+ bMergeFieldSeps ( false ),
+ bRemoveSpace ( false ),
+ bQuotedFieldAsText(false),
+ bDetectSpecialNumber(false),
+ bDetectScientificNumber(true),
+ bEvaluateFormulas(true),
+ bSkipEmptyCells(false),
+ bSaveAsShown(true),
+ bSaveFormulas(false),
+ bIncludeBOM(false),
+ cTextSep ( cDefaultTextSep ),
+ eCharSet ( osl_getThreadTextEncoding() ),
+ eLang ( LANGUAGE_SYSTEM ),
+ bCharSetSystem ( false ),
+ nStartRow ( 1 )
+{
+}
+
+void ScAsciiOptions::SetColumnInfo( const ScCsvExpDataVec& rDataVec )
+{
+ sal_uInt16 nInfoCount = static_cast< sal_uInt16 >( rDataVec.size() );
+ mvColStart.resize(nInfoCount);
+ mvColFormat.resize(nInfoCount);
+ for( sal_uInt16 nIx = 0; nIx < nInfoCount; ++nIx )
+ {
+ mvColStart[ nIx ] = rDataVec[ nIx ].mnIndex;
+ mvColFormat[ nIx ] = rDataVec[ nIx ].mnType;
+ }
+}
+
+static OUString lcl_decodeSepString( std::u16string_view rSepNums, bool & o_bMergeFieldSeps )
+{
+ if ( rSepNums.empty() )
+ return OUString();
+
+ OUStringBuffer aFieldSeps;
+ sal_Int32 nPos = 0;
+ do
+ {
+ const std::u16string_view aCode = o3tl::getToken(rSepNums, 0, '/', nPos );
+ if ( aCode == pStrMrg )
+ o_bMergeFieldSeps = true;
+ else
+ {
+ sal_Int32 nVal = o3tl::toInt32(aCode);
+ if ( nVal )
+ aFieldSeps.append(sal_Unicode(nVal));
+ }
+ }
+ while ( nPos >= 0 );
+
+ return aFieldSeps.makeStringAndClear();
+}
+
+// The options string must not contain semicolons (because of the pick list),
+// use comma as separator.
+
+void ScAsciiOptions::ReadFromString( std::u16string_view rString )
+{
+ sal_Int32 nPos = rString.empty() ? -1 : 0;
+
+ // Token 0: Field separator.
+ if ( nPos >= 0 )
+ {
+ bFixedLen = bMergeFieldSeps = false;
+
+ const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
+ if ( aToken == pStrFix )
+ bFixedLen = true;
+ aFieldSeps = lcl_decodeSepString( aToken, bMergeFieldSeps);
+ }
+
+ // Token 1: Text separator.
+ if ( nPos >= 0 )
+ {
+ const sal_Int32 nVal = o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos));
+ cTextSep = static_cast<sal_Unicode>(nVal);
+ }
+
+ // Token 2: Text encoding.
+ if ( nPos >= 0 )
+ {
+ eCharSet = ScGlobal::GetCharsetValue( o3tl::getToken(rString, 0, ',', nPos) );
+ }
+
+ // Token 3: Number of start row.
+ if ( nPos >= 0 )
+ {
+ nStartRow = o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos));
+ }
+
+ // Token 4: Column info.
+ if ( nPos >= 0 )
+ {
+ const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', nPos);
+ const sal_Int32 nInfoCount = comphelper::string::getTokenCount(aToken, '/')/2;
+ mvColStart.resize(nInfoCount);
+ mvColFormat.resize(nInfoCount);
+ sal_Int32 nP = 0;
+ for (sal_Int32 nInfo=0; nInfo<nInfoCount; ++nInfo)
+ {
+ mvColStart[nInfo] = o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP));
+ mvColFormat[nInfo] = static_cast<sal_uInt8>(o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP)));
+ }
+ }
+
+ // Token 5: Language.
+ if (nPos >= 0)
+ {
+ eLang = static_cast<LanguageType>(o3tl::toInt32(o3tl::getToken(rString, 0, ',', nPos)));
+ }
+
+ // Token 6: Import quoted field as text.
+ if (nPos >= 0)
+ {
+ bQuotedFieldAsText = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+
+ // Token 7: Detect special numbers.
+ if (nPos >= 0)
+ {
+ bDetectSpecialNumber = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bDetectSpecialNumber = true; // default of versions that didn't add the parameter
+
+ // Token 8: used for "Save as shown" in export options
+ if ( nPos >= 0 )
+ {
+ bSaveAsShown = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bSaveAsShown = true; // default value
+
+ // Token 9: used for "Save cell formulas" in export options
+ if ( nPos >= 0 )
+ {
+ bSaveFormulas = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bSaveFormulas = false;
+
+ // Token 10: Boolean for Trim spaces.
+ if (nPos >= 0)
+ {
+ bRemoveSpace = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bRemoveSpace = false;
+
+ // Token 11: sheet to export for --convert-to csv
+ // Does not need to be evaluated here but may be present.
+ if (nPos >= 0)
+ {
+ o3tl::getToken(rString, 0, ',', nPos);
+ }
+
+ // Token 12: evaluate formulas.
+ if (nPos >= 0)
+ {
+ // If present, defaults to "false".
+ bEvaluateFormulas = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bEvaluateFormulas = true; // default of versions that didn't add the parameter
+
+ // Token 13: include BOM.
+ bIncludeBOM = nPos >= 0 && o3tl::getToken(rString, 0, ',', nPos) == u"true";
+
+ // Token 14: Detect scientific numbers.
+ if (nPos >= 0)
+ {
+ bDetectScientificNumber = o3tl::getToken(rString, 0, ',', nPos) == u"true";
+ }
+ else
+ bDetectScientificNumber = true; // default of versions that didn't add the parameter
+
+}
+
+OUString ScAsciiOptions::WriteToString() const
+{
+ OUStringBuffer aOutStr;
+
+ // Token 0: Field separator.
+ if ( bFixedLen )
+ aOutStr.append(pStrFix);
+ else if ( aFieldSeps.isEmpty() )
+ aOutStr.append("0");
+ else
+ {
+ sal_Int32 nLen = aFieldSeps.getLength();
+ for (sal_Int32 i=0; i<nLen; i++)
+ {
+ if (i)
+ aOutStr.append("/");
+ aOutStr.append(OUString::number(aFieldSeps[i]));
+ }
+ if ( bMergeFieldSeps )
+ {
+ aOutStr.append(OUString::Concat("/") + pStrMrg);
+ }
+ }
+
+ // Token 1: Text Quote character.
+ aOutStr.append("," + OUString::number(cTextSep) + ",");
+
+ //Token 2: Text encoding.
+ if ( bCharSetSystem ) // force "SYSTEM"
+ aOutStr.append(ScGlobal::GetCharsetString( RTL_TEXTENCODING_DONTKNOW ));
+ else
+ aOutStr.append(ScGlobal::GetCharsetString( eCharSet ));
+
+ //Token 3: Number of start row.
+ aOutStr.append("," + OUString::number(nStartRow) + ",");
+
+ //Token 4: Column info.
+ for (size_t nInfo=0; nInfo<mvColStart.size(); nInfo++)
+ {
+ if (nInfo)
+ aOutStr.append("/");
+ aOutStr.append(OUString::number(mvColStart[nInfo]) +
+ "/" +
+ OUString::number(mvColFormat[nInfo]));
+ }
+
+ // #i112025# the options string is used in macros and linked sheets,
+ // so new options must be added at the end, to remain compatible
+ // Always keep in sync with ScImportOptions.
+
+ aOutStr.append("," +
+ // Token 5: Language
+ OUString::number(static_cast<sal_uInt16>(eLang)) + "," +
+ // Token 6: Import quoted field as text.
+ OUString::boolean( bQuotedFieldAsText ) + "," +
+ // Token 7: Detect special numbers.
+ OUString::boolean( bDetectSpecialNumber ) + "," +
+ // Token 8: used for "Save as shown" in export options
+ OUString::boolean( bSaveAsShown ) +"," +
+ // Token 9: used for "Save cell formulas" in export options
+ OUString::boolean( bSaveFormulas ) + "," +
+ // Token 10: Trim Space
+ OUString::boolean( bRemoveSpace ) +
+ // Token 11: sheet to export, always 0 for current sheet
+ ",0," +
+ // Token 12: evaluate formulas in import
+ OUString::boolean( bEvaluateFormulas ) + "," +
+ // Token 13: include BOM
+ OUString::boolean(bIncludeBOM) + "," +
+ // Token 14: Detect scientific numbers.
+ OUString::boolean( bDetectScientificNumber )
+ );
+ return aOutStr.makeStringAndClear();
+}
+
+// static
+sal_Unicode ScAsciiOptions::GetWeightedFieldSep( const OUString & rFieldSeps, bool bDecodeNumbers )
+{
+ bool bMergeFieldSeps = false;
+ OUString aFieldSeps( bDecodeNumbers ? lcl_decodeSepString( rFieldSeps, bMergeFieldSeps) : rFieldSeps);
+ if (aFieldSeps.isEmpty())
+ {
+ return 0;
+ }
+ else if (aFieldSeps.getLength() == 1)
+ return aFieldSeps[0];
+ else
+ {
+ // There can be only one separator for output. See also fdo#53449
+ if (aFieldSeps.indexOf(',') != -1)
+ return ',';
+ else if (aFieldSeps.indexOf('\t') != -1)
+ return '\t';
+ else if (aFieldSeps.indexOf(';') != -1)
+ return ';';
+ else if (aFieldSeps.indexOf(' ') != -1)
+ return ' ';
+ else
+ return aFieldSeps[0];
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/consdlg.cxx b/sc/source/ui/dbgui/consdlg.cxx
new file mode 100644
index 0000000000..d0921f3eb9
--- /dev/null
+++ b/sc/source/ui/dbgui/consdlg.cxx
@@ -0,0 +1,534 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <reffact.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <consdlg.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+namespace
+{
+ void INFOBOX(weld::Window* pWindow, TranslateId id)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWindow,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(id)));
+ xInfoBox->run();
+ }
+}
+
+class ScAreaData
+{
+public:
+ ScAreaData()
+ {
+ }
+
+ void Set( const OUString& rName, const OUString& rArea )
+ {
+ aStrName = rName;
+ aStrArea = rArea;
+ }
+
+ OUString aStrName;
+ OUString aStrArea;
+};
+
+ScConsolidateDlg::ScConsolidateDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet)
+
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/consolidatedialog.ui", "ConsolidateDialog")
+ , aStrUndefined ( ScResId( SCSTR_UNDEFINED ) )
+ , theConsData ( static_cast<const ScConsolidateItem&>(
+ rArgSet.Get( rArgSet.GetPool()->
+ GetWhich( SID_CONSOLIDATE ) )
+ ).GetData() )
+ , rViewData ( static_cast<ScTabViewShell*>(SfxViewShell::Current())->
+ GetViewData() )
+ , rDoc ( static_cast<ScTabViewShell*>(SfxViewShell::Current())->
+ GetViewData().GetDocument() )
+ , nAreaDataCount ( 0 )
+ , nWhichCons ( rArgSet.GetPool()->GetWhich( SID_CONSOLIDATE ) )
+ , bDlgLostFocus ( false )
+ , m_xLbFunc(m_xBuilder->weld_combo_box("func"))
+ , m_xLbConsAreas(m_xBuilder->weld_tree_view("consareas"))
+ , m_xLbDataArea(m_xBuilder->weld_combo_box("lbdataarea"))
+ , m_xEdDataArea(new formula::RefEdit(m_xBuilder->weld_entry("eddataarea")))
+ , m_xRbDataArea(new formula::RefButton(m_xBuilder->weld_button("rbdataarea")))
+ , m_xLbDestArea(m_xBuilder->weld_combo_box("lbdestarea"))
+ , m_xEdDestArea(new formula::RefEdit(m_xBuilder->weld_entry("eddestarea")))
+ , m_xRbDestArea(new formula::RefButton(m_xBuilder->weld_button("rbdestarea")))
+ , m_xBtnByRow(m_xBuilder->weld_check_button("byrow"))
+ , m_xBtnByCol(m_xBuilder->weld_check_button("bycol"))
+ , m_xBtnRefs(m_xBuilder->weld_check_button("refs"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("delete"))
+ , m_xDataFT(m_xBuilder->weld_label("ftdataarea"))
+ , m_xDestFT(m_xBuilder->weld_label("ftdestarea"))
+{
+ m_pRefInputEdit = m_xEdDataArea.get();
+ Init();
+}
+
+ScConsolidateDlg::~ScConsolidateDlg()
+{
+}
+
+void ScConsolidateDlg::Init()
+{
+ OUString aStr;
+ sal_uInt16 i=0;
+
+ m_xRbDataArea->SetReferences(this, m_xEdDataArea.get());
+ m_xEdDataArea->SetReferences(this, m_xDataFT.get());
+ m_xRbDestArea->SetReferences(this, m_xEdDestArea.get());
+ m_xEdDestArea->SetReferences(this, m_xDestFT.get());
+
+ m_xEdDataArea->SetGetFocusHdl( LINK( this, ScConsolidateDlg, GetEditFocusHdl ) );
+ m_xEdDestArea->SetGetFocusHdl( LINK( this, ScConsolidateDlg, GetEditFocusHdl ) );
+ m_xLbDataArea->connect_focus_in( LINK( this, ScConsolidateDlg, GetFocusHdl ) );
+ m_xLbDestArea->connect_focus_in( LINK( this, ScConsolidateDlg, GetFocusHdl ) );
+ m_xEdDataArea->SetModifyHdl( LINK( this, ScConsolidateDlg, ModifyHdl ) );
+ m_xEdDestArea->SetModifyHdl( LINK( this, ScConsolidateDlg, ModifyHdl ) );
+ m_xLbConsAreas->connect_changed( LINK( this, ScConsolidateDlg, SelectTVHdl ) );
+ m_xLbDataArea->connect_changed( LINK( this, ScConsolidateDlg, SelectCBHdl ) );
+ m_xLbDestArea->connect_changed( LINK( this, ScConsolidateDlg, SelectCBHdl ) );
+ m_xBtnOk->connect_clicked( LINK( this, ScConsolidateDlg, OkHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScConsolidateDlg, ClickHdl ) );
+ m_xBtnAdd->connect_clicked( LINK( this, ScConsolidateDlg, ClickHdl ) );
+ m_xBtnRemove->connect_clicked( LINK( this, ScConsolidateDlg, ClickHdl ) );
+
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+
+ m_xBtnByRow->set_active( theConsData.bByRow );
+ m_xBtnByCol->set_active( theConsData.bByCol );
+ m_xBtnRefs->set_active( theConsData.bReferenceData );
+
+ m_xLbFunc->set_active( FuncToLbPos( theConsData.eFunction ) );
+
+ m_xLbConsAreas->set_selection_mode(SelectionMode::Multiple);
+ m_xLbConsAreas->set_size_request(m_xLbConsAreas->get_approximate_digit_width() * 16,
+ m_xLbConsAreas->get_height_rows(5));
+
+ // read consolidation areas
+ m_xLbConsAreas->clear();
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ for ( i=0; i<theConsData.nDataAreaCount; i++ )
+ {
+ const ScArea& rArea = theConsData.pDataAreas[i];
+ if ( rArea.nTab < rDoc.GetTableCount() )
+ {
+ aStr = ScRange( rArea.nColStart, rArea.nRowStart, rArea.nTab,
+ rArea.nColEnd, rArea.nRowEnd, rArea.nTab ).Format( rDoc,
+ ScRefFlags::RANGE_ABS_3D, eConv );
+ m_xLbConsAreas->append_text(aStr);
+ }
+ }
+
+ if ( theConsData.nTab < rDoc.GetTableCount() )
+ {
+ aStr = ScAddress( theConsData.nCol, theConsData.nRow, theConsData.nTab
+ ).Format( ScRefFlags::ADDR_ABS_3D, &rDoc, eConv );
+ m_xEdDestArea->SetText( aStr );
+ }
+ else
+ m_xEdDestArea->SetText(OUString());
+
+ // Use the ScAreaData helper class to save those range names from the
+ // RangeNames and database ranges that appear in the ListBoxes.
+
+ ScRangeName* pRangeNames = rDoc.GetRangeName();
+ ScDBCollection* pDbNames = rDoc.GetDBCollection();
+ size_t nRangeCount = pRangeNames ? pRangeNames->size() : 0;
+ size_t nDbCount = pDbNames ? pDbNames->getNamedDBs().size() : 0;
+
+ nAreaDataCount = nRangeCount+nDbCount;
+ pAreaData = nullptr;
+
+ if ( nAreaDataCount > 0 )
+ {
+ pAreaData.reset( new ScAreaData[nAreaDataCount] );
+
+ OUString aStrName;
+ sal_uInt16 nAt = 0;
+ ScRange aRange;
+ ScAreaNameIterator aIter( rDoc );
+ while ( aIter.Next( aStrName, aRange ) )
+ {
+ OUString aStrArea(aRange.Format(rDoc, ScRefFlags::ADDR_ABS_3D, eConv));
+ pAreaData[nAt++].Set( aStrName, aStrArea );
+ }
+ }
+
+ FillAreaLists();
+ ModifyHdl( *m_xEdDestArea );
+ m_xLbDataArea->set_active( 0 );
+ m_xEdDataArea->SetText(OUString());
+ m_xEdDataArea->GrabFocus();
+
+ //aFlSep.SetStyle( aFlSep.GetStyle() | WB_VERT );
+
+ //@BugID 54702 enable/disable only in base class
+ //SFX_APPWINDOW->set_sensitive(true);
+}
+
+void ScConsolidateDlg::FillAreaLists()
+{
+ m_xLbDataArea->clear();
+ m_xLbDestArea->clear();
+ m_xLbDataArea->append_text( aStrUndefined );
+ m_xLbDestArea->append_text( aStrUndefined );
+
+ if ( pAreaData && (nAreaDataCount > 0) )
+ {
+ for ( size_t i=0;
+ (i<nAreaDataCount) && (!pAreaData[i].aStrName.isEmpty());
+ i++ )
+ {
+ m_xLbDataArea->append_text(pAreaData[i].aStrName);
+ m_xLbDestArea->append_text(pAreaData[i].aStrName);
+ }
+ }
+}
+
+// Handover of a range within a table that has been selected by the mouse.
+// This range is then shown in the reference window as new selection.
+
+void ScConsolidateDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if ( !m_pRefInputEdit )
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_pRefInputEdit );
+
+ OUString aStr;
+ ScRefFlags nFmt = ScRefFlags::RANGE_ABS_3D; //!!! nCurTab is still missing
+ const formula::FormulaGrammar::AddressConvention eConv = rDocP.GetAddressConvention();
+
+ if ( rRef.aStart.Tab() != rRef.aEnd.Tab() )
+ nFmt |= ScRefFlags::TAB2_3D;
+
+ if ( m_pRefInputEdit == m_xEdDataArea.get())
+ aStr = rRef.Format(rDocP, nFmt, eConv);
+ else if ( m_pRefInputEdit == m_xEdDestArea.get() )
+ aStr = rRef.aStart.Format(nFmt, &rDocP, eConv);
+
+ m_pRefInputEdit->SetRefString( aStr );
+ ModifyHdl( *m_pRefInputEdit );
+}
+
+void ScConsolidateDlg::Close()
+{
+ DoClose( ScConsolidateDlgWrapper::GetChildWindowId() );
+}
+
+void ScConsolidateDlg::SetActive()
+{
+ if ( bDlgLostFocus )
+ {
+ bDlgLostFocus = false;
+
+ if ( m_pRefInputEdit )
+ {
+ m_pRefInputEdit->GrabFocus();
+ ModifyHdl( *m_pRefInputEdit );
+ }
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+void ScConsolidateDlg::Deactivate()
+{
+ bDlgLostFocus = true;
+}
+
+bool ScConsolidateDlg::VerifyEdit( formula::RefEdit* pEd )
+{
+ if (pEd != m_xEdDataArea.get() && pEd != m_xEdDestArea.get())
+ return false;
+
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bEditOk = false;
+ OUString theCompleteStr;
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+
+ if ( pEd == m_xEdDataArea.get() )
+ {
+ bEditOk = ScRangeUtil::IsAbsArea( pEd->GetText(), rDoc,
+ nTab, &theCompleteStr, nullptr, nullptr, eConv );
+ }
+ else if ( pEd == m_xEdDestArea.get() )
+ {
+ OUString aPosStr;
+
+ ScRangeUtil::CutPosString( pEd->GetText(), aPosStr );
+ bEditOk = ScRangeUtil::IsAbsPos( aPosStr, rDoc,
+ nTab, &theCompleteStr, nullptr, eConv );
+ }
+
+ if ( bEditOk )
+ pEd->SetText( theCompleteStr );
+
+ return bEditOk;
+}
+
+// Handler:
+
+IMPL_LINK( ScConsolidateDlg, GetEditFocusHdl, formula::RefEdit&, rControl, void )
+{
+ m_pRefInputEdit = &rControl;
+}
+
+IMPL_LINK( ScConsolidateDlg, GetFocusHdl, weld::Widget&, rControl, void )
+{
+ if (&rControl == m_xLbDataArea.get())
+ m_pRefInputEdit = m_xEdDataArea.get();
+ else if (&rControl == m_xLbDestArea.get())
+ m_pRefInputEdit = m_xEdDestArea.get();
+}
+
+IMPL_LINK_NOARG(ScConsolidateDlg, OkHdl, weld::Button&, void)
+{
+ const sal_Int32 nDataAreaCount = m_xLbConsAreas->n_children();
+
+ if ( nDataAreaCount > 0 )
+ {
+ ScRefAddress aDestAddress;
+ SCTAB nTab = rViewData.GetTabNo();
+ OUString aDestPosStr( m_xEdDestArea->GetText() );
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+
+ if ( ScRangeUtil::IsAbsPos( aDestPosStr, rDoc, nTab, nullptr, &aDestAddress, eConv ) )
+ {
+ ScConsolidateParam theOutParam( theConsData );
+ std::unique_ptr<ScArea[]> pDataAreas(new ScArea[nDataAreaCount]);
+
+ for ( sal_Int32 i=0; i<nDataAreaCount; ++i )
+ {
+ ScRangeUtil::MakeArea(m_xLbConsAreas->get_text(i),
+ pDataAreas[i], rDoc, nTab, eConv);
+ }
+
+ theOutParam.nCol = aDestAddress.Col();
+ theOutParam.nRow = aDestAddress.Row();
+ theOutParam.nTab = aDestAddress.Tab();
+ theOutParam.eFunction = LbPosToFunc( m_xLbFunc->get_active() );
+ theOutParam.bByCol = m_xBtnByCol->get_active();
+ theOutParam.bByRow = m_xBtnByRow->get_active();
+ theOutParam.bReferenceData = m_xBtnRefs->get_active();
+ theOutParam.SetAreas( std::move(pDataAreas), nDataAreaCount );
+
+ ScConsolidateItem aOutItem( nWhichCons, &theOutParam );
+
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(SID_CONSOLIDATE,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aOutItem });
+ response(RET_OK);
+ }
+ else
+ {
+ INFOBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdDestArea->GrabFocus();
+ }
+ }
+ else
+ response(RET_CANCEL); // no area defined -> Cancel
+}
+
+IMPL_LINK( ScConsolidateDlg, ClickHdl, weld::Button&, rBtn, void )
+{
+ if ( &rBtn == m_xBtnCancel.get() )
+ response(RET_CANCEL);
+ else if ( &rBtn == m_xBtnAdd.get() )
+ {
+ if ( !m_xEdDataArea->GetText().isEmpty() )
+ {
+ OUString aNewEntry( m_xEdDataArea->GetText() );
+ std::unique_ptr<ScArea[]> ppAreas;
+ sal_uInt16 nAreaCount = 0;
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+
+ if ( ScRangeUtil::IsAbsTabArea( aNewEntry, &rDoc, &ppAreas, &nAreaCount, true, eConv ) )
+ {
+ // IsAbsTabArea() creates an array of ScArea pointers,
+ // which have been created dynamically as well.
+ // These objects need to be deleted here.
+
+ for ( sal_uInt16 i=0; i<nAreaCount; i++ )
+ {
+ const ScArea& rArea = ppAreas[i];
+ OUString aNewArea = ScRange( rArea.nColStart, rArea.nRowStart, rArea.nTab,
+ rArea.nColEnd, rArea.nRowEnd, rArea.nTab
+ ).Format(rDoc, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ if (m_xLbConsAreas->find_text(aNewArea) == -1)
+ {
+ m_xLbConsAreas->append_text( aNewArea );
+ }
+ }
+ }
+ else if ( VerifyEdit( m_xEdDataArea.get() ) )
+ {
+ OUString aNewArea( m_xEdDataArea->GetText() );
+
+ if (m_xLbConsAreas->find_text(aNewArea) == -1)
+ m_xLbConsAreas->append_text(aNewArea);
+ else
+ INFOBOX(m_xDialog.get(), STR_AREA_ALREADY_INSERTED);
+ }
+ else
+ {
+ INFOBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdDataArea->GrabFocus();
+ }
+ }
+ }
+ else if ( &rBtn == m_xBtnRemove.get() )
+ {
+ std::vector<int> aSelectedRows(m_xLbConsAreas->get_selected_rows());
+ std::sort(aSelectedRows.begin(), aSelectedRows.end());
+ for (auto it = aSelectedRows.rbegin(); it != aSelectedRows.rend(); ++it)
+ m_xLbConsAreas->remove(*it);
+ m_xBtnRemove->set_sensitive(false);
+ }
+}
+
+IMPL_LINK( ScConsolidateDlg, SelectTVHdl, weld::TreeView&, rLb, void )
+{
+ if (rLb.get_selected_index() != -1)
+ m_xBtnRemove->set_sensitive(true);
+ else
+ m_xBtnRemove->set_sensitive(false);
+}
+
+IMPL_LINK( ScConsolidateDlg, SelectCBHdl, weld::ComboBox&, rLb, void )
+{
+ formula::RefEdit* pEd = (&rLb == m_xLbDataArea.get()) ? m_xEdDataArea.get() : m_xEdDestArea.get();
+ const sal_Int32 nSelPos = rLb.get_active();
+
+ if ( (nSelPos > 0)
+ && (nAreaDataCount > 0)
+ && (pAreaData != nullptr) )
+ {
+ if ( o3tl::make_unsigned(nSelPos) <= nAreaDataCount )
+ {
+ OUString aString( pAreaData[nSelPos-1].aStrArea );
+
+ if ( &rLb == m_xLbDestArea.get() )
+ ScRangeUtil::CutPosString( aString, aString );
+
+ pEd->SetText( aString );
+
+ if ( pEd == m_xEdDataArea.get() )
+ m_xBtnAdd->set_sensitive(true);
+ }
+ }
+ else
+ {
+ pEd->SetText( OUString() );
+ if ( pEd == m_xEdDataArea.get() )
+ m_xBtnAdd->set_sensitive(true);
+ }
+}
+
+IMPL_LINK( ScConsolidateDlg, ModifyHdl, formula::RefEdit&, rEd, void )
+{
+ if ( &rEd == m_xEdDataArea.get() )
+ {
+ OUString aAreaStr( rEd.GetText() );
+ if ( !aAreaStr.isEmpty() )
+ m_xBtnAdd->set_sensitive(true);
+ else
+ m_xBtnAdd->set_sensitive(false);
+ }
+ else if ( &rEd == m_xEdDestArea.get() )
+ {
+ m_xLbDestArea->set_active(0);
+ }
+}
+
+// TODO: generalize!
+// Resource of the ListBox and these two conversion methods are also in
+// tpsubt and everywhere, where StarCalc functions are selectable.
+
+ScSubTotalFunc ScConsolidateDlg::LbPosToFunc( sal_Int32 nPos )
+{
+ switch ( nPos )
+ {
+ case 2: return SUBTOTAL_FUNC_AVE;
+ case 6: return SUBTOTAL_FUNC_CNT;
+ case 1: return SUBTOTAL_FUNC_CNT2;
+ case 3: return SUBTOTAL_FUNC_MAX;
+ case 4: return SUBTOTAL_FUNC_MIN;
+ case 5: return SUBTOTAL_FUNC_PROD;
+ case 7: return SUBTOTAL_FUNC_STD;
+ case 8: return SUBTOTAL_FUNC_STDP;
+ case 9: return SUBTOTAL_FUNC_VAR;
+ case 10: return SUBTOTAL_FUNC_VARP;
+ case 0:
+ default:
+ return SUBTOTAL_FUNC_SUM;
+ }
+}
+
+sal_Int32 ScConsolidateDlg::FuncToLbPos( ScSubTotalFunc eFunc )
+{
+ switch ( eFunc )
+ {
+ case SUBTOTAL_FUNC_AVE: return 2;
+ case SUBTOTAL_FUNC_CNT: return 6;
+ case SUBTOTAL_FUNC_CNT2: return 1;
+ case SUBTOTAL_FUNC_MAX: return 3;
+ case SUBTOTAL_FUNC_MIN: return 4;
+ case SUBTOTAL_FUNC_PROD: return 5;
+ case SUBTOTAL_FUNC_STD: return 7;
+ case SUBTOTAL_FUNC_STDP: return 8;
+ case SUBTOTAL_FUNC_VAR: return 9;
+ case SUBTOTAL_FUNC_VARP: return 10;
+ case SUBTOTAL_FUNC_NONE:
+ case SUBTOTAL_FUNC_SUM:
+ default:
+ return 0;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvcontrol.cxx b/sc/source/ui/dbgui/csvcontrol.cxx
new file mode 100644
index 0000000000..409e898b46
--- /dev/null
+++ b/sc/source/ui/dbgui/csvcontrol.cxx
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <csvcontrol.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+
+ScCsvLayoutData::ScCsvLayoutData() :
+ mnPosCount( 1 ),
+ mnPosOffset( 0 ),
+ mnWinWidth( 1 ),
+ mnHdrWidth( 0 ),
+ mnCharWidth( 1 ),
+ mnLineCount( 1 ),
+ mnLineOffset( 0 ),
+ mnWinHeight( 1 ),
+ mnHdrHeight( 0 ),
+ mnLineHeight( 1 ),
+ mnPosCursor( CSV_POS_INVALID ),
+ mnColCursor( 0 ),
+ mnNoRepaint( 0 ),
+ mbAppRTL( AllSettings::GetLayoutRTL() )
+{
+}
+
+ScCsvDiff ScCsvLayoutData::GetDiff( const ScCsvLayoutData& rData ) const
+{
+ ScCsvDiff nRet = ScCsvDiff::Equal;
+ if( mnPosCount != rData.mnPosCount ) nRet |= ScCsvDiff::PosCount;
+ if( mnPosOffset != rData.mnPosOffset ) nRet |= ScCsvDiff::PosOffset;
+ if( mnHdrWidth != rData.mnHdrWidth ) nRet |= ScCsvDiff::HeaderWidth;
+ if( mnCharWidth != rData.mnCharWidth ) nRet |= ScCsvDiff::CharWidth;
+ if( mnLineCount != rData.mnLineCount ) nRet |= ScCsvDiff::LineCount;
+ if( mnLineOffset != rData.mnLineOffset ) nRet |= ScCsvDiff::LineOffset;
+ if( mnHdrHeight != rData.mnHdrHeight ) nRet |= ScCsvDiff::HeaderHeight;
+ if( mnLineHeight != rData.mnLineHeight ) nRet |= ScCsvDiff::LineHeight;
+ if( mnPosCursor != rData.mnPosCursor ) nRet |= ScCsvDiff::RulerCursor;
+ if( mnColCursor != rData.mnColCursor ) nRet |= ScCsvDiff::GridCursor;
+ return nRet;
+}
+
+ScCsvControl::ScCsvControl(const ScCsvLayoutData& rData)
+ : mrData(rData)
+ , mbValidGfx(false)
+{
+}
+
+ScCsvControl::~ScCsvControl()
+{
+ if( mxAccessible.is() )
+ mxAccessible->dispose();
+ mxAccessible = nullptr; // lp#1566050: prevent cyclic reference zombies
+}
+
+// event handling -------------------------------------------------------------
+
+void ScCsvControl::GetFocus()
+{
+ weld::CustomWidgetController::GetFocus();
+ AccSendFocusEvent( true );
+}
+
+void ScCsvControl::LoseFocus()
+{
+ weld::CustomWidgetController::LoseFocus();
+ AccSendFocusEvent( false );
+}
+
+void ScCsvControl::AccSendFocusEvent( bool bFocused )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendFocusEvent( bFocused );
+}
+
+void ScCsvControl::AccSendCaretEvent()
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendCaretEvent();
+}
+
+void ScCsvControl::AccSendVisibleEvent()
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendVisibleEvent();
+}
+
+void ScCsvControl::AccSendSelectionEvent()
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendSelectionEvent();
+}
+
+void ScCsvControl::AccSendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendTableUpdateEvent( nFirstColumn, nLastColumn, bAllRows );
+}
+
+void ScCsvControl::AccSendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendInsertColumnEvent( nFirstColumn, nLastColumn );
+}
+
+void ScCsvControl::AccSendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( mxAccessible.is() )
+ mxAccessible->SendRemoveColumnEvent( nFirstColumn, nLastColumn );
+}
+
+// repaint helpers ------------------------------------------------------------
+
+void ScCsvControl::Repaint( bool bInvalidate )
+{
+ if( bInvalidate )
+ InvalidateGfx();
+ if( !IsNoRepaint() )
+ Execute( CSVCMD_REPAINT );
+}
+
+void ScCsvControl::DisableRepaint()
+{
+ ++mrData.mnNoRepaint;
+}
+
+void ScCsvControl::EnableRepaint()
+{
+ OSL_ENSURE( IsNoRepaint(), "ScCsvControl::EnableRepaint - invalid call" );
+ --mrData.mnNoRepaint;
+ Repaint();
+}
+
+// command handling -----------------------------------------------------------
+
+void ScCsvControl::Execute( ScCsvCmdType eType, sal_Int32 nParam1, sal_Int32 nParam2 )
+{
+ maCmd.Set( eType, nParam1, nParam2 );
+ maCmdHdl.Call( *this );
+}
+
+// layout helpers -------------------------------------------------------------
+
+sal_Int32 ScCsvControl::GetVisPosCount() const
+{
+ return (mrData.mnWinWidth - GetHdrWidth()) / GetCharWidth();
+}
+
+sal_Int32 ScCsvControl::GetMaxPosOffset() const
+{
+ return std::max<sal_Int32>( GetPosCount() - GetVisPosCount() + 2, 0 );
+}
+
+bool ScCsvControl::IsValidSplitPos( sal_Int32 nPos ) const
+{
+ return (0 < nPos) && (nPos < GetPosCount() );
+}
+
+bool ScCsvControl::IsVisibleSplitPos( sal_Int32 nPos ) const
+{
+ return IsValidSplitPos( nPos ) && (GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos());
+}
+
+sal_Int32 ScCsvControl::GetHdrX() const
+{
+ return IsRTL() ? (mrData.mnWinWidth - GetHdrWidth()) : 0;
+}
+
+sal_Int32 ScCsvControl::GetFirstX() const
+{
+ return IsRTL() ? 0 : GetHdrWidth();
+}
+
+sal_Int32 ScCsvControl::GetLastX() const
+{
+ return mrData.mnWinWidth - (IsRTL() ? GetHdrWidth() : 0) - 1;
+}
+
+sal_Int32 ScCsvControl::GetX( sal_Int32 nPos ) const
+{
+ return GetFirstX() + (nPos - GetFirstVisPos()) * GetCharWidth();
+}
+
+sal_Int32 ScCsvControl::GetPosFromX( sal_Int32 nX ) const
+{
+ return (nX - GetFirstX() + GetCharWidth() / 2) / GetCharWidth() + GetFirstVisPos();
+}
+
+sal_Int32 ScCsvControl::GetVisLineCount() const
+{
+ return (mrData.mnWinHeight - GetHdrHeight() - 2) / GetLineHeight() + 1;
+}
+
+sal_Int32 ScCsvControl::GetLastVisLine() const
+{
+ return std::min( GetFirstVisLine() + GetVisLineCount(), GetLineCount() ) - 1;
+}
+
+sal_Int32 ScCsvControl::GetMaxLineOffset() const
+{
+ return std::max<sal_Int32>( GetLineCount() - GetVisLineCount() + 1, 0 );
+}
+
+bool ScCsvControl::IsValidLine( sal_Int32 nLine ) const
+{
+ return (0 <= nLine) && (nLine < GetLineCount());
+}
+
+bool ScCsvControl::IsVisibleLine( sal_Int32 nLine ) const
+{
+ return IsValidLine( nLine ) && (GetFirstVisLine() <= nLine) && (nLine <= GetLastVisLine());
+}
+
+sal_Int32 ScCsvControl::GetY( sal_Int32 nLine ) const
+{
+ return GetHdrHeight() + (nLine - GetFirstVisLine()) * GetLineHeight();
+}
+
+sal_Int32 ScCsvControl::GetLineFromY( sal_Int32 nY ) const
+{
+ return (nY - GetHdrHeight()) / GetLineHeight() + GetFirstVisLine();
+}
+
+// static helpers -------------------------------------------------------------
+
+void ScCsvControl::ImplInvertRect( OutputDevice& rOutDev, const tools::Rectangle& rRect )
+{
+ rOutDev.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::RASTEROP );
+ rOutDev.SetLineColor( COL_BLACK );
+ rOutDev.SetFillColor( COL_BLACK );
+ rOutDev.SetRasterOp( RasterOp::Invert );
+ rOutDev.DrawRect( rRect );
+ rOutDev.Pop();
+}
+
+ScMoveMode ScCsvControl::GetHorzDirection( sal_uInt16 nCode, bool bHomeEnd )
+{
+ switch( nCode )
+ {
+ case KEY_LEFT: return MOVE_PREV;
+ case KEY_RIGHT: return MOVE_NEXT;
+ }
+ if( bHomeEnd ) switch( nCode )
+ {
+ case KEY_HOME: return MOVE_FIRST;
+ case KEY_END: return MOVE_LAST;
+ }
+ return MOVE_NONE;
+}
+
+ScMoveMode ScCsvControl::GetVertDirection( sal_uInt16 nCode, bool bHomeEnd )
+{
+ switch( nCode )
+ {
+ case KEY_UP: return MOVE_PREV;
+ case KEY_DOWN: return MOVE_NEXT;
+ case KEY_PAGEUP: return MOVE_PREVPAGE;
+ case KEY_PAGEDOWN: return MOVE_NEXTPAGE;
+ }
+ if( bHomeEnd ) switch( nCode )
+ {
+ case KEY_HOME: return MOVE_FIRST;
+ case KEY_END: return MOVE_LAST;
+ }
+ return MOVE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvgrid.cxx b/sc/source/ui/dbgui/csvgrid.cxx
new file mode 100644
index 0000000000..f81e510fda
--- /dev/null
+++ b/sc/source/ui/dbgui/csvgrid.cxx
@@ -0,0 +1,1416 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <csvgrid.hxx>
+#include <csvtablebox.hxx>
+
+#include <algorithm>
+#include <memory>
+
+#include <svtools/colorcfg.hxx>
+#include <sal/macros.h>
+#include <tools/poly.hxx>
+#include <scmod.hxx>
+#include <asciiopt.hxx>
+#include <impex.hxx>
+#include <AccessibleCsvControl.hxx>
+
+// *** edit engine ***
+#include <editeng/eeitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <editutil.hxx>
+// *** edit engine ***
+
+namespace {
+
+struct Func_SetType
+{
+ sal_Int32 mnType;
+ explicit Func_SetType( sal_Int32 nType ) : mnType( nType ) {}
+ void operator()( ScCsvColState& rState ) const
+ { rState.mnType = mnType; }
+};
+
+struct Func_Select
+{
+ bool mbSelect;
+ explicit Func_Select( bool bSelect ) : mbSelect( bSelect ) {}
+ void operator()( ScCsvColState& rState ) const
+ { rState.Select( mbSelect ); }
+};
+
+}
+
+ScCsvGrid::ScCsvGrid(const ScCsvLayoutData& rData, std::unique_ptr<weld::Menu> xPopup, ScCsvTableBox* pTableBox)
+ : ScCsvControl(rData)
+ , mpTableBox(pTableBox)
+ , mpBackgrDev( VclPtr<VirtualDevice>::Create() )
+ , mpGridDev( VclPtr<VirtualDevice>::Create() )
+ , mxPopup(std::move(xPopup))
+ , mpColorConfig( nullptr )
+ , mpEditEngine( new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ) )
+ , maColStates( 1 )
+ , maTypeNames( 1 )
+ , mnFirstImpLine( 0 )
+ , mnRecentSelCol( CSV_COLUMN_INVALID )
+ , mnMTCurrCol( SAL_MAX_UINT32 )
+ , mbTracking( false )
+ , mbMTSelecting( false )
+{
+ mpEditEngine->SetRefDevice( mpBackgrDev.get() );
+ mpEditEngine->SetRefMapMode( MapMode( MapUnit::MapPixel ) );
+ maEdEngSize = mpEditEngine->GetPaperSize();
+}
+
+void ScCsvGrid::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
+ maHeaderFont = Application::GetSettings().GetStyleSettings().GetAppFont();
+
+ // expand the point size of the desired font to the equivalent pixel size
+ weld::SetPointFont(rRefDevice, maHeaderFont);
+ maHeaderFont = rRefDevice.GetFont();
+
+ // Because this is an always LeftToRight layout widget the initial size of
+ // this widget needs to be smaller than the size of the parent scrolling
+ // window (ScCsvTableBox ctor) because in RTL mode the alignment is against
+ // the right edge of the parent, and if larger than the scrolling window
+ // the left edge will be lost. If this widget is smaller than the scrolling
+ // window it is stretched to fit the parent and the problem doesn't arise.
+ Size aInitialSize(10, 10);
+ if (comphelper::LibreOfficeKit::isActive())
+ aInitialSize = Size(-1, 150);
+ ScCsvControl::SetDrawingArea(pDrawingArea);
+ pDrawingArea->set_size_request(aInitialSize.Width(), aInitialSize.Height());
+ SetOutputSizePixel(aInitialSize);
+
+ EnableRTL( false ); // RTL
+
+ InitFonts();
+ ImplClearSplits();
+}
+
+ScCsvGrid::~ScCsvGrid()
+{
+ OSL_ENSURE(mpColorConfig, "the object hasn't been initialized properly");
+ if (mpColorConfig)
+ mpColorConfig->RemoveListener(this);
+ mpBackgrDev.disposeAndClear();
+ mpGridDev.disposeAndClear();
+}
+
+void
+ScCsvGrid::Init()
+{
+ OSL_PRECOND(!mpColorConfig, "the object has already been initialized");
+ mpColorConfig = &SC_MOD()->GetColorConfig();
+ InitColors();
+ mpColorConfig->AddListener(this);
+}
+
+// common grid handling -------------------------------------------------------
+
+void ScCsvGrid::UpdateLayoutData()
+{
+ DisableRepaint();
+ OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device();
+ rRefDevice.SetFont(maMonoFont);
+ Execute(CSVCMD_SETCHARWIDTH, rRefDevice.GetTextWidth(OUString('X')));
+ Execute(CSVCMD_SETLINEHEIGHT, rRefDevice.GetTextHeight() + 1);
+ rRefDevice.SetFont(maHeaderFont);
+ Execute(CSVCMD_SETHDRHEIGHT, rRefDevice.GetTextHeight() + 1);
+ UpdateOffsetX();
+ EnableRepaint();
+}
+
+void ScCsvGrid::UpdateOffsetX()
+{
+ sal_Int32 nLastLine = GetLastVisLine() + 1;
+ sal_Int32 nDigits = 2;
+ for (;;)
+ {
+ nLastLine /= 10;
+ if (!nLastLine)
+ break;
+ ++nDigits;
+ }
+ nDigits = std::max( nDigits, sal_Int32( 3 ) );
+ Execute(CSVCMD_SETHDRWIDTH, GetDrawingArea()->get_approximate_digit_width() * nDigits);
+}
+
+void ScCsvGrid::ApplyLayout( const ScCsvLayoutData& rOldData )
+{
+ ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData );
+ if( nDiff == ScCsvDiff::Equal ) return;
+
+ DisableRepaint();
+
+ if( nDiff & ScCsvDiff::RulerCursor )
+ {
+ ImplInvertCursor( rOldData.mnPosCursor );
+ ImplInvertCursor( GetRulerCursorPos() );
+ }
+
+ if( nDiff & ScCsvDiff::PosCount )
+ {
+ if( GetPosCount() < rOldData.mnPosCount )
+ {
+ SelectAll( false );
+ maSplits.RemoveRange( GetPosCount(), rOldData.mnPosCount );
+ }
+ else
+ maSplits.Remove( rOldData.mnPosCount );
+ maSplits.Insert( GetPosCount() );
+ maColStates.resize( maSplits.Count() - 1 );
+ }
+
+ if( nDiff & ScCsvDiff::LineOffset )
+ {
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ UpdateOffsetX();
+ }
+
+ ScCsvDiff nHVDiff = nDiff & (ScCsvDiff::HorizontalMask | ScCsvDiff::VerticalMask);
+ if( nHVDiff == ScCsvDiff::PosOffset )
+ ImplDrawHorzScrolled( rOldData.mnPosOffset );
+ else if( nHVDiff != ScCsvDiff::Equal )
+ InvalidateGfx();
+
+ EnableRepaint();
+
+ if( nDiff & (ScCsvDiff::PosOffset | ScCsvDiff::LineOffset) )
+ AccSendVisibleEvent();
+}
+
+void ScCsvGrid::SetFirstImportedLine( sal_Int32 nLine )
+{
+ ImplDrawFirstLineSep( false );
+ mnFirstImpLine = nLine;
+ ImplDrawFirstLineSep( true );
+ ImplDrawGridDev();
+ Repaint();
+}
+
+sal_Int32 ScCsvGrid::GetNoScrollCol( sal_Int32 nPos ) const
+{
+ sal_Int32 nNewPos = nPos;
+ if( nNewPos != CSV_POS_INVALID )
+ {
+ if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
+ nNewPos = GetFirstVisPos() + nScroll;
+ }
+ else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
+ nNewPos = GetLastVisPos() - nScroll - 1;
+ }
+ }
+ return nNewPos;
+}
+
+void ScCsvGrid::InitColors()
+{
+ OSL_PRECOND(mpColorConfig, "the object hasn't been initialized properly");
+ if ( !mpColorConfig )
+ return;
+ maBackColor = mpColorConfig->GetColorValue( ::svtools::DOCCOLOR ).nColor;
+ maGridColor = mpColorConfig->GetColorValue( ::svtools::CALCGRID ).nColor;
+ maGridPBColor = mpColorConfig->GetColorValue( ::svtools::CALCPAGEBREAK ).nColor;
+ maAppBackColor = mpColorConfig->GetColorValue( ::svtools::APPBACKGROUND ).nColor;
+ maTextColor = mpColorConfig->GetColorValue( ::svtools::FONTCOLOR, false ).nColor;
+
+ // tdf#147386 If Automatic font color is used, then check background color and use Black/White as font color
+ if ( maTextColor == COL_AUTO )
+ {
+ if ( maBackColor.IsDark() )
+ maTextColor = COL_WHITE;
+ else
+ maTextColor = COL_BLACK;
+ }
+
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ maHeaderBackColor = rSett.GetFaceColor();
+ maHeaderGridColor = rSett.GetDarkShadowColor();
+ maHeaderTextColor = rSett.GetButtonTextColor();
+ maSelectColor = rSett.GetActiveColor();
+
+ InvalidateGfx();
+}
+
+void ScCsvGrid::InitFonts()
+{
+ maMonoFont = OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::NONE );
+ maMonoFont.SetFontSize( Size( maMonoFont.GetFontSize().Width(), maHeaderFont.GetFontSize().Height() ) );
+
+ /* *** Set edit engine defaults ***
+ maMonoFont for Latin script, smaller default font for Asian and Complex script. */
+
+ // get default fonts
+ SvxFontItem aLatinItem( EE_CHAR_FONTINFO );
+ SvxFontItem aAsianItem( EE_CHAR_FONTINFO_CJK );
+ SvxFontItem aComplexItem( EE_CHAR_FONTINFO_CTL );
+ ::GetDefaultFonts( aLatinItem, aAsianItem, aComplexItem );
+
+ // create item set for defaults
+ SfxItemSet aDefSet( mpEditEngine->GetEmptyItemSet() );
+ EditEngine::SetFontInfoInItemSet( aDefSet, maMonoFont );
+ aDefSet.Put( aAsianItem );
+ aDefSet.Put( aComplexItem );
+
+ // set Asian/Complex font size to height of character in Latin font
+ sal_uLong nFontHt = static_cast< sal_uLong >( maMonoFont.GetFontSize().Height() );
+ aDefSet.Put( SvxFontHeightItem( nFontHt, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ aDefSet.Put( SvxFontHeightItem( nFontHt, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ // copy other items from default font
+ const SfxPoolItem& rWeightItem = aDefSet.Get( EE_CHAR_WEIGHT );
+ std::unique_ptr<SfxPoolItem> pNewItem(rWeightItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK);
+ aDefSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL);
+ aDefSet.Put( *pNewItem );
+ const SfxPoolItem& rItalicItem = aDefSet.Get( EE_CHAR_ITALIC );
+ pNewItem.reset(rItalicItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_ITALIC_CJK);
+ aDefSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_ITALIC_CTL);
+ aDefSet.Put( *pNewItem );
+ const SfxPoolItem& rLangItem = aDefSet.Get( EE_CHAR_LANGUAGE );
+ pNewItem.reset(rLangItem.Clone());
+ pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK);
+ aDefSet.Put( *pNewItem );
+ pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL);
+ aDefSet.Put( *pNewItem );
+
+ mpEditEngine->SetDefaults( aDefSet );
+ InvalidateGfx();
+}
+
+void ScCsvGrid::InitSizeData()
+{
+ maWinSize = GetOutputSizePixel();
+ mpBackgrDev->SetOutputSizePixel( maWinSize );
+ mpGridDev->SetOutputSizePixel( maWinSize );
+ InvalidateGfx();
+}
+
+// split handling -------------------------------------------------------------
+
+void ScCsvGrid::InsertSplit( sal_Int32 nPos )
+{
+ if( ImplInsertSplit( nPos ) )
+ {
+ DisableRepaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ ImplDrawColumn( nColIx - 1 );
+ ImplDrawColumn( nColIx );
+ ValidateGfx(); // performance: do not redraw all columns
+ EnableRepaint();
+ }
+}
+
+void ScCsvGrid::RemoveSplit( sal_Int32 nPos )
+{
+ if( ImplRemoveSplit( nPos ) )
+ {
+ DisableRepaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ ImplDrawColumn( GetColumnFromPos( nPos ) );
+ ValidateGfx(); // performance: do not redraw all columns
+ EnableRepaint();
+ }
+}
+
+void ScCsvGrid::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
+{
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ if( nColIx == CSV_COLUMN_INVALID )
+ return;
+
+ DisableRepaint();
+ if( (GetColumnPos( nColIx - 1 ) < nNewPos) && (nNewPos < GetColumnPos( nColIx + 1 )) )
+ {
+ // move a split in the range between 2 others -> keep selection state of both columns
+ maSplits.Remove( nPos );
+ maSplits.Insert( nNewPos );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ ImplDrawColumn( nColIx - 1 );
+ ImplDrawColumn( nColIx );
+ ValidateGfx(); // performance: do not redraw all columns
+ AccSendTableUpdateEvent( nColIx - 1, nColIx );
+ }
+ else
+ {
+ ImplRemoveSplit( nPos );
+ ImplInsertSplit( nNewPos );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ }
+ EnableRepaint();
+}
+
+void ScCsvGrid::RemoveAllSplits()
+{
+ DisableRepaint();
+ ImplClearSplits();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ EnableRepaint();
+}
+
+void ScCsvGrid::SetSplits( const ScCsvSplits& rSplits )
+{
+ DisableRepaint();
+ ImplClearSplits();
+ sal_uInt32 nCount = rSplits.Count();
+ for( sal_uInt32 nIx = 0; nIx < nCount; ++nIx )
+ maSplits.Insert( rSplits[ nIx ] );
+ maColStates.clear();
+ maColStates.resize( maSplits.Count() - 1 );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ Execute( CSVCMD_UPDATECELLTEXTS );
+ EnableRepaint();
+}
+
+bool ScCsvGrid::ImplInsertSplit( sal_Int32 nPos )
+{
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ bool bRet = (nColIx < GetColumnCount()) && maSplits.Insert( nPos );
+ if( bRet )
+ {
+ ScCsvColState aState( GetColumnType( nColIx ) );
+ aState.Select( IsSelected( nColIx ) && IsSelected( nColIx + 1 ) );
+ maColStates.insert( maColStates.begin() + nColIx + 1, aState );
+ AccSendInsertColumnEvent( nColIx + 1, nColIx + 1 );
+ AccSendTableUpdateEvent( nColIx, nColIx );
+ }
+ return bRet;
+}
+
+bool ScCsvGrid::ImplRemoveSplit( sal_Int32 nPos )
+{
+ bool bRet = maSplits.Remove( nPos );
+ if( bRet )
+ {
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ bool bSel = IsSelected( nColIx ) || IsSelected( nColIx + 1 );
+ maColStates.erase( maColStates.begin() + nColIx + 1 );
+ maColStates[ nColIx ].Select( bSel );
+ AccSendRemoveColumnEvent( nColIx + 1, nColIx + 1 );
+ AccSendTableUpdateEvent( nColIx, nColIx );
+ }
+ return bRet;
+}
+
+void ScCsvGrid::ImplClearSplits()
+{
+ sal_uInt32 nColumns = GetColumnCount();
+ maSplits.Clear();
+ maSplits.Insert( 0 );
+ maSplits.Insert( GetPosCount() );
+ maColStates.resize( 1 );
+ InvalidateGfx();
+ AccSendRemoveColumnEvent( 1, nColumns - 1 );
+}
+
+// columns/column types -------------------------------------------------------
+
+sal_uInt32 ScCsvGrid::GetFirstVisColumn() const
+{
+ return GetColumnFromPos( GetFirstVisPos() );
+}
+
+sal_uInt32 ScCsvGrid::GetLastVisColumn() const
+{
+ return GetColumnFromPos( std::min( GetLastVisPos(), GetPosCount() ) - 1 );
+}
+
+bool ScCsvGrid::IsValidColumn( sal_uInt32 nColIndex ) const
+{
+ return nColIndex < GetColumnCount();
+}
+
+bool ScCsvGrid::IsVisibleColumn( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) &&
+ (GetColumnPos( nColIndex ) < GetLastVisPos()) &&
+ (GetFirstVisPos() < GetColumnPos( nColIndex + 1 ));
+}
+
+sal_Int32 ScCsvGrid::GetColumnX( sal_uInt32 nColIndex ) const
+{
+ return GetX( GetColumnPos( nColIndex ) );
+}
+
+sal_uInt32 ScCsvGrid::GetColumnFromX( sal_Int32 nX ) const
+{
+ sal_Int32 nPos = (nX - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
+ return ((GetFirstVisPos() <= nPos) && (nPos <= GetLastVisPos())) ?
+ GetColumnFromPos( nPos ) : CSV_COLUMN_INVALID;
+}
+
+sal_uInt32 ScCsvGrid::GetColumnFromPos( sal_Int32 nPos ) const
+{
+ return maSplits.UpperBound( nPos );
+}
+
+sal_Int32 ScCsvGrid::GetColumnWidth( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) ? (GetColumnPos( nColIndex + 1 ) - GetColumnPos( nColIndex )) : 0;
+}
+
+void ScCsvGrid::SetColumnStates( ScCsvColStateVec&& rStates )
+{
+ maColStates = std::move(rStates);
+ maColStates.resize( maSplits.Count() - 1 );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ AccSendTableUpdateEvent( 0, GetColumnCount(), false );
+ AccSendSelectionEvent();
+}
+
+sal_Int32 ScCsvGrid::GetColumnType( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) ? maColStates[ nColIndex ].mnType : CSV_TYPE_NOSELECTION;
+}
+
+void ScCsvGrid::SetColumnType( sal_uInt32 nColIndex, sal_Int32 nColType )
+{
+ if( IsValidColumn( nColIndex ) )
+ {
+ maColStates[ nColIndex ].mnType = nColType;
+ AccSendTableUpdateEvent( nColIndex, nColIndex, false );
+ }
+}
+
+sal_Int32 ScCsvGrid::GetSelColumnType() const
+{
+ sal_uInt32 nColIx = GetFirstSelected();
+ if( nColIx == CSV_COLUMN_INVALID )
+ return CSV_TYPE_NOSELECTION;
+
+ sal_Int32 nType = GetColumnType( nColIx );
+ while( (nColIx != CSV_COLUMN_INVALID) && (nType != CSV_TYPE_MULTI) )
+ {
+ if( nType != GetColumnType( nColIx ) )
+ nType = CSV_TYPE_MULTI;
+ nColIx = GetNextSelected( nColIx );
+ }
+ return nType;
+}
+
+void ScCsvGrid::SetSelColumnType( sal_Int32 nType )
+{
+ if( (nType != CSV_TYPE_MULTI) && (nType != CSV_TYPE_NOSELECTION) )
+ {
+ for( sal_uInt32 nColIx = GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = GetNextSelected( nColIx ) )
+ SetColumnType( nColIx, nType );
+ Repaint( true );
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ }
+}
+
+void ScCsvGrid::SetTypeNames( std::vector<OUString>&& rTypeNames )
+{
+ OSL_ENSURE( !rTypeNames.empty(), "ScCsvGrid::SetTypeNames - vector is empty" );
+ maTypeNames = std::move(rTypeNames);
+ Repaint( true );
+
+ mxPopup->clear();
+ sal_uInt32 nCount = maTypeNames.size();
+ for (sal_uInt32 nIx = 0; nIx < nCount; ++nIx)
+ mxPopup->append(OUString::number(nIx), maTypeNames[nIx]);
+
+ ::std::for_each( maColStates.begin(), maColStates.end(), Func_SetType( CSV_TYPE_DEFAULT ) );
+}
+
+OUString ScCsvGrid::GetColumnTypeName( sal_uInt32 nColIndex ) const
+{
+ sal_uInt32 nTypeIx = static_cast< sal_uInt32 >( GetColumnType( nColIndex ) );
+ return (nTypeIx < maTypeNames.size()) ? maTypeNames[ nTypeIx ] : OUString();
+}
+
+static sal_uInt8 lcl_GetExtColumnType( sal_Int32 nIntType )
+{
+ static const sal_uInt8 pExtTypes[] =
+ { SC_COL_STANDARD, SC_COL_TEXT, SC_COL_DMY, SC_COL_MDY, SC_COL_YMD, SC_COL_ENGLISH, SC_COL_SKIP };
+ static const sal_Int32 nExtTypeCount = SAL_N_ELEMENTS(pExtTypes);
+ return pExtTypes[ ((0 <= nIntType) && (nIntType < nExtTypeCount)) ? nIntType : 0 ];
+}
+
+void ScCsvGrid::FillColumnDataSep( ScAsciiOptions& rOptions ) const
+{
+ sal_uInt32 nCount = GetColumnCount();
+ ScCsvExpDataVec aDataVec;
+
+ for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
+ {
+ if( GetColumnType( nColIx ) != CSV_TYPE_DEFAULT )
+ // 1-based column index
+ aDataVec.emplace_back(
+ static_cast< sal_Int32 >( nColIx + 1 ),
+ lcl_GetExtColumnType( GetColumnType( nColIx ) ) );
+ }
+ rOptions.SetColumnInfo( aDataVec );
+}
+
+void ScCsvGrid::FillColumnDataFix( ScAsciiOptions& rOptions ) const
+{
+ sal_uInt32 nCount = std::min( GetColumnCount(), static_cast<sal_uInt32>(MAXCOLCOUNT) );
+ ScCsvExpDataVec aDataVec( nCount + 1 );
+
+ for( sal_uInt32 nColIx = 0; nColIx < nCount; ++nColIx )
+ {
+ ScCsvExpData& rData = aDataVec[ nColIx ];
+ rData.mnIndex = GetColumnPos( nColIx );
+ rData.mnType = lcl_GetExtColumnType( GetColumnType( nColIx ) );
+ }
+ aDataVec[ nCount ].mnIndex = SAL_MAX_INT32;
+ aDataVec[ nCount ].mnType = SC_COL_SKIP;
+ rOptions.SetColumnInfo( aDataVec );
+}
+
+void ScCsvGrid::ScrollVertRel( ScMoveMode eDir )
+{
+ sal_Int32 nLine = GetFirstVisLine();
+ switch( eDir )
+ {
+ case MOVE_PREV: --nLine; break;
+ case MOVE_NEXT: ++nLine; break;
+ case MOVE_FIRST: nLine = 0; break;
+ case MOVE_LAST: nLine = GetMaxLineOffset(); break;
+ case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 2; break;
+ case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 2; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ Execute( CSVCMD_SETLINEOFFSET, nLine );
+}
+
+void ScCsvGrid::ExecutePopup( const Point& rPos )
+{
+ OUString sItemId = mxPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1)));
+ if (!sItemId.isEmpty()) // empty = cancelled
+ Execute(CSVCMD_SETCOLUMNTYPE, sItemId.toInt32());
+}
+
+// selection handling ---------------------------------------------------------
+
+bool ScCsvGrid::IsSelected( sal_uInt32 nColIndex ) const
+{
+ return IsValidColumn( nColIndex ) && maColStates[ nColIndex ].IsSelected();
+}
+
+sal_uInt32 ScCsvGrid::GetFirstSelected() const
+{
+ return IsSelected( 0 ) ? 0 : GetNextSelected( 0 );
+}
+
+sal_uInt32 ScCsvGrid::GetNextSelected( sal_uInt32 nFromIndex ) const
+{
+ sal_uInt32 nColCount = GetColumnCount();
+ for( sal_uInt32 nColIx = nFromIndex + 1; nColIx < nColCount; ++nColIx )
+ if( IsSelected( nColIx ) )
+ return nColIx;
+ return CSV_COLUMN_INVALID;
+}
+
+void ScCsvGrid::Select( sal_uInt32 nColIndex, bool bSelect )
+{
+ if( IsValidColumn( nColIndex ) )
+ {
+ maColStates[ nColIndex ].Select( bSelect );
+ ImplDrawColumnSelection( nColIndex );
+ Repaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ if( bSelect )
+ mnRecentSelCol = nColIndex;
+ AccSendSelectionEvent();
+ }
+}
+
+void ScCsvGrid::ToggleSelect( sal_uInt32 nColIndex )
+{
+ Select( nColIndex, !IsSelected( nColIndex ) );
+}
+
+void ScCsvGrid::SelectRange( sal_uInt32 nColIndex1, sal_uInt32 nColIndex2, bool bSelect )
+{
+ if( nColIndex1 == CSV_COLUMN_INVALID )
+ Select( nColIndex2 );
+ else if( nColIndex2 == CSV_COLUMN_INVALID )
+ Select( nColIndex1 );
+ else if( nColIndex1 > nColIndex2 )
+ {
+ SelectRange( nColIndex2, nColIndex1, bSelect );
+ if( bSelect )
+ mnRecentSelCol = nColIndex1;
+ }
+ else if( IsValidColumn( nColIndex1 ) && IsValidColumn( nColIndex2 ) )
+ {
+ for( sal_uInt32 nColIx = nColIndex1; nColIx <= nColIndex2; ++nColIx )
+ {
+ maColStates[ nColIx ].Select( bSelect );
+ ImplDrawColumnSelection( nColIx );
+ }
+ Repaint();
+ Execute( CSVCMD_EXPORTCOLUMNTYPE );
+ if( bSelect )
+ mnRecentSelCol = nColIndex1;
+ AccSendSelectionEvent();
+ }
+}
+
+void ScCsvGrid::SelectAll( bool bSelect )
+{
+ SelectRange( 0, GetColumnCount() - 1, bSelect );
+}
+
+void ScCsvGrid::MoveCursor( sal_uInt32 nColIndex )
+{
+ DisableRepaint();
+ if( IsValidColumn( nColIndex ) )
+ {
+ sal_Int32 nPosBeg = GetColumnPos( nColIndex );
+ sal_Int32 nPosEnd = GetColumnPos( nColIndex + 1 );
+ sal_Int32 nMinPos = std::max( nPosBeg - CSV_SCROLL_DIST, sal_Int32( 0 ) );
+ sal_Int32 nMaxPos = std::min( nPosEnd - GetVisPosCount() + CSV_SCROLL_DIST + sal_Int32( 1 ), nMinPos );
+ if( nPosBeg - CSV_SCROLL_DIST + 1 <= GetFirstVisPos() )
+ Execute( CSVCMD_SETPOSOFFSET, nMinPos );
+ else if( nPosEnd + CSV_SCROLL_DIST >= GetLastVisPos() )
+ Execute( CSVCMD_SETPOSOFFSET, nMaxPos );
+ }
+ Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
+ EnableRepaint();
+}
+
+void ScCsvGrid::MoveCursorRel( ScMoveMode eDir )
+{
+ if( GetFocusColumn() == CSV_COLUMN_INVALID )
+ return;
+
+ switch( eDir )
+ {
+ case MOVE_FIRST:
+ MoveCursor( 0 );
+ break;
+ case MOVE_LAST:
+ MoveCursor( GetColumnCount() - 1 );
+ break;
+ case MOVE_PREV:
+ if( GetFocusColumn() > 0 )
+ MoveCursor( GetFocusColumn() - 1 );
+ break;
+ case MOVE_NEXT:
+ if( GetFocusColumn() < GetColumnCount() - 1 )
+ MoveCursor( GetFocusColumn() + 1 );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScCsvGrid::ImplClearSelection()
+{
+ ::std::for_each( maColStates.begin(), maColStates.end(), Func_Select( false ) );
+ ImplDrawGridDev();
+}
+
+void ScCsvGrid::DoSelectAction( sal_uInt32 nColIndex, sal_uInt16 nModifier )
+{
+ if( !(nModifier & KEY_MOD1) )
+ ImplClearSelection();
+ if( nModifier & KEY_SHIFT ) // SHIFT always expands
+ SelectRange( mnRecentSelCol, nColIndex );
+ else if( !(nModifier & KEY_MOD1) ) // no SHIFT/CTRL always selects 1 column
+ Select( nColIndex );
+ else if( mbTracking ) // CTRL in tracking does not toggle
+ Select( nColIndex, mbMTSelecting );
+ else // CTRL only toggles
+ ToggleSelect( nColIndex );
+ Execute( CSVCMD_MOVEGRIDCURSOR, GetColumnPos( nColIndex ) );
+}
+
+// cell contents --------------------------------------------------------------
+
+void ScCsvGrid::ImplSetTextLineSep(
+ sal_Int32 nLine, const OUString& rTextLine,
+ const OUString& rSepChars, sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace )
+{
+ if( nLine < GetFirstVisLine() ) return;
+
+ sal_uInt32 nLineIx = nLine - GetFirstVisLine();
+ while( maTexts.size() <= nLineIx )
+ maTexts.emplace_back( );
+ std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
+ rStrVec.clear();
+
+ // scan for separators
+ OUString aCellText;
+ const sal_Unicode* pSepChars = rSepChars.getStr();
+ const sal_Unicode* pChar = rTextLine.getStr();
+ sal_uInt32 nColIx = 0;
+
+ while( *pChar && (nColIx < sal::static_int_cast<sal_uInt32>(CSV_MAXCOLCOUNT)) )
+ {
+ // scan for next cell text
+ bool bIsQuoted = false;
+ bool bOverflowCell = false;
+ pChar = ScImportExport::ScanNextFieldFromString( pChar, aCellText,
+ cTextSep, pSepChars, bMergeSep, bIsQuoted, bOverflowCell, bRemoveSpace );
+ /* TODO: signal overflow somewhere in UI */
+
+ // update column width
+ sal_Int32 nWidth = std::max( CSV_MINCOLWIDTH, ScImportExport::CountVisualWidth( aCellText ) + 1 );
+ if( IsValidColumn( nColIx ) )
+ {
+ // expand existing column
+ sal_Int32 nDiff = nWidth - GetColumnWidth( nColIx );
+ if( nDiff > 0 )
+ {
+ Execute( CSVCMD_SETPOSCOUNT, GetPosCount() + nDiff );
+ for( sal_uInt32 nSplitIx = GetColumnCount() - 1; nSplitIx > nColIx; --nSplitIx )
+ {
+ sal_Int32 nPos = maSplits[ nSplitIx ];
+ maSplits.Remove( nPos );
+ maSplits.Insert( nPos + nDiff );
+ }
+ }
+ }
+ else
+ {
+ // append new column
+ sal_Int32 nLastPos = GetPosCount();
+ Execute( CSVCMD_SETPOSCOUNT, nLastPos + nWidth );
+ ImplInsertSplit( nLastPos );
+ }
+
+ if( aCellText.getLength() <= CSV_MAXSTRLEN )
+ rStrVec.push_back( aCellText );
+ else
+ rStrVec.push_back( aCellText.copy( 0, CSV_MAXSTRLEN ) );
+ ++nColIx;
+ }
+ InvalidateGfx();
+}
+
+void ScCsvGrid::ImplSetTextLineFix( sal_Int32 nLine, const OUString& rTextLine )
+{
+ if( nLine < GetFirstVisLine() ) return;
+
+ sal_Int32 nWidth = ScImportExport::CountVisualWidth( rTextLine );
+ if( nWidth > GetPosCount() )
+ Execute( CSVCMD_SETPOSCOUNT, nWidth );
+
+ sal_uInt32 nLineIx = nLine - GetFirstVisLine();
+ while( maTexts.size() <= nLineIx )
+ maTexts.emplace_back( );
+
+ std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
+ rStrVec.clear();
+ sal_uInt32 nColCount = GetColumnCount();
+ sal_Int32 nStrLen = rTextLine.getLength();
+ sal_Int32 nStrIx = 0;
+ for( sal_uInt32 nColIx = 0; (nColIx < nColCount) && (nStrIx < nStrLen); ++nColIx )
+ {
+ sal_Int32 nColWidth = GetColumnWidth( nColIx );
+ sal_Int32 nLastIx = nStrIx;
+ ScImportExport::CountVisualWidth( rTextLine, nLastIx, nColWidth );
+ sal_Int32 nLen = std::min( CSV_MAXSTRLEN, nLastIx - nStrIx );
+ rStrVec.push_back( rTextLine.copy( nStrIx, nLen ) );
+ nStrIx = nStrIx + nLen;
+ }
+ InvalidateGfx();
+}
+
+OUString ScCsvGrid::GetCellText( sal_uInt32 nColIndex, sal_Int32 nLine ) const
+{
+ if( nLine < GetFirstVisLine() ) return OUString();
+
+ sal_uInt32 nLineIx = nLine - GetFirstVisLine();
+ if( nLineIx >= maTexts.size() ) return OUString();
+
+ const std::vector<OUString>& rStrVec = maTexts[ nLineIx ];
+ if( nColIndex >= rStrVec.size() ) return OUString();
+
+ return rStrVec[ nColIndex ];
+}
+
+// event handling -------------------------------------------------------------
+
+void ScCsvGrid::Resize()
+{
+ mpTableBox->InitControls();
+
+ ScCsvControl::Resize();
+ InitSizeData();
+ Execute( CSVCMD_UPDATECELLTEXTS );
+}
+
+void ScCsvGrid::GetFocus()
+{
+ ScCsvControl::GetFocus();
+ Execute( CSVCMD_MOVEGRIDCURSOR, GetNoScrollCol( GetGridCursorPos() ) );
+ Repaint();
+}
+
+void ScCsvGrid::LoseFocus()
+{
+ ScCsvControl::LoseFocus();
+ Repaint();
+}
+
+bool ScCsvGrid::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ DisableRepaint();
+ if( !HasFocus() )
+ GrabFocus();
+
+ Point aPos( rMEvt.GetPosPixel() );
+ sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
+
+ if( rMEvt.IsLeft() )
+ {
+ if( (GetFirstX() > aPos.X()) || (aPos.X() > GetLastX()) ) // in header column
+ {
+ if( aPos.Y() <= GetHdrHeight() )
+ SelectAll();
+ }
+ else if( IsValidColumn( nColIx ) )
+ {
+ DoSelectAction( nColIx, rMEvt.GetModifier() );
+ mnMTCurrCol = nColIx;
+ mbMTSelecting = IsSelected( nColIx );
+ mbTracking = true;
+ }
+ }
+ EnableRepaint();
+ return true;
+}
+
+bool ScCsvGrid::MouseButtonUp( const MouseEvent& )
+{
+ mbTracking = false;
+ return true;
+}
+
+bool ScCsvGrid::MouseMove( const MouseEvent& rMEvt )
+{
+ if (!mbTracking)
+ return true;
+
+ DisableRepaint();
+
+ sal_Int32 nPos = (rMEvt.GetPosPixel().X() - GetFirstX()) / GetCharWidth() + GetFirstVisPos();
+ // on mouse tracking: keep position valid
+ nPos = std::clamp( nPos, sal_Int32(0), GetPosCount() - 1 );
+ Execute( CSVCMD_MAKEPOSVISIBLE, nPos );
+
+ sal_uInt32 nColIx = GetColumnFromPos( nPos );
+ if( mnMTCurrCol != nColIx )
+ {
+ DoSelectAction( nColIx, rMEvt.GetModifier() );
+ mnMTCurrCol = nColIx;
+ }
+ EnableRepaint();
+
+ return true;
+}
+
+bool ScCsvGrid::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKCode.GetCode();
+ bool bShift = rKCode.IsShift();
+ bool bMod1 = rKCode.IsMod1();
+
+ if( !rKCode.IsMod2() )
+ {
+ ScMoveMode eHDir = GetHorzDirection( nCode, !bMod1 );
+ ScMoveMode eVDir = GetVertDirection( nCode, bMod1 );
+
+ if( eHDir != MOVE_NONE )
+ {
+ DisableRepaint();
+ MoveCursorRel( eHDir );
+ if( !bMod1 )
+ ImplClearSelection();
+ if( bShift )
+ SelectRange( mnRecentSelCol, GetFocusColumn() );
+ else if( !bMod1 )
+ Select( GetFocusColumn() );
+ EnableRepaint();
+ }
+ else if( eVDir != MOVE_NONE )
+ ScrollVertRel( eVDir );
+ else if( nCode == KEY_SPACE )
+ {
+ if( !bMod1 )
+ ImplClearSelection();
+ if( bShift )
+ SelectRange( mnRecentSelCol, GetFocusColumn() );
+ else if( bMod1 )
+ ToggleSelect( GetFocusColumn() );
+ else
+ Select( GetFocusColumn() );
+ }
+ else if( !bShift && bMod1 )
+ {
+ if( nCode == KEY_A )
+ SelectAll();
+ else if( (KEY_1 <= nCode) && (nCode <= KEY_9) )
+ {
+ sal_uInt32 nType = nCode - KEY_1;
+ if( nType < maTypeNames.size() )
+ Execute( CSVCMD_SETCOLUMNTYPE, nType );
+ }
+ }
+ }
+
+ return rKCode.GetGroup() == KEYGROUP_CURSOR;
+}
+
+bool ScCsvGrid::Command( const CommandEvent& rCEvt )
+{
+ bool bConsumed = true;
+ switch( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ if( rCEvt.IsMouseEvent() )
+ {
+ Point aPos( rCEvt.GetMousePosPixel() );
+ sal_uInt32 nColIx = GetColumnFromX( aPos.X() );
+ if( IsValidColumn( nColIx ) && (GetFirstX() <= aPos.X()) && (aPos.X() <= GetLastX()) )
+ {
+ if( !IsSelected( nColIx ) )
+ DoSelectAction( nColIx, 0 ); // focus & select
+ ExecutePopup( aPos );
+ }
+ }
+ else
+ {
+ sal_uInt32 nColIx = GetFocusColumn();
+ if( !IsSelected( nColIx ) )
+ Select( nColIx );
+ sal_Int32 nX1 = std::max( GetColumnX( nColIx ), GetFirstX() );
+ sal_Int32 nX2 = std::min( GetColumnX( nColIx + 1 ), GetWidth() );
+ ExecutePopup( Point( (nX1 + nX2) / 2, GetHeight() / 2 ) );
+ }
+ break;
+ }
+ case CommandEventId::Wheel:
+ {
+ tools::Rectangle aRect( Point(), maWinSize );
+ if( aRect.Contains( rCEvt.GetMousePosPixel() ) )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if( pData && (pData->GetMode() == CommandWheelMode::SCROLL) && !pData->IsHorz() )
+ Execute( CSVCMD_SETLINEOFFSET, GetFirstVisLine() - pData->GetNotchDelta() );
+ }
+ break;
+ }
+ default:
+ bConsumed = false;
+ break;
+ }
+ return bConsumed;
+}
+
+void ScCsvGrid::StyleUpdated()
+{
+ InitColors();
+ InitFonts();
+ UpdateLayoutData();
+ Execute( CSVCMD_UPDATECELLTEXTS );
+
+ ScCsvControl::StyleUpdated();
+}
+
+void ScCsvGrid::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints )
+{
+ InitColors();
+ Repaint();
+}
+
+// painting -------------------------------------------------------------------
+
+void ScCsvGrid::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ ImplRedraw(rRenderContext);
+}
+
+void ScCsvGrid::ImplRedraw(vcl::RenderContext& rRenderContext)
+{
+ if( IsVisible() )
+ {
+ if( !IsValidGfx() )
+ {
+ ValidateGfx();
+ ImplDrawBackgrDev();
+ ImplDrawGridDev();
+ }
+ rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpGridDev );
+ }
+}
+
+EditEngine* ScCsvGrid::GetEditEngine()
+{
+ return mpEditEngine.get();
+}
+
+void ScCsvGrid::ImplSetColumnClipRegion( OutputDevice& rOutDev, sal_uInt32 nColIndex )
+{
+ rOutDev.SetClipRegion( vcl::Region( tools::Rectangle(
+ std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1, 0,
+ std::min( GetColumnX( nColIndex + 1 ), GetLastX() ), GetHeight() - 1 ) ) );
+}
+
+void ScCsvGrid::ImplDrawColumnHeader( OutputDevice& rOutDev, sal_uInt32 nColIndex, Color aFillColor )
+{
+ sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
+ sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
+ sal_Int32 nHdrHt = GetHdrHeight();
+
+ rOutDev.SetLineColor();
+ rOutDev.SetFillColor( aFillColor );
+ rOutDev.DrawRect( tools::Rectangle( nX1, 0, nX2, nHdrHt ) );
+
+ rOutDev.SetFont( maHeaderFont );
+ rOutDev.SetTextColor( maHeaderTextColor );
+ rOutDev.SetTextFillColor();
+ rOutDev.DrawText( Point( nX1 + 1, 0 ), GetColumnTypeName( nColIndex ) );
+
+ rOutDev.SetLineColor( maHeaderGridColor );
+ rOutDev.DrawLine( Point( nX1, nHdrHt ), Point( nX2, nHdrHt ) );
+ rOutDev.DrawLine( Point( nX2, 0 ), Point( nX2, nHdrHt ) );
+}
+
+void ScCsvGrid::ImplDrawCellText( const Point& rPos, const OUString& rText )
+{
+ OUString aPlainText = rText.replaceAll( "\t", " " );
+ aPlainText = aPlainText.replaceAll( "\n", " " );
+ mpEditEngine->SetPaperSize( maEdEngSize );
+ mpEditEngine->SetTextCurrentDefaults(aPlainText);
+ mpEditEngine->Draw(*mpBackgrDev, rPos);
+
+ sal_Int32 nCharIx = 0;
+ while( (nCharIx = rText.indexOf( '\t', nCharIx )) != -1 )
+ {
+ sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
+ sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
+ sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
+ Color aColor( maTextColor );
+ mpBackgrDev->SetLineColor( aColor );
+ mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
+ mpBackgrDev->DrawLine( Point( nX2 - 2, nY - 2 ), Point( nX2, nY ) );
+ mpBackgrDev->DrawLine( Point( nX2 - 2, nY + 2 ), Point( nX2, nY ) );
+ ++nCharIx;
+ }
+ nCharIx = 0;
+ while( (nCharIx = rText.indexOf( '\n', nCharIx )) != -1 )
+ {
+ sal_Int32 nX1 = rPos.X() + GetCharWidth() * nCharIx;
+ sal_Int32 nX2 = nX1 + GetCharWidth() - 2;
+ sal_Int32 nY = rPos.Y() + GetLineHeight() / 2;
+ Color aColor( maTextColor );
+ mpBackgrDev->SetLineColor( aColor );
+ mpBackgrDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
+ mpBackgrDev->DrawLine( Point( nX1 + 2, nY - 2 ), Point( nX1, nY ) );
+ mpBackgrDev->DrawLine( Point( nX1 + 2, nY + 2 ), Point( nX1, nY ) );
+ mpBackgrDev->DrawLine( Point( nX2, nY - 2 ), Point( nX2, nY ) );
+ ++nCharIx;
+ }
+}
+
+void ScCsvGrid::ImplDrawFirstLineSep( bool bSet )
+{
+ if( IsVisibleLine( mnFirstImpLine ) && (mnFirstImpLine != GetFirstVisLine() ) )
+ {
+ sal_Int32 nY = GetY( mnFirstImpLine );
+ sal_Int32 nX = std::min( GetColumnX( GetLastVisColumn() + 1 ), GetLastX() );
+ mpBackgrDev->SetLineColor( bSet ? maGridPBColor : maGridColor );
+ mpBackgrDev->DrawLine( Point( GetFirstX() + 1, nY ), Point( nX, nY ) );
+ }
+}
+
+void ScCsvGrid::ImplDrawColumnBackgr( sal_uInt32 nColIndex )
+{
+ if( !IsVisibleColumn( nColIndex ) )
+ return;
+
+ ImplSetColumnClipRegion( *mpBackgrDev, nColIndex );
+
+ // grid
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maBackColor );
+ sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
+ sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
+ sal_Int32 nY2 = GetY( GetLastVisLine() + 1 );
+ sal_Int32 nHdrHt = GetHdrHeight();
+ tools::Rectangle aRect( nX1, nHdrHt, nX2, nY2 );
+ mpBackgrDev->DrawRect( aRect );
+ mpBackgrDev->SetLineColor( maGridColor );
+ mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines );
+ mpBackgrDev->DrawLine( Point( nX2, nHdrHt ), Point( nX2, nY2 ) );
+ ImplDrawFirstLineSep( true );
+
+ // cell texts
+ mpEditEngine->SetDefaultItem( SvxColorItem( maTextColor, EE_CHAR_COLOR ) );
+ size_t nLineCount = ::std::min( static_cast< size_t >( GetLastVisLine() - GetFirstVisLine() + 1 ), maTexts.size() );
+ // #i67432# cut string to avoid edit engine performance problems with very large strings
+ sal_Int32 nFirstVisPos = ::std::max( GetColumnPos( nColIndex ), GetFirstVisPos() );
+ sal_Int32 nLastVisPos = ::std::min( GetColumnPos( nColIndex + 1 ), GetLastVisPos() );
+ sal_Int32 nStrPos = nFirstVisPos - GetColumnPos( nColIndex );
+ sal_Int32 nStrLen = nLastVisPos - nFirstVisPos + 1;
+ sal_Int32 nStrX = GetX( nFirstVisPos );
+ for( size_t nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ std::vector<OUString>& rStrVec = maTexts[ nLine ];
+ if( (nColIndex < rStrVec.size()) && (rStrVec[ nColIndex ].getLength() > nStrPos) )
+ {
+ const OUString& rStr = rStrVec[ nColIndex ];
+ OUString aText = rStr.copy( nStrPos, ::std::min( nStrLen, rStr.getLength() - nStrPos) );
+ ImplDrawCellText( Point( nStrX, GetY( GetFirstVisLine() + nLine ) ), aText );
+ }
+ }
+
+ // header
+ ImplDrawColumnHeader( *mpBackgrDev, nColIndex, maHeaderBackColor );
+
+ mpBackgrDev->SetClipRegion();
+}
+
+void ScCsvGrid::ImplDrawRowHeaders()
+{
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maAppBackColor );
+ Point aPoint( GetHdrX(), 0 );
+ tools::Rectangle aRect( aPoint, Size( GetHdrWidth() + 1, GetHeight() ) );
+ mpBackgrDev->DrawRect( aRect );
+
+ mpBackgrDev->SetFillColor( maHeaderBackColor );
+ aRect.SetBottom( GetY( GetLastVisLine() + 1 ) );
+ mpBackgrDev->DrawRect( aRect );
+
+ // line numbers
+ mpBackgrDev->SetFont( maHeaderFont );
+ mpBackgrDev->SetTextColor( maHeaderTextColor );
+ mpBackgrDev->SetTextFillColor();
+ sal_Int32 nLastLine = GetLastVisLine();
+ for( sal_Int32 nLine = GetFirstVisLine(); nLine <= nLastLine; ++nLine )
+ {
+ OUString aText( OUString::number( nLine + 1 ) );
+ sal_Int32 nX = GetHdrX() + (GetHdrWidth() - mpBackgrDev->GetTextWidth( aText )) / 2;
+ mpBackgrDev->DrawText( Point( nX, GetY( nLine ) ), aText );
+ }
+
+ // grid
+ mpBackgrDev->SetLineColor( maHeaderGridColor );
+ if( IsRTL() )
+ {
+ mpBackgrDev->DrawLine( Point( 0, 0 ), Point( 0, GetHeight() - 1 ) );
+ mpBackgrDev->DrawLine( aRect.TopLeft(), aRect.BottomLeft() );
+ }
+ else
+ mpBackgrDev->DrawLine( aRect.TopRight(), aRect.BottomRight() );
+ aRect.SetTop( GetHdrHeight() );
+ mpBackgrDev->DrawGrid( aRect, Size( 1, GetLineHeight() ), DrawGridFlags::HorzLines );
+}
+
+void ScCsvGrid::ImplDrawBackgrDev()
+{
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maAppBackColor );
+ mpBackgrDev->DrawRect( tools::Rectangle(
+ Point( GetFirstX() + 1, 0 ), Size( GetWidth() - GetHdrWidth(), GetHeight() ) ) );
+
+ sal_uInt32 nLastCol = GetLastVisColumn();
+ if (nLastCol == CSV_COLUMN_INVALID)
+ return;
+ for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
+ ImplDrawColumnBackgr( nColIx );
+
+ ImplDrawRowHeaders();
+}
+
+void ScCsvGrid::ImplDrawColumnSelection( sal_uInt32 nColIndex )
+{
+ ImplInvertCursor( GetRulerCursorPos() );
+ ImplSetColumnClipRegion( *mpGridDev, nColIndex );
+ mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev );
+
+ if( IsSelected( nColIndex ) )
+ {
+ sal_Int32 nX1 = GetColumnX( nColIndex ) + 1;
+ sal_Int32 nX2 = GetColumnX( nColIndex + 1 );
+
+ // header
+ tools::Rectangle aRect( nX1, 0, nX2, GetHdrHeight() );
+ mpGridDev->SetLineColor();
+ if( maHeaderBackColor.IsDark() )
+ // redraw with light gray background in dark mode
+ ImplDrawColumnHeader( *mpGridDev, nColIndex, COL_LIGHTGRAY );
+ else
+ {
+ // use transparent active color
+ mpGridDev->SetFillColor( maSelectColor );
+ mpGridDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aRect ) ), CSV_HDR_TRANSPARENCY );
+ }
+
+ // column selection
+ aRect = tools::Rectangle( nX1, GetHdrHeight() + 1, nX2, GetY( GetLastVisLine() + 1 ) - 1 );
+ ImplInvertRect( *mpGridDev, aRect );
+ }
+
+ mpGridDev->SetClipRegion();
+ ImplInvertCursor( GetRulerCursorPos() );
+}
+
+void ScCsvGrid::ImplDrawGridDev()
+{
+ mpGridDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *mpBackgrDev );
+ sal_uInt32 nLastCol = GetLastVisColumn();
+ if (nLastCol == CSV_COLUMN_INVALID)
+ return;
+ for( sal_uInt32 nColIx = GetFirstVisColumn(); nColIx <= nLastCol; ++nColIx )
+ ImplDrawColumnSelection( nColIx );
+}
+
+void ScCsvGrid::ImplDrawColumn( sal_uInt32 nColIndex )
+{
+ ImplDrawColumnBackgr( nColIndex );
+ ImplDrawColumnSelection( nColIndex );
+}
+
+void ScCsvGrid::ImplDrawHorzScrolled( sal_Int32 nOldPos )
+{
+ sal_Int32 nPos = GetFirstVisPos();
+ if( !IsValidGfx() || (nPos == nOldPos) )
+ return;
+ if( std::abs( nPos - nOldPos ) > GetVisPosCount() / 2 )
+ {
+ ImplDrawBackgrDev();
+ ImplDrawGridDev();
+ return;
+ }
+
+ Point aSrc, aDest;
+ sal_uInt32 nFirstColIx, nLastColIx;
+ if( nPos < nOldPos )
+ {
+ aSrc = Point( GetFirstX() + 1, 0 );
+ aDest = Point( GetFirstX() + GetCharWidth() * (nOldPos - nPos) + 1, 0 );
+ nFirstColIx = GetColumnFromPos( nPos );
+ nLastColIx = GetColumnFromPos( nOldPos );
+ }
+ else
+ {
+ aSrc = Point( GetFirstX() + GetCharWidth() * (nPos - nOldPos) + 1, 0 );
+ aDest = Point( GetFirstX() + 1, 0 );
+ nFirstColIx = GetColumnFromPos( std::min( nOldPos + GetVisPosCount(), GetPosCount() ) - 1 );
+ nLastColIx = GetColumnFromPos( std::min( nPos + GetVisPosCount(), GetPosCount() ) - 1 );
+ }
+
+ ImplInvertCursor( GetRulerCursorPos() + (nPos - nOldPos) );
+ tools::Rectangle aRectangle( GetFirstX(), 0, GetLastX(), GetHeight() - 1 );
+ vcl::Region aClipReg( aRectangle );
+ mpBackgrDev->SetClipRegion( aClipReg );
+ mpBackgrDev->CopyArea( aDest, aSrc, maWinSize );
+ mpBackgrDev->SetClipRegion();
+ mpGridDev->SetClipRegion( aClipReg );
+ mpGridDev->CopyArea( aDest, aSrc, maWinSize );
+ mpGridDev->SetClipRegion();
+ ImplInvertCursor( GetRulerCursorPos() );
+
+ for( sal_uInt32 nColIx = nFirstColIx; nColIx <= nLastColIx; ++nColIx )
+ ImplDrawColumn( nColIx );
+
+ sal_Int32 nLastX = GetX( GetPosCount() ) + 1;
+ if( nLastX <= GetLastX() )
+ {
+ tools::Rectangle aRect( nLastX, 0, GetLastX(), GetHeight() - 1 );
+ mpBackgrDev->SetLineColor();
+ mpBackgrDev->SetFillColor( maAppBackColor );
+ mpBackgrDev->DrawRect( aRect );
+ mpGridDev->SetLineColor();
+ mpGridDev->SetFillColor( maAppBackColor );
+ mpGridDev->DrawRect( aRect );
+ }
+}
+
+void ScCsvGrid::ImplInvertCursor( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ sal_Int32 nX = GetX( nPos ) - 1;
+ tools::Rectangle aRect( Point( nX, 0 ), Size( 3, GetHdrHeight() ) );
+ ImplInvertRect( *mpGridDev, aRect );
+ aRect.SetTop( GetHdrHeight() + 1 );
+ aRect.SetBottom( GetY( GetLastVisLine() + 1 ) );
+ ImplInvertRect( *mpGridDev, aRect );
+ }
+}
+
+tools::Rectangle ScCsvGrid::GetFocusRect()
+{
+ auto nColIndex = GetFocusColumn();
+ if( HasFocus() && IsVisibleColumn( nColIndex ) )
+ {
+ sal_Int32 nX1 = std::max( GetColumnX( nColIndex ), GetFirstX() ) + 1;
+ sal_Int32 nX2 = std::min( GetColumnX( nColIndex + 1 ) - sal_Int32( 1 ), GetLastX() );
+ sal_Int32 nY2 = std::min( GetY( GetLastVisLine() + 1 ), GetHeight() ) - 1;
+ return tools::Rectangle( nX1, 0, nX2, nY2 );
+ }
+ return weld::CustomWidgetController::GetFocusRect();
+}
+
+// accessibility ==============================================================
+
+css::uno::Reference<css::accessibility::XAccessible> ScCsvGrid::CreateAccessible()
+{
+ rtl::Reference<ScAccessibleCsvGrid> xRef(new ScAccessibleCsvGrid(*this));
+ mxAccessible = xRef;
+ return xRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvruler.cxx b/sc/source/ui/dbgui/csvruler.cxx
new file mode 100644
index 0000000000..b900d3491c
--- /dev/null
+++ b/sc/source/ui/dbgui/csvruler.cxx
@@ -0,0 +1,665 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <csvruler.hxx>
+#include <AccessibleCsvControl.hxx>
+
+#include <optutil.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star::uno;
+
+constexpr OUString SEP_PATH = u"Office.Calc/Dialogs/CSVImport"_ustr;
+constexpr OUString FIXED_WIDTH_LIST = u"FixedWidthList"_ustr;
+
+static void load_FixedWidthList(ScCsvSplits &rSplits)
+{
+ Sequence<Any>aValues;
+ const Any *pProperties;
+ Sequence<OUString> aNames { FIXED_WIDTH_LIST };
+ ScLinkConfigItem aItem( SEP_PATH );
+
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getConstArray();
+
+ if( !pProperties[0].hasValue() )
+ return;
+
+ rSplits.Clear();
+
+ OUString sFixedWidthLists;
+ pProperties[0] >>= sFixedWidthLists;
+
+ sal_Int32 nIdx {0};
+ for(;;)
+ {
+ const sal_Int32 n = o3tl::toInt32(o3tl::getToken(sFixedWidthLists, 0, ';', nIdx));
+ if (nIdx<0)
+ {
+ // String ends with a semi-colon so there
+ // is no useful 'int' after the last one.
+ // This also works in case of empty string
+ break;
+ }
+ rSplits.Insert(n);
+ }
+}
+static void save_FixedWidthList(const ScCsvSplits& rSplits)
+{
+ OUStringBuffer sSplits;
+ // Create a semi-colon separated string to save the splits
+ sal_uInt32 n = rSplits.Count();
+ for (sal_uInt32 i = 0; i < n; ++i)
+ {
+ sSplits.append(OUString::number(rSplits[i]) + ";");
+ }
+
+ OUString sFixedWidthLists = sSplits.makeStringAndClear();
+ Sequence<Any> aValues;
+ Any *pProperties;
+ Sequence<OUString> aNames { FIXED_WIDTH_LIST };
+ ScLinkConfigItem aItem( SEP_PATH );
+
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getArray();
+ pProperties[0] <<= sFixedWidthLists;
+
+ aItem.PutProperties(aNames, aValues);
+}
+
+ScCsvRuler::ScCsvRuler(const ScCsvLayoutData& rData, ScCsvTableBox* pTableBox)
+ : ScCsvControl(rData)
+ , mpTableBox(pTableBox)
+ , mnPosCursorLast(1)
+ , mnPosMTStart(0)
+ , mnPosMTCurr(0)
+ , mbPosMTMoved(false)
+ , mnSplitSize(0)
+ , mbTracking(false)
+{
+}
+
+void ScCsvRuler::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ScCsvControl::SetDrawingArea(pDrawingArea);
+
+ UpdateSplitSize();
+
+ Size aSize(1, GetTextHeight() + mnSplitSize + 2);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+
+ EnableRTL( false ); // RTL
+ InitColors();
+ InitSizeData();
+
+ OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
+ maBackgrDev->SetFont( rRefDevice.GetFont() );
+ maRulerDev->SetFont( rRefDevice.GetFont() );
+ load_FixedWidthList( maSplits );
+}
+
+ScCsvRuler::~ScCsvRuler()
+{
+ save_FixedWidthList( maSplits );
+}
+
+// common ruler handling ------------------------------------------------------
+
+void ScCsvRuler::ApplyLayout( const ScCsvLayoutData& rOldData )
+{
+ ScCsvDiff nDiff = GetLayoutData().GetDiff( rOldData ) & (ScCsvDiff::HorizontalMask | ScCsvDiff::RulerCursor);
+ if( nDiff == ScCsvDiff::Equal ) return;
+
+ DisableRepaint();
+ if( nDiff & ScCsvDiff::HorizontalMask )
+ {
+ InitSizeData();
+ if( GetRulerCursorPos() >= GetPosCount() )
+ MoveCursor( GetPosCount() - 1 );
+ }
+ if( nDiff & ScCsvDiff::RulerCursor )
+ {
+ ImplInvertCursor( rOldData.mnPosCursor );
+ ImplInvertCursor( GetRulerCursorPos() );
+ }
+ EnableRepaint();
+
+ if( nDiff & ScCsvDiff::PosOffset )
+ AccSendVisibleEvent();
+}
+
+void ScCsvRuler::InitColors()
+{
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ maBackColor = rSett.GetFaceColor();
+ maActiveColor = rSett.GetWindowColor();
+ maTextColor = rSett.GetLabelTextColor();
+ maSplitColor = maBackColor.IsDark() ? maTextColor : COL_LIGHTRED;
+ InvalidateGfx();
+}
+
+void ScCsvRuler::UpdateSplitSize()
+{
+ mnSplitSize = (GetCharWidth() * 3 / 5) | 1; // make an odd number
+}
+
+void ScCsvRuler::InitSizeData()
+{
+ maWinSize = GetOutputSizePixel();
+
+ UpdateSplitSize();
+
+ sal_Int32 nActiveWidth = std::min( GetWidth() - GetHdrWidth(), GetPosCount() * GetCharWidth() );
+ sal_Int32 nActiveHeight = GetTextHeight();
+
+ maActiveRect.SetPos( Point( GetFirstX(), (GetHeight() - nActiveHeight - 1) / 2 ) );
+ maActiveRect.SetSize( Size( nActiveWidth, nActiveHeight ) );
+
+ maBackgrDev->SetOutputSizePixel( maWinSize );
+ maRulerDev->SetOutputSizePixel( maWinSize );
+
+ InvalidateGfx();
+}
+
+void ScCsvRuler::MoveCursor( sal_Int32 nPos, bool bScroll )
+{
+ DisableRepaint();
+ if( bScroll )
+ Execute( CSVCMD_MAKEPOSVISIBLE, nPos );
+ Execute( CSVCMD_MOVERULERCURSOR, IsVisibleSplitPos( nPos ) ? nPos : CSV_POS_INVALID );
+ EnableRepaint();
+ AccSendCaretEvent();
+}
+
+void ScCsvRuler::MoveCursorRel( ScMoveMode eDir )
+{
+ if( GetRulerCursorPos() == CSV_POS_INVALID )
+ return;
+
+ switch( eDir )
+ {
+ case MOVE_FIRST:
+ MoveCursor( 1 );
+ break;
+ case MOVE_LAST:
+ MoveCursor( GetPosCount() - 1 );
+ break;
+ case MOVE_PREV:
+ if( GetRulerCursorPos() > 1 )
+ MoveCursor( GetRulerCursorPos() - 1 );
+ break;
+ case MOVE_NEXT:
+ if( GetRulerCursorPos() < GetPosCount() - 1 )
+ MoveCursor( GetRulerCursorPos() + 1 );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScCsvRuler::MoveCursorToSplit( ScMoveMode eDir )
+{
+ if( GetRulerCursorPos() == CSV_POS_INVALID )
+ return;
+
+ sal_uInt32 nIndex = CSV_VEC_NOTFOUND;
+ switch( eDir )
+ {
+ case MOVE_FIRST: nIndex = maSplits.LowerBound( 0 ); break;
+ case MOVE_LAST: nIndex = maSplits.UpperBound( GetPosCount() ); break;
+ case MOVE_PREV: nIndex = maSplits.UpperBound( GetRulerCursorPos() - 1 ); break;
+ case MOVE_NEXT: nIndex = maSplits.LowerBound( GetRulerCursorPos() + 1 ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ sal_Int32 nPos = maSplits[ nIndex ];
+ if( nPos != CSV_POS_INVALID )
+ MoveCursor( nPos );
+}
+
+void ScCsvRuler::ScrollVertRel( ScMoveMode eDir )
+{
+ sal_Int32 nLine = GetFirstVisLine();
+ switch( eDir )
+ {
+ case MOVE_PREV: --nLine; break;
+ case MOVE_NEXT: ++nLine; break;
+ case MOVE_PREVPAGE: nLine -= GetVisLineCount() - 1; break;
+ case MOVE_NEXTPAGE: nLine += GetVisLineCount() - 1; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ Execute( CSVCMD_SETLINEOFFSET, nLine );
+}
+
+// split handling -------------------------------------------------------------
+
+sal_Int32 ScCsvRuler::GetNoScrollPos( sal_Int32 nPos ) const
+{
+ sal_Int32 nNewPos = nPos;
+ if( nNewPos != CSV_POS_INVALID )
+ {
+ if( nNewPos < GetFirstVisPos() + CSV_SCROLL_DIST )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() > 0) ? CSV_SCROLL_DIST : 0;
+ nNewPos = std::max( nPos, GetFirstVisPos() + nScroll );
+ }
+ else if( nNewPos > GetLastVisPos() - CSV_SCROLL_DIST - 1 )
+ {
+ sal_Int32 nScroll = (GetFirstVisPos() < GetMaxPosOffset()) ? CSV_SCROLL_DIST : 0;
+ nNewPos = std::min( nNewPos, GetLastVisPos() - nScroll - sal_Int32( 1 ) );
+ }
+ }
+ return nNewPos;
+}
+
+void ScCsvRuler::InsertSplit( sal_Int32 nPos )
+{
+ if( maSplits.Insert( nPos ) )
+ {
+ ImplDrawSplit( nPos );
+ Repaint();
+ }
+}
+
+void ScCsvRuler::RemoveSplit( sal_Int32 nPos )
+{
+ if( maSplits.Remove( nPos ) )
+ {
+ ImplEraseSplit( nPos );
+ Repaint();
+ }
+}
+
+void ScCsvRuler::MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos )
+{
+ bool bRemove = maSplits.Remove( nPos );
+ bool bInsert = maSplits.Insert( nNewPos );
+ if( bRemove || bInsert )
+ {
+ ImplEraseSplit( nPos );
+ ImplDrawSplit( nNewPos );
+ Repaint();
+ }
+}
+
+void ScCsvRuler::RemoveAllSplits()
+{
+ maSplits.Clear();
+ Repaint( true );
+}
+
+sal_Int32 ScCsvRuler::FindEmptyPos( sal_Int32 nPos, ScMoveMode eDir ) const
+{
+ sal_Int32 nNewPos = nPos;
+ if( nNewPos != CSV_POS_INVALID )
+ {
+ switch( eDir )
+ {
+ case MOVE_FIRST:
+ nNewPos = std::min( nPos, FindEmptyPos( 0, MOVE_NEXT ) );
+ break;
+ case MOVE_LAST:
+ nNewPos = std::max( nPos, FindEmptyPos( GetPosCount(), MOVE_PREV ) );
+ break;
+ case MOVE_PREV:
+ while( HasSplit( --nNewPos ) ) ;
+ break;
+ case MOVE_NEXT:
+ while( HasSplit( ++nNewPos ) ) ;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return IsValidSplitPos( nNewPos ) ? nNewPos : CSV_POS_INVALID;
+}
+
+void ScCsvRuler::MoveCurrSplit( sal_Int32 nNewPos )
+{
+ DisableRepaint();
+ Execute( CSVCMD_MOVESPLIT, GetRulerCursorPos(), nNewPos );
+ MoveCursor( nNewPos );
+ EnableRepaint();
+}
+
+void ScCsvRuler::MoveCurrSplitRel( ScMoveMode eDir )
+{
+ if( HasSplit( GetRulerCursorPos() ) )
+ {
+ sal_Int32 nNewPos = FindEmptyPos( GetRulerCursorPos(), eDir );
+ if( nNewPos != CSV_POS_INVALID )
+ MoveCurrSplit( nNewPos );
+ }
+}
+
+// event handling -------------------------------------------------------------
+
+void ScCsvRuler::Resize()
+{
+ ScCsvControl::Resize();
+ InitSizeData();
+ Repaint();
+}
+
+void ScCsvRuler::GetFocus()
+{
+ ScCsvControl::GetFocus();
+ DisableRepaint();
+ if( GetRulerCursorPos() == CSV_POS_INVALID )
+ MoveCursor( GetNoScrollPos( mnPosCursorLast ) );
+ EnableRepaint();
+}
+
+void ScCsvRuler::LoseFocus()
+{
+ ScCsvControl::LoseFocus();
+ mnPosCursorLast = GetRulerCursorPos();
+ MoveCursor( CSV_POS_INVALID );
+}
+
+void ScCsvRuler::StyleUpdated()
+{
+ InitColors();
+ Repaint();
+
+ ScCsvControl::StyleUpdated();
+}
+
+bool ScCsvRuler::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ DisableRepaint();
+ if( !HasFocus() )
+ GrabFocus();
+ if( rMEvt.IsLeft() )
+ {
+ sal_Int32 nPos = GetPosFromX( rMEvt.GetPosPixel().X() );
+ if( IsVisibleSplitPos( nPos ) )
+ StartMouseTracking( nPos );
+ ImplSetMousePointer( nPos );
+ }
+ EnableRepaint();
+ return true;
+}
+
+bool ScCsvRuler::MouseButtonUp( const MouseEvent& )
+{
+ if (mbTracking)
+ {
+ EndMouseTracking();
+ mbTracking = false;
+ }
+ return true;
+}
+
+bool ScCsvRuler::MouseMove( const MouseEvent& rMEvt )
+{
+ if( !rMEvt.IsModifierChanged() )
+ {
+ sal_Int32 nPos = GetPosFromX( rMEvt.GetPosPixel().X() );
+ if( mbTracking )
+ {
+ // on mouse tracking: keep position valid
+ nPos = std::clamp( nPos, sal_Int32(1), GetPosCount() - 1 );
+ MoveMouseTracking( nPos );
+ }
+ else
+ {
+ tools::Rectangle aRect( Point(), maWinSize );
+ if( !IsVisibleSplitPos( nPos ) || !aRect.Contains( rMEvt.GetPosPixel() ) )
+ // if focused, keep old cursor position for key input
+ nPos = HasFocus() ? GetRulerCursorPos() : CSV_POS_INVALID;
+ MoveCursor( nPos, false );
+ }
+ ImplSetMousePointer( nPos );
+ }
+ return true;
+}
+
+bool ScCsvRuler::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKCode.GetCode();
+ bool bNoMod = !rKCode.GetModifier();
+ bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
+ bool bJump = (rKCode.GetModifier() == KEY_MOD1);
+ bool bMove = (rKCode.GetModifier() == (KEY_MOD1 | KEY_SHIFT));
+
+ ScMoveMode eHDir = GetHorzDirection( nCode, true );
+ ScMoveMode eVDir = GetVertDirection( nCode, false );
+
+ if( bNoMod )
+ {
+ if( eHDir != MOVE_NONE )
+ MoveCursorRel( eHDir );
+ else if( eVDir != MOVE_NONE )
+ ScrollVertRel( eVDir );
+ else switch( nCode )
+ {
+ case KEY_SPACE: Execute( CSVCMD_TOGGLESPLIT, GetRulerCursorPos() ); break;
+ case KEY_INSERT: Execute( CSVCMD_INSERTSPLIT, GetRulerCursorPos() ); break;
+ case KEY_DELETE: Execute( CSVCMD_REMOVESPLIT, GetRulerCursorPos() ); break;
+ }
+ }
+ else if( bJump && (eHDir != MOVE_NONE) )
+ MoveCursorToSplit( eHDir );
+ else if( bMove && (eHDir != MOVE_NONE) )
+ MoveCurrSplitRel( eHDir );
+ else if( bShift && (nCode == KEY_DELETE) )
+ Execute( CSVCMD_REMOVEALLSPLITS );
+
+ return rKCode.GetGroup() == KEYGROUP_CURSOR;
+}
+
+void ScCsvRuler::StartMouseTracking( sal_Int32 nPos )
+{
+ mnPosMTStart = mnPosMTCurr = nPos;
+ mbPosMTMoved = false;
+ maOldSplits = maSplits;
+ Execute( CSVCMD_INSERTSPLIT, nPos );
+ if( HasSplit( nPos ) )
+ mbTracking = true;
+}
+
+void ScCsvRuler::MoveMouseTracking( sal_Int32 nPos )
+{
+ if( mnPosMTCurr != nPos )
+ {
+ DisableRepaint();
+ MoveCursor( nPos );
+ if( (mnPosMTCurr != mnPosMTStart) && maOldSplits.HasSplit( mnPosMTCurr ) )
+ Execute( CSVCMD_INSERTSPLIT, nPos );
+ else
+ Execute( CSVCMD_MOVESPLIT, mnPosMTCurr, nPos );
+ mnPosMTCurr = nPos;
+ mbPosMTMoved = true;
+ EnableRepaint();
+ }
+}
+
+void ScCsvRuler::EndMouseTracking()
+{
+ // remove on simple click on an existing split
+ if( (mnPosMTCurr == mnPosMTStart) && maOldSplits.HasSplit( mnPosMTCurr ) && !mbPosMTMoved )
+ Execute( CSVCMD_REMOVESPLIT, mnPosMTCurr );
+ mnPosMTStart = CSV_POS_INVALID;
+}
+
+// painting -------------------------------------------------------------------
+
+void ScCsvRuler::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
+{
+ ImplRedraw(rRenderContext);
+}
+
+void ScCsvRuler::ImplRedraw(vcl::RenderContext& rRenderContext)
+{
+ if( IsVisible() )
+ {
+ if( !IsValidGfx() )
+ {
+ ValidateGfx();
+ ImplDrawBackgrDev();
+ ImplDrawRulerDev();
+ }
+ rRenderContext.DrawOutDev( Point(), maWinSize, Point(), maWinSize, *maRulerDev );
+ }
+}
+
+tools::Rectangle ScCsvRuler::GetFocusRect()
+{
+ /* Draws directly tracking rectangle to the column with the specified index. */
+ if(HasFocus())
+ return tools::Rectangle(0, 0, GetWidth() - 1, GetHeight() - 2);
+ return weld::CustomWidgetController::GetFocusRect();
+}
+
+void ScCsvRuler::ImplDrawArea( sal_Int32 nPosX, sal_Int32 nWidth )
+{
+ maBackgrDev->SetLineColor();
+ tools::Rectangle aRect( Point( nPosX, 0 ), Size( nWidth, GetHeight() ) );
+ maBackgrDev->SetFillColor( maBackColor );
+ maBackgrDev->DrawRect( aRect );
+
+ aRect = maActiveRect;
+ aRect.SetLeft( std::max( GetFirstX(), nPosX ) );
+ aRect.SetRight( std::min( std::min( GetX( GetPosCount() ), GetLastX() ), nPosX + nWidth - sal_Int32( 1 ) ) );
+ if( aRect.Left() <= aRect.Right() )
+ {
+ maBackgrDev->SetFillColor( maActiveColor );
+ maBackgrDev->DrawRect( aRect );
+ }
+
+ maBackgrDev->SetLineColor( maTextColor );
+ sal_Int32 nY = GetHeight() - 1;
+ maBackgrDev->DrawLine( Point( nPosX, nY ), Point( nPosX + nWidth - 1, nY ) );
+}
+
+void ScCsvRuler::ImplDrawBackgrDev()
+{
+ ImplDrawArea( 0, GetWidth() );
+
+ // scale
+ maBackgrDev->SetLineColor( maTextColor );
+ maBackgrDev->SetFillColor();
+ sal_Int32 nPos;
+
+ sal_Int32 nFirstPos = std::max( GetPosFromX( 0 ) - 1, sal_Int32(0) );
+ sal_Int32 nLastPos = GetPosFromX( GetWidth() );
+ sal_Int32 nY = (maActiveRect.Top() + maActiveRect.Bottom()) / 2;
+ for( nPos = nFirstPos; nPos <= nLastPos; ++nPos )
+ {
+ sal_Int32 nX = GetX( nPos );
+ if( nPos % 5 )
+ maBackgrDev->DrawPixel( Point( nX, nY ) );
+ else
+ maBackgrDev->DrawLine( Point( nX, nY - 1 ), Point( nX, nY + 1 ) );
+ }
+
+ // texts
+ maBackgrDev->SetTextColor( maTextColor );
+ maBackgrDev->SetTextFillColor();
+ for( nPos = ((nFirstPos + 9) / 10) * 10; nPos <= nLastPos; nPos += 10 )
+ {
+ OUString aText( OUString::number( nPos ) );
+ sal_Int32 nTextWidth = maBackgrDev->GetTextWidth( aText );
+ sal_Int32 nTextX = GetX( nPos ) - nTextWidth / 2;
+ ImplDrawArea( nTextX - 1, nTextWidth + 2 );
+ maBackgrDev->DrawText( Point( nTextX, maActiveRect.Top() ), aText );
+ }
+}
+
+void ScCsvRuler::ImplDrawSplit( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ Point aPos( GetX( nPos ) - mnSplitSize / 2, GetHeight() - mnSplitSize - 2 );
+ Size aSize( mnSplitSize, mnSplitSize );
+ maRulerDev->SetLineColor( maTextColor );
+ maRulerDev->SetFillColor( maSplitColor );
+ maRulerDev->DrawEllipse( tools::Rectangle( aPos, aSize ) );
+ maRulerDev->DrawPixel( Point( GetX( nPos ), GetHeight() - 2 ) );
+ }
+}
+
+void ScCsvRuler::ImplEraseSplit( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ ImplInvertCursor( GetRulerCursorPos() );
+ Point aPos( GetX( nPos ) - mnSplitSize / 2, 0 );
+ Size aSize( mnSplitSize, GetHeight() );
+ maRulerDev->DrawOutDev( aPos, aSize, aPos, aSize, *maBackgrDev );
+ ImplInvertCursor( GetRulerCursorPos() );
+ }
+}
+
+void ScCsvRuler::ImplDrawRulerDev()
+{
+ maRulerDev->DrawOutDev( Point(), maWinSize, Point(), maWinSize, *maBackgrDev );
+ ImplInvertCursor( GetRulerCursorPos() );
+
+ sal_uInt32 nFirst = maSplits.LowerBound( GetFirstVisPos() );
+ sal_uInt32 nLast = maSplits.UpperBound( GetLastVisPos() );
+ if( (nFirst != CSV_VEC_NOTFOUND) && (nLast != CSV_VEC_NOTFOUND) )
+ for( sal_uInt32 nIndex = nFirst; nIndex <= nLast; ++nIndex )
+ ImplDrawSplit( GetSplitPos( nIndex ) );
+}
+
+void ScCsvRuler::ImplInvertCursor( sal_Int32 nPos )
+{
+ if( IsVisibleSplitPos( nPos ) )
+ {
+ ImplInvertRect( *maRulerDev, tools::Rectangle( Point( GetX( nPos ) - 1, 0 ), Size( 3, GetHeight() - 1 ) ) );
+ if( HasSplit( nPos ) )
+ ImplDrawSplit( nPos );
+ }
+}
+
+void ScCsvRuler::ImplSetMousePointer( sal_Int32 nPos )
+{
+ SetPointer( HasSplit( nPos ) ? PointerStyle::HSplit : PointerStyle::Arrow );
+}
+
+// accessibility ==============================================================
+
+css::uno::Reference<css::accessibility::XAccessible> ScCsvRuler::CreateAccessible()
+{
+ rtl::Reference<ScAccessibleCsvRuler> xRef(new ScAccessibleCsvRuler(*this));
+ mxAccessible = xRef;
+ return xRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvsplits.cxx b/sc/source/ui/dbgui/csvsplits.cxx
new file mode 100644
index 0000000000..575bd53d0a
--- /dev/null
+++ b/sc/source/ui/dbgui/csvsplits.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 <csvsplits.hxx>
+
+#include <algorithm>
+
+#include <sal/log.hxx>
+
+bool ScCsvSplits::Insert( sal_Int32 nPos )
+{
+ if (nPos < 0)
+ return false;
+
+ const auto aIter = ::std::lower_bound( maVec.begin(), maVec.end(), nPos );
+
+ if (aIter != maVec.end() && *aIter == nPos)
+ return false;
+
+ SAL_WARN_IF(maVec.size()>=static_cast<std::size_t>(SAL_MAX_UINT32-1),
+ "sc.ui", "ScCsvSplits::Insert: too many elements in vector");
+
+ maVec.insert( aIter, nPos );
+ return true;
+}
+
+bool ScCsvSplits::Remove( sal_Int32 nPos )
+{
+ sal_uInt32 nIndex = GetIndex( nPos );
+ if (nIndex == CSV_VEC_NOTFOUND)
+ return false;
+
+ maVec.erase( maVec.begin() + nIndex );
+ return true;
+}
+
+void ScCsvSplits::RemoveRange( sal_Int32 nPosStart, sal_Int32 nPosEnd )
+{
+ sal_uInt32 nStartIx = LowerBound( nPosStart );
+ sal_uInt32 nEndIx = UpperBound( nPosEnd );
+ if( (nStartIx != CSV_VEC_NOTFOUND) && (nEndIx != CSV_VEC_NOTFOUND) && (nStartIx <= nEndIx) )
+ maVec.erase( maVec.begin() + nStartIx, maVec.begin() + nEndIx + 1 );
+}
+
+void ScCsvSplits::Clear()
+{
+ maVec.clear();
+}
+
+bool ScCsvSplits::HasSplit( sal_Int32 nPos ) const
+{
+ return GetIndex( nPos ) != CSV_VEC_NOTFOUND;
+}
+
+sal_uInt32 ScCsvSplits::GetIndex( sal_Int32 nPos ) const
+{
+ auto aIter = ::std::lower_bound( maVec.cbegin(), maVec.cend(), nPos );
+ return GetIterIndex( ((aIter != maVec.end()) && (*aIter == nPos)) ? aIter : maVec.end() );
+}
+
+sal_uInt32 ScCsvSplits::LowerBound( sal_Int32 nPos ) const
+{
+ return GetIterIndex( ::std::lower_bound( maVec.begin(), maVec.end(), nPos ) );
+}
+
+sal_uInt32 ScCsvSplits::UpperBound( sal_Int32 nPos ) const
+{
+ sal_uInt32 nIndex = LowerBound( nPos );
+ if( nIndex == CSV_VEC_NOTFOUND )
+ return Count() ? (Count() - 1) : CSV_VEC_NOTFOUND;
+ if( GetPos( nIndex ) == nPos )
+ return nIndex;
+ return nIndex ? (nIndex - 1) : CSV_VEC_NOTFOUND;
+}
+
+sal_Int32 ScCsvSplits::GetPos( sal_uInt32 nIndex ) const
+{
+ return (nIndex < Count()) ? maVec[ nIndex ] : CSV_POS_INVALID;
+}
+
+sal_uInt32 ScCsvSplits::GetIterIndex( const_iterator const & aIter ) const
+{
+ return (aIter == maVec.end()) ? CSV_VEC_NOTFOUND : (aIter - maVec.begin());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/csvtablebox.cxx b/sc/source/ui/dbgui/csvtablebox.cxx
new file mode 100644
index 0000000000..10dba1b810
--- /dev/null
+++ b/sc/source/ui/dbgui/csvtablebox.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <csvtablebox.hxx>
+#include <vcl/settings.hxx>
+
+ScCsvTableBox::ScCsvTableBox(weld::Builder& rBuilder)
+ : mxRuler(new ScCsvRuler(maData, this))
+ , mxGrid(new ScCsvGrid(maData, rBuilder.weld_menu("popup"), this))
+ , mxScroll(rBuilder.weld_scrolled_window("scrolledwindow", true))
+ , mxRulerWeld(new weld::CustomWeld(rBuilder, "csvruler", *mxRuler))
+ , mxGridWeld(new weld::CustomWeld(rBuilder, "csvgrid", *mxGrid))
+ , maEndScrollIdle("ScCsvTableBox maEndScrollIdle")
+{
+ Size aSize(mxScroll->get_approximate_digit_width() * 67,
+ mxScroll->get_text_height() * 10);
+ // this needs to be larger than the ScCsvGrid initial size to get it
+ // to stretch to fit, see ScCsvGrid::SetDrawingArea
+ mxScroll->set_size_request(aSize.Width(), aSize.Height());
+
+ mbFixedMode = false;
+ mnFixedWidth = 1;
+
+ Link<ScCsvControl&,void> aLink = LINK( this, ScCsvTableBox, CsvCmdHdl );
+ mxRuler->SetCmdHdl( aLink );
+ mxGrid->SetCmdHdl( aLink );
+
+ mxScroll->connect_hadjustment_changed(LINK(this, ScCsvTableBox, HScrollHdl));
+ mxScroll->connect_vadjustment_changed(LINK(this, ScCsvTableBox, VScrollHdl));
+
+ maEndScrollIdle.SetPriority(TaskPriority::LOWEST);
+ maEndScrollIdle.SetInvokeHandler(LINK(this,ScCsvTableBox,ScrollEndHdl));
+
+ InitControls();
+}
+
+ScCsvTableBox::~ScCsvTableBox()
+{
+}
+
+// common table box handling --------------------------------------------------
+
+void ScCsvTableBox::SetSeparatorsMode()
+{
+ if( !mbFixedMode )
+ return;
+
+ // rescue data for fixed width mode
+ mnFixedWidth = mxGrid->GetPosCount();
+ maFixColStates = mxGrid->GetColumnStates();
+ // switch to separators mode
+ mbFixedMode = false;
+ // reset and reinitialize controls
+ mxGrid->DisableRepaint();
+ mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
+ mxGrid->Execute( CSVCMD_SETPOSCOUNT, 1 );
+ mxGrid->Execute( CSVCMD_NEWCELLTEXTS );
+ mxGrid->SetColumnStates( std::vector(maSepColStates) );
+ InitControls();
+ mxGrid->EnableRepaint();
+}
+
+void ScCsvTableBox::SetFixedWidthMode()
+{
+ if( mbFixedMode )
+ return;
+
+ // rescue data for separators mode
+ maSepColStates = mxGrid->GetColumnStates();
+ // switch to fixed width mode
+ mbFixedMode = true;
+ // reset and reinitialize controls
+ mxGrid->DisableRepaint();
+ mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
+ mxGrid->Execute( CSVCMD_SETPOSCOUNT, mnFixedWidth );
+ mxGrid->SetSplits( mxRuler->GetSplits() );
+ mxGrid->SetColumnStates( std::vector(maFixColStates) );
+ InitControls();
+ mxGrid->EnableRepaint();
+}
+
+void ScCsvTableBox::Init()
+{
+ mxGrid->Init();
+}
+
+void ScCsvTableBox::InitControls()
+{
+ mxGrid->UpdateLayoutData();
+
+ mxGrid->Show();
+ if (mbFixedMode)
+ mxRuler->Show();
+ else
+ mxRuler->Hide();
+
+ Size aWinSize = mxGrid->GetOutputSizePixel();
+ maData.mnWinWidth = aWinSize.Width();
+ maData.mnWinHeight = aWinSize.Height();
+
+ // scrollbars always visible
+ InitHScrollBar();
+
+ // scrollbars always visible
+ InitVScrollBar();
+
+ // let the controls self-adjust to visible area
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, mxGrid->GetFirstVisPos() );
+ mxGrid->Execute( CSVCMD_SETLINEOFFSET, mxGrid->GetFirstVisLine() );
+}
+
+void ScCsvTableBox::InitHScrollBar()
+{
+ int nLower = 0;
+ int nValue = mxGrid->GetFirstVisPos();
+ int nUpper = mxGrid->GetPosCount() + 2;
+ int nPageSize = mxGrid->GetVisPosCount();
+
+ // Undo scrollbar RTL
+ if (AllSettings::GetLayoutRTL())
+ nValue = nUpper - (nValue - nLower + nPageSize);
+
+ mxScroll->hadjustment_configure(nValue, nLower, nUpper,
+ 1, mxGrid->GetVisPosCount() * 3 / 4,
+ nPageSize);
+}
+
+void ScCsvTableBox::InitVScrollBar()
+{
+ mxScroll->vadjustment_configure(mxGrid->GetFirstVisLine(), 0, mxGrid->GetLineCount() + 1,
+ 1, mxGrid->GetVisLineCount() - 2,
+ mxGrid->GetVisLineCount());
+}
+
+void ScCsvTableBox::MakePosVisible( sal_Int32 nPos )
+{
+ if( (0 <= nPos) && (nPos < mxGrid->GetPosCount()) )
+ {
+ if( nPos - CSV_SCROLL_DIST + 1 <= mxGrid->GetFirstVisPos() )
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, nPos - CSV_SCROLL_DIST );
+ else if( nPos + CSV_SCROLL_DIST >= mxGrid->GetLastVisPos() )
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, nPos - mxGrid->GetVisPosCount() + CSV_SCROLL_DIST );
+ }
+}
+
+// cell contents --------------------------------------------------------------
+
+void ScCsvTableBox::SetUniStrings(
+ const OUString* pTextLines, const OUString& rSepChars,
+ sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace )
+{
+ // assuming that pTextLines is a string array with size CSV_PREVIEW_LINES
+ // -> will be dynamic sometime
+ mxGrid->DisableRepaint();
+ sal_Int32 nEndLine = mxGrid->GetFirstVisLine() + CSV_PREVIEW_LINES;
+ const OUString* pString = pTextLines;
+ for( sal_Int32 nLine = mxGrid->GetFirstVisLine(); nLine < nEndLine; ++nLine, ++pString )
+ {
+ if( mbFixedMode )
+ mxGrid->ImplSetTextLineFix( nLine, *pString );
+ else
+ mxGrid->ImplSetTextLineSep( nLine, *pString, rSepChars, cTextSep, bMergeSep, bRemoveSpace );
+ }
+ mxGrid->EnableRepaint();
+}
+
+// column settings ------------------------------------------------------------
+
+void ScCsvTableBox::InitTypes(const weld::ComboBox& rListBox)
+{
+ const sal_Int32 nTypeCount = rListBox.get_count();
+ std::vector<OUString> aTypeNames( nTypeCount );
+ for( sal_Int32 nIndex = 0; nIndex < nTypeCount; ++nIndex )
+ aTypeNames[ nIndex ] = rListBox.get_text( nIndex );
+ mxGrid->SetTypeNames( std::move(aTypeNames) );
+}
+
+void ScCsvTableBox::FillColumnData( ScAsciiOptions& rOptions ) const
+{
+ if( mbFixedMode )
+ mxGrid->FillColumnDataFix( rOptions );
+ else
+ mxGrid->FillColumnDataSep( rOptions );
+}
+
+// event handling -------------------------------------------------------------
+
+IMPL_LINK( ScCsvTableBox, CsvCmdHdl, ScCsvControl&, rCtrl, void )
+{
+ const ScCsvCmd& rCmd = rCtrl.GetCmd();
+ ScCsvCmdType eType = rCmd.GetType();
+ sal_Int32 nParam1 = rCmd.GetParam1();
+ sal_Int32 nParam2 = rCmd.GetParam2();
+
+ bool bFound = true;
+ switch( eType )
+ {
+ case CSVCMD_REPAINT:
+ if( !mxGrid->IsNoRepaint() )
+ {
+ mxGrid->Invalidate();
+ mxRuler->Invalidate();
+ InitHScrollBar();
+ InitVScrollBar();
+ }
+ break;
+ case CSVCMD_MAKEPOSVISIBLE:
+ MakePosVisible( nParam1 );
+ break;
+
+ case CSVCMD_NEWCELLTEXTS:
+ if( mbFixedMode )
+ mxGrid->Execute( CSVCMD_UPDATECELLTEXTS );
+ else
+ {
+ mxGrid->DisableRepaint();
+ ScCsvColStateVec aStates( mxGrid->GetColumnStates() );
+ sal_Int32 nPos = mxGrid->GetFirstVisPos();
+ mxGrid->Execute( CSVCMD_SETPOSCOUNT, 1 );
+ mxGrid->Execute( CSVCMD_UPDATECELLTEXTS );
+ mxGrid->Execute( CSVCMD_SETPOSOFFSET, nPos );
+ mxGrid->SetColumnStates( std::move(aStates) );
+ mxGrid->EnableRepaint();
+ }
+ break;
+ case CSVCMD_UPDATECELLTEXTS:
+ maUpdateTextHdl.Call( *this );
+ break;
+ case CSVCMD_SETCOLUMNTYPE:
+ mxGrid->SetSelColumnType( nParam1 );
+ break;
+ case CSVCMD_EXPORTCOLUMNTYPE:
+ maColTypeHdl.Call( *this );
+ break;
+ case CSVCMD_SETFIRSTIMPORTLINE:
+ mxGrid->SetFirstImportedLine( nParam1 );
+ break;
+
+ case CSVCMD_INSERTSPLIT:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::InsertSplit - invalid call" );
+ if( mxRuler->GetSplitCount() + 1 < sal::static_int_cast<sal_uInt32>(CSV_MAXCOLCOUNT) )
+ {
+ mxRuler->InsertSplit( nParam1 );
+ mxGrid->InsertSplit( nParam1 );
+ }
+ break;
+ case CSVCMD_REMOVESPLIT:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::RemoveSplit - invalid call" );
+ mxRuler->RemoveSplit( nParam1 );
+ mxGrid->RemoveSplit( nParam1 );
+ break;
+ case CSVCMD_TOGGLESPLIT:
+ mxGrid->Execute( mxRuler->HasSplit( nParam1 ) ? CSVCMD_REMOVESPLIT : CSVCMD_INSERTSPLIT, nParam1 );
+ break;
+ case CSVCMD_MOVESPLIT:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::MoveSplit - invalid call" );
+ mxRuler->MoveSplit( nParam1, nParam2 );
+ mxGrid->MoveSplit( nParam1, nParam2 );
+ break;
+ case CSVCMD_REMOVEALLSPLITS:
+ OSL_ENSURE( mbFixedMode, "ScCsvTableBox::CsvCmdHdl::RemoveAllSplits - invalid call" );
+ mxRuler->RemoveAllSplits();
+ mxGrid->RemoveAllSplits();
+ break;
+ default:
+ bFound = false;
+ }
+ if( bFound )
+ return;
+
+ const ScCsvLayoutData aOldData( maData );
+ switch( eType )
+ {
+ case CSVCMD_SETPOSCOUNT:
+ maData.mnPosCount = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetPosOffset( mxGrid->GetFirstVisPos() );
+ break;
+ case CSVCMD_SETPOSOFFSET:
+ ImplSetPosOffset( nParam1 );
+ break;
+ case CSVCMD_SETHDRWIDTH:
+ maData.mnHdrWidth = std::max( nParam1, sal_Int32( 0 ) );
+ ImplSetPosOffset( mxGrid->GetFirstVisPos() );
+ break;
+ case CSVCMD_SETCHARWIDTH:
+ maData.mnCharWidth = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetPosOffset( mxGrid->GetFirstVisPos() );
+ break;
+ case CSVCMD_SETLINECOUNT:
+ maData.mnLineCount = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetLineOffset( mxGrid->GetFirstVisLine() );
+ break;
+ case CSVCMD_SETLINEOFFSET:
+ ImplSetLineOffset( nParam1 );
+ break;
+ case CSVCMD_SETHDRHEIGHT:
+ maData.mnHdrHeight = std::max( nParam1, sal_Int32( 0 ) );
+ ImplSetLineOffset( mxGrid->GetFirstVisLine() );
+ break;
+ case CSVCMD_SETLINEHEIGHT:
+ maData.mnLineHeight = std::max( nParam1, sal_Int32( 1 ) );
+ ImplSetLineOffset( mxGrid->GetFirstVisLine() );
+ break;
+ case CSVCMD_MOVERULERCURSOR:
+ maData.mnPosCursor = mxGrid->IsVisibleSplitPos( nParam1 ) ? nParam1 : CSV_POS_INVALID;
+ break;
+ case CSVCMD_MOVEGRIDCURSOR:
+ maData.mnColCursor = ((0 <= nParam1) && (nParam1 < mxGrid->GetPosCount())) ? nParam1 : CSV_POS_INVALID;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if( maData != aOldData )
+ {
+ mxGrid->DisableRepaint();
+ mxRuler->ApplyLayout( aOldData );
+ mxGrid->ApplyLayout( aOldData );
+ mxGrid->EnableRepaint();
+ }
+}
+
+IMPL_LINK(ScCsvTableBox, HScrollHdl, weld::ScrolledWindow&, rScroll, void)
+{
+ int nLower = 0;
+ int nValue = rScroll.hadjustment_get_value();
+ int nUpper = mxGrid->GetPosCount() + 2;
+ int nPageSize = mxGrid->GetVisPosCount();
+
+ // Undo scrollbar RTL
+ if (AllSettings::GetLayoutRTL())
+ nValue = nUpper - (nValue - nLower + nPageSize);
+
+ mxGrid->Execute(CSVCMD_SETPOSOFFSET, nValue);
+ maEndScrollIdle.Start();
+}
+
+IMPL_LINK(ScCsvTableBox, VScrollHdl, weld::ScrolledWindow&, rScroll, void)
+{
+ mxGrid->Execute(CSVCMD_SETLINEOFFSET, rScroll.vadjustment_get_value());
+}
+
+IMPL_LINK_NOARG(ScCsvTableBox, ScrollEndHdl, Timer*, void)
+{
+ if( mxGrid->GetRulerCursorPos() != CSV_POS_INVALID )
+ mxGrid->Execute( CSVCMD_MOVERULERCURSOR, mxRuler->GetNoScrollPos( mxGrid->GetRulerCursorPos() ) );
+ if( mxGrid->GetGridCursorPos() != CSV_POS_INVALID )
+ mxGrid->Execute( CSVCMD_MOVEGRIDCURSOR, mxGrid->GetNoScrollCol( mxGrid->GetGridCursorPos() ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dapidata.cxx b/sc/source/ui/dbgui/dapidata.cxx
new file mode 100644
index 0000000000..0bd0861650
--- /dev/null
+++ b/sc/source/ui/dbgui/dapidata.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+
+#include <dapidata.hxx>
+#include <dpsdbtab.hxx>
+
+using namespace com::sun::star;
+
+// entries in the "type" ListBox
+#define DP_TYPELIST_TABLE 0
+#define DP_TYPELIST_QUERY 1
+#define DP_TYPELIST_SQLNAT 3
+
+ScDataPilotDatabaseDlg::ScDataPilotDatabaseDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/selectdatasource.ui", "SelectDataSourceDialog")
+ , m_xLbDatabase(m_xBuilder->weld_combo_box("database"))
+ , m_xCbObject(m_xBuilder->weld_combo_box("datasource"))
+ , m_xLbType(m_xBuilder->weld_combo_box("type"))
+{
+ weld::WaitObject aWait(pParent); // initializing the database service the first time takes a while
+
+ try
+ {
+ // get database names
+
+ uno::Reference<sdb::XDatabaseContext> xContext = sdb::DatabaseContext::create(
+ comphelper::getProcessComponentContext() );
+ const uno::Sequence<OUString> aNames = xContext->getElementNames();
+ for( const OUString& aName : aNames )
+ {
+ m_xLbDatabase->append_text(aName);
+ }
+ }
+ catch(uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "exception in database");
+ }
+
+ m_xLbDatabase->set_active(0);
+ m_xLbType->set_active(0);
+
+ FillObjects();
+
+ m_xLbDatabase->connect_changed( LINK( this, ScDataPilotDatabaseDlg, SelectHdl ) );
+ m_xLbType->connect_changed( LINK( this, ScDataPilotDatabaseDlg, SelectHdl ) );
+}
+
+ScDataPilotDatabaseDlg::~ScDataPilotDatabaseDlg()
+{
+}
+
+void ScDataPilotDatabaseDlg::GetValues( ScImportSourceDesc& rDesc )
+{
+ const sal_Int32 nSelect = m_xLbType->get_active();
+
+ rDesc.aDBName = m_xLbDatabase->get_active_text();
+ rDesc.aObject = m_xCbObject->get_active_text();
+
+ if (rDesc.aDBName.isEmpty() || rDesc.aObject.isEmpty())
+ rDesc.nType = sheet::DataImportMode_NONE;
+ else if ( nSelect == DP_TYPELIST_TABLE )
+ rDesc.nType = sheet::DataImportMode_TABLE;
+ else if ( nSelect == DP_TYPELIST_QUERY )
+ rDesc.nType = sheet::DataImportMode_QUERY;
+ else
+ rDesc.nType = sheet::DataImportMode_SQL;
+
+ rDesc.bNative = ( nSelect == DP_TYPELIST_SQLNAT );
+}
+
+IMPL_LINK_NOARG(ScDataPilotDatabaseDlg, SelectHdl, weld::ComboBox&, void)
+{
+ FillObjects();
+}
+
+void ScDataPilotDatabaseDlg::FillObjects()
+{
+ m_xCbObject->clear();
+
+ OUString aDatabaseName = m_xLbDatabase->get_active_text();
+ if (aDatabaseName.isEmpty())
+ return;
+
+ const int nSelect = m_xLbType->get_active();
+ if ( nSelect > DP_TYPELIST_QUERY )
+ return; // only tables and queries
+
+ try
+ {
+ // open connection (for tables or queries)
+
+ uno::Reference<sdb::XDatabaseContext> xContext = sdb::DatabaseContext::create(
+ comphelper::getProcessComponentContext() );
+
+ uno::Any aSourceAny = xContext->getByName( aDatabaseName );
+ uno::Reference<sdb::XCompletedConnection> xSource(aSourceAny, uno::UNO_QUERY);
+ if ( !xSource.is() ) return;
+
+ uno::Reference<task::XInteractionHandler> xHandler(
+ task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr),
+ uno::UNO_QUERY_THROW);
+
+ uno::Reference<sdbc::XConnection> xConnection = xSource->connectWithCompletion( xHandler );
+
+ uno::Reference<container::XNameAccess> xItems;
+ if ( nSelect == DP_TYPELIST_TABLE )
+ {
+ // get all tables
+
+ uno::Reference<sdbcx::XTablesSupplier> xTablesSupp( xConnection, uno::UNO_QUERY );
+ if ( !xTablesSupp.is() ) return;
+
+ xItems = xTablesSupp->getTables();
+ }
+ else
+ {
+ // get all queries
+
+ uno::Reference<sdb::XQueriesSupplier> xQueriesSupp( xConnection, uno::UNO_QUERY );
+ if ( !xQueriesSupp.is() ) return;
+
+ xItems = xQueriesSupp->getQueries();
+ }
+
+ if ( !xItems.is() ) return;
+
+ // fill list
+ const uno::Sequence<OUString> aNames = xItems->getElementNames();
+ for( const OUString& aName : aNames )
+ {
+ m_xCbObject->append_text(aName);
+ }
+ }
+ catch(uno::Exception&)
+ {
+ // this may happen if an invalid database is selected -> no DBG_ERROR
+ TOOLS_WARN_EXCEPTION( "sc", "exception in database");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dapitype.cxx b/sc/source/ui/dbgui/dapitype.cxx
new file mode 100644
index 0000000000..9843a2bb97
--- /dev/null
+++ b/sc/source/ui/dbgui/dapitype.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <dapitype.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+
+ScDataPilotSourceTypeDlg::ScDataPilotSourceTypeDlg(weld::Window* pParent, bool bEnableExternal)
+ : GenericDialogController(pParent, "modules/scalc/ui/selectsource.ui", "SelectSourceDialog")
+ , m_xBtnSelection(m_xBuilder->weld_radio_button("selection"))
+ , m_xBtnNamedRange(m_xBuilder->weld_radio_button("namedrange"))
+ , m_xBtnDatabase(m_xBuilder->weld_radio_button("database"))
+ , m_xBtnExternal(m_xBuilder->weld_radio_button("external"))
+ , m_xLbNamedRange(m_xBuilder->weld_combo_box("rangelb"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok")) // for LOK jsdialog
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel")) // for LOK jsdialog
+{
+ m_xBtnSelection->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+ m_xBtnNamedRange->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+ m_xBtnDatabase->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+ m_xBtnExternal->connect_toggled( LINK(this, ScDataPilotSourceTypeDlg, RadioClickHdl) );
+
+ m_xBtnOk->connect_clicked( LINK(this, ScDataPilotSourceTypeDlg, ResponseHdl ) );
+ m_xBtnCancel->connect_clicked( LINK(this, ScDataPilotSourceTypeDlg, ResponseHdl ) );
+
+ if (!bEnableExternal)
+ m_xBtnExternal->set_sensitive(false);
+
+ m_xBtnSelection->set_active(true);
+
+ // Disabled unless at least one named range exists.
+ m_xLbNamedRange->set_sensitive(false);
+ m_xBtnNamedRange->set_sensitive(false);
+
+ // Intentionally hide this button to see if anyone complains.
+ m_xBtnExternal->hide();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xBtnDatabase->hide();
+}
+
+IMPL_LINK(ScDataPilotSourceTypeDlg, ResponseHdl, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xBtnOk.get())
+ m_xDialog->response(RET_OK);
+ else
+ m_xDialog->response(RET_CANCEL);
+}
+
+ScDataPilotSourceTypeDlg::~ScDataPilotSourceTypeDlg()
+{
+}
+
+bool ScDataPilotSourceTypeDlg::IsDatabase() const
+{
+ return m_xBtnDatabase->get_active();
+}
+
+bool ScDataPilotSourceTypeDlg::IsExternal() const
+{
+ return m_xBtnExternal->get_active();
+}
+
+bool ScDataPilotSourceTypeDlg::IsNamedRange() const
+{
+ return m_xBtnNamedRange->get_active();
+}
+
+OUString ScDataPilotSourceTypeDlg::GetSelectedNamedRange() const
+{
+ return m_xLbNamedRange->get_active_text();
+}
+
+void ScDataPilotSourceTypeDlg::AppendNamedRange(const OUString& rName)
+{
+ m_xLbNamedRange->append_text(rName);
+ if (m_xLbNamedRange->get_count() == 1)
+ {
+ // Select position 0 only for the first time.
+ m_xLbNamedRange->set_active(0);
+ m_xBtnNamedRange->set_sensitive(true);
+ }
+}
+
+IMPL_LINK_NOARG(ScDataPilotSourceTypeDlg, RadioClickHdl, weld::Toggleable&, void)
+{
+ m_xLbNamedRange->set_sensitive(m_xBtnNamedRange->get_active());
+}
+
+ScDataPilotServiceDlg::ScDataPilotServiceDlg(weld::Window* pParent, const std::vector<OUString>& rServices)
+ : GenericDialogController(pParent, "modules/scalc/ui/dapiservicedialog.ui", "DapiserviceDialog")
+ , m_xLbService(m_xBuilder->weld_combo_box("service"))
+ , m_xEdSource(m_xBuilder->weld_entry("source"))
+ , m_xEdName(m_xBuilder->weld_entry("name"))
+ , m_xEdUser(m_xBuilder->weld_entry("user"))
+ , m_xEdPasswd(m_xBuilder->weld_entry("password"))
+{
+ for (const OUString& aName : rServices)
+ {
+ m_xLbService->append_text(aName);
+ }
+ m_xLbService->set_active(0);
+}
+
+ScDataPilotServiceDlg::~ScDataPilotServiceDlg()
+{
+}
+
+OUString ScDataPilotServiceDlg::GetServiceName() const
+{
+ return m_xLbService->get_active_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParSource() const
+{
+ return m_xEdSource->get_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParName() const
+{
+ return m_xEdName->get_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParUser() const
+{
+ return m_xEdUser->get_text();
+}
+
+OUString ScDataPilotServiceDlg::GetParPass() const
+{
+ return m_xEdPasswd->get_text();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dbnamdlg.cxx b/sc/source/ui/dbgui/dbnamdlg.cxx
new file mode 100644
index 0000000000..68543eb6ce
--- /dev/null
+++ b/sc/source/ui/dbgui/dbnamdlg.cxx
@@ -0,0 +1,634 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <comphelper/string.hxx>
+#include <unotools/charclass.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <reffact.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <rangenam.hxx>
+#include <globalnames.hxx>
+#include <dbnamdlg.hxx>
+#include <dbdocfun.hxx>
+
+namespace
+{
+ void ERRORBOX(weld::Window* pParent, const OUString& rString)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ rString));
+ xBox->run();
+ }
+
+
+class DBSaveData
+{
+public:
+ DBSaveData( formula::RefEdit& rEd, weld::CheckButton& rHdr, weld::CheckButton& rTot, weld::CheckButton& rSize, weld::CheckButton& rFmt,
+ weld::CheckButton& rStrip, ScRange& rArea )
+ : rEdAssign(rEd)
+ , rBtnHeader(rHdr)
+ , rBtnTotals(rTot)
+ , rBtnSize(rSize)
+ , rBtnFormat(rFmt)
+ , rBtnStrip(rStrip)
+ , rCurArea(rArea)
+ , bHeader(false)
+ , bTotals(false)
+ , bSize(false)
+ , bFormat(false)
+ , bStrip(false)
+ , bDirty(false)
+ {
+ }
+ void Save();
+ void Restore();
+
+private:
+ formula::RefEdit& rEdAssign;
+ weld::CheckButton& rBtnHeader;
+ weld::CheckButton& rBtnTotals;
+ weld::CheckButton& rBtnSize;
+ weld::CheckButton& rBtnFormat;
+ weld::CheckButton& rBtnStrip;
+ ScRange& rCurArea;
+ OUString aStr;
+ ScRange aArea;
+ bool bHeader:1;
+ bool bTotals:1;
+ bool bSize:1;
+ bool bFormat:1;
+ bool bStrip:1;
+ bool bDirty:1;
+};
+
+}
+
+void DBSaveData::Save()
+{
+ aArea = rCurArea;
+ aStr = rEdAssign.GetText();
+ bHeader = rBtnHeader.get_active();
+ bTotals = rBtnTotals.get_active();
+ bSize = rBtnSize.get_active();
+ bFormat = rBtnFormat.get_active();
+ bStrip = rBtnStrip.get_active();
+ bDirty = true;
+}
+
+void DBSaveData::Restore()
+{
+ if ( bDirty )
+ {
+ rCurArea = aArea;
+ rEdAssign.SetText( aStr );
+ rBtnHeader.set_active ( bHeader );
+ rBtnTotals.set_active ( bTotals );
+ rBtnSize.set_active ( bSize );
+ rBtnFormat.set_active ( bFormat );
+ rBtnStrip.set_active ( bStrip );
+ bDirty = false;
+ }
+}
+
+static std::unique_ptr<DBSaveData> xSaveObj;
+
+ScDbNameDlg::ScDbNameDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData)
+ : ScAnyRefDlgController(pB, pCW, pParent,
+ "modules/scalc/ui/definedatabaserangedialog.ui", "DefineDatabaseRangeDialog")
+ , m_rViewData(rViewData)
+ , rDoc(rViewData.GetDocument())
+ , bRefInputMode(false)
+ , aAddrDetails(rDoc.GetAddressConvention(), 0, 0)
+ , aLocalDbCol(*(rDoc.GetDBCollection()))
+ , m_xEdName(m_xBuilder->weld_entry_tree_view("entrygrid", "entry", "entry-list"))
+ , m_xAssignFrame(m_xBuilder->weld_frame("RangeFrame"))
+ , m_xEdAssign(new formula::RefEdit(m_xBuilder->weld_entry("assign")))
+ , m_xRbAssign(new formula::RefButton(m_xBuilder->weld_button("assignrb")))
+ , m_xOptions(m_xBuilder->weld_widget("Options"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("ContainsColumnLabels"))
+ , m_xBtnTotals(m_xBuilder->weld_check_button("ContainsTotalsRow"))
+ , m_xBtnDoSize(m_xBuilder->weld_check_button("InsertOrDeleteCells"))
+ , m_xBtnKeepFmt(m_xBuilder->weld_check_button("KeepFormatting"))
+ , m_xBtnStripData(m_xBuilder->weld_check_button("DontSaveImportedData"))
+ , m_xFTSource(m_xBuilder->weld_label("Source"))
+ , m_xFTOperations(m_xBuilder->weld_label("Operations"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("delete"))
+ , m_xModifyPB(m_xBuilder->weld_button("modify"))
+ , m_xInvalidFT(m_xBuilder->weld_label("invalid"))
+ , m_xFrameLabel(m_xAssignFrame->weld_label_widget())
+{
+ m_xEdName->set_height_request_by_rows(4);
+ m_xEdAssign->SetReferences(this, m_xFrameLabel.get());
+ m_xRbAssign->SetReferences(this, m_xEdAssign.get());
+ aStrAdd = m_xBtnAdd->get_label();
+ aStrModify = m_xModifyPB->get_label();
+ aStrInvalid = m_xInvalidFT->get_label();
+
+ // so that the strings in the resource can stay with fixed texts:
+ aStrSource = m_xFTSource->get_label();
+ aStrOperations = m_xFTOperations->get_label();
+
+ xSaveObj.reset(new DBSaveData( *m_xEdAssign, *m_xBtnHeader, *m_xBtnTotals,
+ *m_xBtnDoSize, *m_xBtnKeepFmt, *m_xBtnStripData, theCurArea ));
+ Init();
+}
+
+ScDbNameDlg::~ScDbNameDlg()
+{
+ xSaveObj.reset();
+}
+
+void ScDbNameDlg::Init()
+{
+ m_xBtnHeader->set_active(true); // Default: with column headers
+ m_xBtnTotals->set_active( false ); // Default: without totals row
+ m_xBtnDoSize->set_active(true);
+ m_xBtnKeepFmt->set_active(true);
+
+ m_xBtnOk->connect_clicked ( LINK( this, ScDbNameDlg, OkBtnHdl ) );
+ m_xBtnCancel->connect_clicked ( LINK( this, ScDbNameDlg, CancelBtnHdl ) );
+ m_xBtnAdd->connect_clicked ( LINK( this, ScDbNameDlg, AddBtnHdl ) );
+ m_xBtnRemove->connect_clicked ( LINK( this, ScDbNameDlg, RemoveBtnHdl ) );
+ m_xEdName->connect_changed( LINK( this, ScDbNameDlg, NameModifyHdl ) );
+ m_xEdAssign->SetModifyHdl ( LINK( this, ScDbNameDlg, AssModifyHdl ) );
+ UpdateNames();
+
+ OUString theAreaStr;
+
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCTAB nStartTab = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ SCTAB nEndTab = 0;
+
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+
+ m_rViewData.GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ theCurArea = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+
+ theAreaStr = theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, aAddrDetails);
+
+ if ( pDBColl )
+ {
+ // determine if the defined DB area has been marked:
+ ScDBData* pDBData = pDBColl->GetDBAtCursor( nStartCol, nStartRow, nStartTab, ScDBDataPortion::TOP_LEFT );
+ if ( pDBData )
+ {
+ ScAddress& rStart = theCurArea.aStart;
+ ScAddress& rEnd = theCurArea.aEnd;
+ SCCOL nCol1;
+ SCCOL nCol2;
+ SCROW nRow1;
+ SCROW nRow2;
+ SCTAB nTab;
+
+ pDBData->GetArea( nTab, nCol1, nRow1, nCol2, nRow2 );
+
+ if ( (rStart.Tab() == nTab)
+ && (rStart.Col() == nCol1) && (rStart.Row() == nRow1)
+ && (rEnd.Col() == nCol2) && (rEnd.Row() == nRow2 ) )
+ {
+ OUString aDBName = pDBData->GetName();
+ if ( aDBName != STR_DB_LOCAL_NONAME )
+ m_xEdName->set_entry_text(aDBName);
+
+ m_xBtnHeader->set_active( pDBData->HasHeader() );
+ m_xBtnTotals->set_active( pDBData->HasTotals() );
+ m_xBtnDoSize->set_active( pDBData->IsDoSize() );
+ m_xBtnKeepFmt->set_active( pDBData->IsKeepFmt() );
+ m_xBtnStripData->set_active( pDBData->IsStripData() );
+ SetInfoStrings( pDBData );
+ }
+ }
+ }
+
+ m_xEdAssign->SetText( theAreaStr );
+ m_xEdName->grab_focus();
+ bSaved = true;
+ xSaveObj->Save();
+ NameModifyHdl( *m_xEdName );
+ bInvalid = false;
+}
+
+void ScDbNameDlg::SetInfoStrings( const ScDBData* pDBData )
+{
+ OUStringBuffer aBuf(aStrSource);
+ if (pDBData)
+ {
+ aBuf.append(" " + pDBData->GetSourceString());
+ }
+ m_xFTSource->set_label(aBuf.makeStringAndClear());
+
+ aBuf.append(aStrOperations);
+ if (pDBData)
+ {
+ aBuf.append(" " + pDBData->GetOperations());
+ }
+ m_xFTOperations->set_label(aBuf.makeStringAndClear());
+}
+
+// Transfer of a table area selected with the mouse, which is then displayed
+// as a new selection in the reference window.
+
+void ScDbNameDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (!m_xEdAssign->GetWidget()->get_sensitive())
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_xEdAssign.get());
+
+ theCurArea = rRef;
+
+ OUString aRefStr(theCurArea.Format(rDocP, ScRefFlags::RANGE_ABS_3D, aAddrDetails));
+ m_xEdAssign->SetRefString( aRefStr );
+ m_xOptions->set_sensitive(true);
+ m_xBtnAdd->set_sensitive(true);
+ bSaved = true;
+ xSaveObj->Save();
+}
+
+void ScDbNameDlg::Close()
+{
+ DoClose( ScDbNameDlgWrapper::GetChildWindowId() );
+}
+
+void ScDbNameDlg::SetActive()
+{
+ m_xEdAssign->GrabFocus();
+
+ // No NameModifyHdl, because otherwise areas can not be changed
+ // (the old content would be displayed again after the reference selection is pulled)
+ // (the selected DB name has not changed either)
+
+ RefInputDone();
+}
+
+void ScDbNameDlg::UpdateNames()
+{
+ typedef ScDBCollection::NamedDBs DBsType;
+
+ const DBsType& rDBs = aLocalDbCol.getNamedDBs();
+
+ m_xEdName->freeze();
+
+ m_xEdName->clear();
+ m_xEdAssign->SetText( OUString() );
+
+ if (!rDBs.empty())
+ {
+ for (const auto& rxDB : rDBs)
+ m_xEdName->append_text(rxDB->GetName());
+ }
+ else
+ {
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ }
+
+ m_xEdName->thaw();
+}
+
+void ScDbNameDlg::UpdateDBData( const OUString& rStrName )
+{
+
+ const ScDBData* pData = aLocalDbCol.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rStrName));
+
+ if ( pData )
+ {
+ SCCOL nColStart = 0;
+ SCROW nRowStart = 0;
+ SCCOL nColEnd = 0;
+ SCROW nRowEnd = 0;
+ SCTAB nTab = 0;
+
+ pData->GetArea( nTab, nColStart, nRowStart, nColEnd, nRowEnd );
+ theCurArea = ScRange( ScAddress( nColStart, nRowStart, nTab ),
+ ScAddress( nColEnd, nRowEnd, nTab ) );
+ OUString theArea(theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, aAddrDetails));
+ m_xEdAssign->SetText( theArea );
+ m_xBtnAdd->set_label( aStrModify );
+ m_xBtnHeader->set_active( pData->HasHeader() );
+ m_xBtnTotals->set_active( pData->HasTotals() );
+ m_xBtnDoSize->set_active( pData->IsDoSize() );
+ m_xBtnKeepFmt->set_active( pData->IsKeepFmt() );
+ m_xBtnStripData->set_active( pData->IsStripData() );
+ SetInfoStrings( pData );
+ }
+
+ m_xBtnAdd->set_label( aStrModify );
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnRemove->set_sensitive(true);
+ m_xOptions->set_sensitive(true);
+}
+
+bool ScDbNameDlg::IsRefInputMode() const
+{
+ return bRefInputMode;
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScDbNameDlg, OkBtnHdl, weld::Button&, void)
+{
+ bInvalid = false;
+ AddBtnHdl(*m_xBtnAdd);
+
+ // Pass the changes and the remove list to the view: both are
+ // transferred as a reference only, so that no dead memory can
+ // be created at this point:
+ if (!bInvalid)
+ {
+ ScDBDocFunc aFunc(*m_rViewData.GetDocShell());
+ aFunc.ModifyAllDBData(aLocalDbCol, aRemoveList);
+ response(RET_OK);
+ }
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, CancelBtnHdl, weld::Button&, void)
+{
+ response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, AddBtnHdl, weld::Button&, void)
+{
+ OUString aNewName = comphelper::string::strip(m_xEdName->get_active_text(), ' ');
+ OUString aNewArea = m_xEdAssign->GetText();
+
+ if ( aNewName.isEmpty() || aNewArea.isEmpty() )
+ return;
+
+ if (ScRangeData::IsNameValid(aNewName, rDoc) == ScRangeData::IsNameValidType::NAME_VALID
+ && aNewName != STR_DB_LOCAL_NONAME)
+ {
+ // because editing can be done now, parsing is needed first
+ ScRange aTmpRange;
+ OUString aText = m_xEdAssign->GetText();
+ if ( aTmpRange.ParseAny( aText, rDoc, aAddrDetails ) & ScRefFlags::VALID )
+ {
+ theCurArea = aTmpRange;
+ ScAddress aStart = theCurArea.aStart;
+ ScAddress aEnd = theCurArea.aEnd;
+
+ ScDBData* pOldEntry = aLocalDbCol.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aNewName));
+ if (pOldEntry)
+ {
+ // modify area
+
+ pOldEntry->MoveTo( aStart.Tab(), aStart.Col(), aStart.Row(),
+ aEnd.Col(), aEnd.Row() );
+ pOldEntry->SetByRow( true );
+ pOldEntry->SetHeader( m_xBtnHeader->get_active() );
+ pOldEntry->SetTotals( m_xBtnTotals->get_active() );
+ pOldEntry->SetDoSize( m_xBtnDoSize->get_active() );
+ pOldEntry->SetKeepFmt( m_xBtnKeepFmt->get_active() );
+ pOldEntry->SetStripData( m_xBtnStripData->get_active() );
+ }
+ else
+ {
+ // insert new area
+
+ std::unique_ptr<ScDBData> pNewEntry(new ScDBData( aNewName, aStart.Tab(),
+ aStart.Col(), aStart.Row(),
+ aEnd.Col(), aEnd.Row(),
+ true, m_xBtnHeader->get_active(),
+ m_xBtnTotals->get_active() ));
+ pNewEntry->SetDoSize( m_xBtnDoSize->get_active() );
+ pNewEntry->SetKeepFmt( m_xBtnKeepFmt->get_active() );
+ pNewEntry->SetStripData( m_xBtnStripData->get_active() );
+
+ bool ins = aLocalDbCol.getNamedDBs().insert(std::move(pNewEntry));
+ assert(ins); (void)ins;
+ }
+
+ UpdateNames();
+
+ m_xEdName->set_entry_text( OUString() );
+ m_xEdName->grab_focus();
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->SetText( OUString() );
+ m_xBtnHeader->set_active(true); // Default: with column headers
+ m_xBtnTotals->set_active( false ); // Default: without totals row
+ m_xBtnDoSize->set_active( false );
+ m_xBtnKeepFmt->set_active( false );
+ m_xBtnStripData->set_active( false );
+ SetInfoStrings( nullptr ); // empty
+ theCurArea = ScRange();
+ bSaved = true;
+ xSaveObj->Save();
+ NameModifyHdl( *m_xEdName );
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), aStrInvalid);
+ m_xEdAssign->SelectAll();
+ m_xEdAssign->GrabFocus();
+ bInvalid = true;
+ }
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), ScResId(STR_INVALIDNAME));
+ m_xEdName->select_entry_region(0, -1);
+ m_xEdName->grab_focus();
+ bInvalid = true;
+ }
+}
+
+namespace {
+
+class FindByName
+{
+ const OUString& mrName;
+public:
+ explicit FindByName(const OUString& rName) : mrName(rName) {}
+ bool operator() (std::unique_ptr<ScDBData> const& p) const
+ {
+ return p->GetName() == mrName;
+ }
+};
+
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, RemoveBtnHdl, weld::Button&, void)
+{
+ OUString aStrEntry = m_xEdName->get_active_text();
+ ScDBCollection::NamedDBs& rDBs = aLocalDbCol.getNamedDBs();
+ ScDBCollection::NamedDBs::iterator itr =
+ ::std::find_if(rDBs.begin(), rDBs.end(), FindByName(aStrEntry));
+
+ if (itr == rDBs.end())
+ return;
+
+ OUString aStrDelMsg = ScResId( STR_QUERY_DELENTRY );
+ OUString sMsg{ o3tl::getToken(aStrDelMsg, 0, '#') + aStrEntry + o3tl::getToken(aStrDelMsg, 1, '#') };
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ sMsg));
+ xQueryBox->set_default_response(RET_YES);
+ if (RET_YES != xQueryBox->run())
+ return;
+
+ SCTAB nTab;
+ SCCOL nColStart, nColEnd;
+ SCROW nRowStart, nRowEnd;
+ (*itr)->GetArea( nTab, nColStart, nRowStart, nColEnd, nRowEnd );
+ aRemoveList.emplace_back( ScAddress( nColStart, nRowStart, nTab ),
+ ScAddress( nColEnd, nRowEnd, nTab ) );
+
+ rDBs.erase(itr);
+
+ UpdateNames();
+
+ m_xEdName->set_entry_text( OUString() );
+ m_xEdName->grab_focus();
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->SetText( OUString() );
+ theCurArea = ScRange();
+ m_xBtnHeader->set_active(true); // Default: with column headers
+ m_xBtnTotals->set_active( false ); // Default: without totals row
+ m_xBtnDoSize->set_active( false );
+ m_xBtnKeepFmt->set_active( false );
+ m_xBtnStripData->set_active( false );
+ SetInfoStrings( nullptr ); // empty
+ bSaved=false;
+ xSaveObj->Restore();
+ NameModifyHdl( *m_xEdName );
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, NameModifyHdl, weld::ComboBox&, void)
+{
+ OUString theName = m_xEdName->get_active_text();
+ bool bNameFound = m_xEdName->find_text(theName) != -1;
+
+ if ( theName.isEmpty() )
+ {
+ if (m_xBtnAdd->get_label() != aStrAdd)
+ m_xBtnAdd->set_label( aStrAdd );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xAssignFrame->set_sensitive(false);
+ m_xOptions->set_sensitive(false);
+ //bSaved=sal_False;
+ //xSaveObj->Restore();
+ //@BugID 54702 enable/disable in the base class only
+ //SFX_APPWINDOW->Disable(sal_False); //! general method in ScAnyRefDlg
+ bRefInputMode = false;
+ }
+ else
+ {
+ if ( bNameFound )
+ {
+ if (m_xBtnAdd->get_label() != aStrModify)
+ m_xBtnAdd->set_label( aStrModify );
+
+ if(!bSaved)
+ {
+ bSaved = true;
+ xSaveObj->Save();
+ }
+ UpdateDBData( theName );
+ }
+ else
+ {
+ if (m_xBtnAdd->get_label() != aStrAdd)
+ m_xBtnAdd->set_label( aStrAdd );
+
+ bSaved=false;
+ xSaveObj->Restore();
+
+ if ( !m_xEdAssign->GetText().isEmpty() )
+ {
+ m_xBtnAdd->set_sensitive(true);
+ m_xOptions->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xOptions->set_sensitive(false);
+ }
+ m_xBtnRemove->set_sensitive(false);
+ }
+
+ m_xAssignFrame->set_sensitive(true);
+
+ //@BugID 54702 enable/disable in the base class only
+ //SFX_APPWINDOW->set_sensitive(true);
+ bRefInputMode = true;
+ }
+}
+
+IMPL_LINK_NOARG(ScDbNameDlg, AssModifyHdl, formula::RefEdit&, void)
+{
+ // parse here for Save(), etc.
+
+ ScRange aTmpRange;
+ OUString aText = m_xEdAssign->GetText();
+ if ( aTmpRange.ParseAny( aText, rDoc, aAddrDetails ) & ScRefFlags::VALID )
+ theCurArea = aTmpRange;
+
+ if (!aText.isEmpty() && !m_xEdName->get_active_text().isEmpty())
+ {
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnHeader->set_sensitive(true);
+ m_xBtnTotals->set_sensitive(true);
+ m_xBtnDoSize->set_sensitive(true);
+ m_xBtnKeepFmt->set_sensitive(true);
+ m_xBtnStripData->set_sensitive(true);
+ m_xFTSource->set_sensitive(true);
+ m_xFTOperations->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnHeader->set_sensitive(false);
+ m_xBtnTotals->set_sensitive(false);
+ m_xBtnDoSize->set_sensitive(false);
+ m_xBtnKeepFmt->set_sensitive(false);
+ m_xBtnStripData->set_sensitive(false);
+ m_xFTSource->set_sensitive(false);
+ m_xFTOperations->set_sensitive(false);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/dpgroupdlg.cxx b/sc/source/ui/dbgui/dpgroupdlg.cxx
new file mode 100644
index 0000000000..550695cc31
--- /dev/null
+++ b/sc/source/ui/dbgui/dpgroupdlg.cxx
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifdef SC_DLLIMPLEMENTATION
+#undef SC_DLLIMPLEMENTATION
+#endif
+
+#include <dpgroupdlg.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <editfield.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <svtools/ctrlbox.hxx>
+
+namespace {
+
+/** Date part flags in order of the list box entries. */
+const sal_Int32 spnDateParts[] =
+{
+ css::sheet::DataPilotFieldGroupBy::SECONDS,
+ css::sheet::DataPilotFieldGroupBy::MINUTES,
+ css::sheet::DataPilotFieldGroupBy::HOURS,
+ css::sheet::DataPilotFieldGroupBy::DAYS,
+ css::sheet::DataPilotFieldGroupBy::MONTHS,
+ css::sheet::DataPilotFieldGroupBy::QUARTERS,
+ css::sheet::DataPilotFieldGroupBy::YEARS
+};
+
+const TranslateId aDatePartResIds[] =
+{
+ STR_DPFIELD_GROUP_BY_SECONDS,
+ STR_DPFIELD_GROUP_BY_MINUTES,
+ STR_DPFIELD_GROUP_BY_HOURS,
+ STR_DPFIELD_GROUP_BY_DAYS,
+ STR_DPFIELD_GROUP_BY_MONTHS,
+ STR_DPFIELD_GROUP_BY_QUARTERS,
+ STR_DPFIELD_GROUP_BY_YEARS
+};
+
+} // namespace
+
+ScDPGroupEditHelper::ScDPGroupEditHelper(weld::RadioButton& rRbAuto, weld::RadioButton& rRbMan, weld::Widget& rEdValue)
+ : mrRbAuto(rRbAuto)
+ , mrRbMan(rRbMan)
+ , mrEdValue(rEdValue)
+{
+ mrRbAuto.connect_toggled( LINK( this, ScDPGroupEditHelper, ToggleHdl ) );
+ mrRbMan.connect_toggled( LINK( this, ScDPGroupEditHelper, ToggleHdl ) );
+}
+
+bool ScDPGroupEditHelper::IsAuto() const
+{
+ return mrRbAuto.get_active();
+}
+
+double ScDPGroupEditHelper::GetValue() const
+{
+ double fValue;
+ if( !ImplGetValue( fValue ) )
+ fValue = 0.0;
+ return fValue;
+}
+
+void ScDPGroupEditHelper::SetValue( bool bAuto, double fValue )
+{
+ if( bAuto )
+ {
+ mrRbAuto.set_active(true);
+ ToggleHdl(mrRbAuto);
+ }
+ else
+ {
+ mrRbMan.set_active(true);
+ ToggleHdl(mrRbMan);
+ }
+ ImplSetValue( fValue );
+}
+
+IMPL_LINK(ScDPGroupEditHelper, ToggleHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+
+ if (mrRbAuto.get_active())
+ {
+ // disable edit field on clicking "automatic" radio button
+ mrEdValue.set_sensitive(false);
+ }
+ else if (mrRbMan.get_active())
+ {
+ // enable and set focus to edit field on clicking "manual" radio button
+ mrEdValue.set_sensitive(true);
+ mrEdValue.grab_focus();
+ }
+}
+
+ScDPNumGroupEditHelper::ScDPNumGroupEditHelper(weld::RadioButton& rRbAuto,
+ weld::RadioButton& rRbMan, ScDoubleField& rEdValue)
+ : ScDPGroupEditHelper(rRbAuto, rRbMan, rEdValue.get_widget())
+ , mrEdValue(rEdValue)
+{
+}
+
+bool ScDPNumGroupEditHelper::ImplGetValue( double& rfValue ) const
+{
+ return mrEdValue.GetValue(rfValue);
+}
+
+void ScDPNumGroupEditHelper::ImplSetValue( double fValue )
+{
+ mrEdValue.SetValue(fValue);
+}
+
+ScDPDateGroupEditHelper::ScDPDateGroupEditHelper(weld::RadioButton& rRbAuto, weld::RadioButton& rRbMan,
+ SvtCalendarBox& rEdValue, const Date& rNullDate)
+ : ScDPGroupEditHelper(rRbAuto, rRbMan, rEdValue.get_button())
+ , mrEdValue(rEdValue)
+ , maNullDate(rNullDate)
+{
+}
+
+bool ScDPDateGroupEditHelper::ImplGetValue( double& rfValue ) const
+{
+ rfValue = mrEdValue.get_date() - maNullDate;
+ return true;
+}
+
+void ScDPDateGroupEditHelper::ImplSetValue( double fValue )
+{
+ Date aDate( maNullDate );
+ aDate.AddDays( fValue );
+ mrEdValue.set_date( aDate );
+}
+
+ScDPNumGroupDlg::ScDPNumGroupDlg(weld::Window* pParent, const ScDPNumGroupInfo& rInfo)
+ : GenericDialogController(pParent, "modules/scalc/ui/groupbynumber.ui", "PivotTableGroupByNumber")
+ , mxRbAutoStart(m_xBuilder->weld_radio_button("auto_start"))
+ , mxRbManStart(m_xBuilder->weld_radio_button("manual_start"))
+ , mxEdStart(new ScDoubleField(m_xBuilder->weld_entry("edit_start")))
+ , mxRbAutoEnd(m_xBuilder->weld_radio_button("auto_end"))
+ , mxRbManEnd(m_xBuilder->weld_radio_button("manual_end"))
+ , mxEdEnd(new ScDoubleField(m_xBuilder->weld_entry("edit_end")))
+ , mxEdBy(new ScDoubleField(m_xBuilder->weld_entry("edit_by")))
+ , maStartHelper(*mxRbAutoStart, *mxRbManStart, *mxEdStart)
+ , maEndHelper(*mxRbAutoEnd, *mxRbManEnd, *mxEdEnd)
+{
+ maStartHelper.SetValue( rInfo.mbAutoStart, rInfo.mfStart );
+ maEndHelper.SetValue( rInfo.mbAutoEnd, rInfo.mfEnd );
+ mxEdBy->SetValue( (rInfo.mfStep <= 0.0) ? 1.0 : rInfo.mfStep );
+
+ /* Set the initial focus, currently it is somewhere after calling all the radio
+ button click handlers. Now the first enabled editable control is focused. */
+ if (mxEdStart->get_sensitive())
+ mxEdStart->grab_focus();
+ else if (mxEdEnd->get_sensitive())
+ mxEdEnd->grab_focus();
+ else
+ mxEdBy->grab_focus();
+}
+
+ScDPNumGroupDlg::~ScDPNumGroupDlg()
+{
+}
+
+ScDPNumGroupInfo ScDPNumGroupDlg::GetGroupInfo() const
+{
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = false;
+ aInfo.mbAutoStart = maStartHelper.IsAuto();
+ aInfo.mbAutoEnd = maEndHelper.IsAuto();
+
+ // get values and silently auto-correct them, if they are not valid
+ // TODO: error messages in OK event?
+ aInfo.mfStart = maStartHelper.GetValue();
+ aInfo.mfEnd = maEndHelper.GetValue();
+ if( !mxEdBy->GetValue( aInfo.mfStep ) || (aInfo.mfStep <= 0.0) )
+ aInfo.mfStep = 1.0;
+ if( aInfo.mfEnd <= aInfo.mfStart )
+ aInfo.mfEnd = aInfo.mfStart + aInfo.mfStep;
+
+ return aInfo;
+}
+
+ScDPDateGroupDlg::ScDPDateGroupDlg(weld::Window* pParent,
+ const ScDPNumGroupInfo& rInfo, sal_Int32 nDatePart, const Date& rNullDate)
+ : GenericDialogController(pParent, "modules/scalc/ui/groupbydate.ui", "PivotTableGroupByDate")
+ , mxRbAutoStart(m_xBuilder->weld_radio_button("auto_start"))
+ , mxRbManStart(m_xBuilder->weld_radio_button("manual_start"))
+ , mxEdStart(new SvtCalendarBox(m_xBuilder->weld_menu_button("start_date")))
+ , mxRbAutoEnd(m_xBuilder->weld_radio_button("auto_end"))
+ , mxRbManEnd(m_xBuilder->weld_radio_button("manual_end"))
+ , mxEdEnd(new SvtCalendarBox(m_xBuilder->weld_menu_button("end_date")))
+ , mxRbNumDays(m_xBuilder->weld_radio_button("days"))
+ , mxRbUnits(m_xBuilder->weld_radio_button("intervals"))
+ , mxEdNumDays(m_xBuilder->weld_spin_button("days_value"))
+ , mxLbUnits(m_xBuilder->weld_tree_view("interval_list"))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , maStartHelper(*mxRbAutoStart, *mxRbManStart, *mxEdStart, rNullDate)
+ , maEndHelper(*mxRbAutoEnd, *mxRbManEnd, *mxEdEnd, rNullDate)
+{
+ maStartHelper.SetValue( rInfo.mbAutoStart, rInfo.mfStart );
+ maEndHelper.SetValue( rInfo.mbAutoEnd, rInfo.mfEnd );
+
+ mxLbUnits->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ if( nDatePart == 0 )
+ nDatePart = css::sheet::DataPilotFieldGroupBy::MONTHS;
+ for (size_t nIdx = 0; nIdx < SAL_N_ELEMENTS(aDatePartResIds); ++nIdx)
+ {
+ mxLbUnits->append();
+ mxLbUnits->set_toggle(nIdx, (nDatePart & spnDateParts[ nIdx ]) ? TRISTATE_TRUE : TRISTATE_FALSE);
+ mxLbUnits->set_text(nIdx, ScResId(aDatePartResIds[nIdx]), 0);
+ }
+
+ if( rInfo.mbDateValues )
+ {
+ mxRbNumDays->set_active(true);
+ ToggleHdl(*mxRbNumDays );
+
+ double fNumDays = rInfo.mfStep;
+ if( fNumDays < 1.0 )
+ fNumDays = 1.0;
+ else if( fNumDays > 32767.0 )
+ fNumDays = 32767.0;
+ mxEdNumDays->set_value(fNumDays);
+ }
+ else
+ {
+ mxRbUnits->set_active(true);
+ ToggleHdl(*mxRbUnits);
+ }
+
+ /* Set the initial focus, currently it is somewhere after calling all the radio
+ button click handlers. Now the first enabled editable control is focused. */
+ if( mxEdStart->get_sensitive() )
+ mxEdStart->grab_focus();
+ else if( mxEdEnd->get_sensitive() )
+ mxEdEnd->grab_focus();
+ else if( mxEdNumDays->get_sensitive() )
+ mxEdNumDays->grab_focus();
+ else if( mxLbUnits->get_sensitive() )
+ mxLbUnits->grab_focus();
+
+ mxRbNumDays->connect_toggled( LINK( this, ScDPDateGroupDlg, ToggleHdl ) );
+ mxRbUnits->connect_toggled( LINK( this, ScDPDateGroupDlg, ToggleHdl ) );
+ mxLbUnits->connect_toggled( LINK( this, ScDPDateGroupDlg, CheckHdl ) );
+}
+
+ScDPDateGroupDlg::~ScDPDateGroupDlg()
+{
+}
+
+ScDPNumGroupInfo ScDPDateGroupDlg::GetGroupInfo() const
+{
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = mxRbNumDays->get_active();
+ aInfo.mbAutoStart = maStartHelper.IsAuto();
+ aInfo.mbAutoEnd = maEndHelper.IsAuto();
+
+ // get values and silently auto-correct them, if they are not valid
+ // TODO: error messages in OK event?
+ aInfo.mfStart = maStartHelper.GetValue();
+ aInfo.mfEnd = maEndHelper.GetValue();
+ sal_Int64 nNumDays = mxEdNumDays->get_value();
+ aInfo.mfStep = static_cast<double>( aInfo.mbDateValues ? nNumDays : 0L );
+ if( aInfo.mfEnd <= aInfo.mfStart )
+ aInfo.mfEnd = aInfo.mfStart + nNumDays;
+
+ return aInfo;
+}
+
+sal_Int32 ScDPDateGroupDlg::GetDatePart() const
+{
+ // return DAYS for special "number of days" mode
+ if( mxRbNumDays->get_active() )
+ return css::sheet::DataPilotFieldGroupBy::DAYS;
+
+ // return listbox contents for "units" mode
+ sal_Int32 nDatePart = 0;
+ for (int nIdx = 0, nCount = mxLbUnits->n_children(); nIdx < nCount; ++nIdx )
+ if (mxLbUnits->get_toggle(nIdx) == TRISTATE_TRUE)
+ nDatePart |= spnDateParts[ nIdx ];
+ return nDatePart;
+}
+
+IMPL_LINK(ScDPDateGroupDlg, ToggleHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+ if (mxRbNumDays->get_active())
+ {
+ mxLbUnits->set_sensitive(false);
+ // enable and set focus to edit field on clicking "num of days" radio button
+ mxEdNumDays->set_sensitive(true);
+ mxEdNumDays->grab_focus();
+ mxBtnOk->set_sensitive(true);
+ }
+ else if (mxRbUnits->get_active())
+ {
+ mxEdNumDays->set_sensitive(false);
+ // enable and set focus to listbox on clicking "units" radio button
+ mxLbUnits->set_sensitive(true);
+ mxLbUnits->grab_focus();
+ // disable OK button if no date part selected
+ Check();
+ }
+}
+
+namespace
+{
+ bool HasCheckedEntryCount(const weld::TreeView& rView)
+ {
+ for (int i = 0; i < rView.n_children(); ++i)
+ {
+ if (rView.get_toggle(i) == TRISTATE_TRUE)
+ return true;
+ }
+ return false;
+ }
+}
+
+IMPL_LINK_NOARG(ScDPDateGroupDlg, CheckHdl, const weld::TreeView::iter_col&, void)
+{
+ Check();
+}
+
+void ScDPDateGroupDlg::Check()
+{
+ // enable/disable OK button on modifying check list box
+ mxBtnOk->set_sensitive(HasCheckedEntryCount(*mxLbUnits));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/filtdlg.cxx b/sc/source/ui/dbgui/filtdlg.cxx
new file mode 100644
index 0000000000..ca9a59ec56
--- /dev/null
+++ b/sc/source/ui/dbgui/filtdlg.cxx
@@ -0,0 +1,1571 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sal/log.hxx>
+
+#include <uiitems.hxx>
+#include <reffact.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scresid.hxx>
+#include <queryentry.hxx>
+
+#include <foptmgr.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <filtdlg.hxx>
+#include <svx/colorwindow.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <limits>
+
+#define QUERY_ENTRY_COUNT 4
+#define INVALID_HEADER_POS std::numeric_limits<size_t>::max()
+
+ScFilterDlg::EntryList::EntryList() :
+ mnHeaderPos(INVALID_HEADER_POS) {}
+
+ScFilterDlg::ScFilterDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet)
+ : ScAnyRefDlgController(pB, pCW, pParent,
+ "modules/scalc/ui/standardfilterdialog.ui", "StandardFilterDialog")
+ , aStrUndefined(ScResId(SCSTR_UNDEFINED))
+ , aStrNone(ScResId(SCSTR_NONE))
+ , aStrEmpty(ScResId(SCSTR_FILTER_EMPTY))
+ , aStrNotEmpty(ScResId(SCSTR_FILTER_NOTEMPTY))
+ , aStrColumn(ScResId(SCSTR_COLUMN_LETTER))
+ , aStrFontColor(ScResId(SCSTR_FILTER_FONT_COLOR_COND))
+ , aStrBackgroundColor(ScResId(SCSTR_FILTER_BACKGROUND_COLOR_COND))
+ , nWhichQuery(rArgSet.GetPool()->GetWhich(SID_QUERY))
+ , theQueryData(static_cast<const ScQueryItem&>(rArgSet.Get(nWhichQuery)).GetQueryData())
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , nSrcTab(0)
+ , bRefInputMode(false)
+ , m_xLbConnect1(m_xBuilder->weld_combo_box("connect1"))
+ , m_xLbField1(m_xBuilder->weld_combo_box("field1"))
+ , m_xLbCond1(m_xBuilder->weld_combo_box("cond1"))
+ , m_xEdVal1(m_xBuilder->weld_combo_box("val1"))
+ , m_xLbColor1(m_xBuilder->weld_combo_box("color1"))
+ , m_xBtnRemove1(m_xBuilder->weld_button("remove1"))
+ , m_xLbConnect2(m_xBuilder->weld_combo_box("connect2"))
+ , m_xLbField2(m_xBuilder->weld_combo_box("field2"))
+ , m_xLbCond2(m_xBuilder->weld_combo_box("cond2"))
+ , m_xEdVal2(m_xBuilder->weld_combo_box("val2"))
+ , m_xLbColor2(m_xBuilder->weld_combo_box("color2"))
+ , m_xBtnRemove2(m_xBuilder->weld_button("remove2"))
+ , m_xLbConnect3(m_xBuilder->weld_combo_box("connect3"))
+ , m_xLbField3(m_xBuilder->weld_combo_box("field3"))
+ , m_xLbCond3(m_xBuilder->weld_combo_box("cond3"))
+ , m_xEdVal3(m_xBuilder->weld_combo_box("val3"))
+ , m_xLbColor3(m_xBuilder->weld_combo_box("color3"))
+ , m_xBtnRemove3(m_xBuilder->weld_button("remove3"))
+ , m_xLbConnect4(m_xBuilder->weld_combo_box("connect4"))
+ , m_xLbField4(m_xBuilder->weld_combo_box("field4"))
+ , m_xLbCond4(m_xBuilder->weld_combo_box("cond4"))
+ , m_xEdVal4(m_xBuilder->weld_combo_box("val4"))
+ , m_xLbColor4(m_xBuilder->weld_combo_box("color4"))
+ , m_xBtnRemove4(m_xBuilder->weld_button("remove4"))
+ , m_xContents(m_xBuilder->weld_widget("grid"))
+ , m_xScrollBar(m_xBuilder->weld_scrolled_window("scrollbar", true))
+ , m_xExpander(m_xBuilder->weld_expander("more"))
+ , m_xBtnClear(m_xBuilder->weld_button("clear"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnRegExp(m_xBuilder->weld_check_button("regexp"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("header"))
+ , m_xBtnUnique(m_xBuilder->weld_check_button("unique"))
+ , m_xBtnCopyResult(m_xBuilder->weld_check_button("copyresult"))
+ , m_xLbCopyArea(m_xBuilder->weld_combo_box("lbcopyarea"))
+ , m_xEdCopyArea(new formula::RefEdit(m_xBuilder->weld_entry("edcopyarea")))
+ , m_xRbCopyArea(new formula::RefButton(m_xBuilder->weld_button("rbcopyarea")))
+ , m_xBtnDestPers(m_xBuilder->weld_check_button("destpers"))
+ , m_xFtDbAreaLabel(m_xBuilder->weld_label("dbarealabel"))
+ , m_xFtDbArea(m_xBuilder->weld_label("dbarea"))
+{
+ m_xExpander->connect_expanded(LINK(this, ScFilterDlg, MoreExpandedHdl));
+ m_xEdCopyArea->SetReferences(this, m_xFtDbAreaLabel.get());
+ m_xRbCopyArea->SetReferences(this, m_xEdCopyArea.get());
+
+ assert(m_xLbCond1->find_text(aStrFontColor) != -1);
+ assert(m_xLbCond1->find_text(aStrBackgroundColor) != -1);
+
+ Init( rArgSet );
+
+ // Hack: RefInput control
+ pTimer.reset( new Timer("ScFilterTimer") );
+ pTimer->SetTimeout( 50 ); // Wait 50ms
+ pTimer->SetInvokeHandler( LINK( this, ScFilterDlg, TimeOutHdl ) );
+}
+
+ScFilterDlg::~ScFilterDlg()
+{
+ pOptionsMgr.reset();
+ pOutItem.reset();
+
+ // Hack: RefInput control
+ pTimer->Stop();
+ pTimer.reset();
+}
+
+namespace {
+VirtualDevice* lcl_getColorImage(const Color &rColor)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+
+ VclPtrInstance<VirtualDevice> xDevice;
+ xDevice->SetOutputSize(aImageSize);
+ const tools::Rectangle aRect(Point(0, 0), aImageSize);
+ if (rColor == COL_NONE_COLOR)
+ {
+ const Color aW(COL_WHITE);
+ const Color aG(0xef, 0xef, 0xef);
+ xDevice->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), 8, aW, aG);
+ xDevice->SetFillColor();
+ }
+ else
+ {
+ xDevice->SetFillColor(rColor);
+ }
+
+ xDevice->DrawRect(aRect);
+
+ return xDevice.get();
+}
+}
+
+void ScFilterDlg::Init( const SfxItemSet& rArgSet )
+{
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery ));
+
+ m_xBtnClear->connect_clicked ( LINK( this, ScFilterDlg, BtnClearHdl ) );
+ m_xBtnOk->connect_clicked ( LINK( this, ScFilterDlg, EndDlgHdl ) );
+ m_xBtnCancel->connect_clicked ( LINK( this, ScFilterDlg, EndDlgHdl ) );
+ m_xBtnHeader->connect_toggled ( LINK( this, ScFilterDlg, CheckBoxHdl ) );
+ m_xBtnCase->connect_toggled ( LINK( this, ScFilterDlg, CheckBoxHdl ) );
+
+ m_xLbField1->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbField2->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbField3->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbField4->connect_changed ( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect1->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect2->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect3->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbConnect4->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+
+ m_xLbField1->append_text("0000000000");
+ m_xLbField1->set_active(0);
+ auto nPrefWidth = m_xLbField1->get_preferred_size().Width();
+ m_xLbField1->clear();
+
+ m_xLbField1->set_size_request(nPrefWidth, -1);
+ m_xLbField2->set_size_request(nPrefWidth, -1);
+ m_xLbField3->set_size_request(nPrefWidth, -1);
+ m_xLbField4->set_size_request(nPrefWidth, -1);
+
+ m_xLbCond1->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbCond2->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbCond3->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbCond4->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+
+ m_xLbColor1->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbColor2->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbColor3->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+ m_xLbColor4->connect_changed( LINK( this, ScFilterDlg, LbSelectHdl ) );
+
+ m_xBtnRemove1->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+ m_xBtnRemove2->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+ m_xBtnRemove3->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+ m_xBtnRemove4->connect_clicked( LINK( this, ScFilterDlg, BtnRemoveHdl ) );
+
+ pViewData = rQueryItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+ nSrcTab = pViewData ? pViewData->GetTabNo() : static_cast<SCTAB>(0);
+
+ // for easier access:
+ maFieldLbArr.reserve(QUERY_ENTRY_COUNT);
+ maFieldLbArr.push_back(m_xLbField1.get());
+ maFieldLbArr.push_back(m_xLbField2.get());
+ maFieldLbArr.push_back(m_xLbField3.get());
+ maFieldLbArr.push_back(m_xLbField4.get());
+ maValueEdArr.reserve(QUERY_ENTRY_COUNT);
+ maValueEdArr.push_back(m_xEdVal1.get());
+ maValueEdArr.push_back(m_xEdVal2.get());
+ maValueEdArr.push_back(m_xEdVal3.get());
+ maValueEdArr.push_back(m_xEdVal4.get());
+ maCondLbArr.reserve(QUERY_ENTRY_COUNT);
+ maCondLbArr.push_back(m_xLbCond1.get());
+ maCondLbArr.push_back(m_xLbCond2.get());
+ maCondLbArr.push_back(m_xLbCond3.get());
+ maCondLbArr.push_back(m_xLbCond4.get());
+ maConnLbArr.reserve(QUERY_ENTRY_COUNT);
+ maConnLbArr.push_back(m_xLbConnect1.get());
+ maConnLbArr.push_back(m_xLbConnect2.get());
+ maConnLbArr.push_back(m_xLbConnect3.get());
+ maConnLbArr.push_back(m_xLbConnect4.get());
+ maColorLbArr.reserve(QUERY_ENTRY_COUNT);
+ maColorLbArr.push_back(m_xLbColor1.get());
+ maColorLbArr.push_back(m_xLbColor2.get());
+ maColorLbArr.push_back(m_xLbColor3.get());
+ maColorLbArr.push_back(m_xLbColor4.get());
+ maRemoveBtnArr.reserve(QUERY_ENTRY_COUNT);
+ maRemoveBtnArr.push_back(m_xBtnRemove1.get());
+ maRemoveBtnArr.push_back(m_xBtnRemove2.get());
+ maRemoveBtnArr.push_back(m_xBtnRemove3.get());
+ maRemoveBtnArr.push_back(m_xBtnRemove4.get());
+
+ // Option initialization:
+ pOptionsMgr.reset( new ScFilterOptionsMgr(
+ pViewData,
+ theQueryData,
+ m_xBtnCase.get(),
+ m_xBtnRegExp.get(),
+ m_xBtnHeader.get(),
+ m_xBtnUnique.get(),
+ m_xBtnCopyResult.get(),
+ m_xBtnDestPers.get(),
+ m_xLbCopyArea.get(),
+ m_xEdCopyArea.get(),
+ m_xRbCopyArea.get(),
+ m_xFtDbAreaLabel.get(),
+ m_xFtDbArea.get(),
+ aStrUndefined ) );
+ // Read in field lists and select entries
+
+ FillFieldLists();
+
+ for (size_t i = 0; i < QUERY_ENTRY_COUNT; ++i)
+ {
+ OUString aValStr;
+ size_t nCondPos = 0;
+ size_t nFieldSelPos = 0;
+
+ maColorLbArr[i]->set_visible(false);
+
+ ScQueryEntry& rEntry = theQueryData.GetEntry(i);
+ if ( rEntry.bDoQuery )
+ {
+ nCondPos = static_cast<size_t>(rEntry.eOp);
+ nFieldSelPos = GetFieldSelPos( static_cast<SCCOL>(rEntry.nField) );
+ if (rEntry.IsQueryByEmpty())
+ {
+ aValStr = aStrEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aValStr = aStrNotEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ nCondPos = maCondLbArr[i]->find_text(
+ rEntry.IsQueryByTextColor() ? aStrFontColor : aStrBackgroundColor);
+ maValueEdArr[i]->set_visible(false);
+ maColorLbArr[i]->set_visible(true);
+ maColorLbArr[i]->set_sensitive(true);
+ }
+ else
+ {
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ OUString aQueryStr = rItem.maString.getString();
+ SetValString(aQueryStr, rItem, aValStr);
+ }
+ }
+ else if ( i == 0 )
+ {
+ nFieldSelPos = pViewData ? GetFieldSelPos(pViewData->GetCurX()) : 0;
+ rEntry.nField = nFieldSelPos ? (theQueryData.nCol1 +
+ static_cast<SCCOL>(nFieldSelPos) - 1) : static_cast<SCCOL>(0);
+ rEntry.bDoQuery=true;
+ if (maRefreshExceptQuery.size() < i + 1)
+ maRefreshExceptQuery.resize(i + 1, false);
+ maRefreshExceptQuery[i] = true;
+
+ }
+ maFieldLbArr[i]->set_active( nFieldSelPos );
+ maCondLbArr [i]->set_active( nCondPos );
+ maValueEdArr[i]->set_entry_text( aValStr );
+ maValueEdArr[i]->set_entry_completion(false);
+ maValueEdArr[i]->connect_changed( LINK( this, ScFilterDlg, ValModifyHdl ) );
+ UpdateValueList(i+1);
+ UpdateColorList(i+1);
+ }
+
+ m_xScrollBar->connect_vadjustment_changed( LINK( this, ScFilterDlg, ScrollHdl ) );
+ m_xScrollBar->vadjustment_configure(0, 0, 8, 1, 3, 4);
+ Size aSize(m_xContents->get_preferred_size());
+ m_xContents->set_size_request(aSize.Width(), aSize.Height());
+
+ m_xLbConnect1->hide();
+ // Disable/Enable Logic:
+
+ (m_xLbField1->get_active() != 0)
+ && (m_xLbField2->get_active() != 0)
+ ? m_xLbConnect2->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(1).eConnect) )
+ : m_xLbConnect2->set_active(-1);
+
+ (m_xLbField2->get_active() != 0)
+ && (m_xLbField3->get_active() != 0)
+ ? m_xLbConnect3->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(2).eConnect) )
+ : m_xLbConnect3->set_active(-1);
+
+ (m_xLbField3->get_active() != 0)
+ && (m_xLbField4->get_active() != 0)
+ ? m_xLbConnect4->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(3).eConnect) )
+ : m_xLbConnect4->set_active(-1);
+ if ( m_xLbField1->get_active() == 0 )
+ {
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ }
+ else if ( m_xLbConnect2->get_active() == -1 )
+ {
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ }
+
+ if ( m_xLbField2->get_active() == 0 )
+ {
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ }
+ else if ( m_xLbConnect3->get_active() == -1 )
+ {
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ }
+ if ( m_xLbField3->get_active() == 0 )
+ {
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+ }
+ else if ( m_xLbConnect4->get_active() == -1 )
+ {
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+ }
+
+ m_xEdVal1->set_entry_width_chars(10);
+ m_xEdVal2->set_entry_width_chars(10);
+ m_xEdVal3->set_entry_width_chars(10);
+ m_xEdVal4->set_entry_width_chars(10);
+
+ if (pDoc != nullptr && pDoc->GetChangeTrack() != nullptr)
+ m_xBtnCopyResult->set_sensitive(false);
+}
+
+void ScFilterDlg::Close()
+{
+ if (pViewData)
+ pViewData->GetDocShell()->CancelAutoDBRange();
+
+ DoClose( ScFilterDlgWrapper::GetChildWindowId() );
+}
+
+// Mouse-selected cell area becomes the new selection and is shown in the
+// reference text box
+
+void ScFilterDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if ( bRefInputMode ) // Only possible if in reference edit mode
+ {
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_xEdCopyArea.get() );
+ OUString aRefStr(rRef.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDocP, rDocP.GetAddressConvention()));
+ m_xEdCopyArea->SetRefString( aRefStr );
+ }
+}
+
+void ScFilterDlg::SetActive()
+{
+ if ( bRefInputMode )
+ {
+ m_xEdCopyArea->GrabFocus();
+ m_xEdCopyArea->GetModifyHdl().Call( *m_xEdCopyArea );
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+void ScFilterDlg::FillFieldLists()
+{
+ m_xLbField1->freeze();
+ m_xLbField2->freeze();
+ m_xLbField3->freeze();
+ m_xLbField4->freeze();
+
+ m_xLbField1->clear();
+ m_xLbField2->clear();
+ m_xLbField3->clear();
+ m_xLbField4->clear();
+ m_xLbField1->append_text( aStrNone );
+ m_xLbField2->append_text( aStrNone );
+ m_xLbField3->append_text( aStrNone );
+ m_xLbField4->append_text( aStrNone );
+
+ if ( pDoc )
+ {
+ OUString aFieldName;
+ SCTAB nTab = nSrcTab;
+ SCCOL nFirstCol = theQueryData.nCol1;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCCOL nMaxCol = theQueryData.nCol2;
+ SCCOL col = 0;
+
+ for ( col=nFirstCol; col<=nMaxCol; col++ )
+ {
+ aFieldName = pDoc->GetString(col, nFirstRow, nTab);
+ if (!m_xBtnHeader->get_active() || aFieldName.isEmpty())
+ {
+ aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, u"%1", ScColToAlpha( col ));
+ }
+ m_xLbField1->append_text( aFieldName );
+ m_xLbField2->append_text( aFieldName );
+ m_xLbField3->append_text( aFieldName );
+ m_xLbField4->append_text( aFieldName );
+ }
+ }
+
+ m_xLbField4->thaw();
+ m_xLbField3->thaw();
+ m_xLbField2->thaw();
+ m_xLbField1->thaw();
+}
+
+void ScFilterDlg::UpdateValueList( size_t nList )
+{
+ bool bCaseSens = m_xBtnCase->get_active();
+
+ if (pDoc && nList > 0 && nList <= QUERY_ENTRY_COUNT)
+ {
+ weld::ComboBox* pValList = maValueEdArr[nList-1];
+ const sal_Int32 nFieldSelPos = maFieldLbArr[nList-1]->get_active();
+ OUString aCurValue = pValList->get_active_text();
+
+ std::unique_ptr<weld::WaitObject> xWaiter;
+ std::vector<weld::ComboBoxEntry> aEntries;
+ aEntries.emplace_back(aStrNotEmpty);
+ aEntries.emplace_back(aStrEmpty);
+
+ if (nFieldSelPos)
+ {
+ xWaiter.reset(new weld::WaitObject(m_xDialog.get())); // even if only the list box has content
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ EntryList* pList = nullptr;
+ if (!m_EntryLists.count(nColumn))
+ {
+ size_t nOffset = GetSliderPos();
+ SCTAB nTab = nSrcTab;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCROW nLastRow = theQueryData.nRow2;
+ if (maHasDates.size() < nOffset+nList)
+ maHasDates.resize(nOffset+nList, false);
+ maHasDates[nOffset+nList-1] = false;
+
+ // first without the first line
+ std::pair<EntryListsMap::iterator, bool> r =
+ m_EntryLists.insert(std::make_pair(nColumn, std::make_unique<EntryList>()));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ pList = r.first->second.get();
+ pDoc->GetFilterEntriesArea(
+ nColumn, nFirstRow+1, nLastRow,
+ nTab, bCaseSens, pList->maFilterEntries);
+ maHasDates[nOffset+nList-1] = pList->maFilterEntries.mbHasDates;
+
+ // Entry for the first line
+ //! Entry (pHdrEntry) doesn't generate collection?
+
+ pList->mnHeaderPos = INVALID_HEADER_POS;
+ ScFilterEntries aHdrColl;
+ pDoc->GetFilterEntriesArea(
+ nColumn, nFirstRow, nFirstRow, nTab, true, aHdrColl );
+ if (!aHdrColl.empty())
+ {
+ // See if the header value is already in the list.
+ std::vector<ScTypedStrData>::iterator itBeg = pList->maFilterEntries.begin(), itEnd = pList->maFilterEntries.end();
+ if (std::none_of(itBeg, itEnd, FindTypedStrData(aHdrColl.front(), bCaseSens)))
+ {
+ // Not in the list. Insert it.
+ pList->maFilterEntries.push_back(aHdrColl.front());
+ if (bCaseSens)
+ std::sort(pList->maFilterEntries.begin(), pList->maFilterEntries.end(), ScTypedStrData::LessCaseSensitive());
+ else
+ std::sort(pList->maFilterEntries.begin(), pList->maFilterEntries.end(), ScTypedStrData::LessCaseInsensitive());
+
+ // Record its position.
+ itBeg = pList->maFilterEntries.begin();
+ itEnd = pList->maFilterEntries.end();
+ auto it = std::find_if(itBeg, itEnd, FindTypedStrData(aHdrColl.front(), bCaseSens));
+ pList->mnHeaderPos = std::distance(itBeg, it);
+ }
+ }
+ }
+ else
+ pList = m_EntryLists[nColumn].get();
+
+ assert(pList);
+
+ for (const auto& rEntry : pList->maFilterEntries)
+ aEntries.emplace_back(rEntry.GetString());
+ }
+ pValList->insert_vector(aEntries, false);
+ pValList->set_entry_text(aCurValue);
+ }
+
+ UpdateHdrInValueList( nList );
+}
+
+void ScFilterDlg::UpdateHdrInValueList( size_t nList )
+{
+ //! GetText / SetText ??
+
+ if (!pDoc)
+ return;
+
+ if (nList == 0 || nList > QUERY_ENTRY_COUNT)
+ return;
+
+ size_t nFieldSelPos = maFieldLbArr[nList-1]->get_active();
+ if (!nFieldSelPos)
+ return;
+
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ if (!m_EntryLists.count(nColumn))
+ {
+ OSL_FAIL("column not yet initialized");
+ return;
+ }
+
+ size_t const nPos = m_EntryLists[nColumn]->mnHeaderPos;
+ if (nPos == INVALID_HEADER_POS)
+ return;
+
+ weld::ComboBox* pValList = maValueEdArr[nList-1];
+ int nListPos = nPos + 2; // for "empty" and "non-empty"
+
+ const ScTypedStrData& rHdrEntry = m_EntryLists[nColumn]->maFilterEntries.maStrData[nPos];
+
+ const OUString& aHdrStr = rHdrEntry.GetString();
+ bool bWasThere = nListPos < pValList->get_count() && aHdrStr == pValList->get_text(nListPos);
+ bool bInclude = !m_xBtnHeader->get_active();
+
+ if (bInclude) // Include entry
+ {
+ if (!bWasThere)
+ pValList->insert_text(nListPos, aHdrStr);
+ }
+ else // Omit entry
+ {
+ if (bWasThere)
+ pValList->remove(nListPos);
+ }
+}
+
+void ScFilterDlg::ClearValueList( size_t nList )
+{
+ if (nList > 0 && nList <= QUERY_ENTRY_COUNT)
+ {
+ weld::ComboBox* pValList = maValueEdArr[nList-1];
+ pValList->clear();
+ pValList->append_text( aStrNotEmpty );
+ pValList->append_text( aStrEmpty );
+ pValList->set_entry_text( OUString() );
+ }
+}
+
+void ScFilterDlg::UpdateColorList(size_t nList)
+{
+ if (!pDoc || nList <= 0 || nList > QUERY_ENTRY_COUNT)
+ return;
+
+ size_t nPos = nList - 1;
+ ScQueryEntry& rEntry = theQueryData.GetEntry(nPos);
+ const sal_Int32 nFieldSelPos = maFieldLbArr[nPos]->get_active();
+ if (!nFieldSelPos)
+ return;
+
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ EntryList* pList = m_EntryLists[nColumn].get();
+ if (!pList)
+ return;
+
+ std::set<Color> aColors;
+ OUString sSelectedCondition = maCondLbArr[nPos]->get_active_text();
+ if (sSelectedCondition == aStrFontColor)
+ aColors = pList->maFilterEntries.getTextColors();
+ else if (sSelectedCondition == aStrBackgroundColor)
+ aColors = pList->maFilterEntries.getBackgroundColors();
+ else
+ return;
+
+ maColorLbArr[nPos]->clear();
+ for (const auto& rColor : aColors)
+ {
+ OUString sId = rColor.AsRGBHexString();
+ if (rColor == COL_AUTO)
+ {
+ OUString sText = sSelectedCondition == aStrFontColor
+ ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
+ : ScResId(SCSTR_FILTER_NO_FILL);
+ maColorLbArr[nPos]->append(sId, sText);
+ }
+ else
+ {
+ VirtualDevice* pDev = lcl_getColorImage(rColor);
+ maColorLbArr[nPos]->append(sId, OUString(), *pDev);
+ }
+
+ auto aItem = rEntry.GetQueryItem();
+ if (aItem.maColor == rColor
+ && ((sSelectedCondition == aStrFontColor && aItem.meType == ScQueryEntry::ByTextColor)
+ || (sSelectedCondition == aStrBackgroundColor
+ && aItem.meType == ScQueryEntry::ByBackgroundColor)))
+ {
+ maColorLbArr[nPos]->set_active_id(sId);
+ }
+ }
+}
+
+size_t ScFilterDlg::GetFieldSelPos( SCCOL nField )
+{
+ if ( nField >= theQueryData.nCol1 && nField <= theQueryData.nCol2 )
+ return static_cast<size_t>(nField - theQueryData.nCol1 + 1);
+ else
+ return 0;
+}
+
+ScQueryItem* ScFilterDlg::GetOutputItem()
+{
+ ScAddress theCopyPos;
+ ScQueryParam theParam( theQueryData );
+ bool bCopyPosOk = false;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ ScRefFlags nResult = theCopyPos.Parse(
+ m_xEdCopyArea->GetText(), *pDoc, pDoc->GetAddressConvention());
+ bCopyPosOk = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;
+ }
+
+ if ( m_xBtnCopyResult->get_active() && bCopyPosOk )
+ {
+ theParam.bInplace = false;
+ theParam.nDestTab = theCopyPos.Tab();
+ theParam.nDestCol = theCopyPos.Col();
+ theParam.nDestRow = theCopyPos.Row();
+ }
+ else
+ {
+ theParam.bInplace = true;
+ theParam.nDestTab = 0;
+ theParam.nDestCol = 0;
+ theParam.nDestRow = 0;
+ }
+
+ theParam.bHasHeader = m_xBtnHeader->get_active();
+ theParam.bByRow = true;
+ theParam.bDuplicate = !m_xBtnUnique->get_active();
+ theParam.bCaseSens = m_xBtnCase->get_active();
+ theParam.eSearchType = m_xBtnRegExp->get_active() ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal;
+ theParam.bDestPers = m_xBtnDestPers->get_active();
+
+ // only set the three - reset everything else
+
+ pOutItem.reset( new ScQueryItem( nWhichQuery, &theParam ) );
+
+ return pOutItem.get();
+}
+
+bool ScFilterDlg::IsRefInputMode() const
+{
+ return bRefInputMode;
+}
+
+// Handler:
+
+IMPL_LINK( ScFilterDlg, BtnClearHdl, weld::Button&, rBtn, void )
+{
+ if ( &rBtn != m_xBtnClear.get() )
+ return;
+
+ // scroll to the top
+ m_xScrollBar->vadjustment_set_value(0);
+ size_t nOffset = 0;
+ RefreshEditRow( nOffset);
+
+ // clear all conditions
+ m_xLbConnect1->set_active(-1);
+ m_xLbConnect2->set_active(-1);
+ m_xLbConnect3->set_active(-1);
+ m_xLbConnect4->set_active(-1);
+ m_xLbField1->set_active(0);
+ m_xLbField2->set_active(0);
+ m_xLbField3->set_active(0);
+ m_xLbField4->set_active(0);
+ m_xLbCond1->set_active(0);
+ m_xLbCond2->set_active(0);
+ m_xLbCond3->set_active(0);
+ m_xLbCond4->set_active(0);
+ ClearValueList( 1 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ // disable fields for second row onward
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+
+ // clear query data objects
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount + 1)
+ maRefreshExceptQuery.resize(nCount + 1, false);
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[0] = true;
+}
+
+IMPL_LINK( ScFilterDlg, EndDlgHdl, weld::Button&, rBtn, void )
+{
+ if ( &rBtn == m_xBtnOk.get() )
+ {
+ bool bAreaInputOk = true;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ if ( !pOptionsMgr->VerifyPosStr( m_xEdCopyArea->GetText() ) )
+ {
+ if (!m_xExpander->get_expanded())
+ m_xExpander->set_expanded(true);
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_TABREF)));
+ xBox->run();
+ m_xEdCopyArea->GrabFocus();
+ bAreaInputOk = false;
+ }
+ }
+
+ if ( bAreaInputOk )
+ {
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(FID_FILTER_OK,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { GetOutputItem() });
+ response(RET_OK);
+ }
+ }
+ else if ( &rBtn == m_xBtnCancel.get() )
+ {
+ response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK_NOARG(ScFilterDlg, MoreExpandedHdl, weld::Expander&, void)
+{
+ if ( m_xExpander->get_expanded() )
+ pTimer->Start();
+ else
+ {
+ pTimer->Stop();
+ bRefInputMode = false;
+ //@BugID 54702 Enable/disable only in Basic class
+ //SFX_APPWINDOW->Disable(FALSE); //! general method in ScAnyRefDlg
+ }
+}
+
+IMPL_LINK( ScFilterDlg, TimeOutHdl, Timer*, _pTimer, void )
+{
+ // Check if RefInputMode is still true every 50ms
+ if (_pTimer == pTimer.get() && m_xDialog->has_toplevel_focus())
+ bRefInputMode = (m_xEdCopyArea->GetWidget()->has_focus() || m_xRbCopyArea->GetWidget()->has_focus());
+
+ if ( m_xExpander->get_expanded() )
+ pTimer->Start();
+}
+
+IMPL_LINK(ScFilterDlg, LbSelectHdl, weld::ComboBox&, rLb, void)
+{
+ /*
+ * Handle enable/disable logic depending on which ListBox was selected
+ */
+ sal_uInt16 nOffset = GetSliderPos();
+
+ if ( &rLb == m_xLbConnect1.get() )
+ {
+ m_xLbField1->set_sensitive(true);
+ m_xLbCond1->set_sensitive(true);
+ m_xEdVal1->set_sensitive(true);
+ m_xBtnRemove1->set_sensitive(true);
+
+ const sal_Int32 nConnect1 = m_xLbConnect1->get_active();
+ size_t nQE = nOffset;
+ theQueryData.GetEntry(nQE).eConnect =static_cast<ScQueryConnect>(nConnect1);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE] = true;
+ }
+ else if ( &rLb == m_xLbConnect2.get() )
+ {
+ m_xLbField2->set_sensitive(true);
+ m_xLbCond2->set_sensitive(true);
+ m_xEdVal2->set_sensitive(true);
+ m_xBtnRemove2->set_sensitive(true);
+
+ const sal_Int32 nConnect2 = m_xLbConnect2->get_active();
+ size_t nQE = 1+nOffset;
+ theQueryData.GetEntry(nQE).eConnect =static_cast<ScQueryConnect>(nConnect2);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE]=true;
+ }
+ else if ( &rLb == m_xLbConnect3.get() )
+ {
+ m_xLbField3->set_sensitive(true);
+ m_xLbCond3->set_sensitive(true);
+ m_xEdVal3->set_sensitive(true);
+ m_xBtnRemove3->set_sensitive(true);
+
+ const sal_Int32 nConnect3 = m_xLbConnect3->get_active();
+ size_t nQE = 2 + nOffset;
+ theQueryData.GetEntry(nQE).eConnect = static_cast<ScQueryConnect>(nConnect3);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE] = true;
+
+ }
+ else if ( &rLb == m_xLbConnect4.get() )
+ {
+ m_xLbField4->set_sensitive(true);
+ m_xLbCond4->set_sensitive(true);
+ m_xEdVal4->set_sensitive(true);
+ m_xLbColor4->set_sensitive(true);
+ m_xBtnRemove4->set_sensitive(true);
+
+ const sal_Int32 nConnect4 = m_xLbConnect4->get_active();
+ size_t nQE = 3 + nOffset;
+ theQueryData.GetEntry(nQE).eConnect = static_cast<ScQueryConnect>(nConnect4);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ maRefreshExceptQuery[nQE] = true;
+ }
+ else if ( &rLb == m_xLbField1.get() )
+ {
+ if ( m_xLbField1->get_active() == 0 )
+ {
+ m_xLbConnect2->set_active(-1);
+ m_xLbConnect3->set_active(-1);
+ m_xLbConnect4->set_active(-1);
+ m_xLbField2->set_active( 0 );
+ m_xLbField3->set_active( 0 );
+ m_xLbField4->set_active( 0 );
+ m_xLbCond2->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ m_xLbCond4->set_active( 0 );
+ ClearValueList( 1 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor2->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove2->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount + 1)
+ maRefreshExceptQuery.resize(nCount + 1, false);
+ for (SCSIZE i = nOffset; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nOffset] = true;
+ }
+ else
+ {
+ UpdateValueList( 1 );
+ UpdateColorList( 1 );
+ if ( !m_xLbConnect2->get_sensitive() )
+ {
+ m_xLbConnect2->set_sensitive(true);
+ }
+ theQueryData.GetEntry(nOffset).bDoQuery = true;
+ const sal_Int32 nField = rLb.get_active();
+ theQueryData.GetEntry(nOffset).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+ }
+ }
+ else if ( &rLb == m_xLbField2.get() )
+ {
+ if ( m_xLbField2->get_active() == 0 )
+ {
+ m_xLbConnect3->set_active(-1);
+ m_xLbConnect4->set_active(-1);
+ m_xLbField3->set_active( 0 );
+ m_xLbField4->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ m_xLbCond4->set_active( 0 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ m_xLbConnect3->set_sensitive(false);
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor3->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove3->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+
+ sal_uInt16 nTemp=nOffset+1;
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount)
+ maRefreshExceptQuery.resize(nCount, false);
+ for (SCSIZE i= nTemp; i< nCount; i++)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nTemp] = true;
+ }
+ else
+ {
+ UpdateValueList( 2 );
+ UpdateColorList( 2 );
+ if ( !m_xLbConnect3->get_sensitive() )
+ {
+ m_xLbConnect3->set_sensitive(true);
+ }
+ const sal_Int32 nField = rLb.get_active();
+ sal_uInt16 nQ=1+nOffset;
+ theQueryData.GetEntry(nQ).bDoQuery = true;
+ theQueryData.GetEntry(nQ).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+ }
+ }
+ else if ( &rLb == m_xLbField3.get() )
+ {
+ if ( m_xLbField3->get_active() == 0 )
+ {
+ m_xLbConnect4->set_active(-1);
+ m_xLbField4->set_active( 0 );
+ m_xLbCond4->set_active( 0 );
+ ClearValueList( 3 );
+ ClearValueList( 4 );
+
+ m_xLbConnect4->set_sensitive(false);
+ m_xLbField4->set_sensitive(false);
+ m_xLbCond4->set_sensitive(false);
+ m_xEdVal4->set_sensitive(false);
+ m_xLbColor4->set_sensitive(false);
+ m_xBtnRemove4->set_sensitive(false);
+
+ sal_uInt16 nTemp=nOffset+2;
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount)
+ maRefreshExceptQuery.resize(nCount, false);
+ for (SCSIZE i = nTemp; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nTemp] = true;
+ }
+ else
+ {
+ UpdateValueList( 3 );
+ UpdateColorList( 3 );
+ if ( !m_xLbConnect4->get_sensitive() )
+ {
+ m_xLbConnect4->set_sensitive(true);
+ }
+
+ const sal_Int32 nField = rLb.get_active();
+ sal_uInt16 nQ=2+nOffset;
+ theQueryData.GetEntry(nQ).bDoQuery = true;
+ theQueryData.GetEntry(nQ).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+
+ }
+ }
+ else if ( &rLb == m_xLbField4.get() )
+ {
+ if ( m_xLbField4->get_active() == 0 )
+ {
+ ClearValueList( 4 );
+ sal_uInt16 nTemp=nOffset+3;
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (maRefreshExceptQuery.size() < nCount)
+ maRefreshExceptQuery.resize(nCount, false);
+ for (SCSIZE i = nTemp; i < nCount; ++i)
+ {
+ theQueryData.GetEntry(i).bDoQuery = false;
+ maRefreshExceptQuery[i] = false;
+ theQueryData.GetEntry(i).nField = static_cast<SCCOL>(0);
+ }
+ maRefreshExceptQuery[nTemp] = true;
+ }
+ else
+ {
+ UpdateValueList( 4 );
+ UpdateColorList( 4 );
+ const sal_Int32 nField = rLb.get_active();
+ sal_uInt16 nQ=3+nOffset;
+ theQueryData.GetEntry(nQ).bDoQuery = true;
+ theQueryData.GetEntry(nQ).nField = theQueryData.nCol1 + static_cast<SCCOL>(nField) - 1 ;
+ }
+
+ }
+ else if (&rLb == m_xLbCond1.get() || &rLb == m_xLbCond2.get() || &rLb == m_xLbCond3.get()
+ || &rLb == m_xLbCond4.get())
+ {
+ ScQueryOp op;
+ sal_uInt16 nQ = 0;
+ bool bEnableColorLb = false;
+ if (rLb.get_active_text() == aStrFontColor || rLb.get_active_text() == aStrBackgroundColor)
+ {
+ bEnableColorLb = true;
+ op = SC_EQUAL;
+ }
+ else
+ {
+ op = static_cast<ScQueryOp>(rLb.get_active());
+ }
+
+ if (&rLb == m_xLbCond1.get())
+ {
+ nQ = nOffset;
+ m_xLbColor1->set_visible(bEnableColorLb);
+ m_xLbColor1->set_sensitive(bEnableColorLb);
+ m_xEdVal1->set_visible(!bEnableColorLb);
+ UpdateColorList(1);
+ }
+ else if (&rLb == m_xLbCond2.get())
+ {
+ nQ = 1 + nOffset;
+ m_xLbColor2->set_visible(bEnableColorLb);
+ m_xLbColor2->set_sensitive(bEnableColorLb);
+ m_xEdVal2->set_visible(!bEnableColorLb);
+ UpdateColorList(2);
+ }
+ else if (&rLb == m_xLbCond3.get())
+ {
+ nQ = 2 + nOffset;
+ m_xLbColor3->set_visible(bEnableColorLb);
+ m_xLbColor3->set_sensitive(bEnableColorLb);
+ m_xEdVal3->set_visible(!bEnableColorLb);
+ UpdateColorList(3);
+ }
+ else if (&rLb == m_xLbCond4.get())
+ {
+ nQ = 3 + nOffset;
+ m_xLbColor4->set_visible(bEnableColorLb);
+ m_xLbColor4->set_sensitive(bEnableColorLb);
+ m_xEdVal4->set_visible(!bEnableColorLb);
+ UpdateColorList(4);
+ }
+
+ auto aEntry = theQueryData.GetEntry(nQ);
+ aEntry.eOp = op;
+ }
+ else if (&rLb == m_xLbColor1.get() || &rLb == m_xLbColor2.get() || &rLb == m_xLbColor3.get()
+ || &rLb == m_xLbColor4.get())
+ {
+ sal_uInt16 nQ = 0;
+ if (&rLb == m_xLbColor1.get())
+ {
+ nQ = nOffset;
+ }
+ else if (&rLb == m_xLbColor2.get())
+ {
+ nQ = 1 + nOffset;
+ }
+ else if (&rLb == m_xLbColor3.get())
+ {
+ nQ = 2 + nOffset;
+ }
+ else if (&rLb == m_xLbColor4.get())
+ {
+ nQ = 3 + nOffset;
+ }
+
+ ScQueryEntry& aEntry = theQueryData.GetEntry(nQ);
+ Color aColor = Color::STRtoRGB(maColorLbArr[nQ]->get_active_id());
+ if (maCondLbArr[nQ]->get_active_text() == aStrFontColor)
+ {
+ aEntry.SetQueryByTextColor(aColor);
+ }
+ else if (maCondLbArr[nQ]->get_active_text() == aStrBackgroundColor)
+ {
+ aEntry.SetQueryByBackgroundColor(aColor);
+ }
+ }
+}
+
+IMPL_LINK( ScFilterDlg, CheckBoxHdl, weld::Toggleable&, rBox, void )
+{
+ // Column headers:
+ // Field list: Columnxx <-> column header string
+ // Value list: Column header value not applicable.
+ // Upper/lower case:
+ // Value list: completely new
+
+ if ( &rBox == m_xBtnHeader.get() ) // Field list and value list
+ {
+ const sal_Int32 nCurSel1 = m_xLbField1->get_active();
+ const sal_Int32 nCurSel2 = m_xLbField2->get_active();
+ const sal_Int32 nCurSel3 = m_xLbField3->get_active();
+ const sal_Int32 nCurSel4 = m_xLbField4->get_active();
+ FillFieldLists();
+ m_xLbField1->set_active( nCurSel1 );
+ m_xLbField2->set_active( nCurSel2 );
+ m_xLbField3->set_active( nCurSel3 );
+ m_xLbField4->set_active( nCurSel4 );
+
+ UpdateHdrInValueList( 1 );
+ UpdateHdrInValueList( 2 );
+ UpdateHdrInValueList( 3 );
+ UpdateHdrInValueList( 4 );
+ }
+
+ if ( &rBox != m_xBtnCase.get() ) // Complete value list
+ return;
+
+ m_EntryLists.clear();
+ UpdateValueList( 1 ); // current text is recorded
+ UpdateValueList( 2 );
+ UpdateValueList( 3 );
+ UpdateValueList( 4 );
+
+ UpdateColorList( 1 );
+ UpdateColorList( 2 );
+ UpdateColorList( 3 );
+ UpdateColorList( 4 );
+}
+
+IMPL_LINK( ScFilterDlg, ValModifyHdl, weld::ComboBox&, rEd, void )
+{
+ size_t nOffset = GetSliderPos();
+ size_t i = 0;
+ size_t nQE = i + nOffset;
+ OUString aStrVal = rEd.get_active_text();
+ weld::ComboBox* pLbCond = m_xLbCond1.get();
+ weld::ComboBox* pLbField = m_xLbField1.get();
+ if ( &rEd == m_xEdVal2.get() )
+ {
+ pLbCond = m_xLbCond2.get();
+ pLbField = m_xLbField2.get();
+ i=1;
+ nQE=i+nOffset;
+ }
+ if ( &rEd == m_xEdVal3.get() )
+ {
+ pLbCond = m_xLbCond3.get();
+ pLbField = m_xLbField3.get();
+ i=2;
+ nQE=i+nOffset;
+ }
+ if ( &rEd == m_xEdVal4.get() )
+ {
+ pLbCond = m_xLbCond4.get();
+ pLbField = m_xLbField4.get();
+ i=3;
+ nQE=i+nOffset;
+ }
+
+ if ( aStrEmpty == aStrVal || aStrNotEmpty == aStrVal )
+ {
+ pLbCond->set_active_text(OUString('='));
+ pLbCond->set_sensitive(false);
+ }
+ else
+ pLbCond->set_sensitive(true);
+
+ if (maHasDates.size() < nQE + 1)
+ maHasDates.resize(nQE + 1, false);
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+
+ ScQueryEntry& rEntry = theQueryData.GetEntry( nQE );
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bDoThis = (pLbField->get_active() != 0);
+ rEntry.bDoQuery = bDoThis;
+
+ if ( !(rEntry.bDoQuery || maRefreshExceptQuery[nQE]) )
+ return;
+
+ bool bByEmptyOrNotByEmpty = false;
+ if ( aStrEmpty == aStrVal )
+ {
+ bByEmptyOrNotByEmpty = true;
+ rEntry.SetQueryByEmpty();
+ }
+ else if ( aStrNotEmpty == aStrVal )
+ {
+ bByEmptyOrNotByEmpty = true;
+ rEntry.SetQueryByNonEmpty();
+ }
+ else
+ {
+ rItem.maString = pDoc->GetSharedStringPool().intern(aStrVal);
+ rItem.mfVal = 0.0;
+ rItem.meType = ScQueryEntry::ByString;
+ }
+
+ const sal_Int32 nField = pLbField->get_active();
+ rEntry.nField = nField ? (theQueryData.nCol1 +
+ static_cast<SCCOL>(nField) - 1) : static_cast<SCCOL>(0);
+
+ ScQueryOp eOp = static_cast<ScQueryOp>(pLbCond->get_active());
+ rEntry.eOp = eOp;
+ if (maHasDates[nQE] && !bByEmptyOrNotByEmpty)
+ rItem.meType = ScQueryEntry::ByDate;
+}
+
+IMPL_LINK( ScFilterDlg, BtnRemoveHdl, weld::Button&, rBtn, void )
+{
+ // Calculate the row to delete
+ sal_uInt16 nOffset = GetSliderPos();
+ int nButtonIndex = 0;
+ if ( &rBtn == m_xBtnRemove2.get() )
+ nButtonIndex = 1;
+ if ( &rBtn == m_xBtnRemove3.get() )
+ nButtonIndex = 2;
+ if ( &rBtn == m_xBtnRemove4.get() )
+ nButtonIndex = 3;
+ SCSIZE nRowToDelete = nOffset + nButtonIndex;
+
+ // Check that the index is sensible
+ SCSIZE nCount = theQueryData.GetEntryCount();
+ if (nRowToDelete >= nCount)
+ {
+ SAL_WARN( "sc", "ScFilterDlg::BtnRemoveHdl: could not delete row - invalid index.");
+ return;
+ }
+
+ // Resize maRefreshExceptQuery
+ if (maRefreshExceptQuery.size() < nCount + 1)
+ maRefreshExceptQuery.resize(nCount + 1, false);
+
+ // Move all the subsequent rows back one position;
+ // also find the last row, which we will delete
+ SCSIZE nRowToClear = nCount-1;
+ for (SCSIZE i = nRowToDelete; i < nCount-1; ++i)
+ {
+ if (theQueryData.GetEntry(i+1).bDoQuery)
+ {
+ theQueryData.GetEntry(i) = theQueryData.GetEntry(i+1);
+ }
+ else
+ {
+ nRowToClear = i;
+ break;
+ }
+ }
+
+ // If the next row is being edited, but not confirmed, move it back
+ // one position
+ if (nRowToClear < nCount-1 && maRefreshExceptQuery[nRowToClear+1])
+ {
+ theQueryData.GetEntry(nRowToClear) = theQueryData.GetEntry(nRowToClear+1);
+ maRefreshExceptQuery[nRowToClear] = true;
+ maRefreshExceptQuery[nRowToClear+1] = false;
+ }
+ else
+ {
+ // Remove the very last one, since everything has moved back
+ theQueryData.GetEntry(nRowToClear).bDoQuery = false;
+ theQueryData.GetEntry(nRowToClear).nField = static_cast<SCCOL>(0);
+ maRefreshExceptQuery[nRowToClear] = false;
+ }
+
+ // Always enable the very first row
+ if (!theQueryData.GetEntry(0).bDoQuery)
+ {
+ maRefreshExceptQuery[0] = true;
+ }
+
+ // Refresh the UI
+ RefreshEditRow( nOffset );
+
+ // Special handling if the very first row was cleared
+ if (!theQueryData.GetEntry(0).bDoQuery)
+ {
+ m_xLbConnect1->set_active(-1);
+ m_xLbField1->set_active(0);
+ m_xLbField1->set_sensitive(true);
+ m_xLbCond1->set_active(0);
+ m_xLbCond1->set_sensitive(true);
+ ClearValueList(1);
+ }
+}
+
+IMPL_LINK_NOARG(ScFilterDlg, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ SliderMoved();
+}
+
+void ScFilterDlg::SliderMoved()
+{
+ size_t nOffset = GetSliderPos();
+ RefreshEditRow( nOffset);
+}
+
+size_t ScFilterDlg::GetSliderPos() const
+{
+ return static_cast<size_t>(m_xScrollBar->vadjustment_get_value());
+}
+
+void ScFilterDlg::RefreshEditRow( size_t nOffset )
+{
+ if (nOffset==0)
+ maConnLbArr[0]->hide();
+ else
+ maConnLbArr[0]->show();
+
+ for (size_t i = 0; i < QUERY_ENTRY_COUNT; ++i)
+ {
+ OUString aValStr;
+ size_t nCondPos = 0;
+ size_t nFieldSelPos = 0;
+ size_t nQE = i + nOffset;
+
+ maColorLbArr[i]->set_visible(false);
+
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+
+ ScQueryEntry& rEntry = theQueryData.GetEntry( nQE);
+ if ( rEntry.bDoQuery || maRefreshExceptQuery[nQE] )
+ {
+ nCondPos = static_cast<size_t>(rEntry.eOp);
+ if(rEntry.bDoQuery)
+ nFieldSelPos = GetFieldSelPos( static_cast<SCCOL>(rEntry.nField) );
+
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ OUString aQueryStr = rItem.maString.getString();
+ if (rEntry.IsQueryByEmpty())
+ {
+ aValStr = aStrEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aValStr = aStrNotEmpty;
+ maCondLbArr[i]->set_sensitive(false);
+ }
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ nCondPos = maCondLbArr[i]->find_text(
+ rEntry.IsQueryByTextColor() ? aStrFontColor : aStrBackgroundColor);
+
+ maValueEdArr[i]->set_visible(false);
+ maColorLbArr[i]->set_visible(true);
+ maColorLbArr[i]->set_sensitive(true);
+ }
+ else
+ {
+ SetValString(aQueryStr, rItem, aValStr);
+ maCondLbArr[i]->set_sensitive(true);
+ }
+ maFieldLbArr[i]->set_sensitive(true);
+ maValueEdArr[i]->set_sensitive(true);
+ maRemoveBtnArr[i]->set_sensitive(true);
+
+ if (nOffset==0)
+ {
+ if (i<3)
+ {
+ if(rEntry.bDoQuery)
+ maConnLbArr[i+1]->set_sensitive(true);
+ else
+ maConnLbArr[i+1]->set_sensitive(false);
+ size_t nQENext = nQE + 1;
+ if (maRefreshExceptQuery.size() < nQENext + 1)
+ maRefreshExceptQuery.resize(nQENext + 1, false);
+ if (theQueryData.GetEntry(nQENext).bDoQuery || maRefreshExceptQuery[nQENext])
+ maConnLbArr[i+1]->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(nQENext).eConnect) );
+ else
+ maConnLbArr[i+1]->set_active(-1);
+ }
+ }
+ else
+ {
+ if(theQueryData.GetEntry( nQE-1).bDoQuery)
+ maConnLbArr[i]->set_sensitive(true);
+ else
+ maConnLbArr[i]->set_sensitive(false);
+
+ if (maRefreshExceptQuery.size() < nQE + 1)
+ maRefreshExceptQuery.resize(nQE + 1, false);
+ if(rEntry.bDoQuery || maRefreshExceptQuery[nQE])
+ maConnLbArr[i]->set_active( static_cast<sal_uInt16>(rEntry.eConnect) );
+ else
+ maConnLbArr[i]->set_active(-1);
+ }
+
+ }
+ else
+ {
+ if (nOffset==0)
+ {
+ if(i<3)
+ {
+ maConnLbArr[i+1]->set_active(-1);
+ maConnLbArr[i+1]->set_sensitive(false);
+ }
+ }
+ else
+ {
+ if(theQueryData.GetEntry( nQE-1).bDoQuery)
+ maConnLbArr[i]->set_sensitive(true);
+ else
+ maConnLbArr[i]->set_sensitive(false);
+ maConnLbArr[i]->set_active(-1);
+ }
+ maFieldLbArr[i]->set_sensitive(false);
+ maCondLbArr[i]->set_sensitive(false);
+ maValueEdArr[i]->set_sensitive(false);
+ maRemoveBtnArr[i]->set_sensitive(false);
+ }
+ maFieldLbArr[i]->set_active( nFieldSelPos );
+ maCondLbArr [i]->set_active( nCondPos );
+ maValueEdArr[i]->set_entry_text( aValStr );
+ UpdateValueList(i+1);
+ UpdateColorList(i+1);
+ }
+}
+
+void ScFilterDlg::SetValString( const OUString& rQueryStr, const ScQueryEntry::Item& rItem,
+ OUString& rValStr )
+{
+ if (rQueryStr.isEmpty())
+ {
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+ if (rItem.meType == ScQueryEntry::ByValue)
+ {
+ if (pDoc)
+ {
+ pDoc->GetFormatTable()->GetInputLineString(rItem.mfVal, 0, rValStr);
+ }
+ }
+ else if (rItem.meType == ScQueryEntry::ByDate)
+ {
+ if (pDoc)
+ {
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ pFormatter->GetInputLineString(rItem.mfVal,
+ pFormatter->GetStandardFormat( SvNumFormatType::DATE), rValStr);
+ }
+ }
+ else
+ {
+ SAL_WARN( "sc", "ScFilterDlg::SetValString: empty query string, really?");
+ rValStr = rQueryStr;
+ }
+ }
+ else
+ {
+ // XXX NOTE: if not ByString we just assume this has been
+ // set to a proper string corresponding to the numeric
+ // value earlier!
+ rValStr = rQueryStr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/foptmgr.cxx b/sc/source/ui/dbgui/foptmgr.cxx
new file mode 100644
index 0000000000..decaa622ba
--- /dev/null
+++ b/sc/source/ui/dbgui/foptmgr.cxx
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+
+#include <rangeutl.hxx>
+#include <dbdata.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <queryparam.hxx>
+#include <globalnames.hxx>
+
+#include <foptmgr.hxx>
+#include <formula/funcutl.hxx>
+
+// ScFilterOptionsMgr (.ui's option helper)
+
+ScFilterOptionsMgr::ScFilterOptionsMgr(
+ ScViewData* ptrViewData,
+ const ScQueryParam& refQueryData,
+ weld::CheckButton* refBtnCase,
+ weld::CheckButton* refBtnRegExp,
+ weld::CheckButton* refBtnHeader,
+ weld::CheckButton* refBtnUnique,
+ weld::CheckButton* refBtnCopyResult,
+ weld::CheckButton* refBtnDestPers,
+ weld::ComboBox* refLbCopyArea,
+ formula::RefEdit* refEdCopyArea,
+ formula::RefButton* refRbCopyArea,
+ weld::Label* refFtDbAreaLabel,
+ weld::Label* refFtDbArea,
+ const OUString& refStrUndefined )
+
+ : pViewData ( ptrViewData ),
+ pDoc ( ptrViewData ? &ptrViewData->GetDocument() : nullptr ),
+ pBtnCase ( refBtnCase ),
+ pBtnRegExp ( refBtnRegExp ),
+ pBtnHeader ( refBtnHeader ),
+ pBtnUnique ( refBtnUnique ),
+ pBtnCopyResult ( refBtnCopyResult ),
+ pBtnDestPers ( refBtnDestPers ),
+ pLbCopyArea ( refLbCopyArea ),
+ pEdCopyArea ( refEdCopyArea ),
+ pRbCopyArea ( refRbCopyArea ),
+ pFtDbAreaLabel ( refFtDbAreaLabel ),
+ pFtDbArea ( refFtDbArea ),
+ rStrUndefined ( refStrUndefined ),
+ rQueryData ( refQueryData )
+{
+ Init();
+}
+
+void ScFilterOptionsMgr::Init()
+{
+//moggi:TODO
+ OSL_ENSURE( pViewData && pDoc, "Init failed :-/" );
+
+ pLbCopyArea->connect_changed( LINK( this, ScFilterOptionsMgr, LbAreaSelHdl ) );
+ pEdCopyArea->SetModifyHdl ( LINK( this, ScFilterOptionsMgr, EdAreaModifyHdl ) );
+ pBtnCopyResult->connect_toggled( LINK( this, ScFilterOptionsMgr, BtnCopyResultHdl ) );
+
+ pBtnCase->set_active( rQueryData.bCaseSens );
+ pBtnHeader->set_active( rQueryData.bHasHeader );
+ pBtnRegExp->set_active( rQueryData.eSearchType == utl::SearchParam::SearchType::Regexp );
+ pBtnUnique->set_active( !rQueryData.bDuplicate );
+
+ if ( pViewData && pDoc )
+ {
+ OUString theAreaStr;
+ ScRange theCurArea ( ScAddress( rQueryData.nCol1,
+ rQueryData.nRow1,
+ pViewData->GetTabNo() ),
+ ScAddress( rQueryData.nCol2,
+ rQueryData.nRow2,
+ pViewData->GetTabNo() ) );
+ ScDBCollection* pDBColl = pDoc->GetDBCollection();
+ OUString theDbArea;
+ OUString theDbName(STR_DB_LOCAL_NONAME);
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ theAreaStr = theCurArea.Format(*pDoc, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ // fill the target area list
+
+ pLbCopyArea->clear();
+ pLbCopyArea->append_text(rStrUndefined);
+
+ ScAreaNameIterator aIter( *pDoc );
+ OUString aName;
+ ScRange aRange;
+ while ( aIter.Next( aName, aRange ) )
+ {
+ OUString aRefStr(aRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv));
+ pLbCopyArea->append(aRefStr, aName);
+ }
+
+ pBtnDestPers->set_active(true); // always on when called
+ pLbCopyArea->set_active( 0 );
+ pEdCopyArea->SetText( OUString() );
+
+ /*
+ * Check whether the transferred area is a database area:
+ */
+
+ theDbArea = theAreaStr;
+
+ if ( pDBColl )
+ {
+ ScAddress& rStart = theCurArea.aStart;
+ ScAddress& rEnd = theCurArea.aEnd;
+ const ScDBData* pDBData = pDBColl->GetDBAtArea(
+ rStart.Tab(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row());
+
+ if ( pDBData )
+ {
+ pBtnHeader->set_active( pDBData->HasHeader() );
+ theDbName = pDBData->GetName();
+
+ pBtnHeader->set_sensitive(theDbName == STR_DB_LOCAL_NONAME);
+ }
+ }
+
+ if ( theDbName != STR_DB_LOCAL_NONAME )
+ {
+ theDbArea += " (" + theDbName + ")";
+
+ pFtDbArea->set_label( theDbArea );
+ }
+ else
+ {
+ pFtDbAreaLabel->set_label( OUString() );
+ pFtDbArea->set_label( OUString() );
+ }
+
+ // position to copy to:
+
+ if ( !rQueryData.bInplace )
+ {
+ OUString aString =
+ ScAddress( rQueryData.nDestCol,
+ rQueryData.nDestRow,
+ rQueryData.nDestTab
+ ).Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv);
+
+ pBtnCopyResult->set_active(true);
+ pEdCopyArea->SetText( aString );
+ EdAreaModifyHdl( *pEdCopyArea );
+ pLbCopyArea->set_sensitive(true);
+ pEdCopyArea->GetWidget()->set_sensitive(true);
+ pRbCopyArea->GetWidget()->set_sensitive(true);
+ pBtnDestPers->set_sensitive(true);
+ }
+ else
+ {
+ pBtnCopyResult->set_active( false );
+ pEdCopyArea->SetText( OUString() );
+ pLbCopyArea->set_sensitive(false);
+ pEdCopyArea->GetWidget()->set_sensitive(false);
+ pRbCopyArea->GetWidget()->set_sensitive(false);
+ pBtnDestPers->set_sensitive(false);
+ }
+ }
+ else
+ pEdCopyArea->SetText( OUString() );
+}
+
+bool ScFilterOptionsMgr::VerifyPosStr( const OUString& rPosStr ) const
+{
+ OUString aPosStr( rPosStr );
+ sal_Int32 nColonPos = aPosStr.indexOf( ':' );
+
+ if ( -1 != nColonPos )
+ aPosStr = aPosStr.copy( 0, nColonPos );
+
+ ScRefFlags nResult = ScAddress().Parse( aPosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ return (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;
+}
+
+// Handler:
+
+IMPL_LINK( ScFilterOptionsMgr, LbAreaSelHdl, weld::ComboBox&, rLb, void )
+{
+ if ( &rLb == pLbCopyArea )
+ {
+ OUString aString;
+ const sal_Int32 nSelPos = pLbCopyArea->get_active();
+
+ if ( nSelPos > 0 )
+ aString = pLbCopyArea->get_id(nSelPos);
+
+ pEdCopyArea->SetText( aString );
+ }
+}
+
+IMPL_LINK( ScFilterOptionsMgr, EdAreaModifyHdl, formula::RefEdit&, rEd, void )
+{
+ if ( &rEd != pEdCopyArea )
+ return;
+
+ OUString theCurPosStr = rEd.GetText();
+ ScRefFlags nResult = ScAddress().Parse( theCurPosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::VALID)
+ {
+ const sal_Int32 nCount = pLbCopyArea->get_count();
+
+ for ( sal_Int32 i=2; i<nCount; ++i )
+ {
+ OUString aStr = pLbCopyArea->get_id(i);
+ if (theCurPosStr == aStr)
+ {
+ pLbCopyArea->set_active( i );
+ return;
+ }
+ }
+
+ }
+ pLbCopyArea->set_active( 0 );
+}
+
+IMPL_LINK( ScFilterOptionsMgr, BtnCopyResultHdl, weld::Toggleable&, rBox, void )
+{
+ if ( &rBox != pBtnCopyResult )
+ return;
+
+ if ( rBox.get_active() )
+ {
+ pBtnDestPers->set_sensitive(true);
+ pLbCopyArea->set_sensitive(true);
+ pEdCopyArea->GetWidget()->set_sensitive(true);
+ pRbCopyArea->GetWidget()->set_sensitive(true);
+ pEdCopyArea->GrabFocus();
+ }
+ else
+ {
+ pBtnDestPers->set_sensitive(false);
+ pLbCopyArea->set_sensitive(false);
+ pEdCopyArea->GetWidget()->set_sensitive(false);
+ pRbCopyArea->GetWidget()->set_sensitive(false);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/imoptdlg.cxx b/sc/source/ui/dbgui/imoptdlg.cxx
new file mode 100644
index 0000000000..2777eb9e45
--- /dev/null
+++ b/sc/source/ui/dbgui/imoptdlg.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 <imoptdlg.hxx>
+#include <asciiopt.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/thread.h>
+#include <o3tl/string_view.hxx>
+#include <global.hxx>
+
+const char pStrFix[] = "FIX";
+
+// The option string can no longer contain a semicolon (because of pick list),
+// therefore, starting with version 336 comma instead
+
+ScImportOptions::ScImportOptions( std::u16string_view rStr )
+{
+ // Use the same string format as ScAsciiOptions,
+ // because the import options string is passed here when a CSV file is loaded and saved again.
+ // The old format is still supported because it might be used in macros.
+
+ bFixedWidth = false;
+ nFieldSepCode = 0;
+ nTextSepCode = 0;
+ eCharSet = RTL_TEXTENCODING_DONTKNOW;
+ bSaveAsShown = true; // "true" if not in string (after CSV import)
+ bQuoteAllText = false;
+ bSaveNumberAsSuch = true;
+ bSaveFormulas = false;
+ bRemoveSpace = false;
+ nSheetToExport = 0;
+ bEvaluateFormulas = true; // true if not present at all, for compatibility
+ bIncludeBOM = false;
+ sal_Int32 nTokenCount = comphelper::string::getTokenCount(rStr, ',');
+ if ( nTokenCount < 3 )
+ return;
+
+ sal_Int32 nIdx{ 0 };
+ // first 3 tokens: common
+ OUString aToken( o3tl::getToken(rStr, 0, ',', nIdx ) );
+ if( aToken.equalsIgnoreAsciiCase( pStrFix ) )
+ bFixedWidth = true;
+ else
+ nFieldSepCode = ScAsciiOptions::GetWeightedFieldSep( aToken, true);
+ nTextSepCode = static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rStr, 0, ',', nIdx)));
+ aStrFont = o3tl::getToken(rStr, 0, ',', nIdx);
+ eCharSet = ScGlobal::GetCharsetValue(aStrFont);
+
+ if ( nTokenCount == 4 )
+ {
+ // compatibility with old options string: "Save as shown" as 4th token, numeric
+ bSaveAsShown = o3tl::toInt32(o3tl::getToken(rStr, 0, ',', nIdx)) != 0;
+ bQuoteAllText = true; // use old default then
+ }
+ else
+ {
+ // look at the same positions as in ScAsciiOptions
+ if ( nTokenCount >= 7 )
+ bQuoteAllText = o3tl::getToken(rStr, 3, ',', nIdx) == u"true"; // 7th token
+ if ( nTokenCount >= 8 )
+ bSaveNumberAsSuch = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 9 )
+ bSaveAsShown = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 10 )
+ bSaveFormulas = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 11 )
+ bRemoveSpace = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if ( nTokenCount >= 12 )
+ {
+ const OUString aTok(o3tl::getToken(rStr,0, ',', nIdx));
+ if (aTok == "-1")
+ nSheetToExport = -1; // all
+ else if (aTok.isEmpty() || CharClass::isAsciiNumeric(aTok))
+ nSheetToExport = aTok.toInt32();
+ else
+ nSheetToExport = -23; // invalid, force error
+ }
+ if ( nTokenCount >= 13 )
+ // If present, defaults to "false".
+ bEvaluateFormulas = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ if (nTokenCount >= 14)
+ bIncludeBOM = o3tl::getToken(rStr, 0, ',', nIdx) == u"true";
+ }
+}
+
+OUString ScImportOptions::BuildString() const
+{
+ OUString aResult;
+
+ if( bFixedWidth )
+ aResult += pStrFix;
+ else
+ aResult += OUString::number(nFieldSepCode);
+ aResult += "," + OUString::number(nTextSepCode) + "," + aStrFont +
+ // use the same string format as ScAsciiOptions:
+ ",1,,0," + // first row, no column info, default language
+ OUString::boolean( bQuoteAllText ) + // same as "quoted field as text" in ScAsciiOptions
+ "," +
+ OUString::boolean( bSaveNumberAsSuch ) + // "save number as such": not in ScAsciiOptions
+ "," +
+ OUString::boolean( bSaveAsShown ) + // "save as shown": not in ScAsciiOptions
+ "," +
+ OUString::boolean( bSaveFormulas ) + // "save formulas": not in ScAsciiOptions
+ "," +
+ OUString::boolean( bRemoveSpace ) + // same as "Remove space" in ScAsciiOptions
+ "," +
+ OUString::number(nSheetToExport) + // Only available for command line --convert-to
+ "," +
+ OUString::boolean( bEvaluateFormulas ) + // same as "Evaluate formulas" in ScAsciiOptions
+ "," +
+ OUString::boolean(bIncludeBOM) ; // same as "Include BOM" in ScAsciiOptions
+
+ return aResult;
+}
+
+void ScImportOptions::SetTextEncoding( rtl_TextEncoding nEnc )
+{
+ eCharSet = (nEnc == RTL_TEXTENCODING_DONTKNOW ?
+ osl_getThreadTextEncoding() : nEnc);
+ aStrFont = ScGlobal::GetCharsetString( nEnc );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/pfiltdlg.cxx b/sc/source/ui/dbgui/pfiltdlg.cxx
new file mode 100644
index 0000000000..e87d676b59
--- /dev/null
+++ b/sc/source/ui/dbgui/pfiltdlg.cxx
@@ -0,0 +1,507 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <uiitems.hxx>
+#include <global.hxx>
+#include <globalnames.hxx>
+#include <dbdata.hxx>
+#include <scresid.hxx>
+#include <queryentry.hxx>
+#include <filterentries.hxx>
+
+#include <sc.hrc>
+#include <strings.hrc>
+
+#include <pfiltdlg.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <osl/diagnose.h>
+
+ScPivotFilterDlg::ScPivotFilterDlg(weld::Window* pParent, const SfxItemSet& rArgSet,
+ SCTAB nSourceTab )
+ : GenericDialogController(pParent, "modules/scalc/ui/pivotfilterdialog.ui", "PivotFilterDialog")
+ , aStrNone(ScResId(SCSTR_NONE))
+ , aStrEmpty(ScResId(SCSTR_FILTER_EMPTY))
+ , aStrNotEmpty(ScResId(SCSTR_FILTER_NOTEMPTY))
+ , aStrColumn(ScResId(SCSTR_COLUMN_LETTER))
+ , nWhichQuery(rArgSet.GetPool()->GetWhich(SID_QUERY))
+ , theQueryData(static_cast<const ScQueryItem&>(rArgSet.Get(nWhichQuery)).GetQueryData())
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , nSrcTab(nSourceTab) // is not in QueryParam
+ , m_xLbField1(m_xBuilder->weld_combo_box("field1"))
+ , m_xLbCond1(m_xBuilder->weld_combo_box("cond1"))
+ , m_xEdVal1(m_xBuilder->weld_combo_box("val1"))
+ , m_xLbConnect1(m_xBuilder->weld_combo_box("connect1"))
+ , m_xLbField2(m_xBuilder->weld_combo_box("field2"))
+ , m_xLbCond2(m_xBuilder->weld_combo_box("cond2"))
+ , m_xEdVal2(m_xBuilder->weld_combo_box("val2"))
+ , m_xLbConnect2(m_xBuilder->weld_combo_box("connect2"))
+ , m_xLbField3(m_xBuilder->weld_combo_box("field3"))
+ , m_xLbCond3(m_xBuilder->weld_combo_box("cond3"))
+ , m_xEdVal3(m_xBuilder->weld_combo_box("val3"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnRegExp(m_xBuilder->weld_check_button("regexp"))
+ , m_xBtnUnique(m_xBuilder->weld_check_button("unique"))
+ , m_xFtDbArea(m_xBuilder->weld_label("dbarea"))
+{
+ Init( rArgSet );
+}
+
+ScPivotFilterDlg::~ScPivotFilterDlg()
+{
+}
+
+void ScPivotFilterDlg::Init( const SfxItemSet& rArgSet )
+{
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery ));
+
+ m_xBtnCase->connect_toggled( LINK( this, ScPivotFilterDlg, CheckBoxHdl ) );
+
+ m_xLbField1->connect_changed ( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbField2->connect_changed ( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbField3->connect_changed ( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbConnect1->connect_changed( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+ m_xLbConnect2->connect_changed( LINK( this, ScPivotFilterDlg, LbSelectHdl ) );
+
+ m_xBtnCase->set_active( theQueryData.bCaseSens );
+ m_xBtnRegExp->set_active( theQueryData.eSearchType == utl::SearchParam::SearchType::Regexp );
+ m_xBtnUnique->set_active( !theQueryData.bDuplicate );
+
+ pViewData = rQueryItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+
+ // for easier access:
+ aFieldLbArr [0] = m_xLbField1.get();
+ aFieldLbArr [1] = m_xLbField2.get();
+ aFieldLbArr [2] = m_xLbField3.get();
+ aValueEdArr [0] = m_xEdVal1.get();
+ aValueEdArr [1] = m_xEdVal2.get();
+ aValueEdArr [2] = m_xEdVal3.get();
+ aCondLbArr [0] = m_xLbCond1.get();
+ aCondLbArr [1] = m_xLbCond2.get();
+ aCondLbArr [2] = m_xLbCond3.get();
+
+ if ( pViewData && pDoc )
+ {
+ ScRange theCurArea ( ScAddress( theQueryData.nCol1,
+ theQueryData.nRow1,
+ nSrcTab ),
+ ScAddress( theQueryData.nCol2,
+ theQueryData.nRow2,
+ nSrcTab ) );
+ ScDBCollection* pDBColl = pDoc->GetDBCollection();
+ OUString theDbName = STR_DB_LOCAL_NONAME;
+
+ // Check if the passed range is a database range
+
+ if ( pDBColl )
+ {
+ ScAddress& rStart = theCurArea.aStart;
+ ScAddress& rEnd = theCurArea.aEnd;
+ ScDBData* pDBData = pDBColl->GetDBAtArea( rStart.Tab(),
+ rStart.Col(), rStart.Row(),
+ rEnd.Col(), rEnd.Row() );
+ if ( pDBData )
+ theDbName = pDBData->GetName();
+ }
+
+ OUString sLabel = " (" + theDbName + ")";
+ m_xFtDbArea->set_label(sLabel);
+ }
+ else
+ {
+ m_xFtDbArea->set_label(OUString());
+ }
+
+ // Read the field lists and select the entries:
+
+ FillFieldLists();
+
+ for ( SCSIZE i=0; i<3; i++ )
+ {
+ if ( theQueryData.GetEntry(i).bDoQuery )
+ {
+ const ScQueryEntry& rEntry = theQueryData.GetEntry(i);
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ OUString aValStr = rItem.maString.getString();
+ if (rEntry.IsQueryByEmpty())
+ aValStr = aStrEmpty;
+ else if (rEntry.IsQueryByNonEmpty())
+ aValStr = aStrNotEmpty;
+ sal_uInt16 nCondPos = static_cast<sal_uInt16>(rEntry.eOp);
+ sal_uInt16 nFieldSelPos = GetFieldSelPos( static_cast<SCCOL>(rEntry.nField) );
+
+ aFieldLbArr[i]->set_active( nFieldSelPos );
+ aCondLbArr [i]->set_active( nCondPos );
+ UpdateValueList( static_cast<sal_uInt16>(i+1) );
+ aValueEdArr[i]->set_entry_text(aValStr);
+ if (aValStr == aStrEmpty || aValStr == aStrNotEmpty)
+ aCondLbArr[i]->set_sensitive(false);
+ }
+ else
+ {
+ aFieldLbArr[i]->set_active( 0 ); // "none" selected
+ aCondLbArr [i]->set_active( 0 ); // "=" selected
+ UpdateValueList( static_cast<sal_uInt16>(i) );
+ aValueEdArr[i]->set_entry_text(OUString());
+ }
+ aValueEdArr[i]->connect_changed( LINK( this, ScPivotFilterDlg, ValModifyHdl ) );
+ }
+
+ // disable/enable logic:
+
+ if (m_xLbField1->get_active() != 0 && m_xLbField2->get_active() != 0)
+ m_xLbConnect1->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(1).eConnect) );
+ else
+ m_xLbConnect1->set_active(-1);
+
+ if (m_xLbField2->get_active() != 0 && m_xLbField3->get_active() != 0)
+ m_xLbConnect2->set_active( static_cast<sal_uInt16>(theQueryData.GetEntry(2).eConnect) );
+ else
+ m_xLbConnect2->set_active(-1);
+
+ if (m_xLbField1->get_active() == 0)
+ {
+ m_xLbConnect1->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ }
+ else if (m_xLbConnect1->get_active() == -1)
+ {
+ m_xLbField2->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ }
+
+ if (m_xLbField2->get_active() == 0)
+ {
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+ else if (m_xLbConnect2->get_active() == -1)
+ {
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+}
+
+void ScPivotFilterDlg::FillFieldLists()
+{
+ m_xLbField1->clear();
+ m_xLbField2->clear();
+ m_xLbField3->clear();
+ m_xLbField1->append_text(aStrNone);
+ m_xLbField2->append_text(aStrNone);
+ m_xLbField3->append_text(aStrNone);
+
+ if ( !pDoc )
+ return;
+
+ OUString aFieldName;
+ SCTAB nTab = nSrcTab;
+ SCCOL nFirstCol = theQueryData.nCol1;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCCOL nMaxCol = theQueryData.nCol2;
+ SCCOL col = 0;
+
+ for ( col=nFirstCol; col<=nMaxCol; col++ )
+ {
+ aFieldName = pDoc->GetString(col, nFirstRow, nTab);
+ if ( aFieldName.isEmpty() )
+ {
+ aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, u"%1", ScColToAlpha( col ));
+ }
+ m_xLbField1->append_text(aFieldName);
+ m_xLbField2->append_text(aFieldName);
+ m_xLbField3->append_text(aFieldName);
+ }
+}
+
+void ScPivotFilterDlg::UpdateValueList( sal_uInt16 nList )
+{
+ if ( !(pDoc && nList>0 && nList<=3) )
+ return;
+
+ weld::ComboBox* pValList = aValueEdArr[nList-1];
+ sal_Int32 nFieldSelPos = aFieldLbArr[nList-1]->get_active();
+ OUString aCurValue = pValList->get_active_text();
+
+ pValList->clear();
+ pValList->append_text(aStrNotEmpty);
+ pValList->append_text(aStrEmpty);
+
+ if ( pDoc && nFieldSelPos )
+ {
+ SCCOL nColumn = theQueryData.nCol1 + static_cast<SCCOL>(nFieldSelPos) - 1;
+ if (!m_pEntryLists[nColumn])
+ {
+ weld::WaitObject aWaiter(m_xDialog.get());
+
+ SCTAB nTab = nSrcTab;
+ SCROW nFirstRow = theQueryData.nRow1;
+ SCROW nLastRow = theQueryData.nRow2;
+ nFirstRow++;
+ bool bCaseSens = m_xBtnCase->get_active();
+ m_pEntryLists[nColumn].reset( new ScFilterEntries);
+ pDoc->GetFilterEntriesArea(
+ nColumn, nFirstRow, nLastRow, nTab, bCaseSens, *m_pEntryLists[nColumn]);
+ }
+
+ const ScFilterEntries* pColl = m_pEntryLists[nColumn].get();
+ for (const auto& rEntry : *pColl)
+ {
+ pValList->append_text(rEntry.GetString());
+ }
+ }
+ pValList->set_entry_text(aCurValue);
+}
+
+void ScPivotFilterDlg::ClearValueList( sal_uInt16 nList )
+{
+ if ( nList>0 && nList<=3 )
+ {
+ weld::ComboBox* pValList = aValueEdArr[nList-1];
+ pValList->clear();
+ pValList->append_text(aStrNotEmpty);
+ pValList->append_text(aStrEmpty);
+ pValList->set_entry_text(OUString());
+ }
+}
+
+sal_uInt16 ScPivotFilterDlg::GetFieldSelPos( SCCOL nField )
+{
+ if ( nField >= theQueryData.nCol1 && nField <= theQueryData.nCol2 )
+ return static_cast<sal_uInt16>(nField - theQueryData.nCol1 + 1);
+ else
+ return 0;
+}
+
+const ScQueryItem& ScPivotFilterDlg::GetOutputItem()
+{
+ ScQueryParam theParam( theQueryData );
+ sal_Int32 nConnect1 = m_xLbConnect1->get_active();
+ sal_Int32 nConnect2 = m_xLbConnect2->get_active();
+
+ svl::SharedStringPool& rPool = pViewData->GetDocument().GetSharedStringPool();
+
+ for ( SCSIZE i=0; i<3; i++ )
+ {
+ const sal_Int32 nField = aFieldLbArr[i]->get_active();
+ ScQueryOp eOp = static_cast<ScQueryOp>(aCondLbArr[i]->get_active());
+
+ bool bDoThis = (aFieldLbArr[i]->get_active() != 0);
+ theParam.GetEntry(i).bDoQuery = bDoThis;
+
+ if ( bDoThis )
+ {
+ ScQueryEntry& rEntry = theParam.GetEntry(i);
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+
+ OUString aStrVal = aValueEdArr[i]->get_active_text();
+
+ /*
+ * The dialog returns the specific field values "empty"/"non empty"
+ * as constant in nVal in connection with the bQueryByString switch
+ * set to false
+ */
+ if ( aStrVal == aStrEmpty )
+ {
+ OSL_ASSERT(eOp == SC_EQUAL);
+ rEntry.SetQueryByEmpty();
+ }
+ else if ( aStrVal == aStrNotEmpty )
+ {
+ OSL_ASSERT(eOp == SC_EQUAL);
+ rEntry.SetQueryByNonEmpty();
+ }
+ else
+ {
+ rItem.maString = rPool.intern(aStrVal);
+ rItem.mfVal = 0.0;
+ rItem.meType = ScQueryEntry::ByString;
+ }
+
+ rEntry.nField = nField ? (theQueryData.nCol1 +
+ static_cast<SCCOL>(nField) - 1) : static_cast<SCCOL>(0);
+ rEntry.eOp = eOp;
+ }
+ }
+
+ theParam.GetEntry(1).eConnect = (nConnect1 != -1)
+ ? static_cast<ScQueryConnect>(nConnect1)
+ : SC_AND;
+ theParam.GetEntry(2).eConnect = (nConnect2 != -1)
+ ? static_cast<ScQueryConnect>(nConnect2)
+ : SC_AND;
+
+ theParam.bInplace = false;
+ theParam.nDestTab = 0; // Where do those values come from?
+ theParam.nDestCol = 0;
+ theParam.nDestRow = 0;
+
+ theParam.bDuplicate = !m_xBtnUnique->get_active();
+ theParam.bCaseSens = m_xBtnCase->get_active();
+ theParam.eSearchType = m_xBtnRegExp->get_active() ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal;
+
+ pOutItem.reset( new ScQueryItem( nWhichQuery, &theParam ) );
+
+ return *pOutItem;
+}
+
+// Handler:
+
+IMPL_LINK( ScPivotFilterDlg, LbSelectHdl, weld::ComboBox&, rLb, void )
+{
+ /*
+ * Handling the enable/disable logic based on which ListBox was touched:
+ */
+ if (&rLb == m_xLbConnect1.get())
+ {
+ if ( !m_xLbField2->get_sensitive() )
+ {
+ m_xLbField2->set_sensitive(true);
+ m_xLbCond2->set_sensitive(true);
+ m_xEdVal2->set_sensitive(true);
+ }
+ }
+ else if (&rLb == m_xLbConnect2.get())
+ {
+ if ( !m_xLbField3->get_sensitive() )
+ {
+ m_xLbField3->set_sensitive(true);
+ m_xLbCond3->set_sensitive(true);
+ m_xEdVal3->set_sensitive(true);
+ }
+ }
+ else if (&rLb == m_xLbField1.get())
+ {
+ if ( m_xLbField1->get_active() == 0 )
+ {
+ m_xLbConnect1->set_active(-1);
+ m_xLbConnect2->set_active(-1);
+ m_xLbField2->set_active( 0 );
+ m_xLbField3->set_active( 0 );
+ m_xLbCond2->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ ClearValueList( 1 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+
+ m_xLbConnect1->set_sensitive(false);
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond2->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal2->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+ else
+ {
+ UpdateValueList( 1 );
+ if ( !m_xLbConnect1->get_sensitive() )
+ {
+ m_xLbConnect1->set_sensitive(true);
+ }
+ }
+ }
+ else if (&rLb == m_xLbField2.get())
+ {
+ if ( m_xLbField2->get_active() == 0 )
+ {
+ m_xLbConnect2->set_active(-1);
+ m_xLbField3->set_active( 0 );
+ m_xLbCond3->set_active( 0 );
+ ClearValueList( 2 );
+ ClearValueList( 3 );
+
+ m_xLbConnect2->set_sensitive(false);
+ m_xLbField3->set_sensitive(false);
+ m_xLbCond3->set_sensitive(false);
+ m_xEdVal3->set_sensitive(false);
+ }
+ else
+ {
+ UpdateValueList( 2 );
+ if (!m_xLbConnect2->get_sensitive())
+ {
+ m_xLbConnect2->set_sensitive(true);
+ }
+ }
+ }
+ else if (&rLb == m_xLbField3.get())
+ {
+ if (m_xLbField3->get_active() == 0)
+ ClearValueList(3);
+ else
+ UpdateValueList(3);
+ }
+}
+
+IMPL_LINK(ScPivotFilterDlg, CheckBoxHdl, weld::Toggleable&, rBox, void)
+{
+ // update the value lists when dealing with uppercase/lowercase
+
+ if (&rBox != m_xBtnCase.get()) // value lists
+ return;
+
+ for (auto& a : m_pEntryLists)
+ a.reset();
+
+ OUString aCurVal1 = m_xEdVal1->get_active_text();
+ OUString aCurVal2 = m_xEdVal2->get_active_text();
+ OUString aCurVal3 = m_xEdVal3->get_active_text();
+ UpdateValueList( 1 );
+ UpdateValueList( 2 );
+ UpdateValueList( 3 );
+ m_xEdVal1->set_entry_text(aCurVal1);
+ m_xEdVal2->set_entry_text(aCurVal2);
+ m_xEdVal3->set_entry_text(aCurVal3);
+}
+
+IMPL_LINK( ScPivotFilterDlg, ValModifyHdl, weld::ComboBox&, rEd, void )
+{
+ OUString aStrVal = rEd.get_active_text();
+ weld::ComboBox* pLb = m_xLbCond1.get();
+
+ if ( &rEd == m_xEdVal2.get() ) pLb = m_xLbCond2.get();
+ else if ( &rEd == m_xEdVal3.get() ) pLb = m_xLbCond3.get();
+
+ // if cond of the special values "empty"/"non-empty" was chosen only the
+ // =-operand makes sense:
+
+ if ( aStrEmpty == aStrVal || aStrNotEmpty == aStrVal )
+ {
+ pLb->set_active_text(OUString('='));
+ pLb->set_sensitive(false);
+ }
+ else
+ pLb->set_sensitive(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/pvfundlg.cxx b/sc/source/ui/dbgui/pvfundlg.cxx
new file mode 100644
index 0000000000..7f97e25767
--- /dev/null
+++ b/sc/source/ui/dbgui/pvfundlg.cxx
@@ -0,0 +1,964 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <pvfundlg.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+
+#include <osl/diagnose.h>
+
+#include <scresid.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <pvfundlg.hrc>
+#include <globstr.hrc>
+#include <dputil.hxx>
+
+#include <vector>
+
+using namespace ::com::sun::star::sheet;
+
+using ::com::sun::star::uno::Sequence;
+using ::std::vector;
+
+namespace {
+
+/** Appends all strings from the Sequence to the list box.
+
+ Empty strings are replaced by a localized "(empty)" entry and inserted at
+ the specified position.
+
+ @return true = The passed string list contains an empty string entry.
+ */
+
+bool lclFillListBox(weld::ComboBox& rLBox, const Sequence< OUString >& rStrings)
+{
+ bool bEmpty = false;
+ for (const OUString& str : rStrings)
+ {
+ if (!str.isEmpty())
+ rLBox.append_text(str);
+ else
+ {
+ rLBox.append_text(ScResId(STR_EMPTYDATA));
+ bEmpty = true;
+ }
+ }
+ return bEmpty;
+}
+
+bool lclFillListBox(weld::ComboBox& rLBox, const vector<ScDPLabelData::Member>& rMembers, int nEmptyPos)
+{
+ bool bEmpty = false;
+ vector<ScDPLabelData::Member>::const_iterator itr = rMembers.begin(), itrEnd = rMembers.end();
+ for (; itr != itrEnd; ++itr)
+ {
+ OUString aName = itr->getDisplayName();
+ if (!aName.isEmpty())
+ rLBox.append_text(aName);
+ else
+ {
+ rLBox.insert_text(nEmptyPos, ScResId(STR_EMPTYDATA));
+ bEmpty = true;
+ }
+ }
+ return bEmpty;
+}
+
+bool lclFillListBox(weld::TreeView& rLBox, const vector<ScDPLabelData::Member>& rMembers)
+{
+ bool bEmpty = false;
+ for (const auto& rMember : rMembers)
+ {
+ rLBox.append();
+ int pos = rLBox.n_children() - 1;
+ rLBox.set_toggle(pos, TRISTATE_FALSE);
+ OUString aName = rMember.getDisplayName();
+ if (!aName.isEmpty())
+ rLBox.set_text(pos, aName, 0);
+ else
+ {
+ rLBox.set_text(pos, ScResId(STR_EMPTYDATA), 0);
+ bEmpty = true;
+ }
+ }
+ return bEmpty;
+}
+
+/** This table represents the order of the strings in the resource string array. */
+const PivotFunc spnFunctions[] =
+{
+ PivotFunc::Sum,
+ PivotFunc::Count,
+ PivotFunc::Average,
+ PivotFunc::Median,
+ PivotFunc::Max,
+ PivotFunc::Min,
+ PivotFunc::Product,
+ PivotFunc::CountNum,
+ PivotFunc::StdDev,
+ PivotFunc::StdDevP,
+ PivotFunc::StdVar,
+ PivotFunc::StdVarP
+};
+
+const sal_uInt16 SC_BASEITEM_PREV_POS = 0;
+const sal_uInt16 SC_BASEITEM_NEXT_POS = 1;
+const sal_uInt16 SC_BASEITEM_USER_POS = 2;
+
+const sal_uInt16 SC_SORTNAME_POS = 0;
+const sal_uInt16 SC_SORTDATA_POS = 1;
+
+const tools::Long SC_SHOW_DEFAULT = 10;
+
+} // namespace
+
+ScDPFunctionListBox::ScDPFunctionListBox(std::unique_ptr<weld::TreeView> xControl)
+ : m_xControl(std::move(xControl))
+{
+ FillFunctionNames();
+}
+
+void ScDPFunctionListBox::SetSelection( PivotFunc nFuncMask )
+{
+ if( (nFuncMask == PivotFunc::NONE) || (nFuncMask == PivotFunc::Auto) )
+ m_xControl->unselect_all();
+ else
+ {
+ for( sal_Int32 nEntry = 0, nCount = m_xControl->n_children(); nEntry < nCount; ++nEntry )
+ {
+ if (bool(nFuncMask & spnFunctions[ nEntry ]))
+ m_xControl->select(nEntry);
+ else
+ m_xControl->unselect(nEntry);
+ }
+ }
+}
+
+PivotFunc ScDPFunctionListBox::GetSelection() const
+{
+ PivotFunc nFuncMask = PivotFunc::NONE;
+ std::vector<int> aRows = m_xControl->get_selected_rows();
+ for (int nSel : aRows)
+ nFuncMask |= spnFunctions[nSel];
+ return nFuncMask;
+}
+
+void ScDPFunctionListBox::FillFunctionNames()
+{
+ OSL_ENSURE( !m_xControl->n_children(), "ScDPMultiFuncListBox::FillFunctionNames - do not add texts to resource" );
+ m_xControl->clear();
+ m_xControl->freeze();
+ for (size_t nIndex = 0; nIndex < SAL_N_ELEMENTS(SCSTR_DPFUNCLISTBOX); ++nIndex)
+ m_xControl->append_text(ScResId(SCSTR_DPFUNCLISTBOX[nIndex]));
+ m_xControl->thaw();
+ assert(m_xControl->n_children() == SAL_N_ELEMENTS(spnFunctions));
+}
+
+namespace
+{
+ int FromDataPilotFieldReferenceType(int eMode)
+ {
+ switch (eMode)
+ {
+ case DataPilotFieldReferenceType::NONE:
+ return 0;
+ case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
+ return 1;
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ return 2;
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ return 3;
+ case DataPilotFieldReferenceType::RUNNING_TOTAL:
+ return 4;
+ case DataPilotFieldReferenceType::ROW_PERCENTAGE:
+ return 5;
+ case DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
+ return 6;
+ case DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
+ return 7;
+ case DataPilotFieldReferenceType::INDEX:
+ return 8;
+ }
+ return -1;
+ }
+
+ int ToDataPilotFieldReferenceType(int nPos)
+ {
+ switch (nPos)
+ {
+ case 0:
+ return DataPilotFieldReferenceType::NONE;
+ case 1:
+ return DataPilotFieldReferenceType::ITEM_DIFFERENCE;
+ case 2:
+ return DataPilotFieldReferenceType::ITEM_PERCENTAGE;
+ case 3:
+ return DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
+ case 4:
+ return DataPilotFieldReferenceType::RUNNING_TOTAL;
+ case 5:
+ return DataPilotFieldReferenceType::ROW_PERCENTAGE;
+ case 6:
+ return DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
+ case 7:
+ return DataPilotFieldReferenceType::TOTAL_PERCENTAGE;
+ case 8:
+ return DataPilotFieldReferenceType::INDEX;
+ }
+ return DataPilotFieldReferenceType::NONE;
+
+ }
+}
+
+ScDPFunctionDlg::ScDPFunctionDlg(
+ weld::Widget* pParent, const ScDPLabelDataVector& rLabelVec,
+ const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData)
+ : GenericDialogController(pParent, "modules/scalc/ui/datafielddialog.ui", "DataFieldDialog")
+ , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view("functions")))
+ , mxFtName(m_xBuilder->weld_label("name"))
+ , mxLbType(m_xBuilder->weld_combo_box("type"))
+ , mxFtBaseField(m_xBuilder->weld_label("basefieldft"))
+ , mxLbBaseField(m_xBuilder->weld_combo_box("basefield"))
+ , mxFtBaseItem(m_xBuilder->weld_label("baseitemft"))
+ , mxLbBaseItem(m_xBuilder->weld_combo_box("baseitem"))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mrLabelVec(rLabelVec)
+ , mbEmptyItem(false)
+{
+ mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
+
+ Init(rLabelData, rFuncData);
+}
+
+ScDPFunctionDlg::~ScDPFunctionDlg()
+{
+}
+
+PivotFunc ScDPFunctionDlg::GetFuncMask() const
+{
+ return mxLbFunc->GetSelection();
+}
+
+DataPilotFieldReference ScDPFunctionDlg::GetFieldRef() const
+{
+ DataPilotFieldReference aRef;
+
+ aRef.ReferenceType = ToDataPilotFieldReferenceType(mxLbType->get_active());
+ aRef.ReferenceField = GetBaseFieldName(mxLbBaseField->get_active_text());
+
+ sal_Int32 nBaseItemPos = mxLbBaseItem->get_active();
+ switch( nBaseItemPos )
+ {
+ case SC_BASEITEM_PREV_POS:
+ aRef.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS;
+ break;
+ case SC_BASEITEM_NEXT_POS:
+ aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT;
+ break;
+ default:
+ {
+ aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED;
+ if( !mbEmptyItem || (nBaseItemPos > SC_BASEITEM_USER_POS) )
+ aRef.ReferenceItemName = GetBaseItemName(mxLbBaseItem->get_active_text());
+ }
+ }
+
+ return aRef;
+}
+
+void ScDPFunctionDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
+{
+ mxBtnOk->connect_clicked( LINK( this, ScDPFunctionDlg, ButtonClicked ) );
+ mxBtnCancel->connect_clicked( LINK( this, ScDPFunctionDlg, ButtonClicked ) );
+
+ // list box
+ PivotFunc nFuncMask = (rFuncData.mnFuncMask == PivotFunc::NONE) ? PivotFunc::Sum : rFuncData.mnFuncMask;
+ mxLbFunc->SetSelection( nFuncMask );
+
+ // field name
+ mxFtName->set_label(rLabelData.getDisplayName());
+
+ // handlers
+ mxLbFunc->connect_row_activated( LINK( this, ScDPFunctionDlg, DblClickHdl ) );
+ mxLbType->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
+ mxLbBaseField->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
+
+ // base field list box
+ OUString aSelectedEntry;
+ for( const auto& rxLabel : mrLabelVec )
+ {
+ mxLbBaseField->append_text(rxLabel->getDisplayName());
+ maBaseFieldNameMap.emplace(rxLabel->getDisplayName(), rxLabel->maName);
+ if (rxLabel->maName == rFuncData.maFieldRef.ReferenceField)
+ aSelectedEntry = rxLabel->getDisplayName();
+ }
+
+ // select field reference type
+ mxLbType->set_active(FromDataPilotFieldReferenceType(rFuncData.maFieldRef.ReferenceType));
+ SelectHdl( *mxLbType ); // enables base field/item list boxes
+
+ // select base field
+ mxLbBaseField->set_active_text(aSelectedEntry);
+ if (mxLbBaseField->get_active() == -1)
+ mxLbBaseField->set_active(0);
+ SelectHdl( *mxLbBaseField ); // fills base item list, selects base item
+
+ // select base item
+ switch( rFuncData.maFieldRef.ReferenceItemType )
+ {
+ case DataPilotFieldReferenceItemType::PREVIOUS:
+ mxLbBaseItem->set_active( SC_BASEITEM_PREV_POS );
+ break;
+ case DataPilotFieldReferenceItemType::NEXT:
+ mxLbBaseItem->set_active( SC_BASEITEM_NEXT_POS );
+ break;
+ default:
+ {
+ if( mbEmptyItem && rFuncData.maFieldRef.ReferenceItemName.isEmpty() )
+ {
+ // select special "(empty)" entry added before other items
+ mxLbBaseItem->set_active( SC_BASEITEM_USER_POS );
+ }
+ else
+ {
+ sal_Int32 nStartPos = mbEmptyItem ? (SC_BASEITEM_USER_POS + 1) : SC_BASEITEM_USER_POS;
+ sal_Int32 nPos = FindBaseItemPos( rFuncData.maFieldRef.ReferenceItemName, nStartPos );
+ if( nPos == -1)
+ nPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
+ mxLbBaseItem->set_active( nPos );
+ }
+ }
+ }
+}
+
+const OUString& ScDPFunctionDlg::GetBaseFieldName(const OUString& rLayoutName) const
+{
+ NameMapType::const_iterator itr = maBaseFieldNameMap.find(rLayoutName);
+ return itr == maBaseFieldNameMap.end() ? rLayoutName : itr->second;
+}
+
+const OUString& ScDPFunctionDlg::GetBaseItemName(const OUString& rLayoutName) const
+{
+ NameMapType::const_iterator itr = maBaseItemNameMap.find(rLayoutName);
+ return itr == maBaseItemNameMap.end() ? rLayoutName : itr->second;
+}
+
+sal_Int32 ScDPFunctionDlg::FindBaseItemPos( std::u16string_view rEntry, sal_Int32 nStartPos ) const
+{
+ sal_Int32 nPos = nStartPos;
+ bool bFound = false;
+ while (nPos < mxLbBaseItem->get_count())
+ {
+ // translate the displayed field name back to its original field name.
+ const OUString& rInName = mxLbBaseItem->get_text(nPos);
+ const OUString& rName = GetBaseItemName(rInName);
+ if (rName == rEntry)
+ {
+ bFound = true;
+ break;
+ }
+ ++nPos;
+ }
+ return bFound ? nPos : -1;
+}
+
+IMPL_LINK( ScDPFunctionDlg, SelectHdl, weld::ComboBox&, rLBox, void )
+{
+ if (&rLBox == mxLbType.get())
+ {
+ bool bEnableField, bEnableItem;
+ switch (ToDataPilotFieldReferenceType(mxLbType->get_active()))
+ {
+ case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
+ case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
+ bEnableField = bEnableItem = true;
+ break;
+
+ case DataPilotFieldReferenceType::RUNNING_TOTAL:
+ bEnableField = true;
+ bEnableItem = false;
+ break;
+
+ default:
+ bEnableField = bEnableItem = false;
+ }
+
+ bEnableField &= (mxLbBaseField->get_count() > 0);
+ mxFtBaseField->set_sensitive( bEnableField );
+ mxLbBaseField->set_sensitive( bEnableField );
+
+ bEnableItem &= bEnableField;
+ mxFtBaseItem->set_sensitive( bEnableItem );
+ mxLbBaseItem->set_sensitive( bEnableItem );
+ }
+ else if (&rLBox == mxLbBaseField.get())
+ {
+ // keep "previous" and "next" entries
+ while (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS)
+ mxLbBaseItem->remove(SC_BASEITEM_USER_POS);
+
+ // update item list for current base field
+ mbEmptyItem = false;
+ size_t nBasePos = mxLbBaseField->get_active();
+ if (nBasePos < mrLabelVec.size())
+ {
+ const vector<ScDPLabelData::Member>& rMembers = mrLabelVec[nBasePos]->maMembers;
+ mbEmptyItem = lclFillListBox(*mxLbBaseItem, rMembers, SC_BASEITEM_USER_POS);
+ // build cache for base names.
+ NameMapType aMap;
+ for (const auto& rMember : rMembers)
+ aMap.emplace(rMember.getDisplayName(), rMember.maName);
+ maBaseItemNameMap.swap(aMap);
+ }
+
+ // select base item
+ sal_uInt16 nItemPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
+ mxLbBaseItem->set_active( nItemPos );
+ }
+}
+
+IMPL_LINK(ScDPFunctionDlg, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (&rButton == mxBtnOk.get())
+ response(RET_OK);
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScDPFunctionDlg, DblClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+ScDPSubtotalDlg::ScDPSubtotalDlg(weld::Widget* pParent, ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData,
+ const ScDPNameVec& rDataFields, bool bEnableLayout)
+ : GenericDialogController(pParent, "modules/scalc/ui/pivotfielddialog.ui", "PivotFieldDialog")
+ , mrDPObj(rDPObj)
+ , mrDataFields(rDataFields)
+ , maLabelData(rLabelData)
+ , mbEnableLayout(bEnableLayout)
+ , mxRbNone(m_xBuilder->weld_radio_button("none"))
+ , mxRbAuto(m_xBuilder->weld_radio_button("auto"))
+ , mxRbUser(m_xBuilder->weld_radio_button("user"))
+ , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view("functions")))
+ , mxFtName(m_xBuilder->weld_label("name"))
+ , mxCbShowAll(m_xBuilder->weld_check_button("showall"))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mxBtnOptions(m_xBuilder->weld_button("options"))
+{
+ mxLbFunc->set_selection_mode(SelectionMode::Multiple);
+ mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
+ Init(rLabelData, rFuncData);
+}
+
+ScDPSubtotalDlg::~ScDPSubtotalDlg()
+{
+ CloseSubdialog();
+}
+
+void ScDPSubtotalDlg::CloseSubdialog()
+{
+ if (mxOptionsDlg && mxOptionsDlg->getDialog())
+ {
+ mxOptionsDlg->getDialog()->response(RET_CANCEL);
+ mxOptionsDlg = nullptr;
+ }
+}
+
+PivotFunc ScDPSubtotalDlg::GetFuncMask() const
+{
+ PivotFunc nFuncMask = PivotFunc::NONE;
+
+ if (mxRbAuto->get_active())
+ nFuncMask = PivotFunc::Auto;
+ else if (mxRbUser->get_active())
+ nFuncMask = mxLbFunc->GetSelection();
+
+ return nFuncMask;
+}
+
+void ScDPSubtotalDlg::FillLabelData( ScDPLabelData& rLabelData ) const
+{
+ rLabelData.mnFuncMask = GetFuncMask();
+ rLabelData.mnUsedHier = maLabelData.mnUsedHier;
+ rLabelData.mbShowAll = mxCbShowAll->get_active();
+ rLabelData.maMembers = maLabelData.maMembers;
+ rLabelData.maSortInfo = maLabelData.maSortInfo;
+ rLabelData.maLayoutInfo = maLabelData.maLayoutInfo;
+ rLabelData.maShowInfo = maLabelData.maShowInfo;
+ rLabelData.mbRepeatItemLabels = maLabelData.mbRepeatItemLabels;
+}
+
+void ScDPSubtotalDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
+{
+ mxBtnOk->connect_clicked( LINK( this, ScDPSubtotalDlg, ButtonClicked ) );
+ mxBtnCancel->connect_clicked( LINK( this, ScDPSubtotalDlg, ButtonClicked ) );
+
+ // field name
+ mxFtName->set_label(rLabelData.getDisplayName());
+
+ // radio buttons
+ mxRbNone->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
+ mxRbAuto->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
+ mxRbUser->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
+
+ weld::RadioButton* pRBtn = nullptr;
+ switch( rFuncData.mnFuncMask )
+ {
+ case PivotFunc::NONE: pRBtn = mxRbNone.get(); break;
+ case PivotFunc::Auto: pRBtn = mxRbAuto.get(); break;
+ default: pRBtn = mxRbUser.get();
+ }
+ pRBtn->set_active(true);
+ RadioClickHdl(*pRBtn);
+
+ // list box
+ mxLbFunc->SetSelection( rFuncData.mnFuncMask );
+ mxLbFunc->connect_row_activated( LINK( this, ScDPSubtotalDlg, DblClickHdl ) );
+
+ // show all
+ mxCbShowAll->set_active( rLabelData.mbShowAll );
+
+ // options
+ mxBtnOptions->connect_clicked( LINK( this, ScDPSubtotalDlg, ClickHdl ) );
+}
+
+IMPL_LINK(ScDPSubtotalDlg, ButtonClicked, weld::Button&, rButton, void)
+{
+ CloseSubdialog();
+
+ if (&rButton == mxBtnOk.get())
+ response(RET_OK);
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScDPSubtotalDlg, RadioClickHdl, weld::Toggleable&, rBtn, void)
+{
+ if (!rBtn.get_active())
+ return;
+ mxLbFunc->set_sensitive(mxRbUser->get_active());
+}
+
+IMPL_LINK_NOARG(ScDPSubtotalDlg, DblClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+IMPL_LINK(ScDPSubtotalDlg, ClickHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnOptions.get())
+ {
+ mxOptionsDlg = std::make_shared<ScDPSubtotalOptDlg>(m_xDialog.get(), mrDPObj, maLabelData, mrDataFields, mbEnableLayout);
+
+ weld::DialogController::runAsync(mxOptionsDlg, [this](int nResult) {
+ if (nResult == RET_OK)
+ mxOptionsDlg->FillLabelData(maLabelData);
+ mxOptionsDlg = nullptr;
+ });
+ }
+}
+
+namespace
+{
+ int FromDataPilotFieldLayoutMode(int eMode)
+ {
+ switch (eMode)
+ {
+ case DataPilotFieldLayoutMode::TABULAR_LAYOUT:
+ return 0;
+ case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP:
+ return 1;
+ case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
+ return 2;
+ case DataPilotFieldLayoutMode::COMPACT_LAYOUT:
+ return 3;
+ }
+ return -1;
+ }
+
+ int ToDataPilotFieldLayoutMode(int nPos)
+ {
+ switch (nPos)
+ {
+ case 0:
+ return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ case 1:
+ return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ case 2:
+ return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ case 3:
+ return DataPilotFieldLayoutMode::COMPACT_LAYOUT;
+ }
+ return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ }
+
+ int FromDataPilotFieldShowItemsMode(int eMode)
+ {
+ switch (eMode)
+ {
+ case DataPilotFieldShowItemsMode::FROM_TOP:
+ return 0;
+ case DataPilotFieldShowItemsMode::FROM_BOTTOM:
+ return 1;
+ }
+ return -1;
+ }
+
+ int ToDataPilotFieldShowItemsMode(int nPos)
+ {
+ switch (nPos)
+ {
+ case 0:
+ return DataPilotFieldShowItemsMode::FROM_TOP;
+ case 1:
+ return DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ }
+ return DataPilotFieldShowItemsMode::FROM_TOP;
+ }
+}
+
+ScDPSubtotalOptDlg::ScDPSubtotalOptDlg(weld::Window* pParent, ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData, const ScDPNameVec& rDataFields,
+ bool bEnableLayout )
+ : GenericDialogController(pParent, "modules/scalc/ui/datafieldoptionsdialog.ui",
+ "DataFieldOptionsDialog")
+ , m_xLbSortBy(m_xBuilder->weld_combo_box("sortby"))
+ , m_xRbSortAsc(m_xBuilder->weld_radio_button("ascending"))
+ , m_xRbSortDesc(m_xBuilder->weld_radio_button("descending"))
+ , m_xRbSortMan(m_xBuilder->weld_radio_button("manual"))
+ , m_xLayoutFrame(m_xBuilder->weld_widget("layoutframe"))
+ , m_xLbLayout(m_xBuilder->weld_combo_box("layout"))
+ , m_xCbLayoutEmpty(m_xBuilder->weld_check_button("emptyline"))
+ , m_xCbRepeatItemLabels(m_xBuilder->weld_check_button("repeatitemlabels"))
+ , m_xCbShow(m_xBuilder->weld_check_button("show"))
+ , m_xNfShow(m_xBuilder->weld_spin_button("items"))
+ , m_xFtShow(m_xBuilder->weld_label("showft"))
+ , m_xFtShowFrom(m_xBuilder->weld_label("showfromft"))
+ , m_xLbShowFrom(m_xBuilder->weld_combo_box("from"))
+ , m_xFtShowUsing(m_xBuilder->weld_label("usingft"))
+ , m_xLbShowUsing(m_xBuilder->weld_combo_box("using"))
+ , m_xHideFrame(m_xBuilder->weld_widget("hideframe"))
+ , m_xLbHide(m_xBuilder->weld_tree_view("hideitems"))
+ , m_xFtHierarchy(m_xBuilder->weld_label("hierarchyft"))
+ , m_xLbHierarchy(m_xBuilder->weld_combo_box("hierarchy"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , mrDPObj(rDPObj)
+ , maLabelData(rLabelData)
+{
+ m_xLbHide->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ m_xLbSortBy->set_size_request(m_xLbSortBy->get_approximate_digit_width() * 18, -1);
+ m_xLbHide->set_size_request(-1, m_xLbHide->get_height_rows(5));
+ Init(rDataFields, bEnableLayout);
+}
+
+ScDPSubtotalOptDlg::~ScDPSubtotalOptDlg()
+{
+}
+
+void ScDPSubtotalOptDlg::FillLabelData( ScDPLabelData& rLabelData ) const
+{
+ // *** SORTING ***
+
+ if (m_xRbSortMan->get_active())
+ rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::MANUAL;
+ else if (m_xLbSortBy->get_active() == SC_SORTNAME_POS)
+ rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::NAME;
+ else
+ rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::DATA;
+
+ ScDPName aFieldName = GetFieldName(m_xLbSortBy->get_active_text());
+ if (!aFieldName.maName.isEmpty())
+ {
+ rLabelData.maSortInfo.Field =
+ ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
+ }
+
+ if (rLabelData.maSortInfo.Mode != DataPilotFieldSortMode::MANUAL)
+ rLabelData.maSortInfo.IsAscending = m_xRbSortAsc->get_active();
+
+ // *** LAYOUT MODE ***
+
+ rLabelData.maLayoutInfo.LayoutMode = ToDataPilotFieldLayoutMode(m_xLbLayout->get_active());
+ rLabelData.maLayoutInfo.AddEmptyLines = m_xCbLayoutEmpty->get_active();
+ rLabelData.mbRepeatItemLabels = m_xCbRepeatItemLabels->get_active();
+
+ // *** AUTO SHOW ***
+
+ aFieldName = GetFieldName(m_xLbShowUsing->get_active_text());
+ if (!aFieldName.maName.isEmpty())
+ {
+ rLabelData.maShowInfo.IsEnabled = m_xCbShow->get_active();
+ rLabelData.maShowInfo.ShowItemsMode = ToDataPilotFieldShowItemsMode(m_xLbShowFrom->get_active());
+ rLabelData.maShowInfo.ItemCount = sal::static_int_cast<sal_Int32>( m_xNfShow->get_value() );
+ rLabelData.maShowInfo.DataField =
+ ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
+ }
+
+ // *** HIDDEN ITEMS ***
+
+ rLabelData.maMembers = maLabelData.maMembers;
+ int nVisCount = m_xLbHide->n_children();
+ for (int nPos = 0; nPos < nVisCount; ++nPos)
+ rLabelData.maMembers[nPos].mbVisible = m_xLbHide->get_toggle(nPos) == TRISTATE_FALSE;
+
+ // *** HIERARCHY ***
+
+ rLabelData.mnUsedHier = m_xLbHierarchy->get_active() != -1 ? m_xLbHierarchy->get_active() : 0;
+}
+
+void ScDPSubtotalOptDlg::Init( const ScDPNameVec& rDataFields, bool bEnableLayout )
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScDPSubtotalOptDlg, ButtonClicked));
+ m_xBtnCancel->connect_clicked(LINK(this, ScDPSubtotalOptDlg, ButtonClicked));
+
+ // *** SORTING ***
+
+ sal_Int32 nSortMode = maLabelData.maSortInfo.Mode;
+
+ // sort fields list box
+ m_xLbSortBy->append_text(maLabelData.getDisplayName());
+
+ for( const auto& rDataField : rDataFields )
+ {
+ // Cache names for later lookup.
+ maDataFieldNameMap.emplace(rDataField.maLayoutName, rDataField);
+
+ m_xLbSortBy->append_text(rDataField.maLayoutName);
+ m_xLbShowUsing->append_text(rDataField.maLayoutName); // for AutoShow
+ }
+
+ sal_Int32 nSortPos = SC_SORTNAME_POS;
+ if( nSortMode == DataPilotFieldSortMode::DATA )
+ {
+ nSortPos = FindListBoxEntry( *m_xLbSortBy, maLabelData.maSortInfo.Field, SC_SORTDATA_POS );
+ if( nSortPos == -1 )
+ {
+ nSortPos = SC_SORTNAME_POS;
+ nSortMode = DataPilotFieldSortMode::MANUAL;
+ }
+ }
+ m_xLbSortBy->set_active(nSortPos);
+
+ weld::RadioButton* pRBtn = nullptr;
+ switch( nSortMode )
+ {
+ case DataPilotFieldSortMode::NONE:
+ case DataPilotFieldSortMode::MANUAL:
+ pRBtn = m_xRbSortMan.get();
+ break;
+ default:
+ pRBtn = maLabelData.maSortInfo.IsAscending ? m_xRbSortAsc.get() : m_xRbSortDesc.get();
+ }
+ pRBtn->set_active(true);
+
+ // *** LAYOUT MODE ***
+
+ m_xLayoutFrame->set_sensitive(bEnableLayout);
+
+ m_xLbLayout->set_active(FromDataPilotFieldLayoutMode(maLabelData.maLayoutInfo.LayoutMode));
+ m_xCbLayoutEmpty->set_active( maLabelData.maLayoutInfo.AddEmptyLines );
+ m_xCbRepeatItemLabels->set_active( maLabelData.mbRepeatItemLabels );
+
+ // *** AUTO SHOW ***
+
+ m_xCbShow->set_active( maLabelData.maShowInfo.IsEnabled );
+ m_xCbShow->connect_toggled( LINK( this, ScDPSubtotalOptDlg, CheckHdl ) );
+
+ m_xLbShowFrom->set_active(FromDataPilotFieldShowItemsMode(maLabelData.maShowInfo.ShowItemsMode));
+ tools::Long nCount = static_cast< tools::Long >( maLabelData.maShowInfo.ItemCount );
+ if( nCount < 1 )
+ nCount = SC_SHOW_DEFAULT;
+ m_xNfShow->set_value( nCount );
+
+ // m_xLbShowUsing already filled above
+ m_xLbShowUsing->set_active_text(maLabelData.maShowInfo.DataField);
+ if (m_xLbShowUsing->get_active() == -1)
+ m_xLbShowUsing->set_active(0);
+
+ CheckHdl(*m_xCbShow); // enable/disable dependent controls
+
+ // *** HIDDEN ITEMS ***
+
+ InitHideListBox();
+
+ // *** HIERARCHY ***
+
+ if( maLabelData.maHiers.getLength() > 1 )
+ {
+ lclFillListBox(*m_xLbHierarchy, maLabelData.maHiers);
+ sal_Int32 nHier = maLabelData.mnUsedHier;
+ if( (nHier < 0) || (nHier >= maLabelData.maHiers.getLength()) ) nHier = 0;
+ m_xLbHierarchy->set_active( nHier );
+ m_xLbHierarchy->connect_changed( LINK( this, ScDPSubtotalOptDlg, SelectHdl ) );
+ }
+ else
+ {
+ m_xFtHierarchy->set_sensitive(false);
+ m_xLbHierarchy->set_sensitive(false);
+ }
+}
+
+void ScDPSubtotalOptDlg::InitHideListBox()
+{
+ m_xLbHide->clear();
+ lclFillListBox(*m_xLbHide, maLabelData.maMembers);
+ size_t n = maLabelData.maMembers.size();
+ for (size_t i = 0; i < n; ++i)
+ m_xLbHide->set_toggle(i, maLabelData.maMembers[i].mbVisible ? TRISTATE_FALSE : TRISTATE_TRUE);
+ bool bEnable = m_xLbHide->n_children() > 0;
+ m_xHideFrame->set_sensitive(bEnable);
+}
+
+ScDPName ScDPSubtotalOptDlg::GetFieldName(const OUString& rLayoutName) const
+{
+ NameMapType::const_iterator itr = maDataFieldNameMap.find(rLayoutName);
+ return itr == maDataFieldNameMap.end() ? ScDPName() : itr->second;
+}
+
+sal_Int32 ScDPSubtotalOptDlg::FindListBoxEntry(
+ const weld::ComboBox& rLBox, std::u16string_view rEntry, sal_Int32 nStartPos ) const
+{
+ sal_Int32 nPos = nStartPos;
+ bool bFound = false;
+ while (nPos < rLBox.get_count())
+ {
+ // translate the displayed field name back to its original field name.
+ ScDPName aName = GetFieldName(rLBox.get_text(nPos));
+ OUString aUnoName = ScDPUtil::createDuplicateDimensionName(aName.maName, aName.mnDupCount);
+ if (aUnoName == rEntry)
+ {
+ bFound = true;
+ break;
+ }
+ ++nPos;
+ }
+ return bFound ? nPos : -1;
+}
+
+IMPL_LINK(ScDPSubtotalOptDlg, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xBtnOk.get())
+ response(RET_OK);
+ else
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScDPSubtotalOptDlg, CheckHdl, weld::Toggleable&, rCBox, void)
+{
+ if (&rCBox == m_xCbShow.get())
+ {
+ bool bEnable = m_xCbShow->get_active();
+ m_xNfShow->set_sensitive( bEnable );
+ m_xFtShow->set_sensitive( bEnable );
+ m_xFtShowFrom->set_sensitive( bEnable );
+ m_xLbShowFrom->set_sensitive( bEnable );
+
+ bool bEnableUsing = bEnable && (m_xLbShowUsing->get_count() > 0);
+ m_xFtShowUsing->set_sensitive(bEnableUsing);
+ m_xLbShowUsing->set_sensitive(bEnableUsing);
+ }
+}
+
+IMPL_LINK_NOARG(ScDPSubtotalOptDlg, SelectHdl, weld::ComboBox&, void)
+{
+ mrDPObj.GetMembers(maLabelData.mnCol, m_xLbHierarchy->get_active(), maLabelData.maMembers);
+ InitHideListBox();
+}
+
+ScDPShowDetailDlg::ScDPShowDetailDlg(weld::Window* pParent, ScDPObject& rDPObj, css::sheet::DataPilotFieldOrientation nOrient)
+ : GenericDialogController(pParent, "modules/scalc/ui/showdetaildialog.ui", "ShowDetail")
+ , mrDPObj(rDPObj)
+ , mxLbDims(m_xBuilder->weld_tree_view("dimsTreeview"))
+{
+ ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ tools::Long nDimCount = rDPObj.GetDimCount();
+ for (tools::Long nDim=0; nDim<nDimCount; nDim++)
+ {
+ bool bIsDataLayout;
+ sal_Int32 nDimFlags = 0;
+ OUString aName = rDPObj.GetDimName( nDim, bIsDataLayout, &nDimFlags );
+ if ( !bIsDataLayout && !rDPObj.IsDuplicated( nDim ) && ScDPObject::IsOrientationAllowed( nOrient, nDimFlags ) )
+ {
+ const ScDPSaveDimension* pDimension = pSaveData ? pSaveData->GetExistingDimensionByName(aName) : nullptr;
+ if ( !pDimension || (pDimension->GetOrientation() != nOrient) )
+ {
+ if (pDimension)
+ {
+ const std::optional<OUString> & pLayoutName = pDimension->GetLayoutName();
+ if (pLayoutName)
+ aName = *pLayoutName;
+ }
+ mxLbDims->append_text(aName);
+ maNameIndexMap.emplace(aName, nDim);
+ }
+ }
+ }
+ if (mxLbDims->n_children())
+ mxLbDims->select(0);
+
+ mxLbDims->connect_row_activated(LINK(this, ScDPShowDetailDlg, DblClickHdl));
+}
+
+ScDPShowDetailDlg::~ScDPShowDetailDlg()
+{
+}
+
+short ScDPShowDetailDlg::run()
+{
+ return mxLbDims->n_children() ? GenericDialogController::run() : static_cast<short>(RET_CANCEL);
+}
+
+OUString ScDPShowDetailDlg::GetDimensionName() const
+{
+ // Look up the internal dimension name which may be different from the
+ // displayed field name.
+ OUString aSelectedName = mxLbDims->get_selected_text();
+ DimNameIndexMap::const_iterator itr = maNameIndexMap.find(aSelectedName);
+ if (itr == maNameIndexMap.end())
+ // This should never happen!
+ return aSelectedName;
+
+ tools::Long nDim = itr->second;
+ bool bIsDataLayout = false;
+ return mrDPObj.GetDimName(nDim, bIsDataLayout);
+}
+
+IMPL_LINK_NOARG(ScDPShowDetailDlg, DblClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/scendlg.cxx b/sc/source/ui/dbgui/scendlg.cxx
new file mode 100644
index 0000000000..543914e146
--- /dev/null
+++ b/sc/source/ui/dbgui/scendlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <comphelper/string.hxx>
+#include <svx/colorbox.hxx>
+#include <unotools/useroptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <global.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <scendlg.hxx>
+
+ScNewScenarioDlg::ScNewScenarioDlg(weld::Window* pParent, const OUString& rName, bool bEdit, bool bSheetProtected)
+ : GenericDialogController(pParent, "modules/scalc/ui/scenariodialog.ui", "ScenarioDialog")
+ , aDefScenarioName(rName)
+ , bIsEdit(bEdit)
+ , m_xEdName(m_xBuilder->weld_entry("name"))
+ , m_xEdComment(m_xBuilder->weld_text_view("comment"))
+ , m_xCbShowFrame(m_xBuilder->weld_check_button("showframe"))
+ , m_xLbColor(new ColorListBox(m_xBuilder->weld_menu_button("bordercolor"), [this] { return m_xDialog.get(); }))
+ , m_xCbTwoWay(m_xBuilder->weld_check_button("copyback"))
+ , m_xCbCopyAll(m_xBuilder->weld_check_button("copysheet"))
+ , m_xCbProtect(m_xBuilder->weld_check_button("preventchanges"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ , m_xCreatedFt(m_xBuilder->weld_label("createdft"))
+ , m_xOnFt(m_xBuilder->weld_label("onft"))
+{
+ m_xEdComment->set_size_request(m_xEdComment->get_approximate_digit_width() * 60,
+ m_xEdComment->get_height_rows(6));
+
+ if (bIsEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+
+ SvtUserOptions aUserOpt;
+
+ OUString sCreatedBy(m_xCreatedFt->get_label());
+ OUString sOn(m_xOnFt->get_label());
+
+ OUString aComment(sCreatedBy + " " + aUserOpt.GetFirstName() + " " +aUserOpt.GetLastName()
+ + ", " + sOn + " " + ScGlobal::getLocaleData().getDate(Date(Date::SYSTEM))
+ + ", " + ScGlobal::getLocaleData().getTime(tools::Time(tools::Time::SYSTEM)));
+
+ m_xEdComment->set_text(aComment);
+ m_xEdName->set_text(rName);
+ m_xBtnOk->connect_clicked(LINK(this, ScNewScenarioDlg, OkHdl));
+ m_xCbShowFrame->connect_toggled(LINK(this, ScNewScenarioDlg, EnableHdl));
+
+ m_xLbColor->SelectEntry( COL_LIGHTGRAY );
+ m_xCbShowFrame->set_active(true);
+ m_xCbTwoWay->set_active(true);
+ m_xCbCopyAll->set_active(false);
+ m_xCbProtect->set_active(true);
+
+ if (bIsEdit)
+ m_xCbCopyAll->set_active(false);
+ // If the Sheet is protected then we disable the Scenario Protect input
+ // and default it to true above. Note we are in 'Add' mode here as: if
+ // Sheet && scenario protection are true, then we cannot edit this dialog.
+ if (bSheetProtected)
+ m_xCbProtect->set_active(false);
+}
+
+ScNewScenarioDlg::~ScNewScenarioDlg()
+{
+}
+
+void ScNewScenarioDlg::GetScenarioData( OUString& rName, OUString& rComment,
+ Color& rColor, ScScenarioFlags& rFlags ) const
+{
+ rComment = m_xEdComment->get_text();
+ rName = m_xEdName->get_text();
+
+ if (rName.isEmpty())
+ rName = aDefScenarioName;
+
+ rColor = m_xLbColor->GetSelectEntryColor();
+ ScScenarioFlags nBits = ScScenarioFlags::NONE;
+ if (m_xCbShowFrame->get_active())
+ nBits |= ScScenarioFlags::ShowFrame;
+ if (m_xCbTwoWay->get_active())
+ nBits |= ScScenarioFlags::TwoWay;
+ if (m_xCbCopyAll->get_active())
+ nBits |= ScScenarioFlags::CopyAll;
+ if (m_xCbProtect->get_active())
+ nBits |= ScScenarioFlags::Protected;
+ rFlags = nBits;
+}
+
+void ScNewScenarioDlg::SetScenarioData(const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags)
+{
+ m_xEdComment->set_text(rComment);
+ m_xEdName->set_text(rName);
+ m_xLbColor->SelectEntry(rColor);
+
+ m_xCbShowFrame->set_active( (nFlags & ScScenarioFlags::ShowFrame) != ScScenarioFlags::NONE );
+ EnableHdl(*m_xCbShowFrame);
+ m_xCbTwoWay->set_active( (nFlags & ScScenarioFlags::TwoWay) != ScScenarioFlags::NONE );
+ // not CopyAll
+ m_xCbProtect->set_active( (nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE );
+}
+
+IMPL_LINK_NOARG(ScNewScenarioDlg, OkHdl, weld::Button&, void)
+{
+ OUString aName = comphelper::string::strip(m_xEdName->get_text(), ' ');
+ ScDocument& rDoc = static_cast<ScTabViewShell*>(SfxViewShell::Current())->GetViewData().GetDocument();
+
+ m_xEdName->set_text(aName);
+
+ if ( !ScDocument::ValidTabName( aName ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_INVALIDTABNAME)));
+ xInfoBox->run();
+ m_xEdName->grab_focus();
+ }
+ else if ( !bIsEdit && !rDoc.ValidNewTabName( aName ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_NEWTABNAMENOTUNIQUE)));
+ xInfoBox->run();
+ m_xEdName->grab_focus();
+ }
+ else
+ m_xDialog->response(RET_OK);
+
+ //! when editing, test whether another table has the name!
+}
+
+IMPL_LINK(ScNewScenarioDlg, EnableHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox == m_xCbShowFrame.get())
+ m_xLbColor->set_sensitive(m_xCbShowFrame->get_active());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/scuiasciiopt.cxx b/sc/source/ui/dbgui/scuiasciiopt.cxx
new file mode 100644
index 0000000000..601323a658
--- /dev/null
+++ b/sc/source/ui/dbgui/scuiasciiopt.cxx
@@ -0,0 +1,975 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svx/txencbox.hxx>
+
+#include <global.hxx>
+#include <scresid.hxx>
+#include <impex.hxx>
+#include <scuiasciiopt.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <csvtablebox.hxx>
+#include <osl/thread.h>
+#include <unotools/transliterationwrapper.hxx>
+
+#include <optutil.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <miscuno.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <unicode/ucsdet.h>
+
+//! TODO make dynamic
+const SCSIZE ASCIIDLG_MAXROWS = MAXROWCOUNT;
+
+// Maximum number of source lines to concatenate while generating the preview
+// for one logical line. This may result in a wrong preview if the actual
+// number of embedded line feeds is greater, but a number too high would take
+// too much time (loop excessively if unlimited and large data) if none of the
+// selected separators are actually used in data but a field at start of line
+// is quoted.
+constexpr sal_uInt32 kMaxEmbeddedLinefeeds = 500;
+
+using namespace com::sun::star::uno;
+
+namespace {
+
+// Defines - CSV Import Preserve Options
+// For usage of index order see lcl_CreatePropertiesNames() below.
+enum CSVImportOptionsIndex
+{
+ CSVIO_MergeDelimiters = 0,
+ CSVIO_Separators,
+ CSVIO_TextSeparators,
+ CSVIO_FixedWidth,
+ CSVIO_RemoveSpace,
+ CSVIO_EvaluateFormulas,
+ // Settings for *all* dialog invocations above.
+ // Settings not for SC_TEXTTOCOLUMNS below.
+ CSVIO_FromRow,
+ CSVIO_Text2ColSkipEmptyCells = CSVIO_FromRow,
+ CSVIO_CharSet,
+ CSVIO_QuotedAsText,
+ CSVIO_DetectSpecialNum,
+ CSVIO_DetectScientificNum,
+ CSVIO_Language,
+ // Plus one not for SC_IMPORTFILE.
+ CSVIO_PasteSkipEmptyCells
+};
+
+}
+
+// Config items for all three paths are defined in
+// officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+// If not, options are neither loaded nor saved.
+const ::std::vector<OUString> CSVImportOptionNames =
+{
+ "MergeDelimiters",
+ "Separators",
+ "TextSeparators",
+ "FixedWidth",
+ "RemoveSpace",
+ "EvaluateFormulas",
+ "FromRow",
+ "CharSet",
+ "QuotedFieldAsText",
+ "DetectSpecialNumbers",
+ "DetectScientificNumbers",
+ "Language",
+ "SkipEmptyCells"
+};
+constexpr OUStringLiteral aSep_Path = u"Office.Calc/Dialogs/CSVImport";
+constexpr OUStringLiteral aSep_Path_Clpbrd = u"Office.Calc/Dialogs/ClipboardTextImport";
+constexpr OUStringLiteral aSep_Path_Text2Col = u"Office.Calc/Dialogs/TextToColumnsImport";
+
+namespace {
+CSVImportOptionsIndex getSkipEmptyCellsIndex( ScImportAsciiCall eCall )
+{
+ return eCall == SC_TEXTTOCOLUMNS ? CSVIO_Text2ColSkipEmptyCells : CSVIO_PasteSkipEmptyCells;
+}
+}
+
+static void lcl_FillCombo(weld::ComboBox& rCombo, std::u16string_view rList, sal_Unicode cSelect)
+{
+ OUString aStr;
+ if (!rList.empty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ const OUString sEntry {o3tl::getToken(rList, 0, '\t', nIdx)};
+ rCombo.append_text(sEntry);
+ if (nIdx>0 && static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nIdx))) == cSelect)
+ aStr = sEntry;
+ }
+ while (nIdx>0);
+ }
+
+ if ( cSelect )
+ {
+ if (aStr.isEmpty())
+ aStr = OUString(cSelect); // Ascii
+
+ rCombo.set_entry_text(aStr);
+ }
+}
+
+static sal_Unicode lcl_CharFromCombo(const weld::ComboBox& rCombo, std::u16string_view rList)
+{
+ sal_Unicode c = 0;
+ OUString aStr = rCombo.get_active_text();
+ if ( !aStr.isEmpty() && !rList.empty() )
+ {
+ sal_Int32 nIdx {0};
+ OUString sToken {o3tl::getToken(rList, 0, '\t', nIdx)};
+ while (nIdx>0)
+ {
+ if ( ScGlobal::GetTransliteration().isEqual( aStr, sToken ) )
+ {
+ sal_Int32 nTmpIdx {nIdx};
+ c = static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nTmpIdx)));
+ }
+ // Skip to next token at even position
+ sToken = o3tl::getToken(rList, 1, '\t', nIdx);
+ }
+ if (!c)
+ {
+ sal_Unicode cFirst = aStr[0];
+ // #i24235# first try the first character of the string directly
+ if( (aStr.getLength() == 1) || (cFirst < '0') || (cFirst > '9') )
+ c = cFirst;
+ else // keep old behaviour for compatibility (i.e. "39" -> "'")
+ c = static_cast<sal_Unicode>(aStr.toInt32()); // Ascii
+ }
+ }
+ return c;
+}
+
+static void lcl_CreatePropertiesNames ( OUString& rSepPath, Sequence<OUString>& rNames, ScImportAsciiCall eCall )
+{
+ sal_Int32 nProperties = 0;
+
+ switch(eCall)
+ {
+ case SC_IMPORTFILE:
+ rSepPath = aSep_Path;
+ nProperties = 12;
+ break;
+ case SC_PASTETEXT:
+ rSepPath = aSep_Path_Clpbrd;
+ nProperties = 13;
+ break;
+ case SC_TEXTTOCOLUMNS:
+ default:
+ rSepPath = aSep_Path_Text2Col;
+ nProperties = 7;
+ break;
+ }
+ rNames.realloc( nProperties );
+ OUString* pNames = rNames.getArray();
+ pNames[ CSVIO_MergeDelimiters ] = CSVImportOptionNames[ CSVIO_MergeDelimiters ];
+ pNames[ CSVIO_Separators ] = CSVImportOptionNames[ CSVIO_Separators ];
+ pNames[ CSVIO_TextSeparators ] = CSVImportOptionNames[ CSVIO_TextSeparators ];
+ pNames[ CSVIO_FixedWidth ] = CSVImportOptionNames[ CSVIO_FixedWidth ];
+ pNames[ CSVIO_RemoveSpace ] = CSVImportOptionNames[ CSVIO_RemoveSpace ];
+ pNames[ CSVIO_EvaluateFormulas ] = CSVImportOptionNames[ CSVIO_EvaluateFormulas ];
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ pNames[ CSVIO_FromRow ] = CSVImportOptionNames[ CSVIO_FromRow ];
+ pNames[ CSVIO_CharSet ] = CSVImportOptionNames[ CSVIO_CharSet ];
+ pNames[ CSVIO_QuotedAsText ] = CSVImportOptionNames[ CSVIO_QuotedAsText ];
+ pNames[ CSVIO_DetectSpecialNum ] = CSVImportOptionNames[ CSVIO_DetectSpecialNum ];
+ pNames[ CSVIO_DetectScientificNum ] = CSVImportOptionNames[ CSVIO_DetectScientificNum ];
+ pNames[ CSVIO_Language ] = CSVImportOptionNames[ CSVIO_Language ];
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < rNames.getLength());
+ pNames[ nSkipEmptyCells ] = CSVImportOptionNames[ CSVIO_PasteSkipEmptyCells ];
+ }
+}
+
+static void lcl_LoadSeparators( OUString& rFieldSeparators, OUString& rTextSeparators,
+ bool& rMergeDelimiters, bool& rQuotedAsText, bool& rDetectSpecialNum, bool& rDetectScientificNum,
+ bool& rFixedWidth, sal_Int32& rFromRow, sal_Int32& rCharSet,
+ sal_Int32& rLanguage, bool& rSkipEmptyCells, bool& rRemoveSpace,
+ bool& rEvaluateFormulas, ScImportAsciiCall eCall )
+{
+ Sequence<Any>aValues;
+ const Any *pProperties;
+ Sequence<OUString> aNames;
+ OUString aSepPath;
+ lcl_CreatePropertiesNames ( aSepPath, aNames, eCall);
+ ScLinkConfigItem aItem( aSepPath );
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getConstArray();
+
+ if( pProperties[ CSVIO_MergeDelimiters ].hasValue() )
+ rMergeDelimiters = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_MergeDelimiters ] );
+
+ if( pProperties[ CSVIO_RemoveSpace ].hasValue() )
+ rRemoveSpace = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_RemoveSpace ] );
+
+ if( pProperties[ CSVIO_Separators ].hasValue() )
+ pProperties[ CSVIO_Separators ] >>= rFieldSeparators;
+
+ if( pProperties[ CSVIO_TextSeparators ].hasValue() )
+ pProperties[ CSVIO_TextSeparators ] >>= rTextSeparators;
+
+ if( pProperties[ CSVIO_FixedWidth ].hasValue() )
+ rFixedWidth = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_FixedWidth ] );
+
+ if( pProperties[ CSVIO_EvaluateFormulas ].hasValue() )
+ rEvaluateFormulas = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_EvaluateFormulas ] );
+
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ if( pProperties[ CSVIO_FromRow ].hasValue() )
+ pProperties[ CSVIO_FromRow ] >>= rFromRow;
+
+ if( pProperties[ CSVIO_CharSet ].hasValue() )
+ pProperties[ CSVIO_CharSet ] >>= rCharSet;
+
+ if ( pProperties[ CSVIO_QuotedAsText ].hasValue() )
+ pProperties[ CSVIO_QuotedAsText ] >>= rQuotedAsText;
+
+ if ( pProperties[ CSVIO_DetectSpecialNum ].hasValue() )
+ pProperties[ CSVIO_DetectSpecialNum ] >>= rDetectSpecialNum;
+
+ if ( pProperties[ CSVIO_DetectScientificNum ].hasValue() )
+ pProperties[ CSVIO_DetectScientificNum ] >>= rDetectScientificNum;
+
+ if ( pProperties[ CSVIO_Language ].hasValue() )
+ pProperties[ CSVIO_Language ] >>= rLanguage;
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < aValues.getLength());
+ if ( pProperties[nSkipEmptyCells].hasValue() )
+ rSkipEmptyCells = ScUnoHelpFunctions::GetBoolFromAny( pProperties[nSkipEmptyCells] );
+ }
+}
+
+static void lcl_SaveSeparators(
+ const OUString& sFieldSeparators, const OUString& sTextSeparators, bool bMergeDelimiters, bool bQuotedAsText,
+ bool bDetectSpecialNum, bool bDetectScientificNum, bool bFixedWidth, sal_Int32 nFromRow,
+ sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, bool bEvaluateFormulas,
+ ScImportAsciiCall eCall )
+{
+ Sequence<Any> aValues;
+ Any *pProperties;
+ Sequence<OUString> aNames;
+ OUString aSepPath;
+ lcl_CreatePropertiesNames ( aSepPath, aNames, eCall );
+ ScLinkConfigItem aItem( aSepPath );
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getArray();
+
+ pProperties[ CSVIO_MergeDelimiters ] <<= bMergeDelimiters;
+ pProperties[ CSVIO_RemoveSpace ] <<= bRemoveSpace;
+ pProperties[ CSVIO_Separators ] <<= sFieldSeparators;
+ pProperties[ CSVIO_TextSeparators ] <<= sTextSeparators;
+ pProperties[ CSVIO_FixedWidth ] <<= bFixedWidth;
+ pProperties[ CSVIO_EvaluateFormulas ] <<= bEvaluateFormulas;
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ pProperties[ CSVIO_FromRow ] <<= nFromRow;
+ pProperties[ CSVIO_CharSet ] <<= nCharSet;
+ pProperties[ CSVIO_QuotedAsText ] <<= bQuotedAsText;
+ pProperties[ CSVIO_DetectSpecialNum ] <<= bDetectSpecialNum;
+ pProperties[ CSVIO_DetectScientificNum ] <<= bDetectScientificNum;
+ pProperties[ CSVIO_Language ] <<= nLanguage;
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < aValues.getLength());
+ pProperties[ nSkipEmptyCells ] <<= bSkipEmptyCells;
+ }
+
+ aItem.PutProperties(aNames, aValues);
+}
+
+ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, std::u16string_view aDatName,
+ SvStream* pInStream, ScImportAsciiCall eCall)
+ : GenericDialogController(pParent, "modules/scalc/ui/textimportcsv.ui", "TextImportCsvDialog")
+ , mpDatStream(pInStream)
+ , mnStreamPos(pInStream ? pInStream->Tell() : 0)
+ , mnRowPosCount(0)
+ , mcTextSep(ScAsciiOptions::cDefaultTextSep)
+ , meCall(eCall)
+ , mbDetectSep(eCall != SC_TEXTTOCOLUMNS)
+ , mxFtCharSet(m_xBuilder->weld_label("textcharset"))
+ , mxLbCharSet(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charset")))
+ , mxFtCustomLang(m_xBuilder->weld_label("textlanguage"))
+ , mxLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
+ , mxFtRow(m_xBuilder->weld_label("textfromrow"))
+ , mxNfRow(m_xBuilder->weld_spin_button("fromrow"))
+ , mxRbFixed(m_xBuilder->weld_radio_button("tofixedwidth"))
+ , mxRbSeparated(m_xBuilder->weld_radio_button("toseparatedby"))
+ , mxCkbTab(m_xBuilder->weld_check_button("tab"))
+ , mxCkbSemicolon(m_xBuilder->weld_check_button("semicolon"))
+ , mxCkbComma(m_xBuilder->weld_check_button("comma"))
+ , mxCkbRemoveSpace(m_xBuilder->weld_check_button("removespace"))
+ , mxCkbSpace(m_xBuilder->weld_check_button("space"))
+ , mxCkbOther(m_xBuilder->weld_check_button("other"))
+ , mxEdOther(m_xBuilder->weld_entry("inputother"))
+ , mxCkbAsOnce(m_xBuilder->weld_check_button("mergedelimiters"))
+ , mxFtTextSep(m_xBuilder->weld_label("texttextdelimiter"))
+ , mxCbTextSep(m_xBuilder->weld_combo_box("textdelimiter"))
+ , mxCkbQuotedAsText(m_xBuilder->weld_check_button("quotedfieldastext"))
+ , mxCkbDetectNumber(m_xBuilder->weld_check_button("detectspecialnumbers"))
+ , mxCkbDetectScientificNumber(m_xBuilder->weld_check_button("detectscientificnumbers"))
+ , mxCkbEvaluateFormulas(m_xBuilder->weld_check_button("evaluateformulas"))
+ , mxCkbSkipEmptyCells(m_xBuilder->weld_check_button("skipemptycells"))
+ , mxLbType(m_xBuilder->weld_combo_box("columntype"))
+ , mxAltTitle(m_xBuilder->weld_label("textalttitle"))
+ , mxTableBox(new ScCsvTableBox(*m_xBuilder))
+{
+ OUString aName = m_xDialog->get_title();
+ switch (meCall)
+ {
+ case SC_TEXTTOCOLUMNS:
+ m_xDialog->set_title(mxAltTitle->get_label());
+ break;
+ case SC_IMPORTFILE:
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ aName += OUString::Concat(" - [") + aDatName + "]";
+ m_xDialog->set_title(aName);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // To be able to prefill the correct values based on the file extension
+ bool bIsTSV = (o3tl::endsWithIgnoreAsciiCase(aDatName, ".tsv") || o3tl::endsWithIgnoreAsciiCase(aDatName, ".tab"));
+
+ // Default options are set in officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+ OUString sFieldSeparators(",;\t");
+ OUString sTextSeparators(mcTextSep);
+ bool bMergeDelimiters = false;
+ bool bFixedWidth = false;
+ bool bQuotedFieldAsText = false;
+ bool bDetectSpecialNum = true;
+ bool bDetectScientificNum = true;
+ bool bEvaluateFormulas = (meCall != SC_IMPORTFILE);
+ bool bSkipEmptyCells = true;
+ bool bRemoveSpace = false;
+ sal_Int32 nFromRow = 1;
+ sal_Int32 nCharSet = -1;
+ sal_Int32 nLanguage = 0;
+ lcl_LoadSeparators (sFieldSeparators, sTextSeparators, bMergeDelimiters,
+ bQuotedFieldAsText, bDetectSpecialNum, bDetectScientificNum, bFixedWidth, nFromRow,
+ nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, bEvaluateFormulas, meCall);
+ // load from saved settings
+ maFieldSeparators = sFieldSeparators;
+
+ if( bMergeDelimiters && !bIsTSV )
+ mxCkbAsOnce->set_active(true);
+ if (bQuotedFieldAsText)
+ mxCkbQuotedAsText->set_active(true);
+ if (bRemoveSpace)
+ mxCkbRemoveSpace->set_active(true);
+ if (bDetectSpecialNum)
+ {
+ mxCkbDetectNumber->set_active(true);
+ bDetectScientificNum = true;
+ mxCkbDetectScientificNumber->set_sensitive(false);
+ }
+ if (bDetectScientificNum)
+ mxCkbDetectScientificNumber->set_active(true);
+ if (bEvaluateFormulas)
+ mxCkbEvaluateFormulas->set_active(true);
+ if (bSkipEmptyCells)
+ mxCkbSkipEmptyCells->set_active(true);
+ if (bFixedWidth && !bIsTSV)
+ mxRbFixed->set_active(true);
+ if (nFromRow != 1)
+ mxNfRow->set_value(nFromRow);
+
+ // Clipboard is always Unicode, else detect.
+ rtl_TextEncoding ePreselectUnicode = (meCall == SC_IMPORTFILE ?
+ RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE);
+ // Sniff for Unicode / not
+ if( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW && mpDatStream )
+ {
+ mpDatStream->Seek( 0 );
+ constexpr size_t buffsize = 4096;
+ sal_Int8 bytes[buffsize] = { 0 };
+ sal_Int32 nRead = mpDatStream->ReadBytes( bytes, buffsize );
+ mpDatStream->Seek( 0 );
+
+ if ( nRead > 0 )
+ {
+ UErrorCode uerr = U_ZERO_ERROR;
+ UCharsetDetector* ucd = ucsdet_open( &uerr );
+ ucsdet_setText( ucd, reinterpret_cast<const char*>(bytes), nRead, &uerr );
+
+ if ( const UCharsetMatch* match = ucsdet_detect(ucd, &uerr) )
+ {
+ const char* pEncodingName = ucsdet_getName( match, &uerr );
+
+ if ( U_SUCCESS(uerr) && !strcmp("UTF-8", pEncodingName) )
+ {
+ ePreselectUnicode = RTL_TEXTENCODING_UTF8; // UTF-8
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UTF8 );
+ }
+ else if ( U_SUCCESS(uerr) && !strcmp("UTF-16LE", pEncodingName) )
+ {
+ ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16LE
+ mpDatStream->SetEndian( SvStreamEndian::LITTLE );
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UNICODE );
+ }
+ else if ( U_SUCCESS(uerr) && !strcmp("UTF-16BE", pEncodingName) )
+ {
+ ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16BE
+ mpDatStream->SetEndian( SvStreamEndian::BIG );
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UNICODE );
+ }
+ else // other
+ mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_DONTKNOW );
+ }
+
+ ucsdet_close( ucd );
+ }
+
+ mnStreamPos = mpDatStream->Tell();
+ }
+
+ if (bIsTSV)
+ SetSeparators('\t');
+ else
+ {
+ // Some MS-Excel convention is the first line containing the field
+ // separator as "sep=|" (without quotes and any field separator
+ // character). The second possibility seems to be it is present *with*
+ // quotes so it shows up as cell content *including* the separator and
+ // can be preserved during round trips. Check for an exact match of
+ // any such and set separator.
+ /* TODO: it is debatable whether the unquoted form should rather be
+ * treated special to actually include the separator in the field data.
+ * Currently it does not. */
+ sal_Unicode cSep = 0;
+ OUString aLine;
+ // Try to read one more character, if more than 7 it can't be an exact
+ // match of any.
+ mpDatStream->ReadUniOrByteStringLine( aLine, mpDatStream->GetStreamCharSet(), 8);
+ mpDatStream->Seek(mnStreamPos);
+ if (aLine.getLength() == 8)
+ ; // nothing
+ else if (aLine.getLength() == 5 && aLine.startsWithIgnoreAsciiCase("sep="))
+ cSep = aLine[4];
+ else if (aLine.getLength() == 7 && aLine[6] == '"' && aLine.startsWithIgnoreAsciiCase("\"sep="))
+ cSep = aLine[5];
+
+ // Set Separators in the dialog from maFieldSeparators (empty are not
+ // set) or an optionally defined by file content field separator.
+ SetSeparators(cSep);
+ }
+
+ // Get Separators from the dialog (empty are set from default)
+ maFieldSeparators = GetSeparators();
+
+ mxNfRow->connect_value_changed( LINK( this, ScImportAsciiDlg, FirstRowHdl ) );
+
+ // *** Separator characters ***
+ lcl_FillCombo( *mxCbTextSep, SCSTR_TEXTSEP, mcTextSep );
+ mxCbTextSep->set_entry_text(sTextSeparators);
+ // tdf#69207 - use selected text delimiter to parse the provided data
+ mcTextSep = lcl_CharFromCombo(*mxCbTextSep, SCSTR_TEXTSEP);
+
+ Link<weld::Toggleable&,void> aSeparatorClickHdl =LINK( this, ScImportAsciiDlg, SeparatorClickHdl );
+ Link<weld::Toggleable&,void> aOtherOptionsClickHdl =LINK( this, ScImportAsciiDlg, OtherOptionsClickHdl );
+ mxCbTextSep->connect_changed( LINK( this, ScImportAsciiDlg, SeparatorComboBoxHdl ) );
+ mxCkbTab->connect_toggled( aSeparatorClickHdl );
+ mxCkbSemicolon->connect_toggled( aSeparatorClickHdl );
+ mxCkbComma->connect_toggled( aSeparatorClickHdl );
+ mxCkbAsOnce->connect_toggled( aSeparatorClickHdl );
+ mxCkbSpace->connect_toggled( aSeparatorClickHdl );
+ mxCkbRemoveSpace->connect_toggled( aSeparatorClickHdl );
+ mxCkbOther->connect_toggled( aSeparatorClickHdl );
+ mxEdOther->connect_changed(LINK(this, ScImportAsciiDlg, SeparatorEditHdl));
+ mxCkbQuotedAsText->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbDetectNumber->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbDetectScientificNumber->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbEvaluateFormulas->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbSkipEmptyCells->connect_toggled( aOtherOptionsClickHdl );
+
+ // *** text encoding ListBox ***
+ // all encodings allowed, including Unicode, but subsets are excluded
+ mxLbCharSet->FillFromTextEncodingTable( true );
+ // Insert one "SYSTEM" entry for compatibility in AsciiOptions and system
+ // independent document linkage.
+ mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_DONTKNOW, ScResId( SCSTR_CHARSET_USER ) );
+ if ( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW )
+ {
+ rtl_TextEncoding eSystemEncoding = osl_getThreadTextEncoding();
+ // Prefer UTF-8, as UTF-16 would have already been detected from the stream.
+ // This gives a better chance that the file is going to be opened correctly.
+ if ( ( eSystemEncoding == RTL_TEXTENCODING_UNICODE ) && mpDatStream )
+ eSystemEncoding = RTL_TEXTENCODING_UTF8;
+ mxLbCharSet->SelectTextEncoding( eSystemEncoding );
+ }
+ else
+ {
+ mxLbCharSet->SelectTextEncoding( ePreselectUnicode );
+ }
+
+ if (nCharSet >= 0 && ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW)
+ mxLbCharSet->set_active(nCharSet);
+
+ SetSelectedCharSet();
+ mxLbCharSet->connect_changed( LINK( this, ScImportAsciiDlg, CharSetHdl ) );
+
+ mxLbCustomLang->SetLanguageList(
+ SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false, false);
+ mxLbCustomLang->InsertLanguage(LANGUAGE_SYSTEM);
+ mxLbCustomLang->set_active_id(static_cast<LanguageType>(nLanguage));
+
+ // *** column type ListBox ***
+ OUString aColumnUser( ScResId( SCSTR_COLUMN_USER ) );
+ for (sal_Int32 nIdx {0}; nIdx>=0; )
+ {
+ mxLbType->append_text(aColumnUser.getToken(0, ';', nIdx));
+ }
+
+ mxLbType->connect_changed( LINK( this, ScImportAsciiDlg, LbColTypeHdl ) );
+ mxLbType->set_sensitive(false);
+
+ // *** table box preview ***
+ mxTableBox->Init();
+ mxTableBox->SetUpdateTextHdl( LINK( this, ScImportAsciiDlg, UpdateTextHdl ) );
+ mxTableBox->InitTypes( *mxLbType );
+ mxTableBox->SetColTypeHdl( LINK( this, ScImportAsciiDlg, ColTypeHdl ) );
+
+ mxRbSeparated->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
+ mxRbFixed->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
+
+ SetupSeparatorCtrls();
+ RbSepFix();
+
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+
+ if (meCall == SC_TEXTTOCOLUMNS)
+ {
+ mxFtCharSet->set_sensitive(false);
+ mxLbCharSet->set_sensitive(false);
+ mxFtCustomLang->set_sensitive(false);
+ mxLbCustomLang->set_active_id(LANGUAGE_SYSTEM);
+ mxLbCustomLang->set_sensitive(false);
+ mxFtRow->set_sensitive(false);
+ mxNfRow->set_sensitive(false);
+
+ // Quoted field as text option is not used for text-to-columns mode.
+ mxCkbQuotedAsText->set_active(false);
+ mxCkbQuotedAsText->set_sensitive(false);
+
+ // Always detect special numbers for text-to-columns mode.
+ mxCkbDetectNumber->set_active(true);
+ mxCkbDetectNumber->set_sensitive(false);
+ mxCkbDetectScientificNumber->set_active(true);
+ mxCkbDetectScientificNumber->set_sensitive(false);
+ }
+ if (meCall == SC_IMPORTFILE)
+ {
+ //Empty cells in imported file are empty
+ mxCkbSkipEmptyCells->set_active(false);
+ mxCkbSkipEmptyCells->hide();
+ }
+ m_xDialog->SetInstallLOKNotifierHdl(LINK(this, ScImportAsciiDlg, InstallLOKNotifierHdl));
+}
+
+IMPL_STATIC_LINK_NOARG(ScImportAsciiDlg, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+ScImportAsciiDlg::~ScImportAsciiDlg()
+{
+}
+
+bool ScImportAsciiDlg::GetLine( sal_uLong nLine, OUString &rText, sal_Unicode& rcDetectSep )
+{
+ if (nLine >= ASCIIDLG_MAXROWS || !mpDatStream)
+ return false;
+
+ bool bRet = true;
+ bool bFixed = mxRbFixed->get_active();
+
+ if (!mpRowPosArray)
+ mpRowPosArray.reset( new sal_uLong[ASCIIDLG_MAXROWS + 2] );
+
+ if (!mnRowPosCount) // complete re-fresh
+ {
+ memset( mpRowPosArray.get(), 0, sizeof(mpRowPosArray[0]) * (ASCIIDLG_MAXROWS+2));
+
+ Seek(0);
+ mpDatStream->StartReadingUnicodeText( mpDatStream->GetStreamCharSet() );
+
+ mnStreamPos = mpDatStream->Tell();
+ mpRowPosArray[mnRowPosCount] = mnStreamPos;
+ }
+
+ if (nLine >= mnRowPosCount)
+ {
+ // need to work out some more line information
+ do
+ {
+ if (!Seek(mpRowPosArray[mnRowPosCount]) || !mpDatStream->good())
+ {
+ bRet = false;
+ break;
+ }
+ rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators,
+ mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
+ mnStreamPos = mpDatStream->Tell();
+ mpRowPosArray[++mnRowPosCount] = mnStreamPos;
+ } while (nLine >= mnRowPosCount && mpDatStream->good());
+ if (mpDatStream->eof() &&
+ mnStreamPos == mpRowPosArray[mnRowPosCount-1])
+ {
+ // the very end, not even an empty line read
+ bRet = false;
+ --mnRowPosCount;
+ }
+ }
+ else
+ {
+ Seek( mpRowPosArray[nLine]);
+ rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators, mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
+ mnStreamPos = mpDatStream->Tell();
+ }
+
+ // If the file content isn't unicode, ReadUniStringLine
+ // may try to seek beyond the file's end and cause a CANTSEEK error
+ // (depending on the stream type). The error code has to be cleared,
+ // or further read operations (including non-unicode) will fail.
+ if ( mpDatStream->GetError() == ERRCODE_IO_CANTSEEK )
+ mpDatStream->ResetError();
+
+ ScImportExport::EmbeddedNullTreatment( rText);
+
+ return bRet;
+}
+
+void ScImportAsciiDlg::GetOptions( ScAsciiOptions& rOpt )
+{
+ rOpt.SetCharSet( meCharSet );
+ rOpt.SetCharSetSystem( mbCharSetSystem );
+ rOpt.SetLanguage(mxLbCustomLang->get_active_id());
+ rOpt.SetFixedLen( mxRbFixed->get_active() );
+ rOpt.SetStartRow( mxNfRow->get_value() );
+ mxTableBox->FillColumnData( rOpt );
+ if( mxRbSeparated->get_active() )
+ {
+ rOpt.SetFieldSeps( GetSeparators() );
+ rOpt.SetMergeSeps( mxCkbAsOnce->get_active() );
+ rOpt.SetRemoveSpace( mxCkbRemoveSpace->get_active() );
+ rOpt.SetTextSep( lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP ) );
+ }
+
+ rOpt.SetQuotedAsText(mxCkbQuotedAsText->get_active());
+ rOpt.SetDetectSpecialNumber(mxCkbDetectNumber->get_active());
+ rOpt.SetDetectScientificNumber(mxCkbDetectScientificNumber->get_active());
+ rOpt.SetEvaluateFormulas(mxCkbEvaluateFormulas->get_active());
+ rOpt.SetSkipEmptyCells(mxCkbSkipEmptyCells->get_active());
+}
+
+void ScImportAsciiDlg::SaveParameters()
+{
+ lcl_SaveSeparators( maFieldSeparators, mxCbTextSep->get_active_text(), mxCkbAsOnce->get_active(),
+ mxCkbQuotedAsText->get_active(), mxCkbDetectNumber->get_active(), mxCkbDetectScientificNumber->get_active(),
+ mxRbFixed->get_active(),
+ mxNfRow->get_value(),
+ mxLbCharSet->get_active(),
+ static_cast<sal_uInt16>(mxLbCustomLang->get_active_id()),
+ mxCkbSkipEmptyCells->get_active(), mxCkbRemoveSpace->get_active(),
+ mxCkbEvaluateFormulas->get_active(), meCall );
+}
+
+void ScImportAsciiDlg::SetSeparators( sal_Unicode cSep )
+{
+ if (cSep)
+ {
+ // Exclusively set a separator, maFieldSeparators needs not be
+ // modified, it's obtained by GetSeparators() after this call.
+ constexpr sal_Unicode aSeps[] = { '\t', ';', ',', ' ' };
+ for (const sal_Unicode c : aSeps)
+ {
+ const bool bSet = (c == cSep);
+ switch (c)
+ {
+ case '\t': mxCkbTab->set_active(bSet); break;
+ case ';': mxCkbSemicolon->set_active(bSet); break;
+ case ',': mxCkbComma->set_active(bSet); break;
+ case ' ': mxCkbSpace->set_active(bSet); break;
+ }
+ if (bSet)
+ cSep = 0;
+ }
+ if (cSep)
+ {
+ mxCkbOther->set_active(true);
+ mxEdOther->set_text(OUStringChar(cSep));
+ }
+ }
+ else
+ {
+ for (sal_Int32 i = 0; i < maFieldSeparators.getLength(); ++i)
+ {
+ switch (maFieldSeparators[i])
+ {
+ case '\t': mxCkbTab->set_active(true); break;
+ case ';': mxCkbSemicolon->set_active(true); break;
+ case ',': mxCkbComma->set_active(true); break;
+ case ' ': mxCkbSpace->set_active(true); break;
+ default:
+ mxCkbOther->set_active(true);
+ mxEdOther->set_text(mxEdOther->get_text() + OUStringChar(maFieldSeparators[i]));
+ }
+ }
+ }
+}
+
+void ScImportAsciiDlg::SetSelectedCharSet()
+{
+ meCharSet = mxLbCharSet->GetSelectTextEncoding();
+ mbCharSetSystem = (meCharSet == RTL_TEXTENCODING_DONTKNOW);
+ if( mbCharSetSystem )
+ meCharSet = osl_getThreadTextEncoding();
+}
+
+OUString ScImportAsciiDlg::GetSeparators() const
+{
+ OUString aSepChars;
+ if( mxCkbTab->get_active() )
+ aSepChars += "\t";
+ if( mxCkbSemicolon->get_active() )
+ aSepChars += ";";
+ if( mxCkbComma->get_active() )
+ aSepChars += ",";
+ if( mxCkbSpace->get_active() )
+ aSepChars += " ";
+ if( mxCkbOther->get_active() )
+ aSepChars += mxEdOther->get_text();
+ return aSepChars;
+}
+
+void ScImportAsciiDlg::SetupSeparatorCtrls()
+{
+ bool bEnable = mxRbSeparated->get_active();
+ mxCkbTab->set_sensitive( bEnable );
+ mxCkbSemicolon->set_sensitive( bEnable );
+ mxCkbComma->set_sensitive( bEnable );
+ mxCkbSpace->set_sensitive( bEnable );
+ mxCkbRemoveSpace->set_sensitive( bEnable );
+ mxCkbOther->set_sensitive( bEnable );
+ mxEdOther->set_sensitive( bEnable );
+ mxCkbAsOnce->set_sensitive( bEnable );
+ mxFtTextSep->set_sensitive( bEnable );
+ mxCbTextSep->set_sensitive( bEnable );
+}
+
+void ScImportAsciiDlg::UpdateVertical()
+{
+ mnRowPosCount = 0;
+ if (mpDatStream)
+ mpDatStream->SetStreamCharSet(meCharSet);
+}
+
+void ScImportAsciiDlg::RbSepFix()
+{
+ weld::WaitObject aWaitObj(m_xDialog.get());
+ if( mxRbFixed->get_active() )
+ mxTableBox->SetFixedWidthMode();
+ else
+ mxTableBox->SetSeparatorsMode();
+ SetupSeparatorCtrls();
+}
+
+IMPL_LINK(ScImportAsciiDlg, RbSepFixHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+ RbSepFix();
+}
+
+IMPL_LINK(ScImportAsciiDlg, SeparatorClickHdl, weld::Toggleable&, rCtrl, void)
+{
+ SeparatorHdl(&rCtrl);
+}
+
+IMPL_LINK( ScImportAsciiDlg, SeparatorComboBoxHdl, weld::ComboBox&, rCtrl, void )
+{
+ SeparatorHdl(&rCtrl);
+}
+
+IMPL_LINK( ScImportAsciiDlg, SeparatorEditHdl, weld::Entry&, rEdit, void )
+{
+ SeparatorHdl(&rEdit);
+}
+
+IMPL_LINK(ScImportAsciiDlg, OtherOptionsClickHdl, weld::Toggleable&, rCtrl, void)
+{
+ if (&rCtrl == mxCkbDetectNumber.get())
+ {
+ if (mxCkbDetectNumber->get_active())
+ {
+ mxCkbDetectScientificNumber->set_active(true);
+ mxCkbDetectScientificNumber->set_sensitive(false);
+ }
+ else
+ mxCkbDetectScientificNumber->set_sensitive(true);
+ return;
+ }
+}
+
+void ScImportAsciiDlg::SeparatorHdl(const weld::Widget* pCtrl)
+{
+ OSL_ENSURE( pCtrl, "ScImportAsciiDlg::SeparatorHdl - missing sender" );
+ OSL_ENSURE( !mxRbFixed->get_active(), "ScImportAsciiDlg::SeparatorHdl - not allowed in fixed width" );
+
+ /* #i41550# First update state of the controls. The GetSeparators()
+ function needs final state of the check boxes. */
+ if (pCtrl == mxCkbOther.get() && mxCkbOther->get_active())
+ mxEdOther->grab_focus();
+ else if (pCtrl == mxEdOther.get())
+ mxCkbOther->set_active(!mxEdOther->get_text().isEmpty());
+
+ OUString aOldFldSeps( maFieldSeparators);
+ maFieldSeparators = GetSeparators();
+ sal_Unicode cOldSep = mcTextSep;
+ mcTextSep = lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP );
+ // Any separator changed may result in completely different lines due to
+ // embedded line breaks.
+ if (cOldSep != mcTextSep || aOldFldSeps != maFieldSeparators)
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+}
+
+IMPL_LINK_NOARG(ScImportAsciiDlg, CharSetHdl, weld::ComboBox&, void)
+{
+ if (mxLbCharSet->get_active() != -1)
+ {
+ weld::WaitObject aWaitObj(m_xDialog.get());
+ rtl_TextEncoding eOldCharSet = meCharSet;
+ SetSelectedCharSet();
+ // switching char-set invalidates 8bit -> String conversions
+ if (eOldCharSet != meCharSet)
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+ }
+}
+
+IMPL_LINK(ScImportAsciiDlg, FirstRowHdl, weld::SpinButton&, rNumField, void)
+{
+ mxTableBox->GetGrid().Execute( CSVCMD_SETFIRSTIMPORTLINE, rNumField.get_value() - 1);
+}
+
+IMPL_LINK(ScImportAsciiDlg, LbColTypeHdl, weld::ComboBox&, rListBox, void)
+{
+ if (&rListBox == mxLbType.get())
+ mxTableBox->GetGrid().Execute(CSVCMD_SETCOLUMNTYPE, rListBox.get_active());
+}
+
+IMPL_LINK_NOARG(ScImportAsciiDlg, UpdateTextHdl, ScCsvTableBox&, void)
+{
+ // Checking the separator can only be done once for the very first time
+ // when the dialog wasn't already presented to the user.
+ // As a side effect this has the benefit that the check is only done on the
+ // first set of visible lines.
+ mbDetectSep = (mbDetectSep && !mxRbFixed->get_active()
+ && (!mxCkbTab->get_active() || !mxCkbSemicolon->get_active()
+ || !mxCkbComma->get_active() || !mxCkbSpace->get_active()));
+ sal_Unicode cDetectSep = (mbDetectSep ? 0 : 0xffff);
+
+ sal_Int32 nBaseLine = mxTableBox->GetGrid().GetFirstVisLine();
+ sal_Int32 nRead = mxTableBox->GetGrid().GetVisLineCount();
+ // If mnRowPosCount==0, this is an initializing call, read ahead for row
+ // count and resulting scroll bar size and position to be able to scroll at
+ // all. When adding lines, read only the amount of next lines to be
+ // displayed.
+ if (!mnRowPosCount || nRead > CSV_PREVIEW_LINES)
+ nRead = CSV_PREVIEW_LINES;
+
+ sal_Int32 i;
+ for (i = 0; i < nRead; i++)
+ {
+ if (!GetLine( nBaseLine + i, maPreviewLine[i], cDetectSep))
+ break;
+ }
+ for (; i < CSV_PREVIEW_LINES; i++)
+ maPreviewLine[i].clear();
+
+ if (mbDetectSep)
+ {
+ mbDetectSep = false;
+ if (cDetectSep)
+ {
+ // Expect separator to be appended by now so all subsequent
+ // GetLine()/ReadCsvLine() actually used it.
+ assert(maFieldSeparators.endsWith(OUStringChar(cDetectSep)));
+ // Preselect separator in UI.
+ switch (cDetectSep)
+ {
+ case '\t': mxCkbTab->set_active(true); break;
+ case ';': mxCkbSemicolon->set_active(true); break;
+ case ',': mxCkbComma->set_active(true); break;
+ case ' ': mxCkbSpace->set_active(true); break;
+ }
+ }
+ }
+
+ mxTableBox->GetGrid().Execute( CSVCMD_SETLINECOUNT, mnRowPosCount);
+ bool bMergeSep = mxCkbAsOnce->get_active();
+ bool bRemoveSpace = mxCkbRemoveSpace->get_active();
+ mxTableBox->SetUniStrings( maPreviewLine, maFieldSeparators, mcTextSep, bMergeSep, bRemoveSpace );
+}
+
+IMPL_LINK( ScImportAsciiDlg, ColTypeHdl, ScCsvTableBox&, rTableBox, void )
+{
+ sal_Int32 nType = rTableBox.GetSelColumnType();
+ sal_Int32 nTypeCount = mxLbType->get_count();
+ bool bEmpty = (nType == CSV_TYPE_MULTI);
+ bool bEnable = ((0 <= nType) && (nType < nTypeCount)) || bEmpty;
+
+ mxLbType->set_sensitive( bEnable );
+
+ if (bEmpty)
+ mxLbType->set_active(-1);
+ else if (bEnable)
+ mxLbType->set_active(nType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/scuiimoptdlg.cxx b/sc/source/ui/dbgui/scuiimoptdlg.cxx
new file mode 100644
index 0000000000..6e2bebd353
--- /dev/null
+++ b/sc/source/ui/dbgui/scuiimoptdlg.cxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scuiimoptdlg.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include <osl/thread.h>
+#include <rtl/tencinfo.h>
+#include <imoptdlg.hxx>
+#include <svx/txencbox.hxx>
+#include <o3tl/string_view.hxx>
+#include <utility>
+
+// ScDelimiterTable
+
+class ScDelimiterTable
+{
+public:
+ explicit ScDelimiterTable( OUString aDelTab )
+ : theDelTab (std::move( aDelTab )),
+ nDelIdx ( 0 )
+ {}
+
+ sal_uInt16 GetCode( std::u16string_view rDelimiter ) const;
+ OUString GetDelimiter( sal_Unicode nCode ) const;
+
+ OUString FirstDel() { nDelIdx = 0; return theDelTab.getToken( 0, cSep, nDelIdx ); }
+ OUString NextDel() { return theDelTab.getToken( 1, cSep, nDelIdx ); }
+
+private:
+ const OUString theDelTab;
+ static constexpr sal_Unicode cSep {'\t'};
+ sal_Int32 nDelIdx;
+};
+
+sal_uInt16 ScDelimiterTable::GetCode( std::u16string_view rDel ) const
+{
+ if (!theDelTab.isEmpty())
+ {
+ sal_Int32 nIdx {0};
+
+ // Check even tokens: start from 0 and then skip 1 token at each iteration
+ if (rDel != o3tl::getToken(theDelTab, 0, cSep, nIdx ))
+ while (nIdx>0 && rDel != o3tl::getToken(theDelTab, 1, cSep, nIdx ));
+
+ if (nIdx>0)
+ return static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(theDelTab, 0, cSep, nIdx )));
+ }
+
+ return 0;
+}
+
+OUString ScDelimiterTable::GetDelimiter( sal_Unicode nCode ) const
+{
+ if (!theDelTab.isEmpty())
+ {
+ sal_Int32 nIdx {0};
+ // Check odd tokens: start from 1 and then skip 1 token at each iteration
+ do
+ {
+ sal_Int32 nPrevIdx {nIdx};
+ if (nCode == static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(theDelTab, 1, cSep, nIdx ))))
+ return theDelTab.getToken( 0, cSep, nPrevIdx );
+ }
+ while (nIdx>0);
+ }
+
+ return OUString();
+}
+
+void ScImportOptionsDlg::FillFromTextEncodingTable(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags)
+{
+ if (m_bIsAsciiImport)
+ m_xLbCharset->FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags);
+ else
+ m_xTvCharset->FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags);
+}
+
+void ScImportOptionsDlg::FillFromDbTextEncodingMap(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags)
+{
+ if (m_bIsAsciiImport)
+ m_xLbCharset->FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags);
+ else
+ m_xTvCharset->FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags);
+}
+
+// ScImportOptionsDlg
+ScImportOptionsDlg::ScImportOptionsDlg(weld::Window* pParent, bool bAscii,
+ const ScImportOptions* pOptions,
+ const OUString* pStrTitle,
+ bool bMultiByte, bool bOnlyDbtoolsEncodings,
+ bool bImport)
+ : GenericDialogController(pParent, "modules/scalc/ui/imoptdialog.ui", "ImOptDialog")
+ , m_bIsAsciiImport(bAscii)
+ , m_xFieldFrame(m_xBuilder->weld_frame("fieldframe"))
+ , m_xFtCharset(m_xBuilder->weld_label("charsetft"))
+ , m_xEncGrid(m_xBuilder->weld_widget("grid2"))
+ , m_xFtFieldSep(m_xBuilder->weld_label("fieldft"))
+ , m_xEdFieldSep(m_xBuilder->weld_combo_box("field"))
+ , m_xFtTextSep(m_xBuilder->weld_label("textft"))
+ , m_xEdTextSep(m_xBuilder->weld_combo_box("text"))
+ , m_xCbShown(m_xBuilder->weld_check_button("asshown"))
+ , m_xCbFormulas(m_xBuilder->weld_check_button("formulas"))
+ , m_xCbQuoteAll(m_xBuilder->weld_check_button("quoteall"))
+ , m_xCbFixed(m_xBuilder->weld_check_button("fixedwidth"))
+ , m_xLbCharset(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charsetdropdown")))
+ , m_xTvCharset(new SvxTextEncodingTreeView(m_xBuilder->weld_tree_view("charsetlist")))
+{
+ if (bAscii)
+ {
+ m_xDialog->set_help_id(m_xDialog->get_help_id() + "?config=NonTextImport");
+ m_xLbCharset->show();
+ m_xTvCharset->hide();
+ }
+ else
+ {
+ m_xTvCharset->set_size_request(-1, m_xTvCharset->get_height_rows(6));
+ m_xEncGrid->set_vexpand(true);
+ m_xLbCharset->hide();
+ m_xTvCharset->show();
+ }
+
+ OUString sFieldSep(SCSTR_FIELDSEP);
+ sFieldSep = sFieldSep.replaceFirst( "%TAB", ScResId(SCSTR_FIELDSEP_TAB) );
+ sFieldSep = sFieldSep.replaceFirst( "%SPACE", ScResId(SCSTR_FIELDSEP_SPACE) );
+
+ // not possible in the Ctor initializer (MSC cannot do that):
+ pFieldSepTab.reset( new ScDelimiterTable(sFieldSep) );
+ pTextSepTab.reset( new ScDelimiterTable(SCSTR_TEXTSEP) );
+
+ OUString aStr = pFieldSepTab->FirstDel();
+
+ while (!aStr.isEmpty())
+ {
+ m_xEdFieldSep->append_text(aStr);
+ aStr = pFieldSepTab->NextDel();
+ }
+
+ aStr = pTextSepTab->FirstDel();
+
+ while (!aStr.isEmpty())
+ {
+ m_xEdTextSep->append_text(aStr);
+ aStr = pTextSepTab->NextDel();
+ }
+
+ m_xEdFieldSep->set_active(0);
+ m_xEdTextSep->set_active(0);
+
+ if ( bOnlyDbtoolsEncodings )
+ {
+ // Even dBase export allows multibyte now
+ if ( bMultiByte )
+ FillFromDbTextEncodingMap( bImport );
+ else
+ FillFromDbTextEncodingMap( bImport, RTL_TEXTENCODING_INFO_MULTIBYTE );
+ }
+ else if ( !bAscii )
+ { //!TODO: Unicode would need work in each filter
+ if ( bMultiByte )
+ FillFromTextEncodingTable( bImport, RTL_TEXTENCODING_INFO_UNICODE );
+ else
+ FillFromTextEncodingTable( bImport, RTL_TEXTENCODING_INFO_UNICODE |
+ RTL_TEXTENCODING_INFO_MULTIBYTE );
+ }
+ else
+ {
+ if ( pOptions )
+ {
+ sal_Unicode nCode = pOptions->nFieldSepCode;
+ aStr = pFieldSepTab->GetDelimiter( nCode );
+
+ if ( aStr.isEmpty() )
+ m_xEdFieldSep->set_entry_text(OUString(nCode));
+ else
+ m_xEdFieldSep->set_entry_text(aStr);
+
+ nCode = pOptions->nTextSepCode;
+ aStr = pTextSepTab->GetDelimiter( nCode );
+
+ if ( aStr.isEmpty() )
+ m_xEdTextSep->set_entry_text(OUString(nCode));
+ else
+ m_xEdTextSep->set_entry_text(aStr);
+ }
+ // all encodings allowed, even Unicode
+ FillFromTextEncodingTable( bImport );
+ }
+
+ if( bAscii )
+ {
+ sal_Int32 nCharSet = officecfg::Office::Calc::Dialogs::CSVExport::CharSet::get();
+ OUString strFieldSeparator = officecfg::Office::Calc::Dialogs::CSVExport::FieldSeparator::get();
+ OUString strTextSeparator = officecfg::Office::Calc::Dialogs::CSVExport::TextSeparator::get();
+ bool bSaveTrueCellContent = officecfg::Office::Calc::Dialogs::CSVExport::SaveTrueCellContent::get();
+ bool bSaveCellFormulas = officecfg::Office::Calc::Dialogs::CSVExport::SaveCellFormulas::get();
+ bool bQuoteAllTextCells = officecfg::Office::Calc::Dialogs::CSVExport::QuoteAllTextCells::get();
+ bool bFixedWidth = officecfg::Office::Calc::Dialogs::CSVExport::FixedWidth::get();
+
+ m_xCbFixed->show();
+ m_xCbFixed->connect_toggled(LINK(this, ScImportOptionsDlg, FixedWidthHdl));
+ m_xCbFixed->set_active( bFixedWidth );
+ FixedWidthHdl(*m_xCbFixed);
+ m_xCbShown->show();
+ m_xCbShown->set_active( bSaveTrueCellContent );
+ m_xCbQuoteAll->show();
+ m_xCbQuoteAll->set_active( bQuoteAllTextCells );
+ m_xCbFormulas->show();
+ // default option for "save formulas" no longer taken from view shell but from persisted dialog settings
+ m_xCbFormulas->set_active( bSaveCellFormulas );
+ // if no charset, text separator or field separator exist, keep the values from dialog initialization
+ if (strFieldSeparator.getLength() > 0)
+ m_xEdFieldSep->set_entry_text(strFieldSeparator);
+ if (strTextSeparator.getLength() > 0)
+ m_xEdTextSep->set_entry_text(strTextSeparator);
+ if (nCharSet < 0 || nCharSet == RTL_TEXTENCODING_DONTKNOW )
+ m_xLbCharset->SelectTextEncoding(pOptions ? pOptions->eCharSet : osl_getThreadTextEncoding());
+ else
+ m_xLbCharset->SelectTextEncoding(nCharSet);
+ }
+ else
+ {
+ m_xFieldFrame->set_label(m_xFtCharset->get_label());
+ m_xFtFieldSep->hide();
+ m_xFtTextSep->hide();
+ m_xFtCharset->hide();
+ m_xEdFieldSep->hide();
+ m_xEdTextSep->hide();
+ m_xCbFixed->hide();
+ m_xCbShown->hide();
+ m_xCbQuoteAll->hide();
+ m_xCbFormulas->hide();
+ m_xTvCharset->grab_focus();
+ m_xTvCharset->connect_row_activated(LINK(this, ScImportOptionsDlg, DoubleClickHdl));
+ m_xTvCharset->SelectTextEncoding(pOptions ? pOptions->eCharSet : osl_getThreadTextEncoding());
+ }
+
+ // optional title:
+ if (pStrTitle)
+ m_xDialog->set_title(*pStrTitle);
+}
+
+ScImportOptionsDlg::~ScImportOptionsDlg()
+{
+}
+
+void ScImportOptionsDlg::GetImportOptions( ScImportOptions& rOptions ) const
+{
+ auto nEncoding = m_bIsAsciiImport ? m_xLbCharset->GetSelectTextEncoding() : m_xTvCharset->GetSelectTextEncoding();
+ rOptions.SetTextEncoding(nEncoding);
+
+ if (m_xCbFixed->get_visible())
+ {
+ rOptions.nFieldSepCode = GetCodeFromCombo( *m_xEdFieldSep );
+ rOptions.nTextSepCode = GetCodeFromCombo( *m_xEdTextSep );
+ rOptions.bFixedWidth = m_xCbFixed->get_active();
+ rOptions.bSaveAsShown = m_xCbShown->get_active();
+ rOptions.bQuoteAllText = m_xCbQuoteAll->get_active();
+ rOptions.bSaveFormulas = m_xCbFormulas->get_active();
+ }
+}
+
+sal_uInt16 ScImportOptionsDlg::GetCodeFromCombo(const weld::ComboBox& rEd) const
+{
+ ScDelimiterTable* pTab;
+ OUString aStr( rEd.get_active_text() );
+ sal_uInt16 nCode;
+
+ if (&rEd == m_xEdTextSep.get())
+ pTab = pTextSepTab.get();
+ else
+ pTab = pFieldSepTab.get();
+
+ if ( aStr.isEmpty() )
+ {
+ nCode = 0; // no separator
+ }
+ else
+ {
+ nCode = pTab->GetCode( aStr );
+
+ if ( nCode == 0 )
+ nCode = static_cast<sal_uInt16>(aStr[0]);
+ }
+
+ return nCode;
+}
+
+IMPL_LINK_NOARG(ScImportOptionsDlg, FixedWidthHdl, weld::Toggleable&, void)
+{
+ bool bEnable = !m_xCbFixed->get_active();
+ m_xFtFieldSep->set_sensitive( bEnable );
+ m_xEdFieldSep->set_sensitive( bEnable );
+ m_xFtTextSep->set_sensitive( bEnable );
+ m_xEdTextSep->set_sensitive( bEnable );
+ m_xCbShown->set_sensitive( bEnable );
+ m_xCbQuoteAll->set_sensitive( bEnable );
+}
+
+IMPL_LINK_NOARG(ScImportOptionsDlg, DoubleClickHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+void ScImportOptionsDlg::SaveImportOptions() const
+{
+ std::shared_ptr < comphelper::ConfigurationChanges > batch(comphelper::ConfigurationChanges::create());
+ auto nEncoding = m_bIsAsciiImport ? m_xLbCharset->GetSelectTextEncoding() : m_xTvCharset->GetSelectTextEncoding();
+ officecfg::Office::Calc::Dialogs::CSVExport::CharSet::set(nEncoding, batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::FieldSeparator::set(m_xEdFieldSep->get_active_text(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::TextSeparator::set(m_xEdTextSep->get_active_text(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::FixedWidth::set(m_xCbFixed->get_active(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::SaveCellFormulas::set(m_xCbFormulas->get_active(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::SaveTrueCellContent::set(m_xCbShown->get_active(), batch);
+ officecfg::Office::Calc::Dialogs::CSVExport::QuoteAllTextCells::set(m_xCbQuoteAll->get_active(), batch);
+ batch->commit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/sfiltdlg.cxx b/sc/source/ui/dbgui/sfiltdlg.cxx
new file mode 100644
index 0000000000..3aeb319237
--- /dev/null
+++ b/sc/source/ui/dbgui/sfiltdlg.cxx
@@ -0,0 +1,435 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/dispatch.hxx>
+
+#include <uiitems.hxx>
+#include <rangenam.hxx>
+#include <reffact.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scresid.hxx>
+
+#include <foptmgr.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+
+#include <filtdlg.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+// DEFINE --------------------------------------------------------------------
+
+namespace
+{
+ void ERRORBOX(weld::Window* pParent, TranslateId rid)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(rid)));
+ xBox->run();
+ }
+}
+
+
+ScSpecialFilterDlg::ScSpecialFilterDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet )
+
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/advancedfilterdialog.ui", "AdvancedFilterDialog")
+ , aStrUndefined ( ScResId(SCSTR_UNDEFINED) )
+ , nWhichQuery ( rArgSet.GetPool()->GetWhich( SID_QUERY ) )
+ , theQueryData ( static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery )).GetQueryData() )
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , bRefInputMode(false)
+ , m_pRefInputEdit(nullptr)
+ , m_xLbFilterArea(m_xBuilder->weld_combo_box("lbfilterarea"))
+ , m_xEdFilterArea(new formula::RefEdit(m_xBuilder->weld_entry("edfilterarea")))
+ , m_xRbFilterArea(new formula::RefButton(m_xBuilder->weld_button("rbfilterarea")))
+ , m_xExpander(m_xBuilder->weld_expander("more"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnRegExp(m_xBuilder->weld_check_button("regexp"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("header"))
+ , m_xBtnUnique(m_xBuilder->weld_check_button("unique"))
+ , m_xBtnCopyResult(m_xBuilder->weld_check_button("copyresult"))
+ , m_xLbCopyArea(m_xBuilder->weld_combo_box("lbcopyarea"))
+ , m_xEdCopyArea(new formula::RefEdit(m_xBuilder->weld_entry("edcopyarea")))
+ , m_xRbCopyArea(new formula::RefButton(m_xBuilder->weld_button("rbcopyarea")))
+ , m_xBtnDestPers(m_xBuilder->weld_check_button("destpers"))
+ , m_xFtDbAreaLabel(m_xBuilder->weld_label("dbarealabel"))
+ , m_xFtDbArea(m_xBuilder->weld_label("dbarea"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xFilterFrame(m_xBuilder->weld_frame("filterframe"))
+ , m_xFilterLabel(m_xFilterFrame->weld_label_widget())
+{
+ m_xEdFilterArea->SetReferences(this, m_xFilterLabel.get());
+ m_xRbFilterArea->SetReferences(this, m_xEdFilterArea.get());
+ m_xEdCopyArea->SetReferences(this, m_xFtDbAreaLabel.get());
+ m_xRbCopyArea->SetReferences(this, m_xEdCopyArea.get());
+
+ Init( rArgSet );
+
+ Link<formula::RefEdit&, void> aLinkEdit = LINK(this, ScSpecialFilterDlg, RefInputEditHdl);
+ Link<formula::RefButton&, void> aLinkButton = LINK(this, ScSpecialFilterDlg, RefInputButtonHdl);
+ m_xEdCopyArea->SetGetFocusHdl(aLinkEdit);
+ m_xRbCopyArea->SetGetFocusHdl(aLinkButton);
+ m_xEdFilterArea->SetGetFocusHdl(aLinkEdit);
+ m_xRbFilterArea->SetGetFocusHdl(aLinkButton);
+ m_xEdCopyArea->SetLoseFocusHdl(aLinkEdit);
+ m_xRbCopyArea->SetLoseFocusHdl(aLinkButton);
+ m_xEdFilterArea->SetLoseFocusHdl(aLinkEdit);
+ m_xRbFilterArea->SetLoseFocusHdl(aLinkButton);
+
+ m_xEdFilterArea->GrabFocus();
+}
+
+ScSpecialFilterDlg::~ScSpecialFilterDlg()
+{
+ pOptionsMgr.reset();
+
+ pOutItem.reset();
+}
+
+void ScSpecialFilterDlg::Init( const SfxItemSet& rArgSet )
+{
+ const ScQueryItem& rQueryItem = static_cast<const ScQueryItem&>(
+ rArgSet.Get( nWhichQuery ));
+
+ m_xBtnOk->connect_clicked( LINK( this, ScSpecialFilterDlg, EndDlgHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScSpecialFilterDlg, EndDlgHdl ) );
+ m_xLbFilterArea->connect_changed( LINK( this, ScSpecialFilterDlg, FilterAreaSelHdl ) );
+ m_xEdFilterArea->SetModifyHdl ( LINK( this, ScSpecialFilterDlg, FilterAreaModHdl ) );
+
+ pViewData = rQueryItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+
+ m_xEdFilterArea->SetText( OUString() ); // may be overwritten below
+
+ if ( pViewData && pDoc )
+ {
+ if(pDoc->GetChangeTrack()!=nullptr) m_xBtnCopyResult->set_sensitive(false);
+
+ ScRangeName* pRangeNames = pDoc->GetRangeName();
+ m_xLbFilterArea->clear();
+ m_xLbFilterArea->append_text(aStrUndefined);
+
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (!rEntry.second->HasType(ScRangeData::Type::Criteria))
+ continue;
+
+ OUString aSymbol = rEntry.second->GetSymbol();
+ m_xLbFilterArea->append(aSymbol, rEntry.second->GetName());
+ }
+
+ // is there a stored source range?
+
+ ScRange aAdvSource;
+ if (rQueryItem.GetAdvancedQuerySource(aAdvSource))
+ {
+ OUString aRefStr(aAdvSource.Format(*pDoc, ScRefFlags::RANGE_ABS_3D, pDoc->GetAddressConvention()));
+ m_xEdFilterArea->SetRefString( aRefStr );
+ }
+ }
+
+ m_xLbFilterArea->set_active( 0 );
+
+ // let options be initialized:
+
+ pOptionsMgr.reset( new ScFilterOptionsMgr(
+ pViewData,
+ theQueryData,
+ m_xBtnCase.get(),
+ m_xBtnRegExp.get(),
+ m_xBtnHeader.get(),
+ m_xBtnUnique.get(),
+ m_xBtnCopyResult.get(),
+ m_xBtnDestPers.get(),
+ m_xLbCopyArea.get(),
+ m_xEdCopyArea.get(),
+ m_xRbCopyArea.get(),
+ m_xFtDbAreaLabel.get(),
+ m_xFtDbArea.get(),
+ aStrUndefined ) );
+
+ // special filter always needs column headers
+ m_xBtnHeader->set_active(true);
+ m_xBtnHeader->set_sensitive(false);
+
+ // turn on modal mode
+ // SetDispatcherLock( true );
+ //@BugID 54702 enable/disable in base class only
+ //SFX_APPWINDOW->Disable(false); //! general method in ScAnyRefDlg
+}
+
+void ScSpecialFilterDlg::Close()
+{
+ if (pViewData)
+ pViewData->GetDocShell()->CancelAutoDBRange();
+
+ DoClose( ScSpecialFilterDlgWrapper::GetChildWindowId() );
+}
+
+// Transfer of a table area selected with the mouse, which is then displayed
+// as a new selection in the reference edit.
+
+void ScSpecialFilterDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if ( !(bRefInputMode && m_pRefInputEdit) ) // only possible if in the reference edit mode
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_pRefInputEdit );
+
+ OUString aRefStr;
+ const formula::FormulaGrammar::AddressConvention eConv = rDocP.GetAddressConvention();
+
+ if (m_pRefInputEdit == m_xEdCopyArea.get())
+ aRefStr = rRef.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDocP, eConv);
+ else if (m_pRefInputEdit == m_xEdFilterArea.get())
+ aRefStr = rRef.Format(rDocP, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ m_pRefInputEdit->SetRefString( aRefStr );
+}
+
+void ScSpecialFilterDlg::SetActive()
+{
+ if ( bRefInputMode )
+ {
+ if (m_pRefInputEdit == m_xEdCopyArea.get())
+ {
+ m_xEdCopyArea->GrabFocus();
+ m_xEdCopyArea->GetModifyHdl().Call( *m_xEdCopyArea );
+ }
+ else if (m_pRefInputEdit == m_xEdFilterArea.get())
+ {
+ m_xEdFilterArea->GrabFocus();
+ FilterAreaModHdl( *m_xEdFilterArea );
+ }
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+ScQueryItem* ScSpecialFilterDlg::GetOutputItem( const ScQueryParam& rParam,
+ const ScRange& rSource )
+{
+ pOutItem.reset(new ScQueryItem( nWhichQuery, &rParam ));
+ pOutItem->SetAdvancedQuerySource( &rSource );
+ return pOutItem.get();
+}
+
+bool ScSpecialFilterDlg::IsRefInputMode() const
+{
+ return bRefInputMode;
+}
+
+// Handler:
+
+IMPL_LINK(ScSpecialFilterDlg, EndDlgHdl, weld::Button&, rBtn, void)
+{
+ OSL_ENSURE( pDoc && pViewData, "Document or ViewData not found. :-/" );
+
+ if (&rBtn == m_xBtnOk.get() && pDoc && pViewData)
+ {
+ OUString theCopyStr( m_xEdCopyArea->GetText() );
+ OUString theAreaStr( m_xEdFilterArea->GetText() );
+ ScQueryParam theOutParam( theQueryData );
+ ScAddress theAdrCopy;
+ bool bEditInputOk = true;
+ bool bQueryOk = false;
+ ScRange theFilterArea;
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ sal_Int32 nColonPos = theCopyStr.indexOf( ':' );
+
+ if ( -1 != nColonPos )
+ theCopyStr = theCopyStr.copy( 0, nColonPos );
+
+ ScRefFlags nResult = theAdrCopy.Parse( theCopyStr, *pDoc, eConv );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::ZERO )
+ {
+ if (!m_xExpander->get_expanded())
+ m_xExpander->set_expanded(true);
+
+ ERRORBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdCopyArea->GrabFocus();
+ bEditInputOk = false;
+ }
+ }
+
+ if ( bEditInputOk )
+ {
+ ScRefFlags nResult = ScRange().Parse( theAreaStr, *pDoc, eConv );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::ZERO )
+ {
+ ERRORBOX(m_xDialog.get(), STR_INVALID_TABREF);
+ m_xEdFilterArea->GrabFocus();
+ bEditInputOk = false;
+ }
+ }
+
+ if ( bEditInputOk )
+ {
+ /*
+ * All edit fields contain valid areas. Now try to create
+ * a ScQueryParam from the filter area:
+ */
+
+ ScRefFlags nResult = theFilterArea.Parse( theAreaStr, *pDoc, eConv );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::VALID )
+ {
+ ScAddress& rStart = theFilterArea.aStart;
+ ScAddress& rEnd = theFilterArea.aEnd;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ theOutParam.bInplace = false;
+ theOutParam.nDestTab = theAdrCopy.Tab();
+ theOutParam.nDestCol = theAdrCopy.Col();
+ theOutParam.nDestRow = theAdrCopy.Row();
+ }
+ else
+ {
+ theOutParam.bInplace = true;
+ theOutParam.nDestTab = 0;
+ theOutParam.nDestCol = 0;
+ theOutParam.nDestRow = 0;
+ }
+
+ theOutParam.bHasHeader = m_xBtnHeader->get_active();
+ theOutParam.bByRow = true;
+ theOutParam.bCaseSens = m_xBtnCase->get_active();
+ theOutParam.eSearchType = m_xBtnRegExp->get_active() ? utl::SearchParam::SearchType::Regexp :
+ utl::SearchParam::SearchType::Normal;
+ theOutParam.bDuplicate = !m_xBtnUnique->get_active();
+ theOutParam.bDestPers = m_xBtnDestPers->get_active();
+
+ bQueryOk = pDoc->CreateQueryParam(ScRange(rStart,rEnd), theOutParam);
+ }
+ }
+
+ if ( bQueryOk )
+ {
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(FID_FILTER_OK,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { GetOutputItem(theOutParam, theFilterArea) });
+ response(RET_OK);
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), STR_INVALID_QUERYAREA);
+ m_xEdFilterArea->GrabFocus();
+ }
+ }
+ else if (&rBtn == m_xBtnCancel.get())
+ {
+ response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK_NOARG(ScSpecialFilterDlg, RefInputEditHdl, formula::RefEdit&, void)
+{
+ RefInputHdl();
+}
+
+IMPL_LINK_NOARG(ScSpecialFilterDlg, RefInputButtonHdl, formula::RefButton&, void)
+{
+ RefInputHdl();
+}
+
+void ScSpecialFilterDlg::RefInputHdl()
+{
+ if (!m_xDialog->has_toplevel_focus())
+ return;
+
+ if( m_xEdCopyArea->GetWidget()->has_focus() || m_xRbCopyArea->GetWidget()->has_focus() )
+ {
+ m_pRefInputEdit = m_xEdCopyArea.get();
+ bRefInputMode = true;
+ }
+ else if( m_xEdFilterArea->GetWidget()->has_focus() || m_xRbFilterArea->GetWidget()->has_focus() )
+ {
+ m_pRefInputEdit = m_xEdFilterArea.get();
+ bRefInputMode = true;
+ }
+ else if( bRefInputMode )
+ {
+ m_pRefInputEdit = nullptr;
+ bRefInputMode = false;
+ }
+}
+
+IMPL_LINK(ScSpecialFilterDlg, FilterAreaSelHdl, weld::ComboBox&, rLb, void)
+{
+ if (&rLb == m_xLbFilterArea.get())
+ {
+ OUString aString;
+ const sal_Int32 nSelPos = m_xLbFilterArea->get_active();
+
+ if ( nSelPos > 0 )
+ aString = m_xLbFilterArea->get_id(nSelPos);
+
+ m_xEdFilterArea->SetText( aString );
+ }
+}
+
+IMPL_LINK( ScSpecialFilterDlg, FilterAreaModHdl, formula::RefEdit&, rEd, void )
+{
+ if (&rEd != m_xEdFilterArea.get())
+ return;
+
+ if ( pDoc && pViewData )
+ {
+ OUString theCurAreaStr = rEd.GetText();
+ ScRefFlags nResult = ScRange().Parse( theCurAreaStr, *pDoc );
+
+ if ( (nResult & ScRefFlags::VALID) == ScRefFlags::VALID )
+ {
+ const sal_Int32 nCount = m_xLbFilterArea->get_count();
+ for (sal_Int32 i = 1; i < nCount; ++i)
+ {
+ OUString aStr = m_xLbFilterArea->get_id(i);
+ if (theCurAreaStr == aStr)
+ {
+ m_xLbFilterArea->set_active( i );
+ return;
+ }
+ }
+ m_xLbFilterArea->set_active( 0 );
+ }
+ }
+ else
+ m_xLbFilterArea->set_active( 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/sortdlg.cxx b/sc/source/ui/dbgui/sortdlg.cxx
new file mode 100644
index 0000000000..43978d1b85
--- /dev/null
+++ b/sc/source/ui/dbgui/sortdlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scui_def.hxx>
+#include <tpsort.hxx>
+#include <sortdlg.hxx>
+#include <unotools/viewoptions.hxx>
+
+ScSortDlg::ScSortDlg(weld::Window* pParent, const SfxItemSet* pArgSet)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/sortdialog.ui", "SortDialog", pArgSet)
+{
+ AddTabPage("criteria", ScTabPageSortFields::Create, nullptr);
+ AddTabPage("options", ScTabPageSortOptions::Create, nullptr);
+
+ // restore dialog size
+ SvtViewOptions aDlgOpt(EViewType::Dialog, "SortDialog");
+ if (aDlgOpt.Exists())
+ m_xDialog->set_window_state(aDlgOpt.GetWindowState());
+}
+
+ScSortDlg::~ScSortDlg()
+{
+ // tdf#153852 - Make of sort dialog resizable (and remember size)
+ SvtViewOptions aDlgOpt(EViewType::Dialog, "SortDialog");
+ OUString sWindowState = m_xDialog->get_window_state(vcl::WindowDataMask::PosSize);
+ aDlgOpt.SetWindowState(sWindowState);
+}
+
+ScSortWarningDlg::ScSortWarningDlg(weld::Window* pParent,
+ std::u16string_view rExtendText, std::u16string_view rCurrentText)
+ : GenericDialogController(pParent, "modules/scalc/ui/sortwarning.ui", "SortWarning")
+ , m_xFtText(m_xBuilder->weld_label("sorttext"))
+ , m_xBtnExtSort(m_xBuilder->weld_button("extend"))
+ , m_xBtnCurSort(m_xBuilder->weld_button("current"))
+{
+ OUString sTextName = m_xFtText->get_label();
+ sTextName = sTextName.replaceFirst("%1", rExtendText);
+ sTextName = sTextName.replaceFirst("%2", rCurrentText);
+ m_xFtText->set_label(sTextName);
+
+ m_xBtnExtSort->connect_clicked( LINK( this, ScSortWarningDlg, BtnHdl ) );
+ m_xBtnCurSort->connect_clicked( LINK( this, ScSortWarningDlg, BtnHdl ) );
+}
+
+ScSortWarningDlg::~ScSortWarningDlg()
+{
+}
+
+IMPL_LINK(ScSortWarningDlg, BtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnExtSort.get())
+ {
+ m_xDialog->response(BTN_EXTEND_RANGE);
+ }
+ else if(&rBtn == m_xBtnCurSort.get())
+ {
+ m_xDialog->response(BTN_CURRENT_SELECTION);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/sortkeydlg.cxx b/sc/source/ui/dbgui/sortkeydlg.cxx
new file mode 100644
index 0000000000..0e9f05870a
--- /dev/null
+++ b/sc/source/ui/dbgui/sortkeydlg.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 <memory>
+#include <sortkeydlg.hxx>
+#include <comphelper/lok.hxx>
+#include <vcl/svapp.hxx>
+
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScSortKeyItem::ScSortKeyItem(weld::Container* pParent)
+ : m_xBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/sortkey.ui"))
+ , m_xFrame(m_xBuilder->weld_frame("SortKeyFrame"))
+ , m_xLbSort(m_xBuilder->weld_combo_box("sortlb"))
+ , m_xBtnUp(m_xBuilder->weld_radio_button("up"))
+ , m_xBtnDown(m_xBuilder->weld_radio_button("down"))
+ , m_xLabel(m_xBuilder->weld_label("lbColRow"))
+ , m_pParent(pParent)
+{
+ // tdf#136155 let the other elements in the dialog determine the width of the
+ // combobox
+ m_xLbSort->set_size_request(m_xLbSort->get_approximate_digit_width() * 12, -1);
+ // keep the UI static when switching the labels
+ const sal_Int32 nChars = std::max( ScResId(SCSTR_COLUMN).getLength(), ScResId(SCSTR_ROW).getLength() ) + 2; // +2 to avoid cut-off labels on kf5/gen
+ m_xLabel->set_size_request( m_xLabel->get_approximate_digit_width() * nChars, -1);
+}
+
+ScSortKeyItem::~ScSortKeyItem()
+{
+ m_pParent->move(m_xFrame.get(), nullptr);
+}
+
+void ScSortKeyItem::DisableField()
+{
+ m_xFrame->set_sensitive(false);
+}
+
+void ScSortKeyItem::EnableField()
+{
+ m_xFrame->set_sensitive(true);
+}
+
+ScSortKeyWindow::ScSortKeyWindow(weld::Container* pBox)
+ : m_pBox(pBox)
+{
+}
+
+ScSortKeyWindow::~ScSortKeyWindow()
+{
+}
+
+void ScSortKeyWindow::AddSortKey( sal_uInt16 nItemNumber )
+{
+ ScSortKeyItem* pSortKeyItem = new ScSortKeyItem(m_pBox);
+
+ // Set Sort key number
+ OUString aLine = pSortKeyItem->m_xFrame->get_label() +
+ OUString::number( nItemNumber );
+ pSortKeyItem->m_xFrame->set_label(aLine);
+
+ // for ui-testing. Distinguish the sort keys
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ if ( m_aSortKeyItems.size() > 0 )
+ {
+ pSortKeyItem->m_xLbSort->set_buildable_name(
+ pSortKeyItem->m_xLbSort->get_buildable_name() + OUString::number(m_aSortKeyItems.size() + 1));
+ }
+ }
+
+ m_aSortKeyItems.push_back(std::unique_ptr<ScSortKeyItem>(pSortKeyItem));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/subtdlg.cxx b/sc/source/ui/dbgui/subtdlg.cxx
new file mode 100644
index 0000000000..924716a6ff
--- /dev/null
+++ b/sc/source/ui/dbgui/subtdlg.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <tpsubt.hxx>
+#include <subtdlg.hxx>
+#include <scui_def.hxx>
+
+ScSubTotalDlg::ScSubTotalDlg(weld::Window* pParent, const SfxItemSet& rArgSet)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/subtotaldialog.ui", "SubTotalDialog", &rArgSet)
+ , m_xBtnRemove(m_xBuilder->weld_button("remove"))
+{
+ AddTabPage("1stgroup", ScTpSubTotalGroup1::Create, nullptr);
+ AddTabPage("2ndgroup", ScTpSubTotalGroup2::Create, nullptr);
+ AddTabPage("3rdgroup", ScTpSubTotalGroup3::Create, nullptr);
+ AddTabPage("options", ScTpSubTotalOptions::Create, nullptr);
+ m_xBtnRemove->connect_clicked( LINK( this, ScSubTotalDlg, RemoveHdl ) );
+}
+
+ScSubTotalDlg::~ScSubTotalDlg()
+{
+}
+
+IMPL_LINK_NOARG(ScSubTotalDlg, RemoveHdl, weld::Button&, void)
+{
+ m_xDialog->response(SCRET_REMOVE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/textimportoptions.cxx b/sc/source/ui/dbgui/textimportoptions.cxx
new file mode 100644
index 0000000000..e13bdfbd4a
--- /dev/null
+++ b/sc/source/ui/dbgui/textimportoptions.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <textimportoptions.hxx>
+#include <svx/langbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+ScTextImportOptionsDlg::ScTextImportOptionsDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/textimportoptions.ui", "TextImportOptionsDialog")
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xRbAutomatic(m_xBuilder->weld_radio_button("automatic"))
+ , m_xRbCustom(m_xBuilder->weld_radio_button("custom"))
+ , m_xCkbConvertDate(m_xBuilder->weld_check_button("convertdata"))
+ , m_xCkbConvertScientific(m_xBuilder->weld_check_button("convertscientificnotation"))
+ , m_xCkbKeepAsking(m_xBuilder->weld_check_button("keepasking"))
+ , m_xLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box("lang")))
+{
+ init();
+}
+
+ScTextImportOptionsDlg::~ScTextImportOptionsDlg()
+{
+}
+
+LanguageType ScTextImportOptionsDlg::getLanguageType() const
+{
+ if (m_xRbAutomatic->get_active())
+ return LANGUAGE_SYSTEM;
+
+ return m_xLbCustomLang->get_active_id();
+}
+
+bool ScTextImportOptionsDlg::isDateConversionSet() const
+{
+ return m_xCkbConvertDate->get_active();
+}
+
+bool ScTextImportOptionsDlg::isScientificConversionSet() const
+{
+ return m_xCkbConvertScientific->get_active();
+}
+
+bool ScTextImportOptionsDlg::isKeepAskingSet() const
+{
+ return m_xCkbKeepAsking->get_active();
+}
+
+void ScTextImportOptionsDlg::init()
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScTextImportOptionsDlg, OKHdl));
+ Link<weld::Toggleable&,void> aLink = LINK(this, ScTextImportOptionsDlg, RadioCheckHdl);
+ m_xRbAutomatic->connect_toggled(aLink);
+ m_xRbCustom->connect_toggled(aLink);
+ m_xCkbConvertDate->connect_toggled(aLink);
+ m_xCkbConvertScientific->connect_toggled(aLink);
+
+ m_xRbAutomatic->set_active(true);
+
+ m_xLbCustomLang->SetLanguageList(
+ SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false);
+
+ LanguageType eLang = Application::GetSettings().GetLanguageTag().getLanguageType();
+ m_xLbCustomLang->set_active_id(eLang);
+ m_xLbCustomLang->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScTextImportOptionsDlg, OKHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(ScTextImportOptionsDlg, RadioCheckHdl, weld::Toggleable&, rBtn, void)
+{
+ if (&rBtn == m_xRbAutomatic.get())
+ {
+ m_xLbCustomLang->set_sensitive(false);
+ }
+ else if (&rBtn == m_xRbCustom.get())
+ {
+ m_xLbCustomLang->set_sensitive(true);
+ }
+ else if (&rBtn == m_xCkbConvertDate.get())
+ {
+ if (m_xCkbConvertDate->get_active())
+ {
+ m_xCkbConvertScientific->set_active(true);
+ m_xCkbConvertScientific->set_sensitive(false);
+ }
+ else
+ {
+ m_xCkbConvertScientific->set_sensitive(true);
+ }
+ }
+ else if (&rBtn == m_xCkbConvertScientific.get())
+ {
+ assert( !m_xCkbConvertDate->get_active() && "ScTextImportOptionsDlg::RadioCheckHdl - scientific option disabled if Detect numbers active" );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/tpsort.cxx b/sc/source/ui/dbgui/tpsort.cxx
new file mode 100644
index 0000000000..49390a404f
--- /dev/null
+++ b/sc/source/ui/dbgui/tpsort.cxx
@@ -0,0 +1,873 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/collatorres.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+
+#include <scitems.hxx>
+#include <uiitems.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <global.hxx>
+#include <dbdata.hxx>
+#include <userlist.hxx>
+#include <rangeutl.hxx>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <globstr.hrc>
+
+#include <sortkeydlg.hxx>
+
+#include <sortdlg.hxx>
+
+#include <tpsort.hxx>
+
+using namespace com::sun::star;
+
+/*
+ * Since the settings on the second Tab Page (Options) effects
+ * the first Tab Page, there must be a way for it to communicate with the
+ * other Page.
+ *
+ * At the moment this problem is solved through using two data members of the
+ * Tab Pages. If a page is enabled / disabled, it compares this data member
+ * with its own state (-> Activate() / Deactivate()).
+ *
+ * In the meantime the class SfxTabPage offers the following method:
+ *
+ * virtual sal_Bool HasExchangeSupport() const; -> return sal_True;
+ * virtual void ActivatePage(const SfxItemSet &);
+ * virtual int DeactivatePage(SfxItemSet * = 0);
+ *
+ * This still needs to be changed!
+ */
+
+// Sort Criteria Tab page
+
+ScTabPageSortFields::ScTabPageSortFields(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/sortcriteriapage.ui", "SortCriteriaPage", &rArgSet)
+ ,
+
+ m_aIdle("ScTabPageSortFields Scroll To End Idle"),
+ aStrUndefined ( ScResId( SCSTR_UNDEFINED ) ),
+ aStrColumn ( ScResId( SCSTR_COLUMN ) ),
+ aStrRow ( ScResId( SCSTR_ROW ) ),
+ aStrRowLabel ( ScResId( SCSTR_ROW_LABEL ) ),
+ aStrColLabel ( ScResId( SCSTR_COL_LABEL ) ),
+
+ nWhichSort ( rArgSet.GetPool()->GetWhich( SID_SORT ) ),
+ pViewData ( nullptr ),
+ aSortData ( rArgSet.Get( nWhichSort ).GetSortData() ),
+ nFieldCount ( 0 ),
+ // show actual size of the sorting keys without limiting them to the default size
+ nSortKeyCount(std::max(aSortData.GetSortKeyCount(), static_cast<sal_uInt16>(DEFSORT)))
+
+ , m_xTop(m_xBuilder->weld_container("TopWindow"))
+ , m_xBtnHeader(m_xBuilder->weld_check_button("cbHeader"))
+ , m_xBtnTopDown(m_xBuilder->weld_radio_button("rbTopDown"))
+ , m_xBtnLeftRight(m_xBuilder->weld_radio_button("rbLeftRight"))
+ , m_xScrolledWindow(m_xBuilder->weld_scrolled_window("SortCriteriaPage"))
+ , m_xBox(m_xBuilder->weld_container("SortKeyWindow"))
+ , m_aSortWin(m_xBox.get())
+{
+ // tdf#147722 set some nominal small default height so the height adapts
+ // to all the other contents and the natural height of this widget isn't
+ // an input into the overall size
+ m_xScrolledWindow->set_size_request(-1, 42);
+
+ Init();
+
+ m_aIdle.SetInvokeHandler(LINK(this, ScTabPageSortFields, ScrollToEndHdl));
+
+ SetExchangeSupport();
+}
+
+ScTabPageSortFields::~ScTabPageSortFields()
+{
+ m_aSortWin.m_aSortKeyItems.clear();
+ m_xBox.reset();
+ m_xScrolledWindow.reset();
+}
+
+void ScTabPageSortFields::Init()
+{
+ // Check whether the field that is passed on is a database field:
+ ScDocument* pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+ if ( pDoc )
+ {
+ ScDBCollection* pDBColl = pDoc->GetDBCollection();
+ const SCTAB nCurTab = pViewData->GetTabNo();
+ if ( pDBColl )
+ {
+ ScDBData* pDBData
+ = pDBColl->GetDBAtArea( nCurTab,
+ aSortData.nCol1, aSortData.nRow1,
+ aSortData.nCol2, aSortData.nRow2 );
+ if ( pDBData )
+ {
+ m_xBtnHeader->set_active(pDBData->HasHeader());
+ }
+ }
+ }
+ m_xBtnHeader->set_label(aStrColLabel);
+
+ Link<weld::Toggleable&,void> aLink = LINK(this, ScTabPageSortFields, SortDirHdl );
+ m_xBtnTopDown->connect_toggled( aLink );
+ m_xBtnLeftRight->connect_toggled( aLink );
+ m_xBtnHeader->connect_toggled( aLink );
+
+ const ScSortItem& rSortItem = GetItemSet().Get( nWhichSort );
+
+ pViewData = rSortItem.GetViewData();
+ OSL_ENSURE( pViewData, "ViewData not found!" );
+
+ nFieldArr.push_back( 0 );
+
+ // Create three sort key dialogs by default
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ AddSortKey(i+1);
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->connect_changed(LINK(this, ScTabPageSortFields, SelectHdl));
+ }
+}
+
+std::unique_ptr<SfxTabPage> ScTabPageSortFields::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pArgSet)
+{
+ return std::make_unique<ScTabPageSortFields>(pPage, pController, *pArgSet);
+}
+
+void ScTabPageSortFields::Reset( const SfxItemSet* /* rArgSet */ )
+{
+ m_xBtnHeader->set_active( aSortData.bHasHeader );
+ m_xBtnTopDown->set_active( aSortData.bByRow );
+ m_xBtnLeftRight->set_active( !aSortData.bByRow );
+
+ if (m_aSortWin.m_aSortKeyItems[0]->m_xLbSort->get_count() == 0)
+ FillFieldLists(0);
+
+ // ListBox selection:
+ if (!aSortData.maKeyState.empty() && aSortData.maKeyState[0].bDoSort)
+ {
+ // Make sure that the all sort keys are reset
+ for ( sal_uInt16 i=nSortKeyCount; i<aSortData.GetSortKeyCount(); i++ )
+ {
+ AddSortKey(i+1);
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->connect_changed( LINK( this,
+ ScTabPageSortFields, SelectHdl ) );
+ }
+ nSortKeyCount = aSortData.GetSortKeyCount();
+ FillFieldLists(0);
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ if (aSortData.maKeyState[i].bDoSort )
+ {
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active( GetFieldSelPos(
+ aSortData.maKeyState[i].nField ) );
+ (aSortData.maKeyState[i].bAscending)
+ ? m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true)
+ : m_aSortWin.m_aSortKeyItems[i]->m_xBtnDown->set_active(true);
+ }
+ else
+ {
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(0); // Select none
+ m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true);
+ }
+ }
+
+ // Enable or disable field depending on preceding Listbox selection
+ m_aSortWin.m_aSortKeyItems[0]->EnableField();
+ for ( sal_uInt16 i=1; i<nSortKeyCount; i++ )
+ if ( m_aSortWin.m_aSortKeyItems[i - 1]->m_xLbSort->get_active() == 0 )
+ m_aSortWin.m_aSortKeyItems[i]->DisableField();
+ else
+ m_aSortWin.m_aSortKeyItems[i]->EnableField();
+ }
+ else
+ {
+ SCCOL nCol = pViewData->GetCurX();
+
+ if( nCol < aSortData.nCol1 )
+ nCol = aSortData.nCol1;
+ else if( nCol > aSortData.nCol2 )
+ nCol = aSortData.nCol2;
+
+ sal_uInt16 nSort1Pos = nCol - aSortData.nCol1+1;
+
+ m_aSortWin.m_aSortKeyItems[0]->m_xLbSort->set_active(nSort1Pos);
+ for ( sal_uInt16 i=1; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(0);
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->set_active(true);
+
+ m_aSortWin.m_aSortKeyItems[0]->EnableField();
+ m_aSortWin.m_aSortKeyItems[1]->EnableField();
+ for ( sal_uInt16 i=2; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->DisableField();
+ }
+
+ // Make sure that there is always a last undefined sort key
+ if (m_aSortWin.m_aSortKeyItems[nSortKeyCount - 1]->m_xLbSort->get_active() > 0)
+ SetLastSortKey( nSortKeyCount );
+}
+
+bool ScTabPageSortFields::FillItemSet( SfxItemSet* rArgSet )
+{
+ ScSortParam aNewSortData = aSortData;
+
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSortItem* pItem = pExample->GetItemIfSet(nWhichSort))
+ {
+ ScSortParam aTempData = pItem->GetSortData();
+ aTempData.maKeyState = aNewSortData.maKeyState;
+ aNewSortData = aTempData;
+ }
+ }
+ aNewSortData.bByRow = m_xBtnTopDown->get_active();
+ aNewSortData.bHasHeader = m_xBtnHeader->get_active();
+
+ std::vector<sal_Int32> nSortPos;
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ nSortPos.push_back(m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->get_active());
+ if (nSortPos[i] == -1) nSortPos[i] = 0;
+ }
+
+ if( nSortKeyCount >= aNewSortData.GetSortKeyCount() )
+ aNewSortData.maKeyState.resize(nSortKeyCount);
+
+ if ( nSortPos[0] > 0 )
+ {
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ {
+ aNewSortData.maKeyState[i].bDoSort = (nSortPos[i] > 0);
+ aNewSortData.maKeyState[i].nField = nFieldArr[nSortPos[i]];
+ aNewSortData.maKeyState[i].bAscending = m_aSortWin.m_aSortKeyItems[i]->m_xBtnUp->get_active();
+ }
+ }
+ else
+ {
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ aNewSortData.maKeyState[i].bDoSort = false;
+ }
+
+ rArgSet->Put( ScSortItem( SCITEM_SORTDATA, nullptr, &aNewSortData ) );
+
+ return true;
+}
+
+// for data exchange without dialogue detour:
+void ScTabPageSortFields::ActivatePage( const SfxItemSet& rSet )
+{
+ // Refresh local copy with shared data
+ aSortData = rSet.Get( SCITEM_SORTDATA ).GetSortData();
+
+ m_xBtnHeader->set_active( aSortData.bHasHeader );
+ m_xBtnTopDown->set_active( aSortData.bByRow );
+ m_xBtnLeftRight->set_active( !aSortData.bByRow );
+}
+
+DeactivateRC ScTabPageSortFields::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+void ScTabPageSortFields::FillFieldLists( sal_uInt16 nStartField )
+{
+ if ( !pViewData )
+ return;
+
+ ScDocument& rDoc = pViewData->GetDocument();
+
+ for (sal_uInt16 j = nStartField; j < nSortKeyCount; ++j)
+ {
+ m_aSortWin.m_aSortKeyItems[j]->m_xLabel->set_label(aSortData.bByRow ? aStrColumn : aStrRow);
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->clear();
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->freeze();
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->append_text(aStrUndefined);
+ }
+
+ SCCOL nFirstSortCol = aSortData.nCol1;
+ SCROW nFirstSortRow = aSortData.nRow1;
+ SCTAB nTab = pViewData->GetTabNo();
+ sal_uInt16 i = 1;
+ nFieldArr.clear();
+ nFieldArr.push_back(0);
+
+ if ( aSortData.bByRow )
+ {
+ OUString aFieldName;
+ SCCOL nMaxCol = rDoc.ClampToAllocatedColumns(nTab, aSortData.nCol2);
+ SCCOL col;
+
+ for ( col=nFirstSortCol; col<=nMaxCol && i<SC_MAXFIELDS(rDoc.GetSheetLimits()); col++ )
+ {
+ aFieldName = rDoc.GetString(col, nFirstSortRow, nTab);
+ if ( !aSortData.bHasHeader || aFieldName.isEmpty() )
+ aFieldName = ScColToAlpha( col );
+ nFieldArr.push_back( col );
+
+ for ( sal_uInt16 j=nStartField; j<nSortKeyCount; j++ )
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->insert_text(i, aFieldName);
+
+ i++;
+ }
+ }
+ else
+ {
+ OUString aFieldName;
+ SCROW nMaxRow = aSortData.nRow2;
+ SCROW row;
+
+ for ( row=nFirstSortRow; row<=nMaxRow && i<SC_MAXFIELDS(rDoc.GetSheetLimits()); row++ )
+ {
+ aFieldName = rDoc.GetString(nFirstSortCol, row, nTab);
+ if ( !aSortData.bHasHeader || aFieldName.isEmpty() )
+ aFieldName = OUString::number( row+1);
+ nFieldArr.push_back( row );
+
+ for ( sal_uInt16 j=nStartField; j<nSortKeyCount; j++ )
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->insert_text(i, aFieldName);
+
+ i++;
+ }
+ }
+
+ for (sal_uInt16 j=nStartField; j < nSortKeyCount; ++j)
+ {
+ m_aSortWin.m_aSortKeyItems[j]->m_xLbSort->thaw();
+ }
+
+ nFieldCount = i;
+}
+
+sal_uInt16 ScTabPageSortFields::GetFieldSelPos( SCCOLROW nField )
+{
+ sal_uInt16 nFieldPos = 0;
+ bool bFound = false;
+
+ for ( sal_uInt16 n=1; n<nFieldCount && !bFound; n++ )
+ {
+ if ( nFieldArr[n] == nField )
+ {
+ nFieldPos = n;
+ bFound = true;
+ }
+ }
+
+ return nFieldPos;
+}
+
+void ScTabPageSortFields::SetLastSortKey( sal_uInt16 nItem )
+{
+ // Extend local SortParam copy
+ const ScSortKeyState atempKeyState = { 0, false, true, ScColorSortMode::None, Color() };
+ aSortData.maKeyState.push_back( atempKeyState );
+
+ // Add Sort Key Item
+ ++nSortKeyCount;
+ AddSortKey( nSortKeyCount );
+ m_aSortWin.m_aSortKeyItems[nItem]->m_xLbSort->connect_changed(
+ LINK( this, ScTabPageSortFields, SelectHdl ) );
+
+ FillFieldLists( nItem );
+
+ // Set Status
+ m_aSortWin.m_aSortKeyItems[nItem]->m_xBtnUp->set_active(true);
+ m_aSortWin.m_aSortKeyItems[nItem]->m_xLbSort->set_active(0);
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScTabPageSortFields, SortDirHdl, weld::Toggleable&, void)
+{
+ if ( (m_xBtnTopDown->get_active() != aSortData.bByRow) || (m_xBtnHeader->get_active() != aSortData.bHasHeader))
+ {
+ if (m_xBtnTopDown->get_active())
+ m_xBtnHeader->set_label(aStrColLabel);
+ else
+ m_xBtnHeader->set_label(aStrRowLabel);
+
+ aSortData.bByRow = m_xBtnTopDown->get_active();
+ aSortData.bHasHeader = m_xBtnHeader->get_active();
+
+ // remember selection
+ std::vector<sal_uInt16> nCurSel;
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ nCurSel.push_back( m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->get_active() );
+
+ FillFieldLists(0);
+
+ for ( sal_uInt16 i=0; i<nSortKeyCount; i++ )
+ m_aSortWin.m_aSortKeyItems[i]->m_xLbSort->set_active(nCurSel[i]);
+ }
+}
+
+IMPL_LINK( ScTabPageSortFields, SelectHdl, weld::ComboBox&, rLb, void )
+{
+ OUString aSelEntry = rLb.get_active_text();
+ ScSortKeyItems::iterator pIter;
+
+ // If last listbox is enabled add one item
+ if (m_aSortWin.m_aSortKeyItems.back()->m_xLbSort.get() == &rLb)
+ {
+ if ( aSelEntry != aStrUndefined )
+ {
+ SetLastSortKey( nSortKeyCount );
+ return;
+ }
+ }
+
+ // Find selected listbox
+ pIter = std::find_if(m_aSortWin.m_aSortKeyItems.begin(), m_aSortWin.m_aSortKeyItems.end(),
+ [&rLb](const ScSortKeyItems::value_type& rItem) { return rItem->m_xLbSort.get() == &rLb; });
+
+ if (pIter == m_aSortWin.m_aSortKeyItems.end())
+ return;
+
+ // If not selecting the last Listbox, modify the succeeding ones
+ ++pIter;
+ if ( std::distance(m_aSortWin.m_aSortKeyItems.begin(), pIter) >= nSortKeyCount )
+ return;
+
+ if ( aSelEntry == aStrUndefined )
+ {
+ for ( ; pIter != m_aSortWin.m_aSortKeyItems.end(); ++pIter )
+ {
+ (*pIter)->m_xLbSort->set_active(0);
+
+ (*pIter)->DisableField();
+ }
+ }
+ else
+ {
+ (*pIter)->EnableField();
+ }
+}
+
+IMPL_LINK_NOARG(ScTabPageSortFields, ScrollToEndHdl, Timer*, void)
+{
+ m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_upper());
+}
+
+void ScTabPageSortFields::AddSortKey( sal_uInt16 nItem )
+{
+ m_aSortWin.AddSortKey(nItem);
+ m_aIdle.Start();
+}
+
+// Sort option Tab Page:
+
+ScTabPageSortOptions::ScTabPageSortOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/sortoptionspage.ui", "SortOptionsPage", &rArgSet)
+ , aStrUndefined(ScResId(SCSTR_UNDEFINED))
+ , aStrCommentsRowLabel(ScResId(SCSTR_NOTES_ROW_LABEL))
+ , aStrCommentsColLabel(ScResId(SCSTR_NOTES_COL_LABEL))
+ , aStrImgRowLabel(ScResId(SCSTR_IMAGES_ROW_LABEL))
+ , aStrImgColLabel(ScResId(SCSTR_IMAGES_COL_LABEL))
+ , nWhichSort(rArgSet.GetPool()->GetWhich(SID_SORT))
+ , aSortData(rArgSet.Get(nWhichSort).GetSortData())
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnFormats(m_xBuilder->weld_check_button("formats"))
+ , m_xBtnNaturalSort(m_xBuilder->weld_check_button("naturalsort"))
+ , m_xBtnCopyResult(m_xBuilder->weld_check_button("copyresult"))
+ , m_xLbOutPos(m_xBuilder->weld_combo_box("outarealb"))
+ , m_xEdOutPos(m_xBuilder->weld_entry("outareaed"))
+ , m_xBtnSortUser(m_xBuilder->weld_check_button("sortuser"))
+ , m_xLbSortUser(m_xBuilder->weld_combo_box("sortuserlb"))
+ , m_xLbLanguage(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
+ , m_xFtAlgorithm(m_xBuilder->weld_label("algorithmft"))
+ , m_xLbAlgorithm(m_xBuilder->weld_combo_box("algorithmlb"))
+ , m_xBtnIncComments(m_xBuilder->weld_check_button("includenotes"))
+ , m_xBtnIncImages(m_xBuilder->weld_check_button("includeimages"))
+{
+ m_xLbSortUser->set_size_request(m_xLbSortUser->get_approximate_digit_width() * 50, -1);
+ m_xLbSortUser->set_accessible_description(ScResId(STR_A11Y_DESC_SORTUSER));
+ Init();
+ SetExchangeSupport();
+}
+
+void ScTabPageSortOptions::Init()
+{
+ // CollatorResource has user-visible names for sort algorithms
+ m_xColRes.reset(new CollatorResource);
+
+ //! use CollatorWrapper from document?
+ m_oColWrap.emplace(comphelper::getProcessComponentContext());
+
+ const ScSortItem& rSortItem = GetItemSet().Get( nWhichSort );
+
+ m_xLbOutPos->connect_changed( LINK( this, ScTabPageSortOptions, SelOutPosHdl ) );
+ m_xBtnCopyResult->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
+ m_xBtnSortUser->connect_toggled( LINK( this, ScTabPageSortOptions, EnableHdl ) );
+ m_xLbLanguage->connect_changed( LINK( this, ScTabPageSortOptions, FillAlgorHdl ) );
+
+ pViewData = rSortItem.GetViewData();
+ pDoc = pViewData ? &pViewData->GetDocument() : nullptr;
+
+ OSL_ENSURE( pViewData, "ViewData not found! :-/" );
+
+ if ( pViewData && pDoc )
+ {
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ m_xLbOutPos->clear();
+ m_xLbOutPos->append_text(aStrUndefined);
+ m_xLbOutPos->set_sensitive(false);
+
+ ScAreaNameIterator aIter( *pDoc );
+ OUString aName;
+ ScRange aRange;
+ while ( aIter.Next( aName, aRange ) )
+ {
+ OUString aRefStr(aRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, pDoc, eConv));
+ m_xLbOutPos->append(aRefStr, aName);
+ }
+
+ m_xLbOutPos->set_active(0);
+ m_xEdOutPos->set_text(OUString());
+ }
+
+ m_xBtnIncComments->set_label(aStrCommentsColLabel);
+ m_xBtnIncImages->set_label(aStrImgColLabel);
+
+ FillUserSortListBox();
+
+ // get available languages
+
+ m_xLbLanguage->SetLanguageList( SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false );
+ m_xLbLanguage->InsertLanguage( LANGUAGE_SYSTEM );
+}
+
+std::unique_ptr<SfxTabPage> ScTabPageSortOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTabPageSortOptions>(pPage, pController, *rArgSet);
+}
+
+void ScTabPageSortOptions::Reset( const SfxItemSet* /* rArgSet */ )
+{
+ if ( aSortData.bUserDef )
+ {
+ m_xBtnSortUser->set_active(true);
+ m_xLbSortUser->set_sensitive(true);
+ m_xLbSortUser->set_active(aSortData.nUserIndex);
+ }
+ else
+ {
+ m_xBtnSortUser->set_active(false);
+ m_xLbSortUser->set_sensitive(false);
+ m_xLbSortUser->set_active(0);
+ }
+
+ m_xBtnCase->set_active( aSortData.bCaseSens );
+ m_xBtnFormats->set_active( aSortData.aDataAreaExtras.mbCellFormats );
+ m_xBtnNaturalSort->set_active( aSortData.bNaturalSort );
+ m_xBtnIncComments->set_active( aSortData.aDataAreaExtras.mbCellNotes );
+ m_xBtnIncImages->set_active( aSortData.aDataAreaExtras.mbCellDrawObjects );
+
+ LanguageType eLang = LanguageTag::convertToLanguageType( aSortData.aCollatorLocale, false);
+ if ( eLang == LANGUAGE_DONTKNOW )
+ eLang = LANGUAGE_SYSTEM;
+ m_xLbLanguage->set_active_id(eLang);
+ FillAlgor(); // get algorithms, select default
+ if ( !aSortData.aCollatorAlgorithm.isEmpty() )
+ m_xLbAlgorithm->set_active_text(m_xColRes->GetTranslation(aSortData.aCollatorAlgorithm));
+
+ if ( pDoc && !aSortData.bInplace )
+ {
+ ScRefFlags nFormat = (aSortData.nDestTab != pViewData->GetTabNo())
+ ? ScRefFlags::RANGE_ABS_3D
+ : ScRefFlags::RANGE_ABS;
+
+ theOutPos.Set( aSortData.nDestCol,
+ aSortData.nDestRow,
+ aSortData.nDestTab );
+
+ OUString aStr(theOutPos.Format(nFormat, pDoc, pDoc->GetAddressConvention()));
+ m_xBtnCopyResult->set_active(true);
+ m_xLbOutPos->set_sensitive(true);
+ m_xEdOutPos->set_sensitive(true);
+ m_xEdOutPos->set_text( aStr );
+ EdOutPosModHdl();
+ m_xEdOutPos->grab_focus();
+ m_xEdOutPos->select_region(0, -1);
+ }
+ else
+ {
+ m_xBtnCopyResult->set_active( false );
+ m_xLbOutPos->set_sensitive(false);
+ m_xEdOutPos->set_sensitive(false);
+ m_xEdOutPos->set_text( OUString() );
+ }
+}
+
+bool ScTabPageSortOptions::FillItemSet( SfxItemSet* rArgSet )
+{
+ // Create local copy of ScParam
+ ScSortParam aNewSortData = aSortData;
+
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSortItem* pSortItem = pExample->GetItemIfSet(nWhichSort))
+ aNewSortData = pSortItem->GetSortData();
+ }
+ aNewSortData.bCaseSens = m_xBtnCase->get_active();
+ aNewSortData.bNaturalSort = m_xBtnNaturalSort->get_active();
+ aNewSortData.aDataAreaExtras.mbCellNotes = m_xBtnIncComments->get_active();
+ aNewSortData.aDataAreaExtras.mbCellDrawObjects = m_xBtnIncImages->get_active();
+ aNewSortData.aDataAreaExtras.mbCellFormats = m_xBtnFormats->get_active();
+ aNewSortData.bInplace = !m_xBtnCopyResult->get_active();
+ aNewSortData.nDestCol = theOutPos.Col();
+ aNewSortData.nDestRow = theOutPos.Row();
+ aNewSortData.nDestTab = theOutPos.Tab();
+ aNewSortData.bUserDef = m_xBtnSortUser->get_active();
+ aNewSortData.nUserIndex = (m_xBtnSortUser->get_active())
+ ? m_xLbSortUser->get_active()
+ : 0;
+
+ // get locale
+ LanguageType eLang = m_xLbLanguage->get_active_id();
+ aNewSortData.aCollatorLocale = LanguageTag::convertToLocale( eLang, false);
+
+ // get algorithm
+ OUString sAlg;
+ if ( eLang != LANGUAGE_SYSTEM )
+ {
+ uno::Sequence<OUString> aAlgos = m_oColWrap->listCollatorAlgorithms(
+ aNewSortData.aCollatorLocale );
+ const int nSel = m_xLbAlgorithm->get_active();
+ if ( nSel < aAlgos.getLength() )
+ sAlg = aAlgos[nSel];
+ }
+ aNewSortData.aCollatorAlgorithm = sAlg;
+
+ rArgSet->Put( ScSortItem( SCITEM_SORTDATA, &aNewSortData ) );
+
+ return true;
+}
+
+// for data exchange without dialogue detour:
+void ScTabPageSortOptions::ActivatePage( const SfxItemSet& rSet )
+{
+ // Refresh local copy with shared data
+ aSortData = rSet.Get( SCITEM_SORTDATA ).GetSortData();
+ ScSortDlg* pDlg = static_cast<ScSortDlg*>(GetDialogController());
+ if (!pDlg)
+ return;
+
+ if (aSortData.bByRow)
+ {
+ m_xBtnIncComments->set_label(aStrCommentsRowLabel);
+ m_xBtnIncImages->set_label(aStrImgRowLabel);
+ }
+ else
+ {
+ m_xBtnIncComments->set_label(aStrCommentsColLabel);
+ m_xBtnIncImages->set_label(aStrImgColLabel);
+ }
+}
+
+DeactivateRC ScTabPageSortOptions::DeactivatePage( SfxItemSet* pSetP )
+{
+ bool bPosInputOk = true;
+
+ if ( m_xBtnCopyResult->get_active() )
+ {
+ OUString thePosStr = m_xEdOutPos->get_text();
+ ScAddress thePos;
+ sal_Int32 nColonPos = thePosStr.indexOf( ':' );
+
+ if ( -1 != nColonPos )
+ thePosStr = thePosStr.copy( 0, nColonPos );
+
+ if ( pViewData )
+ {
+ // visible table is default for input without table
+ // must be changed to GetRefTabNo when sorting has RefInput!
+ thePos.SetTab( pViewData->GetTabNo() );
+ }
+
+ ScRefFlags nResult = thePos.Parse( thePosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ bPosInputOk = (nResult & ScRefFlags::VALID) == ScRefFlags::VALID;
+
+ if ( !bPosInputOk )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_TABREF)));
+ xBox->run();
+ m_xEdOutPos->grab_focus();
+ m_xEdOutPos->select_region(0, -1);
+ theOutPos.Set(0,0,0);
+ }
+ else
+ {
+ m_xEdOutPos->set_text(thePosStr);
+ theOutPos = thePos;
+ }
+ }
+
+ if ( pSetP && bPosInputOk )
+ FillItemSet( pSetP );
+
+ return bPosInputOk ? DeactivateRC::LeavePage : DeactivateRC::KeepPage;
+}
+
+void ScTabPageSortOptions::FillUserSortListBox()
+{
+ ScUserList* pUserLists = ScGlobal::GetUserList();
+
+ m_xLbSortUser->clear();
+ if ( pUserLists )
+ {
+ size_t nCount = pUserLists->size();
+ for (size_t i=0; i<nCount; ++i)
+ m_xLbSortUser->append_text((*pUserLists)[i].GetString());
+ }
+}
+
+// Handler:
+
+IMPL_LINK( ScTabPageSortOptions, EnableHdl, weld::Toggleable&, rButton, void )
+{
+ if (&rButton == m_xBtnCopyResult.get())
+ {
+ if (rButton.get_active())
+ {
+ m_xLbOutPos->set_sensitive(true);
+ m_xEdOutPos->set_sensitive(true);
+ m_xEdOutPos->grab_focus();
+ }
+ else
+ {
+ m_xLbOutPos->set_sensitive(false);
+ m_xEdOutPos->set_sensitive(false);
+ }
+ }
+ else if (&rButton == m_xBtnSortUser.get())
+ {
+ if (rButton.get_active())
+ {
+ m_xLbSortUser->set_sensitive(true);
+ m_xLbSortUser->grab_focus();
+ }
+ else
+ m_xLbSortUser->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(ScTabPageSortOptions, SelOutPosHdl, weld::ComboBox&, rLb, void)
+{
+ if (&rLb == m_xLbOutPos.get())
+ {
+ OUString aString;
+ const int nSelPos = m_xLbOutPos->get_active();
+
+ if (nSelPos > 0)
+ aString = m_xLbOutPos->get_id(nSelPos);
+
+ m_xEdOutPos->set_text(aString);
+ }
+}
+
+void ScTabPageSortOptions::EdOutPosModHdl()
+{
+ OUString theCurPosStr = m_xEdOutPos->get_text();
+ ScRefFlags nResult = ScAddress().Parse( theCurPosStr, *pDoc, pDoc->GetAddressConvention() );
+
+ if ( (nResult & ScRefFlags::VALID) != ScRefFlags::VALID )
+ return;
+
+ bool bFound = false;
+ sal_Int32 i = 0;
+ const int nCount = m_xLbOutPos->get_count();
+
+ for ( i=2; i<nCount && !bFound; i++ )
+ {
+ OUString aStr = m_xLbOutPos->get_id(i);
+ bFound = (theCurPosStr == aStr);
+ }
+
+ if ( bFound )
+ m_xLbOutPos->set_active(--i);
+ else
+ m_xLbOutPos->set_active(0);
+}
+
+void ScTabPageSortOptions::FillAlgor()
+{
+ tools::Long nCount = 0;
+
+ m_xLbAlgorithm->freeze();
+ m_xLbAlgorithm->clear();
+
+ LanguageType eLang = m_xLbLanguage->get_active_id();
+ if ( eLang == LANGUAGE_SYSTEM )
+ {
+ // for LANGUAGE_SYSTEM no algorithm can be selected because
+ // it wouldn't necessarily exist for other languages
+ // -> leave list box empty if LANGUAGE_SYSTEM is selected
+ m_xFtAlgorithm->set_sensitive( false ); // nothing to select
+ m_xLbAlgorithm->set_sensitive( false ); // nothing to select
+ }
+ else
+ {
+ lang::Locale aLocale( LanguageTag::convertToLocale( eLang ));
+ const uno::Sequence<OUString> aAlgos = m_oColWrap->listCollatorAlgorithms( aLocale );
+
+ nCount = aAlgos.getLength();
+ for (const OUString& sAlg : aAlgos)
+ {
+ OUString sUser = m_xColRes->GetTranslation( sAlg );
+ m_xLbAlgorithm->append_text(sUser);
+ }
+ }
+
+ m_xLbAlgorithm->thaw();
+
+ m_xLbAlgorithm->set_active(nCount ? 0 : -1); // first entry is default
+ m_xFtAlgorithm->set_sensitive(nCount > 1); // enable only if there is a choice
+ m_xLbAlgorithm->set_sensitive(nCount > 1); // enable only if there is a choice
+}
+
+IMPL_LINK_NOARG(ScTabPageSortOptions, FillAlgorHdl, weld::ComboBox&, void)
+{
+ FillAlgor();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/tpsubt.cxx b/sc/source/ui/dbgui/tpsubt.cxx
new file mode 100644
index 0000000000..b7e5b9bc4a
--- /dev/null
+++ b/sc/source/ui/dbgui/tpsubt.cxx
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scitems.hxx>
+#include <uiitems.hxx>
+#include <global.hxx>
+#include <userlist.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <subtotals.hrc>
+
+#include <tpsubt.hxx>
+#include <tpsort.hxx>
+#include <memory>
+
+#include <osl/diagnose.h>
+
+// Subtotals group tabpage:
+
+ScTpSubTotalGroup::ScTpSubTotalGroup(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet, const sal_uInt16& rTabNumber)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/subtotalgrppage.ui", "SubTotalGrpPage", &rArgSet)
+ , aStrNone(ScResId(SCSTR_NONE))
+ , aStrColumn(ScResId(SCSTR_COLUMN_LETTER))
+ , pViewData(nullptr)
+ , pDoc(nullptr)
+ , nWhichSubTotals(rArgSet.GetPool()->GetWhich(SID_SUBTOTALS))
+ , rSubTotalData(rArgSet.Get(nWhichSubTotals).GetSubTotalData())
+ , nFieldCount(0)
+ , mxLbGroup(m_xBuilder->weld_combo_box("group_by"))
+ , mxLbColumns(m_xBuilder->weld_tree_view("columns"))
+ , mxLbFunctions(m_xBuilder->weld_tree_view("functions"))
+ , mxLbSelectAllColumns(m_xBuilder->weld_check_button("select_all_columns_button"))
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(SCSTR_SUBTOTALS); ++i)
+ mxLbFunctions->append_text(ScResId(SCSTR_SUBTOTALS[i]));
+
+ auto nHeight = mxLbColumns->get_height_rows(14);
+ mxLbColumns->set_size_request(-1, nHeight);
+ mxLbFunctions->set_size_request(-1, nHeight);
+
+ mxLbColumns->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ Init();
+
+ // UI tests
+ mxLbGroup->set_buildable_name(mxLbGroup->get_buildable_name() + OUString::number(rTabNumber));
+ mxLbColumns->set_buildable_name(mxLbColumns->get_buildable_name() + OUString::number(rTabNumber));
+}
+
+ScTpSubTotalGroup::~ScTpSubTotalGroup()
+{
+}
+
+void ScTpSubTotalGroup::Init()
+{
+ const ScSubTotalItem& rSubTotalItem = GetItemSet().Get( nWhichSubTotals );
+
+ pViewData = rSubTotalItem.GetViewData();
+ assert(pViewData && "CreateScSubTotalDlg aArgSet must contain a ScSubTotalItem with ViewData set");
+ pDoc = &pViewData->GetDocument();
+ assert(pDoc && "Document not found :-(");
+
+ mxLbGroup->connect_changed( LINK( this, ScTpSubTotalGroup, SelectListBoxHdl ) );
+ mxLbColumns->connect_changed( LINK( this, ScTpSubTotalGroup, SelectTreeListBoxHdl ) );
+ mxLbColumns->connect_toggled( LINK( this, ScTpSubTotalGroup, CheckHdl ) );
+ mxLbFunctions->connect_changed( LINK( this, ScTpSubTotalGroup, SelectTreeListBoxHdl) );
+ mxLbSelectAllColumns->connect_toggled( LINK( this, ScTpSubTotalGroup, CheckBoxHdl ) );
+
+ mnFieldArr.resize(SC_MAXFIELDS(pDoc->GetSheetLimits()));
+ mnFieldArr[0] = 0;
+ FillListBoxes();
+}
+
+namespace
+{
+ int GetCheckedEntryCount(weld::TreeView& rTreeView)
+ {
+ int nRet = 0;
+
+ rTreeView.all_foreach([&](const weld::TreeIter& rEntry) {
+ if ( rTreeView.get_toggle(rEntry) == TRISTATE_TRUE )
+ ++nRet;
+ return false;
+ });
+
+ return nRet;
+ }
+}
+
+bool ScTpSubTotalGroup::DoReset( sal_uInt16 nGroupNo,
+ const SfxItemSet& rArgSet )
+{
+ sal_uInt16 nGroupIdx = 0;
+
+ OSL_ENSURE( (nGroupNo<=3) && (nGroupNo>0), "Invalid group" );
+
+ if ( (nGroupNo > 3) || (nGroupNo == 0) )
+ return false;
+ else
+ nGroupIdx = nGroupNo-1;
+
+ // first we have to clear the listboxes...
+ for (int nLbEntry = 0, nCount = mxLbColumns->n_children(); nLbEntry < nCount; ++nLbEntry)
+ {
+ mxLbColumns->set_toggle(nLbEntry, TRISTATE_FALSE);
+ mxLbColumns->set_id(nLbEntry, "0");
+ }
+ mxLbFunctions->select(0);
+
+ const ScSubTotalParam & theSubTotalData( rArgSet.Get( nWhichSubTotals ).GetSubTotalData() );
+
+ if ( theSubTotalData.bGroupActive[nGroupIdx] )
+ {
+ SCCOL nField = theSubTotalData.nField[nGroupIdx];
+ SCCOL nSubTotals = theSubTotalData.nSubTotals[nGroupIdx];
+ SCCOL* pSubTotals = theSubTotalData.pSubTotals[nGroupIdx].get();
+ ScSubTotalFunc* pFunctions = theSubTotalData.pFunctions[nGroupIdx].get();
+
+ mxLbGroup->set_active( GetFieldSelPos( nField )+1 );
+
+ sal_uInt16 nFirstChecked = 0;
+ for ( sal_uInt16 i=0; i<nSubTotals; i++ )
+ {
+ sal_uInt16 nCheckPos = GetFieldSelPos( pSubTotals[i] );
+
+ mxLbColumns->set_toggle(nCheckPos, TRISTATE_TRUE);
+ mxLbColumns->set_id(nCheckPos, OUString::number(FuncToLbPos(pFunctions[i])));
+
+ if (i == 0 || nCheckPos < nFirstChecked)
+ nFirstChecked = nCheckPos;
+ }
+ // Select the first checked field from the top.
+ mxLbColumns->select(nFirstChecked);
+ }
+ else
+ {
+ mxLbGroup->set_active( (nGroupNo == 1) ? 1 : 0 );
+ mxLbColumns->select( 0 );
+ mxLbFunctions->select( 0 );
+ }
+
+ if ( mxLbColumns->n_children() == GetCheckedEntryCount(*mxLbColumns) )
+ mxLbSelectAllColumns->set_active( true );
+ else
+ mxLbSelectAllColumns->set_active( false );
+
+ return true;
+}
+
+bool ScTpSubTotalGroup::DoFillItemSet( sal_uInt16 nGroupNo,
+ SfxItemSet& rArgSet )
+{
+ sal_uInt16 nGroupIdx = 0;
+
+ OSL_ENSURE( (nGroupNo<=3) && (nGroupNo>0), "Invalid group" );
+ OSL_ENSURE( (mxLbGroup->get_count() > 0)
+ && (mxLbColumns->n_children() > 0)
+ && (mxLbFunctions->n_children() > 0),
+ "Non-initialized Lists" );
+
+ if ( (nGroupNo > 3) || (nGroupNo == 0)
+ || (mxLbGroup->get_count() == 0)
+ || (mxLbColumns->n_children() == 0)
+ || (mxLbFunctions->n_children() == 0)
+ )
+ return false;
+ else
+ nGroupIdx = nGroupNo-1;
+
+ ScSubTotalParam theSubTotalData; // read out, if already partly filled
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSubTotalItem* pItem = pExample->GetItemIfSet(nWhichSubTotals))
+ theSubTotalData = pItem->GetSubTotalData();
+ }
+
+ std::unique_ptr<ScSubTotalFunc[]> pFunctions;
+ std::unique_ptr<SCCOL[]> pSubTotals;
+ const sal_Int32 nGroup = mxLbGroup->get_active();
+ const sal_Int32 nEntryCount = mxLbColumns->n_children();
+ const sal_Int32 nCheckCount = GetCheckedEntryCount(*mxLbColumns);
+
+ theSubTotalData.nCol1 = rSubTotalData.nCol1;
+ theSubTotalData.nRow1 = rSubTotalData.nRow1;
+ theSubTotalData.nCol2 = rSubTotalData.nCol2;
+ theSubTotalData.nRow2 = rSubTotalData.nRow2;
+ theSubTotalData.bGroupActive[nGroupIdx] = (nGroup != 0);
+ theSubTotalData.nField[nGroupIdx] = (nGroup != 0)
+ ? mnFieldArr[nGroup-1]
+ : static_cast<SCCOL>(0);
+
+ if ( nEntryCount>0 && nCheckCount>0 && nGroup!=0 )
+ {
+ sal_uInt16 nFunction = 0;
+
+ pSubTotals.reset(new SCCOL [nCheckCount]);
+ pFunctions.reset(new ScSubTotalFunc [nCheckCount]);
+
+ for ( sal_Int32 i=0, nCheck=0; i<nEntryCount; i++ )
+ {
+ if (mxLbColumns->get_toggle(i) == TRISTATE_TRUE)
+ {
+ OSL_ENSURE( nCheck <= nCheckCount,
+ "Range error :-(" );
+ nFunction = mxLbColumns->get_id(i).toUInt32();
+ pSubTotals[nCheck] = mnFieldArr[i];
+ pFunctions[nCheck] = LbPosToFunc( nFunction );
+ nCheck++;
+ }
+ }
+ theSubTotalData.SetSubTotals( nGroupNo, // group number
+ pSubTotals.get(),
+ pFunctions.get(),
+ nCheckCount ); // number of array elements
+
+ }
+
+ rArgSet.Put( ScSubTotalItem( SCITEM_SUBTDATA, nullptr, &theSubTotalData ) );
+
+ return true;
+}
+
+void ScTpSubTotalGroup::FillListBoxes()
+{
+ assert(pViewData && pDoc && "CreateScSubTotalDlg aArgSet must contain a ScSubTotalItem with ViewData set");
+
+ SCCOL nFirstCol = rSubTotalData.nCol1;
+ SCROW nFirstRow = rSubTotalData.nRow1;
+ SCTAB nTab = pViewData->GetTabNo();
+ SCCOL nMaxCol = rSubTotalData.nCol2;
+ SCCOL col;
+ OUString aFieldName;
+
+ mxLbGroup->clear();
+ mxLbColumns->clear();
+ mxLbGroup->insert_text(0, aStrNone );
+
+ mxLbColumns->freeze();
+ sal_uInt16 i=0;
+ for ( col=nFirstCol; col<=nMaxCol && i<SC_MAXFIELDS(pDoc->GetSheetLimits()); col++ )
+ {
+ aFieldName = pDoc->GetString(col, nFirstRow, nTab);
+ if ( aFieldName.isEmpty() )
+ {
+ aFieldName = ScGlobal::ReplaceOrAppend( aStrColumn, u"%1", ScColToAlpha( col ));
+ }
+ mnFieldArr[i] = col;
+ mxLbGroup->insert_text(i+1, aFieldName);
+ mxLbColumns->insert(i);
+ mxLbColumns->set_toggle(i, TRISTATE_FALSE);
+ mxLbColumns->set_text(i, aFieldName, 0);
+ mxLbColumns->set_id(i, "0");
+ i++;
+ }
+ mxLbColumns->thaw();
+
+ // subsequent initialization of the constant:
+ nFieldCount = i;
+}
+
+sal_uInt16 ScTpSubTotalGroup::GetFieldSelPos( SCCOL nField )
+{
+ sal_uInt16 nFieldPos = 0;
+ bool bFound = false;
+
+ for ( sal_uInt16 n=0; n<nFieldCount && !bFound; n++ )
+ {
+ if ( mnFieldArr[n] == nField )
+ {
+ nFieldPos = n;
+ bFound = true;
+ }
+ }
+
+ return nFieldPos;
+}
+
+ScSubTotalFunc ScTpSubTotalGroup::LbPosToFunc( sal_uInt16 nPos )
+{
+ switch ( nPos )
+ {
+// case 0: return SUBTOTAL_FUNC_NONE;
+ case 2: return SUBTOTAL_FUNC_AVE;
+ case 6: return SUBTOTAL_FUNC_CNT;
+ case 1: return SUBTOTAL_FUNC_CNT2;
+ case 3: return SUBTOTAL_FUNC_MAX;
+ case 4: return SUBTOTAL_FUNC_MIN;
+ case 5: return SUBTOTAL_FUNC_PROD;
+ case 7: return SUBTOTAL_FUNC_STD;
+ case 8: return SUBTOTAL_FUNC_STDP;
+ case 0: return SUBTOTAL_FUNC_SUM;
+ case 9: return SUBTOTAL_FUNC_VAR;
+ case 10: return SUBTOTAL_FUNC_VARP;
+ default:
+ OSL_FAIL( "ScTpSubTotalGroup::LbPosToFunc" );
+ return SUBTOTAL_FUNC_NONE;
+ }
+}
+
+sal_uInt16 ScTpSubTotalGroup::FuncToLbPos( ScSubTotalFunc eFunc )
+{
+ switch ( eFunc )
+ {
+// case SUBTOTAL_FUNC_NONE: return 0;
+ case SUBTOTAL_FUNC_AVE: return 2;
+ case SUBTOTAL_FUNC_CNT: return 6;
+ case SUBTOTAL_FUNC_CNT2: return 1;
+ case SUBTOTAL_FUNC_MAX: return 3;
+ case SUBTOTAL_FUNC_MIN: return 4;
+ case SUBTOTAL_FUNC_PROD: return 5;
+ case SUBTOTAL_FUNC_STD: return 7;
+ case SUBTOTAL_FUNC_STDP: return 8;
+ case SUBTOTAL_FUNC_SUM: return 0;
+ case SUBTOTAL_FUNC_VAR: return 9;
+ case SUBTOTAL_FUNC_VARP: return 10;
+ default:
+ OSL_FAIL( "ScTpSubTotalGroup::FuncToLbPos" );
+ return 0;
+ }
+}
+
+// Handler:
+
+IMPL_LINK(ScTpSubTotalGroup, SelectTreeListBoxHdl, weld::TreeView&, rLb, void)
+{
+ SelectHdl(&rLb);
+
+ if ( mxLbColumns->n_children() == GetCheckedEntryCount(*mxLbColumns) )
+ mxLbSelectAllColumns->set_active( true );
+ else
+ mxLbSelectAllColumns->set_active( false );
+}
+
+IMPL_LINK(ScTpSubTotalGroup, SelectListBoxHdl, weld::ComboBox&, rLb, void)
+{
+ SelectHdl(&rLb);
+}
+
+void ScTpSubTotalGroup::SelectHdl(const weld::Widget *pLb)
+{
+ const sal_Int32 nColumn = mxLbColumns->get_selected_index();
+ if (nColumn == -1)
+ return;
+
+ const sal_Int32 nFunction = mxLbFunctions->get_selected_index();
+ sal_uInt16 nOldFunction = mxLbColumns->get_id(nColumn).toUInt32();
+
+ if ( pLb == mxLbColumns.get() )
+ {
+ mxLbFunctions->select(nOldFunction);
+ }
+ else if ( pLb == mxLbFunctions.get() )
+ {
+ mxLbColumns->set_id(nColumn, OUString::number(nFunction));
+ mxLbColumns->set_toggle(nColumn, TRISTATE_TRUE);
+ }
+}
+
+IMPL_LINK( ScTpSubTotalGroup, CheckHdl, const weld::TreeView::iter_col&, rRowCol, void )
+{
+ mxLbColumns->select(rRowCol.first);
+ SelectHdl(mxLbColumns.get());
+
+ if ( mxLbColumns->n_children() == GetCheckedEntryCount(*mxLbColumns) )
+ mxLbSelectAllColumns->set_active( true );
+ else
+ mxLbSelectAllColumns->set_active( false );
+}
+
+// Derived Group TabPages:
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalGroup1::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet )
+{
+ return std::make_unique<ScTpSubTotalGroup1>( pPage, pController, *rArgSet );
+}
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalGroup2::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet )
+{
+ return std::make_unique<ScTpSubTotalGroup2>( pPage, pController, *rArgSet );
+}
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalGroup3::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet )
+{
+ return std::make_unique<ScTpSubTotalGroup3>( pPage, pController, *rArgSet );
+}
+
+ScTpSubTotalGroup1::ScTpSubTotalGroup1( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet ) :
+ ScTpSubTotalGroup( pPage, pController, rArgSet, 1 )
+{}
+
+ScTpSubTotalGroup2::ScTpSubTotalGroup2( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet ) :
+ ScTpSubTotalGroup( pPage, pController, rArgSet, 2 )
+{}
+
+ScTpSubTotalGroup3::ScTpSubTotalGroup3( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet ) :
+ ScTpSubTotalGroup( pPage, pController, rArgSet, 3 )
+{}
+
+#define RESET(i) (ScTpSubTotalGroup::DoReset( (i), *rArgSet ))
+void ScTpSubTotalGroup1::Reset( const SfxItemSet* rArgSet ) { RESET(1); }
+void ScTpSubTotalGroup2::Reset( const SfxItemSet* rArgSet ) { RESET(2); }
+void ScTpSubTotalGroup3::Reset( const SfxItemSet* rArgSet ) { RESET(3); }
+#undef RESET
+
+#define FILLSET(i) (ScTpSubTotalGroup::DoFillItemSet( (i), *rArgSet ))
+bool ScTpSubTotalGroup1::FillItemSet( SfxItemSet* rArgSet ) { return FILLSET(1); }
+bool ScTpSubTotalGroup2::FillItemSet( SfxItemSet* rArgSet ) { return FILLSET(2); }
+bool ScTpSubTotalGroup3::FillItemSet( SfxItemSet* rArgSet ) { return FILLSET(3); }
+#undef FILL
+
+// options tab page:
+
+ScTpSubTotalOptions::ScTpSubTotalOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+
+ : SfxTabPage ( pPage, pController,
+ "modules/scalc/ui/subtotaloptionspage.ui", "SubTotalOptionsPage",
+ &rArgSet ),
+ pViewData ( nullptr ),
+ pDoc ( nullptr ),
+ nWhichSubTotals ( rArgSet.GetPool()->GetWhich( SID_SUBTOTALS ) ),
+ rSubTotalData ( rArgSet.Get( nWhichSubTotals ).GetSubTotalData() )
+ , m_xBtnPagebreak(m_xBuilder->weld_check_button("pagebreak"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnSort(m_xBuilder->weld_check_button("sort"))
+ , m_xFlSort(m_xBuilder->weld_label("label2"))
+ , m_xBtnAscending(m_xBuilder->weld_radio_button("ascending"))
+ , m_xBtnDescending(m_xBuilder->weld_radio_button("descending"))
+ , m_xBtnFormats(m_xBuilder->weld_check_button("formats"))
+ , m_xBtnUserDef(m_xBuilder->weld_check_button("btnuserdef"))
+ , m_xLbUserDef(m_xBuilder->weld_combo_box("lbuserdef"))
+{
+ m_xLbUserDef->set_accessible_description(ScResId(STR_A11Y_DESC_USERDEF));
+ m_xBtnUserDef->set_accessible_description(ScResId(STR_A11Y_DESC_USERDEF));
+ Init();
+}
+
+ScTpSubTotalOptions::~ScTpSubTotalOptions()
+{
+}
+
+void ScTpSubTotalOptions::Init()
+{
+ const ScSubTotalItem& rSubTotalItem = GetItemSet().Get( nWhichSubTotals );
+
+ pViewData = rSubTotalItem.GetViewData();
+ assert(pViewData && "CreateScSubTotalDlg aArgSet must contain a ScSubTotalItem with ViewData set");
+ pDoc = &pViewData->GetDocument();
+ assert(pDoc && "Document not found!");
+
+ m_xBtnSort->connect_toggled( LINK( this, ScTpSubTotalOptions, CheckHdl ) );
+ m_xBtnUserDef->connect_toggled( LINK( this, ScTpSubTotalOptions, CheckHdl ) );
+
+ FillUserSortListBox();
+}
+
+std::unique_ptr<SfxTabPage> ScTpSubTotalOptions::Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTpSubTotalOptions>(pPage, pController, *rArgSet);
+}
+
+void ScTpSubTotalOptions::Reset( const SfxItemSet* /* rArgSet */ )
+{
+ m_xBtnPagebreak->set_active( rSubTotalData.bPagebreak );
+ m_xBtnCase->set_active( rSubTotalData.bCaseSens );
+ m_xBtnFormats->set_active( rSubTotalData.bIncludePattern );
+ m_xBtnSort->set_active( rSubTotalData.bDoSort );
+ m_xBtnAscending->set_active( rSubTotalData.bAscending );
+ m_xBtnDescending->set_active( !rSubTotalData.bAscending );
+
+ if ( rSubTotalData.bUserDef )
+ {
+ m_xBtnUserDef->set_active(true);
+ m_xLbUserDef->set_sensitive(true);
+ m_xLbUserDef->set_active(rSubTotalData.nUserIndex);
+ }
+ else
+ {
+ m_xBtnUserDef->set_active( false );
+ m_xLbUserDef->set_sensitive(false);
+ m_xLbUserDef->set_active(0);
+ }
+
+ CheckHdl(*m_xBtnSort);
+}
+
+bool ScTpSubTotalOptions::FillItemSet( SfxItemSet* rArgSet )
+{
+ ScSubTotalParam theSubTotalData; // read out, if already partly filled
+ const SfxItemSet* pExample = GetDialogExampleSet();
+ if (pExample)
+ {
+ if (const ScSubTotalItem* pItem = pExample->GetItemIfSet(nWhichSubTotals))
+ theSubTotalData = pItem->GetSubTotalData();
+ }
+
+ theSubTotalData.bPagebreak = m_xBtnPagebreak->get_active();
+ theSubTotalData.bReplace = true;
+ theSubTotalData.bCaseSens = m_xBtnCase->get_active();
+ theSubTotalData.bIncludePattern = m_xBtnFormats->get_active();
+ theSubTotalData.bDoSort = m_xBtnSort->get_active();
+ theSubTotalData.bAscending = m_xBtnAscending->get_active();
+ theSubTotalData.bUserDef = m_xBtnUserDef->get_active();
+ theSubTotalData.nUserIndex = (m_xBtnUserDef->get_active())
+ ? m_xLbUserDef->get_active()
+ : 0;
+
+ rArgSet->Put( ScSubTotalItem( nWhichSubTotals, nullptr, &theSubTotalData ) );
+
+ return true;
+}
+
+void ScTpSubTotalOptions::FillUserSortListBox()
+{
+ ScUserList* pUserLists = ScGlobal::GetUserList();
+
+ m_xLbUserDef->freeze();
+ m_xLbUserDef->clear();
+ if ( pUserLists )
+ {
+ size_t nCount = pUserLists->size();
+ for ( size_t i=0; i<nCount; ++i )
+ m_xLbUserDef->append_text((*pUserLists)[i].GetString() );
+ }
+ m_xLbUserDef->thaw();
+}
+
+// Handler:
+
+IMPL_LINK(ScTpSubTotalOptions, CheckHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox == m_xBtnSort.get())
+ {
+ if ( m_xBtnSort->get_active() )
+ {
+ m_xFlSort->set_sensitive(true);
+ m_xBtnFormats->set_sensitive(true);
+ m_xBtnUserDef->set_sensitive(true);
+ m_xBtnAscending->set_sensitive(true);
+ m_xBtnDescending->set_sensitive(true);
+
+ if ( m_xBtnUserDef->get_active() )
+ m_xLbUserDef->set_sensitive(true);
+ }
+ else
+ {
+ m_xFlSort->set_sensitive(false);
+ m_xBtnFormats->set_sensitive(false);
+ m_xBtnUserDef->set_sensitive(false);
+ m_xBtnAscending->set_sensitive(false);
+ m_xBtnDescending->set_sensitive(false);
+ m_xLbUserDef->set_sensitive(false);
+ }
+ }
+ else if (&rBox == m_xBtnUserDef.get())
+ {
+ if ( m_xBtnUserDef->get_active() )
+ {
+ m_xLbUserDef->set_sensitive(true);
+ m_xLbUserDef->grab_focus();
+ }
+ else
+ m_xLbUserDef->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(ScTpSubTotalGroup, CheckBoxHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox != mxLbSelectAllColumns.get())
+ return;
+
+ bool bChecked = mxLbSelectAllColumns->get_active();
+
+ mxLbColumns->all_foreach([&](const weld::TreeIter& rEntry) {
+ if ( bChecked )
+ mxLbColumns->set_toggle(rEntry, TRISTATE_TRUE);
+ else
+ mxLbColumns->set_toggle(rEntry, TRISTATE_FALSE);
+
+ return false;
+ });
+}
+
+ScTpSubTotalGroup1::~ScTpSubTotalGroup1()
+{
+}
+
+ScTpSubTotalGroup2::~ScTpSubTotalGroup2()
+{
+}
+
+ScTpSubTotalGroup3::~ScTpSubTotalGroup3()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dbgui/validate.cxx b/sc/source/ui/dbgui/validate.cxx
new file mode 100644
index 0000000000..f8565e9643
--- /dev/null
+++ b/sc/source/ui/dbgui/validate.cxx
@@ -0,0 +1,931 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifdef SC_DLLIMPLEMENTATION
+#undef SC_DLLIMPLEMENTATION
+#endif
+
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <comphelper/string.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <sfx2/app.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <scresid.hxx>
+#include <strings.hrc>
+
+#include <stringutil.hxx>
+#include <validat.hxx>
+#include <validate.hxx>
+#include <compiler.hxx>
+#include <formula/opcode.hxx>
+
+// cell range picker
+#include <tabvwsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/childwin.hxx>
+#include <reffact.hxx>
+#include <comphelper/lok.hxx>
+
+
+#define IS_MOBILE (comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone())
+
+/* Position indexes for "Allow" list box.
+ They do not map directly to ScValidationMode and can safely be modified to
+ change the order of the list box entries. */
+#define SC_VALIDDLG_ALLOW_ANY 0
+#define SC_VALIDDLG_ALLOW_WHOLE 1
+#define SC_VALIDDLG_ALLOW_DECIMAL 2
+#define SC_VALIDDLG_ALLOW_DATE 3
+#define SC_VALIDDLG_ALLOW_TIME 4
+#define SC_VALIDDLG_ALLOW_RANGE 5
+#define SC_VALIDDLG_ALLOW_LIST 6
+#define SC_VALIDDLG_ALLOW_TEXTLEN 7
+#define SC_VALIDDLG_ALLOW_CUSTOM 8
+
+/* Position indexes for "Data" list box.
+ They do not map directly to ScConditionMode and can safely be modified to
+ change the order of the list box entries. */
+#define SC_VALIDDLG_DATA_EQUAL 0
+#define SC_VALIDDLG_DATA_LESS 1
+#define SC_VALIDDLG_DATA_GREATER 2
+#define SC_VALIDDLG_DATA_EQLESS 3
+#define SC_VALIDDLG_DATA_EQGREATER 4
+#define SC_VALIDDLG_DATA_NOTEQUAL 5
+#define SC_VALIDDLG_DATA_VALIDRANGE 6
+#define SC_VALIDDLG_DATA_INVALIDRANGE 7
+#define SC_VALIDDLG_DATA_DIRECT 8
+
+namespace ValidListType = css::sheet::TableValidationVisibility;
+
+const WhichRangesContainer ScTPValidationValue::pValueRanges(svl::Items<
+ FID_VALID_LISTTYPE, FID_VALID_LISTTYPE,
+ FID_VALID_MODE, FID_VALID_ERRTEXT
+>);
+
+ScValidationDlg::ScValidationDlg(weld::Window* pParent, const SfxItemSet* pArgSet,
+ ScTabViewShell *pTabViewSh)
+ : ScValidationDlgBase(pParent,
+ "modules/scalc/ui/validationdialog.ui", "ValidationDialog", pArgSet, nullptr)
+ , m_pTabVwSh(pTabViewSh)
+ , m_sValuePageId("criteria")
+ , m_bOwnRefHdlr(false)
+ , m_bRefInputting(false)
+ , m_xHBox(m_xBuilder->weld_container("refinputbox"))
+{
+ AddTabPage(m_sValuePageId, ScTPValidationValue::Create, nullptr);
+ AddTabPage("inputhelp", ScTPValidationHelp::Create, nullptr);
+ AddTabPage("erroralert", ScTPValidationError::Create, nullptr);
+
+ if (IS_MOBILE)
+ {
+ m_xBuilder->weld_button("cancel")->hide();
+ m_xBuilder->weld_button("help")->hide();
+ }
+}
+
+void ScValidationDlg::EndDialog(int nResponse)
+{
+ // tdf#155708 - do not close, just hide validation window if we click in another sheet
+ if (nResponse == nCloseResponseToJustHide && getDialog()->get_visible())
+ {
+ getDialog()->hide();
+ return;
+ }
+ // tdf#137215 ensure original modality of true is restored before dialog loop ends
+ if (m_bOwnRefHdlr)
+ RemoveRefDlg(true);
+ ScValidationDlgBase::EndDialog(nResponse);
+}
+
+ScValidationDlg::~ScValidationDlg()
+{
+ if (m_bOwnRefHdlr)
+ RemoveRefDlg(false);
+}
+
+void ScTPValidationValue::SetReferenceHdl( const ScRange&rRange , const ScDocument& rDoc )
+{
+ if ( rRange.aStart != rRange.aEnd )
+ if ( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ if( m_pRefEdit )
+ pValidationDlg->RefInputStart( m_pRefEdit );
+
+ if ( m_pRefEdit )
+ {
+ OUString aStr(rRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ m_pRefEdit->SetRefString( aStr );
+ }
+}
+
+void ScTPValidationValue:: SetActiveHdl()
+{
+ if ( m_pRefEdit ) m_pRefEdit->GrabFocus();
+
+ if ( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ if( m_pRefEdit )
+ {
+ pValidationDlg->RefInputDone();
+ }
+}
+
+void ScTPValidationValue::RefInputStartPreHdl( formula::RefEdit* pEdit, const formula::RefButton* pButton )
+{
+ ScValidationDlg *pValidationDlg = GetValidationDlg();
+ if (!pValidationDlg)
+ return;
+
+ weld::Container* pNewParent = pValidationDlg->get_refinput_shrink_parent();
+ if (pEdit == m_pRefEdit && pNewParent != m_pRefEditParent)
+ {
+ m_xRefGrid->move(m_pRefEdit->GetWidget(), pNewParent);
+ m_pRefEditParent = pNewParent;
+ }
+
+ if (pNewParent != m_pBtnRefParent)
+ {
+ // if Edit SetParent but button not, the tab order will be
+ // incorrect, so move button anyway, and restore
+ // parent later in order to restore the tab order. But
+ // hide it if it's moved but unwanted.
+ m_xRefGrid->move(m_xBtnRef->GetWidget(), pNewParent);
+ m_xBtnRef->GetWidget()->set_visible(pButton == m_xBtnRef.get());
+ m_pBtnRefParent = pNewParent;
+ }
+
+ pNewParent->show();
+}
+
+void ScTPValidationValue::RefInputDonePostHdl()
+{
+ if (ScValidationDlg *pValidationDlg = GetValidationDlg())
+ {
+ weld::Container* pOldParent = pValidationDlg->get_refinput_shrink_parent();
+
+ if (m_pRefEdit && m_pRefEditParent != m_xRefGrid.get())
+ {
+ pOldParent->move(m_pRefEdit->GetWidget(), m_xRefGrid.get());
+ m_pRefEditParent = m_xRefGrid.get();
+ }
+
+ if (m_pBtnRefParent != m_xRefGrid.get())
+ {
+ pOldParent->move(m_xBtnRef->GetWidget(), m_xRefGrid.get());
+ m_xBtnRef->GetWidget()->show();
+ m_pBtnRefParent = m_xRefGrid.get();
+ }
+
+ pOldParent->hide();
+ ScViewData& rViewData = pValidationDlg->GetTabViewShell()->GetViewData();
+ SCTAB nCurTab = rViewData.GetTabNo();
+ SCTAB nRefTab = rViewData.GetRefTabNo();
+ // If RefInput switched to a different sheet from the data sheet,
+ // switch back: fdo#53920
+ if ( nCurTab != nRefTab )
+ {
+ rViewData.GetViewShell()->SetTabNo( nRefTab );
+ }
+ }
+
+ if (m_pRefEdit && !m_pRefEdit->GetWidget()->has_focus())
+ m_pRefEdit->GrabFocus();
+}
+
+namespace {
+
+/** Converts the passed ScValidationMode to the position in the list box. */
+sal_uInt16 lclGetPosFromValMode( ScValidationMode eValMode )
+{
+ sal_uInt16 nLbPos = SC_VALIDDLG_ALLOW_ANY;
+ switch( eValMode )
+ {
+ case SC_VALID_ANY: nLbPos = SC_VALIDDLG_ALLOW_ANY; break;
+ case SC_VALID_WHOLE: nLbPos = SC_VALIDDLG_ALLOW_WHOLE; break;
+ case SC_VALID_DECIMAL: nLbPos = SC_VALIDDLG_ALLOW_DECIMAL; break;
+ case SC_VALID_DATE: nLbPos = SC_VALIDDLG_ALLOW_DATE; break;
+ case SC_VALID_TIME: nLbPos = SC_VALIDDLG_ALLOW_TIME; break;
+ case SC_VALID_TEXTLEN: nLbPos = SC_VALIDDLG_ALLOW_TEXTLEN; break;
+ case SC_VALID_LIST: nLbPos = SC_VALIDDLG_ALLOW_RANGE; break;
+ case SC_VALID_CUSTOM: nLbPos = SC_VALIDDLG_ALLOW_CUSTOM; break;
+ default: OSL_FAIL( "lclGetPosFromValMode - unknown validity mode" );
+ }
+ return nLbPos;
+}
+
+/** Converts the passed list box position to an ScValidationMode. */
+ScValidationMode lclGetValModeFromPos( sal_uInt16 nLbPos )
+{
+ ScValidationMode eValMode = SC_VALID_ANY;
+ switch( nLbPos )
+ {
+ case SC_VALIDDLG_ALLOW_ANY: eValMode = SC_VALID_ANY; break;
+ case SC_VALIDDLG_ALLOW_WHOLE: eValMode = SC_VALID_WHOLE; break;
+ case SC_VALIDDLG_ALLOW_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
+ case SC_VALIDDLG_ALLOW_DATE: eValMode = SC_VALID_DATE; break;
+ case SC_VALIDDLG_ALLOW_TIME: eValMode = SC_VALID_TIME; break;
+ case SC_VALIDDLG_ALLOW_RANGE: eValMode = SC_VALID_LIST; break;
+ case SC_VALIDDLG_ALLOW_LIST: eValMode = SC_VALID_LIST; break;
+ case SC_VALIDDLG_ALLOW_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
+ case SC_VALIDDLG_ALLOW_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
+ default: OSL_FAIL( "lclGetValModeFromPos - invalid list box position" );
+ }
+ return eValMode;
+}
+
+/** Converts the passed ScConditionMode to the position in the list box. */
+sal_uInt16 lclGetPosFromCondMode( ScConditionMode eCondMode )
+{
+ sal_uInt16 nLbPos = SC_VALIDDLG_DATA_EQUAL;
+ switch( eCondMode )
+ {
+ case ScConditionMode::NONE: // may occur in old XML files after Excel import
+ case ScConditionMode::Equal: nLbPos = SC_VALIDDLG_DATA_EQUAL; break;
+ case ScConditionMode::Less: nLbPos = SC_VALIDDLG_DATA_LESS; break;
+ case ScConditionMode::Greater: nLbPos = SC_VALIDDLG_DATA_GREATER; break;
+ case ScConditionMode::EqLess: nLbPos = SC_VALIDDLG_DATA_EQLESS; break;
+ case ScConditionMode::EqGreater: nLbPos = SC_VALIDDLG_DATA_EQGREATER; break;
+ case ScConditionMode::NotEqual: nLbPos = SC_VALIDDLG_DATA_NOTEQUAL; break;
+ case ScConditionMode::Between: nLbPos = SC_VALIDDLG_DATA_VALIDRANGE; break;
+ case ScConditionMode::NotBetween: nLbPos = SC_VALIDDLG_DATA_INVALIDRANGE; break;
+ case ScConditionMode::Direct: nLbPos = SC_VALIDDLG_DATA_DIRECT; break;
+ default: OSL_FAIL( "lclGetPosFromCondMode - unknown condition mode" );
+ }
+ return nLbPos;
+}
+
+/** Converts the passed list box position to an ScConditionMode. */
+ScConditionMode lclGetCondModeFromPos( sal_uInt16 nLbPos )
+{
+ ScConditionMode eCondMode = ScConditionMode::Equal;
+ switch( nLbPos )
+ {
+ case SC_VALIDDLG_DATA_EQUAL: eCondMode = ScConditionMode::Equal; break;
+ case SC_VALIDDLG_DATA_LESS: eCondMode = ScConditionMode::Less; break;
+ case SC_VALIDDLG_DATA_GREATER: eCondMode = ScConditionMode::Greater; break;
+ case SC_VALIDDLG_DATA_EQLESS: eCondMode = ScConditionMode::EqLess; break;
+ case SC_VALIDDLG_DATA_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
+ case SC_VALIDDLG_DATA_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
+ case SC_VALIDDLG_DATA_VALIDRANGE: eCondMode = ScConditionMode::Between; break;
+ case SC_VALIDDLG_DATA_INVALIDRANGE: eCondMode = ScConditionMode::NotBetween; break;
+ case SC_VALIDDLG_DATA_DIRECT: eCondMode = ScConditionMode::Direct; break;
+ default: OSL_FAIL( "lclGetCondModeFromPos - invalid list box position" );
+ }
+ return eCondMode;
+}
+
+/** Converts line feed separated string to a formula with strings separated by semicolons.
+ @descr Keeps all empty strings.
+ Example: abc\ndef\n\nghi -> "abc";"def";"";"ghi".
+ @param rFmlaStr (out-param) The converted formula string. */
+void lclGetFormulaFromStringList( OUString& rFmlaStr, std::u16string_view rStringList, sal_Unicode cFmlaSep )
+{
+ rFmlaStr.clear();
+ if (!rStringList.empty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ OUString aToken {o3tl::getToken(rStringList, 0, '\n', nIdx )};
+ ScGlobal::AddQuotes( aToken, '"' );
+ rFmlaStr = ScGlobal::addToken(rFmlaStr, aToken, cFmlaSep);
+ }
+ while (nIdx>0);
+ }
+ if( rFmlaStr.isEmpty() )
+ rFmlaStr = "\"\"";
+}
+
+/** Converts formula with strings separated by semicolons to line feed separated string.
+ @descr Keeps all empty strings. Ignores all empty tokens (multiple semicolons).
+ Example: "abc";;;"def";"";"ghi" -> abc\ndef\n\nghi.
+ @param rStringList (out-param) The converted line feed separated string list.
+ @return true = Conversion successful. */
+bool lclGetStringListFromFormula( OUString& rStringList, const OUString& rFmlaStr, sal_Unicode cFmlaSep )
+{
+ static constexpr OUStringLiteral aQuotes( u"\"\"" );
+
+ rStringList.clear();
+ bool bIsStringList = !rFmlaStr.isEmpty();
+ bool bTokenAdded = false;
+
+ for ( sal_Int32 nStringIx = 0; bIsStringList && nStringIx>=0; )
+ {
+ OUString aToken( ScStringUtil::GetQuotedToken(rFmlaStr, 0, aQuotes, cFmlaSep, nStringIx ) );
+ aToken = comphelper::string::strip(aToken, ' ');
+ if( !aToken.isEmpty() ) // ignore empty tokens, i.e. "a";;"b"
+ {
+ bIsStringList = ScGlobal::IsQuoted( aToken, '"' );
+ if( bIsStringList )
+ {
+ ScGlobal::EraseQuotes( aToken, '"' );
+ rStringList = ScGlobal::addToken(rStringList, aToken, '\n', 1, bTokenAdded);
+ bTokenAdded = true;
+ }
+ }
+ }
+
+ return bIsStringList;
+}
+
+} // namespace
+
+ScTPValidationValue::ScTPValidationValue(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/validationcriteriapage.ui",
+ "ValidationCriteriaPage", &rArgSet)
+ , maStrMin(ScResId(SCSTR_VALID_MINIMUM))
+ , maStrMax(ScResId(SCSTR_VALID_MAXIMUM))
+ , maStrValue(ScResId(SCSTR_VALID_VALUE))
+ , maStrFormula(ScResId(SCSTR_VALID_FORMULA))
+ , maStrRange(ScResId(SCSTR_VALID_RANGE))
+ , maStrList(ScResId(SCSTR_VALID_LIST))
+ , m_pRefEdit(nullptr)
+ , m_xLbAllow(m_xBuilder->weld_combo_box("allow"))
+ , m_xCbAllow(m_xBuilder->weld_check_button("allowempty"))
+ , m_xCbShow(m_xBuilder->weld_check_button("showlist"))
+ , m_xCbSort(m_xBuilder->weld_check_button("sortascend"))
+ , m_xFtValue(m_xBuilder->weld_label("valueft"))
+ , m_xLbValue(m_xBuilder->weld_combo_box("data"))
+ , m_xFtMin(m_xBuilder->weld_label("minft"))
+ , m_xMinGrid(m_xBuilder->weld_widget("mingrid"))
+ , m_xEdMin(new formula::RefEdit(m_xBuilder->weld_entry("min")))
+ , m_xEdList(m_xBuilder->weld_text_view("minlist"))
+ , m_xFtMax(m_xBuilder->weld_label("maxft"))
+ , m_xEdMax(new formula::RefEdit(m_xBuilder->weld_entry("max")))
+ , m_xFtHint(m_xBuilder->weld_label("hintft"))
+ , m_xBtnRef(new formula::RefButton(m_xBuilder->weld_button("validref")))
+ , m_xRefGrid(m_xBuilder->weld_container("refgrid"))
+ , m_pRefEditParent(m_xRefGrid.get())
+ , m_pBtnRefParent(m_xRefGrid.get())
+{
+ m_xEdMin->SetReferences(nullptr, m_xFtMin.get());
+
+ Size aSize(m_xEdList->get_approximate_digit_width() * 40,
+ m_xEdList->get_height_rows(10));
+ m_xEdList->set_size_request(aSize.Width(), aSize.Height());
+ m_xEdMax->SetReferences(nullptr, m_xFtMax.get());
+
+ m_xBtnRef->SetClickHdl(LINK(this, ScTPValidationValue, ClickHdl));
+
+ //lock in the max size initial config
+ aSize = m_xContainer->get_preferred_size();
+ m_xContainer->set_size_request(aSize.Width(), aSize.Height());
+
+ Init();
+
+ // list separator in formulas
+ OUString aListSep = ::ScCompiler::GetNativeSymbol( ocSep );
+ OSL_ENSURE( aListSep.getLength() == 1, "ScTPValidationValue::ScTPValidationValue - list separator error" );
+ mcFmlaSep = aListSep.getLength() ? aListSep[0] : ';';
+ m_xBtnRef->GetWidget()->hide(); // cell range picker
+}
+
+ScTPValidationValue::~ScTPValidationValue()
+{
+ m_xEdMin.reset();
+ m_xEdMax.reset();
+ m_xBtnRef.reset();
+}
+
+void ScTPValidationValue::Init()
+{
+ m_xLbAllow->connect_changed( LINK( this, ScTPValidationValue, SelectHdl ) );
+ m_xLbValue->connect_changed( LINK( this, ScTPValidationValue, SelectHdl ) );
+ m_xCbShow->connect_toggled( LINK( this, ScTPValidationValue, CheckHdl ) );
+
+ // cell range picker
+ m_xEdMin->SetGetFocusHdl( LINK( this, ScTPValidationValue, EditSetFocusHdl ) );
+ m_xEdMin->SetLoseFocusHdl( LINK( this, ScTPValidationValue, KillEditFocusHdl ) );
+ m_xEdMax->SetGetFocusHdl( LINK( this, ScTPValidationValue, EditSetFocusHdl ) );
+ m_xEdMax->SetLoseFocusHdl( LINK( this, ScTPValidationValue, KillEditFocusHdl ) );
+ m_xBtnRef->SetLoseFocusHdl( LINK( this, ScTPValidationValue, KillButtonFocusHdl ) );
+
+ m_xLbAllow->set_active( SC_VALIDDLG_ALLOW_ANY );
+ m_xLbValue->set_active( SC_VALIDDLG_DATA_EQUAL );
+
+ SelectHdl( *m_xLbAllow );
+ CheckHdl( *m_xCbShow );
+}
+
+std::unique_ptr<SfxTabPage> ScTPValidationValue::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTPValidationValue>(pPage, pController, *rArgSet);
+}
+
+void ScTPValidationValue::Reset( const SfxItemSet* rArgSet )
+{
+ sal_uInt16 nLbPos = SC_VALIDDLG_ALLOW_ANY;
+ if( const SfxUInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_MODE ) )
+ nLbPos = lclGetPosFromValMode( static_cast< ScValidationMode >( pItem->GetValue() ) );
+ m_xLbAllow->set_active( nLbPos );
+
+ nLbPos = SC_VALIDDLG_DATA_EQUAL;
+ if( const SfxUInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_CONDMODE ) )
+ nLbPos = lclGetPosFromCondMode( static_cast< ScConditionMode >( pItem->GetValue() ) );
+ m_xLbValue->set_active( nLbPos );
+
+ // *** check boxes ***
+ bool bCheck = true;
+ if( const SfxBoolItem* pItem = rArgSet->GetItemIfSet( FID_VALID_BLANK ) )
+ bCheck = pItem->GetValue();
+ m_xCbAllow->set_active( bCheck );
+
+ sal_Int32 nListType = ValidListType::UNSORTED;
+ if( const SfxInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_LISTTYPE ) )
+ nListType = pItem->GetValue();
+ m_xCbShow->set_active( nListType != ValidListType::INVISIBLE );
+ m_xCbSort->set_active( nListType == ValidListType::SORTEDASCENDING );
+
+ // *** formulas ***
+ OUString aFmlaStr;
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_VALUE1 ) )
+ aFmlaStr = pItem->GetValue();
+ SetFirstFormula( aFmlaStr );
+
+ aFmlaStr.clear();
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_VALUE2 ) )
+ aFmlaStr = pItem->GetValue();
+ SetSecondFormula( aFmlaStr );
+
+ SelectHdl( *m_xLbAllow );
+ CheckHdl( *m_xCbShow );
+}
+
+bool ScTPValidationValue::FillItemSet( SfxItemSet* rArgSet )
+{
+ sal_Int16 nListType = m_xCbShow->get_active() ?
+ (m_xCbSort->get_active() ? ValidListType::SORTEDASCENDING : ValidListType::UNSORTED) :
+ ValidListType::INVISIBLE;
+
+ const sal_Int32 nLbPos = m_xLbAllow->get_active();
+ bool bCustom = (nLbPos == SC_VALIDDLG_ALLOW_CUSTOM);
+ ScConditionMode eCondMode = bCustom ?
+ ScConditionMode::Direct : lclGetCondModeFromPos( m_xLbValue->get_active() );
+
+ rArgSet->Put( SfxUInt16Item( FID_VALID_MODE, sal::static_int_cast<sal_uInt16>(
+ lclGetValModeFromPos( nLbPos ) ) ) );
+ rArgSet->Put( SfxUInt16Item( FID_VALID_CONDMODE, sal::static_int_cast<sal_uInt16>( eCondMode ) ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_VALUE1, GetFirstFormula() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_VALUE2, GetSecondFormula() ) );
+ rArgSet->Put( SfxBoolItem( FID_VALID_BLANK, m_xCbAllow->get_active() ) );
+ rArgSet->Put( SfxInt16Item( FID_VALID_LISTTYPE, nListType ) );
+ return true;
+}
+
+OUString ScTPValidationValue::GetFirstFormula() const
+{
+ OUString aFmlaStr;
+ if( m_xLbAllow->get_active() == SC_VALIDDLG_ALLOW_LIST )
+ lclGetFormulaFromStringList( aFmlaStr, m_xEdList->get_text(), mcFmlaSep );
+ else
+ aFmlaStr = m_xEdMin->GetText();
+ return aFmlaStr;
+}
+
+OUString ScTPValidationValue::GetSecondFormula() const
+{
+ return m_xEdMax->GetText();
+}
+
+void ScTPValidationValue::SetFirstFormula( const OUString& rFmlaStr )
+{
+ // try if formula is a string list, validation mode must already be set
+ OUString aStringList;
+ if( (m_xLbAllow->get_active() == SC_VALIDDLG_ALLOW_RANGE) &&
+ lclGetStringListFromFormula( aStringList, rFmlaStr, mcFmlaSep ) )
+ {
+ m_xEdList->set_text( aStringList );
+ m_xEdMin->SetText( OUString() );
+ // change validation mode to string list
+ m_xLbAllow->set_active( SC_VALIDDLG_ALLOW_LIST );
+ }
+ else
+ {
+ m_xEdMin->SetText( rFmlaStr );
+ m_xEdList->set_text( OUString() );
+ }
+}
+
+void ScTPValidationValue::SetSecondFormula( const OUString& rFmlaStr )
+{
+ m_xEdMax->SetText( rFmlaStr );
+}
+
+ScValidationDlg * ScTPValidationValue::GetValidationDlg()
+{
+ return dynamic_cast<ScValidationDlg*>(GetDialogController());
+}
+
+void ScTPValidationValue::SetupRefDlg()
+{
+ ScValidationDlg *pValidationDlg = GetValidationDlg();
+ if( !pValidationDlg )
+ return;
+
+ if( !pValidationDlg->SetupRefDlg() )
+ return;
+
+ pValidationDlg->SetHandler( this );
+ pValidationDlg->SetSetRefHdl( static_cast<ScRefHandlerHelper::PFUNCSETREFHDLTYPE>( &ScTPValidationValue::SetReferenceHdl ) );
+ pValidationDlg->SetSetActHdl( static_cast<ScRefHandlerHelper::PCOMMONHDLTYPE>( &ScTPValidationValue::SetActiveHdl ) );
+ pValidationDlg->SetRefInputStartPreHdl( static_cast<ScRefHandlerHelper::PINPUTSTARTDLTYPE>( &ScTPValidationValue::RefInputStartPreHdl ) );
+ pValidationDlg->SetRefInputDonePostHdl( static_cast<ScRefHandlerHelper::PCOMMONHDLTYPE>( &ScTPValidationValue::RefInputDonePostHdl ) );
+
+ weld::Label* pLabel = nullptr;
+
+ if (m_xEdMax->GetWidget()->get_visible())
+ {
+ m_pRefEdit = m_xEdMax.get();
+ pLabel = m_xFtMax.get();
+ }
+ else if (m_xEdMin->GetWidget()->get_visible())
+ {
+ m_pRefEdit = m_xEdMin.get();
+ pLabel = m_xFtMin.get();
+ }
+
+ if (m_pRefEdit && !m_pRefEdit->GetWidget()->has_focus())
+ m_pRefEdit->GrabFocus();
+
+ if( m_pRefEdit )
+ m_pRefEdit->SetReferences( pValidationDlg, pLabel );
+
+ m_xBtnRef->SetReferences( pValidationDlg, m_pRefEdit );
+}
+
+void ScTPValidationValue::RemoveRefDlg(bool bRestoreModal)
+{
+ ScValidationDlg *pValidationDlg = GetValidationDlg();
+ if( !pValidationDlg )
+ return;
+
+ if( !pValidationDlg->RemoveRefDlg(bRestoreModal) )
+ return;
+
+ pValidationDlg->SetHandler( nullptr );
+ pValidationDlg->SetSetRefHdl( nullptr );
+ pValidationDlg->SetSetActHdl( nullptr );
+ pValidationDlg->SetRefInputStartPreHdl( nullptr );
+ pValidationDlg->SetRefInputDonePostHdl( nullptr );
+
+ if( m_pRefEdit )
+ m_pRefEdit->SetReferences( nullptr, nullptr );
+ m_pRefEdit = nullptr;
+
+ m_xBtnRef->SetReferences( nullptr, nullptr );
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, EditSetFocusHdl, formula::RefEdit&, void)
+{
+ const sal_Int32 nPos = m_xLbAllow->get_active();
+
+ if ( nPos == SC_VALIDDLG_ALLOW_RANGE )
+ {
+ SetupRefDlg();
+ }
+}
+
+IMPL_LINK( ScTPValidationValue, KillEditFocusHdl, formula::RefEdit&, rWnd, void )
+{
+ if (&rWnd != m_pRefEdit)
+ return;
+ if( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ {
+ if (pValidationDlg->IsChildFocus() && !pValidationDlg->IsRefInputting())
+ {
+ if( ( !m_pRefEdit || !m_pRefEdit->GetWidget()->has_focus()) && !m_xBtnRef->GetWidget()->has_focus() )
+ {
+ RemoveRefDlg(true);
+ }
+ }
+ }
+}
+
+IMPL_LINK( ScTPValidationValue, KillButtonFocusHdl, formula::RefButton&, rWnd, void )
+{
+ if( &rWnd != m_xBtnRef.get())
+ return;
+ if( ScValidationDlg *pValidationDlg = GetValidationDlg() )
+ if (pValidationDlg->IsChildFocus() && !pValidationDlg->IsRefInputting())
+ if( ( !m_pRefEdit || !m_pRefEdit->GetWidget()->has_focus()) && !m_xBtnRef->GetWidget()->has_focus() )
+ {
+ RemoveRefDlg(true);
+ }
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, SelectHdl, weld::ComboBox&, void)
+{
+ const sal_Int32 nLbPos = m_xLbAllow->get_active();
+ bool bEnable = (nLbPos != SC_VALIDDLG_ALLOW_ANY);
+ bool bRange = (nLbPos == SC_VALIDDLG_ALLOW_RANGE);
+ bool bList = (nLbPos == SC_VALIDDLG_ALLOW_LIST);
+ bool bCustom = (nLbPos == SC_VALIDDLG_ALLOW_CUSTOM);
+
+ m_xCbAllow->set_sensitive( bEnable ); // Empty cell
+ m_xFtValue->set_sensitive( bEnable );
+ m_xLbValue->set_sensitive( bEnable );
+ m_xFtMin->set_sensitive( bEnable );
+ m_xEdMin->GetWidget()->set_sensitive( bEnable );
+ m_xEdList->set_sensitive( bEnable );
+ m_xFtMax->set_sensitive( bEnable );
+ m_xEdMax->GetWidget()->set_sensitive( bEnable );
+
+ bool bShowMax = false;
+
+ if( bRange )
+ m_xFtMin->set_label( maStrRange );
+ else if( bList )
+ m_xFtMin->set_label( maStrList );
+ else if( bCustom )
+ m_xFtMin->set_label( maStrFormula );
+ else
+ {
+ switch( m_xLbValue->get_active() )
+ {
+ case SC_VALIDDLG_DATA_EQUAL:
+ case SC_VALIDDLG_DATA_NOTEQUAL: m_xFtMin->set_label( maStrValue ); break;
+
+ case SC_VALIDDLG_DATA_LESS:
+ case SC_VALIDDLG_DATA_EQLESS: m_xFtMin->set_label( maStrMax ); break;
+
+ case SC_VALIDDLG_DATA_VALIDRANGE:
+ case SC_VALIDDLG_DATA_INVALIDRANGE: bShowMax = true;
+ [[fallthrough]];
+ case SC_VALIDDLG_DATA_GREATER:
+ case SC_VALIDDLG_DATA_EQGREATER: m_xFtMin->set_label( maStrMin ); break;
+
+ default:
+ OSL_FAIL( "ScTPValidationValue::SelectHdl - unknown condition mode" );
+ }
+ }
+
+ m_xCbShow->set_visible( bRange || bList );
+ m_xCbSort->set_visible( bRange || bList );
+ m_xFtValue->set_visible( !bRange && !bList && !bCustom);
+ m_xLbValue->set_visible( !bRange && !bList && !bCustom );
+ m_xEdMin->GetWidget()->set_visible( !bList );
+ m_xEdList->set_visible( bList );
+ m_xMinGrid->set_vexpand( bList );
+ m_xFtMax->set_visible( bShowMax );
+ m_xEdMax->GetWidget()->set_visible( bShowMax );
+ m_xFtHint->set_visible( bRange );
+ m_xBtnRef->GetWidget()->set_visible( bRange ); // cell range picker
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, CheckHdl, weld::Toggleable&, void)
+{
+ m_xCbSort->set_sensitive( m_xCbShow->get_active() );
+}
+
+// Input Help Page
+
+ScTPValidationHelp::ScTPValidationHelp(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/validationhelptabpage.ui", "ValidationHelpTabPage", &rArgSet)
+ , m_xTsbHelp(m_xBuilder->weld_check_button("tsbhelp"))
+ , m_xEdtTitle(m_xBuilder->weld_entry("title"))
+ , m_xEdInputHelp(m_xBuilder->weld_text_view("inputhelp_text"))
+{
+ m_xEdInputHelp->set_size_request(m_xEdInputHelp->get_approximate_digit_width() * 40, m_xEdInputHelp->get_height_rows(13));
+}
+
+ScTPValidationHelp::~ScTPValidationHelp()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTPValidationHelp::Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTPValidationHelp>(pPage, pController, *rArgSet);
+}
+
+void ScTPValidationHelp::Reset( const SfxItemSet* rArgSet )
+{
+ if ( const SfxBoolItem* pItem = rArgSet->GetItemIfSet( FID_VALID_SHOWHELP ) )
+ m_xTsbHelp->set_state( pItem->GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE );
+ else
+ m_xTsbHelp->set_state( TRISTATE_FALSE );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_HELPTITLE ) )
+ m_xEdtTitle->set_text( pItem->GetValue() );
+ else
+ m_xEdtTitle->set_text( OUString() );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_HELPTEXT ) )
+ m_xEdInputHelp->set_text( pItem->GetValue() );
+ else
+ m_xEdInputHelp->set_text( OUString() );
+}
+
+bool ScTPValidationHelp::FillItemSet( SfxItemSet* rArgSet )
+{
+ rArgSet->Put( SfxBoolItem( FID_VALID_SHOWHELP, m_xTsbHelp->get_state() == TRISTATE_TRUE ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_HELPTITLE, m_xEdtTitle->get_text() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_HELPTEXT, m_xEdInputHelp->get_text() ) );
+
+ return true;
+}
+
+// Error Alert Page
+
+ScTPValidationError::ScTPValidationError(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rArgSet)
+
+ : SfxTabPage ( pPage, pController,
+ "modules/scalc/ui/erroralerttabpage.ui", "ErrorAlertTabPage",
+ &rArgSet )
+ , m_xTsbShow(m_xBuilder->weld_check_button("tsbshow"))
+ , m_xLbAction(m_xBuilder->weld_combo_box("actionCB"))
+ , m_xBtnSearch(m_xBuilder->weld_button("browseBtn"))
+ , m_xEdtTitle(m_xBuilder->weld_entry("erroralert_title"))
+ , m_xFtError(m_xBuilder->weld_label("errormsg_label"))
+ , m_xEdError(m_xBuilder->weld_text_view("errorMsg"))
+{
+ m_xEdError->set_size_request(m_xEdError->get_approximate_digit_width() * 40, m_xEdError->get_height_rows(12));
+ Init();
+}
+
+ScTPValidationError::~ScTPValidationError()
+{
+}
+
+void ScTPValidationError::Init()
+{
+ m_xLbAction->connect_changed(LINK(this, ScTPValidationError, SelectActionHdl));
+ m_xBtnSearch->connect_clicked(LINK( this, ScTPValidationError, ClickSearchHdl));
+
+ m_xLbAction->set_active(0);
+
+ SelectActionHdl(*m_xLbAction);
+}
+
+std::unique_ptr<SfxTabPage> ScTPValidationError::Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet)
+{
+ return std::make_unique<ScTPValidationError>(pPage, pController, *rArgSet);
+}
+
+void ScTPValidationError::Reset( const SfxItemSet* rArgSet )
+{
+ if ( const SfxBoolItem* pItem = rArgSet->GetItemIfSet( FID_VALID_SHOWERR ) )
+ m_xTsbShow->set_state( pItem->GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE );
+ else
+ m_xTsbShow->set_state( TRISTATE_TRUE ); // check by default
+
+ if ( const SfxUInt16Item* pItem = rArgSet->GetItemIfSet( FID_VALID_ERRSTYLE ) )
+ m_xLbAction->set_active( pItem->GetValue() );
+ else
+ m_xLbAction->set_active( 0 );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_ERRTITLE ) )
+ m_xEdtTitle->set_text( pItem->GetValue() );
+ else
+ m_xEdtTitle->set_text( OUString() );
+
+ if ( const SfxStringItem* pItem = rArgSet->GetItemIfSet( FID_VALID_ERRTEXT ) )
+ m_xEdError->set_text( pItem->GetValue() );
+ else
+ m_xEdError->set_text( OUString() );
+
+ SelectActionHdl(*m_xLbAction);
+}
+
+bool ScTPValidationError::FillItemSet( SfxItemSet* rArgSet )
+{
+ rArgSet->Put( SfxBoolItem( FID_VALID_SHOWERR, m_xTsbShow->get_state() == TRISTATE_TRUE ) );
+ rArgSet->Put( SfxUInt16Item( FID_VALID_ERRSTYLE, m_xLbAction->get_active() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_ERRTITLE, m_xEdtTitle->get_text() ) );
+ rArgSet->Put( SfxStringItem( FID_VALID_ERRTEXT, m_xEdError->get_text() ) );
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ScTPValidationError, SelectActionHdl, weld::ComboBox&, void)
+{
+ ScValidErrorStyle eStyle = static_cast<ScValidErrorStyle>(m_xLbAction->get_active());
+ bool bMacro = ( eStyle == SC_VALERR_MACRO );
+
+ m_xBtnSearch->set_sensitive( bMacro );
+ m_xFtError->set_sensitive( !bMacro );
+ m_xEdError->set_sensitive( !bMacro );
+}
+
+IMPL_LINK_NOARG(ScTPValidationError, ClickSearchHdl, weld::Button&, void)
+{
+ // Use static SfxApplication method to bring up selector dialog for
+ // choosing a script
+ OUString aScriptURL = SfxApplication::ChooseScript(GetFrameWeld());
+
+ if ( !aScriptURL.isEmpty() )
+ {
+ m_xEdtTitle->set_text( aScriptURL );
+ }
+}
+
+bool ScValidationDlg::EnterRefStatus()
+{
+ ScTabViewShell *pTabViewShell = GetTabViewShell();
+
+ if( !pTabViewShell ) return false;
+
+ sal_uInt16 nId = SLOTID;
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ if (pWnd && pWnd->GetController().get() != this) pWnd = nullptr;
+
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+
+ return true;
+}
+
+bool ScValidationDlg::LeaveRefStatus()
+{
+ ScTabViewShell *pTabViewShell = GetTabViewShell();
+
+ if( !pTabViewShell ) return false;
+
+ sal_uInt16 nId = SLOTID;
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ if (rViewFrm.GetChildWindow(nId))
+ {
+ DoClose( nId );
+ }
+ return true;
+}
+
+bool ScValidationDlg::SetupRefDlg()
+{
+ if ( m_bOwnRefHdlr ) return false;
+ if( EnterRefMode() )
+ {
+ SetModal( false );
+ m_bOwnRefHdlr = true;
+ return EnterRefStatus();
+ }
+
+ return false;
+}
+
+bool ScValidationDlg::RemoveRefDlg( bool bRestoreModal /* = true */ )
+{
+ bool bVisLock = false;
+ bool bFreeWindowLock = false;
+
+ ScTabViewShell *pTabVwSh = GetTabViewShell();
+
+ if( !pTabVwSh ) return false;
+
+ if ( SfxChildWindow* pWnd = pTabVwSh->GetViewFrame().GetChildWindow( SID_VALIDITY_REFERENCE ) )
+ {
+ bVisLock = static_cast<ScValidityRefChildWin*>(pWnd)->LockVisible( true );
+ bFreeWindowLock = static_cast<ScValidityRefChildWin*>(pWnd)->LockFreeWindow( true );
+ }
+
+ if ( !m_bOwnRefHdlr ) return false;
+ if( LeaveRefStatus() && LeaveRefMode() )
+ {
+ m_bOwnRefHdlr = false;
+
+ if( bRestoreModal )
+ {
+ SetModal( true );
+ }
+ }
+
+ if ( SfxChildWindow* pWnd = pTabVwSh->GetViewFrame().GetChildWindow( SID_VALIDITY_REFERENCE ) )
+ {
+ static_cast<ScValidityRefChildWin*>(pWnd)->LockVisible( bVisLock );
+ static_cast<ScValidityRefChildWin*>(pWnd)->LockFreeWindow( bFreeWindowLock );
+ }
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ScTPValidationValue, ClickHdl, formula::RefButton&, void)
+{
+ SetupRefDlg();
+}
+
+bool ScValidationDlg::IsChildFocus() const
+{
+ return m_xDialog->has_toplevel_focus();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dialogs/SparklineDataRangeDialog.cxx b/sc/source/ui/dialogs/SparklineDataRangeDialog.cxx
new file mode 100644
index 0000000000..0e91051db4
--- /dev/null
+++ b/sc/source/ui/dialogs/SparklineDataRangeDialog.cxx
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <SparklineDataRangeDialog.hxx>
+#include <Sparkline.hxx>
+#include <reffact.hxx>
+#include <docfunc.hxx>
+
+namespace sc
+{
+SparklineDataRangeDialog::SparklineDataRangeDialog(SfxBindings* pBindings,
+ SfxChildWindow* pChildWindow,
+ weld::Window* pWindow, ScViewData& rViewData)
+ : ScAnyRefDlgController(pBindings, pChildWindow, pWindow,
+ u"modules/scalc/ui/sparklinedatarangedialog.ui"_ustr,
+ "SparklineDataRangeDialog")
+ , mrViewData(rViewData)
+ , mrDocument(rViewData.GetDocument())
+ , mpActiveEdit(nullptr)
+ , mbDialogLostFocus(false)
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+ , mxDataRangeLabel(m_xBuilder->weld_label("cell-range-label"))
+ , mxDataRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("cell-range-edit")))
+ , mxDataRangeButton(new formula::RefButton(m_xBuilder->weld_button("cell-range-button")))
+
+{
+ mxDataRangeEdit->SetReferences(this, mxDataRangeLabel.get());
+ mxDataRangeButton->SetReferences(this, mxDataRangeEdit.get());
+
+ mxButtonCancel->connect_clicked(LINK(this, SparklineDataRangeDialog, ButtonClicked));
+ mxButtonOk->connect_clicked(LINK(this, SparklineDataRangeDialog, ButtonClicked));
+
+ mxButtonOk->set_sensitive(false);
+
+ Link<formula::RefEdit&, void> aEditLink
+ = LINK(this, SparklineDataRangeDialog, EditFocusHandler);
+ mxDataRangeEdit->SetGetFocusHdl(aEditLink);
+ aEditLink = LINK(this, SparklineDataRangeDialog, LoseEditFocusHandler);
+ mxDataRangeEdit->SetLoseFocusHdl(aEditLink);
+
+ Link<formula::RefButton&, void> aButtonLink
+ = LINK(this, SparklineDataRangeDialog, ButtonFocusHandler);
+ mxDataRangeButton->SetGetFocusHdl(aButtonLink);
+ aButtonLink = LINK(this, SparklineDataRangeDialog, LoseButtonFocusHandler);
+ mxDataRangeButton->SetLoseFocusHdl(aButtonLink);
+
+ Link<formula::RefEdit&, void> aModifyLink
+ = LINK(this, SparklineDataRangeDialog, RefInputModifyHandler);
+ mxDataRangeEdit->SetModifyHdl(aModifyLink);
+
+ setupValues();
+
+ mxDataRangeEdit->GrabFocus();
+}
+
+SparklineDataRangeDialog::~SparklineDataRangeDialog() = default;
+
+void SparklineDataRangeDialog::setupValues()
+{
+ ScAddress aCurrentAddress = mrViewData.GetCurPos();
+ mpSparkline = mrDocument.GetSparkline(aCurrentAddress);
+
+ if (mpSparkline)
+ {
+ ScRangeList aRangeList(mpSparkline->getInputRange());
+ if (!aRangeList.empty())
+ {
+ maDataRange = aRangeList[0];
+ OUString aString
+ = maDataRange.Format(mrDocument, ScRefFlags::VALID | ScRefFlags::TAB_3D,
+ mrDocument.GetAddressConvention());
+ mxDataRangeEdit->SetRefString(aString);
+ mxButtonOk->set_sensitive(true);
+ }
+ }
+}
+
+void SparklineDataRangeDialog::Close()
+{
+ DoClose(sc::SparklineDataRangeDialogWrapper::GetChildWindowId());
+}
+
+void SparklineDataRangeDialog::SetActive()
+{
+ if (mbDialogLostFocus)
+ {
+ mbDialogLostFocus = false;
+ if (mpActiveEdit)
+ mpActiveEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void SparklineDataRangeDialog::SetReference(const ScRange& rReferenceRange, ScDocument& rDocument)
+{
+ if (mpActiveEdit)
+ {
+ if (rReferenceRange.aStart != rReferenceRange.aEnd)
+ RefInputStart(mpActiveEdit);
+
+ OUString aString;
+ const ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ auto eAddressConvention = rDocument.GetAddressConvention();
+
+ if (mpActiveEdit == mxDataRangeEdit.get())
+ {
+ maDataRange = rReferenceRange;
+ aString = maDataRange.Format(rDocument, eFlags, eAddressConvention);
+ mxDataRangeEdit->SetRefString(aString);
+ }
+ }
+}
+
+IMPL_LINK(SparklineDataRangeDialog, EditFocusHandler, formula::RefEdit&, rEdit, void)
+{
+ if (mxDataRangeEdit.get() == &rEdit)
+ mpActiveEdit = mxDataRangeEdit.get();
+ else
+ mpActiveEdit = nullptr;
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(SparklineDataRangeDialog, ButtonFocusHandler, formula::RefButton&, rButton, void)
+{
+ if (mxDataRangeButton.get() == &rButton)
+ mpActiveEdit = mxDataRangeEdit.get();
+ else
+ mpActiveEdit = nullptr;
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(SparklineDataRangeDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(SparklineDataRangeDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(SparklineDataRangeDialog, RefInputModifyHandler, formula::RefEdit&, void)
+{
+ if (mpActiveEdit)
+ {
+ if (mpActiveEdit == mxDataRangeEdit.get())
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames(aRangeList, mxDataRangeEdit->GetText(), mrDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ maDataRange = *pRange;
+ mxDataRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ maDataRange = ScRange(ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ }
+}
+
+IMPL_LINK(SparklineDataRangeDialog, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (mxButtonOk.get() == &rButton)
+ {
+ perform();
+ response(RET_OK);
+ }
+ else
+ {
+ response(RET_CANCEL);
+ }
+}
+
+void SparklineDataRangeDialog::perform()
+{
+ ScRangeList aList{ maDataRange };
+
+ auto& rDocFunc = mrViewData.GetDocShell()->GetDocFunc();
+ rDocFunc.ChangeSparkline(mpSparkline, mrViewData.GetTabNo(), aList);
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dialogs/SparklineDialog.cxx b/sc/source/ui/dialogs/SparklineDialog.cxx
new file mode 100644
index 0000000000..ff56e629e6
--- /dev/null
+++ b/sc/source/ui/dialogs/SparklineDialog.cxx
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <SparklineDialog.hxx>
+#include <SparklineData.hxx>
+#include <SparklineGroup.hxx>
+#include <Sparkline.hxx>
+#include <reffact.hxx>
+
+#include <docfunc.hxx>
+
+#include <svx/colorbox.hxx>
+#include <vcl/formatter.hxx>
+
+namespace sc
+{
+SparklineDialog::SparklineDialog(SfxBindings* pBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pWindow, ScViewData& rViewData)
+ : ScAnyRefDlgController(pBindings, pChildWindow, pWindow,
+ u"modules/scalc/ui/sparklinedialog.ui"_ustr, "SparklineDialog")
+ , mrViewData(rViewData)
+ , mrDocument(rViewData.GetDocument())
+ , mpActiveEdit(nullptr)
+ , mbDialogLostFocus(false)
+ , mxButtonOk(m_xBuilder->weld_button("ok"))
+ , mxButtonCancel(m_xBuilder->weld_button("cancel"))
+ , mxFrameData(m_xBuilder->weld_frame("frmData"))
+ , mxInputRangeLabel(m_xBuilder->weld_label("lbInputRange"))
+ , mxInputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("edInputRange")))
+ , mxInputRangeButton(new formula::RefButton(m_xBuilder->weld_button("btnInputRange")))
+ , mxOutputRangeLabel(m_xBuilder->weld_label("lbOutputRange"))
+ , mxOutputRangeEdit(new formula::RefEdit(m_xBuilder->weld_entry("edOutputRange")))
+ , mxOutputRangeButton(new formula::RefButton(m_xBuilder->weld_button("btnOutputRange")))
+ , mxColorSeries(new ColorListBox(m_xBuilder->weld_menu_button("colSeries"),
+ [pWindow] { return pWindow; }))
+ , mxColorNegative(new ColorListBox(m_xBuilder->weld_menu_button("colNegative"),
+ [pWindow] { return pWindow; }))
+ , mxColorMarker(new ColorListBox(m_xBuilder->weld_menu_button("colMarker"),
+ [pWindow] { return pWindow; }))
+ , mxColorHigh(
+ new ColorListBox(m_xBuilder->weld_menu_button("colHigh"), [pWindow] { return pWindow; }))
+ , mxColorLow(
+ new ColorListBox(m_xBuilder->weld_menu_button("colLow"), [pWindow] { return pWindow; }))
+ , mxColorFirst(
+ new ColorListBox(m_xBuilder->weld_menu_button("colFirst"), [pWindow] { return pWindow; }))
+ , mxColorLast(
+ new ColorListBox(m_xBuilder->weld_menu_button("colLast"), [pWindow] { return pWindow; }))
+ , mxCheckButtonNegative(m_xBuilder->weld_check_button("cbNegative"))
+ , mxCheckButtonMarker(m_xBuilder->weld_check_button("cbMarker"))
+ , mxCheckButtonHigh(m_xBuilder->weld_check_button("cbHigh"))
+ , mxCheckButtonLow(m_xBuilder->weld_check_button("cbLow"))
+ , mxCheckButtonFirst(m_xBuilder->weld_check_button("cbFirst"))
+ , mxCheckButtonLast(m_xBuilder->weld_check_button("cbLast"))
+ , mxSpinLineWidth(m_xBuilder->weld_spin_button("seLineWidth"))
+ , mxType(m_xBuilder->weld_combo_box("cbType"))
+ , mxCheckDisplayXAxis(m_xBuilder->weld_check_button("cbDisplayXAxis"))
+ , mxCheckDisplayHidden(m_xBuilder->weld_check_button("cbHidden"))
+ , mxCheckRightToLeft(m_xBuilder->weld_check_button("cbRTL"))
+ , mxDisplayEmptyGap(m_xBuilder->weld_combo_box("cbEmptyCells"))
+ , mxComboMinAxisType(m_xBuilder->weld_combo_box("cbMinAxisType"))
+ , mxComboMaxAxisType(m_xBuilder->weld_combo_box("cbMaxAxisType"))
+ , mxSpinCustomMin(m_xBuilder->weld_formatted_spin_button("seMinAxis"))
+ , mxSpinCustomMax(m_xBuilder->weld_formatted_spin_button("seMaxAxis"))
+ , mbEditMode(false)
+{
+ mxInputRangeEdit->SetReferences(this, mxInputRangeLabel.get());
+ mxInputRangeButton->SetReferences(this, mxInputRangeEdit.get());
+
+ mxOutputRangeEdit->SetReferences(this, mxOutputRangeLabel.get());
+ mxOutputRangeButton->SetReferences(this, mxOutputRangeEdit.get());
+
+ mxButtonCancel->connect_clicked(LINK(this, SparklineDialog, ButtonClicked));
+ mxButtonOk->connect_clicked(LINK(this, SparklineDialog, ButtonClicked));
+ mxButtonOk->set_sensitive(false);
+
+ Link<formula::RefEdit&, void> aEditLink = LINK(this, SparklineDialog, EditFocusHandler);
+ mxInputRangeEdit->SetGetFocusHdl(aEditLink);
+ mxOutputRangeEdit->SetGetFocusHdl(aEditLink);
+ aEditLink = LINK(this, SparklineDialog, LoseEditFocusHandler);
+ mxInputRangeEdit->SetLoseFocusHdl(aEditLink);
+ mxOutputRangeEdit->SetLoseFocusHdl(aEditLink);
+
+ Link<formula::RefButton&, void> aButtonLink = LINK(this, SparklineDialog, ButtonFocusHandler);
+ mxInputRangeButton->SetGetFocusHdl(aButtonLink);
+ mxOutputRangeButton->SetGetFocusHdl(aButtonLink);
+ aButtonLink = LINK(this, SparklineDialog, LoseButtonFocusHandler);
+ mxInputRangeButton->SetLoseFocusHdl(aButtonLink);
+ mxOutputRangeButton->SetLoseFocusHdl(aButtonLink);
+
+ Link<formula::RefEdit&, void> aModifyLink = LINK(this, SparklineDialog, RefInputModifyHandler);
+ mxInputRangeEdit->SetModifyHdl(aModifyLink);
+ mxOutputRangeEdit->SetModifyHdl(aModifyLink);
+
+ mxType->connect_changed(LINK(this, SparklineDialog, SelectSparklineType));
+ mxDisplayEmptyGap->connect_changed(LINK(this, SparklineDialog, SelectSparklineType));
+
+ Link<weld::Toggleable&, void> aLink = LINK(this, SparklineDialog, ToggleHandler);
+ mxCheckButtonNegative->connect_toggled(aLink);
+ mxCheckButtonMarker->connect_toggled(aLink);
+ mxCheckButtonHigh->connect_toggled(aLink);
+ mxCheckButtonLow->connect_toggled(aLink);
+ mxCheckButtonFirst->connect_toggled(aLink);
+ mxCheckButtonLast->connect_toggled(aLink);
+ mxCheckDisplayXAxis->connect_toggled(aLink);
+ mxCheckDisplayHidden->connect_toggled(aLink);
+ mxCheckRightToLeft->connect_toggled(aLink);
+
+ mxSpinLineWidth->connect_value_changed(LINK(this, SparklineDialog, SpinLineWidthChanged));
+
+ mxComboMinAxisType->connect_changed(LINK(this, SparklineDialog, ComboValueChanged));
+ mxComboMaxAxisType->connect_changed(LINK(this, SparklineDialog, ComboValueChanged));
+
+ mxSpinCustomMin->connect_value_changed(LINK(this, SparklineDialog, SpinCustomChanged));
+ Formatter& rSpinCustomMinFormatter = mxSpinCustomMin->GetFormatter();
+ rSpinCustomMinFormatter.ClearMinValue();
+ rSpinCustomMinFormatter.ClearMaxValue();
+ rSpinCustomMinFormatter.UseInputStringForFormatting();
+
+ mxSpinCustomMax->connect_value_changed(LINK(this, SparklineDialog, SpinCustomChanged));
+ Formatter& rSpinCustomMaxFormatter = mxSpinCustomMax->GetFormatter();
+ rSpinCustomMaxFormatter.ClearMinValue();
+ rSpinCustomMaxFormatter.ClearMaxValue();
+ rSpinCustomMaxFormatter.UseInputStringForFormatting();
+
+ setupValues();
+
+ mxOutputRangeEdit->GrabFocus();
+ mxButtonOk->set_sensitive(checkValidInputOutput());
+}
+
+SparklineDialog::~SparklineDialog() = default;
+
+void SparklineDialog::setInputSelection()
+{
+ mrViewData.GetSimpleArea(maInputRange);
+ OUString aString = maInputRange.Format(mrDocument, ScRefFlags::VALID | ScRefFlags::TAB_3D,
+ mrDocument.GetAddressConvention());
+ mxInputRangeEdit->SetRefString(aString);
+}
+
+void SparklineDialog::setupValues()
+{
+ ScRange aSelectionRange;
+ mrViewData.GetSimpleArea(aSelectionRange);
+
+ if (mrDocument.HasOneSparklineGroup(aSelectionRange))
+ {
+ if (auto pSparkline = mrDocument.GetSparkline(aSelectionRange.aStart))
+ {
+ mpSparklineGroup = pSparkline->getSparklineGroup();
+ maAttributes = mpSparklineGroup->getAttributes();
+ mxFrameData->set_visible(false);
+ mbEditMode = true;
+ }
+ }
+ else
+ {
+ maInputRange = aSelectionRange;
+ }
+
+ setInputSelection();
+
+ switch (maAttributes.getType())
+ {
+ case sc::SparklineType::Line:
+ mxType->set_active(0);
+ break;
+ case sc::SparklineType::Column:
+ mxType->set_active(1);
+ break;
+ case sc::SparklineType::Stacked:
+ mxType->set_active(2);
+ break;
+ }
+
+ switch (maAttributes.getDisplayEmptyCellsAs())
+ {
+ case sc::DisplayEmptyCellsAs::Gap:
+ mxDisplayEmptyGap->set_active(0);
+ break;
+ case sc::DisplayEmptyCellsAs::Zero:
+ mxDisplayEmptyGap->set_active(1);
+ break;
+ case sc::DisplayEmptyCellsAs::Span:
+ mxDisplayEmptyGap->set_active(2);
+ break;
+ }
+
+ mxColorSeries->SelectEntry(maAttributes.getColorSeries().getFinalColor());
+ mxColorNegative->SelectEntry(maAttributes.getColorNegative().getFinalColor());
+ mxColorMarker->SelectEntry(maAttributes.getColorMarkers().getFinalColor());
+ mxColorHigh->SelectEntry(maAttributes.getColorHigh().getFinalColor());
+ mxColorLow->SelectEntry(maAttributes.getColorLow().getFinalColor());
+ mxColorFirst->SelectEntry(maAttributes.getColorFirst().getFinalColor());
+ mxColorLast->SelectEntry(maAttributes.getColorLast().getFinalColor());
+
+ mxCheckButtonNegative->set_active(maAttributes.isNegative());
+ mxCheckButtonMarker->set_active(maAttributes.isMarkers());
+ mxCheckButtonHigh->set_active(maAttributes.isHigh());
+ mxCheckButtonLow->set_active(maAttributes.isLow());
+ mxCheckButtonFirst->set_active(maAttributes.isFirst());
+ mxCheckButtonLast->set_active(maAttributes.isLast());
+
+ mxSpinLineWidth->set_value(sal_Int64(maAttributes.getLineWeight() * 100.0));
+
+ mxCheckDisplayXAxis->set_active(maAttributes.shouldDisplayXAxis());
+ mxCheckDisplayHidden->set_active(maAttributes.shouldDisplayHidden());
+ mxCheckRightToLeft->set_active(maAttributes.isRightToLeft());
+
+ switch (maAttributes.getMinAxisType())
+ {
+ case sc::AxisType::Individual:
+ mxComboMinAxisType->set_active(0);
+ mxSpinCustomMin->GetFormatter().SetValue(0.0);
+ break;
+ case sc::AxisType::Group:
+ mxComboMinAxisType->set_active(1);
+ mxSpinCustomMin->GetFormatter().SetValue(0.0);
+ break;
+ case sc::AxisType::Custom:
+ mxComboMinAxisType->set_active(2);
+ if (maAttributes.getManualMin())
+ mxSpinCustomMin->GetFormatter().SetValue(*maAttributes.getManualMin());
+ break;
+ }
+ ComboValueChanged(*mxComboMinAxisType);
+
+ switch (maAttributes.getMaxAxisType())
+ {
+ case sc::AxisType::Individual:
+ mxComboMaxAxisType->set_active(0);
+ mxSpinCustomMax->GetFormatter().SetValue(0.0);
+ break;
+ case sc::AxisType::Group:
+ mxComboMaxAxisType->set_active(1);
+ mxSpinCustomMax->GetFormatter().SetValue(0.0);
+ break;
+ case sc::AxisType::Custom:
+ mxComboMaxAxisType->set_active(2);
+ if (maAttributes.getManualMax())
+ mxSpinCustomMax->GetFormatter().SetValue(*maAttributes.getManualMax());
+ break;
+ }
+ ComboValueChanged(*mxComboMaxAxisType);
+}
+
+void SparklineDialog::Close() { DoClose(sc::SparklineDialogWrapper::GetChildWindowId()); }
+
+void SparklineDialog::SetActive()
+{
+ if (mbDialogLostFocus)
+ {
+ mbDialogLostFocus = false;
+ if (mpActiveEdit)
+ mpActiveEdit->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void SparklineDialog::SetReference(const ScRange& rReferenceRange, ScDocument& rDocument)
+{
+ if (mpActiveEdit)
+ {
+ if (rReferenceRange.aStart != rReferenceRange.aEnd)
+ RefInputStart(mpActiveEdit);
+
+ OUString aString;
+ const ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ auto eAddressConvention = rDocument.GetAddressConvention();
+
+ if (mpActiveEdit == mxInputRangeEdit.get())
+ {
+ maInputRange = rReferenceRange;
+ aString = maInputRange.Format(rDocument, eFlags, eAddressConvention);
+ mxInputRangeEdit->SetRefString(aString);
+ }
+ else if (mpActiveEdit == mxOutputRangeEdit.get())
+ {
+ maOutputRange = rReferenceRange;
+ aString = maOutputRange.Format(rDocument, eFlags, eAddressConvention);
+ mxOutputRangeEdit->SetRefString(aString);
+ }
+ }
+
+ mxButtonOk->set_sensitive(checkValidInputOutput());
+}
+
+IMPL_LINK(SparklineDialog, EditFocusHandler, formula::RefEdit&, rEdit, void)
+{
+ auto* pEdit = &rEdit;
+
+ if (mxInputRangeEdit.get() == pEdit)
+ mpActiveEdit = mxInputRangeEdit.get();
+ else if (mxOutputRangeEdit.get() == pEdit)
+ mpActiveEdit = mxOutputRangeEdit.get();
+ else
+ mpActiveEdit = nullptr;
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK(SparklineDialog, ButtonFocusHandler, formula::RefButton&, rButton, void)
+{
+ auto* pButton = &rButton;
+
+ if (mxInputRangeButton.get() == pButton)
+ mpActiveEdit = mxInputRangeEdit.get();
+ else if (mxOutputRangeButton.get() == pButton)
+ mpActiveEdit = mxOutputRangeEdit.get();
+ else
+ mpActiveEdit = nullptr;
+
+ if (mpActiveEdit)
+ mpActiveEdit->SelectAll();
+}
+
+IMPL_LINK_NOARG(SparklineDialog, LoseEditFocusHandler, formula::RefEdit&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(SparklineDialog, LoseButtonFocusHandler, formula::RefButton&, void)
+{
+ mbDialogLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(SparklineDialog, RefInputModifyHandler, formula::RefEdit&, void)
+{
+ if (mpActiveEdit)
+ {
+ if (mpActiveEdit == mxInputRangeEdit.get())
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames(aRangeList, mxInputRangeEdit->GetText(), mrDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ maInputRange = *pRange;
+ mxInputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ maInputRange = ScRange(ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ else if (mpActiveEdit == mxOutputRangeEdit.get())
+ {
+ ScRangeList aRangeList;
+ bool bValid = ParseWithNames(aRangeList, mxOutputRangeEdit->GetText(), mrDocument);
+ const ScRange* pRange = (bValid && aRangeList.size() == 1) ? &aRangeList[0] : nullptr;
+ if (pRange)
+ {
+ maOutputRange = *pRange;
+ mxOutputRangeEdit->StartUpdateData();
+ }
+ else
+ {
+ maOutputRange = ScRange(ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ }
+
+ mxButtonOk->set_sensitive(checkValidInputOutput());
+}
+
+IMPL_LINK(SparklineDialog, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (mxButtonOk.get() == &rButton)
+ {
+ perform();
+ response(RET_OK);
+ }
+ else
+ {
+ response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK(SparklineDialog, ToggleHandler, weld::Toggleable&, rToggle, void)
+{
+ if (mxCheckButtonNegative.get() == &rToggle)
+ maAttributes.setNegative(mxCheckButtonNegative->get_active());
+ if (mxCheckButtonMarker.get() == &rToggle)
+ maAttributes.setMarkers(mxCheckButtonMarker->get_active());
+ if (mxCheckButtonHigh.get() == &rToggle)
+ maAttributes.setHigh(mxCheckButtonHigh->get_active());
+ if (mxCheckButtonLow.get() == &rToggle)
+ maAttributes.setLow(mxCheckButtonLow->get_active());
+ if (mxCheckButtonFirst.get() == &rToggle)
+ maAttributes.setFirst(mxCheckButtonFirst->get_active());
+ if (mxCheckButtonLast.get() == &rToggle)
+ maAttributes.setLast(mxCheckButtonLast->get_active());
+ if (mxCheckDisplayXAxis.get() == &rToggle)
+ maAttributes.setDisplayXAxis(mxCheckDisplayXAxis->get_active());
+ if (mxCheckDisplayHidden.get() == &rToggle)
+ maAttributes.setDisplayHidden(mxCheckDisplayHidden->get_active());
+ if (mxCheckRightToLeft.get() == &rToggle)
+ maAttributes.setRightToLeft(mxCheckRightToLeft->get_active());
+}
+
+IMPL_LINK_NOARG(SparklineDialog, SelectSparklineType, weld::ComboBox&, void)
+{
+ switch (mxType->get_active())
+ {
+ case 0:
+ maAttributes.setType(sc::SparklineType::Line);
+ break;
+ case 1:
+ maAttributes.setType(sc::SparklineType::Column);
+ break;
+ case 2:
+ maAttributes.setType(sc::SparklineType::Stacked);
+ break;
+ }
+ switch (mxDisplayEmptyGap->get_active())
+ {
+ case 1:
+ maAttributes.setDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs::Gap);
+ break;
+ case 2:
+ maAttributes.setDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs::Zero);
+ break;
+ case 3:
+ maAttributes.setDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs::Span);
+ break;
+ }
+}
+
+IMPL_LINK_NOARG(SparklineDialog, SpinLineWidthChanged, weld::SpinButton&, void)
+{
+ double value = mxSpinLineWidth->get_value() / 100.0;
+ maAttributes.setLineWeight(value);
+}
+
+IMPL_LINK(SparklineDialog, SpinCustomChanged, weld::FormattedSpinButton&, rFormatted, void)
+{
+ if (mxSpinCustomMin.get() == &rFormatted)
+ {
+ maAttributes.setManualMin(rFormatted.GetFormatter().GetValue());
+ }
+ else if (mxSpinCustomMax.get() == &rFormatted)
+ {
+ maAttributes.setManualMax(rFormatted.GetFormatter().GetValue());
+ }
+}
+
+IMPL_LINK(SparklineDialog, ComboValueChanged, weld::ComboBox&, rComboBox, void)
+{
+ int nActive = rComboBox.get_active();
+
+ if (mxComboMinAxisType.get() == &rComboBox)
+ {
+ switch (nActive)
+ {
+ case 0:
+ maAttributes.setMinAxisType(sc::AxisType::Individual);
+ mxSpinCustomMin->set_sensitive(false);
+ break;
+ case 1:
+ maAttributes.setMinAxisType(sc::AxisType::Group);
+ mxSpinCustomMin->set_sensitive(false);
+ break;
+ case 2:
+ maAttributes.setMinAxisType(sc::AxisType::Custom);
+ mxSpinCustomMin->set_sensitive(true);
+ break;
+ default:
+ break;
+ }
+ }
+ else if (mxComboMaxAxisType.get() == &rComboBox)
+ {
+ switch (nActive)
+ {
+ case 0:
+ maAttributes.setMaxAxisType(sc::AxisType::Individual);
+ mxSpinCustomMax->set_sensitive(false);
+ break;
+ case 1:
+ maAttributes.setMaxAxisType(sc::AxisType::Group);
+ mxSpinCustomMax->set_sensitive(false);
+ break;
+ case 2:
+ maAttributes.setMaxAxisType(sc::AxisType::Custom);
+ mxSpinCustomMax->set_sensitive(true);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+bool SparklineDialog::checkValidInputOutput()
+{
+ if (mbEditMode)
+ return true;
+
+ if (!maInputRange.IsValid() || !maOutputRange.IsValid())
+ return false;
+
+ sc::RangeOrientation eInputOrientation = sc::RangeOrientation::Unknown;
+ if (maOutputRange.aStart.Col() == maOutputRange.aEnd.Col())
+ {
+ sal_Int32 nOutputRowSize = maOutputRange.aEnd.Row() - maOutputRange.aStart.Row();
+ eInputOrientation = sc::calculateOrientation(nOutputRowSize, maInputRange);
+ }
+ else if (maOutputRange.aStart.Row() == maOutputRange.aEnd.Row())
+ {
+ sal_Int32 nOutputColSize = maOutputRange.aEnd.Col() - maOutputRange.aStart.Col();
+ eInputOrientation = sc::calculateOrientation(nOutputColSize, maInputRange);
+ }
+
+ return eInputOrientation != sc::RangeOrientation::Unknown;
+}
+
+void SparklineDialog::perform()
+{
+ maAttributes.setColorSeries(mxColorSeries->GetSelectedEntry().getComplexColor());
+ maAttributes.setColorNegative(mxColorNegative->GetSelectedEntry().getComplexColor());
+ maAttributes.setColorMarkers(mxColorMarker->GetSelectedEntry().getComplexColor());
+ maAttributes.setColorHigh(mxColorHigh->GetSelectedEntry().getComplexColor());
+ maAttributes.setColorLow(mxColorLow->GetSelectedEntry().getComplexColor());
+ maAttributes.setColorFirst(mxColorFirst->GetSelectedEntry().getComplexColor());
+ maAttributes.setColorLast(mxColorLast->GetSelectedEntry().getComplexColor());
+
+ auto& rDocFunc = mrViewData.GetDocShell()->GetDocFunc();
+
+ if (mpSparklineGroup)
+ {
+ rDocFunc.ChangeSparklineGroupAttributes(mpSparklineGroup, maAttributes);
+ }
+ else
+ {
+ auto pNewSparklineGroup = std::make_shared<sc::SparklineGroup>(maAttributes);
+ rDocFunc.InsertSparklines(maInputRange, maOutputRange, pNewSparklineGroup);
+ }
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/dialogs/searchresults.cxx b/sc/source/ui/dialogs/searchresults.cxx
new file mode 100644
index 0000000000..4ea08c1d49
--- /dev/null
+++ b/sc/source/ui/dialogs/searchresults.cxx
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <o3tl/safeint.hxx>
+#include <searchresults.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/srchdlg.hxx>
+#include <dociter.hxx>
+#include <document.hxx>
+#include <tabvwsh.hxx>
+#include <strings.hrc>
+#include <sc.hrc>
+#include <scresid.hxx>
+
+namespace sc {
+
+SearchResultsDlg::SearchResultsDlg(SfxBindings* _pBindings, weld::Window* pParent)
+ : SfxDialogController(pParent, "modules/scalc/ui/searchresults.ui", "SearchResultsDialog")
+ , aSkipped(ScResId(SCSTR_SKIPPED))
+ , mpBindings(_pBindings)
+ , mpDoc(nullptr)
+ , mbSorted(false)
+ , mxList(m_xBuilder->weld_tree_view("results"))
+ , mxSearchResults(m_xBuilder->weld_label("lbSearchResults"))
+ , mxShowDialog(m_xBuilder->weld_check_button("cbShow"))
+{
+ mxList->set_size_request(mxList->get_approximate_digit_width() * 50, mxList->get_height_rows(15));
+ mxShowDialog->connect_toggled(LINK(this, SearchResultsDlg, OnShowToggled));
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(mxList->get_approximate_digit_width() * 10),
+ o3tl::narrowing<int>(mxList->get_approximate_digit_width() * 10)
+ };
+ mxList->set_column_fixed_widths(aWidths);
+ mxList->connect_changed(LINK(this, SearchResultsDlg, ListSelectHdl));
+ mxList->connect_column_clicked(LINK(this, SearchResultsDlg, HeaderBarClick));
+}
+
+SearchResultsDlg::~SearchResultsDlg()
+{
+ // tdf#133807 if the search dialog is shown then re-present that dialog
+ // when this results dialog is dismissed
+ SfxViewFrame* pViewFrame = mpBindings->GetDispatcher()->GetFrame();
+ if (!pViewFrame)
+ return;
+ SfxChildWindow* pChildWindow = pViewFrame->GetChildWindow(
+ SvxSearchDialogWrapper::GetChildWindowId());
+ if (!pChildWindow)
+ return;
+ SvxSearchDialog* pSearchDlg = static_cast<SvxSearchDialog*>(pChildWindow->GetController().get());
+ if (!pSearchDlg)
+ return;
+ pSearchDlg->Present();
+}
+
+namespace
+{
+ class ListWrapper {
+ weld::TreeView& mrList;
+ public:
+ size_t mnCount = 0;
+ static const size_t mnMaximum = 1000;
+ ListWrapper(weld::TreeView& rList)
+ : mrList(rList)
+ {
+ mrList.clear();
+ mrList.freeze();
+ }
+ ~ListWrapper()
+ {
+ mrList.thaw();
+ }
+ void Insert(const OUString &rTabName,
+ const ScAddress &rPos,
+ formula::FormulaGrammar::AddressConvention eConvention,
+ const OUString &rText)
+ {
+ if (mnCount++ < mnMaximum)
+ {
+ mrList.append_text(rTabName);
+ int nPos = mrList.n_children() - 1;
+ mrList.set_text(nPos, rPos.Format(ScRefFlags::ADDR_ABS,
+ nullptr, eConvention), 1);
+ mrList.set_text(nPos, rText, 2);
+ }
+ }
+ };
+}
+
+void SearchResultsDlg::FillResults( ScDocument& rDoc, const ScRangeList &rMatchedRanges, bool bCellNotes,
+ bool bEmptyCells, bool bMatchedRangesWereClamped )
+{
+ ListWrapper aList(*mxList);
+ std::vector<OUString> aTabNames = rDoc.GetAllTableNames();
+ SCTAB nTabCount = aTabNames.size();
+
+ // tdf#92160 - too many results blow the widget's mind
+ size_t nMatchMax = rMatchedRanges.size();
+ if (nMatchMax > ListWrapper::mnMaximum)
+ nMatchMax = ListWrapper::mnMaximum;
+
+ if (bCellNotes || bEmptyCells)
+ {
+ for (size_t i = 0, n = nMatchMax; i < n; ++i)
+ {
+ ScRange const & rRange( rMatchedRanges[i] );
+ // Bear in mind that mostly the range is one address position
+ // or a column or a row joined.
+ ScAddress aPos( rRange.aStart );
+ for ( ; aPos.Tab() <= rRange.aEnd.Tab(); aPos.IncTab())
+ {
+ if (aPos.Tab() >= nTabCount)
+ break; // can this even happen? we just searched on existing sheets ...
+ for (aPos.SetCol( rRange.aStart.Col()); aPos.Col() <= rRange.aEnd.Col(); aPos.IncCol())
+ {
+ for (aPos.SetRow( rRange.aStart.Row()); aPos.Row() <= rRange.aEnd.Row(); aPos.IncRow())
+ {
+ if (bCellNotes)
+ {
+ const ScPostIt* pNote = rDoc.GetNote( aPos);
+ if (pNote)
+ aList.Insert(aTabNames[aPos.Tab()], aPos,
+ rDoc.GetAddressConvention(),
+ pNote->GetText());
+ }
+ else // bEmptyCells
+ {
+ aList.Insert(aTabNames[aPos.Tab()], aPos,
+ rDoc.GetAddressConvention(),
+ rDoc.GetString(aPos));
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (size_t i = 0, n = nMatchMax; i < n; ++i)
+ {
+ ScCellIterator aIter(rDoc, rMatchedRanges[i]);
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ const ScAddress& aPos = aIter.GetPos();
+ if (aPos.Tab() >= nTabCount)
+ // Out-of-bound sheet index.
+ continue;
+
+ aList.Insert(aTabNames[aPos.Tab()], aPos,
+ rDoc.GetAddressConvention(),
+ rDoc.GetString(aPos));
+ }
+ }
+ }
+
+ OUString aSearchResultsMsg;
+ if (bMatchedRangesWereClamped)
+ {
+ aSearchResultsMsg = ScResId(SCSTR_RESULTS_CLAMPED);
+ aSearchResultsMsg = aSearchResultsMsg.replaceFirst("%1", OUString::number(1000));
+ }
+ else
+ {
+ OUString aTotal(ScResId(SCSTR_TOTAL, aList.mnCount));
+ aSearchResultsMsg = aTotal.replaceFirst("%1", OUString::number(aList.mnCount));
+ if (aList.mnCount > ListWrapper::mnMaximum)
+ aSearchResultsMsg += " " + ScGlobal::ReplaceOrAppend( aSkipped, u"%1", OUString::number( ListWrapper::mnMaximum ) );
+ }
+ mxSearchResults->set_label(aSearchResultsMsg);
+
+ mpDoc = &rDoc;
+}
+
+void SearchResultsDlg::Close()
+{
+ if (mpBindings)
+ {
+ // Remove this dialog from the view frame after the dialog gets
+ // dismissed, else it would keep popping up endlessly!
+ SfxDispatcher* pDispacher = mpBindings ->GetDispatcher();
+ SfxBoolItem aItem(SID_SEARCH_RESULTS_DIALOG, false);
+ if (pDispacher)
+ {
+ pDispacher->ExecuteList(SID_SEARCH_RESULTS_DIALOG,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD, { &aItem });
+ }
+ }
+
+ SfxDialogController::Close();
+}
+
+IMPL_LINK(SearchResultsDlg, HeaderBarClick, int, nColumn, void)
+{
+ if (!mbSorted)
+ {
+ mxList->make_sorted();
+ mbSorted = true;
+ }
+
+ bool bSortAtoZ = mxList->get_sort_order();
+
+ //set new arrow positions in headerbar
+ if (nColumn == mxList->get_sort_column())
+ {
+ bSortAtoZ = !bSortAtoZ;
+ mxList->set_sort_order(bSortAtoZ);
+ }
+ else
+ {
+ int nOldSortColumn = mxList->get_sort_column();
+ if (nOldSortColumn != -1)
+ mxList->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
+ mxList->set_sort_column(nColumn);
+ }
+
+ if (nColumn != -1)
+ {
+ //sort lists
+ mxList->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
+ }
+}
+
+IMPL_LINK_NOARG( SearchResultsDlg, ListSelectHdl, weld::TreeView&, void )
+{
+ if (!mpDoc)
+ return;
+
+ int nEntry = mxList->get_selected_index();
+ OUString aTabStr = mxList->get_text(nEntry, 0);
+ OUString aPosStr = mxList->get_text(nEntry, 1);
+
+ SCTAB nTab = -1;
+ if (!mpDoc->GetTable(aTabStr, nTab))
+ // No sheet with specified name.
+ return;
+
+ ScAddress aPos;
+ ScRefFlags nRes = aPos.Parse(aPosStr, *mpDoc, mpDoc->GetAddressConvention());
+ if (!(nRes & ScRefFlags::VALID))
+ // Invalid address string.
+ return;
+
+ // Jump to the cell.
+ ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
+ pScViewShell->SetTabNo(nTab);
+ pScViewShell->SetCursor(aPos.Col(), aPos.Row());
+ pScViewShell->AlignToCursor(aPos.Col(), aPos.Row(), SC_FOLLOW_JUMP);
+}
+
+IMPL_STATIC_LINK( SearchResultsDlg, OnShowToggled, weld::Toggleable&, rButton, void )
+{
+ ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
+ ScViewOptions aViewOpt( pScViewShell->GetViewData().GetOptions() );
+ aViewOpt.SetOption( VOPT_SUMMARY, rButton.get_active() );
+ pScViewShell->GetViewData().SetOptions( aViewOpt );
+}
+
+SearchResultsDlgWrapper::SearchResultsDlgWrapper(
+ vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* pBindings, SfxChildWinInfo* /*pInfo*/)
+ : SfxChildWindow(_pParent, nId)
+ , m_xDialog(std::make_shared<SearchResultsDlg>(pBindings, _pParent->GetFrameWeld()))
+{
+ SetController(m_xDialog);
+}
+
+SearchResultsDlgWrapper::~SearchResultsDlgWrapper() {}
+
+SfxChildWinInfo SearchResultsDlgWrapper::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ aInfo.bVisible = false;
+ return aInfo;
+}
+
+SFX_IMPL_CHILDWINDOW_WITHID(SearchResultsDlgWrapper, SID_SEARCH_RESULTS_DIALOG);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/arealink.cxx b/sc/source/ui/docshell/arealink.cxx
new file mode 100644
index 0000000000..26e076bd1c
--- /dev/null
+++ b/sc/source/ui/docshell/arealink.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 <sfx2/fcontnr.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <arealink.hxx>
+
+#include <tablink.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <undoblk.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <markdata.hxx>
+#include <hints.hxx>
+#include <filter.hxx>
+
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+
+#include <scabstdlg.hxx>
+#include <clipparam.hxx>
+
+
+ScAreaLink::ScAreaLink( ScDocShell* pShell, OUString aFile,
+ OUString aFilter, OUString aOpt,
+ OUString aArea, const ScRange& rDest,
+ sal_Int32 nRefreshDelaySeconds ) :
+ ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL,SotClipboardFormatId::SIMPLE_FILE),
+ ScRefreshTimer ( nRefreshDelaySeconds ),
+ m_pDocSh(pShell),
+ aFileName (std::move(aFile)),
+ aFilterName (std::move(aFilter)),
+ aOptions (std::move(aOpt)),
+ aSourceArea (std::move(aArea)),
+ aDestArea (rDest),
+ bAddUndo (true),
+ bInCreate (false),
+ bDoInsert (true)
+{
+ SetRefreshHandler( LINK( this, ScAreaLink, RefreshHdl ) );
+ SetRefreshControl( &m_pDocSh->GetDocument().GetRefreshTimerControlAddress() );
+}
+
+ScAreaLink::~ScAreaLink()
+{
+ StopRefreshTimer();
+}
+
+void ScAreaLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& /* rEndEditHdl */ )
+{
+ // use own dialog instead of SvBaseLink::Edit...
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScLinkedAreaDlg> pDlg(pFact->CreateScLinkedAreaDlg(pParent));
+ pDlg->InitFromOldLink( aFileName, aFilterName, aOptions, aSourceArea, GetRefreshDelaySeconds() );
+ if ( pDlg->Execute() == RET_OK )
+ {
+ aOptions = pDlg->GetOptions();
+ Refresh( pDlg->GetURL(), pDlg->GetFilter(),
+ pDlg->GetSource(), pDlg->GetRefreshDelaySeconds() );
+
+ // copy source data from members (set in Refresh) into link name for dialog
+ OUString aNewLinkName;
+ sfx2::MakeLnkName( aNewLinkName, nullptr, aFileName, aSourceArea, &aFilterName );
+ SetName( aNewLinkName );
+ }
+}
+
+::sfx2::SvBaseLink::UpdateResult ScAreaLink::DataChanged(
+ const OUString&, const css::uno::Any& )
+{
+ // Do not do anything at bInCreate so that update can be called to set
+ // the status in the LinkManager without changing the data in the document
+
+ if (bInCreate)
+ return SUCCESS;
+
+ sfx2::LinkManager* pLinkManager=m_pDocSh->GetDocument().GetLinkManager();
+ if (pLinkManager!=nullptr)
+ {
+ OUString aFile, aArea, aFilter;
+ sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, &aArea, &aFilter);
+
+ // the file dialog returns the filter name with the application prefix
+ // -> remove prefix
+ ScDocumentLoader::RemoveAppPrefix( aFilter );
+
+ // dialog doesn't set area, so keep old one
+ if (aArea.isEmpty())
+ {
+ aArea = aSourceArea;
+
+ // adjust in dialog:
+ OUString aNewLinkName;
+ OUString aTmp = aFilter;
+ sfx2::MakeLnkName(aNewLinkName, nullptr, aFile, aArea, &aTmp);
+ aFilter = aTmp;
+ SetName( aNewLinkName );
+ }
+
+ tools::SvRef<sfx2::SvBaseLink> const xThis(this); // keep yourself alive
+ Refresh( aFile, aFilter, aArea, GetRefreshDelaySeconds() );
+ }
+
+ return SUCCESS;
+}
+
+void ScAreaLink::Closed()
+{
+ // delete link: Undo
+
+ ScDocument& rDoc = m_pDocSh->GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+ if (bAddUndo && bUndo)
+ {
+ m_pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoRemoveAreaLink>( m_pDocSh,
+ aFileName, aFilterName, aOptions,
+ aSourceArea, aDestArea, GetRefreshDelaySeconds() ) );
+
+ bAddUndo = false; // only once
+ }
+
+ SCTAB nDestTab = aDestArea.aStart.Tab();
+ rDoc.SetStreamValid(nDestTab, false);
+
+ SvBaseLink::Closed();
+}
+
+void ScAreaLink::SetDestArea(const ScRange& rNew)
+{
+ aDestArea = rNew; // for Undo
+}
+
+void ScAreaLink::SetSource(const OUString& rDoc, const OUString& rFlt, const OUString& rOpt,
+ const OUString& rArea)
+{
+ aFileName = rDoc;
+ aFilterName = rFlt;
+ aOptions = rOpt;
+ aSourceArea = rArea;
+
+ // also update link name for dialog
+ OUString aNewLinkName;
+ sfx2::MakeLnkName( aNewLinkName, nullptr, aFileName, aSourceArea, &aFilterName );
+ SetName( aNewLinkName );
+}
+
+bool ScAreaLink::IsEqual( std::u16string_view rFile, std::u16string_view rFilter, std::u16string_view rOpt,
+ std::u16string_view rSource, const ScRange& rDest ) const
+{
+ return aFileName == rFile && aFilterName == rFilter && aOptions == rOpt &&
+ aSourceArea == rSource && aDestArea.aStart == rDest.aStart;
+}
+
+// find a range with name >rAreaName< in >rSrcDoc<, return it in >rRange<
+bool ScAreaLink::FindExtRange( ScRange& rRange, const ScDocument& rSrcDoc, const OUString& rAreaName )
+{
+ bool bFound = false;
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rAreaName);
+ ScRangeName* pNames = rSrcDoc.GetRangeName();
+ if (pNames) // named ranges
+ {
+ const ScRangeData* p = pNames->findByUpperName(aUpperName);
+ if (p && p->IsValidReference(rRange))
+ bFound = true;
+ }
+ if (!bFound) // database ranges
+ {
+ ScDBCollection* pDBColl = rSrcDoc.GetDBCollection();
+ if (pDBColl)
+ {
+ const ScDBData* pDB = pDBColl->getNamedDBs().findByUpperName(aUpperName);
+ if (pDB)
+ {
+ SCTAB nTab;
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ pDB->GetArea(nTab,nCol1,nRow1,nCol2,nRow2);
+ rRange = ScRange( nCol1,nRow1,nTab, nCol2,nRow2,nTab );
+ bFound = true;
+ }
+ }
+ }
+ if (!bFound) // direct reference (range or cell)
+ {
+ ScAddress::Details aDetails(rSrcDoc.GetAddressConvention(), 0, 0);
+ if ( rRange.ParseAny( rAreaName, rSrcDoc, aDetails ) & ScRefFlags::VALID )
+ bFound = true;
+ }
+ return bFound;
+}
+
+// execute:
+
+bool ScAreaLink::Refresh( const OUString& rNewFile, const OUString& rNewFilter,
+ const OUString& rNewArea, sal_Int32 nNewRefreshDelaySeconds )
+{
+ // load document - like TabLink
+
+ if (rNewFile.isEmpty() || rNewFilter.isEmpty())
+ return false;
+
+ if (!m_pDocSh->GetEmbeddedObjectContainer().getUserAllowsLinkUpdate())
+ return false;
+
+ OUString aNewUrl( ScGlobal::GetAbsDocName( rNewFile, m_pDocSh ) );
+ bool bNewUrlName = (aNewUrl != aFileName);
+
+ std::shared_ptr<const SfxFilter> pFilter = m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter);
+ if (!pFilter)
+ return false;
+
+ ScDocument& rDoc = m_pDocSh->GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ rDoc.SetInLinkUpdate( true );
+
+ // if new filter was selected, forget options
+ if ( rNewFilter != aFilterName )
+ aOptions.clear();
+
+ SfxMedium* pMed = ScDocumentLoader::CreateMedium( aNewUrl, pFilter, aOptions);
+
+ // aRef->DoClose() will be closed explicitly, but it is still more safe to use SfxObjectShellLock here
+ ScDocShell* pSrcShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS);
+ SfxObjectShellLock aRef = pSrcShell;
+ pSrcShell->DoLoad(pMed);
+
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+
+ // options could have been set
+ OUString aNewOpt = ScDocumentLoader::GetOptions(*pMed);
+ if (aNewOpt.isEmpty())
+ aNewOpt = aOptions;
+
+ // correct source range name list for web query import
+ OUString aTempArea;
+
+ if( rNewFilter == ScDocShell::GetWebQueryFilterName() )
+ aTempArea = ScFormatFilter::Get().GetHTMLRangeNameList( rSrcDoc, rNewArea );
+ else
+ aTempArea = rNewArea;
+
+ // find total size of source area
+ SCCOL nWidth = 0;
+ SCROW nHeight = 0;
+ ScRangeList aSourceRanges;
+
+ if (rNewFilter == SC_TEXT_CSV_FILTER_NAME && aTempArea == "CSV_all")
+ {
+ // The dummy All range. All data, including top/left empty
+ // rows/columns.
+ aTempArea.clear();
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ if (rSrcDoc.GetCellArea( 0, nEndCol, nEndRow))
+ {
+ aSourceRanges.push_back( ScRange( 0,0,0, nEndCol, nEndRow, 0));
+ nWidth = nEndCol + 1;
+ nHeight = nEndRow + 2;
+ }
+ }
+
+ if (!aTempArea.isEmpty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ ScRange aTokenRange;
+ if( FindExtRange( aTokenRange, rSrcDoc, aTempArea.getToken( 0, ';', nIdx ) ) )
+ {
+ aSourceRanges.push_back( aTokenRange);
+ // columns: find maximum
+ nWidth = std::max( nWidth, static_cast<SCCOL>(aTokenRange.aEnd.Col() - aTokenRange.aStart.Col() + 1) );
+ // rows: add row range + 1 empty row
+ nHeight += aTokenRange.aEnd.Row() - aTokenRange.aStart.Row() + 2;
+ }
+ }
+ while (nIdx>0);
+ }
+ // remove the last empty row
+ if( nHeight > 0 )
+ nHeight--;
+
+ // delete old data / copy new
+
+ ScAddress aDestPos = aDestArea.aStart;
+ SCTAB nDestTab = aDestPos.Tab();
+ ScRange aOldRange = aDestArea;
+ ScRange aNewRange = aDestArea; // old range, if file not found or similar
+ if (nWidth > 0 && nHeight > 0)
+ {
+ aNewRange.aEnd.SetCol( aNewRange.aStart.Col() + nWidth - 1 );
+ aNewRange.aEnd.SetRow( aNewRange.aStart.Row() + nHeight - 1 );
+ }
+
+ //! check CanFitBlock only if bDoInsert is set?
+ bool bCanDo = rDoc.ValidColRow( aNewRange.aEnd.Col(), aNewRange.aEnd.Row() ) &&
+ rDoc.CanFitBlock( aOldRange, aNewRange );
+ if (bCanDo)
+ {
+ ScDocShellModificator aModificator( *m_pDocSh );
+
+ SCCOL nOldEndX = aOldRange.aEnd.Col();
+ SCROW nOldEndY = aOldRange.aEnd.Row();
+ SCCOL nNewEndX = aNewRange.aEnd.Col();
+ SCROW nNewEndY = aNewRange.aEnd.Row();
+ ScRange aMaxRange( aDestPos,
+ ScAddress(std::max(nOldEndX,nNewEndX), std::max(nOldEndY,nNewEndY), nDestTab) );
+
+ // initialise Undo
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bAddUndo && bUndo )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ if ( bDoInsert )
+ {
+ if ( nNewEndX != nOldEndX || nNewEndY != nOldEndY ) // range changed?
+ {
+ pUndoDoc->InitUndo( rDoc, 0, rDoc.GetTableCount()-1 );
+ rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc); // all formulas
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nDestTab, nDestTab ); // only destination table
+ rDoc.CopyToDocument(aOldRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc);
+ }
+ else // without insertion
+ {
+ pUndoDoc->InitUndo( rDoc, nDestTab, nDestTab ); // only destination table
+ rDoc.CopyToDocument(aMaxRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc);
+ }
+ }
+
+ // insert / delete cells
+ // DeleteAreaTab also deletes MERGE_FLAG attributes
+
+ if (bDoInsert)
+ rDoc.FitBlock( aOldRange, aNewRange ); // incl. deletion
+ else
+ rDoc.DeleteAreaTab( aMaxRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+
+ // copy data
+
+ if (nWidth > 0 && nHeight > 0)
+ {
+ ScDocument aClipDoc( SCDOCMODE_CLIP );
+ ScRange aNewTokenRange( aNewRange.aStart );
+ for (size_t nRange = 0; nRange < aSourceRanges.size(); ++nRange)
+ {
+ ScRange const & rTokenRange( aSourceRanges[nRange]);
+ SCTAB nSrcTab = rTokenRange.aStart.Tab();
+ ScMarkData aSourceMark(rSrcDoc.GetSheetLimits());
+ aSourceMark.SelectOneTable( nSrcTab ); // selecting for CopyToClip
+ aSourceMark.SetMarkArea( rTokenRange );
+
+ ScClipParam aClipParam(rTokenRange, false);
+ rSrcDoc.CopyToClip(aClipParam, &aClipDoc, &aSourceMark, false, false);
+
+ if ( aClipDoc.HasAttrib( 0,0,nSrcTab, rDoc.MaxCol(),rDoc.MaxRow(),nSrcTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ //! ResetAttrib at document !!!
+
+ ScPatternAttr aPattern( rSrcDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeAttr() ); // Defaults
+ aPattern.GetItemSet().Put( ScMergeFlagAttr() );
+ aClipDoc.ApplyPatternAreaTab( 0,0, rDoc.MaxCol(),rDoc.MaxRow(), nSrcTab, aPattern );
+ }
+
+ aNewTokenRange.aEnd.SetCol( aNewTokenRange.aStart.Col() + (rTokenRange.aEnd.Col() - rTokenRange.aStart.Col()) );
+ aNewTokenRange.aEnd.SetRow( aNewTokenRange.aStart.Row() + (rTokenRange.aEnd.Row() - rTokenRange.aStart.Row()) );
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SelectOneTable( nDestTab );
+ aDestMark.SetMarkArea( aNewTokenRange );
+ rDoc.CopyFromClip( aNewTokenRange, aDestMark, InsertDeleteFlags::ALL, nullptr, &aClipDoc, false );
+ aNewTokenRange.aStart.SetRow( aNewTokenRange.aEnd.Row() + 2 );
+ }
+ }
+ else
+ {
+ OUString aErr = ScResId(STR_LINKERROR);
+ rDoc.SetString( aDestPos.Col(), aDestPos.Row(), aDestPos.Tab(), aErr );
+ }
+
+ // enter Undo
+
+ if ( bAddUndo && bUndo)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nDestTab, nDestTab );
+ rDoc.CopyToDocument(aNewRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pRedoDoc);
+
+ m_pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoUpdateAreaLink>( m_pDocSh,
+ aFileName, aFilterName, aOptions,
+ aSourceArea, aOldRange, GetRefreshDelaySeconds(),
+ aNewUrl, rNewFilter, aNewOpt,
+ rNewArea, aNewRange, nNewRefreshDelaySeconds,
+ std::move(pUndoDoc), std::move(pRedoDoc), bDoInsert ) );
+ }
+
+ // remember new settings
+
+ if ( bNewUrlName )
+ aFileName = aNewUrl;
+ if ( rNewFilter != aFilterName )
+ aFilterName = rNewFilter;
+ if ( rNewArea != aSourceArea )
+ aSourceArea = rNewArea;
+ if ( aNewOpt != aOptions )
+ aOptions = aNewOpt;
+
+ if ( aNewRange != aDestArea )
+ aDestArea = aNewRange;
+
+ if ( nNewRefreshDelaySeconds != GetRefreshDelaySeconds() )
+ SetRefreshDelay( nNewRefreshDelaySeconds );
+
+ SCCOL nPaintEndX = std::max( aOldRange.aEnd.Col(), aNewRange.aEnd.Col() );
+ SCROW nPaintEndY = std::max( aOldRange.aEnd.Row(), aNewRange.aEnd.Row() );
+
+ if ( aOldRange.aEnd.Col() != aNewRange.aEnd.Col() )
+ nPaintEndX = rDoc.MaxCol();
+ if ( aOldRange.aEnd.Row() != aNewRange.aEnd.Row() )
+ nPaintEndY = rDoc.MaxRow();
+
+ if ( !m_pDocSh->AdjustRowHeight( aDestPos.Row(), nPaintEndY, nDestTab ) )
+ m_pDocSh->PostPaint(
+ ScRange(aDestPos.Col(), aDestPos.Row(), nDestTab, nPaintEndX, nPaintEndY, nDestTab),
+ PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+ }
+ else
+ {
+ // CanFitBlock sal_False -> Problems with summarized cells or table boundary reached!
+ //! cell protection ???
+
+ //! Link dialog must set default parent
+ // "cannot insert rows"
+ weld::Window* pWin = Application::GetFrameWeld(m_pDocSh->GetDialogParent());
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_MSSG_DOSUBTOTALS_2)));
+ xInfoBox->run();
+ }
+
+ // clean up
+
+ aRef->DoClose();
+
+ rDoc.SetInLinkUpdate( false );
+
+ if (bCanDo)
+ {
+ // notify Uno objects (for XRefreshListener)
+ //! also notify Uno objects if file name was changed!
+ ScLinkRefreshedHint aHint;
+ aHint.SetAreaLink( aDestPos );
+ rDoc.BroadcastUno( aHint );
+ }
+
+ return bCanDo;
+}
+
+IMPL_LINK_NOARG(ScAreaLink, RefreshHdl, Timer *, void)
+{
+ Refresh( aFileName, aFilterName, aSourceArea, GetRefreshDelaySeconds() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/autostyl.cxx b/sc/source/ui/docshell/autostyl.cxx
new file mode 100644
index 0000000000..5b6eaa30c2
--- /dev/null
+++ b/sc/source/ui/docshell/autostyl.cxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <time.h>
+#include <osl/diagnose.h>
+
+#include <address.hxx>
+#include <autostyl.hxx>
+#include <docsh.hxx>
+
+static sal_uLong TimeNow() // seconds
+{
+ return static_cast<sal_uLong>(time(nullptr));
+}
+
+namespace {
+
+class FindByRange
+{
+ ScRange maRange;
+public:
+ explicit FindByRange(const ScRange& r) : maRange(r) {}
+ bool operator() (const ScAutoStyleData& rData) const { return rData.aRange == maRange; }
+};
+
+class FindByTimeout
+{
+ sal_uLong mnTimeout;
+public:
+ explicit FindByTimeout(sal_uLong n) : mnTimeout(n) {}
+ bool operator() (const ScAutoStyleData& rData) const { return rData.nTimeout >= mnTimeout; }
+};
+
+struct FindNonZeroTimeout
+{
+ bool operator() (const ScAutoStyleData& rData) const
+ {
+ return rData.nTimeout != 0;
+ }
+};
+
+}
+
+ScAutoStyleList::ScAutoStyleList(ScDocShell* pShell)
+ : pDocSh(pShell)
+ , aTimer("ScAutoStyleList Timer")
+ , aInitIdle("ScAutoStyleList InitIdle")
+ , nTimerStart(0)
+{
+ aTimer.SetInvokeHandler( LINK( this, ScAutoStyleList, TimerHdl ) );
+ aInitIdle.SetInvokeHandler( LINK( this, ScAutoStyleList, InitHdl ) );
+ aInitIdle.SetPriority( TaskPriority::HIGHEST );
+}
+
+ScAutoStyleList::~ScAutoStyleList()
+{
+}
+
+// initial short delay (asynchronous call)
+
+void ScAutoStyleList::AddInitial( const ScRange& rRange, const OUString& rStyle1,
+ sal_uLong nTimeout, const OUString& rStyle2 )
+{
+ aInitials.emplace_back( rRange, rStyle1, nTimeout, rStyle2 );
+ aInitIdle.Start();
+}
+
+IMPL_LINK_NOARG(ScAutoStyleList, InitHdl, Timer *, void)
+{
+ std::vector<ScAutoStyleInitData> aLocalInitials(std::move(aInitials));
+ for (const auto& rInitial : aLocalInitials)
+ {
+ // apply first style immediately
+ pDocSh->DoAutoStyle(rInitial.aRange, rInitial.aStyle1);
+
+ // add second style to list
+ if (rInitial.nTimeout)
+ AddEntry(rInitial.nTimeout, rInitial.aRange, rInitial.aStyle2 );
+ }
+}
+
+void ScAutoStyleList::AddEntry( sal_uLong nTimeout, const ScRange& rRange, const OUString& rStyle )
+{
+ aTimer.Stop();
+ sal_uLong nNow = TimeNow();
+
+ // Remove the first item with the same range.
+ std::vector<ScAutoStyleData>::iterator itr =
+ ::std::find_if(aEntries.begin(), aEntries.end(), FindByRange(rRange));
+
+ if (itr != aEntries.end())
+ aEntries.erase(itr);
+
+ // adjust timeouts of all entries
+
+ if (!aEntries.empty() && nNow != nTimerStart)
+ {
+ OSL_ENSURE(nNow>nTimerStart, "Time is running backwards?");
+ AdjustEntries((nNow-nTimerStart)*1000);
+ }
+
+ // find insert position
+ std::vector<ScAutoStyleData>::iterator iter =
+ ::std::find_if(aEntries.begin(), aEntries.end(), FindByTimeout(nTimeout));
+
+ aEntries.insert(iter, ScAutoStyleData(nTimeout,rRange,rStyle));
+
+ // execute expired, restart timer
+
+ ExecuteEntries();
+ StartTimer(nNow);
+}
+
+void ScAutoStyleList::AdjustEntries( sal_uLong nDiff ) // milliseconds
+{
+ for (auto& rEntry : aEntries)
+ {
+ if (rEntry.nTimeout <= nDiff)
+ rEntry.nTimeout = 0; // expired
+ else
+ rEntry.nTimeout -= nDiff; // continue counting
+ }
+}
+
+void ScAutoStyleList::ExecuteEntries()
+{
+ // Execute and remove all items with timeout == 0 from the begin position
+ // until the first item with non-zero timeout value.
+ std::vector<ScAutoStyleData>::iterator itr = aEntries.begin(), itrEnd = aEntries.end();
+ for (; itr != itrEnd; ++itr)
+ {
+ if (itr->nTimeout)
+ break;
+
+ pDocSh->DoAutoStyle(itr->aRange, itr->aStyle);
+ }
+ // At this point itr should be on the first item with non-zero timeout, or
+ // the end position in case all items have timeout == 0.
+ aEntries.erase(aEntries.begin(), itr);
+}
+
+void ScAutoStyleList::ExecuteAllNow()
+{
+ aTimer.Stop();
+
+ for (const auto& rEntry : aEntries)
+ pDocSh->DoAutoStyle(rEntry.aRange, rEntry.aStyle);
+
+ aEntries.clear();
+}
+
+void ScAutoStyleList::StartTimer( sal_uLong nNow ) // seconds
+{
+ // find first entry with Timeout != 0
+ std::vector<ScAutoStyleData>::iterator iter =
+ ::std::find_if(aEntries.begin(),aEntries.end(), FindNonZeroTimeout());
+
+ if (iter != aEntries.end())
+ {
+ aTimer.SetTimeout(iter->nTimeout);
+ aTimer.Start();
+ }
+
+ nTimerStart = nNow;
+}
+
+IMPL_LINK_NOARG(ScAutoStyleList, TimerHdl, Timer *, void)
+{
+ sal_uLong nNow = TimeNow();
+ AdjustEntries(aTimer.GetTimeout()); // the set waiting time
+ ExecuteEntries();
+ StartTimer(nNow);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/datastream.cxx b/sc/source/ui/docshell/datastream.cxx
new file mode 100644
index 0000000000..998985bb78
--- /dev/null
+++ b/sc/source/ui/docshell/datastream.cxx
@@ -0,0 +1,549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <datastream.hxx>
+#include <datastreamgettime.hxx>
+
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <osl/conditn.hxx>
+#include <osl/time.h>
+#include <salhelper/thread.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/stream.hxx>
+#include <vcl/svapp.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <stringutil.hxx>
+#include <documentlinkmgr.hxx>
+#include <o3tl/enumarray.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+
+#include <orcus/csv_parser.hpp>
+
+#include <atomic>
+#include <queue>
+
+namespace com::sun::star::ui { class XUIElement; }
+
+namespace sc {
+
+static o3tl::enumarray<DebugTime, double> fTimes { 0.0, 0.0, 0.0 };
+
+double datastream_get_time(DebugTime nIdx)
+{
+ return fTimes[ nIdx ];
+}
+
+namespace {
+
+double getNow()
+{
+ TimeValue now;
+ osl_getSystemTime(&now);
+ return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
+}
+
+class CSVHandler
+{
+ DataStream::Line& mrLine;
+ size_t mnColCount;
+ size_t mnCols;
+ const char* mpLineHead;
+
+public:
+ CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
+ mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}
+
+ static void begin_parse() {}
+ static void end_parse() {}
+ static void begin_row() {}
+ static void end_row() {}
+
+ void cell(std::string_view s, bool /*transient*/)
+ {
+ if (mnCols >= mnColCount)
+ return;
+
+ DataStream::Cell aCell;
+ if (ScStringUtil::parseSimpleNumber(s.data(), s.size(), '.', ',', aCell.mfValue))
+ {
+ aCell.mbValue = true;
+ }
+ else
+ {
+ aCell.mbValue = false;
+ aCell.maStr.Pos = std::distance(mpLineHead, s.data());
+ aCell.maStr.Size = s.size();
+ }
+ mrLine.maCells.push_back(aCell);
+
+ ++mnCols;
+ }
+};
+
+}
+
+namespace datastreams {
+
+class ReaderThread : public salhelper::Thread
+{
+ std::unique_ptr<SvStream> mpStream;
+ size_t mnColCount;
+ std::atomic<bool> mbTerminate;
+
+ std::queue<DataStream::LinesType> maPendingLines;
+ std::queue<DataStream::LinesType> maUsedLines;
+ std::mutex maMtxLines;
+
+ osl::Condition maCondReadStream;
+ osl::Condition maCondConsume;
+
+ orcus::csv::parser_config maConfig;
+
+public:
+
+ ReaderThread(std::unique_ptr<SvStream> pData, size_t nColCount):
+ Thread("ReaderThread"),
+ mpStream(std::move(pData)),
+ mnColCount(nColCount),
+ mbTerminate(false)
+ {
+ maConfig.delimiters.push_back(',');
+ maConfig.text_qualifier = '"';
+ }
+
+ bool isTerminateRequested()
+ {
+ return mbTerminate.load();
+ }
+
+ void requestTerminate()
+ {
+ mbTerminate.store(true);
+ }
+
+ void endThread()
+ {
+ requestTerminate();
+ maCondReadStream.set();
+ }
+
+ void waitForNewLines()
+ {
+ maCondConsume.wait();
+ maCondConsume.reset();
+ }
+
+ DataStream::LinesType popNewLines()
+ {
+ auto pLines = std::move(maPendingLines.front());
+ maPendingLines.pop();
+ return pLines;
+ }
+
+ void resumeReadStream()
+ {
+ if (maPendingLines.size() <= 4)
+ maCondReadStream.set(); // start producer again
+ }
+
+ bool hasNewLines() const
+ {
+ return !maPendingLines.empty();
+ }
+
+ void pushUsedLines( DataStream::LinesType pLines )
+ {
+ maUsedLines.push(std::move(pLines));
+ }
+
+ std::mutex& getLinesMutex()
+ {
+ return maMtxLines;
+ }
+
+private:
+ virtual void execute() override
+ {
+ while (!isTerminateRequested())
+ {
+ std::optional<DataStream::LinesType> oLines;
+ std::unique_lock aGuard(maMtxLines);
+
+ if (!maUsedLines.empty())
+ {
+ // Re-use lines from previous runs.
+ oLines = std::move(maUsedLines.front());
+ maUsedLines.pop();
+ aGuard.unlock(); // unlock
+ }
+ else
+ {
+ aGuard.unlock(); // unlock
+ oLines.emplace(10);
+ }
+
+ // Read & store new lines from stream.
+ for (DataStream::Line & rLine : *oLines)
+ {
+ rLine.maCells.clear();
+ mpStream->ReadLine(rLine.maLine);
+ CSVHandler aHdl(rLine, mnColCount);
+ orcus::csv_parser<CSVHandler> parser(rLine.maLine, aHdl, maConfig);
+ parser.parse();
+ }
+
+ aGuard.lock(); // lock
+ while (!isTerminateRequested() && maPendingLines.size() >= 8)
+ {
+ // pause reading for a bit
+ aGuard.unlock(); // unlock
+ maCondReadStream.wait();
+ maCondReadStream.reset();
+ aGuard.lock(); // lock
+ }
+ maPendingLines.push(std::move(*oLines));
+ maCondConsume.set();
+ if (!mpStream->good())
+ requestTerminate();
+ }
+ }
+};
+
+}
+
+DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
+
+DataStream::Cell::Cell( const Cell& r ) : mbValue(r.mbValue)
+{
+ if (r.mbValue)
+ mfValue = r.mfValue;
+ else
+ {
+ maStr.Pos = r.maStr.Pos;
+ maStr.Size = r.maStr.Size;
+ }
+}
+
+void DataStream::MakeToolbarVisible()
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ css::uno::Reference< css::frame::XFrame > xFrame =
+ pViewData->GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
+ if (!xFrame.is())
+ return;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
+ if (!xPropSet.is())
+ return;
+
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ if (!xLayoutManager.is())
+ return;
+
+ static constexpr OUString sResourceURL( u"private:resource/toolbar/datastreams"_ustr );
+ css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
+ if (!xUIElement.is())
+ {
+ xLayoutManager->createElement( sResourceURL );
+ xLayoutManager->showElement( sResourceURL );
+ }
+}
+
+DataStream* DataStream::Set(
+ ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
+ sal_Int32 nLimit, MoveType eMove)
+{
+ DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove);
+ sc::DocumentLinkManager& rMgr = pShell->GetDocument().GetDocLinkManager();
+ rMgr.setDataStream(pLink);
+ return pLink;
+}
+
+DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
+ sal_Int32 nLimit, MoveType eMove) :
+ mpDocShell(pShell),
+ maDocAccess(mpDocShell->GetDocument()),
+ meOrigMove(NO_MOVE),
+ meMove(NO_MOVE),
+ mbRunning(false),
+ mbValuesInLine(false),
+ mbRefreshOnEmptyLine(false),
+ mnLinesCount(0),
+ mnLinesSinceRefresh(0),
+ mfLastRefreshTime(0.0),
+ mnCurRow(0),
+ maImportTimer("sc DataStream maImportTimer"),
+ mbIsFirst(true),
+ mbIsUpdate(false)
+{
+ maImportTimer.SetTimeout(0);
+ maImportTimer.SetInvokeHandler( LINK(this, DataStream, ImportTimerHdl) );
+
+ Decode(rURL, rRange, nLimit, eMove);
+}
+
+DataStream::~DataStream()
+{
+ if (mbRunning)
+ StopImport();
+
+ if (mxReaderThread.is())
+ {
+ mxReaderThread->endThread();
+ mxReaderThread->join();
+ }
+ moLines.reset();
+}
+
+DataStream::Line DataStream::ConsumeLine()
+{
+ if (!moLines || mnLinesCount >= moLines->size())
+ {
+ mnLinesCount = 0;
+ if (mxReaderThread->isTerminateRequested())
+ return Line();
+
+ std::unique_lock aGuard(mxReaderThread->getLinesMutex());
+ if (moLines)
+ {
+ mxReaderThread->pushUsedLines(std::move(*moLines));
+ moLines.reset();
+ }
+
+ while (!mxReaderThread->hasNewLines())
+ {
+ aGuard.unlock(); // unlock
+ mxReaderThread->waitForNewLines();
+ aGuard.lock(); // lock
+ }
+
+ moLines = mxReaderThread->popNewLines();
+ mxReaderThread->resumeReadStream();
+ }
+ return moLines->at(mnLinesCount++);
+}
+
+ScRange DataStream::GetRange() const
+{
+ ScRange aRange = maStartRange;
+ aRange.aEnd = maEndRange.aEnd;
+ return aRange;
+}
+
+void DataStream::Decode(const OUString& rURL, const ScRange& rRange,
+ sal_Int32 nLimit, MoveType eMove)
+{
+ msURL = rURL;
+ meMove = eMove;
+ meOrigMove = eMove;
+
+ mbValuesInLine = true; // always true.
+
+ mnCurRow = rRange.aStart.Row();
+
+ ScRange aRange = rRange;
+ if (aRange.aStart.Row() != aRange.aEnd.Row())
+ // We only allow this range to be one row tall.
+ aRange.aEnd.SetRow(aRange.aStart.Row());
+
+ maStartRange = aRange;
+ maEndRange = aRange;
+ const auto & rDoc = mpDocShell->GetDocument();
+ if (nLimit == 0)
+ {
+ // Unlimited
+ maEndRange.aStart.SetRow(rDoc.MaxRow());
+ }
+ else if (nLimit > 0)
+ {
+ // Limited.
+ maEndRange.aStart.IncRow(nLimit-1);
+ if (maEndRange.aStart.Row() > rDoc.MaxRow())
+ maEndRange.aStart.SetRow(rDoc.MaxRow());
+ }
+
+ maEndRange.aEnd.SetRow(maEndRange.aStart.Row());
+}
+
+void DataStream::StartImport()
+{
+ if (mbRunning)
+ return;
+
+ if (!mxReaderThread.is())
+ {
+ std::unique_ptr<SvStream> pStream(new SvFileStream(msURL, StreamMode::READ));
+ mxReaderThread = new datastreams::ReaderThread(std::move(pStream), maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
+ mxReaderThread->launch();
+ }
+ mbRunning = true;
+ maDocAccess.reset();
+
+ maImportTimer.Start();
+}
+
+void DataStream::StopImport()
+{
+ if (!mbRunning)
+ return;
+
+ mbRunning = false;
+ Refresh();
+ maImportTimer.Stop();
+}
+
+void DataStream::SetRefreshOnEmptyLine( bool bVal )
+{
+ mbRefreshOnEmptyLine = bVal;
+}
+
+void DataStream::Refresh()
+{
+ Application::Yield();
+
+ double fStart = getNow();
+
+ // Hard recalc will repaint the grid area.
+ mpDocShell->DoHardRecalc();
+ mpDocShell->SetDocumentModified();
+
+ fTimes[ DebugTime::Recalc ] = getNow() - fStart;
+
+ mfLastRefreshTime = getNow();
+ mnLinesSinceRefresh = 0;
+}
+
+void DataStream::MoveData()
+{
+ switch (meMove)
+ {
+ case RANGE_DOWN:
+ {
+ if (mnCurRow == maEndRange.aStart.Row())
+ meMove = MOVE_UP;
+ }
+ break;
+ case MOVE_UP:
+ {
+ mbIsUpdate = true;
+ // Remove the top row and shift the remaining rows upward. Then
+ // insert a new row at the end row position.
+ ScRange aRange = maStartRange;
+ aRange.aEnd = maEndRange.aEnd;
+ maDocAccess.shiftRangeUp(aRange);
+ }
+ break;
+ case MOVE_DOWN:
+ {
+ mbIsUpdate = true;
+ // Remove the end row and shift the remaining rows downward by
+ // inserting a new row at the top row.
+ ScRange aRange = maStartRange;
+ aRange.aEnd = maEndRange.aEnd;
+ maDocAccess.shiftRangeDown(aRange);
+ }
+ break;
+ case NO_MOVE:
+ default:
+ ;
+ }
+ if(mbIsFirst && mbIsUpdate)
+ {
+ sal_Int32 nStreamTimeout = officecfg::Office::Calc::DataStream::UpdateTimeout::get();
+ maImportTimer.SetTimeout(nStreamTimeout);
+ mbIsFirst = false;
+ }
+}
+
+void DataStream::Text2Doc()
+{
+ Line aLine = ConsumeLine();
+ if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
+ {
+ // Empty line detected. Trigger refresh and discard it.
+ Refresh();
+ return;
+ }
+
+ double fStart = getNow();
+
+ MoveData();
+ {
+ SCCOL nCol = maStartRange.aStart.Col();
+ const char* pLineHead = aLine.maLine.getStr();
+ for (const Cell& rCell : aLine.maCells)
+ {
+ if (rCell.mbValue)
+ {
+ maDocAccess.setNumericCell(
+ ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
+ }
+ else
+ {
+ maDocAccess.setStringCell(
+ ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
+ OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
+ }
+ ++nCol;
+ }
+ }
+
+ fTimes[ DebugTime::Import ] = getNow() - fStart;
+
+ if (meMove == NO_MOVE)
+ return;
+
+ if (meMove == RANGE_DOWN)
+ {
+ ++mnCurRow;
+// mpDocShell->GetViewData().GetView()->AlignToCursor(
+// maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
+ }
+
+ if (getNow() - mfLastRefreshTime > 0.1 && mnLinesSinceRefresh > 200)
+ // Refresh no more frequently than every 0.1 second, and wait until at
+ // least we have processed 200 lines.
+ Refresh();
+
+ ++mnLinesSinceRefresh;
+}
+
+bool DataStream::ImportData()
+{
+ if (!mbValuesInLine)
+ // We no longer support this mode. To be deleted later.
+ return false;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return false;
+
+ if (pViewData->GetViewShell()->NeedsRepaint())
+ return mbRunning;
+
+ Text2Doc();
+ return mbRunning;
+}
+
+IMPL_LINK_NOARG(DataStream, ImportTimerHdl, Timer *, void)
+{
+ if (ImportData())
+ maImportTimer.Start();
+}
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx
new file mode 100644
index 0000000000..ee59f36232
--- /dev/null
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -0,0 +1,1790 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/app.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdoole2.hxx>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <unotools/charclass.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <dbdocfun.hxx>
+#include <dbdata.hxx>
+#include <undodat.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <globalnames.hxx>
+#include <tabvwsh.hxx>
+#include <patattr.hxx>
+#include <rangenam.hxx>
+#include <olinetab.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dociter.hxx>
+#include <editable.hxx>
+#include <attrib.hxx>
+#include <drwlayer.hxx>
+#include <dpshttab.hxx>
+#include <hints.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <progress.hxx>
+#include <undosort.hxx>
+#include <inputopt.hxx>
+#include <scmod.hxx>
+
+#include <chartlis.hxx>
+#include <ChartTools.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange )
+{
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ std::unique_ptr<ScDBCollection> pUndoColl;
+ if (bUndo)
+ pUndoColl.reset( new ScDBCollection( *pDocColl ) );
+
+ std::unique_ptr<ScDBData> pNew(new ScDBData( rName, rRange.aStart.Tab(),
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() ));
+
+ // #i55926# While loading XML, formula cells only have a single string token,
+ // so CompileDBFormula would never find any name (index) tokens, and would
+ // unnecessarily loop through all cells.
+ bool bCompile = !rDoc.IsImportingXML();
+ bool bOk;
+ if ( bCompile )
+ rDoc.PreprocessDBDataUpdate();
+ if ( rName == STR_DB_LOCAL_NONAME )
+ {
+ rDoc.SetAnonymousDBData(rRange.aStart.Tab(), std::move(pNew));
+ bOk = true;
+ }
+ else
+ {
+ bOk = pDocColl->getNamedDBs().insert(std::move(pNew));
+ }
+ if ( bCompile )
+ rDoc.CompileHybridFormula();
+
+ if (!bOk)
+ {
+ return false;
+ }
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
+ std::make_unique<ScDBCollection>( *pDocColl ) ) );
+ }
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ return true;
+}
+
+bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
+{
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
+ auto const iter = rDBs.findByUpperName2(ScGlobal::getCharClass().uppercase(rName));
+ if (iter != rDBs.end())
+ {
+ ScDocShellModificator aModificator( rDocShell );
+
+ std::unique_ptr<ScDBCollection> pUndoColl;
+ if (bUndo)
+ pUndoColl.reset( new ScDBCollection( *pDocColl ) );
+
+ rDoc.PreprocessDBDataUpdate();
+ rDBs.erase(iter);
+ rDoc.CompileHybridFormula();
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
+ std::make_unique<ScDBCollection>( *pDocColl ) ) );
+ }
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ bDone = true;
+ }
+
+ return bDone;
+}
+
+bool ScDBDocFunc::RenameDBRange( const OUString& rOld, const OUString& rNew )
+{
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
+ auto const iterOld = rDBs.findByUpperName2(ScGlobal::getCharClass().uppercase(rOld));
+ const ScDBData* pNew = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(rNew));
+ if (iterOld != rDBs.end() && !pNew)
+ {
+ ScDocShellModificator aModificator( rDocShell );
+
+ std::unique_ptr<ScDBData> pNewData(new ScDBData(rNew, **iterOld));
+
+ std::unique_ptr<ScDBCollection> pUndoColl( new ScDBCollection( *pDocColl ) );
+
+ rDoc.PreprocessDBDataUpdate();
+ rDBs.erase(iterOld);
+ bool bInserted = rDBs.insert(std::move(pNewData));
+ if (!bInserted) // error -> restore old state
+ {
+ rDoc.SetDBCollection(std::move(pUndoColl)); // belongs to the document then
+ }
+
+ rDoc.CompileHybridFormula();
+
+ if (bInserted) // insertion worked
+ {
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
+ std::make_unique<ScDBCollection>( *pDocColl ) ) );
+ }
+ else
+ pUndoColl.reset();
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ bDone = true;
+ }
+ }
+
+ return bDone;
+}
+
+void ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ ScDBData* pData = nullptr;
+ if (rNewData.GetName() == STR_DB_LOCAL_NONAME)
+ {
+ ScRange aRange;
+ rNewData.GetArea(aRange);
+ SCTAB nTab = aRange.aStart.Tab();
+ pData = rDoc.GetAnonymousDBData(nTab);
+ }
+ else
+ pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName());
+
+ if (!pData)
+ return;
+
+ ScDocShellModificator aModificator( rDocShell );
+ ScRange aOldRange, aNewRange;
+ pData->GetArea(aOldRange);
+ rNewData.GetArea(aNewRange);
+ bool bAreaChanged = ( aOldRange != aNewRange ); // then a recompilation is needed
+
+ std::unique_ptr<ScDBCollection> pUndoColl;
+ if (bUndo)
+ pUndoColl.reset( new ScDBCollection( *pDocColl ) );
+
+ *pData = rNewData;
+ if (bAreaChanged)
+ rDoc.CompileDBFormula();
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDBData>( &rDocShell, std::move(pUndoColl),
+ std::make_unique<ScDBCollection>( *pDocColl ) ) );
+ }
+
+ aModificator.SetDocumentModified();
+}
+
+void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector<ScRange>& rDelAreaList )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScDBCollection* pOldColl = rDoc.GetDBCollection();
+ std::unique_ptr<ScDBCollection> pUndoColl;
+ bool bRecord = rDoc.IsUndoEnabled();
+
+ for (const auto& rDelArea : rDelAreaList)
+ {
+ // unregistering target in SBA no longer necessary
+ const ScAddress& rStart = rDelArea.aStart;
+ const ScAddress& rEnd = rDelArea.aEnd;
+ rDocShell.DBAreaDeleted(
+ rStart.Tab(), rStart.Col(), rStart.Row(), rEnd.Col());
+ }
+
+ if (bRecord)
+ pUndoColl.reset( new ScDBCollection( *pOldColl ) );
+
+ // register target in SBA no longer necessary
+
+ rDoc.PreprocessDBDataUpdate();
+ rDoc.SetDBCollection( std::unique_ptr<ScDBCollection>(new ScDBCollection( rNewColl )) );
+ rDoc.CompileHybridFormula();
+ pOldColl = nullptr;
+ rDocShell.PostPaint(ScRange(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB), PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDBData>(&rDocShell, std::move(pUndoColl),
+ std::make_unique<ScDBCollection>(rNewColl)));
+ }
+}
+
+bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bApi, bool bIsUnnamed, SCTAB aTab )
+{
+ //! use also for ScDBFunc::RepeatDB !
+
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScDBData* pDBData = nullptr;
+ if (bIsUnnamed)
+ {
+ pDBData = rDoc.GetAnonymousDBData( aTab );
+ }
+ else
+ {
+ ScDBCollection* pColl = rDoc.GetDBCollection();
+ if (pColl)
+ pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
+ }
+
+ if ( pDBData )
+ {
+ ScQueryParam aQueryParam;
+ pDBData->GetQueryParam( aQueryParam );
+ bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
+
+ ScSortParam aSortParam;
+ pDBData->GetSortParam( aSortParam );
+ bool bSort = aSortParam.maKeyState[0].bDoSort;
+
+ ScSubTotalParam aSubTotalParam;
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
+
+ if ( bQuery || bSort || bSubTotal )
+ {
+ bool bQuerySize = false;
+ ScRange aOldQuery;
+ ScRange aNewQuery;
+ if (bQuery && !aQueryParam.bInplace)
+ {
+ ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDest && pDest->IsDoSize())
+ {
+ pDest->GetArea( aOldQuery );
+ bQuerySize = true;
+ }
+ }
+
+ SCTAB nTab;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+
+ //! Undo needed data only ?
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::unique_ptr<ScRangeName> pUndoRange;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+
+ if (bRecord)
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ // column/row state
+ SCCOLROW nOutStartCol, nOutEndCol;
+ SCCOLROW nOutStartRow, nOutEndRow;
+ pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
+ pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0,
+ nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab,
+ InsertDeleteFlags::NONE, false, *pUndoDoc);
+ rDoc.CopyToDocument(0, static_cast<SCROW>(nOutStartRow),
+ nTab, rDoc.MaxCol(), static_cast<SCROW>(nOutEndRow), nTab,
+ InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+
+ // secure data range - incl. filtering result
+ rDoc.CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ // all formulas because of references
+ rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc);
+
+ // ranges of DB and other
+ ScRangeName* pDocRange = rDoc.GetRangeName();
+ if (!pDocRange->empty())
+ pUndoRange.reset(new ScRangeName( *pDocRange ));
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+ }
+
+ if (bSort && bSubTotal)
+ {
+ // sort without SubTotals
+
+ aSubTotalParam.bRemoveOnly = true; // will be reset again further down
+ DoSubTotals( nTab, aSubTotalParam, false, bApi );
+ }
+
+ if (bSort)
+ {
+ pDBData->GetSortParam( aSortParam ); // range may have changed
+ (void)Sort( nTab, aSortParam, false, false, bApi );
+ }
+ if (bQuery)
+ {
+ pDBData->GetQueryParam( aQueryParam ); // range may have changed
+ ScRange aAdvSource;
+ if (pDBData->GetAdvancedQuerySource(aAdvSource))
+ Query( nTab, aQueryParam, &aAdvSource, false, bApi );
+ else
+ Query( nTab, aQueryParam, nullptr, false, bApi );
+
+ // at not-inplace the table may have been converted
+// if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
+// SetTabNo( nTab );
+ }
+ if (bSubTotal)
+ {
+ pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed
+ aSubTotalParam.bRemoveOnly = false;
+ DoSubTotals( nTab, aSubTotalParam, false, bApi );
+ }
+
+ if (bRecord)
+ {
+ SCTAB nDummyTab;
+ SCCOL nDummyCol;
+ SCROW nDummyRow;
+ SCROW nNewEndRow;
+ pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
+
+ const ScRange* pOld = nullptr;
+ const ScRange* pNew = nullptr;
+ if (bQuerySize)
+ {
+ ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDest)
+ {
+ pDest->GetArea( aNewQuery );
+ pOld = &aOldQuery;
+ pNew = &aNewQuery;
+ }
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRepeatDB>( &rDocShell, nTab,
+ nStartCol, nStartRow, nEndCol, nEndRow,
+ nNewEndRow,
+ //nCurX, nCurY,
+ nStartCol, nStartRow,
+ std::move(pUndoDoc), std::move(pUndoTab),
+ std::move(pUndoRange), std::move(pUndoDB),
+ pOld, pNew ) );
+ }
+
+ rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
+ bDone = true;
+ }
+ else if (!bApi) // "Don't execute any operations"
+ rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
+ }
+
+ return bDone;
+}
+
+bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
+ bool bRecord, bool bPaint, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
+ rSortParam.nCol2, rSortParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "Sort: no DBData" );
+ return false;
+ }
+
+ bool bCopy = !rSortParam.bInplace;
+ if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
+ rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
+ bCopy = false;
+
+ ScSortParam aLocalParam( rSortParam );
+ if ( bCopy )
+ {
+ // Copy the data range to the destination then move the sort range to it.
+ ScRange aSrcRange(rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab);
+ ScAddress aDestPos(rSortParam.nDestCol,rSortParam.nDestRow,rSortParam.nDestTab);
+
+ ScDocFunc& rDocFunc = rDocShell.GetDocFunc();
+ bool bRet = rDocFunc.MoveBlock(aSrcRange, aDestPos, false, bRecord, bPaint, bApi);
+
+ if (!bRet)
+ return false;
+
+ aLocalParam.MoveToDest();
+ nTab = aLocalParam.nDestTab;
+ }
+
+ // tdf#119804: If there is a header row/column, it won't be affected by
+ // sorting; so we can exclude it from the test.
+ SCROW nStartingRowToEdit = aLocalParam.nRow1;
+ SCCOL nStartingColToEdit = aLocalParam.nCol1;
+ if ( aLocalParam.bHasHeader )
+ {
+ if ( aLocalParam.bByRow )
+ nStartingRowToEdit++;
+ else
+ nStartingColToEdit++;
+ }
+ ScEditableTester aTester( rDoc, nTab, nStartingColToEdit, nStartingRowToEdit,
+ aLocalParam.nCol2, aLocalParam.nRow2, true /*bNoMatrixAtAll*/ );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ const ScInputOptions aInputOption = SC_MOD()->GetInputOptions();
+ const bool bUpdateRefs = aInputOption.GetSortRefUpdate();
+
+ // Adjust aLocalParam cols/rows to used data area. Keep sticky top row or
+ // column (depending on direction) in any case, not just if it has headers,
+ // so empty leading cells will be sorted to the end.
+ // aLocalParam.nCol/Row will encompass data content only, extras in
+ // aLocalParam.aDataAreaExtras.
+ bool bShrunk = false;
+ aLocalParam.aDataAreaExtras.resetArea();
+ rDoc.ShrinkToUsedDataArea(bShrunk, nTab, aLocalParam.nCol1, aLocalParam.nRow1,
+ aLocalParam.nCol2, aLocalParam.nRow2, false, aLocalParam.bByRow,
+ !aLocalParam.bByRow,
+ (aLocalParam.aDataAreaExtras.anyExtrasWanted() ?
+ &aLocalParam.aDataAreaExtras : nullptr));
+
+ SCROW nStartRow = aLocalParam.nRow1;
+ if (aLocalParam.bByRow && aLocalParam.bHasHeader && nStartRow < aLocalParam.nRow2)
+ ++nStartRow;
+
+ SCCOL nOverallCol1 = aLocalParam.nCol1;
+ SCROW nOverallRow1 = aLocalParam.nRow1;
+ SCCOL nOverallCol2 = aLocalParam.nCol2;
+ SCROW nOverallRow2 = aLocalParam.nRow2;
+ if (aLocalParam.aDataAreaExtras.anyExtrasWanted())
+ {
+ // Trailing empty excess columns/rows are excluded from being sorted,
+ // they stick at the end. Clip them.
+ const ScDataAreaExtras::Clip eClip = (aLocalParam.bByRow ?
+ ScDataAreaExtras::Clip::Row : ScDataAreaExtras::Clip::Col);
+ aLocalParam.aDataAreaExtras.GetOverallRange( nOverallCol1, nOverallRow1, nOverallCol2, nOverallRow2, eClip);
+ // Make it permanent.
+ aLocalParam.aDataAreaExtras.SetOverallRange( nOverallCol1, nOverallRow1, nOverallCol2, nOverallRow2);
+
+ if (bUpdateRefs)
+ {
+ // With update references the entire range needs to be handled as
+ // one entity for references pointing within to be moved along,
+ // even when there's no data content. For huge ranges we may be
+ // DOOMed then.
+ aLocalParam.nCol1 = nOverallCol1;
+ aLocalParam.nRow1 = nOverallRow1;
+ aLocalParam.nCol2 = nOverallCol2;
+ aLocalParam.nRow2 = nOverallRow2;
+ }
+ }
+
+ if (aLocalParam.aDataAreaExtras.mbCellFormats
+ && rDoc.HasAttrib( nOverallCol1, nStartRow, nTab, nOverallCol2, nOverallRow2, nTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped))
+ {
+ // Merge attributes would be mixed up during sorting.
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
+ return false;
+ }
+
+ // execute
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ // Calculate the script types for all cells in the sort range beforehand.
+ // This will speed up the row height adjustment that takes place after the
+ // sort.
+ rDoc.UpdateScriptTypes(
+ ScAddress(aLocalParam.nCol1,nStartRow,nTab),
+ aLocalParam.nCol2-aLocalParam.nCol1+1,
+ aLocalParam.nRow2-nStartRow+1);
+
+ // No point adjusting row heights after the sort when all rows have the same height.
+ bool bUniformRowHeight = rDoc.HasUniformRowHeight(nTab, nStartRow, nOverallRow2);
+
+ bool bRepeatQuery = false; // repeat existing filter?
+ ScQueryParam aQueryParam;
+ pDBData->GetQueryParam( aQueryParam );
+ if ( aQueryParam.GetEntry(0).bDoQuery )
+ bRepeatQuery = true;
+
+ sc::ReorderParam aUndoParam;
+
+ // don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
+ if (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)
+ {
+ ScProgress aProgress(&rDocShell, ScResId(STR_PROGRESS_SORTING), 0, true);
+ if (!bRepeatQuery)
+ bRepeatQuery = rDoc.HasHiddenRows(aLocalParam.nRow1, aLocalParam.nRow2, nTab);
+ rDoc.Sort(nTab, aLocalParam, bRepeatQuery, bUpdateRefs, &aProgress, &aUndoParam);
+ }
+
+ if (bRecord)
+ {
+ // Set up an undo object.
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<sc::UndoSort>(&rDocShell, aUndoParam));
+ }
+
+ pDBData->SetSortParam(rSortParam);
+ // Remember additional settings on anonymous database ranges.
+ if (pDBData == rDoc.GetAnonymousDBData( nTab) || rDoc.GetDBCollection()->getAnonDBs().has( pDBData))
+ pDBData->UpdateFromSortParam( rSortParam);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pSomeViewForThisDoc = rDocShell.GetBestViewShell(false);
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pSomeViewForThisDoc->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab))
+ pPosHelper->invalidateByIndex(nStartRow);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */, true /* bGroups */, nTab);
+ }
+
+ if (nStartRow <= aLocalParam.nRow2)
+ {
+ ScRange aDirtyRange(
+ aLocalParam.nCol1, nStartRow, nTab,
+ aLocalParam.nCol2, aLocalParam.nRow2, nTab);
+ rDoc.SetDirty( aDirtyRange, true );
+ }
+
+ if (bPaint)
+ {
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ SCCOL nStartX = nOverallCol1;
+ SCROW nStartY = nOverallRow1;
+ SCCOL nEndX = nOverallCol2;
+ SCROW nEndY = nOverallRow2;
+ if ( bRepeatQuery )
+ {
+ nPaint |= PaintPartFlags::Left;
+ nStartX = 0;
+ nEndX = rDoc.MaxCol();
+ }
+ rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
+ }
+
+ if (!bUniformRowHeight && nStartRow <= nOverallRow2)
+ rDocShell.AdjustRowHeight(nStartRow, nOverallRow2, nTab);
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
+ const ScRange* pAdvSource, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (pViewSh && ScTabViewShell::isAnyEditViewInRange(pViewSh, /*bColumns*/ false, rQueryParam.nRow1, rQueryParam.nRow2))
+ {
+ return false;
+ }
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
+ rQueryParam.nCol2, rQueryParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "Query: no DBData" );
+ return false;
+ }
+
+ // Change from Inplace to non-Inplace, only then cancel Inplace:
+ // (only if "Persistent" is selected in the dialog)
+
+ if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
+ {
+ ScQueryParam aOldQuery;
+ pDBData->GetQueryParam(aOldQuery);
+ if (aOldQuery.bInplace)
+ {
+ // cancel old filtering
+
+ SCSIZE nEC = aOldQuery.GetEntryCount();
+ for (SCSIZE i=0; i<nEC; i++)
+ aOldQuery.GetEntry(i).bDoQuery = false;
+ aOldQuery.bDuplicate = true;
+ Query( nTab, aOldQuery, nullptr, bRecord, bApi );
+ }
+ }
+
+ ScQueryParam aLocalParam( rQueryParam ); // for Paint / destination range
+ bool bCopy = !rQueryParam.bInplace; // copied in Table::Query
+ ScDBData* pDestData = nullptr; // range to be copied to
+ bool bDoSize = false; // adjust destination size (insert/delete)
+ SCCOL nFormulaCols = 0; // only at bDoSize
+ bool bKeepFmt = false;
+ ScRange aOldDest;
+ ScRange aDestTotal;
+ if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
+ rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
+ bCopy = false;
+ SCTAB nDestTab = nTab;
+ if ( bCopy )
+ {
+ aLocalParam.MoveToDest();
+ nDestTab = rQueryParam.nDestTab;
+ if ( !rDoc.ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ ScEditableTester aTester( rDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
+ aLocalParam.nCol2,aLocalParam.nRow2);
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ pDestData = rDoc.GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
+ rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDestData)
+ {
+ pDestData->GetArea( aOldDest );
+ aDestTotal=ScRange( rQueryParam.nDestCol,
+ rQueryParam.nDestRow,
+ nDestTab,
+ rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
+ rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
+ nDestTab );
+
+ bDoSize = pDestData->IsDoSize();
+ // test if formulas need to be filled in (nFormulaCols):
+ if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
+ {
+ SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // next to the range
+ SCROW nTestRow = rQueryParam.nDestRow +
+ ( aLocalParam.bHasHeader ? 1 : 0 );
+ while ( nTestCol <= rDoc.MaxCol() &&
+ rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
+ {
+ ++nTestCol;
+ ++nFormulaCols;
+ }
+ }
+
+ bKeepFmt = pDestData->IsKeepFmt();
+ if ( bDoSize && !rDoc.CanFitBlock( aOldDest, aDestTotal ) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // cannot insert rows
+ return false;
+ }
+ }
+ }
+
+ // execute
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ bool bKeepSub = false; // repeat existing partial results?
+ if (rQueryParam.GetEntry(0).bDoQuery) // not at cancellation
+ {
+ ScSubTotalParam aSubTotalParam;
+ pDBData->GetSubTotalParam( aSubTotalParam ); // partial results exist?
+
+ if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
+ bKeepSub = true;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+ const ScRange* pOld = nullptr;
+
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ if (bCopy)
+ {
+ pUndoDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true );
+ rDoc.CopyToDocument(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
+ aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+ // secure attributes in case they were copied along
+
+ if (pDestData)
+ {
+ rDoc.CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
+ pOld = &aOldDest;
+ }
+ }
+ else
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ rDoc.CopyToDocument(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rQueryParam.nRow2, nTab,
+ InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ std::unique_ptr<ScDocument> pAttribDoc;
+ ScRange aAttribRange;
+ if (pDestData) // delete destination range
+ {
+ if ( bKeepFmt )
+ {
+ // smaller of the end columns, header+1 row
+ aAttribRange = aOldDest;
+ if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
+ aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
+ aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
+ ( aLocalParam.bHasHeader ? 1 : 0 ) );
+
+ // also for filled-in formulas
+ aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
+
+ pAttribDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pAttribDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true );
+ rDoc.CopyToDocument(aAttribRange, InsertDeleteFlags::ATTRIB, false, *pAttribDoc);
+ }
+
+ if ( bDoSize )
+ rDoc.FitBlock( aOldDest, aDestTotal );
+ else
+ rDoc.DeleteAreaTab(aOldDest, InsertDeleteFlags::ALL); // simply delete
+ }
+
+ // execute filtering on the document
+ SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub );
+ pDBData->CalcSaveFilteredCount( nCount );
+ if (bCopy)
+ {
+ aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
+ if (!aLocalParam.bHasHeader && nCount > 0)
+ --aLocalParam.nRow2;
+
+ if ( bDoSize )
+ {
+ // adjust to the real result range
+ // (this here is always a reduction)
+
+ ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
+ aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
+ rDoc.FitBlock( aDestTotal, aNewDest, false ); // sal_False - don't delete
+
+ if ( nFormulaCols > 0 )
+ {
+ // fill in formulas
+ //! Undo (Query and Repeat) !!!
+
+ ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
+ aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
+ ScRange aOldForm = aNewForm;
+ aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
+ rDoc.FitBlock( aOldForm, aNewForm, false );
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectOneTable(nDestTab);
+ SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
+
+ sal_uLong nProgCount = nFormulaCols;
+ nProgCount *= aLocalParam.nRow2 - nFStartY;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aLocalParam.nCol2+1, nFStartY,
+ aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark,
+ aLocalParam.nRow2 - nFStartY,
+ FILL_TO_BOTTOM, FILL_SIMPLE );
+ }
+ }
+
+ if ( pAttribDoc ) // copy back the memorized attributes
+ {
+ // Header
+ if (aLocalParam.bHasHeader)
+ {
+ ScRange aHdrRange = aAttribRange;
+ aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
+ pAttribDoc->CopyToDocument(aHdrRange, InsertDeleteFlags::ATTRIB, false, rDoc);
+ }
+
+ // Data
+ SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
+ SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
+ for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
+ {
+ const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
+ nCol, nAttrRow, nDestTab );
+ OSL_ENSURE(pSrcPattern,"Pattern is 0");
+ if (pSrcPattern)
+ {
+ rDoc.ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
+ nDestTab, *pSrcPattern );
+ const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
+ if (pStyle)
+ rDoc.ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
+ nDestTab, *pStyle );
+ }
+ }
+ }
+ }
+
+ // saving: Inplace always, otherwise depending on setting
+ // old Inplace-Filter may have already been removed
+
+ bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
+ if (bSave) // memorize
+ {
+ pDBData->SetQueryParam( rQueryParam );
+ pDBData->SetHeader( rQueryParam.bHasHeader ); //! ???
+ pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam
+ }
+
+ if (bCopy) // memorize new DB range
+ {
+ // Selection is done afterwards from outside (dbfunc).
+ // Currently through the DB area at the destination position,
+ // so a range must be created there in any case.
+
+ ScDBData* pNewData;
+ if (pDestData)
+ pNewData = pDestData; // range exists -> adjust (always!)
+ else // create range
+ pNewData = rDocShell.GetDBData(
+ ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
+ aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
+ SC_DB_MAKE, ScGetDBSelection::ForceMark );
+
+ if (pNewData)
+ {
+ pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
+ aLocalParam.nCol2, aLocalParam.nRow2 );
+
+ // query parameter is no longer set at the destination, only leads to confusion
+ // and mistakes with the query parameter at the source range (#37187#)
+ }
+ else
+ {
+ OSL_FAIL("Target are not available");
+ }
+ }
+
+ if (!bCopy)
+ {
+ rDoc.InvalidatePageBreaks(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+ }
+
+ // #i23299# Subtotal functions depend on cell's filtered states.
+ ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, rDoc.MaxCol(), aLocalParam.nRow2, nDestTab);
+ rDoc.SetSubTotalCellsDirty(aDirtyRange);
+
+ if ( bRecord )
+ {
+ // create undo action after executing, because of drawing layer undo
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoQuery>( &rDocShell, nTab, rQueryParam, std::move(pUndoDoc), std::move(pUndoDB),
+ pOld, bDoSize, pAdvSource ) );
+ }
+
+ if ( pViewSh )
+ {
+ // could there be horizontal autofilter ?
+ // maybe it would be better to set bColumns to !rQueryParam.bByRow ?
+ // anyway at the beginning the value of bByRow is 'false'
+ // then after the first sort action it becomes 'true'
+ pViewSh->OnLOKShowHideColRow(/*bColumns*/ false, rQueryParam.nRow1 - 1);
+ }
+
+ if (bCopy)
+ {
+ SCCOL nEndX = aLocalParam.nCol2;
+ SCROW nEndY = aLocalParam.nRow2;
+ if (pDestData)
+ {
+ if ( aOldDest.aEnd.Col() > nEndX )
+ nEndX = aOldDest.aEnd.Col();
+ if ( aOldDest.aEnd.Row() > nEndY )
+ nEndY = aOldDest.aEnd.Row();
+ }
+ if (bDoSize)
+ nEndY = rDoc.MaxRow();
+
+ // remove AutoFilter button flags
+ rDocShell.DBAreaDeleted(nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, aLocalParam.nCol2);
+
+ rDocShell.PostPaint(
+ ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab),
+ PaintPartFlags::Grid);
+ }
+ else
+ rDocShell.PostPaint(
+ ScRange(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left);
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+void ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
+ bool bRecord, bool bApi )
+{
+ //! use also for ScDBFunc::DoSubTotals !
+ // then stays outside:
+ // - mark new range (from DBData)
+ // - SelectionChanged (?)
+
+ bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
+ rParam.nCol2, rParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "SubTotals: no DBData" );
+ return;
+ }
+
+ ScEditableTester aTester( rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
+ rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // don't insert into merged
+ return;
+ }
+
+ bool bOk = true;
+ if (rParam.bReplace)
+ {
+ if (rDoc.TestRemoveSubTotals( nTab, rParam ))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question,
+ VclButtonsType::YesNo, ScResId(STR_MSSG_DOSUBTOTALS_1))); // "Delete Data?"
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
+ bOk = xBox->run() == RET_YES;
+ }
+ }
+
+ if (!bOk)
+ return;
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScSubTotalParam aNewParam( rParam ); // end of range is being changed
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::unique_ptr<ScRangeName> pUndoRange;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+
+ if (bRecord) // secure old data
+ {
+ bool bOldFilter = bDo && rParam.bDoSort;
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ // column/row state
+ SCCOLROW nOutStartCol, nOutEndCol;
+ SCCOLROW nOutStartRow, nOutEndRow;
+ pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
+ pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ rDoc.CopyToDocument(0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
+
+ // secure data range - incl. filtering result
+ rDoc.CopyToDocument(0, rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ // all formulas because of references
+ rDoc.CopyToDocument(0, 0, 0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc);
+
+ // ranges of DB and other
+ ScRangeName* pDocRange = rDoc.GetRangeName();
+ if (!pDocRange->empty())
+ pUndoRange.reset(new ScRangeName( *pDocRange ));
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+ }
+
+// rDoc.SetOutlineTable( nTab, NULL );
+ ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
+ if (pOut)
+ pOut->GetRowArray().RemoveAll(); // only delete row outlines
+
+ if (rParam.bReplace)
+ rDoc.RemoveSubTotals( nTab, aNewParam );
+ bool bSuccess = true;
+ if (bDo)
+ {
+ // sort
+ if ( rParam.bDoSort )
+ {
+ pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+
+ // set partial result field to before the sorting
+ // (Duplicates are omitted, so can be called again)
+
+ ScSortParam aOldSort;
+ pDBData->GetSortParam( aOldSort );
+ ScSortParam aSortParam( aNewParam, aOldSort );
+ Sort( nTab, aSortParam, false, false, bApi );
+ }
+
+ bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
+ rDoc.SetDrawPageSize(nTab);
+ }
+ ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
+ aNewParam.nCol2, aNewParam.nRow2, nTab );
+ rDoc.SetDirty( aDirtyRange, true );
+
+ if (bRecord)
+ {
+// ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSubTotals>( &rDocShell, nTab,
+ rParam, aNewParam.nRow2,
+ std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
+ std::move(pUndoRange), std::move(pUndoDB) ) );
+ }
+
+ if (!bSuccess)
+ {
+ // "Cannot insert rows"
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
+ }
+
+ // memorize
+ pDBData->SetSubTotalParam( aNewParam );
+ pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+ rDoc.CompileDBFormula();
+
+ rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
+ aModificator.SetDocumentModified();
+}
+
+namespace {
+
+bool lcl_EmptyExcept( ScDocument& rDoc, const ScRange& rRange, const ScRange& rExcept )
+{
+ ScCellIterator aIter( rDoc, rRange );
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ if (!aIter.isEmpty()) // real content?
+ {
+ if (!rExcept.Contains(aIter.GetPos()))
+ return false; // cell found
+ }
+ }
+
+ return true; // nothing found - empty
+}
+
+bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (!rDocShell.IsEditable() || rDoc.GetChangeTrack())
+ {
+ // not recorded -> disallow
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+
+ return false;
+ }
+
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange & r = rRanges[i];
+ ScEditableTester aTester(rDoc, r);
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void createUndoDoc(ScDocumentUniquePtr& pUndoDoc, ScDocument& rDoc, const ScRange& rRange)
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo(rDoc, nTab, nTab);
+ rDoc.CopyToDocument(rRange, InsertDeleteFlags::ALL, false, *pUndoDoc);
+}
+
+bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bOverflow = false;
+ rNewOut = rDPObj.GetNewOutputRange(bOverflow);
+
+ // Test for overlap with source data range.
+ // TODO: Check with other pivot tables as well.
+ const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc();
+ if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut))
+ {
+ // New output range intersteps with the source data. Move it up to
+ // where the old range is and see if that works.
+ ScRange aOldRange = rDPObj.GetOutRange();
+ SCROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row();
+ rNewOut.aStart.SetRow(aOldRange.aStart.Row());
+ rNewOut.aEnd.IncRow(nDiff);
+ if (!rDoc.ValidRow(rNewOut.aStart.Row()) || !rDoc.ValidRow(rNewOut.aEnd.Row()))
+ bOverflow = true;
+ }
+
+ if (bOverflow)
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PIVOT_ERROR);
+
+ return false;
+ }
+
+ ScEditableTester aTester(rDoc, rNewOut);
+ if (!aTester.IsEditable())
+ {
+ // destination area isn't editable
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return false;
+ }
+
+ return true;
+}
+
+}
+
+bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
+ bool bRecord, bool bApi, bool bAllowMove )
+{
+ if (!pOldObj)
+ {
+ if (!pNewObj)
+ return false;
+
+ return CreatePivotTable(*pNewObj, bRecord, bApi);
+ }
+
+ if (!pNewObj)
+ return RemovePivotTable(*pOldObj, bRecord, bApi);
+
+ if (pOldObj == pNewObj)
+ return UpdatePivotTable(*pOldObj, bRecord, bApi);
+
+ OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj);
+
+ ScDocShellModificator aModificator( rDocShell );
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScRangeList aRanges;
+ aRanges.push_back(pOldObj->GetOutRange());
+ aRanges.push_back(pNewObj->GetOutRange().aStart); // at least one cell in the output position must be editable.
+ if (!isEditable(rDocShell, aRanges, bApi))
+ return false;
+
+ ScDocumentUniquePtr pOldUndoDoc;
+ ScDocumentUniquePtr pNewUndoDoc;
+
+ ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if (bRecord)
+ createUndoDoc(pOldUndoDoc, rDoc, pOldObj->GetOutRange());
+
+ pNewObj->WriteSourceDataTo(*pOldObj); // copy source data
+
+ ScDPSaveData* pData = pNewObj->GetSaveData();
+ OSL_ENSURE( pData, "no SaveData from living DPObject" );
+ if (pData)
+ pOldObj->SetSaveData(*pData); // copy SaveData
+
+ pOldObj->SetAllowMove(bAllowMove);
+ pOldObj->ReloadGroupTableData();
+ pOldObj->SyncAllDimensionMembers();
+ pOldObj->InvalidateData(); // before getting the new output area
+
+ // make sure the table has a name (not set by dialog)
+ if (pOldObj->GetName().isEmpty())
+ pOldObj->SetName( rDoc.GetDPCollection()->CreateNewName() );
+
+ ScRange aNewOut;
+ if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi))
+ {
+ *pOldObj = aUndoDPObj;
+ return false;
+ }
+
+ // test if new output area is empty except for old area
+ if (!bApi)
+ {
+ // OutRange of pOldObj (pDestObj) is still old area
+ if (!lcl_EmptyExcept(rDoc, aNewOut, pOldObj->GetOutRange()))
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_PIVOT_NOTEMPTY)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_NO)
+ {
+ //! like above (not editable)
+ *pOldObj = aUndoDPObj;
+ return false;
+ }
+ }
+ }
+
+ if (bRecord)
+ createUndoDoc(pNewUndoDoc, rDoc, aNewOut);
+
+ pOldObj->Output(aNewOut.aStart);
+ rDocShell.PostPaintGridAll(); //! only necessary parts
+
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDataPilot>(
+ &rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, pOldObj, bAllowMove));
+ }
+
+ // notify API objects
+ rDoc.BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) );
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDBDocFunc::RemovePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
+{
+ ScDocShellModificator aModificator(rDocShell);
+ weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
+
+ if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
+ return false;
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (!bApi)
+ {
+ // If we come from GUI - ask to delete the associated pivot charts too...
+ std::vector<SdrOle2Obj*> aListOfObjects =
+ sc::tools::getAllPivotChartsConnectedTo(rDPObj.GetName(), &rDocShell);
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+
+ if (pModel && !aListOfObjects.empty())
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_PIVOT_REMOVE_PIVOTCHART)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_NO)
+ {
+ return false;
+ }
+ else
+ {
+ for (SdrOle2Obj* pChartObject : aListOfObjects)
+ {
+ rDoc.GetChartListenerCollection()->removeByName(pChartObject->GetName());
+ pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pChartObject));
+ pChartObject->getSdrPageFromSdrObject()->RemoveObject(pChartObject->GetOrdNum());
+ }
+ }
+ }
+ }
+
+ ScDocumentUniquePtr pOldUndoDoc;
+ std::unique_ptr<ScDPObject> pUndoDPObj;
+
+ if (bRecord)
+ pUndoDPObj.reset(new ScDPObject(rDPObj)); // copy old settings for undo
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ // delete table
+
+ ScRange aRange = rDPObj.GetOutRange();
+ SCTAB nTab = aRange.aStart.Tab();
+
+ if (bRecord)
+ createUndoDoc(pOldUndoDoc, rDoc, aRange);
+
+ rDoc.DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ nTab, InsertDeleteFlags::ALL );
+ rDoc.RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ nTab, ScMF::Auto );
+
+ rDoc.GetDPCollection()->FreeTable(&rDPObj); // object is deleted here
+
+ rDocShell.PostPaintGridAll(); //! only necessary parts
+ rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
+
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDataPilot>(
+ &rDocShell, std::move(pOldUndoDoc), nullptr, pUndoDPObj.get(), nullptr, false));
+
+ // pUndoDPObj is copied
+ }
+
+ aModificator.SetDocumentModified();
+ return true;
+}
+
+bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
+{
+ ScDocShellModificator aModificator(rDocShell);
+ weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
+
+ // At least one cell in the output range should be editable. Check in advance.
+ if (!isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi))
+ return false;
+
+ ScDocumentUniquePtr pNewUndoDoc;
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ // output range must be set at pNewObj
+ std::unique_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj));
+
+ ScDPObject& rDestObj = *pDestObj;
+
+ // #i94570# When changing the output position in the dialog, a new table is created
+ // with the settings from the old table, including the name.
+ // So we have to check for duplicate names here (before inserting).
+ if (rDoc.GetDPCollection()->GetByName(rDestObj.GetName()))
+ rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below
+
+ // Synchronize groups between linked tables
+ {
+ const ScDPDimensionSaveData* pGroups = nullptr;
+ bool bRefFound = rDoc.GetDPCollection()->GetReferenceGroups(rDestObj, &pGroups);
+ if (bRefFound)
+ {
+ ScDPSaveData* pSaveData = rDestObj.GetSaveData();
+ if (pSaveData)
+ pSaveData->SetDimensionData(pGroups);
+ }
+ }
+
+ rDoc.GetDPCollection()->InsertNewTable(std::move(pDestObj));
+
+ rDestObj.ReloadGroupTableData();
+ rDestObj.SyncAllDimensionMembers();
+ rDestObj.InvalidateData(); // before getting the new output area
+
+ // make sure the table has a name (not set by dialog)
+ if (rDestObj.GetName().isEmpty())
+ rDestObj.SetName(rDoc.GetDPCollection()->CreateNewName());
+
+ bool bOverflow = false;
+ ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow);
+
+ if (bOverflow)
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PIVOT_ERROR);
+
+ return false;
+ }
+
+ {
+ ScEditableTester aTester(rDoc, aNewOut);
+ if (!aTester.IsEditable())
+ {
+ // destination area isn't editable
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return false;
+ }
+ }
+
+ // test if new output area is empty except for old area
+ if (!bApi)
+ {
+ bool bEmpty = rDoc.IsBlockEmpty(
+ aNewOut.aStart.Col(), aNewOut.aStart.Row(),
+ aNewOut.aEnd.Col(), aNewOut.aEnd.Row(), aNewOut.aStart.Tab() );
+
+ if (!bEmpty)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_PIVOT_NOTEMPTY)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_NO)
+ {
+ //! like above (not editable)
+ return false;
+ }
+ }
+ }
+
+ if (bRecord)
+ createUndoDoc(pNewUndoDoc, rDoc, aNewOut);
+
+ rDestObj.Output(aNewOut.aStart);
+ rDocShell.PostPaintGridAll(); //! only necessary parts
+
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDataPilot>(&rDocShell, nullptr, std::move(pNewUndoDoc), nullptr, &rDestObj, false));
+ }
+
+ // notify API objects
+ rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName()));
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
+{
+ ScDocShellModificator aModificator( rDocShell );
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
+ return false;
+
+ ScDocumentUniquePtr pOldUndoDoc;
+ ScDocumentUniquePtr pNewUndoDoc;
+
+ ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure.
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if (bRecord)
+ createUndoDoc(pOldUndoDoc, rDoc, rDPObj.GetOutRange());
+
+ rDPObj.SetAllowMove(false);
+ rDPObj.ReloadGroupTableData();
+ if (!rDPObj.SyncAllDimensionMembers())
+ return false;
+
+ rDPObj.InvalidateData(); // before getting the new output area
+
+ // make sure the table has a name (not set by dialog)
+ if (rDPObj.GetName().isEmpty())
+ rDPObj.SetName( rDoc.GetDPCollection()->CreateNewName() );
+
+ ScRange aNewOut;
+ if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi))
+ {
+ rDPObj = aUndoDPObj;
+ return false;
+ }
+
+ // test if new output area is empty except for old area
+ if (!bApi)
+ {
+ if (!lcl_EmptyExcept(rDoc, aNewOut, rDPObj.GetOutRange()))
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_PIVOT_NOTEMPTY)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_NO)
+ {
+ rDPObj = aUndoDPObj;
+ return false;
+ }
+ }
+ }
+
+ if (bRecord)
+ createUndoDoc(pNewUndoDoc, rDoc, aNewOut);
+
+ rDPObj.Output(aNewOut.aStart);
+ rDocShell.PostPaintGridAll(); //! only necessary parts
+
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDataPilot>(
+ &rDocShell, std::move(pOldUndoDoc), std::move(pNewUndoDoc), &aUndoDPObj, &rDPObj, false));
+ }
+
+ // notify API objects
+ rDoc.BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) );
+ aModificator.SetDocumentModified();
+ return true;
+}
+
+void ScDBDocFunc::RefreshPivotTables(const ScDPObject* pDPObj, bool bApi)
+{
+ ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
+ if (!pDPs)
+ return;
+
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ TranslateId pErrId = pDPs->ReloadCache(pDPObj, aRefs);
+ if (pErrId)
+ return;
+
+ for (ScDPObject* pObj : aRefs)
+ {
+ // This action is intentionally not undoable since it modifies cache.
+ UpdatePivotTable(*pObj, false, bApi);
+ }
+}
+
+void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj)
+{
+ if (!pDPObj)
+ return;
+
+ ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
+ if (!pDPs)
+ return;
+
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if (!pSaveData)
+ return;
+
+ if (!pDPs->HasTable(pDPObj))
+ {
+ // This table is under construction so no need for a whole update (UpdatePivotTable()).
+ pDPObj->ReloadGroupTableData();
+ return;
+ }
+
+ // Update all linked tables, if this table is part of the cache (ScDPCollection)
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs))
+ return;
+
+ // We allow pDimData being NULL.
+ const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
+ for (ScDPObject* pObj : aRefs)
+ {
+ if (pObj != pDPObj)
+ {
+ pSaveData = pObj->GetSaveData();
+ if (pSaveData)
+ pSaveData->SetDimensionData(pDimData);
+ }
+
+ // This action is intentionally not undoable since it modifies cache.
+ UpdatePivotTable(*pObj, false, false);
+ }
+}
+
+// database import
+
+void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
+{
+ // rTarget is the name of a database range
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScDBCollection& rDBColl = *rDoc.GetDBCollection();
+ const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rTarget));
+ if (!pData)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_TARGETNOTFOUND)));
+ xInfoBox->run();
+ return;
+ }
+
+ SCTAB nTab;
+ SCCOL nDummyCol;
+ SCROW nDummyRow;
+ pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
+
+ ScImportParam aImportParam;
+ pData->GetImportParam( aImportParam );
+
+ OUString sDBName;
+ OUString sDBTable;
+ sal_Int32 nCommandType = 0;
+ sDBName = rDescriptor.getDataSource();
+ rDescriptor[svx::DataAccessDescriptorProperty::Command] >>= sDBTable;
+ rDescriptor[svx::DataAccessDescriptorProperty::CommandType] >>= nCommandType;
+
+ aImportParam.aDBName = sDBName;
+ aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND );
+ aImportParam.aStatement = sDBTable;
+ aImportParam.bNative = false;
+ aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
+ aImportParam.bImport = true;
+
+ bool bContinue = DoImport( nTab, aImportParam, &rDescriptor );
+
+ // repeat DB operations
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (!pViewSh)
+ return;
+
+ ScRange aRange;
+ pData->GetArea(aRange);
+ pViewSh->MarkRange(aRange); // select
+
+ if ( bContinue ) // error at import -> abort
+ {
+ // internal operations, if some are saved
+
+ if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
+ pViewSh->RepeatDB();
+
+ // pivot tables which have the range as source data
+
+ rDocShell.RefreshPivotTables(aRange);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/dbdocimp.cxx b/sc/source/ui/docshell/dbdocimp.cxx
new file mode 100644
index 0000000000..5b4dec1731
--- /dev/null
+++ b/sc/source/ui/docshell/dbdocimp.cxx
@@ -0,0 +1,628 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/XCompletedExecution.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <dbdocfun.hxx>
+#include <docsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scerrors.hxx>
+#include <dbdata.hxx>
+#include <markdata.hxx>
+#include <undodat.hxx>
+#include <progress.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+#include <dbdocutl.hxx>
+#include <editable.hxx>
+#include <hints.hxx>
+#include <miscuno.hxx>
+#include <chgtrack.hxx>
+#include <refupdatecontext.hxx>
+
+using namespace com::sun::star;
+
+constexpr OUStringLiteral SC_SERVICE_ROWSET = u"com.sun.star.sdb.RowSet";
+
+//! move to a header file?
+constexpr OUStringLiteral SC_DBPROP_DATASOURCENAME = u"DataSourceName";
+constexpr OUStringLiteral SC_DBPROP_COMMAND = u"Command";
+constexpr OUStringLiteral SC_DBPROP_COMMANDTYPE = u"CommandType";
+
+void ScDBDocFunc::ShowInBeamer( const ScImportParam& rParam, const SfxViewFrame* pFrame )
+{
+ // called after opening the database beamer
+
+ if ( !pFrame || !rParam.bImport )
+ return;
+
+ uno::Reference<frame::XFrame> xFrame = pFrame->GetFrame().GetFrameInterface();
+
+ uno::Reference<frame::XFrame> xBeamerFrame = xFrame->findFrame(
+ "_beamer",
+ frame::FrameSearchFlag::CHILDREN);
+ if (!xBeamerFrame.is())
+ return;
+
+ uno::Reference<frame::XController> xController = xBeamerFrame->getController();
+ uno::Reference<view::XSelectionSupplier> xControllerSelection(xController, uno::UNO_QUERY);
+ if (xControllerSelection.is())
+ {
+ sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND :
+ ( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY :
+ sdb::CommandType::TABLE );
+
+ svx::ODataAccessDescriptor aSelection;
+ aSelection.setDataSource(rParam.aDBName);
+ aSelection[svx::DataAccessDescriptorProperty::Command] <<= rParam.aStatement;
+ aSelection[svx::DataAccessDescriptorProperty::CommandType] <<= nType;
+
+ xControllerSelection->select(uno::Any(aSelection.createPropertyValueSequence()));
+ }
+ else
+ {
+ OSL_FAIL("no selection supplier in the beamer!");
+ }
+}
+
+void ScDBDocFunc::DoImportUno( const ScAddress& rPos,
+ const uno::Sequence<beans::PropertyValue>& aArgs )
+{
+ svx::ODataAccessDescriptor aDesc( aArgs ); // includes selection and result set
+
+ // create database range
+ ScDBData* pDBData = rDocShell.GetDBData( ScRange(rPos), SC_DB_IMPORT, ScGetDBSelection::Keep );
+ DBG_ASSERT(pDBData, "can't create DB data");
+ OUString sTarget = pDBData->GetName();
+
+ UpdateImport( sTarget, aDesc );
+}
+
+bool ScDBDocFunc::DoImport( SCTAB nTab, const ScImportParam& rParam,
+ const svx::ODataAccessDescriptor* pDescriptor )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScChangeTrack *pChangeTrack = nullptr;
+ ScRange aChangedRange;
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
+ rParam.nCol2, rParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "DoImport: no DBData" );
+ return false;
+ }
+
+ std::unique_ptr<weld::WaitObject> xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent()));
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ bool bTruncated = false; // for warning
+ TranslateId pErrStringId;
+ OUString aErrorMessage;
+
+ SCCOL nCol = rParam.nCol1;
+ SCROW nRow = rParam.nRow1;
+ SCCOL nEndCol = nCol; // end of resulting database area
+ SCROW nEndRow = nRow;
+
+ bool bDoSelection = false;
+ bool bRealSelection = false; // sal_True if not everything is selected
+ bool bBookmarkSelection = false;
+ sal_Int32 nListPos = 0;
+ sal_Int32 nRowsRead = 0;
+ sal_Int32 nListCount = 0;
+
+ uno::Sequence<uno::Any> aSelection;
+ if ( pDescriptor && pDescriptor->has(svx::DataAccessDescriptorProperty::Selection) )
+ {
+ (*pDescriptor)[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
+ nListCount = aSelection.getLength();
+ if ( nListCount > 0 )
+ {
+ bDoSelection = true;
+ if ( pDescriptor->has(svx::DataAccessDescriptorProperty::BookmarkSelection) )
+ bBookmarkSelection = ScUnoHelpFunctions::GetBoolFromAny( (*pDescriptor)[svx::DataAccessDescriptorProperty::BookmarkSelection] );
+ if ( bBookmarkSelection )
+ {
+ // From bookmarks, there's no way to detect if all records are selected.
+ // Rely on base to pass no selection in that case.
+ bRealSelection = true;
+ }
+ }
+ }
+
+ uno::Reference<sdbc::XResultSet> xResultSet;
+ if ( pDescriptor && pDescriptor->has(svx::DataAccessDescriptorProperty::Cursor) )
+ xResultSet.set((*pDescriptor)[svx::DataAccessDescriptorProperty::Cursor], uno::UNO_QUERY);
+
+ // ImportDoc - also used for Redo
+ ScDocumentUniquePtr pImportDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pImportDoc->InitUndo( rDoc, nTab, nTab );
+
+ // get data from database into import document
+
+ try
+ {
+ // progress bar
+ // only text (title is still needed, for the cancel button)
+ ScProgress aProgress( &rDocShell, ScResId(STR_UNDO_IMPORTDATA), 0, true );
+
+ uno::Reference<sdbc::XRowSet> xRowSet( xResultSet, uno::UNO_QUERY );
+ bool bDispose = false;
+ if ( !xRowSet.is() )
+ {
+ bDispose = true;
+ xRowSet.set(comphelper::getProcessServiceFactory()->createInstance(
+ SC_SERVICE_ROWSET ),
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xRowProp( xRowSet, uno::UNO_QUERY );
+ OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
+ if ( xRowProp.is() )
+ {
+
+ // set source parameters
+
+ sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND :
+ ( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY :
+ sdb::CommandType::TABLE );
+
+ xRowProp->setPropertyValue( SC_DBPROP_DATASOURCENAME, uno::Any(rParam.aDBName) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_COMMAND, uno::Any(rParam.aStatement) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, uno::Any(nType) );
+
+ uno::Reference<sdb::XCompletedExecution> xExecute( xRowSet, uno::UNO_QUERY );
+ if ( xExecute.is() )
+ {
+ uno::Reference<task::XInteractionHandler> xHandler(
+ task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr),
+ uno::UNO_QUERY_THROW);
+ xExecute->executeWithCompletion( xHandler );
+ }
+ else
+ xRowSet->execute();
+ }
+ }
+ if ( xRowSet.is() )
+ {
+
+ // get column descriptions
+
+ sal_Int32 nColCount = 0;
+ uno::Reference<sdbc::XResultSetMetaData> xMeta;
+ uno::Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp( xRowSet, uno::UNO_QUERY );
+ if ( xMetaSupp.is() )
+ xMeta = xMetaSupp->getMetaData();
+ if ( xMeta.is() )
+ nColCount = xMeta->getColumnCount(); // this is the number of real columns
+
+ if ( rParam.nCol1 + nColCount - 1 > rDoc.MaxCol() )
+ {
+ nColCount = 0;
+ //! error message
+ }
+
+ uno::Reference<sdbcx::XRowLocate> xLocate;
+ if ( bBookmarkSelection )
+ {
+ xLocate.set( xRowSet, uno::UNO_QUERY );
+ if ( !xLocate.is() )
+ {
+ SAL_WARN( "sc.ui","can't get XRowLocate");
+ bDoSelection = bRealSelection = bBookmarkSelection = false;
+ }
+ }
+
+ uno::Reference<sdbc::XRow> xRow( xRowSet, uno::UNO_QUERY );
+ if ( nColCount > 0 && xRow.is() )
+ {
+ nEndCol = static_cast<SCCOL>( rParam.nCol1 + nColCount - 1 );
+
+ uno::Sequence<sal_Int32> aColTypes( nColCount ); // column types
+ uno::Sequence<sal_Bool> aColCurr( nColCount ); // currency flag is not in types
+ sal_Int32* pTypeArr = aColTypes.getArray();
+ sal_Bool* pCurrArr = aColCurr.getArray();
+ for (tools::Long i=0; i<nColCount; i++)
+ {
+ pTypeArr[i] = xMeta->getColumnType( i+1 );
+ pCurrArr[i] = xMeta->isCurrency( i+1 );
+ }
+
+ // read column names
+ nCol = rParam.nCol1;
+ for (tools::Long i=0; i<nColCount; i++)
+ {
+ pImportDoc->SetString( nCol, nRow, nTab,
+ xMeta->getColumnLabel( i+1 ) );
+ ++nCol;
+ }
+ ++nRow;
+
+ bool bEnd = false;
+ if ( !bDoSelection )
+ xRowSet->beforeFirst();
+ sal_uInt16 nInserted = 0;
+ while ( !bEnd )
+ {
+ // skip rows that are not selected
+ if ( !bDoSelection )
+ {
+ bEnd = !xRowSet->next();
+ if ( !bEnd )
+ ++nRowsRead;
+ }
+ else
+ {
+ if (nListPos < nListCount)
+ {
+ if ( bBookmarkSelection )
+ {
+ bEnd = !xLocate->moveToBookmark(aSelection[nListPos]);
+ }
+ else // use record numbers
+ {
+ sal_Int32 nNextRow = 0;
+ aSelection[nListPos] >>= nNextRow;
+ if ( nRowsRead+1 < nNextRow )
+ bRealSelection = true;
+ nRowsRead = nNextRow;
+ bEnd = !xRowSet->absolute(nRowsRead);
+ }
+ ++nListPos;
+ }
+ else
+ {
+ if ( !bBookmarkSelection && xRowSet->next() )
+ bRealSelection = true; // more data available but not used
+ bEnd = true;
+ }
+ }
+
+ if ( !bEnd )
+ {
+ if ( rDoc.ValidRow(nRow) )
+ {
+ nCol = rParam.nCol1;
+ for (tools::Long i=0; i<nColCount; i++)
+ {
+ ScDatabaseDocUtil::PutData( *pImportDoc, nCol, nRow, nTab,
+ xRow, i+1, pTypeArr[i], pCurrArr[i] );
+ ++nCol;
+ }
+ nEndRow = nRow;
+ ++nRow;
+
+ // progress bar
+
+ ++nInserted;
+ if (!(nInserted & 15))
+ {
+ aProgress.SetState( 0 );
+ }
+ }
+ else // past the end of the spreadsheet
+ {
+ bEnd = true; // don't continue
+ bTruncated = true; // warning flag
+ }
+ }
+ }
+
+ bSuccess = true;
+ }
+
+ if ( bDispose )
+ ::comphelper::disposeComponent( xRowSet );
+ }
+ }
+ catch ( const sdbc::SQLException& rError )
+ {
+ aErrorMessage = rError.Message;
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
+ }
+
+ // test for cell protection
+
+ bool bKeepFormat = pDBData->IsKeepFmt();
+ bool bMoveCells = pDBData->IsDoSize();
+ SCCOL nFormulaCols = 0; // columns to be filled with formulas
+ if (bMoveCells && nEndCol == rParam.nCol2)
+ {
+ // if column count changes, formulas would become invalid anyway
+ // -> only set nFormulaCols for unchanged column count
+
+ SCCOL nTestCol = rParam.nCol2 + 1; // right of the data
+ SCROW nTestRow = rParam.nRow1 + 1; // below the title row
+ while ( nTestCol <= rDoc.MaxCol() &&
+ rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
+ {
+ ++nTestCol;
+ ++nFormulaCols;
+ }
+ }
+
+ if (bSuccess)
+ {
+ // old and new range editable?
+ ScEditableTester aTester;
+ aTester.TestBlock( rDoc, nTab, rParam.nCol1,rParam.nRow1,rParam.nCol2,rParam.nRow2 );
+ aTester.TestBlock( rDoc, nTab, rParam.nCol1,rParam.nRow1,nEndCol,nEndRow );
+ if ( !aTester.IsEditable() )
+ {
+ pErrStringId = aTester.GetMessageId();
+ bSuccess = false;
+ }
+ else if ( (pChangeTrack = rDoc.GetChangeTrack()) != nullptr )
+ aChangedRange = ScRange(rParam.nCol1, rParam.nRow1, nTab,
+ nEndCol+nFormulaCols, nEndRow, nTab );
+ }
+
+ if ( bSuccess && bMoveCells )
+ {
+ ScRange aOld( rParam.nCol1, rParam.nRow1, nTab,
+ rParam.nCol2+nFormulaCols, rParam.nRow2, nTab );
+ ScRange aNew( rParam.nCol1, rParam.nRow1, nTab,
+ nEndCol+nFormulaCols, nEndRow, nTab );
+ if (!rDoc.CanFitBlock( aOld, aNew ))
+ {
+ pErrStringId = STR_MSSG_DOSUBTOTALS_2; // can't insert cells
+ bSuccess = false;
+ }
+ }
+
+ // copy data from import doc into real document
+
+ if ( bSuccess )
+ {
+ if (bKeepFormat)
+ {
+ // keep formatting of title and first data row from the document
+ // CopyToDocument also copies styles, Apply... needs separate calls
+
+ SCCOL nMinEndCol = std::min( rParam.nCol2, nEndCol ); // not too much
+ nMinEndCol = sal::static_int_cast<SCCOL>( nMinEndCol + nFormulaCols ); // only if column count unchanged
+ pImportDoc->DeleteAreaTab( 0,0, rDoc.MaxCol(),rDoc.MaxRow(), nTab, InsertDeleteFlags::ATTRIB );
+ rDoc.CopyToDocument(rParam.nCol1, rParam.nRow1, nTab,
+ nMinEndCol, rParam.nRow1, nTab,
+ InsertDeleteFlags::ATTRIB, false, *pImportDoc);
+
+ SCROW nDataStartRow = rParam.nRow1+1;
+ for (SCCOL nCopyCol=rParam.nCol1; nCopyCol<=nMinEndCol; nCopyCol++)
+ {
+ const ScPatternAttr* pSrcPattern = rDoc.GetPattern(
+ nCopyCol, nDataStartRow, nTab );
+ pImportDoc->ApplyPatternAreaTab( nCopyCol, nDataStartRow, nCopyCol, nEndRow,
+ nTab, *pSrcPattern );
+ const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
+ if (pStyle)
+ pImportDoc->ApplyStyleAreaTab( nCopyCol, nDataStartRow, nCopyCol, nEndRow,
+ nTab, *pStyle );
+ }
+ }
+
+ // don't set cell protection attribute if table is protected
+ if (rDoc.IsTabProtected(nTab))
+ {
+ ScPatternAttr aPattern(pImportDoc->GetPool());
+ aPattern.GetItemSet().Put( ScProtectionAttr( false,false,false,false ) );
+ pImportDoc->ApplyPatternAreaTab( 0,0,rDoc.MaxCol(),rDoc.MaxRow(), nTab, aPattern );
+ }
+
+ // copy old data for undo
+
+ SCCOL nUndoEndCol = std::max( nEndCol, rParam.nCol2 ); // rParam = old end
+ SCROW nUndoEndRow = std::max( nEndRow, rParam.nRow2 );
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScDBData> pUndoDBData;
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ pUndoDBData.reset(new ScDBData( *pDBData ));
+ }
+
+ ScMarkData aNewMark(rDoc.GetSheetLimits());
+ aNewMark.SelectOneTable( nTab );
+
+ if (bRecord)
+ {
+ // do not touch notes (ScUndoImportData does not support drawing undo)
+ InsertDeleteFlags nCopyFlags = InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE;
+
+ // nFormulaCols is set only if column count is unchanged
+ rDoc.CopyToDocument(rParam.nCol1, rParam.nRow1, nTab,
+ nEndCol+nFormulaCols, nEndRow, nTab,
+ nCopyFlags, false, *pUndoDoc);
+ if ( rParam.nCol2 > nEndCol )
+ rDoc.CopyToDocument(nEndCol+1, rParam.nRow1, nTab,
+ nUndoEndCol, nUndoEndRow, nTab,
+ nCopyFlags, false, *pUndoDoc);
+ if ( rParam.nRow2 > nEndRow )
+ rDoc.CopyToDocument(rParam.nCol1, nEndRow+1, nTab,
+ nUndoEndCol+nFormulaCols, nUndoEndRow, nTab,
+ nCopyFlags, false, *pUndoDoc);
+ }
+
+ // move new data
+
+ if (bMoveCells)
+ {
+ // clear only the range without the formulas,
+ // so the formula title and first row are preserved
+
+ ScRange aDelRange( rParam.nCol1, rParam.nRow1, nTab,
+ rParam.nCol2, rParam.nRow2, nTab );
+ rDoc.DeleteAreaTab( aDelRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE ); // without the formulas
+
+ ScRange aOld( rParam.nCol1, rParam.nRow1, nTab,
+ rParam.nCol2+nFormulaCols, rParam.nRow2, nTab );
+ ScRange aNew( rParam.nCol1, rParam.nRow1, nTab,
+ nEndCol+nFormulaCols, nEndRow, nTab );
+ rDoc.FitBlock( aOld, aNew, false ); // Do not delete formulas
+ }
+ else if ( nEndCol < rParam.nCol2 ) // DeleteArea calls PutInOrder
+ rDoc.DeleteArea( nEndCol+1, rParam.nRow1, rParam.nCol2, rParam.nRow2,
+ aNewMark, InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE );
+
+ // CopyToDocument doesn't remove contents
+ rDoc.DeleteAreaTab( rParam.nCol1, rParam.nRow1, nEndCol, nEndRow, nTab, InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE );
+
+ // remove each column from ImportDoc after copying to reduce memory usage
+ bool bOldAutoCalc = rDoc.GetAutoCalc();
+ rDoc.SetAutoCalc( false ); // outside of the loop
+ for (SCCOL nCopyCol = rParam.nCol1; nCopyCol <= nEndCol; nCopyCol++)
+ {
+ pImportDoc->CopyToDocument(nCopyCol, rParam.nRow1, nTab, nCopyCol, nEndRow, nTab,
+ InsertDeleteFlags::ALL, false, rDoc);
+ pImportDoc->DeleteAreaTab( nCopyCol, rParam.nRow1, nCopyCol, nEndRow, nTab, InsertDeleteFlags::CONTENTS );
+ }
+ rDoc.SetAutoCalc( bOldAutoCalc );
+
+ if (nFormulaCols > 0) // copy formulas
+ {
+ if (bKeepFormat) // formats for formulas
+ pImportDoc->CopyToDocument(nEndCol+1, rParam.nRow1, nTab,
+ nEndCol+nFormulaCols, nEndRow, nTab,
+ InsertDeleteFlags::ATTRIB, false, rDoc);
+ // fill formulas
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectOneTable(nTab);
+
+ sal_uLong nProgCount = nFormulaCols;
+ nProgCount *= nEndRow-rParam.nRow1-1;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( nEndCol+1, rParam.nRow1+1, nEndCol+nFormulaCols, rParam.nRow1+1,
+ &aProgress, aMark, nEndRow-rParam.nRow1-1, FILL_TO_BOTTOM, FILL_SIMPLE );
+ }
+
+ // if new range is smaller, clear old contents
+
+ if (!bMoveCells) // move has happened above
+ {
+ if ( rParam.nCol2 > nEndCol )
+ rDoc.DeleteArea( nEndCol+1, rParam.nRow1, rParam.nCol2, rParam.nRow2,
+ aNewMark, InsertDeleteFlags::CONTENTS );
+ if ( rParam.nRow2 > nEndRow )
+ rDoc.DeleteArea( rParam.nCol1, nEndRow+1, rParam.nCol2, rParam.nRow2,
+ aNewMark, InsertDeleteFlags::CONTENTS );
+ }
+
+ // update database range
+ pDBData->SetImportParam( rParam );
+ pDBData->SetHeader( true );
+ pDBData->SetByRow( true );
+ pDBData->SetArea( nTab, rParam.nCol1,rParam.nRow1, nEndCol,nEndRow );
+ pDBData->SetImportSelection( bRealSelection );
+ rDoc.CompileDBFormula();
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc = std::move(pImportDoc);
+
+ if (nFormulaCols > 0) // include filled formulas for redo
+ rDoc.CopyToDocument(rParam.nCol1, rParam.nRow1, nTab,
+ nEndCol+nFormulaCols, nEndRow, nTab,
+ InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pRedoDoc);
+
+ std::unique_ptr<ScDBData> pRedoDBData(new ScDBData(*pDBData));
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoImportData>( &rDocShell, nTab,
+ rParam, nUndoEndCol, nUndoEndRow,
+ nFormulaCols,
+ std::move(pUndoDoc), std::move(pRedoDoc),
+ std::move(pUndoDBData), std::move(pRedoDBData) ) );
+ }
+
+ sc::SetFormulaDirtyContext aCxt;
+ rDoc.SetAllFormulasDirty(aCxt);
+ rDocShell.PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+
+ ScDBRangeRefreshedHint aHint( rParam );
+ rDoc.BroadcastUno( aHint );
+
+ xWaitWin.reset();
+
+ if ( bTruncated ) // show warning
+ ErrorHandler::HandleError(SCWARN_IMPORT_RANGE_OVERFLOW);
+ }
+ else
+ {
+ xWaitWin.reset();
+
+ if (aErrorMessage.isEmpty())
+ {
+ if (!pErrStringId)
+ pErrStringId = STR_MSSG_IMPORTDATA_0;
+ aErrorMessage = ScResId(pErrStringId);
+ }
+
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ aErrorMessage));
+ xInfoBox->run();
+ }
+
+ pImportDoc.reset();
+
+ if (bSuccess && pChangeTrack)
+ pChangeTrack->AppendInsert ( aChangedRange );
+
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx
new file mode 100644
index 0000000000..1a8d902bea
--- /dev/null
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -0,0 +1,5923 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <comphelper/lok.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <sfx2/app.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/justifyitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <utility>
+#include <vcl/weld.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svdocapt.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
+
+#include <docfunc.hxx>
+
+#include <sc.hrc>
+#include <strings.hrc>
+
+#include <arealink.hxx>
+#include <attrib.hxx>
+#include <dociter.hxx>
+#include <autoform.hxx>
+#include <formulacell.hxx>
+#include <cellmergeoption.hxx>
+#include <detdata.hxx>
+#include <detfunc.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <olinetab.hxx>
+#include <patattr.hxx>
+#include <rangenam.hxx>
+#include <refundo.hxx>
+#include <scresid.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <tablink.hxx>
+#include <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <undoblk.hxx>
+#include <undocell.hxx>
+#include <undodraw.hxx>
+#include <undotab.hxx>
+#include <sizedev.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <editable.hxx>
+#include <compiler.hxx>
+#include <scui_def.hxx>
+#include <tabprotection.hxx>
+#include <clipparam.hxx>
+#include <externalrefmgr.hxx>
+#include <undorangename.hxx>
+#include <progress.hxx>
+#include <dpobject.hxx>
+#include <stringutil.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <rowheightcontext.hxx>
+#include <cellvalues.hxx>
+#include <undoconvert.hxx>
+#include <docfuncutil.hxx>
+#include <sheetevents.hxx>
+#include <conditio.hxx>
+#include <columnspanset.hxx>
+#include <validat.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+#include <SparklineData.hxx>
+#include <undo/UndoInsertSparkline.hxx>
+#include <undo/UndoDeleteSparkline.hxx>
+#include <undo/UndoDeleteSparklineGroup.hxx>
+#include <undo/UndoEditSparklineGroup.hxx>
+#include <undo/UndoUngroupSparklines.hxx>
+#include <undo/UndoGroupSparklines.hxx>
+#include <undo/UndoEditSparkline.hxx>
+#include <config_features.h>
+
+#include <memory>
+#include <basic/basmgr.hxx>
+#include <set>
+#include <vector>
+#include <sfx2/viewfrm.hxx>
+
+using namespace com::sun::star;
+using ::std::vector;
+
+void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction)
+{
+ // #i101118# if drawing layer collects the undo actions, add it there
+ ScDrawLayer* pDrawLayer = rDocShell.GetDocument().GetDrawLayer();
+ if( pDrawLayer && pDrawLayer->IsRecording() )
+ pDrawLayer->AddCalcUndo( std::move(pUndoAction) );
+ else
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDraw>( std::move(pUndoAction), &rDocShell ) );
+ rDocShell.SetDrawModified();
+
+ // the affected sheet isn't known, so all stream positions are invalidated
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ rDoc.SetStreamValid(nTab, false);
+}
+
+// paint row above the range (because of lines after AdjustRowHeight)
+
+static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange )
+{
+ SCROW nRow = rRange.aStart.Row();
+ if ( nRow > 0 )
+ {
+ SCTAB nTab = rRange.aStart.Tab(); //! all of them?
+ --nRow;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ rDocShell.PostPaint( ScRange(0,nRow,nTab,rDoc.MaxCol(),nRow,nTab), PaintPartFlags::Grid );
+ }
+}
+
+bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SfxViewShell* pSomeViewForThisDoc = rDocShell.GetBestViewShell(false);
+ if ( rDoc.IsImportingXML() )
+ {
+ // for XML import, all row heights are updated together after importing
+ return false;
+ }
+ if ( rDoc.IsAdjustHeightLocked() )
+ {
+ return false;
+ }
+
+ SCTAB nTab = rRange.aStart.Tab();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+
+ ScSizeDeviceProvider aProv( &rDocShell );
+ Fraction aOne(1,1);
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
+ bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pSomeViewForThisDoc->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab))
+ pPosHelper->invalidateByIndex(nStartRow);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ }
+ rDoc.SetDrawPageSize(nTab);
+ }
+
+ if ( bPaint && bChanged )
+ rDocShell.PostPaint(ScRange(0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pSomeViewForThisDoc, ROW_HEADER, nTab);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/,
+ false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab);
+ }
+
+ return bChanged;
+}
+
+bool ScDocFunc::DetectiveAddPred(const ScAddress& rPos)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).ShowPred( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDPRED );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveDelPred(const ScAddress& rPos)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeletePred( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELPRED );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveAddSucc(const ScAddress& rPos)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).ShowSucc( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDSUCC );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveDelSucc(const ScAddress& rPos)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteSucc( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELSUCC );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveAddError(const ScAddress& rPos)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SCCOL nCol = rPos.Col();
+ SCROW nRow = rPos.Row();
+ SCTAB nTab = rPos.Tab();
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).ShowError( nCol, nRow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDERROR );
+ rDoc.AddDetectiveOperation( aOperation );
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveMarkInvalid(SCTAB nTab)
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDocShell.MakeDrawLayer();
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+
+ std::unique_ptr<weld::WaitObject> xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent()));
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bOverflow;
+ bool bDone = ScDetectiveFunc(rDoc, nTab).MarkInvalid( bOverflow );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ xWaitWin.reset();
+ if (bDone)
+ {
+ if (pUndo && bUndo)
+ {
+ pUndo->SetComment( ScResId( STR_UNDO_DETINVALID ) );
+ rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndo) );
+ }
+ aModificator.SetDocumentModified();
+ if ( bOverflow )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_DETINVALID_OVERFLOW)));
+ xInfoBox->run();
+ }
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveDelAll(SCTAB nTab)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Detective );
+ std::unique_ptr<SdrUndoGroup> pUndo;
+ if (bUndo)
+ pUndo = pModel->GetCalcUndo();
+ if (bDone)
+ {
+ ScDetOpList* pOldList = rDoc.GetDetOpList();
+ std::unique_ptr<ScDetOpList> pUndoList;
+ if (bUndo && pOldList)
+ pUndoList.reset(new ScDetOpList(*pOldList));
+
+ rDoc.ClearDetectiveOperations();
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), nullptr, std::move(pUndoList) ) );
+ }
+ aModificator.SetDocumentModified();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_DETECTIVE_REFRESH );
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::DetectiveRefresh( bool bAutomatic )
+{
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ ScDetOpList* pList = rDoc.GetDetOpList();
+ if ( pList && pList->Count() )
+ {
+ rDocShell.MakeDrawLayer();
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ const bool bUndo (rDoc.IsUndoEnabled());
+ if (bUndo)
+ pModel->BeginCalcUndo(false);
+
+ // Delete in all sheets
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ ScDetectiveFunc( rDoc,nTab ).DeleteAll( ScDetectiveDelete::Arrows ); // don't remove circles
+
+ // repeat
+
+ size_t nCount = pList->Count();
+ for (size_t i=0; i < nCount; ++i)
+ {
+ const ScDetOpData& rData = pList->GetObject(i);
+ const ScAddress& aPos = rData.GetPos();
+ ScDetectiveFunc aFunc( rDoc, aPos.Tab() );
+ SCCOL nCol = aPos.Col();
+ SCROW nRow = aPos.Row();
+ switch (rData.GetOperation())
+ {
+ case SCDETOP_ADDSUCC:
+ aFunc.ShowSucc( nCol, nRow );
+ break;
+ case SCDETOP_DELSUCC:
+ aFunc.DeleteSucc( nCol, nRow );
+ break;
+ case SCDETOP_ADDPRED:
+ aFunc.ShowPred( nCol, nRow );
+ break;
+ case SCDETOP_DELPRED:
+ aFunc.DeletePred( nCol, nRow );
+ break;
+ case SCDETOP_ADDERROR:
+ aFunc.ShowError( nCol, nRow );
+ break;
+ default:
+ OSL_FAIL("wrong operation in DetectiveRefresh");
+ }
+ }
+
+ if (bUndo)
+ {
+ std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
+ if (pUndo)
+ {
+ pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) );
+ // associate with the last action
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDraw>( std::move(pUndo), &rDocShell ),
+ bAutomatic );
+ }
+ }
+ rDocShell.SetDrawModified();
+ bDone = true;
+ }
+ return bDone;
+}
+
+static void lcl_collectAllPredOrSuccRanges(
+ const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens, ScDocShell& rDocShell,
+ bool bPred)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ vector<ScTokenRef> aRefTokens;
+ if (rSrcRanges.empty())
+ return;
+ ScRange const & rFrontRange = rSrcRanges.front();
+ ScDetectiveFunc aDetFunc(rDoc, rFrontRange.aStart.Tab());
+ for (size_t i = 0, n = rSrcRanges.size(); i < n; ++i)
+ {
+ ScRange const & r = rSrcRanges[i];
+ if (bPred)
+ {
+ aDetFunc.GetAllPreds(
+ r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
+ }
+ else
+ {
+ aDetFunc.GetAllSuccs(
+ r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
+ }
+ }
+ rRefTokens.swap(aRefTokens);
+}
+
+void ScDocFunc::DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
+{
+ lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true);
+}
+
+void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
+{
+ lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false);
+}
+
+bool ScDocFunc::DeleteContents(
+ const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ OSL_FAIL("ScDocFunc::DeleteContents without markings");
+ return false;
+ }
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ ScMarkData aMultiMark = rMark;
+ aMultiMark.SetMarking(false); // for MarkToMulti
+
+ ScDocumentUniquePtr pUndoDoc;
+ bool bMulti = aMultiMark.IsMultiMarked();
+ aMultiMark.MarkToMulti();
+ const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
+ ScRange aExtendedRange(aMarkRange);
+ if ( rDoc.ExtendMerge( aExtendedRange, true ) )
+ bMulti = false;
+
+ // no objects on protected tabs
+ bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
+
+ sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted
+ if ( nFlags & InsertDeleteFlags::ATTRIB )
+ rDocShell.UpdatePaintExt( nExtFlags, aMarkRange );
+
+ // order of operations:
+ // 1) BeginDrawUndo
+ // 2) Delete objects (DrawUndo will be filled)
+ // 3) Copy content for undo and set up undo actions
+ // 4) Delete content
+
+ bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);
+ if (bRecord && bDrawUndo)
+ rDoc.BeginDrawUndo();
+
+ if (bObjects)
+ {
+ if (bMulti)
+ rDoc.DeleteObjectsInSelection( aMultiMark );
+ else
+ rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(),
+ aMultiMark );
+ }
+
+ // To keep track of all non-empty cells within the deleted area.
+ std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
+
+ if ( bRecord )
+ {
+ pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti);
+ pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange);
+ }
+
+ rDoc.DeleteSelection( nFlags, aMultiMark );
+
+ // add undo action after drawing undo is complete (objects and note captions)
+ if( bRecord )
+ {
+ sc::DocFuncUtil::addDeleteContentsUndo(
+ rDocShell.GetUndoManager(), &rDocShell, aMultiMark, aExtendedRange,
+ std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo);
+ }
+
+ if (!AdjustRowHeight( aExtendedRange, true, bApi ))
+ rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
+ else if (nExtFlags & SC_PF_LINES)
+ lcl_PaintAbove( rDocShell, aExtendedRange ); // for lines above the range
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::DeleteCell(
+ const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator(rDocShell);
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester(rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
+ if (!aTester.IsEditable())
+ {
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ // no objects on protected tabs
+ bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
+
+ sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ rDocShell.UpdatePaintExt(nExtFlags, rPos);
+
+ // order of operations:
+ // 1) BeginDrawUndo
+ // 2) delete objects (DrawUndo is filled)
+ // 3) copy contents for undo
+ // 4) delete contents
+ // 5) add undo-action
+
+ bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE); // needed for shown notes
+ if (bDrawUndo && bRecord)
+ rDoc.BeginDrawUndo();
+
+ if (bObjects)
+ rDoc.DeleteObjectsInArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
+
+ // To keep track of all non-empty cells within the deleted area.
+ std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (bRecord)
+ {
+ pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, rMark, rPos, nFlags, false);
+ pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, rPos);
+ }
+
+ rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, nFlags);
+
+ if (bRecord)
+ {
+ sc::DocFuncUtil::addDeleteContentsUndo(
+ rDocShell.GetUndoManager(), &rDocShell, rMark, rPos, std::move(pUndoDoc),
+ nFlags, pDataSpans, false, bDrawUndo);
+ }
+
+ if (!AdjustRowHeight(rPos, true, bApi))
+ rDocShell.PostPaint(
+ rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Tab(),
+ PaintPartFlags::Grid, nExtFlags);
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags nType,
+ bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ ScMarkData aMultiMark = rMark;
+ aMultiMark.SetMarking(false); // for MarkToMulti
+ aMultiMark.MarkToMulti();
+ const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
+
+ if (bRecord)
+ {
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, *pUndoDoc, &aMultiMark);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTransliterate>( &rDocShell, aMultiMark, std::move(pUndoDoc), nType ) );
+ }
+
+ rDoc.TransliterateText( aMultiMark, nType );
+
+ if (!AdjustRowHeight( aMarkRange, true, true ))
+ rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid );
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ bool bEditDeleted = (rDoc.GetCellType(rPos) == CELLTYPE_EDIT);
+ ScUndoEnterData::ValuesType aOldValues;
+
+ if (bUndo)
+ {
+ ScUndoEnterData::Value aOldValue;
+
+ aOldValue.mnTab = rPos.Tab();
+ aOldValue.maCell.assign(rDoc, rPos);
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(),rPos.Row(),rPos.Tab() );
+ if ( const SfxUInt32Item* pItem = pPattern->GetItemSet().GetItemIfSet(
+ ATTR_VALUE_FORMAT,false) )
+ {
+ aOldValue.mbHasFormat = true;
+ aOldValue.mnFormat = pItem->GetValue();
+ }
+ else
+ aOldValue.mbHasFormat = false;
+
+ aOldValues.push_back(aOldValue);
+ }
+
+ o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText );
+
+ if (bUndo)
+ {
+ // because of ChangeTracking, UndoAction can be created only after SetString was called
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterData>(&rDocShell, rPos, aOldValues, rText, nullptr));
+ }
+
+ if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), HasAttrFlags::NeedHeight ) )
+ AdjustRowHeight( ScRange(rPos), true, bApi );
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // notify input handler here the same way as in PutCell
+ if (bApi)
+ NotifyInputHandler( rPos );
+
+ const SfxUInt32Item* pItem = rDoc.GetAttr(rPos, ATTR_VALIDDATA);
+ const ScValidationData* pData = rDoc.GetValidationEntry(pItem->GetValue());
+ if (pData)
+ {
+ ScRefCellValue aCell(rDoc, rPos);
+ if (pData->IsDataValid(aCell, rPos))
+ ScDetectiveFunc(rDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
+ }
+
+ return true;
+}
+
+bool ScDocFunc::SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ rDoc.SetValue(rPos, fVal);
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+void ScDocFunc::SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ // Check for invalid range.
+ SCROW nLastRow = rPos.Row() + aVals.size() - 1;
+ if (nLastRow > rDoc.MaxRow())
+ // out of bound.
+ return;
+
+ ScRange aRange(rPos);
+ aRange.aEnd.SetRow(nLastRow);
+
+ ScDocShellModificator aModificator(rDocShell);
+
+ if (rDoc.IsUndoEnabled())
+ {
+ std::unique_ptr<sc::UndoSetCells> pUndoObj(new sc::UndoSetCells(&rDocShell, rPos));
+ rDoc.TransferCellValuesTo(rPos, aVals.size(), pUndoObj->GetOldValues());
+ pUndoObj->SetNewValues(aVals);
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ pUndoMgr->AddUndoAction(std::move(pUndoObj));
+ }
+
+ rDoc.SetValues(rPos, aVals);
+
+ rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler(rPos);
+}
+
+bool ScDocFunc::SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(rPos, rStr, &aParam);
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+bool ScDocFunc::SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ rDoc.SetEditText(rPos, rStr.Clone());
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+bool ScDocFunc::SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (ScStringUtil::isMultiline(rStr))
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject());
+ return SetEditCell(rPos, *pEditText, bInteraction);
+ }
+ else
+ return SetStringCell(rPos, rStr, bInteraction);
+}
+
+bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction )
+{
+ std::unique_ptr<ScFormulaCell> xCell(pCell);
+
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
+
+ ScCellValue aOldVal;
+ if (bUndo)
+ aOldVal.assign(rDoc, rPos);
+
+ pCell = rDoc.SetFormulaCell(rPos, xCell.release());
+
+ // For performance reasons API calls may disable calculation while
+ // operating and recalculate once when done. If through user interaction
+ // and AutoCalc is disabled, calculate the formula (without its
+ // dependencies) once so the result matches the current document's content.
+ if (bInteraction && !rDoc.GetAutoCalc() && pCell)
+ {
+ // calculate just the cell once and set Dirty again
+ pCell->Interpret();
+ pCell->SetDirtyVar();
+ rDoc.PutInFormulaTree( pCell);
+ }
+
+ if (bUndo)
+ {
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ ScCellValue aNewVal;
+ aNewVal.assign(rDoc, rPos);
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
+ }
+
+ if (bHeight)
+ AdjustRowHeight(rPos, true, !bInteraction);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+bool ScDocFunc::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ const size_t nLength = rCells.size();
+ if (rPos.Row() + nLength - 1 > o3tl::make_unsigned(rDoc.MaxRow()))
+ // out of bound
+ return false;
+
+ ScRange aRange(rPos);
+ aRange.aEnd.IncRow(nLength - 1);
+
+ ScDocShellModificator aModificator( rDocShell );
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ std::unique_ptr<sc::UndoSetCells> pUndoObj;
+ if (bUndo)
+ {
+ pUndoObj.reset(new sc::UndoSetCells(&rDocShell, rPos));
+ rDoc.TransferCellValuesTo(rPos, nLength, pUndoObj->GetOldValues());
+ }
+
+ rDoc.SetFormulaCells(rPos, rCells);
+
+ // For performance reasons API calls may disable calculation while
+ // operating and recalculate once when done. If through user interaction
+ // and AutoCalc is disabled, calculate the formula (without its
+ // dependencies) once so the result matches the current document's content.
+ if (bInteraction && !rDoc.GetAutoCalc())
+ {
+ for (auto* pCell : rCells)
+ {
+ // calculate just the cell once and set Dirty again
+ pCell->Interpret();
+ pCell->SetDirtyVar();
+ rDoc.PutInFormulaTree( pCell);
+ }
+ }
+
+ if (bUndo)
+ {
+ pUndoObj->SetNewValues(rCells);
+ SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
+ pUndoMgr->AddUndoAction(std::move(pUndoObj));
+ }
+
+ rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
+ aModificator.SetDocumentModified();
+
+ // #103934#; notify editline and cell in edit mode
+ if (!bInteraction)
+ NotifyInputHandler( rPos );
+
+ return true;
+}
+
+void ScDocFunc::NotifyInputHandler( const ScAddress& rPos )
+{
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( !(pViewSh && pViewSh->GetViewData().GetDocShell() == &rDocShell) )
+ return;
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl && pInputHdl->GetCursorPos() == rPos )
+ {
+ bool bIsEditMode(pInputHdl->IsEditMode());
+
+ // set modified if in editmode, because so the string is not set in the InputWindow like in the cell
+ // (the cell shows the same like the InputWindow)
+ if (bIsEditMode)
+ pInputHdl->SetModified();
+ pViewSh->UpdateInputHandler(false, !bIsEditMode);
+ }
+}
+
+namespace {
+
+ struct ScMyRememberItem
+ {
+ sal_Int32 nIndex;
+ SfxItemSet aItemSet;
+
+ ScMyRememberItem(SfxItemSet _aItemSet, sal_Int32 nTempIndex) :
+ nIndex(nTempIndex), aItemSet(std::move(_aItemSet)) {}
+ };
+
+}
+
+void ScDocFunc::PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi )
+{
+ // PutData calls PutCell or SetNormalString
+
+ bool bRet = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScEditAttrTester aTester( &rEngine );
+ bool bEditCell = aTester.NeedsObject();
+ if ( bEditCell )
+ {
+ // #i61702# With bLoseContent set, the content of rEngine isn't restored
+ // (used in loading XML, where after the removeActionLock call the API object's
+ // EditEngine isn't accessed again.
+ bool bLoseContent = rDoc.IsImportingXML();
+
+ const bool bUpdateMode = rEngine.SetUpdateLayout(false);
+
+ std::vector<std::unique_ptr<ScMyRememberItem>> aRememberItems;
+
+ // All paragraph attributes must be removed before calling CreateTextObject,
+ // not only alignment, so the object doesn't contain the cell attributes as
+ // paragraph attributes. Before removing the attributes store them in a vector to
+ // set them back to the EditEngine.
+ sal_Int32 nCount = rEngine.GetParagraphCount();
+ for (sal_Int32 i=0; i<nCount; i++)
+ {
+ const SfxItemSet& rOld = rEngine.GetParaAttribs( i );
+ if ( rOld.Count() )
+ {
+ if ( !bLoseContent )
+ {
+ aRememberItems.push_back(std::make_unique<ScMyRememberItem>(rEngine.GetParaAttribs(i), i));
+ }
+ rEngine.SetParaAttribs( i, SfxItemSet( *rOld.GetPool(), rOld.GetRanges() ) );
+ }
+ }
+
+ // A copy of pNewData will be stored in the cell.
+ std::unique_ptr<EditTextObject> pNewData(rEngine.CreateTextObject());
+ bRet = SetEditCell(rPos, *pNewData, !bApi);
+
+ // Set the paragraph attributes back to the EditEngine.
+ for (const auto& rxItem : aRememberItems)
+ {
+ rEngine.SetParaAttribs(rxItem->nIndex, rxItem->aItemSet);
+ }
+
+ // #i61702# if the content isn't accessed, there's no need to set the UpdateMode again
+ if ( bUpdateMode && !bLoseContent )
+ rEngine.SetUpdateLayout(true);
+ }
+ else
+ {
+ OUString aText = rEngine.GetText();
+ if (aText.isEmpty())
+ {
+ bool bNumFmtSet = false;
+ bRet = SetNormalString( bNumFmtSet, rPos, aText, bApi );
+ }
+ else
+ bRet = SetStringCell(rPos, aText, !bApi);
+ }
+
+ if ( !(bRet && aTester.NeedsCellAttr()) )
+ return;
+
+ const SfxItemSet& rEditAttr = aTester.GetAttribs();
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetFromEditItemSet( &rEditAttr );
+ aPattern.DeleteUnchanged( rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ) );
+ aPattern.GetItemSet().ClearItem( ATTR_HOR_JUSTIFY ); // wasn't removed above if no edit object
+ if ( aPattern.GetItemSet().Count() > 0 )
+ {
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectTable( rPos.Tab(), true );
+ aMark.SetMarkArea( ScRange( rPos ) );
+ ApplyAttributes( aMark, aPattern, bApi );
+ }
+}
+
+bool ScDocFunc::SetCellText(
+ const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
+ const formula::FormulaGrammar::Grammar eGrammar )
+{
+ bool bSet = false;
+ if ( bInterpret )
+ {
+ if ( bEnglish )
+ {
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ ::std::optional<ScExternalRefManager::ApiGuard> pExtRefGuard;
+ if (bApi)
+ pExtRefGuard.emplace(rDoc);
+
+ ScInputStringType aRes =
+ ScStringUtil::parseInputString(*rDoc.GetFormatTable(), rText, LANGUAGE_ENGLISH_US);
+
+ switch (aRes.meType)
+ {
+ case ScInputStringType::Formula:
+ bSet = SetFormulaCell(rPos, new ScFormulaCell(rDoc, rPos, aRes.maText, eGrammar), !bApi);
+ break;
+ case ScInputStringType::Number:
+ bSet = SetValueCell(rPos, aRes.mfValue, !bApi);
+ break;
+ case ScInputStringType::Text:
+ bSet = SetStringOrEditCell(rPos, aRes.maText, !bApi);
+ break;
+ default:
+ ;
+ }
+ }
+ // otherwise keep Null -> SetString with local formulas/number formats
+ }
+ else if (!rText.isEmpty())
+ {
+ bSet = SetStringOrEditCell(rPos, rText, !bApi);
+ }
+
+ if (!bSet)
+ {
+ bool bNumFmtSet = false;
+ bSet = SetNormalString( bNumFmtSet, rPos, rText, bApi );
+ }
+ return bSet;
+}
+
+bool ScDocFunc::ShowNote( const ScAddress& rPos, bool bShow )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScPostIt* pNote = rDoc.GetNote( rPos );
+ if( !pNote || (bShow == pNote->IsCaptionShown()) ||
+ (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) )
+ return false;
+
+ // move the caption to internal or hidden layer and create undo action
+ pNote->ShowCaption( rPos, bShow );
+ if( rDoc.IsUndoEnabled() )
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideNote>( rDocShell, rPos, bShow ) );
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ ScTabView::OnLOKNoteStateChanged(pNote);
+
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ {
+ if (ScDrawView* pDrawView = pViewData->GetScDrawView())
+ pDrawView->SyncForGrid( pNote->GetCaption());
+ }
+
+ rDocShell.SetDocumentModified();
+
+ return true;
+}
+
+void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ OUString aNewText = convertLineEnd(rText, GetSystemLineEnd()); //! is this necessary ???
+
+ if( ScPostIt* pNote = (!aNewText.isEmpty()) ? rDoc.GetOrCreateNote( rPos ) : rDoc.GetNote(rPos) )
+ pNote->SetText( rPos, aNewText );
+
+ //! Undo !!!
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ rDocShell.PostPaintCell( rPos );
+ aModificator.SetDocumentModified();
+}
+
+void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
+ if (aTester.IsEditable())
+ {
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ SfxUndoManager* pUndoMgr = (pDrawLayer && rDoc.IsUndoEnabled()) ? rDocShell.GetUndoManager() : nullptr;
+
+ ScNoteData aOldData;
+ std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
+ sal_uInt32 nNoteId = 0;
+ if( pOldNote )
+ {
+ nNoteId = pOldNote->GetId();
+ // ensure existing caption object before draw undo tracking starts
+ pOldNote->GetOrCreateCaption( rPos );
+ // rescue note data for undo
+ aOldData = pOldNote->GetNoteData();
+ }
+
+ // collect drawing undo actions for deleting/inserting caption objects
+ if( pUndoMgr )
+ pDrawLayer->BeginCalcUndo(false);
+
+ // delete the note (creates drawing undo action for the caption object)
+ bool hadOldNote(pOldNote);
+ pOldNote.reset();
+
+ // create new note (creates drawing undo action for the new caption object)
+ ScNoteData aNewData;
+ ScPostIt* pNewNote = nullptr;
+ if( (pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, nNoteId )) )
+ {
+ if( pAuthor ) pNewNote->SetAuthor( *pAuthor );
+ if( pDate ) pNewNote->SetDate( *pDate );
+
+ // rescue note data for undo
+ aNewData = pNewNote->GetNoteData();
+ }
+
+ // create the undo action
+ if( pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption) )
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( rDocShell, rPos, aOldData, aNewData, pDrawLayer->GetCalcUndo() ) );
+
+ // repaint cell (to make note marker visible)
+ rDocShell.PostPaintCell( rPos );
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ aModificator.SetDocumentModified();
+
+ // Let our LOK clients know about the new/modified note
+ if (pNewNote)
+ {
+ ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify : LOKCommentNotificationType::Add,
+ rDoc, rPos, pNewNote);
+ }
+ }
+ else if (!bApi)
+ {
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ }
+}
+
+void ScDocFunc::ImportNote( const ScAddress& rPos,
+ std::unique_ptr<GenerateNoteCaption> xGenerator,
+ const tools::Rectangle& rCaptionRect, bool bShown )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
+ SAL_WARN_IF(pOldNote, "sc.ui", "imported data has >1 notes on same cell? at pos " << rPos);
+
+ // create new note
+ ScNoteUtil::CreateNoteFromGenerator(rDoc, rPos, std::move(xGenerator),
+ rCaptionRect, bShown);
+
+ rDoc.SetStreamValid(rPos.Tab(), false);
+
+ aModificator.SetDocumentModified();
+}
+
+bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern,
+ bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if ( !rDoc.IsUndoEnabled() )
+ bRecord = false;
+
+ bool bImportingXML = rDoc.IsImportingXML();
+ // Cell formats can still be set if the range isn't editable only because of matrix formulas.
+ // #i62483# When loading XML, the check can be skipped altogether.
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
+ && !bOnlyNotBecauseOfMatrix )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return false;
+ }
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ //! Border
+
+ ScRange aMultiRange;
+ bool bMulti = rMark.IsMultiMarked();
+ if ( bMulti )
+ aMultiRange = rMark.GetMultiMarkArea();
+ else
+ aMultiRange = rMark.GetMarkArea();
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc( new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, aMultiRange.aStart.Tab(), aMultiRange.aEnd.Tab() );
+ rDoc.CopyToDocument(aMultiRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionAttr>(
+ &rDocShell, rMark,
+ aMultiRange.aStart.Col(), aMultiRange.aStart.Row(), aMultiRange.aStart.Tab(),
+ aMultiRange.aEnd.Col(), aMultiRange.aEnd.Row(), aMultiRange.aEnd.Tab(),
+ std::move(pUndoDoc), bMulti, &rPattern ) );
+ }
+
+ // While loading XML it is not necessary to ask HasAttrib. It needs too much time.
+ sal_uInt16 nExtFlags = 0;
+ if ( !bImportingXML )
+ rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content before the change
+
+ bool bChanged = false;
+ rDoc.ApplySelectionPattern( rPattern, rMark, nullptr, &bChanged );
+
+ if(bChanged)
+ {
+ if ( !bImportingXML )
+ rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content after the change
+
+ if (!AdjustRowHeight( aMultiRange, true, bApi ))
+ rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags );
+ else if (nExtFlags & SC_PF_LINES)
+ lcl_PaintAbove( rDocShell, aMultiRange ); // because of lines above the range
+
+ aModificator.SetDocumentModified();
+ }
+
+ return true;
+}
+
+bool ScDocFunc::ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName,
+ bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if ( !rDoc.IsUndoEnabled() )
+ bRecord = false;
+
+ bool bImportingXML = rDoc.IsImportingXML();
+ // Cell formats can still be set if the range isn't editable only because of matrix formulas.
+ // #i62483# When loading XML, the check can be skipped altogether.
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
+ && !bOnlyNotBecauseOfMatrix )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return false;
+ }
+
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( rDoc.GetStyleSheetPool()->Find(
+ rStyleName, SfxStyleFamily::Para ));
+ if (!pStyleSheet)
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScRange aMultiRange;
+ bool bMulti = rMark.IsMultiMarked();
+ if ( bMulti )
+ aMultiRange = rMark.GetMultiMarkArea();
+ else
+ aMultiRange = rMark.GetMarkArea();
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nStartTab = aMultiRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aMultiRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionStyle>(
+ &rDocShell, rMark, aMultiRange, rStyleName, std::move(pUndoDoc) ) );
+
+ }
+
+ rDoc.ApplySelectionStyle( *pStyleSheet, rMark );
+
+ if (!AdjustRowHeight( aMultiRange, true, bApi ))
+ rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid );
+
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+namespace {
+
+/**
+ * Check if this insertion attempt would end up cutting one or more pivot
+ * tables in half, which is not desirable.
+ *
+ * @return true if this insertion can be done safely without shearing any
+ * existing pivot tables, false otherwise.
+ */
+bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd, const ScDocument& rDoc)
+{
+ if (!rDoc.HasPivotTable())
+ // This document has no pivot tables.
+ return true;
+
+ const ScDPCollection* pDPs = rDoc.GetDPCollection();
+
+ ScRange aRange(rRange); // local copy
+ switch (eCmd)
+ {
+ case INS_INSROWS_BEFORE:
+ {
+ aRange.aStart.SetCol(0);
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ [[fallthrough]];
+ }
+ case INS_CELLSDOWN:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ // Start row must be either at the top or above any pivot tables.
+ if (aRange.aStart.Row() < 0)
+ // I don't know how to handle this case.
+ return false;
+
+ if (aRange.aStart.Row() == 0)
+ // First row is always allowed.
+ return true;
+
+ ScRange aTest(aRange);
+ aTest.aStart.IncRow(-1); // Test one row up.
+ aTest.aEnd.SetRow(aTest.aStart.Row());
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ case INS_INSCOLS_BEFORE:
+ {
+ aRange.aStart.SetRow(0);
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ [[fallthrough]];
+ }
+ case INS_CELLSRIGHT:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ // Start row must be either at the top or above any pivot tables.
+ if (aRange.aStart.Col() < 0)
+ // I don't know how to handle this case.
+ return false;
+
+ if (aRange.aStart.Col() == 0)
+ // First row is always allowed.
+ return true;
+
+ ScRange aTest(aRange);
+ aTest.aStart.IncCol(-1); // Test one column to the left.
+ aTest.aEnd.SetCol(aTest.aStart.Col());
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return true;
+}
+
+/**
+ * Check if this deletion attempt would end up cutting one or more pivot
+ * tables in half, which is not desirable.
+ *
+ * @return true if this deletion can be done safely without shearing any
+ * existing pivot tables, false otherwise.
+ */
+bool canDeleteCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, DelCellCmd eCmd, const ScDocument& rDoc)
+{
+ if (!rDoc.HasPivotTable())
+ // This document has no pivot tables.
+ return true;
+
+ const ScDPCollection* pDPs = rDoc.GetDPCollection();
+
+ ScRange aRange(rRange); // local copy
+
+ switch (eCmd)
+ {
+ case DelCellCmd::Rows:
+ {
+ aRange.aStart.SetCol(0);
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ [[fallthrough]];
+ }
+ case DelCellCmd::CellsUp:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ ScRange aTest(aRange);
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ case DelCellCmd::Cols:
+ {
+ aRange.aStart.SetRow(0);
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ [[fallthrough]];
+ }
+ case DelCellCmd::CellsLeft:
+ {
+ auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
+ return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
+ if (bIntersects)
+ // This column range cuts through at least one pivot table. Not good.
+ return false;
+
+ ScRange aTest(aRange);
+ for (const auto& rTab : rMarkData)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (pDPs->HasTable(aTest))
+ return false;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ return true;
+}
+
+}
+
+bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, InsCellCmd eCmd,
+ bool bRecord, bool bApi, bool bPartOfPaste )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (rDocShell.GetDocument().GetChangeTrack() &&
+ ((eCmd == INS_CELLSDOWN && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
+ (eCmd == INS_CELLSRIGHT && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
+ {
+ // We should not reach this via UI disabled slots.
+ assert(bApi);
+ SAL_WARN("sc.ui","ScDocFunc::InsertCells - no change-tracking of partial cell shift");
+ return false;
+ }
+
+ ScRange aTargetRange( rRange );
+
+ // If insertion is for full cols/rows and after the current
+ // selection, then shift the range accordingly
+ if ( eCmd == INS_INSROWS_AFTER )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aTargetRange.Move(0, rRange.aEnd.Row() - rRange.aStart.Row() + 1, 0, aErrorRange, rDoc))
+ {
+ return false;
+ }
+ }
+ if ( eCmd == INS_INSCOLS_AFTER )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ if (!aTargetRange.Move(rRange.aEnd.Col() - rRange.aStart.Col() + 1, 0, 0, aErrorRange, rDoc))
+ {
+ return false;
+ }
+ }
+
+ SCCOL nStartCol = aTargetRange.aStart.Col();
+ SCROW nStartRow = aTargetRange.aStart.Row();
+ SCTAB nStartTab = aTargetRange.aStart.Tab();
+ SCCOL nEndCol = aTargetRange.aEnd.Col();
+ SCROW nEndRow = aTargetRange.aEnd.Row();
+ SCTAB nEndTab = aTargetRange.aEnd.Tab();
+
+ if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
+ {
+ OSL_FAIL("invalid row in InsertCells");
+ return false;
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCCOL nPaintStartCol = nStartCol;
+ SCROW nPaintStartRow = nStartRow;
+ SCCOL nPaintEndCol = nEndCol;
+ SCROW nPaintEndRow = nEndRow;
+ PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
+ bool bSuccess;
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); //preserve current cursor position
+ SCCOL nCursorCol = 0;
+ SCROW nCursorRow = 0;
+ if( pViewSh )
+ {
+ nCursorCol = pViewSh->GetViewData().GetCurX();
+ nCursorRow = pViewSh->GetViewData().GetCurY();
+ }
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ SCTAB nCount = 0;
+ for( SCTAB i=0; i<nTabCount; i++ )
+ {
+ if( !rDoc.IsScenario(i) )
+ {
+ nCount++;
+ if( nCount == nEndTab+1 )
+ {
+ aMark.SelectTable( i, true );
+ break;
+ }
+ }
+ }
+ }
+
+ ScMarkData aFullMark( aMark ); // including scenario sheets
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ aFullMark.SelectTable( j, true );
+ }
+
+ SCTAB nSelCount = aMark.GetSelectCount();
+
+ // Adjust also related scenarios
+
+ SCCOL nMergeTestStartCol = nStartCol;
+ SCROW nMergeTestStartRow = nStartRow;
+ SCCOL nMergeTestEndCol = nEndCol;
+ SCROW nMergeTestEndRow = nEndRow;
+
+ ScRange aExtendMergeRange( aTargetRange );
+
+ if( aTargetRange.aStart == aTargetRange.aEnd && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged) )
+ {
+ rDoc.ExtendMerge( aExtendMergeRange );
+ rDoc.ExtendOverlapped( aExtendMergeRange );
+ nMergeTestEndCol = aExtendMergeRange.aEnd.Col();
+ nMergeTestEndRow = aExtendMergeRange.aEnd.Row();
+ nPaintEndCol = nMergeTestEndCol;
+ nPaintEndRow = nMergeTestEndRow;
+ }
+
+ if ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER )
+ {
+ nMergeTestStartCol = 0;
+ nMergeTestEndCol = rDoc.MaxCol();
+ }
+ if ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
+ {
+ nMergeTestStartRow = 0;
+ nMergeTestEndRow = rDoc.MaxRow();
+ }
+ if ( eCmd == INS_CELLSDOWN )
+ nMergeTestEndRow = rDoc.MaxRow();
+ if ( eCmd == INS_CELLSRIGHT )
+ nMergeTestEndCol = rDoc.MaxCol();
+
+ bool bNeedRefresh = false;
+
+ SCCOL nEditTestEndCol = (eCmd==INS_INSCOLS_BEFORE || eCmd==INS_INSCOLS_AFTER) ? rDoc.MaxCol() : nMergeTestEndCol;
+ SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? rDoc.MaxRow() : nMergeTestEndRow;
+
+ ScEditableTester aTester;
+
+ switch (eCmd)
+ {
+ case INS_INSCOLS_BEFORE:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertColumnsBefore, nMergeTestStartCol, nMergeTestEndCol, aMark);
+ break;
+ case INS_INSCOLS_AFTER:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertColumnsAfter, nMergeTestStartCol, nMergeTestEndCol, aMark);
+ break;
+ case INS_INSROWS_BEFORE:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertRowsBefore, nMergeTestStartRow, nMergeTestEndRow, aMark);
+ break;
+ case INS_INSROWS_AFTER:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::InsertRowsAfter, nMergeTestStartRow, nMergeTestEndRow, aMark);
+ break;
+ default:
+ aTester = ScEditableTester(
+ rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark);
+ }
+
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ // Check if this insertion is allowed with respect to pivot table.
+ if (!canInsertCellsByPivot(aTargetRange, aMark, eCmd, rDoc))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
+ return false;
+ }
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas at UpdateReference
+
+ ScDocumentUniquePtr pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if ( bRecord )
+ {
+ pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
+
+ // pRefUndoDoc is filled in InsertCol / InsertRow
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction
+ // the patch comes from mloiseleur and maoyg
+ bool bInsertMerge = false;
+ std::vector<ScRange> qIncreaseRange;
+ OUString aUndo = ScResId( STR_UNDO_INSERTCELLS );
+ if (bRecord)
+ {
+ ViewShellId nViewShellId(-1);
+ if (pViewSh)
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+ std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
+
+ for (const SCTAB i : aMark)
+ {
+ if (i >= nTabCount)
+ break;
+
+ if( rDoc.HasAttrib( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ if (eCmd==INS_CELLSRIGHT)
+ bNeedRefresh = true;
+
+ SCCOL nMergeStartCol = nMergeTestStartCol;
+ SCROW nMergeStartRow = nMergeTestStartRow;
+ SCCOL nMergeEndCol = nMergeTestEndCol;
+ SCROW nMergeEndRow = nMergeTestEndRow;
+
+ rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+ rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+
+ if(( eCmd == INS_CELLSDOWN && ( nMergeStartCol != nMergeTestStartCol || nMergeEndCol != nMergeTestEndCol )) ||
+ (eCmd == INS_CELLSRIGHT && ( nMergeStartRow != nMergeTestStartRow || nMergeEndRow != nMergeTestEndRow )) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+
+ SCCOL nTestCol = -1;
+ SCROW nTestRow1 = -1;
+ SCROW nTestRow2 = -1;
+
+ ScDocAttrIterator aTestIter( rDoc, i, nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow );
+ ScRange aExtendRange( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
+ const ScPatternAttr* pPattern = nullptr;
+ while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
+ {
+ const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
+ ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
+ if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
+ {
+ ScRange aRange( nTestCol, nTestRow1, i );
+ rDoc.ExtendOverlapped(aRange);
+ rDoc.ExtendMerge(aRange, true);
+
+ if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
+ {
+ for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
+ {
+ ScRange aTestRange( nTestCol, nTestRow, i );
+ rDoc.ExtendOverlapped( aTestRange );
+ rDoc.ExtendMerge( aTestRange, true);
+ ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qIncreaseRange.push_back( aTestRange );
+ bInsertMerge = true;
+ }
+ }
+ }
+ else
+ {
+ ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qIncreaseRange.push_back( aRange );
+ }
+ bInsertMerge = true;
+ }
+ }
+ }
+
+ if( bInsertMerge )
+ {
+ if( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN )
+ {
+ nStartRow = aExtendMergeRange.aStart.Row();
+ nEndRow = aExtendMergeRange.aEnd.Row();
+
+ if( eCmd == INS_CELLSDOWN )
+ nEndCol = nMergeTestEndCol;
+ else
+ {
+ nStartCol = 0;
+ nEndCol = rDoc.MaxCol();
+ }
+ }
+ else if( eCmd == INS_CELLSRIGHT || eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
+ {
+
+ nStartCol = aExtendMergeRange.aStart.Col();
+ nEndCol = aExtendMergeRange.aEnd.Col();
+ if( eCmd == INS_CELLSRIGHT )
+ {
+ nEndRow = nMergeTestEndRow;
+ }
+ else
+ {
+ nStartRow = 0;
+ nEndRow = rDoc.MaxRow();
+ }
+ }
+
+ if( !qIncreaseRange.empty() )
+ {
+ if (bRecord && !pUndoRemoveMerge)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin());
+ pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
+ }
+
+ for( const ScRange& aRange : qIncreaseRange )
+ {
+ if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+ }
+ }
+
+ if (bRecord && pUndoRemoveMerge)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
+ }
+
+ switch (eCmd)
+ {
+ case INS_CELLSDOWN:
+ bSuccess = rDoc.InsertRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintEndRow = rDoc.MaxRow();
+ break;
+ case INS_INSROWS_BEFORE:
+ case INS_INSROWS_AFTER:
+ bSuccess = rDoc.InsertRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintStartCol = 0;
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintFlags |= PaintPartFlags::Left;
+ break;
+ case INS_CELLSRIGHT:
+ bSuccess = rDoc.InsertCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintEndCol = rDoc.MaxCol();
+ break;
+ case INS_INSCOLS_BEFORE:
+ case INS_INSCOLS_AFTER:
+ bSuccess = rDoc.InsertCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
+ nPaintStartRow = 0;
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintFlags |= PaintPartFlags::Top;
+ break;
+ default:
+ OSL_FAIL("Wrong code at inserting");
+ bSuccess = false;
+ break;
+ }
+
+ if ( bSuccess )
+ {
+ SCTAB nUndoPos = 0;
+
+ if ( bRecord )
+ {
+ std::unique_ptr<SCTAB[]> pTabs(new SCTAB[nSelCount]);
+ std::unique_ptr<SCTAB[]> pScenarios(new SCTAB[nSelCount]);
+ nUndoPos = 0;
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nCount = 0;
+ for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nCount ++;
+
+ pScenarios[nUndoPos] = nCount;
+ pTabs[nUndoPos] = rTab;
+ nUndoPos ++;
+ }
+
+ if( !bInsertMerge )
+ {
+ rDocShell.GetUndoManager()->LeaveListAction();
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertCells>(
+ &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
+ nUndoPos, std::move(pTabs), std::move(pScenarios), eCmd, std::move(pRefUndoDoc), std::move(pUndoData), bPartOfPaste ) );
+ }
+
+ // #i8302 : we remerge growing ranges, with the new part inserted
+
+ while( !qIncreaseRange.empty() )
+ {
+ ScRange aRange = qIncreaseRange.back();
+ if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ switch (eCmd)
+ {
+ case INS_CELLSDOWN:
+ case INS_INSROWS_BEFORE:
+ case INS_INSROWS_AFTER:
+ aRange.aEnd.IncRow(static_cast<SCCOL>(nEndRow-nStartRow+1));
+ break;
+ case INS_CELLSRIGHT:
+ case INS_INSCOLS_BEFORE:
+ case INS_INSCOLS_AFTER:
+ aRange.aEnd.IncCol(static_cast<SCCOL>(nEndCol-nStartCol+1));
+ break;
+ default:
+ break;
+ }
+ ScCellMergeOption aMergeOption(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ aMergeOption.maTabs.insert(aRange.aStart.Tab());
+ MergeCells(aMergeOption, false, true, true);
+ }
+ qIncreaseRange.pop_back();
+ }
+
+ if( bInsertMerge )
+ rDocShell.GetUndoManager()->LeaveListAction();
+
+ for (const SCTAB i : aMark)
+ {
+ if (i >= nTabCount)
+ break;
+
+ rDoc.SetDrawPageSize(i);
+
+ if (bNeedRefresh)
+ rDoc.ExtendMerge( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i, true );
+ else
+ rDoc.RefreshAutoFilter( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i );
+
+ if ( eCmd == INS_INSROWS_BEFORE ||eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSROWS_AFTER ||eCmd == INS_INSCOLS_AFTER )
+ rDoc.UpdatePageBreaks( i );
+
+ sal_uInt16 nExtFlags = 0;
+ rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i );
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = i+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ bool bAdjusted = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ) ?
+ AdjustRowHeight(ScRange(0, nStartRow, i, rDoc.MaxCol(), nEndRow, i+nScenarioCount ), true, bApi) :
+ AdjustRowHeight(ScRange(0, nPaintStartRow, i, rDoc.MaxCol(), nPaintEndRow, i+nScenarioCount ), true, bApi);
+ if (bAdjusted)
+ {
+ // paint only what is not done by AdjustRowHeight
+ if (nPaintFlags & PaintPartFlags::Top)
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, PaintPartFlags::Top );
+ }
+ else
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, nPaintFlags, nExtFlags );
+ }
+ }
+ else
+ {
+ if( bInsertMerge )
+ {
+ while( !qIncreaseRange.empty() )
+ {
+ ScRange aRange = qIncreaseRange.back();
+ ScCellMergeOption aMergeOption(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ MergeCells(aMergeOption, false, true, true);
+ qIncreaseRange.pop_back();
+ }
+
+ if( pViewSh )
+ {
+ pViewSh->MarkRange( aTargetRange, false );
+ pViewSh->SetCursor( nCursorCol, nCursorRow );
+ }
+ }
+
+ rDocShell.GetUndoManager()->LeaveListAction();
+ rDocShell.GetUndoManager()->RemoveLastUndoAction();
+
+ pRefUndoDoc.reset();
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_INSERT_FULL); // column/row full
+ }
+
+ // The cursor position needs to be modified earlier than updating
+ // any enabled edit view which is triggered by SetDocumentModified below.
+ if (bSuccess)
+ {
+ bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
+ bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
+
+ if (bInsertCols)
+ {
+ pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), 1);
+ }
+
+ if (bInsertRows)
+ {
+ pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), 1);
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+ return bSuccess;
+}
+
+bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, DelCellCmd eCmd,
+ bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (rDocShell.GetDocument().GetChangeTrack() &&
+ ((eCmd == DelCellCmd::CellsUp && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
+ (eCmd == DelCellCmd::CellsLeft && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
+ {
+ // We should not reach this via UI disabled slots.
+ assert(bApi);
+ SAL_WARN("sc.ui","ScDocFunc::DeleteCells - no change-tracking of partial cell shift");
+ return false;
+ }
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
+ {
+ OSL_FAIL("invalid row in DeleteCells");
+ return false;
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCCOL nPaintStartCol = nStartCol;
+ SCROW nPaintStartRow = nStartRow;
+ SCCOL nPaintEndCol = nEndCol;
+ SCROW nPaintEndRow = nEndRow;
+ PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ SCTAB nCount = 0;
+ for(SCTAB i=0; i<nTabCount; i++ )
+ {
+ if( !rDoc.IsScenario(i) )
+ {
+ nCount++;
+ if( nCount == nEndTab+1 )
+ {
+ aMark.SelectTable(i, true);
+ break;
+ }
+ }
+ }
+ }
+
+ ScMarkData aFullMark( aMark ); // including scenario sheets
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ aFullMark.SelectTable( j, true );
+ }
+
+ SCTAB nSelCount = aMark.GetSelectCount();
+
+ SCCOL nUndoStartCol = nStartCol;
+ SCROW nUndoStartRow = nStartRow;
+ SCCOL nUndoEndCol = nEndCol;
+ SCROW nUndoEndRow = nEndRow;
+
+ ScRange aExtendMergeRange( rRange );
+
+ if( rRange.aStart == rRange.aEnd && rDoc.HasAttrib(rRange, HasAttrFlags::Merged) )
+ {
+ rDoc.ExtendMerge( aExtendMergeRange );
+ rDoc.ExtendOverlapped( aExtendMergeRange );
+ nUndoEndCol = aExtendMergeRange.aEnd.Col();
+ nUndoEndRow = aExtendMergeRange.aEnd.Row();
+ nPaintEndCol = nUndoEndCol;
+ nPaintEndRow = nUndoEndRow;
+ }
+
+ if (eCmd==DelCellCmd::Rows)
+ {
+ nUndoStartCol = 0;
+ nUndoEndCol = rDoc.MaxCol();
+ }
+ if (eCmd==DelCellCmd::Cols)
+ {
+ nUndoStartRow = 0;
+ nUndoEndRow = rDoc.MaxRow();
+ }
+ // Test for cell protection
+
+ SCCOL nEditTestEndX = nUndoEndCol;
+ if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
+ nEditTestEndX = rDoc.MaxCol();
+ SCROW nEditTestEndY = nUndoEndRow;
+ if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
+ nEditTestEndY = rDoc.MaxRow();
+
+ ScEditableTester aTester;
+
+ switch (eCmd)
+ {
+ case DelCellCmd::Cols:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::DeleteColumns, nUndoStartCol, nUndoEndCol, aMark);
+ break;
+ case DelCellCmd::Rows:
+ aTester = ScEditableTester(
+ rDoc, sc::ColRowEditAction::DeleteRows, nUndoStartRow, nUndoEndRow, aMark);
+ break;
+ default:
+ aTester = ScEditableTester(
+ rDoc, nUndoStartCol, nUndoStartRow, nEditTestEndX, nEditTestEndY, aMark);
+ }
+
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ if (!canDeleteCellsByPivot(rRange, aMark, eCmd, rDoc))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
+ return false;
+ }
+ // Test for merged cells
+
+ SCCOL nMergeTestEndCol = (eCmd==DelCellCmd::CellsLeft) ? rDoc.MaxCol() : nUndoEndCol;
+ SCROW nMergeTestEndRow = (eCmd==DelCellCmd::CellsUp) ? rDoc.MaxRow() : nUndoEndRow;
+ SCCOL nExtendStartCol = nUndoStartCol;
+ SCROW nExtendStartRow = nUndoStartRow;
+ bool bNeedRefresh = false;
+
+ //Issue 8302 want to be able to insert into the middle of merged cells
+ //the patch comes from maoyg
+ ::std::vector<ScRange> qDecreaseRange;
+ bool bDeletingMerge = false;
+ OUString aUndo = ScResId( STR_UNDO_DELETECELLS );
+ if (bRecord)
+ {
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+ std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
+
+ for (const SCTAB i : aMark)
+ {
+ if (i >= nTabCount)
+ break;
+
+ if ( rDoc.HasAttrib( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ SCCOL nMergeStartCol = nUndoStartCol;
+ SCROW nMergeStartRow = nUndoStartRow;
+ SCCOL nMergeEndCol = nMergeTestEndCol;
+ SCROW nMergeEndRow = nMergeTestEndRow;
+
+ rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+ rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
+ if( ( eCmd == DelCellCmd::CellsUp && ( nMergeStartCol != nUndoStartCol || nMergeEndCol != nMergeTestEndCol))||
+ ( eCmd == DelCellCmd::CellsLeft && ( nMergeStartRow != nUndoStartRow || nMergeEndRow != nMergeTestEndRow)))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+
+ nExtendStartCol = nMergeStartCol;
+ nExtendStartRow = nMergeStartRow;
+ SCCOL nTestCol = -1;
+ SCROW nTestRow1 = -1;
+ SCROW nTestRow2 = -1;
+
+ ScDocAttrIterator aTestIter( rDoc, i, nUndoStartCol, nUndoStartRow, nMergeTestEndCol, nMergeTestEndRow );
+ ScRange aExtendRange( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
+ const ScPatternAttr* pPattern = nullptr;
+ while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
+ {
+ const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
+ ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
+ if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
+ {
+ ScRange aRange( nTestCol, nTestRow1, i );
+ rDoc.ExtendOverlapped( aRange );
+ rDoc.ExtendMerge( aRange, true );
+
+ if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
+ {
+ for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
+ {
+ ScRange aTestRange( nTestCol, nTestRow, i );
+ rDoc.ExtendOverlapped( aTestRange );
+ rDoc.ExtendMerge( aTestRange, true );
+ ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qDecreaseRange.push_back( aTestRange );
+ bDeletingMerge = true;
+ }
+ }
+ }
+ else
+ {
+ ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
+ if( !aExtendRange.Contains( aMergeRange ) )
+ {
+ qDecreaseRange.push_back( aRange );
+ }
+ bDeletingMerge = true;
+ }
+ }
+ }
+
+ if( bDeletingMerge )
+ {
+
+ if( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp )
+ {
+ nStartRow = aExtendMergeRange.aStart.Row();
+ nEndRow = aExtendMergeRange.aEnd.Row();
+ bNeedRefresh = true;
+
+ if( eCmd == DelCellCmd::CellsUp )
+ {
+ nEndCol = aExtendMergeRange.aEnd.Col();
+ }
+ else
+ {
+ nStartCol = 0;
+ nEndCol = rDoc.MaxCol();
+ }
+ }
+ else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
+ {
+
+ nStartCol = aExtendMergeRange.aStart.Col();
+ nEndCol = aExtendMergeRange.aEnd.Col();
+ if( eCmd == DelCellCmd::CellsLeft )
+ {
+ nEndRow = aExtendMergeRange.aEnd.Row();
+ bNeedRefresh = true;
+ }
+ else
+ {
+ nStartRow = 0;
+ nEndRow = rDoc.MaxRow();
+ }
+ }
+
+ if( !qDecreaseRange.empty() )
+ {
+ if (bRecord && !pUndoRemoveMerge)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin());
+ pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
+ }
+
+ for( const ScRange& aRange : qDecreaseRange )
+ {
+ if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
+ rDocShell.GetUndoManager()->LeaveListAction();
+ return false;
+ }
+ }
+ }
+
+ if (bRecord && pUndoRemoveMerge)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
+ }
+
+ // do it
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScDocument> pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if ( bRecord )
+ {
+ // With the fix for #101329#, UpdateRef always puts cells into pRefUndoDoc at their old position,
+ // so it's no longer necessary to copy more than the deleted range into pUndoDoc.
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, 0, nTabCount-1, (eCmd==DelCellCmd::Cols), (eCmd==DelCellCmd::Rows) );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ rDoc.CopyToDocument( nUndoStartCol, nUndoStartRow, rTab, nUndoEndCol, nUndoEndRow, rTab+nScenarioCount,
+ InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
+ }
+
+ pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ rDocShell.UpdatePaintExt( nExtFlags, nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab );
+ }
+
+ switch (eCmd)
+ {
+ case DelCellCmd::CellsUp:
+ case DelCellCmd::CellsLeft:
+ rDoc.DeleteObjectsInArea(nStartCol, nStartRow, nEndCol, nEndRow, aMark, true);
+ break;
+ case DelCellCmd::Rows:
+ rDoc.DeleteObjectsInArea(0, nStartRow, rDoc.MaxCol(), nEndRow, aMark, true);
+ break;
+ case DelCellCmd::Cols:
+ rDoc.DeleteObjectsInArea(nStartCol, 0, nEndCol, rDoc.MaxRow(), aMark, true);
+ break;
+ default:
+ break;
+ }
+
+
+ bool bUndoOutline = false;
+ switch (eCmd)
+ {
+ case DelCellCmd::CellsUp:
+ rDoc.DeleteRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), nullptr, &aFullMark );
+ nPaintEndRow = rDoc.MaxRow();
+ break;
+ case DelCellCmd::Rows:
+ rDoc.DeleteRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
+ nPaintStartCol = 0;
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintFlags |= PaintPartFlags::Left;
+ break;
+ case DelCellCmd::CellsLeft:
+ rDoc.DeleteCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), nullptr, &aFullMark );
+ nPaintEndCol = rDoc.MaxCol();
+ break;
+ case DelCellCmd::Cols:
+ rDoc.DeleteCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
+ nPaintStartRow = 0;
+ nPaintEndRow = rDoc.MaxRow();
+ nPaintEndCol = rDoc.MaxCol();
+ nPaintFlags |= PaintPartFlags::Top;
+ break;
+ default:
+ OSL_FAIL("Wrong code at deleting");
+ break;
+ }
+
+ //! Test if the size of outline has changed
+
+ if ( bRecord )
+ {
+ for (const auto& rTab : aFullMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ pRefUndoDoc->DeleteAreaTab(nUndoStartCol,nUndoStartRow,nUndoEndCol,nUndoEndRow, rTab, InsertDeleteFlags::ALL);
+ }
+
+ // for all sheets, so that formulas can be copied
+ pUndoDoc->AddUndoTab( 0, nTabCount-1 );
+
+ // copy with bColRowFlags=false (#54194#)
+ pRefUndoDoc->CopyToDocument(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB,InsertDeleteFlags::FORMULA,false,*pUndoDoc,nullptr,false);
+ pRefUndoDoc.reset();
+
+ std::unique_ptr<SCTAB[]> pTabs( new SCTAB[nSelCount]);
+ std::unique_ptr<SCTAB[]> pScenarios( new SCTAB[nSelCount]);
+ SCTAB nUndoPos = 0;
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nCount = 0;
+ for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nCount ++;
+
+ pScenarios[nUndoPos] = nCount;
+ pTabs[nUndoPos] = rTab;
+ nUndoPos ++;
+ }
+
+ if( !bDeletingMerge )
+ {
+ rDocShell.GetUndoManager()->LeaveListAction();
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDeleteCells>(
+ &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
+ nUndoPos, std::move(pTabs), std::move(pScenarios),
+ eCmd, std::move(pUndoDoc), std::move(pUndoData) ) );
+ }
+
+ // #i8302 want to be able to insert into the middle of merged cells
+ // the patch comes from maoyg
+
+ while( !qDecreaseRange.empty() )
+ {
+ ScRange aRange = qDecreaseRange.back();
+
+ sal_Int32 nDecreaseRowCount = 0;
+ sal_Int32 nDecreaseColCount = 0;
+ if( eCmd == DelCellCmd::CellsUp || eCmd == DelCellCmd::Rows )
+ {
+ if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
+ nDecreaseRowCount = nEndRow-nStartRow+1;
+ else if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow >= aRange.aStart.Row() && nEndRow >= aRange.aEnd.Row() )
+ nDecreaseRowCount = aRange.aEnd.Row()-nStartRow+1;
+ else if( nStartRow >= aRange.aStart.Row() && nStartRow >= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
+ nDecreaseRowCount = aRange.aEnd.Row()-nEndRow+1;
+ }
+ else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
+ {
+ if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
+ nDecreaseColCount = nEndCol-nStartCol+1;
+ else if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol >= aRange.aStart.Col() && nEndCol >= aRange.aEnd.Col() )
+ nDecreaseColCount = aRange.aEnd.Col()-nStartCol+1;
+ else if( nStartCol >= aRange.aStart.Col() && nStartCol >= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
+ nDecreaseColCount = aRange.aEnd.Col()-nEndCol+1;
+ }
+
+ switch (eCmd)
+ {
+ case DelCellCmd::CellsUp:
+ case DelCellCmd::Rows:
+ aRange.aEnd.SetRow(static_cast<SCCOL>( aRange.aEnd.Row()-nDecreaseRowCount));
+ break;
+ case DelCellCmd::CellsLeft:
+ case DelCellCmd::Cols:
+ aRange.aEnd.SetCol(static_cast<SCCOL>( aRange.aEnd.Col()-nDecreaseColCount));
+ break;
+ default:
+ break;
+ }
+
+ if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
+ {
+ ScCellMergeOption aMergeOption(aRange);
+ MergeCells( aMergeOption, false, true, true );
+ }
+ qDecreaseRange.pop_back();
+ }
+
+ if( bDeletingMerge )
+ rDocShell.GetUndoManager()->LeaveListAction();
+
+ if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
+ nMergeTestEndCol = rDoc.MaxCol();
+ if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
+ nMergeTestEndRow = rDoc.MaxRow();
+ if ( bNeedRefresh )
+ {
+ // #i51445# old merge flag attributes must be deleted also for single cells,
+ // not only for whole columns/rows
+
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr() );
+
+ rDoc.ApplyPatternArea( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, aMark, aPattern );
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ ScRange aMergedRange( nExtendStartCol, nExtendStartRow, rTab, nMergeTestEndCol, nMergeTestEndRow, rTab+nScenarioCount );
+ rDoc.ExtendMerge( aMergedRange, true );
+ }
+ }
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ rDoc.RefreshAutoFilter( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, rTab );
+ }
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ rDoc.SetDrawPageSize(rTab);
+
+ if ( eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::Rows )
+ rDoc.UpdatePageBreaks( rTab );
+
+ rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab );
+
+ SCTAB nScenarioCount = 0;
+
+ for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
+ nScenarioCount ++;
+
+ // delete entire rows: do not adjust
+ if ( eCmd == DelCellCmd::Rows || !AdjustRowHeight(ScRange( 0, nPaintStartRow, rTab, rDoc.MaxCol(), nPaintEndRow, rTab+nScenarioCount ), true, bApi) )
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, nPaintFlags, nExtFlags );
+ else
+ {
+ // paint only what is not done by AdjustRowHeight
+ if (nExtFlags & SC_PF_LINES)
+ lcl_PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) );
+ if (nPaintFlags & PaintPartFlags::Top)
+ rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, PaintPartFlags::Top );
+ }
+ }
+
+ // The cursor position needs to be modified earlier than updating
+ // any enabled edit view which is triggered by SetDocumentModified below.
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (pViewSh)
+ {
+ if (eCmd == DelCellCmd::Cols)
+ {
+ pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), -1);
+ }
+ if (eCmd == DelCellCmd::Rows)
+ {
+ pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), -1);
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ return true;
+}
+
+bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
+ bool bCut, bool bRecord, bool bPaint, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nStartCol = rSource.aStart.Col();
+ SCROW nStartRow = rSource.aStart.Row();
+ SCTAB nStartTab = rSource.aStart.Tab();
+ SCCOL nEndCol = rSource.aEnd.Col();
+ SCROW nEndRow = rSource.aEnd.Row();
+ SCTAB nEndTab = rSource.aEnd.Tab();
+ SCCOL nDestCol = rDestPos.Col();
+ SCROW nDestRow = rDestPos.Row();
+ SCTAB nDestTab = rDestPos.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) || !rDoc.ValidRow(nDestRow) )
+ {
+ OSL_FAIL("invalid row in MoveBlock");
+ return false;
+ }
+
+ // adjust related scenarios too - but only when moved within one sheet
+ bool bScenariosAdded = false;
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nDestTab == nStartTab && !rDoc.IsScenario(nEndTab) )
+ while ( nEndTab+1 < nTabCount && rDoc.IsScenario(nEndTab+1) )
+ {
+ ++nEndTab;
+ bScenariosAdded = true;
+ }
+
+ SCTAB nSrcTabCount = nEndTab-nStartTab+1;
+ SCTAB nDestEndTab = nDestTab+nSrcTabCount-1;
+ SCTAB nTab;
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument(SCDOCMODE_CLIP));
+
+ ScMarkData aSourceMark(rDoc.GetSheetLimits());
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aSourceMark.SelectTable( nTab, true ); // select source
+ aSourceMark.SetMarkArea( rSource );
+
+ ScDocShellRef aDragShellRef;
+ if ( rDoc.HasOLEObjectsInArea( rSource ) )
+ {
+ aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aDragShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
+
+ ScClipParam aClipParam(ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nStartTab), bCut);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bScenariosAdded, true);
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ SCCOL nOldEndCol = nEndCol;
+ SCROW nOldEndRow = nEndRow;
+ bool bClipOver = false;
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ {
+ SCCOL nTmpEndCol = nOldEndCol;
+ SCROW nTmpEndRow = nOldEndRow;
+ if (rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab ))
+ bClipOver = true;
+ if ( nTmpEndCol > nEndCol ) nEndCol = nTmpEndCol;
+ if ( nTmpEndRow > nEndRow ) nEndRow = nTmpEndRow;
+ }
+
+ SCCOL nDestEndCol = nDestCol + ( nOldEndCol-nStartCol );
+ SCROW nDestEndRow = nDestRow + ( nOldEndRow-nStartRow );
+
+ SCCOL nUndoEndCol = nDestCol + ( nEndCol-nStartCol ); // extended in destination block
+ SCROW nUndoEndRow = nDestRow + ( nEndRow-nStartRow );
+
+ bool bIncludeFiltered = bCut;
+ if ( !bIncludeFiltered )
+ {
+ // adjust sizes to include only non-filtered rows
+
+ SCCOL nClipX;
+ SCROW nClipY;
+ pClipDoc->GetClipArea( nClipX, nClipY, false );
+ SCROW nUndoAdd = nUndoEndRow - nDestEndRow;
+ nDestEndRow = nDestRow + nClipY;
+ nUndoEndRow = nDestEndRow + nUndoAdd;
+ }
+
+ if (!rDoc.ValidCol(nUndoEndCol) || !rDoc.ValidRow(nUndoEndRow))
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ // Test for cell protection
+
+ ScEditableTester aTester;
+ for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
+ aTester.TestBlock( rDoc, nTab, nDestCol,nDestRow, nUndoEndCol,nUndoEndRow );
+ if (bCut)
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aTester.TestBlock( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
+
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ // Test for merged cells- when moving after delete
+
+ if (bClipOver && !bCut)
+ if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab, nUndoEndCol,nUndoEndRow,nDestEndTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ { // "Merge of already merged cells not possible"
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
+ return false;
+ }
+
+ // Are there borders in the cells? (for painting)
+
+ sal_uInt16 nSourceExt = 0;
+ rDocShell.UpdatePaintExt( nSourceExt, nStartCol,nStartRow,nStartTab, nEndCol,nEndRow,nEndTab );
+ sal_uInt16 nDestExt = 0;
+ rDocShell.UpdatePaintExt( nDestExt, nDestCol,nDestRow,nDestTab, nDestEndCol,nDestEndRow,nDestEndTab );
+
+ // do it
+
+ ScDocumentUniquePtr pUndoDoc;
+
+ if (bRecord)
+ {
+ bool bWholeCols = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
+ bool bWholeRows = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
+ InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab, bWholeCols, bWholeRows );
+
+ if (bCut)
+ {
+ rDoc.CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
+ nUndoFlags, false, *pUndoDoc );
+ }
+
+ if ( nDestTab != nStartTab )
+ pUndoDoc->AddUndoTab( nDestTab, nDestEndTab, bWholeCols, bWholeRows );
+ rDoc.CopyToDocument( nDestCol, nDestRow, nDestTab,
+ nDestEndCol, nDestEndRow, nDestEndTab,
+ nUndoFlags, false, *pUndoDoc );
+ rDoc.BeginDrawUndo();
+ }
+
+ bool bSourceHeight = false; // adjust heights?
+ if (bCut)
+ {
+ ScMarkData aDelMark(rDoc.GetSheetLimits()); // only for tables
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ {
+ rDoc.DeleteAreaTab( nStartCol,nStartRow, nOldEndCol,nOldEndRow, nTab, InsertDeleteFlags::ALL );
+ aDelMark.SelectTable( nTab, true );
+ }
+ rDoc.DeleteObjectsInArea( nStartCol,nStartRow, nOldEndCol,nOldEndRow, aDelMark );
+
+ // Test for merged cells
+
+ if (bClipOver)
+ if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab,
+ nUndoEndCol,nUndoEndRow,nDestEndTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ rDoc.CopyFromClip( rSource, aSourceMark, InsertDeleteFlags::ALL, nullptr, pClipDoc.get() );
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ {
+ SCCOL nTmpEndCol = nEndCol;
+ SCROW nTmpEndRow = nEndRow;
+ rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab, true );
+ }
+
+ // Report error only after restoring content
+ if (!bApi) // "Merge of already merged cells not possible"
+ rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
+
+ return false;
+ }
+
+ bSourceHeight = AdjustRowHeight( rSource, false, bApi );
+ }
+
+ ScRange aPasteDest( nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab );
+
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
+ aDestMark.SelectTable( nTab, true ); // select destination
+ aDestMark.SetMarkArea( aPasteDest );
+
+ /* Do not copy drawing objects here. While pasting, the
+ function ScDocument::UpdateReference() is called which calls
+ ScDrawLayer::MoveCells() which may move away inserted objects to wrong
+ positions (e.g. if source and destination range overlaps).*/
+
+ rDoc.CopyFromClip(
+ aPasteDest, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS,
+ pUndoDoc.get(), pClipDoc.get(), true, false, bIncludeFiltered);
+
+ // skipped rows and merged cells don't mix
+ if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
+ UnmergeCells( aPasteDest, false, nullptr );
+
+ bool bDestHeight = AdjustRowHeight(
+ ScRange( 0,nDestRow,nDestTab, rDoc.MaxCol(),nDestEndRow,nDestEndTab ),
+ false, bApi );
+
+ /* Paste drawing objects after adjusting formula references
+ and row heights. There are no cell notes or drawing objects, if the
+ clipdoc does not contain a drawing layer.*/
+ if ( pClipDoc->GetDrawLayer() )
+ rDoc.CopyFromClip( aPasteDest, aDestMark, InsertDeleteFlags::OBJECTS,
+ nullptr, pClipDoc.get(), true, false, bIncludeFiltered );
+
+ if (bRecord)
+ {
+ ScRange aUndoRange(nStartCol, nStartRow, nStartTab, nOldEndCol, nOldEndRow, nEndTab);
+ ScAddress aDestPos(nDestCol, nDestRow, nDestTab);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDragDrop>(
+ &rDocShell, aUndoRange, aDestPos, bCut, std::move(pUndoDoc), bScenariosAdded));
+ }
+
+ SCCOL nDestPaintEndCol = nDestEndCol;
+ SCROW nDestPaintEndRow = nDestEndRow;
+ for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
+ {
+ SCCOL nTmpEndCol = nDestEndCol;
+ SCROW nTmpEndRow = nDestEndRow;
+ rDoc.ExtendMerge( nDestCol, nDestRow, nTmpEndCol, nTmpEndRow, nTab, true );
+ if (nTmpEndCol > nDestPaintEndCol) nDestPaintEndCol = nTmpEndCol;
+ if (nTmpEndRow > nDestPaintEndRow) nDestPaintEndRow = nTmpEndRow;
+ }
+
+ if (bCut)
+ for (nTab=nStartTab; nTab<=nEndTab; nTab++)
+ rDoc.RefreshAutoFilter( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
+
+ if (bPaint)
+ {
+ // destination range:
+
+ SCCOL nPaintStartX = nDestCol;
+ SCROW nPaintStartY = nDestRow;
+ SCCOL nPaintEndX = nDestPaintEndCol;
+ SCROW nPaintEndY = nDestPaintEndRow;
+ PaintPartFlags nFlags = PaintPartFlags::Grid;
+
+ if ( nStartRow==0 && nEndRow==rDoc.MaxRow() ) // copy widths too?
+ {
+ nPaintEndX = rDoc.MaxCol();
+ nPaintStartY = 0;
+ nPaintEndY = rDoc.MaxRow();
+ nFlags |= PaintPartFlags::Top;
+ }
+ if ( bDestHeight || ( nStartCol == 0 && nEndCol == rDoc.MaxCol() ) )
+ {
+ nPaintEndY = rDoc.MaxRow();
+ nPaintStartX = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nFlags |= PaintPartFlags::Left;
+ }
+ if ( bScenariosAdded )
+ {
+ nPaintStartX = 0;
+ nPaintStartY = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nPaintEndY = rDoc.MaxRow();
+ }
+
+ rDocShell.PostPaint( nPaintStartX,nPaintStartY,nDestTab,
+ nPaintEndX,nPaintEndY,nDestEndTab, nFlags, nSourceExt | nDestExt );
+
+ if ( bCut )
+ {
+ // source range:
+
+ nPaintStartX = nStartCol;
+ nPaintStartY = nStartRow;
+ nPaintEndX = nEndCol;
+ nPaintEndY = nEndRow;
+ nFlags = PaintPartFlags::Grid;
+
+ if ( bSourceHeight )
+ {
+ nPaintEndY = rDoc.MaxRow();
+ nPaintStartX = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nFlags |= PaintPartFlags::Left;
+ }
+ if ( bScenariosAdded )
+ {
+ nPaintStartX = 0;
+ nPaintStartY = 0;
+ nPaintEndX = rDoc.MaxCol();
+ nPaintEndY = rDoc.MaxRow();
+ }
+
+ rDocShell.PostPaint( nPaintStartX,nPaintStartY,nStartTab,
+ nPaintEndX,nPaintEndY,nEndTab, nFlags, nSourceExt );
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ return true;
+}
+
+static uno::Reference< uno::XInterface > GetDocModuleObject( const SfxObjectShell& rDocSh, const OUString& sCodeName )
+{
+ uno::Reference< lang::XMultiServiceFactory> xSF(rDocSh.GetModel(), uno::UNO_QUERY);
+ uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess;
+ uno::Reference< uno::XInterface > xDocModuleApiObject;
+ if ( xSF.is() )
+ {
+ xVBACodeNamedObjectAccess.set( xSF->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), uno::UNO_QUERY );
+ xDocModuleApiObject.set( xVBACodeNamedObjectAccess->getByName( sCodeName ), uno::UNO_QUERY );
+ }
+ return xDocModuleApiObject;
+
+}
+
+static script::ModuleInfo lcl_InitModuleInfo( const SfxObjectShell& rDocSh, const OUString& sModule )
+{
+ script::ModuleInfo sModuleInfo;
+ sModuleInfo.ModuleType = script::ModuleType::DOCUMENT;
+ sModuleInfo.ModuleObject = GetDocModuleObject( rDocSh, sModule );
+ return sModuleInfo;
+}
+
+void VBA_InsertModule( ScDocument& rDoc, SCTAB nTab, const OUString& sSource )
+{
+ ScDocShell& rDocSh = *rDoc.GetDocumentShell();
+ uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
+ OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
+
+ uno::Reference< container::XNameContainer > xLib;
+ if( xLibContainer.is() )
+ {
+ OUString aLibName( "Standard" );
+#if HAVE_FEATURE_SCRIPTING
+ if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
+ {
+ aLibName = rDocSh.GetBasicManager()->GetName();
+ }
+#endif
+ uno::Any aLibAny = xLibContainer->getByName( aLibName );
+ aLibAny >>= xLib;
+ }
+ if( !xLib.is() )
+ return;
+
+ // if the Module with codename exists then find a new name
+ sal_Int32 nNum = 1;
+ OUString genModuleName = "Sheet1";
+ while( xLib->hasByName( genModuleName ) )
+ genModuleName = "Sheet" + OUString::number( ++nNum );
+
+ uno::Any aSourceAny;
+ OUString sTmpSource = sSource;
+ if ( sTmpSource.isEmpty() )
+ sTmpSource = "Rem Attribute VBA_ModuleType=VBADocumentModule\nOption VBASupport 1\n";
+ aSourceAny <<= sTmpSource;
+ uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
+ if ( xVBAModuleInfo.is() )
+ {
+ rDoc.SetCodeName( nTab, genModuleName );
+ script::ModuleInfo sModuleInfo = lcl_InitModuleInfo( rDocSh, genModuleName );
+ xVBAModuleInfo->insertModuleInfo( genModuleName, sModuleInfo );
+ xLib->insertByName( genModuleName, aSourceAny );
+ }
+}
+
+void VBA_DeleteModule( ScDocShell& rDocSh, const OUString& sModuleName )
+{
+ uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
+ OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
+
+ uno::Reference< container::XNameContainer > xLib;
+ if( xLibContainer.is() )
+ {
+ OUString aLibName( "Standard" );
+#if HAVE_FEATURE_SCRIPTING
+ if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
+ {
+ aLibName = rDocSh.GetBasicManager()->GetName();
+ }
+#endif
+ uno::Any aLibAny = xLibContainer->getByName( aLibName );
+ aLibAny >>= xLib;
+ }
+ if( xLib.is() )
+ {
+ uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
+ if( xLib->hasByName( sModuleName ) )
+ xLib->removeByName( sModuleName );
+ if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo(sModuleName) )
+ xVBAModuleInfo->removeModuleInfo( sModuleName );
+
+ }
+}
+
+bool ScDocFunc::InsertTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
+{
+ bool bSuccess = false;
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ // Strange loop, also basic is loaded too early ( InsertTable )
+ // is called via the xml import for sheets in described in ODF
+ bool bInsertDocModule = false;
+
+ if( !rDocShell.GetDocument().IsImportingXML() )
+ {
+ bInsertDocModule = rDoc.IsInVBAMode();
+ }
+ if ( bInsertDocModule || ( bRecord && !rDoc.IsUndoEnabled() ) )
+ bRecord = false;
+
+ if (bRecord)
+ rDoc.BeginDrawUndo(); // InsertTab generates SdrUndoNewPage
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bAppend = ( nTab >= nTabCount );
+ if ( bAppend )
+ nTab = nTabCount; // important for Undo
+
+ if (rDoc.InsertTab( nTab, rName ))
+ {
+ if (bRecord)
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( &rDocShell, nTab, bAppend, rName));
+ // Update views:
+ // Only insert vba modules if vba mode ( and not currently importing XML )
+ if( bInsertDocModule )
+ {
+ VBA_InsertModule( rDoc, nTab, OUString() );
+ }
+ rDocShell.Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab ) );
+
+ rDocShell.PostPaintExtras();
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(STR_TABINSERT_ERROR);
+
+ return bSuccess;
+}
+
+bool ScDocFunc::DeleteTable( SCTAB nTab, bool bRecord )
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bVbaEnabled = rDoc.IsInVBAMode();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( bVbaEnabled )
+ bRecord = false;
+ bool bWasLinked = rDoc.IsLinked(nTab);
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nCount = rDoc.GetTableCount();
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); // only nTab with Flags
+ pUndoDoc->AddUndoTab( 0, nCount-1 ); // all sheets for references
+
+ rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
+ OUString aOldName;
+ rDoc.GetName( nTab, aOldName );
+ pUndoDoc->RenameTab( nTab, aOldName );
+ if (bWasLinked)
+ pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
+ rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
+ rDoc.GetLinkTab(nTab),
+ rDoc.GetLinkRefreshDelay(nTab) );
+
+ if ( rDoc.IsScenario(nTab) )
+ {
+ pUndoDoc->SetScenario( nTab, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
+ pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
+ bool bActive = rDoc.IsActiveScenario( nTab );
+ pUndoDoc->SetActiveScenario( nTab, bActive );
+ }
+ pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
+ pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
+ auto pSheetEvents = rDoc.GetSheetEvents( nTab );
+ pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
+
+ // Drawing-Layer has to take care of its own undo!!!
+ rDoc.BeginDrawUndo(); // DeleteTab generates SdrUndoDelPage
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+ }
+
+ if (rDoc.DeleteTab(nTab))
+ {
+ if (bRecord)
+ {
+ vector<SCTAB> theTabs;
+ theTabs.push_back(nTab);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDeleteTab>( &rDocShell, theTabs, std::move(pUndoDoc), std::move(pUndoData) ));
+ }
+ // Update views:
+ if( bVbaEnabled )
+ {
+ OUString sCodeName;
+ if( rDoc.GetCodeName( nTab, sCodeName ) )
+ {
+ VBA_DeleteModule( rDocShell, sCodeName );
+ }
+ }
+ rDocShell.Broadcast( ScTablesHint( SC_TAB_DELETED, nTab ) );
+
+ if (bWasLinked)
+ {
+ rDocShell.UpdateLinks(); // update Link-Manager
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate(SID_LINKS);
+ }
+
+ rDocShell.PostPaintExtras();
+ aModificator.SetDocumentModified();
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+void ScDocFunc::SetTableVisible( SCTAB nTab, bool bVisible, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ if ( rDoc.IsVisible( nTab ) == bVisible )
+ return; // nothing to do - ok
+
+ if ( !rDoc.IsDocEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ if ( !bVisible && !rDoc.IsImportingXML() ) // #i57869# allow hiding in any order for loading
+ {
+ // do not disable all sheets
+
+ sal_uInt16 nVisCount = 0;
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount && nVisCount<2; i++)
+ if (rDoc.IsVisible(i))
+ ++nVisCount;
+
+ if (nVisCount <= 1)
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message?
+ return;
+ }
+ }
+
+ rDoc.SetVisible( nTab, bVisible );
+ if (bUndo)
+ {
+ std::vector<SCTAB> undoTabs { nTab };
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( &rDocShell, std::move(undoTabs), bVisible ) );
+ }
+
+ // update views
+ if (!bVisible)
+ rDocShell.Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ rDocShell.PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
+ aModificator.SetDocumentModified();
+}
+
+bool ScDocFunc::SetLayoutRTL( SCTAB nTab, bool bRTL )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ if ( rDoc.IsLayoutRTL( nTab ) == bRTL )
+ return true; // nothing to do - ok
+
+ //! protection (sheet or document?)
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ rDoc.SetLayoutRTL( nTab, bRTL, ScObjectHandling::MirrorRTLMode);
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoLayoutRTL>( &rDocShell, nTab, bRTL ) );
+ }
+
+ rDocShell.PostPaint( 0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::All );
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_TAB_RTL );
+ pBindings->Invalidate( SID_ATTR_SIZE );
+ }
+
+ return true;
+}
+
+bool ScDocFunc::RenameTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( !rDoc.IsDocEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR);
+ return false;
+ }
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ OUString sOldName;
+ rDoc.GetName(nTab, sOldName);
+ if (rDoc.RenameTab( nTab, rName ))
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRenameTab>( &rDocShell, nTab, sOldName, rName));
+ }
+ rDocShell.PostPaintExtras();
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+bool ScDocFunc::SetTabBgColor( SCTAB nTab, const Color& rColor, bool bRecord, bool bApi )
+{
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( !rDoc.IsDocEditable() || rDoc.IsTabProtected(nTab) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Check to see what this string is...
+ return false;
+ }
+
+ Color aOldTabBgColor = rDoc.GetTabBgColor(nTab);
+
+ bool bSuccess = false;
+ rDoc.SetTabBgColor(nTab, rColor);
+ if ( rDoc.GetTabBgColor(nTab) == rColor)
+ bSuccess = true;
+ if (bSuccess)
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabColor>( &rDocShell, nTab, aOldTabBgColor, rColor));
+ }
+ rDocShell.PostPaintExtras();
+ ScDocShellModificator aModificator( rDocShell );
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+bool ScDocFunc::SetTabBgColor(
+ ScUndoTabColorInfo::List& rUndoTabColorList, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if ( !rDoc.IsDocEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
+ return false;
+ }
+
+ sal_uInt16 nTab;
+ Color aNewTabBgColor;
+ bool bSuccess = true;
+ size_t nTabProtectCount = 0;
+ size_t nTabListCount = rUndoTabColorList.size();
+ for ( size_t i = 0; i < nTabListCount; ++i )
+ {
+ ScUndoTabColorInfo& rInfo = rUndoTabColorList[i];
+ nTab = rInfo.mnTabId;
+ if ( !rDoc.IsTabProtected(nTab) )
+ {
+ aNewTabBgColor = rInfo.maNewTabBgColor;
+ rInfo.maOldTabBgColor = rDoc.GetTabBgColor(nTab);
+ rDoc.SetTabBgColor(nTab, aNewTabBgColor);
+ if ( rDoc.GetTabBgColor(nTab) != aNewTabBgColor)
+ {
+ bSuccess = false;
+ break;
+ }
+ }
+ else
+ {
+ nTabProtectCount++;
+ }
+ }
+
+ if ( nTabProtectCount == nTabListCount )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
+ return false;
+ }
+
+ if (bSuccess)
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabColor>( &rDocShell, std::vector(rUndoTabColorList)));
+ }
+ rDocShell.PostPaintExtras();
+ ScDocShellModificator aModificator( rDocShell );
+ aModificator.SetDocumentModified();
+ }
+ return bSuccess;
+}
+
+//! SetWidthOrHeight - duplicated in ViewFunc !!!!!!
+//! Problems:
+//! - Optimal height of text cells is different for a printer and a screen
+//! - Optimal width needs a selection in order to take only selected cells into account
+
+static sal_uInt16 lcl_GetOptimalColWidth( ScDocShell& rDocShell, SCCOL nCol, SCTAB nTab )
+{
+ ScSizeDeviceProvider aProv(&rDocShell);
+ OutputDevice* pDev = aProv.GetDevice(); // has pixel MapMode
+ double nPPTX = aProv.GetPPTX();
+ double nPPTY = aProv.GetPPTY();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ Fraction aOne(1,1);
+ sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, pDev, nPPTX, nPPTY, aOne, aOne,
+ false/*bFormula*/ );
+
+ return nTwips;
+}
+
+bool ScDocFunc::SetWidthOrHeight(
+ bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, SCTAB nTab,
+ ScSizeMode eMode, sal_uInt16 nSizeTwips, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ if (rRanges.empty())
+ return true;
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if ( bRecord && !rDoc.IsUndoEnabled() )
+ bRecord = false;
+
+ // import into read-only document is possible
+ if ( !rDoc.IsChangeReadOnlyEnabled() && !rDocShell.IsEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message?
+ return false;
+ }
+
+ SCCOLROW nStart = rRanges[0].mnStart;
+ SCCOLROW nEnd = rRanges[0].mnEnd;
+
+ if ( eMode == SC_SIZE_OPTIMAL )
+ {
+ //! Option "Show formulas" - but where to get them from?
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::vector<sc::ColRowSpan> aUndoRanges;
+
+ if ( bRecord )
+ {
+ rDoc.BeginDrawUndo(); // Drawing Updates
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ if (bWidth)
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab, static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ else
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ rDoc.CopyToDocument( 0, static_cast<SCROW>(nStart), nTab, rDoc.MaxCol(), static_cast<SCROW>(nEnd), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+
+ aUndoRanges = rRanges;
+
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+ }
+
+ bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
+ bool bOutline = false;
+
+ for (const sc::ColRowSpan& rRange : rRanges)
+ {
+ SCCOLROW nStartNo = rRange.mnStart;
+ SCCOLROW nEndNo = rRange.mnEnd;
+
+ if ( !bWidth ) // deal with heights always in blocks
+ {
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ {
+ bool bAll = ( eMode==SC_SIZE_OPTIMAL );
+ if (!bAll)
+ {
+ // delete for all that have CRFlags::ManualSize enabled
+ // then SetOptimalHeight with bShrink = FALSE
+ for (SCROW nRow=nStartNo; nRow<=nEndNo; nRow++)
+ {
+ CRFlags nOld = rDoc.GetRowFlags(nRow,nTab);
+ SCROW nLastRow = -1;
+ bool bHidden = rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow);
+ if ( !bHidden && ( nOld & CRFlags::ManualSize ) )
+ rDoc.SetRowFlags( nRow, nTab, nOld & ~CRFlags::ManualSize );
+ }
+ }
+
+ ScSizeDeviceProvider aProv( &rDocShell );
+ Fraction aOne(1,1);
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
+ aCxt.setForceAutoSize(bAll);
+ rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, bApi);
+
+ if (bAll)
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
+
+ // Manual flag will be set already in SetOptimalHeight if bAll=true
+ // (it is on when Extra-Height, otherwise off).
+ }
+ else if ( eMode==SC_SIZE_DIRECT || eMode==SC_SIZE_ORIGINAL )
+ {
+ if (nSizeTwips)
+ {
+ rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
+ rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
+ }
+ if ( eMode != SC_SIZE_ORIGINAL )
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 );
+ }
+ else if ( eMode==SC_SIZE_SHOW )
+ {
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
+ }
+ }
+ else // Column widths
+ {
+ for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
+ {
+ if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) )
+ {
+ sal_uInt16 nThisSize = nSizeTwips;
+
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ nThisSize = nSizeTwips +
+ lcl_GetOptimalColWidth( rDocShell, nCol, nTab );
+ if ( nThisSize )
+ rDoc.SetColWidth( nCol, nTab, nThisSize );
+
+ if ( eMode != SC_SIZE_ORIGINAL )
+ rDoc.ShowCol( nCol, nTab, bShow );
+ }
+ }
+ }
+
+ // adjust outlines
+
+ if ( eMode != SC_SIZE_ORIGINAL )
+ {
+ if (bWidth)
+ bOutline = bOutline || rDoc.UpdateOutlineCol(
+ static_cast<SCCOL>(nStartNo),
+ static_cast<SCCOL>(nEndNo), nTab, bShow );
+ else
+ bOutline = bOutline || rDoc.UpdateOutlineRow(
+ static_cast<SCROW>(nStartNo),
+ static_cast<SCROW>(nEndNo), nTab, bShow );
+ }
+ }
+ rDoc.SetDrawPageSize(nTab);
+
+ if (!bOutline)
+ pUndoTab.reset();
+
+ if (bRecord)
+ {
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectOneTable( nTab );
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoWidthOrHeight>(
+ &rDocShell, aMark, nStart, nTab, nEnd, nTab, std::move(pUndoDoc),
+ std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
+ }
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (pViewSh)
+ pViewSh->OnLOKSetWidthOrHeight(nStart, bWidth);
+
+ rDocShell.PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::All);
+ aModificator.SetDocumentModified();
+
+ return false;
+}
+
+bool ScDocFunc::InsertPageBreak( bool bColumn, const ScAddress& rPos,
+ bool bRecord, bool bSetModified )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ SCTAB nTab = rPos.Tab();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+
+ SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
+ static_cast<SCCOLROW>(rPos.Row());
+ if (nPos == 0)
+ return false; // first column / row
+
+ ScBreakType nBreak = bColumn ?
+ rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab) :
+ rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
+ if (nBreak & ScBreakType::Manual)
+ return true;
+
+ if (bRecord)
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, true ) );
+
+ if (bColumn)
+ rDoc.SetColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
+ else
+ rDoc.SetRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
+
+ rDoc.InvalidatePageBreaks(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ rDoc.SetStreamValid(nTab, false);
+
+ if (bColumn)
+ {
+ rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_COLBRK );
+ pBindings->Invalidate( FID_DEL_COLBRK );
+ }
+ }
+ else
+ {
+ rDocShell.PostPaint( 0, static_cast<SCROW>(nPos)-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_ROWBRK );
+ pBindings->Invalidate( FID_DEL_ROWBRK );
+ }
+ }
+ if (pBindings)
+ pBindings->Invalidate( FID_DEL_MANUALBREAKS );
+
+ if (bSetModified)
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+bool ScDocFunc::RemovePageBreak( bool bColumn, const ScAddress& rPos,
+ bool bRecord, bool bSetModified )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ SCTAB nTab = rPos.Tab();
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+
+ SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
+ static_cast<SCCOLROW>(rPos.Row());
+
+ ScBreakType nBreak;
+ if (bColumn)
+ nBreak = rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab);
+ else
+ nBreak = rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
+ if (!(nBreak & ScBreakType::Manual))
+ // There is no manual break.
+ return false;
+
+ if (bRecord)
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, false ) );
+
+ if (bColumn)
+ rDoc.RemoveColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
+ else
+ rDoc.RemoveRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ rDoc.SetStreamValid(nTab, false);
+
+ if (bColumn)
+ {
+ rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_COLBRK );
+ pBindings->Invalidate( FID_DEL_COLBRK );
+ }
+ }
+ else
+ {
+ rDocShell.PostPaint( 0, nPos-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_INS_ROWBRK );
+ pBindings->Invalidate( FID_DEL_ROWBRK );
+ }
+ }
+ if (pBindings)
+ pBindings->Invalidate( FID_DEL_MANUALBREAKS );
+
+ if (bSetModified)
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+void ScDocFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ std::unique_ptr<ScTableProtection> p;
+ if (!rProtect.isProtected() && rDoc.IsUndoEnabled())
+ {
+ // In case of unprotecting, use a copy of passed ScTableProtection object for undo
+ p = std::make_unique<ScTableProtection>(rProtect);
+ }
+ rDoc.SetTabProtection(nTab, &rProtect);
+ if (rDoc.IsUndoEnabled())
+ {
+ if (!p)
+ {
+ // For protection case, use a copy of resulting ScTableProtection for undo
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ p = std::make_unique<ScTableProtection>(*pProtect);
+ }
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(p)));
+ // ownership of unique_ptr now transferred to ScUndoTabProtect.
+ }
+ for (SfxViewFrame* fr = SfxViewFrame::GetFirst(&rDocShell); fr;
+ fr = SfxViewFrame::GetNext(*fr, &rDocShell))
+ if (ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(fr->GetViewShell()))
+ pTabViewShell->SetTabProtectionSymbol(nTab, rProtect.isProtected());
+ rDocShell.PostPaintGridAll();
+ ScDocShellModificator aModificator(rDocShell);
+ aModificator.SetDocumentModified();
+}
+
+void ScDocFunc::ProtectDocument(const ScDocProtection& rProtect)
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ std::unique_ptr<ScDocProtection> p;
+ if (!rProtect.isProtected() && rDoc.IsUndoEnabled())
+ {
+ // In case of unprotecting, use a copy of passed ScTableProtection object for undo
+ p = std::make_unique<ScDocProtection>(rProtect);
+ }
+ rDoc.SetDocProtection(&rProtect);
+ if (rDoc.IsUndoEnabled())
+ {
+ if (!p)
+ {
+ // For protection case, use a copy of resulting ScTableProtection for undo
+ ScDocProtection* pProtect = rDoc.GetDocProtection();
+ p = std::make_unique<ScDocProtection>(*pProtect);
+ }
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDocProtect>(&rDocShell, std::move(p)));
+ // ownership of unique_ptr now transferred to ScUndoTabProtect.
+ }
+
+ rDocShell.PostPaintGridAll();
+ ScDocShellModificator aModificator(rDocShell);
+ aModificator.SetDocumentModified();
+}
+
+bool ScDocFunc::Protect( SCTAB nTab, const OUString& rPassword )
+{
+ if (nTab == TABLEID_DOC)
+ {
+ // document protection
+ ScDocProtection aProtection;
+ aProtection.setProtected(true);
+ aProtection.setPassword(rPassword);
+ ProtectDocument(aProtection);
+
+ }
+ else
+ {
+ // sheet protection
+
+ const ScTableProtection* pOldProtection = rDocShell.GetDocument().GetTabProtection(nTab);
+ ::std::unique_ptr<ScTableProtection> pNewProtection(pOldProtection ? new ScTableProtection(*pOldProtection) : new ScTableProtection());
+ pNewProtection->setProtected(true);
+ pNewProtection->setPassword(rPassword);
+ ProtectSheet(nTab, *pNewProtection);
+ }
+ return true;
+}
+
+bool ScDocFunc::Unprotect( SCTAB nTab, const OUString& rPassword, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (nTab == TABLEID_DOC)
+ {
+ // document protection
+
+ ScDocProtection* pDocProtect = rDoc.GetDocProtection();
+ if (!pDocProtect || !pDocProtect->isProtected())
+ // already unprotected (should not happen)!
+ return true;
+
+ if (!pDocProtect->verifyPassword(rPassword))
+ {
+ if (!bApi)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(SCSTR_WRONGPASSWORD)));
+ xInfoBox->run();
+ }
+ return false;
+ }
+
+ ScDocProtection aNewProtection(*pDocProtect);
+ aNewProtection.setProtected(false);
+ ProtectDocument(aNewProtection);
+
+ }
+ else
+ {
+ // sheet protection
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(nTab);
+ if (!pTabProtect || !pTabProtect->isProtected())
+ // already unprotected (should not happen)!
+ return true;
+ if (!pTabProtect->verifyPassword(rPassword))
+ {
+ if (!bApi)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(SCSTR_WRONGPASSWORD)));
+ xInfoBox->run();
+ }
+ return false;
+ }
+
+ ScTableProtection aNewProtection(*pTabProtect);
+ aNewProtection.setProtected(false);
+ ProtectSheet(nTab, aNewProtection);
+ }
+
+ return true;
+}
+
+void ScDocFunc::ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ // #i12940# ClearItems is called (from setPropertyToDefault) directly with uno object's cached
+ // MarkData (GetMarkData), so rMark must be changed to multi selection for ClearSelectionItems
+ // here.
+
+ ScMarkData aMultiMark = rMark;
+ aMultiMark.SetMarking(false); // for MarkToMulti
+ aMultiMark.MarkToMulti();
+ const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
+
+ if (bUndo)
+ {
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aMultiMark );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoClearItems>( &rDocShell, aMultiMark, std::move(pUndoDoc), pWhich ) );
+ }
+
+ rDoc.ClearSelectionItems( pWhich, aMultiMark );
+
+ rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+ aModificator.SetDocumentModified();
+
+ //! Bindings-Invalidate etc.?
+}
+
+bool ScDocFunc::ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ const ScRange& aMarkRange = rMark.GetMultiMarkArea();
+
+ if (bUndo)
+ {
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoIndent>( &rDocShell, rMark, std::move(pUndoDoc), bIncrement ) );
+ }
+
+ rDoc.ChangeSelectionIndent( bIncrement, rMark );
+
+ rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_ALIGNLEFT ); // ChangeIndent aligns left
+ pBindings->Invalidate( SID_ALIGNRIGHT );
+ pBindings->Invalidate( SID_ALIGNBLOCK );
+ pBindings->Invalidate( SID_ALIGNCENTERHOR );
+ pBindings->Invalidate( SID_ATTR_LRSPACE );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ pBindings->Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ // pseudo slots for Format menu
+ pBindings->Invalidate( SID_ALIGN_ANY_HDEFAULT );
+ pBindings->Invalidate( SID_ALIGN_ANY_LEFT );
+ pBindings->Invalidate( SID_ALIGN_ANY_HCENTER );
+ pBindings->Invalidate( SID_ALIGN_ANY_RIGHT );
+ pBindings->Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ }
+
+ return true;
+}
+
+bool ScDocFunc::AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark,
+ sal_uInt16 nFormatNo, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat();
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( nFormatNo < pAutoFormat->size() && aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ bool bSize = pAutoFormat->findByIndex(nFormatNo)->GetIncludeWidthHeight();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab, bSize, bSize );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab, bSize, bSize );
+ }
+
+ ScRange aCopyRange = rRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aStart.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc, &aMark );
+ if (bSize)
+ {
+ rDoc.CopyToDocument( nStartCol,0,0, nEndCol,rDoc.MaxRow(),nTabCount-1,
+ InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
+ rDoc.CopyToDocument( 0,nStartRow,0, rDoc.MaxCol(),nEndRow,nTabCount-1,
+ InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
+ }
+ rDoc.BeginDrawUndo();
+ }
+
+ rDoc.AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo, aMark );
+
+ if (bSize)
+ {
+ std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nStartCol,nEndCol));
+ std::vector<sc::ColRowSpan> aRows(1, sc::ColRowSpan(nStartRow,nEndRow));
+
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ SetWidthOrHeight(true, aCols, rTab, SC_SIZE_VISOPT, STD_EXTRA_WIDTH, false, true);
+ SetWidthOrHeight(false, aRows, rTab, SC_SIZE_VISOPT, 0, false, false);
+ rDocShell.PostPaint( 0,0,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top );
+ }
+ }
+ else
+ {
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ bool bAdj = AdjustRowHeight( ScRange(nStartCol, nStartRow, rTab,
+ nEndCol, nEndRow, rTab), false, bApi );
+ if (bAdj)
+ rDocShell.PostPaint( 0,nStartRow,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ else
+ rDocShell.PostPaint( nStartCol, nStartRow, rTab,
+ nEndCol, nEndRow, rTab, PaintPartFlags::Grid );
+ }
+ }
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFormat>( &rDocShell, rRange, std::move(pUndoDoc), aMark, bSize, nFormatNo ) );
+ }
+
+ aModificator.SetDocumentModified();
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return false;
+}
+
+bool ScDocFunc::EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark,
+ const ScTokenArray* pTokenArray, const OUString& rString, bool bApi, bool bEnglish,
+ const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar )
+{
+ if (ScViewData::SelectionFillDOOM( rRange ))
+ return false;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocumentUniquePtr pUndoDoc;
+
+ const bool bUndo(rDoc.IsUndoEnabled());
+ if (bUndo)
+ {
+ //! take selected sheets into account also when undoing
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
+ }
+
+ // use TokenArray if given, string (and flags) otherwise
+ if ( pTokenArray )
+ {
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, OUString(), pTokenArray, eGrammar);
+ }
+ else if ( rDoc.IsImportingXML() )
+ {
+ ScTokenArray aCode(rDoc);
+ aCode.AssignXMLString( rString,
+ ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString()));
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, OUString(), &aCode, eGrammar);
+ rDoc.IncXMLImportedFormulaCount( rString.getLength() );
+ }
+ else if (bEnglish)
+ {
+ ScCompiler aComp( rDoc, rRange.aStart, eGrammar);
+ std::unique_ptr<ScTokenArray> pCode = aComp.CompileString( rString );
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, OUString(), pCode.get(), eGrammar);
+ }
+ else
+ rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
+ aMark, rString, nullptr, eGrammar);
+
+ if (bUndo)
+ {
+ //! take selected sheets into account also when undoing
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterMatrix>( &rDocShell, rRange, std::move(pUndoDoc), rString ) );
+ }
+
+ // Err522 painting of DDE-Formulas will be intercepted during interpreting
+ rDocShell.PostPaint( nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab, PaintPartFlags::Grid );
+ aModificator.SetDocumentModified();
+
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+bool ScDocFunc::TabOp( const ScRange& rRange, const ScMarkData* pTabMark,
+ const ScTabOpParam& rParam, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+ rDoc.SetDirty( rRange, false );
+ if ( bRecord )
+ {
+ //! take selected sheets into account also when undoing
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoTabOp>( &rDocShell,
+ nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab, std::move(pUndoDoc),
+ rParam.aRefFormulaCell,
+ rParam.aRefFormulaEnd,
+ rParam.aRefRowCell,
+ rParam.aRefColCell,
+ rParam.meMode) );
+ }
+ rDoc.InsertTableOp(rParam, nStartCol, nStartRow, nEndCol, nEndRow, aMark);
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+static ScDirection DirFromFillDir( FillDir eDir )
+{
+ if (eDir==FILL_TO_BOTTOM)
+ return DIR_BOTTOM;
+ else if (eDir==FILL_TO_RIGHT)
+ return DIR_RIGHT;
+ else if (eDir==FILL_TO_TOP)
+ return DIR_TOP;
+ else // if (eDir==FILL_TO_LEFT)
+ return DIR_LEFT;
+}
+
+namespace {
+
+/**
+ * Expand the fill range as necessary, to allow copying of adjacent cell(s)
+ * even when those cells are not in the original range.
+ */
+void adjustFillRangeForAdjacentCopy(const ScDocument &rDoc, ScRange& rRange, FillDir eDir)
+{
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ {
+ if (rRange.aStart.Row() == 0)
+ return;
+
+ if (rRange.aStart.Row() != rRange.aEnd.Row())
+ return;
+
+ // Include the above row.
+ ScAddress& s = rRange.aStart;
+ s.SetRow(s.Row()-1);
+ }
+ break;
+ case FILL_TO_TOP:
+ {
+ if (rRange.aStart.Row() == rDoc.MaxRow())
+ return;
+
+ if (rRange.aStart.Row() != rRange.aEnd.Row())
+ return;
+
+ // Include the row below.
+ ScAddress& e = rRange.aEnd;
+ e.SetRow(e.Row()+1);
+ }
+ break;
+ case FILL_TO_LEFT:
+ {
+ if (rRange.aStart.Col() == rDoc.MaxCol())
+ return;
+
+ if (rRange.aStart.Col() != rRange.aEnd.Col())
+ return;
+
+ // Include the column to the right.
+ ScAddress& e = rRange.aEnd;
+ e.SetCol(e.Col()+1);
+ }
+ break;
+ case FILL_TO_RIGHT:
+ {
+ if (rRange.aStart.Col() == 0)
+ return;
+
+ if (rRange.aStart.Col() != rRange.aEnd.Col())
+ return;
+
+ // Include the column to the left.
+ ScAddress& s = rRange.aStart;
+ s.SetCol(s.Col()-1);
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+}
+
+bool ScDocFunc::FillSimple( const ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ bool bSuccess = false;
+ ScRange aRange = rRange;
+ adjustFillRangeForAdjacentCopy(rDoc, aRange, eDir);
+
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCTAB nStartTab = aRange.aStart.Tab();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ SCTAB nEndTab = aRange.aEnd.Tab();
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScRange aSourceArea = aRange;
+ ScRange aDestArea = aRange;
+
+ SCCOLROW nCount = 0;
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
+ aSourceArea.aEnd.SetRow( aSourceArea.aStart.Row() );
+ break;
+ case FILL_TO_RIGHT:
+ nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
+ aSourceArea.aEnd.SetCol( aSourceArea.aStart.Col() );
+ break;
+ case FILL_TO_TOP:
+ nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
+ aSourceArea.aStart.SetRow( aSourceArea.aEnd.Row() );
+ break;
+ case FILL_TO_LEFT:
+ nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
+ aSourceArea.aStart.SetCol( aSourceArea.aEnd.Col() );
+ break;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nDestStartTab = aDestArea.aStart.Tab();
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nDestStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ ScRange aCopyRange = aDestArea;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
+ }
+
+ sal_uLong nProgCount;
+ if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
+ nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
+ else
+ nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
+ aMark, nCount, eDir, FILL_SIMPLE );
+ AdjustRowHeight(aRange, true, bApi);
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
+ eDir, FILL_SIMPLE, FILL_DAY, MAXDOUBLE, 1.0, 1e307) );
+ }
+
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+bool ScDocFunc::FillSeries( const ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
+ double fStart, double fStep, double fMax,
+ bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
+ if ( aTester.IsEditable() )
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScRange aSourceArea = rRange;
+ ScRange aDestArea = rRange;
+
+ SCSIZE nCount = rDoc.GetEmptyLinesInBlock(
+ aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), aSourceArea.aStart.Tab(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), aSourceArea.aEnd.Tab(),
+ DirFromFillDir(eDir) );
+
+ // keep at least one row/column as source range
+ SCSIZE nTotLines = ( eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP ) ?
+ static_cast<SCSIZE>( aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1 ) :
+ static_cast<SCSIZE>( aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1 );
+ if ( nCount >= nTotLines )
+ nCount = nTotLines - 1;
+
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ aSourceArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() - nCount ) );
+ break;
+ case FILL_TO_RIGHT:
+ aSourceArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() - nCount ) );
+ break;
+ case FILL_TO_TOP:
+ aSourceArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() + nCount ) );
+ break;
+ case FILL_TO_LEFT:
+ aSourceArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() + nCount ) );
+ break;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nDestStartTab = aDestArea.aStart.Tab();
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nDestStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ rDoc.CopyToDocument(
+ aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
+ aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
+ InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
+ }
+
+ if (aDestArea.aStart.Col() <= aDestArea.aEnd.Col() &&
+ aDestArea.aStart.Row() <= aDestArea.aEnd.Row())
+ {
+ if ( fStart != MAXDOUBLE )
+ {
+ SCCOL nValX = (eDir == FILL_TO_LEFT) ? aDestArea.aEnd.Col() : aDestArea.aStart.Col();
+ SCROW nValY = (eDir == FILL_TO_TOP ) ? aDestArea.aEnd.Row() : aDestArea.aStart.Row();
+ SCTAB nTab = aDestArea.aStart.Tab();
+ rDoc.SetValue( nValX, nValY, nTab, fStart );
+ }
+
+ sal_uLong nProgCount;
+ if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
+ nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
+ else
+ nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
+ aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
+ AdjustRowHeight(rRange, true, bApi);
+
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+ }
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
+ eDir, eCmd, eDateCmd, fStart, fStep, fMax) );
+ }
+
+ bSuccess = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+
+ return bSuccess;
+}
+
+bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, sal_uLong nCount, bool bApi )
+{
+ return FillAuto( rRange, pTabMark, eDir, FILL_AUTO, FILL_DAY, nCount, 1.0/*fStep*/, MAXDOUBLE/*fMax*/, true/*bRecord*/, bApi );
+}
+
+bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark, FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd, sal_uLong nCount, double fStep, double fMax, bool bRecord, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ if (pTabMark)
+ aMark = *pTabMark;
+ else
+ {
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ aMark.SelectTable( nTab, true );
+ }
+
+ ScRange aSourceArea = rRange;
+ ScRange aDestArea = rRange;
+
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ aDestArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() + nCount ) );
+ break;
+ case FILL_TO_TOP:
+ if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Row() ))
+ {
+ OSL_FAIL("FillAuto: Row < 0");
+ nCount = aSourceArea.aStart.Row();
+ }
+ aDestArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() - nCount ) );
+ break;
+ case FILL_TO_RIGHT:
+ aDestArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() + nCount ) );
+ break;
+ case FILL_TO_LEFT:
+ if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Col() ))
+ {
+ OSL_FAIL("FillAuto: Col < 0");
+ nCount = aSourceArea.aStart.Col();
+ }
+ aDestArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() - nCount ) );
+ break;
+ default:
+ OSL_FAIL("Wrong direction with FillAuto");
+ break;
+ }
+
+ // Test for cell protection
+ //! Source range can be protected !!!
+ //! but can't contain matrix fragments !!!
+
+ ScEditableTester aTester( rDoc, aDestArea );
+ if ( !aTester.IsEditable() )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ if ( rDoc.HasSelectedBlockMatrixFragment( nStartCol, nStartRow,
+ nEndCol, nEndRow, aMark ) )
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MATRIXFRAGMENTERR);
+ return false;
+ }
+
+ // FID_FILL_... slots should already had been disabled, check here for API
+ // calls, no message.
+ if (ScViewData::SelectionFillDOOM( aDestArea))
+ return false;
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nDestStartTab = aDestArea.aStart.Tab();
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab != nDestStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+
+ // do not clone note captions in undo document
+ rDoc.CopyToDocument(
+ aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
+ aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
+ InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
+ }
+
+ sal_uLong nProgCount;
+ if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
+ nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
+ else
+ nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
+ aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
+ aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
+
+ AdjustRowHeight(aDestArea, true, bApi);
+
+ if ( bRecord ) // only now is Draw-Undo available
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
+ eDir, eCmd, eDateCmd, MAXDOUBLE, fStep, fMax) );
+ }
+
+ rDocShell.PostPaintGridAll();
+ aModificator.SetDocumentModified();
+
+ rRange = aDestArea; // return destination range (for marking)
+ return true;
+}
+
+bool ScDocFunc::MergeCells( const ScCellMergeOption& rOption, bool bContents, bool bRecord, bool bApi, bool bEmptyMergedCells /*=false*/ )
+{
+ using ::std::set;
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ SCCOL nStartCol = rOption.mnStartCol;
+ SCROW nStartRow = rOption.mnStartRow;
+ SCCOL nEndCol = rOption.mnEndCol;
+ SCROW nEndRow = rOption.mnEndRow;
+ if ((nStartCol == nEndCol && nStartRow == nEndRow) || rOption.maTabs.empty())
+ {
+ // Nothing to do. Bail out quickly
+ return true;
+ }
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCTAB nTab1 = *rOption.maTabs.begin(), nTab2 = *rOption.maTabs.rbegin();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ for (const auto& rTab : rOption.maTabs)
+ {
+ ScEditableTester aTester( rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ if (!aTester.IsEditable())
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ if ( rDoc.HasAttrib( nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ // "Merge of already merged cells not possible"
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_MERGECELLS_0);
+ return false;
+ }
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ bool bNeedContentsUndo = false;
+ for (const SCTAB nTab : rOption.maTabs)
+ {
+ bool bIsBlockEmpty = ( nStartRow == nEndRow )
+ ? rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab )
+ : rDoc.IsEmptyData( nStartCol,nStartRow+1, nStartCol,nEndRow, nTab ) &&
+ rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab );
+ bool bNeedContents = bContents && !bIsBlockEmpty;
+ bool bNeedEmpty = bEmptyMergedCells && !bIsBlockEmpty && !bNeedContents; // if DoMergeContents then cells are emptied
+
+ if (bRecord)
+ {
+ // test if the range contains other notes which also implies that we need an undo document
+ bool bHasNotes = rDoc.HasNote(nTab, nStartCol, nStartRow, nEndCol, nEndRow);
+ if (!pUndoDoc)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo(rDoc, nTab1, nTab2);
+ }
+ // note captions are collected by drawing undo
+ rDoc.CopyToDocument( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab,
+ InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
+ if( bHasNotes )
+ rDoc.BeginDrawUndo();
+ }
+
+ if (bNeedContents)
+ rDoc.DoMergeContents( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+ else if ( bNeedEmpty )
+ rDoc.DoEmptyBlock( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+ rDoc.DoMerge( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+
+ if (rOption.mbCenter)
+ {
+ rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
+ rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxVerJustifyItem( SvxCellVerJustify::Center, ATTR_VER_JUSTIFY ) );
+ }
+
+ if ( !AdjustRowHeight( ScRange( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab ), true, bApi ) )
+ rDocShell.PostPaint( nStartCol, nStartRow, nTab,
+ nEndCol, nEndRow, nTab, PaintPartFlags::Grid );
+ if (bNeedContents || rOption.mbCenter)
+ {
+ ScRange aRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab);
+ rDoc.SetDirty(aRange, true);
+ }
+
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles );
+ if(bDone)
+ DetectiveMarkInvalid(nTab);
+
+ bNeedContentsUndo |= bNeedContents;
+ }
+
+ if (pUndoDoc)
+ {
+ std::unique_ptr<SdrUndoGroup> pDrawUndo = rDoc.GetDrawLayer() ? rDoc.GetDrawLayer()->GetCalcUndo() : nullptr;
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMerge>(&rDocShell, rOption, bNeedContentsUndo, std::move(pUndoDoc), std::move(pDrawUndo)) );
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_MERGE_ON );
+ pBindings->Invalidate( FID_MERGE_OFF );
+ pBindings->Invalidate( FID_MERGE_TOGGLE );
+ }
+
+ return true;
+}
+
+bool ScDocFunc::UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
+{
+ ScCellMergeOption aOption(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+ SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i = nTab1; i <= nTab2; ++i)
+ aOption.maTabs.insert(i);
+
+ return UnmergeCells(aOption, bRecord, pUndoRemoveMerge);
+}
+
+bool ScDocFunc::UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
+{
+ using ::std::set;
+
+ if (rOption.maTabs.empty())
+ // Nothing to unmerge.
+ return true;
+
+ ScDocShellModificator aModificator( rDocShell );
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDocument* pUndoDoc = (pUndoRemoveMerge ? pUndoRemoveMerge->GetUndoDoc() : nullptr);
+ assert( pUndoDoc || !pUndoRemoveMerge );
+ for (const SCTAB nTab : rOption.maTabs)
+ {
+ ScRange aRange = rOption.getSingleRange(nTab);
+ if ( !rDoc.HasAttrib(aRange, HasAttrFlags::Merged) )
+ continue;
+
+ ScRange aExtended = aRange;
+ rDoc.ExtendMerge(aExtended);
+ ScRange aRefresh = aExtended;
+ rDoc.ExtendOverlapped(aRefresh);
+
+ if (bRecord)
+ {
+ if (!pUndoDoc)
+ {
+ pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
+ pUndoDoc->InitUndo(rDoc, *rOption.maTabs.begin(), *rOption.maTabs.rbegin());
+ }
+ rDoc.CopyToDocument(aExtended, InsertDeleteFlags::ATTRIB, false, *pUndoDoc);
+ }
+
+ const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetDefaultItem( ATTR_MERGE );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( rDefAttr );
+ rDoc.ApplyPatternAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), nTab,
+ aPattern );
+
+ rDoc.RemoveFlagsTab( aExtended.aStart.Col(), aExtended.aStart.Row(),
+ aExtended.aEnd.Col(), aExtended.aEnd.Row(), nTab,
+ ScMF::Hor | ScMF::Ver );
+
+ rDoc.ExtendMerge( aRefresh, true );
+
+ if ( !AdjustRowHeight( aExtended, true, true ) )
+ rDocShell.PostPaint( aExtended, PaintPartFlags::Grid );
+
+ bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles );
+ if(bDone)
+ DetectiveMarkInvalid(nTab);
+ }
+
+ if (bRecord)
+ {
+ if (pUndoRemoveMerge)
+ {
+ // If pUndoRemoveMerge was passed, the caller is responsible for
+ // adding it to Undo. Just add the current option.
+ pUndoRemoveMerge->AddCellMergeOption( rOption);
+ }
+ else
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveMerge>( &rDocShell, rOption, ScDocumentUniquePtr(pUndoDoc) ) );
+ }
+ }
+ aModificator.SetDocumentModified();
+
+ return true;
+}
+
+void ScDocFunc::ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab )
+{
+ SetNewRangeNames( std::unique_ptr<ScRangeName>(new ScRangeName(rNewRanges)), true, nTab );
+}
+
+void ScDocFunc::SetNewRangeNames( std::unique_ptr<ScRangeName> pNewRanges, bool bModifyDoc, SCTAB nTab ) // takes ownership of pNewRanges
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ OSL_ENSURE( pNewRanges, "pNewRanges is 0" );
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ ScRangeName* pOld;
+ if (nTab >=0)
+ {
+ pOld = rDoc.GetRangeName(nTab);
+ }
+ else
+ {
+ pOld = rDoc.GetRangeName();
+ }
+ std::unique_ptr<ScRangeName> pUndoRanges(new ScRangeName(*pOld));
+ std::unique_ptr<ScRangeName> pRedoRanges(new ScRangeName(*pNewRanges));
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRangeNames>( &rDocShell, std::move(pUndoRanges), std::move(pRedoRanges), nTab ) );
+ }
+
+ // #i55926# While loading XML, formula cells only have a single string token,
+ // so CompileNameFormula would never find any name (index) tokens, and would
+ // unnecessarily loop through all cells.
+ bool bCompile = ( !rDoc.IsImportingXML() && rDoc.GetNamedRangesLockCount() == 0 );
+
+ if ( bCompile )
+ rDoc.PreprocessRangeNameUpdate();
+ if (nTab >= 0)
+ rDoc.SetRangeName( nTab, std::move(pNewRanges) ); // takes ownership
+ else
+ rDoc.SetRangeName( std::move(pNewRanges) ); // takes ownership
+ if ( bCompile )
+ rDoc.CompileHybridFormula();
+
+ if (bModifyDoc)
+ {
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint(SfxHintId::ScAreasChanged) );
+ }
+}
+
+void ScDocFunc::ModifyAllRangeNames(const std::map<OUString, ScRangeName>& rRangeMap)
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (rDoc.IsUndoEnabled())
+ {
+ std::map<OUString, ScRangeName*> aOldRangeMap;
+ rDoc.GetRangeNameMap(aOldRangeMap);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAllRangeNames>(&rDocShell, aOldRangeMap, rRangeMap));
+ }
+
+ rDoc.PreprocessAllRangeNamesUpdate(rRangeMap);
+ rDoc.SetAllRangeNames(rRangeMap);
+ rDoc.CompileHybridFormula();
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+void ScDocFunc::CreateOneName( ScRangeName& rList,
+ SCCOL nPosX, SCROW nPosY, SCTAB nTab,
+ SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ bool& rCancel, bool bApi )
+{
+ if (rCancel)
+ return;
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (rDoc.HasValueData( nPosX, nPosY, nTab ))
+ return;
+
+ OUString aName = rDoc.GetString(nPosX, nPosY, nTab);
+ ScRangeData::MakeValidName(rDoc, aName);
+ if (aName.isEmpty())
+ return;
+
+ OUString aContent( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ).Format(
+ rDoc, ScRefFlags::RANGE_ABS_3D, ScAddress::Details( rDoc.GetAddressConvention(), nPosY, nPosX)));
+
+ bool bInsert = false;
+ ScRangeData* pOld = rList.findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (pOld)
+ {
+ OUString aOldStr = pOld->GetSymbol();
+ if (aOldStr != aContent)
+ {
+ if (bApi)
+ bInsert = true; // don't check via API
+ else
+ {
+ OUString aTemplate = ScResId( STR_CREATENAME_REPLACE );
+ OUString aMessage = o3tl::getToken(aTemplate, 0, '#' ) + aName + o3tl::getToken(aTemplate, 1, '#' );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMessage));
+ xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
+ xQueryBox->set_default_response(RET_YES);
+
+ short nResult = xQueryBox->run();
+ if ( nResult == RET_YES )
+ {
+ rList.erase(*pOld);
+ bInsert = true;
+ }
+ else if ( nResult == RET_CANCEL )
+ rCancel = true;
+ }
+ }
+ }
+ else
+ bInsert = true;
+
+ if (bInsert)
+ {
+ ScRangeData* pData = new ScRangeData( rDoc, aName, aContent,
+ ScAddress( nPosX, nPosY, nTab));
+ if (!rList.insert(pData))
+ {
+ OSL_FAIL("nanu?");
+ }
+ }
+}
+
+bool ScDocFunc::CreateNames( const ScRange& rRange, CreateNameFlags nFlags, bool bApi, SCTAB aTab )
+{
+ if (nFlags == CreateNameFlags::NONE)
+ return false; // was nothing
+
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bDone = false;
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+ OSL_ENSURE(rRange.aEnd.Tab() == nTab, "CreateNames: multiple tables not possible");
+
+ bool bValid = true;
+ if ( nFlags & ( CreateNameFlags::Top | CreateNameFlags::Bottom ) )
+ if ( nStartRow == nEndRow )
+ bValid = false;
+ if ( nFlags & ( CreateNameFlags::Left | CreateNameFlags::Right ) )
+ if ( nStartCol == nEndCol )
+ bValid = false;
+
+ if (bValid)
+ {
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScRangeName* pNames;
+ if (aTab >=0)
+ pNames = rDoc.GetRangeName(nTab);
+ else
+ pNames = rDoc.GetRangeName();
+
+ if (!pNames)
+ return false; // shouldn't happen
+ ScRangeName aNewRanges( *pNames );
+
+ bool bTop ( nFlags & CreateNameFlags::Top );
+ bool bLeft ( nFlags & CreateNameFlags::Left );
+ bool bBottom( nFlags & CreateNameFlags::Bottom );
+ bool bRight ( nFlags & CreateNameFlags::Right );
+
+ SCCOL nContX1 = nStartCol;
+ SCROW nContY1 = nStartRow;
+ SCCOL nContX2 = nEndCol;
+ SCROW nContY2 = nEndRow;
+
+ if ( bTop )
+ ++nContY1;
+ if ( bLeft )
+ ++nContX1;
+ if ( bBottom )
+ --nContY2;
+ if ( bRight )
+ --nContX2;
+
+ bool bCancel = false;
+ SCCOL i;
+ SCROW j;
+
+ if ( bTop )
+ for (i=nContX1; i<=nContX2; i++)
+ CreateOneName( aNewRanges, i,nStartRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
+ if ( bLeft )
+ for (j=nContY1; j<=nContY2; j++)
+ CreateOneName( aNewRanges, nStartCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
+ if ( bBottom )
+ for (i=nContX1; i<=nContX2; i++)
+ CreateOneName( aNewRanges, i,nEndRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
+ if ( bRight )
+ for (j=nContY1; j<=nContY2; j++)
+ CreateOneName( aNewRanges, nEndCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
+
+ if ( bTop && bLeft )
+ CreateOneName( aNewRanges, nStartCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+ if ( bTop && bRight )
+ CreateOneName( aNewRanges, nEndCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+ if ( bBottom && bLeft )
+ CreateOneName( aNewRanges, nStartCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+ if ( bBottom && bRight )
+ CreateOneName( aNewRanges, nEndCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
+
+ ModifyRangeNames( aNewRanges, aTab );
+ bDone = true;
+
+ }
+
+ return bDone;
+}
+
+bool ScDocFunc::InsertNameList( const ScAddress& rStartPos, bool bApi )
+{
+ ScDocShellModificator aModificator( rDocShell );
+
+ bool bDone = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+ const bool bRecord = rDoc.IsUndoEnabled();
+ SCTAB nTab = rStartPos.Tab();
+
+ //local names have higher priority than global names
+ ScRangeName* pLocalList = rDoc.GetRangeName(nTab);
+ sal_uInt16 nValidCount = 0;
+ for (const auto& rEntry : *pLocalList)
+ {
+ const ScRangeData& r = *rEntry.second;
+ if (!r.HasType(ScRangeData::Type::Database))
+ ++nValidCount;
+ }
+ ScRangeName* pList = rDoc.GetRangeName();
+ for (const auto& rEntry : *pList)
+ {
+ const ScRangeData& r = *rEntry.second;
+ if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(r.GetUpperName()))
+ ++nValidCount;
+ }
+
+ if (nValidCount)
+ {
+ SCCOL nStartCol = rStartPos.Col();
+ SCROW nStartRow = rStartPos.Row();
+ SCCOL nEndCol = nStartCol + 1;
+ SCROW nEndRow = nStartRow + static_cast<SCROW>(nValidCount) - 1;
+
+ ScEditableTester aTester( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
+ if (aTester.IsEditable())
+ {
+ ScDocumentUniquePtr pUndoDoc;
+
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ rDoc.BeginDrawUndo(); // because of adjusting heights
+ }
+
+ std::unique_ptr<ScRangeData*[]> ppSortArray(new ScRangeData* [ nValidCount ]);
+ sal_uInt16 j = 0;
+ for (const auto& rEntry : *pLocalList)
+ {
+ ScRangeData& r = *rEntry.second;
+ if (!r.HasType(ScRangeData::Type::Database))
+ ppSortArray[j++] = &r;
+ }
+ for (const auto& [rName, rxData] : *pList)
+ {
+ ScRangeData& r = *rxData;
+ if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(rName))
+ ppSortArray[j++] = &r;
+ }
+ qsort( static_cast<void*>(ppSortArray.get()), nValidCount, sizeof(ScRangeData*),
+ &ScRangeData_QsortNameCompare );
+ OUString aName;
+ OUStringBuffer aContent;
+ OUString aFormula;
+ SCROW nOutRow = nStartRow;
+ for (j=0; j<nValidCount; j++)
+ {
+ ScRangeData* pData = ppSortArray[j];
+ pData->GetName(aName);
+ // adjust relative references to the left column in Excel-compliant way:
+ pData->UpdateSymbol(aContent, ScAddress( nStartCol, nOutRow, nTab ));
+ aFormula = "=" + aContent;
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(ScAddress(nStartCol,nOutRow,nTab), aName, &aParam);
+ rDoc.SetString(ScAddress(nEndCol,nOutRow,nTab), aFormula, &aParam);
+ ++nOutRow;
+ }
+
+ ppSortArray.reset();
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
+ InsertDeleteFlags::ALL, false, *pRedoDoc);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoListNames>( &rDocShell,
+ ScRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab ),
+ std::move(pUndoDoc), std::move(pRedoDoc) ) );
+ }
+
+ if (!AdjustRowHeight(ScRange(0,nStartRow,nTab,rDoc.MaxCol(),nEndRow,nTab), true, true))
+ rDocShell.PostPaint( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, PaintPartFlags::Grid );
+
+ aModificator.SetDocumentModified();
+ bDone = true;
+ }
+ else if (!bApi)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ }
+ return bDone;
+}
+
+void ScDocFunc::ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCCOL nStartCol = rOldRange.aStart.Col();
+ SCROW nStartRow = rOldRange.aStart.Row();
+ SCTAB nTab = rOldRange.aStart.Tab();
+
+ OUString aFormula = rDoc.GetFormula( nStartCol, nStartRow, nTab );
+ if ( !(aFormula.startsWith("{") && aFormula.endsWith("}")) )
+ return;
+
+ OUString aUndo = ScResId( STR_UNDO_RESIZEMATRIX );
+ bool bUndo(rDoc.IsUndoEnabled());
+ if (bUndo)
+ {
+ ViewShellId nViewShellId(1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+
+ aFormula = aFormula.copy(1, aFormula.getLength()-2);
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( rOldRange );
+ aMark.SelectTable( nTab, true );
+ ScRange aNewRange( rOldRange.aStart, rNewEnd );
+
+ if ( DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, false/*bApi*/ ) )
+ {
+ // GRAM_API for API compatibility.
+ if (!EnterMatrix( aNewRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), formula::FormulaGrammar::GRAM_API ))
+ {
+ // try to restore the previous state
+ EnterMatrix( rOldRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), formula::FormulaGrammar::GRAM_API );
+ }
+ }
+
+ if (bUndo)
+ rDocShell.GetUndoManager()->LeaveListAction();
+}
+
+void ScDocFunc::InsertAreaLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, const OUString& rSource,
+ const ScRange& rDestRange, sal_Int32 nRefreshDelaySeconds,
+ bool bFitBlock, bool bApi )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ // #i52120# if other area links exist at the same start position,
+ // remove them first (file format specifies only one link definition
+ // for a cell)
+
+ sal_uInt16 nLinkCount = pLinkManager->GetLinks().size();
+ sal_uInt16 nRemoved = 0;
+ sal_uInt16 nLinkPos = 0;
+ while (nLinkPos<nLinkCount)
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[nLinkPos].get();
+ ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase);
+ if (pLink && pLink->GetDestArea().aStart == rDestRange.aStart)
+ {
+ if ( bUndo )
+ {
+ if ( !nRemoved )
+ {
+ // group all remove and the insert action
+ OUString aUndo = ScResId( STR_UNDO_INSERTAREALINK );
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+ }
+
+ ScAreaLink* pOldArea = static_cast<ScAreaLink*>(pBase);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveAreaLink>( &rDocShell,
+ pOldArea->GetFile(), pOldArea->GetFilter(), pOldArea->GetOptions(),
+ pOldArea->GetSource(), pOldArea->GetDestArea(), pOldArea->GetRefreshDelaySeconds() ) );
+ }
+ pLinkManager->Remove( pBase );
+ nLinkCount = pLinkManager->GetLinks().size();
+ ++nRemoved;
+ }
+ else
+ ++nLinkPos;
+ }
+
+ OUString aFilterName = rFilter;
+ OUString aNewOptions = rOptions;
+ if (aFilterName.isEmpty())
+ ScDocumentLoader::GetFilterName( rFile, aFilterName, aNewOptions, true, !bApi );
+
+ // remove application prefix from filter name here, so the filter options
+ // aren't reset when the filter name is changed in ScAreaLink::DataChanged
+ ScDocumentLoader::RemoveAppPrefix( aFilterName );
+
+ ScAreaLink* pLink = new ScAreaLink( &rDocShell, rFile, aFilterName,
+ aNewOptions, rSource, rDestRange, nRefreshDelaySeconds );
+ OUString aTmp = aFilterName;
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, rFile, &aTmp, &rSource );
+
+ // Undo for an empty link
+
+ if (bUndo)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertAreaLink>( &rDocShell,
+ rFile, aFilterName, aNewOptions,
+ rSource, rDestRange, nRefreshDelaySeconds ) );
+ if ( nRemoved )
+ rDocShell.GetUndoManager()->LeaveListAction(); // undo for link update is still separate
+ }
+
+ // Update has its own undo
+ if (rDoc.IsExecuteLinkEnabled())
+ {
+ pLink->SetDoInsert(bFitBlock); // if applicable, don't insert anything on first update
+ pLink->Update(); // no SetInCreate -> carry out update
+ }
+ pLink->SetDoInsert(true); // Default = true
+
+ SfxBindings* pBindings = rDocShell.GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+}
+
+void ScDocFunc::ReplaceConditionalFormat( sal_uLong nOldFormat, std::unique_ptr<ScConditionalFormat> pFormat, SCTAB nTab, const ScRangeList& rRanges )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if(rDoc.IsTabProtected(nTab))
+ return;
+
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScDocumentUniquePtr pUndoDoc;
+ ScRange aCombinedRange = rRanges.Combine();
+ ScRange aCompleteRange;
+ if(bUndo)
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if(pFormat)
+ {
+ aCompleteRange = aCombinedRange;
+ }
+ if(nOldFormat)
+ {
+ ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
+ if(pOldFormat)
+ aCompleteRange.ExtendTo(pOldFormat->GetRange().Combine());
+ }
+
+ rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab,
+ aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+ }
+
+ std::unique_ptr<ScRange> pRepaintRange;
+ if(nOldFormat)
+ {
+ ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
+ if(pOldFormat)
+ {
+ pRepaintRange.reset(new ScRange( pOldFormat->GetRange().Combine() ));
+ rDoc.RemoveCondFormatData(pOldFormat->GetRange(), nTab, pOldFormat->GetKey());
+ }
+
+ rDoc.DeleteConditionalFormat(nOldFormat, nTab);
+ rDoc.SetStreamValid(nTab, false);
+ }
+ if(pFormat)
+ {
+ if(pRepaintRange)
+ pRepaintRange->ExtendTo(aCombinedRange);
+ else
+ pRepaintRange.reset(new ScRange(aCombinedRange));
+
+ sal_uLong nIndex = rDoc.AddCondFormat(std::move(pFormat), nTab);
+
+ rDoc.AddCondFormatData(rRanges, nTab, nIndex);
+ rDoc.SetStreamValid(nTab, false);
+ }
+
+ if(bUndo)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab,
+ aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab,
+ InsertDeleteFlags::ALL, false, *pRedoDoc);
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConditionalFormat>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), aCompleteRange));
+ }
+
+ if(pRepaintRange)
+ rDocShell.PostPaint(*pRepaintRange, PaintPartFlags::Grid, SC_PF_TESTMERGE);
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+void ScDocFunc::SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if(rDoc.IsTabProtected(nTab))
+ return;
+
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScDocumentUniquePtr pUndoDoc;
+ if (bUndo)
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ ScConditionalFormatList* pOld = rDoc.GetCondFormList(nTab);
+
+ if (pOld)
+ pUndoDoc->SetCondFormList(new ScConditionalFormatList(*pUndoDoc, *pOld), nTab);
+ else
+ pUndoDoc->SetCondFormList(nullptr, nTab);
+
+ }
+
+ // first remove all old entries
+ ScConditionalFormatList* pOldList = rDoc.GetCondFormList(nTab);
+ pOldList->RemoveFromDocument(rDoc);
+
+ // then set new entries
+ pList->AddToDocument(rDoc);
+
+ rDoc.SetCondFormList(pList, nTab);
+ rDocShell.PostPaintGridAll();
+
+ if(bUndo)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ pRedoDoc->SetCondFormList(new ScConditionalFormatList(*pRedoDoc, *pList), nTab);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConditionalFormatList>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), nTab));
+ }
+
+ rDoc.SetStreamValid(nTab, false);
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bInteraction )
+{
+ ScDocShellModificator aModificator(rDocShell);
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScEditableTester aTester(rDoc, rRange);
+ if (!aTester.IsEditable())
+ {
+ if (bInteraction)
+ rDocShell.ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ sc::TableValues aUndoVals(rRange);
+ sc::TableValues* pUndoVals = bRecord ? &aUndoVals : nullptr;
+
+ rDoc.ConvertFormulaToValue(rRange, pUndoVals);
+
+ if (bRecord && pUndoVals)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<sc::UndoFormulaToValue>(&rDocShell, *pUndoVals));
+ }
+
+ rDocShell.PostPaint(rRange, PaintPartFlags::Grid);
+ rDocShell.PostDataChanged();
+ rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
+ aModificator.SetDocumentModified();
+}
+
+void ScDocFunc::EnterListAction(TranslateId pNameResId)
+{
+ OUString aUndo(ScResId(pNameResId));
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
+}
+
+void ScDocFunc::EndListAction()
+{
+ rDocShell.GetUndoManager()->LeaveListAction();
+}
+
+bool ScDocFunc::InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup)
+{
+ std::vector<sc::SparklineData> aSparklineDataVector;
+
+ if (rSparklineRange.aStart.Col() == rSparklineRange.aEnd.Col())
+ {
+ sal_Int32 nOutputRowSize = rSparklineRange.aEnd.Row() - rSparklineRange.aStart.Row();
+
+ auto eInputOrientation = sc::calculateOrientation(nOutputRowSize, rDataRange);
+
+ if (eInputOrientation == sc::RangeOrientation::Unknown)
+ return false;
+
+ sal_Int32 nIndex = 0;
+
+ for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Row() <= rSparklineRange.aEnd.Row();
+ aAddress.IncRow())
+ {
+ ScRange aInputRangeSlice = rDataRange;
+ if (eInputOrientation == sc::RangeOrientation::Row)
+ {
+ aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex);
+ aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex);
+ }
+ else
+ {
+ aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex);
+ aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex);
+ }
+
+ aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice);
+
+ nIndex++;
+ }
+ }
+ else if (rSparklineRange.aStart.Row() == rSparklineRange.aEnd.Row())
+ {
+ sal_Int32 nOutputColSize = rSparklineRange.aEnd.Col() - rSparklineRange.aStart.Col();
+
+ auto eInputOrientation = sc::calculateOrientation(nOutputColSize, rDataRange);
+
+ if (eInputOrientation == sc::RangeOrientation::Unknown)
+ return false;
+
+ sal_Int32 nIndex = 0;
+
+ for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Col() <= rSparklineRange.aEnd.Col();
+ aAddress.IncCol())
+ {
+ ScRange aInputRangeSlice = rDataRange;
+ if (eInputOrientation == sc::RangeOrientation::Row)
+ {
+ aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex);
+ aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex);
+ }
+ else
+ {
+ aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex);
+ aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex);
+ }
+
+ aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice);
+
+ nIndex++;
+ }
+ }
+
+ if (aSparklineDataVector.empty())
+ return false;
+
+ auto pUndoInsertSparkline = std::make_unique<sc::UndoInsertSparkline>(rDocShell, aSparklineDataVector, pSparklineGroup);
+ // insert the sparkline by "redoing"
+ pUndoInsertSparkline->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoInsertSparkline));
+
+ return true;
+}
+
+bool ScDocFunc::DeleteSparkline(ScAddress const& rAddress)
+{
+ auto& rDocument = rDocShell.GetDocument();
+
+ if (!rDocument.HasSparkline(rAddress))
+ return false;
+
+ auto pUndoDeleteSparkline = std::make_unique<sc::UndoDeleteSparkline>(rDocShell, rAddress);
+ // delete sparkline by "redoing"
+ pUndoDeleteSparkline->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDeleteSparkline));
+
+ return true;
+}
+
+bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup, SCTAB nTab)
+{
+ if (!pSparklineGroup)
+ return false;
+
+ auto& rDocument = rDocShell.GetDocument();
+
+ if (!rDocument.HasTable(nTab))
+ return false;
+
+ auto pUndo = std::make_unique<sc::UndoDeleteSparklineGroup>(rDocShell, pSparklineGroup, nTab);
+ // delete sparkline group by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGroup> const& pExistingSparklineGroup,
+ sc::SparklineAttributes const& rNewAttributes)
+{
+ auto pUndo = std::make_unique<sc::UndoEditSparklneGroup>(rDocShell, pExistingSparklineGroup, rNewAttributes);
+ // change sparkline group attributes by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup> const& rpGroup)
+{
+ auto pUndo = std::make_unique<sc::UndoGroupSparklines>(rDocShell, rRange, rpGroup);
+ // group sparklines by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::UngroupSparklines(ScRange const& rRange)
+{
+ auto pUndo = std::make_unique<sc::UndoUngroupSparklines>(rDocShell, rRange);
+ // ungroup sparklines by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+bool ScDocFunc::ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange)
+{
+ auto pUndo = std::make_unique<sc::UndoEditSparkline>(rDocShell, rpSparkline, nTab, rDataRange);
+ // change sparkline by "redoing"
+ pUndo->Redo();
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docfuncutil.cxx b/sc/source/ui/docshell/docfuncutil.cxx
new file mode 100644
index 0000000000..ce1a25d618
--- /dev/null
+++ b/sc/source/ui/docshell/docfuncutil.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 <docfuncutil.hxx>
+#include <document.hxx>
+#include <undobase.hxx>
+#include <global.hxx>
+#include <undoblk.hxx>
+#include <columnspanset.hxx>
+
+#include <memory>
+#include <utility>
+
+namespace sc {
+
+bool DocFuncUtil::hasProtectedTab( const ScDocument& rDoc, const ScMarkData& rMark )
+{
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rDoc.IsTabProtected(rTab))
+ return true;
+ }
+
+ return false;
+}
+
+ScDocumentUniquePtr DocFuncUtil::createDeleteContentsUndoDoc(
+ ScDocument& rDoc, const ScMarkData& rMark, const ScRange& rRange,
+ InsertDeleteFlags nFlags, bool bOnlyMarked )
+{
+ ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
+ SCTAB nTab = rRange.aStart.Tab();
+ pUndoDoc->InitUndo(rDoc, nTab, nTab);
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : rMark)
+ if (rTab != nTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ ScRange aCopyRange = rRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+
+ // in case of "Format/Standard" copy all attributes, because CopyToDocument
+ // with InsertDeleteFlags::HARDATTR only is too time-consuming:
+ InsertDeleteFlags nUndoDocFlags = nFlags;
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ nUndoDocFlags |= InsertDeleteFlags::ATTRIB;
+ if (nFlags & InsertDeleteFlags::EDITATTR) // Edit-Engine-Attribute
+ nUndoDocFlags |= InsertDeleteFlags::STRING; // -> cells will be changed
+ if (nFlags & InsertDeleteFlags::NOTE)
+ nUndoDocFlags |= InsertDeleteFlags::CONTENTS; // copy all cells with their notes
+ // do not copy note captions to undo document
+ nUndoDocFlags |= InsertDeleteFlags::NOCAPTIONS;
+ rDoc.CopyToDocument(aCopyRange, nUndoDocFlags, bOnlyMarked, *pUndoDoc, &rMark);
+
+ return pUndoDoc;
+}
+
+void DocFuncUtil::addDeleteContentsUndo(
+ SfxUndoManager* pUndoMgr, ScDocShell* pDocSh, const ScMarkData& rMark,
+ const ScRange& rRange, ScDocumentUniquePtr&& pUndoDoc, InsertDeleteFlags nFlags,
+ const std::shared_ptr<ScSimpleUndo::DataSpansType>& pSpans,
+ bool bMulti, bool bDrawUndo )
+{
+ std::unique_ptr<ScUndoDeleteContents> pUndo(
+ new ScUndoDeleteContents(
+ pDocSh, rMark, rRange, std::move(pUndoDoc), bMulti, nFlags, bDrawUndo));
+ pUndo->SetDataSpans(pSpans);
+
+ pUndoMgr->AddUndoAction(std::move(pUndo));
+}
+
+std::shared_ptr<ScSimpleUndo::DataSpansType> DocFuncUtil::getNonEmptyCellSpans(
+ const ScDocument& rDoc, const ScMarkData& rMark, const ScRange& rRange )
+{
+ auto pDataSpans = std::make_shared<ScSimpleUndo::DataSpansType>();
+ for (const SCTAB nTab : rMark)
+ {
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+
+ std::pair<ScSimpleUndo::DataSpansType::iterator,bool> r =
+ pDataSpans->insert(std::make_pair(nTab, std::make_unique<sc::ColumnSpanSet>()));
+
+ if (r.second)
+ {
+ sc::ColumnSpanSet *const pSet = r.first->second.get();
+ pSet->scan(rDoc, nTab, nCol1, nRow1, nCol2, nRow2, true);
+ }
+ }
+
+ return pDataSpans;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx
new file mode 100644
index 0000000000..77cf975166
--- /dev/null
+++ b/sc/source/ui/docshell/docsh.cxx
@@ -0,0 +1,3458 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <docsh.hxx>
+
+#include <config_features.h>
+#include <scitems.hxx>
+#include <sc.hrc>
+#include <vcl/errinf.hxx>
+#include <editeng/justifyitem.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/classids.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <formula/errorcodes.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <svl/PasswordHelper.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dinfdlg.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/documentlockfile.hxx>
+#include <svl/fstathelper.hxx>
+#include <svl/sharecontrolfile.hxx>
+#include <svl/urihelper.hxx>
+#include <osl/file.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <com/sun/star/awt/Key.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <com/sun/star/script/vba/VBAScriptEventId.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/script/vba/XVBAScriptListener.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <ooo/vba/excel/XWorkbook.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <config_folders.h>
+
+#include <scabstdlg.hxx>
+#include <sot/formats.hxx>
+#include <svx/compatflags.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <docmodel/theme/Theme.hxx>
+
+#include <formulacell.hxx>
+#include <global.hxx>
+#include <filter.hxx>
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+#include <docfunc.hxx>
+#include <imoptdlg.hxx>
+#include <impex.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <globstr.hrc>
+#include <scerrors.hxx>
+#include <brdcst.hxx>
+#include <stlpool.hxx>
+#include <autostyl.hxx>
+#include <attrib.hxx>
+#include <asciiopt.hxx>
+#include <progress.hxx>
+#include <pntlock.hxx>
+#include <docuno.hxx>
+#include <appoptio.hxx>
+#include <formulaopt.hxx>
+#include <scdll.hxx>
+#include <detdata.hxx>
+#include <printfun.hxx>
+#include <dociter.hxx>
+#include <cellform.hxx>
+#include <chartlis.hxx>
+#include <hints.hxx>
+#include <xmlwrap.hxx>
+#include <drwlayer.hxx>
+#include <dbdata.hxx>
+#include <scextopt.hxx>
+#include <compiler.hxx>
+#include <warnpassword.hxx>
+#include <sheetdata.hxx>
+#include <table.hxx>
+#include <tabprotection.hxx>
+#include <docparam.hxx>
+#include "docshimp.hxx"
+#include <sizedev.hxx>
+#include <undomanager.hxx>
+#include <refreshtimerprotector.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <uiitems.hxx>
+#include <dpobject.hxx>
+#include <markdata.hxx>
+#include <docoptio.hxx>
+#include <orcusfilters.hxx>
+#include <datastream.hxx>
+#include <documentlinkmgr.hxx>
+#include <refupdatecontext.hxx>
+
+#include <memory>
+#include <vector>
+
+#include <comphelper/lok.hxx>
+#include <svtools/sfxecode.hxx>
+#include <unotools/pathoptions.hxx>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::lang::XMultiServiceFactory;
+using std::shared_ptr;
+using ::std::vector;
+
+// Filter names (like in sclib.cxx)
+
+constexpr OUStringLiteral pFilterSc50 = u"StarCalc 5.0";
+const char pFilterXML[] = "StarOffice XML (Calc)";
+constexpr OUString pFilterLotus = u"Lotus"_ustr;
+const char pFilterQPro6[] = "Quattro Pro 6.0";
+const char16_t pFilterExcel4[] = u"MS Excel 4.0";
+const char16_t pFilterEx4Temp[] = u"MS Excel 4.0 Vorlage/Template";
+const char pFilterExcel5[] = "MS Excel 5.0/95";
+const char pFilterEx5Temp[] = "MS Excel 5.0/95 Vorlage/Template";
+const char pFilterExcel95[] = "MS Excel 95";
+const char pFilterEx95Temp[] = "MS Excel 95 Vorlage/Template";
+const char pFilterExcel97[] = "MS Excel 97";
+const char pFilterEx97Temp[] = "MS Excel 97 Vorlage/Template";
+constexpr OUString pFilterDBase = u"dBase"_ustr;
+constexpr OUString pFilterDif = u"DIF"_ustr;
+const char16_t pFilterSylk[] = u"SYLK";
+constexpr OUString pFilterHtml = u"HTML (StarCalc)"_ustr;
+constexpr OUString pFilterHtmlWebQ = u"calc_HTML_WebQuery"_ustr;
+const char16_t pFilterRtf[] = u"Rich Text Format (StarCalc)";
+
+#define ShellClass_ScDocShell
+#include <scslots.hxx>
+
+SFX_IMPL_INTERFACE(ScDocShell,SfxObjectShell)
+
+void ScDocShell::InitInterface_Impl()
+{
+}
+
+// GlobalName of the current version:
+SFX_IMPL_OBJECTFACTORY( ScDocShell, SvGlobalName(SO3_SC_CLASSID), "scalc" )
+
+
+void ScDocShell::FillClass( SvGlobalName* pClassName,
+ SotClipboardFormatId* pFormat,
+ OUString* pFullTypeName,
+ sal_Int32 nFileFormat,
+ bool bTemplate /* = false */) const
+{
+ if ( nFileFormat == SOFFICE_FILEFORMAT_60 )
+ {
+ *pClassName = SvGlobalName( SO3_SC_CLASSID_60 );
+ *pFormat = SotClipboardFormatId::STARCALC_60;
+ *pFullTypeName = ScResId( SCSTR_LONG_SCDOC_NAME_60 );
+ }
+ else if ( nFileFormat == SOFFICE_FILEFORMAT_8 )
+ {
+ *pClassName = SvGlobalName( SO3_SC_CLASSID_60 );
+ *pFormat = bTemplate ? SotClipboardFormatId::STARCALC_8_TEMPLATE : SotClipboardFormatId::STARCALC_8;
+ *pFullTypeName = ScResId( SCSTR_LONG_SCDOC_NAME_80 );
+ }
+ else
+ {
+ OSL_FAIL("Which version?");
+ }
+}
+
+std::set<Color> ScDocShell::GetDocColors()
+{
+ return m_pDocument->GetDocColors();
+}
+
+std::shared_ptr<model::ColorSet> ScDocShell::GetThemeColors()
+{
+ ScTabViewShell* pShell = GetBestViewShell();
+ if (!pShell)
+ return {};
+
+ SdrModel* pSdrModel = GetDocument().GetDrawLayer();
+ if (!pSdrModel)
+ return {};
+
+ auto const& pTheme = pSdrModel->getTheme();
+ if (!pTheme)
+ return {};
+
+ return pTheme->getColorSet();
+}
+
+void ScDocShell::DoEnterHandler()
+{
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if (pViewSh && pViewSh->GetViewData().GetDocShell() == this)
+ SC_MOD()->InputEnterHandler();
+}
+
+SCTAB ScDocShell::GetSaveTab()
+{
+ SCTAB nTab = 0;
+ ScTabViewShell* pSh = GetBestViewShell();
+ if (pSh)
+ {
+ const ScMarkData& rMark = pSh->GetViewData().GetMarkData();
+ nTab = rMark.GetFirstSelected();
+ }
+ return nTab;
+}
+
+HiddenInformation ScDocShell::GetHiddenInformationState( HiddenInformation nStates )
+{
+ // get global state like HiddenInformation::DOCUMENTVERSIONS
+ HiddenInformation nState = SfxObjectShell::GetHiddenInformationState( nStates );
+
+ if ( nStates & HiddenInformation::RECORDEDCHANGES )
+ {
+ if ( m_pDocument->GetChangeTrack() && m_pDocument->GetChangeTrack()->GetFirst() )
+ nState |= HiddenInformation::RECORDEDCHANGES;
+ }
+ if ( nStates & HiddenInformation::NOTES )
+ {
+ SCTAB nTableCount = m_pDocument->GetTableCount();
+ bool bFound = false;
+ for (SCTAB nTab = 0; nTab < nTableCount && !bFound; ++nTab)
+ {
+ if (m_pDocument->HasTabNotes(nTab)) //TODO:
+ bFound = true;
+ }
+
+ if (bFound)
+ nState |= HiddenInformation::NOTES;
+ }
+
+ return nState;
+}
+
+void ScDocShell::BeforeXMLLoading()
+{
+ m_pDocument->EnableIdle(false);
+
+ // prevent unnecessary broadcasts and updates
+ OSL_ENSURE(m_pModificator == nullptr, "The Modificator should not exist");
+ m_pModificator.reset( new ScDocShellModificator( *this ) );
+
+ m_pDocument->SetImportingXML( true );
+ m_pDocument->EnableExecuteLink( false ); // #i101304# to be safe, prevent nested loading from external references
+ m_pDocument->EnableUndo( false );
+ // prevent unnecessary broadcasts and "half way listeners"
+ m_pDocument->SetInsertingFromOtherDoc( true );
+}
+
+void ScDocShell::AfterXMLLoading(bool bRet)
+{
+ if (GetCreateMode() != SfxObjectCreateMode::ORGANIZER)
+ {
+ UpdateLinks();
+ // don't prevent establishing of listeners anymore
+ m_pDocument->SetInsertingFromOtherDoc( false );
+ if ( bRet )
+ {
+ ScChartListenerCollection* pChartListener = m_pDocument->GetChartListenerCollection();
+ if (pChartListener)
+ pChartListener->UpdateDirtyCharts();
+
+ // #95582#; set the table names of linked tables to the new path
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ if (m_pDocument->IsLinked( i ))
+ {
+ OUString aName;
+ m_pDocument->GetName(i, aName);
+ OUString aLinkTabName = m_pDocument->GetLinkTab(i);
+ sal_Int32 nLinkTabNameLength = aLinkTabName.getLength();
+ sal_Int32 nNameLength = aName.getLength();
+ if (nLinkTabNameLength < nNameLength)
+ {
+
+ // remove the quotes on begin and end of the docname and restore the escaped quotes
+ const sal_Unicode* pNameBuffer = aName.getStr();
+ if ( *pNameBuffer == '\'' && // all docnames have to have a ' character on the first pos
+ ScGlobal::UnicodeStrChr( pNameBuffer, SC_COMPILER_FILE_TAB_SEP ) )
+ {
+ OUStringBuffer aDocURLBuffer;
+ bool bQuote = true; // Document name is always quoted
+ ++pNameBuffer;
+ while ( bQuote && *pNameBuffer )
+ {
+ if ( *pNameBuffer == '\'' && *(pNameBuffer-1) != '\\' )
+ bQuote = false;
+ else if( *pNameBuffer != '\\' || *(pNameBuffer+1) != '\'' )
+ aDocURLBuffer.append(*pNameBuffer); // If escaped quote: only quote in the name
+ ++pNameBuffer;
+ }
+
+ if( *pNameBuffer == SC_COMPILER_FILE_TAB_SEP ) // after the last quote of the docname should be the # char
+ {
+ sal_Int32 nIndex = nNameLength - nLinkTabNameLength;
+ INetURLObject aINetURLObject(aDocURLBuffer);
+ if(aName.match( aLinkTabName, nIndex) &&
+ (aName[nIndex - 1] == '#') && // before the table name should be the # char
+ !aINetURLObject.HasError()) // the docname should be a valid URL
+ {
+ aName = ScGlobal::GetDocTabName( m_pDocument->GetLinkDoc( i ), m_pDocument->GetLinkTab( i ) );
+ m_pDocument->RenameTab(i, aName, true/*bExternalDocument*/);
+ }
+ // else; nothing has to happen, because it is a user given name
+ }
+ // else; nothing has to happen, because it is a user given name
+ }
+ // else; nothing has to happen, because it is a user given name
+ }
+ // else; nothing has to happen, because it is a user given name
+ }
+ }
+
+ // #i94570# DataPilot table names have to be unique, or the tables can't be accessed by API.
+ // If no name (or an invalid name, skipped in ScXMLDataPilotTableContext::EndElement) was set, create a new name.
+ ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
+ if ( pDPCollection )
+ {
+ size_t nDPCount = pDPCollection->GetCount();
+ for (size_t nDP=0; nDP<nDPCount; ++nDP)
+ {
+ ScDPObject& rDPObj = (*pDPCollection)[nDP];
+ if (rDPObj.GetName().isEmpty())
+ rDPObj.SetName( pDPCollection->CreateNewName() );
+ }
+ }
+ }
+ }
+ else
+ m_pDocument->SetInsertingFromOtherDoc( false );
+
+ m_pDocument->SetImportingXML( false );
+ m_pDocument->EnableExecuteLink( true );
+ m_pDocument->EnableUndo( true );
+ m_bIsEmpty = false;
+
+ if (m_pModificator)
+ {
+ ScDocument::HardRecalcState eRecalcState = m_pDocument->GetHardRecalcState();
+ // Temporarily set hard-recalc to prevent calling
+ // ScFormulaCell::Notify() during destruction of the Modificator which
+ // will set the cells dirty.
+ if (eRecalcState == ScDocument::HardRecalcState::OFF)
+ m_pDocument->SetHardRecalcState(ScDocument::HardRecalcState::TEMPORARY);
+ m_pModificator.reset();
+ m_pDocument->SetHardRecalcState(eRecalcState);
+ }
+ else
+ {
+ OSL_FAIL("The Modificator should exist");
+ }
+
+ m_pDocument->EnableIdle(true);
+}
+
+namespace {
+
+class LoadMediumGuard
+{
+public:
+ explicit LoadMediumGuard(ScDocument* pDoc) :
+ mpDoc(pDoc)
+ {
+ mpDoc->SetLoadingMedium(true);
+ }
+
+ ~LoadMediumGuard()
+ {
+ mpDoc->SetLoadingMedium(false);
+ }
+private:
+ ScDocument* mpDoc;
+};
+
+void processDataStream( ScDocShell& rShell, const sc::ImportPostProcessData& rData )
+{
+ if (!rData.mpDataStream)
+ return;
+
+ const sc::ImportPostProcessData::DataStream& r = *rData.mpDataStream;
+ if (!r.maRange.IsValid())
+ return;
+
+ // Break the streamed range into the top range and the height limit. A
+ // height limit of 0 means unlimited i.e. the streamed data will go all
+ // the way to the last row.
+
+ ScRange aTopRange = r.maRange;
+ aTopRange.aEnd.SetRow(aTopRange.aStart.Row());
+ sal_Int32 nLimit = r.maRange.aEnd.Row() - r.maRange.aStart.Row() + 1;
+ if (r.maRange.aEnd.Row() == rShell.GetDocument().MaxRow())
+ // Unlimited range.
+ nLimit = 0;
+
+ sc::DataStream::MoveType eMove =
+ r.meInsertPos == sc::ImportPostProcessData::DataStream::InsertTop ?
+ sc::DataStream::MOVE_DOWN : sc::DataStream::RANGE_DOWN;
+
+ sc::DataStream* pStrm = new sc::DataStream(&rShell, r.maURL, aTopRange, nLimit, eMove);
+ pStrm->SetRefreshOnEmptyLine(r.mbRefreshOnEmpty);
+ sc::DocumentLinkManager& rMgr = rShell.GetDocument().GetDocLinkManager();
+ rMgr.setDataStream(pStrm);
+}
+
+class MessageWithCheck : public weld::MessageDialogController
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
+public:
+ MessageWithCheck(weld::Window *pParent, const OUString& rUIFile, const OUString& rDialogId)
+ : MessageDialogController(pParent, rUIFile, rDialogId, "ask")
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+ {
+ }
+ bool get_active() const { return m_xWarningOnBox->get_active(); }
+ void hide_ask() const { m_xWarningOnBox->set_visible(false); };
+};
+
+#if HAVE_FEATURE_SCRIPTING
+class VBAScriptListener : public ::cppu::WeakImplHelper< css::script::vba::XVBAScriptListener >
+{
+private:
+ ScDocShell* m_pDocSh;
+public:
+ VBAScriptListener(ScDocShell* pDocSh) : m_pDocSh(pDocSh)
+ {
+ }
+
+ // XVBAScriptListener
+ virtual void SAL_CALL notifyVBAScriptEvent( const ::css::script::vba::VBAScriptEvent& aEvent ) override
+ {
+ if (aEvent.Identifier == script::vba::VBAScriptEventId::SCRIPT_STOPPED &&
+ m_pDocSh->GetClipData().is())
+ {
+ m_pDocSh->SetClipData(uno::Reference<datatransfer::XTransferable2>());
+ }
+ }
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const ::css::lang::EventObject& /*Source*/ ) override
+ {
+ }
+};
+#endif
+
+}
+
+bool ScDocShell::LoadXML( SfxMedium* pLoadMedium, const css::uno::Reference< css::embed::XStorage >& xStor )
+{
+ LoadMediumGuard aLoadGuard(m_pDocument.get());
+
+ // MacroCallMode is no longer needed, state is kept in SfxObjectShell now
+
+ // no Seek(0) here - always loading from storage, GetInStream must not be called
+
+ BeforeXMLLoading();
+
+ ScXMLImportWrapper aImport(*this, pLoadMedium, xStor);
+
+ bool bRet = false;
+ ErrCodeMsg nError = ERRCODE_NONE;
+ m_pDocument->LockAdjustHeight();
+ if (GetCreateMode() == SfxObjectCreateMode::ORGANIZER)
+ bRet = aImport.Import(ImportFlags::Styles, nError);
+ else
+ bRet = aImport.Import(ImportFlags::All, nError);
+
+ if ( nError )
+ pLoadMedium->SetError(nError);
+
+ processDataStream(*this, aImport.GetImportPostProcessData());
+
+ //if the document was not generated by LibreOffice, do hard recalc in case some other document
+ //generator saved cached formula results that differ from LibreOffice's calculated results or
+ //did not use cached formula results.
+ uno::Reference<document::XDocumentProperties> xDocProps = GetModel()->getDocumentProperties();
+
+ ScRecalcOptions nRecalcMode =
+ static_cast<ScRecalcOptions>(officecfg::Office::Calc::Formula::Load::ODFRecalcMode::get());
+
+ bool bHardRecalc = false;
+ if (nRecalcMode == RECALC_ASK)
+ {
+ OUString sProductName(utl::ConfigManager::getProductName());
+ if (m_pDocument->IsUserInteractionEnabled() && xDocProps->getGenerator().indexOf(sProductName) == -1)
+ {
+ // Generator is not LibreOffice. Ask if the user wants to perform
+ // full re-calculation.
+ MessageWithCheck aQueryBox(GetActiveDialogParent(),
+ "modules/scalc/ui/recalcquerydialog.ui", "RecalcQueryDialog");
+ aQueryBox.set_primary_text(ScResId(STR_QUERY_FORMULA_RECALC_ONLOAD_ODS));
+ aQueryBox.set_default_response(RET_YES);
+
+ if ( officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::isReadOnly() )
+ aQueryBox.hide_ask();
+
+ bHardRecalc = aQueryBox.run() == RET_YES;
+
+ if (aQueryBox.get_active())
+ {
+ // Always perform selected action in the future.
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Load::ODFRecalcMode::set(sal_Int32(0), batch);
+ ScFormulaOptions aOpt = SC_MOD()->GetFormulaOptions();
+ aOpt.SetODFRecalcOptions(bHardRecalc ? RECALC_ALWAYS : RECALC_NEVER);
+ /* XXX is this really supposed to set the ScModule options?
+ * Not the ScDocShell options? */
+ SC_MOD()->SetFormulaOptions(aOpt);
+
+ batch->commit();
+ }
+ }
+ }
+ else if (nRecalcMode == RECALC_ALWAYS)
+ bHardRecalc = true;
+
+ if (bHardRecalc)
+ DoHardRecalc();
+ else
+ {
+ // still need to recalc volatile formula cells.
+ m_pDocument->Broadcast(ScHint(SfxHintId::ScDataChanged, BCA_BRDCST_ALWAYS));
+ }
+
+ AfterXMLLoading(bRet);
+
+ m_pDocument->UnlockAdjustHeight();
+ return bRet;
+}
+
+bool ScDocShell::SaveXML( SfxMedium* pSaveMedium, const css::uno::Reference< css::embed::XStorage >& xStor )
+{
+ m_pDocument->EnableIdle(false);
+
+ ScXMLImportWrapper aImport(*this, pSaveMedium, xStor);
+ bool bRet(false);
+ if (GetCreateMode() != SfxObjectCreateMode::ORGANIZER)
+ bRet = aImport.Export(false);
+ else
+ bRet = aImport.Export(true);
+
+ m_pDocument->EnableIdle(true);
+
+ return bRet;
+}
+
+bool ScDocShell::Load( SfxMedium& rMedium )
+{
+ LoadMediumGuard aLoadGuard(m_pDocument.get());
+ ScRefreshTimerProtector aProt( m_pDocument->GetRefreshTimerControlAddress() );
+
+ // only the latin script language is loaded
+ // -> initialize the others from options (before loading)
+ InitOptions(true);
+
+ // If this is an ODF file being loaded, then by default, use legacy processing
+ // (if required, it will be overridden in *::ReadUserDataSequence())
+ if (IsOwnStorageFormat(rMedium))
+ {
+ if (ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer())
+ {
+ pDrawLayer->SetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy,
+ true); // for tdf#99729
+ pDrawLayer->SetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork,
+ true); // for tdf#148000
+ }
+ }
+
+ GetUndoManager()->Clear();
+
+ bool bRet = SfxObjectShell::Load(rMedium);
+ if (bRet)
+ {
+ SetInitialLinkUpdate(&rMedium);
+
+ {
+ // prepare a valid document for XML filter
+ // (for ConvertFrom, InitNew is called before)
+ m_pDocument->MakeTable(0);
+ m_pDocument->GetStyleSheetPool()->CreateStandardStyles();
+ m_pDocument->UpdStlShtPtrsFrmNms();
+
+ /* Create styles that are imported through Orcus */
+
+ OUString aURL("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/calc/styles.xml");
+ rtl::Bootstrap::expandMacros(aURL);
+
+ OUString aPath;
+ osl::FileBase::getSystemPathFromFileURL(aURL, aPath);
+
+ ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
+
+ if (pOrcus)
+ {
+ pOrcus->importODS_Styles(*m_pDocument, aPath);
+ m_pDocument->GetStyleSheetPool()->setAllParaStandard();
+ }
+
+ bRet = LoadXML( &rMedium, nullptr );
+ }
+ }
+
+ if (!bRet && !rMedium.GetErrorIgnoreWarning())
+ rMedium.SetError(SVSTREAM_FILEFORMAT_ERROR);
+
+ if (rMedium.GetErrorIgnoreWarning())
+ SetError(rMedium.GetErrorIgnoreWarning());
+
+ InitItems();
+ CalcOutputFactor();
+
+ // invalidate eventually temporary table areas
+ if ( bRet )
+ m_pDocument->InvalidateTableArea();
+
+ m_bIsEmpty = false;
+ FinishedLoading();
+ return bRet;
+}
+
+void ScDocShell::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const ScTablesHint* pScHint = dynamic_cast< const ScTablesHint* >( &rHint );
+ if (pScHint)
+ {
+ if (pScHint->GetTablesHintId() == SC_TAB_INSERTED)
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = m_pDocument->GetVbaEventProcessor();
+ if ( xVbaEvents.is() ) try
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(pScHint->GetTab1()) };
+ xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKBOOK_NEWSHEET, aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ if ( auto pStyleSheetHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint) ) // Template changed
+ NotifyStyle( *pStyleSheetHint );
+ else if ( auto pStlHint = dynamic_cast<const ScAutoStyleHint*>(&rHint) )
+ {
+ //! direct call for AutoStyles
+
+ // this is called synchronously from ScInterpreter::ScStyle,
+ // modifying the document must be asynchronous
+ // (handled by AddInitial)
+
+ const ScRange& aRange = pStlHint->GetRange();
+ const OUString& aName1 = pStlHint->GetStyle1();
+ const OUString& aName2 = pStlHint->GetStyle2();
+ sal_uInt32 nTimeout = pStlHint->GetTimeout();
+
+ if (!m_pAutoStyleList)
+ m_pAutoStyleList.reset( new ScAutoStyleList(this) );
+ m_pAutoStyleList->AddInitial( aRange, aName1, nTimeout, aName2 );
+ }
+ else if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
+ {
+ switch (static_cast<const SfxEventHint&>(rHint).GetEventId())
+ {
+ case SfxEventHintId::LoadFinished:
+ {
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ // the readonly documents should not be opened in shared mode
+ if ( HasSharedXMLFlagSet() && !SC_MOD()->IsInSharedDocLoading() && !IsReadOnly() )
+ {
+ if ( SwitchToShared( true, false ) )
+ {
+ ScViewData* pViewData = GetViewData();
+ ScTabView* pTabView = ( pViewData ? pViewData->GetView() : nullptr );
+ if ( pTabView )
+ {
+ pTabView->UpdateLayerLocks();
+ }
+ }
+ else
+ {
+ // switching to shared mode has failed, the document should be opened readonly
+ // TODO/LATER: And error message should be shown here probably
+ SetReadOnlyUI();
+ }
+ }
+#endif
+ }
+ break;
+ case SfxEventHintId::ViewCreated:
+ {
+ #if HAVE_FEATURE_SCRIPTING
+ uno::Reference<script::vba::XVBACompatibility> xVBACompat(GetBasicContainer(), uno::UNO_QUERY);
+ if ( !m_xVBAListener.is() && xVBACompat.is() )
+ {
+ m_xVBAListener.set(new VBAScriptListener(this));
+ xVBACompat->addVBAScriptListener(m_xVBAListener);
+ }
+#endif
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ if ( IsDocShared() && !SC_MOD()->IsInSharedDocLoading()
+ && !comphelper::LibreOfficeKit::isActive() )
+ {
+ ScAppOptions aAppOptions = SC_MOD()->GetAppOptions();
+ if ( aAppOptions.GetShowSharedDocumentWarning() )
+ {
+ MessageWithCheck aWarningBox(ScDocShell::GetActiveDialogParent(),
+ "modules/scalc/ui/sharedwarningdialog.ui", "SharedWarningDialog");
+ aWarningBox.run();
+
+ bool bChecked = aWarningBox.get_active();
+ if (bChecked)
+ {
+ aAppOptions.SetShowSharedDocumentWarning(false);
+ SC_MOD()->SetAppOptions( aAppOptions );
+ }
+ }
+ }
+#endif
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+ uno::Reference< lang::XMultiServiceFactory > xServiceManager(
+ xContext->getServiceManager(),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xServiceManager, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration> xEnum = xEnumAccess->createContentEnumeration(
+ "com.sun.star.sheet.SpreadsheetDocumentJob" );
+ if ( xEnum.is() )
+ {
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Any aAny = xEnum->nextElement();
+ uno::Reference< lang::XSingleComponentFactory > xFactory;
+ aAny >>= xFactory;
+ if ( xFactory.is() )
+ {
+ uno::Reference< task::XJob > xJob( xFactory->createInstanceWithContext( xContext ), uno::UNO_QUERY_THROW );
+ ScViewData* pViewData = GetViewData();
+ SfxViewShell* pViewShell = ( pViewData ? pViewData->GetViewShell() : nullptr );
+ SfxViewFrame* pViewFrame = ( pViewShell ? &pViewShell->GetViewFrame() : nullptr );
+ SfxFrame* pFrame = ( pViewFrame ? &pViewFrame->GetFrame() : nullptr );
+ uno::Reference< frame::XController > xController = ( pFrame ? pFrame->GetController() : nullptr );
+ uno::Reference< sheet::XSpreadsheetView > xSpreadsheetView( xController, uno::UNO_QUERY_THROW );
+ uno::Sequence< beans::NamedValue > aArgsForJob { { "SpreadsheetView", uno::Any( xSpreadsheetView ) } };
+ xJob->execute( aArgsForJob );
+ }
+ }
+ }
+ }
+ catch ( uno::Exception & )
+ {
+ }
+ }
+ break;
+ case SfxEventHintId::SaveDoc:
+ {
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ if ( IsDocShared() && !SC_MOD()->IsInSharedDocSaving() )
+ {
+ bool bSuccess = false;
+ bool bRetry = true;
+ while ( bRetry )
+ {
+ bRetry = false;
+ uno::Reference< frame::XModel > xModel;
+ try
+ {
+ // load shared file
+ xModel.set( LoadSharedDocument(), uno::UNO_SET_THROW );
+ uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY_THROW );
+
+ // check if shared flag is set in shared file
+ bool bShared = false;
+ ScModelObj* pDocObj = comphelper::getFromUnoTunnel<ScModelObj>( xModel );
+ ScDocShell* pSharedDocShell = ( pDocObj ? dynamic_cast< ScDocShell* >( pDocObj->GetObjectShell() ) : nullptr );
+ if ( pSharedDocShell )
+ {
+ bShared = pSharedDocShell->HasSharedXMLFlagSet();
+ }
+
+ // #i87870# check if shared status was disabled and enabled again
+ bool bOwnEntry = false;
+ bool bEntriesNotAccessible = false;
+ try
+ {
+ ::svt::ShareControlFile aControlFile( GetSharedFileURL() );
+ bOwnEntry = aControlFile.HasOwnEntry();
+ }
+ catch ( uno::Exception& )
+ {
+ bEntriesNotAccessible = true;
+ }
+
+ if ( bShared && bOwnEntry )
+ {
+ uno::Reference< frame::XStorable > xStorable( xModel, uno::UNO_QUERY_THROW );
+
+ if ( xStorable->isReadonly() )
+ {
+ xCloseable->close( true );
+
+ OUString aUserName( ScResId( STR_UNKNOWN_USER ) );
+ bool bNoLockAccess = false;
+ try
+ {
+ ::svt::DocumentLockFile aLockFile( GetSharedFileURL() );
+ LockFileEntry aData = aLockFile.GetLockData();
+ if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
+ {
+ aUserName = aData[LockFileComponent::OOOUSERNAME];
+ }
+ else if ( !aData[LockFileComponent::SYSUSERNAME].isEmpty() )
+ {
+ aUserName = aData[LockFileComponent::SYSUSERNAME];
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ bNoLockAccess = true;
+ }
+
+ if ( bNoLockAccess )
+ {
+ // TODO/LATER: in future an error regarding impossibility to open file for writing could be shown
+ ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
+ }
+ else
+ {
+ OUString aMessage( ScResId( STR_FILE_LOCKED_SAVE_LATER ) );
+ aMessage = aMessage.replaceFirst( "%1", aUserName );
+
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Warning, VclButtonsType::NONE,
+ aMessage));
+ xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY);
+ xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
+ xWarn->set_default_response(RET_RETRY);
+ if (xWarn->run() == RET_RETRY)
+ {
+ bRetry = true;
+ }
+ }
+ }
+ else
+ {
+ // merge changes from shared file into temp file
+ bool bSaveToShared = false;
+ if ( pSharedDocShell )
+ {
+ bSaveToShared = MergeSharedDocument( pSharedDocShell );
+ }
+
+ // close shared file
+ xCloseable->close( true );
+
+ // TODO: keep file lock on shared file
+
+ // store to shared file
+ if ( bSaveToShared )
+ {
+ bool bChangedViewSettings = false;
+ ScChangeViewSettings* pChangeViewSet = m_pDocument->GetChangeViewSettings();
+ if ( pChangeViewSet && pChangeViewSet->ShowChanges() )
+ {
+ pChangeViewSet->SetShowChanges( false );
+ pChangeViewSet->SetShowAccepted( false );
+ m_pDocument->SetChangeViewSettings( *pChangeViewSet );
+ bChangedViewSettings = true;
+ }
+
+ // TODO/LATER: More entries from the MediaDescriptor might be interesting for the merge
+ uno::Sequence< beans::PropertyValue > aValues{
+ comphelper::makePropertyValue(
+ "FilterName",
+ GetMedium()->GetFilter()->GetFilterName())
+ };
+
+ const SfxStringItem* pPasswordItem = GetMedium()->GetItemSet().GetItem(SID_PASSWORD, false);
+ if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() )
+ {
+ aValues.realloc( 2 );
+ auto pValues = aValues.getArray();
+ pValues[1].Name = "Password";
+ pValues[1].Value <<= pPasswordItem->GetValue();
+ }
+ const SfxUnoAnyItem* pEncryptionItem = GetMedium()->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if (pEncryptionItem)
+ {
+ aValues.realloc(aValues.getLength() + 1);
+ auto pValues = aValues.getArray();
+ pValues[aValues.getLength() - 1].Name = "EncryptionData";
+ pValues[aValues.getLength() - 1].Value = pEncryptionItem->GetValue();
+ }
+
+ SC_MOD()->SetInSharedDocSaving( true );
+ GetModel()->storeToURL( GetSharedFileURL(), aValues );
+ SC_MOD()->SetInSharedDocSaving( false );
+
+ if ( bChangedViewSettings )
+ {
+ pChangeViewSet->SetShowChanges( true );
+ pChangeViewSet->SetShowAccepted( true );
+ m_pDocument->SetChangeViewSettings( *pChangeViewSet );
+ }
+ }
+
+ bSuccess = true;
+ GetUndoManager()->Clear();
+ }
+ }
+ else
+ {
+ xCloseable->close( true );
+
+ if ( bEntriesNotAccessible )
+ {
+ // TODO/LATER: in future an error regarding impossibility to write to share control file could be shown
+ ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_DOC_NOLONGERSHARED)));
+ xWarn->run();
+
+ SfxBindings* pBindings = GetViewBindings();
+ if ( pBindings )
+ {
+ pBindings->ExecuteSynchron( SID_SAVEASDOC );
+ }
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "SfxEventHintId::SaveDoc" );
+ SC_MOD()->SetInSharedDocSaving( false );
+
+ try
+ {
+ uno::Reference< util::XCloseable > xClose( xModel, uno::UNO_QUERY_THROW );
+ xClose->close( true );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ if ( !bSuccess )
+ SetError(ERRCODE_IO_ABORT); // this error code will produce no error message, but will break the further saving process
+ }
+#endif
+
+ if (m_pSheetSaveData)
+ m_pSheetSaveData->SetInSupportedSave(true);
+ }
+ break;
+ case SfxEventHintId::SaveAsDoc:
+ {
+ if ( GetDocument().GetExternalRefManager()->containsUnsavedReferences() )
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Warning, VclButtonsType::YesNo,
+ ScResId(STR_UNSAVED_EXT_REF)));
+ if (RET_NO == xWarn->run())
+ {
+ SetError(ERRCODE_IO_ABORT); // this error code will produce no error message, but will break the further saving process
+ }
+ }
+ [[fallthrough]];
+ }
+ case SfxEventHintId::SaveToDoc:
+ // #i108978# If no event is sent before saving, there will also be no "...DONE" event,
+ // and SAVE/SAVEAS can't be distinguished from SAVETO. So stream copying is only enabled
+ // if there is a SAVE/SAVEAS/SAVETO event first.
+ if (m_pSheetSaveData)
+ m_pSheetSaveData->SetInSupportedSave(true);
+ break;
+ case SfxEventHintId::SaveDocDone:
+ case SfxEventHintId::SaveAsDocDone:
+ {
+ // new positions are used after "save" and "save as", but not "save to"
+ UseSheetSaveEntries(); // use positions from saved file for next saving
+ [[fallthrough]];
+ }
+ case SfxEventHintId::SaveToDocDone:
+ // only reset the flag, don't use the new positions
+ if (m_pSheetSaveData)
+ m_pSheetSaveData->SetInSupportedSave(false);
+ break;
+ default:
+ {
+ }
+ break;
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::TitleChanged) // Without parameter
+ {
+ m_pDocument->SetName( SfxShell::GetName() );
+ // RegisterNewTargetNames doesn't exist any longer
+ SfxGetpApp()->Broadcast(SfxHint( SfxHintId::ScDocNameChanged )); // Navigator
+ }
+ else if (rHint.GetId() == SfxHintId::Deinitializing)
+ {
+
+#if HAVE_FEATURE_SCRIPTING
+ uno::Reference<script::vba::XVBACompatibility> xVBACompat(GetBasicContainer(), uno::UNO_QUERY);
+ if (m_xVBAListener.is() && xVBACompat.is())
+ {
+ xVBACompat->removeVBAScriptListener(m_xVBAListener);
+ }
+#endif
+
+ if (m_pDocument->IsClipboardSource())
+ {
+ // Notes copied to the clipboard have a raw SdrCaptionObj pointer
+ // copied from this document, forget it as it references this
+ // document's drawing layer pages and what not, which otherwise when
+ // pasting to another document after this document was destructed would
+ // attempt to access non-existing data. Preserve the text data though.
+ ScDocument* pClipDoc = ScModule::GetClipDoc();
+ if (pClipDoc)
+ pClipDoc->ClosingClipboardSource();
+ }
+ }
+
+ if (rHint.GetId() != SfxHintId::ThisIsAnSfxEventHint)
+ return;
+
+ switch(static_cast<const SfxEventHint&>(rHint).GetEventId())
+ {
+ case SfxEventHintId::CreateDoc:
+ {
+ uno::Any aWorkbook;
+ aWorkbook <<= mxAutomationWorkbookObject;
+ uno::Sequence< uno::Any > aArgs{ aWorkbook };
+ SC_MOD()->CallAutomationApplicationEventSinks( "NewWorkbook", aArgs );
+ }
+ break;
+ case SfxEventHintId::OpenDoc:
+ {
+ uno::Any aWorkbook;
+ aWorkbook <<= mxAutomationWorkbookObject;
+ uno::Sequence< uno::Any > aArgs{ aWorkbook };
+ SC_MOD()->CallAutomationApplicationEventSinks( "WorkbookOpen", aArgs );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// Load contents for organizer
+bool ScDocShell::LoadFrom( SfxMedium& rMedium )
+{
+ LoadMediumGuard aLoadGuard(m_pDocument.get());
+ ScRefreshTimerProtector aProt( m_pDocument->GetRefreshTimerControlAddress() );
+
+ weld::WaitObject aWait( GetActiveDialogParent() );
+
+ bool bRet = false;
+
+ SetInitialLinkUpdate(&rMedium);
+
+ // until loading/saving only the styles in XML is implemented,
+ // load the whole file
+ bRet = LoadXML( &rMedium, nullptr );
+ InitItems();
+
+ SfxObjectShell::LoadFrom( rMedium );
+
+ return bRet;
+}
+
+static void lcl_parseHtmlFilterOption(const OUString& rOption, LanguageType& rLang, bool& rDateConvert, bool& rScientificConvert)
+{
+ OUStringBuffer aBuf;
+ std::vector< OUString > aTokens;
+ sal_Int32 n = rOption.getLength();
+ const sal_Unicode* p = rOption.getStr();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (c == ' ')
+ {
+ if (!aBuf.isEmpty())
+ aTokens.push_back( aBuf.makeStringAndClear() );
+ }
+ else
+ aBuf.append(c);
+ }
+
+ if (!aBuf.isEmpty())
+ aTokens.push_back( aBuf.makeStringAndClear() );
+
+ rLang = LanguageType( 0 );
+ rDateConvert = false;
+
+ if (!aTokens.empty())
+ rLang = static_cast<LanguageType>(aTokens[0].toInt32());
+ if (aTokens.size() > 1)
+ rDateConvert = static_cast<bool>(aTokens[1].toInt32());
+ if (aTokens.size() > 2)
+ rScientificConvert = static_cast<bool>(aTokens[2].toInt32());
+}
+
+bool ScDocShell::ConvertFrom( SfxMedium& rMedium )
+{
+ LoadMediumGuard aLoadGuard(m_pDocument.get());
+
+ bool bRet = false; // sal_False means user quit!
+ // On error: Set error at stream
+
+ ScRefreshTimerProtector aProt( m_pDocument->GetRefreshTimerControlAddress() );
+
+ GetUndoManager()->Clear();
+
+ // Set optimal col width after import?
+ bool bSetColWidths = false;
+ bool bSetSimpleTextColWidths = false;
+ std::map<SCCOL, ScColWidthParam> aColWidthParam;
+ ScRange aColWidthRange;
+ // Set optimal row height after import?
+ bool bSetRowHeights = false;
+
+ vector<ScDocRowHeightUpdater::TabRanges> aRecalcRowRangesArray;
+
+ // All filters need the complete file in one piece (not asynchronously)
+ // So make sure that we transfer the whole file with CreateFileStream
+ rMedium.GetPhysicalName(); //! Call CreateFileStream directly, if available
+
+ SetInitialLinkUpdate(&rMedium);
+
+ std::shared_ptr<const SfxFilter> pFilter = rMedium.GetFilter();
+ if (pFilter)
+ {
+ OUString aFltName = pFilter->GetFilterName();
+
+ bool bCalc3 = aFltName == "StarCalc 3.0";
+ bool bCalc4 = aFltName == "StarCalc 4.0";
+ if (!bCalc3 && !bCalc4)
+ m_pDocument->SetInsertingFromOtherDoc( true );
+
+ if (aFltName == pFilterXML)
+ bRet = LoadXML( &rMedium, nullptr );
+ else if (aFltName == pFilterLotus)
+ {
+ OUString sItStr;
+ if ( const SfxStringItem* pOptionsItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS, true ) )
+ {
+ sItStr = pOptionsItem->GetValue();
+ }
+
+ if (sItStr.isEmpty())
+ {
+ // default for lotus import (from API without options):
+ // IBM_437 encoding
+ sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_IBM_437 );
+ }
+
+ ErrCode eError = ScFormatFilter::Get().ScImportLotus123( rMedium, *m_pDocument,
+ ScGlobal::GetCharsetValue(sItStr));
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+ bSetColWidths = true;
+ bSetRowHeights = true;
+ }
+ else if ( aFltName == pFilterExcel4 || aFltName == pFilterExcel5 ||
+ aFltName == pFilterExcel95 || aFltName == pFilterExcel97 ||
+ aFltName == pFilterEx4Temp || aFltName == pFilterEx5Temp ||
+ aFltName == pFilterEx95Temp || aFltName == pFilterEx97Temp )
+ {
+ EXCIMPFORMAT eFormat = EIF_AUTO;
+ if ( aFltName == pFilterExcel4 || aFltName == pFilterEx4Temp )
+ eFormat = EIF_BIFF_LE4;
+ else if ( aFltName == pFilterExcel5 || aFltName == pFilterExcel95 ||
+ aFltName == pFilterEx5Temp || aFltName == pFilterEx95Temp )
+ eFormat = EIF_BIFF5;
+ else if ( aFltName == pFilterExcel97 || aFltName == pFilterEx97Temp )
+ eFormat = EIF_BIFF8;
+
+ MakeDrawLayer(); //! In the filter
+ CalcOutputFactor(); // prepare update of row height
+ ErrCode eError = ScFormatFilter::Get().ScImportExcel( rMedium, m_pDocument.get(), eFormat );
+ m_pDocument->UpdateFontCharSet();
+ if ( m_pDocument->IsChartListenerCollectionNeedsUpdate() )
+ m_pDocument->UpdateChartListenerCollection(); //! For all imports?
+
+ // all graphics objects must have names
+ m_pDocument->EnsureGraphicNames();
+
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+ }
+ else if (aFltName == SC_TEXT_CSV_FILTER_NAME)
+ {
+ ScAsciiOptions aOptions;
+ bool bOptInit = false;
+
+ if ( const SfxStringItem* pOptionsItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ aOptions.ReadFromString( pOptionsItem->GetValue() );
+ bOptInit = true;
+ }
+
+ if ( !bOptInit )
+ {
+ // default for ascii import (from API without options):
+ // UTF-8 encoding, comma, double quotes
+
+ aOptions.SetCharSet(RTL_TEXTENCODING_UTF8);
+ aOptions.SetFieldSeps( OUString(',') );
+ aOptions.SetTextSep( '"' );
+ }
+
+ ErrCode eError = ERRCODE_NONE;
+ bool bOverflowRow, bOverflowCol, bOverflowCell;
+ bOverflowRow = bOverflowCol = bOverflowCell = false;
+
+ if( ! rMedium.IsStorage() )
+ {
+ ScImportExport aImpEx( *m_pDocument );
+ aImpEx.SetExtOptions( aOptions );
+
+ SvStream* pInStream = rMedium.GetInStream();
+ if (pInStream)
+ {
+ pInStream->SetStreamCharSet( aOptions.GetCharSet() );
+ pInStream->Seek( 0 );
+ bRet = aImpEx.ImportStream( *pInStream, rMedium.GetBaseURL(), SotClipboardFormatId::STRING );
+ eError = bRet ? ERRCODE_NONE : SCERR_IMPORT_CONNECT;
+ m_pDocument->StartAllListeners();
+ sc::SetFormulaDirtyContext aCxt;
+ m_pDocument->SetAllFormulasDirty(aCxt);
+
+ // tdf#82254 - check whether to include a byte-order-mark in the output
+ if (const bool bIncludeBOM = aImpEx.GetIncludeBOM())
+ {
+ aOptions.SetIncludeBOM(bIncludeBOM);
+ rMedium.GetItemSet().Put(
+ SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions.WriteToString()));
+ }
+
+ // for mobile case, we use a copy of the original document and give it a temporary name before editing
+ // Therefore, the sheet name becomes ugly, long and nonsensical.
+#if !(defined ANDROID)
+ // The same resulting name has to be handled in
+ // ScExternalRefCache::initializeDoc() and related, hence
+ // pass 'true' for RenameTab()'s bExternalDocument for a
+ // composed name so ValidTabName() will not be checked,
+ // which could veto the rename in case it contained
+ // characters that Excel does not handle. If we wanted to
+ // change that then it needed to be handled in all
+ // corresponding places of the external references
+ // manager/cache. Likely then we'd also need a method to
+ // compose a name excluding such characters.
+ m_pDocument->RenameTab( 0, INetURLObject( rMedium.GetName()).GetBase(), true/*bExternalDocument*/);
+#endif
+ bOverflowRow = aImpEx.IsOverflowRow();
+ bOverflowCol = aImpEx.IsOverflowCol();
+ bOverflowCell = aImpEx.IsOverflowCell();
+ }
+ else
+ {
+ OSL_FAIL( "No Stream" );
+ }
+ }
+
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else if (!GetErrorIgnoreWarning() && (bOverflowRow || bOverflowCol || bOverflowCell))
+ {
+ // precedence: row, column, cell
+ ErrCode nWarn = (bOverflowRow ? SCWARN_IMPORT_ROW_OVERFLOW :
+ (bOverflowCol ? SCWARN_IMPORT_COLUMN_OVERFLOW :
+ SCWARN_IMPORT_CELL_OVERFLOW));
+ SetError(nWarn);
+ }
+ bSetColWidths = true;
+ bSetSimpleTextColWidths = true;
+ }
+ else if (aFltName == pFilterDBase)
+ {
+ OUString sItStr;
+ if ( const SfxStringItem* pOptionsItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ sItStr = pOptionsItem->GetValue();
+ }
+
+ if (sItStr.isEmpty())
+ {
+ // default for dBase import (from API without options):
+ // IBM_850 encoding
+
+ sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_IBM_850 );
+ }
+
+ ScDocRowHeightUpdater::TabRanges aRecalcRanges(0, m_pDocument->MaxRow());
+ ErrCode eError = DBaseImport( rMedium.GetPhysicalName(),
+ ScGlobal::GetCharsetValue(sItStr), aColWidthParam, aRecalcRanges.maRanges );
+ aRecalcRowRangesArray.push_back(aRecalcRanges);
+
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+
+ aColWidthRange.aStart.SetRow( 1 ); // Except for the column header
+ bSetColWidths = true;
+ bSetSimpleTextColWidths = true;
+ }
+ else if (aFltName == pFilterDif)
+ {
+ SvStream* pStream = rMedium.GetInStream();
+ if (pStream)
+ {
+ ErrCode eError;
+ OUString sItStr;
+ if ( const SfxStringItem* pOptionsItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ sItStr = pOptionsItem->GetValue();
+ }
+
+ if (sItStr.isEmpty())
+ {
+ // default for DIF import (from API without options):
+ // ISO8859-1/MS_1252 encoding
+
+ sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_MS_1252 );
+ }
+
+ eError = ScFormatFilter::Get().ScImportDif( *pStream, m_pDocument.get(), ScAddress(0,0,0),
+ ScGlobal::GetCharsetValue(sItStr));
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+ }
+ bSetColWidths = true;
+ bSetSimpleTextColWidths = true;
+ bSetRowHeights = true;
+ }
+ else if (aFltName == pFilterSylk)
+ {
+ ErrCode eError = SCERR_IMPORT_UNKNOWN;
+ bool bOverflowRow, bOverflowCol, bOverflowCell;
+ bOverflowRow = bOverflowCol = bOverflowCell = false;
+ if( !rMedium.IsStorage() )
+ {
+ ScImportExport aImpEx( *m_pDocument );
+
+ SvStream* pInStream = rMedium.GetInStream();
+ if (pInStream)
+ {
+ pInStream->Seek( 0 );
+ bRet = aImpEx.ImportStream( *pInStream, rMedium.GetBaseURL(), SotClipboardFormatId::SYLK );
+ eError = bRet ? ERRCODE_NONE : SCERR_IMPORT_UNKNOWN;
+ m_pDocument->StartAllListeners();
+ sc::SetFormulaDirtyContext aCxt;
+ m_pDocument->SetAllFormulasDirty(aCxt);
+
+ bOverflowRow = aImpEx.IsOverflowRow();
+ bOverflowCol = aImpEx.IsOverflowCol();
+ bOverflowCell = aImpEx.IsOverflowCell();
+ }
+ else
+ {
+ OSL_FAIL( "No Stream" );
+ }
+ }
+
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else if (!GetErrorIgnoreWarning() && (bOverflowRow || bOverflowCol || bOverflowCell))
+ {
+ // precedence: row, column, cell
+ ErrCode nWarn = (bOverflowRow ? SCWARN_IMPORT_ROW_OVERFLOW :
+ (bOverflowCol ? SCWARN_IMPORT_COLUMN_OVERFLOW :
+ SCWARN_IMPORT_CELL_OVERFLOW));
+ SetError(nWarn);
+ }
+ bSetColWidths = true;
+ bSetSimpleTextColWidths = true;
+ bSetRowHeights = true;
+ }
+ else if (aFltName == pFilterQPro6)
+ {
+ ErrCode eError = ScFormatFilter::Get().ScImportQuattroPro(rMedium.GetInStream(), *m_pDocument);
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+ // TODO: Filter should set column widths. Not doing it here, it may
+ // result in very narrow or wide columns, depending on content.
+ // Setting row heights makes cells with font size attribution or
+ // wrapping enabled look nicer...
+ bSetRowHeights = true;
+ }
+ else if (aFltName == pFilterRtf)
+ {
+ ErrCode eError = SCERR_IMPORT_UNKNOWN;
+ if( !rMedium.IsStorage() )
+ {
+ SvStream* pInStream = rMedium.GetInStream();
+ if (pInStream)
+ {
+ pInStream->Seek( 0 );
+ ScRange aRange;
+ eError = ScFormatFilter::Get().ScImportRTF( *pInStream, rMedium.GetBaseURL(), m_pDocument.get(), aRange );
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+ m_pDocument->StartAllListeners();
+ sc::SetFormulaDirtyContext aCxt;
+ m_pDocument->SetAllFormulasDirty(aCxt);
+ bSetColWidths = true;
+ bSetRowHeights = true;
+ }
+ else
+ {
+ OSL_FAIL( "No Stream" );
+ }
+ }
+
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ }
+ else if (aFltName == pFilterHtml || aFltName == pFilterHtmlWebQ)
+ {
+ ErrCode eError = SCERR_IMPORT_UNKNOWN;
+ bool bWebQuery = aFltName == pFilterHtmlWebQ;
+ if( !rMedium.IsStorage() )
+ {
+ SvStream* pInStream = rMedium.GetInStream();
+ if (pInStream)
+ {
+ LanguageType eLang = LANGUAGE_SYSTEM;
+ bool bDateConvert = false;
+ bool bScientificConvert = true;
+ if ( const SfxStringItem* pOptionsItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ OUString aFilterOption = pOptionsItem->GetValue();
+ lcl_parseHtmlFilterOption(aFilterOption, eLang, bDateConvert, bScientificConvert);
+ }
+
+ pInStream->Seek( 0 );
+ ScRange aRange;
+ // HTML does its own ColWidth/RowHeight
+ CalcOutputFactor();
+ SvNumberFormatter aNumFormatter( comphelper::getProcessComponentContext(), eLang);
+ eError = ScFormatFilter::Get().ScImportHTML( *pInStream, rMedium.GetBaseURL(), m_pDocument.get(), aRange,
+ GetOutputFactor(), !bWebQuery, &aNumFormatter, bDateConvert, bScientificConvert );
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ else
+ bRet = true;
+ m_pDocument->StartAllListeners();
+
+ sc::SetFormulaDirtyContext aCxt;
+ m_pDocument->SetAllFormulasDirty(aCxt);
+ }
+ else
+ {
+ OSL_FAIL( "No Stream" );
+ }
+ }
+
+ if (eError != ERRCODE_NONE)
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if( eError.IsWarning() )
+ bRet = true;
+ }
+ }
+ else
+ {
+ ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
+ if (!pOrcus)
+ return false;
+
+ switch (pOrcus->importByName(*m_pDocument, rMedium, aFltName))
+ {
+ case ScOrcusFilters::ImportResult::Success:
+ bRet = true;
+ break;
+ case ScOrcusFilters::ImportResult::Failure:
+ bRet = false;
+ break;
+ case ScOrcusFilters::ImportResult::NotSupported:
+ {
+ if (!GetErrorIgnoreWarning())
+ {
+ SAL_WARN("sc.filter", "No match for filter '" << aFltName << "' in ConvertFrom");
+ SetError(SCERR_IMPORT_NI);
+ }
+ break;
+ }
+ }
+ }
+
+ if (!bCalc3)
+ m_pDocument->SetInsertingFromOtherDoc( false );
+ }
+ else
+ {
+ OSL_FAIL("No Filter in ConvertFrom");
+ }
+
+ InitItems();
+ CalcOutputFactor();
+ if ( bRet && (bSetColWidths || bSetRowHeights) )
+ { // Adjust column width/row height; base 100% zoom
+ Fraction aZoom( 1, 1 );
+ double nPPTX = ScGlobal::nScreenPPTX * static_cast<double>(aZoom) / GetOutputFactor(); // Factor is printer display ratio
+ double nPPTY = ScGlobal::nScreenPPTY * static_cast<double>(aZoom);
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ // all sheets (for Excel import)
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ {
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ m_pDocument->GetCellArea( nTab, nEndCol, nEndRow );
+ aColWidthRange.aEnd.SetCol( nEndCol );
+ aColWidthRange.aEnd.SetRow( nEndRow );
+ ScMarkData aMark(m_pDocument->GetSheetLimits());
+ aMark.SetMarkArea( aColWidthRange );
+ aMark.MarkToMulti();
+
+ // Order is important: First width, then height
+ if ( bSetColWidths )
+ {
+ for ( SCCOL nCol=0; nCol <= nEndCol; nCol++ )
+ {
+ if (!bSetSimpleTextColWidths)
+ aColWidthParam[nCol].mbSimpleText = false;
+
+ sal_uInt16 nWidth = m_pDocument->GetOptimalColWidth(
+ nCol, nTab, pVirtDev, nPPTX, nPPTY, aZoom, aZoom, false, &aMark,
+ &aColWidthParam[nCol] );
+ m_pDocument->SetColWidth( nCol, nTab,
+ nWidth + static_cast<sal_uInt16>(ScGlobal::nLastColWidthExtra) );
+ }
+ }
+ }
+
+ if (bSetRowHeights)
+ {
+ // Update all rows in all tables.
+ ScSizeDeviceProvider aProv(this);
+ ScDocRowHeightUpdater aUpdater(*m_pDocument, aProv.GetDevice(), aProv.GetPPTX(), aProv.GetPPTY(), nullptr);
+ aUpdater.update();
+ }
+ else if (!aRecalcRowRangesArray.empty())
+ {
+ // Update only specified row ranges for better performance.
+ ScSizeDeviceProvider aProv(this);
+ ScDocRowHeightUpdater aUpdater(*m_pDocument, aProv.GetDevice(), aProv.GetPPTX(), aProv.GetPPTY(), &aRecalcRowRangesArray);
+ aUpdater.update();
+ }
+ }
+ FinishedLoading();
+
+ // invalidate eventually temporary table areas
+ if ( bRet )
+ m_pDocument->InvalidateTableArea();
+
+ m_bIsEmpty = false;
+
+ return bRet;
+}
+
+bool ScDocShell::LoadExternal( SfxMedium& rMed )
+{
+ std::shared_ptr<const SfxFilter> pFilter = rMed.GetFilter();
+ if (!pFilter)
+ return false;
+
+ if (pFilter->GetProviderName() == "orcus")
+ {
+ ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
+ if (!pOrcus)
+ return false;
+
+ auto res = pOrcus->importByName(*m_pDocument, rMed, pFilter->GetName());
+ if (res != ScOrcusFilters::ImportResult::Success)
+ return false;
+
+ FinishedLoading();
+ return true;
+ }
+
+ return false;
+}
+
+ScDocShell::PrepareSaveGuard::PrepareSaveGuard( ScDocShell& rDocShell )
+ : mrDocShell( rDocShell)
+{
+ // DoEnterHandler not here (because of AutoSave), is in ExecuteSave.
+
+ ScChartListenerCollection* pCharts = mrDocShell.m_pDocument->GetChartListenerCollection();
+ if (pCharts)
+ pCharts->UpdateDirtyCharts(); // Charts to be updated.
+ mrDocShell.m_pDocument->StopTemporaryChartLock();
+ if (mrDocShell.m_pAutoStyleList)
+ mrDocShell.m_pAutoStyleList->ExecuteAllNow(); // Execute template timeouts now.
+ if (mrDocShell.m_pDocument->HasExternalRefManager())
+ {
+ ScExternalRefManager* pRefMgr = mrDocShell.m_pDocument->GetExternalRefManager();
+ if (pRefMgr && pRefMgr->hasExternalData())
+ {
+ pRefMgr->setAllCacheTableReferencedStati( false);
+ mrDocShell.m_pDocument->MarkUsedExternalReferences(); // Mark tables of external references to be written.
+ }
+ }
+ if (mrDocShell.GetCreateMode()== SfxObjectCreateMode::STANDARD)
+ mrDocShell.SfxObjectShell::SetVisArea( tools::Rectangle() ); // "Normally" worked on => no VisArea.
+}
+
+ScDocShell::PrepareSaveGuard::~PrepareSaveGuard() COVERITY_NOEXCEPT_FALSE
+{
+ if (mrDocShell.m_pDocument->HasExternalRefManager())
+ {
+ ScExternalRefManager* pRefMgr = mrDocShell.m_pDocument->GetExternalRefManager();
+ if (pRefMgr && pRefMgr->hasExternalData())
+ {
+ // Prevent accidental data loss due to lack of knowledge.
+ pRefMgr->setAllCacheTableReferencedStati( true);
+ }
+ }
+}
+
+bool ScDocShell::Save()
+{
+ ScRefreshTimerProtector aProt( m_pDocument->GetRefreshTimerControlAddress() );
+
+ PrepareSaveGuard aPrepareGuard( *this);
+
+ if (const auto pFrame1 = SfxViewFrame::GetFirst(this))
+ {
+ if (auto pSysWin = pFrame1->GetWindow().GetSystemWindow())
+ {
+ pSysWin->SetAccessibleName(OUString());
+ }
+ }
+ // wait cursor is handled with progress bar
+ bool bRet = SfxObjectShell::Save();
+ if( bRet )
+ bRet = SaveXML( GetMedium(), nullptr );
+ return bRet;
+}
+
+namespace {
+
+/**
+ * Remove the file name from the full path, to keep only the directory path.
+ */
+void popFileName(OUString& rPath)
+{
+ if (!rPath.isEmpty())
+ {
+ INetURLObject aURLObj(rPath);
+ aURLObj.removeSegment();
+ rPath = aURLObj.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+}
+
+}
+
+void ScDocShell::TerminateEditing()
+{
+ // Commit any cell changes before saving.
+ SC_MOD()->InputEnterHandler();
+}
+
+bool ScDocShell::SaveAs( SfxMedium& rMedium )
+{
+ OUString aCurPath; // empty for new document that hasn't been saved.
+ const SfxMedium* pCurMedium = GetMedium();
+ if (pCurMedium)
+ {
+ aCurPath = pCurMedium->GetName();
+ popFileName(aCurPath);
+ }
+
+ if (!aCurPath.isEmpty())
+ {
+ // current document has a path -> not a brand-new document.
+ OUString aNewPath = rMedium.GetName();
+ popFileName(aNewPath);
+ OUString aRel = URIHelper::simpleNormalizedMakeRelative(aCurPath, aNewPath);
+ if (!aRel.isEmpty())
+ {
+ // Directory path will change before and after the save.
+ m_pDocument->InvalidateStreamOnSave();
+ }
+ }
+
+ ScTabViewShell* pViewShell = GetBestViewShell();
+ bool bNeedsRehash = ScPassHashHelper::needsPassHashRegen(*m_pDocument, PASSHASH_SHA1);
+ if (bNeedsRehash)
+ // legacy xls hash double-hashed by SHA1 is also supported.
+ bNeedsRehash = ScPassHashHelper::needsPassHashRegen(*m_pDocument, PASSHASH_XL, PASSHASH_SHA1);
+ if (bNeedsRehash)
+ { // SHA256 explicitly supported in ODF 1.2, implicitly in ODF 1.1
+ bNeedsRehash = ScPassHashHelper::needsPassHashRegen(*m_pDocument, PASSHASH_SHA256);
+ }
+
+ if (pViewShell && bNeedsRehash)
+ {
+ bool bAutoSaveEvent = false;
+ utl::MediaDescriptor lArgs(rMedium.GetArgs());
+ lArgs[utl::MediaDescriptor::PROP_AUTOSAVEEVENT] >>= bAutoSaveEvent;
+ if (bAutoSaveEvent)
+ {
+ // skip saving recovery file instead of showing re-type password dialog window
+ SAL_WARN("sc.filter",
+ "Should re-type password for own format, won't export recovery file");
+ rMedium.SetError(ERRCODE_SFX_WRONGPASSWORD);
+ return false;
+ }
+
+ if (!pViewShell->ExecuteRetypePassDlg(PASSHASH_SHA1))
+ // password re-type cancelled. Don't save the document.
+ return false;
+ }
+
+ ScRefreshTimerProtector aProt( m_pDocument->GetRefreshTimerControlAddress() );
+
+ PrepareSaveGuard aPrepareGuard( *this);
+
+ // wait cursor is handled with progress bar
+ bool bRet = SfxObjectShell::SaveAs( rMedium );
+ if (bRet)
+ bRet = SaveXML( &rMedium, nullptr );
+
+ return bRet;
+}
+
+namespace {
+
+// Xcl-like column width measured in characters of standard font.
+sal_Int32 lcl_ScDocShell_GetColWidthInChars( sal_uInt16 nWidth )
+{
+ double f = nWidth;
+ f *= 1328.0 / 25.0;
+ f += 90.0;
+ f *= 1.0 / 23.0;
+ f /= 256.0;
+
+ return sal_Int32( f );
+}
+
+void lcl_ScDocShell_GetFixedWidthString( OUString& rStr, const ScDocument& rDoc,
+ SCTAB nTab, SCCOL nCol, bool bValue, SvxCellHorJustify eHorJust )
+{
+ OUString aString = rStr;
+ sal_Int32 nLen = lcl_ScDocShell_GetColWidthInChars(
+ rDoc.GetColWidth( nCol, nTab ) );
+ //If the text won't fit in the column
+ if ( nLen < aString.getLength() )
+ {
+ OUStringBuffer aReplacement;
+ if (bValue)
+ aReplacement.append("###");
+ else
+ aReplacement.append(aString);
+ //truncate to the number of characters that should fit, even in the
+ //bValue case nLen might be < len ###
+ aString = comphelper::string::truncateToLength(aReplacement, nLen).makeStringAndClear();
+ }
+ if ( nLen > aString.getLength() )
+ {
+ if ( bValue && eHorJust == SvxCellHorJustify::Standard )
+ eHorJust = SvxCellHorJustify::Right;
+ OUStringBuffer aTmp(nLen);
+ switch ( eHorJust )
+ {
+ case SvxCellHorJustify::Right:
+ comphelper::string::padToLength( aTmp, nLen - aString.getLength(), ' ' );
+ aString = aTmp.append(aString);
+ break;
+ case SvxCellHorJustify::Center:
+ comphelper::string::padToLength( aTmp, (nLen - aString.getLength()) / 2, ' ' );
+ [[fallthrough]];
+ default:
+ aTmp.append(aString);
+ comphelper::string::padToLength( aTmp, nLen, ' ' );
+ }
+ aString = aTmp.makeStringAndClear();
+ }
+ rStr = aString;
+}
+
+void lcl_ScDocShell_WriteEmptyFixedWidthString( SvStream& rStream,
+ const ScDocument& rDoc, SCTAB nTab, SCCOL nCol )
+{
+ OUString aString;
+ lcl_ScDocShell_GetFixedWidthString( aString, rDoc, nTab, nCol, false,
+ SvxCellHorJustify::Standard );
+ rStream.WriteUnicodeOrByteText( aString );
+}
+
+template<typename StrT, typename SepCharT>
+sal_Int32 getTextSepPos(
+ const StrT& rStr, const ScImportOptions& rAsciiOpt, const SepCharT& rTextSep, const SepCharT& rFieldSep, bool& rNeedQuotes)
+{
+ // #i116636# quotes are needed if text delimiter (quote), field delimiter,
+ // or LF or CR is in the cell text.
+ sal_Int32 nPos = rStr.indexOf(rTextSep);
+ rNeedQuotes = rAsciiOpt.bQuoteAllText || (nPos >= 0) ||
+ (rStr.indexOf(rFieldSep) >= 0) ||
+ (rStr.indexOf('\n') >= 0) ||
+ (rStr.indexOf('\r') >= 0);
+ return nPos;
+}
+
+}
+
+void ScDocShell::AsciiSave( SvStream& rStream, const ScImportOptions& rAsciiOpt, SCTAB nTab )
+{
+ sal_Unicode cDelim = rAsciiOpt.nFieldSepCode;
+ sal_Unicode cStrDelim = rAsciiOpt.nTextSepCode;
+ rtl_TextEncoding eCharSet = rAsciiOpt.eCharSet;
+ bool bFixedWidth = rAsciiOpt.bFixedWidth;
+ bool bSaveNumberAsSuch = rAsciiOpt.bSaveNumberAsSuch;
+ bool bSaveAsShown = rAsciiOpt.bSaveAsShown;
+ bool bShowFormulas = rAsciiOpt.bSaveFormulas;
+ bool bIncludeBOM = rAsciiOpt.bIncludeBOM;
+
+ rtl_TextEncoding eOldCharSet = rStream.GetStreamCharSet();
+ rStream.SetStreamCharSet( eCharSet );
+ SvStreamEndian nOldNumberFormatInt = rStream.GetEndian();
+ OString aStrDelimEncoded; // only used if not Unicode
+ OUString aStrDelimDecoded; // only used if context encoding
+ OString aDelimEncoded;
+ OUString aDelimDecoded;
+ bool bContextOrNotAsciiEncoding;
+ if ( eCharSet == RTL_TEXTENCODING_UNICODE )
+ {
+ rStream.StartWritingUnicodeText();
+ bContextOrNotAsciiEncoding = false;
+ }
+ else
+ {
+ // tdf#82254 - check whether to include a byte-order-mark in the output
+ if (bIncludeBOM && eCharSet == RTL_TEXTENCODING_UTF8)
+ rStream.WriteUChar(0xEF).WriteUChar(0xBB).WriteUChar(0xBF);
+ aStrDelimEncoded = OString(&cStrDelim, 1, eCharSet);
+ aDelimEncoded = OString(&cDelim, 1, eCharSet);
+ rtl_TextEncodingInfo aInfo;
+ aInfo.StructSize = sizeof(aInfo);
+ if ( rtl_getTextEncodingInfo( eCharSet, &aInfo ) )
+ {
+ bContextOrNotAsciiEncoding =
+ (((aInfo.Flags & RTL_TEXTENCODING_INFO_CONTEXT) != 0) ||
+ ((aInfo.Flags & RTL_TEXTENCODING_INFO_ASCII) == 0));
+ if ( bContextOrNotAsciiEncoding )
+ {
+ aStrDelimDecoded = OStringToOUString(aStrDelimEncoded, eCharSet);
+ aDelimDecoded = OStringToOUString(aDelimEncoded, eCharSet);
+ }
+ }
+ else
+ bContextOrNotAsciiEncoding = false;
+ }
+
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ m_pDocument->GetCellArea( nTab, nEndCol, nEndRow );
+
+ ScProgress aProgress( this, ScResId( STR_SAVE_DOC ), nEndRow, true );
+
+ OUString aString;
+
+ bool bTabProtect = m_pDocument->IsTabProtected( nTab );
+
+ SCCOL nCol;
+ SCROW nRow;
+
+ // Treat the top left cell separator "sep=" special.
+ // Here nStartRow == 0 && nStartCol == 0
+ if (!bFixedWidth && cDelim != 0)
+ {
+ // First row iterator.
+ ScHorizontalCellIterator aIter( *m_pDocument, nTab, nStartCol, nStartRow, nEndCol, nStartRow);
+ ScRefCellValue* pCell;
+ // Must be first column and all following cells on this row must be
+ // empty to fiddle with "sep=".
+ if ((pCell = aIter.GetNext( nCol, nRow)) != nullptr && nCol == nStartCol && !aIter.GetNext( nCol, nRow))
+ {
+ if (pCell->getType() == CELLTYPE_STRING)
+ {
+ aString = pCell->getSharedString()->getString();
+ if (aString.getLength() <= 5 && aString.startsWithIgnoreAsciiCase("sep="))
+ {
+ // Cell content is /^sep=.?$/ so write current separator.
+ // Force the quote character to '"' regardless what is set
+ // for export because that is the only one recognized on
+ // import.
+ aString = "sep=" + OUStringChar(cDelim);
+ if (cStrDelim != 0)
+ rStream.WriteUniOrByteChar( '"', eCharSet);
+ if (eCharSet == RTL_TEXTENCODING_UNICODE)
+ {
+ write_uInt16s_FromOUString( rStream, aString);
+ }
+ else
+ {
+ OString aStrEnc = OUStringToOString( aString, eCharSet);
+ // write byte encoded
+ rStream.WriteBytes( aStrEnc.getStr(), aStrEnc.getLength());
+ }
+ if (cStrDelim != 0)
+ rStream.WriteUniOrByteChar( '"', eCharSet);
+ endlub( rStream );
+ ++nStartRow;
+ }
+ }
+ }
+ }
+
+ SCCOL nNextCol = nStartCol;
+ SCROW nNextRow = nStartRow;
+ SCCOL nEmptyCol;
+ SCROW nEmptyRow;
+ SvNumberFormatter& rFormatter = *m_pDocument->GetFormatTable();
+
+ ScHorizontalCellIterator aIter( *m_pDocument, nTab, nStartCol, nStartRow,
+ nEndCol, nEndRow );
+ ScRefCellValue* pCell;
+ while ( ( pCell = aIter.GetNext( nCol, nRow ) ) != nullptr )
+ {
+ bool bProgress = false; // only upon line change
+ if ( nNextRow < nRow )
+ { // empty rows or/and empty columns up to end of row
+ bProgress = true;
+ for ( nEmptyCol = nNextCol; nEmptyCol < nEndCol; nEmptyCol++ )
+ { // remaining columns of last row
+ if ( bFixedWidth )
+ lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
+ *m_pDocument, nTab, nEmptyCol );
+ else if ( cDelim != 0 )
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ endlub( rStream );
+ nNextRow++;
+ for ( nEmptyRow = nNextRow; nEmptyRow < nRow; nEmptyRow++ )
+ { // completely empty rows
+ for ( nEmptyCol = nStartCol; nEmptyCol < nEndCol; nEmptyCol++ )
+ {
+ if ( bFixedWidth )
+ lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
+ *m_pDocument, nTab, nEmptyCol );
+ else if ( cDelim != 0 )
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ endlub( rStream );
+ }
+ for ( nEmptyCol = nStartCol; nEmptyCol < nCol; nEmptyCol++ )
+ { // empty columns at beginning of row
+ if ( bFixedWidth )
+ lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
+ *m_pDocument, nTab, nEmptyCol );
+ else if ( cDelim != 0 )
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ nNextRow = nRow;
+ }
+ else if ( nNextCol < nCol )
+ { // empty columns in same row
+ for ( nEmptyCol = nNextCol; nEmptyCol < nCol; nEmptyCol++ )
+ { // columns in between
+ if ( bFixedWidth )
+ lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
+ *m_pDocument, nTab, nEmptyCol );
+ else if ( cDelim != 0 )
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ }
+ if ( nCol == nEndCol )
+ {
+ bProgress = true;
+ nNextCol = nStartCol;
+ nNextRow = nRow + 1;
+ }
+ else
+ nNextCol = nCol + 1;
+
+ CellType eType = pCell->getType();
+ ScAddress aPos(nCol, nRow, nTab);
+ if ( bTabProtect )
+ {
+ const ScProtectionAttr* pProtAttr =
+ m_pDocument->GetAttr( nCol, nRow, nTab, ATTR_PROTECTION );
+ if ( pProtAttr->GetHideCell() ||
+ ( eType == CELLTYPE_FORMULA && bShowFormulas &&
+ pProtAttr->GetHideFormula() ) )
+ eType = CELLTYPE_NONE; // hide
+ }
+ bool bForceQuotes = false;
+ bool bString;
+ switch ( eType )
+ {
+ case CELLTYPE_NONE:
+ aString.clear();
+ bString = false;
+ break;
+ case CELLTYPE_FORMULA :
+ {
+ FormulaError nErrCode;
+ if ( bShowFormulas )
+ {
+ aString = pCell->getFormula()->GetFormula();
+ bString = true;
+ }
+ else if ((nErrCode = pCell->getFormula()->GetErrCode()) != FormulaError::NONE)
+ {
+ aString = ScGlobal::GetErrorString( nErrCode );
+ bString = true;
+ }
+ else if (pCell->getFormula()->IsValue())
+ {
+ sal_uInt32 nFormat = m_pDocument->GetNumberFormat(aPos);
+ if ( bFixedWidth || bSaveAsShown )
+ {
+ const Color* pDummy;
+ aString = ScCellFormat::GetString(*pCell, nFormat, &pDummy, rFormatter, *m_pDocument);
+ bString = bSaveAsShown && rFormatter.IsTextFormat( nFormat);
+ }
+ else
+ {
+ aString = ScCellFormat::GetInputString(*pCell, nFormat, rFormatter, *m_pDocument);
+ bString = bForceQuotes = !bSaveNumberAsSuch;
+ }
+ }
+ else
+ {
+ if ( bSaveAsShown )
+ {
+ sal_uInt32 nFormat = m_pDocument->GetNumberFormat(aPos);
+ const Color* pDummy;
+ aString = ScCellFormat::GetString(*pCell, nFormat, &pDummy, rFormatter, *m_pDocument);
+ }
+ else
+ aString = pCell->getFormula()->GetString().getString();
+ bString = true;
+ }
+ }
+ break;
+ case CELLTYPE_STRING :
+ if ( bSaveAsShown )
+ {
+ sal_uInt32 nFormat = m_pDocument->GetNumberFormat(aPos);
+ const Color* pDummy;
+ aString = ScCellFormat::GetString(*pCell, nFormat, &pDummy, rFormatter, *m_pDocument);
+ }
+ else
+ aString = pCell->getSharedString()->getString();
+ bString = true;
+ break;
+ case CELLTYPE_EDIT :
+ {
+ const EditTextObject* pObj = pCell->getEditText();
+ EditEngine& rEngine = m_pDocument->GetEditEngine();
+ rEngine.SetText( *pObj);
+ aString = rEngine.GetText(); // including LF
+ bString = true;
+ }
+ break;
+ case CELLTYPE_VALUE :
+ {
+ sal_uInt32 nFormat = m_pDocument->GetNumberFormat( nCol, nRow, nTab );
+ if ( bFixedWidth || bSaveAsShown )
+ {
+ const Color* pDummy;
+ aString = ScCellFormat::GetString(*pCell, nFormat, &pDummy, rFormatter, *m_pDocument);
+ bString = bSaveAsShown && rFormatter.IsTextFormat( nFormat);
+ }
+ else
+ {
+ aString = ScCellFormat::GetInputString(*pCell, nFormat, rFormatter, *m_pDocument);
+ bString = bForceQuotes = !bSaveNumberAsSuch;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "ScDocShell::AsciiSave: unknown CellType" );
+ aString.clear();
+ bString = false;
+ }
+
+ if ( bFixedWidth )
+ {
+ SvxCellHorJustify eHorJust =
+ m_pDocument->GetAttr( nCol, nRow, nTab, ATTR_HOR_JUSTIFY )->GetValue();
+ lcl_ScDocShell_GetFixedWidthString( aString, *m_pDocument, nTab, nCol,
+ !bString, eHorJust );
+ rStream.WriteUnicodeOrByteText( aString );
+ }
+ else
+ {
+ OUString aUniString = aString;// TODO: remove that later
+ if (!bString && cStrDelim != 0 && !aUniString.isEmpty())
+ {
+ sal_Unicode c = aUniString[0];
+ bString = (c == cStrDelim || c == ' ' ||
+ aUniString.endsWith(" ") ||
+ aUniString.indexOf(cStrDelim) >= 0);
+ if (!bString && cDelim != 0)
+ bString = (aUniString.indexOf(cDelim) >= 0);
+ }
+ if ( bString )
+ {
+ if ( cStrDelim != 0 ) //@ BugId 55355
+ {
+ if ( eCharSet == RTL_TEXTENCODING_UNICODE )
+ {
+ bool bNeedQuotes = false;
+ sal_Int32 nPos = getTextSepPos(aUniString, rAsciiOpt, cStrDelim, cDelim, bNeedQuotes);
+ if (nPos >= 0)
+ {
+ OUString strFrom(cStrDelim);
+ OUString strTo = strFrom + strFrom;
+ aUniString = aUniString.replaceAll(strFrom, strTo);
+ }
+
+ if ( bNeedQuotes || bForceQuotes )
+ rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
+ write_uInt16s_FromOUString(rStream, aUniString);
+ if ( bNeedQuotes || bForceQuotes )
+ rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
+ }
+ else
+ {
+ // This is nasty. The Unicode to byte encoding
+ // may convert typographical quotation marks to ASCII
+ // quotation marks, which may interfere with the delimiter,
+ // so we have to escape delimiters after the string has
+ // been encoded. Since this may happen also with UTF-8
+ // encoded typographical quotation marks if such was
+ // specified as a delimiter we have to check for the full
+ // encoded delimiter string, not just one character.
+ // Now for RTL_TEXTENCODING_ISO_2022_... and similar brain
+ // dead encodings where one code point (and especially a
+ // low ASCII value) may represent different characters, we
+ // have to convert forth and back and forth again. Same for
+ // UTF-7 since it is a context sensitive encoding too.
+
+ if ( bContextOrNotAsciiEncoding )
+ {
+ // to byte encoding
+ OString aStrEnc = OUStringToOString(aUniString, eCharSet);
+ // back to Unicode
+ OUString aStrDec = OStringToOUString(aStrEnc, eCharSet);
+
+ // search on re-decoded string
+ bool bNeedQuotes = false;
+ sal_Int32 nPos = getTextSepPos(aStrDec, rAsciiOpt, aStrDelimDecoded, aDelimDecoded, bNeedQuotes);
+ if (nPos >= 0)
+ {
+ OUString strTo = aStrDelimDecoded + aStrDelimDecoded;
+ aStrDec = aStrDec.replaceAll(aStrDelimDecoded, strTo);
+ }
+
+ // write byte re-encoded
+ if ( bNeedQuotes || bForceQuotes )
+ rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
+ rStream.WriteUnicodeOrByteText( aStrDec, eCharSet );
+ if ( bNeedQuotes || bForceQuotes )
+ rStream.WriteUniOrByteChar( cStrDelim, eCharSet );
+ }
+ else
+ {
+ OString aStrEnc = OUStringToOString(aUniString, eCharSet);
+
+ // search on encoded string
+ bool bNeedQuotes = false;
+ sal_Int32 nPos = getTextSepPos(aStrEnc, rAsciiOpt, aStrDelimEncoded, aDelimEncoded, bNeedQuotes);
+ if (nPos >= 0)
+ {
+ OString strTo = aStrDelimEncoded + aStrDelimEncoded;
+ aStrEnc = aStrEnc.replaceAll(aStrDelimEncoded, strTo);
+ }
+
+ // write byte encoded
+ if ( bNeedQuotes || bForceQuotes )
+ rStream.WriteBytes(
+ aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
+ rStream.WriteBytes(aStrEnc.getStr(), aStrEnc.getLength());
+ if ( bNeedQuotes || bForceQuotes )
+ rStream.WriteBytes(
+ aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
+ }
+ }
+ }
+ else
+ rStream.WriteUnicodeOrByteText( aUniString );
+ }
+ else
+ rStream.WriteUnicodeOrByteText( aUniString );
+ }
+
+ if( nCol < nEndCol )
+ {
+ if(cDelim!=0) //@ BugId 55355
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ else
+ endlub( rStream );
+
+ if ( bProgress )
+ aProgress.SetStateOnPercent( nRow );
+ }
+
+ // write out empty if requested
+ if ( nNextRow <= nEndRow )
+ {
+ for ( nEmptyCol = nNextCol; nEmptyCol < nEndCol; nEmptyCol++ )
+ { // remaining empty columns of last row
+ if ( bFixedWidth )
+ lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
+ *m_pDocument, nTab, nEmptyCol );
+ else if ( cDelim != 0 )
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ endlub( rStream );
+ nNextRow++;
+ }
+ for ( nEmptyRow = nNextRow; nEmptyRow <= nEndRow; nEmptyRow++ )
+ { // entire empty rows
+ for ( nEmptyCol = nStartCol; nEmptyCol < nEndCol; nEmptyCol++ )
+ {
+ if ( bFixedWidth )
+ lcl_ScDocShell_WriteEmptyFixedWidthString( rStream,
+ *m_pDocument, nTab, nEmptyCol );
+ else if ( cDelim != 0 )
+ rStream.WriteUniOrByteChar( cDelim );
+ }
+ endlub( rStream );
+ }
+
+ rStream.SetStreamCharSet( eOldCharSet );
+ rStream.SetEndian( nOldNumberFormatInt );
+}
+
+bool ScDocShell::ConvertTo( SfxMedium &rMed )
+{
+ ScRefreshTimerProtector aProt( m_pDocument->GetRefreshTimerControlAddress() );
+
+ // #i6500# don't call DoEnterHandler here (doesn't work with AutoSave),
+ // it's already in ExecuteSave (as for Save and SaveAs)
+
+ if (m_pAutoStyleList)
+ m_pAutoStyleList->ExecuteAllNow(); // Execute template timeouts now
+ if (GetCreateMode()== SfxObjectCreateMode::STANDARD)
+ SfxObjectShell::SetVisArea( tools::Rectangle() ); // Edited normally -> no VisArea
+
+ OSL_ENSURE( rMed.GetFilter(), "Filter == 0" );
+
+ bool bRet = false;
+ OUString aFltName = rMed.GetFilter()->GetFilterName();
+
+ if (aFltName == pFilterXML)
+ {
+ //TODO/LATER: this shouldn't happen!
+ OSL_FAIL("XML filter in ConvertFrom?!");
+ bRet = SaveXML( &rMed, nullptr );
+ }
+ else if (aFltName == pFilterExcel5 || aFltName == pFilterExcel95 ||
+ aFltName == pFilterExcel97 || aFltName == pFilterEx5Temp ||
+ aFltName == pFilterEx95Temp || aFltName == pFilterEx97Temp)
+ {
+ weld::WaitObject aWait( GetActiveDialogParent() );
+
+ bool bDoSave = true;
+ if( ScTabViewShell* pViewShell = GetBestViewShell() )
+ {
+ ScExtDocOptions* pExtDocOpt = m_pDocument->GetExtDocOptions();
+ if( !pExtDocOpt )
+ {
+ m_pDocument->SetExtDocOptions( std::make_unique<ScExtDocOptions>() );
+ pExtDocOpt = m_pDocument->GetExtDocOptions();
+ }
+ pViewShell->GetViewData().WriteExtOptions( *pExtDocOpt );
+
+ /* #i104990# If the imported document contains a medium
+ password, determine if we can save it, otherwise ask the users
+ whether they want to save without it. */
+ if( (rMed.GetFilter()->GetFilterFlags() & SfxFilterFlags::ENCRYPTION) == SfxFilterFlags::NONE )
+ {
+ SfxItemSet& rItemSet = rMed.GetItemSet();
+ if( rItemSet.GetItemState( SID_PASSWORD ) == SfxItemState::SET )
+ {
+ bDoSave = ScWarnPassword::WarningOnPassword( rMed );
+ // #i42858# remove password from medium (warn only one time)
+ if( bDoSave )
+ rItemSet.ClearItem( SID_PASSWORD );
+ }
+ }
+
+ if( bDoSave )
+ {
+ bool bNeedRetypePassDlg = ScPassHashHelper::needsPassHashRegen( *m_pDocument, PASSHASH_XL );
+ bDoSave = !bNeedRetypePassDlg || pViewShell->ExecuteRetypePassDlg( PASSHASH_XL );
+ }
+ }
+
+ if( bDoSave )
+ {
+ ExportFormatExcel eFormat = ExpBiff5;
+ if( aFltName == pFilterExcel97 || aFltName == pFilterEx97Temp )
+ eFormat = ExpBiff8;
+ ErrCode eError = ScFormatFilter::Get().ScExportExcel5( rMed, m_pDocument.get(), eFormat, RTL_TEXTENCODING_MS_1252 );
+
+ if( eError && !GetErrorIgnoreWarning() )
+ SetError(eError);
+
+ // don't return false for warnings
+ bRet = eError.IsWarning() || (eError == ERRCODE_NONE);
+ }
+ else
+ {
+ // export aborted, i.e. "Save without password" warning
+ SetError(ERRCODE_ABORT);
+ }
+ }
+ else if (aFltName == SC_TEXT_CSV_FILTER_NAME)
+ {
+ OUString sItStr;
+ if ( const SfxStringItem* pOptionsItem = rMed.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ sItStr = pOptionsItem->GetValue();
+ }
+
+ if ( sItStr.isEmpty() )
+ {
+ // default for ascii export (from API without options):
+ // UTF-8 encoding, comma, double quotes
+
+ ScImportOptions aDefOptions(',', '"', RTL_TEXTENCODING_UTF8);
+ sItStr = aDefOptions.BuildString();
+ }
+
+ weld::WaitObject aWait( GetActiveDialogParent() );
+ ScImportOptions aOptions( sItStr );
+
+ if (aOptions.nSheetToExport)
+ {
+ // Only from command line --convert-to
+ bRet = true;
+
+ // Verbose only from command line, not UI (in case we actually
+ // implement that) nor macro filter options.
+ bool bVerbose = false;
+ const css::uno::Sequence<css::beans::PropertyValue> & rArgs = rMed.GetArgs();
+ const auto pProp = std::find_if( rArgs.begin(), rArgs.end(),
+ [](const css::beans::PropertyValue& rProp) { return rProp.Name == "ConversionRequestOrigin"; });
+ if (pProp != rArgs.end())
+ {
+ OUString aOrigin;
+ pProp->Value >>= aOrigin;
+ bVerbose = (aOrigin == "CommandLine");
+ }
+
+ SCTAB nStartTab;
+ SCTAB nCount = m_pDocument->GetTableCount();
+ if (aOptions.nSheetToExport == -1)
+ {
+ // All sheets.
+ nStartTab = 0;
+ }
+ else if (0 < aOptions.nSheetToExport && aOptions.nSheetToExport <= nCount)
+ {
+ // One sheet, 1-based.
+ nCount = aOptions.nSheetToExport;
+ nStartTab = nCount - 1;
+ }
+ else
+ {
+ // Usage error, no export but log.
+ if (bVerbose)
+ {
+ if (aOptions.nSheetToExport < 0)
+ std::cout << "Bad sheet number string given." << std::endl;
+ else
+ std::cout << "No sheet number " << aOptions.nSheetToExport
+ << ", number of sheets is " << nCount << std::endl;
+ }
+ nStartTab = 0;
+ nCount = 0;
+ SetError(SCERR_EXPORT_DATA);
+ bRet = false;
+ }
+
+ INetURLObject aURLObject(rMed.GetURLObject());
+ OUString sExt = aURLObject.CutExtension();
+ OUString sBaseName = aURLObject.GetLastName();
+ aURLObject.CutLastName();
+
+ for (SCTAB i = nStartTab; i < nCount; ++i)
+ {
+ OUString sTabName;
+ if (!m_pDocument->GetName(i, sTabName))
+ sTabName = OUString::number(i);
+ INetURLObject aSheetURLObject(aURLObject);
+ OUString sFileName = sBaseName + "-" + sTabName;
+ if (!sExt.isEmpty())
+ sFileName = sFileName + "." + sExt;
+ aSheetURLObject.Append(sFileName);
+
+ // log similar to DispatchWatcher::executeDispatchRequests
+ OUString aOutFile = aSheetURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ if (bVerbose)
+ {
+ OUString aDisplayedName;
+ if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(aOutFile, aDisplayedName))
+ aDisplayedName = aOutFile;
+ std::cout << "Writing sheet " << OUStringToOString(sTabName, osl_getThreadTextEncoding()) << " -> "
+ << OUStringToOString(aDisplayedName, osl_getThreadTextEncoding())
+ << std::endl;
+
+ if (FStatHelper::IsDocument(aOutFile))
+ std::cout << "Overwriting: " << OUStringToOString(aDisplayedName, osl_getThreadTextEncoding())
+ << std::endl ;
+ }
+
+ std::unique_ptr<SvStream> xStm = ::utl::UcbStreamHelper::CreateStream(aOutFile, StreamMode::TRUNC | StreamMode::WRITE);
+ if (!xStm)
+ {
+ SetError(ERRCODE_IO_CANTCREATE);
+ bRet = false;
+ break;
+ }
+ AsciiSave(*xStm, aOptions, i);
+ }
+ }
+ else
+ {
+ SvStream* pStream = rMed.GetOutStream();
+ if (pStream)
+ {
+ AsciiSave(*pStream, aOptions, GetSaveTab());
+ bRet = true;
+
+ if (m_pDocument->GetTableCount() > 1)
+ if (!rMed.GetErrorIgnoreWarning())
+ rMed.SetError(SCWARN_EXPORT_ASCII);
+ }
+ }
+ }
+ else if (aFltName == pFilterDBase)
+ {
+ OUString sCharSet;
+ if ( const SfxStringItem* pOptionsItem = rMed.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ sCharSet = pOptionsItem->GetValue();
+ }
+
+ if (sCharSet.isEmpty())
+ {
+ // default for dBase export (from API without options):
+ // IBM_850 encoding
+
+ sCharSet = ScGlobal::GetCharsetString( RTL_TEXTENCODING_IBM_850 );
+ }
+
+ weld::WaitObject aWait( GetActiveDialogParent() );
+ // Hack so that Sba can overwrite the opened TempFile.
+ rMed.CloseOutStream();
+ bool bHasMemo = false;
+
+ ErrCodeMsg eError = DBaseExport(
+ rMed.GetPhysicalName(), ScGlobal::GetCharsetValue(sCharSet), bHasMemo);
+
+ INetURLObject aTmpFile( rMed.GetPhysicalName(), INetProtocol::File );
+ if ( bHasMemo )
+ aTmpFile.setExtension(u"dbt");
+ if ( eError != ERRCODE_NONE && !eError.IsWarning() )
+ {
+ if (!GetErrorIgnoreWarning())
+ SetError(eError);
+ if ( bHasMemo && IsDocument( aTmpFile ) )
+ KillFile( aTmpFile );
+ }
+ else
+ {
+ bRet = true;
+ if ( bHasMemo )
+ {
+ const SfxStringItem* pNameItem = rMed.GetItemSet().GetItem<SfxStringItem>( SID_FILE_NAME );
+ assert(pNameItem && "SID_FILE_NAME is required");
+ INetURLObject aDbtFile( pNameItem->GetValue(), INetProtocol::File );
+ aDbtFile.setExtension(u"dbt");
+
+ // tdf#40713: don't lose dbt file
+ // if aDbtFile corresponds exactly to aTmpFile, we just have to return
+ if (aDbtFile.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ==
+ aTmpFile.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ))
+ {
+ if (eError != ERRCODE_NONE && !GetErrorIgnoreWarning())
+ SetError(eError);
+ return bRet;
+ }
+
+ if ( IsDocument( aDbtFile ) && !KillFile( aDbtFile ) )
+ bRet = false;
+ if ( bRet && !MoveFile( aTmpFile, aDbtFile ) )
+ bRet = false;
+ if ( !bRet )
+ {
+ KillFile( aTmpFile );
+ if (eError == ERRCODE_NONE || eError.IsWarning())
+ eError = SCERR_EXPORT_DATA;
+ }
+ }
+ if (eError != ERRCODE_NONE && !GetErrorIgnoreWarning())
+ SetError(eError);
+ }
+ }
+ else if (aFltName == pFilterDif)
+ {
+ SvStream* pStream = rMed.GetOutStream();
+ if (pStream)
+ {
+ OUString sItStr;
+ if ( const SfxStringItem* pOptionsItem = rMed.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ {
+ sItStr = pOptionsItem->GetValue();
+ }
+
+ if (sItStr.isEmpty())
+ {
+ // default for DIF export (from API without options):
+ // ISO8859-1/MS_1252 encoding
+
+ sItStr = ScGlobal::GetCharsetString( RTL_TEXTENCODING_MS_1252 );
+ }
+
+ weld::WaitObject aWait( GetActiveDialogParent() );
+ ScFormatFilter::Get().ScExportDif( *pStream, m_pDocument.get(), ScAddress(0,0,0),
+ ScGlobal::GetCharsetValue(sItStr) );
+ bRet = true;
+
+ if (m_pDocument->GetTableCount() > 1)
+ if (!rMed.GetErrorIgnoreWarning())
+ rMed.SetError(SCWARN_EXPORT_ASCII);
+ }
+ }
+ else if (aFltName == pFilterSylk)
+ {
+ SvStream* pStream = rMed.GetOutStream();
+ if ( pStream )
+ {
+ weld::WaitObject aWait( GetActiveDialogParent() );
+
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ m_pDocument->GetCellArea( 0, nEndCol, nEndRow );
+ ScRange aRange( 0,0,0, nEndCol,nEndRow,0 );
+
+ ScImportExport aImExport( *m_pDocument, aRange );
+ aImExport.SetFormulas( true );
+ bRet = aImExport.ExportStream( *pStream, rMed.GetBaseURL( true ), SotClipboardFormatId::SYLK );
+ }
+ }
+ else if (aFltName == pFilterHtml)
+ {
+ SvStream* pStream = rMed.GetOutStream();
+ if ( pStream )
+ {
+ OUString sFilterOptions;
+
+ if (const SfxStringItem* pOptionsItem = rMed.GetItemSet().GetItemIfSet(SID_FILE_FILTEROPTIONS))
+ sFilterOptions = pOptionsItem->GetValue();
+
+ weld::WaitObject aWait(GetActiveDialogParent());
+ ScImportExport aImExport(*m_pDocument);
+ aImExport.SetStreamPath(rMed.GetName());
+ aImExport.SetFilterOptions(sFilterOptions);
+ bRet = aImExport.ExportStream(*pStream, rMed.GetBaseURL(true), SotClipboardFormatId::HTML);
+ if (bRet && !aImExport.GetNonConvertibleChars().isEmpty())
+ {
+ SetError(ErrCodeMsg(
+ SCWARN_EXPORT_NONCONVERTIBLE_CHARS,
+ aImExport.GetNonConvertibleChars(),
+ DialogMask::ButtonsOk | DialogMask::MessageInfo));
+ }
+ }
+ }
+ else
+ {
+ if (GetErrorIgnoreWarning())
+ SetError(SCERR_IMPORT_NI);
+ }
+ return bRet;
+}
+
+bool ScDocShell::DoSaveCompleted( SfxMedium * pNewStor, bool bRegisterRecent )
+{
+ bool bRet = SfxObjectShell::DoSaveCompleted( pNewStor, bRegisterRecent );
+
+ // SfxHintId::ScDocSaved for change ReadOnly -> Read/Write
+ Broadcast( SfxHint( SfxHintId::ScDocSaved ) );
+ return bRet;
+}
+
+bool ScDocShell::QuerySlotExecutable( sal_uInt16 nSlotId )
+{
+ // #i112634# ask VBA event handlers whether to save or print the document
+
+ using namespace ::com::sun::star::script::vba;
+
+ sal_Int32 nVbaEventId = VBAEventId::NO_EVENT;
+ uno::Sequence< uno::Any > aArgs;
+ switch( nSlotId )
+ {
+ case SID_SAVEDOC:
+ case SID_SAVEASDOC:
+ nVbaEventId = VBAEventId::WORKBOOK_BEFORESAVE;
+ aArgs = { uno::Any(nSlotId == SID_SAVEASDOC) };
+ break;
+ case SID_PRINTDOC:
+ case SID_PRINTDOCDIRECT:
+ nVbaEventId = VBAEventId::WORKBOOK_BEFOREPRINT;
+ break;
+ }
+
+ bool bSlotExecutable = true;
+ if( nVbaEventId != VBAEventId::NO_EVENT ) try
+ {
+ uno::Reference< XVBAEventProcessor > xEventProcessor( m_pDocument->GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ xEventProcessor->processVbaEvent( nVbaEventId, aArgs );
+ }
+ catch( util::VetoException& )
+ {
+ bSlotExecutable = false;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return bSlotExecutable;
+}
+
+bool ScDocShell::PrepareClose( bool bUI )
+{
+ if(SC_MOD()->GetCurRefDlgId()>0)
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this );
+ if( pFrame )
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ if(pViewSh!=nullptr)
+ {
+ vcl::Window *pWin=pViewSh->GetWindow();
+ if(pWin!=nullptr) pWin->GrabFocus();
+ }
+ }
+
+ return false;
+ }
+ if ( m_pDocument->IsInLinkUpdate() || m_pDocument->IsInInterpreter() )
+ {
+ ErrorMessage(STR_CLOSE_ERROR_LINK);
+ return false;
+ }
+
+ DoEnterHandler();
+
+ // start 'Workbook_BeforeClose' VBA event handler for possible veto
+ if( !IsInPrepareClose() )
+ {
+ try
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( m_pDocument->GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs;
+ xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKBOOK_BEFORECLOSE, aArgs );
+ }
+ catch( util::VetoException& )
+ {
+ // if event processor throws VetoException, macro has vetoed close
+ return false;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ // end handler code
+
+ bool bRet = SfxObjectShell::PrepareClose( bUI );
+ if (bRet) // true == close
+ m_pDocument->EnableIdle(false); // Do not mess around with it anymore!
+
+ return bRet;
+}
+
+OUString ScDocShell::GetOwnFilterName()
+{
+ return pFilterSc50;
+}
+
+OUString ScDocShell::GetHtmlFilterName()
+{
+ return pFilterHtml;
+}
+
+OUString ScDocShell::GetWebQueryFilterName()
+{
+ return pFilterHtmlWebQ;
+}
+
+OUString ScDocShell::GetAsciiFilterName()
+{
+ return SC_TEXT_CSV_FILTER_NAME;
+}
+
+OUString ScDocShell::GetLotusFilterName()
+{
+ return pFilterLotus;
+}
+
+OUString ScDocShell::GetDBaseFilterName()
+{
+ return pFilterDBase;
+}
+
+OUString ScDocShell::GetDifFilterName()
+{
+ return pFilterDif;
+}
+
+bool ScDocShell::HasAutomaticTableName( std::u16string_view rFilter )
+{
+ // sal_True for those filters that keep the default table name
+ // (which is language specific)
+
+ return rFilter == SC_TEXT_CSV_FILTER_NAME
+ || rFilter == pFilterLotus
+ || rFilter == pFilterExcel4
+ || rFilter == pFilterEx4Temp
+ || rFilter == pFilterDBase
+ || rFilter == pFilterDif
+ || rFilter == pFilterSylk
+ || rFilter == pFilterHtml
+ || rFilter == pFilterRtf;
+}
+
+std::unique_ptr<ScDocFunc> ScDocShell::CreateDocFunc()
+{
+ return std::make_unique<ScDocFuncDirect>( *this );
+}
+
+ScDocShell::ScDocShell( const SfxModelFlags i_nSfxCreationFlags, const std::shared_ptr<ScDocument>& pDoc ) :
+ SfxObjectShell( i_nSfxCreationFlags ),
+ m_pDocument ( pDoc ? pDoc : std::make_shared<ScDocument>( SCDOCMODE_DOCUMENT, this )),
+ m_aDdeTextFmt(OUString("TEXT")),
+ m_nPrtToScreenFactor( 1.0 ),
+ m_pImpl ( new DocShell_Impl ),
+ m_bHeaderOn ( true ),
+ m_bFooterOn ( true ),
+ m_bIsEmpty ( true ),
+ m_bIsInUndo ( false ),
+ m_bDocumentModifiedPending( false ),
+ m_bUpdateEnabled ( true ),
+ m_bAreasChangedNeedBroadcast( false ),
+ m_nDocumentLock ( 0 ),
+ m_nCanUpdate (css::document::UpdateDocMode::ACCORDING_TO_CONFIG)
+{
+ SetPool( &SC_MOD()->GetPool() );
+
+ m_bIsInplace = (GetCreateMode() == SfxObjectCreateMode::EMBEDDED);
+ // Will be reset if not in place
+
+ m_pDocFunc = CreateDocFunc();
+
+ // SetBaseModel needs exception handling
+ ScModelObj::CreateAndSet( this );
+
+ StartListening(*this);
+ SfxStyleSheetPool* pStlPool = m_pDocument->GetStyleSheetPool();
+ if (pStlPool)
+ StartListening(*pStlPool);
+
+ m_pDocument->GetDBCollection()->SetRefreshHandler(
+ LINK( this, ScDocShell, RefreshDBDataHdl ) );
+
+ // InitItems and CalcOutputFactor are called now in Load/ConvertFrom/InitNew
+}
+
+ScDocShell::~ScDocShell()
+{
+ ResetDrawObjectShell(); // If the Drawing Layer still tries to access it, access it
+
+ SfxStyleSheetPool* pStlPool = m_pDocument->GetStyleSheetPool();
+ if (pStlPool)
+ EndListening(*pStlPool);
+ EndListening(*this);
+
+ m_pAutoStyleList.reset();
+
+ SfxApplication *pSfxApp = SfxGetpApp();
+ if ( pSfxApp->GetDdeService() ) // Delete DDE for Document
+ pSfxApp->RemoveDdeTopic( this );
+
+ m_pDocFunc.reset();
+ delete m_pDocument->mpUndoManager;
+ m_pDocument->mpUndoManager = nullptr;
+ m_pImpl.reset();
+
+ m_pPaintLockData.reset();
+
+ m_pSheetSaveData.reset();
+ m_pFormatSaveData.reset();
+ m_pOldAutoDBRange.reset();
+
+ if (m_pModificator)
+ {
+ OSL_FAIL("The Modificator should not exist");
+ m_pModificator.reset();
+ }
+}
+
+SfxUndoManager* ScDocShell::GetUndoManager()
+{
+ return m_pDocument->GetUndoManager();
+}
+
+void ScDocShell::SetModified( bool bModified )
+{
+ if ( SfxObjectShell::IsEnableSetModified() )
+ {
+ SfxObjectShell::SetModified( bModified );
+ Broadcast( SfxHint( SfxHintId::DocChanged ) );
+ }
+}
+
+void ScDocShell::SetDocumentModified()
+{
+ // BroadcastUno must also happen right away with pPaintLockData
+ // FIXME: Also for SetDrawModified, if Drawing is connected
+ // FIXME: Then own Hint?
+
+ if ( m_pPaintLockData )
+ {
+ // #i115009# broadcast BCA_BRDCST_ALWAYS, so a component can read recalculated results
+ // of RecalcModeAlways formulas (like OFFSET) after modifying cells
+ m_pDocument->Broadcast(ScHint(SfxHintId::ScDataChanged, BCA_BRDCST_ALWAYS));
+ m_pDocument->InvalidateTableArea(); // #i105279# needed here
+ m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
+
+ m_pPaintLockData->SetModified(); // Later on ...
+ return;
+ }
+
+ SetDrawModified();
+
+ if ( m_pDocument->IsAutoCalcShellDisabled() )
+ SetDocumentModifiedPending( true );
+ else
+ {
+ SetDocumentModifiedPending( false );
+ m_pDocument->InvalidateStyleSheetUsage();
+ m_pDocument->InvalidateTableArea();
+ m_pDocument->InvalidateLastTableOpParams();
+ m_pDocument->Broadcast(ScHint(SfxHintId::ScDataChanged, BCA_BRDCST_ALWAYS));
+ if ( m_pDocument->IsForcedFormulaPending() && m_pDocument->GetAutoCalc() )
+ m_pDocument->CalcFormulaTree( true );
+ m_pDocument->RefreshDirtyTableColumnNames();
+ PostDataChanged();
+
+ // Detective AutoUpdate:
+ // Update if formulas were modified (DetectiveDirty) or the list contains
+ // "Trace Error" entries (Trace Error can look completely different
+ // after changes to non-formula cells).
+
+ ScDetOpList* pList = m_pDocument->GetDetOpList();
+ if ( pList && ( m_pDocument->IsDetectiveDirty() || pList->HasAddError() ) &&
+ pList->Count() && !IsInUndo() && SC_MOD()->GetAppOptions().GetDetectiveAuto() )
+ {
+ GetDocFunc().DetectiveRefresh(true); // sal_True = caused by automatic update
+ }
+ m_pDocument->SetDetectiveDirty(false); // always reset, also if not refreshed
+ }
+
+ if (m_bAreasChangedNeedBroadcast)
+ {
+ m_bAreasChangedNeedBroadcast = false;
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged));
+ }
+
+ // notify UNO objects after BCA_BRDCST_ALWAYS etc.
+ m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
+}
+
+/**
+ * SetDrawModified - without Formula update
+ *
+ * Drawing also needs to be updated for the normal SetDocumentModified
+ * e.g.: when deleting tables etc.
+ */
+void ScDocShell::SetDrawModified()
+{
+ bool bUpdate = !IsModified();
+
+ SetModified();
+
+ SfxBindings* pBindings = GetViewBindings();
+ if (bUpdate && pBindings)
+ {
+ pBindings->Invalidate( SID_SAVEDOC );
+ pBindings->Invalidate( SID_DOC_MODIFIED );
+ }
+
+ if (pBindings)
+ {
+ // #i105960# Undo etc used to be volatile.
+ // They always have to be invalidated, including drawing layer or row height changes
+ // (but not while pPaintLockData is set).
+ pBindings->Invalidate( SID_UNDO );
+ pBindings->Invalidate( SID_REDO );
+ pBindings->Invalidate( SID_REPEAT );
+ }
+
+ if ( m_pDocument->IsChartListenerCollectionNeedsUpdate() )
+ {
+ m_pDocument->UpdateChartListenerCollection();
+ SfxGetpApp()->Broadcast(SfxHint( SfxHintId::ScDrawChanged )); // Navigator
+ }
+ SC_MOD()->AnythingChanged();
+}
+
+void ScDocShell::SetInUndo(bool bSet)
+{
+ m_bIsInUndo = bSet;
+}
+
+void ScDocShell::GetDocStat( ScDocStat& rDocStat )
+{
+ SfxPrinter* pPrinter = GetPrinter();
+
+ m_pDocument->GetDocStat( rDocStat );
+ rDocStat.nPageCount = 0;
+
+ if ( pPrinter )
+ for ( SCTAB i=0; i<rDocStat.nTableCount; i++ )
+ rDocStat.nPageCount = sal::static_int_cast<sal_uInt16>( rDocStat.nPageCount +
+ static_cast<sal_uInt16>(ScPrintFunc( this, pPrinter, i ).GetTotalPages()) );
+}
+
+std::shared_ptr<SfxDocumentInfoDialog> ScDocShell::CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet &rSet)
+{
+ std::shared_ptr<SfxDocumentInfoDialog> xDlg = std::make_shared<SfxDocumentInfoDialog>(pParent, rSet);
+ ScDocShell* pDocSh = dynamic_cast< ScDocShell *>( SfxObjectShell::Current() );
+
+ // Only for statistics, if this Doc is shown; not from the Doc Manager
+ if( pDocSh == this )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ::CreateTabPage ScDocStatPageCreate = pFact->GetTabPageCreatorFunc(SID_SC_TP_STAT);
+ OSL_ENSURE(ScDocStatPageCreate, "Tabpage create fail!");
+ xDlg->AddFontTabPage();
+ xDlg->AddTabPage("calcstats", ScResId(STR_DOC_STAT), ScDocStatPageCreate);
+ }
+ return xDlg;
+}
+
+weld::Window* ScDocShell::GetActiveDialogParent()
+{
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ return pViewSh->GetDialogParent();
+ return Application::GetDefDialogParent();
+}
+
+ScSheetSaveData* ScDocShell::GetSheetSaveData()
+{
+ if (!m_pSheetSaveData)
+ m_pSheetSaveData.reset( new ScSheetSaveData );
+
+ return m_pSheetSaveData.get();
+}
+
+ScFormatSaveData* ScDocShell::GetFormatSaveData()
+{
+ if (!m_pFormatSaveData)
+ m_pFormatSaveData.reset( new ScFormatSaveData );
+
+ return m_pFormatSaveData.get();
+}
+
+namespace {
+
+void removeKeysIfExists(const Reference<ui::XAcceleratorConfiguration>& xScAccel, const vector<const awt::KeyEvent*>& rKeys)
+{
+ for (const awt::KeyEvent* p : rKeys)
+ {
+ if (!p)
+ continue;
+
+ try
+ {
+ xScAccel->removeKeyEvent(*p);
+ }
+ catch (const container::NoSuchElementException&) {}
+ }
+}
+
+}
+
+void ScDocShell::ResetKeyBindings( ScOptionsUtil::KeyBindingType eType )
+{
+ using namespace ::com::sun::star::ui;
+
+ Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ if (!xContext.is())
+ return;
+
+ Reference<XModuleUIConfigurationManagerSupplier> xModuleCfgSupplier(
+ theModuleUIConfigurationManagerSupplier::get(xContext) );
+
+ // Grab the Calc configuration.
+ Reference<XUIConfigurationManager> xConfigMgr =
+ xModuleCfgSupplier->getUIConfigurationManager(
+ "com.sun.star.sheet.SpreadsheetDocument");
+
+ if (!xConfigMgr.is())
+ return;
+
+ // shortcut manager
+ Reference<XAcceleratorConfiguration> xScAccel = xConfigMgr->getShortCutManager();
+
+ if (!xScAccel.is())
+ return;
+
+ vector<const awt::KeyEvent*> aKeys;
+ aKeys.reserve(9);
+
+ // Backspace key
+ awt::KeyEvent aBackspace;
+ aBackspace.KeyCode = awt::Key::BACKSPACE;
+ aBackspace.Modifiers = 0;
+ aKeys.push_back(&aBackspace);
+
+ // Delete key
+ awt::KeyEvent aDelete;
+ aDelete.KeyCode = awt::Key::DELETE;
+ aDelete.Modifiers = 0;
+ aKeys.push_back(&aDelete);
+
+ // Ctrl-D
+ awt::KeyEvent aCtrlD;
+ aCtrlD.KeyCode = awt::Key::D;
+ aCtrlD.Modifiers = awt::KeyModifier::MOD1;
+ aKeys.push_back(&aCtrlD);
+
+ // Alt-Down
+ awt::KeyEvent aAltDown;
+ aAltDown.KeyCode = awt::Key::DOWN;
+ aAltDown.Modifiers = awt::KeyModifier::MOD2;
+ aKeys.push_back(&aAltDown);
+
+ // Ctrl-Space
+ awt::KeyEvent aCtrlSpace;
+ aCtrlSpace.KeyCode = awt::Key::SPACE;
+ aCtrlSpace.Modifiers = awt::KeyModifier::MOD1;
+ aKeys.push_back(&aCtrlSpace);
+
+ // Ctrl-Shift-Space
+ awt::KeyEvent aCtrlShiftSpace;
+ aCtrlShiftSpace.KeyCode = awt::Key::SPACE;
+ aCtrlShiftSpace.Modifiers = awt::KeyModifier::MOD1 | awt::KeyModifier::SHIFT;
+ aKeys.push_back(&aCtrlShiftSpace);
+
+ // F4
+ awt::KeyEvent aF4;
+ aF4.KeyCode = awt::Key::F4;
+ aF4.Modifiers = 0;
+ aKeys.push_back(&aF4);
+
+ // CTRL+SHIFT+F4
+ awt::KeyEvent aCtrlShiftF4;
+ aCtrlShiftF4.KeyCode = awt::Key::F4;
+ aCtrlShiftF4.Modifiers = awt::KeyModifier::MOD1 | awt::KeyModifier::SHIFT;
+ aKeys.push_back(&aCtrlShiftF4);
+
+ // SHIFT+F4
+ awt::KeyEvent aShiftF4;
+ aShiftF4.KeyCode = awt::Key::F4;
+ aShiftF4.Modifiers = awt::KeyModifier::SHIFT;
+ aKeys.push_back(&aShiftF4);
+
+ // Remove all involved keys first, because swapping commands don't work
+ // well without doing this.
+ removeKeysIfExists(xScAccel, aKeys);
+ xScAccel->store();
+
+ switch (eType)
+ {
+ case ScOptionsUtil::KEY_DEFAULT:
+ xScAccel->setKeyEvent(aDelete, ".uno:ClearContents");
+ xScAccel->setKeyEvent(aBackspace, ".uno:Delete");
+ xScAccel->setKeyEvent(aCtrlD, ".uno:FillDown");
+ xScAccel->setKeyEvent(aAltDown, ".uno:DataSelect");
+ xScAccel->setKeyEvent(aCtrlSpace, ".uno:SelectColumn");
+ xScAccel->setKeyEvent(aCtrlShiftSpace, ".uno:SelectAll");
+ xScAccel->setKeyEvent(aF4, ".uno:ToggleRelative");
+ xScAccel->setKeyEvent(aCtrlShiftF4, ".uno:ViewDataSourceBrowser");
+ break;
+ case ScOptionsUtil::KEY_OOO_LEGACY:
+ xScAccel->setKeyEvent(aDelete, ".uno:Delete");
+ xScAccel->setKeyEvent(aBackspace, ".uno:ClearContents");
+ xScAccel->setKeyEvent(aCtrlD, ".uno:DataSelect");
+ xScAccel->setKeyEvent(aCtrlShiftSpace, ".uno:SelectColumn");
+ xScAccel->setKeyEvent(aF4, ".uno:ViewDataSourceBrowser");
+ xScAccel->setKeyEvent(aShiftF4, ".uno:ToggleRelative");
+ break;
+ default:
+ ;
+ }
+
+ xScAccel->store();
+}
+
+void ScDocShell::UseSheetSaveEntries()
+{
+ if (!m_pSheetSaveData)
+ return;
+
+ m_pSheetSaveData->UseSaveEntries(); // use positions from saved file for next saving
+
+ bool bHasEntries = false;
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ SCTAB nTab;
+ for (nTab = 0; nTab < nTabCount; ++nTab)
+ if (m_pSheetSaveData->HasStreamPos(nTab))
+ bHasEntries = true;
+
+ if (!bHasEntries)
+ {
+ // if no positions were set (for example, export to other format),
+ // reset all "valid" flags
+ for (nTab = 0; nTab < nTabCount; ++nTab)
+ m_pDocument->SetStreamValid(nTab, false);
+ }
+}
+
+// --- ScDocShellModificator ------------------------------------------
+
+ScDocShellModificator::ScDocShellModificator( ScDocShell& rDS )
+ :
+ rDocShell( rDS ),
+ mpProtector(new ScRefreshTimerProtector(rDS.GetDocument().GetRefreshTimerControlAddress()))
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ bAutoCalcShellDisabled = rDoc.IsAutoCalcShellDisabled();
+ bIdleEnabled = rDoc.IsIdleEnabled();
+ rDoc.SetAutoCalcShellDisabled( true );
+ rDoc.EnableIdle(false);
+}
+
+ScDocShellModificator::~ScDocShellModificator() COVERITY_NOEXCEPT_FALSE
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
+ if ( !bAutoCalcShellDisabled && rDocShell.IsDocumentModifiedPending() )
+ rDocShell.SetDocumentModified(); // last one shuts off the lights
+ rDoc.EnableIdle(bIdleEnabled);
+}
+
+void ScDocShellModificator::SetDocumentModified()
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ rDoc.PrepareFormulaCalc();
+ if ( !rDoc.IsImportingXML() )
+ {
+ // temporarily restore AutoCalcShellDisabled
+ bool bDisabled = rDoc.IsAutoCalcShellDisabled();
+ rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
+ rDocShell.SetDocumentModified();
+ rDoc.SetAutoCalcShellDisabled( bDisabled );
+ }
+ else
+ {
+ // uno broadcast is necessary for api to work
+ // -> must also be done during xml import
+ rDoc.BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
+ }
+}
+
+bool ScDocShell::IsChangeRecording() const
+{
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ return pChangeTrack != nullptr;
+}
+
+bool ScDocShell::HasChangeRecordProtection() const
+{
+ bool bRes = false;
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ if (pChangeTrack)
+ bRes = pChangeTrack->IsProtected();
+ return bRes;
+}
+
+void ScDocShell::SetChangeRecording( bool bActivate, bool /*bLockAllViews*/ )
+{
+ bool bOldChangeRecording = IsChangeRecording();
+
+ if (bActivate)
+ {
+ m_pDocument->StartChangeTracking();
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges(true);
+ m_pDocument->SetChangeViewSettings(aChangeViewSet);
+ }
+ else
+ {
+ m_pDocument->EndChangeTracking();
+ PostPaintGridAll();
+ }
+
+ if (bOldChangeRecording != IsChangeRecording())
+ {
+ UpdateAcceptChangesDialog();
+ // invalidate slots
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ pBindings->InvalidateAll(false);
+ }
+}
+
+void ScDocShell::SetProtectionPassword( const OUString &rNewPassword )
+{
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ if (!pChangeTrack)
+ return;
+
+ bool bProtected = pChangeTrack->IsProtected();
+
+ if (!rNewPassword.isEmpty())
+ {
+ // when password protection is applied change tracking must always be active
+ SetChangeRecording( true );
+
+ css::uno::Sequence< sal_Int8 > aProtectionHash;
+ SvPasswordHelper::GetHashPassword( aProtectionHash, rNewPassword );
+ pChangeTrack->SetProtection( aProtectionHash );
+ }
+ else
+ {
+ pChangeTrack->SetProtection( css::uno::Sequence< sal_Int8 >() );
+ }
+
+ if ( bProtected != pChangeTrack->IsProtected() )
+ {
+ UpdateAcceptChangesDialog();
+ SetDocumentModified();
+ }
+}
+
+bool ScDocShell::GetProtectionHash( /*out*/ css::uno::Sequence< sal_Int8 > &rPasswordHash )
+{
+ bool bRes = false;
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ if (pChangeTrack && pChangeTrack->IsProtected())
+ {
+ rPasswordHash = pChangeTrack->GetProtection();
+ bRes = true;
+ }
+ return bRes;
+}
+
+void ScDocShell::RegisterAutomationWorkbookObject(css::uno::Reference< ooo::vba::excel::XWorkbook > const& xWorkbook)
+{
+ mxAutomationWorkbookObject = xWorkbook;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportSLK(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ aDocument.SetImportingXML(true);
+
+ ScImportExport aImpEx(aDocument);
+ return aImpEx.ImportStream(rStream, OUString(), SotClipboardFormatId::SYLK);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDBF(SvStream &rStream)
+{
+ ScDLL::Init();
+
+ // we need a real file for this filter
+
+ // put it in an empty dir
+ utl::TempFileNamed aTmpDir(nullptr, true);
+ aTmpDir.EnableKillingFile();
+ OUString sTmpDir = aTmpDir.GetURL();
+
+ utl::TempFileNamed aTempInput(u"", true, u".dbf", &sTmpDir);
+ aTempInput.EnableKillingFile();
+
+ SvStream* pInputStream = aTempInput.GetStream(StreamMode::WRITE);
+ sal_uInt8 aBuffer[8192];
+ while (auto nRead = rStream.ReadBytes(aBuffer, SAL_N_ELEMENTS(aBuffer)))
+ pInputStream->WriteBytes(aBuffer, nRead);
+ aTempInput.CloseStream();
+
+ SfxMedium aMedium(aTempInput.GetURL(), StreamMode::STD_READWRITE);
+
+ ScDocShellRef xDocShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT |
+ SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
+ SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
+
+ xDocShell->DoInitNew();
+
+ ScDocument& rDoc = xDocShell->GetDocument();
+
+ ScDocOptions aDocOpt = rDoc.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ rDoc.SetDocOptions(aDocOpt);
+ rDoc.MakeTable(0);
+ rDoc.EnableExecuteLink(false);
+ rDoc.SetInsertingFromOtherDoc(true);
+
+ ScDocRowHeightUpdater::TabRanges aRecalcRanges(0, rDoc.MaxRow());
+ std::map<SCCOL, ScColWidthParam> aColWidthParam;
+ ErrCode eError = xDocShell->DBaseImport(aMedium.GetPhysicalName(), RTL_TEXTENCODING_IBM_850, aColWidthParam, aRecalcRanges.maRanges);
+
+ xDocShell->DoClose();
+ xDocShell.clear();
+
+ return eError == ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh2.cxx b/sc/source/ui/docshell/docsh2.cxx
new file mode 100644
index 0000000000..cf77e01774
--- /dev/null
+++ b/sc/source/ui/docshell/docsh2.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 <rtl/bootstrap.hxx>
+#include <osl/file.hxx>
+#include <svx/drawitem.hxx>
+#include <svl/asiancfg.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <orcusfilters.hxx>
+#include <config_folders.h>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <drwlayer.hxx>
+#include <stlpool.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <svx/svxids.hrc>
+#include <filter.hxx>
+#include <functional>
+
+using namespace com::sun::star;
+
+bool ScDocShell::InitNew( const uno::Reference < embed::XStorage >& xStor )
+{
+ bool bRet = SfxObjectShell::InitNew( xStor );
+
+ m_pDocument->MakeTable(0);
+
+ // Additional tables are created by the first View, if bIsEmpty is still sal_True
+ if( bRet )
+ {
+ Size aSize(
+ o3tl::convert(STD_COL_WIDTH * OLE_STD_CELLS_X, o3tl::Length::twip, o3tl::Length::mm100),
+ o3tl::convert(ScGlobal::nStdRowHeight * OLE_STD_CELLS_Y, o3tl::Length::twip,
+ o3tl::Length::mm100));
+ // Also adjust start here
+ SetVisAreaOrSize( tools::Rectangle( Point(), aSize ) );
+ }
+
+ // InitOptions sets the document languages, must be called before CreateStandardStyles
+ InitOptions(false);
+
+ if (ScStyleSheetPool* pStyleSheetPool = m_pDocument->GetStyleSheetPool())
+ {
+ pStyleSheetPool->CreateStandardStyles();
+ m_pDocument->UpdStlShtPtrsFrmNms();
+
+ /* Create styles that are imported through Orcus */
+
+ OUString aURL("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/calc/styles.xml");
+ rtl::Bootstrap::expandMacros(aURL);
+
+ OUString aPath;
+ osl::FileBase::getSystemPathFromFileURL(aURL, aPath);
+
+ ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
+ if (pOrcus)
+ {
+ pOrcus->importODS_Styles(*m_pDocument, aPath);
+ pStyleSheetPool->setAllParaStandard();
+ }
+ }
+
+ // SetDocumentModified is not allowed anymore in Load/InitNew!
+ InitItems();
+ CalcOutputFactor();
+
+ return bRet;
+}
+
+void ScDocShell::SetEmpty(bool bSet)
+{
+ m_bIsEmpty = bSet;
+}
+
+void ScDocShell::InitItems()
+{
+ // Fill AllItemSet for Controller with needed Items:
+ // Printer Options are set in GetPrinter when printing
+ UpdateFontList();
+
+ ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer();
+ if (pDrawLayer)
+ {
+ PutItem( SvxColorListItem ( pDrawLayer->GetColorList(), SID_COLOR_TABLE ) );
+ PutItem( SvxGradientListItem( pDrawLayer->GetGradientList(), SID_GRADIENT_LIST ) );
+ PutItem( SvxHatchListItem ( pDrawLayer->GetHatchList(), SID_HATCH_LIST ) );
+ PutItem( SvxBitmapListItem ( pDrawLayer->GetBitmapList(), SID_BITMAP_LIST ) );
+ PutItem( SvxPatternListItem ( pDrawLayer->GetPatternList(), SID_PATTERN_LIST ) );
+ PutItem( SvxDashListItem ( pDrawLayer->GetDashList(), SID_DASH_LIST ) );
+ PutItem( SvxLineEndListItem ( pDrawLayer->GetLineEndList(), SID_LINEEND_LIST ) );
+
+ // Other modifications after creation of the DrawLayer
+ pDrawLayer->SetNotifyUndoActionHdl( std::bind( &ScDocFunc::NotifyDrawUndo, m_pDocFunc.get(), std::placeholders::_1 ) );
+ }
+ else if (!utl::ConfigManager::IsFuzzing())
+ {
+ // always use global color table instead of local copy
+ PutItem( SvxColorListItem( XColorList::GetStdColorList(), SID_COLOR_TABLE ) );
+ }
+
+ if (utl::ConfigManager::IsFuzzing() ||
+ (m_pDocument->GetForbiddenCharacters() && m_pDocument->IsValidAsianCompression() && m_pDocument->IsValidAsianKerning()))
+ return;
+
+ // get settings from SvxAsianConfig
+ SvxAsianConfig aAsian;
+
+ if (!m_pDocument->GetForbiddenCharacters())
+ {
+ // set forbidden characters if necessary
+ const uno::Sequence<lang::Locale> aLocales = aAsian.GetStartEndCharLocales();
+ if (aLocales.hasElements())
+ {
+ std::shared_ptr<SvxForbiddenCharactersTable> xForbiddenTable(
+ SvxForbiddenCharactersTable::makeForbiddenCharactersTable(comphelper::getProcessComponentContext()));
+
+ for (const lang::Locale& rLocale : aLocales)
+ {
+ i18n::ForbiddenCharacters aForbidden;
+ aAsian.GetStartEndChars( rLocale, aForbidden.beginLine, aForbidden.endLine );
+ LanguageType eLang = LanguageTag::convertToLanguageType(rLocale);
+
+ xForbiddenTable->SetForbiddenCharacters( eLang, aForbidden );
+ }
+
+ m_pDocument->SetForbiddenCharacters( xForbiddenTable );
+ }
+ }
+
+ if ( !m_pDocument->IsValidAsianCompression() )
+ {
+ // set compression mode from configuration if not already set (e.g. XML import)
+ m_pDocument->SetAsianCompression( aAsian.GetCharDistanceCompression() );
+ }
+
+ if ( !m_pDocument->IsValidAsianKerning() )
+ {
+ // set asian punctuation kerning from configuration if not already set (e.g. XML import)
+ m_pDocument->SetAsianKerning( !aAsian.IsKerningWesternTextOnly() ); // reversed
+ }
+}
+
+void ScDocShell::ResetDrawObjectShell()
+{
+ ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer();
+ if (pDrawLayer)
+ pDrawLayer->SetObjectShell( nullptr );
+}
+
+ScDrawLayer* ScDocShell::MakeDrawLayer()
+{
+ ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer();
+ if (!pDrawLayer)
+ {
+ m_pDocument->InitDrawLayer(this);
+ pDrawLayer = m_pDocument->GetDrawLayer();
+ InitItems(); // including Undo and Basic
+ Broadcast( SfxHint( SfxHintId::ScDrawLayerNew ) );
+ if (m_nDocumentLock)
+ pDrawLayer->setLock(true);
+ }
+ return pDrawLayer;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh3.cxx b/sc/source/ui/docshell/docsh3.cxx
new file mode 100644
index 0000000000..96546d11a5
--- /dev/null
+++ b/sc/source/ui/docshell/docsh3.cxx
@@ -0,0 +1,1334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <scitems.hxx>
+#include <rangelst.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/sizeitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <sal/log.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <svl/numformat.hxx>
+#include <svx/pageitem.hxx>
+#include <svx/postattr.hxx>
+#include <svx/svxids.hrc>
+#include <unotools/configmgr.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+
+#include <docsh.hxx>
+#include "docshimp.hxx"
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <patattr.hxx>
+#include <uiitems.hxx>
+#include <hints.hxx>
+#include <docoptio.hxx>
+#include <viewopti.hxx>
+#include <pntlock.hxx>
+#include <chgtrack.hxx>
+#include <docfunc.hxx>
+#include <formulacell.hxx>
+#include <chgviset.hxx>
+#include <progress.hxx>
+#include <redcom.hxx>
+#include <inputopt.hxx>
+#include <drwlayer.hxx>
+#include <inputhdl.hxx>
+#include <conflictsdlg.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <markdata.hxx>
+#include <memory>
+#include <formulaopt.hxx>
+
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+
+// Redraw - Notifications
+
+void ScDocShell::PostEditView( ScEditEngineDefaulter* pEditEngine, const ScAddress& rCursorPos )
+{
+// Broadcast( ScEditViewHint( pEditEngine, rCursorPos ) );
+
+ // Test: only active ViewShell
+
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if (pViewSh && pViewSh->GetViewData().GetDocShell() == this)
+ {
+ ScEditViewHint aHint( pEditEngine, rCursorPos );
+ pViewSh->Notify( *this, aHint );
+ }
+}
+
+void ScDocShell::PostDataChanged()
+{
+ Broadcast( SfxHint( SfxHintId::ScDataChanged ) );
+ SfxGetpApp()->Broadcast(SfxHint( SfxHintId::ScAnyDataChanged )); // Navigator
+ m_pDocument->PrepareFormulaCalc();
+ //! notify navigator directly!
+}
+
+void ScDocShell::PostPaint( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, PaintPartFlags nPart,
+ sal_uInt16 nExtFlags )
+{
+ ScRange aRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ PostPaint(aRange, nPart, nExtFlags);
+}
+
+void ScDocShell::PostPaint( const ScRangeList& rRanges, PaintPartFlags nPart, sal_uInt16 nExtFlags )
+{
+ ScRangeList aPaintRanges;
+ std::set<SCTAB> aTabsInvalidated;
+ const SCTAB nMaxTab = m_pDocument->GetTableCount() - 1;
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange& rRange = rRanges[i];
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+ SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = std::min<SCTAB>(nMaxTab, rRange.aEnd.Tab());
+
+ if (!m_pDocument->ValidCol(nCol1)) nCol1 = m_pDocument->MaxCol();
+ if (!m_pDocument->ValidRow(nRow1)) nRow1 = m_pDocument->MaxRow();
+ if (!m_pDocument->ValidCol(nCol2)) nCol2 = m_pDocument->MaxCol();
+ if (!m_pDocument->ValidRow(nRow2)) nRow2 = m_pDocument->MaxRow();
+
+ if ( m_pPaintLockData )
+ {
+ // #i54081# PaintPartFlags::Extras still has to be broadcast because it changes the
+ // current sheet if it's invalid. All other flags added to pPaintLockData.
+ PaintPartFlags nLockPart = nPart & ~PaintPartFlags::Extras;
+ if ( nLockPart != PaintPartFlags::NONE )
+ {
+ //! nExtFlags ???
+ m_pPaintLockData->AddRange( ScRange( nCol1, nRow1, nTab1,
+ nCol2, nRow2, nTab2 ), nLockPart );
+ }
+
+ nPart &= PaintPartFlags::Extras; // for broadcasting
+ if (nPart == PaintPartFlags::NONE)
+ continue;
+ }
+
+ if (nExtFlags & SC_PF_LINES) // respect space for lines
+ {
+ //! check for hidden columns/rows!
+ if (nCol1>0) --nCol1;
+ if (nCol2<m_pDocument->MaxCol()) ++nCol2;
+ if (nRow1>0) --nRow1;
+ if (nRow2<m_pDocument->MaxRow()) ++nRow2;
+ }
+
+ // expand for the merged ones
+ if (nExtFlags & SC_PF_TESTMERGE)
+ m_pDocument->ExtendMerge( nCol1, nRow1, nCol2, nRow2, nTab1 );
+
+ if ( nCol1 != 0 || nCol2 != m_pDocument->MaxCol() )
+ {
+ // Extend to whole rows if SC_PF_WHOLEROWS is set, or rotated or non-left
+ // aligned cells are contained (see UpdatePaintExt).
+ // Special handling for RTL text (#i9731#) is unnecessary now with full
+ // support of right-aligned text.
+
+ if ( ( nExtFlags & SC_PF_WHOLEROWS ) ||
+ m_pDocument->HasAttrib( nCol1,nRow1,nTab1,
+ m_pDocument->MaxCol(),nRow2,nTab2, HasAttrFlags::Rotate | HasAttrFlags::RightOrCenter ) )
+ {
+ nCol1 = 0;
+ nCol2 = m_pDocument->MaxCol();
+ }
+ }
+ aPaintRanges.push_back(ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
+ for (auto nTabNum = nTab1; nTabNum <= nTab2; ++nTabNum)
+ aTabsInvalidated.insert(nTabNum);
+ }
+
+ Broadcast(ScPaintHint(aPaintRanges.Combine(), nPart));
+
+ // LOK: we are supposed to update the row / columns headers (and actually
+ // the document size too - cell size affects that, obviously)
+ if ((nPart & (PaintPartFlags::Top | PaintPartFlags::Left)) && comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = GetModel();
+ for (auto nTab : aTabsInvalidated)
+ SfxLokHelper::notifyPartSizeChangedAllViews(pModel, nTab);
+ }
+}
+
+void ScDocShell::PostPaintGridAll()
+{
+ PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, PaintPartFlags::Grid );
+}
+
+void ScDocShell::PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, SC_PF_TESTMERGE );
+}
+
+void ScDocShell::PostPaintCell( const ScAddress& rPos )
+{
+ PostPaintCell( rPos.Col(), rPos.Row(), rPos.Tab() );
+}
+
+void ScDocShell::PostPaintExtras()
+{
+ PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, PaintPartFlags::Extras );
+}
+
+void ScDocShell::UpdatePaintExt( sal_uInt16& rExtFlags, const ScRange& rRange )
+{
+ if ( ( rExtFlags & SC_PF_LINES ) == 0 &&
+ m_pDocument->HasAttrib( rRange, HasAttrFlags::Lines | HasAttrFlags::Shadow | HasAttrFlags::Conditional ) )
+ {
+ // If the range contains lines, shadow or conditional formats,
+ // set SC_PF_LINES to include one extra cell in all directions.
+
+ rExtFlags |= SC_PF_LINES;
+ }
+
+ if ( ( rExtFlags & SC_PF_WHOLEROWS ) == 0 &&
+ ( rRange.aStart.Col() != 0 || rRange.aEnd.Col() != m_pDocument->MaxCol() ) &&
+ m_pDocument->HasAttrib( rRange, HasAttrFlags::Rotate | HasAttrFlags::RightOrCenter ) )
+ {
+ // If the range contains (logically) right- or center-aligned cells,
+ // or rotated cells, set SC_PF_WHOLEROWS to paint the whole rows.
+ // This test isn't needed after the cell changes, because it's also
+ // tested in PostPaint. UpdatePaintExt may later be changed to do this
+ // only if called before the changes.
+
+ rExtFlags |= SC_PF_WHOLEROWS;
+ }
+}
+
+void ScDocShell::UpdatePaintExt( sal_uInt16& rExtFlags, SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
+{
+ UpdatePaintExt( rExtFlags, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ) );
+}
+
+void ScDocShell::LockPaint_Impl(bool bDoc)
+{
+ if ( !m_pPaintLockData )
+ m_pPaintLockData.reset( new ScPaintLockData );
+ m_pPaintLockData->IncLevel(bDoc);
+}
+
+void ScDocShell::UnlockPaint_Impl(bool bDoc)
+{
+ if ( m_pPaintLockData )
+ {
+ if ( m_pPaintLockData->GetLevel(bDoc) )
+ m_pPaintLockData->DecLevel(bDoc);
+ if (!m_pPaintLockData->GetLevel(!bDoc) && !m_pPaintLockData->GetLevel(bDoc))
+ {
+ // Execute Paint now
+
+ // don't continue collecting
+ std::unique_ptr<ScPaintLockData> pPaint = std::move(m_pPaintLockData);
+
+ ScRangeListRef xRangeList = pPaint->GetRangeList();
+ if ( xRangeList.is() )
+ {
+ PaintPartFlags nParts = pPaint->GetParts();
+ for ( size_t i = 0, nCount = xRangeList->size(); i < nCount; i++ )
+ {
+ //! nExtFlags ???
+ ScRange const & rRange = (*xRangeList)[i];
+ PostPaint( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ nParts );
+ }
+ }
+
+ if ( pPaint->GetModified() )
+ SetDocumentModified();
+ }
+ }
+ else
+ {
+ OSL_FAIL("UnlockPaint without LockPaint");
+ }
+}
+
+void ScDocShell::LockDocument_Impl(sal_uInt16 nNew)
+{
+ if (!m_nDocumentLock)
+ {
+ ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer();
+ if (pDrawLayer)
+ pDrawLayer->setLock(true);
+ }
+ m_nDocumentLock = nNew;
+}
+
+void ScDocShell::UnlockDocument_Impl(sal_uInt16 nNew)
+{
+ m_nDocumentLock = nNew;
+ if (!m_nDocumentLock)
+ {
+ ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer();
+ if (pDrawLayer)
+ pDrawLayer->setLock(false);
+ }
+}
+
+void ScDocShell::SetLockCount(sal_uInt16 nNew)
+{
+ if (nNew) // set
+ {
+ if ( !m_pPaintLockData )
+ m_pPaintLockData.reset( new ScPaintLockData );
+ m_pPaintLockData->SetDocLevel(nNew-1);
+ LockDocument_Impl(nNew);
+ }
+ else if (m_pPaintLockData) // delete
+ {
+ m_pPaintLockData->SetDocLevel(0); // at unlock, execute immediately
+ UnlockPaint_Impl(true); // now
+ UnlockDocument_Impl(0);
+ }
+}
+
+void ScDocShell::LockPaint()
+{
+ LockPaint_Impl(false);
+}
+
+void ScDocShell::UnlockPaint()
+{
+ UnlockPaint_Impl(false);
+}
+
+void ScDocShell::LockDocument()
+{
+ LockPaint_Impl(true);
+ LockDocument_Impl(m_nDocumentLock + 1);
+}
+
+void ScDocShell::UnlockDocument()
+{
+ if (m_nDocumentLock)
+ {
+ UnlockPaint_Impl(true);
+ UnlockDocument_Impl(m_nDocumentLock - 1);
+ }
+ else
+ {
+ OSL_FAIL("UnlockDocument without LockDocument");
+ }
+}
+
+void ScDocShell::SetInplace( bool bInplace )
+{
+ if (m_bIsInplace != bInplace)
+ {
+ m_bIsInplace = bInplace;
+ CalcOutputFactor();
+ }
+}
+
+void ScDocShell::CalcOutputFactor()
+{
+ if (m_bIsInplace)
+ {
+ m_nPrtToScreenFactor = 1.0; // otherwise it does not match the inactive display
+ return;
+ }
+
+ bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
+ if (bTextWysiwyg)
+ {
+ m_nPrtToScreenFactor = 1.0;
+ return;
+ }
+
+ OUString aTestString(
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789");
+ tools::Long nPrinterWidth = 0;
+ const ScPatternAttr* pPattern = &m_pDocument->GetPool()->GetDefaultItem(ATTR_PATTERN);
+
+ vcl::Font aDefFont;
+ OutputDevice* pRefDev = GetRefDevice();
+ MapMode aOldMode = pRefDev->GetMapMode();
+ vcl::Font aOldFont = pRefDev->GetFont();
+
+ pRefDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ pPattern->fillFontOnly(aDefFont, pRefDev); // font color doesn't matter here
+ pRefDev->SetFont(aDefFont);
+ nPrinterWidth = pRefDev->PixelToLogic(Size(pRefDev->GetTextWidth(aTestString), 0), MapMode(MapUnit::Map100thMM)).Width();
+ pRefDev->SetFont(aOldFont);
+ pRefDev->SetMapMode(aOldMode);
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtWindow( *Application::GetDefaultDevice() );
+ pVirtWindow->SetMapMode(MapMode(MapUnit::MapPixel));
+ pPattern->fillFontOnly(aDefFont, pVirtWindow); // font color doesn't matter here
+ pVirtWindow->SetFont(aDefFont);
+ double nWindowWidth = pVirtWindow->GetTextWidth(aTestString) / ScGlobal::nScreenPPTX;
+ nWindowWidth = o3tl::convert(nWindowWidth, o3tl::Length::twip, o3tl::Length::mm100);
+
+ if (nPrinterWidth && nWindowWidth)
+ m_nPrtToScreenFactor = nPrinterWidth / nWindowWidth;
+ else
+ {
+ OSL_FAIL("GetTextSize returns 0 ??");
+ m_nPrtToScreenFactor = 1.0;
+ }
+}
+
+void ScDocShell::InitOptions(bool bForLoading) // called from InitNew and Load
+{
+ // Settings from the SpellCheckCfg get into Doc- and ViewOptions
+
+ LanguageType nDefLang, nCjkLang, nCtlLang;
+ bool bAutoSpell;
+ ScModule::GetSpellSettings( nDefLang, nCjkLang, nCtlLang, bAutoSpell );
+ ScModule* pScMod = SC_MOD();
+
+ ScDocOptions aDocOpt = pScMod->GetDocOptions();
+ ScFormulaOptions aFormulaOpt = pScMod->GetFormulaOptions();
+ ScViewOptions aViewOpt = pScMod->GetViewOptions();
+ aDocOpt.SetAutoSpell( bAutoSpell );
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ // two-digit year entry from Tools->Options->General
+ aDocOpt.SetYear2000(officecfg::Office::Common::DateFormat::TwoDigitYear::get());
+ }
+
+ if (bForLoading)
+ {
+ // #i112123# No style:decimal-places attribute means automatic decimals, not the configured default,
+ // so it must not be taken from the global options.
+ // Calculation settings are handled separately in ScXMLBodyContext::EndElement.
+ aDocOpt.SetStdPrecision( SvNumberFormatter::UNLIMITED_PRECISION );
+
+ // fdo#78294 The default null-date if
+ // <table:null-date table:date-value='...' />
+ // is absent is 1899-12-30 regardless what the configuration is set to.
+ // Import filters may override this value.
+ aDocOpt.SetDate( 30, 12, 1899);
+ }
+
+ m_pDocument->SetDocOptions( aDocOpt );
+ m_pDocument->SetViewOptions( aViewOpt );
+ SetFormulaOptions( aFormulaOpt, bForLoading );
+
+ // print options are now set directly before the printing
+
+ m_pDocument->SetLanguage( nDefLang, nCjkLang, nCtlLang );
+}
+
+Printer* ScDocShell::GetDocumentPrinter() // for OLE
+{
+ return m_pDocument->GetPrinter();
+}
+
+SfxPrinter* ScDocShell::GetPrinter(bool bCreateIfNotExist)
+{
+ return m_pDocument->GetPrinter(bCreateIfNotExist);
+}
+
+void ScDocShell::UpdateFontList()
+{
+ // pImpl->pFontList = new FontList( GetPrinter(), Application::GetDefaultDevice() );
+ m_pImpl->pFontList.reset(new FontList(GetRefDevice(), nullptr));
+ SvxFontListItem aFontListItem( m_pImpl->pFontList.get(), SID_ATTR_CHAR_FONTLIST );
+ PutItem( aFontListItem );
+
+ CalcOutputFactor();
+}
+
+OutputDevice* ScDocShell::GetRefDevice()
+{
+ return m_pDocument->GetRefDevice();
+}
+
+sal_uInt16 ScDocShell::SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ SfxPrinter *pOld = m_pDocument->GetPrinter( false );
+ if ( pOld && pOld->IsPrinting() )
+ return SFX_PRINTERROR_BUSY;
+
+ if (nDiffFlags & SfxPrinterChangeFlags::PRINTER)
+ {
+ if ( m_pDocument->GetPrinter() != pNewPrinter )
+ {
+ m_pDocument->SetPrinter( pNewPrinter );
+ m_pDocument->SetPrintOptions();
+
+ // MT: Use UpdateFontList: Will use Printer fonts only if needed!
+ /*
+ delete pImpl->pFontList;
+ pImpl->pFontList = new FontList( pNewPrinter, Application::GetDefaultDevice() );
+ SvxFontListItem aFontListItem( pImpl->pFontList, SID_ATTR_CHAR_FONTLIST );
+ PutItem( aFontListItem );
+
+ CalcOutputFactor();
+ */
+ if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() )
+ UpdateFontList();
+
+ ScModule* pScMod = SC_MOD();
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( this );
+ while (pFrame)
+ {
+ SfxViewShell* pSh = pFrame->GetViewShell();
+ if (ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(pSh))
+ {
+ ScInputHandler* pInputHdl = pScMod->GetInputHdl(pViewSh);
+ if (pInputHdl)
+ pInputHdl->UpdateRefDevice();
+ }
+ pFrame = SfxViewFrame::GetNext( *pFrame, this );
+ }
+ }
+ }
+ else if (nDiffFlags & SfxPrinterChangeFlags::JOBSETUP)
+ {
+ SfxPrinter* pOldPrinter = m_pDocument->GetPrinter();
+ if (pOldPrinter)
+ {
+ pOldPrinter->SetJobSetup( pNewPrinter->GetJobSetup() );
+
+ // #i6706# Call SetPrinter with the old printer again, so the drawing layer
+ // RefDevice is set (calling ReformatAllTextObjects and rebuilding charts),
+ // because the JobSetup (printer device settings) may affect text layout.
+ m_pDocument->SetPrinter( pOldPrinter );
+ CalcOutputFactor(); // also with the new settings
+ }
+ }
+
+ if (nDiffFlags & SfxPrinterChangeFlags::OPTIONS)
+ {
+ m_pDocument->SetPrintOptions(); //! from new printer ???
+ }
+
+ if (nDiffFlags & (SfxPrinterChangeFlags::CHG_ORIENTATION | SfxPrinterChangeFlags::CHG_SIZE))
+ {
+ OUString aStyle = m_pDocument->GetPageStyle( GetCurTab() );
+ ScStyleSheetPool* pStPl = m_pDocument->GetStyleSheetPool();
+ SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(pStPl->Find(aStyle, SfxStyleFamily::Page));
+ if (pStyleSheet)
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+
+ if (nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION)
+ {
+ const SvxPageItem& rOldItem = rSet.Get(ATTR_PAGE);
+ bool bWasLand = rOldItem.IsLandscape();
+ bool bNewLand = ( pNewPrinter->GetOrientation() == Orientation::Landscape );
+ if (bNewLand != bWasLand)
+ {
+ SvxPageItem aNewItem( rOldItem );
+ aNewItem.SetLandscape( bNewLand );
+ rSet.Put( aNewItem );
+
+ // flip size
+ Size aOldSize = rSet.Get(ATTR_PAGE_SIZE).GetSize();
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ Size aNewSize(aOldSize.Height(),aOldSize.Width());
+ SvxSizeItem aNewSItem(ATTR_PAGE_SIZE,aNewSize);
+ rSet.Put( aNewSItem );
+ }
+ }
+ if (nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE)
+ {
+ SvxSizeItem aPaperSizeItem( ATTR_PAGE_SIZE, SvxPaperInfo::GetPaperSize(pNewPrinter) );
+ rSet.Put( aPaperSizeItem );
+ }
+ }
+ }
+
+ PostPaint(0,0,0,m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB,PaintPartFlags::All);
+
+ return 0;
+}
+
+ScChangeAction* ScDocShell::GetChangeAction( const ScAddress& rPos )
+{
+ ScChangeTrack* pTrack = GetDocument().GetChangeTrack();
+ if (!pTrack)
+ return nullptr;
+
+ SCTAB nTab = rPos.Tab();
+
+ const ScChangeAction* pFound = nullptr;
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ ScChangeActionType eType = pAction->GetType();
+ //! ScViewUtil::IsActionShown( *pAction, *pSettings, *pDoc )...
+ if ( pAction->IsVisible() && eType != SC_CAT_DELETE_TABS )
+ {
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == nTab )
+ {
+ ScRange aRange = rBig.MakeRange( GetDocument() );
+
+ if ( eType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+
+ if ( aRange.Contains( rPos ) )
+ {
+ pFound = pAction; // the last one wins
+ }
+ }
+ if ( pAction->GetType() == SC_CAT_MOVE )
+ {
+ ScRange aRange =
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( GetDocument() );
+ if ( aRange.Contains( rPos ) )
+ {
+ pFound = pAction;
+ }
+ }
+ }
+ pAction = pAction->GetNext();
+ }
+
+ return const_cast<ScChangeAction*>(pFound);
+}
+
+void ScDocShell::SetChangeComment( ScChangeAction* pAction, const OUString& rComment )
+{
+ if (!pAction)
+ return;
+
+ pAction->SetComment( rComment );
+ //! Undo ???
+ SetDocumentModified();
+
+ // Dialog-Notify
+ ScChangeTrack* pTrack = GetDocument().GetChangeTrack();
+ if (pTrack)
+ {
+ sal_uLong nNumber = pAction->GetActionNumber();
+ pTrack->NotifyModified( ScChangeTrackMsgType::Change, nNumber, nNumber );
+ }
+}
+
+void ScDocShell::ExecuteChangeCommentDialog( ScChangeAction* pAction, weld::Window* pParent, bool bPrevNext)
+{
+ if (!pAction) return; // without action is nothing...
+
+ OUString aComment = pAction->GetComment();
+ OUString aAuthor = pAction->GetUser();
+
+ DateTime aDT = pAction->GetDateTime();
+ OUString aDate = ScGlobal::getLocaleData().getDate( aDT ) + " " +
+ ScGlobal::getLocaleData().getTime( aDT, false );
+
+ SfxItemSetFixed<SID_ATTR_POSTIT_AUTHOR, SID_ATTR_POSTIT_TEXT> aSet( GetPool() );
+
+ aSet.Put( SvxPostItTextItem ( aComment, SID_ATTR_POSTIT_TEXT ) );
+ aSet.Put( SvxPostItAuthorItem( aAuthor, SID_ATTR_POSTIT_AUTHOR ) );
+ aSet.Put( SvxPostItDateItem ( aDate, SID_ATTR_POSTIT_DATE ) );
+
+ std::unique_ptr<ScRedComDialog> pDlg(new ScRedComDialog( pParent, aSet,this,pAction,bPrevNext));
+
+ pDlg->Execute();
+}
+
+void ScDocShell::CompareDocument( ScDocument& rOtherDoc )
+{
+ ScChangeTrack* pTrack = m_pDocument->GetChangeTrack();
+ if ( pTrack && pTrack->GetFirst() )
+ {
+ //! there are changes -> inquiry if needs to be deleted
+ }
+
+ m_pDocument->EndChangeTracking();
+ m_pDocument->StartChangeTracking();
+
+ OUString aOldUser;
+ pTrack = m_pDocument->GetChangeTrack();
+ if ( pTrack )
+ {
+ aOldUser = pTrack->GetUser();
+
+ // check if comparing to same document
+
+ OUString aThisFile;
+ const SfxMedium* pThisMed = GetMedium();
+ if (pThisMed)
+ aThisFile = pThisMed->GetName();
+ OUString aOtherFile;
+ ScDocShell* pOtherSh = rOtherDoc.GetDocumentShell();
+ if (pOtherSh)
+ {
+ const SfxMedium* pOtherMed = pOtherSh->GetMedium();
+ if (pOtherMed)
+ aOtherFile = pOtherMed->GetName();
+ }
+ bool bSameDoc = ( aThisFile == aOtherFile && !aThisFile.isEmpty() );
+ if ( !bSameDoc )
+ {
+ // create change actions from comparing with the name of the user
+ // who last saved the document
+ // (only if comparing different documents)
+
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ GetModel()->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "no DocumentProperties");
+ OUString aDocUser = xDocProps->getModifiedBy();
+
+ if ( !aDocUser.isEmpty() )
+ pTrack->SetUser( aDocUser );
+ }
+ }
+
+ m_pDocument->CompareDocument( rOtherDoc );
+
+ pTrack = m_pDocument->GetChangeTrack();
+ if ( pTrack )
+ pTrack->SetUser( aOldUser );
+
+ PostPaintGridAll();
+ SetDocumentModified();
+}
+
+// Merge (combine documents)
+
+static bool lcl_Equal( const ScChangeAction* pA, const ScChangeAction* pB, bool bIgnore100Sec )
+{
+ return pA && pB &&
+ pA->GetActionNumber() == pB->GetActionNumber() &&
+ pA->GetType() == pB->GetType() &&
+ pA->GetUser() == pB->GetUser() &&
+ (bIgnore100Sec ?
+ pA->GetDateTimeUTC().IsEqualIgnoreNanoSec( pB->GetDateTimeUTC() ) :
+ pA->GetDateTimeUTC() == pB->GetDateTimeUTC());
+ // don't compare state if an old change has been accepted
+}
+
+static bool lcl_FindAction( ScDocument& rDoc, const ScChangeAction* pAction, ScDocument& rSearchDoc, const ScChangeAction* pFirstSearchAction, const ScChangeAction* pLastSearchAction, bool bIgnore100Sec )
+{
+ if ( !pAction || !pFirstSearchAction || !pLastSearchAction )
+ {
+ return false;
+ }
+
+ sal_uLong nLastSearchAction = pLastSearchAction->GetActionNumber();
+ const ScChangeAction* pA = pFirstSearchAction;
+ while ( pA && pA->GetActionNumber() <= nLastSearchAction )
+ {
+ if ( pAction->GetType() == pA->GetType() &&
+ pAction->GetUser() == pA->GetUser() &&
+ (bIgnore100Sec ?
+ pAction->GetDateTimeUTC().IsEqualIgnoreNanoSec( pA->GetDateTimeUTC() ) :
+ pAction->GetDateTimeUTC() == pA->GetDateTimeUTC() ) &&
+ pAction->GetBigRange() == pA->GetBigRange() )
+ {
+ OUString aActionDesc = pAction->GetDescription(rDoc, true);
+ OUString aADesc = pA->GetDescription(rSearchDoc, true);
+ if (aActionDesc == aADesc)
+ {
+ OSL_FAIL( "lcl_FindAction(): found equal action!" );
+ return true;
+ }
+ }
+ pA = pA->GetNext();
+ }
+
+ return false;
+}
+
+void ScDocShell::MergeDocument( ScDocument& rOtherDoc, bool bShared, bool bCheckDuplicates, sal_uLong nOffset, ScChangeActionMergeMap* pMergeMap, bool bInverseMap )
+{
+ ScTabViewShell* pViewSh = GetBestViewShell( false ); //! functions to the DocShell
+ if (!pViewSh)
+ return;
+
+ ScChangeTrack* pSourceTrack = rOtherDoc.GetChangeTrack();
+ if (!pSourceTrack)
+ return; //! nothing to do - error notification?
+
+ ScChangeTrack* pThisTrack = m_pDocument->GetChangeTrack();
+ if ( !pThisTrack )
+ { // turn on
+ m_pDocument->StartChangeTracking();
+ pThisTrack = m_pDocument->GetChangeTrack();
+ OSL_ENSURE(pThisTrack,"ChangeTracking not enabled?");
+ if ( !bShared )
+ {
+ // turn on visual RedLining
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges(true);
+ m_pDocument->SetChangeViewSettings(aChangeViewSet);
+ }
+ }
+
+ // include Nano seconds in compare?
+ bool bIgnore100Sec = !pSourceTrack->IsTimeNanoSeconds() ||
+ !pThisTrack->IsTimeNanoSeconds();
+
+ // find common initial position
+ sal_uLong nFirstNewNumber = 0;
+ const ScChangeAction* pSourceAction = pSourceTrack->GetFirst();
+ const ScChangeAction* pThisAction = pThisTrack->GetFirst();
+ // skip identical actions
+ while ( lcl_Equal( pSourceAction, pThisAction, bIgnore100Sec ) )
+ {
+ nFirstNewNumber = pSourceAction->GetActionNumber() + 1;
+ pSourceAction = pSourceAction->GetNext();
+ pThisAction = pThisAction->GetNext();
+ }
+ // pSourceAction and pThisAction now point to the first "own" actions
+ // The common actions before don't interest at all
+
+ //! Inquiry if the documents where equal before the change tracking !!!
+
+ const ScChangeAction* pFirstMergeAction = pSourceAction;
+ const ScChangeAction* pFirstSearchAction = pThisAction;
+
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ const ScChangeAction* pLastSearchAction = pThisTrack->GetLast();
+
+ // Create MergeChangeData from the following actions
+ sal_uLong nNewActionCount = 0;
+ const ScChangeAction* pCount = pSourceAction;
+ while ( pCount )
+ {
+ if ( bShared || !ScChangeTrack::MergeIgnore( *pCount, nFirstNewNumber ) )
+ ++nNewActionCount;
+ pCount = pCount->GetNext();
+ }
+ if (!nNewActionCount)
+ return; //! nothing to do - error notification?
+ // from here on no return
+
+ ScProgress aProgress( this, "...", nNewActionCount, true );
+
+ sal_uLong nLastMergeAction = pSourceTrack->GetLast()->GetActionNumber();
+ // UpdateReference-Undo, valid references for the last common state
+ pSourceTrack->MergePrepare( pFirstMergeAction, bShared );
+
+ // adjust MergeChangeData to all yet following actions in this document
+ // -> references valid for this document
+ while ( pThisAction )
+ {
+ // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
+ if ( !bShared || !ScChangeTrack::MergeIgnore( *pThisAction, nFirstNewNumber ) )
+ {
+ ScChangeActionType eType = pThisAction->GetType();
+ switch ( eType )
+ {
+ case SC_CAT_INSERT_COLS :
+ case SC_CAT_INSERT_ROWS :
+ case SC_CAT_INSERT_TABS :
+ pSourceTrack->AppendInsert( pThisAction->GetBigRange().MakeRange( GetDocument() ) );
+ break;
+ case SC_CAT_DELETE_COLS :
+ case SC_CAT_DELETE_ROWS :
+ case SC_CAT_DELETE_TABS :
+ {
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(pThisAction);
+ if ( pDel->IsTopDelete() && !pDel->IsTabDeleteCol() )
+ { // deleted table contains deleted cols, which are not
+ sal_uLong nStart, nEnd;
+ pSourceTrack->AppendDeleteRange(
+ pDel->GetOverAllRange().MakeRange( GetDocument() ), nullptr, nStart, nEnd );
+ }
+ }
+ break;
+ case SC_CAT_MOVE :
+ {
+ const ScChangeActionMove* pMove = static_cast<const ScChangeActionMove*>(pThisAction);
+ pSourceTrack->AppendMove( pMove->GetFromRange().MakeRange( GetDocument() ),
+ pMove->GetBigRange().MakeRange( GetDocument() ), nullptr );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ pThisAction = pThisAction->GetNext();
+ }
+
+ LockPaint(); // #i73877# no repainting after each action
+
+ // take over MergeChangeData into the current document
+ bool bHasRejected = false;
+ OUString aOldUser = pThisTrack->GetUser();
+ pThisTrack->SetUseFixDateTime( true );
+ ScMarkData& rMarkData = pViewSh->GetViewData().GetMarkData();
+ ScMarkData aOldMarkData( rMarkData );
+ pSourceAction = pFirstMergeAction;
+ while ( pSourceAction && pSourceAction->GetActionNumber() <= nLastMergeAction )
+ {
+ bool bMergeAction = false;
+ if ( bShared )
+ {
+ if ( !bCheckDuplicates || !lcl_FindAction( rOtherDoc, pSourceAction, *m_pDocument, pFirstSearchAction, pLastSearchAction, bIgnore100Sec ) )
+ {
+ bMergeAction = true;
+ }
+ }
+ else
+ {
+ if ( !ScChangeTrack::MergeIgnore( *pSourceAction, nFirstNewNumber ) )
+ {
+ bMergeAction = true;
+ }
+ }
+
+ if ( bMergeAction )
+ {
+ ScChangeActionType eSourceType = pSourceAction->GetType();
+ if ( !bShared && pSourceAction->IsDeletedIn() )
+ {
+ //! does it need to be determined yet if really deleted in
+ //! _this_ document?
+
+ // lies in a range, which was deleted in this document
+ // -> is omitted
+ //! ??? revert deletion action ???
+ //! ??? save action somewhere else ???
+#if OSL_DEBUG_LEVEL > 0
+ OUString aValue;
+ if ( eSourceType == SC_CAT_CONTENT )
+ aValue = static_cast<const ScChangeActionContent*>(pSourceAction)->GetNewString( m_pDocument.get() );
+ SAL_WARN( "sc", aValue << " omitted");
+#endif
+ }
+ else
+ {
+ //! Take over date/author/comment of the source action!
+
+ pThisTrack->SetUser( pSourceAction->GetUser() );
+ pThisTrack->SetFixDateTimeUTC( pSourceAction->GetDateTimeUTC() );
+ sal_uLong nOldActionMax = pThisTrack->GetActionMax();
+
+ bool bExecute = true;
+ sal_uLong nReject = pSourceAction->GetRejectAction();
+ if ( nReject )
+ {
+ if ( bShared )
+ {
+ if ( nReject >= nFirstNewNumber )
+ {
+ nReject += nOffset;
+ }
+ ScChangeAction* pOldAction = pThisTrack->GetAction( nReject );
+ if ( pOldAction && pOldAction->IsVirgin() )
+ {
+ pThisTrack->Reject( pOldAction );
+ bHasRejected = true;
+ bExecute = false;
+ }
+ }
+ else
+ {
+ // decline old action (of the common ones)
+ ScChangeAction* pOldAction = pThisTrack->GetAction( nReject );
+ if (pOldAction && pOldAction->GetState() == SC_CAS_VIRGIN)
+ {
+ //! what happens at actions, which were accepted in this document???
+ //! error notification or what???
+ //! or execute reject change normally
+
+ pThisTrack->Reject(pOldAction);
+ bHasRejected = true; // for Paint
+ }
+ bExecute = false;
+ }
+ }
+
+ if ( bExecute )
+ {
+ // execute normally
+ ScRange aSourceRange = pSourceAction->GetBigRange().MakeRange( GetDocument() );
+ rMarkData.SelectOneTable( aSourceRange.aStart.Tab() );
+ switch ( eSourceType )
+ {
+ case SC_CAT_CONTENT:
+ {
+ //! Test if it was at the very bottom in the document, then automatic
+ //! row insert ???
+
+ OSL_ENSURE( aSourceRange.aStart == aSourceRange.aEnd, "huch?" );
+ ScAddress aPos = aSourceRange.aStart;
+ OUString aValue = static_cast<const ScChangeActionContent*>(pSourceAction)->GetNewString( m_pDocument.get() );
+ ScMatrixMode eMatrix = ScMatrixMode::NONE;
+ const ScCellValue& rCell = static_cast<const ScChangeActionContent*>(pSourceAction)->GetNewCell();
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ eMatrix = rCell.getFormula()->GetMatrixFlag();
+ switch ( eMatrix )
+ {
+ case ScMatrixMode::NONE :
+ pViewSh->EnterData( aPos.Col(), aPos.Row(), aPos.Tab(), aValue );
+ break;
+ case ScMatrixMode::Formula :
+ {
+ SCCOL nCols;
+ SCROW nRows;
+ rCell.getFormula()->GetMatColsRows(nCols, nRows);
+ aSourceRange.aEnd.SetCol( aPos.Col() + nCols - 1 );
+ aSourceRange.aEnd.SetRow( aPos.Row() + nRows - 1 );
+ aValue = aValue.copy(1, aValue.getLength()-2); // remove the 1st and last characters.
+ GetDocFunc().EnterMatrix( aSourceRange,
+ nullptr, nullptr, aValue, false, false,
+ OUString(), formula::FormulaGrammar::GRAM_DEFAULT );
+ }
+ break;
+ case ScMatrixMode::Reference : // do nothing
+ break;
+ }
+ }
+ break;
+ case SC_CAT_INSERT_TABS :
+ {
+ OUString aName;
+ m_pDocument->CreateValidTabName( aName );
+ (void)GetDocFunc().InsertTable( aSourceRange.aStart.Tab(), aName, true, false );
+ }
+ break;
+ case SC_CAT_INSERT_ROWS:
+ (void)GetDocFunc().InsertCells( aSourceRange, nullptr, INS_INSROWS_BEFORE, true, false );
+ break;
+ case SC_CAT_INSERT_COLS:
+ (void)GetDocFunc().InsertCells( aSourceRange, nullptr, INS_INSCOLS_BEFORE, true, false );
+ break;
+ case SC_CAT_DELETE_TABS :
+ (void)GetDocFunc().DeleteTable( aSourceRange.aStart.Tab(), true );
+ break;
+ case SC_CAT_DELETE_ROWS:
+ {
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(pSourceAction);
+ if ( pDel->IsTopDelete() )
+ {
+ aSourceRange = pDel->GetOverAllRange().MakeRange( GetDocument() );
+ (void)GetDocFunc().DeleteCells( aSourceRange, nullptr, DelCellCmd::Rows, false );
+
+ // #i101099# [Collaboration] Changes are not correctly shown
+ if ( bShared )
+ {
+ ScChangeAction* pAct = pThisTrack->GetLast();
+ if ( pAct && pAct->GetType() == eSourceType && pAct->IsDeletedIn() && !pSourceAction->IsDeletedIn() )
+ {
+ pAct->RemoveAllDeletedIn();
+ }
+ }
+ }
+ }
+ break;
+ case SC_CAT_DELETE_COLS:
+ {
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(pSourceAction);
+ if ( pDel->IsTopDelete() && !pDel->IsTabDeleteCol() )
+ { // deleted table contains deleted cols, which are not
+ aSourceRange = pDel->GetOverAllRange().MakeRange( GetDocument() );
+ (void)GetDocFunc().DeleteCells( aSourceRange, nullptr, DelCellCmd::Cols, false );
+ }
+ }
+ break;
+ case SC_CAT_MOVE :
+ {
+ const ScChangeActionMove* pMove = static_cast<const ScChangeActionMove*>(pSourceAction);
+ ScRange aFromRange( pMove->GetFromRange().MakeRange( GetDocument() ) );
+ (void)GetDocFunc().MoveBlock( aFromRange,
+ aSourceRange.aStart, true, true, false, false );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ const OUString& rComment = pSourceAction->GetComment();
+ if ( !rComment.isEmpty() )
+ {
+ ScChangeAction* pAct = pThisTrack->GetLast();
+ if ( pAct && pAct->GetActionNumber() > nOldActionMax )
+ pAct->SetComment( rComment );
+ else
+ OSL_FAIL( "MergeDocument: what to do with the comment?!?" );
+ }
+
+ // adjust references
+ pSourceTrack->MergeOwn( const_cast<ScChangeAction*>(pSourceAction), nFirstNewNumber, bShared );
+
+ // merge action state
+ if ( bShared && !pSourceAction->IsRejected() )
+ {
+ ScChangeAction* pAct = pThisTrack->GetLast();
+ if ( pAct && pAct->GetActionNumber() > nOldActionMax )
+ {
+ ScChangeTrack::MergeActionState( pAct, pSourceAction );
+ }
+ }
+
+ // fill merge map
+ if ( bShared && pMergeMap )
+ {
+ ScChangeAction* pAct = pThisTrack->GetLast();
+ if ( pAct && pAct->GetActionNumber() > nOldActionMax )
+ {
+ sal_uLong nActionMax = pAct->GetActionNumber();
+ sal_uLong nActionCount = nActionMax - nOldActionMax;
+ sal_uLong nAction = nActionMax - nActionCount + 1;
+ sal_uLong nSourceAction = pSourceAction->GetActionNumber() - nActionCount + 1;
+ while ( nAction <= nActionMax )
+ {
+ if ( bInverseMap )
+ {
+ (*pMergeMap)[ nAction++ ] = nSourceAction++;
+ }
+ else
+ {
+ (*pMergeMap)[ nSourceAction++ ] = nAction++;
+ }
+ }
+ }
+ }
+ }
+ aProgress.SetStateCountDown( --nNewActionCount );
+ }
+ pSourceAction = pSourceAction->GetNext();
+ }
+
+ rMarkData = std::move(aOldMarkData);
+ pThisTrack->SetUser(aOldUser);
+ pThisTrack->SetUseFixDateTime( false );
+
+ pSourceTrack->Clear(); //! this one is bungled now
+
+ if (bHasRejected)
+ PostPaintGridAll(); // Reject() doesn't paint itself
+
+ UnlockPaint();
+}
+
+bool ScDocShell::MergeSharedDocument( ScDocShell* pSharedDocShell )
+{
+ if ( !pSharedDocShell )
+ {
+ return false;
+ }
+
+ ScChangeTrack* pThisTrack = m_pDocument->GetChangeTrack();
+ if ( !pThisTrack )
+ {
+ return false;
+ }
+
+ ScDocument& rSharedDoc = pSharedDocShell->GetDocument();
+ ScChangeTrack* pSharedTrack = rSharedDoc.GetChangeTrack();
+ if ( !pSharedTrack )
+ {
+ return false;
+ }
+
+ // reset show changes
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges( false );
+ m_pDocument->SetChangeViewSettings( aChangeViewSet );
+
+ // find first merge action in this document
+ bool bIgnore100Sec = !pThisTrack->IsTimeNanoSeconds() || !pSharedTrack->IsTimeNanoSeconds();
+ ScChangeAction* pThisAction = pThisTrack->GetFirst();
+ ScChangeAction* pSharedAction = pSharedTrack->GetFirst();
+ while ( lcl_Equal( pThisAction, pSharedAction, bIgnore100Sec ) )
+ {
+ pThisAction = pThisAction->GetNext();
+ pSharedAction = pSharedAction->GetNext();
+ }
+
+ if ( pSharedAction )
+ {
+ if ( pThisAction )
+ {
+ // merge own changes into shared document
+ sal_uLong nActStartShared = pSharedAction->GetActionNumber();
+ sal_uLong nActEndShared = pSharedTrack->GetActionMax();
+ std::optional<ScDocument> pTmpDoc(std::in_place);
+ for ( sal_Int32 nIndex = 0; nIndex < m_pDocument->GetTableCount(); ++nIndex )
+ {
+ OUString sTabName;
+ pTmpDoc->CreateValidTabName( sTabName );
+ pTmpDoc->InsertTab( SC_TAB_APPEND, sTabName );
+ }
+ m_pDocument->GetChangeTrack()->Clone( &*pTmpDoc );
+ ScChangeActionMergeMap aOwnInverseMergeMap;
+ pSharedDocShell->MergeDocument( *pTmpDoc, true, true, 0, &aOwnInverseMergeMap, true );
+ pTmpDoc.reset();
+ sal_uLong nActStartOwn = nActEndShared + 1;
+ sal_uLong nActEndOwn = pSharedTrack->GetActionMax();
+
+ // find conflicts
+ ScConflictsList aConflictsList;
+ ScConflictsFinder aFinder( pSharedTrack, nActStartShared, nActEndShared, nActStartOwn, nActEndOwn, aConflictsList );
+ if ( aFinder.Find() )
+ {
+ ScConflictsListHelper::TransformConflictsList( aConflictsList, nullptr, &aOwnInverseMergeMap );
+ bool bLoop = true;
+ while ( bLoop )
+ {
+ bLoop = false;
+ weld::Window* pWin = GetActiveDialogParent();
+ ScConflictsDlg aDlg(pWin, GetViewData(), &rSharedDoc, aConflictsList);
+ if (aDlg.run() == RET_CANCEL)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_DOC_WILLNOTBESAVED)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_YES)
+ {
+ return false;
+ }
+ else
+ {
+ bLoop = true;
+ }
+ }
+ }
+ }
+
+ // undo own changes in shared document
+ pSharedTrack->Undo( nActStartOwn, nActEndOwn );
+
+ // clone change track for merging into own document
+ pTmpDoc.emplace();
+ for ( sal_Int32 nIndex = 0; nIndex < m_pDocument->GetTableCount(); ++nIndex )
+ {
+ OUString sTabName;
+ pTmpDoc->CreateValidTabName( sTabName );
+ pTmpDoc->InsertTab( SC_TAB_APPEND, sTabName );
+ }
+ pThisTrack->Clone( &*pTmpDoc );
+
+ // undo own changes since last save in own document
+ sal_uLong nStartShared = pThisAction->GetActionNumber();
+ ScChangeAction* pAction = pThisTrack->GetLast();
+ while ( pAction && pAction->GetActionNumber() >= nStartShared )
+ {
+ pThisTrack->Reject( pAction, true );
+ pAction = pAction->GetPrev();
+ }
+
+ // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
+ pThisTrack->Undo( nStartShared, pThisTrack->GetActionMax(), true );
+
+ // merge shared changes into own document
+ ScChangeActionMergeMap aSharedMergeMap;
+ MergeDocument( rSharedDoc, true, true, 0, &aSharedMergeMap );
+ sal_uLong nEndShared = pThisTrack->GetActionMax();
+
+ // resolve conflicts for shared non-content actions
+ if ( !aConflictsList.empty() )
+ {
+ ScConflictsListHelper::TransformConflictsList( aConflictsList, &aSharedMergeMap, nullptr );
+ ScConflictsResolver aResolver( pThisTrack, aConflictsList );
+ pAction = pThisTrack->GetAction( nEndShared );
+ while ( pAction && pAction->GetActionNumber() >= nStartShared )
+ {
+ aResolver.HandleAction( pAction, true /*bIsSharedAction*/,
+ false /*bHandleContentAction*/, true /*bHandleNonContentAction*/ );
+ pAction = pAction->GetPrev();
+ }
+ }
+ nEndShared = pThisTrack->GetActionMax();
+
+ // only show changes from shared document
+ aChangeViewSet.SetShowChanges( true );
+ aChangeViewSet.SetShowAccepted( true );
+ aChangeViewSet.SetHasActionRange();
+ aChangeViewSet.SetTheActionRange( nStartShared, nEndShared );
+ m_pDocument->SetChangeViewSettings( aChangeViewSet );
+
+ // merge own changes back into own document
+ sal_uLong nStartOwn = nEndShared + 1;
+ ScChangeActionMergeMap aOwnMergeMap;
+ MergeDocument( *pTmpDoc, true, true, nEndShared - nStartShared + 1, &aOwnMergeMap );
+ pTmpDoc.reset();
+ sal_uLong nEndOwn = pThisTrack->GetActionMax();
+
+ // resolve conflicts for shared content actions and own actions
+ if ( !aConflictsList.empty() )
+ {
+ ScConflictsListHelper::TransformConflictsList( aConflictsList, nullptr, &aOwnMergeMap );
+ ScConflictsResolver aResolver( pThisTrack, aConflictsList );
+ pAction = pThisTrack->GetAction( nEndShared );
+ while ( pAction && pAction->GetActionNumber() >= nStartShared )
+ {
+ aResolver.HandleAction( pAction, true /*bIsSharedAction*/,
+ true /*bHandleContentAction*/, false /*bHandleNonContentAction*/ );
+ pAction = pAction->GetPrev();
+ }
+
+ pAction = pThisTrack->GetAction( nEndOwn );
+ while ( pAction && pAction->GetActionNumber() >= nStartOwn )
+ {
+ aResolver.HandleAction( pAction, false /*bIsSharedAction*/,
+ true /*bHandleContentAction*/, true /*bHandleNonContentAction*/ );
+ pAction = pAction->GetPrev();
+ }
+ }
+ }
+ else
+ {
+ // merge shared changes into own document
+ sal_uLong nStartShared = pThisTrack->GetActionMax() + 1;
+ MergeDocument( rSharedDoc, true, true );
+ sal_uLong nEndShared = pThisTrack->GetActionMax();
+
+ // only show changes from shared document
+ aChangeViewSet.SetShowChanges( true );
+ aChangeViewSet.SetShowAccepted( true );
+ aChangeViewSet.SetHasActionRange();
+ aChangeViewSet.SetTheActionRange( nStartShared, nEndShared );
+ m_pDocument->SetChangeViewSettings( aChangeViewSet );
+ }
+
+ // update view
+ PostPaintExtras();
+ PostPaintGridAll();
+
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_DOC_UPDATED)));
+ xInfoBox->run();
+ }
+
+ return ( pThisAction != nullptr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh4.cxx b/sc/source/ui/docshell/docsh4.cxx
new file mode 100644
index 0000000000..4de5b19501
--- /dev/null
+++ b/sc/source/ui/docshell/docsh4.cxx
@@ -0,0 +1,2788 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <scitems.hxx>
+#include <editeng/flstitem.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/strings.hrc>
+#include <svtools/ehdl.hxx>
+#include <svtools/langtab.hxx>
+#include <basic/sbxcore.hxx>
+#include <basic/sberrors.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svx/ofaitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svx/drawitem.hxx>
+#include <svx/fmshell.hxx>
+#include <sfx2/passwd.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <svl/PasswordHelper.hxx>
+#include <svl/documentlockfile.hxx>
+#include <svl/sharecontrolfile.hxx>
+#include <tools/json_writer.hxx>
+#include <unotools/securityoptions.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <docuno.hxx>
+
+#include <docsh.hxx>
+#include "docshimp.hxx"
+#include <docfunc.hxx>
+#include <scres.hrc>
+#include <strings.hrc>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+#include <appoptio.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <dbdocfun.hxx>
+#include <printfun.hxx>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <impex.hxx>
+#include <undodat.hxx>
+#include <undocell.hxx>
+#include <inputhdl.hxx>
+#include <dbdata.hxx>
+#include <servobj.hxx>
+#include <rangenam.hxx>
+#include <scmod.hxx>
+#include <chgviset.hxx>
+#include <reffact.hxx>
+#include <chartlis.hxx>
+#include <chartpos.hxx>
+#include <tablink.hxx>
+#include <drwlayer.hxx>
+#include <docoptio.hxx>
+#include <undostyl.hxx>
+#include <rangeseq.hxx>
+#include <chgtrack.hxx>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <scresid.hxx>
+#include <scabstdlg.hxx>
+#include <sharedocdlg.hxx>
+#include <conditio.hxx>
+#include <sheetevents.hxx>
+#include <formulacell.hxx>
+#include <documentlinkmgr.hxx>
+#include <memory>
+#include <sfx2/notebookbar/SfxNotebookBar.hxx>
+#include <helpids.h>
+#include <editeng/eeitem.hxx>
+#include <editeng/langitem.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <svx/xdef.hxx>
+
+using namespace ::com::sun::star;
+
+void ScDocShell::SetInitialLinkUpdate( const SfxMedium* pMed )
+{
+ if (pMed)
+ {
+ const SfxUInt16Item* pUpdateDocItem = pMed->GetItemSet().GetItem(SID_UPDATEDOCMODE, false);
+ m_nCanUpdate = pUpdateDocItem ? pUpdateDocItem->GetValue() : css::document::UpdateDocMode::NO_UPDATE;
+ }
+
+ // GetLinkUpdateModeState() evaluates m_nCanUpdate so that must have
+ // been set first. Do not override an already forbidden LinkUpdate (the
+ // default is allow).
+ comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer();
+ if (rEmbeddedObjectContainer.getUserAllowsLinkUpdate())
+ {
+ // For anything else than LM_ALWAYS we need user confirmation.
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate( GetLinkUpdateModeState() == LM_ALWAYS);
+ }
+}
+
+ScLkUpdMode ScDocShell::GetLinkUpdateModeState() const
+{
+ ScLkUpdMode nSet;
+ if (m_nCanUpdate == css::document::UpdateDocMode::NO_UPDATE)
+ nSet = LM_NEVER;
+ else if (m_nCanUpdate == css::document::UpdateDocMode::FULL_UPDATE)
+ nSet = LM_ALWAYS;
+ else
+ {
+ nSet = GetDocument().GetLinkMode();
+ if (nSet == LM_UNKNOWN)
+ {
+ ScAppOptions aAppOptions = SC_MOD()->GetAppOptions();
+ nSet = aAppOptions.GetLinkMode();
+ }
+ }
+
+ if (nSet == LM_ALWAYS
+ && !(SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks(
+ GetMedium() == nullptr ? OUString() : GetMedium()->GetName())
+ || (IsDocShared()
+ && SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks(
+ GetSharedFileURL()))))
+ {
+ nSet = LM_ON_DEMAND;
+ }
+ if (m_nCanUpdate == css::document::UpdateDocMode::QUIET_UPDATE
+ && nSet == LM_ON_DEMAND)
+ {
+ nSet = LM_NEVER;
+ }
+
+ return nSet;
+}
+
+void ScDocShell::AllowLinkUpdate()
+{
+ m_pDocument->SetLinkFormulaNeedingCheck(false);
+ getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true);
+}
+
+void ScDocShell::ReloadAllLinks()
+{
+ AllowLinkUpdate();
+
+ ReloadTabLinks();
+ weld::Window *pDialogParent = GetActiveDialogParent();
+ m_pDocument->UpdateExternalRefLinks(pDialogParent);
+
+ bool bAnyDde = m_pDocument->GetDocLinkManager().updateDdeOrOleOrWebServiceLinks(pDialogParent);
+
+ if (bAnyDde)
+ {
+ // calculate formulas and paint like in the TrackTimeHdl
+ m_pDocument->TrackFormulas();
+ Broadcast(SfxHint(SfxHintId::ScDataChanged));
+
+ // Should FID_DATACHANGED become asynchronous some time
+ // (e.g., with Invalidate at Window), an update needs to be forced here.
+ }
+
+ m_pDocument->UpdateAreaLinks();
+}
+
+IMPL_LINK( ScDocShell, ReloadAllLinksHdl, weld::Button&, rButton, void )
+{
+ ScDocument& rDoc = GetDocument();
+ if (rDoc.HasLinkFormulaNeedingCheck() && rDoc.GetDocLinkManager().hasExternalRefLinks())
+ {
+ // If we have WEBSERVICE/Dde link and other external links in the document, it might indicate some
+ // exfiltration attempt, add *another* warning about this on top of the "Security Warning"
+ // shown in the infobar before they got here.
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(&rButton,
+ VclMessageType::Warning, VclButtonsType::YesNo,
+ ScResId(STR_TRUST_DOCUMENT_WARNING)));
+ xQueryBox->set_secondary_text(ScResId(STR_WEBSERVICE_WITH_LINKS_WARNING));
+ xQueryBox->set_default_response(RET_NO);
+ if (xQueryBox->run() != RET_YES)
+ return;
+ }
+
+ ReloadAllLinks();
+
+ ScTabViewShell* pViewSh = GetBestViewShell();
+ SfxViewFrame* pViewFrame = pViewSh ? pViewSh->GetFrame() : nullptr;
+ if (pViewFrame)
+ pViewFrame->RemoveInfoBar(u"enablecontent");
+ SAL_WARN_IF(!pViewFrame, "sc", "expected there to be a ViewFrame");
+}
+
+namespace
+{
+ class LinkHelp
+ {
+ public:
+ DECL_STATIC_LINK(LinkHelp, DispatchHelpLinksHdl, weld::Button&, void);
+ };
+}
+
+IMPL_STATIC_LINK(LinkHelp, DispatchHelpLinksHdl, weld::Button&, rBtn, void)
+{
+ if (Help* pHelp = Application::GetHelp())
+ pHelp->Start(HID_UPDATE_LINK_WARNING, &rBtn);
+}
+
+void ScDocShell::Execute( SfxRequest& rReq )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ SfxBindings* pBindings = GetViewBindings();
+ bool bUndo (m_pDocument->IsUndoEnabled());
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_SC_SETTEXT:
+ {
+ const SfxPoolItem* pColItem;
+ const SfxPoolItem* pRowItem;
+ const SfxPoolItem* pTabItem;
+ const SfxPoolItem* pTextItem;
+ if( pReqArgs && pReqArgs->HasItem( FN_PARAM_1, &pColItem ) &&
+ pReqArgs->HasItem( FN_PARAM_2, &pRowItem ) &&
+ pReqArgs->HasItem( FN_PARAM_3, &pTabItem ) &&
+ pReqArgs->HasItem( SID_SC_SETTEXT, &pTextItem ) )
+ {
+ // parameters are 1-based !!!
+ SCCOL nCol = static_cast<const SfxInt16Item*>(pColItem)->GetValue() - 1;
+ SCROW nRow = static_cast<const SfxInt32Item*>(pRowItem)->GetValue() - 1;
+ SCTAB nTab = static_cast<const SfxInt16Item*>(pTabItem)->GetValue() - 1;
+
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ if ( m_pDocument->ValidCol(nCol) && m_pDocument->ValidRow(nRow) && ValidTab(nTab,nTabCount) )
+ {
+ if ( m_pDocument->IsBlockEditable( nTab, nCol,nRow, nCol, nRow ) )
+ {
+ OUString aVal = static_cast<const SfxStringItem*>(pTextItem)->GetValue();
+ m_pDocument->SetString( nCol, nRow, nTab, aVal );
+
+ PostPaintCell( nCol, nRow, nTab );
+ SetDocumentModified();
+
+ rReq.Done();
+ break;
+ }
+ else // protected cell
+ {
+#if HAVE_FEATURE_SCRIPTING
+ SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); //! which error ?
+#endif
+ break;
+ }
+ }
+ }
+#if HAVE_FEATURE_SCRIPTING
+ SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
+#endif
+ }
+ break;
+
+ case SID_SBA_IMPORT:
+ {
+ if (pReqArgs)
+ {
+ const SfxPoolItem* pItem;
+ svx::ODataAccessDescriptor aDesc;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ {
+ uno::Any aAny = static_cast<const SfxUnoAnyItem*>(pItem)->GetValue();
+ uno::Sequence<beans::PropertyValue> aProperties;
+ if ( aAny >>= aProperties )
+ aDesc.initializeFrom( aProperties );
+ }
+
+ OUString sTarget;
+ if ( pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET )
+ sTarget = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ bool bIsNewArea = true; // Default sal_True (no inquiry)
+ if ( pReqArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET )
+ bIsNewArea = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // if necessary, create new database area
+ bool bMakeArea = false;
+ if (bIsNewArea)
+ {
+ ScDBCollection* pDBColl = m_pDocument->GetDBCollection();
+ if ( !pDBColl || !pDBColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(sTarget)) )
+ {
+ ScAddress aPos;
+ if ( aPos.Parse( sTarget, *m_pDocument, m_pDocument->GetAddressConvention() ) & ScRefFlags::VALID )
+ {
+ bMakeArea = true;
+ if (bUndo)
+ {
+ OUString aStrImport = ScResId( STR_UNDO_IMPORTDATA );
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ GetUndoManager()->EnterListAction( aStrImport, aStrImport, 0, nViewShellId );
+ }
+
+ ScDBData* pDBData = GetDBData( ScRange(aPos), SC_DB_IMPORT, ScGetDBSelection::Keep );
+ OSL_ENSURE(pDBData, "Cannot create DB data");
+ sTarget = pDBData->GetName();
+ }
+ }
+ }
+
+ // inquire, before old DB range gets overwritten
+ bool bDo = true;
+ if (!bIsNewArea)
+ {
+ OUString aTemplate = ScResId( STR_IMPORT_REPLACE );
+ OUString aMessage = o3tl::getToken(aTemplate, 0, '#' )
+ + sTarget
+ + o3tl::getToken(aTemplate, 1, '#' );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMessage));
+ xQueryBox->set_default_response(RET_YES);
+ bDo = xQueryBox->run() == RET_YES;
+ }
+
+ if (bDo)
+ {
+ ScDBDocFunc(*this).UpdateImport( sTarget, aDesc );
+ rReq.Done();
+
+ // UpdateImport also updates the internal operations
+ }
+ else
+ rReq.Ignore();
+
+ if ( bMakeArea && bUndo)
+ GetUndoManager()->LeaveListAction();
+ }
+ else
+ {
+ OSL_FAIL( "arguments expected" );
+ }
+ }
+ break;
+
+ case SID_CHART_SOURCE:
+ case SID_CHART_ADDSOURCE:
+ if (pReqArgs)
+ {
+ ScDocument& rDoc = GetDocument();
+ const SfxPoolItem* pItem;
+ OUString aChartName, aRangeName;
+
+ ScRange aSingleRange;
+ ScRangeListRef aRangeListRef;
+ bool bMultiRange = false;
+
+ bool bColHeaders = true;
+ bool bRowHeaders = true;
+ bool bColInit = false;
+ bool bRowInit = false;
+ bool bAddRange = (nSlot == SID_CHART_ADDSOURCE);
+
+ if( const SfxStringItem* pChartItem = pReqArgs->GetItemIfSet( SID_CHART_NAME ) )
+ aChartName = pChartItem->GetValue();
+
+ if( const SfxStringItem* pChartItem = pReqArgs->GetItemIfSet( SID_CHART_SOURCE ) )
+ aRangeName = pChartItem->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ {
+ bColHeaders = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ bColInit = true;
+ }
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ {
+ bRowHeaders = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ bRowInit = true;
+ }
+
+ ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0);
+ bool bValid = (aSingleRange.ParseAny(aRangeName, rDoc, aDetails) & ScRefFlags::VALID) != ScRefFlags::ZERO;
+ if (!bValid)
+ {
+ aRangeListRef = new ScRangeList;
+ aRangeListRef->Parse( aRangeName, rDoc, rDoc.GetAddressConvention());
+ if ( !aRangeListRef->empty() )
+ {
+ bMultiRange = true;
+ aSingleRange = aRangeListRef->front(); // for header
+ bValid = true;
+ }
+ else
+ aRangeListRef.clear();
+ }
+
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if (pViewSh && bValid && !aChartName.isEmpty() )
+ {
+ weld::Window* pParent = pViewSh->GetFrameWeld();
+
+ SCCOL nCol1 = aSingleRange.aStart.Col();
+ SCROW nRow1 = aSingleRange.aStart.Row();
+ SCCOL nCol2 = aSingleRange.aEnd.Col();
+ SCROW nRow2 = aSingleRange.aEnd.Row();
+ SCTAB nTab = aSingleRange.aStart.Tab();
+
+ //! limit always or not at all ???
+ if (!bMultiRange)
+ m_pDocument->LimitChartArea( nTab, nCol1,nRow1, nCol2,nRow2 );
+
+ // Dialog for column/row headers
+ bool bOk = true;
+ if ( !bAddRange && ( !bColInit || !bRowInit ) )
+ {
+ ScChartPositioner aChartPositioner( *m_pDocument, nTab, nCol1,nRow1, nCol2,nRow2 );
+ if (!bColInit)
+ bColHeaders = aChartPositioner.HasColHeaders();
+ if (!bRowInit)
+ bRowHeaders = aChartPositioner.HasRowHeaders();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScColRowLabelDlg> pDlg(pFact->CreateScColRowLabelDlg(pParent, bRowHeaders, bColHeaders));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ bColHeaders = pDlg->IsRow();
+ bRowHeaders = pDlg->IsCol();
+
+ rReq.AppendItem(SfxBoolItem(FN_PARAM_1, bColHeaders));
+ rReq.AppendItem(SfxBoolItem(FN_PARAM_2, bRowHeaders));
+ }
+ else
+ bOk = false;
+ }
+
+ if (bOk) // execute
+ {
+ if (bMultiRange)
+ {
+ if (bUndo)
+ {
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoChartData>( this, aChartName, aRangeListRef,
+ bColHeaders, bRowHeaders, bAddRange ) );
+ }
+ m_pDocument->UpdateChartArea( aChartName, aRangeListRef,
+ bColHeaders, bRowHeaders, bAddRange );
+ }
+ else
+ {
+ ScRange aNewRange( nCol1,nRow1,nTab, nCol2,nRow2,nTab );
+ if (bUndo)
+ {
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoChartData>( this, aChartName, aNewRange,
+ bColHeaders, bRowHeaders, bAddRange ) );
+ }
+ m_pDocument->UpdateChartArea( aChartName, aNewRange,
+ bColHeaders, bRowHeaders, bAddRange );
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("UpdateChartArea: no ViewShell or wrong data");
+ }
+ rReq.Done();
+ }
+ else
+ {
+ OSL_FAIL("SID_CHART_SOURCE without arguments");
+ }
+ break;
+
+ case FID_AUTO_CALC:
+ {
+ bool bNewVal;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) )
+ bNewVal = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ else
+ bNewVal = !m_pDocument->GetAutoCalc(); // Toggle for menu
+ m_pDocument->SetAutoCalc( bNewVal );
+ SetDocumentModified();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_AUTO_CALC );
+ }
+ rReq.AppendItem( SfxBoolItem( FID_AUTO_CALC, bNewVal ) );
+ rReq.Done();
+ }
+ break;
+ case FID_RECALC:
+ DoRecalc( rReq.IsAPI() );
+ rReq.Done();
+ break;
+ case FID_HARD_RECALC:
+ DoHardRecalc();
+ rReq.Done();
+ break;
+ case SID_UPDATETABLINKS:
+ {
+ ScLkUpdMode nSet = GetLinkUpdateModeState();
+
+ if (nSet == LM_ALWAYS)
+ {
+ ReloadAllLinks();
+ rReq.Done();
+ }
+ else if (nSet == LM_NEVER)
+ {
+ getEmbeddedObjectContainer().setUserAllowsLinkUpdate(false);
+ rReq.Ignore();
+ }
+ else if (nSet == LM_ON_DEMAND)
+ {
+ ScTabViewShell* pViewSh = GetBestViewShell();
+ SfxViewFrame* pViewFrame = pViewSh ? pViewSh->GetFrame() : nullptr;
+ if (pViewFrame)
+ {
+ pViewFrame->RemoveInfoBar(u"enablecontent");
+ auto pInfoBar = pViewFrame->AppendInfoBar("enablecontent", SfxResId(RID_SECURITY_WARNING_TITLE),
+ ScResId(STR_RELOAD_TABLES), InfobarType::WARNING);
+ if (pInfoBar)
+ {
+ weld::Button& rHelpBtn = pInfoBar->addButton();
+ rHelpBtn.set_label(GetStandardText(StandardButtonType::Help).replaceFirst("~", ""));
+ rHelpBtn.connect_clicked(LINK(nullptr, LinkHelp, DispatchHelpLinksHdl));
+ weld::Button& rBtn = pInfoBar->addButton();
+ rBtn.set_label(ScResId(STR_ENABLE_CONTENT));
+ rBtn.set_tooltip_text(ScResId(STR_ENABLE_CONTENT_TOOLTIP));
+ rBtn.connect_clicked(LINK(this, ScDocShell, ReloadAllLinksHdl));
+
+ // when active content is disabled the "Allow updating" button has no functionality.
+ if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
+ {
+ rBtn.set_tooltip_text(ScResId(STR_ENABLE_CONTENT_TOOLTIP_DISABLED));
+ rBtn.set_sensitive(false);
+ }
+ }
+ }
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_REIMPORT_AFTER_LOAD:
+ {
+ // Is called after loading if there are DB areas with omitted data
+
+ bool bDone = false;
+ ScDBCollection* pDBColl = m_pDocument->GetDBCollection();
+
+ if ((m_nCanUpdate != css::document::UpdateDocMode::NO_UPDATE) &&
+ (m_nCanUpdate != css::document::UpdateDocMode::QUIET_UPDATE))
+ {
+ ScRange aRange;
+ ScTabViewShell* pViewSh = GetBestViewShell();
+ OSL_ENSURE(pViewSh,"SID_REIMPORT_AFTER_LOAD: no View");
+ if (pViewSh && pDBColl)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_REIMPORT_AFTER_LOAD)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_YES)
+ {
+ ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ ScDBData& rDBData = *rxDB;
+ if ( rDBData.IsStripData() &&
+ rDBData.HasImportParam() && !rDBData.HasImportSelection() )
+ {
+ rDBData.GetArea(aRange);
+ pViewSh->MarkRange(aRange);
+
+ // Import and internal operations like SID_REFRESH_DBAREA
+ // (inquiry for import not needed here)
+
+ ScImportParam aImportParam;
+ rDBData.GetImportParam( aImportParam );
+ bool bContinue = pViewSh->ImportData( aImportParam );
+ rDBData.SetImportParam( aImportParam );
+
+ // mark (size may have changed)
+ rDBData.GetArea(aRange);
+ pViewSh->MarkRange(aRange);
+
+ if ( bContinue ) // error at import -> abort
+ {
+ // internal operations, if some where saved
+
+ if ( rDBData.HasQueryParam() || rDBData.HasSortParam() ||
+ rDBData.HasSubTotalParam() )
+ pViewSh->RepeatDB();
+
+ // pivot tables, which have the range as source data
+
+ RefreshPivotTables(aRange);
+ }
+ }
+ }
+ bDone = true;
+ }
+ }
+ }
+
+ if ( !bDone && pDBColl )
+ {
+ // if not, but then update the dependent formulas
+ //! also for individual ranges, which cannot be updated
+
+ m_pDocument->CalcAll(); //! only for the dependent
+ PostDataChanged();
+ }
+
+ if (bDone)
+ rReq.Done();
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_AUTO_STYLE:
+ OSL_FAIL("use ScAutoStyleHint instead of SID_AUTO_STYLE");
+ break;
+
+ case SID_GET_COLORLIST:
+ {
+ const SvxColorListItem* pColItem = GetItem(SID_COLOR_TABLE);
+ const XColorListRef& pList = pColItem->GetColorList();
+ rReq.SetReturnValue(OfaXColorListItem(SID_GET_COLORLIST, pList));
+ }
+ break;
+
+ case FID_CHG_RECORD:
+ {
+ ScDocument& rDoc = GetDocument();
+ // get argument (recorded macro)
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(FID_CHG_RECORD);
+ bool bDo = true;
+
+ // desired state
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ bool bActivateTracking = (pChangeTrack == nullptr); // toggle
+ if ( pItem )
+ bActivateTracking = pItem->GetValue(); // from argument
+
+ if ( !bActivateTracking )
+ {
+ if ( !pItem )
+ {
+ // no dialog on playing the macro
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Warning, VclButtonsType::YesNo,
+ ScResId(STR_END_REDLINING)));
+ xWarn->set_default_response(RET_NO);
+ bDo = (xWarn->run() == RET_YES );
+ }
+
+ if ( bDo )
+ {
+ if (pChangeTrack)
+ {
+ if ( pChangeTrack->IsProtected() )
+ bDo = ExecuteChangeProtectionDialog();
+ }
+ if ( bDo )
+ {
+ rDoc.EndChangeTracking();
+ PostPaintGridAll();
+ }
+ }
+ }
+ else
+ {
+ rDoc.StartChangeTracking();
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges(true);
+ rDoc.SetChangeViewSettings(aChangeViewSet);
+ }
+
+ if ( bDo )
+ {
+ UpdateAcceptChangesDialog();
+
+ // invalidate slots
+ if (pBindings)
+ pBindings->InvalidateAll(false);
+ if ( !pItem )
+ rReq.AppendItem( SfxBoolItem( FID_CHG_RECORD, bActivateTracking ) );
+ rReq.Done();
+ }
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_CHG_PROTECT :
+ {
+ if ( ExecuteChangeProtectionDialog() )
+ {
+ rReq.Done();
+ SetDocumentModified();
+ }
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_DOCUMENT_MERGE:
+ case SID_DOCUMENT_COMPARE:
+ {
+ bool bDo = true;
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ if ( pChangeTrack && !m_pImpl->bIgnoreLostRedliningWarning )
+ {
+ if ( nSlot == SID_DOCUMENT_COMPARE )
+ { //! old changes trace will be lost
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Warning, VclButtonsType::YesNo,
+ ScResId(STR_END_REDLINING)));
+ xWarn->set_default_response(RET_NO);
+ if (xWarn->run() == RET_YES)
+ bDo = ExecuteChangeProtectionDialog( true );
+ else
+ bDo = false;
+ }
+ else // merge might reject some actions
+ bDo = ExecuteChangeProtectionDialog( true );
+ }
+ if ( !bDo )
+ {
+ rReq.Ignore();
+ break;
+ }
+ SfxApplication* pApp = SfxGetpApp();
+ const SfxPoolItem* pItem;
+ const SfxStringItem* pFileNameItem(nullptr);
+ SfxMedium* pMed = nullptr;
+ if (pReqArgs)
+ pFileNameItem = pReqArgs->GetItemIfSet(SID_FILE_NAME);
+ if (pFileNameItem)
+ {
+ OUString aFileName = pFileNameItem->GetValue();
+
+ OUString aFilterName;
+ if (const SfxStringItem* pFilterItem = pReqArgs->GetItemIfSet(SID_FILTER_NAME))
+ {
+ aFilterName = pFilterItem->GetValue();
+ }
+ OUString aOptions;
+ if (const SfxStringItem* pOptionsItem = pReqArgs->GetItemIfSet(SID_FILE_FILTEROPTIONS))
+ {
+ aOptions = pOptionsItem->GetValue();
+ }
+ short nVersion = 0;
+ const SfxInt16Item* pInt16Item(nullptr);
+ if (pReqArgs->GetItemState(SID_VERSION, true, &pItem) == SfxItemState::SET)
+ pInt16Item = dynamic_cast<const SfxInt16Item*>(pItem);
+ if (pInt16Item)
+ {
+ nVersion = pInt16Item->GetValue();
+ }
+
+ // no filter specified -> detection
+ if (aFilterName.isEmpty())
+ ScDocumentLoader::GetFilterName( aFileName, aFilterName, aOptions, true, false );
+
+ // filter name from dialog contains application prefix,
+ // GetFilter needs name without the prefix.
+ ScDocumentLoader::RemoveAppPrefix( aFilterName );
+
+ std::shared_ptr<const SfxFilter> pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( aFilterName );
+ auto pSet = std::make_shared<SfxAllItemSet>( pApp->GetPool() );
+ if (!aOptions.isEmpty())
+ pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) );
+ if ( nVersion != 0 )
+ pSet->Put( SfxInt16Item( SID_VERSION, nVersion ) );
+ pMed = new SfxMedium( aFileName, StreamMode::STD_READ, pFilter, std::move(pSet) );
+ }
+ else
+ {
+ const sfx2::DocumentInserter::Mode mode { nSlot==SID_DOCUMENT_COMPARE
+ ? sfx2::DocumentInserter::Mode::Compare
+ : sfx2::DocumentInserter::Mode::Merge};
+ // start file dialog asynchronous
+ m_pImpl->bIgnoreLostRedliningWarning = true;
+ m_pImpl->pRequest.reset(new SfxRequest( rReq ));
+ m_pImpl->pDocInserter.reset();
+
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ weld::Window* pParent = pViewSh ? pViewSh->GetFrameWeld() : nullptr;
+ m_pImpl->pDocInserter.reset( new ::sfx2::DocumentInserter(pParent,
+ ScDocShell::Factory().GetFactoryName(), mode ) );
+ m_pImpl->pDocInserter->StartExecuteModal( LINK( this, ScDocShell, DialogClosedHdl ) );
+ return ;
+ }
+
+ // now execute in earnest...
+ SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, pMed->GetName() );
+
+ // pOtherDocSh->DoClose() will be called explicitly later, but it is still more safe to use SfxObjectShellLock here
+ ScDocShell* pOtherDocSh = new ScDocShell;
+ SfxObjectShellLock aDocShTablesRef = pOtherDocSh;
+ pOtherDocSh->DoLoad( pMed );
+ ErrCodeMsg nErr = pOtherDocSh->GetErrorCode();
+ if (nErr)
+ ErrorHandler::HandleError( nErr ); // also warnings
+
+ if ( !pOtherDocSh->GetErrorIgnoreWarning() ) // only errors
+ {
+ bool bHadTrack = ( m_pDocument->GetChangeTrack() != nullptr );
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ sal_uLong nStart = 0;
+ if ( nSlot == SID_DOCUMENT_MERGE && pChangeTrack )
+ {
+ nStart = pChangeTrack->GetActionMax() + 1;
+ }
+#endif
+ if ( nSlot == SID_DOCUMENT_COMPARE )
+ CompareDocument( pOtherDocSh->GetDocument() );
+ else
+ MergeDocument( pOtherDocSh->GetDocument() );
+
+ // show "accept changes" dialog
+ //! get view for this document!
+ if ( !IsDocShared() )
+ {
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if ( pViewFrm )
+ {
+ pViewFrm->ShowChildWindow( ScAcceptChgDlgWrapper::GetChildWindowId() ); //@51669
+ }
+ if ( pBindings )
+ {
+ pBindings->Invalidate( FID_CHG_ACCEPT );
+ }
+ }
+
+ rReq.SetReturnValue( SfxInt32Item( TypedWhichId<SfxInt32Item>(nSlot), 0 ) ); //! ???????
+ rReq.Done();
+
+ if (!bHadTrack) // newly turned on -> show as well
+ {
+ ScChangeViewSettings* pOldSet = m_pDocument->GetChangeViewSettings();
+ if ( !pOldSet || !pOldSet->ShowChanges() )
+ {
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges(true);
+ m_pDocument->SetChangeViewSettings(aChangeViewSet);
+ }
+ }
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ else if ( nSlot == SID_DOCUMENT_MERGE && IsDocShared() && pChangeTrack )
+ {
+ sal_uLong nEnd = pChangeTrack->GetActionMax();
+ if ( nEnd >= nStart )
+ {
+ // only show changes from merged document
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges( true );
+ aChangeViewSet.SetShowAccepted( true );
+ aChangeViewSet.SetHasActionRange();
+ aChangeViewSet.SetTheActionRange( nStart, nEnd );
+ m_pDocument->SetChangeViewSettings( aChangeViewSet );
+
+ // update view
+ PostPaintExtras();
+ PostPaintGridAll();
+ }
+ }
+#endif
+ }
+ pOtherDocSh->DoClose(); // delete happens with the Ref
+ }
+ break;
+
+ case SID_DELETE_SCENARIO:
+ if (pReqArgs)
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ {
+ if (const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>(pItem))
+ {
+ const OUString& aName = pStringItem->GetValue();
+ SCTAB nTab;
+ if (m_pDocument->GetTable( aName, nTab ))
+ {
+ // move DeleteTable from viewfunc to docfunc!
+
+ ScTabViewShell* pSh = GetBestViewShell();
+ if ( pSh )
+ {
+ //! omit SetTabNo in DeleteTable?
+ SCTAB nDispTab = pSh->GetViewData().GetTabNo();
+ pSh->DeleteTable( nTab );
+ pSh->SetTabNo(nDispTab);
+ rReq.Done();
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_EDIT_SCENARIO:
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ {
+ if (const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>(pItem))
+ {
+ OUString aName = pStringItem->GetValue();
+ SCTAB nTab;
+ if (m_pDocument->GetTable( aName, nTab ))
+ {
+ if (m_pDocument->IsScenario(nTab))
+ {
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ m_pDocument->GetScenarioData( nTab, aComment, aColor, nFlags );
+
+ // Determine if the Sheet that the Scenario was created on
+ // is protected. But first we need to find that Sheet.
+ // Rewind back to the actual sheet.
+ SCTAB nActualTab = nTab;
+ do
+ {
+ nActualTab--;
+ }
+ while(m_pDocument->IsScenario(nActualTab));
+ bool bSheetProtected = m_pDocument->IsTabProtected(nActualTab);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNewScenarioDlg> pNewDlg(pFact->CreateScNewScenarioDlg(GetActiveDialogParent(), aName, true, bSheetProtected));
+ pNewDlg->SetScenarioData( aName, aComment, aColor, nFlags );
+ if ( pNewDlg->Execute() == RET_OK )
+ {
+ pNewDlg->GetScenarioData( aName, aComment, aColor, nFlags );
+ ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_YEAR2000 :
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ {
+ if (const SfxUInt16Item* pInt16Item = dynamic_cast<const SfxUInt16Item*>(pItem))
+ {
+ sal_uInt16 nY2k = pInt16Item->GetValue();
+ // set always to DocOptions, so that it is also saved for S050
+ // (and all inquiries run up until now on it as well).
+ // SetDocOptions propagates that to the NumberFormatter
+ ScDocOptions aDocOpt( m_pDocument->GetDocOptions() );
+ aDocOpt.SetYear2000( nY2k );
+ m_pDocument->SetDocOptions( aDocOpt );
+ // the FormShell shall notice it as well
+ ScTabViewShell* pSh = GetBestViewShell();
+ if ( pSh )
+ {
+ FmFormShell* pFSh = pSh->GetFormShell();
+ if ( pFSh )
+ pFSh->SetY2KState( nY2k );
+ }
+ }
+ }
+ }
+ break;
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ case SID_SHARE_DOC:
+ {
+ ScViewData* pViewData = GetViewData();
+ if ( !pViewData )
+ {
+ rReq.Ignore();
+ break;
+ }
+
+ weld::Window* pWin = GetActiveDialogParent();
+ ScShareDocumentDlg aDlg(pWin, pViewData);
+ if (aDlg.run() == RET_OK)
+ {
+ bool bSetShared = aDlg.IsShareDocumentChecked();
+ if ( bSetShared != IsDocShared() )
+ {
+ if ( bSetShared )
+ {
+ bool bContinue = true;
+ if ( HasName() )
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_DOC_WILLBESAVED)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_NO)
+ {
+ bContinue = false;
+ }
+ }
+ if ( bContinue )
+ {
+ EnableSharedSettings( true );
+
+ SC_MOD()->SetInSharedDocSaving( true );
+ if ( !SwitchToShared( true, true ) )
+ {
+ // TODO/LATER: what should be done in case the switch has failed?
+ // for example in case the user has cancelled the saveAs operation
+ }
+
+ SC_MOD()->SetInSharedDocSaving( false );
+
+ InvalidateName();
+ GetUndoManager()->Clear();
+
+ ScTabView* pTabView = pViewData->GetView();
+ if ( pTabView )
+ {
+ pTabView->UpdateLayerLocks();
+ }
+ }
+ }
+ else
+ {
+ uno::Reference< frame::XModel > xModel;
+ try
+ {
+ // load shared file
+ xModel.set( LoadSharedDocument(), uno::UNO_SET_THROW );
+ uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY_THROW );
+
+ // check if shared flag is set in shared file
+ bool bShared = false;
+ ScModelObj* pDocObj = comphelper::getFromUnoTunnel<ScModelObj>( xModel );
+ if ( pDocObj )
+ {
+ ScDocShell* pDocShell = dynamic_cast< ScDocShell* >( pDocObj->GetEmbeddedObject() );
+ if ( pDocShell )
+ {
+ bShared = pDocShell->HasSharedXMLFlagSet();
+ }
+ }
+
+ // #i87870# check if shared status was disabled and enabled again
+ bool bOwnEntry = false;
+ try
+ {
+ ::svt::ShareControlFile aControlFile( GetSharedFileURL() );
+ bOwnEntry = aControlFile.HasOwnEntry();
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ if ( bShared && bOwnEntry )
+ {
+ uno::Reference< frame::XStorable > xStorable( xModel, uno::UNO_QUERY_THROW );
+ if ( xStorable->isReadonly() )
+ {
+ xCloseable->close( true );
+
+ OUString aUserName( ScResId( STR_UNKNOWN_USER ) );
+ try
+ {
+ ::svt::DocumentLockFile aLockFile( GetSharedFileURL() );
+ LockFileEntry aData = aLockFile.GetLockData();
+ if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
+ {
+ aUserName = aData[LockFileComponent::OOOUSERNAME];
+ }
+ else if ( !aData[LockFileComponent::SYSUSERNAME].isEmpty() )
+ {
+ aUserName = aData[LockFileComponent::SYSUSERNAME];
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ OUString aMessage( ScResId( STR_FILE_LOCKED_TRY_LATER ) );
+ aMessage = aMessage.replaceFirst( "%1", aUserName );
+
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pWin,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMessage));
+ xWarn->run();
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pWin,
+ VclMessageType::Warning, VclButtonsType::YesNo,
+ ScResId(STR_DOC_DISABLESHARED)));
+ xWarn->set_default_response(RET_YES);
+
+ if (xWarn->run() == RET_YES)
+ {
+ xCloseable->close( true );
+
+ if ( !SwitchToShared( false, true ) )
+ {
+ // TODO/LATER: what should be done in case the switch has failed?
+ // for example in case the user has cancelled the saveAs operation
+ }
+
+ EnableSharedSettings( false );
+
+ // Do *not* use dispatch mechanism in this place - we don't want others (extensions etc.) to intercept this.
+ GetModel()->store();
+
+ ScTabView* pTabView = pViewData->GetView();
+ if ( pTabView )
+ {
+ pTabView->UpdateLayerLocks();
+ }
+ }
+ else
+ {
+ xCloseable->close( true );
+ }
+ }
+ }
+ else
+ {
+ xCloseable->close( true );
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pWin,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_DOC_NOLONGERSHARED)));
+ xWarn->run();
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "SID_SHARE_DOC" );
+ SC_MOD()->SetInSharedDocSaving( false );
+
+ try
+ {
+ uno::Reference< util::XCloseable > xClose( xModel, uno::UNO_QUERY_THROW );
+ xClose->close( true );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+ }
+ rReq.Done();
+ }
+ break;
+#endif
+ case SID_OPEN_CALC:
+ {
+ ScViewData* pViewData = GetViewData();
+ if (pViewData)
+ {
+ SfxStringItem aApp(SID_DOC_SERVICE, "com.sun.star.sheet.SpreadsheetDocument");
+ SfxStringItem aTarget(SID_TARGETNAME, "_blank");
+ pViewData->GetDispatcher().ExecuteList(
+ SID_OPENDOC, SfxCallMode::API|SfxCallMode::SYNCHRON,
+ { &aApp, &aTarget });
+ }
+ }
+ break;
+ case SID_NOTEBOOKBAR:
+ {
+ const SfxStringItem* pFile = rReq.GetArg<SfxStringItem>( SID_NOTEBOOKBAR );
+
+ if ( pBindings && sfx2::SfxNotebookBar::IsActive() )
+ sfx2::SfxNotebookBar::ExecMethod(*pBindings, pFile ? pFile->GetValue() : "");
+ else if ( pBindings )
+ sfx2::SfxNotebookBar::CloseMethod(*pBindings);
+ }
+ break;
+ case SID_LANGUAGE_STATUS:
+ {
+ OUString aLangText;
+ const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(nSlot);
+ if ( pItem )
+ aLangText = pItem->GetValue();
+
+ if ( !aLangText.isEmpty() )
+ {
+ LanguageType eLang, eLatin, eCjk, eCtl;
+ static constexpr OUString aSelectionLangPrefix(u"Current_"_ustr);
+ static constexpr OUString aParagraphLangPrefix(u"Paragraph_"_ustr);
+ static constexpr OUString aDocLangPrefix(u"Default_"_ustr);
+
+ bool bSelection = false;
+ bool bParagraph = false;
+
+ ScDocument& rDoc = GetDocument();
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+
+ sal_Int32 nPos = 0;
+ if ( aLangText == "*" )
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ ScTabViewShell* pSh = GetBestViewShell();
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog(pSh ? pSh->GetDialogParent() : nullptr, SID_LANGUAGE_OPTIONS));
+ pDlg->Execute();
+
+ rDoc.GetLanguage( eLang, eCjk, eCtl );
+ }
+ else if ( (nPos = aLangText.indexOf(aDocLangPrefix)) != -1 )
+ {
+ aLangText = aLangText.replaceAt(nPos, aDocLangPrefix.getLength(), u"");
+
+ if ( aLangText == "LANGUAGE_NONE" )
+ {
+ eLang = LANGUAGE_NONE;
+ rDoc.SetLanguage( eLang, eCjk, eCtl );
+ }
+ else if ( aLangText == "RESET_LANGUAGES" )
+ {
+ bool bAutoSpell;
+
+ ScModule::GetSpellSettings(eLang, eCjk, eCtl, bAutoSpell);
+ rDoc.SetLanguage(eLang, eCjk, eCtl);
+ }
+ else
+ {
+ eLang = SvtLanguageTable::GetLanguageType( aLangText );
+ if ( eLang != LANGUAGE_DONTKNOW && SvtLanguageOptions::GetScriptTypeOfLanguage(eLang) == SvtScriptType::LATIN )
+ {
+ rDoc.SetLanguage( eLang, eCjk, eCtl );
+ }
+ else
+ {
+ eLang = eLatin;
+ }
+ }
+ }
+ else if (-1 != (nPos = aLangText.indexOf( aSelectionLangPrefix )))
+ {
+ bSelection = true;
+ aLangText = aLangText.replaceAt( nPos, aSelectionLangPrefix.getLength(), u"" );
+ }
+ else if (-1 != (nPos = aLangText.indexOf( aParagraphLangPrefix )))
+ {
+ bParagraph = true;
+ aLangText = aLangText.replaceAt( nPos, aParagraphLangPrefix.getLength(), u"" );
+ }
+
+ if (bSelection || bParagraph)
+ {
+ ScViewData* pViewData = GetViewData();
+ if (!pViewData)
+ return;
+
+ EditView* pEditView = pViewData->GetEditView(pViewData->GetActivePart());
+ if (!pEditView)
+ return;
+
+ const LanguageType nLangToUse = SvtLanguageTable::GetLanguageType( aLangText );
+ SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLangToUse );
+
+ SfxItemSet aAttrs = pEditView->GetEditEngine()->GetEmptyItemSet();
+ if (nScriptType == SvtScriptType::LATIN)
+ aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE ) );
+ if (nScriptType == SvtScriptType::COMPLEX)
+ aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CTL ) );
+ if (nScriptType == SvtScriptType::ASIAN)
+ aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CJK ) );
+ ESelection aOldSel;
+ if (bParagraph)
+ {
+ ESelection aSel = pEditView->GetSelection();
+ aOldSel = aSel;
+ aSel.nStartPos = 0;
+ aSel.nEndPos = EE_TEXTPOS_ALL;
+ pEditView->SetSelection( aSel );
+ }
+
+ pEditView->SetAttribs( aAttrs );
+ if (bParagraph)
+ pEditView->SetSelection( aOldSel );
+ }
+ else if ( eLang != eLatin )
+ {
+ if ( ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell() )
+ {
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewSh);
+ if ( pInputHandler )
+ pInputHandler->UpdateSpellSettings();
+
+ pViewSh->UpdateDrawTextOutliner();
+ }
+
+ SetDocumentModified();
+ Broadcast(SfxHint(SfxHintId::LanguageChanged));
+ PostPaintGridAll();
+ }
+ }
+ }
+ break;
+ case SID_SPELLCHECK_IGNORE_ALL:
+ {
+ ScViewData* pViewData = GetViewData();
+ if (!pViewData)
+ return;
+
+ EditView* pEditView = pViewData->GetEditView(pViewData->GetActivePart());
+ if (!pEditView)
+ return;
+
+ OUString sIgnoreText;
+ const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(FN_PARAM_1);
+ if (pItem2)
+ sIgnoreText = pItem2->GetValue();
+
+ if(sIgnoreText == "Spelling")
+ {
+ ESelection aOldSel = pEditView->GetSelection();
+ pEditView->SpellIgnoreWord();
+ pEditView->SetSelection( aOldSel );
+ }
+ }
+ break;
+ case SID_SPELLCHECK_APPLY_SUGGESTION:
+ {
+ ScViewData* pViewData = GetViewData();
+ if (!pViewData)
+ return;
+
+ EditView* pEditView = pViewData->GetEditView(pViewData->GetActivePart());
+ if (!pEditView)
+ return;
+
+ OUString sApplyText;
+ const SfxStringItem* pItem2 = rReq.GetArg<SfxStringItem>(FN_PARAM_1);
+ if (pItem2)
+ sApplyText = pItem2->GetValue();
+
+ static constexpr OUString sSpellingRule(u"Spelling_"_ustr);
+ sal_Int32 nPos = 0;
+ if(-1 != (nPos = sApplyText.indexOf( sSpellingRule )))
+ {
+ sApplyText = sApplyText.replaceAt(nPos, sSpellingRule.getLength(), u"");
+ pEditView->InsertText( sApplyText );
+ }
+ }
+ break;
+ case SID_REFRESH_VIEW:
+ {
+ PostPaintGridAll();
+ }
+ break;
+ default:
+ {
+ // small (?) hack -> forwarding of the slots to TabViewShell
+ ScTabViewShell* pSh = GetBestViewShell();
+ if ( pSh )
+ pSh->Execute( rReq );
+#if HAVE_FEATURE_SCRIPTING
+ else
+ SbxBase::SetError( ERRCODE_BASIC_NO_ACTIVE_OBJECT );
+#endif
+ }
+ }
+}
+
+void UpdateAcceptChangesDialog()
+{
+ // update "accept changes" dialog
+ //! notify all views
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if ( pViewFrm && pViewFrm->HasChildWindow( FID_CHG_ACCEPT ) )
+ {
+ SfxChildWindow* pChild = pViewFrm->GetChildWindow( FID_CHG_ACCEPT );
+ if ( pChild )
+ static_cast<ScAcceptChgDlgWrapper*>(pChild)->ReInitDlg();
+ }
+}
+
+bool ScDocShell::ExecuteChangeProtectionDialog( bool bJustQueryIfProtected )
+{
+ bool bDone = false;
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ bool bProtected = pChangeTrack->IsProtected();
+ if ( bJustQueryIfProtected && !bProtected )
+ return true;
+
+ OUString aTitle( ScResId( bProtected ? SCSTR_CHG_UNPROTECT : SCSTR_CHG_PROTECT ) );
+ OUString aText( ScResId( SCSTR_PASSWORD ) );
+ OUString aPassword;
+
+ weld::Window* pWin = ScDocShell::GetActiveDialogParent();
+ SfxPasswordDialog aDlg(pWin, &aText);
+ aDlg.set_title(aTitle);
+ aDlg.SetMinLen(1);
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(SID_CHG_PROTECT)->GetCommand());
+ aDlg.SetEditHelpId( HID_CHG_PROTECT );
+ if ( !bProtected )
+ aDlg.ShowExtras(SfxShowExtras::CONFIRM);
+ if (aDlg.run() == RET_OK)
+ aPassword = aDlg.GetPassword();
+
+ if (!aPassword.isEmpty())
+ {
+ if ( bProtected )
+ {
+ if ( SvPasswordHelper::CompareHashPassword(pChangeTrack->GetProtection(), aPassword) )
+ {
+ if ( bJustQueryIfProtected )
+ bDone = true;
+ else
+ pChangeTrack->SetProtection( {} );
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(SCSTR_WRONGPASSWORD)));
+ xInfoBox->run();
+ }
+ }
+ else
+ {
+ css::uno::Sequence< sal_Int8 > aPass;
+ SvPasswordHelper::GetHashPassword( aPass, aPassword );
+ pChangeTrack->SetProtection( aPass );
+ }
+ if ( bProtected != pChangeTrack->IsProtected() )
+ {
+ UpdateAcceptChangesDialog();
+ bDone = true;
+ }
+ }
+ }
+ else if ( bJustQueryIfProtected )
+ bDone = true;
+ return bDone;
+}
+
+void ScDocShell::DoRecalc( bool bApi )
+{
+ if (m_pDocument->IsInDocShellRecalc())
+ {
+ SAL_WARN("sc","ScDocShell::DoRecalc tries re-entering while in Recalc; probably Forms->BASIC->Dispatcher.");
+ return;
+ }
+ ScDocShellRecalcGuard aGuard(*m_pDocument);
+ bool bDone = false;
+ ScTabViewShell* pSh = GetBestViewShell();
+ ScInputHandler* pHdl = ( pSh ? SC_MOD()->GetInputHdl( pSh ) : nullptr );
+ if ( pSh )
+ {
+ if ( pHdl && pHdl->IsInputMode() && pHdl->IsFormulaMode() && !bApi )
+ {
+ pHdl->FormulaPreview(); // partial result as QuickHelp
+ bDone = true;
+ }
+ else
+ {
+ ScTabView::UpdateInputLine(); // InputEnterHandler
+ pSh->UpdateInputHandler();
+ }
+ }
+ if (bDone) // otherwise re-calculate document
+ return;
+
+ weld::WaitObject aWaitObj( GetActiveDialogParent() );
+ if ( pHdl )
+ {
+ // tdf97897 set current cell to Dirty to force recalculation of cell
+ ScFormulaCell* pFC = m_pDocument->GetFormulaCell( pHdl->GetCursorPos());
+ if (pFC)
+ pFC->SetDirty();
+ }
+ m_pDocument->CalcFormulaTree();
+ if ( pSh )
+ pSh->UpdateCharts(true);
+
+ m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
+
+ // If there are charts, then paint everything, so that PostDataChanged
+ // and the charts do not come one after the other and parts are painted twice.
+
+ ScChartListenerCollection* pCharts = m_pDocument->GetChartListenerCollection();
+ if ( pCharts && pCharts->hasListeners() )
+ PostPaintGridAll();
+ else
+ PostDataChanged();
+}
+
+void ScDocShell::DoHardRecalc()
+{
+ if (m_pDocument->IsInDocShellRecalc())
+ {
+ SAL_WARN("sc","ScDocShell::DoHardRecalc tries re-entering while in Recalc; probably Forms->BASIC->Dispatcher.");
+ return;
+ }
+ auto start = std::chrono::steady_clock::now();
+ ScDocShellRecalcGuard aGuard(*m_pDocument);
+ weld::WaitObject aWaitObj( GetActiveDialogParent() );
+ ScTabViewShell* pSh = GetBestViewShell();
+ if ( pSh )
+ {
+ ScTabView::UpdateInputLine(); // InputEnterHandler
+ pSh->UpdateInputHandler();
+ }
+ m_pDocument->CalcAll();
+ GetDocFunc().DetectiveRefresh(); // creates own Undo
+ if ( pSh )
+ pSh->UpdateCharts(true);
+
+ // set notification flags for "calculate" event (used in SfxHintId::DataChanged broadcast)
+ // (might check for the presence of any formulas on each sheet)
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ if (m_pDocument->HasAnySheetEventScript( ScSheetEventId::CALCULATE, true )) // search also for VBA handler
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ m_pDocument->SetCalcNotification(nTab);
+
+ // CalcAll doesn't broadcast value changes, so SfxHintId::ScCalcAll is broadcasted globally
+ // in addition to SfxHintId::DataChanged.
+ m_pDocument->BroadcastUno( SfxHint( SfxHintId::ScCalcAll ) );
+ m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
+
+ // use hard recalc also to disable stream-copying of all sheets
+ // (somewhat consistent with charts)
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ m_pDocument->SetStreamValid(nTab, false);
+
+ PostPaintGridAll();
+ auto end = std::chrono::steady_clock::now();
+ SAL_INFO("sc.timing", "ScDocShell::DoHardRecalc(): took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms");
+}
+
+void ScDocShell::DoAutoStyle( const ScRange& rRange, const OUString& rStyle )
+{
+ ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool();
+ ScStyleSheet* pStyleSheet = pStylePool->FindAutoStyle(rStyle);
+ if (!pStyleSheet)
+ return;
+
+ OSL_ENSURE(rRange.aStart.Tab() == rRange.aEnd.Tab(),
+ "DoAutoStyle with several tables");
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ m_pDocument->ApplyStyleAreaTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, *pStyleSheet );
+ m_pDocument->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
+ PostPaint( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab, PaintPartFlags::Grid );
+}
+
+void ScDocShell::NotifyStyle( const SfxStyleSheetHint& rHint )
+{
+ SfxHintId nId = rHint.GetId();
+ const SfxStyleSheetBase* pStyle = rHint.GetStyleSheet();
+ if (!pStyle)
+ return;
+
+ if ( pStyle->GetFamily() == SfxStyleFamily::Page )
+ {
+ if ( nId == SfxHintId::StyleSheetModified )
+ {
+ ScDocShellModificator aModificator( *this );
+
+ const OUString& aNewName = pStyle->GetName();
+ OUString aOldName = aNewName;
+ const SfxStyleSheetModifiedHint* pExtendedHint = dynamic_cast<const SfxStyleSheetModifiedHint*>(&rHint); // name changed?
+ if (pExtendedHint)
+ aOldName = pExtendedHint->GetOldName();
+
+ if ( aNewName != aOldName )
+ m_pDocument->RenamePageStyleInUse( aOldName, aNewName );
+
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if (m_pDocument->GetPageStyle(nTab) == aNewName) // already adjusted to new
+ {
+ m_pDocument->PageStyleModified( nTab, aNewName );
+ ScPrintFunc aPrintFunc( this, GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ }
+
+ aModificator.SetDocumentModified();
+
+ if (pExtendedHint)
+ {
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_STATUS_PAGESTYLE );
+ pBindings->Invalidate( SID_STYLE_FAMILY4 );
+ pBindings->Invalidate( FID_RESET_PRINTZOOM );
+ pBindings->Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ pBindings->Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ }
+ }
+ }
+ }
+ else if ( pStyle->GetFamily() == SfxStyleFamily::Para )
+ {
+ if ( nId == SfxHintId::StyleSheetModified)
+ {
+ const OUString& aNewName = pStyle->GetName();
+ OUString aOldName = aNewName;
+ const SfxStyleSheetModifiedHint* pExtendedHint = dynamic_cast<const SfxStyleSheetModifiedHint*>(&rHint);
+ if (pExtendedHint)
+ aOldName = pExtendedHint->GetOldName();
+ if ( aNewName != aOldName )
+ {
+ for(SCTAB i = 0; i < m_pDocument->GetTableCount(); ++i)
+ {
+ ScConditionalFormatList* pList = m_pDocument->GetCondFormList(i);
+ if (pList)
+ pList->RenameCellStyle( aOldName,aNewName );
+ }
+ }
+ }
+ }
+
+ // everything else goes via slots...
+}
+
+// like in printfun.cxx
+#define ZOOM_MIN 10
+
+void ScDocShell::SetPrintZoom( SCTAB nTab, sal_uInt16 nScale, sal_uInt16 nPages )
+{
+ OUString aStyleName = m_pDocument->GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( !pStyleSheet )
+ return;
+
+ ScDocShellModificator aModificator( *this );
+
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ const bool bUndo(m_pDocument->IsUndoEnabled());
+ if (bUndo)
+ {
+ sal_uInt16 nOldScale = rSet.Get(ATTR_PAGE_SCALE).GetValue();
+ sal_uInt16 nOldPages = rSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue();
+ GetUndoManager()->AddUndoAction( std::make_unique<ScUndoPrintZoom>(
+ this, nTab, nOldScale, nOldPages, nScale, nPages ) );
+ }
+
+ rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, nScale ) );
+ rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, nPages ) );
+
+ ScPrintFunc aPrintFunc( this, GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( FID_RESET_PRINTZOOM );
+}
+
+bool ScDocShell::AdjustPrintZoom( const ScRange& rRange )
+{
+ bool bChange = false;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ OUString aStyleName = m_pDocument->GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ bool bHeaders = rSet.Get(ATTR_PAGE_HEADERS).GetValue();
+ sal_uInt16 nOldScale = rSet.Get(ATTR_PAGE_SCALE).GetValue();
+ sal_uInt16 nOldPages = rSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue();
+ std::optional<ScRange> oRepeatCol = m_pDocument->GetRepeatColRange( nTab );
+ std::optional<ScRange> oRepeatRow = m_pDocument->GetRepeatRowRange( nTab );
+
+ // calculate needed scaling for selection
+
+ sal_uInt16 nNewScale = nOldScale;
+
+ tools::Long nBlkTwipsX = 0;
+ if (bHeaders)
+ nBlkTwipsX += PRINT_HEADER_WIDTH;
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ if ( oRepeatCol && nStartCol >= oRepeatCol->aStart.Col() )
+ {
+ for (SCCOL i=oRepeatCol->aStart.Col(); i<=oRepeatCol->aEnd.Col(); i++ )
+ nBlkTwipsX += m_pDocument->GetColWidth( i, nTab );
+ if ( nStartCol <= oRepeatCol->aEnd.Col() )
+ nStartCol = oRepeatCol->aEnd.Col() + 1;
+ }
+ // legacy compilers' own scope for i
+ {
+ for ( SCCOL i=nStartCol; i<=nEndCol; i++ )
+ nBlkTwipsX += m_pDocument->GetColWidth( i, nTab );
+ }
+
+ tools::Long nBlkTwipsY = 0;
+ if (bHeaders)
+ nBlkTwipsY += PRINT_HEADER_HEIGHT;
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ if ( oRepeatRow && nStartRow >= oRepeatRow->aStart.Row() )
+ {
+ nBlkTwipsY += m_pDocument->GetRowHeight( oRepeatRow->aStart.Row(),
+ oRepeatRow->aEnd.Row(), nTab );
+ if ( nStartRow <= oRepeatRow->aEnd.Row() )
+ nStartRow = oRepeatRow->aEnd.Row() + 1;
+ }
+ nBlkTwipsY += m_pDocument->GetRowHeight( nStartRow, nEndRow, nTab );
+
+ Size aPhysPage;
+ tools::Long nHdr, nFtr;
+ ScPrintFunc aOldPrFunc( this, GetPrinter(), nTab );
+ aOldPrFunc.GetScaleData( aPhysPage, nHdr, nFtr );
+ nBlkTwipsY += nHdr + nFtr;
+
+ if ( nBlkTwipsX == 0 ) // hidden columns/rows may lead to 0
+ nBlkTwipsX = 1;
+ if ( nBlkTwipsY == 0 )
+ nBlkTwipsY = 1;
+
+ tools::Long nNeeded = std::min( aPhysPage.Width() * 100 / nBlkTwipsX,
+ aPhysPage.Height() * 100 / nBlkTwipsY );
+ if ( nNeeded < ZOOM_MIN )
+ nNeeded = ZOOM_MIN; // boundary
+ if ( nNeeded < static_cast<tools::Long>(nNewScale) )
+ nNewScale = static_cast<sal_uInt16>(nNeeded);
+
+ bChange = ( nNewScale != nOldScale || nOldPages != 0 );
+ if ( bChange )
+ SetPrintZoom( nTab, nNewScale, 0 );
+ }
+ return bChange;
+}
+
+void ScDocShell::PageStyleModified( std::u16string_view rStyleName, bool bApi )
+{
+ ScDocShellModificator aModificator( *this );
+
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ SCTAB nUseTab = MAXTAB+1;
+ for (SCTAB nTab=0; nTab<nTabCount && nUseTab>MAXTAB; nTab++)
+ if ( m_pDocument->GetPageStyle(nTab) == rStyleName &&
+ ( !bApi || m_pDocument->GetPageSize(nTab).Width() ) )
+ nUseTab = nTab;
+ // at bApi only if breaks already shown
+
+ if (ValidTab(nUseTab)) // not used -> nothing to do
+ {
+ bool bWarn = false;
+
+ ScPrintFunc aPrintFunc( this, GetPrinter(), nUseTab ); //! cope without CountPages
+ if (!aPrintFunc.UpdatePages()) // sets breaks on all tabs
+ bWarn = true;
+
+ if (bWarn && !bApi)
+ {
+ weld::Window* pWin = GetActiveDialogParent();
+ weld::WaitObject aWaitOff(pWin);
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_PRINT_INVALID_AREA)));
+ xInfoBox->run();
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( FID_RESET_PRINTZOOM );
+ pBindings->Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ pBindings->Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ }
+}
+
+void ScDocShell::ExecutePageStyle( const SfxViewShell& rCaller,
+ SfxRequest& rReq,
+ SCTAB nCurTab )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ switch ( rReq.GetSlot() )
+ {
+ case SID_STATUS_PAGESTYLE: // click on StatusBar control
+ case SID_FORMATPAGE:
+ {
+ if ( pReqArgs == nullptr )
+ {
+ OUString aOldName = m_pDocument->GetPageStyle( nCurTab );
+ ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet
+ = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pStyleSheet )
+ {
+ ScStyleSaveData aOldData;
+ const bool bUndo(m_pDocument->IsUndoEnabled());
+ if (bUndo)
+ aOldData.InitFromStyle( pStyleSheet );
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ rStyleSet.MergeRange( XATTR_FILL_FIRST, XATTR_FILL_LAST );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScStyleDlg(GetActiveDialogParent(), *pStyleSheet, true));
+
+ auto pRequest = std::make_shared<SfxRequest>(rReq);
+ rReq.Ignore(); // the 'old' request is not relevant any more
+ pDlg->StartExecuteAsync([this, pDlg, pRequest, pStyleSheet, aOldData, aOldName, &rStyleSet, nCurTab, &rCaller, bUndo](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+
+ weld::WaitObject aWait( GetActiveDialogParent() );
+
+ OUString aNewName = pStyleSheet->GetName();
+ if ( aNewName != aOldName &&
+ m_pDocument->RenamePageStyleInUse( aOldName, aNewName ) )
+ {
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_STATUS_PAGESTYLE );
+ pBindings->Invalidate( FID_RESET_PRINTZOOM );
+ }
+ }
+
+ if ( pOutSet )
+ m_pDocument->ModifyStyleSheet( *pStyleSheet, *pOutSet );
+
+ // memorizing for GetState():
+ GetPageOnFromPageStyleSet( &rStyleSet, nCurTab, m_bHeaderOn, m_bFooterOn );
+ rCaller.GetViewFrame().GetBindings().Invalidate( SID_HFEDIT );
+
+ ScStyleSaveData aNewData;
+ aNewData.InitFromStyle( pStyleSheet );
+ if (bUndo)
+ {
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( this, SfxStyleFamily::Page,
+ aOldData, aNewData ) );
+ }
+
+ PageStyleModified( aNewName, false );
+ pRequest->Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ }
+ break;
+
+ case SID_HFEDIT:
+ {
+ if ( pReqArgs == nullptr )
+ {
+ OUString aStr( m_pDocument->GetPageStyle( nCurTab ) );
+
+ ScStyleSheetPool* pStylePool
+ = m_pDocument->GetStyleSheetPool();
+
+ SfxStyleSheetBase* pStyleSheet
+ = pStylePool->Find( aStr, SfxStyleFamily::Page );
+
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+
+ SvxPageUsage eUsage = rStyleSet.Get( ATTR_PAGE ).GetPageUsage();
+ bool bShareHeader = rStyleSet
+ .Get(ATTR_PAGE_HEADERSET)
+ .GetItemSet()
+ .Get(ATTR_PAGE_SHARED)
+ .GetValue();
+ bool bShareFooter = rStyleSet
+ .Get(ATTR_PAGE_FOOTERSET)
+ .GetItemSet()
+ .Get(ATTR_PAGE_SHARED)
+ .GetValue();
+ sal_uInt16 nResId = 0;
+
+ switch ( eUsage )
+ {
+ case SvxPageUsage::Left:
+ case SvxPageUsage::Right:
+ {
+ if ( m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT;
+ else if ( SvxPageUsage::Right == eUsage )
+ {
+ if ( !m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_RIGHTFOOTER;
+ else if ( m_bHeaderOn && !m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_RIGHTHEADER;
+ }
+ else
+ {
+ // #69193a# respect "shared" setting
+ if ( !m_bHeaderOn && m_bFooterOn )
+ nResId = bShareFooter ?
+ RID_SCDLG_HFEDIT_RIGHTFOOTER :
+ RID_SCDLG_HFEDIT_LEFTFOOTER;
+ else if ( m_bHeaderOn && !m_bFooterOn )
+ nResId = bShareHeader ?
+ RID_SCDLG_HFEDIT_RIGHTHEADER :
+ RID_SCDLG_HFEDIT_LEFTHEADER;
+ }
+ }
+ break;
+
+ case SvxPageUsage::Mirror:
+ case SvxPageUsage::All:
+ default:
+ {
+ if ( !bShareHeader && !bShareFooter )
+ {
+ if ( m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_ALL;
+ else if ( !m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_FOOTER;
+ else if ( m_bHeaderOn && !m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_HEADER;
+ }
+ else if ( bShareHeader && bShareFooter )
+ {
+ if ( m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT;
+ else
+ {
+ if ( !m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_RIGHTFOOTER;
+ else if ( m_bHeaderOn && !m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_RIGHTHEADER;
+ }
+ }
+ else if ( !bShareHeader && bShareFooter )
+ {
+ if ( m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_SFTR;
+ else if ( !m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_RIGHTFOOTER;
+ else if ( m_bHeaderOn && !m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_HEADER;
+ }
+ else if ( bShareHeader && !bShareFooter )
+ {
+ if ( m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_SHDR;
+ else if ( !m_bHeaderOn && m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_FOOTER;
+ else if ( m_bHeaderOn && !m_bFooterOn )
+ nResId = RID_SCDLG_HFEDIT_RIGHTHEADER;
+ }
+ }
+ }
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScHFEditDlg(
+ GetActiveDialogParent(),
+ rStyleSet,
+ aStr,
+ nResId));
+ auto xRequest = std::make_shared<SfxRequest>(rReq);
+ rReq.Ignore(); // the 'old' request is not relevant any more
+ pDlg->StartExecuteAsync([this, pDlg, pStyleSheet, xRequest](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+
+ if ( pOutSet )
+ m_pDocument->ModifyStyleSheet( *pStyleSheet, *pOutSet );
+
+ SetDocumentModified();
+ xRequest->Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ScDocShell::GetStatePageStyle( SfxItemSet& rSet,
+ SCTAB nCurTab )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_STATUS_PAGESTYLE:
+ rSet.Put( SfxStringItem( nWhich, m_pDocument->GetPageStyle( nCurTab ) ) );
+ break;
+
+ case SID_HFEDIT:
+ {
+ OUString aStr = m_pDocument->GetPageStyle( nCurTab );
+ ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStr, SfxStyleFamily::Page );
+
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ GetPageOnFromPageStyleSet( &rStyleSet, nCurTab, m_bHeaderOn, m_bFooterOn );
+
+ if ( !m_bHeaderOn && !m_bFooterOn )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScDocShell::GetState( SfxItemSet &rSet )
+{
+ bool bTabView = GetBestViewShell() != nullptr;
+
+ SfxWhichIter aIter(rSet);
+ for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
+ {
+ if (!bTabView)
+ {
+ rSet.DisableItem(nWhich);
+ continue;
+ }
+
+ switch (nWhich)
+ {
+ case FID_AUTO_CALC:
+ if ( m_pDocument->GetHardRecalcState() != ScDocument::HardRecalcState::OFF )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, m_pDocument->GetAutoCalc() ) );
+ break;
+
+ case FID_CHG_RECORD:
+ if ( IsDocShared() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich,
+ m_pDocument->GetChangeTrack() != nullptr ) );
+ break;
+
+ case SID_CHG_PROTECT:
+ {
+ ScChangeTrack* pChangeTrack = m_pDocument->GetChangeTrack();
+ if ( pChangeTrack && !IsDocShared() )
+ rSet.Put( SfxBoolItem( nWhich,
+ pChangeTrack->IsProtected() ) );
+ else
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DOCUMENT_COMPARE:
+ {
+ if ( IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ // When a formula is edited, FID_RECALC must be enabled in any case. Recalc for
+ // the doc was disabled once because of a bug if AutoCalc was on, but is now
+ // always enabled because of another bug.
+
+ case SID_TABLES_COUNT:
+ rSet.Put( SfxInt16Item( nWhich, m_pDocument->GetTableCount() ) );
+ break;
+
+ case SID_ATTR_YEAR2000 :
+ rSet.Put( SfxUInt16Item( nWhich,
+ m_pDocument->GetDocOptions().GetYear2000() ) );
+ break;
+
+ case SID_SHARE_DOC:
+ {
+ if ( IsReadOnly() || GetObjectShell()->isExportLocked() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_ATTR_CHAR_FONTLIST:
+ rSet.Put( SvxFontListItem( m_pImpl->pFontList.get(), nWhich ) );
+ break;
+
+ case SID_NOTEBOOKBAR:
+ {
+ if (GetViewBindings())
+ {
+ bool bVisible = sfx2::SfxNotebookBar::StateMethod(*GetViewBindings(),
+ u"modules/scalc/ui/");
+ rSet.Put( SfxBoolItem( SID_NOTEBOOKBAR, bVisible ) );
+ }
+ }
+ break;
+
+ case SID_LANGUAGE_STATUS:
+ {
+ LanguageType eLatin, eCjk, eCtl;
+
+ GetDocument().GetLanguage( eLatin, eCjk, eCtl );
+ OUString sLanguage = SvtLanguageTable::GetLanguageString(eLatin);
+ if (comphelper::LibreOfficeKit::isActive()) {
+ if (eLatin == LANGUAGE_NONE)
+ sLanguage += ";-";
+ else
+ sLanguage += ";" + LanguageTag(eLatin).getBcp47(false);
+ }
+ rSet.Put(SfxStringItem(nWhich, sLanguage));
+ }
+ break;
+
+ default:
+ {
+ }
+ break;
+ }
+ }
+}
+
+void ScDocShell::Draw( OutputDevice* pDev, const JobSetup & /* rSetup */, sal_uInt16 nAspect, bool /*bOutputToWindow*/ )
+{
+
+ SCTAB nVisTab = m_pDocument->GetVisibleTab();
+ if (!m_pDocument->HasTable(nVisTab))
+ return;
+
+ vcl::text::ComplexTextLayoutFlags nOldLayoutMode = pDev->GetLayoutMode();
+ pDev->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::Default ); // even if it's the same, to get the metafile action
+
+ if ( nAspect == ASPECT_THUMBNAIL )
+ {
+ tools::Rectangle aBoundRect = GetVisArea( ASPECT_THUMBNAIL );
+ ScViewData aTmpData( *this, nullptr );
+ aTmpData.SetTabNo(nVisTab);
+ SnapVisArea( aBoundRect );
+ aTmpData.SetScreen( aBoundRect );
+ ScPrintFunc::DrawToDev( *m_pDocument, pDev, 1.0, aBoundRect, &aTmpData, true );
+ }
+ else
+ {
+ tools::Rectangle aOldArea = SfxObjectShell::GetVisArea();
+ tools::Rectangle aNewArea = aOldArea;
+ ScViewData aTmpData( *this, nullptr );
+ aTmpData.SetTabNo(nVisTab);
+ SnapVisArea( aNewArea );
+ if ( aNewArea != aOldArea && (m_pDocument->GetPosLeft() > 0 || m_pDocument->GetPosTop() > 0) )
+ SfxObjectShell::SetVisArea( aNewArea );
+ aTmpData.SetScreen( aNewArea );
+ ScPrintFunc::DrawToDev( *m_pDocument, pDev, 1.0, aNewArea, &aTmpData, true );
+ }
+
+ pDev->SetLayoutMode( nOldLayoutMode );
+}
+
+tools::Rectangle ScDocShell::GetVisArea( sal_uInt16 nAspect ) const
+{
+ SfxObjectCreateMode eShellMode = GetCreateMode();
+ if ( eShellMode == SfxObjectCreateMode::ORGANIZER )
+ {
+ // without contents we also don't know how large are the contents;
+ // return empty rectangle, it will then be calculated after the loading
+ return tools::Rectangle();
+ }
+
+ if( nAspect == ASPECT_THUMBNAIL )
+ {
+ SCTAB nVisTab = m_pDocument->GetVisibleTab();
+ if (!m_pDocument->HasTable(nVisTab))
+ {
+ nVisTab = 0;
+ const_cast<ScDocShell*>(this)->m_pDocument->SetVisibleTab(nVisTab);
+ }
+ Size aSize = m_pDocument->GetPageSize(nVisTab);
+ const tools::Long SC_PREVIEW_SIZE_X = 10000;
+ const tools::Long SC_PREVIEW_SIZE_Y = 12400;
+ tools::Rectangle aArea( 0,0, SC_PREVIEW_SIZE_X, SC_PREVIEW_SIZE_Y);
+ if (aSize.Width() > aSize.Height())
+ {
+ aArea.SetRight( SC_PREVIEW_SIZE_Y );
+ aArea.SetBottom( SC_PREVIEW_SIZE_X );
+ }
+
+ bool bNegativePage = m_pDocument->IsNegativePage( m_pDocument->GetVisibleTab() );
+ if ( bNegativePage )
+ ScDrawLayer::MirrorRectRTL( aArea );
+ SnapVisArea( aArea );
+ return aArea;
+ }
+ else if( nAspect == ASPECT_CONTENT && eShellMode != SfxObjectCreateMode::EMBEDDED )
+ {
+ // fetch visarea like after loading
+
+ SCTAB nVisTab = m_pDocument->GetVisibleTab();
+ if (!m_pDocument->HasTable(nVisTab))
+ {
+ nVisTab = 0;
+ const_cast<ScDocShell*>(this)->m_pDocument->SetVisibleTab(nVisTab);
+ }
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ m_pDocument->GetDataStart( nVisTab, nStartCol, nStartRow );
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ m_pDocument->GetPrintArea( nVisTab, nEndCol, nEndRow );
+ if (nStartCol>nEndCol)
+ nStartCol = nEndCol;
+ if (nStartRow>nEndRow)
+ nStartRow = nEndRow;
+ tools::Rectangle aNewArea = m_pDocument
+ ->GetMMRect( nStartCol,nStartRow, nEndCol,nEndRow, nVisTab );
+ return aNewArea;
+ }
+ else
+ return SfxObjectShell::GetVisArea( nAspect );
+}
+
+namespace {
+
+[[nodiscard]]
+tools::Long SnapHorizontal( const ScDocument& rDoc, SCTAB nTab, tools::Long nVal, SCCOL& rStartCol )
+{
+ SCCOL nCol = 0;
+ tools::Long nTwips = o3tl::convert(nVal, o3tl::Length::mm100, o3tl::Length::twip);
+ tools::Long nSnap = 0;
+ while ( nCol<rDoc.MaxCol() )
+ {
+ tools::Long nAdd = rDoc.GetColWidth(nCol, nTab);
+ if ( nSnap + nAdd/2 < nTwips || nCol < rStartCol )
+ {
+ nSnap += nAdd;
+ ++nCol;
+ }
+ else
+ break;
+ }
+ nVal = o3tl::convert(nSnap, o3tl::Length::twip, o3tl::Length::mm100);
+ rStartCol = nCol;
+ return nVal;
+}
+
+[[nodiscard]]
+tools::Long SnapVertical( const ScDocument& rDoc, SCTAB nTab, tools::Long nVal, SCROW& rStartRow )
+{
+ SCROW nRow = 0;
+ tools::Long nTwips = o3tl::convert(nVal, o3tl::Length::mm100, o3tl::Length::twip);
+ tools::Long nSnap = 0;
+
+ bool bFound = false;
+ for (SCROW i = nRow; i <= rDoc.MaxRow(); ++i)
+ {
+ SCROW nLastRow;
+ if (rDoc.RowHidden(i, nTab, nullptr, &nLastRow))
+ {
+ i = nLastRow;
+ continue;
+ }
+
+ nRow = i;
+ tools::Long nAdd = rDoc.GetRowHeight(i, nTab);
+ if ( nSnap + nAdd/2 < nTwips || nRow < rStartRow )
+ {
+ nSnap += nAdd;
+ ++nRow;
+ }
+ else
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ nRow = rDoc.MaxRow(); // all hidden down to the bottom
+
+ nVal = o3tl::convert(nSnap, o3tl::Length::twip, o3tl::Length::mm100);
+ rStartRow = nRow;
+ return nVal;
+}
+
+}
+
+void ScDocShell::SnapVisArea( tools::Rectangle& rRect ) const
+{
+ SCTAB nTab = m_pDocument->GetVisibleTab();
+ tools::Long nOrigTop = rRect.Top();
+ tools::Long nOrigLeft = rRect.Left();
+ bool bNegativePage = m_pDocument->IsNegativePage( nTab );
+ if ( bNegativePage )
+ ScDrawLayer::MirrorRectRTL( rRect ); // calculate with positive (LTR) values
+
+ SCCOL nCol = m_pDocument->GetPosLeft();
+ tools::Long nSetLeft = SnapHorizontal( *m_pDocument, nTab, rRect.Left(), nCol );
+ rRect.SetLeft( nSetLeft );
+ ++nCol; // at least one column
+ tools::Long nCorrectionLeft = (nOrigLeft == 0 && nCol > 0) ? nSetLeft : 0; // initial correction
+ rRect.SetRight( SnapHorizontal( *m_pDocument, nTab, rRect.Right() + nCorrectionLeft, nCol ));
+
+ SCROW nRow = m_pDocument->GetPosTop();
+ tools::Long nSetTop = SnapVertical( *m_pDocument, nTab, rRect.Top(), nRow );
+ rRect.SetTop( nSetTop );
+ ++nRow; // at least one row
+ tools::Long nCorrectionTop = (nOrigTop == 0 && nRow > 0) ? nSetTop : 0; // initial correction
+ rRect.SetBottom( SnapVertical( *m_pDocument, nTab, rRect.Bottom() + nCorrectionTop, nRow ));
+
+ if ( bNegativePage )
+ ScDrawLayer::MirrorRectRTL( rRect ); // back to real rectangle
+}
+
+void ScDocShell::GetPageOnFromPageStyleSet( const SfxItemSet* pStyleSet,
+ SCTAB nCurTab,
+ bool& rbHeader,
+ bool& rbFooter )
+{
+ if ( !pStyleSet )
+ {
+ ScStyleSheetPool* pStylePool = m_pDocument->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->
+ Find( m_pDocument->GetPageStyle( nCurTab ),
+ SfxStyleFamily::Page );
+
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pStyleSheet )
+ pStyleSet = &pStyleSheet->GetItemSet();
+ else
+ rbHeader = rbFooter = false;
+ }
+
+ OSL_ENSURE( pStyleSet, "PageStyle-Set not found! :-(" );
+ if (!pStyleSet)
+ return;
+
+ const SvxSetItem* pSetItem = nullptr;
+ const SfxItemSet* pSet = nullptr;
+
+ pSetItem = &pStyleSet->Get( ATTR_PAGE_HEADERSET );
+ pSet = &pSetItem->GetItemSet();
+ rbHeader = pSet->Get(ATTR_PAGE_ON).GetValue();
+
+ pSetItem = &pStyleSet->Get( ATTR_PAGE_FOOTERSET );
+ pSet = &pSetItem->GetItemSet();
+ rbFooter = pSet->Get(ATTR_PAGE_ON).GetValue();
+}
+
+#if defined(_WIN32)
+bool ScDocShell::DdeGetData( const OUString& rItem,
+ const OUString& rMimeType,
+ css::uno::Any & rValue )
+{
+ SotClipboardFormatId eFormatId = SotExchange::GetFormatIdFromMimeType( rMimeType );
+ if (SotClipboardFormatId::STRING == eFormatId || SotClipboardFormatId::STRING_TSVC == eFormatId)
+ {
+ if( rItem.equalsIgnoreAsciiCase( "Format" ) )
+ {
+ OString aFmtByte(OUStringToOString(m_aDdeTextFmt,
+ osl_getThreadTextEncoding()));
+ rValue <<= css::uno::Sequence< sal_Int8 >(
+ reinterpret_cast<const sal_Int8*>(aFmtByte.getStr()),
+ aFmtByte.getLength() + 1 );
+ return true;
+ }
+ ScImportExport aObj( *m_pDocument, rItem );
+ if ( !aObj.IsRef() )
+ return false; // invalid range
+
+ if( m_aDdeTextFmt[0] == 'F' )
+ aObj.SetFormulas( true );
+ if( m_aDdeTextFmt == "SYLK" ||
+ m_aDdeTextFmt == "FSYLK" )
+ {
+ OString aData;
+ if( aObj.ExportByteString( aData, osl_getThreadTextEncoding(),
+ SotClipboardFormatId::SYLK ) )
+ {
+ rValue <<= css::uno::Sequence< sal_Int8 >(
+ reinterpret_cast<const sal_Int8*>(aData.getStr()),
+ aData.getLength() + 1 );
+ return true;
+ }
+ else
+ return false;
+ }
+ if( m_aDdeTextFmt == "CSV" ||
+ m_aDdeTextFmt == "FCSV" )
+ aObj.SetSeparator( ',' );
+ aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, 0, false ) );
+ return aObj.ExportData( rMimeType, rValue );
+ }
+
+ ScImportExport aObj( *m_pDocument, rItem );
+ aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, 0, false ) );
+ return aObj.IsRef() && aObj.ExportData( rMimeType, rValue );
+}
+
+bool ScDocShell::DdeSetData( const OUString& rItem,
+ const OUString& rMimeType,
+ const css::uno::Any & rValue )
+{
+ SotClipboardFormatId eFormatId = SotExchange::GetFormatIdFromMimeType( rMimeType );
+ if (SotClipboardFormatId::STRING == eFormatId || SotClipboardFormatId::STRING_TSVC == eFormatId)
+ {
+ if( rItem.equalsIgnoreAsciiCase( "Format" ) )
+ {
+ if ( ScByteSequenceToString::GetString( m_aDdeTextFmt, rValue ) )
+ {
+ m_aDdeTextFmt = m_aDdeTextFmt.toAsciiUpperCase();
+ return true;
+ }
+ return false;
+ }
+ ScImportExport aObj( *m_pDocument, rItem );
+ if( m_aDdeTextFmt[0] == 'F' )
+ aObj.SetFormulas( true );
+ if( m_aDdeTextFmt == "SYLK" ||
+ m_aDdeTextFmt == "FSYLK" )
+ {
+ OUString aData;
+ if ( ScByteSequenceToString::GetString( aData, rValue ) )
+ {
+ return aObj.ImportString( aData, SotClipboardFormatId::SYLK );
+ }
+ return false;
+ }
+ if( m_aDdeTextFmt == "CSV" ||
+ m_aDdeTextFmt == "FCSV" )
+ aObj.SetSeparator( ',' );
+ OSL_ENSURE( false, "Implementation is missing" );
+ return false;
+ }
+ /*ScImportExport aObj( aDocument, rItem );
+ return aObj.IsRef() && ScImportExport::ImportData( rMimeType, rValue );*/
+ OSL_ENSURE( false, "Implementation is missing" );
+ return false;
+}
+#endif
+
+::sfx2::SvLinkSource* ScDocShell::DdeCreateLinkSource( const OUString& rItem )
+{
+ if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
+ return nullptr;
+
+ // only check for valid item string - range is parsed again in ScServerObject ctor
+
+ // named range?
+ OUString aPos = rItem;
+ ScRangeName* pRange = m_pDocument->GetRangeName();
+ if( pRange )
+ {
+ const ScRangeData* pData = pRange->findByUpperName(ScGlobal::getCharClass().uppercase(aPos));
+ if (pData)
+ {
+ if( pData->HasType( ScRangeData::Type::RefArea )
+ || pData->HasType( ScRangeData::Type::AbsArea )
+ || pData->HasType( ScRangeData::Type::AbsPos ) )
+ aPos = pData->GetSymbol(); // continue with the name's contents
+ }
+ }
+
+ // Address in DDE function must be always parsed as CONV_OOO so that it
+ // would always work regardless of current address conversion. We do this
+ // because the address item in a DDE entry is *not* normalized when saved
+ // into ODF.
+ ScRange aRange;
+ bool bValid = ( (aRange.Parse(aPos, *m_pDocument, formula::FormulaGrammar::CONV_OOO ) & ScRefFlags::VALID) ||
+ (aRange.aStart.Parse(aPos, *m_pDocument, formula::FormulaGrammar::CONV_OOO) & ScRefFlags::VALID) );
+
+ ScServerObject* pObj = nullptr; // NULL = error
+ if ( bValid )
+ pObj = new ScServerObject( this, rItem );
+
+ // GetLinkManager()->InsertServer() is in the ScServerObject ctor
+
+ return pObj;
+}
+
+void ScDocShell::LOKCommentNotify(LOKCommentNotificationType nType, const ScDocument& rDocument, const ScAddress& rPos, const ScPostIt* pNote)
+{
+ if ( !rDocument.IsDocVisible() || // don't want callbacks until document load
+ !comphelper::LibreOfficeKit::isActive() ||
+ comphelper::LibreOfficeKit::isTiledAnnotations() )
+ return;
+
+ tools::JsonWriter aAnnotation;
+ {
+ auto commentNode = aAnnotation.startNode("comment");
+ aAnnotation.put("action", (nType == LOKCommentNotificationType::Add ? "Add" :
+ (nType == LOKCommentNotificationType::Remove ? "Remove" :
+ (nType == LOKCommentNotificationType::Modify ? "Modify" : "???"))));
+
+ assert(pNote);
+ aAnnotation.put("id", pNote->GetId());
+ aAnnotation.put("tab", rPos.Tab());
+
+ if (nType != LOKCommentNotificationType::Remove)
+ {
+ aAnnotation.put("author", pNote->GetAuthor());
+ aAnnotation.put("dateTime", pNote->GetDate());
+ aAnnotation.put("text", pNote->GetText());
+
+ // Calculating the cell cursor position
+ ScViewData* pViewData = GetViewData();
+ if (pViewData && pViewData->GetActiveWin())
+ aAnnotation.put("cellRange", ScPostIt::NoteRangeToJsonString(rDocument, rPos));
+ }
+ }
+
+ OString aPayload = aAnnotation.finishAndGetAsOString();
+
+ ScViewData* pViewData = GetViewData();
+ SfxViewShell* pThisViewShell = ( pViewData ? pViewData->GetViewShell() : nullptr );
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pThisViewShell == nullptr || pViewShell->GetDocId() == pThisViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload);
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+ScViewData* ScDocShell::GetViewData()
+{
+ SfxViewShell* pCur = SfxViewShell::Current();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( pCur );
+ return pViewSh ? &pViewSh->GetViewData() : nullptr;
+}
+
+SCTAB ScDocShell::GetCurTab()
+{
+ //! this must be made non-static and use a ViewShell from this document!
+
+ ScViewData* pViewData = GetViewData();
+
+ return pViewData ? pViewData->GetTabNo() : static_cast<SCTAB>(0);
+}
+
+ScTabViewShell* ScDocShell::GetBestViewShell( bool bOnlyVisible )
+{
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ // wrong Doc?
+ if( pViewSh && pViewSh->GetViewData().GetDocShell() != this )
+ pViewSh = nullptr;
+ if( !pViewSh )
+ {
+ // 1. find ViewShell
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( this, bOnlyVisible );
+ if( pFrame )
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ }
+ }
+ return pViewSh;
+}
+
+SfxBindings* ScDocShell::GetViewBindings()
+{
+ // used to invalidate slots after changes to this document
+
+ SfxViewShell* pViewSh = GetBestViewShell();
+ if (pViewSh)
+ return &pViewSh->GetViewFrame().GetBindings();
+ else
+ return nullptr;
+}
+
+ScDocShell* ScDocShell::GetShellByNum( sal_uInt16 nDocNo ) // static
+{
+ ScDocShell* pFound = nullptr;
+ SfxObjectShell* pShell = SfxObjectShell::GetFirst();
+ sal_uInt16 nShellCnt = 0;
+
+ while ( pShell && !pFound )
+ {
+ if ( auto pDocSh = dynamic_cast<ScDocShell*>(pShell) )
+ {
+ if ( nShellCnt == nDocNo )
+ pFound = pDocSh;
+ else
+ ++nShellCnt;
+ }
+ pShell = SfxObjectShell::GetNext( *pShell );
+ }
+
+ return pFound;
+}
+
+IMPL_LINK( ScDocShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
+{
+ OSL_ENSURE( _pFileDlg, "ScDocShell::DialogClosedHdl(): no file dialog" );
+ OSL_ENSURE( m_pImpl->pDocInserter, "ScDocShell::DialogClosedHdl(): no document inserter" );
+
+ if ( ERRCODE_NONE == _pFileDlg->GetError() )
+ {
+ sal_uInt16 nSlot = m_pImpl->pRequest->GetSlot();
+ std::unique_ptr<SfxMedium> pMed = m_pImpl->pDocInserter->CreateMedium();
+ // #i87094# If a .odt was selected pMed is NULL.
+ if (pMed)
+ {
+ m_pImpl->pRequest->AppendItem( SfxStringItem( SID_FILE_NAME, pMed->GetName() ) );
+ if ( SID_DOCUMENT_COMPARE == nSlot )
+ {
+ if ( pMed->GetFilter() )
+ m_pImpl->pRequest->AppendItem(
+ SfxStringItem( SID_FILTER_NAME, pMed->GetFilter()->GetFilterName() ) );
+ OUString sOptions = ScDocumentLoader::GetOptions( *pMed );
+ if ( !sOptions.isEmpty() )
+ m_pImpl->pRequest->AppendItem( SfxStringItem( SID_FILE_FILTEROPTIONS, sOptions ) );
+ }
+ const SfxPoolItem* pItem = nullptr;
+ const SfxInt16Item* pInt16Item(nullptr);
+ if (pMed->GetItemSet().GetItemState(SID_VERSION, true, &pItem) == SfxItemState::SET)
+ {
+ pInt16Item = dynamic_cast<const SfxInt16Item*>(pItem);
+ }
+ if (pInt16Item)
+ {
+ m_pImpl->pRequest->AppendItem( *pItem );
+ }
+
+ Execute( *(m_pImpl->pRequest) );
+ }
+ }
+
+ m_pImpl->bIgnoreLostRedliningWarning = false;
+}
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+
+void ScDocShell::EnableSharedSettings( bool bEnable )
+{
+ SetDocumentModified();
+
+ if ( bEnable )
+ {
+ m_pDocument->EndChangeTracking();
+ m_pDocument->StartChangeTracking();
+
+ // hide accept or reject changes dialog
+ sal_uInt16 nId = ScAcceptChgDlgWrapper::GetChildWindowId();
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if ( pViewFrame && pViewFrame->HasChildWindow( nId ) )
+ {
+ pViewFrame->ToggleChildWindow( nId );
+ SfxBindings* pBindings = GetViewBindings();
+ if ( pBindings )
+ {
+ pBindings->Invalidate( FID_CHG_ACCEPT );
+ }
+ }
+ }
+ else
+ {
+ m_pDocument->EndChangeTracking();
+ }
+
+ ScChangeViewSettings aChangeViewSet;
+ aChangeViewSet.SetShowChanges( false );
+ m_pDocument->SetChangeViewSettings( aChangeViewSet );
+}
+
+uno::Reference< frame::XModel > ScDocShell::LoadSharedDocument()
+{
+ uno::Reference< frame::XModel > xModel;
+ try
+ {
+ SC_MOD()->SetInSharedDocLoading( true );
+ uno::Reference< frame::XDesktop2 > xLoader = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ uno::Sequence aArgs{ comphelper::makePropertyValue("Hidden", true) };
+
+ if ( GetMedium() )
+ {
+ const SfxStringItem* pPasswordItem = GetMedium()->GetItemSet().GetItem(SID_PASSWORD, false);
+ if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() )
+ {
+ aArgs.realloc( 2 );
+ auto pArgs = aArgs.getArray();
+ pArgs[1].Name = "Password";
+ pArgs[1].Value <<= pPasswordItem->GetValue();
+ }
+ const SfxUnoAnyItem* pEncryptionItem = GetMedium()->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
+ if (pEncryptionItem)
+ {
+ aArgs.realloc(aArgs.getLength() + 1);
+ auto pArgs = aArgs.getArray();
+ pArgs[aArgs.getLength() - 1].Name = "EncryptionData";
+ pArgs[aArgs.getLength() - 1].Value = pEncryptionItem->GetValue();
+ }
+ }
+
+ xModel.set(
+ xLoader->loadComponentFromURL( GetSharedFileURL(), "_blank", 0, aArgs ),
+ uno::UNO_QUERY_THROW );
+ SC_MOD()->SetInSharedDocLoading( false );
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "ScDocShell::LoadSharedDocument(): caught exception" );
+ SC_MOD()->SetInSharedDocLoading( false );
+ try
+ {
+ uno::Reference< util::XCloseable > xClose( xModel, uno::UNO_QUERY_THROW );
+ xClose->close( true );
+ return uno::Reference< frame::XModel >();
+ }
+ catch ( uno::Exception& )
+ {
+ return uno::Reference< frame::XModel >();
+ }
+ }
+ return xModel;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh5.cxx b/sc/source/ui/docshell/docsh5.cxx
new file mode 100644
index 0000000000..e4cec9200c
--- /dev/null
+++ b/sc/source/ui/docshell/docsh5.cxx
@@ -0,0 +1,1047 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <cassert>
+
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <unotools/charclass.hxx>
+
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+
+#include <dociter.hxx>
+#include <docsh.hxx>
+#include <global.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <globalnames.hxx>
+#include <undodat.hxx>
+#include <undotab.hxx>
+#include <undoblk.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dbdocfun.hxx>
+#include <consoli.hxx>
+#include <dbdata.hxx>
+#include <progress.hxx>
+#include <olinetab.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <docpool.hxx>
+#include <uiitems.hxx>
+#include <sc.hrc>
+#include <sizedev.hxx>
+#include <clipparam.hxx>
+#include <rowheightcontext.hxx>
+#include <refupdatecontext.hxx>
+
+using com::sun::star::script::XLibraryContainer;
+using com::sun::star::script::vba::XVBACompatibility;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::UNO_QUERY;
+
+using ::std::unique_ptr;
+using ::std::vector;
+
+// former viewfunc/dbfunc methods
+
+void ScDocShell::ErrorMessage(TranslateId pGlobStrId)
+{
+ //! StopMarking at the (active) view?
+
+ weld::Window* pParent = GetActiveDialogParent();
+ weld::WaitObject aWaitOff( pParent );
+ bool bFocus = pParent && pParent->has_focus();
+
+ if (pGlobStrId && pGlobStrId == STR_PROTECTIONERR)
+ {
+ if (IsReadOnly())
+ {
+ pGlobStrId = STR_READONLYERR;
+ }
+ }
+
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(pGlobStrId)));
+ xInfoBox->run();
+
+ if (bFocus)
+ pParent->grab_focus();
+}
+
+bool ScDocShell::IsEditable() const
+{
+ // import into read-only document is possible - must be extended if other filters use api
+ // #i108547# MSOOXML filter uses "IsChangeReadOnlyEnabled" property
+
+ return !IsReadOnly() || m_pDocument->IsImportingXML() || m_pDocument->IsChangeReadOnlyEnabled();
+}
+
+void ScDocShell::DBAreaDeleted( SCTAB nTab, SCCOL nX1, SCROW nY1, SCCOL nX2 )
+{
+ ScDocShellModificator aModificator( *this );
+ // the auto-filter is in the first row of the area
+ m_pDocument->RemoveFlagsTab( nX1, nY1, nX2, nY1, nTab, ScMF::Auto );
+ PostPaint( nX1, nY1, nTab, nX2, nY1, nTab, PaintPartFlags::Grid );
+ // No SetDocumentModified, as the unnamed database range might have to be restored later.
+ // The UNO hint is broadcast directly instead, to keep UNO objects in valid state.
+ m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) );
+}
+
+ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGetDBSelection eSel )
+{
+ SCCOL nCol = rMarked.aStart.Col();
+ SCROW nRow = rMarked.aStart.Row();
+ SCTAB nTab = rMarked.aStart.Tab();
+
+ SCCOL nStartCol = nCol;
+ SCROW nStartRow = nRow;
+ SCTAB nStartTab = nTab;
+ SCCOL nEndCol = rMarked.aEnd.Col();
+ SCROW nEndRow = rMarked.aEnd.Row();
+ // Not simply GetDBAtCursor: The continuous data range for "unnamed" (GetDataArea) may be
+ // located next to the cursor; so a named DB range needs to be searched for there as well.
+ ScDBCollection* pColl = m_pDocument->GetDBCollection();
+ ScDBData* pData = m_pDocument->GetDBAtArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ if (!pData)
+ pData = pColl->GetDBNearCursor(nCol, nRow, nTab );
+
+ bool bSelected = ( eSel == ScGetDBSelection::ForceMark ||
+ (rMarked.aStart != rMarked.aEnd && eSel != ScGetDBSelection::RowDown) );
+ bool bOnlyDown = (!bSelected && eSel == ScGetDBSelection::RowDown && rMarked.aStart.Row() == rMarked.aEnd.Row());
+
+ bool bUseThis = false;
+ if (pData)
+ {
+ // take range, if nothing else is marked
+
+ SCTAB nDummy;
+ SCCOL nOldCol1;
+ SCROW nOldRow1;
+ SCCOL nOldCol2;
+ SCROW nOldRow2;
+ pData->GetArea( nDummy, nOldCol1,nOldRow1, nOldCol2,nOldRow2 );
+ bool bIsNoName = ( pData->GetName() == STR_DB_LOCAL_NONAME );
+
+ if (!bSelected)
+ {
+ bUseThis = true;
+ if ( bIsNoName && (eMode == SC_DB_MAKE || eMode == SC_DB_AUTOFILTER) )
+ {
+ // If nothing marked or only one row marked, adapt
+ // "unnamed" to contiguous area.
+ nStartCol = nCol;
+ nStartRow = nRow;
+ if (bOnlyDown)
+ {
+ nEndCol = rMarked.aEnd.Col();
+ nEndRow = rMarked.aEnd.Row();
+ }
+ else
+ {
+ nEndCol = nStartCol;
+ nEndRow = nStartRow;
+ }
+ m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown );
+ if ( nOldCol1 != nStartCol || nOldCol2 != nEndCol || nOldRow1 != nStartRow )
+ bUseThis = false; // doesn't fit at all
+ else if ( nOldRow2 != nEndRow )
+ {
+ // extend range to new end row
+ pData->SetArea( nTab, nOldCol1,nOldRow1, nOldCol2,nEndRow );
+ }
+ }
+ }
+ else
+ {
+ if ( nOldCol1 == nStartCol && nOldRow1 == nStartRow &&
+ nOldCol2 == nEndCol && nOldRow2 == nEndRow ) // marked precisely?
+ bUseThis = true;
+ else
+ bUseThis = false; // always take marking (Bug 11964)
+ }
+
+ // never take "unnamed" for import
+
+ if ( bUseThis && eMode == SC_DB_IMPORT && bIsNoName )
+ bUseThis = false;
+ }
+
+ if ( bUseThis )
+ {
+ pData->GetArea( nStartTab, nStartCol,nStartRow, nEndCol,nEndRow );
+ }
+ else if ( eMode == SC_DB_OLD )
+ {
+ pData = nullptr; // nothing found
+ }
+ else
+ {
+ if ( !bSelected )
+ { // continuous range
+ nStartCol = nCol;
+ nStartRow = nRow;
+ if (bOnlyDown)
+ {
+ nEndCol = rMarked.aEnd.Col();
+ nEndRow = rMarked.aEnd.Row();
+ }
+ else
+ {
+ nEndCol = nStartCol;
+ nEndRow = nStartRow;
+ }
+ m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown );
+ }
+
+ bool bHasHeader = m_pDocument->HasColHeader( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+
+ ScDBData* pNoNameData = m_pDocument->GetAnonymousDBData(nTab);
+ if ( eMode != SC_DB_IMPORT && pNoNameData)
+ {
+ // Do not reset AutoFilter range during temporary operations on
+ // other ranges, use the document global temporary anonymous range
+ // instead. But, if AutoFilter is to be toggled then do use the
+ // sheet-local DB range.
+ bool bSheetLocal = true;
+ if (eMode != SC_DB_AUTOFILTER && pNoNameData->HasAutoFilter())
+ {
+ bSheetLocal = false;
+ pNoNameData = m_pDocument->GetAnonymousDBData();
+ if (!pNoNameData)
+ {
+ m_pDocument->SetAnonymousDBData( std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME,
+ nTab, nStartCol, nStartRow, nEndCol, nEndRow, true, bHasHeader) ) );
+ pNoNameData = m_pDocument->GetAnonymousDBData();
+ }
+ // ScDocShell::CancelAutoDBRange() would restore the
+ // sheet-local anonymous DBData from pOldAutoDBRange, unset so
+ // that won't happen with data of a previous sheet-local
+ // DBData.
+ m_pOldAutoDBRange.reset();
+ }
+ else if (!m_pOldAutoDBRange)
+ {
+ // store the old unnamed database range with its settings for undo
+ // (store at the first change, get the state before all changes)
+ m_pOldAutoDBRange.reset( new ScDBData( *pNoNameData ) );
+ }
+ else if (m_pOldAutoDBRange->GetTab() != pNoNameData->GetTab())
+ {
+ // Different sheet-local unnamed DB range than the previous one.
+ *m_pOldAutoDBRange = *pNoNameData;
+ }
+
+ SCCOL nOldX1; // take old range away cleanly
+ SCROW nOldY1; //! (UNDO ???)
+ SCCOL nOldX2;
+ SCROW nOldY2;
+ SCTAB nOldTab;
+ pNoNameData->GetArea( nOldTab, nOldX1, nOldY1, nOldX2, nOldY2 );
+
+ // If previously bHasHeader was set and the new range starts on the
+ // same row and intersects the old column range, then don't reset
+ // bHasHeader but assume that the new range still has headers, just
+ // some are empty or numeric.
+ if (!bHasHeader && pNoNameData->HasHeader() && nTab == nOldTab && nStartRow == nOldY1 &&
+ nStartCol <= nOldY2 && nOldY1 <= nEndCol)
+ bHasHeader = true;
+
+ // Remove AutoFilter button flags only for sheet-local DB range,
+ // not if a temporary is used.
+ if (bSheetLocal)
+ DBAreaDeleted( nOldTab, nOldX1, nOldY1, nOldX2 );
+
+ pNoNameData->SetSortParam( ScSortParam() ); // reset parameter
+ pNoNameData->SetQueryParam( ScQueryParam() );
+ pNoNameData->SetSubTotalParam( ScSubTotalParam() );
+
+ pNoNameData->SetArea( nTab, nStartCol,nStartRow, nEndCol,nEndRow ); // set anew
+ pNoNameData->SetByRow( true );
+ pNoNameData->SetHeader( bHasHeader );
+ pNoNameData->SetAutoFilter( false );
+ }
+ else
+ {
+ std::unique_ptr<ScDBCollection> pUndoColl;
+
+ if (eMode==SC_DB_IMPORT)
+ {
+ m_pDocument->PreprocessDBDataUpdate();
+ pUndoColl.reset( new ScDBCollection( *pColl ) ); // Undo for import range
+
+ OUString aImport = ScResId( STR_DBNAME_IMPORT );
+ tools::Long nCount = 0;
+ const ScDBData* pDummy = nullptr;
+ ScDBCollection::NamedDBs& rDBs = pColl->getNamedDBs();
+ OUString aNewName;
+ do
+ {
+ ++nCount;
+ aNewName = aImport + OUString::number( nCount );
+ pDummy = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(aNewName));
+ }
+ while (pDummy);
+ pNoNameData = new ScDBData( aNewName, nTab,
+ nStartCol,nStartRow, nEndCol,nEndRow,
+ true, bHasHeader );
+ bool ins = rDBs.insert(std::unique_ptr<ScDBData>(pNoNameData));
+ assert(ins); (void)ins;
+ }
+ else
+ {
+ pNoNameData = new ScDBData(STR_DB_LOCAL_NONAME, nTab,
+ nStartCol,nStartRow, nEndCol,nEndRow,
+ true, bHasHeader );
+ m_pDocument->SetAnonymousDBData(nTab, std::unique_ptr<ScDBData>(pNoNameData));
+ }
+
+ if ( pUndoColl )
+ {
+ m_pDocument->CompileHybridFormula();
+
+ GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDBData>( this,
+ std::move(pUndoColl),
+ std::make_unique<ScDBCollection>( *pColl ) ) );
+ }
+
+ // no longer needed to register new range at the Sba
+
+ // announce "Import1", etc., at the Navigator
+ if (eMode==SC_DB_IMPORT)
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ }
+ pData = pNoNameData;
+ }
+
+ return pData;
+}
+
+ScDBData* ScDocShell::GetAnonymousDBData(const ScRange& rRange)
+{
+ ScDBCollection* pColl = m_pDocument->GetDBCollection();
+ if (!pColl)
+ return nullptr;
+
+ ScDBData* pData = pColl->getAnonDBs().getByRange(rRange);
+ if (!pData)
+ return nullptr;
+
+ if (!pData->HasHeader())
+ {
+ bool bHasHeader = m_pDocument->HasColHeader(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab());
+ pData->SetHeader(bHasHeader);
+ }
+
+ return pData;
+}
+
+std::unique_ptr<ScDBData> ScDocShell::GetOldAutoDBRange()
+{
+ return std::move(m_pOldAutoDBRange);
+}
+
+void ScDocShell::CancelAutoDBRange()
+{
+ // called when dialog is cancelled
+//moggi:TODO
+ if ( !m_pOldAutoDBRange )
+ return;
+
+ SCTAB nTab = GetCurTab();
+ ScDBData* pDBData = m_pDocument->GetAnonymousDBData(nTab);
+ if ( pDBData )
+ {
+ SCCOL nRangeX1;
+ SCROW nRangeY1;
+ SCCOL nRangeX2;
+ SCROW nRangeY2;
+ SCTAB nRangeTab;
+ pDBData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
+
+ *pDBData = *m_pOldAutoDBRange; // restore old settings
+
+ if ( m_pOldAutoDBRange->HasAutoFilter() )
+ {
+ // restore AutoFilter buttons
+ m_pOldAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ m_pDocument->ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
+ PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid );
+ }
+ }
+
+ m_pOldAutoDBRange.reset();
+}
+
+ // adjust height
+ //! merge with docfunc
+
+bool ScDocShell::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab )
+{
+ ScSizeDeviceProvider aProv(this);
+ Fraction aZoom(1,1);
+ sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aZoom, aZoom, aProv.GetDevice());
+ bool bChange = m_pDocument->SetOptimalHeight(aCxt, nStartRow,nEndRow, nTab, true);
+
+ if (bChange)
+ {
+ // tdf#76183: recalculate objects' positions
+ m_pDocument->SetDrawPageSize(nTab);
+
+ PostPaint( 0,nStartRow,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid|PaintPartFlags::Left );
+ }
+
+ return bChange;
+}
+
+void ScDocShell::UpdateAllRowHeights( const ScMarkData* pTabMark )
+{
+ // update automatic row heights
+
+ ScSizeDeviceProvider aProv(this);
+ Fraction aZoom(1,1);
+ sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aZoom, aZoom, aProv.GetDevice());
+ m_pDocument->UpdateAllRowHeights(aCxt, pTabMark);
+}
+
+void ScDocShell::UpdateAllRowHeights(const bool bOnlyUsedRows)
+{
+ // update automatic row heights on all sheets using the newer ScDocRowHeightUpdater
+ ScSizeDeviceProvider aProv(this);
+ ScDocRowHeightUpdater aUpdater(*m_pDocument, aProv.GetDevice(), aProv.GetPPTX(),
+ aProv.GetPPTY(), nullptr);
+ aUpdater.update(bOnlyUsedRows);
+}
+
+void ScDocShell::UpdatePendingRowHeights( SCTAB nUpdateTab, bool bBefore )
+{
+ bool bIsUndoEnabled = m_pDocument->IsUndoEnabled();
+ m_pDocument->EnableUndo( false );
+ m_pDocument->LockStreamValid( true ); // ignore draw page size (but not formula results)
+ if ( bBefore ) // check all sheets up to nUpdateTab
+ {
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ if ( nUpdateTab >= nTabCount )
+ nUpdateTab = nTabCount-1; // nUpdateTab is inclusive
+
+ ScMarkData aUpdateSheets(m_pDocument->GetSheetLimits());
+ SCTAB nTab;
+ for (nTab=0; nTab<=nUpdateTab; ++nTab)
+ if ( m_pDocument->IsPendingRowHeights( nTab ) )
+ aUpdateSheets.SelectTable( nTab, true );
+
+ if (aUpdateSheets.GetSelectCount())
+ UpdateAllRowHeights(&aUpdateSheets); // update with a single progress bar
+
+ for (nTab=0; nTab<=nUpdateTab; ++nTab)
+ if ( aUpdateSheets.GetTableSelect( nTab ) )
+ {
+ m_pDocument->UpdatePageBreaks( nTab );
+ m_pDocument->SetPendingRowHeights( nTab, false );
+ }
+ }
+ else // only nUpdateTab
+ {
+ if ( m_pDocument->IsPendingRowHeights( nUpdateTab ) )
+ {
+ AdjustRowHeight( 0, m_pDocument->MaxRow(), nUpdateTab );
+ m_pDocument->UpdatePageBreaks( nUpdateTab );
+ m_pDocument->SetPendingRowHeights( nUpdateTab, false );
+ }
+ }
+ m_pDocument->LockStreamValid( false );
+ m_pDocument->EnableUndo( bIsUndoEnabled );
+}
+
+void ScDocShell::RefreshPivotTables( const ScRange& rSource )
+{
+ ScDPCollection* pColl = m_pDocument->GetDPCollection();
+ if (!pColl)
+ return;
+
+ ScDBDocFunc aFunc(*this);
+ for (size_t i = 0, n = pColl->GetCount(); i < n; ++i)
+ {
+ ScDPObject& rOld = (*pColl)[i];
+
+ const ScSheetSourceDesc* pSheetDesc = rOld.GetSheetDesc();
+ if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rSource))
+ aFunc.UpdatePivotTable(rOld, true, false);
+ }
+}
+
+static OUString lcl_GetAreaName( ScDocument* pDoc, const ScArea* pArea )
+{
+ ScDBData* pData = pDoc->GetDBAtArea( pArea->nTab, pArea->nColStart, pArea->nRowStart,
+ pArea->nColEnd, pArea->nRowEnd );
+ if (pData)
+ return pData->GetName();
+
+ OUString aName;
+ pDoc->GetName( pArea->nTab, aName );
+ return aName;
+}
+
+void ScDocShell::DoConsolidate( const ScConsolidateParam& rParam, bool bRecord )
+{
+ ScConsData aData;
+
+ sal_uInt16 nPos;
+ SCCOL nColSize = 0;
+ SCROW nRowSize = 0;
+ bool bErr = false;
+ for (nPos=0; nPos<rParam.nDataAreaCount; nPos++)
+ {
+ ScArea const & rArea = rParam.pDataAreas[nPos];
+ nColSize = std::max( nColSize, SCCOL( rArea.nColEnd - rArea.nColStart + 1 ) );
+ nRowSize = std::max( nRowSize, SCROW( rArea.nRowEnd - rArea.nRowStart + 1 ) );
+
+ // test if source data were moved
+ if (rParam.bReferenceData)
+ if (rArea.nTab == rParam.nTab && rArea.nRowEnd >= rParam.nRow)
+ bErr = true;
+ }
+
+ if (bErr)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_CONSOLIDATE_ERR1)));
+ xInfoBox->run();
+ return;
+ }
+
+ // execute
+
+ weld::WaitObject aWait( GetActiveDialogParent() );
+ ScDocShellModificator aModificator( *this );
+
+ ScRange aOldDest;
+ ScDBData* pDestData = m_pDocument->GetDBAtCursor( rParam.nCol, rParam.nRow, rParam.nTab, ScDBDataPortion::TOP_LEFT );
+ if (pDestData)
+ pDestData->GetArea(aOldDest);
+
+ aData.SetSize( nColSize, nRowSize );
+ aData.SetFlags( rParam.eFunction, rParam.bByCol, rParam.bByRow, rParam.bReferenceData );
+ if ( rParam.bByCol || rParam.bByRow )
+ for (nPos=0; nPos<rParam.nDataAreaCount; nPos++)
+ {
+ ScArea const & rArea = rParam.pDataAreas[nPos];
+ aData.AddFields( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart,
+ rArea.nColEnd, rArea.nRowEnd );
+ }
+ aData.DoneFields();
+ for (nPos=0; nPos<rParam.nDataAreaCount; nPos++)
+ {
+ ScArea const & rArea = rParam.pDataAreas[nPos];
+ aData.AddData( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart,
+ rArea.nColEnd, rArea.nRowEnd );
+ aData.AddName( lcl_GetAreaName(m_pDocument.get(), &rArea) );
+ }
+
+ aData.GetSize( nColSize, nRowSize );
+ if (bRecord && nColSize > 0 && nRowSize > 0)
+ {
+ std::unique_ptr<ScDBData> pUndoData(pDestData ? new ScDBData(*pDestData) : nullptr);
+
+ SCTAB nDestTab = rParam.nTab;
+ ScArea aDestArea( rParam.nTab, rParam.nCol, rParam.nRow,
+ rParam.nCol+nColSize-1, rParam.nRow+nRowSize-1 );
+ if (rParam.bByCol) ++aDestArea.nColEnd;
+ if (rParam.bByRow) ++aDestArea.nRowEnd;
+
+ if (rParam.bReferenceData)
+ {
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ SCROW nInsertCount = aData.GetInsertCount();
+
+ // old outlines
+ ScOutlineTable* pTable = m_pDocument->GetOutlineTable( nDestTab );
+ std::unique_ptr<ScOutlineTable> pUndoTab(pTable ? new ScOutlineTable( *pTable ) : nullptr);
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( *m_pDocument, 0, nTabCount-1, false, true );
+
+ // row state
+ m_pDocument->CopyToDocument(0, 0, nDestTab, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nDestTab,
+ InsertDeleteFlags::NONE, false, *pUndoDoc);
+
+ // all formulas
+ m_pDocument->CopyToDocument(0, 0, 0, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nTabCount-1,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc);
+
+ // complete output rows
+ m_pDocument->CopyToDocument(0, aDestArea.nRowStart, nDestTab,
+ m_pDocument->MaxCol(),aDestArea.nRowEnd, nDestTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ // old output range
+ if (pDestData)
+ m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConsolidate>( this, aDestArea, rParam, std::move(pUndoDoc),
+ true, nInsertCount, std::move(pUndoTab), std::move(pUndoData) ) );
+ }
+ else
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( *m_pDocument, aDestArea.nTab, aDestArea.nTab );
+
+ m_pDocument->CopyToDocument(aDestArea.nColStart, aDestArea.nRowStart, aDestArea.nTab,
+ aDestArea.nColEnd, aDestArea.nRowEnd, aDestArea.nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ // old output range
+ if (pDestData)
+ m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc);
+
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConsolidate>( this, aDestArea, rParam, std::move(pUndoDoc),
+ false, 0, nullptr, std::move(pUndoData) ) );
+ }
+ }
+
+ if (pDestData) // delete / adjust destination range
+ {
+ m_pDocument->DeleteAreaTab(aOldDest, InsertDeleteFlags::CONTENTS);
+ pDestData->SetArea( rParam.nTab, rParam.nCol, rParam.nRow,
+ rParam.nCol + nColSize - 1, rParam.nRow + nRowSize - 1 );
+ pDestData->SetHeader( rParam.bByRow );
+ }
+
+ aData.OutputToDocument( *m_pDocument, rParam.nCol, rParam.nRow, rParam.nTab );
+
+ SCCOL nPaintStartCol = rParam.nCol;
+ SCROW nPaintStartRow = rParam.nRow;
+ SCCOL nPaintEndCol = nPaintStartCol + nColSize - 1;
+ SCROW nPaintEndRow = nPaintStartRow + nRowSize - 1;
+ PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
+ if (rParam.bByCol)
+ ++nPaintEndRow;
+ if (rParam.bByRow)
+ ++nPaintEndCol;
+ if (rParam.bReferenceData)
+ {
+ nPaintStartCol = 0;
+ nPaintEndCol = m_pDocument->MaxCol();
+ nPaintEndRow = m_pDocument->MaxRow();
+ nPaintFlags |= PaintPartFlags::Left | PaintPartFlags::Size;
+ }
+ if (pDestData)
+ {
+ if ( aOldDest.aEnd.Col() > nPaintEndCol )
+ nPaintEndCol = aOldDest.aEnd.Col();
+ if ( aOldDest.aEnd.Row() > nPaintEndRow )
+ nPaintEndRow = aOldDest.aEnd.Row();
+ }
+ PostPaint( nPaintStartCol, nPaintStartRow, rParam.nTab,
+ nPaintEndCol, nPaintEndRow, rParam.nTab, nPaintFlags );
+ aModificator.SetDocumentModified();
+}
+
+void ScDocShell::UseScenario( SCTAB nTab, const OUString& rName, bool bRecord )
+{
+ if (!m_pDocument->IsScenario(nTab))
+ {
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ SCTAB nSrcTab = SCTAB_MAX;
+ SCTAB nEndTab = nTab;
+ OUString aCompare;
+ while ( nEndTab+1 < nTabCount && m_pDocument->IsScenario(nEndTab+1) )
+ {
+ ++nEndTab;
+ if (nSrcTab > MAXTAB) // still searching for the scenario?
+ {
+ m_pDocument->GetName( nEndTab, aCompare );
+ if (aCompare == rName)
+ nSrcTab = nEndTab; // found
+ }
+ }
+ if (ValidTab(nSrcTab))
+ {
+ if ( m_pDocument->TestCopyScenario( nSrcTab, nTab ) ) // test cell protection
+ {
+ ScDocShellModificator aModificator( *this );
+ ScMarkData aScenMark(m_pDocument->GetSheetLimits());
+ m_pDocument->MarkScenario( nSrcTab, nTab, aScenMark );
+ const ScRange& aMultiRange = aScenMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiRange.aStart.Col();
+ SCROW nStartRow = aMultiRange.aStart.Row();
+ SCCOL nEndCol = aMultiRange.aEnd.Col();
+ SCROW nEndRow = aMultiRange.aEnd.Row();
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( *m_pDocument, nTab,nEndTab ); // also all scenarios
+ // shown table:
+ m_pDocument->CopyToDocument(nStartCol, nStartRow, nTab,
+ nEndCol, nEndRow, nTab, InsertDeleteFlags::ALL,
+ true, *pUndoDoc, &aScenMark);
+ // scenarios
+ for (SCTAB i=nTab+1; i<=nEndTab; i++)
+ {
+ pUndoDoc->SetScenario( i, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ m_pDocument->GetScenarioData( i, aComment, aColor, nScenFlags );
+ pUndoDoc->SetScenarioData( i, aComment, aColor, nScenFlags );
+ bool bActive = m_pDocument->IsActiveScenario( i );
+ pUndoDoc->SetActiveScenario( i, bActive );
+ // At copy-back scenarios also contents
+ if ( nScenFlags & ScScenarioFlags::TwoWay )
+ m_pDocument->CopyToDocument(0, 0, i, m_pDocument->MaxCol(), m_pDocument->MaxRow(), i,
+ InsertDeleteFlags::ALL, false, *pUndoDoc );
+ }
+
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoUseScenario>( this, aScenMark,
+ ScArea( nTab,nStartCol,nStartRow,nEndCol,nEndRow ),
+ std::move(pUndoDoc), rName ) );
+ }
+
+ m_pDocument->CopyScenario( nSrcTab, nTab );
+
+ sc::SetFormulaDirtyContext aCxt;
+ m_pDocument->SetAllFormulasDirty(aCxt);
+
+ // paint all, because the active scenario may be modified in other ranges;
+ //! only if there are visible frames?
+ PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid );
+ aModificator.SetDocumentModified();
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_PROTECTIONERR)));
+ xInfoBox->run();
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_SCENARIO_NOTFOUND)));
+ xInfoBox->run();
+ }
+ }
+ else
+ {
+ OSL_FAIL( "UseScenario on Scenario-Sheet" );
+ }
+}
+
+void ScDocShell::ModifyScenario( SCTAB nTab, const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags )
+{
+ // Undo
+ OUString aOldName;
+ m_pDocument->GetName( nTab, aOldName );
+ OUString aOldComment;
+ Color aOldColor;
+ ScScenarioFlags nOldFlags;
+ m_pDocument->GetScenarioData( nTab, aOldComment, aOldColor, nOldFlags );
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoScenarioFlags>(this, nTab,
+ aOldName, rName, aOldComment, rComment,
+ aOldColor, rColor, nOldFlags, nFlags) );
+
+ // execute
+ ScDocShellModificator aModificator( *this );
+ m_pDocument->RenameTab( nTab, rName );
+ m_pDocument->SetScenarioData( nTab, rComment, rColor, nFlags );
+ PostPaintGridAll();
+ aModificator.SetDocumentModified();
+
+ if (aOldName != rName)
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_SELECT_SCENARIO );
+}
+
+SCTAB ScDocShell::MakeScenario( SCTAB nTab, const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags,
+ ScMarkData& rMark, bool bRecord )
+{
+ rMark.MarkToMulti();
+ if (rMark.IsMultiMarked())
+ {
+ SCTAB nNewTab = nTab + 1;
+ while (m_pDocument->IsScenario(nNewTab))
+ ++nNewTab;
+
+ bool bCopyAll = ( (nFlags & ScScenarioFlags::CopyAll) != ScScenarioFlags::NONE );
+ const ScMarkData* pCopyMark = nullptr;
+ if (!bCopyAll)
+ pCopyMark = &rMark;
+
+ ScDocShellModificator aModificator( *this );
+
+ if (bRecord)
+ m_pDocument->BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ if (m_pDocument->CopyTab( nTab, nNewTab, pCopyMark ))
+ {
+ if (bRecord)
+ {
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMakeScenario>( this, nTab, nNewTab,
+ rName, rComment, rColor, nFlags, rMark ));
+ }
+
+ m_pDocument->RenameTab( nNewTab, rName);
+ m_pDocument->SetScenario( nNewTab, true );
+ m_pDocument->SetScenarioData( nNewTab, rComment, rColor, nFlags );
+
+ ScMarkData aDestMark = rMark;
+ aDestMark.SelectOneTable( nNewTab );
+
+ //! test for filter / buttons / merging
+
+ ScPatternAttr aProtPattern( m_pDocument->GetPool() );
+ aProtPattern.GetItemSet().Put( ScProtectionAttr( true ) );
+ m_pDocument->ApplyPatternAreaTab( 0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(), nNewTab, aProtPattern );
+
+ ScPatternAttr aPattern( m_pDocument->GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) );
+ aPattern.GetItemSet().Put( ScProtectionAttr( true ) );
+ m_pDocument->ApplySelectionPattern( aPattern, aDestMark );
+
+ if (!bCopyAll)
+ m_pDocument->SetVisible( nNewTab, false );
+
+ // this is the active scenario, then
+ m_pDocument->CopyScenario( nNewTab, nTab, true ); // sal_True - don't copy anything from scenario
+
+ if (nFlags & ScScenarioFlags::ShowFrame)
+ PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid ); // paint frames
+ PostPaintExtras(); // table tab
+ aModificator.SetDocumentModified();
+
+ // A scenario tab is like a hidden sheet, broadcasting also
+ // notifies ScTabViewShell to add an ScViewData::maTabData entry.
+ Broadcast( ScTablesHint( SC_TAB_INSERTED, nNewTab ));
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+
+ return nNewTab;
+ }
+ }
+ return nTab;
+}
+
+sal_uLong ScDocShell::TransferTab( ScDocShell& rSrcDocShell, SCTAB nSrcPos,
+ SCTAB nDestPos, bool bInsertNew,
+ bool bNotifyAndPaint )
+{
+ ScDocument& rSrcDoc = rSrcDocShell.GetDocument();
+
+ // set the transferred area to the copyparam to make adjusting formulas possible
+ ScClipParam aParam;
+ ScRange aRange(0, 0, nSrcPos, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nSrcPos);
+ aParam.maRanges.push_back(aRange);
+ rSrcDoc.SetClipParam(aParam);
+
+ sal_uLong nErrVal = m_pDocument->TransferTab( rSrcDoc, nSrcPos, nDestPos,
+ bInsertNew ); // no insert
+
+ // TransferTab doesn't copy drawing objects with bInsertNew=FALSE
+ if ( nErrVal > 0 && !bInsertNew)
+ m_pDocument->TransferDrawPage( rSrcDoc, nSrcPos, nDestPos );
+
+ if(nErrVal>0 && rSrcDoc.IsScenario( nSrcPos ))
+ {
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+
+ rSrcDoc.GetScenarioData( nSrcPos, aComment,aColor, nFlags);
+ m_pDocument->SetScenario(nDestPos,true);
+ m_pDocument->SetScenarioData(nDestPos,aComment,aColor,nFlags);
+ bool bActive = rSrcDoc.IsActiveScenario(nSrcPos);
+ m_pDocument->SetActiveScenario(nDestPos, bActive );
+
+ bool bVisible = rSrcDoc.IsVisible(nSrcPos);
+ m_pDocument->SetVisible(nDestPos,bVisible );
+
+ }
+
+ if ( nErrVal > 0 && rSrcDoc.IsTabProtected( nSrcPos ) )
+ m_pDocument->SetTabProtection(nDestPos, rSrcDoc.GetTabProtection(nSrcPos));
+ if ( bNotifyAndPaint )
+ {
+ Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestPos ) );
+ PostPaintExtras();
+ PostPaintGridAll();
+ }
+ return nErrVal;
+}
+
+bool ScDocShell::MoveTable( SCTAB nSrcTab, SCTAB nDestTab, bool bCopy, bool bRecord )
+{
+ ScDocShellModificator aModificator( *this );
+
+ // #i92477# be consistent with ScDocFunc::InsertTable: any index past the last sheet means "append"
+ // #i101139# nDestTab must be the target position, not APPEND (for CopyTabProtection etc.)
+ if ( nDestTab >= m_pDocument->GetTableCount() )
+ nDestTab = m_pDocument->GetTableCount();
+
+ if (bCopy)
+ {
+ if (bRecord)
+ m_pDocument->BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ OUString sSrcCodeName;
+ m_pDocument->GetCodeName( nSrcTab, sSrcCodeName );
+ if (!m_pDocument->CopyTab( nSrcTab, nDestTab ))
+ {
+ //! EndDrawUndo?
+ return false;
+ }
+ else
+ {
+ SCTAB nAdjSource = nSrcTab;
+ if ( nDestTab <= nSrcTab )
+ ++nAdjSource; // new position of source table after CopyTab
+
+ if ( m_pDocument->IsTabProtected( nAdjSource ) )
+ m_pDocument->CopyTabProtection(nAdjSource, nDestTab);
+
+ if (bRecord)
+ {
+ unique_ptr< vector<SCTAB> > pSrcList(new vector<SCTAB>(1, nSrcTab));
+ unique_ptr< vector<SCTAB> > pDestList(new vector<SCTAB>(1, nDestTab));
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoCopyTab>(this, std::move(pSrcList), std::move(pDestList)));
+ }
+
+ bool bVbaEnabled = m_pDocument->IsInVBAMode();
+ if ( bVbaEnabled )
+ {
+ OUString aLibName( "Standard" );
+ Reference< XLibraryContainer > xLibContainer = GetBasicContainer();
+ Reference< XVBACompatibility > xVBACompat( xLibContainer, UNO_QUERY );
+
+ if ( xVBACompat.is() )
+ {
+ aLibName = xVBACompat->getProjectName();
+ }
+
+ SCTAB nTabToUse = nDestTab;
+ if ( nDestTab == SC_TAB_APPEND )
+ nTabToUse = m_pDocument->GetMaxTableNumber() - 1;
+ OUString sSource;
+ try
+ {
+ Reference< XNameContainer > xLib;
+ if( xLibContainer.is() )
+ {
+ css::uno::Any aLibAny = xLibContainer->getByName( aLibName );
+ aLibAny >>= xLib;
+ }
+ if( xLib.is() )
+ {
+ xLib->getByName( sSrcCodeName ) >>= sSource;
+ }
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+ VBA_InsertModule( *m_pDocument, nTabToUse, sSource );
+ }
+ }
+ Broadcast( ScTablesHint( SC_TAB_COPIED, nSrcTab, nDestTab ) );
+ }
+ else
+ {
+ if ( m_pDocument->GetChangeTrack() )
+ return false;
+
+ if ( nSrcTab<nDestTab && nDestTab!=SC_TAB_APPEND )
+ nDestTab--;
+
+ if ( nSrcTab == nDestTab )
+ {
+ //! allow only for api calls?
+ return true; // nothing to do, but valid
+ }
+
+ std::optional<ScProgress> pProgress(std::in_place, this, ScResId(STR_UNDO_MOVE_TAB),
+ m_pDocument->GetCodeCount(), true);
+ bool bDone = m_pDocument->MoveTab( nSrcTab, nDestTab, &*pProgress );
+ pProgress.reset();
+ if (!bDone)
+ {
+ return false;
+ }
+ else if (bRecord)
+ {
+ unique_ptr< vector<SCTAB> > pSrcList(new vector<SCTAB>(1, nSrcTab));
+ unique_ptr< vector<SCTAB> > pDestList(new vector<SCTAB>(1, nDestTab));
+ GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMoveTab>(this, std::move(pSrcList), std::move(pDestList)));
+ }
+
+ Broadcast( ScTablesHint( SC_TAB_MOVED, nSrcTab, nDestTab ) );
+ }
+
+ PostPaintGridAll();
+ PostPaintExtras();
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+
+ return true;
+}
+
+IMPL_LINK( ScDocShell, RefreshDBDataHdl, Timer*, pRefreshTimer, void )
+{
+ ScDBDocFunc aFunc(*this);
+
+ ScDBData* pDBData = static_cast<ScDBData*>(pRefreshTimer);
+ ScImportParam aImportParam;
+ pDBData->GetImportParam( aImportParam );
+ if (aImportParam.bImport && !pDBData->HasImportSelection())
+ {
+ ScRange aRange;
+ pDBData->GetArea( aRange );
+ bool bContinue = aFunc.DoImport( aRange.aStart.Tab(), aImportParam, nullptr ); //! Api-Flag as parameter
+ // internal operations (sort, query, subtotal) only if no error
+ if (bContinue)
+ {
+ aFunc.RepeatDB( pDBData->GetName(), true, true );
+ RefreshPivotTables(aRange);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh6.cxx b/sc/source/ui/docshell/docsh6.cxx
new file mode 100644
index 0000000000..caefdfc0fe
--- /dev/null
+++ b/sc/source/ui/docshell/docsh6.cxx
@@ -0,0 +1,517 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <svx/pageitem.hxx>
+#include <sfx2/linkmgr.hxx>
+
+#include <docsh.hxx>
+
+#include <stlpool.hxx>
+#include <global.hxx>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <tablink.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <compiler.hxx>
+#include <interpre.hxx>
+#include <formulaopt.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <memory>
+#include <utility>
+
+namespace {
+
+struct ScStylePair
+{
+ SfxStyleSheetBase *pSource;
+ SfxStyleSheetBase *pDest;
+};
+
+}
+
+// Ole
+
+void ScDocShell::SetVisArea( const tools::Rectangle & rVisArea )
+{
+ // with the SnapVisArea call in SetVisAreaOrSize, it's safe to always
+ // use both the size and position of the VisArea
+ SetVisAreaOrSize( rVisArea );
+}
+
+static void lcl_SetTopRight( tools::Rectangle& rRect, const Point& rPos )
+{
+ Size aSize = rRect.GetSize();
+ rRect.SetRight( rPos.X() );
+ rRect.SetLeft( rPos.X() - aSize.Width() + 1 );
+ rRect.SetTop( rPos.Y() );
+ rRect.SetBottom( rPos.Y() + aSize.Height() - 1 );
+}
+
+void ScDocShell::SetVisAreaOrSize( const tools::Rectangle& rVisArea )
+{
+ bool bNegativePage = m_pDocument->IsNegativePage( m_pDocument->GetVisibleTab() );
+
+ tools::Rectangle aArea = rVisArea;
+ // when loading, don't check for negative values, because the sheet orientation
+ // might be set later
+ if ( !m_pDocument->IsImportingXML() )
+ {
+ if ( ( bNegativePage ? (aArea.Right() > 0) : (aArea.Left() < 0) ) || aArea.Top() < 0 )
+ {
+ // VisArea start position can't be negative.
+ // Move the VisArea, otherwise only the upper left position would
+ // be changed in SnapVisArea, and the size would be wrong.
+
+ Point aNewPos( 0, std::max( aArea.Top(), tools::Long(0) ) );
+ if ( bNegativePage )
+ {
+ aNewPos.setX( std::min( aArea.Right(), tools::Long(0) ) );
+ lcl_SetTopRight( aArea, aNewPos );
+ }
+ else
+ {
+ aNewPos.setX( std::max( aArea.Left(), tools::Long(0) ) );
+ aArea.SetPos( aNewPos );
+ }
+ }
+ }
+
+ // adjust position here!
+
+ // when loading an ole object, the VisArea is set from the document's
+ // view settings and must be used as-is (document content may not be complete yet).
+ if ( !m_pDocument->IsImportingXML() )
+ SnapVisArea( aArea );
+
+ //TODO/LATER: it's unclear which IPEnv is used here
+ /*
+ SvInPlaceEnvironment* pEnv = GetIPEnv();
+ if (pEnv)
+ {
+ vcl::Window* pWin = pEnv->GetEditWin();
+ pEnv->MakeScale( aArea.GetSize(), MapUnit::Map100thMM,
+ pWin->LogicToPixel( aArea.GetSize() ) );
+ } */
+
+ //TODO/LATER: formerly in SvInplaceObject
+ SfxObjectShell::SetVisArea( aArea );
+
+ if (m_bIsInplace) // adjust zoom in the InPlace View
+ {
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if (pViewSh)
+ {
+ if (pViewSh->GetViewData().GetDocShell() == this)
+ pViewSh->UpdateOleZoom();
+ }
+ }
+
+ if (!m_pDocument->IsEmbedded())
+ return;
+
+ ScRange aOld;
+ m_pDocument->GetEmbedded( aOld);
+ m_pDocument->SetEmbedded( m_pDocument->GetVisibleTab(), aArea );
+ ScRange aNew;
+ m_pDocument->GetEmbedded( aNew);
+ if (aOld != aNew)
+ PostPaint(0,0,0,m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB,PaintPartFlags::Grid);
+
+ //TODO/LATER: currently not implemented
+ //ViewChanged( ASPECT_CONTENT ); // show in the container as well
+}
+
+bool ScDocShell::IsOle() const
+{
+ return (GetCreateMode() == SfxObjectCreateMode::EMBEDDED);
+}
+
+void ScDocShell::UpdateOle(const ScViewData& rViewData, bool bSnapSize)
+{
+ // if it isn't Ole at all, one can be spared the calculations
+ // (VisArea will then be reset at the save)
+
+ if (GetCreateMode() == SfxObjectCreateMode::STANDARD)
+ return;
+
+ tools::Rectangle aOldArea = SfxObjectShell::GetVisArea();
+ tools::Rectangle aNewArea = aOldArea;
+
+ bool bEmbedded = m_pDocument->IsEmbedded();
+ if (bEmbedded)
+ aNewArea = m_pDocument->GetEmbeddedRect();
+ else
+ {
+ SCTAB nTab = rViewData.GetTabNo();
+ if ( nTab != m_pDocument->GetVisibleTab() )
+ m_pDocument->SetVisibleTab( nTab );
+
+ bool bNegativePage = m_pDocument->IsNegativePage( nTab );
+ SCCOL nX = rViewData.GetPosX(SC_SPLIT_LEFT);
+ if ( nX != m_pDocument->GetPosLeft() )
+ m_pDocument->SetPosLeft( nX );
+ SCROW nY = rViewData.GetPosY(SC_SPLIT_BOTTOM);
+ if ( nY != m_pDocument->GetPosTop() )
+ m_pDocument->SetPosTop( nY );
+ tools::Rectangle aMMRect = m_pDocument->GetMMRect( nX,nY, nX,nY, nTab );
+ if (bNegativePage)
+ lcl_SetTopRight( aNewArea, aMMRect.TopRight() );
+ else
+ aNewArea.SetPos( aMMRect.TopLeft() );
+ if (bSnapSize)
+ SnapVisArea(aNewArea); // uses the new VisibleTab
+ }
+
+ if (aNewArea != aOldArea)
+ SetVisAreaOrSize( aNewArea ); // the start must also be adjusted here
+}
+
+// Style stuff for Organizer, etc.
+
+SfxStyleSheetBasePool* ScDocShell::GetStyleSheetPool()
+{
+ return static_cast<SfxStyleSheetBasePool*>(m_pDocument->GetStyleSheetPool());
+}
+
+// After loading styles from another document (LoadStyles, Insert), the SetItems
+// (ATTR_PAGE_HEADERSET, ATTR_PAGE_FOOTERSET) must be converted to the correct pool
+// before the source pool is deleted.
+
+static void lcl_AdjustPool( SfxStyleSheetBasePool* pStylePool )
+{
+ SfxStyleSheetBase *pStyle = pStylePool->First(SfxStyleFamily::Page);
+ while ( pStyle )
+ {
+ SfxItemSet& rStyleSet = pStyle->GetItemSet();
+
+ if (const SvxSetItem* pItem = rStyleSet.GetItemIfSet(ATTR_PAGE_HEADERSET,false))
+ {
+ const SfxItemSet& rSrcSet = pItem->GetItemSet();
+ SfxItemSet aDestSet(*rStyleSet.GetPool(),rSrcSet.GetRanges());
+ aDestSet.Put(rSrcSet);
+ rStyleSet.Put(SvxSetItem(ATTR_PAGE_HEADERSET, std::move(aDestSet)));
+ }
+ if (const SvxSetItem* pItem = rStyleSet.GetItemIfSet(ATTR_PAGE_FOOTERSET,false))
+ {
+ const SfxItemSet& rSrcSet = pItem->GetItemSet();
+ SfxItemSet aDestSet(*rStyleSet.GetPool(),rSrcSet.GetRanges());
+ aDestSet.Put(rSrcSet);
+ rStyleSet.Put(SvxSetItem(ATTR_PAGE_FOOTERSET, std::move(aDestSet)));
+ }
+
+ pStyle = pStylePool->Next();
+ }
+}
+
+void ScDocShell::LoadStyles( SfxObjectShell &rSource )
+{
+ m_pDocument->StylesToNames();
+
+ SfxObjectShell::LoadStyles(rSource);
+ lcl_AdjustPool( GetStyleSheetPool() ); // adjust SetItems
+
+ m_pDocument->UpdStlShtPtrsFrmNms();
+
+ UpdateAllRowHeights();
+
+ // Paint
+
+ PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, PaintPartFlags::Grid | PaintPartFlags::Left );
+}
+
+void ScDocShell::LoadStylesArgs( ScDocShell& rSource, bool bReplace, bool bCellStyles, bool bPageStyles )
+{
+ // similar to LoadStyles, but with selectable behavior for XStyleLoader::loadStylesFromURL call
+
+ if ( !bCellStyles && !bPageStyles ) // nothing to do
+ return;
+
+ ScStyleSheetPool* pSourcePool = rSource.GetDocument().GetStyleSheetPool();
+ ScStyleSheetPool* pDestPool = m_pDocument->GetStyleSheetPool();
+
+ SfxStyleFamily eFamily = bCellStyles ?
+ ( bPageStyles ? SfxStyleFamily::All : SfxStyleFamily::Para ) :
+ SfxStyleFamily::Page;
+ SfxStyleSheetIterator aIter( pSourcePool, eFamily );
+ sal_uInt16 nSourceCount = aIter.Count();
+ if ( nSourceCount == 0 )
+ return; // no source styles
+
+ std::unique_ptr<ScStylePair[]> pStyles(new ScStylePair[ nSourceCount ]);
+ sal_uInt16 nFound = 0;
+
+ // first create all new styles
+
+ SfxStyleSheetBase* pSourceStyle = aIter.First();
+ while (pSourceStyle)
+ {
+ OUString aName = pSourceStyle->GetName();
+ SfxStyleSheetBase* pDestStyle = pDestPool->Find( pSourceStyle->GetName(), pSourceStyle->GetFamily() );
+ if ( pDestStyle )
+ {
+ // touch existing styles only if replace flag is set
+ if ( bReplace )
+ {
+ pStyles[nFound].pSource = pSourceStyle;
+ pStyles[nFound].pDest = pDestStyle;
+ ++nFound;
+ }
+ }
+ else
+ {
+ pStyles[nFound].pSource = pSourceStyle;
+ pStyles[nFound].pDest = &pDestPool->Make( aName, pSourceStyle->GetFamily(), pSourceStyle->GetMask() );
+ ++nFound;
+ }
+
+ pSourceStyle = aIter.Next();
+ }
+
+ // then copy contents (after inserting all styles, for parent etc.)
+
+ for ( sal_uInt16 i = 0; i < nFound; ++i )
+ {
+ pStyles[i].pDest->GetItemSet().PutExtended(
+ pStyles[i].pSource->GetItemSet(), SfxItemState::DONTCARE, SfxItemState::DEFAULT);
+ if(pStyles[i].pSource->HasParentSupport())
+ pStyles[i].pDest->SetParent(pStyles[i].pSource->GetParent());
+ // follow is never used
+ }
+
+ lcl_AdjustPool( GetStyleSheetPool() ); // adjust SetItems
+ UpdateAllRowHeights();
+ PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, PaintPartFlags::Grid | PaintPartFlags::Left ); // Paint
+}
+
+void ScDocShell::ReconnectDdeLink(SfxObjectShell& rServer)
+{
+ ::sfx2::LinkManager* pLinkManager = m_pDocument->GetLinkManager();
+ if (!pLinkManager)
+ return;
+
+ pLinkManager->ReconnectDdeLink(rServer);
+}
+
+void ScDocShell::UpdateLinks()
+{
+ typedef std::unordered_set<OUString> StrSetType;
+
+ sfx2::LinkManager* pLinkManager = m_pDocument->GetLinkManager();
+ StrSetType aNames;
+
+ // out with the no longer used links
+
+ size_t nCount = pLinkManager->GetLinks().size();
+ for (size_t k=nCount; k>0; )
+ {
+ --k;
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[k].get();
+ if (ScTableLink* pTabLink = dynamic_cast<ScTableLink*>(pBase))
+ {
+ if (pTabLink->IsUsed())
+ aNames.insert(pTabLink->GetFileName());
+ else // no longer used -> delete
+ {
+ pTabLink->SetAddUndo(true);
+ pLinkManager->Remove(k);
+ }
+ }
+ }
+
+ // enter new links
+
+ SCTAB nTabCount = m_pDocument->GetTableCount();
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ if (!m_pDocument->IsLinked(i))
+ continue;
+
+ OUString aDocName = m_pDocument->GetLinkDoc(i);
+ OUString aFltName = m_pDocument->GetLinkFlt(i);
+ OUString aOptions = m_pDocument->GetLinkOpt(i);
+ sal_uLong nRefresh = m_pDocument->GetLinkRefreshDelay(i);
+ bool bThere = false;
+ for (SCTAB j = 0; j < i && !bThere; ++j) // several times in the document?
+ {
+ if (m_pDocument->IsLinked(j)
+ && m_pDocument->GetLinkDoc(j) == aDocName
+ && m_pDocument->GetLinkFlt(j) == aFltName
+ && m_pDocument->GetLinkOpt(j) == aOptions)
+ // Ignore refresh delay in compare, it should be the
+ // same for identical links and we don't want dupes
+ // if it ain't.
+ bThere = true;
+ }
+
+ if (!bThere) // already entered as filter?
+ {
+ if (!aNames.insert(aDocName).second)
+ bThere = true;
+ }
+
+ if (!bThere)
+ {
+ ScTableLink* pLink = new ScTableLink( this, aDocName, aFltName, aOptions, nRefresh );
+ pLink->SetInCreate(true);
+ pLinkManager->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile, aDocName, &aFltName);
+ pLink->Update();
+ pLink->SetInCreate(false);
+ }
+ }
+}
+
+void ScDocShell::ReloadTabLinks()
+{
+ sfx2::LinkManager* pLinkManager = m_pDocument->GetLinkManager();
+
+ bool bAny = false;
+ size_t nCount = pLinkManager->GetLinks().size();
+ for (size_t i=0; i<nCount; i++ )
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
+ if (ScTableLink* pTabLink = dynamic_cast<ScTableLink*>(pBase))
+ {
+// pTabLink->SetAddUndo(sal_False); //! merge Undos
+
+ // Painting only after Update() makes no sense:
+ // ScTableLink::Refresh() will post a Paint only is bDoPaint is true
+ // pTabLink->SetPaint(false); // Paint only once at the end
+ pTabLink->Update();
+ //pTabLink->SetPaint(true);
+// pTabLink->SetAddUndo(sal_True);
+ bAny = true;
+ }
+ }
+
+ if ( bAny )
+ {
+ // Paint only once
+ PostPaint( ScRange(0,0,0,m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB),
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left );
+
+ SetDocumentModified();
+ }
+}
+
+void ScDocShell::SetFormulaOptions( const ScFormulaOptions& rOpt, bool bForLoading )
+{
+ m_pDocument->SetGrammar( rOpt.GetFormulaSyntax() );
+
+ // This is nasty because it resets module globals from within a docshell!
+ // For actual damage caused see fdo#82183 where an unconditional
+ // ScGlobal::ResetFunctionList() (without checking GetUseEnglishFuncName())
+ // lead to a crash because the function list was still used by the Formula
+ // Wizard when loading the second document.
+ // Do the stupid stuff only when we're not called while loading a document.
+
+ /* TODO: bForLoading is a workaround, rather get rid of setting any
+ * globals from per document instances like ScDocShell. */
+
+ /* XXX this is utter crap, we rely on the options being set here at least
+ * once, for the very first document, empty or loaded. */
+ static bool bInitOnce = true;
+
+ if (!bForLoading || bInitOnce)
+ {
+ bool bForceInit = bInitOnce;
+ bInitOnce = false;
+ if (bForceInit || rOpt.GetUseEnglishFuncName() != SC_MOD()->GetFormulaOptions().GetUseEnglishFuncName())
+ {
+ // This needs to be called first since it may re-initialize the entire
+ // opcode map.
+ if (rOpt.GetUseEnglishFuncName())
+ {
+ // switch native symbols to English.
+ ScAddress aAddress;
+ ScCompiler aComp( *m_pDocument, aAddress);
+ ScCompiler::OpCodeMapPtr xMap = aComp.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH);
+ ScCompiler::SetNativeSymbols(xMap);
+ }
+ else
+ // re-initialize native symbols with localized function names.
+ ScCompiler::ResetNativeSymbols();
+
+ // Force re-population of function names for the function wizard, function tip etc.
+ ScGlobal::ResetFunctionList();
+ }
+
+ // Update the separators.
+ ScCompiler::UpdateSeparatorsNative(
+ rOpt.GetFormulaSepArg(), rOpt.GetFormulaSepArrayCol(), rOpt.GetFormulaSepArrayRow());
+
+ // Global interpreter settings.
+ ScInterpreter::SetGlobalConfig(rOpt.GetCalcConfig());
+ }
+
+ // Per document interpreter settings.
+ m_pDocument->SetCalcConfig( rOpt.GetCalcConfig() );
+}
+
+void ScDocShell::CheckConfigOptions()
+{
+ if (IsConfigOptionsChecked())
+ // no need to check repeatedly.
+ return;
+
+ OUString aDecSep = ScGlobal::getLocaleData().getNumDecimalSep();
+ OUString aDecSepAlt = ScGlobal::getLocaleData().getNumDecimalSepAlt();
+
+ ScModule* pScMod = SC_MOD();
+ const ScFormulaOptions& rOpt=pScMod->GetFormulaOptions();
+ const OUString& aSepArg = rOpt.GetFormulaSepArg();
+ const OUString& aSepArrRow = rOpt.GetFormulaSepArrayRow();
+ const OUString& aSepArrCol = rOpt.GetFormulaSepArrayCol();
+
+ if (aDecSep == aSepArg || aDecSep == aSepArrRow || aDecSep == aSepArrCol ||
+ aDecSepAlt == aSepArg || aDecSepAlt == aSepArrRow || aDecSepAlt == aSepArrCol)
+ {
+ // One of arg separators conflicts with the current decimal
+ // separator. Reset them to default.
+ ScFormulaOptions aNew = rOpt;
+ aNew.ResetFormulaSeparators();
+ SetFormulaOptions(aNew);
+ pScMod->SetFormulaOptions(aNew);
+
+ // Launch a nice warning dialog to let the users know of this change.
+ ScTabViewShell* pViewShell = GetBestViewShell();
+ if (pViewShell)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pViewShell->GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_OPTIONS_WARN_SEPARATORS)));
+ xInfoBox->run();
+ }
+
+ // For now, this is the only option setting that could launch info
+ // dialog. But in the future we may want to implement a nicer
+ // dialog to display a list of warnings in case we have several
+ // pieces of information to display.
+ }
+
+ SetConfigOptionsChecked(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docsh8.cxx b/sc/source/ui/docshell/docsh8.cxx
new file mode 100644
index 0000000000..cc0be89a4b
--- /dev/null
+++ b/sc/source/ui/docshell/docsh8.cxx
@@ -0,0 +1,1082 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <vcl/errinf.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/converter.hxx>
+#include <svl/numformat.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/types.hxx>
+#include <ucbhelper/content.hxx>
+#include <svx/txenctab.hxx>
+#include <unotools/sharedunocomponent.hxx>
+#include <unotools/charclass.hxx>
+#include <rtl/character.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/DriverManager.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbc/XRowUpdate.hpp>
+#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
+#include <com/sun/star/sdbcx/XAppend.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
+#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+
+#include <scerrors.hxx>
+#include <docsh.hxx>
+#include <progress.hxx>
+#include <editutil.hxx>
+#include <cellform.hxx>
+#include <dbdocutl.hxx>
+#include <dociter.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <svl/zformat.hxx>
+#include <svl/intitem.hxx>
+#include <patattr.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+#include <segmenttree.hxx>
+#include <docparam.hxx>
+#include <cellvalue.hxx>
+
+#include <unordered_set>
+#include <vector>
+
+using namespace com::sun::star;
+using ::std::vector;
+
+#if HAVE_FEATURE_DBCONNECTIVITY
+
+constexpr OUString SC_SERVICE_ROWSET = u"com.sun.star.sdb.RowSet"_ustr;
+
+//! move to a header file?
+constexpr OUString SC_DBPROP_ACTIVECONNECTION = u"ActiveConnection"_ustr;
+constexpr OUString SC_DBPROP_COMMAND = u"Command"_ustr;
+constexpr OUString SC_DBPROP_COMMANDTYPE = u"CommandType"_ustr;
+constexpr OUStringLiteral SC_DBPROP_PROPCHANGE_NOTIFY = u"PropertyChangeNotificationEnabled";
+
+constexpr OUString SC_DBPROP_NAME = u"Name"_ustr;
+constexpr OUStringLiteral SC_DBPROP_TYPE = u"Type";
+constexpr OUStringLiteral SC_DBPROP_PRECISION = u"Precision";
+constexpr OUStringLiteral SC_DBPROP_SCALE = u"Scale";
+
+constexpr OUString SC_DBPROP_EXTENSION = u"Extension"_ustr;
+constexpr OUString SC_DBPROP_CHARSET = u"CharSet"_ustr;
+
+namespace
+{
+ ErrCode lcl_getDBaseConnection(uno::Reference<sdbc::XDriverManager2>& _rDrvMgr, uno::Reference<sdbc::XConnection>& _rConnection, OUString& _rTabName, std::u16string_view rFullFileName, rtl_TextEncoding eCharSet)
+ {
+ INetURLObject aURL;
+ aURL.SetSmartProtocol( INetProtocol::File );
+ aURL.SetSmartURL( rFullFileName );
+ _rTabName = aURL.getBase( INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::Unambiguous );
+ OUString aExtension = aURL.getExtension();
+ aURL.removeSegment();
+ aURL.removeFinalSlash();
+ OUString aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ _rDrvMgr.set( sdbc::DriverManager::create( xContext ) );
+
+ // get connection
+
+ const OUString aConnUrl{"sdbc:dbase:" + aPath};
+
+ // sdbc:dbase is based on the css.sdbc.FILEConnectionProperties UNOIDL service, so we can
+ // transport the raw rtl_TextEncoding value instead of having to translate it into an IANA
+ // character set name string (which might not exist for certain eCharSet values, like
+ // RTL_TEXTENCODING_MS_950):
+ uno::Sequence<beans::PropertyValue> aProps( comphelper::InitPropertySequence({
+ { SC_DBPROP_EXTENSION, uno::Any(aExtension) },
+ { SC_DBPROP_CHARSET, uno::Any(eCharSet) }
+ }));
+
+ _rConnection = _rDrvMgr->getConnectionWithInfo( aConnUrl, aProps );
+ return ERRCODE_NONE;
+ }
+}
+
+#endif // HAVE_FEATURE_DBCONNECTIVITY
+
+// MoveFile/KillFile/IsDocument: similar to SfxContentHelper
+
+bool ScDocShell::MoveFile( const INetURLObject& rSourceObj, const INetURLObject& rDestObj )
+{
+ bool bMoveData = true;
+ bool bRet = true, bKillSource = false;
+ if ( rSourceObj.GetProtocol() != rDestObj.GetProtocol() )
+ {
+ bMoveData = false;
+ bKillSource = true;
+ }
+ OUString aName = rDestObj.getName();
+ INetURLObject aDestPathObj = rDestObj;
+ aDestPathObj.removeSegment();
+ aDestPathObj.setFinalSlash();
+
+ try
+ {
+ ::ucbhelper::Content aDestPath( aDestPathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ uno::Reference< css::ucb::XCommandInfo > xInfo = aDestPath.getCommands();
+ OUString aTransferName = "transfer";
+ if ( xInfo->hasCommandByName( aTransferName ) )
+ {
+ aDestPath.executeCommand( aTransferName, uno::Any(
+ css::ucb::TransferInfo( bMoveData, rSourceObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aName,
+ css::ucb::NameClash::ERROR ) ) );
+ }
+ else
+ {
+ OSL_FAIL( "transfer command not available" );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // ucb may throw different exceptions on failure now
+ bRet = false;
+ }
+
+ if ( bKillSource )
+ KillFile( rSourceObj );
+
+ return bRet;
+}
+
+bool ScDocShell::KillFile( const INetURLObject& rURL )
+{
+ bool bRet = true;
+ try
+ {
+ ::ucbhelper::Content aCnt( rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", css::uno::Any( true ) );
+ }
+ catch( uno::Exception& )
+ {
+ // ucb may throw different exceptions on failure now
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+bool ScDocShell::IsDocument( const INetURLObject& rURL )
+{
+ bool bRet = false;
+ try
+ {
+ ::ucbhelper::Content aCnt( rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ bRet = aCnt.isDocument();
+ }
+ catch( uno::Exception& )
+ {
+ // ucb may throw different exceptions on failure now - warning only
+ TOOLS_WARN_EXCEPTION( "sc", "Any other exception" );
+ }
+
+ return bRet;
+}
+
+#if HAVE_FEATURE_DBCONNECTIVITY
+
+static void lcl_setScalesToColumns(ScDocument& rDoc, const vector<tools::Long>& rScales)
+{
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ if (!pFormatter)
+ return;
+
+ SCCOL nColCount = static_cast<SCCOL>(rScales.size());
+ for (SCCOL i = 0; i < nColCount; ++i)
+ {
+ if (rScales[i] < 0)
+ continue;
+
+ sal_uInt32 nOldFormat = rDoc.GetNumberFormat(i, 0, 0);
+ const SvNumberformat* pOldEntry = pFormatter->GetEntry(nOldFormat);
+ if (!pOldEntry)
+ continue;
+
+ LanguageType eLang = pOldEntry->GetLanguage();
+ bool bThousand, bNegRed;
+ sal_uInt16 nPrecision, nLeading;
+ pOldEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrecision, nLeading);
+
+ nPrecision = static_cast<sal_uInt16>(rScales[i]);
+ OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLang,
+ bThousand, bNegRed, nPrecision, nLeading);
+
+ sal_uInt32 nNewFormat = pFormatter->GetEntryKey(aNewPicture, eLang);
+ if (nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nNewType = SvNumFormatType::ALL;
+ bool bOk = pFormatter->PutEntry(
+ aNewPicture, nErrPos, nNewType, nNewFormat, eLang);
+
+ if (!bOk)
+ continue;
+ }
+
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNewFormat) );
+ rDoc.ApplyPatternAreaTab(i, 0, i, rDoc.MaxRow(), 0, aNewAttrs);
+ }
+}
+
+#endif // HAVE_FEATURE_DBCONNECTIVITY
+
+ErrCode ScDocShell::DBaseImport( const OUString& rFullFileName, rtl_TextEncoding eCharSet,
+ std::map<SCCOL, ScColWidthParam>& aColWidthParam, ScFlatBoolRowSegments& rRowHeightsRecalc )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY
+ (void) rFullFileName;
+ (void) eCharSet;
+ (void) aColWidthParam;
+ (void) rRowHeightsRecalc;
+
+ return ERRCODE_IO_GENERAL;
+#else
+
+ ErrCode nErr = ERRCODE_NONE;
+
+ try
+ {
+ tools::Long i;
+ sal_Int32 nColCount = 0;
+ OUString aTabName;
+ uno::Reference<sdbc::XDriverManager2> xDrvMan;
+ uno::Reference<sdbc::XConnection> xConnection;
+ ErrCode nRet = lcl_getDBaseConnection(xDrvMan,xConnection,aTabName,rFullFileName,eCharSet);
+ if ( !xConnection.is() || !xDrvMan.is() )
+ return nRet;
+ ::utl::DisposableComponent aConnectionHelper(xConnection);
+
+ ScProgress aProgress( this, ScResId( STR_LOAD_DOC ), 0, true );
+ uno::Reference<lang::XMultiServiceFactory> xFactory = comphelper::getProcessServiceFactory();
+ uno::Reference<sdbc::XRowSet> xRowSet( xFactory->createInstance(SC_SERVICE_ROWSET),
+ uno::UNO_QUERY);
+ ::utl::DisposableComponent aRowSetHelper(xRowSet);
+ uno::Reference<beans::XPropertySet> xRowProp( xRowSet, uno::UNO_QUERY );
+ OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
+ if (!xRowProp.is()) return SCERR_IMPORT_CONNECT;
+
+ xRowProp->setPropertyValue( SC_DBPROP_ACTIVECONNECTION, uno::Any(xConnection) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, uno::Any(sdb::CommandType::TABLE) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_COMMAND, uno::Any(aTabName) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_PROPCHANGE_NOTIFY, uno::Any(false) );
+
+ xRowSet->execute();
+
+ uno::Reference<sdbc::XResultSetMetaData> xMeta;
+ uno::Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp( xRowSet, uno::UNO_QUERY );
+ if ( xMetaSupp.is() )
+ xMeta = xMetaSupp->getMetaData();
+ if ( xMeta.is() )
+ nColCount = xMeta->getColumnCount(); // this is the number of real columns
+
+ if ( nColCount > m_pDocument->MaxCol()+1 )
+ {
+ nColCount = m_pDocument->MaxCol()+1;
+ nErr = SCWARN_IMPORT_COLUMN_OVERFLOW; // warning
+ }
+
+ uno::Reference<sdbc::XRow> xRow( xRowSet, uno::UNO_QUERY );
+ OSL_ENSURE( xRow.is(), "can't get Row" );
+ if (!xRow.is()) return SCERR_IMPORT_CONNECT;
+
+ // currency flag is not needed for dBase
+ uno::Sequence<sal_Int32> aColTypes( nColCount ); // column types
+ sal_Int32* pTypeArr = aColTypes.getArray();
+ for (i=0; i<nColCount; i++)
+ pTypeArr[i] = xMeta->getColumnType( i+1 );
+
+ // read column names
+ //! add type descriptions
+
+ aProgress.SetState( 0 );
+
+ vector<tools::Long> aScales(nColCount, -1);
+ for (i=0; i<nColCount; i++)
+ {
+ OUString aHeader = xMeta->getColumnLabel( i+1 );
+
+ switch ( pTypeArr[i] )
+ {
+ case sdbc::DataType::BIT:
+ aHeader += ",L";
+ break;
+ case sdbc::DataType::DATE:
+ aHeader += ",D";
+ break;
+ case sdbc::DataType::LONGVARCHAR:
+ aHeader += ",M";
+ break;
+ case sdbc::DataType::VARCHAR:
+ aHeader += ",C," + OUString::number( xMeta->getColumnDisplaySize( i+1 ) );
+ break;
+ case sdbc::DataType::DECIMAL:
+ {
+ tools::Long nPrec = xMeta->getPrecision( i+1 );
+ tools::Long nScale = xMeta->getScale( i+1 );
+ aHeader += ",N," +
+ OUString::number(
+ SvDbaseConverter::ConvertPrecisionToDbase(
+ nPrec, nScale ) ) +
+ "," +
+ OUString::number( nScale );
+ aScales[i] = nScale;
+ }
+ break;
+ }
+
+ m_pDocument->SetString( static_cast<SCCOL>(i), 0, 0, aHeader );
+ }
+
+ lcl_setScalesToColumns(*m_pDocument, aScales);
+
+ SCROW nRow = 1; // 0 is column titles
+ bool bEnd = false;
+ while ( !bEnd && xRowSet->next() )
+ {
+ if ( nRow <= m_pDocument->MaxRow() )
+ {
+ bool bSimpleRow = true;
+ SCCOL nCol = 0;
+ for (i=0; i<nColCount; i++)
+ {
+ ScDatabaseDocUtil::StrData aStrData;
+ ScDatabaseDocUtil::PutData( *m_pDocument, nCol, nRow, 0,
+ xRow, i+1, pTypeArr[i], false,
+ &aStrData );
+
+ if (aStrData.mnStrLength > aColWidthParam[nCol].mnMaxTextLen)
+ {
+ aColWidthParam[nCol].mnMaxTextLen = aStrData.mnStrLength;
+ aColWidthParam[nCol].mnMaxTextRow = nRow;
+ }
+
+ if (!aStrData.mbSimpleText)
+ {
+ bSimpleRow = false;
+ aColWidthParam[nCol].mbSimpleText = false;
+ }
+
+ ++nCol;
+ }
+ if (!bSimpleRow)
+ rRowHeightsRecalc.setTrue(nRow, nRow);
+ ++nRow;
+ }
+ else // past the end of the spreadsheet
+ {
+ bEnd = true; // don't continue
+ nErr = SCWARN_IMPORT_RANGE_OVERFLOW; // warning message
+ }
+ }
+ }
+ catch ( sdbc::SQLException& )
+ {
+ nErr = SCERR_IMPORT_CONNECT;
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
+ nErr = ERRCODE_IO_GENERAL;
+ }
+
+ return nErr;
+#endif // HAVE_FEATURE_DBCONNECTIVITY
+}
+
+#if HAVE_FEATURE_DBCONNECTIVITY
+
+namespace {
+
+void lcl_GetColumnTypes(
+ ScDocShell& rDocShell, const ScRange& rDataRange, bool bHasFieldNames,
+ OUString* pColNames, sal_Int32* pColTypes, sal_Int32* pColLengths,
+ sal_Int32* pColScales, bool& bHasMemo, rtl_TextEncoding eCharSet )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SvNumberFormatter* pNumFmt = rDoc.GetFormatTable();
+
+ SCTAB nTab = rDataRange.aStart.Tab();
+ SCCOL nFirstCol = rDataRange.aStart.Col();
+ SCROW nFirstRow = rDataRange.aStart.Row();
+ SCCOL nLastCol = rDataRange.aEnd.Col();
+ SCROW nLastRow = rDataRange.aEnd.Row();
+
+ typedef std::unordered_set<OUString> StrSetType;
+ StrSetType aFieldNames;
+
+ tools::Long nField = 0;
+ SCROW nFirstDataRow = ( bHasFieldNames ? nFirstRow + 1 : nFirstRow );
+ for ( SCCOL nCol = nFirstCol; nCol <= nLastCol; nCol++ )
+ {
+ bool bTypeDefined = false;
+ bool bPrecDefined = false;
+ sal_Int32 nFieldLen = 0;
+ sal_Int32 nPrecision = 0;
+ sal_Int32 nDbType = sdbc::DataType::SQLNULL;
+ OUString aFieldName;
+
+ // Fieldname[,Type[,Width[,Prec]]]
+ // Type etc.: L; D; C[,W]; N[,W[,P]]
+ if ( bHasFieldNames )
+ {
+ OUString aString {rDoc.GetString(nCol, nFirstRow, nTab).toAsciiUpperCase()};
+ sal_Int32 nIdx {0};
+ aFieldName = aString.getToken( 0, ',', nIdx);
+ if ( nIdx>0 )
+ {
+ aString = aString.replaceAll(" ", "");
+ switch ( o3tl::getToken(aString, 0, ',', nIdx )[0] )
+ {
+ case 'L' :
+ nDbType = sdbc::DataType::BIT;
+ nFieldLen = 1;
+ bTypeDefined = true;
+ bPrecDefined = true;
+ break;
+ case 'D' :
+ nDbType = sdbc::DataType::DATE;
+ nFieldLen = 8;
+ bTypeDefined = true;
+ bPrecDefined = true;
+ break;
+ case 'M' :
+ nDbType = sdbc::DataType::LONGVARCHAR;
+ nFieldLen = 10;
+ bTypeDefined = true;
+ bPrecDefined = true;
+ bHasMemo = true;
+ break;
+ case 'C' :
+ nDbType = sdbc::DataType::VARCHAR;
+ bTypeDefined = true;
+ bPrecDefined = true;
+ break;
+ case 'N' :
+ nDbType = sdbc::DataType::DECIMAL;
+ bTypeDefined = true;
+ break;
+ }
+ if ( bTypeDefined && !nFieldLen && nIdx>0 )
+ {
+ nFieldLen = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nIdx ));
+ if ( !bPrecDefined && nIdx>0 )
+ {
+ OUString aTmp( aString.getToken( 0, ',', nIdx ) );
+ if ( CharClass::isAsciiNumeric(aTmp) )
+ {
+ nPrecision = aTmp.toInt32();
+ if (nPrecision && nFieldLen < nPrecision+1)
+ nFieldLen = nPrecision + 1; // include decimal separator
+ bPrecDefined = true;
+ }
+ }
+ }
+ }
+
+ // Check field name and generate valid field name if necessary.
+ // First character has to be alphabetical, subsequent characters
+ // have to be alphanumerical or underscore.
+ // "_DBASELOCK" is reserved (obsolete because first character is
+ // not alphabetical).
+ // No duplicated names.
+ if ( !rtl::isAsciiAlpha(aFieldName[0]) )
+ aFieldName = "N" + aFieldName;
+ OUStringBuffer aTmpStr;
+ sal_Unicode c;
+ for ( const sal_Unicode* p = aFieldName.getStr(); ( c = *p ) != 0; p++ )
+ {
+ if ( rtl::isAsciiAlpha(c) || rtl::isAsciiDigit(c) || c == '_' )
+ aTmpStr.append(c);
+ else
+ aTmpStr.append("_");
+ }
+ aFieldName = aTmpStr.makeStringAndClear();
+ if ( aFieldName.getLength() > 10 )
+ aFieldName = aFieldName.copy(0, 10);
+
+ if (!aFieldNames.insert(aFieldName).second)
+ { // Duplicated field name, append numeric suffix.
+ sal_uInt16 nSub = 1;
+ OUString aFixPart( aFieldName );
+ do
+ {
+ ++nSub;
+ OUString aVarPart = OUString::number( nSub );
+ if ( aFixPart.getLength() + aVarPart.getLength() > 10 )
+ aFixPart = aFixPart.copy( 0, 10 - aVarPart.getLength() );
+ aFieldName = aFixPart + aVarPart;
+ } while (!aFieldNames.insert(aFieldName).second);
+ }
+ }
+ else
+ {
+ aFieldName = "N" + OUString::number(nCol+1);
+ }
+
+ if ( !bTypeDefined )
+ { // Field type.
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nFirstDataRow, nTab));
+ if (aCell.isEmpty() || aCell.hasString())
+ nDbType = sdbc::DataType::VARCHAR;
+ else
+ {
+ sal_uInt32 nFormat = rDoc.GetNumberFormat( nCol, nFirstDataRow, nTab );
+ switch ( pNumFmt->GetType( nFormat ) )
+ {
+ case SvNumFormatType::LOGICAL :
+ nDbType = sdbc::DataType::BIT;
+ nFieldLen = 1;
+ break;
+ case SvNumFormatType::DATE :
+ nDbType = sdbc::DataType::DATE;
+ nFieldLen = 8;
+ break;
+ case SvNumFormatType::TIME :
+ case SvNumFormatType::DATETIME :
+ nDbType = sdbc::DataType::VARCHAR;
+ break;
+ default:
+ nDbType = sdbc::DataType::DECIMAL;
+ }
+ }
+ }
+ // Field length.
+ if ( nDbType == sdbc::DataType::VARCHAR && !nFieldLen )
+ { // Determine maximum field width.
+ nFieldLen = rDoc.GetMaxStringLen( nTab, nCol, nFirstDataRow,
+ nLastRow, eCharSet );
+ if ( nFieldLen == 0 )
+ nFieldLen = 1;
+ }
+ else if ( nDbType == sdbc::DataType::DECIMAL )
+ { // Determine maximum field width and precision.
+ sal_Int32 nLen;
+ sal_uInt16 nPrec;
+ nLen = rDoc.GetMaxNumberStringLen( nPrec, nTab, nCol,
+ nFirstDataRow, nLastRow );
+ // dBaseIII precision limit: 15
+ if ( nPrecision > 15 )
+ nPrecision = 15;
+ if ( nPrec > 15 )
+ nPrec = 15;
+ if ( bPrecDefined && nPrecision != nPrec )
+ {
+ if (nPrecision < nPrec)
+ {
+ // This is a hairy case. User defined nPrecision but a
+ // number format has more precision. Modifying a dBase
+ // field may as well render the resulting file useless for
+ // an application that relies on its defined structure,
+ // especially if we are resaving an already existing file.
+ // So who's right, the user who (or the loaded file that)
+ // defined the field, or the user who applied the format?
+ // Commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca gave the
+ // format a higher priority, which is debatable.
+ SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field precision for "
+ << aFieldName << " (" << nPrecision << "<" << nPrec << ")");
+
+ // Adjust length to larger predefined integer part. There
+ // may be a reason that the field was prepared for larger
+ // numbers.
+ if (nFieldLen - nPrecision > nLen - nPrec)
+ nLen = nFieldLen - (nPrecision ? nPrecision+1 : 0) + 1 + nPrec;
+ // And override precision.
+ nPrecision = nPrec;
+ }
+ else
+ {
+#if 1
+ // Adjust length to predefined precision.
+ nLen = nLen + ( nPrecision - nPrec );
+#else
+ /* If the above override for (nPrecision < nPrec) was not in place then
+ * nPrecision could be 0 and this would be the code path to correctly
+ * calculate nLen. But as is, nPrecision is never 0 here, see CID#982304 */
+
+ // Adjust length to predefined precision.
+ if ( nPrecision )
+ nLen = nLen + ( nPrecision - nPrec );
+ else
+ nLen -= nPrec+1; // also remove the decimal separator
+#endif
+ }
+ }
+ if (nFieldLen < nLen)
+ {
+ if (!bTypeDefined)
+ nFieldLen = nLen;
+ else
+ {
+ // Again a hairy case and conflict. Furthermore, the
+ // larger overall length may be a result of only a higher
+ // precision obtained from formats.
+ SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field length for "
+ << aFieldName << " (" << nFieldLen << "<" << nLen << ")");
+ nFieldLen = nLen;
+ }
+ }
+ if ( !bPrecDefined )
+ nPrecision = nPrec;
+ if ( nFieldLen == 0 )
+ nFieldLen = 1;
+ else if ( nFieldLen > 19 )
+ nFieldLen = 19; // dBaseIII numeric field length limit: 19
+ if ( nPrecision && nFieldLen < nPrecision + 2 )
+ nFieldLen = nPrecision + 2; // 0. must fit into
+ // 538 MUST: Sdb internal representation adds 2 to the field length!
+ // To give the user what he wants we must subtract it here.
+ //! CAVEAT! There is no way to define a numeric field with a length
+ //! of 1 and no decimals!
+ nFieldLen = SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen, nPrecision );
+ }
+ if ( nFieldLen > 254 )
+ {
+ if ( nDbType == sdbc::DataType::VARCHAR )
+ { // Too long for a normal text field => memo field.
+ nDbType = sdbc::DataType::LONGVARCHAR;
+ nFieldLen = 10;
+ bHasMemo = true;
+ }
+ else
+ nFieldLen = 254; // bad luck...
+ }
+
+ pColNames[nField] = aFieldName;
+ pColTypes[nField] = nDbType;
+ pColLengths[nField] = nFieldLen;
+ pColScales[nField] = nPrecision;
+
+ ++nField;
+ }
+}
+
+void lcl_getLongVarCharEditString( OUString& rString,
+ const ScRefCellValue& rCell, ScFieldEditEngine& rEditEngine )
+{
+ if (!rCell.getEditText())
+ return;
+
+ rEditEngine.SetTextCurrentDefaults(*rCell.getEditText());
+ rString = rEditEngine.GetText( LINEEND_CRLF );
+}
+
+void lcl_getLongVarCharString(
+ OUString& rString, ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, SvNumberFormatter& rNumFmt )
+{
+ const Color* pColor;
+ ScAddress aPos(nCol, nRow, nTab);
+ sal_uInt32 nFormat = rDoc.GetNumberFormat(aPos);
+ rString = ScCellFormat::GetString(rDoc, aPos, nFormat, &pColor, rNumFmt);
+}
+
+}
+
+#endif // HAVE_FEATURE_DBCONNECTIVITY
+
+ErrCodeMsg ScDocShell::DBaseExport( const OUString& rFullFileName, rtl_TextEncoding eCharSet, bool& bHasMemo )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY
+ (void) rFullFileName;
+ (void) eCharSet;
+ (void) bHasMemo;
+
+ return ERRCODE_IO_GENERAL;
+#else
+ // remove the file so the dBase driver doesn't find an invalid file
+ INetURLObject aDeleteObj( rFullFileName, INetProtocol::File );
+ KillFile( aDeleteObj );
+
+ ErrCodeMsg nErr = ERRCODE_NONE;
+
+ SCCOL nFirstCol, nLastCol;
+ SCROW nFirstRow, nLastRow;
+ SCTAB nTab = GetSaveTab();
+ m_pDocument->GetDataStart( nTab, nFirstCol, nFirstRow );
+ m_pDocument->GetCellArea( nTab, nLastCol, nLastRow );
+ if ( nFirstCol > nLastCol )
+ nFirstCol = nLastCol;
+ if ( nFirstRow > nLastRow )
+ nFirstRow = nLastRow;
+ ScProgress aProgress( this, ScResId( STR_SAVE_DOC ),
+ nLastRow - nFirstRow, true );
+ SvNumberFormatter* pNumFmt = m_pDocument->GetFormatTable();
+
+ bool bHasFieldNames = true;
+ for ( SCCOL nDocCol = nFirstCol; nDocCol <= nLastCol && bHasFieldNames; nDocCol++ )
+ { // only Strings in first row => are field names
+ if ( !m_pDocument->HasStringData( nDocCol, nFirstRow, nTab ) )
+ bHasFieldNames = false;
+ }
+
+ sal_Int32 nColCount = nLastCol - nFirstCol + 1;
+ uno::Sequence<OUString> aColNames( nColCount );
+ uno::Sequence<sal_Int32> aColTypes( nColCount );
+ uno::Sequence<sal_Int32> aColLengths( nColCount );
+ uno::Sequence<sal_Int32> aColScales( nColCount );
+
+ ScRange aDataRange( nFirstCol, nFirstRow, nTab, nLastCol, nLastRow, nTab );
+ lcl_GetColumnTypes( *this, aDataRange, bHasFieldNames,
+ aColNames.getArray(), aColTypes.getArray(),
+ aColLengths.getArray(), aColScales.getArray(),
+ bHasMemo, eCharSet );
+ // also needed for exception catch
+ SCROW nDocRow = 0;
+ ScFieldEditEngine aEditEngine(m_pDocument.get(), m_pDocument->GetEditPool());
+ OUString aString;
+
+ try
+ {
+ uno::Reference<sdbc::XDriverManager2> xDrvMan;
+ uno::Reference<sdbc::XConnection> xConnection;
+ OUString aTabName;
+ ErrCode nRet = lcl_getDBaseConnection(xDrvMan,xConnection,aTabName,rFullFileName,eCharSet);
+ if ( !xConnection.is() || !xDrvMan.is() )
+ return nRet;
+ ::utl::DisposableComponent aConnectionHelper(xConnection);
+
+ // get dBase driver
+ uno::Reference< sdbcx::XDataDefinitionSupplier > xDDSup( xDrvMan->getDriverByURL( xConnection->getMetaData()->getURL() ), uno::UNO_QUERY );
+ if ( !xDDSup.is() )
+ return SCERR_EXPORT_CONNECT;
+
+ // create table
+ uno::Reference<sdbcx::XTablesSupplier> xTablesSupp =xDDSup->getDataDefinitionByConnection( xConnection );
+ OSL_ENSURE( xTablesSupp.is(), "can't get Data Definition" );
+ if (!xTablesSupp.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<container::XNameAccess> xTables = xTablesSupp->getTables();
+ OSL_ENSURE( xTables.is(), "can't get Tables" );
+ if (!xTables.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<sdbcx::XDataDescriptorFactory> xTablesFact( xTables, uno::UNO_QUERY );
+ OSL_ENSURE( xTablesFact.is(), "can't get tables factory" );
+ if (!xTablesFact.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<sdbcx::XAppend> xTablesAppend( xTables, uno::UNO_QUERY );
+ OSL_ENSURE( xTablesAppend.is(), "can't get tables XAppend" );
+ if (!xTablesAppend.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<beans::XPropertySet> xTableDesc = xTablesFact->createDataDescriptor();
+ OSL_ENSURE( xTableDesc.is(), "can't get table descriptor" );
+ if (!xTableDesc.is()) return SCERR_EXPORT_CONNECT;
+
+ xTableDesc->setPropertyValue( SC_DBPROP_NAME, uno::Any(aTabName) );
+
+ // create columns
+
+ uno::Reference<sdbcx::XColumnsSupplier> xColumnsSupp( xTableDesc, uno::UNO_QUERY );
+ OSL_ENSURE( xColumnsSupp.is(), "can't get columns supplier" );
+ if (!xColumnsSupp.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<container::XNameAccess> xColumns = xColumnsSupp->getColumns();
+ OSL_ENSURE( xColumns.is(), "can't get columns" );
+ if (!xColumns.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<sdbcx::XDataDescriptorFactory> xColumnsFact( xColumns, uno::UNO_QUERY );
+ OSL_ENSURE( xColumnsFact.is(), "can't get columns factory" );
+ if (!xColumnsFact.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<sdbcx::XAppend> xColumnsAppend( xColumns, uno::UNO_QUERY );
+ OSL_ENSURE( xColumnsAppend.is(), "can't get columns XAppend" );
+ if (!xColumnsAppend.is()) return SCERR_EXPORT_CONNECT;
+
+ const OUString* pColNames = aColNames.getConstArray();
+ const sal_Int32* pColTypes = aColTypes.getConstArray();
+ const sal_Int32* pColLengths = aColLengths.getConstArray();
+ const sal_Int32* pColScales = aColScales.getConstArray();
+ sal_Int32 nCol;
+
+ for (nCol=0; nCol<nColCount; nCol++)
+ {
+ uno::Reference<beans::XPropertySet> xColumnDesc = xColumnsFact->createDataDescriptor();
+ OSL_ENSURE( xColumnDesc.is(), "can't get column descriptor" );
+ if (!xColumnDesc.is()) return SCERR_EXPORT_CONNECT;
+
+ xColumnDesc->setPropertyValue( SC_DBPROP_NAME, uno::Any(pColNames[nCol]) );
+
+ xColumnDesc->setPropertyValue( SC_DBPROP_TYPE, uno::Any(pColTypes[nCol]) );
+
+ xColumnDesc->setPropertyValue( SC_DBPROP_PRECISION, uno::Any(pColLengths[nCol]) );
+
+ xColumnDesc->setPropertyValue( SC_DBPROP_SCALE, uno::Any(pColScales[nCol]) );
+
+ xColumnsAppend->appendByDescriptor( xColumnDesc );
+ }
+
+ xTablesAppend->appendByDescriptor( xTableDesc );
+
+ // get row set for writing
+ uno::Reference<lang::XMultiServiceFactory> xFactory = comphelper::getProcessServiceFactory();
+ uno::Reference<sdbc::XRowSet> xRowSet( xFactory->createInstance(SC_SERVICE_ROWSET),
+ uno::UNO_QUERY);
+ ::utl::DisposableComponent aRowSetHelper(xRowSet);
+ uno::Reference<beans::XPropertySet> xRowProp( xRowSet, uno::UNO_QUERY );
+ OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
+ if (!xRowProp.is()) return SCERR_EXPORT_CONNECT;
+
+ xRowProp->setPropertyValue( SC_DBPROP_ACTIVECONNECTION, uno::Any(xConnection) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, uno::Any(sal_Int32(sdb::CommandType::TABLE)) );
+
+ xRowProp->setPropertyValue( SC_DBPROP_COMMAND, uno::Any(aTabName) );
+
+ xRowSet->execute();
+
+ // write data rows
+
+ uno::Reference<sdbc::XResultSetUpdate> xResultUpdate( xRowSet, uno::UNO_QUERY );
+ OSL_ENSURE( xResultUpdate.is(), "can't get XResultSetUpdate" );
+ if (!xResultUpdate.is()) return SCERR_EXPORT_CONNECT;
+
+ uno::Reference<sdbc::XRowUpdate> xRowUpdate( xRowSet, uno::UNO_QUERY );
+ OSL_ENSURE( xRowUpdate.is(), "can't get XRowUpdate" );
+ if (!xRowUpdate.is()) return SCERR_EXPORT_CONNECT;
+
+ SCROW nFirstDataRow = ( bHasFieldNames ? nFirstRow + 1 : nFirstRow );
+ double fVal;
+
+ for ( nDocRow = nFirstDataRow; nDocRow <= nLastRow; nDocRow++ )
+ {
+ xResultUpdate->moveToInsertRow();
+
+ for (nCol=0; nCol<nColCount; nCol++)
+ {
+ SCCOL nDocCol = sal::static_int_cast<SCCOL>( nFirstCol + nCol );
+
+ switch (pColTypes[nCol])
+ {
+ case sdbc::DataType::LONGVARCHAR:
+ {
+ ScRefCellValue aCell(*m_pDocument, ScAddress(nDocCol, nDocRow, nTab));
+ if (!aCell.isEmpty())
+ {
+ if (aCell.getType() == CELLTYPE_EDIT)
+ { // preserve paragraphs
+ lcl_getLongVarCharEditString(aString, aCell, aEditEngine);
+ }
+ else
+ {
+ lcl_getLongVarCharString(
+ aString, *m_pDocument, nDocCol, nDocRow, nTab, *pNumFmt);
+ }
+ xRowUpdate->updateString( nCol+1, aString );
+ }
+ else
+ xRowUpdate->updateNull( nCol+1 );
+ }
+ break;
+
+ case sdbc::DataType::VARCHAR:
+ aString = m_pDocument->GetString(nDocCol, nDocRow, nTab);
+ xRowUpdate->updateString( nCol+1, aString );
+ if ( nErr == ERRCODE_NONE && pColLengths[nCol] < aString.getLength() )
+ nErr = SCWARN_EXPORT_DATALOST;
+ break;
+
+ case sdbc::DataType::DATE:
+ {
+ fVal = m_pDocument->GetValue( nDocCol, nDocRow, nTab );
+ // differentiate between 0 with value and 0 no-value
+ bool bIsNull = (fVal == 0.0);
+ if ( bIsNull )
+ bIsNull = !m_pDocument->HasValueData( nDocCol, nDocRow, nTab );
+ if ( bIsNull )
+ {
+ xRowUpdate->updateNull( nCol+1 );
+ if ( nErr == ERRCODE_NONE &&
+ m_pDocument->HasStringData( nDocCol, nDocRow, nTab ) )
+ nErr = SCWARN_EXPORT_DATALOST;
+ }
+ else
+ {
+ Date aDate = pNumFmt->GetNullDate(); // tools date
+ aDate.AddDays(fVal); //! approxfloor?
+ xRowUpdate->updateDate( nCol+1, aDate.GetUNODate() );
+ }
+ }
+ break;
+
+ case sdbc::DataType::DECIMAL:
+ case sdbc::DataType::BIT:
+ fVal = m_pDocument->GetValue( nDocCol, nDocRow, nTab );
+ if ( fVal == 0.0 && nErr == ERRCODE_NONE &&
+ m_pDocument->HasStringData( nDocCol, nDocRow, nTab ) )
+ nErr = SCWARN_EXPORT_DATALOST;
+ if ( pColTypes[nCol] == sdbc::DataType::BIT )
+ xRowUpdate->updateBoolean( nCol+1, ( fVal != 0.0 ) );
+ else
+ xRowUpdate->updateDouble( nCol+1, fVal );
+ break;
+
+ default:
+ OSL_FAIL( "ScDocShell::DBaseExport: unknown FieldType" );
+ if ( nErr == ERRCODE_NONE )
+ nErr = SCWARN_EXPORT_DATALOST;
+ fVal = m_pDocument->GetValue( nDocCol, nDocRow, nTab );
+ xRowUpdate->updateDouble( nCol+1, fVal );
+ }
+ }
+
+ xResultUpdate->insertRow();
+
+ //! error handling and recovery of old
+ //! ScDocShell::SbaSdbExport is still missing!
+
+ aProgress.SetStateOnPercent( nDocRow - nFirstRow );
+ }
+
+ comphelper::disposeComponent( xRowSet );
+ comphelper::disposeComponent( xConnection );
+ }
+ catch ( const sdbc::SQLException& aException )
+ {
+ sal_Int32 nError = aException.ErrorCode;
+ TOOLS_WARN_EXCEPTION("sc", "ScDocShell::DBaseExport");
+
+ if (nError == 22018 || nError == 22001)
+ {
+ // SQL error 22018: Character not in target encoding.
+ // SQL error 22001: String length exceeds field width (after encoding).
+ bool bEncErr = (nError == 22018);
+ bool bIsOctetTextEncoding = rtl_isOctetTextEncoding( eCharSet);
+ OSL_ENSURE( !bEncErr || bIsOctetTextEncoding, "ScDocShell::DBaseExport: encoding error and not an octet textencoding");
+ SCCOL nDocCol = nFirstCol;
+ const sal_Int32* pColTypes = aColTypes.getConstArray();
+ const sal_Int32* pColLengths = aColLengths.getConstArray();
+ ScHorizontalCellIterator aIter( *m_pDocument, nTab, nFirstCol,
+ nDocRow, nLastCol, nDocRow);
+ bool bTest = true;
+ while (bTest)
+ {
+ ScRefCellValue* pCell = aIter.GetNext( nDocCol, nDocRow);
+ if (!pCell)
+ break;
+ SCCOL nCol = nDocCol - nFirstCol;
+ switch (pColTypes[nCol])
+ {
+ case sdbc::DataType::LONGVARCHAR:
+ {
+ if (pCell->getType() == CELLTYPE_EDIT)
+ lcl_getLongVarCharEditString(aString, *pCell, aEditEngine);
+ else
+ lcl_getLongVarCharString(
+ aString, *m_pDocument, nDocCol, nDocRow, nTab, *pNumFmt);
+ }
+ break;
+
+ case sdbc::DataType::VARCHAR:
+ aString = m_pDocument->GetString(nDocCol, nDocRow, nTab);
+ break;
+
+ // NOTE: length of DECIMAL fields doesn't need to be
+ // checked here, the database driver adjusts the field
+ // width accordingly.
+
+ default:
+ bTest = false;
+ }
+ if (bTest)
+ {
+ sal_Int32 nLen;
+ if (bIsOctetTextEncoding)
+ {
+ OString aOString;
+ if (!aString.convertToString( &aOString, eCharSet,
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
+ {
+ bTest = false;
+ bEncErr = true;
+ }
+ nLen = aOString.getLength();
+ if (!bTest)
+ SAL_WARN("sc", "ScDocShell::DBaseExport encoding error, string with default replacements: ``" << aString << "''");
+ }
+ else
+ nLen = aString.getLength() * sizeof(sal_Unicode);
+ if (!bEncErr &&
+ pColTypes[nCol] != sdbc::DataType::LONGVARCHAR &&
+ pColLengths[nCol] < nLen)
+ {
+ bTest = false;
+ SAL_INFO("sc", "ScDocShell::DBaseExport: field width: " << pColLengths[nCol] << ", encoded length: " << nLen);
+ }
+ }
+ else
+ bTest = true;
+ }
+ OUString sPosition(ScAddress(nDocCol, nDocRow, nTab).GetColRowString());
+ OUString sEncoding(SvxTextEncodingTable::GetTextString(eCharSet));
+ nErr = ErrCodeMsg( (bEncErr ? SCERR_EXPORT_ENCODING :
+ SCERR_EXPORT_FIELDWIDTH), sPosition, sEncoding,
+ DialogMask::ButtonsOk | DialogMask::MessageError);
+ }
+ else if ( !aException.Message.isEmpty() )
+ nErr = ErrCodeMsg( SCERR_EXPORT_SQLEXCEPTION, aException.Message, DialogMask::ButtonsOk | DialogMask::MessageError);
+ else
+ nErr = SCERR_EXPORT_DATA;
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
+ nErr = ERRCODE_IO_GENERAL;
+ }
+
+ return nErr;
+#endif // HAVE_FEATURE_DBCONNECTIVITY
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/docshimp.hxx b/sc/source/ui/docshell/docshimp.hxx
new file mode 100644
index 0000000000..58cfb23ac7
--- /dev/null
+++ b/sc/source/ui/docshell/docshimp.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <svtools/ctrltool.hxx>
+#include <sfx2/docinsert.hxx>
+#include <sfx2/request.hxx>
+
+struct DocShell_Impl
+{
+ bool bIgnoreLostRedliningWarning;
+ std::unique_ptr<FontList> pFontList;
+ std::unique_ptr<sfx2::DocumentInserter> pDocInserter;
+ std::unique_ptr<SfxRequest> pRequest;
+
+ DocShell_Impl() :
+ bIgnoreLostRedliningWarning( false )
+ {}
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/documentlinkmgr.cxx b/sc/source/ui/docshell/documentlinkmgr.cxx
new file mode 100644
index 0000000000..0fb89cfa0d
--- /dev/null
+++ b/sc/source/ui/docshell/documentlinkmgr.cxx
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/doublecheckedinit.hxx>
+#include <documentlinkmgr.hxx>
+#include <datastream.hxx>
+#include <ddelink.hxx>
+#include <externalrefmgr.hxx>
+#include <webservicelink.hxx>
+#include <strings.hrc>
+#include <scresid.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/linksrc.hxx>
+#include <o3tl/deleter.hxx>
+#include <svx/svdoole2.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <memory>
+
+namespace sc {
+
+struct DocumentLinkManagerImpl
+{
+ SfxObjectShell* mpShell;
+ std::unique_ptr<DataStream, o3tl::default_delete<DataStream>> mpDataStream;
+ std::atomic<sfx2::LinkManager*> mpLinkManager;
+
+ DocumentLinkManagerImpl(const DocumentLinkManagerImpl&) = delete;
+ const DocumentLinkManagerImpl& operator=(const DocumentLinkManagerImpl&) = delete;
+
+ explicit DocumentLinkManagerImpl(SfxObjectShell* pShell)
+ : mpShell(pShell), mpLinkManager(nullptr) {}
+
+ ~DocumentLinkManagerImpl()
+ {
+ // Shared base links
+ sfx2::LinkManager* linkManager = mpLinkManager;
+ if (linkManager)
+ {
+ sfx2::SvLinkSources aTemp = linkManager->GetServers();
+ for (const auto& pLinkSource : aTemp)
+ pLinkSource->Closed();
+
+ if (!linkManager->GetLinks().empty())
+ linkManager->Remove(0, linkManager->GetLinks().size());
+ }
+ delete linkManager;
+ }
+};
+
+DocumentLinkManager::DocumentLinkManager( SfxObjectShell* pShell ) :
+ mpImpl(new DocumentLinkManagerImpl(pShell)) {}
+
+DocumentLinkManager::~DocumentLinkManager()
+{
+}
+
+void DocumentLinkManager::setDataStream( DataStream* p )
+{
+ mpImpl->mpDataStream.reset(p);
+}
+
+DataStream* DocumentLinkManager::getDataStream()
+{
+ return mpImpl->mpDataStream.get();
+}
+
+const DataStream* DocumentLinkManager::getDataStream() const
+{
+ return mpImpl->mpDataStream.get();
+}
+
+sfx2::LinkManager* DocumentLinkManager::getLinkManager( bool bCreate )
+{
+ if (bCreate && mpImpl->mpShell)
+ return comphelper::doubleCheckedInit( mpImpl->mpLinkManager,
+ [this]() { return new sfx2::LinkManager(mpImpl->mpShell); } );
+ return mpImpl->mpLinkManager;
+}
+
+const sfx2::LinkManager* DocumentLinkManager::getExistingLinkManager() const
+{
+ return mpImpl->mpLinkManager;
+}
+
+bool DocumentLinkManager::idleCheckLinks()
+{
+ sfx2::LinkManager* pMgr = mpImpl->mpLinkManager;
+ if (!pMgr)
+ return false;
+
+ bool bAnyLeft = false;
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ sfx2::SvBaseLink* pBase = rLink.get();
+ ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>(pBase);
+ if (!pDdeLink || !pDdeLink->NeedsUpdate())
+ continue;
+
+ pDdeLink->TryUpdate();
+ if (pDdeLink->NeedsUpdate()) // Was not successful?
+ bAnyLeft = true;
+ }
+
+ return bAnyLeft;
+}
+
+bool DocumentLinkManager::hasDdeLinks() const
+{
+ return hasDdeOrOleOrWebServiceLinks(true, false, false);
+}
+
+bool DocumentLinkManager::hasDdeOrOleOrWebServiceLinks() const
+{
+ return hasDdeOrOleOrWebServiceLinks(true, true, true);
+}
+
+bool DocumentLinkManager::hasDdeOrOleOrWebServiceLinks(bool bDde, bool bOle, bool bWebService) const
+{
+ sfx2::LinkManager* pMgr = mpImpl->mpLinkManager;
+ if (!pMgr)
+ return false;
+
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ sfx2::SvBaseLink* pBase = rLink.get();
+ if (bDde && dynamic_cast<ScDdeLink*>(pBase))
+ return true;
+ if (bOle && (dynamic_cast<SdrEmbedObjectLink*>(pBase) || dynamic_cast<SdrIFrameLink*>(pBase)))
+ return true;
+ if (bWebService && dynamic_cast<ScWebServiceLink*>(pBase))
+ return true;
+ }
+
+ return false;
+}
+
+bool DocumentLinkManager::hasExternalRefLinks() const
+{
+ sfx2::LinkManager* pMgr = mpImpl->mpLinkManager;
+ if (!pMgr)
+ return false;
+
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ sfx2::SvBaseLink* pBase = rLink.get();
+ if (dynamic_cast<ScExternalRefLink*>(pBase))
+ return true;
+ }
+
+ return false;
+}
+
+bool DocumentLinkManager::updateDdeOrOleOrWebServiceLinks(weld::Window* pWin)
+{
+ sfx2::LinkManager* pMgr = mpImpl->mpLinkManager;
+ if (!pMgr)
+ return false;
+
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+
+ // If the update takes longer, reset all values so that nothing
+ // old (wrong) is left behind
+ bool bAny = false;
+ for (const auto & rLink : rLinks)
+ {
+ sfx2::SvBaseLink* pBase = rLink.get();
+
+ SdrEmbedObjectLink* pOleLink = dynamic_cast<SdrEmbedObjectLink*>(pBase);
+ if (pOleLink)
+ {
+ pOleLink->Update();
+ continue;
+ }
+
+ SdrIFrameLink* pIFrameLink = dynamic_cast<SdrIFrameLink*>(pBase);
+ if (pIFrameLink)
+ {
+ pIFrameLink->Update();
+ continue;
+ }
+
+ ScWebServiceLink* pWebserviceLink = dynamic_cast<ScWebServiceLink*>(pBase);
+ if (pWebserviceLink)
+ {
+ pWebserviceLink->Update();
+ continue;
+ }
+
+ ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>(pBase);
+ if (!pDdeLink)
+ continue;
+
+ if (pDdeLink->Update())
+ bAny = true;
+ else
+ {
+ // Update failed. Notify the user.
+ const OUString& aFile = pDdeLink->GetTopic();
+ const OUString& aElem = pDdeLink->GetItem();
+ const OUString& aType = pDdeLink->GetAppl();
+
+ OUString sMessage =
+ ScResId(SCSTR_DDEDOC_NOT_LOADED) +
+ "\n\n"
+ "Source : " +
+ aFile +
+ "\nElement : " +
+ aElem +
+ "\nType : " +
+ aType;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ sMessage));
+ xBox->run();
+ }
+ }
+
+ pMgr->CloseCachedComps();
+
+ return bAny;
+}
+
+void DocumentLinkManager::updateDdeLink( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem )
+{
+ sfx2::LinkManager* pMgr = mpImpl->mpLinkManager;
+ if (!pMgr)
+ return;
+
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+
+ for (const auto & rLink : rLinks)
+ {
+ ::sfx2::SvBaseLink* pBase = rLink.get();
+ ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>(pBase);
+ if (!pDdeLink)
+ continue;
+
+ if ( pDdeLink->GetAppl() == rAppl &&
+ pDdeLink->GetTopic() == rTopic &&
+ pDdeLink->GetItem() == rItem )
+ {
+ pDdeLink->TryUpdate();
+ // Could be multiple (Mode), so continue searching
+ }
+ }
+}
+
+size_t DocumentLinkManager::getDdeLinkCount() const
+{
+ sfx2::LinkManager* pMgr = mpImpl->mpLinkManager;
+ if (!pMgr)
+ return 0;
+
+ size_t nDdeCount = 0;
+ const sfx2::SvBaseLinks& rLinks = pMgr->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ ::sfx2::SvBaseLink* pBase = rLink.get();
+ ScDdeLink* pDdeLink = dynamic_cast<ScDdeLink*>(pBase);
+ if (!pDdeLink)
+ continue;
+
+ ++nDdeCount;
+ }
+
+ return nDdeCount;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/editable.cxx b/sc/source/ui/docshell/editable.cxx
new file mode 100644
index 0000000000..86bbb9f2e0
--- /dev/null
+++ b/sc/source/ui/docshell/editable.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 <editable.hxx>
+#include <document.hxx>
+#include <viewfunc.hxx>
+#include <globstr.hrc>
+
+ScEditableTester::ScEditableTester() :
+ mbIsEditable(true),
+ mbOnlyMatrix(true)
+{
+}
+
+ScEditableTester::ScEditableTester( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bNoMatrixAtAll ) :
+ mbIsEditable(true),
+ mbOnlyMatrix(true)
+{
+ TestBlock( rDoc, nTab, nStartCol, nStartRow, nEndCol, nEndRow, bNoMatrixAtAll );
+}
+
+ScEditableTester::ScEditableTester( const ScDocument& rDoc,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark ) :
+ mbIsEditable(true),
+ mbOnlyMatrix(true)
+{
+ TestSelectedBlock( rDoc, nStartCol, nStartRow, nEndCol, nEndRow, rMark );
+}
+
+ScEditableTester::ScEditableTester( const ScDocument& rDoc, const ScRange& rRange ) :
+ mbIsEditable(true),
+ mbOnlyMatrix(true)
+{
+ TestRange( rDoc, rRange );
+}
+
+ScEditableTester::ScEditableTester( const ScDocument& rDoc, const ScMarkData& rMark ) :
+ mbIsEditable(true),
+ mbOnlyMatrix(true)
+{
+ TestSelection( rDoc, rMark );
+}
+
+ScEditableTester::ScEditableTester( ScViewFunc* pView ) :
+ mbIsEditable(true),
+ mbOnlyMatrix(true)
+{
+ bool bThisMatrix;
+ if ( !pView->SelectionEditable( &bThisMatrix ) )
+ {
+ mbIsEditable = false;
+ if ( !bThisMatrix )
+ mbOnlyMatrix = false;
+ }
+}
+
+ScEditableTester::ScEditableTester(
+ const ScDocument& rDoc, sc::ColRowEditAction eAction, SCCOLROW nStart, SCCOLROW nEnd, const ScMarkData& rMark ) :
+ ScEditableTester()
+{
+ TestBlockForAction(rDoc, eAction, nStart, nEnd, rMark);
+}
+
+void ScEditableTester::TestBlock( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bNoMatrixAtAll )
+{
+ if (mbIsEditable || mbOnlyMatrix)
+ {
+ bool bThisMatrix;
+ if (!rDoc.IsBlockEditable( nTab, nStartCol, nStartRow, nEndCol, nEndRow, &bThisMatrix, bNoMatrixAtAll))
+ {
+ mbIsEditable = false;
+ if ( !bThisMatrix )
+ mbOnlyMatrix = false;
+ }
+ }
+}
+
+void ScEditableTester::TestSelectedBlock( const ScDocument& rDoc,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark )
+{
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ TestBlock( rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow, false );
+ }
+}
+
+void ScEditableTester::TestRange( const ScDocument& rDoc, const ScRange& rRange )
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
+ TestBlock( rDoc, nTab, nStartCol, nStartRow, nEndCol, nEndRow, false );
+}
+
+void ScEditableTester::TestSelection( const ScDocument& rDoc, const ScMarkData& rMark )
+{
+ if (mbIsEditable || mbOnlyMatrix)
+ {
+ bool bThisMatrix;
+ if ( !rDoc.IsSelectionEditable( rMark, &bThisMatrix ) )
+ {
+ mbIsEditable = false;
+ if ( !bThisMatrix )
+ mbOnlyMatrix = false;
+ }
+ }
+}
+
+void ScEditableTester::TestBlockForAction(
+ const ScDocument& rDoc, sc::ColRowEditAction eAction, SCCOLROW nStart, SCCOLROW nEnd,
+ const ScMarkData& rMark )
+{
+ mbOnlyMatrix = false;
+
+ for (const auto& rTab : rMark)
+ {
+ if (!mbIsEditable)
+ return;
+
+ mbIsEditable = rDoc.IsEditActionAllowed(eAction, rTab, nStart, nEnd);
+ }
+}
+
+TranslateId ScEditableTester::GetMessageId() const
+{
+ if (mbIsEditable)
+ return {};
+ else if (mbOnlyMatrix)
+ return STR_MATRIXFRAGMENTERR;
+ else
+ return STR_PROTECTIONERR;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/externalrefmgr.cxx b/sc/source/ui/docshell/externalrefmgr.cxx
new file mode 100644
index 0000000000..24fb7a808e
--- /dev/null
+++ b/sc/source/ui/docshell/externalrefmgr.cxx
@@ -0,0 +1,3323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <externalrefmgr.hxx>
+#include <document.hxx>
+#include <token.hxx>
+#include <tokenarray.hxx>
+#include <address.hxx>
+#include <tablink.hxx>
+#include <docsh.hxx>
+#include <scextopt.hxx>
+#include <rangenam.hxx>
+#include <formulacell.hxx>
+#include <utility>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <cellvalue.hxx>
+#include <defaultsoptions.hxx>
+#include <scmod.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <stringutil.hxx>
+#include <scmatrix.hxx>
+#include <columnspanset.hxx>
+#include <column.hxx>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <algorithm>
+
+using ::std::unique_ptr;
+using ::com::sun::star::uno::Any;
+using ::std::vector;
+using ::std::find_if;
+using ::std::for_each;
+using ::std::distance;
+using ::std::pair;
+using namespace formula;
+
+#define SRCDOC_LIFE_SPAN 30000 // 5 minutes (in 100th of a sec)
+#define SRCDOC_SCAN_INTERVAL 1000*30 // every 30 seconds (in msec)
+
+namespace {
+
+class TabNameSearchPredicate
+{
+public:
+ explicit TabNameSearchPredicate(const OUString& rSearchName) :
+ maSearchName(ScGlobal::getCharClass().uppercase(rSearchName))
+ {
+ }
+
+ bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
+ {
+ // Ok, I'm doing case insensitive search here.
+ return rTabNameSet.maUpperName == maSearchName;
+ }
+
+private:
+ OUString maSearchName;
+};
+
+class FindSrcFileByName
+{
+public:
+ explicit FindSrcFileByName(const OUString& rMatchName) :
+ mrMatchName(rMatchName)
+ {
+ }
+
+ bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
+ {
+ return rSrcData.maFileName == mrMatchName;
+ }
+
+private:
+ const OUString& mrMatchName;
+};
+
+class NotifyLinkListener
+{
+public:
+ NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
+ mnFileId(nFileId), meType(eType) {}
+
+ void operator() (ScExternalRefManager::LinkListener* p) const
+ {
+ p->notify(mnFileId, meType);
+ }
+private:
+ sal_uInt16 mnFileId;
+ ScExternalRefManager::LinkUpdateType meType;
+};
+
+struct UpdateFormulaCell
+{
+ void operator() (ScFormulaCell* pCell) const
+ {
+ // Check to make sure the cell really contains svExternal*.
+ // External names, external cell and range references all have a
+ // token of svExternal*. Additionally check for INDIRECT() that can be
+ // called with any constructed URI string.
+ ScTokenArray* pCode = pCell->GetCode();
+ if (!pCode->HasExternalRef() && !pCode->HasOpCode(ocIndirect))
+ return;
+
+ if (pCode->GetCodeError() != FormulaError::NONE)
+ {
+ // Clear the error code, or a cell with error won't get re-compiled.
+ pCode->SetCodeError(FormulaError::NONE);
+ pCell->SetCompile(true);
+ pCell->CompileTokenArray();
+ }
+
+ pCell->SetDirty();
+ }
+};
+
+class RemoveFormulaCell
+{
+public:
+ explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {}
+ void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const
+ {
+ r.second.erase(mpCell);
+ }
+private:
+ ScFormulaCell* mpCell;
+};
+
+class ConvertFormulaToStatic
+{
+public:
+ explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {}
+ void operator() (ScFormulaCell* pCell) const
+ {
+ ScAddress aPos = pCell->aPos;
+
+ // We don't check for empty cells because empty external cells are
+ // treated as having a value of 0.
+
+ if (pCell->IsValue())
+ {
+ // Turn this into value cell.
+ mpDoc->SetValue(aPos, pCell->GetValue());
+ }
+ else
+ {
+ // string cell otherwise.
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ mpDoc->SetString(aPos, pCell->GetString().getString(), &aParam);
+ }
+ }
+private:
+ ScDocument* mpDoc;
+};
+
+/**
+ * Check whether a named range contains an external reference to a
+ * particular document.
+ */
+bool hasRefsToSrcDoc(ScRangeData& rData, sal_uInt16 nFileId)
+{
+ ScTokenArray* pArray = rData.GetCode();
+ if (!pArray)
+ return false;
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pArray);
+ formula::FormulaToken* p = aIter.GetNextReference();
+ for (; p; p = aIter.GetNextReference())
+ {
+ if (!p->IsExternalRef())
+ continue;
+
+ if (p->GetIndex() == nFileId)
+ return true;
+ }
+ return false;
+}
+
+class EraseRangeByIterator
+{
+ ScRangeName& mrRanges;
+public:
+ explicit EraseRangeByIterator(ScRangeName& rRanges) : mrRanges(rRanges) {}
+ void operator() (const ScRangeName::const_iterator& itr)
+ {
+ mrRanges.erase(itr);
+ }
+};
+
+/**
+ * Remove all named ranges that contain references to specified source
+ * document.
+ */
+void removeRangeNamesBySrcDoc(ScRangeName& rRanges, sal_uInt16 nFileId)
+{
+ ScRangeName::const_iterator itr = rRanges.begin(), itrEnd = rRanges.end();
+ vector<ScRangeName::const_iterator> v;
+ for (; itr != itrEnd; ++itr)
+ {
+ if (hasRefsToSrcDoc(*itr->second, nFileId))
+ v.push_back(itr);
+ }
+ for_each(v.begin(), v.end(), EraseRangeByIterator(rRanges));
+}
+
+}
+
+ScExternalRefCache::Table::Table()
+ : mbReferenced( true )
+ // Prevent accidental data loss due to lack of knowledge.
+{
+}
+
+ScExternalRefCache::Table::~Table()
+{
+}
+
+void ScExternalRefCache::Table::clear()
+{
+ maRows.clear();
+ maCachedRanges.RemoveAll();
+ mbReferenced = true;
+}
+
+void ScExternalRefCache::Table::setReferenced( bool bReferenced )
+{
+ mbReferenced = bReferenced;
+}
+
+bool ScExternalRefCache::Table::isReferenced() const
+{
+ return mbReferenced;
+}
+
+void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef const & pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
+{
+ using ::std::pair;
+ RowsDataType::iterator itrRow = maRows.find(nRow);
+ if (itrRow == maRows.end())
+ {
+ // This row does not exist yet.
+ pair<RowsDataType::iterator, bool> res = maRows.emplace(
+ nRow, RowDataType());
+
+ if (!res.second)
+ return;
+
+ itrRow = res.first;
+ }
+
+ // Insert this token into the specified column location. I don't need to
+ // check for existing data. Just overwrite it.
+ RowDataType& rRow = itrRow->second;
+ ScExternalRefCache::Cell aCell;
+ aCell.mxToken = pToken;
+ aCell.mnFmtIndex = nFmtIndex;
+ rRow.emplace(nCol, aCell);
+ if (bSetCacheRange)
+ setCachedCell(nCol, nRow);
+}
+
+ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const
+{
+ RowsDataType::const_iterator itrTable = maRows.find(nRow);
+ if (itrTable == maRows.end())
+ {
+ // this table doesn't have the specified row.
+ return getEmptyOrNullToken(nCol, nRow);
+ }
+
+ const RowDataType& rRowData = itrTable->second;
+ RowDataType::const_iterator itrRow = rRowData.find(nCol);
+ if (itrRow == rRowData.end())
+ {
+ // this row doesn't have the specified column.
+ return getEmptyOrNullToken(nCol, nRow);
+ }
+
+ const Cell& rCell = itrRow->second;
+ if (pnFmtIndex)
+ *pnFmtIndex = rCell.mnFmtIndex;
+
+ return rCell.mxToken;
+}
+
+bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const
+{
+ RowsDataType::const_iterator itrRow = maRows.find(nRow);
+ return itrRow != maRows.end();
+}
+
+template< typename P >
+void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, P predicate) const
+{
+ vector<SCROW> aRows;
+ aRows.reserve(maRows.size());
+ for (const auto& rEntry : maRows)
+ if (predicate(rEntry))
+ aRows.push_back(rEntry.first);
+
+ // hash map is not ordered, so we need to explicitly sort it.
+ ::std::sort(aRows.begin(), aRows.end());
+ rRows.swap(aRows);
+}
+
+void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
+{
+ getAllRows(rRows,
+ [nLow, nHigh](std::pair<SCROW, RowDataType> rEntry) { return (nLow <= rEntry.first && rEntry.first <= nHigh); });
+}
+
+void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows) const
+{
+ getAllRows(rRows, [](std::pair<SCROW, RowDataType>) { return true; } );
+}
+
+::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
+{
+ ::std::pair< SCROW, SCROW > aRange( 0, 0 );
+ if( !maRows.empty() )
+ {
+ // iterate over entire container (hash map is not sorted by key)
+ auto itMinMax = std::minmax_element(maRows.begin(), maRows.end(),
+ [](const RowsDataType::value_type& a, const RowsDataType::value_type& b) { return a.first < b.first; });
+ aRange.first = itMinMax.first->first;
+ aRange.second = itMinMax.second->first + 1;
+ }
+ return aRange;
+}
+
+template< typename P >
+void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, P predicate) const
+{
+ RowsDataType::const_iterator itrRow = maRows.find(nRow);
+ if (itrRow == maRows.end())
+ // this table doesn't have the specified row.
+ return;
+
+ const RowDataType& rRowData = itrRow->second;
+ vector<SCCOL> aCols;
+ aCols.reserve(rRowData.size());
+ for (const auto& rCol : rRowData)
+ if (predicate(rCol))
+ aCols.push_back(rCol.first);
+
+ // hash map is not ordered, so we need to explicitly sort it.
+ ::std::sort(aCols.begin(), aCols.end());
+ rCols.swap(aCols);
+}
+
+void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
+{
+ getAllCols(nRow, rCols,
+ [nLow, nHigh](std::pair<SCCOL, Cell> rCol) { return nLow <= rCol.first && rCol.first <= nHigh; } );
+}
+
+void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols) const
+{
+ getAllCols(nRow, rCols, [](std::pair<SCCOL, Cell>) { return true; } );
+}
+
+::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
+{
+ ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
+
+ RowsDataType::const_iterator itrRow = maRows.find( nRow );
+ if (itrRow == maRows.end())
+ // this table doesn't have the specified row.
+ return aRange;
+
+ const RowDataType& rRowData = itrRow->second;
+ if( !rRowData.empty() )
+ {
+ // iterate over entire container (hash map is not sorted by key)
+ auto itMinMax = std::minmax_element(rRowData.begin(), rRowData.end(),
+ [](const RowDataType::value_type& a, const RowDataType::value_type& b) { return a.first < b.first; });
+ aRange.first = itMinMax.first->first;
+ aRange.second = itMinMax.second->first + 1;
+ }
+ return aRange;
+}
+
+void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
+{
+ for (const auto& rRow : maRows)
+ {
+ const RowDataType& rRowData = rRow.second;
+ for (const auto& rCol : rRowData)
+ {
+ const Cell& rCell = rCol.second;
+ rNumFmts.push_back(rCell.mnFmtIndex);
+ }
+ }
+}
+
+bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
+{
+ return maCachedRanges.Contains(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
+}
+
+void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow)
+{
+ setCachedCellRange(nCol, nRow, nCol, nRow);
+}
+
+void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
+ maCachedRanges.Join(aRange);
+}
+
+void ScExternalRefCache::Table::setWholeTableCached()
+{
+ setCachedCellRange(0, 0, MAXCOL, MAXROW);
+}
+
+bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol, SCROW nRow) const
+{
+ return maCachedRanges.Contains(ScRange(nCol, nRow, 0, nCol, nRow, 0));
+}
+
+ScExternalRefCache::TokenRef ScExternalRefCache::Table::getEmptyOrNullToken(
+ SCCOL nCol, SCROW nRow) const
+{
+ if (isInCachedRanges(nCol, nRow))
+ {
+ TokenRef p(new ScEmptyCellToken(false, false));
+ return p;
+ }
+ return TokenRef();
+}
+
+ScExternalRefCache::TableName::TableName(OUString aUpper, OUString aReal) :
+ maUpperName(std::move(aUpper)), maRealName(std::move(aReal))
+{
+}
+
+ScExternalRefCache::CellFormat::CellFormat() :
+ mbIsSet(false), mnType(SvNumFormatType::ALL), mnIndex(0)
+{
+}
+
+ScExternalRefCache::ScExternalRefCache(const ScDocument& rDoc)
+ : mrDoc(rDoc)
+{
+}
+
+ScExternalRefCache::~ScExternalRefCache() {}
+
+const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ return nullptr;
+ }
+
+ const DocItem& rDoc = itrDoc->second;
+ TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
+ if (itrTabId == rDoc.maTableNameIndex.end())
+ {
+ // the specified table is not in cache.
+ return nullptr;
+ }
+
+ return &rDoc.maTableNames[itrTabId->second].maRealName;
+}
+
+const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ return nullptr;
+ }
+
+ const DocItem& rDoc = itrDoc->second;
+ NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
+ ScGlobal::getCharClass().uppercase(rRangeName));
+ if (itr == rDoc.maRealRangeNameMap.end())
+ // range name not found.
+ return nullptr;
+
+ return &itr->second;
+}
+
+ScExternalRefCache::TokenRef ScExternalRefCache::getCellData(
+ sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ return TokenRef();
+ }
+
+ const DocItem& rDoc = itrDoc->second;
+ TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
+ if (itrTabId == rDoc.maTableNameIndex.end())
+ {
+ // the specified table is not in cache.
+ return TokenRef();
+ }
+
+ const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
+ if (!pTableData)
+ {
+ // the table data is not instantiated yet.
+ return TokenRef();
+ }
+
+ return pTableData->getCell(nCol, nRow, pnFmtIndex);
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData(
+ sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocDataType::iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ // specified document is not cached.
+ return TokenArrayRef();
+
+ DocItem& rDoc = itrDoc->second;
+
+ TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
+ if (itrTabId == rDoc.maTableNameIndex.end())
+ // the specified table is not in cache.
+ return TokenArrayRef();
+
+ const ScAddress& s = rRange.aStart;
+ const ScAddress& e = rRange.aEnd;
+
+ const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
+ const SCCOL nCol1 = s.Col(), nCol2 = e.Col();
+ const SCROW nRow1 = s.Row(), nRow2 = e.Row();
+
+ // Make sure I have all the tables cached.
+ size_t nTabFirstId = itrTabId->second;
+ size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
+ if (nTabLastId >= rDoc.maTables.size())
+ // not all tables are cached.
+ return TokenArrayRef();
+
+ ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
+
+ RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
+ if (itrRange != rDoc.maRangeArrays.end())
+ // Cache hit!
+ return itrRange->second;
+
+ std::unique_ptr<ScRange> pNewRange;
+ TokenArrayRef pArray;
+ bool bFirstTab = true;
+ for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
+ {
+ TableTypeRef pTab = rDoc.maTables[nTab];
+ if (!pTab)
+ return TokenArrayRef();
+
+ SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
+ SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
+
+ if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
+ {
+ // specified range is not entirely within cached ranges.
+ return TokenArrayRef();
+ }
+
+ SCSIZE nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1);
+ SCSIZE nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
+ ScMatrixRef xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
+
+ // Needed in shrink and fill.
+ vector<SCROW> aRows;
+ pTab->getAllRows(aRows, nDataRow1, nDataRow2);
+ bool bFill = true;
+
+ // Check if size could be allocated and if not skip the fill, there's
+ // one error element instead. But retry first with the actual data area
+ // if that is smaller than the original range, which works for most
+ // functions just not some that operate/compare with the original size
+ // and expect empty values in non-data areas.
+ // Restrict this though to ranges of entire columns or rows, other
+ // ranges might be on purpose. (Other special cases to handle?)
+ /* TODO: sparse matrix could help */
+ SCSIZE nMatCols, nMatRows;
+ xMat->GetDimensions( nMatCols, nMatRows);
+ if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows)
+ {
+ bFill = false;
+ if (aRows.empty())
+ {
+ // There's no data at all. Set the one matrix element to empty
+ // for column-repeated and row-repeated access.
+ xMat->PutEmpty(0,0);
+ }
+ else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW))
+ {
+ nDataRow1 = aRows.front();
+ nDataRow2 = aRows.back();
+ SCCOL nMinCol = std::numeric_limits<SCCOL>::max();
+ SCCOL nMaxCol = std::numeric_limits<SCCOL>::min();
+ for (const auto& rRow : aRows)
+ {
+ vector<SCCOL> aCols;
+ pTab->getAllCols(rRow, aCols, nDataCol1, nDataCol2);
+ if (!aCols.empty())
+ {
+ nMinCol = std::min( nMinCol, aCols.front());
+ nMaxCol = std::max( nMaxCol, aCols.back());
+ }
+ }
+
+ if (nMinCol <= nMaxCol && ((o3tl::make_unsigned(nMaxCol-nMinCol+1) < nMatrixColumns) ||
+ (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows)))
+ {
+ nMatrixColumns = static_cast<SCSIZE>(nMaxCol-nMinCol+1);
+ nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
+ xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
+ xMat->GetDimensions( nMatCols, nMatRows);
+ if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
+ {
+ nDataCol1 = nMinCol;
+ nDataCol2 = nMaxCol;
+ bFill = true;
+ }
+ }
+ }
+ }
+
+ if (bFill)
+ {
+ // Only fill non-empty cells, for better performance.
+ for (SCROW nRow : aRows)
+ {
+ vector<SCCOL> aCols;
+ pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
+ for (SCCOL nCol : aCols)
+ {
+ TokenRef pToken = pTab->getCell(nCol, nRow);
+ if (!pToken)
+ // This should never happen!
+ return TokenArrayRef();
+
+ SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
+ switch (pToken->GetType())
+ {
+ case svDouble:
+ xMat->PutDouble(pToken->GetDouble(), nC, nR);
+ break;
+ case svString:
+ xMat->PutString(pToken->GetString(), nC, nR);
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ if (!bFirstTab)
+ pArray->AddOpCode(ocSep);
+
+ ScMatrixToken aToken(std::move(xMat));
+ if (!pArray)
+ pArray = std::make_shared<ScTokenArray>(mrDoc);
+ pArray->AddToken(aToken);
+
+ bFirstTab = false;
+
+ if (!pNewRange)
+ pNewRange.reset(new ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
+ else
+ pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
+ }
+ }
+
+ rDoc.maRangeArrays.emplace(aCacheRange, pArray);
+ if (pNewRange && *pNewRange != aCacheRange)
+ rDoc.maRangeArrays.emplace(*pNewRange, pArray);
+
+ return pArray;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const OUString& rName)
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocItem* pDoc = getDocItem(aGuard, nFileId);
+ if (!pDoc)
+ return TokenArrayRef();
+
+ RangeNameMap& rMap = pDoc->maRangeNames;
+ RangeNameMap::const_iterator itr = rMap.find(
+ ScGlobal::getCharClass().uppercase(rName));
+ if (itr == rMap.end())
+ return TokenArrayRef();
+
+ return itr->second;
+}
+
+void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocItem* pDoc = getDocItem(aGuard, nFileId);
+ if (!pDoc)
+ return;
+
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ RangeNameMap& rMap = pDoc->maRangeNames;
+ rMap.emplace(aUpperName, pArray);
+ pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
+}
+
+bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocItem* pDoc = getDocItem(aGuard, nFileId);
+ if (!pDoc)
+ return false;
+
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ const RangeNameMap& rMap = pDoc->maRangeNames;
+ return rMap.count(aUpperName) > 0;
+}
+
+void ScExternalRefCache::setRangeName(sal_uInt16 nFileId, const OUString& rName)
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ DocItem* pDoc = getDocItem(aGuard, nFileId);
+ if (!pDoc)
+ return;
+
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
+}
+
+void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
+ TokenRef const & pToken, sal_uLong nFmtIndex)
+{
+ if (!isDocInitialized(nFileId))
+ return;
+
+ using ::std::pair;
+ DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ return;
+
+ DocItem& rDoc = *pDocItem;
+
+ // See if the table by this name already exists.
+ TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rTabName);
+ if (itrTabName == rDoc.maTableNameIndex.end())
+ // Table not found. Maybe the table name or the file id is wrong ???
+ return;
+
+ TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
+ if (!pTableData)
+ pTableData = std::make_shared<Table>();
+
+ pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
+ pTableData->setCachedCell(nCol, nRow);
+}
+
+void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
+ const TokenArrayRef& pArray)
+{
+ using ::std::pair;
+ if (rData.empty() || !isDocInitialized(nFileId))
+ // nothing to cache
+ return;
+
+ // First, get the document item for the given file ID.
+ DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ return;
+
+ DocItem& rDoc = *pDocItem;
+
+ // Now, find the table position of the first table to cache.
+ const OUString& rFirstTabName = rData.front().maTableName;
+ TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rFirstTabName);
+ if (itrTabName == rDoc.maTableNameIndex.end())
+ {
+ // table index not found.
+ return;
+ }
+
+ size_t nTabFirstId = itrTabName->second;
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+ size_t i = nTabFirstId;
+ for (const auto& rItem : rData)
+ {
+ TableTypeRef& pTabData = rDoc.maTables[i];
+ if (!pTabData)
+ pTabData = std::make_shared<Table>();
+
+ const ScMatrixRef& pMat = rItem.mpRangeData;
+ SCSIZE nMatCols, nMatRows;
+ pMat->GetDimensions( nMatCols, nMatRows);
+ if (nMatCols > o3tl::make_unsigned(nCol2 - nCol1) && nMatRows > o3tl::make_unsigned(nRow2 - nRow1))
+ {
+ ScMatrix::DoubleOpFunction aDoubleFunc = [=](size_t row, size_t col, double val) -> void
+ {
+ pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val), 0, false);
+ };
+ ScMatrix::BoolOpFunction aBoolFunc = [=](size_t row, size_t col, bool val) -> void
+ {
+ pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val ? 1.0 : 0.0), 0, false);
+ };
+ ScMatrix::StringOpFunction aStringFunc = [=](size_t row, size_t col, svl::SharedString val) -> void
+ {
+ pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaStringToken(std::move(val)), 0, false);
+ };
+ ScMatrix::EmptyOpFunction aEmptyFunc = [](size_t /*row*/, size_t /*col*/) -> void
+ {
+ // Nothing. Empty cell.
+ };
+ pMat->ExecuteOperation(std::pair<size_t, size_t>(0, 0),
+ std::pair<size_t, size_t>(nRow2-nRow1, nCol2-nCol1),
+ std::move(aDoubleFunc), std::move(aBoolFunc), std::move(aStringFunc), std::move(aEmptyFunc));
+ // Mark the whole range 'cached'.
+ pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2);
+ }
+ else
+ {
+ // This may happen due to a matrix not been allocated earlier, in
+ // which case it should have exactly one error element.
+ SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix size mismatch");
+ if (nMatCols != 1 || nMatRows != 1)
+ SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix");
+ else
+ {
+ FormulaError nErr = GetDoubleErrorValue( pMat->GetDouble(0,0));
+ SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << static_cast<int>(nErr) <<
+ (nErr == FormulaError::MatrixSize ? ", ok" : ", not ok"));
+ }
+ }
+ ++i;
+ }
+
+ size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
+ ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
+
+ rDoc.maRangeArrays.emplace(aCacheRange, pArray);
+}
+
+bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return false;
+
+ return pDoc->mbInitFromSource;
+}
+
+static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
+{
+ ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
+ if (itr == rMap.end())
+ return false;
+
+ rIndex = itr->second;
+ return true;
+}
+
+bool ScExternalRefCache::DocItem::getTableDataIndex( const OUString& rTabName, size_t& rIndex ) const
+{
+ ScExternalRefCache::TableNameIndexMap::const_iterator itr = findTableNameIndex(rTabName);
+ if (itr == maTableNameIndex.end())
+ return false;
+
+ rIndex = itr->second;
+ return true;
+}
+
+namespace {
+OUString getFirstSheetName()
+{
+ // Get Custom prefix.
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ // Form sheet name identical to the first generated sheet name when
+ // creating an internal document, e.g. 'Sheet1'.
+ return rOpt.GetInitTabPrefix() + "1";
+}
+}
+
+void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames,
+ const OUString& rBaseName)
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return;
+
+ size_t n = rTabNames.size();
+
+ // table name list - the list must include all table names in the source
+ // document and only to be populated when loading the source document, not
+ // when loading cached data from, say, Excel XCT/CRN records.
+ vector<TableName> aNewTabNames;
+ aNewTabNames.reserve(n);
+ for (const auto& rTabName : rTabNames)
+ {
+ TableName aNameItem(ScGlobal::getCharClass().uppercase(rTabName), rTabName);
+ aNewTabNames.push_back(aNameItem);
+ }
+ pDoc->maTableNames.swap(aNewTabNames);
+
+ // data tables - preserve any existing data that may have been set during
+ // file import.
+ vector<TableTypeRef> aNewTables(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ size_t nIndex;
+ if (lcl_getStrictTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
+ {
+ aNewTables[i] = pDoc->maTables[nIndex];
+ }
+ }
+ pDoc->maTables.swap(aNewTables);
+
+ // name index map
+ TableNameIndexMap aNewNameIndex;
+ for (size_t i = 0; i < n; ++i)
+ aNewNameIndex.emplace(pDoc->maTableNames[i].maUpperName, i);
+ pDoc->maTableNameIndex.swap(aNewNameIndex);
+
+ // Setup name for Sheet1 vs base name to be able to load documents
+ // that store the base name as table name, or vice versa.
+ pDoc->maSingleTableNameAlias.clear();
+ if (!rBaseName.isEmpty() && pDoc->maTableNames.size() == 1)
+ {
+ OUString aSheetName = getFirstSheetName();
+ // If the one and only table name matches exactly, carry on the base
+ // file name for further alias use. If instead the table name matches
+ // the base name, carry on the sheet name as alias.
+ if (ScGlobal::GetTransliteration().isEqual( pDoc->maTableNames[0].maRealName, aSheetName))
+ pDoc->maSingleTableNameAlias = rBaseName;
+ else if (ScGlobal::GetTransliteration().isEqual( pDoc->maTableNames[0].maRealName, rBaseName))
+ pDoc->maSingleTableNameAlias = aSheetName;
+ }
+
+ pDoc->mbInitFromSource = true;
+}
+
+ScExternalRefCache::TableNameIndexMap::const_iterator ScExternalRefCache::DocItem::findTableNameIndex(
+ const OUString& rTabName ) const
+{
+ const OUString aTabNameUpper = ScGlobal::getCharClass().uppercase( rTabName);
+ TableNameIndexMap::const_iterator itrTabName = maTableNameIndex.find( aTabNameUpper);
+ if (itrTabName != maTableNameIndex.end())
+ return itrTabName;
+
+ // Since some time for external references to CSV files the base name is
+ // used as sheet name instead of Sheet1, check if we can resolve that.
+ // Also helps users that got accustomed to one or the other way.
+ if (maSingleTableNameAlias.isEmpty() || maTableNameIndex.size() != 1)
+ return itrTabName;
+
+ // maSingleTableNameAlias has been set up only if the original file loaded
+ // had exactly one sheet and internal sheet name was Sheet1 or localized or
+ // customized equivalent, or base name.
+ if (aTabNameUpper == ScGlobal::getCharClass().uppercase( maSingleTableNameAlias))
+ return maTableNameIndex.begin();
+
+ return itrTabName;
+}
+
+bool ScExternalRefCache::DocItem::getSingleTableNameAlternative( OUString& rTabName ) const
+{
+ if (maSingleTableNameAlias.isEmpty() || maTableNames.size() != 1)
+ return false;
+ if (ScGlobal::GetTransliteration().isEqual( rTabName, maTableNames[0].maRealName))
+ {
+ rTabName = maSingleTableNameAlias;
+ return true;
+ }
+ if (ScGlobal::GetTransliteration().isEqual( rTabName, maSingleTableNameAlias))
+ {
+ rTabName = maTableNames[0].maRealName;
+ return true;
+ }
+ return false;
+}
+
+bool ScExternalRefCache::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
+ sal_uInt16 nFileId ) const
+{
+ bool bFound = rSrcDoc.GetTable( rTabName, rTab);
+ if (!bFound)
+ {
+ // Check the one table alias alternative.
+ const DocItem* pDoc = getDocItem( nFileId );
+ if (pDoc)
+ {
+ OUString aTabName( rTabName);
+ if (pDoc->getSingleTableNameAlternative( aTabName))
+ bFound = rSrcDoc.GetTable( aTabName, rTab);
+ }
+ }
+ return bFound;
+}
+
+OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
+{
+ if( DocItem* pDoc = getDocItem( nFileId ) )
+ if( nCacheId < pDoc->maTableNames.size() )
+ return pDoc->maTableNames[ nCacheId ].maRealName;
+ return OUString();
+}
+
+void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
+{
+ rTabNames.clear();
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return;
+
+ size_t n = pDoc->maTableNames.size();
+ rTabNames.reserve(n);
+ for (const auto& rTableName : pDoc->maTableNames)
+ rTabNames.push_back(rTableName.maRealName);
+}
+
+SCTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ return -1;
+
+ vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
+ vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
+
+ vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
+ TabNameSearchPredicate( rStartTabName));
+ if (itrStartTab == itrEnd)
+ return -1;
+
+ vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
+ TabNameSearchPredicate( rEndTabName));
+ if (itrEndTab == itrEnd)
+ return 0;
+
+ size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
+ size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
+ return nStartDist <= nEndDist ? static_cast<SCTAB>(nEndDist - nStartDist + 1) : -static_cast<SCTAB>(nStartDist - nEndDist + 1);
+}
+
+void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ using ::std::sort;
+ using ::std::unique;
+
+ vector<sal_uInt32> aNumFmts;
+ for (const auto& rEntry : maDocs)
+ {
+ const vector<TableTypeRef>& rTables = rEntry.second.maTables;
+ for (const TableTypeRef& pTab : rTables)
+ {
+ if (!pTab)
+ continue;
+
+ pTab->getAllNumberFormats(aNumFmts);
+ }
+ }
+
+ // remove duplicates.
+ sort(aNumFmts.begin(), aNumFmts.end());
+ aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
+ rNumFmts.swap(aNumFmts);
+}
+
+bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId )
+{
+ DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ return areAllCacheTablesReferenced();
+
+ for (auto& rxTab : pDocItem->maTables)
+ {
+ if (rxTab)
+ rxTab->setReferenced(true);
+ }
+ addCacheDocToReferenced( nFileId);
+ return areAllCacheTablesReferenced();
+}
+
+bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (pDoc)
+ {
+ size_t nIndex = 0;
+ if (pDoc->getTableDataIndex( rTabName, nIndex))
+ {
+ size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
+ for (size_t i = nIndex; i < nStop; ++i)
+ {
+ TableTypeRef pTab = pDoc->maTables[i];
+ if (pTab)
+ {
+ if (!pTab->isReferenced())
+ {
+ pTab->setReferenced(true);
+ addCacheTableToReferenced( nFileId, i);
+ }
+ }
+ }
+ }
+ }
+ return areAllCacheTablesReferenced();
+}
+
+void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced )
+{
+ std::unique_lock aGuard(maMtxDocs);
+
+ if (bReferenced)
+ {
+ maReferenced.reset(0);
+ for (auto& rEntry : maDocs)
+ {
+ ScExternalRefCache::DocItem& rDocItem = rEntry.second;
+ for (auto& rxTab : rDocItem.maTables)
+ {
+ if (rxTab)
+ rxTab->setReferenced(true);
+ }
+ }
+ }
+ else
+ {
+ size_t nDocs = 0;
+ auto itrMax = std::max_element(maDocs.begin(), maDocs.end(),
+ [](const DocDataType::value_type& a, const DocDataType::value_type& b) { return a.first < b.first; });
+ if (itrMax != maDocs.end())
+ nDocs = itrMax->first + 1;
+ maReferenced.reset( nDocs);
+
+ for (auto& [nFileId, rDocItem] : maDocs)
+ {
+ size_t nTables = rDocItem.maTables.size();
+ ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
+ // All referenced => non-existing tables evaluate as completed.
+ rDocReferenced.maTables.resize( nTables, true);
+ for (size_t i=0; i < nTables; ++i)
+ {
+ TableTypeRef & xTab = rDocItem.maTables[i];
+ if (xTab)
+ {
+ xTab->setReferenced(false);
+ rDocReferenced.maTables[i] = false;
+ rDocReferenced.mbAllTablesReferenced = false;
+ // An addCacheTableToReferenced() actually may have
+ // resulted in mbAllReferenced been set. Clear it.
+ maReferenced.mbAllReferenced = false;
+ }
+ }
+ }
+ }
+}
+
+void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
+{
+ if (nFileId >= maReferenced.maDocs.size())
+ return;
+
+ ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
+ size_t nTables = rTables.size();
+ if (nIndex >= nTables)
+ return;
+
+ if (!rTables[nIndex])
+ {
+ rTables[nIndex] = true;
+ size_t i = 0;
+ while (i < nTables && rTables[i])
+ ++i;
+ if (i == nTables)
+ {
+ maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
+ maReferenced.checkAllDocs();
+ }
+ }
+}
+
+void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId )
+{
+ if (nFileId >= maReferenced.maDocs.size())
+ return;
+
+ if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
+ {
+ ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
+ size_t nSize = rTables.size();
+ for (size_t i=0; i < nSize; ++i)
+ rTables[i] = true;
+ maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
+ maReferenced.checkAllDocs();
+ }
+}
+
+void ScExternalRefCache::getAllCachedDataSpans( const ScDocument& rSrcDoc, sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const
+{
+ const DocItem* pDocItem = getDocItem(nFileId);
+ if (!pDocItem)
+ // This document is not cached.
+ return;
+
+ const std::vector<TableTypeRef>& rTables = pDocItem->maTables;
+ for (size_t nTab = 0, nTabCount = rTables.size(); nTab < nTabCount; ++nTab)
+ {
+ TableTypeRef pTab = rTables[nTab];
+ if (!pTab)
+ continue;
+
+ std::vector<SCROW> aRows;
+ pTab->getAllRows(aRows);
+ for (SCROW nRow : aRows)
+ {
+ std::vector<SCCOL> aCols;
+ pTab->getAllCols(nRow, aCols);
+ for (SCCOL nCol : aCols)
+ {
+ rSet.set(rSrcDoc, nTab, nCol, nRow, true);
+ }
+ }
+ }
+}
+
+ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
+ mbAllReferenced(false)
+{
+ reset(0);
+}
+
+void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs )
+{
+ if (nDocs)
+ {
+ mbAllReferenced = false;
+ DocReferencedVec aRefs( nDocs);
+ maDocs.swap( aRefs);
+ }
+ else
+ {
+ mbAllReferenced = true;
+ DocReferencedVec aRefs;
+ maDocs.swap( aRefs);
+ }
+}
+
+void ScExternalRefCache::ReferencedStatus::checkAllDocs()
+{
+ if (std::all_of(maDocs.begin(), maDocs.end(), [](const DocReferenced& rDoc) { return rDoc.mbAllTablesReferenced; }))
+ mbAllReferenced = true;
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
+{
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc || nTabIndex >= pDoc->maTables.size())
+ return TableTypeRef();
+
+ return pDoc->maTables[nTabIndex];
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName,
+ bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
+{
+ // In API, the index is transported as cached sheet ID of type sal_Int32 in
+ // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
+ // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
+ // being 0xffffffff
+ const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
+
+ DocItem* pDoc = getDocItem(nFileId);
+ if (!pDoc)
+ {
+ if (pnIndex) *pnIndex = nNotAvailable;
+ return TableTypeRef();
+ }
+
+ DocItem& rDoc = *pDoc;
+
+ size_t nIndex;
+ if (rDoc.getTableDataIndex(rTabName, nIndex))
+ {
+ // specified table found.
+ if( pnIndex ) *pnIndex = nIndex;
+ if (bCreateNew && !rDoc.maTables[nIndex])
+ rDoc.maTables[nIndex] = std::make_shared<Table>();
+
+ return rDoc.maTables[nIndex];
+ }
+
+ if (!bCreateNew)
+ {
+ if (pnIndex) *pnIndex = nNotAvailable;
+ return TableTypeRef();
+ }
+
+ // If this is the first table to be created propagate the base name or
+ // Sheet1 as an alias. For subsequent tables remove it again.
+ if (rDoc.maTableNames.empty())
+ {
+ if (pExtUrl)
+ {
+ const OUString aBaseName( INetURLObject( *pExtUrl).GetBase());
+ const OUString aSheetName( getFirstSheetName());
+ if (ScGlobal::GetTransliteration().isEqual( rTabName, aSheetName))
+ pDoc->maSingleTableNameAlias = aBaseName;
+ else if (ScGlobal::GetTransliteration().isEqual( rTabName, aBaseName))
+ pDoc->maSingleTableNameAlias = aSheetName;
+ }
+ }
+ else
+ {
+ rDoc.maSingleTableNameAlias.clear();
+ }
+
+ // Specified table doesn't exist yet. Create one.
+ OUString aTabNameUpper = ScGlobal::getCharClass().uppercase(rTabName);
+ nIndex = rDoc.maTables.size();
+ if( pnIndex ) *pnIndex = nIndex;
+ TableTypeRef pTab = std::make_shared<Table>();
+ rDoc.maTables.push_back(pTab);
+ rDoc.maTableNames.emplace_back(aTabNameUpper, rTabName);
+ rDoc.maTableNameIndex.emplace(aTabNameUpper, nIndex);
+ return pTab;
+}
+
+void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
+{
+ std::unique_lock aGuard(maMtxDocs);
+ maDocs.erase(nFileId);
+}
+
+void ScExternalRefCache::clearCacheTables(sal_uInt16 nFileId)
+{
+ std::unique_lock aGuard(maMtxDocs);
+ DocItem* pDocItem = getDocItem(aGuard, nFileId);
+ if (!pDocItem)
+ // This document is not cached at all.
+ return;
+
+ // Clear all cache table content, but keep the tables.
+ std::vector<TableTypeRef>& rTabs = pDocItem->maTables;
+ for (TableTypeRef & pTab : rTabs)
+ {
+ if (!pTab)
+ continue;
+
+ pTab->clear();
+ }
+
+ // Clear the external range name caches.
+ pDocItem->maRangeNames.clear();
+ pDocItem->maRangeArrays.clear();
+ pDocItem->maRealRangeNameMap.clear();
+}
+
+ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const
+{
+ std::unique_lock aGuard(maMtxDocs);
+ return getDocItem(aGuard, nFileId);
+}
+
+ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(std::unique_lock<std::mutex>& /*rGuard*/, sal_uInt16 nFileId) const
+{
+
+ using ::std::pair;
+ DocDataType::iterator itrDoc = maDocs.find(nFileId);
+ if (itrDoc == maDocs.end())
+ {
+ // specified document is not cached.
+ pair<DocDataType::iterator, bool> res = maDocs.emplace(
+ nFileId, DocItem());
+
+ if (!res.second)
+ // insertion failed.
+ return nullptr;
+
+ itrDoc = res.first;
+ }
+
+ return &itrDoc->second;
+}
+
+ScExternalRefLink::ScExternalRefLink(ScDocument& rDoc, sal_uInt16 nFileId) :
+ ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE),
+ mnFileId(nFileId),
+ mrDoc(rDoc),
+ mbDoRefresh(true)
+{
+}
+
+ScExternalRefLink::~ScExternalRefLink()
+{
+}
+
+void ScExternalRefLink::Closed()
+{
+ ScExternalRefManager* pMgr = mrDoc.GetExternalRefManager();
+ pMgr->breakLink(mnFileId);
+}
+
+::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
+{
+ if (!mbDoRefresh)
+ return SUCCESS;
+
+ OUString aFile, aFilter;
+ sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, nullptr, &aFilter);
+ ScExternalRefManager* pMgr = mrDoc.GetExternalRefManager();
+
+ if (!pMgr->isFileLoadable(aFile))
+ return ERROR_GENERAL;
+
+ const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
+ if (!pCurFile)
+ return ERROR_GENERAL;
+
+ if (*pCurFile == aFile)
+ {
+ // Refresh the current source document.
+ if (!pMgr->refreshSrcDocument(mnFileId))
+ return ERROR_GENERAL;
+ }
+ else
+ {
+ // The source document has changed.
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return ERROR_GENERAL;
+
+ ScDocShell* pDocShell = pViewData->GetDocShell();
+ ScDocShellModificator aMod(*pDocShell);
+ pMgr->switchSrcFile(mnFileId, aFile, aFilter);
+ aMod.SetDocumentModified();
+ }
+
+ return SUCCESS;
+}
+
+void ScExternalRefLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& /*rEndEditHdl*/)
+{
+ SvBaseLink::Edit(pParent, Link<SvBaseLink&,void>());
+}
+
+void ScExternalRefLink::SetDoRefresh(bool b)
+{
+ mbDoRefresh = b;
+}
+
+static FormulaToken* convertToToken( ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRefCellValue& rCell )
+{
+ if (rCell.hasEmptyValue())
+ {
+ bool bInherited = (rCell.getType() == CELLTYPE_FORMULA);
+ return new ScEmptyCellToken(bInherited, false);
+ }
+
+ switch (rCell.getType())
+ {
+ case CELLTYPE_EDIT:
+ case CELLTYPE_STRING:
+ {
+ OUString aStr = rCell.getString(&rSrcDoc);
+ svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern(aStr);
+ return new formula::FormulaStringToken(std::move(aSS));
+ }
+ case CELLTYPE_VALUE:
+ return new formula::FormulaDoubleToken(rCell.getDouble());
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ FormulaError nError = pFCell->GetErrCode();
+ if (nError != FormulaError::NONE)
+ return new FormulaErrorToken( nError);
+ else if (pFCell->IsValue())
+ {
+ double fVal = pFCell->GetValue();
+ return new formula::FormulaDoubleToken(fVal);
+ }
+ else
+ {
+ svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern( pFCell->GetString().getString());
+ return new formula::FormulaStringToken(std::move(aSS));
+ }
+ }
+ default:
+ OSL_FAIL("attempted to convert an unknown cell type.");
+ }
+
+ return nullptr;
+}
+
+static std::unique_ptr<ScTokenArray> convertToTokenArray(
+ ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
+{
+ ScAddress& s = rRange.aStart;
+ ScAddress& e = rRange.aEnd;
+
+ const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
+ const SCCOL nCol1 = s.Col(), nCol2 = e.Col();
+ const SCROW nRow1 = s.Row(), nRow2 = e.Row();
+
+ if (nTab2 != nTab1)
+ // For now, we don't support multi-sheet ranges intentionally because
+ // we don't have a way to express them in a single token. In the
+ // future we can introduce a new stack variable type svMatrixList with
+ // a new token type that can store a 3D matrix value and convert a 3D
+ // range to it.
+ return nullptr;
+
+ std::unique_ptr<ScRange> pUsedRange;
+
+ unique_ptr<ScTokenArray> pArray(new ScTokenArray(rSrcDoc));
+ bool bFirstTab = true;
+ vector<ScExternalRefCache::SingleRangeData>::iterator
+ itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
+
+ for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
+ {
+ // Only loop within the data area.
+ SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
+ SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
+ bool bShrunk;
+ if (!rSrcDoc.ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
+ // no data within specified range.
+ continue;
+
+ if (pUsedRange)
+ // Make sure the used area only grows, not shrinks.
+ pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
+ else
+ pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
+
+ SCSIZE nMatrixColumns = static_cast<SCSIZE>(nCol2-nCol1+1);
+ SCSIZE nMatrixRows = static_cast<SCSIZE>(nRow2-nRow1+1);
+ ScMatrixRef xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
+
+ // Check if size could be allocated and if not skip the fill, there's
+ // one error element instead. But retry first with the actual data area
+ // if that is smaller than the original range, which works for most
+ // functions just not some that operate/compare with the original size
+ // and expect empty values in non-data areas.
+ // Restrict this though to ranges of entire columns or rows, other
+ // ranges might be on purpose. (Other special cases to handle?)
+ /* TODO: sparse matrix could help */
+ SCSIZE nMatCols, nMatRows;
+ xMat->GetDimensions( nMatCols, nMatRows);
+ if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
+ {
+ rSrcDoc.FillMatrix(*xMat, nTab, nCol1, nRow1, nCol2, nRow2, &rHostDoc.GetSharedStringPool());
+ }
+ else if ((nCol1 == 0 && nCol2 == rSrcDoc.MaxCol()) || (nRow1 == 0 && nRow2 == rSrcDoc.MaxRow()))
+ {
+ if ((o3tl::make_unsigned(nDataCol2-nDataCol1+1) < nMatrixColumns) ||
+ (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows))
+ {
+ nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1);
+ nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
+ xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
+ xMat->GetDimensions( nMatCols, nMatRows);
+ if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
+ rSrcDoc.FillMatrix(*xMat, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, &rHostDoc.GetSharedStringPool());
+ }
+ }
+
+ if (!bFirstTab)
+ pArray->AddOpCode(ocSep);
+
+ ScMatrixToken aToken(xMat);
+ pArray->AddToken(aToken);
+
+ itrCache->mpRangeData = xMat;
+
+ bFirstTab = false;
+ }
+
+ if (!pUsedRange)
+ return nullptr;
+
+ s.SetCol(pUsedRange->aStart.Col());
+ s.SetRow(pUsedRange->aStart.Row());
+ e.SetCol(pUsedRange->aEnd.Col());
+ e.SetRow(pUsedRange->aEnd.Row());
+
+ return pArray;
+}
+
+static std::unique_ptr<ScTokenArray> lcl_fillEmptyMatrix(const ScDocument& rDoc, const ScRange& rRange)
+{
+ SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1);
+ SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1);
+ ScMatrixRef xMat = new ScMatrix(nC, nR);
+
+ ScMatrixToken aToken(std::move(xMat));
+ unique_ptr<ScTokenArray> pArray(new ScTokenArray(rDoc));
+ pArray->AddToken(aToken);
+ return pArray;
+}
+
+namespace {
+bool isLinkUpdateAllowedInDoc(const ScDocument& rDoc)
+{
+ ScDocShell* pDocShell = rDoc.GetDocumentShell();
+ if (!pDocShell)
+ return rDoc.IsFunctionAccess();
+
+ return pDocShell->GetEmbeddedObjectContainer().getUserAllowsLinkUpdate();
+}
+}
+
+ScExternalRefManager::ScExternalRefManager(ScDocument& rDoc) :
+ mrDoc(rDoc),
+ maRefCache(rDoc),
+ mbInReferenceMarking(false),
+ mbUserInteractionEnabled(true),
+ mbDocTimerEnabled(true),
+ maSrcDocTimer( "sc::ScExternalRefManager maSrcDocTimer" )
+{
+ maSrcDocTimer.SetInvokeHandler( LINK(this, ScExternalRefManager, TimeOutHdl) );
+ maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL);
+}
+
+ScExternalRefManager::~ScExternalRefManager()
+{
+ clear();
+}
+
+OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
+{
+ return maRefCache.getTableName(nFileId, nTabIndex);
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
+{
+ return maRefCache.getCacheTable(nFileId, nTabIndex);
+}
+
+ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(
+ sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
+{
+ return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex, pExtUrl);
+}
+
+ScExternalRefManager::LinkListener::LinkListener()
+{
+}
+
+ScExternalRefManager::LinkListener::~LinkListener()
+{
+}
+
+ScExternalRefManager::ApiGuard::ApiGuard(const ScDocument& rDoc) :
+ mpMgr(rDoc.GetExternalRefManager()),
+ mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
+{
+ // We don't want user interaction handled in the API.
+ mpMgr->mbUserInteractionEnabled = false;
+}
+
+ScExternalRefManager::ApiGuard::~ApiGuard()
+{
+ // Restore old value.
+ mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
+}
+
+void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
+{
+ maRefCache.getAllTableNames(nFileId, rTabNames);
+}
+
+SCTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
+{
+ return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
+}
+
+void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
+{
+ maRefCache.getAllNumberFormats(rNumFmts);
+}
+
+sal_uInt16 ScExternalRefManager::getExternalFileCount() const
+{
+ return static_cast< sal_uInt16 >( maSrcFiles.size() );
+}
+
+void ScExternalRefManager::markUsedByLinkListeners()
+{
+ bool bAllMarked = false;
+ for (const auto& [rFileId, rLinkListeners] : maLinkListeners)
+ {
+ if (!rLinkListeners.empty())
+ bAllMarked = maRefCache.setCacheDocReferenced(rFileId);
+
+ if (bAllMarked)
+ break;
+ /* TODO: LinkListeners should remember the table they're listening to.
+ * As is, listening to one table will mark all tables of the document
+ * being referenced. */
+ }
+}
+
+void ScExternalRefManager::markUsedExternalRefCells()
+{
+ for (const auto& rEntry : maRefCells)
+ {
+ for (ScFormulaCell* pCell : rEntry.second)
+ {
+ bool bUsed = pCell->MarkUsedExternalReferences();
+ if (bUsed)
+ // Return true when at least one cell references external docs.
+ return;
+ }
+ }
+}
+
+bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
+{
+ return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets);
+}
+
+void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced )
+{
+ mbInReferenceMarking = !bReferenced;
+ maRefCache.setAllCacheTableReferencedStati( bReferenced );
+}
+
+void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
+{
+ ScExternalRefCache::TokenArrayRef pNewArray;
+ if (!rArray.HasExternalRef())
+ {
+ // Parse all tokens in this external range data, and replace each absolute
+ // reference token with an external reference token, and cache them.
+ pNewArray = std::make_shared<ScTokenArray>(mrDoc);
+ FormulaTokenArrayPlainIterator aIter(rArray);
+ for (const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next())
+ {
+ bool bTokenAdded = false;
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ OUString aTabName;
+ if (SCTAB nCacheId = rRef.Tab(); nCacheId >= 0)
+ aTabName = maRefCache.getTableName(nFileId, nCacheId);
+ ScExternalSingleRefToken aNewToken(nFileId, svl::SharedString(aTabName), // string not interned
+ *pToken->GetSingleRef());
+ pNewArray->AddToken(aNewToken);
+ bTokenAdded = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ OUString aTabName;
+ if (SCTAB nCacheId = rRef.Tab(); nCacheId >= 0)
+ aTabName = maRefCache.getTableName(nFileId, nCacheId);
+ ScExternalDoubleRefToken aNewToken(nFileId, svl::SharedString(aTabName), // string not interned
+ *pToken->GetDoubleRef());
+ pNewArray->AddToken(aNewToken);
+ bTokenAdded = true;
+ }
+ break;
+ default:
+ ; // nothing
+ }
+
+ if (!bTokenAdded)
+ pNewArray->AddToken(*pToken);
+ }
+ }
+ else
+ pNewArray = rArray.Clone();
+
+ maRefCache.setRangeNameTokens(nFileId, rName, pNewArray);
+}
+
+namespace {
+
+/**
+ * Put a single cell data into internal cache table.
+ *
+ * @param pFmt optional cell format index that may need to be stored with
+ * the cell value.
+ */
+void putCellDataIntoCache(
+ ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
+ sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
+ const ScExternalRefCache::CellFormat* pFmt)
+{
+ // Now, insert the token into cache table but don't cache empty cells.
+ if (pToken->GetType() != formula::svEmptyCell)
+ {
+ sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
+ rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
+ }
+}
+
+/**
+ * Put the data into our internal cache table.
+ *
+ * @param rRefCache cache table set.
+ * @param pArray single range data to be returned.
+ * @param nFileId external file ID
+ * @param rTabName name of the table where the data should be cached.
+ * @param rCacheData range data to be cached.
+ * @param rCacheRange original cache range, including the empty region if
+ * any.
+ * @param rDataRange reduced cache range that includes only the non-empty
+ * data area.
+ */
+void putRangeDataIntoCache(
+ ScExternalRefCache& rRefCache, ScExternalRefCache::TokenArrayRef& pArray,
+ sal_uInt16 nFileId, const OUString& rTabName,
+ const vector<ScExternalRefCache::SingleRangeData>& rCacheData,
+ const ScRange& rCacheRange, const ScRange& rDataRange)
+{
+ if (pArray)
+ // Cache these values.
+ rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
+ else
+ {
+ // Array is empty. Fill it with an empty matrix of the required size.
+ pArray = lcl_fillEmptyMatrix(rRefCache.getDoc(), rCacheRange);
+
+ // Make sure to set this range 'cached', to prevent unnecessarily
+ // accessing the src document time and time again.
+ ScExternalRefCache::TableTypeRef pCacheTab =
+ rRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr);
+ if (pCacheTab)
+ pCacheTab->setCachedCellRange(
+ rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
+ }
+}
+
+/**
+ * When accessing an external document for the first time, we need to
+ * populate the cache with all its sheet names (whether they are referenced
+ * or not) in the correct order. Many client codes that use external
+ * references make this assumption.
+ *
+ * @param rRefCache cache table set.
+ * @param pSrcDoc source document instance.
+ * @param nFileId external file ID associated with the source document.
+ */
+void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
+{
+ if (!pSrcDoc)
+ return;
+
+ if (rRefCache.isDocInitialized(nFileId))
+ // Already initialized. No need to do this twice.
+ return;
+
+ SCTAB nTabCount = pSrcDoc->GetTableCount();
+ if (!nTabCount)
+ return;
+
+ // Populate the cache with all table names in the source document.
+ vector<OUString> aTabNames;
+ aTabNames.reserve(nTabCount);
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ OUString aName;
+ pSrcDoc->GetName(i, aName);
+ aTabNames.push_back(aName);
+ }
+
+ // Obtain the base name, don't bother if there are more than one sheets.
+ OUString aBaseName;
+ if (nTabCount == 1)
+ {
+ const ScDocShell* pShell = pSrcDoc->GetDocumentShell();
+ if (pShell && pShell->GetMedium())
+ {
+ OUString aName = pShell->GetMedium()->GetName();
+ aBaseName = INetURLObject( aName).GetBase();
+ }
+ }
+
+ rRefCache.initializeDoc(nFileId, aTabNames, aBaseName);
+}
+
+}
+
+bool ScExternalRefManager::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
+ sal_uInt16 nFileId ) const
+{
+ return maRefCache.getSrcDocTable( rSrcDoc, rTabName, rTab, nFileId);
+}
+
+ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken(
+ sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
+ const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
+{
+ if (pCurPos)
+ insertRefCell(nFileId, *pCurPos);
+
+ maybeLinkExternalFile(nFileId);
+
+ if (pTab)
+ *pTab = -1;
+
+ if (pFmt)
+ pFmt->mbIsSet = false;
+
+ ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
+ if (pSrcDoc)
+ {
+ // source document already loaded in memory. Re-use this instance.
+ SCTAB nTab;
+ if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
+ {
+ // specified table name doesn't exist in the source document.
+ ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(FormulaError::NoRef));
+ return pToken;
+ }
+
+ if (pTab)
+ *pTab = nTab;
+
+ ScExternalRefCache::TokenRef pToken =
+ getSingleRefTokenFromSrcDoc(
+ nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
+
+ putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
+ return pToken;
+ }
+
+ // Check if the given table name and the cell position is cached.
+ sal_uInt32 nFmtIndex = 0;
+ ScExternalRefCache::TokenRef pToken = maRefCache.getCellData(
+ nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex);
+ if (pToken)
+ {
+ // Cache hit !
+ fillCellFormat(nFmtIndex, pFmt);
+ return pToken;
+ }
+
+ // reference not cached. read from the source document.
+ pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ {
+ // Source document not reachable.
+ if (!isLinkUpdateAllowedInDoc(mrDoc))
+ {
+ // Indicate with specific error.
+ pToken.reset(new FormulaErrorToken(FormulaError::LinkFormulaNeedingCheck));
+ }
+ else
+ {
+ // Throw a reference error.
+ pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
+ }
+ return pToken;
+ }
+
+ SCTAB nTab;
+ if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
+ {
+ // specified table name doesn't exist in the source document.
+ pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
+ return pToken;
+ }
+
+ if (pTab)
+ *pTab = nTab;
+
+ SCCOL nDataCol1 = 0, nDataCol2 = pSrcDoc->MaxCol();
+ SCROW nDataRow1 = 0, nDataRow2 = pSrcDoc->MaxRow();
+ bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2);
+ if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row())
+ {
+ // requested cell is outside the data area. Don't even bother caching
+ // this data, but add it to the cached range to prevent accessing the
+ // source document time and time again.
+ ScExternalRefCache::TableTypeRef pCacheTab =
+ maRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr);
+ if (pCacheTab)
+ pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
+
+ pToken.reset(new ScEmptyCellToken(false, false));
+ return pToken;
+ }
+
+ pToken = getSingleRefTokenFromSrcDoc(
+ nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
+
+ putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
+ return pToken;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens(
+ sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
+{
+ if (pCurPos)
+ insertRefCell(nFileId, *pCurPos);
+
+ maybeLinkExternalFile(nFileId);
+
+ ScRange aDataRange(rRange);
+ ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
+ if (pSrcDoc)
+ {
+ // Document already loaded in memory.
+ vector<ScExternalRefCache::SingleRangeData> aCacheData;
+ ScExternalRefCache::TokenArrayRef pArray =
+ getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
+
+ // Put the data into cache.
+ putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
+ return pArray;
+ }
+
+ // Check if the given table name and the cell position is cached.
+ ScExternalRefCache::TokenArrayRef pArray =
+ maRefCache.getCellRangeData(nFileId, rTabName, rRange);
+ if (pArray)
+ // Cache hit !
+ return pArray;
+
+ pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ {
+ // Source document is not reachable. Throw a reference error.
+ pArray = std::make_shared<ScTokenArray>(maRefCache.getDoc());
+ pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
+ return pArray;
+ }
+
+ vector<ScExternalRefCache::SingleRangeData> aCacheData;
+ pArray = getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
+
+ // Put the data into cache.
+ putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
+ return pArray;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(
+ sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
+{
+ if (pCurPos)
+ insertRefCell(nFileId, *pCurPos);
+
+ maybeLinkExternalFile(nFileId);
+
+ OUString aName = rName; // make a copy to have the casing corrected.
+ ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
+ if (pSrcDoc)
+ {
+ // Document already loaded in memory.
+ ScExternalRefCache::TokenArrayRef pArray =
+ getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
+
+ if (pArray)
+ // Cache this range name array.
+ maRefCache.setRangeNameTokens(nFileId, aName, pArray);
+
+ return pArray;
+ }
+
+ ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName);
+ if (pArray)
+ // This range name is cached.
+ return pArray;
+
+ pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ // failed to load document from disk.
+ return ScExternalRefCache::TokenArrayRef();
+
+ pArray = getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
+
+ if (pArray)
+ // Cache this range name array.
+ maRefCache.setRangeNameTokens(nFileId, aName, pArray);
+
+ return pArray;
+}
+
+namespace {
+
+bool hasRangeName(const ScDocument& rDoc, const OUString& rName)
+{
+ ScRangeName* pExtNames = rDoc.GetRangeName();
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
+ return pRangeData != nullptr;
+}
+
+}
+
+bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
+{
+ maybeLinkExternalFile(nFileId);
+ ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
+ if (pSrcDoc)
+ {
+ // Only check the presence of the name.
+ if (hasRangeName(*pSrcDoc, rName))
+ {
+ maRefCache.setRangeName(nFileId, rName);
+ return true;
+ }
+ return false;
+ }
+
+ if (maRefCache.isValidRangeName(nFileId, rName))
+ // Range name is cached.
+ return true;
+
+ pSrcDoc = getSrcDocument(nFileId);
+ if (!pSrcDoc)
+ // failed to load document from disk.
+ return false;
+
+ if (hasRangeName(*pSrcDoc, rName))
+ {
+ maRefCache.setRangeName(nFileId, rName);
+ return true;
+ }
+
+ return false;
+}
+
+void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId)
+{
+ RefCellMap::iterator itrFile = maRefCells.find(nFileId);
+ if (itrFile == maRefCells.end())
+ return;
+
+ RefCellSet& rRefCells = itrFile->second;
+ for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScTabViewShell* pVShell = pViewData->GetViewShell();
+ if (!pVShell)
+ return;
+
+ // Repainting the grid also repaints the texts, but is there a better way
+ // to refresh texts?
+ pVShell->Invalidate(FID_REPAINT);
+ pVShell->PaintGrid();
+}
+
+namespace {
+
+void insertRefCellByIterator(
+ const ScExternalRefManager::RefCellMap::iterator& itr, ScFormulaCell* pCell)
+{
+ if (pCell)
+ {
+ itr->second.insert(pCell);
+ pCell->SetIsExtRef();
+ }
+}
+
+}
+
+void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
+{
+ RefCellMap::iterator itr = maRefCells.find(nFileId);
+ if (itr == maRefCells.end())
+ {
+ RefCellSet aRefCells;
+ pair<RefCellMap::iterator, bool> r = maRefCells.emplace(
+ nFileId, aRefCells);
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+
+ insertRefCellByIterator(itr, mrDoc.GetFormulaCell(rCell));
+}
+
+void ScExternalRefManager::insertRefCellFromTemplate( ScFormulaCell* pTemplateCell, ScFormulaCell* pCell )
+{
+ if (!pTemplateCell || !pCell)
+ return;
+
+ for (RefCellMap::iterator itr = maRefCells.begin(); itr != maRefCells.end(); ++itr)
+ {
+ if (itr->second.find(pTemplateCell) != itr->second.end())
+ insertRefCellByIterator(itr, pCell);
+ }
+}
+
+bool ScExternalRefManager::hasCellExternalReference(const ScAddress& rCell)
+{
+ ScFormulaCell* pCell = mrDoc.GetFormulaCell(rCell);
+
+ if (pCell)
+ return std::any_of(maRefCells.begin(), maRefCells.end(),
+ [&pCell](const RefCellMap::value_type& rEntry) { return rEntry.second.find(pCell) != rEntry.second.end(); });
+
+ return false;
+}
+
+void ScExternalRefManager::enableDocTimer( bool bEnable )
+{
+ if (mbDocTimerEnabled == bEnable)
+ return;
+
+ mbDocTimerEnabled = bEnable;
+ if (mbDocTimerEnabled)
+ {
+ if (!maDocShells.empty())
+ {
+ for (auto& rEntry : maDocShells)
+ rEntry.second.maLastAccess = tools::Time(tools::Time::SYSTEM);
+
+ maSrcDocTimer.Start();
+ }
+ }
+ else
+ maSrcDocTimer.Stop();
+}
+
+void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat* pFmt) const
+{
+ if (!pFmt)
+ return;
+
+ SvNumFormatType nFmtType = mrDoc.GetFormatTable()->GetType(nFmtIndex);
+ if (nFmtType != SvNumFormatType::UNDEFINED)
+ {
+ pFmt->mbIsSet = true;
+ pFmt->mnIndex = nFmtIndex;
+ pFmt->mnType = nFmtType;
+ }
+}
+
+ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefTokenFromSrcDoc(
+ sal_uInt16 nFileId, ScDocument& rSrcDoc, const ScAddress& rPos,
+ ScExternalRefCache::CellFormat* pFmt)
+{
+ // Get the cell from src doc, and convert it into a token.
+ ScRefCellValue aCell(rSrcDoc, rPos);
+ ScExternalRefCache::TokenRef pToken(convertToToken(mrDoc, rSrcDoc, aCell));
+
+ if (!pToken)
+ {
+ // Generate an error for unresolvable cells.
+ pToken.reset( new FormulaErrorToken( FormulaError::NoValue));
+ }
+
+ // Get number format information.
+ sal_uInt32 nFmtIndex = rSrcDoc.GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab());
+ nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, rSrcDoc);
+ fillCellFormat(nFmtIndex, pFmt);
+ return pToken;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
+ const ScDocument& rSrcDoc, const OUString& rTabName, ScRange& rRange,
+ vector<ScExternalRefCache::SingleRangeData>& rCacheData)
+{
+ ScExternalRefCache::TokenArrayRef pArray;
+ SCTAB nTab1;
+
+ if (!rSrcDoc.GetTable(rTabName, nTab1))
+ {
+ // specified table name doesn't exist in the source document.
+ pArray = std::make_shared<ScTokenArray>(rSrcDoc);
+ pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
+ return pArray;
+ }
+
+ ScRange aRange(rRange);
+ aRange.PutInOrder();
+ SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
+
+ vector<ScExternalRefCache::SingleRangeData> aCacheData;
+ aCacheData.reserve(nTabSpan+1);
+ aCacheData.emplace_back();
+ aCacheData.back().maTableName = ScGlobal::getCharClass().uppercase(rTabName);
+
+ for (SCTAB i = 1; i < nTabSpan + 1; ++i)
+ {
+ OUString aTabName;
+ if (!rSrcDoc.GetName(nTab1 + 1, aTabName))
+ // source document doesn't have any table by the specified name.
+ break;
+
+ aCacheData.emplace_back();
+ aCacheData.back().maTableName = ScGlobal::getCharClass().uppercase(aTabName);
+ }
+
+ aRange.aStart.SetTab(nTab1);
+ aRange.aEnd.SetTab(nTab1 + nTabSpan);
+
+ pArray = convertToTokenArray(mrDoc, rSrcDoc, aRange, aCacheData);
+ rRange = aRange;
+ rCacheData.swap(aCacheData);
+ return pArray;
+}
+
+ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokensFromSrcDoc(
+ sal_uInt16 nFileId, const ScDocument& rSrcDoc, OUString& rName)
+{
+ ScRangeName* pExtNames = rSrcDoc.GetRangeName();
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
+ if (!pRangeData)
+ return ScExternalRefCache::TokenArrayRef();
+
+ // Parse all tokens in this external range data, and replace each absolute
+ // reference token with an external reference token, and cache them. Also
+ // register the source document with the link manager if it's a new
+ // source.
+
+ ScExternalRefCache::TokenArrayRef pNew = std::make_shared<ScTokenArray>(rSrcDoc);
+
+ ScTokenArray aCode(*pRangeData->GetCode());
+ FormulaTokenArrayPlainIterator aIter(aCode);
+ for (const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next())
+ {
+ bool bTokenAdded = false;
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ OUString aTabName;
+ rSrcDoc.GetName(rRef.Tab(), aTabName);
+ ScExternalSingleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
+ *pToken->GetSingleRef());
+ pNew->AddToken(aNewToken);
+ bTokenAdded = true;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ OUString aTabName;
+ rSrcDoc.GetName(rRef.Tab(), aTabName);
+ ScExternalDoubleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
+ *pToken->GetDoubleRef());
+ pNew->AddToken(aNewToken);
+ bTokenAdded = true;
+ }
+ break;
+ default:
+ ; // nothing
+ }
+
+ if (!bTokenAdded)
+ pNew->AddToken(*pToken);
+ }
+
+ rName = pRangeData->GetName(); // Get the correctly-cased name.
+ return pNew;
+}
+
+ScDocument* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId)
+{
+ const OUString* pFileName = getExternalFileName(nFileId);
+ if (!pFileName)
+ return nullptr;
+
+ // Do not load document until it was allowed.
+ if (!isLinkUpdateAllowedInDoc(mrDoc))
+ return nullptr;
+
+ ScDocument* pSrcDoc = nullptr;
+ ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
+ while (pShell)
+ {
+ SfxMedium* pMedium = pShell->GetMedium();
+ if (pMedium && !pMedium->GetName().isEmpty())
+ {
+ // TODO: We should make the case sensitivity platform dependent.
+ if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
+ {
+ // Found !
+ pSrcDoc = &pShell->GetDocument();
+ break;
+ }
+ }
+ else
+ {
+ // handle unsaved documents here
+ OUString aName = pShell->GetName();
+ if (pFileName->equalsIgnoreAsciiCase(aName))
+ {
+ // Found !
+ SrcShell aSrcDoc;
+ aSrcDoc.maShell = pShell;
+ maUnsavedDocShells.emplace(nFileId, aSrcDoc);
+ StartListening(*pShell);
+ pSrcDoc = &pShell->GetDocument();
+ break;
+ }
+ }
+ pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
+ }
+
+ initDocInCache(maRefCache, pSrcDoc, nFileId);
+ return pSrcDoc;
+}
+
+ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId)
+{
+ if (!mrDoc.IsExecuteLinkEnabled())
+ return nullptr;
+
+ DocShellMap::iterator itrEnd = maDocShells.end();
+ DocShellMap::iterator itr = maDocShells.find(nFileId);
+
+ if (itr != itrEnd)
+ {
+ // document already loaded.
+
+ SfxObjectShell* p = itr->second.maShell.get();
+ itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
+ return &static_cast<ScDocShell*>(p)->GetDocument();
+ }
+
+ itrEnd = maUnsavedDocShells.end();
+ itr = maUnsavedDocShells.find(nFileId);
+ if (itr != itrEnd)
+ {
+ //document is unsaved document
+
+ SfxObjectShell* p = itr->second.maShell.get();
+ itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
+ return &static_cast<ScDocShell*>(p)->GetDocument();
+ }
+
+ const OUString* pFile = getExternalFileName(nFileId);
+ if (!pFile)
+ // no file name associated with this ID.
+ return nullptr;
+
+ SrcShell aSrcDoc;
+ try
+ {
+ OUString aFilter;
+ aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ if (!aSrcDoc.maShell.is())
+ {
+ // source document could not be loaded.
+ return nullptr;
+ }
+
+ return &cacheNewDocShell(nFileId, aSrcDoc);
+}
+
+SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
+{
+ // Do not load document until it was allowed.
+ if (!isLinkUpdateAllowedInDoc(mrDoc))
+ return nullptr;
+
+ const SrcFileData* pFileData = getExternalFileData(nFileId);
+ if (!pFileData)
+ return nullptr;
+
+ // Always load the document by using the path created from the relative
+ // path. If the referenced document is not there, simply exit. The
+ // original file name should be used only when the relative path is not
+ // given.
+ OUString aFile = pFileData->maFileName;
+ maybeCreateRealFileName(nFileId);
+ if (!pFileData->maRealFileName.isEmpty())
+ aFile = pFileData->maRealFileName;
+
+ if (!isFileLoadable(aFile))
+ return nullptr;
+
+ OUString aOptions = pFileData->maFilterOptions;
+ if ( !pFileData->maFilterName.isEmpty() )
+ rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
+ else
+ ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
+ std::shared_ptr<const SfxFilter> pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
+
+ if (pFileData->maRelativeName.isEmpty())
+ {
+ // Generate a relative file path.
+ INetURLObject aBaseURL(getOwnDocumentName());
+ aBaseURL.insertName(u"content.xml");
+
+ OUString aStr = URIHelper::simpleNormalizedMakeRelative(
+ aBaseURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), aFile);
+
+ setRelativeFileName(nFileId, aStr);
+ }
+
+ std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet(SfxGetpApp()->GetPool()));
+ if (!aOptions.isEmpty())
+ pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
+
+ // make medium hidden to prevent assertion from progress bar
+ pSet->Put( SfxBoolItem(SID_HIDDEN, true) );
+
+ // If the current document is allowed to execute macros then the referenced
+ // document may execute macros according to the security configuration.
+ // Similar for UpdateDocMode to update links, just that if we reach here
+ // the user already allowed updates and intermediate documents are expected
+ // to update as well. When loading the document ScDocShell::Load() will
+ // check through ScDocShell::GetLinkUpdateModeState() if its location is
+ // trusted.
+ ScDocShell* pShell = mrDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SfxMedium* pMedium = pShell->GetMedium();
+ if (pMedium)
+ {
+ const SfxUInt16Item* pItem = pMedium->GetItemSet().GetItemIfSet( SID_MACROEXECMODE, false );
+ if (pItem &&
+ pItem->GetValue() != css::document::MacroExecMode::NEVER_EXECUTE)
+ pSet->Put( SfxUInt16Item( SID_MACROEXECMODE, css::document::MacroExecMode::USE_CONFIG));
+ }
+
+ pSet->Put( SfxUInt16Item( SID_UPDATEDOCMODE, css::document::UpdateDocMode::FULL_UPDATE));
+ }
+
+ unique_ptr<SfxMedium> pMedium(new SfxMedium(aFile, StreamMode::STD_READ, pFilter, std::move(pSet)));
+ if (pMedium->GetErrorIgnoreWarning() != ERRCODE_NONE)
+ return nullptr;
+
+ // To load encrypted documents with password, user interaction needs to be enabled.
+ pMedium->UseInteractionHandler(mbUserInteractionEnabled);
+
+ ScDocShell* pNewShell = new ScDocShell(SfxModelFlags::EXTERNAL_LINK);
+ SfxObjectShellRef aRef = pNewShell;
+
+ // increment the recursive link count of the source document.
+ ScExtDocOptions* pExtOpt = mrDoc.GetExtDocOptions();
+ sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
+ ScDocument& rSrcDoc = pNewShell->GetDocument();
+ rSrcDoc.EnableExecuteLink(false); // to prevent circular access of external references.
+ rSrcDoc.EnableUndo(false);
+ rSrcDoc.LockAdjustHeight();
+ rSrcDoc.EnableUserInteraction(false);
+
+ ScExtDocOptions* pExtOptNew = rSrcDoc.GetExtDocOptions();
+ if (!pExtOptNew)
+ {
+ rSrcDoc.SetExtDocOptions(std::make_unique<ScExtDocOptions>());
+ pExtOptNew = rSrcDoc.GetExtDocOptions();
+ }
+ pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
+
+ if (!pNewShell->DoLoad(pMedium.release()))
+ {
+ aRef->DoClose();
+ aRef.clear();
+ return aRef;
+ }
+
+ // with UseInteractionHandler, options may be set by dialog during DoLoad
+ OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
+ if (!aNew.isEmpty() && aNew != aOptions)
+ aOptions = aNew;
+ setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
+
+ return aRef;
+}
+
+ScDocument& ScExternalRefManager::cacheNewDocShell( sal_uInt16 nFileId, SrcShell& rSrcShell )
+{
+ if (mbDocTimerEnabled && maDocShells.empty())
+ // If this is the first source document insertion, start up the timer.
+ maSrcDocTimer.Start();
+
+ maDocShells.emplace(nFileId, rSrcShell);
+ SfxObjectShell& rShell = *rSrcShell.maShell;
+ ScDocument& rSrcDoc = static_cast<ScDocShell&>(rShell).GetDocument();
+ initDocInCache(maRefCache, &rSrcDoc, nFileId);
+ return rSrcDoc;
+}
+
+bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
+{
+ if (rFile.isEmpty())
+ return false;
+
+ if (isOwnDocument(rFile))
+ return false;
+ OUString aPhysical;
+ if (osl::FileBase::getSystemPathFromFileURL(rFile, aPhysical)
+ == osl::FileBase::E_None)
+ {
+ // #i114504# try IsFolder/Exists only for file URLs
+
+ if (utl::UCBContentHelper::IsFolder(rFile))
+ return false;
+
+ return utl::UCBContentHelper::Exists(rFile);
+ }
+ else
+ return true; // for http and others, Exists doesn't work, but the URL can still be opened
+}
+
+void ScExternalRefManager::maybeLinkExternalFile( sal_uInt16 nFileId, bool bDeferFilterDetection )
+{
+ if (maLinkedDocs.count(nFileId))
+ // file already linked, or the link has been broken.
+ return;
+
+ // Source document not linked yet. Link it now.
+ const OUString* pFileName = getExternalFileName(nFileId);
+ if (!pFileName)
+ return;
+
+ OUString aFilter, aOptions;
+ const SrcFileData* pFileData = getExternalFileData(nFileId);
+ if (pFileData)
+ {
+ aFilter = pFileData->maFilterName;
+ aOptions = pFileData->maFilterOptions;
+ }
+
+ // Filter detection may access external links; defer it until we are allowed.
+ if (!bDeferFilterDetection)
+ bDeferFilterDetection = !isLinkUpdateAllowedInDoc(mrDoc);
+
+ // If a filter was already set (for example, loading the cached table),
+ // don't call GetFilterName which has to access the source file.
+ // If filter detection is deferred, the next successful loadSrcDocument()
+ // will update SrcFileData filter name.
+ if (aFilter.isEmpty() && !bDeferFilterDetection)
+ ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
+ sfx2::LinkManager* pLinkMgr = mrDoc.GetLinkManager();
+ if (!pLinkMgr)
+ {
+ SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
+ return;
+ }
+ ScExternalRefLink* pLink = new ScExternalRefLink(mrDoc, nFileId);
+ OSL_ENSURE(pFileName, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
+ pLinkMgr->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile, *pFileName,
+ (aFilter.isEmpty() && bDeferFilterDetection ? nullptr : &aFilter));
+
+ pLink->SetDoRefresh(false);
+ pLink->Update();
+ pLink->SetDoRefresh(true);
+
+ maLinkedDocs.emplace(nFileId, true);
+}
+
+void ScExternalRefManager::addFilesToLinkManager()
+{
+ if (maSrcFiles.empty())
+ return;
+
+ SAL_WARN_IF( maSrcFiles.size() >= SAL_MAX_UINT16,
+ "sc.ui", "ScExternalRefManager::addFilesToLinkManager: files overflow");
+ const sal_uInt16 nSize = static_cast<sal_uInt16>( std::min<size_t>( maSrcFiles.size(), SAL_MAX_UINT16));
+ for (sal_uInt16 nFileId = 0; nFileId < nSize; ++nFileId)
+ maybeLinkExternalFile( nFileId, true);
+}
+
+void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(std::u16string_view rOwnDocName)
+{
+ if (maRelativeName.isEmpty())
+ // No relative path given. Nothing to do.
+ return;
+
+ if (!maRealFileName.isEmpty())
+ // Real file name already created. Nothing to do.
+ return;
+
+ // Formulate the absolute file path from the relative path.
+ const OUString& rRelPath = maRelativeName;
+ INetURLObject aBaseURL(rOwnDocName);
+ aBaseURL.insertName(u"content.xml");
+ bool bWasAbs = false;
+ maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::DecodeMechanism::NONE);
+}
+
+void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId)
+{
+ if (nFileId >= maSrcFiles.size())
+ return;
+
+ maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
+}
+
+OUString ScExternalRefManager::getOwnDocumentName() const
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return "file:///tmp/document";
+
+ ScDocShell* pShell = mrDoc.GetDocumentShell();
+ if (!pShell)
+ // This should not happen!
+ return OUString();
+
+ SfxMedium* pMed = pShell->GetMedium();
+ if (!pMed)
+ return OUString();
+
+ return pMed->GetName();
+}
+
+bool ScExternalRefManager::isOwnDocument(std::u16string_view rFile) const
+{
+ return getOwnDocumentName() == rFile;
+}
+
+void ScExternalRefManager::convertToAbsName(OUString& rFile) const
+{
+ // unsaved documents have no AbsName
+ ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
+ while (pShell)
+ {
+ if (rFile == pShell->GetName())
+ return;
+
+ pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
+ }
+
+ ScDocShell* pDocShell = mrDoc.GetDocumentShell();
+ rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
+}
+
+sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
+{
+ vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
+ vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
+ if (itr != itrEnd)
+ {
+ size_t nId = distance(itrBeg, itr);
+ return static_cast<sal_uInt16>(nId);
+ }
+
+ SrcFileData aData;
+ aData.maFileName = rFile;
+ maSrcFiles.push_back(aData);
+ return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
+}
+
+const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
+{
+ if (nFileId >= maSrcFiles.size())
+ return nullptr;
+
+ if (bForceOriginal)
+ return &maSrcFiles[nFileId].maFileName;
+
+ maybeCreateRealFileName(nFileId);
+
+ if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
+ return &maSrcFiles[nFileId].maRealFileName;
+
+ return &maSrcFiles[nFileId].maFileName;
+}
+
+sal_uInt16 ScExternalRefManager::convertFileIdToUsedFileId(sal_uInt16 nFileId)
+{
+ if (!mbSkipUnusedFileIds)
+ return nFileId;
+ else
+ return maConvertFileIdToUsedFileId[nFileId];
+}
+
+void ScExternalRefManager::setSkipUnusedFileIds(std::vector<sal_uInt16>& rExternFileIds)
+{
+ mbSkipUnusedFileIds = true;
+ maConvertFileIdToUsedFileId.resize(maSrcFiles.size());
+ std::fill(maConvertFileIdToUsedFileId.begin(), maConvertFileIdToUsedFileId.end(), 0);
+ int nUsedCount = 0;
+ for (auto nEntry : rExternFileIds)
+ {
+ maConvertFileIdToUsedFileId[nEntry] = nUsedCount++;
+ }
+}
+
+void ScExternalRefManager::disableSkipUnusedFileIds()
+{
+ mbSkipUnusedFileIds = false;
+}
+
+std::vector<OUString> ScExternalRefManager::getAllCachedExternalFileNames() const
+{
+ std::vector<OUString> aNames;
+ aNames.reserve(maSrcFiles.size());
+ for (const SrcFileData& rData : maSrcFiles)
+ {
+ aNames.push_back(rData.maFileName);
+ }
+
+ return aNames;
+}
+
+bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
+{
+ return nFileId < maSrcFiles.size();
+}
+
+bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
+{
+ return ::std::any_of(maSrcFiles.begin(), maSrcFiles.end(), FindSrcFileByName(rFile));
+}
+
+const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const
+{
+ if (nFileId >= maSrcFiles.size())
+ return nullptr;
+
+ return &maSrcFiles[nFileId];
+}
+
+const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
+{
+ return maRefCache.getRealTableName(nFileId, rTabName);
+}
+
+const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
+{
+ return maRefCache.getRealRangeName(nFileId, rRangeName);
+}
+
+template<typename MapContainer>
+static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
+{
+ typename MapContainer::iterator itr = rMap.find(nFileId);
+ if (itr != rMap.end())
+ {
+ // Close this document shell.
+ itr->second.maShell->DoClose();
+ rMap.erase(itr);
+ }
+}
+
+void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
+{
+ maRefCache.clearCache(nFileId);
+}
+
+namespace {
+
+class RefCacheFiller : public sc::ColumnSpanSet::ColumnAction
+{
+ svl::SharedStringPool& mrStrPool;
+
+ ScExternalRefCache& mrRefCache;
+ ScExternalRefCache::TableTypeRef mpRefTab;
+ sal_uInt16 mnFileId;
+ ScColumn* mpCurCol;
+ sc::ColumnBlockConstPosition maBlockPos;
+
+public:
+ RefCacheFiller( svl::SharedStringPool& rStrPool, ScExternalRefCache& rRefCache, sal_uInt16 nFileId ) :
+ mrStrPool(rStrPool), mrRefCache(rRefCache), mnFileId(nFileId), mpCurCol(nullptr) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mpCurCol = pCol;
+ if (!mpCurCol)
+ return;
+
+ mpCurCol->InitBlockPosition(maBlockPos);
+ mpRefTab = mrRefCache.getCacheTable(mnFileId, mpCurCol->GetTab());
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ if (!mpCurCol || !bVal)
+ return;
+
+ if (!mpRefTab)
+ return;
+
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ ScExternalRefCache::TokenRef pTok;
+ ScRefCellValue aCell = mpCurCol->GetCellValue(maBlockPos, nRow);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ OUString aStr = aCell.getString(&mpCurCol->GetDoc());
+ svl::SharedString aSS = mrStrPool.intern(aStr);
+ pTok.reset(new formula::FormulaStringToken(std::move(aSS)));
+ }
+ break;
+ case CELLTYPE_VALUE:
+ pTok.reset(new formula::FormulaDoubleToken(aCell.getDouble()));
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ sc::FormulaResultValue aRes = aCell.getFormula()->GetResult();
+ switch (aRes.meType)
+ {
+ case sc::FormulaResultValue::Value:
+ pTok.reset(new formula::FormulaDoubleToken(aRes.mfValue));
+ break;
+ case sc::FormulaResultValue::String:
+ {
+ // Re-intern the string to the host document pool.
+ svl::SharedString aInterned = mrStrPool.intern(aRes.maString.getString());
+ pTok.reset(new formula::FormulaStringToken(std::move(aInterned)));
+ }
+ break;
+ case sc::FormulaResultValue::Error:
+ case sc::FormulaResultValue::Invalid:
+ default:
+ pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
+ }
+ }
+ break;
+ default:
+ pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
+ }
+
+ if (pTok)
+ {
+ // Cache this cell.
+ mpRefTab->setCell(mpCurCol->GetCol(), nRow, pTok, mpCurCol->GetNumberFormat(mpCurCol->GetDoc().GetNonThreadedContext(), nRow));
+ mpRefTab->setCachedCell(mpCurCol->GetCol(), nRow);
+ }
+ }
+ };
+};
+
+}
+
+bool ScExternalRefManager::refreshSrcDocument(sal_uInt16 nFileId)
+{
+ SfxObjectShellRef xDocShell;
+ try
+ {
+ OUString aFilter;
+ xDocShell = loadSrcDocument(nFileId, aFilter);
+ }
+ catch ( const css::uno::Exception& ) {}
+
+ if (!xDocShell.is())
+ // Failed to load the document. Bail out.
+ return false;
+
+ ScDocShell& rDocSh = static_cast<ScDocShell&>(*xDocShell);
+ ScDocument& rSrcDoc = rDocSh.GetDocument();
+
+ sc::ColumnSpanSet aCachedArea;
+ maRefCache.getAllCachedDataSpans(rSrcDoc, nFileId, aCachedArea);
+
+ // Clear the existing cache, and refill it. Make sure we keep the
+ // existing cache table instances here.
+ maRefCache.clearCacheTables(nFileId);
+ RefCacheFiller aAction(mrDoc.GetSharedStringPool(), maRefCache, nFileId);
+ aCachedArea.executeColumnAction(rSrcDoc, aAction);
+
+ DocShellMap::iterator it = maDocShells.find(nFileId);
+ if (it != maDocShells.end())
+ {
+ it->second.maShell->DoClose();
+ it->second.maShell = xDocShell;
+ it->second.maLastAccess = tools::Time(tools::Time::SYSTEM);
+ }
+ else
+ {
+ SrcShell aSrcDoc;
+ aSrcDoc.maShell = xDocShell;
+ aSrcDoc.maLastAccess = tools::Time(tools::Time::SYSTEM);
+ cacheNewDocShell(nFileId, aSrcDoc);
+ }
+
+ // Update all cells containing names from this source document.
+ refreshAllRefCells(nFileId);
+
+ notifyAllLinkListeners(nFileId, LINK_MODIFIED);
+
+ return true;
+}
+
+void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
+{
+ // Turn all formula cells referencing this external document into static
+ // cells.
+ RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
+ if (itrRefs != maRefCells.end())
+ {
+ // Make a copy because removing the formula cells below will modify
+ // the original container.
+ RefCellSet aSet = itrRefs->second;
+ for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(&mrDoc));
+ maRefCells.erase(nFileId);
+ }
+
+ // Remove all named ranges that reference this document.
+
+ // Global named ranges.
+ ScRangeName* pRanges = mrDoc.GetRangeName();
+ if (pRanges)
+ removeRangeNamesBySrcDoc(*pRanges, nFileId);
+
+ // Sheet-local named ranges.
+ for (SCTAB i = 0, n = mrDoc.GetTableCount(); i < n; ++i)
+ {
+ pRanges = mrDoc.GetRangeName(i);
+ if (pRanges)
+ removeRangeNamesBySrcDoc(*pRanges, nFileId);
+ }
+
+ clearCache(nFileId);
+ lcl_removeByFileId(nFileId, maDocShells);
+
+ if (maDocShells.empty())
+ maSrcDocTimer.Stop();
+
+ LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
+ if (itr != maLinkedDocs.end())
+ itr->second = false;
+
+ notifyAllLinkListeners(nFileId, LINK_BROKEN);
+}
+
+void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
+{
+ maSrcFiles[nFileId].maFileName = rNewFile;
+ maSrcFiles[nFileId].maRelativeName.clear();
+ maSrcFiles[nFileId].maRealFileName.clear();
+ if (maSrcFiles[nFileId].maFilterName != rNewFilter)
+ {
+ // Filter type has changed.
+ maSrcFiles[nFileId].maFilterName = rNewFilter;
+ maSrcFiles[nFileId].maFilterOptions.clear();
+ }
+ refreshSrcDocument(nFileId);
+}
+
+void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
+{
+ if (nFileId >= maSrcFiles.size())
+ return;
+ maSrcFiles[nFileId].maRelativeName = rRelUrl;
+}
+
+void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
+{
+ if (nFileId >= maSrcFiles.size())
+ return;
+ maSrcFiles[nFileId].maFilterName = rFilterName;
+ maSrcFiles[nFileId].maFilterOptions = rOptions;
+}
+
+void ScExternalRefManager::clear()
+{
+ for (auto& rEntry : maLinkListeners)
+ {
+ for (auto& it : rEntry.second)
+ {
+ it->notify(0, OH_NO_WE_ARE_GOING_TO_DIE);
+ }
+ }
+
+ for (auto& rEntry : maDocShells)
+ rEntry.second.maShell->DoClose();
+
+ maDocShells.clear();
+ maSrcDocTimer.Stop();
+}
+
+bool ScExternalRefManager::hasExternalData() const
+{
+ return !maSrcFiles.empty();
+}
+
+void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
+{
+ for (auto& rSrcFile : maSrcFiles)
+ {
+ // Re-generate relative file name from the absolute file name.
+ OUString aAbsName = rSrcFile.maRealFileName;
+ if (aAbsName.isEmpty())
+ aAbsName = rSrcFile.maFileName;
+
+ rSrcFile.maRelativeName = URIHelper::simpleNormalizedMakeRelative(
+ rBaseFileUrl, aAbsName);
+ }
+}
+
+void ScExternalRefManager::updateAbsAfterLoad()
+{
+ OUString aOwn( getOwnDocumentName() );
+ for (auto& rSrcFile : maSrcFiles)
+ {
+ // update maFileName to the real file name,
+ // to be called when the original name is no longer needed (after CompileXML)
+
+ rSrcFile.maybeCreateRealFileName( aOwn );
+ OUString aReal = rSrcFile.maRealFileName;
+ if (!aReal.isEmpty())
+ rSrcFile.maFileName = aReal;
+ }
+}
+
+void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell)
+{
+ for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
+}
+
+void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
+ if (itr == maLinkListeners.end())
+ {
+ pair<LinkListenerMap::iterator, bool> r = maLinkListeners.emplace(
+ nFileId, LinkListeners());
+ if (!r.second)
+ {
+ OSL_FAIL("insertion of new link listener list failed");
+ return;
+ }
+
+ itr = r.first;
+ }
+
+ LinkListeners& rList = itr->second;
+ rList.insert(pListener);
+}
+
+void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
+ if (itr == maLinkListeners.end())
+ // no listeners for a specified file.
+ return;
+
+ LinkListeners& rList = itr->second;
+ rList.erase(pListener);
+
+ if (rList.empty())
+ // No more listeners for this file. Remove its entry.
+ maLinkListeners.erase(itr);
+}
+
+void ScExternalRefManager::removeLinkListener(LinkListener* pListener)
+{
+ for (auto& rEntry : maLinkListeners)
+ rEntry.second.erase(pListener);
+}
+
+void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
+{
+ LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
+ if (itr == maLinkListeners.end())
+ // no listeners for a specified file.
+ return;
+
+ LinkListeners& rList = itr->second;
+ for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
+}
+
+void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut)
+{
+ // To avoid potentially freezing Calc, we close one stale document at a time.
+ DocShellMap::iterator itr = std::find_if(maDocShells.begin(), maDocShells.end(),
+ [nTimeOut](const DocShellMap::value_type& rEntry) {
+ // in 100th of a second.
+ sal_Int32 nSinceLastAccess = (tools::Time( tools::Time::SYSTEM ) - rEntry.second.maLastAccess).GetTime();
+ return nSinceLastAccess >= nTimeOut;
+ });
+ if (itr != maDocShells.end())
+ {
+ // Timed out. Let's close this.
+ itr->second.maShell->DoClose();
+ maDocShells.erase(itr);
+ }
+
+ if (maDocShells.empty())
+ maSrcDocTimer.Stop();
+}
+
+sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument& rSrcDoc)
+{
+ NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
+ if (itr == maNumFormatMap.end())
+ {
+ // Number formatter map is not initialized for this external document.
+ pair<NumFmtMap::iterator, bool> r = maNumFormatMap.emplace(
+ nFileId, SvNumberFormatterMergeMap());
+
+ if (!r.second)
+ // insertion failed.
+ return nNumFmt;
+
+ itr = r.first;
+ mrDoc.GetFormatTable()->MergeFormatter(*rSrcDoc.GetFormatTable());
+ SvNumberFormatterMergeMap aMap = mrDoc.GetFormatTable()->ConvertMergeTableToMap();
+ itr->second.swap(aMap);
+ }
+ const SvNumberFormatterMergeMap& rMap = itr->second;
+ SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
+ if (itrNumFmt != rMap.end())
+ // mapped value found.
+ return itrNumFmt->second;
+
+ return nNumFmt;
+}
+
+void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell* pShell )
+{
+ DocShellMap::iterator itr = maUnsavedDocShells.begin();
+ while( itr != maUnsavedDocShells.end() )
+ {
+ if ( itr->second.maShell.get() == pShell )
+ {
+ // found that the shell is marked as unsaved
+ OUString aFileURL = pShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ switchSrcFile(itr->first, aFileURL, OUString());
+ EndListening(*pShell);
+ itr = maUnsavedDocShells.erase(itr);
+ }
+ else
+ ++itr;
+ }
+}
+
+void ScExternalRefManager::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSfxEventHint)
+ return;
+
+ switch (static_cast<const SfxEventHint&>(rHint).GetEventId())
+ {
+ case SfxEventHintId::PrepareCloseDoc:
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_CLOSE_WITH_UNSAVED_REFS)));
+ xWarn->run();
+ }
+ break;
+ case SfxEventHintId::SaveDocDone:
+ case SfxEventHintId::SaveAsDocDone:
+ {
+ SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
+ transformUnsavedRefToSavedRef(pObjShell);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+IMPL_LINK(ScExternalRefManager, TimeOutHdl, Timer*, pTimer, void)
+{
+ if (pTimer == &maSrcDocTimer)
+ purgeStaleSrcDocument(SRCDOC_LIFE_SPAN);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/impex.cxx b/sc/source/ui/docshell/impex.cxx
new file mode 100644
index 0000000000..4a585657de
--- /dev/null
+++ b/sc/source/ui/docshell/impex.cxx
@@ -0,0 +1,2894 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <i18nlangtag/languagetag.hxx>
+#include <i18nutil/unicode.hxx>
+#include <sot/formats.hxx>
+#include <sfx2/mieclip.hxx>
+#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/module.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <global.hxx>
+#include <docsh.hxx>
+#include <undoblk.hxx>
+#include <rangenam.hxx>
+#include <tabvwsh.hxx>
+#include <filter.hxx>
+#include <asciiopt.hxx>
+#include <formulacell.hxx>
+#include <cellform.hxx>
+#include <progress.hxx>
+#include <scitems.hxx>
+#include <editable.hxx>
+#include <compiler.hxx>
+#include <warnbox.hxx>
+#include <clipparam.hxx>
+#include <impex.hxx>
+#include <editutil.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <stringutil.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <documentimport.hxx>
+#include <refundo.hxx>
+#include <mtvelements.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/svlibrary.h>
+#include <unotools/configmgr.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <editeng/editobj.hxx>
+#include <svl/numformat.hxx>
+#include <rtl/character.hxx>
+#include <rtl/math.hxx>
+#include <sax/tools/converter.hxx>
+
+#include <memory>
+#include <string_view>
+
+#include <unicode/uchar.h>
+
+#include <osl/endian.h>
+
+// We don't want to end up with 2GB read in one line just because of malformed
+// multiline fields, so chop it _somewhere_, which is twice supported columns
+// times arbitrary maximum cell content length, 2*1024*64K=128M, and because
+// it's sal_Unicode that's 256MB. If it's 2GB of data without LF we're out of
+// luck anyway.
+constexpr sal_Int32 nArbitraryCellLengthLimit = SAL_MAX_UINT16;
+constexpr sal_Int32 nArbitraryLineLengthLimit = 2 * MAXCOLCOUNT * nArbitraryCellLengthLimit;
+
+namespace
+{
+ const char SYLK_LF[] = "\x1b :";
+
+ bool lcl_IsEndianSwap( const SvStream& rStrm )
+ {
+ #ifdef OSL_BIGENDIAN
+ return rStrm.GetEndian() != SvStreamEndian::BIG;
+ #else
+ return rStrm.GetEndian() != SvStreamEndian::LITTLE;
+ #endif
+ }
+}
+
+namespace {
+
+enum class SylkVersion
+{
+ SCALC3, // Wrote wrongly quoted strings and unescaped semicolons.
+ OOO32, // Correct strings, plus multiline content.
+ OWN, // Place our new versions, if any, before this value.
+ OTHER // Assume that aliens wrote correct strings.
+};
+
+}
+
+// Whole document without Undo
+ScImportExport::ScImportExport( ScDocument& r )
+ : pDocSh( r.GetDocumentShell() ), rDoc( r ),
+ nSizeLimit( 0 ), nMaxImportRow(!utl::ConfigManager::IsFuzzing() ? rDoc.MaxRow() : SCROWS32K),
+ cSep( '\t' ), cStr( '"' ),
+ bFormulas( false ), bIncludeFiltered( true ),
+ bAll( true ), bSingle( true ), bUndo( false ),
+ bOverflowRow( false ), bOverflowCol( false ), bOverflowCell( false ),
+ mbApi( true ), mbImportBroadcast(false), mbOverwriting( false ), mbIncludeBOM(false)
+{
+ pUndoDoc = nullptr;
+ pExtOptions = nullptr;
+}
+
+// Insert am current cell without range(es)
+ScImportExport::ScImportExport( ScDocument& r, const ScAddress& rPt )
+ : pDocSh( r.GetDocumentShell() ), rDoc( r ),
+ aRange( rPt ),
+ nSizeLimit( 0 ), nMaxImportRow(!utl::ConfigManager::IsFuzzing() ? rDoc.MaxRow() : SCROWS32K),
+ cSep( '\t' ), cStr( '"' ),
+ bFormulas( false ), bIncludeFiltered( true ),
+ bAll( false ), bSingle( true ), bUndo( pDocSh != nullptr ),
+ bOverflowRow( false ), bOverflowCol( false ), bOverflowCell( false ),
+ mbApi( true ), mbImportBroadcast(false), mbOverwriting( false ), mbIncludeBOM(false)
+{
+ pUndoDoc = nullptr;
+ pExtOptions = nullptr;
+}
+
+// ctor with a range is only used for export
+//! ctor with a string (and bSingle=true) is also used for DdeSetData
+ScImportExport::ScImportExport( ScDocument& r, const ScRange& rRange )
+ : pDocSh( r.GetDocumentShell() ), rDoc( r ),
+ aRange( rRange ),
+ nSizeLimit( 0 ), nMaxImportRow(!utl::ConfigManager::IsFuzzing() ? rDoc.MaxRow() : SCROWS32K),
+ cSep( '\t' ), cStr( '"' ),
+ bFormulas( false ), bIncludeFiltered( true ),
+ bAll( false ), bSingle( false ), bUndo( pDocSh != nullptr ),
+ bOverflowRow( false ), bOverflowCol( false ), bOverflowCell( false ),
+ mbApi( true ), mbImportBroadcast(false), mbOverwriting( false ), mbIncludeBOM(false)
+{
+ pUndoDoc = nullptr;
+ pExtOptions = nullptr;
+ // Only one sheet (table) supported
+ aRange.aEnd.SetTab( aRange.aStart.Tab() );
+}
+
+// Evaluate input string - either range, cell or the whole document (when error)
+// If a View exists, the TabNo of the view will be used.
+ScImportExport::ScImportExport( ScDocument& r, const OUString& rPos )
+ : pDocSh( r.GetDocumentShell() ), rDoc( r ),
+ nSizeLimit( 0 ), nMaxImportRow(!utl::ConfigManager::IsFuzzing() ? rDoc.MaxRow() : SCROWS32K),
+ cSep( '\t' ), cStr( '"' ),
+ bFormulas( false ), bIncludeFiltered( true ),
+ bAll( false ), bSingle( true ), bUndo( pDocSh != nullptr ),
+ bOverflowRow( false ), bOverflowCol( false ), bOverflowCell( false ),
+ mbApi( true ), mbImportBroadcast(false), mbOverwriting( false ), mbIncludeBOM(false)
+{
+ pUndoDoc = nullptr;
+ pExtOptions = nullptr;
+
+ SCTAB nTab = ScDocShell::GetCurTab();
+ aRange.aStart.SetTab( nTab );
+ OUString aPos( rPos );
+ // Named range?
+ ScRangeName* pRange = rDoc.GetRangeName();
+ if (pRange)
+ {
+ const ScRangeData* pData = pRange->findByUpperName(ScGlobal::getCharClass().uppercase(aPos));
+ if (pData)
+ {
+ if( pData->HasType( ScRangeData::Type::RefArea )
+ || pData->HasType( ScRangeData::Type::AbsArea )
+ || pData->HasType( ScRangeData::Type::AbsPos ) )
+ {
+ aPos = pData->GetSymbol();
+ }
+ }
+ }
+ formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ // Range?
+ if (aRange.Parse(aPos, rDoc, eConv) & ScRefFlags::VALID)
+ bSingle = false;
+ // Cell?
+ else if (aRange.aStart.Parse(aPos, rDoc, eConv) & ScRefFlags::VALID)
+ aRange.aEnd = aRange.aStart;
+ else
+ bAll = true;
+}
+
+ScImportExport::~ScImportExport() COVERITY_NOEXCEPT_FALSE
+{
+ pUndoDoc.reset();
+ pExtOptions.reset();
+}
+
+void ScImportExport::SetExtOptions( const ScAsciiOptions& rOpt )
+{
+ if ( pExtOptions )
+ *pExtOptions = rOpt;
+ else
+ pExtOptions.reset(new ScAsciiOptions( rOpt ));
+
+ // "normal" Options
+
+ cSep = ScAsciiOptions::GetWeightedFieldSep( rOpt.GetFieldSeps(), false);
+ cStr = rOpt.GetTextSep();
+}
+
+void ScImportExport::SetFilterOptions(const OUString& rFilterOptions)
+{
+ maFilterOptions = rFilterOptions;
+}
+
+bool ScImportExport::IsFormatSupported( SotClipboardFormatId nFormat )
+{
+ return nFormat == SotClipboardFormatId::STRING
+ || nFormat == SotClipboardFormatId::STRING_TSVC
+ || nFormat == SotClipboardFormatId::SYLK
+ || nFormat == SotClipboardFormatId::LINK
+ || nFormat == SotClipboardFormatId::HTML
+ || nFormat == SotClipboardFormatId::HTML_SIMPLE
+ || nFormat == SotClipboardFormatId::DIF;
+}
+
+// Prepare for Undo
+bool ScImportExport::StartPaste()
+{
+ if ( !bAll )
+ {
+ ScEditableTester aTester( rDoc, aRange );
+ if ( !aTester.IsEditable() )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(aTester.GetMessageId())));
+ xInfoBox->run();
+ return false;
+ }
+ }
+ if( bUndo && pDocSh && rDoc.IsUndoEnabled())
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, aRange.aStart.Tab(), aRange.aEnd.Tab() );
+ rDoc.CopyToDocument(aRange, InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc);
+ }
+ return true;
+}
+
+// Create Undo/Redo actions, Invalidate/Repaint
+void ScImportExport::EndPaste(bool bAutoRowHeight)
+{
+ bool bHeight = bAutoRowHeight && pDocSh && pDocSh->AdjustRowHeight(
+ aRange.aStart.Row(), aRange.aEnd.Row(), aRange.aStart.Tab() );
+
+ if( pUndoDoc && rDoc.IsUndoEnabled() && pDocSh )
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, aRange.aStart.Tab(), aRange.aEnd.Tab() );
+ rDoc.CopyToDocument(aRange, InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pRedoDoc);
+ ScMarkData aDestMark(pRedoDoc->GetSheetLimits());
+ aDestMark.SetMarkArea(aRange);
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPaste>(pDocSh, aRange, aDestMark, std::move(pUndoDoc), std::move(pRedoDoc), InsertDeleteFlags::ALL, nullptr));
+ }
+ pUndoDoc.reset();
+ if( pDocSh )
+ {
+ if (!bHeight)
+ pDocSh->PostPaint( aRange, PaintPartFlags::Grid );
+ pDocSh->SetDocumentModified();
+ }
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ if ( pViewSh )
+ pViewSh->UpdateInputHandler();
+
+}
+
+bool ScImportExport::ExportData( std::u16string_view rMimeType,
+ css::uno::Any & rValue )
+{
+ SvMemoryStream aStrm;
+ SotClipboardFormatId fmtId = SotExchange::GetFormatIdFromMimeType(rMimeType);
+ if (fmtId == SotClipboardFormatId::STRING)
+ aStrm.SetStreamCharSet(RTL_TEXTENCODING_UNICODE);
+ // mba: no BaseURL for data exchange
+ if (ExportStream(aStrm, OUString(), fmtId))
+ {
+ if (fmtId == SotClipboardFormatId::STRING)
+ {
+ assert(aStrm.TellEnd() % sizeof(sal_Unicode) == 0);
+ rValue <<= OUString(static_cast<const sal_Unicode*>(aStrm.GetData()),
+ aStrm.TellEnd() / sizeof(sal_Unicode));
+ }
+ else
+ {
+ aStrm.WriteUChar(0);
+ rValue <<= css::uno::Sequence<sal_Int8>(static_cast<sal_Int8 const*>(aStrm.GetData()),
+ aStrm.TellEnd());
+ }
+ return true;
+ }
+ return false;
+}
+
+bool ScImportExport::ImportString( const OUString& rText, SotClipboardFormatId nFmt )
+{
+ switch ( nFmt )
+ {
+ // formats supporting unicode
+ case SotClipboardFormatId::STRING :
+ case SotClipboardFormatId::STRING_TSVC :
+ {
+ ScImportStringStream aStrm( rText);
+ return ImportStream( aStrm, OUString(), nFmt );
+ // ImportStream must handle RTL_TEXTENCODING_UNICODE
+ }
+ default:
+ {
+ rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
+ OString aTmp( rText.getStr(), rText.getLength(), eEnc );
+ SvMemoryStream aStrm( const_cast<char *>(aTmp.getStr()), aTmp.getLength() * sizeof(char), StreamMode::READ );
+ aStrm.SetStreamCharSet( eEnc );
+ SetNoEndianSwap( aStrm ); //! no swapping in memory
+ return ImportStream( aStrm, OUString(), nFmt );
+ }
+ }
+}
+
+bool ScImportExport::ExportString( OUString& rText, SotClipboardFormatId nFmt )
+{
+ if ( nFmt != SotClipboardFormatId::STRING && nFmt != SotClipboardFormatId::STRING_TSVC )
+ {
+ SAL_WARN("sc.ui", "ScImportExport::ExportString: Unicode not supported for other formats than SotClipboardFormatId::STRING[_TSV]");
+ rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
+ OString aTmp;
+ bool bOk = ExportByteString( aTmp, eEnc, nFmt );
+ rText = OStringToOUString( aTmp, eEnc );
+ return bOk;
+ }
+ // nSizeLimit not needed for OUString
+
+ SvMemoryStream aStrm;
+ aStrm.SetStreamCharSet( RTL_TEXTENCODING_UNICODE );
+ SetNoEndianSwap( aStrm ); //! no swapping in memory
+ // mba: no BaseURL for data exc
+ if( ExportStream( aStrm, OUString(), nFmt ) )
+ {
+ aStrm.WriteUInt16( 0 );
+ rText = OUString( static_cast<const sal_Unicode*>(aStrm.GetData()) );
+ return true;
+ }
+ rText.clear();
+ return false;
+
+ // ExportStream must handle RTL_TEXTENCODING_UNICODE
+}
+
+bool ScImportExport::ExportByteString( OString& rText, rtl_TextEncoding eEnc, SotClipboardFormatId nFmt )
+{
+ OSL_ENSURE( eEnc != RTL_TEXTENCODING_UNICODE, "ScImportExport::ExportByteString: Unicode not supported" );
+ if ( eEnc == RTL_TEXTENCODING_UNICODE )
+ eEnc = osl_getThreadTextEncoding();
+
+ if (!nSizeLimit)
+ nSizeLimit = SAL_MAX_UINT16;
+
+ SvMemoryStream aStrm;
+ aStrm.SetStreamCharSet( eEnc );
+ SetNoEndianSwap( aStrm ); //! no swapping in memory
+ // mba: no BaseURL for data exchange
+ if( ExportStream( aStrm, OUString(), nFmt ) )
+ {
+ aStrm.WriteChar( 0 );
+ if( aStrm.TellEnd() <= nSizeLimit )
+ {
+ rText = static_cast<const char*>(aStrm.GetData());
+ return true;
+ }
+ }
+ rText.clear();
+ return false;
+}
+
+bool ScImportExport::ImportStream( SvStream& rStrm, const OUString& rBaseURL, SotClipboardFormatId nFmt )
+{
+ if( nFmt == SotClipboardFormatId::STRING || nFmt == SotClipboardFormatId::STRING_TSVC )
+ {
+ if( ExtText2Doc( rStrm ) ) // evaluate pExtOptions
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::SYLK )
+ {
+ if( Sylk2Doc( rStrm ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::DIF )
+ {
+ if( Dif2Doc( rStrm ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::RTF || nFmt == SotClipboardFormatId::RICHTEXT )
+ {
+ if( RTF2Doc( rStrm, rBaseURL ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::LINK )
+ return true; // Link-Import?
+ if ( nFmt == SotClipboardFormatId::HTML )
+ {
+ if( HTML2Doc( rStrm, rBaseURL ) )
+ return true;
+ }
+ if ( nFmt == SotClipboardFormatId::HTML_SIMPLE )
+ {
+ MSE40HTMLClipFormatObj aMSE40ClpObj; // needed to skip the header data
+ SvStream* pHTML = aMSE40ClpObj.IsValid( rStrm );
+ if ( pHTML && HTML2Doc( *pHTML, rBaseURL ) )
+ return true;
+ }
+
+ return false;
+}
+
+bool ScImportExport::ExportStream( SvStream& rStrm, const OUString& rBaseURL, SotClipboardFormatId nFmt )
+{
+ if( nFmt == SotClipboardFormatId::STRING || nFmt == SotClipboardFormatId::STRING_TSVC )
+ {
+ if( Doc2Text( rStrm ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::SYLK )
+ {
+ if( Doc2Sylk( rStrm ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::DIF )
+ {
+ if( Doc2Dif( rStrm ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::LINK && !bAll )
+ {
+ OUString aDocName;
+ if ( rDoc.IsClipboard() )
+ aDocName = ScGlobal::GetClipDocName();
+ else
+ {
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ aDocName = pShell->GetTitle( SFX_TITLE_FULLNAME );
+ }
+
+ OSL_ENSURE( !aDocName.isEmpty(), "ClipBoard document has no name! :-/" );
+ if( !aDocName.isEmpty() )
+ {
+ // Always use Calc A1 syntax for paste link.
+ OUString aRefName;
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ if( bSingle )
+ aRefName = aRange.aStart.Format(nFlags, &rDoc, formula::FormulaGrammar::CONV_OOO);
+ else
+ {
+ if( aRange.aStart.Tab() != aRange.aEnd.Tab() )
+ nFlags |= ScRefFlags::TAB2_3D;
+ aRefName = aRange.Format(rDoc, nFlags, formula::FormulaGrammar::CONV_OOO);
+ }
+ OUString aAppName = Application::GetAppName();
+
+ // extra bits are used to tell the client to prefer external
+ // reference link.
+
+ WriteUnicodeOrByteString( rStrm, aAppName, true );
+ WriteUnicodeOrByteString( rStrm, aDocName, true );
+ WriteUnicodeOrByteString( rStrm, aRefName, true );
+ WriteUnicodeOrByteString( rStrm, u"calc:extref", true );
+ if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE )
+ rStrm.WriteUInt16( 0 );
+ else
+ rStrm.WriteChar( 0 );
+ return rStrm.GetError() == ERRCODE_NONE;
+ }
+ }
+ if( nFmt == SotClipboardFormatId::HTML )
+ {
+ if( Doc2HTML( rStrm, rBaseURL ) )
+ return true;
+ }
+ if( nFmt == SotClipboardFormatId::RTF || nFmt == SotClipboardFormatId::RICHTEXT )
+ {
+ if( Doc2RTF( rStrm ) )
+ return true;
+ }
+
+ return false;
+}
+
+void ScImportExport::WriteUnicodeOrByteString( SvStream& rStrm, std::u16string_view rString, bool bZero )
+{
+ rtl_TextEncoding eEnc = rStrm.GetStreamCharSet();
+ if ( eEnc == RTL_TEXTENCODING_UNICODE )
+ {
+ if ( !lcl_IsEndianSwap( rStrm ) )
+ rStrm.WriteBytes(rString.data(), rString.size() * sizeof(sal_Unicode));
+ else
+ {
+ const sal_Unicode* p = rString.data();
+ const sal_Unicode* const pStop = p + rString.size();
+ while ( p < pStop )
+ {
+ rStrm.WriteUInt16( *p );
+ }
+ }
+ if ( bZero )
+ rStrm.WriteUInt16( 0 );
+ }
+ else
+ {
+ OString aByteStr(OUStringToOString(rString, eEnc));
+ rStrm.WriteOString( aByteStr );
+ if ( bZero )
+ rStrm.WriteChar( 0 );
+ }
+}
+
+// This function could be replaced by endlub()
+void ScImportExport::WriteUnicodeOrByteEndl( SvStream& rStrm )
+{
+ if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE )
+ { // same as endl() but unicode
+ switch ( rStrm.GetLineDelimiter() )
+ {
+ case LINEEND_CR :
+ rStrm.WriteUInt16( '\r' );
+ break;
+ case LINEEND_LF :
+ rStrm.WriteUInt16( '\n' );
+ break;
+ default:
+ rStrm.WriteUInt16( '\r' ).WriteUInt16( '\n' );
+ }
+ }
+ else
+ endl( rStrm );
+}
+
+// tdf#104927
+// http://www.unicode.org/reports/tr11/
+sal_Int32 ScImportExport::CountVisualWidth(const OUString& rStr, sal_Int32& nIdx, sal_Int32 nMaxWidth)
+{
+ sal_Int32 nWidth = 0;
+ while(nIdx < rStr.getLength() && nWidth < nMaxWidth)
+ {
+ sal_uInt32 nCode = rStr.iterateCodePoints(&nIdx);
+
+ auto nEaWidth = u_getIntPropertyValue(nCode, UCHAR_EAST_ASIAN_WIDTH);
+ if (nEaWidth == U_EA_FULLWIDTH || nEaWidth == U_EA_WIDE)
+ nWidth += 2;
+ else if (!u_getIntPropertyValue(nCode, UCHAR_DEFAULT_IGNORABLE_CODE_POINT))
+ nWidth += 1;
+ }
+
+ if (nIdx < rStr.getLength())
+ {
+ sal_Int32 nTmpIdx = nIdx;
+ sal_uInt32 nCode = rStr.iterateCodePoints(&nTmpIdx);
+
+ if (u_getIntPropertyValue(nCode, UCHAR_DEFAULT_IGNORABLE_CODE_POINT))
+ nIdx = nTmpIdx;
+ }
+ return nWidth;
+}
+
+sal_Int32 ScImportExport::CountVisualWidth(const OUString& rStr)
+{
+ sal_Int32 nIdx = 0;
+ return CountVisualWidth(rStr, nIdx, SAL_MAX_INT32);
+}
+
+void ScImportExport::SetNoEndianSwap( SvStream& rStrm )
+{
+#ifdef OSL_BIGENDIAN
+ rStrm.SetEndian( SvStreamEndian::BIG );
+#else
+ rStrm.SetEndian( SvStreamEndian::LITTLE );
+#endif
+}
+
+static inline bool lcl_isFieldEnd( sal_Unicode c, const sal_Unicode* pSeps )
+{
+ return !c || ScGlobal::UnicodeStrChr( pSeps, c);
+}
+
+namespace {
+
+enum QuoteType
+{
+ FIELDSTART_QUOTE,
+ FIRST_QUOTE,
+ SECOND_QUOTE,
+ FIELDEND_QUOTE,
+ DONTKNOW_QUOTE
+};
+
+}
+
+/** Determine if *p is a quote that ends a quoted field.
+
+ Precondition: we are parsing a quoted field already and *p is a quote.
+
+ @return
+ FIELDEND_QUOTE if end of field quote
+ DONTKNOW_QUOTE anything else
+ */
+static QuoteType lcl_isFieldEndQuote( const sal_Unicode* p, const sal_Unicode* pSeps, sal_Unicode& rcDetectSep )
+{
+ // Due to broken CSV generators that don't double embedded quotes check if
+ // a field separator immediately or with trailing spaces follows the quote,
+ // only then end the field, or at end of string.
+ constexpr sal_Unicode cBlank = ' ';
+ if (p[1] == cBlank && ScGlobal::UnicodeStrChr( pSeps, cBlank))
+ return FIELDEND_QUOTE;
+ // Detect a possible blank separator if it's not already in the list (which
+ // was checked right above for p[1]==cBlank).
+ const bool bBlankSep = (p[1] == cBlank && !rcDetectSep && p[2] && p[2] != cBlank);
+ while (p[1] == cBlank)
+ ++p;
+ if (lcl_isFieldEnd( p[1], pSeps))
+ return FIELDEND_QUOTE;
+ // Extended separator detection after a closing quote (with or without
+ // blanks). Note that nQuotes is incremented *after* the call so is not yet
+ // even here, and that with separator detection we reach here only if
+ // lcl_isEscapedOrFieldEndQuote() did not already detect FIRST_QUOTE or
+ // SECOND_QUOTE for an escaped embedded quote, thus nQuotes does not have
+ // to be checked.
+ if (!rcDetectSep)
+ {
+ constexpr sal_Unicode vSep[] = { ',', '\t', ';' };
+ for (const sal_Unicode c : vSep)
+ {
+ if (p[1] == c)
+ {
+ rcDetectSep = c;
+ return FIELDEND_QUOTE;
+ }
+ }
+ }
+ // Blank separator is least significant, after others.
+ if (bBlankSep)
+ {
+ rcDetectSep = cBlank;
+ return FIELDEND_QUOTE;
+ }
+ return DONTKNOW_QUOTE;
+}
+
+/** Determine if *p is a quote that is escaped by being doubled or ends a
+ quoted field.
+
+ Precondition: *p is a quote.
+
+ @param nQuotes
+ Quote characters encountered so far.
+ Odd (after opening quote) means either no embedded quotes or only quote
+ pairs so far.
+ Even means either not in a quoted field or already one quote
+ encountered, the first of a pair.
+
+ @return
+ FIELDSTART_QUOTE if first quote in a field, either starting content or
+ embedded so caller should check beforehand.
+ FIRST_QUOTE if first of a doubled quote
+ SECOND_QUOTE if second of a doubled quote
+ FIELDEND_QUOTE if end of field quote
+ DONTKNOW_QUOTE if an unescaped quote we don't consider as end of field,
+ do not increment nQuotes in caller then!
+ */
+static QuoteType lcl_isEscapedOrFieldEndQuote( sal_Int32 nQuotes, const sal_Unicode* p,
+ const sal_Unicode* pSeps, sal_Unicode cStr, sal_Unicode& rcDetectSep )
+{
+ if ((nQuotes & 1) == 0)
+ {
+ if (p[-1] == cStr)
+ return SECOND_QUOTE;
+ else
+ {
+ SAL_WARN( "sc", "lcl_isEscapedOrFieldEndQuote: really want a FIELDSTART_QUOTE?");
+ return FIELDSTART_QUOTE;
+ }
+ }
+ if (p[1] == cStr)
+ return FIRST_QUOTE;
+ return lcl_isFieldEndQuote( p, pSeps, rcDetectSep);
+}
+
+/** Append characters of [p1,p2) to rField.
+
+ @returns TRUE if ok; FALSE if data overflow, truncated
+ */
+static bool lcl_appendLineData( OUString& rField, const sal_Unicode* p1, const sal_Unicode* p2 )
+{
+ if (rField.getLength() + (p2 - p1) <= nArbitraryCellLengthLimit)
+ {
+ rField += std::u16string_view( p1, p2 - p1 );
+ return true;
+ }
+ else
+ {
+ SAL_WARN( "sc", "lcl_appendLineData: data overflow");
+ rField += std::u16string_view( p1, nArbitraryCellLengthLimit - rField.getLength() );
+ return false;
+ }
+}
+
+namespace {
+
+enum class DoubledQuoteMode
+{
+ KEEP_ALL, // both are taken, additionally start and end quote are included in string
+ ESCAPE, // escaped quote, one is taken, one ignored
+};
+
+}
+
+/** Scan for a quoted string.
+
+ Precondition: initial current position *p is a cStr quote.
+
+ For DoubledQuoteMode::ESCAPE, if after the closing quote there is a field
+ end (with or without trailing blanks and as determined by
+ lcl_isFieldEndQuote()), then the content is appended to rField with quotes
+ processed and removed. Else if no field end after the quoted string was
+ detected, nothing is appended and processing continues and is repeated
+ until the next quote. If no closing quote at a field end was found at all,
+ nothing is appended and the initial position is returned and caller has to
+ decide, usually just taking all as literal data.
+
+ For DoubledQuoteMode::KEEP_ALL, the string up to and including the closing
+ quote is appended to rField and the next position returned, regardless
+ whether there is a field separator following or not.
+
+ */
+static const sal_Unicode* lcl_ScanString( const sal_Unicode* p, OUString& rField,
+ const sal_Unicode* pSeps, sal_Unicode cStr, DoubledQuoteMode eMode, bool& rbOverflowCell )
+{
+ OUString aString;
+ bool bClosingQuote = (eMode == DoubledQuoteMode::KEEP_ALL);
+ const sal_Unicode* const pStart = p;
+ if (eMode != DoubledQuoteMode::KEEP_ALL)
+ p++; //! jump over opening quote
+ bool bCont;
+ do
+ {
+ bCont = false;
+ const sal_Unicode* p0 = p;
+ for( ;; )
+ {
+ if (!*p)
+ {
+ // Encountering end of data after an opening quote is not a
+ // quoted string, ReadCsvLine() concatenated lines with '\n'
+ // for a properly quoted embedded linefeed.
+ if (eMode == DoubledQuoteMode::KEEP_ALL)
+ // Caller would append that data anyway, so we can do it
+ // already here.
+ break;
+
+ return pStart;
+ }
+
+ if( *p == cStr )
+ {
+ if ( *++p != cStr )
+ {
+ // break or continue for loop
+ if (eMode == DoubledQuoteMode::ESCAPE)
+ {
+ sal_Unicode cDetectSep = 0xffff; // No separator detection here.
+ if (lcl_isFieldEndQuote( p-1, pSeps, cDetectSep) == FIELDEND_QUOTE)
+ {
+ bClosingQuote = true;
+ break;
+ }
+ else
+ continue;
+ }
+ else
+ break;
+ }
+ // doubled quote char
+ switch ( eMode )
+ {
+ case DoubledQuoteMode::KEEP_ALL :
+ p++; // both for us (not breaking for-loop)
+ break;
+ case DoubledQuoteMode::ESCAPE :
+ p++; // one for us (breaking for-loop)
+ bCont = true; // and more
+ break;
+ }
+ if ( eMode == DoubledQuoteMode::ESCAPE )
+ break;
+ }
+ else
+ p++;
+ }
+ if ( p0 < p )
+ {
+ if (!lcl_appendLineData( aString, p0, ((eMode != DoubledQuoteMode::KEEP_ALL && (*p || *(p-1) == cStr)) ? p-1 : p)))
+ rbOverflowCell = true;
+ }
+ } while ( bCont );
+
+ if (!bClosingQuote)
+ return pStart;
+
+ if (!aString.isEmpty())
+ rField += aString;
+
+ return p;
+}
+
+static void lcl_UnescapeSylk( OUString & rString, SylkVersion eVersion )
+{
+ // Older versions didn't escape the semicolon.
+ // Older versions quoted the string and doubled embedded quotes, but not
+ // the semicolons, which was plain wrong.
+ if (eVersion >= SylkVersion::OOO32)
+ rString = rString.replaceAll(";;", ";");
+ else
+ rString = rString.replaceAll("\"\"", "\"");
+
+ rString = rString.replaceAll(SYLK_LF, "\n");
+}
+
+static const sal_Unicode* lcl_ScanSylkString( const sal_Unicode* p,
+ OUString& rString, SylkVersion eVersion )
+{
+ const sal_Unicode* pStartQuote = p;
+ const sal_Unicode* pEndQuote = nullptr;
+ while( *(++p) )
+ {
+ if( *p == '"' )
+ {
+ pEndQuote = p;
+ if (eVersion >= SylkVersion::OOO32)
+ {
+ if (*(p+1) == ';')
+ {
+ if (*(p+2) == ';')
+ {
+ p += 2; // escaped ';'
+ pEndQuote = nullptr;
+ }
+ else
+ break; // end field
+ }
+ }
+ else
+ {
+ if (*(p+1) == '"')
+ {
+ ++p; // escaped '"'
+ pEndQuote = nullptr;
+ }
+ else if (*(p+1) == ';')
+ break; // end field
+ }
+ }
+ }
+ if (!pEndQuote)
+ pEndQuote = p; // Take all data as string.
+ rString += std::u16string_view(pStartQuote + 1, pEndQuote - pStartQuote - 1 );
+ lcl_UnescapeSylk( rString, eVersion);
+ return p;
+}
+
+static const sal_Unicode* lcl_ScanSylkFormula( const sal_Unicode* p,
+ OUString& rString, SylkVersion eVersion )
+{
+ const sal_Unicode* pStart = p;
+ if (eVersion >= SylkVersion::OOO32)
+ {
+ while (*p)
+ {
+ if (*p == ';')
+ {
+ if (*(p+1) == ';')
+ ++p; // escaped ';'
+ else
+ break; // end field
+ }
+ ++p;
+ }
+ rString += std::u16string_view( pStart, p - pStart);
+ lcl_UnescapeSylk( rString, eVersion);
+ }
+ else
+ {
+ // Nasty. If in old versions the formula contained a semicolon, it was
+ // quoted and embedded quotes were doubled, but semicolons were not. If
+ // there was no semicolon, it could still contain quotes and doubled
+ // embedded quotes if it was something like ="a""b", which was saved as
+ // E"a""b" as is and has to be preserved, even if older versions
+ // couldn't even load it correctly. However, theoretically another
+ // field might follow and thus the line contain a semicolon again, such
+ // as ...;E"a""b";...
+ bool bQuoted = false;
+ if (*p == '"')
+ {
+ // May be a quoted expression or just a string constant expression
+ // with quotes.
+ while (*(++p))
+ {
+ if (*p == '"')
+ {
+ if (*(p+1) == '"')
+ ++p; // escaped '"'
+ else
+ break; // closing '"', had no ';' yet
+ }
+ else if (*p == ';')
+ {
+ bQuoted = true; // ';' within quoted expression
+ break;
+ }
+ }
+ p = pStart;
+ }
+ if (bQuoted)
+ p = lcl_ScanSylkString( p, rString, eVersion);
+ else
+ {
+ while (*p && *p != ';')
+ ++p;
+ rString += std::u16string_view( pStart, p - pStart);
+ }
+ }
+ return p;
+}
+
+static void lcl_WriteString( SvStream& rStrm, OUString& rString, sal_Unicode cQuote, sal_Unicode cEsc )
+{
+ if (cEsc)
+ {
+ // the goal is to replace cStr by cStr+cStr
+ OUString strFrom(cEsc);
+ OUString strTo = strFrom + strFrom;
+ rString = rString.replaceAll(strFrom, strTo);
+ }
+
+ if (cQuote)
+ {
+ rString = OUStringChar(cQuote) + rString + OUStringChar(cQuote);
+ }
+
+ ScImportExport::WriteUnicodeOrByteString( rStrm, rString );
+}
+
+static void lcl_WriteSimpleString( SvStream& rStrm, std::u16string_view rString )
+{
+ ScImportExport::WriteUnicodeOrByteString( rStrm, rString );
+}
+
+bool ScImportExport::Text2Doc( SvStream& rStrm )
+{
+ bool bOk = true;
+
+ sal_Unicode pSeps[2];
+ pSeps[0] = cSep;
+ pSeps[1] = 0;
+
+ ScSetStringParam aSetStringParam;
+ aSetStringParam.mbCheckLinkFormula = true;
+
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ sal_uInt64 nOldPos = rStrm.Tell();
+ rStrm.StartReadingUnicodeText( rStrm.GetStreamCharSet() );
+ bool bData = !bSingle;
+ if( !bSingle)
+ bOk = StartPaste();
+
+ while( bOk )
+ {
+ OUString aLine;
+ OUString aCell;
+ SCROW nRow = nStartRow;
+ rStrm.Seek( nOldPos );
+ for( ;; )
+ {
+ rStrm.ReadUniOrByteStringLine( aLine, rStrm.GetStreamCharSet(), nArbitraryLineLengthLimit );
+ // tdf#125440 When inserting tab separated string, consider quotes as field markers
+ DoubledQuoteMode mode = aLine.indexOf("\t") >= 0 ? DoubledQuoteMode::ESCAPE : DoubledQuoteMode::KEEP_ALL;
+ if( rStrm.eof() )
+ break;
+ SCCOL nCol = nStartCol;
+ const sal_Unicode* p = aLine.getStr();
+ while( *p )
+ {
+ aCell.clear();
+ const sal_Unicode* q = p;
+ if (*p == cStr)
+ {
+ // Look for a pairing quote.
+ q = p = lcl_ScanString( p, aCell, pSeps, cStr, mode, bOverflowCell );
+ }
+ // All until next separator.
+ while (*p && *p != cSep)
+ ++p;
+ if (!lcl_appendLineData( aCell, q, p))
+ bOverflowCell = true; // display warning on import
+ if (*p)
+ ++p;
+ if (rDoc.ValidCol(nCol) && rDoc.ValidRow(nRow) )
+ {
+ if( bSingle )
+ {
+ if (nCol>nEndCol) nEndCol = nCol;
+ if (nRow>nEndRow) nEndRow = nRow;
+ }
+ if( bData && nCol <= nEndCol && nRow <= nEndRow )
+ rDoc.SetString( nCol, nRow, aRange.aStart.Tab(), aCell, &aSetStringParam );
+ }
+ else // too many columns/rows
+ {
+ if (!rDoc.ValidRow(nRow))
+ bOverflowRow = true; // display warning on import
+ if (!rDoc.ValidCol(nCol))
+ bOverflowCol = true; // display warning on import
+ }
+ ++nCol;
+ }
+ ++nRow;
+ }
+
+ if( !bData )
+ {
+ aRange.aEnd.SetCol( nEndCol );
+ aRange.aEnd.SetRow( nEndRow );
+ bOk = StartPaste();
+ bData = true;
+ }
+ else
+ break;
+ }
+
+ EndPaste();
+ if (bOk && mbImportBroadcast)
+ {
+ rDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ pDocSh->PostDataChanged();
+ }
+
+ return bOk;
+}
+
+// Extended Ascii-Import
+
+static bool lcl_PutString(
+ ScDocumentImport& rDocImport, bool bUseDocImport,
+ SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rStr, sal_uInt8 nColFormat,
+ SvNumberFormatter* pFormatter, bool bDetectNumFormat, bool bDetectSciNumFormat, bool bEvaluateFormulas, bool bSkipEmptyCells,
+ const ::utl::TransliterationWrapper& rTransliteration, CalendarWrapper& rCalendar,
+ const ::utl::TransliterationWrapper* pSecondTransliteration, CalendarWrapper* pSecondCalendar )
+{
+ ScDocument& rDoc = rDocImport.getDoc();
+ bool bMultiLine = false;
+ if ( nColFormat == SC_COL_SKIP || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow) )
+ return bMultiLine;
+ if ( rStr.isEmpty() )
+ {
+ if ( !bSkipEmptyCells )
+ { // delete destination cell
+ if ( bUseDocImport )
+ rDocImport.setAutoInput(ScAddress(nCol, nRow, nTab), rStr );
+ else
+ rDoc.SetString( nCol, nRow, nTab, rStr );
+ }
+ return false;
+ }
+
+ const bool bForceFormulaText = (!bEvaluateFormulas && rStr[0] == '=');
+ if (nColFormat == SC_COL_TEXT || bForceFormulaText)
+ {
+ if ( bUseDocImport )
+ {
+ double fDummy;
+ sal_uInt32 nIndex = 0;
+ if (bForceFormulaText || rDoc.GetFormatTable()->IsNumberFormat(rStr, nIndex, fDummy))
+ {
+ // Set the format of this cell to Text.
+ // This is only necessary for ScDocumentImport,
+ // ScDocument::SetTextCell() forces it by ScSetStringParam.
+ sal_uInt32 nFormat = rDoc.GetFormatTable()->GetStandardFormat(SvNumFormatType::TEXT);
+ ScPatternAttr aNewAttrs(rDoc.GetPool());
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nFormat) );
+ rDoc.ApplyPattern(nCol, nRow, nTab, aNewAttrs);
+ }
+ if (ScStringUtil::isMultiline(rStr))
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ rDocImport.setEditCell(ScAddress(nCol, nRow, nTab), rEngine.CreateTextObject());
+ return true;
+ }
+ else
+ {
+ rDocImport.setStringCell(ScAddress(nCol, nRow, nTab), rStr);
+ return false;
+ }
+ }
+ else
+ {
+ rDoc.SetTextCell(ScAddress(nCol, nRow, nTab), rStr);
+ return bMultiLine;
+ }
+ }
+
+ if ( nColFormat == SC_COL_ENGLISH )
+ {
+ //! SetString with Extra-Flag ???
+
+ SvNumberFormatter* pDocFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nEnglish = pDocFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US);
+ double fVal;
+ if ( pDocFormatter->IsNumberFormat( rStr, nEnglish, fVal ) )
+ {
+ // Numberformat will not be set to English
+ if ( bUseDocImport )
+ rDocImport.setNumericCell( ScAddress( nCol, nRow, nTab ), fVal );
+ else
+ rDoc.SetValue( nCol, nRow, nTab, fVal );
+ return bMultiLine;
+ }
+ // else, continue with SetString
+ }
+ else if ( nColFormat != SC_COL_STANDARD ) // Datumformats
+ {
+ const sal_uInt16 nMaxNumberParts = 7; // Y-M-D h:m:s.t
+ const sal_Int32 nLen = rStr.getLength();
+ sal_Int32 nStart[nMaxNumberParts];
+ sal_Int32 nEnd[nMaxNumberParts];
+
+ bool bIso;
+ sal_uInt16 nDP, nMP, nYP;
+ switch ( nColFormat )
+ {
+ case SC_COL_YMD: nDP = 2; nMP = 1; nYP = 0; bIso = true; break;
+ case SC_COL_MDY: nDP = 1; nMP = 0; nYP = 2; bIso = false; break;
+ case SC_COL_DMY:
+ default: nDP = 0; nMP = 1; nYP = 2; bIso = false; break;
+ }
+
+ sal_uInt16 nFound = 0;
+ bool bInNum = false;
+ for (sal_Int32 nPos = 0; nPos < nLen && (bInNum || nFound < nMaxNumberParts); ++nPos)
+ {
+ bool bLetter = false;
+ if (rtl::isAsciiDigit(rStr[nPos]) ||
+ (((!bInNum && nFound==nMP) || (bInNum && nFound==nMP+1))
+ && (bLetter = ScGlobal::getCharClass().isLetterNumeric( rStr, nPos))))
+ {
+ if (!bInNum)
+ {
+ bInNum = true;
+ nStart[nFound] = nPos;
+ ++nFound;
+ }
+ nEnd[nFound-1] = nPos;
+ if (bIso && (bLetter || (2 <= nFound && nFound <= 6 && nPos > nStart[nFound-1] + 1)))
+ // Each M,D,h,m,s at most 2 digits.
+ bIso = false;
+ }
+ else
+ {
+ bInNum = false;
+ if (bIso)
+ {
+ // ([+-])YYYY-MM-DD([T ]hh:mm(:ss(.fff)))(([+-])TZ)
+ // XXX NOTE: timezone is accepted here, but number
+ // formatter parser will not, so the end result will be
+ // type Text to preserve timezone information.
+ switch (rStr[nPos])
+ {
+ case '+':
+ if (nFound >= 5 && nPos == nEnd[nFound-1] + 1)
+ // Accept timezone offset.
+ ;
+ else if (nPos > 0)
+ // Accept one leading sign.
+ bIso = false;
+ break;
+ case '-':
+ if (nFound >= 5 && nPos == nEnd[nFound-1] + 1)
+ // Accept timezone offset.
+ ;
+ else if (nFound == 0 && nPos > 0)
+ // Accept one leading sign.
+ bIso = false;
+ else if (nFound < 1 || 2 < nFound || nPos != nEnd[nFound-1] + 1)
+ // Not immediately after 1 or 1-2
+ bIso = false;
+ break;
+ case 'T':
+ case ' ':
+ if (nFound != 3 || nPos != nEnd[nFound-1] + 1)
+ // Not immediately after 1-2-3
+ bIso = false;
+ break;
+ case ':':
+ if (nFound < 4 || 5 < nFound || nPos != nEnd[nFound-1] + 1)
+ // Not at 1-2-3T4:5:
+ bIso = false;
+ break;
+ case '.':
+ case ',':
+ if (nFound != 6 || nPos != nEnd[nFound-1] + 1)
+ // Not at 1-2-3T4:5:6.
+ bIso = false;
+ break;
+ case 'Z':
+ if (nFound >= 5 && nPos == nEnd[nFound-1] + 1)
+ // Accept Zero timezone.
+ ;
+ else
+ bIso = false;
+ break;
+ default:
+ bIso = false;
+ }
+ }
+ }
+ }
+
+ if (nFound < 3)
+ bIso = false;
+
+ if (bIso)
+ {
+ // Leave conversion and detection of various possible number
+ // formats to the number formatter. ISO is recognized in any locale
+ // so we can directly use the document's formatter.
+ sal_uInt32 nFormat = 0;
+ double fVal = 0.0;
+ SvNumberFormatter* pDocFormatter = rDoc.GetFormatTable();
+ if (pDocFormatter->IsNumberFormat( rStr, nFormat, fVal))
+ {
+ if (pDocFormatter->GetType(nFormat) & SvNumFormatType::DATE)
+ {
+ ScAddress aPos(nCol,nRow,nTab);
+ if (bUseDocImport)
+ rDocImport.setNumericCell(aPos, fVal);
+ else
+ rDoc.SetValue(aPos, fVal);
+ rDoc.SetNumberFormat(aPos, nFormat);
+
+ return bMultiLine; // success
+ }
+ }
+ // If we reach here it is type Text (e.g. timezone or trailing
+ // characters). Handled below.
+ }
+
+ if ( nFound == 1 )
+ {
+ // try to break one number (without separators) into date fields
+
+ sal_Int32 nDateStart = nStart[0];
+ sal_Int32 nDateLen = nEnd[0] + 1 - nDateStart;
+
+ if ( nDateLen >= 5 && nDateLen <= 8 &&
+ ScGlobal::getCharClass().isNumeric( rStr.copy( nDateStart, nDateLen ) ) )
+ {
+ // 6 digits: 2 each for day, month, year
+ // 8 digits: 4 for year, 2 each for day and month
+ // 5 or 7 digits: first field is shortened by 1
+
+ bool bLongYear = ( nDateLen >= 7 );
+ bool bShortFirst = ( nDateLen == 5 || nDateLen == 7 );
+
+ sal_uInt16 nFieldStart = nDateStart;
+ for (sal_uInt16 nPos=0; nPos<3; nPos++)
+ {
+ sal_uInt16 nFieldEnd = nFieldStart + 1; // default: 2 digits
+ if ( bLongYear && nPos == nYP )
+ nFieldEnd += 2; // 2 extra digits for long year
+ if ( bShortFirst && nPos == 0 )
+ --nFieldEnd; // first field shortened?
+
+ nStart[nPos] = nFieldStart;
+ nEnd[nPos] = nFieldEnd;
+ nFieldStart = nFieldEnd + 1;
+ }
+ nFound = 3;
+ }
+ }
+
+ if (!bIso && nFound >= 3)
+ {
+ using namespace ::com::sun::star;
+ bool bSecondCal = false;
+ sal_uInt16 nDay = static_cast<sal_uInt16>(o3tl::toInt32(rStr.subView( nStart[nDP], nEnd[nDP]+1-nStart[nDP] )));
+ sal_uInt16 nYear = static_cast<sal_uInt16>(o3tl::toInt32(rStr.subView( nStart[nYP], nEnd[nYP]+1-nStart[nYP] )));
+ OUString aMStr = rStr.copy( nStart[nMP], nEnd[nMP]+1-nStart[nMP] );
+ sal_Int16 nMonth = static_cast<sal_Int16>(aMStr.toInt32());
+ if (!nMonth)
+ {
+ static constexpr OUString aSepShortened = u"SEP"_ustr;
+ uno::Sequence< i18n::CalendarItem2 > xMonths;
+ sal_Int32 i, nMonthCount;
+ // first test all month names from local international
+ xMonths = rCalendar.getMonths();
+ nMonthCount = xMonths.getLength();
+ for (i=0; i<nMonthCount && !nMonth; i++)
+ {
+ if ( rTransliteration.isEqual( aMStr, xMonths[i].FullName ) ||
+ rTransliteration.isEqual( aMStr, xMonths[i].AbbrevName ) )
+ nMonth = sal::static_int_cast<sal_Int16>( i+1 );
+ else if ( i == 8 && rTransliteration.isEqual( "SEPT",
+ xMonths[i].AbbrevName ) &&
+ rTransliteration.isEqual( aMStr, aSepShortened ) )
+ { // correct English abbreviation is SEPT,
+ // but data mostly contains SEP only
+ nMonth = sal::static_int_cast<sal_Int16>( i+1 );
+ }
+ }
+ // if none found, then test english month names
+ if ( !nMonth && pSecondCalendar && pSecondTransliteration )
+ {
+ xMonths = pSecondCalendar->getMonths();
+ nMonthCount = xMonths.getLength();
+ for (i=0; i<nMonthCount && !nMonth; i++)
+ {
+ if ( pSecondTransliteration->isEqual( aMStr, xMonths[i].FullName ) ||
+ pSecondTransliteration->isEqual( aMStr, xMonths[i].AbbrevName ) )
+ {
+ nMonth = sal::static_int_cast<sal_Int16>( i+1 );
+ bSecondCal = true;
+ }
+ else if ( i == 8 && pSecondTransliteration->isEqual(
+ aMStr, aSepShortened ) )
+ { // correct English abbreviation is SEPT,
+ // but data mostly contains SEP only
+ nMonth = sal::static_int_cast<sal_Int16>( i+1 );
+ bSecondCal = true;
+ }
+ }
+ }
+ }
+
+ SvNumberFormatter* pDocFormatter = rDoc.GetFormatTable();
+ if ( nYear < 100 )
+ nYear = pDocFormatter->ExpandTwoDigitYear( nYear );
+
+ CalendarWrapper* pCalendar = (bSecondCal ? pSecondCalendar : &rCalendar);
+ sal_Int16 nNumMonths = pCalendar->getNumberOfMonthsInYear();
+ if ( nDay && nMonth && nDay<=31 && nMonth<=nNumMonths )
+ {
+ --nMonth;
+ pCalendar->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDay );
+ pCalendar->setValue( i18n::CalendarFieldIndex::MONTH, nMonth );
+ pCalendar->setValue( i18n::CalendarFieldIndex::YEAR, nYear );
+ sal_Int16 nHour, nMinute, nSecond;
+ // #i14974# The imported value should have no fractional value, so set the
+ // time fields to zero (ICU calendar instance defaults to current date/time)
+ nHour = nMinute = nSecond = 0;
+ if (nFound > 3)
+ nHour = static_cast<sal_Int16>(o3tl::toInt32(rStr.subView( nStart[3], nEnd[3]+1-nStart[3])));
+ if (nFound > 4)
+ nMinute = static_cast<sal_Int16>(o3tl::toInt32(rStr.subView( nStart[4], nEnd[4]+1-nStart[4])));
+ if (nFound > 5)
+ nSecond = static_cast<sal_Int16>(o3tl::toInt32(rStr.subView( nStart[5], nEnd[5]+1-nStart[5])));
+ // do not use calendar's milliseconds, to avoid fractional part truncation
+ double fFrac = 0.0;
+ if (nFound > 6)
+ {
+ sal_Unicode cDec = '.';
+ OUString aT = OUStringChar(cDec) + rStr.subView( nStart[6], nEnd[6]+1-nStart[6]);
+ rtl_math_ConversionStatus eStatus;
+ double fV = rtl::math::stringToDouble( aT, cDec, 0, &eStatus );
+ if (eStatus == rtl_math_ConversionStatus_Ok)
+ fFrac = fV / 86400.0;
+ }
+ sal_Int32 nPos;
+ if (nFound > 3 && 1 <= nHour && nHour <= 12 // nHour 0 and >=13 can't be AM/PM
+ && (nPos = nEnd[nFound-1] + 1) < nLen)
+ {
+ // Dreaded AM/PM may be following.
+ while (nPos < nLen && rStr[nPos] == ' ')
+ ++nPos;
+ if (nPos < nLen)
+ {
+ sal_Int32 nStop = nPos;
+ while (nStop < nLen && rStr[nStop] != ' ')
+ ++nStop;
+ OUString aAmPm = rStr.copy( nPos, nStop - nPos);
+ // For AM only 12 needs to be treated, whereas for PM
+ // it must not. Check both, locale and second/English
+ // strings.
+ if (nHour == 12 &&
+ (rTransliteration.isEqual( aAmPm, pFormatter->GetLocaleData()->getTimeAM()) ||
+ (pSecondTransliteration && pSecondTransliteration->isEqual( aAmPm, "AM"))))
+ {
+ nHour = 0;
+ }
+ else if (nHour < 12 &&
+ (rTransliteration.isEqual( aAmPm, pFormatter->GetLocaleData()->getTimePM()) ||
+ (pSecondTransliteration && pSecondTransliteration->isEqual( aAmPm, "PM"))))
+ {
+ nHour += 12;
+ }
+ }
+ }
+ pCalendar->setValue( i18n::CalendarFieldIndex::HOUR, nHour );
+ pCalendar->setValue( i18n::CalendarFieldIndex::MINUTE, nMinute );
+ pCalendar->setValue( i18n::CalendarFieldIndex::SECOND, nSecond );
+ pCalendar->setValue( i18n::CalendarFieldIndex::MILLISECOND, 0 );
+ if ( pCalendar->isValid() )
+ {
+ // Whole days diff.
+ double fDiff = DateTime::Sub( DateTime(pDocFormatter->GetNullDate()),
+ pCalendar->getEpochStart());
+ // #i14974# must use getLocalDateTime to get the same
+ // date values as set above
+ double fDays = pCalendar->getLocalDateTime() + fFrac;
+ fDays -= fDiff;
+
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ LanguageType eDocLang = eLatin; //! which language for date formats?
+
+ SvNumFormatType nType = (nFound > 3 ? SvNumFormatType::DATETIME : SvNumFormatType::DATE);
+ sal_uLong nFormat = pDocFormatter->GetStandardFormat( nType, eDocLang );
+ // maybe there is a special format including seconds or milliseconds
+ if (nFound > 5)
+ nFormat = pDocFormatter->GetStandardFormat( fDays, nFormat, nType, eDocLang);
+
+ ScAddress aPos(nCol,nRow,nTab);
+ if ( bUseDocImport )
+ rDocImport.setNumericCell(aPos, fDays);
+ else
+ rDoc.SetValue( aPos, fDays );
+ rDoc.SetNumberFormat(aPos, nFormat);
+
+ return bMultiLine; // success
+ }
+ }
+ }
+ }
+
+ // Standard or date not determined -> SetString / EditCell
+ if( rStr.indexOf( '\n' ) == -1 )
+ {
+ if (!bDetectNumFormat && nColFormat == SC_COL_STANDARD)
+ {
+ // Import a strict ISO 8601 date(+time) string even without
+ // "Detect special numbers" or "Date (YMD)".
+ do
+ {
+ // Simple pre-check before calling more expensive parser.
+ // ([+-])(Y)YYYY-MM-DD
+ if (rStr.getLength() < 10)
+ break;
+ const sal_Int32 n1 = rStr.indexOf('-', 1);
+ if (n1 < 4)
+ break;
+ const sal_Int32 n2 = rStr.indexOf('-', n1 + 1);
+ if (n2 < 7 || n1 + 3 < n2)
+ break;
+
+ css::util::DateTime aDateTime;
+ if (!sax::Converter::parseDateTime( aDateTime, rStr))
+ break;
+
+ sal_uInt32 nFormat = 0;
+ double fVal = 0.0;
+ SvNumberFormatter* pDocFormatter = rDoc.GetFormatTable();
+ if (pDocFormatter->IsNumberFormat( rStr, nFormat, fVal))
+ {
+ if (pDocFormatter->GetType(nFormat) & SvNumFormatType::DATE)
+ {
+ ScAddress aPos(nCol,nRow,nTab);
+ if (bUseDocImport)
+ rDocImport.setNumericCell(aPos, fVal);
+ else
+ rDoc.SetValue(aPos, fVal);
+ rDoc.SetNumberFormat(aPos, nFormat);
+
+ return bMultiLine; // success
+ }
+ }
+ }
+ while(false);
+ }
+
+ ScSetStringParam aParam;
+ aParam.mpNumFormatter = pFormatter;
+ aParam.mbDetectNumberFormat = bDetectNumFormat;
+ aParam.mbDetectScientificNumberFormat = bDetectSciNumFormat;
+ aParam.meSetTextNumFormat = ScSetStringParam::SpecialNumberOnly;
+ aParam.mbHandleApostrophe = false;
+ aParam.mbCheckLinkFormula = true;
+ if ( bUseDocImport )
+ rDocImport.setAutoInput(ScAddress(nCol, nRow, nTab), rStr, &aParam);
+ else
+ rDoc.SetString( nCol, nRow, nTab, rStr, &aParam );
+ }
+ else
+ {
+ bMultiLine = true;
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ if ( bUseDocImport )
+ rDocImport.setEditCell(ScAddress(nCol, nRow, nTab), rEngine.CreateTextObject());
+ else
+ rDoc.SetEditText( ScAddress( nCol, nRow, nTab ), rEngine.CreateTextObject() );
+ }
+ return bMultiLine;
+}
+
+static OUString lcl_GetFixed( const OUString& rLine, sal_Int32 nStart, sal_Int32 nNext,
+ bool& rbIsQuoted, bool& rbOverflowCell )
+{
+ sal_Int32 nLen = rLine.getLength();
+ if (nNext > nLen)
+ nNext = nLen;
+ if ( nNext <= nStart )
+ return OUString();
+
+ const sal_Unicode* pStr = rLine.getStr();
+
+ sal_Int32 nSpace = nNext;
+ while ( nSpace > nStart && pStr[nSpace-1] == ' ' )
+ --nSpace;
+
+ rbIsQuoted = (pStr[nStart] == '"' && pStr[nSpace-1] == '"');
+ if (rbIsQuoted)
+ {
+ bool bFits = (nSpace - nStart - 3 <= nArbitraryCellLengthLimit);
+ if (bFits)
+ return rLine.copy(nStart+1, std::max< sal_Int32 >(0, nSpace-nStart-2));
+ else
+ {
+ SAL_WARN( "sc", "lcl_GetFixed: line doesn't fit into data");
+ rbOverflowCell = true;
+ return rLine.copy(nStart+1, nArbitraryCellLengthLimit);
+ }
+ }
+ else
+ {
+ bool bFits = (nSpace - nStart <= nArbitraryCellLengthLimit);
+ if (bFits)
+ return rLine.copy(nStart, nSpace-nStart);
+ else
+ {
+ SAL_WARN( "sc", "lcl_GetFixed: line doesn't fit into data");
+ rbOverflowCell = true;
+ return rLine.copy(nStart, nArbitraryCellLengthLimit);
+ }
+ }
+}
+
+bool ScImportExport::ExtText2Doc( SvStream& rStrm )
+{
+ if (!pExtOptions)
+ return Text2Doc( rStrm );
+
+ sal_uInt64 const nOldPos = rStrm.Tell();
+ sal_uInt64 const nRemaining = rStrm.remainingSize();
+ std::unique_ptr<ScProgress> xProgress( new ScProgress( pDocSh,
+ ScResId( STR_LOAD_DOC ), nRemaining, true ));
+ rStrm.StartReadingUnicodeText( rStrm.GetStreamCharSet() );
+ // tdf#82254 - check whether to include a byte-order-mark in the output
+ if (nOldPos != rStrm.Tell())
+ mbIncludeBOM = true;
+
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ const SCTAB nTab = aRange.aStart.Tab();
+
+ bool bFixed = pExtOptions->IsFixedLen();
+ OUString aSeps = pExtOptions->GetFieldSeps(); // Need non-const for ReadCsvLine(),
+ const sal_Unicode* pSeps = aSeps.getStr(); // but it will be const anyway (asserted below).
+ bool bMerge = pExtOptions->IsMergeSeps();
+ bool bRemoveSpace = pExtOptions->IsRemoveSpace();
+ sal_uInt16 nInfoCount = pExtOptions->GetInfoCount();
+ const sal_Int32* pColStart = pExtOptions->GetColStart();
+ const sal_uInt8* pColFormat = pExtOptions->GetColFormat();
+ tools::Long nSkipLines = pExtOptions->GetStartRow();
+
+ LanguageType eDocLang = pExtOptions->GetLanguage();
+ SvNumberFormatter aNumFormatter( comphelper::getProcessComponentContext(), eDocLang);
+ bool bDetectNumFormat = pExtOptions->IsDetectSpecialNumber();
+ bool bDetectSciNumFormat = pExtOptions->IsDetectScientificNumber();
+ bool bEvaluateFormulas = pExtOptions->IsEvaluateFormulas();
+ bool bSkipEmptyCells = pExtOptions->IsSkipEmptyCells();
+
+ // For date recognition
+ ::utl::TransliterationWrapper aTransliteration(
+ comphelper::getProcessComponentContext(), TransliterationFlags::IGNORE_CASE );
+ aTransliteration.loadModuleIfNeeded( eDocLang );
+ CalendarWrapper aCalendar( comphelper::getProcessComponentContext() );
+ aCalendar.loadDefaultCalendar(
+ LanguageTag::convertToLocale( eDocLang ) );
+ std::unique_ptr< ::utl::TransliterationWrapper > pEnglishTransliteration;
+ std::unique_ptr< CalendarWrapper > pEnglishCalendar;
+ if ( eDocLang != LANGUAGE_ENGLISH_US )
+ {
+ pEnglishTransliteration.reset(new ::utl::TransliterationWrapper (
+ comphelper::getProcessComponentContext(), TransliterationFlags::IGNORE_CASE ));
+ aTransliteration.loadModuleIfNeeded( LANGUAGE_ENGLISH_US );
+ pEnglishCalendar.reset(new CalendarWrapper ( comphelper::getProcessComponentContext() ));
+ pEnglishCalendar->loadDefaultCalendar(
+ LanguageTag::convertToLocale( LANGUAGE_ENGLISH_US ) );
+ }
+
+ OUString aLine;
+ OUString aCell;
+ sal_uInt16 i;
+ SCROW nRow = nStartRow;
+ sal_Unicode cDetectSep = 0xffff; // No separator detection here.
+
+ while(--nSkipLines>0)
+ {
+ aLine = ReadCsvLine(rStrm, !bFixed, aSeps, cStr, cDetectSep); // content is ignored
+ if ( rStrm.eof() )
+ break;
+ }
+
+ // Determine range for Undo.
+ // We don't need this during import of a file to a new sheet or document...
+ bool bDetermineRange = bUndo;
+ bool bColumnsAreDetermined = false;
+
+ // Row heights don't need to be adjusted on the fly if EndPaste() is called
+ // afterwards, which happens only if bDetermineRange. This variable also
+ // survives the toggle of bDetermineRange down at the end of the do{} loop.
+ bool bRangeIsDetermined = bDetermineRange;
+
+ bool bQuotedAsText = pExtOptions && pExtOptions->IsQuotedAsText();
+
+ sal_uInt64 nOriginalStreamPos = rStrm.Tell();
+
+ SCROW nFirstUpdateRowHeight = SCROW_MAX;
+ SCROW nLastUpdateRowHeight = -1;
+
+ ScDocumentImport aDocImport(rDoc);
+ do
+ {
+ for( ;; )
+ {
+ aLine = ReadCsvLine(rStrm, !bFixed, aSeps, cStr, cDetectSep);
+ if ( rStrm.eof() && aLine.isEmpty() )
+ break;
+
+ assert(pSeps == aSeps.getStr());
+
+ if ( nRow > rDoc.MaxRow() )
+ {
+ bOverflowRow = true; // display warning on import
+ break; // for
+ }
+
+ if (!bDetermineRange)
+ EmbeddedNullTreatment( aLine);
+
+ sal_Int32 nLineLen = aLine.getLength();
+ SCCOL nCol = nStartCol;
+ bool bMultiLine = false;
+ if ( bFixed ) // Fixed line length
+ {
+ if (bDetermineRange)
+ {
+ if (!bColumnsAreDetermined)
+ {
+ // Yes, the check is nCol<=rDoc.MaxCol()+1, +1 because it
+ // is only an overflow if there is really data following to
+ // be put behind the last column, which doesn't happen if
+ // info is SC_COL_SKIP.
+ for (i=0; i < nInfoCount && nCol <= rDoc.MaxCol()+1; ++i)
+ {
+ const sal_uInt8 nFmt = pColFormat[i];
+ if (nFmt != SC_COL_SKIP) // otherwise don't increment nCol either
+ {
+ if (nCol > rDoc.MaxCol())
+ bOverflowCol = true; // display warning on import
+ ++nCol;
+ }
+ }
+ bColumnsAreDetermined = true;
+ }
+ }
+ else
+ {
+ sal_Int32 nStartIdx = 0;
+ // Same maxcol+1 check reason as above.
+ for (i=0; i < nInfoCount && nCol <= rDoc.MaxCol()+1; ++i)
+ {
+ sal_Int32 nNextIdx = nStartIdx;
+ if (i + 1 < nInfoCount)
+ CountVisualWidth( aLine, nNextIdx, pColStart[i+1] - pColStart[i] );
+ else
+ nNextIdx = nLineLen;
+ sal_uInt8 nFmt = pColFormat[i];
+ if (nFmt != SC_COL_SKIP) // otherwise don't increment nCol either
+ {
+ if (nCol > rDoc.MaxCol())
+ bOverflowCol = true; // display warning on import
+ else
+ {
+ bool bIsQuoted = false;
+ aCell = lcl_GetFixed( aLine, nStartIdx, nNextIdx, bIsQuoted, bOverflowCell );
+ if (bIsQuoted && bQuotedAsText)
+ nFmt = SC_COL_TEXT;
+
+ bMultiLine |= lcl_PutString(
+ aDocImport, !mbOverwriting, nCol, nRow, nTab, aCell, nFmt,
+ &aNumFormatter, bDetectNumFormat, bDetectSciNumFormat, bEvaluateFormulas, bSkipEmptyCells,
+ aTransliteration, aCalendar,
+ pEnglishTransliteration.get(), pEnglishCalendar.get());
+ }
+ ++nCol;
+ }
+ nStartIdx = nNextIdx;
+ }
+ }
+ }
+ else // Search for the separator
+ {
+ SCCOL nSourceCol = 0;
+ sal_uInt16 nInfoStart = 0;
+ const sal_Unicode* p = aLine.getStr();
+ // Yes, the check is nCol<=rDoc.MaxCol()+1, +1 because it is only an
+ // overflow if there is really data following to be put behind
+ // the last column, which doesn't happen if info is
+ // SC_COL_SKIP.
+ while (*p && nCol <= rDoc.MaxCol()+1)
+ {
+ bool bIsQuoted = false;
+ p = ScImportExport::ScanNextFieldFromString( p, aCell,
+ cStr, pSeps, bMerge, bIsQuoted, bOverflowCell, bRemoveSpace );
+
+ sal_uInt8 nFmt = SC_COL_STANDARD;
+ for ( i=nInfoStart; i<nInfoCount; i++ )
+ {
+ if ( pColStart[i] == nSourceCol + 1 ) // pColStart is 1-based
+ {
+ nFmt = pColFormat[i];
+ nInfoStart = i + 1; // ColInfos are in succession
+ break; // for
+ }
+ }
+ if ( nFmt != SC_COL_SKIP )
+ {
+ if (nCol > rDoc.MaxCol())
+ bOverflowCol = true; // display warning on import
+ else if (!bDetermineRange)
+ {
+ if (bIsQuoted && bQuotedAsText)
+ nFmt = SC_COL_TEXT;
+
+ bMultiLine |= lcl_PutString(
+ aDocImport, !mbOverwriting, nCol, nRow, nTab, aCell, nFmt,
+ &aNumFormatter, bDetectNumFormat, bDetectSciNumFormat, bEvaluateFormulas, bSkipEmptyCells,
+ aTransliteration, aCalendar,
+ pEnglishTransliteration.get(), pEnglishCalendar.get());
+ }
+ ++nCol;
+ }
+
+ ++nSourceCol;
+ }
+ }
+ if (nEndCol < nCol)
+ nEndCol = nCol; //! points to the next free or even rDoc.MaxCol()+2
+
+ if (!bDetermineRange)
+ {
+ if (bMultiLine && !bRangeIsDetermined && pDocSh)
+ { // Adjust just once at the end for a whole range.
+ nFirstUpdateRowHeight = std::min( nFirstUpdateRowHeight, nRow );
+ nLastUpdateRowHeight = std::max( nLastUpdateRowHeight, nRow );
+ }
+ xProgress->SetStateOnPercent( rStrm.Tell() - nOldPos );
+ }
+ ++nRow;
+ }
+ // so far nRow/nEndCol pointed to the next free
+ if (nRow > nStartRow)
+ --nRow;
+ if (nEndCol > nStartCol)
+ nEndCol = ::std::min( static_cast<SCCOL>(nEndCol - 1), rDoc.MaxCol());
+
+ if (bDetermineRange)
+ {
+ aRange.aEnd.SetCol( nEndCol );
+ aRange.aEnd.SetRow( nRow );
+
+ if ( !mbApi && nStartCol != nEndCol &&
+ !rDoc.IsBlockEmpty( nStartCol + 1, nStartRow, nEndCol, nRow, nTab ) )
+ {
+ ScReplaceWarnBox aBox(ScDocShell::GetActiveDialogParent());
+ if (aBox.run() != RET_YES)
+ {
+ return false;
+ }
+ }
+
+ rStrm.Seek( nOriginalStreamPos );
+ nRow = nStartRow;
+ if (!StartPaste())
+ {
+ EndPaste(false);
+ return false;
+ }
+ }
+
+ bDetermineRange = !bDetermineRange; // toggle
+ } while (!bDetermineRange);
+
+ if ( !mbOverwriting )
+ aDocImport.finalize();
+
+ xProgress.reset(); // make room for AdjustRowHeight progress
+
+ if( nFirstUpdateRowHeight < nLastUpdateRowHeight && pDocSh )
+ pDocSh->AdjustRowHeight( nFirstUpdateRowHeight, nLastUpdateRowHeight, nTab);
+
+ if (bRangeIsDetermined)
+ EndPaste(false);
+
+ if (mbImportBroadcast && !mbOverwriting)
+ {
+ rDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ pDocSh->PostDataChanged();
+ }
+ return true;
+}
+
+void ScImportExport::EmbeddedNullTreatment( OUString & rStr )
+{
+ // A nasty workaround for data with embedded NULL characters. As long as we
+ // can't handle them properly as cell content (things assume 0-terminated
+ // strings at too many places) simply strip all NULL characters from raw
+ // data. Excel does the same. See fdo#57841 for sample data.
+
+ // The normal case is no embedded NULL, check first before de-/allocating
+ // ustring stuff.
+ sal_Unicode cNull = 0;
+ if (sal_Int32 pos = rStr.indexOf(cNull); pos >= 0)
+ {
+ rStr = rStr.replaceAll(std::u16string_view(&cNull, 1), u"", pos);
+ }
+}
+
+const sal_Unicode* ScImportExport::ScanNextFieldFromString( const sal_Unicode* p,
+ OUString& rField, sal_Unicode cStr, const sal_Unicode* pSeps, bool bMergeSeps, bool& rbIsQuoted,
+ bool& rbOverflowCell, bool bRemoveSpace )
+{
+ rbIsQuoted = false;
+ rField.clear();
+ const sal_Unicode cBlank = ' ';
+ if (cStr && !ScGlobal::UnicodeStrChr(pSeps, cBlank))
+ {
+ // Cope with broken generators that put leading blanks before a quoted
+ // field, like "field1", "field2", "..."
+ // NOTE: this is not in conformance with http://tools.ietf.org/html/rfc4180
+ const sal_Unicode* pb = p;
+ while (*pb == cBlank)
+ ++pb;
+ if (*pb == cStr)
+ p = pb;
+ }
+ if (cStr && *p == cStr) // String in quotes
+ {
+ rbIsQuoted = true;
+ const sal_Unicode* p1;
+ p1 = p = lcl_ScanString( p, rField, pSeps, cStr, DoubledQuoteMode::ESCAPE, rbOverflowCell );
+ while (!lcl_isFieldEnd( *p, pSeps))
+ p++;
+ // Append remaining unquoted and undelimited data (dirty, dirty) to
+ // this field.
+ if (p > p1)
+ {
+ const sal_Unicode* ptrim_f = p;
+ if ( bRemoveSpace )
+ {
+ while ( ptrim_f > p1 && ( *(ptrim_f - 1) == cBlank ) )
+ --ptrim_f;
+ }
+ if (!lcl_appendLineData( rField, p1, ptrim_f))
+ rbOverflowCell = true;
+ }
+ if( *p )
+ p++;
+ }
+ else // up to delimiter
+ {
+ const sal_Unicode* p0 = p;
+ while (!lcl_isFieldEnd( *p, pSeps))
+ p++;
+ const sal_Unicode* ptrim_i = p0;
+ const sal_Unicode* ptrim_f = p; // [ptrim_i,ptrim_f) is cell data after trimming
+ if ( bRemoveSpace )
+ {
+ while ( ptrim_i < ptrim_f && *ptrim_i == cBlank )
+ ++ptrim_i;
+ while ( ptrim_f > ptrim_i && ( *(ptrim_f - 1) == cBlank ) )
+ --ptrim_f;
+ }
+ if (!lcl_appendLineData( rField, ptrim_i, ptrim_f))
+ rbOverflowCell = true;
+ if( *p )
+ p++;
+ }
+ if ( bMergeSeps ) // skip following delimiters
+ {
+ while (*p && ScGlobal::UnicodeStrChr( pSeps, *p))
+ p++;
+ }
+ return p;
+}
+
+namespace {
+
+/**
+ * Check if a given string has any line break characters or separators.
+ *
+ * @param rStr string to inspect.
+ * @param cSep separator character.
+ */
+bool hasLineBreaksOrSeps( const OUString& rStr, sal_Unicode cSep )
+{
+ const sal_Unicode* p = rStr.getStr();
+ for (sal_Int32 i = 0, n = rStr.getLength(); i < n; ++i, ++p)
+ {
+ sal_Unicode c = *p;
+ if (c == cSep)
+ // separator found.
+ return true;
+
+ switch (c)
+ {
+ case '\n':
+ case '\r':
+ // line break found.
+ return true;
+ default:
+ ;
+ }
+ }
+ return false;
+}
+
+}
+
+bool ScImportExport::Doc2Text( SvStream& rStrm )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCTAB nStartTab = aRange.aStart.Tab();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ SCTAB nEndTab = aRange.aEnd.Tab();
+
+ if (!rDoc.GetClipParam().isMultiRange() && nStartTab == nEndTab)
+ if (!rDoc.ShrinkToDataArea( nStartTab, nStartCol, nStartRow, nEndCol, nEndRow ))
+ return false;
+
+ OUString aCellStr;
+
+ bool bConvertLF = (GetSystemLineEnd() != LINEEND_LF);
+
+ // We need to cache sc::ColumnBlockPosition per each column, tab is always nStartTab.
+ std::vector< sc::ColumnBlockPosition > blockPos( nEndCol - nStartCol + 1 );
+ for( SCCOL i = nStartCol; i <= nEndCol; ++i )
+ rDoc.InitColumnBlockPosition( blockPos[ i - nStartCol ], nStartTab, i );
+ for (nRow = nStartRow; nRow <= nEndRow; nRow++)
+ {
+ if (bIncludeFiltered || !rDoc.RowFiltered( nRow, nStartTab ))
+ {
+ for (nCol = nStartCol; nCol <= nEndCol; nCol++)
+ {
+ ScAddress aPos(nCol, nRow, nStartTab);
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat(aPos);
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ ScRefCellValue aCell(rDoc, aPos, blockPos[ nCol - nStartCol ]);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_FORMULA:
+ {
+ if (bFormulas)
+ {
+ aCellStr = aCell.getFormula()->GetFormula();
+ if( aCellStr.indexOf( cSep ) != -1 )
+ lcl_WriteString( rStrm, aCellStr, cStr, cStr );
+ else
+ lcl_WriteSimpleString( rStrm, aCellStr );
+ }
+ else
+ {
+ const Color* pColor;
+ aCellStr = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, rDoc);
+
+ bool bMultiLineText = ( aCellStr.indexOf( '\n' ) != -1 );
+ if( bMultiLineText )
+ {
+ if( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSpace )
+ aCellStr = aCellStr.replaceAll( "\n", " " );
+ else if ( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSystem && bConvertLF )
+ aCellStr = convertLineEnd(aCellStr, GetSystemLineEnd());
+ }
+
+ if( mExportTextOptions.mcSeparatorConvertTo && cSep )
+ aCellStr = aCellStr.replaceAll( OUStringChar(cSep), OUStringChar(mExportTextOptions.mcSeparatorConvertTo) );
+
+ if( mExportTextOptions.mbAddQuotes && ( aCellStr.indexOf( cSep ) != -1 ) )
+ lcl_WriteString( rStrm, aCellStr, cStr, cStr );
+ else
+ lcl_WriteSimpleString( rStrm, aCellStr );
+ }
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ const Color* pColor;
+ aCellStr = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, rDoc);
+ lcl_WriteSimpleString( rStrm, aCellStr );
+ }
+ break;
+ case CELLTYPE_NONE:
+ break;
+ default:
+ {
+ const Color* pColor;
+ aCellStr = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, rDoc);
+
+ bool bMultiLineText = ( aCellStr.indexOf( '\n' ) != -1 );
+ if( bMultiLineText )
+ {
+ if( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSpace )
+ aCellStr = aCellStr.replaceAll( "\n", " " );
+ else if ( mExportTextOptions.meNewlineConversion == ScExportTextOptions::ToSystem && bConvertLF )
+ aCellStr = convertLineEnd(aCellStr, GetSystemLineEnd());
+ }
+
+ if( mExportTextOptions.mcSeparatorConvertTo && cSep )
+ aCellStr = aCellStr.replaceAll( OUStringChar(cSep), OUStringChar(mExportTextOptions.mcSeparatorConvertTo) );
+
+ if( mExportTextOptions.mbAddQuotes && hasLineBreaksOrSeps(aCellStr, cSep) )
+ lcl_WriteString( rStrm, aCellStr, cStr, cStr );
+ else
+ lcl_WriteSimpleString( rStrm, aCellStr );
+ }
+ }
+ if( nCol < nEndCol )
+ lcl_WriteSimpleString( rStrm, rtl::OUStringChar(cSep) );
+ }
+ // Do not append a line feed for one single cell.
+ // NOTE: this Doc2Text() is only called for clipboard via
+ // ScImportExport::ExportStream().
+ if (nStartRow != nEndRow || nStartCol != nEndCol)
+ WriteUnicodeOrByteEndl( rStrm );
+ if( rStrm.GetError() != ERRCODE_NONE )
+ break;
+ if( nSizeLimit && rStrm.Tell() > nSizeLimit )
+ break;
+ }
+ }
+
+ return rStrm.GetError() == ERRCODE_NONE;
+}
+
+bool ScImportExport::Sylk2Doc( SvStream& rStrm )
+{
+ bool bOk = true;
+ bool bMyDoc = false;
+ SylkVersion eVersion = SylkVersion::OTHER;
+
+ // US-English separators for StringToDouble
+ sal_Unicode const cDecSep = '.';
+ sal_Unicode const cGrpSep = ',';
+
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ sal_uInt64 nOldPos = rStrm.Tell();
+ bool bData = !bSingle;
+ ::std::vector< sal_uInt32 > aFormats;
+
+ if( !bSingle)
+ bOk = StartPaste();
+
+ while( bOk )
+ {
+ OUString aLine;
+ OUString aText;
+ OStringBuffer aByteLine;
+ SCCOL nCol = nStartCol;
+ SCROW nRow = nStartRow;
+ SCCOL nRefCol = nCol;
+ SCROW nRefRow = nRow;
+ rStrm.Seek( nOldPos );
+ for( ;; )
+ {
+ //! allow unicode
+ rStrm.ReadLine( aByteLine );
+ aLine = OStringToOUString(aByteLine, rStrm.GetStreamCharSet());
+ if( rStrm.eof() )
+ break;
+ bool bInvalidCol = false;
+ bool bInvalidRow = false;
+ const sal_Unicode* p = aLine.getStr();
+ sal_Unicode cTag = *p++;
+ if( cTag == 'C' ) // Content
+ {
+ if( *p++ != ';' )
+ return false;
+
+ bool bInvalidRefCol = false;
+ bool bInvalidRefRow = false;
+ while( *p )
+ {
+ sal_Unicode ch = *p++;
+ ch = ScGlobal::ToUpperAlpha( ch );
+ switch( ch )
+ {
+ case 'X':
+ {
+ bInvalidCol = false;
+ bool bFail = o3tl::checked_add<SCCOL>(o3tl::toInt32(std::u16string_view(p)), nStartCol - 1, nCol);
+ if (bFail || nCol < 0 || rDoc.MaxCol() < nCol)
+ {
+ SAL_WARN("sc.ui","ScImportExport::Sylk2Doc - ;X invalid nCol=" << nCol);
+ nCol = std::clamp<SCCOL>(nCol, 0, rDoc.MaxCol());
+ bInvalidCol = bOverflowCol = true;
+ }
+ break;
+ }
+ case 'Y':
+ {
+ bInvalidRow = false;
+ bool bFail = o3tl::checked_add(o3tl::toInt32(std::u16string_view(p)), nStartRow - 1, nRow);
+ if (bFail || nRow < 0 || nMaxImportRow < nRow)
+ {
+ SAL_WARN("sc.ui","ScImportExport::Sylk2Doc - ;Y invalid nRow=" << nRow);
+ nRow = std::clamp<SCROW>(nRow, 0, nMaxImportRow);
+ bInvalidRow = bOverflowRow = true;
+ }
+ break;
+ }
+ case 'C':
+ {
+ bInvalidRefCol = false;
+ bool bFail = o3tl::checked_add<SCCOL>(o3tl::toInt32(std::u16string_view(p)), nStartCol - 1, nRefCol);
+ if (bFail || nRefCol < 0 || rDoc.MaxCol() < nRefCol)
+ {
+ SAL_WARN("sc.ui","ScImportExport::Sylk2Doc - ;C invalid nRefCol=" << nRefCol);
+ nRefCol = std::clamp<SCCOL>(nRefCol, 0, rDoc.MaxCol());
+ bInvalidRefCol = bOverflowCol = true;
+ }
+ break;
+ }
+ case 'R':
+ {
+ bInvalidRefRow = false;
+ bool bFail = o3tl::checked_add(o3tl::toInt32(std::u16string_view(p)), nStartRow - 1, nRefRow);
+ if (bFail || nRefRow < 0 || nMaxImportRow < nRefRow)
+ {
+ SAL_WARN("sc.ui","ScImportExport::Sylk2Doc - ;R invalid nRefRow=" << nRefRow);
+ nRefRow = std::clamp<SCROW>(nRefRow, 0, nMaxImportRow);
+ bInvalidRefRow = bOverflowRow = true;
+ }
+ break;
+ }
+ case 'K':
+ {
+ if( !bSingle &&
+ ( nCol < nStartCol || nCol > nEndCol
+ || nRow < nStartRow || nRow > nEndRow
+ || nCol > rDoc.MaxCol() || nRow > nMaxImportRow
+ || bInvalidCol || bInvalidRow ) )
+ break;
+ if( !bData )
+ {
+ if( nRow > nEndRow )
+ nEndRow = nRow;
+ if( nCol > nEndCol )
+ nEndCol = nCol;
+ break;
+ }
+ bool bText;
+ if( *p == '"' )
+ {
+ bText = true;
+ aText.clear();
+ p = lcl_ScanSylkString( p, aText, eVersion);
+ }
+ else
+ bText = false;
+ const sal_Unicode* q = p;
+ while( *q && *q != ';' )
+ q++;
+ if ( (*q != ';' || *(q+1) != 'I') && !bInvalidCol && !bInvalidRow )
+ { // don't ignore value
+ if( bText )
+ {
+ rDoc.EnsureTable(aRange.aStart.Tab());
+ rDoc.SetTextCell(
+ ScAddress(nCol, nRow, aRange.aStart.Tab()), aText);
+ }
+ else
+ {
+ double fVal = rtl_math_uStringToDouble( p,
+ aLine.getStr() + aLine.getLength(),
+ cDecSep, cGrpSep, nullptr, nullptr );
+ rDoc.SetValue( nCol, nRow, aRange.aStart.Tab(), fVal );
+ }
+ }
+ }
+ break;
+ case 'E':
+ case 'M':
+ {
+ if ( ch == 'M' )
+ {
+ if ( nRefCol < nCol )
+ nRefCol = nCol;
+ if ( nRefRow < nRow )
+ nRefRow = nRow;
+ if ( !bData )
+ {
+ if( nRefRow > nEndRow )
+ nEndRow = nRefRow;
+ if( nRefCol > nEndCol )
+ nEndCol = nRefCol;
+ }
+ }
+ if( !bMyDoc || !bData )
+ break;
+ aText = "=";
+ p = lcl_ScanSylkFormula( p, aText, eVersion);
+
+ if (bInvalidCol || bInvalidRow || (ch == 'M' && (bInvalidRefCol || bInvalidRefRow)))
+ break;
+
+ ScAddress aPos( nCol, nRow, aRange.aStart.Tab() );
+ /* FIXME: do we want GRAM_ODFF_A1 instead? At the
+ * end it probably should be GRAM_ODFF_R1C1, since
+ * R1C1 is what Excel writes in SYLK, or even
+ * better GRAM_ENGLISH_XL_R1C1. */
+ const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_PODF_A1;
+ ScCompiler aComp(rDoc, aPos, eGrammar);
+ std::unique_ptr<ScTokenArray> xCode(aComp.CompileString(aText)); // ctor/InsertMatrixFormula did copy TokenArray
+ rDoc.CheckLinkFormulaNeedingCheck(*xCode);
+ if ( ch == 'M' )
+ {
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectTable( aPos.Tab(), true );
+ rDoc.InsertMatrixFormula( nCol, nRow, nRefCol,
+ nRefRow, aMark, OUString(), xCode.get() );
+ }
+ else
+ {
+ ScFormulaCell* pFCell = new ScFormulaCell(
+ rDoc, aPos, *xCode, eGrammar, ScMatrixMode::NONE);
+ rDoc.SetFormulaCell(aPos, pFCell);
+ }
+ }
+ break;
+ }
+ while( *p && *p != ';' )
+ p++;
+ if( *p )
+ p++;
+ }
+ }
+ else if( cTag == 'F' ) // Format
+ {
+ if( *p++ != ';' )
+ return false;
+ sal_Int32 nFormat = -1;
+ while( *p )
+ {
+ sal_Unicode ch = *p++;
+ ch = ScGlobal::ToUpperAlpha( ch );
+ switch( ch )
+ {
+ case 'X':
+ {
+ bInvalidCol = false;
+ bool bFail = o3tl::checked_add<SCCOL>(o3tl::toInt32(std::u16string_view(p)), nStartCol - 1, nCol);
+ if (bFail || nCol < 0 || rDoc.MaxCol() < nCol)
+ {
+ SAL_WARN("sc.ui","ScImportExport::Sylk2Doc - ;X invalid nCol=" << nCol);
+ nCol = std::clamp<SCCOL>(nCol, 0, rDoc.MaxCol());
+ bInvalidCol = bOverflowCol = true;
+ }
+ break;
+ }
+ case 'Y':
+ {
+ bInvalidRow = false;
+ bool bFail = o3tl::checked_add(o3tl::toInt32(std::u16string_view(p)), nStartRow - 1, nRow);
+ if (bFail || nRow < 0 || nMaxImportRow < nRow)
+ {
+ SAL_WARN("sc.ui","ScImportExport::Sylk2Doc - ;Y invalid nRow=" << nRow);
+ nRow = std::clamp<SCROW>(nRow, 0, nMaxImportRow);
+ bInvalidRow = bOverflowRow = true;
+ }
+ break;
+ }
+ case 'P' :
+ if ( bData )
+ {
+ // F;P<n> sets format code of P;P<code> at
+ // current position, or at ;X;Y if specified.
+ // Note that ;X;Y may appear after ;P
+ const sal_Unicode* p0 = p;
+ while( *p && *p != ';' )
+ p++;
+ OUString aNumber(p0, p - p0);
+ nFormat = aNumber.toInt32();
+ }
+ break;
+ }
+ while( *p && *p != ';' )
+ p++;
+ if( *p )
+ p++;
+ }
+ if ( !bData )
+ {
+ if( nRow > nEndRow )
+ nEndRow = nRow;
+ if( nCol > nEndCol )
+ nEndCol = nCol;
+ }
+ if ( 0 <= nFormat && o3tl::make_unsigned(nFormat) < aFormats.size() && !bInvalidCol && !bInvalidRow )
+ {
+ sal_uInt32 nKey = aFormats[nFormat];
+ rDoc.ApplyAttr( nCol, nRow, aRange.aStart.Tab(),
+ SfxUInt32Item( ATTR_VALUE_FORMAT, nKey ) );
+ }
+ }
+ else if( cTag == 'P' )
+ {
+ if ( bData && *p == ';' && *(p+1) == 'P' )
+ {
+ OUString aCode( p+2 );
+
+ sal_uInt32 nKey;
+ sal_Int32 nCheckPos;
+
+ if (aCode.getLength() > 2048 && utl::ConfigManager::IsFuzzing())
+ {
+ // consider an excessive length as a failure when fuzzing
+ nCheckPos = 1;
+ }
+ else
+ {
+ // unescape doubled semicolons
+ aCode = aCode.replaceAll(";;", ";");
+ // get rid of Xcl escape characters
+ aCode = aCode.replaceAll("\x1b", "");
+ SvNumFormatType nType;
+ rDoc.GetFormatTable()->PutandConvertEntry( aCode, nCheckPos, nType, nKey,
+ LANGUAGE_ENGLISH_US, ScGlobal::eLnge, false);
+ }
+
+ if ( nCheckPos )
+ nKey = 0;
+
+ aFormats.push_back( nKey );
+ }
+ }
+ else if (cTag == 'I' && *p == 'D' && aLine.getLength() > 4)
+ {
+ aLine = aLine.copy(4);
+ if (aLine == "CALCOOO32")
+ eVersion = SylkVersion::OOO32;
+ else if (aLine == "SCALC3")
+ eVersion = SylkVersion::SCALC3;
+ bMyDoc = (eVersion <= SylkVersion::OWN);
+ }
+ else if( cTag == 'E' ) // End
+ break;
+ }
+ if( !bData )
+ {
+ aRange.aEnd.SetCol( nEndCol );
+ aRange.aEnd.SetRow( nEndRow );
+ bOk = StartPaste();
+ bData = true;
+ }
+ else
+ break;
+ }
+
+ EndPaste();
+ return bOk;
+}
+
+bool ScImportExport::Doc2Sylk( SvStream& rStrm )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ OUString aCellStr;
+ OUString aValStr;
+ lcl_WriteSimpleString( rStrm, u"ID;PCALCOOO32" );
+ WriteUnicodeOrByteEndl( rStrm );
+
+ for (nRow = nStartRow; nRow <= nEndRow; nRow++)
+ {
+ for (nCol = nStartCol; nCol <= nEndCol; nCol++)
+ {
+ OUString aBufStr;
+ double nVal;
+ bool bForm = false;
+ SCROW r = nRow - nStartRow + 1;
+ SCCOL c = nCol - nStartCol + 1;
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, aRange.aStart.Tab()));
+ CellType eType = aCell.getType();
+ switch( eType )
+ {
+ case CELLTYPE_FORMULA:
+ bForm = bFormulas;
+ if( rDoc.HasValueData( nCol, nRow, aRange.aStart.Tab()) )
+ goto hasvalue;
+ else
+ goto hasstring;
+
+ case CELLTYPE_VALUE:
+ hasvalue:
+ nVal = rDoc.GetValue( nCol, nRow, aRange.aStart.Tab() );
+
+ aValStr = ::rtl::math::doubleToUString( nVal,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true );
+
+ aBufStr = "C;X"
+ + OUString::number( c )
+ + ";Y"
+ + OUString::number( r )
+ + ";K"
+ + aValStr;
+ lcl_WriteSimpleString( rStrm, aBufStr );
+ goto checkformula;
+
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ hasstring:
+ aCellStr = rDoc.GetString(nCol, nRow, aRange.aStart.Tab());
+ aCellStr = aCellStr.replaceAll("\n", SYLK_LF);
+
+ aBufStr = "C;X"
+ + OUString::number( c )
+ + ";Y"
+ + OUString::number( r )
+ + ";K";
+ lcl_WriteSimpleString( rStrm, aBufStr );
+ lcl_WriteString( rStrm, aCellStr, '"', ';' );
+
+ checkformula:
+ if( bForm )
+ {
+ const ScFormulaCell* pFCell = aCell.getFormula();
+ switch ( pFCell->GetMatrixFlag() )
+ {
+ case ScMatrixMode::Reference :
+ aCellStr.clear();
+ break;
+ default:
+ aCellStr = pFCell->GetFormula( formula::FormulaGrammar::GRAM_PODF_A1);
+ /* FIXME: do we want GRAM_ODFF_A1 instead? At
+ * the end it probably should be
+ * GRAM_ODFF_R1C1, since R1C1 is what Excel
+ * writes in SYLK, or even better
+ * GRAM_ENGLISH_XL_R1C1. */
+ }
+ if ( pFCell->GetMatrixFlag() != ScMatrixMode::NONE &&
+ aCellStr.startsWith("{") &&
+ aCellStr.endsWith("}") )
+ { // cut off matrix {} characters
+ aCellStr = aCellStr.copy(1, aCellStr.getLength()-2);
+ }
+ if ( aCellStr[0] == '=' )
+ aCellStr = aCellStr.copy(1);
+ OUString aPrefix;
+ switch ( pFCell->GetMatrixFlag() )
+ {
+ case ScMatrixMode::Formula :
+ { // diff expression with 'M' M$-extension
+ SCCOL nC;
+ SCROW nR;
+ pFCell->GetMatColsRows( nC, nR );
+ nC += c - 1;
+ nR += r - 1;
+ aPrefix = ";R"
+ + OUString::number( nR )
+ + ";C"
+ + OUString::number( nC )
+ + ";M";
+ }
+ break;
+ case ScMatrixMode::Reference :
+ { // diff expression with 'I' M$-extension
+ ScAddress aPos;
+ (void)pFCell->GetMatrixOrigin( rDoc, aPos );
+ aPrefix = ";I;R"
+ + OUString::number( aPos.Row() - nStartRow + 1 )
+ + ";C"
+ + OUString::number( aPos.Col() - nStartCol + 1 );
+ }
+ break;
+ default:
+ // formula Expression
+ aPrefix = ";E";
+ }
+ lcl_WriteSimpleString( rStrm, aPrefix );
+ if ( !aCellStr.isEmpty() )
+ lcl_WriteString( rStrm, aCellStr, 0, ';' );
+ }
+ WriteUnicodeOrByteEndl( rStrm );
+ break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ lcl_WriteSimpleString( rStrm, rtl::OUStringChar( 'E' ) );
+ WriteUnicodeOrByteEndl( rStrm );
+ return rStrm.GetError() == ERRCODE_NONE;
+}
+
+bool ScImportExport::Doc2HTML( SvStream& rStrm, const OUString& rBaseURL )
+{
+ // rtl_TextEncoding is ignored in ScExportHTML, read from Load/Save HTML options
+ ScFormatFilter::Get().ScExportHTML( rStrm, rBaseURL, &rDoc, aRange, RTL_TEXTENCODING_DONTKNOW, bAll,
+ aStreamPath, aNonConvertibleChars, maFilterOptions );
+ return rStrm.GetError() == ERRCODE_NONE;
+}
+
+bool ScImportExport::Doc2RTF( SvStream& rStrm )
+{
+ // rtl_TextEncoding is ignored in ScExportRTF
+ ScFormatFilter::Get().ScExportRTF( rStrm, &rDoc, aRange, RTL_TEXTENCODING_DONTKNOW );
+ return rStrm.GetError() == ERRCODE_NONE;
+}
+
+bool ScImportExport::Doc2Dif( SvStream& rStrm )
+{
+ // for DIF in the clipboard, IBM_850 is always used
+ ScFormatFilter::Get().ScExportDif( rStrm, &rDoc, aRange, RTL_TEXTENCODING_IBM_850 );
+ return true;
+}
+
+bool ScImportExport::Dif2Doc( SvStream& rStrm )
+{
+ SCTAB nTab = aRange.aStart.Tab();
+ ScDocumentUniquePtr pImportDoc( new ScDocument( SCDOCMODE_UNDO ) );
+ pImportDoc->InitUndo( rDoc, nTab, nTab );
+
+ // for DIF in the clipboard, IBM_850 is always used
+ ScFormatFilter::Get().ScImportDif( rStrm, pImportDoc.get(), aRange.aStart, RTL_TEXTENCODING_IBM_850 );
+
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pImportDoc->GetCellArea( nTab, nEndCol, nEndRow );
+ // if there are no cells in the imported content, nEndCol/nEndRow may be before the start
+ if ( nEndCol < aRange.aStart.Col() )
+ nEndCol = aRange.aStart.Col();
+ if ( nEndRow < aRange.aStart.Row() )
+ nEndRow = aRange.aStart.Row();
+ aRange.aEnd = ScAddress( nEndCol, nEndRow, nTab );
+
+ bool bOk = StartPaste();
+ if (bOk)
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::ALL & ~InsertDeleteFlags::STYLES;
+ rDoc.DeleteAreaTab( aRange, nFlags );
+ pImportDoc->CopyToDocument(aRange, nFlags, false, rDoc);
+ EndPaste();
+ }
+
+ return bOk;
+}
+
+bool ScImportExport::RTF2Doc( SvStream& rStrm, const OUString& rBaseURL )
+{
+ std::unique_ptr<ScEEAbsImport> pImp = ScFormatFilter::Get().CreateRTFImport( &rDoc, aRange );
+ if (!pImp)
+ return false;
+ pImp->Read( rStrm, rBaseURL );
+ aRange = pImp->GetRange();
+
+ bool bOk = StartPaste();
+ if (bOk)
+ {
+ InsertDeleteFlags const nFlags = InsertDeleteFlags::ALL & ~InsertDeleteFlags::STYLES;
+ rDoc.DeleteAreaTab( aRange, nFlags );
+ pImp->WriteToDocument();
+ EndPaste();
+ }
+ return bOk;
+}
+
+bool ScImportExport::HTML2Doc( SvStream& rStrm, const OUString& rBaseURL )
+{
+ std::unique_ptr<ScEEAbsImport> pImp = ScFormatFilter::Get().CreateHTMLImport( &rDoc, rBaseURL, aRange);
+ if (!pImp)
+ return false;
+ pImp->Read( rStrm, rBaseURL );
+ aRange = pImp->GetRange();
+
+ bool bOk = StartPaste();
+ if (bOk)
+ {
+ // ScHTMLImport may call ScDocument::InitDrawLayer, resulting in
+ // a Draw Layer but no Draw View -> create Draw Layer and View here
+ if (pDocSh)
+ pDocSh->MakeDrawLayer();
+
+ InsertDeleteFlags const nFlags = InsertDeleteFlags::ALL & ~InsertDeleteFlags::STYLES;
+ rDoc.DeleteAreaTab( aRange, nFlags );
+
+ if (pExtOptions)
+ {
+ // Pick up import options if available.
+ LanguageType eLang = pExtOptions->GetLanguage();
+ SvNumberFormatter aNumFormatter( comphelper::getProcessComponentContext(), eLang);
+ bool bSpecialNumber = pExtOptions->IsDetectSpecialNumber();
+ bool bScientificNumber = pExtOptions->IsDetectScientificNumber();
+ pImp->WriteToDocument(false, 1.0, &aNumFormatter, bSpecialNumber, bScientificNumber);
+ }
+ else
+ // Regular import, with no options.
+ pImp->WriteToDocument();
+
+ EndPaste();
+ }
+ return bOk;
+}
+
+#ifndef DISABLE_DYNLOADING
+
+extern "C" { static void thisModule() {} }
+
+#else
+
+extern "C" {
+ScFormatFilterPlugin* ScFilterCreate();
+}
+
+#endif
+
+typedef ScFormatFilterPlugin * (*FilterFn)();
+ScFormatFilterPlugin &ScFormatFilter::Get()
+{
+ static ScFormatFilterPlugin *plugin = []()
+ {
+#ifndef DISABLE_DYNLOADING
+ OUString sFilterLib(SVLIBRARY("scfilt"));
+ static ::osl::Module aModule;
+ bool bLoaded = aModule.is();
+ if (!bLoaded)
+ bLoaded = aModule.loadRelative(&thisModule, sFilterLib);
+ if (!bLoaded)
+ bLoaded = aModule.load(sFilterLib);
+ if (bLoaded)
+ {
+ oslGenericFunction fn = aModule.getFunctionSymbol( "ScFilterCreate" );
+ if (fn != nullptr)
+ return reinterpret_cast<FilterFn>(fn)();
+ }
+ assert(false);
+ return static_cast<ScFormatFilterPlugin*>(nullptr);
+#else
+ return ScFilterCreate();
+#endif
+ }();
+
+ return *plugin;
+}
+
+// Precondition: pStr is guaranteed to be non-NULL and points to a 0-terminated
+// array.
+static const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr,
+ sal_Unicode c )
+{
+ while (*pStr)
+ {
+ if (*pStr == c)
+ return pStr;
+ ++pStr;
+ }
+ return nullptr;
+}
+
+ScImportStringStream::ScImportStringStream( const OUString& rStr )
+ : SvMemoryStream( const_cast<sal_Unicode *>(rStr.getStr()),
+ rStr.getLength() * sizeof(sal_Unicode), StreamMode::READ)
+{
+ SetStreamCharSet( RTL_TEXTENCODING_UNICODE );
+#ifdef OSL_BIGENDIAN
+ SetEndian(SvStreamEndian::BIG);
+#else
+ SetEndian(SvStreamEndian::LITTLE);
+#endif
+}
+
+OUString ReadCsvLine( SvStream &rStream, bool bEmbeddedLineBreak,
+ OUString& rFieldSeparators, sal_Unicode cFieldQuote, sal_Unicode& rcDetectSep, sal_uInt32 nMaxSourceLines )
+{
+ enum RetryState
+ {
+ FORBID,
+ ALLOW,
+ RETRY,
+ RETRIED
+ } eRetryState = (bEmbeddedLineBreak && rcDetectSep == 0 ? RetryState::ALLOW : RetryState::FORBID);
+
+ sal_uInt64 nStreamPos = (eRetryState == RetryState::ALLOW ? rStream.Tell() : 0);
+
+Label_RetryWithNewSep:
+
+ if (eRetryState == RetryState::RETRY)
+ {
+ eRetryState = RetryState::RETRIED;
+ rStream.Seek( nStreamPos);
+ }
+
+ OUString aStr;
+ rStream.ReadUniOrByteStringLine(aStr, rStream.GetStreamCharSet(), nArbitraryLineLengthLimit);
+
+ if (bEmbeddedLineBreak)
+ {
+ sal_Int32 nFirstLineLength = aStr.getLength();
+ sal_uInt64 nFirstLineStreamPos = rStream.Tell();
+ sal_uInt32 nLine = 0;
+
+ const sal_Unicode* pSeps = rFieldSeparators.getStr();
+
+ QuoteType eQuoteState = FIELDEND_QUOTE;
+ bool bFieldStart = true;
+
+ sal_Int32 nLastOffset = 0;
+ sal_Int32 nQuotes = 0;
+ while (!rStream.eof() && aStr.getLength() < nArbitraryLineLengthLimit)
+ {
+ const sal_Unicode * p = aStr.getStr() + nLastOffset;
+ const sal_Unicode * const pStop = aStr.getStr() + aStr.getLength();
+ while (p < pStop)
+ {
+ if (!*p)
+ {
+ // Skip embedded null-characters. They don't change
+ // anything and are handled at a higher level.
+ ++p;
+ continue;
+ }
+
+ if (nQuotes)
+ {
+ if (*p == cFieldQuote)
+ {
+ if (bFieldStart)
+ {
+ ++nQuotes;
+ bFieldStart = false;
+ eQuoteState = FIELDSTART_QUOTE;
+ nFirstLineLength = aStr.getLength();
+ nFirstLineStreamPos = rStream.Tell();
+ }
+ // Do not detect a FIELDSTART_QUOTE if not in
+ // bFieldStart mode, in which case for unquoted content
+ // we are in FIELDEND_QUOTE state.
+ else if (eQuoteState != FIELDEND_QUOTE)
+ {
+ eQuoteState = lcl_isEscapedOrFieldEndQuote( nQuotes, p, pSeps, cFieldQuote, rcDetectSep);
+
+ if (eRetryState == RetryState::ALLOW && rcDetectSep)
+ {
+ eRetryState = RetryState::RETRY;
+ rFieldSeparators += OUStringChar(rcDetectSep);
+ pSeps = rFieldSeparators.getStr();
+ goto Label_RetryWithNewSep;
+ }
+
+ // DONTKNOW_QUOTE is an embedded unescaped quote we
+ // don't count for pairing.
+ if (eQuoteState != DONTKNOW_QUOTE)
+ ++nQuotes;
+ }
+ }
+ else if (eQuoteState == FIELDEND_QUOTE)
+ {
+ if (bFieldStart)
+ // If blank is a separator it starts a field, if it
+ // is not and thus maybe leading before quote we
+ // are still at start of field regarding quotes.
+ bFieldStart = (*p == ' ' || lcl_UnicodeStrChr( pSeps, *p) != nullptr);
+ else
+ bFieldStart = (lcl_UnicodeStrChr( pSeps, *p) != nullptr);
+ }
+ }
+ else
+ {
+ if (*p == cFieldQuote && bFieldStart)
+ {
+ nQuotes = 1;
+ eQuoteState = FIELDSTART_QUOTE;
+ bFieldStart = false;
+ nFirstLineLength = aStr.getLength();
+ nFirstLineStreamPos = rStream.Tell();
+ }
+ else if (eQuoteState == FIELDEND_QUOTE)
+ {
+ // This also skips leading blanks at beginning of line
+ // if followed by a quote. It's debatable whether we
+ // actually want that or not, but congruent with what
+ // ScanNextFieldFromString() does.
+ if (bFieldStart)
+ bFieldStart = (*p == ' ' || lcl_UnicodeStrChr( pSeps, *p) != nullptr);
+ else
+ bFieldStart = (lcl_UnicodeStrChr( pSeps, *p) != nullptr);
+ }
+ }
+ // A quote character inside a field content does not start
+ // a quote.
+ ++p;
+ }
+
+ if ((nQuotes & 1) == 0)
+ // We still have a (theoretical?) problem here if due to
+ // nArbitraryLineLengthLimit (or nMaxSourceLines below) we
+ // split a string right between a doubled quote pair.
+ break;
+ else if (eQuoteState == DONTKNOW_QUOTE)
+ // A single unescaped quote somewhere in a quote started
+ // field, most likely that was not meant to have embedded
+ // linefeeds either.
+ break;
+ else if (++nLine >= nMaxSourceLines && nMaxSourceLines > 0)
+ // Unconditionally increment nLine even if nMaxSourceLines==0
+ // so it can be observed in debugger.
+ break;
+ else
+ {
+ nLastOffset = aStr.getLength();
+ OUString aNext;
+ rStream.ReadUniOrByteStringLine(aNext, rStream.GetStreamCharSet(), nArbitraryLineLengthLimit);
+ if (!rStream.eof())
+ aStr += "\n" + aNext;
+ }
+ }
+ if (nQuotes & 1)
+ {
+ // No closing quote at all. A single quote at field start => no
+ // embedded linefeeds for that field, take only first logical line.
+ aStr = aStr.copy( 0, nFirstLineLength);
+ rStream.Seek( nFirstLineStreamPos);
+ }
+ }
+ return aStr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/macromgr.cxx b/sc/source/ui/docshell/macromgr.cxx
new file mode 100644
index 0000000000..455571e791
--- /dev/null
+++ b/sc/source/ui/docshell/macromgr.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 <macromgr.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <basic/basmgr.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/objsh.hxx>
+#include <formulacell.hxx>
+#include <config_features.h>
+#include <vector>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::std::vector;
+using ::std::pair;
+
+/**
+ * A simple container to keep track of cells that depend on basic modules
+ * changes. We don't check for duplicates at insertion time; instead, we
+ * remove duplicates at query time.
+ */
+class ScUserMacroDepTracker
+{
+public:
+ void addCell(const OUString& rModuleName, ScFormulaCell* pCell)
+ {
+ ModuleCellMap::iterator itr = maCells.find(rModuleName);
+ if (itr == maCells.end())
+ {
+ pair<ModuleCellMap::iterator, bool> r = maCells.emplace(
+ rModuleName, vector<ScFormulaCell*>());
+
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(pCell);
+ }
+
+ void removeCell(const ScFormulaCell* pCell)
+ {
+ for (auto& rEntry : maCells)
+ {
+ std::erase(rEntry.second, pCell);
+ }
+ }
+
+ void getCellsByModule(const OUString& rModuleName, vector<ScFormulaCell*>& rCells)
+ {
+ ModuleCellMap::iterator itr = maCells.find(rModuleName);
+ if (itr == maCells.end())
+ return;
+
+ vector<ScFormulaCell*>& rCellList = itr->second;
+
+ // Remove duplicates.
+ std::sort(rCellList.begin(), rCellList.end());
+ auto last = std::unique(rCellList.begin(), rCellList.end());
+ rCellList.erase(last, rCellList.end());
+
+ // exception safe copy
+ vector<ScFormulaCell*> temp(rCellList);
+ rCells.swap(temp);
+ }
+
+private:
+ typedef std::unordered_map<OUString, vector<ScFormulaCell*>> ModuleCellMap;
+ ModuleCellMap maCells;
+};
+
+ScMacroManager::ScMacroManager(ScDocument& rDoc) :
+ mpDepTracker(new ScUserMacroDepTracker),
+ mrDoc(rDoc)
+{
+}
+
+ScMacroManager::~ScMacroManager()
+{
+}
+
+typedef ::cppu::WeakImplHelper< css::container::XContainerListener > ContainerListenerHelper;
+
+namespace {
+
+class VBAProjectListener : public ContainerListenerHelper
+{
+ ScMacroManager* mpMacroMgr;
+public:
+ explicit VBAProjectListener( ScMacroManager* pMacroMgr ) : mpMacroMgr( pMacroMgr ) {}
+ // XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& /*Source*/ ) override {}
+
+ // XContainerListener
+ virtual void SAL_CALL elementInserted( const container::ContainerEvent& /*Event*/ ) override {}
+ virtual void SAL_CALL elementReplaced( const container::ContainerEvent& Event ) override
+ {
+ OUString sModuleName;
+ Event.Accessor >>= sModuleName;
+ mpMacroMgr->InitUserFuncData();
+ mpMacroMgr->BroadcastModuleUpdate(sModuleName);
+ }
+ virtual void SAL_CALL elementRemoved( const container::ContainerEvent& /*Event*/ ) override {}
+
+};
+
+}
+
+void ScMacroManager::InitUserFuncData()
+{
+ // Clear unordered_map
+ mhFuncToVolatile.clear();
+ OUString sProjectName("Standard");
+
+ Reference< container::XContainer > xModuleContainer;
+ ScDocShell* pShell = mrDoc.GetDocumentShell();
+ if (!pShell)
+ return;
+#if HAVE_FEATURE_SCRIPTING
+ const BasicManager *pBasicManager = pShell->GetBasicManager();
+ if (!pBasicManager->GetName().isEmpty())
+ {
+ sProjectName = pBasicManager->GetName();
+ }
+#endif
+ try
+ {
+ Reference< script::XLibraryContainer > xLibraries( pShell->GetBasicContainer(), uno::UNO_SET_THROW );
+ xModuleContainer.set( xLibraries->getByName( sProjectName ), uno::UNO_QUERY_THROW );
+
+ // remove old listener ( if there was one )
+ if ( mxContainerListener.is() )
+ xModuleContainer->removeContainerListener( mxContainerListener );
+ // Create listener
+ mxContainerListener = new VBAProjectListener( this );
+ xModuleContainer->addContainerListener( mxContainerListener );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+void ScMacroManager::SetUserFuncVolatile( const OUString& sName, bool isVolatile )
+{
+ mhFuncToVolatile[ sName ] = isVolatile;
+}
+
+bool ScMacroManager::GetUserFuncVolatile( const OUString& sName )
+{
+ NameBoolMap::iterator it = mhFuncToVolatile.find( sName );
+ if ( it == mhFuncToVolatile.end() )
+ return false;
+ return it->second;
+}
+
+void ScMacroManager::AddDependentCell(const OUString& aModuleName, ScFormulaCell* pCell)
+{
+ mpDepTracker->addCell(aModuleName, pCell);
+}
+
+void ScMacroManager::RemoveDependentCell(const ScFormulaCell* pCell)
+{
+ mpDepTracker->removeCell(pCell);
+}
+
+void ScMacroManager::BroadcastModuleUpdate(const OUString& aModuleName)
+{
+ vector<ScFormulaCell*> aCells;
+ mpDepTracker->getCellsByModule(aModuleName, aCells);
+ for (ScFormulaCell* pCell : aCells)
+ {
+ mrDoc.PutInFormulaTree(pCell); // for F9 recalc
+
+ // for recalc on cell value change. If the cell is not volatile, the
+ // cell stops listening right away after it gets re-interpreted.
+ mrDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, pCell);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/olinefun.cxx b/sc/source/ui/docshell/olinefun.cxx
new file mode 100644
index 0000000000..3b15989c36
--- /dev/null
+++ b/sc/source/ui/docshell/olinefun.cxx
@@ -0,0 +1,797 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/bindings.hxx>
+
+#include <olinefun.hxx>
+
+#include <docsh.hxx>
+#include <olinetab.hxx>
+#include <tabvwsh.hxx>
+#include <undodat.hxx>
+#include <globstr.hrc>
+#include <sc.hrc>
+
+#include <comphelper/lok.hxx>
+
+
+static void lcl_InvalidateOutliner( SfxBindings* pBindings )
+{
+ if ( pBindings )
+ {
+ pBindings->Invalidate( SID_OUTLINE_SHOW );
+ pBindings->Invalidate( SID_OUTLINE_HIDE );
+ pBindings->Invalidate( SID_OUTLINE_REMOVE );
+
+ pBindings->Invalidate( SID_STATUS_SUM ); // because of enabling/disabling
+ pBindings->Invalidate( SID_ATTR_SIZE );
+ }
+}
+
+//! Move PaintWidthHeight to DocShell ?
+
+static void lcl_PaintWidthHeight( ScDocShell& rDocShell, SCTAB nTab,
+ bool bColumns, SCCOLROW nStart, SCCOLROW nEnd )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ PaintPartFlags nParts = PaintPartFlags::Grid;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = rDoc.MaxCol(); // for testing if merged
+ SCROW nEndRow = rDoc.MaxRow();
+ if ( bColumns )
+ {
+ nParts |= PaintPartFlags::Top;
+ nStartCol = static_cast<SCCOL>(nStart);
+ nEndCol = static_cast<SCCOL>(nEnd);
+ }
+ else
+ {
+ nParts |= PaintPartFlags::Left;
+ nStartRow = nStart;
+ nEndRow = nEnd;
+ }
+ if (rDoc.HasAttrib( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ nStartCol = 0;
+ nStartRow = 0;
+ }
+ rDocShell.PostPaint( nStartCol,nStartRow,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, nParts );
+}
+
+void ScOutlineDocFunc::MakeOutline( const ScRange& rRange, bool bColumns, bool bRecord, bool bApi )
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab, true );
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if (bRecord)
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ ScOutlineArray& rArray = bColumns ? pTable->GetColArray() : pTable->GetRowArray();
+
+ bool bRes;
+ bool bSize = false;
+ if ( bColumns )
+ bRes = rArray.Insert( nStartCol, nEndCol, bSize );
+ else
+ bRes = rArray.Insert( nStartRow, nEndRow, bSize );
+
+ if ( bRes )
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMakeOutline>( &rDocShell,
+ nStartCol,nStartRow,nTab,nEndCol,nEndRow,nTab,
+ std::move(pUndoTab), bColumns, true ) );
+ }
+
+ rDoc.SetStreamValid(nTab, false);
+
+ PaintPartFlags nParts = PaintPartFlags::NONE; // Data range hasn't been changed
+ if ( bColumns )
+ nParts |= PaintPartFlags::Top;
+ else
+ nParts |= PaintPartFlags::Left;
+ if ( bSize )
+ nParts |= PaintPartFlags::Size;
+
+ rDocShell.PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, nParts );
+ rDocShell.SetDocumentModified();
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+ }
+ else
+ {
+ if (!bApi)
+ rDocShell.ErrorMessage(STR_MSSG_MAKEOUTLINE_0); // "Grouping not possible"
+ }
+}
+
+void ScOutlineDocFunc::RemoveOutline( const ScRange& rRange, bool bColumns, bool bRecord, bool bApi )
+{
+ bool bDone = false;
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ if (bRecord)
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ ScOutlineArray& rArray = bColumns ? pTable->GetColArray() : pTable->GetRowArray();
+
+ bool bRes;
+ bool bSize = false;
+ if ( bColumns )
+ bRes = rArray.Remove( nStartCol, nEndCol, bSize );
+ else
+ bRes = rArray.Remove( nStartRow, nEndRow, bSize );
+
+ if ( bRes )
+ {
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMakeOutline>( &rDocShell,
+ nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
+ std::move(pUndoTab), bColumns, false ) );
+ }
+
+ rDoc.SetStreamValid(nTab, false);
+
+ PaintPartFlags nParts = PaintPartFlags::NONE; // Data range hasn't been changed
+ if ( bColumns )
+ nParts |= PaintPartFlags::Top;
+ else
+ nParts |= PaintPartFlags::Left;
+ if ( bSize )
+ nParts |= PaintPartFlags::Size;
+
+ rDocShell.PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, nParts );
+ rDocShell.SetDocumentModified();
+ bDone = true;
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+
+ // we are not enabling again -> no UpdatePageBreaks
+ }
+ }
+
+ if (!bDone && !bApi)
+ rDocShell.ErrorMessage(STR_MSSG_REMOVEOUTLINE_0); // "Ungrouping not possible"
+}
+
+bool ScOutlineDocFunc::RemoveAllOutlines( SCTAB nTab, bool bRecord )
+{
+ bool bSuccess = false;
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ if (bRecord)
+ {
+ SCCOLROW nCol1, nCol2, nRow1, nRow2;
+ pTable->GetColArray().GetRange( nCol1, nCol2 );
+ pTable->GetRowArray().GetRange( nRow1, nRow2 );
+ SCCOL nStartCol = static_cast<SCCOL>(nCol1);
+ SCROW nStartRow = nRow1;
+ SCCOL nEndCol = static_cast<SCCOL>(nCol2);
+ SCROW nEndRow = nRow2;
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(nStartCol, 0, nTab, nEndCol, rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ rDoc.CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+
+ std::unique_ptr<ScOutlineTable> pUndoTab(new ScOutlineTable( *pTable ));
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveAllOutlines>( &rDocShell,
+ nStartCol, nStartRow, nTab,
+ nEndCol, nEndRow, nTab,
+ std::move(pUndoDoc), std::move(pUndoTab) ) );
+ }
+
+ SelectLevel( nTab, true, pTable->GetColArray().GetDepth(), false, false );
+ SelectLevel( nTab, false, pTable->GetRowArray().GetDepth(), false, false );
+ rDoc.SetOutlineTable( nTab, nullptr );
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ rDoc.SetStreamValid(nTab, false);
+
+ rDocShell.PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size );
+ rDocShell.SetDocumentModified();
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+ bSuccess = true;
+ }
+
+ return bSuccess;
+}
+
+void ScOutlineDocFunc::AutoOutline( const ScRange& rRange, bool bRecord )
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+
+ if ( pTable )
+ {
+ if ( bRecord )
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ SCCOLROW nCol1, nCol2, nRow1, nRow2;
+ pTable->GetColArray().GetRange( nCol1, nCol2 );
+ pTable->GetRowArray().GetRange( nRow1, nRow2 );
+ SCCOL nOutStartCol = static_cast<SCCOL>(nCol1);
+ SCROW nOutStartRow = nRow1;
+ SCCOL nOutEndCol = static_cast<SCCOL>(nCol2);
+ SCROW nOutEndRow = nRow2;
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(nOutStartCol, 0, nTab, nOutEndCol, rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ rDoc.CopyToDocument(0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+
+ // enable
+ SelectLevel( nTab, true, pTable->GetColArray().GetDepth(), false, false );
+ SelectLevel( nTab, false, pTable->GetRowArray().GetDepth(), false, false );
+ rDoc.SetOutlineTable( nTab, nullptr );
+ }
+
+ rDoc.DoAutoOutline( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
+
+ if (bRecord)
+ {
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoOutline>( &rDocShell,
+ nStartCol, nStartRow, nTab,
+ nEndCol, nEndRow, nTab,
+ std::move(pUndoDoc), std::move(pUndoTab) ) );
+ }
+
+ rDoc.SetStreamValid(nTab, false);
+
+ rDocShell.PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size );
+ rDocShell.SetDocumentModified();
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+}
+
+bool ScOutlineDocFunc::SelectLevel( SCTAB nTab, bool bColumns, sal_uInt16 nLevel,
+ bool bRecord, bool bPaint )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab ); // already there
+ if (!pTable)
+ return false;
+ ScOutlineArray& rArray = bColumns ? pTable->GetColArray() : pTable->GetRowArray();
+
+ SCCOLROW nStart, nEnd;
+ rArray.GetRange( nStart, nEnd );
+
+ // TODO undo can mess things up when another view is editing a cell in the range of group entry
+ // this is a temporarily workaround
+ if (!comphelper::LibreOfficeKit::isActive() && bRecord )
+ {
+ std::unique_ptr<ScOutlineTable> pUndoTab(new ScOutlineTable( *pTable ));
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ if (bColumns)
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ rDoc.CopyToDocument(static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false,
+ *pUndoDoc);
+ }
+ else
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ rDoc.CopyToDocument(0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoOutlineLevel>( &rDocShell,
+ nStart, nEnd, nTab, //! calculate start and end
+ std::move(pUndoDoc), std::move(pUndoTab),
+ bColumns, nLevel ) );
+ }
+
+ ScSubOutlineIterator aIter( &rArray ); // all entries
+ ScOutlineEntry* pEntry;
+ while ((pEntry=aIter.GetNext()) != nullptr)
+ {
+ SCCOLROW nThisStart = pEntry->GetStart();
+ SCCOLROW nThisEnd = pEntry->GetEnd();
+
+ sal_uInt16 nThisLevel = aIter.LastLevel();
+ bool bShow = (nThisLevel < nLevel);
+
+ if (!bShow && pViewSh && ScTabViewShell::isAnyEditViewInRange(pViewSh, bColumns, nThisStart, nThisEnd))
+ continue;
+
+ if (bShow) // enable
+ {
+ pEntry->SetHidden( false );
+ pEntry->SetVisible( true );
+ }
+ else if ( nThisLevel == nLevel ) // disable
+ {
+ pEntry->SetHidden( true );
+ pEntry->SetVisible( true );
+ }
+ else // hidden below
+ {
+ if (comphelper::LibreOfficeKit::isActive() && nThisLevel > 0)
+ {
+ pEntry->SetHidden( true );
+ const ScOutlineEntry* pParentEntry = rArray.GetEntryByPos(nThisLevel - 1, nThisStart);
+ if (pParentEntry && pParentEntry->IsHidden())
+ pEntry->SetVisible( false );
+ }
+ else
+ {
+ pEntry->SetVisible( false );
+ }
+ }
+
+ for (SCCOLROW i=nThisStart; i<=nThisEnd; i++)
+ {
+ if ( bColumns )
+ rDoc.ShowCol( static_cast<SCCOL>(i), nTab, bShow );
+ else
+ {
+ // show several rows together, don't show filtered rows
+ SCROW nFilterEnd = i;
+ bool bFiltered = rDoc.RowFiltered( i, nTab, nullptr, &nFilterEnd );
+ nFilterEnd = std::min( nThisEnd, nFilterEnd );
+ if ( !bShow || !bFiltered )
+ rDoc.ShowRows( i, nFilterEnd, nTab, bShow );
+ i = nFilterEnd;
+ }
+ }
+ }
+
+ rDoc.SetDrawPageSize(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ if ( pViewSh )
+ pViewSh->OnLOKShowHideColRow(bColumns, nStart - 1);
+
+ if (bPaint)
+ lcl_PaintWidthHeight( rDocShell, nTab, bColumns, nStart, nEnd );
+
+ rDocShell.SetDocumentModified();
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+
+ return true;
+}
+
+bool ScOutlineDocFunc::ShowMarkedOutlines( const ScRange& rRange, bool bRecord )
+{
+ bool bDone = false;
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+
+ if (pTable)
+ {
+ ScOutlineEntry* pEntry;
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ SCCOLROW nMin;
+ SCCOLROW nMax;
+ SCCOLROW i;
+
+ // TODO undo can mess things up when another view is editing a cell in the range of group entry
+ // this is a temporarily workaround
+ if ( !comphelper::LibreOfficeKit::isActive() && bRecord )
+ {
+ std::unique_ptr<ScOutlineTable> pUndoTab(new ScOutlineTable( *pTable ));
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(nStartCol, 0, nTab, nEndCol, rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ rDoc.CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoOutlineBlock>( &rDocShell,
+ nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab,
+ std::move(pUndoDoc), std::move(pUndoTab), true ) );
+ }
+
+ // Columns
+
+ nMin=rDoc.MaxCol();
+ nMax=0;
+ ScOutlineArray& rColArray = pTable->GetColArray();
+ ScSubOutlineIterator aColIter( &rColArray );
+ while ((pEntry=aColIter.GetNext()) != nullptr)
+ {
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( nStart>=nStartCol && nEnd<=nEndCol )
+ {
+ pEntry->SetHidden( false );
+ pEntry->SetVisible( true );
+ if (nStart<nMin) nMin=nStart;
+ if (nEnd>nMax) nMax=nEnd;
+ }
+ }
+ const SCCOLROW nMinStartCol = nMin;
+ for ( i=nMin; i<=nMax; i++ )
+ rDoc.ShowCol( static_cast<SCCOL>(i), nTab, true );
+
+ // Rows
+
+ nMin=rDoc.MaxRow();
+ nMax=0;
+ ScOutlineArray& rRowArray = pTable->GetRowArray();
+ ScSubOutlineIterator aRowIter( &rRowArray );
+ while ((pEntry=aRowIter.GetNext()) != nullptr)
+ {
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( nStart>=nStartRow && nEnd<=nEndRow )
+ {
+ pEntry->SetHidden( false );
+ pEntry->SetVisible( true );
+ if (nStart<nMin) nMin=nStart;
+ if (nEnd>nMax) nMax=nEnd;
+ }
+ }
+ const SCCOLROW nMinStartRow = nMin;
+ for ( i=nMin; i<=nMax; i++ )
+ {
+ // show several rows together, don't show filtered rows
+ SCROW nFilterEnd = i;
+ bool bFiltered = rDoc.RowFiltered( i, nTab, nullptr, &nFilterEnd );
+ nFilterEnd = std::min( nMax, nFilterEnd );
+ if ( !bFiltered )
+ rDoc.ShowRows( i, nFilterEnd, nTab, true );
+ i = nFilterEnd;
+ }
+
+
+ rDoc.SetDrawPageSize(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if ( pViewSh )
+ {
+ pViewSh->OnLOKShowHideColRow(/*columns: */ true, nMinStartCol - 1);
+ pViewSh->OnLOKShowHideColRow(/*columns: */ false, nMinStartRow - 1);
+ }
+
+ rDocShell.PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top );
+ rDocShell.SetDocumentModified();
+ bDone = true;
+
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+ }
+
+ return bDone;
+}
+
+bool ScOutlineDocFunc::HideMarkedOutlines( const ScRange& rRange, bool bRecord )
+{
+ bool bDone = false;
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScDocument& rDoc = rDocShell.GetDocument();
+
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+
+ if (pTable)
+ {
+ const ScOutlineEntry* pEntry;
+ size_t nColLevel;
+ size_t nRowLevel;
+ sal_uInt16 nCount;
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ sal_uInt16 i;
+
+ SCCOLROW nEffStartCol = nStartCol;
+ SCCOLROW nEffEndCol = nEndCol;
+ ScOutlineArray& rColArray = pTable->GetColArray();
+ rColArray.FindTouchedLevel( nStartCol, nEndCol, nColLevel );
+ rColArray.ExtendBlock( nColLevel, nEffStartCol, nEffEndCol );
+ SCCOLROW nEffStartRow = nStartRow;
+ SCCOLROW nEffEndRow = nEndRow;
+ ScOutlineArray& rRowArray = pTable->GetRowArray();
+ rRowArray.FindTouchedLevel( nStartRow, nEndRow, nRowLevel );
+ rRowArray.ExtendBlock( nRowLevel, nEffStartRow, nEffEndRow );
+
+ // TODO undo can mess things up when another view is editing a cell in the range of group entry
+ // this is a temporarily workaround
+ if ( !comphelper::LibreOfficeKit::isActive() && bRecord )
+ {
+ std::unique_ptr<ScOutlineTable> pUndoTab(new ScOutlineTable( *pTable ));
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(static_cast<SCCOL>(nEffStartCol), 0, nTab,
+ static_cast<SCCOL>(nEffEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
+ false, *pUndoDoc);
+ rDoc.CopyToDocument(0, nEffStartRow, nTab, rDoc.MaxCol(), nEffEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoOutlineBlock>( &rDocShell,
+ nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab,
+ std::move(pUndoDoc), std::move(pUndoTab), false ) );
+ }
+
+ // Columns
+
+ nCount = rColArray.GetCount(nColLevel);
+ for ( i=0; i<nCount; i++ )
+ {
+ pEntry = rColArray.GetEntry(nColLevel,i);
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+
+ if ( static_cast<SCCOLROW>(nStartCol)<=nEnd && static_cast<SCCOLROW>(nEndCol)>=nStart )
+ HideOutline( nTab, true, nColLevel, i, false, false );
+ }
+
+ // Rows
+
+ nCount = rRowArray.GetCount(nRowLevel);
+ for ( i=0; i<nCount; i++ )
+ {
+ pEntry = rRowArray.GetEntry(nRowLevel,i);
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+
+ if ( nStartRow<=nEnd && nEndRow>=nStart )
+ HideOutline( nTab, false, nRowLevel, i, false, false );
+ }
+
+ rDoc.SetDrawPageSize(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ rDocShell.PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top );
+
+ rDocShell.SetDocumentModified();
+ bDone = true;
+
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+ }
+
+ return bDone;
+}
+
+void ScOutlineDocFunc::ShowOutline( SCTAB nTab, bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry,
+ bool bRecord, bool bPaint )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (!pTable)
+ return;
+
+ ScOutlineArray& rArray = bColumns ? pTable->GetColArray() : pTable->GetRowArray();
+ ScOutlineEntry* pEntry = rArray.GetEntry( nLevel, nEntry );
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ // TODO undo can mess things up when another view is editing a cell in the range of group entry
+ // this is a temporarily workaround
+ if ( !comphelper::LibreOfficeKit::isActive() && bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ if (bColumns)
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ rDoc.CopyToDocument(static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false,
+ *pUndoDoc);
+ }
+ else
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ rDoc.CopyToDocument(0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDoOutline>( &rDocShell,
+ nStart, nEnd, nTab, std::move(pUndoDoc), //! calc start and end
+ bColumns, nLevel, nEntry, true ) );
+ }
+
+ pEntry->SetHidden(false);
+ SCCOLROW i;
+ for ( i = nStart; i <= nEnd; i++ )
+ {
+ if ( bColumns )
+ rDoc.ShowCol( static_cast<SCCOL>(i), nTab, true );
+ else
+ {
+ // show several rows together, don't show filtered rows
+ SCROW nFilterEnd = i;
+ bool bFiltered = rDoc.RowFiltered( i, nTab, nullptr, &nFilterEnd );
+ nFilterEnd = std::min( nEnd, nFilterEnd );
+ if ( !bFiltered )
+ rDoc.ShowRows( i, nFilterEnd, nTab, true );
+ i = nFilterEnd;
+ }
+ }
+
+ ScSubOutlineIterator aIter( &rArray, nLevel, nEntry );
+ while ((pEntry=aIter.GetNext()) != nullptr)
+ {
+ if ( pEntry->IsHidden() )
+ {
+ SCCOLROW nSubStart = pEntry->GetStart();
+ SCCOLROW nSubEnd = pEntry->GetEnd();
+ if ( bColumns )
+ for ( i = nSubStart; i <= nSubEnd; i++ )
+ rDoc.ShowCol( static_cast<SCCOL>(i), nTab, false );
+ else
+ rDoc.ShowRows( nSubStart, nSubEnd, nTab, false );
+ }
+ }
+
+ rArray.SetVisibleBelow( nLevel, nEntry, true, true );
+
+ rDoc.SetDrawPageSize(nTab);
+ rDoc.InvalidatePageBreaks(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if ( pViewSh )
+ pViewSh->OnLOKShowHideColRow(bColumns, nStart - 1);
+
+ if (bPaint)
+ lcl_PaintWidthHeight( rDocShell, nTab, bColumns, nStart, nEnd );
+
+ rDocShell.SetDocumentModified();
+
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+}
+
+bool ScOutlineDocFunc::HideOutline( SCTAB nTab, bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry,
+ bool bRecord, bool bPaint )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (!pTable)
+ return false;
+ ScOutlineArray& rArray = bColumns ? pTable->GetColArray() : pTable->GetRowArray();
+ ScOutlineEntry* pEntry = rArray.GetEntry( nLevel, nEntry );
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
+ if (pViewSh && ScTabViewShell::isAnyEditViewInRange(pViewSh, bColumns, nStart, nEnd))
+ return false;
+
+ // TODO undo can mess things up when another view is editing a cell in the range of group entry
+ // this is a temporarily workaround
+ if ( !comphelper::LibreOfficeKit::isActive() && bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ if (bColumns)
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ rDoc.CopyToDocument(static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false,
+ *pUndoDoc);
+ }
+ else
+ {
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ rDoc.CopyToDocument(0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ }
+
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDoOutline>( &rDocShell,
+ nStart, nEnd, nTab, std::move(pUndoDoc),
+ bColumns, nLevel, nEntry, false ) );
+ }
+
+ pEntry->SetHidden(true);
+ SCCOLROW i;
+ if ( bColumns )
+ for ( i = nStart; i <= nEnd; i++ )
+ rDoc.ShowCol( static_cast<SCCOL>(i), nTab, false );
+ else
+ rDoc.ShowRows( nStart, nEnd, nTab, false );
+
+ rArray.SetVisibleBelow( nLevel, nEntry, false );
+
+ rDoc.SetDrawPageSize(nTab);
+ rDoc.InvalidatePageBreaks(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+
+ if ( pViewSh )
+ pViewSh->OnLOKShowHideColRow(bColumns, nStart - 1);
+
+ if (bPaint)
+ lcl_PaintWidthHeight( rDocShell, nTab, bColumns, nStart, nEnd );
+
+ rDocShell.SetDocumentModified();
+
+ lcl_InvalidateOutliner( rDocShell.GetViewBindings() );
+
+
+ return true; //! always ???
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/pagedata.cxx b/sc/source/ui/docshell/pagedata.cxx
new file mode 100644
index 0000000000..4ab70ed9fe
--- /dev/null
+++ b/sc/source/ui/docshell/pagedata.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 <string.h>
+
+#include <pagedata.hxx>
+
+#include <osl/diagnose.h>
+
+ScPrintRangeData::ScPrintRangeData()
+{
+ bTopDown = bAutomatic = true;
+ nFirstPage = 1;
+}
+
+ScPrintRangeData::~ScPrintRangeData()
+{
+}
+
+void ScPrintRangeData::SetPagesX( size_t nCount, const SCCOL* pData )
+{
+ mvPageEndX.resize( nCount );
+ memcpy( mvPageEndX.data(), pData, nCount * sizeof(SCCOL) );
+}
+
+void ScPrintRangeData::SetPagesY( size_t nCount, const SCROW* pData )
+{
+ mvPageEndY.resize(nCount);
+ memcpy( mvPageEndY.data(), pData, nCount * sizeof(SCROW) );
+}
+
+ScPageBreakData::ScPageBreakData(size_t nMax)
+{
+ nUsed = 0;
+ if (nMax)
+ pData.reset( new ScPrintRangeData[nMax] );
+ nAlloc = nMax;
+}
+
+ScPageBreakData::~ScPageBreakData()
+{
+}
+
+ScPrintRangeData& ScPageBreakData::GetData(size_t nPos)
+{
+ OSL_ENSURE(nPos < nAlloc, "ScPageBreakData::GetData bumm");
+
+ if ( nPos >= nUsed )
+ {
+ OSL_ENSURE(nPos == nUsed, "ScPageBreakData::GetData wrong order");
+ nUsed = nPos+1;
+ }
+
+ return pData[nPos];
+}
+
+bool ScPageBreakData::operator==( const ScPageBreakData& rOther ) const
+{
+ if ( nUsed != rOther.nUsed )
+ return false;
+
+ for (size_t i=0; i<nUsed; i++)
+ if ( pData[i].GetPrintRange() != rOther.pData[i].GetPrintRange() )
+ return false;
+
+ //! compare ScPrintRangeData completely ??
+
+ return true;
+}
+
+void ScPageBreakData::AddPages()
+{
+ if ( nUsed > 1 )
+ {
+ tools::Long nPage = pData[0].GetFirstPage();
+ for (size_t i=0; i+1<nUsed; i++)
+ {
+ nPage += static_cast<tools::Long>(pData[i].GetPagesX())*pData[i].GetPagesY();
+ pData[i+1].SetFirstPage( nPage );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/pntlock.cxx b/sc/source/ui/docshell/pntlock.cxx
new file mode 100644
index 0000000000..39713941c0
--- /dev/null
+++ b/sc/source/ui/docshell/pntlock.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 <pntlock.hxx>
+
+ScPaintLockData::ScPaintLockData() :
+ nLevel( 0 ),
+ nDocLevel( 0 ),
+ nParts( PaintPartFlags::NONE ),
+ bModified( false )
+{
+}
+
+ScPaintLockData::~ScPaintLockData()
+{
+}
+
+void ScPaintLockData::AddRange( const ScRange& rRange, PaintPartFlags nP )
+{
+ if (!xRangeList.is())
+ xRangeList = new ScRangeList;
+
+ xRangeList->Join( rRange );
+ nParts |= nP;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/servobj.cxx b/sc/source/ui/docshell/servobj.cxx
new file mode 100644
index 0000000000..4367c7140c
--- /dev/null
+++ b/sc/source/ui/docshell/servobj.cxx
@@ -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 .
+ */
+
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <servobj.hxx>
+#include <docsh.hxx>
+#include <impex.hxx>
+#include <brdcst.hxx>
+#include <rangenam.hxx>
+#include <unotools/charclass.hxx>
+
+using namespace formula;
+
+static bool lcl_FillRangeFromName( ScRange& rRange, ScDocShell* pDocSh, const OUString& rName )
+{
+ if (pDocSh)
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScRangeName* pNames = rDoc.GetRangeName();
+ if (pNames)
+ {
+ const ScRangeData* pData = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
+ if (pData)
+ {
+ if ( pData->IsValidReference( rRange ) )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ScServerObjectSvtListenerForwarder::ScServerObjectSvtListenerForwarder(
+ ScServerObject* pObjP)
+ : pObj(pObjP)
+{
+}
+
+ScServerObjectSvtListenerForwarder::~ScServerObjectSvtListenerForwarder()
+{
+ //! do NOT access pObj
+}
+
+void ScServerObjectSvtListenerForwarder::Notify( const SfxHint& rHint )
+{
+ pObj->Notify( aBroadcaster, rHint);
+}
+
+ScServerObject::ScServerObject( ScDocShell* pShell, const OUString& rItem ) :
+ aForwarder( this ),
+ pDocSh( pShell ),
+ bRefreshListener( false )
+{
+ // parse item string
+
+ if ( lcl_FillRangeFromName( aRange, pDocSh, rItem ) )
+ {
+ aItemStr = rItem; // must be parsed again on ref update
+ }
+ else
+ {
+ // parse ref
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = ScDocShell::GetCurTab();
+ aRange.aStart.SetTab( nTab );
+
+ // For DDE link, we always must parse references using OOO A1 convention.
+
+ if ( aRange.Parse( rItem, rDoc, FormulaGrammar::CONV_OOO ) & ScRefFlags::VALID )
+ {
+ // area reference
+ }
+ else if ( aRange.aStart.Parse( rItem, rDoc, FormulaGrammar::CONV_OOO ) & ScRefFlags::VALID )
+ {
+ // cell reference
+ aRange.aEnd = aRange.aStart;
+ }
+ else
+ {
+ OSL_FAIL("ScServerObject: invalid item");
+ }
+ }
+
+ pDocSh->GetDocument().GetLinkManager()->InsertServer( this );
+ pDocSh->GetDocument().StartListeningArea( aRange, false, &aForwarder );
+
+ StartListening(*pDocSh); // to notice if DocShell gets deleted
+ StartListening(*SfxGetpApp()); // for SfxHintId::ScAreasChanged
+}
+
+ScServerObject::~ScServerObject()
+{
+ Clear();
+}
+
+void ScServerObject::Clear()
+{
+ if (pDocSh)
+ {
+ ScDocShell* pTemp = pDocSh;
+ pDocSh = nullptr;
+
+ pTemp->GetDocument().EndListeningArea(aRange, false, &aForwarder);
+ pTemp->GetDocument().GetLinkManager()->RemoveServer( this );
+ EndListening(*pTemp);
+ EndListening(*SfxGetpApp());
+ }
+}
+
+void ScServerObject::EndListeningAll()
+{
+ aForwarder.EndListeningAll();
+ SfxListener::EndListeningAll();
+}
+
+bool ScServerObject::GetData(
+ css::uno::Any & rData /*out param*/,
+ const OUString & rMimeType, bool /* bSynchron */ )
+{
+ if (!pDocSh)
+ return false;
+
+ // named ranges may have changed -> update aRange
+ if ( !aItemStr.isEmpty() )
+ {
+ ScRange aNew;
+ if ( lcl_FillRangeFromName( aNew, pDocSh, aItemStr ) && aNew != aRange )
+ {
+ aRange = aNew;
+ bRefreshListener = true;
+ }
+ }
+
+ if ( bRefreshListener )
+ {
+ // refresh the listeners now (this is called from a timer)
+
+ EndListeningAll();
+ pDocSh->GetDocument().StartListeningArea( aRange, false, &aForwarder );
+ StartListening(*pDocSh);
+ StartListening(*SfxGetpApp());
+ bRefreshListener = false;
+ }
+
+ OUString aDdeTextFmt = pDocSh->GetDdeTextFmt();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ SotClipboardFormatId eFormatId = SotExchange::GetFormatIdFromMimeType( rMimeType );
+ if (SotClipboardFormatId::STRING == eFormatId || SotClipboardFormatId::STRING_TSVC == eFormatId)
+ {
+ ScImportExport aObj( rDoc, aRange );
+ if( aDdeTextFmt[0] == 'F' )
+ aObj.SetFormulas( true );
+ if( aDdeTextFmt == "SYLK" || aDdeTextFmt == "FSYLK" )
+ {
+ OString aByteData;
+ if( aObj.ExportByteString( aByteData, osl_getThreadTextEncoding(), SotClipboardFormatId::SYLK ) )
+ {
+ rData <<= css::uno::Sequence< sal_Int8 >(
+ reinterpret_cast<const sal_Int8*>(aByteData.getStr()),
+ aByteData.getLength() + 1 );
+ return true;
+ }
+ return false;
+ }
+ if( aDdeTextFmt == "CSV" || aDdeTextFmt == "FCSV" )
+ aObj.SetSeparator( ',' );
+ /* TODO: STRING_TSVC could preserve line breaks with added quotes. */
+ aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
+ return aObj.ExportData( rMimeType, rData );
+ }
+
+ ScImportExport aObj( rDoc, aRange );
+ aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
+ if( aObj.IsRef() )
+ return aObj.ExportData( rMimeType, rData );
+ return false;
+}
+
+void ScServerObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ bool bDataChanged = false;
+
+ // DocShell can't be tested via type info, because SfxHintId::Dying comes from the dtor
+ if ( &rBC == pDocSh )
+ {
+ // from DocShell, only SfxHintId::Dying is interesting
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocSh = nullptr;
+ EndListening(*SfxGetpApp());
+ // don't access DocShell anymore for EndListening etc.
+ }
+ }
+ else if (dynamic_cast<const SfxApplication*>( &rBC) != nullptr)
+ {
+ if ( !aItemStr.isEmpty() && rHint.GetId() == SfxHintId::ScAreasChanged )
+ {
+ // check if named range was modified
+ ScRange aNew;
+ if ( lcl_FillRangeFromName( aNew, pDocSh, aItemStr ) && aNew != aRange )
+ bDataChanged = true;
+ }
+ }
+ else
+ {
+ // must be from Area broadcasters
+
+ const ScHint* pScHint = dynamic_cast<const ScHint*>( &rHint );
+ if (pScHint && (pScHint->GetId() == SfxHintId::ScDataChanged))
+ bDataChanged = true;
+ else if (const ScAreaChangedHint *pChgHint = dynamic_cast<const ScAreaChangedHint*>(&rHint)) // position of broadcaster changed
+ {
+ const ScRange& aNewRange = pChgHint->GetRange();
+ if ( aRange != aNewRange )
+ {
+ bRefreshListener = true;
+ bDataChanged = true;
+ }
+ }
+ else
+ {
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ // If the range is being deleted, listening must be restarted
+ // after the deletion is complete (done in GetData)
+ bRefreshListener = true;
+ bDataChanged = true;
+ }
+ }
+ }
+
+ if ( bDataChanged && HasDataLinks() )
+ SvLinkSource::NotifyDataChanged();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/sizedev.cxx b/sc/source/ui/docshell/sizedev.cxx
new file mode 100644
index 0000000000..65e03375bb
--- /dev/null
+++ b/sc/source/ui/docshell/sizedev.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 <sfx2/printer.hxx>
+#include <vcl/virdev.hxx>
+
+#include <sizedev.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+
+ScSizeDeviceProvider::ScSizeDeviceProvider( ScDocShell* pDocSh )
+{
+ bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg();
+ if ( bTextWysiwyg )
+ {
+ pDevice = pDocSh->GetPrinter();
+ bOwner = false;
+
+ aOldMapMode = pDevice->GetMapMode();
+ pDevice->SetMapMode(MapMode(MapUnit::MapPixel)); // GetNeededSize needs pixel MapMode
+ // printer has right DigitLanguage already
+ }
+ else
+ {
+ pDevice = VclPtr<VirtualDevice>::Create();
+ pDevice->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ bOwner = true;
+ }
+
+ Point aLogic = pDevice->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ nPPTX = aLogic.X() / 1000.0;
+ nPPTY = aLogic.Y() / 1000.0;
+
+ if ( !bTextWysiwyg )
+ nPPTX /= pDocSh->GetOutputFactor();
+}
+
+ScSizeDeviceProvider::~ScSizeDeviceProvider()
+{
+ if (bOwner)
+ pDevice.disposeAndClear();
+ else
+ pDevice->SetMapMode( aOldMapMode );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/tablink.cxx b/sc/source/ui/docshell/tablink.cxx
new file mode 100644
index 0000000000..affcbb5c7f
--- /dev/null
+++ b/sc/source/ui/docshell/tablink.cxx
@@ -0,0 +1,561 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/task/InteractionHandler.hpp>
+
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/app.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <utility>
+#include <vcl/weld.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <tablink.hxx>
+
+#include <scextopt.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <undoblk.hxx>
+#include <undotab.hxx>
+#include <global.hxx>
+#include <hints.hxx>
+#include <dociter.hxx>
+#include <formula/opcode.hxx>
+#include <formulaiter.hxx>
+#include <tokenarray.hxx>
+
+struct TableLink_Impl
+{
+ ScDocShell* m_pDocSh;
+ Link<sfx2::SvBaseLink&,void> m_aEndEditLink;
+
+ TableLink_Impl() : m_pDocSh( nullptr ) {}
+};
+
+ScTableLink::ScTableLink(ScDocShell* pShell, OUString aFile,
+ OUString aFilter, OUString aOpt,
+ sal_Int32 nRefreshDelaySeconds ):
+ ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL,SotClipboardFormatId::SIMPLE_FILE),
+ ScRefreshTimer( nRefreshDelaySeconds ),
+ pImpl( new TableLink_Impl ),
+ aFileName(std::move(aFile)),
+ aFilterName(std::move(aFilter)),
+ aOptions(std::move(aOpt)),
+ bInCreate( false ),
+ bInEdit( false ),
+ bAddUndo( true )
+{
+ pImpl->m_pDocSh = pShell;
+}
+
+ScTableLink::~ScTableLink()
+{
+ // cancel connection
+
+ StopRefreshTimer();
+ ScDocument& rDoc = pImpl->m_pDocSh->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nCount; nTab++)
+ if (rDoc.IsLinked(nTab) && aFileName == rDoc.GetLinkDoc(nTab))
+ rDoc.SetLink( nTab, ScLinkMode::NONE, "", "", "", "", 0 );
+}
+
+void ScTableLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& rEndEditHdl)
+{
+ pImpl->m_aEndEditLink = rEndEditHdl;
+
+ bInEdit = true;
+ SvBaseLink::Edit( pParent, LINK( this, ScTableLink, TableEndEditHdl ) );
+}
+
+::sfx2::SvBaseLink::UpdateResult ScTableLink::DataChanged(
+ const OUString&, const css::uno::Any& )
+{
+ sfx2::LinkManager* pLinkManager=pImpl->m_pDocSh->GetDocument().GetLinkManager();
+ if (pLinkManager!=nullptr)
+ {
+ OUString aFile, aFilter;
+ sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, nullptr, &aFilter);
+
+ // the file dialog returns the filter name with the application prefix
+ // -> remove prefix
+ ScDocumentLoader::RemoveAppPrefix( aFilter );
+
+ if (!bInCreate)
+ Refresh( aFile, aFilter, nullptr, GetRefreshDelaySeconds() ); // don't load twice
+ }
+ return SUCCESS;
+}
+
+void ScTableLink::Closed()
+{
+ // delete link: Undo
+ ScDocument& rDoc = pImpl->m_pDocSh->GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ if (bAddUndo && bUndo)
+ {
+ pImpl->m_pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveLink>( pImpl->m_pDocSh, aFileName ) );
+
+ bAddUndo = false; // only once
+ }
+
+ // connection gets cancelled in the dtor
+
+ SvBaseLink::Closed();
+}
+
+bool ScTableLink::IsUsed() const
+{
+ return pImpl->m_pDocSh->GetDocument().HasLink( aFileName, aFilterName, aOptions );
+}
+
+bool ScTableLink::Refresh(const OUString& rNewFile, const OUString& rNewFilter,
+ const OUString* pNewOptions, sal_Int32 nNewRefreshDelaySeconds )
+{
+ // load document
+
+ if (rNewFile.isEmpty() || rNewFilter.isEmpty())
+ return false;
+
+ OUString aNewUrl = ScGlobal::GetAbsDocName(rNewFile, pImpl->m_pDocSh);
+ bool bNewUrlName = aFileName != aNewUrl;
+
+ std::shared_ptr<const SfxFilter> pFilter = pImpl->m_pDocSh->GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter);
+ if (!pFilter)
+ return false;
+
+ ScDocument& rDoc = pImpl->m_pDocSh->GetDocument();
+ rDoc.SetInLinkUpdate( true );
+
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ // if new filter has been selected, forget options
+ if (aFilterName != rNewFilter)
+ aOptions.clear();
+ if ( pNewOptions ) // options hard-specified?
+ aOptions = *pNewOptions;
+
+ // always create ItemSet, so that DocShell can set the options
+ auto pSet = std::make_shared<SfxAllItemSet>( SfxGetpApp()->GetPool() );
+ if (!aOptions.isEmpty())
+ pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, aOptions ) );
+
+ SfxMedium* pMed = new SfxMedium(aNewUrl, StreamMode::STD_READ, pFilter, std::move(pSet));
+
+ if ( bInEdit ) // only if using the edit dialog,
+ pMed->UseInteractionHandler(true); // enable the filter options dialog
+
+ // aRef->DoClose() will be called explicitly, but it is still more safe to use SfxObjectShellLock here
+ ScDocShell* pSrcShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS);
+ SfxObjectShellLock aRef = pSrcShell;
+ pSrcShell->DoLoad(pMed);
+
+ // options might have been set
+ OUString aNewOpt = ScDocumentLoader::GetOptions(*pMed);
+ if (aNewOpt.isEmpty())
+ aNewOpt = aOptions;
+
+ // Undo...
+
+ ScDocumentUniquePtr pUndoDoc;
+ bool bFirst = true;
+ if (bAddUndo && bUndo)
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+
+ // copy tables
+
+ ScDocShellModificator aModificator( *pImpl->m_pDocSh );
+
+ bool bNotFound = false;
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+
+ // from text filters that don't set the table name,
+ // use the one table regardless of link table name
+ bool bAutoTab = (rSrcDoc.GetTableCount() == 1) &&
+ ScDocShell::HasAutomaticTableName( rNewFilter );
+
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nCount; nTab++)
+ {
+ ScLinkMode nMode = rDoc.GetLinkMode(nTab);
+ if (nMode != ScLinkMode::NONE && aFileName == rDoc.GetLinkDoc(nTab))
+ {
+ OUString aTabName = rDoc.GetLinkTab(nTab);
+
+ // Undo
+
+ if (bAddUndo && bUndo)
+ {
+ if (bFirst)
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ else
+ pUndoDoc->AddUndoTab( nTab, nTab, true, true );
+ bFirst = false;
+ ScRange aRange(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab);
+ rDoc.CopyToDocument(aRange, InsertDeleteFlags::ALL, false, *pUndoDoc);
+ pUndoDoc->TransferDrawPage( rDoc, nTab, nTab );
+ pUndoDoc->SetLink( nTab, nMode, aFileName, aFilterName,
+ aOptions, aTabName, GetRefreshDelaySeconds() );
+ pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
+ }
+
+ // adjust table name of an ExtDocRef
+
+ if ( bNewUrlName && nMode == ScLinkMode::VALUE )
+ {
+ OUString aName;
+ rDoc.GetName( nTab, aName );
+ if ( ScGlobal::GetTransliteration().isEqual(
+ ScGlobal::GetDocTabName( aFileName, aTabName ), aName ) )
+ {
+ rDoc.RenameTab( nTab,
+ ScGlobal::GetDocTabName( aNewUrl, aTabName ),
+ true/*bExternalDocument*/ );
+ }
+ }
+
+ // copy
+
+ SCTAB nSrcTab = 0;
+ bool bFound = false;
+ /* #i71497# check if external document is loaded successfully,
+ otherwise we may find the empty default sheet "Sheet1" in
+ rSrcDoc, even if the document does not exist. */
+ if( pMed->GetErrorIgnoreWarning() == ERRCODE_NONE )
+ {
+ // no sheet name -> use first sheet
+ if ( !aTabName.isEmpty() && !bAutoTab )
+ bFound = rSrcDoc.GetTable( aTabName, nSrcTab );
+ else
+ bFound = true;
+ }
+
+ if (bFound)
+ rDoc.TransferTab( rSrcDoc, nSrcTab, nTab, false, // don't insert anew
+ (nMode == ScLinkMode::VALUE) ); // only values?
+ else
+ {
+ rDoc.DeleteAreaTab( 0,0,rDoc.MaxCol(),rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL );
+
+ bool bShowError = true;
+ if ( nMode == ScLinkMode::VALUE )
+ {
+ // Value link (used with external references in formulas):
+ // Look for formulas that reference the sheet, and put errors in the referenced cells.
+
+ ScRangeList aErrorCells; // cells on the linked sheets that need error values
+
+ ScCellIterator aIter(rDoc, ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB)); // all sheets
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pCell = aIter.getFormulaCell();
+ ScDetectiveRefIter aRefIter(rDoc, pCell);
+ ScRange aRefRange;
+ while ( aRefIter.GetNextRef( aRefRange ) )
+ {
+ if ( aRefRange.aStart.Tab() <= nTab && aRefRange.aEnd.Tab() >= nTab )
+ {
+ // use first cell of range references (don't fill potentially large ranges)
+
+ aErrorCells.Join( ScRange( aRefRange.aStart ) );
+ }
+ }
+ }
+
+ size_t nRanges = aErrorCells.size();
+ if ( nRanges ) // found any?
+ {
+ ScTokenArray aTokenArr(rDoc);
+ aTokenArr.AddOpCode( ocNotAvail );
+ aTokenArr.AddOpCode( ocOpen );
+ aTokenArr.AddOpCode( ocClose );
+ aTokenArr.AddOpCode( ocStop );
+
+ for (size_t nPos=0; nPos < nRanges; nPos++)
+ {
+ const ScRange & rRange = aErrorCells[ nPos ];
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ ScAddress aDestPos( nCol, nRow, nTab );
+ rDoc.SetFormula(aDestPos, aTokenArr);
+ }
+ }
+
+ bShowError = false;
+ }
+ // if no references were found, insert error message (don't leave the sheet empty)
+ }
+
+ if ( bShowError )
+ {
+ // Normal link or no references: put error message on sheet.
+
+ rDoc.SetString( 0,0,nTab, ScResId(STR_LINKERROR) );
+ rDoc.SetString( 0,1,nTab, ScResId(STR_LINKERRORFILE) );
+ rDoc.SetString( 1,1,nTab, aNewUrl );
+ rDoc.SetString( 0,2,nTab, ScResId(STR_LINKERRORTAB) );
+ rDoc.SetString( 1,2,nTab, aTabName );
+ }
+
+ bNotFound = true;
+ }
+
+ if ( bNewUrlName || aFilterName != rNewFilter ||
+ aOptions != aNewOpt || pNewOptions ||
+ nNewRefreshDelaySeconds != GetRefreshDelaySeconds() )
+ rDoc.SetLink( nTab, nMode, aNewUrl, rNewFilter, aNewOpt,
+ aTabName, nNewRefreshDelaySeconds );
+ }
+ }
+
+ // memorize new settings
+
+ if ( bNewUrlName )
+ aFileName = aNewUrl;
+ if (aFilterName != rNewFilter)
+ aFilterName = rNewFilter;
+ if (aOptions != aNewOpt)
+ aOptions = aNewOpt;
+
+ // clean up
+
+ aRef->DoClose();
+
+ // Undo
+
+ if (bAddUndo && bUndo)
+ pImpl->m_pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRefreshLink>( pImpl->m_pDocSh, std::move(pUndoDoc) ) );
+
+ // Paint (may be several tables)
+
+ pImpl->m_pDocSh->PostPaint( ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB),
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras );
+ aModificator.SetDocumentModified();
+
+ if (bNotFound)
+ {
+ //! output error ?
+ }
+
+ rDoc.SetInLinkUpdate( false );
+
+ // notify Uno objects (for XRefreshListener)
+ //! also notify Uno objects if file name was changed!
+ ScLinkRefreshedHint aHint;
+ aHint.SetSheetLink( aFileName );
+ rDoc.BroadcastUno( aHint );
+
+ return true;
+}
+
+IMPL_LINK( ScTableLink, TableEndEditHdl, ::sfx2::SvBaseLink&, rLink, void )
+{
+ pImpl->m_aEndEditLink.Call( rLink );
+ bInEdit = false;
+}
+
+// === ScDocumentLoader ==================================================
+
+OUString ScDocumentLoader::GetOptions( const SfxMedium& rMedium )
+{
+ if ( const SfxStringItem* pItem = rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
+ return pItem->GetValue();
+
+ return OUString();
+}
+
+bool ScDocumentLoader::GetFilterName( const OUString& rFileName,
+ OUString& rFilter, OUString& rOptions,
+ bool bWithContent, bool bWithInteraction )
+{
+ SfxObjectShell* pDocSh = SfxObjectShell::GetFirst( checkSfxObjectShell<ScDocShell> );
+ while ( pDocSh )
+ {
+ if ( pDocSh->HasName() )
+ {
+ SfxMedium* pMed = pDocSh->GetMedium();
+ if ( pMed->GetName() == rFileName )
+ {
+ rFilter = pMed->GetFilter()->GetFilterName();
+ rOptions = GetOptions(*pMed);
+ return true;
+ }
+ }
+ pDocSh = SfxObjectShell::GetNext( *pDocSh, checkSfxObjectShell<ScDocShell> );
+ }
+
+ INetURLObject aUrl( rFileName );
+ INetProtocol eProt = aUrl.GetProtocol();
+ if ( eProt == INetProtocol::NotValid ) // invalid URL?
+ return false; // abort without creating a medium
+
+ // Filter detection
+
+ std::shared_ptr<const SfxFilter> pSfxFilter;
+ auto pMedium = std::make_unique<SfxMedium>( rFileName, StreamMode::STD_READ );
+ if (pMedium->GetErrorIgnoreWarning() == ERRCODE_NONE && !utl::ConfigManager::IsFuzzing())
+ {
+ if ( bWithInteraction )
+ pMedium->UseInteractionHandler(true); // #i73992# no longer called from GuessFilter
+
+ SfxFilterMatcher aMatcher("scalc");
+ if( bWithContent )
+ aMatcher.GuessFilter( *pMedium, pSfxFilter );
+ else
+ aMatcher.GuessFilterIgnoringContent( *pMedium, pSfxFilter );
+ }
+
+ bool bOK = false;
+ if ( pMedium->GetErrorIgnoreWarning() == ERRCODE_NONE )
+ {
+ if ( pSfxFilter )
+ rFilter = pSfxFilter->GetFilterName();
+ else
+ rFilter = ScDocShell::GetOwnFilterName(); // otherwise Calc file
+ bOK = !rFilter.isEmpty();
+ }
+
+ return bOK;
+}
+
+void ScDocumentLoader::RemoveAppPrefix( OUString& rFilterName )
+{
+ OUString aAppPrefix( STRING_SCAPP + ": ");
+ if (rFilterName.startsWith( aAppPrefix))
+ rFilterName = rFilterName.copy( aAppPrefix.getLength());
+}
+
+SfxMedium* ScDocumentLoader::CreateMedium( const OUString& rFileName, std::shared_ptr<const SfxFilter> const & pFilter,
+ const OUString& rOptions, weld::Window* pInteractionParent )
+{
+ // Always create SfxItemSet so ScDocShell can set options.
+ auto pSet = std::make_shared<SfxAllItemSet>( SfxGetpApp()->GetPool() );
+ if ( !rOptions.isEmpty() )
+ pSet->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, rOptions ) );
+
+ if (pInteractionParent)
+ {
+ css::uno::Reference<css::uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+ css::uno::Reference<css::task::XInteractionHandler> xIHdl(css::task::InteractionHandler::createWithParent(xContext,
+ pInteractionParent->GetXWindow()), css::uno::UNO_QUERY_THROW);
+ pSet->Put(SfxUnoAnyItem(SID_INTERACTIONHANDLER, css::uno::Any(xIHdl)));
+ }
+
+ SfxMedium *pRet = new SfxMedium( rFileName, StreamMode::STD_READ, pFilter, std::move(pSet) );
+ if (pInteractionParent)
+ pRet->UseInteractionHandler(true); // to enable the filter options dialog
+ return pRet;
+}
+
+ScDocumentLoader::ScDocumentLoader(const OUString& rFileName,
+ OUString& rFilterName, OUString& rOptions,
+ sal_uInt32 nRekCnt, weld::Window* pInteractionParent,
+ css::uno::Reference<css::io::XInputStream> xInputStream)
+ : pDocShell(nullptr)
+ , pMedium(nullptr)
+{
+ if ( rFilterName.isEmpty() )
+ GetFilterName(rFileName, rFilterName, rOptions, true, pInteractionParent != nullptr);
+
+ std::shared_ptr<const SfxFilter> pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( rFilterName );
+
+ pMedium = CreateMedium(rFileName, pFilter, rOptions, pInteractionParent);
+ if (xInputStream.is())
+ pMedium->setStreamToLoadFrom(xInputStream, true);
+ if ( pMedium->GetErrorIgnoreWarning() != ERRCODE_NONE )
+ return ;
+
+ pDocShell = new ScDocShell( SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS );
+ aRef = pDocShell;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScExtDocOptions* pExtDocOpt = rDoc.GetExtDocOptions();
+ if( !pExtDocOpt )
+ {
+ rDoc.SetExtDocOptions( std::make_unique<ScExtDocOptions>() );
+ pExtDocOpt = rDoc.GetExtDocOptions();
+ }
+ pExtDocOpt->GetDocSettings().mnLinkCnt = nRekCnt;
+
+ pDocShell->DoLoad( pMedium );
+
+ OUString aNew = GetOptions(*pMedium); // options are set per dialog on load
+ if (!aNew.isEmpty() && aNew != rOptions)
+ rOptions = aNew;
+}
+
+ScDocumentLoader::~ScDocumentLoader()
+{
+ if ( aRef.is() )
+ aRef->DoClose();
+ else
+ delete pMedium;
+}
+
+void ScDocumentLoader::ReleaseDocRef()
+{
+ if ( aRef.is() )
+ {
+ // release reference without calling DoClose - caller must
+ // have another reference to the doc and call DoClose later
+
+ pDocShell = nullptr;
+ pMedium = nullptr;
+ aRef.clear();
+ }
+}
+
+ScDocument* ScDocumentLoader::GetDocument()
+{
+ return pDocShell ? &pDocShell->GetDocument() : nullptr;
+}
+
+bool ScDocumentLoader::IsError() const
+{
+ if ( pDocShell && pMedium )
+ return pMedium->GetErrorIgnoreWarning() != ERRCODE_NONE;
+ else
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/docshell/tpstat.cxx b/sc/source/ui/docshell/tpstat.cxx
new file mode 100644
index 0000000000..226c862f37
--- /dev/null
+++ b/sc/source/ui/docshell/tpstat.cxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <document.hxx>
+#include <docsh.hxx>
+
+#include <tpstat.hxx>
+
+// Dokumentinfo-Tabpage:
+
+std::unique_ptr<SfxTabPage> ScDocStatPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<ScDocStatPage>( pPage, pController, *rSet );
+}
+
+ScDocStatPage::ScDocStatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/statisticsinfopage.ui", "StatisticsInfoPage", &rSet)
+ , m_xFtTables(m_xBuilder->weld_label("nosheets"))
+ , m_xFtCells(m_xBuilder->weld_label("nocells"))
+ , m_xFtPages(m_xBuilder->weld_label("nopages"))
+ , m_xFtFormula(m_xBuilder->weld_label("noformula"))
+ , m_xFrame(m_xBuilder->weld_frame("StatisticsInfoPage"))
+{
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ ScDocStat aDocStat;
+
+ if ( pDocSh )
+ pDocSh->GetDocStat( aDocStat );
+
+ OUString aInfo = m_xFrame->get_label() + aDocStat.aDocName;
+ m_xFrame->set_label(aInfo);
+ m_xFtTables->set_label( OUString::number( aDocStat.nTableCount ) );
+ m_xFtCells->set_label( OUString::number( aDocStat.nCellCount ) );
+ m_xFtPages->set_label( OUString::number( aDocStat.nPageCount ) );
+ m_xFtFormula->set_label( OUString::number( aDocStat.nFormulaCount ) );
+
+}
+
+ScDocStatPage::~ScDocStatPage()
+{
+}
+
+bool ScDocStatPage::FillItemSet( SfxItemSet* /* rSet */ )
+{
+ return false;
+}
+
+void ScDocStatPage::Reset( const SfxItemSet* /* rSet */ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/chartsh.cxx b/sc/source/ui/drawfunc/chartsh.cxx
new file mode 100644
index 0000000000..e6378a5fa9
--- /dev/null
+++ b/sc/source/ui/drawfunc/chartsh.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 <svx/svdoole2.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/graphichelper.hxx>
+
+#include <sfx2/objface.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <chartsh.hxx>
+#include <sc.hrc>
+#include <viewdata.hxx>
+#include <drawview.hxx>
+#include <gridwin.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+#include <tabvwsh.hxx>
+
+#define ShellClass_ScChartShell
+#include <scslots.hxx>
+
+using namespace css::uno;
+using namespace sfx2::sidebar;
+
+namespace drawing = com::sun::star::drawing;
+
+namespace {
+
+bool inChartOrMathContext(const ScTabViewShell* pViewShell)
+{
+ SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell);
+ if (pSidebar)
+ return pSidebar->hasChartOrMathContextCurrently();
+
+ return false;
+}
+
+} // anonymous namespace
+
+SFX_IMPL_INTERFACE(ScChartShell, ScDrawShell)
+
+void ScChartShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Draw_Objectbar);
+
+ GetStaticInterface()->RegisterPopupMenu("oleobject");
+}
+
+void ScChartShell::Activate(bool bMDI)
+{
+ if(!inChartOrMathContext(GetViewData().GetViewShell()))
+ ScDrawShell::Activate(bMDI);
+ else
+ {
+ // Avoid context changes for chart/math during activation / deactivation.
+ const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false));
+
+ SfxShell::Activate(bMDI);
+
+ SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled);
+ }
+}
+
+void ScChartShell::Deactivate(bool bMDI)
+{
+ if(!inChartOrMathContext(GetViewData().GetViewShell()))
+ ScDrawShell::Deactivate(bMDI);
+ else
+ {
+ // Avoid context changes for chart/math during activation / deactivation.
+ const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false));
+
+ SfxShell::Deactivate(bMDI);
+
+ SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled);
+ }
+}
+
+ScChartShell::ScChartShell(ScViewData& rData) :
+ ScDrawShell(rData)
+{
+ SetName( "ChartObject" );
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Chart));
+}
+
+ScChartShell::~ScChartShell()
+{
+}
+
+void ScChartShell::GetExportAsGraphicState( SfxItemSet& rSet )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( dynamic_cast<const SdrOle2Obj*>( pObj) )
+ bEnable = true;
+ }
+
+ if (GetObjectShell()->isExportLocked())
+ bEnable = false;
+
+ if( !bEnable )
+ rSet.DisableItem( SID_EXPORT_AS_GRAPHIC );
+}
+
+void ScChartShell::ExecuteExportAsGraphic( SfxRequest& )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObject = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( dynamic_cast<const SdrOle2Obj*>( pObject) )
+ {
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ css::uno::Reference<css::lang::XComponent> xComponent;
+ const SfxObjectShell* pShell = GetObjectShell();
+ if (pShell)
+ {
+ xComponent = pShell->GetModel();
+ }
+ Reference< drawing::XShape > xSourceDoc( pObject->getUnoShape() );
+ GraphicHelper::SaveShapeAsGraphic(pWin ? pWin->GetFrameWeld() : nullptr, xComponent,
+ xSourceDoc);
+ }
+ }
+
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drawsh.cxx b/sc/source/ui/drawfunc/drawsh.cxx
new file mode 100644
index 0000000000..837cf4bc02
--- /dev/null
+++ b/sc/source/ui/drawfunc/drawsh.cxx
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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>
+#include <svx/dialogs.hrc>
+#include <sc.hrc>
+
+#include <editeng/eeitem.hxx>
+#include <svx/fontwork.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/whiter.hxx>
+
+#include <drawsh.hxx>
+#include <drwlayer.hxx>
+#include <strings.hrc>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <drawview.hxx>
+#include <scresid.hxx>
+#include <svx/svdobj.hxx>
+#include <tabvwsh.hxx>
+#include <gridwin.hxx>
+#include <sfx2/bindings.hxx>
+
+#define ShellClass_ScDrawShell
+#include <scslots.hxx>
+
+#include <userdat.hxx>
+#include <svl/macitem.hxx>
+#include <sfx2/evntconf.hxx>
+#include <sfx2/viewsh.hxx>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <memory>
+#include <svx/xlnwtit.hxx>
+#include <svx/chrtitem.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <comphelper/lok.hxx>
+#include <vcl/unohelp2.hxx>
+
+using namespace css;
+
+SFX_IMPL_INTERFACE(ScDrawShell, SfxShell)
+
+namespace
+{
+ void lcl_convertStringArguments(SfxItemSet& rArgs)
+ {
+ if (const SvxDoubleItem* pWidthItem = rArgs.GetItemIfSet(SID_ATTR_LINE_WIDTH_ARG, false))
+ {
+ double fValue = pWidthItem->GetValue();
+ // FIXME: different units...
+ int nPow = 100;
+ int nValue = fValue * nPow;
+
+ XLineWidthItem aItem(nValue);
+ rArgs.Put(aItem);
+ }
+ if (const SfxStringItem* pJSON = rArgs.GetItemIfSet(SID_FILL_GRADIENT_JSON, false))
+ {
+ basegfx::BGradient aGradient = basegfx::BGradient::fromJSON(pJSON->GetValue());
+ XFillGradientItem aItem(aGradient);
+ rArgs.Put(aItem);
+ }
+ }
+}
+
+void ScDrawShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Draw_Objectbar);
+
+ GetStaticInterface()->RegisterPopupMenu("draw");
+
+ GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId());
+}
+
+// disable the unwanted Accelerators
+
+void ScDrawShell::StateDisableItems( SfxItemSet &rSet )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while (nWhich)
+ {
+ rSet.DisableItem( nWhich );
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScDrawShell::setModified()
+{
+ const SfxObjectShell* pShell = GetObjectShell();
+ if ( pShell )
+ {
+ css::uno::Reference< css::util::XModifiable > xModif( pShell->GetModel(), css::uno::UNO_QUERY );
+ if ( xModif.is() )
+ xModif->setModified( true );
+ }
+}
+
+static void lcl_invalidateTransformAttr(const ScTabViewShell* pViewShell)
+{
+ SfxBindings& rBindings=pViewShell->GetViewFrame().GetBindings();
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_WIDTH);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_HEIGHT);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_ANGLE);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_X);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_Y);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT);
+}
+
+void ScDrawShell::ExecDrawAttr( SfxRequest& rReq )
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ ScDrawView* pView = rViewData.GetScDrawView();
+ SdrModel* pDoc = rViewData.GetDocument().GetDrawLayer();
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+ SdrObject* pSingleSelectedObj = nullptr;
+ if ( nMarkCount > 0 )
+ pSingleSelectedObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ switch ( nSlot )
+ {
+ case SID_ASSIGNMACRO:
+ {
+ if ( pSingleSelectedObj )
+ ExecuteMacroAssign(pSingleSelectedObj, pWin ? pWin->GetFrameWeld() : nullptr);
+ }
+ break;
+
+ case SID_CELL_FORMAT_RESET:
+ case SID_TEXT_STANDARD:
+ {
+ SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_MINFRAMEHEIGHT,
+ SDRATTR_TEXT_MAXFRAMEHEIGHT, SDRATTR_TEXT_MAXFRAMEWIDTH> aEmptyAttr(GetPool());
+
+ if (ScDrawLayer::IsNoteCaption(pSingleSelectedObj))
+ aEmptyAttr.Put(pView->GetAttrFromMarked(true));
+
+ pView->SetAttributes(aEmptyAttr, true);
+ }
+ break;
+ case SID_MOVE_SHAPE_HANDLE:
+ {
+ const SfxItemSet *pArgs = rReq.GetArgs ();
+ if (pArgs && pArgs->Count () >= 3)
+ {
+ const SfxUInt32Item* handleNumItem = rReq.GetArg<SfxUInt32Item>(FN_PARAM_1);
+ const SfxUInt32Item* newPosXTwips = rReq.GetArg<SfxUInt32Item>(FN_PARAM_2);
+ const SfxUInt32Item* newPosYTwips = rReq.GetArg<SfxUInt32Item>(FN_PARAM_3);
+ const SfxInt32Item* OrdNum = rReq.GetArg<SfxInt32Item>(FN_PARAM_4);
+
+ const sal_uLong handleNum = handleNumItem->GetValue();
+ const sal_uLong newPosX = convertTwipToMm100(newPosXTwips->GetValue());
+ const sal_uLong newPosY = convertTwipToMm100(newPosYTwips->GetValue());
+
+ bool bNegateX = comphelper::LibreOfficeKit::isActive() && rViewData.GetDocument().IsLayoutRTL(rViewData.GetTabNo());
+ pView->MoveShapeHandle(handleNum, Point(bNegateX ? -static_cast<tools::Long>(newPosX) : newPosX, newPosY), OrdNum ? OrdNum->GetValue() : -1);
+ }
+ }
+ break;
+
+ case SID_ATTR_LINE_STYLE:
+ case SID_ATTR_LINEEND_STYLE:
+ case SID_ATTR_LINE_START:
+ case SID_ATTR_LINE_END:
+ case SID_ATTR_LINE_DASH:
+ case SID_ATTR_LINE_WIDTH:
+ case SID_ATTR_LINE_COLOR:
+ case SID_ATTR_LINE_TRANSPARENCE:
+ case SID_ATTR_LINE_JOINT:
+ case SID_ATTR_LINE_CAP:
+ case SID_ATTR_FILL_STYLE:
+ case SID_ATTR_FILL_COLOR:
+ case SID_ATTR_FILL_GRADIENT:
+ case SID_ATTR_FILL_HATCH:
+ case SID_ATTR_FILL_BITMAP:
+ case SID_ATTR_FILL_TRANSPARENCE:
+ case SID_ATTR_FILL_FLOATTRANSPARENCE:
+
+ // #i25616#
+ case SID_ATTR_FILL_SHADOW:
+ case SID_ATTR_SHADOW_TRANSPARENCE:
+ case SID_ATTR_SHADOW_COLOR:
+ case SID_ATTR_SHADOW_XDISTANCE:
+ case SID_ATTR_SHADOW_YDISTANCE:
+ {
+ // if toolbar is vertical :
+ if ( !rReq.GetArgs() )
+ {
+ switch ( nSlot )
+ {
+ case SID_ATTR_LINE_STYLE:
+ case SID_ATTR_LINE_DASH:
+ case SID_ATTR_LINE_WIDTH:
+ case SID_ATTR_LINE_COLOR:
+ case SID_ATTR_LINE_TRANSPARENCE:
+ case SID_ATTR_LINE_JOINT:
+ case SID_ATTR_LINE_CAP:
+ ExecuteLineDlg( rReq );
+ break;
+
+ case SID_ATTR_FILL_STYLE:
+ case SID_ATTR_FILL_COLOR:
+ case SID_ATTR_FILL_GRADIENT:
+ case SID_ATTR_FILL_HATCH:
+ case SID_ATTR_FILL_BITMAP:
+ case SID_ATTR_FILL_TRANSPARENCE:
+ case SID_ATTR_FILL_FLOATTRANSPARENCE:
+
+ // #i25616#
+ case SID_ATTR_FILL_SHADOW:
+ case SID_ATTR_SHADOW_TRANSPARENCE:
+ case SID_ATTR_SHADOW_COLOR:
+ case SID_ATTR_SHADOW_XDISTANCE:
+ case SID_ATTR_SHADOW_YDISTANCE:
+ ExecuteAreaDlg( rReq );
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+
+ }
+
+ if( pView->AreObjectsMarked() )
+ {
+ std::unique_ptr<SfxItemSet> pNewArgs = rReq.GetArgs()->Clone();
+ lcl_convertStringArguments(*pNewArgs);
+ pView->SetAttrToMarked(*pNewArgs, false);
+ }
+ else
+ pView->SetDefaultAttr( *rReq.GetArgs(), false);
+ pView->InvalidateAttribs();
+ }
+ break;
+
+ case SID_ATTRIBUTES_LINE:
+ ExecuteLineDlg( rReq );
+ break;
+
+ case SID_ATTRIBUTES_AREA:
+ ExecuteAreaDlg( rReq );
+ break;
+
+ case SID_MEASURE_DLG:
+ ExecuteMeasureDlg( rReq );
+ break;
+
+ case SID_DRAWTEXT_ATTR_DLG:
+ ExecuteTextAttrDlg( rReq );
+ break;
+
+ case SID_EDIT_HYPERLINK:
+ if ( pSingleSelectedObj )
+ rViewData.GetDispatcher().Execute( SID_HYPERLINK_DIALOG );
+ break;
+
+ case SID_REMOVE_HYPERLINK:
+ if ( pSingleSelectedObj )
+ {
+ pSingleSelectedObj->setHyperlink(OUString());
+ setModified();
+ }
+ break;
+
+ case SID_OPEN_HYPERLINK:
+ case SID_COPY_HYPERLINK_LOCATION:
+ if ( nMarkCount == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if ( pObj->IsGroupObject() )
+ {
+ SdrPageView* pPV = nullptr;
+ SdrObject* pHit = pView->PickObj(pWin->PixelToLogic(rViewData.GetMousePosPixel()), pView->getHitTolLog(), pPV, SdrSearchOptions::DEEP);
+ if (pHit)
+ pObj = pHit;
+ }
+
+ if (!pObj->getHyperlink().isEmpty())
+ {
+ if (nSlot == SID_OPEN_HYPERLINK)
+ {
+ ScGlobal::OpenURL(pObj->getHyperlink(), OUString(), true);
+ }
+ else if (nSlot == SID_COPY_HYPERLINK_LOCATION)
+ {
+ uno::Reference<datatransfer::clipboard::XClipboard> xClipboard
+ = GetViewShell()->GetWindow()->GetClipboard();
+ vcl::unohelper::TextDataObject::CopyStringTo(pObj->getHyperlink(), xClipboard);
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_TRANSFORM:
+ {
+ if ( pView->AreObjectsMarked() )
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ if( !pArgs )
+ {
+ if( rMarkList.GetMark(0) != nullptr )
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ std::shared_ptr<SfxRequest> pRequest = std::make_shared<SfxRequest>(rReq);
+
+ if( pObj->GetObjIdentifier() == SdrObjKind::Caption )
+ {
+ // Caption Itemset
+ SfxItemSet aNewAttr(pDoc->GetItemPool());
+ pView->GetAttributes(aNewAttr);
+ // Size and Position Itemset
+ SfxItemSet aNewGeoAttr(pView->GetGeoAttrFromMarked());
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateCaptionDialog(pWin ? pWin->GetFrameWeld() : nullptr, pView));
+
+ const WhichRangesContainer& pRange = pDlg->GetInputRanges( *aNewAttr.GetPool() );
+ SfxItemSet aCombSet( *aNewAttr.GetPool(), pRange );
+ aCombSet.Put( aNewAttr );
+ aCombSet.Put( aNewGeoAttr );
+ pDlg->SetInputSet( &aCombSet );
+
+ pDlg->StartExecuteAsync([pDlg, pRequest, pView, this](
+ sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ pRequest->Done(*(pDlg->GetOutputItemSet()));
+ pView->SetAttributes(*pDlg->GetOutputItemSet());
+ pView->SetGeoAttrToMarked(*pDlg->GetOutputItemSet());
+ }
+
+ lcl_invalidateTransformAttr(rViewData.GetViewShell());
+ pDlg->disposeOnce();
+ });
+ }
+ else
+ {
+ SfxItemSet aNewAttr(pView->GetGeoAttrFromMarked());
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxTransformTabDialog(pWin ? pWin->GetFrameWeld() : nullptr, &aNewAttr, pView));
+
+ pDlg->StartExecuteAsync([pDlg, pRequest, pView, this](
+ sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ pRequest->Done(*(pDlg->GetOutputItemSet()));
+ pView->SetGeoAttrToMarked(*pDlg->GetOutputItemSet());
+ }
+
+ lcl_invalidateTransformAttr(rViewData.GetViewShell());
+ pDlg->disposeOnce();
+ });
+ }
+ }
+
+ }
+ else
+ pView->SetGeoAttrToMarked( *pArgs );
+ }
+
+ lcl_invalidateTransformAttr(rViewData.GetViewShell());
+ }
+ break;
+
+ case SID_ATTR_GLOW_COLOR:
+ case SID_ATTR_GLOW_RADIUS:
+ case SID_ATTR_GLOW_TRANSPARENCY:
+ case SID_ATTR_SOFTEDGE_RADIUS:
+ case SID_ATTR_TEXTCOLUMNS_NUMBER:
+ case SID_ATTR_TEXTCOLUMNS_SPACING:
+ if (const SfxItemSet* pNewArgs = rReq.GetArgs())
+ pView->SetAttrToMarked(*pNewArgs, false);
+ rReq.Done();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ScDrawShell::ExecuteMacroAssign(SdrObject* pObj, weld::Window* pWin)
+{
+ SvxMacroItem aItem ( SfxGetpApp()->GetPool().GetWhich( SID_ATTR_MACROITEM ) );
+ ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pObj, true );
+ if ( !pInfo->GetMacro().isEmpty() )
+ {
+ SvxMacroTableDtor aTab;
+ const OUString& sMacro = pInfo->GetMacro();
+ aTab.Insert(SvMacroItemId::OnClick, SvxMacro(sMacro, OUString()));
+ aItem.SetMacroTable( aTab );
+ }
+
+ // create empty itemset for macro-dlg
+ SfxItemSetFixed<SID_ATTR_MACROITEM, SID_ATTR_MACROITEM, SID_EVENTCONFIG, SID_EVENTCONFIG> aItemSet(SfxGetpApp()->GetPool() );
+ aItemSet.Put ( aItem );
+
+ SfxEventNamesItem aNamesItem(SID_EVENTCONFIG);
+ aNamesItem.AddEvent( ScResId(RID_SCSTR_ONCLICK), OUString(), SvMacroItemId::OnClick );
+ aItemSet.Put( aNamesItem );
+
+ css::uno::Reference < css::frame::XFrame > xFrame;
+ if (GetViewShell())
+ xFrame = GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractDialog> pMacroDlg(pFact->CreateEventConfigDialog( pWin, aItemSet, xFrame ));
+ if ( pMacroDlg->Execute() != RET_OK )
+ return;
+
+ const SfxItemSet* pOutSet = pMacroDlg->GetOutputItemSet();
+ const SvxMacroItem* pItem = pOutSet->GetItemIfSet( SID_ATTR_MACROITEM, false );
+ if( !pItem )
+ return;
+
+ OUString sMacro;
+ const SvxMacro* pMacro = pItem->GetMacroTable().Get( SvMacroItemId::OnClick );
+ if ( pMacro )
+ sMacro = pMacro->GetMacName();
+
+ if ( pObj->IsGroupObject() )
+ {
+ SdrObjList* pOL = pObj->GetSubList();
+ for (const rtl::Reference<SdrObject>& pChildObj : *pOL)
+ {
+ pInfo = ScDrawLayer::GetMacroInfo( pChildObj.get(), true );
+ pInfo->SetMacro( sMacro );
+ }
+ }
+ else
+ pInfo->SetMacro( sMacro );
+ setModified();
+}
+
+void ScDrawShell::ExecuteLineDlg( const SfxRequest& rReq )
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ bool bHasMarked = pView->AreObjectsMarked();
+ const SdrObject* pObj = nullptr;
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ std::shared_ptr<SfxRequest> pRequest = std::make_shared<SfxRequest>(rReq);
+
+ if( rMarkList.GetMarkCount() == 1 )
+ pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ SfxItemSet aNewAttr( pView->GetDefaultAttr() );
+ if( bHasMarked )
+ pView->MergeAttrFromMarked( aNewAttr, false );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxLineTabDialog( rViewData.GetDialogParent(),
+ &aNewAttr,
+ rViewData.GetDocument().GetDrawLayer(),
+ pObj,
+ bHasMarked));
+
+ pDlg->StartExecuteAsync([=](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ if( bHasMarked )
+ pView->SetAttrToMarked( *pDlg->GetOutputItemSet(), false );
+ else
+ pView->SetDefaultAttr( *pDlg->GetOutputItemSet(), false );
+
+ pView->InvalidateAttribs();
+ pRequest->Done();
+ }
+ pDlg->disposeOnce();
+ });
+}
+
+void ScDrawShell::ExecuteAreaDlg( const SfxRequest& rReq )
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ bool bHasMarked = pView->AreObjectsMarked();
+
+ std::shared_ptr<SfxRequest> pRequest = std::make_shared<SfxRequest>(rReq);
+
+ SfxItemSet aNewAttr( pView->GetDefaultAttr() );
+ if( bHasMarked )
+ pView->MergeAttrFromMarked( aNewAttr, false );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ weld::Window* pWin = rViewData.GetDialogParent();
+ VclPtr<AbstractSvxAreaTabDialog> pDlg(pFact->CreateSvxAreaTabDialog(
+ pWin, &aNewAttr,
+ rViewData.GetDocument().GetDrawLayer(), true, false));
+
+ pDlg->StartExecuteAsync([=](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ if( bHasMarked )
+ pView->SetAttrToMarked( *pDlg->GetOutputItemSet(), false );
+ else
+ pView->SetDefaultAttr( *pDlg->GetOutputItemSet(), false );
+
+ pView->InvalidateAttribs();
+ pRequest->Done();
+ }
+ pDlg->disposeOnce();
+ });
+}
+
+void ScDrawShell::ExecuteTextAttrDlg( SfxRequest& rReq )
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ bool bHasMarked = pView->AreObjectsMarked();
+ SfxItemSet aNewAttr ( pView->GetDefaultAttr() );
+
+ if( bHasMarked )
+ pView->MergeAttrFromMarked( aNewAttr, false );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ weld::Window* pWin = rViewData.GetDialogParent();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTextTabDialog(pWin, &aNewAttr, pView));
+
+ sal_uInt16 nResult = pDlg->Execute();
+
+ if ( RET_OK == nResult )
+ {
+ if ( bHasMarked )
+ pView->SetAttributes( *pDlg->GetOutputItemSet() );
+ else
+ pView->SetDefaultAttr( *pDlg->GetOutputItemSet(), false );
+
+ pView->InvalidateAttribs();
+ rReq.Done();
+ }
+}
+
+void ScDrawShell::ExecuteMeasureDlg( SfxRequest& rReq )
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ bool bHasMarked = pView->AreObjectsMarked();
+ SfxItemSet aNewAttr ( pView->GetDefaultAttr() );
+
+ if( bHasMarked )
+ pView->MergeAttrFromMarked( aNewAttr, false );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ weld::Window* pWin = rViewData.GetDialogParent();
+ ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateSfxDialog(pWin, aNewAttr, pView, RID_SVXPAGE_MEASURE));
+
+ sal_uInt16 nResult = pDlg->Execute();
+
+ if ( RET_OK == nResult )
+ {
+ if ( bHasMarked )
+ pView->SetAttrToMarked( *pDlg->GetOutputItemSet(), false );
+ else
+ pView->SetDefaultAttr( *pDlg->GetOutputItemSet(), false );
+
+ pView->InvalidateAttribs();
+ rReq.Done();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drawsh2.cxx b/sc/source/ui/drawfunc/drawsh2.cxx
new file mode 100644
index 0000000000..8556c18e6b
--- /dev/null
+++ b/sc/source/ui/drawfunc/drawsh2.cxx
@@ -0,0 +1,564 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <editeng/eeitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/xdef.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/ptitem.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/extrusionbar.hxx>
+#include <svx/fontworkbar.hxx>
+#include <svx/sidebar/SelectionChangeHandler.hxx>
+#include <svx/sidebar/SelectionAnalyzer.hxx>
+#include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
+#include <svx/unomid.hxx>
+
+#include <drawsh.hxx>
+#include <drawview.hxx>
+#include <viewdata.hxx>
+#include <sc.hrc>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <drtxtob.hxx>
+#include <gridwin.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/xflgrit.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <svx/xflclit.hxx>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <sfx2/ipclient.hxx>
+
+using namespace com::sun::star::drawing;
+using namespace com::sun::star;
+
+
+ScDrawShell::ScDrawShell( ScViewData& rData ) :
+ SfxShell(rData.GetViewShell()),
+ rViewData( rData ),
+ mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
+ [this] () { return this->GetSidebarContextName(); },
+ GetFrame()->GetFrame().GetController(),
+ vcl::EnumContext::Context::Cell))
+{
+ SetPool( &rViewData.GetScDrawView()->GetModel().GetItemPool() );
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if ( !rViewData.GetDocument().IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Drawing");
+
+ mpSelectionChangeHandler->Connect();
+}
+
+ScDrawShell::~ScDrawShell()
+{
+ mpSelectionChangeHandler->Disconnect();
+}
+
+void ScDrawShell::GetState( SfxItemSet& rSet ) // Conditions / Toggles
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ SdrDragMode eMode = pView->GetDragMode();
+
+ rSet.Put( SfxBoolItem( SID_OBJECT_ROTATE, eMode == SdrDragMode::Rotate ) );
+ rSet.Put( SfxBoolItem( SID_OBJECT_MIRROR, eMode == SdrDragMode::Mirror ) );
+ rSet.Put( SfxBoolItem( SID_BEZIER_EDIT, !pView->IsFrameDragSingles() ) );
+
+ sal_uInt16 nFWId = ScGetFontWorkId();
+ SfxViewFrame& rViewFrm = rViewData.GetViewShell()->GetViewFrame();
+ rSet.Put(SfxBoolItem(SID_FONTWORK, rViewFrm.HasChildWindow(nFWId)));
+
+ // Notes always default to Page anchor.
+ bool bDisableAnchor = false;
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if ( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ bDisableAnchor = true;
+ rSet.DisableItem( SID_ANCHOR_PAGE );
+ rSet.DisableItem( SID_ANCHOR_CELL );
+ rSet.DisableItem( SID_ANCHOR_CELL_RESIZE );
+ }
+ }
+
+ if ( bDisableAnchor )
+ return;
+
+ switch( pView->GetAnchorType() )
+ {
+ case SCA_PAGE:
+ rSet.Put( SfxBoolItem( SID_ANCHOR_PAGE, true ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL, false ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL_RESIZE, false ) );
+ break;
+
+ case SCA_CELL:
+ rSet.Put( SfxBoolItem( SID_ANCHOR_PAGE, false ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL, true ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL_RESIZE, false ) );
+ break;
+
+ case SCA_CELL_RESIZE:
+ rSet.Put( SfxBoolItem( SID_ANCHOR_PAGE, false ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL, false ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL_RESIZE, true ) );
+ break;
+
+ default:
+ rSet.Put( SfxBoolItem( SID_ANCHOR_PAGE, false ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL, false ) );
+ rSet.Put( SfxBoolItem( SID_ANCHOR_CELL_RESIZE, false ) );
+ break;
+ }
+}
+
+void ScDrawShell::GetDrawFuncState( SfxItemSet& rSet ) // disable functions
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+
+ // call IsMirrorAllowed first to make sure ForcePossibilities (and thus CheckMarked)
+ // is called before GetMarkCount, so the nMarkCount value is valid for the rest of this method.
+ if (!pView->IsMirrorAllowed(true,true))
+ {
+ rSet.DisableItem( SID_MIRROR_HORIZONTAL );
+ rSet.DisableItem( SID_MIRROR_VERTICAL );
+ rSet.DisableItem( SID_FLIP_HORIZONTAL );
+ rSet.DisableItem( SID_FLIP_VERTICAL );
+ }
+
+
+ if (GetObjectShell()->isContentExtractionLocked())
+ {
+ rSet.DisableItem(SID_COPY);
+ rSet.DisableItem(SID_CUT);
+ }
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+
+ if ( nMarkCount <= 1 || !pView->IsGroupPossible() )
+ rSet.DisableItem( SID_GROUP );
+ if ( nMarkCount == 0 || !pView->IsUnGroupPossible() )
+ rSet.DisableItem( SID_UNGROUP );
+ if ( nMarkCount != 1 || !pView->IsGroupEnterPossible() )
+ rSet.DisableItem( SID_ENTER_GROUP );
+ if ( !pView->IsGroupEntered() )
+ rSet.DisableItem( SID_LEAVE_GROUP );
+
+ if ( nMarkCount <= 1 ) // Nothing or only one object selected
+ {
+ // alignment
+ rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); // no alignment on the side
+ rSet.DisableItem( SID_OBJECT_ALIGN_CENTER );
+ rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT );
+ rSet.DisableItem( SID_OBJECT_ALIGN_UP );
+ rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE );
+ rSet.DisableItem( SID_OBJECT_ALIGN_DOWN );
+ rSet.DisableItem( SID_OBJECT_ALIGN );
+
+ // pseudo slots for Format menu
+ rSet.DisableItem( SID_ALIGN_ANY_LEFT );
+ rSet.DisableItem( SID_ALIGN_ANY_HCENTER );
+ rSet.DisableItem( SID_ALIGN_ANY_RIGHT );
+ rSet.DisableItem( SID_ALIGN_ANY_TOP );
+ rSet.DisableItem( SID_ALIGN_ANY_VCENTER );
+ rSet.DisableItem( SID_ALIGN_ANY_BOTTOM );
+ }
+
+ // do not change layer of form controls
+ // #i83729# do not change layer of cell notes (on internal layer)
+ if ( !nMarkCount || pView->HasMarkedControl() || pView->HasMarkedInternal() )
+ {
+ rSet.DisableItem( SID_OBJECT_HEAVEN );
+ rSet.DisableItem( SID_OBJECT_HELL );
+ }
+ else
+ {
+ if(AreAllObjectsOnLayer(SC_LAYER_FRONT,rMarkList))
+ {
+ rSet.DisableItem( SID_OBJECT_HEAVEN );
+ }
+ else if(AreAllObjectsOnLayer(SC_LAYER_BACK,rMarkList))
+ {
+ rSet.DisableItem( SID_OBJECT_HELL );
+ }
+ }
+
+ bool bCanRename = false;
+ if ( nMarkCount > 1 )
+ {
+ // no hyperlink options for a selected group
+ rSet.DisableItem( SID_EDIT_HYPERLINK );
+ rSet.DisableItem( SID_REMOVE_HYPERLINK );
+ rSet.DisableItem( SID_OPEN_HYPERLINK );
+ rSet.DisableItem( SID_COPY_HYPERLINK_LOCATION );
+ // Fit to cell only works with a single graphic
+ rSet.DisableItem( SID_FITCELLSIZE );
+ }
+ else if ( nMarkCount == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if (pObj->getHyperlink().isEmpty())
+ {
+ rSet.DisableItem( SID_EDIT_HYPERLINK );
+ rSet.DisableItem( SID_OPEN_HYPERLINK );
+ rSet.DisableItem( SID_REMOVE_HYPERLINK );
+ rSet.DisableItem( SID_COPY_HYPERLINK_LOCATION );
+ }
+ SdrLayerID nLayerID = pObj->GetLayer();
+ if ( nLayerID != SC_LAYER_INTERN )
+ bCanRename = true; // #i51351# anything except internal objects can be renamed
+
+ // #91929#; don't show original size entry if not possible
+ SdrObjKind nObjType = pObj->GetObjIdentifier();
+ if ( nObjType == SdrObjKind::OLE2 )
+ {
+ SdrOle2Obj* pOleObj = static_cast<SdrOle2Obj*>(rMarkList.GetMark( 0 )->GetMarkedSdrObj());
+ if (pOleObj->GetObjRef().is() &&
+ (pOleObj->GetObjRef()->getStatus( pOleObj->GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) )
+ //TODO/LATER: why different slots in Draw and Calc?
+ rSet.DisableItem(SID_ORIGINALSIZE);
+ }
+ else if ( nObjType == SdrObjKind::Caption )
+ {
+ if ( nLayerID == SC_LAYER_INTERN )
+ {
+ // SdrCaptionObj() Notes cannot be cut/copy in isolation from
+ // their cells.
+ rSet.DisableItem( SID_CUT );
+ rSet.DisableItem( SID_COPY );
+ // Notes always default to Page anchor.
+ rSet.DisableItem( SID_ANCHOR_TOGGLE );
+ rSet.DisableItem( SID_ANCHOR_MENU );
+ }
+ }
+
+ // Fit to cell is only available for cell anchored graphics obviously
+ if (pView->GetAnchorType() != SCA_CELL &&
+ pView->GetAnchorType() != SCA_CELL_RESIZE)
+ rSet.DisableItem( SID_FITCELLSIZE );
+
+ // Support advanced DiagramHelper
+ if (!pObj->isDiagram())
+ {
+ rSet.DisableItem( SID_REGENERATE_DIAGRAM );
+ rSet.DisableItem( SID_EDIT_DIAGRAM );
+ }
+ }
+ if ( !bCanRename )
+ {
+ // #i68101#
+ rSet.DisableItem( SID_RENAME_OBJECT );
+ rSet.DisableItem( SID_TITLE_DESCRIPTION_OBJECT );
+ }
+
+ if ( !nMarkCount ) // nothing selected
+ {
+ // Arrangement
+ rSet.DisableItem( SID_FRAME_UP );
+ rSet.DisableItem( SID_FRAME_DOWN );
+ rSet.DisableItem( SID_FRAME_TO_TOP );
+ rSet.DisableItem( SID_FRAME_TO_BOTTOM );
+ // Clipboard / delete
+ rSet.DisableItem( SID_DELETE );
+ rSet.DisableItem( SID_DELETE_CONTENTS );
+ rSet.DisableItem( SID_CUT );
+ rSet.DisableItem( SID_COPY );
+ // other
+ rSet.DisableItem( SID_ANCHOR_TOGGLE );
+ rSet.DisableItem( SID_ANCHOR_MENU );
+ rSet.DisableItem( SID_ORIGINALSIZE );
+ rSet.DisableItem( SID_FITCELLSIZE );
+ rSet.DisableItem( SID_ATTR_TRANSFORM );
+ }
+
+ if ( rSet.GetItemState( SID_ENABLE_HYPHENATION ) != SfxItemState::UNKNOWN )
+ {
+ SfxItemSet aAttrs( pView->GetModel().GetItemPool() );
+ pView->GetAttributes( aAttrs );
+ if( aAttrs.GetItemState( EE_PARA_HYPHENATE ) >= SfxItemState::DEFAULT )
+ {
+ bool bValue = aAttrs.Get( EE_PARA_HYPHENATE ).GetValue();
+ rSet.Put( SfxBoolItem( SID_ENABLE_HYPHENATION, bValue ) );
+ }
+ }
+
+ svx::ExtrusionBar::getState( pView, rSet );
+ svx::FontworkBar::getState( pView, rSet );
+}
+
+static void setupFillColorForChart(const SfxViewShell* pShell, SfxItemSet& rSet)
+{
+ if (!pShell)
+ return;
+
+ SfxInPlaceClient* pIPClient = pShell->GetIPClient();
+ if (!pIPClient)
+ return;
+
+ const css::uno::Reference<::css::embed::XEmbeddedObject>& xEmbObj = pIPClient->GetObject();
+ if( !xEmbObj.is() )
+ return;
+
+ ::css::uno::Reference<::css::chart2::XChartDocument> xChart( xEmbObj->getComponent(), uno::UNO_QUERY );
+ if( !xChart.is() )
+ return;
+
+ css::uno::Reference<css::beans::XPropertySet> xPropSet = xChart->getPageBackground();
+ if (!xPropSet.is())
+ return;
+
+ css::uno::Reference<css::beans::XPropertySetInfo> xInfo(xPropSet->getPropertySetInfo());
+ if (!xInfo.is())
+ return;
+
+ if (xInfo->hasPropertyByName("FillColor"))
+ {
+ sal_uInt32 nFillColor = 0;
+ xPropSet->getPropertyValue("FillColor") >>= nFillColor;
+
+ XFillColorItem aFillColorItem("", Color(ColorTransparency, nFillColor));
+ rSet.Put(aFillColorItem);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ (".uno:FillColor=" + OString::number(nFillColor)));
+ }
+
+ if (!(comphelper::LibreOfficeKit::isActive() && xInfo->hasPropertyByName("FillGradientName")))
+ return;
+
+ OUString aGradientName;
+ xPropSet->getPropertyValue("FillGradientName") >>= aGradientName;
+
+ ::css::uno::Reference< ::css::frame::XController > xChartController = xChart->getCurrentController();
+ if( !xChartController.is() )
+ return;
+
+ css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xChartController->getModel(), css::uno::UNO_QUERY);
+
+ if (!xFact.is())
+ return;
+
+ css::uno::Reference<css::container::XNameAccess> xNameAccess(
+ xFact->createInstance("com.sun.star.drawing.GradientTable"), css::uno::UNO_QUERY);
+
+ if (xNameAccess.is() && xNameAccess->hasByName(aGradientName))
+ {
+ css::uno::Any aAny = xNameAccess->getByName(aGradientName);
+
+ XFillGradientItem aItem;
+ aItem.SetName(aGradientName);
+ aItem.PutValue(aAny, MID_FILLGRADIENT);
+
+ rSet.Put(aItem);
+ }
+}
+
+// Attributes for Drawing-Objects
+
+void ScDrawShell::GetDrawAttrState( SfxItemSet& rSet )
+{
+ Point aMousePos = rViewData.GetMousePosPixel();
+ vcl::Window* pWindow = rViewData.GetActiveWin();
+ ScDrawView* pDrView = rViewData.GetScDrawView();
+ Point aPos = pWindow->PixelToLogic(aMousePos);
+ bool bHasMarked = pDrView->AreObjectsMarked();
+
+ if( bHasMarked )
+ {
+ SfxAllItemSet aSet(pDrView->GetAttrFromMarked(false));
+ if (const SfxPoolItem* pItem = nullptr;
+ aSet.GetItemState(SDRATTR_TEXTCOLUMNS_NUMBER, false, &pItem) >= SfxItemState::DEFAULT
+ && pItem)
+ {
+ aSet.Put(pItem->CloneSetWhich(SID_ATTR_TEXTCOLUMNS_NUMBER));
+ }
+ if (const SfxPoolItem* pItem = nullptr;
+ aSet.GetItemState(SDRATTR_TEXTCOLUMNS_SPACING, false, &pItem) >= SfxItemState::DEFAULT
+ && pItem)
+ {
+ aSet.Put(pItem->CloneSetWhich(SID_ATTR_TEXTCOLUMNS_SPACING));
+ }
+ rSet.Put(aSet, false);
+ }
+ else
+ {
+ pDrView->GetAttributes(rSet);
+ }
+
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ if ( !pPV )
+ return;
+
+ // #i52073# when a sheet with an active OLE object is deleted,
+ // the slot state is queried without an active page view
+
+ // Items for position and size (see ScGridWindow::UpdateStatusPosSize, #108137#)
+
+ // #i34458# The SvxSizeItem in SID_TABLE_CELL is no longer needed by
+ // SvxPosSizeStatusBarControl, it's enough to have it in SID_ATTR_SIZE.
+
+ bool bActionItem = false;
+ if ( pDrView->IsAction() ) // action rectangle
+ {
+ tools::Rectangle aRect;
+ pDrView->TakeActionRect( aRect );
+ if ( !aRect.IsEmpty() )
+ {
+ pPV->LogicToPagePos(aRect);
+ rSet.Put( SfxPointItem( SID_ATTR_POSITION, aRect.TopLeft() ) );
+ Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() );
+ rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize ) );
+ bActionItem = true;
+ }
+
+ // Set correct colors for charts in sidebar
+ setupFillColorForChart(pDrView->GetSfxViewShell(), rSet);
+ }
+ if ( bActionItem )
+ return;
+
+ if ( pDrView->AreObjectsMarked() ) // selected objects
+ {
+ tools::Rectangle aRect = pDrView->GetAllMarkedRect();
+ pPV->LogicToPagePos(aRect);
+ rSet.Put( SfxPointItem( SID_ATTR_POSITION, aRect.TopLeft() ) );
+ Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() );
+ rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize ) );
+ }
+ else // mouse position
+ {
+ // aPos is initialized above
+ pPV->LogicToPagePos(aPos);
+ rSet.Put( SfxPointItem( SID_ATTR_POSITION, aPos ) );
+ rSet.Put( SvxSizeItem( SID_ATTR_SIZE, Size( 0, 0 ) ) );
+ }
+}
+
+void ScDrawShell::GetAttrFuncState(SfxItemSet &rSet)
+{
+ // Disable dialogs for Draw-attributes if necessary
+
+ ScDrawView* pDrView = rViewData.GetScDrawView();
+ SfxItemSet aViewSet = pDrView->GetAttrFromMarked(false);
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+ bool bShowArea = true, bShowMeasure = true;
+
+ for ( size_t i = 0; i < nMarkCount && i < 50; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ SdrObjKind nObjType = pObj->GetObjIdentifier();
+
+ if ( nObjType != SdrObjKind::Measure )
+ bShowMeasure = false;
+
+ // If marked object is 2D, disable format area command.
+ if ( nObjType == SdrObjKind::PolyLine ||
+ nObjType == SdrObjKind::Line ||
+ nObjType == SdrObjKind::PathLine ||
+ nObjType == SdrObjKind::FreehandLine ||
+ nObjType == SdrObjKind::Edge ||
+ nObjType == SdrObjKind::CircleArc ||
+ bShowMeasure )
+ bShowArea = false;
+
+ if ( !bShowArea && !bShowMeasure )
+ break;
+ }
+
+ if ( !bShowArea )
+ rSet.DisableItem( SID_ATTRIBUTES_AREA );
+
+ if ( !bShowMeasure )
+ rSet.DisableItem( SID_MEASURE_DLG );
+
+ if ( aViewSet.GetItemState( XATTR_LINESTYLE ) == SfxItemState::DEFAULT )
+ {
+ rSet.DisableItem( SID_ATTRIBUTES_LINE );
+ rSet.DisableItem( SID_ATTR_LINEEND_STYLE ); // Tbx-Controller
+ }
+
+ if ( aViewSet.GetItemState( XATTR_FILLSTYLE ) == SfxItemState::DEFAULT )
+ rSet.DisableItem( SID_ATTRIBUTES_AREA );
+}
+
+bool ScDrawShell::AreAllObjectsOnLayer(SdrLayerID nLayerNo,const SdrMarkList& rMark)
+{
+ bool bResult=true;
+ const size_t nCount = rMark.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMark.GetMark(i)->GetMarkedSdrObj();
+ if ( dynamic_cast<const SdrUnoObj*>( pObj) == nullptr )
+ {
+ if(nLayerNo!=pObj->GetLayer())
+ {
+ bResult=false;
+ break;
+ }
+ }
+ }
+ return bResult;
+}
+
+void ScDrawShell::GetDrawAttrStateForIFBX( SfxItemSet& rSet )
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMark(0) != nullptr )
+ {
+ SfxItemSet aNewAttr(pView->GetGeoAttrFromMarked());
+ rSet.Put(aNewAttr, false);
+ }
+}
+
+void ScDrawShell::Activate (const bool)
+{
+ ContextChangeEventMultiplexer::NotifyContextChange(
+ GetFrame()->GetFrame().GetController(),
+ vcl::EnumContext::GetContextEnum(
+ GetSidebarContextName()));
+}
+
+const OUString & ScDrawShell::GetSidebarContextName()
+{
+ return vcl::EnumContext::GetContextName(
+ svx::sidebar::SelectionAnalyzer::GetContextForSelection_SC(
+ GetDrawView()->GetMarkedObjectList()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drawsh4.cxx b/sc/source/ui/drawfunc/drawsh4.cxx
new file mode 100644
index 0000000000..5c9b8cf926
--- /dev/null
+++ b/sc/source/ui/drawfunc/drawsh4.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 <svx/svdotext.hxx>
+#include <svx/xdef.hxx>
+#include <svx/svdoashp.hxx>
+#include <drawsh.hxx>
+#include <drawview.hxx>
+#include <viewdata.hxx>
+
+void ScDrawShell::GetFormTextState(SfxItemSet& rSet)
+{
+ const SdrObject* pObj = nullptr;
+ ScDrawView* pDrView = rViewData.GetScDrawView();
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+
+ if ( rMarkList.GetMarkCount() == 1 )
+ pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ const SdrTextObj* pTextObj = DynCastSdrTextObj(pObj);
+ const bool bDeactivate(
+ !pObj ||
+ !pTextObj ||
+ !pTextObj->HasText() ||
+ dynamic_cast< const SdrObjCustomShape* >(pObj)); // #121538# no FontWork for CustomShapes
+
+ if(bDeactivate)
+ {
+ rSet.DisableItem(XATTR_FORMTXTSTYLE);
+ rSet.DisableItem(XATTR_FORMTXTADJUST);
+ rSet.DisableItem(XATTR_FORMTXTDISTANCE);
+ rSet.DisableItem(XATTR_FORMTXTSTART);
+ rSet.DisableItem(XATTR_FORMTXTMIRROR);
+ rSet.DisableItem(XATTR_FORMTXTHIDEFORM);
+ rSet.DisableItem(XATTR_FORMTXTOUTLINE);
+ rSet.DisableItem(XATTR_FORMTXTSHADOW);
+ rSet.DisableItem(XATTR_FORMTXTSHDWCOLOR);
+ rSet.DisableItem(XATTR_FORMTXTSHDWXVAL);
+ rSet.DisableItem(XATTR_FORMTXTSHDWYVAL);
+ }
+ else
+ {
+ SfxItemSet aViewAttr(pDrView->GetModel().GetItemPool());
+ pDrView->GetAttributes(aViewAttr);
+ rSet.Set(aViewAttr);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drawsh5.cxx b/sc/source/ui/drawfunc/drawsh5.cxx
new file mode 100644
index 0000000000..7c39afc2de
--- /dev/null
+++ b/sc/source/ui/drawfunc/drawsh5.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 <editeng/eeitem.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/bindings.hxx>
+#include <tools/urlobj.hxx>
+#include <cliputil.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/hlnkitem.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/extrusionbar.hxx>
+#include <svx/fontworkbar.hxx>
+#include <svx/svdogrp.hxx>
+#include <sfx2/docfile.hxx>
+#include <osl/diagnose.h>
+#include <svx/diagram/IDiagramHelper.hxx>
+
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+
+#include <drawsh.hxx>
+#include <drawview.hxx>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <undotab.hxx>
+#include <drwlayer.hxx>
+#include <drtxtob.hxx>
+#include <memory>
+
+#include <sc.hrc>
+
+using namespace com::sun::star;
+
+void ScDrawShell::GetHLinkState( SfxItemSet& rSet ) // Hyperlink
+{
+ ScDrawView* pView = rViewData.GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ // Hyperlink
+
+ SvxHyperlinkItem aHLinkItem;
+
+ if ( rMarkList.GetMarkCount() == 1 ) // URL-Button marked ?
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if ( pObj && !pObj->getHyperlink().isEmpty() )
+ {
+ aHLinkItem.SetURL( pObj->getHyperlink() );
+ aHLinkItem.SetInsertMode(HLINK_FIELD);
+ }
+ SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObj );
+ if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor())
+ {
+ const uno::Reference<awt::XControlModel>& xControlModel = pUnoCtrl->GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "UNO-Control without model" );
+ if( !xControlModel.is() )
+ return;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo();
+
+ OUString sPropButtonType( "ButtonType" );
+
+ if(xInfo->hasPropertyByName( sPropButtonType ))
+ {
+ uno::Any aAny = xPropSet->getPropertyValue( sPropButtonType );
+ form::FormButtonType eTmp;
+ if ( (aAny >>= eTmp) && eTmp == form::FormButtonType_URL )
+ {
+ OUString sTmp;
+ // Label
+ OUString sPropLabel( "Label" );
+ if(xInfo->hasPropertyByName( sPropLabel ))
+ {
+ aAny = xPropSet->getPropertyValue( sPropLabel );
+ if ( (aAny >>= sTmp) && !sTmp.isEmpty() )
+ {
+ aHLinkItem.SetName(sTmp);
+ }
+ }
+ // URL
+ OUString sPropTargetURL( "TargetURL" );
+ if(xInfo->hasPropertyByName( sPropTargetURL ))
+ {
+ aAny = xPropSet->getPropertyValue( sPropTargetURL );
+ if ( (aAny >>= sTmp) && !sTmp.isEmpty() )
+ {
+ aHLinkItem.SetURL(sTmp);
+ }
+ }
+ // Target
+ OUString sPropTargetFrame( "TargetFrame" );
+ if(xInfo->hasPropertyByName( sPropTargetFrame ))
+ {
+ aAny = xPropSet->getPropertyValue( sPropTargetFrame );
+ if ( (aAny >>= sTmp) && !sTmp.isEmpty() )
+ {
+ aHLinkItem.SetTargetFrame(sTmp);
+ }
+ }
+ aHLinkItem.SetInsertMode(HLINK_BUTTON);
+ }
+ }
+ }
+ }
+
+ rSet.Put(aHLinkItem);
+}
+
+void ScDrawShell::ExecuteHLink( const SfxRequest& rReq )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_HYPERLINK_SETLINK:
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_HYPERLINK_SETLINK, true, &pItem ) == SfxItemState::SET )
+ {
+ const SvxHyperlinkItem* pHyper = static_cast<const SvxHyperlinkItem*>(pItem);
+ const OUString& rName = pHyper->GetName();
+ const OUString& rURL = pHyper->GetURL();
+ const OUString& rTarget = pHyper->GetTargetFrame();
+ SvxLinkInsertMode eMode = pHyper->GetInsertMode();
+
+ bool bDone = false;
+ if ( eMode == HLINK_FIELD || eMode == HLINK_BUTTON )
+ {
+ ScDrawView* pView = rViewData.GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if ( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObj );
+ if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor())
+ {
+ const uno::Reference<awt::XControlModel>& xControlModel =
+ pUnoCtrl->GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "UNO-Control without model" );
+ if( !xControlModel.is() )
+ return;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySetInfo > xInfo = xPropSet->getPropertySetInfo();
+
+ OUString sPropTargetURL( "TargetURL" );
+
+ // Is it possible to set a URL in the object?
+ if (xInfo->hasPropertyByName( sPropTargetURL ))
+ {
+
+ OUString sPropButtonType( "ButtonType");
+ OUString sPropTargetFrame( "TargetFrame" );
+ OUString sPropLabel( "Label" );
+
+ if ( xInfo->hasPropertyByName( sPropLabel ) )
+ {
+ xPropSet->setPropertyValue( sPropLabel, uno::Any(rName) );
+ }
+
+ OUString aTmp = INetURLObject::GetAbsURL( rViewData.GetDocShell()->GetMedium()->GetBaseURL(), rURL );
+ xPropSet->setPropertyValue( sPropTargetURL, uno::Any(aTmp) );
+
+ if( !rTarget.isEmpty() && xInfo->hasPropertyByName( sPropTargetFrame ) )
+ {
+ xPropSet->setPropertyValue( sPropTargetFrame, uno::Any(rTarget) );
+ }
+
+ if ( xInfo->hasPropertyByName( sPropButtonType ) )
+ {
+ xPropSet->setPropertyValue( sPropButtonType, uno::Any(form::FormButtonType_URL) );
+ }
+
+ //! Undo ???
+ rViewData.GetDocShell()->SetDocumentModified();
+ bDone = true;
+ }
+ }
+ else
+ {
+ pObj->setHyperlink(rURL);
+ setModified();
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ rViewData.GetViewShell()->
+ InsertURL( rName, rURL, rTarget, static_cast<sal_uInt16>(eMode) );
+
+ // If "text" is received by InsertURL of ViewShell, then the DrawShell is turned off !!!
+ }
+ }
+ break;
+ default:
+ OSL_FAIL("wrong slot");
+ }
+}
+
+// Functions on Drawing-Objects
+
+void ScDrawShell::ExecDrawFunc( SfxRequest& rReq )
+{
+ SfxBindings& rBindings = rViewData.GetBindings();
+ ScTabView* pTabView = rViewData.GetView();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+
+ switch (nSlotId)
+ {
+ case SID_OBJECT_HEAVEN:
+ pView->SetMarkedToLayer( SC_LAYER_FRONT );
+ rBindings.Invalidate(SID_OBJECT_HEAVEN);
+ rBindings.Invalidate(SID_OBJECT_HELL);
+ break;
+ case SID_OBJECT_HELL:
+ pView->SetMarkedToLayer( SC_LAYER_BACK );
+ rBindings.Invalidate(SID_OBJECT_HEAVEN);
+ rBindings.Invalidate(SID_OBJECT_HELL);
+ // leave draw shell if nothing selected (layer may be locked)
+ rViewData.GetViewShell()->UpdateDrawShell();
+ break;
+
+ case SID_FRAME_TO_TOP:
+ pView->PutMarkedToTop();
+ break;
+ case SID_FRAME_TO_BOTTOM:
+ pView->PutMarkedToBtm();
+ break;
+ case SID_FRAME_UP:
+ pView->MovMarkedToTop();
+ break;
+ case SID_FRAME_DOWN:
+ pView->MovMarkedToBtm();
+ break;
+
+ case SID_GROUP:
+ pView->GroupMarked();
+ break;
+ case SID_UNGROUP:
+ pView->UnGroupMarked();
+ break;
+ case SID_ENTER_GROUP:
+ pView->EnterMarkedGroup();
+ break;
+ case SID_LEAVE_GROUP:
+ pView->LeaveOneGroup();
+ break;
+
+ case SID_REGENERATE_DIAGRAM:
+ case SID_EDIT_DIAGRAM:
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if (1 == rMarkList.GetMarkCount())
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ // Support advanced DiagramHelper
+ if(nullptr != pObj && pObj->isDiagram())
+ {
+ if(SID_REGENERATE_DIAGRAM == nSlotId)
+ {
+ pView->UnmarkAll();
+ pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj));
+ pView->MarkObj(pObj, pView->GetSdrPageView());
+ }
+ else // SID_EDIT_DIAGRAM
+ {
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ ScopedVclPtr<VclAbstractDialog> pDlg = pFact->CreateDiagramDialog(
+ pWin ? pWin->GetFrameWeld() : nullptr,
+ *static_cast<SdrObjGroup*>(pObj));
+ pDlg->Execute();
+ }
+ }
+ }
+
+ rReq.Done();
+ }
+ break;
+
+ case SID_MIRROR_HORIZONTAL:
+ case SID_FLIP_HORIZONTAL:
+ pView->MirrorAllMarkedHorizontal();
+ rBindings.Invalidate( SID_ATTR_TRANSFORM_ANGLE );
+ break;
+ case SID_MIRROR_VERTICAL:
+ case SID_FLIP_VERTICAL:
+ pView->MirrorAllMarkedVertical();
+ rBindings.Invalidate( SID_ATTR_TRANSFORM_ANGLE );
+ break;
+
+ case SID_OBJECT_ALIGN_LEFT:
+ case SID_ALIGN_ANY_LEFT:
+ if (pView->IsAlignPossible())
+ pView->AlignMarkedObjects(SdrHorAlign::Left, SdrVertAlign::NONE);
+ break;
+ case SID_OBJECT_ALIGN_CENTER:
+ case SID_ALIGN_ANY_HCENTER:
+ if (pView->IsAlignPossible())
+ pView->AlignMarkedObjects(SdrHorAlign::Center, SdrVertAlign::NONE);
+ break;
+ case SID_OBJECT_ALIGN_RIGHT:
+ case SID_ALIGN_ANY_RIGHT:
+ if (pView->IsAlignPossible())
+ pView->AlignMarkedObjects(SdrHorAlign::Right, SdrVertAlign::NONE);
+ break;
+ case SID_OBJECT_ALIGN_UP:
+ case SID_ALIGN_ANY_TOP:
+ if (pView->IsAlignPossible())
+ pView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Top);
+ break;
+ case SID_OBJECT_ALIGN_MIDDLE:
+ case SID_ALIGN_ANY_VCENTER:
+ if (pView->IsAlignPossible())
+ pView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Center);
+ break;
+ case SID_OBJECT_ALIGN_DOWN:
+ case SID_ALIGN_ANY_BOTTOM:
+ if (pView->IsAlignPossible())
+ pView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Bottom);
+ break;
+
+ case SID_DELETE:
+ case SID_DELETE_CONTENTS:
+ pView->DeleteMarked();
+ rViewData.GetViewShell()->UpdateDrawShell();
+ break;
+
+ case SID_CUT:
+ pView->DoCut();
+ rViewData.GetViewShell()->UpdateDrawShell();
+ break;
+
+ case SID_COPY:
+ pView->DoCopy();
+ break;
+
+ case SID_PASTE:
+ ScClipUtil::PasteFromClipboard(GetViewData(), GetViewData().GetViewShell(), true);
+ break;
+
+ case SID_SELECTALL:
+ pView->MarkAll();
+ break;
+
+ case SID_ANCHOR_PAGE:
+ pView->SetPageAnchored();
+ rBindings.Invalidate( SID_ANCHOR_PAGE );
+ rBindings.Invalidate( SID_ANCHOR_CELL );
+ rBindings.Invalidate( SID_ANCHOR_CELL_RESIZE );
+ break;
+
+ case SID_ANCHOR_CELL:
+ pView->SetCellAnchored(false);
+ rBindings.Invalidate( SID_ANCHOR_PAGE );
+ rBindings.Invalidate( SID_ANCHOR_CELL );
+ rBindings.Invalidate( SID_ANCHOR_CELL_RESIZE );
+ break;
+
+ case SID_ANCHOR_CELL_RESIZE:
+ pView->SetCellAnchored(true);
+ rBindings.Invalidate( SID_ANCHOR_PAGE );
+ rBindings.Invalidate( SID_ANCHOR_CELL );
+ rBindings.Invalidate( SID_ANCHOR_CELL_RESIZE );
+ break;
+
+ case SID_ANCHOR_TOGGLE:
+ {
+ switch( pView->GetAnchorType() )
+ {
+ case SCA_CELL:
+ case SCA_CELL_RESIZE:
+ pView->SetPageAnchored();
+ break;
+ default:
+ pView->SetCellAnchored(false);
+ break;
+ }
+ }
+ rBindings.Invalidate( SID_ANCHOR_PAGE );
+ rBindings.Invalidate( SID_ANCHOR_CELL );
+ rBindings.Invalidate( SID_ANCHOR_CELL_RESIZE );
+ break;
+
+ case SID_OBJECT_ROTATE:
+ {
+ SdrDragMode eMode;
+ if (pView->GetDragMode() == SdrDragMode::Rotate)
+ eMode = SdrDragMode::Move;
+ else
+ eMode = SdrDragMode::Rotate;
+ pView->SetDragMode( eMode );
+ rBindings.Invalidate( SID_OBJECT_ROTATE );
+ rBindings.Invalidate( SID_OBJECT_MIRROR );
+ if (eMode == SdrDragMode::Rotate && !pView->IsFrameDragSingles())
+ {
+ pView->SetFrameDragSingles();
+ rBindings.Invalidate( SID_BEZIER_EDIT );
+ }
+ }
+ break;
+ case SID_OBJECT_MIRROR:
+ {
+ SdrDragMode eMode;
+ if (pView->GetDragMode() == SdrDragMode::Mirror)
+ eMode = SdrDragMode::Move;
+ else
+ eMode = SdrDragMode::Mirror;
+ pView->SetDragMode( eMode );
+ rBindings.Invalidate( SID_OBJECT_ROTATE );
+ rBindings.Invalidate( SID_OBJECT_MIRROR );
+ if (eMode == SdrDragMode::Mirror && !pView->IsFrameDragSingles())
+ {
+ pView->SetFrameDragSingles();
+ rBindings.Invalidate( SID_BEZIER_EDIT );
+ }
+ }
+ break;
+ case SID_BEZIER_EDIT:
+ {
+ bool bOld = pView->IsFrameDragSingles();
+ pView->SetFrameDragSingles( !bOld );
+ rBindings.Invalidate( SID_BEZIER_EDIT );
+ if (bOld && pView->GetDragMode() != SdrDragMode::Move)
+ {
+ pView->SetDragMode( SdrDragMode::Move );
+ rBindings.Invalidate( SID_OBJECT_ROTATE );
+ rBindings.Invalidate( SID_OBJECT_MIRROR );
+ }
+ }
+ break;
+
+ case SID_FONTWORK:
+ {
+ sal_uInt16 nId = ScGetFontWorkId();
+ SfxViewFrame& rViewFrm = rViewData.GetViewShell()->GetViewFrame();
+
+ if ( rReq.GetArgs() )
+ rViewFrm.SetChildWindow( nId,
+ static_cast<const SfxBoolItem&>(
+ (rReq.GetArgs()->Get(SID_FONTWORK))).
+ GetValue() );
+ else
+ rViewFrm.ToggleChildWindow( nId );
+
+ rBindings.Invalidate( SID_FONTWORK );
+ rReq.Done();
+ }
+ break;
+
+ case SID_ORIGINALSIZE:
+ pView->SetMarkedOriginalSize();
+ break;
+
+ case SID_FITCELLSIZE:
+ pView->FitToCellSize();
+ break;
+
+ case SID_ENABLE_HYPHENATION:
+ {
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(SID_ENABLE_HYPHENATION);
+ if( pItem )
+ {
+ SfxItemSetFixed<EE_PARA_HYPHENATE, EE_PARA_HYPHENATE> aSet( GetPool() );
+ bool bValue = pItem->GetValue();
+ aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, bValue ) );
+ pView->SetAttributes( aSet );
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_RENAME_OBJECT:
+ {
+ if(1 == pView->GetMarkedObjectCount())
+ {
+ // #i68101#
+ SdrObject* pSelected = pView->GetMarkedObjectByIndex(0);
+ OSL_ENSURE(pSelected, "ScDrawShell::ExecDrawFunc: nMarkCount, but no object (!)");
+
+ if(SC_LAYER_INTERN != pSelected->GetLayer())
+ {
+ OUString aName = pSelected->GetName();
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ ScopedVclPtr<AbstractSvxObjectNameDialog> pDlg(pFact->CreateSvxObjectNameDialog(pWin ? pWin->GetFrameWeld() : nullptr, aName));
+
+ pDlg->SetCheckNameHdl(LINK(this, ScDrawShell, NameObjectHdl));
+
+ if(RET_OK == pDlg->Execute())
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ pDlg->GetName(aName);
+
+ if (aName != pSelected->GetName())
+ {
+ // handle name change
+ const SdrObjKind nObjType(pSelected->GetObjIdentifier());
+
+ if (SdrObjKind::Graphic == nObjType && aName.isEmpty())
+ {
+ // graphics objects must have names
+ // (all graphics are supposed to be in the navigator)
+ ScDrawLayer* pModel = rViewData.GetDocument().GetDrawLayer();
+
+ if(pModel)
+ {
+ aName = pModel->GetNewGraphicName();
+ }
+ }
+
+ // An undo action for renaming is missing in svdraw (99363).
+ // For OLE objects (which can be identified using the persist name),
+ // ScUndoRenameObject can be used until there is a common action for all objects.
+ if(SdrObjKind::OLE2 == nObjType)
+ {
+ const OUString aPersistName = static_cast<SdrOle2Obj*>(pSelected)->GetPersistName();
+
+ if(!aPersistName.isEmpty())
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRenameObject>(pDocSh, aPersistName, pSelected->GetName(), aName));
+ }
+ }
+
+ // set new name
+ pSelected->SetName(aName);
+ }
+
+ // ChartListenerCollectionNeedsUpdate is needed for Navigator update
+ pDocSh->GetDocument().SetChartListenerCollectionNeedsUpdate( true );
+ pDocSh->SetDrawModified();
+ }
+ }
+ }
+ break;
+ }
+
+ // #i68101#
+ case SID_TITLE_DESCRIPTION_OBJECT:
+ {
+ if(1 == pView->GetMarkedObjectCount())
+ {
+ SdrObject* pSelected = pView->GetMarkedObjectByIndex(0);
+ OSL_ENSURE(pSelected, "ScDrawShell::ExecDrawFunc: nMarkCount, but no object (!)");
+
+ if(SC_LAYER_INTERN != pSelected->GetLayer())
+ {
+ OUString aTitle(pSelected->GetTitle());
+ OUString aDescription(pSelected->GetDescription());
+ bool isDecorative(pSelected->IsDecorative());
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ ScopedVclPtr<AbstractSvxObjectTitleDescDialog> pDlg(pFact->CreateSvxObjectTitleDescDialog(
+ pWin ? pWin->GetFrameWeld() : nullptr, aTitle, aDescription, isDecorative));
+
+ if(RET_OK == pDlg->Execute())
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ // handle Title and Description
+ pDlg->GetTitle(aTitle);
+ pDlg->GetDescription(aDescription);
+ pDlg->IsDecorative(isDecorative);
+ pSelected->SetTitle(aTitle);
+ pSelected->SetDescription(aDescription);
+ pSelected->SetDecorative(isDecorative);
+
+ // ChartListenerCollectionNeedsUpdate is needed for Navigator update
+ pDocSh->GetDocument().SetChartListenerCollectionNeedsUpdate( true );
+ pDocSh->SetDrawModified();
+ }
+ }
+ }
+ break;
+ }
+
+ case SID_EXTRUSION_TOGGLE:
+ case SID_EXTRUSION_TILT_DOWN:
+ case SID_EXTRUSION_TILT_UP:
+ case SID_EXTRUSION_TILT_LEFT:
+ case SID_EXTRUSION_TILT_RIGHT:
+ case SID_EXTRUSION_3D_COLOR:
+ case SID_EXTRUSION_DEPTH:
+ case SID_EXTRUSION_DIRECTION:
+ case SID_EXTRUSION_PROJECTION:
+ case SID_EXTRUSION_LIGHTING_DIRECTION:
+ case SID_EXTRUSION_LIGHTING_INTENSITY:
+ case SID_EXTRUSION_SURFACE:
+ case SID_EXTRUSION_DEPTH_FLOATER:
+ case SID_EXTRUSION_DIRECTION_FLOATER:
+ case SID_EXTRUSION_LIGHTING_FLOATER:
+ case SID_EXTRUSION_SURFACE_FLOATER:
+ case SID_EXTRUSION_DEPTH_DIALOG:
+ svx::ExtrusionBar::execute( pView, rReq, rBindings );
+ rReq.Ignore ();
+ break;
+
+ case SID_FONTWORK_SHAPE:
+ case SID_FONTWORK_SHAPE_TYPE:
+ case SID_FONTWORK_ALIGNMENT:
+ case SID_FONTWORK_SAME_LETTER_HEIGHTS:
+ case SID_FONTWORK_CHARACTER_SPACING:
+ case SID_FONTWORK_KERN_CHARACTER_PAIRS:
+ case SID_FONTWORK_CHARACTER_SPACING_FLOATER:
+ case SID_FONTWORK_ALIGNMENT_FLOATER:
+ case SID_FONTWORK_CHARACTER_SPACING_DIALOG:
+ svx::FontworkBar::execute( *pView, rReq, rBindings );
+ rReq.Ignore ();
+ break;
+
+ default:
+ break;
+ }
+}
+
+IMPL_LINK( ScDrawShell, NameObjectHdl, AbstractSvxObjectNameDialog&, rDialog, bool )
+{
+ OUString aName;
+ rDialog.GetName( aName );
+
+ ScDrawLayer* pModel = rViewData.GetDocument().GetDrawLayer();
+ if ( !aName.isEmpty() && pModel )
+ {
+ SCTAB nDummyTab;
+ if ( pModel->GetNamedObject( aName, SdrObjKind::NONE, nDummyTab ) )
+ {
+ // existing object found -> name invalid
+ return false;
+ }
+ }
+
+ return true; // name is valid
+}
+
+void ScDrawShell::ExecFormText(const SfxRequest& rReq)
+{
+ ScDrawView* pDrView = rViewData.GetScDrawView();
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+
+ if ( rMarkList.GetMarkCount() == 1 && rReq.GetArgs() )
+ {
+ const SfxItemSet& rSet = *rReq.GetArgs();
+
+ if ( pDrView->IsTextEdit() )
+ pDrView->ScEndTextEdit();
+
+ pDrView->SetAttributes(rSet);
+ }
+}
+
+void ScDrawShell::ExecFormatPaintbrush( const SfxRequest& rReq )
+{
+ ScViewFunc* pView = rViewData.GetView();
+ if ( pView->HasPaintBrush() )
+ {
+ // cancel paintbrush mode
+ pView->ResetBrushDocument();
+ }
+ else
+ {
+ bool bLock = false;
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ if( pArgs && pArgs->Count() >= 1 )
+ bLock = pArgs->Get(SID_FORMATPAINTBRUSH).GetValue();
+
+ ScDrawView* pDrawView = rViewData.GetScDrawView();
+ if ( pDrawView && pDrawView->AreObjectsMarked() )
+ {
+ std::unique_ptr<SfxItemSet> pItemSet(new SfxItemSet( pDrawView->GetAttrFromMarked(true/*bOnlyHardAttr*/) ));
+ pView->SetDrawBrushSet( std::move(pItemSet), bLock );
+ }
+ }
+}
+
+void ScDrawShell::StateFormatPaintbrush( SfxItemSet& rSet )
+{
+ ScDrawView* pDrawView = rViewData.GetScDrawView();
+ bool bSelection = pDrawView && pDrawView->AreObjectsMarked();
+ bool bHasPaintBrush = rViewData.GetView()->HasPaintBrush();
+
+ if ( !bHasPaintBrush && !bSelection )
+ rSet.DisableItem( SID_FORMATPAINTBRUSH );
+ else
+ rSet.Put( SfxBoolItem( SID_FORMATPAINTBRUSH, bHasPaintBrush ) );
+}
+
+ScDrawView* ScDrawShell::GetDrawView()
+{
+ return rViewData.GetView()->GetScDrawView();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drformsh.cxx b/sc/source/ui/drawfunc/drformsh.cxx
new file mode 100644
index 0000000000..b91e08646b
--- /dev/null
+++ b/sc/source/ui/drawfunc/drformsh.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 <sfx2/objface.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/toolbarids.hxx>
+#include <sfx2/shell.hxx>
+
+#include <drawsh.hxx>
+#include <drformsh.hxx>
+#include <vcl/EnumContext.hxx>
+
+#define ShellClass_ScDrawFormShell
+#include <scslots.hxx>
+
+SFX_IMPL_INTERFACE(ScDrawFormShell, ScDrawShell)
+
+void ScDrawFormShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Format);
+
+ GetStaticInterface()->RegisterPopupMenu("form");
+}
+
+
+ScDrawFormShell::ScDrawFormShell(ScViewData& rData) :
+ ScDrawShell(rData)
+{
+ SetName("DrawForm");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Form));
+}
+
+ScDrawFormShell::~ScDrawFormShell()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drtxtob.cxx b/sc/source/ui/drawfunc/drtxtob.cxx
new file mode 100644
index 0000000000..189ae2793a
--- /dev/null
+++ b/sc/source/ui/drawfunc/drtxtob.cxx
@@ -0,0 +1,1256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <scitems.hxx>
+
+#include <i18nutil/transliteration.hxx>
+#include <editeng/adjustitem.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/urlfieldhelper.hxx>
+#include <editeng/editund2.hxx>
+#include <svx/hlnkitem.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/sdooitm.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/cliplistener.hxx>
+#include <vcl/transfer.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/languageoptions.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/unohelp2.hxx>
+
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <drtxtob.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <drawview.hxx>
+#include <viewutil.hxx>
+#include <tabvwsh.hxx>
+#include <gridwin.hxx>
+
+#define ShellClass_ScDrawTextObjectBar
+#include <scslots.hxx>
+
+using namespace ::com::sun::star;
+
+SFX_IMPL_INTERFACE(ScDrawTextObjectBar, SfxShell)
+
+void ScDrawTextObjectBar::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Text_Toolbox_Sc);
+
+ GetStaticInterface()->RegisterPopupMenu("drawtext");
+
+ GetStaticInterface()->RegisterChildWindow(ScGetFontWorkId());
+}
+
+
+// disable not wanted accelerators
+
+void ScDrawTextObjectBar::StateDisableItems( SfxItemSet &rSet )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while (nWhich)
+ {
+ rSet.DisableItem( nWhich );
+ nWhich = aIter.NextWhich();
+ }
+}
+
+ScDrawTextObjectBar::ScDrawTextObjectBar(ScViewData& rData) :
+ SfxShell(rData.GetViewShell()),
+ mrViewData(rData),
+ bPastePossible(false)
+{
+ SetPool( mrViewData.GetScDrawView()->GetDefaultAttr().GetPool() );
+
+ // At the switching-over the UndoManager is changed to edit mode
+ SfxUndoManager* pMgr = mrViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if ( !mrViewData.GetDocument().IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+
+ SetName("DrawText");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::DrawText));
+}
+
+ScDrawTextObjectBar::~ScDrawTextObjectBar()
+{
+ if ( mxClipEvtLstnr.is() )
+ {
+ mxClipEvtLstnr->RemoveListener( mrViewData.GetActiveWin() );
+
+ // The listener may just now be waiting for the SolarMutex and call the link
+ // afterwards, in spite of RemoveListener. So the link has to be reset, too.
+ mxClipEvtLstnr->ClearCallbackLink();
+ }
+}
+
+// Functions
+
+void ScDrawTextObjectBar::Execute( SfxRequest &rReq )
+{
+ ScDrawView* pView = mrViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ Outliner* pOutliner = pView->GetTextEditOutliner();
+
+ if (!pOutView || !pOutliner)
+ {
+ ExecuteGlobal( rReq ); // on whole objects
+ return;
+ }
+
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_COPY:
+ pOutView->Copy();
+ break;
+
+ case SID_CUT:
+ pOutView->Cut();
+ break;
+
+ case SID_PASTE:
+ pOutView->PasteSpecial();
+ break;
+
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ {
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ if (auto pIntItem = dynamic_cast<const SfxUInt32Item*>( pItem))
+ nFormat = static_cast<SotClipboardFormatId>(pIntItem->GetValue());
+
+ if ( nFormat != SotClipboardFormatId::NONE )
+ {
+ if (nFormat == SotClipboardFormatId::STRING)
+ pOutView->Paste();
+ else
+ pOutView->PasteSpecial(nFormat);
+ }
+ }
+ break;
+
+ case SID_PASTE_SPECIAL:
+ ExecutePasteContents( rReq );
+ break;
+
+ case SID_PASTE_UNFORMATTED:
+ pOutView->Paste();
+ break;
+
+ case SID_SELECTALL:
+ {
+ sal_Int32 nCount = pOutliner->GetParagraphCount();
+ ESelection aSel( 0,0,nCount,0 );
+ pOutView->SetSelection( aSel );
+ }
+ break;
+
+ case SID_CHARMAP:
+ {
+ auto const attribs = pOutView->GetAttribs();
+ const SvxFontItem& rItem = attribs.Get(EE_CHAR_FONTINFO);
+
+ OUString aString;
+ std::shared_ptr<SvxFontItem> aNewItem(std::make_shared<SvxFontItem>(EE_CHAR_FONTINFO));
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem = nullptr;
+ if( pArgs )
+ pArgs->GetItemState(SID_CHARMAP, false, &pItem);
+
+ if ( pItem )
+ {
+ aString = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false);
+ if ( pFontItem )
+ {
+ const OUString& aFontName(pFontItem->GetValue());
+ vcl::Font aFont(aFontName, Size(1,1)); // Size only because of CTOR
+ aNewItem = std::make_shared<SvxFontItem>(
+ aFont.GetFamilyType(), aFont.GetFamilyName(),
+ aFont.GetStyleName(), aFont.GetPitch(),
+ aFont.GetCharSet(), ATTR_FONT);
+ }
+ else
+ {
+ aNewItem.reset(rItem.Clone());
+ }
+ }
+ else
+ ScViewUtil::ExecuteCharMap(rItem, *mrViewData.GetViewShell());
+
+ if ( !aString.isEmpty() )
+ {
+ SfxItemSet aSet( pOutliner->GetEmptyItemSet() );
+ // tdf#125054
+ // checked against original, indeed aNewItem looks as if it can have
+ // either WhichID EE_CHAR_FONTINFO or ATTR_FONT when it was reset
+ // above, original uses '= SvxFontItem(..., ATTR_FONT).
+ // BUT beware: the operator=() did not copy the WhichID when resetting,
+ // so it indeed has WhichID of EE_CHAR_FONTINFO despite copying an Item
+ // that was constructed using ATTR_FONT as WhichID (!)
+ aSet.Put( *aNewItem, EE_CHAR_FONTINFO );
+
+ // If nothing is selected, then SetAttribs of the View selects a word
+ pOutView->GetOutliner()->QuickSetAttribs( aSet, pOutView->GetSelection() );
+ pOutView->InsertText(aString);
+ }
+
+ Invalidate( SID_ATTR_CHAR_FONT );
+ }
+ break;
+
+ case SID_HYPERLINK_SETLINK:
+ if( pReqArgs )
+ {
+ if ( const SvxHyperlinkItem* pHyper = pReqArgs->GetItemIfSet( SID_HYPERLINK_SETLINK) )
+ {
+ const OUString& rName = pHyper->GetName();
+ const OUString& rURL = pHyper->GetURL();
+ const OUString& rTarget = pHyper->GetTargetFrame();
+ SvxLinkInsertMode eMode = pHyper->GetInsertMode();
+
+ bool bDone = false;
+ if (eMode == HLINK_DEFAULT || eMode == HLINK_FIELD)
+ {
+ pOutView->SelectFieldAtCursor();
+
+ // insert new field
+ SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr );
+ aURLField.SetTargetFrame( rTarget );
+ SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD );
+ pOutView->InsertField( aURLItem );
+
+ bDone = true;
+ }
+
+ if (!bDone)
+ ExecuteGlobal( rReq ); // normal at View
+
+ // If "text" is received by InsertURL of ViewShell, then the DrawShell is turned off !!!
+ }
+ }
+ break;
+
+ case SID_OPEN_HYPERLINK:
+ {
+ const SvxFieldItem* pFieldItem
+ = pOutView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ ScGlobal::OpenURL(pURLField->GetURL(), pURLField->GetTargetFrame(), true);
+ }
+ }
+ break;
+
+ case SID_EDIT_HYPERLINK:
+ {
+ // Ensure the field is selected first
+ pOutView->SelectFieldAtCursor();
+ mrViewData.GetViewShell()->GetViewFrame().GetDispatcher()->Execute(SID_HYPERLINK_DIALOG);
+ }
+ break;
+
+ case SID_COPY_HYPERLINK_LOCATION:
+ {
+ const SvxFieldItem* pFieldItem
+ = pOutView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ uno::Reference<datatransfer::clipboard::XClipboard> xClipboard
+ = pOutView->GetWindow()->GetClipboard();
+ vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard);
+ }
+ }
+ break;
+
+ case SID_REMOVE_HYPERLINK:
+ {
+ URLFieldHelper::RemoveURLField(pOutView->GetEditView());
+ }
+ break;
+
+ case SID_ENABLE_HYPHENATION:
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ pView->ScEndTextEdit(); // end text edit before switching direction
+ ExecuteGlobal( rReq );
+ // restore consistent state between shells and functions:
+ mrViewData.GetDispatcher().Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ break;
+
+ case SID_THES:
+ {
+ OUString aReplaceText;
+ const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE);
+ if (pItem2)
+ aReplaceText = pItem2->GetValue();
+ if (!aReplaceText.isEmpty())
+ ReplaceTextWithSynonym( pOutView->GetEditView(), aReplaceText );
+ }
+ break;
+
+ case SID_THESAURUS:
+ {
+ pOutView->StartThesaurus(rReq.GetFrameWeld());
+ }
+ break;
+ }
+}
+
+void ScDrawTextObjectBar::GetState( SfxItemSet& rSet )
+{
+ SfxViewFrame& rViewFrm = mrViewData.GetViewShell()->GetViewFrame();
+ bool bHasFontWork = rViewFrm.HasChildWindow(SID_FONTWORK);
+ bool bDisableFontWork = false;
+
+ if (IsNoteEdit())
+ {
+ // #i21255# notes now support rich text formatting (#i74140# but not fontwork)
+ bDisableFontWork = true;
+ }
+
+ if ( bDisableFontWork )
+ rSet.DisableItem( SID_FONTWORK );
+ else
+ rSet.Put(SfxBoolItem(SID_FONTWORK, bHasFontWork));
+
+ if ( rSet.GetItemState( SID_HYPERLINK_GETLINK ) != SfxItemState::UNKNOWN )
+ {
+ SvxHyperlinkItem aHLinkItem;
+ SdrView* pView = mrViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ if ( pOutView )
+ {
+ bool bField = false;
+ const SvxFieldItem* pFieldItem = pOutView->GetFieldAtSelection();
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ aHLinkItem.SetName( pURLField->GetRepresentation() );
+ aHLinkItem.SetURL( pURLField->GetURL() );
+ aHLinkItem.SetTargetFrame( pURLField->GetTargetFrame() );
+ bField = true;
+ }
+
+ if (!bField)
+ {
+ // use selected text as name for urls
+ OUString sReturn = pOutView->GetSelected();
+ sal_Int32 nLen = std::min<sal_Int32>(sReturn.getLength(), 255);
+ sReturn = sReturn.copy(0, nLen);
+ aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' '));
+ }
+ }
+ rSet.Put(aHLinkItem);
+ }
+
+ if (rSet.GetItemState(SID_OPEN_HYPERLINK) != SfxItemState::UNKNOWN
+ || rSet.GetItemState(SID_EDIT_HYPERLINK) != SfxItemState::UNKNOWN
+ || rSet.GetItemState(SID_COPY_HYPERLINK_LOCATION) != SfxItemState::UNKNOWN
+ || rSet.GetItemState(SID_REMOVE_HYPERLINK) != SfxItemState::UNKNOWN)
+ {
+ OutlinerView* pOutView = mrViewData.GetScDrawView()->GetTextEditOutlinerView();
+ if (!URLFieldHelper::IsCursorAtURLField(pOutView, /*AlsoCheckBeforeCursor=*/true))
+ {
+ rSet.DisableItem( SID_OPEN_HYPERLINK );
+ rSet.DisableItem( SID_EDIT_HYPERLINK );
+ rSet.DisableItem( SID_COPY_HYPERLINK_LOCATION );
+ rSet.DisableItem( SID_REMOVE_HYPERLINK );
+ }
+ }
+
+ if( rSet.GetItemState( SID_TRANSLITERATE_HALFWIDTH ) != SfxItemState::UNKNOWN )
+ ScViewUtil::HideDisabledSlot( rSet, rViewFrm.GetBindings(), SID_TRANSLITERATE_HALFWIDTH );
+ if( rSet.GetItemState( SID_TRANSLITERATE_FULLWIDTH ) != SfxItemState::UNKNOWN )
+ ScViewUtil::HideDisabledSlot( rSet, rViewFrm.GetBindings(), SID_TRANSLITERATE_FULLWIDTH );
+ if( rSet.GetItemState( SID_TRANSLITERATE_HIRAGANA ) != SfxItemState::UNKNOWN )
+ ScViewUtil::HideDisabledSlot( rSet, rViewFrm.GetBindings(), SID_TRANSLITERATE_HIRAGANA );
+ if( rSet.GetItemState( SID_TRANSLITERATE_KATAKANA ) != SfxItemState::UNKNOWN )
+ ScViewUtil::HideDisabledSlot( rSet, rViewFrm.GetBindings(), SID_TRANSLITERATE_KATAKANA );
+
+ if ( rSet.GetItemState( SID_ENABLE_HYPHENATION ) != SfxItemState::UNKNOWN )
+ {
+ SdrView* pView = mrViewData.GetScDrawView();
+ SfxItemSet aAttrs(pView->GetModel().GetItemPool());
+ pView->GetAttributes( aAttrs );
+ if( aAttrs.GetItemState( EE_PARA_HYPHENATE ) >= SfxItemState::DEFAULT )
+ {
+ bool bValue = aAttrs.Get( EE_PARA_HYPHENATE ).GetValue();
+ rSet.Put( SfxBoolItem( SID_ENABLE_HYPHENATION, bValue ) );
+ }
+ }
+
+ if ( rSet.GetItemState( SID_THES ) != SfxItemState::UNKNOWN ||
+ rSet.GetItemState( SID_THESAURUS ) != SfxItemState::UNKNOWN )
+ {
+ SdrView * pView = mrViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+
+ OUString aStatusVal;
+ LanguageType nLang = LANGUAGE_NONE;
+ bool bIsLookUpWord = false;
+ if ( pOutView )
+ {
+ EditView& rEditView = pOutView->GetEditView();
+ bIsLookUpWord = GetStatusValueForThesaurusFromContext( aStatusVal, nLang, rEditView );
+ }
+ rSet.Put( SfxStringItem( SID_THES, aStatusVal ) );
+
+ // disable thesaurus main menu and context menu entry if there is nothing to look up
+ bool bCanDoThesaurus = ScModule::HasThesaurusLanguage( nLang );
+ if (!bIsLookUpWord || !bCanDoThesaurus)
+ rSet.DisableItem( SID_THES );
+ if (!bCanDoThesaurus)
+ rSet.DisableItem( SID_THESAURUS );
+ }
+
+ if (GetObjectShell()->isContentExtractionLocked())
+ {
+ rSet.DisableItem(SID_COPY);
+ rSet.DisableItem(SID_CUT);
+ }
+}
+
+IMPL_LINK( ScDrawTextObjectBar, ClipboardChanged, TransferableDataHelper*, pDataHelper, void )
+{
+ bPastePossible = ( pDataHelper->HasFormat( SotClipboardFormatId::STRING ) || pDataHelper->HasFormat( SotClipboardFormatId::RTF )
+ || pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ) );
+
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+ rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS );
+}
+
+void ScDrawTextObjectBar::GetClipState( SfxItemSet& rSet )
+{
+ SdrView* pView = mrViewData.GetScDrawView();
+ if ( !pView->GetTextEditOutlinerView() )
+ {
+ GetGlobalClipState( rSet );
+ return;
+ }
+
+ if ( !mxClipEvtLstnr.is() )
+ {
+ // create listener
+ mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScDrawTextObjectBar, ClipboardChanged ) );
+ vcl::Window* pWin = mrViewData.GetActiveWin();
+ mxClipEvtLstnr->AddListener( pWin );
+
+ // get initial state
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mrViewData.GetActiveWin() ) );
+ bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || aDataHelper.HasFormat( SotClipboardFormatId::RTF )
+ || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) );
+ }
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_PASTE:
+ case SID_PASTE_SPECIAL:
+ case SID_PASTE_UNFORMATTED:
+ if( !bPastePossible )
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ if ( bPastePossible )
+ {
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( mrViewData.GetActiveWin() ) );
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING );
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF );
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::RICHTEXT );
+ if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
+ aFormats.AddClipbrdFormat(SotClipboardFormatId::HTML_SIMPLE);
+
+ rSet.Put( aFormats );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+// Attributes
+
+void ScDrawTextObjectBar::ExecuteToggle( SfxRequest &rReq )
+{
+ // Underline
+
+ SdrView* pView = mrViewData.GetScDrawView();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ SfxItemSet aSet( pView->GetDefaultAttr() );
+
+ SfxItemSet aViewAttr(pView->GetModel().GetItemPool());
+ pView->GetAttributes(aViewAttr);
+
+ // Underline
+ FontLineStyle eOld = aViewAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle();
+ FontLineStyle eNew = eOld;
+ switch (nSlot)
+ {
+ case SID_ULINE_VAL_NONE:
+ eNew = LINESTYLE_NONE;
+ break;
+ case SID_ULINE_VAL_SINGLE:
+ eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ break;
+ case SID_ULINE_VAL_DOUBLE:
+ eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE;
+ break;
+ case SID_ULINE_VAL_DOTTED:
+ eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED;
+ break;
+ default:
+ break;
+ }
+ aSet.Put( SvxUnderlineItem( eNew, EE_CHAR_UNDERLINE ) );
+
+ pView->SetAttributes( aSet );
+ rReq.Done();
+ mrViewData.GetScDrawView()->InvalidateDrawTextAttrs();
+}
+
+static void lcl_RemoveFields( OutlinerView& rOutView )
+{
+ //! Outliner should have RemoveFields with a selection
+
+ Outliner* pOutliner = rOutView.GetOutliner();
+ if (!pOutliner) return;
+
+ ESelection aOldSel = rOutView.GetSelection();
+ ESelection aSel = aOldSel;
+ aSel.Adjust();
+ sal_Int32 nNewEnd = aSel.nEndPos;
+
+ bool bUpdate = pOutliner->IsUpdateLayout();
+ bool bChanged = false;
+
+ //! GetPortions and GetAttribs should be const!
+ EditEngine& rEditEng = const_cast<EditEngine&>(pOutliner->GetEditEngine());
+
+ sal_Int32 nParCount = pOutliner->GetParagraphCount();
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ if ( nPar >= aSel.nStartPara && nPar <= aSel.nEndPara )
+ {
+ std::vector<sal_Int32> aPortions;
+ rEditEng.GetPortions( nPar, aPortions );
+
+ for ( size_t nPos = aPortions.size(); nPos; )
+ {
+ --nPos;
+ sal_Int32 nEnd = aPortions[ nPos ];
+ sal_Int32 nStart = nPos ? aPortions[ nPos - 1 ] : 0;
+ // fields are single characters
+ if ( nEnd == nStart+1 &&
+ ( nPar > aSel.nStartPara || nStart >= aSel.nStartPos ) &&
+ ( nPar < aSel.nEndPara || nEnd <= aSel.nEndPos ) )
+ {
+ ESelection aFieldSel( nPar, nStart, nPar, nEnd );
+ SfxItemSet aSet = rEditEng.GetAttribs( aFieldSel );
+ if ( aSet.GetItemState( EE_FEATURE_FIELD ) == SfxItemState::SET )
+ {
+ if (!bChanged)
+ {
+ if (bUpdate)
+ pOutliner->SetUpdateLayout( false );
+ OUString aName = ScResId( STR_UNDO_DELETECONTENTS );
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ pOutliner->GetUndoManager().EnterListAction( aName, aName, 0, nViewShellId );
+ bChanged = true;
+ }
+
+ OUString aFieldText = rEditEng.GetText( aFieldSel );
+ pOutliner->QuickInsertText( aFieldText, aFieldSel );
+ if ( nPar == aSel.nEndPara )
+ {
+ nNewEnd = nNewEnd + aFieldText.getLength();
+ --nNewEnd;
+ }
+ }
+ }
+ }
+ }
+
+ if (bUpdate && bChanged)
+ {
+ pOutliner->GetUndoManager().LeaveListAction();
+ pOutliner->SetUpdateLayout( true );
+ }
+
+ if ( aOldSel == aSel ) // aSel is adjusted
+ aOldSel.nEndPos = nNewEnd;
+ else
+ aOldSel.nStartPos = nNewEnd; // if aOldSel is backwards
+ rOutView.SetSelection( aOldSel );
+}
+
+void ScDrawTextObjectBar::ExecuteAttr( SfxRequest &rReq )
+{
+ SdrView* pView = mrViewData.GetScDrawView();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ SfxItemSet aEditAttr( pView->GetModel().GetItemPool() );
+ pView->GetAttributes( aEditAttr );
+ SfxItemSet aNewAttr( *aEditAttr.GetPool(), aEditAttr.GetRanges() );
+
+ bool bSet = true;
+ switch ( nSlot )
+ {
+ case SID_ALIGNLEFT:
+ case SID_ALIGN_ANY_LEFT:
+ case SID_ATTR_PARA_ADJUST_LEFT:
+ aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+ break;
+
+ case SID_ALIGNCENTERHOR:
+ case SID_ALIGN_ANY_HCENTER:
+ case SID_ATTR_PARA_ADJUST_CENTER:
+ aNewAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+ break;
+
+ case SID_ALIGNRIGHT:
+ case SID_ALIGN_ANY_RIGHT:
+ case SID_ATTR_PARA_ADJUST_RIGHT:
+ aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+ break;
+
+ case SID_ALIGNBLOCK:
+ case SID_ALIGN_ANY_JUSTIFIED:
+ case SID_ATTR_PARA_ADJUST_BLOCK:
+ aNewAttr.Put( SvxAdjustItem( SvxAdjust::Block, EE_PARA_JUST ) );
+ break;
+
+ case SID_ATTR_PARA_LINESPACE_10:
+ {
+ SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL );
+ aItem.SetPropLineSpace( 100 );
+ aNewAttr.Put( aItem );
+ }
+ break;
+
+ case SID_ATTR_PARA_LINESPACE_15:
+ {
+ SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL );
+ aItem.SetPropLineSpace( 150 );
+ aNewAttr.Put( aItem );
+ }
+ break;
+
+ case SID_ATTR_PARA_LINESPACE_20:
+ {
+ SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL );
+ aItem.SetPropLineSpace( 200 );
+ aNewAttr.Put( aItem );
+ }
+ break;
+
+ case SID_SET_SUPER_SCRIPT:
+ {
+ SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT);
+ SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue());
+
+ if( eEsc == SvxEscapement::Superscript )
+ aItem.SetEscapement( SvxEscapement::Off );
+ else
+ aItem.SetEscapement( SvxEscapement::Superscript );
+ aNewAttr.Put( aItem );
+ }
+ break;
+
+ case SID_SET_SUB_SCRIPT:
+ {
+ SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT);
+ SvxEscapement eEsc = static_cast<SvxEscapement>(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue());
+
+ if( eEsc == SvxEscapement::Subscript )
+ aItem.SetEscapement( SvxEscapement::Off );
+ else
+ aItem.SetEscapement( SvxEscapement::Subscript );
+ aNewAttr.Put( aItem );
+ }
+ break;
+
+ case SID_TABLE_VERT_NONE:
+ case SID_TABLE_VERT_CENTER:
+ case SID_TABLE_VERT_BOTTOM:
+ {
+ SdrTextVertAdjust eTVA = SDRTEXTVERTADJUST_TOP;
+ if (nSlot == SID_TABLE_VERT_CENTER)
+ eTVA = SDRTEXTVERTADJUST_CENTER;
+ else if (nSlot == SID_TABLE_VERT_BOTTOM)
+ eTVA = SDRTEXTVERTADJUST_BOTTOM;
+ aNewAttr.Put(SdrTextVertAdjustItem(eTVA));
+ }
+ break;
+
+ case SID_PARASPACE_INCREASE:
+ case SID_PARASPACE_DECREASE:
+ {
+ SvxULSpaceItem aULSpace( aEditAttr.Get( EE_PARA_ULSPACE ) );
+ sal_uInt16 nUpper = aULSpace.GetUpper();
+ sal_uInt16 nLower = aULSpace.GetLower();
+
+ if ( nSlot == SID_PARASPACE_INCREASE )
+ {
+ nUpper += 100;
+ nLower += 100;
+ }
+ else
+ {
+ nUpper = std::max< sal_Int16 >( nUpper - 100, 0 );
+ nLower = std::max< sal_Int16 >( nLower - 100, 0 );
+ }
+
+ aULSpace.SetUpper( nUpper );
+ aULSpace.SetLower( nLower );
+ aNewAttr.Put( aULSpace );
+ }
+ break;
+
+ default:
+ bSet = false;
+ }
+
+ bool bDone = true;
+ bool bArgsInReq = ( pArgs != nullptr );
+
+ if ( !bArgsInReq )
+ {
+ switch ( nSlot )
+ {
+ case SID_CELL_FORMAT_RESET:
+ case SID_TEXT_STANDARD:
+ {
+ OutlinerView* pOutView = pView->IsTextEdit() ?
+ pView->GetTextEditOutlinerView() : nullptr;
+ if ( pOutView )
+ pOutView->Paint( tools::Rectangle() );
+
+ SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aEmptyAttr( *aEditAttr.GetPool() );
+ SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_MINFRAMEHEIGHT,
+ SDRATTR_TEXT_MAXFRAMEHEIGHT, SDRATTR_TEXT_MAXFRAMEWIDTH> aSizeAttr(*aEditAttr.GetPool());
+
+ aSizeAttr.Put(pView->GetAttrFromMarked(true));
+ pView->SetAttributes( aEmptyAttr, true );
+
+ if (IsNoteEdit())
+ {
+ pView->SetAttributes(aSizeAttr, false);
+ pView->GetTextEditObject()->AdjustTextFrameWidthAndHeight();
+ }
+
+ if ( pOutView )
+ {
+ lcl_RemoveFields( *pOutView );
+ pOutView->ShowCursor();
+ }
+
+ rReq.Done( aEmptyAttr );
+ mrViewData.GetScDrawView()->InvalidateDrawTextAttrs();
+ bDone = false; // already happened here
+ }
+ break;
+
+ case SID_GROW_FONT_SIZE:
+ case SID_SHRINK_FONT_SIZE:
+ {
+ OutlinerView* pOutView = pView->IsTextEdit() ?
+ pView->GetTextEditOutlinerView() : nullptr;
+ if ( pOutView )
+ {
+ if (SfxObjectShell* pObjSh = SfxObjectShell::Current())
+ {
+ const SvxFontListItem* pFontListItem = static_cast< const SvxFontListItem* >
+ ( pObjSh->GetItem( SID_ATTR_CHAR_FONTLIST ) );
+ const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr;
+ pOutView->GetEditView().ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pFontList );
+ mrViewData.GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ bDone = false;
+ }
+ }
+ break;
+
+ case SID_CHAR_DLG_EFFECT:
+ case SID_CHAR_DLG: // dialog button
+ case SID_ATTR_CHAR_FONT: // Controller not shown
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ bDone = ExecuteCharDlg( aEditAttr, aNewAttr , nSlot);
+ break;
+
+ case SID_PARA_DLG:
+ bDone = ExecuteParaDlg( aEditAttr, aNewAttr );
+ break;
+
+ case SID_ATTR_CHAR_WEIGHT:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_WEIGHT ) );
+ break;
+
+ case SID_ATTR_CHAR_POSTURE:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_ITALIC ) );
+ break;
+
+ case SID_ATTR_CHAR_UNDERLINE:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_UNDERLINE ) );
+ break;
+
+ case SID_ATTR_CHAR_OVERLINE:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_OVERLINE ) );
+ break;
+
+ case SID_ATTR_CHAR_CONTOUR:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_OUTLINE ) );
+ break;
+
+ case SID_ATTR_CHAR_SHADOWED:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_SHADOW ) );
+ break;
+
+ case SID_ATTR_CHAR_STRIKEOUT:
+ aNewAttr.Put( aEditAttr.Get( EE_CHAR_STRIKEOUT ) );
+ break;
+
+ case SID_DRAWTEXT_ATTR_DLG:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTextTabDialog(mrViewData.GetDialogParent(), &aEditAttr, pView));
+
+ bDone = ( RET_OK == pDlg->Execute() );
+
+ if ( bDone )
+ aNewAttr.Put( *pDlg->GetOutputItemSet() );
+
+ pDlg.disposeAndClear();
+
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ rBindings.Invalidate( SID_TABLE_VERT_NONE );
+ rBindings.Invalidate( SID_TABLE_VERT_CENTER );
+ rBindings.Invalidate( SID_TABLE_VERT_BOTTOM );
+ }
+ break;
+ }
+ }
+
+ if ( bSet || bDone )
+ {
+ rReq.Done( aNewAttr );
+ pArgs = rReq.GetArgs();
+ }
+
+ if ( !pArgs )
+ return;
+
+ if ( bArgsInReq &&
+ ( nSlot == SID_ATTR_CHAR_FONT || nSlot == SID_ATTR_CHAR_FONTHEIGHT ||
+ nSlot == SID_ATTR_CHAR_WEIGHT || nSlot == SID_ATTR_CHAR_POSTURE ) )
+ {
+ // font items from toolbox controller have to be applied for the right script type
+
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ if (nSlot == SID_ATTR_CHAR_FONT)
+ nScript = pView->GetScriptType();
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ aSetItem.PutItemForScriptType( nScript, pArgs->Get( nWhich ) );
+
+ pView->SetAttributes( aSetItem.GetItemSet() );
+ }
+ else if( nSlot == SID_ATTR_PARA_LRSPACE )
+ {
+ sal_uInt16 nId = SID_ATTR_PARA_LRSPACE;
+ const SvxLRSpaceItem& rItem = static_cast<const SvxLRSpaceItem&>(
+ pArgs->Get( nId ));
+ SfxItemSetFixed<EE_PARA_LRSPACE, EE_PARA_LRSPACE> aAttr( GetPool() );
+ nId = EE_PARA_LRSPACE;
+ SvxLRSpaceItem aLRSpaceItem( rItem.GetLeft(),
+ rItem.GetRight(), rItem.GetTextFirstLineOffset(), nId );
+ aAttr.Put( aLRSpaceItem );
+ pView->SetAttributes( aAttr );
+ }
+ else if( nSlot == SID_ATTR_PARA_LINESPACE )
+ {
+ SvxLineSpacingItem aLineSpaceItem = static_cast<const SvxLineSpacingItem&>(pArgs->Get(
+ GetPool().GetWhich(nSlot)));
+ SfxItemSetFixed<EE_PARA_SBL, EE_PARA_SBL> aAttr( GetPool() );
+ aAttr.Put( aLineSpaceItem );
+ pView->SetAttributes( aAttr );
+ }
+ else if( nSlot == SID_ATTR_PARA_ULSPACE )
+ {
+ SvxULSpaceItem aULSpaceItem = static_cast<const SvxULSpaceItem&>(pArgs->Get(
+ GetPool().GetWhich(nSlot)));
+ SfxItemSetFixed<EE_PARA_ULSPACE, EE_PARA_ULSPACE> aAttr( GetPool() );
+ aULSpaceItem.SetWhich(EE_PARA_ULSPACE);
+ aAttr.Put( aULSpaceItem );
+ pView->SetAttributes( aAttr );
+ }
+ else
+ {
+ // use args directly
+ pView->SetAttributes( *pArgs );
+ }
+ mrViewData.GetScDrawView()->InvalidateDrawTextAttrs();
+}
+
+void ScDrawTextObjectBar::GetAttrState( SfxItemSet& rDestSet )
+{
+ if ( IsNoteEdit() )
+ {
+ // issue 21255 - Notes now support rich text formatting.
+ }
+
+ bool bDisableCTLFont = !::SvtCTLOptions::IsCTLFontEnabled();
+ bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled();
+
+ SdrView* pView = mrViewData.GetScDrawView();
+ SfxItemSet aAttrSet(pView->GetModel().GetItemPool());
+ pView->GetAttributes(aAttrSet);
+
+ // direct attributes
+
+ rDestSet.Put( aAttrSet );
+
+ // choose font info according to selection script type
+
+ SvtScriptType nScript = pView->GetScriptType();
+
+ // #i55929# input-language-dependent script type (depends on input language if nothing selected)
+ SvtScriptType nInputScript = nScript;
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ if (pOutView && !pOutView->GetSelection().HasRange())
+ {
+ LanguageType nInputLang = mrViewData.GetActiveWin()->GetInputLanguage();
+ if (nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM)
+ nInputScript = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang );
+ }
+
+ // #i55929# according to spec, nInputScript is used for font and font height only
+ if ( rDestSet.GetItemState( EE_CHAR_FONTINFO ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rDestSet, aAttrSet, EE_CHAR_FONTINFO, nInputScript );
+ if ( rDestSet.GetItemState( EE_CHAR_FONTHEIGHT ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rDestSet, aAttrSet, EE_CHAR_FONTHEIGHT, nInputScript );
+ if ( rDestSet.GetItemState( EE_CHAR_WEIGHT ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rDestSet, aAttrSet, EE_CHAR_WEIGHT, nScript );
+ if ( rDestSet.GetItemState( EE_CHAR_ITALIC ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rDestSet, aAttrSet, EE_CHAR_ITALIC, nScript );
+ // Alignment
+
+ SvxAdjust eAdj = aAttrSet.Get(EE_PARA_JUST).GetAdjust();
+ switch( eAdj )
+ {
+ case SvxAdjust::Left:
+ {
+ rDestSet.Put( SfxBoolItem( SID_ALIGNLEFT, true ) );
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, true ) );
+ }
+ break;
+ case SvxAdjust::Center:
+ {
+ rDestSet.Put( SfxBoolItem( SID_ALIGNCENTERHOR, true ) );
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, true ) );
+ }
+ break;
+ case SvxAdjust::Right:
+ {
+ rDestSet.Put( SfxBoolItem( SID_ALIGNRIGHT, true ) );
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, true ) );
+ }
+ break;
+ case SvxAdjust::Block:
+ {
+ rDestSet.Put( SfxBoolItem( SID_ALIGNBLOCK, true ) );
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, true ) );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ // pseudo slots for Format menu
+ rDestSet.Put( SfxBoolItem( SID_ALIGN_ANY_LEFT, eAdj == SvxAdjust::Left ) );
+ rDestSet.Put( SfxBoolItem( SID_ALIGN_ANY_HCENTER, eAdj == SvxAdjust::Center ) );
+ rDestSet.Put( SfxBoolItem( SID_ALIGN_ANY_RIGHT, eAdj == SvxAdjust::Right ) );
+ rDestSet.Put( SfxBoolItem( SID_ALIGN_ANY_JUSTIFIED, eAdj == SvxAdjust::Block ) );
+
+ SvxLRSpaceItem aLR = aAttrSet.Get( EE_PARA_LRSPACE );
+ aLR.SetWhich(SID_ATTR_PARA_LRSPACE);
+ rDestSet.Put(aLR);
+ Invalidate( SID_ATTR_PARA_LRSPACE );
+ SfxItemState eState = aAttrSet.GetItemState( EE_PARA_LRSPACE );
+ if ( eState == SfxItemState::DONTCARE )
+ rDestSet.InvalidateItem(SID_ATTR_PARA_LRSPACE);
+ //xuxu for Line Space
+ SvxLineSpacingItem aLineSP = aAttrSet.Get( EE_PARA_SBL );
+ aLineSP.SetWhich(SID_ATTR_PARA_LINESPACE);
+ rDestSet.Put(aLineSP);
+ Invalidate(SID_ATTR_PARA_LINESPACE);
+ eState = aAttrSet.GetItemState( EE_PARA_SBL );
+ if ( eState == SfxItemState::DONTCARE )
+ rDestSet.InvalidateItem(SID_ATTR_PARA_LINESPACE);
+ //xuxu for UL Space
+ SvxULSpaceItem aULSP = aAttrSet.Get( EE_PARA_ULSPACE );
+ aULSP.SetWhich(SID_ATTR_PARA_ULSPACE);
+ rDestSet.Put(aULSP);
+ Invalidate(SID_ATTR_PARA_ULSPACE);
+ Invalidate(SID_PARASPACE_INCREASE);
+ Invalidate(SID_PARASPACE_DECREASE);
+ eState = aAttrSet.GetItemState( EE_PARA_ULSPACE );
+ if( eState >= SfxItemState::DEFAULT )
+ {
+ if ( !aULSP.GetUpper() && !aULSP.GetLower() )
+ rDestSet.DisableItem( SID_PARASPACE_DECREASE );
+ }
+ else
+ {
+ rDestSet.DisableItem( SID_PARASPACE_INCREASE );
+ rDestSet.DisableItem( SID_PARASPACE_DECREASE );
+ rDestSet.InvalidateItem(SID_ATTR_PARA_ULSPACE);
+ }
+
+ // Line spacing
+
+ sal_uInt16 nLineSpace = aAttrSet.Get( EE_PARA_SBL ).GetPropLineSpace();
+ switch( nLineSpace )
+ {
+ case 100:
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, true ) );
+ break;
+ case 150:
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, true ) );
+ break;
+ case 200:
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, true ) );
+ break;
+ }
+
+ // super-/subscript
+ SvxEscapement eEsc = static_cast<SvxEscapement>(aAttrSet.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue());
+ rDestSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript));
+ rDestSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript));
+
+ // Underline
+ eState = aAttrSet.GetItemState( EE_CHAR_UNDERLINE );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rDestSet.InvalidateItem( SID_ULINE_VAL_NONE );
+ rDestSet.InvalidateItem( SID_ULINE_VAL_SINGLE );
+ rDestSet.InvalidateItem( SID_ULINE_VAL_DOUBLE );
+ rDestSet.InvalidateItem( SID_ULINE_VAL_DOTTED );
+ }
+ else
+ {
+ FontLineStyle eUnderline = aAttrSet.Get(EE_CHAR_UNDERLINE).GetLineStyle();
+ rDestSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE));
+ rDestSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE));
+ rDestSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED));
+ rDestSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE));
+ }
+
+ // horizontal / vertical
+
+ bool bLeftToRight = true;
+
+ SdrOutliner* pOutl = pView->GetTextEditOutliner();
+ if( pOutl )
+ {
+ if( pOutl->IsVertical() )
+ bLeftToRight = false;
+ }
+ else
+ bLeftToRight = aAttrSet.Get( SDRATTR_TEXTDIRECTION ).GetValue() == css::text::WritingMode_LR_TB;
+
+ if ( bDisableVerticalText )
+ {
+ rDestSet.DisableItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
+ rDestSet.DisableItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
+ }
+ else
+ {
+ rDestSet.Put( SfxBoolItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT, bLeftToRight ) );
+ rDestSet.Put( SfxBoolItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM, !bLeftToRight ) );
+ }
+
+ // left-to-right or right-to-left
+
+ if ( !bLeftToRight || bDisableCTLFont )
+ {
+ // disabled if vertical
+ rDestSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rDestSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ }
+ else if ( aAttrSet.GetItemState( EE_PARA_WRITINGDIR ) == SfxItemState::DONTCARE )
+ {
+ rDestSet.InvalidateItem( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rDestSet.InvalidateItem( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ }
+ else
+ {
+ SvxFrameDirection eAttrDir = aAttrSet.Get( EE_PARA_WRITINGDIR ).GetValue();
+ if ( eAttrDir == SvxFrameDirection::Environment )
+ {
+ // get "environment" direction from page style
+ if ( mrViewData.GetDocument().GetEditTextDirection( mrViewData.GetTabNo() ) == EEHorizontalTextDirection::R2L )
+ eAttrDir = SvxFrameDirection::Horizontal_RL_TB;
+ else
+ eAttrDir = SvxFrameDirection::Horizontal_LR_TB;
+ }
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, ( eAttrDir == SvxFrameDirection::Horizontal_LR_TB ) ) );
+ rDestSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, ( eAttrDir == SvxFrameDirection::Horizontal_RL_TB ) ) );
+ }
+}
+
+void ScDrawTextObjectBar::ExecuteTrans( const SfxRequest& rReq )
+{
+ TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() );
+ if ( nType == TransliterationFlags::NONE )
+ return;
+
+ ScDrawView* pView = mrViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ if ( pOutView )
+ {
+ // change selected text in object
+ pOutView->TransliterateText( nType );
+ }
+ else
+ {
+ //! apply to whole objects?
+ }
+}
+
+void ScDrawTextObjectBar::GetStatePropPanelAttr(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ SdrView* pView = mrViewData.GetScDrawView();
+
+ SfxItemSet aEditAttr(pView->GetModel().GetItemPool());
+ pView->GetAttributes(aEditAttr);
+ //SfxItemSet aAttrs( *aEditAttr.GetPool(), aEditAttr.GetRanges() );
+
+ while ( nWhich )
+ {
+ sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich)
+ ? GetPool().GetSlotId(nWhich)
+ : nWhich;
+ switch ( nSlotId )
+ {
+ case SID_TABLE_VERT_NONE:
+ case SID_TABLE_VERT_CENTER:
+ case SID_TABLE_VERT_BOTTOM:
+ bool bContour = false;
+ SfxItemState eConState = aEditAttr.GetItemState( SDRATTR_TEXT_CONTOURFRAME );
+ if( eConState != SfxItemState::DONTCARE )
+ {
+ bContour = aEditAttr.Get( SDRATTR_TEXT_CONTOURFRAME ).GetValue();
+ }
+ if (bContour) break;
+
+ SfxItemState eVState = aEditAttr.GetItemState( SDRATTR_TEXT_VERTADJUST );
+ //SfxItemState eHState = aAttrs.GetItemState( SDRATTR_TEXT_HORZADJUST );
+
+ //if(SfxItemState::DONTCARE != eVState && SfxItemState::DONTCARE != eHState)
+ if(SfxItemState::DONTCARE != eVState)
+ {
+ SdrTextVertAdjust eTVA = aEditAttr.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+ bool bSet = (nSlotId == SID_TABLE_VERT_NONE && eTVA == SDRTEXTVERTADJUST_TOP) ||
+ (nSlotId == SID_TABLE_VERT_CENTER && eTVA == SDRTEXTVERTADJUST_CENTER) ||
+ (nSlotId == SID_TABLE_VERT_BOTTOM && eTVA == SDRTEXTVERTADJUST_BOTTOM);
+ rSet.Put(SfxBoolItem(nSlotId, bSet));
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(nSlotId, false));
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drtxtob1.cxx b/sc/source/ui/drawfunc/drtxtob1.cxx
new file mode 100644
index 0000000000..4810876a33
--- /dev/null
+++ b/sc/source/ui/drawfunc/drtxtob1.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 <editeng/eeitem.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/editids.hrc>
+#include <svx/svxids.hrc>
+#include <vcl/transfer.hxx>
+
+#include <drtxtob.hxx>
+#include <drawview.hxx>
+#include <viewdata.hxx>
+#include <gridwin.hxx>
+
+#include <scabstdlg.hxx>
+
+bool ScDrawTextObjectBar::ExecuteCharDlg( const SfxItemSet& rArgs,
+ SfxItemSet& rOutSet , sal_uInt16 nSlot)
+{
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScCharDlg(
+ mrViewData.GetDialogParent(), &rArgs,
+ mrViewData.GetSfxDocShell(), true));
+ if (nSlot == SID_CHAR_DLG_EFFECT)
+ {
+ pDlg->SetCurPageId("fonteffects");
+ }
+ bool bRet = ( pDlg->Execute() == RET_OK );
+
+ if ( bRet )
+ {
+ const SfxItemSet* pNewAttrs = pDlg->GetOutputItemSet();
+ if ( pNewAttrs )
+ rOutSet.Put( *pNewAttrs );
+ }
+
+ return bRet;
+}
+
+bool ScDrawTextObjectBar::ExecuteParaDlg( const SfxItemSet& rArgs,
+ SfxItemSet& rOutSet )
+{
+ SfxItemPool* pArgPool = rArgs.GetPool();
+ SfxItemSetFixed<
+ EE_ITEMS_START, EE_ITEMS_END,
+ SID_ATTR_PARA_PAGEBREAK, SID_ATTR_PARA_WIDOWS> aNewAttr(*pArgPool);
+ aNewAttr.Put( rArgs );
+
+ // Values have been taken over once to show the dialog.
+ // Has to be changed
+ // aNewAttr.Put( SvxParaDlgLimitsItem( 567 * 50, 5670) );
+
+ aNewAttr.Put( SvxHyphenZoneItem( false, SID_ATTR_PARA_HYPHENZONE ) );
+ aNewAttr.Put( SvxFormatBreakItem( SvxBreak::NONE, SID_ATTR_PARA_PAGEBREAK ) );
+ aNewAttr.Put( SvxFormatSplitItem( true, SID_ATTR_PARA_SPLIT) );
+ aNewAttr.Put( SvxWidowsItem( 0, SID_ATTR_PARA_WIDOWS) );
+ aNewAttr.Put( SvxOrphansItem( 0, SID_ATTR_PARA_ORPHANS) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScParagraphDlg(
+ mrViewData.GetDialogParent(), &aNewAttr));
+ bool bRet = ( pDlg->Execute() == RET_OK );
+
+ if ( bRet )
+ {
+ const SfxItemSet* pNewAttrs = pDlg->GetOutputItemSet();
+ if ( pNewAttrs )
+ rOutSet.Put( *pNewAttrs );
+ }
+
+ return bRet;
+}
+
+void ScDrawTextObjectBar::ExecutePasteContents( SfxRequest & /* rReq */ )
+{
+ SdrView* pView = mrViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(mrViewData.GetDialogParent()));
+
+ pDlg->Insert( SotClipboardFormatId::STRING, OUString() );
+ pDlg->Insert( SotClipboardFormatId::RTF, OUString() );
+ pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() );
+ pDlg->Insert(SotClipboardFormatId::HTML_SIMPLE, OUString());
+
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mrViewData.GetActiveWin() ) );
+
+ SotClipboardFormatId nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() );
+
+ //! test if outliner view is still valid
+
+ if (nFormat != SotClipboardFormatId::NONE)
+ {
+ if (nFormat == SotClipboardFormatId::STRING)
+ pOutView->Paste();
+ else
+ pOutView->PasteSpecial(nFormat);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/drtxtob2.cxx b/sc/source/ui/drawfunc/drtxtob2.cxx
new file mode 100644
index 0000000000..37629721cf
--- /dev/null
+++ b/sc/source/ui/drawfunc/drtxtob2.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 <editeng/adjustitem.hxx>
+#include <svx/fontwork.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/request.hxx>
+#include <svl/whiter.hxx>
+#include <svx/svdoashp.hxx>
+#include <sc.hrc>
+#include <drtxtob.hxx>
+#include <viewdata.hxx>
+#include <drawview.hxx>
+#include <tabvwsh.hxx>
+#include <drwlayer.hxx>
+
+sal_uInt16 ScGetFontWorkId()
+{
+ return SvxFontWorkChildWindow::GetChildWindowId();
+}
+
+bool ScDrawTextObjectBar::IsNoteEdit() const
+{
+ return ScDrawLayer::IsNoteCaption( mrViewData.GetView()->GetScDrawView()->GetTextEditObject() );
+}
+
+// if no text edited, functions like in drawsh
+
+void ScDrawTextObjectBar::ExecuteGlobal( SfxRequest &rReq )
+{
+ ScTabView* pTabView = mrViewData.GetView();
+ ScDrawView* pView = pTabView->GetScDrawView();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_COPY:
+ pView->DoCopy();
+ break;
+
+ case SID_CUT:
+ pView->DoCut();
+ mrViewData.GetViewShell()->UpdateDrawShell();
+ break;
+
+ case SID_PASTE:
+ case SID_PASTE_SPECIAL:
+ case SID_PASTE_UNFORMATTED:
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ case SID_HYPERLINK_SETLINK:
+ {
+ // cell methods are at cell shell, which is not available if
+ // ScDrawTextObjectBar is active
+ //! move paste etc. to view shell?
+ }
+ break;
+
+ case SID_SELECTALL:
+ pView->MarkAll();
+ break;
+
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ {
+ SfxItemSetFixed<SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION> aAttr(pView->GetModel().GetItemPool());
+ aAttr.Put( SvxWritingModeItem(
+ nSlot == SID_TEXTDIRECTION_LEFT_TO_RIGHT ?
+ css::text::WritingMode_LR_TB : css::text::WritingMode_TB_RL,
+ SDRATTR_TEXTDIRECTION ) );
+ pView->SetAttributes( aAttr );
+ mrViewData.GetScDrawView()->InvalidateDrawTextAttrs(); // Bidi slots may be disabled
+ rReq.Done( aAttr );
+ }
+ break;
+
+ case SID_ENABLE_HYPHENATION:
+ {
+ const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(SID_ENABLE_HYPHENATION);
+ if( pItem )
+ {
+ SfxItemSetFixed<EE_PARA_HYPHENATE, EE_PARA_HYPHENATE> aSet( GetPool() );
+ bool bValue = pItem->GetValue();
+ aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, bValue ) );
+ pView->SetAttributes( aSet );
+ }
+ rReq.Done();
+ }
+ break;
+ }
+}
+
+void ScDrawTextObjectBar::GetGlobalClipState( SfxItemSet& rSet )
+{
+ // cell methods are at cell shell, which is not available if
+ // ScDrawTextObjectBar is active -> disable everything
+ //! move paste etc. to view shell?
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ rSet.DisableItem( nWhich );
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScDrawTextObjectBar::ExecuteExtra( SfxRequest &rReq )
+{
+ ScTabView* pTabView = mrViewData.GetView();
+ ScDrawView* pView = pTabView->GetScDrawView();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_FONTWORK:
+ {
+ sal_uInt16 nId = SvxFontWorkChildWindow::GetChildWindowId();
+ SfxViewFrame& rViewFrm = mrViewData.GetViewShell()->GetViewFrame();
+
+ if ( rReq.GetArgs() )
+ rViewFrm.SetChildWindow( nId,
+ static_cast<const SfxBoolItem&>(
+ (rReq.GetArgs()->Get(SID_FONTWORK))).
+ GetValue() );
+ else
+ rViewFrm.ToggleChildWindow( nId );
+
+ rViewFrm.GetBindings().Invalidate( SID_FONTWORK );
+ rReq.Done();
+ }
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ {
+ SfxItemSetFixed<EE_PARA_WRITINGDIR, EE_PARA_WRITINGDIR,
+ EE_PARA_JUST, EE_PARA_JUST> aAttr(pView->GetModel().GetItemPool());
+ bool bLeft = ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT );
+ aAttr.Put( SvxFrameDirectionItem(
+ bLeft ? SvxFrameDirection::Horizontal_LR_TB : SvxFrameDirection::Horizontal_RL_TB,
+ EE_PARA_WRITINGDIR ) );
+ aAttr.Put( SvxAdjustItem(
+ bLeft ? SvxAdjust::Left : SvxAdjust::Right,
+ EE_PARA_JUST ) );
+ pView->SetAttributes( aAttr );
+ mrViewData.GetScDrawView()->InvalidateDrawTextAttrs();
+ rReq.Done(); //! Done(aAttr) ?
+
+ }
+ break;
+ }
+}
+
+void ScDrawTextObjectBar::ExecFormText(const SfxRequest& rReq)
+{
+ ScTabView* pTabView = mrViewData.GetView();
+ ScDrawView* pDrView = pTabView->GetScDrawView();
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+
+ if ( rMarkList.GetMarkCount() == 1 && rReq.GetArgs() )
+ {
+ const SfxItemSet& rSet = *rReq.GetArgs();
+
+ if ( pDrView->IsTextEdit() )
+ pDrView->ScEndTextEdit();
+
+ pDrView->SetAttributes(rSet);
+ }
+}
+
+void ScDrawTextObjectBar::GetFormTextState(SfxItemSet& rSet)
+{
+ const SdrObject* pObj = nullptr;
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+
+ if ( rMarkList.GetMarkCount() == 1 )
+ pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ const SdrTextObj* pTextObj = DynCastSdrTextObj(pObj);
+ const bool bDeactivate(
+ !pObj ||
+ !pTextObj ||
+ !pTextObj->HasText() ||
+ dynamic_cast< const SdrObjCustomShape* >(pObj)); // #121538# no FontWork for CustomShapes
+
+ if(bDeactivate)
+ {
+ rSet.DisableItem(XATTR_FORMTXTSTYLE);
+ rSet.DisableItem(XATTR_FORMTXTADJUST);
+ rSet.DisableItem(XATTR_FORMTXTDISTANCE);
+ rSet.DisableItem(XATTR_FORMTXTSTART);
+ rSet.DisableItem(XATTR_FORMTXTMIRROR);
+ rSet.DisableItem(XATTR_FORMTXTHIDEFORM);
+ rSet.DisableItem(XATTR_FORMTXTOUTLINE);
+ rSet.DisableItem(XATTR_FORMTXTSHADOW);
+ rSet.DisableItem(XATTR_FORMTXTSHDWCOLOR);
+ rSet.DisableItem(XATTR_FORMTXTSHDWXVAL);
+ rSet.DisableItem(XATTR_FORMTXTSHDWYVAL);
+ }
+ else
+ {
+ SfxItemSet aViewAttr(pDrView->GetModel().GetItemPool());
+ pDrView->GetAttributes(aViewAttr);
+ rSet.Set(aViewAttr);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuconarc.cxx b/sc/source/ui/drawfunc/fuconarc.cxx
new file mode 100644
index 0000000000..77deb115fa
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuconarc.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 <fuconarc.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+
+// Create default drawing objects via keyboard
+#include <svx/svdocirc.hxx>
+#include <svx/svxids.hrc>
+#include <svx/sxciaitm.hxx>
+#include <osl/diagnose.h>
+
+FuConstArc::FuConstArc(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuConstruct(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+}
+
+FuConstArc::~FuConstArc()
+{
+}
+
+bool FuConstArc::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuConstruct::MouseButtonDown( rMEvt );
+
+ if ( rMEvt.IsLeft() && !pView->IsAction() )
+ {
+ Point aPnt( pWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
+ pWindow->CaptureMouse();
+ pView->BegCreateObj( aPnt );
+ bReturn = true;
+ }
+ return bReturn;
+}
+
+bool FuConstArc::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = false;
+
+ if ( pView->IsCreateObj() && rMEvt.IsLeft() )
+ {
+ pView->EndCreateObj( SdrCreateCmd::NextPoint );
+ bReturn = true;
+ }
+ return (FuConstruct::MouseButtonUp(rMEvt) || bReturn);
+}
+
+void FuConstArc::Activate()
+{
+ SdrObjKind aObjKind;
+
+ switch (aSfxRequest.GetSlot() )
+ {
+ case SID_DRAW_ARC:
+ aNewPointer = PointerStyle::DrawArc;
+ aObjKind = SdrObjKind::CircleArc;
+ break;
+
+ case SID_DRAW_PIE:
+ aNewPointer = PointerStyle::DrawPie;
+ aObjKind = SdrObjKind::CircleSection;
+ break;
+
+ case SID_DRAW_CIRCLECUT:
+ aNewPointer = PointerStyle::DrawCircleCut;
+ aObjKind = SdrObjKind::CircleCut;
+ break;
+
+ default:
+ aNewPointer = PointerStyle::Cross;
+ aObjKind = SdrObjKind::CircleArc;
+ break;
+ }
+
+ pView->SetCurrentObj(aObjKind);
+
+ aOldPointer = pWindow->GetPointer();
+ rViewShell.SetActivePointer( aNewPointer );
+
+ FuDraw::Activate();
+}
+
+void FuConstArc::Deactivate()
+{
+ FuDraw::Deactivate();
+ rViewShell.SetActivePointer( aOldPointer );
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuConstArc::CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle)
+{
+ // case SID_DRAW_ARC:
+ // case SID_DRAW_PIE:
+ // case SID_DRAW_CIRCLECUT:
+
+ rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
+ *pDrDoc,
+ pView->GetCurrentObjInventor(),
+ pView->GetCurrentObjIdentifier()));
+
+ if(pObj)
+ {
+ if(dynamic_cast<const SdrCircObj*>( pObj.get() ) != nullptr)
+ {
+ tools::Rectangle aRect(rRectangle);
+
+ if(SID_DRAW_ARC == nID || SID_DRAW_CIRCLECUT == nID)
+ {
+ // force quadratic
+ ImpForceQuadratic(aRect);
+ }
+
+ pObj->SetLogicRect(aRect);
+
+ SfxItemSet aAttr(pDrDoc->GetItemPool());
+ aAttr.Put(makeSdrCircStartAngleItem(9000_deg100));
+ aAttr.Put(makeSdrCircEndAngleItem(0_deg100));
+
+ pObj->SetMergedItemSet(aAttr);
+ }
+ else
+ {
+ OSL_FAIL("Object is NO circle object");
+ }
+ }
+
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuconcustomshape.cxx b/sc/source/ui/drawfunc/fuconcustomshape.cxx
new file mode 100644
index 0000000000..2994187b36
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuconcustomshape.cxx
@@ -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 .
+ */
+
+#include <fuconcustomshape.hxx>
+#include <editeng/svxenum.hxx>
+#include <svx/gallery.hxx>
+#include <sfx2/request.hxx>
+#include <svx/fmmodel.hxx>
+#include <svl/itempool.hxx>
+#include <svl/stritem.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/xfillit0.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/sdtagitm.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <editeng/adjustitem.hxx>
+
+using namespace com::sun::star;
+
+FuConstCustomShape::FuConstCustomShape(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP, SdrModel* pDoc, const SfxRequest& rReq )
+ : FuConstruct(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ const SfxStringItem& rItm = static_cast<const SfxStringItem&>(pArgs->Get( rReq.GetSlot() ));
+ aCustomShape = rItm.GetValue();
+ }
+}
+
+FuConstCustomShape::~FuConstCustomShape()
+{
+}
+
+bool FuConstCustomShape::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuConstruct::MouseButtonDown(rMEvt);
+ if ( rMEvt.IsLeft() && !pView->IsAction() )
+ {
+ Point aPnt( pWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
+ pWindow->CaptureMouse();
+ pView->BegCreateObj(aPnt);
+
+ SdrObject* pObj = pView->GetCreateObj();
+ if ( pObj )
+ {
+ SetAttributes( pObj );
+ bool bForceNoFillStyle = false;
+ if ( static_cast<SdrObjCustomShape*>(pObj)->UseNoFillStyle() )
+ bForceNoFillStyle = true;
+ if ( bForceNoFillStyle )
+ pObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) );
+ }
+
+ bReturn = true;
+ }
+ return bReturn;
+}
+
+bool FuConstCustomShape::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = false;
+
+ if ( pView->IsCreateObj() && rMEvt.IsLeft() )
+ {
+ pView->EndCreateObj(SdrCreateCmd::ForceEnd);
+ bReturn = true;
+ }
+ return (FuConstruct::MouseButtonUp(rMEvt) || bReturn);
+}
+
+void FuConstCustomShape::Activate()
+{
+ pView->SetCurrentObj( SdrObjKind::CustomShape );
+
+ aNewPointer = PointerStyle::DrawRect;
+ aOldPointer = pWindow->GetPointer();
+ rViewShell.SetActivePointer( aNewPointer );
+
+ FuConstruct::Activate();
+}
+
+void FuConstCustomShape::Deactivate()
+{
+ FuConstruct::Deactivate();
+
+ rViewShell.SetActivePointer( aOldPointer );
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuConstCustomShape::CreateDefaultObject(const sal_uInt16 /* nID */, const tools::Rectangle& rRectangle)
+{
+ rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
+ *pDrDoc,
+ pView->GetCurrentObjInventor(),
+ pView->GetCurrentObjIdentifier()));
+
+ if( pObj )
+ {
+ tools::Rectangle aRectangle( rRectangle );
+ SetAttributes( pObj.get() );
+ if ( SdrObjCustomShape::doConstructOrthogonal( aCustomShape ) )
+ ImpForceQuadratic( aRectangle );
+ pObj->SetLogicRect( aRectangle );
+ }
+
+ return pObj;
+}
+
+void FuConstCustomShape::SetAttributes( SdrObject* pObj )
+{
+ bool bAttributesAppliedFromGallery = false;
+
+ if ( GalleryExplorer::GetSdrObjCount( GALLERY_THEME_POWERPOINT ) )
+ {
+ std::vector< OUString > aObjList;
+ if ( GalleryExplorer::FillObjListTitle( GALLERY_THEME_POWERPOINT, aObjList ) )
+ {
+ for ( std::vector<OUString>::size_type i = 0; i < aObjList.size(); i++ )
+ {
+ if ( aObjList[ i ].equalsIgnoreAsciiCase( aCustomShape ) )
+ {
+ FmFormModel aFormModel;
+ SfxItemPool& rPool(aFormModel.GetItemPool());
+ rPool.FreezeIdRanges();
+
+ if ( GalleryExplorer::GetSdrObj( GALLERY_THEME_POWERPOINT, i, &aFormModel ) )
+ {
+ const SdrObject* pSourceObj = aFormModel.GetPage( 0 )->GetObj( 0 );
+ if( pSourceObj )
+ {
+ const SfxItemSet& rSource = pSourceObj->GetMergedItemSet();
+ SfxItemSetFixed<
+ // Ranges from SdrAttrObj:
+ SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTDIRECTION,
+ // Graphic attributes, 3D properties,
+ // CustomShape properties:
+ SDRATTR_GRAF_FIRST,
+ SDRATTR_CUSTOMSHAPE_LAST,
+ // Range from SdrTextObj:
+ EE_ITEMS_START, EE_ITEMS_END> aDest(
+ pObj->getSdrModelFromSdrObject().GetItemPool());
+ aDest.Set( rSource );
+ pObj->SetMergedItemSet( aDest );
+ Degree100 nAngle = pSourceObj->GetRotateAngle();
+ if ( nAngle )
+ pObj->NbcRotate( pObj->GetSnapRect().Center(), nAngle );
+ bAttributesAppliedFromGallery = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ if ( !bAttributesAppliedFromGallery )
+ {
+ pObj->SetMergedItem( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+ pObj->SetMergedItem( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) );
+ pObj->SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) );
+ pObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) );
+ static_cast<SdrObjCustomShape*>(pObj)->MergeDefaultAttributes( &aCustomShape );
+ }
+}
+
+// #i33136#
+bool FuConstCustomShape::doConstructOrthogonal() const
+{
+ return SdrObjCustomShape::doConstructOrthogonal(aCustomShape);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuconpol.cxx b/sc/source/ui/drawfunc/fuconpol.cxx
new file mode 100644
index 0000000000..7290b5186a
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuconpol.cxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fuconpol.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+
+// Create default drawing objects via keyboard
+#include <svx/svdopath.hxx>
+#include <svx/svxids.hrc>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+
+FuConstPolygon::FuConstPolygon(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuConstruct(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+}
+
+FuConstPolygon::~FuConstPolygon()
+{
+}
+
+bool FuConstPolygon::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuConstruct::MouseButtonDown(rMEvt);
+
+ SdrViewEvent aVEvt;
+ (void)pView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt);
+ if (aVEvt.meEvent == SdrEventKind::BeginTextEdit)
+ {
+ // Text input not allowed here
+ aVEvt.meEvent = SdrEventKind::BeginDragObj;
+ pView->EnableExtendedMouseEventDispatcher(false);
+ }
+ else
+ {
+ pView->EnableExtendedMouseEventDispatcher(true);
+ }
+
+ if ( pView->MouseButtonDown(rMEvt, pWindow->GetOutDev()) )
+ bReturn = true;
+
+ return bReturn;
+}
+
+bool FuConstPolygon::MouseMove(const MouseEvent& rMEvt)
+{
+ pView->MouseMove(rMEvt, pWindow->GetOutDev());
+ return FuConstruct::MouseMove(rMEvt);
+}
+
+bool FuConstPolygon::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = false;
+ bool bSimple = false;
+
+ SdrViewEvent aVEvt;
+ (void)pView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aVEvt);
+
+ pView->MouseButtonUp(rMEvt, pWindow->GetOutDev());
+
+ if (aVEvt.meEvent == SdrEventKind::EndCreate)
+ {
+ bReturn = true;
+ bSimple = true; // Do not pass on double-click
+ }
+
+ bool bParent;
+ if (bSimple)
+ bParent = FuConstruct::SimpleMouseButtonUp(rMEvt);
+ else
+ bParent = FuConstruct::MouseButtonUp(rMEvt);
+
+ return (bParent || bReturn);
+}
+
+void FuConstPolygon::Activate()
+{
+ pView->EnableExtendedMouseEventDispatcher(true);
+
+ SdrObjKind eKind;
+
+ switch (GetSlotID())
+ {
+ case SID_DRAW_POLYGON_NOFILL:
+ case SID_DRAW_XPOLYGON_NOFILL:
+ {
+ eKind = SdrObjKind::PolyLine;
+ }
+ break;
+
+ case SID_DRAW_POLYGON:
+ case SID_DRAW_XPOLYGON:
+ {
+ eKind = SdrObjKind::Polygon;
+ }
+ break;
+
+ case SID_DRAW_BEZIER_NOFILL:
+ {
+ eKind = SdrObjKind::PathLine;
+ }
+ break;
+
+ case SID_DRAW_BEZIER_FILL:
+ {
+ eKind = SdrObjKind::PathFill;
+ }
+ break;
+
+ case SID_DRAW_FREELINE_NOFILL:
+ {
+ eKind = SdrObjKind::FreehandLine;
+ }
+ break;
+
+ case SID_DRAW_FREELINE:
+ {
+ eKind = SdrObjKind::FreehandFill;
+ }
+ break;
+
+ default:
+ {
+ eKind = SdrObjKind::PathLine;
+ }
+ break;
+ }
+
+ pView->SetCurrentObj(eKind);
+
+ pView->SetEditMode(SdrViewEditMode::Create);
+
+ FuConstruct::Activate();
+
+ aNewPointer = PointerStyle::DrawPolygon;
+ aOldPointer = pWindow->GetPointer();
+ rViewShell.SetActivePointer( aNewPointer );
+}
+
+void FuConstPolygon::Deactivate()
+{
+ pView->SetEditMode(SdrViewEditMode::Edit);
+
+ pView->EnableExtendedMouseEventDispatcher(false);
+
+ FuConstruct::Deactivate();
+
+ rViewShell.SetActivePointer( aOldPointer );
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuConstPolygon::CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle)
+{
+ // case SID_DRAW_XPOLYGON:
+ // case SID_DRAW_XPOLYGON_NOFILL:
+ // case SID_DRAW_POLYGON:
+ // case SID_DRAW_POLYGON_NOFILL:
+ // case SID_DRAW_BEZIER_FILL:
+ // case SID_DRAW_BEZIER_NOFILL:
+ // case SID_DRAW_FREELINE:
+ // case SID_DRAW_FREELINE_NOFILL:
+
+ rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
+ *pDrDoc,
+ pView->GetCurrentObjInventor(),
+ pView->GetCurrentObjIdentifier()));
+
+ if(pObj)
+ {
+ if(dynamic_cast<const SdrPathObj*>( pObj.get() ) != nullptr)
+ {
+ basegfx::B2DPolyPolygon aPoly;
+
+ switch(nID)
+ {
+ case SID_DRAW_BEZIER_FILL:
+ case SID_DRAW_BEZIER_NOFILL:
+ {
+ basegfx::B2DPolygon aInnerPoly;
+
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
+
+ const basegfx::B2DPoint aCenterBottom(rRectangle.Center().X(), rRectangle.Bottom());
+ aInnerPoly.appendBezierSegment(
+ aCenterBottom,
+ aCenterBottom,
+ basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()));
+
+ const basegfx::B2DPoint aCenterTop(rRectangle.Center().X(), rRectangle.Top());
+ aInnerPoly.appendBezierSegment(
+ aCenterTop,
+ aCenterTop,
+ basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top()));
+
+ aPoly.append(aInnerPoly);
+ break;
+ }
+ case SID_DRAW_FREELINE:
+ case SID_DRAW_FREELINE_NOFILL:
+ {
+ basegfx::B2DPolygon aInnerPoly;
+
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
+
+ aInnerPoly.appendBezierSegment(
+ basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()),
+ basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top()),
+ basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()));
+
+ aInnerPoly.appendBezierSegment(
+ basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()),
+ basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()),
+ basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top()));
+
+ aPoly.append(aInnerPoly);
+ break;
+ }
+ case SID_DRAW_XPOLYGON:
+ case SID_DRAW_XPOLYGON_NOFILL:
+ case SID_DRAW_POLYGON:
+ case SID_DRAW_POLYGON_NOFILL:
+ {
+ basegfx::B2DPolygon aInnerPoly;
+ const sal_Int32 nWdt(rRectangle.GetWidth());
+ const sal_Int32 nHgt(rRectangle.GetHeight());
+
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 30) / 100, rRectangle.Top() + (nHgt * 70) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top() + (nHgt * 15) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 65) / 100, rRectangle.Top()));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + nWdt, rRectangle.Top() + (nHgt * 30) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 50) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 75) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Bottom(), rRectangle.Right()));
+
+ if(SID_DRAW_POLYGON_NOFILL == nID)
+ {
+ aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()));
+ }
+ else
+ {
+ aInnerPoly.setClosed(true);
+ }
+
+ aPoly.append(aInnerPoly);
+ break;
+ }
+ }
+
+ static_cast<SdrPathObj*>(pObj.get())->SetPathPoly(aPoly);
+ }
+ else
+ {
+ OSL_FAIL("Object is NO path object");
+ }
+
+ pObj->SetLogicRect(rRectangle);
+ }
+
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuconrec.cxx b/sc/source/ui/drawfunc/fuconrec.cxx
new file mode 100644
index 0000000000..0f06de3d82
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuconrec.cxx
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fuconrec.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+
+#include <editeng/outlobj.hxx>
+#include <svx/constructhelper.hxx>
+// Create default drawing objects via keyboard
+#include <svx/svdopath.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svxids.hrc>
+#include <svx/strings.hrc>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdomeas.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+
+FuConstRectangle::FuConstRectangle(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuConstruct(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+}
+
+FuConstRectangle::~FuConstRectangle()
+{
+}
+
+bool FuConstRectangle::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuConstruct::MouseButtonDown(rMEvt);
+
+ if ( rMEvt.IsLeft() && !pView->IsAction() )
+ {
+ Point aPos( pWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
+ pWindow->CaptureMouse();
+
+ if ( pView->GetCurrentObjIdentifier() == SdrObjKind::Caption )
+ {
+ Size aCaptionSize ( 2268, 1134 ); // 4x2cm
+
+ bReturn = pView->BegCreateCaptionObj( aPos, aCaptionSize );
+
+ // How do you set the font for writing
+ }
+ else
+ bReturn = pView->BegCreateObj(aPos);
+
+ SdrObject* pObj = pView->GetCreateObj();
+
+ if (pObj)
+ {
+ SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool());
+ SetLineEnds(aAttr, *pObj, aSfxRequest.GetSlot());
+ pObj->SetMergedItemSet(aAttr);
+ }
+ }
+ return bReturn;
+}
+
+bool FuConstRectangle::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = false;
+
+ if ( pView->IsCreateObj() && rMEvt.IsLeft() )
+ {
+ pView->EndCreateObj(SdrCreateCmd::ForceEnd);
+
+ if (aSfxRequest.GetSlot() == SID_DRAW_CAPTION_VERTICAL)
+ {
+ // set vertical flag for caption object
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMark(0))
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ // create OutlinerParaObject now so it can be set to vertical
+ if ( auto pSdrTextObj = DynCastSdrTextObj( pObj) )
+ pSdrTextObj->ForceOutlinerParaObject();
+ OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject();
+ if( pOPO && !pOPO->IsEffectivelyVertical() )
+ pOPO->SetVertical( true );
+ }
+ }
+
+ bReturn = true;
+ }
+ return (FuConstruct::MouseButtonUp(rMEvt) || bReturn);
+}
+
+void FuConstRectangle::Activate()
+{
+ SdrObjKind aObjKind;
+
+ switch (aSfxRequest.GetSlot() )
+ {
+ case SID_DRAW_LINE:
+ case SID_DRAW_XLINE:
+ case SID_LINE_ARROW_END:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ case SID_LINE_ARROW_START:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ case SID_LINE_ARROWS:
+ aNewPointer = PointerStyle::DrawLine;
+ aObjKind = SdrObjKind::Line;
+ break;
+
+ case SID_DRAW_MEASURELINE:
+ aNewPointer = PointerStyle::DrawLine;
+ aObjKind = SdrObjKind::Measure;
+ break;
+
+ case SID_DRAW_RECT:
+ aNewPointer = PointerStyle::DrawRect;
+ aObjKind = SdrObjKind::Rectangle;
+ break;
+
+ case SID_DRAW_ELLIPSE:
+ aNewPointer = PointerStyle::DrawEllipse;
+ aObjKind = SdrObjKind::CircleOrEllipse;
+ break;
+
+ case SID_DRAW_CAPTION:
+ case SID_DRAW_CAPTION_VERTICAL:
+ aNewPointer = PointerStyle::DrawCaption;
+ aObjKind = SdrObjKind::Caption;
+ break;
+
+ default:
+ aNewPointer = PointerStyle::Cross;
+ aObjKind = SdrObjKind::Rectangle;
+ break;
+ }
+
+ pView->SetCurrentObj(aObjKind);
+
+ aOldPointer = pWindow->GetPointer();
+ rViewShell.SetActivePointer( aNewPointer );
+
+ FuConstruct::Activate();
+}
+
+void FuConstRectangle::SetLineEnds(SfxItemSet& rAttr, const SdrObject& rObj, sal_uInt16 nSlotId)
+{
+ ConstructHelper::SetLineEnds(rAttr, rObj, nSlotId, 200);
+}
+
+void FuConstRectangle::Deactivate()
+{
+ FuConstruct::Deactivate();
+ rViewShell.SetActivePointer( aOldPointer );
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuConstRectangle::CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle)
+{
+ rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
+ *pDrDoc,
+ pView->GetCurrentObjInventor(),
+ pView->GetCurrentObjIdentifier()));
+
+ if(pObj)
+ {
+ tools::Rectangle aRect(rRectangle);
+ Point aStart = aRect.TopLeft();
+ Point aEnd = aRect.BottomRight();
+
+ switch(nID)
+ {
+ case SID_DRAW_LINE:
+ case SID_DRAW_XLINE:
+ case SID_LINE_ARROW_END:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ case SID_LINE_ARROW_START:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ case SID_LINE_ARROWS:
+ {
+ if(auto pPathObj = dynamic_cast<SdrPathObj*>( pObj.get() ))
+ {
+ sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2);
+ basegfx::B2DPolygon aPoly;
+ aPoly.append(basegfx::B2DPoint(aStart.X(), nYMiddle));
+ aPoly.append(basegfx::B2DPoint(aEnd.X(), nYMiddle));
+ pPathObj->SetPathPoly(basegfx::B2DPolyPolygon(aPoly));
+ }
+ else
+ {
+ OSL_FAIL("Object is NO line object");
+ }
+
+ break;
+ }
+
+ case SID_DRAW_MEASURELINE:
+ {
+ if(auto pMeasureObj = dynamic_cast<SdrMeasureObj*>( pObj.get() ))
+ {
+ sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2);
+ pMeasureObj->SetPoint(Point(aStart.X(), nYMiddle), 0);
+ pMeasureObj->SetPoint(Point(aEnd.X(), nYMiddle), 1);
+ }
+
+ break;
+ }
+
+ case SID_DRAW_CAPTION:
+ case SID_DRAW_CAPTION_VERTICAL:
+ {
+ if(auto pCaptionObj = dynamic_cast<SdrCaptionObj*>( pObj.get() ))
+ {
+ bool bIsVertical(SID_DRAW_CAPTION_VERTICAL == nID);
+
+ pCaptionObj->SetVerticalWriting(bIsVertical);
+
+ if(bIsVertical)
+ {
+ SfxItemSet aSet(pObj->GetMergedItemSet());
+ aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER));
+ aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT));
+ pObj->SetMergedItemSet(aSet);
+ }
+
+ // don't set default text, start edit mode instead
+ // (Edit mode is started in ScTabViewShell::ExecDraw, because
+ // it must be handled by FuText)
+
+ pCaptionObj->SetLogicRect(aRect);
+ pCaptionObj->SetTailPos(
+ aRect.TopLeft() - Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2));
+ }
+ else
+ {
+ OSL_FAIL("Object is NO caption object");
+ }
+
+ break;
+ }
+
+ default:
+ {
+ pObj->SetLogicRect(aRect);
+
+ break;
+ }
+ }
+
+ SfxItemSet aAttr(pDrDoc->GetItemPool());
+ SetLineEnds(aAttr, *pObj, nID);
+ pObj->SetMergedItemSet(aAttr);
+ }
+
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuconstr.cxx b/sc/source/ui/drawfunc/fuconstr.cxx
new file mode 100644
index 0000000000..0ffb6e832d
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuconstr.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 <editeng/outlobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/dispatch.hxx>
+
+#include <fuconstr.hxx>
+#include <fudraw.hxx>
+#include <tabvwsh.hxx>
+#include <futext.hxx>
+#include <drawview.hxx>
+
+// maximal permitted mouse movement to start Drag&Drop
+//! fusel,fuconstr,futext - combine them!
+#define SC_MAXDRAGMOVE 3
+
+FuConstruct::FuConstruct(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuDraw(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+}
+
+FuConstruct::~FuConstruct()
+{
+}
+
+bool FuConstruct::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuDraw::MouseButtonDown(rMEvt);
+
+ if ( pView->IsAction() )
+ {
+ if ( rMEvt.IsRight() )
+ pView->BckAction();
+ return true;
+ }
+
+ aDragTimer.Start();
+
+ aMDPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() );
+
+ if ( rMEvt.IsLeft() )
+ {
+ pWindow->CaptureMouse();
+
+ SdrHdl* pHdl = pView->PickHandle(aMDPos);
+
+ if ( pHdl != nullptr || pView->IsMarkedHit(aMDPos) )
+ {
+ pView->BegDragObj(aMDPos, nullptr, pHdl, 1);
+ bReturn = true;
+ }
+ else if ( pView->AreObjectsMarked() )
+ {
+ pView->UnmarkAll();
+ bReturn = true;
+ }
+ }
+
+ bIsInDragMode = false;
+
+ return bReturn;
+}
+
+bool FuConstruct::MouseMove(const MouseEvent& rMEvt)
+{
+ FuDraw::MouseMove(rMEvt);
+
+ if (aDragTimer.IsActive() )
+ {
+ Point aOldPixel = pWindow->LogicToPixel( aMDPos );
+ Point aNewPixel = rMEvt.GetPosPixel();
+ if ( std::abs( aOldPixel.X() - aNewPixel.X() ) > SC_MAXDRAGMOVE ||
+ std::abs( aOldPixel.Y() - aNewPixel.Y() ) > SC_MAXDRAGMOVE )
+ aDragTimer.Stop();
+ }
+
+ Point aPix(rMEvt.GetPosPixel());
+ Point aPnt( pWindow->PixelToLogic(aPix) );
+
+ if ( pView->IsAction() )
+ {
+ ForceScroll(aPix);
+ pView->MovAction(aPnt);
+ }
+ else
+ {
+ SdrHdl* pHdl=pView->PickHandle(aPnt);
+
+ if ( pHdl != nullptr )
+ {
+ rViewShell.SetActivePointer(pHdl->GetPointer());
+ }
+ else if ( pView->IsMarkedHit(aPnt) )
+ {
+ rViewShell.SetActivePointer(PointerStyle::Move);
+ }
+ else
+ {
+ rViewShell.SetActivePointer( aNewPointer );
+ }
+ }
+ return true;
+}
+
+bool FuConstruct::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = SimpleMouseButtonUp( rMEvt );
+
+ // Double-click on text object? (->fusel)
+
+ sal_uInt16 nClicks = rMEvt.GetClicks();
+ if ( nClicks == 2 && rMEvt.IsLeft() )
+ {
+ if ( pView->AreObjectsMarked() )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ // if Uno-Controls no text mode
+ if ( DynCastSdrTextObj( pObj) != nullptr && dynamic_cast<const SdrUnoObj*>( pObj) == nullptr )
+ {
+ OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject();
+ bool bVertical = ( pOPO && pOPO->IsEffectivelyVertical() );
+ sal_uInt16 nTextSlotId = bVertical ? SID_DRAW_TEXT_VERTICAL : SID_DRAW_TEXT;
+
+ rViewShell.GetViewData().GetDispatcher().
+ Execute(nTextSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD);
+
+ // Get the created FuText now and change into EditMode
+ FuPoor* pPoor = rViewShell.GetViewData().GetView()->GetDrawFuncPtr();
+ if ( pPoor && pPoor->GetSlotID() == nTextSlotId ) // has no RTTI
+ {
+ FuText* pText = static_cast<FuText*>(pPoor);
+ Point aMousePixel = rMEvt.GetPosPixel();
+ pText->SetInEditMode( pObj, &aMousePixel );
+ }
+ bReturn = true;
+ }
+ }
+ }
+ }
+
+ FuDraw::MouseButtonUp(rMEvt);
+
+ return bReturn;
+}
+
+// SimpleMouseButtonUp - no test on double-click
+
+bool FuConstruct::SimpleMouseButtonUp(const MouseEvent& rMEvt)
+{
+ bool bReturn = true;
+
+ if (aDragTimer.IsActive() )
+ {
+ aDragTimer.Stop();
+ }
+
+ Point aPnt( pWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ if ( pView->IsDragObj() )
+ pView->EndDragObj( rMEvt.IsMod1() );
+
+ else if ( pView->IsMarkObj() )
+ pView->EndMarkObj();
+
+ else bReturn = false;
+
+ if ( !pView->IsAction() )
+ {
+ pWindow->ReleaseMouse();
+
+ if ( !pView->AreObjectsMarked() && rMEvt.GetClicks() < 2 )
+ {
+ pView->MarkObj(aPnt, -2, false, rMEvt.IsMod1());
+
+ SfxDispatcher& rDisp = rViewShell.GetViewData().GetDispatcher();
+ if ( pView->AreObjectsMarked() )
+ rDisp.Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ else
+ rDisp.Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ }
+ }
+
+ return bReturn;
+}
+
+// If we handle a KeyEvent, then the return value is sal_True else FALSE.
+bool FuConstruct::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bReturn = false;
+
+ switch ( rKEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_ESCAPE:
+ if ( pView->IsAction() )
+ {
+ pView->BrkAction();
+ pWindow->ReleaseMouse();
+ bReturn = true;
+ }
+ else // end drawing mode
+ {
+ rViewShell.GetViewData().GetDispatcher().
+ Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ }
+ break;
+
+ case KEY_DELETE:
+ pView->DeleteMarked();
+ bReturn = true;
+ break;
+ }
+
+ if ( !bReturn )
+ {
+ bReturn = FuDraw::KeyInput(rKEvt);
+ }
+
+ return bReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuconuno.cxx b/sc/source/ui/drawfunc/fuconuno.cxx
new file mode 100644
index 0000000000..e3c8f0b4d7
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuconuno.cxx
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fuconuno.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+
+#include <svx/svxids.hrc>
+
+FuConstUnoControl::FuConstUnoControl(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuConstruct(rViewSh, pWin, pViewP, pDoc, rReq)
+ , nInventor(SdrInventor::Unknown)
+ , nIdentifier(SdrObjKind::NONE)
+{
+ const SfxUInt32Item* pInventorItem = rReq.GetArg<SfxUInt32Item>(SID_FM_CONTROL_INVENTOR);
+ const SfxUInt16Item* pIdentifierItem = rReq.GetArg<SfxUInt16Item>(SID_FM_CONTROL_IDENTIFIER);
+ if( pInventorItem )
+ nInventor = static_cast<SdrInventor>(pInventorItem->GetValue());
+ if( pIdentifierItem )
+ nIdentifier = static_cast<SdrObjKind>(pIdentifierItem->GetValue());
+}
+
+FuConstUnoControl::~FuConstUnoControl()
+{
+}
+
+bool FuConstUnoControl::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuConstruct::MouseButtonDown(rMEvt);
+
+ if ( rMEvt.IsLeft() && !pView->IsAction() )
+ {
+ Point aPnt( pWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
+ pWindow->CaptureMouse();
+ pView->BegCreateObj(aPnt);
+ bReturn = true;
+ }
+ return bReturn;
+}
+
+bool FuConstUnoControl::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = false;
+
+ if ( pView->IsCreateObj() && rMEvt.IsLeft() )
+ {
+ pView->EndCreateObj(SdrCreateCmd::ForceEnd);
+ bReturn = true;
+ }
+ return (FuConstruct::MouseButtonUp(rMEvt) || bReturn);
+}
+
+void FuConstUnoControl::Activate()
+{
+ pView->SetCurrentObj( nIdentifier, nInventor );
+
+ aNewPointer = PointerStyle::DrawRect;
+ aOldPointer = pWindow->GetPointer();
+ rViewShell.SetActivePointer( aNewPointer );
+
+ SdrLayer* pLayer = pView->GetModel().GetLayerAdmin().GetLayerPerID(SC_LAYER_CONTROLS);
+ if (pLayer)
+ pView->SetActiveLayer( pLayer->GetName() );
+
+ FuConstruct::Activate();
+}
+
+void FuConstUnoControl::Deactivate()
+{
+ FuConstruct::Deactivate();
+
+ SdrLayer* pLayer = pView->GetModel().GetLayerAdmin().GetLayerPerID(SC_LAYER_FRONT);
+ if (pLayer)
+ pView->SetActiveLayer( pLayer->GetName() );
+
+ rViewShell.SetActivePointer( aOldPointer );
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuConstUnoControl::CreateDefaultObject(const sal_uInt16 /* nID */, const tools::Rectangle& rRectangle)
+{
+ // case SID_FM_CREATE_CONTROL:
+
+ rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
+ *pDrDoc,
+ pView->GetCurrentObjInventor(),
+ pView->GetCurrentObjIdentifier()));
+
+ if(pObj)
+ {
+ pObj->SetLogicRect(rRectangle);
+ // tdf#140252 Controls are always on layer "controls"
+ pObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ }
+
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fudraw.cxx b/sc/source/ui/drawfunc/fudraw.cxx
new file mode 100644
index 0000000000..9076a75ada
--- /dev/null
+++ b/sc/source/ui/drawfunc/fudraw.cxx
@@ -0,0 +1,766 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/editeng.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <sc.hrc>
+#include <fudraw.hxx>
+#include <futext.hxx>
+#include <tabvwsh.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <comphelper/lok.hxx>
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+
+namespace
+{
+
+void collectUIInformation( const OUString& aevent )
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{ aevent , ""}};
+ aDescription.aAction = "COMMENT";
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+// base class for draw module specific functions
+FuDraw::FuDraw(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuPoor(rViewSh, pWin, pViewP, pDoc, rReq)
+ , aNewPointer(PointerStyle::Arrow)
+ , aOldPointer(PointerStyle::Arrow)
+{
+}
+
+FuDraw::~FuDraw()
+{
+}
+
+void FuDraw::DoModifiers(const MouseEvent& rMEvt)
+{
+ // Shift = Ortho and AngleSnap
+ // Control = Snap (Toggle)
+ // Alt = centric
+
+ bool bShift = rMEvt.IsShift();
+ bool bAlt = rMEvt.IsMod2();
+
+ bool bOrtho = bShift;
+ bool bAngleSnap = bShift;
+ bool bCenter = bAlt;
+
+ // #i33136#
+ if(doConstructOrthogonal())
+ {
+ bOrtho = !bShift;
+ }
+
+ if (pView->IsOrtho() != bOrtho)
+ pView->SetOrtho(bOrtho);
+ if (pView->IsAngleSnapEnabled() != bAngleSnap)
+ pView->SetAngleSnapEnabled(bAngleSnap);
+
+ if (pView->IsCreate1stPointAsCenter() != bCenter)
+ pView->SetCreate1stPointAsCenter(bCenter);
+ if (pView->IsResizeAtCenter() != bCenter)
+ pView->SetResizeAtCenter(bCenter);
+
+}
+
+void FuDraw::ResetModifiers()
+{
+ if (!pView)
+ return;
+
+ ScViewData& rViewData = rViewShell.GetViewData();
+ const ScViewOptions& rOpt = rViewData.GetOptions();
+ const ScGridOptions& rGrid = rOpt.GetGridOptions();
+ bool bGridOpt = rGrid.GetUseGridSnap();
+
+ if (pView->IsOrtho())
+ pView->SetOrtho(false);
+ if (pView->IsAngleSnapEnabled())
+ pView->SetAngleSnapEnabled(false);
+
+ if (pView->IsGridSnap() != bGridOpt)
+ pView->SetGridSnap(bGridOpt);
+ if (pView->IsSnapEnabled() != bGridOpt)
+ pView->SetSnapEnabled(bGridOpt);
+
+ if (pView->IsCreate1stPointAsCenter())
+ pView->SetCreate1stPointAsCenter(false);
+ if (pView->IsResizeAtCenter())
+ pView->SetResizeAtCenter(false);
+}
+
+bool FuDraw::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ DoModifiers( rMEvt );
+ return false;
+}
+
+bool FuDraw::MouseMove(const MouseEvent& rMEvt)
+{
+ // evaluate modifiers only if in a drawing layer action
+ // (don't interfere with keyboard shortcut handling)
+ if (pView->IsAction())
+ DoModifiers( rMEvt );
+
+ return false;
+}
+
+bool FuDraw::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ ResetModifiers();
+ return false;
+}
+
+// Process Keyboard events. Return true if an event is being handled
+static bool lcl_KeyEditMode( SdrObject* pObj, ScTabViewShell& rViewShell, const KeyEvent* pInitialKey )
+{
+ bool bReturn = false;
+ if ( DynCastSdrTextObj( pObj) != nullptr && dynamic_cast<const SdrUnoObj*>( pObj) == nullptr )
+ {
+ // start text edit - like FuSelection::MouseButtonUp,
+ // but with bCursorToEnd instead of mouse position
+
+ OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject();
+ bool bVertical = ( pOPO && pOPO->IsEffectivelyVertical() );
+ sal_uInt16 nTextSlotId = bVertical ? SID_DRAW_TEXT_VERTICAL : SID_DRAW_TEXT;
+
+ // don't switch shells if text shell is already active
+ FuPoor* pPoor = rViewShell.GetViewData().GetView()->GetDrawFuncPtr();
+ if ( !pPoor || pPoor->GetSlotID() != nTextSlotId )
+ {
+ rViewShell.GetViewData().GetDispatcher().
+ Execute(nTextSlotId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD);
+ }
+
+ // get the resulting FuText and set in edit mode
+ pPoor = rViewShell.GetViewData().GetView()->GetDrawFuncPtr();
+ if ( pPoor && pPoor->GetSlotID() == nTextSlotId ) // no RTTI
+ {
+ FuText* pText = static_cast<FuText*>(pPoor);
+ pText->SetInEditMode( pObj, nullptr, true, pInitialKey );
+ //! set cursor to end of text
+ }
+ bReturn = true;
+ }
+ return bReturn;
+}
+
+bool FuDraw::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bReturn = false;
+ ScViewData& rViewData = rViewShell.GetViewData();
+
+ switch ( rKEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_ESCAPE:
+ if ( rViewShell.IsDrawTextShell() || aSfxRequest.GetSlot() == SID_DRAW_NOTEEDIT )
+ {
+ collectUIInformation("CLOSE");
+ // if object selected -> normal draw-shell, else turn off drawing
+ rViewData.GetDispatcher().Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ bReturn = true;
+ }
+ else if ( rViewShell.IsDrawSelMode() )
+ {
+ pView->UnmarkAll();
+ rViewData.GetDispatcher().Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ bReturn = true;
+ }
+ else if ( pView->AreObjectsMarked() )
+ {
+ // III
+ SdrHdlList& rHdlList = const_cast< SdrHdlList& >( pView->GetHdlList() );
+ if( rHdlList.GetFocusHdl() )
+ rHdlList.ResetFocusHdl();
+ else
+ pView->UnmarkAll();
+
+ // while bezier editing, object is selected
+ if (!pView->AreObjectsMarked())
+ rViewShell.SetDrawShell( false );
+
+ bReturn = true;
+ }
+ break;
+
+ case KEY_DELETE: //! via accelerator
+ pView->DeleteMarked();
+ bReturn = true;
+ break;
+
+ case KEY_RETURN:
+ {
+ if( rKEvt.GetKeyCode().GetModifier() == 0 )
+ {
+ // activate OLE object on RETURN for selected object
+ // put selected text object in edit mode
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( !pView->IsTextEdit() && 1 == rMarkList.GetMarkCount() )
+ {
+ bool bOle = rViewShell.GetViewFrame().GetFrame().IsInPlace();
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ auto pOleObj = dynamic_cast<SdrOle2Obj*>(pObj);
+ if( pOleObj && !bOle )
+ {
+ rViewShell.ActivateObject(pOleObj, css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY);
+
+ // consumed
+ bReturn = true;
+ }
+ else if ( lcl_KeyEditMode( pObj, rViewShell, nullptr ) ) // start text edit for suitable object
+ bReturn = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_F2:
+ {
+ if( rKEvt.GetKeyCode().GetModifier() == 0 )
+ {
+ // put selected text object in edit mode
+ // (this is not SID_SETINPUTMODE, but F2 hardcoded, like in Writer)
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( !pView->IsTextEdit() && 1 == rMarkList.GetMarkCount() )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ bool isMobilePhone = comphelper::LibreOfficeKit::isActive() && rViewShell.isLOKMobilePhone();
+ // Double tapping on charts on phone may result in activating the edit mode which is not wanted.
+ // It happens due to the delay of selection message of the object from kit to javascript
+ // in that case F2 is sent instead of double click
+ if (isMobilePhone && ScDocument::IsChart(pObj))
+ {
+ rViewShell.ActivateObject(static_cast<SdrOle2Obj*>(pObj), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY);
+ break;
+ }
+ if ( lcl_KeyEditMode( pObj, rViewShell, nullptr ) ) // start text edit for suitable object
+ bReturn = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_TAB:
+ {
+ // in calc do NOT start draw object selection using TAB/SHIFT-TAB when
+ // there is not yet an object selected
+ if(pView->AreObjectsMarked())
+ {
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+
+ if ( !aCode.IsMod1() && !aCode.IsMod2() )
+ {
+ // changeover to the next object
+ if(!pView->MarkNextObj( !aCode.IsShift() ))
+ {
+ //If there is only one object, don't do the UnmarkAllObj() & MarkNextObj().
+ if ( pView->HasMultipleMarkableObjects() && pView->HasMarkableObj() )
+ {
+ // No next object: go over open end and
+ // get first from the other side
+ pView->UnmarkAllObj();
+ pView->MarkNextObj(!aCode.IsShift());
+ }
+ }
+
+ // II
+ if(pView->AreObjectsMarked())
+ pView->MakeVisible(pView->GetAllMarkedRect(), *pWindow);
+
+ bReturn = true;
+ }
+
+ // handle Mod1 and Mod2 to get travelling running on different systems
+ if(rKEvt.GetKeyCode().IsMod1() || rKEvt.GetKeyCode().IsMod2())
+ {
+ // II do something with a selected handle?
+ const SdrHdlList& rHdlList = pView->GetHdlList();
+ bool bForward(!rKEvt.GetKeyCode().IsShift());
+
+ const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward);
+
+ // guarantee visibility of focused handle
+ SdrHdl* pHdl = rHdlList.GetFocusHdl();
+
+ if(pHdl)
+ {
+ Point aHdlPosition(pHdl->GetPos());
+ tools::Rectangle aVisRect(aHdlPosition - Point(100, 100), Size(200, 200));
+ pView->MakeVisible(aVisRect, *pWindow);
+ }
+
+ // consumed
+ bReturn = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_END:
+ {
+ // in calc do NOT select the last draw object when
+ // there is not yet an object selected
+ if(pView->AreObjectsMarked())
+ {
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+
+ if ( aCode.IsMod1() )
+ {
+ // mark last object
+ pView->UnmarkAllObj();
+ pView->MarkNextObj();
+
+ // II
+ if(pView->AreObjectsMarked())
+ pView->MakeVisible(pView->GetAllMarkedRect(), *pWindow);
+
+ bReturn = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_HOME:
+ {
+ // in calc do NOT select the first draw object when
+ // there is not yet an object selected
+ if(pView->AreObjectsMarked())
+ {
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+
+ if ( aCode.IsMod1() )
+ {
+ // mark first object
+ pView->UnmarkAllObj();
+ pView->MarkNextObj(true);
+
+ // II
+ if(pView->AreObjectsMarked())
+ pView->MakeVisible(pView->GetAllMarkedRect(), *pWindow);
+
+ bReturn = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ {
+ // in calc do cursor travelling of draw objects only when
+ // there is an object selected yet
+ if(pView->AreObjectsMarked())
+ {
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if(rMarkList.GetMarkCount() == 1)
+ {
+ // disable cursor travelling on note objects as the tail connector position
+ // must not move.
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( ScDrawLayer::IsNoteCaption( pObj ) )
+ break;
+ }
+
+ tools::Long nX = 0;
+ tools::Long nY = 0;
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+
+ if (nCode == KEY_UP)
+ {
+ // scroll up
+ nX = 0;
+ nY =-1;
+ }
+ else if (nCode == KEY_DOWN)
+ {
+ // scroll down
+ nX = 0;
+ nY = 1;
+ }
+ else if (nCode == KEY_LEFT)
+ {
+ // scroll left
+ nX =-1;
+ nY = 0;
+ }
+ else if (nCode == KEY_RIGHT)
+ {
+ // scroll right
+ nX = 1;
+ nY = 0;
+ }
+
+ bool bReadOnly = rViewData.GetDocShell()->IsReadOnly();
+
+ if(!rKEvt.GetKeyCode().IsMod1() && !bReadOnly)
+ {
+ if(rKEvt.GetKeyCode().IsMod2())
+ {
+ // move in 1 pixel distance
+ Size aLogicSizeOnePixel = pWindow ? pWindow->PixelToLogic(Size(1,1)) : Size(100, 100);
+ nX *= aLogicSizeOnePixel.Width();
+ nY *= aLogicSizeOnePixel.Height();
+ }
+ else if(rKEvt.GetKeyCode().IsShift()) // #i121236# Support for shift key in calc
+ {
+ nX *= 1000;
+ nY *= 1000;
+ }
+ else
+ {
+ // old, fixed move distance
+ nX *= 100;
+ nY *= 100;
+ }
+
+ // is there a movement to do?
+ if(0 != nX || 0 != nY)
+ {
+ // II
+ const SdrHdlList& rHdlList = pView->GetHdlList();
+ SdrHdl* pHdl = rHdlList.GetFocusHdl();
+
+ if(nullptr == pHdl)
+ {
+ // only take action when move is allowed
+ if(pView->IsMoveAllowed())
+ {
+ // 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();
+ }
+ }
+ }
+
+ // now move the selected draw objects
+ pView->MoveAllMarked(Size(nX, nY));
+
+ // II
+ pView->MakeVisible(pView->GetAllMarkedRect(), *pWindow);
+
+ bReturn = true;
+ }
+ }
+ 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);
+ }
+
+ // make moved handle visible
+ tools::Rectangle aVisRect(aEndPoint - Point(100, 100), Size(200, 200));
+ pView->MakeVisible(aVisRect, *pWindow);
+
+ bReturn = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case KEY_SPACE:
+ {
+ // in calc do only something when draw objects are selected
+ if(pView->AreObjectsMarked())
+ {
+ 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);
+ }
+ }
+
+ bReturn = true;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ if (!bReturn)
+ {
+ bReturn = FuPoor::KeyInput(rKEvt);
+ }
+
+ if (!bReturn)
+ {
+ // allow direct typing into a selected text object
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( !pView->IsTextEdit() && 1 == rMarkList.GetMarkCount() && EditEngine::IsSimpleCharInput(rKEvt) )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ // start text edit for suitable object, pass key event to OutlinerView
+ if ( lcl_KeyEditMode( pObj, rViewShell, &rKEvt ) )
+ bReturn = true;
+ }
+ }
+
+ return bReturn;
+}
+
+// toggle mouse-pointer
+static bool lcl_UrlHit( const SdrView* pView, const Point& rPosPixel, const vcl::Window* pWindow )
+{
+ SdrViewEvent aVEvt;
+ MouseEvent aMEvt( rPosPixel, 1, MouseEventModifiers::NONE, MOUSE_LEFT );
+ SdrHitKind eHit = pView->PickAnything( aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt );
+
+ if (eHit != SdrHitKind::NONE && aVEvt.mpObj != nullptr)
+ {
+ if ( SvxIMapInfo::GetIMapInfo(aVEvt.mpObj) && SvxIMapInfo::GetHitIMapObject(
+ aVEvt.mpObj, pWindow->PixelToLogic(rPosPixel), pWindow->GetOutDev() ) )
+ return true;
+
+ if (aVEvt.meEvent == SdrEventKind::ExecuteUrl)
+ return true;
+ }
+
+ return false;
+}
+
+void FuDraw::ForcePointer(const MouseEvent* pMEvt)
+{
+ if ( pView->IsAction() )
+ return;
+
+ Point aPosPixel = pWindow->GetPointerPosPixel();
+ bool bAlt = pMEvt && pMEvt->IsMod2();
+ Point aPnt = pWindow->PixelToLogic( aPosPixel );
+ SdrHdl* pHdl = pView->PickHandle(aPnt);
+ SdrPageView* pPV;
+ SdrObject* pMacroPickObj;
+
+ ScMacroInfo* pInfo = nullptr;
+ SdrObject* pObj = pView->PickObj(aPnt, pView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER);
+ if (pObj)
+ {
+ if ( pObj->IsGroupObject() )
+ {
+ SdrObject* pHit = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::DEEP);
+ if (pHit)
+ pObj = pHit;
+ }
+ pInfo = ScDrawLayer::GetMacroInfo( pObj );
+ }
+
+ if ( pView->IsTextEdit() )
+ {
+ rViewShell.SetActivePointer(PointerStyle::Text); // can't be ?
+ }
+ else if ( pHdl )
+ {
+ rViewShell.SetActivePointer(
+ pView->GetPreferredPointer( aPnt, pWindow->GetOutDev() ) );
+ }
+ else if ( pView->IsMarkedHit(aPnt) )
+ {
+ rViewShell.SetActivePointer( PointerStyle::Move );
+ }
+ else if ( !bAlt && ( !pMEvt || !pMEvt->GetButtons() )
+ && lcl_UrlHit( pView, aPosPixel, pWindow ) )
+ {
+ // could be suppressed with ALT
+ pWindow->SetPointer( PointerStyle::RefHand ); // Text-URL / ImageMap
+ }
+ else if ( !bAlt && (pMacroPickObj = pView->PickObj(aPnt, pView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO)) )
+ {
+ // could be suppressed with ALT
+ SdrObjMacroHitRec aHitRec; //! something missing ????
+ rViewShell.SetActivePointer(pMacroPickObj->GetMacroPointer(aHitRec));
+ }
+ else if ( !bAlt && pInfo && (!pInfo->GetMacro().isEmpty() || !pObj->getHyperlink().isEmpty()) )
+ pWindow->SetPointer( PointerStyle::RefHand );
+ else if ( IsDetectiveHit( aPnt ) )
+ rViewShell.SetActivePointer( PointerStyle::Detective );
+ else
+ {
+ const bool bIsThemed = rViewShell.GetViewData().IsThemedCursor();
+ rViewShell.SetActivePointer( bIsThemed ? PointerStyle::FatCross : PointerStyle::Arrow ); //! in Gridwin?
+ }
+}
+
+bool FuDraw::IsEditingANote() const
+{
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ const size_t backval=rMarkList.GetMarkCount();
+ for (size_t nlv1=0; nlv1<backval; ++nlv1)
+ {
+ SdrObject* pObj = rMarkList.GetMark( nlv1 )->GetMarkedSdrObj();
+ if ( ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FuDraw::IsSizingOrMovingNote( const MouseEvent& rMEvt ) const
+{
+ bool bIsSizingOrMoving = false;
+ if ( rMEvt.IsLeft() )
+ {
+ const SdrMarkList& rNoteMarkList = pView->GetMarkedObjectList();
+ if(rNoteMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rNoteMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if ( ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ Point aMPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() );
+ bIsSizingOrMoving =
+ pView->PickHandle( aMPos ) || // handles to resize the note
+ pView->IsTextEditFrameHit( aMPos ); // frame for moving the note
+ }
+ }
+ }
+ return bIsSizingOrMoving;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuins1.cxx b/sc/source/ui/drawfunc/fuins1.cxx
new file mode 100644
index 0000000000..72886789b4
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuins1.cxx
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <officecfg/Office/Common.hxx>
+#include <editeng/sizeitem.hxx>
+#include <sal/log.hxx>
+#include <sfx2/opengrf.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/linkwarn.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/graphicfilter.hxx>
+#include <svl/stritem.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/GraphicNativeTransform.hxx>
+#include <vcl/GraphicNativeMetadata.hxx>
+#include <fuinsert.hxx>
+#include <tabvwsh.hxx>
+#include <drwlayer.hxx>
+#include <drawview.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <globstr.hrc>
+#include <comphelper/lok.hxx>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/media/XPlayer.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/ListboxControlActions.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+void ScLimitSizeOnDrawPage( Size& rSize, Point& rPos, const Size& rPage )
+{
+ if ( !rPage.Width() || !rPage.Height() )
+ return;
+
+ Size aPageSize = rPage;
+ bool bNegative = aPageSize.Width() < 0;
+ if ( bNegative )
+ {
+ // make everything positive temporarily
+ aPageSize.setWidth( -aPageSize.Width() );
+ rPos.setX( -rPos.X() - rSize.Width() );
+ }
+
+ if ( rSize.Width() > aPageSize.Width() || rSize.Height() > aPageSize.Height() )
+ {
+ double fX = aPageSize.Width() / static_cast<double>(rSize.Width());
+ double fY = aPageSize.Height() / static_cast<double>(rSize.Height());
+
+ if ( fX < fY )
+ {
+ rSize.setWidth( aPageSize.Width() );
+ rSize.setHeight( static_cast<tools::Long>( rSize.Height() * fX ) );
+ }
+ else
+ {
+ rSize.setHeight( aPageSize.Height() );
+ rSize.setWidth( static_cast<tools::Long>( rSize.Width() * fY ) );
+ }
+
+ if (!rSize.Width())
+ rSize.setWidth( 1 );
+ if (!rSize.Height())
+ rSize.setHeight( 1 );
+ }
+
+ if ( rPos.X() + rSize.Width() > aPageSize.Width() )
+ rPos.setX( aPageSize.Width() - rSize.Width() );
+ if ( rPos.Y() + rSize.Height() > aPageSize.Height() )
+ rPos.setY( aPageSize.Height() - rSize.Height() );
+
+ if ( bNegative )
+ rPos.setX( -rPos.X() - rSize.Width() ); // back to real position
+}
+
+static void lcl_InsertGraphic( const Graphic& rGraphic,
+ const OUString& rFileName, bool bAsLink, bool bApi,
+ ScTabViewShell& rViewSh, const vcl::Window* pWindow, SdrView* pView,
+ ScAnchorType aAnchorType = SCA_CELL )
+{
+ Graphic& rGraphic1 = const_cast<Graphic &>(rGraphic);
+ GraphicNativeMetadata aMetadata;
+ if ( aMetadata.read(rGraphic1) )
+ {
+ const Degree10 aRotation = aMetadata.getRotation();
+ if (aRotation)
+ {
+ GraphicNativeTransform aTransform( rGraphic1 );
+ aTransform.rotate( aRotation );
+ }
+ }
+ ScDrawView* pDrawView = rViewSh.GetScDrawView();
+
+ // #i123922# check if an existing object is selected; if yes, evtl. replace
+ // the graphic for a SdrGraphObj (including link state updates) or adapt the fill
+ // style for other objects
+ if(pDrawView && 1 == pDrawView->GetMarkedObjectCount())
+ {
+ SdrObject* pPickObj = pDrawView->GetMarkedObjectByIndex(0);
+
+ if(pPickObj)
+ {
+ //sal_Int8 nAction(DND_ACTION_MOVE);
+ //Point aPos;
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ SdrObject* pResult = pDrawView->ApplyGraphicToObject(
+ *pPickObj,
+ rGraphic1,
+ aBeginUndo,
+ bAsLink ? rFileName : OUString());
+
+ if(pResult)
+ {
+ // we are done; mark the modified/new object
+ pDrawView->MarkObj(pResult, pDrawView->GetSdrPageView());
+ return;
+ }
+ }
+ }
+
+ // set the size so the graphic has its original pixel size
+ // at 100% view scale (as in SetMarkedOriginalSize),
+ // instead of respecting the current view scale
+ MapMode aSourceMap = rGraphic.GetPrefMapMode();
+ MapMode aDestMap( MapUnit::Map100thMM );
+ if ( aSourceMap.GetMapUnit() == MapUnit::MapPixel && pDrawView )
+ {
+ Fraction aScaleX, aScaleY;
+ pDrawView->CalcNormScale( aScaleX, aScaleY );
+ aDestMap.SetScaleX(aScaleX);
+ aDestMap.SetScaleY(aScaleY);
+ }
+ Size aLogicSize = pWindow->LogicToLogic(
+ rGraphic.GetPrefSize(), &aSourceMap, &aDestMap );
+
+ // Limit size
+
+ SdrPageView* pPV = pView->GetSdrPageView();
+ SdrPage* pPage = pPV->GetPage();
+ Point aInsertPos = rViewSh.GetInsertPos();
+
+ ScViewData& rData = rViewSh.GetViewData();
+ if ( rData.GetDocument().IsNegativePage( rData.GetTabNo() ) )
+ aInsertPos.AdjustX( -(aLogicSize.Width()) ); // move position to left edge
+
+ ScLimitSizeOnDrawPage( aLogicSize, aInsertPos, pPage->GetSize() );
+
+ tools::Rectangle aRect ( aInsertPos, aLogicSize );
+
+ rtl::Reference<SdrGrafObj> pObj = new SdrGrafObj(
+ pView->getSdrModelFromSdrView(), // TTTT pView should be reference
+ rGraphic1,
+ aRect);
+
+ // calling SetGraphicLink here doesn't work
+ // Yes, due to the SdrObject had no SdrModel
+ // Path is no longer used as name for the graphics object
+
+ ScDrawLayer* pLayer = static_cast<ScDrawLayer*>(&pView->GetModel());
+ OUString aName = pLayer->GetNewGraphicName(); // "Graphics"
+ pObj->SetName(aName);
+
+ if (aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE)
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rData.GetDocument(), rData.GetTabNo(),
+ aAnchorType == SCA_CELL_RESIZE);
+
+ // don't select if from (dispatch) API, to allow subsequent cell operations
+ SdrInsertFlags nInsOptions = (bApi && !comphelper::LibreOfficeKit::isActive()) ? SdrInsertFlags::DONTMARK : SdrInsertFlags::NONE;
+ bool bSuccess = pView->InsertObjectAtView( pObj.get(), *pPV, nInsOptions );
+
+ // SetGraphicLink has to be used after inserting the object,
+ // otherwise an empty graphic is swapped in and the contact stuff crashes.
+ // See #i37444#.
+ if (bSuccess && bAsLink)
+ pObj->SetGraphicLink( rFileName );
+}
+
+#if HAVE_FEATURE_AVMEDIA
+
+static void lcl_InsertMedia( const OUString& rMediaURL, bool bApi,
+ ScTabViewShell* pViewSh, const vcl::Window* pWindow, SdrView* pView,
+ const Size& rPrefSize, bool const bLink )
+{
+ SdrPageView* pPV = pView->GetSdrPageView();
+ SdrPage* pPage = pPV->GetPage();
+ ScViewData& rData = pViewSh->GetViewData();
+ Point aInsertPos( pViewSh->GetInsertPos() );
+ Size aSize;
+
+ if( rPrefSize.Width() && rPrefSize.Height() )
+ {
+ if( pWindow )
+ aSize = pWindow->PixelToLogic(rPrefSize, MapMode(MapUnit::Map100thMM));
+ else
+ aSize = Application::GetDefaultDevice()->PixelToLogic(rPrefSize, MapMode(MapUnit::Map100thMM));
+ }
+ else
+ aSize = Size( 5000, 5000 );
+
+ ScLimitSizeOnDrawPage( aSize, aInsertPos, pPage->GetSize() );
+
+ if( rData.GetDocument().IsNegativePage( rData.GetTabNo() ) )
+ aInsertPos.AdjustX( -(aSize.Width()) );
+
+ OUString realURL;
+ if (bLink)
+ {
+ realURL = rMediaURL;
+ }
+ else
+ {
+ uno::Reference<frame::XModel> const xModel(
+ rData.GetDocument().GetDocumentShell()->GetModel());
+ bool const bRet = ::avmedia::EmbedMedia(xModel, rMediaURL, realURL);
+ if (!bRet) { return; }
+ }
+
+ rtl::Reference<SdrMediaObj> pObj = new SdrMediaObj(
+ *rData.GetDocument().GetDrawLayer(),
+ tools::Rectangle(aInsertPos, aSize));
+
+ pObj->setURL( realURL, ""/*TODO?*/ );
+ pView->InsertObjectAtView( pObj.get(), *pPV, bApi ? SdrInsertFlags::DONTMARK : SdrInsertFlags::NONE );
+}
+#endif
+
+FuInsertGraphic::FuInsertGraphic( ScTabViewShell& rViewSh,
+ vcl::Window* pWin,
+ ScDrawView* pViewP,
+ SdrModel* pDoc,
+ SfxRequest& rReq )
+ : FuPoor(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ const SfxStringItem* pGraphicItem;
+ if ( pReqArgs &&
+ (pGraphicItem = pReqArgs->GetItemIfSet( SID_INSERT_GRAPHIC, true )) )
+ {
+ OUString aFileName = pGraphicItem->GetValue();
+
+ OUString aFilterName;
+ if ( const SfxStringItem* pFilterItem = pReqArgs->GetItemIfSet( FN_PARAM_FILTER ) )
+ aFilterName = pFilterItem->GetValue();
+
+ bool bAsLink = false;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET )
+ bAsLink = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ Graphic aGraphic;
+ ErrCode nError = GraphicFilter::LoadGraphic( aFileName, aFilterName, aGraphic, &GraphicFilter::GetGraphicFilter() );
+ if ( nError == ERRCODE_NONE )
+ {
+ lcl_InsertGraphic( aGraphic, aFileName, bAsLink, true, rViewSh, pWindow, pView );
+ }
+ }
+ else
+ {
+ SvxOpenGraphicDialog aDlg(ScResId(STR_INSERTGRAPHIC), pWin ? pWin->GetFrameWeld() : nullptr,
+ ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR);
+
+ Reference<ui::dialogs::XFilePickerControlAccess> xCtrlAcc = aDlg.GetFilePickerControlAccess();
+ sal_Int16 nSelect = 0;
+ Sequence<OUString> aListBoxEntries {
+ ScResId(STR_ANCHOR_TO_CELL),
+ ScResId(STR_ANCHOR_TO_CELL_RESIZE),
+ ScResId(STR_ANCHOR_TO_PAGE)
+ };
+ try
+ {
+ Any aTemplates(&aListBoxEntries, cppu::UnoType<decltype(aListBoxEntries)>::get());
+
+ xCtrlAcc->setValue(ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR,
+ ui::dialogs::ListboxControlActions::ADD_ITEMS, aTemplates);
+
+ Any aSelectPos(&nSelect, cppu::UnoType<decltype(nSelect)>::get());
+ xCtrlAcc->setValue(ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR,
+ ui::dialogs::ListboxControlActions::SET_SELECT_ITEM, aSelectPos);
+ }
+ catch (const Exception&)
+ {
+ SAL_WARN("sc", "control access failed");
+ }
+
+ if( aDlg.Execute() == ERRCODE_NONE )
+ {
+ Graphic aGraphic;
+ ErrCode nError = aDlg.GetGraphic(aGraphic);
+ if( nError == ERRCODE_NONE )
+ {
+ OUString aFileName = aDlg.GetPath();
+ const OUString& aFilterName = aDlg.GetDetectedFilter();
+ bool bAsLink = aDlg.IsAsLink();
+
+ // really store as link only?
+ if( bAsLink && officecfg::Office::Common::Misc::ShowLinkWarningDialog::get() )
+ {
+ SvxLinkWarningDialog aWarnDlg(pWin ? pWin->GetFrameWeld() : nullptr, aFileName);
+ if (aWarnDlg.run() != RET_OK)
+ bAsLink = false; // don't store as link
+ }
+
+ // Anchor to cell or to page?
+ Any aAnchorValue = xCtrlAcc->getValue(
+ ui::dialogs::ExtendedFilePickerElementIds::LISTBOX_IMAGE_ANCHOR,
+ ui::dialogs::ListboxControlActions::GET_SELECTED_ITEM );
+ OUString sAnchor;
+ aAnchorValue >>= sAnchor;
+
+ ScAnchorType aAnchorType;
+ if (sAnchor == ScResId(STR_ANCHOR_TO_CELL))
+ aAnchorType = SCA_CELL;
+ else if (sAnchor == ScResId(STR_ANCHOR_TO_CELL_RESIZE))
+ aAnchorType = SCA_CELL_RESIZE;
+ else if (sAnchor == ScResId(STR_ANCHOR_TO_PAGE))
+ aAnchorType = SCA_PAGE;
+ else
+ aAnchorType = SCA_DONTKNOW;
+
+ lcl_InsertGraphic( aGraphic, aFileName, bAsLink, false, rViewSh, pWindow, pView, aAnchorType );
+
+ // append items for recording
+ rReq.AppendItem( SfxStringItem( SID_INSERT_GRAPHIC, aFileName ) );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_FILTER, aFilterName ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bAsLink ) );
+ rReq.Done();
+ }
+ else
+ {
+ // error is handled in SvxOpenGraphicDialog::GetGraphic
+ }
+ }
+ }
+}
+
+FuInsertGraphic::~FuInsertGraphic()
+{
+}
+
+FuInsertMedia::FuInsertMedia( ScTabViewShell& rViewSh,
+ vcl::Window* pWin,
+ ScDrawView* pViewP,
+ SdrModel* pDoc,
+ const SfxRequest& rReq ) :
+ FuPoor(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+#if HAVE_FEATURE_AVMEDIA
+ OUString aURL;
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ bool bAPI = false;
+
+ const SvxSizeItem* pSizeItem = rReq.GetArg<SvxSizeItem>(FN_PARAM_1);
+ const SfxBoolItem* pLinkItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_2);
+ const bool bSizeUnknown = !pSizeItem;
+ Size aPrefSize;
+
+ if( pReqArgs )
+ {
+ const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>( &pReqArgs->Get( rReq.GetSlot() ) );
+
+ if( pStringItem )
+ {
+ aURL = pStringItem->GetValue();
+ bAPI = aURL.getLength();
+ }
+ }
+
+ bool bLink(pLinkItem ? pLinkItem->GetValue() : true);
+ bool bInsertMedia = bAPI;
+ if (!bInsertMedia)
+ bInsertMedia = ::avmedia::MediaWindow::executeMediaURLDialog(pWin ? pWin->GetFrameWeld() : nullptr, aURL, &bLink);
+ if (!bInsertMedia)
+ return;
+
+ if (!bSizeUnknown)
+ {
+ aPrefSize = pSizeItem->GetSize();
+ }
+ else
+ {
+ if( pWin )
+ pWin->EnterWait();
+
+ css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider(rViewShell.GetViewFrame().GetFrame().GetFrameInterface(), css::uno::UNO_QUERY);
+
+ rtl::Reference<avmedia::PlayerListener> xPlayerListener(new avmedia::PlayerListener(
+ [xDispatchProvider, aURL, bLink](const css::uno::Reference<css::media::XPlayer>& rPlayer){
+ css::awt::Size aSize = rPlayer->getPreferredPlayerWindowSize();
+ avmedia::MediaWindow::dispatchInsertAVMedia(xDispatchProvider, aSize, aURL, bLink);
+ }));
+
+ const bool bIsMediaURL = ::avmedia::MediaWindow::isMediaURL(aURL, ""/*TODO?*/, true, xPlayerListener);
+
+ if( pWin )
+ pWin->LeaveWait();
+
+ if (!bIsMediaURL && !bAPI)
+ ::avmedia::MediaWindow::executeFormatErrorBox(pWindow ? pWindow->GetFrameWeld() : nullptr);
+
+ return;
+ }
+
+ if (pWin)
+ pWin->EnterWait();
+
+ lcl_InsertMedia(aURL, bAPI, &rViewSh, pWindow, pView, aPrefSize, bLink);
+
+ if (pWin)
+ pWin->LeaveWait();
+#endif
+}
+
+FuInsertMedia::~FuInsertMedia()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fuins2.cxx b/sc/source/ui/drawfunc/fuins2.cxx
new file mode 100644
index 0000000000..959424b40b
--- /dev/null
+++ b/sc/source/ui/drawfunc/fuins2.cxx
@@ -0,0 +1,703 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <sot/exchange.hxx>
+#include <svl/globalnameitem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/stritem.hxx>
+#include <svx/svdoole2.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svtools/insdlg.hxx>
+#include <svtools/embedhlp.hxx>
+#include <svtools/strings.hrc>
+#include <svtools/svtresid.hxx>
+#include <svx/svxdlg.hxx>
+#include <comphelper/classids.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdundo.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/msg.hxx>
+#include <scmod.hxx>
+#include <sal/log.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <cppuhelper/bootstrap.hxx>
+#include <svtools/dialogclosedlistener.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <PivotTableDataProvider.hxx>
+#include <chart2uno.hxx>
+#include <fuinsert.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <chartpos.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <undotab.hxx>
+#include <uiitems.hxx>
+#include <drawview.hxx>
+#include <markdata.hxx>
+#include <dpobject.hxx>
+#include <memory>
+
+using namespace css;
+
+namespace
+{
+
+void lcl_ChartInit(const uno::Reference <embed::XEmbeddedObject>& xObj, ScViewData* pViewData,
+ const OUString& rRangeParam, bool bRangeIsPivotTable)
+{
+ ScDocShell* pDocShell = pViewData->GetDocShell();
+ ScDocument& rScDoc = pDocShell->GetDocument();
+
+ OUString aRangeString(rRangeParam);
+
+ if (aRangeString.isEmpty() && !bRangeIsPivotTable)
+ {
+ SCCOL nCol1 = 0;
+ SCROW nRow1 = 0;
+ SCTAB nTab1 = 0;
+ SCCOL nCol2 = 0;
+ SCROW nRow2 = 0;
+ SCTAB nTab2 = 0;
+
+ ScMarkData& rMark = pViewData->GetMarkData();
+ if ( !rMark.IsMarked() )
+ pViewData->GetView()->MarkDataArea();
+
+ if ( pViewData->GetSimpleArea( nCol1,nRow1,nTab1, nCol2,nRow2,nTab2 ) == SC_MARK_SIMPLE )
+ {
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ if (nCol2 >= nCol1 || nRow2 >= nRow1)
+ {
+ ScDocument& rDoc = pViewData->GetDocument();
+ rDoc.LimitChartArea( nTab1, nCol1,nRow1, nCol2,nRow2 );
+
+ ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ aRangeString = aRange.Format(rScDoc, ScRefFlags::RANGE_ABS_3D, rScDoc.GetAddressConvention());
+ }
+ }
+ }
+
+ if (aRangeString.isEmpty())
+ return;
+
+ // connect to Calc data (if no range string, leave chart alone, with its own data)
+
+ uno::Reference< css::chart2::data::XDataReceiver > xReceiver;
+ if( xObj.is())
+ xReceiver.set( xObj->getComponent(), uno::UNO_QUERY );
+ OSL_ASSERT( xReceiver.is());
+ if( !xReceiver.is() )
+ return;
+
+ uno::Reference<chart2::data::XDataProvider> xDataProvider;
+ if (bRangeIsPivotTable)
+ {
+ rtl::Reference<sc::PivotTableDataProvider> pPivotTableDataProvider(new sc::PivotTableDataProvider(rScDoc));
+ pPivotTableDataProvider->setPivotTableName(aRangeString);
+ xDataProvider = pPivotTableDataProvider;
+ }
+ else
+ {
+ xDataProvider.set(new ScChart2DataProvider(&rScDoc));
+ }
+
+ xReceiver->attachDataProvider(xDataProvider);
+
+ uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( getXWeak(pDocShell->GetModel()), uno::UNO_QUERY );
+ xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier );
+
+ // Same behavior as with old chart: Always assume data series in columns
+ chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS;
+ bool bHasCategories = false;
+ bool bFirstCellAsLabel = false;
+
+ // use ScChartPositioner to auto-detect column/row headers (like ScChartArray in old version)
+ ScRangeListRef aRangeListRef( new ScRangeList );
+ aRangeListRef->Parse( aRangeString, rScDoc, rScDoc.GetAddressConvention() );
+ if ( !aRangeListRef->empty() )
+ {
+ rScDoc.LimitChartIfAll( aRangeListRef ); // limit whole columns/rows to used area
+
+ // update string from modified ranges. The ranges must be in the current formula syntax.
+ OUString aTmpStr;
+ aRangeListRef->Format( aTmpStr, ScRefFlags::RANGE_ABS_3D, rScDoc, rScDoc.GetAddressConvention() );
+ aRangeString = aTmpStr;
+
+ ScChartPositioner aChartPositioner( rScDoc, aRangeListRef );
+ const ScChartPositionMap* pPositionMap( aChartPositioner.GetPositionMap() );
+ if( pPositionMap )
+ {
+ SCSIZE nRowCount = pPositionMap->GetRowCount();
+ if( 1==nRowCount )
+ eDataRowSource = chart::ChartDataRowSource_ROWS;
+ }
+ if ( eDataRowSource == chart::ChartDataRowSource_COLUMNS )
+ {
+ bHasCategories = aChartPositioner.HasRowHeaders();
+ bFirstCellAsLabel = aChartPositioner.HasColHeaders();
+ }
+ else // in case the default is changed
+ {
+ bHasCategories = aChartPositioner.HasColHeaders();
+ bFirstCellAsLabel = aChartPositioner.HasRowHeaders();
+ }
+ }
+
+ uno::Sequence< beans::PropertyValue > aArgs{
+ beans::PropertyValue(
+ "CellRangeRepresentation", -1,
+ uno::Any( aRangeString ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "HasCategories", -1,
+ uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "FirstCellAsLabel", -1,
+ uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "DataRowSource", -1,
+ uno::Any( eDataRowSource ), beans::PropertyState_DIRECT_VALUE )
+ };
+
+ try
+ {
+ xReceiver->setArguments( aArgs );
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ // Can happen for invalid aRangeString, in which case a Chart
+ // will be created nevertheless and the range string can be
+ // edited.
+ TOOLS_WARN_EXCEPTION("sc.ui",
+ "lcl_ChartInit - caught IllegalArgumentException might be due to aRangeString: " << aRangeString);
+ }
+
+ // don't create chart listener here (range may be modified in chart dialog)
+}
+
+}
+
+FuInsertOLE::FuInsertOLE(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, SfxRequest& rReq)
+ : FuPoor(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+
+ //! initialize DLLs here, so that the factories exist?
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+ bool bIsFromFile = false;
+ OUString aName;
+
+ sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT;
+ OUString aIconMediaType;
+ uno::Reference< io::XInputStream > xIconMetaFile;
+
+ const sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxGlobalNameItem* pNameItem = rReq.GetArg<SfxGlobalNameItem>(SID_INSERT_OBJECT);
+ if ( nSlot == SID_INSERT_OBJECT && pNameItem )
+ {
+ const SvGlobalName& aClassName = pNameItem->GetValue();
+ xObj = rViewShell.GetViewFrame().GetObjectShell()->GetEmbeddedObjectContainer().CreateEmbeddedObject( aClassName.GetByteSequence(), aName );
+ }
+ else if ( nSlot == SID_INSERT_SMATH )
+ {
+ if ( SvtModuleOptions().IsMath() )
+ {
+ xObj = rViewShell.GetViewFrame().GetObjectShell()->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_SM_CLASSID_60 ).GetByteSequence(), aName );
+ rReq.AppendItem( SfxGlobalNameItem( SID_INSERT_OBJECT, SvGlobalName( SO3_SM_CLASSID_60 ) ) );
+ }
+ }
+ else
+ {
+ SvObjectServerList aServerLst;
+ switch ( nSlot )
+ {
+ case SID_INSERT_OBJECT :
+ if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
+ {
+ std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(
+ nullptr, VclMessageType::Warning, VclButtonsType::Ok,
+ SvtResId(STR_WARNING_ACTIVE_CONTENT_DISABLED)));
+ xError->run();
+ break;
+ }
+ aServerLst.FillInsertObjects();
+ aServerLst.Remove( ScDocShell::Factory().GetClassId() ); // Do not show Starcalc
+ //TODO/LATER: currently no inserting of ClassId into SfxRequest!
+ [[fallthrough]]; //TODO ???
+ case SID_INSERT_FLOATINGFRAME :
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractInsertObjectDialog> pDlg(
+ pFact->CreateInsertObjectDialog( rViewShell.GetFrameWeld(), SC_MOD()->GetSlotPool()->GetSlot(nSlot)->GetCommand(),
+ xStorage, &aServerLst ));
+ if ( pDlg )
+ {
+ pDlg->Execute();
+ xObj = pDlg->GetObject();
+
+ xIconMetaFile = pDlg->GetIconIfIconified( &aIconMediaType );
+ if ( xIconMetaFile.is() )
+ nAspect = embed::Aspects::MSOLE_ICON;
+
+ if ( xObj.is() )
+ rViewSh.GetObjectShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName );
+ // to activate DrawShell (no need to activate Object)
+ bIsFromFile = !pDlg->IsCreateNew();
+ }
+
+ break;
+ }
+ }
+ }
+
+ // SvInsertObjectDialog (everything in one Dialog) are not used anymore
+ if (xObj.is())
+ {
+ pView->UnmarkAll();
+
+ try
+ {
+ ::svt::EmbeddedObjectRef aObjRef( xObj, nAspect );
+ Size aSize;
+ MapMode aMap100( MapUnit::Map100thMM );
+ MapUnit aMapUnit = MapUnit::Map100thMM;
+
+ if ( nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ aObjRef.SetGraphicStream( xIconMetaFile, aIconMediaType );
+ aSize = aObjRef.GetSize( &aMap100 );
+ }
+ else
+ {
+ awt::Size aSz;
+ try
+ {
+ aSz = xObj->getVisualAreaSize( nAspect );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {
+ // the default size will be set later
+ }
+
+ aSize = Size( aSz.Width, aSz.Height );
+
+ aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
+ if (aSize.IsEmpty())
+ {
+ // rectangle with balanced edge ratio
+ aSize.setWidth( 5000 );
+ aSize.setHeight( 5000 );
+ Size aTmp = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit));
+ aSz.Width = aTmp.Width();
+ aSz.Height = aTmp.Height();
+ xObj->setVisualAreaSize( nAspect, aSz );
+
+ // re-convert aSize to 1/100th mm to avoid rounding errors in comparison below
+ aSize = OutputDevice::LogicToLogic( aTmp,
+ MapMode( aMapUnit ), aMap100 );
+ }
+ else
+ aSize = OutputDevice::LogicToLogic( aSize,
+ MapMode( aMapUnit ), aMap100 );
+ }
+
+ // initialize chart ?
+ if ( SvtModuleOptions().IsChart() && SotExchange::IsChart( SvGlobalName( xObj->getClassID() ) ) )
+ lcl_ChartInit(xObj, &rViewSh.GetViewData(), OUString(), false);
+
+ ScViewData& rData = rViewSh.GetViewData();
+
+ Point aPnt = rViewSh.GetInsertPos();
+ if ( rData.GetDocument().IsNegativePage( rData.GetTabNo() ) )
+ aPnt.AdjustX( -(aSize.Width()) ); // move position to left edge
+ tools::Rectangle aRect (aPnt, aSize);
+ rtl::Reference<SdrOle2Obj> pObj = new SdrOle2Obj(
+ *pDoc, // TTTT should be reference
+ aObjRef,
+ aName,
+ aRect);
+ SdrPageView* pPV = pView->GetSdrPageView();
+ bool bSuccess = pView->InsertObjectAtView(pObj.get(), *pPV);
+
+ if (bSuccess && nAspect != embed::Aspects::MSOLE_ICON)
+ {
+ // Math objects change their object size during InsertObject.
+ // New size must be set in SdrObject, or a wrong scale will be set at
+ // ActivateObject.
+
+ try
+ {
+ awt::Size aSz = xObj->getVisualAreaSize( nAspect );
+
+ Size aNewSize( aSz.Width, aSz.Height );
+ aNewSize = OutputDevice::LogicToLogic(aNewSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM));
+
+ if ( aNewSize != aSize )
+ {
+ aRect.SetSize( aNewSize );
+ pObj->SetLogicRect( aRect );
+ }
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {}
+ }
+
+ if ( !rReq.IsAPI() )
+ {
+ // XXX Activate from macro is deadly !!! ???
+ if (bIsFromFile)
+ {
+ // Object selected, activate Draw-Shell
+ rViewShell.SetDrawShell( true );
+ }
+ else if (bSuccess)
+ {
+ rViewShell.ActivateObject(pObj.get(), embed::EmbedVerbs::MS_OLEVERB_SHOW);
+ }
+ }
+
+ rReq.Done();
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "May need error handling here!" );
+ }
+ }
+ else
+ rReq.Ignore();
+}
+
+FuInsertChart::FuInsertChart(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, SfxRequest& rReq, const Link<css::ui::dialogs::DialogClosedEvent*, void>& rLink)
+ : FuPoor(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+
+ if (!SvtModuleOptions().IsChart())
+ return;
+
+ // BM/IHA --
+
+ // get range
+ OUString aRangeString;
+ bool bRangeIsPivotTable = false;
+ ScRange aPositionRange; // cell range for chart positioning
+ ScMarkData aMark = rViewSh.GetViewData().GetMarkData();
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FN_PARAM_5, &pItem ) )
+ aRangeString = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ aPositionRange = rViewSh.GetViewData().GetCurPos();
+ }
+ else
+ {
+ ScDocument& rDocument = rViewSh.GetViewData().GetDocument();
+ ScDPObject* pObject = rDocument.GetDPAtCursor(rViewSh.GetViewData().GetCurX(),
+ rViewSh.GetViewData().GetCurY(),
+ rViewSh.GetViewData().GetTabNo());
+ if (pObject)
+ {
+ aRangeString = pObject->GetName();
+ bRangeIsPivotTable = true;
+ }
+ else
+ {
+ bool bAutomaticMark = false;
+ if ( !aMark.IsMarked() && !aMark.IsMultiMarked() )
+ {
+ rViewSh.GetViewData().GetView()->MarkDataArea();
+ bAutomaticMark = true;
+ }
+
+ ScMarkData aMultiMark( aMark );
+ aMultiMark.MarkToMulti();
+
+ ScRangeList aRanges;
+ aMultiMark.FillRangeListWithMarks( &aRanges, false );
+ OUString aStr;
+ aRanges.Format( aStr, ScRefFlags::RANGE_ABS_3D, rDocument, rDocument.GetAddressConvention() );
+ aRangeString = aStr;
+
+ // get "total" range for positioning
+ if ( !aRanges.empty() )
+ {
+ aPositionRange = aRanges[ 0 ];
+ for ( size_t i = 1, nCount = aRanges.size(); i < nCount; ++i )
+ {
+ aPositionRange.ExtendTo( aRanges[ i ] );
+ }
+ }
+
+ if(bAutomaticMark)
+ rViewSh.GetViewData().GetView()->Unmark();
+ }
+ }
+
+ // adapted old code
+ pView->UnmarkAll();
+
+ OUString aName;
+ const sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT;
+
+ uno::Reference < embed::XEmbeddedObject > xObj =
+ rViewShell.GetObjectShell()->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID_60 ).GetByteSequence(), aName );
+
+ uno::Reference< css::chart2::data::XDataReceiver > xReceiver;
+ if( xObj.is())
+ xReceiver.set( xObj->getComponent(), uno::UNO_QUERY );
+
+ uno::Reference<chart2::XChartDocument> xChartDoc(xReceiver, uno::UNO_QUERY);
+ if (xChartDoc.is())
+ xChartDoc->createDefaultChart();
+
+ // lock the model to suppress any internal updates
+ uno::Reference< frame::XModel > xChartModel( xReceiver, uno::UNO_QUERY );
+ if( xChartModel.is() )
+ xChartModel->lockControllers();
+
+ // object size
+ awt::Size aSz = xObj->getVisualAreaSize( nAspect );
+ Size aSize( aSz.Width, aSz.Height );
+
+ MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
+
+ bool bSizeCh = false;
+ if (aSize.IsEmpty())
+ {
+ aSize.setWidth( 5000 );
+ aSize.setHeight( 5000 );
+ bSizeCh = true;
+ }
+ if (bSizeCh)
+ {
+ aSize = OutputDevice::LogicToLogic( aSize, MapMode( MapUnit::Map100thMM ), MapMode( aMapUnit ) );
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xObj->setVisualAreaSize( nAspect, aSz );
+ }
+
+ ScViewData& rData = rViewSh.GetViewData();
+ ScDocShell* pScDocSh = rData.GetDocShell();
+ ScDocument& rScDoc = pScDocSh->GetDocument();
+ bool bUndo (rScDoc.IsUndoEnabled());
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ sal_uInt16 nToTable = 0;
+
+ if( pReqArgs->HasItem( FN_PARAM_4, &pItem ) )
+ {
+ if ( auto pUInt16Item = dynamic_cast<const SfxUInt16Item*>( pItem) )
+ nToTable = pUInt16Item->GetValue();
+ else if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( pItem) )
+ {
+ // In IDL for Basic FN_PARAM_4 means SfxBoolItem
+ // -> if set new table, else current table
+
+ if ( pBoolItem->GetValue() )
+ nToTable = static_cast<sal_uInt16>(rScDoc.GetTableCount());
+ else
+ nToTable = static_cast<sal_uInt16>(rData.GetTabNo());
+ }
+ }
+ else
+ {
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_4, nToTable ) );
+ }
+
+ // Output on new table?
+ if ( nToTable == rScDoc.GetTableCount() )
+ {
+ // Let's go...
+ OUString aTabName;
+ SCTAB nNewTab = rScDoc.GetTableCount();
+
+ rScDoc.CreateValidTabName( aTabName );
+
+ if ( rScDoc.InsertTab( nNewTab, aTabName ) )
+ {
+ if (bUndo)
+ {
+ pScDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( pScDocSh, nNewTab,
+ true/*bAppend*/, aTabName ) );
+ }
+
+ pScDocSh->Broadcast( ScTablesHint( SC_TAB_INSERTED, nNewTab ) );
+ rViewSh.SetTabNo( nNewTab, true );
+ pScDocSh->PostPaintExtras(); //! done afterwards ???
+ }
+ else
+ {
+ OSL_FAIL( "Could not create new table :-/" );
+ }
+ }
+ else if ( nToTable != rData.GetTabNo() )
+ {
+ rViewSh.SetTabNo( nToTable, true );
+ }
+ }
+
+ lcl_ChartInit(xObj, &rData, aRangeString, bRangeIsPivotTable); // set source range, auto-detect column/row headers
+
+ // object position
+
+ // get chart position (from window size and data range)
+ Point aStart = rViewSh.GetChartInsertPos( aSize, aPositionRange );
+
+ tools::Rectangle aRect (aStart, aSize);
+ rtl::Reference<SdrOle2Obj> pObj = new SdrOle2Obj(
+ *pDoc, // TTTT should be reference
+ svt::EmbeddedObjectRef(xObj, nAspect),
+ aName,
+ aRect);
+ SdrPageView* pPV = pView->GetSdrPageView();
+
+ // #i121334# This call will change the chart's default background fill from white to transparent.
+ // Add here again if this is wanted (see task description for details)
+ // ChartHelper::AdaptDefaultsForChart( xObj );
+
+// pView->InsertObjectAtView(pObj, *pPV);//this call leads to an immediate redraw and asks the chart for a visual representation
+
+ // use the page instead of the view to insert, so no undo action is created yet
+ SdrPage* pPage = pPV->GetPage();
+ pPage->InsertObject( pObj.get() );
+ pView->UnmarkAllObj();
+ pView->MarkObj( pObj.get(), pPV );
+
+ if (rReq.IsAPI())
+ {
+ if( xChartModel.is() )
+ xChartModel->unlockControllers();
+ }
+ else if (!rViewSh.isLOKMobilePhone())
+ {
+ //the controller will be unlocked by the dialog when the dialog is told to do so
+
+ // only activate object if not called via API (e.g. macro)
+ if (!comphelper::LibreOfficeKit::isActive())
+ rViewShell.ActivateObject(pObj.get(), embed::EmbedVerbs::MS_OLEVERB_SHOW);
+
+ //open wizard
+ //@todo get context from calc if that has one
+ uno::Reference< uno::XComponentContext > xContext(
+ ::cppu::defaultBootstrap_InitialComponentContext() );
+ if(xContext.is())
+ {
+ uno::Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() );
+ if(xMCF.is())
+ {
+ css::uno::Reference<css::ui::dialogs::XAsynchronousExecutableDialog> xDialog(
+ xMCF->createInstanceWithContext(
+ "com.sun.star.comp.chart2.WizardDialog"
+ , xContext), uno::UNO_QUERY);
+ uno::Reference< lang::XInitialization > xInit( xDialog, uno::UNO_QUERY );
+ if( xChartModel.is() && xInit.is() )
+ {
+ css::uno::Reference< css::awt::XWindow > xParent
+ = new weld::TransportAsXWindow(pWin->GetFrameWeld());
+ uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence(
+ {
+ {"ParentWindow", uno::Any(xParent)},
+ {"ChartModel", uno::Any(xChartModel)}
+ }));
+ xInit->initialize( aSeq );
+
+ // try to set the dialog's position so it doesn't hide the chart
+ uno::Reference < beans::XPropertySet > xDialogProps( xDialog, uno::UNO_QUERY );
+ if ( xDialogProps.is() )
+ {
+ try
+ {
+ //get dialog size:
+ awt::Size aDialogAWTSize;
+ if( xDialogProps->getPropertyValue("Size")
+ >>= aDialogAWTSize )
+ {
+ Size aDialogSize( aDialogAWTSize.Width, aDialogAWTSize.Height );
+ if ( !aDialogSize.IsEmpty() )
+ {
+ //calculate and set new position
+ Point aDialogPos = rViewShell.GetChartDialogPos( aDialogSize, aRect );
+ xDialogProps->setPropertyValue("Position",
+ uno::Any( awt::Point(aDialogPos.getX(),aDialogPos.getY()) ) );
+ }
+ }
+ //tell the dialog to unlock controller
+ xDialogProps->setPropertyValue("UnlockControllersOnExecute",
+ uno::Any( true ) );
+
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Chart wizard couldn't be positioned automatically" );
+ }
+ }
+
+ pView->AddUndo(std::make_unique<SdrUndoNewObj>(*pObj));
+ rtl::Reference<::svt::DialogClosedListener> pListener = new ::svt::DialogClosedListener();
+ pListener->SetDialogClosedLink( rLink );
+
+ xDialog->startExecuteModal( pListener );
+ }
+ else
+ {
+ uno::Reference< lang::XComponent > xComponent( xDialog, uno::UNO_QUERY );
+ if( xComponent.is())
+ xComponent->dispose();
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fupoor.cxx b/sc/source/ui/drawfunc/fupoor.cxx
new file mode 100644
index 0000000000..4299e02512
--- /dev/null
+++ b/sc/source/ui/drawfunc/fupoor.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/outliner.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svxids.hrc>
+
+#include <fupoor.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <detfunc.hxx>
+#include <document.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdrhittesthelper.hxx>
+
+FuPoor::FuPoor(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq) :
+ pView(pViewP),
+ rViewShell(rViewSh),
+ pWindow(pWin),
+ pDrDoc(pDoc),
+ aSfxRequest(rReq),
+ aScrollTimer("sc FuPoor aScrollTimer"),
+ aDragTimer("sc FuPoor aDragTimer"),
+ bIsInDragMode(false),
+ // remember MouseButton state
+ mnCode(0)
+{
+ aScrollTimer.SetInvokeHandler( LINK(this, FuPoor, ScrollHdl) );
+ aScrollTimer.SetTimeout(SELENG_AUTOREPEAT_INTERVAL);
+
+ aDragTimer.SetInvokeHandler( LINK(this, FuPoor, DragTimerHdl) );
+ aDragTimer.SetTimeout(SELENG_DRAGDROP_TIMEOUT);
+}
+
+FuPoor::~FuPoor()
+{
+ aDragTimer.Stop();
+ aScrollTimer.Stop();
+}
+
+void FuPoor::Activate()
+{
+}
+
+void FuPoor::Deactivate()
+{
+ aDragTimer.Stop();
+ aScrollTimer.Stop();
+}
+
+// Scroll when reached the window border; is called from MouseMove
+void FuPoor::ForceScroll(const Point& aPixPos)
+{
+ aScrollTimer.Stop();
+
+ Size aSize = pWindow->GetSizePixel();
+ SCCOL dx = 0;
+ SCROW dy = 0;
+
+ if ( aPixPos.X() <= 0 ) dx = -1;
+ if ( aPixPos.X() >= aSize.Width() ) dx = 1;
+ if ( aPixPos.Y() <= 0 ) dy = -1;
+ if ( aPixPos.Y() >= aSize.Height() ) dy = 1;
+
+ ScViewData& rViewData = rViewShell.GetViewData();
+ if ( rViewData.GetDocument().IsNegativePage( rViewData.GetTabNo() ) )
+ dx = -dx;
+
+ ScSplitPos eWhich = rViewData.GetActivePart();
+ if ( dx > 0 && rViewData.GetHSplitMode() == SC_SPLIT_FIX && WhichH(eWhich) == SC_SPLIT_LEFT )
+ {
+ rViewShell.ActivatePart( ( eWhich == SC_SPLIT_TOPLEFT ) ?
+ SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT );
+ dx = 0;
+ }
+ if ( dy > 0 && rViewData.GetVSplitMode() == SC_SPLIT_FIX && WhichV(eWhich) == SC_SPLIT_TOP )
+ {
+ rViewShell.ActivatePart( ( eWhich == SC_SPLIT_TOPLEFT ) ?
+ SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
+ dy = 0;
+ }
+
+ if ( dx != 0 || dy != 0 )
+ {
+ rViewShell.ScrollLines(2*dx, 4*dy);
+ aScrollTimer.Start();
+ }
+}
+
+// Timer handler for window scrolling
+IMPL_LINK_NOARG(FuPoor, ScrollHdl, Timer *, void)
+{
+ Point aPosPixel = pWindow->GetPointerPosPixel();
+
+ // use remembered MouseButton state to create correct
+ // MouseEvents for this artificial MouseMove.
+ MouseMove(MouseEvent(aPosPixel, 1, MouseEventModifiers::NONE, GetMouseButtonCode()));
+}
+
+bool FuPoor::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ return false;
+}
+
+bool FuPoor::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ return false;
+}
+
+// If we handle a KeyEvent, then the return value is sal_True else FALSE.
+bool FuPoor::KeyInput(const KeyEvent& /* rKEvt */)
+{
+ return false;
+}
+
+sal_uInt8 FuPoor::Command(const CommandEvent& rCEvt)
+{
+ if ( CommandEventId::StartDrag == rCEvt.GetCommand() )
+ {
+ // Only if a selection is in Outliner, then Command is allowed
+ // to return sal_True
+
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+
+ if ( pOutView )
+ return pOutView->HasSelection() ? (pView->Command(rCEvt,pWindow) ? 1 : 0) : SC_CMD_NONE;
+ else
+ return pView->Command(rCEvt,pWindow) ? 1 : 0;
+ }
+ else
+ return pView->Command(rCEvt,pWindow) ? 1 : 0;
+}
+
+// Timer-Handler for Drag&Drop
+IMPL_LINK_NOARG(FuPoor, DragTimerHdl, Timer *, void)
+{
+ // Calling ExecuteDrag (and that associated reschedule) directly from
+ // the Timer, will confuse the VCL-Timer-Management, if (e.g during Drop)
+ // a new timer is started (e.g ComeBack-Timer of DrawView for
+ // Solid Handles / ModelHasChanged) - the new timer will end with a delay
+ // of the duration of the Drag&Drop.
+ // Therefore Drag&Drop from own event:
+
+ Application::PostUserEvent( LINK( this, FuPoor, DragHdl ) );
+}
+
+IMPL_LINK_NOARG(FuPoor, DragHdl, void*, void)
+{
+ SdrHdl* pHdl = pView->PickHandle(aMDPos);
+
+ if ( pHdl==nullptr && pView->IsMarkedHit(aMDPos) )
+ {
+ pWindow->ReleaseMouse();
+ bIsInDragMode = true;
+ rViewShell.GetScDrawView()->BeginDrag(pWindow, aMDPos);
+ }
+}
+
+// Detective-line
+bool FuPoor::IsDetectiveHit( const Point& rLogicPos )
+{
+ SdrPageView* pPV = pView->GetSdrPageView();
+ if (!pPV)
+ return false;
+
+ bool bFound = false;
+ SdrObjListIter aIter( pPV->GetObjList(), SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ if (ScDetectiveFunc::IsNonAlienArrow( pObject ))
+ {
+ double fHitLog = pWindow->PixelToLogic(Size(pView->GetHitTolerancePixel(),0)).Width();
+ if(SdrObjectPrimitiveHit(*pObject, rLogicPos, {fHitLog, fHitLog}, *pPV, nullptr, false))
+ {
+ bFound = true;
+ }
+ }
+
+ pObject = aIter.Next();
+ }
+ return bFound;
+}
+
+void FuPoor::StopDragTimer()
+{
+ if (aDragTimer.IsActive() )
+ aDragTimer.Stop();
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuPoor::CreateDefaultObject(const sal_uInt16 /* nID */, const tools::Rectangle& /* rRectangle */)
+{
+ // empty base implementation
+ return nullptr;
+}
+
+void FuPoor::ImpForceQuadratic(tools::Rectangle& rRect)
+{
+ if(rRect.GetWidth() > rRect.GetHeight())
+ {
+ rRect = tools::Rectangle(
+ Point(rRect.Left() + ((rRect.GetWidth() - rRect.GetHeight()) / 2), rRect.Top()),
+ Size(rRect.GetHeight(), rRect.GetHeight()));
+ }
+ else
+ {
+ rRect = tools::Rectangle(
+ Point(rRect.Left(), rRect.Top() + ((rRect.GetHeight() - rRect.GetWidth()) / 2)),
+ Size(rRect.GetWidth(), rRect.GetWidth()));
+ }
+}
+
+// #i33136# fdo#88339
+bool FuPoor::doConstructOrthogonal() const
+{
+ // Detect whether we're moving an object or resizing.
+ if (pView->IsDragObj())
+ {
+ const SdrHdl* pHdl = pView->GetDragStat().GetHdl();
+ if (!pHdl || (!pHdl->IsCornerHdl() && !pHdl->IsVertexHdl()))
+ {
+ return false;
+ }
+ }
+
+ // Detect image/media and resize proportionally, but don't constrain movement by default
+ if (pView->AreObjectsMarked())
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObjKind aObjIdentifier = rMarkList.GetMark(0)->GetMarkedSdrObj()->GetObjIdentifier();
+ bool bIsMediaSelected = aObjIdentifier == SdrObjKind::Graphic ||
+ aObjIdentifier == SdrObjKind::Media ||
+ aObjIdentifier == SdrObjKind::OLE2;
+
+ SdrHdl* pHdl = pView->PickHandle(aMDPos);
+ // Resize proportionally when media is selected and the user drags on a corner
+ if (pHdl)
+ return bIsMediaSelected && pHdl->IsCornerHdl();
+ return bIsMediaSelected;
+ }
+ }
+ else if (aSfxRequest.GetSlot() == SID_DRAW_XPOLYGON
+ || aSfxRequest.GetSlot() == SID_DRAW_XPOLYGON_NOFILL
+ || aSfxRequest.GetSlot() == SID_DRAW_XLINE)
+ return true;
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fusel.cxx b/sc/source/ui/drawfunc/fusel.cxx
new file mode 100644
index 0000000000..d95f810308
--- /dev/null
+++ b/sc/source/ui/drawfunc/fusel.cxx
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+#include <editeng/flditem.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdotext.hxx>
+#include <sfx2/dispatch.hxx>
+#include <vcl/imapobj.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <editeng/outlobj.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/lok.hxx>
+
+#include <fusel.hxx>
+#include <sc.hrc>
+#include <fudraw.hxx>
+#include <futext.hxx>
+#include <drawview.hxx>
+#include <tabvwsh.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <scmod.hxx>
+#include <charthelper.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <stlpool.hxx>
+
+// maximal permitted mouse movement to start Drag&Drop
+//! fusel,fuconstr,futext - combine them!
+#define SC_MAXDRAGMOVE 3
+// Min necessary mouse motion for normal dragging
+#define SC_MINDRAGMOVE 2
+
+using namespace com::sun::star;
+
+FuSelection::FuSelection(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuDraw(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+}
+
+FuSelection::~FuSelection()
+{
+}
+
+bool FuSelection::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+ const bool bSelectionOnly = rMEvt.IsRight();
+ if ( pView->IsAction() )
+ {
+ if ( bSelectionOnly )
+ pView->BckAction();
+ return true;
+ }
+
+ bIsInDragMode = false; // somewhere it has to be reset (#50033#)
+
+ bool bReturn = FuDraw::MouseButtonDown(rMEvt);
+ auto aLogicPosition = rMEvt.getLogicPosition();
+ if (aLogicPosition)
+ aMDPos = *aLogicPosition;
+ else
+ aMDPos = pWindow->PixelToLogic(rMEvt.GetPosPixel());
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScViewData& rViewData = rViewShell.GetViewData();
+ ScDocument& rDocument = rViewData.GetDocument();
+ if (rDocument.IsNegativePage(rViewData.GetTabNo()))
+ aMDPos.setX(-aMDPos.X());
+ }
+
+ if ( rMEvt.IsLeft() )
+ {
+ SdrHdl* pHdl = pView->PickHandle(aMDPos);
+
+ if ( pHdl!=nullptr || pView->IsMarkedHit(aMDPos) )
+ {
+ // Determine if this is the tail of a SdrCaptionObj i.e.
+ // we need to disable the drag option on the tail of a note
+ // object. Also, disable the ability to use the circular
+ // drag of a note object.
+ bool bDrag = false;
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pMarkedObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( ScDrawLayer::IsNoteCaption( pMarkedObj ) )
+ {
+ // move using the valid caption handles for note text box.
+ if(pHdl && (pHdl->GetKind() != SdrHdlKind::Poly && pHdl->GetKind() != SdrHdlKind::Circle))
+ bDrag = true;
+ // move the complete note box.
+ else if(!pHdl)
+ bDrag = true;
+ }
+ else
+ bDrag = true; // different object
+ }
+ else
+ bDrag = true; // several objects
+
+ if ( bDrag )
+ {
+ aDragTimer.Start();
+ if (pView->BegDragObj(aMDPos, nullptr, pHdl))
+ pView->GetDragMethod()->SetShiftPressed( rMEvt.IsShift() );
+ bReturn = true;
+ }
+ }
+ else
+ {
+ SdrPageView* pPV = nullptr;
+ bool bAlt = rMEvt.IsMod2();
+ SdrObject* pObj = !bAlt ? pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) : nullptr;
+ if (pObj)
+ {
+ pView->BegMacroObj(aMDPos, pObj, pPV, pWindow);
+ bReturn = true;
+ }
+ else
+ {
+ OUString sURL, sTarget;
+ pObj = !bAlt ? pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER) : nullptr;
+ if (pObj)
+ {
+ // Support for imported Excel docs
+ // Excel is of course not consistent and allows
+ // a hyperlink to be assigned for an object group
+ // and even though the hyperlink is exported in the Escher layer
+ // its never used, when dealing with a group object the link
+ // associated with the clicked object is used only
+
+ // additionally you can also select a macro in Excel for a grouped
+ // objects and this *usually* results in the macro being set
+ // for the elements in the group and no macro is exported
+ // for the group itself ( this however is not always true )
+ // if a macro and hlink are defined favour the hlink
+ // If a group object has no hyperlink use the hyperlink of the
+ // object clicked
+
+ if ( pObj->IsGroupObject() )
+ {
+ ScMacroInfo* pTmpInfo = ScDrawLayer::GetMacroInfo( pObj );
+ if ( !pTmpInfo || pTmpInfo->GetMacro().isEmpty() )
+ {
+ SdrObject* pHit = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::DEEP);
+ if (pHit)
+ pObj = pHit;
+ }
+ }
+
+ ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pObj, true );
+ // For interoperability favour links over macros if both are defined
+ if ( !pObj->getHyperlink().isEmpty() )
+ {
+ sURL = pObj->getHyperlink();
+ }
+ else if ( !pInfo->GetMacro().isEmpty() )
+ {
+ SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if ( pObjSh && SfxApplication::IsXScriptURL( pInfo->GetMacro() ) )
+ {
+ uno::Reference< beans::XPropertySet > xProps( pObj->getUnoShape(), uno::UNO_QUERY );
+ uno::Any aCaller;
+ if ( xProps.is() )
+ {
+ try
+ {
+ aCaller = xProps->getPropertyValue("Name");
+ }
+ catch( uno::Exception& ) {}
+ }
+ uno::Any aRet;
+ uno::Sequence< sal_Int16 > aOutArgsIndex;
+ uno::Sequence< uno::Any > aOutArgs;
+ uno::Sequence< uno::Any > aInArgs;
+ pObjSh->CallXScript( pInfo->GetMacro(),
+ aInArgs, aRet, aOutArgsIndex, aOutArgs, true, &aCaller );
+ rViewShell.FakeButtonUp( rViewShell.GetViewData().GetActivePart() );
+ return true; // no CaptureMouse etc.
+ }
+ }
+ }
+
+ // URL / ImageMap
+
+ SdrViewEvent aVEvt;
+ if ( !bAlt &&
+ pView->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt ) != SdrHitKind::NONE &&
+ aVEvt.mpObj != nullptr )
+ {
+ if ( SvxIMapInfo::GetIMapInfo( aVEvt.mpObj ) ) // ImageMap
+ {
+ const IMapObject* pIMapObj =
+ SvxIMapInfo::GetHitIMapObject( aVEvt.mpObj, aMDPos, pWindow->GetOutDev() );
+ if ( pIMapObj && !pIMapObj->GetURL().isEmpty() )
+ {
+ sURL = pIMapObj->GetURL();
+ sTarget = pIMapObj->GetTarget();
+ }
+ }
+ if ( aVEvt.meEvent == SdrEventKind::ExecuteUrl && aVEvt.mpURLField ) // URL
+ {
+ sURL = aVEvt.mpURLField->GetURL();
+ sTarget = aVEvt.mpURLField->GetTargetFrame();
+ }
+ }
+
+ // open hyperlink, if found at object or in object's text
+ // Fragments pointing into the current document should be always opened.
+ if ( !sURL.isEmpty() && (ScGlobal::ShouldOpenURL() || sURL.startsWith("#")) )
+ {
+ ScGlobal::OpenURL( sURL, sTarget );
+ rViewShell.FakeButtonUp( rViewShell.GetViewData().GetActivePart() );
+ return true; // no CaptureMouse etc.
+ }
+
+ // Is another object being edited in this view?
+ // (Editing is ended in MarkListHasChanged - test before UnmarkAll)
+ SfxInPlaceClient* pClient = rViewShell.GetIPClient();
+ bool bWasOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
+
+ // Selection
+
+ // do not allow multiselection with note caption
+ bool bCaptionClicked = IsNoteCaptionClicked( aMDPos );
+ if ( !rMEvt.IsShift() || bCaptionClicked || IsNoteCaptionMarked() )
+ pView->UnmarkAll();
+
+ /* Unlock internal layer, if a note caption is clicked. The
+ layer will be relocked in ScDrawView::MarkListHasChanged(). */
+ if( bCaptionClicked )
+ pView->UnlockInternalLayer();
+
+ // try to select the clicked object
+ if ( pView->MarkObj( aMDPos, -2, false, rMEvt.IsMod1() ) )
+ {
+
+ // move object
+
+ if (pView->IsMarkedHit(aMDPos))
+ {
+ // Don't start drag timer if inplace editing of an OLE object
+ // was just ended with this mouse click - the view will be moved
+ // (different tool bars) and the object that was clicked on would
+ // be moved unintentionally.
+ if ( !bWasOleActive )
+ aDragTimer.Start();
+
+ pHdl=pView->PickHandle(aMDPos);
+ pView->BegDragObj(aMDPos, nullptr, pHdl);
+ bReturn = true;
+ }
+ else // object at the edge
+ if (rViewShell.IsDrawSelMode())
+ bReturn = true;
+ }
+ else
+ {
+ if (rViewShell.IsDrawSelMode())
+ {
+
+ // select object
+
+ pView->BegMarkObj(aMDPos);
+ bReturn = true;
+ }
+ }
+ }
+ }
+
+ }
+
+ if (!bIsInDragMode)
+ {
+ // VC calls CaptureMouse itself
+ pWindow->CaptureMouse();
+ ForcePointer(&rMEvt);
+ }
+
+ return bReturn;
+}
+
+bool FuSelection::MouseMove(const MouseEvent& rMEvt)
+{
+ bool bReturn = FuDraw::MouseMove(rMEvt);
+
+ if (aDragTimer.IsActive() )
+ {
+ Point aOldPixel = pWindow->LogicToPixel( aMDPos );
+ Point aNewPixel = rMEvt.GetPosPixel();
+ if ( std::abs( aOldPixel.X() - aNewPixel.X() ) > SC_MAXDRAGMOVE ||
+ std::abs( aOldPixel.Y() - aNewPixel.Y() ) > SC_MAXDRAGMOVE )
+ aDragTimer.Stop();
+ }
+
+ if ( pView->IsAction() )
+ {
+ Point aPix(rMEvt.GetPosPixel());
+ Point aPnt(pWindow->PixelToLogic(aPix));
+
+ ForceScroll(aPix);
+ pView->MovAction(aPnt);
+ bReturn = true;
+ }
+
+ ForcePointer(&rMEvt);
+
+ return bReturn;
+}
+
+bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ bool bReturn = FuDraw::MouseButtonUp(rMEvt);
+ bool bOle = rViewShell.GetViewFrame().GetFrame().IsInPlace();
+
+ SdrObject* pObj = nullptr;
+ if (aDragTimer.IsActive() )
+ {
+ aDragTimer.Stop();
+ }
+
+ sal_uInt16 nDrgLog = sal_uInt16 ( pWindow->PixelToLogic(Size(SC_MINDRAGMOVE,0)).Width() );
+ auto aLogicPosition = rMEvt.getLogicPosition();
+ Point aPnt(aLogicPosition ? *aLogicPosition : pWindow->PixelToLogic(rMEvt.GetPosPixel()));
+
+ bool bCopy = false;
+ ScViewData& rViewData = rViewShell.GetViewData();
+ ScDocument& rDocument = rViewData.GetDocument();
+ SdrPageView* pPageView = ( pView ? pView->GetSdrPageView() : nullptr );
+ SdrPage* pPage = ( pPageView ? pPageView->GetPage() : nullptr );
+ ::std::vector< OUString > aExcludedChartNames;
+ ScRangeListVector aProtectedChartRangesVector;
+
+ if (comphelper::LibreOfficeKit::isActive() && rDocument.IsNegativePage(rViewData.GetTabNo()))
+ aPnt.setX(-aPnt.X());
+
+ if (pView && rMEvt.IsLeft())
+ {
+ if ( pView->IsDragObj() )
+ {
+ // object was moved
+ if ( rMEvt.IsMod1() )
+ {
+ if ( pPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
+ }
+ if ( pView )
+ {
+ const SdrMarkList& rSdrMarkList = pView->GetMarkedObjectList();
+ const size_t nMarkCount = rSdrMarkList.GetMarkCount();
+ for ( size_t i = 0; i < nMarkCount; ++i )
+ {
+ SdrMark* pMark = rSdrMarkList.GetMark( i );
+ pObj = ( pMark ? pMark->GetMarkedSdrObj() : nullptr );
+ if ( pObj )
+ {
+ ScChartHelper::AddRangesIfProtectedChart( aProtectedChartRangesVector, rDocument, pObj );
+ }
+ }
+ }
+ bCopy = true;
+ }
+
+ if (!rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() &&
+ std::abs(aPnt.X() - aMDPos.X()) < nDrgLog &&
+ std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog)
+ {
+ /* If a user wants to click on an object in front of a marked
+ one, he releases the mouse button immediately */
+ SdrPageView* pPV = nullptr;
+ pObj = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK);
+ if (pObj)
+ {
+ pView->UnmarkAllObj();
+ pView->MarkObj(pObj,pPV);
+ return true;
+ }
+ }
+ pView->EndDragObj( rMEvt.IsMod1() );
+ pView->ForceMarkedToAnotherPage();
+
+ bReturn = true;
+ }
+ else if (pView->IsAction() )
+ {
+ // unlock internal layer to include note captions
+ pView->UnlockInternalLayer();
+ pView->EndAction();
+ if ( pView->AreObjectsMarked() )
+ {
+ bReturn = true;
+
+ /* if multi-selection contains a note caption object, remove
+ all other objects from selection. */
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+ if( nCount > 1 )
+ {
+ bool bFound = false;
+ for( size_t nIdx = 0; !bFound && (nIdx < nCount); ++nIdx )
+ {
+ pObj = rMarkList.GetMark( nIdx )->GetMarkedSdrObj();
+ bFound = ScDrawLayer::IsNoteCaption( pObj );
+ if( bFound )
+ {
+ pView->UnMarkAll();
+ pView->MarkObj( pObj, pView->GetSdrPageView() );
+ }
+ }
+ }
+ }
+ }
+
+ if (SC_MOD()->GetIsWaterCan())
+ {
+ auto pStyleSheet = rViewData.GetDocument().GetStyleSheetPool()->GetActualStyleSheet();
+ if (pStyleSheet && pStyleSheet->GetFamily() == SfxStyleFamily::Frame)
+ pView->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), false);
+ }
+ }
+
+ // maybe consider OLE object
+ SfxInPlaceClient* pIPClient = rViewShell.GetIPClient();
+
+ if (pIPClient)
+ {
+ ScModule* pScMod = SC_MOD();
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ if ( pIPClient->IsObjectInPlaceActive() && !bUnoRefDialog )
+ pIPClient->DeactivateObject();
+ }
+
+ sal_uInt16 nClicks = rMEvt.GetClicks();
+ if (pView && nClicks == 2 && rMEvt.IsLeft())
+ {
+ if ( pView->AreObjectsMarked() )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ pObj = pMark->GetMarkedSdrObj();
+
+ // only activate, when the mouse also is over the selected object
+
+ SdrViewEvent aVEvt;
+ SdrHitKind eHit = pView->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt );
+ if (eHit != SdrHitKind::NONE && aVEvt.mpObj == pObj)
+ {
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+
+ // OLE: activate
+
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ if (!bOle)
+ {
+ if (static_cast<SdrOle2Obj*>(pObj)->GetObjRef().is())
+ {
+ // release so if ActivateObject launches a warning dialog, then that dialog
+ // can get mouse events
+ if (pWindow->IsMouseCaptured())
+ pWindow->ReleaseMouse();
+ rViewShell.ActivateObject(static_cast<SdrOle2Obj*>(pObj), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY);
+ }
+ }
+ }
+
+ // Edit text
+ // not in UNO controls
+ // #i32352# not in media objects
+
+ else if ( DynCastSdrTextObj( pObj) != nullptr && dynamic_cast<const SdrUnoObj*>( pObj) == nullptr && dynamic_cast<const SdrMediaObj*>( pObj) == nullptr )
+ {
+ OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject();
+ bool bVertical = ( pOPO && pOPO->IsEffectivelyVertical() );
+ sal_uInt16 nTextSlotId = bVertical ? SID_DRAW_TEXT_VERTICAL : SID_DRAW_TEXT;
+
+ rViewShell.GetViewData().GetDispatcher().
+ Execute(nTextSlotId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD);
+
+ // Get the created FuText now and change into EditMode
+ FuPoor* pPoor = rViewShell.GetViewData().GetView()->GetDrawFuncPtr();
+ if ( pPoor && pPoor->GetSlotID() == nTextSlotId ) // has no RTTI
+ {
+ FuText* pText = static_cast<FuText*>(pPoor);
+ Point aMousePixel = rMEvt.GetPosPixel();
+ pText->SetInEditMode( pObj, &aMousePixel );
+ }
+ bReturn = true;
+ }
+ }
+ }
+ }
+ else if ( TestDetective( pView->GetSdrPageView(), aPnt ) )
+ bReturn = true;
+ }
+
+ ForcePointer(&rMEvt);
+
+ if (pWindow->IsMouseCaptured())
+ pWindow->ReleaseMouse();
+
+ // command handler for context menu follows after MouseButtonUp,
+ // therefore here the hard IsLeft call
+ if ( !bReturn && rMEvt.IsLeft() )
+ if (rViewShell.IsDrawSelMode())
+ rViewShell.GetViewData().GetDispatcher().
+ Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD);
+
+ if ( bCopy && pPage )
+ {
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ ScModelObj* pModelObj = ( pDocShell ? pDocShell->GetModel() : nullptr );
+ if ( pModelObj )
+ {
+ SCTAB nTab = rViewData.GetTabNo();
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDocument, pPage, pModelObj, nTab,
+ aProtectedChartRangesVector, aExcludedChartNames );
+ }
+ }
+
+ return bReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/fusel2.cxx b/sc/source/ui/drawfunc/fusel2.cxx
new file mode 100644
index 0000000000..cc9a1478ce
--- /dev/null
+++ b/sc/source/ui/drawfunc/fusel2.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/svditer.hxx>
+#include <svx/svdpagv.hxx>
+
+#include <fusel.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <detfunc.hxx>
+#include <attrib.hxx>
+#include <scitems.hxx>
+#include <userdat.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <svx/sdrhittesthelper.hxx>
+
+static tools::Long Diff( const Point& rP1, const Point& rP2 )
+{
+ tools::Long nX = rP1.X() - rP2.X();
+ if (nX<0) nX = -nX;
+ tools::Long nY = rP1.Y() - rP2.Y();
+ if (nY<0) nY = -nY;
+ return nX+nY;
+}
+
+bool FuSelection::TestDetective( const SdrPageView* pPV, const Point& rPos )
+{
+ if (!pPV)
+ return false;
+
+ bool bFound = false;
+ SdrObjListIter aIter( pPV->GetObjList(), SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ if (ScDetectiveFunc::IsNonAlienArrow( pObject ))
+ {
+ double fHitLog = pWindow->PixelToLogic(Size(pView->GetHitTolerancePixel(),0)).Width();
+ if (SdrObjectPrimitiveHit(*pObject, rPos, {fHitLog, fHitLog}, *pPV, nullptr, false))
+ {
+ ScViewData& rViewData = rViewShell.GetViewData();
+ ScSplitPos ePos = rViewShell.FindWindow( pWindow );
+ Point aLineStart = pObject->GetPoint(0);
+ Point aLineEnd = pObject->GetPoint(1);
+ Point aPixel = pWindow->LogicToPixel( aLineStart );
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ rViewData.GetPosFromPixel( aPixel.X(), aPixel.Y(), ePos, nStartCol, nStartRow );
+ aPixel = pWindow->LogicToPixel( aLineEnd );
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ rViewData.GetPosFromPixel( aPixel.X(), aPixel.Y(), ePos, nEndCol, nEndRow );
+ SCCOL nCurX = rViewData.GetCurX();
+ SCROW nCurY = rViewData.GetCurY();
+ bool bStart = ( Diff( rPos,aLineStart ) > Diff( rPos,aLineEnd ) );
+ if ( nCurX == nStartCol && nCurY == nStartRow )
+ bStart = false;
+ else if ( nCurX == nEndCol && nCurY == nEndRow )
+ bStart = true;
+
+ SCCOL nDifX;
+ SCROW nDifY;
+ if ( bStart )
+ {
+ nDifX = nStartCol - nCurX;
+ nDifY = nStartRow - nCurY;
+ }
+ else
+ {
+ nDifX = nEndCol - nCurX;
+ nDifY = nEndRow - nCurY;
+ }
+ rViewShell.MoveCursorRel( nDifX, nDifY, SC_FOLLOW_JUMP, false );
+
+ bFound = true;
+ }
+ }
+
+ pObject = aIter.Next();
+ }
+ return bFound;
+}
+
+bool FuSelection::IsNoteCaptionMarked() const
+{
+ if( pView )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ return ScDrawLayer::IsNoteCaption( pObj );
+ }
+ }
+ return false;
+}
+
+bool FuSelection::IsNoteCaptionClicked( const Point& rPos ) const
+{
+ SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;
+ if( pPageView )
+ {
+ const ScViewData& rViewData = rViewShell.GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ bool bProtectDoc = rDoc.IsTabProtected( nTab ) || (pDocSh && pDocSh->IsReadOnly());
+
+ // search the last object (on top) in the object list
+ SdrObjListIter aIter( pPageView->GetObjList(), SdrIterMode::DeepNoGroups, true );
+ for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
+ {
+ if( pObj->GetLogicRect().Contains( rPos ) )
+ {
+ if( const ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObj, nTab ) )
+ {
+ const ScAddress& rNotePos = pCaptData->maStart;
+ // skip caption objects of notes in protected cells
+ const ScProtectionAttr* pProtAttr = rDoc.GetAttr( rNotePos.Col(), rNotePos.Row(), nTab, ATTR_PROTECTION );
+ bool bProtectAttr = pProtAttr->GetProtection() || pProtAttr->GetHideCell();
+ if( !bProtectAttr || !bProtectDoc )
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/futext.cxx b/sc/source/ui/drawfunc/futext.cxx
new file mode 100644
index 0000000000..0a26c7693b
--- /dev/null
+++ b/sc/source/ui/drawfunc/futext.cxx
@@ -0,0 +1,688 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svddef.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/sdtagitm.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/editund2.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/eeitem.hxx>
+#include <svl/itemset.hxx>
+#include <osl/diagnose.h>
+
+#include <futext.hxx>
+#include <drwlayer.hxx>
+#include <sc.hrc>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+
+// maximum of mouse movement which allows to start Drag&Drop
+//! fusel,fuconstr,futext - combined!
+#define SC_MAXDRAGMOVE 3
+
+static void lcl_InvalidateAttribs( SfxBindings& rBindings )
+{
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+ rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+ rBindings.Invalidate( SID_ATTR_CHAR_BACK_COLOR );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_10 );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_15 );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_20 );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_HYPERLINK_GETLINK );
+ rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
+ rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ rBindings.Invalidate( SID_TABLE_VERT_NONE );
+ rBindings.Invalidate( SID_TABLE_VERT_CENTER );
+ rBindings.Invalidate( SID_TABLE_VERT_BOTTOM );
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+}
+
+static void lcl_UpdateHyphenator( Outliner& rOutliner, const SdrObject* pObj )
+{
+ // use hyphenator only if hyphenation attribute is set
+ if ( pObj && pObj->GetMergedItem(EE_PARA_HYPHENATE).GetValue() ) {
+ css::uno::Reference<css::linguistic2::XHyphenator> xHyphenator( LinguMgr::GetHyphenator() );
+ rOutliner.SetHyphenator( xHyphenator );
+ }
+}
+
+FuText::FuText(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pViewP,
+ SdrModel* pDoc, const SfxRequest& rReq)
+ : FuConstruct(rViewSh, pWin, pViewP, pDoc, rReq)
+{
+}
+
+FuText::~FuText()
+{
+// StopEditMode(); // in Deactivate !
+}
+
+bool FuText::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+ bool bStraightEnter = true;
+
+ if ( pView->MouseButtonDown(rMEvt, pWindow->GetOutDev()) )
+ return true; // event handled from SdrView
+
+ if ( pView->IsTextEdit() )
+ {
+ if ( IsEditingANote() )
+ {
+ if( !IsSizingOrMovingNote(rMEvt) )
+ {
+ StopEditMode(); // Clicked outside, ending edit
+ bStraightEnter = false;
+ }
+ }
+ else
+ {
+ StopEditMode(); // Clicked outside, ending edit
+ pView->UnmarkAll();
+ bStraightEnter = false;
+ }
+ pView->SetCreateMode();
+ }
+
+ aMDPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() );
+
+ if ( rMEvt.IsLeft() )
+ {
+ SdrHdl* pHdl = pView->PickHandle(aMDPos);
+ const size_t nHdlNum = pView->GetHdlNum(pHdl);
+ if (pHdl != nullptr)
+ {
+ if (pView->HasMarkablePoints() && pView->IsPointMarkable(*pHdl))
+ {
+ bool bPointMarked=pView->IsPointMarked(*pHdl);
+
+ if ( rMEvt.IsShift() )
+ {
+ if (!bPointMarked)
+ {
+ pView->MarkPoint(*pHdl);
+ }
+ else
+ {
+ pView->UnmarkPoint(*pHdl);
+ }
+ }
+ else
+ {
+ if (!bPointMarked)
+ {
+ pView->UnmarkAllPoints();
+ pView->MarkPoint(*pHdl);
+ }
+ }
+ pHdl=pView->GetHdl(nHdlNum);
+ }
+ }
+
+ SdrPageView* pPV = nullptr;
+
+ if ( pHdl != nullptr || pView->IsMarkedHit(aMDPos) )
+ {
+ SdrObject* pObj = (pHdl == nullptr) ?
+ pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::PICKTEXTEDIT) :
+ nullptr;
+ if (pObj)
+ {
+ std::unique_ptr<SdrOutliner> pO = MakeOutliner();
+ lcl_UpdateHyphenator( *pO, pObj );
+
+ // vertical flag:
+ // deduced from slot ids only if text object has no content
+ sal_uInt16 nSlotID = aSfxRequest.GetSlot();
+ bool bVertical = ( nSlotID == SID_DRAW_TEXT_VERTICAL );
+ OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject();
+ if ( pOPO )
+ bVertical = pOPO->IsEffectivelyVertical(); // content wins
+ pO->SetVertical( bVertical );
+
+ //!?? the default values are not correct when result is without outliner ???!?
+ auto pOTemp = pO.get();
+ if ( pView->SdrBeginTextEdit(pObj, pPV, pWindow, true, pO.release()) )
+ {
+ // subscribe EditEngine-UndoManager
+ rViewShell.SetDrawTextUndo( &pOTemp->GetUndoManager() );
+
+ OutlinerView* pOLV = pView->GetTextEditOutlinerView();
+ if ( pOLV->MouseButtonDown(rMEvt) )
+ return true; // Event to the Outliner
+ }
+ }
+ else
+ {
+ // disable tail & circular move for caption objects.
+ bool bDrag = false;
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pMarkedObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( ScDrawLayer::IsNoteCaption( pMarkedObj ) )
+ {
+ if(pHdl->GetKind() != SdrHdlKind::Poly && pHdl->GetKind() != SdrHdlKind::Circle)
+ bDrag = true;
+ }
+ else
+ bDrag = true; // different object
+ }
+ else
+ bDrag = true; // several objects
+
+ if ( bDrag )
+ {
+ aDragTimer.Start();
+ pView->BegDragObj(aMDPos, nullptr, pHdl);
+ }
+ }
+ }
+ else
+ {
+ if (pView->IsEditMode())
+ {
+ bool bPointMode=pView->HasMarkablePoints();
+
+ if (!rMEvt.IsShift())
+ {
+ if (bPointMode)
+ {
+ pView->UnmarkAllPoints();
+ }
+ else
+ {
+ pView->UnmarkAll();
+ }
+
+ pView->SetDragMode(SdrDragMode::Move);
+ SfxBindings& rBindings = rViewShell.GetViewFrame().GetBindings();
+ rBindings.Invalidate( SID_OBJECT_ROTATE );
+ rBindings.Invalidate( SID_OBJECT_MIRROR );
+ }
+
+ if ( pView->MarkObj(aMDPos, -2, false, rMEvt.IsMod1()) )
+ {
+ aDragTimer.Start();
+
+ pHdl=pView->PickHandle(aMDPos);
+
+ if (pHdl!=nullptr)
+ {
+ pView->MarkPoint(*pHdl);
+ pHdl=pView->GetHdl(nHdlNum);
+ }
+
+ pView->BegDragObj(aMDPos, nullptr, pHdl);
+ }
+ else
+ {
+ if (bPointMode)
+ {
+ pView->BegMarkPoints(aMDPos);
+ }
+ else
+ {
+ pView->BegMarkObj(aMDPos);
+ }
+ }
+ }
+ else if (aSfxRequest.GetSlot() == SID_DRAW_NOTEEDIT )
+ {
+ // Edit notes -> create no new text objects
+ // and leave text mode
+ rViewShell.GetViewData().GetDispatcher().
+ Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ }
+ else
+ {
+ if (bStraightEnter)//Hack for that silly idea that creating text fields is inside the text routine
+ {
+ // create object
+ pView->BegCreateObj(aMDPos);
+ }
+ else if (SdrObject* pObj = pView->PickObj(aMDPos, pView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK))
+ {
+ pView->UnmarkAllObj();
+ ScViewData& rViewData = rViewShell.GetViewData();
+ rViewData.GetDispatcher().Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ pView->MarkObj(pObj,pPV);
+
+ pHdl=pView->PickHandle(aMDPos);
+ pView->BegDragObj(aMDPos, nullptr, pHdl);
+ return true;
+ }
+ }
+ }
+ }
+
+ if (!bIsInDragMode)
+ {
+ pWindow->CaptureMouse();
+// ForcePointer(&rMEvt);
+ lcl_InvalidateAttribs( rViewShell.GetViewFrame().GetBindings() );
+ }
+
+ rViewShell.SetActivePointer(pView->GetPreferredPointer(
+ pWindow->PixelToLogic(rMEvt.GetPosPixel()), pWindow->GetOutDev() ));
+ if (!bStraightEnter)
+ {
+ pView->UnmarkAll();
+ ScViewData& rViewData = rViewShell.GetViewData();
+ rViewData.GetDispatcher().Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ }
+
+ return true;
+}
+
+bool FuText::MouseMove(const MouseEvent& rMEvt)
+{
+ rViewShell.SetActivePointer(pView->GetPreferredPointer(
+ pWindow->PixelToLogic(rMEvt.GetPosPixel()), pWindow->GetOutDev() ));
+
+ if (aDragTimer.IsActive() )
+ {
+ Point aOldPixel = pWindow->LogicToPixel( aMDPos );
+ Point aNewPixel = rMEvt.GetPosPixel();
+ if ( std::abs( aOldPixel.X() - aNewPixel.X() ) > SC_MAXDRAGMOVE ||
+ std::abs( aOldPixel.Y() - aNewPixel.Y() ) > SC_MAXDRAGMOVE )
+ aDragTimer.Stop();
+ }
+
+ Point aPix(rMEvt.GetPosPixel());
+ Point aPnt(pWindow->PixelToLogic(aPix));
+
+ if ( pView->MouseMove(rMEvt, pWindow->GetOutDev()) )
+ return true; // event handled from SdrView
+
+ if ( pView->IsAction() )
+ {
+ ForceScroll(aPix);
+ pView->MovAction(aPnt);
+ }
+
+ return false;
+}
+
+bool FuText::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ // remember button state for creation of own MouseEvents
+ SetMouseButtonCode(rMEvt.GetButtons());
+
+ if (aDragTimer.IsActive() )
+ {
+ aDragTimer.Stop();
+ }
+
+ lcl_InvalidateAttribs( rViewShell.GetViewFrame().GetBindings() );
+
+ Point aPnt( pWindow->PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ if ( pView->MouseButtonUp(rMEvt, pWindow->GetOutDev()) )
+ return true; // Event evaluated by SdrView
+
+ if ( pView->IsDragObj() )
+ {
+ pView->EndDragObj( rMEvt.IsShift() );
+ pView->ForceMarkedToAnotherPage();
+ }
+ else if ( pView->IsCreateObj() )
+ {
+ if (rMEvt.IsLeft())
+ {
+ pView->EndCreateObj(SdrCreateCmd::ForceEnd);
+ if (aSfxRequest.GetSlot() == SID_DRAW_TEXT_MARQUEE)
+ {
+ // create marquee-object?
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMark(0))
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ // set needed attributes for scrolling
+ SfxItemSetFixed<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST> aItemSet( pDrDoc->GetItemPool());
+
+ aItemSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
+ aItemSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
+ aItemSet.Put( SdrTextAniKindItem( SdrTextAniKind::Slide ) );
+ aItemSet.Put( SdrTextAniDirectionItem( SdrTextAniDirection::Left ) );
+ aItemSet.Put( SdrTextAniCountItem( 1 ) );
+ aItemSet.Put( SdrTextAniAmountItem(
+ static_cast<sal_Int16>(pWindow->PixelToLogic(Size(2,1)).Width())) );
+ pObj->SetMergedItemSetAndBroadcast(aItemSet);
+ }
+ }
+
+ // init object different when vertical writing
+ sal_uInt16 nSlotID(aSfxRequest.GetSlot());
+ bool bVertical = (SID_DRAW_TEXT_VERTICAL == nSlotID);
+ if(bVertical)
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if(rMarkList.GetMark(0))
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if(auto pText = DynCastSdrTextObj( pObj))
+ {
+ SfxItemSet aSet(pDrDoc->GetItemPool());
+
+ pText->SetVerticalWriting(true);
+
+ aSet.Put(makeSdrTextAutoGrowWidthItem(true));
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP));
+ aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT));
+
+ pText->SetMergedItemSet(aSet);
+ }
+ }
+ }
+
+ SetInEditMode();
+
+ // leave mode when sole click (-> fuconstr)
+ if ( !pView->AreObjectsMarked() )
+ {
+ pView->MarkObj(aPnt, -2, false, rMEvt.IsMod1());
+
+ SfxDispatcher& rDisp = rViewShell.GetViewData().GetDispatcher();
+ if ( pView->AreObjectsMarked() )
+ rDisp.Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ else
+ rDisp.Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ }
+ }
+ }
+ else if ( pView->IsAction() )
+ {
+ pView->EndAction();
+ }
+ else if( !pView->IsAction() )
+ {
+ pWindow->ReleaseMouse();
+
+ if ( !pView->AreObjectsMarked() && rMEvt.GetClicks() < 2 )
+ {
+ pView->MarkObj(aPnt, -2, false, rMEvt.IsMod1());
+
+ SfxDispatcher& rDisp = rViewShell.GetViewData().GetDispatcher();
+ if ( pView->AreObjectsMarked() )
+ rDisp.Execute(SID_OBJECT_SELECT, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ else
+ rDisp.Execute(aSfxRequest.GetSlot(), SfxCallMode::SLOT | SfxCallMode::RECORD);
+ }
+ }
+
+ return false;
+}
+
+// switch mouse-pointer
+void FuText::ForcePointer(const MouseEvent* /* pMEvt */)
+{
+ rViewShell.SetActivePointer( aNewPointer );
+}
+
+// modify keyboard events
+// if a KeyEvent is being processed, then the return value is sal_True, else FALSE.
+bool FuText::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bReturn = false;
+
+ if ( pView->KeyInput(rKEvt, pWindow) )
+ {
+ bReturn = true;
+ lcl_InvalidateAttribs( rViewShell.GetViewFrame().GetBindings() );
+ }
+ else
+ {
+ bReturn = FuDraw::KeyInput(rKEvt);
+ }
+
+ return bReturn;
+}
+
+void FuText::Activate()
+{
+ pView->SetDragMode(SdrDragMode::Move);
+ SfxBindings& rBindings = rViewShell.GetViewFrame().GetBindings();
+ rBindings.Invalidate( SID_OBJECT_ROTATE );
+ rBindings.Invalidate( SID_OBJECT_MIRROR );
+
+// instant set the edit mode
+// SetInEditMode();
+
+// if (!pTextObj)
+ {
+ // no text object in EditMode, therefore set CreateMode
+
+ pView->SetCurrentObj(SdrObjKind::Text);
+
+ pView->SetCreateMode();
+ }
+
+ aNewPointer = PointerStyle::Text;
+
+ aOldPointer = pWindow->GetPointer();
+ rViewShell.SetActivePointer( aNewPointer );
+
+ FuConstruct::Activate();
+}
+
+void FuText::Deactivate()
+{
+ FuConstruct::Deactivate();
+ rViewShell.SetActivePointer( aOldPointer );
+ StopEditMode();
+}
+
+// switch object to Edit-Mode
+void FuText::SetInEditMode(SdrObject* pObj, const Point* pMousePixel,
+ bool bCursorToEnd, const KeyEvent* pInitialKey)
+{
+ /* It is possible to pass a special (unselected) object in pObj, e.g. the
+ caption object of a cell note. If pObj is 0, then the selected object
+ is used. The layer will be relocked in FuText::StopEditMode(). */
+ if ( pObj && (pObj->GetLayer() == SC_LAYER_INTERN) )
+ pView->UnlockInternalLayer();
+
+ if ( !pObj && pView->AreObjectsMarked() )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ pObj = pMark->GetMarkedSdrObj();
+ }
+ }
+
+ if ( !pObj )
+ return;
+
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+
+ if (!(nSdrObjKind == SdrObjKind::Text ||
+ nSdrObjKind == SdrObjKind::TitleText ||
+ nSdrObjKind == SdrObjKind::OutlineText ||
+ DynCastSdrTextObj( pObj) != nullptr))
+ return;
+
+ SdrPageView* pPV = pView->GetSdrPageView();
+
+ if ( !pObj->HasTextEdit() )
+ return;
+
+ std::unique_ptr<SdrOutliner> pO = MakeOutliner();
+ lcl_UpdateHyphenator( *pO, pObj );
+
+ // vertical flag:
+ // deduced from slot ids only if text object has no content
+
+ sal_uInt16 nSlotID = aSfxRequest.GetSlot();
+ bool bVertical = ( nSlotID == SID_DRAW_TEXT_VERTICAL );
+ OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject();
+ if ( pOPO )
+ bVertical = pOPO->IsEffectivelyVertical(); // content wins
+ pO->SetVertical( bVertical );
+
+ //!?? without returned Outliner the defaults are not correct ???!?
+ auto pOTemp = pO.get();
+ if ( !pView->SdrBeginTextEdit(pObj, pPV, pWindow, true, pO.release()) )
+ return;
+
+ // Toggle out of paste mode if we are in it, otherwise
+ // pressing return in this object will instead go to the
+ // sheet and be considered an overwrite-cell instruction
+ rViewShell.GetViewData().SetPasteMode(ScPasteFlags::NONE);
+ rViewShell.UpdateCopySourceOverlay();
+
+ // EditEngine-UndoManager anmelden
+ rViewShell.SetDrawTextUndo( &pOTemp->GetUndoManager() );
+
+ pView->SetEditMode();
+
+ // set text cursor to click position or to end,
+ // pass initial key event to outliner view
+ if ( !(pMousePixel || bCursorToEnd || pInitialKey) )
+ return;
+
+ OutlinerView* pOLV = pView->GetTextEditOutlinerView();
+ if (!pOLV)
+ return;
+
+ if ( pMousePixel )
+ {
+ MouseEvent aEditEvt( *pMousePixel, 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 );
+ pOLV->MouseButtonDown(aEditEvt);
+ pOLV->MouseButtonUp(aEditEvt);
+ }
+ else if ( bCursorToEnd )
+ {
+ ESelection aNewSelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
+ pOLV->SetSelection(aNewSelection);
+ }
+
+ if ( pInitialKey )
+ pOLV->PostKeyEvent( *pInitialKey );
+}
+
+// Create default drawing objects via keyboard
+rtl::Reference<SdrObject> FuText::CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle)
+{
+ // case SID_DRAW_TEXT:
+ // case SID_DRAW_TEXT_VERTICAL:
+ // case SID_DRAW_TEXT_MARQUEE:
+ // case SID_DRAW_NOTEEDIT:
+
+ rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
+ *pDrDoc,
+ pView->GetCurrentObjInventor(),
+ pView->GetCurrentObjIdentifier()));
+
+ if(pObj)
+ {
+ if(auto pText = DynCastSdrTextObj( pObj.get() ))
+ {
+ pText->SetLogicRect(rRectangle);
+
+ // don't set default text, start edit mode instead
+ // String aText(ScResId(STR_CAPTION_DEFAULT_TEXT));
+ // pText->SetText(aText);
+
+ bool bVertical = (SID_DRAW_TEXT_VERTICAL == nID);
+ bool bMarquee = (SID_DRAW_TEXT_MARQUEE == nID);
+
+ pText->SetVerticalWriting(bVertical);
+
+ if(bVertical)
+ {
+ SfxItemSet aSet(pDrDoc->GetItemPool());
+
+ aSet.Put(makeSdrTextAutoGrowWidthItem(true));
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP));
+ aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT));
+
+ pText->SetMergedItemSet(aSet);
+ }
+
+ if(bMarquee)
+ {
+ SfxItemSetFixed<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST> aSet(pDrDoc->GetItemPool());
+
+ aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
+ aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
+ aSet.Put( SdrTextAniKindItem( SdrTextAniKind::Slide ) );
+ aSet.Put( SdrTextAniDirectionItem( SdrTextAniDirection::Left ) );
+ aSet.Put( SdrTextAniCountItem( 1 ) );
+ aSet.Put( SdrTextAniAmountItem( static_cast<sal_Int16>(pWindow->PixelToLogic(Size(2,1)).Width())) );
+
+ pObj->SetMergedItemSetAndBroadcast(aSet);
+ }
+
+ SetInEditMode( pObj.get() ); // start edit mode
+ }
+ else
+ {
+ OSL_FAIL("Object is NO text object");
+ }
+ }
+
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/futext2.cxx b/sc/source/ui/drawfunc/futext2.cxx
new file mode 100644
index 0000000000..8f30cb4a6f
--- /dev/null
+++ b/sc/source/ui/drawfunc/futext2.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdetc.hxx>
+
+#include <futext.hxx>
+#include <tabvwsh.hxx>
+
+std::unique_ptr<SdrOutliner> FuText::MakeOutliner()
+{
+ ScViewData& rViewData = rViewShell.GetViewData();
+ std::unique_ptr<SdrOutliner> pOutl = SdrMakeOutliner(OutlinerMode::OutlineObject, *pDrDoc);
+
+ rViewData.UpdateOutlinerFlags(*pOutl);
+
+ // The EditEngine uses during RTF export (Clipboard / Drag&Drop)
+ // the MapMode of RefDevice to set the font size
+
+ // #i10426# The ref device isn't set to the EditEngine before SdrBeginTextEdit now,
+ // so the device must be taken from the model here.
+ OutputDevice* pRef = pDrDoc->GetRefDevice();
+ if (pRef && pRef != pWindow->GetOutDev())
+ pRef->SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ return pOutl;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/futext3.cxx b/sc/source/ui/drawfunc/futext3.cxx
new file mode 100644
index 0000000000..c7e59c5bea
--- /dev/null
+++ b/sc/source/ui/drawfunc/futext3.cxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdocapt.hxx>
+#include <svx/svdundo.hxx>
+#include <vcl/cursor.hxx>
+#include <osl/diagnose.h>
+
+#include <global.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <futext.hxx>
+#include <docsh.hxx>
+#include <postit.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <drawview.hxx>
+#include <undocell.hxx>
+
+// Editing of Note-Key-Objects has to be stopped always via StopEditMode,
+// so that changes are taken over into the document!
+// (Fontwork-Execute in drawsh and drtxtob does not happen for Key-Objects)
+
+void FuText::StopEditMode()
+{
+ SdrObject* pObject = pView->GetTextEditObject();
+ if( !pObject ) return;
+
+ // relock the internal layer that has been unlocked in FuText::SetInEditMode()
+ if ( pObject->GetLayer() == SC_LAYER_INTERN )
+ pView->LockInternalLayer();
+
+ ScViewData& rViewData = rViewShell.GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ OSL_ENSURE( pDrawLayer && (pDrawLayer == pDrDoc), "FuText::StopEditMode - missing or different drawing layers" );
+
+ ScAddress aNotePos;
+ ScPostIt* pNote = nullptr;
+ if( const ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObject, rViewData.GetTabNo() ) )
+ {
+ aNotePos = pCaptData->maStart;
+ pNote = rDoc.GetNote( aNotePos );
+ OSL_ENSURE( pNote && (pNote->GetCaption() == pObject), "FuText::StopEditMode - missing or invalid cell note" );
+ }
+
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ SfxUndoManager* pUndoMgr = rDoc.IsUndoEnabled() ? pDocShell->GetUndoManager() : nullptr;
+ if (pUndoMgr && !pUndoMgr->GetMaxUndoActionCount()) // tdf#134308 if max undo is 0, treat as if no undo
+ pUndoMgr = nullptr;
+ bool bNewNote = false;
+ if( pNote && pUndoMgr )
+ {
+ /* Put all undo actions already collected (e.g. create caption object)
+ and all following undo actions (text changed) together into a ListAction. */
+ std::unique_ptr<SdrUndoGroup> pCalcUndo = pDrawLayer->GetCalcUndo();
+
+ if(pCalcUndo)
+ {
+ const OUString aUndoStr = ScResId( STR_UNDO_EDITNOTE );
+ pUndoMgr->EnterListAction( aUndoStr, aUndoStr, 0, rViewShell.GetViewShellId() );
+
+ /* Note has been created before editing, if first undo action is
+ an insert action. Needed below to decide whether to drop the
+ undo if editing a new note has been cancelled. */
+ bNewNote = (pCalcUndo->GetActionCount() > 0) && dynamic_cast< SdrUndoNewObj* >(pCalcUndo->GetAction( 0 ));
+
+ // create a "insert note" undo action if needed
+ if( bNewNote )
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( *pDocShell, aNotePos, pNote->GetNoteData(), true, std::move(pCalcUndo) ) );
+ else
+ pUndoMgr->AddUndoAction( std::move(pCalcUndo) );
+ }
+ }
+
+ if( pNote )
+ rDoc.LockStreamValid(true); // only the affected sheet is invalidated below
+
+ /* Unset the outliner undo manager before the call to SdrEndTextEdit.
+ SdrObjEditView::SdrEndTextEdit destroys it, but then ScDrawView::SdrEndTextEdit
+ initiates some UI update which might try to access the now invalid pointer. */
+ rViewShell.SetDrawTextUndo( nullptr );
+
+ /* SdrObjEditView::SdrEndTextEdit() may try to delete the entire drawing
+ object, if it does not contain text and has invisible border and fill.
+ This must not happen for note caption objects. They will be removed
+ below together with the cell note if the text is empty (independent of
+ border and area formatting). It is possible to prevent automatic
+ deletion by passing sal_True to this function. The return value changes
+ from SdrEndTextEditKind::Deleted to SdrEndTextEditKind::ShouldBeDeleted in this
+ case. */
+ /*SdrEndTextEditKind eResult =*/ pView->SdrEndTextEdit( pNote != nullptr );
+
+ vcl::Cursor* pCur = pWindow->GetCursor();
+ if( pCur && pCur->IsVisible() )
+ pCur->Hide();
+
+ if( !pNote )
+ return;
+
+ ScTabView::OnLOKNoteStateChanged( pNote );
+
+ // hide the caption object if it is in hidden state
+ pNote->ShowCaptionTemp( aNotePos, false );
+
+ // update author and date
+ pNote->AutoStamp();
+
+ /* If the entire text has been cleared, the cell note and its caption
+ object have to be removed. */
+ SdrTextObj* pTextObject = DynCastSdrTextObj( pObject );
+ bool bDeleteNote = !pTextObject || !pTextObject->HasText();
+ if( bDeleteNote )
+ {
+ if( pUndoMgr )
+ {
+ // collect the "remove object" drawing undo action created by DeleteNote()
+ pDrawLayer->BeginCalcUndo(false);
+ // rescue note data before deletion
+ ScNoteData aNoteData( pNote->GetNoteData() );
+ // delete note from document (removes caption, but does not delete it)
+ rDoc.ReleaseNote(aNotePos);
+ // create undo action for removed note
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( *pDocShell, aNotePos, aNoteData, false, pDrawLayer->GetCalcUndo() ) );
+ }
+ else
+ {
+ rDoc.ReleaseNote(aNotePos);
+ }
+ // ScDocument::DeleteNote has deleted the note that pNote points to
+ pNote = nullptr;
+ }
+
+ // finalize the undo list action
+ if( pUndoMgr )
+ {
+ pUndoMgr->LeaveListAction();
+
+ /* #i94039# Update the default name "Edit Note" of the undo action
+ if the note has been created before editing or is deleted due
+ to deleted text. If the note has been created *and* is deleted,
+ the last undo action can be removed completely. Note: The
+ function LeaveListAction() removes the last action by itself,
+ if it is empty (when result is SdrEndTextEditKind::Unchanged). */
+ if( bNewNote && bDeleteNote )
+ {
+ pUndoMgr->RemoveLastUndoAction();
+
+ // Make sure the former area of the note anchor is invalidated.
+ ScRangeList aRangeList(aNotePos);
+ ScMarkData aMarkData(rDoc.GetSheetLimits(), aRangeList);
+ rViewShell.UpdateSelectionArea(aMarkData);
+ }
+ else if( bNewNote || bDeleteNote )
+ {
+ SfxListUndoAction* pAction = dynamic_cast< SfxListUndoAction* >( pUndoMgr->GetUndoAction() );
+ OSL_ENSURE( pAction, "FuText::StopEditMode - list undo action expected" );
+ if( pAction )
+ pAction->SetComment( ScResId( bNewNote ? STR_UNDO_INSERTNOTE : STR_UNDO_DELETENOTE ) );
+ }
+ }
+
+ // invalidate stream positions only for the affected sheet
+ rDoc.LockStreamValid(false);
+ rDoc.SetStreamValid(aNotePos.Tab(), false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/graphsh.cxx b/sc/source/ui/drawfunc/graphsh.cxx
new file mode 100644
index 0000000000..9252dffa76
--- /dev/null
+++ b/sc/source/ui/drawfunc/graphsh.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 <sfx2/objface.hxx>
+#include <vcl/EnumContext.hxx>
+#include <sfx2/opengrf.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/grfflt.hxx>
+#include <svx/grafctrl.hxx>
+#include <svx/compressgraphicdialog.hxx>
+#include <svx/graphichelper.hxx>
+#include <svx/svxids.hrc>
+
+#include <graphsh.hxx>
+#include <strings.hrc>
+#include <viewdata.hxx>
+#include <drawview.hxx>
+#include <gridwin.hxx>
+#include <scresid.hxx>
+#include <svx/extedit.hxx>
+
+#define ShellClass_ScGraphicShell
+#include <scslots.hxx>
+
+SFX_IMPL_INTERFACE(ScGraphicShell, ScDrawShell)
+
+void ScGraphicShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Graphic_Objectbar);
+
+ GetStaticInterface()->RegisterPopupMenu("graphic");
+}
+
+
+ScGraphicShell::ScGraphicShell(ScViewData& rData) :
+ ScDrawShell(rData)
+{
+ SetName("GraphicObject");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Graphic));
+}
+
+ScGraphicShell::~ScGraphicShell()
+{
+}
+
+void ScGraphicShell::GetAttrState( SfxItemSet& rSet )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+
+ if( pView )
+ SvxGrafAttrHelper::GetGrafAttrState( rSet, *pView );
+}
+
+void ScGraphicShell::Execute( SfxRequest& rReq )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+
+ if( pView )
+ {
+ SvxGrafAttrHelper::ExecuteGrafAttr( rReq, *pView );
+ Invalidate();
+ }
+}
+
+void ScGraphicShell::GetFilterState( SfxItemSet& rSet )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ bEnable = true;
+ }
+
+ if( !bEnable )
+ SvxGraphicFilter::DisableGraphicFilterSlots( rSet );
+}
+
+void ScGraphicShell::ExecuteFilter( const SfxRequest& rReq )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ {
+ GraphicObject aFilterObj( pGraphicObj->GetGraphicObject() );
+
+ if( SvxGraphicFilterResult::NONE ==
+ SvxGraphicFilter::ExecuteGrfFilterSlot( rReq, aFilterObj ) )
+ {
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if( pPageView )
+ {
+ rtl::Reference<SdrGrafObj> pFilteredObj = SdrObject::Clone(*pGraphicObj, pGraphicObj->getSdrModelFromSdrObject());
+ OUString aStr = pView->GetDescriptionOfMarkedObjects() + " " + ScResId(SCSTR_UNDO_GRAFFILTER);
+ pView->BegUndo( aStr );
+ pFilteredObj->SetGraphicObject( aFilterObj );
+ pView->ReplaceObjectAtView( pObj, *pPageView, pFilteredObj.get() );
+ pView->EndUndo();
+ }
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGraphicShell::GetExternalEditState( SfxItemSet& rSet )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ bEnable = true;
+ }
+
+ if (GetObjectShell()->isExportLocked())
+ bEnable = false;
+
+ if( !bEnable )
+ rSet.DisableItem( SID_EXTERNAL_EDIT );
+}
+
+void ScGraphicShell::ExecuteExternalEdit( SAL_UNUSED_PARAMETER SfxRequest& )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ {
+ GraphicObject aGraphicObject( pGraphicObj->GetGraphicObject() );
+ m_ExternalEdits.push_back( std::make_unique<SdrExternalToolEdit>(
+ pView, pGraphicObj));
+ m_ExternalEdits.back()->Edit( &aGraphicObject );
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGraphicShell::GetCompressGraphicState( SfxItemSet& rSet )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<const SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ bEnable = true;
+ }
+
+ if( !bEnable )
+ rSet.DisableItem( SID_COMPRESS_GRAPHIC );
+}
+
+void ScGraphicShell::ExecuteCompressGraphic( SAL_UNUSED_PARAMETER SfxRequest& )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ {
+ CompressGraphicsDialog dialog(GetViewData().GetDialogParent(), pGraphicObj, GetViewData().GetBindings());
+ if (dialog.run() == RET_OK)
+ {
+ rtl::Reference<SdrGrafObj> pNewObject = dialog.GetCompressedSdrGrafObj();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+ OUString aUndoString = pView->GetDescriptionOfMarkedObjects() + " Compress";
+ pView->BegUndo( aUndoString );
+ pView->ReplaceObjectAtView( pObj, *pPageView, pNewObject.get() );
+ pView->EndUndo();
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGraphicShell::GetCropGraphicState( SfxItemSet& rSet )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<const SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ bEnable = true;
+ }
+
+ if( !bEnable )
+ rSet.DisableItem( SID_OBJECT_CROP );
+}
+
+void ScGraphicShell::ExecuteCropGraphic( SAL_UNUSED_PARAMETER SfxRequest& )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<const SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ {
+ pView->SetEditMode(SdrViewEditMode::Edit);
+ pView->SetDragMode(SdrDragMode::Crop);
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGraphicShell::ExecuteSaveGraphic( SAL_UNUSED_PARAMETER SfxRequest& /*rReq*/)
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ const SdrGrafObj* pObj = dynamic_cast<const SdrGrafObj*>(rMarkList.GetMark( 0 )->GetMarkedSdrObj());
+ if( pObj && pObj->GetGraphicType() == GraphicType::Bitmap )
+ {
+ GraphicAttr aGraphicAttr = pObj->GetGraphicAttr();
+ short nState = RET_CANCEL;
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ weld::Window* pWinFrame = pWin ? pWin->GetFrameWeld() : nullptr;
+ if (aGraphicAttr != GraphicAttr()) // the image has been modified
+ {
+ if (pWin)
+ {
+ nState = GraphicHelper::HasToSaveTransformedImage(pWinFrame);
+ }
+ }
+ else
+ {
+ nState = RET_NO;
+ }
+
+ if (nState == RET_YES)
+ {
+ GraphicHelper::ExportGraphic(pWinFrame, pObj->GetTransformedGraphic(), "");
+ }
+ else if (nState == RET_NO)
+ {
+ const GraphicObject& aGraphicObject(pObj->GetGraphicObject());
+ GraphicHelper::ExportGraphic(pWinFrame, aGraphicObject.GetGraphic(), "");
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGraphicShell::GetSaveGraphicState(SfxItemSet &rSet)
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<const SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ bEnable = true;
+ }
+
+ if (GetObjectShell()->isExportLocked())
+ bEnable = false;
+
+ if( !bEnable )
+ rSet.DisableItem( SID_SAVE_GRAPHIC );
+}
+
+void ScGraphicShell::ExecuteChangePicture( SAL_UNUSED_PARAMETER SfxRequest& /*rReq*/)
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGraphicObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap )
+ {
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ SvxOpenGraphicDialog aDlg(ScResId(STR_INSERTGRAPHIC), pWin ? pWin->GetFrameWeld() : nullptr);
+
+ if( aDlg.Execute() == ERRCODE_NONE )
+ {
+ Graphic aGraphic;
+ ErrCode nError = aDlg.GetGraphic(aGraphic);
+ if( nError == ERRCODE_NONE )
+ {
+ rtl::Reference<SdrGrafObj> pNewObject(SdrObject::Clone(*pGraphicObj, pGraphicObj->getSdrModelFromSdrObject()));
+ pNewObject->SetGraphic( aGraphic );
+ SdrPageView* pPageView = pView->GetSdrPageView();
+ OUString aUndoString = pView->GetDescriptionOfMarkedObjects() + " Change";
+ pView->BegUndo( aUndoString );
+ pView->ReplaceObjectAtView( pObj, *pPageView, pNewObject.get() );
+ pView->EndUndo();
+ }
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGraphicShell::GetChangePictureState(SfxItemSet &rSet)
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ bool bEnable = false;
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ if( auto pGrafObj = dynamic_cast<const SdrGrafObj*>( pObj) )
+ if( pGrafObj->GetGraphicType() == GraphicType::Bitmap )
+ bEnable = true;
+ }
+
+ if( !bEnable )
+ rSet.DisableItem( SID_CHANGE_PICTURE );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/mediash.cxx b/sc/source/ui/drawfunc/mediash.cxx
new file mode 100644
index 0000000000..f5b3350ba6
--- /dev/null
+++ b/sc/source/ui/drawfunc/mediash.cxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/objface.hxx>
+#include <vcl/EnumContext.hxx>
+#include <svx/MediaShellHelpers.hxx>
+
+#include <mediash.hxx>
+#include <strings.hrc>
+#include <viewdata.hxx>
+#include <drawview.hxx>
+#include <scresid.hxx>
+
+#define ShellClass_ScMediaShell
+#include <scslots.hxx>
+
+using namespace svx;
+
+SFX_IMPL_INTERFACE(ScMediaShell, ScDrawShell)
+
+void ScMediaShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible,
+ ToolbarId::Media_Objectbar);
+
+ GetStaticInterface()->RegisterPopupMenu("media");
+}
+
+ScMediaShell::ScMediaShell(ScViewData& rData)
+ : ScDrawShell(rData)
+{
+ SetName(ScResId(SCSTR_MEDIASHELL));
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Media));
+}
+
+ScMediaShell::~ScMediaShell() {}
+
+void ScMediaShell::GetMediaState(SfxItemSet& rSet)
+{
+ MediaShellHelpers::GetState(GetViewData().GetScDrawView(), rSet);
+}
+
+void ScMediaShell::ExecuteMedia(const SfxRequest& rReq)
+{
+ MediaShellHelpers::Execute(GetViewData().GetScDrawView(), rReq);
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/drawfunc/oleobjsh.cxx b/sc/source/ui/drawfunc/oleobjsh.cxx
new file mode 100644
index 0000000000..0e9f0075d9
--- /dev/null
+++ b/sc/source/ui/drawfunc/oleobjsh.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/objface.hxx>
+
+#include <oleobjsh.hxx>
+#include <vcl/EnumContext.hxx>
+
+#define ShellClass_ScOleObjectShell
+#include <scslots.hxx>
+
+SFX_IMPL_INTERFACE(ScOleObjectShell, ScDrawShell)
+
+void ScOleObjectShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Draw_Objectbar);
+
+ GetStaticInterface()->RegisterPopupMenu("oleobject");
+}
+
+
+ScOleObjectShell::ScOleObjectShell(ScViewData& rData) :
+ ScDrawShell(rData)
+{
+ SetName("OleObject");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::OLE));
+}
+
+ScOleObjectShell::~ScOleObjectShell()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/formdlg/dwfunctr.cxx b/sc/source/ui/formdlg/dwfunctr.cxx
new file mode 100644
index 0000000000..d158a4aada
--- /dev/null
+++ b/sc/source/ui/formdlg/dwfunctr.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 <comphelper/string.hxx>
+#include <editeng/editview.hxx>
+#include <sfx2/viewsh.hxx>
+#include <formula/funcvarargs.h>
+#include <unotools/charclass.hxx>
+
+#include <global.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <tabvwsh.hxx>
+#include <funcdesc.hxx>
+
+#include <dwfunctr.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+/*************************************************************************
+#* Member: ScFunctionWin
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Constructor of ScFunctionWin Class
+#*
+#* Input: Sfx - links, window, resource
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+ScFunctionWin::ScFunctionWin(weld::Widget* pParent)
+ : PanelLayout(pParent, "FunctionPanel", "modules/scalc/ui/functionpanel.ui")
+ , xCatBox(m_xBuilder->weld_combo_box("category"))
+ , xFuncList(m_xBuilder->weld_tree_view("funclist"))
+ , xInsertButton(m_xBuilder->weld_button("insert"))
+ , xFiFuncDesc(m_xBuilder->weld_text_view("funcdesc"))
+ , m_xSearchString(m_xBuilder->weld_entry("search"))
+ , xConfigListener(new comphelper::ConfigurationListener("/org.openoffice.Office.Calc/Formula/Syntax"))
+ , xConfigChange(std::make_unique<EnglishFunctionNameChange>(xConfigListener, this))
+ , pFuncDesc(nullptr)
+{
+ InitLRUList();
+
+ nArgs=0;
+ m_aListHelpId = xFuncList->get_help_id();
+ m_aSearchHelpId = m_xSearchString->get_help_id();
+
+ // Description box has a height of 8 lines of text
+ xFiFuncDesc->set_size_request(-1, 8 * xFiFuncDesc->get_text_height());
+
+ m_xSearchString->connect_changed(LINK(this, ScFunctionWin, ModifyHdl));
+ m_xSearchString->connect_key_press(LINK(this, ScFunctionWin, KeyInputHdl));
+
+ xCatBox->connect_changed(LINK( this, ScFunctionWin, SelComboHdl));
+ xFuncList->connect_changed(LINK( this, ScFunctionWin, SelTreeHdl));
+
+ xFuncList->connect_row_activated(LINK( this, ScFunctionWin, SetRowActivatedHdl));
+ xInsertButton->connect_clicked(LINK( this, ScFunctionWin, SetSelectionClickHdl));
+
+ xCatBox->set_active(0);
+
+ SelComboHdl(*xCatBox);
+}
+
+/*************************************************************************
+#* Member: ScFunctionWin
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Destructor of ScFunctionWin Class
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+ScFunctionWin::~ScFunctionWin()
+{
+ xConfigChange.reset();
+ xConfigListener->dispose();
+ xConfigListener.clear();
+
+ xCatBox.reset();
+ xFuncList.reset();
+ xInsertButton.reset();
+ xFiFuncDesc.reset();
+}
+
+/*************************************************************************
+#* Member: InitLRUList
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Updates the list of functions depending on the set category
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+void ScFunctionWin::InitLRUList()
+{
+ ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
+ pFuncMgr->fillLastRecentlyUsedFunctions(aLRUList);
+
+ sal_Int32 nSelPos = xCatBox->get_active();
+
+ if (nSelPos == 0)
+ UpdateFunctionList("");
+}
+
+/*************************************************************************
+#* Member: UpdateLRUList
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Updates the list of last used functions.
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+void ScFunctionWin::UpdateLRUList()
+{
+ if (pFuncDesc && pFuncDesc->nFIndex!=0)
+ {
+ ScModule* pScMod = SC_MOD();
+ pScMod->InsertEntryToLRUList(pFuncDesc->nFIndex);
+ }
+}
+
+/*************************************************************************
+#* Member: SetDescription
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function:
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+void ScFunctionWin::SetDescription()
+{
+ xFiFuncDesc->set_text(OUString());
+ const ScFuncDesc* pDesc =
+ weld::fromId<const ScFuncDesc*>(xFuncList->get_selected_id());
+ if (pDesc)
+ {
+ pDesc->initArgumentInfo(); // full argument info is needed
+
+ OUString aBuf = xFuncList->get_selected_text() +
+ ":\n\n" +
+ pDesc->GetParamList() +
+ "\n\n" +
+ *pDesc->mxFuncDesc;
+
+ xFiFuncDesc->set_text(aBuf);
+
+ // Update help ID for the selected entry
+ const OUString sHelpId = pDesc->getHelpId();
+ if (!sHelpId.isEmpty())
+ xFuncList->set_help_id(pDesc->getHelpId());
+ else
+ xFuncList->set_help_id(m_aListHelpId);
+ }
+}
+
+/*************************************************************************
+#* Member: UpdateFunctionList
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Updates the list of functions depending on the set category
+#*
+#* Input: Search string used to filter the list of functions
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+void ScFunctionWin::UpdateFunctionList(const OUString& rSearchString)
+{
+ sal_Int32 nSelPos = xCatBox->get_active();
+ sal_Int32 nCategory = ( -1 != nSelPos )
+ ? (nSelPos-1) : 0;
+
+ xFuncList->clear();
+ xFuncList->freeze();
+
+ if ( nSelPos > 0 )
+ {
+ ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
+
+ SvtSysLocale aSysLocale;
+ const CharClass& rCharClass = aSysLocale.GetCharClass();
+ const OUString aSearchStr(rCharClass.uppercase(rSearchString));
+
+ // First add the functions that start with the search string
+ const ScFuncDesc* pDesc = pFuncMgr->First( nCategory );
+ while ( pDesc )
+ {
+ if (rSearchString.isEmpty()
+ || (rCharClass.uppercase(pDesc->getFunctionName()).startsWith(aSearchStr)))
+ xFuncList->append(weld::toId(pDesc), *(pDesc->mxFuncName));
+ pDesc = pFuncMgr->Next();
+ }
+
+ // Now add the functions that have the search string in the middle of the function name
+ // Note that this will only be necessary if the search string is not empty
+ if (!rSearchString.isEmpty())
+ {
+ pDesc = pFuncMgr->First( nCategory );
+ while ( pDesc )
+ {
+ if (rCharClass.uppercase(pDesc->getFunctionName()).indexOf(aSearchStr) > 0)
+ xFuncList->append(weld::toId(pDesc), *(pDesc->mxFuncName));
+ pDesc = pFuncMgr->Next();
+ }
+ }
+ }
+ else // LRU list
+ {
+ for (const formula::IFunctionDescription* pDesc : aLRUList)
+ {
+ if (pDesc)
+ {
+ xFuncList->append(weld::toId(pDesc), pDesc->getFunctionName());
+ }
+ }
+ }
+
+ xFuncList->thaw();
+
+ if (xFuncList->n_children() > 0)
+ {
+ xFuncList->set_sensitive(true);
+ xFuncList->select(0);
+ }
+ else
+ {
+ xFuncList->set_sensitive(false);
+ }
+}
+
+/*************************************************************************
+#* Member: DoEnter
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Save input into document. Is called after clicking the
+#* Apply button or a double-click on the function list.
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+void ScFunctionWin::DoEnter()
+{
+ OUStringBuffer aArgStr;
+ OUString aString=xFuncList->get_selected_text();
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+ nArgs=0;
+
+ if(!aString.isEmpty())
+ {
+ OUString aFirstArgStr;
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pCurSh );
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
+ if(!pScMod->IsEditMode())
+ {
+ rtl::Reference<comphelper::ConfigurationListener> xDetectDisposed(xConfigListener);
+ pScMod->SetInputMode(SC_INPUT_TABLE);
+ // the above call can result in us being disposed
+ if (xDetectDisposed->isDisposed())
+ return;
+ aString = "=" + xFuncList->get_selected_text();
+ if (pHdl)
+ pHdl->ClearText();
+ }
+ const ScFuncDesc* pDesc =
+ weld::fromId<const ScFuncDesc*>(xFuncList->get_selected_id());
+ if (pDesc)
+ {
+ pFuncDesc=pDesc;
+ UpdateLRUList();
+ nArgs = pDesc->nArgCount;
+ if(nArgs>0)
+ {
+ // NOTE: Theoretically the first parameter could have the
+ // suppress flag as well, but practically it doesn't.
+ aFirstArgStr = pDesc->maDefArgNames[0];
+ aFirstArgStr = comphelper::string::strip(aFirstArgStr, ' ');
+ aFirstArgStr = aFirstArgStr.replaceAll(" ", "_");
+ aArgStr = aFirstArgStr;
+ if ( nArgs != VAR_ARGS && nArgs != PAIRED_VAR_ARGS )
+ { // no VarArgs or Fix plus VarArgs, but not VarArgs only
+ sal_uInt16 nFix;
+ if (nArgs >= PAIRED_VAR_ARGS)
+ nFix = nArgs - PAIRED_VAR_ARGS + 2;
+ else if (nArgs >= VAR_ARGS)
+ nFix = nArgs - VAR_ARGS + 1;
+ else
+ nFix = nArgs;
+ for ( sal_uInt16 nArg = 1;
+ nArg < nFix && !pDesc->pDefArgFlags[nArg].bOptional; nArg++ )
+ {
+ aArgStr.append("; ");
+ OUString sTmp = pDesc->maDefArgNames[nArg];
+ sTmp = comphelper::string::strip(sTmp, ' ');
+ sTmp = sTmp.replaceAll(" ", "_");
+ aArgStr.append(sTmp);
+ }
+ }
+ }
+ }
+ if (pHdl)
+ {
+ if (pHdl->GetEditString().isEmpty())
+ {
+ aString = "=" + xFuncList->get_selected_text();
+ }
+ EditView *pEdView=pHdl->GetActiveView();
+ if(pEdView!=nullptr) // @ needed because of crash during setting a name
+ {
+ if(nArgs>0)
+ {
+ pHdl->InsertFunction(aString);
+ pEdView->InsertText(aArgStr.makeStringAndClear(),true);
+ ESelection aESel=pEdView->GetSelection();
+ aESel.nEndPos = aESel.nStartPos + aFirstArgStr.getLength();
+ pEdView->SetSelection(aESel);
+ pHdl->DataChanged();
+ }
+ else
+ {
+ aString += "()";
+ pEdView->InsertText(aString);
+ pHdl->DataChanged();
+ }
+ }
+ }
+ InitLRUList();
+ }
+ if ( pCurSh )
+ {
+ vcl::Window* pShellWnd = pCurSh->GetWindow();
+
+ if ( pShellWnd )
+ pShellWnd->GrabFocus();
+ }
+
+}
+
+/*************************************************************************
+#* Handle: ModifyHdl
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Handles changes in the search text
+#*
+#************************************************************************/
+
+IMPL_LINK_NOARG(ScFunctionWin, ModifyHdl, weld::Entry&, void)
+{
+ // Switch to the "All" category when searching a function
+ xCatBox->set_active(1);
+ OUString searchStr = m_xSearchString->get_text();
+ UpdateFunctionList(searchStr);
+ SetDescription();
+}
+
+/*************************************************************************
+#* Handle: KeyInputHdl
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: Processes key inputs when the serch entry has focus
+#*
+#************************************************************************/
+
+IMPL_LINK(ScFunctionWin, KeyInputHdl, const KeyEvent&, rEvent, bool)
+{
+ bool bHandled = false;
+
+ switch (rEvent.GetKeyCode().GetCode())
+ {
+ case KEY_RETURN:
+ {
+ DoEnter();
+ bHandled = true;
+ }
+ break;
+ case KEY_DOWN:
+ {
+ int nNewIndex = std::min(xFuncList->get_selected_index() + 1, xFuncList->n_children() - 1);
+ xFuncList->select(nNewIndex);
+ SetDescription();
+ bHandled = true;
+ }
+ break;
+ case KEY_UP:
+ {
+ int nNewIndex = std::max(xFuncList->get_selected_index() - 1, 0);
+ xFuncList->select(nNewIndex);
+ SetDescription();
+ bHandled = true;
+ }
+ break;
+ case KEY_ESCAPE:
+ {
+ m_xSearchString->set_text("");
+ UpdateFunctionList("");
+ bHandled = true;
+ }
+ break;
+ case KEY_F1:
+ {
+ const ScFuncDesc* pDesc = weld::fromId<const ScFuncDesc*>(xFuncList->get_selected_id());
+ OUString sHelpId;
+ if (pDesc)
+ sHelpId = pDesc->getHelpId();
+
+ if (!sHelpId.isEmpty())
+ m_xSearchString->set_help_id(sHelpId);
+ else
+ m_xSearchString->set_help_id(m_aSearchHelpId);
+ bHandled = false;
+ }
+ break;
+ }
+
+ return bHandled;
+}
+
+/*************************************************************************
+#* Handle: SelComboHdl
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: A change of the category will update the list of functions.
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+IMPL_LINK_NOARG(ScFunctionWin, SelComboHdl, weld::ComboBox&, void)
+{
+ UpdateFunctionList("");
+ SetDescription();
+ m_xSearchString->set_text("");
+ m_xSearchString->grab_focus();
+}
+
+IMPL_LINK_NOARG(ScFunctionWin, SelTreeHdl, weld::TreeView&, void)
+{
+ SetDescription();
+}
+
+/*************************************************************************
+#* Handle: SetSelectionClickHdl
+#*------------------------------------------------------------------------
+#*
+#* Class: ScFunctionWin
+#*
+#* Function: A change of the category will update the list of functions.
+#*
+#* Input: ---
+#*
+#* Output: ---
+#*
+#************************************************************************/
+
+IMPL_LINK_NOARG( ScFunctionWin, SetSelectionClickHdl, weld::Button&, void )
+{
+ DoEnter(); // saves the input
+}
+
+IMPL_LINK_NOARG( ScFunctionWin, SetRowActivatedHdl, weld::TreeView&, bool )
+{
+ DoEnter(); // saves the input
+ return true;
+}
+
+void EnglishFunctionNameChange::setProperty(const css::uno::Any &rProperty)
+{
+ ConfigurationListenerProperty::setProperty(rProperty);
+ m_pFunctionWin->InitLRUList();
+ m_pFunctionWin->UpdateFunctionList("");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/formdlg/formdata.cxx b/sc/source/ui/formdlg/formdata.cxx
new file mode 100644
index 0000000000..aabce7653b
--- /dev/null
+++ b/sc/source/ui/formdlg/formdata.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 <formdata.hxx>
+
+ScFormEditData::ScFormEditData()
+ : pInputHandler(nullptr)
+ , pScDocShell(nullptr)
+{
+ Reset();
+}
+
+ScFormEditData::~ScFormEditData() {}
+
+void ScFormEditData::SaveValues() { Reset(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/formdlg/formula.cxx b/sc/source/ui/formdlg/formula.cxx
new file mode 100644
index 0000000000..c75091bde2
--- /dev/null
+++ b/sc/source/ui/formdlg/formula.cxx
@@ -0,0 +1,691 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <scitems.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/formulahelper.hxx>
+#include <formula/IFunctionDescription.hxx>
+#include <formula/errorcodes.hxx>
+
+#include <compiler.hxx>
+#include <formula.hxx>
+#include <formdata.hxx>
+#include <reffact.hxx>
+#include <document.hxx>
+#include <simpleformulacalc.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <funcdesc.hxx>
+#include <tokenarray.hxx>
+#include <sc.hrc>
+#include <servuno.hxx>
+#include <unonames.hxx>
+#include <externalrefmgr.hxx>
+
+#include <com/sun/star/table/CellAddress.hpp>
+#include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp>
+#include <com/sun/star/sheet/XFormulaParser.hpp>
+
+using namespace formula;
+using namespace com::sun::star;
+
+// init/ shared functions for dialog
+
+ScFormulaDlg::ScFormulaDlg(SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, const ScViewData& rViewData, const formula::IFunctionManager* _pFunctionMgr)
+ : formula::FormulaDlg(pB, pCW, pParent, _pFunctionMgr, this)
+ , m_aHelper(this,pB)
+ , m_pViewShell( nullptr )
+{
+ m_aHelper.SetDialog(m_xDialog.get());
+ ScModule* pScMod = SC_MOD();
+ pScMod->InputEnterHandler();
+ m_pViewShell = nullptr;
+
+ // title has to be from the view that opened the dialog,
+ // even if it's not the current view
+
+ if ( pB )
+ {
+ SfxDispatcher* pMyDisp = pB->GetDispatcher();
+ if (pMyDisp)
+ {
+ SfxViewFrame* pMyViewFrm = pMyDisp->GetFrame();
+ if (pMyViewFrm)
+ {
+ m_pViewShell = dynamic_cast<ScTabViewShell*>( pMyViewFrm->GetViewShell() );
+ if( m_pViewShell )
+ m_pViewShell->UpdateInputHandler(true);
+ }
+ }
+ }
+
+ m_pDoc = &rViewData.GetDocument();
+ m_xParser.set(ScServiceProvider::MakeInstance(ScServiceProvider::Type::FORMULAPARS,
+ m_pDoc->GetDocumentShell()), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySet> xSet(m_xParser,uno::UNO_QUERY);
+ xSet->setPropertyValue(SC_UNO_COMPILEFAP, uno::Any(true));
+
+ m_xOpCodeMapper.set(ScServiceProvider::MakeInstance(ScServiceProvider::Type::OPCODEMAPPER,
+ m_pDoc->GetDocumentShell()), uno::UNO_QUERY);
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(m_pViewShell);
+
+ assert(pInputHdl && "Missing input handler :-/");
+
+ pInputHdl->NotifyChange( nullptr );
+
+ ScFormulaReferenceHelper::enableInput( true );
+ ScFormulaReferenceHelper::EnableSpreadsheets();
+ m_aHelper.Init();
+ m_aHelper.SetDispatcherLock( true );
+
+ notifyChange();
+ fill();
+
+ ScFormEditData* pData = m_pViewShell->GetFormEditData();
+ if (pData)
+ return;
+
+ pScMod->SetRefInputHdl(pInputHdl);
+
+ m_pDoc = &rViewData.GetDocument();
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ m_CursorPos = ScAddress( nCol, nRow, nTab );
+
+ m_pViewShell->InitFormEditData(); // create new
+ pData = m_pViewShell->GetFormEditData();
+ pData->SetInputHandler(pInputHdl);
+ pData->SetDocShell(rViewData.GetDocShell());
+
+ OSL_ENSURE(pData,"FormEditData not available");
+
+ formula::FormulaDlgMode eMode = FormulaDlgMode::Formula; // default...
+
+ // edit if formula exists
+
+ OUString aFormula = m_pDoc->GetFormula( nCol, nRow, nTab );
+ bool bEdit = ( aFormula.getLength() > 1 );
+ bool bMatrix = false;
+ if ( bEdit )
+ {
+ bMatrix = CheckMatrix(aFormula);
+
+ sal_Int32 nFStart = 0;
+ sal_Int32 nFEnd = 0;
+ if ( GetFormulaHelper().GetNextFunc( aFormula, false, nFStart, &nFEnd) )
+ {
+ pInputHdl->InputReplaceSelection( aFormula );
+ pInputHdl->InputSetSelection( nFStart, nFEnd );
+ sal_Int32 PrivStart, PrivEnd;
+ pInputHdl->InputGetSelection( PrivStart, PrivEnd);
+
+ eMode = SetMeText(pInputHdl->GetFormString(),PrivStart, PrivEnd, bMatrix, true, true);
+ pData->SetFStart( nFStart );
+ }
+ else
+ bEdit = false;
+ }
+
+ if ( !bEdit )
+ {
+ OUString aNewFormula('=');
+ if ( aFormula.startsWith("=") )
+ aNewFormula = aFormula;
+
+ pInputHdl->InputReplaceSelection( aNewFormula );
+ pInputHdl->InputSetSelection( 1, aNewFormula.getLength()+1 );
+ sal_Int32 PrivStart, PrivEnd;
+ pInputHdl->InputGetSelection( PrivStart, PrivEnd);
+ SetMeText(pInputHdl->GetFormString(),PrivStart, PrivEnd,bMatrix,false,false);
+
+ pData->SetFStart( 1 ); // after "="
+ }
+
+ pData->SetMode( eMode );
+ OUString rStrExp = GetMeText();
+
+ Update(rStrExp);
+
+}
+
+void ScFormulaDlg::notifyChange()
+{
+ ScInputHandler* pInputHdl = m_pViewShell->GetInputHandler();
+ if ( pInputHdl )
+ pInputHdl->NotifyChange( nullptr );
+}
+
+void ScFormulaDlg::fill()
+{
+ ScModule* pScMod = SC_MOD();
+ ScFormEditData* pData = static_cast<ScFormEditData*>(getFormEditData());
+ notifyChange();
+ OUString rStrExp;
+ if (!pData)
+ return;
+
+ // data exists -> restore state (after switch)
+ // don't reinitialise m_pDoc and m_CursorPos
+ //pDoc = rViewData.GetDocument();
+ if(IsInputHdl(pData->GetInputHandler()))
+ {
+ pScMod->SetRefInputHdl(pData->GetInputHandler());
+ }
+ else
+ {
+ ScTabViewShell* pTabViewShell;
+ ScInputHandler* pInputHdl = GetNextInputHandler(pData->GetDocShell(),&pTabViewShell);
+
+ if ( pInputHdl == nullptr ) //no more InputHandler for DocShell
+ {
+ disableOk();
+ pInputHdl = pScMod->GetInputHdl();
+ }
+ else
+ {
+ pInputHdl->SetRefViewShell(pTabViewShell);
+ }
+ pScMod->SetRefInputHdl(pInputHdl);
+ pData->SetInputHandler(pInputHdl);
+ }
+
+ OUString aOldFormulaTmp(pData->GetInputHandler()->GetFormString());
+ pData->GetInputHandler()->InputSetSelection( 0, aOldFormulaTmp.getLength());
+
+ rStrExp=pData->GetUndoStr();
+ pData->GetInputHandler()->InputReplaceSelection(rStrExp);
+
+ SetMeText(rStrExp);
+
+ Update();
+ // switch back, maybe new Doc has been opened
+ pScMod->SetRefInputHdl(nullptr);
+}
+
+ScFormulaDlg::~ScFormulaDlg() COVERITY_NOEXCEPT_FALSE
+{
+ ScFormEditData* pData = m_pViewShell->GetFormEditData();
+
+ m_aHelper.dispose();
+
+ if (pData) // close doesn't destroy;
+ {
+ //set back reference input handler
+ SC_MOD()->SetRefInputHdl(nullptr);
+ StoreFormEditData(pData);
+ }
+
+ m_pViewShell->ClearFormEditData();
+}
+
+bool ScFormulaDlg::IsInputHdl(const ScInputHandler* pHdl)
+{
+ bool bAlive = false;
+
+ // belongs InputHandler to a ViewShell?
+
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh && !bAlive )
+ {
+ if (static_cast<ScTabViewShell*>(pSh)->GetInputHandler() == pHdl)
+ bAlive = true;
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+
+ return bAlive;
+
+}
+
+ScInputHandler* ScFormulaDlg::GetNextInputHandler(const ScDocShell* pDocShell, ScTabViewShell** ppViewSh)
+{
+ ScInputHandler* pHdl=nullptr;
+
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell );
+ while( pFrame && pHdl==nullptr)
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ if(pViewSh!=nullptr)
+ {
+ pHdl=pViewSh->GetInputHandler();
+ if(ppViewSh!=nullptr) *ppViewSh=pViewSh;
+ }
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell );
+ }
+
+ return pHdl;
+}
+
+void ScFormulaDlg::Close()
+{
+ if (IsClosing())
+ return;
+
+ DoEnter();
+}
+
+// functions for right side
+
+bool ScFormulaDlg::calculateValue( const OUString& rStrExp, OUString& rStrResult, bool bMatrixFormula )
+{
+ std::optional<ScSimpleFormulaCalculator> pFCell(std::in_place,
+ *m_pDoc, m_CursorPos, rStrExp, bMatrixFormula);
+ pFCell->SetLimitString(true);
+
+ // HACK! to avoid neither #REF! from ColRowNames
+ // if a name is added as actually range in the overall formula,
+ // but is interpreted at the individual representation as single-cell reference
+ bool bColRowName = pFCell->HasColRowName();
+ if ( bColRowName )
+ {
+ // ColRowName from RPN-Code?
+ if ( pFCell->GetCode()->GetCodeLen() <= 1 )
+ { // ==1: area
+ // ==0: would be an area if...
+ OUString aBraced = "(" + rStrExp + ")";
+ pFCell.emplace(*m_pDoc, m_CursorPos, aBraced, bMatrixFormula);
+ pFCell->SetLimitString(true);
+ }
+ else
+ bColRowName = false;
+ }
+
+ FormulaError nErrCode = pFCell->GetErrCode();
+ if ( nErrCode == FormulaError::NONE || pFCell->IsMatrix() )
+ {
+ SvNumberFormatter& aFormatter = *(m_pDoc->GetFormatTable());
+ const Color* pColor;
+ if (pFCell->IsMatrix())
+ {
+ rStrResult = pFCell->GetString().getString();
+ }
+ else if (pFCell->IsValue())
+ {
+ double n = pFCell->GetValue();
+ sal_uLong nFormat = aFormatter.GetStandardFormat( n, 0,
+ pFCell->GetFormatType(), ScGlobal::eLnge );
+ aFormatter.GetOutputString( n, nFormat, rStrResult, &pColor );
+ }
+ else
+ {
+ sal_uLong nFormat = aFormatter.GetStandardFormat(
+ pFCell->GetFormatType(), ScGlobal::eLnge);
+ aFormatter.GetOutputString( pFCell->GetString().getString(), nFormat,
+ rStrResult, &pColor );
+ // Indicate it's a string, so a number string doesn't look numeric.
+ // Escape embedded quotation marks first by doubling them, as
+ // usual. Actually the result can be copy-pasted from the result
+ // box as literal into a formula expression.
+ rStrResult = "\"" + rStrResult.replaceAll( "\"", "\"\"") + "\"";
+ }
+
+ ScRange aTestRange;
+ if ( bColRowName || (aTestRange.Parse(rStrExp, *m_pDoc) & ScRefFlags::VALID) )
+ rStrResult += " ...";
+ // area
+ }
+ else
+ rStrResult += ScGlobal::GetErrorString(nErrCode);
+
+ return true;
+}
+
+std::shared_ptr<formula::FormulaCompiler> ScFormulaDlg::getCompiler() const
+{
+ if (!m_xCompiler)
+ m_xCompiler = std::make_shared<ScCompiler>(*m_pDoc, m_CursorPos, m_pDoc->GetGrammar());
+ return m_xCompiler;
+}
+
+std::unique_ptr<formula::FormulaCompiler> ScFormulaDlg::createCompiler( formula::FormulaTokenArray& rArray ) const
+{
+ ScCompiler* pCompiler = nullptr;
+ ScTokenArray* pArr = dynamic_cast<ScTokenArray*>(&rArray);
+ assert(pArr); // violation of contract and not created using convertToTokenArray()?
+ if (pArr)
+ pCompiler = new ScCompiler(*m_pDoc, m_CursorPos, *pArr, m_pDoc->GetGrammar());
+ return std::unique_ptr<formula::FormulaCompiler>(pCompiler);
+}
+
+// virtual methods of ScAnyRefDlg:
+void ScFormulaDlg::RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton )
+{
+ pEdit->SelectAll();
+ ::std::pair<formula::RefButton*,formula::RefEdit*> aPair = RefInputStartBefore( pEdit, pButton );
+ m_aHelper.RefInputStart( aPair.second, aPair.first);
+ RefInputStartAfter();
+}
+
+void ScFormulaDlg::RefInputDone( bool bForced )
+{
+ m_aHelper.RefInputDone( bForced );
+ RefInputDoneAfter( bForced );
+}
+
+void ScFormulaDlg::SetReference( const ScRange& rRef, ScDocument& rRefDoc )
+{
+ const IFunctionDescription* pFunc = getCurrentFunctionDescription();
+ if ( !(pFunc && pFunc->getSuppressedArgumentCount() > 0) )
+ return;
+
+ Selection theSel;
+ bool bRefNull = UpdateParaWin(theSel);
+
+ if ( rRef.aStart != rRef.aEnd && bRefNull )
+ {
+ RefInputStart(GetActiveEdit());
+ }
+
+ // Pointer-selected => absolute range references for the non-single
+ // dimensions, so in the other dimension (if any) it's still
+ // copy-adjustable.
+ constexpr ScRefFlags eColFlags = ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
+ constexpr ScRefFlags eRowFlags = ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
+ ScRefFlags eRangeFlags = ScRefFlags::ZERO;
+ if (rRef.aStart.Col() != rRef.aEnd.Col())
+ eRangeFlags |= eColFlags;
+ if (rRef.aStart.Row() != rRef.aEnd.Row())
+ eRangeFlags |= eRowFlags;
+ OUString aRefStr;
+ bool bOtherDoc = (&rRefDoc != m_pDoc && rRefDoc.GetDocumentShell()->HasName());
+ if ( bOtherDoc )
+ {
+ // reference to other document - like inputhdl.cxx
+
+ OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
+
+ // Sheet always 3D and absolute.
+ OUString aTmp( rRef.Format(rRefDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D | eRangeFlags));
+
+ ScDocShell* pObjSh = rRefDoc.GetDocumentShell();
+
+ // #i75893# convert escaped URL of the document to something user friendly
+// OUString aFileName = pObjSh->GetMedium()->GetName();
+ OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+
+ aRefStr = "'" + aFileName + "'#" + aTmp;
+ }
+ else
+ {
+ // We can't use ScRange::Format here because in R1C1 mode we need
+ // to display the reference position relative to the cursor
+ // position.
+ ScTokenArray aArray(rRefDoc);
+ ScComplexRefData aRefData;
+ aRefData.InitRangeRel(rRefDoc, rRef, m_CursorPos);
+ if ((eRangeFlags & eColFlags) == eColFlags)
+ {
+ aRefData.Ref1.SetAbsCol( rRef.aStart.Col() );
+ aRefData.Ref2.SetAbsCol( rRef.aEnd.Col() );
+ }
+ if ((eRangeFlags & eRowFlags) == eRowFlags)
+ {
+ aRefData.Ref1.SetAbsRow( rRef.aStart.Row() );
+ aRefData.Ref2.SetAbsRow( rRef.aEnd.Row() );
+ }
+ bool bSingle = aRefData.Ref1 == aRefData.Ref2;
+ if (m_CursorPos.Tab() != rRef.aStart.Tab())
+ {
+ // pointer-selected => absolute sheet reference
+ aRefData.Ref1.SetAbsTab( rRef.aStart.Tab() );
+ aRefData.Ref1.SetFlag3D(true);
+ }
+ if (bSingle)
+ aArray.AddSingleReference(aRefData.Ref1);
+ else
+ aArray.AddDoubleReference(aRefData);
+ ScCompiler aComp(*m_pDoc, m_CursorPos, aArray, m_pDoc->GetGrammar());
+ OUStringBuffer aBuf;
+ aComp.CreateStringFromTokenArray(aBuf);
+ aRefStr = aBuf.makeStringAndClear();
+ }
+
+ UpdateParaWin(theSel,aRefStr);
+}
+
+bool ScFormulaDlg::IsRefInputMode() const
+{
+ const IFunctionDescription* pDesc = getCurrentFunctionDescription();
+ bool bRef = (pDesc && (pDesc->getSuppressedArgumentCount() > 0)) && (m_pDoc != nullptr);
+ return bRef;
+}
+
+bool ScFormulaDlg::IsDocAllowed(SfxObjectShell* pDocSh) const
+{
+ // not allowed: different from this doc, and no name
+ // pDocSh is always a ScDocShell
+ return !pDocSh || &static_cast<ScDocShell*>(pDocSh)->GetDocument() == m_pDoc || pDocSh->HasName(); // everything else is allowed
+}
+
+void ScFormulaDlg::SetActive()
+{
+ const IFunctionDescription* pFunc = getCurrentFunctionDescription();
+ if ( pFunc && pFunc->getSuppressedArgumentCount() > 0 )
+ {
+ RefInputDone();
+ SetEdSelection();
+ }
+}
+
+void ScFormulaDlg::SaveLRUEntry(const ScFuncDesc* pFuncDescP)
+{
+ if (pFuncDescP && pFuncDescP->nFIndex!=0)
+ {
+ ScModule* pScMod = SC_MOD();
+ pScMod->InsertEntryToLRUList(pFuncDescP->nFIndex);
+ }
+}
+
+void ScFormulaDlg::doClose(bool /*_bOk*/)
+{
+ m_aHelper.DoClose( ScFormulaDlgWrapper::GetChildWindowId() );
+}
+void ScFormulaDlg::insertEntryToLRUList(const formula::IFunctionDescription* _pDesc)
+{
+ const ScFuncDesc* pDesc = dynamic_cast<const ScFuncDesc*>(_pDesc);
+ SaveLRUEntry(pDesc);
+}
+void ScFormulaDlg::showReference(const OUString& _sFormula)
+{
+ ShowReference(_sFormula);
+}
+void ScFormulaDlg::ShowReference(const OUString& _sFormula)
+{
+ m_aHelper.ShowReference(_sFormula);
+}
+void ScFormulaDlg::HideReference( bool bDoneRefMode )
+{
+ m_aHelper.HideReference(bDoneRefMode);
+}
+void ScFormulaDlg::ViewShellChanged()
+{
+ ScFormulaReferenceHelper::ViewShellChanged();
+}
+void ScFormulaDlg::AddRefEntry( )
+{
+
+}
+bool ScFormulaDlg::IsTableLocked( ) const
+{
+ // default: reference input can also be used to switch the table
+ return false;
+}
+
+void ScFormulaDlg::ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton)
+{
+ m_aHelper.ToggleCollapsed(pEdit,pButton);
+}
+
+void ScFormulaDlg::ReleaseFocus( formula::RefEdit* pEdit)
+{
+ m_aHelper.ReleaseFocus(pEdit);
+}
+
+void ScFormulaDlg::dispatch(bool _bOK, bool _bMatrixChecked)
+{
+ SfxBoolItem aRetItem( SID_DLG_RETOK, _bOK );
+ SfxBoolItem aMatItem( SID_DLG_MATRIX, _bMatrixChecked );
+ SfxStringItem aStrItem( SCITEM_STRING, getCurrentFormula() );
+
+ // if edit line is empty (caused by document switching) -> string is empty
+ // -> don't delete old formula
+ if ( aStrItem.GetValue().isEmpty() )
+ aRetItem.SetValue( false ); // sal_False = Cancel
+
+ m_aHelper.SetDispatcherLock( false ); // turn off modal-mode
+
+ clear();
+
+ GetBindings().GetDispatcher()->ExecuteList( SID_INS_FUNCTION,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
+ { &aRetItem, &aStrItem, &aMatItem });
+}
+void ScFormulaDlg::setDispatcherLock( bool bLock )
+{
+ m_aHelper.SetDispatcherLock( bLock );
+}
+void ScFormulaDlg::deleteFormData()
+{
+ if (m_pViewShell)
+ m_pViewShell->ClearFormEditData(); // pData is invalid!
+}
+void ScFormulaDlg::clear()
+{
+ m_pDoc = nullptr;
+
+ //restore reference inputhandler
+ ScModule* pScMod = SC_MOD();
+ pScMod->SetRefInputHdl(nullptr);
+
+ // force Enable() of edit line
+ ScTabViewShell* pScViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( pScViewShell )
+ pScViewShell->UpdateInputHandler();
+}
+void ScFormulaDlg::switchBack()
+{
+ // back to the document
+ // (foreign doc could be above - #34222#)
+ ScInputHandler* pHdl = m_pViewShell->GetInputHandler();
+ if ( pHdl )
+ {
+ pHdl->ViewShellGone(nullptr); // -> get active view
+ pHdl->ShowRefFrame();
+ }
+
+ // restore current chart (cause mouse-RefInput)
+ ScTabViewShell* pScViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ if ( !pScViewShell )
+ return;
+
+ ScViewData& rVD=pScViewShell->GetViewData();
+ SCTAB nExecTab = m_CursorPos.Tab();
+ if ( nExecTab != rVD.GetTabNo() )
+ pScViewShell->SetTabNo( nExecTab );
+
+ SCROW nRow = m_CursorPos.Row();
+ SCCOL nCol = m_CursorPos.Col();
+
+ if(rVD.GetCurX()!=nCol || rVD.GetCurY()!=nRow)
+ pScViewShell->SetCursor(nCol,nRow);
+}
+formula::FormEditData* ScFormulaDlg::getFormEditData() const
+{
+ ScTabViewShell* pViewShell = m_pViewShell;
+ if (pViewShell)
+ return pViewShell->GetFormEditData();
+ return nullptr;
+}
+void ScFormulaDlg::setCurrentFormula(const OUString& _sReplacement)
+{
+ ScModule* pScMod = SC_MOD();
+ {
+ //fdo#69971 We need the EditEngine Modification handler of the inputbar that we
+ //are feeding to be disabled while this dialog is open. Otherwise we end up in
+ //a situation where...
+ //a) this ScFormulaDlg changes the editengine
+ //b) the modify callback gets called
+ //c) which also modifies the editengine
+ //d) on return from that modify handler the editengine attempts to use
+ // old node pointers which were replaced and removed by c
+ //
+ //We turn it off in the ctor and back on in the dtor, but if calc has
+ //to repaint, e.g. when switching to another window and back, then in
+ //ScMultiTextWnd::Paint a new editengine will have been created via
+ //GetEditView with its default Modification handler enabled. So ensure
+ //its off when we will access it via InputReplaceSelection
+ pScMod->InputTurnOffWinEngine();
+ }
+ pScMod->InputReplaceSelection(_sReplacement);
+}
+void ScFormulaDlg::setSelection(sal_Int32 _nStart, sal_Int32 _nEnd)
+{
+ ScModule* pScMod = SC_MOD();
+ pScMod->InputSetSelection( _nStart, _nEnd );
+}
+void ScFormulaDlg::getSelection(sal_Int32& _nStart, sal_Int32& _nEnd) const
+{
+ ScModule* pScMod = SC_MOD();
+ pScMod->InputGetSelection( _nStart, _nEnd );
+}
+OUString ScFormulaDlg::getCurrentFormula() const
+{
+ ScFormEditData* pData = m_pViewShell->GetFormEditData();
+ if (pData && pData->GetInputHandler())
+ return pData->GetInputHandler()->GetFormString();
+ return "";
+}
+formula::IFunctionManager* ScFormulaDlg::getFunctionManager()
+{
+ return ScGlobal::GetStarCalcFunctionMgr();
+}
+uno::Reference< sheet::XFormulaParser> ScFormulaDlg::getFormulaParser() const
+{
+ return m_xParser;
+}
+uno::Reference< sheet::XFormulaOpCodeMapper> ScFormulaDlg::getFormulaOpCodeMapper() const
+{
+ return m_xOpCodeMapper;
+}
+
+table::CellAddress ScFormulaDlg::getReferencePosition() const
+{
+ return table::CellAddress(m_CursorPos.Tab(), m_CursorPos.Col(), m_CursorPos.Row());
+}
+
+::std::unique_ptr<formula::FormulaTokenArray> ScFormulaDlg::convertToTokenArray(const uno::Sequence< sheet::FormulaToken >& _aTokenList)
+{
+ ::std::unique_ptr<formula::FormulaTokenArray> pArray(new ScTokenArray(*m_pDoc));
+ pArray->Fill(_aTokenList, m_pDoc->GetSharedStringPool(), m_pDoc->GetExternalRefManager());
+ return pArray;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibilityHints.hxx b/sc/source/ui/inc/AccessibilityHints.hxx
new file mode 100644
index 0000000000..41b8415a65
--- /dev/null
+++ b/sc/source/ui/inc/AccessibilityHints.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 .
+ */
+
+#pragma once
+
+#include "viewdata.hxx"
+#include <svl/hint.hxx>
+
+class ScAccWinFocusLostHint : public SfxHint
+{
+public:
+ virtual ~ScAccWinFocusLostHint() override;
+};
+
+class ScAccWinFocusGotHint : public SfxHint
+{
+public:
+ virtual ~ScAccWinFocusGotHint() override;
+};
+
+class ScAccGridWinFocusLostHint : public ScAccWinFocusLostHint
+{
+ ScSplitPos eOldGridWin;
+public:
+ ScAccGridWinFocusLostHint( ScSplitPos eOldGridWin );
+ virtual ~ScAccGridWinFocusLostHint() override;
+
+ ScSplitPos GetOldGridWin() const { return eOldGridWin; }
+};
+
+class ScAccGridWinFocusGotHint : public ScAccWinFocusGotHint
+{
+ ScSplitPos eNewGridWin;
+public:
+ ScAccGridWinFocusGotHint( ScSplitPos eNewGridWin );
+ virtual ~ScAccGridWinFocusGotHint() override;
+
+ ScSplitPos GetNewGridWin() const { return eNewGridWin; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleCell.hxx b/sc/source/ui/inc/AccessibleCell.hxx
new file mode 100644
index 0000000000..db6b4c3944
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleCell.hxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "AccessibleCellBase.hxx"
+#include "viewdata.hxx"
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+#include <rtl/ref.hxx>
+#include <editeng/AccessibleStaticTextBase.hxx>
+#include <comphelper/uno3.hxx>
+
+namespace com::sun::star::accessibility { class XAccessibleRelationSet; }
+namespace utl { class AccessibleRelationSetHelper; }
+
+class ScTabViewShell;
+class ScAccessibleDocument;
+
+typedef cppu::ImplHelper1< css::accessibility::XAccessibleExtendedAttributes>
+ ScAccessibleCellAttributeImpl;
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleCell</code> service.
+*/
+class ScAccessibleCell
+ : public ScAccessibleCellBase,
+ public accessibility::AccessibleStaticTextBase,
+ public ScAccessibleCellAttributeImpl
+{
+public:
+ static rtl::Reference<ScAccessibleCell> create(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex,
+ ScSplitPos eSplitPos,
+ ScAccessibleDocument* pAccDoc);
+
+private:
+ ScAccessibleCell(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex,
+ ScSplitPos eSplitPos,
+ ScAccessibleDocument* pAccDoc);
+
+ virtual void Init() override;
+
+ using ScAccessibleCellBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+protected:
+ virtual ~ScAccessibleCell() override;
+
+ using ScAccessibleCellBase::IsDefunc;
+
+public:
+ ///===== XInterface =====================================================
+
+ DECLARE_XINTERFACE()
+
+ ///===== XTypeProvider ===================================================
+
+ DECLARE_XTYPEPROVIDER()
+
+ ///===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible >
+ SAL_CALL getAccessibleAtPoint( const css::awt::Point& rPoint ) override;
+
+ virtual void SAL_CALL grabFocus( ) override;
+
+protected:
+ /// Return the object's current bounding box relative to the desktop.
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+
+ /// Return the object's current bounding box relative to the parent object.
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+public:
+ ///===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+ /// override to calculate this on demand
+ virtual sal_Int64 SAL_CALL
+ getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ /// override to calculate this on demand
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleStateSet() override;
+
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Returns a list of all supported services.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override;
+
+ // Override this method to handle cell's ParaIndent attribute specially.
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+private:
+ ScTabViewShell* mpViewShell;
+ ScAccessibleDocument* mpAccDoc;
+
+ ScSplitPos meSplitPos;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+ virtual bool IsEditable(sal_Int64 nParentStates) override;
+ bool IsOpaque() const;
+ bool IsFocused() const;
+ bool IsSelected();
+
+ static ScDocument* GetDocument(ScTabViewShell* mpViewShell);
+
+ ::std::unique_ptr< SvxEditSource > CreateEditSource(ScTabViewShell* pViewShell, ScAddress aCell, ScSplitPos eSplitPos);
+
+ void FillDependents(utl::AccessibleRelationSetHelper* pRelationSet);
+ void FillPrecedents(utl::AccessibleRelationSetHelper* pRelationSet);
+ void AddRelation(const ScAddress& rCell,
+ const sal_uInt16 aRelationType,
+ ::utl::AccessibleRelationSetHelper* pRelationSet);
+ void AddRelation(const ScRange& rRange,
+ const sal_uInt16 aRelationType,
+ ::utl::AccessibleRelationSetHelper* pRelationSet);
+ bool IsFormulaMode();
+ bool IsDropdown() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleCellBase.hxx b/sc/source/ui/inc/AccessibleCellBase.hxx
new file mode 100644
index 0000000000..f756faf482
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleCellBase.hxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+#include <address.hxx>
+#include <com/sun/star/accessibility/XAccessibleValue.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+typedef cppu::ImplHelper1< css::accessibility::XAccessibleValue>
+ ScAccessibleCellBaseImpl;
+
+class ScAccessibleCellBase
+ : public ScAccessibleContextBase,
+ public ScAccessibleCellBaseImpl
+{
+public:
+ //===== internal ========================================================
+ ScAccessibleCellBase(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScDocument* pDoc,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex);
+protected:
+ virtual ~ScAccessibleCellBase() override;
+public:
+
+ virtual bool isVisible() override;
+
+ ///===== XInterface =====================================================
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ ///===== XAccessibleComponent ============================================
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ ///===== XAccessibleContext ==============================================
+
+ /// Return this objects index among the parents children.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleIndexInParent() override;
+
+protected:
+ /// Return this object's description.
+ virtual OUString
+ createAccessibleDescription() override;
+
+ /// Return the object's current name.
+ virtual OUString
+ createAccessibleName() override;
+
+public:
+ ///===== XAccessibleValue ================================================
+
+ virtual css::uno::Any SAL_CALL
+ getCurrentValue() override;
+
+ virtual sal_Bool SAL_CALL
+ setCurrentValue( const css::uno::Any& aNumber ) override;
+
+ virtual css::uno::Any SAL_CALL
+ getMaximumValue( ) override;
+
+ virtual css::uno::Any SAL_CALL
+ getMinimumValue( ) override;
+
+ virtual css::uno::Any SAL_CALL
+ getMinimumIncrement( ) override;
+
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /// returns the possible types
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+protected:
+ ScAddress maCellAddress;
+
+ ScDocument* mpDoc;
+
+ sal_Int64 mnIndex;
+
+private:
+ virtual bool IsEditable(sal_Int64 nParentStates);
+protected:
+ /// @throw css::uno::RuntimeException
+ OUString GetNote() const;
+
+ /// @throw css::uno::RuntimeException
+ OUString GetAllDisplayNote() const;
+ /// @throw css::uno::RuntimeException
+ OUString getShadowAttrs() const;
+ /// @throw css::uno::RuntimeException
+ OUString getBorderAttrs();
+public:
+ const ScAddress& GetCellAddress() const { return maCellAddress; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleContextBase.hxx b/sc/source/ui/inc/AccessibleContextBase.hxx
new file mode 100644
index 0000000000..d683dee6c2
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleContextBase.hxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <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/uno/Reference.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/interfacecontainer.h>
+
+#include <svl/lstner.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <rtl/ref.hxx>
+
+namespace tools { class Rectangle; }
+class AbsoluteScreenPixelRectangle;
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleContext</code> service.
+*/
+typedef cppu::WeakComponentImplHelper<
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::lang::XServiceInfo
+ > ScAccessibleContextBaseWeakImpl;
+
+class ScAccessibleContextBase
+ : public cppu::BaseMutex,
+ public ScAccessibleContextBaseWeakImpl,
+ public SfxListener
+{
+class ScAccessibleContextBaseEventListener;
+
+public:
+ //===== internal ========================================================
+ ScAccessibleContextBase(
+ css::uno::Reference<css::accessibility::XAccessible> xParent,
+ const sal_Int16 aRole);
+
+ virtual void Init();
+ virtual void SAL_CALL disposing() override;
+protected:
+ virtual ~ScAccessibleContextBase() override;
+public:
+
+ /// @throws css::uno::RuntimeException
+ bool isShowing();
+
+ /// @throws css::uno::RuntimeException
+ virtual bool isVisible();
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) 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 ==============================================
+
+ /// Return the number of currently visible children.
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+
+ /// Return a reference to the parent.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleParent() override;
+
+ /// Return this objects index among the parents children.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleIndexInParent() override;
+
+ /// Return this object's role.
+ virtual sal_Int16 SAL_CALL
+ getAccessibleRole() override;
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ /// Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+
+ /// Return NULL to indicate that an empty relation set.
+ virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ /** Return the parents locale or throw exception if this object has no
+ parent yet/anymore.
+ */
+ virtual css::lang::Locale SAL_CALL
+ getLocale() override;
+
+ ///===== XAccessibleEventBroadcaster =====================================
+
+ /** Add listener that is informed of future changes of name,
+ description and so on events.
+ */
+ virtual void SAL_CALL
+ addAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& xListener) override;
+
+ // Remove an existing event listener.
+ virtual void SAL_CALL
+ removeAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& xListener) override;
+
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService(const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext and Accessible service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+protected:
+ /// Return this object's description.
+ ///
+ /// @throws css::uno::RuntimeException
+ virtual OUString
+ createAccessibleDescription();
+
+ /// Return the object's current name.
+ ///
+ /// @throws css::uno::RuntimeException
+ virtual OUString
+ createAccessibleName();
+
+ /// Return the object's current bounding box relative to the desktop.
+ ///
+ /// @throws css::uno::RuntimeException
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const;
+
+ /// Return the object's current bounding box relative to the parent object.
+ ///
+ /// @throws css::uno::RuntimeException
+ virtual tools::Rectangle GetBoundingBox() const;
+
+public:
+ /// Calls all Listener to tell they the change.
+ void
+ CommitChange(const css::accessibility::AccessibleEventObject& rEvent) const;
+
+ /// Use this method to set initial Name without notification
+ void SetName(const OUString& rName) { msName = rName; }
+
+ /// Use this method to set initial Description without notification
+ void SetDescription(const OUString& rDesc) { msDescription = rDesc; }
+
+ void SetParent(const css::uno::Reference<css::accessibility::XAccessible>& rParent) { mxParent = rParent; }
+
+protected:
+ /// Calls all FocusListener to tell they that the focus is gained.
+ void CommitFocusGained() const;
+
+ /// Calls all FocusListener to tell they that the focus is lost.
+ void CommitFocusLost() const;
+
+ bool IsDefunc() const { return rBHelper.bDisposed; }
+
+ /// @throws css::lang::DisposedException
+ void IsObjectValid() const;
+
+ /// Reference to the parent object.
+ css::uno::Reference<css::accessibility::XAccessible> mxParent;
+
+private:
+ /** 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;
+
+ /// client id in the AccessibleEventNotifier queue
+ sal_uInt32 mnClientId;
+
+ /** This is the role of this object.
+ */
+ sal_Int16 maRole;
+
+ rtl::Reference<ScAccessibleContextBaseEventListener> mxEventListener;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleCsvControl.hxx b/sc/source/ui/inc/AccessibleCsvControl.hxx
new file mode 100644
index 0000000000..cb1efc3093
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleCsvControl.hxx
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <tools/gen.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/accessiblecomponenthelper.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <editeng/AccessibleStaticTextBase.hxx>
+#include <comphelper/uno3.hxx>
+#include <map>
+
+class ScCsvControl;
+
+/** Accessible base class used for CSV controls. */
+class ScAccessibleCsvControl : public comphelper::OAccessibleComponentHelper
+{
+private:
+ ScCsvControl* mpControl; /// Pointer to the VCL control.
+
+public:
+ explicit ScAccessibleCsvControl(ScCsvControl& rControl);
+ virtual ~ScAccessibleCsvControl() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ // events -----------------------------------------------------------------
+public:
+ /** Sends a GetFocus or LoseFocus event to all listeners. */
+ virtual void SendFocusEvent( bool bFocused );
+ /** Sends a caret changed event to all listeners. */
+ virtual void SendCaretEvent();
+ /** Sends a visible area changed event to all listeners. */
+ void SendVisibleEvent();
+ /** Sends a selection changed event to all listeners. */
+ void SendSelectionEvent();
+ /** Sends a table model changed event for changed cell contents to all listeners. */
+ virtual void SendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows );
+ /** Sends a table model changed event for an inserted column to all listeners. */
+ virtual void SendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn );
+ /** Sends a table model changed event for a removed column to all listeners. */
+ virtual void SendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn );
+
+ // helpers ----------------------------------------------------------------
+protected:
+ virtual css::awt::Rectangle implGetBounds() override;
+
+ /** Returns the VCL control. Assumes a living object. */
+ ScCsvControl& implGetControl() const;
+
+ /** Creates a StateSet and fills it with DEFUNC, OPAQUE, ENABLED, SHOWING and VISIBLE. */
+ sal_Int64 implCreateStateSet();
+};
+
+class ScCsvRuler;
+
+/** Accessible class representing the CSV ruler control. */
+class ScAccessibleCsvRuler : public cppu::ImplInheritanceHelper<
+ ScAccessibleCsvControl,
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleText>
+{
+private:
+ OUStringBuffer maBuffer; /// Contains the text representation of the ruler.
+
+public:
+ explicit ScAccessibleCsvRuler( ScCsvRuler& rRuler );
+ virtual ~ScAccessibleCsvRuler() override;
+
+ // XAccessibleComponent -----------------------------------------------------
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override { return this; }
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ // XAccessibleContext -----------------------------------------------------
+
+ /** Returns the child count (the ruler does not have children). */
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ /** Throws an exception (the ruler does not have children). */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nIndex ) override;
+
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override { return css::accessibility::AccessibleRole::TEXT; }
+
+ /** Returns the relation to the grid control. */
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
+
+ /** Returns the current set of states. */
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ // XAccessibleText --------------------------------------------------------
+
+ /** Return the position of the caret. */
+ virtual sal_Int32 SAL_CALL getCaretPosition() override;
+
+ /** Sets the position of the caret. */
+ virtual sal_Bool SAL_CALL setCaretPosition( sal_Int32 nIndex ) override;
+
+ /** Returns the specified character. */
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;
+
+ /** Returns the attributes of the specified character. */
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+
+ /** Returns the screen coordinates of the specified character. */
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+
+ /** Returns the count of characters. */
+ virtual sal_Int32 SAL_CALL getCharacterCount() override;
+
+ /** Returns the character index at the specified coordinate (object's coordinate system). */
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& rPoint ) override;
+
+ /** Returns the selected text (ruler returns empty string). */
+ virtual OUString SAL_CALL getSelectedText() override;
+
+ /** Returns the start index of the selection (ruler returns -1). */
+ virtual sal_Int32 SAL_CALL getSelectionStart() override;
+
+ /** Returns the end index of the selection (ruler returns -1). */
+ virtual sal_Int32 SAL_CALL getSelectionEnd() override;
+
+ /** Selects a part of the text (ruler does nothing). */
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+
+ /** Returns the entire text. */
+ virtual OUString SAL_CALL getText() override;
+
+ /** Returns the specified range [Start,End) of the text. */
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+
+ /** Returns the specified text portion. */
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+
+ /** Copies the specified text range into the clipboard (ruler does nothing). */
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // events -----------------------------------------------------------------
+public:
+ /** Sends a caret changed event to all listeners. */
+ virtual void SendCaretEvent() override;
+
+ // helpers ----------------------------------------------------------------
+private:
+
+ /** @throws css::lang::IndexOutOfBoundsException if the specified character position is invalid (outside 0..len-1). */
+ void ensureValidIndex( sal_Int32 nIndex ) const;
+ /** @throws css::lang::IndexOutOfBoundsException if the specified character position is invalid (outside 0..len). */
+ void ensureValidIndexWithEnd( sal_Int32 nIndex ) const;
+ /** @throws css::lang::IndexOutOfBoundsException if the specified character range [Start,End) is invalid.
+ @descr If Start>End, swaps Start and End before checking. */
+ void ensureValidRange( sal_Int32& rnStartIndex, sal_Int32& rnEndIndex ) const;
+
+ /** Returns the VCL ruler control. Assumes a living object. */
+ ScCsvRuler& implGetRuler() const;
+
+ /** Builds the entire string buffer.
+
+ @throws css::uno::RuntimeException
+ */
+ void constructStringBuffer();
+ /** Returns the character count of the text. */
+ sal_Int32 implGetTextLength() const;
+
+ /** Returns true, if the character at the specified index has a split. */
+ bool implHasSplit( sal_Int32 nApiPos );
+
+ /** Returns the first character index with equal formatting as at nApiPos. */
+ sal_Int32 implGetFirstEqualFormatted( sal_Int32 nApiPos );
+ /** Returns the last character index with equal formatting as at nApiPos. */
+ sal_Int32 implGetLastEqualFormatted( sal_Int32 nApiPos );
+};
+
+class ScCsvGrid;
+class ScAccessibleCsvCell;
+
+/** Accessible class representing the CSV grid control. */
+class ScAccessibleCsvGrid : public cppu::ImplInheritanceHelper<
+ ScAccessibleCsvControl,
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleTable,
+ css::accessibility::XAccessibleSelection>
+{
+protected:
+ typedef std::map< sal_Int64, rtl::Reference<ScAccessibleCsvCell> > XAccessibleSet;
+
+private:
+ XAccessibleSet maAccessibleChildren;
+
+public:
+ explicit ScAccessibleCsvGrid( ScCsvGrid& rGrid );
+ virtual ~ScAccessibleCsvGrid() override;
+ virtual void SAL_CALL disposing() override;
+
+ // XAccessibleComponent ---------------------------------------------------
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override { return this; }
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+
+ /** Returns the cell at the specified point. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& rPoint ) override;
+
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override { return css::accessibility::AccessibleRole::TABLE; }
+
+ // XAccessibleContext -----------------------------------------------------
+
+ /** Returns the child count (count of cells in the table). */
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ /** Returns the specified child cell. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nIndex ) override;
+
+ /** Returns the relation to the ruler control. */
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
+
+ /** Returns the current set of states. */
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ // XAccessibleTable -------------------------------------------------------
+
+ /** Returns the number of rows in the table. */
+ virtual sal_Int32 SAL_CALL getAccessibleRowCount() override;
+
+ /** Returns the number of columns in the table. */
+ virtual sal_Int32 SAL_CALL getAccessibleColumnCount() override;
+
+ /** Returns the description of the specified row in the table. */
+ virtual OUString SAL_CALL getAccessibleRowDescription( sal_Int32 nRow ) override;
+
+ /** Returns the description text of the specified column in the table. */
+ virtual OUString SAL_CALL getAccessibleColumnDescription( sal_Int32 nColumn ) override;
+
+ /** Returns the number of rows occupied at a specified row and column.
+ @descr Returns always 1 (Merged cells not supported). */
+ virtual sal_Int32 SAL_CALL getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /** Returns the number of rows occupied at a specified row and column.
+ @descr Returns always 1 (Merged cells not supported). */
+ virtual sal_Int32 SAL_CALL getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /** Returns the row headers as an AccessibleTable. */
+ virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL getAccessibleRowHeaders() override;
+
+ /** Returns the column headers as an AccessibleTable. */
+ virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL getAccessibleColumnHeaders() override;
+
+ /** Returns the selected rows as a sequence. */
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL getSelectedAccessibleRows() override;
+
+ /** Returns the selected columns as a sequence. */
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL getSelectedAccessibleColumns() override;
+
+ /** Returns true, if the specified row is selected. */
+ virtual sal_Bool SAL_CALL isAccessibleRowSelected( sal_Int32 nRow ) override;
+
+ /** Returns true, if the specified column is selected. */
+ virtual sal_Bool SAL_CALL isAccessibleColumnSelected( sal_Int32 nColumn ) override;
+
+ /** Returns the accessible cell object at the specified position. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /** Returns the caption object of the table. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleCaption() override;
+
+ /** Returns the summary description object of the table. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleSummary() override;
+
+ /** Returns true, if the cell at a specified position is selected. */
+ virtual sal_Bool SAL_CALL isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /** Returns the child index of the cell at the specified position. */
+ virtual sal_Int64 SAL_CALL getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /** Returns the row index of the specified child. */
+ virtual sal_Int32 SAL_CALL getAccessibleRow( sal_Int64 nChildIndex ) override;
+
+ /** Returns the column index of the specified child. */
+ virtual sal_Int32 SAL_CALL getAccessibleColumn( sal_Int64 nChildIndex ) override;
+
+ // XAccessibleSelection ---------------------------------------------------
+
+ /** Selects the specified child (selects the entire column or the entire table). */
+ virtual void SAL_CALL selectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ /** Returns true, if the specified child is selected. */
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int64 nChildIndex ) override;
+
+ /** Deselects all cells. */
+ virtual void SAL_CALL clearAccessibleSelection() override;
+
+ /** Selects all cells. */
+ virtual void SAL_CALL selectAllAccessibleChildren() override;
+
+ /** Returns the count of selected children. */
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount() override;
+
+ /** Returns the child with the specified index in all selected children. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+
+ /** Deselects the child with the specified index in all selected children. */
+ virtual void SAL_CALL deselectAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+
+ // events -----------------------------------------------------------------
+public:
+ /** Sends a GetFocus or LoseFocus event to all listeners. */
+ virtual void SendFocusEvent( bool bFocused ) override;
+ /** Sends a table model changed event for changed cell contents to all listeners. */
+ virtual void SendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows ) override;
+ /** Sends a table model changed event for an inserted column to all listeners. */
+ virtual void SendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn ) override;
+ /** Sends a table model changed event for a removed column to all listeners. */
+ virtual void SendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn ) override;
+
+ // helpers ----------------------------------------------------------------
+private:
+
+ /** @throws css::lang::IndexOutOfBoundsException if nIndex is not a valid child index. */
+ void ensureValidIndex( sal_Int64 nIndex ) const;
+ /** @Throws css::lang::IndexOutOfBoundsException if the specified position is invalid. */
+ void ensureValidPosition( sal_Int32 nRow, sal_Int32 nColumn ) const;
+
+ /** Returns the VCL grid control. Assumes a living object. */
+ ScCsvGrid& implGetGrid() const;
+
+ /** Returns true, if the specified column (including header) is selected. */
+ bool implIsColumnSelected( sal_Int32 nColumn ) const;
+ /** Selects the specified column (including header). */
+ void implSelectColumn( sal_Int32 nColumn, bool bSelect );
+
+ /** Returns the count of visible rows in the table (including header). */
+ sal_Int32 implGetRowCount() const;
+ /** Returns the total column count in the table (including header). */
+ sal_Int32 implGetColumnCount() const;
+ /** Returns the count of selected columns in the table. */
+ sal_Int32 implGetSelColumnCount() const;
+ /** Returns the total cell count in the table (including header). */
+ sal_Int64 implGetCellCount() const { return static_cast<sal_Int64>(implGetRowCount()) * static_cast<sal_Int64>(implGetColumnCount()); }
+
+ /** Returns the row index from cell index (including header). */
+ sal_Int32 implGetRow( sal_Int64 nIndex ) const { return nIndex / implGetColumnCount(); }
+ /** Returns the column index from cell index (including header). */
+ sal_Int32 implGetColumn( sal_Int64 nIndex ) const { return nIndex % implGetColumnCount(); }
+ /** Returns the absolute column index of the nSelColumn-th selected column. */
+ sal_Int32 implGetSelColumn( sal_Int32 nSelColumn ) const;
+ /** Returns the child index from cell position (including header). */
+ sal_Int64 implGetIndex( sal_Int32 nRow, sal_Int32 nColumn ) const { return static_cast<sal_Int64>(nRow) * static_cast<sal_Int64>(implGetColumnCount()) + nColumn; }
+
+ /** Returns the contents of the specified cell (including header). Indexes must be valid. */
+ OUString implGetCellText( sal_Int32 nRow, sal_Int32 nColumn ) const;
+ /** Creates a new accessible object of the specified cell. Indexes must be valid. */
+ rtl::Reference<ScAccessibleCsvCell> implCreateCellObj(sal_Int32 nRow, sal_Int32 nColumn);
+
+ css::uno::Reference<css::accessibility::XAccessible> getAccessibleCell(sal_Int32 nRow, sal_Int32 nColumn);
+};
+
+/** Accessible class representing a cell of the CSV grid control. */
+class ScAccessibleCsvCell : public cppu::ImplInheritanceHelper<
+ ScAccessibleCsvControl,
+ css::accessibility::XAccessible>
+ , public ::accessibility::AccessibleStaticTextBase
+{
+protected:
+ typedef ::std::unique_ptr< SvxEditSource > SvxEditSourcePtr;
+
+private:
+ OUString maCellText; /// The text contents of this cell.
+ sal_Int32 mnLine; /// The grid line index (core index).
+ sal_uInt32 mnColumn; /// The grid column index (core index).
+ sal_Int32 mnIndex; /// The index of the cell in the table.
+
+public:
+ explicit ScAccessibleCsvCell(
+ ScCsvGrid& rGrid,
+ OUString aCellText,
+ sal_Int32 nRow, sal_Int32 nColumn);
+ virtual ~ScAccessibleCsvCell() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // XAccessibleComponent ---------------------------------------------------
+
+ /** Sets the focus to the column of this cell. */
+ virtual void SAL_CALL grabFocus() override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override { return this; }
+
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override { return css::accessibility::AccessibleRole::TEXT; }
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ // XAccessibleContext -----------------------------------------------------
+
+ /** Returns the child count. */
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ /** Returns the specified child. */
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nIndex ) override;
+
+ /** Returns the index of this cell in the table. */
+ virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override;
+
+ /** Returns the relation to the ruler control. */
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
+
+ /** Returns the current set of states. */
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ // XInterface -------------------------------------------------------------
+
+ DECLARE_XINTERFACE()
+
+ // XTypeProvider ----------------------------------------------------------
+
+ DECLARE_XTYPEPROVIDER()
+
+private:
+ /** Returns the VCL grid control. Assumes a living object. */
+ ScCsvGrid& implGetGrid() const;
+ /** Returns the pixel position of the cell (rel. to parent), regardless of visibility. */
+ Point implGetRealPos() const;
+ /** Returns the width of the character count */
+ sal_uInt32 implCalcPixelWidth(sal_uInt32 nChars) const;
+ /** Returns the pixel size of the cell, regardless of visibility. */
+ Size implGetRealSize() const;
+ /** Returns the bounding box of the cell relative in the table. */
+ virtual css::awt::Rectangle implGetBounds() override;
+
+ /** Creates the edit source the text helper needs. */
+ ::std::unique_ptr< SvxEditSource > implCreateEditSource();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleDocument.hxx b/sc/source/ui/inc/AccessibleDocument.hxx
new file mode 100644
index 0000000000..d7935dfd76
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleDocument.hxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "AccessibleDocumentBase.hxx"
+#include "viewdata.hxx"
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/view/XSelectionChangeListener.hpp>
+#include <cppuhelper/implbase3.hxx>
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+#include <svx/IAccessibleViewForwarder.hxx>
+
+class ScTabViewShell;
+class ScAccessibleSpreadsheet;
+class ScChildrenShapes;
+class ScAccessibleEditObject;
+class VclWindowEvent;
+
+namespace utl
+{
+ class AccessibleRelationSetHelper;
+}
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleContext</code> service.
+*/
+
+typedef cppu::ImplHelper3< css::accessibility::XAccessibleSelection,
+ css::accessibility::XAccessibleExtendedAttributes,
+ css::view::XSelectionChangeListener >
+ ScAccessibleDocumentImpl;
+
+class ScAccessibleDocument
+ : public ScAccessibleDocumentBase,
+ public ScAccessibleDocumentImpl,
+ public accessibility::IAccessibleViewForwarder
+{
+public:
+ //===== internal ========================================================
+ ScAccessibleDocument(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ ScSplitPos eSplitPos);
+
+ void PreInit();
+
+ virtual void Init() override;
+
+ DECL_LINK( WindowChildEventListener, VclWindowEvent&, void );
+protected:
+ virtual ~ScAccessibleDocument() override;
+
+ using ScAccessibleDocumentBase::IsDefunc;
+
+public:
+
+ virtual void SAL_CALL disposing() override;
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== XInterface =====================================================
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ ///===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible >
+ SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& rPoint ) override;
+
+ virtual void SAL_CALL grabFocus( ) override;
+
+ ///===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleStateSet() override;
+
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override ;
+ ///===== XAccessibleSelection ===========================================
+
+ virtual void SAL_CALL
+ selectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL
+ isAccessibleChildSelected( sal_Int64 nChildIndex ) override;
+
+ virtual void SAL_CALL
+ clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL
+ selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int64 SAL_CALL
+ getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL
+ deselectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ ///===== XSelectionListener =============================================
+
+ virtual void SAL_CALL selectionChanged( const css::lang::EventObject& aEvent ) override;
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ ///===== XServiceInfo ===================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Returns a list of all supported services.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /// returns the possible types
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+ ///===== IAccessibleViewForwarder ========================================
+
+ /** Returns the area of the underlying document that is visible in the
+ * corresponding window.
+
+ @return
+ The rectangle of the visible part of the document. The values
+ are, contrary to the base class, in internal coordinates.
+ */
+ virtual tools::Rectangle GetVisibleArea() const override;
+
+ /** Transform the specified point from internal coordinates to an
+ absolute screen position.
+
+ @param rPoint
+ Point in internal coordinates.
+
+ @return
+ The same point but in screen coordinates relative to the upper
+ left corner of the (current) screen.
+ */
+ virtual Point LogicToPixel (const Point& rPoint) const override;
+
+ /** Transform the specified size from internal coordinates to a screen
+ * oriented pixel size.
+
+ @param rSize
+ Size in internal coordinates.
+
+ @return
+ The same size but in screen coordinates.
+ */
+ virtual Size LogicToPixel (const Size& rSize) const override;
+
+ ///======== internal =====================================================
+
+ rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAddress* pAddress) const;
+
+ css::uno::Reference< css::accessibility::XAccessible >
+ GetAccessibleSpreadsheet();
+
+protected:
+ /// Return this object's description.
+ virtual OUString
+ createAccessibleDescription() override;
+
+ /// Return the object's current name.
+ virtual OUString
+ createAccessibleName() override;
+
+ /// Return the object's current bounding box relative to the desktop.
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+
+ /// Return the object's current bounding box relative to the parent object.
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ ScTabViewShell* mpViewShell;
+ ScSplitPos meSplitPos;
+ rtl::Reference<ScAccessibleSpreadsheet> mpAccessibleSpreadsheet;
+ std::unique_ptr<ScChildrenShapes> mpChildrenShapes;
+ rtl::Reference<ScAccessibleEditObject> mpTempAccEdit;
+ css::uno::Reference<css::accessibility::XAccessible> mxTempAcc;
+ tools::Rectangle maVisArea;
+ bool mbCompleteSheetSelected;
+
+public:
+ SCTAB getVisibleTable() const; // use it in ScChildrenShapes
+
+private:
+ void FreeAccessibleSpreadsheet();
+
+ bool IsTableSelected() const;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+
+ void AddChild(const css::uno::Reference<css::accessibility::XAccessible>& xAcc, bool bFireEvent);
+ void RemoveChild(const css::uno::Reference<css::accessibility::XAccessible>& xAcc, bool bFireEvent);
+
+ OUString GetCurrentCellName() const;
+ static OUString GetCurrentCellDescription();
+
+ tools::Rectangle GetVisibleArea_Impl() const;
+public:
+ ScDocument *GetDocument() const ;
+ ScAddress GetCurCellAddress() const;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleDocumentBase.hxx b/sc/source/ui/inc/AccessibleDocumentBase.hxx
new file mode 100644
index 0000000000..23a984f666
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleDocumentBase.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+
+class ScAccessibleDocumentBase : public ScAccessibleContextBase
+{
+public:
+ //===== internal ========================================================
+ ScAccessibleDocumentBase(const css::uno::Reference<css::accessibility::XAccessible>& rxParent);
+
+protected:
+ virtual ~ScAccessibleDocumentBase() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleDocumentPagePreview.hxx b/sc/source/ui/inc/AccessibleDocumentPagePreview.hxx
new file mode 100644
index 0000000000..956825a2dc
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleDocumentPagePreview.hxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+
+#include "AccessibleDocumentBase.hxx"
+
+class ScPreviewShell;
+class ScNotesChildren;
+class ScShapeChildren;
+class ScAccessiblePreviewTable;
+class ScAccessiblePageHeader;
+
+class ScAccessibleDocumentPagePreview
+ : public ScAccessibleDocumentBase
+{
+public:
+ //===== internal ========================================================
+ ScAccessibleDocumentPagePreview(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell );
+protected:
+ virtual ~ScAccessibleDocumentPagePreview() override;
+
+ using ScAccessibleDocumentBase::IsDefunc;
+
+public:
+ using ScAccessibleContextBase::disposing;
+
+ virtual void SAL_CALL disposing() override;
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible >
+ SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& rPoint ) override;
+
+ virtual void SAL_CALL grabFocus( ) override;
+
+ ///===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ virtual OUString SAL_CALL getAccessibleName() override;
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Returns a list of all supported services.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+ ///===== internal ========================================================
+
+protected:
+ /// Return this object's description.
+ virtual OUString
+ createAccessibleDescription() override;
+
+ /// Return the object's current name.
+ virtual OUString
+ createAccessibleName() override;
+
+public: // needed in ScShapeChildren
+ /// Return the object's current bounding box relative to the desktop.
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+
+protected:
+ /// Return the object's current bounding box relative to the parent object.
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+ std::unique_ptr<ScNotesChildren> mpNotesChildren;
+ std::unique_ptr<ScShapeChildren> mpShapeChildren;
+ rtl::Reference<ScAccessiblePreviewTable> mpTable;
+ rtl::Reference<ScAccessiblePageHeader> mpHeader;
+ rtl::Reference<ScAccessiblePageHeader> mpFooter;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+
+ ScNotesChildren* GetNotesChildren();
+ ScShapeChildren* GetShapeChildren();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleEditObject.hxx b/sc/source/ui/inc/AccessibleEditObject.hxx
new file mode 100644
index 0000000000..159fa2eeb7
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleEditObject.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <address.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/customweld.hxx>
+
+#include <memory>
+
+namespace accessibility
+{
+ class AccessibleTextHelper;
+}
+class EditView;
+class ScTextWnd;
+namespace vcl { class Window; }
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleCell</code> service.
+*/
+class ScAccessibleEditObject
+ : public ScAccessibleContextBase,
+ public css::accessibility::XAccessibleSelection
+{
+public:
+ enum EditObjectType
+ {
+ CellInEditMode,
+ EditLine,
+ EditControl
+ };
+
+ ScAccessibleEditObject(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ EditView* pEditView, vcl::Window* pWin, const OUString& rName,
+ const OUString& rDescription, EditObjectType eObjectType);
+
+ void InitAcc(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ EditView* pEditView, const OUString& rName, const OUString& rDescription);
+
+protected:
+ virtual ~ScAccessibleEditObject() override;
+
+ ScAccessibleEditObject(EditObjectType eObjectType);
+
+ using ScAccessibleContextBase::IsDefunc;
+
+public:
+ using ScAccessibleContextBase::disposing;
+
+ virtual void SAL_CALL disposing() override;
+
+ void LostFocus();
+
+ void GotFocus();
+///===== XInterface =====================================================
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+ ///===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible >
+ SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& rPoint ) override;
+
+ virtual OutputDevice* GetOutputDeviceForView();
+
+protected:
+ /// Return the object's current bounding box relative to the desktop.
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+
+ /// Return the object's current bounding box relative to the parent object.
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+public:
+ ///===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+ /// override to calculate this on demand
+ virtual sal_Int64 SAL_CALL
+ getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ /// override to calculate this on demand
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ //===== XAccessibleSelection ============================================
+
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int64 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+protected:
+ /// Return this object's description.
+ virtual OUString
+ createAccessibleDescription() override;
+
+ /// Return the object's current name.
+ virtual OUString
+ createAccessibleName() override;
+
+public:
+ ///===== XAccessibleEventBroadcaster =====================================
+
+ /** Add listener that is informed of future changes of name,
+ description and so on events.
+ */
+ virtual void SAL_CALL
+ addAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& xListener) override;
+
+ // Remove an existing event listener.
+ virtual void SAL_CALL
+ removeAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& xListener) override;
+
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+private:
+ std::unique_ptr<accessibility::AccessibleTextHelper> mpTextHelper;
+ EditView* mpEditView;
+ VclPtr<vcl::Window> mpWindow;
+protected:
+ ScTextWnd* mpTextWnd;
+private:
+ EditObjectType meObjectType;
+ bool mbHasFocus;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+
+ void CreateTextHelper();
+ ScDocument *m_pScDoc;
+ ScAddress m_curCellAddress;
+
+ ///===== XAccessibleComponent ============================================
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ sal_Int32 GetFgBgColor( const OUString &strPropColor) ;
+};
+
+class ScAccessibleEditControlObject : public ScAccessibleEditObject
+{
+private:
+ weld::CustomWidgetController* m_pController;
+
+protected:
+ /// Return the object's current bounding box relative to the desktop.
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+
+ /// Return the object's current bounding box relative to the parent object.
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+public:
+ ScAccessibleEditControlObject(weld::CustomWidgetController* pController, EditObjectType eObjectType)
+ : ScAccessibleEditObject(eObjectType)
+ , m_pController(pController)
+ {
+ }
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+
+ // for mapping positions/sizes within the TextView to a11y
+ virtual OutputDevice* GetOutputDeviceForView() override;
+
+ using ScAccessibleContextBase::disposing;
+ virtual void SAL_CALL disposing() override;
+};
+
+class ScAccessibleEditLineObject : public ScAccessibleEditControlObject
+{
+public:
+ ScAccessibleEditLineObject(ScTextWnd* pTextWnd);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessiblePageHeader.hxx b/sc/source/ui/inc/AccessiblePageHeader.hxx
new file mode 100644
index 0000000000..d08db8dacd
--- /dev/null
+++ b/sc/source/ui/inc/AccessiblePageHeader.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+#include <editeng/svxenum.hxx>
+#include <rtl/ref.hxx>
+
+class ScPreviewShell;
+class EditTextObject;
+class ScAccessiblePageHeaderArea;
+
+class ScAccessiblePageHeader : public ScAccessibleContextBase
+{
+public:
+ ScAccessiblePageHeader( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, bool bHeader, sal_Int32 nIndex );
+
+protected:
+ virtual ~ScAccessiblePageHeader() override;
+
+ using ScAccessibleContextBase::IsDefunc;
+
+public:
+ using ScAccessibleContextBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+ //===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ //===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual void SAL_CALL grabFocus() override;
+
+ //===== XAccessibleContext ==============================================
+
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleChild( sal_Int64 i ) override;
+ virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override;
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ //===== XServiceInfo ====================================================
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+protected:
+ virtual OUString createAccessibleDescription() override;
+ virtual OUString createAccessibleName() override;
+
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+ sal_Int32 mnIndex;
+ bool mbHeader;
+ std::vector< rtl::Reference<ScAccessiblePageHeaderArea> >
+ maAreas;
+ sal_Int32 mnChildCount;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+
+ void AddChild(const EditTextObject* pArea, sal_uInt32 nIndex, SvxAdjust eAdjust);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessiblePageHeaderArea.hxx b/sc/source/ui/inc/AccessiblePageHeaderArea.hxx
new file mode 100644
index 0000000000..a1e813ec67
--- /dev/null
+++ b/sc/source/ui/inc/AccessiblePageHeaderArea.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+#include <editeng/svxenum.hxx>
+
+class EditTextObject;
+namespace accessibility
+{
+ class AccessibleTextHelper;
+}
+class ScPreviewShell;
+
+class ScAccessiblePageHeaderArea
+ : public ScAccessibleContextBase
+{
+public:
+ //===== internal ========================================================
+ ScAccessiblePageHeaderArea(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const EditTextObject* pEditObj,
+ SvxAdjust eAdjust);
+protected:
+ virtual ~ScAccessiblePageHeaderArea() override;
+public:
+ const EditTextObject* GetEditTextObject() const { return mpEditObj.get(); }
+
+ using ScAccessibleContextBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible >
+ SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& rPoint ) override;
+
+ ///===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+ /// override to calculate this on demand
+ virtual sal_Int64 SAL_CALL
+ getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ /// override to calculate this on demand
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleStateSet() override;
+
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext and Accessible service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+protected:
+ virtual OUString createAccessibleDescription() override;
+ virtual OUString createAccessibleName() override;
+
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ std::unique_ptr<EditTextObject> mpEditObj;
+ std::unique_ptr<accessibility::AccessibleTextHelper> mpTextHelper;
+ ScPreviewShell* mpViewShell;
+ SvxAdjust meAdjust;
+
+ void CreateTextHelper();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessiblePreviewCell.hxx b/sc/source/ui/inc/AccessiblePreviewCell.hxx
new file mode 100644
index 0000000000..b40b65242d
--- /dev/null
+++ b/sc/source/ui/inc/AccessiblePreviewCell.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 .
+ */
+
+#pragma once
+
+#include "AccessibleCellBase.hxx"
+
+class ScPreviewShell;
+
+namespace accessibility
+{
+ class AccessibleTextHelper;
+}
+
+class ScAccessiblePreviewCell : public ScAccessibleCellBase
+{
+public:
+ //===== internal ========================================================
+ ScAccessiblePreviewCell(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, const ScAddress& rCellAddress, sal_Int32 nIndex );
+
+protected:
+ virtual ~ScAccessiblePreviewCell() override;
+
+ using ScAccessibleCellBase::IsDefunc;
+
+public:
+ using ScAccessibleCellBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ //===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual void SAL_CALL grabFocus() override;
+
+ //===== XAccessibleContext ==============================================
+
+ // override to calculate this on demand
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleChild( sal_Int64 i ) override;
+
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ //===== XServiceInfo ====================================================
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+protected:
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+
+ std::unique_ptr<accessibility::AccessibleTextHelper> mpTextHelper;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+ virtual bool IsEditable(sal_Int64 nParentStates) override;
+ bool IsOpaque() const;
+
+ void CreateTextHelper();
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessiblePreviewHeaderCell.hxx b/sc/source/ui/inc/AccessiblePreviewHeaderCell.hxx
new file mode 100644
index 0000000000..f3317b4133
--- /dev/null
+++ b/sc/source/ui/inc/AccessiblePreviewHeaderCell.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+#include <com/sun/star/accessibility/XAccessibleValue.hpp>
+#include <tools/gen.hxx>
+#include <address.hxx>
+#include <cppuhelper/implbase1.hxx>
+
+class ScPreviewShell;
+class ScPreviewTableInfo;
+namespace accessibility {
+ class AccessibleTextHelper;
+}
+
+typedef cppu::ImplHelper1< css::accessibility::XAccessibleValue>
+ ScAccessiblePreviewHeaderCellImpl;
+
+class ScAccessiblePreviewHeaderCell :
+ public ScAccessibleContextBase,
+ public ScAccessiblePreviewHeaderCellImpl
+{
+public:
+ ScAccessiblePreviewHeaderCell( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const ScAddress& rCellPos, bool bIsColHdr, bool bIsRowHdr,
+ sal_Int32 nIndex );
+
+protected:
+ virtual ~ScAccessiblePreviewHeaderCell() override;
+
+ using ScAccessibleContextBase::IsDefunc;
+
+public:
+ using ScAccessibleContextBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+ //===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== XInterface =====================================================
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ //===== XAccessibleValue ================================================
+
+ virtual css::uno::Any SAL_CALL getCurrentValue() override;
+ virtual sal_Bool SAL_CALL setCurrentValue( const css::uno::Any& aNumber ) override;
+ virtual css::uno::Any SAL_CALL getMaximumValue() override;
+ virtual css::uno::Any SAL_CALL getMinimumValue() override;
+ virtual css::uno::Any SAL_CALL getMinimumIncrement() override;
+
+ //===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual void SAL_CALL grabFocus() override;
+
+ //===== XAccessibleContext ==============================================
+
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleChild( sal_Int64 i ) override;
+ virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override;
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ //===== XServiceInfo ====================================================
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ ///===== XTypeProvider ===================================================
+
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+protected:
+ virtual OUString createAccessibleDescription() override;
+ virtual OUString createAccessibleName() override;
+
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+ std::unique_ptr<accessibility::AccessibleTextHelper> mxTextHelper;
+ sal_Int32 mnIndex;
+ ScAddress maCellPos;
+ bool mbColumnHeader;
+ bool mbRowHeader;
+ mutable std::unique_ptr<ScPreviewTableInfo> mpTableInfo;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+
+ void CreateTextHelper();
+ void FillTableInfo() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessiblePreviewTable.hxx b/sc/source/ui/inc/AccessiblePreviewTable.hxx
new file mode 100644
index 0000000000..34fa7a8d36
--- /dev/null
+++ b/sc/source/ui/inc/AccessiblePreviewTable.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 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+class ScPreviewShell;
+class ScPreviewTableInfo;
+
+typedef cppu::ImplHelper1< css::accessibility::XAccessibleTable>
+ ScAccessiblePreviewTableImpl;
+
+class ScAccessiblePreviewTable :
+ public ScAccessibleContextBase,
+ public ScAccessiblePreviewTableImpl
+{
+public:
+ ScAccessiblePreviewTable( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, sal_Int32 nIndex );
+
+protected:
+ virtual ~ScAccessiblePreviewTable() override;
+
+ using ScAccessibleContextBase::IsDefunc;
+
+public:
+ using ScAccessibleContextBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+ //===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== XInterface =====================================================
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ //===== 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_Int64 SAL_CALL getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleRow( sal_Int64 nChildIndex ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumn( sal_Int64 nChildIndex ) override;
+
+ //===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual void SAL_CALL grabFocus() override;
+
+ //===== XAccessibleContext ==============================================
+
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleChild( sal_Int64 i ) override;
+ virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override;
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ //===== XServiceInfo ====================================================
+
+ virtual OUString SAL_CALL getImplementationName() 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;
+
+protected:
+ virtual OUString createAccessibleDescription() override;
+ virtual OUString createAccessibleName() override;
+
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+ sal_Int32 mnIndex;
+ mutable std::unique_ptr<ScPreviewTableInfo> mpTableInfo;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+
+ void FillTableInfo() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleSpreadsheet.hxx b/sc/source/ui/inc/AccessibleSpreadsheet.hxx
new file mode 100644
index 0000000000..5cf8b7018b
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleSpreadsheet.hxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <rtl/ref.hxx>
+
+#include "AccessibleTableBase.hxx"
+#include "viewdata.hxx"
+
+#include <vector>
+
+#include <rangelst.hxx>
+#include <map>
+
+class ScMyAddress : public ScAddress
+{
+public:
+ ScMyAddress(SCCOL nColP, SCROW nRowP, SCTAB nTabP) : ScAddress(nColP, nRowP, nTabP) {}
+ ScMyAddress(const ScAddress& rAddress) : ScAddress(rAddress) {}
+
+ bool operator< ( const ScMyAddress& rAddress ) const
+ {
+ if( Row() != rAddress.Row() )
+ return (Row() < rAddress.Row());
+ else
+ return (Col() < rAddress.Col());
+ }
+};
+
+class ScTabViewShell;
+class ScAccessibleDocument;
+class ScAccessibleCell;
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleTable</code> service.
+*/
+class ScAccessibleSpreadsheet final : public ScAccessibleTableBase
+{
+public:
+ ScAccessibleSpreadsheet(
+ ScAccessibleDocument* pAccDoc,
+ ScTabViewShell* pViewShell,
+ SCTAB nTab,
+ ScSplitPos eSplitPos);
+
+ using ScAccessibleTableBase::disposing;
+
+ virtual void SAL_CALL disposing() override;
+
+ void CompleteSelectionChanged(bool bNewState);
+
+ void LostFocus();
+ void GotFocus();
+
+ void BoundingBoxChanged();
+ void VisAreaChanged();
+ void FireFirstCellFocus();
+
+ bool IsScAddrFormulaSel (const ScAddress &addr) const;
+ bool IsFormulaMode();
+ ScMyAddress CalcScAddressFromRangeList(ScRangeList *pMarkedRanges,sal_Int32 nSelectedChildIndex);
+ static bool CalcScRangeDifferenceMax(const ScRange & rSrc, const ScRange & rDest,int nMax,std::vector<ScMyAddress> &vecRet,int &nSize);
+ static bool CalcScRangeListDifferenceMax(ScRangeList *pSrc,ScRangeList *pDest,int nMax,std::vector<ScMyAddress> &vecRet);
+
+private:
+ ScAccessibleSpreadsheet(
+ ScAccessibleSpreadsheet& rParent,
+ const ScRange& rRange );
+
+ virtual ~ScAccessibleSpreadsheet() override;
+
+ void ConstructScAccessibleSpreadsheet(
+ ScAccessibleDocument* pAccDoc,
+ ScTabViewShell* pViewShell,
+ SCTAB nTab,
+ ScSplitPos eSplitPos);
+
+ using ScAccessibleTableBase::IsDefunc;
+
+ ///===== SfxListener =====================================================
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== XAccessibleTable ================================================
+
+ /// Returns the row headers as an AccessibleTable.
+ virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL
+ getAccessibleRowHeaders( ) override;
+
+ /// Returns the column headers as an AccessibleTable.
+ virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL
+ getAccessibleColumnHeaders( ) override;
+
+ /// Returns the selected rows in a table.
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleRows( ) override;
+
+ /// Returns the selected columns in a table.
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleColumns( ) override;
+
+ /// Returns a boolean value indicating whether the specified row is selected.
+ virtual sal_Bool SAL_CALL
+ isAccessibleRowSelected( sal_Int32 nRow ) override;
+
+ /// Returns a boolean value indicating whether the specified column is selected.
+ virtual sal_Bool SAL_CALL
+ isAccessibleColumnSelected( sal_Int32 nColumn ) override;
+
+ /// Returns the Accessible at a specified row and column in the table.
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ rtl::Reference<ScAccessibleCell> GetAccessibleCellAt(sal_Int32 nRow, sal_Int32 nColumn);
+
+ /// Returns a boolean value indicating whether the accessible at a specified row and column is selected.
+ virtual sal_Bool SAL_CALL
+ isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ ///===== XAccessibleComponent ============================================
+
+ virtual css::uno::Reference< css::accessibility::XAccessible >
+ SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& rPoint ) override;
+
+ virtual void SAL_CALL grabFocus( ) override;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ ///===== XAccessibleContext ==============================================
+
+ /// Return NULL to indicate that an empty relation set.
+ virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ /// Return the set of current states.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleStateSet() override;
+
+ ///===== XAccessibleSelection ===========================================
+
+ virtual void SAL_CALL
+ selectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ virtual void SAL_CALL
+ clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL
+ selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int64 SAL_CALL
+ getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference<css::accessibility::XAccessible > SAL_CALL
+ getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL
+ deselectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ ///===== XServiceInfo ====================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Returns a list of all supported services.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+ ///===== XAccessibleEventBroadcaster =====================================
+
+ /** Add listener that is informed of future changes of name,
+ description and so on events.
+ */
+ virtual void SAL_CALL
+ addAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& xListener) override;
+
+ //===== XAccessibleTableSelection ============================================
+
+ virtual sal_Bool SAL_CALL selectRow( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL selectColumn( sal_Int32 column ) override;
+ virtual sal_Bool SAL_CALL unselectRow( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL unselectColumn( sal_Int32 column ) override;
+
+ /// Return the object's current bounding box relative to the desktop.
+ virtual AbsoluteScreenPixelRectangle GetBoundingBoxOnScreen() const override;
+
+ /// Return the object's current bounding box relative to the parent object.
+ virtual tools::Rectangle GetBoundingBox() const override;
+
+ bool IsDefunc(sal_Int64 nParentStates);
+ bool IsEditable();
+ bool IsFocused();
+ bool IsCompleteSheetSelected();
+
+ void SelectCell(sal_Int32 nRow, sal_Int32 nCol, bool bDeselect);
+
+ static ScDocument* GetDocument(ScTabViewShell* pViewShell);
+
+ void RemoveSelection(const ScMarkData &refScMarkData);
+ void CommitFocusCell(const ScAddress &aNewCell);
+
+ sal_Int32 GetRowAll() const { return m_nMaxY - m_nMinY + 1 ; }
+ sal_uInt16 GetColAll() const { return m_nMaxX - m_nMinX + 1; }
+ void NotifyRefMode();
+ void RemoveFormulaSelection(bool bRemoveAll = false);
+ bool CheckChildIndex(sal_Int64) const;
+ ScAddress GetChildIndexAddress(sal_Int64) const;
+ sal_Int64 GetAccessibleIndexFormula( sal_Int32 nRow, sal_Int32 nColumn );
+ bool GetFormulaCurrentFocusCell(ScAddress &addr);
+
+ ScTabViewShell* mpViewShell;
+ std::unique_ptr<ScRangeList> mpMarkedRanges;
+ ScAccessibleDocument* mpAccDoc;
+ rtl::Reference<ScAccessibleCell> mpAccCell;
+ ScSplitPos meSplitPos;
+ ScAddress maActiveCell;
+ SCTAB mnTab;
+ bool mbIsSpreadsheet;
+ bool mbDelIns;
+ bool mbIsFocusSend;
+ typedef std::map<ScMyAddress,css::uno::Reference< css::accessibility::XAccessible > >
+ MAP_ADDR_XACC;
+ MAP_ADDR_XACC m_mapSelectionSend;
+ bool m_bFormulaMode;
+ bool m_bFormulaLastMode;
+ ScAddress m_aFormulaActiveCell;
+ MAP_ADDR_XACC m_mapFormulaSelectionSend;
+ std::vector<ScMyAddress> m_vecFormulaLastMyAddr;
+ rtl::Reference<ScAccessibleCell> m_pAccFormulaCell;
+ sal_uInt16 m_nMinX;
+ sal_uInt16 m_nMaxX;
+ sal_Int32 m_nMinY;
+ sal_Int32 m_nMaxY;
+ ScRange m_aLastWithInMarkRange;
+ OUString m_strCurCellValue;
+ ScRangeList m_LastMarkedRanges;
+ OUString m_strOldTabName;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleTableBase.hxx b/sc/source/ui/inc/AccessibleTableBase.hxx
new file mode 100644
index 0000000000..6fac6ce537
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleTableBase.hxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "AccessibleContextBase.hxx"
+#include <address.hxx>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleTableSelection.hpp>
+#include <cppuhelper/implbase2.hxx>
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleTable</code> service.
+*/
+
+typedef cppu::ImplHelper2< css::accessibility::XAccessibleTable,
+ css::accessibility::XAccessibleSelection>
+ ScAccessibleTableBaseImpl;
+
+class ScAccessibleTableBase :
+ public ScAccessibleContextBase,
+ public css::accessibility::XAccessibleTableSelection,
+ public ScAccessibleTableBaseImpl
+{
+public:
+ //===== internal ========================================================
+ ScAccessibleTableBase(
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScDocument* pDoc,
+ const ScRange& rRange);
+protected:
+ virtual ~ScAccessibleTableBase() override;
+public:
+
+ using ScAccessibleContextBase::disposing;
+ virtual void SAL_CALL disposing() override;
+
+ ///===== XInterface =====================================================
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+
+ virtual void SAL_CALL acquire() noexcept override;
+
+ virtual void SAL_CALL release() noexcept override;
+
+ ///===== XAccessibleTable ================================================
+
+ /// Returns the number of rows in the table.
+ virtual sal_Int32 SAL_CALL
+ getAccessibleRowCount( ) override;
+
+ /// Returns the number of columns in the table.
+ virtual sal_Int32 SAL_CALL
+ getAccessibleColumnCount( ) override;
+
+ /// Returns the description of the specified row in the table.
+ virtual OUString SAL_CALL
+ getAccessibleRowDescription( sal_Int32 nRow ) override;
+
+ /// Returns the description text of the specified column in the table.
+ virtual OUString SAL_CALL
+ getAccessibleColumnDescription( sal_Int32 nColumn ) override;
+
+ /** Returns the number of rows occupied by the Accessible at a specified row and column in the table.
+ Returns 1 if it is only a cell and the number of rows the cell is merged if the cell is a merged cell.
+ */
+ virtual sal_Int32 SAL_CALL
+ getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /** Returns the number of columns occupied by the Accessible at a specified row and column in the table.
+ Returns 1 if it is only a cell and the number of columns the cell is merged if the cell is a merged cell.
+ */
+ virtual sal_Int32 SAL_CALL
+ getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /// Returns the row headers as an AccessibleTable.
+ virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL
+ getAccessibleRowHeaders( ) override;
+
+ /// Returns the column headers as an AccessibleTable.
+ virtual css::uno::Reference< css::accessibility::XAccessibleTable > SAL_CALL
+ getAccessibleColumnHeaders( ) override;
+
+ /// Returns the selected rows in a table.
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleRows( ) override;
+
+ /// Returns the selected columns in a table.
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleColumns( ) override;
+
+ /// Returns a boolean value indicating whether the specified row is selected.
+ virtual sal_Bool SAL_CALL
+ isAccessibleRowSelected( sal_Int32 nRow ) override;
+
+ /// Returns a boolean value indicating whether the specified column is selected.
+ virtual sal_Bool SAL_CALL
+ isAccessibleColumnSelected( sal_Int32 nColumn ) override;
+
+ /// Returns the Accessible at a specified row and column in the table.
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /// Returns the caption for the table.
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCaption( ) override;
+
+ /// Returns the summary description of the table.
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleSummary( ) override;
+
+ /// Returns a boolean value indicating whether the accessible at a specified row and column is selected.
+ virtual sal_Bool SAL_CALL
+ isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ ///===== XAccessibleExtendedTable ========================================
+
+ /// Returns the index of the cell on the given position.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn ) override;
+
+ /// Returns the row number of an index in the table.
+ virtual sal_Int32 SAL_CALL
+ getAccessibleRow( sal_Int64 nChildIndex ) override;
+
+ /// Returns the column number of an index in the table.
+ virtual sal_Int32 SAL_CALL
+ getAccessibleColumn( sal_Int64 nChildIndex ) override;
+
+ //===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+ /// override to calculate this on demand
+ virtual sal_Int64 SAL_CALL
+ getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ /// override to calculate this on demand
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild(sal_Int64 nIndex) override;
+ virtual sal_Bool SAL_CALL selectRow( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL selectColumn( sal_Int32 column ) override;
+ virtual sal_Bool SAL_CALL unselectRow( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL unselectColumn( sal_Int32 column ) override;
+
+protected:
+ /// Return this object's description.
+ virtual OUString
+ createAccessibleDescription() override;
+
+ /// Return the object's current name.
+ virtual OUString
+ createAccessibleName() override;
+
+public:
+ /// Return NULL to indicate that an empty relation set.
+ virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ /// Return the set of current states.
+ // perhaps sometimes to be implemented
+ virtual sal_Int64 SAL_CALL
+ getAccessibleStateSet() override;
+
+ ///===== XAccessibleSelection ===========================================
+
+ virtual void SAL_CALL
+ selectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL
+ isAccessibleChildSelected( sal_Int64 nChildIndex ) override;
+
+ virtual void SAL_CALL
+ clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL
+ selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int64 SAL_CALL
+ getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL
+ deselectAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+
+ ///===== XServiceInfo ===================================================
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ ///===== XTypeProvider ===================================================
+
+ /// returns the possible types
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+
+ /** Returns an implementation id.
+ */
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL
+ getImplementationId() override;
+
+protected:
+ /// contains the range of the table, because it could be a subrange of the complete table
+ ScRange maRange;
+
+ ScDocument* mpDoc;
+
+ void CommitTableModelChange(sal_Int32 nStartRow, sal_Int32 nStartCol, sal_Int32 nEndRow, sal_Int32 nEndCol, sal_uInt16 nId);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AccessibleText.hxx b/sc/source/ui/inc/AccessibleText.hxx
new file mode 100644
index 0000000000..b4706a09c8
--- /dev/null
+++ b/sc/source/ui/inc/AccessibleText.hxx
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <textuno.hxx>
+#include <address.hxx>
+#include "viewdata.hxx"
+#include <editeng/svxenum.hxx>
+#include <svl/SfxBroadcaster.hxx>
+
+#include <memory>
+
+class ScDocShell;
+class ScViewForwarder;
+class ScEditObjectViewForwarder;
+class ScPreviewViewForwarder;
+class ScEditViewForwarder;
+class ScPreviewShell;
+class EditTextObject;
+class ScCsvViewForwarder;
+class ScAccessibleCell;
+class ScTextWnd;
+
+class ScAccessibleTextData : public SfxListener
+{
+public:
+ ScAccessibleTextData() {}
+
+ virtual ScAccessibleTextData* Clone() const = 0;
+
+ virtual void Notify( SfxBroadcaster& /* rBC */, const SfxHint& /* rHint */ ) override {}
+
+ virtual SvxTextForwarder* GetTextForwarder() = 0;
+ virtual SvxViewForwarder* GetViewForwarder() = 0;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate ) = 0;
+ SfxBroadcaster& GetBroadcaster() const { return maBroadcaster; }
+
+ virtual void UpdateData() = 0;
+ DECL_LINK( NotifyHdl, EENotify&, void );
+
+private:
+ mutable SfxBroadcaster maBroadcaster;
+
+};
+
+class ScAccessibleCellBaseTextData : public ScAccessibleTextData,
+ public ScCellTextData
+{
+public:
+ ScAccessibleCellBaseTextData(ScDocShell* pDocShellP,
+ const ScAddress& rP)
+ : ScCellTextData(pDocShellP, rP) {}
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override { ScCellTextData::Notify(rBC, rHint); }
+
+ virtual void UpdateData() override { ScCellTextData::UpdateData(); }
+};
+
+// ScAccessibleCellTextData: shared data between sub objects of an accessible cell text object
+
+class ScAccessibleCellTextData : public ScAccessibleCellBaseTextData
+{
+public:
+ ScAccessibleCellTextData(ScTabViewShell* pViewShell,
+ const ScAddress& rP, ScSplitPos eSplitPos, ScAccessibleCell* pAccCell);
+ virtual ~ScAccessibleCellTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate ) override;
+
+private:
+ std::unique_ptr<ScViewForwarder> mpViewForwarder;
+ ScTabViewShell* mpViewShell;
+ ScSplitPos meSplitPos;
+ ScAccessibleCell* mpAccessibleCell;
+
+ using ScAccessibleCellBaseTextData::GetDocShell;
+ static ScDocShell* GetDocShell(ScTabViewShell* pViewShell);
+};
+
+class ScAccessibleEditObjectTextData : public ScAccessibleTextData
+{
+public:
+ // Add a para to indicate whether the object is cloned
+ ScAccessibleEditObjectTextData(EditView* pEditView, OutputDevice* pWin, bool isClone = false);
+ virtual ~ScAccessibleEditObjectTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate ) override;
+
+ virtual void UpdateData() override { }
+
+ DECL_LINK( NotifyHdl, EENotify&, void );
+protected:
+ std::unique_ptr<ScEditObjectViewForwarder> mpViewForwarder;
+ std::unique_ptr<ScEditViewForwarder> mpEditViewForwarder;
+ EditView* mpEditView;
+ EditEngine* mpEditEngine;
+ std::unique_ptr<SvxEditEngineForwarder> mpForwarder;
+ VclPtr<OutputDevice> mpWindow;
+ bool mbIsCloned;
+};
+
+class ScAccessibleEditLineTextData : public ScAccessibleEditObjectTextData
+{
+public:
+ ScAccessibleEditLineTextData(EditView* pEditView,
+ OutputDevice* pWin,
+ ScTextWnd* pTextWnd);
+ virtual ~ScAccessibleEditLineTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate ) override;
+
+ void Dispose();
+ void TextChanged();
+ void StartEdit();
+ void EndEdit();
+private:
+ void ResetEditMode();
+
+ ScTextWnd* mpTxtWnd;
+ bool mbEditEngineCreated;
+};
+
+class ScAccessiblePreviewCellTextData : public ScAccessibleCellBaseTextData
+{
+public:
+ ScAccessiblePreviewCellTextData(ScPreviewShell* pViewShell,
+ const ScAddress& rP);
+ virtual ~ScAccessiblePreviewCellTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool /* bCreate */ ) override { return nullptr; }
+
+private:
+ std::unique_ptr<ScPreviewViewForwarder> mpViewForwarder;
+ ScPreviewShell* mpViewShell;
+
+ using ScAccessibleCellBaseTextData::GetDocShell;
+ static ScDocShell* GetDocShell(ScPreviewShell* pViewShell);
+};
+
+class ScAccessiblePreviewHeaderCellTextData : public ScAccessibleCellBaseTextData
+{
+public:
+ ScAccessiblePreviewHeaderCellTextData(ScPreviewShell* pViewShell,
+ OUString aText, const ScAddress& rP, bool bColHeader, bool bRowHeader);
+ virtual ~ScAccessiblePreviewHeaderCellTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool /* bCreate */ ) override { return nullptr; }
+
+private:
+ std::unique_ptr<ScPreviewViewForwarder> mpViewForwarder;
+ ScPreviewShell* mpViewShell;
+ OUString maText;
+ bool mbColHeader;
+ bool mbRowHeader;
+
+ using ScAccessibleCellBaseTextData::GetDocShell;
+ static ScDocShell* GetDocShell(ScPreviewShell* pViewShell);
+};
+
+class ScAccessibleHeaderTextData : public ScAccessibleTextData
+{
+public:
+ ScAccessibleHeaderTextData(ScPreviewShell* pViewShell,
+ const EditTextObject* pEditObj, SvxAdjust eAdjust);
+ virtual ~ScAccessibleHeaderTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool /* bCreate */ ) override { return nullptr; }
+
+ virtual void UpdateData() override { }
+private:
+ std::unique_ptr<ScPreviewViewForwarder> mxViewForwarder;
+ ScPreviewShell* mpViewShell;
+ std::unique_ptr<ScEditEngineDefaulter> mpEditEngine;
+ std::unique_ptr<SvxEditEngineForwarder> mpForwarder;
+ ScDocShell* mpDocSh;
+ const EditTextObject* mpEditObj;
+ bool mbDataValid;
+ SvxAdjust meAdjust;
+};
+
+class ScAccessibleNoteTextData : public ScAccessibleTextData
+{
+public:
+ ScAccessibleNoteTextData(ScPreviewShell* pViewShell,
+ OUString sText, const ScAddress& aCellPos, bool bMarkNote);
+ virtual ~ScAccessibleNoteTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool /* bCreate */ ) override { return nullptr; }
+
+ virtual void UpdateData() override { }
+private:
+ std::unique_ptr<ScPreviewViewForwarder> mxViewForwarder;
+ ScPreviewShell* mpViewShell;
+ std::unique_ptr<ScEditEngineDefaulter> mpEditEngine;
+ std::unique_ptr<SvxEditEngineForwarder> mpForwarder;
+ ScDocShell* mpDocSh;
+ OUString msText;
+ ScAddress maCellPos;
+ bool mbMarkNote;
+ bool mbDataValid;
+};
+
+class ScAccessibleCsvTextData : public ScAccessibleTextData
+{
+private:
+ typedef ::std::unique_ptr< SvxTextForwarder > TextForwarderPtr;
+ typedef ::std::unique_ptr< ScCsvViewForwarder > ViewForwarderPtr;
+
+ VclPtr<OutputDevice> mpWindow;
+ EditEngine* mpEditEngine;
+ TextForwarderPtr mpTextForwarder;
+ ViewForwarderPtr mpViewForwarder;
+ OUString maCellText;
+ Size maCellSize;
+
+public:
+ explicit ScAccessibleCsvTextData(
+ OutputDevice* pWindow,
+ EditEngine* pEditEngine,
+ OUString aCellText,
+ const Size& rCellSize );
+ virtual ~ScAccessibleCsvTextData() override;
+
+ virtual ScAccessibleTextData* Clone() const override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual SvxViewForwarder* GetViewForwarder() override;
+ virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate ) override;
+
+ virtual void UpdateData() override {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/AnalysisOfVarianceDialog.hxx b/sc/source/ui/inc/AnalysisOfVarianceDialog.hxx
new file mode 100644
index 0000000000..35c53e7277
--- /dev/null
+++ b/sc/source/ui/inc/AnalysisOfVarianceDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "viewdata.hxx"
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class FormulaTemplate;
+class AddressWalkerWriter;
+
+class ScAnalysisOfVarianceDialog : public ScStatisticsInputOutputDialog
+{
+private:
+ enum AnovaFactor
+ {
+ SINGLE_FACTOR,
+ TWO_FACTOR
+ };
+
+ DECL_LINK(FactorChanged, weld::Toggleable&, void);
+ void FactorChanged();
+
+ AnovaFactor meFactor;
+
+ std::unique_ptr<weld::SpinButton> mxAlphaField;
+ std::unique_ptr<weld::RadioButton> mxSingleFactorRadio;
+ std::unique_ptr<weld::RadioButton> mxTwoFactorRadio;
+ std::unique_ptr<weld::SpinButton> mxRowsPerSampleField;
+
+ static void RowColumn(ScRangeList& rRangeList, AddressWalkerWriter& aOutput,
+ FormulaTemplate& aTemplate, const OUString& sFormula,
+ GroupedBy aGroupedBy, ScRange* pResultRange);
+
+ void AnovaSingleFactor(AddressWalkerWriter& output, FormulaTemplate& aTemplate);
+ void AnovaTwoFactor(AddressWalkerWriter& output, FormulaTemplate& aTemplate);
+
+public:
+ ScAnalysisOfVarianceDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScAnalysisOfVarianceDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/ChartRangeSelectionListener.hxx b/sc/source/ui/inc/ChartRangeSelectionListener.hxx
new file mode 100644
index 0000000000..b7033c4137
--- /dev/null
+++ b/sc/source/ui/inc/ChartRangeSelectionListener.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <comphelper/compbase.hxx>
+#include <com/sun/star/view/XSelectionChangeListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+class ScTabViewShell;
+
+typedef comphelper::WeakComponentImplHelper<
+ css::view::XSelectionChangeListener,
+ css::lang::XServiceInfo >
+ ScChartRangeSelectionListener_Base;
+
+class ScChartRangeSelectionListener :
+ public ScChartRangeSelectionListener_Base
+{
+public:
+ explicit ScChartRangeSelectionListener( ScTabViewShell * pViewShell );
+ virtual ~ScChartRangeSelectionListener() override;
+
+protected:
+ // ____ XSelectionChangeListener ____
+ virtual void SAL_CALL selectionChanged(
+ const css::lang::EventObject& aEvent ) override;
+
+ // ____ XEventListener (base of XSelectionChangeListener) ____
+ virtual void SAL_CALL disposing(
+ const css::lang::EventObject& Source ) override;
+
+ // ____ WeakComponentImplHelperBase ____
+ // is called when dispose() is called at this component
+ virtual void disposing(std::unique_lock<std::mutex>&) 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:
+ ScTabViewShell * m_pViewShell;
+};
+
+// INCLUDED_SC_SOURCE_UI_INC_CHARTRANGESELECTIONLISTENER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/ChiSquareTestDialog.hxx b/sc/source/ui/inc/ChiSquareTestDialog.hxx
new file mode 100644
index 0000000000..20e7d696c9
--- /dev/null
+++ b/sc/source/ui/inc/ChiSquareTestDialog.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class ScChiSquareTestDialog : public ScStatisticsInputOutputDialog
+{
+public:
+ ScChiSquareTestDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScChiSquareTestDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/ChildWindowWrapper.hxx b/sc/source/ui/inc/ChildWindowWrapper.hxx
new file mode 100644
index 0000000000..5fb038df27
--- /dev/null
+++ b/sc/source/ui/inc/ChildWindowWrapper.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/.
+ *
+ */
+
+#pragma once
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/childwin.hxx>
+#include <osl/diagnose.h>
+
+#include "tabvwsh.hxx"
+
+template <sal_Int16 WindowID>
+class ChildControllerWrapper : public SfxChildWindow
+{
+public:
+ ChildControllerWrapper(vcl::Window* pParentP, sal_uInt16 nId,
+ SfxBindings* pBindings, const SfxChildWinInfo* pInfo)
+ : SfxChildWindow(pParentP, nId)
+ {
+ ScTabViewShell* pViewShell = getTabViewShell( pBindings );
+ if (!pViewShell)
+ pViewShell = dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() );
+ OSL_ENSURE(pViewShell, "Missing view shell!");
+
+ if (pViewShell)
+ SetController(pViewShell->CreateRefDialogController(pBindings, this, pInfo, pParentP->GetFrameWeld(), WindowID));
+
+ if (pViewShell && !GetController())
+ pViewShell->GetViewFrame().SetChildWindow( nId, false );
+ }
+
+ static std::unique_ptr<SfxChildWindow> CreateImpl(
+ vcl::Window *pParent, sal_uInt16 nId,
+ SfxBindings *pBindings, SfxChildWinInfo* pInfo )
+ {
+ return std::make_unique<ChildControllerWrapper>(pParent, nId, pBindings, pInfo);
+ }
+
+ static void RegisterChildWindow (
+ bool bVisible = false,
+ SfxModule* pModule = nullptr,
+ SfxChildWindowFlags nFlags = SfxChildWindowFlags::NONE)
+ {
+ SfxChildWinFactory aFactory(ChildControllerWrapper::CreateImpl, WindowID, CHILDWIN_NOPOS );
+ aFactory.aInfo.nFlags |= nFlags;
+ aFactory.aInfo.bVisible = bVisible;
+ SfxChildWindow::RegisterChildWindow(pModule, aFactory);
+ }
+
+ static sal_uInt16 GetChildWindowId()
+ {
+ return WindowID;
+ }
+
+private:
+ static ScTabViewShell* getTabViewShell( const SfxBindings *pBindings )
+ {
+ if( !pBindings )
+ return nullptr;
+ SfxDispatcher* pDispacher = pBindings ->GetDispatcher();
+ if( !pDispacher )
+ return nullptr;
+ SfxViewFrame* pFrame = pDispacher->GetFrame();
+ if( !pFrame )
+ return nullptr;
+ SfxViewShell* pViewShell = pFrame->GetViewShell();
+ if( !pViewShell )
+ return nullptr;
+ return dynamic_cast<ScTabViewShell*>( pViewShell );
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/CorrelationDialog.hxx b/sc/source/ui/inc/CorrelationDialog.hxx
new file mode 100644
index 0000000000..5200c3f55b
--- /dev/null
+++ b/sc/source/ui/inc/CorrelationDialog.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "MatrixComparisonGenerator.hxx"
+
+class ScCorrelationDialog : public ScMatrixComparisonGenerator
+{
+public:
+ ScCorrelationDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData);
+
+ virtual void Close() override;
+
+protected:
+ virtual OUString getLabel() override;
+ virtual OUString getTemplate() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/CovarianceDialog.hxx b/sc/source/ui/inc/CovarianceDialog.hxx
new file mode 100644
index 0000000000..181bebeac1
--- /dev/null
+++ b/sc/source/ui/inc/CovarianceDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include "MatrixComparisonGenerator.hxx"
+
+class ScCovarianceDialog : public ScMatrixComparisonGenerator
+{
+public:
+ ScCovarianceDialog(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData);
+
+ virtual void Close() override;
+
+protected:
+ virtual OUString getLabel() override;
+ virtual OUString getTemplate() override;
+ virtual TranslateId GetUndoNameId() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/DescriptiveStatisticsDialog.hxx b/sc/source/ui/inc/DescriptiveStatisticsDialog.hxx
new file mode 100644
index 0000000000..dd2488450b
--- /dev/null
+++ b/sc/source/ui/inc/DescriptiveStatisticsDialog.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class ScDescriptiveStatisticsDialog : public ScStatisticsInputOutputDialog
+{
+public:
+ ScDescriptiveStatisticsDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScDescriptiveStatisticsDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/DrawModelBroadcaster.hxx b/sc/source/ui/inc/DrawModelBroadcaster.hxx
new file mode 100644
index 0000000000..bd4c2e0b1b
--- /dev/null
+++ b/sc/source/ui/inc/DrawModelBroadcaster.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 .
+ */
+
+#pragma once
+
+#include <svl/lstner.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/document/XEventBroadcaster.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <mutex>
+#include <unordered_map>
+
+class SdrModel;
+
+class ScDrawModelBroadcaster final : public SfxListener,
+ public ::cppu::WeakImplHelper< css::document::XShapeEventBroadcaster >
+{
+ mutable std::mutex maListenerMutex;
+ ::comphelper::OInterfaceContainerHelper4<css::document::XEventListener> maEventListeners;
+ std::unordered_map<css::uno::Reference< css::drawing::XShape >, css::uno::Reference< css::document::XShapeEventListener >> maShapeListeners;
+ SdrModel *mpDrawModel;
+
+public:
+
+ ScDrawModelBroadcaster( SdrModel *pDrawModel );
+ virtual ~ScDrawModelBroadcaster() override;
+
+ // css::document::XEventBroadcaster
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::document::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::document::XEventListener >& xListener ) override;
+ // css::document::XShapeEventBroadcaster
+ virtual void SAL_CALL addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+ virtual void SAL_CALL removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/ExponentialSmoothingDialog.hxx b/sc/source/ui/inc/ExponentialSmoothingDialog.hxx
new file mode 100644
index 0000000000..54b8771678
--- /dev/null
+++ b/sc/source/ui/inc/ExponentialSmoothingDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "viewdata.hxx"
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class ScExponentialSmoothingDialog : public ScStatisticsInputOutputDialog
+{
+private:
+ std::unique_ptr<weld::SpinButton> mxSmoothingFactor;
+
+public:
+ ScExponentialSmoothingDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScExponentialSmoothingDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/FTestDialog.hxx b/sc/source/ui/inc/FTestDialog.hxx
new file mode 100644
index 0000000000..efd477ce08
--- /dev/null
+++ b/sc/source/ui/inc/FTestDialog.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsTwoVariableDialog.hxx"
+
+class ScFTestDialog : public ScStatisticsTwoVariableDialog
+{
+public:
+ ScFTestDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScFTestDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/FilterListBox.hxx b/sc/source/ui/inc/FilterListBox.hxx
new file mode 100644
index 0000000000..a17a1694a8
--- /dev/null
+++ b/sc/source/ui/inc/FilterListBox.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 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+
+#include <tools/solar.h>
+#include <vcl/weld.hxx>
+
+class ScGridWindow;
+struct ImplSVEvent;
+
+enum class ScFilterBoxMode
+{
+ DataSelect,
+ Scenario
+};
+
+class ScFilterListBox final : public std::enable_shared_from_this<ScFilterListBox>
+{
+private:
+ std::unique_ptr<weld::Builder> xBuilder;
+ std::unique_ptr<weld::Popover> xPopover;
+ std::unique_ptr<weld::TreeView> xTreeView;
+ VclPtr<ScGridWindow> pGridWin;
+ SCCOL nCol;
+ SCROW nRow;
+ bool bInit;
+ bool bCancelled;
+ bool bGridHadMouseCaptured;
+ sal_uLong nSel;
+ ScFilterBoxMode eMode;
+ ImplSVEvent* nAsyncSelectHdl;
+
+ DECL_LINK(SelectHdl, weld::TreeView&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(AsyncSelectHdl, void*, void);
+
+public:
+ ScFilterListBox(weld::Window* pParent, ScGridWindow* pGrid, SCCOL nNewCol, SCROW nNewRow,
+ ScFilterBoxMode eNewMode);
+ void popup_at_rect(weld::Widget* pParent, const tools::Rectangle& rRect)
+ {
+ xPopover->popup_at_rect(pParent, rRect);
+ }
+ void connect_closed(const Link<weld::Popover&, void>& rLink)
+ {
+ xPopover->connect_closed(rLink);
+ }
+ void popdown() { xPopover->popdown(); }
+ ~ScFilterListBox();
+
+ weld::TreeView& get_widget() { return *xTreeView; }
+
+ SCCOL GetCol() const { return nCol; }
+ SCROW GetRow() const { return nRow; }
+ ScFilterBoxMode GetMode() const { return eMode; }
+ void EndInit();
+ bool IsInInit() const { return bInit; }
+ bool MouseWasCaptured() const { return bGridHadMouseCaptured; }
+ void SetCancelled() { bCancelled = true; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/FourierAnalysisDialog.hxx b/sc/source/ui/inc/FourierAnalysisDialog.hxx
new file mode 100644
index 0000000000..f839b274e4
--- /dev/null
+++ b/sc/source/ui/inc/FourierAnalysisDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class ScFourierAnalysisDialog : public ScStatisticsInputOutputDialog
+{
+ ScAddress maLabelAddr;
+ ScRange maActualInputRange;
+ SCSIZE mnLen;
+
+ double mfMinMag;
+
+ bool mbUse3DAddresses : 1;
+ bool mbGroupedByColumn : 1;
+ bool mbWithLabels : 1;
+ bool mbInverse : 1;
+ bool mbPolar : 1;
+
+ std::unique_ptr<weld::CheckButton> mxWithLabelsCheckBox;
+ std::unique_ptr<weld::CheckButton> mxInverseCheckBox;
+ std::unique_ptr<weld::CheckButton> mxPolarCheckBox;
+ std::unique_ptr<weld::SpinButton> mxMinMagnitudeField;
+ std::unique_ptr<weld::Label> mxErrorMessage;
+
+public:
+ ScFourierAnalysisDialog(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData);
+
+ virtual ~ScFourierAnalysisDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+ virtual bool InputRangesValid() override;
+
+private:
+ void getOptions();
+ void getDataLabel(OUString& rLabel);
+ void genFormula(OUString& rFormula);
+
+ DECL_LINK(CheckBoxHdl, weld::Toggleable&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/IAnyRefDialog.hxx b/sc/source/ui/inc/IAnyRefDialog.hxx
new file mode 100644
index 0000000000..9db61ff04e
--- /dev/null
+++ b/sc/source/ui/inc/IAnyRefDialog.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <formula/IControlReferenceHandler.hxx>
+
+class ScRange;
+class ScDocument;
+class SfxObjectShell;
+namespace formula
+{
+class RefEdit;
+class RefButton;
+}
+class SAL_NO_VTABLE SAL_LOPLUGIN_ANNOTATE("crosscast") IAnyRefDialog
+ : public formula::IControlReferenceHandler
+{
+public:
+ virtual ~IAnyRefDialog() COVERITY_NOEXCEPT_FALSE {}
+
+ virtual void SetReference(const ScRange& rRef, ScDocument& rDoc) = 0;
+ virtual void RefInputStart(formula::RefEdit* pEdit, formula::RefButton* pButton = nullptr) = 0;
+ virtual void RefInputDone(bool bForced = false) = 0;
+ virtual bool IsTableLocked() const = 0;
+ virtual bool IsRefInputMode() const = 0;
+
+ virtual bool IsDocAllowed(SfxObjectShell* pDocSh) const = 0;
+ virtual void AddRefEntry() = 0;
+ virtual void SetActive() = 0;
+ virtual void ViewShellChanged() = 0;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/MatrixComparisonGenerator.hxx b/sc/source/ui/inc/MatrixComparisonGenerator.hxx
new file mode 100644
index 0000000000..ab1e51d7a9
--- /dev/null
+++ b/sc/source/ui/inc/MatrixComparisonGenerator.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "viewdata.hxx"
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class ScMatrixComparisonGenerator : public ScStatisticsInputOutputDialog
+{
+public:
+ ScMatrixComparisonGenerator(
+ SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pParent, ScViewData& rViewData,
+ const OUString& rUiXmlDescription, const OUString& rID);
+
+ virtual ~ScMatrixComparisonGenerator() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+
+ virtual OUString getLabel() = 0;
+ virtual OUString getTemplate() = 0;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/MovingAverageDialog.hxx b/sc/source/ui/inc/MovingAverageDialog.hxx
new file mode 100644
index 0000000000..216b67c8b4
--- /dev/null
+++ b/sc/source/ui/inc/MovingAverageDialog.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "viewdata.hxx"
+
+#include "StatisticsInputOutputDialog.hxx"
+
+class ScMovingAverageDialog : public ScStatisticsInputOutputDialog
+{
+private:
+ std::unique_ptr<weld::CheckButton> mxTrimRangeCheck;
+ std::unique_ptr<weld::SpinButton> mxIntervalSpin;
+
+public:
+ ScMovingAverageDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScMovingAverageDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/PivotLayoutDialog.hxx b/sc/source/ui/inc/PivotLayoutDialog.hxx
new file mode 100644
index 0000000000..c07693e351
--- /dev/null
+++ b/sc/source/ui/inc/PivotLayoutDialog.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+#include <dpobject.hxx>
+#include "viewdata.hxx"
+
+#include "PivotLayoutTreeList.hxx"
+#include "PivotLayoutTreeListData.hxx"
+#include "PivotLayoutTreeListLabel.hxx"
+
+class ScItemValue final
+{
+public:
+ OUString maName;
+ ScPivotFuncData maFunctionData;
+ ScItemValue* mpOriginalItemValue;
+
+ ScItemValue(OUString aName, SCCOL nColumn, PivotFunc nFunctionMask);
+ ScItemValue(const ScItemValue* pInputItemValue);
+
+ ~ScItemValue();
+};
+
+class ScPivotLayoutDialog : public ScAnyRefDlgController
+{
+public:
+ ScDPObject maPivotTableObject;
+
+ ScPivotLayoutTreeListBase* mpPreviouslyFocusedListBox;
+
+private:
+ ScViewData* mpViewData;
+ ScDocument& mrDocument;
+
+ bool mbNewPivotTable;
+
+ ScAddress::Details maAddressDetails;
+ bool mbDialogLostFocus;
+
+ formula::RefEdit* mpActiveEdit;
+ std::unique_ptr<ScPivotLayoutTreeListLabel> mxListBoxField;
+ std::unique_ptr<ScPivotLayoutTreeList> mxListBoxPage;
+ std::unique_ptr<ScPivotLayoutTreeList> mxListBoxColumn;
+ std::unique_ptr<ScPivotLayoutTreeList> mxListBoxRow;
+ std::unique_ptr<ScPivotLayoutTreeListData> mxListBoxData;
+
+ std::unique_ptr<weld::CheckButton> mxCheckIgnoreEmptyRows;
+ std::unique_ptr<weld::CheckButton> mxCheckTotalColumns;
+ std::unique_ptr<weld::CheckButton> mxCheckAddFilter;
+ std::unique_ptr<weld::CheckButton> mxCheckIdentifyCategories;
+ std::unique_ptr<weld::CheckButton> mxCheckTotalRows;
+ std::unique_ptr<weld::CheckButton> mxCheckDrillToDetail;
+ std::unique_ptr<weld::CheckButton> mxCheckExpandCollapse;
+
+ std::unique_ptr<weld::RadioButton> mxSourceRadioNamedRange;
+ std::unique_ptr<weld::RadioButton> mxSourceRadioSelection;
+
+ std::unique_ptr<weld::ComboBox> mxSourceListBox;
+ std::unique_ptr<formula::RefEdit> mxSourceEdit;
+ std::unique_ptr<formula::RefButton> mxSourceButton;
+
+ std::unique_ptr<weld::RadioButton> mxDestinationRadioNewSheet;
+ std::unique_ptr<weld::RadioButton> mxDestinationRadioNamedRange;
+ std::unique_ptr<weld::RadioButton> mxDestinationRadioSelection;
+
+ std::unique_ptr<weld::ComboBox> mxDestinationListBox;
+ std::unique_ptr<formula::RefEdit> mxDestinationEdit;
+ std::unique_ptr<formula::RefButton> mxDestinationButton;
+
+ std::unique_ptr<weld::Button> mxBtnOK;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+
+ std::unique_ptr<weld::Frame> mxSourceFrame;
+ std::unique_ptr<weld::Label> mxSourceLabel;
+ std::unique_ptr<weld::Frame> mxDestFrame;
+ std::unique_ptr<weld::Label> mxDestLabel;
+
+ DECL_LINK(CancelClicked, weld::Button&, void);
+ DECL_LINK(OKClicked, weld::Button&, void);
+ DECL_LINK(GetEditFocusHandler, formula::RefEdit&, void);
+ DECL_LINK(GetButtonFocusHandler, formula::RefButton&, void);
+ DECL_LINK(LoseEditFocusHandler, formula::RefEdit&, void);
+ DECL_LINK(LoseButtonFocusHandler, formula::RefButton&, void);
+ DECL_LINK(ToggleSource, weld::Toggleable&, void);
+ DECL_LINK(ToggleDestination, weld::Toggleable&, void);
+ DECL_LINK(SourceListSelected, weld::ComboBox&, void);
+ DECL_LINK(SourceEditModified, formula::RefEdit&, void);
+ void ToggleSource();
+ void ToggleDestination();
+ virtual void Close() override;
+
+ ScPivotParam maPivotParameters;
+
+ // UI
+ void SetupSource();
+ void SetupDestination();
+ void FillValuesToListBoxes();
+
+ // Other
+ bool GetDestination(ScRange& aDestinationRange, bool& bToNewSheet);
+
+public:
+ ScPivotLayoutDialog(SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow, weld::Window* pParent,
+ ScViewData* pViewData, const ScDPObject* pPivotTableObject, bool bCreateNewPivotTable);
+ virtual ~ScPivotLayoutDialog() override;
+
+ virtual void SetReference(const ScRange& rReferenceRange, ScDocument& rDocument) override;
+ virtual void SetActive() override;
+ virtual bool IsRefInputMode() const override;
+
+ void ItemInserted(const ScItemValue* pItemValue, ScPivotLayoutTreeList::SvPivotTreeListType eType);
+
+ void UpdateSourceRange();
+
+ void ApplyChanges();
+ void ApplySaveData(ScDPSaveData& rSaveData);
+ void ApplyLabelData(const ScDPSaveData& rSaveData);
+
+ ScItemValue* GetItem(SCCOL nColumn);
+ bool IsDataElement(SCCOL nColumn);
+
+ ScDPLabelData& GetLabelData(SCCOL nColumn);
+ ScDPLabelDataVector& GetLabelDataVector() { return maPivotParameters.maLabelArray;}
+ void PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/ui/inc/PivotLayoutTreeList.hxx b/sc/source/ui/inc/PivotLayoutTreeList.hxx
new file mode 100644
index 0000000000..14faa13d04
--- /dev/null
+++ b/sc/source/ui/inc/PivotLayoutTreeList.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include "PivotLayoutTreeListBase.hxx"
+#include <scabstdlg.hxx>
+
+class ScPivotLayoutTreeList : public ScPivotLayoutTreeListBase
+{
+private:
+ std::vector<std::unique_ptr<ScItemValue>> maItemValues;
+ std::vector<ScDPName> maDataFieldNames;
+
+ VclPtr<AbstractScDPSubtotalDlg> mpSubtotalDlg;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+
+public:
+ ScPivotLayoutTreeList(std::unique_ptr<weld::TreeView> xControl);
+ virtual ~ScPivotLayoutTreeList() override;
+
+ void Setup(ScPivotLayoutDialog* pParent, SvPivotTreeListType eType);
+ void FillFields(ScPivotFieldVector& rFieldVector);
+
+ virtual void InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget) override;
+
+protected:
+ void InsertEntryForItem(const ScItemValue* pItemValue, int nPosition);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/PivotLayoutTreeListBase.hxx b/sc/source/ui/inc/PivotLayoutTreeListBase.hxx
new file mode 100644
index 0000000000..51858de685
--- /dev/null
+++ b/sc/source/ui/inc/PivotLayoutTreeListBase.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/.
+ *
+ */
+
+#pragma once
+
+#include <vcl/transfer.hxx>
+#include <vcl/weld.hxx>
+#include <pivot.hxx>
+
+class ScPivotLayoutDialog;
+class ScPivotLayoutTreeListBase;
+class ScItemValue;
+
+class ScPivotLayoutTreeDropTarget : public DropTargetHelper
+{
+private:
+ ScPivotLayoutTreeListBase& m_rTreeView;
+
+ virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override;
+ virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override;
+
+public:
+ ScPivotLayoutTreeDropTarget(ScPivotLayoutTreeListBase& rTreeView);
+};
+
+class ScPivotLayoutTreeListBase
+{
+public:
+ enum SvPivotTreeListType
+ {
+ UNDEFINED,
+ LABEL_LIST,
+ PAGE_LIST,
+ ROW_LIST,
+ COLUMN_LIST
+ };
+
+protected:
+ std::unique_ptr<weld::TreeView> mxControl;
+ ScPivotLayoutTreeDropTarget maDropTargetHelper;
+ SvPivotTreeListType meType;
+ ScPivotLayoutDialog* mpParent;
+
+ DECL_LINK(GetFocusHdl, weld::Widget&, void);
+ DECL_LINK(MnemonicActivateHdl, weld::Widget&, bool);
+ DECL_LINK(LoseFocusHdl, weld::Widget&, void);
+
+public:
+ void Setup(ScPivotLayoutDialog* pParent);
+
+ ScPivotLayoutTreeListBase(std::unique_ptr<weld::TreeView> xControl,
+ SvPivotTreeListType eType = UNDEFINED);
+ weld::TreeView& get_widget() { return *mxControl; }
+ virtual ~ScPivotLayoutTreeListBase();
+
+ void PushEntriesToPivotFieldVector(ScPivotFieldVector& rVector);
+
+ void RemoveEntryForItem(const ScItemValue* pItemValue);
+
+ virtual void InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/PivotLayoutTreeListData.hxx b/sc/source/ui/inc/PivotLayoutTreeListData.hxx
new file mode 100644
index 0000000000..89e83abad5
--- /dev/null
+++ b/sc/source/ui/inc/PivotLayoutTreeListData.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/.
+ *
+ */
+
+#pragma once
+
+#include "PivotLayoutTreeListBase.hxx"
+#include <vector>
+#include <memory>
+#include <scabstdlg.hxx>
+
+class ScPivotLayoutTreeListData final : public ScPivotLayoutTreeListBase
+{
+private:
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+
+public:
+ ScPivotLayoutTreeListData(std::unique_ptr<weld::TreeView> xControl);
+ virtual ~ScPivotLayoutTreeListData() override;
+
+ void FillDataField(ScPivotFieldVector& rDataFields);
+ void PushDataFieldNames(std::vector<ScDPName>& rDataFieldNames);
+ virtual void InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget) override;
+
+private:
+ void InsertEntryForItem(ScItemValue* pItemValue, int nPosition);
+
+ void AdjustDuplicateCount(ScItemValue* pInputItemValue);
+
+ std::vector<std::unique_ptr<ScItemValue>> maDataItemValues;
+
+ VclPtr<AbstractScDPFunctionDlg> mpFunctionDlg;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/PivotLayoutTreeListLabel.hxx b/sc/source/ui/inc/PivotLayoutTreeListLabel.hxx
new file mode 100644
index 0000000000..d551798e3b
--- /dev/null
+++ b/sc/source/ui/inc/PivotLayoutTreeListLabel.hxx
@@ -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/.
+ *
+ */
+
+#pragma once
+
+#include "PivotLayoutTreeListBase.hxx"
+#include <vector>
+#include <memory>
+
+class ScPivotLayoutTreeListLabel : public ScPivotLayoutTreeListBase
+{
+private:
+ std::vector<std::unique_ptr<ScItemValue>> maItemValues;
+ SCCOL maDataItem;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+
+public:
+ ScPivotLayoutTreeListLabel(std::unique_ptr<weld::TreeView> xControl);
+ virtual ~ScPivotLayoutTreeListLabel() override;
+ void FillLabelFields(ScDPLabelDataVector& rLabelVector);
+ ScItemValue* GetItem(SCCOL nColumn);
+ bool IsDataElement(SCCOL nColumn);
+ virtual void InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/RandomNumberGeneratorDialog.hxx b/sc/source/ui/inc/RandomNumberGeneratorDialog.hxx
new file mode 100644
index 0000000000..0f890fc799
--- /dev/null
+++ b/sc/source/ui/inc/RandomNumberGeneratorDialog.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <optional>
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "viewdata.hxx"
+
+class ScRandomNumberGeneratorDialog : public ScAnyRefDlgController
+{
+public:
+ ScRandomNumberGeneratorDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScRandomNumberGeneratorDialog() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ // Data
+ ScViewData& mrViewData;
+ const ScDocument& mrDoc;
+
+ ScRange maInputRange;
+
+ bool mbDialogLostFocus;
+
+ // Widgets
+ std::unique_ptr<weld::Label> mxInputRangeText;
+ std::unique_ptr<formula::RefEdit> mxInputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxInputRangeButton;
+ std::unique_ptr<weld::ComboBox> mxDistributionCombo;
+ std::unique_ptr<weld::Label> mxParameter1Text;
+ std::unique_ptr<weld::SpinButton> mxParameter1Value;
+ std::unique_ptr<weld::Label> mxParameter2Text;
+ std::unique_ptr<weld::SpinButton> mxParameter2Value;
+ std::unique_ptr<weld::SpinButton> mxSeed;
+ std::unique_ptr<weld::CheckButton> mxEnableSeed;
+ std::unique_ptr<weld::SpinButton> mxDecimalPlaces;
+ std::unique_ptr<weld::CheckButton> mxEnableRounding;
+ std::unique_ptr<weld::Button> mxButtonApply;
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonClose;
+
+ void Init();
+ void GetRangeFromSelection();
+
+ template<class RNG>
+
+ void GenerateNumbers(RNG& randomGenerator, TranslateId pDistributionStringId, const std::optional<sal_Int8> aDecimalPlaces);
+
+ void SelectGeneratorAndGenerateNumbers();
+
+ DECL_LINK( OkClicked, weld::Button&, void );
+ DECL_LINK( CloseClicked, weld::Button&, void );
+ DECL_LINK( ApplyClicked, weld::Button&, void );
+ DECL_LINK( GetEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( LoseEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( LoseButtonFocusHandler, formula::RefButton&, void );
+
+ DECL_LINK( InputRangeModified, formula::RefEdit&, void );
+ DECL_LINK( Parameter1ValueModified, weld::SpinButton&, void );
+ DECL_LINK( Parameter2ValueModified, weld::SpinButton&, void );
+ DECL_LINK( DistributionChanged, weld::ComboBox&, void );
+ DECL_LINK( CheckChanged, weld::Toggleable&, void );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/RegressionDialog.hxx b/sc/source/ui/inc/RegressionDialog.hxx
new file mode 100644
index 0000000000..f3783c36c9
--- /dev/null
+++ b/sc/source/ui/inc/RegressionDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsTwoVariableDialog.hxx"
+#include "TableFillingAndNavigationTools.hxx"
+
+class ScRegressionDialog : public ScStatisticsTwoVariableDialog
+{
+ bool mbUnivariate;
+ size_t mnNumIndependentVars;
+ size_t mnNumObservations;
+ bool mbUse3DAddresses;
+ bool mbCalcIntercept;
+
+ std::unique_ptr<weld::CheckButton> mxWithLabelsCheckBox;
+ std::unique_ptr<weld::RadioButton> mxLinearRadioButton;
+ std::unique_ptr<weld::RadioButton> mxLogarithmicRadioButton;
+ std::unique_ptr<weld::Label> mxErrorMessage;
+ std::unique_ptr<weld::SpinButton> mxConfidenceLevelField;
+ std::unique_ptr<weld::CheckButton> mxCalcResidualsCheckBox;
+ std::unique_ptr<weld::CheckButton> mxNoInterceptCheckBox;
+
+public:
+ ScRegressionDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScRegressionDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+ virtual bool InputRangesValid() override;
+
+private:
+
+ using CellValueGetter = const OUString&(size_t, size_t);
+ using CellWriter = void(const OUString&, size_t, size_t);
+
+ size_t GetRegressionTypeIndex() const;
+ ScRange GetDataRange(const ScRange& rRange);
+ OUString GetVariableNameFormula(bool bXVar, size_t nIndex, bool bWithLog);
+ OUString GetXVariableNameFormula(size_t nIndex, bool bWithLog);
+ OUString GetYVariableNameFormula(bool bWithLog);
+
+ // Helper methods for writing different parts of regression results.
+ void WriteRawRegressionResults(AddressWalkerWriter& rOutput,
+ FormulaTemplate& rTemplate,
+ size_t nRegressionIndex);
+ void WriteRegressionStatistics(AddressWalkerWriter& rOutput,
+ FormulaTemplate& rTemplate);
+ void WriteRegressionANOVAResults(AddressWalkerWriter& rOutput,
+ FormulaTemplate& rTemplate);
+ void WriteRegressionEstimatesWithCI(AddressWalkerWriter& rOutput,
+ FormulaTemplate& rTemplate,
+ bool bTakeLogX);
+ void WritePredictionsWithResiduals(AddressWalkerWriter& rOutput,
+ FormulaTemplate& rTemplate,
+ size_t nRegressionIndex);
+ // Generic table writer
+ static void WriteTable(const std::function<CellValueGetter>& rCellGetter, size_t nRowsInTable,
+ size_t nColsInTable, AddressWalkerWriter& rOutput,
+ const std::function<CellWriter>& rFunc);
+
+ DECL_LINK( CheckBoxHdl, weld::Toggleable&, void );
+ DECL_LINK( NumericFieldHdl, weld::SpinButton&, void );
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/SamplingDialog.hxx b/sc/source/ui/inc/SamplingDialog.hxx
new file mode 100644
index 0000000000..e561092a80
--- /dev/null
+++ b/sc/source/ui/inc/SamplingDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "viewdata.hxx"
+
+class ScSamplingDialog : public ScAnyRefDlgController
+{
+public:
+ ScSamplingDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScSamplingDialog() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ formula::RefEdit* mpActiveEdit;
+
+ // Data
+ ScViewData& mViewData;
+ const ScDocument& mDocument;
+
+ ScRange mInputRange;
+ ScAddress::Details mAddressDetails;
+ ScAddress mOutputAddress;
+
+ ScAddress mCurrentAddress;
+
+ sal_Int64 mnLastSampleSizeValue;
+ sal_Int64 mnLastPeriodValue;
+
+ bool mDialogLostFocus;
+
+ // Widgets
+ std::unique_ptr<weld::Label> mxInputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxInputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxInputRangeButton;
+
+ std::unique_ptr<weld::Label> mxOutputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxOutputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxOutputRangeButton;
+
+ std::unique_ptr<weld::SpinButton> mxSampleSize;
+ std::unique_ptr<weld::SpinButton> mxPeriod;
+
+ std::unique_ptr<weld::RadioButton> mxRandomMethodRadio;
+ std::unique_ptr<weld::CheckButton> mxWithReplacement;
+ std::unique_ptr<weld::CheckButton> mxKeepOrder;
+ std::unique_ptr<weld::RadioButton> mxPeriodicMethodRadio;
+
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonCancel;
+
+ void Init();
+ void GetRangeFromSelection();
+ void PerformSampling();
+ sal_Int64 GetPopulationSize() const;
+ void LimitSampleSizeAndPeriod();
+
+ ScRange PerformRandomSampling(ScDocShell* pDocShell);
+ ScRange PerformRandomSamplingKeepOrder(ScDocShell* pDocShell);
+ ScRange PerformPeriodicSampling(ScDocShell* pDocShell);
+
+ DECL_LINK( ButtonClicked, weld::Button&, void );
+ DECL_LINK( GetEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( LoseEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( LoseButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( SamplingSizeValueModified, weld::SpinButton&, void );
+ DECL_LINK( PeriodValueModified, weld::SpinButton&, void );
+ DECL_LINK( ToggleSamplingMethod, weld::Toggleable&, void );
+ DECL_LINK( RefInputModifyHandler, formula::RefEdit&, void );
+ DECL_LINK( CheckHdl, weld::Toggleable&, void );
+ void ToggleSamplingMethod();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/SparklineDataRangeDialog.hxx b/sc/source/ui/inc/SparklineDataRangeDialog.hxx
new file mode 100644
index 0000000000..32d3ed035c
--- /dev/null
+++ b/sc/source/ui/inc/SparklineDataRangeDialog.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/.
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "viewdata.hxx"
+
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+
+class ColorListBox;
+
+namespace sc
+{
+/** Dialog to change input data range for a sparkline */
+class SparklineDataRangeDialog : public ScAnyRefDlgController
+{
+private:
+ ScViewData& mrViewData;
+ ScDocument& mrDocument;
+
+ std::shared_ptr<sc::Sparkline> mpSparkline;
+
+ ScRange maDataRange;
+
+ formula::RefEdit* mpActiveEdit;
+ bool mbDialogLostFocus;
+
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonCancel;
+
+ std::unique_ptr<weld::Label> mxDataRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxDataRangeEdit;
+ std::unique_ptr<formula::RefButton> mxDataRangeButton;
+
+ DECL_LINK(ButtonClicked, weld::Button&, void);
+ DECL_LINK(EditFocusHandler, formula::RefEdit&, void);
+ DECL_LINK(ButtonFocusHandler, formula::RefButton&, void);
+ DECL_LINK(LoseEditFocusHandler, formula::RefEdit&, void);
+ DECL_LINK(LoseButtonFocusHandler, formula::RefButton&, void);
+ DECL_LINK(RefInputModifyHandler, formula::RefEdit&, void);
+
+ void setupValues();
+
+ void perform();
+ bool checkValidInputOutput();
+
+public:
+ SparklineDataRangeDialog(SfxBindings* pBindings, SfxChildWindow* pChildWindow,
+ weld::Window* pWindow, ScViewData& rViewData);
+ virtual ~SparklineDataRangeDialog() override;
+
+ void SetReference(const ScRange& rRef, ScDocument& rDocument) override;
+ void SetActive() override;
+ void Close() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/SparklineDialog.hxx b/sc/source/ui/inc/SparklineDialog.hxx
new file mode 100644
index 0000000000..f3f309c568
--- /dev/null
+++ b/sc/source/ui/inc/SparklineDialog.hxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "viewdata.hxx"
+
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+
+class ColorListBox;
+
+namespace sc
+{
+/** Dialog to create or edit sparkline group attributes */
+class SparklineDialog : public ScAnyRefDlgController
+{
+private:
+ ScViewData& mrViewData;
+ ScDocument& mrDocument;
+
+ ScRange maInputRange;
+ ScRange maOutputRange;
+
+ formula::RefEdit* mpActiveEdit;
+ bool mbDialogLostFocus;
+
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonCancel;
+
+ std::unique_ptr<weld::Frame> mxFrameData;
+
+ std::unique_ptr<weld::Label> mxInputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxInputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxInputRangeButton;
+
+ std::unique_ptr<weld::Label> mxOutputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxOutputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxOutputRangeButton;
+
+ std::unique_ptr<ColorListBox> mxColorSeries;
+ std::unique_ptr<ColorListBox> mxColorNegative;
+ std::unique_ptr<ColorListBox> mxColorMarker;
+ std::unique_ptr<ColorListBox> mxColorHigh;
+ std::unique_ptr<ColorListBox> mxColorLow;
+ std::unique_ptr<ColorListBox> mxColorFirst;
+ std::unique_ptr<ColorListBox> mxColorLast;
+
+ std::unique_ptr<weld::CheckButton> mxCheckButtonNegative;
+ std::unique_ptr<weld::CheckButton> mxCheckButtonMarker;
+ std::unique_ptr<weld::CheckButton> mxCheckButtonHigh;
+ std::unique_ptr<weld::CheckButton> mxCheckButtonLow;
+ std::unique_ptr<weld::CheckButton> mxCheckButtonFirst;
+ std::unique_ptr<weld::CheckButton> mxCheckButtonLast;
+
+ std::unique_ptr<weld::SpinButton> mxSpinLineWidth;
+ std::unique_ptr<weld::ComboBox> mxType;
+
+ std::unique_ptr<weld::CheckButton> mxCheckDisplayXAxis;
+ std::unique_ptr<weld::CheckButton> mxCheckDisplayHidden;
+ std::unique_ptr<weld::CheckButton> mxCheckRightToLeft;
+
+ std::unique_ptr<weld::ComboBox> mxDisplayEmptyGap;
+
+ std::unique_ptr<weld::ComboBox> mxComboMinAxisType;
+ std::unique_ptr<weld::ComboBox> mxComboMaxAxisType;
+
+ std::unique_ptr<weld::FormattedSpinButton> mxSpinCustomMin;
+ std::unique_ptr<weld::FormattedSpinButton> mxSpinCustomMax;
+
+ DECL_LINK(ButtonClicked, weld::Button&, void);
+ DECL_LINK(EditFocusHandler, formula::RefEdit&, void);
+ DECL_LINK(ButtonFocusHandler, formula::RefButton&, void);
+ DECL_LINK(LoseEditFocusHandler, formula::RefEdit&, void);
+ DECL_LINK(LoseButtonFocusHandler, formula::RefButton&, void);
+ DECL_LINK(RefInputModifyHandler, formula::RefEdit&, void);
+ DECL_LINK(ToggleHandler, weld::Toggleable&, void);
+ DECL_LINK(SelectSparklineType, weld::ComboBox&, void);
+ DECL_LINK(ComboValueChanged, weld::ComboBox&, void);
+ DECL_LINK(SpinLineWidthChanged, weld::SpinButton&, void);
+ DECL_LINK(SpinCustomChanged, weld::FormattedSpinButton&, void);
+
+ std::shared_ptr<sc::SparklineGroup> mpSparklineGroup;
+ sc::SparklineAttributes maAttributes;
+
+ bool mbEditMode;
+
+ void setupValues();
+ void setInputSelection();
+
+ void perform();
+ bool checkValidInputOutput();
+
+public:
+ SparklineDialog(SfxBindings* pBindings, SfxChildWindow* pChildWindow, weld::Window* pWindow,
+ ScViewData& rViewData);
+ virtual ~SparklineDialog() override;
+
+ virtual void SetReference(const ScRange& rRef, ScDocument& rDocument) override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/SparklineRenderer.hxx b/sc/source/ui/inc/SparklineRenderer.hxx
new file mode 100644
index 0000000000..1a8adc39a8
--- /dev/null
+++ b/sc/source/ui/inc/SparklineRenderer.hxx
@@ -0,0 +1,576 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <document.hxx>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+
+namespace sc
+{
+/** Contains the marker polygon and the color of a marker */
+struct SparklineMarker
+{
+ basegfx::B2DPolygon maPolygon;
+ Color maColor;
+};
+
+/** Sparkline value and action that needs to me performed on the value */
+struct SparklineValue
+{
+ enum class Action
+ {
+ None, // No action on the value
+ Skip, // Skip the value
+ Interpolate // Interpolate the value
+ };
+
+ double maValue;
+ Action meAction;
+
+ SparklineValue(double aValue, Action eAction)
+ : maValue(aValue)
+ , meAction(eAction)
+ {
+ }
+};
+
+/** Contains and manages the values of the sparkline.
+ *
+ * It automatically keeps track of the minimums and maximums, and
+ * skips or interpolates the sparkline values if needed, depending on
+ * the input. This is done so it is easier to handle the sparkline
+ * values later on.
+ */
+class SparklineValues
+{
+private:
+ double mfPreviousValue = 0.0;
+ size_t mnPreviousIndex = std::numeric_limits<size_t>::max();
+
+ std::vector<size_t> maToInterpolateIndex;
+
+ std::vector<SparklineValue> maValueList;
+
+public:
+ size_t mnFirstIndex = std::numeric_limits<size_t>::max();
+ size_t mnLastIndex = 0;
+
+ double mfMinimum = std::numeric_limits<double>::max();
+ double mfMaximum = std::numeric_limits<double>::lowest();
+
+ std::vector<SparklineValue> const& getValuesList() const { return maValueList; }
+
+ void add(double fValue, SparklineValue::Action eAction)
+ {
+ maValueList.emplace_back(fValue, eAction);
+ size_t nCurrentIndex = maValueList.size() - 1;
+
+ if (eAction == SparklineValue::Action::None)
+ {
+ mnLastIndex = nCurrentIndex;
+
+ if (mnLastIndex < mnFirstIndex)
+ mnFirstIndex = mnLastIndex;
+
+ if (fValue < mfMinimum)
+ mfMinimum = fValue;
+
+ if (fValue > mfMaximum)
+ mfMaximum = fValue;
+
+ interpolatePastValues(fValue, nCurrentIndex);
+
+ mnPreviousIndex = nCurrentIndex;
+ mfPreviousValue = fValue;
+ }
+ else if (eAction == SparklineValue::Action::Interpolate)
+ {
+ maToInterpolateIndex.push_back(nCurrentIndex);
+ maValueList.back().meAction = SparklineValue::Action::Skip;
+ }
+ }
+
+ static constexpr double interpolate(double x1, double y1, double x2, double y2, double x)
+ {
+ return (y1 * (x2 - x) + y2 * (x - x1)) / (x2 - x1);
+ }
+
+ void interpolatePastValues(double nCurrentValue, size_t nCurrentIndex)
+ {
+ if (maToInterpolateIndex.empty())
+ return;
+
+ if (mnPreviousIndex == std::numeric_limits<size_t>::max())
+ {
+ for (size_t nIndex : maToInterpolateIndex)
+ {
+ auto& rValue = maValueList[nIndex];
+ rValue.meAction = SparklineValue::Action::Skip;
+ }
+ }
+ else
+ {
+ for (size_t nIndex : maToInterpolateIndex)
+ {
+ double fInterpolated = interpolate(mnPreviousIndex, mfPreviousValue, nCurrentIndex,
+ nCurrentValue, nIndex);
+
+ auto& rValue = maValueList[nIndex];
+ rValue.maValue = fInterpolated;
+ rValue.meAction = SparklineValue::Action::None;
+ }
+ }
+ maToInterpolateIndex.clear();
+ }
+
+ void convertToStacked()
+ {
+ // transform the data to 1, -1
+ for (auto& rValue : maValueList)
+ {
+ if (rValue.maValue != 0.0)
+ {
+ double fNewValue = rValue.maValue > 0.0 ? 1.0 : -1.0;
+
+ if (rValue.maValue == mfMinimum)
+ fNewValue -= 0.01;
+
+ if (rValue.maValue == mfMaximum)
+ fNewValue += 0.01;
+
+ rValue.maValue = fNewValue;
+ }
+ }
+ mfMinimum = -1.01;
+ mfMaximum = 1.01;
+ }
+
+ void reverse() { std::reverse(maValueList.begin(), maValueList.end()); }
+};
+
+/** Iterator to traverse the addresses in a range if the range is one dimensional.
+ *
+ * The direction to traverse is detected automatically or hasNext returns
+ * false if it is not possible to detect.
+ *
+ */
+class RangeTraverser
+{
+ enum class Direction
+ {
+ UNKNOWN,
+ ROW,
+ COLUMN
+ };
+
+ ScAddress m_aCurrent;
+ ScRange m_aRange;
+ Direction m_eDirection;
+
+public:
+ RangeTraverser(ScRange const& rRange)
+ : m_aCurrent(ScAddress::INITIALIZE_INVALID)
+ , m_aRange(rRange)
+ , m_eDirection(Direction::UNKNOWN)
+
+ {
+ }
+
+ ScAddress const& first()
+ {
+ m_aCurrent.SetInvalid();
+
+ if (m_aRange.aStart.Row() == m_aRange.aEnd.Row())
+ {
+ m_eDirection = Direction::COLUMN;
+ m_aCurrent = m_aRange.aStart;
+ }
+ else if (m_aRange.aStart.Col() == m_aRange.aEnd.Col())
+ {
+ m_eDirection = Direction::ROW;
+ m_aCurrent = m_aRange.aStart;
+ }
+
+ return m_aCurrent;
+ }
+
+ bool hasNext()
+ {
+ if (m_eDirection == Direction::COLUMN)
+ return m_aCurrent.Col() <= m_aRange.aEnd.Col();
+ else if (m_eDirection == Direction::ROW)
+ return m_aCurrent.Row() <= m_aRange.aEnd.Row();
+ else
+ return false;
+ }
+
+ void next()
+ {
+ if (hasNext())
+ {
+ if (m_eDirection == Direction::COLUMN)
+ m_aCurrent.IncCol();
+ else if (m_eDirection == Direction::ROW)
+ m_aCurrent.IncRow();
+ }
+ }
+};
+
+/** Render a provided sparkline into the input rectangle */
+class SparklineRenderer
+{
+private:
+ ScDocument& mrDocument;
+ tools::Long mnOneX;
+ tools::Long mnOneY;
+
+ double mfScaleX;
+ double mfScaleY;
+
+ void createMarker(std::vector<SparklineMarker>& rMarkers, double x, double y,
+ Color const& rColor)
+ {
+ auto& rMarker = rMarkers.emplace_back();
+ const double nHalfSizeX = double(mnOneX * 2 * mfScaleX);
+ const double nHalfSizeY = double(mnOneY * 2 * mfScaleY);
+ basegfx::B2DRectangle aRectangle(std::round(x - nHalfSizeX), std::round(y - nHalfSizeY),
+ std::round(x + nHalfSizeX), std::round(y + nHalfSizeY));
+ rMarker.maPolygon = basegfx::utils::createPolygonFromRect(aRectangle);
+ rMarker.maColor = rColor;
+ }
+
+ void drawLine(vcl::RenderContext& rRenderContext, tools::Rectangle const& rRectangle,
+ SparklineValues const& rSparklineValues,
+ sc::SparklineAttributes const& rAttributes)
+ {
+ double nMax = rSparklineValues.mfMaximum;
+ if (rAttributes.getMaxAxisType() == sc::AxisType::Custom && rAttributes.getManualMax())
+ nMax = *rAttributes.getManualMax();
+
+ double nMin = rSparklineValues.mfMinimum;
+ if (rAttributes.getMinAxisType() == sc::AxisType::Custom && rAttributes.getManualMin())
+ nMin = *rAttributes.getManualMin();
+
+ std::vector<SparklineValue> const& rValueList = rSparklineValues.getValuesList();
+ std::vector<basegfx::B2DPolygon> aPolygons;
+ aPolygons.emplace_back();
+ double numebrOfSteps = rValueList.size() - 1;
+ double xStep = 0;
+ double nDelta = nMax - nMin;
+
+ std::vector<SparklineMarker> aMarkers;
+ size_t nValueIndex = 0;
+
+ for (auto const& rSparklineValue : rValueList)
+ {
+ if (rSparklineValue.meAction == SparklineValue::Action::Skip)
+ {
+ aPolygons.emplace_back();
+ }
+ else
+ {
+ auto& aPolygon = aPolygons.back();
+ double nValue = rSparklineValue.maValue;
+
+ double nP = (nValue - nMin) / nDelta;
+ double x = rRectangle.GetWidth() * (xStep / numebrOfSteps);
+ double y = rRectangle.GetHeight() - rRectangle.GetHeight() * nP;
+
+ aPolygon.append({ x, y });
+
+ if (rAttributes.isFirst() && nValueIndex == rSparklineValues.mnFirstIndex)
+ {
+ createMarker(aMarkers, x, y, rAttributes.getColorFirst().getFinalColor());
+ }
+ else if (rAttributes.isLast() && nValueIndex == rSparklineValues.mnLastIndex)
+ {
+ createMarker(aMarkers, x, y, rAttributes.getColorLast().getFinalColor());
+ }
+ else if (rAttributes.isHigh() && nValue == rSparklineValues.mfMaximum)
+ {
+ createMarker(aMarkers, x, y, rAttributes.getColorHigh().getFinalColor());
+ }
+ else if (rAttributes.isLow() && nValue == rSparklineValues.mfMinimum)
+ {
+ createMarker(aMarkers, x, y, rAttributes.getColorLow().getFinalColor());
+ }
+ else if (rAttributes.isNegative() && nValue < 0.0)
+ {
+ createMarker(aMarkers, x, y, rAttributes.getColorNegative().getFinalColor());
+ }
+ else if (rAttributes.isMarkers())
+ {
+ createMarker(aMarkers, x, y, rAttributes.getColorMarkers().getFinalColor());
+ }
+ }
+
+ xStep++;
+ nValueIndex++;
+ }
+
+ basegfx::B2DHomMatrix aMatrix;
+ aMatrix.translate(rRectangle.Left(), rRectangle.Top());
+
+ if (rAttributes.shouldDisplayXAxis())
+ {
+ double nZero = 0 - nMin / nDelta;
+
+ if (nZero >= 0) // if nZero < 0, the axis is not visible
+ {
+ double x1 = 0.0;
+ double x2 = double(rRectangle.GetWidth());
+ double y = rRectangle.GetHeight() - rRectangle.GetHeight() * nZero;
+
+ basegfx::B2DPolygon aAxisPolygon;
+ aAxisPolygon.append({ x1, y });
+ aAxisPolygon.append({ x2, y });
+
+ rRenderContext.SetLineColor(rAttributes.getColorAxis().getFinalColor());
+ rRenderContext.DrawPolyLineDirect(aMatrix, aAxisPolygon, 0.2 * mfScaleX);
+ }
+ }
+
+ rRenderContext.SetLineColor(rAttributes.getColorSeries().getFinalColor());
+
+ for (auto& rPolygon : aPolygons)
+ {
+ rRenderContext.DrawPolyLineDirect(aMatrix, rPolygon,
+ rAttributes.getLineWeight() * mfScaleX, 0.0, nullptr,
+ basegfx::B2DLineJoin::Round);
+ }
+
+ for (auto& rMarker : aMarkers)
+ {
+ rRenderContext.SetLineColor(rMarker.maColor);
+ rRenderContext.SetFillColor(rMarker.maColor);
+ auto& rPolygon = rMarker.maPolygon;
+ rPolygon.transform(aMatrix);
+ rRenderContext.DrawPolygon(rPolygon);
+ }
+ }
+
+ static void setFillAndLineColor(vcl::RenderContext& rRenderContext,
+ sc::SparklineAttributes const& rAttributes, double nValue,
+ size_t nValueIndex, SparklineValues const& rSparklineValues)
+ {
+ if (rAttributes.isFirst() && nValueIndex == rSparklineValues.mnFirstIndex)
+ {
+ rRenderContext.SetLineColor(rAttributes.getColorFirst().getFinalColor());
+ rRenderContext.SetFillColor(rAttributes.getColorFirst().getFinalColor());
+ }
+ else if (rAttributes.isLast() && nValueIndex == rSparklineValues.mnLastIndex)
+ {
+ rRenderContext.SetLineColor(rAttributes.getColorLast().getFinalColor());
+ rRenderContext.SetFillColor(rAttributes.getColorLast().getFinalColor());
+ }
+ else if (rAttributes.isHigh() && nValue == rSparklineValues.mfMaximum)
+ {
+ rRenderContext.SetLineColor(rAttributes.getColorHigh().getFinalColor());
+ rRenderContext.SetFillColor(rAttributes.getColorHigh().getFinalColor());
+ }
+ else if (rAttributes.isLow() && nValue == rSparklineValues.mfMinimum)
+ {
+ rRenderContext.SetLineColor(rAttributes.getColorLow().getFinalColor());
+ rRenderContext.SetFillColor(rAttributes.getColorLow().getFinalColor());
+ }
+ else if (rAttributes.isNegative() && nValue < 0.0)
+ {
+ rRenderContext.SetLineColor(rAttributes.getColorNegative().getFinalColor());
+ rRenderContext.SetFillColor(rAttributes.getColorNegative().getFinalColor());
+ }
+ else
+ {
+ rRenderContext.SetLineColor(rAttributes.getColorSeries().getFinalColor());
+ rRenderContext.SetFillColor(rAttributes.getColorSeries().getFinalColor());
+ }
+ }
+
+ void drawColumn(vcl::RenderContext& rRenderContext, tools::Rectangle const& rRectangle,
+ SparklineValues const& rSparklineValues,
+ sc::SparklineAttributes const& rAttributes)
+ {
+ double nMax = rSparklineValues.mfMaximum;
+ if (rAttributes.getMaxAxisType() == sc::AxisType::Custom && rAttributes.getManualMax())
+ nMax = *rAttributes.getManualMax();
+
+ double nMin = rSparklineValues.mfMinimum;
+ if (rAttributes.getMinAxisType() == sc::AxisType::Custom && rAttributes.getManualMin())
+ nMin = *rAttributes.getManualMin();
+
+ std::vector<SparklineValue> const& rValueList = rSparklineValues.getValuesList();
+
+ basegfx::B2DPolygon aPolygon;
+ basegfx::B2DHomMatrix aMatrix;
+ aMatrix.translate(rRectangle.Left(), rRectangle.Top());
+
+ double xStep = 0;
+ double numberOfSteps = rValueList.size();
+ double nDelta = nMax - nMin;
+
+ double nColumnSize = rRectangle.GetWidth() / numberOfSteps;
+ nColumnSize = nColumnSize - (nColumnSize * 0.3);
+
+ double nZero = (0 - nMin) / nDelta;
+ double nZeroPosition = 0.0;
+ if (nZero >= 0)
+ {
+ nZeroPosition = rRectangle.GetHeight() - rRectangle.GetHeight() * nZero;
+
+ if (rAttributes.shouldDisplayXAxis())
+ {
+ double x1 = 0.0;
+ double x2 = double(rRectangle.GetWidth());
+
+ basegfx::B2DPolygon aAxisPolygon;
+ aAxisPolygon.append({ x1, nZeroPosition });
+ aAxisPolygon.append({ x2, nZeroPosition });
+
+ rRenderContext.SetLineColor(rAttributes.getColorAxis().getFinalColor());
+ rRenderContext.DrawPolyLineDirect(aMatrix, aAxisPolygon, 0.2 * mfScaleX);
+ }
+ }
+ else
+ nZeroPosition = rRectangle.GetHeight();
+
+ size_t nValueIndex = 0;
+
+ for (auto const& rSparklineValue : rValueList)
+ {
+ double nValue = rSparklineValue.maValue;
+
+ if (nValue != 0.0)
+ {
+ setFillAndLineColor(rRenderContext, rAttributes, nValue, nValueIndex,
+ rSparklineValues);
+
+ double nP = (nValue - nMin) / nDelta;
+ double x = rRectangle.GetWidth() * (xStep / numberOfSteps);
+ double y = rRectangle.GetHeight() - rRectangle.GetHeight() * nP;
+
+ basegfx::B2DRectangle aRectangle(x, y, x + nColumnSize, nZeroPosition);
+ aPolygon = basegfx::utils::createPolygonFromRect(aRectangle);
+
+ aPolygon.transform(aMatrix);
+ rRenderContext.DrawPolygon(aPolygon);
+ }
+ xStep++;
+ nValueIndex++;
+ }
+ }
+
+ bool isCellHidden(ScAddress const& rAddress)
+ {
+ return mrDocument.RowHidden(rAddress.Row(), rAddress.Tab())
+ || mrDocument.ColHidden(rAddress.Col(), rAddress.Tab());
+ }
+
+public:
+ SparklineRenderer(ScDocument& rDocument)
+ : mrDocument(rDocument)
+ , mnOneX(1)
+ , mnOneY(1)
+ , mfScaleX(1.0)
+ , mfScaleY(1.0)
+ {
+ }
+
+ void render(std::shared_ptr<sc::Sparkline> const& pSparkline,
+ vcl::RenderContext& rRenderContext, tools::Rectangle const& rRectangle,
+ tools::Long nOneX, tools::Long nOneY, double fScaleX, double fScaleY)
+ {
+ rRenderContext.Push();
+ comphelper::ScopeGuard aPushPopGuard([&rRenderContext]() { rRenderContext.Pop(); });
+
+ rRenderContext.SetAntialiasing(AntialiasingFlags::Enable);
+ rRenderContext.SetClipRegion(vcl::Region(rRectangle));
+
+ tools::Rectangle aOutputRectangle(rRectangle);
+ aOutputRectangle.shrink(6); // provide border
+
+ mnOneX = nOneX;
+ mnOneY = nOneY;
+ mfScaleX = fScaleX;
+ mfScaleY = fScaleY;
+
+ auto const& rRangeList = pSparkline->getInputRange();
+
+ if (rRangeList.empty())
+ {
+ return;
+ }
+
+ auto pSparklineGroup = pSparkline->getSparklineGroup();
+ auto const& rAttributes = pSparklineGroup->getAttributes();
+
+ ScRange aRange = rRangeList[0];
+
+ SparklineValues aSparklineValues;
+
+ RangeTraverser aTraverser(aRange);
+ for (ScAddress const& rCurrent = aTraverser.first(); aTraverser.hasNext();
+ aTraverser.next())
+ {
+ // Skip if the cell is hidden and "displayHidden" attribute is not selected
+ if (!rAttributes.shouldDisplayHidden() && isCellHidden(rCurrent))
+ continue;
+
+ double fCellValue = 0.0;
+ SparklineValue::Action eAction = SparklineValue::Action::None;
+ CellType eType = mrDocument.GetCellType(rCurrent);
+
+ if (eType == CELLTYPE_NONE) // if cell is empty
+ {
+ auto eDisplayEmpty = rAttributes.getDisplayEmptyCellsAs();
+ if (eDisplayEmpty == sc::DisplayEmptyCellsAs::Gap)
+ eAction = SparklineValue::Action::Skip;
+ else if (eDisplayEmpty == sc::DisplayEmptyCellsAs::Span)
+ eAction = SparklineValue::Action::Interpolate;
+ }
+ else
+ {
+ fCellValue = mrDocument.GetValue(rCurrent);
+ }
+
+ aSparklineValues.add(fCellValue, eAction);
+ }
+
+ if (rAttributes.isRightToLeft())
+ aSparklineValues.reverse();
+
+ if (rAttributes.getType() == sc::SparklineType::Column)
+ {
+ drawColumn(rRenderContext, aOutputRectangle, aSparklineValues,
+ pSparklineGroup->getAttributes());
+ }
+ else if (rAttributes.getType() == sc::SparklineType::Stacked)
+ {
+ aSparklineValues.convertToStacked();
+ drawColumn(rRenderContext, aOutputRectangle, aSparklineValues,
+ pSparklineGroup->getAttributes());
+ }
+ else if (rAttributes.getType() == sc::SparklineType::Line)
+ {
+ drawLine(rRenderContext, aOutputRectangle, aSparklineValues,
+ pSparklineGroup->getAttributes());
+ }
+ }
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/SparklineShell.hxx b/sc/source/ui/inc/SparklineShell.hxx
new file mode 100644
index 0000000000..912928d149
--- /dev/null
+++ b/sc/source/ui/inc/SparklineShell.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+
+#include <shellids.hxx>
+
+class ScTabViewShell;
+class SfxModule;
+
+namespace sc
+{
+/** Shell to handle the sparkline context */
+class SparklineShell final : public SfxShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_SPARKLINE_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ SparklineShell(ScTabViewShell* pView);
+ virtual ~SparklineShell() override;
+
+private:
+ ScTabViewShell* m_pViewShell;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/StatisticsInputOutputDialog.hxx b/sc/source/ui/inc/StatisticsInputOutputDialog.hxx
new file mode 100644
index 0000000000..e831772c84
--- /dev/null
+++ b/sc/source/ui/inc/StatisticsInputOutputDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "viewdata.hxx"
+
+class ScStatisticsInputOutputDialog : public ScAnyRefDlgController
+{
+public:
+ enum GroupedBy {
+ BY_COLUMN,
+ BY_ROW
+ };
+
+ ScStatisticsInputOutputDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData,
+ const OUString& rUIXMLDescription,
+ const OUString& rID);
+
+ virtual ~ScStatisticsInputOutputDialog() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual void SetActive() override;
+
+protected:
+ void CalculateInputAndWriteToOutput();
+
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) = 0;
+ virtual TranslateId GetUndoNameId() = 0;
+ virtual bool InputRangesValid();
+ void ValidateDialogInput();
+
+ // Widgets
+ std::unique_ptr<weld::Label> mxInputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxInputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxInputRangeButton;
+
+ std::unique_ptr<weld::Label> mxOutputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxOutputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxOutputRangeButton;
+
+ std::unique_ptr<weld::RadioButton> mxGroupByColumnsRadio;
+ std::unique_ptr<weld::RadioButton> mxGroupByRowsRadio;
+
+ // Data
+ ScViewData& mViewData;
+ ScDocument& mDocument;
+
+ ScRange mInputRange;
+ ScAddress::Details mAddressDetails;
+ ScAddress mOutputAddress;
+ GroupedBy mGroupedBy;
+
+ static ScRangeList MakeColumnRangeList(SCTAB aTab, ScAddress const & aStart, ScAddress const & aEnd);
+ static ScRangeList MakeRowRangeList(SCTAB aTab, ScAddress const & aStart, ScAddress const & aEnd);
+
+private:
+ // Widgets
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonCancel;
+
+ formula::RefEdit* mpActiveEdit;
+ ScAddress mCurrentAddress;
+ bool mDialogLostFocus;
+
+ void Init();
+ void GetRangeFromSelection();
+
+ DECL_LINK( GroupByChanged, weld::Toggleable&, void );
+ DECL_LINK( ButtonClicked, weld::Button&, void );
+ DECL_LINK( GetEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( LoseEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( LoseButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( RefInputModifyHandler, formula::RefEdit&, void );
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/StatisticsTwoVariableDialog.hxx b/sc/source/ui/inc/StatisticsTwoVariableDialog.hxx
new file mode 100644
index 0000000000..150a574209
--- /dev/null
+++ b/sc/source/ui/inc/StatisticsTwoVariableDialog.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/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "viewdata.hxx"
+
+class ScStatisticsTwoVariableDialog : public ScAnyRefDlgController
+{
+public:
+ enum GroupedBy {
+ BY_COLUMN,
+ BY_ROW
+ };
+
+ ScStatisticsTwoVariableDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData,
+ const OUString& rUIXMLDescription, const OUString& rID);
+
+ virtual ~ScStatisticsTwoVariableDialog() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual void SetActive() override;
+
+protected:
+ void CalculateInputAndWriteToOutput();
+
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) = 0;
+ virtual TranslateId GetUndoNameId() = 0;
+ virtual bool InputRangesValid();
+ void ValidateDialogInput();
+
+ // Widgets
+ std::unique_ptr<weld::Label> mxVariable1RangeLabel;
+ std::unique_ptr<formula::RefEdit> mxVariable1RangeEdit;
+ std::unique_ptr<formula::RefButton> mxVariable1RangeButton;
+
+ std::unique_ptr<weld::Label> mxVariable2RangeLabel;
+ std::unique_ptr<formula::RefEdit> mxVariable2RangeEdit;
+ std::unique_ptr<formula::RefButton> mxVariable2RangeButton;
+
+ std::unique_ptr<weld::Label> mxOutputRangeLabel;
+ std::unique_ptr<formula::RefEdit> mxOutputRangeEdit;
+ std::unique_ptr<formula::RefButton> mxOutputRangeButton;
+
+ // Data
+ ScViewData& mViewData;
+ ScDocument& mDocument;
+
+ ScRange mVariable1Range;
+ ScRange mVariable2Range;
+
+ ScAddress::Details const mAddressDetails;
+ ScAddress mOutputAddress;
+ GroupedBy mGroupedBy;
+
+private:
+ // Widgets
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonCancel;
+
+ std::unique_ptr<weld::RadioButton> mxGroupByColumnsRadio;
+ std::unique_ptr<weld::RadioButton> mxGroupByRowsRadio;
+
+ formula::RefEdit* mpActiveEdit;
+ ScAddress mCurrentAddress;
+ bool mDialogLostFocus;
+
+ void Init();
+ void GetRangeFromSelection();
+
+ DECL_LINK( GroupByChanged, weld::Toggleable&, void );
+ DECL_LINK( ButtonClicked, weld::Button&, void );
+ DECL_LINK( GetEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( LoseEditFocusHandler, formula::RefEdit&, void );
+ DECL_LINK( LoseButtonFocusHandler, formula::RefButton&, void );
+ DECL_LINK( RefInputModifyHandler, formula::RefEdit&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/TTestDialog.hxx b/sc/source/ui/inc/TTestDialog.hxx
new file mode 100644
index 0000000000..0ed370dce2
--- /dev/null
+++ b/sc/source/ui/inc/TTestDialog.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsTwoVariableDialog.hxx"
+
+class ScTTestDialog : public ScStatisticsTwoVariableDialog
+{
+public:
+ ScTTestDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScTTestDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/TableFillingAndNavigationTools.hxx b/sc/source/ui/inc/TableFillingAndNavigationTools.hxx
new file mode 100644
index 0000000000..ab79115118
--- /dev/null
+++ b/sc/source/ui/inc/TableFillingAndNavigationTools.hxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <rangelst.hxx>
+
+#include <map>
+#include <vector>
+
+class FormulaTemplate
+{
+private:
+ OUString mTemplate;
+ ScDocument* mpDoc;
+ bool mbUse3D;
+
+ typedef std::map<OUString, ScRange> RangeReplacementMap;
+ typedef std::map<OUString, ScAddress> AddressReplacementMap;
+
+ AddressReplacementMap mAddressReplacementMap;
+ RangeReplacementMap mRangeReplacementMap;
+
+public:
+ FormulaTemplate(ScDocument* pDoc);
+
+ void setTemplate(const OUString& aTemplate);
+ void setTemplate(const char* aTemplate);
+ const OUString& getTemplate();
+
+ void autoReplaceRange(const OUString& aVariable, const ScRange& rRange);
+ void autoReplaceAddress(const OUString& aVariable, ScAddress const & aAddress);
+ void autoReplaceUses3D(bool bUse3D) { mbUse3D = bUse3D; }
+
+ void applyRange(std::u16string_view aVariable, const ScRange& aRange, bool b3D = true);
+ void applyRangeList(std::u16string_view aVariable, const ScRangeList& aRangeList, sal_Unicode cDelimiter );
+ void applyAddress(std::u16string_view aVariable, const ScAddress& aAddress, bool b3D = true);
+ void applyString(std::u16string_view aVariable, std::u16string_view aValue);
+ void applyNumber(std::u16string_view aVariable, sal_Int32 aValue);
+};
+
+class AddressWalker
+{
+public:
+ std::vector<ScAddress> mAddressStack;
+
+ ScAddress mCurrentAddress;
+ ScAddress mMinimumAddress;
+ ScAddress mMaximumAddress;
+
+ AddressWalker(const ScAddress& aInitialAddress);
+
+ ScAddress current(SCCOL aRelativeCol = 0, SCROW aRelativeRow = 0, SCTAB aRelativeTab = 0);
+
+ void reset();
+ void resetColumn();
+ void resetRow();
+ void nextColumn();
+ void nextRow();
+ void newLine();
+ void push(SCCOL aRelativeCol = 0, SCROW aRelativeRow = 0, SCTAB aRelativeTab = 0);
+};
+
+class AddressWalkerWriter : public AddressWalker
+{
+ ScDocShell* mpDocShell;
+ ScDocument& mrDocument;
+ formula::FormulaGrammar::Grammar meGrammar;
+
+public:
+ AddressWalkerWriter(const ScAddress& aInitialAddress, ScDocShell* pDocShell, ScDocument& rDocument,
+ formula::FormulaGrammar::Grammar eGrammar );
+
+ void writeFormula(const OUString& aFormula);
+ void writeFormulas(const std::vector<OUString>& rFormulas);
+ void writeMatrixFormula(const OUString& aFormula, SCCOL nCols = 1, SCROW nRows = 1);
+ void writeString(const OUString& aString);
+ void writeString(const char* aCharArray);
+ void writeBoldString(const OUString& aString);
+ void writeValue(double aValue);
+};
+
+class DataCellIterator final
+{
+private:
+ ScRange mInputRange;
+ bool mByColumn;
+ SCCOL mCol;
+ SCROW mRow;
+
+public:
+ DataCellIterator(const ScRange& aInputRange, bool aByColumn);
+
+ bool hasNext() const;
+ ScAddress get();
+ void next();
+ ScAddress getRelative(int aDelta);
+};
+
+class DataRangeIterator
+{
+protected:
+ ScRange mInputRange;
+ sal_Int32 mIndex;
+
+public:
+ DataRangeIterator(const ScRange& aInputRange);
+ virtual ~DataRangeIterator();
+
+ virtual bool hasNext() = 0;
+ virtual ScRange get() = 0;
+ virtual size_t size() = 0;
+ virtual void next() = 0;
+ virtual void reset() = 0;
+
+ sal_Int32 index();
+
+ virtual DataCellIterator iterateCells() = 0;
+};
+
+class DataRangeByColumnIterator final : public DataRangeIterator
+{
+ SCCOL mCol;
+
+public:
+ DataRangeByColumnIterator(const ScRange& aInputRange);
+
+ virtual bool hasNext() override;
+ virtual void next() override;
+ virtual ScRange get() override;
+ virtual size_t size() override;
+ virtual void reset() override;
+ virtual DataCellIterator iterateCells() override;
+};
+
+class DataRangeByRowIterator final : public DataRangeIterator
+{
+ SCROW mRow;
+
+public:
+ DataRangeByRowIterator(const ScRange& aInputRange);
+
+ virtual bool hasNext() override;
+ virtual void next() override;
+ virtual ScRange get() override;
+ virtual size_t size() override;
+ virtual void reset() override;
+ virtual DataCellIterator iterateCells() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/ThemeColorChanger.hxx b/sc/source/ui/inc/ThemeColorChanger.hxx
new file mode 100644
index 0000000000..57ca91cdb8
--- /dev/null
+++ b/sc/source/ui/inc/ThemeColorChanger.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/.
+ *
+ */
+
+#pragma once
+
+#include <scdllapi.h>
+#include <svx/theme/IThemeColorChanger.hxx>
+#include "docsh.hxx"
+
+namespace sc
+{
+class SC_DLLPUBLIC ThemeColorChanger : public svx::IThemeColorChanger
+{
+ ScDocShell& m_rDocShell;
+
+public:
+ ThemeColorChanger(ScDocShell& rDocShell);
+ virtual ~ThemeColorChanger() override;
+
+ void apply(std::shared_ptr<model::ColorSet> const& pColorSet) override;
+};
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/ZTestDialog.hxx b/sc/source/ui/inc/ZTestDialog.hxx
new file mode 100644
index 0000000000..2476c38f32
--- /dev/null
+++ b/sc/source/ui/inc/ZTestDialog.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include "StatisticsTwoVariableDialog.hxx"
+
+class ScZTestDialog : public ScStatisticsTwoVariableDialog
+{
+public:
+ ScZTestDialog(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, ScViewData& rViewData );
+
+ virtual ~ScZTestDialog() override;
+
+ virtual void Close() override;
+
+protected:
+ virtual TranslateId GetUndoNameId() override;
+ virtual ScRange ApplyOutput(ScDocShell* pDocShell) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/acredlin.hxx b/sc/source/ui/inc/acredlin.hxx
new file mode 100644
index 0000000000..f939b92052
--- /dev/null
+++ b/sc/source/ui/inc/acredlin.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/ctredlin.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/bindings.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <vcl/idle.hxx>
+
+class ScViewData;
+class ScDocument;
+
+struct SfxChildWinInfo;
+
+class ScRedlinData : public RedlinData
+{
+public:
+ ScRedlinData();
+ virtual ~ScRedlinData() override;
+ sal_uLong nActionNo;
+ sal_uLong nInfo;
+ SCTAB nTable;
+ SCCOL nCol;
+ SCROW nRow;
+ bool bIsRejectable;
+ bool bIsAcceptable;
+};
+
+class ScAcceptChgDlg final : public SfxModelessDialogController
+{
+ Idle aSelectionIdle;
+ Idle aReOpenIdle;
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+ ScRangeList aRangeList;
+ ScChangeViewSettings aChangeViewSet;
+ OUString aStrInsertCols;
+ OUString aStrInsertRows;
+ OUString aStrInsertTabs;
+ OUString aStrDeleteCols;
+ OUString aStrDeleteRows;
+ OUString aStrDeleteTabs;
+ OUString aStrMove;
+ OUString aStrContent;
+ OUString aStrReject;
+ OUString aStrAllAccepted;
+ OUString aStrAllRejected;
+ OUString aStrNoEntry;
+ OUString aStrContentWithChild;
+ OUString aStrChildContent;
+ OUString aStrChildOrgContent;
+ OUString aStrEmpty;
+ OUString aUnknown;
+ sal_uLong nAcceptCount;
+ sal_uLong nRejectCount;
+ bool bIgnoreMsg:1;
+ bool bNoSelection:1;
+ bool bHasFilterEntry:1;
+ bool bUseColor:1;
+
+ SvxTPFilter* pTPFilter;
+ SvxTPView* pTPView;
+ SvxRedlinTable* pTheView; // #i48648 now SvHeaderTabListBox
+
+ std::unique_ptr<weld::Container> m_xContentArea;
+ std::unique_ptr<weld::Menu> m_xPopup, m_xSortMenu;
+ std::unique_ptr<SvxAcceptChgCtr> m_xAcceptChgCtr;
+
+ void Init();
+
+ DECL_LINK( FilterHandle, SvxTPFilter*, void );
+ DECL_LINK( RefHandle, SvxTPFilter*, void );
+ DECL_LINK( RejectHandle, SvxTPView*, void );
+ DECL_LINK( AcceptHandle, SvxTPView*, void );
+ DECL_LINK( RejectAllHandle, SvxTPView*, void );
+ DECL_LINK( AcceptAllHandle, SvxTPView*, void );
+ DECL_LINK( ExpandingHandle, const weld::TreeIter&, bool );
+ DECL_LINK( SelectHandle, weld::TreeView&, void );
+ DECL_LINK( RefInfoHandle, const OUString*, void );
+
+ DECL_LINK( UpdateSelectionHdl, Timer*, void );
+ DECL_LINK( ChgTrackModHdl, ScChangeTrack&, void );
+ DECL_LINK( CommandHdl, const CommandEvent&, bool );
+ DECL_LINK( ReOpenTimerHdl, Timer*, void );
+
+ int ColCompareHdl(const weld::TreeIter& rLeft, const weld::TreeIter& rRight) const;
+
+ void RejectFiltered();
+ void AcceptFiltered();
+
+ bool IsValidAction(const ScChangeAction* pScChangeAction);
+
+ OUString* MakeTypeString(ScChangeActionType eType);
+
+ std::unique_ptr<weld::TreeIter> AppendChangeAction(
+ const ScChangeAction* pScChangeAction, bool bCreateOnDemand,
+ const weld::TreeIter* pParent = nullptr, bool bDelMaster = false,
+ bool bDisabled = false);
+
+ std::unique_ptr<weld::TreeIter> AppendFilteredAction(
+ const ScChangeAction* pScChangeAction,ScChangeActionState eState,
+ bool bCreateOnDemand,
+ const weld::TreeIter* pParent = nullptr, bool bDelMaster = false,
+ bool bDisabled = false);
+
+ std::unique_ptr<weld::TreeIter> InsertChangeActionContent(const ScChangeActionContent* pScChangeAction,
+ const weld::TreeIter& rParent, sal_uLong nSpecial);
+
+ void GetDependents(const ScChangeAction* pScChangeAction,
+ ScChangeActionMap& aActionMap,
+ const weld::TreeIter& rEntry);
+
+ bool InsertContentChildren(ScChangeActionMap* pActionMap, const weld::TreeIter& rParent);
+
+ bool InsertAcceptedORejected(const weld::TreeIter& rParent);
+
+ bool InsertDeletedChildren(const ScChangeAction* pChangeAction, ScChangeActionMap* pActionMap,
+ const weld::TreeIter& rParent);
+
+ bool InsertChildren(ScChangeActionMap* pActionMap, const weld::TreeIter& rParent);
+
+ void AppendChanges(const ScChangeTrack* pChanges,sal_uLong nStartAction, sal_uLong nEndAction);
+
+ void RemoveEntries(sal_uLong nStartAction,sal_uLong nEndAction);
+ void UpdateEntries(const ScChangeTrack* pChgTrack, sal_uLong nStartAction,sal_uLong nEndAction);
+
+ void UpdateView();
+ void ClearView();
+
+ bool Expand(const ScChangeTrack* pChanges,const ScChangeAction* pScChangeAction,
+ const weld::TreeIter& rEntry, bool bFilter = false);
+
+public:
+ ScAcceptChgDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData* ptrViewData);
+ virtual ~ScAcceptChgDlg() override;
+
+ void ReInit(ScViewData* ptrViewData);
+
+ void Initialize (SfxChildWinInfo* pInfo);
+ virtual void FillInfo(SfxChildWinInfo&) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/anyrefdg.hxx b/sc/source/ui/inc/anyrefdg.hxx
new file mode 100644
index 0000000000..ad3feac803
--- /dev/null
+++ b/sc/source/ui/inc/anyrefdg.hxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/basedlgs.hxx>
+#include <address.hxx>
+#include <formula/funcutl.hxx>
+#include "IAnyRefDialog.hxx"
+
+#include <memory>
+
+class SfxObjectShell;
+class ScDocument;
+class ScRangeList;
+class ScCompiler;
+
+class ScFormulaReferenceHelper
+{
+ IAnyRefDialog* m_pDlg;
+ ::std::unique_ptr<ScCompiler> m_pRefComp;
+ formula::RefEdit* m_pRefEdit; // active input field
+ formula::RefButton* m_pRefBtn; // associated button
+ weld::Dialog* m_pDialog;
+ SfxBindings* m_pBindings;
+ SCTAB m_nRefTab; // used for ShowReference
+
+ OUString m_sOldDialogText; // Original title of the dialog window
+
+ bool m_bEnableColorRef;
+ bool m_bHighlightRef;
+
+ DECL_LINK( ActivateHdl, weld::Widget&, bool );
+
+public:
+ ScFormulaReferenceHelper(IAnyRefDialog* _pDlg, SfxBindings* _pBindings);
+ ~ScFormulaReferenceHelper() COVERITY_NOEXCEPT_FALSE;
+ void dispose();
+
+ void ShowSimpleReference(std::u16string_view rStr);
+ void ShowFormulaReference(const OUString& rStr);
+ bool ParseWithNames( ScRangeList& rRanges, std::u16string_view rStr, const ScDocument& rDoc );
+ void Init();
+
+ void ShowReference(const OUString& rStr);
+ void ReleaseFocus( formula::RefEdit* pEdit );
+ void HideReference( bool bDoneRefMode = true );
+ void RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton );
+ void RefInputDone( bool bForced );
+ void ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton );
+
+ void SetDialog(weld::Dialog* pDialog) { m_pDialog = pDialog; }
+ void DoClose( sal_uInt16 nId );
+ void SetDispatcherLock( bool bLock );
+ static void EnableSpreadsheets( bool bFlag = true );
+ static void ViewShellChanged();
+
+ static void enableInput(bool _bInput);
+
+public:
+ static bool CanInputStart( const formula::RefEdit *pEdit ){ return !!pEdit; }
+ bool CanInputDone(bool bForced) const { return m_pRefEdit && (bForced || !m_pRefBtn); }
+};
+
+class ScRefHandler : public IAnyRefDialog
+{
+ weld::DialogController* m_pController;
+ bool m_bInRefMode;
+
+private:
+ ScFormulaReferenceHelper
+ m_aHelper;
+ SfxBindings* m_pMyBindings;
+
+ OUString m_aDocName; // document on which the dialog was opened
+
+protected:
+ void disposeRefHandler();
+ bool DoClose( sal_uInt16 nId );
+
+ void SetDispatcherLock( bool bLock );
+
+ virtual void RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton = nullptr ) override;
+ virtual void RefInputDone( bool bForced = false ) override;
+
+ bool ParseWithNames( ScRangeList& rRanges, std::u16string_view rStr, const ScDocument& pDoc );
+
+public:
+ ScRefHandler(SfxDialogController &rController, SfxBindings* pB, bool bBindRef);
+ virtual ~ScRefHandler() COVERITY_NOEXCEPT_FALSE override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override = 0;
+ virtual void AddRefEntry() override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual bool IsTableLocked() const override;
+ virtual bool IsDocAllowed( SfxObjectShell* pDocSh ) const override;
+
+ virtual void ShowReference(const OUString& rStr) override;
+ virtual void HideReference( bool bDoneRefMode = true ) override;
+
+ virtual void ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton ) override;
+ virtual void ReleaseFocus( formula::RefEdit* pEdit ) override;
+
+ virtual void ViewShellChanged() override;
+ void SwitchToDocument();
+
+ virtual void SetActive() override = 0;
+
+public:
+ bool EnterRefMode();
+ bool LeaveRefMode();
+ static bool CanInputStart( const formula::RefEdit *pEdit )
+ {
+ return ScFormulaReferenceHelper::CanInputStart( pEdit );
+ }
+ bool CanInputDone( bool bForced )
+ {
+ return m_aHelper.CanInputDone( bForced );
+ }
+};
+
+template<class TBase, bool bBindRef = true>
+struct ScRefHdlrControllerImpl : public TBase, public ScRefHandler
+{
+ enum { UNKNOWN_SLOTID = 0U, SLOTID = UNKNOWN_SLOTID };
+
+ ScRefHdlrControllerImpl(weld::Window* pParent, const OUString& rUIXMLDescription, const OUString& rID, const SfxItemSet* pArg, SfxBindings *pB)
+ : TBase(pParent, rUIXMLDescription, rID, pArg)
+ , ScRefHandler(*static_cast<TBase*>(this), pB, bBindRef)
+ {
+ }
+
+ ScRefHdlrControllerImpl(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, const OUString& rUIXMLDescription, const OUString& rID)
+ : TBase(pB, pCW, pParent, rUIXMLDescription, rID)
+ , ScRefHandler(*static_cast<TBase*>(this), pB, bBindRef)
+ {
+ }
+};
+
+struct ScAnyRefDlgController : ScRefHdlrControllerImpl<SfxModelessDialogController>
+{
+ ScAnyRefDlgController(SfxBindings* rt1, SfxChildWindow* rt2, weld::Window* rt3, const OUString& rt4, const OUString& rt5)
+ : ScRefHdlrControllerImpl<SfxModelessDialogController>(rt1, rt2, rt3, rt4, rt5)
+ {
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/areasave.hxx b/sc/source/ui/inc/areasave.hxx
new file mode 100644
index 0000000000..0b317c6eed
--- /dev/null
+++ b/sc/source/ui/inc/areasave.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 <address.hxx>
+
+#include <memory>
+#include <vector>
+
+class ScDocument;
+class ScAreaLink;
+
+class ScAreaLinkSaver
+{
+private:
+ OUString aFileName;
+ OUString aFilterName;
+ OUString aOptions;
+ OUString aSourceArea;
+ ScRange aDestArea;
+ sal_Int32 nRefreshDelaySeconds;
+
+public:
+ ScAreaLinkSaver( const ScAreaLink& rSource );
+
+ bool IsEqual( const ScAreaLink& rCompare ) const;
+ bool IsEqualSource( const ScAreaLink& rCompare ) const;
+
+ void WriteToLink( ScAreaLink& rLink ) const;
+ void InsertNewLink( ScDocument* pDoc );
+};
+
+class ScAreaLinkSaveCollection
+{
+ typedef ::std::vector<ScAreaLinkSaver> DataType;
+ DataType maData;
+public:
+ ScAreaLinkSaveCollection();
+ ~ScAreaLinkSaveCollection();
+
+ bool IsEqual( const ScDocument* pDoc ) const;
+ void Restore( ScDocument* pDoc );
+
+ // returns NULL if empty
+ static std::unique_ptr<ScAreaLinkSaveCollection> CreateFromDoc( const ScDocument* pDoc );
+
+ ScAreaLinkSaver& operator[](size_t nIndex);
+ const ScAreaLinkSaver& operator[](size_t nIndex) const;
+ size_t size() const;
+ void push_back(const ScAreaLinkSaver&);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/areasdlg.hxx b/sc/source/ui/inc/areasdlg.hxx
new file mode 100644
index 0000000000..be4d778d4e
--- /dev/null
+++ b/sc/source/ui/inc/areasdlg.hxx
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <address.hxx>
+
+#include "anyrefdg.hxx"
+
+class ScDocument;
+class ScViewData;
+class SfxStringItem;
+
+class ScPrintAreasDlg : public ScAnyRefDlgController
+{
+public:
+ ScPrintAreasDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent);
+ virtual ~ScPrintAreasDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual void AddRefEntry() override;
+
+ virtual bool IsTableLocked() const override;
+
+ virtual void SetActive() override;
+ virtual void Deactivate() override;
+ virtual void Close() override;
+
+private:
+ bool bDlgLostFocus;
+ ScDocument* pDoc;
+ ScViewData* pViewData;
+ SCTAB nCurTab;
+
+ formula::RefEdit* m_pRefInputEdit;
+
+ std::unique_ptr<weld::ComboBox> m_xLbPrintArea;
+ std::unique_ptr<formula::RefEdit> m_xEdPrintArea;
+ std::unique_ptr<formula::RefButton> m_xRbPrintArea;
+
+ std::unique_ptr<weld::ComboBox> m_xLbRepeatRow;
+ std::unique_ptr<formula::RefEdit> m_xEdRepeatRow;
+ std::unique_ptr<formula::RefButton> m_xRbRepeatRow;
+
+ std::unique_ptr<weld::ComboBox> m_xLbRepeatCol;
+ std::unique_ptr<formula::RefEdit> m_xEdRepeatCol;
+ std::unique_ptr<formula::RefButton> m_xRbRepeatCol;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ std::unique_ptr<weld::Frame> m_xPrintFrame;
+ std::unique_ptr<weld::Frame> m_xRowFrame;
+ std::unique_ptr<weld::Frame> m_xColFrame;
+
+ std::unique_ptr<weld::Label> m_xPrintFrameFT;
+ std::unique_ptr<weld::Label> m_xRowFrameFT;
+ std::unique_ptr<weld::Label> m_xColFrameFT;
+
+ void Impl_Reset();
+ bool Impl_CheckRefStrings();
+ void Impl_FillLists();
+ bool Impl_GetItem( const formula::RefEdit* pEd, SfxStringItem& rItem );
+
+ // Handler:
+ DECL_LINK( Impl_SelectHdl, weld::ComboBox&, void );
+ DECL_LINK( Impl_ModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( Impl_BtnHdl, weld::Button&, void );
+ DECL_LINK( Impl_GetEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( Impl_GetFocusHdl, weld::Widget&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/asciiopt.hxx b/sc/source/ui/inc/asciiopt.hxx
new file mode 100644
index 0000000000..6028b8825d
--- /dev/null
+++ b/sc/source/ui/inc/asciiopt.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <i18nlangtag/lang.h>
+
+#include "csvcontrol.hxx"
+
+class ScAsciiOptions
+{
+private:
+ bool bFixedLen;
+ OUString aFieldSeps;
+ bool bMergeFieldSeps;
+ bool bRemoveSpace;
+ bool bQuotedFieldAsText;
+ bool bDetectSpecialNumber;
+ bool bDetectScientificNumber;
+ bool bEvaluateFormulas;
+ bool bSkipEmptyCells;
+ bool bSaveAsShown;
+ bool bSaveFormulas;
+ bool bIncludeBOM;
+ sal_Unicode cTextSep;
+ rtl_TextEncoding eCharSet;
+ LanguageType eLang;
+ bool bCharSetSystem;
+ sal_Int32 nStartRow;
+ std::vector<sal_Int32> mvColStart;
+ std::vector<sal_uInt8> mvColFormat;
+
+public:
+ ScAsciiOptions();
+
+ static const sal_Unicode cDefaultTextSep = '"';
+
+ void ReadFromString( std::u16string_view rString );
+ OUString WriteToString() const;
+
+ rtl_TextEncoding GetCharSet() const { return eCharSet; }
+ const OUString& GetFieldSeps() const { return aFieldSeps; }
+ bool IsMergeSeps() const { return bMergeFieldSeps; }
+ bool IsRemoveSpace() const { return bRemoveSpace; }
+ bool IsQuotedAsText() const { return bQuotedFieldAsText; }
+ bool IsDetectSpecialNumber() const { return bDetectSpecialNumber; }
+ bool IsDetectScientificNumber() const { return bDetectScientificNumber; }
+ bool IsEvaluateFormulas() const { return bEvaluateFormulas; }
+ bool IsSkipEmptyCells() const { return bSkipEmptyCells; }
+ bool GetIncludeBOM() const { return bIncludeBOM; }
+ sal_Unicode GetTextSep() const { return cTextSep; }
+ bool IsFixedLen() const { return bFixedLen; }
+ sal_uInt16 GetInfoCount() const { return mvColStart.size(); }
+ const sal_Int32* GetColStart() const { return mvColStart.data(); }
+ const sal_uInt8* GetColFormat() const { return mvColFormat.data(); }
+ sal_Int32 GetStartRow() const { return nStartRow; }
+ LanguageType GetLanguage() const { return eLang; }
+
+ void SetCharSet( rtl_TextEncoding eNew ) { eCharSet = eNew; }
+ void SetCharSetSystem( bool bSet ) { bCharSetSystem = bSet; }
+ void SetFixedLen( bool bSet ) { bFixedLen = bSet; }
+ void SetFieldSeps( const OUString& rStr ) { aFieldSeps = rStr; }
+ void SetMergeSeps( bool bSet ) { bMergeFieldSeps = bSet; }
+ void SetRemoveSpace( bool bSet ) { bRemoveSpace = bSet; }
+ void SetQuotedAsText(bool bSet) { bQuotedFieldAsText = bSet; }
+ void SetDetectSpecialNumber(bool bSet) { bDetectSpecialNumber = bSet; }
+ void SetDetectScientificNumber(bool bSet){ bDetectScientificNumber = bSet; }
+ void SetEvaluateFormulas(bool bSet) { bEvaluateFormulas = bSet; }
+ void SetSkipEmptyCells(bool bSet) { bSkipEmptyCells = bSet; }
+ void SetIncludeBOM(bool bVal) { bIncludeBOM = bVal; }
+ void SetTextSep( sal_Unicode c ) { cTextSep = c; }
+ void SetStartRow( sal_Int32 nRow) { nStartRow= nRow; }
+ void SetLanguage(LanguageType e) { eLang = e; }
+
+ void SetColumnInfo( const ScCsvExpDataVec& rDataVec );
+
+ /** From the import field separators obtain the one most likely to be used
+ for export, if multiple separators weighted comma, tab, semicolon,
+ space and other.
+
+ @param bDecodeNumbers
+ If TRUE, the separators are encoded as numbers and need to be
+ decoded before characters can be extracted, for example "59/44"
+ to ";,".
+ If FALSE, the string is taken as is and each character is
+ expected to be one separator.
+ */
+ static sal_Unicode GetWeightedFieldSep( const OUString & rFieldSeps, bool bDecodeNumbers );
+};
+
+/// How ScImportAsciiDlg is called
+enum ScImportAsciiCall {
+ SC_IMPORTFILE, // with File > Open: Text - CSV
+ SC_PASTETEXT, // with Paste > Unformatted Text
+ SC_TEXTTOCOLUMNS }; // with Data > Text to Columns
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/attrdlg.hxx b/sc/source/ui/inc/attrdlg.hxx
new file mode 100644
index 0000000000..1f4268481f
--- /dev/null
+++ b/sc/source/ui/inc/attrdlg.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 <sfx2/tabdlg.hxx>
+
+namespace weld
+{
+class Window;
+}
+class SfxItemSet;
+
+class ScAttrDlg : public SfxTabDialogController
+{
+public:
+ ScAttrDlg(weld::Window* pParent, const SfxItemSet* pCellAttrs);
+ virtual ~ScAttrDlg() override;
+
+protected:
+ virtual void PageCreated(const OUString& rPageId, SfxTabPage& rTabPage) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/auditsh.hxx b/sc/source/ui/inc/auditsh.hxx
new file mode 100644
index 0000000000..74a17cccab
--- /dev/null
+++ b/sc/source/ui/inc/auditsh.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+
+#include <shellids.hxx>
+
+class ScViewData;
+
+class ScAuditingShell : public SfxShell
+{
+private:
+ ScViewData& rViewData;
+ sal_uInt16 nFunction;
+
+public:
+ SFX_DECL_INTERFACE(SCID_AUDITING_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScAuditingShell(ScViewData& rData);
+ virtual ~ScAuditingShell() override;
+
+ void Execute(const SfxRequest& rReq);
+ void GetState(SfxItemSet& rSet);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/autofmt.hxx b/sc/source/ui/inc/autofmt.hxx
new file mode 100644
index 0000000000..b0bc8b61ba
--- /dev/null
+++ b/sc/source/ui/inc/autofmt.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 <svx/framelinkarray.hxx>
+#include <scdllapi.h>
+#include <vcl/customweld.hxx>
+
+namespace com::sun::star::i18n { class XBreakIterator; }
+
+class ScAutoFormatData;
+class SvxBoxItem;
+class SvxLineItem;
+class SvNumberFormatter;
+class VirtualDevice;
+class ScViewData;
+
+class SC_DLLPUBLIC ScAutoFmtPreview : public weld::CustomWidgetController
+{
+public:
+ ScAutoFmtPreview();
+ void DetectRTL(const ScViewData& rViewData);
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ virtual ~ScAutoFmtPreview() override;
+
+ void NotifyChange( ScAutoFormatData* pNewData );
+
+protected:
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual void Resize() override;
+
+private:
+ ScAutoFormatData* pCurData;
+ ScopedVclPtrInstance<VirtualDevice> aVD;
+ css::uno::Reference<css::i18n::XBreakIterator> xBreakIter;
+ bool bFitWidth;
+ svx::frame::Array maArray; /// Implementation to draw the frame borders.
+ bool mbRTL;
+ Size aPrvSize;
+ tools::Long mnLabelColWidth;
+ tools::Long mnDataColWidth1;
+ tools::Long mnDataColWidth2;
+ tools::Long mnRowHeight;
+ const OUString aStrJan;
+ const OUString aStrFeb;
+ const OUString aStrMar;
+ const OUString aStrNorth;
+ const OUString aStrMid;
+ const OUString aStrSouth;
+ const OUString aStrSum;
+ std::unique_ptr<SvNumberFormatter> pNumFmt;
+
+ SAL_DLLPRIVATE void Init();
+ SAL_DLLPRIVATE void DoPaint(vcl::RenderContext& rRenderContext);
+ SAL_DLLPRIVATE void CalcCellArray(bool bFitWidth);
+ SAL_DLLPRIVATE void CalcLineMap();
+ SAL_DLLPRIVATE void PaintCells(vcl::RenderContext& rRenderContext);
+
+/* Usage of type size_t instead of SCCOL/SCROW is correct here - used in
+ conjunction with class svx::frame::Array (svx/framelinkarray.hxx), which
+ expects size_t coordinates. */
+
+ SAL_DLLPRIVATE sal_uInt16 GetFormatIndex( size_t nCol, size_t nRow ) const;
+ SAL_DLLPRIVATE const SvxBoxItem& GetBoxItem( size_t nCol, size_t nRow ) const;
+ SAL_DLLPRIVATE const SvxLineItem& GetDiagItem( size_t nCol, size_t nRow, bool bTLBR ) const;
+
+ SAL_DLLPRIVATE void DrawString(vcl::RenderContext& rRenderContext, size_t nCol, size_t nRow);
+ SAL_DLLPRIVATE void DrawBackground(vcl::RenderContext& rRenderContext);
+
+ SAL_DLLPRIVATE void MakeFonts(vcl::RenderContext const& rRenderContext, sal_uInt16 nIndex,
+ vcl::Font& rFont, vcl::Font& rCJKFont, vcl::Font& rCTLFont);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/autostyl.hxx b/sc/source/ui/inc/autostyl.hxx
new file mode 100644
index 0000000000..891455cc82
--- /dev/null
+++ b/sc/source/ui/inc/autostyl.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <utility>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <tools/solar.h>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+
+class ScDocShell;
+class ScRange;
+
+struct ScAutoStyleData
+{
+ sal_uLong nTimeout;
+ ScRange aRange;
+ OUString aStyle;
+
+ ScAutoStyleData( sal_uLong nT, const ScRange& rR, OUString aT ) :
+ nTimeout(nT), aRange(rR), aStyle(std::move(aT)) {}
+};
+struct ScAutoStyleInitData
+{
+ ScRange aRange;
+ OUString aStyle1;
+ sal_uLong nTimeout;
+ OUString aStyle2;
+
+ ScAutoStyleInitData( const ScRange& rR, OUString aSt1, sal_uLong nT, OUString aSt2 ) :
+ aRange(rR), aStyle1(std::move(aSt1)), nTimeout(nT), aStyle2(std::move(aSt2)) {}
+};
+
+
+class ScAutoStyleList
+{
+private:
+
+ ScDocShell* pDocSh;
+ Timer aTimer;
+ Idle aInitIdle;
+ sal_uLong nTimerStart;
+ std::vector<ScAutoStyleData> aEntries;
+ std::vector<ScAutoStyleInitData> aInitials;
+
+ void ExecuteEntries();
+ void AdjustEntries(sal_uLong nDiff);
+ void StartTimer(sal_uLong nNow);
+ DECL_LINK( TimerHdl, Timer*, void );
+ DECL_LINK( InitHdl, Timer*, void );
+
+public:
+ ScAutoStyleList(ScDocShell* pShell);
+ ~ScAutoStyleList();
+
+ void AddInitial( const ScRange& rRange, const OUString& rStyle1,
+ sal_uLong nTimeout, const OUString& rStyle2 );
+ void AddEntry( sal_uLong nTimeout, const ScRange& rRange, const OUString& rStyle );
+
+ void ExecuteAllNow();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/cbnumberformat.hxx b/sc/source/ui/inc/cbnumberformat.hxx
new file mode 100644
index 0000000000..5f2920ab43
--- /dev/null
+++ b/sc/source/ui/inc/cbnumberformat.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 .
+ */
+
+#pragma once
+
+#include <vcl/InterimItemWindow.hxx>
+
+class ScNumberFormat final : public InterimItemWindow
+{
+public:
+ explicit ScNumberFormat(vcl::Window* pParent);
+ virtual void dispose() override;
+ virtual ~ScNumberFormat() override;
+
+ virtual void GetFocus() override;
+
+ void set_active(int nPos) { m_xWidget->set_active(nPos); }
+
+private:
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+
+ DECL_STATIC_LINK(ScNumberFormat, NumFormatSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/cbutton.hxx b/sc/source/ui/inc/cbutton.hxx
new file mode 100644
index 0000000000..5b22c1a83e
--- /dev/null
+++ b/sc/source/ui/inc/cbutton.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 <tools/gen.hxx>
+#include <vcl/vclptr.hxx>
+
+class OutputDevice;
+
+class ScDDComboBoxButton final
+{
+public:
+ ScDDComboBoxButton( OutputDevice* pOutputDevice );
+ ~ScDDComboBoxButton();
+
+ void SetOutputDevice( OutputDevice* pOutputDevice );
+
+ void Draw( const Point& rAt,
+ const Size& rSize );
+ void Draw()
+ { Draw( aBtnPos, aBtnSize ); }
+
+ void SetOptSizePixel();
+
+ void SetPosPixel( const Point& rNewPos ) { aBtnPos = rNewPos; }
+ const Point& GetPosPixel() const { return aBtnPos; }
+
+ void SetSizePixel( const Size& rNewSize ) { aBtnSize = rNewSize; }
+ const Size& GetSizePixel() const { return aBtnSize; }
+
+private:
+ void ImpDrawArrow( const tools::Rectangle& rRect );
+
+ VclPtr<OutputDevice> pOut;
+ Point aBtnPos;
+ Size aBtnSize;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/cellmergeoption.hxx b/sc/source/ui/inc/cellmergeoption.hxx
new file mode 100644
index 0000000000..600a5e03dd
--- /dev/null
+++ b/sc/source/ui/inc/cellmergeoption.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include <address.hxx>
+
+#include <set>
+
+struct ScCellMergeOption
+{
+ ::std::set<SCTAB> maTabs;
+ SCCOL mnStartCol;
+ SCROW mnStartRow;
+ SCCOL mnEndCol;
+ SCROW mnEndRow;
+ bool mbCenter;
+
+ explicit ScCellMergeOption(const ScRange& rRange);
+ SC_DLLPUBLIC explicit ScCellMergeOption(SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ bool bCenter = false);
+
+ ScRange getSingleRange(SCTAB nTab) const;
+ ScRange getFirstSingleRange() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/cellsh.hxx b/sc/source/ui/inc/cellsh.hxx
new file mode 100644
index 0000000000..023d516084
--- /dev/null
+++ b/sc/source/ui/inc/cellsh.hxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+#include <unotools/caserotate.hxx>
+#include <tools/link.hxx>
+#include <memory>
+#include "formatsh.hxx"
+#include <rtl/ref.hxx>
+#include <sot/formats.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/window.hxx>
+
+class SvxClipboardFormatItem;
+class TransferableDataHelper;
+class TransferableClipboardListener;
+class AbstractScLinkedAreaDlg;
+
+struct CellShell_Impl
+{
+ rtl::Reference<TransferableClipboardListener>
+ m_xClipEvtLstnr;
+ VclPtr<AbstractScLinkedAreaDlg> m_pLinkedDlg;
+ SfxRequest* m_pRequest;
+
+ CellShell_Impl();
+ ~CellShell_Impl();
+};
+
+class ScCellShell final : public ScFormatShell
+{
+private:
+ std::unique_ptr<CellShell_Impl> pImpl;
+ bool bPastePossible;
+
+ void GetPossibleClipboardFormats( SvxClipboardFormatItem& rFormats );
+ bool HasClipboardFormat( SotClipboardFormatId nFormatId );
+ void ExecuteExternalSource(
+ const OUString& _rFile, const OUString& _rFilter, const OUString& _rOptions,
+ const OUString& _rSource, sal_Int32 _nRefreshDelaySeconds, SfxRequest& _rRequest );
+
+ void ExecuteDataPilotDialog();
+ void ExecuteXMLSourceDialog();
+ void ExecuteSubtotals(SfxRequest& rReq);
+
+ void ExecuteFillSingleEdit();
+
+ DECL_LINK( ClipboardChanged, TransferableDataHelper*, void );
+
+ RotateTransliteration m_aRotateCase;
+
+ VclPtr<vcl::Window> pFrameWin;
+
+public:
+ SFX_DECL_INTERFACE(SCID_CELL_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScCellShell( ScViewData& rData, const VclPtr<vcl::Window>& pFrameWin );
+ virtual ~ScCellShell() override;
+
+ void Execute(SfxRequest &);
+ void GetState(SfxItemSet &);
+
+ void ExecuteEdit( SfxRequest& rReq );
+ void ExecuteTrans( SfxRequest& rReq );
+ void ExecuteRotateTrans( const SfxRequest& rReq );
+
+ void GetBlockState( SfxItemSet& rSet );
+ void GetCellState( SfxItemSet& rSet );
+
+ void ExecuteDB( SfxRequest& rReq );
+ void GetDBState( SfxItemSet& rSet );
+
+ void GetClipState( SfxItemSet& rSet );
+ void GetHLinkState( SfxItemSet& rSet );
+
+ void ExecuteCursor( SfxRequest& rReq );
+ void ExecuteCursorSel( SfxRequest& rReq );
+ void ExecutePage( SfxRequest& rReq );
+ void ExecutePageSel( SfxRequest& rReq );
+ void ExecuteMove( SfxRequest& rReq );
+
+ static void GetStateCursor( SfxItemSet& rSet );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/chartsh.hxx b/sc/source/ui/inc/chartsh.hxx
new file mode 100644
index 0000000000..9eee30a1a9
--- /dev/null
+++ b/sc/source/ui/inc/chartsh.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 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+#include "drawsh.hxx"
+
+class ScViewData;
+
+class ScChartShell final : public ScDrawShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_CHART_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+ virtual void Activate(bool bMDI) override;
+ virtual void Deactivate(bool bMDI) override;
+
+public:
+ ScChartShell(ScViewData& rData);
+ virtual ~ScChartShell() override;
+
+ void ExecuteExportAsGraphic(SfxRequest& rReq);
+ void GetExportAsGraphicState(SfxItemSet& rSet);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/checklistmenu.hxx b/sc/source/ui/inc/checklistmenu.hxx
new file mode 100644
index 0000000000..1e85b17c48
--- /dev/null
+++ b/sc/source/ui/inc/checklistmenu.hxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/timer.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+
+#include <memory>
+#include <unordered_set>
+#include <map>
+#include <set>
+
+class ScCheckListMenuControl;
+class ScViewData;
+struct ScCheckListMember;
+struct ImplSVEvent;
+
+struct ScCheckListMember
+{
+ enum DatePartType
+ {
+ YEAR,
+ MONTH,
+ DAY,
+ };
+
+ OUString maName; // node name
+ OUString maRealName;
+ double mnValue; // number value of filter condition
+ bool mbVisible;
+ bool mbHiddenByOtherFilter;
+ bool mbDate;
+ bool mbLeaf;
+ bool mbValue; // true if the filter condition is value
+ DatePartType meDatePartType;
+ // To store Year and Month if the member if DAY type
+ std::vector<OUString> maDateParts;
+ ScCheckListMember();
+ std::unique_ptr<weld::TreeIter> mxParent;
+};
+
+class ScCheckListMenuWindow;
+class ScListSubMenuControl;
+
+/**
+ * This class implements a popup window for the auto filter dropdown.
+ */
+class ScCheckListMenuControl final
+{
+public:
+ static constexpr size_t MENU_NOT_SELECTED = 999;
+
+ /**
+ * Action to perform when an event takes place. Create a sub-class of
+ * this to implement the desired action.
+ */
+ class Action
+ {
+ public:
+ virtual ~Action() {}
+ // return true to dismiss the popup
+ virtual bool execute() = 0;
+ };
+
+ struct ResultEntry
+ {
+ OUString aName;
+ double nValue; // number value of filter condition
+ bool bValid;
+ bool bDate;
+ bool bValue; // true if the filter condition is value
+
+ bool operator<(const ResultEntry& rhs) const
+ {
+ return aName < rhs.aName;
+ }
+
+ bool operator == (const ResultEntry& rhs) const
+ {
+ return aName == rhs.aName &&
+ bValid == rhs.bValid &&
+ bDate == rhs.bDate &&
+ bValue == rhs.bValue &&
+ nValue == rhs.nValue;
+ }
+ };
+ typedef std::set<ResultEntry> ResultType;
+
+ struct MenuItemData
+ {
+ bool mbEnabled:1;
+ std::shared_ptr<Action> mxAction;
+ std::unique_ptr<ScListSubMenuControl> mxSubMenuWin;
+
+ MenuItemData();
+ };
+
+ /**
+ * Extended data that the client code may need to store. Create a
+ * sub-class of this and store data there.
+ */
+ struct ExtendedData {
+
+ virtual ~ExtendedData() {}
+
+ };
+
+ /**
+ * Configuration options for this popup window.
+ */
+ struct Config
+ {
+ bool mbAllowEmptySet;
+ bool mbRTL;
+ Config();
+ };
+
+ ScCheckListMenuControl(weld::Widget* pParent, ScViewData& rViewData,
+ bool bTreeMode, int nWidth, bool bIsMultiField = false);
+ ~ScCheckListMenuControl();
+
+ void addMenuItem(const OUString& rText, Action* pAction);
+ void addSeparator();
+ ScListSubMenuControl* addSubMenuItem(const OUString& rText, bool bEnabled, bool bColorMenu);
+
+ void selectMenuItem(size_t nPos, bool bSubMenuTimer);
+ void queueLaunchSubMenu(size_t nPos, ScListSubMenuControl* pMenu);
+
+ void setMemberSize(size_t n);
+ void addDateMember(const OUString& rName, double nVal, bool bVisible, bool bHiddenByOtherFilter);
+ void addMember(const OUString& rName, const double nVal, bool bVisible, bool bHiddenByOtherFilter,
+ bool bValue = false);
+ void clearMembers();
+ size_t initMembers(int nMaxMemberWidth = -1);
+ void setConfig(const Config& rConfig);
+
+ bool isAllSelected() const;
+ void getResult(ResultType& rResult);
+ void launch(weld::Widget* pWidget, const tools::Rectangle& rRect);
+ void close(bool bOK);
+
+ void StartPopupMode(weld::Widget* pParent, const tools::Rectangle& rRect);
+ void EndPopupMode();
+
+ size_t getSubMenuPos(const ScListSubMenuControl* pSubMenu);
+ void setSubMenuFocused(const ScListSubMenuControl* pSubMenu);
+ void queueCloseSubMenu();
+ void clearSelectedMenuItem();
+
+ /**
+ * Set auxiliary data that the client code might need. Note that this
+ * popup window class manages its life time; no explicit deletion of the
+ * instance is needed in the client code.
+ */
+ void setExtendedData(std::unique_ptr<ExtendedData> p);
+
+ /**
+ * Get the store auxiliary data, or NULL if no such data is stored.
+ */
+ ExtendedData* getExtendedData();
+
+ ScViewData& GetViewData() const { return mrViewData; }
+
+ void GrabFocus();
+
+ void setOKAction(Action* p);
+ void setPopupEndAction(Action* p);
+ void setFieldChangedAction(Action* p);
+
+ int GetTextWidth(const OUString& rsName) const;
+ int IncreaseWindowWidthToFitText(int nMaxTextWidth);
+
+ /**
+ * Dismiss all visible popup menus and set focus back to the application
+ * window. This method is called e.g. when a menu action is fired.
+ */
+ void terminateAllPopupMenus();
+
+ void endSubMenu(ScListSubMenuControl& rSubMenu);
+
+ void addFields(const std::vector<OUString>& aFields);
+ tools::Long getField();
+private:
+
+ std::vector<MenuItemData> maMenuItems;
+
+ /**
+ * Calculate the appropriate window size based on the menu items.
+ */
+ void prepWindow();
+ void setAllMemberState(bool bSet);
+ void selectCurrentMemberOnly(bool bSet);
+ void updateMemberParents(const weld::TreeIter* pLeaf, size_t nIdx);
+
+ std::unique_ptr<weld::TreeIter> ShowCheckEntry(const OUString& sName, ScCheckListMember& rMember, bool bShow = true, bool bCheck = true);
+ void CheckEntry(std::u16string_view sName, const weld::TreeIter* pParent, bool bCheck);
+ void CheckEntry(const weld::TreeIter& rEntry, bool bCheck);
+ void GetRecursiveChecked(const weld::TreeIter* pEntry, std::unordered_set<OUString>& vOut, OUString& rLabel);
+ std::unordered_set<OUString> GetAllChecked();
+ bool IsChecked(std::u16string_view sName, const weld::TreeIter* pParent);
+ int GetCheckedEntryCount() const;
+ void CheckAllChildren(const weld::TreeIter& rEntry, bool bCheck);
+
+ void setSelectedMenuItem(size_t nPos);
+
+ std::unique_ptr<weld::TreeIter> FindEntry(const weld::TreeIter* pParent, std::u16string_view sNode);
+
+ void executeMenuItem(size_t nPos);
+
+ /**
+ * Get the area of the active row. Suitable as the parent rectangle
+ * argument for Executing a popup
+ */
+ tools::Rectangle GetSubMenuParentRect();
+
+ struct SubMenuItemData;
+
+ void handleMenuTimeout(const SubMenuItemData* pTimer);
+
+ void launchSubMenu();
+
+ void CreateDropDown();
+
+ DECL_LINK(ButtonHdl, weld::Button&, void);
+ DECL_LINK(TriStateHdl, weld::Toggleable&, void);
+
+ void Check(const weld::TreeIter* pIter);
+
+ DECL_LINK(CheckHdl, const weld::TreeView::iter_col&, void);
+
+ DECL_LINK(PopupModeEndHdl, weld::Popover&, void);
+
+ DECL_LINK(SearchEditTimeoutHdl, Timer*, void);
+ DECL_LINK(ComboChangedHdl, weld::ComboBox&, void);
+
+ DECL_LINK(EdModifyHdl, weld::Entry&, void);
+ DECL_LINK(EdActivateHdl, weld::Entry&, bool);
+
+ DECL_LINK(RowActivatedHdl, weld::TreeView& rMEvt, bool);
+ DECL_LINK(SelectHdl, weld::TreeView&, void);
+ DECL_LINK(TreeSizeAllocHdl, const Size&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(MenuKeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(MouseEnterHdl, const MouseEvent&, bool);
+
+ DECL_LINK(PostPopdownHdl, void*, void);
+
+ void SetDropdownPos();
+
+ DECL_LINK(SetDropdownPosHdl, void*, void);
+
+ DECL_LINK(CommandHdl, const CommandEvent&, bool);
+
+ void ResizeToRequest();
+
+ void DropPendingEvents();
+
+private:
+ std::unique_ptr<weld::Builder> mxBuilder;
+ std::unique_ptr<weld::Popover> mxPopover;
+ std::unique_ptr<weld::Container> mxContainer;
+ std::unique_ptr<weld::TreeView> mxMenu;
+ std::unique_ptr<weld::TreeIter> mxScratchIter;
+ std::unique_ptr<weld::Widget> mxNonMenu;
+ std::unique_ptr<weld::Label> mxFieldsComboLabel;
+ std::unique_ptr<weld::ComboBox> mxFieldsCombo;
+ std::unique_ptr<weld::Entry> mxEdSearch;
+ std::unique_ptr<weld::Widget> mxBox;
+ std::unique_ptr<weld::TreeView> mxListChecks;
+ std::unique_ptr<weld::TreeView> mxTreeChecks;
+ weld::TreeView* mpChecks;
+
+ std::unique_ptr<weld::CheckButton> mxChkToggleAll;
+ std::unique_ptr<weld::Button> mxBtnSelectSingle;
+ std::unique_ptr<weld::Button> mxBtnUnselectSingle;
+
+ std::unique_ptr<weld::Box> mxButtonBox;
+ std::unique_ptr<weld::Button> mxBtnOk;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+ std::unique_ptr<weld::Menu> mxContextMenu;
+
+ ScopedVclPtr<VirtualDevice> mxDropDown;
+
+ std::vector<ScCheckListMember> maMembers;
+ // For Dates
+ std::map<OUString, size_t> maYearMonthMap;
+
+ std::unique_ptr<ExtendedData> mxExtendedData;
+ std::unique_ptr<Action> mxOKAction;
+ std::unique_ptr<Action> mxPopupEndAction;
+ std::unique_ptr<Action> mxFieldChangedAction;
+
+ Config maConfig;
+ Size maAllocatedSize;
+ int mnCheckWidthReq; /// matching width request for mxChecks
+ int mnWndWidth; /// whole window width.
+ int mnCheckListVisibleRows;
+ TriState mePrevToggleAllState;
+
+ size_t mnSelectedMenu;
+
+ ScViewData& mrViewData;
+
+ ImplSVEvent* mnAsyncPostPopdownId;
+ ImplSVEvent* mnAsyncSetDropdownPosId;
+
+ bool mbHasDates;
+ bool mbIsPoppedUp;
+
+ struct SubMenuItemData
+ {
+ Timer maTimer;
+ ScListSubMenuControl* mpSubMenu;
+ size_t mnMenuPos;
+
+ DECL_LINK( TimeoutHdl, Timer*, void );
+
+ SubMenuItemData(ScCheckListMenuControl* pParent);
+ void reset();
+
+ private:
+ ScCheckListMenuControl* mpParent;
+ };
+
+ SubMenuItemData maOpenTimer;
+ SubMenuItemData maCloseTimer;
+
+ Timer maSearchEditTimer;
+ bool mbIsMultiField;
+};
+
+class ScListSubMenuControl final
+{
+public:
+ ScListSubMenuControl(weld::Widget* pParent, ScCheckListMenuControl& rParentControl, bool bColorMenu);
+
+ void setPopupStartAction(ScCheckListMenuControl::Action* p);
+
+ void GrabFocus();
+ bool IsVisible() const;
+
+ void StartPopupMode(weld::Widget* pParent, const tools::Rectangle& rRect);
+ void EndPopupMode();
+
+ void addMenuItem(const OUString& rText, ScCheckListMenuControl::Action* pAction);
+ // nMenu of 0 for background color, nMenu of 1 for text color
+ void addMenuColorItem(const OUString& rText, bool bActive, VirtualDevice& rImage,
+ int nMenu, ScCheckListMenuControl::Action* pAction);
+ void addSeparator();
+ void clearMenuItems();
+ void resizeToFitMenuItems();
+
+ ScViewData& GetViewData() const { return mrParentControl.GetViewData(); }
+ ScCheckListMenuControl::ExtendedData* getExtendedData() { return mrParentControl.getExtendedData(); }
+ VclPtr<VirtualDevice> create_virtual_device() const { return mxMenu->create_virtual_device(); }
+
+ /**
+ * Dismiss all visible popup menus and set focus back to the application
+ * window. This method is called e.g. when a menu action is fired.
+ */
+ void terminateAllPopupMenus();
+
+private:
+ std::unique_ptr<weld::Builder> mxBuilder;
+ std::unique_ptr<weld::Popover> mxPopover;
+ std::unique_ptr<weld::Container> mxContainer;
+ std::unique_ptr<weld::TreeView> mxMenu;
+ std::unique_ptr<weld::TreeView> mxBackColorMenu;
+ std::unique_ptr<weld::TreeView> mxTextColorMenu;
+ std::unique_ptr<weld::TreeIter> mxScratchIter;
+ std::unique_ptr<ScCheckListMenuControl::Action> mxPopupStartAction;
+ std::vector<ScCheckListMenuControl::MenuItemData> maMenuItems;
+ ScCheckListMenuControl& mrParentControl;
+ int mnBackColorMenuPrefHeight;
+ int mnTextColorMenuPrefHeight;
+ bool mbColorMenu;
+
+ DECL_LINK(RowActivatedHdl, weld::TreeView& rMEvt, bool);
+ DECL_LINK(ColorSelChangedHdl, weld::TreeView&, void);
+ DECL_LINK(MenuKeyInputHdl, const KeyEvent&, bool);
+
+ void SetupMenu(weld::TreeView& rMenu);
+
+ void executeMenuItem(ScCheckListMenuControl::Action* pAction);
+ void addItem(ScCheckListMenuControl::Action* pAction);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/client.hxx b/sc/source/ui/inc/client.hxx
new file mode 100644
index 0000000000..bff5079d14
--- /dev/null
+++ b/sc/source/ui/inc/client.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/ipclient.hxx>
+
+class ScTabViewShell;
+class SdrOle2Obj;
+class SdrModel;
+
+class ScClient final : public SfxInPlaceClient
+{
+private:
+ SdrModel* pModel;
+
+ virtual void ObjectAreaChanged() override;
+ virtual void RequestNewObjectArea( tools::Rectangle& ) override;
+ virtual void ViewChanged() override;
+
+public:
+ ScClient( ScTabViewShell* pViewShell, vcl::Window* pDraw, SdrModel* pSdrModel, const SdrOle2Obj* pObj );
+ virtual ~ScClient() override;
+
+ SdrOle2Obj* GetDrawObj();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/cliputil.hxx b/sc/source/ui/inc/cliputil.hxx
new file mode 100644
index 0000000000..dc0ee5b9b8
--- /dev/null
+++ b/sc/source/ui/inc/cliputil.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include <scdllapi.h>
+
+class ScViewData;
+class ScTabViewShell;
+class ScDocument;
+class ScMarkData;
+class ScRangeList;
+
+namespace ScClipUtil
+{
+
+SC_DLLPUBLIC void PasteFromClipboard( ScViewData& rViewData, ScTabViewShell* pTabViewShell, bool bShowDialog );
+
+bool CheckDestRanges(
+ const ScDocument& rDoc, SCCOL nSrcCols, SCROW nSrcRows, const ScMarkData& rMark,
+ const ScRangeList& rDest);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/colorformat.hxx b/sc/source/ui/inc/colorformat.hxx
new file mode 100644
index 0000000000..c30e4c80fa
--- /dev/null
+++ b/sc/source/ui/inc/colorformat.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/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <svx/colorbox.hxx>
+#include <address.hxx>
+
+struct ScDataBarFormatData;
+class ScDocument;
+class SvNumberFormatter;
+
+class ScDataBarSettingsDlg : public weld::GenericDialogController
+{
+private:
+ OUString maStrWarnSameValue;
+ SvNumberFormatter* mpNumberFormatter;
+
+ ScDocument* mpDoc;
+ ScAddress maPos;
+
+ std::unique_ptr<weld::Button> mxBtnOk;
+
+ std::unique_ptr<ColorListBox> mxLbPos;
+ std::unique_ptr<ColorListBox> mxLbNeg;
+ std::unique_ptr<ColorListBox> mxLbAxisCol;
+
+ std::unique_ptr<weld::ComboBox> mxLbFillType;
+ std::unique_ptr<weld::ComboBox> mxLbTypeMin;
+ std::unique_ptr<weld::ComboBox> mxLbTypeMax;
+ std::unique_ptr<weld::ComboBox> mxLbAxisPos;
+
+ std::unique_ptr<weld::Entry> mxEdMin;
+ std::unique_ptr<weld::Entry> mxEdMax;
+ std::unique_ptr<weld::Entry> mxLenMin;
+ std::unique_ptr<weld::Entry> mxLenMax;
+
+ std::unique_ptr<weld::CheckButton> mxCbOnlyBar;
+
+ std::unique_ptr<weld::Label> mxStrSameValueFT;
+
+ DECL_LINK(OkBtnHdl, weld::Button&, void);
+ DECL_LINK(TypeSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(PosSelectHdl, weld::ComboBox&, void);
+
+ void Init();
+
+public:
+ ScDataBarSettingsDlg(weld::Window* pParent, const ScDataBarFormatData& rData, ScDocument* pDoc,
+ const ScAddress& rPos);
+ virtual ~ScDataBarSettingsDlg() override;
+
+ ScDataBarFormatData* GetData();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/colrowba.hxx b/sc/source/ui/inc/colrowba.hxx
new file mode 100644
index 0000000000..536a78dd1d
--- /dev/null
+++ b/sc/source/ui/inc/colrowba.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "hdrcont.hxx"
+#include "viewdata.hxx"
+
+class ScHeaderFunctionSet;
+class ScHeaderSelectionEngine;
+class ScTabView;
+
+class ScColBar : public ScHeaderControl
+{
+ ScHSplitPos meWhich;
+ ScHeaderFunctionSet* mpFuncSet;
+
+public:
+ ScColBar( vcl::Window* pParent, ScHSplitPos eWhich,
+ ScHeaderFunctionSet* pFuncSet, ScHeaderSelectionEngine* pEng,
+ ScTabView* pTab );
+ virtual ~ScColBar() override;
+
+ virtual SCCOLROW GetPos() const override;
+ virtual sal_uInt16 GetEntrySize( SCCOLROW nEntryNo ) const override;
+ virtual OUString GetEntryText( SCCOLROW nEntryNo ) const override;
+
+ virtual bool IsLayoutRTL() const override;
+
+ virtual void SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize ) override;
+ virtual void HideEntries( SCCOLROW nStart, SCCOLROW nEnd ) override;
+
+ virtual void SetMarking( bool bSet ) override;
+ virtual void SelectWindow() override;
+ virtual bool IsDisabled() const override;
+ virtual bool ResizeAllowed() const override;
+
+ virtual void DrawInvert( tools::Long nDragPos ) override;
+
+ virtual OUString GetDragHelp( tools::Long nVal ) override;
+};
+
+class ScRowBar : public ScHeaderControl
+{
+ ScVSplitPos meWhich;
+ ScHeaderFunctionSet* mpFuncSet;
+
+public:
+ ScRowBar( vcl::Window* pParent, ScVSplitPos eWhich,
+ ScHeaderFunctionSet* pFuncSet, ScHeaderSelectionEngine* pEng,
+ ScTabView* pTab );
+ virtual ~ScRowBar() override;
+
+ virtual SCCOLROW GetPos() const override;
+ virtual sal_uInt16 GetEntrySize( SCCOLROW nEntryNo ) const override;
+ virtual OUString GetEntryText( SCCOLROW nEntryNo ) const override;
+
+ virtual bool IsMirrored() const override;
+ virtual SCCOLROW GetHiddenCount( SCCOLROW nEntryNo ) const override;
+
+ virtual void SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize ) override;
+ virtual void HideEntries( SCCOLROW nStart, SCCOLROW nEnd ) override;
+
+ virtual void SetMarking( bool bSet ) override;
+ virtual void SelectWindow() override;
+ virtual bool IsDisabled() const override;
+ virtual bool ResizeAllowed() const override;
+
+ virtual void DrawInvert( tools::Long nDragPos ) override;
+
+ virtual OUString GetDragHelp( tools::Long nVal ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/condformatdlg.hxx b/sc/source/ui/inc/condformatdlg.hxx
new file mode 100644
index 0000000000..51f92750d4
--- /dev/null
+++ b/sc/source/ui/inc/condformatdlg.hxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rangelst.hxx>
+#include "condformatdlgitem.hxx"
+#include "condformatdlgentry.hxx"
+
+#include "anyrefdg.hxx"
+
+#include <memory>
+
+#define DLG_RET_ADD 8
+#define DLG_RET_EDIT 16
+
+class ScDocument;
+class ScConditionalFormat;
+class ScViewData;
+
+class ScCondFormatDlg;
+
+class ScCondFormatList
+{
+private:
+ std::unique_ptr<weld::ScrolledWindow> mxScrollWindow;
+ std::unique_ptr<weld::Container> mxGrid;
+
+ typedef std::vector<std::unique_ptr<ScCondFrmtEntry>> EntryContainer;
+ EntryContainer maEntries;
+
+ bool mbFrozen;
+ bool mbNewEntry;
+
+ ScDocument* mpDoc;
+ ScAddress maPos;
+ ScRangeList maRanges;
+ ScCondFormatDlg* mpDialogParent;
+
+public:
+ ScCondFormatList(ScCondFormatDlg* pParent,
+ std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Container> xGrid);
+ weld::Container* GetContainer() { return mxGrid.get(); }
+ ~ScCondFormatList();
+
+ void init(ScDocument& rDoc, const ScConditionalFormat* pFormat,
+ const ScRangeList& rRanges, const ScAddress& rPos,
+ condformat::dialog::ScCondFormatDialogType eType);
+
+ void SetRange(const ScRangeList& rRange);
+
+ std::unique_ptr<ScConditionalFormat> GetConditionalFormat() const;
+ weld::Window* GetFrameWeld();
+ void Freeze() { mbFrozen = true; }
+ void Thaw() { mbFrozen = false; }
+ void RecalcAll();
+
+ DECL_LINK( AddBtnHdl, weld::Button&, void );
+ DECL_LINK( RemoveBtnHdl, weld::Button&, void );
+ DECL_LINK( UpBtnHdl, weld::Button&, void );
+ DECL_LINK( DownBtnHdl, weld::Button&, void );
+ DECL_LINK( EntrySelectHdl, ScCondFrmtEntry&, void );
+
+ DECL_LINK( TypeListHdl, weld::ComboBox&, void );
+ DECL_LINK( AfterTypeListHdl, void*, void );
+ DECL_LINK( ColFormatTypeHdl, weld::ComboBox&, void );
+ DECL_LINK( AfterColFormatTypeHdl, void*, void );
+};
+
+class ScCondFormatDlg : public ScAnyRefDlgController
+{
+private:
+ sal_Int32 mnKey;
+
+ ScAddress maPos;
+ ScViewData* mpViewData;
+
+ std::shared_ptr<ScCondFormatDlgItem> mpDlgItem;
+
+ OUString msBaseTitle;
+
+ formula::RefEdit* mpLastEdit;
+ std::unique_ptr<weld::Button> mxBtnOk;
+ std::unique_ptr<weld::Button> mxBtnAdd;
+ std::unique_ptr<weld::Button> mxBtnRemove;
+ std::unique_ptr<weld::Button> mxBtnUp;
+ std::unique_ptr<weld::Button> mxBtnDown;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+ std::unique_ptr<weld::Label> mxFtRange;
+ std::unique_ptr<formula::RefEdit> mxEdRange;
+ std::unique_ptr<formula::RefButton> mxRbRange;
+ std::unique_ptr<ScCondFormatList> mxCondFormList;
+
+ void updateTitle();
+ DECL_LINK( EdRangeModifyHdl, formula::RefEdit&, void );
+protected:
+
+ virtual void RefInputDone( bool bForced = false ) override;
+ void OkPressed();
+ void CancelPressed();
+
+public:
+ ScCondFormatDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pWindow,
+ ScViewData* pViewData, const ScCondFormatDlgItem* pDlgItem);
+ virtual ~ScCondFormatDlg() override;
+
+ std::unique_ptr<ScConditionalFormat> GetConditionalFormat() const;
+
+ virtual void SetReference(const ScRange&, ScDocument&) override;
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+ virtual bool IsTableLocked() const override;
+ virtual void Close() override;
+
+ void InvalidateRefData();
+ void OnSelectionChange(size_t nIndex, size_t nSize, bool bSelected = true);
+
+ DECL_LINK( BtnPressedHdl, weld::Button&, void );
+ DECL_LINK( RangeGetFocusHdl, formula::RefEdit&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/condformatdlgentry.hxx b/sc/source/ui/inc/condformatdlgentry.hxx
new file mode 100644
index 0000000000..6725ef94c9
--- /dev/null
+++ b/sc/source/ui/inc/condformatdlgentry.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include <conditio.hxx>
+#include <formula/funcutl.hxx>
+#include <vcl/weld.hxx>
+#include <svl/lstner.hxx>
+#include <svx/fntctrl.hxx>
+
+class ScIconSetFrmtDataEntry;
+class ScCondFormatDlg;
+class ScCondFormatList;
+class ColorListBox;
+class ScColorScaleFormat;
+class ScDataBarFormat;
+class ScIconSetFormat;
+struct ScDataBarFormatData;
+
+namespace condformat::entry {
+
+enum ScCondFrmtEntryType
+{
+ CONDITION,
+ FORMULA,
+ COLORSCALE2,
+ COLORSCALE3,
+ DATABAR,
+ ICONSET,
+ DATE
+};
+
+}
+
+class ScCondFrmtEntry
+{
+protected:
+ ScCondFormatList* mpParent;
+ std::unique_ptr<weld::Builder> mxBuilder;
+
+private:
+ //general ui elements
+ std::unique_ptr<weld::Widget> mxBorder;
+ std::unique_ptr<weld::Container> mxGrid;
+ std::unique_ptr<weld::Label> mxFtCondNr;
+ std::unique_ptr<weld::Label> mxFtCondition;
+
+ bool mbActive;
+ OUString const maStrCondition;
+ Link<ScCondFrmtEntry&,void> maClickHdl;
+
+ DECL_LINK( EntrySelectHdl, const MouseEvent&, bool );
+
+protected:
+ std::unique_ptr<weld::ComboBox> mxLbType;
+
+ ScDocument* mpDoc;
+ ScAddress maPos;
+
+ virtual void Select();
+ virtual void Deselect();
+
+ virtual OUString GetExpressionString() = 0;
+
+public:
+ ScCondFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos);
+ virtual ~ScCondFrmtEntry();
+
+ void Show() { mxGrid->show(); }
+
+ void set_grid_top_attach(int nAttach) { mxBorder->set_grid_top_attach(nAttach); }
+ int get_preferred_height() const { return mxBorder->get_preferred_size().Height(); }
+
+ void SetPos(const ScAddress& rPos) { maPos = rPos; };
+ bool IsSelected() const { return mbActive;}
+ void SetIndex(sal_Int32 nIndex);
+
+ virtual ScFormatEntry* GetEntry() const = 0;
+ virtual void SetActive() = 0;
+ virtual void SetInactive() = 0;
+
+ virtual condformat::entry::ScCondFrmtEntryType GetType() = 0;
+};
+
+class ScConditionFrmtEntry : public ScCondFrmtEntry, public SfxListener
+{
+ //cond format ui elements
+ SvxFontPrevWindow maWdPreview;
+ std::unique_ptr<weld::ComboBox> mxLbCondType;
+ std::unique_ptr<formula::RefEdit> mxEdVal1;
+ std::unique_ptr<formula::RefEdit> mxEdVal2;
+ std::unique_ptr<weld::Label> mxFtVal;
+ std::unique_ptr<weld::Label> mxFtStyle;
+ std::unique_ptr<weld::ComboBox> mxLbStyle;
+ std::unique_ptr<weld::Widget> mxWdPreviewWin;
+ std::unique_ptr<weld::CustomWeld> mxWdPreview;
+ bool mbIsInStyleCreate;
+
+ static const sal_Int32 NUM_COND_ENTRIES = 24;
+ // Lookup table from positions in maLbCondType to the condition mode enum
+ static const ScConditionMode mpEntryToCond[NUM_COND_ENTRIES];
+
+ ScFormatEntry* createConditionEntry() const;
+
+ virtual OUString GetExpressionString() override;
+ void Init(ScCondFormatDlg* pDialogParent);
+ DECL_LINK( StyleSelectHdl, weld::ComboBox&, void );
+ DECL_LINK( ConditionTypeSelectHdl, weld::ComboBox&, void );
+ DECL_LINK( OnEdChanged, formula::RefEdit&, void );
+
+ // Searches the lookup table for the entry position, given condition mode
+ static sal_Int32 ConditionModeToEntryPos( ScConditionMode eMode );
+ // Accesses the lookup table for the condition mode, given entry position
+ static ScConditionMode EntryPosToConditionMode( sal_Int32 aEntryPos );
+ // Returns the number of edit fields used for a given condition mode
+ static sal_Int32 GetNumberEditFields( ScConditionMode eMode );
+
+protected:
+ virtual void Select() override;
+ virtual void Deselect() override;
+
+public:
+ ScConditionFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, ScCondFormatDlg* pDialogParent,
+ const ScAddress& rPos, const ScCondFormatEntry* pFormatEntry = nullptr);
+ virtual ~ScConditionFrmtEntry() override;
+
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::CONDITION; }
+};
+
+class ScFormulaFrmtEntry : public ScCondFrmtEntry
+{
+ SvxFontPrevWindow maWdPreview;
+ std::unique_ptr<weld::Label> mxFtStyle;
+ std::unique_ptr<weld::ComboBox> mxLbStyle;
+ std::unique_ptr<weld::Widget> mxWdPreviewWin;
+ std::unique_ptr<weld::CustomWeld> mxWdPreview;
+ std::unique_ptr<formula::RefEdit> mxEdFormula;
+
+ ScFormatEntry* createFormulaEntry() const;
+ virtual OUString GetExpressionString() override;
+ void Init(ScCondFormatDlg* pDialogParent);
+
+ DECL_LINK(StyleSelectHdl, weld::ComboBox&, void);
+
+public:
+ ScFormulaFrmtEntry(ScCondFormatList* pParent, ScDocument* PDoc, ScCondFormatDlg* pDialogParent, const ScAddress& rPos, const ScCondFormatEntry* pFormatEntry = nullptr);
+ virtual ~ScFormulaFrmtEntry() override;
+
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::FORMULA; }
+};
+
+class ScColorScale2FrmtEntry : public ScCondFrmtEntry
+{
+
+ //color format ui elements
+ std::unique_ptr<weld::ComboBox> mxLbColorFormat;
+
+ //color scale ui elements
+ std::unique_ptr<weld::ComboBox> mxLbEntryTypeMin;
+ std::unique_ptr<weld::ComboBox> mxLbEntryTypeMax;
+
+ std::unique_ptr<weld::Entry> mxEdMin;
+ std::unique_ptr<weld::Entry> mxEdMax;
+
+ std::unique_ptr<ColorListBox> mxLbColMin;
+ std::unique_ptr<ColorListBox> mxLbColMax;
+
+ std::unique_ptr<weld::Label> mxFtMin;
+ std::unique_ptr<weld::Label> mxFtMax;
+
+ ScFormatEntry* createColorscaleEntry() const;
+
+ virtual OUString GetExpressionString() override;
+ void Init();
+
+ DECL_LINK( EntryTypeHdl, weld::ComboBox&, void );
+public:
+ ScColorScale2FrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScColorScaleFormat* pFormat = nullptr);
+ virtual ~ScColorScale2FrmtEntry() override;
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::COLORSCALE2; }
+};
+
+class ScColorScale3FrmtEntry : public ScCondFrmtEntry
+{
+
+ //color format ui elements
+ std::unique_ptr<weld::ComboBox> mxLbColorFormat;
+
+ //color scale ui elements
+ std::unique_ptr<weld::ComboBox> mxLbEntryTypeMin;
+ std::unique_ptr<weld::ComboBox> mxLbEntryTypeMiddle;
+ std::unique_ptr<weld::ComboBox> mxLbEntryTypeMax;
+
+ std::unique_ptr<weld::Entry> mxEdMin;
+ std::unique_ptr<weld::Entry> mxEdMiddle;
+ std::unique_ptr<weld::Entry> mxEdMax;
+
+ std::unique_ptr<ColorListBox> mxLbColMin;
+ std::unique_ptr<ColorListBox> mxLbColMiddle;
+ std::unique_ptr<ColorListBox> mxLbColMax;
+
+ std::unique_ptr<weld::Label> mxFtMin;
+ std::unique_ptr<weld::Label> mxFtMax;
+
+ ScFormatEntry* createColorscaleEntry() const;
+
+ virtual OUString GetExpressionString() override;
+ void Init();
+
+ DECL_LINK( EntryTypeHdl, weld::ComboBox&, void );
+public:
+ ScColorScale3FrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScColorScaleFormat* pFormat = nullptr);
+ virtual ~ScColorScale3FrmtEntry() override;
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::COLORSCALE3; }
+};
+
+class ScDataBarFrmtEntry : public ScCondFrmtEntry
+{
+ //color format ui elements
+ std::unique_ptr<weld::ComboBox> mxLbColorFormat;
+
+ //data bar ui elements
+ std::unique_ptr<weld::ComboBox> mxLbDataBarMinType;
+ std::unique_ptr<weld::ComboBox> mxLbDataBarMaxType;
+ std::unique_ptr<weld::Entry> mxEdDataBarMin;
+ std::unique_ptr<weld::Entry> mxEdDataBarMax;
+ std::unique_ptr<weld::Button> mxBtOptions;
+
+ std::unique_ptr<weld::Label> mxFtMin;
+ std::unique_ptr<weld::Label> mxFtMax;
+
+ std::unique_ptr<ScDataBarFormatData> mpDataBarData;
+
+ ScFormatEntry* createDatabarEntry() const;
+
+ virtual OUString GetExpressionString() override;
+ void Init();
+
+ DECL_LINK( OptionBtnHdl, weld::Button&, void );
+ DECL_LINK( DataBarTypeSelectHdl, weld::ComboBox&, void );
+public:
+ ScDataBarFrmtEntry(ScCondFormatList* pParemt, ScDocument* pDoc, const ScAddress& rPos, const ScDataBarFormat* pFormat = nullptr);
+ virtual ~ScDataBarFrmtEntry() override;
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::DATABAR; }
+};
+
+class ScDateFrmtEntry : public ScCondFrmtEntry, public SfxListener
+{
+public:
+ ScDateFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScCondDateFormatEntry* pFormat = nullptr);
+ virtual ~ScDateFrmtEntry() override;
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::DATE; }
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+protected:
+ virtual OUString GetExpressionString() override;
+
+private:
+ void Init();
+
+ DECL_LINK( StyleSelectHdl, weld::ComboBox&, void );
+
+ SvxFontPrevWindow maWdPreview;
+ std::unique_ptr<weld::ComboBox> mxLbDateEntry;
+ std::unique_ptr<weld::Label> mxFtStyle;
+ std::unique_ptr<weld::ComboBox> mxLbStyle;
+ std::unique_ptr<weld::Widget> mxWdPreviewWin;
+ std::unique_ptr<weld::CustomWeld> mxWdPreview;
+
+ bool mbIsInStyleCreate;
+};
+
+class ScIconSetFrmtEntry : public ScCondFrmtEntry
+{
+ //color format ui elements
+ std::unique_ptr<weld::ComboBox> mxLbColorFormat;
+
+ // icon set ui elements
+ std::unique_ptr<weld::ComboBox> mxLbIconSetType;
+
+ std::unique_ptr<weld::Container> mxIconParent;
+
+ typedef std::vector<std::unique_ptr<ScIconSetFrmtDataEntry>> ScIconSetFrmtDataEntriesType;
+ ScIconSetFrmtDataEntriesType maEntries;
+
+ virtual OUString GetExpressionString() override;
+
+ void Init();
+
+ DECL_LINK(IconSetTypeHdl, weld::ComboBox&, void);
+
+public:
+ ScIconSetFrmtEntry(ScCondFormatList* pParent, ScDocument* pDoc, const ScAddress& rPos, const ScIconSetFormat* pFormat = nullptr);
+ virtual ~ScIconSetFrmtEntry() override;
+ virtual ScFormatEntry* GetEntry() const override;
+ virtual void SetActive() override;
+ virtual void SetInactive() override;
+ virtual condformat::entry::ScCondFrmtEntryType GetType() override { return condformat::entry::ICONSET; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/condformatdlgitem.hxx b/sc/source/ui/inc/condformatdlgitem.hxx
new file mode 100644
index 0000000000..b50b1d417c
--- /dev/null
+++ b/sc/source/ui/inc/condformatdlgitem.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/.
+ */
+
+#pragma once
+
+#include <svl/poolitem.hxx>
+
+#include <memory>
+
+namespace condformat::dialog
+{
+enum ScCondFormatDialogType
+{
+ NONE,
+ CONDITION,
+ COLORSCALE,
+ DATABAR,
+ ICONSET,
+ DATE
+};
+}
+
+class ScConditionalFormatList;
+
+class ScCondFormatDlgItem : public SfxPoolItem
+{
+public:
+ ScCondFormatDlgItem(std::shared_ptr<ScConditionalFormatList> pCondFormats, sal_Int32 nItem,
+ bool bManaged);
+
+ virtual ~ScCondFormatDlgItem() override;
+
+ ScCondFormatDlgItem(ScCondFormatDlgItem const&) = default;
+ ScCondFormatDlgItem(ScCondFormatDlgItem&&) = default;
+ ScCondFormatDlgItem& operator=(ScCondFormatDlgItem const&) = delete; // due to SfxPoolItem
+ ScCondFormatDlgItem& operator=(ScCondFormatDlgItem&&) = delete; // due to SfxPoolItem
+
+ virtual bool operator==(const SfxPoolItem&) const override;
+ virtual ScCondFormatDlgItem* Clone(SfxItemPool* pPool = nullptr) const override;
+
+ bool IsManaged() const;
+ condformat::dialog::ScCondFormatDialogType GetDialogType() const;
+ sal_Int32 GetIndex() const;
+
+ void SetDialogType(condformat::dialog::ScCondFormatDialogType eType);
+
+ ScConditionalFormatList* GetConditionalFormatList();
+
+private:
+ std::shared_ptr<ScConditionalFormatList> mpCondFormats;
+ sal_Int32 mnItem;
+ condformat::dialog::ScCondFormatDialogType meDialogType;
+ bool mbManaged;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/condformateasydlg.hxx b/sc/source/ui/inc/condformateasydlg.hxx
new file mode 100644
index 0000000000..4ecf720dec
--- /dev/null
+++ b/sc/source/ui/inc/condformateasydlg.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+#include <svl/lstner.hxx>
+#include <conditio.hxx>
+
+class ScViewData;
+class ScConditionalFormat;
+class ScTabViewShell;
+
+namespace sc
+{
+class ConditionalFormatEasyDialog : public ScAnyRefDlgController, public SfxListener
+{
+public:
+ ConditionalFormatEasyDialog(SfxBindings*, SfxChildWindow*, weld::Window*, ScViewData*);
+ virtual ~ConditionalFormatEasyDialog() override;
+
+ virtual void SetReference(const ScRange&, ScDocument&) override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+ virtual void Notify(SfxBroadcaster&, const SfxHint&) override;
+
+ DECL_LINK(ButtonPressed, weld::Button&, void);
+
+private:
+ void SetDescription(std::u16string_view rCondition);
+
+ ScViewData* mpViewData;
+ ScDocument* mpDocument;
+ ScConditionMode meMode;
+ ScAddress maPosition;
+
+ std::unique_ptr<weld::Entry> mxNumberEntry;
+ std::unique_ptr<weld::Entry> mxNumberEntry2;
+ std::unique_ptr<formula::RefEdit> mxRangeEntry;
+ std::unique_ptr<formula::RefButton> mxButtonRangeEdit;
+ std::unique_ptr<weld::ComboBox> mxStyles;
+ std::unique_ptr<weld::Label> mxDescription;
+ std::unique_ptr<weld::Button> mxButtonOk;
+ std::unique_ptr<weld::Button> mxButtonCancel;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/source/ui/inc/condformathelper.hxx b/sc/source/ui/inc/condformathelper.hxx
new file mode 100644
index 0000000000..9e4a0768db
--- /dev/null
+++ b/sc/source/ui/inc/condformathelper.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <address.hxx>
+
+class ScConditionalFormat;
+
+enum ScCondFormatEntryType
+{
+ CONDITION,
+ COLORSCALE,
+ DATABAR,
+ FORMULA,
+ ICONSET,
+ DATE
+};
+
+class ScCondFormatHelper
+{
+public:
+ static SC_DLLPUBLIC OUString GetExpression(const ScConditionalFormat& rFormat, const ScAddress& rPos);
+
+ static SC_DLLPUBLIC OUString GetExpression( ScCondFormatEntryType eType, sal_Int32 nIndex,
+ std::u16string_view aStr1 = std::u16string_view(), std::u16string_view aStr2 = std::u16string_view() );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/condformatmgr.hxx b/sc/source/ui/inc/condformatmgr.hxx
new file mode 100644
index 0000000000..ca9f16e8a5
--- /dev/null
+++ b/sc/source/ui/inc/condformatmgr.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScDocument;
+class ScConditionalFormat;
+class ScConditionalFormatList;
+
+class ScCondFormatManagerWindow
+{
+private:
+ void Init();
+ void setColSizes();
+
+ weld::TreeView& mrTreeView;
+ ScDocument& mrDoc;
+ ScConditionalFormatList* mpFormatList;
+
+public:
+ ScCondFormatManagerWindow(weld::TreeView& rTreeView, ScDocument& rDoc, ScConditionalFormatList* pFormatList);
+
+ void DeleteSelection();
+ ScConditionalFormat* GetSelection();
+};
+
+class ScCondFormatManagerDlg : public weld::GenericDialogController
+{
+public:
+ ScCondFormatManagerDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList);
+ virtual ~ScCondFormatManagerDlg() override;
+
+ std::unique_ptr<ScConditionalFormatList> GetConditionalFormatList();
+
+ bool CondFormatsChanged() const;
+ void SetModified();
+
+ ScConditionalFormat* GetCondFormatSelected();
+
+private:
+ bool m_bModified;
+ std::unique_ptr<ScConditionalFormatList> m_xFormatList;
+
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnRemove;
+ std::unique_ptr<weld::Button> m_xBtnEdit;
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+ std::unique_ptr<ScCondFormatManagerWindow> m_xCtrlManager;
+
+ void UpdateButtonSensitivity();
+
+ DECL_LINK(RemoveBtnHdl, weld::Button&, void);
+ DECL_LINK(EditBtnClickHdl, weld::Button&, void);
+ DECL_LINK(AddBtnHdl, weld::Button&, void);
+ DECL_LINK(EditBtnHdl, weld::TreeView&, bool);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/condformatuno.hxx b/sc/source/ui/inc/condformatuno.hxx
new file mode 100644
index 0000000000..16b971a538
--- /dev/null
+++ b/sc/source/ui/inc/condformatuno.hxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <types.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XConditionalFormats.hpp>
+#include <com/sun/star/sheet/XConditionalFormat.hpp>
+#include <com/sun/star/sheet/XConditionEntry.hpp>
+#include <com/sun/star/sheet/XColorScaleEntry.hpp>
+#include <com/sun/star/sheet/XDataBarEntry.hpp>
+#include <com/sun/star/sheet/XIconSetEntry.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/lstner.hxx>
+#include <rtl/ref.hxx>
+
+class ScDocShell;
+class ScConditionalFormatList;
+class ScConditionalFormat;
+class ScIconSetFormat;
+class ScDataBarFormat;
+class ScColorScaleFormat;
+class ScCondFormatEntry;
+class ScColorScaleEntry;
+class ScCondDateFormatEntry;
+
+using namespace com::sun::star;
+
+namespace com::sun::star::sheet { class XSheetCellRanges; }
+
+class ScCondFormatsObj : public cppu::WeakImplHelper<css::sheet::XConditionalFormats>,
+ public SfxListener
+{
+public:
+ ScCondFormatsObj(ScDocShell* pDocShell, SCTAB nTab);
+
+ virtual ~ScCondFormatsObj() override;
+
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+
+ // XConditionalFormats
+ virtual sal_Int32 SAL_CALL createByRange(const uno::Reference<sheet::XSheetCellRanges>& xRanges) override;
+
+ virtual void SAL_CALL removeByID( const sal_Int32 nID ) override;
+
+ virtual uno::Sequence< uno::Reference< sheet::XConditionalFormat > > SAL_CALL getConditionalFormats() override;
+
+ virtual sal_Int32 SAL_CALL getLength() override;
+
+ ScConditionalFormatList* getCoreObject();
+
+private:
+ SCTAB mnTab;
+ ScDocShell* mpDocShell;
+};
+
+class ScCondFormatObj : public cppu::WeakImplHelper<css::sheet::XConditionalFormat,
+ css::beans::XPropertySet>
+{
+public:
+ ScCondFormatObj(ScDocShell* pDocShell, rtl::Reference<ScCondFormatsObj> xCondFormats, sal_Int32 nKey);
+
+ virtual ~ScCondFormatObj() override;
+
+ ScDocShell* getDocShell();
+
+ // XConditionalFormat
+ virtual void SAL_CALL createEntry(const sal_Int32 nType, const sal_Int32 nPos) override;
+
+ virtual void SAL_CALL removeByIndex(const sal_Int32 nIndex) override;
+
+ // XIndexAccess
+
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ virtual sal_Int32 SAL_CALL getCount() override;
+
+ virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) 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;
+
+ ScConditionalFormat* getCoreObject();
+
+private:
+ rtl::Reference<ScCondFormatsObj> mxCondFormatList;
+ ScDocShell* mpDocShell;
+ SfxItemPropertySet maPropSet;
+ sal_Int32 mnKey;
+};
+
+class ScConditionEntryObj : public cppu::WeakImplHelper<css::beans::XPropertySet,
+ css::sheet::XConditionEntry>
+{
+public:
+
+ ScConditionEntryObj(rtl::Reference<ScCondFormatObj> const & xParent,
+ const ScCondFormatEntry* pFormat);
+ virtual ~ScConditionEntryObj() override;
+
+ ScCondFormatEntry* getCoreObject();
+
+ // XConditionEntry
+ virtual sal_Int32 SAL_CALL getType() 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;
+
+private:
+ ScDocShell* mpDocShell;
+ rtl::Reference<ScCondFormatObj> mxParent;
+ SfxItemPropertySet maPropSet;
+ const ScCondFormatEntry* mpFormat;
+};
+
+class ScColorScaleFormatObj : public cppu::WeakImplHelper<css::beans::XPropertySet,
+ css::sheet::XConditionEntry>
+{
+public:
+
+ ScColorScaleFormatObj(rtl::Reference<ScCondFormatObj> xParent, const ScColorScaleFormat* pFormat);
+ virtual ~ScColorScaleFormatObj() override;
+
+ // XConditionEntry
+ virtual sal_Int32 SAL_CALL getType() override;
+
+
+ ScColorScaleFormat* getCoreObject();
+
+ // 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;
+
+private:
+ rtl::Reference<ScCondFormatObj> mxParent;
+ SfxItemPropertySet maPropSet;
+ const ScColorScaleFormat* mpFormat;
+};
+
+class ScColorScaleEntryObj : public cppu::WeakImplHelper<css::sheet::XColorScaleEntry>
+{
+public:
+ ScColorScaleEntryObj(rtl::Reference<ScColorScaleFormatObj> xParent, size_t nPos);
+
+ virtual ~ScColorScaleEntryObj() override;
+
+ virtual sal_Int32 SAL_CALL getColor() override;
+
+ virtual void SAL_CALL setColor(sal_Int32 aColor) override;
+
+ virtual sal_Int32 SAL_CALL getType() override;
+
+ virtual void SAL_CALL setType(sal_Int32 nType) override;
+
+ virtual OUString SAL_CALL getFormula() override;
+
+ virtual void SAL_CALL setFormula(const OUString& rString) override;
+
+private:
+ ScColorScaleEntry* getCoreObject();
+
+ rtl::Reference<ScColorScaleFormatObj> mxParent;
+ size_t mnPos;
+};
+
+class ScDataBarFormatObj : public cppu::WeakImplHelper<css::beans::XPropertySet,
+ css::sheet::XConditionEntry>
+{
+public:
+ ScDataBarFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScDataBarFormat* pFormat);
+ virtual ~ScDataBarFormatObj() override;
+
+ ScDataBarFormat* getCoreObject();
+
+ // XConditionEntry
+ virtual sal_Int32 SAL_CALL getType() 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;
+
+private:
+ rtl::Reference<ScCondFormatObj> mxParent;
+ SfxItemPropertySet maPropSet;
+ const ScDataBarFormat* mpFormat;
+};
+
+class ScDataBarEntryObj : public cppu::WeakImplHelper<css::sheet::XDataBarEntry>
+{
+public:
+ ScDataBarEntryObj(rtl::Reference<ScDataBarFormatObj> xParent, size_t nPos);
+
+ virtual ~ScDataBarEntryObj() override;
+
+ virtual sal_Int32 SAL_CALL getType() override;
+
+ virtual void SAL_CALL setType(sal_Int32 nType) override;
+
+ virtual OUString SAL_CALL getFormula() override;
+
+ virtual void SAL_CALL setFormula(const OUString& rString) override;
+
+private:
+ ScColorScaleEntry* getCoreObject();
+
+ rtl::Reference<ScDataBarFormatObj> mxParent;
+ size_t mnPos;
+};
+
+class ScIconSetFormatObj : public cppu::WeakImplHelper<css::beans::XPropertySet,
+ css::sheet::XConditionEntry>
+{
+public:
+ ScIconSetFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScIconSetFormat* pFormat);
+ virtual ~ScIconSetFormatObj() override;
+
+ ScIconSetFormat* getCoreObject();
+
+ // XConditionEntry
+ virtual sal_Int32 SAL_CALL getType() 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;
+
+private:
+ rtl::Reference<ScCondFormatObj> mxParent;
+ SfxItemPropertySet maPropSet;
+ const ScIconSetFormat* mpFormat;
+};
+
+class ScIconSetEntryObj : public cppu::WeakImplHelper<css::sheet::XIconSetEntry>
+{
+public:
+ ScIconSetEntryObj(rtl::Reference<ScIconSetFormatObj> xParent, size_t nPos);
+
+ virtual ~ScIconSetEntryObj() override;
+
+ virtual sal_Int32 SAL_CALL getType() override;
+
+ virtual void SAL_CALL setType(sal_Int32 nType) override;
+
+ virtual OUString SAL_CALL getFormula() override;
+
+ virtual void SAL_CALL setFormula(const OUString& rString) override;
+
+private:
+ ScColorScaleEntry* getCoreObject();
+
+ rtl::Reference<ScIconSetFormatObj> mxParent;
+ size_t mnPos;
+};
+
+class ScCondDateFormatObj : public cppu::WeakImplHelper<css::beans::XPropertySet,
+ css::sheet::XConditionEntry>
+{
+public:
+ ScCondDateFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScCondDateFormatEntry* pFormat);
+
+ virtual ~ScCondDateFormatObj() override;
+
+ ScCondDateFormatEntry* getCoreObject();
+
+ // XConditionEntry
+ virtual sal_Int32 SAL_CALL getType() 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;
+
+private:
+ rtl::Reference<ScCondFormatObj> mxParent;
+ SfxItemPropertySet maPropSet;
+ const ScCondDateFormatEntry* mpFormat;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/conflictsdlg.hxx b/sc/source/ui/inc/conflictsdlg.hxx
new file mode 100644
index 0000000000..f0ab643e05
--- /dev/null
+++ b/sc/source/ui/inc/conflictsdlg.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <svx/ctredlin.hxx>
+
+#include "docsh.hxx"
+
+class ScViewData;
+class ScChangeTrack;
+class ScChangeAction;
+
+enum ScConflictAction
+{
+ SC_CONFLICT_ACTION_NONE,
+ SC_CONFLICT_ACTION_KEEP_MINE,
+ SC_CONFLICT_ACTION_KEEP_OTHER
+};
+
+// struct ScConflictsListEntry
+
+struct ScConflictsListEntry
+{
+ ScConflictAction meConflictAction;
+ std::vector<sal_uLong> maSharedActions;
+ std::vector<sal_uLong> maOwnActions;
+
+ bool HasSharedAction( sal_uLong nSharedAction ) const;
+ bool HasOwnAction( sal_uLong nOwnAction ) const;
+};
+
+typedef ::std::vector< ScConflictsListEntry > ScConflictsList;
+
+
+class ScConflictsListHelper
+{
+private:
+ static void Transform_Impl( std::vector<sal_uLong>& rActionList, ScChangeActionMergeMap* pMergeMap );
+
+public:
+ static bool HasOwnAction( ScConflictsList& rConflictsList, sal_uLong nOwnAction );
+
+ static ScConflictsListEntry* GetSharedActionEntry( ScConflictsList& rConflictsList, sal_uLong nSharedAction );
+ static ScConflictsListEntry* GetOwnActionEntry( ScConflictsList& rConflictsList, sal_uLong nOwnAction );
+
+ static void TransformConflictsList( ScConflictsList& rConflictsList,
+ ScChangeActionMergeMap* pSharedMap, ScChangeActionMergeMap* pOwnMap );
+};
+
+
+class ScConflictsFinder final
+{
+private:
+ ScChangeTrack* mpTrack;
+ sal_uLong mnStartShared;
+ sal_uLong mnEndShared;
+ sal_uLong mnStartOwn;
+ sal_uLong mnEndOwn;
+ ScConflictsList& mrConflictsList;
+
+ static bool DoActionsIntersect( const ScChangeAction* pAction1, const ScChangeAction* pAction2 );
+ ScConflictsListEntry* GetIntersectingEntry( const ScChangeAction* pAction ) const;
+ ScConflictsListEntry& GetEntry(sal_uLong nSharedAction, const std::vector<sal_uLong>& rOwnActions);
+
+public:
+ ScConflictsFinder( ScChangeTrack* pTrack, sal_uLong nStartShared, sal_uLong nEndShared,
+ sal_uLong nStartOwn, sal_uLong nEndOwn, ScConflictsList& rConflictsList );
+
+ bool Find();
+};
+
+
+class ScConflictsResolver final
+{
+private:
+ ScChangeTrack* mpTrack;
+ ScConflictsList& mrConflictsList;
+
+public:
+ ScConflictsResolver( ScChangeTrack* pTrack, ScConflictsList& rConflictsList );
+
+ void HandleAction( ScChangeAction* pAction, bool bIsSharedAction,
+ bool bHandleContentAction, bool bHandleNonContentAction );
+};
+
+
+class ScConflictsDlg : public weld::GenericDialogController
+{
+private:
+ OUString maStrUnknownUser;
+
+ ScViewData* const mpViewData;
+ ScDocument* mpOwnDoc;
+ ScChangeTrack* mpOwnTrack;
+ ScDocument* const mpSharedDoc;
+ ScChangeTrack* mpSharedTrack;
+ ScConflictsList& mrConflictsList;
+
+ Idle maSelectionIdle;
+ bool mbInSelectHdl;
+
+ std::unique_ptr<weld::Button> m_xBtnKeepMine;
+ std::unique_ptr<weld::Button> m_xBtnKeepOther;
+ std::unique_ptr<weld::Button> m_xBtnKeepAllMine;
+ std::unique_ptr<weld::Button> m_xBtnKeepAllOthers;
+ std::unique_ptr<SvxRedlinTable> m_xLbConflicts;
+
+ OUString GetConflictString( const ScConflictsListEntry& rConflictEntry );
+ void SetActionString(const ScChangeAction* pAction, ScDocument* pDoc, const weld::TreeIter& rEntry);
+ void HandleListBoxSelection();
+
+ void SetConflictAction(const weld::TreeIter& rRootEntry, ScConflictAction eConflictAction);
+ void KeepHandler( bool bMine );
+ void KeepAllHandler( bool bMine );
+
+ DECL_LINK( SelectHandle, weld::TreeView&, void );
+ DECL_LINK( UpdateSelectionHdl, Timer*, void );
+ DECL_LINK( KeepMineHandle, weld::Button&, void );
+ DECL_LINK( KeepOtherHandle, weld::Button&, void );
+ DECL_LINK( KeepAllMineHandle, weld::Button&, void );
+ DECL_LINK( KeepAllOthersHandle, weld::Button&, void );
+
+public:
+ ScConflictsDlg(weld::Window* pParent, ScViewData* pViewData, ScDocument* pSharedDoc, ScConflictsList& rConflictsList);
+ virtual ~ScConflictsDlg() override;
+
+ void UpdateView();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/consdlg.hxx b/sc/source/ui/inc/consdlg.hxx
new file mode 100644
index 0000000000..07fb1f3ed5
--- /dev/null
+++ b/sc/source/ui/inc/consdlg.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <global.hxx>
+#include "anyrefdg.hxx"
+
+class ScViewData;
+class ScDocument;
+class ScRangeUtil;
+class ScAreaData;
+
+class ScConsolidateDlg : public ScAnyRefDlgController
+{
+public:
+ ScConsolidateDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet);
+ virtual ~ScConsolidateDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+
+ virtual bool IsRefInputMode() const override { return true; }
+ virtual void SetActive() override;
+
+ virtual void Close() override;
+ virtual void Deactivate() override;
+
+private:
+ OUString aStrUndefined;
+
+ ScConsolidateParam theConsData;
+ ScViewData& rViewData;
+ ScDocument& rDoc;
+ std::unique_ptr<ScAreaData[]> pAreaData;
+ size_t nAreaDataCount;
+ sal_uInt16 nWhichCons;
+ bool bDlgLostFocus;
+
+ formula::RefEdit* m_pRefInputEdit;
+
+ std::unique_ptr<weld::ComboBox> m_xLbFunc;
+ std::unique_ptr<weld::TreeView> m_xLbConsAreas;
+
+ std::unique_ptr<weld::ComboBox> m_xLbDataArea;
+ std::unique_ptr<formula::RefEdit> m_xEdDataArea;
+ std::unique_ptr<formula::RefButton> m_xRbDataArea;
+
+ std::unique_ptr<weld::ComboBox> m_xLbDestArea;
+ std::unique_ptr<formula::RefEdit> m_xEdDestArea;
+ std::unique_ptr<formula::RefButton> m_xRbDestArea;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnByRow;
+ std::unique_ptr<weld::CheckButton> m_xBtnByCol;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnRefs;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnRemove;
+
+ std::unique_ptr<weld::Label> m_xDataFT;
+ std::unique_ptr<weld::Label> m_xDestFT;
+
+ void Init ();
+ void FillAreaLists ();
+ bool VerifyEdit(formula::RefEdit* pEd);
+
+ DECL_LINK( OkHdl, weld::Button&, void );
+ DECL_LINK( ClickHdl, weld::Button&, void );
+ DECL_LINK( GetFocusHdl, weld::Widget&, void );
+ DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( ModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( SelectTVHdl, weld::TreeView&, void );
+ DECL_LINK( SelectCBHdl, weld::ComboBox&, void );
+
+ static ScSubTotalFunc LbPosToFunc( sal_Int32 nPos );
+ static sal_Int32 FuncToLbPos( ScSubTotalFunc eFunc );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/content.hxx b/sc/source/ui/inc/content.hxx
new file mode 100644
index 0000000000..2e8c6757e2
--- /dev/null
+++ b/sc/source/ui/inc/content.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <address.hxx>
+#include <tools/solar.h>
+#include <o3tl/enumarray.hxx>
+
+class ScAreaLink;
+class ScLinkTransferObj;
+class ScDocument;
+class ScDocShell;
+class ScNavigatorDlg;
+struct ImplSVEvent;
+enum class SdrObjKind : sal_uInt16;
+
+enum class ScContentId {
+ ROOT, TABLE, RANGENAME, DBAREA,
+ GRAPHIC, OLEOBJECT, NOTE, AREALINK,
+ DRAWING, LAST = DRAWING
+};
+
+const sal_uLong SC_CONTENT_NOCHILD = ~0UL;
+
+class ScContentTree
+{
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+ rtl::Reference<ScLinkTransferObj> m_xTransferObj;
+ ScNavigatorDlg* pParentWindow;
+ o3tl::enumarray<ScContentId, std::unique_ptr<weld::TreeIter>> m_aRootNodes;
+ ScContentId nRootType; // set as Root
+ OUString aManualDoc; // Switched in Navigator (Title)
+ bool bIsInNavigatorDlg;
+ bool m_bFreeze;
+ ImplSVEvent* m_nAsyncMouseReleaseId;
+
+ o3tl::enumarray<ScContentId, sal_uInt16> pPosList; // for the sequence
+
+ ScDocShell* GetManualOrCurrent();
+
+ void InitRoot(ScContentId nType);
+ void ClearType(ScContentId nType);
+ void ClearAll();
+ void InsertContent( ScContentId nType, const OUString& rValue );
+ void GetDrawNames( ScContentId nType );
+
+ void GetTableNames();
+ void GetAreaNames();
+ void GetDbNames();
+ void GetLinkNames();
+ void GetGraphicNames();
+ void GetOleNames();
+ void GetDrawingNames();
+ void GetNoteStrings();
+
+ static bool IsPartOfType( ScContentId nContentType, SdrObjKind nObjIdentifier );
+
+ bool DrawNamesChanged( ScContentId nType );
+ bool NoteStringsChanged();
+
+ ScAddress GetNotePos( sal_uLong nIndex );
+ const ScAreaLink* GetLink( sal_uLong nIndex );
+
+ /** Returns the indexes of the specified listbox entry.
+ @param rnRootIndex Root index of specified entry is returned.
+ @param rnChildIndex Index of the entry inside its root is returned (or SC_CONTENT_NOCHILD if entry is root).
+ @param pEntry The entry to examine. */
+ void GetEntryIndexes(ScContentId& rnRootIndex, sal_uLong& rnChildIndex, const weld::TreeIter* pEntry) const;
+
+ /** Returns the child index of the specified listbox entry.
+ @param pEntry The entry to examine or NULL for the selected entry.
+ @return Index of the entry inside its root or SC_CONTENT_NOCHILD if entry is root. */
+ sal_uLong GetChildIndex(const weld::TreeIter* pEntry) const;
+
+ ScDocument* GetSourceDocument();
+
+ void freeze()
+ {
+ m_xTreeView->freeze();
+ m_bFreeze = true;
+ }
+
+ void thaw()
+ {
+ m_xTreeView->thaw();
+ m_bFreeze = false;
+ }
+
+ void LaunchAsyncStoreNavigatorSettings();
+
+ DECL_LINK(ContentDoubleClickHdl, weld::TreeView&, bool);
+ DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(AsyncStoreNavigatorSettings, void*, void);
+ DECL_LINK(CommandHdl, const CommandEvent&, bool);
+ DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
+ DECL_LINK(DragBeginHdl, bool&, bool);
+
+public:
+ ScContentTree(std::unique_ptr<weld::TreeView> xTreeView, ScNavigatorDlg* pNavigatorDlg);
+ ~ScContentTree();
+
+ void SetNavigatorDlgFlag(bool isInNavigateDlg){ bIsInNavigatorDlg=isInNavigateDlg;};
+
+ void hide()
+ {
+ m_xTreeView->hide();
+ }
+
+ void show()
+ {
+ m_xTreeView->show();
+ }
+
+ void Refresh( ScContentId nType = ScContentId::ROOT );
+
+ void ToggleRoot();
+ void SetRootType( ScContentId nNew );
+ ScContentId GetRootType() const { return nRootType; }
+
+ // return true if Refresh was called to allow detecting that the navigator
+ // tree is now up to date
+ bool ActiveDocChanged();
+ void ResetManualDoc();
+ void SetManualDoc(const OUString& rName);
+ void SelectDoc(const OUString& rName);
+ void SelectEntryByName(const ScContentId nRoot, std::u16string_view rName);
+
+ /** Applies the navigator settings to the listbox. */
+ void ApplyNavigatorSettings();
+ /** Stores the current listbox state in the navigator settings. */
+ void StoreNavigatorSettings();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/corodlg.hxx b/sc/source/ui/inc/corodlg.hxx
new file mode 100644
index 0000000000..68e5fd0f1f
--- /dev/null
+++ b/sc/source/ui/inc/corodlg.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScColRowLabelDlg : public weld::GenericDialogController
+{
+public:
+ ScColRowLabelDlg(weld::Window* pParent, bool bCol, bool bRow)
+ : GenericDialogController(pParent, "modules/scalc/ui/changesourcedialog.ui",
+ "ChangeSourceDialog")
+ , m_xBtnRow(m_xBuilder->weld_check_button("row"))
+ , m_xBtnCol(m_xBuilder->weld_check_button("col"))
+ {
+ m_xBtnCol->set_active(bCol);
+ m_xBtnRow->set_active(bRow);
+ }
+
+ bool IsCol() const { return m_xBtnCol->get_active(); }
+ bool IsRow() const { return m_xBtnRow->get_active(); }
+
+private:
+ std::unique_ptr<weld::CheckButton> m_xBtnRow;
+ std::unique_ptr<weld::CheckButton> m_xBtnCol;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/crdlg.hxx b/sc/source/ui/inc/crdlg.hxx
new file mode 100644
index 0000000000..7a3804c567
--- /dev/null
+++ b/sc/source/ui/inc/crdlg.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScColOrRowDlg : public weld::GenericDialogController
+{
+public:
+ ScColOrRowDlg(weld::Window* pParent, const OUString& rStrTitle, const OUString& rStrLabel);
+ virtual ~ScColOrRowDlg() override;
+
+private:
+ std::unique_ptr<weld::Frame> m_xFrame;
+ std::unique_ptr<weld::RadioButton> m_xBtnCols;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+
+ DECL_LINK(OkHdl, weld::Button&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/crnrdlg.hxx b/sc/source/ui/inc/crnrdlg.hxx
new file mode 100644
index 0000000000..5bff6d2e8c
--- /dev/null
+++ b/sc/source/ui/inc/crnrdlg.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 .
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+#include <rangelst.hxx>
+
+#include <unordered_map>
+
+class ScViewData;
+class ScDocument;
+
+class ScColRowNameRangesDlg : public ScAnyRefDlgController
+{
+public:
+ ScColRowNameRangesDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData);
+ virtual ~ScColRowNameRangesDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ ScRange theCurArea;
+ ScRange theCurData;
+
+ ScRangePairListRef xColNameRanges;
+ ScRangePairListRef xRowNameRanges;
+
+ typedef std::unordered_map< OUString, ScRange > NameRangeMap;
+ NameRangeMap aRangeMap;
+ ScViewData& m_rViewData;
+ ScDocument& rDoc;
+ bool bDlgLostFocus;
+
+ formula::RefEdit* m_pEdActive;
+ std::unique_ptr<weld::TreeView> m_xLbRange;
+
+ std::unique_ptr<formula::RefEdit> m_xEdAssign;
+ std::unique_ptr<formula::RefButton> m_xRbAssign;
+ std::unique_ptr<weld::RadioButton> m_xBtnColHead;
+ std::unique_ptr<weld::RadioButton> m_xBtnRowHead;
+ std::unique_ptr<formula::RefEdit> m_xEdAssign2;
+ std::unique_ptr<formula::RefButton> m_xRbAssign2;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnRemove;
+
+ std::unique_ptr<weld::Frame> m_xRangeFrame;
+ std::unique_ptr<weld::Label> m_xRangeFT;
+ std::unique_ptr<weld::Label> m_xDataFT;
+
+ void Init ();
+ void UpdateNames ();
+ void UpdateRangeData ( const ScRange& rRange, bool bColName );
+ void SetColRowData( const ScRange& rLabelRange, bool bRef=false);
+ void AdjustColRowData( const ScRange& rDataRange, bool bRef=false);
+ DECL_LINK( CancelBtnHdl, weld::Button&, void );
+ DECL_LINK( OkBtnHdl, weld::Button&, void );
+ DECL_LINK( AddBtnHdl, weld::Button&, void );
+ DECL_LINK( RemoveBtnHdl, weld::Button&, void );
+ DECL_LINK( Range1SelectHdl, weld::TreeView&, void );
+ DECL_LINK( Range1DataModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( ColRowToggleHdl, weld::Toggleable&, void );
+ DECL_LINK( Range2DataModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( LoseEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHdl, formula::RefButton&, void );
+ DECL_LINK( LoseButtonFocusHdl, formula::RefButton&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/csvcontrol.hxx b/sc/source/ui/inc/csvcontrol.hxx
new file mode 100644
index 0000000000..8da12e3785
--- /dev/null
+++ b/sc/source/ui/inc/csvcontrol.hxx
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <scdllapi.h>
+#include <address.hxx>
+#include "csvsplits.hxx"
+#include <o3tl/typed_flags_set.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/customweld.hxx>
+#include "AccessibleCsvControl.hxx"
+
+namespace com::sun::star::accessibility { class XAccessible; }
+
+/** Minimum character count for a column in separators mode. */
+const sal_Int32 CSV_MINCOLWIDTH = 8;
+/** Maximum length of a cell string. */
+const sal_Int32 CSV_MAXSTRLEN = 0x7FFF;
+/** Transparency for header color of selected columns. */
+const sal_uInt16 CSV_HDR_TRANSPARENCY = 85;
+/** Minimum distance to border for auto scroll. */
+const sal_Int32 CSV_SCROLL_DIST = 3;
+
+//! TODO make string array dynamic
+const sal_Int32 CSV_PREVIEW_LINES = 32; // maximum count of preview lines
+/** Maximum count of columns. */
+const sal_Int32 CSV_MAXCOLCOUNT = MAXCOLCOUNT;
+
+/** Default column data type. */
+const sal_Int32 CSV_TYPE_DEFAULT = 0;
+/** Multi selection with different types. */
+const sal_Int32 CSV_TYPE_MULTI = -1;
+/** No column selected. */
+const sal_Int32 CSV_TYPE_NOSELECTION = -2;
+
+// External used column types.
+const sal_uInt8 SC_COL_STANDARD = 1;
+const sal_uInt8 SC_COL_TEXT = 2;
+const sal_uInt8 SC_COL_MDY = 3;
+const sal_uInt8 SC_COL_DMY = 4;
+const sal_uInt8 SC_COL_YMD = 5;
+const sal_uInt8 SC_COL_SKIP = 9;
+const sal_uInt8 SC_COL_ENGLISH = 10;
+
+/** Exported data of a column (data used in the dialog). */
+struct ScCsvExpData
+{
+ sal_Int32 mnIndex; /// Index of a column.
+ sal_uInt8 mnType; /// External type of the column.
+
+ ScCsvExpData() : mnIndex( 0 ), mnType( SC_COL_STANDARD ) {}
+ ScCsvExpData( sal_Int32 nIndex, sal_uInt8 nType ) :
+ mnIndex( nIndex ), mnType( nType ) {}
+};
+
+typedef ::std::vector< ScCsvExpData > ScCsvExpDataVec;
+
+/** Specifies which element should be used to perform an action. */
+enum ScMoveMode
+{
+ MOVE_NONE, /// No action.
+ MOVE_FIRST, /// First element in current context.
+ MOVE_LAST, /// Last element in current context.
+ MOVE_PREV, /// Predecessor of current element in current context.
+ MOVE_NEXT, /// Successor of current element in current context.
+ MOVE_PREVPAGE, /// Previous page relative to current context.
+ MOVE_NEXTPAGE /// Next page relative to current context.
+};
+
+/** Flags for comparison of old and new control layout data. */
+enum class ScCsvDiff : sal_uInt32 {
+ Equal = 0x0000,
+ PosCount = 0x0001,
+ PosOffset = 0x0002,
+ HeaderWidth = 0x0004,
+ CharWidth = 0x0008,
+ LineCount = 0x0010,
+ LineOffset = 0x0020,
+ HeaderHeight = 0x0040,
+ LineHeight = 0x0080,
+ RulerCursor = 0x0100,
+ GridCursor = 0x0200,
+
+ HorizontalMask = PosCount | PosOffset | HeaderWidth | CharWidth,
+ VerticalMask = LineCount | LineOffset | HeaderHeight | LineHeight
+};
+namespace o3tl {
+ template<> struct typed_flags<ScCsvDiff> : is_typed_flags<ScCsvDiff, 0x03ff> {};
+}
+
+
+/** A structure containing all layout data valid for both ruler and data grid
+ (i.e. scroll position or column width). */
+struct ScCsvLayoutData
+{
+ // horizontal settings
+ sal_Int32 mnPosCount; /// Number of positions.
+ sal_Int32 mnPosOffset; /// Horizontal scroll offset.
+
+ sal_Int32 mnWinWidth; /// Width of ruler and data grid.
+ sal_Int32 mnHdrWidth; /// Width of the header column.
+ sal_Int32 mnCharWidth; /// Pixel width of one character.
+
+ // vertical settings
+ sal_Int32 mnLineCount; /// Number of data lines.
+ sal_Int32 mnLineOffset; /// Index of first visible line (0-based).
+
+ sal_Int32 mnWinHeight; /// Height of entire data grid (incl. header).
+ sal_Int32 mnHdrHeight; /// Height of the header line.
+ sal_Int32 mnLineHeight; /// Height of a data line.
+
+ // cursor settings
+ sal_Int32 mnPosCursor; /// Position of ruler cursor.
+ sal_Int32 mnColCursor; /// Position of grid column cursor.
+
+ mutable sal_Int32 mnNoRepaint; /// >0 = no repaint.
+ bool mbAppRTL; /// true = application in RTL mode.
+
+ explicit ScCsvLayoutData();
+
+ /** Returns differences to rData.
+ @descr For each difference the appropriate bit is set in the returned value. */
+ ScCsvDiff GetDiff( const ScCsvLayoutData& rData ) const;
+};
+
+inline bool operator==( const ScCsvLayoutData& rData1, const ScCsvLayoutData& rData2 )
+{
+ return rData1.GetDiff( rData2 ) == ScCsvDiff::Equal;
+}
+
+inline bool operator!=( const ScCsvLayoutData& rData1, const ScCsvLayoutData& rData2 )
+{
+ return !(rData1 == rData2);
+}
+
+/** Enumeration of possible commands to change any settings of the CSV controls.
+ @descr Controls have to send commands instead of changing their settings directly.
+ This helps to keep the different controls consistent to each other.
+ A command can contain 0 to 2 sal_Int32 parameters. In the description of each
+ command the required parameters are shown in brackets. [-] means no parameter. */
+enum ScCsvCmdType
+{
+ // misc
+ CSVCMD_NONE, /// No command. [-]
+ CSVCMD_REPAINT, /// Repaint all controls. [-]
+
+ // modify horizontal dimensions
+ CSVCMD_SETPOSCOUNT, /// Change position/column count. [character count]
+ CSVCMD_SETPOSOFFSET, /// Change position offset (scroll pos). [position]
+ CSVCMD_SETHDRWIDTH, /// Change width of the header column. [width in pixel]
+ CSVCMD_SETCHARWIDTH, /// Change character pixel width. [width in pixel]
+
+ // modify vertical dimensions
+ CSVCMD_SETLINECOUNT, /// Change number of data lines. [line count]
+ CSVCMD_SETLINEOFFSET, /// Change first visible line. [line index]
+ CSVCMD_SETHDRHEIGHT, /// Change height of top header line. [height in pixel]
+ CSVCMD_SETLINEHEIGHT, /// Change data line pixel height. [height in pixel}
+
+ // cursors/positions
+ CSVCMD_MOVERULERCURSOR, /// Move ruler cursor to new position. [position]
+ CSVCMD_MOVEGRIDCURSOR, /// Move data grid cursor to new column. [position]
+ CSVCMD_MAKEPOSVISIBLE, /// Move to make passed position visible (for mouse tracking). [position]
+
+ // table contents
+ CSVCMD_NEWCELLTEXTS, /// Recalculate splits and cell texts. [-]
+ CSVCMD_UPDATECELLTEXTS, /// Update cell texts with current split settings. [-]
+ CSVCMD_SETCOLUMNTYPE, /// Change data type of selected columns. [column type]
+ CSVCMD_EXPORTCOLUMNTYPE, /// Send selected column type to external controls. [-]
+ CSVCMD_SETFIRSTIMPORTLINE, /// Set number of first imported line. [line index]
+
+ // splits
+ CSVCMD_INSERTSPLIT, /// Insert a split. [position]
+ CSVCMD_REMOVESPLIT, /// Remove a split. [position]
+ CSVCMD_TOGGLESPLIT, /// Inserts or removes a split. [position]
+ CSVCMD_MOVESPLIT, /// Move a split. [old position, new position]
+ CSVCMD_REMOVEALLSPLITS /// Remove all splits. [-]
+};
+
+/** Data for a CSV control command. The stored position data is always character based,
+ it's never a column index (required for internal consistency). */
+class ScCsvCmd
+{
+private:
+ ScCsvCmdType meType; /// The command.
+ sal_Int32 mnParam1; /// First parameter.
+ sal_Int32 mnParam2; /// Second parameter.
+
+public:
+ explicit ScCsvCmd() : meType( CSVCMD_NONE ),
+ mnParam1( CSV_POS_INVALID ), mnParam2( CSV_POS_INVALID ) {}
+
+ inline void Set( ScCsvCmdType eType, sal_Int32 nParam1, sal_Int32 nParam2 );
+
+ ScCsvCmdType GetType() const { return meType; }
+ sal_Int32 GetParam1() const { return mnParam1; }
+ sal_Int32 GetParam2() const { return mnParam2; }
+};
+
+inline void ScCsvCmd::Set( ScCsvCmdType eType, sal_Int32 nParam1, sal_Int32 nParam2 )
+{
+ meType = eType; mnParam1 = nParam1; mnParam2 = nParam2;
+}
+
+/** Base class for the CSV ruler and the data grid control. Implements command handling. */
+class SC_DLLPUBLIC ScCsvControl : public weld::CustomWidgetController
+{
+private:
+ Link<ScCsvControl&,void> maCmdHdl; /// External command handler.
+ ScCsvCmd maCmd; /// Data of last command.
+ const ScCsvLayoutData& mrData; /// Shared layout data.
+
+ bool mbValidGfx; /// Content of virtual devices valid?
+
+protected:
+ rtl::Reference<ScAccessibleCsvControl> mxAccessible; /// Reference to the accessible implementation object.
+
+public:
+ explicit ScCsvControl(const ScCsvLayoutData& rData);
+ virtual ~ScCsvControl() override;
+
+ // event handling ---------------------------------------------------------
+
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+ /** Sends a GetFocus or LoseFocus event to the accessibility object. */
+ void AccSendFocusEvent( bool bFocused );
+ /** Sends a caret changed event to the accessibility object. */
+ void AccSendCaretEvent();
+ /** Sends a visible area changed event to the accessibility object. */
+ void AccSendVisibleEvent();
+ /** Sends a selection changed event to the accessibility object. */
+ void AccSendSelectionEvent();
+ /** Sends a table model changed event for changed cell contents to the accessibility object. */
+ void AccSendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows = true );
+ /** Sends a table model changed event for an inserted column to the accessibility object. */
+ void AccSendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn );
+ /** Sends a table model changed event for a removed column to the accessibility object. */
+ void AccSendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn );
+
+ ScAccessibleCsvControl* GetAccessible() { return mxAccessible.get(); }
+
+ // repaint helpers --------------------------------------------------------
+
+ /** Sets the graphic invalid (next Redraw() will not use cached graphic). */
+ void InvalidateGfx() { mbValidGfx = false; }
+ /** Sets the graphic valid (next Redraw() will use cached graphic). */
+ void ValidateGfx() { mbValidGfx = true; }
+ /** Returns true, if cached graphic is valid. */
+ bool IsValidGfx() const { return mbValidGfx; }
+
+ /** Repaints all controls.
+ @param bInvalidate true = invalidates graphics of this control (not all). */
+ void Repaint( bool bInvalidate = false );
+ /** Increases no-repaint counter (controls do not repaint until the last EnableRepaint()). */
+ void DisableRepaint();
+ /** Decreases no-repaint counter and repaints if counter reaches 0. */
+ void EnableRepaint();
+ /** Returns true, if controls will not repaint. */
+ bool IsNoRepaint() const { return mrData.mnNoRepaint > 0; }
+
+ // command handling -------------------------------------------------------
+
+ /** Sets a new command handler. */
+ void SetCmdHdl( const Link<ScCsvControl&,void>& rHdl ) { maCmdHdl = rHdl; }
+ /** Returns data of the last command. */
+ const ScCsvCmd& GetCmd() const { return maCmd; }
+
+ /** Executes a command by calling command handler. */
+ void Execute(
+ ScCsvCmdType eType,
+ sal_Int32 nParam1 = CSV_POS_INVALID,
+ sal_Int32 nParam2 = CSV_POS_INVALID );
+
+ // layout helpers ---------------------------------------------------------
+
+ /** Returns a reference to the current layout data. */
+ const ScCsvLayoutData& GetLayoutData() const { return mrData; }
+ /** Returns true, if the Right-to-Left layout mode is active. */
+ bool IsRTL() const { return mrData.mbAppRTL; }
+
+ /** Returns the number of available positions. */
+ sal_Int32 GetPosCount() const { return mrData.mnPosCount; }
+ /** Returns the number of visible positions. */
+ sal_Int32 GetVisPosCount() const;
+ /** Returns the first visible position. */
+ sal_Int32 GetFirstVisPos() const { return mrData.mnPosOffset; }
+ /** Returns the last visible position. */
+ sal_Int32 GetLastVisPos() const { return GetFirstVisPos() + GetVisPosCount(); }
+ /** Returns highest possible position for first visible character. */
+ sal_Int32 GetMaxPosOffset() const;
+
+ /** Returns true, if it is allowed to set a split at nPos. */
+ bool IsValidSplitPos( sal_Int32 nPos ) const;
+ /** Returns true, if nPos is an allowed AND visible split position. */
+ bool IsVisibleSplitPos( sal_Int32 nPos ) const;
+
+ /** Returns the width of the header column. */
+ sal_Int32 GetHdrWidth() const { return mrData.mnHdrWidth; }
+ /** Returns the width of one character column. */
+ sal_Int32 GetCharWidth() const { return mrData.mnCharWidth; }
+ /** Returns the start position of the header column. */
+ sal_Int32 GetHdrX() const;
+ /** Returns the X position of the first pixel of the data area. */
+ sal_Int32 GetFirstX() const;
+ /** Returns the X position of the last pixel of the data area. */
+ sal_Int32 GetLastX() const;
+ /** Returns output X coordinate of the specified position. */
+ sal_Int32 GetX( sal_Int32 nPos ) const;
+ /** Returns position from output coordinate. */
+ sal_Int32 GetPosFromX( sal_Int32 nX ) const;
+
+ /** Returns the number of data lines. */
+ sal_Int32 GetLineCount() const { return mrData.mnLineCount; }
+ /** Returns the number of visible lines (including partly visible bottom line). */
+ sal_Int32 GetVisLineCount() const;
+ /** Returns index of first visible line. */
+ sal_Int32 GetFirstVisLine() const { return mrData.mnLineOffset; }
+ /** Returns index of last visible line. */
+ sal_Int32 GetLastVisLine() const;
+ /** Returns highest possible index for first line. */
+ sal_Int32 GetMaxLineOffset() const;
+
+ /** Returns true, if nLine is a valid line index. */
+ bool IsValidLine( sal_Int32 nLine ) const;
+ /** Returns true, if nLine is a valid and visible line index. */
+ bool IsVisibleLine( sal_Int32 nLine ) const;
+
+ /** Returns the height of the header line. */
+ sal_Int32 GetHdrHeight() const { return mrData.mnHdrHeight; }
+ /** Returns the height of one line. */
+ sal_Int32 GetLineHeight() const { return mrData.mnLineHeight; }
+ /** Returns output Y coordinate of the specified line. */
+ sal_Int32 GetY( sal_Int32 nLine ) const;
+ /** Returns line index from output coordinate. */
+ sal_Int32 GetLineFromY( sal_Int32 nY ) const;
+
+ /** Returns the ruler cursor position. */
+ sal_Int32 GetRulerCursorPos() const { return mrData.mnPosCursor; }
+ /** Returns the data grid cursor position (not column index!). */
+ sal_Int32 GetGridCursorPos() const { return mrData.mnColCursor; }
+
+ // static helpers ---------------------------------------------------------
+
+ /** Inverts a rectangle in the specified output device. */
+ static void ImplInvertRect( OutputDevice& rOutDev, const tools::Rectangle& rRect );
+
+ /** Returns direction code for the keys LEFT, RIGHT, HOME, END.
+ @param bHomeEnd false = ignore HOME and END key. */
+ static ScMoveMode GetHorzDirection( sal_uInt16 nCode, bool bHomeEnd );
+ /** Returns direction code for the keys UP, DOWN, HOME, END, PAGE UP, PAGE DOWN.
+ @param bHomeEnd false = ignore HOME and END key. */
+ static ScMoveMode GetVertDirection( sal_uInt16 nCode, bool bHomeEnd );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/csvgrid.hxx b/sc/source/ui/inc/csvgrid.hxx
new file mode 100644
index 0000000000..c729d9815f
--- /dev/null
+++ b/sc/source/ui/inc/csvgrid.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <vcl/virdev.hxx>
+#include <unotools/options.hxx>
+
+#include <vector>
+#include <memory>
+#include <scdllapi.h>
+#include <editutil.hxx>
+#include "csvcontrol.hxx"
+#include "csvsplits.hxx"
+
+namespace svtools { class ColorConfig; }
+class EditEngine;
+class ScAsciiOptions;
+class ScAccessibleCsvControl;
+class ScCsvTableBox;
+
+const sal_uInt32 CSV_COLUMN_INVALID = CSV_VEC_NOTFOUND;
+
+/** This struct contains the state of one table column. */
+struct ScCsvColState
+{
+ sal_Int32 mnType; /// Data type.
+ bool mbColumnSelected;
+
+ explicit ScCsvColState( sal_Int32 nType = CSV_TYPE_DEFAULT ) :
+ mnType( nType ), mbColumnSelected( false ) {}
+
+ bool IsSelected() const { return mbColumnSelected; }
+ void Select( bool bSel ) { mbColumnSelected = bSel; }
+};
+
+typedef ::std::vector< ScCsvColState > ScCsvColStateVec;
+
+/** A data grid control for the CSV import dialog. The design of this control
+ simulates a Calc spreadsheet with row and column headers. */
+class SC_DLLPUBLIC ScCsvGrid : public ScCsvControl, public utl::ConfigurationListener
+{
+private:
+ ScCsvTableBox* mpTableBox; /// Grid Parent
+ VclPtr<VirtualDevice> mpBackgrDev; /// Grid background, headers, cell texts.
+ VclPtr<VirtualDevice> mpGridDev; /// Data grid with selection and cursor.
+ std::unique_ptr<weld::Menu> mxPopup; /// Popup menu for column types.
+
+ ::svtools::ColorConfig* mpColorConfig; /// Application color configuration.
+ Color maBackColor; /// Cell background color.
+ Color maGridColor; /// Table grid color.
+ Color maGridPBColor; /// Grid color for "first imported line" delimiter.
+ Color maAppBackColor; /// Background color for unused area.
+ Color maTextColor; /// Text color for data area.
+ Color maHeaderBackColor; /// Background color for headers.
+ Color maHeaderGridColor; /// Grid color for headers.
+ Color maHeaderTextColor; /// Text color for headers.
+ Color maSelectColor; /// Header color of selected columns.
+
+ std::unique_ptr< ScEditEngineDefaulter >
+ mpEditEngine; /// For drawing cell texts.
+ vcl::Font maHeaderFont; /// Font for column and row headers.
+ vcl::Font maMonoFont; /// Monospace font for data cells.
+ Size maWinSize; /// Size of the control.
+ Size maEdEngSize; /// Paper size for edit engine.
+
+ ScCsvSplits maSplits; /// Vector with split positions.
+ ScCsvColStateVec maColStates; /// State of each column.
+ std::vector<OUString> maTypeNames; /// UI names of data types.
+ std::vector< std::vector<OUString> > maTexts; /// 2D-vector for cell texts.
+
+ sal_Int32 mnFirstImpLine; /// First imported line (0-based).
+ sal_uInt32 mnRecentSelCol; /// Index of most recently selected column.
+ sal_uInt32 mnMTCurrCol; /// Current column of mouse tracking.
+ bool mbTracking; /// True if Mouse tracking
+ bool mbMTSelecting; /// Mouse tracking mode: true = select, false = deselect.
+
+public:
+ explicit ScCsvGrid(const ScCsvLayoutData& rData, std::unique_ptr<weld::Menu> xPopup, ScCsvTableBox* pTableBox);
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ ScCsvTableBox* GetTableBox() { return mpTableBox; }
+ virtual ~ScCsvGrid() override;
+
+ /** Finishes initialization. Must be called after constructing a new object. */
+ void Init();
+
+ // common grid handling ---------------------------------------------------
+public:
+ /** Updates layout data dependent from the control's state. */
+ void UpdateLayoutData();
+ /** Updates X coordinate of first visible position dependent from line numbers. */
+ void UpdateOffsetX();
+ /** Apply current layout data to the grid control. */
+ void ApplyLayout( const ScCsvLayoutData& rOldData );
+ /** Sets the number of the first imported line (for visual feedback). nLine is 0-based! */
+ void SetFirstImportedLine( sal_Int32 nLine );
+
+ /** Finds a column position nearest to nPos which does not cause scrolling the visible area. */
+ sal_Int32 GetNoScrollCol( sal_Int32 nPos ) const;
+
+private:
+ /** Reads colors from system settings. */
+ SAL_DLLPRIVATE void InitColors();
+ /** Initializes all font settings. */
+ SAL_DLLPRIVATE void InitFonts();
+ /** Initializes all data dependent from the control's size. */
+ SAL_DLLPRIVATE void InitSizeData();
+
+ // split handling ---------------------------------------------------------
+public:
+ /** Inserts a split. */
+ void InsertSplit( sal_Int32 nPos );
+ /** Removes a split. */
+ void RemoveSplit( sal_Int32 nPos );
+ /** Inserts a new or removes an existing split. */
+ void MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos );
+ /** Removes all splits. */
+ void RemoveAllSplits();
+ /** Removes all splits and inserts the splits from rSplits. */
+ void SetSplits( const ScCsvSplits& rSplits );
+
+private:
+ /** Inserts a split and adjusts column data. */
+ SAL_DLLPRIVATE bool ImplInsertSplit( sal_Int32 nPos );
+ /** Removes a split and adjusts column data. */
+ SAL_DLLPRIVATE bool ImplRemoveSplit( sal_Int32 nPos );
+ /** Clears the split array and re-inserts boundary splits. */
+ SAL_DLLPRIVATE void ImplClearSplits();
+
+ // columns/column types ---------------------------------------------------
+public:
+ /** Returns the number of columns. */
+ sal_uInt32 GetColumnCount() const { return maColStates.size(); }
+ /** Returns the index of the first visible column. */
+ sal_uInt32 GetFirstVisColumn() const;
+ /** Returns the index of the last visible column. */
+ sal_uInt32 GetLastVisColumn() const;
+
+ /** Returns true, if nColIndex points to an existing column. */
+ bool IsValidColumn( sal_uInt32 nColIndex ) const;
+ /** Returns true, if column with index nColIndex is (at least partly) visible. */
+ bool IsVisibleColumn( sal_uInt32 nColIndex ) const;
+
+ /** Returns X coordinate of the specified column. */
+ sal_Int32 GetColumnX( sal_uInt32 nColIndex ) const;
+ /** Returns column index from output coordinate. */
+ sal_uInt32 GetColumnFromX( sal_Int32 nX ) const;
+
+ /** Returns start position of the column with the specified index. */
+ sal_Int32 GetColumnPos( sal_uInt32 nColIndex ) const { return maSplits[ nColIndex ]; }
+ /** Returns column index from position. A split counts to its following column. */
+ sal_uInt32 GetColumnFromPos( sal_Int32 nPos ) const;
+ /** Returns the character width of the column with the specified index. */
+ sal_Int32 GetColumnWidth( sal_uInt32 nColIndex ) const;
+
+ /** Returns the vector with the states of all columns. */
+ const ScCsvColStateVec& GetColumnStates() const { return maColStates; }
+ /** Sets all column states to the values in the passed vector. */
+ void SetColumnStates( ScCsvColStateVec&& rColStates );
+ /** Returns the data type of the selected columns. */
+ sal_Int32 GetSelColumnType() const;
+ /** Changes the data type of all selected columns. */
+ void SetSelColumnType( sal_Int32 nType );
+ /** Sets new UI data type names. */
+ void SetTypeNames( std::vector<OUString>&& rTypeNames );
+ /** Returns the UI type name of the specified column. */
+ OUString GetColumnTypeName( sal_uInt32 nColIndex ) const;
+
+ /** Fills the options object with column data for separators mode. */
+ void FillColumnDataSep( ScAsciiOptions& rOptions ) const;
+ /** Fills the options object with column data for fixed width mode. */
+ void FillColumnDataFix( ScAsciiOptions& rOptions ) const;
+
+private:
+ /** Returns the data type of the specified column. */
+ SAL_DLLPRIVATE sal_Int32 GetColumnType( sal_uInt32 nColIndex ) const;
+ /** Sets the data type of the specified column. */
+ SAL_DLLPRIVATE void SetColumnType( sal_uInt32 nColIndex, sal_Int32 nColType );
+
+ /** Scrolls data grid vertically. */
+ SAL_DLLPRIVATE void ScrollVertRel( ScMoveMode eDir );
+ /** Executes the data type popup menu. */
+ SAL_DLLPRIVATE void ExecutePopup( const Point& rPos );
+
+ // selection handling -----------------------------------------------------
+public:
+ /** Returns true, if the specified column is selected. */
+ bool IsSelected( sal_uInt32 nColIndex ) const;
+ /** Returns index of the first selected column. */
+ sal_uInt32 GetFirstSelected() const;
+ /** Returns index of the first selected column really after nFromIndex. */
+ sal_uInt32 GetNextSelected( sal_uInt32 nFromIndex ) const;
+ /** Selects or deselects the specified column. */
+ void Select( sal_uInt32 nColIndex, bool bSelect = true );
+ /** Toggles selection of the specified column. */
+ void ToggleSelect( sal_uInt32 nColIndex );
+ /** Selects or deselects the specified column range. */
+ void SelectRange( sal_uInt32 nColIndex1, sal_uInt32 nColIndex2, bool bSelect = true );
+ /** Selects or deselects all columns. */
+ void SelectAll( bool bSelect = true );
+
+ /** Returns index of the focused column. */
+ sal_uInt32 GetFocusColumn() const { return GetColumnFromPos( GetGridCursorPos() ); }
+
+private:
+ /** Moves column cursor to a new position. */
+ SAL_DLLPRIVATE void MoveCursor( sal_uInt32 nColIndex );
+ /** Moves column cursor to the given direction. */
+ SAL_DLLPRIVATE void MoveCursorRel( ScMoveMode eDir );
+
+ /** Clears the entire selection without notify. */
+ SAL_DLLPRIVATE void ImplClearSelection();
+
+ /** Executes selection action for a specific column. */
+ SAL_DLLPRIVATE void DoSelectAction( sal_uInt32 nColIndex, sal_uInt16 nModifier );
+
+ // cell contents ----------------------------------------------------------
+public:
+ /** Fills all cells of a line with the passed text (separators mode). */
+ void ImplSetTextLineSep(
+ sal_Int32 nLine, const OUString& rTextLine,
+ const OUString& rSepChars, sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace = false );
+ /** Fills all cells of a line with the passed text (fixed width mode). */
+ void ImplSetTextLineFix( sal_Int32 nLine, const OUString& rTextLine );
+
+ /** Returns the text of the specified cell. */
+ OUString GetCellText( sal_uInt32 nColIndex, sal_Int32 nLine ) const;
+
+ // event handling ---------------------------------------------------------
+protected:
+ virtual void Resize() override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual bool KeyInput( const KeyEvent& rKEvt ) override;
+ virtual bool Command( const CommandEvent& rCEvt ) override;
+
+ virtual tools::Rectangle GetFocusRect() override;
+
+ virtual void StyleUpdated() override;
+
+ virtual void ConfigurationChanged( ::utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
+
+ // painting ---------------------------------------------------------------
+protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override;
+
+public:
+ /** Redraws the entire data grid. */
+ void ImplRedraw(vcl::RenderContext& rRenderContext);
+ /** Returns a pointer to the used edit engine. */
+ EditEngine* GetEditEngine();
+
+private:
+ /** Returns the width of the control. */
+ sal_Int32 GetWidth() const { return maWinSize.Width(); }
+ /** Returns the height of the control. */
+ sal_Int32 GetHeight() const { return maWinSize.Height(); }
+
+ /** Sets a clip region in the specified output device for the specified column. */
+ SAL_DLLPRIVATE void ImplSetColumnClipRegion( OutputDevice& rOutDev, sal_uInt32 nColIndex );
+ /** Draws the header of the specified column to the specified output device. */
+ SAL_DLLPRIVATE void ImplDrawColumnHeader( OutputDevice& rOutDev, sal_uInt32 nColIndex, Color aFillColor );
+
+ /** Draws the text at the specified position to maBackgrDev. */
+ SAL_DLLPRIVATE void ImplDrawCellText( const Point& rPos, const OUString& rText );
+ /** Draws the "first imported line" separator to maBackgrDev (or erases, if bSet is false). */
+ SAL_DLLPRIVATE void ImplDrawFirstLineSep( bool bSet );
+ /** Draws the column with index nColIndex to maBackgrDev. */
+ SAL_DLLPRIVATE void ImplDrawColumnBackgr( sal_uInt32 nColIndex );
+ /** Draws the row headers column to maBackgrDev. */
+ SAL_DLLPRIVATE void ImplDrawRowHeaders();
+ /** Draws all columns and the row headers column to maBackgrDev. */
+ SAL_DLLPRIVATE void ImplDrawBackgrDev();
+
+ /** Draws the column with index nColIndex with its selection state to maGridDev. */
+ SAL_DLLPRIVATE void ImplDrawColumnSelection( sal_uInt32 nColIndex );
+ /** Draws all columns with selection and cursor to maGridDev. */
+ SAL_DLLPRIVATE void ImplDrawGridDev();
+
+ /** Redraws the entire column (background and selection). */
+ SAL_DLLPRIVATE void ImplDrawColumn( sal_uInt32 nColIndex );
+
+ /** Optimized drawing: Scrolls horizontally and redraws only missing parts. */
+ SAL_DLLPRIVATE void ImplDrawHorzScrolled( sal_Int32 nOldPos );
+
+ /** Inverts the cursor bar at the specified position in maGridDev. */
+ SAL_DLLPRIVATE void ImplInvertCursor( sal_Int32 nPos );
+
+ // accessibility ----------------------------------------------------------
+protected:
+ /** Creates a new accessible object. */
+ virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/csvruler.hxx b/sc/source/ui/inc/csvruler.hxx
new file mode 100644
index 0000000000..5173f0d200
--- /dev/null
+++ b/sc/source/ui/inc/csvruler.hxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "csvcontrol.hxx"
+#include "csvsplits.hxx"
+
+#include <vcl/virdev.hxx>
+
+class ScAccessibleCsvControl;
+class ScCsvTableBox;
+
+/** A ruler control for the CSV import dialog. Supports setting and moving
+ splits (which divide lines of data into several columns). */
+class ScCsvRuler : public ScCsvControl
+{
+private:
+ ScCsvTableBox* mpTableBox; /// Grid Parent
+
+ ScopedVclPtrInstance<VirtualDevice> maBackgrDev;/// Ruler background, scaling.
+ ScopedVclPtrInstance<VirtualDevice> maRulerDev; /// Ruler with splits and cursor.
+
+ Color maBackColor; /// Background color.
+ Color maActiveColor; /// Color for active part of ruler.
+ Color maTextColor; /// Text and scale color.
+ Color maSplitColor; /// Split area color.
+
+ ScCsvSplits maSplits; /// Vector with split positions.
+ ScCsvSplits maOldSplits; /// Old state for cancellation.
+
+ sal_Int32 mnPosCursorLast; /// Last valid position of cursor.
+ sal_Int32 mnPosMTStart; /// Start position of mouse tracking.
+ sal_Int32 mnPosMTCurr; /// Current position of mouse tracking.
+ bool mbPosMTMoved; /// Tracking: Anytime moved to another position?
+
+ Size maWinSize; /// Size of the control.
+ tools::Rectangle maActiveRect; /// The active area of the ruler.
+ sal_Int32 mnSplitSize; /// Size of a split circle.
+ bool mbTracking; /// If currently mouse tracking
+
+public:
+ explicit ScCsvRuler(const ScCsvLayoutData& rData, ScCsvTableBox* pTableBox);
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ ScCsvTableBox* GetTableBox() { return mpTableBox; }
+ virtual ~ScCsvRuler() override;
+
+ // common ruler handling --------------------------------------------------
+public:
+ /** Apply current layout data to the ruler. */
+ void ApplyLayout( const ScCsvLayoutData& rOldData );
+
+private:
+ /** Reads colors from system settings. */
+ void InitColors();
+ /** Initializes all data dependent from the control's size. */
+ void InitSizeData();
+
+ /** Moves cursor to a new position.
+ @param bScroll sal_True = The method may scroll the ruler. */
+ void MoveCursor( sal_Int32 nPos, bool bScroll = true );
+ /** Moves cursor to the given direction. */
+ void MoveCursorRel( ScMoveMode eDir );
+ /** Sets cursor to an existing split, according to eDir. */
+ void MoveCursorToSplit( ScMoveMode eDir );
+ /** Scrolls data grid vertically. */
+ void ScrollVertRel( ScMoveMode eDir );
+
+ // split handling ---------------------------------------------------------
+public:
+ /** Returns the split array. */
+ const ScCsvSplits& GetSplits() const { return maSplits; }
+ /** Returns the number of splits. */
+ sal_uInt32 GetSplitCount() const
+ { return maSplits.Count(); }
+ /** Returns the position of the specified split. */
+ sal_Int32 GetSplitPos( sal_uInt32 nIndex ) const
+ { return maSplits[ nIndex ]; }
+ /** Finds a position nearest to nPos which does not cause scrolling the visible area. */
+ sal_Int32 GetNoScrollPos( sal_Int32 nPos ) const;
+
+ /** Returns true if at position nPos is a split. */
+ bool HasSplit( sal_Int32 nPos ) const { return maSplits.HasSplit( nPos ); }
+ /** Inserts a split. */
+ void InsertSplit( sal_Int32 nPos );
+ /** Removes a split. */
+ void RemoveSplit( sal_Int32 nPos );
+ /** Moves a split from nPos to nNewPos. */
+ void MoveSplit( sal_Int32 nPos, sal_Int32 nNewPos );
+ /** Removes all splits of the ruler. */
+ void RemoveAllSplits();
+
+private:
+ /** Finds next position without a split. */
+ sal_Int32 FindEmptyPos( sal_Int32 nPos, ScMoveMode eDir ) const;
+
+ /** Moves split and cursor to nNewPos and commits event. */
+ void MoveCurrSplit( sal_Int32 nNewPos );
+ /** Moves split and cursor to the given direction and commits event. */
+ void MoveCurrSplitRel( ScMoveMode eDir );
+
+ // event handling ---------------------------------------------------------
+protected:
+ virtual void Resize() override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+ virtual void StyleUpdated() override;
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool MouseButtonUp( const MouseEvent& rMEvt ) override;
+
+ virtual bool KeyInput( const KeyEvent& rKEvt ) override;
+
+ virtual tools::Rectangle GetFocusRect() override;
+
+private:
+ /** Starts tracking at the specified position. */
+ void StartMouseTracking( sal_Int32 nPos );
+ /** Moves tracking to a new position. */
+ void MoveMouseTracking( sal_Int32 nPos );
+ /** Applies tracking action for the current tracking position */
+ void EndMouseTracking();
+
+ // painting ---------------------------------------------------------------
+protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override;
+
+public:
+ /** Redraws the entire ruler. */
+ void ImplRedraw(vcl::RenderContext& rRenderContext);
+
+private:
+ /** Returns the width of the control. */
+ sal_Int32 GetWidth() const { return maWinSize.Width(); }
+ /** Returns the height of the control. */
+ sal_Int32 GetHeight() const { return maWinSize.Height(); }
+ /** Update the split size depending on the last width set by CSVCMD_SETCHARWIDTH */
+ void UpdateSplitSize();
+
+ /** Draws the background and active area to maBackgrDev (only the given X range). */
+ void ImplDrawArea( sal_Int32 nPosX, sal_Int32 nWidth );
+ /** Draws the entire ruler background with scaling to maBackgrDev. */
+ void ImplDrawBackgrDev();
+
+ /** Draws a split to maRulerDev. */
+ void ImplDrawSplit( sal_Int32 nPos );
+ /** Erases a split from maRulerDev. */
+ void ImplEraseSplit( sal_Int32 nPos );
+ /** Draws the ruler background, all splits and the cursor to maRulerDev. */
+ void ImplDrawRulerDev();
+
+ /** Inverts the cursor bar at the specified position in maRulerDev. */
+ void ImplInvertCursor( sal_Int32 nPos );
+
+ /** Sets arrow or horizontal split pointer. */
+ void ImplSetMousePointer( sal_Int32 nPos );
+
+ // accessibility ----------------------------------------------------------
+protected:
+ /** Creates a new accessible object. */
+ virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/csvsplits.hxx b/sc/source/ui/inc/csvsplits.hxx
new file mode 100644
index 0000000000..d10e72cc79
--- /dev/null
+++ b/sc/source/ui/inc/csvsplits.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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#include <vector>
+
+/** Constant for an invalid vector index. */
+const sal_uInt32 CSV_VEC_NOTFOUND = SAL_MAX_UINT32;
+/** Constant for an invalid ruler position. */
+const sal_Int32 CSV_POS_INVALID = -1;
+
+/** A vector of column splits that supports inserting, removing and moving splits. */
+class ScCsvSplits
+{
+private:
+ typedef ::std::vector< sal_Int32 > ScSplitVector;
+ typedef ScSplitVector::const_iterator const_iterator;
+
+ ScSplitVector maVec; /// The split container.
+
+public:
+ // *** access by position *** ---------------------------------------------
+
+ /** Inserts a new split at position nPos into the vector.
+ @return true = split inserted (nPos was valid and empty). */
+ bool Insert( sal_Int32 nPos );
+ /** Removes a split by position.
+ @return true = split found and removed. */
+ bool Remove( sal_Int32 nPos );
+ /** Removes a range of splits in the given position range. */
+ void RemoveRange( sal_Int32 nPosStart, sal_Int32 nPosEnd );
+ /** Removes all elements from the vector. */
+ void Clear();
+
+ /** Returns true if at position nPos is a split. */
+ bool HasSplit( sal_Int32 nPos ) const;
+
+ // *** access by index *** ------------------------------------------------
+
+ /** Searches for a split at position nPos.
+ @return the vector index of the split. */
+ sal_uInt32 GetIndex( sal_Int32 nPos ) const;
+ /** Returns index of the first split greater than or equal to nPos. */
+ sal_uInt32 LowerBound( sal_Int32 nPos ) const;
+ /** Returns index of the last split less than or equal to nPos. */
+ sal_uInt32 UpperBound( sal_Int32 nPos ) const;
+
+ /** Returns the number of splits. */
+ sal_uInt32 Count() const
+ { return static_cast<sal_uInt32>(maVec.size()); }
+ /** Returns the position of the specified split. */
+ sal_Int32 GetPos( sal_uInt32 nIndex ) const;
+ /** Returns the position of the specified split. */
+ sal_Int32 operator[]( sal_uInt32 nIndex ) const
+ { return GetPos( nIndex ); }
+
+private:
+ /** Returns the vector index of an iterator. */
+ sal_uInt32 GetIterIndex( const_iterator const & aIter ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/csvtablebox.hxx b/sc/source/ui/inc/csvtablebox.hxx
new file mode 100644
index 0000000000..e2392a478f
--- /dev/null
+++ b/sc/source/ui/inc/csvtablebox.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 .
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <vcl/weld.hxx>
+#include <scdllapi.h>
+#include "csvcontrol.hxx"
+#include "csvruler.hxx"
+#include "csvgrid.hxx"
+
+class ScAsciiOptions;
+namespace weld {
+ class ComboBox;
+}
+
+/* ============================================================================
+Position: Positions between the characters (the dots in the ruler).
+Character: The characters (the range from one position to the next).
+Split: Positions which contain a split to divide characters into groups (columns).
+Column: The range between two splits.
+============================================================================ */
+
+/** The control in the CSV import dialog that contains a ruler and a data grid
+ to visualize and modify the current import settings. */
+class SC_DLLPUBLIC ScCsvTableBox
+{
+private:
+ ScCsvLayoutData maData; /// Current layout data of the controls.
+
+ std::unique_ptr<ScCsvRuler> mxRuler; /// The ruler for fixed width mode.
+ std::unique_ptr<ScCsvGrid> mxGrid; /// Calc-like data table for fixed width mode.
+ std::unique_ptr<weld::ScrolledWindow> mxScroll; /// Scrolled Window
+ std::unique_ptr<weld::CustomWeld> mxRulerWeld; /// Connect the ruler to its drawingarea
+ std::unique_ptr<weld::CustomWeld> mxGridWeld; /// connect the grid to its drawingarea
+
+ Link<ScCsvTableBox&,void> maUpdateTextHdl; /// Updates all cell texts.
+ Link<ScCsvTableBox&,void> maColTypeHdl; /// Handler for exporting the column type.
+
+ Idle maEndScrollIdle; /// Called when horizontal scrolling has ended
+
+ ScCsvColStateVec maFixColStates; /// Column states in fixed width mode.
+ ScCsvColStateVec maSepColStates; /// Column states in separators mode.
+
+ sal_Int32 mnFixedWidth; /// Cached total width for fixed width mode.
+
+ bool mbFixedMode; /// false = Separators, true = Fixed width.
+
+public:
+ explicit ScCsvTableBox(weld::Builder& rBuilder);
+ ~ScCsvTableBox();
+
+ /** Finishes initialization. Must be called after constructing a new object. */
+ void Init();
+
+ // common table box handling ----------------------------------------------
+public:
+ /** Sets the control to separators mode. */
+ void SetSeparatorsMode();
+ /** Sets the control to fixed width mode. */
+ void SetFixedWidthMode();
+
+ ScCsvRuler& GetRuler() { return *mxRuler; }
+ ScCsvGrid& GetGrid() { return *mxGrid; }
+
+ /** Initializes the children controls (pos/size, scroll bars, ...). */
+ SAL_DLLPRIVATE void InitControls();
+
+private:
+ /** Initializes size and position data of horizontal scrollbar. */
+ SAL_DLLPRIVATE void InitHScrollBar();
+ /** Initializes size and position data of vertical scrollbar. */
+ SAL_DLLPRIVATE void InitVScrollBar();
+
+ /** Calculates and sets valid position offset nearest to nPos. */
+ SAL_DLLPRIVATE void ImplSetPosOffset( sal_Int32 nPos )
+ { maData.mnPosOffset = std::clamp( nPos, sal_Int32(0), mxGrid->GetMaxPosOffset() ); }
+ /** Calculates and sets valid line offset nearest to nLine. */
+ SAL_DLLPRIVATE void ImplSetLineOffset( sal_Int32 nLine )
+ { maData.mnLineOffset = std::clamp( nLine, sal_Int32(0), mxGrid->GetMaxLineOffset() ); }
+ /** Moves controls (not cursors!) so that nPos becomes visible. */
+ SAL_DLLPRIVATE void MakePosVisible( sal_Int32 nPos );
+
+ // cell contents ----------------------------------------------------------
+public:
+ /** Fills all cells of all lines with the passed texts (Unicode strings). */
+ void SetUniStrings(
+ const OUString* pTextLines, const OUString& rSepChars,
+ sal_Unicode cTextSep, bool bMergeSep, bool bRemoveSpace );
+
+ // column settings --------------------------------------------------------
+public:
+ /** Reads UI strings for data types from the list box. */
+ void InitTypes(const weld::ComboBox& rListBox);
+ /** Returns the data type of the selected columns. */
+ sal_Int32 GetSelColumnType() const { return mxGrid->GetSelColumnType(); }
+
+ /** Fills the options object with current column data. */
+ void FillColumnData( ScAsciiOptions& rOptions ) const;
+
+ // event handling ---------------------------------------------------------
+public:
+ /** Sets a new handler for "update cell texts" requests. */
+ void SetUpdateTextHdl( const Link<ScCsvTableBox&,void>& rHdl ) { maUpdateTextHdl = rHdl; }
+ /** Sets a new handler for "column selection changed" events. */
+ void SetColTypeHdl( const Link<ScCsvTableBox&,void>& rHdl ) { maColTypeHdl = rHdl; }
+
+private:
+ DECL_DLLPRIVATE_LINK( CsvCmdHdl, ScCsvControl&, void );
+ DECL_DLLPRIVATE_LINK( HScrollHdl, weld::ScrolledWindow&, void );
+ DECL_DLLPRIVATE_LINK( VScrollHdl, weld::ScrolledWindow&, void );
+ DECL_DLLPRIVATE_LINK( ScrollEndHdl, Timer*, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dapidata.hxx b/sc/source/ui/inc/dapidata.hxx
new file mode 100644
index 0000000000..ebad1d2e65
--- /dev/null
+++ b/sc/source/ui/inc/dapidata.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+struct ScImportSourceDesc;
+
+class ScDataPilotDatabaseDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::ComboBox> m_xLbDatabase;
+ std::unique_ptr<weld::ComboBox> m_xCbObject;
+ std::unique_ptr<weld::ComboBox> m_xLbType;
+
+ void FillObjects();
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+
+public:
+ ScDataPilotDatabaseDlg(weld::Window* pParent);
+ virtual ~ScDataPilotDatabaseDlg() override;
+
+ void GetValues(ScImportSourceDesc& rDesc);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dapitype.hxx b/sc/source/ui/inc/dapitype.hxx
new file mode 100644
index 0000000000..e45400cdf6
--- /dev/null
+++ b/sc/source/ui/inc/dapitype.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScDataPilotSourceTypeDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::RadioButton> m_xBtnSelection;
+ std::unique_ptr<weld::RadioButton> m_xBtnNamedRange;
+ std::unique_ptr<weld::RadioButton> m_xBtnDatabase;
+ std::unique_ptr<weld::RadioButton> m_xBtnExternal;
+ std::unique_ptr<weld::ComboBox> m_xLbNamedRange;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+public:
+ ScDataPilotSourceTypeDlg(weld::Window* pParent, bool bEnableExternal);
+ virtual ~ScDataPilotSourceTypeDlg() override;
+ bool IsDatabase() const;
+ bool IsExternal() const;
+ bool IsNamedRange() const;
+ OUString GetSelectedNamedRange() const;
+ void AppendNamedRange(const OUString& rNames);
+
+private:
+ DECL_LINK(RadioClickHdl, weld::Toggleable&, void);
+ DECL_LINK(ResponseHdl, weld::Button&, void);
+};
+
+class ScDataPilotServiceDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::ComboBox> m_xLbService;
+ std::unique_ptr<weld::Entry> m_xEdSource;
+ std::unique_ptr<weld::Entry> m_xEdName;
+ std::unique_ptr<weld::Entry> m_xEdUser;
+ std::unique_ptr<weld::Entry> m_xEdPasswd;
+
+public:
+ ScDataPilotServiceDlg(weld::Window* pParent, const std::vector<OUString>& rServices);
+ virtual ~ScDataPilotServiceDlg() override;
+
+ OUString GetServiceName() const;
+ OUString GetParSource() const;
+ OUString GetParName() const;
+ OUString GetParUser() const;
+ OUString GetParPass() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/datafdlg.hxx b/sc/source/ui/inc/datafdlg.hxx
new file mode 100644
index 0000000000..4a05cbf1a1
--- /dev/null
+++ b/sc/source/ui/inc/datafdlg.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/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <types.hxx>
+#include "viewfunc.hxx"
+
+class ScTabViewShell;
+class ScDocument;
+
+#define MAX_DATAFORM_COLS 256
+#define MAX_DATAFORM_ROWS 32000
+
+class ScDataFormDlg : public weld::GenericDialogController
+{
+private:
+ OUString sNewRecord;
+
+ ScTabViewShell* pTabViewShell;
+ ScDocument* pDoc;
+ sal_uInt16 aColLength;
+ SCROW nCurrentRow;
+ SCCOL nStartCol;
+ SCCOL nEndCol;
+ SCROW nStartRow;
+ SCROW nEndRow;
+ SCTAB nTab;
+
+ std::unique_ptr<weld::Button> m_xBtnNew;
+ std::unique_ptr<weld::Button> m_xBtnDelete;
+ std::unique_ptr<weld::Button> m_xBtnRestore;
+ std::unique_ptr<weld::Button> m_xBtnPrev;
+ std::unique_ptr<weld::Button> m_xBtnNext;
+ std::unique_ptr<weld::Button> m_xBtnClose;
+ std::unique_ptr<weld::ScrolledWindow> m_xSlider;
+ std::unique_ptr<weld::Container> m_xGrid;
+ std::unique_ptr<weld::Label> m_xFixedText;
+ std::vector<std::unique_ptr<ScDataFormFragment>> m_aEntries;
+
+public:
+ ScDataFormDlg(weld::Window* pParent, ScTabViewShell* pTabViewShell);
+ virtual ~ScDataFormDlg() override;
+
+ void FillCtrls();
+private:
+
+ void SetButtonState();
+
+ // Handler:
+ DECL_LINK(Impl_NewHdl, weld::Button&, void);
+ DECL_LINK(Impl_PrevHdl, weld::Button&, void);
+ DECL_LINK(Impl_NextHdl, weld::Button&, void);
+
+ DECL_LINK(Impl_RestoreHdl, weld::Button&, void);
+ DECL_LINK(Impl_DeleteHdl, weld::Button&, void);
+ DECL_LINK(Impl_CloseHdl, weld::Button&, void);
+
+ DECL_LINK(Impl_ScrollHdl, weld::ScrolledWindow&, void);
+ DECL_LINK(Impl_DataModifyHdl, weld::Entry&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dataprovider.hxx b/sc/source/ui/inc/dataprovider.hxx
new file mode 100644
index 0000000000..680bf38193
--- /dev/null
+++ b/sc/source/ui/inc/dataprovider.hxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+#include <salhelper/thread.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <osl/mutex.hxx>
+#include <document.hxx>
+
+#include <rtl/strbuf.hxx>
+
+#include <atomic>
+#include <vector>
+//#include <map>
+
+#include <orcus/csv_parser.hpp>
+
+class SvStream;
+class ScDBData;
+
+namespace sc {
+
+class DataTransformation;
+class ExternalDataSource;
+
+class CSVFetchThread : public salhelper::Thread
+{
+ ScDocument& mrDocument;
+ OUString maURL;
+
+ std::atomic<bool> mbTerminate;
+
+ orcus::csv::parser_config maConfig;
+
+ std::vector<std::shared_ptr<sc::DataTransformation>> maDataTransformations;
+
+ std::function<void()> maImportFinishedHdl;
+
+
+public:
+ CSVFetchThread(ScDocument& rDoc, OUString , std::function<void()> aImportFinishedHdl,
+ std::vector<std::shared_ptr<sc::DataTransformation>>&& mrDataTransformations);
+ virtual ~CSVFetchThread() override;
+
+ void RequestTerminate();
+ bool IsRequestedTerminate();
+ void Terminate();
+ void EndThread();
+
+ virtual void execute() override;
+};
+
+/**
+ * Abstract class for all data provider.
+ *
+ */
+class DataProvider
+{
+protected:
+ /**
+ * If true make the threaded import deterministic for the tests.
+ */
+ bool mbDeterministic;
+ sc::ExternalDataSource& mrDataSource;
+
+public:
+ DataProvider(sc::ExternalDataSource& rDataSource);
+
+ virtual ~DataProvider();
+
+ virtual void Import() = 0;
+
+ virtual const OUString& GetURL() const = 0;
+
+ static std::unique_ptr<SvStream> FetchStreamFromURL(const OUString&, OStringBuffer& rBuffer);
+
+ void setDeterministic();
+};
+
+class CSVDataProvider : public DataProvider
+{
+ rtl::Reference<CSVFetchThread> mxCSVFetchThread;
+ ScDocument* mpDocument;
+ ScDocumentUniquePtr mpDoc;
+
+ void Refresh();
+
+public:
+ CSVDataProvider (ScDocument* pDoc, sc::ExternalDataSource& rDataSource);
+ virtual ~CSVDataProvider() override;
+
+ virtual void Import() override;
+
+ const OUString& GetURL() const override;
+ void ImportFinished();
+};
+
+/**
+ * This class handles the copying of the data from the imported
+ * temporary document to the actual document. Additionally, in the future
+ * we may decide to store data transformations in this class.
+ *
+ * In addition this class also handles how to deal with excess data by for example extending the ScDBData or by only showing the first or last entries.
+ *
+ * TODO: move the DataProvider::WriteToDoc here
+ *
+ */
+class ScDBDataManager
+{
+ OUString maDBName;
+ ScDocument* mpDoc;
+
+public:
+ ScDBDataManager(OUString aDBName, ScDocument* pDoc);
+ ~ScDBDataManager();
+
+ void SetDatabase(const OUString& rDBName);
+
+ ScDBData* getDBData();
+
+ void WriteToDoc(ScDocument& rDoc);
+};
+
+class DataProviderFactory
+{
+private:
+
+ static bool isInternalDataProvider(std::u16string_view rProvider);
+
+public:
+
+ static std::shared_ptr<DataProvider> getDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource);
+
+ static std::vector<OUString> getDataProviders();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dataproviderdlg.hxx b/sc/source/ui/inc/dataproviderdlg.hxx
new file mode 100644
index 0000000000..127b6361ab
--- /dev/null
+++ b/sc/source/ui/inc/dataproviderdlg.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/.
+ */
+
+#pragma once
+
+#include <datamapper.hxx>
+
+#include <sal/config.h>
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <vcl/idle.hxx>
+#include <vcl/weld.hxx>
+#include "datatableview.hxx"
+#include <memory>
+
+class ScDocument;
+class ScDataTransformationBaseControl;
+class ScDBData;
+
+class ScDataProviderDlg : public weld::GenericDialogController
+{
+private:
+ std::shared_ptr<ScDocument> mxDoc;
+ std::unique_ptr<weld::Container> mxBox;
+ css::uno::Reference<css::awt::XWindow> m_xTableParent;
+ VclPtr<ScDataTableView> mxTable;
+ std::unique_ptr<weld::Container> mxList;
+ std::unique_ptr<weld::ComboBox> mxDBRanges;
+ std::unique_ptr<weld::Button> mxOKBtn;
+ std::unique_ptr<weld::Button> mxCancelBtn;
+ std::unique_ptr<weld::Button> mxAddTransformationBtn;
+ std::unique_ptr<weld::ScrolledWindow> mxScroll;
+ std::unique_ptr<weld::Container> mxTransformationList;
+ std::unique_ptr<weld::ComboBox> mxTransformationBox;
+ std::unique_ptr<weld::ComboBox> mxProviderList;
+ std::unique_ptr<weld::Entry> mxEditURL;
+ std::unique_ptr<weld::Entry> mxEditID;
+ std::unique_ptr<weld::Button> mxApplyBtn;
+ std::unique_ptr<weld::Button> mxBrowseBtn;
+
+ OUString msApplyTooltip;
+ OUString msAddTransformationToolTip;
+
+ std::vector<std::unique_ptr<ScDataTransformationBaseControl>> maControls;
+
+ Idle maIdle;
+
+ sal_uInt32 mnIndex;
+ ScDBData* pDBData;
+
+ DECL_LINK(StartMenuHdl, const OString&, void);
+ DECL_LINK(ColumnMenuHdl, const weld::ComboBox&, void);
+ DECL_LINK(ScrollToEnd, Timer*, void);
+ DECL_LINK(ApplyQuitHdl, weld::Button&, void);
+ DECL_LINK(CancelQuitHdl, weld::Button&, void);
+ DECL_LINK(TransformationListHdl, weld::Button&, void);
+ DECL_LINK(ProviderSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(TransformationSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(IDEditHdl, weld::Entry&, void);
+ DECL_LINK(URLEditHdl, weld::Entry&, void);
+ DECL_LINK(ApplyBtnHdl, weld::Button&, void);
+ DECL_LINK(BrowseBtnHdl, weld::Button&, void);
+
+public:
+ ScDataProviderDlg(weld::Window* pWindow, std::shared_ptr<ScDocument> pDoc,
+ const ScDocument* pDocument);
+ virtual ~ScDataProviderDlg() override;
+
+ void applyAndQuit();
+ void cancelAndQuit();
+
+ void deleteColumn();
+ void splitColumn();
+ void mergeColumns();
+ void textTransformation();
+ void sortTransformation();
+ void aggregateFunction();
+ void numberTransformation();
+ void deletefromList(sal_uInt32 nIndex);
+ void replaceNullTransformation();
+ void dateTimeTransformation();
+ void findReplaceTransformation();
+ void deleteRowTransformation();
+ void swapRowsTransformation();
+
+ void updateApplyBtn(bool bValidConfig);
+ void isValid();
+
+ sc::ExternalDataSource getDataSource(ScDocument* pDoc);
+
+ void import(ScDocument& rDoc, bool bInternal = false);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/datastream.hxx b/sc/source/ui/inc/datastream.hxx
new file mode 100644
index 0000000000..b7a0a36797
--- /dev/null
+++ b/sc/source/ui/inc/datastream.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <vcl/timer.hxx>
+#include <address.hxx>
+#include <optional>
+#include <vector>
+
+#include <documentstreamaccess.hxx>
+
+class ScDocShell;
+
+namespace sc {
+
+namespace datastreams {
+ class ReaderThread;
+}
+
+class DataStream
+{
+public:
+ DataStream(const DataStream&) = delete;
+ const DataStream& operator=(const DataStream&) = delete;
+
+ struct Cell
+ {
+ struct Str
+ {
+ size_t Pos;
+ size_t Size;
+ };
+
+ union
+ {
+ Str maStr;
+ double mfValue;
+ };
+
+ bool mbValue;
+
+ Cell();
+ Cell( const Cell& r );
+ };
+
+ struct Line
+ {
+ OString maLine;
+ std::vector<Cell> maCells;
+ };
+ typedef std::vector<Line> LinesType;
+
+ enum MoveType { NO_MOVE, RANGE_DOWN, MOVE_DOWN, MOVE_UP };
+
+ static void MakeToolbarVisible();
+ static DataStream* Set(ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
+ sal_Int32 nLimit, MoveType eMove);
+
+ DataStream(
+ ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
+ sal_Int32 nLimit, MoveType eMove);
+
+ ~DataStream();
+
+ ScRange GetRange() const;
+ const OUString& GetURL() const { return msURL; }
+ MoveType GetMove() const { return meOrigMove;}
+ bool IsRefreshOnEmptyLine() const { return mbRefreshOnEmptyLine;}
+
+ void Decode(
+ const OUString& rURL, const ScRange& rRange, sal_Int32 nLimit,
+ MoveType eMove);
+
+ bool ImportData();
+ void StartImport();
+ void StopImport();
+
+ void SetRefreshOnEmptyLine( bool bVal );
+
+private:
+ Line ConsumeLine();
+ void MoveData();
+ void Text2Doc();
+ void Refresh();
+
+ DECL_LINK( ImportTimerHdl, Timer*, void );
+
+private:
+ ScDocShell* mpDocShell;
+ DocumentStreamAccess maDocAccess;
+ OUString msURL;
+ MoveType meOrigMove; // Initial move setting. This one gets saved to file.
+ MoveType meMove; // move setting during streaming, which may change in the middle.
+ bool mbRunning;
+ bool mbValuesInLine;
+ bool mbRefreshOnEmptyLine;
+ std::optional<LinesType> moLines;
+ size_t mnLinesCount;
+ size_t mnLinesSinceRefresh;
+ double mfLastRefreshTime;
+ SCROW mnCurRow;
+ ScRange maStartRange;
+ ScRange maEndRange;
+
+ Timer maImportTimer;
+
+ rtl::Reference<datastreams::ReaderThread> mxReaderThread;
+ bool mbIsFirst;
+ bool mbIsUpdate;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/datastreamdlg.hxx b/sc/source/ui/inc/datastreamdlg.hxx
new file mode 100644
index 0000000000..5f1f7eec49
--- /dev/null
+++ b/sc/source/ui/inc/datastreamdlg.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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vcl/weld.hxx>
+
+class ScDocShell;
+class SvtURLBox;
+class ScRange;
+
+namespace sc
+{
+class DataStream;
+
+class DataStreamDlg : public weld::GenericDialogController
+{
+ ScDocShell* m_pDocShell;
+
+ std::unique_ptr<SvtURLBox> m_xCbUrl;
+ std::unique_ptr<weld::Button> m_xBtnBrowse;
+ std::unique_ptr<weld::RadioButton> m_xRBValuesInLine;
+ std::unique_ptr<weld::RadioButton> m_xRBAddressValue;
+ std::unique_ptr<weld::CheckButton> m_xCBRefreshOnEmpty;
+ std::unique_ptr<weld::RadioButton> m_xRBDataDown;
+ std::unique_ptr<weld::RadioButton> m_xRBRangeDown;
+ std::unique_ptr<weld::RadioButton> m_xRBNoMove;
+ std::unique_ptr<weld::RadioButton> m_xRBMaxLimit;
+ std::unique_ptr<weld::RadioButton> m_xRBUnlimited;
+ std::unique_ptr<weld::Entry> m_xEdRange;
+ std::unique_ptr<weld::Entry> m_xEdLimit;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Frame> m_xVclFrameLimit;
+ std::unique_ptr<weld::Frame> m_xVclFrameMove;
+
+ DECL_LINK(UpdateClickHdl, weld::Toggleable&, void);
+ DECL_LINK(UpdateHdl, weld::Entry&, void);
+ DECL_LINK(UpdateComboBoxHdl, weld::ComboBox&, void);
+ DECL_LINK(BrowseHdl, weld::Button&, void);
+
+ void UpdateEnable();
+ ScRange GetStartRange();
+
+public:
+ DataStreamDlg(ScDocShell* pDocShell, weld::Window* pParent);
+ virtual ~DataStreamDlg() override;
+
+ void Init(const DataStream& rStrm);
+
+ void StartStream();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/datatableview.hxx b/sc/source/ui/inc/datatableview.hxx
new file mode 100644
index 0000000000..2635c7952a
--- /dev/null
+++ b/sc/source/ui/inc/datatableview.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <svtools/scrolladaptor.hxx>
+#include <vcl/ctrl.hxx>
+#include <types.hxx>
+#include "hdrcont.hxx"
+
+class ScDocument;
+
+class ScDataTableColView : public ScHeaderControl
+{
+ ScDocument* mpDoc;
+ SCCOL mnCol;
+
+public:
+ ScDataTableColView(vcl::Window* pParent, ScDocument* pDoc, SelectionEngine* pSelectionEngine);
+
+ void SetPos(SCCOLROW nRow);
+
+ virtual SCCOLROW GetPos() const override;
+ virtual sal_uInt16 GetEntrySize(SCCOLROW nPos) const override;
+ virtual OUString GetEntryText(SCCOLROW nPos) const override;
+ virtual bool IsLayoutRTL() const override;
+ virtual void SetEntrySize(SCCOLROW nPos, sal_uInt16 nWidth) override;
+ virtual void HideEntries(SCCOLROW nPos, SCCOLROW nEndPos) override;
+};
+
+class ScDataTableRowView : public ScHeaderControl
+{
+ ScDocument* mpDoc;
+ SCROW mnRow;
+
+public:
+ ScDataTableRowView(vcl::Window* pParent, ScDocument* pDoc, SelectionEngine* pSelectionEngine);
+
+ void SetPos(SCCOLROW nRow);
+
+ virtual SCCOLROW GetPos() const override;
+ virtual sal_uInt16 GetEntrySize(SCCOLROW nPos) const override;
+ virtual OUString GetEntryText(SCCOLROW nPos) const override;
+ virtual bool IsLayoutRTL() const override;
+ virtual void SetEntrySize(SCCOLROW nPos, sal_uInt16 nWidth) override;
+ virtual void HideEntries(SCCOLROW nPos, SCCOLROW nEndPos) override;
+};
+
+/*
+ * A simple UI component that presents a data table.
+ *
+ * Shares as much code as possible with the normal
+ * Calc grid rendering.
+ *
+ * This class should only depend on ScDocument and not
+ * on some of the Calc view shells.
+ */
+class ScDataTableView : public Control
+{
+ std::shared_ptr<ScDocument> mpDoc;
+ std::unique_ptr<SelectionEngine> mpSelectionEngine;
+ VclPtr<ScDataTableColView> mpColView;
+ VclPtr<ScDataTableRowView> mpRowView;
+ VclPtr<ScrollAdaptor> mpVScroll;
+ VclPtr<ScrollAdaptor> mpHScroll;
+
+ sal_uInt16 mnScrollBarSize;
+
+ SCROW mnFirstVisibleRow;
+ SCCOL mnFirstVisibleCol;
+
+ std::unique_ptr<MouseEvent> mpMouseEvent;
+
+ DECL_LINK(VertScrollHdl, weld::Scrollbar&, void);
+ DECL_LINK(HorzScrollHdl, weld::Scrollbar&, void);
+
+public:
+ ScDataTableView(const css::uno::Reference<css::awt::XWindow>& rParent,
+ std::shared_ptr<ScDocument> pDoc);
+ ~ScDataTableView() override;
+
+ virtual void dispose() override;
+
+ virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
+ virtual void MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual void Resize() override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual Size GetOptimalSize() const override;
+
+ void getColRange(SCCOL& rStartCol, SCCOL& rEndCol) const;
+ void getRowRange(SCROW& rStartRow, SCROW& rEndRow) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/datatransformation.hxx b/sc/source/ui/inc/datatransformation.hxx
new file mode 100644
index 0000000000..aa822340fa
--- /dev/null
+++ b/sc/source/ui/inc/datatransformation.hxx
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include <scdllapi.h>
+
+#include <sortparam.hxx>
+
+#include <set>
+
+class ScDocument;
+
+namespace sc {
+
+enum class TransformationType
+{
+ MERGE_TRANSFORMATION,
+ SPLIT_TRANSFORMATION,
+ DELETE_TRANSFORMATION,
+ SORT_TRANSFORMATION,
+ TEXT_TRANSFORMATION,
+ AGGREGATE_FUNCTION,
+ NUMBER_TRANSFORMATION,
+ REMOVE_NULL_TRANSFORMATION,
+ DATETIME_TRANSFORMATION,
+ FINDREPLACE_TRANSFORMATION,
+ DELETEROW_TRANSFORMATION,
+ SWAPROWS_TRANSFORMATION
+};
+
+enum class TEXT_TRANSFORM_TYPE { TO_LOWER, TO_UPPER, CAPITALIZE, TRIM };
+
+enum class AGGREGATE_FUNCTION { SUM, AVERAGE, MIN, MAX };
+
+enum class NUMBER_TRANSFORM_TYPE { ROUND, ROUND_UP, ROUND_DOWN, ABSOLUTE, LOG_E, LOG_10, CUBE,
+ SQUARE, SQUARE_ROOT, EXPONENT, IS_EVEN, IS_ODD, SIGN };
+
+enum class DATETIME_TRANSFORMATION_TYPE { DATE_STRING, YEAR, START_OF_YEAR, END_OF_YEAR, MONTH,
+ MONTH_NAME, START_OF_MONTH, END_OF_MONTH, DAY, DAY_OF_WEEK, DAY_OF_YEAR, QUARTER, START_OF_QUARTER,
+ END_OF_QUARTER, TIME, HOUR, MINUTE, SECOND };
+
+class SC_DLLPUBLIC DataTransformation
+{
+protected:
+
+ static SCROW getLastRow(const ScDocument& rDoc, SCCOL nCol);
+
+public:
+ virtual ~DataTransformation();
+
+ virtual void Transform(ScDocument& rDoc) const = 0;
+
+ virtual TransformationType getTransformationType() const = 0;
+
+};
+
+class SC_DLLPUBLIC ColumnRemoveTransformation : public DataTransformation
+{
+ std::set<SCCOL> maColumns;
+
+public:
+
+ ColumnRemoveTransformation(std::set<SCCOL>&& rColumns);
+ virtual ~ColumnRemoveTransformation() override;
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ const std::set<SCCOL> & getColumns() const;
+};
+
+class SC_DLLPUBLIC SplitColumnTransformation : public DataTransformation
+{
+ SCCOL mnCol;
+ sal_Unicode mcSeparator;
+
+public:
+
+ SplitColumnTransformation(SCCOL nCol, sal_Unicode cSeparator);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ SCCOL getColumn() const;
+ sal_Unicode getSeparator() const;
+};
+
+class SC_DLLPUBLIC MergeColumnTransformation : public DataTransformation
+{
+ std::set<SCCOL> maColumns;
+ OUString maMergeString;
+
+public:
+
+ MergeColumnTransformation(std::set<SCCOL>&& rColumns, OUString aMergeString);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ const OUString & getMergeString() const;
+ const std::set<SCCOL> & getColumns() const;
+};
+
+class SortTransformation : public DataTransformation
+{
+ ScSortParam maSortParam;
+public:
+
+ SortTransformation(const ScSortParam& rParam);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ const ScSortParam & getSortParam() const;
+};
+
+class SC_DLLPUBLIC TextTransformation : public DataTransformation
+{
+ std::set<SCCOL> mnCol;
+ TEXT_TRANSFORM_TYPE maType;
+
+ public:
+ TextTransformation(std::set<SCCOL>&& nCol, const TEXT_TRANSFORM_TYPE rType);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ TEXT_TRANSFORM_TYPE getTextTransformationType() const;
+ const std::set<SCCOL>& getColumns() const;
+};
+
+class SC_DLLPUBLIC AggregateFunction : public DataTransformation
+{
+ std::set<SCCOL> maColumns;
+ AGGREGATE_FUNCTION maType;
+
+ public:
+ AggregateFunction(std::set<SCCOL>&& rColumns, const AGGREGATE_FUNCTION rType);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ AGGREGATE_FUNCTION getAggregateType() const;
+ const std::set<SCCOL>& getColumns() const;
+};
+
+class SC_DLLPUBLIC NumberTransformation : public DataTransformation
+{
+ std::set<SCCOL> mnCol;
+ NUMBER_TRANSFORM_TYPE maType;
+ int maPrecision;
+
+ public:
+ NumberTransformation(std::set<SCCOL>&& nCol, const NUMBER_TRANSFORM_TYPE rType);
+ NumberTransformation(std::set<SCCOL>&& nCol, const NUMBER_TRANSFORM_TYPE rType,
+ int nPrecision);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ NUMBER_TRANSFORM_TYPE getNumberTransformationType() const;
+ int getPrecision() const;
+ const std::set<SCCOL>& getColumn() const;
+};
+
+class SC_DLLPUBLIC ReplaceNullTransformation : public DataTransformation
+{
+ std::set<SCCOL> mnCol;
+ OUString msReplaceWith;
+
+ public:
+ ReplaceNullTransformation(std::set<SCCOL>&& nCol, OUString sReplaceWith);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ const std::set<SCCOL>& getColumn() const;
+ const OUString& getReplaceString() const;
+};
+
+class SC_DLLPUBLIC DateTimeTransformation : public DataTransformation
+{
+ std::set<SCCOL> mnCol;
+ DATETIME_TRANSFORMATION_TYPE maType;
+
+ public:
+ DateTimeTransformation(std::set<SCCOL>&& nCol,
+ const DATETIME_TRANSFORMATION_TYPE rType);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ DATETIME_TRANSFORMATION_TYPE getDateTimeTransformationType() const;
+ const std::set<SCCOL>& getColumn() const;
+};
+
+class FindReplaceTransformation : public DataTransformation
+{
+ SCCOL mnCol;
+ OUString maFindString;
+ OUString maReplaceString;
+
+ public:
+ FindReplaceTransformation(SCCOL nCol, OUString aFindString, OUString aReplaceString);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ SCCOL getColumn() const;
+ const OUString & getFindString() const;
+ const OUString & getReplaceString() const;
+};
+
+class DeleteRowTransformation : public DataTransformation
+{
+ SCCOL mnCol;
+ OUString maFindString;
+
+ public:
+ DeleteRowTransformation(SCCOL nCol, OUString aFindString);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ SCCOL getColumn() const;
+ const OUString & getFindString() const;
+};
+
+class SwapRowsTransformation : public DataTransformation
+{
+ SCROW mxRow, nxRow;
+
+ public:
+ SwapRowsTransformation(SCROW mRow, SCROW nRow);
+ virtual void Transform(ScDocument& rDoc) const override;
+ virtual TransformationType getTransformationType() const override;
+ SCROW getFirstRow() const;
+ SCROW getSecondRow() const;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dbdocfun.hxx b/sc/source/ui/inc/dbdocfun.hxx
new file mode 100644
index 0000000000..780e523b51
--- /dev/null
+++ b/sc/source/ui/inc/dbdocfun.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <vector>
+
+struct ScImportParam;
+struct ScQueryParam;
+struct ScSortParam;
+struct ScSubTotalParam;
+
+class SfxViewFrame;
+class ScDBData;
+class ScDocShell;
+class ScDPObject;
+class ScDBCollection;
+
+namespace com::sun::star::uno { template <typename > class Sequence; }
+namespace com::sun::star::beans { struct PropertyValue; }
+
+namespace svx {
+ class ODataAccessDescriptor;
+}
+
+class ScDBDocFunc
+{
+friend class ScDBFunc;
+
+private:
+ ScDocShell& rDocShell;
+
+public:
+ ScDBDocFunc( ScDocShell& rDocSh ): rDocShell(rDocSh) {}
+
+ void UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor );
+
+ bool DoImport( SCTAB nTab, const ScImportParam& rParam,
+ const svx::ODataAccessDescriptor* pDescriptor); // used for selection an existing ResultSet
+
+ void DoImportUno( const ScAddress& rPos,
+ const css::uno::Sequence<css::beans::PropertyValue>& aArgs );
+
+ static void ShowInBeamer( const ScImportParam& rParam, const SfxViewFrame* pFrame );
+
+ SC_DLLPUBLIC bool Sort(
+ SCTAB nTab, const ScSortParam& rSortParam, bool bRecord, bool bPaint, bool bApi );
+
+ SC_DLLPUBLIC bool Query( SCTAB nTab, const ScQueryParam& rQueryParam,
+ const ScRange* pAdvSource, bool bRecord, bool bApi );
+
+ void DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
+ bool bRecord, bool bApi );
+
+ bool AddDBRange( const OUString& rName, const ScRange& rRange );
+ bool DeleteDBRange( const OUString& rName );
+ bool RenameDBRange( const OUString& rOld, const OUString& rNew );
+ void ModifyDBData( const ScDBData& rNewData ); // Name unveraendert
+
+ void ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector<ScRange>& rDelAreaList );
+
+ bool RepeatDB( const OUString& rDBName, bool bApi, bool bIsUnnamed, SCTAB aTab = 0);
+
+ bool DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
+ bool bRecord, bool bApi, bool bAllowMove = false );
+
+ bool RemovePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi);
+ bool CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi);
+ bool UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi);
+
+ /**
+ * Reload the referenced pivot cache, and refresh all pivot tables that
+ * reference the cache.
+ */
+ void RefreshPivotTables(const ScDPObject* pDPObj, bool bApi);
+
+ /**
+ * Refresh the group dimensions of all pivot tables referencing the same
+ * cache.
+ */
+ void RefreshPivotTableGroups(ScDPObject* pDPObj);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dbfunc.hxx b/sc/source/ui/inc/dbfunc.hxx
new file mode 100644
index 0000000000..4ab5688a2b
--- /dev/null
+++ b/sc/source/ui/inc/dbfunc.hxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "viewfunc.hxx"
+#include <dptypes.hxx>
+
+namespace com::sun::star::sheet { struct DataPilotFieldFilter; }
+struct ScSortParam;
+struct ScQueryParam;
+class ScDBData;
+class ScDPObject;
+class ScDPSaveData;
+struct ScDPNumGroupInfo;
+struct ScSubTotalParam;
+
+class SAL_DLLPUBLIC_RTTI ScDBFunc : public ScViewFunc
+{
+private:
+ void GetSelectedMemberList(ScDPUniqueStringSet& rEntries, tools::Long& rDimension);
+ static void ModifiedAutoFilter(ScDocShell* pDocSh);
+ static void ApplyAutoFilter(ScDocShell* pDocSh, ScViewData* pViewData, ScDBData* pDBData,
+ SCCOL nCol, SCROW nRow, SCTAB nTab, ScQueryParam aParam);
+
+ DECL_STATIC_LINK(ScDBFunc, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*);
+
+public:
+ ScDBFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell );
+ virtual ~ScDBFunc();
+
+ // only UISort repeat the partial results if necessary
+
+ void UISort( const ScSortParam& rSortParam );
+
+ void Sort( const ScSortParam& rSortParam,
+ bool bRecord = true, bool bPaint = true );
+ SC_DLLPUBLIC void Query( const ScQueryParam& rQueryParam,
+ const ScRange* pAdvSource, bool bRecord );
+ void DoSubTotals( const ScSubTotalParam& rParam, bool bRecord = true,
+ const ScSortParam* pForceNewSort = nullptr );
+
+ void ToggleAutoFilter();
+ void HideAutoFilter();
+
+ void RepeatDB( bool bRecord = true );
+
+ bool ImportData( const ScImportParam& rParam );
+
+ void GotoDBArea( const OUString& rDBName );
+
+ // DB range from Cursor
+ ScDBData* GetDBData( bool bMarkArea = true, ScGetDBMode eMode = SC_DB_MAKE, ScGetDBSelection eSel = ScGetDBSelection::Keep);
+ ScDBData* GetAnonymousDBData();
+
+ void Consolidate( const ScConsolidateParam& rParam );
+
+ bool MakePivotTable(
+ const ScDPSaveData& rData, const ScRange& rDest, bool bNewTable,
+ const ScDPObject& rSource );
+
+ void DeletePivotTable();
+ void RecalcPivotTable();
+ bool HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts );
+ bool HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo );
+ void GroupDataPilot();
+ void DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts );
+ void NumGroupDataPilot( const ScDPNumGroupInfo& rInfo );
+ void UngroupDataPilot();
+ void DataPilotInput( const ScAddress& rPos, const OUString& rString );
+
+ void DataPilotSort(ScDPObject* pDPObject, tools::Long nDimIndex, bool bAscending, const sal_uInt16* pUserListId = nullptr);
+ bool DataPilotMove( const ScRange& rSource, const ScAddress& rDest );
+
+ bool HasSelectionForDrillDown( css::sheet::DataPilotFieldOrientation& rOrientation );
+ void SetDataPilotDetails(bool bShow, const OUString* pNewDimensionName = nullptr);
+
+ void ShowDataPilotSourceData( ScDPObject& rDPObj,
+ const css::uno::Sequence< css::sheet::DataPilotFieldFilter >& rFilters );
+
+ void MakeOutline( bool bColumns, bool bRecord = true );
+ void RemoveOutline( bool bColumns, bool bRecord = true );
+ void RemoveAllOutlines( bool bRecord = true );
+ void TestRemoveOutline( bool& rCol, bool& rRow );
+
+ void AutoOutline();
+
+ void SelectLevel( bool bColumns, sal_uInt16 nLevel,
+ bool bRecord = true );
+ void SetOutlineState( bool bColumn, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bHidden);
+ void ShowOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry,
+ bool bRecord = true, bool bPaint = true );
+ void HideOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry,
+ bool bRecord = true, bool bPaint = true );
+
+ void ShowMarkedOutlines( bool bRecord = true );
+ void HideMarkedOutlines( bool bRecord = true );
+ bool OutlinePossible(bool bHide);
+
+ void UpdateCharts(bool bAllCharts); // Default: at the Cursor
+
+ static sal_uInt16 DoUpdateCharts( const ScAddress& rPos, ScDocument& rDoc, bool bAllCharts );
+
+ void OnLOKShowHideColRow(bool bColumns, SCCOLROW nStartRow);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dbnamdlg.hxx b/sc/source/ui/inc/dbnamdlg.hxx
new file mode 100644
index 0000000000..f175c7ac53
--- /dev/null
+++ b/sc/source/ui/inc/dbnamdlg.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 .
+ */
+
+#pragma once
+
+#include <vector>
+#include "anyrefdg.hxx"
+#include <dbdata.hxx>
+
+class ScViewData;
+class ScDocument;
+
+class ScDbNameDlg : public ScAnyRefDlgController
+{
+public:
+ ScDbNameDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData);
+ virtual ~ScDbNameDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ bool bSaved;
+ bool bInvalid;
+
+ OUString aStrAdd;
+ OUString aStrModify;
+ OUString aStrInvalid;
+
+ OUString aStrSource;
+ OUString aStrOperations;
+
+ ScViewData& m_rViewData;
+ const ScDocument& rDoc;
+ bool bRefInputMode;
+ ScAddress::Details aAddrDetails;
+
+ ScDBCollection aLocalDbCol;
+ ScRange theCurArea;
+ std::vector<ScRange> aRemoveList;
+
+ std::unique_ptr<weld::EntryTreeView> m_xEdName;
+
+ std::unique_ptr<weld::Frame> m_xAssignFrame;
+ std::unique_ptr<formula::RefEdit> m_xEdAssign;
+ std::unique_ptr<formula::RefButton> m_xRbAssign;
+
+ std::unique_ptr<weld::Widget> m_xOptions;
+ std::unique_ptr<weld::CheckButton> m_xBtnHeader;
+ std::unique_ptr<weld::CheckButton> m_xBtnTotals;
+ std::unique_ptr<weld::CheckButton> m_xBtnDoSize;
+ std::unique_ptr<weld::CheckButton> m_xBtnKeepFmt;
+ std::unique_ptr<weld::CheckButton> m_xBtnStripData;
+ std::unique_ptr<weld::Label> m_xFTSource;
+ std::unique_ptr<weld::Label> m_xFTOperations;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnRemove;
+
+ std::unique_ptr<weld::Button> m_xModifyPB;
+ std::unique_ptr<weld::Label> m_xInvalidFT;
+
+ std::unique_ptr<weld::Label> m_xFrameLabel;
+private:
+ void Init();
+ void UpdateNames();
+ void UpdateDBData( const OUString& rStrName );
+ void SetInfoStrings( const ScDBData* pDBData );
+
+ DECL_LINK( CancelBtnHdl, weld::Button&, void );
+ DECL_LINK( OkBtnHdl, weld::Button&, void );
+ DECL_LINK( AddBtnHdl, weld::Button&, void );
+ DECL_LINK( RemoveBtnHdl, weld::Button&, void );
+ DECL_LINK( NameModifyHdl, weld::ComboBox&, void );
+ DECL_LINK( AssModifyHdl, formula::RefEdit&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/delcldlg.hxx b/sc/source/ui/inc/delcldlg.hxx
new file mode 100644
index 0000000000..4537b3df27
--- /dev/null
+++ b/sc/source/ui/inc/delcldlg.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+#include <global.hxx>
+
+class ScDeleteCellDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::RadioButton> m_xBtnCellsUp;
+ std::unique_ptr<weld::RadioButton> m_xBtnCellsLeft;
+ std::unique_ptr<weld::RadioButton> m_xBtnDelRows;
+ std::unique_ptr<weld::RadioButton> m_xBtnDelCols;
+
+public:
+ ScDeleteCellDlg(weld::Window* pParent, bool bDisallowCellMove);
+ virtual ~ScDeleteCellDlg() override;
+
+ DelCellCmd GetDelCellCmd() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/delcodlg.hxx b/sc/source/ui/inc/delcodlg.hxx
new file mode 100644
index 0000000000..7c49a79f9d
--- /dev/null
+++ b/sc/source/ui/inc/delcodlg.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <global.hxx>
+
+class ScDeleteContentsDlg : public weld::GenericDialogController
+{
+private:
+ bool m_bObjectsDisabled;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnDelAll;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelStrings;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelNumbers;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelDateTime;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelFormulas;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelNotes;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelAttrs;
+ std::unique_ptr<weld::CheckButton> m_xBtnDelObjects;
+
+ static bool bPreviousAllCheck;
+ static InsertDeleteFlags nPreviousChecks;
+
+ void DisableChecks(bool bDelAllChecked);
+ DECL_LINK(DelAllHdl, weld::Toggleable&, void);
+
+public:
+ ScDeleteContentsDlg(weld::Window* pParent);
+ virtual ~ScDeleteContentsDlg() override;
+ void DisableObjects();
+
+ InsertDeleteFlags GetDelContentsCmdBits() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/docfunc.hxx b/sc/source/ui/inc/docfunc.hxx
new file mode 100644
index 0000000000..aa9755566e
--- /dev/null
+++ b/sc/source/ui/inc/docfunc.hxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/solar.h>
+#include <global.hxx>
+#include <formula/grammar.hxx>
+#include <tabbgcolor.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <memory>
+#include <vector>
+#include <map>
+
+class GenerateNoteCaption;
+class ScEditEngineDefaulter;
+class SdrUndoAction;
+class ScAddress;
+class ScDocShell;
+class ScMarkData;
+class ScPatternAttr;
+class ScRange;
+class ScRangeList;
+class ScFormulaCell;
+class ScTokenArray;
+struct ScTabOpParam;
+class ScTableProtection;
+class ScDocProtection;
+struct ScCellMergeOption;
+class ScConditionalFormat;
+class ScConditionalFormatList;
+class ScUndoRemoveMerge;
+class ScRangeName;
+
+enum class TransliterationFlags;
+enum class CreateNameFlags;
+namespace sc
+{
+ struct ColRowSpan;
+ class SparklineAttributes;
+ class SparklineGroup;
+ class Sparkline;
+}
+namespace tools
+{
+ class Rectangle;
+}
+
+class ScDocFunc
+{
+ ScDocShell& rDocShell;
+
+protected:
+ bool AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi );
+ void CreateOneName( ScRangeName& rList,
+ SCCOL nPosX, SCROW nPosY, SCTAB nTab,
+ SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ bool& rCancel, bool bApi );
+ void NotifyInputHandler( const ScAddress& rPos );
+
+ ScDocFunc( ScDocShell& rDocSh ): rDocShell(rDocSh) {}
+public:
+ virtual ~ScDocFunc() {}
+
+ void NotifyDrawUndo(std::unique_ptr<SdrUndoAction>);
+
+ // for grouping multiple operations into one with a new name
+ void EnterListAction(TranslateId pNameResId);
+ void EndListAction();
+
+ bool DetectiveAddPred(const ScAddress& rPos);
+ bool DetectiveDelPred(const ScAddress& rPos);
+ bool DetectiveAddSucc(const ScAddress& rPos);
+ bool DetectiveDelSucc(const ScAddress& rPos);
+ bool DetectiveAddError(const ScAddress& rPos);
+ bool DetectiveMarkInvalid(SCTAB nTab);
+ bool DetectiveDelAll(SCTAB nTab);
+ bool DetectiveRefresh(bool bAutomatic = false);
+ void DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, ::std::vector<ScTokenRef>& rRefTokens);
+ void DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, ::std::vector<ScTokenRef>& rRefTokens);
+
+ SC_DLLPUBLIC bool DeleteContents(
+ const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi );
+
+ bool DeleteCell(
+ const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi );
+
+ bool TransliterateText( const ScMarkData& rMark, TransliterationFlags nType,
+ bool bApi );
+
+ bool SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi );
+ bool SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction );
+ void SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction );
+ bool SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction );
+ bool SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction );
+
+ bool SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction );
+
+ /**
+ * Below two methods take ownership of the formula cell instance(s). The caller
+ * must not delete it after passing it to this call.
+ */
+ bool SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction );
+ bool SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction );
+ void PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi );
+ bool SetCellText(
+ const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
+ const formula::FormulaGrammar::Grammar eGrammar );
+
+ SC_DLLPUBLIC bool ShowNote( const ScAddress& rPos, bool bShow );
+
+ void SetNoteText( const ScAddress& rPos, const OUString& rNoteText, bool bApi );
+ void ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi );
+ SC_DLLPUBLIC void ImportNote( const ScAddress& rPos,
+ std::unique_ptr<GenerateNoteCaption> xGenerator,
+ const tools::Rectangle& rCaptionRect, bool bShown );
+
+ bool ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern,
+ bool bApi );
+ bool ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName,
+ bool bApi );
+
+ bool InsertCells( const ScRange& rRange,const ScMarkData* pTabMark,
+ InsCellCmd eCmd, bool bRecord, bool bApi, bool bPartOfPaste = false );
+
+ bool DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark,
+ DelCellCmd eCmd, bool bApi );
+
+ bool MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
+ bool bCut, bool bRecord, bool bPaint, bool bApi );
+
+ SC_DLLPUBLIC bool InsertTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi );
+ SC_DLLPUBLIC bool RenameTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi );
+ bool DeleteTable( SCTAB nTab, bool bRecord );
+
+ bool SetTabBgColor( SCTAB nTab, const Color& rColor, bool bRecord, bool bApi );
+ bool SetTabBgColor( ScUndoTabColorInfo::List& rUndoTabColorList, bool bApi );
+
+ void SetTableVisible( SCTAB nTab, bool bVisible, bool bApi );
+
+ bool SetLayoutRTL( SCTAB nTab, bool bRTL );
+
+ SC_DLLPUBLIC bool SetWidthOrHeight(
+ bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, SCTAB nTab,
+ ScSizeMode eMode, sal_uInt16 nSizeTwips, bool bRecord, bool bApi );
+
+ bool InsertPageBreak( bool bColumn, const ScAddress& rPos,
+ bool bRecord, bool bSetModified );
+ bool RemovePageBreak( bool bColumn, const ScAddress& rPos,
+ bool bRecord, bool bSetModified );
+
+ void ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect );
+
+ bool Protect( SCTAB nTab, const OUString& rPassword );
+ bool Unprotect( SCTAB nTab, const OUString& rPassword, bool bApi );
+
+ void ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, bool bApi );
+ bool ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bApi );
+ bool AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark,
+ sal_uInt16 nFormatNo, bool bApi );
+
+ SC_DLLPUBLIC bool
+ EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark,
+ const ScTokenArray* pTokenArray,
+ const OUString& rString, bool bApi, bool bEnglish,
+ const OUString& rFormulaNmsp,
+ const formula::FormulaGrammar::Grammar );
+
+ bool TabOp( const ScRange& rRange, const ScMarkData* pTabMark,
+ const ScTabOpParam& rParam, bool bRecord, bool bApi );
+
+ bool FillSimple( const ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, bool bApi );
+ bool FillSeries( const ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
+ double fStart, double fStep, double fMax,
+ bool bApi );
+
+ // FillAuto: rRange is change from Source-Range to Dest-Range
+ SC_DLLPUBLIC bool
+ FillAuto( ScRange& rRange, const ScMarkData* pTabMark, FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd, sal_uLong nCount, double fStep, double fMax, bool bRecord, bool bApi );
+
+ bool FillAuto( ScRange& rRange, const ScMarkData* pTabMark,
+ FillDir eDir, sal_uLong nCount, bool bApi );
+
+ void ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd );
+
+ SC_DLLPUBLIC bool
+ MergeCells( const ScCellMergeOption& rOption, bool bContents,
+ bool bRecord, bool bApi, bool bEmptyMergedCells = false );
+ bool UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge );
+ SC_DLLPUBLIC bool
+ UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge );
+
+ // takes ownership of pNewRanges, nTab = -1 for local range names
+ void SetNewRangeNames( std::unique_ptr<ScRangeName> pNewRanges, bool bModifyDoc, SCTAB nTab );
+ void ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab = -1 );
+ /**
+ * Modify all range names, global scope names as well as sheet local ones,
+ * in one go. Note that this method will <b>not</b> destroy the instances
+ * passed as arguments (it creates copies); the caller is responsible for
+ * destroying them.
+ */
+ void ModifyAllRangeNames(const std::map<OUString, ScRangeName>& rRangeMap);
+
+ bool CreateNames( const ScRange& rRange, CreateNameFlags nFlags, bool bApi, SCTAB nTab = -1 ); // -1 for global range names
+ bool InsertNameList( const ScAddress& rStartPos, bool bApi );
+
+ void InsertAreaLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, const OUString& rSource,
+ const ScRange& rDestRange, sal_Int32 nRefreshDelaySeconds,
+ bool bFitBlock, bool bApi );
+
+ /**
+ * @param nOldIndex If 0 don't delete an old format
+ * @param pFormat if NULL only delete an old format
+ */
+ void ReplaceConditionalFormat( sal_uLong nOldIndex, std::unique_ptr<ScConditionalFormat> pFormat, SCTAB nTab, const ScRangeList& rRanges );
+
+ /**
+ * Sets or replaces the conditional format list of a table
+ *
+ * @param pList the new ScConditionalFormatList, method takes ownership
+ * @param nTab the tab to which the conditional format list belongs
+ */
+ void SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab );
+
+ void ConvertFormulaToValue( const ScRange& rRange, bool bInteraction );
+
+ SC_DLLPUBLIC bool InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup);
+
+ SC_DLLPUBLIC bool DeleteSparkline(ScAddress const& rAddress);
+ SC_DLLPUBLIC bool DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup, SCTAB nTab);
+ SC_DLLPUBLIC bool ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGroup> const& pExistingSparklineGroup,
+ sc::SparklineAttributes const& rNewAttributes);
+ SC_DLLPUBLIC bool GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup> const& rpGroup);
+ SC_DLLPUBLIC bool UngroupSparklines(ScRange const& rRange);
+ SC_DLLPUBLIC bool ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange);
+
+private:
+ void ProtectDocument(const ScDocProtection& rProtect);
+};
+
+class ScDocFuncDirect final : public ScDocFunc
+{
+public:
+ ScDocFuncDirect( ScDocShell& rDocSh ) : ScDocFunc( rDocSh ) {}
+};
+
+void VBA_DeleteModule( ScDocShell& rDocSh, const OUString& sModuleName );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/docfuncutil.hxx b/sc/source/ui/inc/docfuncutil.hxx
new file mode 100644
index 0000000000..c0b439d3e9
--- /dev/null
+++ b/sc/source/ui/inc/docfuncutil.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "undobase.hxx"
+
+#include <memory>
+
+class ScMarkData;
+class ScRange;
+enum class InsertDeleteFlags : sal_uInt16;
+
+namespace sc {
+
+class DocFuncUtil
+{
+public:
+
+ static bool hasProtectedTab( const ScDocument& rDoc, const ScMarkData& rMark );
+
+ static ScDocumentUniquePtr createDeleteContentsUndoDoc(
+ ScDocument& rDoc, const ScMarkData& rMark, const ScRange& rRange,
+ InsertDeleteFlags nFlags, bool bOnlyMarked );
+
+ static void addDeleteContentsUndo(
+ SfxUndoManager* pUndoMgr, ScDocShell* pDocSh, const ScMarkData& rMark,
+ const ScRange& rRange, ScDocumentUniquePtr&& pUndoDoc, InsertDeleteFlags nFlags,
+ const std::shared_ptr<ScSimpleUndo::DataSpansType>& pSpans,
+ bool bMulti, bool bDrawUndo );
+
+ static std::shared_ptr<ScSimpleUndo::DataSpansType> getNonEmptyCellSpans(
+ const ScDocument& rDoc, const ScMarkData& rMark, const ScRange& rRange );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/docsh.hxx b/sc/source/ui/inc/docsh.hxx
new file mode 100644
index 0000000000..eb9a3450ad
--- /dev/null
+++ b/sc/source/ui/inc/docsh.hxx
@@ -0,0 +1,524 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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>
+#include <sfx2/docfac.hxx>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <sfx2/viewsh.hxx>
+#include <o3tl/deleter.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <scdllapi.h>
+#include <document.hxx>
+#include <shellids.hxx>
+#include <optutil.hxx>
+#include <docuno.hxx>
+
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+#include <map>
+
+class ScRefreshTimerProtector;
+class ScEditEngineDefaulter;
+class SfxStyleSheetBasePool;
+class SfxStyleSheetHint;
+class INetURLObject;
+
+class ScViewData;
+class ScDocFunc;
+class ScDrawLayer;
+class ScTabViewShell;
+class ScAutoStyleList;
+class ScMarkData;
+class ScPaintLockData;
+class ScChangeAction;
+class ScImportOptions;
+class ScDocShellModificator;
+class ScSheetSaveData;
+class ScFlatBoolRowSegments;
+struct ScColWidthParam;
+class ScFormulaOptions;
+namespace com::sun::star::script::vba { class XVBAScriptListener; }
+namespace ooo::vba::excel { class XWorkbook; }
+namespace com::sun::star::datatransfer { class XTransferable2; }
+namespace sfx2 { class FileDialogHelper; }
+struct DocShell_Impl;
+
+typedef std::unordered_map< sal_uLong, sal_uLong > ScChangeActionMergeMap;
+
+//enum ScDBFormat { SC_FORMAT_SDF, SC_FORMAT_DBF };
+
+enum class LOKCommentNotificationType { Add, Modify, Remove };
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDBF(SvStream &rStream);
+
+ // Extra flags for Repaint
+#define SC_PF_LINES 1
+#define SC_PF_TESTMERGE 2
+#define SC_PF_WHOLEROWS 4
+
+class SC_DLLPUBLIC ScDocShell final: public SfxObjectShell, public SfxListener
+{
+ std::shared_ptr<ScDocument> m_pDocument;
+
+ OUString m_aDdeTextFmt;
+
+ double m_nPrtToScreenFactor;
+ std::unique_ptr<DocShell_Impl> m_pImpl;
+ std::unique_ptr<ScDocFunc> m_pDocFunc;
+
+ bool m_bHeaderOn;
+ bool m_bFooterOn;
+ bool m_bIsInplace:1; // Is set by the View
+ bool m_bIsEmpty:1;
+ bool m_bIsInUndo:1;
+ bool m_bDocumentModifiedPending:1;
+ bool m_bUpdateEnabled:1;
+ bool m_bAreasChangedNeedBroadcast:1;
+ sal_uInt16 m_nDocumentLock;
+ sal_Int16 m_nCanUpdate; // stores the UpdateDocMode from loading a document till update links
+
+ std::unique_ptr<ScDBData> m_pOldAutoDBRange;
+
+ std::unique_ptr<ScAutoStyleList> m_pAutoStyleList;
+ std::unique_ptr<ScPaintLockData> m_pPaintLockData;
+ std::unique_ptr<ScSheetSaveData> m_pSheetSaveData;
+ std::unique_ptr<ScFormatSaveData> m_pFormatSaveData;
+
+ std::unique_ptr<ScDocShellModificator, o3tl::default_delete<ScDocShellModificator>> m_pModificator; // #109979#; is used to load XML (created in BeforeXMLLoading and destroyed in AfterXMLLoading)
+
+ css::uno::Reference< ooo::vba::excel::XWorkbook> mxAutomationWorkbookObject;
+
+ // Only used by Vba helper functions
+ css::uno::Reference<css::script::vba::XVBAScriptListener> m_xVBAListener;
+ css::uno::Reference<css::datatransfer::XTransferable2> m_xClipData;
+
+ SAL_DLLPRIVATE void InitItems();
+ SAL_DLLPRIVATE void DoEnterHandler();
+ SAL_DLLPRIVATE void InitOptions(bool bForLoading);
+ SAL_DLLPRIVATE void ResetDrawObjectShell();
+
+ /** Do things that need to be done before saving to our own format and
+ necessary clean ups in dtor. */
+ class SAL_DLLPRIVATE PrepareSaveGuard
+ {
+ public:
+ explicit PrepareSaveGuard( ScDocShell & rDocShell );
+ ~PrepareSaveGuard() COVERITY_NOEXCEPT_FALSE;
+ private:
+ ScDocShell & mrDocShell;
+ };
+
+ SAL_DLLPRIVATE bool LoadXML( SfxMedium* pMedium, const css::uno::Reference< css::embed::XStorage >& );
+ SAL_DLLPRIVATE bool SaveXML( SfxMedium* pMedium, const css::uno::Reference< css::embed::XStorage >& );
+ SAL_DLLPRIVATE SCTAB GetSaveTab();
+
+ friend bool TestImportDBF(SvStream &rStream);
+
+ SAL_DLLPRIVATE ErrCode DBaseImport( const OUString& rFullFileName, rtl_TextEncoding eCharSet,
+ std::map<SCCOL, ScColWidthParam>& aColWidthParam, ScFlatBoolRowSegments& rRowHeightsRecalc );
+ SAL_DLLPRIVATE ErrCodeMsg DBaseExport(
+ const OUString& rFullFileName, rtl_TextEncoding eCharSet, bool& bHasMemo );
+
+ SAL_DLLPRIVATE static bool MoveFile( const INetURLObject& rSource, const INetURLObject& rDest );
+ SAL_DLLPRIVATE static bool KillFile( const INetURLObject& rURL );
+ SAL_DLLPRIVATE static bool IsDocument( const INetURLObject& rURL );
+
+ SAL_DLLPRIVATE void LockPaint_Impl(bool bDoc);
+ SAL_DLLPRIVATE void UnlockPaint_Impl(bool bDoc);
+ SAL_DLLPRIVATE void LockDocument_Impl(sal_uInt16 nNew);
+ SAL_DLLPRIVATE void UnlockDocument_Impl(sal_uInt16 nNew);
+
+ SAL_DLLPRIVATE void EnableSharedSettings( bool bEnable );
+ SAL_DLLPRIVATE css::uno::Reference< css::frame::XModel > LoadSharedDocument();
+
+ SAL_DLLPRIVATE void UseSheetSaveEntries();
+
+ SAL_DLLPRIVATE std::unique_ptr<ScDocFunc> CreateDocFunc();
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+public:
+ SFX_DECL_INTERFACE(SCID_DOC_SHELL)
+ SFX_DECL_OBJECTFACTORY();
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ explicit ScDocShell( const ScDocShell& rDocShell ) = delete;
+ explicit ScDocShell( const SfxModelFlags i_nSfxCreationFlags = SfxModelFlags::EMBEDDED_OBJECT, const std::shared_ptr<ScDocument>& pDoc = {} );
+ virtual ~ScDocShell() override;
+
+ virtual SfxUndoManager*
+ GetUndoManager() override;
+
+ virtual void FillClass( SvGlobalName * pClassName,
+ SotClipboardFormatId * pFormat,
+ OUString * pFullTypeName,
+ sal_Int32 nFileFormat,
+ bool bTemplate = false ) const override;
+
+ virtual std::set<Color> GetDocColors() override;
+ virtual std::shared_ptr<model::ColorSet> GetThemeColors() override;
+
+ virtual bool InitNew( const css::uno::Reference< css::embed::XStorage >& ) override;
+ virtual bool Load( SfxMedium& rMedium ) override;
+ virtual bool LoadFrom( SfxMedium& rMedium ) override;
+ virtual bool ConvertFrom( SfxMedium &rMedium ) override;
+ virtual bool LoadExternal( SfxMedium& rMedium ) override;
+ virtual bool Save() override;
+ virtual bool SaveAs( SfxMedium& rMedium ) override;
+ virtual bool ConvertTo( SfxMedium &rMedium ) override;
+ virtual bool PrepareClose( bool bUI = true ) override;
+ virtual void LoadStyles( SfxObjectShell &rSource ) override;
+
+ virtual bool DoSaveCompleted( SfxMedium * pNewStor=nullptr, bool bRegisterRecent=true ) override; // SfxObjectShell
+ virtual bool QuerySlotExecutable( sal_uInt16 nSlotId ) override;
+
+ virtual void Draw(OutputDevice *, const JobSetup & rSetup, sal_uInt16 nAspect, bool bOutputForScreen) override;
+
+ virtual void SetVisArea( const tools::Rectangle & rVisArea ) override;
+
+ virtual void TerminateEditing() override;
+
+ using SfxObjectShell::GetVisArea;
+ virtual tools::Rectangle GetVisArea( sal_uInt16 nAspect ) const override;
+
+ virtual Printer* GetDocumentPrinter() override;
+
+ virtual void SetModified( bool = true ) override;
+
+ void SetVisAreaOrSize( const tools::Rectangle& rVisArea );
+
+ virtual std::shared_ptr<SfxDocumentInfoDialog> CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet &rSet) override;
+
+ void GetDocStat( ScDocStat& rDocStat );
+
+ const ScDocument& GetDocument() const { return *m_pDocument; }
+ ScDocument& GetDocument() { return *m_pDocument; }
+ ScDocFunc& GetDocFunc() { return *m_pDocFunc; }
+
+ css::uno::Reference<css::datatransfer::XTransferable2> const & GetClipData() const { return m_xClipData; }
+ void SetClipData(const css::uno::Reference<css::datatransfer::XTransferable2>& xTransferable) { m_xClipData = xTransferable; }
+
+ SfxPrinter* GetPrinter( bool bCreateIfNotExist = true );
+ sal_uInt16 SetPrinter( VclPtr<SfxPrinter> const & pNewPrinter, SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL );
+
+ void UpdateFontList();
+
+ ScDrawLayer* MakeDrawLayer();
+
+ void AsciiSave( SvStream& rStream, const ScImportOptions& rOpt, SCTAB nTab );
+
+ void Execute( SfxRequest& rReq );
+ void GetState( SfxItemSet &rSet );
+ void ExecutePageStyle ( const SfxViewShell& rCaller, SfxRequest& rReq, SCTAB nCurTab );
+ void GetStatePageStyle( SfxItemSet& rSet, SCTAB nCurTab );
+
+ void CompareDocument( ScDocument& rOtherDoc );
+ void MergeDocument( ScDocument& rOtherDoc, bool bShared = false, bool bCheckDuplicates = false, sal_uLong nOffset = 0, ScChangeActionMergeMap* pMergeMap = nullptr, bool bInverseMap = false );
+ bool MergeSharedDocument( ScDocShell* pSharedDocShell );
+
+ ScChangeAction* GetChangeAction( const ScAddress& rPos );
+ void SetChangeComment( ScChangeAction* pAction, const OUString& rComment );
+ void ExecuteChangeCommentDialog( ScChangeAction* pAction, weld::Window* pParent, bool bPrevNext = true );
+ /// Protect/unprotect ChangeTrack and return <TRUE/> if
+ /// protection was successfully changed.
+ /// If bJustQueryIfProtected==sal_True protection is not
+ /// changed and <TRUE/> is returned if not protected or
+ /// password was entered correctly.
+ bool ExecuteChangeProtectionDialog( bool bJustQueryIfProtected = false );
+
+ void SetPrintZoom( SCTAB nTab, sal_uInt16 nScale, sal_uInt16 nPages );
+ bool AdjustPrintZoom( const ScRange& rRange );
+
+ void LoadStylesArgs( ScDocShell& rSource, bool bReplace, bool bCellStyles, bool bPageStyles );
+
+ void PageStyleModified( std::u16string_view rStyleName, bool bApi );
+
+ void NotifyStyle( const SfxStyleSheetHint& rHint );
+ void DoAutoStyle( const ScRange& rRange, const OUString& rStyle );
+
+ static weld::Window* GetActiveDialogParent();
+ void ErrorMessage(TranslateId pGlobStrId);
+ bool IsEditable() const;
+
+ bool AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab );
+ void UpdateAllRowHeights( const ScMarkData* pTabMark = nullptr );
+ void UpdateAllRowHeights(const bool bOnlyUsedRows);
+ void UpdatePendingRowHeights( SCTAB nUpdateTab, bool bBefore = false );
+
+ void RefreshPivotTables( const ScRange& rSource );
+ void DoConsolidate( const ScConsolidateParam& rParam, bool bRecord = true );
+ void UseScenario( SCTAB nTab, const OUString& rName, bool bRecord = true );
+ SCTAB MakeScenario(SCTAB nTab, const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags,
+ ScMarkData& rMark, bool bRecord = true);
+ void ModifyScenario(SCTAB nTab, const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags);
+ sal_uLong TransferTab( ScDocShell& rSrcDocShell, SCTAB nSrcPos,
+ SCTAB nDestPos, bool bInsertNew,
+ bool bNotifyAndPaint );
+
+ bool MoveTable( SCTAB nSrcTab, SCTAB nDestTab, bool bCopy, bool bRecord );
+
+ void DoRecalc( bool bApi );
+ void DoHardRecalc();
+
+ void UpdateOle(const ScViewData& rViewData, bool bSnapSize = false);
+ bool IsOle() const;
+
+ void DBAreaDeleted( SCTAB nTab, SCCOL nX1, SCROW nY1, SCCOL nX2 );
+ ScDBData* GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGetDBSelection eSel );
+ ScDBData* GetAnonymousDBData(const ScRange& rRange);
+ std::unique_ptr<ScDBData> GetOldAutoDBRange();
+ void CancelAutoDBRange(); // called when dialog is cancelled
+
+ virtual void ReconnectDdeLink(SfxObjectShell& rServer) override;
+ void UpdateLinks() override;
+ void SetInitialLinkUpdate( const SfxMedium* pMedium );
+ void AllowLinkUpdate();
+ void ReloadAllLinks();
+ void ReloadTabLinks();
+ ScLkUpdMode GetLinkUpdateModeState() const;
+
+ void SetFormulaOptions( const ScFormulaOptions& rOpt, bool bForLoading = false );
+ /**
+ * Called when the Options dialog is dismissed with the OK button, to
+ * handle potentially conflicting option settings.
+ */
+ void CheckConfigOptions();
+
+ void PostEditView( ScEditEngineDefaulter* pEditEngine, const ScAddress& rCursorPos );
+
+ void PostPaint( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, PaintPartFlags nPart,
+ sal_uInt16 nExtFlags = 0 );
+ void PostPaint( const ScRangeList& rRanges, PaintPartFlags nPart, sal_uInt16 nExtFlags = 0 );
+
+ void PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab );
+ void PostPaintCell( const ScAddress& rPos );
+ void PostPaintGridAll();
+ void PostPaintExtras();
+
+ bool IsPaintLocked() const { return m_pPaintLockData != nullptr; }
+
+ void PostDataChanged();
+
+ void UpdatePaintExt( sal_uInt16& rExtFlags, SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab );
+ void UpdatePaintExt( sal_uInt16& rExtFlags, const ScRange& rRange );
+
+ void SetDocumentModified();
+ void SetDrawModified();
+
+ void LockPaint();
+ void UnlockPaint();
+ sal_uInt16 GetLockCount() const { return m_nDocumentLock;}
+ void SetLockCount(sal_uInt16 nNew);
+
+ void LockDocument();
+ void UnlockDocument();
+
+ DECL_DLLPRIVATE_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void );
+ DECL_DLLPRIVATE_LINK( ReloadAllLinksHdl, weld::Button&, void );
+
+ virtual SfxStyleSheetBasePool* GetStyleSheetPool() override;
+
+ void SetInplace( bool bInplace );
+ bool IsEmpty() const { return m_bIsEmpty; }
+ void SetEmpty(bool bSet);
+
+ bool IsInUndo() const { return m_bIsInUndo; }
+ void SetInUndo(bool bSet);
+
+ void CalcOutputFactor();
+ double GetOutputFactor() const { return m_nPrtToScreenFactor;}
+ void GetPageOnFromPageStyleSet( const SfxItemSet* pStyleSet,
+ SCTAB nCurTab,
+ bool& rbHeader,
+ bool& rbFooter );
+
+#if defined(_WIN32)
+ virtual bool DdeGetData( const OUString& rItem, const OUString& rMimeType,
+ css::uno::Any & rValue ) override;
+ virtual bool DdeSetData( const OUString& rItem, const OUString& rMimeType,
+ const css::uno::Any & rValue ) override;
+#endif
+
+ virtual ::sfx2::SvLinkSource* DdeCreateLinkSource( const OUString& rItem ) override;
+
+ const OUString& GetDdeTextFmt() const { return m_aDdeTextFmt; }
+
+ SfxBindings* GetViewBindings();
+
+ ScTabViewShell* GetBestViewShell( bool bOnlyVisible = true );
+
+ void SetDocumentModifiedPending( bool bVal )
+ { m_bDocumentModifiedPending = bVal; }
+ bool IsDocumentModifiedPending() const
+ { return m_bDocumentModifiedPending; }
+
+ bool IsUpdateEnabled() const
+ { return m_bUpdateEnabled; }
+ void SetUpdateEnabled(bool bValue)
+ { m_bUpdateEnabled = bValue; }
+
+ void SetAreasChangedNeedBroadcast()
+ { m_bAreasChangedNeedBroadcast = true; }
+
+ OutputDevice* GetRefDevice(); // WYSIWYG: Printer, otherwise VirtualDevice...
+
+ static ScViewData* GetViewData();
+ static SCTAB GetCurTab();
+
+ static ScDocShell* GetShellByNum( sal_uInt16 nDocNo );
+ static OUString GetOwnFilterName();
+ static OUString GetHtmlFilterName();
+ static OUString GetWebQueryFilterName();
+ static OUString GetAsciiFilterName();
+ static OUString GetLotusFilterName();
+ static OUString GetDBaseFilterName();
+ static OUString GetDifFilterName();
+ static bool HasAutomaticTableName( std::u16string_view rFilter );
+ static void LOKCommentNotify(LOKCommentNotificationType nType, const ScDocument& rDocument, const ScAddress& rPos, const ScPostIt* pNote);
+
+ DECL_DLLPRIVATE_LINK( RefreshDBDataHdl, Timer*, void );
+
+ void BeforeXMLLoading();
+ void AfterXMLLoading(bool bRet);
+
+ virtual HiddenInformation GetHiddenInformationState( HiddenInformation nStates ) override;
+
+ ScSheetSaveData* GetSheetSaveData();
+ ScFormatSaveData* GetFormatSaveData();
+
+ static void ResetKeyBindings( ScOptionsUtil::KeyBindingType eType );
+
+ // password protection for Calc (derived from SfxObjectShell)
+ // see also: FID_CHG_RECORD, SID_CHG_PROTECT
+ virtual bool IsChangeRecording() const override;
+ virtual bool HasChangeRecordProtection() const override;
+ virtual void SetChangeRecording( bool bActivate, bool bLockAllViews = false ) override;
+ virtual void SetProtectionPassword( const OUString &rPassword ) override;
+ virtual bool GetProtectionHash( /*out*/ css::uno::Sequence< sal_Int8 > &rPasswordHash ) override;
+
+ void SnapVisArea( tools::Rectangle& rRect ) const;
+
+ void RegisterAutomationWorkbookObject(css::uno::Reference< ooo::vba::excel::XWorkbook > const& xWorkbook);
+
+ ScModelObj* GetModel() const { return static_cast<ScModelObj*>(SfxObjectShell::GetModel().get()); }
+};
+
+void UpdateAcceptChangesDialog();
+
+typedef tools::SvRef<ScDocShell> ScDocShellRef;
+
+/** Create before modifications of the document and destroy thereafter.
+ Call SetDocumentModified() at an instance of this class instead of at
+ ScDocShell.
+
+ Remembers in the ctor ScDocument's AutoCalcShellDisabled and IdleDisabled,
+ switches them off and restores them in the dtor, AutoCalcShellDisabled
+ also before a ScDocShell::SetDocumentModified() call if necessary.
+ In the dtor, if ScDocShell's bDocumentModifiedPending is set and
+ bAutoCalcShellDisabled is not set, then ScDocShell::SetDocumentModified()
+ is called.
+
+ Several instances can be used in nested calls to ScDocFunc or ScDocShell
+ methods to avoid multiple modified status changes, only the last instance
+ destroyed calls ScDocShell::SetDocumentModified().
+ */
+class ScDocShellModificator
+{
+ ScDocShell& rDocShell;
+ std::unique_ptr<ScRefreshTimerProtector> mpProtector;
+ bool bAutoCalcShellDisabled;
+ bool bIdleEnabled;
+
+ ScDocShellModificator( const ScDocShellModificator& ) = delete;
+ ScDocShellModificator& operator=( const ScDocShellModificator& ) = delete;
+
+public:
+ explicit ScDocShellModificator( ScDocShell& );
+ ~ScDocShellModificator() COVERITY_NOEXCEPT_FALSE;
+ void SetDocumentModified();
+};
+
+//#i97876# Spreadsheet data changes are not notified
+namespace HelperNotifyChanges
+{
+ inline bool isDataAreaInvalidateType(std::u16string_view rType)
+ {
+ if (rType == u"delete-content")
+ return true;
+ if (rType == u"delete-rows")
+ return true;
+ if (rType == u"delete-columns")
+ return true;
+ if (rType == u"undo")
+ return true;
+ if (rType == u"redo")
+ return true;
+ if (rType == u"paste")
+ return true;
+ if (rType == u"note")
+ return true;
+
+ return false;
+ }
+
+ inline bool getMustPropagateChangesModel(ScModelObj* pModelObj)
+ {
+ return pModelObj && pModelObj->HasChangesListeners();
+ }
+
+ inline void Notify(ScModelObj &rModelObj, const ScRangeList &rChangeRanges,
+ const OUString &rType = OUString("cell-change"),
+ const css::uno::Sequence< css::beans::PropertyValue >& rProperties =
+ css::uno::Sequence< css::beans::PropertyValue >())
+ {
+ rModelObj.NotifyChanges(rType, rChangeRanges, rProperties);
+ }
+
+ inline void NotifyIfChangesListeners(const ScDocShell &rDocShell, const ScRange &rRange,
+ const OUString &rType = OUString("cell-change"))
+ {
+ ScModelObj* pModelObj = rDocShell.GetModel();
+ ScRangeList aChangeRanges(rRange);
+
+ if (getMustPropagateChangesModel(pModelObj))
+ Notify(*pModelObj, aChangeRanges, rType);
+ else if (pModelObj) // possibly need to invalidate getCellArea results
+ {
+ Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
+ ? OUString("data-area-invalidate") : OUString("data-area-extend"));
+ }
+ }
+};
+
+void VBA_InsertModule( ScDocument& rDoc, SCTAB nTab, const OUString& sModuleSource );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dpcontrol.hxx b/sc/source/ui/inc/dpcontrol.hxx
new file mode 100644
index 0000000000..a447331cd7
--- /dev/null
+++ b/sc/source/ui/inc/dpcontrol.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <tools/gen.hxx>
+#include <tools/fract.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/outdev.hxx>
+
+class StyleSettings;
+class ScDocument;
+
+/**
+ * This class takes care of physically drawing field button controls inside
+ * data pilot tables.
+ */
+class ScDPFieldButton
+{
+public:
+ ScDPFieldButton(OutputDevice* pOutDev, const StyleSettings* pStyle, const Fraction* pZoomY = nullptr,
+ ScDocument* pDoc = nullptr);
+ ~ScDPFieldButton();
+
+ void setText(const OUString& rText);
+ void setBoundingBox(const Point& rPos, const Size& rSize, bool bLayoutRTL);
+ void setDrawBaseButton(bool b);
+ void setDrawPopupButton(bool b);
+ void setDrawPopupButtonMulti(bool b);
+ void setDrawToggleButton(bool b, bool bCollapse, sal_Int32 nIndent);
+ void setHasHiddenMember(bool b);
+ void setPopupPressed(bool b);
+ void setPopupLeft(bool b);
+ void draw();
+
+ void getPopupBoundingBox(Point& rPos, Size& rSize) const;
+ void getToggleBoundingBox(Point& rPos, Size& rSize) const;
+
+private:
+ void drawPopupButton();
+ void drawToggleButton();
+
+private:
+ Point maPos;
+ Size maSize;
+ OUString maText;
+ Fraction maZoomY;
+ ScDocument* mpDoc;
+ VclPtr<OutputDevice> mpOutDev;
+ const StyleSettings* mpStyle;
+ sal_Int32 mnToggleIndent;
+ bool mbBaseButton;
+ bool mbPopupButton;
+ bool mbPopupButtonMulti;
+ bool mbToggleButton;
+ bool mbToggleCollapse;
+ bool mbHasHiddenMember;
+ bool mbPopupPressed;
+ bool mbPopupLeft;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dpgroupdlg.hxx b/sc/source/ui/inc/dpgroupdlg.hxx
new file mode 100644
index 0000000000..d3c390f460
--- /dev/null
+++ b/sc/source/ui/inc/dpgroupdlg.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <dpnumgroupinfo.hxx>
+
+class ScDoubleField;
+class SvtCalendarBox;
+
+class ScDPGroupEditHelper
+{
+public:
+ explicit ScDPGroupEditHelper(weld::RadioButton& rRbAuto,
+ weld::RadioButton& rRbMan,
+ weld::Widget& rEdValue);
+
+ bool IsAuto() const;
+ double GetValue() const;
+ void SetValue( bool bAuto, double fValue );
+
+protected:
+ ~ScDPGroupEditHelper() {}
+
+private:
+ virtual bool ImplGetValue( double& rfValue ) const = 0;
+ virtual void ImplSetValue( double fValue ) = 0;
+
+ DECL_LINK(ToggleHdl, weld::Toggleable&, void);
+
+private:
+ weld::RadioButton& mrRbAuto;
+ weld::RadioButton& mrRbMan;
+ weld::Widget& mrEdValue;
+};
+
+class ScDPNumGroupEditHelper : public ScDPGroupEditHelper
+{
+public:
+ explicit ScDPNumGroupEditHelper(weld::RadioButton& rRbAuto,
+ weld::RadioButton& rRbMan,
+ ScDoubleField& rEdValue);
+
+ virtual ~ScDPNumGroupEditHelper() {}
+private:
+ virtual bool ImplGetValue( double& rfValue ) const override;
+ virtual void ImplSetValue( double fValue ) override;
+
+private:
+ ScDoubleField& mrEdValue;
+};
+
+class ScDPDateGroupEditHelper : public ScDPGroupEditHelper
+{
+public:
+ explicit ScDPDateGroupEditHelper(weld::RadioButton& rRbAuto,
+ weld::RadioButton& rRbMan,
+ SvtCalendarBox& rEdValue,
+ const Date& rNullDate);
+
+ virtual ~ScDPDateGroupEditHelper() {}
+
+private:
+ virtual bool ImplGetValue( double& rfValue ) const override;
+ virtual void ImplSetValue( double fValue ) override;
+
+private:
+ SvtCalendarBox& mrEdValue;
+ Date maNullDate;
+};
+
+class ScDPNumGroupDlg : public weld::GenericDialogController
+{
+public:
+ explicit ScDPNumGroupDlg(weld::Window* pParent, const ScDPNumGroupInfo& rInfo);
+ virtual ~ScDPNumGroupDlg() override;
+ ScDPNumGroupInfo GetGroupInfo() const;
+
+private:
+ std::unique_ptr<weld::RadioButton> mxRbAutoStart;
+ std::unique_ptr<weld::RadioButton> mxRbManStart;
+ std::unique_ptr<ScDoubleField> mxEdStart;
+ std::unique_ptr<weld::RadioButton> mxRbAutoEnd;
+ std::unique_ptr<weld::RadioButton> mxRbManEnd;
+ std::unique_ptr<ScDoubleField> mxEdEnd;
+ std::unique_ptr<ScDoubleField> mxEdBy;
+ ScDPNumGroupEditHelper maStartHelper;
+ ScDPNumGroupEditHelper maEndHelper;
+};
+
+class ScDPDateGroupDlg : public weld::GenericDialogController
+{
+public:
+ explicit ScDPDateGroupDlg(weld::Window* pParent, const ScDPNumGroupInfo& rInfo,
+ sal_Int32 nDatePart, const Date& rNullDate);
+ virtual ~ScDPDateGroupDlg() override;
+ ScDPNumGroupInfo GetGroupInfo() const;
+ sal_Int32 GetDatePart() const;
+
+private:
+ DECL_LINK(ToggleHdl, weld::Toggleable&, void);
+ DECL_LINK(CheckHdl, const weld::TreeView::iter_col&, void);
+
+ void Check();
+private:
+ std::unique_ptr<weld::RadioButton> mxRbAutoStart;
+ std::unique_ptr<weld::RadioButton> mxRbManStart;
+ std::unique_ptr<SvtCalendarBox> mxEdStart;
+ std::unique_ptr<weld::RadioButton> mxRbAutoEnd;
+ std::unique_ptr<weld::RadioButton> mxRbManEnd;
+ std::unique_ptr<SvtCalendarBox> mxEdEnd;
+ std::unique_ptr<weld::RadioButton> mxRbNumDays;
+ std::unique_ptr<weld::RadioButton> mxRbUnits;
+ std::unique_ptr<weld::SpinButton> mxEdNumDays;
+ std::unique_ptr<weld::TreeView> mxLbUnits;
+ std::unique_ptr<weld::Button> mxBtnOk;
+ ScDPDateGroupEditHelper maStartHelper;
+ ScDPDateGroupEditHelper maEndHelper;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/drawsh.hxx b/sc/source/ui/inc/drawsh.hxx
new file mode 100644
index 0000000000..ae78ad1029
--- /dev/null
+++ b/sc/source/ui/inc/drawsh.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 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+#include <svx/svdtypes.hxx>
+#include <tools/link.hxx>
+#include <rtl/ref.hxx>
+
+class AbstractSvxObjectNameDialog;
+class ScViewData;
+class ScDrawView;
+class SdrMarkList;
+class SfxModule;
+class SdrObject;
+
+namespace weld { class Window; }
+
+namespace svx::sidebar { class SelectionChangeHandler; }
+
+class ScDrawShell : public SfxShell
+{
+ ScViewData& rViewData;
+ ::rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler;
+
+ DECL_LINK( NameObjectHdl, AbstractSvxObjectNameDialog&, bool );
+
+protected:
+ virtual void Activate(bool bMDI) override;
+ ScViewData& GetViewData() { return rViewData; }
+
+public:
+ SFX_DECL_INTERFACE(SCID_DRAW_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScDrawShell(ScViewData& rData);
+ virtual ~ScDrawShell() override;
+
+ static void StateDisableItems( SfxItemSet &rSet );
+
+ void ExecDrawAttr(SfxRequest& rReq);
+ void GetDrawAttrState(SfxItemSet &rSet);
+ void GetAttrFuncState(SfxItemSet &rSet);
+
+ void ExecDrawFunc(SfxRequest& rReq);
+ void GetDrawFuncState(SfxItemSet &rSet);
+ void GetState(SfxItemSet &rSet);
+
+ void ExecFormText(const SfxRequest& rReq); // StarFontWork
+ void GetFormTextState(SfxItemSet& rSet);
+
+ void ExecuteHLink(const SfxRequest& rReq); // Hyperlink
+ void GetHLinkState(SfxItemSet& rSet);
+
+ void ExecFormatPaintbrush(const SfxRequest& rReq);
+ void StateFormatPaintbrush(SfxItemSet& rSet);
+
+ void ExecuteMacroAssign(SdrObject* pObj, weld::Window* pWin);
+ void ExecuteLineDlg( const SfxRequest& rReq );
+ void ExecuteAreaDlg( const SfxRequest& rReq );
+ void ExecuteTextAttrDlg( SfxRequest& rReq );
+ void ExecuteMeasureDlg( SfxRequest& rReq );
+
+ ScDrawView* GetDrawView();
+
+ static bool AreAllObjectsOnLayer(SdrLayerID nLayerNo,const SdrMarkList& rMark);
+
+ void GetDrawAttrStateForIFBX( SfxItemSet& rSet );
+ OUString const & GetSidebarContextName();
+
+ void setModified();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/drawutil.hxx b/sc/source/ui/inc/drawutil.hxx
new file mode 100644
index 0000000000..7a241c4f92
--- /dev/null
+++ b/sc/source/ui/inc/drawutil.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+
+class Fraction;
+class OutputDevice;
+class ScDocument;
+
+class ScDrawUtil
+{
+public:
+ static void CalcScale( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const OutputDevice* pDev, const Fraction& rZoomX, const Fraction& rZoomY,
+ double nPPTX, double nPPTY,
+ Fraction& rScaleX, Fraction& rScaleY );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/drawview.hxx b/sc/source/ui/inc/drawview.hxx
new file mode 100644
index 0000000000..816a3428aa
--- /dev/null
+++ b/sc/source/ui/inc/drawview.hxx
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/fmview.hxx>
+
+#include <global.hxx>
+
+namespace com::sun::star::datatransfer { class XTransferable; }
+
+class ScDocument;
+class ScViewData;
+class ScDrawObjData;
+class SdrUndoManager;
+
+class ScDrawView final : public FmFormView
+{
+ ScViewData* pViewData;
+ VclPtr<OutputDevice> pDev; //! needed ?
+ ScDocument& rDoc;
+ SCTAB nTab;
+ Fraction aScaleX; // Factor for Drawing-MapMode
+ Fraction aScaleY;
+ std::unique_ptr<SdrDropMarkerOverlay> pDropMarker;
+ SdrObject* pDropMarkObj;
+ bool bInConstruct;
+
+ void Construct();
+
+ virtual void ModelHasChanged() override;
+
+ // add custom handles (used by other apps, e.g. AnchorPos)
+ virtual void AddCustomHdl() override;
+
+ void ImplClearCalcDropMarker();
+
+ // Create a local UndoManager
+ std::unique_ptr<SdrUndoManager> createLocalTextUndoManager() override;
+
+public:
+ ScDrawView(
+ OutputDevice* pOut,
+ ScViewData* pData);
+
+ virtual ~ScDrawView() override;
+
+ virtual void MarkListHasChanged() override;
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual void DoConnect(SdrOle2Obj* pOleObj) override;
+
+ virtual void MakeVisible( const tools::Rectangle& rRect, vcl::Window& rWin ) override;
+
+ virtual void DeleteMarked() override;
+
+ virtual bool SdrBeginTextEdit(
+ SdrObject* pObj,
+ SdrPageView* pPV = nullptr,
+ vcl::Window* pWin = nullptr,
+ bool bIsNewObj = false,
+ SdrOutliner* pGivenOutliner = nullptr,
+ OutlinerView* pGivenOutlinerView = nullptr,
+ bool bDontDeleteOutliner = false,
+ bool bOnlyOneView = false,
+ bool bGrabFocus = true) override;
+
+ virtual SdrEndTextEditKind SdrEndTextEdit( bool bDontDeleteReally = false ) override;
+
+ void MarkDropObj( SdrObject* pObj );
+
+ void SetMarkedToLayer( SdrLayerID nLayerNo );
+
+ void InvalidateAttribs();
+ void InvalidateDrawTextAttrs();
+
+ void BeginDrag( vcl::Window* pWindow, const Point& rStartPos );
+ void DoCut();
+ void DoCopy();
+
+ void GetScale( Fraction& rFractX, Fraction& rFractY ) const;
+ void RecalcScale();
+ void UpdateWorkArea();
+ SCTAB GetTab() const { return nTab; }
+
+ void CalcNormScale( Fraction& rFractX, Fraction& rFractY ) const;
+
+ void SetPageAnchored();
+ void SetCellAnchored(bool bResizeWithCell);
+ ScAnchorType GetAnchorType() const;
+
+ void UpdateIMap( SdrObject* pObj );
+
+ void UpdateUserViewOptions();
+
+ void SetMarkedOriginalSize();
+ void FitToCellSize();
+
+ bool SelectObject( std::u16string_view rName );
+ bool HasMarkedControl() const;
+ bool HasMarkedInternal() const;
+
+ bool InsertObjectSafe(SdrObject* pObj, SdrPageView& rPV);
+
+ /** Returns the selected object, if it is the caption object of a cell note.
+ @param ppCaptData (out-param) If not null, returns the pointer to the caption object data. */
+ SdrObject* GetMarkedNoteCaption( ScDrawObjData** ppCaptData );
+
+ /** Locks/unlocks the specified layer in the draw page.
+ Unlocked layer is required to be able to edit the contained objects. */
+ void LockCalcLayer( SdrLayerID nLayer, bool bLock );
+
+ /** Locks/unlocks the background layer that contains background objects.
+ Unlocked layer is required to be able to edit the objects. */
+ void LockBackgroundLayer( bool bLock ) { LockCalcLayer( SC_LAYER_BACK, bLock ); }
+
+ /** Locks/unlocks the internal layer that contains caption objects of cell notes.
+ Unlocked layer is required to be able to edit the contained objects. */
+ void LockInternalLayer( bool bLock = true ) { LockCalcLayer( SC_LAYER_INTERN, bLock ); }
+ /** Unlocks the internal layer that contains caption objects of cell notes. */
+ void UnlockInternalLayer() { LockInternalLayer( false ); }
+
+ SdrEndTextEditKind ScEndTextEdit(); // calls SetDrawTextUndo(0)
+ css::uno::Reference< css::datatransfer::XTransferable > CopyToTransferable();
+
+ SdrObject* GetObjectByName(std::u16string_view rName);
+ bool GetObjectIsMarked( const SdrObject * pObject );
+ void SelectCurrentViewObject( std::u16string_view rName );
+
+ // #i123922# helper which checks if a Graphic may be applied to an existing
+ // SdrObject; if it's a SdrGrafObj the fill will be replaced. If it's a
+ // fillable, non-OLE SdrObject, the FillStyle will be adapted
+ SdrObject* ApplyGraphicToObject(
+ SdrObject& rHitObject,
+ const Graphic& rGraphic,
+ const OUString& rBeginUndoText,
+ const OUString& rFile);
+
+ static void CheckOle( const SdrMarkList& rMarkList, bool& rAnyOle, bool& rOneOle );
+
+ void SyncForGrid( SdrObject* pObj );
+
+ bool calculateGridOffsetForSdrObject(
+ SdrObject& rSdrObject,
+ basegfx::B2DVector& rTarget) const;
+ bool calculateGridOffsetForB2DRange(
+ const basegfx::B2DRange& rB2DRange,
+ basegfx::B2DVector& rTarget) const;
+ void resetGridOffsetsForAllSdrPageViews();
+
+ /// See SdrMarkView::GetSfxViewShell().
+ SfxViewShell* GetSfxViewShell() const override;
+
+ // Do not create ObjectContact locally, but offer a call to allow override
+ // and to create own derivations of ObjectContact
+ virtual sdr::contact::ObjectContact* createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) const override;
+};
+
+extern Point aDragStartDiff;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/drformsh.hxx b/sc/source/ui/inc/drformsh.hxx
new file mode 100644
index 0000000000..97da8dee13
--- /dev/null
+++ b/sc/source/ui/inc/drformsh.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 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+#include "drawsh.hxx"
+
+class ScViewData;
+class SfxModule;
+
+class ScDrawFormShell final : public ScDrawShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_FORM_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScDrawFormShell(ScViewData& rData);
+ virtual ~ScDrawFormShell() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/drtxtob.hxx b/sc/source/ui/inc/drtxtob.hxx
new file mode 100644
index 0000000000..4690c46258
--- /dev/null
+++ b/sc/source/ui/inc/drtxtob.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 <sfx2/shell.hxx>
+#include <tools/link.hxx>
+#include <rtl/ref.hxx>
+
+#include <shellids.hxx>
+
+sal_uInt16 ScGetFontWorkId(); // instead of SvxFontWorkChildWindow::GetChildWindowId()
+
+class SfxModule;
+class ScViewData;
+class TransferableDataHelper;
+class TransferableClipboardListener;
+
+class ScDrawTextObjectBar final : public SfxShell
+{
+ ScViewData& mrViewData;
+ rtl::Reference<TransferableClipboardListener> mxClipEvtLstnr;
+ bool bPastePossible;
+
+ DECL_LINK( ClipboardChanged, TransferableDataHelper*, void );
+
+public:
+ SFX_DECL_INTERFACE(SCID_DRAW_TEXT_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScDrawTextObjectBar(ScViewData& rData);
+ virtual ~ScDrawTextObjectBar() override;
+
+ static void StateDisableItems( SfxItemSet &rSet );
+
+ void Execute( SfxRequest &rReq );
+ void ExecuteTrans( const SfxRequest& rReq );
+ void GetState( SfxItemSet& rSet );
+ void GetClipState( SfxItemSet& rSet );
+
+ void ExecuteAttr( SfxRequest &rReq );
+ void GetAttrState( SfxItemSet& rSet );
+ void ExecuteToggle( SfxRequest &rReq );
+ void GetStatePropPanelAttr(SfxItemSet &);
+
+ bool ExecuteCharDlg( const SfxItemSet& rArgs, SfxItemSet& rOutSet , sal_uInt16 nSlot);
+ bool ExecuteParaDlg( const SfxItemSet& rArgs, SfxItemSet& rOutSet );
+
+ void ExecuteExtra( SfxRequest &rReq );
+ void ExecFormText(const SfxRequest& rReq); // StarFontWork
+ void GetFormTextState(SfxItemSet& rSet);
+
+private:
+ void ExecuteGlobal( SfxRequest &rReq ); // called by Execute for all objects
+ static void GetGlobalClipState( SfxItemSet& rSet );
+ void ExecutePasteContents( SfxRequest &rReq );
+ bool IsNoteEdit() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/drwtrans.hxx b/sc/source/ui/inc/drwtrans.hxx
new file mode 100644
index 0000000000..71213739dd
--- /dev/null
+++ b/sc/source/ui/inc/drwtrans.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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <vcl/transfer.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <svl/urlbmk.hxx>
+#include <charthelper.hxx>
+
+class SdrModel;
+class ScDocShell;
+class SdrObject;
+class SdrView;
+class ScDrawView;
+class SdrOle2Obj;
+enum class ScDragSrc;
+
+class ScDrawTransferObj final : public TransferDataContainer
+{
+private:
+ std::unique_ptr<SdrModel> m_pModel;
+ TransferableDataHelper m_aOleData;
+ TransferableObjectDescriptor m_aObjDesc;
+ SfxObjectShellRef m_aDocShellRef;
+ SfxObjectShellRef m_aDrawPersistRef;
+
+ // extracted from model in ctor:
+ Size m_aSrcSize;
+ std::optional<INetBookmark> m_oBookmark;
+ bool m_bGraphic;
+ bool m_bGrIsBit;
+ bool m_bOleObj;
+ // source information for drag&drop:
+ // (view is needed to handle drawing objects)
+ std::unique_ptr<SdrView> m_pDragSourceView;
+ ScDragSrc m_nDragSourceFlags;
+ bool m_bDragWasInternal;
+
+ ScRangeListVector m_aProtectedChartRangesVector;
+
+ OUString maShellID;
+
+ void InitDocShell();
+ SdrOle2Obj* GetSingleObject();
+
+ void CreateOLEData();
+
+public:
+ ScDrawTransferObj( std::unique_ptr<SdrModel> pClipModel, ScDocShell* pContainerShell,
+ TransferableObjectDescriptor aDesc );
+ virtual ~ScDrawTransferObj() override;
+
+ virtual void AddSupportedFormats() override;
+ virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override;
+ virtual bool WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId,
+ const css::datatransfer::DataFlavor& rFlavor ) override;
+ virtual void DragFinished( sal_Int8 nDropAction ) override;
+
+ SdrModel* GetModel() const { return m_pModel.get(); }
+
+ void SetDrawPersist( const SfxObjectShellRef& rRef );
+ void SetDragSource( const ScDrawView* pView );
+ void SetDragSourceObj( SdrObject& rObj, SCTAB nTab );
+ void SetDragSourceFlags( ScDragSrc nFlags );
+ void SetDragWasInternal();
+
+ const OUString& GetShellID() const;
+
+ SdrView* GetDragSourceView() { return m_pDragSourceView.get(); }
+ ScDragSrc GetDragSourceFlags() const { return m_nDragSourceFlags; }
+
+ static ScDrawTransferObj* GetOwnClipboard(const css::uno::Reference<css::datatransfer::XTransferable2>&);
+
+ const ScRangeListVector& GetProtectedChartRangesVector() const { return m_aProtectedChartRangesVector; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/dwfunctr.hxx b/sc/source/ui/inc/dwfunctr.hxx
new file mode 100644
index 0000000000..faf68ecd41
--- /dev/null
+++ b/sc/source/ui/inc/dwfunctr.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 .
+ */
+#pragma once
+
+#include <comphelper/configurationlistener.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+class ScFuncDesc;
+namespace formula { class IFunctionDescription; }
+
+class ScFunctionWin;
+
+class EnglishFunctionNameChange : public comphelper::ConfigurationListenerProperty<bool>
+{
+ ScFunctionWin* m_pFunctionWin;
+protected:
+ virtual void setProperty(const css::uno::Any &rProperty) override;
+public:
+ EnglishFunctionNameChange(const rtl::Reference<comphelper::ConfigurationListener> &rListener, ScFunctionWin* pFunctionWin)
+ : ConfigurationListenerProperty(rListener, "EnglishFunctionName")
+ , m_pFunctionWin(pFunctionWin)
+ {
+ }
+};
+
+class ScFunctionWin : public PanelLayout
+{
+
+private:
+ std::unique_ptr<weld::ComboBox> xCatBox;
+ std::unique_ptr<weld::TreeView> xFuncList;
+ std::unique_ptr<weld::Button> xInsertButton;
+ std::unique_ptr<weld::TextView> xFiFuncDesc;
+ std::unique_ptr<weld::Entry> m_xSearchString;
+
+ rtl::Reference<comphelper::ConfigurationListener> xConfigListener;
+ std::unique_ptr<EnglishFunctionNameChange> xConfigChange;
+ const ScFuncDesc* pFuncDesc;
+ sal_uInt16 nArgs;
+ OUString m_aListHelpId;
+ OUString m_aSearchHelpId;
+
+ ::std::vector< const formula::IFunctionDescription*> aLRUList;
+
+ void UpdateLRUList();
+ void DoEnter();
+ void SetDescription();
+
+ DECL_LINK( SetRowActivatedHdl, weld::TreeView&, bool );
+ DECL_LINK( SetSelectionClickHdl, weld::Button&, void );
+ DECL_LINK( SelComboHdl, weld::ComboBox&, void );
+ DECL_LINK( SelTreeHdl, weld::TreeView&, void );
+ DECL_LINK( ModifyHdl, weld::Entry&, void );
+ DECL_LINK( KeyInputHdl, const KeyEvent&, bool);
+
+public:
+ ScFunctionWin(weld::Widget* pParent);
+
+ virtual ~ScFunctionWin() override;
+
+ void InitLRUList();
+ void UpdateFunctionList(const OUString&);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/editable.hxx b/sc/source/ui/inc/editable.hxx
new file mode 100644
index 0000000000..1c229a1b11
--- /dev/null
+++ b/sc/source/ui/inc/editable.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 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <unotools/resmgr.hxx>
+
+class ScDocument;
+class ScViewFunc;
+class ScMarkData;
+
+namespace sc {
+
+enum class ColRowEditAction;
+
+}
+
+class ScEditableTester
+{
+ bool mbIsEditable;
+ bool mbOnlyMatrix;
+
+public:
+ ScEditableTester();
+
+ // calls TestBlock
+ /** @param bNoMatrixAtAll
+ TRUE if there must not be any matrix, not even entirely
+ contained; for example in sorting. */
+ ScEditableTester( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ bool bNoMatrixAtAll = false );
+
+ // calls TestSelectedBlock
+ ScEditableTester( const ScDocument& rDoc,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark );
+
+ // calls TestRange
+ ScEditableTester( const ScDocument& rDoc, const ScRange& rRange );
+
+ // calls TestSelection
+ ScEditableTester( const ScDocument& rDoc, const ScMarkData& rMark );
+
+ // calls TestView
+ ScEditableTester( ScViewFunc* pView );
+
+ ScEditableTester(
+ const ScDocument& rDoc, sc::ColRowEditAction eAction, SCCOLROW nStart, SCCOLROW nEnd,
+ const ScMarkData& rMark );
+
+ // Several calls to the Test... methods check if *all* of the ranges
+ // are editable. For several independent checks, Reset() has to be used.
+ void TestBlock( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ bool bNoMatrixAtAll = false );
+ void TestSelectedBlock( const ScDocument& rDoc,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark );
+ void TestRange( const ScDocument& rDoc, const ScRange& rRange );
+ void TestSelection( const ScDocument& rDoc, const ScMarkData& rMark );
+
+ void TestBlockForAction(
+ const ScDocument& rDoc, sc::ColRowEditAction eAction, SCCOLROW nStart, SCCOLROW nEnd,
+ const ScMarkData& rMark );
+
+ bool IsEditable() const { return mbIsEditable; }
+ bool IsFormatEditable() const { return mbIsEditable || mbOnlyMatrix; }
+ TranslateId GetMessageId() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/editfield.hxx b/sc/source/ui/inc/editfield.hxx
new file mode 100644
index 0000000000..103919d1b5
--- /dev/null
+++ b/sc/source/ui/inc/editfield.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+/** An edit control that contains a double precision floating-point value. */
+class ScDoubleField
+{
+private:
+ std::unique_ptr<weld::Entry> m_xEntry;
+
+public:
+ explicit ScDoubleField(std::unique_ptr<weld::Entry> xEntry);
+
+ bool GetValue(double& rfValue) const;
+ void SetValue(double fValue, sal_Int32 nDecPlaces = 12);
+
+ weld::Entry& get_widget() { return *m_xEntry; }
+
+ void grab_focus() { m_xEntry->grab_focus(); }
+ bool get_sensitive() const { return m_xEntry->get_sensitive(); }
+ void set_sensitive(bool bSensitive) { m_xEntry->set_sensitive(bSensitive); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/editsh.hxx b/sc/source/ui/inc/editsh.hxx
new file mode 100644
index 0000000000..99df24eb78
--- /dev/null
+++ b/sc/source/ui/inc/editsh.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <tools/link.hxx>
+#include <rtl/ref.hxx>
+
+#include <shellids.hxx>
+
+class SfxModule;
+class EditView;
+class ScViewData;
+class ScInputHandler;
+class SvxFieldData;
+class TransferableDataHelper;
+class TransferableClipboardListener;
+
+class ScEditShell final : public SfxShell
+{
+private:
+ EditView* pEditView;
+ ScViewData& rViewData;
+ rtl::Reference<TransferableClipboardListener> mxClipEvtLstnr;
+ bool bPastePossible;
+ bool bIsInsertMode;
+
+ // These methods did return 'const SvxURLField*' before, but
+ // at least for GetFirstURLFieldFromCell this is not safe: The
+ // SvxFieldItem accessed there and held in the local temporary
+ // SfxItemSet may be deleted with it, so return value can be
+ // corrupted/deleted. To avoid that, return a Clone
+ std::unique_ptr<const SvxFieldData> GetURLField();
+ std::unique_ptr<const SvxFieldData> GetFirstURLFieldFromCell();
+
+ ScInputHandler* GetMyInputHdl();
+
+ DECL_LINK( ClipboardChanged, TransferableDataHelper*, void );
+
+public:
+ SFX_DECL_INTERFACE(SCID_EDIT_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScEditShell(EditView* pView, ScViewData& rData);
+ virtual ~ScEditShell() override;
+
+ void SetEditView(EditView* pView);
+ EditView* GetEditView() {return pEditView;}
+
+ void Execute(SfxRequest& rReq);
+ void ExecuteTrans(const SfxRequest& rReq);
+ void GetState(SfxItemSet &rSet);
+ void GetClipState(SfxItemSet& rSet);
+
+ void ExecuteAttr(SfxRequest& rReq);
+ void GetAttrState(SfxItemSet &rSet);
+
+ void ExecuteUndo(const SfxRequest& rReq);
+ void GetUndoState(SfxItemSet &rSet);
+
+ OUString GetSelectionText( bool bWholeWord );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/filldlg.hxx b/sc/source/ui/inc/filldlg.hxx
new file mode 100644
index 0000000000..8c82037465
--- /dev/null
+++ b/sc/source/ui/inc/filldlg.hxx
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <global.hxx>
+
+class ScDocument;
+
+class ScFillSeriesDlg : public weld::GenericDialogController
+{
+public:
+ ScFillSeriesDlg( weld::Window* pParent,
+ ScDocument& rDocument,
+ FillDir eFillDir,
+ FillCmd eFillCmd,
+ FillDateCmd eFillDateCmd,
+ OUString aStartStr,
+ double fStep,
+ double fMax,
+ SCSIZE nSelectHeight,
+ SCSIZE nSelectWidth,
+ sal_uInt16 nPossDir );
+ virtual ~ScFillSeriesDlg() override;
+
+ FillDir GetFillDir() const { return theFillDir; }
+ FillCmd GetFillCmd() const { return theFillCmd; }
+ FillDateCmd GetFillDateCmd() const { return theFillDateCmd; }
+ double GetStart() const { return fStartVal; }
+ double GetStep() const { return fIncrement; }
+ double GetMax() const { return fEndVal; }
+
+ OUString GetStartStr() const { return m_xEdStartVal->get_text(); }
+
+ void SetEdStartValEnabled(bool bFlag);
+
+private:
+ const OUString aStartStrVal;
+ const OUString aErrMsgInvalidVal;
+
+ ScDocument& rDoc;
+ FillDir theFillDir;
+ FillCmd theFillCmd;
+ FillDateCmd theFillDateCmd;
+ double fStartVal;
+ double fIncrement;
+ double fEndVal;
+ const SCSIZE m_nSelectHeight;
+ const SCSIZE m_nSelectWidth;
+
+ std::unique_ptr<weld::Label> m_xFtStartVal;
+ std::unique_ptr<weld::Entry> m_xEdStartVal;
+
+ std::unique_ptr<weld::Label> m_xFtEndVal;
+ std::unique_ptr<weld::Entry> m_xEdEndVal;
+
+ std::unique_ptr<weld::Label> m_xFtIncrement;
+ std::unique_ptr<weld::Entry> m_xEdIncrement;
+ std::unique_ptr<weld::RadioButton> m_xBtnDown;
+ std::unique_ptr<weld::RadioButton> m_xBtnRight;
+ std::unique_ptr<weld::RadioButton> m_xBtnUp;
+ std::unique_ptr<weld::RadioButton> m_xBtnLeft;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnArithmetic;
+ std::unique_ptr<weld::RadioButton> m_xBtnGeometric;
+ std::unique_ptr<weld::RadioButton> m_xBtnDate;
+ std::unique_ptr<weld::RadioButton> m_xBtnAutoFill;
+
+ std::unique_ptr<weld::Label> m_xFtTimeUnit;
+ std::unique_ptr<weld::RadioButton> m_xBtnDay;
+ std::unique_ptr<weld::RadioButton> m_xBtnDayOfWeek;
+ std::unique_ptr<weld::RadioButton> m_xBtnMonth;
+ std::unique_ptr<weld::RadioButton> m_xBtnYear;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+
+ void Init( sal_uInt16 nPossDir );
+ weld::Entry* CheckValues();
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(DisableHdl, weld::Toggleable&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/filtdlg.hxx b/sc/source/ui/inc/filtdlg.hxx
new file mode 100644
index 0000000000..a99bc93edf
--- /dev/null
+++ b/sc/source/ui/inc/filtdlg.hxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include <queryparam.hxx>
+#include <filterentries.hxx>
+#include <queryentry.hxx>
+
+#include <memory>
+#include <deque>
+#include <vector>
+#include <map>
+
+class ScFilterOptionsMgr;
+class ScViewData;
+class ScDocument;
+class ScQueryItem;
+
+class ScFilterDlg : public ScAnyRefDlgController
+{
+ struct EntryList
+ {
+ ScFilterEntries maFilterEntries;
+ size_t mnHeaderPos;
+
+ EntryList(const EntryList&) = delete;
+ const EntryList& operator=(const EntryList&) = delete;
+
+ EntryList();
+ };
+ typedef std::map<SCCOL, std::unique_ptr<EntryList>> EntryListsMap;
+public:
+ ScFilterDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet);
+ virtual ~ScFilterDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+
+ virtual void Close() override;
+ void SliderMoved();
+ size_t GetSliderPos() const;
+ void RefreshEditRow( size_t nOffset );
+
+private:
+ const OUString aStrUndefined;
+ const OUString aStrNone;
+
+ const OUString aStrEmpty;
+ const OUString aStrNotEmpty;
+ const OUString aStrColumn;
+ const OUString aStrFontColor;
+ const OUString aStrBackgroundColor;
+
+ std::unique_ptr<ScFilterOptionsMgr> pOptionsMgr;
+
+ const sal_uInt16 nWhichQuery;
+ ScQueryParam theQueryData;
+ std::unique_ptr<ScQueryItem> pOutItem;
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+ SCTAB nSrcTab;
+
+ std::vector<weld::ComboBox*> maValueEdArr;
+ std::vector<weld::ComboBox*> maFieldLbArr;
+ std::vector<weld::ComboBox*> maCondLbArr;
+ std::vector<weld::ComboBox*> maConnLbArr;
+ std::vector<weld::ComboBox*> maColorLbArr;
+ std::vector<weld::Button*> maRemoveBtnArr;
+
+ std::deque<bool> maHasDates;
+ std::deque<bool> maRefreshExceptQuery;
+ bool bRefInputMode;
+
+ EntryListsMap m_EntryLists;
+
+ // Hack: RefInput control
+ std::unique_ptr<Timer> pTimer;
+
+ std::unique_ptr<weld::ComboBox> m_xLbConnect1;
+ std::unique_ptr<weld::ComboBox> m_xLbField1;
+ std::unique_ptr<weld::ComboBox> m_xLbCond1;
+ std::unique_ptr<weld::ComboBox> m_xEdVal1;
+ std::unique_ptr<weld::ComboBox> m_xLbColor1;
+ std::unique_ptr<weld::Button> m_xBtnRemove1;
+
+ std::unique_ptr<weld::ComboBox> m_xLbConnect2;
+ std::unique_ptr<weld::ComboBox> m_xLbField2;
+ std::unique_ptr<weld::ComboBox> m_xLbCond2;
+ std::unique_ptr<weld::ComboBox> m_xEdVal2;
+ std::unique_ptr<weld::ComboBox> m_xLbColor2;
+ std::unique_ptr<weld::Button> m_xBtnRemove2;
+
+ std::unique_ptr<weld::ComboBox> m_xLbConnect3;
+ std::unique_ptr<weld::ComboBox> m_xLbField3;
+ std::unique_ptr<weld::ComboBox> m_xLbCond3;
+ std::unique_ptr<weld::ComboBox> m_xEdVal3;
+ std::unique_ptr<weld::ComboBox> m_xLbColor3;
+ std::unique_ptr<weld::Button> m_xBtnRemove3;
+
+ std::unique_ptr<weld::ComboBox> m_xLbConnect4;
+ std::unique_ptr<weld::ComboBox> m_xLbField4;
+ std::unique_ptr<weld::ComboBox> m_xLbCond4;
+ std::unique_ptr<weld::ComboBox> m_xEdVal4;
+ std::unique_ptr<weld::ComboBox> m_xLbColor4;
+ std::unique_ptr<weld::Button> m_xBtnRemove4;
+
+ std::unique_ptr<weld::Widget> m_xContents;
+ std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
+ std::unique_ptr<weld::Expander> m_xExpander;
+
+ std::unique_ptr<weld::Button> m_xBtnClear;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnCase;
+ std::unique_ptr<weld::CheckButton> m_xBtnRegExp;
+ std::unique_ptr<weld::CheckButton> m_xBtnHeader;
+ std::unique_ptr<weld::CheckButton> m_xBtnUnique;
+ std::unique_ptr<weld::CheckButton> m_xBtnCopyResult;
+ std::unique_ptr<weld::ComboBox> m_xLbCopyArea;
+ std::unique_ptr<formula::RefEdit> m_xEdCopyArea;
+ std::unique_ptr<formula::RefButton> m_xRbCopyArea;
+ std::unique_ptr<weld::CheckButton> m_xBtnDestPers;
+ std::unique_ptr<weld::Label> m_xFtDbAreaLabel;
+ std::unique_ptr<weld::Label> m_xFtDbArea;
+
+private:
+ void Init ( const SfxItemSet& rArgSet );
+ void FillFieldLists ();
+ void UpdateValueList ( size_t nList );
+ void UpdateHdrInValueList( size_t nList );
+ void ClearValueList ( size_t nList );
+ void UpdateColorList ( size_t nList );
+ size_t GetFieldSelPos ( SCCOL nField );
+ ScQueryItem* GetOutputItem ();
+ void SetValString ( const OUString& rQueryStr,
+ const ScQueryEntry::Item& rItem,
+ OUString& rValStr );
+
+ // Handler:
+ DECL_LINK( LbSelectHdl, weld::ComboBox&, void );
+ DECL_LINK( ValModifyHdl, weld::ComboBox&, void );
+ DECL_LINK( CheckBoxHdl, weld::Toggleable&, void );
+ DECL_LINK( BtnClearHdl, weld::Button&, void );
+ DECL_LINK( BtnRemoveHdl, weld::Button&, void );
+ DECL_LINK( EndDlgHdl, weld::Button&, void );
+ DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void );
+ DECL_LINK( MoreExpandedHdl, weld::Expander&, void );
+
+ // Hack: RefInput control
+ DECL_LINK( TimeOutHdl, Timer*, void );
+};
+
+class ScSpecialFilterDlg : public ScAnyRefDlgController
+{
+public:
+ ScSpecialFilterDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const SfxItemSet& rArgSet);
+ virtual ~ScSpecialFilterDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+
+ virtual void Close() override;
+
+private:
+ const OUString aStrUndefined;
+
+ std::unique_ptr<ScFilterOptionsMgr> pOptionsMgr;
+
+ const sal_uInt16 nWhichQuery;
+ const ScQueryParam theQueryData;
+ std::unique_ptr<ScQueryItem> pOutItem;
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+
+ bool bRefInputMode;
+
+ formula::RefEdit* m_pRefInputEdit;
+
+ std::unique_ptr<weld::ComboBox> m_xLbFilterArea;
+ std::unique_ptr<formula::RefEdit> m_xEdFilterArea;
+ std::unique_ptr<formula::RefButton> m_xRbFilterArea;
+
+ std::unique_ptr<weld::Expander> m_xExpander;
+ std::unique_ptr<weld::CheckButton> m_xBtnCase;
+ std::unique_ptr<weld::CheckButton> m_xBtnRegExp;
+ std::unique_ptr<weld::CheckButton> m_xBtnHeader;
+ std::unique_ptr<weld::CheckButton> m_xBtnUnique;
+ std::unique_ptr<weld::CheckButton> m_xBtnCopyResult;
+ std::unique_ptr<weld::ComboBox> m_xLbCopyArea;
+ std::unique_ptr<formula::RefEdit> m_xEdCopyArea;
+ std::unique_ptr<formula::RefButton> m_xRbCopyArea;
+ std::unique_ptr<weld::CheckButton> m_xBtnDestPers;
+ std::unique_ptr<weld::Label> m_xFtDbAreaLabel;
+ std::unique_ptr<weld::Label> m_xFtDbArea;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ std::unique_ptr<weld::Frame> m_xFilterFrame;
+ std::unique_ptr<weld::Label> m_xFilterLabel;
+
+private:
+ void Init( const SfxItemSet& rArgSet );
+ ScQueryItem* GetOutputItem( const ScQueryParam& rParam,
+ const ScRange& rSource );
+
+ // Handler
+ DECL_LINK( FilterAreaSelHdl, weld::ComboBox&, void );
+ DECL_LINK( FilterAreaModHdl, formula::RefEdit&, void );
+ DECL_LINK( EndDlgHdl, weld::Button&, void );
+
+ // RefInput control
+ DECL_LINK( RefInputEditHdl, formula::RefEdit&, void );
+ DECL_LINK( RefInputButtonHdl, formula::RefButton&, void );
+ void RefInputHdl();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/foptmgr.hxx b/sc/source/ui/inc/foptmgr.hxx
new file mode 100644
index 0000000000..661feac98f
--- /dev/null
+++ b/sc/source/ui/inc/foptmgr.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+namespace formula
+{
+ class RefButton;
+ class RefButton;
+ class RefEdit;
+}
+struct ScQueryParam;
+class ScDocument;
+class ScViewData;
+
+class ScFilterOptionsMgr
+{
+public:
+ ScFilterOptionsMgr(ScViewData* ptrViewData,
+ const ScQueryParam& refQueryData,
+ weld::CheckButton* refBtnCase,
+ weld::CheckButton* refBtnRegExp,
+ weld::CheckButton* refBtnHeader,
+ weld::CheckButton* refBtnUnique,
+ weld::CheckButton* refBtnCopyResult,
+ weld::CheckButton* refBtnDestPers,
+ weld::ComboBox* refLbCopyArea,
+ formula::RefEdit* refEdCopyArea,
+ formula::RefButton* refRbCopyArea,
+ weld::Label* refFtDbAreaLabel,
+ weld::Label* refFtDbArea,
+ const OUString& refStrUndefined );
+ bool VerifyPosStr ( const OUString& rPosStr ) const;
+
+private:
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+
+ weld::CheckButton* pBtnCase;
+ weld::CheckButton* pBtnRegExp;
+ weld::CheckButton* pBtnHeader;
+ weld::CheckButton* pBtnUnique;
+ weld::CheckButton* pBtnCopyResult;
+ weld::CheckButton* pBtnDestPers;
+ weld::ComboBox* pLbCopyArea;
+ formula::RefEdit* pEdCopyArea;
+ formula::RefButton* pRbCopyArea;
+ weld::Label* pFtDbAreaLabel;
+ weld::Label* pFtDbArea;
+
+ const OUString& rStrUndefined;
+
+ const ScQueryParam& rQueryData;
+
+private:
+ void Init();
+
+ // Handler:
+ DECL_LINK( EdAreaModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( LbAreaSelHdl, weld::ComboBox&, void );
+ DECL_LINK( BtnCopyResultHdl, weld::Toggleable&, void );
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/formatsh.hxx b/sc/source/ui/inc/formatsh.hxx
new file mode 100644
index 0000000000..85819b1ac5
--- /dev/null
+++ b/sc/source/ui/inc/formatsh.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+
+class SfxModule;
+class ScViewData;
+enum class SvNumFormatType : sal_Int16;
+
+class ScFormatShell: public SfxShell
+{
+ ScViewData& rViewData;
+
+protected:
+ ScViewData& GetViewData() { return rViewData; }
+ const ScViewData& GetViewData() const { return rViewData; }
+
+public:
+ SFX_DECL_INTERFACE(SCID_FORMAT_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScFormatShell(ScViewData& rData);
+ virtual ~ScFormatShell() override;
+
+ void ExecuteNumFormat( SfxRequest& rReq );
+ void GetNumFormatState( SfxItemSet& rSet );
+
+ void ExecuteAttr( SfxRequest& rReq );
+ void GetAttrState( SfxItemSet& rSet );
+
+ void ExecuteAlignment( SfxRequest& rReq );
+
+ void ExecuteTextAttr( SfxRequest& rReq );
+ void GetTextAttrState( SfxItemSet& rSet );
+
+ void GetAlignState( SfxItemSet& rSet );
+ void GetBorderState( SfxItemSet& rSet );
+
+ void ExecuteStyle( SfxRequest& rReq );
+
+ void ExecuteTextDirection( const SfxRequest& rReq );
+ void GetTextDirectionState( SfxItemSet& rSet );
+
+ void ExecFormatPaintbrush( const SfxRequest& rReq );
+ void StateFormatPaintbrush( SfxItemSet& rSet );
+
+private:
+ SvNumFormatType GetCurrentNumberFormatType();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/formdata.hxx b/sc/source/ui/inc/formdata.hxx
new file mode 100644
index 0000000000..a2cecbffaf
--- /dev/null
+++ b/sc/source/ui/inc/formdata.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 .
+ */
+
+#pragma once
+
+#include <formula/formdata.hxx>
+class ScInputHandler;
+class ScDocShell;
+
+class ScFormEditData : public formula::FormEditData
+{
+public:
+ ScFormEditData();
+ virtual ~ScFormEditData() override;
+
+ ScInputHandler* GetInputHandler() { return pInputHandler;}
+ ScDocShell* GetDocShell() { return pScDocShell;}
+
+ void SetInputHandler(ScInputHandler* pHdl) { pInputHandler=pHdl;}
+ void SetDocShell(ScDocShell* pSds) { pScDocShell=pSds;}
+
+ virtual void SaveValues() override;
+
+private:
+ ScFormEditData(ScFormEditData const &) = delete;
+ void operator =(ScFormEditData const &) = delete;
+
+ ScInputHandler* pInputHandler;
+ ScDocShell* pScDocShell;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/formula.hxx b/sc/source/ui/inc/formula.hxx
new file mode 100644
index 0000000000..02ee866c1b
--- /dev/null
+++ b/sc/source/ui/inc/formula.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "anyrefdg.hxx"
+
+#include <scmod.hxx>
+#include <formula/formula.hxx>
+#include "IAnyRefDialog.hxx"
+
+class ScViewData;
+class ScDocument;
+class ScFuncDesc;
+class ScInputHandler;
+class ScDocShell;
+
+class ScFormulaDlg final : public formula::FormulaDlg,
+ public IAnyRefDialog
+{
+ ScFormulaReferenceHelper m_aHelper;
+ css::uno::Reference< css::sheet::XFormulaParser> m_xParser;
+ css::uno::Reference< css::sheet::XFormulaOpCodeMapper> m_xOpCodeMapper;
+
+ ScDocument* m_pDoc;
+ ScAddress m_CursorPos;
+ ScTabViewShell* m_pViewShell;
+ mutable std::shared_ptr<ScCompiler> m_xCompiler;
+
+public:
+ ScFormulaDlg( SfxBindings* pB, SfxChildWindow* pCW,
+ weld::Window* pParent, const ScViewData& rViewData, const formula::IFunctionManager* _pFunctionMgr);
+ virtual ~ScFormulaDlg() COVERITY_NOEXCEPT_FALSE override;
+
+ // IFormulaEditorHelper
+ virtual void notifyChange() override;
+ virtual void fill() override;
+ virtual bool calculateValue(const OUString& _sExpression, OUString& _rResult, bool bMatrixFormula) override;
+ virtual std::shared_ptr<formula::FormulaCompiler> getCompiler() const override;
+ virtual std::unique_ptr<formula::FormulaCompiler> createCompiler( formula::FormulaTokenArray& rArray ) const override;
+ virtual void doClose(bool _bOk) override;
+ virtual void insertEntryToLRUList(const formula::IFunctionDescription* pDesc) override;
+ virtual void showReference(const OUString& _sFormula) override;
+ virtual void dispatch(bool _bOK, bool _bMatrixChecked) override;
+ virtual void setDispatcherLock( bool bLock ) override;
+ virtual void deleteFormData() override;
+ virtual void clear() override;
+ virtual void switchBack() override;
+ virtual formula::FormEditData* getFormEditData() const override;
+ virtual void setCurrentFormula(const OUString& _sReplacement) override;
+ virtual void setSelection(sal_Int32 _nStart, sal_Int32 _nEnd) override;
+ virtual void getSelection(sal_Int32& _nStart, sal_Int32& _nEnd) const override;
+ virtual OUString getCurrentFormula() const override;
+
+ virtual formula::IFunctionManager* getFunctionManager() override;
+ virtual ::std::unique_ptr<formula::FormulaTokenArray> convertToTokenArray(const css::uno::Sequence< css::sheet::FormulaToken >& _aTokenList) override;
+ virtual css::uno::Reference< css::sheet::XFormulaParser> getFormulaParser() const override;
+ virtual css::uno::Reference< css::sheet::XFormulaOpCodeMapper> getFormulaOpCodeMapper() const override;
+ virtual css::table::CellAddress getReferencePosition() const override;
+
+ virtual void Close() override;
+
+ // sc::IAnyRefDialog
+ virtual void ShowReference(const OUString& _sRef) override;
+ virtual void HideReference( bool bDoneRefMode = true ) override;
+ virtual void SetReference( const ScRange& rRef, ScDocument& rD ) override;
+
+ virtual void ReleaseFocus( formula::RefEdit* pEdit ) override;
+ virtual void ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton ) override;
+ virtual void RefInputDone( bool bForced = false ) override;
+ virtual bool IsTableLocked() const override;
+ virtual bool IsRefInputMode() const override;
+
+ virtual bool IsDocAllowed( SfxObjectShell* pDocSh ) const override;
+ virtual void AddRefEntry() override;
+ virtual void SetActive() override;
+ virtual void ViewShellChanged() override;
+
+private:
+ virtual void RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton = nullptr ) override;
+ static void SaveLRUEntry(const ScFuncDesc* pFuncDesc);
+
+ static bool IsInputHdl(const ScInputHandler* pHdl);
+ static ScInputHandler* GetNextInputHandler(const ScDocShell* pDocShell, ScTabViewShell** ppViewSh);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuconarc.hxx b/sc/source/ui/inc/fuconarc.hxx
new file mode 100644
index 0000000000..a330840ed0
--- /dev/null
+++ b/sc/source/ui/inc/fuconarc.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 .
+ */
+
+#pragma once
+
+#include "fuconstr.hxx"
+
+/** Draw rectangle */
+class FuConstArc : public FuConstruct
+{
+public:
+ FuConstArc(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuConstArc() override;
+ // Mouse- & Key-Events
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuconcustomshape.hxx b/sc/source/ui/inc/fuconcustomshape.hxx
new file mode 100644
index 0000000000..b491b48fe5
--- /dev/null
+++ b/sc/source/ui/inc/fuconcustomshape.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "fuconstr.hxx"
+
+class SAL_DLLPUBLIC_RTTI FuConstCustomShape : public FuConstruct
+{
+ OUString aCustomShape;
+
+ void SetAttributes( SdrObject* pObj );
+
+public:
+ FuConstCustomShape(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuConstCustomShape() override;
+ // Mouse- & Key-Events
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject( const sal_uInt16 nID, const tools::Rectangle& rRectangle ) override;
+
+ // #i33136#
+ virtual bool doConstructOrthogonal() const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuconpol.hxx b/sc/source/ui/inc/fuconpol.hxx
new file mode 100644
index 0000000000..8cebc3b03a
--- /dev/null
+++ b/sc/source/ui/inc/fuconpol.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "fuconstr.hxx"
+
+/** Base class for all functions */
+class FuConstPolygon : public FuConstruct
+{
+public:
+ FuConstPolygon(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuConstPolygon() override;
+ // Mouse- & Key-Events
+ virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuconrec.hxx b/sc/source/ui/inc/fuconrec.hxx
new file mode 100644
index 0000000000..865ad5b2b4
--- /dev/null
+++ b/sc/source/ui/inc/fuconrec.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "fuconstr.hxx"
+
+/** Draw rectangle */
+class FuConstRectangle : public FuConstruct
+{
+public:
+ FuConstRectangle(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuConstRectangle() override;
+ // Mouse- & Key-Events
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+ static void SetLineEnds(SfxItemSet& rAttr, const SdrObject& rObj, sal_uInt16 nSlotId);
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuconstr.hxx b/sc/source/ui/inc/fuconstr.hxx
new file mode 100644
index 0000000000..a9076ee3ea
--- /dev/null
+++ b/sc/source/ui/inc/fuconstr.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 .
+ */
+
+#pragma once
+
+#include "fudraw.hxx"
+
+#include <scdllapi.h> // SC_DLLPUBLIC is needed for unittest
+
+/** Draw rectangle */
+class SAL_DLLPUBLIC_RTTI FuConstruct : public FuDraw
+{
+public:
+ FuConstruct(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuConstruct() override;
+ // Mouse- & Key-Events
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ SC_DLLPUBLIC virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ bool SimpleMouseButtonUp(const MouseEvent& rMEvt);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuconuno.hxx b/sc/source/ui/inc/fuconuno.hxx
new file mode 100644
index 0000000000..a1034c3ed4
--- /dev/null
+++ b/sc/source/ui/inc/fuconuno.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 .
+ */
+
+#pragma once
+
+#include "fuconstr.hxx"
+
+#include <scdllapi.h> // SC_DLLPUBLIC is needed for unittest
+
+enum class SdrInventor : sal_uInt32;
+
+/** Draw Control */
+class SAL_DLLPUBLIC_RTTI FuConstUnoControl final : public FuConstruct
+{
+ SdrInventor nInventor;
+ SdrObjKind nIdentifier;
+
+public:
+ FuConstUnoControl(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuConstUnoControl() override;
+ // Mouse- & Key-Events
+ SC_DLLPUBLIC virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ SC_DLLPUBLIC virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ SC_DLLPUBLIC virtual void Activate() override;
+ SC_DLLPUBLIC virtual void Deactivate() override;
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fudraw.hxx b/sc/source/ui/inc/fudraw.hxx
new file mode 100644
index 0000000000..0fdd6f9e88
--- /dev/null
+++ b/sc/source/ui/inc/fudraw.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 .
+ */
+
+#pragma once
+
+#include "fupoor.hxx"
+
+enum class PointerStyle;
+
+/** Base class for all Drawmodule specific functions */
+class FuDraw : public FuPoor
+{
+protected:
+ PointerStyle aNewPointer;
+ PointerStyle aOldPointer;
+
+public:
+ FuDraw(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView, SdrModel* pDoc,
+ const SfxRequest& rReq);
+ virtual ~FuDraw() override;
+
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+
+ virtual void ForcePointer(const MouseEvent* pMEvt);
+
+ virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ bool IsEditingANote() const;
+ bool IsSizingOrMovingNote(const MouseEvent& rMEvt) const;
+
+private:
+ void DoModifiers(const MouseEvent& rMEvt);
+ void ResetModifiers();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fuinsert.hxx b/sc/source/ui/inc/fuinsert.hxx
new file mode 100644
index 0000000000..3018d66fec
--- /dev/null
+++ b/sc/source/ui/inc/fuinsert.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 .
+ */
+
+#pragma once
+
+#include "fupoor.hxx"
+#include <scdllapi.h>
+#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp>
+
+class FuInsertGraphic : public FuPoor
+{
+public:
+ FuInsertGraphic(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, SfxRequest& rReq);
+ virtual ~FuInsertGraphic() override;
+};
+
+class FuInsertOLE : public FuPoor
+{
+public:
+ FuInsertOLE(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, SfxRequest& rReq);
+};
+
+class FuInsertChart : public FuPoor
+{
+ public:
+ FuInsertChart( ScTabViewShell& pViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, SfxRequest& rReq,
+ const Link<css::ui::dialogs::DialogClosedEvent*, void>& rLink);
+};
+
+class FuInsertMedia : public FuPoor
+{
+public:
+ FuInsertMedia(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+ virtual ~FuInsertMedia() override;
+};
+
+void SC_DLLPUBLIC ScLimitSizeOnDrawPage( Size& rSize, Point& rPos, const Size& rPage );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fupoor.hxx b/sc/source/ui/inc/fupoor.hxx
new file mode 100644
index 0000000000..6f226776b0
--- /dev/null
+++ b/sc/source/ui/inc/fupoor.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/timer.hxx>
+#include <sfx2/request.hxx>
+#include <svx/svdobj.hxx>
+#include <vcl/window.hxx>
+
+class ScDrawView;
+class ScTabViewShell;
+class SdrModel;
+class CommandEvent;
+class KeyEvent;
+class MouseEvent;
+
+// Return values for command
+#define SC_CMD_NONE 0
+#define SC_CMD_USED 1
+
+/** Base class for all functions */
+class FuPoor
+{
+protected:
+ ScDrawView* pView;
+ ScTabViewShell& rViewShell;
+ VclPtr<vcl::Window> pWindow;
+ SdrModel* pDrDoc;
+
+ SfxRequest aSfxRequest;
+
+ Timer aScrollTimer; // for Autoscrolling
+ DECL_LINK( ScrollHdl, Timer *, void );
+ void ForceScroll(const Point& aPixPos);
+
+ Timer aDragTimer; // for Drag&Drop
+ DECL_LINK( DragTimerHdl, Timer *, void );
+ DECL_LINK( DragHdl, void *, void );
+ bool bIsInDragMode;
+ Point aMDPos; // Position of MouseButtonDown
+
+ // member to hold state of the mouse buttons for creation
+ // of own MouseEvents (like in ScrollHdl)
+private:
+ sal_uInt16 mnCode;
+
+public:
+ FuPoor(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+ virtual ~FuPoor();
+
+ // see member
+ void SetMouseButtonCode(sal_uInt16 nNew) { if(nNew != mnCode) mnCode = nNew; }
+ sal_uInt16 GetMouseButtonCode() const { return mnCode; }
+
+ // Mouse- & Key-Events; return value=TRUE: Event was processed
+ virtual bool KeyInput(const KeyEvent& rKEvt);
+ virtual bool MouseMove(const MouseEvent&) { return false; }
+
+ // moved from inline to *.cxx
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt); // { return FALSE; }
+
+ // moved from inline to *.cxx
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt); // { return FALSE; }
+
+ sal_uInt8 Command(const CommandEvent& rCEvt);
+
+ virtual void Activate();
+ virtual void Deactivate();
+
+ void SetWindow(vcl::Window* pWin) { pWindow = pWin; }
+
+ sal_uInt16 GetSlotID() const { return aSfxRequest.GetSlot(); }
+
+ bool IsDetectiveHit( const Point& rLogicPos );
+
+ void StopDragTimer();
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle);
+
+protected:
+ static void ImpForceQuadratic(tools::Rectangle& rRect);
+
+public:
+ // #i33136#
+ virtual bool doConstructOrthogonal() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/fusel.hxx b/sc/source/ui/inc/fusel.hxx
new file mode 100644
index 0000000000..fcb5815630
--- /dev/null
+++ b/sc/source/ui/inc/fusel.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "fudraw.hxx"
+
+class SdrPageView;
+
+/** Base class for all functions */
+class FuSelection : public FuDraw
+{
+public:
+ FuSelection(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq );
+
+ virtual ~FuSelection() override;
+ // Mouse- & Key-Events
+ virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+private:
+ bool TestDetective( const SdrPageView* pPV, const Point& rPos ); // -> fusel2
+
+ bool IsNoteCaptionMarked() const;
+ bool IsNoteCaptionClicked( const Point& rPos ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/futext.hxx b/sc/source/ui/inc/futext.hxx
new file mode 100644
index 0000000000..fc8e75dc15
--- /dev/null
+++ b/sc/source/ui/inc/futext.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 "fuconstr.hxx"
+
+class SdrOutliner;
+
+/** Base class for Text functions */
+class FuText : public FuConstruct
+{
+public:
+ FuText(ScTabViewShell& rViewSh, vcl::Window* pWin, ScDrawView* pView,
+ SdrModel* pDoc, const SfxRequest& rReq);
+
+ virtual ~FuText() override;
+
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+ virtual void Activate() override;
+ virtual void Deactivate() override;
+
+ virtual void ForcePointer(const MouseEvent* pMEvt) override;
+
+ void SetInEditMode( SdrObject* pObj = nullptr, const Point* pMousePixel = nullptr,
+ bool bCursorToEnd = false, const KeyEvent* pInitialKey = nullptr );
+ void StopEditMode();
+
+ // Create default drawing objects via keyboard
+ virtual rtl::Reference<SdrObject> CreateDefaultObject(const sal_uInt16 nID, const tools::Rectangle& rRectangle) override;
+
+private:
+ std::unique_ptr<SdrOutliner> MakeOutliner();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/gototabdlg.hxx b/sc/source/ui/inc/gototabdlg.hxx
new file mode 100644
index 0000000000..1bbd5ed976
--- /dev/null
+++ b/sc/source/ui/inc/gototabdlg.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/.
+ *
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScGoToTabDlg : public weld::GenericDialogController
+{
+private:
+ std::vector<OUString> maCacheSheetsNames;
+
+ std::unique_ptr<weld::Frame> m_xFrameMask;
+ std::unique_ptr<weld::Entry> m_xEnNameMask;
+ std::unique_ptr<weld::Frame> m_xFrameSheets;
+ std::unique_ptr<weld::TreeView> m_xLb;
+
+ DECL_LINK(DblClkHdl, weld::TreeView&, bool);
+ DECL_LINK(FindNameHdl, weld::Entry&, void);
+
+public:
+ ScGoToTabDlg(weld::Window* pParent);
+ virtual ~ScGoToTabDlg() override;
+
+ /** Sets dialog title, label texts and help IDs. */
+ void SetDescription(const OUString& rTitle, const OUString& rEntryLabel,
+ const OUString& rListLabel, const OUString& rDlgHelpId,
+ const OUString& rEnHelpId, const OUString& rLbHelpId);
+
+ /** Inserts a string into the weld::TreeView. */
+ void Insert(const OUString& rString, bool bSelected);
+
+ OUString GetSelectedEntry() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/graphsh.hxx b/sc/source/ui/inc/graphsh.hxx
new file mode 100644
index 0000000000..cae5ac69bc
--- /dev/null
+++ b/sc/source/ui/inc/graphsh.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 <memory>
+#include <vector>
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+#include "drawsh.hxx"
+
+class SdrExternalToolEdit;
+class ScViewData;
+class SfxModule;
+
+
+class ScGraphicShell final : public ScDrawShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_GRAPHIC_SHELL)
+
+private:
+ std::vector<std::unique_ptr<SdrExternalToolEdit>> m_ExternalEdits;
+
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScGraphicShell(ScViewData& rData);
+ virtual ~ScGraphicShell() override;
+
+ void Execute(SfxRequest& rReq);
+ void GetAttrState(SfxItemSet &rSet);
+
+ void ExecuteFilter(const SfxRequest& rReq);
+ void GetFilterState(SfxItemSet &rSet);
+
+ void ExecuteExternalEdit(SfxRequest& rReq);
+ void GetExternalEditState(SfxItemSet &rSet);
+
+ void ExecuteCompressGraphic(SfxRequest& rReq);
+ void GetCompressGraphicState(SfxItemSet &rSet);
+
+ void ExecuteCropGraphic(SfxRequest& rReq);
+ void GetCropGraphicState(SfxItemSet &rSet);
+
+ void ExecuteSaveGraphic(SfxRequest& rReq);
+ void GetSaveGraphicState(SfxItemSet &rSet);
+
+ void ExecuteChangePicture(SfxRequest& rReq);
+ void GetChangePictureState(SfxItemSet &rSet);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/gridmerg.hxx b/sc/source/ui/inc/gridmerg.hxx
new file mode 100644
index 0000000000..62e9fdcbee
--- /dev/null
+++ b/sc/source/ui/inc/gridmerg.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/long.hxx>
+#include <vcl/vclptr.hxx>
+
+class OutputDevice;
+
+class ScGridMerger
+{
+private:
+ VclPtr<OutputDevice> pDev;
+ tools::Long nOneX;
+ tools::Long nOneY;
+ tools::Long nFixStart;
+ tools::Long nFixEnd;
+ tools::Long nVarStart;
+ tools::Long nVarDiff;
+ tools::Long nCount;
+ bool bVertical;
+ bool bOptimize;
+
+ void AddLine( tools::Long nStart, tools::Long nEnd, tools::Long nPos );
+
+public:
+ ScGridMerger( OutputDevice* pOutDev, tools::Long nOnePixelX, tools::Long nOnePixelY );
+ ~ScGridMerger();
+
+ void AddHorLine(bool bWorksInPixels, tools::Long nX1, tools::Long nX2, tools::Long nY, bool bDashed = false);
+ void AddVerLine(bool bWorksInPixels, tools::Long nX, tools::Long nY1, tools::Long nY2, bool bDashed = false);
+ void Flush();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx
new file mode 100644
index 0000000000..37c38fe069
--- /dev/null
+++ b/sc/source/ui/inc/gridwin.hxx
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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/DocWindow.hxx>
+#include <vcl/transfer.hxx>
+#include "viewutil.hxx"
+#include "viewdata.hxx"
+#include "cbutton.hxx"
+#include "checklistmenu.hxx"
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <o3tl/deleter.hxx>
+#include <vcl/window.hxx>
+
+#include <memory>
+#include <vector>
+
+
+namespace editeng {
+ struct MisspellRanges;
+}
+
+namespace sc {
+ class SpellCheckContext;
+}
+
+namespace sdr::overlay { class OverlayManager; }
+
+class FmFormView;
+struct ScTableInfo;
+struct ScDragData;
+class ScDPObject;
+class ScDPFieldButton;
+class ScOutputData;
+class SdrObject;
+class SdrEditView;
+class ScNoteMarker;
+class SdrHdlList;
+class ScTransferObj;
+struct SpellCallbackInfo;
+class ScLokRTLContext;
+
+ // mouse status (nMouseStatus)
+
+#define SC_GM_NONE 0
+#define SC_GM_TABDOWN 1
+#define SC_GM_DBLDOWN 2
+#define SC_GM_FILTER 3
+#define SC_GM_IGNORE 4
+#define SC_GM_WATERUNDO 5
+#define SC_GM_URLDOWN 6
+
+ // page drag mode
+
+#define SC_PD_NONE 0
+#define SC_PD_RANGE_L 1
+#define SC_PD_RANGE_R 2
+#define SC_PD_RANGE_T 4
+#define SC_PD_RANGE_B 8
+#define SC_PD_RANGE_TL (SC_PD_RANGE_T|SC_PD_RANGE_L)
+#define SC_PD_RANGE_TR (SC_PD_RANGE_T|SC_PD_RANGE_R)
+#define SC_PD_RANGE_BL (SC_PD_RANGE_B|SC_PD_RANGE_L)
+#define SC_PD_RANGE_BR (SC_PD_RANGE_B|SC_PD_RANGE_R)
+#define SC_PD_BREAK_H 16
+#define SC_PD_BREAK_V 32
+
+// predefines
+namespace sdr::overlay { class OverlayObjectList; }
+
+class ScFilterListBox;
+struct ScDPLabelData;
+
+class SAL_DLLPUBLIC_RTTI ScGridWindow : public vcl::DocWindow, public DropTargetHelper, public DragSourceHelper
+{
+ // ScFilterListBox is always used for selection list
+ friend class ScFilterListBox;
+
+ enum RfCorner
+ {
+ NONE,
+ LEFT_UP,
+ RIGHT_UP,
+ LEFT_DOWN,
+ RIGHT_DOWN
+ };
+
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOCursors;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOSelection;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOHighlight;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOSelectionBorder;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOAutoFill;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOODragRect;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOHeader;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOShrink;
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mpOOSparklineGroup;
+
+ std::optional<tools::Rectangle> mpAutoFillRect;
+
+ /// LibreOfficeKit needs a persistent FmFormView for tiled rendering,
+ /// otherwise the invalidations from drawinglayer do not work.
+ std::unique_ptr<FmFormView> mpLOKDrawView;
+
+ struct MouseEventState;
+
+ /**
+ * Stores current visible column and row ranges, used to avoid expensive
+ * operations on objects that are outside visible area.
+ */
+ struct VisibleRange
+ {
+ SCCOL mnCol1;
+ SCCOL mnCol2;
+ SCROW mnRow1;
+ SCROW mnRow2;
+
+ VisibleRange(const ScDocument&);
+
+ bool isInside(SCCOL nCol, SCROW nRow) const;
+ bool set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2);
+ };
+
+ VisibleRange maVisibleRange;
+
+ struct LOKCursorEntry
+ {
+ Fraction aScaleX;
+ Fraction aScaleY;
+ tools::Rectangle aRect;
+ };
+
+ // Stores the last cursor position in twips for all
+ // zoom levels demanded from a ScGridWindow instance.
+ std::vector<LOKCursorEntry> maLOKLastCursor;
+
+ std::shared_ptr<sc::SpellCheckContext> mpSpellCheckCxt;
+
+ ScViewData& mrViewData;
+ ScSplitPos eWhich;
+ ScHSplitPos eHWhich;
+ ScVSplitPos eVWhich;
+
+ std::unique_ptr<ScNoteMarker, o3tl::default_delete<ScNoteMarker>> mpNoteMarker;
+
+ std::shared_ptr<ScFilterListBox> mpFilterBox;
+ std::unique_ptr<ScCheckListMenuControl> mpAutoFilterPopup;
+ std::unique_ptr<ScCheckListMenuControl> mpDPFieldPopup;
+ std::unique_ptr<ScDPFieldButton> mpFilterButton;
+
+ ScCheckListMenuControl::ResultType aSaveAutoFilterResult;
+
+ sal_uInt16 nCursorHideCount;
+
+ sal_uInt16 nButtonDown;
+ sal_uInt8 nMouseStatus;
+ enum class ScNestedButtonState { NONE, Down, Up };
+ ScNestedButtonState nNestedButtonState; // track nested button up/down calls
+
+ tools::Long nDPField;
+ ScDPObject* pDragDPObj; //! name?
+
+ sal_uInt16 nRFIndex;
+ SCCOL nRFAddX;
+ SCROW nRFAddY;
+
+ sal_uInt16 nPagebreakMouse; // Page break mode, Drag
+ SCCOLROW nPagebreakBreak;
+ SCCOLROW nPagebreakPrev;
+ ScRange aPagebreakSource;
+ ScRange aPagebreakDrag;
+
+ SvtScriptType nPageScript;
+
+ SCCOL nDragStartX;
+ SCROW nDragStartY;
+ SCCOL nDragEndX;
+ SCROW nDragEndY;
+ InsCellCmd meDragInsertMode;
+
+ ScDDComboBoxButton aComboButton;
+
+ Point aCurMousePos;
+
+ sal_uInt16 nPaintCount;
+ tools::Rectangle aRepaintPixel;
+
+ ScAddress aAutoMarkPos;
+ ScAddress aListValPos;
+
+ tools::Rectangle aInvertRect;
+
+ RfCorner aRFSelectedCorned;
+
+ Timer maShowPageBreaksTimer;
+
+ bool bEEMouse:1; // Edit Engine has mouse
+ bool bDPMouse:1; // DataPilot D&D (new Pivot table)
+ bool bRFMouse:1; // RangeFinder drag
+ bool bRFSize:1;
+ bool bPagebreakDrawn:1;
+ bool bDragRect:1;
+ bool bIsInPaint:1;
+ bool bNeedsRepaint:1;
+ bool bAutoMarkVisible:1;
+ bool bListValButton:1;
+ bool bInitialPageBreaks:1;
+
+ DECL_DLLPRIVATE_LINK( PopupModeEndHdl, weld::Popover&, void );
+ DECL_DLLPRIVATE_LINK( PopupSpellingHdl, SpellCallbackInfo&, void );
+
+ bool TestMouse( const MouseEvent& rMEvt, bool bAction );
+
+ bool DoPageFieldSelection( SCCOL nCol, SCROW nRow );
+ bool DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt );
+ void DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField );
+ void DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt );
+
+ void DPMouseMove( const MouseEvent& rMEvt );
+ void DPMouseButtonUp( const MouseEvent& rMEvt );
+ void DPTestMouse( const MouseEvent& rMEvt, bool bMove );
+
+ /**
+ * Check if the mouse click is on a field popup button.
+ *
+ * @return true if the field popup menu has been launched and no further
+ * mouse event handling is necessary, false otherwise.
+ */
+ bool DPTestFieldPopupArrow(const MouseEvent& rMEvt, const ScAddress& rPos, const ScAddress& rDimPos, ScDPObject* pDPObj);
+ bool DPTestMultiFieldPopupArrow(const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj);
+
+ void DPPopulateFieldMembers(const ScDPLabelData& rLabelData);
+ void DPSetupFieldPopup(std::unique_ptr<ScCheckListMenuControl::ExtendedData> pDPData, bool bDimOrientNotPage,
+ ScDPObject* pDPObj, bool bMultiField = false);
+ void DPConfigFieldPopup();
+ void DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize, const ScAddress& rPos, ScDPObject* pDPObj);
+ void DPLaunchMultiFieldPopupMenu(const Point& rScrPos, const Size& rScrSize, ScDPObject* pDPObj,
+ css::sheet::DataPilotFieldOrientation nOrient);
+
+ void RFMouseMove( const MouseEvent& rMEvt, bool bUp );
+
+ void PagebreakMove( const MouseEvent& rMEvt, bool bUp );
+
+ void UpdateDragRect( bool bShowRange, const tools::Rectangle& rPosRect );
+
+ bool IsAutoFilterActive( SCCOL nCol, SCROW nRow, SCTAB nTab );
+ void FilterSelect( sal_uLong nSel );
+
+ void ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr );
+
+ bool HasScenarioButton( const Point& rPosPixel, ScRange& rScenRange );
+
+ void DropScroll( const Point& rMousePos );
+
+ sal_Int8 AcceptPrivateDrop( const AcceptDropEvent& rEvt, const ScDragData& rData );
+ sal_Int8 ExecutePrivateDrop( const ExecuteDropEvent& rEvt, const ScDragData& rData );
+ sal_Int8 DropTransferObj( ScTransferObj* pTransObj, SCCOL nDestPosX, SCROW nDestPosY,
+ const Point& rLogicPos, sal_Int8 nDndAction );
+
+ void HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventState& rState );
+
+ bool DrawMouseButtonDown(const MouseEvent& rMEvt);
+ bool DrawMouseButtonUp(const MouseEvent& rMEvt);
+ bool DrawMouseMove(const MouseEvent& rMEvt);
+ bool DrawKeyInput(const KeyEvent& rKEvt, vcl::Window* pWin);
+ bool DrawCommand(const CommandEvent& rCEvt);
+ bool DrawHasMarkedObj();
+ void DrawEndAction();
+ void DrawMarkDropObj( SdrObject* pObj );
+ bool IsMyModel(const SdrEditView* pSdrView);
+
+ void DrawRedraw( ScOutputData& rOutputData, SdrLayerID nLayer );
+ void DrawSdrGrid( const tools::Rectangle& rDrawingRect, OutputDevice* pContentDev );
+ void DrawAfterScroll();
+ tools::Rectangle GetListValButtonRect( const ScAddress& rButtonPos );
+
+ void DrawHiddenIndicator( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext);
+ void DrawPagePreview( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext);
+
+ bool GetEditUrl( const Point& rPos,
+ OUString* pName=nullptr, OUString* pUrl=nullptr, OUString* pTarget=nullptr );
+
+ bool HitRangeFinder( const Point& rMouse, RfCorner& rCorner, sal_uInt16* pIndex,
+ SCCOL* pAddX, SCROW* pAddY );
+
+ sal_uInt16 HitPageBreak( const Point& rMouse, ScRange* pSource,
+ SCCOLROW* pBreak, SCCOLROW* pPrev );
+
+ void PasteSelection( const Point& rPosPixel );
+
+ void SelectForContextMenu( const Point& rPosPixel, SCCOL nCellX, SCROW nCellY );
+
+ void GetSelectionRects( ::std::vector< tools::Rectangle >& rPixelRects ) const;
+ void GetSelectionRectsPrintTwips(::std::vector< tools::Rectangle >& rRects) const;
+ void GetPixelRectsFor( const ScMarkData &rMarkData,
+ ::std::vector< tools::Rectangle >& rPixelRects ) const;
+ void GetRectsAnyFor(const ScMarkData &rMarkData,
+ ::std::vector< tools::Rectangle >& rRects, bool bInPrintTwips) const;
+ void UpdateKitSelection(const std::vector<tools::Rectangle>& rRectangles,
+ std::vector<tools::Rectangle>* pLogicRects = nullptr);
+ bool NeedLOKCursorInvalidation(const tools::Rectangle& rCursorRect,
+ const Fraction aScaleX, const Fraction aScaleY);
+ void InvalidateLOKViewCursor(const tools::Rectangle& rCursorRect,
+ const Fraction aScaleX, const Fraction aScaleY);
+
+ void SetupInitialPageBreaks(const ScDocument& rDoc, SCTAB nTab);
+ DECL_DLLPRIVATE_LINK(InitiatePageBreaksTimer, Timer*, void);
+
+protected:
+ virtual void PrePaint(vcl::RenderContext& rRenderContext) override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+ virtual void RequestHelp( const HelpEvent& rEvt ) 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;
+
+public:
+ enum class AutoFilterMode
+ {
+ Normal,
+ Empty,
+ NonEmpty,
+ Top10,
+ Bottom10,
+ Custom,
+ TextColor,
+ BackgroundColor,
+ SortAscending,
+ SortDescending,
+ Clear
+ };
+
+ ScGridWindow( vcl::Window* pParent, ScViewData& rData, ScSplitPos eWhichPos );
+ virtual ~ScGridWindow() override;
+ virtual void dispose() override;
+
+ virtual void KeyInput(const KeyEvent& rKEvt) override;
+ // #i70788# flush and get overlay
+ rtl::Reference<sdr::overlay::OverlayManager> getOverlayManager() const;
+ void flushOverlayManager();
+
+ virtual OUString GetSurroundingText() const override;
+ virtual Selection GetSurroundingTextSelection() const override;
+ virtual bool DeleteSurroundingText(const Selection& rSelection) override;
+
+ virtual void Command( const CommandEvent& rCEvt ) override;
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool PreNotify( NotifyEvent& rNEvt ) override;
+ virtual void Tracking( const TrackingEvent& rTEvt ) override;
+
+ void PaintTile( VirtualDevice& rDevice,
+ int nOutputWidth, int nOutputHeight,
+ int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight,
+ SCCOL nTiledRenderingAreaEndCol, SCROW nTiledRenderingAreaEndRow );
+
+ /// @see Window::LogicInvalidate().
+ void LogicInvalidate(const tools::Rectangle* pRectangle) override;
+ void LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart);
+
+ /// Update the cell selection according to what handles have been dragged.
+ /// @see vcl::ITiledRenderable::setTextSelection() for the values of nType.
+ /// Coordinates are in pixels.
+ void SetCellSelectionPixel(int nType, int nPixelX, int nPixelY);
+ /// Get the cell selection, coordinates are in logic units.
+ void GetCellSelection(std::vector<tools::Rectangle>& rLogicRects);
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
+
+ void FakeButtonUp();
+
+ const Point& GetMousePosPixel() const { return aCurMousePos; }
+ void UpdateStatusPosSize();
+
+ void ClickExtern();
+
+ using Window::SetPointer;
+
+ void MoveMouseStatus( ScGridWindow &rDestWin );
+
+ void ScrollPixel( tools::Long nDifX, tools::Long nDifY );
+ void UpdateEditViewPos();
+
+ void UpdateFormulas(SCCOL nX1 = -1, SCROW nY1 = -1, SCCOL nX2 = -1, SCROW nY2 = -1);
+
+ void ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL);
+
+ void LaunchDataSelectMenu( SCCOL nCol, SCROW nRow );
+ void DoScenarioMenu( const ScRange& rScenRange );
+
+ void LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow);
+ void RefreshAutoFilterButton(const ScAddress& rPos);
+ void UpdateAutoFilterFromMenu(AutoFilterMode eMode);
+
+ void LaunchPageFieldMenu( SCCOL nCol, SCROW nRow );
+ void LaunchDPFieldMenu( SCCOL nCol, SCROW nRow );
+
+ css::sheet::DataPilotFieldOrientation GetDPFieldOrientation( SCCOL nCol, SCROW nRow ) const;
+
+ void DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize,
+ tools::Long nDimIndex, ScDPObject* pDPObj);
+
+ void DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo, OutputDevice* pContentDev,
+ const ScLokRTLContext* pLokRTLContext);
+
+ using Window::Draw;
+ void Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ ScUpdateMode eMode );
+
+ /// Draw content of the gridwindow; shared between the desktop and the tiled rendering.
+ void DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData, bool bLogicText);
+
+ void CreateAnchorHandle(SdrHdlList& rHdl, const ScAddress& rAddress);
+
+ void HideCursor();
+ void ShowCursor();
+ void UpdateAutoFillMark(bool bMarked, const ScRange& rMarkRange);
+
+ void UpdateListValPos( bool bVisible, const ScAddress& rPos );
+
+ bool ShowNoteMarker( SCCOL nPosX, SCROW nPosY, bool bKeyboard );
+ void HideNoteMarker();
+
+ /// MapMode for the drawinglayer objects.
+ MapMode GetDrawMapMode( bool bForce = false );
+
+ void StopMarking();
+ void UpdateInputContext();
+
+ bool NeedsRepaint() const { return bNeedsRepaint; }
+
+ void DoInvertRect( const tools::Rectangle& rPixel );
+
+ void CheckNeedsRepaint();
+
+ void UpdateDPPopupMenuForFieldChange();
+ void UpdateDPFromFieldPopupMenu();
+ bool UpdateVisibleRange();
+
+ void CursorChanged();
+ void DrawLayerCreated();
+ void SetAutoSpellContext( const std::shared_ptr<sc::SpellCheckContext> &ctx );
+ void ResetAutoSpell();
+ void ResetAutoSpellForContentChange();
+ void SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges );
+ const std::vector<editeng::MisspellRanges>* GetAutoSpellData( SCCOL nPosX, SCROW nPosY );
+ bool InsideVisibleRange( SCCOL nPosX, SCROW nPosY );
+
+ void UpdateSparklineGroupOverlay();
+ void DeleteSparklineGroupOverlay();
+ void DeleteCopySourceOverlay();
+ void UpdateCopySourceOverlay();
+ void DeleteCursorOverlay();
+ void UpdateCursorOverlay();
+ void DeleteSelectionOverlay();
+ void UpdateSelectionOverlay();
+ void UpdateHighlightOverlay();
+ void DeleteAutoFillOverlay();
+ void UpdateAutoFillOverlay();
+ void DeleteDragRectOverlay();
+ void UpdateDragRectOverlay();
+ void DeleteHeaderOverlay();
+ void UpdateHeaderOverlay();
+ void DeleteShrinkOverlay();
+ void UpdateShrinkOverlay();
+ void UpdateAllOverlays();
+
+ /// get Cell cursor in this view's co-ordinate system @see ScModelObj::getCellCursor().
+ OString getCellCursor() const;
+ void notifyKitCellCursor() const;
+ void notifyKitCellViewCursor(const SfxViewShell* pForShell) const;
+ void updateKitCellCursor(const SfxViewShell* pOtherShell) const;
+ /// notify this view with new positions for other view's cursors (after zoom)
+ void updateKitOtherCursors() const;
+ void updateOtherKitSelections() const;
+
+ void notifyKitCellFollowJump() const;
+
+ ScViewData& getViewData();
+ virtual FactoryFunction GetUITestFactory() const override;
+
+ void updateLOKValListButton(bool bVisible, const ScAddress& rPos) const;
+ void updateLOKInputHelp(const OUString& title, const OUString& content) const;
+
+ void initiatePageBreaks();
+
+protected:
+ void ImpCreateOverlayObjects();
+ void ImpDestroyOverlayObjects();
+
+private:
+ SCCOL m_nDownPosX;
+ SCROW m_nDownPosY;
+
+#ifdef DBG_UTIL
+ void dumpCellProperties();
+ void dumpColumnInformationPixel();
+ void dumpColumnInformationHmm();
+ void dumpGraphicInformation();
+ void dumpColumnCellStorage();
+#endif
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/groupdlg.hxx b/sc/source/ui/inc/groupdlg.hxx
new file mode 100644
index 0000000000..26c3129b3d
--- /dev/null
+++ b/sc/source/ui/inc/groupdlg.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScGroupDlg : public weld::GenericDialogController
+{
+public:
+ ScGroupDlg(weld::Window* pParent, bool bUnGroup, bool bRows);
+ virtual ~ScGroupDlg() override;
+ bool GetColsChecked() const;
+
+private:
+ std::unique_ptr<weld::RadioButton> m_xBtnRows;
+ std::unique_ptr<weld::RadioButton> m_xBtnCols;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/hdrcont.hxx b/sc/source/ui/inc/hdrcont.hxx
new file mode 100644
index 0000000000..149db145e2
--- /dev/null
+++ b/sc/source/ui/inc/hdrcont.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/window.hxx>
+#include <vcl/timer.hxx>
+#include <types.hxx>
+
+#define HDR_SIZE_OPTIMUM 0xFFFF
+
+// Size of the sliders
+#define HDR_SLIDERSIZE 2
+
+class ScTabView;
+class SelectionEngine;
+
+class ScHeaderControl : public vcl::Window
+{
+private:
+ SelectionEngine* pSelEngine;
+ Timer aShowHelpTimer;
+ vcl::Font aNormFont;
+ vcl::Font aBoldFont;
+ vcl::Font aAutoFilterFont;
+ bool bBoldSet;
+ bool bAutoFilterSet;
+
+ bool bVertical; // Vertical = Row header
+
+ tools::Long nWidth;
+ tools::Long nSmallWidth;
+ tools::Long nBigWidth;
+
+ SCCOLROW nSize;
+
+ SCCOLROW nMarkStart;
+ SCCOLROW nMarkEnd;
+ bool bMarkRange;
+
+ bool bDragging; // Resizing
+ SCCOLROW nDragNo;
+ tools::Long nDragStart;
+ tools::Long nDragPos;
+ void* nTipVisible;
+ bool bDragMoved;
+
+ bool bIgnoreMove;
+
+ bool bInRefMode;
+
+ tools::Long GetScrPos( SCCOLROW nEntryNo ) const;
+ SCCOLROW GetMousePos(const Point& rPos, bool& rBorder) const;
+ bool IsSelectionAllowed(SCCOLROW nPos) const;
+ void ShowDragHelp();
+ void HideDragHelp();
+
+ void DoPaint( SCCOLROW nStart, SCCOLROW nEnd );
+
+ DECL_LINK(ShowDragHelpHdl, Timer*, void);
+
+protected:
+ ScTabView* pTabView;
+
+ // Window overrides
+
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void Tracking( const TrackingEvent& rTEvt ) override;
+
+ virtual void RequestHelp( const HelpEvent& rHEvt ) override;
+
+ // new methods
+
+ virtual SCCOLROW GetPos() const = 0; // current position (Scrolling)
+ virtual sal_uInt16 GetEntrySize( SCCOLROW nEntryNo ) const = 0; // width / height (Pixel)
+ virtual OUString GetEntryText( SCCOLROW nEntryNo ) const = 0;
+
+ virtual SCCOLROW GetHiddenCount( SCCOLROW nEntryNo ) const;
+ virtual bool IsLayoutRTL() const;
+ virtual bool IsMirrored() const;
+
+ virtual void SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewWidth ) = 0;
+ virtual void HideEntries( SCCOLROW nStart, SCCOLROW nEnd ) = 0;
+
+ virtual void SetMarking( bool bSet );
+ virtual void SelectWindow();
+ virtual bool IsDisabled() const;
+ virtual bool ResizeAllowed() const;
+ virtual OUString GetDragHelp( tools::Long nVal );
+
+ virtual void DrawInvert( tools::Long nDragPos );
+ virtual void Command( const CommandEvent& rCEvt ) override;
+ virtual void dispose() override;
+
+public:
+ ScHeaderControl( vcl::Window* pParent, SelectionEngine* pSelectionEngine,
+ SCCOLROW nNewSize, bool bNewVertical, ScTabView* pTab );
+ virtual ~ScHeaderControl() override;
+
+ void SetIgnoreMove(bool bSet) { bIgnoreMove = bSet; }
+
+ void StopMarking();
+
+ void SetMark( bool bNewSet, SCCOLROW nNewStart, SCCOLROW nNewEnd );
+
+ tools::Long GetWidth() const { return nWidth; }
+ tools::Long GetSmallWidth() const { return nSmallWidth; }
+ tools::Long GetBigWidth() const { return nBigWidth; }
+ void SetWidth( tools::Long nNew );
+ void GetMarkRange(SCCOLROW& rStart, SCCOLROW& rEnd) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/hfedtdlg.hxx b/sc/source/ui/inc/hfedtdlg.hxx
new file mode 100644
index 0000000000..6bec2d9958
--- /dev/null
+++ b/sc/source/ui/inc/hfedtdlg.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <sfx2/tabdlg.hxx>
+#include <editeng/svxenum.hxx>
+
+class ScHFEditDlg : public SfxTabDialogController
+{
+ SvxNumType eNumType;
+protected:
+ ScHFEditDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle,
+ const OUString& rUIXMLDescription, const OUString& rID);
+public:
+ virtual void PageCreated(const OUString& rId, SfxTabPage& rPage) override;
+};
+
+class ScHFEditHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditSharedFirstHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditSharedFirstHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditSharedFirstFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditSharedFirstFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditSharedLeftHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditSharedLeftHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditSharedLeftFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditSharedLeftFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditFirstHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditFirstHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditLeftHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditLeftHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditRightHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditRightHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditFirstFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditFirstFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditLeftFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditLeftFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditRightFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditRightFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditSharedHeaderDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditSharedHeaderDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditSharedFooterDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditSharedFooterDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditAllDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditAllDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+class ScHFEditActiveDlg : public ScHFEditDlg
+{
+public:
+ ScHFEditActiveDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet, std::u16string_view rPageStyle);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/highred.hxx b/sc/source/ui/inc/highred.hxx
new file mode 100644
index 0000000000..cfb706a8ec
--- /dev/null
+++ b/sc/source/ui/inc/highred.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+
+#include <svx/ctredlin.hxx>
+#include <chgviset.hxx>
+
+class ScViewData;
+class ScDocument;
+
+
+class ScHighlightChgDlg : public ScAnyRefDlgController
+{
+private:
+ ScViewData& m_rViewData;
+ ScDocument& rDoc;
+ ScChangeViewSettings aChangeViewSet;
+
+ std::unique_ptr<weld::CheckButton> m_xHighlightBox;
+ std::unique_ptr<weld::CheckButton> m_xCbAccept;
+ std::unique_ptr<weld::CheckButton> m_xCbReject;
+ std::unique_ptr<weld::Button> m_xOkButton;
+
+ std::unique_ptr<formula::RefEdit> m_xEdAssign;
+ std::unique_ptr<formula::RefButton> m_xRbAssign;
+
+ std::unique_ptr<weld::Container> m_xBox;
+
+ std::unique_ptr<SvxTPFilter> m_xFilterCtr;
+
+ void Init();
+
+ DECL_LINK( RefHandle, SvxTPFilter*, void );
+ DECL_LINK( HighlightHandle, weld::Toggleable&, void );
+ DECL_LINK( OKBtnHdl, weld::Button&, void );
+
+protected:
+
+ virtual void RefInputDone( bool bForced = false ) override;
+
+public:
+ ScHighlightChgDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData);
+
+ virtual ~ScHighlightChgDlg() override;
+
+ virtual void SetActive() override;
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual void Close() override;
+ virtual bool IsRefInputMode() const override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/hiranges.hxx b/sc/source/ui/inc/hiranges.hxx
new file mode 100644
index 0000000000..9689e3593e
--- /dev/null
+++ b/sc/source/ui/inc/hiranges.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <tools/color.hxx>
+#include <address.hxx>
+
+struct ScHighlightEntry
+{
+ ScRange aRef;
+ Color aColor;
+
+ ScHighlightEntry( const ScRange& rR, const Color& rC ) :
+ aRef(rR), aColor(rC) {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/imoptdlg.hxx b/sc/source/ui/inc/imoptdlg.hxx
new file mode 100644
index 0000000000..91bd9e460a
--- /dev/null
+++ b/sc/source/ui/inc/imoptdlg.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 .
+ */
+
+#pragma once
+
+#include <rtl/textenc.h>
+#include <rtl/ustring.hxx>
+#include <scdllapi.h>
+
+class SC_DLLPUBLIC ScImportOptions
+{
+public:
+ ScImportOptions( std::u16string_view rStr );
+
+ ScImportOptions( sal_Unicode nFieldSep, sal_Unicode nTextSep, rtl_TextEncoding nEnc )
+ : nFieldSepCode(nFieldSep), nTextSepCode(nTextSep),
+ bFixedWidth(false), bSaveAsShown(false), bQuoteAllText(false),
+ bSaveNumberAsSuch(true), bSaveFormulas(false), bRemoveSpace(false),
+ bEvaluateFormulas(true), bIncludeBOM(false), nSheetToExport(0)
+ { SetTextEncoding( nEnc ); }
+
+ ScImportOptions& operator=( const ScImportOptions& rCpy ) = default;
+
+ OUString BuildString() const;
+
+ void SetTextEncoding( rtl_TextEncoding nEnc );
+
+ sal_Unicode nFieldSepCode;
+ sal_Unicode nTextSepCode;
+ OUString aStrFont;
+ rtl_TextEncoding eCharSet;
+ bool bFixedWidth;
+ bool bSaveAsShown;
+ bool bQuoteAllText;
+ bool bSaveNumberAsSuch;
+ bool bSaveFormulas;
+ bool bRemoveSpace;
+ bool bEvaluateFormulas;
+ bool bIncludeBOM;
+ // "0" for 'current sheet', "-1" for all sheets (each to a separate file),
+ // or 1-based specific sheet number (to a separate file).
+ sal_Int32 nSheetToExport;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/impex.hxx b/sc/source/ui/inc/impex.hxx
new file mode 100644
index 0000000000..c33c151f81
--- /dev/null
+++ b/sc/source/ui/inc/impex.hxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <o3tl/deleter.hxx>
+#include <sot/formats.hxx>
+#include <address.hxx>
+#include <tools/stream.hxx>
+
+#include <com/sun/star/uno/Any.hxx>
+
+class ScDocShell;
+class ScDocument;
+class ScAsciiOptions;
+
+/**
+ * These options control how multi-line cells are converted during export in
+ * certain lossy formats (such as csv).
+ */
+struct ScExportTextOptions
+{
+ enum NewlineConversion { ToSystem, ToSpace, None };
+ ScExportTextOptions( NewlineConversion eNewlineConversion = ToSystem, sal_Unicode cSeparatorConvertTo = 0, bool bAddQuotes = false ) :
+ meNewlineConversion( eNewlineConversion ), mcSeparatorConvertTo( cSeparatorConvertTo ), mbAddQuotes( bAddQuotes ) {}
+
+ NewlineConversion meNewlineConversion;
+ sal_Unicode mcSeparatorConvertTo; // Convert separator to this character
+ bool mbAddQuotes;
+};
+
+class SC_DLLPUBLIC ScImportExport
+{
+ ScDocShell* pDocSh;
+ ScDocument& rDoc;
+ std::unique_ptr<ScDocument, o3tl::default_delete<ScDocument>> pUndoDoc;
+ ScRange aRange;
+ OUString aStreamPath;
+ OUString aNonConvertibleChars;
+ OUString maFilterOptions;
+ sal_uInt32 nSizeLimit;
+ SCROW nMaxImportRow;
+ sal_Unicode cSep; // Separator
+ sal_Unicode cStr; // String Delimiter
+ bool bFormulas; // Formula in Text?
+ bool bIncludeFiltered; // include filtered rows? (default true)
+ bool bAll; // no selection
+ bool bSingle; // Single selection
+ bool bUndo; // with Undo?
+ bool bOverflowRow; // too many rows
+ bool bOverflowCol; // too many columns
+ bool bOverflowCell; // too much data for a cell
+ bool mbApi;
+ bool mbImportBroadcast; // whether or not to broadcast after data import.
+ bool mbOverwriting; // Whether we could be overwriting existing values (paste).
+ // In this case we cannot use the insert optimization, but we
+ // do not need to broadcast after the import.
+ bool mbIncludeBOM; // Whether to include a byte-order-mark in the output.
+ ScExportTextOptions mExportTextOptions;
+
+ std::unique_ptr<ScAsciiOptions> pExtOptions; // extended options
+
+ bool StartPaste(); // Protect check, set up Undo
+ void EndPaste(bool bAutoRowHeight = true); // Undo/Redo actions, Repaint
+ bool Doc2Text( SvStream& );
+ bool Text2Doc( SvStream& );
+ bool Doc2Sylk( SvStream& );
+ bool Sylk2Doc( SvStream& );
+ bool Doc2HTML( SvStream&, const OUString& );
+ bool Doc2RTF( SvStream& );
+ bool Doc2Dif( SvStream& );
+ bool Dif2Doc( SvStream& );
+ bool ExtText2Doc( SvStream& ); // with pExtOptions
+ bool RTF2Doc( SvStream&, const OUString& rBaseURL );
+ bool HTML2Doc( SvStream&, const OUString& rBaseURL );
+
+public:
+ ScImportExport( ScDocument& ); // the whole document
+ ScImportExport( ScDocument&, const OUString& ); // Range/cell input
+ ScImportExport( ScDocument&, const ScAddress& );
+ ScImportExport( ScDocument&, const ScRange& );
+ ~ScImportExport() COVERITY_NOEXCEPT_FALSE;
+
+ void SetExtOptions( const ScAsciiOptions& rOpt );
+ void SetFilterOptions( const OUString& rFilterOptions );
+ bool IsRef() const { return !bAll; }
+
+ const ScRange& GetRange() const { return aRange; }
+
+ static void EmbeddedNullTreatment( OUString & rStr );
+
+ static bool IsFormatSupported( SotClipboardFormatId nFormat );
+ static const sal_Unicode* ScanNextFieldFromString( const sal_Unicode* p,
+ OUString& rField, sal_Unicode cStr, const sal_Unicode* pSeps,
+ bool bMergeSeps, bool& rbIsQuoted, bool& rbOverflowCell, bool bRemoveSpace );
+ static void WriteUnicodeOrByteString( SvStream& rStrm, std::u16string_view rString, bool bZero = false );
+ static void WriteUnicodeOrByteEndl( SvStream& rStrm );
+
+ /** ScImportExport::CountVisualWidth
+ Count the width of string visually ( in multiple of western characters), considering CJK
+ ideographs and CJK symbols (U+3000-U+303F) as twice the width of western characters.
+ @param rStr the string.
+ @param nIdx the starting index, index is incremented for each counted character.
+ @param nMaxWidth the maximum width to count.
+ @return the sum of the width of counted characters.
+ **/
+ static sal_Int32 CountVisualWidth(const OUString& rStr, sal_Int32& nIdx, sal_Int32 nMaxWidth);
+
+ /** ScImportExport::CountVisualWidth
+ @return the sum of the visual width of the whole string.
+ **/
+ static sal_Int32 CountVisualWidth(const OUString& rStr);
+
+ //! only if stream is only used in own (!) memory
+ static void SetNoEndianSwap( SvStream& rStrm );
+
+ void SetSeparator( sal_Unicode c ) { cSep = c; }
+ void SetDelimiter( sal_Unicode c ) { cStr = c; }
+ void SetFormulas( bool b ) { bFormulas = b; }
+ void SetIncludeFiltered( bool b ) { bIncludeFiltered = b; }
+
+ void SetStreamPath( const OUString& rPath ) { aStreamPath = rPath; }
+
+ bool ImportString( const OUString&, SotClipboardFormatId );
+ bool ExportString( OUString&, SotClipboardFormatId );
+ bool ExportByteString( OString&, rtl_TextEncoding, SotClipboardFormatId );
+
+ bool ImportStream( SvStream&, const OUString& rBaseURL, SotClipboardFormatId );
+ bool ExportStream( SvStream&, const OUString& rBaseURL, SotClipboardFormatId );
+
+ bool ExportData( std::u16string_view rMimeType,
+ css::uno::Any & rValue );
+
+ // after import
+ bool IsOverflowRow() const { return bOverflowRow; }
+ bool IsOverflowCol() const { return bOverflowCol; }
+ bool IsOverflowCell() const { return bOverflowCell; }
+ bool IsOverflow() const { return bOverflowRow || bOverflowCol || bOverflowCell; }
+
+ const OUString& GetNonConvertibleChars() const { return aNonConvertibleChars; }
+
+ void SetApi( bool bApi ) { mbApi = bApi; }
+ void SetImportBroadcast( bool b ) { mbImportBroadcast = b; }
+ void SetOverwriting( const bool bOverwriting ) { mbOverwriting = bOverwriting; }
+ void SetExportTextOptions( const ScExportTextOptions& options ) { mExportTextOptions = options; }
+
+ bool GetIncludeBOM() const { return mbIncludeBOM; }
+};
+
+// Helper class for importing clipboard strings as streams.
+class ScImportStringStream : public SvMemoryStream
+{
+public:
+ ScImportStringStream(const OUString& rStr);
+};
+
+/** Read a CSV (comma separated values) data line using
+ ReadUniOrByteStringLine().
+
+ @param bEmbeddedLineBreak
+ If TRUE and a line-break occurs inside a field of data,
+ a line feed LF '\n' and the next line are appended. Repeats
+ until a line-break is not in a field. A field is determined
+ by delimiting rFieldSeparators and optionally surrounded by
+ a pair of cFieldQuote characters. For a line-break to be
+ within a field, the field content MUST be surrounded by
+ cFieldQuote characters, and the opening cFieldQuote MUST be
+ at the very start of a line or follow right behind a field
+ separator with no extra characters in between, with the
+ exception of blanks contradictory to RFC 4180. Anything,
+ including field separators and escaped quotes (by doubling
+ them) may appear in a quoted field.
+
+ If bEmbeddedLineBreak==FALSE, nothing is parsed and the
+ string returned is simply one ReadUniOrByteStringLine().
+
+ @param rFieldSeparators
+ A list of characters that each may act as a field separator.
+ If rcDetectSep was 0 and a separator is detected then it is appended to
+ rFieldSeparators.
+
+ @param cFieldQuote
+ The quote character used.
+
+ @param rcDetectSep
+ If 0 then attempt to detect a possible separator if
+ rFieldSeparators doesn't include it already. This can be necessary because
+ of the "accept broken misquoted CSV fields" feature that tries to ignore
+ trailing blanks after a quoted field and if no separator follows continues
+ to add content to the field assuming the single double quote was in error.
+ It is also necessary if the only possible separator was not selected and
+ not included in rFieldSeparators and a line starts with a quoted field, in
+ which case appending lines is tried until end of file.
+ If a separator is detected it is added to rFieldSeparators and the
+ line is reread with the new separators
+
+ @param nMaxSourceLines
+ Maximum source lines to read and combine into one logical line for embedded
+ new line purpose. Should be limited for the preview dialog because only
+ non-matching separators selected otherwise would lead to trying to
+ concatenate lines until file end.
+ If 0 no limit other than the internal arbitrary resulting line length
+ limit.
+
+ check Stream::good() to detect IO problems during read
+
+ @ATTENTION
+ Note that the string returned may be truncated even inside
+ a quoted field if some (arbitrary) maximum length was reached.
+ There currently is no way to exactly determine the conditions,
+ whether this was at a line end, or whether open quotes
+ would have closed the field before the line end, as even a
+ ReadUniOrByteStringLine() may return prematurely but the
+ stream was positioned ahead until the real end of line.
+ Additionally, due to character encoding conversions, string
+ length and bytes read don't necessarily match, and
+ resyncing to a previous position matching the string's
+ length isn't always possible. As a result, a logical line
+ with embedded line breaks and more than the maximum length
+ characters will be spoiled, and a subsequent ReadCsvLine()
+ may start under false preconditions.
+
+ */
+SC_DLLPUBLIC OUString ReadCsvLine( SvStream &rStream, bool bEmbeddedLineBreak,
+ OUString& rFieldSeparators, sal_Unicode cFieldQuote, sal_Unicode& rcDetectSep,
+ sal_uInt32 nMaxSourceLines = 0 );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/inputhdl.hxx b/sc/source/ui/inc/inputhdl.hxx
new file mode 100644
index 0000000000..3067dd8193
--- /dev/null
+++ b/sc/source/ui/inc/inputhdl.hxx
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <global.hxx>
+#include <address.hxx>
+#include <tools/solar.h>
+#include <typedstrdata.hxx>
+
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <vcl/vclptr.hxx>
+#include <editeng/svxenum.hxx>
+#include "viewdata.hxx"
+
+#include <set>
+#include <memory>
+#include <vector>
+
+class ScDocument;
+class ScTabViewShell;
+class ScInputWindow;
+class ScPatternAttr;
+class ScEditEngineDefaulter;
+class EditView;
+class EditTextObject;
+class ScInputHdlState;
+class ScRangeFindList;
+class Timer;
+class KeyEvent;
+class CommandEvent;
+class VclWindowEvent;
+namespace vcl { class Window; }
+struct ReferenceMark;
+struct ESelection;
+
+// ScInputHandler
+
+class ScInputHandler final
+{
+private:
+ VclPtr<ScInputWindow> pInputWin;
+
+ std::unique_ptr<ScEditEngineDefaulter> mpEditEngine; ///< Edited data in the sheet (when the user clicks into the sheet, and starts writing there).
+ EditView* pTableView; // associated active EditView
+ EditView* pTopView; // EditView in the input row
+
+ std::unique_ptr<ScTypedCaseStrSet> pColumnData;
+ std::unique_ptr<ScTypedCaseStrSet> pFormulaData;
+ std::unique_ptr<ScTypedCaseStrSet> pFormulaDataPara;
+ ScTypedCaseStrSet::const_iterator miAutoPosColumn;
+ ScTypedCaseStrSet::const_iterator miAutoPosFormula;
+ std::set<sal_Unicode> maFormulaChar;
+
+ VclPtr<vcl::Window> pTipVisibleParent;
+ void* nTipVisible;
+ VclPtr<vcl::Window> pTipVisibleSecParent;
+ void* nTipVisibleSec;
+ OUString aManualTip;
+ OUString aAutoSearch;
+
+ OUString aCurrentText;
+
+ OUString aFormText; // for autopilot function
+ sal_Int32 nFormSelStart; // Selection for autopilot function
+ sal_Int32 nFormSelEnd;
+
+ sal_Unicode nCellPercentFormatDecSep; // 0:= no percent format, else which decimal separator
+
+ sal_uInt16 nAutoPar; // autom.parentheses than can be overwritten
+
+ ScAddress aCursorPos;
+ ScInputMode eMode;
+ bool bUseTab:1; // Scrolling possible
+ bool bTextValid:1; // Text is not in edit engine
+ bool bModified:1;
+ bool bSelIsRef:1;
+ bool bFormulaMode:1;
+ bool bInRangeUpdate:1;
+ bool bParenthesisShown:1;
+ bool bCreatingFuncView:1;
+ bool bInEnterHandler:1;
+ bool bCommandErrorShown:1;
+ bool bInOwnChange:1;
+
+ bool bProtected:1;
+ bool bLastIsSymbol:1;
+ bool mbDocumentDisposing:1;
+ /// To indicate if there is a partial prefix completion.
+ bool mbPartialPrefix:1;
+ bool mbEditingExistingContent:1;
+
+ sal_uLong nValidation;
+ SvxCellHorJustify eAttrAdjust;
+
+ Fraction aScaleX; // for ref MapMode
+ Fraction aScaleY;
+
+ ScTabViewShell* pRefViewSh;
+ ScTabViewShell* pActiveViewSh;
+
+ const ScPatternAttr* pLastPattern;
+ std::unique_ptr<SfxItemSet>
+ pEditDefaults;
+
+ std::unique_ptr<ScInputHdlState>
+ pLastState;
+ std::unique_ptr<Timer> pDelayTimer;
+
+ std::unique_ptr<ScRangeFindList>
+ pRangeFindList;
+
+private:
+ void UpdateActiveView();
+ void SyncViews( const EditView* pSourceView = nullptr );
+ /**
+ * @param cTyped typed character. If 0, look at existing document content
+ * for text or number.
+ * @param bInputActivated true if the cell input mode is activated (via
+ * F2), false otherwise.
+ * @param pTopEngine top window input line EditEngine. If not nullptr then
+ * some default attributes are merged to it from the
+ * table EditEngine.
+ * @return true if the new edit mode has been started.
+ */
+ bool StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated,
+ ScEditEngineDefaulter* pTopEngine );
+ void RemoveSelection();
+ bool StartsLikeFormula( std::u16string_view rStr ) const;
+ void UpdateFormulaMode();
+ static void InvalidateAttribs();
+ void ImplCreateEditEngine();
+ DECL_LINK( DelayTimer, Timer*, void );
+ void GetColData();
+ void UseColData();
+ void NextAutoEntry( bool bBack );
+ void UpdateAdjust( sal_Unicode cTyped );
+ void GetFormulaData();
+ void UseFormulaData();
+ void NextFormulaEntry( bool bBack );
+ void PasteFunctionData();
+ void PasteManualTip();
+ EditView* GetFuncEditView();
+ void RemoveAdjust();
+ void RemoveRangeFinder();
+ void DeleteRangeFinder();
+ void UpdateParenthesis();
+ void UpdateAutoCorrFlag();
+ void ResetAutoPar();
+ void AutoParAdded();
+ bool CursorAtClosingPar();
+ void SkipClosingPar();
+ bool GetFuncName( OUString& aStart, OUString& aResult ); // fdo75264
+ void ShowArgumentsTip( OUString& rSelText );
+ DECL_LINK( ModifyHdl, LinkParamNone*, void );
+ DECL_LINK( ShowHideTipVisibleParentListener, VclWindowEvent&, void );
+ DECL_LINK( ShowHideTipVisibleSecParentListener, VclWindowEvent&, void );
+
+public:
+ ScInputHandler(const ScInputHandler&) = delete;
+ const ScInputHandler& operator=(const ScInputHandler&) = delete;
+
+ ScInputHandler();
+ ~ScInputHandler();
+
+ void SetMode( ScInputMode eNewMode, const OUString* pInitText = nullptr,
+ ScEditEngineDefaulter* pTopEngine = nullptr );
+ bool IsInputMode() const { return (eMode != SC_INPUT_NONE); }
+ bool IsEditMode() const { return (eMode != SC_INPUT_NONE &&
+ eMode != SC_INPUT_TYPE); }
+ bool IsTopMode() const { return (eMode == SC_INPUT_TOP); }
+
+ const OUString& GetEditString();
+ const OUString& GetFormString() const { return aFormText; }
+
+ const ScAddress& GetCursorPos() const { return aCursorPos; }
+
+ bool GetTextAndFields( ScEditEngineDefaulter& rDestEngine );
+ void MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const;
+
+ bool KeyInput( const KeyEvent& rKEvt, bool bStartEdit );
+ void EnterHandler( ScEnterMode nBlockMode = ScEnterMode::NORMAL, bool bBeforeSavingInLOK = false );
+ void CancelHandler();
+ void SetReference( const ScRange& rRef, const ScDocument& rDoc );
+ void AddRefEntry();
+
+ void InputCommand( const CommandEvent& rCEvt );
+
+ OUString GetSurroundingText();
+ Selection GetSurroundingTextSelection();
+ bool DeleteSurroundingText(const Selection& rSelection);
+
+ void InsertFunction( const OUString& rFuncName, bool bAddPar = true );
+ void ClearText();
+
+ void InputSelection( const EditView* pView );
+ void InputChanged( const EditView* pView, bool bFromNotify );
+
+ void ViewShellGone(const ScTabViewShell* pViewSh);
+ void SetRefViewShell(ScTabViewShell* pRefVsh) {pRefViewSh=pRefVsh;}
+
+ void NotifyChange( const ScInputHdlState* pState, bool bForce = false,
+ ScTabViewShell* pSourceSh = nullptr,
+ bool bStopEditing = true);
+ void UpdateCellAdjust( SvxCellHorJustify eJust );
+
+ void ResetDelayTimer(); //BugId 54702
+
+ void HideTip();
+ void HideTipBelow();
+ void ShowTipCursor();
+ void ShowTip( const OUString& rText ); // at Cursor
+ void ShowTipBelow( const OUString& rText );
+ void ShowFuncList( const ::std::vector< OUString > & rFuncStrVec );
+
+ void SetRefScale( const Fraction& rX, const Fraction& rY );
+ void UpdateRefDevice();
+
+ EditView* GetActiveView();
+ EditView* GetTableView() { return pTableView; }
+ EditView* GetTopView() { return pTopView; }
+
+ bool DataChanging( sal_Unicode cTyped = 0, bool bFromCommand = false );
+ void DataChanged( bool bFromTopNotify = false, bool bSetModified = true );
+
+ bool TakesReturn() const { return ( nTipVisible != nullptr ); }
+
+ void SetModified() { bModified = true; }
+
+ bool GetSelIsRef() const { return bSelIsRef; }
+ void SetSelIsRef(bool bSet) { bSelIsRef = bSet; }
+
+ void ShowRefFrame();
+
+ ScRangeFindList* GetRangeFindList() { return pRangeFindList.get(); }
+
+ void UpdateRange( sal_uInt16 nIndex, const ScRange& rNew );
+
+ // Communication with the autopilot function
+ void InputGetSelection ( sal_Int32& rStart, sal_Int32& rEnd );
+ void InputSetSelection ( sal_Int32 nStart, sal_Int32 nEnd );
+ void InputReplaceSelection ( std::u16string_view aStr );
+ void InputTurnOffWinEngine();
+
+ bool IsFormulaMode() const { return bFormulaMode; }
+ ScInputWindow* GetInputWindow() { return pInputWin; }
+ void SetInputWindow( ScInputWindow* pNew );
+ void StopInputWinEngine( bool bAll );
+
+ bool IsInEnterHandler() const { return bInEnterHandler; }
+ bool IsInOwnChange() const { return bInOwnChange; }
+
+ /// Returns true if there is a partial autocomplete suggestion.
+ bool HasPartialComplete() const { return mbPartialPrefix; };
+
+ bool IsModalMode( const SfxObjectShell* pDocSh );
+
+ void ForgetLastPattern();
+
+ void UpdateSpellSettings( bool bFromStartTab = false );
+
+ void FormulaPreview();
+
+ Size GetTextSize(); // in 1/100mm
+
+ // actually private, public for SID_INPUT_SUM
+ void InitRangeFinder(const OUString& rFormula);
+
+ void UpdateLokReferenceMarks();
+ static void SendReferenceMarks( const SfxViewShell* pViewShell,
+ const std::vector<ReferenceMark>& rReferenceMarks );
+
+ void SetDocumentDisposing( bool b );
+
+ static ReferenceMark GetReferenceMark( const ScViewData& rViewData, ScDocShell* pDocSh,
+ tools::Long nX1, tools::Long nX2, tools::Long nY1, tools::Long nY2,
+ tools::Long nTab, const Color& rColor );
+
+ void LOKPasteFunctionData(const OUString& rFunctionName);
+};
+
+// ScInputHdlState
+
+class ScInputHdlState
+{
+ friend class ScInputHandler;
+
+public:
+ ScInputHdlState( const ScAddress& rCurPos,
+ const ScAddress& rStartPos,
+ const ScAddress& rEndPos,
+ OUString aString,
+ const EditTextObject* pData );
+ ScInputHdlState( const ScInputHdlState& rCpy );
+ ~ScInputHdlState();
+
+ ScInputHdlState& operator= ( const ScInputHdlState& r );
+ bool operator==( const ScInputHdlState& r ) const;
+
+ const ScAddress& GetPos() const { return aCursorPos; }
+ const ScAddress& GetStartPos() const { return aStartPos; }
+ const ScAddress& GetEndPos() const { return aEndPos; }
+ const OUString& GetString() const { return aString; }
+ const EditTextObject* GetEditData() const { return pEditData.get(); }
+
+private:
+ ScAddress aCursorPos;
+ ScAddress aStartPos;
+ ScAddress aEndPos;
+ OUString aString;
+ std::unique_ptr<EditTextObject> pEditData;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/inputwin.hxx b/sc/source/ui/inc/inputwin.hxx
new file mode 100644
index 0000000000..d98b5a85c5
--- /dev/null
+++ b/sc/source/ui/inc/inputwin.hxx
@@ -0,0 +1,370 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <vcl/customweld.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/childwin.hxx>
+#include <svl/lstner.hxx>
+#include <svtools/stringtransfer.hxx>
+#include <vcl/window.hxx>
+#include <formula/opcode.hxx>
+#include <svx/weldeditview.hxx>
+
+namespace com::sun::star::accessibility { class XAccessible; }
+
+class EditView;
+class ScAccessibleEditLineTextData;
+class ScAccessibleEditObject;
+class ScEditEngineDefaulter;
+class ScTextWndGroup;
+class ScInputBarGroup;
+class ScInputHandler;
+class ScTabViewShell;
+struct EENotify;
+
+class ScTextWndBase
+{
+public:
+ virtual void InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) = 0;
+ virtual void RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) = 0;
+ virtual void SetTextString( const OUString& rString, bool bKitUpdate ) = 0;
+ virtual const OUString& GetTextString() const = 0;
+ virtual void StartEditEngine() = 0;
+ virtual void StopEditEngine( bool bAll ) = 0;
+ virtual EditView* GetEditView() const = 0;
+ virtual bool HasEditView() const = 0;
+ virtual void MakeDialogEditView() = 0;
+ virtual void SetFormulaMode( bool bSet ) = 0;
+ virtual bool IsInputActive() = 0;
+ virtual void TextGrabFocus() = 0;
+ virtual tools::Long GetNumLines() const = 0;
+ virtual ~ScTextWndBase() {}
+};
+
+class ScTextWnd : public WeldEditView
+ , public ScTextWndBase
+{
+public:
+ ScTextWnd(ScTextWndGroup& rParent, ScTabViewShell* pViewSh);
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ virtual ~ScTextWnd() override;
+
+ virtual void SetTextString( const OUString& rString, bool bKitUpdate ) override;
+ virtual const OUString& GetTextString() const override;
+
+ bool IsInputActive() override;
+ virtual EditView* GetEditView() const override;
+ virtual bool HasEditView() const override;
+
+ const OutputDevice& GetEditViewDevice() const;
+
+ // for function autopilots
+ virtual void MakeDialogEditView() override;
+
+ virtual void StartEditEngine() override;
+ virtual void StopEditEngine( bool bAll ) override;
+
+ virtual void TextGrabFocus() override;
+
+ virtual void StyleUpdated() override;
+
+ // Triggered if scroll bar state should change
+ virtual void EditViewScrollStateChange() override;
+
+ virtual void SetFormulaMode( bool bSet ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
+
+ virtual void InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) override;
+ virtual void RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData ) override;
+
+ virtual void Resize() override;
+
+ int GetPixelHeightForLines(tools::Long nLines);
+ int GetEditEngTxtHeight() const;
+
+ virtual tools::Long GetNumLines() const override;
+ void SetNumLines(tools::Long nLines);
+ tools::Long GetLastNumExpandedLines() const { return mnLastExpandedLines; }
+ void SetLastNumExpandedLines(tools::Long nLastExpandedLines) { mnLastExpandedLines = nLastExpandedLines; }
+
+ void DoScroll();
+
+ DECL_LINK(ModifyHdl, LinkParamNone*, void);
+ DECL_LINK(EditStatusHdl, EditStatus&, void);
+
+protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual bool Command( const CommandEvent& rCEvt ) override;
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ virtual bool CanFocus() const override;
+ virtual void GetFocus() override;
+
+ virtual bool StartDrag() override;
+
+private:
+ void ImplInitSettings();
+ void UpdateAutoCorrFlag();
+
+ void SetScrollBarRange();
+
+ void InitEditEngine();
+
+ void UpdateFocus();
+
+ rtl::Reference<svt::OStringTransferable> m_xHelper;
+
+ typedef ::std::vector< ScAccessibleEditLineTextData* > AccTextDataVector;
+
+ rtl::Reference<ScAccessibleEditObject> pAcc;
+
+ OUString aString;
+ vcl::Font aTextFont;
+ AccTextDataVector maAccTextDatas; // #i105267# text data may be cloned, remember all copies
+ bool bIsRTL;
+ bool bIsInsertMode;
+ bool bFormulaMode;
+
+ // #102710#; this flag should be true if a key input or a command is handled
+ // it prevents the call of InputChanged in the ModifyHandler of the EditEngine
+ bool bInputMode;
+
+ ScTabViewShell* mpViewShell;
+ ScTextWndGroup& mrGroupBar;
+ tools::Long mnLastExpandedLines;
+ bool mbInvalidate;
+};
+
+class ScPosWnd final : public InterimItemWindow, public SfxListener // Display position
+{
+private:
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+
+ ImplSVEvent* m_nAsyncGetFocusId;
+
+ OUString aPosStr;
+ void* nTipVisible;
+ bool bFormulaMode;
+
+public:
+ ScPosWnd( vcl::Window* pParent );
+ virtual ~ScPosWnd() override;
+ virtual void dispose() override;
+
+ void SetPos( const OUString& rPosStr ); // Displayed Text
+ void SetFormulaMode( bool bSet );
+
+ static OUString createLocalRangeName(std::u16string_view rName, std::u16string_view rTableName);
+
+private:
+ DECL_LINK(OnAsyncGetFocus, void*, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+ DECL_LINK(ModifyHdl, weld::ComboBox&, void);
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+private:
+ void FillRangeNames();
+ void FillFunctions();
+ void DoEnter();
+ void HideTip();
+
+ void ReleaseFocus_Impl();
+};
+
+class ScTextWndGroup : public ScTextWndBase
+{
+public:
+ ScTextWndGroup(ScInputBarGroup& pParent, ScTabViewShell* pViewSh);
+ virtual ~ScTextWndGroup() override;
+
+ virtual void InsertAccessibleTextData(ScAccessibleEditLineTextData& rTextData) override;
+ virtual EditView* GetEditView() const override;
+ const OutputDevice& GetEditViewDevice() const;
+ Point GetCursorScreenPixelPos(bool bBelowLine);
+ tools::Long GetLastNumExpandedLines() const;
+ void SetLastNumExpandedLines(tools::Long nLastExpandedLines);
+ virtual tools::Long GetNumLines() const override;
+ int GetPixelHeightForLines(tools::Long nLines);
+ weld::ScrolledWindow& GetScrollWin();
+ virtual const OUString& GetTextString() const override;
+ virtual bool HasEditView() const override;
+ virtual bool IsInputActive() override;
+ virtual void MakeDialogEditView() override;
+ virtual void RemoveAccessibleTextData(ScAccessibleEditLineTextData& rTextData) override;
+ void SetScrollPolicy();
+ void SetNumLines(tools::Long nLines);
+ virtual void SetFormulaMode(bool bSet) override;
+ virtual void SetTextString(const OUString& rString, bool bKitUpdate) override;
+ virtual void StartEditEngine() override;
+ virtual void StopEditEngine(bool bAll) override;
+ virtual void TextGrabFocus() override;
+
+ vcl::Window& GetVclParent() { return mrParent; }
+
+private:
+ std::unique_ptr<ScTextWnd> mxTextWnd;
+ std::unique_ptr<weld::ScrolledWindow> mxScrollWin;
+ std::unique_ptr<weld::CustomWeld> mxTextWndWin;
+ vcl::Window& mrParent;
+
+ DECL_LINK(Impl_ScrollHdl, weld::ScrolledWindow&, void);
+};
+
+class ScInputBarGroup : public InterimItemWindow
+ , public ScTextWndBase
+{
+public:
+ ScInputBarGroup(vcl::Window* Parent, ScTabViewShell* pViewSh);
+ virtual ~ScInputBarGroup() override;
+ virtual void dispose() override;
+ virtual void InsertAccessibleTextData(ScAccessibleEditLineTextData& rTextData) override;
+ virtual void RemoveAccessibleTextData(ScAccessibleEditLineTextData& rTextData) override;
+ void SetTextString(const OUString& rString, bool bKitUpdate) override;
+ void StartEditEngine() override;
+ virtual EditView* GetEditView() const override;
+ virtual bool HasEditView() const override;
+ Point GetCursorScreenPixelPos(bool bBelowLine);
+ virtual void Resize() override;
+ virtual void DataChanged(const DataChangedEvent& rDCEvt) override;
+ virtual const OUString& GetTextString() const override;
+ virtual void StopEditEngine(bool bAll) override;
+ virtual void TextGrabFocus() override;
+ void SetFormulaMode(bool bSet) override;
+ void MakeDialogEditView() override;
+ bool IsInputActive() override;
+ void IncrementVerticalSize();
+ void DecrementVerticalSize();
+ void NumLinesChanged();
+ virtual tools::Long GetNumLines() const override { return mxTextWndGroup->GetNumLines(); }
+
+ int GetPixelHeightForLines(tools::Long nLines) const
+ {
+ return mxTextWndGroup->GetPixelHeightForLines(nLines);
+ }
+
+ weld::Builder& GetBuilder() { return *m_xBuilder; }
+
+private:
+ void TriggerToolboxLayout();
+ void SetBackgrounds();
+
+ std::unique_ptr<weld::Container> mxBackground;
+ std::unique_ptr<ScTextWndGroup> mxTextWndGroup;
+ std::unique_ptr<weld::Button> mxButtonUp;
+ std::unique_ptr<weld::Button> mxButtonDown;
+
+ DECL_LINK(ClickHdl, weld::Button&, void);
+};
+
+class ScInputWindow final : public ToolBox // Parent toolbox
+{
+public:
+ ScInputWindow( vcl::Window* pParent, const SfxBindings* pBind );
+ virtual ~ScInputWindow() override;
+ virtual void dispose() override;
+
+ virtual void PixelInvalidate(const tools::Rectangle* pRectangle) override;
+ virtual void SetSizePixel( const Size& rNewSize ) override;
+ virtual void Resize() override;
+ virtual void Select() override;
+
+ void SetFuncString( const OUString& rString, bool bDoEdit = true );
+ void SetPosString( const OUString& rStr );
+ void SetTextString(const OUString& rString, bool bKitUpdate);
+
+ void SetOkCancelMode();
+ void SetSumAssignMode();
+ void EnableButtons( bool bEnable );
+ /// Update Input bar after the number of lines was changed externally
+ void NumLinesChanged();
+
+ void StartFormula();
+ void SetFormulaMode( bool bSet );
+
+ bool IsInputActive();
+ EditView* GetEditView();
+ vcl::Window* GetEditWindow();
+ Point GetCursorScreenPixelPos(bool bBelowLine = false);
+
+ void TextGrabFocus();
+ void TextInvalidate();
+ void SwitchToTextWin();
+
+ void PosGrabFocus();
+
+ // For function autopilots
+ void MakeDialogEditView();
+
+ void StopEditEngine( bool bAll );
+
+ void SetInputHandler( ScInputHandler* pNew );
+
+ ScInputHandler* GetInputHandler(){ return pInputHdl;}
+
+ void StateChanged( StateChangedType nType ) override;
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+
+ void NotifyLOKClient();
+
+ void MenuHdl(std::u16string_view command);
+ DECL_LINK( DropdownClickHdl, ToolBox*, void );
+
+ void AutoSum( bool& bRangeFinder, bool& bSubTotal, OpCode eCode );
+
+private:
+ bool IsPointerAtResizePos();
+
+ VclPtr<ScPosWnd> aWndPos;
+ VclPtr<ScInputBarGroup> mxTextWindow;
+ ScInputHandler* pInputHdl;
+ ScTabViewShell* mpViewShell;
+ tools::Long mnMaxY;
+ tools::Long mnStandardItemHeight;
+ bool bIsOkCancelMode;
+ bool bInResize;
+};
+
+class ScInputWindowWrapper : public SfxChildWindow
+{
+public:
+ ScInputWindowWrapper( vcl::Window* pParent,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo );
+
+ SFX_DECL_CHILDWINDOW_WITHID(ScInputWindowWrapper);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/inscldlg.hxx b/sc/source/ui/inc/inscldlg.hxx
new file mode 100644
index 0000000000..b44bfb60ba
--- /dev/null
+++ b/sc/source/ui/inc/inscldlg.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+#include <global.hxx>
+
+class ScInsertCellDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::RadioButton> m_xBtnCellsDown;
+ std::unique_ptr<weld::RadioButton> m_xBtnCellsRight;
+ std::unique_ptr<weld::RadioButton> m_xBtnInsRow;
+ std::unique_ptr<weld::RadioButton> m_xBtnInsCol;
+
+public:
+ ScInsertCellDlg(weld::Window* pParent, bool bDisallowCellMove);
+ virtual ~ScInsertCellDlg() override;
+
+ InsCellCmd GetInsCellCmd() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/inscodlg.hxx b/sc/source/ui/inc/inscodlg.hxx
new file mode 100644
index 0000000000..31577f832e
--- /dev/null
+++ b/sc/source/ui/inc/inscodlg.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <global.hxx>
+
+#include "scui_def.hxx"
+
+class ScInsertContentsDlg : public weld::GenericDialogController
+{
+public:
+ ScInsertContentsDlg( weld::Window* pParent,
+ const OUString* pStrTitle );
+ virtual ~ScInsertContentsDlg() override;
+
+ InsertDeleteFlags GetInsContentsCmdBits() const;
+ ScPasteFunc GetFormulaCmdBits() const;
+
+ bool IsSkipEmptyCells() const;
+ bool IsTranspose() const;
+ bool IsLink() const;
+ InsCellCmd GetMoveMode() const;
+
+ void SetOtherDoc( bool bSet );
+ void SetFillMode( bool bSet );
+ void SetChangeTrack( bool bSet );
+ void SetCellShiftDisabled( CellShiftDisabledFlags nDisable );
+ void storeFlagsInRegistry();
+
+private:
+ bool bOtherDoc;
+ bool bFillMode;
+ bool bChangeTrack;
+ bool bMoveDownDisabled;
+ bool bMoveRightDisabled;
+ void SetInsContentsCmdBits(const InsertDeleteFlags eFlags);
+ void SetFormulaCmdBits(const ScPasteFunc eFlags);
+ void SetCellCmdFlags(const InsCellCmd eFlags);
+ void SetContentsFlags(const InsertContentsFlags eFlags);
+
+ std::unique_ptr<weld::CheckButton> mxBtnInsAll;
+ std::unique_ptr<weld::CheckButton> mxBtnInsStrings;
+ std::unique_ptr<weld::CheckButton> mxBtnInsNumbers;
+ std::unique_ptr<weld::CheckButton> mxBtnInsDateTime;
+ std::unique_ptr<weld::CheckButton> mxBtnInsFormulas;
+ std::unique_ptr<weld::CheckButton> mxBtnInsNotes;
+ std::unique_ptr<weld::CheckButton> mxBtnInsAttrs;
+ std::unique_ptr<weld::CheckButton> mxBtnInsObjects;
+
+ std::unique_ptr<weld::CheckButton> mxBtnSkipEmptyCells;
+ std::unique_ptr<weld::CheckButton> mxBtnTranspose;
+ std::unique_ptr<weld::CheckButton> mxBtnLink;
+
+ std::unique_ptr<weld::RadioButton> mxRbNoOp;
+ std::unique_ptr<weld::RadioButton> mxRbAdd;
+ std::unique_ptr<weld::RadioButton> mxRbSub;
+ std::unique_ptr<weld::RadioButton> mxRbMul;
+ std::unique_ptr<weld::RadioButton> mxRbDiv;
+
+ std::unique_ptr<weld::RadioButton> mxRbMoveNone;
+ std::unique_ptr<weld::RadioButton> mxRbMoveDown;
+ std::unique_ptr<weld::RadioButton> mxRbMoveRight;
+
+ std::unique_ptr<weld::Button> mxBtnShortCutPasteValuesOnly;
+ std::unique_ptr<weld::Button> mxBtnShortCutPasteValuesFormats;
+ std::unique_ptr<weld::Button> mxBtnShortCutPasteTranspose;
+ std::unique_ptr<weld::Button> mxBtnShortCutPasteFormats;
+ std::unique_ptr<weld::Button> mxOKBtn;
+
+ std::unique_ptr<weld::CheckButton> mxImmediately;
+
+ static InsertDeleteFlags nPreviousChecks;
+ static InsertContentsFlags nPreviousChecks2;
+ static ScPasteFunc nPreviousFormulaChecks;
+ static InsCellCmd nPreviousMoveMode;
+
+ void DisableChecks( bool bInsAllChecked );
+ void TestModes();
+
+ // Handler
+ DECL_LINK( InsAllHdl, weld::Toggleable&, void );
+ DECL_LINK( LinkBtnHdl, weld::Toggleable&, void );
+ DECL_LINK( ShortCutHdl, weld::Button&, void );
+ DECL_LINK( ClickHdl, weld::Button&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/instbdlg.hxx b/sc/source/ui/inc/instbdlg.hxx
new file mode 100644
index 0000000000..9698229b69
--- /dev/null
+++ b/sc/source/ui/inc/instbdlg.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 .
+ */
+
+#pragma once
+
+#include <sfx2/objsh.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/weld.hxx>
+#include <types.hxx>
+
+class ScViewData;
+class ScDocument;
+class ScDocShell;
+
+namespace sfx2 { class DocumentInserter; }
+namespace sfx2 { class FileDialogHelper; }
+
+class ScInsertTableDlg : public weld::GenericDialogController
+{
+public:
+ ScInsertTableDlg(weld::Window* pParent, ScViewData& rViewData, SCTAB nTabCount, bool bFromFile);
+ virtual ~ScInsertTableDlg() override;
+
+ virtual short run() override; // override to set parent dialog
+
+ bool GetTablesFromFile() const { return m_xBtnFromFile->get_active(); }
+ bool GetTablesAsLink() const { return m_xBtnLink->get_active(); }
+
+ const OUString* GetFirstTable( sal_uInt16* pN );
+ const OUString* GetNextTable( sal_uInt16* pN );
+ ScDocShell* GetDocShellTables() { return pDocShTables; }
+ bool IsTableBefore() const { return m_xBtnBefore->get_active(); }
+ SCTAB GetTableCount() const { return nTableCount;}
+
+private:
+ Timer aBrowseTimer;
+ ScViewData& rViewData;
+ ScDocument& rDoc;
+ ScDocShell* pDocShTables;
+ std::unique_ptr<sfx2::DocumentInserter> pDocInserter;
+ SfxObjectShellRef aDocShTablesRef;
+
+ bool bMustClose;
+ sal_uInt16 nSelTabIndex; // for GetFirstTable() / GetNextTable()
+ OUString aStrCurSelTable;
+ SCTAB nTableCount;
+ OUString m_sSheetDotDotDot;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnBefore;
+ std::unique_ptr<weld::RadioButton> m_xBtnNew;
+ std::unique_ptr<weld::RadioButton> m_xBtnFromFile;
+ std::unique_ptr<weld::Label> m_xFtCount;
+ std::unique_ptr<weld::SpinButton> m_xNfCount;
+ std::unique_ptr<weld::Label> m_xFtName;
+ std::unique_ptr<weld::Entry> m_xEdName;
+ std::unique_ptr<weld::TreeView> m_xLbTables;
+ std::unique_ptr<weld::Label> m_xFtPath;
+ std::unique_ptr<weld::Button> m_xBtnBrowse;
+ std::unique_ptr<weld::CheckButton> m_xBtnLink;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+
+ void Init_Impl( bool bFromFile );
+ void SetNewTable_Impl();
+ void SetFromTo_Impl();
+ void FillTables_Impl( const ScDocument* pSrcDoc );
+ void DoEnable_Impl();
+
+ DECL_LINK( BrowseHdl_Impl, weld::Button&, void );
+ DECL_LINK( ChoiceHdl_Impl, weld::Toggleable&, void );
+ DECL_LINK( SelectHdl_Impl, weld::TreeView&, void );
+ DECL_LINK( CountHdl_Impl, weld::SpinButton&, void );
+ DECL_LINK( DoEnterHdl, weld::Button&, void );
+ DECL_LINK( BrowseTimeoutHdl, Timer *, void );
+ DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/invmerge.hxx b/sc/source/ui/inc/invmerge.hxx
new file mode 100644
index 0000000000..54dd8c160e
--- /dev/null
+++ b/sc/source/ui/inc/invmerge.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+
+#include <vector>
+
+class ScInvertMerger
+{
+private:
+ ::std::vector< tools::Rectangle >* pRects;
+ tools::Rectangle aTotalRect;
+ tools::Rectangle aLineRect;
+
+ void FlushLine();
+ void FlushTotal();
+
+public:
+ ScInvertMerger( ::std::vector< tools::Rectangle >* pRectangles );
+ ~ScInvertMerger();
+
+ void AddRect( const tools::Rectangle& rRect );
+ void Flush();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/lbseldlg.hxx b/sc/source/ui/inc/lbseldlg.hxx
new file mode 100644
index 0000000000..cf313c06bd
--- /dev/null
+++ b/sc/source/ui/inc/lbseldlg.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScSelEntryDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xLb;
+
+ DECL_LINK(DblClkHdl, weld::TreeView&, bool);
+
+public:
+ ScSelEntryDlg(weld::Window* pParent, const std::vector<OUString>& rEntryList);
+ virtual ~ScSelEntryDlg() override;
+
+ OUString GetSelectedEntry() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/linkarea.hxx b/sc/source/ui/inc/linkarea.hxx
new file mode 100644
index 0000000000..a209d3b8a3
--- /dev/null
+++ b/sc/source/ui/inc/linkarea.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/objsh.hxx>
+#include <vcl/weld.hxx>
+
+namespace sfx2 { class DocumentInserter; }
+namespace sfx2 { class FileDialogHelper; }
+
+class ScDocShell;
+class SvtURLBox;
+
+class ScLinkedAreaDlg : public weld::GenericDialogController
+{
+private:
+ ScDocShell* m_pSourceShell;
+ std::unique_ptr<sfx2::DocumentInserter> m_xDocInserter;
+ SfxObjectShellRef aSourceRef;
+
+ std::unique_ptr<SvtURLBox> m_xCbUrl;
+ std::unique_ptr<weld::Button> m_xBtnBrowse;
+ std::unique_ptr<weld::TreeView> m_xLbRanges;
+ std::unique_ptr<weld::CheckButton> m_xBtnReload;
+ std::unique_ptr<weld::SpinButton> m_xNfDelay;
+ std::unique_ptr<weld::Label> m_xFtSeconds;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+
+ DECL_LINK(FileHdl, weld::ComboBox&, bool);
+ DECL_LINK(BrowseHdl, weld::Button&, void);
+ DECL_LINK(RangeHdl, weld::TreeView&, void);
+ DECL_LINK(ReloadHdl, weld::Toggleable&, void);
+ DECL_LINK(DialogClosedHdl, sfx2::FileDialogHelper*, void);
+
+ void UpdateSourceRanges();
+ void UpdateEnable();
+ void LoadDocument( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions );
+
+public:
+ ScLinkedAreaDlg(weld::Widget* pParent);
+ virtual ~ScLinkedAreaDlg() override;
+
+ void InitFromOldLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, std::u16string_view rSource,
+ sal_Int32 nRefreshDelaySeconds );
+
+ OUString GetURL() const;
+ OUString GetFilter() const; // may be empty
+ OUString GetOptions() const; // filter options
+ OUString GetSource() const; // separated by ";"
+ sal_Int32 GetRefreshDelaySeconds() const; // 0 if disabled
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/lnktrans.hxx b/sc/source/ui/inc/lnktrans.hxx
new file mode 100644
index 0000000000..2ef5469074
--- /dev/null
+++ b/sc/source/ui/inc/lnktrans.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 .
+ */
+
+#pragma once
+
+#include <vcl/transfer.hxx>
+#include <rtl/ustring.hxx>
+
+class ScLinkTransferObj : public TransferDataContainer
+{
+private:
+ OUString aLinkURL;
+ OUString aLinkText;
+
+public:
+ ScLinkTransferObj();
+ virtual ~ScLinkTransferObj() override;
+
+ void SetLinkURL( const OUString& rURL, const OUString& rText );
+
+ virtual void AddSupportedFormats() override;
+ virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override;
+ virtual void DragFinished( sal_Int8 nDropAction ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/mediash.hxx b/sc/source/ui/inc/mediash.hxx
new file mode 100644
index 0000000000..471902b28f
--- /dev/null
+++ b/sc/source/ui/inc/mediash.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <shellids.hxx>
+#include "drawsh.hxx"
+
+class ScViewData;
+class SfxModule;
+
+class ScMediaShell final : public ScDrawShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_MEDIA_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScMediaShell(ScViewData& rData);
+ virtual ~ScMediaShell() override;
+
+ void ExecuteMedia(const SfxRequest& rReq);
+ void GetMediaState(SfxItemSet& rSet);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/mergecellsdialog.hxx b/sc/source/ui/inc/mergecellsdialog.hxx
new file mode 100644
index 0000000000..33370d793d
--- /dev/null
+++ b/sc/source/ui/inc/mergecellsdialog.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+enum ScMergeCellsOption
+{
+ MoveContentHiddenCells,
+ KeepContentHiddenCells,
+ EmptyContentHiddenCells
+};
+
+class ScMergeCellsDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::RadioButton> m_xRBMoveContent;
+ std::unique_ptr<weld::RadioButton> m_xRBKeepContent;
+ std::unique_ptr<weld::RadioButton> m_xRBEmptyContent;
+
+public:
+ ScMergeCellsDialog(weld::Window* pParent);
+ virtual ~ScMergeCellsDialog() override;
+
+ ScMergeCellsOption GetMergeCellsOption() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/msgpool.hxx b/sc/source/ui/inc/msgpool.hxx
new file mode 100644
index 0000000000..926fb38bf1
--- /dev/null
+++ b/sc/source/ui/inc/msgpool.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 .
+ */
+
+#pragma once
+
+#include <svl/srchitem.hxx>
+
+#include <svl/itempool.hxx>
+#include <svl/stritem.hxx>
+
+#include "uiitems.hxx"
+#include "condformatdlgitem.hxx"
+
+class ScDocumentPool;
+
+class ScMessagePool final : public SfxItemPool
+{
+ SfxStringItem aGlobalStringItem;
+ SvxSearchItem aGlobalSearchItem;
+ ScSortItem aGlobalSortItem;
+ ScQueryItem aGlobalQueryItem;
+ ScSubTotalItem aGlobalSubTotalItem;
+ ScConsolidateItem aGlobalConsolidateItem;
+ ScPivotItem aGlobalPivotItem;
+ ScSolveItem aGlobalSolveItem;
+ ScUserListItem aGlobalUserListItem;
+ ScCondFormatDlgItem aCondFormatDlgItem;
+
+ std::vector<SfxPoolItem*> mvPoolDefaults;
+ rtl::Reference<ScDocumentPool> pDocPool;
+
+public:
+ ScMessagePool();
+private:
+ virtual ~ScMessagePool() override;
+public:
+
+ virtual MapUnit GetMetric( sal_uInt16 nWhich ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/mtrindlg.hxx b/sc/source/ui/inc/mtrindlg.hxx
new file mode 100644
index 0000000000..1b3edfca93
--- /dev/null
+++ b/sc/source/ui/inc/mtrindlg.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScMetricInputDlg : public weld::GenericDialogController
+{
+public:
+ ScMetricInputDlg( weld::Window* pParent,
+ const OUString& sDialogName,
+ tools::Long nCurrent,
+ tools::Long nDefault,
+ FieldUnit eFUnit,
+ sal_uInt16 nDecimals,
+ tools::Long nMaximum,
+ tools::Long nMinimum);
+ virtual ~ScMetricInputDlg() override;
+
+ int GetInputValue() const;
+
+private:
+ std::unique_ptr<weld::MetricSpinButton> m_xEdValue;
+ std::unique_ptr<weld::CheckButton> m_xBtnDefVal;
+ int nDefaultValue;
+ int nCurrentValue;
+
+ DECL_LINK(SetDefValHdl, weld::Toggleable&, void);
+ DECL_LINK(ModifyHdl, weld::MetricSpinButton&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/mvtabdlg.hxx b/sc/source/ui/inc/mvtabdlg.hxx
new file mode 100644
index 0000000000..c6e89ab9fe
--- /dev/null
+++ b/sc/source/ui/inc/mvtabdlg.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 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include <vcl/weld.hxx>
+
+class ScDocument;
+
+class ScMoveTableDlg : public weld::GenericDialogController
+{
+public:
+ ScMoveTableDlg(weld::Window* pParent, OUString aDefault);
+ virtual ~ScMoveTableDlg() override;
+
+ sal_uInt16 GetSelectedDocument () const { return nDocument; }
+ SCTAB GetSelectedTable () const { return nTable; }
+ bool GetCopyTable () const { return bCopyTable; }
+ bool GetRenameTable () const { return bRenameTable; }
+ void GetTabNameString( OUString& rString ) const;
+ void SetForceCopyTable ();
+ void EnableRenameTable (bool bFlag);
+ void SetOkBtnLabel ();
+
+private:
+ void ResetRenameInput();
+ void CheckNewTabName();
+ ScDocument* GetSelectedDoc();
+
+private:
+ OUString msCurrentDoc;
+ OUString msNewDoc;
+
+ OUString msStrTabNameUsed;
+ OUString msStrTabNameEmpty;
+ OUString msStrTabNameInvalid;
+
+ const OUString maDefaultName;
+
+ sal_uInt16 mnCurrentDocPos;
+ sal_uInt16 nDocument;
+ SCTAB nTable;
+ bool bCopyTable:1;
+ bool bRenameTable:1;
+ bool mbEverEdited:1;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnMove;
+ std::unique_ptr<weld::RadioButton> m_xBtnCopy;
+ std::unique_ptr<weld::Label> m_xFtDoc;
+ std::unique_ptr<weld::ComboBox> m_xLbDoc;
+ std::unique_ptr<weld::TreeView> m_xLbTable;
+ std::unique_ptr<weld::Entry> m_xEdTabName;
+ std::unique_ptr<weld::Label> m_xFtWarn;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Label> m_xUnusedLabel;
+ std::unique_ptr<weld::Label> m_xEmptyLabel;
+ std::unique_ptr<weld::Label> m_xInvalidLabel;
+
+ void Init ();
+ void InitDocListBox ();
+ DECL_LINK(OkHdl, weld::Button&, void);
+ DECL_LINK(SelHdl, weld::ComboBox&, void);
+ DECL_LINK(CheckBtnHdl, weld::Toggleable&, void);
+ DECL_LINK(CheckNameHdl, weld::Entry&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/namecrea.hxx b/sc/source/ui/inc/namecrea.hxx
new file mode 100644
index 0000000000..89adb82167
--- /dev/null
+++ b/sc/source/ui/inc/namecrea.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include "scui_def.hxx"
+
+class ScNameCreateDlg final : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::CheckButton> m_xTopBox;
+ std::unique_ptr<weld::CheckButton> m_xLeftBox;
+ std::unique_ptr<weld::CheckButton> m_xBottomBox;
+ std::unique_ptr<weld::CheckButton> m_xRightBox;
+
+public:
+ ScNameCreateDlg(weld::Window* pParent, CreateNameFlags nFlags);
+ virtual ~ScNameCreateDlg() override;
+ CreateNameFlags GetFlags() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/namedefdlg.hxx b/sc/source/ui/inc/namedefdlg.hxx
new file mode 100644
index 0000000000..2c6634def3
--- /dev/null
+++ b/sc/source/ui/inc/namedefdlg.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+
+#include <map>
+
+class ScRangeName;
+class ScDocument;
+class ScDocShell;
+class ScViewData;
+
+class ScNameDefDlg : public ScAnyRefDlgController
+{
+private:
+ bool mbUndo; //if true we need to add an undo action after creating a range name
+ ScDocument& mrDoc;
+ ScDocShell* mpDocShell;
+
+ ScAddress maCursorPos;
+ OUString maStrInfoDefault;
+ const OUString maGlobalNameStr;
+ const OUString maErrInvalidNameStr;
+ const OUString maErrInvalidNameCellRefStr;
+ const OUString maErrNameInUse;
+
+ //hack to call this dialog from Manage Names
+ OUString maName;
+ OUString maScope;
+
+ std::map<OUString, ScRangeName*> maRangeMap;
+
+ std::unique_ptr<weld::Entry> m_xEdName;
+
+ std::unique_ptr<formula::RefEdit> m_xEdRange;
+ std::unique_ptr<formula::RefButton> m_xRbRange;
+
+ std::unique_ptr<weld::ComboBox> m_xLbScope;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnRowHeader;
+ std::unique_ptr<weld::CheckButton> m_xBtnColHeader;
+ std::unique_ptr<weld::CheckButton> m_xBtnPrintArea;
+ std::unique_ptr<weld::CheckButton> m_xBtnCriteria;
+
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Label> m_xFtInfo;
+ std::unique_ptr<weld::Label> m_xFtRange;
+
+ void CancelPushed();
+ void AddPushed();
+
+ bool IsNameValid();
+ bool IsFormulaValid();
+
+ DECL_LINK(CancelBtnHdl, weld::Button&, void);
+ DECL_LINK(AddBtnHdl, weld::Button&, void);
+ DECL_LINK(NameModifyHdl, weld::Entry&, void);
+ DECL_LINK(AssignGetFocusHdl, formula::RefEdit&, void);
+
+protected:
+ virtual void RefInputDone(bool bForced = false) override;
+
+public:
+ ScNameDefDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const ScViewData& rViewData, std::map<OUString, ScRangeName*>&& aRangeMap,
+ const ScAddress& aCursorPos, const bool bUndo);
+
+ virtual ~ScNameDefDlg() override;
+
+ virtual void SetReference(const ScRange& rRef, ScDocument& rDoc) override;
+ virtual bool IsRefInputMode() const override;
+
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+ void GetNewData(OUString& rName, OUString& rScope);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/namedlg.hxx b/sc/source/ui/inc/namedlg.hxx
new file mode 100644
index 0000000000..d06912120e
--- /dev/null
+++ b/sc/source/ui/inc/namedlg.hxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+#include "namemgrtable.hxx"
+
+#include <memory>
+#include <map>
+
+class ScViewData;
+class ScDocument;
+
+//logic behind the manage names dialog
+class ScNameDlg : public ScAnyRefDlgController
+{
+private:
+ const OUString maGlobalNameStr;
+ const OUString maErrInvalidNameStr;
+ const OUString maErrNameInUse;
+ const OUString maStrMultiSelect;
+ OUString maStrInfoDefault;
+
+ ScViewData& mrViewData;
+ ScDocument& mrDoc;
+ const ScAddress maCursorPos;
+
+ bool mbDataChanged;
+ //ugly hack to call DefineNames from ManageNames
+ bool mbCloseWithoutUndo;
+
+ typedef std::map<OUString, ScRangeName> RangeNameContainer;
+
+ RangeNameContainer m_RangeMap;
+
+ std::unique_ptr<weld::Entry> m_xEdName;
+ std::unique_ptr<weld::Label> m_xFtAssign;
+ std::unique_ptr<formula::RefEdit> m_xEdAssign;
+ std::unique_ptr<formula::RefButton> m_xRbAssign;
+ std::unique_ptr<weld::ComboBox> m_xLbScope;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnPrintArea;
+ std::unique_ptr<weld::CheckButton> m_xBtnColHeader;
+ std::unique_ptr<weld::CheckButton> m_xBtnCriteria;
+ std::unique_ptr<weld::CheckButton> m_xBtnRowHeader;
+
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnDelete;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ std::unique_ptr<weld::Label> m_xFtInfo;
+
+ std::unique_ptr<ScRangeManagerTable> m_xRangeManagerTable;
+
+private:
+ void Init();
+ void UpdateChecks(const ScRangeData* pData);
+ void ShowOptions(const ScRangeNameLine& rLine);
+
+ bool IsNameValid();
+ bool IsFormulaValid();
+ void CheckForEmptyTable();
+
+ ScRangeName* GetRangeName(const OUString& rScope);
+
+ void AddPushed();
+ void RemovePushed();
+ void ScopeChanged();
+ void NameModified();
+
+ void SelectionChanged();
+
+ // Handler:
+ DECL_LINK(OkBtnHdl, weld::Button&, void);
+ DECL_LINK(CancelBtnHdl, weld::Button&, void);
+ DECL_LINK(AddBtnHdl, weld::Button&, void);
+ DECL_LINK(RemoveBtnHdl, weld::Button&, void);
+ DECL_LINK(EdModifyHdl, weld::Entry&, void);
+ DECL_LINK(RefEdModifyHdl, formula::RefEdit&, void);
+ DECL_LINK(EdModifyCheckBoxHdl, weld::Toggleable&, void);
+ DECL_LINK(AssignGetFocusHdl, formula::RefEdit&, void);
+ DECL_LINK(SelectionChangedHdl_Impl, weld::TreeView&, void);
+ DECL_LINK(ScopeChangedHdl, weld::ComboBox&, void);
+
+protected:
+ virtual void RefInputDone(bool bForced = false) override;
+
+public:
+ ScNameDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, ScViewData& rViewData,
+ const ScAddress& aCursorPos, std::map<OUString, ScRangeName>* pRangeMap = nullptr);
+ virtual ~ScNameDlg() override;
+
+ virtual void SetReference(const ScRange& rRef, ScDocument& rDoc) override;
+ virtual bool IsRefInputMode() const override;
+
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+ void GetRangeNames(std::map<OUString, ScRangeName>& rRangeMap);
+ void SetEntry(const OUString& rName, const OUString& rScope);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/namemgrtable.hxx b/sc/source/ui/inc/namemgrtable.hxx
new file mode 100644
index 0000000000..347fbac3db
--- /dev/null
+++ b/sc/source/ui/inc/namemgrtable.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/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+#include <address.hxx>
+
+#include <memory>
+#include <vector>
+#include <map>
+
+class ScRangeName;
+class ScRangeData;
+
+struct ScRangeNameLine
+{
+ OUString aName;
+ OUString aExpression;
+ OUString aScope;
+};
+
+class SC_DLLPUBLIC ScRangeManagerTable
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+
+ OUString maGlobalString;
+
+ // should be const because we should not modify it here
+ const std::map<OUString, ScRangeName>& m_RangeMap;
+ // for performance, save which entries already have the formula entry
+ // otherwise opening the dialog with a lot of range names is extremely slow because
+ // we would calculate all formula strings during opening
+ std::map<OUString, bool> maCalculatedFormulaEntries;
+ const ScAddress maPos;
+
+ int m_nId;
+
+ bool mbNeedUpdate;
+
+ void GetLine(ScRangeNameLine& aLine, const weld::TreeIter& rEntry);
+ void Init();
+ const ScRangeData* findRangeData(const ScRangeNameLine& rLine);
+
+ DECL_DLLPRIVATE_LINK(SizeAllocHdl, const Size&, void);
+ DECL_DLLPRIVATE_LINK(VisRowsScrolledHdl, weld::TreeView&, void);
+
+public:
+ ScRangeManagerTable(std::unique_ptr<weld::TreeView>,
+ const std::map<OUString, ScRangeName>& rTabRangeNames,
+ const ScAddress& rPos);
+
+ void CheckForFormulaString();
+
+ int n_children() const { return m_xTreeView->n_children(); }
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xTreeView->connect_changed(rLink); }
+ void set_cursor(int nPos) { m_xTreeView->set_cursor(nPos); }
+
+ void addEntry(const ScRangeNameLine& rLine, bool bSetCurEntry);
+ void DeleteSelectedEntries();
+ void SetEntry( const ScRangeNameLine& rLine );
+
+ void GetCurrentLine(ScRangeNameLine& rLine);
+ bool IsMultiSelection() const;
+ std::vector<ScRangeNameLine> GetSelectedEntries();
+
+ void BlockUpdate()
+ {
+ mbNeedUpdate = false;
+ }
+
+ bool UpdatesBlocked() const
+ {
+ return !mbNeedUpdate;
+ }
+
+ void UnblockUpdate()
+ {
+ mbNeedUpdate = true;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/namepast.hxx b/sc/source/ui/inc/namepast.hxx
new file mode 100644
index 0000000000..b155dfacc6
--- /dev/null
+++ b/sc/source/ui/inc/namepast.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include "namemgrtable.hxx"
+#include <memory>
+#include <vector>
+#include <map>
+
+class ScRangeName;
+class ScDocShell;
+
+class ScNamePasteDlg : public weld::GenericDialogController
+{
+ DECL_LINK(ButtonHdl, weld::Button&, void);
+
+private:
+ std::unique_ptr<weld::Button> m_xBtnPasteAll;
+ std::unique_ptr<weld::Button> m_xBtnPaste;
+ std::unique_ptr<weld::Button> m_xBtnClose;
+ std::unique_ptr<ScRangeManagerTable> m_xTable;
+
+ std::vector<OUString> maSelectedNames;
+ std::map<OUString, ScRangeName> m_RangeMap;
+ OUString m_aSheetSep;
+
+public:
+ ScNamePasteDlg(weld::Window* pParent, ScDocShell* pShell);
+
+ virtual ~ScNamePasteDlg() override;
+
+ const std::vector<OUString>& GetSelectedNames() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/navcitem.hxx b/sc/source/ui/inc/navcitem.hxx
new file mode 100644
index 0000000000..17131afbae
--- /dev/null
+++ b/sc/source/ui/inc/navcitem.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 <sfx2/ctrlitem.hxx>
+
+class ScNavigatorDlg;
+
+class ScNavigatorControllerItem : public SfxControllerItem
+{
+public:
+ ScNavigatorControllerItem( sal_uInt16 nId,
+ ScNavigatorDlg& rDlg,
+ SfxBindings& rBindings );
+protected:
+ virtual void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pItem ) override;
+
+private:
+ ScNavigatorDlg& rNavigatorDlg;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/navipi.hxx b/sc/source/ui/inc/navipi.hxx
new file mode 100644
index 0000000000..dbaae24657
--- /dev/null
+++ b/sc/source/ui/inc/navipi.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 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <vcl/idle.hxx>
+#include <svl/lstner.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/navigat.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <rangeutl.hxx>
+#include "content.hxx"
+
+class SfxPoolItem;
+class ScTabViewShell;
+class ScViewData;
+class ScScenarioWindow;
+class ScNavigatorControllerItem;
+class ScNavigatorDlg;
+class ScNavigatorSettings;
+
+#define SC_DROPMODE_URL 0
+#define SC_DROPMODE_LINK 1
+#define SC_DROPMODE_COPY 2
+
+enum NavListMode { NAV_LMODE_NONE = 0x4000,
+ NAV_LMODE_AREAS = 0x2000,
+ NAV_LMODE_SCENARIOS = 0x400 };
+
+class ScScenarioWindow
+{
+public:
+ ScScenarioWindow(weld::Builder& rBuilder, const OUString& rQH_List, const OUString& rQH_Comment);
+ ~ScScenarioWindow();
+ void NotifyState(const SfxPoolItem* pState);
+ void SetComment(const OUString& rComment)
+ {
+ m_xEdComment->set_text(rComment);
+ }
+
+private:
+ std::unique_ptr<weld::TreeView> m_xLbScenario;
+ std::unique_ptr<weld::TextView> m_xEdComment;
+
+ struct ScenarioEntry
+ {
+ OUString maName;
+ OUString maComment;
+ bool mbProtected;
+
+ explicit ScenarioEntry() : mbProtected( false ) {}
+ };
+
+ std::vector< ScenarioEntry > m_aEntries;
+
+ void UpdateEntries(const std::vector<OUString> &rNewEntryList);
+ void SelectScenario();
+ void ExecuteScenarioSlot(sal_uInt16 nSlotId);
+ void EditScenario();
+ void DeleteScenario();
+ const ScenarioEntry* GetSelectedScenarioEntry() const;
+
+ DECL_LINK(SelectHdl, weld::TreeView&, void);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ContextMenuHdl, const CommandEvent&, bool);
+};
+
+class ScNavigatorDlg : public PanelLayout, public SfxListener
+{
+friend class ScNavigatorWin;
+friend class ScNavigatorControllerItem;
+friend class ScContentTree;
+
+private:
+ static constexpr int CTRL_ITEMS = 4;
+
+ SfxBindings& rBindings; // must be first member
+
+ std::unique_ptr<weld::SpinButton> m_xEdCol;
+ std::unique_ptr<weld::SpinButton> m_xEdRow;
+ std::unique_ptr<weld::Toolbar> m_xTbxCmd1;
+ std::unique_ptr<weld::Toolbar> m_xTbxCmd2;
+ std::unique_ptr<ScContentTree> m_xLbEntries;
+ std::unique_ptr<weld::Widget> m_xScenarioBox;
+ std::unique_ptr<ScScenarioWindow> m_xWndScenarios;
+ std::unique_ptr<weld::ComboBox> m_xLbDocuments;
+ std::unique_ptr<weld::Menu> m_xDragModeMenu;
+
+ VclPtr<SfxNavigator> m_xNavigatorDlg;
+
+ Size aExpandedSize;
+ Idle aContentIdle;
+
+ OUString aStrActive;
+ OUString aStrNotActive;
+ OUString aStrActiveWin;
+
+ std::optional<ScArea> moMarkArea;
+ ScViewData* pViewData;
+
+ NavListMode eListMode;
+ sal_uInt16 nDropMode;
+ SCCOL nCurCol;
+ SCROW nCurRow;
+ SCTAB nCurTab;
+
+ std::array<std::unique_ptr<ScNavigatorControllerItem>,CTRL_ITEMS> mvBoundItems;
+
+ DECL_LINK(TimeHdl, Timer*, void);
+ DECL_LINK(DocumentSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(ExecuteRowHdl, weld::Entry&, bool);
+ DECL_LINK(ExecuteColHdl, weld::Entry&, bool);
+ DECL_LINK(ToolBoxSelectHdl, const OUString&, void);
+ DECL_LINK(ToolBoxDropdownClickHdl, const OUString&, void);
+ DECL_LINK(MenuSelectHdl, const OUString&, void);
+ DECL_LINK(FormatRowOutputHdl, weld::SpinButton&, void);
+ DECL_LINK(ParseRowInputHdl, int*, bool);
+
+ void UpdateButtons();
+ void SetCurrentCell( SCCOL nCol, SCROW Row );
+ void SetCurrentCellStr( const OUString& rName );
+ void SetCurrentTable( SCTAB nTab );
+ void SetCurrentTableStr( std::u16string_view rName );
+ void SetCurrentObject( const OUString& rName );
+ void SetCurrentDoc( const OUString& rDocName );
+ void UpdateSelection();
+ void ContentUpdated(); // stop aContentIdle because content is up to date
+
+ static ScTabViewShell* GetTabViewShell();
+ static ScNavigatorSettings* GetNavigatorSettings();
+ ScViewData* GetViewData();
+
+ void UpdateSheetLimits();
+
+ void UpdateColumn ( const SCCOL* pCol = nullptr );
+ void UpdateRow ( const SCROW* pRow = nullptr );
+ void UpdateTable ( const SCTAB* pTab );
+ void UpdateAll ();
+
+ void GetDocNames(const OUString* pSelEntry);
+
+ void SetListMode(NavListMode eMode);
+ void ShowList(bool bShow);
+ void ShowScenarios();
+
+ void SetDropMode(sal_uInt16 nNew);
+ sal_uInt16 GetDropMode() const { return nDropMode; }
+
+ void MarkDataArea ();
+ void UnmarkDataArea ();
+ void StartOfDataArea ();
+ void EndOfDataArea ();
+
+ void UpdateInitShow();
+
+ static void ReleaseFocus();
+
+public:
+ ScNavigatorDlg(SfxBindings* pB, weld::Widget* pParent, SfxNavigator* pNavigatorDlg);
+ virtual weld::Window* GetFrameWeld() const override;
+ virtual ~ScNavigatorDlg() override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+};
+
+class ScNavigatorWrapper final : public SfxNavigatorWrapper
+{
+public:
+ ScNavigatorWrapper(vcl::Window *pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo* pInfo);
+ SFX_DECL_CHILDWINDOW(ScNavigatorWrapper);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/navsett.hxx b/sc/source/ui/inc/navsett.hxx
new file mode 100644
index 0000000000..c77bf259f6
--- /dev/null
+++ b/sc/source/ui/inc/navsett.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/solar.h>
+
+#include <o3tl/enumarray.hxx>
+#include "content.hxx"
+
+/** Contains settings of the navigator listbox. This includes the expand state
+ of each listbox entry and the index of the selected entry and sub entry. */
+class ScNavigatorSettings
+{
+private:
+ o3tl::enumarray<ScContentId,bool> maExpandedVec; /// Array of Booleans for expand state.
+ ScContentId mnRootSelected; /// Index of selected root entry.
+ sal_uLong mnChildSelected; /// Index of selected child entry.
+
+public:
+ ScNavigatorSettings();
+
+ void SetExpanded( ScContentId nIndex, bool bExpand ) { maExpandedVec[ nIndex ] = bExpand; }
+ bool IsExpanded( ScContentId nIndex ) const { return maExpandedVec[ nIndex ]; }
+
+ void SetRootSelected( ScContentId nIndex ) { mnRootSelected = nIndex; }
+ ScContentId GetRootSelected() const { return mnRootSelected; }
+
+ void SetChildSelected( sal_uLong nIndex ) { mnChildSelected = nIndex; }
+ sal_uLong GetChildSelected() const { return mnChildSelected; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/notemark.hxx b/sc/source/ui/inc/notemark.hxx
new file mode 100644
index 0000000000..31965be078
--- /dev/null
+++ b/sc/source/ui/inc/notemark.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 .
+ */
+
+#pragma once
+
+#include <vcl/mapmod.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/vclptr.hxx>
+#include <tools/gen.hxx>
+#include <address.hxx>
+#include <postit.hxx>
+
+namespace vcl { class Window; }
+
+class SdrModel;
+class SdrCaptionObj;
+
+class ScNoteMarker
+{
+private:
+ VclPtr<vcl::Window> m_pWindow;
+ VclPtr<vcl::Window> m_pRightWin;
+ VclPtr<vcl::Window> m_pBottomWin;
+ VclPtr<vcl::Window> m_pDiagWin;
+ ScDocument* m_pDoc;
+ ScAddress m_aDocPos;
+ OUString m_aUserText;
+ tools::Rectangle m_aVisRect;
+ Timer m_aTimer;
+ MapMode m_aMapMode;
+ bool m_bLeft;
+ bool m_bByKeyboard;
+
+ tools::Rectangle m_aRect;
+ std::unique_ptr<SdrModel> m_pModel;
+ rtl::Reference<SdrCaptionObj> m_xObject;
+ bool m_bVisible;
+ DECL_LINK( TimeHdl, Timer*, void );
+
+public:
+ ScNoteMarker( vcl::Window* pWin, vcl::Window* pRight, vcl::Window* pBottom, vcl::Window* pDiagonal,
+ ScDocument* pD, const ScAddress& aPos, OUString aUser,
+ const MapMode& rMap, bool bLeftEdge, bool bForce, bool bKeyboard);
+ ~ScNoteMarker();
+
+ void Draw();
+ void InvalidateWin();
+
+ const ScAddress& GetDocPos() const { return m_aDocPos; }
+ bool IsByKeyboard() const { return m_bByKeyboard; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/oleobjsh.hxx b/sc/source/ui/inc/oleobjsh.hxx
new file mode 100644
index 0000000000..88be75adc2
--- /dev/null
+++ b/sc/source/ui/inc/oleobjsh.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 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+#include <shellids.hxx>
+#include "drawsh.hxx"
+
+class ScViewData;
+class SfxModule;
+
+class ScOleObjectShell final : public ScDrawShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_OLEOBJECT_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScOleObjectShell(ScViewData& rData);
+ virtual ~ScOleObjectShell() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/olinefun.hxx b/sc/source/ui/inc/olinefun.hxx
new file mode 100644
index 0000000000..ec167a8e4c
--- /dev/null
+++ b/sc/source/ui/inc/olinefun.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+
+class ScDocShell;
+class ScRange;
+
+class ScOutlineDocFunc
+{
+private:
+ ScDocShell& rDocShell;
+
+public:
+ ScOutlineDocFunc( ScDocShell& rDocSh ): rDocShell(rDocSh) {}
+
+ void MakeOutline( const ScRange& rRange, bool bColumns, bool bRecord, bool bApi );
+ void RemoveOutline( const ScRange& rRange, bool bColumns, bool bRecord, bool bApi );
+ bool RemoveAllOutlines( SCTAB nTab, bool bRecord );
+ void AutoOutline( const ScRange& rRange, bool bRecord );
+
+ bool SelectLevel( SCTAB nTab, bool bColumns, sal_uInt16 nLevel,
+ bool bRecord, bool bPaint );
+
+ bool ShowMarkedOutlines( const ScRange& rRange, bool bRecord );
+ bool HideMarkedOutlines( const ScRange& rRange, bool bRecord );
+
+ void ShowOutline( SCTAB nTab, bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry,
+ bool bRecord, bool bPaint );
+ bool HideOutline( SCTAB nTab, bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry,
+ bool bRecord, bool bPaint );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/olinewin.hxx b/sc/source/ui/inc/olinewin.hxx
new file mode 100644
index 0000000000..7f99ae6624
--- /dev/null
+++ b/sc/source/ui/inc/olinewin.hxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/window.hxx>
+#include "viewdata.hxx"
+
+class ScOutlineEntry;
+class ScOutlineArray;
+
+enum ScOutlineMode { SC_OUTLINE_HOR, SC_OUTLINE_VER };
+
+/** The window left of or above the spreadsheet containing the outline groups
+ and controls to expand/collapse them. */
+class ScOutlineWindow : public vcl::Window
+{
+private:
+ ScViewData& mrViewData; /// View data containing the document.
+ ScSplitPos meWhich; /// Which area in split window.
+ bool mbHoriz; /// true = Horizontal orientation.
+ bool mbMirrorEntries; /// true = mirror the order of entries (including header)
+ bool mbMirrorLevels; /// true = mirror the order of levels, including the border
+
+ Color maLineColor; /// Line color for expanded groups.
+ tools::Long mnHeaderSize; /// Size of the header area in entry direction.
+ tools::Long mnHeaderPos; /// Position of the header area in entry direction.
+ tools::Long mnMainFirstPos; /// First position of main area in entry direction.
+ tools::Long mnMainLastPos; /// Last position of main area in entry direction.
+
+ size_t mnMTLevel; /// Mouse tracking: Level of active button.
+ size_t mnMTEntry; /// Mouse tracking: Entry index of active button.
+ bool mbMTActive; /// Mouse tracking active?
+ bool mbMTPressed; /// Mouse tracking: Button currently drawn pressed?
+
+ tools::Rectangle maFocusRect; /// Focus rectangle on screen.
+ size_t mnFocusLevel; /// Level of focused button.
+ size_t mnFocusEntry; /// Entry index of focused button.
+ bool mbDontDrawFocus; /// Do not redraw focus in next Paint().
+
+public:
+ ScOutlineWindow(
+ vcl::Window* pParent,
+ ScOutlineMode eMode,
+ ScViewData* pViewData,
+ ScSplitPos eWhich );
+ virtual ~ScOutlineWindow() override;
+ virtual void dispose() override;
+
+ /** Sets the size of the header area (width/height dep. on window type). */
+ void SetHeaderSize( tools::Long nNewSize );
+ /** Returns the width/height the window needs to show all levels. */
+ tools::Long GetDepthSize() const;
+
+ /** Scrolls the window content by the specified amount of pixels. */
+ void ScrollPixel( tools::Long nDiff );
+
+ using Window::ShowFocus;
+
+private:
+ /** Initializes color and image settings. */
+ void InitSettings();
+
+ /** Returns the calc document. */
+ ScDocument& GetDoc() const { return mrViewData.GetDocument(); }
+ /** Returns the current sheet index. */
+ SCTAB GetTab() const { return mrViewData.GetTabNo(); }
+ /** Returns the outline array of the corresponding document. */
+ const ScOutlineArray* GetOutlineArray() const;
+ /** Returns the specified outline entry. */
+ const ScOutlineEntry* GetOutlineEntry( size_t nLevel, size_t nEntry ) const;
+
+ /** Returns true, if the column/row is hidden. */
+ bool IsHidden( SCCOLROW nColRowIndex ) const;
+ /** Returns true, if the column/row is filtered. */
+ bool IsFiltered( SCCOLROW nColRowIndex ) const;
+ /** Returns true, if all columns/rows before nColRowIndex are hidden. */
+ bool IsFirstVisible( SCCOLROW nColRowIndex ) const;
+ /** Returns the currently visible column/row range. */
+ void GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const;
+
+ /** Returns the point in the window of the specified position. */
+ Point GetPoint( tools::Long nLevelPos, tools::Long nEntryPos ) const;
+ /** Returns the rectangle in the window of the specified position. */
+ tools::Rectangle GetRectangle(
+ tools::Long nLevelStart, tools::Long nEntryStart,
+ tools::Long nLevelEnd, tools::Long nEntryEnd ) const;
+
+ /** Returns the window size for the level coordinate. */
+ tools::Long GetOutputSizeLevel() const;
+ /** Returns the window size for the entry coordinate. */
+ tools::Long GetOutputSizeEntry() const;
+
+ /** Returns the count of levels of the outline array. 0 means no outlines. */
+ size_t GetLevelCount() const;
+ /** Returns the pixel position of the specified level. */
+ tools::Long GetLevelPos( size_t nLevel ) const;
+ /** Returns the level of the passed pixel position. */
+ size_t GetLevelFromPos( tools::Long nLevelPos ) const;
+
+ /** Returns the start coordinate of the specified column/row in the window. */
+ tools::Long GetColRowPos( SCCOLROW nColRowIndex ) const;
+ /** Returns the entry position of header images. */
+ tools::Long GetHeaderEntryPos() const;
+ /** Calculates the coordinates the outline entry takes in the window.
+ @return false = no part of the group is visible (outside window or collapsed by parent group). */
+ bool GetEntryPos(
+ size_t nLevel, size_t nEntry,
+ tools::Long& rnStartPos, tools::Long& rnEndPos, tools::Long& rnImagePos ) const;
+ /** Calculates the absolute position of the image of the specified outline entry.
+ @param nLevel The level of the entry.
+ @param nEntry The entry index or SC_OL_HEADERENTRY for the header image.
+ @return false = image is not visible. */
+ bool GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const;
+ /** Returns true, if the button of the specified entry is visible in the window. */
+ bool IsButtonVisible( size_t nLevel, size_t nEntry ) const;
+
+ /** Returns true, if rPos is inside of a button or over the line of an expanded
+ group. The outline entry data is stored in the passed variables. */
+ bool ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const;
+ /** Returns true, if rPos is inside of a button.
+ The button data is stored in the passed variables. */
+ bool ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const;
+ /** Returns true, if rPos is over the line of an expanded group.
+ The outline entry data is stored in the passed variables. */
+ bool LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const;
+
+ /** Performs an action with the specified item.
+ @param nLevel The level of the entry.
+ @param nEntry The entry index or SC_OL_HEADERENTRY for the header entry. */
+ void DoFunction( size_t nLevel, size_t nEntry ) const;
+ /** Expands the specified entry (does nothing with header entries). */
+ void DoExpand( size_t nLevel, size_t nEntry ) const;
+ /** Collapses the specified entry (does nothing with header entries). */
+ void DoCollapse( size_t nLevel, size_t nEntry ) const;
+
+ /** Returns true, if the focused button is visible in the window. */
+ bool IsFocusButtonVisible() const;
+
+ /** Calculates index of next/previous focus button in the current level (no paint).
+ @param bFindVisible true = repeats until a visible button has been found.
+ @return true = focus wrapped from end to start or vice versa. */
+ bool ImplMoveFocusByEntry( bool bForward, bool bFindVisible );
+ /** Calculates position of focus button in next/previous level (no paint).
+ @return true = focus wrapped from end to start or vice versa. */
+ bool ImplMoveFocusByLevel( bool bForward );
+ /** Calculates position of focus button in tab order.
+ Repeats until a visible button has been found.
+ @return true = focus wrapped from end to start or vice versa. */
+ bool ImplMoveFocusByTabOrder( bool bForward );
+
+ /** If the focused entry is invisible, tries to move to visible position. */
+ void ImplMoveFocusToVisible( bool bForward );
+
+ /** Focuses next/previous button in the current level. */
+ void MoveFocusByEntry( bool bForward );
+ /** Focuses button in next/previous level. */
+ void MoveFocusByLevel( bool bForward );
+ /** Focuses next/previous button in tab order. */
+ void MoveFocusByTabOrder( bool bForward );
+
+ /** Starts mouse tracking after click on a button. */
+ void StartMouseTracking( size_t nLevel, size_t nEntry );
+ /** Returns whether mouse tracking mode is active. */
+ bool IsMouseTracking() const { return mbMTActive; }
+ /** Ends mouse tracking. */
+ void EndMouseTracking();
+
+ /** Sets a clip region for the window area without header. */
+ void SetEntryAreaClipRegion();
+ /** Converts coordinates to real window points and draws the line. */
+ void DrawLineRel(
+ tools::Long nLevelStart, tools::Long nEntryStart,
+ tools::Long nLevelEnd, tools::Long nEntryEnd );
+ /** Converts coordinates to real window points and draws the rectangle. */
+ void DrawRectRel(
+ tools::Long nLevelStart, tools::Long nEntryStart,
+ tools::Long nLevelEnd, tools::Long nEntryEnd );
+ /** Draws the specified image unpressed. */
+ void DrawImageRel(tools::Long nLevelPos, tools::Long nEntryPos, const OUString& rId);
+ /** Draws a pressed or unpressed border. */
+ void DrawBorderRel(size_t nLevel, size_t nEntry, bool bPressed);
+
+ /** Draws the focus rectangle into the focused button. */
+ void ShowFocus();
+ /** Erases the focus rectangle from the focused button. */
+ void HideFocus();
+
+ /** Scrolls the specified range of the window in entry-relative direction. */
+ void ScrollRel( tools::Long nEntryDiff, tools::Long nEntryStart, tools::Long nEntryEnd );
+
+protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+
+ virtual void Resize() override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+
+public:
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/opredlin.hxx b/sc/source/ui/inc/opredlin.hxx
new file mode 100644
index 0000000000..4cd5660be4
--- /dev/null
+++ b/sc/source/ui/inc/opredlin.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 .
+ */
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ColorListBox;
+
+class ScRedlineOptionsTabPage : public SfxTabPage
+{
+ std::unique_ptr<ColorListBox> m_xContentColorLB;
+ std::unique_ptr<ColorListBox> m_xRemoveColorLB;
+ std::unique_ptr<ColorListBox> m_xInsertColorLB;
+ std::unique_ptr<ColorListBox> m_xMoveColorLB;
+
+public:
+ ScRedlineOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet );
+ virtual ~ScRedlineOptionsTabPage() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet( SfxItemSet* rSet ) override;
+ virtual void Reset( const SfxItemSet* rSet ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/optsolver.hxx b/sc/source/ui/inc/optsolver.hxx
new file mode 100644
index 0000000000..4f28a59d5f
--- /dev/null
+++ b/sc/source/ui/inc/optsolver.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 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+#include "docsh.hxx"
+#include <SolverSettings.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <string_view>
+#include <vector>
+
+namespace com::sun::star {
+ namespace beans { struct PropertyValue; }
+}
+
+class ScCursorRefEdit : public formula::RefEdit
+{
+ Link<ScCursorRefEdit&,void> maCursorUpLink;
+ Link<ScCursorRefEdit&,void> maCursorDownLink;
+
+public:
+ ScCursorRefEdit(std::unique_ptr<weld::Entry> xEntry);
+ void SetCursorLinks( const Link<ScCursorRefEdit&,void>& rUp, const Link<ScCursorRefEdit&,void>& rDown );
+
+protected:
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+};
+
+class ScSolverOptionsDialog;
+
+class ScOptSolverDlg : public ScAnyRefDlgController
+{
+public:
+ ScOptSolverDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScDocShell* pDocSh, const ScAddress& aCursorPos );
+ virtual ~ScOptSolverDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ OUString maInputError;
+ OUString maConditionError;
+
+ ScDocShell* mpDocShell;
+ ScDocument& mrDoc;
+ const SCTAB mnCurTab;
+ bool mbDlgLostFocus;
+
+ std::vector<sc::ModelConstraint> m_aConditions;
+ tools::Long nScrollPos;
+
+ css::uno::Sequence<OUString> maImplNames;
+ css::uno::Sequence<OUString> maDescriptions;
+ OUString maEngine;
+ css::uno::Sequence<css::beans::PropertyValue> maProperties;
+
+ static const sal_uInt16 EDIT_ROW_COUNT = 4;
+ ScCursorRefEdit* mpLeftEdit[EDIT_ROW_COUNT];
+ formula::RefButton* mpLeftButton[EDIT_ROW_COUNT];
+ ScCursorRefEdit* mpRightEdit[EDIT_ROW_COUNT];
+ formula::RefButton* mpRightButton[EDIT_ROW_COUNT];
+ weld::ComboBox* mpOperator[EDIT_ROW_COUNT];
+ weld::Button* mpDelButton[EDIT_ROW_COUNT];
+
+ formula::RefEdit* mpEdActive;
+
+ std::unique_ptr<weld::Label> m_xFtObjectiveCell;
+ std::unique_ptr<formula::RefEdit> m_xEdObjectiveCell;
+ std::unique_ptr<formula::RefButton> m_xRBObjectiveCell;
+
+ std::unique_ptr<weld::RadioButton> m_xRbMax;
+ std::unique_ptr<weld::RadioButton> m_xRbMin;
+ std::unique_ptr<weld::RadioButton> m_xRbValue;
+ std::unique_ptr<formula::RefEdit> m_xEdTargetValue;
+ std::unique_ptr<formula::RefButton> m_xRBTargetValue;
+
+ std::unique_ptr<weld::Label> m_xFtVariableCells;
+ std::unique_ptr<formula::RefEdit> m_xEdVariableCells;
+ std::unique_ptr<formula::RefButton> m_xRBVariableCells;
+
+ std::unique_ptr<weld::Label> m_xFtCellRef; // labels are together with controls for the first row
+ std::unique_ptr<ScCursorRefEdit> m_xEdLeft1;
+ std::unique_ptr<formula::RefButton> m_xRBLeft1;
+ std::unique_ptr<weld::ComboBox> m_xLbOp1;
+ std::unique_ptr<weld::Label> m_xFtConstraint;
+ std::unique_ptr<ScCursorRefEdit> m_xEdRight1;
+ std::unique_ptr<formula::RefButton> m_xRBRight1;
+ std::unique_ptr<weld::Button> m_xBtnDel1;
+
+ std::unique_ptr<ScCursorRefEdit> m_xEdLeft2;
+ std::unique_ptr<formula::RefButton> m_xRBLeft2;
+ std::unique_ptr<weld::ComboBox> m_xLbOp2;
+ std::unique_ptr<ScCursorRefEdit> m_xEdRight2;
+ std::unique_ptr<formula::RefButton> m_xRBRight2;
+ std::unique_ptr<weld::Button> m_xBtnDel2;
+
+ std::unique_ptr<ScCursorRefEdit> m_xEdLeft3;
+ std::unique_ptr<formula::RefButton> m_xRBLeft3;
+ std::unique_ptr<weld::ComboBox> m_xLbOp3;
+ std::unique_ptr<ScCursorRefEdit> m_xEdRight3;
+ std::unique_ptr<formula::RefButton> m_xRBRight3;
+ std::unique_ptr<weld::Button> m_xBtnDel3;
+
+ std::unique_ptr<ScCursorRefEdit> m_xEdLeft4;
+ std::unique_ptr<formula::RefButton> m_xRBLeft4;
+ std::unique_ptr<weld::ComboBox> m_xLbOp4;
+ std::unique_ptr<ScCursorRefEdit> m_xEdRight4;
+ std::unique_ptr<formula::RefButton> m_xRBRight4;
+ std::unique_ptr<weld::Button> m_xBtnDel4;
+
+ std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
+
+ std::unique_ptr<weld::Button> m_xBtnOpt;
+ std::unique_ptr<weld::Button> m_xBtnClose;
+ std::unique_ptr<weld::Button> m_xBtnSolve;
+ std::unique_ptr<weld::Button> m_xBtnResetAll;
+
+ std::unique_ptr<weld::Label> m_xResultFT;
+ std::unique_ptr<weld::Widget> m_xContents;
+
+ std::shared_ptr<ScSolverOptionsDialog> m_xOptDlg;
+ std::shared_ptr<sc::SolverSettings> m_pSolverSettings;
+
+ void Init(const ScAddress& rCursorPos);
+ bool CallSolver();
+ void ReadConditions();
+ void ShowConditions();
+ void EnableButtons();
+ bool ParseRef( ScRange& rRange, const OUString& rInput, bool bAllowRange );
+ bool FindTimeout( sal_Int32& rTimeout );
+ void ShowError( bool bCondition, formula::RefEdit* pFocus );
+ void LoadSolverSettings();
+ void SaveSolverSettings();
+ bool IsEngineAvailable(std::u16string_view sEngineName);
+
+ static sc::ConstraintOperator OperatorIndexToConstraintOperator(sal_Int32 nIndex);
+
+ DECL_LINK( BtnHdl, weld::Button&, void );
+ DECL_LINK( DelBtnHdl, weld::Button&, void );
+ DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHdl, formula::RefButton&, void );
+ DECL_LINK( GetFocusHdl, weld::Widget&, void );
+ DECL_LINK( LoseEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( LoseButtonFocusHdl, formula::RefButton&, void );
+ DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void);
+ DECL_LINK( CursorUpHdl, ScCursorRefEdit&, void );
+ DECL_LINK( CursorDownHdl, ScCursorRefEdit&, void );
+ DECL_LINK( CondModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( TargetModifyHdl, formula::RefEdit&, void );
+ DECL_LINK( SelectHdl, weld::ComboBox&, void );
+};
+
+class ScSolverProgressDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Label> m_xFtTime;
+
+public:
+ ScSolverProgressDialog(weld::Window* pParent);
+ virtual ~ScSolverProgressDialog() override;
+
+ void HideTimeLimit();
+ void SetTimeLimit( sal_Int32 nSeconds );
+};
+
+class ScSolverNoSolutionDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Label> m_xFtErrorText;
+
+public:
+ ScSolverNoSolutionDialog(weld::Window* pParent, const OUString& rErrorText);
+ virtual ~ScSolverNoSolutionDialog() override;
+};
+
+class ScSolverSuccessDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Label> m_xFtResult;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ DECL_LINK(ClickHdl, weld::Button&, void);
+
+public:
+ ScSolverSuccessDialog(weld::Window* pParent, std::u16string_view rSolution);
+ virtual ~ScSolverSuccessDialog() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/output.hxx b/sc/source/ui/inc/output.hxx
new file mode 100644
index 0000000000..e4763767b7
--- /dev/null
+++ b/sc/source/ui/inc/output.hxx
@@ -0,0 +1,395 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <cellvalue.hxx>
+#include <tools/color.hxx>
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <editeng/svxenum.hxx>
+#include <vcl/outdev.hxx>
+#include <tools/degree.hxx>
+#include <o3tl/deleter.hxx>
+#include <optional>
+
+struct ScCellInfo;
+
+namespace sc {
+ class SpellCheckContext;
+}
+
+namespace editeng {
+ struct MisspellRanges;
+}
+namespace drawinglayer::processor2d { class BaseProcessor2D; }
+
+namespace vcl { class Font; }
+class EditEngine;
+class ScDocument;
+class ScPatternAttr;
+struct RowInfo;
+struct ScTableInfo;
+class ScTabViewShell;
+class ScPageBreakData;
+class FmFormView;
+class ScFieldEditEngine;
+class SdrPaintWindow;
+
+#define SC_SCENARIO_HSPACE 60
+#define SC_SCENARIO_VSPACE 50
+
+enum ScOutputType { OUTTYPE_WINDOW, OUTTYPE_PRINTER };
+
+class ClearableClipRegion;
+typedef std::unique_ptr<ClearableClipRegion, o3tl::default_delete<ClearableClipRegion>> ClearableClipRegionPtr;
+
+/// Describes reference mark to be drawn, position & size in TWIPs
+struct ReferenceMark {
+ tools::Long nX;
+ tools::Long nY;
+ tools::Long nWidth;
+ tools::Long nHeight;
+ tools::Long nTab;
+ Color aColor;
+
+ ReferenceMark()
+ : nX( 0 )
+ , nY( 0 )
+ , nWidth( 0 )
+ , nHeight( 0 )
+ , nTab( 0 )
+ , aColor( COL_AUTO ) {}
+
+ ReferenceMark( tools::Long aX,
+ tools::Long aY,
+ tools::Long aWidth,
+ tools::Long aHeight,
+ tools::Long aTab,
+ const Color& rColor )
+ : nX( aX )
+ , nY( aY )
+ , nWidth( aWidth )
+ , nHeight( aHeight )
+ , nTab( aTab )
+ , aColor( rColor ) {}
+
+ bool Is() const { return ( nWidth > 0 && nHeight > 0 ); }
+};
+
+class ScOutputData
+{
+friend class ScDrawStringsVars;
+friend class ScGridWindow;
+private:
+ struct OutputAreaParam
+ {
+ tools::Rectangle maAlignRect;
+ tools::Rectangle maClipRect;
+ tools::Long mnColWidth;
+ tools::Long mnLeftClipLength; /// length of the string getting cut off on the left.
+ tools::Long mnRightClipLength; /// length of the string getting cut off on the right.
+ bool mbLeftClip;
+ bool mbRightClip;
+ };
+
+ class DrawEditParam
+ {
+ public:
+ SvxCellHorJustify meHorJustAttr; ///< alignment attribute
+ SvxCellHorJustify meHorJustContext; ///< context depending on attribute, content and direction
+ SvxCellHorJustify meHorJustResult; ///< result for EditEngine
+ SvxCellVerJustify meVerJust;
+ SvxCellJustifyMethod meHorJustMethod;
+ SvxCellJustifyMethod meVerJustMethod;
+ SvxCellOrientation meOrient;
+ SCSIZE mnArrY;
+ SCCOL mnX;
+ SCCOL mnCellX;
+ SCROW mnCellY;
+ tools::Long mnPosX;
+ tools::Long mnPosY;
+ tools::Long mnInitPosX;
+ bool mbBreak:1;
+ bool mbCellIsValue:1;
+ bool mbAsianVertical:1;
+ bool mbPixelToLogic:1;
+ bool mbHyphenatorSet:1;
+ ScFieldEditEngine* mpEngine;
+ ScRefCellValue maCell;
+ const ScPatternAttr* mpPattern;
+ const SfxItemSet* mpCondSet;
+ const SfxItemSet* mpPreviewFontSet;
+ const ScPatternAttr* mpOldPattern;
+ const SfxItemSet* mpOldCondSet;
+ const SfxItemSet* mpOldPreviewFontSet;
+ RowInfo* mpThisRowInfo;
+ const std::vector<editeng::MisspellRanges>* mpMisspellRanges;
+
+ explicit DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, bool bCellIsValue);
+
+ bool readCellContent(const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields);
+ void setPatternToEngine(bool bUseStyleColor);
+ void calcMargins(tools::Long& rTop, tools::Long& rLeft, tools::Long& rBottom, tools::Long& rRight, double nPPTX, double nPPTY) const;
+ void calcPaperSize(Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const;
+ void getEngineSize(ScFieldEditEngine* pEngine, tools::Long& rWidth, tools::Long& rHeight) const;
+ bool hasLineBreak() const;
+ bool isHyperlinkCell() const;
+
+ /**
+ * When the text is vertically oriented, the text is either rotated 90
+ * degrees to the right or 90 degrees to the left. Note that this is
+ * different from being vertically stacked.
+ */
+ bool isVerticallyOriented() const;
+
+ /**
+ * Calculate offset position for vertically oriented (either
+ * top-bottom or bottom-top orientation) text.
+ *
+ * @param rLogicStart initial position in pixels. When the call is
+ * finished, this parameter will store the new
+ * position.
+ */
+ void calcStartPosForVertical(Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice);
+
+ void setAlignmentToEngine();
+ bool adjustHorAlignment(ScFieldEditEngine* pEngine);
+ void adjustForHyperlinkInPDF(Point aURLStart, const OutputDevice* pDev);
+ };
+
+ VclPtr<OutputDevice> mpDev; // Device
+ VclPtr<OutputDevice> mpRefDevice; // printer if used for preview
+ VclPtr<OutputDevice> pFmtDevice; // reference for text formatting
+ ScTableInfo& mrTabInfo;
+ RowInfo* pRowInfo; // Info block
+ SCSIZE nArrCount; // occupied lines in info block
+ ScDocument* mpDoc; // Document
+ SCTAB nTab; // sheet
+ tools::Long nScrX; // Output Startpos. (Pixel)
+ tools::Long nScrY;
+ tools::Long nScrW; // Output size (Pixel)
+ tools::Long nScrH;
+ tools::Long nMirrorW; // Visible output width for mirroring (default: nScrW)
+ SCCOL nX1; // Start-/End coordinates
+ SCROW nY1; // ( incl. hidden )
+ SCCOL nX2;
+ SCROW nY2;
+ SCCOL nVisX1; // Start-/End coordinates
+ SCROW nVisY1; // ( visible range )
+ SCCOL nVisX2;
+ SCROW nVisY2;
+ ScOutputType eType; // Screen/Printer ...
+ double mnPPTX; // Pixel per Twips
+ double mnPPTY;
+ Fraction aZoomX;
+ Fraction aZoomY;
+
+ ScTabViewShell* pViewShell; // for connect from visible plug-ins
+
+ FmFormView* pDrawView; // SdrView to paint to
+
+ bool bEditMode; // InPlace edited cell - do not output
+ SCCOL nEditCol;
+ SCROW nEditRow;
+
+ bool bMetaFile; // Output to metafile (not pixels!)
+
+ bool bPagebreakMode; // Page break preview
+ bool bSolidBackground; // white instead of transparent
+
+ bool mbUseStyleColor;
+ bool mbForceAutoColor;
+
+ bool mbSyntaxMode; // Syntax highlighting
+ std::optional<Color> mxValueColor;
+ std::optional<Color> mxTextColor;
+ std::optional<Color> mxFormulaColor;
+
+ Color aGridColor;
+
+ bool mbShowNullValues;
+ bool mbShowFormulas;
+ bool bShowSpellErrors; // Show spelling errors in EditObjects
+ bool bMarkClipped;
+
+ bool bSnapPixel;
+
+ bool bAnyClipped; // internal
+ bool bVertical;
+ bool bTabProtected;
+ bool bLayoutRTL;
+
+ // #i74769# use SdrPaintWindow direct, remember it during BeginDrawLayers/EndDrawLayers
+ SdrPaintWindow* mpTargetPaintWindow;
+ const sc::SpellCheckContext* mpSpellCheckCxt;
+ std::unique_ptr<ScFieldEditEngine> mxOutputEditEngine;
+
+ // private methods
+
+ bool GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY,
+ SCCOL& rOverX, SCROW& rOverY, bool bVisRowChanged );
+ bool IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY );
+ void GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTab, ScRefCellValue& rCell );
+
+ bool IsAvailable( SCCOL nX, SCROW nY );
+
+ void GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::Long nPosY,
+ SCCOL nCellX, SCROW nCellY, tools::Long nNeeded,
+ const ScPatternAttr& rPattern,
+ sal_uInt16 nHorJustify, bool bCellIsValue,
+ bool bBreak, bool bOverwrite,
+ OutputAreaParam& rParam );
+
+ void ShrinkEditEngine( EditEngine& rEngine, const tools::Rectangle& rAlignRect,
+ tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM,
+ bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic,
+ tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel,
+ bool& rLeftClip, bool& rRightClip );
+
+ void SetSyntaxColor( vcl::Font* pFont, const ScRefCellValue& rCell );
+ void SetEditSyntaxColor( EditEngine& rEngine, const ScRefCellValue& rCell );
+
+ double GetStretch() const;
+
+ void DrawRotatedFrame(vcl::RenderContext& rRenderContext); // pixel
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> CreateProcessor2D( );
+
+ void DrawEditStandard(DrawEditParam& rParam);
+ void DrawEditBottomTop(DrawEditParam& rParam);
+ void DrawEditTopBottom(DrawEditParam& rParam);
+ void DrawEditStacked(DrawEditParam& rParam);
+ void DrawEditAsianVertical(DrawEditParam& rParam);
+
+ void InitOutputEditEngine();
+
+ void SetClipMarks( OutputAreaParam &aAreaParam, ScCellInfo* pClipMarkCell,
+ SvxCellHorJustify eOutHorJust, tools::Long nLayoutSign );
+
+ void ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
+ bool bMerged, OutputAreaParam& aAreaParam, bool bTop );
+
+ ClearableClipRegionPtr Clip(DrawEditParam& rParam, const Size& aCellSize, OutputAreaParam& aAreaParam,
+ tools::Long nEngineWidth, bool bWrapFields, bool bTop);
+
+ bool AdjustAreaParamClipRect(OutputAreaParam& rAreaParam);
+ tools::Long SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString,
+ tools::Long& rNeededPixel, tools::Long nAddWidthPixels );
+
+ // Check for and set cell rotations at OutputData to have it available
+ // in the svx tooling to render the borders. Moved to private section
+ // and the single call to end of constructor to be sure this always happens
+ void SetCellRotations();
+
+public:
+
+ /**
+ * @param nNewScrX: X-Offset in the output device for the table
+ * @param nNewScrY: Y-Offset in the output device for the table
+ *
+ */
+ ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
+ ScTableInfo& rTabInfo, ScDocument* pNewDoc,
+ SCTAB nNewTab, tools::Long nNewScrX, tools::Long nNewScrY,
+ SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
+ double nPixelPerTwipsX, double nPixelPerTwipsY,
+ const Fraction* pZoomX = nullptr,
+ const Fraction* pZoomY = nullptr );
+
+ ~ScOutputData();
+
+ void SetSpellCheckContext( const sc::SpellCheckContext* pCxt );
+ void SetContentDevice( OutputDevice* pContentDev );
+
+ void SetRefDevice( OutputDevice* pRDev );
+ void SetFmtDevice( OutputDevice* pRDev );
+ void SetViewShell( ScTabViewShell* pSh ) { pViewShell = pSh; }
+
+ void SetDrawView( FmFormView* pNew ) { pDrawView = pNew; }
+
+ void SetSolidBackground( bool bSet ) { bSolidBackground = bSet; }
+ void SetUseStyleColor( bool bSet );
+
+ void SetEditCell( SCCOL nCol, SCROW nRow );
+ void SetSyntaxMode( bool bNewMode );
+ void SetMetaFileMode( bool bNewMode );
+ void SetGridColor( const Color& rColor );
+ void SetMarkClipped( bool bSet );
+ void SetShowNullValues ( bool bSet );
+ void SetShowFormulas ( bool bSet );
+ void SetShowSpellErrors( bool bSet );
+ void SetMirrorWidth( tools::Long nNew );
+ tools::Long GetScrW() const { return nScrW; }
+ tools::Long GetScrH() const { return nScrH; }
+
+ void SetSnapPixel();
+
+ void DrawGrid(vcl::RenderContext& rRenderContext, bool bGrid, bool bPage, bool bMergeCover = false);
+ void DrawStrings( bool bPixelToLogic = false );
+
+ /// Draw all strings
+ void LayoutStrings(bool bPixelToLogic);
+
+ void DrawDocumentBackground();
+ void DrawBackground(vcl::RenderContext& rRenderContext);
+ void DrawShadow();
+ void DrawExtraShadow(bool bLeft, bool bTop, bool bRight, bool bBottom);
+ void DrawFrame(vcl::RenderContext& rRenderContext);
+
+ // with logic MapMode set!
+ void DrawEdit(bool bPixelToLogic);
+ void DrawRotated(bool bPixelToLogic); // logical
+
+ void DrawClear();
+
+ // #i72502# printer only command set
+ Point PrePrintDrawingLayer(tools::Long nLogStX, tools::Long nLogStY );
+ void PostPrintDrawingLayer(const Point& rMMOffset); // #i74768# need offset for FormLayer
+ void PrintDrawingLayer(SdrLayerID nLayer, const Point& rMMOffset);
+
+ // only screen:
+ void DrawSelectiveObjects(SdrLayerID nLayer);
+
+ bool SetChangedClip(); // sal_False = not
+ vcl::Region GetChangedAreaRegion();
+
+ void FindChanged();
+ void SetPagebreakMode( ScPageBreakData* pPageData );
+ /// Draws reference mark and returns its properties
+ void DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor, bool bHandle );
+ ReferenceMark FillReferenceMark( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor );
+ void DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor, sal_uInt16 nType );
+ void DrawChangeTrack();
+ void DrawClipMarks();
+
+ void DrawNoteMarks(vcl::RenderContext& rRenderContext);
+ void DrawFormulaMarks(vcl::RenderContext& rRenderContext);
+ void AddPDFNotes();
+ void DrawSparklines(vcl::RenderContext& rRenderContext);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/overlayobject.hxx b/sc/source/ui/inc/overlayobject.hxx
new file mode 100644
index 0000000000..35ea695eda
--- /dev/null
+++ b/sc/source/ui/inc/overlayobject.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <vcl/font.hxx>
+#include <vcl/mapmod.hxx>
+
+class ScOverlayDashedBorder : public sdr::overlay::OverlayObject
+{
+public:
+ ScOverlayDashedBorder(const ::basegfx::B2DRange& rRange, const Color& rColor);
+ virtual ~ScOverlayDashedBorder() override;
+
+ virtual void Trigger(sal_uInt32 nTime) override;
+
+ virtual void stripeDefinitionHasChanged() override;
+
+protected:
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+private:
+ ::basegfx::B2DRange maRange;
+ bool mbToggle;
+};
+
+class ScOverlayHint : public sdr::overlay::OverlayObject
+{
+public:
+ ScOverlayHint(OUString aTit, const OUString& rMsg, const Color& rColor, vcl::Font aFont);
+ Size GetSizePixel() const;
+ void SetPos(const Point& rPos, const MapMode& rMode);
+
+public:
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+private:
+ drawinglayer::primitive2d::Primitive2DContainer createOverlaySequence(sal_Int32 nLeft, sal_Int32 nTop, const MapMode &rMapMode, basegfx::B2DRange &rRange) const;
+
+ const OUString m_aTitle;
+ const OUString m_aMessage;
+ const vcl::Font m_aTextFont;
+ MapMode m_aMapMode;
+ sal_Int32 m_nLeft;
+ sal_Int32 m_nTop;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pagedata.hxx b/sc/source/ui/inc/pagedata.hxx
new file mode 100644
index 0000000000..265a857432
--- /dev/null
+++ b/sc/source/ui/inc/pagedata.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 .
+ */
+
+#pragma once
+
+#include <tools/long.hxx>
+#include <address.hxx>
+#include <memory>
+#include <vector>
+
+class ScPrintRangeData
+{
+private:
+ ScRange aPrintRange;
+ std::vector<SCCOL>
+ mvPageEndX;
+ std::vector<SCROW>
+ mvPageEndY;
+ tools::Long nFirstPage;
+ bool bTopDown;
+ bool bAutomatic;
+
+public:
+ ScPrintRangeData();
+ ~ScPrintRangeData();
+
+ void SetPrintRange( const ScRange& rNew ) { aPrintRange = rNew; }
+ const ScRange& GetPrintRange() const { return aPrintRange; }
+
+ void SetPagesX( size_t nCount, const SCCOL* pEnd );
+ void SetPagesY( size_t nCount, const SCROW* pEnd );
+
+ size_t GetPagesX() const { return mvPageEndX.size(); }
+ const SCCOL* GetPageEndX() const { return mvPageEndX.data(); }
+ size_t GetPagesY() const { return mvPageEndY.size(); }
+ const SCROW* GetPageEndY() const { return mvPageEndY.data(); }
+
+ void SetFirstPage( tools::Long nNew ) { nFirstPage = nNew; }
+ tools::Long GetFirstPage() const { return nFirstPage; }
+ void SetTopDown( bool bSet ) { bTopDown = bSet; }
+ bool IsTopDown() const { return bTopDown; }
+ void SetAutomatic( bool bSet ) { bAutomatic = bSet; }
+ bool IsAutomatic() const { return bAutomatic; }
+};
+
+class ScPageBreakData
+{
+private:
+ size_t nAlloc;
+ size_t nUsed;
+ std::unique_ptr<ScPrintRangeData[]> pData;
+
+public:
+ ScPageBreakData(size_t nMax);
+ ~ScPageBreakData();
+
+ size_t GetCount() const { return nUsed; }
+ ScPrintRangeData& GetData(size_t i);
+
+ bool operator==( const ScPageBreakData& rOther ) const;
+
+ void AddPages();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pfiltdlg.hxx b/sc/source/ui/inc/pfiltdlg.hxx
new file mode 100644
index 0000000000..892d865525
--- /dev/null
+++ b/sc/source/ui/inc/pfiltdlg.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <address.hxx>
+#include <queryparam.hxx>
+#include <array>
+#include <memory>
+
+class ScViewData;
+class ScDocument;
+class ScQueryItem;
+class SfxItemSet;
+struct ScFilterEntries;
+
+class ScPivotFilterDlg : public weld::GenericDialogController
+{
+public:
+ ScPivotFilterDlg(weld::Window* pParent, const SfxItemSet& rArgSet, SCTAB nSourceTab);
+ virtual ~ScPivotFilterDlg() override;
+
+ const ScQueryItem& GetOutputItem();
+
+private:
+ const OUString aStrNone;
+ const OUString aStrEmpty;
+ const OUString aStrNotEmpty;
+ const OUString aStrColumn;
+
+ const sal_uInt16 nWhichQuery;
+ const ScQueryParam theQueryData;
+ std::unique_ptr<ScQueryItem> pOutItem;
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+ SCTAB nSrcTab;
+
+ std::unique_ptr<weld::ComboBox> m_xLbField1;
+ std::unique_ptr<weld::ComboBox> m_xLbCond1;
+ std::unique_ptr<weld::ComboBox> m_xEdVal1;
+
+ std::unique_ptr<weld::ComboBox> m_xLbConnect1;
+ std::unique_ptr<weld::ComboBox> m_xLbField2;
+ std::unique_ptr<weld::ComboBox> m_xLbCond2;
+ std::unique_ptr<weld::ComboBox> m_xEdVal2;
+
+ std::unique_ptr<weld::ComboBox> m_xLbConnect2;
+ std::unique_ptr<weld::ComboBox> m_xLbField3;
+ std::unique_ptr<weld::ComboBox> m_xLbCond3;
+ std::unique_ptr<weld::ComboBox> m_xEdVal3;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnCase;
+ std::unique_ptr<weld::CheckButton> m_xBtnRegExp;
+ std::unique_ptr<weld::CheckButton> m_xBtnUnique;
+ std::unique_ptr<weld::Label> m_xFtDbArea;
+
+ weld::ComboBox* aValueEdArr[3];
+ weld::ComboBox* aFieldLbArr[3];
+ weld::ComboBox* aCondLbArr[3];
+
+ std::array<std::unique_ptr<ScFilterEntries>, MAXCOLCOUNT> m_pEntryLists;
+
+private:
+ void Init ( const SfxItemSet& rArgSet );
+ void FillFieldLists ();
+ void UpdateValueList ( sal_uInt16 nList );
+ void ClearValueList ( sal_uInt16 nList );
+ sal_uInt16 GetFieldSelPos ( SCCOL nField );
+
+ // Handler:
+ DECL_LINK( LbSelectHdl, weld::ComboBox&, void );
+ DECL_LINK( ValModifyHdl, weld::ComboBox&, void );
+ DECL_LINK( CheckBoxHdl, weld::Toggleable&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pfuncache.hxx b/sc/source/ui/inc/pfuncache.hxx
new file mode 100644
index 0000000000..5621cd2628
--- /dev/null
+++ b/sc/source/ui/inc/pfuncache.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <tools/gen.hxx>
+#include <rangelst.hxx>
+#include <printopt.hxx>
+
+class ScDocShell;
+class ScMarkData;
+class OutputDevice;
+
+/** Possible types of selection for print functions */
+
+enum class ScPrintSelectionMode
+{
+ Invalid,
+ Document,
+ Cursor,
+ Range,
+ RangeExclusivelyOleAndDrawObjects
+};
+
+/** Stores the selection in the ScPrintFuncCache so it is only used
+ for the same selection again. */
+
+class ScPrintSelectionStatus
+{
+ ScPrintSelectionMode eMode;
+ ScRangeList aRanges;
+ ScPrintOptions aOptions;
+
+public:
+ ScPrintSelectionStatus() : eMode(ScPrintSelectionMode::Invalid) {}
+
+ void SetMode(ScPrintSelectionMode eNew) { eMode = eNew; }
+ void SetRanges(const ScRangeList& rNew) { aRanges = rNew; }
+ void SetOptions(const ScPrintOptions& rNew) { aOptions = rNew; }
+
+ bool operator==(const ScPrintSelectionStatus& rOther) const
+ { return eMode == rOther.eMode && aRanges == rOther.aRanges && aOptions == rOther.aOptions; }
+
+ ScPrintSelectionMode GetMode() const { return eMode; }
+ const ScPrintOptions& GetOptions() const { return aOptions; }
+};
+
+/** The range that is printed on a page (excluding repeated columns/rows),
+ and its position on the page, used to find hyperlink targets. */
+
+struct ScPrintPageLocation
+{
+ tools::Long nPage;
+ ScRange aCellRange;
+ tools::Rectangle aRectangle; // pixels
+
+ ScPrintPageLocation() :
+ nPage(-1) {} // default: invalid
+
+ ScPrintPageLocation( tools::Long nP, const ScRange& rRange, const tools::Rectangle& rRect ) :
+ nPage(nP), aCellRange(rRange), aRectangle(rRect) {}
+};
+
+/** Stores the data for printing that is needed from several sheets,
+ so it doesn't have to be calculated for rendering each page. */
+
+class ScPrintFuncCache
+{
+ ScPrintSelectionStatus aSelection;
+ ScDocShell* pDocSh;
+ tools::Long nTotalPages;
+ std::vector<tools::Long> nPages;
+ std::vector<tools::Long> nFirstAttr;
+ std::vector<ScPrintPageLocation> aLocations;
+ bool bLocInitialized;
+
+public:
+ ScPrintFuncCache( ScDocShell* pD, const ScMarkData& rMark,
+ ScPrintSelectionStatus aStatus );
+ ~ScPrintFuncCache();
+
+ bool IsSameSelection( const ScPrintSelectionStatus& rStatus ) const;
+
+ void InitLocations( const ScMarkData& rMark, OutputDevice* pDev );
+ bool FindLocation( const ScAddress& rCell, ScPrintPageLocation& rLocation ) const;
+
+ tools::Long GetPageCount() const { return nTotalPages; }
+ tools::Long GetFirstAttr( SCTAB nTab ) const { return nFirstAttr[nTab]; }
+ SCTAB GetTabForPage( tools::Long nPage ) const;
+ tools::Long GetTabStart( SCTAB nTab ) const;
+ tools::Long GetDisplayStart( SCTAB nTab ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pgbrksh.hxx b/sc/source/ui/inc/pgbrksh.hxx
new file mode 100644
index 0000000000..7b00f315bc
--- /dev/null
+++ b/sc/source/ui/inc/pgbrksh.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 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+
+#include <shellids.hxx>
+
+class SfxModule;
+class ScTabViewShell;
+
+class ScPageBreakShell final : public SfxShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_PAGEBREAK_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScPageBreakShell(ScTabViewShell* pView);
+ virtual ~ScPageBreakShell() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pivotsh.hxx b/sc/source/ui/inc/pivotsh.hxx
new file mode 100644
index 0000000000..d69b80ee43
--- /dev/null
+++ b/sc/source/ui/inc/pivotsh.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/shell.hxx>
+
+#include <shellids.hxx>
+
+class ScTabViewShell;
+class ScDPObject;
+class SfxModule;
+
+class ScPivotShell final : public SfxShell
+{
+public:
+ SFX_DECL_INTERFACE(SCID_PIVOT_SHELL)
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScPivotShell(ScTabViewShell* pView);
+ virtual ~ScPivotShell() override;
+
+ void Execute(const SfxRequest& rReq);
+ void GetState(SfxItemSet& rSet);
+
+private:
+ ScTabViewShell* pViewShell;
+
+ ScDPObject* GetCurrDPObject();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pntlock.hxx b/sc/source/ui/inc/pntlock.hxx
new file mode 100644
index 0000000000..a6c569e492
--- /dev/null
+++ b/sc/source/ui/inc/pntlock.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 <rangelst.hxx>
+
+class ScPaintLockData
+{
+private:
+ ScRangeListRef xRangeList;
+ sal_uInt16 nLevel;
+ sal_uInt16 nDocLevel;
+ PaintPartFlags nParts;
+ bool bModified;
+
+public:
+ ScPaintLockData();
+ ~ScPaintLockData();
+
+ void AddRange( const ScRange& rRange, PaintPartFlags nP );
+
+ void SetModified() { bModified = true; }
+ void IncLevel(bool bDoc)
+ { if (bDoc) ++nDocLevel; else ++nLevel; }
+ void DecLevel(bool bDoc)
+ { if (bDoc) --nDocLevel; else --nLevel; }
+
+ const ScRangeListRef& GetRangeList() const { return xRangeList; }
+ PaintPartFlags GetParts() const { return nParts; }
+ sal_uInt16 GetLevel(bool bDoc) const
+ { return bDoc ? nDocLevel : nLevel; }
+ bool GetModified() const { return bModified; }
+
+ /** for recovery after reset */
+ void SetDocLevel(sal_uInt16 nNew)
+ { nDocLevel = nNew; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/preview.hxx b/sc/source/ui/inc/preview.hxx
new file mode 100644
index 0000000000..606454d992
--- /dev/null
+++ b/sc/source/ui/inc/preview.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/window.hxx>
+#include "printfun.hxx"
+#include <markdata.hxx>
+
+#include <vector>
+
+class ScDocShell;
+class ScPreviewShell;
+class FmFormView;
+
+class SAL_DLLPUBLIC_RTTI ScPreview : public vcl::Window
+{
+private:
+ ScMarkData::MarkedTabsType maSelectedTabs;
+ // set:
+ tools::Long nPageNo; // Pages in document
+ sal_uInt16 nZoom; // set Zoom
+ Point aOffset; // positive
+
+ // calculated:
+ SCTAB nTabCount;
+ SCTAB nTabsTested; // for how many sheets is nPages valid?
+ std::vector<tools::Long> nPages;
+ std::vector<tools::Long> nFirstAttr;
+ SCTAB nTab; // Sheet
+ tools::Long nTabPage; // Page of sheet
+ tools::Long nTabStart; // First (real) page of the sheet
+ tools::Long nDisplayStart; // same as above, relative to the start of counting
+ DateTime aDateTime;
+ tools::Long nTotalPages;
+ ScPrintState aState;
+ std::unique_ptr<ScPreviewLocationData> pLocationData; // stores table layout for accessibility API
+ std::unique_ptr<FmFormView> pDrawView;
+
+ // internal:
+ ScDocShell* pDocShell;
+ ScPreviewShell* pViewShell;
+
+ bool bInGetState:1;
+ bool bValid:1; // the following values true
+ bool bStateValid:1;
+ bool bLocationValid:1;
+ bool bInPaint:1;
+ bool bInSetZoom:1;
+ bool bLeftRulerMove:1;
+ bool bRightRulerMove:1;
+ bool bTopRulerMove:1;
+ bool bBottomRulerMove:1;
+ bool bHeaderRulerMove:1;
+ bool bFooterRulerMove:1;
+ bool bLeftRulerChange:1;
+ bool bRightRulerChange:1;
+ bool bTopRulerChange:1;
+ bool bBottomRulerChange:1;
+ bool bHeaderRulerChange:1;
+ bool bFooterRulerChange:1;
+ bool bPageMargin:1;
+ bool bColRulerMove:1;
+ bool mbHasEmptyRangeTable:1; /// we have at least one sheet with empty print range (print range set to '- none -').
+
+ ScRange aPageArea;
+ std::vector<tools::Long> mvRight;
+ tools::Long nLeftPosition;
+ tools::Long mnScale;
+ SCCOL nColNumberButtonDown;
+ Point aButtonDownChangePoint;
+ Point aButtonDownPt;
+ Point aButtonUpPt;
+ tools::Long nHeaderHeight;
+ tools::Long nFooterHeight;
+
+ void TestLastPage();
+ void CalcPages();
+ void RecalcPages();
+ void UpdateDrawView();
+ void DoPrint( ScPreviewLocationData* pFillLocation );
+
+ void InvalidateLocationData( SfxHintId nId );
+
+ using Window::SetZoom;
+
+protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+ virtual void Command( const CommandEvent& rCEvt ) override;
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
+
+public:
+ ScPreview( vcl::Window* pParent, ScDocShell* pDocSh, ScPreviewShell* pViewSh );
+ virtual ~ScPreview() override;
+ virtual void dispose() override;
+
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+
+ SC_DLLPUBLIC void DataChanged(bool bNewTime); // Instead of calling Invalidate
+ void DoInvalidate();
+
+ void SetXOffset( tools::Long nX );
+ void SetYOffset( tools::Long nY );
+ void SetZoom(sal_uInt16 nNewZoom);
+ SC_DLLPUBLIC void SetPageNo( tools::Long nPage );
+
+ bool GetPageMargins() const { return bPageMargin; }
+ void SetPageMargins( bool bVal ) { bPageMargin = bVal; }
+ void DrawInvert( tools::Long nDragPos, PointerStyle nFlags );
+ void DragMove( tools::Long nDragMovePos, PointerStyle nFlags );
+
+ const ScPreviewLocationData& GetLocationData();
+
+ OUString GetPosString();
+
+ tools::Long GetPageNo() const { return nPageNo; }
+ sal_uInt16 GetZoom() const { return nZoom; }
+ const Point& GetOffset() const { return aOffset; }
+
+ SCTAB GetTab() { if (!bValid) { CalcPages(); RecalcPages(); } return nTab; }
+ tools::Long GetTotalPages() { if (!bValid) { CalcPages(); RecalcPages(); } return nTotalPages; }
+
+ bool AllTested() const { return bValid && nTabsTested >= nTabCount; }
+
+ sal_uInt16 GetOptimalZoom(bool bWidthOnly);
+ SC_DLLPUBLIC tools::Long GetFirstPage(SCTAB nTab);
+
+ void CalcAll() { CalcPages(); }
+ void SetInGetState(bool bSet) { bInGetState = bSet; }
+
+ DECL_DLLPRIVATE_STATIC_LINK( ScPreview, InvalidateHdl, void*, void );
+ static void StaticInvalidate();
+
+ FmFormView* GetDrawView() { return pDrawView.get(); }
+
+ SC_DLLPUBLIC void SetSelectedTabs(const ScMarkData& rMark);
+ const ScMarkData::MarkedTabsType& GetSelectedTabs() const { return maSelectedTabs; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/prevloc.hxx b/sc/source/ui/inc/prevloc.hxx
new file mode 100644
index 0000000000..17a7353b7b
--- /dev/null
+++ b/sc/source/ui/inc/prevloc.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#include <address.hxx>
+
+#include <vcl/mapmod.hxx>
+#include <vcl/vclptr.hxx>
+#include <tools/gen.hxx>
+
+#include <memory>
+#include <list>
+
+#define SC_PREVIEW_MAXRANGES 4
+#define SC_PREVIEW_RANGE_EDGE 0
+#define SC_PREVIEW_RANGE_REPCOL 1
+#define SC_PREVIEW_RANGE_REPROW 2
+#define SC_PREVIEW_RANGE_TAB 3
+
+class OutputDevice;
+class ScDocument;
+struct ScPreviewLocationEntry;
+
+struct ScPreviewColRowInfo
+{
+ bool bIsHeader;
+ SCCOLROW nDocIndex;
+ tools::Long nPixelStart;
+ tools::Long nPixelEnd;
+
+ void Set( bool bHeader, SCCOLROW nIndex, tools::Long nStart, tools::Long nEnd )
+ {
+ bIsHeader = bHeader;
+ nDocIndex = nIndex;
+ nPixelStart = nStart;
+ nPixelEnd = nEnd;
+ }
+};
+
+class ScPreviewTableInfo
+{
+ SCTAB nTab;
+ SCCOL nCols;
+ SCROW nRows;
+ std::unique_ptr<ScPreviewColRowInfo[]>
+ pColInfo;
+ std::unique_ptr<ScPreviewColRowInfo[]>
+ pRowInfo;
+
+public:
+ ScPreviewTableInfo();
+ ~ScPreviewTableInfo();
+
+ SCTAB GetTab() const { return nTab; }
+ SCCOL GetCols() const { return nCols; }
+ SCROW GetRows() const { return nRows; }
+ const ScPreviewColRowInfo* GetColInfo() const { return pColInfo.get(); }
+ const ScPreviewColRowInfo* GetRowInfo() const { return pRowInfo.get(); }
+
+ void SetTab( SCTAB nNewTab );
+ void SetColInfo( SCCOL nCount, ScPreviewColRowInfo* pNewInfo );
+ void SetRowInfo( SCROW nCount, ScPreviewColRowInfo* pNewInfo );
+ void LimitToArea( const tools::Rectangle& rPixelArea );
+};
+
+class ScPreviewLocationData
+{
+public:
+ typedef std::list<std::unique_ptr<ScPreviewLocationEntry>> Entries_t;
+private:
+ VclPtr<OutputDevice> pWindow;
+ ScDocument* pDoc;
+ MapMode aCellMapMode;
+ MapMode aDrawMapMode[SC_PREVIEW_MAXRANGES];
+ tools::Rectangle aDrawRectangle[SC_PREVIEW_MAXRANGES];
+ sal_uInt8 aDrawRangeId[SC_PREVIEW_MAXRANGES];
+ sal_uInt16 nDrawRanges;
+ SCTAB nPrintTab;
+ Entries_t m_Entries;
+
+ tools::Rectangle GetOffsetPixel( const ScAddress& rCellPos, const ScRange& rRange ) const;
+
+public:
+ ScPreviewLocationData( ScDocument* pDocument, OutputDevice* pWin );
+ ~ScPreviewLocationData();
+
+ void SetCellMapMode( const MapMode& rMapMode );
+ void SetPrintTab( SCTAB nNew );
+ void Clear();
+ void AddCellRange( const tools::Rectangle& rRect, const ScRange& rRange, bool bRepCol, bool bRepRow,
+ const MapMode& rDrawMap );
+ void AddColHeaders( const tools::Rectangle& rRect, SCCOL nStartCol, SCCOL nEndCol, bool bRepCol );
+ void AddRowHeaders( const tools::Rectangle& rRect, SCROW nStartRow, SCROW nEndRow, bool bRepRow );
+ void AddHeaderFooter( const tools::Rectangle& rRect, bool bHeader, bool bLeft );
+ void AddNoteMark( const tools::Rectangle& rRect, const ScAddress& rPos );
+ void AddNoteText( const tools::Rectangle& rRect, const ScAddress& rPos );
+
+ SCTAB GetPrintTab() const { return nPrintTab; }
+
+ // Get info on visible columns/rows in the visible area
+ void GetTableInfo( const tools::Rectangle& rVisiblePixel, ScPreviewTableInfo& rInfo ) const;
+
+ sal_uInt16 GetDrawRanges() const { return nDrawRanges; }
+ void GetDrawRange( sal_uInt16 nPos, tools::Rectangle& rPixelRect, MapMode& rMapMode, sal_uInt8& rRangeId ) const;
+
+ bool GetHeaderPosition( tools::Rectangle& rHeaderRect ) const;
+ bool GetFooterPosition( tools::Rectangle& rFooterRect ) const;
+ bool IsHeaderLeft() const;
+ bool IsFooterLeft() const;
+
+ tools::Long GetNoteCountInRange( const tools::Rectangle& rVisiblePixel, bool bNoteMarks ) const;
+ bool GetNoteInRange( const tools::Rectangle& rVisiblePixel, tools::Long nIndex, bool bNoteMarks,
+ ScAddress& rCellPos, tools::Rectangle& rNoteRect ) const;
+ tools::Rectangle GetNoteInRangeOutputRect(const tools::Rectangle& rVisiblePixel, bool bNoteMarks,
+ const ScAddress& aCellPos) const;
+
+ // Check if any cells (including column/row headers) are in the visible area
+ bool HasCellsInRange( const tools::Rectangle& rVisiblePixel ) const;
+
+ void GetCellPosition( const ScAddress& rCellPos, tools::Rectangle& rCellRect ) const;
+
+ // returns the rectangle where the EditEngine draws the text of a Header Cell
+ // if bColHeader is true it returns the rectangle of the header of the column in rCellPos
+ // otherwise of the header of the row in rCellPos
+ tools::Rectangle GetHeaderCellOutputRect(const tools::Rectangle& rVisRect, const ScAddress& rCellPos, bool bColHeader) const;
+ tools::Rectangle GetCellOutputRect(const ScAddress& rCellPos) const;
+
+ // Query the range and rectangle of the main (non-repeat) cell range.
+ // Returns sal_False if not contained.
+ bool GetMainCellRange( ScRange& rRange, tools::Rectangle& rPixRect ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/prevwsh.hxx b/sc/source/ui/inc/prevwsh.hxx
new file mode 100644
index 0000000000..5c2a37c4a5
--- /dev/null
+++ b/sc/source/ui/inc/prevwsh.hxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include <scdllapi.h>
+
+#include <sfx2/viewsh.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <svtools/scrolladaptor.hxx>
+#include <vcl/syswin.hxx>
+
+#include <shellids.hxx>
+
+class ScDocument;
+class ScDocShell;
+class ScPreview;
+struct ScHeaderFieldData;
+class ScPreviewLocationData;
+class CommandEvent;
+class SfxViewFactory;
+
+class SC_DLLPUBLIC ScPreviewShell final : public SfxViewShell
+{
+ ScDocShell* pDocShell;
+
+ VclPtr<SystemWindow> mpFrameWindow;
+ VclPtr<ScPreview> pPreview; // Output window
+ VclPtr<ScrollAdaptor> pHorScroll;
+ VclPtr<ScrollAdaptor> pVerScroll;
+
+ TriState nSourceDesignMode; // form design mode from TabView
+ SvxZoomType eZoom;
+ tools::Long nMaxVertPos;
+ tools::Long nPrevHThumbPos;
+ tools::Long nPrevVThumbPos;
+
+ std::unique_ptr<SfxBroadcaster> pAccessibilityBroadcaster;
+ bool GetPageSize( Size& aPageSize );
+private:
+ void Construct( vcl::Window* pParent );
+ DECL_DLLPRIVATE_LINK( HorzScrollHandler, weld::Scrollbar&, void );
+ DECL_DLLPRIVATE_LINK( VertScrollHandler, weld::Scrollbar&, void );
+ void ScrollHandler(ScrollAdaptor* pScrollBar);
+ DECL_DLLPRIVATE_LINK( CloseHdl, SystemWindow&, void);
+ void DoScroll( sal_uInt16 nMode );
+ void ExitPreview();
+
+ virtual void Activate(bool bMDI) override;
+ void AdjustPosSizePixel( const Point &rPos, const Size &rSize );
+
+ virtual void InnerResizePixel( const Point &rOfs, const Size &rSize, bool inplaceEditModeChange ) override;
+ virtual void OuterResizePixel( const Point &rOfs, const Size &rSize ) override;
+
+ virtual void WriteUserData(OUString &, bool bBrowse = false) override;
+ virtual void ReadUserData(const OUString &, bool bBrowse = false) override;
+
+ virtual void WriteUserDataSequence (css::uno::Sequence < css::beans::PropertyValue >& ) override;
+ virtual void ReadUserDataSequence (const css::uno::Sequence < css::beans::PropertyValue >& ) override;
+
+public:
+ SFX_DECL_INTERFACE(SCID_PREVIEW_SHELL)
+ SFX_DECL_VIEWFACTORY(ScPreviewShell);
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ ScPreviewShell(SfxViewFrame& rViewFrame, SfxViewShell* pOldSh);
+
+ virtual ~ScPreviewShell() override;
+
+ void InitStartTable(SCTAB nTab);
+
+ void UpdateScrollBars();
+ void UpdateNeededScrollBars(bool bFromZoom);
+ bool ScrollCommand( const CommandEvent& rCEvt );
+
+ void Execute( SfxRequest& rReq );
+ void GetState( SfxItemSet& rSet );
+
+ void FillFieldData( ScHeaderFieldData& rData );
+
+ TriState GetSourceDesignMode() const { return nSourceDesignMode; }
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ virtual SfxPrinter* GetPrinter( bool bCreate = false ) override;
+ virtual sal_uInt16 SetPrinter( SfxPrinter* pNewPrinter, SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL ) override;
+ virtual bool HasPrintOptionsPage() const override;
+ virtual std::unique_ptr<SfxTabPage> CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions) override;
+
+ void AddAccessibilityObject( SfxListener& rObject );
+ void RemoveAccessibilityObject( SfxListener& rObject );
+ void BroadcastAccessibility( const SfxHint &rHint );
+ bool HasAccessibilityObjects() const;
+
+ const ScPreviewLocationData& GetLocationData();
+ ScDocument& GetDocument();
+ ScPreview* GetPreview() { return pPreview; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/printfun.hxx b/sc/source/ui/inc/printfun.hxx
new file mode 100644
index 0000000000..f9780bd0e5
--- /dev/null
+++ b/sc/source/ui/inc/printfun.hxx
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include <pagepar.hxx>
+#include <editutil.hxx>
+
+class SfxPrinter;
+class ScDocShell;
+class ScDocument;
+class ScViewData;
+class SfxItemSet;
+class ScPageHFItem;
+class EditTextObject;
+class MultiSelection;
+class ScPageBreakData;
+class ScPreviewLocationData;
+class ScPrintOptions;
+class SvxBoxItem;
+class SvxBrushItem;
+class SvxShadowItem;
+class FmFormView;
+
+#define RANGENO_NORANGE USHRT_MAX
+
+constexpr sal_Int64 PRINT_HEADER_WIDTH = o3tl::toTwips(1, o3tl::Length::cm);
+constexpr sal_Int64 PRINT_HEADER_HEIGHT = o3tl::toTwips(12.8, o3tl::Length::pt);
+
+ // Settings for headers/footers
+struct ScPrintHFParam
+{
+ bool bEnable;
+ bool bDynamic;
+ bool bShared;
+ bool bSharedFirst;
+ tools::Long nHeight; // in total (height + distance + frames)
+ tools::Long nManHeight; // set size (min when dynamic)
+ sal_uInt16 nDistance;
+ sal_uInt16 nLeft; // edges
+ sal_uInt16 nRight;
+ const ScPageHFItem* pLeft;
+ const ScPageHFItem* pRight;
+ const ScPageHFItem* pFirst;
+ const SvxBoxItem* pBorder;
+ const SvxBrushItem* pBack;
+ const SvxShadowItem* pShadow;
+};
+
+class ScPageRowEntry
+{
+private:
+ SCROW nStartRow;
+ SCROW nEndRow;
+ size_t nPagesX;
+ std::vector<bool> aHidden;
+ //! Cache Number of really visible?
+
+public:
+ ScPageRowEntry() { nStartRow = nEndRow = 0; nPagesX = 0; }
+
+ ScPageRowEntry(const ScPageRowEntry& r);
+ ScPageRowEntry& operator=(const ScPageRowEntry& r);
+
+ SCROW GetStartRow() const { return nStartRow; }
+ SCROW GetEndRow() const { return nEndRow; }
+ size_t GetPagesX() const { return nPagesX; }
+ void SetStartRow(SCROW n) { nStartRow = n; }
+ void SetEndRow(SCROW n) { nEndRow = n; }
+
+ void SetPagesX(size_t nNew);
+ void SetHidden(size_t nX);
+ bool IsHidden(size_t nX) const;
+
+ size_t CountVisible() const;
+};
+
+namespace sc
+{
+
+struct PrintPageRangesInput
+{
+ bool m_bSkipEmpty;
+ bool m_bPrintArea;
+ ScRange m_aRange;
+ Size m_aDocSize;
+
+ PrintPageRangesInput()
+ : m_bSkipEmpty(false)
+ , m_bPrintArea(false)
+ {}
+
+ PrintPageRangesInput(bool bSkipEmpty, bool bPrintArea, ScRange const& rRange, Size const& rDocSize)
+ : m_bSkipEmpty(bSkipEmpty)
+ , m_bPrintArea(bPrintArea)
+ , m_aRange(rRange)
+ , m_aDocSize(rDocSize)
+ {}
+
+ bool operator==(PrintPageRangesInput const& rInput) const
+ {
+ return
+ m_bSkipEmpty == rInput.m_bSkipEmpty &&
+ m_bPrintArea == rInput.m_bPrintArea &&
+ m_aRange == rInput.m_aRange &&
+ m_aDocSize == rInput.m_aDocSize;
+ }
+
+ PrintPageRangesInput& operator=(PrintPageRangesInput const& rInput)
+ {
+ m_bSkipEmpty = rInput.m_bSkipEmpty;
+ m_bPrintArea = rInput.m_bPrintArea;
+ m_aRange = rInput.m_aRange;
+ m_aDocSize = rInput.m_aDocSize;
+
+ return *this;
+ }
+
+ SCROW getStartRow() const
+ {
+ return m_aRange.aStart.Row();
+ }
+
+ SCROW getEndRow() const
+ {
+ return m_aRange.aEnd.Row();
+ }
+
+ SCCOL getStartColumn() const
+ {
+ return m_aRange.aStart.Col();
+ }
+
+ SCCOL getEndColumn() const
+ {
+ return m_aRange.aEnd.Col();
+ }
+
+ SCTAB getPrintTab() const
+ {
+ return m_aRange.aStart.Tab();
+ }
+};
+
+class PrintPageRanges
+{
+public:
+ PrintPageRanges();
+
+ // use shared_ptr to avoid copying this (potentially large) data back and forth
+ std::shared_ptr<std::vector<SCCOL>> m_xPageEndX;
+ std::shared_ptr<std::vector<SCROW>> m_xPageEndY;
+ std::shared_ptr<std::map<size_t, ScPageRowEntry>> m_xPageRows;
+
+ size_t m_nPagesX;
+ size_t m_nPagesY;
+ size_t m_nTotalY;
+
+ PrintPageRangesInput m_aInput;
+
+ void calculate(ScDocument& rDoc, PrintPageRangesInput const& rInput);
+};
+
+} // end sc namespace
+
+struct ScPrintState // Save Variables from ScPrintFunc
+{
+ SCTAB nPrintTab;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ bool bPrintAreaValid; // the 4 variables above are set
+ sal_uInt16 nZoom;
+ size_t nPagesX;
+ size_t nPagesY;
+ tools::Long nTabPages;
+ tools::Long nTotalPages;
+ tools::Long nPageStart;
+ tools::Long nDocPages;
+
+ // Additional state of page ranges
+ bool bSavedStateRanges;
+ sc::PrintPageRangesInput aPrintPageRangesInput;
+ size_t nTotalY;
+ // use shared_ptr to avoid copying this (potentially large) map back and forth
+ std::shared_ptr<std::vector<SCCOL>> xPageEndX;
+ std::shared_ptr<std::vector<SCROW>> xPageEndY;
+ std::shared_ptr<std::map<size_t, ScPageRowEntry>> xPageRows;
+
+ ScPrintState()
+ : nPrintTab(0)
+ , nStartCol(0)
+ , nStartRow(0)
+ , nEndCol(0)
+ , nEndRow(0)
+ , bPrintAreaValid(false)
+ , nZoom(0)
+ , nPagesX(0)
+ , nPagesY(0)
+ , nTabPages(0)
+ , nTotalPages(0)
+ , nPageStart(0)
+ , nDocPages(0)
+ , bSavedStateRanges(false)
+ , nTotalY(0)
+ {}
+};
+
+class ScPrintFunc
+{
+private:
+ ScDocShell* pDocShell;
+ ScDocument& rDoc;
+ VclPtr<SfxPrinter> pPrinter;
+ VclPtr<OutputDevice> pDev;
+ FmFormView* pDrawView;
+
+ MapMode aOldPrinterMode; // MapMode before the call
+
+ Point aSrcOffset; // Paper-1/100 mm
+ Point aOffset; // scaled by a factor of page size
+ sal_uInt16 nManualZoom; // Zoom in Preview (percent)
+ bool bClearWin; // Clear output before
+ bool bUseStyleColor;
+ bool bIsRender;
+
+ SCTAB nPrintTab;
+ tools::Long nPageStart; // Offset for the first page
+ tools::Long nDocPages; // Number of Pages in Document
+
+ const ScRange* pUserArea; // Selection, if set in dialog
+
+ const SfxItemSet* pParamSet; // Selected template
+ bool bFromPrintState; // created from State-struct
+
+ // Parameter from template:
+ sal_uInt16 nLeftMargin;
+ sal_uInt16 nTopMargin;
+ sal_uInt16 nRightMargin;
+ sal_uInt16 nBottomMargin;
+ bool bCenterHor;
+ bool bCenterVer;
+ bool bLandscape;
+ bool bSourceRangeValid;
+
+ SvxPageUsage nPageUsage;
+ Size aPageSize; // Printer Twips
+ const SvxBoxItem* pBorderItem;
+ const SvxBrushItem* pBackgroundItem;
+ const SvxShadowItem* pShadowItem;
+
+ ScRange aLastSourceRange;
+ ScPrintHFParam aHdr;
+ ScPrintHFParam aFtr;
+ ScPageTableParam aTableParam;
+ ScPageAreaParam aAreaParam;
+
+ // Calculated values:
+ sal_uInt16 nZoom;
+ bool bPrintCurrentTable;
+ bool bMultiArea;
+ bool mbHasPrintRange;
+ tools::Long nTabPages;
+ tools::Long nTotalPages;
+
+ tools::Rectangle aPageRect; // Document Twips
+
+ MapMode aLogicMode; // Set in DoPrint
+ MapMode aOffsetMode;
+ MapMode aTwipMode;
+ double nScaleX;
+ double nScaleY;
+
+ SCCOL nRepeatStartCol;
+ SCCOL nRepeatEndCol;
+ SCROW nRepeatStartRow;
+ SCROW nRepeatEndRow;
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ bool bPrintAreaValid; // the 4 variables above are set
+
+ sc::PrintPageRanges m_aRanges;
+
+ std::unique_ptr<ScHeaderEditEngine> pEditEngine;
+ std::unique_ptr<SfxItemSet> pEditDefaults;
+
+ ScHeaderFieldData aFieldData;
+
+ std::vector<ScAddress> aNotePosList; // The order of notes
+
+ ScPageBreakData* pPageData; // for recording the breaks etc.
+
+public:
+ ScPrintFunc( ScDocShell* pShell, SfxPrinter* pNewPrinter, SCTAB nTab,
+ tools::Long nPage = 0, tools::Long nDocP = 0,
+ const ScRange* pArea = nullptr,
+ const ScPrintOptions* pOptions = nullptr,
+ ScPageBreakData* pData = nullptr );
+
+ ScPrintFunc( ScDocShell* pShell, SfxPrinter* pNewPrinter,
+ const ScPrintState& rState, const ScPrintOptions* pOptions );
+
+ // ctors for device other than printer - for preview and pdf:
+
+ ScPrintFunc( OutputDevice* pOutDev, ScDocShell* pShell, SCTAB nTab,
+ tools::Long nPage = 0, tools::Long nDocP = 0,
+ const ScRange* pArea = nullptr,
+ const ScPrintOptions* pOptions = nullptr );
+
+ ScPrintFunc( OutputDevice* pOutDev, ScDocShell* pShell,
+ const ScPrintState& rState,
+ const ScPrintOptions* pOptions );
+
+ ~ScPrintFunc();
+
+ static void DrawToDev( ScDocument& rDoc, OutputDevice* pDev, double nPrintFactor,
+ const tools::Rectangle& rBound, ScViewData* pViewData, bool bMetaFile );
+
+ void SetDrawView( FmFormView* pNew );
+
+ void SetOffset( const Point& rOfs );
+ void SetManualZoom( sal_uInt16 nNewZoom );
+ void SetDateTime( const DateTime& );
+
+ void SetClearFlag( bool bFlag );
+ void SetUseStyleColor( bool bFlag );
+ void SetRenderFlag( bool bFlag );
+
+ void SetExclusivelyDrawOleAndDrawObjects();//for printing selected objects without surrounding cell contents
+
+ bool UpdatePages();
+
+ void ApplyPrintSettings(); // Already called from DoPrint()
+ tools::Long DoPrint( const MultiSelection& rPageRanges,
+ tools::Long nStartPage, tools::Long nDisplayStart, bool bDoPrint,
+ ScPreviewLocationData* pLocationData );
+
+ // Query values - immediately
+
+ const Size& GetPageSize() const { return aPageSize; }
+ Size GetDataSize() const;
+ void GetScaleData( Size& rPhysSize, tools::Long& rDocHdr, tools::Long& rDocFtr );
+ tools::Long GetFirstPageNo() const { return aTableParam.nFirstPageNo; }
+
+ tools::Long GetTotalPages() const { return nTotalPages; }
+ sal_uInt16 GetZoom() const { return nZoom; }
+
+ void ResetBreaks( SCTAB nTab );
+
+ void GetPrintState(ScPrintState& rState, bool bSavePageRanges = false);
+ bool GetLastSourceRange( ScRange& rRange ) const;
+ sal_uInt16 GetLeftMargin() const{return nLeftMargin;}
+ sal_uInt16 GetRightMargin() const{return nRightMargin;}
+ sal_uInt16 GetTopMargin() const{return nTopMargin;}
+ sal_uInt16 GetBottomMargin() const{return nBottomMargin;}
+ const ScPrintHFParam& GetHeader() const {return aHdr;}
+ const ScPrintHFParam& GetFooter() const {return aFtr;}
+
+ bool HasPrintRange() const { return mbHasPrintRange;}
+
+private:
+ void Construct( const ScPrintOptions* pOptions );
+ void InitParam( const ScPrintOptions* pOptions );
+ void CalcZoom( sal_uInt16 nRangeNo );
+ void CalcPages();
+ tools::Long CountPages();
+ tools::Long CountNotePages();
+
+ bool AdjustPrintArea( bool bNew );
+
+ Size GetDocPageSize();
+
+ tools::Long TextHeight( const EditTextObject* pObject );
+ void MakeEditEngine();
+ void UpdateHFHeight( ScPrintHFParam& rParam );
+
+ void InitModes();
+
+ bool IsLeft( tools::Long nPageNo );
+ bool IsMirror( tools::Long nPageNo );
+ void MakeTableString(); // sets aTableStr
+
+ void PrintPage( tools::Long nPageNo,
+ SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ bool bDoPrint, ScPreviewLocationData* pLocationData );
+ void PrintArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ tools::Long nScrX, tools::Long nScrY,
+ bool bShLeft, bool bShTop, bool bShRight, bool bShBottom );
+ void LocateArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ tools::Long nScrX, tools::Long nScrY, bool bRepCol, bool bRepRow,
+ ScPreviewLocationData& rLocationData );
+ void PrintColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY );
+ void PrintRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY );
+ void LocateColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY,
+ bool bRepCol, ScPreviewLocationData& rLocationData );
+ void LocateRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY,
+ bool bRepRow, ScPreviewLocationData& rLocationData );
+ void PrintHF( tools::Long nPageNo, bool bHeader, tools::Long nStartY,
+ bool bDoPrint, ScPreviewLocationData* pLocationData );
+
+ tools::Long PrintNotes( tools::Long nPageNo, tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData );
+ tools::Long DoNotes( tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData );
+
+ void DrawBorder( tools::Long nScrX, tools::Long nScrY, tools::Long nScrW, tools::Long nScrH,
+ const SvxBoxItem* pBorderData,
+ const SvxBrushItem* pBackground,
+ const SvxShadowItem* pShadow );
+
+ void FillPageData();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/protectiondlg.hxx b/sc/source/ui/inc/protectiondlg.hxx
new file mode 100644
index 0000000000..e59fb3accf
--- /dev/null
+++ b/sc/source/ui/inc/protectiondlg.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScTableProtection;
+
+class ScTableProtectionDlg : public weld::GenericDialogController
+{
+public:
+ ScTableProtectionDlg() = delete;
+ explicit ScTableProtectionDlg(weld::Window* pParent);
+ virtual ~ScTableProtectionDlg() override;
+
+ void SetDialogData(const ScTableProtection& rData);
+
+ void WriteData(ScTableProtection& rData) const;
+
+private:
+ void Init();
+
+ void EnableOptionalWidgets(bool bEnable);
+
+ OUString m_aSelectLockedCells;
+ OUString m_aSelectUnlockedCells;
+ OUString m_aInsertColumns;
+ OUString m_aInsertRows;
+ OUString m_aDeleteColumns;
+ OUString m_aDeleteRows;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnProtect;
+ std::unique_ptr<weld::Container> m_xPasswords;
+ std::unique_ptr<weld::Container> m_xOptions;
+ std::unique_ptr<weld::Entry> m_xPassword1Edit;
+ std::unique_ptr<weld::Entry> m_xPassword2Edit;
+ std::unique_ptr<weld::LevelBar> m_xPasswordStrengthBar;
+ std::unique_ptr<weld::TreeView> m_xOptionsListBox;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Label> m_xProtected;
+ std::unique_ptr<weld::Label> m_xUnprotected;
+ std::unique_ptr<weld::Label> m_xInsertColumns;
+ std::unique_ptr<weld::Label> m_xInsertRows;
+ std::unique_ptr<weld::Label> m_xDeleteColumns;
+ std::unique_ptr<weld::Label> m_xDeleteRows;
+
+ void InsertEntry(const OUString& rTxt);
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(CheckBoxHdl, weld::Toggleable&, void);
+ DECL_LINK(PasswordModifyHdl, weld::Entry&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/pvfundlg.hxx b/sc/source/ui/inc/pvfundlg.hxx
new file mode 100644
index 0000000000..146048cfed
--- /dev/null
+++ b/sc/source/ui/inc/pvfundlg.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+
+#include <vcl/weld.hxx>
+#include <pivot.hxx>
+
+#include <memory>
+#include <unordered_map>
+
+class ScDPObject;
+
+class ScDPFunctionListBox
+{
+public:
+ ScDPFunctionListBox(std::unique_ptr<weld::TreeView> xControl);
+
+ void SetSelection( PivotFunc nFuncMask );
+ PivotFunc GetSelection() const;
+
+ void set_sensitive(bool sensitive) { m_xControl->set_sensitive(sensitive); }
+ void set_selection_mode(SelectionMode eMode) { m_xControl->set_selection_mode(eMode); }
+ void connect_row_activated(const Link<weld::TreeView&, bool>& rLink) { m_xControl->connect_row_activated(rLink); }
+ int get_height_rows(int nRows) const { return m_xControl->get_height_rows(nRows); }
+ void set_size_request(int nWidth, int nHeight) { m_xControl->set_size_request(nWidth, nHeight); }
+
+private:
+ std::unique_ptr<weld::TreeView> m_xControl;
+ void FillFunctionNames();
+};
+
+class ScDPFunctionDlg : public weld::GenericDialogController
+{
+ typedef std::unordered_map< OUString, OUString > NameMapType;
+public:
+ explicit ScDPFunctionDlg(weld::Widget* pParent, const ScDPLabelDataVector& rLabelVec,
+ const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData );
+ virtual ~ScDPFunctionDlg() override;
+ PivotFunc GetFuncMask() const;
+ css::sheet::DataPilotFieldReference GetFieldRef() const;
+
+private:
+ void Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData );
+
+ const OUString& GetBaseFieldName(const OUString& rLayoutName) const;
+ const OUString& GetBaseItemName(const OUString& rLayoutName) const;
+
+ /** Searches for a listbox entry, starts search at specified position. */
+ sal_Int32 FindBaseItemPos( std::u16string_view rEntry, sal_Int32 nStartPos ) const;
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(DblClickHdl, weld::TreeView&, bool);
+ DECL_LINK(ButtonClicked, weld::Button&, void);
+
+private:
+ std::unique_ptr<ScDPFunctionListBox> mxLbFunc;
+ std::unique_ptr<weld::Label> mxFtName;
+ std::unique_ptr<weld::ComboBox> mxLbType;
+ std::unique_ptr<weld::Label> mxFtBaseField;
+ std::unique_ptr<weld::ComboBox> mxLbBaseField;
+ std::unique_ptr<weld::Label> mxFtBaseItem;
+ std::unique_ptr<weld::ComboBox> mxLbBaseItem;
+ std::unique_ptr<weld::Button> mxBtnOk;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+
+ NameMapType maBaseFieldNameMap; // cache for base field display -> original name.
+ NameMapType maBaseItemNameMap; // cache for base item display -> original name.
+
+ const ScDPLabelDataVector& mrLabelVec; /// Data of all labels.
+ bool mbEmptyItem; /// true = Empty base item in listbox.
+};
+
+class ScDPSubtotalOptDlg;
+
+class ScDPSubtotalDlg : public weld::GenericDialogController
+{
+public:
+ explicit ScDPSubtotalDlg(weld::Widget* pParent, ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData,
+ const ScDPNameVec& rDataFields, bool bEnableLayout);
+ virtual ~ScDPSubtotalDlg() override;
+ PivotFunc GetFuncMask() const;
+
+ void FillLabelData( ScDPLabelData& rLabelData ) const;
+
+private:
+ void Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData );
+ void CloseSubdialog();
+
+ DECL_LINK( DblClickHdl, weld::TreeView&, bool );
+ DECL_LINK( RadioClickHdl, weld::Toggleable&, void );
+ DECL_LINK( ClickHdl, weld::Button&, void );
+ DECL_LINK( ButtonClicked, weld::Button&, void );
+
+private:
+ ScDPObject& mrDPObj; /// The DataPilot object (for member names).
+ const ScDPNameVec& mrDataFields; /// The list of all data field names.
+
+ ScDPLabelData maLabelData; /// Cache for sub dialog.
+ bool mbEnableLayout; /// true = Enable Layout mode controls.
+
+ std::unique_ptr<weld::RadioButton> mxRbNone;
+ std::unique_ptr<weld::RadioButton> mxRbAuto;
+ std::unique_ptr<weld::RadioButton> mxRbUser;
+ std::unique_ptr<ScDPFunctionListBox> mxLbFunc;
+ std::unique_ptr<weld::Label> mxFtName;
+ std::unique_ptr<weld::CheckButton> mxCbShowAll;
+ std::unique_ptr<weld::Button> mxBtnOk;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+ std::unique_ptr<weld::Button> mxBtnOptions;
+
+ std::shared_ptr<ScDPSubtotalOptDlg> mxOptionsDlg;
+};
+
+class ScDPSubtotalOptDlg : public weld::GenericDialogController
+{
+public:
+ explicit ScDPSubtotalOptDlg(weld::Window* pParent, ScDPObject& rDPObj,
+ const ScDPLabelData& rLabelData, const ScDPNameVec& rDataFields,
+ bool bEnableLayout );
+ virtual ~ScDPSubtotalOptDlg() override;
+ void FillLabelData( ScDPLabelData& rLabelData ) const;
+
+private:
+ void Init( const ScDPNameVec& rDataFields, bool bEnableLayout );
+ void InitHideListBox();
+
+ ScDPName GetFieldName(const OUString& rLayoutName) const;
+
+ /** Searches for a listbox entry, starts search at specified position. */
+ sal_Int32 FindListBoxEntry( const weld::ComboBox& rLBox, std::u16string_view rEntry, sal_Int32 nStartPos ) const;
+
+ DECL_LINK( CheckHdl, weld::Toggleable&, void );
+ DECL_LINK( SelectHdl, weld::ComboBox&, void );
+ DECL_LINK( ButtonClicked, weld::Button&, void );
+
+private:
+ std::unique_ptr<weld::ComboBox> m_xLbSortBy;
+ std::unique_ptr<weld::RadioButton> m_xRbSortAsc;
+ std::unique_ptr<weld::RadioButton> m_xRbSortDesc;
+ std::unique_ptr<weld::RadioButton> m_xRbSortMan;
+ std::unique_ptr<weld::Widget> m_xLayoutFrame;
+ std::unique_ptr<weld::ComboBox> m_xLbLayout;
+ std::unique_ptr<weld::CheckButton> m_xCbLayoutEmpty;
+ std::unique_ptr<weld::CheckButton> m_xCbRepeatItemLabels;
+ std::unique_ptr<weld::CheckButton> m_xCbShow;
+ std::unique_ptr<weld::SpinButton> m_xNfShow;
+ std::unique_ptr<weld::Label> m_xFtShow;
+ std::unique_ptr<weld::Label> m_xFtShowFrom;
+ std::unique_ptr<weld::ComboBox> m_xLbShowFrom;
+ std::unique_ptr<weld::Label> m_xFtShowUsing;
+ std::unique_ptr<weld::ComboBox> m_xLbShowUsing;
+ std::unique_ptr<weld::Widget> m_xHideFrame;
+ std::unique_ptr<weld::TreeView> m_xLbHide;
+ std::unique_ptr<weld::Label> m_xFtHierarchy;
+ std::unique_ptr<weld::ComboBox> m_xLbHierarchy;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ ScDPObject& mrDPObj; /// The DataPilot object (for member names).
+ ScDPLabelData maLabelData; /// Cache for members data.
+
+ typedef std::unordered_map<OUString, ScDPName> NameMapType;
+ NameMapType maDataFieldNameMap; /// Cache for displayed name to field name mapping.
+};
+
+class ScDPShowDetailDlg : public weld::GenericDialogController
+{
+public:
+ explicit ScDPShowDetailDlg(weld::Window* pParent, ScDPObject& rDPObj,
+ css::sheet::DataPilotFieldOrientation nOrient);
+ virtual ~ScDPShowDetailDlg() override;
+
+ virtual short run() override;
+
+ /**
+ * @return String internal name of the selected field. Note that this may
+ * be different from the name displayed in the dialog if the field
+ * has a layout name.
+ */
+ OUString GetDimensionName() const;
+
+private:
+ DECL_LINK(DblClickHdl, weld::TreeView&, bool);
+
+private:
+ typedef std::unordered_map<OUString, tools::Long> DimNameIndexMap;
+ DimNameIndexMap maNameIndexMap;
+ ScDPObject& mrDPObj;
+
+ std::unique_ptr<weld::TreeView> mxLbDims;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/redcom.hxx b/sc/source/ui/inc/redcom.hxx
new file mode 100644
index 0000000000..893b8cf1a3
--- /dev/null
+++ b/sc/source/ui/inc/redcom.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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <tools/link.hxx>
+#include <vcl/vclptr.hxx>
+
+namespace weld { class Window; }
+class ScChangeAction;
+class SfxItemSet;
+class ScDocShell;
+class AbstractSvxPostItDialog;
+
+class ScRedComDialog final
+{
+ ScChangeAction *pChangeAction;
+ ScDocShell *pDocShell;
+ OUString aComment;
+ VclPtr<AbstractSvxPostItDialog> pDlg;
+
+ DECL_LINK( PrevHdl, AbstractSvxPostItDialog&, void );
+ DECL_LINK( NextHdl, AbstractSvxPostItDialog&, void );
+
+ void ReInit(ScChangeAction *);
+ void SelectCell();
+
+ ScChangeAction *FindPrev(ScChangeAction *pAction);
+ ScChangeAction *FindNext(ScChangeAction *pAction);
+
+public:
+
+ ScRedComDialog( weld::Window* pParent, const SfxItemSet& rCoreSet,
+ ScDocShell *pShell, ScChangeAction *pAction, bool bPrevNext);
+ ~ScRedComDialog();
+
+ void Execute();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/reffact.hxx b/sc/source/ui/inc/reffact.hxx
new file mode 100644
index 0000000000..4fbe965c15
--- /dev/null
+++ b/sc/source/ui/inc/reffact.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 .
+ */
+
+#pragma once
+
+#include <sfx2/childwin.hxx>
+#include "ChildWindowWrapper.hxx"
+
+#include <sc.hrc>
+
+#define DECL_WRAPPER_WITHID(Class) \
+ class Class : public SfxChildWindow \
+ { \
+ public: \
+ Class( vcl::Window*, sal_uInt16, SfxBindings*, const SfxChildWinInfo* ); \
+ SFX_DECL_CHILDWINDOW_WITHID(Class); \
+ };
+
+DECL_WRAPPER_WITHID(ScNameDlgWrapper)
+DECL_WRAPPER_WITHID(ScNameDefDlgWrapper)
+DECL_WRAPPER_WITHID(ScSolverDlgWrapper)
+DECL_WRAPPER_WITHID(ScOptSolverDlgWrapper)
+DECL_WRAPPER_WITHID(ScXMLSourceDlgWrapper)
+DECL_WRAPPER_WITHID(ScPivotLayoutWrapper)
+DECL_WRAPPER_WITHID(ScTabOpDlgWrapper)
+DECL_WRAPPER_WITHID(ScFilterDlgWrapper)
+DECL_WRAPPER_WITHID(ScSpecialFilterDlgWrapper)
+DECL_WRAPPER_WITHID(ScDbNameDlgWrapper)
+DECL_WRAPPER_WITHID(ScConsolidateDlgWrapper)
+DECL_WRAPPER_WITHID(ScPrintAreasDlgWrapper)
+DECL_WRAPPER_WITHID(ScColRowNameRangesDlgWrapper)
+DECL_WRAPPER_WITHID(ScFormulaDlgWrapper)
+DECL_WRAPPER_WITHID(ScHighlightChgDlgWrapper)
+DECL_WRAPPER_WITHID(ScCondFormatDlgWrapper)
+
+class ScDescriptiveStatisticsDialogWrapper :
+ public ChildControllerWrapper<SID_DESCRIPTIVE_STATISTICS_DIALOG>
+{
+private:
+ ScDescriptiveStatisticsDialogWrapper() = delete;
+};
+
+class ScSamplingDialogWrapper :
+ public ChildControllerWrapper<SID_SAMPLING_DIALOG>
+{
+private:
+ ScSamplingDialogWrapper() = delete;
+};
+
+class ScRandomNumberGeneratorDialogWrapper :
+ public ChildControllerWrapper<SID_RANDOM_NUMBER_GENERATOR_DIALOG>
+{
+private:
+ ScRandomNumberGeneratorDialogWrapper() = delete;
+};
+
+class ScAnalysisOfVarianceDialogWrapper :
+ public ChildControllerWrapper<SID_ANALYSIS_OF_VARIANCE_DIALOG>
+{
+private:
+ ScAnalysisOfVarianceDialogWrapper() = delete;
+};
+
+class ScCorrelationDialogWrapper :
+ public ChildControllerWrapper<SID_CORRELATION_DIALOG>
+{
+private:
+ ScCorrelationDialogWrapper() = delete;
+};
+
+class ScCovarianceDialogWrapper :
+ public ChildControllerWrapper<SID_COVARIANCE_DIALOG>
+{
+private:
+ ScCovarianceDialogWrapper() = delete;
+};
+
+class ScExponentialSmoothingDialogWrapper :
+ public ChildControllerWrapper<SID_EXPONENTIAL_SMOOTHING_DIALOG>
+{
+private:
+ ScExponentialSmoothingDialogWrapper() = delete;
+};
+
+class ScMovingAverageDialogWrapper :
+ public ChildControllerWrapper<SID_MOVING_AVERAGE_DIALOG>
+{
+private:
+ ScMovingAverageDialogWrapper() = delete;
+};
+
+class ScRegressionDialogWrapper :
+ public ChildControllerWrapper<SID_REGRESSION_DIALOG>
+{
+private:
+ ScRegressionDialogWrapper() = delete;
+};
+
+class ScTTestDialogWrapper :
+ public ChildControllerWrapper<SID_TTEST_DIALOG>
+{
+private:
+ ScTTestDialogWrapper() = delete;
+};
+
+class ScFTestDialogWrapper :
+ public ChildControllerWrapper<SID_FTEST_DIALOG>
+{
+private:
+ ScFTestDialogWrapper() = delete;
+};
+
+class ScZTestDialogWrapper :
+ public ChildControllerWrapper<SID_ZTEST_DIALOG>
+{
+private:
+ ScZTestDialogWrapper() = delete;
+};
+
+class ScChiSquareTestDialogWrapper :
+ public ChildControllerWrapper<SID_CHI_SQUARE_TEST_DIALOG>
+{
+private:
+ ScChiSquareTestDialogWrapper() = delete;
+};
+
+class ScFourierAnalysisDialogWrapper :
+ public ChildControllerWrapper<SID_FOURIER_ANALYSIS_DIALOG>
+{
+private:
+ ScFourierAnalysisDialogWrapper() = delete;
+};
+
+namespace sc
+{
+/** Wrapper for the sparkline properties dialog */
+class SparklineDialogWrapper :
+ public ChildControllerWrapper<SID_SPARKLINE_DIALOG>
+{
+private:
+ SparklineDialogWrapper() = delete;
+};
+
+/** Wrapper for the sparkline data range dialog */
+class SparklineDataRangeDialogWrapper :
+ public ChildControllerWrapper<SID_SPARKLINE_DATA_RANGE_DIALOG>
+{
+private:
+ SparklineDataRangeDialogWrapper() = delete;
+};
+
+/** Wrapper for the easy conditional format dialog */
+class ConditionalFormatEasyDialogWrapper :
+ public ChildControllerWrapper<SID_EASY_CONDITIONAL_FORMAT_DIALOG>
+{
+private:
+ ConditionalFormatEasyDialogWrapper() = delete;
+};
+}
+
+class ScAcceptChgDlgWrapper : public SfxChildWindow
+{
+public:
+ ScAcceptChgDlgWrapper( vcl::Window*,
+ sal_uInt16,
+ SfxBindings*,
+ SfxChildWinInfo* );
+
+ SFX_DECL_CHILDWINDOW_WITHID(Class);
+
+ void ReInitDlg();
+};
+
+class ScSimpleRefDlgWrapper: public SfxChildWindow
+{
+public:
+ ScSimpleRefDlgWrapper(vcl::Window*,
+ sal_uInt16,
+ SfxBindings*,
+ SfxChildWinInfo*);
+
+ SFX_DECL_CHILDWINDOW_WITHID(Class);
+
+ void SetRefString(const OUString& rStr);
+ void SetCloseHdl( const Link<const OUString*,void>& rLink );
+ void SetUnoLinks( const Link<const OUString&,void>& rDone, const Link<const OUString&,void>& rAbort,
+ const Link<const OUString&,void>& rChange );
+ void SetFlags( bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection );
+ static void SetAutoReOpen(bool bFlag);
+
+ void StartRefInput();
+};
+
+class ScValidityRefChildWin : public SfxChildWindow
+{
+ bool m_bVisibleLock:1;
+ bool m_bFreeWindowLock:1;
+public:
+ ScValidityRefChildWin( vcl::Window*, sal_uInt16, const SfxBindings*, SfxChildWinInfo* );
+ SFX_DECL_CHILDWINDOW_WITHID(ScValidityRefChildWin);
+ virtual ~ScValidityRefChildWin() override;
+ bool LockVisible( bool bLock ){ bool bVis = m_bVisibleLock; m_bVisibleLock = bLock; return bVis; }
+ bool LockFreeWindow( bool bLock ){ bool bFreeWindow = m_bFreeWindowLock; m_bFreeWindowLock = bLock; return bFreeWindow; }
+ void Hide() override { if( !m_bVisibleLock) SfxChildWindow::Hide(); }
+ void Show( ShowFlags nFlags ) override { if( !m_bVisibleLock ) SfxChildWindow::Show( nFlags ); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/refundo.hxx b/sc/source/ui/inc/refundo.hxx
new file mode 100644
index 0000000000..bf0eb62e91
--- /dev/null
+++ b/sc/source/ui/inc/refundo.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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <scdllapi.h>
+
+class ScDocument;
+class ScDBCollection;
+class ScRangeName;
+class ScPrintRangeSaver;
+class ScDPCollection;
+class ScDetOpList;
+class ScChartListenerCollection;
+class ScAreaLinkSaveCollection;
+class ScUnoRefList;
+
+class SC_DLLPUBLIC ScRefUndoData
+{
+private:
+ std::unique_ptr<ScDBCollection> pDBCollection;
+ std::unique_ptr<ScRangeName> pRangeName;
+ std::unique_ptr<ScPrintRangeSaver> pPrintRanges;
+ std::unique_ptr<ScDPCollection> pDPCollection;
+ std::unique_ptr<ScDetOpList> pDetOpList;
+ std::unique_ptr<ScChartListenerCollection> pChartListenerCollection;
+ std::unique_ptr<ScAreaLinkSaveCollection> pAreaLinks;
+ std::unique_ptr<ScUnoRefList> pUnoRefs;
+
+public:
+ ScRefUndoData( const ScDocument* pDoc );
+ ~ScRefUndoData();
+
+ void DeleteUnchanged( const ScDocument* pDoc );
+ void DoUndo( ScDocument* pDoc, bool bUndoRefFirst );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/retypepassdlg.hxx b/sc/source/ui/inc/retypepassdlg.hxx
new file mode 100644
index 0000000000..cf4598c3f2
--- /dev/null
+++ b/sc/source/ui/inc/retypepassdlg.hxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <tabprotection.hxx>
+#include <memory>
+
+class ScDocument;
+
+struct PassFragment
+{
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Container> m_xSheetsBox;
+ std::unique_ptr<weld::Label> m_xName;
+ std::unique_ptr<weld::Label> m_xStatus;
+ std::unique_ptr<weld::Button> m_xButton;
+
+ PassFragment(weld::Widget* pParent);
+};
+
+class ScRetypePassDlg : public weld::GenericDialogController
+{
+public:
+ explicit ScRetypePassDlg(weld::Window* pParent);
+ virtual ~ScRetypePassDlg() override;
+
+ virtual short run() override;
+
+ void SetDataFromDocument(const ScDocument& rDoc);
+ void SetDesiredHash(ScPasswordHash eHash);
+
+ /** Write the new set of password data to the document instance to
+ overwrite the current ones. */
+ void WriteNewDataToDocument(ScDocument& rDoc) const;
+
+private:
+ void Init();
+ void PopulateDialog();
+ void SetDocData();
+ void SetTableData(size_t nRowPos, SCTAB nTab);
+
+ /** Check the status of all hash values to see if it's okay to enable
+ the OK button. */
+ void CheckHashStatus();
+
+ void DeleteSheets();
+
+private:
+ OUString maTextNotProtected;
+ OUString maTextNotPassProtected;
+ OUString maTextHashBad;
+ OUString maTextHashGood;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(RetypeBtnHdl, weld::Button&, void);
+
+ struct TableItem
+ {
+ OUString maName;
+ std::shared_ptr<ScTableProtection> mpProtect;
+ };
+ ::std::vector<TableItem> maTableItems;
+
+ std::shared_ptr<ScDocProtection> mpDocItem;
+ ScPasswordHash meDesiredHash;
+
+ std::unique_ptr<weld::Button> mxBtnOk;
+ std::unique_ptr<weld::Label> mxTextDocStatus;
+ std::unique_ptr<weld::Button> mxBtnRetypeDoc;
+ std::unique_ptr<weld::ScrolledWindow> mxScrolledWindow;
+ std::unique_ptr<weld::Container> mxSheetsBox;
+ std::vector<std::unique_ptr<PassFragment>> maSheets;
+};
+
+class ScRetypePassInputDlg : public weld::GenericDialogController
+{
+public:
+ ScRetypePassInputDlg() = delete;
+ explicit ScRetypePassInputDlg(weld::Window* pParent, ScPassHashProtectable* pProtected);
+ virtual ~ScRetypePassInputDlg() override;
+
+ bool IsRemovePassword() const;
+ OUString GetNewPassword() const;
+
+private:
+ void Init();
+ void CheckPasswordInput();
+
+private:
+ ScPassHashProtectable* m_pProtected;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnRetypePassword;
+
+ std::unique_ptr<weld::Widget> m_xPasswordGrid;
+ std::unique_ptr<weld::Entry> m_xPassword1Edit;
+ std::unique_ptr<weld::Entry> m_xPassword2Edit;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnMatchOldPass;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnRemovePassword;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(RadioBtnHdl, weld::Toggleable&, void);
+ DECL_LINK(CheckBoxHdl, weld::Toggleable&, void);
+ DECL_LINK(PasswordModifyHdl, weld::Entry&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/rfindlst.hxx b/sc/source/ui/inc/rfindlst.hxx
new file mode 100644
index 0000000000..216264cc29
--- /dev/null
+++ b/sc/source/ui/inc/rfindlst.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/color.hxx>
+#include <address.hxx>
+#include <tools/solar.h>
+#include <editeng/editdata.hxx>
+
+#include <vector>
+
+struct ScRangeFindData
+{
+ ScRange aRef;
+ ESelection maSel;
+ ScRefFlags nFlags;
+ Color nColor;
+
+ ScRangeFindData( const ScRange& rR, ScRefFlags nF, const ESelection& rSel ) :
+ aRef(rR), maSel(rSel), nFlags(nF) {}
+};
+
+class ScRangeFindList
+{
+ std::vector<ScRangeFindData> maEntries;
+ OUString aDocName;
+ bool bHidden;
+ sal_uInt16 nIndexColor;
+
+public:
+ ScRangeFindList(OUString aName);
+
+ size_t Count() const { return maEntries.size(); }
+ Color Insert( const ScRangeFindData &rNew );
+
+ ScRangeFindData& GetObject( size_t nIndex ) { return maEntries[nIndex]; }
+
+ void SetHidden( bool bSet ) { bHidden = bSet; }
+
+ const OUString& GetDocName() const { return aDocName; }
+ bool IsHidden() const { return bHidden; }
+
+ static Color GetColorName(const size_t nIndex);
+ Color FindColor(const ScRange& rRef, const size_t nIndex);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/scendlg.hxx b/sc/source/ui/inc/scendlg.hxx
new file mode 100644
index 0000000000..07ee1f3ef1
--- /dev/null
+++ b/sc/source/ui/inc/scendlg.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+enum class ScScenarioFlags;
+
+class ColorListBox;
+
+class ScNewScenarioDlg : public weld::GenericDialogController
+{
+public:
+ ScNewScenarioDlg(weld::Window* pParent, const OUString& rName, bool bEdit, bool bSheetProtected);
+ virtual ~ScNewScenarioDlg() override;
+ void SetScenarioData( const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags );
+
+ void GetScenarioData(OUString& rName, OUString& rComment,
+ Color& rColor, ScScenarioFlags &rFlags) const;
+
+private:
+ OUString aDefScenarioName;
+ bool bIsEdit;
+ std::unique_ptr<weld::Entry> m_xEdName;
+ std::unique_ptr<weld::TextView> m_xEdComment;
+ std::unique_ptr<weld::CheckButton> m_xCbShowFrame;
+ std::unique_ptr<ColorListBox> m_xLbColor;
+ std::unique_ptr<weld::CheckButton> m_xCbTwoWay;
+ std::unique_ptr<weld::CheckButton> m_xCbCopyAll;
+ std::unique_ptr<weld::CheckButton> m_xCbProtect;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Label> m_xAltTitle;
+ std::unique_ptr<weld::Label> m_xCreatedFt;
+ std::unique_ptr<weld::Label> m_xOnFt;
+
+ DECL_LINK(OkHdl, weld::Button&, void);
+ DECL_LINK(EnableHdl, weld::Toggleable&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/scui_def.hxx b/sc/source/ui/inc/scui_def.hxx
new file mode 100644
index 0000000000..af4252ba4a
--- /dev/null
+++ b/sc/source/ui/inc/scui_def.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 .
+ */
+
+#pragma once
+
+#include <o3tl/typed_flags_set.hxx>
+
+#define SCRET_COLS 0x42
+#define SCRET_ROWS 0x43
+
+#define FDS_OPT_NONE 0 // from filldlg.hxx
+#define FDS_OPT_HORZ 1 // from filldlg.hxx
+#define FDS_OPT_VERT 2 // from filldlg.hxx
+
+enum class InsertContentsFlags {
+ NONE = 0x00,
+ NoEmpty = 0x01, //from inscodlg.hxx
+ Trans = 0x02, //from inscodlg.hxx
+ Link = 0x04 //from inscodlg.hxx
+};
+namespace o3tl {
+ template<> struct typed_flags<InsertContentsFlags> : is_typed_flags<InsertContentsFlags, 0x07> {};
+}
+
+enum class CellShiftDisabledFlags {
+ NONE = 0x00,
+ Down = 0x01, //from inscodlg.hxx
+ Right = 0x02 //from inscodlg.hxx
+};
+namespace o3tl {
+ template<> struct typed_flags<CellShiftDisabledFlags> : is_typed_flags<CellShiftDisabledFlags, 0x3> {};
+}
+
+enum class CreateNameFlags {
+ NONE = 0,
+ Top = 1, //from namecrea.hxx
+ Left = 2, //from namecrea.hxx
+ Bottom = 4, //from namecrea.hxx
+ Right = 8, //from namecrea.hxx
+};
+namespace o3tl {
+ template<> struct typed_flags<CreateNameFlags> : is_typed_flags<CreateNameFlags, 0xf> {};
+}
+
+#define BTN_PASTE_NAME 100 // from namepast.hxx
+#define BTN_PASTE_LIST 101 // from namepast.hxx
+#define BTN_PASTE_CLOSE 102 // from namepast.hxx
+
+#define BTN_EXTEND_RANGE 150
+#define BTN_CURRENT_SELECTION 151
+#define SCRET_REMOVE 0x42 //from subtdlg.hxx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/scuiasciiopt.hxx b/sc/source/ui/inc/scuiasciiopt.hxx
new file mode 100644
index 0000000000..309192477e
--- /dev/null
+++ b/sc/source/ui/inc/scuiasciiopt.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 .
+ */
+
+#pragma once
+
+#include <svx/langbox.hxx>
+#include <tools/solar.h>
+#include <tools/stream.hxx>
+#include <vcl/weld.hxx>
+
+#include "asciiopt.hxx"
+
+class ScCsvTableBox;
+class SvxTextEncodingBox;
+
+class ScImportAsciiDlg : public weld::GenericDialogController
+{
+ SvStream* mpDatStream;
+ sal_uLong mnStreamPos;
+ std::unique_ptr<sal_uLong[]> mpRowPosArray;
+ sal_uLong mnRowPosCount;
+
+ OUString maPreviewLine[ CSV_PREVIEW_LINES ];
+
+ OUString maFieldSeparators; // selected field separators
+ sal_Unicode mcTextSep;
+
+ rtl_TextEncoding meCharSet; /// Selected char set.
+ bool mbCharSetSystem; /// Is System char set selected?
+ ScImportAsciiCall meCall; /// How the dialog is called (see asciiopt.hxx)
+ bool mbDetectSep; /// Whether to detect a possible separator.
+
+ std::unique_ptr<weld::Label> mxFtCharSet;
+ std::unique_ptr<SvxTextEncodingBox> mxLbCharSet;
+ std::unique_ptr<weld::Label> mxFtCustomLang;
+ std::unique_ptr<SvxLanguageBox> mxLbCustomLang;
+
+ std::unique_ptr<weld::Label> mxFtRow;
+ std::unique_ptr<weld::SpinButton> mxNfRow;
+
+ std::unique_ptr<weld::RadioButton> mxRbFixed;
+ std::unique_ptr<weld::RadioButton> mxRbSeparated;
+
+ std::unique_ptr<weld::CheckButton> mxCkbTab;
+ std::unique_ptr<weld::CheckButton> mxCkbSemicolon;
+ std::unique_ptr<weld::CheckButton> mxCkbComma;
+ std::unique_ptr<weld::CheckButton> mxCkbRemoveSpace;
+ std::unique_ptr<weld::CheckButton> mxCkbSpace;
+ std::unique_ptr<weld::CheckButton> mxCkbOther;
+ std::unique_ptr<weld::Entry> mxEdOther;
+ std::unique_ptr<weld::CheckButton> mxCkbAsOnce;
+
+ std::unique_ptr<weld::Label> mxFtTextSep;
+ std::unique_ptr<weld::ComboBox> mxCbTextSep;
+
+ std::unique_ptr<weld::CheckButton> mxCkbQuotedAsText;
+ std::unique_ptr<weld::CheckButton> mxCkbDetectNumber;
+ std::unique_ptr<weld::CheckButton> mxCkbDetectScientificNumber;
+ std::unique_ptr<weld::CheckButton> mxCkbEvaluateFormulas;
+ std::unique_ptr<weld::CheckButton> mxCkbSkipEmptyCells;
+
+ std::unique_ptr<weld::ComboBox> mxLbType;
+ std::unique_ptr<weld::Label> mxAltTitle;
+
+ std::unique_ptr<ScCsvTableBox> mxTableBox;
+
+public:
+ ScImportAsciiDlg(
+ weld::Window* pParent, std::u16string_view aDatName,
+ SvStream* pInStream, ScImportAsciiCall eCall );
+ virtual ~ScImportAsciiDlg() override;
+
+ void GetOptions( ScAsciiOptions& rOpt );
+ void SaveParameters();
+
+private:
+ /** Sets the selected char set data to meCharSet and mbCharSetSystem. */
+ void SetSelectedCharSet();
+ /** Set separators in ui from maFieldSeparators or an optionally defined
+ separator. */
+ void SetSeparators( sal_Unicode cSep );
+ /** Returns all separator characters in a string. */
+ OUString GetSeparators() const;
+
+ /** Enables or disables all separator checkboxes and edit fields. */
+ void SetupSeparatorCtrls();
+
+ bool GetLine( sal_uLong nLine, OUString &rText, sal_Unicode& rcDetectSep );
+ void UpdateVertical();
+ inline bool Seek( sal_uLong nPos ); // synced to and from mnStreamPos
+ void RbSepFix();
+
+ DECL_LINK( CharSetHdl, weld::ComboBox&, void );
+ DECL_LINK( FirstRowHdl, weld::SpinButton&, void );
+ DECL_LINK( RbSepFixHdl, weld::Toggleable&, void );
+ DECL_LINK( SeparatorEditHdl, weld::Entry&, void );
+ DECL_LINK( SeparatorClickHdl, weld::Toggleable&, void );
+ DECL_LINK( OtherOptionsClickHdl, weld::Toggleable&, void );
+ DECL_LINK( SeparatorComboBoxHdl, weld::ComboBox&, void );
+ void SeparatorHdl(const weld::Widget*);
+ DECL_LINK( LbColTypeHdl, weld::ComboBox&, void );
+ DECL_LINK( UpdateTextHdl, ScCsvTableBox&, void );
+ DECL_LINK( ColTypeHdl, ScCsvTableBox&, void );
+ DECL_STATIC_LINK(ScImportAsciiDlg, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*);
+
+};
+
+inline bool ScImportAsciiDlg::Seek(sal_uLong nPos)
+{
+ bool bSuccess = true;
+ if (nPos != mnStreamPos && mpDatStream)
+ {
+ if (mpDatStream->Seek( nPos ) != nPos)
+ bSuccess = false;
+ else
+ mnStreamPos = nPos;
+ }
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/scuiautofmt.hxx b/sc/source/ui/inc/scuiautofmt.hxx
new file mode 100644
index 0000000000..a89d508a20
--- /dev/null
+++ b/sc/source/ui/inc/scuiautofmt.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/customweld.hxx>
+#include <autoform.hxx>
+#include "autofmt.hxx"
+
+class ScAutoFormatDlg : public weld::GenericDialogController
+{
+public:
+ ScAutoFormatDlg(weld::Window* pParent,
+ ScAutoFormat* pAutoFormat,
+ const ScAutoFormatData* pSelFormatData,
+ const ScViewData& rViewData);
+ virtual ~ScAutoFormatDlg() override;
+
+ sal_uInt16 GetIndex() const { return nIndex; }
+ OUString GetCurrFormatName();
+
+private:
+ OUString aStrTitle;
+ OUString aStrLabel;
+ OUString aStrClose;
+ OUString aStrDelMsg;
+ OUString aStrRename;
+
+ ScAutoFormat* pFormat;
+ const ScAutoFormatData* pSelFmtData;
+ sal_uInt16 nIndex;
+ bool bCoreDataChanged;
+ bool bFmtInserted;
+
+ ScAutoFmtPreview m_aWndPreview;
+ std::unique_ptr<weld::TreeView> m_xLbFormat;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+ std::unique_ptr<weld::Button> m_xBtnAdd;
+ std::unique_ptr<weld::Button> m_xBtnRemove;
+ std::unique_ptr<weld::Button> m_xBtnRename;
+ std::unique_ptr<weld::CheckButton> m_xBtnNumFormat;
+ std::unique_ptr<weld::CheckButton> m_xBtnBorder;
+ std::unique_ptr<weld::CheckButton> m_xBtnFont;
+ std::unique_ptr<weld::CheckButton> m_xBtnPattern;
+ std::unique_ptr<weld::CheckButton> m_xBtnAlignment;
+ std::unique_ptr<weld::CheckButton> m_xBtnAdjust;
+ std::unique_ptr<weld::CustomWeld> m_xWndPreview;
+
+ void Init ();
+ void UpdateChecks ();
+
+ DECL_LINK( CheckHdl, weld::Toggleable&, void );
+ DECL_LINK( AddHdl, weld::Button&, void );
+ DECL_LINK( RemoveHdl, weld::Button&, void );
+ DECL_LINK( SelFmtHdl, weld::TreeView&, void );
+ DECL_LINK( CloseHdl, weld::Button&, void );
+ DECL_LINK( DblClkHdl, weld::TreeView&, bool );
+ DECL_LINK( RenameHdl, weld::Button&, void );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/scuiimoptdlg.hxx b/sc/source/ui/inc/scuiimoptdlg.hxx
new file mode 100644
index 0000000000..7728c3e806
--- /dev/null
+++ b/sc/source/ui/inc/scuiimoptdlg.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScDelimiterTable;
+class ScImportOptions;
+class SvxTextEncodingBox;
+class SvxTextEncodingTreeView;
+
+class ScImportOptionsDlg : public weld::GenericDialogController
+{
+public:
+ ScImportOptionsDlg(weld::Window* pParent,
+ bool bAscii,
+ const ScImportOptions* pOptions,
+ const OUString* pStrTitle,
+ bool bMultiByte,
+ bool bOnlyDbtoolsEncodings,
+ bool bImport);
+
+ virtual ~ScImportOptionsDlg() override;
+
+ void GetImportOptions( ScImportOptions& rOptions ) const;
+ void SaveImportOptions() const;
+
+private:
+ std::unique_ptr<ScDelimiterTable> pFieldSepTab;
+ std::unique_ptr<ScDelimiterTable> pTextSepTab;
+
+ bool m_bIsAsciiImport;
+
+ std::unique_ptr<weld::Frame> m_xFieldFrame;
+ std::unique_ptr<weld::Label> m_xFtCharset;
+ std::unique_ptr<weld::Widget> m_xEncGrid;
+ std::unique_ptr<weld::Label> m_xFtFieldSep;
+ std::unique_ptr<weld::ComboBox> m_xEdFieldSep;
+ std::unique_ptr<weld::Label> m_xFtTextSep;
+ std::unique_ptr<weld::ComboBox> m_xEdTextSep;
+ std::unique_ptr<weld::CheckButton> m_xCbShown;
+ std::unique_ptr<weld::CheckButton> m_xCbFormulas;
+ std::unique_ptr<weld::CheckButton> m_xCbQuoteAll;
+ std::unique_ptr<weld::CheckButton> m_xCbFixed;
+ std::unique_ptr<SvxTextEncodingBox> m_xLbCharset;
+ std::unique_ptr<SvxTextEncodingTreeView> m_xTvCharset;
+
+private:
+ sal_uInt16 GetCodeFromCombo( const weld::ComboBox& rEd ) const;
+ void FillFromTextEncodingTable(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags = 0);
+ void FillFromDbTextEncodingMap(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags = 0);
+
+ DECL_LINK(FixedWidthHdl, weld::Toggleable&, void);
+ DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/scuitphfedit.hxx b/sc/source/ui/inc/scuitphfedit.hxx
new file mode 100644
index 0000000000..18c4f97f8c
--- /dev/null
+++ b/sc/source/ui/inc/scuitphfedit.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "tphfedit.hxx"
+#include <sfx2/tabdlg.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+enum ScHFEntryId
+{
+ eNoneEntry ,
+ ePageEntry ,
+ ePagesEntry ,
+ eSheetEntry ,
+ eConfidentialEntry ,
+ eFileNamePageEntry ,
+ eExtFileNameEntry ,
+ ePageSheetEntry ,
+ ePageFileNameEntry ,
+ ePageExtFileNameEntry ,
+ eUserNameEntry ,
+ eCreatedByEntry ,
+ eEntryCount
+};
+
+class EditTextObject;
+class EditEngine;
+
+class ScHFEditPage : public SfxTabPage
+{
+public:
+ virtual bool FillItemSet ( SfxItemSet* rCoreSet ) override;
+ virtual void Reset ( const SfxItemSet* rCoreSet ) override;
+
+ void SetNumType(SvxNumType eNumType);
+ void ClearTextAreas();
+
+protected:
+ ScHFEditPage( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rCoreSet,
+ TypedWhichId<ScPageHFItem> nWhich,
+ bool bHeader );
+ virtual ~ScHFEditPage() override;
+
+private:
+ TypedWhichId<ScPageHFItem> nWhich;
+ bool m_bDropDownActive;
+ sal_Int64 m_nTimeToggled;
+
+ std::unique_ptr<weld::Label> m_xFtDefinedHF;
+ std::unique_ptr<weld::ComboBox> m_xLbDefined;
+ std::unique_ptr<weld::Label> m_xFtCustomHF;
+ std::unique_ptr<weld::Button> m_xBtnText;
+ std::unique_ptr<weld::MenuButton> m_xBtnFile;
+ std::unique_ptr<weld::Button> m_xBtnTable;
+ std::unique_ptr<weld::Button> m_xBtnPage;
+ std::unique_ptr<weld::Button> m_xBtnLastPage;
+ std::unique_ptr<weld::Button> m_xBtnDate;
+ std::unique_ptr<weld::Button> m_xBtnTime;
+
+ std::unique_ptr<weld::Label> m_xFtConfidential;
+ std::unique_ptr<weld::Label> m_xFtPage;
+ std::unique_ptr<weld::Label> m_xFtOfQuestion;
+ std::unique_ptr<weld::Label> m_xFtOf;
+ std::unique_ptr<weld::Label> m_xFtNone;
+ std::unique_ptr<weld::Label> m_xFtCreatedBy;
+ std::unique_ptr<weld::Label> m_xFtCustomized;
+
+ std::unique_ptr<weld::Widget> m_xLeft;
+ std::unique_ptr<weld::Widget> m_xRight;
+
+ std::unique_ptr<ScEditWindow> m_xWndLeft;
+ std::unique_ptr<ScEditWindow> m_xWndCenter;
+ std::unique_ptr<ScEditWindow> m_xWndRight;
+ std::unique_ptr<weld::CustomWeld> m_xWndLeftWnd;
+ std::unique_ptr<weld::CustomWeld> m_xWndCenterWnd;
+ std::unique_ptr<weld::CustomWeld> m_xWndRightWnd;
+
+ ScEditWindow * m_pEditFocus; ///one of m_pWndLeft, m_pWndCenter, m_pWndRight
+
+ DECL_LINK( ObjectSelectHdl, ScEditWindow&, void );
+
+private:
+ void InitPreDefinedList();
+ void ProcessDefinedListSel(ScHFEntryId eSel, bool bTravelling);
+ void InsertToDefinedList();
+ void RemoveFromDefinedList();
+ void SetSelectDefinedList();
+ bool IsPageEntry(EditEngine*pEngine, const EditTextObject* pTextObj);
+ static bool IsDateEntry(const EditTextObject* pTextObj);
+ static bool IsExtFileNameEntry(const EditTextObject* pTextObj);
+ DECL_LINK( ListHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK( ListToggleHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK( ClickHdl, weld::Button&, void );
+ DECL_LINK( MenuHdl, const OUString&, void );
+};
+
+class ScFirstHeaderEditPage : public ScHFEditPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ ScFirstHeaderEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+};
+
+class ScRightHeaderEditPage : public ScHFEditPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ ScRightHeaderEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+};
+
+class ScLeftHeaderEditPage : public ScHFEditPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ ScLeftHeaderEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+};
+
+class ScFirstFooterEditPage : public ScHFEditPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ ScFirstFooterEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+};
+
+class ScRightFooterEditPage : public ScHFEditPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ ScRightFooterEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+};
+
+class ScLeftFooterEditPage : public ScHFEditPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ ScLeftFooterEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/searchresults.hxx b/sc/source/ui/inc/searchresults.hxx
new file mode 100644
index 0000000000..61cfc520b4
--- /dev/null
+++ b/sc/source/ui/inc/searchresults.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/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/childwin.hxx>
+
+class ScDocument;
+class ScRangeList;
+
+namespace sc {
+
+class SearchResultsDlg : public SfxDialogController
+{
+ OUString aSkipped;
+ SfxBindings* mpBindings;
+ ScDocument* mpDoc;
+ bool mbSorted;
+ std::unique_ptr<weld::TreeView> mxList;
+ std::unique_ptr<weld::Label> mxSearchResults;
+ std::unique_ptr<weld::CheckButton> mxShowDialog;
+
+ DECL_LINK(ListSelectHdl, weld::TreeView&, void);
+ DECL_LINK(HeaderBarClick, int, void);
+ DECL_STATIC_LINK(SearchResultsDlg, OnShowToggled, weld::Toggleable&, void);
+public:
+ SearchResultsDlg(SfxBindings* _pBindings, weld::Window* pParent);
+ virtual ~SearchResultsDlg() override;
+
+ virtual void Close() override;
+
+ void FillResults( ScDocument& rDoc, const ScRangeList& rMatchedRanges,
+ bool bCellNotes, bool bEmptyCells,
+ bool bMatchedRangesWereClamped);
+};
+
+class SearchResultsDlgWrapper : public SfxChildWindow
+{
+ std::shared_ptr<SearchResultsDlg> m_xDialog;
+public:
+ SearchResultsDlgWrapper(
+ vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* pBindings, SfxChildWinInfo* pInfo );
+
+ virtual ~SearchResultsDlgWrapper() override;
+
+ SFX_DECL_CHILDWINDOW_WITHID(SearchResultsDlgWrapper);
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/select.hxx b/sc/source/ui/inc/select.hxx
new file mode 100644
index 0000000000..299463afdd
--- /dev/null
+++ b/sc/source/ui/inc/select.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/seleng.hxx>
+
+#include "viewdata.hxx"
+
+class ScTabView;
+
+class ScViewSelectionEngine : public SelectionEngine
+{
+private:
+ ScSplitPos eWhich;
+public:
+ ScViewSelectionEngine( vcl::Window* pWindow, ScTabView* pView,
+ ScSplitPos eSplitPos );
+
+ ScSplitPos GetWhich() const { return eWhich; }
+ void SetWhich(ScSplitPos eNew) { eWhich = eNew; }
+};
+
+class ScViewFunctionSet : public FunctionSet // View (Gridwin / keyboard)
+{
+private:
+ ScViewData* m_pViewData;
+ ScViewSelectionEngine* m_pEngine;
+
+ bool m_bAnchor;
+ bool m_bStarted;
+ ScAddress m_aAnchorPos;
+
+ ScSplitPos GetWhich() const;
+
+ sal_uInt64 CalcUpdateInterval( const Size& rWinSize, const Point& rEffPos,
+ bool bLeftScroll, bool bTopScroll, bool bRightScroll, bool bBottomScroll );
+
+public:
+ ScViewFunctionSet( ScViewData* pNewViewData );
+
+ void SetSelectionEngine( ScViewSelectionEngine* pSelEngine );
+
+ void SetAnchor( SCCOL nPosX, SCROW nPosY );
+ void SetAnchorFlag( bool bSet );
+
+ virtual void BeginDrag() override;
+ virtual void CreateAnchor() override;
+ virtual void DestroyAnchor() override;
+ virtual void SetCursorAtPoint( const Point& rPointPixel, bool bDontSelectAtCursor = false ) override;
+ virtual bool IsSelectionAtPoint( const Point& rPointPixel ) override;
+ virtual void DeselectAtPoint( const Point& rPointPixel ) override;
+ virtual void DeselectAll() override;
+
+ bool SetCursorAtCell( SCCOL nPosX, SCROW nPosY, bool bScroll );
+ bool CheckRefBounds(SCCOL nPosX, SCROW nPosY);
+};
+
+class ScHeaderFunctionSet : public FunctionSet // Column / row headers
+{
+private:
+ ScViewData* pViewData;
+ bool bColumn; // Col- / Rowbar
+ ScSplitPos eWhich;
+
+ bool bAnchor;
+ SCCOLROW nCursorPos;
+
+public:
+ ScHeaderFunctionSet( ScViewData* pNewViewData );
+
+ void SetColumn( bool bSet );
+ void SetWhich( ScSplitPos eNew );
+
+ virtual void BeginDrag() override;
+ virtual void CreateAnchor() override;
+ virtual void DestroyAnchor() override;
+ virtual void SetCursorAtPoint( const Point& rPointPixel, bool bDontSelectAtCursor = false ) override;
+ virtual bool IsSelectionAtPoint( const Point& rPointPixel ) override;
+ virtual void DeselectAtPoint( const Point& rPointPixel ) override;
+ virtual void DeselectAll() override;
+
+ void SetAnchorFlag(bool bSet) { bAnchor = bSet; }
+};
+
+class ScHeaderSelectionEngine : public SelectionEngine
+{
+public:
+ ScHeaderSelectionEngine( vcl::Window* pWindow, ScHeaderFunctionSet* pFuncSet );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/selectionstate.hxx b/sc/source/ui/inc/selectionstate.hxx
new file mode 100644
index 0000000000..4243e618e0
--- /dev/null
+++ b/sc/source/ui/inc/selectionstate.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 .
+ */
+
+#pragma once
+
+#include <editeng/editdata.hxx>
+#include <address.hxx>
+
+/** Enumerates all possible types of selections in a Calc document. */
+enum ScSelectionType
+{
+ SC_SELECTTYPE_NONE, /// No selection, simple cell cursor.
+ SC_SELECTTYPE_SHEET, /// Single cell, cell range, or multi range selection.
+ SC_SELECTTYPE_EDITCELL, /// Cell in edit mode (with or without selection).
+};
+
+class ScViewData;
+
+/** Contains all available data about any possible selection in a Calc document. */
+class ScSelectionState
+{
+public:
+ explicit ScSelectionState( ScViewData& rViewData );
+
+ /** Returns the type of the selection this object contains. */
+ ScSelectionType GetSelectionType() const { return meType; }
+
+ /** Returns the position of the cell cursor. */
+ const ScAddress& GetCellCursor() const { return maCursor; }
+ /** Returns the edit engine selection. */
+ const ESelection& GetEditSelection() const { return maEditSel; }
+
+private:
+ ScSelectionType meType; /// Type of the selection.
+ ScAddress maCursor; /// Cell cursor position.
+ ESelection maEditSel; /// Selection in edit mode.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/seltrans.hxx b/sc/source/ui/inc/seltrans.hxx
new file mode 100644
index 0000000000..f783e58fa5
--- /dev/null
+++ b/sc/source/ui/inc/seltrans.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/transfer.hxx>
+#include <rtl/ref.hxx>
+
+class ScTabView;
+class ScTransferObj;
+class ScDrawTransferObj;
+
+enum ScSelectionTransferMode
+{
+ SC_SELTRANS_INVALID,
+ SC_SELTRANS_CELL,
+ SC_SELTRANS_CELLS,
+ SC_SELTRANS_DRAW_BITMAP,
+ SC_SELTRANS_DRAW_GRAPHIC,
+ SC_SELTRANS_DRAW_BOOKMARK,
+ SC_SELTRANS_DRAW_OLE,
+ SC_SELTRANS_DRAW_OTHER
+};
+
+class ScSelectionTransferObj : public TransferableHelper
+{
+private:
+ ScTabView* pView;
+ ScSelectionTransferMode eMode;
+ rtl::Reference<ScTransferObj> mxCellData;
+ rtl::Reference<ScDrawTransferObj> mxDrawData;
+
+ ScSelectionTransferObj( ScTabView* pSource, ScSelectionTransferMode eNewMode );
+ void CreateCellData();
+ void CreateDrawData();
+
+public:
+ // creates an object if the view has a valid selection,
+ // returns NULL otherwise
+ static rtl::Reference<ScSelectionTransferObj> CreateFromView( ScTabView* pSource );
+
+ virtual ~ScSelectionTransferObj() override;
+
+ void ForgetView();
+ ScTabView* GetView() const { return pView; }
+
+ ScTransferObj* GetCellData();
+ ScDrawTransferObj* GetDrawData();
+
+ virtual void AddSupportedFormats() override;
+ virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override;
+ virtual void ObjectReleased() override;
+ virtual sal_Bool SAL_CALL isComplex() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/servobj.hxx b/sc/source/ui/inc/servobj.hxx
new file mode 100644
index 0000000000..2bb36aa6d9
--- /dev/null
+++ b/sc/source/ui/inc/servobj.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svl/lstner.hxx>
+#include <svl/listener.hxx>
+#include <sfx2/linksrc.hxx>
+#include <address.hxx>
+#include <svl/SfxBroadcaster.hxx>
+
+class ScDocShell;
+class ScServerObject;
+
+class ScServerObjectSvtListenerForwarder : public SvtListener
+{
+ ScServerObject* pObj;
+ SfxBroadcaster aBroadcaster;
+public:
+ ScServerObjectSvtListenerForwarder( ScServerObject* pObjP);
+ virtual ~ScServerObjectSvtListenerForwarder() override;
+ virtual void Notify( const SfxHint& rHint ) override;
+};
+
+class ScServerObject : public ::sfx2::SvLinkSource, public SfxListener
+{
+private:
+ ScServerObjectSvtListenerForwarder aForwarder;
+ ScDocShell* pDocSh;
+ ScRange aRange;
+ OUString aItemStr;
+ bool bRefreshListener;
+
+ void Clear();
+
+public:
+ ScServerObject( ScDocShell* pShell, const OUString& rItem );
+ virtual ~ScServerObject() override;
+
+ virtual bool GetData( css::uno::Any & rData /*out param*/,
+ const OUString & rMimeType,
+ bool bSynchron = false ) override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ void EndListeningAll();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/sharedocdlg.hxx b/sc/source/ui/inc/sharedocdlg.hxx
new file mode 100644
index 0000000000..d6dcc4810d
--- /dev/null
+++ b/sc/source/ui/inc/sharedocdlg.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScViewData;
+class ScDocShell;
+
+
+class ScShareDocumentDlg : public weld::GenericDialogController
+{
+private:
+ OUString m_aStrNoUserData;
+ OUString m_aStrUnknownUser;
+ OUString m_aStrExclusiveAccess;
+
+ ScDocShell* mpDocShell;
+
+ std::unique_ptr<weld::CheckButton> m_xCbShare;
+ std::unique_ptr<weld::Label> m_xFtWarning;
+ std::unique_ptr<weld::TreeView> m_xLbUsers;
+
+ DECL_LINK(ToggleHandle, weld::Toggleable&, void);
+ DECL_LINK(SizeAllocated, const Size&, void);
+
+public:
+ ScShareDocumentDlg(weld::Window* pParent, const ScViewData* pViewData);
+ virtual ~ScShareDocumentDlg() override;
+
+ bool IsShareDocumentChecked() const;
+ void UpdateView();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/shtabdlg.hxx b/sc/source/ui/inc/shtabdlg.hxx
new file mode 100644
index 0000000000..448a938ba7
--- /dev/null
+++ b/sc/source/ui/inc/shtabdlg.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScShowTabDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::Frame> m_xFrame;
+ std::unique_ptr<weld::TreeView> m_xLb;
+
+ DECL_LINK(DblClkHdl, weld::TreeView&, bool);
+
+public:
+ ScShowTabDlg(weld::Window* pParent);
+ virtual ~ScShowTabDlg() override;
+
+ /** Sets dialog title, fixed text for listbox and help IDs. */
+ void SetDescription(const OUString& rTitle, const OUString& rFixedText,
+ const OUString& nDlgHelpId, const OUString& nLbHelpId);
+
+ /** Inserts a string into the weld::TreeView. */
+ void Insert(const OUString& rString, bool bSelected);
+
+ std::vector<sal_Int32> GetSelectedRows() const;
+ OUString GetEntry(sal_Int32 nPos) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/simpref.hxx b/sc/source/ui/inc/simpref.hxx
new file mode 100644
index 0000000000..0286aa6cf0
--- /dev/null
+++ b/sc/source/ui/inc/simpref.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+
+class ScDocument;
+
+class ScSimpleRefDlg: public ScAnyRefDlgController
+{
+private:
+ Link<const OUString*,void> aCloseHdl;
+ Link<const OUString&,void> aDoneHdl;
+ Link<const OUString&,void> aAbortedHdl;
+ Link<const OUString&,void> aChangeHdl;
+
+ ScRange theCurArea;
+ bool bCloseFlag;
+ bool bAutoReOpen;
+ bool bCloseOnButtonUp;
+ bool bSingleCell;
+ bool bMultiSelection;
+
+ std::unique_ptr<weld::Label> m_xFtAssign;
+ std::unique_ptr<formula::RefEdit> m_xEdAssign;
+ std::unique_ptr<formula::RefButton> m_xRbAssign;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ void Init();
+
+ DECL_LINK( CancelBtnHdl, weld::Button&, void );
+ DECL_LINK( OkBtnHdl, weld::Button&, void );
+
+protected:
+
+ virtual void RefInputDone( bool bForced = false ) override;
+
+public:
+ ScSimpleRefDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent);
+ virtual ~ScSimpleRefDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+ void StartRefInput();
+
+ void SetRefString(const OUString &rStr);
+ virtual void FillInfo(SfxChildWinInfo&) const override;
+
+ void SetCloseHdl( const Link<const OUString*,void>& rLink );
+ void SetUnoLinks( const Link<const OUString&,void>& rDone, const Link<const OUString&,void>& rAbort,
+ const Link<const OUString&,void>& rChange );
+
+ void SetFlags( bool bSetCloseOnButtonUp, bool bSetSingleCell, bool bSetMultiSelection );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/sizedev.hxx b/sc/source/ui/inc/sizedev.hxx
new file mode 100644
index 0000000000..8bf98be8ef
--- /dev/null
+++ b/sc/source/ui/inc/sizedev.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/mapmod.hxx>
+#include <vcl/vclptr.hxx>
+
+class OutputDevice;
+class ScDocShell;
+
+class ScSizeDeviceProvider
+{
+ VclPtr<OutputDevice> pDevice;
+ bool bOwner;
+ double nPPTX;
+ double nPPTY;
+ MapMode aOldMapMode;
+
+public:
+ ScSizeDeviceProvider( ScDocShell* pDocSh );
+ ~ScSizeDeviceProvider();
+
+ OutputDevice* GetDevice() const { return pDevice.get(); }
+ double GetPPTX() const { return nPPTX; }
+ double GetPPTY() const { return nPPTY; }
+ bool IsPrinter() const { return !bOwner; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/solveroptions.hxx b/sc/source/ui/inc/solveroptions.hxx
new file mode 100644
index 0000000000..a43a6b6320
--- /dev/null
+++ b/sc/source/ui/inc/solveroptions.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <utility>
+#include <vcl/weld.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+namespace com::sun::star {
+ namespace beans { struct PropertyValue; }
+}
+
+class ScSolverOptionsString
+{
+ bool mbIsDouble;
+ double mfDoubleValue;
+ sal_Int32 mnIntValue;
+ OUString msStr;
+
+public:
+ explicit ScSolverOptionsString(OUString aStr)
+ : mbIsDouble(false)
+ , mfDoubleValue(0.0)
+ , mnIntValue(0)
+ , msStr(std::move(aStr))
+ {
+ }
+
+ bool IsDouble() const { return mbIsDouble; }
+ double GetDoubleValue() const { return mfDoubleValue; }
+ sal_Int32 GetIntValue() const { return mnIntValue; }
+ const OUString& GetText() const { return msStr; }
+
+ void SetDoubleValue( double fNew ) { mbIsDouble = true; mfDoubleValue = fNew; }
+ void SetIntValue( sal_Int32 nNew ) { mbIsDouble = false; mnIntValue = nNew; }
+};
+
+class ScSolverIntegerDialog;
+class ScSolverValueDialog;
+
+class ScSolverOptionsDialog : public weld::GenericDialogController
+{
+ css::uno::Sequence<OUString> maImplNames;
+ OUString maEngine;
+ css::uno::Sequence<css::beans::PropertyValue> maProperties;
+
+ std::vector<std::unique_ptr<ScSolverOptionsString>> m_aOptions;
+
+ std::unique_ptr<weld::ComboBox> m_xLbEngine;
+ std::unique_ptr<weld::TreeView> m_xLbSettings;
+ std::unique_ptr<weld::Button> m_xBtnEdit;
+
+ std::shared_ptr<ScSolverIntegerDialog> m_xIntDialog;
+ std::shared_ptr<ScSolverValueDialog> m_xValDialog;
+
+ DECL_LINK( EngineSelectHdl, weld::ComboBox&, void );
+ DECL_LINK( SettingsSelHdl, weld::TreeView&, void );
+ DECL_LINK( SettingsDoubleClickHdl, weld::TreeView&, bool );
+ DECL_LINK( ButtonHdl, weld::Button&, void );
+
+ void ReadFromComponent();
+ void FillListBox();
+ void EditOption();
+
+public:
+ ScSolverOptionsDialog( weld::Window* pParent,
+ const css::uno::Sequence<OUString>& rImplNames,
+ const css::uno::Sequence<OUString>& rDescriptions,
+ OUString aEngine,
+ const css::uno::Sequence<css::beans::PropertyValue>& rProperties );
+ virtual ~ScSolverOptionsDialog() override;
+
+ // already updated in selection handler
+ const OUString& GetEngine() const { return maEngine; }
+ const css::uno::Sequence<css::beans::PropertyValue>& GetProperties();
+};
+
+class ScSolverIntegerDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Frame> m_xFrame;
+ std::unique_ptr<weld::SpinButton> m_xNfValue;
+
+public:
+ ScSolverIntegerDialog(weld::Window* pParent);
+ virtual ~ScSolverIntegerDialog() override;
+
+ void SetOptionName( const OUString& rName );
+ void SetValue( sal_Int32 nValue );
+ void SetMax( sal_Int32 nValue );
+ sal_Int32 GetValue() const;
+};
+
+class ScSolverValueDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::Frame> m_xFrame;
+ std::unique_ptr<weld::Entry> m_xEdValue;
+ double m_fMaxValue;
+
+public:
+ ScSolverValueDialog(weld::Window* pParent);
+ virtual ~ScSolverValueDialog() override;
+
+ void SetOptionName( const OUString& rName );
+ void SetValue( double fValue );
+ void SetMax( double fValue );
+ double GetValue() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/solverutil.hxx b/sc/source/ui/inc/solverutil.hxx
new file mode 100644
index 0000000000..34cdbb51aa
--- /dev/null
+++ b/sc/source/ui/inc/solverutil.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 <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+namespace com::sun::star {
+ namespace beans { struct PropertyValue; }
+ namespace sheet { class XSolver; }
+}
+
+class ScSolverUtil
+{
+public:
+ static void GetImplementations( css::uno::Sequence<OUString>& rImplNames,
+ css::uno::Sequence<OUString>& rDescriptions );
+ static css::uno::Reference<css::sheet::XSolver> GetSolver( std::u16string_view rImplName );
+ static css::uno::Sequence<css::beans::PropertyValue> GetDefaults( std::u16string_view rImplName );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/solvrdlg.hxx b/sc/source/ui/inc/solvrdlg.hxx
new file mode 100644
index 0000000000..8b5026ba2c
--- /dev/null
+++ b/sc/source/ui/inc/solvrdlg.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+
+enum ScSolverErr
+ {
+ SOLVERR_NOFORMULA,
+ SOLVERR_INVALID_FORMULA,
+ SOLVERR_INVALID_VARIABLE,
+ SOLVERR_INVALID_TARGETVALUE
+ };
+
+class ScSolverDlg : public ScAnyRefDlgController
+{
+public:
+ ScSolverDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScDocument* pDocument,
+ const ScAddress& aCursorPos );
+ virtual ~ScSolverDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual bool IsRefInputMode() const override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ ScAddress theFormulaCell;
+ ScAddress theVariableCell;
+ OUString theTargetValStr;
+
+ ScDocument* pDoc;
+ const SCTAB nCurTab;
+ bool bDlgLostFocus;
+ const OUString errMsgInvalidVar;
+ const OUString errMsgInvalidForm;
+ const OUString errMsgNoFormula;
+ const OUString errMsgInvalidVal;
+
+ formula::RefEdit* m_pEdActive;
+
+ std::unique_ptr<weld::Label> m_xFtFormulaCell;
+ std::unique_ptr<formula::RefEdit> m_xEdFormulaCell;
+ std::unique_ptr<formula::RefButton> m_xRBFormulaCell;
+
+ std::unique_ptr<weld::Entry> m_xEdTargetVal;
+
+ std::unique_ptr<weld::Label> m_xFtVariableCell;
+ std::unique_ptr<formula::RefEdit> m_xEdVariableCell;
+ std::unique_ptr<formula::RefButton> m_xRBVariableCell;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ std::shared_ptr<weld::MessageDialog> m_xMessageBox;
+
+ void Init();
+ bool CheckTargetValue( const OUString& rStrVal );
+ void RaiseError( ScSolverErr eError );
+
+ DECL_LINK( BtnHdl, weld::Button&, void );
+ DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( LoseEditFocusHdl, formula::RefEdit&, void );
+
+ DECL_LINK( GetButtonFocusHdl, formula::RefButton&, void );
+ DECL_LINK( LoseButtonFocusHdl, formula::RefButton&, void );
+
+ DECL_LINK( GetFocusHdl, weld::Widget&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/sortdlg.hxx b/sc/source/ui/inc/sortdlg.hxx
new file mode 100644
index 0000000000..a18bc66da8
--- /dev/null
+++ b/sc/source/ui/inc/sortdlg.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+#include <vcl/weld.hxx>
+
+class ScSortDlg : public SfxTabDialogController
+{
+public:
+ ScSortDlg(weld::Window* pParent, const SfxItemSet* pArgSet);
+ virtual ~ScSortDlg() override;
+};
+
+class ScSortWarningDlg : public weld::GenericDialogController
+{
+public:
+ ScSortWarningDlg(weld::Window* pParent, std::u16string_view rExtendText, std::u16string_view rCurrentText);
+ virtual ~ScSortWarningDlg() override;
+ DECL_LINK(BtnHdl, weld::Button&, void);
+private:
+ std::unique_ptr<weld::Label> m_xFtText;
+ std::unique_ptr<weld::Button> m_xBtnExtSort;
+ std::unique_ptr<weld::Button> m_xBtnCurSort;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/sortkeydlg.hxx b/sc/source/ui/inc/sortkeydlg.hxx
new file mode 100644
index 0000000000..2c702e6100
--- /dev/null
+++ b/sc/source/ui/inc/sortkeydlg.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+
+#include <vcl/weld.hxx>
+
+struct ScSortKeyItem
+{
+ std::unique_ptr<weld::Builder> m_xBuilder;
+
+ std::unique_ptr<weld::Frame> m_xFrame;
+ std::unique_ptr<weld::ComboBox> m_xLbSort;
+ std::unique_ptr<weld::RadioButton> m_xBtnUp;
+ std::unique_ptr<weld::RadioButton> m_xBtnDown;
+ std::unique_ptr<weld::Label> m_xLabel;
+ weld::Container* m_pParent;
+
+ ScSortKeyItem(weld::Container* pParent);
+ ~ScSortKeyItem();
+
+ void DisableField();
+ void EnableField();
+};
+
+typedef std::vector<std::unique_ptr<ScSortKeyItem>> ScSortKeyItems;
+
+class ScSortKeyWindow
+{
+public:
+ ScSortKeyItems m_aSortKeyItems;
+
+private:
+ weld::Container* m_pBox;
+
+public:
+ ScSortKeyWindow(weld::Container* pBox);
+ ~ScSortKeyWindow();
+
+ void AddSortKey(sal_uInt16 nItem);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/spelldialog.hxx b/sc/source/ui/inc/spelldialog.hxx
new file mode 100644
index 0000000000..2319866afb
--- /dev/null
+++ b/sc/source/ui/inc/spelldialog.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <svx/SpellDialogChildWindow.hxx>
+#include <document.hxx>
+
+class ScConversionEngineBase;
+class ScSelectionState;
+class ScTabViewShell;
+class ScViewData;
+class ScRangeList;
+
+/** Specialized spell check dialog child window for Calc.
+
+ This derivation of the svx::SpellDialogChildWindow base class provides
+ Calc specific implementations of the virtual functions GetNextWrongSentence()
+ and ApplyChangedSentence().
+ */
+class ScSpellDialogChildWindow : public svx::SpellDialogChildWindow
+{
+public:
+ SFX_DECL_CHILDWINDOW_WITHID( ScSpellDialogChildWindow );
+
+ explicit ScSpellDialogChildWindow( vcl::Window* pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo* pInfo );
+ virtual ~ScSpellDialogChildWindow() override;
+
+ /** This method makes the one from the base class public so that
+ it can be called from the view shell when one is created.
+ */
+ void InvalidateSpellDialog();
+
+protected:
+ /** Iterate over the sentences in all text shapes and stop at the
+ next sentence with spelling errors. While doing so the view
+ mode may be changed and text shapes are set into edit mode.
+ */
+ virtual svx::SpellPortions GetNextWrongSentence( bool bRecheck ) override;
+
+ /** This method is responsible for merging corrections made in the
+ spelling dialog back into the document.
+ */
+ virtual void ApplyChangedSentence( const svx::SpellPortions& rChanged, bool bRecheck ) override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+private:
+ void Reset();
+ void Init();
+
+ bool IsSelectionChanged();
+
+private:
+ typedef ::std::unique_ptr< ScConversionEngineBase > ScConvEnginePtr;
+ typedef ::std::unique_ptr< ScSelectionState > ScSelectionStatePtr;
+
+ ScConvEnginePtr mxEngine;
+ ScDocumentUniquePtr mxUndoDoc;
+ ScDocumentUniquePtr mxRedoDoc;
+ ScSelectionStatePtr mxOldSel; /// For cursor position in selection
+ tools::SvRef< ScRangeList >
+ mxOldRangeList; /// Original selection range for comparison.
+ ScTabViewShell* mpViewShell;
+ ScViewData* mpViewData;
+ ScDocShell* mpDocShell;
+ ScDocument* mpDoc;
+ bool mbNeedNextObj;
+ bool mbOldIdleEnabled;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/spelleng.hxx b/sc/source/ui/inc/spelleng.hxx
new file mode 100644
index 0000000000..98baa5c8e4
--- /dev/null
+++ b/sc/source/ui/inc/spelleng.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <editutil.hxx>
+#include "selectionstate.hxx"
+#include "spellparam.hxx"
+
+class ScViewData;
+class ScDocShell;
+class ScDocument;
+class SfxItemPool;
+
+namespace weld { class Widget; }
+
+/** Base class for special type of edit engines, i.e. for spell checker and text conversion. */
+class ScConversionEngineBase : public ScEditEngineDefaulter
+{
+public:
+ explicit ScConversionEngineBase(
+ SfxItemPool* pEnginePool, ScViewData& rViewData,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc );
+
+ virtual ~ScConversionEngineBase() override;
+
+ /** Derived classes implement to convert all cells in the selection or sheet. */
+ virtual void ConvertAll(weld::Widget* pDialogParent, EditView& rEditView) = 0;
+
+ /** Returns true, if at least one cell has been modified. */
+ bool IsAnyModified() const { return mbIsAnyModified; }
+ /** Returns true, if the entire document/selection has been finished. */
+ bool IsFinished() const { return mbFinished; }
+
+protected:
+ /** Implementation of cell iteration. Finds a cell that needs conversion.
+ @return true = Current cell needs conversion (i.e. spelling error found). */
+ bool FindNextConversionCell();
+ /** Restores the initial cursor position. */
+ void RestoreCursorPos();
+
+ /** Derived classes return, if the current text needs conversion (i.e. spelling error found).
+ @return true = Current edit text needs conversion. */
+ virtual bool NeedsConversion() = 0;
+
+ /** Derived classes may show a query box that asks whether to restart at top of the sheet.
+ @descr Default here is no dialog and restart always.
+ @return true = Restart at top, false = Stop the conversion. */
+ virtual bool ShowTableWrapDialog();
+ /** Derived classes may show a message box stating that the conversion is finished.
+ @descr Default here is no dialog. */
+ virtual void ShowFinishDialog();
+
+private:
+ /** Fills the edit engine from a document cell. */
+ void FillFromCell( SCCOL nCol, SCROW nRow, SCTAB nTab );
+
+protected: // for usage in derived classes
+ ScViewData& mrViewData;
+ ScDocShell& mrDocShell;
+ ScDocument& mrDoc;
+
+private:
+ ScSelectionState maSelState; /// Selection data of the document.
+ ScDocument* mpUndoDoc; /// Document stores all old cells for UNDO action.
+ ScDocument* mpRedoDoc; /// Document stores all new cells for REDO action.
+ LanguageType meCurrLang; /// Current cell language.
+ SCCOL mnStartCol; /// Initial column index.
+ SCROW mnStartRow; /// Initial row index.
+ SCTAB mnStartTab; /// Initial sheet index.
+ SCCOL mnCurrCol; /// Current column index.
+ SCROW mnCurrRow; /// Current row index.
+ bool mbIsAnyModified; /// true = At least one cell has been changed.
+ bool mbInitialState; /// true = Not searched for a cell yet.
+ bool mbWrappedInTable; /// true = Already restarted at top of the sheet.
+ bool mbFinished; /// true = Entire document/selection finished.
+};
+
+/** Edit engine for spell checking. */
+class ScSpellingEngine : public ScConversionEngineBase
+{
+public:
+ explicit ScSpellingEngine(
+ SfxItemPool* pEnginePool,
+ ScViewData& rViewData,
+ ScDocument* pUndoDoc,
+ ScDocument* pRedoDoc,
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > const & xSpeller );
+
+ /** Checks spelling of all cells in the selection or sheet. */
+ virtual void ConvertAll(weld::Widget* pDialogParent, EditView& rEditView) override;
+
+protected:
+ /** Callback from edit engine to check the next cell. */
+ virtual bool SpellNextDocument() override;
+
+ /** Returns true, if the current text contains a spelling error. */
+ virtual bool NeedsConversion() override;
+
+ /** Show a query box that asks whether to restart at top of the sheet.
+ @return true = Restart at top, false = Stop the conversion. */
+ virtual bool ShowTableWrapDialog() override;
+ /** Show a message box stating that spell checking is finished. */
+ virtual void ShowFinishDialog() override;
+
+private:
+ /** Returns the spelling dialog if it is open. */
+ weld::Widget* GetDialogParent();
+};
+
+/** Edit engine for text conversion. */
+class ScTextConversionEngine : public ScConversionEngineBase
+{
+public:
+ explicit ScTextConversionEngine(
+ SfxItemPool* pEnginePool,
+ ScViewData& rViewData,
+ ScConversionParam aConvParam,
+ ScDocument* pUndoDoc,
+ ScDocument* pRedoDoc );
+
+ /** Converts all cells in the selection or sheet according to set language. */
+ virtual void ConvertAll(weld::Widget* pDialogParent, EditView& rEditView) override;
+
+protected:
+ /** Callback from edit engine to convert the next cell. */
+ virtual bool ConvertNextDocument() override;
+
+ /** Returns true, if the current text contains text to convert. */
+ virtual bool NeedsConversion() override;
+
+private:
+ ScConversionParam maConvParam; /// Conversion parameters.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/spellparam.hxx b/sc/source/ui/inc/spellparam.hxx
new file mode 100644
index 0000000000..7aff4317fd
--- /dev/null
+++ b/sc/source/ui/inc/spellparam.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 .
+ */
+#pragma once
+
+#include <vcl/font.hxx>
+
+/** Specifiers for sheet conversion (functions iterating over the sheet and modifying cells). */
+enum ScConversionType
+{
+ SC_CONVERSION_SPELLCHECK, /// Spell checker.
+ SC_CONVERSION_HANGULHANJA, /// Hangul-Hanja converter.
+ SC_CONVERSION_CHINESE_TRANSL /// Chinese simplified/traditional converter.
+};
+
+/** Parameters for conversion. */
+class ScConversionParam
+{
+public:
+ /** Constructs an empty parameter struct with the passed conversion type. */
+ explicit ScConversionParam( ScConversionType eConvType );
+
+ /** Constructs parameter struct for text conversion without changing the language. */
+ explicit ScConversionParam(
+ ScConversionType eConvType,
+ LanguageType eLang,
+ sal_Int32 nOptions,
+ bool bIsInteractive );
+
+ /** Constructs parameter struct for text conversion with language change. */
+ explicit ScConversionParam(
+ ScConversionType eConvType,
+ LanguageType eSourceLang,
+ LanguageType eTargetLang,
+ vcl::Font aTargetFont,
+ sal_Int32 nOptions,
+ bool bIsInteractive );
+
+ ScConversionType GetType() const { return meConvType; }
+ LanguageType GetSourceLang() const { return meSourceLang; }
+ LanguageType GetTargetLang() const { return meTargetLang; }
+ const vcl::Font* GetTargetFont() const { return mbUseTargetFont ? &maTargetFont : nullptr; }
+ sal_Int32 GetOptions() const { return mnOptions; }
+ bool IsInteractive() const { return mbIsInteractive; }
+
+private:
+ ScConversionType meConvType; /// Type of the conversion.
+ LanguageType meSourceLang; /// Source language for conversion.
+ LanguageType meTargetLang; /// Target language for conversion.
+ vcl::Font maTargetFont; /// Target font to be used if language has to be changed.
+ sal_Int32 mnOptions; /// Conversion options.
+ bool mbUseTargetFont; /// True = Use maTargetFont to change font during conversion.
+ bool mbIsInteractive; /// True = Text conversion has (specific) dialog that may be raised.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/strindlg.hxx b/sc/source/ui/inc/strindlg.hxx
new file mode 100644
index 0000000000..6602a5563d
--- /dev/null
+++ b/sc/source/ui/inc/strindlg.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+class ScStringInputDlg : public weld::GenericDialogController
+{
+private:
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::Entry> m_xEdInput;
+
+public:
+ ScStringInputDlg(weld::Window* pParent,
+ const OUString& rTitle,
+ const OUString& rEditTitle,
+ const OUString& rDefault,
+ const OUString& sHelpId, const OUString& sEditHelpId);
+
+ OUString GetInputString() const
+ {
+ return m_xEdInput->get_text();
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/styledlg.hxx b/sc/source/ui/inc/styledlg.hxx
new file mode 100644
index 0000000000..2fe13111c6
--- /dev/null
+++ b/sc/source/ui/inc/styledlg.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 .
+ */
+
+#pragma once
+
+#include <sfx2/styledlg.hxx>
+
+class SfxStyleSheetBase;
+class SdrView;
+
+class ScStyleDlg : public SfxStyleDialogController
+{
+public:
+ ScStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ bool bPage);
+
+protected:
+ virtual void PageCreated(const OUString& rPageId, SfxTabPage& rTabPage) override;
+ virtual void RefreshInputSet() override;
+
+private:
+ bool m_bPage;
+};
+
+class ScDrawStyleDlg : public SfxStyleDialogController
+{
+public:
+ ScDrawStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ SdrView* pView);
+
+protected:
+ virtual void PageCreated(const OUString& rPageId, SfxTabPage& rTabPage) override;
+ virtual void RefreshInputSet() override;
+
+private:
+ SdrView* mpView;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/subtdlg.hxx b/sc/source/ui/inc/subtdlg.hxx
new file mode 100644
index 0000000000..b0c97b09e1
--- /dev/null
+++ b/sc/source/ui/inc/subtdlg.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ScSubTotalDlg : public SfxTabDialogController
+{
+public:
+ ScSubTotalDlg(weld::Window* pParent, const SfxItemSet& rArgSet);
+ virtual ~ScSubTotalDlg() override;
+
+private:
+ std::unique_ptr<weld::Button> m_xBtnRemove;
+ DECL_LINK(RemoveHdl, weld::Button&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabbgcolordlg.hxx b/sc/source/ui/inc/tabbgcolordlg.hxx
new file mode 100644
index 0000000000..94323f676b
--- /dev/null
+++ b/sc/source/ui/inc/tabbgcolordlg.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/SvxColorValueSet.hxx>
+#include <svx/PaletteManager.hxx>
+
+class ScTabBgColorDlg : public weld::GenericDialogController
+{
+public:
+ ScTabBgColorDlg(weld::Window* pParent,
+ const OUString& rTitle,
+ const OUString& rTabBgColorNoColorText,
+ const Color& rDefaultColor);
+ virtual ~ScTabBgColorDlg() override;
+
+ void GetSelectedColor( Color& rColor ) const;
+
+ class ScTabBgColorValueSet : public SvxColorValueSet
+ {
+ public:
+ ScTabBgColorValueSet(std::unique_ptr<weld::ScrolledWindow> pWindow);
+ virtual ~ScTabBgColorValueSet() override;
+
+ void SetDialog(ScTabBgColorDlg* pTabBgColorDlg)
+ {
+ m_pTabBgColorDlg = pTabBgColorDlg;
+ }
+
+ virtual bool KeyInput( const KeyEvent& rKEvt ) override;
+ private:
+ ScTabBgColorDlg* m_pTabBgColorDlg;
+ };
+
+private:
+ PaletteManager m_aPaletteManager;
+ Color m_aTabBgColor;
+
+ std::unique_ptr<weld::ComboBox> m_xSelectPalette;
+ std::unique_ptr<ScTabBgColorValueSet> m_xTabBgColorSet;
+ std::unique_ptr<weld::CustomWeld> m_xTabBgColorSetWin;
+ std::unique_ptr<weld::Button> m_xBtnOk;
+
+ void FillPaletteLB();
+
+ DECL_LINK(SelectPaletteLBHdl, weld::ComboBox&, void);
+ DECL_LINK(TabBgColorDblClickHdl_Impl, ValueSet*, void);
+ DECL_LINK(TabBgColorOKHdl_Impl, weld::Button&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabcont.hxx b/sc/source/ui/inc/tabcont.hxx
new file mode 100644
index 0000000000..6a12c1d073
--- /dev/null
+++ b/sc/source/ui/inc/tabcont.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include <svtools/tabbar.hxx>
+#include <vcl/transfer.hxx>
+
+class ScViewData;
+
+// initial size
+#define SC_TABBAR_DEFWIDTH 270
+
+class ScTabControl : public TabBar, public DropTargetHelper, public DragSourceHelper
+{
+private:
+ ScViewData* pViewData;
+ sal_uInt16 nMouseClickPageId; /// Last page ID after mouse button down/up
+ sal_uInt16 nSelPageIdByMouse; /// Selected page ID, if selected with mouse
+ bool bErrorShown;
+
+ void DoDrag();
+
+ sal_uInt16 GetMaxId() const;
+ SCTAB GetPrivatDropPos(const Point& rPos );
+
+ DECL_LINK(ShowPageList, const CommandEvent&, void);
+
+protected:
+ virtual void Select() override;
+ virtual void Command( const CommandEvent& rCEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) 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;
+
+ virtual bool StartRenaming() override;
+ virtual TabBarAllowRenamingReturnCode AllowRenaming() override;
+ virtual void EndRenaming() override;
+ virtual void Mirror() override;
+
+ virtual void AddTabClick() override;
+
+public:
+ ScTabControl( vcl::Window* pParent, ScViewData* pData );
+ virtual void dispose() override;
+ virtual ~ScTabControl() override;
+
+ using TabBar::StartDrag;
+
+ void UpdateInputContext();
+ void UpdateStatus();
+
+ void SetSheetLayoutRTL( bool bSheetRTL );
+ void SwitchToPageId( sal_uInt16 nId );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabopdlg.hxx b/sc/source/ui/inc/tabopdlg.hxx
new file mode 100644
index 0000000000..2e39e94d5f
--- /dev/null
+++ b/sc/source/ui/inc/tabopdlg.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 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include "anyrefdg.hxx"
+
+enum ScTabOpErr
+{
+ TABOPERR_NOFORMULA = 1,
+ TABOPERR_NOCOLROW,
+ TABOPERR_WRONGFORMULA,
+ TABOPERR_WRONGROW,
+ TABOPERR_NOCOLFORMULA,
+ TABOPERR_WRONGCOL,
+ TABOPERR_NOROWFORMULA
+};
+
+class ScTabOpDlg : public ScAnyRefDlgController
+{
+public:
+ ScTabOpDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScDocument* pDocument,
+ const ScRefAddress& rCursorPos);
+ virtual ~ScTabOpDlg() override;
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override;
+ virtual bool IsRefInputMode() const override { return true; }
+ virtual void SetActive() override;
+
+ virtual void Close() override;
+
+private:
+ ScRefAddress theFormulaCell;
+ ScRefAddress theFormulaEnd;
+ ScRefAddress theRowCell;
+ ScRefAddress theColCell;
+
+ ScDocument* pDoc;
+ const SCTAB nCurTab;
+ bool bDlgLostFocus;
+ const OUString errMsgNoFormula;
+ const OUString errMsgNoColRow;
+ const OUString errMsgWrongFormula;
+ const OUString errMsgWrongRowCol;
+ const OUString errMsgNoColFormula;
+ const OUString errMsgNoRowFormula;
+
+ formula::RefEdit* m_pEdActive;
+ std::unique_ptr<weld::Label> m_xFtFormulaRange;
+ std::unique_ptr<formula::RefEdit> m_xEdFormulaRange;
+ std::unique_ptr<formula::RefButton> m_xRBFormulaRange;
+
+ std::unique_ptr<weld::Label> m_xFtRowCell;
+ std::unique_ptr<formula::RefEdit> m_xEdRowCell;
+ std::unique_ptr<formula::RefButton> m_xRBRowCell;
+
+ std::unique_ptr<weld::Label> m_xFtColCell;
+ std::unique_ptr<formula::RefEdit> m_xEdColCell;
+ std::unique_ptr<formula::RefButton> m_xRBColCell;
+
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::Button> m_xBtnCancel;
+
+ void Init();
+ void RaiseError( ScTabOpErr eError );
+
+ DECL_LINK( BtnHdl, weld::Button&, void );
+ DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( LoseEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( GetButtonFocusHdl, formula::RefButton&, void );
+ DECL_LINK( LoseButtonFocusHdl, formula::RefButton&, void );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabpages.hxx b/sc/source/ui/inc/tabpages.hxx
new file mode 100644
index 0000000000..37d7515234
--- /dev/null
+++ b/sc/source/ui/inc/tabpages.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ScTabPageProtection : public SfxTabPage
+{
+ static const WhichRangesContainer pProtectionRanges;
+public:
+ ScTabPageProtection(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rAttrSet);
+ virtual ~ScTabPageProtection() override;
+
+ static WhichRangesContainer GetRanges () { return pProtectionRanges; }
+ virtual bool FillItemSet ( SfxItemSet* rCoreAttrs ) override;
+ virtual void Reset ( const SfxItemSet* ) override;
+
+protected:
+ virtual DeactivateRC DeactivatePage ( SfxItemSet* pSet ) override;
+
+private:
+ // current status:
+ bool bTriEnabled; // if before - DontCare
+ bool bDontCare; // all in TriState
+ bool bProtect; // secure individual settings for TriState
+ bool bHideForm;
+ bool bHideCell;
+ bool bHidePrint;
+
+ weld::TriStateEnabled aHideCellState;
+ weld::TriStateEnabled aProtectState;
+ weld::TriStateEnabled aHideFormulaState;
+ weld::TriStateEnabled aHidePrintState;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnHideCell;
+ std::unique_ptr<weld::CheckButton> m_xBtnProtect;
+ std::unique_ptr<weld::CheckButton> m_xBtnHideFormula;
+ std::unique_ptr<weld::CheckButton> m_xBtnHidePrint;
+
+ // Handler:
+ DECL_LINK(ProtectClickHdl, weld::Toggleable&, void);
+ DECL_LINK(HideCellClickHdl, weld::Toggleable&, void);
+ DECL_LINK(HideFormulaClickHdl, weld::Toggleable&, void);
+ DECL_LINK(HidePrintClickHdl, weld::Toggleable&, void);
+ void ButtonClick(const weld::Toggleable& rBox);
+ void UpdateButtons();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabsplit.hxx b/sc/source/ui/inc/tabsplit.hxx
new file mode 100644
index 0000000000..ce09efddc2
--- /dev/null
+++ b/sc/source/ui/inc/tabsplit.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/split.hxx>
+
+class ScViewData;
+
+class ScTabSplitter : public Splitter
+{
+private:
+ const ScViewData *const pViewData;
+ bool bFixed;
+
+protected:
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+
+public:
+ ScTabSplitter( vcl::Window* pParent, WinBits nWinStyle,
+ const ScViewData* pData );
+ virtual ~ScTabSplitter() override;
+
+ void SetFixed(bool bSet);
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rPaintRect ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx
new file mode 100644
index 0000000000..66bbc010ea
--- /dev/null
+++ b/sc/source/ui/inc/tabview.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <array>
+#include <memory>
+#include <svtools/scrolladaptor.hxx>
+#include <vcl/help.hxx>
+
+#include "hiranges.hxx"
+#include "viewutil.hxx"
+#include "select.hxx"
+#include "gridwin.hxx"
+#include "drawview.hxx"
+
+namespace editeng {
+ struct MisspellRanges;
+}
+
+class ScEditEngineDefaulter;
+class ScOutlineWindow;
+class ScRowBar;
+class ScColBar;
+class ScTabControl;
+class ScTabViewShell;
+struct ScRangeFindData;
+class SvBorder;
+class FuPoor;
+class Splitter;
+class ScTabSplitter;
+class SdrView;
+class SdrObject;
+class ScPageBreakData;
+class SdrHdlList;
+class TabBar;
+namespace com::sun::star::chart2::data { struct HighlightedRange; }
+namespace tools { class JsonWriter; }
+
+enum HeaderType
+{
+ COLUMN_HEADER,
+ ROW_HEADER,
+ BOTH_HEADERS
+};
+
+// Help - Window
+
+class ScCornerButton : public vcl::Window
+{
+private:
+ ScViewData* pViewData;
+
+protected:
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+ virtual void Resize() override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+public:
+ ScCornerButton( vcl::Window* pParent, ScViewData* pData );
+ virtual ~ScCornerButton() override;
+
+ virtual void StateChanged( StateChangedType nType ) override;
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+};
+
+class ScExtraEditViewManager
+{
+private:
+ enum ModifierTagType { Adder, Remover };
+
+public:
+ ScExtraEditViewManager(ScTabViewShell* pThisViewShell, std::array<VclPtr<ScGridWindow>, 4> const & pGridWin)
+ : mpThisViewShell(pThisViewShell)
+ , mpGridWin(pGridWin)
+ , mpOtherEditView(nullptr)
+ , nTotalWindows(0)
+ {}
+
+ ~ScExtraEditViewManager();
+
+ void Add(SfxViewShell* pViewShell, ScSplitPos eWhich);
+
+ void Remove(SfxViewShell* pViewShell, ScSplitPos eWhich);
+
+private:
+ template<ModifierTagType ModifierTag>
+ void Apply(SfxViewShell* pViewShell, ScSplitPos eWhich);
+
+ template<ModifierTagType ModifierTag>
+ void Modifier(ScGridWindow* pWin);
+
+private:
+ ScTabViewShell* mpThisViewShell;
+ std::array<VclPtr<ScGridWindow>, 4> const & mpGridWin;
+ EditView* mpOtherEditView;
+ int nTotalWindows;
+};
+
+class ScTabView
+{
+private:
+ enum BlockMode { None = 0, Normal = 1, Own = 2 };
+
+ VclPtr<vcl::Window> pFrameWin; // First !!!
+ ScViewData aViewData; // must be at the front !
+
+ std::unique_ptr<ScViewSelectionEngine> pSelEngine;
+ ScViewFunctionSet aFunctionSet;
+
+ std::unique_ptr<ScHeaderSelectionEngine> pHdrSelEng;
+ ScHeaderFunctionSet aHdrFunc;
+
+ std::unique_ptr<ScDrawView> pDrawView;
+
+ Size aFrameSize; // passed on as for DoResize
+ Point aBorderPos;
+
+ // The ownership of these two is rather weird. we seem to need
+ // to keep an old copy alive for some period of time to avoid crashing.
+ FuPoor* pDrawActual;
+ FuPoor* pDrawOld;
+
+ std::shared_ptr<weld::MessageDialog> m_xMessageBox;
+
+ std::array<VclPtr<ScGridWindow>, 4> pGridWin;
+ std::array<VclPtr<ScColBar>, 2> pColBar;
+ std::array<VclPtr<ScRowBar>, 2> pRowBar;
+ std::array<VclPtr<ScOutlineWindow>, 2> pColOutline;
+ std::array<VclPtr<ScOutlineWindow>, 2> pRowOutline;
+ VclPtr<ScTabSplitter> pHSplitter;
+ VclPtr<ScTabSplitter> pVSplitter;
+ VclPtr<ScTabControl> pTabControl;
+ VclPtr<ScrollAdaptor> aVScrollTop;
+ VclPtr<ScrollAdaptor> aVScrollBottom; // initially visible
+ VclPtr<ScrollAdaptor> aHScrollLeft; // initially visible
+ VclPtr<ScrollAdaptor> aHScrollRight;
+ VclPtr<ScCornerButton> aCornerButton;
+ VclPtr<ScCornerButton> aTopButton;
+
+ std::shared_ptr<sc::SpellCheckContext> mpSpellCheckCxt;
+
+ std::unique_ptr<sdr::overlay::OverlayObjectList> mxInputHintOO; // help hint for data validation
+
+ std::unique_ptr<ScPageBreakData> pPageBreakData;
+ std::vector<ScHighlightEntry> maHighlightRanges;
+
+ ScDocumentUniquePtr pBrushDocument; // cell formats for format paint brush
+ std::unique_ptr<SfxItemSet> pDrawBrushSet; // drawing object attributes for paint brush
+
+ Timer aScrollTimer;
+ VclPtr<ScGridWindow> pTimerWindow;
+ MouseEvent aTimerMEvt;
+
+ ScExtraEditViewManager aExtraEditViewManager;
+
+ void* nTipVisible;
+ tools::Rectangle aTipRectangle;
+ QuickHelpFlags nTipAlign;
+ OUString sTipString;
+ VclPtr<vcl::Window> sTopParent;
+
+ tools::Long nPrevDragPos;
+
+ BlockMode meBlockMode; // Marks block
+ BlockMode meHighlightBlockMode; // Highlight row/col
+
+ SCCOL nBlockStartX;
+ SCCOL nBlockStartXOrig;
+ SCCOL nBlockEndX;
+
+ SCROW nBlockStartY;
+ SCROW nBlockStartYOrig;
+ SCROW nBlockEndY;
+
+ SCTAB nBlockStartZ;
+ SCTAB nBlockEndZ;
+
+ SCCOL nOldCurX;
+ SCROW nOldCurY;
+
+ double mfPendingTabBarWidth; // Tab bar width relative to frame window width.
+
+ SCROW mnLOKStartHeaderRow;
+ SCROW mnLOKEndHeaderRow;
+ SCCOL mnLOKStartHeaderCol;
+ SCCOL mnLOKEndHeaderCol;
+
+ bool bMinimized:1;
+ bool bInUpdateHeader:1;
+ bool bInActivatePart:1;
+ bool bInZoomUpdate:1;
+ bool bMoveIsShift:1;
+ bool bDrawSelMode:1; // Only select draw objects ?
+ bool bLockPaintBrush:1; // keep for more than one use?
+ bool bDragging:1; // for scroll bars
+ bool bBlockNeg:1; // is no longer highlighted?
+ bool bBlockCols:1; // are whole columns selected?
+ bool bBlockRows:1; // are whole rows selected?
+ bool mbInlineWithScrollbar:1; // should inline with scrollbar?
+
+ double mfLastZoomScale = 0;
+ double mfAccumulatedZoom = 0;
+ tools::Long mnPendingaHScrollLeftDelta = 0;
+ tools::Long mnPendingaHScrollRightDelta = 0;
+
+ void Init();
+
+ void DoAddWin( ScGridWindow* pWin );
+
+ void InitScrollBar(ScrollAdaptor& rScrollBar, tools::Long nMaxVal, const Link<weld::Scrollbar&, void>& rLink);
+ DECL_LINK(HScrollLeftHdl, weld::Scrollbar&, void );
+ DECL_LINK(HScrollRightHdl, weld::Scrollbar&, void );
+ DECL_LINK(VScrollTopHdl, weld::Scrollbar&, void );
+ DECL_LINK(VScrollBottomHdl, weld::Scrollbar&, void );
+ DECL_LINK(EndScrollHdl, const MouseEvent&, bool);
+ void ScrollHdl(ScrollAdaptor* rScrollBar);
+
+ DECL_LINK(SplitHdl, Splitter*, void);
+ void DoHSplit(tools::Long nSplitPos);
+ void DoVSplit(tools::Long nSplitPos);
+
+ DECL_LINK( TimerHdl, Timer*, void );
+
+ void UpdateVarZoom();
+
+ static void SetScrollBar( ScrollAdaptor& rScroll, tools::Long nRangeMax, tools::Long nVisible, tools::Long nPos, bool bLayoutRTL );
+ static tools::Long GetScrollBarPos( const ScrollAdaptor& rScroll );
+
+ void GetAreaMoveEndPosition(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ SCCOL& rAreaX, SCROW& rAreaY, ScFollowMode& rMode,
+ bool bInteractiveByUser = false);
+
+ void SkipCursorHorizontal(SCCOL& rCurX, SCROW& rCurY, SCCOL nOldX, SCCOL nMovX);
+ void SkipCursorVertical(SCCOL& rCurX, SCROW& rCurY, SCROW nOldY, SCROW nMovY);
+
+ /**
+ *
+ * @brief Update marks for a selected Range. This is a helper function
+ * for PaintRangeFinder.
+ *
+ * @param pData: Range to update for painting.
+ * @param nTab: Current tab.
+ *
+ **/
+
+ void PaintRangeFinderEntry (const ScRangeFindData* pData, SCTAB nTab);
+
+ void SetZoomPercentFromCommand(sal_uInt16 nZoomPercent);
+
+ DECL_STATIC_LINK(ScTabView, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*);
+
+ void UpdateHighlightOverlay();
+
+protected:
+ void UpdateHeaderWidth( const ScVSplitPos* pWhich = nullptr,
+ const SCROW* pPosY = nullptr );
+
+ void HideTip();
+ void ShowRefTip();
+
+ void ZoomChanged();
+ void UpdateShow();
+ bool UpdateVisibleRange();
+ void GetBorderSize( SvBorder& rBorder, const Size& rSize );
+
+ void ResetDrawDragMode();
+ bool IsDrawTextEdit() const;
+ void DrawEnableAnim(bool bSet);
+
+ void MakeDrawView( TriState nForceDesignMode );
+
+ void HideNoteMarker();
+
+ void UpdateIMap( SdrObject* pObj );
+
+public:
+ /** make noncopyable */
+ ScTabView(const ScTabView&) = delete;
+ const ScTabView& operator=(const ScTabView&) = delete;
+
+ ScTabView( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell );
+ ~ScTabView();
+
+ enum SplitMethod { SC_SPLIT_METHOD_COL, SC_SPLIT_METHOD_ROW, SC_SPLIT_METHOD_CURSOR };
+
+ void MakeDrawLayer();
+
+ void HideListBox();
+
+ bool HasHintWindow() const;
+ void RemoveHintWindow();
+ void TestHintWindow();
+
+ DECL_LINK( TabBarResize, ::TabBar*, void );
+ /** Sets an absolute tab bar width (in pixels). */
+ void SetTabBarWidth( tools::Long nNewWidth );
+ /** Sets a relative tab bar width.
+ @param fRelTabBarWidth Tab bar width relative to frame window width (0.0 ... 1.0). */
+ SC_DLLPUBLIC void SetRelTabBarWidth( double fRelTabBarWidth );
+ /** Sets a relative tab bar width. Tab bar is resized again in next DoResize().
+ @param fRelTabBarWidth Tab bar width relative to frame window width (0.0 ... 1.0). */
+ void SetPendingRelTabBarWidth( double fRelTabBarWidth );
+ /** Returns the current tab bar width in pixels. */
+ tools::Long GetTabBarWidth() const;
+ /** Returns the current tab bar width relative to the frame window width (0.0 ... 1.0). */
+ SC_DLLPUBLIC static double GetRelTabBarWidth();
+ /** Returns the pending tab bar width relative to the frame window width (0.0 ... 1.0). */
+ double GetPendingRelTabBarWidth() const { return mfPendingTabBarWidth;}
+
+ void DoResize( const Point& rOffset, const Size& rSize, bool bInner = false );
+ void RepeatResize( bool bUpdateFix = true );
+ void UpdateFixPos();
+ Point GetGridOffset() const;
+
+ bool IsDrawSelMode() const { return bDrawSelMode; }
+ void SetDrawSelMode(bool bNew) { bDrawSelMode = bNew; }
+
+ void SetDrawFuncPtr(FuPoor* pFuncPtr) { pDrawActual = pFuncPtr; }
+ void SetDrawFuncOldPtr(FuPoor* pFuncPtr) { pDrawOld = pFuncPtr; }
+ FuPoor* GetDrawFuncPtr() { return pDrawActual; }
+ FuPoor* GetDrawFuncOldPtr() { return pDrawOld; }
+
+ void DrawDeselectAll();
+ void DrawMarkListHasChanged();
+ void UpdateAnchorHandles();
+
+ ScPageBreakData* GetPageBreakData() { return pPageBreakData.get(); }
+ const std::vector<ScHighlightEntry>& GetHighlightRanges() const { return maHighlightRanges; }
+
+ void UpdatePageBreakData( bool bForcePaint = false );
+
+ ScViewData& GetViewData() { return aViewData; }
+ const ScViewData& GetViewData() const { return aViewData; }
+
+ ScViewFunctionSet& GetFunctionSet() { return aFunctionSet; }
+ ScViewSelectionEngine* GetSelEngine() { return pSelEngine.get(); }
+
+ bool SelMouseButtonDown( const MouseEvent& rMEvt );
+
+ ScDrawView* GetScDrawView() { return pDrawView.get(); }
+
+ bool IsMinimized() const { return bMinimized; }
+
+ /**
+ * Called after moving, copying, inserting or deleting a sheet.
+ *
+ * @param bSameTabButMoved true if the same sheet as before is activated.
+ */
+ void TabChanged( bool bSameTabButMoved = false );
+ void SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll );
+ SC_DLLPUBLIC void RefreshZoom();
+ void SetPagebreakMode( bool bSet );
+
+ void UpdateLayerLocks();
+
+ void UpdateDrawTextOutliner();
+ void DigitLanguageChanged();
+
+ static void UpdateInputLine();
+
+ void InitRefMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScRefType eType );
+ void DoneRefMode( bool bContinue = false );
+ void UpdateRef( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ );
+ void StopRefMode();
+
+ void StopMarking();
+ void FakeButtonUp( ScSplitPos eWhich );
+
+ ScGridWindow* GetActiveWin();
+ vcl::Window* GetWindowByPos( ScSplitPos ePos ) const { return pGridWin[ePos]; }
+
+ ScSplitPos FindWindow( const vcl::Window* pWindow ) const;
+
+ void SetActivePointer( PointerStyle nPointer );
+
+ void ActiveGrabFocus();
+
+ void ClickCursor( SCCOL nPosX, SCROW nPosY, bool bControl );
+
+ SC_DLLPUBLIC void SetCursor( SCCOL nPosX, SCROW nPosY, bool bNew = false );
+
+ SC_DLLPUBLIC void CellContentChanged();
+ void SelectionChanged( bool bFromPaste = false );
+ void CursorPosChanged();
+ void UpdateInputContext();
+
+ void CheckSelectionTransfer();
+
+ void InvertHorizontal( ScVSplitPos eWhich, tools::Long nDragPos );
+ void InvertVertical( ScHSplitPos eWhich, tools::Long nDragPos );
+
+ Point GetInsertPos() const;
+
+ Point GetChartInsertPos( const Size& rSize, const ScRange& rCellRange );
+ Point GetChartDialogPos( const Size& rDialogSize, const tools::Rectangle& rLogicChart );
+
+ void UpdateAutoFillMark( bool bFromPaste = false );
+
+ void ShowCursor();
+ void HideAllCursors();
+ void ShowAllCursors();
+
+ void AlignToCursor( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
+ const ScSplitPos* pWhich = nullptr );
+
+ SvxZoomType GetZoomType() const;
+ void SetZoomType( SvxZoomType eNew, bool bAll );
+ sal_uInt16 CalcZoom( SvxZoomType eType, sal_uInt16 nOldZoom );
+
+ bool HasPageFieldDataAtCursor() const;
+ void StartDataSelect();
+
+ // MoveCursorAbs - absolute
+ // MoveCursorRel - single cells
+ // MoveCursorPage - screen
+ // MoveCursorArea - Data block
+ // MoveCursorEnd - top left / user range
+
+ SC_DLLPUBLIC void MoveCursorAbs( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
+ bool bShift, bool bControl,
+ bool bKeepOld = false, bool bKeepSel = false );
+ void MoveCursorRel( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ bool bShift, bool bKeepSel = false );
+ void MoveCursorPage( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ bool bShift, bool bKeepSel = false );
+ void MoveCursorArea( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ bool bShift, bool bKeepSel = false,
+ bool bInteractiveByUser = false );
+ void MoveCursorEnd( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ bool bShift, bool bKeepSel = false );
+ void MoveCursorScreen( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift );
+
+ void MoveCursorEnter( bool bShift ); // Shift for direction (select nothing)
+
+ bool MoveCursorKeyInput( const KeyEvent& rKeyEvent );
+
+ void FindNextUnprot( bool bShift, bool bInSelection );
+
+ void GetPageMoveEndPosition(SCCOL nMovX, SCROW nMovY, SCCOL& rPageX, SCROW& rPageY);
+
+ SC_DLLPUBLIC void SetTabNo( SCTAB nTab, bool bNew = false, bool bExtendSelection = false, bool bSameTabButMoved = false );
+ void SelectNextTab( short nDir, bool bExtendSelection );
+ void SelectTabPage( const sal_uInt16 nTab );
+
+ void ActivateView( bool bActivate, bool bFirst );
+ void ActivatePart( ScSplitPos eWhich );
+ bool IsInActivatePart() const { return bInActivatePart; }
+
+ void SetTimer( ScGridWindow* pWin, const MouseEvent& rMEvt );
+ void ResetTimer();
+
+ void ScrollX( tools::Long nDeltaX, ScHSplitPos eWhich, bool bUpdBars = true );
+ void ScrollY( tools::Long nDeltaY, ScVSplitPos eWhich, bool bUpdBars = true );
+ SC_DLLPUBLIC void ScrollLines( tools::Long nDeltaX, tools::Long nDeltaY ); // active
+
+ bool ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos );
+ bool GestureZoomCommand(const CommandEvent& rCEvt);
+
+ void ScrollToObject( const SdrObject* pDrawObj );
+ void MakeVisible( const tools::Rectangle& rHMMRect );
+
+ // Drawing
+
+ void PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScUpdateMode eMode = ScUpdateMode::All );
+
+ void PaintGrid();
+
+ void PaintTopArea( SCCOL nStartCol, SCCOL nEndCol );
+ void PaintTop();
+
+ void PaintLeftArea( SCROW nStartRow, SCROW nEndRow );
+ void PaintLeft();
+
+ bool PaintExtras();
+
+ void RecalcPPT();
+
+ void CreateAnchorHandles(SdrHdlList& rHdl, const ScAddress& rAddress);
+
+ void UpdateCopySourceOverlay();
+ void UpdateSelectionOverlay();
+ void UpdateShrinkOverlay();
+ void UpdateAllOverlays();
+
+ void UpdateFormulas( SCCOL nStartCol = -1, SCROW nStartRow = -1, SCCOL nEndCol = -1, SCROW nEndRow = -1 );
+ void InterpretVisible();
+ void CheckNeedsRepaint();
+ bool NeedsRepaint();
+
+ void PaintRangeFinder( tools::Long nNumber );
+ void AddHighlightRange( const ScRange& rRange, const Color& rColor );
+ void ClearHighlightRanges();
+
+ void DoChartSelection( const css::uno::Sequence< css::chart2::data::HighlightedRange > & rHilightRanges );
+ void DoDPFieldPopup(std::u16string_view rPivotTableName, sal_Int32 nDimensionIndex, Point aPoint, Size aSize);
+
+ tools::Long GetGridWidth( ScHSplitPos eWhich );
+ tools::Long GetGridHeight( ScVSplitPos eWhich );
+
+ void UpdateScrollBars( HeaderType eHeaderType = BOTH_HEADERS );
+ void SetNewVisArea();
+ void SetTabProtectionSymbol( SCTAB nTab, const bool bProtect ); // for protection icon of a tab on tabbar
+
+ void InvalidateAttribs();
+
+ void OnLibreOfficeKitTabChanged();
+ void AddWindowToForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich);
+ void RemoveWindowFromForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich);
+ void MakeEditView( ScEditEngineDefaulter* pEngine, SCCOL nCol, SCROW nRow );
+ void KillEditView( bool bNoPaint );
+ void UpdateEditView();
+
+ // Blocks
+
+ void SelectAll( bool bContinue = false );
+ void SelectAllTables();
+ void DeselectAllTables();
+
+ void MarkCursor( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bCols = false, bool bRows = false, bool bCellSelection = false );
+ void InitBlockMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bTestNeg = false,
+ bool bCols = false, bool bRows = false, bool bForceNeg = false );
+ void InitOwnBlockMode( const ScRange& rMarkRange );
+ void DoneBlockMode( bool bContinue = false );
+ void InitBlockModeHighlight( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, bool bCols, bool bRows );
+ void DoneBlockModeHighlight( bool bContinue );
+
+ bool IsBlockMode() const;
+
+ void ExpandBlock(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode);
+ void ExpandBlockPage(SCCOL nMovX, SCROW nMovY);
+ void ExpandBlockArea(SCCOL nMovX, SCROW nMovY);
+
+ void MarkColumns();
+ void MarkRows();
+
+ /**
+ * Called to select the specified full column.
+ *
+ * @param nCol: Column number to do operation on
+ * @param nModifier: 0, KEY_SHIFT, KEY_MOD1, KEY_SHIFT | KEY_MOD1
+ */
+
+ void MarkColumns(SCCOL nCol, sal_Int16 nModifier);
+ /**
+ * Called to select the specified full row.
+ *
+ * @param nRow: Row number to do operation on
+ * @param nModifier: 0, KEY_SHIFT, KEY_MOD1, KEY_SHIFT | KEY_MOD1
+ */
+ void MarkRows(SCROW nRow, sal_Int16 nModifier);
+ void HighlightOverlay();
+
+ void MarkDataArea( bool bIncludeCursor = true );
+ void MarkMatrixFormula();
+ void Unmark();
+
+ void MarkRange( const ScRange& rRange, bool bSetCursor = true, bool bContinue = false );
+
+ bool IsMarking( SCCOL nCol, SCROW nRow, SCTAB nTab ) const;
+
+ void PaintMarks( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow );
+ void PaintBlock( bool bReset );
+
+ void SetMarkData( const ScMarkData& rNew );
+ void MarkDataChanged();
+
+ void LockModifiers( sal_uInt16 nModifiers );
+ sal_uInt16 GetLockedModifiers() const;
+ void ViewOptionsHasChanged( bool bHScrollChanged,
+ bool bGraphicsChanged);
+
+ Point GetMousePosPixel();
+
+ void FreezeSplitters( bool bFreeze, SplitMethod eSplitMethod = SC_SPLIT_METHOD_CURSOR, SCCOLROW nFreezeIndex = -1 );
+ void RemoveSplit();
+ void SplitAtCursor();
+ void SplitAtPixel( const Point& rPixel );
+ void InvalidateSplit();
+
+ void ErrorMessage(TranslateId pGlobStrId);
+
+ void EnableRefInput(bool bFlag);
+
+ vcl::Window* GetFrameWin() const { return pFrameWin; }
+
+ bool HasPaintBrush() const { return pBrushDocument || pDrawBrushSet; }
+ ScDocument* GetBrushDocument() const { return pBrushDocument.get(); }
+ SfxItemSet* GetDrawBrushSet() const { return pDrawBrushSet.get(); }
+ bool IsPaintBrushLocked() const { return bLockPaintBrush; }
+ void SetBrushDocument( ScDocumentUniquePtr pNew, bool bLock );
+ void SetDrawBrushSet( std::unique_ptr<SfxItemSet> pNew, bool bLock );
+ void ResetBrushDocument();
+
+ void EnableAutoSpell( bool bEnable );
+ void ResetAutoSpell();
+ void ResetAutoSpellForContentChange();
+ void SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges );
+ /// @see ScModelObj::getRowColumnHeaders().
+ void getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter);
+ /// @see ScModelObj::getSheetGeometryData()
+ OString getSheetGeometryData(bool bColumns, bool bRows, bool bSizes, bool bHidden,
+ bool bFiltered, bool bGroups);
+ void extendTiledAreaIfNeeded();
+
+ static void OnLOKNoteStateChanged(const ScPostIt* pNote);
+
+ SCROW GetLOKStartHeaderRow() const { return mnLOKStartHeaderRow; }
+ SCROW GetLOKEndHeaderRow() const { return mnLOKEndHeaderRow; }
+ SCCOL GetLOKStartHeaderCol() const { return mnLOKStartHeaderCol; }
+ SCCOL GetLOKEndHeaderCol() const { return mnLOKEndHeaderCol; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabvwsh.hxx b/sc/source/ui/inc/tabvwsh.hxx
new file mode 100644
index 0000000000..b537af6900
--- /dev/null
+++ b/sc/source/ui/inc/tabvwsh.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <formula/errorcodes.hxx>
+#include <formula/opcode.hxx>
+#include <svx/fmshell.hxx>
+#include <sfx2/viewsh.hxx>
+#include <editeng/svxenum.hxx>
+#include <o3tl/deleter.hxx>
+#include <scdllapi.h>
+#include "dbfunc.hxx"
+#include "target.hxx"
+#include <shellids.hxx>
+#include <tabprotection.hxx>
+#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp>
+#include <dragdata.hxx>
+
+#include <memory>
+#include <map>
+
+class SdrOle2Obj;
+class SfxBindings;
+class SfxChildWindow;
+class SvxNumberInfoItem;
+struct SfxChildWinInfo;
+
+class ScArea;
+class ScAuditingShell;
+class ScDrawShell;
+class ScDrawTextObjectBar;
+class ScEditShell;
+class ScInputHandler;
+class ScPivotShell;
+class ScDrawFormShell;
+class ScCellShell;
+class ScOleObjectShell;
+class ScGraphicShell;
+class ScMediaShell;
+class ScChartShell;
+class ScPageBreakShell;
+class ScDPObject;
+class ScNavigatorSettings;
+class ScRangeName;
+class ScDrawTransferObj;
+namespace sc { class SparklineShell; }
+
+struct ScHeaderFieldData;
+
+namespace editeng { class SvxBorderLine; }
+
+namespace com::sun::star::frame { class XDispatchProviderInterceptor; }
+
+namespace svx {
+ class ExtrusionBar;
+ class FontworkBar;
+}
+
+enum ObjectSelectionType
+{
+ OST_NONE,
+ OST_Cell,
+ OST_Editing,
+ OST_DrawText,
+ OST_Drawing,
+ OST_DrawForm,
+ OST_Pivot,
+ OST_Auditing,
+ OST_OleObject,
+ OST_Chart,
+ OST_Graphic,
+ OST_Media,
+ OST_Sparkline,
+};
+
+class ScFormEditData;
+class SC_DLLPUBLIC ScTabViewShell : public SfxViewShell, public ScDBFunc
+{
+private:
+ struct SendFormulabarUpdate
+ {
+ OUString m_aText;
+ OUString m_aSelection;
+ sal_uInt64 m_nShellId;
+ std::chrono::steady_clock::time_point m_nTimeStamp;
+
+ SendFormulabarUpdate()
+ : m_nShellId(0)
+ {
+ }
+
+ void Send();
+ };
+
+ SendFormulabarUpdate maSendFormulabarUpdate;
+
+ ObjectSelectionType eCurOST;
+ sal_uInt16 nDrawSfxId;
+ SdrObjKind eFormObjKind;
+ OUString sDrawCustom; // current custom shape type
+ std::unique_ptr<ScDrawShell> pDrawShell;
+ std::unique_ptr<ScDrawTextObjectBar> pDrawTextShell;
+ std::unique_ptr<ScEditShell> pEditShell;
+ std::unique_ptr<ScPivotShell> pPivotShell;
+ std::unique_ptr<sc::SparklineShell> m_pSparklineShell;
+ std::unique_ptr<ScAuditingShell> pAuditingShell;
+ std::unique_ptr<ScDrawFormShell> pDrawFormShell;
+ std::unique_ptr<ScCellShell> pCellShell;
+ std::unique_ptr<ScOleObjectShell> pOleObjectShell;
+ std::unique_ptr<ScChartShell> pChartShell;
+ std::unique_ptr<ScGraphicShell> pGraphicShell;
+ std::unique_ptr<ScMediaShell> pMediaShell;
+ std::unique_ptr<ScPageBreakShell> pPageBreakShell;
+ std::unique_ptr<svx::ExtrusionBar> pExtrusionBarShell;
+ std::unique_ptr<svx::FontworkBar> pFontworkBarShell;
+
+ std::unique_ptr<FmFormShell> pFormShell;
+
+ std::unique_ptr<ScFormEditData> mpFormEditData;
+ std::unique_ptr<ScInputHandler, o3tl::default_delete<ScInputHandler>> mpInputHandler; // for OLE input cell
+
+ std::unique_ptr<::editeng::SvxBorderLine> pCurFrameLine;
+
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor >
+ xDisProvInterceptor;
+
+ Point aWinPos;
+
+ ScTabViewTarget aTarget;
+ std::unique_ptr<ScDPObject> pDialogDPObject;
+
+ std::unique_ptr<ScNavigatorSettings> pNavSettings;
+
+ // used in first Activate
+ bool bFirstActivate;
+
+ bool bActiveDrawSh;
+ bool bActiveDrawTextSh;
+ bool bActiveDrawFormSh;
+ bool bActiveOleObjectSh;
+ bool bActiveChartSh;
+ bool bActiveGraphicSh;
+ bool bActiveMediaSh;
+ bool bActiveEditSh;
+
+ bool bFormShellAtTop; // does the FormShell need to be on top?
+
+ bool bDontSwitch; // Don't turn off EditShell
+ bool bInFormatDialog; // for GetSelectionText
+
+ bool bReadOnly; // to detect status changes
+
+ bool bIsActive;
+
+ bool bForceFocusOnCurCell; // #i123629#
+
+ bool bInPrepareClose;
+ bool bInDispose;
+
+ sal_uInt16 nCurRefDlgId;
+
+ std::unique_ptr<SfxBroadcaster> pAccessibilityBroadcaster;
+
+ // ugly hack for Add button in ScNameDlg
+ std::map<OUString, ScRangeName> m_RangeMap;
+ bool mbInSwitch;
+ OUString maName;
+ OUString maScope;
+
+ std::unique_ptr<ScDragData> m_pDragData;
+private:
+ void Construct( TriState nForceDesignMode );
+
+ SfxShell* GetMySubShell() const;
+
+ void DoReadUserData( std::u16string_view rData );
+ void DoReadUserDataSequence( const css::uno::Sequence< css::beans::PropertyValue >& rSettings );
+ bool IsSignatureLineSelected();
+ bool IsSignatureLineSigned();
+ bool IsQRCodeSelected();
+
+ DECL_DLLPRIVATE_LINK( SimpleRefClose, const OUString*, void );
+ DECL_DLLPRIVATE_LINK( SimpleRefDone, const OUString&, void );
+ DECL_DLLPRIVATE_LINK( SimpleRefAborted, const OUString&, void );
+ DECL_DLLPRIVATE_LINK( SimpleRefChange, const OUString&, void );
+ DECL_DLLPRIVATE_LINK( FormControlActivated, LinkParamNone*, void );
+ DECL_DLLPRIVATE_LINK( DialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, void );
+
+protected:
+ virtual void Activate(bool bMDI) override;
+ virtual void Deactivate(bool bMDI) override;
+ virtual bool PrepareClose( bool bUI = true ) override;
+
+ virtual void ShowCursor(bool bOn) override;
+
+ virtual void Move() override; // notification
+
+ virtual void InnerResizePixel( const Point &rOfs, const Size &rSize, bool inplaceEditModeChange ) override; // new
+ virtual void OuterResizePixel( const Point &rOfs, const Size &rSize ) override;
+ virtual void SetZoomFactor( const Fraction &rZoomX, const Fraction &rZoomY ) override;
+
+ virtual void QueryObjAreaPixel( tools::Rectangle& rRect ) const override;
+
+ virtual OUString GetSelectionText( bool bWholeWord = false, bool bOnlyASample = false ) override;
+ virtual bool HasSelection( bool bText = true ) const override;
+
+ virtual void WriteUserData(OUString &, bool bBrowse = false) override;
+ virtual void ReadUserData(const OUString &, bool bBrowse = false) override;
+ virtual void WriteUserDataSequence (css::uno::Sequence < css::beans::PropertyValue >& ) override;
+ virtual void ReadUserDataSequence (const css::uno::Sequence < css::beans::PropertyValue >& ) override;
+
+ virtual void UIDeactivated( SfxInPlaceClient* pClient ) override;
+
+ virtual bool KeyInput( const KeyEvent &rKeyEvent ) override;
+ virtual SdrView* GetDrawView() const override;
+
+public:
+ SFX_DECL_INTERFACE(SCID_TABVIEW_SHELL)
+ SFX_DECL_VIEWFACTORY(ScTabViewShell);
+
+private:
+ /// SfxInterface initializer.
+ static void InitInterface_Impl();
+
+public:
+ /** -> Clone Method for Factory
+ Created from a general shell and inherit as much as possible */
+ ScTabViewShell(SfxViewFrame& rViewFrame, SfxViewShell* pOldSh);
+
+ virtual ~ScTabViewShell() override;
+
+ weld::Window* GetDialogParent();
+
+ bool IsRefInputMode() const;
+ void ExecuteInputDirect();
+
+ const ScInputHandler* GetInputHandler() const { return mpInputHandler.get(); }
+ ScInputHandler* GetInputHandler() { return mpInputHandler.get(); }
+ const OUString* GetEditString() const;
+ void UpdateInputHandler( bool bForce = false, bool bStopEditing = true );
+ void UpdateInputHandlerCellAdjust( SvxCellHorJustify eJust );
+ bool TabKeyInput(const KeyEvent& rKEvt);
+ bool SfxKeyInput(const KeyEvent& rKEvt);
+
+ void SetActive();
+
+ ::editeng::SvxBorderLine* GetDefaultFrameLine() const { return pCurFrameLine.get(); }
+ void SetDefaultFrameLine(const ::editeng::SvxBorderLine* pLine );
+
+ void Execute( SfxRequest& rReq );
+ void GetState( SfxItemSet& rSet );
+
+ void ExecuteTable( SfxRequest& rReq );
+ void GetStateTable( SfxItemSet& rSet );
+
+ void WindowChanged();
+ void ExecDraw(SfxRequest&);
+ void ExecDrawIns(SfxRequest& rReq);
+ void GetDrawState(SfxItemSet &rSet);
+ void GetDrawInsState(SfxItemSet &rSet);
+ void ExecGallery(const SfxRequest& rReq);
+
+ void ExecChildWin(const SfxRequest& rReq);
+
+ void ExecImageMap( SfxRequest& rReq );
+ void GetImageMapState( SfxItemSet& rSet );
+
+ void ExecuteSave( SfxRequest& rReq );
+ void GetSaveState( SfxItemSet& rSet );
+ void ExecSearch( SfxRequest& rReq );
+
+ void ExecuteUndo(SfxRequest& rReq);
+ void GetUndoState(SfxItemSet &rSet);
+
+ void ExecuteObject(const SfxRequest& rReq);
+ void GetObjectState(SfxItemSet &rSet);
+
+ void ExecDrawOpt(const SfxRequest& rReq);
+ void GetDrawOptState(SfxItemSet &rSet);
+
+ void ExecStyle(SfxRequest& rReq);
+ void GetStyleState(SfxItemSet &rSet);
+
+ void UpdateDrawShell();
+ void SetDrawShell( bool bActive );
+ void SetDrawTextShell( bool bActive );
+
+ void SetPivotShell( bool bActive );
+ void SetSparklineShell(bool bActive);
+ void SetDialogDPObject( std::unique_ptr<ScDPObject> pObj );
+ const ScDPObject* GetDialogDPObject() const { return pDialogDPObject.get(); }
+
+ void SetDontSwitch(bool bFlag){bDontSwitch=bFlag;}
+
+ void SetAuditShell( bool bActive );
+ void SetDrawFormShell( bool bActive );
+ void SetEditShell(EditView* pView, bool bActive );
+ void SetOleObjectShell( bool bActive );
+ void SetChartShell( bool bActive );
+ void SetGraphicShell( bool bActive );
+ void SetMediaShell( bool bActive );
+
+ void SetDrawShellOrSub();
+ void SetCurSubShell( ObjectSelectionType eOST, bool bForce = false );
+
+ void SetFormShellAtTop( bool bSet );
+
+ ObjectSelectionType GetCurObjectSelectionType() const { return eCurOST; }
+
+ virtual ErrCode DoVerb(sal_Int32 nVerb) override;
+
+ void StopEditShell();
+ bool IsDrawTextShell() const;
+ bool IsAuditShell() const;
+
+ void SetDrawTextUndo( SfxUndoManager* pUndoMgr );
+
+ void FillFieldData( ScHeaderFieldData& rData );
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ScNavigatorSettings* GetNavigatorSettings();
+
+ // Drucken:
+ virtual SfxPrinter* GetPrinter( bool bCreate = false ) override;
+ virtual sal_uInt16 SetPrinter( SfxPrinter* pNewPrinter,
+ SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL ) override;
+
+ virtual bool HasPrintOptionsPage() const override;
+ virtual std::unique_ptr<SfxTabPage> CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions) override;
+
+ void ConnectObject( const SdrOle2Obj* pObj );
+ void ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb);
+
+ void DeactivateOle();
+
+ static ScTabViewShell* GetActiveViewShell();
+
+ std::shared_ptr<SfxModelessDialogController> CreateRefDialogController(SfxBindings* pB, SfxChildWindow* pCW,
+ const SfxChildWinInfo* pInfo,
+ weld::Window* pParent, sal_uInt16 nSlotId);
+
+ void UpdateOleZoom();
+
+ virtual const FmFormShell* GetFormShell() const override { return pFormShell.get(); }
+ virtual FmFormShell* GetFormShell() override { return pFormShell.get(); }
+
+ void InsertURL( const OUString& rName, const OUString& rURL, const OUString& rTarget,
+ sal_uInt16 nMode );
+ void InsertURLButton( const OUString& rName, const OUString& rURL, const OUString& rTarget,
+ const Point* pInsPos );
+ void InsertURLField( const OUString& rName, const OUString& rURL, const OUString& rTarget );
+
+ bool SelectObject( std::u16string_view rName );
+
+ void SetInFormatDialog(bool bFlag) {bInFormatDialog=bFlag;}
+
+ void ForceMove() { Move(); }
+
+ static std::unique_ptr<SvxNumberInfoItem> MakeNumberInfoItem( ScDocument& rDoc, const ScViewData& rViewData );
+
+ static void UpdateNumberFormatter( const SvxNumberInfoItem& rInfoItem );
+
+ void ExecuteCellFormatDlg( SfxRequest& rReq, const OUString &rTabPage);
+
+ bool GetFunction( OUString& rFuncStr, FormulaError nErrCode );
+
+ void StartSimpleRefDialog( const OUString& rTitle, const OUString& rInitVal,
+ bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection );
+ void StopSimpleRefDialog();
+
+ void SetCurRefDlgId( sal_uInt16 nNew );
+
+ void AddAccessibilityObject( SfxListener& rObject );
+ void RemoveAccessibilityObject( SfxListener& rObject );
+ void BroadcastAccessibility( const SfxHint &rHint );
+ bool HasAccessibilityObjects() const;
+
+ bool ExecuteRetypePassDlg(ScPasswordHash eDesiredHash);
+
+ using ScTabView::ShowCursor;
+
+ bool IsActive() const { return bIsActive; }
+ OUString GetFormula(const ScAddress& rAddress);
+ bool UseSubTotal(ScRangeList* pRangeList);
+ OUString DoAutoSum(bool& rRangeFinder, bool& rSubTotal, const OpCode eCode);
+
+ // ugly hack to call Define Names from Manage Names
+ void SwitchBetweenRefDialogs(SfxModelessDialogController* pDialog);
+ // #i123629#
+ bool GetForceFocusOnCurCell() const { return bForceFocusOnCurCell; }
+ void SetForceFocusOnCurCell(bool bFlag) { bForceFocusOnCurCell=bFlag; }
+ /// See SfxViewShell::getPart().
+ int getPart() const override;
+ /// See SfxViewShell::afterCallbackRegistered().
+ void afterCallbackRegistered() override;
+ /// See SfxViewShell::NotifyCursor().
+ void NotifyCursor(SfxViewShell* pViewShell) const override;
+ /// See SfxViewShell::GetColorConfigColor().
+ ::Color GetColorConfigColor(svtools::ColorConfigEntry nColorType) const override;
+ /// Emits a LOK_CALLBACK_INVALIDATE_HEADER for all views whose current tab is equal to nCurrentTabIndex
+ static void notifyAllViewsHeaderInvalidation(const SfxViewShell* pForViewShell, HeaderType eHeaderType, SCTAB nCurrentTabIndex);
+ static bool isAnyEditViewInRange(const SfxViewShell* pForViewShell, bool bColumns, SCCOLROW nStart, SCCOLROW nEnd);
+ /// Emits a LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY for all views whose current tab
+ /// is equal to nCurrentTabIndex
+ static void notifyAllViewsSheetGeomInvalidation(const SfxViewShell* pForViewShell, bool bColumns, bool bRows, bool bSizes,
+ bool bHidden, bool bFiltered, bool bGroups, SCTAB nCurrentTabIndex);
+ void LOKSendFormulabarUpdate(EditView* pEditView, const OUString& rText, const ESelection& rSelection);
+ css::uno::Reference<css::drawing::XShapes> getSelectedXShapes();
+ static css::uno::Reference<css::datatransfer::XTransferable2> GetClipData(vcl::Window* pWin);
+
+ void InitFormEditData();
+ void ClearFormEditData();
+ ScFormEditData* GetFormEditData() { return mpFormEditData.get(); }
+
+ virtual tools::Rectangle getLOKVisibleArea() const override;
+
+ const ScDragData& GetDragData() const { return *m_pDragData; }
+ void SetDragObject(ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj);
+ void ResetDragObject();
+ void SetDragLink(const OUString& rDoc, const OUString& rTab, const OUString& rArea);
+ void SetDragJump(ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/target.hxx b/sc/source/ui/inc/target.hxx
new file mode 100644
index 0000000000..b7c5c78e24
--- /dev/null
+++ b/sc/source/ui/inc/target.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 <svl/undo.hxx>
+
+class ScTabViewShell;
+
+class ScTabViewTarget : public SfxRepeatTarget
+{
+private:
+ ScTabViewShell* pViewShell;
+
+public:
+
+ ScTabViewTarget( ScTabViewShell* pShell ) : pViewShell( pShell ) {}
+ virtual ~ScTabViewTarget() override;
+
+ ScTabViewShell* GetViewShell() const { return pViewShell; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tbzoomsliderctrl.hxx b/sc/source/ui/inc/tbzoomsliderctrl.hxx
new file mode 100644
index 0000000000..93a3716f7a
--- /dev/null
+++ b/sc/source/ui/inc/tbzoomsliderctrl.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vcl/customweld.hxx>
+#include <vcl/image.hxx>
+#include <vcl/window.hxx>
+#include <svl/poolitem.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/tbxctrl.hxx>
+
+namespace com::sun::star::frame { class XDispatchProvider; }
+
+class SvxZoomSliderItem;
+
+class ScZoomSliderControl : public SfxToolBoxControl
+{
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+ ScZoomSliderControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx );
+ virtual ~ScZoomSliderControl() override;
+
+ virtual void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;
+ virtual VclPtr<InterimItemWindow> CreateItemWindow( vcl::Window *pParent ) override;
+};
+
+class ScZoomSlider final : public weld::CustomWidgetController
+{
+private:
+ sal_uInt16 mnCurrentZoom;
+ sal_uInt16 mnMinZoom;
+ sal_uInt16 mnMaxZoom;
+ std::vector< tools::Long > maSnappingPointOffsets;
+ std::vector< sal_uInt16 > maSnappingPointZooms;
+ Image maSliderButton;
+ Image maIncreaseButton;
+ Image maDecreaseButton;
+ bool mbOmitPaint;
+ css::uno::Reference<css::frame::XDispatchProvider> m_xDispatchProvider;
+
+ sal_uInt16 Offset2Zoom(tools::Long nOffset) const;
+ tools::Long Zoom2Offset(sal_uInt16 nZoom) const;
+
+ void DoPaint(vcl::RenderContext& rRenderContext);
+public:
+ ScZoomSlider(css::uno::Reference<css::frame::XDispatchProvider> xDispatchProvider,
+ sal_uInt16 nCurrentZoom);
+
+ void UpdateFromItem(const SvxZoomSliderItem* pZoomSliderItem);
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+};
+
+class ScZoomSliderWnd final : public InterimItemWindow
+{
+private:
+ std::unique_ptr<ScZoomSlider> mxWidget;
+ std::unique_ptr<weld::CustomWeld> mxWeld;
+
+public:
+ ScZoomSliderWnd(vcl::Window* pParent, const css::uno::Reference<css::frame::XDispatchProvider>& rDispatchProvider,
+ sal_uInt16 nCurrentZoom);
+ virtual ~ScZoomSliderWnd() override;
+ virtual void dispose() override;
+ void UpdateFromItem( const SvxZoomSliderItem* pZoomSliderItem );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/textdlgs.hxx b/sc/source/ui/inc/textdlgs.hxx
new file mode 100644
index 0000000000..3d01bdc434
--- /dev/null
+++ b/sc/source/ui/inc/textdlgs.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 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class SfxObjectShell;
+
+class ScCharDlg : public SfxTabDialogController
+{
+private:
+ const SfxObjectShell& m_rDocShell;
+ bool m_bDrawText;
+
+ virtual void PageCreated(const OUString& rId, SfxTabPage& rPage) override;
+
+public:
+ ScCharDlg(weld::Window* pParent, const SfxItemSet* pAttr, const SfxObjectShell* pDocShell,
+ bool bDrawText);
+};
+
+class ScParagraphDlg : public SfxTabDialogController
+{
+private:
+ virtual void PageCreated(const OUString& rId, SfxTabPage& rPage) override;
+
+public:
+ ScParagraphDlg(weld::Window* pParent, const SfxItemSet* pAttr);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/textimportoptions.hxx b/sc/source/ui/inc/textimportoptions.hxx
new file mode 100644
index 0000000000..7a2cfb6df5
--- /dev/null
+++ b/sc/source/ui/inc/textimportoptions.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <i18nlangtag/lang.h>
+
+class SvxLanguageBox;
+
+class ScTextImportOptionsDlg : public weld::GenericDialogController
+{
+public:
+ ScTextImportOptionsDlg(weld::Window* pParent);
+ virtual ~ScTextImportOptionsDlg() override;
+
+ LanguageType getLanguageType() const;
+ bool isDateConversionSet() const;
+ bool isScientificConversionSet() const;
+ bool isKeepAskingSet() const;
+
+private:
+ void init();
+
+private:
+ std::unique_ptr<weld::Button> m_xBtnOk;
+ std::unique_ptr<weld::RadioButton> m_xRbAutomatic;
+ std::unique_ptr<weld::RadioButton> m_xRbCustom;
+ std::unique_ptr<weld::CheckButton> m_xCkbConvertDate;
+ std::unique_ptr<weld::CheckButton> m_xCkbConvertScientific;
+ std::unique_ptr<weld::CheckButton> m_xCkbKeepAsking;
+ std::unique_ptr<SvxLanguageBox> m_xLbCustomLang;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+ DECL_LINK(RadioCheckHdl, weld::Toggleable&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpcalc.hxx b/sc/source/ui/inc/tpcalc.hxx
new file mode 100644
index 0000000000..87b4f90368
--- /dev/null
+++ b/sc/source/ui/inc/tpcalc.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 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+#include "editfield.hxx"
+
+class ScDocOptions;
+
+class ScTpCalcOptions : public SfxTabPage
+{
+public:
+ ScTpCalcOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet);
+ virtual ~ScTpCalcOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rCoreSet ) override;
+ virtual void Reset ( const SfxItemSet* rCoreSet ) override;
+ virtual DeactivateRC DeactivatePage ( SfxItemSet* pSet ) override;
+
+private:
+ std::unique_ptr<ScDocOptions> pOldOptions;
+ std::unique_ptr<ScDocOptions> pLocalOptions;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnIterate;
+ std::unique_ptr<weld::Label> m_xFtSteps;
+ std::unique_ptr<weld::SpinButton> m_xEdSteps;
+ std::unique_ptr<weld::Label> m_xFtEps;
+ std::unique_ptr<ScDoubleField> m_xEdEps;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnDateStd;
+ std::unique_ptr<weld::RadioButton> m_xBtnDateSc10;
+ std::unique_ptr<weld::RadioButton> m_xBtnDate1904;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnCase;
+ std::unique_ptr<weld::CheckButton> m_xBtnCalc;
+ std::unique_ptr<weld::CheckButton> m_xBtnMatch;
+ std::unique_ptr<weld::RadioButton> m_xBtnWildcards;
+ std::unique_ptr<weld::RadioButton> m_xBtnRegex;
+ std::unique_ptr<weld::RadioButton> m_xBtnLiteral;
+ std::unique_ptr<weld::CheckButton> m_xBtnLookUp;
+ std::unique_ptr<weld::CheckButton> m_xBtnGeneralPrec;
+
+ std::unique_ptr<weld::Label> m_xFtPrec;
+ std::unique_ptr<weld::SpinButton> m_xEdPrec;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnThread;
+
+private:
+ void Init();
+
+ // Handler:
+ DECL_LINK( RadioClickHdl, weld::Toggleable&, void );
+ DECL_LINK( CheckClickHdl, weld::Toggleable&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpcompatibility.hxx b/sc/source/ui/inc/tpcompatibility.hxx
new file mode 100644
index 0000000000..4efdbc172b
--- /dev/null
+++ b/sc/source/ui/inc/tpcompatibility.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/.
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ScTpCompatOptions : public SfxTabPage
+{
+public:
+ explicit ScTpCompatOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreAttrs);
+ virtual ~ScTpCompatOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet(SfxItemSet* rCoreAttrs) override;
+ virtual void Reset(const SfxItemSet* rCoreAttrs) override;
+ virtual DeactivateRC DeactivatePage(SfxItemSet* pSet ) override;
+
+private:
+ std::unique_ptr<weld::ComboBox> m_xLbKeyBindings;
+ std::unique_ptr<weld::CheckButton> m_xBtnLink;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpdefaults.hxx b/sc/source/ui/inc/tpdefaults.hxx
new file mode 100644
index 0000000000..d3760948b6
--- /dev/null
+++ b/sc/source/ui/inc/tpdefaults.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/.
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ScTpDefaultsOptions : public SfxTabPage
+{
+public:
+ explicit ScTpDefaultsOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet);
+ virtual ~ScTpDefaultsOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet(SfxItemSet* rCoreSet) override;
+ virtual void Reset(const SfxItemSet* rCoreSet) override;
+ virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override;
+
+private:
+ void CheckNumSheets();
+ void CheckPrefix();
+ void OnFocusPrefixInput();
+
+ DECL_LINK( NumModifiedHdl, weld::Entry&, void );
+ DECL_LINK( PrefixModifiedHdl, weld::Entry&, void );
+ DECL_LINK( PrefixEditOnFocusHdl, weld::Widget&, void );
+
+private:
+ // Stores old Sheet Prefix
+ OUString maOldPrefixValue;
+
+ std::unique_ptr<weld::SpinButton> m_xEdNSheets;
+ std::unique_ptr<weld::Widget> m_xEdNSheetsImg;
+ std::unique_ptr<weld::Entry> m_xEdSheetPrefix;
+ std::unique_ptr<weld::Widget> m_xEdSheetPrefixImg;
+ std::unique_ptr<weld::CheckButton> m_xEdJumboSheets;
+ std::unique_ptr<weld::Widget> m_xEdJumboSheetsImg;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpformula.hxx b/sc/source/ui/inc/tpformula.hxx
new file mode 100644
index 0000000000..14a72b7511
--- /dev/null
+++ b/sc/source/ui/inc/tpformula.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+#include <calcconfig.hxx>
+#include <docoptio.hxx>
+
+class ScTpFormulaOptions : public SfxTabPage
+{
+public:
+ explicit ScTpFormulaOptions(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rCoreSet);
+ static std::unique_ptr<SfxTabPage>
+ Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet);
+ virtual ~ScTpFormulaOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet(SfxItemSet* rCoreSet) override;
+ virtual void Reset(const SfxItemSet* rCoreSet) override;
+ virtual DeactivateRC DeactivatePage(SfxItemSet* pSet) override;
+
+private:
+ void ResetSeparators();
+ void OnFocusSeparatorInput(weld::Entry* pEdit);
+ void UpdateCustomCalcRadioButtons(bool bDefault);
+ void LaunchCustomCalcSettings();
+
+ bool IsValidSeparator(std::u16string_view aSep, bool bArray) const;
+
+ DECL_LINK(ButtonHdl, weld::Button&, void);
+ DECL_LINK(ToggleHdl, weld::Toggleable&, void);
+ DECL_LINK(SepInsertTextHdl, OUString&, bool);
+ DECL_LINK(ColSepInsertTextHdl, OUString&, bool);
+ DECL_LINK(RowSepInsertTextHdl, OUString&, bool);
+ DECL_LINK(SepModifyHdl, weld::Entry&, void);
+ DECL_LINK(SepEditOnFocusHdl, weld::Widget&, void);
+
+private:
+ /** Stores old separator value of currently focused separator edit box.
+ This value is used to revert undesired value change. */
+ OUString maOldSepValue;
+
+ ScCalcConfig maSavedConfig;
+ ScCalcConfig maCurrentConfig;
+
+ ScDocOptions maSavedDocOptions;
+ ScDocOptions maCurrentDocOptions;
+
+ sal_Unicode mnDecSep;
+
+ std::unique_ptr<weld::ComboBox> mxLbFormulaSyntax;
+ std::unique_ptr<weld::CheckButton> mxCbEnglishFuncName;
+
+ std::unique_ptr<weld::RadioButton> mxBtnCustomCalcDefault;
+ std::unique_ptr<weld::RadioButton> mxBtnCustomCalcCustom;
+ std::unique_ptr<weld::Button> mxBtnCustomCalcDetails;
+
+ std::unique_ptr<weld::Entry> mxEdSepFuncArg;
+ std::unique_ptr<weld::Entry> mxEdSepArrayCol;
+ std::unique_ptr<weld::Entry> mxEdSepArrayRow;
+ std::unique_ptr<weld::Button> mxBtnSepReset;
+
+ std::unique_ptr<weld::ComboBox> mxLbOOXMLRecalcOptions;
+ std::unique_ptr<weld::ComboBox> mxLbODFRecalcOptions;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tphf.hxx b/sc/source/ui/inc/tphf.hxx
new file mode 100644
index 0000000000..9af97f6d70
--- /dev/null
+++ b/sc/source/ui/inc/tphf.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 <svx/hdft.hxx>
+
+class ScStyleDlg;
+
+class ScHFPage : public SvxHFPage
+{
+public:
+ virtual ~ScHFPage() override;
+
+ virtual void Reset( const SfxItemSet* rSet ) override;
+ virtual bool FillItemSet( SfxItemSet* rOutSet ) override;
+
+ void SetPageStyle( const OUString& rName ) { aStrPageStyle = rName; }
+ void SetStyleDlg ( ScStyleDlg* pDlg ) { pStyleDlg = pDlg; }
+
+protected:
+ ScHFPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet, sal_uInt16 nSetId);
+
+ virtual void ActivatePage( const SfxItemSet& rSet ) override;
+ virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override;
+
+private:
+ SfxItemSet aDataSet;
+ OUString aStrPageStyle;
+ SvxPageUsage nPageUsage;
+ ScStyleDlg* pStyleDlg;
+ std::unique_ptr<weld::Button> m_xBtnEdit;
+
+ DECL_LINK(BtnHdl, weld::Button&, void);
+ DECL_LINK(TurnOnHdl, weld::Toggleable&, void);
+};
+
+class ScHeaderPage : public ScHFPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet );
+ ScHeaderPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet);
+ static WhichRangesContainer GetRanges();
+};
+
+class ScFooterPage : public ScHFPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet );
+ ScFooterPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet);
+ static WhichRangesContainer GetRanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tphfedit.hxx b/sc/source/ui/inc/tphfedit.hxx
new file mode 100644
index 0000000000..4247f2ad98
--- /dev/null
+++ b/sc/source/ui/inc/tphfedit.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <scdllapi.h>
+#include <editutil.hxx>
+#include <svx/weldeditview.hxx>
+#include <editeng/svxenum.hxx>
+#include <unotools/weakref.hxx>
+
+#include <functional>
+
+namespace com::sun::star::accessibility { class XAccessible; }
+
+class ScPatternAttr;
+class EditView;
+class EditTextObject;
+class SvxFieldItem;
+class ScAccessibleEditObject;
+
+enum ScEditWindowLocation
+{
+ Left,
+ Center,
+ Right
+};
+
+class SC_DLLPUBLIC ScEditWindow : public WeldEditView
+{
+public:
+ ScEditWindow(ScEditWindowLocation eLoc, weld::Window* pParent);
+ virtual void SetDrawingArea(weld::DrawingArea* pArea) override;
+ virtual ~ScEditWindow() override;
+
+ void SetFont( const ScPatternAttr& rPattern );
+ void SetText( const EditTextObject& rTextObject );
+ std::unique_ptr<EditTextObject> CreateTextObject();
+ void SetCharAttributes();
+
+ void InsertField( const SvxFieldItem& rFld );
+
+ void SetNumType(SvxNumType eNumType);
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
+
+ ScHeaderEditEngine* GetEditEngine() const override;
+ void SetObjectSelectHdl( const Link<ScEditWindow&,void>& aLink) { aObjectSelectLink = aLink; }
+ void SetGetFocusHdl(const std::function<void (ScEditWindow&)>& rLink) { m_GetFocusLink = rLink; }
+
+protected:
+ virtual void makeEditEngine() override;
+ virtual bool KeyInput( const KeyEvent& rKEvt ) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+private:
+ ScEditWindowLocation eLocation;
+ bool mbRTL;
+ weld::Window* mpDialog;
+
+ unotools::WeakReference<ScAccessibleEditObject> mxAcc;
+
+ Link<ScEditWindow&,void> aObjectSelectLink;
+ std::function<void (ScEditWindow&)> m_GetFocusLink;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpprint.hxx b/sc/source/ui/inc/tpprint.hxx
new file mode 100644
index 0000000000..3dc7d36e53
--- /dev/null
+++ b/sc/source/ui/inc/tpprint.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 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ScTpPrintOptions : public SfxTabPage
+{
+ std::unique_ptr<weld::CheckButton> m_xSkipEmptyPagesCB;
+ std::unique_ptr<weld::CheckButton> m_xSelectedSheetsCB;
+ std::unique_ptr<weld::CheckButton> m_xForceBreaksCB;
+
+public:
+ ScTpPrintOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet);
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet );
+ virtual ~ScTpPrintOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet( SfxItemSet* rCoreSet ) override;
+ virtual void Reset( const SfxItemSet* rCoreSet ) override;
+ virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpsort.hxx b/sc/source/ui/inc/tpsort.hxx
new file mode 100644
index 0000000000..1c9d27926b
--- /dev/null
+++ b/sc/source/ui/inc/tpsort.hxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <sfx2/tabdlg.hxx>
+#include <svtools/collatorres.hxx>
+#include <svx/langbox.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <vcl/idle.hxx>
+
+#include "sortkeydlg.hxx"
+
+#include <address.hxx>
+#include <sheetlimits.hxx>
+#include <sortparam.hxx>
+
+// +1 because one field is reserved for the "- undefined -" entry
+inline SCCOL SC_MAXFIELDS(const ScSheetLimits& rLimits) { return rLimits.GetMaxColCount() + 1; }
+
+class ScSortItem;
+class ScViewData;
+
+// Sort Criteria
+
+class ScTabPageSortFields : public SfxTabPage
+{
+public:
+ ScTabPageSortFields(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet);
+ virtual ~ScTabPageSortFields() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+
+protected:
+ virtual void ActivatePage ( const SfxItemSet& rSet ) override;
+ virtual DeactivateRC DeactivatePage ( SfxItemSet* pSet ) override;
+
+private:
+ Idle m_aIdle;
+
+ OUString aStrUndefined;
+ OUString aStrColumn;
+ OUString aStrRow;
+ OUString aStrRowLabel;
+ OUString aStrColLabel;
+
+ TypedWhichId<ScSortItem> nWhichSort;
+ ScViewData* pViewData;
+ ScSortParam aSortData;
+ std::vector<SCCOLROW> nFieldArr;
+ sal_uInt16 nFieldCount;
+ sal_uInt16 nSortKeyCount;
+
+ std::unique_ptr<weld::Container> m_xTop;
+ std::unique_ptr<weld::CheckButton> m_xBtnHeader;
+ std::unique_ptr<weld::RadioButton> m_xBtnTopDown;
+ std::unique_ptr<weld::RadioButton> m_xBtnLeftRight;
+ std::unique_ptr<weld::ScrolledWindow> m_xScrolledWindow;
+ std::unique_ptr<weld::Container> m_xBox;
+ ScSortKeyWindow m_aSortWin;
+
+ void AddSortKey( sal_uInt16 nItem );
+
+private:
+ void Init ();
+ void FillFieldLists ( sal_uInt16 nStartField );
+ sal_uInt16 GetFieldSelPos ( SCCOLROW nField );
+ void SetLastSortKey( sal_uInt16 nItem );
+
+ // Handler ------------------------
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(ScrollToEndHdl, Timer*, void);
+ DECL_LINK(SortDirHdl, weld::Toggleable&, void);
+
+};
+
+// Sort Options
+
+class ScDocument;
+
+class ScTabPageSortOptions : public SfxTabPage
+{
+public:
+ ScTabPageSortOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pArgSet);
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+
+protected:
+ virtual void ActivatePage ( const SfxItemSet& rSet ) override;
+ virtual DeactivateRC DeactivatePage ( SfxItemSet* pSet ) override;
+
+private:
+ OUString aStrUndefined;
+ OUString aStrCommentsRowLabel;
+ OUString aStrCommentsColLabel;
+ OUString aStrImgRowLabel;
+ OUString aStrImgColLabel;
+
+ TypedWhichId<ScSortItem> nWhichSort;
+ ScSortParam aSortData;
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+ ScAddress theOutPos;
+
+ std::unique_ptr<CollatorResource> m_xColRes;
+ std::optional<CollatorWrapper> m_oColWrap;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnCase;
+ std::unique_ptr<weld::CheckButton> m_xBtnFormats;
+ std::unique_ptr<weld::CheckButton> m_xBtnNaturalSort;
+ std::unique_ptr<weld::CheckButton> m_xBtnCopyResult;
+ std::unique_ptr<weld::ComboBox> m_xLbOutPos;
+ std::unique_ptr<weld::Entry> m_xEdOutPos;
+ std::unique_ptr<weld::CheckButton> m_xBtnSortUser;
+ std::unique_ptr<weld::ComboBox> m_xLbSortUser;
+ std::unique_ptr<SvxLanguageBox> m_xLbLanguage;
+ std::unique_ptr<weld::Label> m_xFtAlgorithm;
+ std::unique_ptr<weld::ComboBox> m_xLbAlgorithm;
+ std::unique_ptr<weld::CheckButton> m_xBtnIncComments;
+ std::unique_ptr<weld::CheckButton> m_xBtnIncImages;
+
+private:
+ void Init ();
+ void FillUserSortListBox ();
+
+ // Handler ------------------------
+ DECL_LINK( EnableHdl, weld::Toggleable&, void );
+ DECL_LINK( SelOutPosHdl, weld::ComboBox&, void );
+ void EdOutPosModHdl();
+ void FillAlgor();
+ DECL_LINK( FillAlgorHdl, weld::ComboBox&, void );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpstat.hxx b/sc/source/ui/inc/tpstat.hxx
new file mode 100644
index 0000000000..bb57d72127
--- /dev/null
+++ b/sc/source/ui/inc/tpstat.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 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+class ScDocStatPage: public SfxTabPage
+{
+public:
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet);
+ ScDocStatPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet);
+ virtual ~ScDocStatPage() override;
+
+protected:
+ virtual bool FillItemSet( SfxItemSet* rSet ) override;
+ virtual void Reset ( const SfxItemSet* rSet ) override;
+
+private:
+ std::unique_ptr<weld::Label> m_xFtTables;
+ std::unique_ptr<weld::Label> m_xFtCells;
+ std::unique_ptr<weld::Label> m_xFtPages;
+ std::unique_ptr<weld::Label> m_xFtFormula;
+ std::unique_ptr<weld::Frame> m_xFrame;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpsubt.hxx b/sc/source/ui/inc/tpsubt.hxx
new file mode 100644
index 0000000000..ecfa2ec185
--- /dev/null
+++ b/sc/source/ui/inc/tpsubt.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+#include <global.hxx>
+
+class ScViewData;
+class ScDocument;
+struct ScSubTotalParam;
+class ScSubTotalItem;
+
+class ScTpSubTotalGroup : public SfxTabPage
+{
+protected:
+ ScTpSubTotalGroup(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet, const sal_uInt16& nTabNumber);
+
+public:
+ virtual ~ScTpSubTotalGroup() override;
+
+ bool DoReset ( sal_uInt16 nGroupNo,
+ const SfxItemSet& rArgSet );
+ bool DoFillItemSet ( sal_uInt16 nGroupNo,
+ SfxItemSet& rArgSet );
+private:
+ void Init ();
+ void FillListBoxes ();
+ static ScSubTotalFunc LbPosToFunc ( sal_uInt16 nPos );
+ static sal_uInt16 FuncToLbPos ( ScSubTotalFunc eFunc );
+ sal_uInt16 GetFieldSelPos ( SCCOL nField );
+
+ // Handler ------------------------
+ DECL_LINK( SelectListBoxHdl, weld::ComboBox&, void );
+ DECL_LINK( SelectTreeListBoxHdl, weld::TreeView&, void );
+ DECL_LINK(CheckHdl, const weld::TreeView::iter_col&, void);
+ DECL_LINK(CheckBoxHdl, weld::Toggleable&, void);
+ void SelectHdl(const weld::Widget*);
+
+ const OUString aStrNone;
+ const OUString aStrColumn;
+
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+
+ const TypedWhichId<ScSubTotalItem> nWhichSubTotals;
+ const ScSubTotalParam& rSubTotalData;
+ std::vector<SCCOL> mnFieldArr;
+ sal_uInt16 nFieldCount;
+
+ std::unique_ptr<weld::ComboBox> mxLbGroup;
+ std::unique_ptr<weld::TreeView> mxLbColumns;
+ std::unique_ptr<weld::TreeView> mxLbFunctions;
+ std::unique_ptr<weld::CheckButton> mxLbSelectAllColumns;
+};
+
+class ScTpSubTotalGroup1 final : public ScTpSubTotalGroup
+{
+public:
+ ScTpSubTotalGroup1( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rArgSet );
+ static std::unique_ptr<SfxTabPage> Create ( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet );
+ virtual ~ScTpSubTotalGroup1() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+};
+
+class ScTpSubTotalGroup2 final : public ScTpSubTotalGroup
+{
+public:
+ ScTpSubTotalGroup2( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rArgSet );
+ static std::unique_ptr<SfxTabPage> Create ( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet );
+ virtual ~ScTpSubTotalGroup2() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+};
+
+class ScTpSubTotalGroup3 final : public ScTpSubTotalGroup
+{
+public:
+ ScTpSubTotalGroup3( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rArgSet );
+ static std::unique_ptr<SfxTabPage> Create ( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet );
+ virtual ~ScTpSubTotalGroup3() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+};
+
+class ScTpSubTotalOptions final : public SfxTabPage
+{
+public:
+ ScTpSubTotalOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create ( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rArgSet );
+ virtual ~ScTpSubTotalOptions() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+
+private:
+ void Init ();
+ void FillUserSortListBox ();
+
+ // Handler ------------------------
+ DECL_LINK(CheckHdl, weld::Toggleable&, void);
+
+ ScViewData* pViewData;
+ ScDocument* pDoc;
+ const TypedWhichId<ScSubTotalItem> nWhichSubTotals;
+ const ScSubTotalParam& rSubTotalData;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnPagebreak;
+ std::unique_ptr<weld::CheckButton> m_xBtnCase;
+ std::unique_ptr<weld::CheckButton> m_xBtnSort;
+ std::unique_ptr<weld::Label> m_xFlSort;
+ std::unique_ptr<weld::RadioButton> m_xBtnAscending;
+ std::unique_ptr<weld::RadioButton> m_xBtnDescending;
+ std::unique_ptr<weld::CheckButton> m_xBtnFormats;
+ std::unique_ptr<weld::CheckButton> m_xBtnUserDef;
+ std::unique_ptr<weld::ComboBox> m_xLbUserDef;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tptable.hxx b/sc/source/ui/inc/tptable.hxx
new file mode 100644
index 0000000000..e843f769cb
--- /dev/null
+++ b/sc/source/ui/inc/tptable.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 <sfx2/tabdlg.hxx>
+
+class ScTablePage : public SfxTabPage
+{
+ static const WhichRangesContainer pPageTableRanges;
+public:
+ ScTablePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet);
+ virtual ~ScTablePage() override;
+
+ static WhichRangesContainer GetRanges () { return pPageTableRanges; }
+ virtual bool FillItemSet ( SfxItemSet* rCoreSet ) override;
+ virtual void Reset ( const SfxItemSet* rCoreSet ) override;
+ virtual DeactivateRC DeactivatePage ( SfxItemSet* pSet ) override;
+
+private:
+ void ShowImage();
+
+private:
+ sal_uInt16 m_nOrigScalePageWidth;
+ sal_uInt16 m_nOrigScalePageHeight;
+
+ std::unique_ptr<weld::RadioButton> m_xBtnTopDown;
+ std::unique_ptr<weld::RadioButton> m_xBtnLeftRight;
+ std::unique_ptr<weld::Image> m_xBmpPageDir;
+ std::unique_ptr<weld::CheckButton> m_xBtnPageNo;
+ std::unique_ptr<weld::SpinButton> m_xEdPageNo;
+
+ std::unique_ptr<weld::CheckButton> m_xBtnHeaders;
+ std::unique_ptr<weld::CheckButton> m_xBtnGrid;
+ std::unique_ptr<weld::CheckButton> m_xBtnNotes;
+ std::unique_ptr<weld::CheckButton> m_xBtnObjects;
+ std::unique_ptr<weld::CheckButton> m_xBtnCharts;
+ std::unique_ptr<weld::CheckButton> m_xBtnDrawings;
+ std::unique_ptr<weld::CheckButton> m_xBtnFormulas;
+ std::unique_ptr<weld::CheckButton> m_xBtnNullVals;
+
+ std::unique_ptr<weld::ComboBox> m_xLbScaleMode;
+ std::unique_ptr<weld::Widget> m_xBxScaleAll;
+ std::unique_ptr<weld::MetricSpinButton> m_xEdScaleAll;
+ std::unique_ptr<weld::Widget> m_xGrHeightWidth;
+ std::unique_ptr<weld::SpinButton> m_xEdScalePageWidth;
+ std::unique_ptr<weld::CheckButton> m_xCbScalePageWidth;
+ std::unique_ptr<weld::SpinButton> m_xEdScalePageHeight;
+ std::unique_ptr<weld::CheckButton> m_xCbScalePageHeight;
+ std::unique_ptr<weld::Widget> m_xBxScalePageNum;
+ std::unique_ptr<weld::SpinButton> m_xEdScalePageNum;
+
+private:
+
+ // Handler:
+ DECL_LINK(PageDirHdl, weld::Toggleable&, void);
+ DECL_LINK(PageNoHdl, weld::Toggleable&, void);
+ void PageNoHdl(const weld::Toggleable* pBtn);
+ DECL_LINK(ScaleHdl, weld::ComboBox&, void);
+ DECL_LINK(ToggleHdl, weld::Toggleable&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpusrlst.hxx b/sc/source/ui/inc/tpusrlst.hxx
new file mode 100644
index 0000000000..96d75eca1e
--- /dev/null
+++ b/sc/source/ui/inc/tpusrlst.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 <sfx2/tabdlg.hxx>
+
+class ScUserList;
+class ScDocument;
+class ScViewData;
+class ScRefAddress;
+
+class ScTpUserLists : public SfxTabPage
+{
+public:
+ ScTpUserLists(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rAttrSet);
+ virtual ~ScTpUserLists() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rCoreAttrs ) override;
+ virtual void Reset ( const SfxItemSet* rCoreAttrs ) override;
+ virtual DeactivateRC DeactivatePage ( SfxItemSet* pSet ) override;
+
+private:
+ std::unique_ptr<weld::Label> mxFtLists;
+ std::unique_ptr<weld::TreeView> mxLbLists;
+ std::unique_ptr<weld::Label> mxFtEntries;
+ std::unique_ptr<weld::TextView> mxEdEntries;
+ std::unique_ptr<weld::Label> mxFtCopyFrom;
+ std::unique_ptr<weld::Entry> mxEdCopyFrom;
+ std::unique_ptr<weld::Button> mxBtnNew;
+ std::unique_ptr<weld::Button> mxBtnDiscard;
+ std::unique_ptr<weld::Button> mxBtnAdd;
+ std::unique_ptr<weld::Button> mxBtnModify;
+ std::unique_ptr<weld::Button> mxBtnRemove;
+ std::unique_ptr<weld::Button> mxBtnCopy;
+
+ const OUString aStrQueryRemove;
+ const OUString aStrCopyList;
+ const OUString aStrCopyFrom;
+ const OUString aStrCopyErr;
+
+ const sal_uInt16 nWhichUserLists;
+ std::unique_ptr<ScUserList> pUserLists;
+
+ ScDocument* pDoc;
+ ScViewData* pViewData;
+ OUString aStrSelectedArea;
+
+ bool bModifyMode;
+ bool bCancelMode;
+ bool bCopyDone;
+ sal_Int32 nCancelPos;
+
+ void Init ();
+ size_t UpdateUserListBox ();
+ void UpdateEntries ( size_t nList );
+ static void MakeListStr ( OUString& rListStr );
+ void AddNewList ( const OUString& rEntriesStr );
+ void RemoveList ( size_t nList );
+ void ModifyList ( size_t nSelList,
+ const OUString& rEntriesStr );
+ void CopyListFromArea ( const ScRefAddress& rStartPos,
+ const ScRefAddress& rEndPos );
+
+ // Handler:
+ DECL_LINK( LbSelectHdl, weld::TreeView&, void );
+ DECL_LINK( BtnClickHdl, weld::Button&, void );
+ DECL_LINK( EdEntriesModHdl, weld::TextView&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tpview.hxx b/sc/source/ui/inc/tpview.hxx
new file mode 100644
index 0000000000..97addc9f53
--- /dev/null
+++ b/sc/source/ui/inc/tpview.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 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+#include <svx/colorbox.hxx>
+
+class ScViewOptions;
+
+class ScTpContentOptions : public SfxTabPage
+{
+ std::unique_ptr<ScViewOptions> m_xLocalOptions;
+
+ std::unique_ptr<weld::ComboBox> m_xGridLB;
+ std::unique_ptr<weld::Widget> m_xGridImg;
+ std::unique_ptr<weld::CheckButton> m_xBreakCB;
+ std::unique_ptr<weld::Widget> m_xBreakImg;
+ std::unique_ptr<weld::CheckButton> m_xGuideLineCB;
+ std::unique_ptr<weld::Widget> m_xGuideLineImg;
+
+ std::unique_ptr<weld::CheckButton> m_xFormulaCB;
+ std::unique_ptr<weld::Widget> m_xFormulaImg;
+ std::unique_ptr<weld::CheckButton> m_xNilCB;
+ std::unique_ptr<weld::Widget> m_xNilImg;
+ std::unique_ptr<weld::CheckButton> m_xAnnotCB;
+ std::unique_ptr<weld::Widget> m_xAnnotImg;
+ std::unique_ptr<weld::CheckButton> m_xFormulaMarkCB;
+ std::unique_ptr<weld::Widget> m_xFormulaMarkImg;
+ std::unique_ptr<weld::CheckButton> m_xValueCB;
+ std::unique_ptr<weld::Widget> m_xValueImg;
+ std::unique_ptr<weld::CheckButton> m_xColRowHighCB;
+ std::unique_ptr<weld::Widget> m_xColRowHighImg;
+ std::unique_ptr<weld::CheckButton> m_xAnchorCB;
+ std::unique_ptr<weld::Widget> m_xAnchorImg;
+ std::unique_ptr<weld::CheckButton> m_xRangeFindCB;
+ std::unique_ptr<weld::Widget> m_xRangeFindImg;
+
+ std::unique_ptr<weld::ComboBox> m_xObjGrfLB;
+ std::unique_ptr<weld::Widget> m_xObjGrfImg;
+ std::unique_ptr<weld::ComboBox> m_xDiagramLB;
+ std::unique_ptr<weld::Widget> m_xDiagramImg;
+ std::unique_ptr<weld::ComboBox> m_xDrawLB;
+ std::unique_ptr<weld::Widget> m_xDrawImg;
+
+ std::unique_ptr<weld::CheckButton> m_xSyncZoomCB;
+ std::unique_ptr<weld::Widget> m_xSyncZoomImg;
+
+ std::unique_ptr<weld::CheckButton> m_xRowColHeaderCB;
+ std::unique_ptr<weld::Widget> m_xRowColHeaderImg;
+ std::unique_ptr<weld::CheckButton> m_xHScrollCB;
+ std::unique_ptr<weld::Widget> m_xHScrollImg;
+ std::unique_ptr<weld::CheckButton> m_xVScrollCB;
+ std::unique_ptr<weld::Widget> m_xVScrollImg;
+ std::unique_ptr<weld::CheckButton> m_xTblRegCB;
+ std::unique_ptr<weld::Widget> m_xTblRegImg;
+ std::unique_ptr<weld::CheckButton> m_xOutlineCB;
+ std::unique_ptr<weld::Widget> m_xOutlineImg;
+ std::unique_ptr<weld::CheckButton> m_xSummaryCB;
+ std::unique_ptr<weld::Widget> m_xSummaryImg;
+ std::unique_ptr<weld::RadioButton> m_xThemedCursorRB;
+ std::unique_ptr<weld::RadioButton> m_xSystemCursorRB;
+ std::unique_ptr<weld::Widget> m_xCursorImg;
+
+ void InitGridOpt();
+ DECL_LINK( GridHdl, weld::ComboBox&, void );
+ DECL_LINK( SelLbObjHdl, weld::ComboBox&, void );
+ DECL_LINK( CBHdl, weld::Toggleable&, void );
+
+public:
+ ScTpContentOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet);
+ virtual ~ScTpContentOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rCoreSet ) override;
+ virtual void Reset ( const SfxItemSet* rCoreSet ) override;
+ virtual void ActivatePage( const SfxItemSet& ) override;
+ virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override;
+
+};
+
+class ScDocument;
+class ScTpLayoutOptions : public SfxTabPage
+{
+ ScDocument *pDoc;
+
+ std::unique_ptr<weld::ComboBox> m_xUnitLB;
+ std::unique_ptr<weld::Widget> m_xUnitImg;
+ std::unique_ptr<weld::MetricSpinButton> m_xTabMF;
+ std::unique_ptr<weld::Widget> m_xTabImg;
+
+ std::unique_ptr<weld::RadioButton> m_xAlwaysRB;
+ std::unique_ptr<weld::RadioButton> m_xRequestRB;
+ std::unique_ptr<weld::RadioButton> m_xNeverRB;
+ std::unique_ptr<weld::Widget> m_xUpdateLinksImg;
+
+ std::unique_ptr<weld::CheckButton> m_xAlignCB;
+ std::unique_ptr<weld::Widget> m_xAlignImg;
+ std::unique_ptr<weld::ComboBox> m_xAlignLB;
+ std::unique_ptr<weld::CheckButton> m_xEditModeCB;
+ std::unique_ptr<weld::Widget> m_xEditModeImg;
+ std::unique_ptr<weld::CheckButton> m_xFormatCB;
+ std::unique_ptr<weld::Widget> m_xFormatImg;
+ std::unique_ptr<weld::CheckButton> m_xExpRefCB;
+ std::unique_ptr<weld::Widget> m_xExpRefImg;
+ std::unique_ptr<weld::CheckButton> m_xSortRefUpdateCB;
+ std::unique_ptr<weld::Widget> m_xSortRefUpdateImg;
+ std::unique_ptr<weld::CheckButton> m_xMarkHdrCB;
+ std::unique_ptr<weld::Widget> m_xMarkHdrImg;
+ std::unique_ptr<weld::CheckButton> m_xReplWarnCB;
+ std::unique_ptr<weld::Widget> m_xReplWarnImg;
+ std::unique_ptr<weld::CheckButton> m_xLegacyCellSelectionCB;
+ std::unique_ptr<weld::Widget> m_xLegacyCellSelectionImg;
+ std::unique_ptr<weld::CheckButton> m_xEnterPasteModeCB;
+ std::unique_ptr<weld::Widget> m_xEnterPasteModeImg;
+
+ DECL_LINK(MetricHdl, weld::ComboBox&, void );
+ DECL_LINK( AlignHdl, weld::Toggleable&, void );
+
+
+public:
+ ScTpLayoutOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet );
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rCoreSet);
+ virtual ~ScTpLayoutOptions() override;
+
+ virtual OUString GetAllStrings() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rCoreSet ) override;
+ virtual void Reset ( const SfxItemSet* rCoreSet ) override;
+ virtual void ActivatePage( const SfxItemSet& ) override;
+ virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/transobj.hxx b/sc/source/ui/inc/transobj.hxx
new file mode 100644
index 0000000000..1c81610e6f
--- /dev/null
+++ b/sc/source/ui/inc/transobj.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/transfer.hxx>
+#include <address.hxx>
+#include <document.hxx>
+#include <rtl/ref.hxx>
+#include <sfx2/objsh.hxx>
+
+
+class ScDocShell;
+class ScMarkData;
+enum class ScDragSrc;
+class ScCellRangesBase;
+
+namespace com::sun::star {
+ namespace sheet {
+ class XSheetCellRanges;
+ }
+}
+
+class SAL_DLLPUBLIC_RTTI ScTransferObj : public TransferDataContainer
+{
+private:
+ std::shared_ptr<ScDocument> m_pDoc;
+ ScRange m_aBlock;
+ SCROW m_nNonFiltered; // non-filtered rows
+ TransferableObjectDescriptor m_aObjDesc;
+ SfxObjectShellRef m_aDocShellRef;
+ SfxObjectShellRef m_aDrawPersistRef;
+ rtl::Reference<ScCellRangesBase> m_xDragSourceRanges;
+ SCCOL m_nDragHandleX;
+ SCROW m_nDragHandleY;
+ SCCOL m_nSourceCursorX;
+ SCROW m_nSourceCursorY;
+ SCTAB m_nVisibleTab;
+ ScDragSrc m_nDragSourceFlags;
+ bool m_bDragWasInternal;
+ bool m_bUsedForLink;
+ bool m_bHasFiltered; // if has filtered rows
+ bool m_bUseInApi; // to recognize clipboard content copied from API
+
+ // #i123405# added parameter to allow size calculation without limitation
+ // to PageSize, e.g. used for Metafile creation for clipboard.
+ void InitDocShell(bool bLimitToPageSize);
+ static void StripRefs( ScDocument& rDoc, SCCOL nStartX, SCROW nStartY,
+ SCCOL nEndX, SCROW nEndY,
+ ScDocument& rDestDoc );
+ static void PaintToDev( OutputDevice* pDev, ScDocument& rDoc, double nPrintFactor,
+ const ScRange& rBlock );
+ static void GetAreaSize( const ScDocument& rDoc, SCTAB nTab1, SCTAB nTab2, SCROW& nRow, SCCOL& nCol );
+
+public:
+ ScTransferObj( const std::shared_ptr<ScDocument>& pClipDoc, TransferableObjectDescriptor aDesc );
+ virtual ~ScTransferObj() override;
+
+ virtual void AddSupportedFormats() override;
+ virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override;
+ virtual bool WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId,
+ const css::datatransfer::DataFlavor& rFlavor ) override;
+ virtual void DragFinished( sal_Int8 nDropAction ) override;
+ virtual sal_Bool SAL_CALL isComplex() override;
+
+ ScDocument* GetDocument() const { return m_pDoc.get(); } // owned by ScTransferObj
+ const ScRange& GetRange() const { return m_aBlock; }
+ SCROW GetNonFilteredRows() const { return m_nNonFiltered; }
+ SCCOL GetDragHandleX() const { return m_nDragHandleX; }
+ SCROW GetDragHandleY() const { return m_nDragHandleY; }
+ bool WasSourceCursorInSelection() const;
+ SCCOL GetSourceCursorX() const { return m_nSourceCursorX; }
+ SCROW GetSourceCursorY() const { return m_nSourceCursorY; }
+ SCTAB GetVisibleTab() const { return m_nVisibleTab; }
+ ScDragSrc GetDragSourceFlags() const { return m_nDragSourceFlags; }
+ bool HasFilteredRows() const { return m_bHasFiltered; }
+ bool GetUseInApi() const { return m_bUseInApi; }
+ ScDocShell* GetSourceDocShell();
+ ScDocument* GetSourceDocument();
+ ScMarkData GetSourceMarkData() const;
+
+ void SetDrawPersist( const SfxObjectShellRef& rRef );
+ void SetDragHandlePos( SCCOL nX, SCROW nY );
+ void SetSourceCursorPos( SCCOL nX, SCROW nY );
+ void SetVisibleTab( SCTAB nNew );
+ void SetDragSource( ScDocShell* pSourceShell, const ScMarkData& rMark );
+ void SetDragSourceFlags( ScDragSrc nFlags );
+ void SetDragWasInternal();
+ SC_DLLPUBLIC void SetUseInApi( bool bSet );
+
+ static SC_DLLPUBLIC ScTransferObj* GetOwnClipboard(const css::uno::Reference<css::datatransfer::XTransferable2>&);
+
+ static SfxObjectShell* SetDrawClipDoc(bool bAnyOle, const std::shared_ptr<ScDocument>& = {} ); // update ScGlobal::xDrawClipDocShellRef
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/uiitems.hxx b/sc/source/ui/inc/uiitems.hxx
new file mode 100644
index 0000000000..71216eb687
--- /dev/null
+++ b/sc/source/ui/inc/uiitems.hxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <scdllapi.h>
+#include <sortparam.hxx>
+#include <subtotalparam.hxx>
+#include <paramisc.hxx>
+#include <svl/poolitem.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace editeng {
+ struct MisspellRanges;
+}
+
+class ScEditEngineDefaulter;
+class EditTextObject;
+class ScViewData;
+class ScDPSaveData;
+struct ScQueryParam;
+
+// Items
+
+class ScInputStatusItem : public SfxPoolItem
+{
+ ScAddress aCursorPos;
+ ScAddress aStartPos;
+ ScAddress aEndPos;
+ OUString aString;
+ std::unique_ptr<EditTextObject> pEditData;
+ const std::vector<editeng::MisspellRanges>* mpMisspellRanges;
+
+public:
+
+ ScInputStatusItem( sal_uInt16 nWhich,
+ const ScAddress& rCurPos,
+ const ScAddress& rStartPos,
+ const ScAddress& rEndPos,
+ OUString aString,
+ const EditTextObject* pData );
+ ScInputStatusItem( const ScInputStatusItem& rItem );
+ virtual ~ScInputStatusItem() override;
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScInputStatusItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ const ScAddress& GetPos() const { return aCursorPos; }
+
+ const OUString& GetString() const { return aString; }
+ const EditTextObject* GetEditData() const { return pEditData.get(); }
+
+ void SetMisspellRanges( const std::vector<editeng::MisspellRanges>* pRanges );
+ const std::vector<editeng::MisspellRanges>* GetMisspellRanges() const { return mpMisspellRanges;}
+};
+
+#define SC_TAB_INSERTED 1
+#define SC_TAB_DELETED 2
+#define SC_TAB_MOVED 3
+#define SC_TAB_COPIED 4
+#define SC_TAB_HIDDEN 5
+#define SC_TABS_INSERTED 6
+#define SC_TABS_DELETED 7
+
+class ScTablesHint : public SfxHint
+{
+ sal_uInt16 nId;
+ SCTAB nTab1;
+ SCTAB nTab2;
+
+public:
+ ScTablesHint(sal_uInt16 nNewId, SCTAB nTable1, SCTAB nTable2=0);
+ virtual ~ScTablesHint() override;
+
+ sal_uInt16 GetTablesHintId() const { return nId; }
+ SCTAB GetTab1() const { return nTab1; }
+ SCTAB GetTab2() const { return nTab2; }
+};
+
+class ScEditViewHint : public SfxHint
+{
+ ScEditEngineDefaulter* pEditEngine;
+ ScAddress aCursorPos;
+
+public:
+ ScEditViewHint() = delete;
+ ScEditViewHint( ScEditEngineDefaulter* pEngine, const ScAddress& rCurPos );
+ virtual ~ScEditViewHint() override;
+
+ SCCOL GetCol() const { return aCursorPos.Col(); }
+ SCROW GetRow() const { return aCursorPos.Row(); }
+ SCTAB GetTab() const { return aCursorPos.Tab(); }
+ ScEditEngineDefaulter* GetEngine() const { return pEditEngine; }
+};
+
+class ScIndexHint : public SfxHint
+{
+ sal_uInt16 nIndex;
+
+public:
+ ScIndexHint(SfxHintId nNewId, sal_uInt16 nIdx);
+ virtual ~ScIndexHint() override;
+
+ sal_uInt16 GetIndex() const { return nIndex; }
+};
+
+// Parameter item for the sort dialog:
+
+class SC_DLLPUBLIC ScSortItem : public SfxPoolItem
+{
+public:
+ ScSortItem( sal_uInt16 nWhich,
+ ScViewData* ptrViewData,
+ const ScSortParam* pSortData );
+ ScSortItem( sal_uInt16 nWhich,
+ const ScSortParam* pSortData );
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScSortItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+ virtual bool QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const override;
+
+ ScViewData* GetViewData () const { return pViewData; }
+ const ScSortParam& GetSortData () const { return theSortData; }
+
+private:
+ ScViewData* pViewData;
+ ScSortParam theSortData;
+};
+
+class SC_DLLPUBLIC ScQueryItem : public SfxPoolItem
+{
+public:
+ ScQueryItem( sal_uInt16 nWhich,
+ ScViewData* ptrViewData,
+ const ScQueryParam* pQueryData );
+ ScQueryItem( sal_uInt16 nWhich,
+ const ScQueryParam* pQueryData );
+ ScQueryItem( const ScQueryItem& rItem );
+ virtual ~ScQueryItem() override;
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScQueryItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ ScViewData* GetViewData () const { return pViewData; }
+ const ScQueryParam& GetQueryData() const;
+
+ bool GetAdvancedQuerySource(ScRange& rSource) const;
+ void SetAdvancedQuerySource(const ScRange* pSource);
+
+private:
+ std::unique_ptr<ScQueryParam> mpQueryData;
+ ScViewData* pViewData;
+ ScRange aAdvSource;
+ bool bIsAdvanced;
+};
+
+class SC_DLLPUBLIC ScSubTotalItem : public SfxPoolItem
+{
+public:
+ ScSubTotalItem( sal_uInt16 nWhich,
+ ScViewData* ptrViewData,
+ const ScSubTotalParam* pSubTotalData );
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScSubTotalItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+ virtual bool QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId = 0 ) const override;
+
+ ScViewData* GetViewData () const { return pViewData; }
+ const ScSubTotalParam& GetSubTotalData() const { return theSubTotalData; }
+
+private:
+ ScViewData* pViewData;
+ ScSubTotalParam theSubTotalData;
+};
+
+class SC_DLLPUBLIC ScUserListItem : public SfxPoolItem
+{
+public:
+ ScUserListItem( sal_uInt16 nWhich );
+ ScUserListItem( const ScUserListItem& rItem );
+ virtual ~ScUserListItem() override;
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScUserListItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ void SetUserList ( const ScUserList& rUserList );
+ ScUserList* GetUserList () const { return pUserList.get(); }
+
+private:
+ std::unique_ptr<ScUserList> pUserList;
+};
+
+class ScConsolidateItem : public SfxPoolItem
+{
+public:
+ ScConsolidateItem( sal_uInt16 nWhich,
+ const ScConsolidateParam* pParam );
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScConsolidateItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ const ScConsolidateParam& GetData() const { return theConsData; }
+
+private:
+ ScConsolidateParam theConsData;
+};
+
+class ScPivotItem : public SfxPoolItem
+{
+public:
+ ScPivotItem( sal_uInt16 nWhich, const ScDPSaveData* pData,
+ const ScRange* pRange, bool bNew );
+ ScPivotItem( const ScPivotItem& rItem );
+ virtual ~ScPivotItem() override;
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScPivotItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ const ScDPSaveData& GetData() const { return *pSaveData; }
+ const ScRange& GetDestRange() const { return aDestRange; }
+ bool IsNewSheet() const { return bNewSheet; }
+
+private:
+ std::unique_ptr<ScDPSaveData> pSaveData;
+ ScRange aDestRange;
+ bool bNewSheet;
+};
+
+class ScSolveItem : public SfxPoolItem
+{
+public:
+ ScSolveItem( sal_uInt16 nWhich,
+ const ScSolveParam* pParam );
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScSolveItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ const ScSolveParam& GetData() const { return theSolveData; }
+
+private:
+ ScSolveParam theSolveData;
+};
+
+class ScTabOpItem : public SfxPoolItem
+{
+public:
+ ScTabOpItem( sal_uInt16 nWhich,
+ const ScTabOpParam* pParam );
+
+ virtual bool operator==( const SfxPoolItem& ) const override;
+ virtual ScTabOpItem* Clone( SfxItemPool *pPool = nullptr ) const override;
+
+ const ScTabOpParam& GetData() const { return theTabOpData; }
+
+private:
+ ScTabOpParam theTabOpData;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/uiobject.hxx b/sc/source/ui/inc/uiobject.hxx
new file mode 100644
index 0000000000..4545c50e79
--- /dev/null
+++ b/sc/source/ui/inc/uiobject.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/.
+ */
+
+#include "gridwin.hxx"
+#include <memory>
+#include <vcl/uitest/uiobject.hxx>
+
+class ScGridWindow;
+class ScDBFunc;
+class ScDrawView;
+class ScTabViewShell;
+class ScViewFunc;
+
+class ScGridWinUIObject : public WindowUIObject
+{
+ VclPtr<ScGridWindow> mxGridWindow;
+
+public:
+ ScGridWinUIObject(const VclPtr<ScGridWindow>& xGridWin);
+
+ virtual StringMap get_state() override;
+
+ virtual void execute(const OUString& rAction, const StringMap& rParameters) override;
+
+ virtual std::unique_ptr<UIObject> get_child(const OUString& rID) override;
+
+ static std::unique_ptr<UIObject> create(vcl::Window* pWindow);
+
+ virtual std::set<OUString> get_children() const override;
+
+protected:
+ virtual OUString get_name() const override;
+
+private:
+ ScDBFunc* getDBFunc();
+ ScDrawView* getDrawView();
+ ScTabViewShell* getViewShell();
+ ScViewFunc* getViewFunc();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoDeleteSparkline.hxx b/sc/source/ui/inc/undo/UndoDeleteSparkline.hxx
new file mode 100644
index 0000000000..97fcd77d95
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoDeleteSparkline.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/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <address.hxx>
+#include <memory>
+
+namespace sc
+{
+class SparklineGroup;
+struct SparklineData;
+
+/** Undo action for deleting a Sparkline */
+class UndoDeleteSparkline : public ScSimpleUndo
+{
+private:
+ std::shared_ptr<sc::SparklineGroup> mpSparklineGroup;
+ ScAddress maSparklinePosition;
+
+public:
+ UndoDeleteSparkline(ScDocShell& rDocShell, ScAddress const& rSparklinePosition);
+
+ virtual ~UndoDeleteSparkline() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoDeleteSparklineGroup.hxx b/sc/source/ui/inc/undo/UndoDeleteSparklineGroup.hxx
new file mode 100644
index 0000000000..7ca6c60077
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoDeleteSparklineGroup.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/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <address.hxx>
+#include <memory>
+
+namespace sc
+{
+class SparklineGroup;
+
+/** Undo action for deleting a sparkline group and all associated sparklines */
+class UndoDeleteSparklineGroup : public ScSimpleUndo
+{
+private:
+ std::shared_ptr<sc::SparklineGroup> mpSparklineGroup;
+ std::vector<std::shared_ptr<sc::Sparkline>> maSparklines;
+ SCTAB mnTab;
+
+public:
+ UndoDeleteSparklineGroup(ScDocShell& rDocShell,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup,
+ SCTAB nSheetIndex);
+
+ virtual ~UndoDeleteSparklineGroup() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoEditSparkline.hxx b/sc/source/ui/inc/undo/UndoEditSparkline.hxx
new file mode 100644
index 0000000000..78bd096bc9
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoEditSparkline.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/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <address.hxx>
+#include <rangelst.hxx>
+#include <memory>
+
+namespace sc
+{
+class Sparkline;
+struct SparklineData;
+
+/** Undo action for editing a Sparkline */
+class UndoEditSparkline : public ScSimpleUndo
+{
+private:
+ std::shared_ptr<sc::Sparkline> mpSparkline;
+ SCTAB mnTab;
+ ScRangeList maOldDataRange;
+ ScRangeList maNewDataRange;
+
+public:
+ UndoEditSparkline(ScDocShell& rDocShell, std::shared_ptr<sc::Sparkline> pSparkline, SCTAB nTab,
+ ScRangeList rDataRange);
+
+ virtual ~UndoEditSparkline() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoEditSparklineGroup.hxx b/sc/source/ui/inc/undo/UndoEditSparklineGroup.hxx
new file mode 100644
index 0000000000..b529c0234d
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoEditSparklineGroup.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <memory>
+
+#include <SparklineAttributes.hxx>
+#include <SparklineGroup.hxx>
+
+namespace sc
+{
+/** Undo action for editing a Sparkline */
+class UndoEditSparklneGroup : public ScSimpleUndo
+{
+private:
+ std::shared_ptr<sc::SparklineGroup> m_pSparklineGroup;
+ sc::SparklineAttributes m_aNewAttributes;
+ sc::SparklineAttributes m_aOriginalAttributes;
+
+public:
+ UndoEditSparklneGroup(ScDocShell& rDocShell,
+ std::shared_ptr<sc::SparklineGroup> const& rSparklineGroup,
+ sc::SparklineAttributes aAttributes);
+ virtual ~UndoEditSparklneGroup() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoGroupSparklines.hxx b/sc/source/ui/inc/undo/UndoGroupSparklines.hxx
new file mode 100644
index 0000000000..e67778c213
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoGroupSparklines.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/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <memory>
+#include <utility>
+
+namespace sc
+{
+/** Previous sparkline group data, which is restored at Undo grouping */
+struct UndoGroupSparklinesData
+{
+ UndoGroupSparklinesData(ScAddress const& rAddress, ScRangeList aDataRangeList,
+ std::shared_ptr<sc::SparklineGroup> pGroup)
+ : m_aAddress(rAddress)
+ , m_aDataRangeList(std::move(aDataRangeList))
+ , m_pSparklineGroup(std::move(pGroup))
+ {
+ }
+
+ ScAddress m_aAddress;
+ ScRangeList m_aDataRangeList;
+ std::shared_ptr<sc::SparklineGroup> m_pSparklineGroup;
+};
+
+/** Undo action for grouping sparklines */
+class UndoGroupSparklines : public ScSimpleUndo
+{
+private:
+ ScRange m_aRange;
+ std::shared_ptr<sc::SparklineGroup> m_pSparklineGroup;
+ std::vector<UndoGroupSparklinesData> m_aUndoData;
+
+public:
+ UndoGroupSparklines(ScDocShell& rDocShell, ScRange const& rRange,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup);
+ virtual ~UndoGroupSparklines() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoInsertSparkline.hxx b/sc/source/ui/inc/undo/UndoInsertSparkline.hxx
new file mode 100644
index 0000000000..5262297a24
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoInsertSparkline.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <address.hxx>
+#include <memory>
+
+namespace sc
+{
+class SparklineGroup;
+struct SparklineData;
+
+/** Undo action for inserting a Sparkline */
+class UndoInsertSparkline : public ScSimpleUndo
+{
+private:
+ std::vector<sc::SparklineData> maSparklineDataVector;
+ std::shared_ptr<sc::SparklineGroup> mpSparklineGroup;
+
+public:
+ UndoInsertSparkline(ScDocShell& rDocShell, std::vector<SparklineData> pSparklineDataVector,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup);
+
+ virtual ~UndoInsertSparkline() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoThemeChange.hxx b/sc/source/ui/inc/undo/UndoThemeChange.hxx
new file mode 100644
index 0000000000..6c0558df8a
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoThemeChange.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+
+namespace sc
+{
+class UndoThemeChange : public ScSimpleUndo
+{
+private:
+ std::shared_ptr<model::ColorSet> mpOldColorSet;
+ std::shared_ptr<model::ColorSet> mpNewColorSet;
+
+public:
+ UndoThemeChange(ScDocShell& rDocShell, std::shared_ptr<model::ColorSet> const& pOldColorSet,
+ std::shared_ptr<model::ColorSet> const& pNewColorSet);
+ virtual ~UndoThemeChange() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undo/UndoUngroupSparklines.hxx b/sc/source/ui/inc/undo/UndoUngroupSparklines.hxx
new file mode 100644
index 0000000000..7c836ac2d1
--- /dev/null
+++ b/sc/source/ui/inc/undo/UndoUngroupSparklines.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/.
+ *
+ */
+
+#pragma once
+
+#include <undobase.hxx>
+#include <memory>
+#include <utility>
+
+namespace sc
+{
+/** Previous sparkline group data, which is restored at undo ungroupping */
+struct SparklineUndoData
+{
+ SparklineUndoData(ScAddress const& rAddress, ScRangeList aDataRangeList,
+ std::shared_ptr<sc::SparklineGroup> pGroup)
+ : m_aAddress(rAddress)
+ , m_aDataRangeList(std::move(aDataRangeList))
+ , m_pSparklineGroup(std::move(pGroup))
+ {
+ }
+
+ ScAddress m_aAddress;
+ ScRangeList m_aDataRangeList;
+ std::shared_ptr<sc::SparklineGroup> m_pSparklineGroup;
+};
+
+/** Undo action for ungrouping sparklines */
+class UndoUngroupSparklines : public ScSimpleUndo
+{
+private:
+ ScRange m_aRange;
+ std::vector<SparklineUndoData> m_aUndoData;
+
+public:
+ UndoUngroupSparklines(ScDocShell& rDocShell, ScRange const& rRange);
+ virtual ~UndoUngroupSparklines() override;
+
+ void Undo() override;
+ void Redo() override;
+ bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ void Repeat(SfxRepeatTarget& rTarget) override;
+ OUString GetComment() const override;
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undobase.hxx b/sc/source/ui/inc/undobase.hxx
new file mode 100644
index 0000000000..25cee7e5c5
--- /dev/null
+++ b/sc/source/ui/inc/undobase.hxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svl/undo.hxx>
+#include <address.hxx>
+#include "docsh.hxx"
+
+#include <memory>
+#include <map>
+#include <optional>
+
+class SdrUndoAction;
+class ScRefUndoData;
+class ScDBData;
+
+class SC_DLLPUBLIC ScSimpleUndo: public SfxUndoAction
+{
+ ScSimpleUndo(const ScSimpleUndo&) = delete;
+
+public:
+ typedef std::map<SCTAB, std::unique_ptr<sc::ColumnSpanSet>> DataSpansType;
+
+ ScSimpleUndo( ScDocShell* pDocSh );
+
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+ /// See SfxUndoAction::GetViewShellId().
+ ViewShellId GetViewShellId() const override;
+
+ virtual std::optional<ScRange> getAffectedRange() const { return std::nullopt; }
+
+protected:
+ ScDocShell* pDocShell;
+ std::unique_ptr<SfxUndoAction>
+ pDetectiveUndo;
+ ViewShellId mnViewShellId;
+
+ bool IsPaintLocked() const { return pDocShell->IsPaintLocked(); }
+
+ bool SetViewMarkData( const ScMarkData& rMarkData );
+
+ void BeginUndo();
+ void EndUndo();
+ void BeginRedo();
+ void EndRedo();
+
+ void BroadcastChanges( const ScRange& rRange );
+
+ /**
+ * Broadcast changes on specified spans.
+ *
+ * @param rSpans container that specifies all spans whose changes need to
+ * be broadcasted.
+ */
+ void BroadcastChanges( const DataSpansType& rSpans );
+
+ static void ShowTable( SCTAB nTab );
+ static void ShowTable( const ScRange& rRange );
+};
+
+enum ScBlockUndoMode { SC_UNDO_SIMPLE, SC_UNDO_MANUALHEIGHT, SC_UNDO_AUTOHEIGHT };
+
+class ScBlockUndo: public ScSimpleUndo
+{
+public:
+ ScBlockUndo( ScDocShell* pDocSh, const ScRange& rRange,
+ ScBlockUndoMode eBlockMode );
+ virtual ~ScBlockUndo() override;
+
+ virtual std::optional<ScRange> getAffectedRange() const override { return aBlockRange; }
+
+protected:
+ ScRange aBlockRange;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+ ScBlockUndoMode eMode;
+
+ void BeginUndo();
+ void EndUndo();
+// void BeginRedo();
+ void EndRedo();
+
+ bool AdjustHeight();
+ void ShowBlock();
+};
+
+class SC_DLLPUBLIC ScMultiBlockUndo: public ScSimpleUndo
+{
+public:
+ ScMultiBlockUndo(ScDocShell* pDocSh, ScRangeList aRanges);
+ virtual ~ScMultiBlockUndo() override;
+
+protected:
+ ScRangeList maBlockRanges;
+ std::unique_ptr<SdrUndoAction> mpDrawUndo;
+
+ void BeginUndo();
+ void EndUndo();
+ void EndRedo();
+
+ void ShowBlock();
+};
+
+// for functions that act on a database range - takes care of the unnamed database range
+// (collected separately, before the undo action, for showing dialogs etc.)
+
+class ScDBFuncUndo: public ScSimpleUndo
+{
+protected:
+ std::unique_ptr<ScDBData> pAutoDBRange;
+ ScRange aOriginalRange;
+
+public:
+ ScDBFuncUndo( ScDocShell* pDocSh, const ScRange& rOriginal );
+ virtual ~ScDBFuncUndo() override;
+
+ void BeginUndo();
+ void EndUndo();
+ void BeginRedo();
+ void EndRedo();
+};
+
+class ScMoveUndo: public ScSimpleUndo // with references
+{
+public:
+ ScMoveUndo( ScDocShell* pDocSh,
+ ScDocumentUniquePtr pRefDoc, std::unique_ptr<ScRefUndoData> pRefData );
+ virtual ~ScMoveUndo() override;
+
+protected:
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+ ScDocumentUniquePtr pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pRefUndoData;
+
+ void BeginUndo();
+ void EndUndo();
+// void BeginRedo();
+// void EndRedo();
+
+private:
+ void UndoRef();
+};
+
+class ScUndoWrapper: public SfxUndoAction // for manual merging of actions
+{
+ std::unique_ptr<SfxUndoAction> pWrappedUndo;
+ ViewShellId mnViewShellId;
+
+public:
+ ScUndoWrapper( std::unique_ptr<SfxUndoAction> pUndo );
+ virtual ~ScUndoWrapper() override;
+
+ SfxUndoAction* GetWrappedUndo() { return pWrappedUndo.get(); }
+ void ForgetWrappedUndo();
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+ virtual OUString GetComment() const override;
+ virtual OUString GetRepeatComment(SfxRepeatTarget&) const override;
+ /// See SfxUndoAction::GetViewShellId().
+ ViewShellId GetViewShellId() const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undoblk.hxx b/sc/source/ui/inc/undoblk.hxx
new file mode 100644
index 0000000000..9bda36a1e1
--- /dev/null
+++ b/sc/source/ui/inc/undoblk.hxx
@@ -0,0 +1,983 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "undobase.hxx"
+#include <markdata.hxx>
+#include "spellparam.hxx"
+#include "cellmergeoption.hxx"
+#include <paramisc.hxx>
+#include <editeng/boxitem.hxx>
+
+#include <memory>
+
+class ScDocShell;
+class ScOutlineTable;
+class ScPatternAttr;
+class SvxSearchItem;
+class SdrUndoAction;
+class ScEditDataArray;
+
+class ScUndoInsertCells: public ScMoveUndo
+{
+public:
+ ScUndoInsertCells( ScDocShell* pNewDocShell,
+ const ScRange& rRange,
+ SCTAB nNewCount, std::unique_ptr<SCTAB[]> pNewTabs, std::unique_ptr<SCTAB[]> pNewScenarios,
+ InsCellCmd eNewCmd, ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData,
+ bool bNewPartOfPaste );
+ virtual ~ScUndoInsertCells() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat( SfxRepeatTarget& rTarget ) override;
+ virtual bool CanRepeat( SfxRepeatTarget& rTarget ) const override;
+
+ virtual OUString GetComment() const override;
+
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+private:
+ ScRange aEffRange;
+ SCTAB nCount;
+ std::unique_ptr<SCTAB[]>
+ pTabs;
+ std::unique_ptr<SCTAB[]>
+ pScenarios;
+ sal_uLong nEndChangeAction;
+ InsCellCmd eCmd;
+ bool bPartOfPaste;
+ std::unique_ptr<SfxUndoAction>
+ pPasteUndo;
+
+ void DoChange ( const bool bUndo );
+ void SetChangeTrack();
+};
+
+class ScUndoDeleteCells: public ScMoveUndo
+{
+public:
+ ScUndoDeleteCells( ScDocShell* pNewDocShell,
+ const ScRange& rRange,
+ SCTAB nNewCount, std::unique_ptr<SCTAB[]> pNewTabs, std::unique_ptr<SCTAB[]> pNewScenarios,
+ DelCellCmd eNewCmd, ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData );
+ virtual ~ScUndoDeleteCells() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScRange aEffRange;
+ SCTAB nCount;
+ std::unique_ptr<SCTAB[]>
+ pTabs;
+ std::unique_ptr<SCTAB[]>
+ pScenarios;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ DelCellCmd eCmd;
+
+ void DoChange ( const bool bUndo );
+ void SetChangeTrack();
+};
+
+class ScUndoDeleteMulti: public ScMoveUndo
+{
+public:
+
+ ScUndoDeleteMulti( ScDocShell* pNewDocShell,
+ bool bNewRows, bool bNeedsRefresh, SCTAB nNewTab,
+ std::vector<sc::ColRowSpan>&& rSpans,
+ ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData );
+
+ virtual ~ScUndoDeleteMulti() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ bool mbRows:1;
+ bool mbRefresh:1;
+ SCTAB nTab;
+ std::vector<sc::ColRowSpan> maSpans;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void DoChange() const;
+ void SetChangeTrack();
+};
+
+class SC_DLLPUBLIC ScUndoCut: public ScBlockUndo
+{
+public:
+ ScUndoCut(ScDocShell* pNewDocShell,
+ const ScRange& aRange, // adjusted for merged cells
+ const ScAddress& aOldEnd, // end position without adjustment
+ const ScMarkData& rMark, // selected sheets
+ ScDocumentUniquePtr pNewUndoDoc);
+ virtual ~ScUndoCut() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ ScRange aExtendedRange;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void DoChange( const bool bUndo );
+ void SetChangeTrack();
+};
+
+struct ScUndoPasteOptions
+{
+ ScPasteFunc nFunction;
+ bool bSkipEmptyCells;
+ bool bTranspose;
+ bool bAsLink;
+ InsCellCmd eMoveMode;
+
+ ScUndoPasteOptions() :
+ nFunction( ScPasteFunc::NONE ),
+ bSkipEmptyCells( false ),
+ bTranspose( false ),
+ bAsLink( false ),
+ eMoveMode( INS_NONE )
+ {}
+};
+
+class SC_DLLPUBLIC ScUndoPaste: public ScMultiBlockUndo
+{
+public:
+ ScUndoPaste(ScDocShell* pNewDocShell, const ScRangeList& rRanges,
+ const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
+ InsertDeleteFlags nNewFlags,
+ std::unique_ptr<ScRefUndoData> pRefData,
+ bool bRedoIsFilled = true,
+ const ScUndoPasteOptions* pOptions = nullptr);
+ virtual ~ScUndoPaste() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr pUndoDoc;
+ ScDocumentUniquePtr pRedoDoc;
+ InsertDeleteFlags nFlags;
+ std::unique_ptr<ScRefUndoData> pRefUndoData;
+ std::unique_ptr<ScRefUndoData> pRefRedoData;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ bool bRedoFilled;
+ ScUndoPasteOptions aPasteOptions;
+
+ void DoChange(bool bUndo);
+ void SetChangeTrack();
+};
+
+class ScUndoDragDrop: public ScMoveUndo
+{
+public:
+ ScUndoDragDrop( ScDocShell* pNewDocShell,
+ const ScRange& rRange, const ScAddress& aNewDestPos, bool bNewCut,
+ ScDocumentUniquePtr pUndoDocument,
+ bool bScenario );
+ virtual ~ScUndoDragDrop() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ sal_uInt16 mnPaintExtFlags;
+ ScRangeList maPaintRanges;
+
+ ScRange aSrcRange;
+ ScRange aDestRange;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ bool bCut;
+ bool bKeepScenarioFlags;
+
+ void PaintArea( ScRange aRange, sal_uInt16 nExtFlags ) const;
+ void DoUndo( ScRange aRange );
+
+ void SetChangeTrack();
+};
+
+class ScUndoDeleteContents: public ScSimpleUndo
+{
+public:
+ ScUndoDeleteContents( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ const ScRange& rRange,
+ ScDocumentUniquePtr&& pNewUndoDoc, bool bNewMulti,
+ InsertDeleteFlags nNewFlags, bool bObjects );
+ virtual ~ScUndoDeleteContents() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+ void SetDataSpans( const std::shared_ptr<DataSpansType>& pSpans );
+
+private:
+ std::shared_ptr<DataSpansType> mpDataSpans; // Spans of non-empty cells.
+
+ ScRange aRange;
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr pUndoDoc; // Block mark and deleted data
+ std::unique_ptr<SdrUndoAction> pDrawUndo; // Deleted objects
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ InsertDeleteFlags nFlags;
+ bool bMulti; // Multi selection
+
+ void DoChange( const bool bUndo );
+ void SetChangeTrack();
+};
+
+class ScUndoFillTable: public ScSimpleUndo
+{
+public:
+ ScUndoFillTable( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, bool bNewMulti, SCTAB nSrc,
+ InsertDeleteFlags nFlg, ScPasteFunc nFunc, bool bSkip, bool bLink );
+ virtual ~ScUndoFillTable() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScRange aRange;
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc; // Block mark and deleted data
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ InsertDeleteFlags nFlags;
+ ScPasteFunc nFunction;
+ SCTAB nSrcTab;
+ bool bMulti; // Multi selection
+ bool bSkipEmpty;
+ bool bAsLink;
+
+ void DoChange( const bool bUndo );
+ void SetChangeTrack();
+};
+
+class ScUndoSelectionAttr: public ScSimpleUndo
+{
+public:
+ ScUndoSelectionAttr( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, bool bNewMulti,
+ const ScPatternAttr* pNewApply,
+ const SvxBoxItem* pNewOuter = nullptr,
+ const SvxBoxInfoItem* pNewInner = nullptr,
+ const ScRange* pRangeCover = nullptr );
+ virtual ~ScUndoSelectionAttr() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+ ScEditDataArray* GetDataArray();
+private:
+ ScMarkData aMarkData;
+ ScRange aRange;
+ ScRange aRangeCover;
+ std::unique_ptr<ScEditDataArray> mpDataArray;
+ ScDocumentUniquePtr pUndoDoc;
+ bool bMulti;
+ ScPatternAttr* pApplyPattern;
+ SvxBoxItem* pLineOuter;
+ SvxBoxInfoItem* pLineInner;
+
+ void DoChange( const bool bUndo );
+ void ChangeEditData( const bool bUndo );
+};
+
+class ScUndoWidthOrHeight: public ScSimpleUndo
+{
+public:
+ ScUndoWidthOrHeight( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOLROW nNewStart, SCTAB nNewStartTab,
+ SCCOLROW nNewEnd, SCTAB nNewEndTab,
+ ScDocumentUniquePtr pNewUndoDoc,
+ std::vector<sc::ColRowSpan>&& rRanges,
+ std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ ScSizeMode eNewMode, sal_uInt16 nNewSizeTwips,
+ bool bNewWidth );
+ virtual ~ScUndoWidthOrHeight() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ SCTAB nStartTab;
+ SCTAB nEndTab;
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::vector<sc::ColRowSpan> maRanges;
+ sal_uInt16 nNewSize;
+ bool bWidth;
+ ScSizeMode eMode;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+};
+
+class ScUndoAutoFill: public ScBlockUndo
+{
+public:
+ ScUndoAutoFill( ScDocShell* pNewDocShell,
+ const ScRange& rRange, const ScRange& rSourceArea,
+ ScDocumentUniquePtr pNewUndoDoc, const ScMarkData& rMark,
+ FillDir eNewFillDir,
+ FillCmd eNewFillCmd, FillDateCmd eNewFillDateCmd,
+ double fNewStartValue, double fNewStepValue, double fNewMaxValue );
+ virtual ~ScUndoAutoFill() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScRange aSource;
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ FillDir eFillDir;
+ FillCmd eFillCmd;
+ FillDateCmd eFillDateCmd;
+ double fStartValue;
+ double fStepValue;
+ double fMaxValue;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void SetChangeTrack();
+};
+
+class ScUndoMerge: public ScSimpleUndo
+{
+public:
+ ScUndoMerge( ScDocShell* pNewDocShell, ScCellMergeOption aOption,
+ bool bMergeContents, ScDocumentUniquePtr pUndoDoc,
+ std::unique_ptr<SdrUndoAction> pDrawUndo);
+ virtual ~ScUndoMerge() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScCellMergeOption maOption;
+ bool mbMergeContents; // Merge contents in Redo().
+ ScDocumentUniquePtr mxUndoDoc; // when data is merged
+ std::unique_ptr<SdrUndoAction> mpDrawUndo;
+
+ void DoChange( bool bUndo ) const;
+};
+
+class ScUndoAutoFormat: public ScBlockUndo
+{
+public:
+ ScUndoAutoFormat( ScDocShell* pNewDocShell,
+ const ScRange& rRange, ScDocumentUniquePtr pNewUndoDoc,
+ const ScMarkData& rMark,
+ bool bNewSize, sal_uInt16 nNewFormatNo );
+ virtual ~ScUndoAutoFormat() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr
+ pUndoDoc; // deleted data
+ ScMarkData aMarkData;
+ bool bSize;
+ sal_uInt16 nFormatNo;
+};
+
+class ScUndoReplace: public ScSimpleUndo
+{
+public:
+ ScUndoReplace( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ OUString aNewUndoStr, ScDocumentUniquePtr pNewUndoDoc,
+ const SvxSearchItem* pItem );
+ virtual ~ScUndoReplace() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScAddress aCursorPos;
+ ScMarkData aMarkData;
+ OUString aUndoStr; // Data at single selection
+ ScDocumentUniquePtr pUndoDoc; // Block mark and deleted data
+ std::unique_ptr<SvxSearchItem> pSearchItem;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void SetChangeTrack();
+};
+
+class ScUndoTabOp: public ScSimpleUndo
+{
+public:
+ ScUndoTabOp( ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc,
+ const ScRefAddress& rFormulaCell,
+ const ScRefAddress& rFormulaEnd,
+ const ScRefAddress& rRowCell,
+ const ScRefAddress& rColCell,
+ ScTabOpParam::Mode eMode );
+ virtual ~ScUndoTabOp() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScRange aRange;
+ ScDocumentUniquePtr
+ pUndoDoc; // Deleted data
+ ScRefAddress theFormulaCell;
+ ScRefAddress theFormulaEnd;
+ ScRefAddress theRowCell;
+ ScRefAddress theColCell;
+ ScTabOpParam::Mode meMode;
+};
+
+class ScUndoConversion : public ScSimpleUndo
+{
+public:
+
+ ScUndoConversion( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScDocumentUniquePtr pNewUndoDoc,
+ SCCOL nNewX, SCROW nNewY, SCTAB nNewZ, ScDocumentUniquePtr pNewRedoDoc,
+ ScConversionParam aConvParam );
+ virtual ~ScUndoConversion() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScAddress aCursorPos;
+ ScDocumentUniquePtr pUndoDoc; // Block mark and deleted data
+ ScAddress aNewCursorPos;
+ ScDocumentUniquePtr pRedoDoc; // Block mark and new data
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ ScConversionParam maConvParam; /// Conversion type and parameters.
+
+ void DoChange( ScDocument* pRefDoc, const ScAddress& rCursorPos );
+ void SetChangeTrack();
+};
+
+class ScUndoRefConversion: public ScSimpleUndo
+{
+public:
+ ScUndoRefConversion( ScDocShell* pNewDocShell,
+ const ScRange& aMarkRange, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc, bool bNewMulti);
+ virtual ~ScUndoRefConversion() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr pUndoDoc;
+ ScDocumentUniquePtr pRedoDoc;
+ ScRange aRange;
+ bool bMulti;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void DoChange( ScDocument* pRefDoc);
+ void SetChangeTrack();
+};
+
+class ScUndoListNames: public ScBlockUndo
+{
+public:
+ ScUndoListNames(ScDocShell* pNewDocShell,
+ const ScRange& rRange,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr xUndoDoc;
+ ScDocumentUniquePtr xRedoDoc;
+
+ void DoChange( ScDocument* pSrcDoc ) const;
+};
+
+class ScUndoConditionalFormat : public ScSimpleUndo
+{
+public:
+ ScUndoConditionalFormat( ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pUndoDoc, ScDocumentUniquePtr pRedoDoc, const ScRange& rRange);
+ virtual ~ScUndoConditionalFormat() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ void DoChange(ScDocument* pDoc);
+ ScDocumentUniquePtr mpUndoDoc;
+ ScDocumentUniquePtr mpRedoDoc;
+ ScRange maRange;
+};
+
+class ScUndoConditionalFormatList : public ScSimpleUndo
+{
+public:
+ ScUndoConditionalFormatList( ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pUndoDoc, ScDocumentUniquePtr pRedoDoc, SCTAB nTab);
+ virtual ~ScUndoConditionalFormatList() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ void DoChange(const ScDocument* pDoc);
+ ScDocumentUniquePtr mpUndoDoc;
+ ScDocumentUniquePtr mpRedoDoc;
+ SCTAB mnTab;
+};
+
+class ScUndoUseScenario: public ScSimpleUndo
+{
+public:
+ ScUndoUseScenario( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ const ScArea& rDestArea, ScDocumentUniquePtr pNewUndoDoc,
+ OUString aNewName );
+ virtual ~ScUndoUseScenario() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr
+ pUndoDoc;
+ ScRange aRange;
+ ScMarkData aMarkData;
+ OUString aName;
+};
+
+class ScUndoSelectionStyle: public ScSimpleUndo
+{
+public:
+ ScUndoSelectionStyle( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ const ScRange& rRange,
+ OUString aName,
+ ScDocumentUniquePtr pNewUndoDoc );
+ virtual ~ScUndoSelectionStyle() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ OUString aStyleName;
+ ScRange aRange;
+
+ void DoChange( const bool bUndo );
+};
+
+class ScUndoRefreshLink: public ScSimpleUndo
+{
+public:
+ ScUndoRefreshLink(ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pNewUndoDoc);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr xUndoDoc;
+ ScDocumentUniquePtr xRedoDoc;
+};
+
+class ScUndoEnterMatrix: public ScBlockUndo
+{
+public:
+ ScUndoEnterMatrix( ScDocShell* pNewDocShell,
+ const ScRange& rArea,
+ ScDocumentUniquePtr pNewUndoDoc,
+ OUString aForm );
+ virtual ~ScUndoEnterMatrix() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr
+ pUndoDoc;
+ OUString aFormula;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void SetChangeTrack();
+};
+
+class ScUndoInsertAreaLink : public ScSimpleUndo
+{
+public:
+ ScUndoInsertAreaLink( ScDocShell* pShell,
+ OUString aDocName,
+ OUString aFltName, OUString aOptions,
+ OUString aAreaName, const ScRange& rDestRange,
+ sal_uLong nRefreshDelay );
+ virtual ~ScUndoInsertAreaLink() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString aDocName;
+ OUString aFltName;
+ OUString aOptions;
+ OUString aAreaName;
+ ScRange aRange;
+ sal_uLong nRefreshDelay;
+};
+
+class ScUndoRemoveAreaLink : public ScSimpleUndo
+{
+public:
+ ScUndoRemoveAreaLink( ScDocShell* pShell,
+ OUString aDocName,
+ OUString aFltName, OUString aOptions,
+ OUString aAreaName, const ScRange& rDestRange,
+ sal_uLong nRefreshDelay );
+ virtual ~ScUndoRemoveAreaLink() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString aDocName;
+ OUString aFltName;
+ OUString aOptions;
+ OUString aAreaName;
+ ScRange aRange;
+ sal_uLong nRefreshDelay;
+};
+
+class ScUndoUpdateAreaLink : public ScSimpleUndo //! also change BlockUndo?
+{
+public:
+ ScUndoUpdateAreaLink(ScDocShell* pShell,
+ OUString aOldD,
+ OUString aOldF, OUString aOldO,
+ OUString aOldA, const ScRange& rOldR,
+ sal_uLong nOldRD,
+ OUString aNewD,
+ OUString aNewF, OUString aNewO,
+ OUString aNewA, const ScRange& rNewR,
+ sal_uLong nNewRD,
+ ScDocumentUniquePtr pUndo, ScDocumentUniquePtr pRedo,
+ bool bDoInsert);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString aOldDoc;
+ OUString aOldFlt;
+ OUString aOldOpt;
+ OUString aOldArea;
+ ScRange aOldRange;
+ OUString aNewDoc;
+ OUString aNewFlt;
+ OUString aNewOpt;
+ OUString aNewArea;
+ ScRange aNewRange;
+ ScDocumentUniquePtr xUndoDoc;
+ ScDocumentUniquePtr xRedoDoc;
+ sal_uLong nOldRefresh;
+ sal_uLong nNewRefresh;
+ bool bWithInsert;
+
+ void DoChange( const bool bUndo ) const;
+};
+
+class ScUndoIndent: public ScBlockUndo
+{
+public:
+ ScUndoIndent( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, bool bIncrement );
+ virtual ~ScUndoIndent() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ bool bIsIncrement;
+};
+
+class ScUndoTransliterate: public ScBlockUndo
+{
+public:
+ ScUndoTransliterate( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, TransliterationFlags nType );
+ virtual ~ScUndoTransliterate() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ TransliterationFlags
+ nTransliterationType;
+};
+
+class ScUndoClearItems: public ScBlockUndo
+{
+public:
+ ScUndoClearItems( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, const sal_uInt16* pW );
+ virtual ~ScUndoClearItems() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScMarkData aMarkData;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ std::unique_ptr<sal_uInt16[]>
+ pWhich;
+};
+
+class ScUndoRemoveBreaks: public ScSimpleUndo
+{
+public:
+ ScUndoRemoveBreaks( ScDocShell* pNewDocShell,
+ SCTAB nNewTab, ScDocumentUniquePtr pNewUndoDoc );
+ virtual ~ScUndoRemoveBreaks() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ ScDocumentUniquePtr
+ pUndoDoc;
+};
+
+class ScUndoRemoveMerge: public ScBlockUndo
+{
+public:
+ ScUndoRemoveMerge( ScDocShell* pNewDocShell,
+ const ScCellMergeOption& rOption,
+ ScDocumentUniquePtr pNewUndoDoc );
+ ScUndoRemoveMerge( ScDocShell* pNewDocShell,
+ const ScRange& rRange,
+ ScDocumentUniquePtr pNewUndoDoc );
+ virtual ~ScUndoRemoveMerge() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+ ScDocument* GetUndoDoc();
+ void AddCellMergeOption( const ScCellMergeOption& rOption );
+
+private:
+ void SetCurTab();
+
+ std::vector<ScCellMergeOption> maOptions;
+ ScDocumentUniquePtr pUndoDoc;
+};
+
+class ScUndoBorder: public ScBlockUndo
+{
+public:
+ ScUndoBorder(ScDocShell* pNewDocShell,
+ const ScRangeList& rRangeList,
+ ScDocumentUniquePtr pNewUndoDoc,
+ const SvxBoxItem& rNewOuter,
+ const SvxBoxInfoItem& rNewInner);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScRangeList> xRanges;
+ std::unique_ptr<SvxBoxItem> xOuter;
+ std::unique_ptr<SvxBoxInfoItem> xInner;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undocell.hxx b/sc/source/ui/inc/undocell.hxx
new file mode 100644
index 0000000000..bfc82abe97
--- /dev/null
+++ b/sc/source/ui/inc/undocell.hxx
@@ -0,0 +1,379 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "undobase.hxx"
+#include <detdata.hxx>
+#include <postit.hxx>
+#include <cellvalue.hxx>
+#include <cellvalues.hxx>
+#include <editeng/editobj.hxx>
+
+#include <memory>
+
+class ScDocShell;
+class ScPatternAttr;
+class ScRangeName;
+class ScFormulaCell;
+
+class ScUndoCursorAttr: public ScSimpleUndo
+{
+public:
+ ScUndoCursorAttr( ScDocShell* pNewDocShell,
+ SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
+ const ScPatternAttr* pOldPat, const ScPatternAttr* pNewPat,
+ const ScPatternAttr* pApplyPat );
+ virtual ~ScUndoCursorAttr() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+ /** once the objects are passed to this class, their life-cycle is
+ managed by this class; the calling function must pass new'ed
+ objects to this method. */
+ void SetEditData( std::unique_ptr<EditTextObject> pOld, std::unique_ptr<EditTextObject> pNew );
+
+private:
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ ScPatternAttr* pOldPattern;
+ ScPatternAttr* pNewPattern;
+ ScPatternAttr* pApplyPattern;
+ std::unique_ptr<EditTextObject> pOldEditData;
+ std::unique_ptr<EditTextObject> pNewEditData;
+
+ void DoChange( const ScPatternAttr* pWhichPattern, const std::unique_ptr<EditTextObject>& pEditData ) const;
+};
+
+class ScUndoEnterData: public ScSimpleUndo
+{
+public:
+
+ struct Value
+ {
+ SCTAB mnTab;
+ bool mbHasFormat;
+ sal_uInt32 mnFormat;
+ ScCellValue maCell;
+
+ Value();
+ };
+
+ typedef std::vector<Value> ValuesType;
+
+ ScUndoEnterData(
+ ScDocShell* pNewDocShell, const ScAddress& rPos,
+ ValuesType& rOldValues, OUString aNewStr, std::unique_ptr<EditTextObject> pObj );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+ virtual std::optional<ScRange> getAffectedRange() const override { return maPos; }
+
+private:
+ ValuesType maOldValues;
+
+ OUString maNewString;
+ std::unique_ptr<EditTextObject> mpNewEditData;
+ sal_uLong mnEndChangeAction;
+ ScAddress maPos;
+
+ void DoChange() const;
+ void SetChangeTrack();
+};
+
+class ScUndoEnterValue: public ScSimpleUndo
+{
+public:
+ ScUndoEnterValue(
+ ScDocShell* pNewDocShell, const ScAddress& rNewPos,
+ ScCellValue aUndoCell, double nVal );
+
+ virtual ~ScUndoEnterValue() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+ virtual std::optional<ScRange> getAffectedRange() const override { return aPos; }
+
+private:
+ ScAddress aPos;
+ ScCellValue maOldCell;
+ double nValue;
+ sal_uLong nEndChangeAction;
+
+ void SetChangeTrack();
+};
+
+class ScUndoSetCell : public ScSimpleUndo
+{
+public:
+ ScUndoSetCell( ScDocShell* pDocSh, const ScAddress& rPos, ScCellValue aOldVal, ScCellValue aNewVal );
+
+ virtual ~ScUndoSetCell() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat( SfxRepeatTarget& rTarget ) override;
+ virtual bool CanRepeat( SfxRepeatTarget& rTarget ) const override;
+ virtual OUString GetComment() const override;
+
+ virtual std::optional<ScRange> getAffectedRange() const override { return maPos; }
+
+private:
+ void SetChangeTrack();
+ void SetValue( const ScCellValue& rVal );
+ void MoveCursorToCell();
+
+private:
+ ScAddress maPos;
+ ScCellValue maOldValue;
+ ScCellValue maNewValue;
+ sal_uLong mnEndChangeAction;
+};
+
+class ScUndoPageBreak: public ScSimpleUndo
+{
+public:
+ ScUndoPageBreak( ScDocShell* pNewDocShell,
+ SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
+ bool bNewColumn, bool bNewInsert );
+ virtual ~ScUndoPageBreak() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ bool bColumn; // Column or row break
+ bool bInsert; // Insert or Delete
+
+ void DoChange( bool bInsert ) const;
+};
+
+class ScUndoPrintZoom: public ScSimpleUndo
+{
+public:
+ ScUndoPrintZoom( ScDocShell* pNewDocShell, SCTAB nT,
+ sal_uInt16 nOS, sal_uInt16 nOP, sal_uInt16 nNS, sal_uInt16 nNP );
+ virtual ~ScUndoPrintZoom() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ sal_uInt16 nOldScale;
+ sal_uInt16 nOldPages;
+ sal_uInt16 nNewScale;
+ sal_uInt16 nNewPages;
+
+ void DoChange( bool bUndo );
+};
+
+class ScUndoThesaurus: public ScSimpleUndo
+{
+public:
+ ScUndoThesaurus( ScDocShell* pNewDocShell,
+ SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
+ ScCellValue aOldText, ScCellValue aNewText );
+ virtual ~ScUndoThesaurus() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ sal_uLong nEndChangeAction;
+
+ ScCellValue maOldText;
+ ScCellValue maNewText;
+
+ void DoChange( bool bUndo, const ScCellValue& rText );
+ void SetChangeTrack( const ScCellValue& rOldCell );
+};
+
+/** Undo action for inserting, removing, and replacing a cell note. */
+class ScUndoReplaceNote : public ScSimpleUndo
+{
+public:
+
+ /** Constructs an undo action for inserting or removing a cell note. */
+ ScUndoReplaceNote(
+ ScDocShell& rDocShell,
+ const ScAddress& rPos,
+ const ScNoteData& rNoteData,
+ bool bInsert,
+ std::unique_ptr<SdrUndoAction> pDrawUndo );
+
+ /** Constructs an undo action for replacing a cell note with another. */
+ ScUndoReplaceNote(
+ ScDocShell& rDocShell,
+ const ScAddress& rPos,
+ ScNoteData aOldData,
+ ScNoteData aNewData,
+ std::unique_ptr<SdrUndoAction> pDrawUndo );
+
+ virtual ~ScUndoReplaceNote() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat( SfxRepeatTarget& rTarget ) override;
+ virtual bool CanRepeat( SfxRepeatTarget& rTarget ) const override;
+
+ virtual OUString GetComment() const override;
+
+ virtual std::optional<ScRange> getAffectedRange() const override { return maPos; }
+
+private:
+ void DoInsertNote( const ScNoteData& rNoteData );
+ void DoRemoveNote( const ScNoteData& rNoteData );
+
+private:
+ ScAddress maPos;
+ ScNoteData maOldData;
+ ScNoteData maNewData;
+ std::unique_ptr<SdrUndoAction> mpDrawUndo;
+};
+
+/** Undo action for showing or hiding a cell note caption. */
+class ScUndoShowHideNote : public ScSimpleUndo
+{
+public:
+ ScUndoShowHideNote( ScDocShell& rDocShell, const ScAddress& rPos, bool bShow );
+ virtual ~ScUndoShowHideNote() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat( SfxRepeatTarget& rTarget ) override;
+ virtual bool CanRepeat( SfxRepeatTarget& rTarget ) const override;
+
+ virtual OUString GetComment() const override;
+
+ virtual std::optional<ScRange> getAffectedRange() const override { return maPos; }
+
+private:
+ ScAddress maPos;
+ bool mbShown;
+};
+
+class ScUndoDetective: public ScSimpleUndo
+{
+public:
+ ScUndoDetective( ScDocShell* pNewDocShell,
+ std::unique_ptr<SdrUndoAction> pDraw, const ScDetOpData* pOperation,
+ std::unique_ptr<ScDetOpList> pUndoList = nullptr );
+ virtual ~ScUndoDetective() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ bool bIsDelete;
+ std::unique_ptr<ScDetOpList> pOldList;
+ sal_uInt16 nAction;
+ ScAddress aPos;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+};
+
+class ScUndoRangeNames: public ScSimpleUndo
+{
+public:
+ //use nTab = -1 for global range names
+ ScUndoRangeNames( ScDocShell* pNewDocShell,
+ std::unique_ptr<ScRangeName> pOld, std::unique_ptr<ScRangeName> pNew , SCTAB nTab);
+ virtual ~ScUndoRangeNames() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr<ScRangeName> pOldRanges;
+ std::unique_ptr<ScRangeName> pNewRanges;
+ SCTAB mnTab;
+
+ void DoChange( bool bUndo );
+};
+
+namespace sc {
+
+class UndoSetCells : public ScSimpleUndo
+{
+ ScAddress maTopPos;
+ CellValues maOldValues;
+ CellValues maNewValues;
+
+ void DoChange( const CellValues& rValues );
+
+public:
+ UndoSetCells( ScDocShell* pDocSh, const ScAddress& rTopPos );
+ virtual ~UndoSetCells() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ virtual bool CanRepeat( SfxRepeatTarget& ) const override;
+ virtual OUString GetComment() const override;
+
+ CellValues& GetOldValues() { return maOldValues;}
+ void SetNewValues( const std::vector<double>& rVals );
+ void SetNewValues( const std::vector<ScFormulaCell*>& rVals );
+};
+
+} // namespace sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undoconvert.hxx b/sc/source/ui/inc/undoconvert.hxx
new file mode 100644
index 0000000000..428710d836
--- /dev/null
+++ b/sc/source/ui/inc/undoconvert.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/.
+ */
+
+#pragma once
+
+#include "undobase.hxx"
+#include <cellvalues.hxx>
+
+namespace sc
+{
+class UndoFormulaToValue : public ScSimpleUndo
+{
+ TableValues maUndoValues;
+
+public:
+ UndoFormulaToValue(ScDocShell* pDocSh, TableValues& rUndoValues);
+
+ virtual OUString GetComment() const override;
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ void Execute();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undodat.hxx b/sc/source/ui/inc/undodat.hxx
new file mode 100644
index 0000000000..5060bf0c10
--- /dev/null
+++ b/sc/source/ui/inc/undodat.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "undobase.hxx"
+#include "refundo.hxx"
+#include <dpobject.hxx>
+#include <olinetab.hxx>
+#include <rangeutl.hxx>
+#include <rangelst.hxx>
+#include <queryparam.hxx>
+#include <subtotalparam.hxx>
+
+#include <memory>
+
+class ScDocShell;
+class SdrUndoAction;
+
+class ScUndoDoOutline: public ScSimpleUndo
+{
+public:
+ ScUndoDoOutline( ScDocShell* pNewDocShell,
+ SCCOLROW nNewStart, SCCOLROW nNewEnd, SCTAB nNewTab,
+ ScDocumentUniquePtr pNewUndoDoc, bool bNewColumns,
+ sal_uInt16 nNewLevel, sal_uInt16 nNewEntry, bool bNewShow );
+ virtual ~ScUndoDoOutline() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ SCTAB nTab;
+ ScDocumentUniquePtr
+ pUndoDoc;
+ bool bColumns;
+ sal_uInt16 nLevel;
+ sal_uInt16 nEntry;
+ bool bShow;
+};
+
+class ScUndoMakeOutline: public ScSimpleUndo
+{
+public:
+ ScUndoMakeOutline( ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ bool bNewColumns, bool bNewMake );
+ virtual ~ScUndoMakeOutline() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScAddress aBlockStart;
+ ScAddress aBlockEnd;
+ std::unique_ptr<ScOutlineTable>
+ pUndoTable;
+ bool bColumns;
+ bool bMake;
+};
+
+class ScUndoOutlineLevel: public ScSimpleUndo
+{
+public:
+ ScUndoOutlineLevel(ScDocShell* pNewDocShell,
+ SCCOLROW nNewStart, SCCOLROW nNewEnd, SCTAB nNewTab,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ bool bNewColumns, sal_uInt16 nNewLevel);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ SCTAB nTab;
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScOutlineTable> xUndoTable;
+ bool bColumns;
+ sal_uInt16 nLevel;
+};
+
+class ScUndoOutlineBlock: public ScSimpleUndo
+{
+public:
+ ScUndoOutlineBlock(ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ bool bNewShow);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScAddress aBlockStart;
+ ScAddress aBlockEnd;
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScOutlineTable> xUndoTable;
+ bool bShow;
+};
+
+class ScUndoRemoveAllOutlines: public ScSimpleUndo
+{
+public:
+ ScUndoRemoveAllOutlines(ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScAddress aBlockStart;
+ ScAddress aBlockEnd;
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScOutlineTable> xUndoTable;
+};
+
+class ScUndoAutoOutline: public ScSimpleUndo
+{
+public:
+ ScUndoAutoOutline(ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScAddress aBlockStart;
+ ScAddress aBlockEnd;
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScOutlineTable> xUndoTable;
+};
+
+class ScUndoSubTotals: public ScDBFuncUndo
+{
+public:
+ ScUndoSubTotals(ScDocShell* pNewDocShell, SCTAB nNewTab,
+ const ScSubTotalParam& rNewParam, SCROW nNewEndY,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ std::unique_ptr<ScRangeName> pNewUndoRange, std::unique_ptr<ScDBCollection> pNewUndoDB);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ ScSubTotalParam aParam; // The original passed parameter
+ SCROW nNewEndRow; // Size of result
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScOutlineTable> xUndoTable;
+ std::unique_ptr<ScRangeName> xUndoRange;
+ std::unique_ptr<ScDBCollection> xUndoDB;
+};
+
+class ScUndoQuery: public ScDBFuncUndo
+{
+public:
+ ScUndoQuery( ScDocShell* pNewDocShell, SCTAB nNewTab,
+ const ScQueryParam& rParam, ScDocumentUniquePtr pNewUndoDoc,
+ std::unique_ptr<ScDBCollection> pNewUndoDB, const ScRange* pOld,
+ bool bSize, const ScRange* pAdvSrc );
+ virtual ~ScUndoQuery() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+ SCTAB nTab;
+ ScQueryParam aQueryParam;
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScDBCollection> xUndoDB; // due to source and target range
+ ScRange aOldDest;
+ ScRange aAdvSource;
+ bool bIsAdvanced;
+ bool bDestArea;
+ bool bDoSize;
+};
+
+class ScUndoAutoFilter: public ScDBFuncUndo
+{
+private:
+ OUString aDBName;
+ bool bFilterSet;
+
+ void DoChange( bool bUndo );
+
+public:
+ ScUndoAutoFilter( ScDocShell* pNewDocShell, const ScRange& rRange,
+ OUString aName, bool bSet );
+ virtual ~ScUndoAutoFilter() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+};
+
+class ScUndoDBData: public ScSimpleUndo
+{
+public:
+ ScUndoDBData( ScDocShell* pNewDocShell,
+ std::unique_ptr<ScDBCollection> pNewUndoColl,
+ std::unique_ptr<ScDBCollection> pNewRedoColl );
+ virtual ~ScUndoDBData() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr<ScDBCollection> pUndoColl;
+ std::unique_ptr<ScDBCollection> pRedoColl;
+};
+
+class ScUndoImportData: public ScSimpleUndo
+{
+public:
+ ScUndoImportData(ScDocShell* pNewDocShell, SCTAB nNewTab,
+ const ScImportParam& rParam, SCCOL nNewEndX, SCROW nNewEndY,
+ SCCOL nNewFormula,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
+ std::unique_ptr<ScDBData> pNewUndoData, std::unique_ptr<ScDBData> pNewRedoData);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ ScImportParam aImportParam;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ ScDocumentUniquePtr xUndoDoc;
+ ScDocumentUniquePtr xRedoDoc;
+ std::unique_ptr<ScDBData> xUndoDBData;
+ std::unique_ptr<ScDBData> xRedoDBData;
+ SCCOL nFormulaCols;
+ bool bRedoFilled;
+};
+
+class ScUndoRepeatDB: public ScSimpleUndo
+{
+public:
+ ScUndoRepeatDB(ScDocShell* pNewDocShell, SCTAB nNewTab,
+ SCCOL nStartX, SCROW nStartY, SCCOL nEndX, SCROW nEndY,
+ SCROW nResultEndRow, SCCOL nCurX, SCROW nCurY,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ std::unique_ptr<ScRangeName> pNewUndoRange, std::unique_ptr<ScDBCollection> pNewUndoDB,
+ const ScRange* pOldQ, const ScRange* pNewQ);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScAddress aBlockStart;
+ ScAddress aBlockEnd;
+ SCROW nNewEndRow;
+ ScAddress aCursorPos;
+ ScDocumentUniquePtr xUndoDoc;
+ std::unique_ptr<ScOutlineTable> xUndoTable;
+ std::unique_ptr<ScRangeName> xUndoRange;
+ std::unique_ptr<ScDBCollection> xUndoDB;
+ ScRange aOldQuery;
+ ScRange aNewQuery;
+ bool bQuerySize;
+};
+
+class ScUndoDataPilot: public ScSimpleUndo
+{
+public:
+ ScUndoDataPilot(ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pOldDoc, ScDocumentUniquePtr pNewDoc,
+ const ScDPObject* pOldObj, const ScDPObject* pNewObj,
+ bool bMove);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScDocumentUniquePtr xOldUndoDoc;
+ ScDocumentUniquePtr xNewUndoDoc;
+ std::unique_ptr<ScDPObject> xOldDPObject;
+ std::unique_ptr<ScDPObject> xNewDPObject;
+ bool bAllowMove;
+};
+
+class ScUndoConsolidate: public ScSimpleUndo
+{
+public:
+ ScUndoConsolidate(ScDocShell* pNewDocShell,
+ const ScArea& rArea, const ScConsolidateParam& rPar,
+ ScDocumentUniquePtr pNewUndoDoc, bool bReference,
+ SCROW nInsCount, std::unique_ptr<ScOutlineTable> pTab,
+ std::unique_ptr<ScDBData> pData);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScArea aDestArea;
+ ScDocumentUniquePtr xUndoDoc;
+ ScConsolidateParam aParam;
+ bool bInsRef;
+ SCSIZE nInsertCount;
+ std::unique_ptr<ScOutlineTable> xUndoTab;
+ std::unique_ptr<ScDBData> xUndoData;
+};
+
+class ScUndoChartData: public ScSimpleUndo
+{
+public:
+ ScUndoChartData( ScDocShell* pNewDocShell,
+ OUString aName, const ScRange& rNew,
+ bool bColHdr, bool bRowHdr, bool bAdd );
+ ScUndoChartData( ScDocShell* pNewDocShell,
+ OUString aName, ScRangeListRef xNew,
+ bool bColHdr, bool bRowHdr, bool bAdd );
+ virtual ~ScUndoChartData() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString aChartName;
+ ScRangeListRef aOldRangeListRef;
+ bool bOldColHeaders;
+ bool bOldRowHeaders;
+// ScRange aNewRange;
+ ScRangeListRef aNewRangeListRef;
+ bool bNewColHeaders;
+ bool bNewRowHeaders;
+ bool bAddRange;
+
+ void Init();
+};
+
+class ScUndoDataForm: public ScBlockUndo
+{
+public:
+ ScUndoDataForm(ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
+ std::unique_ptr<ScRefUndoData> pRefData);
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr<ScMarkData> mxMarkData;
+ ScDocumentUniquePtr xUndoDoc;
+ ScDocumentUniquePtr xRedoDoc;
+ std::unique_ptr<ScRefUndoData> xRefUndoData;
+ std::unique_ptr<ScRefUndoData> xRefRedoData;
+ bool bRedoFilled;
+
+ void DoChange( const bool bUndo );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undodraw.hxx b/sc/source/ui/inc/undodraw.hxx
new file mode 100644
index 0000000000..b12eebdb3e
--- /dev/null
+++ b/sc/source/ui/inc/undodraw.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <svl/undo.hxx>
+
+class ScDocShell;
+
+class ScUndoDraw: public SfxUndoAction
+{
+ std::unique_ptr<SfxUndoAction> pDrawUndo;
+ ScDocShell* pDocShell;
+ ViewShellId mnViewShellId;
+
+ void UpdateSubShell();
+
+public:
+ ScUndoDraw( std::unique_ptr<SfxUndoAction> pUndo, ScDocShell* pDocSh );
+ virtual ~ScUndoDraw() override;
+
+ std::unique_ptr<SfxUndoAction> ReleaseDrawUndo() { return std::move(pDrawUndo); }
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+ virtual OUString GetComment() const override;
+ virtual OUString GetRepeatComment(SfxRepeatTarget&) const override;
+ /// See SfxUndoAction::GetViewShellId().
+ ViewShellId GetViewShellId() const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undomanager.hxx b/sc/source/ui/inc/undomanager.hxx
new file mode 100644
index 0000000000..03168dfab9
--- /dev/null
+++ b/sc/source/ui/inc/undomanager.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <svx/sdrundomanager.hxx>
+
+class SfxViewShell;
+class ScSimpleUndo;
+
+class SC_DLLPUBLIC ScUndoManager : public SdrUndoManager
+{
+public:
+ ~ScUndoManager();
+
+ /**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action. Sets rOffset to the offset of that independent undo action on success.
+ */
+ bool IsViewUndoActionIndependent(const SfxViewShell* pView, sal_uInt16& rOffset) const;
+
+ /// Make these public
+ using SdrUndoManager::UndoWithContext;
+ using SdrUndoManager::RedoWithContext;
+
+private:
+ static std::optional<ScRange> getAffectedRangeFromUndo(const SfxUndoAction*);
+ static const ScSimpleUndo* getScSimpleUndo(const SfxUndoAction*);
+};
+
+class ScUndoRedoContext final : public SfxUndoContext
+{
+public:
+ void SetUndoOffset(size_t nUndoOffset) { m_nUndoOffset = nUndoOffset; }
+
+ size_t GetUndoOffset() override { return m_nUndoOffset; }
+
+private:
+ size_t m_nUndoOffset = 0;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/source/ui/inc/undoolk.hxx b/sc/source/ui/inc/undoolk.hxx
new file mode 100644
index 0000000000..67d5194380
--- /dev/null
+++ b/sc/source/ui/inc/undoolk.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 .
+ */
+
+#pragma once
+
+#include <memory>
+
+class SdrUndoAction;
+class ScDocument;
+
+std::unique_ptr<SdrUndoAction> GetSdrUndoAction( ScDocument* pDoc );
+void DoSdrUndoAction ( SdrUndoAction* pUndoAction, ScDocument* pDoc );
+void RedoSdrUndoAction ( SdrUndoAction* pUndoAction );
+void EnableDrawAdjust ( ScDocument* pDoc, bool bEnable );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undosort.hxx b/sc/source/ui/inc/undosort.hxx
new file mode 100644
index 0000000000..40ba12cac9
--- /dev/null
+++ b/sc/source/ui/inc/undosort.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include "undobase.hxx"
+#include <sortparam.hxx>
+
+namespace sc {
+
+class UndoSort : public ScSimpleUndo
+{
+ ReorderParam maParam;
+
+public:
+ UndoSort( ScDocShell* pDocSh, ReorderParam aParam );
+
+ virtual OUString GetComment() const override;
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ void Execute( bool bUndo );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undostyl.hxx b/sc/source/ui/inc/undostyl.hxx
new file mode 100644
index 0000000000..39b55c0bda
--- /dev/null
+++ b/sc/source/ui/inc/undostyl.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <optional>
+#include <svl/style.hxx>
+#include <svl/itemset.hxx>
+#include "undobase.hxx"
+
+class ScDocShell;
+
+class ScStyleSaveData
+{
+private:
+ OUString aName;
+ OUString aParent;
+ std::optional<SfxItemSet> moItems;
+
+public:
+ ScStyleSaveData();
+ ScStyleSaveData( const ScStyleSaveData& rOther );
+ ScStyleSaveData& operator=( const ScStyleSaveData& rOther );
+
+ void InitFromStyle( const SfxStyleSheetBase* pSource );
+
+ const OUString& GetName() const { return aName; }
+ const OUString& GetParent() const { return aParent; }
+ const std::optional<SfxItemSet>& GetItems() const { return moItems; }
+};
+
+class ScUndoModifyStyle: public ScSimpleUndo
+{
+private:
+ SfxStyleFamily eFamily;
+ ScStyleSaveData aOldData;
+ ScStyleSaveData aNewData;
+
+ static void DoChange( ScDocShell* pDocSh,
+ const OUString& rName, SfxStyleFamily eStyleFamily,
+ const ScStyleSaveData& rData );
+
+public:
+ ScUndoModifyStyle( ScDocShell* pDocSh,
+ SfxStyleFamily eFam,
+ const ScStyleSaveData& rOld,
+ const ScStyleSaveData& rNew );
+ virtual ~ScUndoModifyStyle() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+};
+
+class ScUndoApplyPageStyle: public ScSimpleUndo
+{
+public:
+ ScUndoApplyPageStyle( ScDocShell* pDocSh, OUString aNewStyle );
+ virtual ~ScUndoApplyPageStyle() override;
+
+ void AddSheetAction( SCTAB nTab, const OUString& rOld );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ struct ApplyStyleEntry
+ {
+ SCTAB mnTab;
+ OUString maOldStyle;
+ explicit ApplyStyleEntry( SCTAB nTab, OUString aOldStyle );
+ };
+ typedef ::std::vector< ApplyStyleEntry > ApplyStyleVec;
+
+ ApplyStyleVec maEntries;
+ OUString maNewStyle;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undotab.hxx b/sc/source/ui/inc/undotab.hxx
new file mode 100644
index 0000000000..ce9a9d190f
--- /dev/null
+++ b/sc/source/ui/inc/undotab.hxx
@@ -0,0 +1,471 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 "undobase.hxx"
+#include <tools/color.hxx>
+#include <tabbgcolor.hxx>
+
+#include <memory>
+#include <vector>
+
+class ScDocShell;
+class SdrUndoAction;
+class ScPrintRangeSaver;
+class SdrObject;
+class ScDocProtection;
+class ScTableProtection;
+class ScMarkData;
+
+class ScUndoInsertTab : public ScSimpleUndo
+{
+public:
+ ScUndoInsertTab(
+ ScDocShell* pNewDocShell,
+ SCTAB nTabNum,
+ bool bApp,
+ OUString aNewName);
+ virtual ~ScUndoInsertTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString sNewName;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+ sal_uLong nEndChangeAction;
+ SCTAB nTab;
+ bool bAppend;
+
+ void SetChangeTrack();
+};
+
+class ScUndoInsertTables : public ScSimpleUndo
+{
+public:
+ ScUndoInsertTables(
+ ScDocShell* pNewDocShell,
+ SCTAB nTabNum,
+ std::vector<OUString>&& newNameList);
+ virtual ~ScUndoInsertTables() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+ std::vector<OUString> aNameList;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+ SCTAB nTab;
+
+ void SetChangeTrack();
+};
+
+class ScUndoDeleteTab: public ScMoveUndo
+{
+public:
+ ScUndoDeleteTab(
+ ScDocShell* pNewDocShell,
+ const std::vector<SCTAB> &theTabs, //SCTAB nNewTab,
+ ScDocumentUniquePtr pUndoDocument,
+ std::unique_ptr<ScRefUndoData> pRefData );
+ virtual ~ScUndoDeleteTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::vector<SCTAB> theTabs;
+ sal_uLong nStartChangeAction;
+ sal_uLong nEndChangeAction;
+
+ void SetChangeTrack();
+};
+
+class ScUndoRenameTab: public ScSimpleUndo
+{
+public:
+ ScUndoRenameTab(
+ ScDocShell* pNewDocShell,
+ SCTAB nT,
+ const OUString& rOldName,
+ const OUString& rNewName);
+ virtual ~ScUndoRenameTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ OUString sOldName;
+ OUString sNewName;
+
+ void DoChange( SCTAB nTab, const OUString& rName ) const;
+};
+
+class ScUndoMoveTab: public ScSimpleUndo
+{
+public:
+ ScUndoMoveTab(
+ ScDocShell* pNewDocShell,
+ std::unique_ptr<std::vector<SCTAB>> pOldTabs,
+ std::unique_ptr<std::vector<SCTAB>> pNewTabs,
+ std::unique_ptr<std::vector< OUString>> pOldNames = nullptr,
+ std::unique_ptr<std::vector< OUString>> pNewNames = nullptr );
+
+ virtual ~ScUndoMoveTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr< ::std::vector<SCTAB> > mpOldTabs;
+ std::unique_ptr< ::std::vector<SCTAB> > mpNewTabs;
+ std::unique_ptr< ::std::vector< OUString> > mpOldNames;
+ std::unique_ptr< ::std::vector< OUString> > mpNewNames;
+
+ void DoChange( bool bUndo ) const;
+};
+
+class ScUndoCopyTab: public ScSimpleUndo
+{
+public:
+ ScUndoCopyTab(
+ ScDocShell* pNewDocShell,
+ std::unique_ptr<std::vector<SCTAB>> pOldTabs,
+ std::unique_ptr<std::vector<SCTAB>> pNewTabs,
+ std::unique_ptr<std::vector< OUString>> pNewNames = nullptr );
+
+ virtual ~ScUndoCopyTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr< ::std::vector<SCTAB> > mpOldTabs;
+ std::unique_ptr< ::std::vector<SCTAB> > mpNewTabs;
+ std::unique_ptr< ::std::vector< OUString> > mpNewNames;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+
+ void DoChange() const;
+};
+
+class ScUndoTabColor: public ScSimpleUndo
+{
+public:
+ ScUndoTabColor(
+ ScDocShell* pNewDocShell,
+ SCTAB nT,
+ const Color& aOTabBgColor,
+ const Color& aNTabBgColor);
+ ScUndoTabColor(
+ ScDocShell* pNewDocShell,
+ ScUndoTabColorInfo::List&& rUndoTabColorList);
+ virtual ~ScUndoTabColor() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ScUndoTabColorInfo::List aTabColorList;
+
+ void DoChange(bool bUndoType) const;
+};
+
+class ScUndoMakeScenario: public ScSimpleUndo
+{
+public:
+ ScUndoMakeScenario(
+ ScDocShell* pNewDocShell,
+ SCTAB nSrc, SCTAB nDest,
+ OUString aN, OUString aC,
+ const Color& rCol, ScScenarioFlags nF,
+ const ScMarkData& rMark );
+ virtual ~ScUndoMakeScenario() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::unique_ptr<ScMarkData> mpMarkData;
+ SCTAB nSrcTab;
+ SCTAB nDestTab;
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+};
+
+class ScUndoImportTab : public ScSimpleUndo
+{
+public:
+ ScUndoImportTab(
+ ScDocShell* pShell,
+ SCTAB nNewTab, SCTAB nNewCount );
+ virtual ~ScUndoImportTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ SCTAB nCount;
+ ScDocumentUniquePtr xRedoDoc;
+ std::unique_ptr<SdrUndoAction> pDrawUndo;
+
+ void DoChange() const;
+};
+
+class ScUndoRemoveLink : public ScSimpleUndo
+{
+public:
+ ScUndoRemoveLink( // Call before delete!
+ ScDocShell* pShell,
+ OUString aDoc );
+ virtual ~ScUndoRemoveLink() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString aDocName;
+ OUString aFltName;
+ OUString aOptions;
+ sal_uLong nRefreshDelay;
+ sal_uInt16 nCount;
+ std::unique_ptr<SCTAB[]>
+ pTabs;
+ std::unique_ptr<ScLinkMode[]>
+ pModes;
+ std::unique_ptr<OUString[]>
+ pTabNames;
+
+ void DoChange( bool bLink ) const;
+};
+
+class ScUndoShowHideTab : public ScSimpleUndo
+{
+public:
+ ScUndoShowHideTab(
+ ScDocShell* pShell,
+ std::vector<SCTAB>&& newUndoTabs,
+ bool bNewShow );
+ virtual ~ScUndoShowHideTab() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ std::vector<SCTAB> undoTabs;
+ bool bShow;
+
+ void DoChange( bool bShow ) const;
+};
+
+/** This class implements undo & redo of document protect & unprotect
+ operations. */
+class ScUndoDocProtect : public ScSimpleUndo
+{
+public:
+ ScUndoDocProtect(ScDocShell* pShell, ::std::unique_ptr<ScDocProtection> && pProtectSettings);
+ virtual ~ScUndoDocProtect() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ ::std::unique_ptr<ScDocProtection> mpProtectSettings;
+
+ void DoProtect(bool bProtect);
+};
+
+/** This class implements undo & redo of both protect and unprotect of
+ sheet. */
+class ScUndoTabProtect : public ScSimpleUndo
+{
+public:
+ ScUndoTabProtect(ScDocShell* pShell, SCTAB nTab,
+ std::unique_ptr<ScTableProtection> && pProtectSettings);
+ virtual ~ScUndoTabProtect() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB mnTab;
+ ::std::unique_ptr<ScTableProtection> mpProtectSettings;
+
+ void DoProtect(bool bProtect);
+};
+
+class ScUndoPrintRange : public ScSimpleUndo
+{
+public:
+ ScUndoPrintRange( ScDocShell* pShell, SCTAB nNewTab,
+ std::unique_ptr<ScPrintRangeSaver> pOld,
+ std::unique_ptr<ScPrintRangeSaver> pNew );
+ virtual ~ScUndoPrintRange() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges;
+ std::unique_ptr<ScPrintRangeSaver> pNewRanges;
+
+ void DoChange( bool bUndo );
+};
+
+class ScUndoScenarioFlags: public ScSimpleUndo
+{
+public:
+ ScUndoScenarioFlags(
+ ScDocShell* pNewDocShell, SCTAB nT,
+ OUString aON, OUString aNN,
+ OUString aOC, OUString aNC,
+ const Color& rOCol, const Color& rNCol,
+ ScScenarioFlags nOF, ScScenarioFlags nNF);
+
+ virtual ~ScUndoScenarioFlags() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ OUString aOldName;
+ OUString aNewName;
+ OUString aOldComment;
+ OUString aNewComment;
+ Color aOldColor;
+ Color aNewColor;
+ ScScenarioFlags nOldFlags;
+ ScScenarioFlags nNewFlags;
+};
+
+class ScUndoRenameObject: public ScSimpleUndo
+{
+public:
+ ScUndoRenameObject(
+ ScDocShell* pNewDocShell, OUString aPN,
+ OUString aON, OUString aNN );
+
+ virtual ~ScUndoRenameObject() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ OUString aPersistName; // to find object (works only for OLE objects)
+ OUString aOldName;
+ OUString aNewName;
+
+ SdrObject* GetObject();
+};
+
+class ScUndoLayoutRTL : public ScSimpleUndo
+{
+public:
+ ScUndoLayoutRTL( ScDocShell* pShell, SCTAB nNewTab, bool bNewRTL );
+ virtual ~ScUndoLayoutRTL() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual void Repeat(SfxRepeatTarget& rTarget) override;
+ virtual bool CanRepeat(SfxRepeatTarget& rTarget) const override;
+
+ virtual OUString GetComment() const override;
+
+private:
+ SCTAB nTab;
+ bool bRTL;
+
+ void DoChange( bool bNew );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/undoutil.hxx b/sc/source/ui/inc/undoutil.hxx
new file mode 100644
index 0000000000..59887e5705
--- /dev/null
+++ b/sc/source/ui/inc/undoutil.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 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+
+class ScDocShell;
+class ScDBData;
+class ScDocument;
+
+class ScUndoUtil
+{
+public:
+ /** Mark Block (invisible - has to be repainted) */
+ static void MarkSimpleBlock( const ScDocShell* pDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ );
+ static void MarkSimpleBlock( const ScDocShell* pDocShell,
+ const ScAddress& rBlockStart,
+ const ScAddress& rBlockEnd );
+ static void MarkSimpleBlock( const ScDocShell* pDocShell,
+ const ScRange& rRange );
+
+ static void PaintMore( ScDocShell* pDocShell,
+ const ScRange& rRange );
+
+ /** Search for Data base range in Document ("untitled" or by region)
+ create new if not found */
+ static ScDBData* GetOldDBData( const ScDBData* pUndoData, ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/validate.hxx b/sc/source/ui/inc/validate.hxx
new file mode 100644
index 0000000000..ee97afe138
--- /dev/null
+++ b/sc/source/ui/inc/validate.hxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sfx2/tabdlg.hxx>
+
+#include "anyrefdg.hxx"
+#include <sc.hrc>
+#include <scmod.hxx>
+
+struct ScRefHandlerCaller{
+ virtual ~ScRefHandlerCaller(){}
+};
+
+class ScRefHandlerHelper
+{
+protected:
+ ScRefHandlerCaller* m_pHandler;
+// workaround VS2013 issue with pointers to things that contain virtual base class
+#ifdef _WIN32
+ #pragma pack(push, 16)
+#endif
+ void (ScRefHandlerCaller::*m_pSetReferenceHdl)( const ScRange& , const ScDocument& );
+ void (ScRefHandlerCaller::*m_pSetActiveHdl)();
+ void (ScRefHandlerCaller::*m_pRefInputStartPreHdl)( formula::RefEdit* pEdit, const formula::RefButton* pButton );
+ void (ScRefHandlerCaller::*m_pRefInputDonePostHdl)();
+#if defined( _WIN32)
+ #pragma pack(pop)
+#endif
+
+public:
+ typedef void (ScRefHandlerCaller::*PFUNCSETREFHDLTYPE)( const ScRange& , const ScDocument& );
+ typedef void (ScRefHandlerCaller::*PCOMMONHDLTYPE)();
+ typedef void (ScRefHandlerCaller::*PINPUTSTARTDLTYPE)( formula::RefEdit* pEdit, const formula::RefButton* pButton );
+
+ void SetSetRefHdl( PFUNCSETREFHDLTYPE pNewHdl )
+ {
+ m_pSetReferenceHdl = pNewHdl;
+ }
+
+ void SetSetActHdl( PCOMMONHDLTYPE pNewHdl )
+ {
+ m_pSetActiveHdl = pNewHdl;
+ }
+
+ void SetHandler( ScRefHandlerCaller *pNewHandler )
+ {
+ m_pHandler = pNewHandler;
+ }
+ void SetRefInputStartPreHdl( PINPUTSTARTDLTYPE pNewHdl ){ m_pRefInputStartPreHdl = pNewHdl; }
+ void SetRefInputDonePostHdl( void (ScRefHandlerCaller::*pNewHdl)() ){ m_pRefInputDonePostHdl = pNewHdl; }
+
+ ScRefHandlerHelper():m_pHandler(nullptr), m_pSetReferenceHdl( nullptr ), m_pSetActiveHdl(nullptr), m_pRefInputStartPreHdl( nullptr ), m_pRefInputDonePostHdl( nullptr ){}
+};
+
+class ScValidationDlg;
+
+/** The tab page "Criteria" from the Validation dialog. */
+class ScTPValidationValue : public ScRefHandlerCaller, public SfxTabPage
+{
+ static const WhichRangesContainer pValueRanges;
+public:
+ explicit ScTPValidationValue(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet );
+ virtual ~ScTPValidationValue() override;
+
+ static const WhichRangesContainer& GetRanges() { return pValueRanges; }
+
+ virtual bool FillItemSet( SfxItemSet* rArgSet ) override;
+ virtual void Reset( const SfxItemSet* rArgSet ) override;
+
+private:
+ void Init();
+
+ OUString GetFirstFormula() const;
+ OUString GetSecondFormula() const;
+
+ void SetFirstFormula( const OUString& rFmlaStr );
+ void SetSecondFormula( const OUString& rFmlaStr );
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(CheckHdl, weld::Toggleable&, void);
+
+ OUString maStrMin;
+ OUString maStrMax;
+ OUString maStrValue;
+ OUString maStrFormula;
+ OUString maStrRange;
+ OUString maStrList;
+ sal_Unicode mcFmlaSep; /// List separator in formulas.
+
+ DECL_LINK( EditSetFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( KillEditFocusHdl, formula::RefEdit&, void );
+ DECL_LINK( KillButtonFocusHdl, formula::RefButton&, void );
+ DECL_LINK( ClickHdl, formula::RefButton&, void );
+
+ formula::RefEdit* m_pRefEdit;
+
+ std::unique_ptr<weld::ComboBox> m_xLbAllow;
+ std::unique_ptr<weld::CheckButton> m_xCbAllow; /// Allow blank cells.
+ std::unique_ptr<weld::CheckButton> m_xCbShow; /// Show selection list in cell.
+ std::unique_ptr<weld::CheckButton> m_xCbSort; /// Sort selection list in cell.
+ std::unique_ptr<weld::Label> m_xFtValue;
+ std::unique_ptr<weld::ComboBox> m_xLbValue;
+ std::unique_ptr<weld::Label> m_xFtMin;
+ std::unique_ptr<weld::Widget> m_xMinGrid;
+ std::unique_ptr<formula::RefEdit> m_xEdMin;
+ std::unique_ptr<weld::TextView> m_xEdList; /// Entries for explicit list
+ std::unique_ptr<weld::Label> m_xFtMax;
+ std::unique_ptr<formula::RefEdit> m_xEdMax;
+ std::unique_ptr<weld::Label> m_xFtHint; /// Hint text for cell range validity.
+ std::unique_ptr<formula::RefButton> m_xBtnRef;
+ std::unique_ptr<weld::Container> m_xRefGrid;
+
+ weld::Container* m_pRefEditParent;
+ weld::Container* m_pBtnRefParent;
+
+ void SetReferenceHdl( const ScRange& , const ScDocument& );
+ void SetActiveHdl();
+ void RefInputStartPreHdl(formula::RefEdit* pEdit, const formula::RefButton* pButton);
+ void RefInputDonePostHdl();
+ ScValidationDlg * GetValidationDlg();
+public:
+ void SetupRefDlg();
+ void RemoveRefDlg(bool bRestoreModal);
+};
+
+/** The "Validity" tab dialog. */
+class ScValidationDlg
+ : public ScRefHdlrControllerImpl<SfxTabDialogController, false>
+ , public ScRefHandlerHelper
+{
+ typedef ScRefHdlrControllerImpl<SfxTabDialogController, false> ScValidationDlgBase;
+
+ ScTabViewShell * m_pTabVwSh;
+ OUString m_sValuePageId;
+ bool m_bOwnRefHdlr:1;
+ bool m_bRefInputting:1;
+
+ std::unique_ptr<weld::Container> m_xHBox;
+
+ bool EnterRefStatus();
+ bool LeaveRefStatus();
+
+public:
+ explicit ScValidationDlg(weld::Window* pParent, const SfxItemSet* pArgSet, ScTabViewShell* pTabViewSh);
+ virtual ~ScValidationDlg() override;
+ static std::shared_ptr<SfxDialogController> Find1AliveObject(const weld::Window *pAncestor)
+ {
+ return SC_MOD()->Find1RefWindow(SLOTID, pAncestor);
+ }
+ ScTabViewShell *GetTabViewShell()
+ {
+ return m_pTabVwSh;
+ }
+
+ bool SetupRefDlg();
+ bool RemoveRefDlg(bool bRestoreModal);
+
+ void SetModal(bool bModal) { m_xDialog->set_modal(bModal); }
+
+ virtual void EndDialog(int nResponse) override;
+
+ virtual bool CloseOnHide() const override { return false; }
+
+ virtual void SetReference( const ScRange& rRef, ScDocument& rDoc ) override
+ {
+ if ( m_pHandler && m_pSetReferenceHdl )
+ (m_pHandler->*m_pSetReferenceHdl)( rRef, rDoc );
+ }
+
+ virtual void SetActive() override
+ {
+ if ( m_pHandler && m_pSetActiveHdl )
+ (m_pHandler->*m_pSetActiveHdl)();
+ }
+
+ bool IsRefInputting() const { return m_bRefInputting; }
+ weld::Container* get_refinput_shrink_parent() { return m_xHBox.get(); }
+
+ virtual void RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton = nullptr ) override
+ {
+ if( !CanInputStart( pEdit ) )
+ return;
+
+ if ( m_pHandler && m_pRefInputStartPreHdl )
+ (m_pHandler->*m_pRefInputStartPreHdl)( pEdit, pButton );
+ m_bRefInputting = true;
+ ScValidationDlgBase::RefInputStart( pEdit, pButton );
+ }
+
+ virtual void RefInputDone( bool bForced = false ) override
+ {
+ if( !CanInputDone( bForced ) )
+ return;
+
+ ScValidationDlgBase::RefInputDone( bForced );
+ m_bRefInputting = false;
+
+ if ( m_pHandler && m_pRefInputDonePostHdl )
+ (m_pHandler->*m_pRefInputDonePostHdl)();
+ }
+
+ bool IsChildFocus() const;
+
+ enum { SLOTID = SID_VALIDITY_REFERENCE };
+
+ virtual void Close() override
+ {
+ if (m_bOwnRefHdlr)
+ {
+ if (SfxTabPage* pPage = GetTabPage(m_sValuePageId))
+ static_cast<ScTPValidationValue*>(pPage)->RemoveRefDlg(false);
+ }
+ ScValidationDlgBase::Close();
+ }
+};
+
+class ScTPValidationHelp : public SfxTabPage
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xTsbHelp;
+ std::unique_ptr<weld::Entry> m_xEdtTitle;
+ std::unique_ptr<weld::TextView> m_xEdInputHelp;
+
+public:
+ ScTPValidationHelp(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet);
+ virtual ~ScTPValidationHelp() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+};
+
+class ScTPValidationError : public SfxTabPage
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xTsbShow;
+ std::unique_ptr<weld::ComboBox> m_xLbAction;
+ std::unique_ptr<weld::Button> m_xBtnSearch;
+ std::unique_ptr<weld::Entry> m_xEdtTitle;
+ std::unique_ptr<weld::Label> m_xFtError;
+ std::unique_ptr<weld::TextView> m_xEdError;
+
+ void Init();
+
+ // Handler ------------------------
+ DECL_LINK(SelectActionHdl, weld::ComboBox&, void);
+ DECL_LINK(ClickSearchHdl, weld::Button&, void);
+
+public:
+ ScTPValidationError(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet);
+ static std::unique_ptr<SfxTabPage> Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet);
+ virtual ~ScTPValidationError() override;
+
+ virtual bool FillItemSet ( SfxItemSet* rArgSet ) override;
+ virtual void Reset ( const SfxItemSet* rArgSet ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/viewdata.hxx b/sc/source/ui/inc/viewdata.hxx
new file mode 100644
index 0000000000..72386b2efa
--- /dev/null
+++ b/sc/source/ui/inc/viewdata.hxx
@@ -0,0 +1,737 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <tools/fract.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <rangelst.hxx>
+#include <scdllapi.h>
+#include <viewopti.hxx>
+#include "docsh.hxx"
+
+#include <memory>
+#include <o3tl/typed_flags_set.hxx>
+
+#define SC_SIZE_NONE 65535
+
+enum class ScFillMode
+{
+ NONE = 0,
+ FILL = 1,
+ EMBED_LT = 2,
+ EMBED_RB = 3,
+ MATRIX = 4,
+};
+
+enum ScSplitMode { SC_SPLIT_NONE = 0, SC_SPLIT_NORMAL, SC_SPLIT_FIX, SC_SPLIT_MODE_MAX_ENUM = SC_SPLIT_FIX };
+
+enum ScSplitPos { SC_SPLIT_TOPLEFT, SC_SPLIT_TOPRIGHT, SC_SPLIT_BOTTOMLEFT, SC_SPLIT_BOTTOMRIGHT, SC_SPLIT_POS_MAX_ENUM = SC_SPLIT_BOTTOMRIGHT };
+enum ScHSplitPos { SC_SPLIT_LEFT, SC_SPLIT_RIGHT };
+enum ScVSplitPos { SC_SPLIT_TOP, SC_SPLIT_BOTTOM };
+
+inline ScHSplitPos WhichH( ScSplitPos ePos );
+inline ScVSplitPos WhichV( ScSplitPos ePos );
+
+/** Screen behavior related to cursor movements */
+enum ScFollowMode { SC_FOLLOW_NONE, SC_FOLLOW_LINE, SC_FOLLOW_FIX, SC_FOLLOW_JUMP, SC_FOLLOW_JUMP_END };
+
+/** Mouse mode to select areas */
+enum ScRefType { SC_REFTYPE_NONE, SC_REFTYPE_REF, SC_REFTYPE_FILL,
+ SC_REFTYPE_EMBED_LT, SC_REFTYPE_EMBED_RB };
+
+/** States GetSimpleArea() returns for the underlying selection marks, so the
+ caller can react if the result is not of type SC_MARK_SIMPLE. */
+enum ScMarkType
+{
+ SC_MARK_NONE = 0, // Not returned by GetSimpleArea(), used internally.
+ // Nothing marked always results in the
+ // current cursor position being selected and a simple mark.
+ SC_MARK_SIMPLE = 1, // Simple rectangular area marked, no filtered rows.
+ SC_MARK_FILTERED = 2, // At least one mark contains filtered rows.
+ SC_MARK_SIMPLE_FILTERED = // Simple rectangular area marked containing filtered rows.
+ SC_MARK_SIMPLE |
+ SC_MARK_FILTERED, // 3
+ SC_MARK_MULTI = 4 // Multiple selection marks.
+ /* TODO: if filtered multi-selection was implemented, this would be the value to use. */
+#if 0
+ ,
+ SC_MARK_MULTI_FILTERED = // Multiple selection marks containing filtered rows.
+ SC_MARK_MULTI |
+ SC_MARK_FILTERED // 6
+#endif
+};
+
+enum class ScPasteFlags
+{
+ NONE = 0, // No flags specified
+ Mode = 1, // Enable paste-mode
+ Border = 2, // Show a border around the source cells
+};
+namespace o3tl {
+ template<> struct typed_flags<ScPasteFlags> : is_typed_flags<ScPasteFlags, 0x03> {};
+}
+
+// for internal Drag&Drop:
+enum class ScDragSrc{
+ Undefined = 0,
+ Navigator = 1,
+ Table = 2
+};
+namespace o3tl {
+ template<> struct typed_flags<ScDragSrc> : is_typed_flags<ScDragSrc, 0x00000003> {};
+}
+
+class ScDocFunc;
+class ScDocument;
+class ScDBFunc;
+class ScTabViewShell;
+class ScDrawView;
+class ScEditEngineDefaulter;
+class EditView;
+class EditStatus;
+class Outliner;
+namespace vcl { class Window; }
+class SfxObjectShell;
+class SfxBindings;
+class SfxDispatcher;
+class ScPatternAttr;
+class ScExtDocOptions;
+class ScViewData;
+class ScMarkData;
+class ScGridWindow;
+
+class ScPositionHelper
+{
+public:
+ typedef SCCOLROW index_type;
+ typedef std::pair<index_type, tools::Long> value_type;
+ static_assert(std::numeric_limits<index_type>::is_signed, "ScPositionCache: index type is not signed");
+
+private:
+ static const index_type null = std::numeric_limits<index_type>::min();
+
+ class Comp
+ {
+ public:
+ bool operator() (const value_type& rValue1, const value_type& rValue2) const;
+ };
+
+ index_type MAX_INDEX;
+ std::set<value_type, Comp> mData;
+
+public:
+ ScPositionHelper(const ScDocument *pDoc, bool bColumn);
+ void setDocument(const ScDocument& rDoc, bool bColumn);
+
+ void insert(index_type nIndex, tools::Long nPos);
+ void removeByIndex(index_type nIndex);
+ void invalidateByIndex(index_type nIndex);
+ void invalidateByPosition(tools::Long nPos);
+ const value_type& getNearestByIndex(index_type nIndex) const;
+ const value_type& getNearestByPosition(tools::Long nPos) const;
+ tools::Long getPosition(index_type nIndex) const;
+ tools::Long computePosition(index_type nIndex, const std::function<long (index_type)>& getSizePx);
+};
+
+class ScBoundsProvider
+{
+ typedef ScPositionHelper::value_type value_type;
+ typedef SCCOLROW index_type;
+
+ ScDocument& rDoc;
+ const SCTAB nTab;
+ const bool bColumnHeader;
+ const index_type MAX_INDEX;
+
+ double mfPPTX;
+ double mfPPTY;
+ index_type nFirstIndex;
+ index_type nSecondIndex;
+ tools::Long nFirstPositionPx;
+ tools::Long nSecondPositionPx;
+
+public:
+ ScBoundsProvider(const ScViewData &rView, SCTAB nT, bool bColumnHeader);
+
+ void GetStartIndexAndPosition(SCCOL& nIndex, tools::Long& nPosition) const;
+ void GetEndIndexAndPosition(SCCOL& nIndex, tools::Long& nPosition) const;
+ void GetStartIndexAndPosition(SCROW& nIndex, tools::Long& nPosition) const;
+ void GetEndIndexAndPosition(SCROW& nIndex, tools::Long& nPosition) const;
+
+ void Compute(value_type aFirstNearest, value_type aSecondNearest,
+ tools::Long nFirstBound, tools::Long nSecondBound);
+
+ void EnlargeStartBy(tools::Long nOffset);
+
+ void EnlargeEndBy(tools::Long nOffset);
+
+ void EnlargeBy(tools::Long nOffset)
+ {
+ EnlargeStartBy(nOffset);
+ EnlargeEndBy(nOffset);
+ }
+
+private:
+ tools::Long GetSize(index_type nIndex) const;
+
+ void GetIndexAndPos(index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition,
+ bool bTowards, tools::Long nDiff);
+
+ void GeIndexBackwards(index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition,
+ bool bTowards);
+
+ void GetIndexTowards(index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition,
+ bool bTowards);
+};
+
+class ScViewDataTable // per-sheet data
+{
+friend class ScViewData;
+private:
+ SvxZoomType eZoomType; // selected zoom type (normal view)
+ Fraction aZoomX; // selected zoom X
+ Fraction aZoomY; // selected zoom Y (displayed)
+ Fraction aPageZoomX; // zoom in page break preview mode
+ Fraction aPageZoomY;
+
+ tools::Long nTPosX[2]; // MapMode - Offset (Twips)
+ tools::Long nTPosY[2];
+ tools::Long nMPosX[2]; // MapMode - Offset (1/100 mm)
+ tools::Long nMPosY[2];
+ tools::Long nPixPosX[2]; // Offset in Pixels
+ tools::Long nPixPosY[2];
+ tools::Long nHSplitPos;
+ tools::Long nVSplitPos;
+
+ ScSplitMode eHSplitMode;
+ ScSplitMode eVSplitMode;
+ ScSplitPos eWhichActive;
+
+ SCCOL nFixPosX; // Cell position of the splitter when freeze pane
+ SCROW nFixPosY;
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ SCCOL nOldCurX;
+ SCROW nOldCurY;
+
+ ScPositionHelper aWidthHelper;
+ ScPositionHelper aHeightHelper;
+
+ SCCOL nPosX[2]; ///< X position of the top left cell of the visible area.
+ SCROW nPosY[2]; ///< Y position of the top left cell of the visible area.
+ SCCOL nMaxTiledCol;
+ SCROW nMaxTiledRow;
+
+ bool bShowGrid; // per sheet show grid lines option.
+ bool mbOldCursorValid; // "virtual" Cursor position when combined
+ ScViewDataTable(const ScDocument *pDoc = nullptr);
+
+ void InitData(const ScDocument& rDoc);
+ void WriteUserDataSequence(
+ css::uno::Sequence <css::beans::PropertyValue>& rSettings,
+ const ScViewData& rViewData, SCTAB nTab ) const;
+
+ void ReadUserDataSequence(
+ const css::uno::Sequence <css::beans::PropertyValue>& rSettings,
+ ScViewData& rViewData, SCTAB nTab, bool& rHasZoom);
+
+ /** Sanitize the active split range value to not point into a grid window
+ that would never be initialized due to non-matching split modes.
+
+ This is to be done when reading settings from file formats or
+ configurations that could have arbitrary values. The caller is
+ responsible for actually assigning the new value to eWhichActive because
+ we want this function to be const to be able to call the check from
+ anywhere.
+ */
+ [[nodiscard]] ScSplitPos SanitizeWhichActive() const;
+};
+
+class SC_DLLPUBLIC ScViewData
+{
+private:
+ double nPPTX, nPPTY; // Scaling factors
+
+ ::std::vector<std::unique_ptr<ScViewDataTable>> maTabData;
+ ScMarkData maMarkData;
+ ScMarkData maHighlightData;
+ ScViewDataTable* pThisTab; // Data of the displayed sheet
+ ScDocShell* pDocShell;
+ ScDocument& mrDoc;
+ ScTabViewShell* pView;
+ std::unique_ptr<EditView> pEditView[4]; // Belongs to the window
+ ScViewOptions maOptions;
+ EditView* pSpellingView;
+
+ Size aScenButSize;
+
+ Size aScrSize;
+ MapMode aLogicMode; // skalierter 1/100mm-MapMode
+
+ SvxZoomType eDefZoomType; // default zoom and type for missing TabData
+ Fraction aDefZoomX;
+ Fraction aDefZoomY;
+ Fraction aDefPageZoomX; // zoom in page break preview mode
+ Fraction aDefPageZoomY;
+
+ ScRefType eRefType;
+
+ SCTAB nTabNo; // displayed sheet
+ SCTAB nRefTabNo; // sheet which contains RefInput
+ SCCOL nRefStartX;
+ SCROW nRefStartY;
+ SCTAB nRefStartZ;
+ SCCOL nRefEndX;
+ SCROW nRefEndY;
+ SCTAB nRefEndZ;
+ SCCOL nFillStartX; // Fill Cursor
+ SCROW nFillStartY;
+ SCCOL nFillEndX;
+ SCROW nFillEndY;
+ SCCOL nEditCol; // Related position
+ SCROW nEditRow;
+ SCCOL nEditStartCol;
+ SCCOL nEditEndCol; // End of Edit View
+ SCROW nEditEndRow;
+ SCCOL nTabStartCol; // for Enter after Tab
+ ScRange aDelRange; // for delete AutoFill
+
+ ScPasteFlags nPasteFlags;
+
+ ScSplitPos eEditActivePart; // the part that was active when edit mode was started
+ ScFillMode nFillMode;
+ SvxAdjust eEditAdjust;
+ bool bEditActive[4] = {}; // Active?
+ bool bActive:1; // Active Window ?
+ bool bIsRefMode:1; // Reference input
+ bool bDelMarkValid:1; // Only valid at SC_REFTYPE_FILL
+ bool bPagebreak:1; // Page break preview mode
+ bool bSelCtrlMouseClick:1; // special selection handling for ctrl-mouse-click
+ bool bMoveArea:1;
+
+ bool bGrowing;
+ sal_Int16 nFormulaBarLines; // Visible lines in the formula bar
+
+ tools::Long m_nLOKPageUpDownOffset;
+ tools::Rectangle maLOKVisibleArea;///< The visible area in the LibreOfficeKit client.
+
+ DECL_DLLPRIVATE_LINK( EditEngineHdl, EditStatus&, void );
+
+
+ SAL_DLLPRIVATE void CalcPPT();
+ SAL_DLLPRIVATE void CreateTabData( SCTAB nNewTab );
+ SAL_DLLPRIVATE void CreateTabData( std::vector< SCTAB >& rvTabs );
+ SAL_DLLPRIVATE void CreateSelectedTabData();
+ SAL_DLLPRIVATE void EnsureTabDataSize(size_t nSize);
+ SAL_DLLPRIVATE void UpdateCurrentTab();
+ SAL_DLLPRIVATE ScViewDataTable* FetchTableData(SCTAB) const;
+
+ ScViewData(ScDocument* pDoc, ScDocShell* pDocSh, ScTabViewShell* pViewSh);
+
+public:
+ ScViewData( ScDocShell& rDocSh, ScTabViewShell* pViewSh );
+ ScViewData( ScDocument& rDoc );
+ ~ScViewData() COVERITY_NOEXCEPT_FALSE;
+
+ ScDocShell* GetDocShell() const { return pDocShell; }
+ ScDocFunc& GetDocFunc() const;
+ ScDBFunc* GetView() const;
+ ScTabViewShell* GetViewShell() const { return pView; }
+ SfxObjectShell* GetSfxDocShell() const { return pDocShell; }
+ SfxBindings& GetBindings(); // from ViewShell's ViewFrame
+ SfxDispatcher& GetDispatcher(); // from ViewShell's ViewFrame
+
+ ScMarkData& GetMarkData();
+ ScMarkData& GetHighlightData();
+ const ScMarkData& GetMarkData() const;
+
+ weld::Window* GetDialogParent(); // forwarded from tabvwsh
+ ScGridWindow* GetActiveWin(); // from View
+ const ScGridWindow* GetActiveWin() const;
+ ScDrawView* GetScDrawView(); // from View
+ bool IsMinimized() const; // from View
+
+ void UpdateInputHandler( bool bForce = false );
+
+ void WriteUserData(OUString& rData);
+ void ReadUserData(std::u16string_view rData);
+ void WriteExtOptions( ScExtDocOptions& rOpt ) const;
+ void ReadExtOptions( const ScExtDocOptions& rOpt );
+ void WriteUserDataSequence(css::uno::Sequence <css::beans::PropertyValue>& rSettings) const;
+ void ReadUserDataSequence(const css::uno::Sequence <css::beans::PropertyValue>& rSettings);
+
+ ScDocument& GetDocument() const { return mrDoc; }
+
+ bool IsActive() const { return bActive; }
+ void Activate(bool bActivate) { bActive = bActivate; }
+
+ void InsertTab( SCTAB nTab );
+ void InsertTabs( SCTAB nTab, SCTAB nNewSheets );
+ void DeleteTab( SCTAB nTab );
+ void DeleteTabs( SCTAB nTab, SCTAB nSheets );
+ void CopyTab( SCTAB nSrcTab, SCTAB nDestTab );
+ void MoveTab( SCTAB nSrcTab, SCTAB nDestTab );
+
+ SCTAB GetRefTabNo() const { return nRefTabNo; }
+ void SetRefTabNo( SCTAB nNewTab ) { nRefTabNo = nNewTab; }
+
+ SCTAB GetTabNo() const { return nTabNo; }
+ SCCOL MaxCol() const { return mrDoc.MaxCol(); }
+ SCROW MaxRow() const { return mrDoc.MaxRow(); }
+ ScSplitPos GetActivePart() const { return pThisTab->eWhichActive; }
+ SCCOL GetPosX( ScHSplitPos eWhich, SCTAB nForTab = -1 ) const;
+ SCROW GetPosY( ScVSplitPos eWhich, SCTAB nForTab = -1 ) const;
+ SCCOL GetCurX() const { return pThisTab->nCurX; }
+ SCROW GetCurY() const { return pThisTab->nCurY; }
+ SCCOL GetCurXForTab( SCTAB nTabIndex ) const;
+ SCROW GetCurYForTab( SCTAB nTabIndex ) const;
+ SCCOL GetOldCurX() const;
+ SCROW GetOldCurY() const;
+ tools::Long GetLOKDocWidthPixel() const { return pThisTab->aWidthHelper.getPosition(pThisTab->nMaxTiledCol); }
+ tools::Long GetLOKDocHeightPixel() const { return pThisTab->aHeightHelper.getPosition(pThisTab->nMaxTiledRow); }
+
+ ScPositionHelper& GetLOKWidthHelper() { return pThisTab->aWidthHelper; }
+ ScPositionHelper& GetLOKHeightHelper() { return pThisTab->aHeightHelper; }
+
+ ScPositionHelper* GetLOKWidthHelper(SCTAB nTabIndex);
+ ScPositionHelper* GetLOKHeightHelper(SCTAB nTabIndex);
+
+ ScSplitMode GetHSplitMode() const { return pThisTab->eHSplitMode; }
+ ScSplitMode GetVSplitMode() const { return pThisTab->eVSplitMode; }
+ tools::Long GetHSplitPos() const { return pThisTab->nHSplitPos; }
+ tools::Long GetVSplitPos() const { return pThisTab->nVSplitPos; }
+ SCCOL GetFixPosX() const { return pThisTab->nFixPosX; }
+ SCROW GetFixPosY() const { return pThisTab->nFixPosY; }
+ SCCOL GetMaxTiledCol() const { return pThisTab->nMaxTiledCol; }
+ SCROW GetMaxTiledRow() const { return pThisTab->nMaxTiledRow; }
+
+ bool IsPagebreakMode() const { return bPagebreak; }
+ bool IsPasteMode() const { return bool(nPasteFlags & ScPasteFlags::Mode); }
+ bool ShowPasteSource() const { return bool(nPasteFlags & ScPasteFlags::Border); }
+
+ void SetPosX( ScHSplitPos eWhich, SCCOL nNewPosX );
+ void SetPosY( ScVSplitPos eWhich, SCROW nNewPosY );
+ void SetCurX( SCCOL nNewCurX ) { pThisTab->nCurX = nNewCurX; }
+ void SetCurY( SCROW nNewCurY ) { pThisTab->nCurY = nNewCurY; }
+ void SetCurXForTab( SCCOL nNewCurX, SCTAB nTabIndex );
+ void SetCurYForTab( SCCOL nNewCurY, SCTAB nTabIndex );
+ void SetOldCursor( SCCOL nNewX, SCROW nNewY );
+ void ResetOldCursor();
+
+ void SetHSplitMode( ScSplitMode eMode ) { pThisTab->eHSplitMode = eMode; }
+ void SetVSplitMode( ScSplitMode eMode ) { pThisTab->eVSplitMode = eMode; }
+ void SetHSplitPos( tools::Long nPos ) { pThisTab->nHSplitPos = nPos; }
+ void SetVSplitPos( tools::Long nPos ) { pThisTab->nVSplitPos = nPos; }
+ void SetFixPosX( SCCOL nPos ) { pThisTab->nFixPosX = nPos; }
+ void SetFixPosY( SCROW nPos ) { pThisTab->nFixPosY = nPos; }
+ void SetMaxTiledCol( SCCOL nCol );
+ void SetMaxTiledRow( SCROW nRow );
+
+ void SetPagebreakMode( bool bSet );
+ void SetPasteMode ( ScPasteFlags nFlags ) { nPasteFlags = nFlags; }
+
+ void SetZoomType( SvxZoomType eNew, bool bAll );
+ void SetZoomType( SvxZoomType eNew, std::vector< SCTAB >& tabs );
+ void SetZoom( const Fraction& rNewX, const Fraction& rNewY, std::vector< SCTAB >& tabs );
+ void SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll );
+ void RefreshZoom();
+
+ void SetSelCtrlMouseClick( bool bTmp ) { bSelCtrlMouseClick = bTmp; }
+
+ SvxZoomType GetZoomType() const { return pThisTab->eZoomType; }
+ const Fraction& GetZoomX() const { return bPagebreak ? pThisTab->aPageZoomX : pThisTab->aZoomX; }
+ const Fraction& GetZoomY() const { return bPagebreak ? pThisTab->aPageZoomY : pThisTab->aZoomY; }
+
+ void SetShowGrid( bool bShow );
+ bool GetShowGrid() const { return pThisTab->bShowGrid; }
+
+ const MapMode& GetLogicMode( ScSplitPos eWhich );
+ const MapMode& GetLogicMode(); // Offset 0
+
+ double GetPPTX() const { return nPPTX; }
+ double GetPPTY() const { return nPPTY; }
+
+ void SetFormulaBarLines(sal_Int16 nLines)
+ {
+ // Formula bar must be between 1 and 25 lines (see SpreadsheetViewSettings.idl)
+ nLines = std::max(nLines, static_cast<sal_Int16>(1));
+ nLines = std::min(nLines, static_cast<sal_Int16>(25));
+ nFormulaBarLines = nLines;
+ }
+ sal_Int16 GetFormulaBarLines() const { return nFormulaBarLines; };
+
+ ScMarkType GetSimpleArea( SCCOL& rStartCol, SCROW& rStartRow, SCTAB& rStartTab,
+ SCCOL& rEndCol, SCROW& rEndRow, SCTAB& rEndTab ) const;
+ ScMarkType GetSimpleArea( ScRange& rRange ) const;
+ /// May modify rNewMark using MarkToSimple().
+ ScMarkType GetSimpleArea( ScRange & rRange, ScMarkData & rNewMark ) const;
+ void GetMultiArea( ScRangeListRef& rRange ) const;
+
+ bool SimpleColMarked();
+ bool SimpleRowMarked();
+
+ bool IsMultiMarked() const;
+
+ /** Disallow Paste on Ctrl+A all selected or another high
+ amount of selected cells that is not the same size in
+ one direction as the clipboard source.
+ To prevent DOOM.
+ */
+ bool SelectionForbidsPaste( ScDocument* pClipDoc = nullptr );
+ bool SelectionForbidsPaste( SCCOL nSrcCols, SCROW nSrcRows );
+
+ /** Disallow cell fill (Fill,Enter,...) on Ctrl+A all
+ selected or another high amount of selected cells.
+ We'd go DOOM.
+ */
+ bool SelectionForbidsCellFill();
+ /// Determine DOOM condition, i.e. from selected range.
+ static bool SelectionFillDOOM( const ScRange& rRange );
+
+ void SetFillMode( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow );
+ void SetDragMode( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScFillMode nMode );
+ void GetFillData( SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow );
+ void ResetFillMode();
+ bool IsAnyFillMode() const { return nFillMode != ScFillMode::NONE; }
+ bool IsFillMode() const { return nFillMode == ScFillMode::FILL; }
+ ScFillMode GetFillMode() const { return nFillMode; }
+
+ SvxAdjust GetEditAdjust() const {return eEditAdjust; }
+ void SetEditAdjust( SvxAdjust eNewEditAdjust ) { eEditAdjust = eNewEditAdjust; }
+
+ // TRUE: Cell is merged
+ bool GetMergeSizePixel( SCCOL nX, SCROW nY, tools::Long& rSizeXPix, tools::Long& rSizeYPix ) const;
+ bool GetMergeSizePrintTwips( SCCOL nX, SCROW nY, tools::Long& rSizeXTwips, tools::Long& rSizeYTwips ) const;
+ void GetPosFromPixel( tools::Long nClickX, tools::Long nClickY, ScSplitPos eWhich,
+ SCCOL& rPosX, SCROW& rPosY,
+ bool bTestMerge = true, bool bRepair = false, SCTAB nForTab = -1 );
+ void GetMouseQuadrant( const Point& rClickPos, ScSplitPos eWhich,
+ SCCOL nPosX, SCROW nPosY, bool& rLeft, bool& rTop );
+
+ bool IsRefMode() const { return bIsRefMode; }
+ ScRefType GetRefType() const { return eRefType; }
+ SCCOL GetRefStartX() const { return nRefStartX; }
+ SCROW GetRefStartY() const { return nRefStartY; }
+ SCTAB GetRefStartZ() const { return nRefStartZ; }
+ SCCOL GetRefEndX() const { return nRefEndX; }
+ SCROW GetRefEndY() const { return nRefEndY; }
+ SCTAB GetRefEndZ() const { return nRefEndZ; }
+
+ void SetRefMode( bool bNewMode, ScRefType eNewType )
+ { bIsRefMode = bNewMode; eRefType = eNewType; }
+
+ void SetRefStart( SCCOL nNewX, SCROW nNewY, SCTAB nNewZ );
+ void SetRefEnd( SCCOL nNewX, SCROW nNewY, SCTAB nNewZ );
+
+ void ResetDelMark() { bDelMarkValid = false; }
+ void SetDelMark( const ScRange& rRange )
+ { aDelRange = rRange; bDelMarkValid = true; }
+
+ bool GetDelMark( ScRange& rRange ) const
+ { rRange = aDelRange; return bDelMarkValid; }
+
+ inline void GetMoveCursor( SCCOL& rCurX, SCROW& rCurY );
+
+ const ScViewOptions& GetOptions() const { return maOptions; }
+ void SetOptions( const ScViewOptions& rOpt );
+
+ bool IsGridMode () const { return maOptions.GetOption(VOPT_GRID); }
+ bool IsSyntaxMode () const { return maOptions.GetOption(VOPT_SYNTAX); }
+ void SetSyntaxMode ( bool bNewMode ) { maOptions.SetOption(VOPT_SYNTAX, bNewMode); }
+ bool IsHeaderMode () const { return maOptions.GetOption(VOPT_HEADER); }
+ void SetHeaderMode ( bool bNewMode ) { maOptions.SetOption(VOPT_HEADER, bNewMode); }
+ bool IsTabMode () const { return maOptions.GetOption(VOPT_TABCONTROLS); }
+ bool IsVScrollMode () const { return maOptions.GetOption(VOPT_VSCROLL); }
+ bool IsHScrollMode () const { return maOptions.GetOption(VOPT_HSCROLL); }
+ bool IsOutlineMode () const { return maOptions.GetOption(VOPT_OUTLINER); }
+ bool IsThemedCursor () const { return maOptions.GetOption(VOPT_THEMEDCURSOR); }
+
+ /// Force page size for PgUp/PgDown to overwrite the computation based on m_aVisArea.
+ void ForcePageUpDownOffset(tools::Long nTwips) { m_nLOKPageUpDownOffset = nTwips; }
+ tools::Long GetPageUpDownOffset() const { return m_nLOKPageUpDownOffset; }
+
+ /// The visible area in the client (set by setClientVisibleArea).
+ const tools::Rectangle& getLOKVisibleArea() const { return maLOKVisibleArea; }
+ void setLOKVisibleArea(const tools::Rectangle& rArea) { maLOKVisibleArea = rArea; }
+
+ void KillEditView();
+ void ResetEditView();
+ void SetEditEngine( ScSplitPos eWhich,
+ ScEditEngineDefaulter* pNewEngine,
+ vcl::Window* pWin, SCCOL nNewX, SCROW nNewY );
+ void GetEditView( ScSplitPos eWhich, EditView*& rViewPtr, SCCOL& rCol, SCROW& rRow );
+ bool HasEditView( ScSplitPos eWhich ) const
+ { return pEditView[eWhich] && bEditActive[eWhich]; }
+ EditView* GetEditView( ScSplitPos eWhich ) const
+ { return pEditView[eWhich].get(); }
+
+ /**
+ * Extend the output area for the edit engine view in a horizontal
+ * direction as needed.
+ */
+ void EditGrowX();
+
+ /**
+ * Extend the output area for the edit engine view in a vertical direction
+ * as needed.
+ *
+ * @param bInitial when true, then the call originates from a brand-new
+ * edit engine instance.
+ */
+ void EditGrowY( bool bInitial = false );
+
+ ScSplitPos GetEditActivePart() const { return eEditActivePart; }
+ SCCOL GetEditViewCol() const { return nEditCol; }
+ SCROW GetEditViewRow() const { return nEditRow; }
+ SCCOL GetEditStartCol() const { return nEditStartCol; }
+ SCROW GetEditStartRow() const { return nEditRow; } // never editing above the cell
+ SCCOL GetEditEndCol() const { return nEditEndCol; }
+ SCROW GetEditEndRow() const { return nEditEndRow; }
+
+ tools::Rectangle GetEditArea( ScSplitPos eWhich, SCCOL nPosX, SCROW nPosY, vcl::Window* pWin,
+ const ScPatternAttr* pPattern, bool bForceToTop, bool bInPrintTwips = false );
+
+ void SetTabNo( SCTAB nNewTab );
+ void SetActivePart( ScSplitPos eNewActive );
+
+ Point GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScSplitPos eWhich,
+ bool bAllowNeg = false, SCTAB nForTab = -1 ) const;
+ Point GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScHSplitPos eWhich ) const;
+ Point GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScVSplitPos eWhich ) const;
+ /// returns the position (top-left corner) of the requested cell in print twips coordinates.
+ Point GetPrintTwipsPos( SCCOL nCol, SCROW nRow ) const;
+ Point GetPrintTwipsPosFromTileTwips(const Point& rTileTwipsPos) const;
+
+ /// return json for our cursor position.
+ OString describeCellCursor() const { return describeCellCursorAt(GetCurX(), GetCurY()); }
+ OString describeCellCursorInPrintTwips() const { return describeCellCursorAt(GetCurX(), GetCurY(), false); }
+ OString describeCellCursorAt( SCCOL nCol, SCROW nRow, bool bPixelAligned = true ) const;
+
+ SCCOL CellsAtX( SCCOL nPosX, SCCOL nDir, ScHSplitPos eWhichX, sal_uInt16 nScrSizeY = SC_SIZE_NONE ) const;
+ SCROW CellsAtY( SCROW nPosY, SCROW nDir, ScVSplitPos eWhichY, sal_uInt16 nScrSizeX = SC_SIZE_NONE ) const;
+
+ SCCOL VisibleCellsX( ScHSplitPos eWhichX ) const; // Completely visible cell
+ SCROW VisibleCellsY( ScVSplitPos eWhichY ) const;
+ SCCOL PrevCellsX( ScHSplitPos eWhichX ) const; // Cells on the preceding page
+ SCROW PrevCellsY( ScVSplitPos eWhichY ) const;
+
+ bool IsOle() const;
+ void SetScreen( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
+ void SetScreen( const tools::Rectangle& rVisArea );
+ void SetScreenPos( const Point& rVisAreaStart );
+
+ void UpdateScreenZoom( const Fraction& rNewX, const Fraction& rNewY );
+
+ const Size& GetScrSize() const { return aScrSize; }
+
+ void RecalcPixPos();
+ Point GetPixPos( ScSplitPos eWhich ) const
+ { return Point( pThisTab->nPixPosX[WhichH(eWhich)],
+ pThisTab->nPixPosY[WhichV(eWhich)] ); }
+ void SetSpellingView( EditView* pSpView) { pSpellingView = pSpView; }
+ EditView* GetSpellingView() const { return pSpellingView; }
+
+ void UpdateOutlinerFlags( Outliner& rOutl ) const;
+
+ Point GetMousePosPixel();
+
+ bool UpdateFixX(SCTAB nTab = MAXTAB+1);
+ bool UpdateFixY(SCTAB nTab = MAXTAB+1);
+
+ SCCOL GetTabStartCol() const { return nTabStartCol; }
+ void SetTabStartCol(SCCOL nNew) { nTabStartCol = nNew; }
+
+ ScAddress GetCurPos() const;
+
+ const Size& GetScenButSize() const { return aScenButSize; }
+ void SetScenButSize(const Size& rNew) { aScenButSize = rNew; }
+
+ bool IsSelCtrlMouseClick() const { return bSelCtrlMouseClick; }
+
+ SCCOLROW GetLOKSheetFreezeIndex(bool bIsCol) const;
+ bool SetLOKSheetFreezeIndex(const SCCOLROW nFreezeIndex, bool bIsCol, SCTAB nForTab = -1);
+ bool RemoveLOKFreeze();
+ void DeriveLOKFreezeAllSheets();
+ void DeriveLOKFreezeIfNeeded(SCTAB nForTab);
+ void OverrideWithLOKFreeze(ScSplitMode& eExHSplitMode, ScSplitMode& eExVSplitMode,
+ SCCOL& nExFixPosX, SCROW& nExFixPosY,
+ tools::Long& nExHSplitPos, tools::Long& nExVSplitPos, SCTAB nForTab) const;
+
+ static inline tools::Long ToPixel( sal_uInt16 nTwips, double nFactor );
+
+ /** while (rScrY <= nEndPixels && rPosY <= nEndRow) add pixels of row
+ heights converted with nPPTY to rScrY, optimized for row height
+ segments. Upon return rPosY is the last row evaluated <= nEndRow, rScrY
+ may be > nEndPixels!
+ */
+ static void AddPixelsWhile( tools::Long & rScrY, tools::Long nEndPixels,
+ SCROW & rPosY, SCROW nEndRow, double nPPTY,
+ const ScDocument * pDoc, SCTAB nTabNo );
+
+ /** while (rScrY <= nEndPixels && rPosY >= nStartRow) add pixels of row
+ heights converted with nPPTY to rScrY, optimized for row height
+ segments. Upon return rPosY is the last row evaluated >= nStartRow,
+ rScrY may be > nEndPixels!
+ */
+ static void AddPixelsWhileBackward( tools::Long & rScrY, tools::Long nEndPixels,
+ SCROW & rPosY, SCROW nStartRow, double nPPTY,
+ const ScDocument * pDoc, SCTAB nTabNo );
+};
+
+inline tools::Long ScViewData::ToPixel( sal_uInt16 nTwips, double nFactor )
+{
+ tools::Long nRet = static_cast<tools::Long>( nTwips * nFactor );
+ if ( !nRet && nTwips )
+ nRet = 1;
+ return nRet;
+}
+
+inline void ScViewData::GetMoveCursor( SCCOL& rCurX, SCROW& rCurY )
+{
+ if ( bIsRefMode )
+ {
+ rCurX = nRefEndX;
+ rCurY = nRefEndY;
+ }
+ else
+ {
+ rCurX = GetCurX();
+ rCurY = GetCurY();
+ }
+}
+
+inline ScHSplitPos WhichH( ScSplitPos ePos )
+{
+ return (ePos==SC_SPLIT_TOPLEFT || ePos==SC_SPLIT_BOTTOMLEFT) ?
+ SC_SPLIT_LEFT : SC_SPLIT_RIGHT;
+}
+
+inline ScVSplitPos WhichV( ScSplitPos ePos )
+{
+ return (ePos==SC_SPLIT_TOPLEFT || ePos==SC_SPLIT_TOPRIGHT) ?
+ SC_SPLIT_TOP : SC_SPLIT_BOTTOM;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx
new file mode 100644
index 0000000000..52e2aedd9d
--- /dev/null
+++ b/sc/source/ui/inc/viewfunc.hxx
@@ -0,0 +1,385 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "tabview.hxx"
+
+#include <tabbgcolor.hxx>
+
+#include <com/sun/star/embed/Aspects.hpp>
+#include <vector>
+
+class ScPatternAttr;
+class ScAutoFormatData;
+class SvxSearchItem;
+class SfxItemSet;
+class SvxBoxItem;
+class SvxBoxInfoItem;
+class SfxStyleSheetBase;
+class SfxStyleSheet;
+class SfxPoolItem;
+class EditTextObject;
+struct ScSolveParam;
+struct ScTabOpParam;
+class ScValidationData;
+class ScConversionParam;
+class SdrModel;
+class Graphic;
+class ScRangeList;
+class SvxHyperlinkItem;
+class ScTransferObj;
+class ScTableProtection;
+enum class CreateNameFlags;
+
+namespace editeng { class SvxBorderLine; }
+namespace com::sun::star::embed { class XEmbeddedObject; }
+
+namespace sc {
+
+struct ColRowSpan;
+
+}
+
+namespace com::sun::star::datatransfer { class XTransferable; }
+
+struct ScDataFormFragment
+{
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::Entry> m_xEdit;
+
+ ScDataFormFragment(weld::Container* pGrid, int nLine);
+};
+
+class ScViewFunc : public ScTabView
+{
+private:
+ ScAddress aFormatSource; // for automatic extension of formatting
+ ScRange aFormatArea;
+ bool bFormatValid;
+
+public:
+ ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell );
+ ~ScViewFunc();
+
+ SC_DLLPUBLIC const ScPatternAttr* GetSelectionPattern ();
+ void GetSelectionFrame(
+ std::shared_ptr<SvxBoxItem>& rLineOuter,
+ std::shared_ptr<SvxBoxInfoItem>& rLineInner );
+
+ SvtScriptType GetSelectionScriptType();
+
+ bool GetAutoSumArea( ScRangeList& rRangeList );
+ void EnterAutoSum( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode );
+ bool AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue, const OpCode eCode );
+ OUString GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode );
+
+ SC_DLLPUBLIC void EnterData(SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString,
+ const EditTextObject* pData = nullptr, bool bMatrixExpand = false);
+ void EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const EditTextObject& rData, bool bTestSimple = false );
+ void EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue );
+
+ void EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram );
+
+ /**
+ * @param pData The caller must manage the life cycle of the object this
+ * pointer points to. NULL is allowed.
+ */
+ void EnterBlock( const OUString& rString, const EditTextObject* pData );
+
+ void EnterDataAtCursor( const OUString& rString );
+
+ SC_DLLPUBLIC void CutToClip();
+ SC_DLLPUBLIC bool CopyToClip( ScDocument* pClipDoc, bool bCut, bool bApi = false,
+ bool bIncludeObjects = false, bool bStopEdit = true );
+ SC_DLLPUBLIC bool CopyToClip( ScDocument* pClipDoc, const ScRangeList& rRange, bool bCut,
+ bool bApi = false, bool bIncludeObjects = false, bool bStopEdit = true );
+ bool CopyToClipSingleRange( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut,
+ bool bIncludeObjects );
+ bool CopyToClipMultiRange( const ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut,
+ bool bApi, bool bIncludeObjects );
+ rtl::Reference<ScTransferObj> CopyToTransferable();
+
+ SC_DLLPUBLIC bool PasteFromClip(
+ InsertDeleteFlags nFlags, ScDocument* pClipDoc,
+ ScPasteFunc nFunction = ScPasteFunc::NONE, bool bSkipEmptyCells = false,
+ bool bTranspose = false, bool bAsLink = false,
+ InsCellCmd eMoveMode = INS_NONE,
+ InsertDeleteFlags nUndoExtraFlags = InsertDeleteFlags::NONE,
+ bool bAllowDialogs = false );
+
+ void FillTab( InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bAsLink );
+
+ SC_DLLPUBLIC void PasteFromSystem();
+ SC_DLLPUBLIC bool PasteFromSystem( SotClipboardFormatId nFormatId, bool bApi = false );
+ void PasteFromTransferable( const css::uno::Reference<
+ css::datatransfer::XTransferable >& rxTransferable );
+
+ void PasteDraw();
+ void PasteDraw( const Point& rLogicPos, SdrModel* pModel, bool bGroup,
+ std::u16string_view rSrcShellID, std::u16string_view rDestShellID );
+
+ bool PasteOnDrawObjectLinked(
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable,
+ SdrObject& rHitObj);
+
+ bool PasteDataFormat( SotClipboardFormatId nFormatId,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable,
+ SCCOL nPosX, SCROW nPosY, const Point* pLogicPos,
+ bool bLink = false, bool bAllowDialogs = false );
+
+ bool PasteFile( const Point&, const OUString&, bool bLink );
+ bool PasteObject( const Point&, const css::uno::Reference < css::embed::XEmbeddedObject >&, const Size*, const Graphic* = nullptr, const OUString& = OUString(), sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT );
+ bool PasteBitmapEx( const Point&, const BitmapEx& );
+ bool PasteMetaFile( const Point&, const GDIMetaFile& );
+ bool PasteGraphic( const Point& rPos, const Graphic& rGraphic,
+ const OUString& rFile );
+ bool PasteBookmark( SotClipboardFormatId nFormatId,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable,
+ SCCOL nPosX, SCROW nPosY );
+ bool PasteLink( const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable );
+
+ void InsertBookmark( const OUString& rDescription, const OUString& rURL,
+ SCCOL nPosX, SCROW nPosY, const OUString* pTarget = nullptr,
+ bool bTryReplace = false );
+ bool HasBookmarkAtCursor( SvxHyperlinkItem* pContent );
+
+ bool MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos,
+ bool bCut );
+
+ bool LinkBlock( const ScRange& rSource, const ScAddress& rDestPos );
+
+ void CreateNames( CreateNameFlags nFlags );
+ CreateNameFlags GetCreateNameFlags();
+ void InsertNameList();
+ bool InsertName( const OUString& rName, const OUString& rSymbol,
+ const OUString& rType );
+
+ void ApplyAttributes( const SfxItemSet& rDialogSet, const SfxItemSet& rOldSet, bool bAdjustBlockHeight = true );
+ void ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight = true );
+
+ void ApplySelectionPattern( const ScPatternAttr& rAttr,
+ bool bCursorOnly = false);
+ void ApplyPatternLines(const ScPatternAttr& rAttr,
+ const SvxBoxItem& rNewOuter,
+ const SvxBoxInfoItem* pNewInner);
+
+ void ApplyUserItemSet( const SfxItemSet& rItemSet );
+
+ const SfxStyleSheet*
+ GetStyleSheetFromMarked();
+ void SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet );
+ void RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet );
+ void UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet );
+
+ void SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd = 0 );
+ void SetNumFmtByStr( const OUString& rCode );
+ void ChangeNumFmtDecimals( bool bIncrement );
+
+ void SetValidation( const ScValidationData& rNew );
+
+ void ChangeIndent( bool bIncrement );
+
+ void ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect );
+
+ void ProtectDoc( const OUString& rPassword );
+ bool Unprotect( SCTAB nTab, const OUString& rPassword );
+
+ void DeleteCells( DelCellCmd eCmd );
+ bool InsertCells( InsCellCmd eCmd, bool bRecord = true, bool bPartOfPaste = false );
+ void DeleteMulti( bool bRows );
+
+ void DeleteContents( InsertDeleteFlags nFlags );
+
+ void SetWidthOrHeight(
+ bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
+ sal_uInt16 nSizeTwips, bool bRecord = true, const ScMarkData* pMarkData = nullptr );
+
+ void SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips );
+
+ bool AdjustBlockHeight( bool bPaint = true, ScMarkData* pMarkData = nullptr );
+ bool AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, bool bApi );
+
+ void ModifyCellSize( ScDirection eDir, bool bOptimal );
+
+ SC_DLLPUBLIC void
+ InsertPageBreak( bool bColumn, bool bRecord = true,
+ const ScAddress* pPos = nullptr,
+ bool bSetModified = true );
+ SC_DLLPUBLIC void
+ DeletePageBreak( bool bColumn, bool bRecord = true,
+ const ScAddress* pPos = nullptr,
+ bool bSetModified = true );
+
+ void RemoveManualBreaks();
+
+ void SetPrintZoom(sal_uInt16 nScale);
+ void AdjustPrintZoom();
+
+ bool TestMergeCells();
+ bool TestRemoveMerge();
+
+ void MergeCells( bool bApi, bool bDoContents, bool bCenter,
+ const sal_uInt16 nSlot );
+ bool RemoveMerge();
+
+ SC_DLLPUBLIC void
+ FillSimple( FillDir eDir );
+ void FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
+ double fStart, double fStep, double fMax );
+ SC_DLLPUBLIC void
+ FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount );
+ void FillCrossDblClick();
+ void ConvertFormulaToValue();
+
+ void TransliterateText( TransliterationFlags nType );
+
+ ScAutoFormatData* CreateAutoFormatData();
+ void AutoFormat( sal_uInt16 nFormatNo );
+
+ bool SearchAndReplace( const SvxSearchItem* pSearchItem,
+ bool bAddUndo, bool bIsApi );
+
+ void Solve( const ScSolveParam& rParam );
+ void TabOp( const ScTabOpParam& rParam, bool bRecord = true );
+
+ bool InsertTable( const OUString& rName, SCTAB nTabNr, bool bRecord = true );
+ void InsertTables(std::vector<OUString>& aNames, SCTAB nTab, SCTAB nCount, bool bRecord = true);
+
+ bool AppendTable( const OUString& rName, bool bRecord = true );
+
+ void DeleteTable( SCTAB nTabNr, bool bRecord = true );
+ bool DeleteTables(const std::vector<SCTAB>& TheTabs, bool bRecord = true );
+ void DeleteTables(SCTAB nTab, SCTAB nSheets);
+
+ bool RenameTable( const OUString& rName, SCTAB nTabNr );
+ void MoveTable( sal_uInt16 nDestDocNo, SCTAB nDestTab, bool bCopy, const OUString* pNewTabName = nullptr );
+ void ImportTables( ScDocShell* pSrcShell,
+ SCTAB nCount, const SCTAB* pSrcTabs,
+ bool bLink,SCTAB nTab);
+
+ bool SetTabBgColor( const Color& rColor, SCTAB nTabNr );
+ bool SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList );
+
+ void InsertTableLink( const OUString& rFile,
+ const OUString& rFilter, const OUString& rOptions,
+ std::u16string_view rTabName );
+ void InsertAreaLink( const OUString& rFile,
+ const OUString& rFilter, const OUString& rOptions,
+ const OUString& rSource );
+
+ void ShowTable( const std::vector<OUString>& rNames );
+ void HideTable( const ScMarkData& rMark, SCTAB nTabToSelect = -1);
+
+ void MakeScenario(const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags);
+ void ExtendScenario();
+ void UseScenario( const OUString& rName );
+
+ void InsertSpecialChar( const OUString& rStr, const vcl::Font& rFont );
+
+ void SetSelectionFrameLines( const ::editeng::SvxBorderLine* pLine,
+ bool bColorOnly );
+
+ void SetNoteText( const ScAddress& rPos, const OUString& rNoteText );
+ void ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate );
+ void DoRefConversion();
+
+ void DoHangulHanjaConversion();
+ void DoThesaurus();
+
+ /** Generic implementation of sheet conversion functions. */
+ void DoSheetConversion( const ScConversionParam& rParam );
+
+ void SetPrintRanges( bool bEntireSheet,
+ const OUString* pPrint,
+ const OUString* pRepCol, const OUString* pRepRow,
+ bool bAddPrint );
+
+ void DetectiveAddPred();
+ void DetectiveDelPred();
+ void DetectiveAddSucc();
+ void DetectiveDelSucc();
+ void DetectiveAddError();
+ void DetectiveMarkInvalid();
+ void DetectiveDelAll();
+ void DetectiveRefresh();
+ void DetectiveMarkPred();
+ void DetectiveMarkSucc();
+
+ void InsertCurrentTime(SvNumFormatType nCellFmt, const OUString& rUndoStr);
+
+ void ShowNote( bool bShow );
+ void EditNote();
+
+ bool SelectionEditable( bool* pOnlyNotBecauseOfMatrix = nullptr );
+
+ SC_DLLPUBLIC void
+ DataFormPutData(SCROW nCurrentRow ,
+ SCROW nStartRow , SCCOL nStartCol ,
+ SCROW nEndRow , SCCOL nEndCol ,
+ std::vector<std::unique_ptr<ScDataFormFragment>>& rEdits,
+ sal_uInt16 aColLength);
+ void UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr = nullptr );
+
+ void OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset);
+ void OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset);
+ void OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth);
+
+ // Internal helper functions
+protected:
+ static void UpdateLineAttrs( ::editeng::SvxBorderLine& rLine,
+ const ::editeng::SvxBorderLine* pDestLine,
+ const ::editeng::SvxBorderLine* pSrcLine,
+ bool bColor );
+
+private:
+ void PasteRTF( SCCOL nCol, SCROW nStartRow,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable );
+
+ bool PasteMultiRangesFromClip(
+ InsertDeleteFlags nFlags, ScDocument* pClipDoc,
+ ScPasteFunc nFunction, bool bSkipEmptyCells,
+ bool bTranspose, bool bAsLink, bool bAllowDialogs,
+ InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags );
+
+ bool PasteFromClipToMultiRanges(
+ InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction,
+ bool bSkipEmptyCells, bool bTranspose, bool bAsLink, bool bAllowDialogs,
+ InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags );
+
+ void PostPasteFromClip(const ScRangeList& rPasteRanges, const ScMarkData& rMark);
+
+ sal_uInt16 GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula );
+
+ void StartFormatArea();
+ bool TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged );
+ void DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ bool bAttrChanged );
+
+ void MarkAndJumpToRanges(const ScRangeList& rRanges);
+ void CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount );
+};
+
+extern bool bPasteIsMove;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/viewutil.hxx b/sc/source/ui/inc/viewutil.hxx
new file mode 100644
index 0000000000..41367e6722
--- /dev/null
+++ b/sc/source/ui/inc/viewutil.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <address.hxx>
+#include <sal/types.h>
+#include <i18nlangtag/lang.h>
+
+class SfxItemSet;
+class SfxBindings;
+class SvxFontItem;
+class SfxViewShell;
+class SfxViewFrame;
+class ScChangeAction;
+class ScChangeViewSettings;
+class ScDocument;
+class ScMarkData;
+class ScTabViewShell;
+enum class SvtScriptType : sal_uInt8;
+enum class TransliterationFlags;
+
+
+enum class ScUpdateMode { All, Marks };
+
+class SC_DLLPUBLIC ScViewUtil
+{
+public:
+ static void ExecuteCharMap(const SvxFontItem& rOldFont, const ScTabViewShell& rShell);
+
+ static bool IsActionShown( const ScChangeAction& rAction,
+ const ScChangeViewSettings& rSettings,
+ ScDocument& rDocument );
+
+ static void PutItemScript( SfxItemSet& rShellSet, const SfxItemSet& rCoreSet,
+ sal_uInt16 nWhichId, SvtScriptType nScript );
+
+ static LanguageType GetEffLanguage( ScDocument& rDoc, const ScAddress& rPos );
+
+ static TransliterationFlags GetTransliterationType( sal_uInt16 nSlotID );
+
+ static bool HasFiltered( const ScRange& rRange, const ScDocument& rDoc );
+ /** Fit a range to cover nRows number of unfiltered rows.
+ @return <TRUE/> if the resulting range covers nRows unfiltered rows. */
+ static bool FitToUnfilteredRows( ScRange & rRange, const ScDocument& rDoc, size_t nRows );
+ static void UnmarkFiltered( ScMarkData& rMark, const ScDocument& rDoc );
+
+ static void HideDisabledSlot( SfxItemSet& rSet, SfxBindings& rBindings, sal_uInt16 nSlotId );
+
+ /** Returns true, if the passed view shell is in full screen mode. */
+ static bool IsFullScreen( const SfxViewShell& rViewShell );
+ /** Enters or leaves full screen mode at the passed view shell. */
+ static void SetFullScreen( const SfxViewShell& rViewShell, bool bSet );
+};
+
+class ScUpdateRect
+{
+private:
+ SCCOL nOldStartX;
+ SCROW nOldStartY;
+ SCCOL nOldEndX;
+ SCROW nOldEndY;
+ SCCOL nNewStartX;
+ SCROW nNewStartY;
+ SCCOL nNewEndX;
+ SCROW nNewEndY;
+public:
+ ScUpdateRect( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 );
+ void SetNew( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 );
+ bool GetDiff( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/warnbox.hxx b/sc/source/ui/inc/warnbox.hxx
new file mode 100644
index 0000000000..f3a437b5ed
--- /dev/null
+++ b/sc/source/ui/inc/warnbox.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 <vcl/weld.hxx>
+
+/** Warning box for "Replace cell contents?".
+ With warning image and "Do not show again" checkbox. */
+class ScReplaceWarnBox : public weld::MessageDialogController
+{
+ std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
+
+public:
+ ScReplaceWarnBox(weld::Window* pParent);
+
+ /** Opens dialog if IsDialogEnabled() returns true.
+ @descr If after executing the dialog the checkbox "Do not show again" is set,
+ the method DisableDialog() will be called. */
+ virtual short run() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/xmlsourcedlg.hxx b/sc/source/ui/inc/xmlsourcedlg.hxx
new file mode 100644
index 0000000000..3d45d27a82
--- /dev/null
+++ b/sc/source/ui/inc/xmlsourcedlg.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "anyrefdg.hxx"
+#include <orcusxml.hxx>
+
+#include <set>
+#include <memory>
+
+class ScDocument;
+class ScRange;
+class ScOrcusXMLContext;
+
+struct CustomCompare
+{
+ weld::TreeView& mrLbTree;
+ CustomCompare(weld::TreeView& rLbTree)
+ : mrLbTree(rLbTree)
+ {
+ }
+ bool operator()(const std::unique_ptr<weld::TreeIter>& lhs,
+ const std::unique_ptr<weld::TreeIter>& rhs) const
+ {
+ return mrLbTree.iter_compare(*lhs, *rhs) == -1;
+ }
+};
+
+class ScXMLSourceDlg : public ScAnyRefDlgController
+{
+ OUString maSrcPath;
+
+ ScOrcusXMLTreeParam maXMLParam;
+ std::unique_ptr<weld::TreeIter> mxCurRefEntry;
+ std::unique_ptr<ScOrcusXMLContext> mpXMLContext;
+
+ ScDocument* mpDoc;
+ bool mbDlgLostFocus;
+
+ formula::RefEdit* mpActiveEdit;
+ std::unique_ptr<weld::Button> mxBtnSelectSource;
+ std::unique_ptr<weld::Label> mxFtSourceFile;
+
+ std::unique_ptr<weld::Container> mxMapGrid;
+
+ std::unique_ptr<weld::TreeView> mxLbTree;
+ std::unique_ptr<formula::RefEdit> mxRefEdit;
+ std::unique_ptr<formula::RefButton> mxRefBtn;
+
+ std::unique_ptr<weld::Button> mxBtnOk;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+
+ CustomCompare maCustomCompare;
+
+ std::set<std::unique_ptr<weld::TreeIter>, CustomCompare> maCellLinks;
+ std::set<std::unique_ptr<weld::TreeIter>, CustomCompare> maRangeLinks;
+
+public:
+ ScXMLSourceDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, ScDocument* pDoc);
+ virtual ~ScXMLSourceDlg() override;
+
+ virtual bool IsRefInputMode() const override;
+ virtual void SetReference(const ScRange& rRange, ScDocument& rDoc) override;
+ virtual void Deactivate() override;
+ virtual void SetActive() override;
+ virtual void Close() override;
+
+private:
+ void SelectSourceFile();
+ void LoadSourceFileStructure(const OUString& rPath);
+ void TreeItemSelected();
+ void DefaultElementSelected(const weld::TreeIter& rEntry);
+ void RepeatElementSelected(const weld::TreeIter& rEntry);
+ void AttributeSelected(const weld::TreeIter& rEntry);
+
+ void SetNonLinkable();
+ void SetSingleLinkable();
+ void SetRangeLinkable();
+ void SelectAllChildEntries(const weld::TreeIter& rEntry);
+
+ /**
+ * Check if any of its parents is linked or repeated. The passed entry is
+ * not checked; its parent is the first one to be checked, then all its
+ * parents get checked all the way to the root.
+ */
+ bool IsParentDirty(const weld::TreeIter* pEntry) const;
+
+ bool IsChildrenDirty(const weld::TreeIter* pEntry) const;
+
+ void OkPressed();
+ void CancelPressed();
+ void RefEditModified();
+
+ DECL_LINK(BtnPressedHdl, weld::Button&, void);
+ DECL_LINK(TreeItemSelectHdl, weld::TreeView&, void);
+ DECL_LINK(RefModifiedHdl, formula::RefEdit&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/acredlin.cxx b/sc/source/ui/miscdlgs/acredlin.cxx
new file mode 100644
index 0000000000..9576611869
--- /dev/null
+++ b/sc/source/ui/miscdlgs/acredlin.cxx
@@ -0,0 +1,1792 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/undo.hxx>
+#include <unotools/textsearch.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <vcl/commandevent.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <acredlin.hxx>
+#include <global.hxx>
+#include <reffact.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+
+// defines -------------------------------------------------------------------
+
+#define RD_SPECIAL_NONE 0
+#define RD_SPECIAL_CONTENT 1
+#define RD_SPECIAL_VISCONTENT 2
+
+
+ScRedlinData::ScRedlinData()
+{
+ nInfo=RD_SPECIAL_NONE;
+ nActionNo=0;
+ pData=nullptr;
+ bDisabled=false;
+ bIsRejectable=false;
+ bIsAcceptable=false;
+ nTable=SCTAB_MAX;
+ nCol=SCCOL_MAX;
+ nRow=SCROW_MAX;
+}
+
+ScRedlinData::~ScRedlinData()
+{
+ nInfo=RD_SPECIAL_NONE;
+ nActionNo=0;
+ pData=nullptr;
+ bDisabled=false;
+ bIsRejectable=false;
+ bIsAcceptable=false;
+}
+
+
+ScAcceptChgDlg::ScAcceptChgDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData* ptrViewData)
+ : SfxModelessDialogController(pB, pCW, pParent,
+ "svx/ui/acceptrejectchangesdialog.ui", "AcceptRejectChangesDialog")
+ , aSelectionIdle( "ScAcceptChgDlg aSelectionIdle" )
+ , aReOpenIdle("ScAcceptChgDlg ReOpenIdle")
+ , pViewData( ptrViewData )
+ , pDoc( &ptrViewData->GetDocument() )
+ , aStrInsertCols(ScResId(STR_CHG_INSERT_COLS))
+ , aStrInsertRows(ScResId(STR_CHG_INSERT_ROWS))
+ , aStrInsertTabs(ScResId(STR_CHG_INSERT_TABS))
+ , aStrDeleteCols(ScResId(STR_CHG_DELETE_COLS))
+ , aStrDeleteRows(ScResId(STR_CHG_DELETE_ROWS))
+ , aStrDeleteTabs(ScResId(STR_CHG_DELETE_TABS))
+ , aStrMove(ScResId(STR_CHG_MOVE))
+ , aStrContent(ScResId(STR_CHG_CONTENT))
+ , aStrReject(ScResId(STR_CHG_REJECT))
+ , aStrAllAccepted(ScResId(STR_CHG_ACCEPTED))
+ , aStrAllRejected(ScResId(STR_CHG_REJECTED))
+ , aStrNoEntry(ScResId(STR_CHG_NO_ENTRY))
+ , aStrContentWithChild(ScResId(STR_CHG_CONTENT_WITH_CHILD))
+ , aStrChildContent(ScResId(STR_CHG_CHILD_CONTENT))
+ , aStrChildOrgContent(ScResId(STR_CHG_CHILD_ORGCONTENT))
+ , aStrEmpty(ScResId(STR_CHG_EMPTY))
+ , aUnknown("Unknown")
+ , bIgnoreMsg(false)
+ , bNoSelection(false)
+ , bHasFilterEntry(false)
+ , bUseColor(false)
+ , m_xContentArea(m_xDialog->weld_content_area())
+ , m_xPopup(m_xBuilder->weld_menu("calcmenu"))
+ , m_xSortMenu(m_xBuilder->weld_menu("calcsortmenu"))
+{
+ m_xAcceptChgCtr.reset(new SvxAcceptChgCtr(m_xContentArea.get()));
+ nAcceptCount=0;
+ nRejectCount=0;
+ aReOpenIdle.SetInvokeHandler(LINK( this, ScAcceptChgDlg, ReOpenTimerHdl ));
+
+ pTPFilter = m_xAcceptChgCtr->GetFilterPage();
+ pTPView = m_xAcceptChgCtr->GetViewPage();
+
+ // tdf#136062 Don't use "Reject/Clear formatting" instead of "Reject" buttons in Calc
+ pTPView->EnableClearFormat(false);
+ pTPView->EnableClearFormatAll(false);
+
+ pTheView = pTPView->GetTableControl();
+ pTheView->SetCalcView();
+ aSelectionIdle.SetInvokeHandler(LINK( this, ScAcceptChgDlg, UpdateSelectionHdl ));
+
+ pTPFilter->SetReadyHdl(LINK( this, ScAcceptChgDlg, FilterHandle ));
+ pTPFilter->SetRefHdl(LINK( this, ScAcceptChgDlg, RefHandle ));
+ pTPFilter->HideRange(false);
+ pTPView->SetRejectClickHdl( LINK( this, ScAcceptChgDlg,RejectHandle));
+ pTPView->SetAcceptClickHdl( LINK(this, ScAcceptChgDlg, AcceptHandle));
+ pTPView->SetRejectAllClickHdl( LINK( this, ScAcceptChgDlg,RejectAllHandle));
+ pTPView->SetAcceptAllClickHdl( LINK(this, ScAcceptChgDlg, AcceptAllHandle));
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.connect_expanding(LINK(this, ScAcceptChgDlg, ExpandingHandle));
+ rTreeView.connect_changed(LINK(this, ScAcceptChgDlg, SelectHandle));
+ rTreeView.connect_popup_menu(LINK(this, ScAcceptChgDlg, CommandHdl));
+ rTreeView.set_sort_func([this](const weld::TreeIter& rLeft, const weld::TreeIter& rRight){
+ return ColCompareHdl(rLeft, rRight);
+ });
+ rTreeView.set_selection_mode(SelectionMode::Multiple);
+
+ Init();
+
+ UpdateView();
+
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ if (rTreeView.get_iter_first(*xEntry))
+ rTreeView.select(*xEntry);
+}
+
+ScAcceptChgDlg::~ScAcceptChgDlg()
+{
+ ClearView();
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ if (pChanges)
+ {
+ Link<ScChangeTrack&,void> aLink;
+ pChanges->SetModifiedLink(aLink);
+ }
+}
+
+void ScAcceptChgDlg::ReInit(ScViewData* ptrViewData)
+{
+ pViewData=ptrViewData;
+ if (pViewData)
+ pDoc = &ptrViewData->GetDocument();
+ else
+ pDoc = nullptr;
+
+ bNoSelection=false;
+ bIgnoreMsg=false;
+ nAcceptCount=0;
+ nRejectCount=0;
+
+ // don't call Init here (switching between views), just set link below
+ // (dialog is just hidden, not deleted anymore, when switching views)
+ ClearView();
+ UpdateView();
+
+ if ( pDoc )
+ {
+ ScChangeTrack* pChanges = pDoc->GetChangeTrack();
+ if ( pChanges )
+ pChanges->SetModifiedLink( LINK( this, ScAcceptChgDlg, ChgTrackModHdl ) );
+ }
+}
+
+void ScAcceptChgDlg::Init()
+{
+ OSL_ENSURE( pViewData && pDoc, "ViewData or Document not found!" );
+
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ if(pChanges!=nullptr)
+ {
+ pChanges->SetModifiedLink( LINK( this, ScAcceptChgDlg,ChgTrackModHdl));
+ aChangeViewSet.SetTheAuthorToShow(pChanges->GetUser());
+ pTPFilter->ClearAuthors();
+ const std::set<OUString>& rUserColl = pChanges->GetUserCollection();
+ for (const auto& rItem : rUserColl)
+ pTPFilter->InsertAuthor(rItem);
+ }
+
+ ScChangeViewSettings* pViewSettings=pDoc->GetChangeViewSettings();
+ if ( pViewSettings!=nullptr )
+ aChangeViewSet = *pViewSettings;
+ // adjust TimeField for filter tabpage
+ aChangeViewSet.AdjustDateMode( *pDoc );
+
+ pTPFilter->CheckDate(aChangeViewSet.HasDate());
+
+ DateTime aEmpty(DateTime::EMPTY);
+
+ DateTime aDateTime(aChangeViewSet.GetTheFirstDateTime());
+ if (aDateTime != aEmpty)
+ {
+ pTPFilter->SetFirstDate(aDateTime);
+ pTPFilter->SetFirstTime(aDateTime);
+ }
+ aDateTime = aChangeViewSet.GetTheLastDateTime();
+ if (aDateTime != aEmpty)
+ {
+ pTPFilter->SetLastDate(aDateTime);
+ pTPFilter->SetLastTime(aDateTime);
+ }
+
+ pTPFilter->SetDateMode(static_cast<sal_uInt16>(aChangeViewSet.GetTheDateMode()));
+ pTPFilter->CheckComment(aChangeViewSet.HasComment());
+ pTPFilter->SetComment(aChangeViewSet.GetTheComment());
+
+ pTPFilter->CheckAuthor(aChangeViewSet.HasAuthor());
+ OUString aString=aChangeViewSet.GetTheAuthorToShow();
+ if(!aString.isEmpty())
+ {
+ pTPFilter->SelectAuthor(aString);
+ if(pTPFilter->GetSelectedAuthor()!=aString)
+ {
+ pTPFilter->InsertAuthor(aString);
+ pTPFilter->SelectAuthor(aString);
+ }
+ }
+ else
+ pTPFilter->SelectedAuthorPos(0);
+
+ pTPFilter->CheckRange(aChangeViewSet.HasRange());
+
+ aRangeList=aChangeViewSet.GetTheRangeList();
+
+ if( !aChangeViewSet.GetTheRangeList().empty() )
+ {
+ const ScRange & rRangeEntry = aChangeViewSet.GetTheRangeList().front();
+ OUString aRefStr(rRangeEntry.Format(*pDoc, ScRefFlags::RANGE_ABS_3D));
+ pTPFilter->SetRange(aRefStr);
+ }
+
+ // init filter
+ if(!(pTPFilter->IsDate()||pTPFilter->IsRange()||
+ pTPFilter->IsAuthor()||pTPFilter->IsComment()))
+ return;
+
+ pTheView->SetFilterDate(pTPFilter->IsDate());
+ pTheView->SetDateTimeMode(pTPFilter->GetDateMode());
+ pTheView->SetFirstDate(pTPFilter->GetFirstDate());
+ pTheView->SetLastDate(pTPFilter->GetLastDate());
+ pTheView->SetFirstTime(pTPFilter->GetFirstTime());
+ pTheView->SetLastTime(pTPFilter->GetLastTime());
+ pTheView->SetFilterAuthor(pTPFilter->IsAuthor());
+ pTheView->SetAuthor(pTPFilter->GetSelectedAuthor());
+
+ pTheView->SetFilterComment(pTPFilter->IsComment());
+
+ utl::SearchParam aSearchParam( pTPFilter->GetComment(),
+ utl::SearchParam::SearchType::Regexp,false );
+
+ pTheView->SetCommentParams(&aSearchParam);
+
+ pTheView->UpdateFilterTest();
+}
+
+void ScAcceptChgDlg::ClearView()
+{
+ nAcceptCount=0;
+ nRejectCount=0;
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.all_foreach(
+ [&rTreeView](weld::TreeIter& rEntry)
+ {
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rEntry));
+ delete pEntryData;
+ return false;
+ });
+ rTreeView.freeze();
+ rTreeView.clear();
+ rTreeView.thaw();
+}
+
+OUString* ScAcceptChgDlg::MakeTypeString(ScChangeActionType eType)
+{
+ OUString* pStr;
+
+ switch(eType)
+ {
+
+ case SC_CAT_INSERT_COLS: pStr=&aStrInsertCols;break;
+ case SC_CAT_INSERT_ROWS: pStr=&aStrInsertRows;break;
+ case SC_CAT_INSERT_TABS: pStr=&aStrInsertTabs;break;
+ case SC_CAT_DELETE_COLS: pStr=&aStrDeleteCols;break;
+ case SC_CAT_DELETE_ROWS: pStr=&aStrDeleteRows;break;
+ case SC_CAT_DELETE_TABS: pStr=&aStrDeleteTabs;break;
+ case SC_CAT_MOVE: pStr=&aStrMove;break;
+ case SC_CAT_CONTENT: pStr=&aStrContent;break;
+ case SC_CAT_REJECT: pStr=&aStrReject;break;
+ default: pStr=&aUnknown;break;
+ }
+ return pStr;
+}
+
+bool ScAcceptChgDlg::IsValidAction(const ScChangeAction* pScChangeAction)
+{
+ if(pScChangeAction==nullptr) return false;
+
+ bool bFlag = false;
+
+ ScRange aRef=pScChangeAction->GetBigRange().MakeRange(*pDoc);
+ OUString aUser=pScChangeAction->GetUser();
+ DateTime aDateTime=pScChangeAction->GetDateTime();
+
+ ScChangeActionType eType=pScChangeAction->GetType();
+ OUString aDesc;
+
+ OUString aComment = pScChangeAction->GetComment().replaceAll("\n", "");
+
+ if(eType==SC_CAT_CONTENT)
+ {
+ if(!pScChangeAction->IsDialogParent())
+ aDesc = pScChangeAction->GetDescription(*pDoc, true);
+ }
+ else
+ aDesc = pScChangeAction->GetDescription(*pDoc, !pScChangeAction->IsMasterDelete());
+
+ if (!aDesc.isEmpty())
+ {
+ aComment += " (" + aDesc + ")";
+ }
+
+ if (pTheView->IsValidEntry(aUser, aDateTime, aComment))
+ {
+ if(pTPFilter->IsRange())
+ {
+ for ( size_t i = 0, nRanges = aRangeList.size(); i < nRanges; ++i )
+ {
+ ScRange const & rRangeEntry = aRangeList[ i ];
+ if (rRangeEntry.Intersects(aRef)) {
+ bFlag = true;
+ break;
+ }
+ }
+ }
+ else
+ bFlag=true;
+ }
+
+ return bFlag;
+}
+
+std::unique_ptr<weld::TreeIter> ScAcceptChgDlg::AppendChangeAction(
+ const ScChangeAction* pScChangeAction, bool bCreateOnDemand,
+ const weld::TreeIter* pParent, bool bDelMaster, bool bDisabled)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ if(pScChangeAction==nullptr || pChanges==nullptr) return nullptr;
+
+ bool bFlag = false;
+
+ ScRange aRef=pScChangeAction->GetBigRange().MakeRange(*pDoc);
+ DateTime aDateTime=pScChangeAction->GetDateTime();
+
+ ScChangeActionType eType=pScChangeAction->GetType();
+ OUString aActionString;
+ OUString aRefStr;
+ OUString aUser;
+ OUString aDate;
+ OUString aDesc;
+
+ std::unique_ptr<ScRedlinData> pNewData(new ScRedlinData);
+ pNewData->pData=const_cast<ScChangeAction *>(pScChangeAction);
+ pNewData->nActionNo=pScChangeAction->GetActionNumber();
+ pNewData->bIsAcceptable=pScChangeAction->IsClickable();
+ pNewData->bIsRejectable=pScChangeAction->IsRejectable();
+ pNewData->bDisabled=!pNewData->bIsAcceptable || bDisabled;
+ pNewData->aDateTime=aDateTime;
+ pNewData->nRow = aRef.aStart.Row();
+ pNewData->nCol = aRef.aStart.Col();
+ pNewData->nTable= aRef.aStart.Tab();
+
+ if(eType==SC_CAT_CONTENT)
+ {
+ if(pScChangeAction->IsDialogParent())
+ {
+ aActionString = aStrContentWithChild;
+ pNewData->nInfo=RD_SPECIAL_VISCONTENT;
+ pNewData->bIsRejectable=false;
+ pNewData->bIsAcceptable=false;
+ }
+ else
+ {
+ aActionString = *MakeTypeString(eType);
+ aDesc = pScChangeAction->GetDescription(*pDoc, true);
+ }
+ }
+ else
+ {
+ aActionString = *MakeTypeString(eType);
+
+ if(bDelMaster)
+ {
+ aDesc = pScChangeAction->GetDescription(*pDoc,true);
+ pNewData->bDisabled=true;
+ pNewData->bIsRejectable=false;
+ }
+ else
+ aDesc = pScChangeAction->GetDescription(*pDoc, !pScChangeAction->IsMasterDelete());
+
+ }
+
+ aRefStr = pScChangeAction->GetRefString(*pDoc, true);
+
+ bool bIsGenerated = false;
+
+ if(!pChanges->IsGenerated(pScChangeAction->GetActionNumber()))
+ {
+ aUser = pScChangeAction->GetUser();
+ aDate = ScGlobal::getLocaleData().getDate(aDateTime) + " " + ScGlobal::getLocaleData().getTime(aDateTime);
+ bIsGenerated = false;
+ }
+ else
+ {
+ bIsGenerated = true;
+ }
+
+ OUString aComment = pScChangeAction->GetComment().replaceAll("\n", "");
+
+ if (!aDesc.isEmpty())
+ {
+ aComment += " (" + aDesc + ")";
+ }
+
+ if (pTheView->IsValidEntry(aUser, aDateTime) || bIsGenerated)
+ {
+ if (pTheView->IsValidComment(aComment))
+ {
+ if(pTPFilter->IsRange())
+ {
+ for ( size_t i = 0, nRanges = aRangeList.size(); i < nRanges; ++i )
+ {
+ ScRange const & rRangeEntry = aRangeList[ i ];
+ if( rRangeEntry.Intersects(aRef) )
+ {
+ bHasFilterEntry=true;
+ bFlag=true;
+ break;
+ }
+ }
+ }
+ else if(!bIsGenerated)
+ {
+ bHasFilterEntry=true;
+ bFlag=true;
+ }
+ }
+ }
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ OUString sId(weld::toId(pNewData.release()));
+ rTreeView.insert(pParent, -1, &aActionString, &sId, nullptr, nullptr, bCreateOnDemand, xEntry.get());
+ rTreeView.set_text( *xEntry, aRefStr, 1);
+ if (!aUser.isEmpty())
+ rTreeView.set_text( *xEntry, aUser, 2);
+ if (!aDate.isEmpty())
+ rTreeView.set_text( *xEntry, aDate, 3);
+ if (!aComment.isEmpty())
+ rTreeView.set_text( *xEntry, aComment, 4);
+ if (!bFlag && bUseColor && !pParent)
+ {
+ rTreeView.set_font_color(*xEntry, COL_LIGHTBLUE);
+ }
+ else if (bFlag && bUseColor && pParent)
+ {
+ rTreeView.set_font_color(*xEntry, COL_GREEN);
+
+ std::unique_ptr<weld::TreeIter> xExpEntry(rTreeView.make_iterator(pParent));
+
+ while (!rTreeView.get_row_expanded(*xExpEntry))
+ {
+ if (rTreeView.get_iter_depth(*xExpEntry))
+ rTreeView.expand_row(*xExpEntry);
+
+ if (!rTreeView.iter_parent(*xExpEntry))
+ break;
+ }
+ }
+ return xEntry;
+}
+
+std::unique_ptr<weld::TreeIter> ScAcceptChgDlg::AppendFilteredAction(
+ const ScChangeAction* pScChangeAction, ScChangeActionState eState,
+ bool bCreateOnDemand,
+ const weld::TreeIter* pParent, bool bDelMaster, bool bDisabled)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ if(pScChangeAction==nullptr || pChanges==nullptr) return nullptr;
+
+ bool bIsGenerated = pChanges->IsGenerated(pScChangeAction->GetActionNumber());
+
+ bool bFlag = false;
+
+ ScRange aRef=pScChangeAction->GetBigRange().MakeRange(*pDoc);
+ OUString aUser=pScChangeAction->GetUser();
+ DateTime aDateTime=pScChangeAction->GetDateTime();
+
+ if (pTheView->IsValidEntry(aUser, aDateTime) || bIsGenerated)
+ {
+ if(pTPFilter->IsRange())
+ {
+ for ( size_t i = 0, nRanges = aRangeList.size(); i < nRanges; ++i )
+ {
+ ScRange const & rRangeEntry=aRangeList[ i ];
+ if( rRangeEntry.Intersects(aRef) )
+ {
+ if( pScChangeAction->GetState()==eState )
+ bFlag = true;
+ break;
+ }
+ }
+ }
+ else if(pScChangeAction->GetState()==eState && !bIsGenerated)
+ bFlag = true;
+ }
+
+ std::unique_ptr<weld::TreeIter> xEntry;
+ if(bFlag)
+ {
+ ScChangeActionType eType=pScChangeAction->GetType();
+ OUString aActionString;
+ OUString aDesc;
+
+ std::unique_ptr<ScRedlinData> pNewData(new ScRedlinData);
+ pNewData->pData=const_cast<ScChangeAction *>(pScChangeAction);
+ pNewData->nActionNo=pScChangeAction->GetActionNumber();
+ pNewData->bIsAcceptable=pScChangeAction->IsClickable();
+ pNewData->bIsRejectable=pScChangeAction->IsRejectable();
+ pNewData->bDisabled=!pNewData->bIsAcceptable || bDisabled;
+ pNewData->aDateTime=aDateTime;
+ pNewData->nRow = aRef.aStart.Row();
+ pNewData->nCol = aRef.aStart.Col();
+ pNewData->nTable= aRef.aStart.Tab();
+
+ if(eType==SC_CAT_CONTENT)
+ {
+ if(pScChangeAction->IsDialogParent())
+ {
+ aActionString=aStrContentWithChild;
+ pNewData->nInfo=RD_SPECIAL_VISCONTENT;
+ pNewData->bIsRejectable=false;
+ pNewData->bIsAcceptable=false;
+ }
+ else
+ {
+ aActionString=*MakeTypeString(eType);
+ aDesc = pScChangeAction->GetDescription(*pDoc, true);
+ }
+ }
+ else
+ {
+ aActionString=*MakeTypeString(eType);
+
+ if(bDelMaster)
+ {
+ aDesc = pScChangeAction->GetDescription(*pDoc,true);
+ pNewData->bDisabled=true;
+ pNewData->bIsRejectable=false;
+ }
+ else
+ aDesc = pScChangeAction->GetDescription(*pDoc,!pScChangeAction->IsMasterDelete());
+
+ }
+
+
+ OUString aComment = pScChangeAction->GetComment().replaceAll("\n", "");
+ if (!aDesc.isEmpty())
+ {
+ aComment += " (" + aDesc + ")";
+ }
+ if (pTheView->IsValidComment(aComment))
+ {
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ xEntry = rTreeView.make_iterator();
+ OUString sId(weld::toId(pNewData.release()));
+ rTreeView.insert(pParent, -1, &aActionString, &sId, nullptr, nullptr, bCreateOnDemand, xEntry.get());
+
+ OUString aRefStr = pScChangeAction->GetRefString(*pDoc, true);
+ rTreeView.set_text(*xEntry, aRefStr, 1);
+
+ if (!bIsGenerated)
+ {
+ rTreeView.set_text(*xEntry, aUser, 2);
+ OUString sDate = ScGlobal::getLocaleData().getDate(aDateTime) + " " + ScGlobal::getLocaleData().getTime(aDateTime);
+ rTreeView.set_text(*xEntry, sDate, 3);
+ }
+
+ rTreeView.set_text(*xEntry, aComment, 4);
+ }
+ }
+ return xEntry;
+}
+
+std::unique_ptr<weld::TreeIter> ScAcceptChgDlg::InsertChangeActionContent(const ScChangeActionContent* pScChangeAction,
+ const weld::TreeIter& rParent, sal_uLong nSpecial)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ if(pScChangeAction==nullptr || pChanges==nullptr) return nullptr;
+
+ bool bIsGenerated = pChanges->IsGenerated(pScChangeAction->GetActionNumber());
+
+ bool bFlag = false;
+
+ ScRange aRef=pScChangeAction->GetBigRange().MakeRange(*pDoc);
+ OUString aUser=pScChangeAction->GetUser();
+ DateTime aDateTime=pScChangeAction->GetDateTime();
+
+ if (pTheView->IsValidEntry(aUser, aDateTime) || bIsGenerated)
+ {
+ if(pTPFilter->IsRange())
+ {
+ for ( size_t i = 0, nRanges = aRangeList.size(); i < nRanges; ++i )
+ {
+ ScRange const & rRangeEntry = aRangeList[ i ];
+ if( rRangeEntry.Intersects(aRef) )
+ {
+ bFlag=true;
+ break;
+ }
+ }
+ }
+ else if(!bIsGenerated)
+ bFlag=true;
+ }
+
+ OUString aContent;
+ OUString aRefStr;
+ OUString aDate;
+ OUString aDesc;
+
+ if(nSpecial==RD_SPECIAL_CONTENT)
+ {
+ aContent = pScChangeAction->GetOldString(pDoc);
+ if (aContent.isEmpty())
+ aContent = aStrEmpty;
+ aDesc = aStrChildOrgContent + ": " + aContent;
+ }
+ else
+ {
+ const OUString aTmp( pScChangeAction->GetNewString(pDoc));
+ if (aTmp.isEmpty())
+ aContent = aStrEmpty;
+ else
+ aContent = "\'" + aTmp + "\'";
+ aDesc = aStrChildContent + aContent;
+ }
+
+ aRefStr = pScChangeAction->GetRefString(*pDoc, true);
+
+ if(!bIsGenerated)
+ {
+ // aUser is kept.
+ aDate = ScGlobal::getLocaleData().getDate(aDateTime) + " " + ScGlobal::getLocaleData().getTime(aDateTime);
+ }
+ else
+ {
+ aUser.clear();
+ }
+
+ OUString aComment = pScChangeAction->GetComment().replaceAll("\n", "");
+
+ if(!aDesc.isEmpty())
+ {
+ aComment += " (" + aDesc + ")";
+ }
+
+ std::unique_ptr<ScRedlinData> pNewData(new ScRedlinData);
+ pNewData->nInfo=nSpecial;
+ pNewData->pData=const_cast<ScChangeActionContent *>(pScChangeAction);
+ pNewData->nActionNo=pScChangeAction->GetActionNumber();
+ pNewData->bIsAcceptable=pScChangeAction->IsClickable();
+ pNewData->bIsRejectable=false;
+ pNewData->bDisabled=!pNewData->bIsAcceptable;
+ pNewData->aDateTime=aDateTime;
+ pNewData->nRow = aRef.aStart.Row();
+ pNewData->nCol = aRef.aStart.Col();
+ pNewData->nTable= aRef.aStart.Tab();
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ OUString sId(weld::toId(pNewData.release()));
+ rTreeView.insert(&rParent, -1, &aContent, &sId, nullptr, nullptr, false, xEntry.get());
+ rTreeView.set_text( *xEntry, aRefStr, 1);
+ if (!aUser.isEmpty())
+ rTreeView.set_text( *xEntry, aUser, 2);
+ if (!aDate.isEmpty())
+ rTreeView.set_text( *xEntry, aDate, 3);
+ if (!aComment.isEmpty())
+ rTreeView.set_text( *xEntry, aComment, 4);
+ if (pTheView->IsValidComment(aComment) && bFlag)
+ bHasFilterEntry=true;
+ else
+ {
+ rTreeView.set_font_color(*xEntry, COL_LIGHTBLUE);
+ }
+ return xEntry;
+}
+
+void ScAcceptChgDlg::UpdateView()
+{
+ std::unique_ptr<weld::TreeIter> xParent;
+ ScChangeTrack* pChanges=nullptr;
+ const ScChangeAction* pScChangeAction=nullptr;
+ m_xDialog->set_busy_cursor(true);
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.freeze();
+ bool bFilterFlag = pTPFilter->IsDate() || pTPFilter->IsRange() ||
+ pTPFilter->IsAuthor() || pTPFilter->IsComment();
+
+ bUseColor = bFilterFlag;
+
+ if(pDoc!=nullptr)
+ {
+ pChanges=pDoc->GetChangeTrack();
+ if(pChanges!=nullptr)
+ pScChangeAction=pChanges->GetFirst();
+ }
+ bool bTheFlag = false;
+
+ while(pScChangeAction!=nullptr)
+ {
+ bHasFilterEntry=false;
+ switch (pScChangeAction->GetState())
+ {
+ case SC_CAS_VIRGIN:
+
+ if (pScChangeAction->IsDialogRoot())
+ {
+ bool bOnDemandChildren = !bFilterFlag && pScChangeAction->IsDialogParent();
+ if (pScChangeAction->IsDialogParent())
+ xParent = AppendChangeAction(pScChangeAction, bOnDemandChildren);
+ else
+ xParent = AppendFilteredAction(pScChangeAction, SC_CAS_VIRGIN, bOnDemandChildren);
+ }
+ else
+ xParent.reset();
+
+ bTheFlag=true;
+ break;
+
+ case SC_CAS_ACCEPTED:
+ xParent.reset();
+ nAcceptCount++;
+ break;
+
+ case SC_CAS_REJECTED:
+ xParent.reset();
+ nRejectCount++;
+ break;
+ }
+
+ if (xParent && pScChangeAction->IsDialogParent() && bFilterFlag)
+ {
+ bool bTestFlag = bHasFilterEntry;
+ bHasFilterEntry=false;
+ if (Expand(pChanges, pScChangeAction, *xParent, !bTestFlag) && !bTestFlag)
+ rTreeView.remove(*xParent);
+ }
+
+ pScChangeAction=pScChangeAction->GetNext();
+ }
+
+ if( bTheFlag && (!pDoc->IsDocEditable() || pChanges->IsProtected()) )
+ bTheFlag=false;
+
+ pTPView->EnableAccept(bTheFlag);
+ pTPView->EnableAcceptAll(bTheFlag);
+ pTPView->EnableReject(bTheFlag);
+ pTPView->EnableRejectAll(bTheFlag);
+
+ if (nAcceptCount>0)
+ rTreeView.insert(nullptr, -1, &aStrAllAccepted, nullptr, nullptr, nullptr, true, nullptr);
+ if (nRejectCount>0)
+ rTreeView.insert(nullptr, -1, &aStrAllRejected, nullptr, nullptr, nullptr, true, nullptr);
+ rTreeView.thaw();
+ m_xDialog->set_busy_cursor(false);
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ if (rTreeView.get_iter_first(*xEntry))
+ rTreeView.select(*xEntry);
+}
+
+IMPL_LINK_NOARG(ScAcceptChgDlg, RefHandle, SvxTPFilter*, void)
+{
+ sal_uInt16 nId =ScSimpleRefDlgWrapper::GetChildWindowId();
+
+ SC_MOD()->SetRefDialog( nId, true );
+
+ SfxViewFrame& rViewFrm = pViewData->GetViewShell()->GetViewFrame();
+ ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
+
+ if(pWnd!=nullptr)
+ {
+ sal_uInt16 nAcceptId=ScAcceptChgDlgWrapper::GetChildWindowId();
+ rViewFrm.ShowChildWindow(nAcceptId,false);
+ pWnd->SetCloseHdl(LINK( this, ScAcceptChgDlg,RefInfoHandle));
+ pWnd->SetRefString(pTPFilter->GetRange());
+ ScSimpleRefDlgWrapper::SetAutoReOpen(false);
+ auto xWin = pWnd->GetController();
+ m_xDialog->hide();
+ xWin->set_title(m_xDialog->get_title());
+ pWnd->StartRefInput();
+ }
+}
+
+IMPL_LINK( ScAcceptChgDlg, RefInfoHandle, const OUString*, pResult, void)
+{
+ sal_uInt16 nId = ScAcceptChgDlgWrapper::GetChildWindowId();
+
+ ScSimpleRefDlgWrapper::SetAutoReOpen(true);
+
+ SfxViewFrame& rViewFrm = pViewData->GetViewShell()->GetViewFrame();
+ if (pResult)
+ {
+ pTPFilter->SetRange(*pResult);
+ FilterHandle(pTPFilter);
+
+ rViewFrm.ShowChildWindow(nId);
+ }
+ else
+ {
+ rViewFrm.SetChildWindow(nId, false);
+ }
+}
+
+IMPL_LINK( ScAcceptChgDlg, FilterHandle, SvxTPFilter*, pRef, void )
+{
+ if(pRef!=nullptr)
+ {
+ ClearView();
+ aRangeList.RemoveAll();
+ aRangeList.Parse(pTPFilter->GetRange(), *pDoc);
+ UpdateView();
+ }
+}
+
+IMPL_LINK( ScAcceptChgDlg, RejectHandle, SvxTPView*, pRef, void )
+{
+ m_xDialog->set_busy_cursor(true);
+
+ bIgnoreMsg=true;
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ if(pRef!=nullptr)
+ {
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.selected_foreach([this, pChanges, &rTreeView](weld::TreeIter& rEntry){
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rEntry));
+ if (pEntryData)
+ {
+ ScChangeAction* pScChangeAction= static_cast<ScChangeAction*>(pEntryData->pData);
+ if (pScChangeAction->GetType()==SC_CAT_INSERT_TABS)
+ pViewData->SetTabNo(0);
+ pChanges->Reject(pScChangeAction);
+ }
+ return false;
+ });
+ ScDocShell* pDocSh=pViewData->GetDocShell();
+ pDocSh->PostPaintExtras();
+ pDocSh->PostPaintGridAll();
+ pDocSh->GetUndoManager()->Clear();
+ pDocSh->SetDocumentModified();
+ ClearView();
+ UpdateView();
+ }
+
+ m_xDialog->set_busy_cursor(false);
+
+ bIgnoreMsg=false;
+}
+IMPL_LINK( ScAcceptChgDlg, AcceptHandle, SvxTPView*, pRef, void )
+{
+ m_xDialog->set_busy_cursor(true);
+
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ bIgnoreMsg=true;
+ if(pRef!=nullptr)
+ {
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.selected_foreach([pChanges, &rTreeView](weld::TreeIter& rEntry) {
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rEntry));
+ if (pEntryData)
+ {
+ ScChangeAction* pScChangeAction=
+ static_cast<ScChangeAction*>(pEntryData->pData);
+ if(pScChangeAction->GetType()==SC_CAT_CONTENT)
+ {
+ if(pEntryData->nInfo==RD_SPECIAL_CONTENT)
+ pChanges->SelectContent(pScChangeAction,true);
+ else
+ pChanges->SelectContent(pScChangeAction);
+ }
+ else
+ pChanges->Accept(pScChangeAction);
+ }
+ return false;
+ });
+ ScDocShell* pDocSh=pViewData->GetDocShell();
+ pDocSh->PostPaintExtras();
+ pDocSh->PostPaintGridAll();
+ pDocSh->SetDocumentModified();
+ ClearView();
+ UpdateView();
+ }
+ m_xDialog->set_busy_cursor(false);
+ bIgnoreMsg=false;
+}
+
+void ScAcceptChgDlg::RejectFiltered()
+{
+ if(pDoc==nullptr) return;
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ const ScChangeAction* pScChangeAction=nullptr;
+
+ if(pChanges!=nullptr)
+ {
+ pScChangeAction=pChanges->GetLast();
+ }
+
+ while(pScChangeAction!=nullptr)
+ {
+ if(pScChangeAction->IsDialogRoot())
+ if(IsValidAction(pScChangeAction))
+ pChanges->Reject(const_cast<ScChangeAction*>(pScChangeAction));
+
+ pScChangeAction=pScChangeAction->GetPrev();
+ }
+}
+void ScAcceptChgDlg::AcceptFiltered()
+{
+ if(pDoc==nullptr) return;
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ const ScChangeAction* pScChangeAction=nullptr;
+
+ if(pChanges!=nullptr)
+ pScChangeAction=pChanges->GetLast();
+
+ while(pScChangeAction!=nullptr)
+ {
+ if(pScChangeAction->IsDialogRoot())
+ if(IsValidAction(pScChangeAction))
+ pChanges->Accept(const_cast<ScChangeAction*>(pScChangeAction));
+
+ pScChangeAction=pScChangeAction->GetPrev();
+ }
+}
+
+IMPL_LINK_NOARG(ScAcceptChgDlg, RejectAllHandle, SvxTPView*, void)
+{
+ m_xDialog->set_busy_cursor(true);
+ bIgnoreMsg=true;
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ if(pChanges!=nullptr)
+ {
+ if(pTPFilter->IsDate()||pTPFilter->IsAuthor()||pTPFilter->IsRange()||pTPFilter->IsComment())
+ RejectFiltered();
+ else
+ pChanges->RejectAll();
+
+ pViewData->SetTabNo(0);
+
+ ScDocShell* pDocSh=pViewData->GetDocShell();
+ pDocSh->PostPaintExtras();
+ pDocSh->PostPaintGridAll();
+ pDocSh->GetUndoManager()->Clear();
+ pDocSh->SetDocumentModified();
+ ClearView();
+ UpdateView();
+ }
+ m_xDialog->set_busy_cursor(false);
+
+ bIgnoreMsg=false;
+}
+
+IMPL_LINK_NOARG(ScAcceptChgDlg, AcceptAllHandle, SvxTPView*, void)
+{
+ m_xDialog->set_busy_cursor(true);
+
+ bIgnoreMsg=true;
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ if(pChanges!=nullptr)
+ {
+ if(pTPFilter->IsDate()||pTPFilter->IsAuthor()||pTPFilter->IsRange()||pTPFilter->IsComment())
+ AcceptFiltered();
+ else
+ pChanges->AcceptAll();
+
+ ScDocShell* pDocSh=pViewData->GetDocShell();
+ pDocSh->PostPaintExtras();
+ pDocSh->PostPaintGridAll();
+ pDocSh->SetDocumentModified();
+ ClearView();
+ UpdateView();
+ }
+ bIgnoreMsg=false;
+
+ m_xDialog->set_busy_cursor(false);
+}
+
+IMPL_LINK_NOARG(ScAcceptChgDlg, SelectHandle, weld::TreeView&, void)
+{
+ if (!bNoSelection)
+ aSelectionIdle.Start();
+
+ bNoSelection=false;
+}
+
+void ScAcceptChgDlg::GetDependents(const ScChangeAction* pScChangeAction,
+ ScChangeActionMap& aActionMap,
+ const weld::TreeIter& rEntry)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ std::unique_ptr<weld::TreeIter> xParent(rTreeView.make_iterator(&rEntry));
+ if (rTreeView.iter_parent(*xParent))
+ {
+ ScRedlinData *pParentData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xParent));
+ ScChangeAction* pParentAction=static_cast<ScChangeAction*>(pParentData->pData);
+
+ if(pParentAction!=pScChangeAction)
+ pChanges->GetDependents(const_cast<ScChangeAction*>(pScChangeAction),
+ aActionMap,pScChangeAction->IsMasterDelete());
+ else
+ pChanges->GetDependents( const_cast<ScChangeAction*>(pScChangeAction),
+ aActionMap );
+ }
+ else
+ pChanges->GetDependents(const_cast<ScChangeAction*>(pScChangeAction),
+ aActionMap, pScChangeAction->IsMasterDelete() );
+}
+
+bool ScAcceptChgDlg::InsertContentChildren(ScChangeActionMap* pActionMap, const weld::TreeIter& rParent)
+{
+ bool bTheTestFlag = true;
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rParent));
+ const ScChangeAction* pScChangeAction = static_cast<ScChangeAction*>(pEntryData->pData);
+ bool bParentInserted = false;
+ // If the parent is a MatrixOrigin then place it in the right order before
+ // the MatrixReferences. Also if it is the first content change at this
+ // position don't insert the first dependent MatrixReference as the special
+ // content (original value) but insert the predecessor of the MatrixOrigin
+ // itself instead.
+ if ( pScChangeAction->GetType() == SC_CAT_CONTENT &&
+ static_cast<const ScChangeActionContent*>(pScChangeAction)->IsMatrixOrigin() )
+ {
+ pActionMap->insert( ::std::make_pair( pScChangeAction->GetActionNumber(),
+ const_cast<ScChangeAction*>( pScChangeAction ) ) );
+ bParentInserted = true;
+ }
+
+ ScChangeActionMap::iterator itChangeAction = std::find_if(pActionMap->begin(), pActionMap->end(),
+ [](const std::pair<sal_uLong, ScChangeAction*>& rEntry) { return rEntry.second->GetState() == SC_CAS_VIRGIN; });
+
+ if( itChangeAction == pActionMap->end() )
+ return true;
+
+ std::unique_ptr<weld::TreeIter> xOriginal = InsertChangeActionContent(
+ dynamic_cast<const ScChangeActionContent*>( itChangeAction->second ),
+ rParent, RD_SPECIAL_CONTENT );
+
+ if (xOriginal)
+ {
+ bTheTestFlag=false;
+ ScRedlinData *pParentData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xOriginal));
+ pParentData->pData=const_cast<ScChangeAction *>(pScChangeAction);
+ pParentData->nActionNo=pScChangeAction->GetActionNumber();
+ pParentData->bIsAcceptable=pScChangeAction->IsRejectable(); // select old value
+ pParentData->bIsRejectable=false;
+ pParentData->bDisabled=false;
+ }
+ while( itChangeAction != pActionMap->end() )
+ {
+ if( itChangeAction->second->GetState() == SC_CAS_VIRGIN )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry =
+ InsertChangeActionContent( dynamic_cast<const ScChangeActionContent*>( itChangeAction->second ),
+ rParent, RD_SPECIAL_NONE );
+
+ if (xEntry)
+ bTheTestFlag=false;
+ }
+ ++itChangeAction;
+ }
+
+ if ( !bParentInserted )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry =
+ InsertChangeActionContent(static_cast<const ScChangeActionContent*>(
+ pScChangeAction),rParent,RD_SPECIAL_NONE);
+
+ if (xEntry)
+ {
+ bTheTestFlag=false;
+ ScRedlinData *pParentData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+ pParentData->pData=const_cast<ScChangeAction *>(pScChangeAction);
+ pParentData->nActionNo=pScChangeAction->GetActionNumber();
+ pParentData->bIsAcceptable=pScChangeAction->IsClickable();
+ pParentData->bIsRejectable=false;
+ pParentData->bDisabled=false;
+ }
+ }
+
+ return bTheTestFlag;
+}
+
+bool ScAcceptChgDlg::InsertAcceptedORejected(const weld::TreeIter& rParent)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ bool bTheTestFlag = true;
+
+ ScChangeActionState eState = SC_CAS_VIRGIN;
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ OUString aString = rTreeView.get_text(rParent, 0);
+ OUString a2String = aString.copy(0, aStrAllAccepted.getLength());
+ if (a2String == aStrAllAccepted)
+ eState=SC_CAS_ACCEPTED;
+ else
+ {
+ a2String = aString.copy(0, aStrAllRejected.getLength());
+ if (a2String == aStrAllRejected)
+ eState=SC_CAS_REJECTED;
+ }
+
+ ScChangeAction* pScChangeAction = pChanges->GetFirst();
+ while (pScChangeAction)
+ {
+ if (pScChangeAction->GetState()==eState &&
+ AppendFilteredAction(pScChangeAction, eState, false, &rParent))
+ bTheTestFlag=false;
+ pScChangeAction=pScChangeAction->GetNext();
+ }
+ return bTheTestFlag;
+}
+
+bool ScAcceptChgDlg::InsertChildren(ScChangeActionMap* pActionMap, const weld::TreeIter& rParent)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ bool bTheTestFlag = true;
+
+ for( const auto& rChangeAction : *pActionMap )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = AppendChangeAction(rChangeAction.second, false, &rParent, false, true);
+
+ if (xEntry)
+ {
+ bTheTestFlag=false;
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+ pEntryData->bIsRejectable=false;
+ pEntryData->bIsAcceptable=false;
+ pEntryData->bDisabled=true;
+
+ if (rChangeAction.second->IsDialogParent())
+ Expand(pChanges, rChangeAction.second, *xEntry);
+ }
+ }
+ return bTheTestFlag;
+}
+
+bool ScAcceptChgDlg::InsertDeletedChildren(const ScChangeAction* pScChangeAction,
+ ScChangeActionMap* pActionMap, const weld::TreeIter& rParent)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ bool bTheTestFlag = true;
+ std::unique_ptr<weld::TreeIter> xEntry;
+
+ for( const auto& rChangeAction : *pActionMap )
+ {
+
+ if( pScChangeAction != rChangeAction.second )
+ xEntry = AppendChangeAction(rChangeAction.second, false, &rParent, false, true);
+ else
+ xEntry = AppendChangeAction(rChangeAction.second, false, &rParent, true, true);
+
+ if (xEntry)
+ {
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+ pEntryData->bIsRejectable=false;
+ pEntryData->bIsAcceptable=false;
+ pEntryData->bDisabled=true;
+
+ bTheTestFlag=false;
+
+ if (rChangeAction.second->IsDialogParent())
+ Expand(pChanges, rChangeAction.second, *xEntry);
+ }
+ }
+ return bTheTestFlag;
+}
+
+bool ScAcceptChgDlg::Expand(const ScChangeTrack* pChanges, const ScChangeAction* pScChangeAction,
+ const weld::TreeIter& rEntry, bool bFilter)
+{
+ bool bTheTestFlag = true;
+
+ if (pChanges && pScChangeAction)
+ {
+ ScChangeActionMap aActionMap;
+
+ GetDependents(pScChangeAction, aActionMap, rEntry);
+
+ switch(pScChangeAction->GetType())
+ {
+ case SC_CAT_CONTENT:
+ {
+ InsertContentChildren(&aActionMap, rEntry);
+ bTheTestFlag=!bHasFilterEntry;
+ break;
+ }
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ InsertDeletedChildren(pScChangeAction, &aActionMap, rEntry);
+ bTheTestFlag=!bHasFilterEntry;
+ break;
+ }
+ default:
+ {
+ if(!bFilter)
+ bTheTestFlag = InsertChildren(&aActionMap, rEntry);
+ break;
+ }
+ }
+ aActionMap.clear();
+ }
+ return bTheTestFlag;
+}
+
+IMPL_LINK(ScAcceptChgDlg, ExpandingHandle, const weld::TreeIter&, rEntry, bool)
+{
+ ScChangeTrack* pChanges=pDoc->GetChangeTrack();
+ if (pChanges)
+ {
+ m_xDialog->set_busy_cursor(true);
+ ScChangeActionMap aActionMap;
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rEntry));
+ if (!rTreeView.iter_has_child(rEntry))
+ {
+ bool bTheTestFlag = true;
+
+ if (pEntryData)
+ {
+ ScChangeAction* pScChangeAction=static_cast<ScChangeAction*>(pEntryData->pData);
+
+ GetDependents(pScChangeAction, aActionMap, rEntry);
+
+ switch (pScChangeAction->GetType())
+ {
+ case SC_CAT_CONTENT:
+ {
+ bTheTestFlag = InsertContentChildren( &aActionMap, rEntry );
+ break;
+ }
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ bTheTestFlag = InsertDeletedChildren( pScChangeAction, &aActionMap, rEntry );
+ break;
+ }
+ default:
+ {
+ bTheTestFlag = InsertChildren( &aActionMap, rEntry );
+ break;
+ }
+ }
+ aActionMap.clear();
+
+ }
+ else
+ {
+ bTheTestFlag = InsertAcceptedORejected(rEntry);
+ }
+ if (bTheTestFlag)
+ {
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ rTreeView.insert(&rEntry, -1, &aStrNoEntry, nullptr, nullptr, nullptr, false, xEntry.get());
+ rTreeView.set_font_color(*xEntry, COL_GRAY);
+ }
+ }
+ m_xDialog->set_busy_cursor(false);
+ }
+ return true;
+}
+
+void ScAcceptChgDlg::AppendChanges(const ScChangeTrack* pChanges,sal_uLong nStartAction,
+ sal_uLong nEndAction)
+{
+ if(pChanges==nullptr)
+ return;
+
+ std::unique_ptr<weld::TreeIter> xParent;
+ m_xDialog->set_busy_cursor(true);
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.freeze();
+
+ bool bTheFlag = false;
+
+ bool bFilterFlag = pTPFilter->IsDate() || pTPFilter->IsRange() ||
+ pTPFilter->IsAuthor() || pTPFilter->IsComment();
+
+ bUseColor = bFilterFlag;
+
+ for(sal_uLong i=nStartAction;i<=nEndAction;i++)
+ {
+ const ScChangeAction* pScChangeAction=pChanges->GetAction(i);
+ if(pScChangeAction==nullptr) continue;
+
+ switch (pScChangeAction->GetState())
+ {
+ case SC_CAS_VIRGIN:
+
+ if (pScChangeAction->IsDialogRoot())
+ {
+ bool bOnDemandChildren = !bFilterFlag && pScChangeAction->IsDialogParent();
+ if (pScChangeAction->IsDialogParent())
+ xParent = AppendChangeAction(pScChangeAction, bOnDemandChildren);
+ else
+ xParent = AppendFilteredAction(pScChangeAction, SC_CAS_VIRGIN, bOnDemandChildren);
+ }
+ else
+ xParent.reset();
+
+ bTheFlag=true;
+ break;
+
+ case SC_CAS_ACCEPTED:
+ xParent.reset();
+ nAcceptCount++;
+ break;
+
+ case SC_CAS_REJECTED:
+ xParent.reset();
+ nRejectCount++;
+ break;
+ }
+
+ if (xParent && pScChangeAction->IsDialogParent() && bFilterFlag)
+ {
+ bool bTestFlag = bHasFilterEntry;
+ bHasFilterEntry = false;
+ if (Expand(pChanges,pScChangeAction,*xParent,!bTestFlag)&&!bTestFlag)
+ rTreeView.remove(*xParent);
+ }
+ }
+
+ if( bTheFlag && (!pDoc->IsDocEditable() || pChanges->IsProtected()) )
+ bTheFlag=false;
+
+ pTPView->EnableAccept(bTheFlag);
+ pTPView->EnableAcceptAll(bTheFlag);
+ pTPView->EnableReject(bTheFlag);
+ pTPView->EnableRejectAll(bTheFlag);
+
+ rTreeView.thaw();
+ m_xDialog->set_busy_cursor(false);
+}
+
+void ScAcceptChgDlg::RemoveEntries(sal_uLong nStartAction,sal_uLong nEndAction)
+{
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+
+ ScRedlinData *pEntryData=nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ if (rTreeView.get_cursor(xEntry.get()))
+ pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+
+ if (!rTreeView.get_iter_first(*xEntry))
+ return;
+
+ sal_uLong nAction=0;
+ if (pEntryData)
+ nAction=pEntryData->nActionNo;
+
+ if (nAction>=nStartAction && nAction<=nEndAction)
+ rTreeView.set_cursor(*xEntry);
+
+ std::vector<OUString> aIdsToRemove;
+
+ do
+ {
+ OUString sId(rTreeView.get_id(*xEntry));
+ pEntryData = weld::fromId<ScRedlinData*>(sId);
+ if (pEntryData)
+ {
+ nAction = pEntryData->nActionNo;
+ if (nStartAction <= nAction && nAction <= nEndAction)
+ {
+ aIdsToRemove.push_back(sId);
+ delete pEntryData;
+ }
+ }
+ }
+ while (rTreeView.iter_next(*xEntry));
+
+ rTreeView.freeze();
+
+ // MUST do it backwards, don't delete parents before children and GPF
+ for (auto it = aIdsToRemove.rbegin(); it != aIdsToRemove.rend(); ++it)
+ rTreeView.remove_id(*it);
+
+ rTreeView.thaw();
+}
+
+void ScAcceptChgDlg::UpdateEntries(const ScChangeTrack* pChgTrack, sal_uLong nStartAction,sal_uLong nEndAction)
+{
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.freeze();
+
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ std::unique_ptr<weld::TreeIter> xLastEntry(rTreeView.make_iterator());
+ std::unique_ptr<weld::TreeIter> xNextEntry(rTreeView.make_iterator());
+
+ bool bEntry = rTreeView.get_iter_first(*xEntry);
+ bool bLastEntry = false;
+
+ while (bEntry)
+ {
+ bool bRemove = false;
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+ if (pEntryData)
+ {
+ ScChangeAction* pScChangeAction=
+ static_cast<ScChangeAction*>(pEntryData->pData);
+
+ sal_uLong nAction=pScChangeAction->GetActionNumber();
+
+ if(nStartAction<=nAction && nAction<=nEndAction) bRemove=true;
+ }
+
+ bool bNextEntry;
+ if (bRemove)
+ {
+ rTreeView.remove(*xEntry);
+ delete pEntryData;
+
+ if (!bLastEntry)
+ bLastEntry = rTreeView.get_iter_first(*xLastEntry);
+ if (bLastEntry)
+ {
+ rTreeView.copy_iterator(*xLastEntry, *xNextEntry);
+ bNextEntry = rTreeView.iter_next(*xNextEntry);
+ if (!bNextEntry)
+ {
+ rTreeView.copy_iterator(*xLastEntry, *xNextEntry);
+ bLastEntry = false;
+ }
+ }
+ else
+ bNextEntry = false;
+ }
+ else
+ {
+ rTreeView.copy_iterator(*xEntry, *xLastEntry);
+ bLastEntry = true;
+
+ rTreeView.copy_iterator(*xEntry, *xNextEntry);
+ bNextEntry = rTreeView.iter_next(*xNextEntry);
+ }
+
+ rTreeView.copy_iterator(*xNextEntry, *xEntry);
+ bEntry = bNextEntry;
+ }
+
+ AppendChanges(pChgTrack,nStartAction,nEndAction);
+
+ rTreeView.thaw();
+}
+
+IMPL_LINK( ScAcceptChgDlg, ChgTrackModHdl, ScChangeTrack&, rChgTrack, void)
+{
+ ScChangeTrackMsgQueue& aMsgQueue= rChgTrack.GetMsgQueue();
+
+ sal_uLong nStartAction;
+ sal_uLong nEndAction;
+
+ for (const auto& rMsg : aMsgQueue)
+ {
+ nStartAction = rMsg.nStartAction;
+ nEndAction = rMsg.nEndAction;
+
+ if(!bIgnoreMsg)
+ {
+ bNoSelection=true;
+
+ switch(rMsg.eMsgType)
+ {
+ case ScChangeTrackMsgType::Append:
+ AppendChanges(&rChgTrack,nStartAction,nEndAction);
+ break;
+ case ScChangeTrackMsgType::Remove:
+ RemoveEntries(nStartAction,nEndAction);
+ break;
+ case ScChangeTrackMsgType::Parent:
+ case ScChangeTrackMsgType::Change: //bNeedsUpdate=true;
+ UpdateEntries(&rChgTrack,nStartAction,nEndAction);
+ break;
+ default: assert(false); break;
+ }
+ }
+ }
+
+ aMsgQueue.clear();
+}
+
+IMPL_LINK_NOARG(ScAcceptChgDlg, ReOpenTimerHdl, Timer *, void)
+{
+ ScSimpleRefDlgWrapper::SetAutoReOpen(true);
+ m_xAcceptChgCtr->ShowFilterPage();
+ RefHandle(nullptr);
+}
+
+IMPL_LINK_NOARG(ScAcceptChgDlg, UpdateSelectionHdl, Timer *, void)
+{
+ ScTabView* pTabView = pViewData->GetView();
+
+ bool bAcceptFlag = true;
+ bool bRejectFlag = true;
+
+ pTabView->DoneBlockMode(); // clears old marking
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ std::vector<const ScChangeAction*> aActions;
+ rTreeView.selected_foreach([&rTreeView, &bAcceptFlag, &bRejectFlag, &aActions](weld::TreeIter& rEntry){
+ ScRedlinData* pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rEntry));
+ if (pEntryData)
+ {
+ bRejectFlag &= pEntryData->bIsRejectable;
+ bAcceptFlag &= pEntryData->bIsAcceptable;
+
+ const ScChangeAction* pScChangeAction = static_cast<ScChangeAction*>(pEntryData->pData);
+ if( pScChangeAction && (pScChangeAction->GetType() != SC_CAT_DELETE_TABS) &&
+ (!pEntryData->bDisabled || pScChangeAction->IsVisible()) )
+ {
+ aActions.push_back(pScChangeAction);
+ }
+ }
+ else
+ {
+ bAcceptFlag = false;
+ bRejectFlag = false;
+ }
+ return false;
+ });
+
+ bool bContMark = false;
+ for (size_t i = 0, nCount = aActions.size(); i < nCount; ++i)
+ {
+ const ScBigRange& rBigRange = aActions[i]->GetBigRange();
+ if (rBigRange.IsValid(*pDoc) && m_xDialog->has_toplevel_focus())
+ {
+ bool bSetCursor = i == nCount - 1;
+ pTabView->MarkRange(rBigRange.MakeRange(*pDoc), bSetCursor, bContMark);
+ bContMark = true;
+ }
+ }
+
+ ScChangeTrack* pChanges = pDoc->GetChangeTrack();
+ bool bEnable = pDoc->IsDocEditable() && pChanges && !pChanges->IsProtected();
+ pTPView->EnableAccept( bAcceptFlag && bEnable );
+ pTPView->EnableReject( bRejectFlag && bEnable );
+}
+
+IMPL_LINK(ScAcceptChgDlg, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ bool bEntry = rTreeView.get_cursor(xEntry.get());
+ if (bEntry)
+ rTreeView.select(*xEntry);
+
+ int nSortedCol = rTreeView.get_sort_column();
+ for (sal_Int32 i = 0; i < 5; ++i)
+ m_xSortMenu->set_active("calcsort" + OUString::number(i), i == nSortedCol);
+
+ m_xPopup->set_sensitive("calcedit", false);
+
+ if (pDoc->IsDocEditable() && bEntry)
+ {
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+ if (pEntryData)
+ {
+ ScChangeAction* pScChangeAction = static_cast<ScChangeAction*>(pEntryData->pData);
+ if (pScChangeAction && !rTreeView.get_iter_depth(*xEntry))
+ m_xPopup->set_sensitive("calcedit", true);
+ }
+ }
+
+ OUString sCommand = m_xPopup->popup_at_rect(&rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
+
+ if (!sCommand.isEmpty())
+ {
+ if (sCommand == "calcedit")
+ {
+ if (bEntry)
+ {
+ ScRedlinData *pEntryData = weld::fromId<ScRedlinData*>(rTreeView.get_id(*xEntry));
+ if (pEntryData)
+ {
+ ScChangeAction* pScChangeAction = static_cast<ScChangeAction*>(pEntryData->pData);
+ pViewData->GetDocShell()->ExecuteChangeCommentDialog(pScChangeAction, m_xDialog.get(), false);
+ }
+ }
+ }
+ else
+ {
+ int nDialogCol = o3tl::toInt32(sCommand.subView(8));
+ pTheView->HeaderBarClick(nDialogCol);
+ }
+ }
+
+ return true;
+}
+
+namespace
+{
+ //at one point we were writing multiple AcceptChgDat strings,
+ //so strip all of them and keep the results of the last one
+ OUString lcl_StripAcceptChgDat(OUString &rExtraString)
+ {
+ OUString aStr;
+ while (true)
+ {
+ sal_Int32 nPos = rExtraString.indexOf("AcceptChgDat:");
+ if (nPos == -1)
+ break;
+ // Try to read the alignment string "ALIGN:(...)"; if it is missing
+ // we have an old version
+ sal_Int32 n1 = rExtraString.indexOf('(', nPos);
+ if ( n1 != -1 )
+ {
+ sal_Int32 n2 = rExtraString.indexOf(')', n1);
+ if ( n2 != -1 )
+ {
+ // cut out alignment string
+ aStr = rExtraString.copy(nPos, n2 - nPos + 1);
+ rExtraString = rExtraString.replaceAt(nPos, n2 - nPos + 1, u"");
+ aStr = aStr.copy( n1-nPos+1 );
+ }
+ }
+ }
+ return aStr;
+ }
+}
+
+void ScAcceptChgDlg::Initialize(SfxChildWinInfo* pInfo)
+{
+ OUString aStr;
+ if (pInfo && !pInfo->aExtraString.isEmpty())
+ aStr = lcl_StripAcceptChgDat(pInfo->aExtraString);
+
+ SfxModelessDialogController::Initialize(pInfo);
+
+ if (aStr.isEmpty())
+ return;
+
+ int nCount = aStr.toInt32();
+ if (nCount <= 2)
+ return;
+
+ std::vector<int> aEndPos;
+
+ for (int i = 0; i < nCount; ++i)
+ {
+ sal_Int32 n1 = aStr.indexOf(';');
+ aStr = aStr.copy( n1+1 );
+ aEndPos.push_back(aStr.toInt32());
+ }
+
+ std::vector<int> aWidths;
+ for (int i = 1; i < nCount; ++i)
+ aWidths.push_back(aEndPos[i] - aEndPos[i - 1]);
+
+ // turn column end points back to column widths, ignoring the small
+ // value used for the expander column
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ rTreeView.set_column_fixed_widths(aWidths);
+}
+
+void ScAcceptChgDlg::FillInfo(SfxChildWinInfo& rInfo) const
+{
+ SfxModelessDialogController::FillInfo(rInfo);
+ //remove any old one before adding a new one
+ lcl_StripAcceptChgDat(rInfo.aExtraString);
+ rInfo.aExtraString += "AcceptChgDat:(";
+
+ const int nTabCount = 5;
+
+ rInfo.aExtraString += OUString::number(nTabCount);
+ rInfo.aExtraString += ";";
+
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+ std::vector<int> aWidths;
+ // turn column widths back into column end points for compatibility
+ // with how they used to be stored, including a small value for the
+ // expander column
+ aWidths.push_back(rTreeView.get_checkbox_column_width());
+ for (int i = 0; i < nTabCount - 1; ++i)
+ aWidths.push_back(aWidths.back() + rTreeView.get_column_width(i));
+
+ for (auto a : aWidths)
+ {
+ rInfo.aExtraString += OUString::number(a);
+ rInfo.aExtraString += ";";
+ }
+ rInfo.aExtraString += ")";
+}
+
+#define CALC_DATE 3
+#define CALC_POS 1
+
+int ScAcceptChgDlg::ColCompareHdl(const weld::TreeIter& rLeft, const weld::TreeIter& rRight) const
+{
+ weld::TreeView& rTreeView = pTheView->GetWidget();
+
+ sal_Int32 nCompare = 0;
+ SCCOL nSortCol = rTreeView.get_sort_column();
+
+ if (CALC_DATE == nSortCol)
+ {
+ RedlinData *pLeftData = weld::fromId<RedlinData*>(rTreeView.get_id(rLeft));
+ RedlinData *pRightData = weld::fromId<RedlinData*>(rTreeView.get_id(rRight));
+ if (pLeftData && pRightData)
+ {
+ if(pLeftData->aDateTime < pRightData->aDateTime)
+ nCompare = -1;
+ else if(pLeftData->aDateTime > pRightData->aDateTime)
+ nCompare = 1;
+ return nCompare;
+ }
+ }
+ else if (CALC_POS == nSortCol)
+ {
+ ScRedlinData *pLeftData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rLeft));
+ ScRedlinData *pRightData = weld::fromId<ScRedlinData*>(rTreeView.get_id(rRight));
+ if (pLeftData && pRightData)
+ {
+ nCompare = 1;
+
+ if(pLeftData->nTable < pRightData->nTable)
+ nCompare = -1;
+ else if(pLeftData->nTable == pRightData->nTable)
+ {
+ if(pLeftData->nRow < pRightData->nRow)
+ nCompare = -1;
+ else if(pLeftData->nRow == pRightData->nRow)
+ {
+ if(pLeftData->nCol < pRightData->nCol)
+ nCompare = -1;
+ else if(pLeftData->nCol == pRightData->nCol)
+ nCompare = 0;
+ }
+ }
+
+ return nCompare;
+ }
+ }
+
+ return ScGlobal::GetCaseCollator().compareString(rTreeView.get_text(rLeft, nSortCol),
+ rTreeView.get_text(rRight, nSortCol));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/anyrefdg.cxx b/sc/source/ui/miscdlgs/anyrefdg.cxx
new file mode 100644
index 0000000000..579a61ee23
--- /dev/null
+++ b/sc/source/ui/miscdlgs/anyrefdg.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 <rangelst.hxx>
+#include <comphelper/string.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <anyrefdg.hxx>
+#include <sc.hrc>
+#include <inputhdl.hxx>
+#include <scmod.hxx>
+#include <inputwin.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <rfindlst.hxx>
+#include <compiler.hxx>
+#include <inputopt.hxx>
+#include <rangeutl.hxx>
+#include <tokenarray.hxx>
+#include <comphelper/lok.hxx>
+#include <output.hxx>
+
+#include <memory>
+
+ScFormulaReferenceHelper::ScFormulaReferenceHelper(IAnyRefDialog* _pDlg,SfxBindings* _pBindings)
+ : m_pDlg(_pDlg)
+ , m_pRefEdit (nullptr)
+ , m_pRefBtn (nullptr)
+ , m_pDialog(nullptr)
+ , m_pBindings(_pBindings)
+ , m_nRefTab(0)
+ , m_bHighlightRef(false)
+{
+ ScInputOptions aInputOption=SC_MOD()->GetInputOptions();
+ m_bEnableColorRef=aInputOption.GetRangeFinder();
+}
+
+ScFormulaReferenceHelper::~ScFormulaReferenceHelper() COVERITY_NOEXCEPT_FALSE
+{
+ dispose();
+}
+
+void ScFormulaReferenceHelper::dispose()
+{
+ // common cleanup for ScAnyRefDlg and ScFormulaDlg is done here
+ HideReference();
+ enableInput( true );
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl )
+ pInputHdl->ResetDelayTimer(); // stop the timer for disabling the input line
+
+ m_pDialog = nullptr;
+}
+
+void ScFormulaReferenceHelper::enableInput( bool bEnable )
+{
+ ScDocShell* pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>));
+ while( pDocShell )
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell );
+ while( pFrame )
+ {
+ // enable everything except InPlace, including bean frames
+ if ( !pFrame->GetFrame().IsInPlace() )
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ if(pViewSh!=nullptr)
+ {
+ vcl::Window *pWin=pViewSh->GetWindow();
+ if(pWin)
+ {
+ vcl::Window *pParent=pWin->GetParent();
+ if(pParent)
+ {
+ pParent->EnableInput(bEnable);
+ pViewSh->EnableRefInput(bEnable);
+ }
+ }
+ }
+ }
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell );
+ }
+
+ pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell<ScDocShell>));
+ }
+}
+
+void ScFormulaReferenceHelper::ShowSimpleReference(std::u16string_view rStr)
+{
+ if (!m_bEnableColorRef)
+ return;
+
+ m_bHighlightRef = true;
+ ScViewData* pViewData=ScDocShell::GetViewData();
+ if ( !pViewData )
+ return;
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScTabViewShell* pTabViewShell=pViewData->GetViewShell();
+
+ ScRangeList aRangeList;
+
+ pTabViewShell->DoneRefMode();
+ pTabViewShell->ClearHighlightRanges();
+
+ if( ParseWithNames( aRangeList, rStr, rDoc ) )
+ {
+ for ( size_t i = 0, nRanges = aRangeList.size(); i < nRanges; ++i )
+ {
+ ScRange const & rRangeEntry = aRangeList[ i ];
+ Color aColName = ScRangeFindList::GetColorName( i );
+ pTabViewShell->AddHighlightRange( rRangeEntry, aColName );
+ }
+ }
+}
+
+bool ScFormulaReferenceHelper::ParseWithNames( ScRangeList& rRanges, std::u16string_view rStr, const ScDocument& rDoc )
+{
+ rRanges.RemoveAll();
+
+ if (rStr.empty())
+ return true;
+
+ ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0);
+
+ bool bError = false;
+ sal_Int32 nIdx {0};
+ do
+ {
+ ScRange aRange;
+ OUString aRangeStr( o3tl::getToken(rStr, 0, ';', nIdx ) );
+
+ ScRefFlags nFlags = aRange.ParseAny( aRangeStr, rDoc, aDetails );
+ if ( nFlags & ScRefFlags::VALID )
+ {
+ if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO )
+ aRange.aStart.SetTab( m_nRefTab );
+ if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO )
+ aRange.aEnd.SetTab( aRange.aStart.Tab() );
+ rRanges.push_back( aRange );
+ }
+ else if ( ScRangeUtil::MakeRangeFromName( aRangeStr, rDoc, m_nRefTab, aRange, RUTL_NAMES, aDetails ) )
+ rRanges.push_back( aRange );
+ else
+ bError = true;
+ }
+ while (nIdx>0);
+
+ return !bError;
+}
+
+void ScFormulaReferenceHelper::ShowFormulaReference(const OUString& rStr)
+{
+ if( !m_bEnableColorRef)
+ return;
+
+ m_bHighlightRef=true;
+ ScViewData* pViewData=ScDocShell::GetViewData();
+ if ( !(pViewData && m_pRefComp) )
+ return;
+
+ ScTabViewShell* pTabViewShell=pViewData->GetViewShell();
+ SCCOL nCol = pViewData->GetCurX();
+ SCROW nRow = pViewData->GetCurY();
+ SCTAB nTab = pViewData->GetTabNo();
+ ScAddress aPos( nCol, nRow, nTab );
+
+ std::unique_ptr<ScTokenArray> pScTokA(m_pRefComp->CompileString(rStr));
+
+ if (!(pTabViewShell && pScTokA))
+ return;
+
+ const ScViewData& rViewData = pTabViewShell->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ pTabViewShell->DoneRefMode();
+ pTabViewShell->ClearHighlightRanges();
+
+ formula::FormulaTokenArrayPlainIterator aIter(*pScTokA);
+ const formula::FormulaToken* pToken = aIter.GetNextReference();
+
+ sal_uInt16 nIndex=0;
+
+ while(pToken!=nullptr)
+ {
+ bool bDoubleRef=(pToken->GetType()==formula::svDoubleRef);
+
+ if(pToken->GetType()==formula::svSingleRef || bDoubleRef)
+ {
+ ScRange aRange;
+ if(bDoubleRef)
+ {
+ ScComplexRefData aRef( *pToken->GetDoubleRef() );
+ aRange = aRef.toAbs(rDoc, aPos);
+ }
+ else
+ {
+ ScSingleRefData aRef( *pToken->GetSingleRef() );
+ aRange.aStart = aRef.toAbs(rDoc, aPos);
+ aRange.aEnd = aRange.aStart;
+ }
+ Color aColName=ScRangeFindList::GetColorName(nIndex++);
+ pTabViewShell->AddHighlightRange(aRange, aColName);
+ }
+
+ pToken = aIter.GetNextReference();
+ }
+}
+
+void ScFormulaReferenceHelper::HideReference( bool bDoneRefMode )
+{
+ ScViewData* pViewData=ScDocShell::GetViewData();
+
+ if( !(pViewData && m_bHighlightRef && m_bEnableColorRef))
+ return;
+
+ ScTabViewShell* pTabViewShell=pViewData->GetViewShell();
+
+ if(pTabViewShell!=nullptr)
+ {
+ // bDoneRefMode is sal_False when called from before SetReference.
+ // In that case, RefMode was just started and must not be ended now.
+
+ if ( bDoneRefMode )
+ pTabViewShell->DoneRefMode();
+ pTabViewShell->ClearHighlightRanges();
+
+ if( comphelper::LibreOfficeKit::isActive() )
+ {
+ // Clear
+ std::vector<ReferenceMark> aReferenceMarks;
+ ScInputHandler::SendReferenceMarks( pTabViewShell, aReferenceMarks );
+ }
+ }
+ m_bHighlightRef=false;
+}
+
+void ScFormulaReferenceHelper::ShowReference(const OUString& rStr)
+{
+ if( !m_bEnableColorRef )
+ return;
+
+ // Exclude ';' semicolon as it is the separator for ParseWithNames() used
+ // in ShowSimpleReference(). Also sheet separator '.' dot is part of simple
+ // reference (could be array col/row separator as well but then in '{' '}'
+ // braces). Prefer '!' exclamation mark to be intersection operator rather
+ // than Excel sheet separator.
+ if (comphelper::string::indexOfAny( rStr, u"()+-*/^%&=<>~! #[]{},|\\@", 0) != -1)
+ {
+ ShowFormulaReference(rStr);
+ }
+ else
+ {
+ ShowSimpleReference(rStr);
+ }
+}
+
+void ScFormulaReferenceHelper::ReleaseFocus( formula::RefEdit* pEdit )
+{
+ if( !m_pRefEdit && pEdit )
+ {
+ m_pDlg->RefInputStart( pEdit );
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if( !pViewShell )
+ return;
+
+ pViewShell->ActiveGrabFocus();
+ if( !m_pRefEdit )
+ return;
+
+ const ScViewData& rViewData = pViewShell->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScRangeList aRangeList;
+ if( !ParseWithNames( aRangeList, m_pRefEdit->GetText(), rDoc ) )
+ return;
+
+ if ( !aRangeList.empty() )
+ {
+ const ScRange & rRange = aRangeList.front();
+ pViewShell->SetTabNo( rRange.aStart.Tab() );
+ pViewShell->MoveCursorAbs( rRange.aStart.Col(),
+ rRange.aStart.Row(), SC_FOLLOW_JUMP, false, false );
+ pViewShell->MoveCursorAbs( rRange.aEnd.Col(),
+ rRange.aEnd.Row(), SC_FOLLOW_JUMP, true, false );
+ m_pDlg->SetReference( rRange, rDoc );
+ }
+}
+
+void ScFormulaReferenceHelper::Init()
+{
+ ScViewData* pViewData=ScDocShell::GetViewData(); //! use pScViewShell?
+ if ( !pViewData )
+ return;
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ SCCOL nCol = pViewData->GetCurX();
+ SCROW nRow = pViewData->GetCurY();
+ SCTAB nTab = pViewData->GetTabNo();
+ ScAddress aCursorPos( nCol, nRow, nTab );
+
+ m_pRefComp.reset( new ScCompiler( rDoc, aCursorPos, rDoc.GetGrammar()) );
+ m_pRefComp->EnableJumpCommandReorder(false);
+ m_pRefComp->EnableStopOnError(false);
+
+ m_nRefTab = nTab;
+}
+
+IMPL_LINK_NOARG(ScFormulaReferenceHelper, ActivateHdl, weld::Widget&, bool)
+{
+ if (m_pRefEdit)
+ m_pRefEdit->GrabFocus();
+ m_pDlg->RefInputDone(true);
+ return true;
+}
+
+void ScFormulaReferenceHelper::RefInputDone( bool bForced )
+{
+ if ( !CanInputDone( bForced ) )
+ return;
+
+ if (!m_pDialog)
+ return;
+
+ // Adjust window title
+ m_pDialog->set_title(m_sOldDialogText);
+
+ if (m_pRefEdit)
+ m_pRefEdit->SetActivateHdl(Link<weld::Widget&, bool>());
+
+ // set button image
+ if (m_pRefBtn)
+ {
+ m_pRefBtn->SetActivateHdl(Link<weld::Widget&, bool>());
+ m_pRefBtn->SetStartImage();
+ }
+
+ m_pDialog->undo_collapse();
+
+ m_pRefEdit = nullptr;
+ m_pRefBtn = nullptr;
+}
+
+void ScFormulaReferenceHelper::RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton )
+{
+ if (m_pRefEdit)
+ return;
+
+ m_pRefEdit = pEdit;
+ m_pRefBtn = pButton;
+
+ // Save and adjust window title
+ m_sOldDialogText = m_pDialog->get_title();
+ if (weld::Label *pLabel = m_pRefEdit->GetLabelWidgetForShrinkMode())
+ {
+ const OUString sLabel = pLabel->get_label();
+ if (!sLabel.isEmpty())
+ {
+ const OUString sNewDialogText = m_sOldDialogText + ": " + comphelper::string::stripEnd(sLabel, ':');
+ m_pDialog->set_title(pLabel->strip_mnemonic(sNewDialogText));
+ }
+ }
+
+ m_pDialog->collapse(pEdit->GetWidget(), pButton ? pButton->GetWidget() : nullptr);
+
+ // set button image
+ if (pButton)
+ pButton->SetEndImage();
+
+ m_pRefEdit->SetActivateHdl(LINK(this, ScFormulaReferenceHelper, ActivateHdl));
+ if (m_pRefBtn)
+ m_pRefBtn->SetActivateHdl(LINK(this, ScFormulaReferenceHelper, ActivateHdl));
+}
+
+void ScFormulaReferenceHelper::ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton )
+{
+ if( !pEdit )
+ return;
+
+ if( m_pRefEdit == pEdit ) // is this the active ref edit field?
+ {
+ m_pRefEdit->GrabFocus(); // before RefInputDone()
+ m_pDlg->RefInputDone( true ); // finish ref input
+ }
+ else
+ {
+ m_pDlg->RefInputDone( true ); // another active ref edit?
+ m_pDlg->RefInputStart( pEdit, pButton ); // start ref input
+ // pRefEdit might differ from pEdit after RefInputStart() (i.e. ScFormulaDlg)
+ if( m_pRefEdit )
+ m_pRefEdit->GrabFocus();
+ }
+}
+
+void ScFormulaReferenceHelper::DoClose( sal_uInt16 nId )
+{
+ SfxApplication* pSfxApp = SfxGetpApp();
+
+ SetDispatcherLock( false ); //! here and in dtor ?
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if ( pViewFrm && pViewFrm->HasChildWindow(FID_INPUTLINE_STATUS) )
+ {
+ // The input row is disabled with ToolBox::Disable disabled, thus it must be
+ // reenabled with ToolBox::Enable (before the AppWindow is enabled)
+ // for the buttons to be drawn as enabled.
+ SfxChildWindow* pChild = pViewFrm->GetChildWindow(FID_INPUTLINE_STATUS);
+ if (pChild)
+ {
+ ScInputWindow* pWin = static_cast<ScInputWindow*>(pChild->GetWindow());
+ pWin->Enable();
+ }
+ }
+
+ // find parent view frame to close dialog
+ SfxViewFrame* pMyViewFrm = nullptr;
+ if ( m_pBindings )
+ {
+ SfxDispatcher* pMyDisp = m_pBindings->GetDispatcher();
+ if (pMyDisp)
+ pMyViewFrm = pMyDisp->GetFrame();
+ }
+ SC_MOD()->SetRefDialog( nId, false, pMyViewFrm );
+
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
+
+ ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( pScViewShell )
+ pScViewShell->UpdateInputHandler(true);
+}
+
+void ScFormulaReferenceHelper::SetDispatcherLock( bool bLock )
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // lock / unlock only the dispatchers of Calc documents
+ ScDocShell* pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>));
+ while (pDocShell)
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDocShell);
+ while (pFrame)
+ {
+ SfxDispatcher* pDisp = pFrame->GetDispatcher();
+ if (pDisp)
+ pDisp->Lock(bLock);
+ pFrame = SfxViewFrame::GetNext(*pFrame, pDocShell);
+ }
+ pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell<ScDocShell>));
+ }
+ return;
+ // if a new view is created while the dialog is open,
+ // that view's dispatcher is locked when trying to create the dialog
+ // for that view (ScTabViewShell::CreateRefDialog)
+ }
+
+ // lock / unlock only the dispatcher of Calc document
+ SfxDispatcher* pDisp = nullptr;
+ if ( m_pBindings )
+ {
+ pDisp = m_pBindings->GetDispatcher();
+ }
+ else if(SfxViewFrame* pViewFrame = SfxViewFrame::Current())
+ {
+ if (dynamic_cast< ScTabViewShell* >(pViewFrame->GetViewShell()))
+ pDisp = pViewFrame->GetDispatcher();
+ }
+
+ if (pDisp)
+ pDisp->Lock(bLock);
+}
+
+void ScFormulaReferenceHelper::ViewShellChanged()
+{
+ enableInput( false );
+
+ EnableSpreadsheets();
+}
+void ScFormulaReferenceHelper::EnableSpreadsheets(bool bFlag)
+{
+ ScDocShell* pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>));
+ while( pDocShell )
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell );
+ while( pFrame )
+ {
+ // enable everything except InPlace, including bean frames
+ if ( !pFrame->GetFrame().IsInPlace() )
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ if(pViewSh!=nullptr)
+ {
+ vcl::Window *pWin=pViewSh->GetWindow();
+ if(pWin)
+ {
+ vcl::Window *pParent=pWin->GetParent();
+ if(pParent)
+ {
+ pParent->EnableInput(bFlag,false);
+ pViewSh->EnableRefInput(bFlag);
+ }
+ }
+ }
+ }
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell );
+ }
+
+ pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell<ScDocShell>));
+ }
+}
+
+static void lcl_InvalidateWindows()
+{
+ ScDocShell* pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>));
+ while( pDocShell )
+ {
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDocShell );
+ while( pFrame )
+ {
+ // enable everything except InPlace, including bean frames
+ if ( !pFrame->GetFrame().IsInPlace() )
+ {
+ SfxViewShell* p = pFrame->GetViewShell();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( p );
+ if(pViewSh!=nullptr)
+ {
+ vcl::Window *pWin=pViewSh->GetWindow();
+ if(pWin)
+ {
+ vcl::Window *pParent=pWin->GetParent();
+ if(pParent)
+ pParent->Invalidate();
+ }
+ }
+ }
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell );
+ }
+
+ pDocShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pDocShell, checkSfxObjectShell<ScDocShell>));
+ }
+}
+
+static void lcl_HideAllReferences()
+{
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh )
+ {
+ static_cast<ScTabViewShell*>(pSh)->ClearHighlightRanges();
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+}
+
+ScRefHandler::ScRefHandler(SfxDialogController& rController, SfxBindings* pB, bool bBindRef)
+ : m_pController(&rController)
+ , m_bInRefMode(false)
+ , m_aHelper(this, pB)
+ , m_pMyBindings(pB)
+{
+ m_aHelper.SetDialog(rController.getDialog());
+
+ if( bBindRef ) EnterRefMode();
+}
+
+bool ScRefHandler::EnterRefMode()
+{
+ if( m_bInRefMode ) return false;
+
+ SC_MOD()->InputEnterHandler();
+
+ ScTabViewShell* pScViewShell = nullptr;
+
+ // title has to be from the view that opened the dialog,
+ // even if it's not the current view
+
+ SfxObjectShell* pParentDoc = nullptr;
+ if ( m_pMyBindings )
+ {
+ SfxDispatcher* pMyDisp = m_pMyBindings->GetDispatcher();
+ if (pMyDisp)
+ {
+ SfxViewFrame* pMyViewFrm = pMyDisp->GetFrame();
+ if (pMyViewFrm)
+ {
+ pScViewShell = dynamic_cast<ScTabViewShell*>( pMyViewFrm->GetViewShell() );
+ if( pScViewShell )
+ pScViewShell->UpdateInputHandler(true);
+ pParentDoc = pMyViewFrm->GetObjectShell();
+ }
+ }
+ }
+ if ( !pParentDoc && pScViewShell ) // use current only if above fails
+ pParentDoc = pScViewShell->GetObjectShell();
+ if ( pParentDoc )
+ m_aDocName = pParentDoc->GetTitle();
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(pScViewShell);
+
+ OSL_ENSURE( pInputHdl, "Missing input handler :-/" );
+
+ if ( pInputHdl )
+ pInputHdl->NotifyChange( nullptr );
+
+ ScFormulaReferenceHelper::enableInput( false );
+
+ ScFormulaReferenceHelper::EnableSpreadsheets();
+
+ m_aHelper.Init();
+
+ m_aHelper.SetDispatcherLock( true );
+
+ m_bInRefMode = true;
+ return m_bInRefMode;
+}
+
+ScRefHandler::~ScRefHandler() COVERITY_NOEXCEPT_FALSE
+{
+ disposeRefHandler();
+}
+
+void ScRefHandler::disposeRefHandler()
+{
+ m_pController = nullptr;
+ LeaveRefMode();
+ m_aHelper.dispose();
+}
+
+bool ScRefHandler::LeaveRefMode()
+{
+ if( !m_bInRefMode ) return false;
+
+ lcl_HideAllReferences();
+
+ SetDispatcherLock( false ); //! here and in DoClose ?
+
+ ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
+ if( pScViewShell )
+ pScViewShell->UpdateInputHandler(true);
+
+ lcl_InvalidateWindows();
+
+ m_bInRefMode = false;
+ return true;
+}
+
+void ScRefHandler::SwitchToDocument()
+{
+ ScTabViewShell* pCurrent = ScTabViewShell::GetActiveViewShell();
+ if (pCurrent)
+ {
+ SfxObjectShell* pObjSh = pCurrent->GetObjectShell();
+ if ( pObjSh && pObjSh->GetTitle() == m_aDocName )
+ {
+ // right document already visible -> nothing to do
+ return;
+ }
+ }
+
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh )
+ {
+ SfxObjectShell* pObjSh = pSh->GetObjectShell();
+ if ( pObjSh && pObjSh->GetTitle() == m_aDocName )
+ {
+ // switch to first TabViewShell for document
+ static_cast<ScTabViewShell*>(pSh)->SetActive();
+ return;
+ }
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+}
+
+bool ScRefHandler::IsDocAllowed(SfxObjectShell* pDocSh) const // pDocSh may be 0
+{
+ // if aDocName isn't initialized, allow
+ if ( m_aDocName.isEmpty() )
+ return true;
+
+ if ( !pDocSh )
+ return false;
+
+ // default: allow only same document (overridden in function dialog)
+ return m_aDocName==pDocSh->GetTitle();
+}
+
+bool ScRefHandler::IsRefInputMode() const
+{
+ return m_pController->getDialog()->get_visible();
+}
+
+bool ScRefHandler::DoClose( sal_uInt16 nId )
+{
+ m_aHelper.DoClose(nId);
+ return true;
+}
+
+void ScRefHandler::SetDispatcherLock( bool bLock )
+{
+ m_aHelper.SetDispatcherLock( bLock );
+}
+
+void ScRefHandler::ViewShellChanged()
+{
+ ScFormulaReferenceHelper::ViewShellChanged();
+}
+
+void ScRefHandler::AddRefEntry()
+{
+ // override this for multi-references
+}
+
+bool ScRefHandler::IsTableLocked() const
+{
+ // the default is that the sheet can be switched during while the reference is edited
+
+ return false;
+}
+
+// RefInputStart/Done: Zoom-In (AutoHide) on single field
+// (using button or movement)
+
+void ScRefHandler::RefInputStart( formula::RefEdit* pEdit, formula::RefButton* pButton )
+{
+ m_aHelper.RefInputStart( pEdit, pButton );
+}
+
+void ScRefHandler::ToggleCollapsed( formula::RefEdit* pEdit, formula::RefButton* pButton )
+{
+ m_aHelper.ToggleCollapsed( pEdit, pButton );
+}
+
+bool ScRefHandler::ParseWithNames( ScRangeList& rRanges, std::u16string_view rStr, const ScDocument& rDoc )
+{
+ return m_aHelper.ParseWithNames( rRanges, rStr, rDoc );
+}
+
+void ScRefHandler::HideReference( bool bDoneRefMode )
+{
+ m_aHelper.HideReference( bDoneRefMode );
+}
+
+void ScRefHandler::ShowReference(const OUString& rStr)
+{
+ m_aHelper.ShowReference(rStr);
+}
+
+void ScRefHandler::ReleaseFocus( formula::RefEdit* pEdit )
+{
+ m_aHelper.ReleaseFocus( pEdit );
+}
+
+void ScRefHandler::RefInputDone( bool bForced )
+{
+ m_aHelper.RefInputDone( bForced );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/autofmt.cxx b/sc/source/ui/miscdlgs/autofmt.cxx
new file mode 100644
index 0000000000..877c412a59
--- /dev/null
+++ b/sc/source/ui/miscdlgs/autofmt.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 <scitems.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <svtools/scriptedtext.hxx>
+#include <svx/framelink.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <comphelper/processfactory.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+
+#include <strings.hrc>
+#include <zforauto.hxx>
+#include <global.hxx>
+#include <autoform.hxx>
+#include <autofmt.hxx>
+#include <scresid.hxx>
+#include <document.hxx>
+#include <viewdata.hxx>
+#include <svtools/colorcfg.hxx>
+#include <scmod.hxx>
+
+#define FRAME_OFFSET 4
+
+// ScAutoFmtPreview
+
+ScAutoFmtPreview::ScAutoFmtPreview()
+ : pCurData(nullptr)
+ , bFitWidth(false)
+ , mbRTL(false)
+ , aStrJan(ScResId(STR_JAN))
+ , aStrFeb(ScResId(STR_FEB))
+ , aStrMar(ScResId(STR_MAR))
+ , aStrNorth(ScResId(STR_NORTH))
+ , aStrMid(ScResId(STR_MID))
+ , aStrSouth(ScResId(STR_SOUTH))
+ , aStrSum(ScResId(STR_SUM))
+ , pNumFmt(new SvNumberFormatter(::comphelper::getProcessComponentContext(), ScGlobal::eLnge))
+{
+ Init();
+}
+
+void ScAutoFmtPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ aVD.disposeAndReset(VclPtr<VirtualDevice>::Create(pDrawingArea->get_ref_device()));
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+}
+
+void ScAutoFmtPreview::Resize()
+{
+ Size aSize(GetOutputSizePixel());
+ aPrvSize = Size(aSize.Width() - 6, aSize.Height() - 30);
+ mnLabelColWidth = (aPrvSize.Width() - 4) / 4 - 12;
+ mnDataColWidth1 = (aPrvSize.Width() - 4 - 2 * mnLabelColWidth) / 3;
+ mnDataColWidth2 = (aPrvSize.Width() - 4 - 2 * mnLabelColWidth) / 4;
+ mnRowHeight = (aPrvSize.Height() - 4) / 5;
+ NotifyChange(pCurData);
+}
+
+ScAutoFmtPreview::~ScAutoFmtPreview()
+{
+}
+
+static void lcl_SetFontProperties(
+ vcl::Font& rFont,
+ const SvxFontItem& rFontItem,
+ const SvxWeightItem& rWeightItem,
+ const SvxPostureItem& rPostureItem )
+{
+ rFont.SetFamily ( rFontItem.GetFamily() );
+ rFont.SetFamilyName ( rFontItem.GetFamilyName() );
+ rFont.SetStyleName ( rFontItem.GetStyleName() );
+ rFont.SetCharSet ( rFontItem.GetCharSet() );
+ rFont.SetPitch ( rFontItem.GetPitch() );
+ rFont.SetWeight ( rWeightItem.GetValue() );
+ rFont.SetItalic ( rPostureItem.GetValue() );
+}
+
+void ScAutoFmtPreview::MakeFonts(vcl::RenderContext const& rRenderContext, sal_uInt16 nIndex, vcl::Font& rFont, vcl::Font& rCJKFont, vcl::Font& rCTLFont)
+{
+ if ( !pCurData )
+ return;
+
+ rFont = rCJKFont = rCTLFont = rRenderContext.GetFont();
+ Size aFontSize(rFont.GetFontSize().Width(), 10 * rRenderContext.GetDPIScaleFactor());
+
+ const SvxFontItem* pFontItem = pCurData->GetItem( nIndex, ATTR_FONT );
+ const SvxWeightItem* pWeightItem = pCurData->GetItem( nIndex, ATTR_FONT_WEIGHT );
+ const SvxPostureItem* pPostureItem = pCurData->GetItem( nIndex, ATTR_FONT_POSTURE );
+ const SvxFontItem* pCJKFontItem = pCurData->GetItem( nIndex, ATTR_CJK_FONT );
+ const SvxWeightItem* pCJKWeightItem = pCurData->GetItem( nIndex, ATTR_CJK_FONT_WEIGHT );
+ const SvxPostureItem* pCJKPostureItem = pCurData->GetItem( nIndex, ATTR_CJK_FONT_POSTURE );
+ const SvxFontItem* pCTLFontItem = pCurData->GetItem( nIndex, ATTR_CTL_FONT );
+ const SvxWeightItem* pCTLWeightItem = pCurData->GetItem( nIndex, ATTR_CTL_FONT_WEIGHT );
+ const SvxPostureItem* pCTLPostureItem = pCurData->GetItem( nIndex, ATTR_CTL_FONT_POSTURE );
+ const SvxUnderlineItem* pUnderlineItem = pCurData->GetItem( nIndex, ATTR_FONT_UNDERLINE );
+ const SvxOverlineItem* pOverlineItem = pCurData->GetItem( nIndex, ATTR_FONT_OVERLINE );
+ const SvxCrossedOutItem* pCrossedOutItem = pCurData->GetItem( nIndex, ATTR_FONT_CROSSEDOUT );
+ const SvxContourItem* pContourItem = pCurData->GetItem( nIndex, ATTR_FONT_CONTOUR );
+ const SvxShadowedItem* pShadowedItem = pCurData->GetItem( nIndex, ATTR_FONT_SHADOWED );
+ const SvxColorItem* pColorItem = pCurData->GetItem( nIndex, ATTR_FONT_COLOR );
+
+ lcl_SetFontProperties( rFont, *pFontItem, *pWeightItem, *pPostureItem );
+ lcl_SetFontProperties( rCJKFont, *pCJKFontItem, *pCJKWeightItem, *pCJKPostureItem );
+ lcl_SetFontProperties( rCTLFont, *pCTLFontItem, *pCTLWeightItem, *pCTLPostureItem );
+
+ Color aColor( pColorItem->GetValue() );
+ if( aColor == COL_TRANSPARENT )
+ aColor = Application::GetSettings().GetStyleSettings().GetWindowTextColor();
+
+#define SETONALLFONTS( MethodName, Value ) \
+rFont.MethodName( Value ); rCJKFont.MethodName( Value ); rCTLFont.MethodName( Value );
+
+ SETONALLFONTS( SetUnderline, pUnderlineItem->GetValue() )
+ SETONALLFONTS( SetOverline, pOverlineItem->GetValue() )
+ SETONALLFONTS( SetStrikeout, pCrossedOutItem->GetValue() )
+ SETONALLFONTS( SetOutline, pContourItem->GetValue() )
+ SETONALLFONTS( SetShadow, pShadowedItem->GetValue() )
+ SETONALLFONTS( SetColor, aColor )
+ SETONALLFONTS( SetFontSize, aFontSize )
+ SETONALLFONTS( SetTransparent, true )
+
+#undef SETONALLFONTS
+}
+
+sal_uInt16 ScAutoFmtPreview::GetFormatIndex( size_t nCol, size_t nRow ) const
+{
+ static const sal_uInt16 pnFmtMap[] =
+ {
+ 0, 1, 2, 1, 3,
+ 4, 5, 6, 5, 7,
+ 8, 9, 10, 9, 11,
+ 4, 5, 6, 5, 7,
+ 12, 13, 14, 13, 15
+ };
+ return pnFmtMap[ maArray.GetCellIndex( nCol, nRow, mbRTL ) ];
+}
+
+const SvxBoxItem& ScAutoFmtPreview::GetBoxItem( size_t nCol, size_t nRow ) const
+{
+ OSL_ENSURE( pCurData, "ScAutoFmtPreview::GetBoxItem - no format data found" );
+ return * pCurData->GetItem( GetFormatIndex( nCol, nRow ), ATTR_BORDER );
+}
+
+const SvxLineItem& ScAutoFmtPreview::GetDiagItem( size_t nCol, size_t nRow, bool bTLBR ) const
+{
+ OSL_ENSURE( pCurData, "ScAutoFmtPreview::GetDiagItem - no format data found" );
+ return * pCurData->GetItem( GetFormatIndex( nCol, nRow ), bTLBR ? ATTR_BORDER_TLBR : ATTR_BORDER_BLTR );
+}
+
+void ScAutoFmtPreview::DrawString(vcl::RenderContext& rRenderContext, size_t nCol, size_t nRow)
+{
+ if (!pCurData)
+ {
+ return;
+ }
+
+ // Emit the cell text
+
+ OUString cellString;
+ bool bNumFormat = pCurData->GetIncludeValueFormat();
+ sal_uInt32 nNum;
+ double nVal;
+ const Color* pDummy = nullptr;
+ sal_uInt16 nIndex = static_cast<sal_uInt16>(maArray.GetCellIndex(nCol, nRow, mbRTL));
+
+ switch (nIndex)
+ {
+ case 1: cellString = aStrJan; break;
+ case 2: cellString = aStrFeb; break;
+ case 3: cellString = aStrMar; break;
+ case 5: cellString = aStrNorth; break;
+ case 10: cellString = aStrMid; break;
+ case 15: cellString = aStrSouth; break;
+ case 4:
+ case 20: cellString = aStrSum; break;
+
+ case 6:
+ case 8:
+ case 16:
+ case 18: nVal = nIndex;
+ nNum = 5;
+ goto mknum;
+ case 17:
+ case 7: nVal = nIndex;
+ nNum = 6;
+ goto mknum;
+ case 11:
+ case 12:
+ case 13: nVal = nIndex;
+ nNum = 12 == nIndex ? 10 : 9;
+ goto mknum;
+
+ case 9: nVal = 21; nNum = 7; goto mknum;
+ case 14: nVal = 36; nNum = 11; goto mknum;
+ case 19: nVal = 51; nNum = 7; goto mknum;
+ case 21: nVal = 33; nNum = 13; goto mknum;
+ case 22: nVal = 36; nNum = 14; goto mknum;
+ case 23: nVal = 39; nNum = 13; goto mknum;
+ case 24: nVal = 108; nNum = 15;
+ mknum:
+ if (bNumFormat)
+ {
+ ScNumFormatAbbrev& rNumFormat = const_cast<ScNumFormatAbbrev&>(pCurData->GetNumFormat(sal_uInt16(nNum)));
+ nNum = rNumFormat.GetFormatIndex(*pNumFmt);
+ }
+ else
+ nNum = 0;
+ pNumFmt->GetOutputString(nVal, nNum, cellString, &pDummy);
+ break;
+ }
+
+ if (cellString.isEmpty())
+ return;
+
+ Size aStrSize;
+ sal_uInt16 nFmtIndex = GetFormatIndex( nCol, nRow );
+ const basegfx::B2DRange cellRange(maArray.GetCellRange( nCol, nRow ));
+ Point aPos(basegfx::fround(cellRange.getMinX()), basegfx::fround(cellRange.getMinY()));
+ sal_uInt16 nRightX = 0;
+ bool bJustify = pCurData->GetIncludeJustify();
+ SvxCellHorJustify eJustification;
+
+ SvtScriptedTextHelper aScriptedText(rRenderContext);
+
+ // Justification:
+
+ eJustification = mbRTL ? SvxCellHorJustify::Right : bJustify ?
+ pCurData->GetItem(nFmtIndex, ATTR_HOR_JUSTIFY)->GetValue() :
+ SvxCellHorJustify::Standard;
+
+ if (pCurData->GetIncludeFont())
+ {
+ vcl::Font aFont, aCJKFont, aCTLFont;
+ Size theMaxStrSize;
+
+ MakeFonts(rRenderContext, nFmtIndex, aFont, aCJKFont, aCTLFont);
+
+ theMaxStrSize = Size(basegfx::fround(cellRange.getWidth()), basegfx::fround(cellRange.getHeight()));
+ theMaxStrSize.AdjustWidth( -(FRAME_OFFSET) );
+ theMaxStrSize.AdjustHeight( -(FRAME_OFFSET) );
+
+ aScriptedText.SetFonts( &aFont, &aCJKFont, &aCTLFont );
+ aScriptedText.SetText(cellString, xBreakIter);
+ aStrSize = aScriptedText.GetTextSize();
+
+ if (theMaxStrSize.Height() < aStrSize.Height())
+ {
+ // if the string does not fit in the row using this font,
+ // the default font is used
+ aScriptedText.SetDefaultFont();
+ aStrSize = aScriptedText.GetTextSize();
+ }
+ while((theMaxStrSize.Width() <= aStrSize.Width()) && (cellString.getLength() > 1))
+ {
+ if( eJustification == SvxCellHorJustify::Right )
+ cellString = cellString.copy(1);
+ else
+ cellString = cellString.copy(0, cellString.getLength() - 1 );
+
+ aScriptedText.SetText( cellString, xBreakIter );
+ aStrSize = aScriptedText.GetTextSize();
+ }
+ }
+ else
+ {
+ aScriptedText.SetDefaultFont();
+ aScriptedText.SetText( cellString, xBreakIter );
+ aStrSize = aScriptedText.GetTextSize();
+ }
+
+ nRightX = sal_uInt16(basegfx::fround(cellRange.getWidth()) - aStrSize.Width() - FRAME_OFFSET);
+
+ // vertical (always center):
+
+ aPos.AdjustY((mnRowHeight - static_cast<sal_uInt16>(aStrSize.Height())) / 2 );
+
+ // horizontal
+
+ if (eJustification != SvxCellHorJustify::Standard)
+ {
+ sal_uInt16 nHorPos = sal_uInt16((basegfx::fround(cellRange.getWidth())-aStrSize.Width()) / 2);
+ //sal_uInt16 nHorPos = sal_uInt16((basegfx::fround(cellRange.getWidth())-aStrSize.Width()) / 2);
+
+ switch (eJustification)
+ {
+ case SvxCellHorJustify::Left:
+ aPos.AdjustX(FRAME_OFFSET );
+ break;
+ case SvxCellHorJustify::Right:
+ aPos.AdjustX(nRightX );
+ break;
+ case SvxCellHorJustify::Block:
+ case SvxCellHorJustify::Repeat:
+ case SvxCellHorJustify::Center:
+ aPos.AdjustX(nHorPos );
+ break;
+ // coverity[dead_error_line] - following conditions exist to avoid compiler warning
+ case SvxCellHorJustify::Standard:
+ default:
+ // Standard is not handled here
+ break;
+ }
+ }
+ else
+ {
+
+ // Standard justification
+
+ if (nCol == 0 || nRow == 0)
+ {
+ // Text label to the left or sum left adjusted
+ aPos.AdjustX(FRAME_OFFSET );
+ }
+ else
+ {
+ // Numbers/Dates right adjusted
+ aPos.AdjustX(nRightX );
+ }
+ }
+ aScriptedText.DrawText(aPos);
+}
+
+#undef FRAME_OFFSET
+
+void ScAutoFmtPreview::DrawBackground(vcl::RenderContext& rRenderContext)
+{
+ if (!pCurData)
+ return;
+
+ for(size_t nRow = 0; nRow < 5; ++nRow)
+ {
+ for(size_t nCol = 0; nCol < 5; ++nCol)
+ {
+ const SvxBrushItem* pItem =
+ pCurData->GetItem( GetFormatIndex( nCol, nRow ), ATTR_BACKGROUND );
+
+ rRenderContext.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor( pItem->GetColor() );
+
+ const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow ));
+ rRenderContext.DrawRect(
+ tools::Rectangle(
+ basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()),
+ basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY())));
+
+ rRenderContext.Pop();
+ }
+ }
+}
+
+void ScAutoFmtPreview::PaintCells(vcl::RenderContext& rRenderContext)
+{
+ if (!pCurData)
+ return;
+
+ // 1) background
+ if (pCurData->GetIncludeBackground())
+ DrawBackground(rRenderContext);
+
+ // 2) values
+ for(size_t nRow = 0; nRow < 5; ++nRow)
+ for(size_t nCol = 0; nCol < 5; ++nCol)
+ DrawString(rRenderContext, nCol, nRow);
+
+ // 3) border
+ if (!pCurData->GetIncludeFrame())
+ return;
+
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
+ drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ rRenderContext,
+ aNewViewInformation2D));
+
+ pProcessor2D->process(maArray.CreateB2DPrimitiveArray());
+ pProcessor2D.reset();
+}
+
+void ScAutoFmtPreview::Init()
+{
+ maArray.Initialize( 5, 5 );
+ mnLabelColWidth = 0;
+ mnDataColWidth1 = 0;
+ mnDataColWidth2 = 0;
+ mnRowHeight = 0;
+ CalcCellArray( false );
+ CalcLineMap();
+}
+
+void ScAutoFmtPreview::DetectRTL(const ScViewData& rViewData)
+{
+ SCTAB nCurrentTab = rViewData.GetTabNo();
+ ScDocument& rDoc = rViewData.GetDocument();
+ mbRTL = rDoc.IsLayoutRTL(nCurrentTab);
+ xBreakIter = rDoc.GetBreakIterator();
+}
+
+void ScAutoFmtPreview::CalcCellArray( bool bFitWidthP )
+{
+ maArray.SetXOffset( 2 );
+ maArray.SetAllColWidths( bFitWidthP ? mnDataColWidth2 : mnDataColWidth1 );
+ maArray.SetColWidth( 0, mnLabelColWidth );
+ maArray.SetColWidth( 4, mnLabelColWidth );
+
+ maArray.SetYOffset( 2 );
+ maArray.SetAllRowHeights( mnRowHeight );
+
+ aPrvSize.setWidth( maArray.GetWidth() + 4 );
+ aPrvSize.setHeight( maArray.GetHeight() + 4 );
+}
+
+static void lclSetStyleFromBorder( svx::frame::Style& rStyle, const ::editeng::SvxBorderLine* pBorder )
+{
+ rStyle.Set(pBorder, o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::pt), 5);
+}
+
+void ScAutoFmtPreview::CalcLineMap()
+{
+ if ( !pCurData )
+ return;
+
+ for( size_t nRow = 0; nRow < 5; ++nRow )
+ {
+ for( size_t nCol = 0; nCol < 5; ++nCol )
+ {
+ svx::frame::Style aStyle;
+
+ const SvxBoxItem& rItem = GetBoxItem( nCol, nRow );
+ lclSetStyleFromBorder( aStyle, rItem.GetLeft() );
+ maArray.SetCellStyleLeft( nCol, nRow, aStyle );
+ lclSetStyleFromBorder( aStyle, rItem.GetRight() );
+ maArray.SetCellStyleRight( nCol, nRow, aStyle );
+ lclSetStyleFromBorder( aStyle, rItem.GetTop() );
+ maArray.SetCellStyleTop( nCol, nRow, aStyle );
+ lclSetStyleFromBorder( aStyle, rItem.GetBottom() );
+ maArray.SetCellStyleBottom( nCol, nRow, aStyle );
+
+ lclSetStyleFromBorder( aStyle, GetDiagItem( nCol, nRow, true ).GetLine() );
+ maArray.SetCellStyleTLBR( nCol, nRow, aStyle );
+ lclSetStyleFromBorder( aStyle, GetDiagItem( nCol, nRow, false ).GetLine() );
+ maArray.SetCellStyleBLTR( nCol, nRow, aStyle );
+ }
+ }
+}
+
+void ScAutoFmtPreview::NotifyChange( ScAutoFormatData* pNewData )
+{
+ if (pNewData)
+ {
+ pCurData = pNewData;
+ bFitWidth = pNewData->GetIncludeWidthHeight();
+ }
+
+ CalcCellArray( bFitWidth );
+ CalcLineMap();
+
+ Invalidate();
+}
+
+void ScAutoFmtPreview::DoPaint(vcl::RenderContext& rRenderContext)
+{
+ rRenderContext.Push(vcl::PushFlags::ALL);
+ DrawModeFlags nOldDrawMode = aVD->GetDrawMode();
+
+ Size aWndSize(GetOutputSizePixel());
+ vcl::Font aFont(aVD->GetFont());
+ const Color& aBackCol = SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor;
+ tools::Rectangle aRect(Point(), aWndSize);
+
+ aFont.SetTransparent( true );
+ aVD->SetFont(aFont);
+ aVD->SetLineColor();
+ aVD->SetFillColor(aBackCol);
+ aVD->SetOutputSize(aWndSize);
+ aVD->DrawRect(aRect);
+
+ PaintCells(*aVD);
+
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(aBackCol);
+ rRenderContext.DrawRect(aRect);
+
+ Point aPos((aWndSize.Width() - aPrvSize.Width()) / 2, (aWndSize.Height() - aPrvSize.Height()) / 2);
+ if (AllSettings::GetLayoutRTL())
+ aPos.setX( -aPos.X() );
+ rRenderContext.DrawOutDev(aPos, aWndSize, Point(), aWndSize, *aVD);
+ aVD->SetDrawMode(nOldDrawMode);
+ rRenderContext.Pop();
+}
+
+void ScAutoFmtPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ DoPaint(rRenderContext);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/conflictsdlg.cxx b/sc/source/ui/miscdlgs/conflictsdlg.cxx
new file mode 100644
index 0000000000..3a48325aa5
--- /dev/null
+++ b/sc/source/ui/miscdlgs/conflictsdlg.cxx
@@ -0,0 +1,643 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <osl/diagnose.h>
+
+#include <conflictsdlg.hxx>
+#include <o3tl/safeint.hxx>
+#include <strings.hrc>
+#include <scresid.hxx>
+#include <viewdata.hxx>
+#include <dbfunc.hxx>
+#include <chgtrack.hxx>
+
+// struct ScConflictsListEntry
+
+bool ScConflictsListEntry::HasSharedAction( sal_uLong nSharedAction ) const
+{
+ auto aEnd = maSharedActions.cend();
+ auto aItr = std::find(maSharedActions.cbegin(), aEnd, nSharedAction);
+
+ return aItr != aEnd;
+}
+
+bool ScConflictsListEntry::HasOwnAction( sal_uLong nOwnAction ) const
+{
+ auto aEnd = maOwnActions.cend();
+ auto aItr = std::find(maOwnActions.cbegin(), aEnd, nOwnAction);
+
+ return aItr != aEnd;
+}
+
+
+bool ScConflictsListHelper::HasOwnAction( ScConflictsList& rConflictsList, sal_uLong nOwnAction )
+{
+ return std::any_of(rConflictsList.begin(), rConflictsList.end(),
+ [nOwnAction](ScConflictsListEntry& rConflict) { return rConflict.HasOwnAction( nOwnAction ); });
+}
+
+ScConflictsListEntry* ScConflictsListHelper::GetSharedActionEntry( ScConflictsList& rConflictsList, sal_uLong nSharedAction )
+{
+ auto aEnd = rConflictsList.end();
+ auto aItr = std::find_if(rConflictsList.begin(), aEnd,
+ [nSharedAction](ScConflictsListEntry& rConflict) { return rConflict.HasSharedAction( nSharedAction ); });
+
+ if (aItr != aEnd)
+ return &(*aItr);
+
+ return nullptr;
+}
+
+ScConflictsListEntry* ScConflictsListHelper::GetOwnActionEntry( ScConflictsList& rConflictsList, sal_uLong nOwnAction )
+{
+ auto aEnd = rConflictsList.end();
+ auto aItr = std::find_if(rConflictsList.begin(), aEnd,
+ [nOwnAction](ScConflictsListEntry& rConflict) { return rConflict.HasOwnAction( nOwnAction ); });
+
+ if (aItr != aEnd)
+ return &(*aItr);
+
+ return nullptr;
+}
+
+void ScConflictsListHelper::Transform_Impl( std::vector<sal_uLong>& rActionList, ScChangeActionMergeMap* pMergeMap )
+{
+ if ( !pMergeMap )
+ {
+ return;
+ }
+
+ for ( auto aItr = rActionList.begin(); aItr != rActionList.end(); )
+ {
+ ScChangeActionMergeMap::iterator aItrMap = pMergeMap->find( *aItr );
+ if ( aItrMap != pMergeMap->end() )
+ {
+ *aItr = aItrMap->second;
+ ++aItr;
+ }
+ else
+ {
+ aItr = rActionList.erase( aItr );
+ OSL_FAIL( "ScConflictsListHelper::Transform_Impl: erased action from conflicts list!" );
+ }
+ }
+}
+
+void ScConflictsListHelper::TransformConflictsList( ScConflictsList& rConflictsList,
+ ScChangeActionMergeMap* pSharedMap, ScChangeActionMergeMap* pOwnMap )
+{
+ for ( auto& rConflictEntry : rConflictsList )
+ {
+ if ( pSharedMap )
+ {
+ ScConflictsListHelper::Transform_Impl( rConflictEntry.maSharedActions, pSharedMap );
+ }
+
+ if ( pOwnMap )
+ {
+ ScConflictsListHelper::Transform_Impl( rConflictEntry.maOwnActions, pOwnMap );
+ }
+ }
+}
+
+
+ScConflictsFinder::ScConflictsFinder( ScChangeTrack* pTrack, sal_uLong nStartShared, sal_uLong nEndShared,
+ sal_uLong nStartOwn, sal_uLong nEndOwn, ScConflictsList& rConflictsList )
+ :mpTrack( pTrack )
+ ,mnStartShared( nStartShared )
+ ,mnEndShared( nEndShared )
+ ,mnStartOwn( nStartOwn )
+ ,mnEndOwn( nEndOwn )
+ ,mrConflictsList( rConflictsList )
+{
+}
+
+bool ScConflictsFinder::DoActionsIntersect( const ScChangeAction* pAction1, const ScChangeAction* pAction2 )
+{
+ return pAction1 && pAction2 && pAction1->GetBigRange().Intersects( pAction2->GetBigRange() );
+}
+
+ScConflictsListEntry* ScConflictsFinder::GetIntersectingEntry( const ScChangeAction* pAction ) const
+{
+ auto doActionsIntersect = [this, pAction](const sal_uLong& aAction) { return DoActionsIntersect( mpTrack->GetAction( aAction ), pAction ); };
+
+ for ( auto& rConflict : mrConflictsList )
+ {
+ if (std::any_of( rConflict.maSharedActions.cbegin(), rConflict.maSharedActions.cend(), doActionsIntersect ))
+ return &rConflict;
+
+ if (std::any_of( rConflict.maOwnActions.cbegin(), rConflict.maOwnActions.cend(), doActionsIntersect ))
+ return &rConflict;
+ }
+
+ return nullptr;
+}
+
+ScConflictsListEntry& ScConflictsFinder::GetEntry( sal_uLong nSharedAction, const std::vector<sal_uLong>& rOwnActions )
+{
+ // try to get a list entry which already contains the shared action
+ ScConflictsListEntry* pEntry = ScConflictsListHelper::GetSharedActionEntry( mrConflictsList, nSharedAction );
+ if ( pEntry )
+ {
+ return *pEntry;
+ }
+
+ // try to get a list entry for which the shared action intersects with any
+ // other action of this entry
+ pEntry = GetIntersectingEntry( mpTrack->GetAction( nSharedAction ) );
+ if ( pEntry )
+ {
+ pEntry->maSharedActions.push_back( nSharedAction );
+ return *pEntry;
+ }
+
+ // try to get a list entry for which any of the own actions intersects with
+ // any other action of this entry
+ for ( auto& rOwnAction : rOwnActions )
+ {
+ pEntry = GetIntersectingEntry( mpTrack->GetAction( rOwnAction ) );
+ if ( pEntry )
+ {
+ pEntry->maSharedActions.push_back( nSharedAction );
+ return *pEntry;
+ }
+ }
+
+ // if no entry was found, create a new one
+ ScConflictsListEntry aEntry;
+ aEntry.meConflictAction = SC_CONFLICT_ACTION_NONE;
+ aEntry.maSharedActions.push_back( nSharedAction );
+ mrConflictsList.push_back( aEntry );
+ return mrConflictsList.back();
+}
+
+bool ScConflictsFinder::Find()
+{
+ if ( !mpTrack )
+ {
+ return false;
+ }
+
+ bool bReturn = false;
+ ScChangeAction* pSharedAction = mpTrack->GetAction( mnStartShared );
+ while ( pSharedAction && pSharedAction->GetActionNumber() <= mnEndShared )
+ {
+ std::vector<sal_uLong> aOwnActions;
+ ScChangeAction* pOwnAction = mpTrack->GetAction( mnStartOwn );
+ while ( pOwnAction && pOwnAction->GetActionNumber() <= mnEndOwn )
+ {
+ if ( DoActionsIntersect( pSharedAction, pOwnAction ) )
+ {
+ aOwnActions.push_back( pOwnAction->GetActionNumber() );
+ }
+ pOwnAction = pOwnAction->GetNext();
+ }
+
+ if ( !aOwnActions.empty() )
+ {
+ ScConflictsListEntry& rEntry = GetEntry(pSharedAction->GetActionNumber(), aOwnActions);
+ for ( const auto& aOwnAction : aOwnActions )
+ {
+ if (!ScConflictsListHelper::HasOwnAction(mrConflictsList, aOwnAction))
+ {
+ rEntry.maOwnActions.push_back(aOwnAction);
+ }
+ }
+ bReturn = true;
+ }
+
+ pSharedAction = pSharedAction->GetNext();
+ }
+
+ return bReturn;
+}
+
+
+ScConflictsResolver::ScConflictsResolver( ScChangeTrack* pTrack, ScConflictsList& rConflictsList )
+ :mpTrack ( pTrack )
+ ,mrConflictsList ( rConflictsList )
+{
+ OSL_ENSURE( mpTrack, "ScConflictsResolver CTOR: mpTrack is null!" );
+}
+
+void ScConflictsResolver::HandleAction( ScChangeAction* pAction, bool bIsSharedAction,
+ bool bHandleContentAction, bool bHandleNonContentAction )
+{
+ if ( !mpTrack || !pAction )
+ {
+ return;
+ }
+
+ if ( bIsSharedAction )
+ {
+ ScConflictsListEntry* pConflictEntry = ScConflictsListHelper::GetSharedActionEntry(
+ mrConflictsList, pAction->GetActionNumber() );
+ if ( pConflictEntry )
+ {
+ ScConflictAction eConflictAction = pConflictEntry->meConflictAction;
+ if ( eConflictAction == SC_CONFLICT_ACTION_KEEP_MINE )
+ {
+ if ( pAction->GetType() == SC_CAT_CONTENT )
+ {
+ if ( bHandleContentAction )
+ {
+ mpTrack->Reject( pAction );
+ }
+ }
+ else
+ {
+ if ( bHandleNonContentAction )
+ {
+ mpTrack->Reject( pAction );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ ScConflictsListEntry* pConflictEntry = ScConflictsListHelper::GetOwnActionEntry(
+ mrConflictsList, pAction->GetActionNumber() );
+ if ( pConflictEntry )
+ {
+ ScConflictAction eConflictAction = pConflictEntry->meConflictAction;
+ if ( eConflictAction == SC_CONFLICT_ACTION_KEEP_MINE )
+ {
+ if ( pAction->GetType() == SC_CAT_CONTENT )
+ {
+ if ( bHandleContentAction )
+ {
+ // do nothing
+ //mpTrack->SelectContent( pAction );
+ }
+ }
+ else
+ {
+ if ( bHandleNonContentAction )
+ {
+ // do nothing
+ //mpTrack->Accept( pAction );
+ }
+ }
+ }
+ else if ( eConflictAction == SC_CONFLICT_ACTION_KEEP_OTHER )
+ {
+ if ( pAction->GetType() == SC_CAT_CONTENT )
+ {
+ if ( bHandleContentAction )
+ {
+ mpTrack->Reject( pAction );
+ }
+ }
+ else
+ {
+ if ( bHandleNonContentAction )
+ {
+ mpTrack->Reject( pAction );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+ScConflictsDlg::ScConflictsDlg(weld::Window* pParent, ScViewData* pViewData, ScDocument* pSharedDoc, ScConflictsList& rConflictsList)
+ : GenericDialogController(pParent, "modules/scalc/ui/conflictsdialog.ui", "ConflictsDialog")
+ , maStrUnknownUser ( ScResId( STR_UNKNOWN_USER_CONFLICT ) )
+ , mpViewData ( pViewData )
+ , mpOwnDoc ( nullptr )
+ , mpOwnTrack ( nullptr )
+ , mpSharedDoc ( pSharedDoc )
+ , mpSharedTrack ( nullptr )
+ , mrConflictsList ( rConflictsList )
+ , maSelectionIdle ( "ScConflictsDlg maSelectionIdle" )
+ , mbInSelectHdl ( false )
+ , m_xBtnKeepMine(m_xBuilder->weld_button("keepmine"))
+ , m_xBtnKeepOther(m_xBuilder->weld_button("keepother"))
+ , m_xBtnKeepAllMine(m_xBuilder->weld_button("keepallmine"))
+ , m_xBtnKeepAllOthers(m_xBuilder->weld_button("keepallothers"))
+ , m_xLbConflicts(new SvxRedlinTable(m_xBuilder->weld_tree_view("container"), nullptr))
+{
+ OSL_ENSURE( mpViewData, "ScConflictsDlg CTOR: mpViewData is null!" );
+ mpOwnDoc = ( mpViewData ? &mpViewData->GetDocument() : nullptr );
+ OSL_ENSURE( mpOwnDoc, "ScConflictsDlg CTOR: mpOwnDoc is null!" );
+ mpOwnTrack = ( mpOwnDoc ? mpOwnDoc->GetChangeTrack() : nullptr );
+ OSL_ENSURE( mpOwnTrack, "ScConflictsDlg CTOR: mpOwnTrack is null!" );
+ OSL_ENSURE( mpSharedDoc, "ScConflictsDlg CTOR: mpSharedDoc is null!" );
+ mpSharedTrack = ( mpSharedDoc ? mpSharedDoc->GetChangeTrack() : nullptr );
+ OSL_ENSURE( mpSharedTrack, "ScConflictsDlg CTOR: mpSharedTrack is null!" );
+
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+
+ auto nDigitWidth = rTreeView.get_approximate_digit_width();
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(nDigitWidth * 60),
+ o3tl::narrowing<int>(nDigitWidth * 20)
+ };
+ rTreeView.set_column_fixed_widths(aWidths);
+
+ rTreeView.set_selection_mode(SelectionMode::Multiple);
+ rTreeView.set_size_request(-1, rTreeView.get_height_rows(16));
+
+ maSelectionIdle.SetInvokeHandler( LINK( this, ScConflictsDlg, UpdateSelectionHdl ) );
+
+ rTreeView.connect_changed(LINK(this, ScConflictsDlg, SelectHandle));
+
+ m_xBtnKeepMine->connect_clicked( LINK( this, ScConflictsDlg, KeepMineHandle ) );
+ m_xBtnKeepOther->connect_clicked( LINK( this, ScConflictsDlg, KeepOtherHandle ) );
+ m_xBtnKeepAllMine->connect_clicked( LINK( this, ScConflictsDlg, KeepAllMineHandle ) );
+ m_xBtnKeepAllOthers->connect_clicked( LINK( this, ScConflictsDlg, KeepAllOthersHandle ) );
+
+ UpdateView();
+
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ if (rTreeView.get_iter_first(*xEntry))
+ rTreeView.select(*xEntry);
+}
+
+ScConflictsDlg::~ScConflictsDlg()
+{
+}
+
+OUString ScConflictsDlg::GetConflictString( const ScConflictsListEntry& rConflictEntry )
+{
+ OUString aString;
+ if ( mpOwnTrack )
+ {
+ const ScChangeAction* pAction = mpOwnTrack->GetAction( rConflictEntry.maOwnActions[ 0 ] );
+ if ( pAction && mpOwnDoc )
+ {
+ SCTAB nTab = pAction->GetBigRange().MakeRange( *mpOwnDoc ).aStart.Tab();
+ mpOwnDoc->GetName( nTab, aString );
+ }
+ }
+ return aString;
+}
+
+void ScConflictsDlg::SetActionString(const ScChangeAction* pAction, ScDocument* pDoc, const weld::TreeIter& rEntry)
+{
+ OSL_ENSURE( pAction, "ScConflictsDlg::GetActionString(): pAction is null!" );
+ OSL_ENSURE( pDoc, "ScConflictsDlg::GetActionString(): pDoc is null!" );
+ if (!pAction || !pDoc)
+ return;
+
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ OUString aDesc = pAction->GetDescription(*pDoc, true, false);
+ rTreeView.set_text(rEntry, aDesc, 0);
+
+ OUString aUser = comphelper::string::strip(pAction->GetUser(), ' ');
+ if ( aUser.isEmpty() )
+ {
+ aUser = maStrUnknownUser;
+ }
+ rTreeView.set_text(rEntry, aUser, 1);
+
+ DateTime aDateTime = pAction->GetDateTime();
+ OUString aString = ScGlobal::getLocaleData().getDate( aDateTime ) + " " +
+ ScGlobal::getLocaleData().getTime( aDateTime, false );
+ rTreeView.set_text(rEntry, aString, 2);
+}
+
+void ScConflictsDlg::HandleListBoxSelection()
+{
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ bool bSelEntry = rTreeView.get_cursor(xEntry.get());
+ if (!bSelEntry)
+ bSelEntry = rTreeView.get_selected(xEntry.get());
+ if (!bSelEntry)
+ return;
+
+ bool bSelectHandle = rTreeView.is_selected(*xEntry);
+
+ while (rTreeView.get_iter_depth(*xEntry))
+ rTreeView.iter_parent(*xEntry);
+
+ if (bSelectHandle)
+ rTreeView.unselect_all();
+ if (!rTreeView.is_selected(*xEntry))
+ rTreeView.select(*xEntry);
+ if (rTreeView.iter_children(*xEntry))
+ {
+ do
+ {
+ if (!rTreeView.is_selected(*xEntry))
+ rTreeView.select(*xEntry);
+ } while (rTreeView.iter_next_sibling(*xEntry));
+ }
+}
+
+IMPL_LINK_NOARG(ScConflictsDlg, SelectHandle, weld::TreeView&, void)
+{
+ if (mbInSelectHdl)
+ return;
+
+ mbInSelectHdl = true;
+ HandleListBoxSelection();
+ maSelectionIdle.Start();
+ mbInSelectHdl = false;
+}
+
+IMPL_LINK_NOARG(ScConflictsDlg, UpdateSelectionHdl, Timer *, void)
+{
+ if ( !mpViewData || !mpOwnDoc )
+ {
+ return;
+ }
+
+ ScTabView* pTabView = mpViewData->GetView();
+ pTabView->DoneBlockMode();
+
+ std::vector<const ScChangeAction*> aActions;
+
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ rTreeView.selected_foreach([&rTreeView, &aActions](weld::TreeIter& rEntry){
+ if (rTreeView.get_iter_depth(rEntry))
+ {
+ RedlinData* pUserData = weld::fromId<RedlinData*>(rTreeView.get_id(rEntry));
+ if (pUserData)
+ {
+ ScChangeAction* pAction = static_cast< ScChangeAction* >( pUserData->pData );
+ if ( pAction && ( pAction->GetType() != SC_CAT_DELETE_TABS ) &&
+ ( pAction->IsClickable() || pAction->IsVisible() ) )
+ {
+ aActions.push_back(pAction);
+ }
+ }
+ }
+ return false;
+ });
+
+ bool bContMark = false;
+ for (size_t i = 0, nCount = aActions.size(); i < nCount; ++i)
+ {
+ const ScBigRange& rBigRange = aActions[i]->GetBigRange();
+ if (rBigRange.IsValid(*mpOwnDoc))
+ {
+ bool bSetCursor = i == nCount - 1;
+ pTabView->MarkRange(rBigRange.MakeRange( *mpOwnDoc ), bSetCursor, bContMark);
+ bContMark = true;
+ }
+ }
+}
+
+void ScConflictsDlg::SetConflictAction(const weld::TreeIter& rRootEntry, ScConflictAction eConflictAction)
+{
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ RedlinData* pUserData = weld::fromId<RedlinData*>(rTreeView.get_id(rRootEntry));
+ ScConflictsListEntry* pConflictEntry = static_cast< ScConflictsListEntry* >( pUserData ? pUserData->pData : nullptr );
+ if ( pConflictEntry )
+ {
+ pConflictEntry->meConflictAction = eConflictAction;
+ }
+}
+
+void ScConflictsDlg::KeepHandler(bool bMine)
+{
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ if (!rTreeView.get_selected(xEntry.get()))
+ return;
+
+ while (rTreeView.get_iter_depth(*xEntry))
+ rTreeView.iter_parent(*xEntry);
+
+ m_xDialog->set_busy_cursor(true);
+ ScConflictAction eConflictAction = ( bMine ? SC_CONFLICT_ACTION_KEEP_MINE : SC_CONFLICT_ACTION_KEEP_OTHER );
+ SetConflictAction(*xEntry, eConflictAction);
+ rTreeView.remove(*xEntry);
+ m_xDialog->set_busy_cursor(false);
+ if (rTreeView.n_children() == 0)
+ m_xDialog->response(RET_OK);
+}
+
+void ScConflictsDlg::KeepAllHandler( bool bMine )
+{
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ if (!rTreeView.get_iter_first(*xEntry))
+ return;
+
+ while (rTreeView.get_iter_depth(*xEntry))
+ rTreeView.iter_parent(*xEntry);
+
+ m_xDialog->set_busy_cursor(true);
+
+ ScConflictAction eConflictAction = ( bMine ? SC_CONFLICT_ACTION_KEEP_MINE : SC_CONFLICT_ACTION_KEEP_OTHER );
+ do
+ {
+ SetConflictAction(*xEntry, eConflictAction);
+ } while (rTreeView.iter_next_sibling(*xEntry));
+
+ rTreeView.freeze();
+ rTreeView.clear();
+ rTreeView.thaw();
+
+ m_xDialog->set_busy_cursor(false);
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ScConflictsDlg, KeepMineHandle, weld::Button&, void)
+{
+ KeepHandler( true );
+}
+
+IMPL_LINK_NOARG(ScConflictsDlg, KeepOtherHandle, weld::Button&, void)
+{
+ KeepHandler( false );
+}
+
+IMPL_LINK_NOARG(ScConflictsDlg, KeepAllMineHandle, weld::Button&, void)
+{
+ KeepAllHandler( true );
+}
+
+IMPL_LINK_NOARG(ScConflictsDlg, KeepAllOthersHandle, weld::Button&, void)
+{
+ KeepAllHandler( false );
+}
+
+void ScConflictsDlg::UpdateView()
+{
+ weld::TreeView& rTreeView = m_xLbConflicts->GetWidget();
+ for ( ScConflictsListEntry& rConflictEntry : mrConflictsList )
+ {
+ if (rConflictEntry.meConflictAction == SC_CONFLICT_ACTION_NONE)
+ {
+ std::unique_ptr<RedlinData> pRootUserData(new RedlinData());
+ pRootUserData->pData = static_cast<void*>(&rConflictEntry);
+ OUString sString(GetConflictString(rConflictEntry));
+ OUString sId(weld::toId(pRootUserData.release()));
+ std::unique_ptr<weld::TreeIter> xRootEntry(rTreeView.make_iterator());
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ rTreeView.insert(nullptr, -1, &sString, &sId, nullptr, nullptr, false, xRootEntry.get());
+
+ for ( const auto& aSharedAction : rConflictEntry.maSharedActions )
+ {
+ ScChangeAction* pAction = mpSharedTrack ? mpSharedTrack->GetAction(aSharedAction) : nullptr;
+ if ( pAction )
+ {
+ // only display shared top content entries
+ if ( pAction->GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContent* pNextContent = dynamic_cast<ScChangeActionContent&>(*pAction).GetNextContent();
+ if ( pNextContent && rConflictEntry.HasSharedAction( pNextContent->GetActionNumber() ) )
+ {
+ continue;
+ }
+ }
+
+ rTreeView.insert(xRootEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xEntry.get());
+ SetActionString(pAction, mpSharedDoc, *xEntry);
+ }
+ }
+
+ for ( const auto& aOwnAction : rConflictEntry.maOwnActions )
+ {
+ ScChangeAction* pAction = mpOwnTrack ? mpOwnTrack->GetAction(aOwnAction) : nullptr;
+ if ( pAction )
+ {
+ // only display own top content entries
+ if ( pAction->GetType() == SC_CAT_CONTENT )
+ {
+ ScChangeActionContent* pNextContent = dynamic_cast<ScChangeActionContent&>(*pAction).GetNextContent();
+ if ( pNextContent && rConflictEntry.HasOwnAction( pNextContent->GetActionNumber() ) )
+ {
+ continue;
+ }
+ }
+
+ std::unique_ptr<RedlinData> pUserData(new RedlinData());
+ pUserData->pData = static_cast< void* >( pAction );
+ OUString aId(weld::toId(pUserData.release()));
+ rTreeView.insert(xRootEntry.get(), -1, nullptr, &aId, nullptr, nullptr, false, xEntry.get());
+ SetActionString(pAction, mpOwnDoc, *xEntry);
+ }
+ }
+
+ rTreeView.expand_row(*xRootEntry);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/crdlg.cxx b/sc/source/ui/miscdlgs/crdlg.cxx
new file mode 100644
index 0000000000..51f4fe9bb4
--- /dev/null
+++ b/sc/source/ui/miscdlgs/crdlg.cxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <crdlg.hxx>
+#include <scui_def.hxx>
+
+ScColOrRowDlg::ScColOrRowDlg(weld::Window* pParent, const OUString& rStrTitle,
+ const OUString& rStrLabel)
+ : GenericDialogController(pParent, "modules/scalc/ui/colorrowdialog.ui", "ColOrRowDialog")
+ , m_xFrame(m_xBuilder->weld_frame("frame"))
+ , m_xBtnCols(m_xBuilder->weld_radio_button("columns"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+{
+ m_xDialog->set_title(rStrTitle);
+ m_xFrame->set_label(rStrLabel);
+ m_xBtnOk->connect_clicked(LINK(this, ScColOrRowDlg, OkHdl));
+}
+
+ScColOrRowDlg::~ScColOrRowDlg() {}
+
+IMPL_LINK_NOARG(ScColOrRowDlg, OkHdl, weld::Button&, void)
+{
+ m_xDialog->response(m_xBtnCols->get_active() ? SCRET_COLS : SCRET_ROWS);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/crnrdlg.cxx b/sc/source/ui/miscdlgs/crnrdlg.cxx
new file mode 100644
index 0000000000..38a0cfe668
--- /dev/null
+++ b/sc/source/ui/miscdlgs/crnrdlg.cxx
@@ -0,0 +1,800 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <reffact.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <crnrdlg.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <o3tl/string_view.hxx>
+#include <memory>
+
+namespace
+{
+ void ERRORBOX(weld::Window* pParent, const OUString& rString)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ rString));
+ xBox->run();
+ }
+
+ int QUERYBOX(weld::Window* pParent, const OUString& rString)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ rString));
+ xBox->set_default_response(RET_YES);
+ return xBox->run();
+ }
+
+}
+
+const sal_uLong nEntryDataCol = 0;
+const sal_uLong nEntryDataRow = 1;
+const sal_uLong nEntryDataDelim = 2;
+
+
+// note: some of the initialisation is done in Init
+ScColRowNameRangesDlg::ScColRowNameRangesDlg( SfxBindings* pB,
+ SfxChildWindow* pCW,
+ weld::Window* pParent,
+ ScViewData& rViewData )
+
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/namerangesdialog.ui", "NameRangesDialog")
+ , m_rViewData(rViewData)
+ , rDoc(rViewData.GetDocument())
+ , bDlgLostFocus(false)
+ , m_pEdActive(nullptr)
+ , m_xLbRange(m_xBuilder->weld_tree_view("range"))
+ , m_xEdAssign(new formula::RefEdit(m_xBuilder->weld_entry("edassign")))
+ , m_xRbAssign(new formula::RefButton(m_xBuilder->weld_button("rbassign")))
+ , m_xBtnColHead(m_xBuilder->weld_radio_button("colhead"))
+ , m_xBtnRowHead(m_xBuilder->weld_radio_button("rowhead"))
+ , m_xEdAssign2(new formula::RefEdit(m_xBuilder->weld_entry("edassign2")))
+ , m_xRbAssign2(new formula::RefButton(m_xBuilder->weld_button("rbassign2")))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("delete"))
+ , m_xRangeFrame(m_xBuilder->weld_frame("rangeframe"))
+ , m_xRangeFT(m_xRangeFrame->weld_label_widget())
+ , m_xDataFT(m_xBuilder->weld_label("datarange"))
+{
+ m_xRbAssign->SetReferences(this, m_xEdAssign.get());
+ m_xEdAssign->SetReferences(this, m_xRangeFT.get());
+ m_xRbAssign2->SetReferences(this, m_xEdAssign2.get());
+ m_xEdAssign2->SetReferences(this, m_xDataFT.get());
+
+ xColNameRanges = rDoc.GetColNameRanges()->Clone();
+ xRowNameRanges = rDoc.GetRowNameRanges()->Clone();
+ Init();
+}
+
+ScColRowNameRangesDlg::~ScColRowNameRangesDlg()
+{
+}
+
+// initialises event handlers and start parameters in the dialog
+void ScColRowNameRangesDlg::Init()
+{
+ m_xBtnOk->connect_clicked ( LINK( this, ScColRowNameRangesDlg, OkBtnHdl ) );
+ m_xBtnCancel->connect_clicked ( LINK( this, ScColRowNameRangesDlg, CancelBtnHdl ) );
+ m_xBtnAdd->connect_clicked ( LINK( this, ScColRowNameRangesDlg, AddBtnHdl ) );
+ m_xBtnRemove->connect_clicked ( LINK( this, ScColRowNameRangesDlg, RemoveBtnHdl ) );
+ m_xLbRange->connect_changed( LINK( this, ScColRowNameRangesDlg, Range1SelectHdl ) );
+ m_xEdAssign->SetModifyHdl ( LINK( this, ScColRowNameRangesDlg, Range1DataModifyHdl ) );
+ m_xBtnColHead->connect_toggled ( LINK( this, ScColRowNameRangesDlg, ColRowToggleHdl ) );
+ m_xEdAssign2->SetModifyHdl ( LINK( this, ScColRowNameRangesDlg, Range2DataModifyHdl ) );
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScColRowNameRangesDlg, GetEditFocusHdl );
+ m_xEdAssign->SetGetFocusHdl( aEditLink );
+ m_xEdAssign2->SetGetFocusHdl( aEditLink );
+
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScColRowNameRangesDlg, GetButtonFocusHdl );
+ m_xRbAssign->SetGetFocusHdl( aButtonLink );
+ m_xRbAssign2->SetGetFocusHdl( aButtonLink );
+
+ aEditLink = LINK( this, ScColRowNameRangesDlg, LoseEditFocusHdl );
+ m_xEdAssign->SetLoseFocusHdl( aEditLink );
+ m_xEdAssign2->SetLoseFocusHdl( aEditLink );
+
+ aButtonLink = LINK( this, ScColRowNameRangesDlg, LoseButtonFocusHdl );
+ m_xRbAssign2->SetLoseFocusHdl( aButtonLink );
+ m_xRbAssign->SetLoseFocusHdl( aButtonLink );
+
+ m_pEdActive = m_xEdAssign.get();
+
+ UpdateNames();
+
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCTAB nStartTab = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ SCTAB nEndTab = 0;
+ m_rViewData.GetSimpleArea(nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+ SetColRowData( ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab));
+
+ m_xBtnColHead->set_sensitive(true);
+ m_xBtnRowHead->set_sensitive(true);
+ m_xEdAssign->GetWidget()->set_sensitive(true);
+ m_xEdAssign->GrabFocus();
+ m_xRbAssign->GetWidget()->set_sensitive(true);
+
+ Range1SelectHdl( *m_xLbRange );
+}
+
+// set data range of a labeled range to default values and set the
+// form elements for the reference
+void ScColRowNameRangesDlg::SetColRowData( const ScRange& rLabelRange, bool bRef)
+{
+ theCurData = theCurArea = rLabelRange;
+ bool bValid = true;
+ SCCOL nCol1 = theCurArea.aStart.Col();
+ SCCOL nCol2 = theCurArea.aEnd.Col();
+ SCROW nRow1 = theCurArea.aStart.Row();
+ SCROW nRow2 = theCurArea.aEnd.Row();
+ if ( (static_cast<SCCOLROW>(nCol2 - nCol1) >= nRow2 - nRow1) || (nCol1 == 0 && nCol2 == rDoc.MaxCol()) )
+ { // Column headers and the limiting case of the whole sheet
+ m_xBtnColHead->set_active(true);
+ m_xBtnRowHead->set_active(false);
+ if ( nRow2 == rDoc.MaxRow() )
+ {
+ if ( nRow1 == 0 )
+ bValid = false; // limiting case of the whole sheet
+ else
+ { // Header at bottom, data above
+ theCurData.aStart.SetRow( 0 );
+ theCurData.aEnd.SetRow( nRow1 - 1 );
+ }
+ }
+ else
+ { // Header at top, data below
+ theCurData.aStart.SetRow( nRow2 + 1 );
+ theCurData.aEnd.SetRow( rDoc.MaxRow() );
+ }
+ }
+ else
+ { // Column headers
+ m_xBtnRowHead->set_active(true);
+ m_xBtnColHead->set_active(false);
+ if ( nCol2 == rDoc.MaxCol() )
+ { // Header at the right, data to the left
+ theCurData.aStart.SetCol( 0 );
+ theCurData.aEnd.SetCol( nCol2 - 1 );
+ }
+ else
+ { // Header at the left, data to the right
+ theCurData.aStart.SetCol( nCol2 + 1 );
+ theCurData.aEnd.SetCol( rDoc.MaxCol() );
+ }
+ }
+ if ( bValid )
+ {
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ OUString aStr(theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, eConv));
+
+ if(bRef)
+ m_xEdAssign->SetRefString( aStr );
+ else
+ m_xEdAssign->SetText( aStr );
+
+ m_xEdAssign->SetCursorAtLast();
+ aStr = theCurData.Format(rDoc, ScRefFlags::RANGE_ABS_3D, eConv);
+
+ if(bRef)
+ m_xEdAssign2->SetRefString( aStr );
+ else
+ m_xEdAssign2->SetText( aStr );
+ }
+ else
+ {
+ theCurData = theCurArea = ScRange();
+
+ if(bRef)
+ {
+ m_xEdAssign->SetRefString( OUString() );
+ m_xEdAssign2->SetRefString( OUString() );
+ }
+ else
+ {
+ m_xEdAssign->SetText( OUString() );
+ m_xEdAssign2->SetText( OUString() );
+ }
+
+ m_xBtnColHead->set_sensitive(false);
+ m_xBtnRowHead->set_sensitive(false);
+ m_xEdAssign2->GetWidget()->set_sensitive(false);
+ m_xRbAssign2->GetWidget()->set_sensitive(false);
+ }
+}
+
+// adjust label range and set the data reference form element
+void ScColRowNameRangesDlg::AdjustColRowData( const ScRange& rDataRange, bool bRef)
+{
+ theCurData = rDataRange;
+ if ( m_xBtnColHead->get_active() )
+ { // Data range is the same columns as the header
+ theCurData.aStart.SetCol( theCurArea.aStart.Col() );
+ theCurData.aEnd.SetCol( theCurArea.aEnd.Col() );
+ if ( theCurData.Intersects( theCurArea ) )
+ {
+ SCROW nRow1 = theCurArea.aStart.Row();
+ SCROW nRow2 = theCurArea.aEnd.Row();
+ if ( nRow1 > 0
+ && (theCurData.aEnd.Row() < nRow2 || nRow2 == rDoc.MaxRow()) )
+ { // Data above header
+ theCurData.aEnd.SetRow( nRow1 - 1 );
+ if ( theCurData.aStart.Row() > theCurData.aEnd.Row() )
+ theCurData.aStart.SetRow( theCurData.aEnd.Row() );
+ }
+ else
+ { // Data below header
+ theCurData.aStart.SetRow( nRow2 + 1 );
+ if ( theCurData.aStart.Row() > theCurData.aEnd.Row() )
+ theCurData.aEnd.SetRow( theCurData.aStart.Row() );
+ }
+ }
+ }
+ else
+ { // Data range in the same rows as header
+ theCurData.aStart.SetRow( theCurArea.aStart.Row() );
+ theCurData.aEnd.SetRow( theCurArea.aEnd.Row() );
+ if ( theCurData.Intersects( theCurArea ) )
+ {
+ SCCOL nCol1 = theCurArea.aStart.Col();
+ SCCOL nCol2 = theCurArea.aEnd.Col();
+ if ( nCol1 > 0
+ && (theCurData.aEnd.Col() < nCol2 || nCol2 == rDoc.MaxCol()) )
+ { // Data left of header
+ theCurData.aEnd.SetCol( nCol1 - 1 );
+ if ( theCurData.aStart.Col() > theCurData.aEnd.Col() )
+ theCurData.aStart.SetCol( theCurData.aEnd.Col() );
+ }
+ else
+ { // Data right of header
+ theCurData.aStart.SetCol( nCol2 + 1 );
+ if ( theCurData.aStart.Col() > theCurData.aEnd.Col() )
+ theCurData.aEnd.SetCol( theCurData.aStart.Col() );
+ }
+ }
+ }
+ OUString aStr(theCurData.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+
+ if(bRef)
+ m_xEdAssign2->SetRefString( aStr );
+ else
+ m_xEdAssign2->SetText( aStr );
+
+ m_xEdAssign2->SetCursorAtLast();
+}
+
+// Set the reference to a cell range selected with the mouse and update
+// the selection form element
+void ScColRowNameRangesDlg::SetReference( const ScRange& rRef, ScDocument& /* rDoc */ )
+{
+ if ( !m_pEdActive )
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_pEdActive );
+
+ if (m_pEdActive == m_xEdAssign.get())
+ SetColRowData( rRef, true );
+ else
+ AdjustColRowData( rRef, true );
+ m_xBtnColHead->set_sensitive(true);
+ m_xBtnRowHead->set_sensitive(true);
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnRemove->set_sensitive(false);
+}
+
+void ScColRowNameRangesDlg::Close()
+{
+ DoClose( ScColRowNameRangesDlgWrapper::GetChildWindowId() );
+}
+
+void ScColRowNameRangesDlg::SetActive()
+{
+ if ( bDlgLostFocus )
+ {
+ bDlgLostFocus = false;
+ if( m_pEdActive )
+ m_pEdActive->GrabFocus();
+ }
+ else
+ m_xDialog->grab_focus();
+
+ if( m_pEdActive == m_xEdAssign.get() )
+ Range1DataModifyHdl( *m_xEdAssign );
+ else if( m_pEdActive == m_xEdAssign2.get() )
+ Range2DataModifyHdl( *m_xEdAssign2 );
+
+ RefInputDone();
+}
+
+void ScColRowNameRangesDlg::UpdateNames()
+{
+ m_xLbRange->freeze();
+
+ m_xLbRange->clear();
+ aRangeMap.clear();
+ m_xEdAssign->SetText( OUString() );
+
+ size_t nCount, j;
+
+ SCCOL nCol1;
+ SCROW nRow1; //Extension for range names
+ SCTAB nTab1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ SCTAB nTab2;
+ OUString rString;
+ const ScAddress::Details aDetails(rDoc.GetAddressConvention());
+
+ OUString strDelim(" --- ");
+ OUString aString = strDelim + ScResId( STR_COLUMN ) + strDelim;
+ m_xLbRange->append(OUString::number(nEntryDataDelim), aString);
+ if ( xColNameRanges->size() > 0 )
+ {
+ std::vector<const ScRangePair*> aSortArray(xColNameRanges->CreateNameSortedArray(
+ rDoc ));
+ nCount = aSortArray.size();
+ for ( j=0; j < nCount; j++ )
+ {
+ const ScRange aRange(aSortArray[j]->GetRange(0));
+ aString = aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, aDetails);
+
+ //@008 get range parameters from document
+ aSortArray[j]->GetRange(0).GetVars( nCol1, nRow1, nTab1,
+ nCol2, nRow2, nTab2 );
+ SCCOL q=nCol1+3;
+ if(q>nCol2) q=nCol2;
+ //@008 construct string
+ OUStringBuffer strShow = " [";
+ rString = rDoc.GetString(nCol1, nRow1, nTab1);
+ strShow.append(rString);
+ for(SCCOL i=nCol1+1;i<=q;i++)
+ {
+ strShow.append(", ");
+ rString = rDoc.GetString(i, nRow1, nTab1);
+ strShow.append(rString);
+ }
+ if(q<nCol2) // Too long? Add ",..."
+ {
+ strShow.append(", ...");
+ }
+ strShow.append("]");
+
+ //@008 Add string to listbox
+ OUString aInsStr = aString + strShow;
+ aRangeMap.emplace( aInsStr, aRange );
+ m_xLbRange->append(OUString::number(nEntryDataCol), aInsStr);
+ }
+ }
+ aString = strDelim + ScResId( STR_ROW ) + strDelim;
+ m_xLbRange->append(OUString::number(nEntryDataDelim), aString);
+ if ( xRowNameRanges->size() > 0 )
+ {
+ std::vector<const ScRangePair*> aSortArray(xRowNameRanges->CreateNameSortedArray(
+ rDoc ));
+ nCount = aSortArray.size();
+ for ( j=0; j < nCount; j++ )
+ {
+ const ScRange aRange(aSortArray[j]->GetRange(0));
+ aString = aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, aDetails);
+
+ //@008 Build string for rows below
+ aSortArray[j]->GetRange(0).GetVars( nCol1, nRow1, nTab1,
+ nCol2, nRow2, nTab2 );
+ SCROW q=nRow1+3;
+ if(q>nRow2) q=nRow2;
+ OUStringBuffer strShow = " [";
+ rString = rDoc.GetString(nCol1, nRow1, nTab1);
+ strShow.append(rString);
+ for(SCROW i=nRow1+1;i<=q;i++)
+ {
+ strShow.append(", ");
+ rString = rDoc.GetString(nCol1, i, nTab1);
+ strShow.append(rString);
+ }
+ if(q<nRow2)
+ {
+ strShow.append(", ...");
+ }
+ strShow.append("]");
+
+ OUString aInsStr = aString + strShow;
+ aRangeMap.emplace( aInsStr, aRange );
+ m_xLbRange->append(OUString::number(nEntryDataRow), aInsStr);
+ }
+ }
+
+ m_xLbRange->thaw();
+}
+
+void ScColRowNameRangesDlg::UpdateRangeData( const ScRange& rRange, bool bColName )
+{
+ ScRangePair* pPair = nullptr;
+ bool bFound = false;
+ if ( bColName && (pPair = xColNameRanges->Find( rRange )) != nullptr )
+ bFound = true;
+ else if ( !bColName && (pPair = xRowNameRanges->Find( rRange )) != nullptr )
+ bFound = true;
+
+ if ( bFound )
+ {
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ theCurArea = rRange;
+ OUString aStr(theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, eConv));
+ m_xEdAssign->SetText( aStr );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(true);
+ m_xBtnColHead->set_active(bColName);
+ m_xBtnRowHead->set_active(!bColName);
+ theCurData = pPair->GetRange(1);
+ aStr = theCurData.Format(rDoc, ScRefFlags::RANGE_ABS_3D, eConv);
+ m_xEdAssign2->SetText( aStr );
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnRemove->set_sensitive(false);
+ }
+ m_xBtnColHead->set_sensitive(true);
+ m_xBtnRowHead->set_sensitive(true);
+ m_xEdAssign2->GetWidget()->set_sensitive(true);
+ m_xRbAssign2->GetWidget()->set_sensitive(true);
+}
+
+bool ScColRowNameRangesDlg::IsRefInputMode() const
+{
+ return (m_pEdActive != nullptr);
+}
+
+// Handler:
+
+// handler called when OK is clicked, calls the add button handler before
+// passing the range lists to the document
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, OkBtnHdl, weld::Button&, void)
+{
+ AddBtnHdl(*m_xBtnAdd);
+
+ // assign RangeLists to the references in the document
+ rDoc.GetColNameRangesRef() = xColNameRanges;
+ rDoc.GetRowNameRangesRef() = xRowNameRanges;
+ // changed ranges need to take effect
+ rDoc.CompileColRowNameFormula();
+ ScDocShell* pDocShell = m_rViewData.GetDocShell();
+ pDocShell->PostPaint(ScRange(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB), PaintPartFlags::Grid);
+ pDocShell->SetDocumentModified();
+
+ response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, CancelBtnHdl, weld::Button&, void)
+{
+ response(RET_CANCEL);
+}
+
+// handler called when add button clicked: set ranges and add to listbox
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, AddBtnHdl, weld::Button&, void)
+{
+ OUString aNewArea( m_xEdAssign->GetText() );
+ OUString aNewData( m_xEdAssign2->GetText() );
+
+ if (aNewArea.isEmpty() || aNewData.isEmpty())
+ return;
+
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ ScRange aRange1, aRange2;
+ bool bOk1 = (aRange1.ParseAny( aNewArea, rDoc, eConv ) & ScRefFlags::VALID) == ScRefFlags::VALID;
+ if ( bOk1 && (aRange2.ParseAny( aNewData, rDoc, eConv ) & ScRefFlags::VALID) == ScRefFlags::VALID)
+ {
+ theCurArea = aRange1;
+ AdjustColRowData( aRange2 );
+ ScRangePair* pPair;
+ if ( ( pPair = xColNameRanges->Find( theCurArea ) ) != nullptr )
+ {
+ xColNameRanges->Remove( *pPair );
+ }
+ if ( ( pPair = xRowNameRanges->Find( theCurArea ) ) != nullptr )
+ {
+ xRowNameRanges->Remove( *pPair );
+ }
+ if ( m_xBtnColHead->get_active() )
+ xColNameRanges->Join( ScRangePair( theCurArea, theCurData ) );
+ else
+ xRowNameRanges->Join( ScRangePair( theCurArea, theCurData ) );
+
+ UpdateNames();
+
+ m_xEdAssign->GrabFocus();
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->SetText( OUString() );
+ m_xBtnColHead->set_active(true);
+ m_xBtnRowHead->set_active(false);
+ m_xEdAssign2->SetText( OUString() );
+ theCurArea = ScRange();
+ theCurData = theCurArea;
+ Range1SelectHdl( *m_xLbRange );
+ }
+ else
+ {
+ ERRORBOX(m_xDialog.get(), ScResId(STR_INVALIDTABNAME));
+ if ( !bOk1 )
+ m_xEdAssign->GrabFocus();
+ else
+ m_xEdAssign2->GrabFocus();
+ }
+}
+
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, RemoveBtnHdl, weld::Button&, void)
+{
+ OUString aRangeStr = m_xLbRange->get_selected_text();
+ sal_Int32 nSelectPos = m_xLbRange->get_selected_index();
+ bool bColName = nSelectPos != -1 && m_xLbRange->get_id(nSelectPos).toInt32() == nEntryDataCol;
+ NameRangeMap::const_iterator itr = aRangeMap.find(aRangeStr);
+ if (itr == aRangeMap.end())
+ return;
+ const ScRange& rRange = itr->second;
+
+ ScRangePair* pPair = nullptr;
+ bool bFound = false;
+ if ( bColName && (pPair = xColNameRanges->Find( rRange )) != nullptr )
+ bFound = true;
+ else if ( !bColName && (pPair = xRowNameRanges->Find( rRange )) != nullptr )
+ bFound = true;
+ if ( !bFound )
+ return;
+
+ OUString aStrDelMsg = ScResId( STR_QUERY_DELENTRY );
+ OUString aMsg = o3tl::getToken(aStrDelMsg, 0, '#' )
+ + aRangeStr
+ + o3tl::getToken(aStrDelMsg, 1, '#' );
+
+ if (RET_YES != QUERYBOX(m_xDialog.get(), aMsg))
+ return;
+
+ if ( bColName )
+ xColNameRanges->Remove( *pPair );
+ else
+ xRowNameRanges->Remove( *pPair );
+
+ UpdateNames();
+ const sal_Int32 nCnt = m_xLbRange->n_children();
+ if ( nSelectPos >= nCnt )
+ {
+ if ( nCnt )
+ nSelectPos = nCnt - 1;
+ else
+ nSelectPos = 0;
+ }
+ m_xLbRange->select(nSelectPos);
+ if (nSelectPos && m_xLbRange->get_id(nSelectPos).toInt32() == nEntryDataDelim)
+ m_xLbRange->select( --nSelectPos ); // ---Row---
+
+ m_xLbRange->grab_focus();
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->SetText( OUString() );
+ theCurArea = theCurData = ScRange();
+ m_xBtnColHead->set_active(true);
+ m_xBtnRowHead->set_active(false);
+ m_xEdAssign2->SetText( OUString() );
+ Range1SelectHdl( *m_xLbRange );
+}
+
+// handler called when a row in the listbox is selected, updates form input fields
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, Range1SelectHdl, weld::TreeView&, void)
+{
+ sal_Int32 nSelectPos = m_xLbRange->get_selected_index();
+ const sal_Int32 nCnt = m_xLbRange->n_children();
+ sal_uInt16 nMoves = 0;
+ while (nSelectPos != -1 && nSelectPos < nCnt && m_xLbRange->get_id(nSelectPos).toInt32() == nEntryDataDelim)
+ { // skip Delimiter
+ ++nMoves;
+ ++nSelectPos;
+ }
+ OUString aRangeStr = m_xLbRange->get_selected_text();
+ if ( nMoves )
+ {
+ if ( nSelectPos > 1 && nSelectPos >= nCnt )
+ { // if entries exist before the " --- Row --- " Delimiter then
+ // do not stop at the delimiter
+ nSelectPos = nCnt - 2;
+ m_xLbRange->select(nSelectPos);
+ aRangeStr = m_xLbRange->get_selected_text();
+ }
+ else if ( nSelectPos > 2 && nSelectPos < nCnt && !aRangeStr.isEmpty()
+ && aRangeStr == m_xEdAssign->GetText() )
+ { // move upwards instead of below to the previous position
+ nSelectPos -= 2;
+ m_xLbRange->select( nSelectPos );
+ aRangeStr = m_xLbRange->get_selected_text();
+ }
+ else
+ m_xLbRange->select(nSelectPos);
+ }
+ NameRangeMap::const_iterator itr = aRangeMap.find(aRangeStr);
+ if ( itr != aRangeMap.end() )
+ {
+ bool bColName = m_xLbRange->get_id(nSelectPos).toInt32() == nEntryDataCol;
+ UpdateRangeData( itr->second, bColName );
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(true);
+ }
+ else
+ {
+ if ( !m_xEdAssign->GetText().isEmpty() )
+ {
+ if ( !m_xEdAssign2->GetText().isEmpty() )
+ m_xBtnAdd->set_sensitive(true);
+ else
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnColHead->set_sensitive(true);
+ m_xBtnRowHead->set_sensitive(true);
+ m_xEdAssign2->GetWidget()->set_sensitive(true);
+ m_xRbAssign2->GetWidget()->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnColHead->set_sensitive(false);
+ m_xBtnRowHead->set_sensitive(false);
+ m_xEdAssign2->GetWidget()->set_sensitive(false);
+ m_xRbAssign2->GetWidget()->set_sensitive(false);
+ }
+ m_xBtnRemove->set_sensitive(false);
+ m_xEdAssign->GrabFocus();
+ }
+
+ m_xEdAssign->GetWidget()->set_sensitive(true);
+ m_xRbAssign->GetWidget()->set_sensitive(true);
+}
+
+// handler called when the label range has changed
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, Range1DataModifyHdl, formula::RefEdit&, void)
+{
+ OUString aNewArea( m_xEdAssign->GetText() );
+ bool bValid = false;
+ if (!aNewArea.isEmpty())
+ {
+ ScRange aRange;
+ if ( (aRange.ParseAny(aNewArea, rDoc, rDoc.GetAddressConvention() ) & ScRefFlags::VALID) == ScRefFlags::VALID)
+ {
+ SetColRowData( aRange );
+ bValid = true;
+ }
+ }
+ if ( bValid )
+ {
+ m_xBtnAdd->set_sensitive(true);
+ m_xBtnColHead->set_sensitive(true);
+ m_xBtnRowHead->set_sensitive(true);
+ m_xEdAssign2->GetWidget()->set_sensitive(true);
+ m_xRbAssign2->GetWidget()->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnColHead->set_sensitive(false);
+ m_xBtnRowHead->set_sensitive(false);
+ m_xEdAssign2->GetWidget()->set_sensitive(false);
+ m_xRbAssign2->GetWidget()->set_sensitive(false);
+ }
+ m_xBtnRemove->set_sensitive(false);
+}
+
+// handler called when the data range has changed
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, Range2DataModifyHdl, formula::RefEdit&, void)
+{
+ OUString aNewData( m_xEdAssign2->GetText() );
+ if ( !aNewData.isEmpty() )
+ {
+ ScRange aRange;
+ if ( (aRange.ParseAny(aNewData, rDoc, rDoc.GetAddressConvention() ) & ScRefFlags::VALID) == ScRefFlags::VALID)
+ {
+ AdjustColRowData( aRange );
+ m_xBtnAdd->set_sensitive(true);
+ }
+ else
+ m_xBtnAdd->set_sensitive(false);
+ }
+ else
+ {
+ m_xBtnAdd->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, ColRowToggleHdl, weld::Toggleable&, void)
+{
+ if (m_xBtnColHead->get_active())
+ {
+ // handler for the radio button for columns, adjust ranges
+ if ( theCurArea.aStart.Row() == 0 && theCurArea.aEnd.Row() == rDoc.MaxRow() )
+ {
+ theCurArea.aEnd.SetRow( rDoc.MaxRow() - 1 );
+ OUString aStr(theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ m_xEdAssign->SetText( aStr );
+ }
+ ScRange aRange( theCurData );
+ aRange.aStart.SetRow( std::min( static_cast<tools::Long>(theCurArea.aEnd.Row() + 1), static_cast<tools::Long>(rDoc.MaxRow()) ) );
+ aRange.aEnd.SetRow( rDoc.MaxRow() );
+ AdjustColRowData( aRange );
+ }
+ else if (m_xBtnRowHead->get_active())
+ {
+ // handler for the radio button for columns, adjust range
+ if ( theCurArea.aStart.Col() == 0 && theCurArea.aEnd.Col() == rDoc.MaxCol() )
+ {
+ theCurArea.aEnd.SetCol( rDoc.MaxCol() - 1 );
+ OUString aStr(theCurArea.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
+ m_xEdAssign->SetText( aStr );
+ }
+ ScRange aRange( theCurData );
+ aRange.aStart.SetCol( static_cast<SCCOL>(std::min( static_cast<tools::Long>(theCurArea.aEnd.Col() + 1), static_cast<tools::Long>(rDoc.MaxCol()) )) );
+ aRange.aEnd.SetCol( rDoc.MaxCol() );
+ AdjustColRowData( aRange );
+ }
+}
+
+IMPL_LINK( ScColRowNameRangesDlg, GetEditFocusHdl, formula::RefEdit&, rCtrl, void )
+{
+ if (&rCtrl == m_xEdAssign.get())
+ m_pEdActive = m_xEdAssign.get();
+ else if (&rCtrl == m_xEdAssign2.get())
+ m_pEdActive = m_xEdAssign2.get();
+ else
+ m_pEdActive = nullptr;
+
+ if( m_pEdActive )
+ m_pEdActive->SelectAll();
+}
+
+IMPL_LINK( ScColRowNameRangesDlg, GetButtonFocusHdl, formula::RefButton&, rCtrl, void )
+{
+ if (&rCtrl == m_xRbAssign.get())
+ m_pEdActive = m_xEdAssign.get();
+ else if (&rCtrl == m_xRbAssign2.get())
+ m_pEdActive = m_xEdAssign2.get();
+ else
+ m_pEdActive = nullptr;
+
+ if( m_pEdActive )
+ m_pEdActive->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, LoseEditFocusHdl, formula::RefEdit&, void)
+{
+ bDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScColRowNameRangesDlg, LoseButtonFocusHdl, formula::RefButton&, void)
+{
+ bDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/datafdlg.cxx b/sc/source/ui/miscdlgs/datafdlg.cxx
new file mode 100644
index 0000000000..0ed421cba8
--- /dev/null
+++ b/sc/source/ui/miscdlgs/datafdlg.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/.
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <datafdlg.hxx>
+#include <viewdata.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+
+ScDataFormDlg::ScDataFormDlg(weld::Window* pParent, ScTabViewShell* pTabViewShellOri)
+ : GenericDialogController(pParent, "modules/scalc/ui/dataform.ui", "DataFormDialog")
+ , pTabViewShell(pTabViewShellOri)
+ , aColLength(0)
+ , nCurrentRow(0)
+ , nStartCol(0)
+ , nEndCol(0)
+ , nStartRow(0)
+ , nEndRow(0)
+ , nTab(0)
+ , m_xBtnNew(m_xBuilder->weld_button("new"))
+ , m_xBtnDelete(m_xBuilder->weld_button("delete"))
+ , m_xBtnRestore(m_xBuilder->weld_button("restore"))
+ , m_xBtnPrev(m_xBuilder->weld_button("prev"))
+ , m_xBtnNext(m_xBuilder->weld_button("next"))
+ , m_xBtnClose(m_xBuilder->weld_button("close"))
+ , m_xSlider(m_xBuilder->weld_scrolled_window("scrollbar", true))
+ , m_xGrid(m_xBuilder->weld_container("grid"))
+ , m_xFixedText(m_xBuilder->weld_label("label"))
+{
+ sNewRecord = m_xFixedText->get_label();
+
+ //read header from current document, and add new controls
+ OSL_ENSURE( pTabViewShell, "pTabViewShell is NULL! :-/" );
+ ScViewData& rViewData = pTabViewShell->GetViewData();
+
+ pDoc = &rViewData.GetDocument();
+
+ {
+ ScRange aRange;
+ rViewData.GetSimpleArea( aRange );
+ ScAddress aStart = aRange.aStart;
+ ScAddress aEnd = aRange.aEnd;
+
+ nStartCol = aStart.Col();
+ nEndCol = aEnd.Col();
+ nStartRow = aStart.Row();
+ nEndRow = aEnd.Row();
+
+ nTab = rViewData.GetTabNo();
+ bool bNoSelection(false);
+ //if there is no selection
+ if ((nStartCol == nEndCol) && (nStartRow == nEndRow))
+ bNoSelection = true;
+
+ if (bNoSelection)
+ {
+ //find last not blank cell in row
+ for (int i=1;i<=MAX_DATAFORM_COLS;i++)
+ {
+ nEndCol++;
+ OUString aColName = pDoc->GetString(nEndCol, nStartRow, nTab);
+ int nColWidth = pDoc->GetColWidth( nEndCol, nTab );
+ if (aColName.isEmpty() && nColWidth)
+ {
+ nEndCol--;
+ break;
+ }
+ }
+
+ //find first not blank cell in row
+ for (int i=1;i<=MAX_DATAFORM_COLS;i++)
+ {
+ if (nStartCol <= 0)
+ break;
+ nStartCol--;
+
+ OUString aColName = pDoc->GetString(nStartCol, nStartRow, nTab);
+ int nColWidth = pDoc->GetColWidth( nEndCol, nTab );
+ if (aColName.isEmpty() && nColWidth)
+ {
+ nStartCol++;
+ break;
+ }
+ }
+
+ //skip leading hide column
+ for (int i=1;i<=MAX_DATAFORM_COLS;i++)
+ {
+ int nColWidth = pDoc->GetColWidth( nStartCol, nTab );
+ if (nColWidth)
+ break;
+ nStartCol++;
+ }
+
+ if (nEndCol < nStartCol)
+ nEndCol = nStartCol;
+
+ //find last not blank cell in row
+ for (int i=1;i<=MAX_DATAFORM_ROWS;i++)
+ {
+ nEndRow++;
+ OUString aColName = pDoc->GetString(nStartCol, nEndRow, nTab);
+ if (aColName.isEmpty())
+ {
+ nEndRow--;
+ break;
+ }
+ }
+
+ //find first not blank cell in row
+ for (int i=1;i<=MAX_DATAFORM_ROWS;i++)
+ {
+ if (nStartRow <= 0)
+ break;
+ nStartRow--;
+
+ OUString aColName = pDoc->GetString(nStartCol, nStartRow, nTab);
+ if (aColName.isEmpty())
+ {
+ nStartRow++;
+ break;
+ }
+ }
+
+ if (nEndRow < nStartRow)
+ nEndRow = nStartRow;
+ }
+
+ nCurrentRow = nStartRow + 1;
+
+ aColLength = nEndCol - nStartCol + 1;
+
+ //new the controls
+ m_aEntries.reserve(aColLength);
+
+ sal_Int32 nGridRow = 0;
+ for(sal_uInt16 nIndex = 0; nIndex < aColLength; ++nIndex)
+ {
+ OUString aFieldName = pDoc->GetString(nIndex + nStartCol, nStartRow, nTab);
+ int nColWidth = pDoc->GetColWidth( nIndex + nStartCol, nTab );
+ if (nColWidth)
+ {
+ m_aEntries.emplace_back(new ScDataFormFragment(m_xGrid.get(), nGridRow));
+
+ ++nGridRow;
+
+ m_aEntries[nIndex]->m_xLabel->set_label(aFieldName);
+ m_aEntries[nIndex]->m_xLabel->show();
+ m_aEntries[nIndex]->m_xEdit->show();
+ }
+ else
+ {
+ m_aEntries.emplace_back(nullptr );
+ }
+ if (m_aEntries[nIndex] != nullptr)
+ {
+ m_aEntries[nIndex]->m_xEdit->connect_changed(LINK( this, ScDataFormDlg, Impl_DataModifyHdl));
+ m_aEntries[nIndex]->m_xEdit->save_value();
+ }
+ }
+ }
+
+ FillCtrls();
+
+ m_xSlider->vadjustment_configure(0, 0, nEndRow - nStartRow + 1, 1, 10, 1);
+
+ m_xBtnNew->connect_clicked(LINK( this, ScDataFormDlg, Impl_NewHdl));
+ m_xBtnPrev->connect_clicked(LINK( this, ScDataFormDlg, Impl_PrevHdl));
+ m_xBtnNext->connect_clicked(LINK( this, ScDataFormDlg, Impl_NextHdl));
+
+ m_xBtnRestore->connect_clicked(LINK( this, ScDataFormDlg, Impl_RestoreHdl));
+ m_xBtnDelete->connect_clicked(LINK( this, ScDataFormDlg, Impl_DeleteHdl));
+ m_xBtnClose->connect_clicked(LINK( this, ScDataFormDlg, Impl_CloseHdl));
+
+ m_xSlider->connect_vadjustment_changed(LINK( this, ScDataFormDlg, Impl_ScrollHdl));
+
+ SetButtonState();
+}
+
+ScDataFormDlg::~ScDataFormDlg()
+{
+}
+
+void ScDataFormDlg::FillCtrls()
+{
+ for (sal_uInt16 i = 0; i < aColLength; ++i)
+ {
+ if (m_aEntries[i])
+ {
+ if (nCurrentRow<=nEndRow && pDoc)
+ {
+ OUString aFieldName(pDoc->GetString(i + nStartCol, nCurrentRow, nTab));
+ m_aEntries[i]->m_xEdit->set_text(aFieldName);
+ }
+ else
+ m_aEntries[i]->m_xEdit->set_text(OUString());
+ }
+ }
+
+ if (nCurrentRow <= nEndRow)
+ {
+ OUString sLabel =
+ OUString::number(static_cast<sal_Int32>(nCurrentRow - nStartRow)) +
+ " / " +
+ OUString::number(static_cast<sal_Int32>(nEndRow - nStartRow));
+ m_xFixedText->set_label(sLabel);
+ }
+ else
+ m_xFixedText->set_label(sNewRecord);
+
+ m_xSlider->vadjustment_set_value(nCurrentRow-nStartRow-1);
+}
+
+IMPL_LINK( ScDataFormDlg, Impl_DataModifyHdl, weld::Entry&, rEdit, void)
+{
+ if (rEdit.get_value_changed_from_saved())
+ m_xBtnRestore->set_sensitive(true);
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_NewHdl, weld::Button&, void)
+{
+ ScViewData& rViewData = pTabViewShell->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ if ( !pDoc )
+ return;
+
+ bool bHasData = std::any_of(m_aEntries.begin(), m_aEntries.end(),
+ [](const std::unique_ptr<ScDataFormFragment>& rElem) { return (rElem != nullptr) && (!rElem->m_xEdit->get_text().isEmpty()); });
+
+ if ( !bHasData )
+ return;
+
+ pTabViewShell->DataFormPutData(nCurrentRow, nStartRow, nStartCol, nEndRow, nEndCol, m_aEntries, aColLength);
+ nCurrentRow++;
+ if (nCurrentRow >= nEndRow + 2)
+ {
+ nEndRow++;
+ m_xSlider->vadjustment_set_upper(nEndRow - nStartRow + 1);
+ }
+ SetButtonState();
+ FillCtrls();
+ pDocSh->SetDocumentModified();
+ pDocSh->PostPaintGridAll();
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_PrevHdl, weld::Button&, void)
+{
+ if (pDoc)
+ {
+ if ( nCurrentRow > nStartRow +1 )
+ nCurrentRow--;
+
+ SetButtonState();
+ FillCtrls();
+ }
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_NextHdl, weld::Button&, void)
+{
+ if (pDoc)
+ {
+ if ( nCurrentRow <= nEndRow)
+ nCurrentRow++;
+
+ SetButtonState();
+ FillCtrls();
+ }
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_RestoreHdl, weld::Button&, void)
+{
+ if (pDoc)
+ {
+ FillCtrls();
+ }
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_DeleteHdl, weld::Button&, void)
+{
+ ScViewData& rViewData = pTabViewShell->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ if (!pDoc)
+ return;
+
+ ScRange aRange(nStartCol, nCurrentRow, nTab, nEndCol, nCurrentRow, nTab);
+ pDoc->DeleteRow(aRange);
+ nEndRow--;
+
+ SetButtonState();
+ pDocSh->GetUndoManager()->Clear();
+
+ FillCtrls();
+ pDocSh->SetDocumentModified();
+ pDocSh->PostPaintGridAll();
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_CloseHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScDataFormDlg, Impl_ScrollHdl, weld::ScrolledWindow&, void)
+{
+ auto nOffset = m_xSlider->vadjustment_get_value();
+ nCurrentRow = nStartRow + nOffset + 1;
+ SetButtonState();
+ FillCtrls();
+}
+
+void ScDataFormDlg::SetButtonState()
+{
+ if (nCurrentRow > nEndRow)
+ {
+ m_xBtnDelete->set_sensitive( false );
+ m_xBtnNext->set_sensitive( false );
+ }
+ else
+ {
+ m_xBtnDelete->set_sensitive(true);
+ m_xBtnNext->set_sensitive(true);
+ }
+
+ if (nCurrentRow == nStartRow + 1)
+ m_xBtnPrev->set_sensitive( false );
+ else
+ m_xBtnPrev->set_sensitive(true);
+
+ m_xBtnRestore->set_sensitive( false );
+ if (!m_aEntries.empty() && m_aEntries[0] != nullptr)
+ m_aEntries[0]->m_xEdit->grab_focus();
+}
+
+ScDataFormFragment::ScDataFormFragment(weld::Container* pGrid, int nLine)
+ : m_xBuilder(Application::CreateBuilder(pGrid, "modules/scalc/ui/dataformfragment.ui"))
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xEdit(m_xBuilder->weld_entry("entry"))
+{
+ m_xLabel->set_grid_left_attach(0);
+ m_xLabel->set_grid_top_attach(nLine);
+
+ m_xEdit->set_grid_left_attach(1);
+ m_xEdit->set_grid_top_attach(nLine);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/dataproviderdlg.cxx b/sc/source/ui/miscdlgs/dataproviderdlg.cxx
new file mode 100644
index 0000000000..16a6c44664
--- /dev/null
+++ b/sc/source/ui/miscdlgs/dataproviderdlg.cxx
@@ -0,0 +1,1122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <dataproviderdlg.hxx>
+
+#include <document.hxx>
+#include <dataprovider.hxx>
+#include <datatransformation.hxx>
+#include <datamapper.hxx>
+#include <dbdata.hxx>
+
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <unotools/charclass.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+class ScDataTransformationBaseControl
+{
+protected:
+ std::unique_ptr<weld::Builder> mxBuilder;
+ std::unique_ptr<weld::Container> mxGrid;
+ weld::Container* mpContainer;
+
+ sal_uInt32 mnIndex;
+
+public:
+ ScDataTransformationBaseControl(weld::Container* pParent, const OUString& rUIFile, sal_uInt32 nIndex);
+ virtual ~ScDataTransformationBaseControl();
+
+ void updateIndex(sal_uInt32 nIndex) { mnIndex = nIndex; }
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() = 0;
+
+ static SCROW getLastRow(const ScDocument& rDoc);
+ static SCCOL getLastCol(const ScDocument& rDoc);
+};
+
+SCROW ScDataTransformationBaseControl::getLastRow(const ScDocument& rDoc)
+{
+ SCROW nEndRow = rDoc.MaxRow();
+ return rDoc.GetLastDataRow(0, 0, 0, nEndRow);
+}
+
+SCCOL ScDataTransformationBaseControl::getLastCol(const ScDocument& rDoc)
+{
+ for (SCCOL nCol = 1; nCol <= rDoc.MaxCol(); ++nCol)
+ {
+ if (rDoc.GetCellType(nCol, 0, 0) == CELLTYPE_NONE)
+ {
+ return static_cast<SCCOL>(nCol - 1 );
+ }
+ }
+ return rDoc.MaxCol();
+}
+
+ScDataTransformationBaseControl::ScDataTransformationBaseControl(weld::Container* pParent, const OUString& rUIFile, sal_uInt32 nIndex)
+ : mxBuilder(Application::CreateBuilder(pParent, rUIFile))
+ , mxGrid(mxBuilder->weld_container("grid"))
+ , mpContainer(pParent)
+ , mnIndex(nIndex)
+{
+}
+
+ScDataTransformationBaseControl::~ScDataTransformationBaseControl()
+{
+ mpContainer->move(mxGrid.get(), nullptr);
+}
+
+namespace {
+
+struct MenuData
+{
+ const char* aTransformationName;
+ std::function<void(ScDataProviderDlg*)> maCallback;
+};
+
+MenuData aTransformationData[] = {
+ { "Delete Column", &ScDataProviderDlg::deleteColumn },
+ { "Delete Row", &ScDataProviderDlg::deleteRowTransformation},
+ { "Swap Rows", &ScDataProviderDlg::swapRowsTransformation},
+ { "Split Column", &ScDataProviderDlg::splitColumn },
+ { "Merge Columns", &ScDataProviderDlg::mergeColumns },
+ { "Text Transformation", &ScDataProviderDlg::textTransformation },
+ { "Sort Columns", &ScDataProviderDlg::sortTransformation },
+ { "Aggregate Functions", &ScDataProviderDlg::aggregateFunction},
+ { "Number Transformations", &ScDataProviderDlg::numberTransformation },
+ { "Replace Null Transformations", &ScDataProviderDlg::replaceNullTransformation },
+ { "Date & Time Transformations", &ScDataProviderDlg::dateTimeTransformation },
+ { "Find Replace Transformation", &ScDataProviderDlg::findReplaceTransformation}
+};
+
+class ScDeleteColumnTransformationControl : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxColumnNums;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScDeleteColumnTransformationControl(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 aIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScDeleteColumnTransformationControl::ScDeleteColumnTransformationControl(
+ const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/deletecolumnentry.ui", nIndex)
+ , mxColumnNums(mxBuilder->weld_entry("ed_columns"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScDeleteColumnTransformationControl, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScDeleteColumnTransformationControl::getTransformation()
+{
+ OUString aColumnString = mxColumnNums->get_text();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> ColNums;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ ColNums.insert(nCol - 1);
+ }
+
+ return std::make_shared<sc::ColumnRemoveTransformation>(std::move(ColNums));
+}
+
+class ScSplitColumnTransformationControl : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxSeparator;
+ std::unique_ptr<weld::Entry> mxNumColumns;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScSplitColumnTransformationControl(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScSplitColumnTransformationControl::ScSplitColumnTransformationControl(
+ const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex,
+ std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/splitcolumnentry.ui", nIndex)
+ , mxSeparator(mxBuilder->weld_entry("ed_separator"))
+ , mxNumColumns(mxBuilder->weld_entry("num_cols"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScSplitColumnTransformationControl, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScSplitColumnTransformationControl::getTransformation()
+{
+ OUString aSeparator = mxSeparator->get_text();
+ sal_Unicode cSeparator = aSeparator.isEmpty() ? ',' : aSeparator[0];
+ OUString aColStr = mxNumColumns->get_text();
+ SCCOL mnCol = -1;
+ sal_Int32 nCol = aColStr.toInt32();
+ if (nCol > 0 && nCol <= mpDoc->MaxCol())
+ mnCol = nCol - 1;
+ return std::make_shared<sc::SplitColumnTransformation>(mnCol, cSeparator);
+}
+
+class ScMergeColumnTransformationControl : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxSeparator;
+ std::unique_ptr<weld::Entry> mxEdColumns;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScMergeColumnTransformationControl(const ScDocument *pDoc, weld::Container* pParent, SCCOL nStartCol, SCCOL nEndCol, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScMergeColumnTransformationControl::ScMergeColumnTransformationControl(
+ const ScDocument* pDoc, weld::Container* pParent, SCCOL nStartCol, SCCOL nEndCol, sal_uInt32 nIndex,
+ std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/mergecolumnentry.ui", nIndex)
+ , mxSeparator(mxBuilder->weld_entry("ed_separator"))
+ , mxEdColumns(mxBuilder->weld_entry("ed_columns"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScMergeColumnTransformationControl, DeleteHdl));
+
+ OUStringBuffer aBuffer;
+
+ // map from zero based to one based column numbers
+ aBuffer.append(static_cast<sal_Int32>(nStartCol + 1));
+ for ( SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
+ {
+ aBuffer.append(";" + OUString::number(nCol + 1));
+ }
+
+ mxEdColumns->set_text(aBuffer.makeStringAndClear());
+}
+
+std::shared_ptr<sc::DataTransformation> ScMergeColumnTransformationControl::getTransformation()
+{
+ OUString aColumnString = mxEdColumns->get_text();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> aMergedColumns;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ aMergedColumns.insert(nCol - 1);
+ }
+ return std::make_shared<sc::MergeColumnTransformation>(std::move(aMergedColumns), mxSeparator->get_text());
+}
+
+class ScSortTransformationControl : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::ComboBox> mxType;
+ std::unique_ptr<weld::Entry> mxEdColumns;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScSortTransformationControl(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScSortTransformationControl::ScSortTransformationControl(
+ const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/sorttransformationentry.ui", nIndex)
+ , mxType(mxBuilder->weld_combo_box("ed_ascending"))
+ , mxEdColumns(mxBuilder->weld_entry("ed_columns"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScSortTransformationControl, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScSortTransformationControl::getTransformation()
+{
+ OUString aColStr = mxEdColumns->get_text();
+ bool aIsAscending = mxType->get_active();
+ SCCOL aColumn = 0;
+ sal_Int32 nCol = aColStr.toInt32();
+ if (nCol > 0 && nCol <= mpDoc->MaxCol())
+ aColumn = nCol - 1; // translate from 1-based column notations to internal Calc one
+
+ ScSortParam aSortParam;
+ aSortParam.nRow1=0;
+ aSortParam.nRow2=getLastRow(*mpDoc);
+ aSortParam.nCol1=0;
+ aSortParam.nCol2=getLastCol(*mpDoc);
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = aColumn;
+ aSortParam.maKeyState[0].bAscending = aIsAscending;
+ return std::make_shared<sc::SortTransformation>(aSortParam);
+}
+
+class ScColumnTextTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxColumnNums;
+ std::unique_ptr<weld::ComboBox> mxType;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScColumnTextTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScColumnTextTransformation::ScColumnTextTransformation(
+ const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/texttransformationentry.ui", nIndex)
+ , mxColumnNums(mxBuilder->weld_entry("ed_columns"))
+ , mxType(mxBuilder->weld_combo_box("ed_lst"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScColumnTextTransformation, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScColumnTextTransformation::getTransformation()
+{
+ OUString aColumnString = mxColumnNums->get_text();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> aColumns;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ aColumns.insert(nCol - 1);
+ }
+
+ sal_Int32 nPos = mxType->get_active();
+ switch (nPos)
+ {
+ case 0:
+ return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::TO_LOWER);
+ case 1:
+ return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::TO_UPPER);
+ case 2:
+ return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::CAPITALIZE);
+ case 3:
+ return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::TRIM);
+ default:
+ assert(false);
+ }
+
+ return nullptr;
+}
+
+class ScAggregateFunction : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxColumnNums;
+ std::unique_ptr<weld::ComboBox> mxType;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScAggregateFunction(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScAggregateFunction::ScAggregateFunction(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex,
+ std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/aggregatefunctionentry.ui", nIndex)
+ , mxColumnNums(mxBuilder->weld_entry("ed_columns"))
+ , mxType(mxBuilder->weld_combo_box("ed_lst"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScAggregateFunction, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScAggregateFunction::getTransformation()
+{
+ OUString aColumnString = mxColumnNums->get_text();
+ sal_Int32 nPos = mxType->get_active();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> aColumns;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ aColumns.insert(nCol - 1);
+ }
+ switch (nPos)
+ {
+ case 0:
+ return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::SUM);
+ case 1:
+ return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::AVERAGE);
+ case 2:
+ return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::MIN);
+ case 3:
+ return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::MAX);
+ default:
+ assert(false);
+ }
+
+ return nullptr;
+}
+
+class ScNumberTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxColumnNums;
+ std::unique_ptr<weld::ComboBox> mxType;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScNumberTransformation(const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScNumberTransformation::ScNumberTransformation(
+ const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/numbertransformationentry.ui", nIndex)
+ , mxColumnNums(mxBuilder->weld_entry("ed_columns"))
+ , mxType(mxBuilder->weld_combo_box("ed_lst"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScNumberTransformation, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScNumberTransformation::getTransformation()
+{
+ OUString aColumnString = mxColumnNums->get_text();
+ sal_Int32 nPos = mxType->get_active();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> aColumns;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ aColumns.insert(nCol - 1);
+ }
+ switch (nPos)
+ {
+ case 0:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::SIGN);
+ case 1:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ROUND);
+ case 2:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ROUND_UP);
+ case 3:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN);
+ case 4:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE);
+ case 5:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::LOG_E);
+ case 6:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::LOG_10);
+ case 7:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::CUBE);
+ case 8:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::SQUARE);
+ case 9:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT);
+ case 10:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::EXPONENT);
+ case 11:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::IS_EVEN);
+ case 12:
+ return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::IS_ODD);
+ default:
+ assert(false);
+ }
+
+ return nullptr;
+}
+
+class ScReplaceNullTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxColumnNums;
+ std::unique_ptr<weld::Entry> mxReplaceString;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument *mpDoc;
+
+public:
+
+ ScReplaceNullTransformation(const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScReplaceNullTransformation::ScReplaceNullTransformation(const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent,"modules/scalc/ui/replacenulltransformationentry.ui", nIndex)
+ , mxColumnNums(mxBuilder->weld_entry("ed_columns"))
+ , mxReplaceString(mxBuilder->weld_entry("ed_str"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScReplaceNullTransformation, DeleteHdl));
+}
+
+
+std::shared_ptr<sc::DataTransformation> ScReplaceNullTransformation::getTransformation()
+{
+ OUString aColumnString = mxColumnNums->get_text();
+ OUString aReplaceWithString = mxReplaceString->get_text();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> aColumns;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ aColumns.insert(nCol - 1);
+ }
+
+ return std::make_shared<sc::ReplaceNullTransformation>(std::move(aColumns),aReplaceWithString);
+}
+
+class ScDateTimeTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxColumnNums;
+ std::unique_ptr<weld::ComboBox> mxType;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+
+ ScDateTimeTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScDateTimeTransformation::ScDateTimeTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent,"modules/scalc/ui/datetimetransformationentry.ui", nIndex)
+ , mxColumnNums(mxBuilder->weld_entry("ed_columns"))
+ , mxType(mxBuilder->weld_combo_box("ed_lst"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this,ScDateTimeTransformation, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScDateTimeTransformation::getTransformation()
+{
+ OUString aColumnString = mxColumnNums->get_text();
+ sal_Int32 nPos = mxType->get_active();
+ std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
+ std::set<SCCOL> aColumns;
+ for (const auto& rColStr : aSplitColumns)
+ {
+ sal_Int32 nCol = rColStr.toInt32();
+ if (nCol <= 0)
+ continue;
+
+ if (nCol > mpDoc->MaxCol())
+ continue;
+
+ // translate from 1-based column notations to internal Calc one
+ aColumns.insert(nCol - 1);
+ }
+ switch (nPos)
+ {
+ case 0:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING);
+ case 1:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::YEAR);
+ case 2:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR);
+ case 3:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR);
+ case 4:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::MONTH);
+ case 5:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME);
+ case 6:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH);
+ case 7:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH);
+ case 8:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DAY);
+ case 9:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK);
+ case 10:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR);
+ case 11:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::QUARTER);
+ case 12:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER);
+ case 13:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER);
+ case 14:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::HOUR);
+ case 15:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::MINUTE);
+ case 16:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::SECOND);
+ case 17:
+ return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::TIME);
+ default:
+ assert(false);
+ }
+
+ return nullptr;
+}
+
+class ScFindReplaceTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxFindString;
+ std::unique_ptr<weld::Entry> mxReplaceString;
+ std::unique_ptr<weld::Entry> mxEdColumns;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScFindReplaceTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScFindReplaceTransformation::ScFindReplaceTransformation(
+ const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex,
+ std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/findreplaceentry.ui", nIndex)
+ , mxFindString(mxBuilder->weld_entry("ed_find"))
+ , mxReplaceString(mxBuilder->weld_entry("ed_replace"))
+ , mxEdColumns(mxBuilder->weld_entry("ed_columns"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this, ScFindReplaceTransformation, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScFindReplaceTransformation::getTransformation()
+{
+ OUString aColStr = mxEdColumns->get_text();
+ SCCOL aColumn = -1;
+ sal_Int32 nCol = aColStr.toInt32();
+ if (nCol > 0 && nCol <= mpDoc->MaxCol())
+ aColumn = nCol - 1;
+ return std::make_shared<sc::FindReplaceTransformation>(aColumn, mxFindString->get_text(), mxReplaceString->get_text());
+}
+
+class ScDeleteRowTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxFindString;
+ std::unique_ptr<weld::Entry> mxEdColumns;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScDeleteRowTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScDeleteRowTransformation::ScDeleteRowTransformation(
+ const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex,
+ std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/deleterowentry.ui", nIndex)
+ , mxFindString(mxBuilder->weld_entry("ed_find"))
+ , mxEdColumns(mxBuilder->weld_entry("ed_columns"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this, ScDeleteRowTransformation, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScDeleteRowTransformation::getTransformation()
+{
+ OUString aColStr = mxEdColumns->get_text();
+ SCCOL aColumn = -1;
+ sal_Int32 nCol = aColStr.toInt32();
+ if (nCol > 0 && nCol <= mpDoc->MaxCol())
+ aColumn = nCol - 1;
+ return std::make_shared<sc::DeleteRowTransformation>(aColumn, mxFindString->get_text());
+}
+
+class ScSwapRowsTransformation : public ScDataTransformationBaseControl
+{
+private:
+ std::unique_ptr<weld::Entry> mxRow;
+ std::unique_ptr<weld::Entry> nxRow;
+ std::unique_ptr<weld::Button> mxDelete;
+ std::function<void(sal_uInt32&)> maDeleteTransformation;
+ const ScDocument* mpDoc;
+
+public:
+ ScSwapRowsTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);
+
+ virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
+ DECL_LINK(DeleteHdl, weld::Button&, void);
+};
+
+ScSwapRowsTransformation::ScSwapRowsTransformation(
+ const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex,
+ std::function<void(sal_uInt32&)> aDeleteTransformation)
+ : ScDataTransformationBaseControl(pParent, "modules/scalc/ui/swaprowsentry.ui", nIndex)
+ , mxRow(mxBuilder->weld_entry("ed_row1"))
+ , nxRow(mxBuilder->weld_entry("ed_row2"))
+ , mxDelete(mxBuilder->weld_button("ed_delete"))
+ , maDeleteTransformation(std::move(aDeleteTransformation))
+ , mpDoc(pDoc)
+{
+ mxDelete->connect_clicked(LINK(this, ScSwapRowsTransformation, DeleteHdl));
+}
+
+std::shared_ptr<sc::DataTransformation> ScSwapRowsTransformation::getTransformation()
+{
+ OUString aRowStr = mxRow->get_text();
+ OUString bRowStr = nxRow->get_text();
+ SCROW aRow = -1;
+ SCROW bRow = -1;
+ sal_Int32 mRow = aRowStr.toInt32();
+ sal_Int32 nRow = bRowStr.toInt32();
+ if (mRow > 0 && mRow <= mpDoc->MaxRow())
+ aRow = mRow - 1;
+ if (nRow > 0 && nRow <= mpDoc->MaxRow())
+ bRow = nRow - 1;
+ return std::make_shared<sc::SwapRowsTransformation>(aRow, bRow);
+}
+
+}
+
+ScDataProviderDlg::ScDataProviderDlg(weld::Window* pParent, std::shared_ptr<ScDocument> pDoc,
+ const ScDocument* pDocument)
+ : GenericDialogController(pParent, "modules/scalc/ui/dataproviderdlg.ui", "dataproviderdlg")
+ , mxDoc(std::move(pDoc))
+ , mxBox(m_xBuilder->weld_container("data_table"))
+ , m_xTableParent(mxBox->CreateChildFrame())
+ , mxTable(VclPtr<ScDataTableView>::Create(m_xTableParent, mxDoc))
+ , mxDBRanges(m_xBuilder->weld_combo_box("select_db_range"))
+ , mxOKBtn(m_xBuilder->weld_button("okay"))
+ , mxCancelBtn(m_xBuilder->weld_button("cancel"))
+ , mxAddTransformationBtn(m_xBuilder->weld_button("add_transformation"))
+ , mxScroll(m_xBuilder->weld_scrolled_window("scroll"))
+ , mxTransformationList(m_xBuilder->weld_container("transformation_ctrl"))
+ , mxTransformationBox(m_xBuilder->weld_combo_box("transformation_box"))
+ , mxProviderList(m_xBuilder->weld_combo_box("provider_lst"))
+ , mxEditURL(m_xBuilder->weld_entry("ed_url"))
+ , mxEditID(m_xBuilder->weld_entry("ed_id"))
+ , mxApplyBtn(m_xBuilder->weld_button("apply"))
+ , mxBrowseBtn(m_xBuilder->weld_button("browse"))
+ , maIdle("ScDataProviderDlg maIdle")
+ , mnIndex(0)
+{
+ Size aPrefSize = mxTable->GetOptimalSize();
+ mxBox->set_size_request(aPrefSize.Width(), aPrefSize.Height());
+ mxTable->Show();
+
+ ScDBCollection* pDBCollection = pDocument->GetDBCollection();
+ auto& rNamedDBs = pDBCollection->getNamedDBs();
+ for (auto& rNamedDB : rNamedDBs)
+ {
+ mxDBRanges->append_text(rNamedDB->GetName());
+ }
+
+ for (const auto& i : aTransformationData)
+ {
+ mxTransformationBox->append_text(OUString::createFromAscii(i.aTransformationName));
+ }
+
+ pDBData = new ScDBData("data", 0, 0, 0, mxDoc->MaxCol(), mxDoc->MaxRow());
+ bool bSuccess = mxDoc->GetDBCollection()->getNamedDBs().insert(std::unique_ptr<ScDBData>(pDBData));
+ SAL_WARN_IF(!bSuccess, "sc", "temporary warning");
+
+ auto aDataProvider = sc::DataProviderFactory::getDataProviders();
+ for (const auto& rDataProvider : aDataProvider)
+ {
+ mxProviderList->append_text(rDataProvider);
+ }
+
+ mxOKBtn->connect_clicked(LINK(this, ScDataProviderDlg, ApplyQuitHdl));
+ mxCancelBtn->connect_clicked(LINK(this, ScDataProviderDlg, CancelQuitHdl));
+ mxAddTransformationBtn->connect_clicked(LINK(this, ScDataProviderDlg, TransformationListHdl));
+ mxApplyBtn->connect_clicked(LINK(this, ScDataProviderDlg, ApplyBtnHdl));
+ mxBrowseBtn->connect_clicked(LINK(this, ScDataProviderDlg, BrowseBtnHdl));
+ mxTransformationBox->connect_changed(LINK(this, ScDataProviderDlg, TransformationSelectHdl));
+ mxProviderList->connect_changed(LINK(this, ScDataProviderDlg, ProviderSelectHdl));
+ mxEditID->connect_changed(LINK(this, ScDataProviderDlg, IDEditHdl));
+ mxEditURL->connect_changed(LINK(this, ScDataProviderDlg, URLEditHdl));
+
+ msApplyTooltip = mxApplyBtn->get_tooltip_text();
+ msAddTransformationToolTip = mxAddTransformationBtn->get_tooltip_text();
+ mxAddTransformationBtn->set_sensitive(false);
+ mxAddTransformationBtn->set_tooltip_text(OUString());
+ isValid();
+
+ maIdle.SetPriority( TaskPriority::LOWEST );
+ maIdle.SetInvokeHandler( LINK( this, ScDataProviderDlg, ScrollToEnd) );
+}
+
+ScDataProviderDlg::~ScDataProviderDlg()
+{
+ mxTable.disposeAndClear();
+ m_xTableParent->dispose();
+ m_xTableParent.clear();
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, ScrollToEnd, Timer*, void)
+{
+ mxScroll->vadjustment_set_value(mxScroll->vadjustment_get_upper());
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, ApplyQuitHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, CancelQuitHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, TransformationListHdl, weld::Button&, void)
+{
+ OUString transformation_string = mxTransformationBox->get_active_text();
+ for (auto& i: aTransformationData)
+ {
+ if (transformation_string == OUString::createFromAscii(i.aTransformationName))
+ {
+ i.maCallback(this);
+ maIdle.Start();
+ return;
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, ProviderSelectHdl, weld::ComboBox&, void)
+{
+ isValid();
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, IDEditHdl, weld::Entry&, void)
+{
+ isValid();
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, URLEditHdl, weld::Entry&, void)
+{
+ isValid();
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, ApplyBtnHdl, weld::Button&, void)
+{
+ updateApplyBtn(true);
+ import(*mxDoc, true);
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, BrowseBtnHdl, weld::Button&, void)
+{
+ sfx2::FileDialogHelper aFileDialog(0, FileDialogFlags::NONE, m_xDialog.get());
+ aFileDialog.SetContext(sfx2::FileDialogHelper::CalcDataProvider);
+ if (aFileDialog.Execute() != ERRCODE_NONE)
+ return;
+
+ mxEditURL->set_text(aFileDialog.GetPath());
+ isValid();
+}
+
+IMPL_LINK_NOARG(ScDataProviderDlg, TransformationSelectHdl, weld::ComboBox&, void)
+{
+ mxAddTransformationBtn->set_sensitive(true);
+ mxAddTransformationBtn->set_tooltip_text(msAddTransformationToolTip);
+}
+
+sc::ExternalDataSource ScDataProviderDlg::getDataSource(ScDocument* pDoc)
+{
+ sc::ExternalDataSource aSource(mxEditURL->get_text(), mxProviderList->get_active_text(), pDoc);
+
+ OUString aID = mxEditID->get_text();
+ aSource.setID(aID);
+ return aSource;
+}
+
+void ScDataProviderDlg::isValid()
+{
+ bool bValid = !mxProviderList->get_active_text().isEmpty();
+ bValid &= !mxEditURL->get_text().isEmpty();
+ updateApplyBtn(bValid);
+}
+
+void ScDataProviderDlg::updateApplyBtn(bool bValidConfig)
+{
+ if (!bValidConfig)
+ {
+ mxApplyBtn->set_sensitive(false);
+ mxApplyBtn->set_tooltip_text(OUString());
+ return;
+ }
+
+ mxApplyBtn->set_sensitive(true);
+ mxApplyBtn->set_tooltip_text(msApplyTooltip);
+}
+
+void ScDataProviderDlg::deleteColumn()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScDeleteColumnTransformationControl>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::splitColumn()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScSplitColumnTransformationControl>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::mergeColumns()
+{
+ SCCOL nStartCol = -1;
+ SCCOL nEndCol = -1;
+ mxTable->getColRange(nStartCol, nEndCol);
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScMergeColumnTransformationControl>(mxDoc.get(), mxTransformationList.get(), nStartCol, nEndCol, mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::textTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScColumnTextTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::sortTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScSortTransformationControl>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::aggregateFunction()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScAggregateFunction>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::numberTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScNumberTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::replaceNullTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScReplaceNullTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::dateTimeTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList,this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScDateTimeTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::findReplaceTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList, this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScFindReplaceTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::deleteRowTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList, this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScDeleteRowTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+void ScDataProviderDlg::swapRowsTransformation()
+{
+ std::function<void(sal_uInt32&)> adeleteTransformation = std::bind(&ScDataProviderDlg::deletefromList, this, std::placeholders::_1);
+ maControls.emplace_back(std::make_unique<ScSwapRowsTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
+}
+
+namespace {
+
+bool hasDBName(const OUString& rName, ScDBCollection* pDBCollection)
+{
+ if (pDBCollection->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rName)))
+ return true;
+
+ return false;
+}
+
+}
+
+void ScDataProviderDlg::import(ScDocument& rDoc, bool bInternal)
+{
+ sc::ExternalDataSource aSource = getDataSource(&rDoc);
+
+ for (size_t i = 0; i < maControls.size(); ++i)
+ {
+ ScDataTransformationBaseControl* pTransformationCtrl = maControls[i].get();
+ aSource.AddDataTransformation(pTransformationCtrl->getTransformation());
+ }
+ if (bInternal)
+ aSource.setDBData(pDBData->GetName());
+ else
+ {
+ aSource.setDBData(mxDBRanges->get_active_text());
+ if (!hasDBName(aSource.getDBName(), rDoc.GetDBCollection()))
+ return;
+ rDoc.GetExternalDataMapper().insertDataSource(aSource);
+ }
+ aSource.refresh(&rDoc, true);
+ mxTable->Invalidate();
+}
+
+void ScDataProviderDlg::deletefromList(sal_uInt32 nIndex)
+{
+ auto itr = maControls.erase(maControls.begin() + nIndex);
+ while (itr != maControls.end())
+ {
+ (*itr)->updateIndex(nIndex++);
+ ++itr;
+ }
+ --mnIndex;
+}
+
+IMPL_LINK_NOARG(ScDeleteColumnTransformationControl, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScSplitColumnTransformationControl, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScMergeColumnTransformationControl, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScNumberTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScAggregateFunction, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScSortTransformationControl, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScColumnTextTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScReplaceNullTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScDateTimeTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScFindReplaceTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScDeleteRowTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+
+IMPL_LINK_NOARG(ScSwapRowsTransformation, DeleteHdl, weld::Button&, void)
+{
+ maDeleteTransformation(mnIndex);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/datastreamdlg.cxx b/sc/source/ui/miscdlgs/datastreamdlg.cxx
new file mode 100644
index 0000000000..1876665dab
--- /dev/null
+++ b/sc/source/ui/miscdlgs/datastreamdlg.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/.
+ */
+
+#include <datastreamdlg.hxx>
+
+#include <sfx2/filedlghelper.hxx>
+#include <svtools/inettbc.hxx>
+#include <address.hxx>
+#include <docsh.hxx>
+#include <datastream.hxx>
+
+namespace sc
+{
+DataStreamDlg::DataStreamDlg(ScDocShell* pDocShell, weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/datastreams.ui", "DataStreamDialog")
+ , m_pDocShell(pDocShell)
+ , m_xCbUrl(new SvtURLBox(m_xBuilder->weld_combo_box("url")))
+ , m_xBtnBrowse(m_xBuilder->weld_button("browse"))
+ , m_xRBValuesInLine(m_xBuilder->weld_radio_button("valuesinline"))
+ , m_xRBAddressValue(m_xBuilder->weld_radio_button("addressvalue"))
+ , m_xCBRefreshOnEmpty(m_xBuilder->weld_check_button("refresh_ui"))
+ , m_xRBDataDown(m_xBuilder->weld_radio_button("datadown"))
+ , m_xRBRangeDown(m_xBuilder->weld_radio_button("rangedown"))
+ , m_xRBNoMove(m_xBuilder->weld_radio_button("nomove"))
+ , m_xRBMaxLimit(m_xBuilder->weld_radio_button("maxlimit"))
+ , m_xRBUnlimited(m_xBuilder->weld_radio_button("unlimited"))
+ , m_xEdRange(m_xBuilder->weld_entry("range"))
+ , m_xEdLimit(m_xBuilder->weld_entry("limit"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xVclFrameLimit(m_xBuilder->weld_frame("framelimit"))
+ , m_xVclFrameMove(m_xBuilder->weld_frame("framemove"))
+{
+ m_xCbUrl->connect_changed(LINK(this, DataStreamDlg, UpdateComboBoxHdl));
+ m_xRBAddressValue->connect_toggled(LINK(this, DataStreamDlg, UpdateClickHdl));
+ m_xRBAddressValue->set_sensitive(false);
+ m_xRBNoMove->hide();
+ m_xRBValuesInLine->connect_toggled(LINK(this, DataStreamDlg, UpdateClickHdl));
+ m_xEdRange->connect_changed(LINK(this, DataStreamDlg, UpdateHdl));
+ m_xBtnBrowse->connect_clicked(LINK(this, DataStreamDlg, BrowseHdl));
+ UpdateEnable();
+}
+
+DataStreamDlg::~DataStreamDlg() {}
+
+IMPL_LINK_NOARG(DataStreamDlg, BrowseHdl, weld::Button&, void)
+{
+ sfx2::FileDialogHelper aFileDialog(0, FileDialogFlags::NONE, m_xDialog.get());
+ aFileDialog.SetContext(sfx2::FileDialogHelper::CalcDataStream);
+ if (aFileDialog.Execute() != ERRCODE_NONE)
+ return;
+
+ m_xCbUrl->set_entry_text(aFileDialog.GetPath());
+ UpdateEnable();
+}
+
+IMPL_LINK_NOARG(DataStreamDlg, UpdateClickHdl, weld::Toggleable&, void) { UpdateEnable(); }
+
+IMPL_LINK_NOARG(DataStreamDlg, UpdateComboBoxHdl, weld::ComboBox&, void) { UpdateEnable(); }
+
+IMPL_LINK_NOARG(DataStreamDlg, UpdateHdl, weld::Entry&, void) { UpdateEnable(); }
+
+void DataStreamDlg::UpdateEnable()
+{
+ bool bOk = !m_xCbUrl->GetURL().isEmpty();
+ if (m_xRBAddressValue->get_active())
+ {
+ m_xVclFrameLimit->set_sensitive(false);
+ m_xVclFrameMove->set_sensitive(false);
+ m_xEdRange->set_sensitive(false);
+ }
+ else
+ {
+ m_xVclFrameLimit->set_sensitive(true);
+ m_xVclFrameMove->set_sensitive(true);
+ m_xEdRange->set_sensitive(true);
+ if (bOk)
+ {
+ // Check the given range to make sure it's valid.
+ ScRange aTest = GetStartRange();
+ if (!aTest.IsValid())
+ bOk = false;
+ }
+ }
+ m_xBtnOk->set_sensitive(bOk);
+ // setOptimalLayoutSize();
+}
+
+ScRange DataStreamDlg::GetStartRange()
+{
+ OUString aStr = m_xEdRange->get_text();
+ ScDocument& rDoc = m_pDocShell->GetDocument();
+ ScRange aRange;
+ ScRefFlags nRes = aRange.Parse(aStr, rDoc, rDoc.GetAddressConvention());
+ if (((nRes & ScRefFlags::VALID) == ScRefFlags::ZERO) || !aRange.IsValid())
+ {
+ // Invalid range.
+ aRange.SetInvalid();
+ return aRange;
+ }
+
+ // Make sure it's only one row tall.
+ if (aRange.aStart.Row() != aRange.aEnd.Row())
+ aRange.SetInvalid();
+
+ return aRange;
+}
+
+void DataStreamDlg::Init(const DataStream& rStrm)
+{
+ m_xCbUrl->set_entry_text(rStrm.GetURL());
+ ScDocument& rDoc = m_pDocShell->GetDocument();
+
+ ScRange aRange = rStrm.GetRange();
+ ScRange aTopRange = aRange;
+ aTopRange.aEnd.SetRow(aTopRange.aStart.Row());
+ OUString aStr = aTopRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention());
+ m_xEdRange->set_text(aStr);
+ SCROW nRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
+
+ if (aRange.aEnd.Row() == rDoc.MaxRow())
+ m_xRBUnlimited->set_active(true);
+ else
+ {
+ m_xRBMaxLimit->set_active(true);
+ m_xEdLimit->set_text(OUString::number(nRows));
+ }
+
+ DataStream::MoveType eMove = rStrm.GetMove();
+ switch (eMove)
+ {
+ case DataStream::MOVE_DOWN:
+ m_xRBDataDown->set_active(true);
+ break;
+ case DataStream::RANGE_DOWN:
+ m_xRBRangeDown->set_active(true);
+ break;
+ case DataStream::MOVE_UP:
+ case DataStream::NO_MOVE:
+ default:;
+ }
+
+ m_xCBRefreshOnEmpty->set_active(rStrm.IsRefreshOnEmptyLine());
+
+ UpdateEnable();
+}
+
+void DataStreamDlg::StartStream()
+{
+ ScRange aStartRange = GetStartRange();
+ if (!aStartRange.IsValid())
+ // Don't start the stream without a valid range.
+ return;
+
+ sal_Int32 nLimit = 0;
+ if (m_xRBMaxLimit->get_active())
+ nLimit = m_xEdLimit->get_text().toInt32();
+ OUString rURL = m_xCbUrl->get_active_text();
+
+ DataStream::MoveType eMove
+ = m_xRBRangeDown->get_active() ? DataStream::RANGE_DOWN : DataStream::MOVE_DOWN;
+
+ DataStream* pStream = DataStream::Set(m_pDocShell, rURL, aStartRange, nLimit, eMove);
+ pStream->SetRefreshOnEmptyLine(m_xCBRefreshOnEmpty->get_active());
+ DataStream::MakeToolbarVisible();
+ pStream->StartImport();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/datatableview.cxx b/sc/source/ui/miscdlgs/datatableview.cxx
new file mode 100644
index 0000000000..649f85bfc1
--- /dev/null
+++ b/sc/source/ui/miscdlgs/datatableview.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 <datatableview.hxx>
+
+#include <document.hxx>
+#include <utility>
+#include <viewdata.hxx>
+#include <output.hxx>
+#include <fillinfo.hxx>
+#include <table.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/seleng.hxx>
+#include <sal/log.hxx>
+
+constexpr double nPPTX = 0.06666;
+constexpr double nPPTY = 0.06666;
+
+constexpr sal_uInt16 nRowHeaderWidth = 100;
+constexpr sal_uInt16 nColHeaderHeight = 20;
+
+ScDataTableColView::ScDataTableColView(vcl::Window* pParent, ScDocument* pDoc, SelectionEngine* pSelectionEngine):
+ ScHeaderControl(pParent, pSelectionEngine, pDoc->MaxCol()+1, false, nullptr),
+ mpDoc(pDoc),
+ mnCol(0)
+{
+}
+
+void ScDataTableColView::SetPos(SCCOLROW nCol)
+{
+ mnCol = nCol;
+}
+
+SCCOLROW ScDataTableColView::GetPos() const
+{
+ return mnCol;
+}
+
+sal_uInt16 ScDataTableColView::GetEntrySize(SCCOLROW nPos) const
+{
+ return ScViewData::ToPixel(mpDoc->GetColWidth(nPos, 0), nPPTX);
+}
+
+OUString ScDataTableColView::GetEntryText(SCCOLROW nPos) const
+{
+ return "Col: " + OUString::number(nPos + 1);
+}
+
+bool ScDataTableColView::IsLayoutRTL() const
+{
+ return false;
+}
+
+void ScDataTableColView::SetEntrySize(SCCOLROW nPos, sal_uInt16 nColWidth)
+{
+ mpDoc->SetColWidthOnly(nPos, 0, nColWidth/nPPTX);
+}
+
+void ScDataTableColView::HideEntries(SCCOLROW nPos, SCCOLROW nEndPos)
+{
+ for (SCCOLROW nCol = nPos; nCol <= nEndPos; ++nCol)
+ {
+ mpDoc->ShowCol(nCol, 0, false);
+ }
+}
+
+
+ScDataTableRowView::ScDataTableRowView(vcl::Window* pParent, ScDocument* pDoc, SelectionEngine* pSelectionEngine):
+ ScHeaderControl(pParent, pSelectionEngine, pDoc->MaxRow()+1, true, nullptr),
+ mpDoc(pDoc),
+ mnRow(0)
+{
+}
+
+void ScDataTableRowView::SetPos(SCCOLROW nRow)
+{
+ mnRow = nRow;
+}
+
+SCCOLROW ScDataTableRowView::GetPos() const
+{
+ return mnRow;
+}
+
+sal_uInt16 ScDataTableRowView::GetEntrySize(SCCOLROW nPos) const
+{
+ return ScViewData::ToPixel(mpDoc->GetRowHeight(nPos, SCTAB(0), true), nPPTX);
+}
+
+OUString ScDataTableRowView::GetEntryText(SCCOLROW nPos) const
+{
+ return OUString::number(nPos + 1);
+}
+
+bool ScDataTableRowView::IsLayoutRTL() const
+{
+ return false;
+}
+
+void ScDataTableRowView::SetEntrySize(SCCOLROW nPos, sal_uInt16 nColWidth)
+{
+ mpDoc->SetRowHeight(nPos, 0, nColWidth/nPPTX);
+}
+
+void ScDataTableRowView::HideEntries(SCCOLROW nPos, SCCOLROW nEndPos)
+{
+ for (SCCOLROW nCol = nPos; nCol <= nEndPos; ++nCol)
+ {
+ mpDoc->ShowRow(nCol, 0, false);
+ }
+}
+
+ScDataTableView::ScDataTableView(const css::uno::Reference<css::awt::XWindow> &rParent, std::shared_ptr<ScDocument> pDoc) :
+ Control(VCLUnoHelper::GetWindow(rParent)),
+ mpDoc(std::move(pDoc)),
+ mpSelectionEngine(new SelectionEngine(this)),
+ mpColView(VclPtr<ScDataTableColView>::Create(this, mpDoc.get(), mpSelectionEngine.get())),
+ mpRowView(VclPtr<ScDataTableRowView>::Create(this, mpDoc.get(), mpSelectionEngine.get())),
+ mpVScroll(VclPtr<ScrollAdaptor>::Create(this, false)),
+ mpHScroll(VclPtr<ScrollAdaptor>::Create(this, true)),
+ mnScrollBarSize(mpVScroll->GetSizePixel().Width()),
+ mnFirstVisibleRow(0),
+ mnFirstVisibleCol(0)
+{
+ mpColView->setPosSizePixel(nRowHeaderWidth, 0, nRowHeaderWidth, nColHeaderHeight);
+ mpRowView->setPosSizePixel(0, nColHeaderHeight, nRowHeaderWidth, nColHeaderHeight);
+
+ mpVScroll->SetRangeMin(0);
+ mpVScroll->SetRangeMax(100);
+ mpVScroll->SetScrollHdl(LINK(this, ScDataTableView, VertScrollHdl));
+
+ mpHScroll->SetRangeMin(0);
+ mpHScroll->SetRangeMax(50);
+ mpHScroll->SetScrollHdl(LINK(this, ScDataTableView, HorzScrollHdl));
+
+ mpColView->Show();
+ mpRowView->Show();
+ mpVScroll->Show();
+ mpHScroll->Show();
+}
+
+ScDataTableView::~ScDataTableView()
+{
+ disposeOnce();
+}
+
+void ScDataTableView::dispose()
+{
+ mpColView.disposeAndClear();
+ mpRowView.disposeAndClear();
+ mpVScroll.disposeAndClear();
+ mpHScroll.disposeAndClear();
+ Control::dispose();
+}
+
+void ScDataTableView::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (!rMEvt.IsLeft())
+ return;
+
+ mpMouseEvent.reset(new MouseEvent(rMEvt));
+}
+
+namespace {
+
+SCCOL findColFromPos(sal_uInt16 nPixelPos, const ScDocument* pDoc, SCCOL nStartCol = 0)
+{
+ nPixelPos -= nRowHeaderWidth;
+ sal_uInt32 nPixelLength = 0;
+ for (SCCOL nCol : pDoc->GetColumnsRange(0, nStartCol, pDoc->MaxCol()))
+ {
+ sal_uInt16 nColWidth = pDoc->GetColWidth(nCol, 0, true);
+ sal_uInt32 nPixel = ScViewData::ToPixel(nColWidth, nPPTX);
+ nPixelLength += nPixel;
+
+ if (nPixelLength >= nPixelPos)
+ {
+ return nCol;
+ }
+ }
+
+ SAL_WARN("sc", "Could not find the corresponding column");
+ return pDoc->MaxCol();
+}
+
+SCROW findRowFromPos(sal_uInt16 nPixelPos, const ScDocument* pDoc, SCROW nStartRow = 0)
+{
+ nPixelPos -= nColHeaderHeight;
+ sal_uInt32 nPixelLength = 0;
+ for (SCROW nRow = nStartRow; nRow <= pDoc->MaxRow(); ++nRow)
+ {
+ sal_uInt16 nColWidth = pDoc->GetRowHeight(nRow, SCTAB(0), true);
+ sal_uInt32 nPixel = ScViewData::ToPixel(nColWidth, nPPTX);
+ nPixelLength += nPixel;
+
+ if (nPixelLength >= nPixelPos)
+ {
+ return nRow;
+ }
+ }
+
+ SAL_WARN("sc", "Could not find the corresponding row");
+ return pDoc->MaxRow();
+}
+
+}
+
+void ScDataTableView::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (!rMEvt.IsLeft())
+ return;
+ if (!mpMouseEvent) // tdf#120528 The event originated in another window, like context menu
+ return;
+
+ SCCOL nStartCol = findColFromPos(mpMouseEvent->GetPosPixel().getX(), mpDoc.get());
+ SCCOL nEndCol = findColFromPos(rMEvt.GetPosPixel().getX(), mpDoc.get());
+ SCROW nStartRow = findRowFromPos(mpMouseEvent->GetPosPixel().getY(), mpDoc.get());
+ SCROW nEndRow = findRowFromPos(rMEvt.GetPosPixel().getY(), mpDoc.get());
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ mpColView->SetMark(true, nStartCol, nEndCol);
+ mpRowView->SetMark(true, nStartRow, nEndRow);
+
+ mpMouseEvent.reset();
+}
+
+void ScDataTableView::Resize()
+{
+ Size aSize = GetSizePixel();
+ mpColView->setPosSizePixel(nRowHeaderWidth, 0, aSize.Width() - mnScrollBarSize, nColHeaderHeight);
+ mpRowView->setPosSizePixel(0, nColHeaderHeight, nRowHeaderWidth, aSize.Height());
+
+ mpVScroll->setPosSizePixel(aSize.Width() - mnScrollBarSize, nColHeaderHeight, mnScrollBarSize, aSize.Height() - nColHeaderHeight - mnScrollBarSize);
+ mpHScroll->setPosSizePixel(nRowHeaderWidth, aSize.Height() - mnScrollBarSize, aSize.Width() - nRowHeaderWidth - mnScrollBarSize, mnScrollBarSize);
+}
+
+void ScDataTableView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
+{
+ Size aSize = GetSizePixel();
+ SCCOL nMaxVisibleCol = findColFromPos(aSize.Width() - mnScrollBarSize, mpDoc.get(), mnFirstVisibleCol);
+ SCROW nMaxVisibleRow = findRowFromPos(aSize.Height(), mpDoc.get(), mnFirstVisibleRow);
+
+ ScTableInfo aTableInfo;
+ mpDoc->FillInfo(aTableInfo, mnFirstVisibleCol, mnFirstVisibleRow, nMaxVisibleCol, nMaxVisibleRow, 0, 0.06666, 0.06666, false, false);
+ ScOutputData aOutput(&rRenderContext, OUTTYPE_WINDOW, aTableInfo, mpDoc.get(), 0,
+ nRowHeaderWidth, nColHeaderHeight, mnFirstVisibleCol, mnFirstVisibleRow, nMaxVisibleCol, nMaxVisibleRow, nPPTX, nPPTY);
+
+ aOutput.SetGridColor(COL_BLACK);
+ aOutput.SetSolidBackground(true);
+ aOutput.DrawClear();
+ aOutput.DrawDocumentBackground();
+ aOutput.DrawGrid(rRenderContext, true, false);
+ aOutput.DrawStrings();
+
+ Color aFaceColor(rRenderContext.GetSettings().GetStyleSettings().GetFaceColor());
+ rRenderContext.SetLineColor(aFaceColor);
+ rRenderContext.SetFillColor(aFaceColor);
+ rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), Size(nRowHeaderWidth, nColHeaderHeight)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(aSize.Width() - mnScrollBarSize, aSize.Height() - mnScrollBarSize), Size(mnScrollBarSize, mnScrollBarSize)));
+
+ Control::Paint(rRenderContext, rRectangle);
+}
+
+Size ScDataTableView::GetOptimalSize() const
+{
+ return Size(450, 400);
+}
+
+void ScDataTableView::getColRange(SCCOL& rStartCol, SCCOL& rEndCol) const
+{
+ SCCOLROW aStart = 0;
+ SCCOLROW aEnd = 0;
+ mpColView->GetMarkRange(aStart, aEnd);
+ rStartCol = static_cast<SCCOL>(aStart);
+ rEndCol = static_cast<SCCOL>(aEnd);
+}
+
+void ScDataTableView::getRowRange(SCROW& rStartCol, SCROW& rEndCol) const
+{
+ SCCOLROW aStart = 0;
+ SCCOLROW aEnd = 0;
+ mpRowView->GetMarkRange(aStart, aEnd);
+ rStartCol = static_cast<SCROW>(aStart);
+ rEndCol = static_cast<SCROW>(aEnd);
+}
+
+IMPL_LINK_NOARG(ScDataTableView, VertScrollHdl, weld::Scrollbar&, void)
+{
+ mnFirstVisibleRow = mpVScroll->GetThumbPos();
+ mpVScroll->SetRangeMax(std::min(mpDoc->MaxRow(), static_cast<SCROW>(mnFirstVisibleRow + 100)));
+ mpRowView->SetPos(mnFirstVisibleRow);
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(ScDataTableView, HorzScrollHdl, weld::Scrollbar&, void)
+{
+ mnFirstVisibleCol = mpHScroll->GetThumbPos();
+ mpHScroll->SetRangeMax(std::min(mpDoc->MaxCol(), static_cast<SCCOL>(mnFirstVisibleCol + 50)));
+ mpColView->SetPos(mnFirstVisibleCol);
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/delcldlg.cxx b/sc/source/ui/miscdlgs/delcldlg.cxx
new file mode 100644
index 0000000000..71ce6b99ed
--- /dev/null
+++ b/sc/source/ui/miscdlgs/delcldlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <delcldlg.hxx>
+
+static sal_uInt8 nDelItemChecked = 0;
+
+ScDeleteCellDlg::ScDeleteCellDlg(weld::Window* pParent, bool bDisallowCellMove)
+ : GenericDialogController(pParent, "modules/scalc/ui/deletecells.ui", "DeleteCellsDialog")
+ , m_xBtnCellsUp(m_xBuilder->weld_radio_button("up"))
+ , m_xBtnCellsLeft(m_xBuilder->weld_radio_button("left"))
+ , m_xBtnDelRows(m_xBuilder->weld_radio_button("rows"))
+ , m_xBtnDelCols(m_xBuilder->weld_radio_button("cols"))
+{
+ if (bDisallowCellMove)
+ {
+ m_xBtnCellsUp->set_sensitive(false);
+ m_xBtnCellsLeft->set_sensitive(false);
+
+ switch (nDelItemChecked)
+ {
+ case 2:
+ m_xBtnDelRows->set_active(true);
+ break;
+ case 3:
+ m_xBtnDelCols->set_active(true);
+ break;
+ default:
+ m_xBtnDelRows->set_active(true);
+ break;
+ }
+ }
+ else
+ {
+ switch (nDelItemChecked)
+ {
+ case 0:
+ m_xBtnCellsUp->set_active(true);
+ break;
+ case 1:
+ m_xBtnCellsLeft->set_active(true);
+ break;
+ case 2:
+ m_xBtnDelRows->set_active(true);
+ break;
+ case 3:
+ m_xBtnDelCols->set_active(true);
+ break;
+ }
+ }
+}
+
+ScDeleteCellDlg::~ScDeleteCellDlg() {}
+
+DelCellCmd ScDeleteCellDlg::GetDelCellCmd() const
+{
+ DelCellCmd nReturn = DelCellCmd::NONE;
+
+ if (m_xBtnCellsUp->get_active())
+ {
+ nDelItemChecked = 0;
+ nReturn = DelCellCmd::CellsUp;
+ }
+ else if (m_xBtnCellsLeft->get_active())
+ {
+ nDelItemChecked = 1;
+ nReturn = DelCellCmd::CellsLeft;
+ }
+ else if (m_xBtnDelRows->get_active())
+ {
+ nDelItemChecked = 2;
+ nReturn = DelCellCmd::Rows;
+ }
+ else if (m_xBtnDelCols->get_active())
+ {
+ nDelItemChecked = 3;
+ nReturn = DelCellCmd::Cols;
+ }
+
+ return nReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/delcodlg.cxx b/sc/source/ui/miscdlgs/delcodlg.cxx
new file mode 100644
index 0000000000..56334ba9ca
--- /dev/null
+++ b/sc/source/ui/miscdlgs/delcodlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <delcodlg.hxx>
+
+bool ScDeleteContentsDlg::bPreviousAllCheck = false;
+InsertDeleteFlags ScDeleteContentsDlg::nPreviousChecks = InsertDeleteFlags::DATETIME | InsertDeleteFlags::STRING |
+ InsertDeleteFlags::NOTE | InsertDeleteFlags::FORMULA |
+ InsertDeleteFlags::VALUE;
+
+ScDeleteContentsDlg::ScDeleteContentsDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/deletecontents.ui", "DeleteContentsDialog")
+ , m_bObjectsDisabled(false)
+ , m_xBtnDelAll(m_xBuilder->weld_check_button("deleteall"))
+ , m_xBtnDelStrings(m_xBuilder->weld_check_button("text"))
+ , m_xBtnDelNumbers(m_xBuilder->weld_check_button("numbers"))
+ , m_xBtnDelDateTime(m_xBuilder->weld_check_button("datetime"))
+ , m_xBtnDelFormulas(m_xBuilder->weld_check_button("formulas"))
+ , m_xBtnDelNotes(m_xBuilder->weld_check_button("comments"))
+ , m_xBtnDelAttrs(m_xBuilder->weld_check_button("formats"))
+ , m_xBtnDelObjects(m_xBuilder->weld_check_button("objects"))
+{
+ m_xBtnDelAll->set_active( ScDeleteContentsDlg::bPreviousAllCheck );
+ m_xBtnDelStrings->set_active( bool(InsertDeleteFlags::STRING & ScDeleteContentsDlg::nPreviousChecks) );
+ m_xBtnDelNumbers->set_active( bool(InsertDeleteFlags::VALUE & ScDeleteContentsDlg::nPreviousChecks) );
+ m_xBtnDelDateTime->set_active( bool(InsertDeleteFlags::DATETIME & ScDeleteContentsDlg::nPreviousChecks) );
+ m_xBtnDelFormulas->set_active( bool(InsertDeleteFlags::FORMULA & ScDeleteContentsDlg::nPreviousChecks) );
+ m_xBtnDelNotes->set_active( bool(InsertDeleteFlags::NOTE & ScDeleteContentsDlg::nPreviousChecks) );
+ m_xBtnDelAttrs->set_active( (InsertDeleteFlags::ATTRIB & ScDeleteContentsDlg::nPreviousChecks) == InsertDeleteFlags::ATTRIB );
+ m_xBtnDelObjects->set_active( bool(InsertDeleteFlags::OBJECTS & ScDeleteContentsDlg::nPreviousChecks) );
+
+ DisableChecks( m_xBtnDelAll->get_active() );
+
+ m_xBtnDelAll->connect_toggled( LINK( this, ScDeleteContentsDlg, DelAllHdl ) );
+}
+
+ScDeleteContentsDlg::~ScDeleteContentsDlg()
+{
+}
+
+InsertDeleteFlags ScDeleteContentsDlg::GetDelContentsCmdBits() const
+{
+ ScDeleteContentsDlg::nPreviousChecks = InsertDeleteFlags::NONE;
+
+ if ( m_xBtnDelStrings->get_active() )
+ ScDeleteContentsDlg::nPreviousChecks = InsertDeleteFlags::STRING;
+ if ( m_xBtnDelNumbers->get_active() )
+ ScDeleteContentsDlg::nPreviousChecks |= InsertDeleteFlags::VALUE;
+ if ( m_xBtnDelDateTime->get_active())
+ ScDeleteContentsDlg::nPreviousChecks |= InsertDeleteFlags::DATETIME;
+ if ( m_xBtnDelFormulas->get_active())
+ ScDeleteContentsDlg::nPreviousChecks |= InsertDeleteFlags::FORMULA;
+ if ( m_xBtnDelNotes->get_active() )
+ ScDeleteContentsDlg::nPreviousChecks |= InsertDeleteFlags::NOTE;
+ if ( m_xBtnDelAttrs->get_active() )
+ ScDeleteContentsDlg::nPreviousChecks |= InsertDeleteFlags::ATTRIB;
+ if ( m_xBtnDelObjects->get_active() )
+ ScDeleteContentsDlg::nPreviousChecks |= InsertDeleteFlags::OBJECTS;
+
+ ScDeleteContentsDlg::bPreviousAllCheck = m_xBtnDelAll->get_active();
+
+ return ( ScDeleteContentsDlg::bPreviousAllCheck
+ ? InsertDeleteFlags::ALL
+ : ScDeleteContentsDlg::nPreviousChecks );
+}
+
+void ScDeleteContentsDlg::DisableChecks( bool bDelAllChecked )
+{
+ if ( bDelAllChecked )
+ {
+ m_xBtnDelStrings->set_sensitive(false);
+ m_xBtnDelNumbers->set_sensitive(false);
+ m_xBtnDelDateTime->set_sensitive(false);
+ m_xBtnDelFormulas->set_sensitive(false);
+ m_xBtnDelNotes->set_sensitive(false);
+ m_xBtnDelAttrs->set_sensitive(false);
+ m_xBtnDelObjects->set_sensitive(false);
+ }
+ else
+ {
+ m_xBtnDelStrings->set_sensitive(true);
+ m_xBtnDelNumbers->set_sensitive(true);
+ m_xBtnDelDateTime->set_sensitive(true);
+ m_xBtnDelFormulas->set_sensitive(true);
+ m_xBtnDelNotes->set_sensitive(true);
+ m_xBtnDelAttrs->set_sensitive(true);
+ if (m_bObjectsDisabled)
+ m_xBtnDelObjects->set_sensitive(false);
+ else
+ m_xBtnDelObjects->set_sensitive(true);
+ }
+}
+
+void ScDeleteContentsDlg::DisableObjects()
+{
+ m_bObjectsDisabled = true;
+ m_xBtnDelObjects->set_active(false);
+ m_xBtnDelObjects->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScDeleteContentsDlg, DelAllHdl, weld::Toggleable&, void)
+{
+ DisableChecks( m_xBtnDelAll->get_active() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/filldlg.cxx b/sc/source/ui/miscdlgs/filldlg.cxx
new file mode 100644
index 0000000000..2210df0f8e
--- /dev/null
+++ b/sc/source/ui/miscdlgs/filldlg.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svl/numformat.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <scresid.hxx>
+#include <document.hxx>
+#include <strings.hrc>
+#include <filldlg.hxx>
+#include <scui_def.hxx>
+
+
+ScFillSeriesDlg::ScFillSeriesDlg( weld::Window* pParent,
+ ScDocument& rDocument,
+ FillDir eFillDir,
+ FillCmd eFillCmd,
+ FillDateCmd eFillDateCmd,
+ OUString aStartStr,
+ double fStep,
+ double fMax,
+ const SCSIZE nSelectHeight,
+ const SCSIZE nSelectWidth,
+ sal_uInt16 nPossDir )
+ : GenericDialogController(pParent, "modules/scalc/ui/filldlg.ui", "FillSeriesDialog")
+ , aStartStrVal(std::move(aStartStr))
+ , aErrMsgInvalidVal(ScResId(SCSTR_VALERR))
+ , rDoc(rDocument)
+ , theFillDir(eFillDir)
+ , theFillCmd(eFillCmd)
+ , theFillDateCmd(eFillDateCmd)
+ , fIncrement(fStep)
+ , fEndVal(fMax)
+ , m_nSelectHeight(nSelectHeight)
+ , m_nSelectWidth(nSelectWidth)
+ , m_xFtStartVal(m_xBuilder->weld_label("startL"))
+ , m_xEdStartVal(m_xBuilder->weld_entry("startValue"))
+ , m_xFtEndVal(m_xBuilder->weld_label("endL"))
+ , m_xEdEndVal(m_xBuilder->weld_entry("endValue"))
+ , m_xFtIncrement(m_xBuilder->weld_label("incrementL"))
+ , m_xEdIncrement(m_xBuilder->weld_entry("increment"))
+ , m_xBtnDown(m_xBuilder->weld_radio_button("down"))
+ , m_xBtnRight(m_xBuilder->weld_radio_button("right"))
+ , m_xBtnUp(m_xBuilder->weld_radio_button("up"))
+ , m_xBtnLeft(m_xBuilder->weld_radio_button("left"))
+ , m_xBtnArithmetic(m_xBuilder->weld_radio_button("linear"))
+ , m_xBtnGeometric(m_xBuilder->weld_radio_button("growth"))
+ , m_xBtnDate(m_xBuilder->weld_radio_button("date"))
+ , m_xBtnAutoFill(m_xBuilder->weld_radio_button("autofill"))
+ , m_xFtTimeUnit(m_xBuilder->weld_label("tuL"))
+ , m_xBtnDay(m_xBuilder->weld_radio_button("day"))
+ , m_xBtnDayOfWeek(m_xBuilder->weld_radio_button("week"))
+ , m_xBtnMonth(m_xBuilder->weld_radio_button("month"))
+ , m_xBtnYear(m_xBuilder->weld_radio_button("year"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+{
+ Init(nPossDir);
+}
+
+ScFillSeriesDlg::~ScFillSeriesDlg()
+{
+}
+
+void ScFillSeriesDlg::SetEdStartValEnabled(bool bFlag)
+{
+ if(bFlag)
+ {
+ m_xFtStartVal->set_sensitive(true);
+ m_xEdStartVal->set_sensitive(true);
+ }
+ else
+ {
+ m_xFtStartVal->set_sensitive(false);
+ m_xEdStartVal->set_sensitive(false);
+ }
+}
+
+void ScFillSeriesDlg::Init( sal_uInt16 nPossDir )
+{
+ m_xBtnOk->connect_clicked ( LINK( this, ScFillSeriesDlg, OKHdl ) );
+ m_xBtnArithmetic->connect_toggled ( LINK( this, ScFillSeriesDlg, DisableHdl ) );
+ m_xBtnGeometric->connect_toggled ( LINK( this, ScFillSeriesDlg, DisableHdl ) );
+ m_xBtnDate->connect_toggled ( LINK( this, ScFillSeriesDlg, DisableHdl ) );
+ m_xBtnAutoFill->connect_toggled ( LINK( this, ScFillSeriesDlg, DisableHdl ) );
+
+ if( nPossDir == FDS_OPT_NONE )
+ {
+ m_xBtnLeft->set_sensitive(false);
+ m_xBtnRight->set_sensitive(false);
+ m_xBtnDown->set_sensitive(false);
+ m_xBtnUp->set_sensitive(false);
+ }
+
+ if( nPossDir == FDS_OPT_HORZ )
+ {
+ m_xBtnDown->set_sensitive(false);
+ m_xBtnUp->set_sensitive(false);
+ }
+
+ if( nPossDir == FDS_OPT_VERT )
+ {
+ m_xBtnLeft->set_sensitive(false);
+ m_xBtnRight->set_sensitive(false);
+ }
+
+ switch ( theFillDir )
+ {
+ case FILL_TO_LEFT: m_xBtnLeft->set_active(true); break;
+ case FILL_TO_RIGHT: m_xBtnRight->set_active(true); break;
+ case FILL_TO_BOTTOM: m_xBtnDown->set_active(true); break;
+ case FILL_TO_TOP: m_xBtnUp->set_active(true); break;
+ default:
+ break;
+ }
+
+ switch ( theFillCmd )
+ {
+ case FILL_LINEAR:
+ m_xBtnArithmetic->set_active(true);
+ DisableHdl(*m_xBtnArithmetic);
+ break;
+ case FILL_GROWTH:
+ m_xBtnGeometric->set_active(true);
+ DisableHdl(*m_xBtnGeometric );
+ break;
+ case FILL_DATE:
+ m_xBtnDate->set_active(true);
+ DisableHdl(*m_xBtnDate);
+ break;
+ case FILL_AUTO:
+ m_xBtnAutoFill->set_active(true);
+ DisableHdl(*m_xBtnAutoFill);
+ break;
+ default:
+ break;
+ }
+
+ switch ( theFillDateCmd )
+ {
+ case FILL_DAY: m_xBtnDay->set_active(true); break;
+ case FILL_WEEKDAY: m_xBtnDayOfWeek->set_active(true); break;
+ case FILL_MONTH: m_xBtnMonth->set_active(true); break;
+ case FILL_YEAR: m_xBtnYear->set_active(true); break;
+ default:
+ break;
+ }
+
+ fStartVal = MAXDOUBLE;
+
+ m_xEdStartVal->set_text( aStartStrVal);
+
+ OUString aIncrTxt;
+ rDoc.GetFormatTable()->GetInputLineString( fIncrement, 0, aIncrTxt );
+ m_xEdIncrement->set_text( aIncrTxt );
+
+ OUString aEndTxt;
+ if ( fEndVal != MAXDOUBLE )
+ rDoc.GetFormatTable()->GetInputLineString( fEndVal, 0, aEndTxt );
+ m_xEdEndVal->set_text( aEndTxt );
+}
+
+weld::Entry* ScFillSeriesDlg::CheckValues()
+{
+ OUString aStartStr = m_xEdStartVal->get_text();
+ OUString aIncStr = m_xEdIncrement->get_text();
+ OUString aEndStr = m_xEdEndVal->get_text();
+ sal_uInt32 nKey = 0;
+
+ // If entry is filled, capture value before handling special cases.
+ if ( !aStartStr.isEmpty()
+ && theFillCmd != FILL_AUTO
+ && !rDoc.GetFormatTable()->IsNumberFormat( aStartStr, nKey, fStartVal ) )
+ return m_xEdStartVal.get();
+ if ( !aIncStr.isEmpty()
+ && !rDoc.GetFormatTable()->IsNumberFormat( aIncStr, nKey, fIncrement ) )
+ return m_xEdIncrement.get();
+ if ( !aEndStr.isEmpty()
+ && !rDoc.GetFormatTable()->IsNumberFormat( aEndStr, nKey, fEndVal ) )
+ return m_xEdEndVal.get();
+
+ if ( theFillCmd == FILL_LINEAR && !aEndStr.isEmpty()
+ && aStartStr.isEmpty() != aIncStr.isEmpty()
+ && ( ( m_nSelectHeight == 1 ) != ( m_nSelectWidth == 1 ) ) )
+ {
+ SCSIZE nStepAmount = ( theFillDir == FILL_TO_BOTTOM || theFillDir == FILL_TO_TOP ) ?
+ m_nSelectHeight - 1 : m_nSelectWidth - 1 ;
+ if ( aStartStr.isEmpty() )
+ fStartVal = fEndVal - fIncrement * nStepAmount;
+ if ( aIncStr.isEmpty() && nStepAmount != 0 )
+ fIncrement = (fEndVal - fStartVal) / nStepAmount;
+ }
+ else
+ {
+ if ( aStartStr.isEmpty() || m_xBtnAutoFill->get_active() )
+ fStartVal = MAXDOUBLE;
+ if ( aIncStr.isEmpty() )
+ return m_xEdIncrement.get();
+ if ( aEndStr.isEmpty() )
+ fEndVal = ( fIncrement < 0 ) ? -MAXDOUBLE : MAXDOUBLE;
+ }
+ return nullptr;
+}
+
+// Handler:
+IMPL_LINK(ScFillSeriesDlg, DisableHdl, weld::Toggleable&, rBtn, void)
+{
+ if (&rBtn == m_xBtnDate.get())
+ {
+ m_xBtnDay->set_sensitive(true);
+ m_xBtnDayOfWeek->set_sensitive(true);
+ m_xBtnMonth->set_sensitive(true);
+ m_xBtnYear->set_sensitive(true);
+ m_xFtTimeUnit->set_sensitive(true);
+ }
+ else
+ {
+ m_xBtnDay->set_sensitive(false);
+ m_xBtnDayOfWeek->set_sensitive(false);
+ m_xBtnMonth->set_sensitive(false);
+ m_xBtnYear->set_sensitive(false);
+ m_xFtTimeUnit->set_sensitive(false);
+ }
+
+ if (&rBtn != m_xBtnAutoFill.get())
+ {
+ m_xFtIncrement->set_sensitive(true);
+ m_xEdIncrement->set_sensitive(true);
+ m_xFtEndVal->set_sensitive(true);
+ m_xEdEndVal->set_sensitive(true);
+ }
+ else
+ {
+ m_xFtIncrement->set_sensitive(false);
+ m_xEdIncrement->set_sensitive(false);
+ m_xFtEndVal->set_sensitive(false);
+ m_xEdEndVal->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(ScFillSeriesDlg, OKHdl, weld::Button&, void)
+{
+ if ( m_xBtnLeft->get_active() ) theFillDir = FILL_TO_LEFT;
+ else if ( m_xBtnRight->get_active() ) theFillDir = FILL_TO_RIGHT;
+ else if ( m_xBtnDown->get_active() ) theFillDir = FILL_TO_BOTTOM;
+ else if ( m_xBtnUp->get_active() ) theFillDir = FILL_TO_TOP;
+
+ if ( m_xBtnArithmetic->get_active() ) theFillCmd = FILL_LINEAR;
+ else if ( m_xBtnGeometric->get_active() ) theFillCmd = FILL_GROWTH;
+ else if ( m_xBtnDate->get_active() ) theFillCmd = FILL_DATE;
+ else if ( m_xBtnAutoFill->get_active() ) theFillCmd = FILL_AUTO;
+
+ if ( m_xBtnDay->get_active() ) theFillDateCmd = FILL_DAY;
+ else if ( m_xBtnDayOfWeek->get_active() ) theFillDateCmd = FILL_WEEKDAY;
+ else if ( m_xBtnMonth->get_active() ) theFillDateCmd = FILL_MONTH;
+ else if ( m_xBtnYear->get_active() ) theFillDateCmd = FILL_YEAR;
+
+ weld::Entry* pEdWrong = CheckValues();
+ if ( pEdWrong == nullptr )
+ {
+ m_xDialog->response(RET_OK);
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Warning,
+ VclButtonsType::Ok, aErrMsgInvalidVal));
+ xBox->run();
+ pEdWrong->grab_focus();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/gototabdlg.cxx b/sc/source/ui/miscdlgs/gototabdlg.cxx
new file mode 100644
index 0000000000..c817e506d6
--- /dev/null
+++ b/sc/source/ui/miscdlgs/gototabdlg.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/.
+ *
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <gototabdlg.hxx>
+
+ScGoToTabDlg::ScGoToTabDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/gotosheetdialog.ui", "GoToSheetDialog")
+ , m_xFrameMask(m_xBuilder->weld_frame("frame-mask"))
+ , m_xEnNameMask(m_xBuilder->weld_entry("entry-mask"))
+ , m_xFrameSheets(m_xBuilder->weld_frame("frame-sheets"))
+ , m_xLb(m_xBuilder->weld_tree_view("treeview"))
+{
+ m_xLb->set_selection_mode(SelectionMode::Single);
+ m_xLb->set_size_request(-1, m_xLb->get_height_rows(10));
+ m_xLb->connect_row_activated(LINK(this, ScGoToTabDlg, DblClkHdl));
+ m_xEnNameMask->connect_changed(LINK(this, ScGoToTabDlg, FindNameHdl));
+}
+
+ScGoToTabDlg::~ScGoToTabDlg() {}
+
+void ScGoToTabDlg::SetDescription(const OUString& rTitle, const OUString& rEntryLabel,
+ const OUString& rListLabel, const OUString& rDlgHelpId,
+ const OUString& rEnHelpId, const OUString& rLbHelpId)
+{
+ m_xDialog->set_title(rTitle);
+ m_xFrameMask->set_label(rEntryLabel);
+ m_xFrameSheets->set_label(rListLabel);
+ m_xDialog->set_help_id(rDlgHelpId);
+ m_xEnNameMask->set_help_id(rEnHelpId);
+ m_xLb->set_help_id(rLbHelpId);
+}
+
+void ScGoToTabDlg::Insert(const OUString& rString, bool bSelected)
+{
+ maCacheSheetsNames.push_back(rString);
+ m_xLb->append_text(rString);
+ if (bSelected)
+ m_xLb->select(m_xLb->n_children() - 1);
+}
+
+OUString ScGoToTabDlg::GetSelectedEntry() const { return m_xLb->get_selected_text(); }
+
+IMPL_LINK_NOARG(ScGoToTabDlg, DblClkHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+IMPL_LINK_NOARG(ScGoToTabDlg, FindNameHdl, weld::Entry&, void)
+{
+ const OUString aMask = m_xEnNameMask->get_text();
+ m_xLb->clear();
+ if (aMask.isEmpty())
+ {
+ for (const OUString& s : maCacheSheetsNames)
+ {
+ m_xLb->append_text(s);
+ }
+ }
+ else
+ {
+ for (const OUString& s : maCacheSheetsNames)
+ {
+ if (s.indexOf(aMask) >= 0)
+ {
+ m_xLb->append_text(s);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/groupdlg.cxx b/sc/source/ui/miscdlgs/groupdlg.cxx
new file mode 100644
index 0000000000..f200dbccba
--- /dev/null
+++ b/sc/source/ui/miscdlgs/groupdlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <groupdlg.hxx>
+
+ScGroupDlg::ScGroupDlg(weld::Window* pParent, bool bUngroup, bool bRows)
+ : GenericDialogController(pParent,
+ bUngroup ?
+ OUString("modules/scalc/ui/ungroupdialog.ui") :
+ OUString("modules/scalc/ui/groupdialog.ui")
+ ,
+ bUngroup ?
+ OUString("UngroupDialog") :
+ OUString("GroupDialog"))
+ , m_xBtnRows(m_xBuilder->weld_radio_button("rows"))
+ , m_xBtnCols(m_xBuilder->weld_radio_button("cols"))
+{
+ if (bRows)
+ m_xBtnRows->set_active(true);
+ else
+ m_xBtnCols->set_active(true);
+ m_xBtnRows->grab_focus();
+}
+
+ScGroupDlg::~ScGroupDlg()
+{
+}
+
+bool ScGroupDlg::GetColsChecked() const
+{
+ return m_xBtnCols->get_active();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/highred.cxx b/sc/source/ui/miscdlgs/highred.cxx
new file mode 100644
index 0000000000..fa655f80e4
--- /dev/null
+++ b/sc/source/ui/miscdlgs/highred.cxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <reffact.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <chgtrack.hxx>
+
+#include <highred.hxx>
+
+
+ScHighlightChgDlg::ScHighlightChgDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData)
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/showchangesdialog.ui", "ShowChangesDialog")
+ , m_rViewData(rViewData)
+ , rDoc(rViewData.GetDocument())
+ , m_xHighlightBox(m_xBuilder->weld_check_button("showchanges"))
+ , m_xCbAccept(m_xBuilder->weld_check_button("showaccepted"))
+ , m_xCbReject(m_xBuilder->weld_check_button("showrejected"))
+ , m_xOkButton(m_xBuilder->weld_button("ok"))
+ , m_xEdAssign(new formula::RefEdit(m_xBuilder->weld_entry("range")))
+ , m_xRbAssign(new formula::RefButton(m_xBuilder->weld_button("rangeref")))
+ , m_xBox(m_xBuilder->weld_container("box"))
+ , m_xFilterCtr(new SvxTPFilter(m_xBox.get()))
+{
+ m_xEdAssign->SetReferences(this, nullptr);
+ m_xRbAssign->SetReferences(this, m_xEdAssign.get());
+
+ m_xOkButton->connect_clicked(LINK( this, ScHighlightChgDlg, OKBtnHdl));
+ m_xHighlightBox->connect_toggled(LINK( this, ScHighlightChgDlg, HighlightHandle ));
+ m_xFilterCtr->SetRefHdl(LINK( this, ScHighlightChgDlg, RefHandle ));
+ m_xFilterCtr->HideRange(false);
+ m_xFilterCtr->Show();
+ SetDispatcherLock( true );
+
+ Init();
+}
+
+ScHighlightChgDlg::~ScHighlightChgDlg()
+{
+ SetDispatcherLock( false );
+}
+
+void ScHighlightChgDlg::Init()
+{
+ ScChangeTrack* pChanges = rDoc.GetChangeTrack();
+ if(pChanges!=nullptr)
+ {
+ aChangeViewSet.SetTheAuthorToShow(pChanges->GetUser());
+ m_xFilterCtr->ClearAuthors();
+ const std::set<OUString>& rUserColl = pChanges->GetUserCollection();
+ for (const auto& rItem : rUserColl)
+ m_xFilterCtr->InsertAuthor(rItem);
+ }
+
+ ScChangeViewSettings* pViewSettings = rDoc.GetChangeViewSettings();
+
+ if(pViewSettings!=nullptr)
+ aChangeViewSet=*pViewSettings;
+ m_xHighlightBox->set_active(aChangeViewSet.ShowChanges());
+ m_xFilterCtr->CheckDate(aChangeViewSet.HasDate());
+
+ DateTime aEmpty(DateTime::EMPTY);
+
+ DateTime aDateTime(aChangeViewSet.GetTheFirstDateTime());
+ if (aDateTime != aEmpty)
+ {
+ m_xFilterCtr->SetFirstDate(aDateTime);
+ m_xFilterCtr->SetFirstTime(aDateTime);
+ }
+ aDateTime = aChangeViewSet.GetTheLastDateTime();
+ if (aDateTime != aEmpty)
+ {
+ m_xFilterCtr->SetLastDate(aDateTime);
+ m_xFilterCtr->SetLastTime(aDateTime);
+ }
+
+ m_xFilterCtr->SetDateMode(static_cast<sal_uInt16>(aChangeViewSet.GetTheDateMode()));
+ m_xFilterCtr->CheckAuthor(aChangeViewSet.HasAuthor());
+ m_xFilterCtr->CheckComment(aChangeViewSet.HasComment());
+ m_xFilterCtr->SetComment(aChangeViewSet.GetTheComment());
+
+ m_xCbAccept->set_active(aChangeViewSet.IsShowAccepted());
+ m_xCbReject->set_active(aChangeViewSet.IsShowRejected());
+
+ OUString aString=aChangeViewSet.GetTheAuthorToShow();
+ if(!aString.isEmpty())
+ {
+ m_xFilterCtr->SelectAuthor(aString);
+ }
+ else
+ {
+ m_xFilterCtr->SelectedAuthorPos(0);
+ }
+
+ m_xFilterCtr->CheckRange(aChangeViewSet.HasRange());
+
+ if ( !aChangeViewSet.GetTheRangeList().empty() )
+ {
+ const ScRange & rRangeEntry = aChangeViewSet.GetTheRangeList().front();
+ OUString aRefStr(rRangeEntry.Format(rDoc, ScRefFlags::RANGE_ABS_3D));
+ m_xFilterCtr->SetRange(aRefStr);
+ }
+ m_xFilterCtr->Enable(true);
+ HighlightHandle(*m_xHighlightBox);
+}
+
+// Set the reference to a cell range selected with the mouse. This is then
+// shown as the new selection in the reference field.
+void ScHighlightChgDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (m_xEdAssign->GetWidget()->get_visible())
+ {
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_xEdAssign.get());
+ OUString aRefStr(rRef.Format(rDocP, ScRefFlags::RANGE_ABS_3D, rDocP.GetAddressConvention()));
+ m_xEdAssign->SetRefString( aRefStr );
+ m_xFilterCtr->SetRange(aRefStr);
+ }
+}
+
+void ScHighlightChgDlg::Close()
+{
+ DoClose( ScHighlightChgDlgWrapper::GetChildWindowId() );
+}
+
+void ScHighlightChgDlg::RefInputDone( bool bForced)
+{
+ ScAnyRefDlgController::RefInputDone(bForced);
+ if (bForced || !m_xRbAssign->GetWidget()->get_visible())
+ {
+ m_xFilterCtr->SetRange(m_xEdAssign->GetText());
+ m_xFilterCtr->SetFocusToRange();
+ m_xEdAssign->GetWidget()->hide();
+ m_xRbAssign->GetWidget()->hide();
+ }
+}
+
+void ScHighlightChgDlg::SetActive()
+{
+}
+
+bool ScHighlightChgDlg::IsRefInputMode() const
+{
+ return m_xEdAssign->GetWidget()->get_visible();
+}
+
+IMPL_LINK_NOARG(ScHighlightChgDlg, HighlightHandle, weld::Toggleable&, void)
+{
+ if (m_xHighlightBox->get_active())
+ {
+ m_xFilterCtr->Enable(true);
+ m_xCbAccept->set_sensitive(true);
+ m_xCbReject->set_sensitive(true);
+ }
+ else
+ {
+ m_xFilterCtr->Enable(false);
+ m_xCbAccept->set_sensitive(false);
+ m_xCbReject->set_sensitive(false);
+ }
+}
+
+IMPL_LINK( ScHighlightChgDlg, RefHandle, SvxTPFilter*, pRef, void )
+{
+ if(pRef!=nullptr)
+ {
+ SetDispatcherLock( true );
+ m_xEdAssign->GetWidget()->show();
+ m_xRbAssign->GetWidget()->show();
+ m_xEdAssign->SetText(m_xFilterCtr->GetRange());
+ m_xEdAssign->GrabFocus();
+ ScAnyRefDlgController::RefInputStart(m_xEdAssign.get(), m_xRbAssign.get());
+ }
+}
+
+IMPL_LINK_NOARG(ScHighlightChgDlg, OKBtnHdl, weld::Button&, void)
+{
+ aChangeViewSet.SetShowChanges(m_xHighlightBox->get_active());
+ aChangeViewSet.SetHasDate(m_xFilterCtr->IsDate());
+ SvxRedlinDateMode eMode = m_xFilterCtr->GetDateMode();
+ aChangeViewSet.SetTheDateMode( eMode );
+ Date aFirstDate( m_xFilterCtr->GetFirstDate() );
+ tools::Time aFirstTime( m_xFilterCtr->GetFirstTime() );
+ Date aLastDate( m_xFilterCtr->GetLastDate() );
+ tools::Time aLastTime( m_xFilterCtr->GetLastTime() );
+ aChangeViewSet.SetTheFirstDateTime( DateTime( aFirstDate, aFirstTime ) );
+ aChangeViewSet.SetTheLastDateTime( DateTime( aLastDate, aLastTime ) );
+ aChangeViewSet.SetHasAuthor(m_xFilterCtr->IsAuthor());
+ aChangeViewSet.SetTheAuthorToShow(m_xFilterCtr->GetSelectedAuthor());
+ aChangeViewSet.SetHasRange(m_xFilterCtr->IsRange());
+ aChangeViewSet.SetShowAccepted(m_xCbAccept->get_active());
+ aChangeViewSet.SetShowRejected(m_xCbReject->get_active());
+ aChangeViewSet.SetHasComment(m_xFilterCtr->IsComment());
+ aChangeViewSet.SetTheComment(m_xFilterCtr->GetComment());
+ ScRangeList aLocalRangeList;
+ aLocalRangeList.Parse(m_xFilterCtr->GetRange(), rDoc);
+ aChangeViewSet.SetTheRangeList(aLocalRangeList);
+ aChangeViewSet.AdjustDateMode( rDoc );
+ rDoc.SetChangeViewSettings(aChangeViewSet);
+ m_rViewData.GetDocShell()->PostPaintGridAll();
+ response(RET_OK);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/inscldlg.cxx b/sc/source/ui/miscdlgs/inscldlg.cxx
new file mode 100644
index 0000000000..e077724f42
--- /dev/null
+++ b/sc/source/ui/miscdlgs/inscldlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <inscldlg.hxx>
+#include <viewdata.hxx>
+#include <strings.hrc>
+#include <scresid.hxx>
+
+static sal_uInt8 nInsItemChecked = 0;
+
+ScInsertCellDlg::ScInsertCellDlg(weld::Window* pParent, bool bDisallowCellMove)
+ : GenericDialogController(pParent, "modules/scalc/ui/insertcells.ui", "InsertCellsDialog")
+ , m_xBtnCellsDown(m_xBuilder->weld_radio_button("down"))
+ , m_xBtnCellsRight(m_xBuilder->weld_radio_button("right"))
+ , m_xBtnInsRow(m_xBuilder->weld_radio_button("rows"))
+ , m_xBtnInsCol(m_xBuilder->weld_radio_button("cols"))
+{
+ const ScViewData* pViewData = ScDocShell::GetViewData();
+ if (pViewData && pViewData->GetDocument().IsLayoutRTL(pViewData->GetTabNo()))
+ m_xBtnCellsRight->set_label(ScResId(SCSTR_INSERT_RTL));
+
+ if (bDisallowCellMove)
+ {
+ m_xBtnCellsDown->set_sensitive(false);
+ m_xBtnCellsRight->set_sensitive(false);
+ m_xBtnInsRow->set_active(true);
+
+ switch (nInsItemChecked)
+ {
+ case 2:
+ m_xBtnInsRow->set_active(true);
+ break;
+ case 3:
+ m_xBtnInsCol->set_active(true);
+ break;
+ default:
+ m_xBtnInsRow->set_active(true);
+ break;
+ }
+ }
+ else
+ {
+ switch (nInsItemChecked)
+ {
+ case 0:
+ m_xBtnCellsDown->set_active(true);
+ break;
+ case 1:
+ m_xBtnCellsRight->set_active(true);
+ break;
+ case 2:
+ m_xBtnInsRow->set_active(true);
+ break;
+ case 3:
+ m_xBtnInsCol->set_active(true);
+ break;
+ }
+ }
+}
+
+ScInsertCellDlg::~ScInsertCellDlg() {}
+
+InsCellCmd ScInsertCellDlg::GetInsCellCmd() const
+{
+ InsCellCmd nReturn = INS_NONE;
+
+ if (m_xBtnCellsDown->get_active())
+ {
+ nInsItemChecked = 0;
+ nReturn = INS_CELLSDOWN;
+ }
+ else if (m_xBtnCellsRight->get_active())
+ {
+ nInsItemChecked = 1;
+ nReturn = INS_CELLSRIGHT;
+ }
+ else if (m_xBtnInsRow->get_active())
+ {
+ nInsItemChecked = 2;
+ nReturn = INS_INSROWS_BEFORE;
+ }
+ else if (m_xBtnInsCol->get_active())
+ {
+ nInsItemChecked = 3;
+ nReturn = INS_INSCOLS_BEFORE;
+ }
+
+ return nReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/inscodlg.cxx b/sc/source/ui/miscdlgs/inscodlg.cxx
new file mode 100644
index 0000000000..84292dcd91
--- /dev/null
+++ b/sc/source/ui/miscdlgs/inscodlg.cxx
@@ -0,0 +1,504 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <inscodlg.hxx>
+#include <officecfg/Office/Common.hxx>
+
+InsertDeleteFlags ScInsertContentsDlg::nPreviousChecks = InsertDeleteFlags::NONE;
+ScPasteFunc ScInsertContentsDlg::nPreviousFormulaChecks = ScPasteFunc::NONE;
+InsertContentsFlags ScInsertContentsDlg::nPreviousChecks2 = InsertContentsFlags::NONE;
+InsCellCmd ScInsertContentsDlg::nPreviousMoveMode = InsCellCmd::INS_NONE;
+
+//whether the dialog has loaded for the first time
+static bool firstLoad = true;
+
+void ScInsertContentsDlg::storeFlagsInRegistry()
+{
+ //store the flags in the registry
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+
+ //InsertDeleteFlags
+ officecfg::Office::Common::PasteSpecial::Paste::All::set(ScInsertContentsDlg::mxBtnInsAll->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::Numbers::set(ScInsertContentsDlg::mxBtnInsNumbers->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::Text::set(ScInsertContentsDlg::mxBtnInsStrings->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::DateTime::set(ScInsertContentsDlg::mxBtnInsDateTime->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::Formats::set(ScInsertContentsDlg::mxBtnInsAttrs->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::Comments::set(ScInsertContentsDlg::mxBtnInsNotes->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::Objects::set(ScInsertContentsDlg::mxBtnInsObjects->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Paste::Formulas::set(ScInsertContentsDlg::mxBtnInsFormulas->get_active(), batch);
+
+ //ScPasteFunc
+ if(ScInsertContentsDlg::mxRbNoOp->get_active())
+ officecfg::Office::Common::PasteSpecial::Operations::set(0, batch);
+ else if(ScInsertContentsDlg::mxRbAdd->get_active())
+ officecfg::Office::Common::PasteSpecial::Operations::set(1, batch);
+ else if(ScInsertContentsDlg::mxRbSub->get_active())
+ officecfg::Office::Common::PasteSpecial::Operations::set(2, batch);
+ else if(ScInsertContentsDlg::mxRbMul->get_active())
+ officecfg::Office::Common::PasteSpecial::Operations::set(3, batch);
+ else if(ScInsertContentsDlg::mxRbDiv->get_active())
+ officecfg::Office::Common::PasteSpecial::Operations::set(4, batch);
+
+ //InsertContentsFlags
+ officecfg::Office::Common::PasteSpecial::Options::AsLink::set(ScInsertContentsDlg::mxBtnLink->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Options::Transpose::set(ScInsertContentsDlg::mxBtnTranspose->get_active(), batch);
+ officecfg::Office::Common::PasteSpecial::Options::SkipEmptyCells::set(ScInsertContentsDlg::mxBtnSkipEmptyCells->get_active(), batch);
+
+ //InsCellCmd
+ if(ScInsertContentsDlg::mxRbMoveNone->get_active())
+ officecfg::Office::Common::PasteSpecial::ShiftCells::set(4, batch);
+ else if(ScInsertContentsDlg::mxRbMoveDown->get_active())
+ officecfg::Office::Common::PasteSpecial::ShiftCells::set(0, batch);
+ else if(ScInsertContentsDlg::mxRbMoveRight->get_active())
+ officecfg::Office::Common::PasteSpecial::ShiftCells::set(1, batch);
+
+ batch->commit();
+}
+
+ScInsertContentsDlg::ScInsertContentsDlg(weld::Window* pParent,
+ const OUString* pStrTitle )
+ : GenericDialogController(pParent, "modules/scalc/ui/pastespecial.ui", "PasteSpecial")
+ , bOtherDoc(false)
+ , bFillMode(false)
+ , bChangeTrack(false)
+ , bMoveDownDisabled(false)
+ , bMoveRightDisabled(false)
+ , mxBtnInsAll(m_xBuilder->weld_check_button("paste_all"))
+ , mxBtnInsStrings(m_xBuilder->weld_check_button("text"))
+ , mxBtnInsNumbers(m_xBuilder->weld_check_button("numbers"))
+ , mxBtnInsDateTime(m_xBuilder->weld_check_button("datetime"))
+ , mxBtnInsFormulas(m_xBuilder->weld_check_button("formulas"))
+ , mxBtnInsNotes(m_xBuilder->weld_check_button("comments"))
+ , mxBtnInsAttrs(m_xBuilder->weld_check_button("formats"))
+ , mxBtnInsObjects(m_xBuilder->weld_check_button("objects"))
+ , mxBtnSkipEmptyCells(m_xBuilder->weld_check_button("skip_empty"))
+ , mxBtnTranspose(m_xBuilder->weld_check_button("transpose"))
+ , mxBtnLink(m_xBuilder->weld_check_button("link"))
+ , mxRbNoOp(m_xBuilder->weld_radio_button("none"))
+ , mxRbAdd(m_xBuilder->weld_radio_button("add"))
+ , mxRbSub(m_xBuilder->weld_radio_button("subtract"))
+ , mxRbMul(m_xBuilder->weld_radio_button("multiply"))
+ , mxRbDiv(m_xBuilder->weld_radio_button("divide"))
+ , mxRbMoveNone(m_xBuilder->weld_radio_button("no_shift"))
+ , mxRbMoveDown(m_xBuilder->weld_radio_button("move_down"))
+ , mxRbMoveRight(m_xBuilder->weld_radio_button("move_right"))
+ , mxBtnShortCutPasteValuesOnly(m_xBuilder->weld_button("paste_values_only"))
+ , mxBtnShortCutPasteValuesFormats(m_xBuilder->weld_button("paste_values_formats"))
+ , mxBtnShortCutPasteTranspose(m_xBuilder->weld_button("paste_transpose"))
+ , mxBtnShortCutPasteFormats(m_xBuilder->weld_button("paste_formats"))
+ , mxOKBtn(m_xBuilder->weld_button("ok"))
+ , mxImmediately(m_xBuilder->weld_check_button("cbImmediately"))
+{
+ if (pStrTitle)
+ m_xDialog->set_title(*pStrTitle);
+
+ if (firstLoad)
+ {
+ //unset firstLoad
+ firstLoad = false;
+
+ //set the flags from the registry
+
+ /*
+ * Flags for nPreviousChecks
+ */
+ if(officecfg::Office::Common::PasteSpecial::Paste::All::get())
+ nPreviousChecks |= InsertDeleteFlags::ALL;
+ if(officecfg::Office::Common::PasteSpecial::Paste::Numbers::get())
+ nPreviousChecks |= InsertDeleteFlags::VALUE;
+ if(officecfg::Office::Common::PasteSpecial::Paste::Text::get())
+ nPreviousChecks |= InsertDeleteFlags::STRING;
+ if(officecfg::Office::Common::PasteSpecial::Paste::DateTime::get())
+ nPreviousChecks |= InsertDeleteFlags::DATETIME;
+ if(officecfg::Office::Common::PasteSpecial::Paste::Formats::get())
+ nPreviousChecks |= InsertDeleteFlags::ATTRIB;
+ if(officecfg::Office::Common::PasteSpecial::Paste::Comments::get())
+ nPreviousChecks |= InsertDeleteFlags::NOTE;
+ if(officecfg::Office::Common::PasteSpecial::Paste::Objects::get())
+ nPreviousChecks |= InsertDeleteFlags::OBJECTS;
+ if(officecfg::Office::Common::PasteSpecial::Paste::Formulas::get())
+ nPreviousChecks |= InsertDeleteFlags::FORMULA;
+
+ /*
+ * Flags for nPreviousFormulaChecks
+ */
+ int optionFlagValue = officecfg::Office::Common::PasteSpecial::Operations::get();
+ switch(optionFlagValue)
+ {
+ case 0: nPreviousFormulaChecks = ScPasteFunc::NONE;
+ break;
+
+ case 1: nPreviousFormulaChecks = ScPasteFunc::ADD;
+ break;
+
+ case 2: nPreviousFormulaChecks = ScPasteFunc::SUB;
+ break;
+
+ case 3: nPreviousFormulaChecks = ScPasteFunc::MUL;
+ break;
+
+ case 4: nPreviousFormulaChecks = ScPasteFunc::DIV;
+ break;
+ }
+
+ /*
+ * Flags for nPreviousChecks2
+ */
+ if(officecfg::Office::Common::PasteSpecial::Options::AsLink::get())
+ nPreviousChecks2 |= InsertContentsFlags::Link;
+ if(officecfg::Office::Common::PasteSpecial::Options::Transpose::get())
+ nPreviousChecks2 |= InsertContentsFlags::Trans;
+ if(officecfg::Office::Common::PasteSpecial::Options::SkipEmptyCells::get())
+ nPreviousChecks2 |= InsertContentsFlags::NoEmpty;
+
+ /*
+ * Flags for nPreviousMoveMode
+ */
+ int shiftFlagValue = officecfg::Office::Common::PasteSpecial::ShiftCells::get();
+ switch(shiftFlagValue)
+ {
+ case 0: nPreviousMoveMode = InsCellCmd::INS_CELLSDOWN;
+ break;
+
+ case 1: nPreviousMoveMode = InsCellCmd::INS_CELLSRIGHT;
+ break;
+
+ case 4: nPreviousMoveMode = InsCellCmd::INS_NONE;
+ break;
+ }
+ }
+
+ SetInsContentsCmdBits( ScInsertContentsDlg::nPreviousChecks );
+ SetFormulaCmdBits( ScInsertContentsDlg::nPreviousFormulaChecks );
+ SetCellCmdFlags( ScInsertContentsDlg::nPreviousMoveMode );
+ SetContentsFlags( ScInsertContentsDlg::nPreviousChecks2 );
+ DisableChecks( mxBtnInsAll->get_active() );
+
+ mxBtnInsAll->connect_toggled( LINK( this, ScInsertContentsDlg, InsAllHdl ) );
+ mxBtnLink->connect_toggled( LINK( this, ScInsertContentsDlg, LinkBtnHdl ) );
+ mxBtnShortCutPasteValuesOnly->connect_clicked( LINK( this, ScInsertContentsDlg, ShortCutHdl ) );
+ mxBtnShortCutPasteValuesFormats->connect_clicked( LINK( this, ScInsertContentsDlg, ShortCutHdl ) );
+ mxBtnShortCutPasteTranspose->connect_clicked( LINK( this, ScInsertContentsDlg, ShortCutHdl ) );
+ mxBtnShortCutPasteFormats->connect_clicked( LINK( this, ScInsertContentsDlg, ShortCutHdl ) );
+ mxOKBtn->connect_clicked( LINK( this, ScInsertContentsDlg, ClickHdl ) );
+}
+
+InsertDeleteFlags ScInsertContentsDlg::GetInsContentsCmdBits() const
+{
+ ScInsertContentsDlg::nPreviousChecks = InsertDeleteFlags::NONE;
+
+ if ( mxBtnInsStrings->get_active() )
+ ScInsertContentsDlg::nPreviousChecks = InsertDeleteFlags::STRING;
+ if ( mxBtnInsNumbers->get_active() )
+ ScInsertContentsDlg::nPreviousChecks |= InsertDeleteFlags::VALUE;
+ if ( mxBtnInsDateTime->get_active())
+ ScInsertContentsDlg::nPreviousChecks |= InsertDeleteFlags::DATETIME;
+ if ( mxBtnInsFormulas->get_active())
+ ScInsertContentsDlg::nPreviousChecks |= InsertDeleteFlags::FORMULA;
+ // tdf#139858 - do not delete existing cell contents when pasting notes
+ if ( mxBtnInsNotes->get_active() )
+ ScInsertContentsDlg::nPreviousChecks |= InsertDeleteFlags::ADDNOTES;
+ if ( mxBtnInsAttrs->get_active() )
+ ScInsertContentsDlg::nPreviousChecks |= InsertDeleteFlags::ATTRIB;
+ if ( mxBtnInsObjects->get_active() )
+ ScInsertContentsDlg::nPreviousChecks |= InsertDeleteFlags::OBJECTS;
+
+ return ( mxBtnInsAll->get_active()
+ ? InsertDeleteFlags::ALL
+ : ScInsertContentsDlg::nPreviousChecks );
+}
+
+void ScInsertContentsDlg::SetInsContentsCmdBits(const InsertDeleteFlags eFlags)
+{
+ mxBtnInsNumbers->set_active((InsertDeleteFlags::VALUE & eFlags) == InsertDeleteFlags::VALUE);
+ mxBtnInsDateTime->set_active((InsertDeleteFlags::DATETIME & eFlags) == InsertDeleteFlags::DATETIME);
+ mxBtnInsStrings->set_active((InsertDeleteFlags::STRING & eFlags) == InsertDeleteFlags::STRING);
+ mxBtnInsNotes->set_active((InsertDeleteFlags::NOTE & eFlags) == InsertDeleteFlags::NOTE);
+ mxBtnInsFormulas->set_active((InsertDeleteFlags::FORMULA & eFlags) == InsertDeleteFlags::FORMULA);
+ mxBtnInsAttrs->set_active((InsertDeleteFlags::ATTRIB & eFlags) == InsertDeleteFlags::ATTRIB);
+ mxBtnInsObjects->set_active((InsertDeleteFlags::OBJECTS & eFlags) == InsertDeleteFlags::OBJECTS);
+ mxBtnInsAll->set_active((InsertDeleteFlags::ALL & eFlags) == InsertDeleteFlags::ALL);
+ DisableChecks( mxBtnInsAll->get_active() );
+}
+
+void ScInsertContentsDlg::SetFormulaCmdBits(const ScPasteFunc eFlags)
+{
+ switch( eFlags )
+ {
+ case ScPasteFunc::NONE: mxRbNoOp->set_active(true); break;
+ case ScPasteFunc::ADD: mxRbAdd->set_active(true); break;
+ case ScPasteFunc::SUB: mxRbSub->set_active(true); break;
+ case ScPasteFunc::MUL: mxRbMul->set_active(true); break;
+ case ScPasteFunc::DIV: mxRbDiv->set_active(true); break;
+ }
+}
+
+void ScInsertContentsDlg::SetCellCmdFlags(const InsCellCmd eFlags)
+{
+ switch( eFlags )
+ {
+ case INS_NONE: mxRbMoveNone->set_active(true); break;
+ case INS_CELLSDOWN: mxRbMoveDown->set_active(true); break;
+ case INS_CELLSRIGHT: mxRbMoveRight->set_active(true); break;
+ case INS_INSROWS_BEFORE:
+ case INS_INSCOLS_BEFORE:
+ case INS_INSROWS_AFTER:
+ case INS_INSCOLS_AFTER: break;
+ }
+}
+
+void ScInsertContentsDlg::SetContentsFlags(const InsertContentsFlags eFlags)
+{
+ mxBtnSkipEmptyCells->set_active(bool(InsertContentsFlags::NoEmpty & eFlags));
+ mxBtnTranspose->set_active(bool(InsertContentsFlags::Trans & eFlags));
+ mxBtnLink->set_active(bool(InsertContentsFlags::Link & eFlags));
+}
+
+InsCellCmd ScInsertContentsDlg::GetMoveMode() const
+{
+ if ( mxRbMoveDown->get_active() )
+ return INS_CELLSDOWN;
+ if ( mxRbMoveRight->get_active() )
+ return INS_CELLSRIGHT;
+
+ return INS_NONE;
+}
+
+bool ScInsertContentsDlg::IsSkipEmptyCells() const
+{
+ return mxBtnSkipEmptyCells->get_active();
+}
+
+bool ScInsertContentsDlg::IsTranspose() const
+{
+ return mxBtnTranspose->get_active();
+}
+
+bool ScInsertContentsDlg::IsLink() const
+{
+ return mxBtnLink->get_active();
+}
+
+void ScInsertContentsDlg::DisableChecks( bool bInsAllChecked )
+{
+ if ( bInsAllChecked )
+ {
+ mxBtnInsStrings->set_sensitive(false);
+ mxBtnInsNumbers->set_sensitive(false);
+ mxBtnInsDateTime->set_sensitive(false);
+ mxBtnInsFormulas->set_sensitive(false);
+ mxBtnInsNotes->set_sensitive(false);
+ mxBtnInsAttrs->set_sensitive(false);
+ mxBtnInsObjects->set_sensitive(false);
+ }
+ else
+ {
+ mxBtnInsStrings->set_sensitive(true);
+ mxBtnInsNumbers->set_sensitive(true);
+ mxBtnInsDateTime->set_sensitive(true);
+ mxBtnInsFormulas->set_sensitive(true);
+ mxBtnInsNotes->set_sensitive(true);
+ mxBtnInsAttrs->set_sensitive(true);
+
+ // "Objects" is disabled for "Fill Tables"
+ if ( bFillMode )
+ mxBtnInsObjects->set_sensitive(false);
+ else
+ mxBtnInsObjects->set_sensitive(true);
+ }
+}
+
+// Link to other document -> everything else is disabled
+
+void ScInsertContentsDlg::TestModes()
+{
+ if ( bOtherDoc && mxBtnLink->get_active() )
+ {
+ mxBtnSkipEmptyCells->set_sensitive(false);
+ mxBtnTranspose->set_sensitive(false);
+ mxRbNoOp->set_sensitive(false);
+ mxRbAdd->set_sensitive(false);
+ mxRbSub->set_sensitive(false);
+ mxRbMul->set_sensitive(false);
+ mxRbDiv->set_sensitive(false);
+
+ mxRbMoveNone->set_sensitive(false);
+ mxRbMoveDown->set_sensitive(false);
+ mxRbMoveRight->set_sensitive(false);
+
+ mxBtnInsAll->set_sensitive(false);
+ DisableChecks(true);
+ }
+ else
+ {
+ mxBtnSkipEmptyCells->set_sensitive(true);
+ mxBtnTranspose->set_sensitive(!bFillMode);
+ mxRbNoOp->set_sensitive(true);
+ mxRbAdd->set_sensitive(true);
+ mxRbSub->set_sensitive(true);
+ mxRbMul->set_sensitive(true);
+ mxRbDiv->set_sensitive(true);
+
+ mxRbMoveNone->set_sensitive(!bFillMode && !bChangeTrack && !(bMoveDownDisabled && bMoveRightDisabled));
+ mxRbMoveDown->set_sensitive(!bFillMode && !bChangeTrack && !bMoveDownDisabled);
+ mxRbMoveRight->set_sensitive(!bFillMode && !bChangeTrack && !bMoveRightDisabled);
+
+ mxBtnInsAll->set_sensitive(true);
+ DisableChecks( mxBtnInsAll->get_active() );
+ }
+}
+
+void ScInsertContentsDlg::SetOtherDoc( bool bSet )
+{
+ if ( bSet != bOtherDoc )
+ {
+ bOtherDoc = bSet;
+ TestModes();
+ if ( bSet )
+ mxRbMoveNone->set_active(true);
+ }
+}
+
+void ScInsertContentsDlg::SetFillMode( bool bSet )
+{
+ if ( bSet != bFillMode )
+ {
+ bFillMode = bSet;
+ TestModes();
+ if ( bSet )
+ mxRbMoveNone->set_active(true);
+ }
+}
+
+void ScInsertContentsDlg::SetChangeTrack( bool bSet )
+{
+ if ( bSet != bChangeTrack )
+ {
+ bChangeTrack = bSet;
+ TestModes();
+ if ( bSet )
+ mxRbMoveNone->set_active(true);
+ }
+}
+
+void ScInsertContentsDlg::SetCellShiftDisabled( CellShiftDisabledFlags nDisable )
+{
+ bool bDown(nDisable & CellShiftDisabledFlags::Down);
+ bool bRight(nDisable & CellShiftDisabledFlags::Right);
+ if ( bDown != bMoveDownDisabled || bRight != bMoveRightDisabled )
+ {
+ bMoveDownDisabled = bDown;
+ bMoveRightDisabled = bRight;
+ TestModes();
+ if ( bMoveDownDisabled && mxRbMoveDown->get_active() )
+ mxRbMoveNone->set_active(true);
+ if ( bMoveRightDisabled && mxRbMoveRight->get_active() )
+ mxRbMoveNone->set_active(true);
+ }
+}
+
+IMPL_LINK(ScInsertContentsDlg, ShortCutHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnShortCutPasteValuesOnly.get())
+ {
+ SetInsContentsCmdBits( InsertDeleteFlags::STRING | InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME );
+ SetContentsFlags( InsertContentsFlags::NONE );
+ }
+ else if (&rBtn == mxBtnShortCutPasteValuesFormats.get())
+ {
+ SetInsContentsCmdBits( InsertDeleteFlags::STRING | InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME | InsertDeleteFlags::ATTRIB );
+ SetContentsFlags( InsertContentsFlags::NONE );
+ }
+ else if (&rBtn == mxBtnShortCutPasteTranspose.get())
+ {
+ SetInsContentsCmdBits( InsertDeleteFlags::ALL );
+ SetContentsFlags( InsertContentsFlags::Trans );
+ }
+ else if (&rBtn == mxBtnShortCutPasteFormats.get())
+ {
+ SetInsContentsCmdBits( InsertDeleteFlags::ATTRIB );
+ SetContentsFlags( InsertContentsFlags::NONE );
+ }
+ else
+ return;
+
+ SetCellCmdFlags( InsCellCmd::INS_NONE );
+ SetFormulaCmdBits(ScPasteFunc::NONE);
+
+ if (mxImmediately->get_active())
+ {
+ storeFlagsInRegistry();
+ m_xDialog->response(RET_OK);
+ }
+}
+
+IMPL_LINK_NOARG(ScInsertContentsDlg, ClickHdl, weld::Button&, void)
+{
+ storeFlagsInRegistry();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ScInsertContentsDlg, InsAllHdl, weld::Toggleable&, void)
+{
+ DisableChecks( mxBtnInsAll->get_active() );
+}
+
+IMPL_LINK_NOARG(ScInsertContentsDlg, LinkBtnHdl, weld::Toggleable&, void)
+{
+ TestModes();
+}
+
+ScInsertContentsDlg::~ScInsertContentsDlg()
+{
+ ScInsertContentsDlg::nPreviousChecks2 = InsertContentsFlags::NONE;
+ if(mxBtnSkipEmptyCells->get_active())
+ ScInsertContentsDlg::nPreviousChecks2 |= InsertContentsFlags::NoEmpty;
+ if( mxBtnTranspose->get_active())
+ ScInsertContentsDlg::nPreviousChecks2 |= InsertContentsFlags::Trans;
+ if( mxBtnLink->get_active() )
+ ScInsertContentsDlg::nPreviousChecks2 |= InsertContentsFlags::Link;
+
+ if (!bFillMode) // in FillMode, None is checked and all three options are disabled
+ {
+ if ( mxRbMoveNone->get_active() )
+ ScInsertContentsDlg::nPreviousMoveMode = INS_NONE;
+ else if ( mxRbMoveDown->get_active() )
+ ScInsertContentsDlg::nPreviousMoveMode = INS_CELLSDOWN;
+ else if ( mxRbMoveRight->get_active() )
+ ScInsertContentsDlg::nPreviousMoveMode = INS_CELLSRIGHT;
+ }
+}
+
+ScPasteFunc ScInsertContentsDlg::GetFormulaCmdBits() const
+{
+ ScInsertContentsDlg::nPreviousFormulaChecks = ScPasteFunc::NONE;
+ if(mxRbAdd->get_active())
+ ScInsertContentsDlg::nPreviousFormulaChecks = ScPasteFunc::ADD;
+ else if(mxRbSub->get_active())
+ ScInsertContentsDlg::nPreviousFormulaChecks = ScPasteFunc::SUB;
+ else if(mxRbMul->get_active())
+ ScInsertContentsDlg::nPreviousFormulaChecks = ScPasteFunc::MUL;
+ else if(mxRbDiv->get_active())
+ ScInsertContentsDlg::nPreviousFormulaChecks = ScPasteFunc::DIV;
+ return ScInsertContentsDlg::nPreviousFormulaChecks;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/instbdlg.cxx b/sc/source/ui/miscdlgs/instbdlg.cxx
new file mode 100644
index 0000000000..e34b22a6f3
--- /dev/null
+++ b/sc/source/ui/miscdlgs/instbdlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/docinsert.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svtools/ehdl.hxx>
+#include <svtools/sfxecode.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <instbdlg.hxx>
+
+ScInsertTableDlg::ScInsertTableDlg(weld::Window* pParent, ScViewData& rData, SCTAB nTabCount, bool bFromFile)
+ : GenericDialogController(pParent, "modules/scalc/ui/insertsheet.ui", "InsertSheetDialog")
+ , aBrowseTimer("ScInsertTableDlg aBrowseTimer")
+ , rViewData(rData)
+ , rDoc(rData.GetDocument())
+ , pDocShTables(nullptr)
+ , bMustClose(false)
+ , nSelTabIndex(0)
+ , nTableCount(nTabCount)
+ , m_xBtnBefore(m_xBuilder->weld_radio_button("before"))
+ , m_xBtnNew(m_xBuilder->weld_radio_button("new"))
+ , m_xBtnFromFile(m_xBuilder->weld_radio_button("fromfile"))
+ , m_xFtCount(m_xBuilder->weld_label("countft"))
+ , m_xNfCount(m_xBuilder->weld_spin_button("countnf"))
+ , m_xFtName(m_xBuilder->weld_label("nameft"))
+ , m_xEdName(m_xBuilder->weld_entry("nameed"))
+ , m_xLbTables(m_xBuilder->weld_tree_view("tables"))
+ , m_xFtPath(m_xBuilder->weld_label("path"))
+ , m_xBtnBrowse(m_xBuilder->weld_button("browse"))
+ , m_xBtnLink(m_xBuilder->weld_check_button("link"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+{
+ m_sSheetDotDotDot = m_xEdName->get_text();
+ m_xLbTables->set_size_request(-1, m_xLbTables->get_height_rows(8));
+ Init_Impl(bFromFile);
+}
+
+ScInsertTableDlg::~ScInsertTableDlg()
+{
+ if (pDocShTables)
+ pDocShTables->DoClose();
+ pDocInserter.reset();
+}
+
+void ScInsertTableDlg::Init_Impl( bool bFromFile )
+{
+ m_xLbTables->set_selection_mode(SelectionMode::Multiple);
+ m_xBtnBrowse->connect_clicked( LINK( this, ScInsertTableDlg, BrowseHdl_Impl ) );
+ m_xBtnNew->connect_toggled( LINK( this, ScInsertTableDlg, ChoiceHdl_Impl ) );
+ m_xBtnFromFile->connect_toggled( LINK( this, ScInsertTableDlg, ChoiceHdl_Impl ) );
+ m_xLbTables->connect_changed( LINK( this, ScInsertTableDlg, SelectHdl_Impl ) );
+ m_xNfCount->connect_value_changed( LINK( this, ScInsertTableDlg, CountHdl_Impl));
+ m_xBtnOk->connect_clicked( LINK( this, ScInsertTableDlg, DoEnterHdl ));
+ m_xBtnBefore->set_active(true);
+
+ m_xNfCount->set_max(MAXTAB - rDoc.GetTableCount() + 1);
+ m_xNfCount->set_value(nTableCount);
+
+ if(nTableCount==1)
+ {
+ OUString aName;
+ rDoc.CreateValidTabName( aName );
+ m_xEdName->set_text( aName );
+ }
+ else
+ {
+ m_xEdName->set_text(m_sSheetDotDotDot);
+ m_xFtName->set_sensitive(false);
+ m_xEdName->set_sensitive(false);
+ }
+
+ bool bShared = rViewData.GetDocShell() && rViewData.GetDocShell()->IsDocShared();
+
+ if ( !bFromFile || bShared )
+ {
+ m_xBtnNew->set_active(true);
+ SetNewTable_Impl();
+ if ( bShared )
+ {
+ m_xBtnFromFile->set_sensitive(false);
+ }
+ }
+ else
+ {
+ m_xBtnFromFile->set_active(true);
+ SetFromTo_Impl();
+
+ aBrowseTimer.SetInvokeHandler( LINK( this, ScInsertTableDlg, BrowseTimeoutHdl ) );
+ aBrowseTimer.SetTimeout( 200 );
+ }
+}
+
+short ScInsertTableDlg::run()
+{
+ if (m_xBtnFromFile->get_active())
+ aBrowseTimer.Start();
+
+ return GenericDialogController::run();
+}
+
+void ScInsertTableDlg::SetNewTable_Impl()
+{
+ if (!m_xBtnNew->get_active() )
+ return;
+
+ m_xNfCount->set_sensitive(true);
+ m_xFtCount->set_sensitive(true);
+ m_xLbTables->set_sensitive(false);
+ m_xFtPath->set_sensitive(false);
+ m_xBtnBrowse->set_sensitive(false);
+ m_xBtnLink->set_sensitive(false);
+
+ if(nTableCount==1)
+ {
+ m_xEdName->set_sensitive(true);
+ m_xFtName->set_sensitive(true);
+ }
+}
+
+void ScInsertTableDlg::SetFromTo_Impl()
+{
+ if (m_xBtnFromFile->get_active() )
+ {
+ m_xEdName->set_sensitive(false);
+ m_xFtName->set_sensitive(false);
+ m_xFtCount->set_sensitive(false);
+ m_xNfCount->set_sensitive(false);
+ m_xLbTables->set_sensitive(true);
+ m_xFtPath->set_sensitive(true);
+ m_xBtnBrowse->set_sensitive(true);
+ m_xBtnLink->set_sensitive(true);
+ }
+}
+
+void ScInsertTableDlg::FillTables_Impl( const ScDocument* pSrcDoc )
+{
+ m_xLbTables->freeze();
+ m_xLbTables->clear();
+
+ if ( pSrcDoc )
+ {
+ SCTAB nCount = pSrcDoc->GetTableCount();
+ OUString aName;
+
+ for (SCTAB i=0; i<nCount; ++i)
+ {
+ pSrcDoc->GetName( i, aName );
+ m_xLbTables->append_text(aName);
+ }
+ }
+
+ m_xLbTables->thaw();
+
+ if (m_xLbTables->n_children() == 1)
+ m_xLbTables->select(0);
+}
+
+const OUString* ScInsertTableDlg::GetFirstTable( sal_uInt16* pN )
+{
+ const OUString* pStr = nullptr;
+
+ if ( m_xBtnNew->get_active() )
+ {
+ aStrCurSelTable = m_xEdName->get_text();
+ pStr = &aStrCurSelTable;
+ }
+ else
+ {
+ std::vector<int> aRows(m_xLbTables->get_selected_rows());
+ if (nSelTabIndex < aRows.size())
+ {
+ aStrCurSelTable = m_xLbTables->get_text(aRows[0]);
+ pStr = &aStrCurSelTable;
+ if ( pN )
+ *pN = aRows[0];
+ nSelTabIndex = 1;
+ }
+ }
+
+ return pStr;
+}
+
+const OUString* ScInsertTableDlg::GetNextTable( sal_uInt16* pN )
+{
+ if (m_xBtnNew->get_active())
+ return nullptr;
+
+ std::vector<int> aRows(m_xLbTables->get_selected_rows());
+
+ const OUString* pStr = nullptr;
+ if (nSelTabIndex < aRows.size())
+ {
+ aStrCurSelTable = m_xLbTables->get_text(aRows[nSelTabIndex]);
+ pStr = &aStrCurSelTable;
+ if ( pN )
+ *pN = aRows[nSelTabIndex];
+ nSelTabIndex++;
+ }
+
+ return pStr;
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScInsertTableDlg, CountHdl_Impl, weld::SpinButton&, void)
+{
+ nTableCount = static_cast<SCTAB>(m_xNfCount->get_value());
+ if ( nTableCount==1)
+ {
+ OUString aName;
+ rDoc.CreateValidTabName( aName );
+ m_xEdName->set_text( aName );
+ m_xFtName->set_sensitive(true);
+ m_xEdName->set_sensitive(true);
+ }
+ else
+ {
+ m_xEdName->set_text(m_sSheetDotDotDot);
+ m_xFtName->set_sensitive(false);
+ m_xEdName->set_sensitive(false);
+ }
+
+ DoEnable_Impl();
+}
+
+IMPL_LINK(ScInsertTableDlg, ChoiceHdl_Impl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+
+ if ( m_xBtnNew->get_active() )
+ SetNewTable_Impl();
+ else
+ SetFromTo_Impl();
+
+ DoEnable_Impl();
+}
+
+IMPL_LINK_NOARG(ScInsertTableDlg, BrowseHdl_Impl, weld::Button&, void)
+{
+ pDocInserter.reset();
+ pDocInserter.reset( new ::sfx2::DocumentInserter(m_xDialog.get(), ScDocShell::Factory().GetFactoryName()) );
+ pDocInserter->StartExecuteModal( LINK( this, ScInsertTableDlg, DialogClosedHdl ) );
+}
+
+IMPL_LINK_NOARG(ScInsertTableDlg, SelectHdl_Impl, weld::TreeView&, void)
+{
+ DoEnable_Impl();
+}
+
+void ScInsertTableDlg::DoEnable_Impl()
+{
+ if ( m_xBtnNew->get_active() || ( pDocShTables && m_xLbTables->count_selected_rows() ) )
+ m_xBtnOk->set_sensitive(true);
+ else
+ m_xBtnOk->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScInsertTableDlg, DoEnterHdl, weld::Button&, void)
+{
+ if (nTableCount > 1 || ScDocument::ValidTabName(m_xEdName->get_text()))
+ {
+ m_xDialog->response(RET_OK);
+ }
+ else
+ {
+ OUString aErrMsg ( ScResId( STR_INVALIDTABNAME ) );
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Warning,
+ VclButtonsType::Ok, aErrMsg));
+ xBox->run();
+ }
+}
+
+IMPL_LINK_NOARG(ScInsertTableDlg, BrowseTimeoutHdl, Timer *, void)
+{
+ bMustClose = true;
+ BrowseHdl_Impl(*m_xBtnBrowse);
+}
+
+IMPL_LINK( ScInsertTableDlg, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
+{
+ if ( ERRCODE_NONE == _pFileDlg->GetError() )
+ {
+ std::unique_ptr<SfxMedium> pMed = pDocInserter->CreateMedium();
+ if ( pMed )
+ {
+ // ERRCTX_SFX_OPENDOC -> "Error loading document"
+ SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, pMed->GetName() );
+
+ if ( pDocShTables )
+ pDocShTables->DoClose(); // deletion is done when assigning to the reference
+
+ pMed->UseInteractionHandler( true ); // to enable the filter options dialog
+
+ pDocShTables = new ScDocShell;
+ aDocShTablesRef = pDocShTables;
+
+ {
+ weld::WaitObject aWait(m_xDialog.get());
+ pDocShTables->DoLoad(pMed.release());
+ }
+
+ ErrCodeMsg nErr = pDocShTables->GetErrorCode();
+ if ( nErr )
+ ErrorHandler::HandleError(nErr, m_xDialog.get()); // warnings, too
+
+ if ( !pDocShTables->GetErrorIgnoreWarning() ) // errors only
+ {
+ FillTables_Impl( &pDocShTables->GetDocument() );
+ m_xFtPath->set_label(pDocShTables->GetTitle(SFX_TITLE_FULLNAME));
+ }
+ else
+ {
+ pDocShTables->DoClose();
+ aDocShTablesRef.clear();
+ pDocShTables = nullptr;
+
+ FillTables_Impl( nullptr );
+ m_xFtPath->set_label(OUString());
+ }
+ }
+
+ DoEnable_Impl();
+ }
+ else if ( bMustClose )
+ // execute slot FID_INS_TABLE_EXT and cancel file dialog
+ m_xDialog->response(RET_CANCEL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/lbseldlg.cxx b/sc/source/ui/miscdlgs/lbseldlg.cxx
new file mode 100644
index 0000000000..4c0b592c70
--- /dev/null
+++ b/sc/source/ui/miscdlgs/lbseldlg.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <lbseldlg.hxx>
+
+ScSelEntryDlg::ScSelEntryDlg(weld::Window* pParent, const std::vector<OUString> &rEntryList)
+ : GenericDialogController(pParent, "modules/scalc/ui/selectrange.ui", "SelectRangeDialog")
+ , m_xLb(m_xBuilder->weld_tree_view("treeview"))
+{
+ m_xLb->set_size_request(m_xLb->get_approximate_digit_width() * 32,
+ m_xLb->get_height_rows(8));
+ m_xLb->connect_row_activated(LINK(this, ScSelEntryDlg, DblClkHdl));
+
+ for (const auto& rEntry : rEntryList)
+ m_xLb->append_text(rEntry);
+
+ if (m_xLb->n_children() > 0)
+ m_xLb->select(0);
+}
+
+ScSelEntryDlg::~ScSelEntryDlg()
+{
+}
+
+OUString ScSelEntryDlg::GetSelectedEntry() const
+{
+ return m_xLb->get_selected_text();
+}
+
+IMPL_LINK_NOARG(ScSelEntryDlg, DblClkHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/linkarea.cxx b/sc/source/ui/miscdlgs/linkarea.cxx
new file mode 100644
index 0000000000..0b9dc129d7
--- /dev/null
+++ b/sc/source/ui/miscdlgs/linkarea.cxx
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docinsert.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svtools/ehdl.hxx>
+#include <svtools/inettbc.hxx>
+#include <svtools/sfxecode.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <dbdata.hxx>
+#include <linkarea.hxx>
+#include <docsh.hxx>
+#include <tablink.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+ScLinkedAreaDlg::ScLinkedAreaDlg(weld::Widget* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/externaldata.ui", "ExternalDataDialog")
+ , m_pSourceShell(nullptr)
+ , m_xCbUrl(new SvtURLBox(m_xBuilder->weld_combo_box("url")))
+ , m_xBtnBrowse(m_xBuilder->weld_button("browse"))
+ , m_xLbRanges(m_xBuilder->weld_tree_view("ranges"))
+ , m_xBtnReload(m_xBuilder->weld_check_button("reload"))
+ , m_xNfDelay(m_xBuilder->weld_spin_button("delay"))
+ , m_xFtSeconds(m_xBuilder->weld_label("secondsft"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+{
+ m_xLbRanges->set_selection_mode(SelectionMode::Multiple);
+
+ m_xCbUrl->connect_entry_activate(LINK(this, ScLinkedAreaDlg, FileHdl));
+ m_xBtnBrowse->connect_clicked(LINK( this, ScLinkedAreaDlg, BrowseHdl));
+ m_xLbRanges->connect_changed(LINK( this, ScLinkedAreaDlg, RangeHdl));
+ m_xLbRanges->set_size_request(m_xLbRanges->get_approximate_digit_width() * 54,
+ m_xLbRanges->get_height_rows(5));
+ m_xBtnReload->connect_toggled(LINK( this, ScLinkedAreaDlg, ReloadHdl));
+ UpdateEnable();
+}
+
+ScLinkedAreaDlg::~ScLinkedAreaDlg()
+{
+}
+
+constexpr OUString FILTERNAME_HTML = u"HTML (StarCalc)"_ustr;
+constexpr OUString FILTERNAME_QUERY = u"calc_HTML_WebQuery"_ustr;
+
+IMPL_LINK_NOARG(ScLinkedAreaDlg, BrowseHdl, weld::Button&, void)
+{
+ m_xDocInserter.reset( new sfx2::DocumentInserter(m_xDialog.get(), ScDocShell::Factory().GetFactoryName()) );
+ m_xDocInserter->StartExecuteModal( LINK( this, ScLinkedAreaDlg, DialogClosedHdl ) );
+}
+
+IMPL_LINK_NOARG(ScLinkedAreaDlg, FileHdl, weld::ComboBox&, bool)
+{
+ OUString aEntered = m_xCbUrl->GetURL();
+ if (m_pSourceShell)
+ {
+ SfxMedium* pMed = m_pSourceShell->GetMedium();
+ if ( aEntered == pMed->GetName() )
+ {
+ // already loaded - nothing to do
+ return true;
+ }
+ }
+
+ OUString aFilter;
+ OUString aOptions;
+ // get filter name by looking at the file content (bWithContent = true)
+ // Break operation if any error occurred inside.
+ if (!ScDocumentLoader::GetFilterName( aEntered, aFilter, aOptions, true, false ))
+ return true;
+
+ // #i53241# replace HTML filter with DataQuery filter
+ if (aFilter == FILTERNAME_HTML)
+ aFilter = FILTERNAME_QUERY;
+
+ LoadDocument( aEntered, aFilter, aOptions );
+
+ UpdateSourceRanges();
+ UpdateEnable();
+
+ return true;
+}
+
+void ScLinkedAreaDlg::LoadDocument( const OUString& rFile, const OUString& rFilter, const OUString& rOptions )
+{
+ if (m_pSourceShell)
+ {
+ // unload old document
+ m_pSourceShell->DoClose();
+ m_pSourceShell = nullptr;
+ aSourceRef.clear();
+ }
+
+ if ( rFile.isEmpty() )
+ return;
+
+ weld::WaitObject aWait(m_xDialog.get());
+
+ OUString aNewFilter = rFilter;
+ OUString aNewOptions = rOptions;
+
+ SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, rFile );
+
+ ScDocumentLoader aLoader( rFile, aNewFilter, aNewOptions, 0, m_xDialog.get() ); // with interaction
+ m_pSourceShell = aLoader.GetDocShell();
+ if (m_pSourceShell)
+ {
+ ErrCodeMsg nErr = m_pSourceShell->GetErrorCode();
+ if (nErr)
+ ErrorHandler::HandleError( nErr ); // including warnings
+
+ aSourceRef = m_pSourceShell;
+ aLoader.ReleaseDocRef(); // don't call DoClose in DocLoader dtor
+ }
+}
+
+void ScLinkedAreaDlg::InitFromOldLink( const OUString& rFile, const OUString& rFilter,
+ const OUString& rOptions, std::u16string_view rSource,
+ sal_Int32 nRefreshDelaySeconds )
+{
+ LoadDocument( rFile, rFilter, rOptions );
+ if (m_pSourceShell)
+ {
+ SfxMedium* pMed = m_pSourceShell->GetMedium();
+ m_xCbUrl->set_entry_text(pMed->GetName());
+ }
+ else
+ m_xCbUrl->set_entry_text(OUString());
+
+ UpdateSourceRanges();
+
+ if (!rSource.empty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ m_xLbRanges->select_text(OUString(o3tl::getToken(rSource, 0, ';', nIdx)));
+ }
+ while (nIdx>0);
+ }
+
+ bool bDoRefresh = (nRefreshDelaySeconds != 0);
+ m_xBtnReload->set_active(bDoRefresh);
+ if (bDoRefresh)
+ m_xNfDelay->set_value(nRefreshDelaySeconds);
+
+ UpdateEnable();
+}
+
+IMPL_LINK_NOARG(ScLinkedAreaDlg, RangeHdl, weld::TreeView&, void)
+{
+ UpdateEnable();
+}
+
+IMPL_LINK_NOARG(ScLinkedAreaDlg, ReloadHdl, weld::Toggleable&, void)
+{
+ UpdateEnable();
+}
+
+IMPL_LINK( ScLinkedAreaDlg, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
+{
+ if ( _pFileDlg->GetError() != ERRCODE_NONE )
+ return;
+
+ std::unique_ptr<SfxMedium> pMed = m_xDocInserter->CreateMedium();
+ if ( pMed )
+ {
+ weld::WaitObject aWait(m_xDialog.get());
+
+ // replace HTML filter with DataQuery filter
+ std::shared_ptr<const SfxFilter> pFilter = pMed->GetFilter();
+ if (pFilter && FILTERNAME_HTML == pFilter->GetFilterName())
+ {
+ std::shared_ptr<const SfxFilter> pNewFilter =
+ ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( FILTERNAME_QUERY );
+ if( pNewFilter )
+ pMed->SetFilter( pNewFilter );
+ }
+
+ // ERRCTX_SFX_OPENDOC -> "Error loading document"
+ SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, pMed->GetName() );
+
+ if (m_pSourceShell)
+ m_pSourceShell->DoClose(); // deleted when assigning aSourceRef
+
+ pMed->UseInteractionHandler( true ); // to enable the filter options dialog
+
+ m_pSourceShell = new ScDocShell;
+ aSourceRef = m_pSourceShell;
+ m_pSourceShell->DoLoad( pMed.get() );
+
+ ErrCodeMsg nErr = m_pSourceShell->GetErrorCode();
+ if (nErr)
+ ErrorHandler::HandleError( nErr ); // including warnings
+
+ if (!m_pSourceShell->GetErrorIgnoreWarning()) // only errors
+ {
+ m_xCbUrl->set_entry_text(pMed->GetName());
+ }
+ else
+ {
+ m_pSourceShell->DoClose();
+ m_pSourceShell = nullptr;
+ aSourceRef.clear();
+
+ m_xCbUrl->set_entry_text(OUString());
+ }
+ pMed.release(); // DoLoad takes ownership
+ }
+
+ UpdateSourceRanges();
+ UpdateEnable();
+}
+
+#undef FILTERNAME_HTML
+#undef FILTERNAME_QUERY
+
+void ScLinkedAreaDlg::UpdateSourceRanges()
+{
+ m_xLbRanges->freeze();
+
+ m_xLbRanges->clear();
+ if ( m_pSourceShell )
+ {
+ std::shared_ptr<const SfxFilter> pFilter = m_pSourceShell->GetMedium()->GetFilter();
+ if (pFilter && pFilter->GetFilterName() == SC_TEXT_CSV_FILTER_NAME)
+ {
+ // Insert dummy All range to have something selectable.
+ m_xLbRanges->append_text("CSV_all");
+ }
+
+ // tdf#142600 - list tables in order of their appearance in the document's source
+ const ScRangeName* pRangeName = m_pSourceShell->GetDocument().GetRangeName();
+ for (size_t i = 1; i <= pRangeName->index_size(); i++)
+ {
+ if (const ScRangeData* pRangeData = pRangeName->findByIndex(i))
+ {
+ m_xLbRanges->append_text(pRangeData->GetName());
+ }
+ }
+ // tdf#142600 - list database ranges
+ if (const auto pDBs = m_pSourceShell->GetDocument().GetDBCollection())
+ {
+ const auto& rNamedDBs = pDBs->getNamedDBs();
+ for (const auto& rNamedDB : rNamedDBs)
+ m_xLbRanges->append_text(rNamedDB->GetName());
+ }
+ }
+
+ m_xLbRanges->thaw();
+
+ if (m_xLbRanges->n_children() >= 1)
+ m_xLbRanges->select(0);
+ else
+ {
+ m_xLbRanges->append_text(ScResId(STR_NO_NAMED_RANGES_AVAILABLE));
+ m_xLbRanges->set_sensitive(false);
+ }
+}
+
+void ScLinkedAreaDlg::UpdateEnable()
+{
+ bool bEnable = ( m_pSourceShell && m_xLbRanges->count_selected_rows() );
+ m_xBtnOk->set_sensitive(bEnable);
+
+ bool bReload = m_xBtnReload->get_active();
+ m_xNfDelay->set_sensitive(bReload);
+ m_xFtSeconds->set_sensitive(bReload);
+}
+
+OUString ScLinkedAreaDlg::GetURL() const
+{
+ if (m_pSourceShell)
+ {
+ SfxMedium* pMed = m_pSourceShell->GetMedium();
+ return pMed->GetName();
+ }
+ return OUString();
+}
+
+OUString ScLinkedAreaDlg::GetFilter() const
+{
+ if (m_pSourceShell)
+ {
+ SfxMedium* pMed = m_pSourceShell->GetMedium();
+ return pMed->GetFilter()->GetFilterName();
+ }
+ return OUString();
+}
+
+OUString ScLinkedAreaDlg::GetOptions() const
+{
+ if (m_pSourceShell)
+ {
+ SfxMedium* pMed = m_pSourceShell->GetMedium();
+ return ScDocumentLoader::GetOptions( *pMed );
+ }
+ return OUString();
+}
+
+OUString ScLinkedAreaDlg::GetSource() const
+{
+ OUStringBuffer aBuf;
+ std::vector<OUString> aSelection = m_xLbRanges->get_selected_rows_text();
+ for (size_t i = 0; i < aSelection.size(); ++i)
+ {
+ if (i > 0)
+ aBuf.append(';');
+ aBuf.append(aSelection[i]);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+sal_Int32 ScLinkedAreaDlg::GetRefreshDelaySeconds() const
+{
+ if (m_xBtnReload->get_active())
+ return m_xNfDelay->get_value();
+ else
+ return 0; // disabled
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/mergecellsdialog.cxx b/sc/source/ui/miscdlgs/mergecellsdialog.cxx
new file mode 100644
index 0000000000..2bfcc5da3b
--- /dev/null
+++ b/sc/source/ui/miscdlgs/mergecellsdialog.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 <mergecellsdialog.hxx>
+
+ScMergeCellsDialog::ScMergeCellsDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/mergecellsdialog.ui", "MergeCellsDialog")
+ , m_xRBMoveContent(m_xBuilder->weld_radio_button("move-cells-radio"))
+ , m_xRBKeepContent(m_xBuilder->weld_radio_button("keep-content-radio"))
+ , m_xRBEmptyContent(m_xBuilder->weld_radio_button("empty-cells-radio"))
+{
+ m_xRBKeepContent->set_active(true);
+}
+
+ScMergeCellsDialog::~ScMergeCellsDialog() {}
+
+ScMergeCellsOption ScMergeCellsDialog::GetMergeCellsOption() const
+{
+ if (m_xRBMoveContent->get_active())
+ return MoveContentHiddenCells;
+ if (m_xRBKeepContent->get_active())
+ return KeepContentHiddenCells;
+ if (m_xRBEmptyContent->get_active())
+ return EmptyContentHiddenCells;
+ assert(!"Unknown selection for merge cells.");
+ return KeepContentHiddenCells; // default value
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/mtrindlg.cxx b/sc/source/ui/miscdlgs/mtrindlg.cxx
new file mode 100644
index 0000000000..a203a3710d
--- /dev/null
+++ b/sc/source/ui/miscdlgs/mtrindlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <mtrindlg.hxx>
+
+ScMetricInputDlg::ScMetricInputDlg( weld::Window* pParent,
+ const OUString& sDialogName,
+ tools::Long nCurrent,
+ tools::Long nDefault,
+ FieldUnit eFUnit,
+ sal_uInt16 nDecimals,
+ tools::Long nMaximum,
+ tools::Long nMinimum)
+
+ : GenericDialogController(pParent, "modules/scalc/ui/" + sDialogName.toAsciiLowerCase() + ".ui", sDialogName)
+ , m_xEdValue(m_xBuilder->weld_metric_spin_button("value", FieldUnit::CM))
+ , m_xBtnDefVal(m_xBuilder->weld_check_button("default"))
+{
+ m_xBtnDefVal->connect_toggled(LINK(this, ScMetricInputDlg, SetDefValHdl));
+ m_xEdValue->connect_value_changed(LINK( this, ScMetricInputDlg, ModifyHdl));
+
+ m_xEdValue->set_unit(eFUnit);
+ m_xEdValue->set_digits(nDecimals);
+ m_xEdValue->set_range(m_xEdValue->normalize(nMinimum),
+ m_xEdValue->normalize(nMaximum), FieldUnit::TWIP);
+
+ sal_Int64 nMin(0), nMax(0);
+ m_xEdValue->get_range(nMin, nMax, FieldUnit::TWIP);
+
+ auto nIncrement = m_xEdValue->normalize(1);
+ m_xEdValue->set_increments(nIncrement / 10, nIncrement, FieldUnit::NONE);
+ m_xEdValue->set_value(m_xEdValue->normalize(nDefault), FieldUnit::TWIP);
+ nDefaultValue = m_xEdValue->get_value(FieldUnit::NONE);
+ m_xEdValue->set_value(m_xEdValue->normalize(nCurrent), FieldUnit::TWIP);
+ nCurrentValue = m_xEdValue->get_value(FieldUnit::NONE);
+ m_xBtnDefVal->set_active(nCurrentValue == nDefaultValue);
+}
+
+ScMetricInputDlg::~ScMetricInputDlg()
+{
+}
+
+int ScMetricInputDlg::GetInputValue() const
+{
+/*
+ with decimal digits
+
+ double nVal = m_xEdValue->GetValue( eUnit );
+ sal_uInt16 nDecs = m_xEdValue->GetDecimalDigits();
+ double nFactor = 0.0;
+
+ // static long ImpPower10( sal_uInt16 nDecs )
+ {
+ nFactor = 1.0;
+
+ for ( sal_uInt16 i=0; i < nDecs; i++ )
+ nFactor *= 10.0;
+ }
+
+ return nVal / nFactor;
+*/
+ // first cut off the decimal digits - not that great...
+
+ return m_xEdValue->denormalize(m_xEdValue->get_value(FieldUnit::TWIP));
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScMetricInputDlg, SetDefValHdl, weld::Toggleable&, void)
+{
+ if (m_xBtnDefVal->get_active())
+ {
+ nCurrentValue = m_xEdValue->get_value(FieldUnit::NONE);
+ m_xEdValue->set_value(nDefaultValue, FieldUnit::NONE);
+ }
+ else
+ m_xEdValue->set_value(nCurrentValue, FieldUnit::NONE);
+}
+
+IMPL_LINK_NOARG(ScMetricInputDlg, ModifyHdl, weld::MetricSpinButton&, void)
+{
+ m_xBtnDefVal->set_active(nDefaultValue == m_xEdValue->get_value(FieldUnit::NONE));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/mvtabdlg.cxx b/sc/source/ui/miscdlgs/mvtabdlg.cxx
new file mode 100644
index 0000000000..afacdc48b4
--- /dev/null
+++ b/sc/source/ui/miscdlgs/mvtabdlg.cxx
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <mvtabdlg.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <comphelper/lok.hxx>
+#include <utility>
+#include <tabvwsh.hxx>
+
+ScMoveTableDlg::ScMoveTableDlg(weld::Window* pParent, OUString aDefault)
+ : GenericDialogController(pParent, "modules/scalc/ui/movecopysheet.ui", "MoveCopySheetDialog")
+ , maDefaultName(std::move(aDefault))
+ , mnCurrentDocPos(0)
+ , nDocument(0)
+ , nTable(0)
+ , bCopyTable(false)
+ , bRenameTable(false)
+ , mbEverEdited(false)
+ , m_xBtnMove(m_xBuilder->weld_radio_button("move"))
+ , m_xBtnCopy(m_xBuilder->weld_radio_button("copy"))
+ , m_xFtDoc(m_xBuilder->weld_label("toDocumentLabel"))
+ , m_xLbDoc(m_xBuilder->weld_combo_box("toDocument"))
+ , m_xLbTable(m_xBuilder->weld_tree_view("insertBefore"))
+ , m_xEdTabName(m_xBuilder->weld_entry("newName"))
+ , m_xFtWarn(m_xBuilder->weld_label("newNameWarn"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xUnusedLabel(m_xBuilder->weld_label("warnunused"))
+ , m_xEmptyLabel(m_xBuilder->weld_label("warnempty"))
+ , m_xInvalidLabel(m_xBuilder->weld_label("warninvalid"))
+{
+ assert(m_xLbDoc->get_count() == 2);
+ msCurrentDoc = m_xLbDoc->get_text(0);
+ msNewDoc = m_xLbDoc->get_text(1);
+ m_xLbDoc->clear();
+ assert(m_xLbDoc->get_count() == 0);
+
+ m_xLbTable->set_size_request(-1, m_xLbTable->get_height_rows(8));
+
+ msStrTabNameUsed = m_xUnusedLabel->get_label();
+ msStrTabNameEmpty = m_xEmptyLabel->get_label();
+ msStrTabNameInvalid = m_xInvalidLabel->get_label();
+
+ Init();
+}
+
+ScMoveTableDlg::~ScMoveTableDlg() {}
+
+void ScMoveTableDlg::GetTabNameString(OUString& rString) const
+{
+ rString = m_xEdTabName->get_text();
+}
+
+void ScMoveTableDlg::SetForceCopyTable()
+{
+ m_xBtnCopy->set_active(true);
+ m_xBtnMove->set_sensitive(false);
+ SetOkBtnLabel();
+}
+
+void ScMoveTableDlg::EnableRenameTable(bool bFlag)
+{
+ bRenameTable = bFlag;
+ m_xEdTabName->set_sensitive(bFlag);
+ ResetRenameInput();
+}
+
+void ScMoveTableDlg::ResetRenameInput()
+{
+ if (mbEverEdited)
+ {
+ // Don't reset the name when the sheet name has ever been edited.
+ // But check the name, as this is also called for change of copy/move
+ // buttons and document listbox selection.
+ CheckNewTabName();
+ return;
+ }
+
+ if (!m_xEdTabName->get_sensitive())
+ {
+ m_xEdTabName->set_text(OUString());
+ return;
+ }
+
+ bool bVal = m_xBtnCopy->get_active();
+ if (bVal)
+ {
+ // copy
+ ScDocument* pDoc = GetSelectedDoc();
+ if (pDoc)
+ {
+ OUString aStr = maDefaultName;
+ pDoc->CreateValidTabName(aStr);
+ m_xEdTabName->set_text(aStr);
+ }
+ else
+ m_xEdTabName->set_text(maDefaultName);
+ }
+ else
+ {
+ // move
+ m_xEdTabName->set_text(maDefaultName);
+ }
+
+ CheckNewTabName();
+}
+
+void ScMoveTableDlg::CheckNewTabName()
+{
+ const OUString aNewName = m_xEdTabName->get_text();
+ if (aNewName.isEmpty())
+ {
+ // New sheet name is empty. This is not good.
+ m_xFtWarn->show();
+ //TODO m_xFtWarn->SetControlBackground(COL_YELLOW);
+ m_xFtWarn->set_label(msStrTabNameEmpty);
+ m_xBtnOk->set_sensitive(false);
+ return;
+ }
+
+ if (!ScDocument::ValidTabName(aNewName))
+ {
+ // New sheet name contains invalid characters.
+ m_xFtWarn->show();
+ //TODO m_xFtWarn->SetControlBackground(COL_YELLOW);
+ m_xFtWarn->set_label(msStrTabNameInvalid);
+ m_xBtnOk->set_sensitive(false);
+ return;
+ }
+
+ bool bMoveInCurrentDoc = m_xBtnMove->get_active() && m_xLbDoc->get_active() == mnCurrentDocPos;
+ bool bFound = false;
+ const int nLast = m_xLbTable->n_children();
+ for (int i = 0; i < nLast && !bFound; ++i)
+ {
+ if (aNewName == m_xLbTable->get_text(i))
+ {
+ // Only for move within same document the same name is allowed.
+ if (!bMoveInCurrentDoc || maDefaultName != m_xEdTabName->get_text())
+ bFound = true;
+ }
+ }
+
+ if (bFound)
+ {
+ m_xFtWarn->show();
+ //TODO m_xFtWarn->SetControlBackground(COL_YELLOW);
+ m_xFtWarn->set_label(msStrTabNameUsed);
+ m_xBtnOk->set_sensitive(false);
+ }
+ else
+ {
+ m_xFtWarn->hide();
+ //TODO m_xFtWarn->SetControlBackground();
+ m_xFtWarn->set_label(OUString());
+ m_xBtnOk->set_sensitive(true);
+ }
+}
+
+ScDocument* ScMoveTableDlg::GetSelectedDoc()
+{
+ return weld::fromId<ScDocument*>(m_xLbDoc->get_active_id());
+}
+
+void ScMoveTableDlg::Init()
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScMoveTableDlg, OkHdl));
+ m_xLbDoc->connect_changed(LINK(this, ScMoveTableDlg, SelHdl));
+ m_xBtnCopy->connect_toggled(LINK(this, ScMoveTableDlg, CheckBtnHdl));
+ m_xBtnMove->connect_toggled(LINK(this, ScMoveTableDlg, CheckBtnHdl));
+ m_xEdTabName->connect_changed(LINK(this, ScMoveTableDlg, CheckNameHdl));
+
+ // tdf#96854 - remember last used option for copy/move sheet
+ const bool bIsCopyActive
+ = ScTabViewShell::GetActiveViewShell()->GetViewData().GetOptions().GetOption(
+ VOPT_COPY_SHEET);
+ m_xBtnMove->set_active(!bIsCopyActive);
+ m_xBtnCopy->set_active(bIsCopyActive);
+ m_xEdTabName->set_sensitive(false);
+ m_xFtWarn->hide();
+ InitDocListBox();
+ SelHdl(*m_xLbDoc);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ m_xFtDoc->hide();
+ m_xLbDoc->hide();
+ }
+ SetOkBtnLabel();
+}
+
+void ScMoveTableDlg::InitDocListBox()
+{
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ ScDocShell* pScSh = nullptr;
+ sal_uInt16 nSelPos = 0;
+ sal_uInt16 i = 0;
+
+ m_xLbDoc->clear();
+ m_xLbDoc->freeze();
+
+ while (pSh)
+ {
+ pScSh = dynamic_cast<ScDocShell*>(pSh);
+
+ if (pScSh)
+ {
+ OUString aEntryName = pScSh->GetTitle();
+
+ if (pScSh == SfxObjectShell::Current())
+ {
+ mnCurrentDocPos = nSelPos = i;
+ aEntryName += " " + msCurrentDoc;
+ }
+
+ OUString sId(weld::toId(&pScSh->GetDocument()));
+ m_xLbDoc->insert(i, aEntryName, &sId, nullptr, nullptr);
+
+ i++;
+ }
+ pSh = SfxObjectShell::GetNext(*pSh);
+ }
+
+ m_xLbDoc->thaw();
+ m_xLbDoc->append_text(msNewDoc);
+ m_xLbDoc->set_active(nSelPos);
+}
+
+void ScMoveTableDlg::SetOkBtnLabel()
+{
+ const bool bIsCopyActive = m_xBtnCopy->get_active();
+ // tdf#139464 Write "Copy" or "Move" on OK button
+ m_xBtnOk->set_label(bIsCopyActive ? m_xBtnCopy->get_label() : m_xBtnMove->get_label());
+ // tdf#96854 - remember last used option for copy/move sheet
+ ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
+ ScViewOptions aViewOpt(pScViewShell->GetViewData().GetOptions());
+ aViewOpt.SetOption(VOPT_COPY_SHEET, bIsCopyActive);
+ pScViewShell->GetViewData().SetOptions(aViewOpt);
+}
+
+// Handler:
+
+IMPL_LINK(ScMoveTableDlg, CheckBtnHdl, weld::Toggleable&, rBtn, void)
+{
+ if (&rBtn == m_xBtnCopy.get())
+ ResetRenameInput();
+ SetOkBtnLabel();
+}
+
+IMPL_LINK_NOARG(ScMoveTableDlg, OkHdl, weld::Button&, void)
+{
+ const sal_Int32 nDocSel = m_xLbDoc->get_active();
+ const sal_Int32 nDocLast = m_xLbDoc->get_count() - 1;
+ const sal_Int32 nTabSel = m_xLbTable->get_selected_index();
+ const sal_Int32 nTabLast = m_xLbTable->n_children() - 1;
+
+ nDocument = (nDocSel != nDocLast) ? nDocSel : SC_DOC_NEW;
+ nTable = (nTabSel != nTabLast) ? static_cast<SCTAB>(nTabSel) : SC_TAB_APPEND;
+ bCopyTable = m_xBtnCopy->get_active();
+
+ if (bCopyTable)
+ {
+ // Return an empty string when the new name is the same as the
+ // automatic name assigned by the document.
+ OUString aCopyName = maDefaultName;
+ ScDocument* pDoc = GetSelectedDoc();
+ if (pDoc)
+ pDoc->CreateValidTabName(aCopyName);
+ if (aCopyName == m_xEdTabName->get_text())
+ m_xEdTabName->set_text(OUString());
+ }
+ else
+ {
+ // Return an empty string, when the new name is the same as the
+ // original name.
+ if (maDefaultName == m_xEdTabName->get_text())
+ m_xEdTabName->set_text(OUString());
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ScMoveTableDlg, SelHdl, weld::ComboBox&, void)
+{
+ ScDocument* pDoc = GetSelectedDoc();
+ OUString aName;
+
+ m_xLbTable->clear();
+ m_xLbTable->freeze();
+ if (pDoc)
+ {
+ SCTAB nLast = pDoc->GetTableCount() - 1;
+ for (SCTAB i = 0; i <= nLast; ++i)
+ {
+ pDoc->GetName(i, aName);
+ m_xLbTable->append_text(aName);
+ }
+ }
+ m_xLbTable->append_text(ScResId(STR_MOVE_TO_END));
+ m_xLbTable->thaw();
+ m_xLbTable->select(0);
+ ResetRenameInput();
+}
+
+IMPL_LINK_NOARG(ScMoveTableDlg, CheckNameHdl, weld::Entry&, void)
+{
+ mbEverEdited = true;
+ CheckNewTabName();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/namecrea.cxx b/sc/source/ui/miscdlgs/namecrea.cxx
new file mode 100644
index 0000000000..334b89d8ba
--- /dev/null
+++ b/sc/source/ui/miscdlgs/namecrea.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <namecrea.hxx>
+
+ScNameCreateDlg::ScNameCreateDlg(weld::Window* pParent, CreateNameFlags nFlags)
+ : GenericDialogController(pParent, "modules/scalc/ui/createnamesdialog.ui", "CreateNamesDialog")
+ , m_xTopBox(m_xBuilder->weld_check_button("top"))
+ , m_xLeftBox(m_xBuilder->weld_check_button("left"))
+ , m_xBottomBox(m_xBuilder->weld_check_button("bottom"))
+ , m_xRightBox(m_xBuilder->weld_check_button("right"))
+{
+ m_xTopBox->set_active(bool(nFlags & CreateNameFlags::Top));
+ m_xLeftBox->set_active(bool(nFlags & CreateNameFlags::Left));
+ m_xBottomBox->set_active(bool(nFlags & CreateNameFlags::Bottom));
+ m_xRightBox->set_active(bool(nFlags & CreateNameFlags::Right));
+}
+
+ScNameCreateDlg::~ScNameCreateDlg() {}
+
+CreateNameFlags ScNameCreateDlg::GetFlags() const
+{
+ CreateNameFlags nResult = CreateNameFlags::NONE;
+
+ if (m_xTopBox->get_active())
+ nResult |= CreateNameFlags::Top;
+ if (m_xLeftBox->get_active())
+ nResult |= CreateNameFlags::Left;
+ if (m_xBottomBox->get_active())
+ nResult |= CreateNameFlags::Bottom;
+ if (m_xRightBox->get_active())
+ nResult |= CreateNameFlags::Right;
+
+ return nResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/optsolver.cxx b/sc/source/ui/miscdlgs/optsolver.cxx
new file mode 100644
index 0000000000..bf40b00920
--- /dev/null
+++ b/sc/source/ui/miscdlgs/optsolver.cxx
@@ -0,0 +1,1124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rangelst.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/numformat.hxx>
+#include <utility>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+
+#include <reffact.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <rangeutl.hxx>
+#include <convuno.hxx>
+#include <unonames.hxx>
+#include <solveroptions.hxx>
+#include <solverutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <comphelper/sequence.hxx>
+#include <optsolver.hxx>
+#include <table.hxx>
+
+#include <com/sun/star/sheet/SolverConstraint.hpp>
+#include <com/sun/star/sheet/SolverConstraintOperator.hpp>
+#include <com/sun/star/sheet/XSolverDescription.hpp>
+#include <com/sun/star/sheet/XSolver.hpp>
+
+using namespace com::sun::star;
+
+ScSolverProgressDialog::ScSolverProgressDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/solverprogressdialog.ui",
+ "SolverProgressDialog")
+ , m_xFtTime(m_xBuilder->weld_label("progress"))
+{
+}
+
+ScSolverProgressDialog::~ScSolverProgressDialog()
+{
+}
+
+void ScSolverProgressDialog::HideTimeLimit()
+{
+ m_xFtTime->hide();
+}
+
+void ScSolverProgressDialog::SetTimeLimit( sal_Int32 nSeconds )
+{
+ OUString aOld = m_xFtTime->get_label();
+ OUString aNew = aOld.replaceFirst("#", OUString::number(nSeconds));
+ m_xFtTime->set_label(aNew);
+}
+
+ScSolverNoSolutionDialog::ScSolverNoSolutionDialog(weld::Window* pParent, const OUString& rErrorText)
+ : GenericDialogController(pParent, "modules/scalc/ui/nosolutiondialog.ui", "NoSolutionDialog")
+ , m_xFtErrorText(m_xBuilder->weld_label("error"))
+{
+ m_xFtErrorText->set_label(rErrorText);
+}
+
+ScSolverNoSolutionDialog::~ScSolverNoSolutionDialog()
+{
+}
+
+ScSolverSuccessDialog::ScSolverSuccessDialog(weld::Window* pParent, std::u16string_view rSolution)
+ : GenericDialogController(pParent, "modules/scalc/ui/solversuccessdialog.ui", "SolverSuccessDialog")
+ , m_xFtResult(m_xBuilder->weld_label("result"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScSolverSuccessDialog, ClickHdl));
+ m_xBtnCancel->connect_clicked(LINK(this, ScSolverSuccessDialog, ClickHdl));
+ OUString aMessage = m_xFtResult->get_label() + " " + rSolution;
+ m_xFtResult->set_label(aMessage);
+}
+
+ScSolverSuccessDialog::~ScSolverSuccessDialog()
+{
+}
+
+IMPL_LINK(ScSolverSuccessDialog, ClickHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnOk.get())
+ m_xDialog->response(RET_OK);
+ else
+ m_xDialog->response(RET_CANCEL);
+}
+
+ScCursorRefEdit::ScCursorRefEdit(std::unique_ptr<weld::Entry> xControl)
+ : formula::RefEdit(std::move(xControl))
+{
+ xEntry->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one
+ xEntry->connect_key_press(LINK(this, ScCursorRefEdit, KeyInputHdl));
+}
+
+void ScCursorRefEdit::SetCursorLinks( const Link<ScCursorRefEdit&,void>& rUp, const Link<ScCursorRefEdit&,void>& rDown )
+{
+ maCursorUpLink = rUp;
+ maCursorDownLink = rDown;
+}
+
+IMPL_LINK(ScCursorRefEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ bool bUp = (aCode.GetCode() == KEY_UP);
+ bool bDown = (aCode.GetCode() == KEY_DOWN);
+ if ( !aCode.IsShift() && !aCode.IsMod1() && !aCode.IsMod2() && ( bUp || bDown ) )
+ {
+ if ( bUp )
+ maCursorUpLink.Call( *this );
+ else
+ maCursorDownLink.Call( *this );
+ return true;
+ }
+ return formula::RefEdit::KeyInput(rKEvt);
+}
+
+ScOptSolverDlg::ScOptSolverDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScDocShell* pDocSh, const ScAddress& aCursorPos)
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/solverdlg.ui", "SolverDialog")
+ , maInputError(ScResId(STR_INVALIDINPUT))
+ , maConditionError(ScResId(STR_INVALIDCONDITION))
+
+ , mpDocShell(pDocSh)
+ , mrDoc(pDocSh->GetDocument())
+ , mnCurTab(aCursorPos.Tab())
+ , mbDlgLostFocus(false)
+ , nScrollPos(0)
+ , mpEdActive(nullptr)
+ , m_xFtObjectiveCell(m_xBuilder->weld_label("targetlabel"))
+ , m_xEdObjectiveCell(new formula::RefEdit(m_xBuilder->weld_entry("targetedit")))
+ , m_xRBObjectiveCell(new formula::RefButton(m_xBuilder->weld_button("targetbutton")))
+ , m_xRbMax(m_xBuilder->weld_radio_button("max"))
+ , m_xRbMin(m_xBuilder->weld_radio_button("min"))
+ , m_xRbValue(m_xBuilder->weld_radio_button("value"))
+ , m_xEdTargetValue(new formula::RefEdit(m_xBuilder->weld_entry("valueedit")))
+ , m_xRBTargetValue(new formula::RefButton(m_xBuilder->weld_button("valuebutton")))
+ , m_xFtVariableCells(m_xBuilder->weld_label("changelabel"))
+ , m_xEdVariableCells(new formula::RefEdit(m_xBuilder->weld_entry("changeedit")))
+ , m_xRBVariableCells(new formula::RefButton(m_xBuilder->weld_button("changebutton")))
+ , m_xFtCellRef(m_xBuilder->weld_label("cellreflabel"))
+ , m_xEdLeft1(new ScCursorRefEdit(m_xBuilder->weld_entry("ref1edit")))
+ , m_xRBLeft1(new formula::RefButton(m_xBuilder->weld_button("ref1button")))
+ , m_xLbOp1(m_xBuilder->weld_combo_box("op1list"))
+ , m_xFtConstraint(m_xBuilder->weld_label("constraintlabel"))
+ , m_xEdRight1(new ScCursorRefEdit(m_xBuilder->weld_entry("val1edit")))
+ , m_xRBRight1(new formula::RefButton(m_xBuilder->weld_button("val1button")))
+ , m_xBtnDel1(m_xBuilder->weld_button("del1"))
+ , m_xEdLeft2(new ScCursorRefEdit(m_xBuilder->weld_entry("ref2edit")))
+ , m_xRBLeft2(new formula::RefButton(m_xBuilder->weld_button("ref2button")))
+ , m_xLbOp2(m_xBuilder->weld_combo_box("op2list"))
+ , m_xEdRight2(new ScCursorRefEdit(m_xBuilder->weld_entry("val2edit")))
+ , m_xRBRight2(new formula::RefButton(m_xBuilder->weld_button("val2button")))
+ , m_xBtnDel2(m_xBuilder->weld_button("del2"))
+ , m_xEdLeft3(new ScCursorRefEdit(m_xBuilder->weld_entry("ref3edit")))
+ , m_xRBLeft3(new formula::RefButton(m_xBuilder->weld_button("ref3button")))
+ , m_xLbOp3(m_xBuilder->weld_combo_box("op3list"))
+ , m_xEdRight3(new ScCursorRefEdit(m_xBuilder->weld_entry("val3edit")))
+ , m_xRBRight3(new formula::RefButton(m_xBuilder->weld_button("val3button")))
+ , m_xBtnDel3(m_xBuilder->weld_button("del3"))
+ , m_xEdLeft4(new ScCursorRefEdit(m_xBuilder->weld_entry("ref4edit")))
+ , m_xRBLeft4(new formula::RefButton(m_xBuilder->weld_button("ref4button")))
+ , m_xLbOp4(m_xBuilder->weld_combo_box("op4list"))
+ , m_xEdRight4(new ScCursorRefEdit(m_xBuilder->weld_entry("val4edit")))
+ , m_xRBRight4(new formula::RefButton(m_xBuilder->weld_button("val4button")))
+ , m_xBtnDel4(m_xBuilder->weld_button("del4"))
+ , m_xScrollBar(m_xBuilder->weld_scrolled_window("scrollbar", true))
+ , m_xBtnOpt(m_xBuilder->weld_button("options"))
+ , m_xBtnClose(m_xBuilder->weld_button("close"))
+ , m_xBtnSolve(m_xBuilder->weld_button("ok"))
+ , m_xBtnResetAll(m_xBuilder->weld_button("resetall"))
+ , m_xResultFT(m_xBuilder->weld_label("result"))
+ , m_xContents(m_xBuilder->weld_widget("grid"))
+ , m_pSolverSettings(mrDoc.FetchTable(mnCurTab)->GetSolverSettings())
+{
+ m_xEdObjectiveCell->SetReferences(this, m_xFtObjectiveCell.get());
+ m_xRBObjectiveCell->SetReferences(this, m_xEdObjectiveCell.get());
+ m_xEdTargetValue->SetReferences(this, m_xResultFT.get());
+ m_xRBTargetValue->SetReferences(this, m_xEdTargetValue.get());
+ m_xEdVariableCells->SetReferences(this, m_xFtVariableCells.get());
+ m_xRBVariableCells->SetReferences(this, m_xEdVariableCells.get());
+ m_xEdLeft1->SetReferences(this, m_xFtCellRef.get());
+ m_xRBLeft1->SetReferences(this, m_xEdLeft1.get());
+ m_xEdRight1->SetReferences(this, m_xFtConstraint.get());
+ m_xRBRight1->SetReferences(this, m_xEdRight1.get());
+ m_xEdLeft2->SetReferences(this, m_xFtCellRef.get());
+ m_xRBLeft2->SetReferences(this, m_xEdLeft2.get());
+ m_xEdRight2->SetReferences(this, m_xFtConstraint.get());
+ m_xRBRight2->SetReferences(this, m_xEdRight2.get());
+ m_xEdLeft3->SetReferences(this, m_xFtCellRef.get());
+ m_xRBLeft3->SetReferences(this, m_xEdLeft3.get());
+ m_xEdRight3->SetReferences(this, m_xFtConstraint.get());
+ m_xRBRight3->SetReferences(this, m_xEdRight3.get());
+ m_xEdLeft4->SetReferences(this, m_xFtCellRef.get());
+ m_xRBLeft4->SetReferences(this, m_xEdLeft4.get());
+ m_xEdRight4->SetReferences(this, m_xFtConstraint.get());
+ m_xRBRight4->SetReferences(this, m_xEdRight4.get());
+
+ mpLeftEdit[0] = m_xEdLeft1.get();
+ mpLeftButton[0] = m_xRBLeft1.get();
+ mpRightEdit[0] = m_xEdRight1.get();
+ mpRightButton[0] = m_xRBRight1.get();
+ mpOperator[0] = m_xLbOp1.get();
+ mpDelButton[0] = m_xBtnDel1.get();
+
+ mpLeftEdit[1] = m_xEdLeft2.get();
+ mpLeftButton[1] = m_xRBLeft2.get();
+ mpRightEdit[1] = m_xEdRight2.get();
+ mpRightButton[1] = m_xRBRight2.get();
+ mpOperator[1] = m_xLbOp2.get();
+ mpDelButton[1] = m_xBtnDel2.get();
+
+ mpLeftEdit[2] = m_xEdLeft3.get();
+ mpLeftButton[2] = m_xRBLeft3.get();
+ mpRightEdit[2] = m_xEdRight3.get();
+ mpRightButton[2] = m_xRBRight3.get();
+ mpOperator[2] = m_xLbOp3.get();
+ mpDelButton[2] = m_xBtnDel3.get();
+
+ mpLeftEdit[3] = m_xEdLeft4.get();
+ mpLeftButton[3] = m_xRBLeft4.get();
+ mpRightEdit[3] = m_xEdRight4.get();
+ mpRightButton[3] = m_xRBRight4.get();
+ mpOperator[3] = m_xLbOp4.get();
+ mpDelButton[3] = m_xBtnDel4.get();
+
+ Init( aCursorPos );
+}
+
+ScOptSolverDlg::~ScOptSolverDlg()
+{
+}
+
+void ScOptSolverDlg::Init(const ScAddress& rCursorPos)
+{
+ uno::Reference<frame::XFrame> xFrame = GetBindings().GetActiveFrame();
+ auto xDelNm = vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:DeleteRows", xFrame);
+ for (weld::Button* pButton : mpDelButton)
+ pButton->set_image(xDelNm);
+
+ m_xBtnOpt->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
+ m_xBtnClose->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
+ m_xBtnSolve->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
+ m_xBtnResetAll->connect_clicked( LINK( this, ScOptSolverDlg, BtnHdl ) );
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScOptSolverDlg, GetEditFocusHdl );
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScOptSolverDlg, GetButtonFocusHdl );
+ m_xEdObjectiveCell->SetGetFocusHdl( aEditLink );
+ m_xRBObjectiveCell->SetGetFocusHdl( aButtonLink );
+ m_xEdTargetValue->SetGetFocusHdl( aEditLink );
+ m_xRBTargetValue->SetGetFocusHdl( aButtonLink );
+ m_xEdVariableCells->SetGetFocusHdl( aEditLink );
+ m_xRBVariableCells->SetGetFocusHdl( aButtonLink );
+ Link<weld::Widget&,void> aLink = LINK(this, ScOptSolverDlg, GetFocusHdl);
+ m_xRbValue->connect_focus_in(aLink);
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ mpLeftEdit[nRow]->SetGetFocusHdl( aEditLink );
+ mpLeftButton[nRow]->SetGetFocusHdl( aButtonLink );
+ mpRightEdit[nRow]->SetGetFocusHdl( aEditLink );
+ mpRightButton[nRow]->SetGetFocusHdl( aButtonLink );
+ mpOperator[nRow]->connect_focus_in(aLink);
+ }
+
+ aEditLink = LINK( this, ScOptSolverDlg, LoseEditFocusHdl );
+ aButtonLink = LINK( this, ScOptSolverDlg, LoseButtonFocusHdl );
+ m_xEdObjectiveCell->SetLoseFocusHdl( aEditLink );
+ m_xRBObjectiveCell->SetLoseFocusHdl( aButtonLink );
+ m_xEdTargetValue->SetLoseFocusHdl( aEditLink );
+ m_xRBTargetValue-> SetLoseFocusHdl( aButtonLink );
+ m_xEdVariableCells->SetLoseFocusHdl( aEditLink );
+ m_xRBVariableCells->SetLoseFocusHdl( aButtonLink );
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ mpLeftEdit[nRow]->SetLoseFocusHdl( aEditLink );
+ mpLeftButton[nRow]->SetLoseFocusHdl( aButtonLink );
+ mpRightEdit[nRow]->SetLoseFocusHdl( aEditLink );
+ mpRightButton[nRow]->SetLoseFocusHdl( aButtonLink );
+ }
+
+ Link<ScCursorRefEdit&,void> aCursorUp = LINK( this, ScOptSolverDlg, CursorUpHdl );
+ Link<ScCursorRefEdit&,void> aCursorDown = LINK( this, ScOptSolverDlg, CursorDownHdl );
+ Link<formula::RefEdit&,void> aCondModify = LINK( this, ScOptSolverDlg, CondModifyHdl );
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ mpLeftEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown );
+ mpRightEdit[nRow]->SetCursorLinks( aCursorUp, aCursorDown );
+ mpLeftEdit[nRow]->SetModifyHdl( aCondModify );
+ mpRightEdit[nRow]->SetModifyHdl( aCondModify );
+ mpDelButton[nRow]->connect_clicked( LINK( this, ScOptSolverDlg, DelBtnHdl ) );
+ mpOperator[nRow]->connect_changed( LINK( this, ScOptSolverDlg, SelectHdl ) );
+ }
+ m_xEdTargetValue->SetModifyHdl( LINK( this, ScOptSolverDlg, TargetModifyHdl ) );
+
+ Size aSize(m_xContents->get_preferred_size());
+ m_xContents->set_size_request(aSize.Width(), aSize.Height());
+ m_xScrollBar->connect_vadjustment_changed( LINK( this, ScOptSolverDlg, ScrollHdl ) );
+
+ m_xScrollBar->vadjustment_set_page_increment( EDIT_ROW_COUNT );
+ m_xScrollBar->vadjustment_set_page_size( EDIT_ROW_COUNT );
+ // Range is set in ShowConditions
+
+ // get available solver implementations
+ //! sort by descriptions?
+ ScSolverUtil::GetImplementations( maImplNames, maDescriptions );
+
+ // Load existing settings stored in the tab
+ LoadSolverSettings();
+ ShowConditions();
+
+ // If no objective cell has been loaded, then use the selected cell
+ if (m_xEdObjectiveCell->GetText().isEmpty())
+ {
+ OUString aCursorStr;
+ if (!mrDoc.GetRangeAtBlock(ScRange(rCursorPos), aCursorStr))
+ aCursorStr = rCursorPos.Format(ScRefFlags::ADDR_ABS, nullptr, mrDoc.GetAddressConvention());
+ m_xEdObjectiveCell->SetRefString(aCursorStr);
+ }
+
+ m_xEdObjectiveCell->GrabFocus();
+ mpEdActive = m_xEdObjectiveCell.get();
+}
+
+void ScOptSolverDlg::ReadConditions()
+{
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ sc::ModelConstraint aRowEntry;
+ aRowEntry.aLeftStr = mpLeftEdit[nRow]->GetText();
+ aRowEntry.aRightStr = mpRightEdit[nRow]->GetText();
+ aRowEntry.nOperator = OperatorIndexToConstraintOperator(mpOperator[nRow]->get_active());
+
+ tools::Long nVecPos = nScrollPos + nRow;
+ if ( nVecPos >= static_cast<tools::Long>(m_aConditions.size()) && !aRowEntry.IsDefault() )
+ m_aConditions.resize( nVecPos + 1 );
+
+ if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
+ m_aConditions[nVecPos] = aRowEntry;
+
+ // remove default entries at the end
+ size_t nSize = m_aConditions.size();
+ while ( nSize > 0 && m_aConditions[ nSize-1 ].IsDefault() )
+ --nSize;
+ m_aConditions.resize( nSize );
+ }
+}
+
+void ScOptSolverDlg::ShowConditions()
+{
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ sc::ModelConstraint aRowEntry;
+
+ tools::Long nVecPos = nScrollPos + nRow;
+ if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
+ aRowEntry = m_aConditions[nVecPos];
+
+ mpLeftEdit[nRow]->SetRefString( aRowEntry.aLeftStr );
+ mpRightEdit[nRow]->SetRefString( aRowEntry.aRightStr );
+ mpOperator[nRow]->set_active( aRowEntry.nOperator - 1);
+ }
+
+ // allow to scroll one page behind the visible or stored rows
+ tools::Long nVisible = nScrollPos + EDIT_ROW_COUNT;
+ tools::Long nMax = std::max( nVisible, static_cast<tools::Long>(m_aConditions.size()) );
+ m_xScrollBar->vadjustment_configure(nScrollPos, 0, nMax + EDIT_ROW_COUNT, 1,
+ EDIT_ROW_COUNT - 1, EDIT_ROW_COUNT);
+
+ EnableButtons();
+}
+
+void ScOptSolverDlg::EnableButtons()
+{
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ tools::Long nVecPos = nScrollPos + nRow;
+ mpDelButton[nRow]->set_sensitive(nVecPos < static_cast<tools::Long>(m_aConditions.size()));
+ }
+}
+
+void ScOptSolverDlg::Close()
+{
+ if (m_xOptDlg)
+ m_xOptDlg->response(RET_CANCEL);
+ assert(!m_xOptDlg);
+ DoClose( ScOptSolverDlgWrapper::GetChildWindowId() );
+}
+
+void ScOptSolverDlg::SetActive()
+{
+ if ( mbDlgLostFocus )
+ {
+ mbDlgLostFocus = false;
+ if( mpEdActive )
+ mpEdActive->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScOptSolverDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if( !mpEdActive )
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(mpEdActive);
+
+ // "target"/"value": single cell
+ bool bSingle = ( mpEdActive == m_xEdObjectiveCell.get() || mpEdActive == m_xEdTargetValue.get() );
+
+ OUString aStr;
+ ScAddress aAdr = rRef.aStart;
+ ScRange aNewRef( rRef );
+ if ( bSingle )
+ aNewRef.aEnd = aAdr;
+
+ OUString aName;
+ if ( rDocP.GetRangeAtBlock( aNewRef, aName ) ) // named range: show name
+ aStr = aName;
+ else // format cell/range reference
+ {
+ ScRefFlags nFmt = ( aAdr.Tab() == mnCurTab ) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
+ if ( bSingle )
+ aStr = aAdr.Format(nFmt, &rDocP, rDocP.GetAddressConvention());
+ else
+ aStr = rRef.Format(rDocP, nFmt | ScRefFlags::RANGE_ABS, rDocP.GetAddressConvention());
+ }
+
+ // variable cells can be several ranges, so only the selection is replaced
+ if ( mpEdActive == m_xEdVariableCells.get() )
+ {
+ OUString aVal = mpEdActive->GetText();
+ Selection aSel = mpEdActive->GetSelection();
+ aSel.Normalize();
+ aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aStr );
+ Selection aNewSel( aSel.Min(), aSel.Min()+aStr.getLength() );
+ mpEdActive->SetRefString( aVal );
+ mpEdActive->SetSelection( aNewSel );
+ }
+ else
+ mpEdActive->SetRefString( aStr );
+
+ ReadConditions();
+ EnableButtons();
+
+ // select "Value of" if a ref is input into "target" edit
+ if ( mpEdActive == m_xEdTargetValue.get() )
+ m_xRbValue->set_active(true);
+}
+
+bool ScOptSolverDlg::IsRefInputMode() const
+{
+ return mpEdActive != nullptr;
+}
+
+// Loads solver settings into the dialog
+void ScOptSolverDlg::LoadSolverSettings()
+{
+ m_xEdObjectiveCell->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_CELL));
+ m_xEdTargetValue->SetRefString(m_pSolverSettings->GetParameter(sc::SP_OBJ_VAL));
+ m_xEdVariableCells->SetRefString(m_pSolverSettings->GetParameter(sc::SP_VAR_CELLS));
+
+ // Objective type
+ sc::ObjectiveType eType = m_pSolverSettings->GetObjectiveType();
+ switch (eType)
+ {
+ case sc::OT_MAXIMIZE : m_xRbMax->set_active(true); break;
+ case sc::OT_MINIMIZE : m_xRbMin->set_active(true); break;
+ case sc::OT_VALUE : m_xRbValue->set_active(true); break;
+ }
+
+ // Model constraints
+ m_aConditions = m_pSolverSettings->GetConstraints();
+
+ // Loads solver engine name
+ // If the solver engine in the current settings are not supported, use the first available
+ maEngine = m_pSolverSettings->GetParameter(sc::SP_LO_ENGINE);
+ if (!IsEngineAvailable(maEngine))
+ {
+ maEngine = maImplNames[0];
+ m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
+ }
+
+ // Query current engine options
+ maProperties = ScSolverUtil::GetDefaults(maEngine);
+ m_pSolverSettings->GetEngineOptions(maProperties);
+}
+
+// Set solver settings and save them
+void ScOptSolverDlg::SaveSolverSettings()
+{
+ m_pSolverSettings->SetParameter(sc::SP_OBJ_CELL, m_xEdObjectiveCell->GetText());
+ m_pSolverSettings->SetParameter(sc::SP_OBJ_VAL, m_xEdTargetValue->GetText());
+ m_pSolverSettings->SetParameter(sc::SP_VAR_CELLS, m_xEdVariableCells->GetText());
+
+ // Objective type
+ if (m_xRbMax->get_active())
+ m_pSolverSettings->SetObjectiveType(sc::OT_MAXIMIZE);
+ else if (m_xRbMin->get_active())
+ m_pSolverSettings->SetObjectiveType(sc::OT_MINIMIZE);
+ else if (m_xRbValue->get_active())
+ m_pSolverSettings->SetObjectiveType(sc::OT_VALUE);
+
+ // Model constraints
+ m_pSolverSettings->SetConstraints(m_aConditions);
+
+ // Solver engine name
+ m_pSolverSettings->SetParameter(sc::SP_LO_ENGINE, maEngine);
+
+ // Solver engine options
+ m_pSolverSettings->SetEngineOptions(maProperties);
+
+ // Effectively save settings to file
+ m_pSolverSettings->SaveSolverSettings();
+}
+
+// Test if a LO engine implementation exists
+bool ScOptSolverDlg::IsEngineAvailable(std::u16string_view sEngineName)
+{
+ auto nIndex = comphelper::findValue(maImplNames, sEngineName);
+ return nIndex != -1;
+}
+
+// Handler:
+
+IMPL_LINK(ScOptSolverDlg, BtnHdl, weld::Button&, rBtn, void)
+{
+ auto xKeepAlive = shared_from_this();
+ if (&rBtn == m_xBtnSolve.get() || &rBtn == m_xBtnClose.get())
+ {
+ bool bSolve = ( &rBtn == m_xBtnSolve.get() );
+
+ SetDispatcherLock( false );
+ SwitchToDocument();
+
+ bool bClose = true;
+ if ( bSolve )
+ bClose = CallSolver();
+
+ if ( bClose )
+ {
+ // Close: write dialog settings to DocShell for subsequent calls
+ ReadConditions();
+ SaveSolverSettings();
+ response(RET_CLOSE);
+ }
+ else
+ {
+ // no solution -> dialog is kept open
+ SetDispatcherLock( true );
+ }
+ }
+ else if (&rBtn == m_xBtnOpt.get())
+ {
+ //! move options dialog to UI lib?
+ m_xOptDlg = std::make_shared<ScSolverOptionsDialog>(m_xDialog.get(), maImplNames, maDescriptions, maEngine, maProperties);
+ weld::DialogController::runAsync(m_xOptDlg, [this](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ maEngine = m_xOptDlg->GetEngine();
+ maProperties = m_xOptDlg->GetProperties();
+ }
+ m_xOptDlg.reset();
+ });
+ }
+ else if (&rBtn == m_xBtnResetAll.get())
+ {
+ OUString sEmpty;
+ m_xEdObjectiveCell->SetText(sEmpty);
+ m_xEdTargetValue->SetText(sEmpty);
+ m_xEdVariableCells->SetText(sEmpty);
+
+ // Get default property values of solver implementations
+ maEngine = maImplNames[0];
+ maProperties = ScSolverUtil::GetDefaults( maEngine );
+
+ // Clear all conditions (Constraints)
+ m_aConditions.clear();
+ ShowConditions();
+
+ m_xRbMax->set_active(true);
+ m_xEdObjectiveCell->GrabFocus();
+ mpEdActive = m_xEdObjectiveCell.get();
+ }
+}
+
+IMPL_LINK( ScOptSolverDlg, GetEditFocusHdl, formula::RefEdit&, rCtrl, void )
+{
+ formula::RefEdit* pEdit = nullptr;
+ mpEdActive = nullptr;
+
+ if( &rCtrl == m_xEdObjectiveCell.get() )
+ pEdit = mpEdActive = m_xEdObjectiveCell.get();
+ else if( &rCtrl == m_xEdTargetValue.get() )
+ pEdit = mpEdActive = m_xEdTargetValue.get();
+ else if( &rCtrl == m_xEdVariableCells.get() )
+ pEdit = mpEdActive = m_xEdVariableCells.get();
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ if( &rCtrl == mpLeftEdit[nRow] )
+ pEdit = mpEdActive = mpLeftEdit[nRow];
+ else if( &rCtrl == mpRightEdit[nRow] )
+ pEdit = mpEdActive = mpRightEdit[nRow];
+ }
+
+ if( pEdit )
+ pEdit->SelectAll();
+}
+
+IMPL_LINK( ScOptSolverDlg, GetButtonFocusHdl, formula::RefButton&, rCtrl, void )
+{
+ formula::RefEdit* pEdit = nullptr;
+ mpEdActive = nullptr;
+
+ if( &rCtrl == m_xRBObjectiveCell.get() )
+ pEdit = mpEdActive = m_xEdObjectiveCell.get();
+ else if( &rCtrl == m_xRBTargetValue.get() )
+ pEdit = mpEdActive = m_xEdTargetValue.get();
+ else if( &rCtrl == m_xRBVariableCells.get() )
+ pEdit = mpEdActive = m_xEdVariableCells.get();
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ if( &rCtrl == mpLeftButton[nRow] )
+ pEdit = mpEdActive = mpLeftEdit[nRow];
+ else if( &rCtrl == mpRightButton[nRow] )
+ pEdit = mpEdActive = mpRightEdit[nRow];
+ }
+
+ if( pEdit )
+ pEdit->SelectAll();
+}
+
+
+IMPL_LINK(ScOptSolverDlg, GetFocusHdl, weld::Widget&, rCtrl, void)
+{
+ if( &rCtrl == m_xRbValue.get() ) // focus on "Value of" radio button
+ mpEdActive = m_xEdTargetValue.get(); // use value edit for ref input, but don't change selection
+ else
+ {
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ {
+ if( &rCtrl == mpOperator[nRow] ) // focus on "operator" list box
+ mpEdActive = mpRightEdit[nRow]; // use right edit for ref input, but don't change selection
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScOptSolverDlg, LoseEditFocusHdl, formula::RefEdit&, void)
+{
+ mbDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScOptSolverDlg, LoseButtonFocusHdl, formula::RefButton&, void)
+{
+ mbDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK(ScOptSolverDlg, DelBtnHdl, weld::Button&, rBtn, void)
+{
+ for ( sal_uInt16 nRow = 0; nRow < EDIT_ROW_COUNT; ++nRow )
+ if (&rBtn == mpDelButton[nRow])
+ {
+ bool bHadFocus = rBtn.has_focus();
+
+ ReadConditions();
+ tools::Long nVecPos = nScrollPos + nRow;
+ if ( nVecPos < static_cast<tools::Long>(m_aConditions.size()) )
+ {
+ m_aConditions.erase( m_aConditions.begin() + nVecPos );
+ ShowConditions();
+
+ if ( bHadFocus && !rBtn.get_sensitive() )
+ {
+ // If the button is disabled, focus would normally move to the next control,
+ // (left edit of the next row). Move it to left edit of this row instead.
+
+ mpEdActive = mpLeftEdit[nRow];
+ mpEdActive->GrabFocus();
+ }
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScOptSolverDlg, TargetModifyHdl, formula::RefEdit&, void)
+{
+ // modify handler for the target edit:
+ // select "Value of" if something is input into the edit
+ if ( !m_xEdTargetValue->GetText().isEmpty() )
+ m_xRbValue->set_active(true);
+}
+
+IMPL_LINK_NOARG(ScOptSolverDlg, CondModifyHdl, formula::RefEdit&, void)
+{
+ // modify handler for the condition edits, just to enable/disable "delete" buttons
+ ReadConditions();
+ EnableButtons();
+}
+
+IMPL_LINK_NOARG(ScOptSolverDlg, SelectHdl, weld::ComboBox&, void)
+{
+ // select handler for operator list boxes, just to enable/disable "delete" buttons
+ ReadConditions();
+ EnableButtons();
+}
+
+IMPL_LINK_NOARG(ScOptSolverDlg, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ ReadConditions();
+ nScrollPos = m_xScrollBar->vadjustment_get_value();
+ ShowConditions();
+ if( mpEdActive )
+ mpEdActive->SelectAll();
+}
+
+IMPL_LINK( ScOptSolverDlg, CursorUpHdl, ScCursorRefEdit&, rEdit, void )
+{
+ if ( &rEdit == mpLeftEdit[0] || &rEdit == mpRightEdit[0] )
+ {
+ if ( nScrollPos > 0 )
+ {
+ ReadConditions();
+ --nScrollPos;
+ ShowConditions();
+ if( mpEdActive )
+ mpEdActive->SelectAll();
+ }
+ }
+ else
+ {
+ formula::RefEdit* pFocus = nullptr;
+ for ( sal_uInt16 nRow = 1; nRow < EDIT_ROW_COUNT; ++nRow ) // second row or below: move focus
+ {
+ if ( &rEdit == mpLeftEdit[nRow] )
+ pFocus = mpLeftEdit[nRow-1];
+ else if ( &rEdit == mpRightEdit[nRow] )
+ pFocus = mpRightEdit[nRow-1];
+ }
+ if (pFocus)
+ {
+ mpEdActive = pFocus;
+ pFocus->GrabFocus();
+ }
+ }
+}
+
+IMPL_LINK( ScOptSolverDlg, CursorDownHdl, ScCursorRefEdit&, rEdit, void )
+{
+ if ( &rEdit == mpLeftEdit[EDIT_ROW_COUNT-1] || &rEdit == mpRightEdit[EDIT_ROW_COUNT-1] )
+ {
+ //! limit scroll position?
+ ReadConditions();
+ ++nScrollPos;
+ ShowConditions();
+ if( mpEdActive )
+ mpEdActive->SelectAll();
+ }
+ else
+ {
+ formula::RefEdit* pFocus = nullptr;
+ for ( sal_uInt16 nRow = 0; nRow+1 < EDIT_ROW_COUNT; ++nRow ) // before last row: move focus
+ {
+ if ( &rEdit == mpLeftEdit[nRow] )
+ pFocus = mpLeftEdit[nRow+1];
+ else if ( &rEdit == mpRightEdit[nRow] )
+ pFocus = mpRightEdit[nRow+1];
+ }
+ if (pFocus)
+ {
+ mpEdActive = pFocus;
+ pFocus->GrabFocus();
+ }
+ }
+}
+
+// Converts the position of the operator in the dropdown menu to a ConstraintOperator type
+sc::ConstraintOperator ScOptSolverDlg::OperatorIndexToConstraintOperator(sal_Int32 nIndex)
+{
+ switch(nIndex)
+ {
+ case 0 : return sc::CO_LESS_EQUAL; break;
+ case 1 : return sc::CO_EQUAL; break;
+ case 2 : return sc::CO_GREATER_EQUAL; break;
+ case 3 : return sc::CO_INTEGER; break;
+ case 4 : return sc::CO_BINARY; break;
+ default : return sc::CO_LESS_EQUAL; break;
+ }
+}
+
+void ScOptSolverDlg::ShowError( bool bCondition, formula::RefEdit* pFocus )
+{
+ OUString aMessage = bCondition ? maConditionError : maInputError;
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ aMessage));
+ xBox->run();
+ if (pFocus)
+ {
+ mpEdActive = pFocus;
+ pFocus->GrabFocus();
+ }
+}
+
+bool ScOptSolverDlg::ParseRef( ScRange& rRange, const OUString& rInput, bool bAllowRange )
+{
+ ScAddress::Details aDetails(mrDoc.GetAddressConvention(), 0, 0);
+ ScRefFlags nFlags = rRange.ParseAny( rInput, mrDoc, aDetails );
+ if ( nFlags & ScRefFlags::VALID )
+ {
+ if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
+ rRange.aStart.SetTab( mnCurTab );
+ if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
+ rRange.aEnd.SetTab( rRange.aStart.Tab() );
+ return ( bAllowRange || rRange.aStart == rRange.aEnd );
+ }
+ else if ( ScRangeUtil::MakeRangeFromName( rInput, mrDoc, mnCurTab, rRange, RUTL_NAMES, aDetails ) )
+ return ( bAllowRange || rRange.aStart == rRange.aEnd );
+
+ return false; // not recognized
+}
+
+bool ScOptSolverDlg::FindTimeout( sal_Int32& rTimeout )
+{
+ bool bFound = false;
+
+ if ( !maProperties.hasElements() )
+ maProperties = ScSolverUtil::GetDefaults( maEngine ); // get property defaults from component
+
+ sal_Int32 nPropCount = maProperties.getLength();
+ for (sal_Int32 nProp=0; nProp<nPropCount && !bFound; ++nProp)
+ {
+ const beans::PropertyValue& rValue = maProperties[nProp];
+ if ( rValue.Name == SC_UNONAME_TIMEOUT )
+ bFound = ( rValue.Value >>= rTimeout );
+ }
+ return bFound;
+}
+
+bool ScOptSolverDlg::CallSolver() // return true -> close dialog after calling
+{
+ // show progress dialog
+
+ auto xProgress = std::make_shared<ScSolverProgressDialog>(m_xDialog.get());
+ sal_Int32 nTimeout = 0;
+ if ( FindTimeout( nTimeout ) )
+ xProgress->SetTimeLimit( nTimeout );
+ else
+ xProgress->HideTimeLimit();
+
+ weld::DialogController::runAsync(xProgress, [](sal_Int32 /*nResult*/){});
+
+ // try to make sure the progress dialog is painted before continuing
+ Application::Reschedule(true);
+
+ // collect solver parameters
+
+ ReadConditions();
+
+ rtl::Reference<ScModelObj> xDocument( mpDocShell->GetModel() );
+
+ ScRange aObjRange;
+ if ( !ParseRef( aObjRange, m_xEdObjectiveCell->GetText(), false ) )
+ {
+ ShowError( false, m_xEdObjectiveCell.get() );
+ return false;
+ }
+ table::CellAddress aObjective( aObjRange.aStart.Tab(), aObjRange.aStart.Col(), aObjRange.aStart.Row() );
+
+ // "changing cells" can be several ranges
+ ScRangeList aVarRanges;
+ if ( !ParseWithNames( aVarRanges, m_xEdVariableCells->GetText(), mrDoc ) )
+ {
+ ShowError( false, m_xEdVariableCells.get() );
+ return false;
+ }
+ uno::Sequence<table::CellAddress> aVariables;
+ sal_Int32 nVarPos = 0;
+
+ for ( size_t nRangePos=0, nRange = aVarRanges.size(); nRangePos < nRange; ++nRangePos )
+ {
+ ScRange aRange( aVarRanges[ nRangePos ] );
+ aRange.PutInOrder();
+ SCTAB nTab = aRange.aStart.Tab();
+
+ // resolve into single cells
+
+ sal_Int32 nAdd = ( aRange.aEnd.Col() - aRange.aStart.Col() + 1 ) *
+ ( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
+ aVariables.realloc( nVarPos + nAdd );
+ auto pVariables = aVariables.getArray();
+
+ for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
+ for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
+ pVariables[nVarPos++] = table::CellAddress( nTab, nCol, nRow );
+ }
+
+ uno::Sequence<sheet::SolverConstraint> aConstraints;
+ sal_Int32 nConstrPos = 0;
+ for ( const auto& rConstr : m_aConditions )
+ {
+ if ( !rConstr.aLeftStr.isEmpty() )
+ {
+ sheet::SolverConstraint aConstraint;
+ // Order of list box entries must match enum values.
+ // The enum SolverConstraintOperator starts at zero, whereas ConstraintOperator starts at 1
+ // hence we need to subtract -1 here
+ aConstraint.Operator = static_cast<sheet::SolverConstraintOperator>(rConstr.nOperator - 1);
+
+ ScRange aLeftRange;
+ if ( !ParseRef( aLeftRange, rConstr.aLeftStr, true ) )
+ {
+ ShowError( true, nullptr );
+ return false;
+ }
+
+ bool bIsRange = false;
+ ScRange aRightRange;
+ if ( ParseRef( aRightRange, rConstr.aRightStr, true ) )
+ {
+ if ( aRightRange.aStart == aRightRange.aEnd )
+ aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(),
+ aRightRange.aStart.Col(), aRightRange.aStart.Row() );
+ else if ( aRightRange.aEnd.Col()-aRightRange.aStart.Col() == aLeftRange.aEnd.Col()-aLeftRange.aStart.Col() &&
+ aRightRange.aEnd.Row()-aRightRange.aStart.Row() == aLeftRange.aEnd.Row()-aLeftRange.aStart.Row() )
+ bIsRange = true; // same size as "left" range, resolve into single cells
+ else
+ {
+ ShowError( true, nullptr );
+ return false;
+ }
+ }
+ else
+ {
+ sal_uInt32 nFormat = 0; //! explicit language?
+ double fValue = 0.0;
+ if ( mrDoc.GetFormatTable()->IsNumberFormat( rConstr.aRightStr, nFormat, fValue ) )
+ aConstraint.Right <<= fValue;
+ else if ( aConstraint.Operator != sheet::SolverConstraintOperator_INTEGER &&
+ aConstraint.Operator != sheet::SolverConstraintOperator_BINARY )
+ {
+ ShowError( true, nullptr );
+ return false;
+ }
+ }
+
+ // resolve into single cells
+
+ sal_Int32 nAdd = ( aLeftRange.aEnd.Col() - aLeftRange.aStart.Col() + 1 ) *
+ ( aLeftRange.aEnd.Row() - aLeftRange.aStart.Row() + 1 );
+ aConstraints.realloc( nConstrPos + nAdd );
+ auto pConstraints = aConstraints.getArray();
+
+ for (SCROW nRow = aLeftRange.aStart.Row(); nRow <= aLeftRange.aEnd.Row(); ++nRow)
+ for (SCCOL nCol = aLeftRange.aStart.Col(); nCol <= aLeftRange.aEnd.Col(); ++nCol)
+ {
+ aConstraint.Left = table::CellAddress( aLeftRange.aStart.Tab(), nCol, nRow );
+ if ( bIsRange )
+ aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(),
+ aRightRange.aStart.Col() + ( nCol - aLeftRange.aStart.Col() ),
+ aRightRange.aStart.Row() + ( nRow - aLeftRange.aStart.Row() ) );
+
+ pConstraints[nConstrPos++] = aConstraint;
+ }
+ }
+ }
+
+ bool bMaximize = m_xRbMax->get_active();
+ if ( m_xRbValue->get_active() )
+ {
+ // handle "value of" with an additional constraint (and then minimize)
+
+ sheet::SolverConstraint aConstraint;
+ aConstraint.Left = aObjective;
+ aConstraint.Operator = sheet::SolverConstraintOperator_EQUAL;
+
+ OUString aValStr = m_xEdTargetValue->GetText();
+ ScRange aRightRange;
+ if ( ParseRef( aRightRange, aValStr, false ) )
+ aConstraint.Right <<= table::CellAddress( aRightRange.aStart.Tab(),
+ aRightRange.aStart.Col(), aRightRange.aStart.Row() );
+ else
+ {
+ sal_uInt32 nFormat = 0; //! explicit language?
+ double fValue = 0.0;
+ if ( mrDoc.GetFormatTable()->IsNumberFormat( aValStr, nFormat, fValue ) )
+ aConstraint.Right <<= fValue;
+ else
+ {
+ ShowError( false, m_xEdTargetValue.get() );
+ return false;
+ }
+ }
+
+ aConstraints.realloc( nConstrPos + 1 );
+ aConstraints.getArray()[nConstrPos++] = aConstraint;
+ }
+
+ // copy old document values
+
+ sal_Int32 nVarCount = aVariables.getLength();
+ uno::Sequence<double> aOldValues( nVarCount );
+ std::transform(std::cbegin(aVariables), std::cend(aVariables), aOldValues.getArray(),
+ [this](const table::CellAddress& rVariable) -> double {
+ ScAddress aCellPos;
+ ScUnoConversion::FillScAddress( aCellPos, rVariable );
+ return mrDoc.GetValue( aCellPos );
+ });
+
+ // create and initialize solver
+
+ uno::Reference<sheet::XSolver> xSolver = ScSolverUtil::GetSolver( maEngine );
+ OSL_ENSURE( xSolver.is(), "can't get solver component" );
+ if ( !xSolver.is() )
+ return false;
+
+ xSolver->setDocument( xDocument );
+ xSolver->setObjective( aObjective );
+ xSolver->setVariables( aVariables );
+ xSolver->setConstraints( aConstraints );
+ xSolver->setMaximize( bMaximize );
+
+ // set options
+ uno::Reference<beans::XPropertySet> xOptProp(xSolver, uno::UNO_QUERY);
+ if ( xOptProp.is() )
+ {
+ for (const beans::PropertyValue& rValue : std::as_const(maProperties))
+ {
+ try
+ {
+ xOptProp->setPropertyValue( rValue.Name, rValue.Value );
+ }
+ catch ( uno::Exception & )
+ {
+ OSL_FAIL("Exception in solver option property");
+ }
+ }
+ }
+
+ xSolver->solve();
+ bool bSuccess = xSolver->getSuccess();
+
+ xProgress->response(RET_CLOSE);
+
+ bool bClose = false;
+ bool bRestore = true; // restore old values unless a solution is accepted
+ if ( bSuccess )
+ {
+ // put solution into document so it is visible when asking
+ uno::Sequence<double> aSolution = xSolver->getSolution();
+ if ( aSolution.getLength() == nVarCount )
+ {
+ mpDocShell->LockPaint();
+ ScDocFunc &rFunc = mpDocShell->GetDocFunc();
+ for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
+ {
+ ScAddress aCellPos;
+ ScUnoConversion::FillScAddress( aCellPos, std::as_const(aVariables)[nVarPos] );
+ rFunc.SetValueCell(aCellPos, aSolution[nVarPos], false);
+ }
+ mpDocShell->UnlockPaint();
+ }
+ //! else error?
+
+ // take formatted result from document (result value from component is ignored)
+ OUString aResultStr = mrDoc.GetString(
+ static_cast<SCCOL>(aObjective.Column), static_cast<SCROW>(aObjective.Row),
+ static_cast<SCTAB>(aObjective.Sheet));
+
+ ScSolverSuccessDialog aDialog(m_xDialog.get(), aResultStr);
+ if (aDialog.run() == RET_OK)
+ {
+ // keep results and close dialog
+ bRestore = false;
+ bClose = true;
+ }
+ }
+ else
+ {
+ OUString aError;
+ uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY );
+ if ( xDesc.is() )
+ aError = xDesc->getStatusDescription(); // error description from component
+ ScSolverNoSolutionDialog aDialog(m_xDialog.get(), aError);
+ aDialog.run();
+ }
+
+ if ( bRestore ) // restore old values
+ {
+ mpDocShell->LockPaint();
+ ScDocFunc &rFunc = mpDocShell->GetDocFunc();
+ for (nVarPos=0; nVarPos<nVarCount; ++nVarPos)
+ {
+ ScAddress aCellPos;
+ ScUnoConversion::FillScAddress( aCellPos, aVariables[nVarPos] );
+ rFunc.SetValueCell(aCellPos, std::as_const(aOldValues)[nVarPos], false);
+ }
+ mpDocShell->UnlockPaint();
+ }
+
+ return bClose;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/protectiondlg.cxx b/sc/source/ui/miscdlgs/protectiondlg.cxx
new file mode 100644
index 0000000000..1353f5f154
--- /dev/null
+++ b/sc/source/ui/miscdlgs/protectiondlg.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <protectiondlg.hxx>
+#include <tabprotection.hxx>
+#include <svl/PasswordHelper.hxx>
+
+#include <vector>
+
+namespace {
+
+// The order must match that of the list box.
+const std::vector<ScTableProtection::Option> aOptions = {
+ ScTableProtection::SELECT_LOCKED_CELLS,
+ ScTableProtection::SELECT_UNLOCKED_CELLS,
+ ScTableProtection::INSERT_COLUMNS,
+ ScTableProtection::INSERT_ROWS,
+ ScTableProtection::DELETE_COLUMNS,
+ ScTableProtection::DELETE_ROWS,
+};
+
+}
+
+ScTableProtectionDlg::ScTableProtectionDlg(weld::Window* pParent)
+ : weld::GenericDialogController(pParent, "modules/scalc/ui/protectsheetdlg.ui", "ProtectSheetDialog")
+ , m_xBtnProtect(m_xBuilder->weld_check_button("protect"))
+ , m_xPasswords(m_xBuilder->weld_container("passwords"))
+ , m_xOptions(m_xBuilder->weld_container("options"))
+ , m_xPassword1Edit(m_xBuilder->weld_entry("password1"))
+ , m_xPassword2Edit(m_xBuilder->weld_entry("password2"))
+ , m_xPasswordStrengthBar(m_xBuilder->weld_level_bar("passwordbar"))
+ , m_xOptionsListBox(m_xBuilder->weld_tree_view("checklist"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xProtected(m_xBuilder->weld_label("protected"))
+ , m_xUnprotected(m_xBuilder->weld_label("unprotected"))
+ , m_xInsertColumns(m_xBuilder->weld_label("insert-columns"))
+ , m_xInsertRows(m_xBuilder->weld_label("insert-rows"))
+ , m_xDeleteColumns(m_xBuilder->weld_label("delete-columns"))
+ , m_xDeleteRows(m_xBuilder->weld_label("delete-rows"))
+{
+ m_aSelectLockedCells = m_xProtected->get_label();
+ m_aSelectUnlockedCells = m_xUnprotected->get_label();
+ m_aInsertColumns = m_xInsertColumns->get_label();
+ m_aInsertRows = m_xInsertRows->get_label();
+ m_aDeleteColumns = m_xDeleteColumns->get_label();
+ m_aDeleteRows = m_xDeleteRows->get_label();
+
+ m_xOptionsListBox->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ Init();
+}
+
+ScTableProtectionDlg::~ScTableProtectionDlg()
+{
+}
+
+void ScTableProtectionDlg::SetDialogData(const ScTableProtection& rData)
+{
+ for (size_t i = 0; i < aOptions.size(); ++i)
+ m_xOptionsListBox->set_toggle(i, rData.isOptionEnabled(aOptions[i]) ? TRISTATE_TRUE : TRISTATE_FALSE);
+}
+
+void ScTableProtectionDlg::WriteData(ScTableProtection& rData) const
+{
+ rData.setProtected(m_xBtnProtect->get_active());
+
+ // We assume that the two password texts match.
+ rData.setPassword(m_xPassword1Edit->get_text());
+
+ for (size_t i = 0; i < aOptions.size(); ++i)
+ rData.setOption(aOptions[i], m_xOptionsListBox->get_toggle(i) == TRISTATE_TRUE);
+}
+
+void ScTableProtectionDlg::InsertEntry(const OUString& rTxt)
+{
+ m_xOptionsListBox->append();
+ const int nRow = m_xOptionsListBox->n_children() - 1;
+ m_xOptionsListBox->set_toggle(nRow, TRISTATE_FALSE);
+ m_xOptionsListBox->set_text(nRow, rTxt, 0);
+}
+
+void ScTableProtectionDlg::Init()
+{
+ m_xBtnProtect->connect_toggled(LINK(this, ScTableProtectionDlg, CheckBoxHdl));
+
+ m_xBtnOk->connect_clicked(LINK(this, ScTableProtectionDlg, OKHdl));
+
+ Link<weld::Entry&,void> aLink = LINK(this, ScTableProtectionDlg, PasswordModifyHdl);
+ m_xPassword1Edit->connect_changed(aLink);
+ m_xPassword2Edit->connect_changed(aLink);
+
+ m_xOptionsListBox->freeze();
+ m_xOptionsListBox->clear();
+
+ InsertEntry(m_aSelectLockedCells);
+ InsertEntry(m_aSelectUnlockedCells);
+ InsertEntry(m_aInsertColumns);
+ InsertEntry(m_aInsertRows);
+ InsertEntry(m_aDeleteColumns);
+ InsertEntry(m_aDeleteRows);
+
+ m_xOptionsListBox->set_toggle(0, TRISTATE_TRUE);
+ m_xOptionsListBox->set_toggle(1, TRISTATE_TRUE);
+
+ m_xOptionsListBox->thaw();
+
+ // Set the default state of the dialog.
+ m_xBtnProtect->set_active(true);
+ m_xPassword1Edit->grab_focus();
+}
+
+void ScTableProtectionDlg::EnableOptionalWidgets(bool bEnable)
+{
+ m_xPasswords->set_sensitive(bEnable);
+ m_xOptions->set_sensitive(bEnable);
+}
+
+IMPL_LINK(ScTableProtectionDlg, CheckBoxHdl, weld::Toggleable&, rBtn, void)
+{
+ if (&rBtn == m_xBtnProtect.get())
+ {
+ bool bChecked = m_xBtnProtect->get_active();
+ EnableOptionalWidgets(bChecked);
+ m_xBtnOk->set_sensitive(bChecked);
+ }
+}
+
+IMPL_LINK_NOARG(ScTableProtectionDlg, OKHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(ScTableProtectionDlg, PasswordModifyHdl, weld::Entry&, rEntry, void)
+{
+ OUString aPass1 = m_xPassword1Edit->get_text();
+ if (&rEntry == m_xPassword1Edit.get())
+ {
+ m_xPasswordStrengthBar->set_percentage(
+ SvPasswordHelper::GetPasswordStrengthPercentage(aPass1));
+ }
+ OUString aPass2 = m_xPassword2Edit->get_text();
+ m_xBtnOk->set_sensitive(aPass1 == aPass2);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/redcom.cxx b/sc/source/ui/miscdlgs/redcom.cxx
new file mode 100644
index 0000000000..ef04bd57e1
--- /dev/null
+++ b/sc/source/ui/miscdlgs/redcom.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 <unotools/localedatawrapper.hxx>
+
+#include <chgtrack.hxx>
+#include <redcom.hxx>
+#include <docsh.hxx>
+#include <dbfunc.hxx>
+#include <tabview.hxx>
+#include <viewutil.hxx>
+#include <svx/svxdlg.hxx>
+
+ScRedComDialog::ScRedComDialog( weld::Window* pParent, const SfxItemSet& rCoreSet,
+ ScDocShell *pShell, ScChangeAction *pAction, bool bPrevNext)
+ : pChangeAction(nullptr)
+ , pDocShell(nullptr)
+ , pDlg(nullptr)
+{
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ pDlg = pFact->CreateSvxPostItDialog( pParent, rCoreSet, bPrevNext );
+ pDocShell=pShell;
+ pDlg->DontChangeAuthor();
+ pDlg->HideAuthor();
+
+ pDlg->SetPrevHdl(LINK( this, ScRedComDialog, PrevHdl));
+ pDlg->SetNextHdl(LINK( this, ScRedComDialog, NextHdl));
+
+ ReInit(pAction);
+}
+
+ScRedComDialog::~ScRedComDialog()
+{
+ pDlg.disposeAndClear();
+}
+
+ScChangeAction *ScRedComDialog::FindPrev(ScChangeAction *pAction)
+{
+ if(pAction!=nullptr && pDocShell !=nullptr)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeViewSettings* pSettings = rDoc.GetChangeViewSettings();
+
+ pAction=pAction->GetPrev();
+
+ while(pAction!=nullptr)
+ {
+ if( pAction->GetState()==SC_CAS_VIRGIN &&
+ pAction->IsDialogRoot() &&
+ ScViewUtil::IsActionShown(*pAction,*pSettings,rDoc)) break;
+
+ pAction=pAction->GetPrev();
+ }
+ }
+ return pAction;
+}
+
+ScChangeAction *ScRedComDialog::FindNext(ScChangeAction *pAction)
+{
+ if(pAction!=nullptr && pDocShell !=nullptr)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeViewSettings* pSettings = rDoc.GetChangeViewSettings();
+
+ pAction=pAction->GetNext();
+
+ while(pAction!=nullptr)
+ {
+ if( pAction->GetState()==SC_CAS_VIRGIN &&
+ pAction->IsDialogRoot() &&
+ ScViewUtil::IsActionShown(*pAction,*pSettings,rDoc)) break;
+
+ pAction=pAction->GetNext();
+ }
+ }
+ return pAction;
+}
+
+void ScRedComDialog::ReInit(ScChangeAction *pAction)
+{
+ pChangeAction=pAction;
+ if(pChangeAction==nullptr || pDocShell ==nullptr)
+ return;
+
+ OUString aTitle = pChangeAction->GetDescription(pDocShell->GetDocument());
+ pDlg->SetText(aTitle);
+ aComment=pChangeAction->GetComment();
+
+ bool bNext=FindNext(pChangeAction)!=nullptr;
+ bool bPrev=FindPrev(pChangeAction)!=nullptr;
+ pDlg->EnableTravel(bNext,bPrev);
+
+ OUString aAuthor = pChangeAction->GetUser();
+
+ DateTime aDT = pChangeAction->GetDateTime();
+ OUString aDate = ScGlobal::getLocaleData().getDate( aDT ) + " " +
+ ScGlobal::getLocaleData().getTime( aDT, false );
+
+ pDlg->ShowLastAuthor(aAuthor, aDate);
+ pDlg->SetNote(aComment);
+}
+
+void ScRedComDialog::Execute()
+{
+ short nRet=pDlg->Execute();
+
+ if(nRet== RET_OK )
+ {
+ if ( pDocShell!=nullptr && pDlg->GetNote() != aComment )
+ pDocShell->SetChangeComment( pChangeAction, pDlg->GetNote());
+ }
+}
+
+void ScRedComDialog::SelectCell()
+{
+ if (!pChangeAction || !pDocShell)
+ return;
+
+ const ScChangeAction* pAction=pChangeAction;
+ const ScBigRange& rRange = pAction->GetBigRange();
+
+ if(rRange.IsValid(pDocShell->GetDocument()))
+ {
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ {
+ ScRange aRef = rRange.MakeRange(pDocShell->GetDocument());
+ ScTabView* pTabView = pViewData->GetView();
+ pTabView->MarkRange(aRef);
+ }
+ }
+}
+
+IMPL_LINK(ScRedComDialog, PrevHdl, AbstractSvxPostItDialog&, rDlgP, void )
+{
+ if (pDocShell!=nullptr && rDlgP.GetNote() != aComment )
+ pDocShell->SetChangeComment( pChangeAction, rDlgP.GetNote());
+
+ ReInit(FindPrev(pChangeAction));
+ SelectCell();
+}
+
+IMPL_LINK(ScRedComDialog, NextHdl, AbstractSvxPostItDialog&, rDlgP, void )
+{
+ if ( pDocShell!=nullptr && rDlgP.GetNote() != aComment )
+ pDocShell->SetChangeComment( pChangeAction, rDlgP.GetNote());
+
+ ReInit(FindNext(pChangeAction));
+ SelectCell();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/retypepassdlg.cxx b/sc/source/ui/miscdlgs/retypepassdlg.cxx
new file mode 100644
index 0000000000..c7b9084a82
--- /dev/null
+++ b/sc/source/ui/miscdlgs/retypepassdlg.cxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <strings.hrc>
+#include <retypepassdlg.hxx>
+#include <scresid.hxx>
+#include <document.hxx>
+#include <tabprotection.hxx>
+
+ScRetypePassDlg::ScRetypePassDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/retypepassdialog.ui", "RetypePass")
+ , maTextNotProtected(ScResId(STR_NOT_PROTECTED))
+ , maTextNotPassProtected(ScResId(STR_NOT_PASS_PROTECTED))
+ , maTextHashBad(ScResId(STR_HASH_BAD))
+ , maTextHashGood(ScResId(STR_HASH_GOOD))
+ , meDesiredHash(PASSHASH_SHA1)
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxTextDocStatus(m_xBuilder->weld_label("docStatusLabel"))
+ , mxBtnRetypeDoc(m_xBuilder->weld_button("retypeDocButton"))
+ , mxScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow"))
+ , mxSheetsBox(m_xBuilder->weld_container("sheetsBox"))
+{
+ mxScrolledWindow->set_size_request(mxScrolledWindow->get_approximate_digit_width() * 46,
+ mxScrolledWindow->get_text_height() * 10);
+ Init();
+}
+
+ScRetypePassDlg::~ScRetypePassDlg() {}
+
+void ScRetypePassDlg::DeleteSheets() { maSheets.clear(); }
+
+short ScRetypePassDlg::run()
+{
+ PopulateDialog();
+ CheckHashStatus();
+ return GenericDialogController::run();
+}
+
+PassFragment::PassFragment(weld::Widget* pParent)
+ : m_xBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/passfragment.ui"))
+ , m_xSheetsBox(m_xBuilder->weld_container("PassEntry"))
+ , m_xName(m_xBuilder->weld_label("name"))
+ , m_xStatus(m_xBuilder->weld_label("status"))
+ , m_xButton(m_xBuilder->weld_button("button"))
+{
+ m_xButton->set_label(ScResId(STR_RETYPE));
+}
+
+void ScRetypePassDlg::SetDataFromDocument(const ScDocument& rDoc)
+{
+ DeleteSheets();
+ const ScDocProtection* pDocProtect = rDoc.GetDocProtection();
+ if (pDocProtect && pDocProtect->isProtected())
+ mpDocItem = std::make_shared<ScDocProtection>(*pDocProtect);
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ maTableItems.reserve(nTabCount);
+ maSheets.reserve(nTabCount);
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ TableItem aTabItem;
+ rDoc.GetName(i, aTabItem.maName);
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(i);
+ if (pTabProtect && pTabProtect->isProtected())
+ aTabItem.mpProtect = std::make_shared<ScTableProtection>(*pTabProtect);
+
+ maTableItems.push_back(aTabItem);
+ maSheets.emplace_back(new PassFragment(mxSheetsBox.get()));
+ maSheets.back()->m_xButton->connect_clicked(LINK(this, ScRetypePassDlg, RetypeBtnHdl));
+ }
+}
+
+void ScRetypePassDlg::SetDesiredHash(ScPasswordHash eHash) { meDesiredHash = eHash; }
+
+void ScRetypePassDlg::WriteNewDataToDocument(ScDocument& rDoc) const
+{
+ if (mpDocItem)
+ rDoc.SetDocProtection(mpDocItem.get());
+
+ size_t nTabCount = static_cast<size_t>(rDoc.GetTableCount());
+ size_t n = maTableItems.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (i >= nTabCount)
+ break;
+
+ ScTableProtection* pTabProtect = maTableItems[i].mpProtect.get();
+ if (pTabProtect)
+ rDoc.SetTabProtection(static_cast<SCTAB>(i), pTabProtect);
+ }
+}
+
+void ScRetypePassDlg::Init()
+{
+ Link<weld::Button&, void> aLink = LINK(this, ScRetypePassDlg, OKHdl);
+ mxBtnOk->connect_clicked(aLink);
+
+ aLink = LINK(this, ScRetypePassDlg, RetypeBtnHdl);
+ mxBtnRetypeDoc->connect_clicked(aLink);
+
+ mxTextDocStatus->set_label(maTextNotProtected);
+ mxBtnRetypeDoc->set_sensitive(false);
+}
+
+void ScRetypePassDlg::PopulateDialog()
+{
+ // Document protection first.
+ SetDocData();
+
+ // Sheet protection next.
+ for (size_t i = 0; i < maTableItems.size(); ++i)
+ SetTableData(i, static_cast<SCTAB>(i));
+}
+
+void ScRetypePassDlg::SetDocData()
+{
+ bool bBtnEnabled = false;
+ if (mpDocItem && mpDocItem->isProtected())
+ {
+ if (mpDocItem->isPasswordEmpty())
+ mxTextDocStatus->set_label(maTextNotPassProtected);
+ else if (mpDocItem->hasPasswordHash(meDesiredHash))
+ mxTextDocStatus->set_label(maTextHashGood);
+ else
+ {
+ // incompatible hash
+ mxTextDocStatus->set_label(maTextHashBad);
+ bBtnEnabled = true;
+ }
+ }
+ mxBtnRetypeDoc->set_sensitive(bBtnEnabled);
+}
+
+void ScRetypePassDlg::SetTableData(size_t nRowPos, SCTAB nTab)
+{
+ if (nRowPos >= maSheets.size())
+ return;
+
+ weld::Label& rName = *maSheets[nRowPos]->m_xName;
+ weld::Label& rStatus = *maSheets[nRowPos]->m_xStatus;
+ weld::Button& rBtn = *maSheets[nRowPos]->m_xButton;
+
+ bool bBtnEnabled = false;
+ rName.set_label(maTableItems[nTab].maName);
+ const ScTableProtection* pTabProtect = maTableItems[nTab].mpProtect.get();
+ if (pTabProtect && pTabProtect->isProtected())
+ {
+ if (pTabProtect->isPasswordEmpty())
+ rStatus.set_label(maTextNotPassProtected);
+ else if (pTabProtect->hasPasswordHash(meDesiredHash))
+ rStatus.set_label(maTextHashGood);
+ else
+ {
+ // incompatible hash
+ rStatus.set_label(maTextHashBad);
+ bBtnEnabled = true;
+ }
+ }
+ else
+ rStatus.set_label(maTextNotProtected);
+
+ rBtn.set_sensitive(bBtnEnabled);
+}
+
+static bool lcl_IsInGoodStatus(const ScPassHashProtectable* pProtected, ScPasswordHash eDesiredHash)
+{
+ if (!pProtected || !pProtected->isProtected())
+ // Not protected.
+ return true;
+
+ if (pProtected->isPasswordEmpty())
+ return true;
+
+ if (pProtected->hasPasswordHash(eDesiredHash))
+ return true;
+
+ return false;
+}
+
+void ScRetypePassDlg::CheckHashStatus()
+{
+ do
+ {
+ if (!lcl_IsInGoodStatus(mpDocItem.get(), meDesiredHash))
+ break;
+
+ bool bStatusGood = true;
+ size_t nTabCount = maTableItems.size();
+ for (size_t i = 0; i < nTabCount && bStatusGood; ++i)
+ {
+ if (!lcl_IsInGoodStatus(maTableItems[i].mpProtect.get(), meDesiredHash))
+ bStatusGood = false;
+ }
+ if (!bStatusGood)
+ break;
+
+ mxBtnOk->set_sensitive(true);
+ return;
+ } while (false);
+
+ mxBtnOk->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScRetypePassDlg, OKHdl, weld::Button&, void) { m_xDialog->response(RET_OK); }
+
+IMPL_LINK(ScRetypePassDlg, RetypeBtnHdl, weld::Button&, rBtn, void)
+{
+ ScPassHashProtectable* pProtected = nullptr;
+ if (&rBtn == mxBtnRetypeDoc.get())
+ {
+ // document protection.
+ pProtected = mpDocItem.get();
+ }
+ else
+ {
+ // sheet protection.
+ size_t aPos = 0;
+ while (aPos < maSheets.size() && &rBtn != maSheets[aPos]->m_xButton.get())
+ ++aPos;
+
+ pProtected = aPos < maSheets.size() ? maTableItems[aPos].mpProtect.get() : nullptr;
+ }
+
+ if (!pProtected)
+ // What the ... !?
+ return;
+
+ ScRetypePassInputDlg aDlg(m_xDialog.get(), pProtected);
+ if (aDlg.run() != RET_OK)
+ return;
+
+ // OK is pressed. Update the protected item.
+ if (aDlg.IsRemovePassword())
+ {
+ // Remove password from this item.
+ pProtected->setPassword(OUString());
+ }
+ else
+ {
+ // Set a new password.
+ OUString aNewPass = aDlg.GetNewPassword();
+ pProtected->setPassword(aNewPass);
+ }
+
+ SetDocData();
+ CheckHashStatus();
+}
+
+ScRetypePassInputDlg::ScRetypePassInputDlg(weld::Window* pParent, ScPassHashProtectable* pProtected)
+ : GenericDialogController(pParent, "modules/scalc/ui/retypepassworddialog.ui",
+ "RetypePasswordDialog")
+ , m_pProtected(pProtected)
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnRetypePassword(m_xBuilder->weld_radio_button("retypepassword"))
+ , m_xPasswordGrid(m_xBuilder->weld_widget("passwordgrid"))
+ , m_xPassword1Edit(m_xBuilder->weld_entry("newpassEntry"))
+ , m_xPassword2Edit(m_xBuilder->weld_entry("confirmpassEntry"))
+ , m_xBtnMatchOldPass(m_xBuilder->weld_check_button("mustmatch"))
+ , m_xBtnRemovePassword(m_xBuilder->weld_radio_button("removepassword"))
+{
+ Init();
+}
+
+ScRetypePassInputDlg::~ScRetypePassInputDlg() {}
+
+bool ScRetypePassInputDlg::IsRemovePassword() const { return m_xBtnRemovePassword->get_active(); }
+
+OUString ScRetypePassInputDlg::GetNewPassword() const { return m_xPassword1Edit->get_text(); }
+
+void ScRetypePassInputDlg::Init()
+{
+ m_xBtnOk->connect_clicked(LINK(this, ScRetypePassInputDlg, OKHdl));
+ m_xBtnRetypePassword->connect_toggled(LINK(this, ScRetypePassInputDlg, RadioBtnHdl));
+ m_xBtnRemovePassword->connect_toggled(LINK(this, ScRetypePassInputDlg, RadioBtnHdl));
+ m_xBtnMatchOldPass->connect_toggled(LINK(this, ScRetypePassInputDlg, CheckBoxHdl));
+ Link<weld::Entry&, void> aLink2 = LINK(this, ScRetypePassInputDlg, PasswordModifyHdl);
+ m_xPassword1Edit->connect_changed(aLink2);
+ m_xPassword2Edit->connect_changed(aLink2);
+
+ m_xBtnOk->set_sensitive(false);
+ m_xBtnRetypePassword->set_active(true);
+ m_xBtnMatchOldPass->set_active(true);
+ m_xPassword1Edit->grab_focus();
+}
+
+void ScRetypePassInputDlg::CheckPasswordInput()
+{
+ OUString aPass1 = m_xPassword1Edit->get_text();
+ OUString aPass2 = m_xPassword2Edit->get_text();
+
+ if (aPass1.isEmpty() || aPass2.isEmpty())
+ {
+ // Empty password is not allowed.
+ m_xBtnOk->set_sensitive(false);
+ return;
+ }
+
+ if (aPass1 != aPass2)
+ {
+ // The two passwords differ.
+ m_xBtnOk->set_sensitive(false);
+ return;
+ }
+
+ if (!m_xBtnMatchOldPass->get_active())
+ {
+ m_xBtnOk->set_sensitive(true);
+ return;
+ }
+
+ if (!m_pProtected)
+ {
+ // This should never happen!
+ m_xBtnOk->set_sensitive(false);
+ return;
+ }
+
+ bool bPassGood = m_pProtected->verifyPassword(aPass1);
+ m_xBtnOk->set_sensitive(bPassGood);
+}
+
+IMPL_LINK_NOARG(ScRetypePassInputDlg, OKHdl, weld::Button&, void) { m_xDialog->response(RET_OK); }
+
+IMPL_LINK_NOARG(ScRetypePassInputDlg, RadioBtnHdl, weld::Toggleable&, void)
+{
+ if (m_xBtnRetypePassword->get_active())
+ {
+ m_xPasswordGrid->set_sensitive(true);
+ CheckPasswordInput();
+ }
+ else
+ {
+ m_xPasswordGrid->set_sensitive(false);
+ m_xBtnOk->set_sensitive(true);
+ }
+}
+
+IMPL_LINK_NOARG(ScRetypePassInputDlg, CheckBoxHdl, weld::Toggleable&, void)
+{
+ CheckPasswordInput();
+}
+
+IMPL_LINK_NOARG(ScRetypePassInputDlg, PasswordModifyHdl, weld::Entry&, void)
+{
+ CheckPasswordInput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/scuiautofmt.cxx b/sc/source/ui/miscdlgs/scuiautofmt.cxx
new file mode 100644
index 0000000000..ecf446081c
--- /dev/null
+++ b/sc/source/ui/miscdlgs/scuiautofmt.cxx
@@ -0,0 +1,385 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <o3tl/string_view.hxx>
+#include <strings.hrc>
+#include <global.hxx>
+#include <globstr.hrc>
+#include <autoform.hxx>
+#include <strindlg.hxx>
+#include <scuiautofmt.hxx>
+#include <scresid.hxx>
+#include <helpids.h>
+
+// AutoFormat-Dialog:
+
+ScAutoFormatDlg::ScAutoFormatDlg(weld::Window* pParent,
+ ScAutoFormat* pAutoFormat,
+ const ScAutoFormatData* pSelFormatData,
+ const ScViewData& rViewData)
+ : GenericDialogController(pParent, "modules/scalc/ui/autoformattable.ui", "AutoFormatTableDialog")
+ , aStrTitle(ScResId(STR_ADD_AUTOFORMAT_TITLE))
+ , aStrLabel(ScResId(STR_ADD_AUTOFORMAT_LABEL))
+ , aStrClose(ScResId(STR_BTN_AUTOFORMAT_CLOSE))
+ , aStrDelMsg(ScResId(STR_DEL_AUTOFORMAT_MSG))
+ , aStrRename(ScResId(STR_RENAME_AUTOFORMAT_TITLE))
+ , pFormat(pAutoFormat)
+ , pSelFmtData(pSelFormatData)
+ , nIndex(0)
+ , bCoreDataChanged(false)
+ , bFmtInserted(false)
+ , m_xLbFormat(m_xBuilder->weld_tree_view("formatlb"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnRemove(m_xBuilder->weld_button("remove"))
+ , m_xBtnRename(m_xBuilder->weld_button("rename"))
+ , m_xBtnNumFormat(m_xBuilder->weld_check_button("numformatcb"))
+ , m_xBtnBorder(m_xBuilder->weld_check_button("bordercb"))
+ , m_xBtnFont(m_xBuilder->weld_check_button("fontcb"))
+ , m_xBtnPattern(m_xBuilder->weld_check_button("patterncb"))
+ , m_xBtnAlignment(m_xBuilder->weld_check_button("alignmentcb"))
+ , m_xBtnAdjust(m_xBuilder->weld_check_button("autofitcb"))
+ , m_xWndPreview(new weld::CustomWeld(*m_xBuilder, "preview", m_aWndPreview))
+{
+ m_aWndPreview.DetectRTL(rViewData);
+
+ const int nWidth = m_xLbFormat->get_approximate_digit_width() * 32;
+ const int nHeight = m_xLbFormat->get_height_rows(8);
+ m_xLbFormat->set_size_request(nWidth, nHeight);
+ m_xWndPreview->set_size_request(nWidth, nHeight);
+
+ Init();
+ ScAutoFormat::iterator it = pFormat->begin();
+ m_aWndPreview.NotifyChange(it->second.get());
+}
+
+ScAutoFormatDlg::~ScAutoFormatDlg()
+{
+}
+
+void ScAutoFormatDlg::Init()
+{
+ m_xLbFormat->connect_changed( LINK( this, ScAutoFormatDlg, SelFmtHdl ) );
+ m_xBtnNumFormat->connect_toggled( LINK( this, ScAutoFormatDlg, CheckHdl ) );
+ m_xBtnBorder->connect_toggled( LINK( this, ScAutoFormatDlg, CheckHdl ) );
+ m_xBtnFont->connect_toggled( LINK( this, ScAutoFormatDlg, CheckHdl ) );
+ m_xBtnPattern->connect_toggled( LINK( this, ScAutoFormatDlg, CheckHdl ) );
+ m_xBtnAlignment->connect_toggled( LINK( this, ScAutoFormatDlg, CheckHdl ) );
+ m_xBtnAdjust->connect_toggled( LINK( this, ScAutoFormatDlg, CheckHdl ) );
+ m_xBtnAdd->connect_clicked ( LINK( this, ScAutoFormatDlg, AddHdl ) );
+ m_xBtnRemove->connect_clicked ( LINK( this, ScAutoFormatDlg, RemoveHdl ) );
+ m_xBtnOk->connect_clicked ( LINK( this, ScAutoFormatDlg, CloseHdl ) );
+ m_xBtnCancel->connect_clicked ( LINK( this, ScAutoFormatDlg, CloseHdl ) );
+ m_xBtnRename->connect_clicked ( LINK( this, ScAutoFormatDlg, RenameHdl ) );
+ m_xLbFormat->connect_row_activated( LINK( this, ScAutoFormatDlg, DblClkHdl ) );
+
+ for (const auto& rEntry : *pFormat)
+ m_xLbFormat->append_text(rEntry.second->GetName());
+
+ if (pFormat->size() == 1)
+ m_xBtnRemove->set_sensitive(false);
+
+ m_xLbFormat->select(0);
+ m_xBtnRename->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+
+ nIndex = 0;
+ UpdateChecks();
+
+ if ( !pSelFmtData )
+ {
+ m_xBtnAdd->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ bFmtInserted = true;
+ }
+}
+
+void ScAutoFormatDlg::UpdateChecks()
+{
+ const ScAutoFormatData* pData = pFormat->findByIndex(nIndex);
+
+ m_xBtnNumFormat->set_active( pData->GetIncludeValueFormat() );
+ m_xBtnBorder->set_active( pData->GetIncludeFrame() );
+ m_xBtnFont->set_active( pData->GetIncludeFont() );
+ m_xBtnPattern->set_active( pData->GetIncludeBackground() );
+ m_xBtnAlignment->set_active( pData->GetIncludeJustify() );
+ m_xBtnAdjust->set_active( pData->GetIncludeWidthHeight() );
+}
+
+// Handler:
+
+IMPL_LINK(ScAutoFormatDlg, CloseHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnOk.get() || &rBtn == m_xBtnCancel.get())
+ {
+ if ( bCoreDataChanged )
+ ScGlobal::GetOrCreateAutoFormat()->Save();
+
+ m_xDialog->response( (&rBtn == m_xBtnOk.get()) ? RET_OK : RET_CANCEL );
+ }
+}
+
+IMPL_LINK_NOARG(ScAutoFormatDlg, DblClkHdl, weld::TreeView&, bool)
+{
+ if ( bCoreDataChanged )
+ ScGlobal::GetOrCreateAutoFormat()->Save();
+
+ m_xDialog->response( RET_OK );
+
+ return true;
+}
+
+IMPL_LINK(ScAutoFormatDlg, CheckHdl, weld::Toggleable&, rBtn, void)
+{
+ ScAutoFormatData* pData = pFormat->findByIndex(nIndex);
+ bool bCheck = rBtn.get_active();
+
+ if (&rBtn == m_xBtnNumFormat.get())
+ pData->SetIncludeValueFormat( bCheck );
+ else if (&rBtn == m_xBtnBorder.get())
+ pData->SetIncludeFrame( bCheck );
+ else if (&rBtn == m_xBtnFont.get())
+ pData->SetIncludeFont( bCheck );
+ else if (&rBtn == m_xBtnPattern.get())
+ pData->SetIncludeBackground( bCheck );
+ else if (&rBtn == m_xBtnAlignment.get())
+ pData->SetIncludeJustify( bCheck );
+ else if (&rBtn == m_xBtnAdjust.get())
+ pData->SetIncludeWidthHeight( bCheck );
+
+ if ( !bCoreDataChanged )
+ {
+ m_xBtnCancel->set_label(aStrClose);
+ bCoreDataChanged = true;
+ }
+
+ m_aWndPreview.NotifyChange( pData );
+}
+
+IMPL_LINK_NOARG(ScAutoFormatDlg, AddHdl, weld::Button&, void)
+{
+ if ( bFmtInserted || !pSelFmtData )
+ return;
+
+ OUString aStrStandard( SfxResId(STR_STANDARD) );
+ OUString aFormatName;
+ bool bOk = false;
+
+ while ( !bOk )
+ {
+ ScStringInputDlg aDlg(m_xDialog.get(), aStrTitle, aStrLabel, aFormatName,
+ HID_SC_ADD_AUTOFMT, HID_SC_AUTOFMT_NAME);
+
+ if (aDlg.run() == RET_OK)
+ {
+ aFormatName = aDlg.GetInputString();
+
+ if ( !aFormatName.isEmpty() && aFormatName != aStrStandard && pFormat->find(aFormatName) == pFormat->end() )
+ {
+ std::unique_ptr<ScAutoFormatData> pNewData(
+ new ScAutoFormatData( *pSelFmtData ));
+
+ pNewData->SetName( aFormatName );
+ ScAutoFormat::iterator it = pFormat->insert(std::move(pNewData));
+ bFmtInserted = it != pFormat->end();
+
+ if ( bFmtInserted )
+ {
+ size_t nPos = std::distance(pFormat->begin(), it);
+ m_xLbFormat->insert_text(nPos, aFormatName);
+ m_xLbFormat->select_text( aFormatName );
+ m_xBtnAdd->set_sensitive(false);
+
+ if ( !bCoreDataChanged )
+ {
+ m_xBtnCancel->set_label( aStrClose );
+ bCoreDataChanged = true;
+ }
+
+ SelFmtHdl( *m_xLbFormat );
+ bOk = true;
+ }
+ }
+
+ if ( !bFmtInserted )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Error, VclButtonsType::OkCancel,
+ ScResId(STR_INVALID_AFNAME)));
+
+ sal_uInt16 nRet = xBox->run();
+
+ bOk = ( nRet == RET_CANCEL );
+ }
+ }
+ else
+ bOk = true;
+ }
+}
+
+IMPL_LINK_NOARG(ScAutoFormatDlg, RemoveHdl, weld::Button&, void)
+{
+ if ( (nIndex > 0) && (m_xLbFormat->n_children() > 0) )
+ {
+ OUString aMsg = o3tl::getToken(aStrDelMsg, 0, '#' )
+ + m_xLbFormat->get_selected_text()
+ + o3tl::getToken(aStrDelMsg, 1, '#' );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMsg));
+ xQueryBox->set_default_response(RET_YES);
+
+ if (RET_YES == xQueryBox->run())
+ {
+ m_xLbFormat->remove(nIndex);
+ m_xLbFormat->select(nIndex-1);
+
+ if ( nIndex-1 == 0 )
+ m_xBtnRemove->set_sensitive(false);
+
+ if ( !bCoreDataChanged )
+ {
+ m_xBtnCancel->set_label( aStrClose );
+ bCoreDataChanged = true;
+ }
+
+ ScAutoFormat::iterator it = pFormat->begin();
+ std::advance(it, nIndex);
+ pFormat->erase(it);
+ nIndex--;
+
+ SelFmtHdl( *m_xLbFormat );
+ }
+ }
+
+ SelFmtHdl( *m_xLbFormat );
+}
+
+IMPL_LINK_NOARG(ScAutoFormatDlg, RenameHdl, weld::Button&, void)
+{
+ bool bOk = false;
+ while( !bOk )
+ {
+
+ OUString aFormatName = m_xLbFormat->get_selected_text();
+ OUString aEntry;
+
+ ScStringInputDlg aDlg(m_xDialog.get(), aStrRename, aStrLabel, aFormatName,
+ HID_SC_REN_AFMT_DLG, HID_SC_REN_AFMT_NAME);
+ if (aDlg.run() == RET_OK)
+ {
+ bool bFmtRenamed = false;
+ aFormatName = aDlg.GetInputString();
+
+ if (!aFormatName.isEmpty())
+ {
+ ScAutoFormat::iterator it = pFormat->begin(), itEnd = pFormat->end();
+ for (; it != itEnd; ++it)
+ {
+ aEntry = it->second->GetName();
+ if (aFormatName == aEntry)
+ break;
+ }
+ if (it == itEnd)
+ {
+ // no format with this name yet, so we can rename
+
+ m_xLbFormat->remove(nIndex);
+ const ScAutoFormatData* p = pFormat->findByIndex(nIndex);
+ std::unique_ptr<ScAutoFormatData> pNewData(new ScAutoFormatData(*p));
+
+ it = pFormat->begin();
+ std::advance(it, nIndex);
+ pFormat->erase(it);
+
+ pNewData->SetName( aFormatName );
+
+ pFormat->insert(std::move(pNewData));
+
+ m_xLbFormat->freeze();
+ m_xLbFormat->clear();
+ for (it = pFormat->begin(); it != itEnd; ++it)
+ {
+ aEntry = it->second->GetName();
+ m_xLbFormat->append_text(aEntry);
+ }
+
+ m_xLbFormat->thaw();
+ m_xLbFormat->select_text(aFormatName);
+
+ if ( !bCoreDataChanged )
+ {
+ m_xBtnCancel->set_label( aStrClose );
+ bCoreDataChanged = true;
+ }
+
+ SelFmtHdl( *m_xLbFormat );
+ bOk = true;
+ bFmtRenamed = true;
+ }
+ }
+ if( !bFmtRenamed )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Error, VclButtonsType::OkCancel,
+ ScResId(STR_INVALID_AFNAME)));
+
+ bOk = RET_CANCEL == xBox->run();
+ }
+ }
+ else
+ bOk = true;
+ }
+}
+
+IMPL_LINK_NOARG(ScAutoFormatDlg, SelFmtHdl, weld::TreeView&, void)
+{
+ nIndex = m_xLbFormat->get_selected_index();
+ UpdateChecks();
+
+ if ( nIndex == 0 )
+ {
+ m_xBtnRename->set_sensitive(false);
+ m_xBtnRemove->set_sensitive(false);
+ }
+ else
+ {
+ m_xBtnRename->set_sensitive(true);
+ m_xBtnRemove->set_sensitive(true);
+ }
+
+ ScAutoFormatData* p = pFormat->findByIndex(nIndex);
+ m_aWndPreview.NotifyChange(p);
+}
+
+OUString ScAutoFormatDlg::GetCurrFormatName()
+{
+ const ScAutoFormatData* p = pFormat->findByIndex(nIndex);
+ return p ? p->GetName() : OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/sharedocdlg.cxx b/sc/source/ui/miscdlgs/sharedocdlg.cxx
new file mode 100644
index 0000000000..5ebe52a8e6
--- /dev/null
+++ b/sc/source/ui/miscdlgs/sharedocdlg.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 <o3tl/safeint.hxx>
+#include <osl/security.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/dialoghelper.hxx>
+#include <svl/sharecontrolfile.hxx>
+#include <unotools/useroptions.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <docsh.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <scresid.hxx>
+#include <sharedocdlg.hxx>
+#include <strings.hrc>
+#include <viewdata.hxx>
+
+using namespace ::com::sun::star;
+
+IMPL_LINK(ScShareDocumentDlg, SizeAllocated, const Size&, rSize, void)
+{
+ OUString sWidestAccessString = getWidestDateTime(ScGlobal::getLocaleData(), false);
+ const int nAccessWidth = m_xLbUsers->get_pixel_size(sWidestAccessString).Width() * 2;
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(rSize.Width() - nAccessWidth)
+ };
+ m_xLbUsers->set_column_fixed_widths(aWidths);
+}
+
+
+ScShareDocumentDlg::ScShareDocumentDlg(weld::Window* pParent, const ScViewData* pViewData)
+ : GenericDialogController(pParent, "modules/scalc/ui/sharedocumentdlg.ui",
+ "ShareDocumentDialog")
+ , m_aStrNoUserData(ScResId(STR_NO_USER_DATA_AVAILABLE))
+ , m_aStrUnknownUser(ScResId(STR_UNKNOWN_USER_CONFLICT))
+ , m_aStrExclusiveAccess(ScResId(STR_EXCLUSIVE_ACCESS))
+ , mpDocShell(nullptr)
+ , m_xCbShare(m_xBuilder->weld_check_button("share"))
+ , m_xFtWarning(m_xBuilder->weld_label("warning"))
+ , m_xLbUsers(m_xBuilder->weld_tree_view("users"))
+{
+
+ OSL_ENSURE( pViewData, "ScShareDocumentDlg CTOR: mpViewData is null!" );
+ mpDocShell = ( pViewData ? pViewData->GetDocShell() : nullptr );
+ OSL_ENSURE( mpDocShell, "ScShareDocumentDlg CTOR: mpDocShell is null!" );
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xLbUsers->get_approximate_digit_width() * 25)
+ };
+ m_xLbUsers->set_column_fixed_widths(aWidths);
+
+ m_xLbUsers->set_size_request(-1, m_xLbUsers->get_height_rows(9));
+ m_xLbUsers->connect_size_allocate(LINK(this, ScShareDocumentDlg, SizeAllocated));
+
+ bool bIsDocShared = mpDocShell && mpDocShell->IsDocShared();
+ m_xCbShare->set_active(bIsDocShared);
+ m_xCbShare->connect_toggled( LINK( this, ScShareDocumentDlg, ToggleHandle ) );
+ m_xFtWarning->set_sensitive(bIsDocShared);
+
+ m_xLbUsers->set_selection_mode(SelectionMode::NONE);
+
+ UpdateView();
+}
+
+ScShareDocumentDlg::~ScShareDocumentDlg()
+{
+}
+
+IMPL_LINK_NOARG(ScShareDocumentDlg, ToggleHandle, weld::Toggleable&, void)
+{
+ m_xFtWarning->set_sensitive(m_xCbShare->get_active());
+}
+
+bool ScShareDocumentDlg::IsShareDocumentChecked() const
+{
+ return m_xCbShare->get_active();
+}
+
+void ScShareDocumentDlg::UpdateView()
+{
+ if ( !mpDocShell )
+ {
+ return;
+ }
+
+ if ( mpDocShell->IsDocShared() )
+ {
+ try
+ {
+ ::svt::ShareControlFile aControlFile( mpDocShell->GetSharedFileURL() );
+ std::vector<LockFileEntry> aUsersData = aControlFile.GetUsersData();
+ sal_Int32 nLength = aUsersData.size();
+
+ if ( nLength > 0 )
+ {
+ sal_Int32 nUnknownUser = 1;
+
+ for ( sal_Int32 i = 0; i < nLength; ++i )
+ {
+ if ( !aUsersData[i][LockFileComponent::EDITTIME].isEmpty() )
+ {
+ OUString aUser;
+ if ( !aUsersData[i][LockFileComponent::OOOUSERNAME].isEmpty() )
+ {
+ aUser = aUsersData[i][LockFileComponent::OOOUSERNAME];
+ }
+ else if ( !aUsersData[i][LockFileComponent::SYSUSERNAME].isEmpty() )
+ {
+ aUser = aUsersData[i][LockFileComponent::SYSUSERNAME];
+ }
+ else
+ {
+ aUser = m_aStrUnknownUser + " " + OUString::number( nUnknownUser++ );
+ }
+
+ // parse the edit time string of the format "DD.MM.YYYY hh:mm"
+ OUString aDateTimeStr = aUsersData[i][LockFileComponent::EDITTIME];
+ sal_Int32 nIndex = 0;
+ std::u16string_view aDateStr = o3tl::getToken(aDateTimeStr, 0, ' ', nIndex );
+ std::u16string_view aTimeStr = o3tl::getToken(aDateTimeStr, 0, ' ', nIndex );
+ nIndex = 0;
+ sal_uInt16 nDay = sal::static_int_cast< sal_uInt16 >( o3tl::toInt32(o3tl::getToken(aDateStr, 0, '.', nIndex )) );
+ sal_uInt16 nMonth = sal::static_int_cast< sal_uInt16 >( o3tl::toInt32(o3tl::getToken(aDateStr, 0, '.', nIndex )) );
+ sal_uInt16 nYear = sal::static_int_cast< sal_uInt16 >( o3tl::toInt32(o3tl::getToken(aDateStr, 0, '.', nIndex )) );
+ nIndex = 0;
+ sal_uInt16 nHours = sal::static_int_cast< sal_uInt16 >( o3tl::toInt32(o3tl::getToken(aTimeStr, 0, ':', nIndex )) );
+ sal_uInt16 nMinutes = sal::static_int_cast< sal_uInt16 >( o3tl::toInt32(o3tl::getToken(aTimeStr, 0, ':', nIndex )) );
+ Date aDate( nDay, nMonth, nYear );
+ tools::Time aTime( nHours, nMinutes );
+ DateTime aDateTime( aDate, aTime );
+
+ OUString aString = formatDateTime(aDateTime, ScGlobal::getLocaleData(), false);
+
+ m_xLbUsers->append_text(aUser);
+ m_xLbUsers->set_text(m_xLbUsers->n_children() - 1, aString, 1);
+ }
+ }
+ }
+ else
+ {
+ m_xLbUsers->append_text(m_aStrNoUserData);
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "ScShareDocumentDlg::UpdateView()" );
+ m_xLbUsers->clear();
+ m_xLbUsers->append_text(m_aStrNoUserData);
+ }
+ }
+ else
+ {
+ // get OOO user name
+ SvtUserOptions aUserOpt;
+ OUString aUser = aUserOpt.GetFirstName();
+ if ( !aUser.isEmpty() )
+ {
+ aUser += " ";
+ }
+ aUser += aUserOpt.GetLastName();
+ if ( aUser.isEmpty() )
+ {
+ // get sys user name
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ aUser = aUserName;
+ }
+ if ( aUser.isEmpty() )
+ {
+ // unknown user name
+ aUser = m_aStrUnknownUser;
+ }
+ aUser += " " + m_aStrExclusiveAccess;
+
+ uno::Reference<document::XDocumentProperties> xDocProps = mpDocShell->GetModel()->getDocumentProperties();
+
+ util::DateTime uDT(xDocProps->getModificationDate());
+ DateTime aDateTime(uDT);
+
+ OUString aString = formatDateTime(aDateTime, ScGlobal::getLocaleData(), false) + " " +
+ ScGlobal::getLocaleData().getTime( aDateTime, false );
+
+ m_xLbUsers->append_text(aUser);
+ m_xLbUsers->set_text(m_xLbUsers->n_children() - 1, aString, 1);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/shtabdlg.cxx b/sc/source/ui/miscdlgs/shtabdlg.cxx
new file mode 100644
index 0000000000..23dc981e6a
--- /dev/null
+++ b/sc/source/ui/miscdlgs/shtabdlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <shtabdlg.hxx>
+
+ScShowTabDlg::ScShowTabDlg(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/showsheetdialog.ui", "ShowSheetDialog")
+ , m_xFrame(m_xBuilder->weld_frame("frame"))
+ , m_xLb(m_xBuilder->weld_tree_view("treeview"))
+{
+ m_xLb->set_selection_mode(SelectionMode::Multiple);
+ m_xLb->set_size_request(-1, m_xLb->get_height_rows(10));
+ m_xLb->connect_row_activated(LINK(this, ScShowTabDlg, DblClkHdl));
+}
+
+ScShowTabDlg::~ScShowTabDlg() {}
+
+void ScShowTabDlg::SetDescription(const OUString& rTitle, const OUString& rFixedText,
+ const OUString& rDlgHelpId, const OUString& sLbHelpId)
+{
+ m_xDialog->set_title(rTitle);
+ m_xFrame->set_label(rFixedText);
+ m_xDialog->set_help_id(rDlgHelpId);
+ m_xLb->set_help_id(sLbHelpId);
+}
+
+void ScShowTabDlg::Insert(const OUString& rString, bool bSelected)
+{
+ m_xLb->append_text(rString);
+ if (bSelected)
+ m_xLb->select(m_xLb->n_children() - 1);
+}
+
+std::vector<sal_Int32> ScShowTabDlg::GetSelectedRows() const
+{
+ auto aTmp = m_xLb->get_selected_rows();
+ return std::vector<sal_Int32>(aTmp.begin(), aTmp.end());
+}
+
+OUString ScShowTabDlg::GetEntry(sal_Int32 nIndex) const { return m_xLb->get_text(nIndex); }
+
+IMPL_LINK_NOARG(ScShowTabDlg, DblClkHdl, weld::TreeView&, bool)
+{
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/simpref.cxx b/sc/source/ui/miscdlgs/simpref.cxx
new file mode 100644
index 0000000000..cb98e263f3
--- /dev/null
+++ b/sc/source/ui/miscdlgs/simpref.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 <reffact.hxx>
+#include <document.hxx>
+#include <simpref.hxx>
+
+ScSimpleRefDlg::ScSimpleRefDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent)
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/simplerefdialog.ui", "SimpleRefDialog")
+ , bAutoReOpen(true)
+ , bCloseOnButtonUp(false)
+ , bSingleCell(false)
+ , bMultiSelection(false)
+ , m_xFtAssign(m_xBuilder->weld_label("area"))
+ , m_xEdAssign(new formula::RefEdit(m_xBuilder->weld_entry("assign")))
+ , m_xRbAssign(new formula::RefButton(m_xBuilder->weld_button("assignref")))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xEdAssign->SetReferences(this, m_xFtAssign.get());
+ m_xRbAssign->SetReferences(this, m_xEdAssign.get());
+
+ // in order to keep the Strings with the FixedTexts in the resource:
+ Init();
+ SetDispatcherLock( true ); // activate modal mode
+}
+
+ScSimpleRefDlg::~ScSimpleRefDlg()
+{
+ SetDispatcherLock( false ); // deactivate modal mode
+}
+
+void ScSimpleRefDlg::FillInfo(SfxChildWinInfo& rWinInfo) const
+{
+ ScAnyRefDlgController::FillInfo(rWinInfo);
+ rWinInfo.bVisible = bAutoReOpen;
+}
+
+void ScSimpleRefDlg::SetRefString(const OUString &rStr)
+{
+ m_xEdAssign->SetText(rStr);
+}
+
+void ScSimpleRefDlg::Init()
+{
+ m_xBtnOk->connect_clicked( LINK( this, ScSimpleRefDlg, OkBtnHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScSimpleRefDlg, CancelBtnHdl ) );
+ bCloseFlag=false;
+}
+
+// Set the reference to a cell range selected with the mouse. This is then
+// shown as the new selection in the reference field.
+void ScSimpleRefDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (!m_xEdAssign->GetWidget()->get_sensitive())
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_xEdAssign.get());
+
+ theCurArea = rRef;
+ OUString aRefStr;
+ if ( bSingleCell )
+ {
+ ScAddress aAdr = rRef.aStart;
+ aRefStr = aAdr.Format(ScRefFlags::ADDR_ABS_3D, &rDocP, rDocP.GetAddressConvention());
+ }
+ else
+ aRefStr = theCurArea.Format(rDocP, ScRefFlags::RANGE_ABS_3D, rDocP.GetAddressConvention());
+
+ if ( bMultiSelection )
+ {
+ OUString aVal = m_xEdAssign->GetText();
+ Selection aSel = m_xEdAssign->GetSelection();
+ aSel.Normalize();
+ aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aRefStr );
+ Selection aNewSel( aSel.Min(), aSel.Min()+aRefStr.getLength() );
+ m_xEdAssign->SetRefString( aVal );
+ m_xEdAssign->SetSelection( aNewSel );
+ }
+ else
+ m_xEdAssign->SetRefString( aRefStr );
+
+ aChangeHdl.Call( aRefStr );
+}
+
+void ScSimpleRefDlg::Close()
+{
+ CancelBtnHdl(*m_xBtnCancel);
+}
+
+void ScSimpleRefDlg::SetActive()
+{
+ m_xEdAssign->GrabFocus();
+
+ // no NameModifyHdl. Otherwise ranges could not be altered
+ // (after marking the reference, the old field content would be shown)
+ // (also, the selected DB name has also not been altered)
+
+ RefInputDone();
+}
+
+bool ScSimpleRefDlg::IsRefInputMode() const
+{
+ return true;
+}
+
+void ScSimpleRefDlg::SetCloseHdl( const Link<const OUString*,void>& rLink )
+{
+ aCloseHdl=rLink;
+}
+
+void ScSimpleRefDlg::SetUnoLinks( const Link<const OUString&,void>& rDone,
+ const Link<const OUString&,void>& rAbort,
+ const Link<const OUString&,void>& rChange )
+{
+ aDoneHdl = rDone;
+ aAbortedHdl = rAbort;
+ aChangeHdl = rChange;
+}
+
+void ScSimpleRefDlg::SetFlags( bool bSetCloseOnButtonUp, bool bSetSingleCell, bool bSetMultiSelection )
+{
+ bCloseOnButtonUp = bSetCloseOnButtonUp;
+ bSingleCell = bSetSingleCell;
+ bMultiSelection = bSetMultiSelection;
+}
+
+void ScSimpleRefDlg::StartRefInput()
+{
+ if ( bMultiSelection )
+ {
+ // initially select the whole string, so it gets replaced by default
+ m_xEdAssign->SelectAll();
+ }
+
+ m_xRbAssign->DoRef();
+ bCloseFlag = true;
+}
+
+void ScSimpleRefDlg::RefInputDone( bool bForced)
+{
+ ScAnyRefDlgController::RefInputDone(bForced);
+ if ( (bForced || bCloseOnButtonUp) && bCloseFlag )
+ OkBtnHdl(*m_xBtnOk);
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScSimpleRefDlg, OkBtnHdl, weld::Button&, void)
+{
+ if (IsClosing())
+ return;
+ bAutoReOpen = false;
+ OUString aResult=m_xEdAssign->GetText();
+ aCloseHdl.Call(&aResult);
+ Link<const OUString&,void> aUnoLink = aDoneHdl; // stack var because this is deleted in DoClose
+ DoClose( ScSimpleRefDlgWrapper::GetChildWindowId() );
+ aUnoLink.Call( aResult );
+}
+
+IMPL_LINK_NOARG(ScSimpleRefDlg, CancelBtnHdl, weld::Button&, void)
+{
+ if (IsClosing())
+ return;
+ bAutoReOpen = false;
+ OUString aResult=m_xEdAssign->GetText();
+ aCloseHdl.Call(nullptr);
+ Link<const OUString&,void> aUnoLink = aAbortedHdl; // stack var because this is deleted in DoClose
+ DoClose( ScSimpleRefDlgWrapper::GetChildWindowId() );
+ aUnoLink.Call( aResult );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/solveroptions.cxx b/sc/source/ui/miscdlgs/solveroptions.cxx
new file mode 100644
index 0000000000..3d5b2b47c1
--- /dev/null
+++ b/sc/source/ui/miscdlgs/solveroptions.cxx
@@ -0,0 +1,416 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <solveroptions.hxx>
+#include <global.hxx>
+#include <miscuno.hxx>
+#include <solverutil.hxx>
+
+#include <rtl/math.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+#include <com/sun/star/sheet/XSolver.hpp>
+#include <com/sun/star/sheet/XSolverDescription.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <utility>
+
+using namespace com::sun::star;
+
+namespace {
+
+/// Helper for sorting properties
+struct ScSolverOptionsEntry
+{
+ sal_Int32 nPosition;
+ OUString aDescription;
+
+ ScSolverOptionsEntry() : nPosition(0) {}
+
+ bool operator< (const ScSolverOptionsEntry& rOther) const
+ {
+ return (ScGlobal::GetCollator().compareString( aDescription, rOther.aDescription ) < 0);
+ }
+};
+
+}
+
+ScSolverOptionsDialog::ScSolverOptionsDialog(weld::Window* pParent,
+ const uno::Sequence<OUString>& rImplNames,
+ const uno::Sequence<OUString>& rDescriptions,
+ OUString aEngine,
+ const uno::Sequence<beans::PropertyValue>& rProperties )
+ : GenericDialogController(pParent, "modules/scalc/ui/solveroptionsdialog.ui", "SolverOptionsDialog")
+ , maImplNames(rImplNames)
+ , maEngine(std::move(aEngine))
+ , maProperties(rProperties)
+ , m_xLbEngine(m_xBuilder->weld_combo_box("engine"))
+ , m_xLbSettings(m_xBuilder->weld_tree_view("settings"))
+ , m_xBtnEdit(m_xBuilder->weld_button("edit"))
+{
+ m_xLbSettings->set_size_request(m_xLbSettings->get_approximate_digit_width() * 32,
+ m_xLbSettings->get_height_rows(6));
+
+ m_xLbSettings->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ m_xLbEngine->connect_changed( LINK( this, ScSolverOptionsDialog, EngineSelectHdl ) );
+
+ m_xBtnEdit->connect_clicked( LINK( this, ScSolverOptionsDialog, ButtonHdl ) );
+
+ m_xLbSettings->connect_changed( LINK( this, ScSolverOptionsDialog, SettingsSelHdl ) );
+ m_xLbSettings->connect_row_activated( LINK( this, ScSolverOptionsDialog, SettingsDoubleClickHdl ) );
+
+ sal_Int32 nSelect = -1;
+ sal_Int32 nImplCount = maImplNames.getLength();
+ for (sal_Int32 nImpl=0; nImpl<nImplCount; ++nImpl)
+ {
+ OUString aImplName( maImplNames[nImpl] );
+ OUString aDescription( rDescriptions[nImpl] ); // user-visible descriptions in list box
+ m_xLbEngine->append_text(aDescription);
+ if ( aImplName == maEngine )
+ nSelect = nImpl;
+ }
+ if ( nSelect < 0 ) // no (valid) engine given
+ {
+ if ( nImplCount > 0 )
+ {
+ maEngine = maImplNames[0]; // use first implementation
+ nSelect = 0;
+ }
+ else
+ maEngine.clear();
+ maProperties.realloc(0); // don't use options from different engine
+ }
+ if ( nSelect >= 0 ) // select in list box
+ m_xLbEngine->set_active(nSelect);
+
+ if ( !maProperties.hasElements() )
+ ReadFromComponent(); // fill maProperties from component (using maEngine)
+ FillListBox(); // using maProperties
+}
+
+ScSolverOptionsDialog::~ScSolverOptionsDialog()
+{
+ if (m_xIntDialog)
+ m_xIntDialog->response(RET_CANCEL);
+ assert(!m_xIntDialog);
+ if (m_xValDialog)
+ m_xValDialog->response(RET_CANCEL);
+ assert(!m_xValDialog);
+}
+
+const uno::Sequence<beans::PropertyValue>& ScSolverOptionsDialog::GetProperties()
+{
+ // update maProperties from list box content
+ // order of entries in list box and maProperties is the same
+ sal_Int32 nEntryCount = maProperties.getLength();
+ if (nEntryCount == m_xLbSettings->n_children())
+ {
+ auto maPropertiesRange = asNonConstRange(maProperties);
+ for (sal_Int32 nEntryPos=0; nEntryPos<nEntryCount; ++nEntryPos)
+ {
+ uno::Any& rValue = maPropertiesRange[nEntryPos].Value;
+ if (ScSolverOptionsString* pStringItem = weld::fromId<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntryPos)))
+ {
+ if (pStringItem->IsDouble())
+ rValue <<= pStringItem->GetDoubleValue();
+ else
+ rValue <<= pStringItem->GetIntValue();
+ }
+ else
+ rValue <<= m_xLbSettings->get_toggle(nEntryPos) == TRISTATE_TRUE;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "wrong count" );
+ }
+
+ return maProperties;
+}
+
+void ScSolverOptionsDialog::FillListBox()
+{
+ // get property descriptions, sort by them
+
+ uno::Reference<sheet::XSolverDescription> xDesc( ScSolverUtil::GetSolver( maEngine ), uno::UNO_QUERY );
+ sal_Int32 nCount = maProperties.getLength();
+ std::vector<ScSolverOptionsEntry> aDescriptions( nCount );
+ for (sal_Int32 nPos=0; nPos<nCount; nPos++)
+ {
+ OUString aPropName( maProperties[nPos].Name );
+ OUString aVisName;
+ if ( xDesc.is() )
+ aVisName = xDesc->getPropertyDescription( aPropName );
+ if ( aVisName.isEmpty() )
+ aVisName = aPropName;
+ aDescriptions[nPos].nPosition = nPos;
+ aDescriptions[nPos].aDescription = aVisName;
+ }
+ std::sort( aDescriptions.begin(), aDescriptions.end() );
+
+ // also update maProperties to the order of descriptions
+
+ uno::Sequence<beans::PropertyValue> aNewSeq;
+ aNewSeq.realloc( nCount );
+ std::transform(aDescriptions.begin(), aDescriptions.end(), aNewSeq.getArray(),
+ [this](const ScSolverOptionsEntry& rDescr) -> beans::PropertyValue { return maProperties[ rDescr.nPosition ]; });
+ maProperties = aNewSeq;
+
+ // fill the list box
+
+ m_xLbSettings->freeze();
+ m_xLbSettings->clear();
+
+ for (sal_Int32 nPos=0; nPos<nCount; nPos++)
+ {
+ OUString aVisName = aDescriptions[nPos].aDescription;
+
+ uno::Any aValue = maProperties[nPos].Value;
+ uno::TypeClass eClass = aValue.getValueTypeClass();
+
+ m_xLbSettings->append();
+
+ if ( eClass == uno::TypeClass_BOOLEAN )
+ {
+ // check box entry
+ m_xLbSettings->set_toggle(nPos, ScUnoHelpFunctions::GetBoolFromAny(aValue) ? TRISTATE_TRUE : TRISTATE_FALSE);
+ m_xLbSettings->set_text(nPos, aVisName, 0);
+ }
+ else
+ {
+ // value entry
+ m_xLbSettings->set_text(nPos, aVisName, 0);
+ m_aOptions.emplace_back(new ScSolverOptionsString(aVisName));
+ if (eClass == uno::TypeClass_DOUBLE)
+ {
+ double fDoubleValue = 0.0;
+ if (aValue >>= fDoubleValue)
+ m_aOptions.back()->SetDoubleValue(fDoubleValue);
+
+ OUString sTxt = aVisName + ": " +
+ rtl::math::doubleToUString(fDoubleValue,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true );
+
+ m_xLbSettings->set_text(nPos, sTxt, 0);
+ }
+ else
+ {
+ sal_Int32 nIntValue = 0;
+ if (aValue >>= nIntValue)
+ m_aOptions.back()->SetIntValue(nIntValue);
+
+ OUString sTxt = aVisName + ": " + OUString::number(nIntValue);
+
+ m_xLbSettings->set_text(nPos, sTxt, 0);
+ }
+ m_xLbSettings->set_id(nPos, weld::toId(m_aOptions.back().get()));
+ }
+ }
+
+ m_xLbSettings->thaw();
+}
+
+void ScSolverOptionsDialog::ReadFromComponent()
+{
+ maProperties = ScSolverUtil::GetDefaults( maEngine );
+}
+
+void ScSolverOptionsDialog::EditOption()
+{
+ int nEntry = m_xLbSettings->get_selected_index();
+ if (nEntry == -1)
+ return;
+ ScSolverOptionsString* pStringItem = weld::fromId<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntry));
+ if (!pStringItem)
+ return;
+
+ if (pStringItem->IsDouble())
+ {
+ m_xValDialog = std::make_shared<ScSolverValueDialog>(m_xDialog.get());
+ m_xValDialog->SetOptionName(pStringItem->GetText());
+ if (maProperties[nEntry].Name == "DECR")
+ m_xValDialog->SetMax(1.0);
+ else if (maProperties[nEntry].Name == "DEFactorMax")
+ m_xValDialog->SetMax(1.2);
+ else if (maProperties[nEntry].Name == "DEFactorMin")
+ m_xValDialog->SetMax(1.2);
+ else if (maProperties[nEntry].Name == "PSCL")
+ m_xValDialog->SetMax(0.005);
+ m_xValDialog->SetValue(pStringItem->GetDoubleValue());
+ weld::DialogController::runAsync(m_xValDialog, [nEntry, pStringItem, this](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ pStringItem->SetDoubleValue(m_xValDialog->GetValue());
+
+ OUString sTxt(pStringItem->GetText() + ": " +
+ rtl::math::doubleToUString(pStringItem->GetDoubleValue(),
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true ));
+
+ m_xLbSettings->set_text(nEntry, sTxt, 0);
+ }
+ m_xValDialog.reset();
+ });
+ }
+ else
+ {
+ m_xIntDialog = std::make_shared<ScSolverIntegerDialog>(m_xDialog.get());
+ m_xIntDialog->SetOptionName( pStringItem->GetText() );
+ if (maProperties[nEntry].Name == "EpsilonLevel")
+ m_xIntDialog->SetMax(3);
+ else if (maProperties[nEntry].Name == "Algorithm")
+ m_xIntDialog->SetMax(1);
+ m_xIntDialog->SetValue( pStringItem->GetIntValue() );
+ weld::DialogController::runAsync(m_xIntDialog, [nEntry, pStringItem, this](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ pStringItem->SetIntValue(m_xIntDialog->GetValue());
+
+ OUString sTxt(
+ pStringItem->GetText() + ": " + OUString::number(pStringItem->GetIntValue()));
+
+ m_xLbSettings->set_text(nEntry, sTxt, 0);
+ }
+ m_xIntDialog.reset();
+ });
+ }
+}
+
+IMPL_LINK( ScSolverOptionsDialog, ButtonHdl, weld::Button&, rBtn, void )
+{
+ if (&rBtn == m_xBtnEdit.get())
+ EditOption();
+}
+
+IMPL_LINK_NOARG(ScSolverOptionsDialog, SettingsDoubleClickHdl, weld::TreeView&, bool)
+{
+ EditOption();
+ return true;
+}
+
+IMPL_LINK_NOARG(ScSolverOptionsDialog, EngineSelectHdl, weld::ComboBox&, void)
+{
+ const sal_Int32 nSelectPos = m_xLbEngine->get_active();
+ if ( nSelectPos < maImplNames.getLength() )
+ {
+ OUString aNewEngine( maImplNames[nSelectPos] );
+ if ( aNewEngine != maEngine )
+ {
+ maEngine = aNewEngine;
+ ReadFromComponent(); // fill maProperties from component (using maEngine)
+ FillListBox(); // using maProperties
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScSolverOptionsDialog, SettingsSelHdl, weld::TreeView&, void)
+{
+ bool bCheckbox = false;
+
+ int nEntry = m_xLbSettings->get_selected_index();
+ if (nEntry != -1)
+ {
+ ScSolverOptionsString* pStringItem = weld::fromId<ScSolverOptionsString*>(m_xLbSettings->get_id(nEntry));
+ if (!pStringItem)
+ bCheckbox = true;
+ }
+
+ m_xBtnEdit->set_sensitive(!bCheckbox);
+}
+
+ScSolverIntegerDialog::ScSolverIntegerDialog(weld::Window * pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/integerdialog.ui", "IntegerDialog")
+ , m_xFrame(m_xBuilder->weld_frame("frame"))
+ , m_xNfValue(m_xBuilder->weld_spin_button("value"))
+{
+}
+
+ScSolverIntegerDialog::~ScSolverIntegerDialog()
+{
+}
+
+void ScSolverIntegerDialog::SetOptionName( const OUString& rName )
+{
+ m_xFrame->set_label(rName);
+}
+
+void ScSolverIntegerDialog::SetValue( sal_Int32 nValue )
+{
+ m_xNfValue->set_value( nValue );
+}
+
+void ScSolverIntegerDialog::SetMax( sal_Int32 nMax )
+{
+ m_xNfValue->set_range(0, nMax);
+}
+
+sal_Int32 ScSolverIntegerDialog::GetValue() const
+{
+ return m_xNfValue->get_value();
+}
+
+ScSolverValueDialog::ScSolverValueDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/scalc/ui/doubledialog.ui", "DoubleDialog")
+ , m_xFrame(m_xBuilder->weld_frame("frame"))
+ , m_xEdValue(m_xBuilder->weld_entry("value"))
+ , m_fMaxValue(std::numeric_limits<double>::quiet_NaN())
+{
+}
+
+ScSolverValueDialog::~ScSolverValueDialog()
+{
+}
+
+void ScSolverValueDialog::SetOptionName( const OUString& rName )
+{
+ m_xFrame->set_label(rName);
+}
+
+void ScSolverValueDialog::SetValue( double fValue )
+{
+ m_xEdValue->set_text( rtl::math::doubleToUString( fValue,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true ) );
+}
+
+void ScSolverValueDialog::SetMax(double fMax)
+{
+ m_fMaxValue = fMax;
+}
+
+double ScSolverValueDialog::GetValue() const
+{
+ OUString aInput = m_xEdValue->get_text();
+
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ sal_Int32 nParseEnd = 0;
+ double fValue = ScGlobal::getLocaleData().stringToDouble( aInput, true, &eStatus, &nParseEnd);
+ /* TODO: shouldn't there be some error checking? */
+ if (!std::isnan(m_fMaxValue) && fValue > m_fMaxValue)
+ fValue = m_fMaxValue;
+ return fValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/solverutil.cxx b/sc/source/ui/miscdlgs/solverutil.cxx
new file mode 100644
index 0000000000..31772a05cf
--- /dev/null
+++ b/sc/source/ui/miscdlgs/solverutil.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 <solverutil.hxx>
+
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/sheet/XSolver.hpp>
+#include <com/sun/star/sheet/XSolverDescription.hpp>
+
+#include <osl/diagnose.h>
+#include <comphelper/processfactory.hxx>
+#include <sal/log.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace com::sun::star;
+
+constexpr OUString SCSOLVER_SERVICE = u"com.sun.star.sheet.Solver"_ustr;
+
+void ScSolverUtil::GetImplementations( uno::Sequence<OUString>& rImplNames,
+ uno::Sequence<OUString>& rDescriptions )
+{
+ rImplNames.realloc(0); // clear
+ rDescriptions.realloc(0);
+
+ uno::Reference<uno::XComponentContext> xCtx(
+ comphelper::getProcessComponentContext() );
+
+ uno::Reference<container::XContentEnumerationAccess> xEnAc(
+ xCtx->getServiceManager(), uno::UNO_QUERY );
+ if ( !xEnAc.is() )
+ return;
+
+ uno::Reference<container::XEnumeration> xEnum =
+ xEnAc->createContentEnumeration( SCSOLVER_SERVICE );
+ if ( !xEnum.is() )
+ return;
+
+ sal_Int32 nCount = 0;
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Any aAny = xEnum->nextElement();
+ uno::Reference<lang::XServiceInfo> xInfo;
+ aAny >>= xInfo;
+ if ( xInfo.is() )
+ {
+ uno::Reference<lang::XSingleComponentFactory> xCFac( xInfo, uno::UNO_QUERY );
+ if ( xCFac.is() )
+ {
+ OUString sName = xInfo->getImplementationName();
+
+ try
+ {
+ uno::Reference<sheet::XSolver> xSolver(
+ xCFac->createInstanceWithContext(xCtx), uno::UNO_QUERY );
+ uno::Reference<sheet::XSolverDescription> xDesc( xSolver, uno::UNO_QUERY );
+ OUString sDescription;
+ if ( xDesc.is() )
+ sDescription = xDesc->getComponentDescription();
+
+ if ( sDescription.isEmpty() )
+ sDescription = sName; // use implementation name if no description available
+
+ rImplNames.realloc( nCount+1 );
+ rImplNames.getArray()[nCount] = sName;
+ rDescriptions.realloc( nCount+1 );
+ rDescriptions.getArray()[nCount] = sDescription;
+ ++nCount;
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("sc.ui", "ScSolverUtil::GetImplementations: cannot instantiate: " << sName);
+ }
+ }
+ }
+ }
+}
+
+uno::Reference<sheet::XSolver> ScSolverUtil::GetSolver( std::u16string_view rImplName )
+{
+ uno::Reference<sheet::XSolver> xSolver;
+
+ uno::Reference<uno::XComponentContext> xCtx(
+ comphelper::getProcessComponentContext() );
+
+ uno::Reference<container::XContentEnumerationAccess> xEnAc(
+ xCtx->getServiceManager(), uno::UNO_QUERY );
+ if ( xEnAc.is() )
+ {
+ uno::Reference<container::XEnumeration> xEnum =
+ xEnAc->createContentEnumeration( SCSOLVER_SERVICE );
+ if ( xEnum.is() )
+ {
+ while ( xEnum->hasMoreElements() && !xSolver.is() )
+ {
+ uno::Any aAny = xEnum->nextElement();
+ uno::Reference<lang::XServiceInfo> xInfo;
+ aAny >>= xInfo;
+ if ( xInfo.is() )
+ {
+ uno::Reference<lang::XSingleComponentFactory> xCFac( xInfo, uno::UNO_QUERY );
+ if ( xCFac.is() )
+ {
+ OUString sName = xInfo->getImplementationName();
+ if ( sName == rImplName )
+ xSolver.set( xCFac->createInstanceWithContext(xCtx), uno::UNO_QUERY );
+ }
+ }
+ }
+ }
+ }
+
+ SAL_WARN_IF( !xSolver.is(), "sc.ui", "can't get solver" );
+ return xSolver;
+}
+
+uno::Sequence<beans::PropertyValue> ScSolverUtil::GetDefaults( std::u16string_view rImplName )
+{
+ uno::Sequence<beans::PropertyValue> aDefaults;
+
+ uno::Reference<sheet::XSolver> xSolver = GetSolver( rImplName );
+ uno::Reference<beans::XPropertySet> xPropSet( xSolver, uno::UNO_QUERY );
+ if ( !xPropSet.is() )
+ {
+ // no XPropertySet - no options
+ return aDefaults;
+ }
+
+ // fill maProperties
+
+ uno::Reference<beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo();
+ OSL_ENSURE( xInfo.is(), "can't get property set info" );
+ if ( !xInfo.is() )
+ return aDefaults;
+
+ const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
+ const sal_Int32 nSize = aPropSeq.getLength();
+ aDefaults.realloc(nSize);
+ auto pDefaults = aDefaults.getArray();
+ sal_Int32 nValid = 0;
+ for (const beans::Property& rProp : aPropSeq)
+ {
+ uno::Any aValue = xPropSet->getPropertyValue( rProp.Name );
+ uno::TypeClass eClass = aValue.getValueTypeClass();
+ // only use properties of supported types
+ if ( eClass == uno::TypeClass_BOOLEAN || eClass == uno::TypeClass_LONG || eClass == uno::TypeClass_DOUBLE )
+ pDefaults[nValid++] = beans::PropertyValue( rProp.Name, -1, aValue, beans::PropertyState_DIRECT_VALUE );
+ }
+ aDefaults.realloc(nValid);
+
+ //! get user-visible names, sort by them
+
+ return aDefaults;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/solvrdlg.cxx b/sc/source/ui/miscdlgs/solvrdlg.cxx
new file mode 100644
index 0000000000..f7cd8e27b6
--- /dev/null
+++ b/sc/source/ui/miscdlgs/solvrdlg.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 <scitems.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/numformat.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <uiitems.hxx>
+#include <reffact.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <solvrdlg.hxx>
+
+ScSolverDlg::ScSolverDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScDocument* pDocument,
+ const ScAddress& aCursorPos )
+
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/goalseekdlg.ui", "GoalSeekDialog")
+ , theFormulaCell(aCursorPos)
+ , theVariableCell(aCursorPos)
+ , pDoc(pDocument)
+ , nCurTab(aCursorPos.Tab())
+ , bDlgLostFocus(false)
+ , errMsgInvalidVar(ScResId(STR_INVALIDVAR))
+ , errMsgInvalidForm(ScResId(STR_INVALIDFORM))
+ , errMsgNoFormula(ScResId(STR_NOFORMULA))
+ , errMsgInvalidVal(ScResId(STR_INVALIDVAL))
+ , m_pEdActive(nullptr)
+ , m_xFtFormulaCell(m_xBuilder->weld_label("formulatext"))
+ , m_xEdFormulaCell(new formula::RefEdit(m_xBuilder->weld_entry("formulaedit")))
+ , m_xRBFormulaCell(new formula::RefButton(m_xBuilder->weld_button("formulabutton")))
+ , m_xEdTargetVal(m_xBuilder->weld_entry("target"))
+ , m_xFtVariableCell(m_xBuilder->weld_label("vartext"))
+ , m_xEdVariableCell(new formula::RefEdit(m_xBuilder->weld_entry("varedit")))
+ , m_xRBVariableCell(new formula::RefButton(m_xBuilder->weld_button("varbutton")))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xEdFormulaCell->SetReferences(this, m_xFtFormulaCell.get());
+ m_xRBFormulaCell->SetReferences(this, m_xEdFormulaCell.get());
+ m_xEdVariableCell->SetReferences(this, m_xFtVariableCell.get());
+ m_xRBVariableCell->SetReferences(this, m_xEdVariableCell.get());
+ Init();
+}
+
+ScSolverDlg::~ScSolverDlg()
+{
+ if (m_xMessageBox)
+ m_xMessageBox->response(RET_CANCEL);
+ assert(!m_xMessageBox);
+}
+
+void ScSolverDlg::Init()
+{
+ m_xBtnOk->connect_clicked( LINK( this, ScSolverDlg, BtnHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScSolverDlg, BtnHdl ) );
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScSolverDlg, GetEditFocusHdl );
+ m_xEdFormulaCell->SetGetFocusHdl( aEditLink );
+ m_xEdVariableCell->SetGetFocusHdl( aEditLink );
+
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScSolverDlg, GetButtonFocusHdl );
+ m_xRBFormulaCell->SetGetFocusHdl( aButtonLink );
+ m_xRBVariableCell->SetGetFocusHdl( aButtonLink );
+
+ m_xEdTargetVal->connect_focus_in(LINK(this, ScSolverDlg, GetFocusHdl));
+
+ aEditLink = LINK( this, ScSolverDlg, LoseEditFocusHdl );
+ m_xEdFormulaCell->SetLoseFocusHdl ( aEditLink );
+ m_xEdVariableCell->SetLoseFocusHdl ( aEditLink );
+
+ aButtonLink = LINK( this, ScSolverDlg, LoseButtonFocusHdl );
+ m_xRBFormulaCell->SetLoseFocusHdl ( aButtonLink );
+ m_xRBVariableCell->SetLoseFocusHdl ( aButtonLink );
+
+ OUString aStr(theFormulaCell.Format(ScRefFlags::ADDR_ABS, nullptr, pDoc->GetAddressConvention()));
+
+ m_xEdFormulaCell->SetText( aStr );
+ m_xEdFormulaCell->GrabFocus();
+ m_pEdActive = m_xEdFormulaCell.get();
+}
+
+void ScSolverDlg::Close()
+{
+ DoClose( ScSolverDlgWrapper::GetChildWindowId() );
+}
+
+void ScSolverDlg::SetActive()
+{
+ if ( bDlgLostFocus )
+ {
+ bDlgLostFocus = false;
+ if( m_pEdActive )
+ m_pEdActive->GrabFocus();
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+ RefInputDone();
+}
+
+void ScSolverDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if( !m_pEdActive )
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_pEdActive);
+
+ ScAddress aAdr = rRef.aStart;
+ ScRefFlags nFmt = ( aAdr.Tab() == nCurTab )
+ ? ScRefFlags::ADDR_ABS
+ : ScRefFlags::ADDR_ABS_3D;
+
+ OUString aStr(aAdr.Format(nFmt, &rDocP, rDocP.GetAddressConvention()));
+ m_pEdActive->SetRefString( aStr );
+
+ if (m_pEdActive == m_xEdFormulaCell.get())
+ theFormulaCell = aAdr;
+ else if (m_pEdActive == m_xEdVariableCell.get())
+ theVariableCell = aAdr;
+}
+
+void ScSolverDlg::RaiseError( ScSolverErr eError )
+{
+ OUString sMessage;
+
+ switch (eError)
+ {
+ case SOLVERR_NOFORMULA:
+ sMessage = errMsgNoFormula;
+ break;
+ case SOLVERR_INVALID_FORMULA:
+ sMessage = errMsgInvalidForm;
+ break;
+ case SOLVERR_INVALID_VARIABLE:
+ sMessage = errMsgInvalidVar;
+ break;
+ case SOLVERR_INVALID_TARGETVALUE:
+ sMessage = errMsgInvalidVal;
+ break;
+ }
+
+ m_xMessageBox.reset(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ sMessage));
+ m_xMessageBox->runAsync(m_xMessageBox, [this](sal_Int32 /*nResult*/) {
+ m_xEdTargetVal->grab_focus();
+ m_xMessageBox.reset();
+ });
+}
+
+bool ScSolverDlg::IsRefInputMode() const
+{
+ return m_pEdActive != nullptr;
+}
+
+bool ScSolverDlg::CheckTargetValue( const OUString& rStrVal )
+{
+ sal_uInt32 n1 = 0;
+ double n2;
+
+ return pDoc->GetFormatTable()->IsNumberFormat( rStrVal, n1, n2 );
+}
+
+// Handler:
+
+IMPL_LINK(ScSolverDlg, BtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnOk.get())
+ {
+ theTargetValStr = m_xEdTargetVal->get_text();
+
+ // The following code checks:
+ // 1. do the strings contain correct references / defined names?
+ // 2. does the formula coordinate refer to a cell containing a formula?
+ // 3. has a valid target value been entered?
+
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+ ScRefFlags nRes1 = theFormulaCell .Parse( m_xEdFormulaCell->GetText(), *pDoc, eConv );
+ ScRefFlags nRes2 = theVariableCell.Parse( m_xEdVariableCell->GetText(), *pDoc, eConv );
+
+ if ( (nRes1 & ScRefFlags::VALID) == ScRefFlags::VALID )
+ {
+ if ( (nRes2 & ScRefFlags::VALID) == ScRefFlags::VALID )
+ {
+ if ( CheckTargetValue( theTargetValStr ) )
+ {
+ CellType eType = pDoc->GetCellType( theFormulaCell.Col(),
+ theFormulaCell.Row(),
+ theFormulaCell.Tab());
+
+ if ( CELLTYPE_FORMULA == eType )
+ {
+ ScSolveParam aOutParam( theFormulaCell,
+ theVariableCell,
+ theTargetValStr );
+ ScSolveItem aOutItem( SCITEM_SOLVEDATA, &aOutParam );
+
+ SetDispatcherLock( false );
+
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(SID_SOLVE,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aOutItem });
+ response(RET_OK);
+ }
+ else RaiseError( SOLVERR_NOFORMULA );
+ }
+ else RaiseError( SOLVERR_INVALID_TARGETVALUE );
+ }
+ else RaiseError( SOLVERR_INVALID_VARIABLE );
+ }
+ else RaiseError( SOLVERR_INVALID_FORMULA );
+ }
+ else if (&rBtn == m_xBtnCancel.get())
+ {
+ response(RET_CANCEL);
+ }
+}
+
+IMPL_LINK(ScSolverDlg, GetEditFocusHdl, formula::RefEdit&, rCtrl, void)
+{
+ if (&rCtrl == m_xEdFormulaCell.get())
+ m_pEdActive = m_xEdFormulaCell.get();
+ else if (&rCtrl == m_xEdVariableCell.get())
+ m_pEdActive = m_xEdVariableCell.get();
+
+ if (m_pEdActive)
+ m_pEdActive->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScSolverDlg, GetFocusHdl, weld::Widget&, void)
+{
+ m_pEdActive = nullptr;
+ m_xEdTargetVal->select_region(0, -1);
+}
+
+IMPL_LINK(ScSolverDlg, GetButtonFocusHdl, formula::RefButton&, rCtrl, void)
+{
+ if (&rCtrl == m_xRBFormulaCell.get())
+ m_pEdActive = m_xEdFormulaCell.get();
+ else if (&rCtrl == m_xRBVariableCell.get())
+ m_pEdActive = m_xEdVariableCell.get();
+
+ if (m_pEdActive)
+ m_pEdActive->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScSolverDlg, LoseEditFocusHdl, formula::RefEdit&, void)
+{
+ bDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScSolverDlg, LoseButtonFocusHdl, formula::RefButton&, void)
+{
+ bDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/strindlg.cxx b/sc/source/ui/miscdlgs/strindlg.cxx
new file mode 100644
index 0000000000..18112a6934
--- /dev/null
+++ b/sc/source/ui/miscdlgs/strindlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <strindlg.hxx>
+
+ScStringInputDlg::ScStringInputDlg(weld::Window* pParent,
+ const OUString& rTitle,
+ const OUString& rEditTitle,
+ const OUString& rDefault,
+ const OUString& rHelpId, const OUString& rEditHelpId)
+ : GenericDialogController(pParent, "modules/scalc/ui/inputstringdialog.ui",
+ "InputStringDialog")
+ , m_xLabel(m_xBuilder->weld_label("description_label"))
+ , m_xEdInput(m_xBuilder->weld_entry("name_entry"))
+{
+ m_xLabel->set_label(rEditTitle);
+ m_xDialog->set_title(rTitle);
+ m_xDialog->set_help_id(rHelpId);
+ m_xEdInput->set_text(rDefault);
+ m_xEdInput->set_help_id(rEditHelpId);
+ m_xEdInput->select_region(0, -1);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/tabbgcolordlg.cxx b/sc/source/ui/miscdlgs/tabbgcolordlg.cxx
new file mode 100644
index 0000000000..7a346a4e3b
--- /dev/null
+++ b/sc/source/ui/miscdlgs/tabbgcolordlg.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <tabbgcolordlg.hxx>
+
+#include <tools/color.hxx>
+#include <vcl/event.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+ScTabBgColorDlg::ScTabBgColorDlg(weld::Window* pParent, const OUString& rTitle,
+ const OUString& rTabBgColorNoColorText, const Color& rDefaultColor)
+ : GenericDialogController(pParent, "modules/scalc/ui/tabcolordialog.ui", "TabColorDialog")
+ , m_aTabBgColor(rDefaultColor)
+ , m_xSelectPalette(m_xBuilder->weld_combo_box("paletteselector"))
+ , m_xTabBgColorSet(new ScTabBgColorValueSet(m_xBuilder->weld_scrolled_window("colorsetwin", true)))
+ , m_xTabBgColorSetWin(new weld::CustomWeld(*m_xBuilder, "colorset", *m_xTabBgColorSet))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+{
+ m_xTabBgColorSet->SetDialog(this);
+ m_xTabBgColorSet->SetColCount(SvxColorValueSet::getColumnCount());
+
+ m_xDialog->set_title(rTitle);
+
+ const WinBits nBits(m_xTabBgColorSet->GetStyle() | WB_NAMEFIELD | WB_ITEMBORDER | WB_NONEFIELD | WB_3DLOOK | WB_NO_DIRECTSELECT);
+ m_xTabBgColorSet->SetStyle(nBits);
+ m_xTabBgColorSet->SetText(rTabBgColorNoColorText);
+
+ const sal_uInt32 nColCount = SvxColorValueSet::getColumnCount();
+ const sal_uInt32 nRowCount(10);
+ const sal_uInt32 nLength = SvxColorValueSet::getEntryEdgeLength();
+ Size aSize(m_xTabBgColorSet->CalcWindowSizePixel(Size(nLength, nLength), nColCount, nRowCount));
+ m_xTabBgColorSetWin->set_size_request(aSize.Width() + 8, aSize.Height() + 8);
+
+ FillPaletteLB();
+
+ m_xSelectPalette->connect_changed(LINK(this, ScTabBgColorDlg, SelectPaletteLBHdl));
+ m_xTabBgColorSet->SetDoubleClickHdl(LINK(this, ScTabBgColorDlg, TabBgColorDblClickHdl_Impl));
+ m_xBtnOk->connect_clicked(LINK(this, ScTabBgColorDlg, TabBgColorOKHdl_Impl));
+}
+
+ScTabBgColorDlg::~ScTabBgColorDlg()
+{
+}
+
+void ScTabBgColorDlg::GetSelectedColor( Color& rColor ) const
+{
+ rColor = m_aTabBgColor;
+}
+
+void ScTabBgColorDlg::FillPaletteLB()
+{
+ m_xSelectPalette->clear();
+ std::vector<OUString> aPaletteList = m_aPaletteManager.GetPaletteList();
+ for (auto const& palette : aPaletteList)
+ {
+ m_xSelectPalette->append_text(palette);
+ }
+ OUString aPaletteName( officecfg::Office::Common::UserColors::PaletteName::get() );
+ m_xSelectPalette->set_active_text(aPaletteName);
+ if (m_xSelectPalette->get_active() != -1)
+ {
+ SelectPaletteLBHdl(*m_xSelectPalette);
+ }
+}
+
+IMPL_LINK_NOARG(ScTabBgColorDlg, SelectPaletteLBHdl, weld::ComboBox&, void)
+{
+ m_xTabBgColorSet->Clear();
+ sal_Int32 nPos = m_xSelectPalette->get_active();
+ m_aPaletteManager.SetPalette( nPos );
+ m_aPaletteManager.ReloadColorSet(*m_xTabBgColorSet);
+ m_xTabBgColorSet->Resize();
+ m_xTabBgColorSet->SelectItem(0);
+}
+
+// Handler, called when color selection is changed
+IMPL_LINK_NOARG(ScTabBgColorDlg, TabBgColorDblClickHdl_Impl, ValueSet*, void)
+{
+ sal_uInt16 nItemId = m_xTabBgColorSet->GetSelectedItemId();
+ Color aColor = nItemId ? ( m_xTabBgColorSet->GetItemColor( nItemId ) ) : COL_AUTO;
+ m_aTabBgColor = aColor;
+ m_xDialog->response(RET_OK);
+}
+
+// Handler, called when the OK button is pushed
+IMPL_LINK_NOARG(ScTabBgColorDlg, TabBgColorOKHdl_Impl, weld::Button&, void)
+{
+ sal_uInt16 nItemId = m_xTabBgColorSet->GetSelectedItemId();
+ Color aColor = nItemId ? ( m_xTabBgColorSet->GetItemColor( nItemId ) ) : COL_AUTO;
+ m_aTabBgColor = aColor;
+ m_xDialog->response(RET_OK);
+}
+
+ScTabBgColorDlg::ScTabBgColorValueSet::ScTabBgColorValueSet(std::unique_ptr<weld::ScrolledWindow> pWindow)
+ : SvxColorValueSet(std::move(pWindow))
+ , m_pTabBgColorDlg(nullptr)
+{
+}
+
+ScTabBgColorDlg::ScTabBgColorValueSet::~ScTabBgColorValueSet()
+{
+}
+
+bool ScTabBgColorDlg::ScTabBgColorValueSet::KeyInput( const KeyEvent& rKEvt )
+{
+ switch ( rKEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_SPACE:
+ case KEY_RETURN:
+ {
+ sal_uInt16 nItemId = GetSelectedItemId();
+ const Color& aColor = nItemId ? ( GetItemColor( nItemId ) ) : COL_AUTO;
+ m_pTabBgColorDlg->m_aTabBgColor = aColor;
+ m_pTabBgColorDlg->response(RET_OK);
+ return true;
+ }
+ break;
+ }
+ return SvxColorValueSet::KeyInput(rKEvt);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/tabopdlg.cxx b/sc/source/ui/miscdlgs/tabopdlg.cxx
new file mode 100644
index 0000000000..7e9be6ea82
--- /dev/null
+++ b/sc/source/ui/miscdlgs/tabopdlg.cxx
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <uiitems.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <reffact.hxx>
+
+#include <tabopdlg.hxx>
+
+
+ScTabOpDlg::ScTabOpDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScDocument* pDocument,
+ const ScRefAddress& rCursorPos )
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/multipleoperationsdialog.ui",
+ "MultipleOperationsDialog")
+ , theFormulaCell(rCursorPos)
+ , pDoc(pDocument)
+ , nCurTab(theFormulaCell.Tab())
+ , bDlgLostFocus(false)
+ , errMsgNoFormula(ScResId(STR_NOFORMULASPECIFIED))
+ , errMsgNoColRow(ScResId(STR_NOCOLROW))
+ , errMsgWrongFormula(ScResId(STR_WRONGFORMULA))
+ , errMsgWrongRowCol(ScResId(STR_WRONGROWCOL))
+ , errMsgNoColFormula(ScResId(STR_NOCOLFORMULA))
+ , errMsgNoRowFormula(ScResId(STR_NOROWFORMULA))
+ , m_pEdActive(nullptr)
+ , m_xFtFormulaRange(m_xBuilder->weld_label("formulasft"))
+ , m_xEdFormulaRange(new formula::RefEdit(m_xBuilder->weld_entry("formulas")))
+ , m_xRBFormulaRange(new formula::RefButton(m_xBuilder->weld_button("formulasref")))
+ , m_xFtRowCell(m_xBuilder->weld_label("rowft"))
+ , m_xEdRowCell(new formula::RefEdit(m_xBuilder->weld_entry("row")))
+ , m_xRBRowCell(new formula::RefButton(m_xBuilder->weld_button("rowref")))
+ , m_xFtColCell(m_xBuilder->weld_label("colft"))
+ , m_xEdColCell(new formula::RefEdit(m_xBuilder->weld_entry("col")))
+ , m_xRBColCell(new formula::RefButton(m_xBuilder->weld_button("colref")))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xEdFormulaRange->SetReferences(this, m_xFtFormulaRange.get());
+ m_xRBFormulaRange->SetReferences(this, m_xEdFormulaRange.get());
+
+ m_xEdRowCell->SetReferences(this, m_xFtRowCell.get());
+ m_xRBRowCell->SetReferences(this, m_xEdRowCell.get());
+
+ m_xEdColCell->SetReferences(this, m_xFtColCell.get());
+ m_xRBColCell->SetReferences(this, m_xEdColCell.get());
+
+ Init();
+}
+
+ScTabOpDlg::~ScTabOpDlg()
+{
+}
+
+void ScTabOpDlg::Init()
+{
+ m_xBtnOk->connect_clicked( LINK( this, ScTabOpDlg, BtnHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScTabOpDlg, BtnHdl ) );
+
+ Link<formula::RefEdit&,void> aEditLink = LINK( this, ScTabOpDlg, GetEditFocusHdl );
+ m_xEdFormulaRange->SetGetFocusHdl( aEditLink );
+ m_xEdRowCell->SetGetFocusHdl( aEditLink );
+ m_xEdColCell->SetGetFocusHdl( aEditLink );
+
+ Link<formula::RefButton&,void> aButtonLink = LINK( this, ScTabOpDlg, GetButtonFocusHdl );
+ m_xRBFormulaRange->SetGetFocusHdl( aButtonLink );
+ m_xRBRowCell->SetGetFocusHdl( aButtonLink );
+ m_xRBColCell->SetGetFocusHdl( aButtonLink );
+
+ aEditLink = LINK( this, ScTabOpDlg, LoseEditFocusHdl );
+ m_xEdFormulaRange->SetLoseFocusHdl( aEditLink );
+ m_xEdRowCell->SetLoseFocusHdl( aEditLink );
+ m_xEdColCell->SetLoseFocusHdl( aEditLink );
+
+ aButtonLink = LINK( this, ScTabOpDlg, LoseButtonFocusHdl );
+ m_xRBFormulaRange->SetLoseFocusHdl( aButtonLink );
+ m_xRBRowCell->SetLoseFocusHdl( aButtonLink );
+ m_xRBColCell->SetLoseFocusHdl( aButtonLink );
+
+ m_xEdFormulaRange->GrabFocus();
+ m_pEdActive = m_xEdFormulaRange.get();
+}
+
+void ScTabOpDlg::Close()
+{
+ DoClose( ScTabOpDlgWrapper::GetChildWindowId() );
+}
+
+void ScTabOpDlg::SetActive()
+{
+ if ( bDlgLostFocus )
+ {
+ bDlgLostFocus = false;
+ if (m_pEdActive)
+ m_pEdActive->GrabFocus();
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+void ScTabOpDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (!m_pEdActive)
+ return;
+
+ ScAddress::Details aDetails(rDocP.GetAddressConvention(), 0, 0);
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_pEdActive);
+
+ OUString aStr;
+ ScRefFlags nFmt = ( rRef.aStart.Tab() == nCurTab )
+ ? ScRefFlags::RANGE_ABS
+ : ScRefFlags::RANGE_ABS_3D;
+
+ if (m_pEdActive == m_xEdFormulaRange.get())
+ {
+ theFormulaCell.Set( rRef.aStart, false, false, false);
+ theFormulaEnd.Set( rRef.aEnd, false, false, false);
+ aStr = rRef.Format(rDocP, nFmt, aDetails);
+ }
+ else if (m_pEdActive == m_xEdRowCell.get())
+ {
+ theRowCell.Set( rRef.aStart, false, false, false);
+ aStr = rRef.aStart.Format(nFmt, &rDocP, aDetails);
+ }
+ else if (m_pEdActive == m_xEdColCell.get())
+ {
+ theColCell.Set( rRef.aStart, false, false, false);
+ aStr = rRef.aStart.Format(nFmt, &rDocP, aDetails);
+ }
+
+ m_pEdActive->SetRefString( aStr );
+}
+
+void ScTabOpDlg::RaiseError( ScTabOpErr eError )
+{
+ const OUString* pMsg = &errMsgNoFormula;
+ formula::RefEdit* pEd = m_xEdFormulaRange.get();
+
+ switch ( eError )
+ {
+ case TABOPERR_NOFORMULA:
+ pMsg = &errMsgNoFormula;
+ pEd = m_xEdFormulaRange.get();
+ break;
+
+ case TABOPERR_NOCOLROW:
+ pMsg = &errMsgNoColRow;
+ pEd = m_xEdRowCell.get();
+ break;
+
+ case TABOPERR_WRONGFORMULA:
+ pMsg = &errMsgWrongFormula;
+ pEd = m_xEdFormulaRange.get();
+ break;
+
+ case TABOPERR_WRONGROW:
+ pMsg = &errMsgWrongRowCol;
+ pEd = m_xEdRowCell.get();
+ break;
+
+ case TABOPERR_NOCOLFORMULA:
+ pMsg = &errMsgNoColFormula;
+ pEd = m_xEdFormulaRange.get();
+ break;
+
+ case TABOPERR_WRONGCOL:
+ pMsg = &errMsgWrongRowCol;
+ pEd = m_xEdColCell.get();
+ break;
+
+ case TABOPERR_NOROWFORMULA:
+ pMsg = &errMsgNoRowFormula;
+ pEd = m_xEdFormulaRange.get();
+ break;
+ }
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Error, VclButtonsType::OkCancel, *pMsg));
+ xBox->run();
+ pEd->GrabFocus();
+}
+
+static bool lcl_Parse( const OUString& rString, const ScDocument& rDoc, SCTAB nCurTab,
+ ScRefAddress& rStart, ScRefAddress& rEnd )
+{
+ bool bRet = false;
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ if ( rString.indexOf(':') != -1 )
+ bRet = ConvertDoubleRef( rDoc, rString, nCurTab, rStart, rEnd, eConv );
+ else
+ {
+ bRet = ConvertSingleRef( rDoc, rString, nCurTab, rStart, eConv );
+ rEnd = rStart;
+ }
+ return bRet;
+}
+
+// Handler:
+
+IMPL_LINK(ScTabOpDlg, BtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == m_xBtnOk.get())
+ {
+ ScTabOpParam::Mode eMode = ScTabOpParam::Column;
+ sal_uInt16 nError = 0;
+
+ // The following code checks:
+ // 1. do the strings contain correct cell references / defined names?
+ // 2. is formula range row if row is empty or column if column is empty
+ // or single reference if both?
+ // 3. is at least one of row or column non-empty?
+
+ if (m_xEdFormulaRange->GetText().isEmpty())
+ nError = TABOPERR_NOFORMULA;
+ else if (m_xEdRowCell->GetText().isEmpty() &&
+ m_xEdColCell->GetText().isEmpty())
+ nError = TABOPERR_NOCOLROW;
+ else if ( !lcl_Parse( m_xEdFormulaRange->GetText(), *pDoc, nCurTab,
+ theFormulaCell, theFormulaEnd ) )
+ nError = TABOPERR_WRONGFORMULA;
+ else
+ {
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+ if (!m_xEdRowCell->GetText().isEmpty())
+ {
+ if (!ConvertSingleRef( *pDoc, m_xEdRowCell->GetText(), nCurTab,
+ theRowCell, eConv ))
+ nError = TABOPERR_WRONGROW;
+ else
+ {
+ if (m_xEdColCell->GetText().isEmpty() &&
+ theFormulaCell.Col() != theFormulaEnd.Col())
+ nError = TABOPERR_NOCOLFORMULA;
+ else
+ eMode = ScTabOpParam::Row;
+ }
+ }
+ if (!m_xEdColCell->GetText().isEmpty())
+ {
+ if (!ConvertSingleRef( *pDoc, m_xEdColCell->GetText(), nCurTab,
+ theColCell, eConv ))
+ nError = TABOPERR_WRONGCOL;
+ else
+ {
+ if (eMode == ScTabOpParam::Row) // both
+ {
+ eMode = ScTabOpParam::Both;
+ ConvertSingleRef( *pDoc, m_xEdFormulaRange->GetText(), nCurTab,
+ theFormulaCell, eConv );
+ }
+ else if (theFormulaCell.Row() != theFormulaEnd.Row())
+ nError = TABOPERR_NOROWFORMULA;
+ else
+ eMode = ScTabOpParam::Column;
+ }
+ }
+ }
+
+ if (nError)
+ RaiseError( static_cast<ScTabOpErr>(nError) );
+ else
+ {
+ ScTabOpParam aOutParam(theFormulaCell, theFormulaEnd, theRowCell, theColCell, eMode);
+ ScTabOpItem aOutItem( SID_TABOP, &aOutParam );
+
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(SID_TABOP,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aOutItem });
+ response(RET_OK);
+ }
+ }
+ else if (&rBtn == m_xBtnCancel.get())
+ response(RET_CANCEL);
+}
+
+IMPL_LINK( ScTabOpDlg, GetEditFocusHdl, formula::RefEdit&, rCtrl, void )
+{
+ if (&rCtrl == m_xEdFormulaRange.get())
+ m_pEdActive = m_xEdFormulaRange.get();
+ else if (&rCtrl == m_xEdRowCell.get())
+ m_pEdActive = m_xEdRowCell.get();
+ else if (&rCtrl == m_xEdColCell.get())
+ m_pEdActive = m_xEdColCell.get();
+ else
+ m_pEdActive = nullptr;
+
+ if( m_pEdActive )
+ m_pEdActive->SelectAll();
+}
+
+IMPL_LINK( ScTabOpDlg, GetButtonFocusHdl, formula::RefButton&, rCtrl, void )
+{
+ if (&rCtrl == m_xRBFormulaRange.get())
+ m_pEdActive = m_xEdFormulaRange.get();
+ else if (&rCtrl == m_xRBRowCell.get())
+ m_pEdActive = m_xEdRowCell.get();
+ else if (&rCtrl == m_xRBColCell.get())
+ m_pEdActive = m_xEdColCell.get();
+ else
+ m_pEdActive = nullptr;
+
+ if( m_pEdActive )
+ m_pEdActive->SelectAll();
+}
+
+IMPL_LINK_NOARG(ScTabOpDlg, LoseEditFocusHdl, formula::RefEdit&, void)
+{
+ bDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+IMPL_LINK_NOARG(ScTabOpDlg, LoseButtonFocusHdl, formula::RefButton&, void)
+{
+ bDlgLostFocus = !m_xDialog->has_toplevel_focus();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/textdlgs.cxx b/sc/source/ui/miscdlgs/textdlgs.cxx
new file mode 100644
index 0000000000..2cf70d8f57
--- /dev/null
+++ b/sc/source/ui/miscdlgs/textdlgs.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svx/svxids.hrc>
+#include <svx/dialogs.hrc>
+
+#include <editeng/flstitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/cjkoptions.hxx>
+
+#include <textdlgs.hxx>
+#include <svl/intitem.hxx>
+#include <svx/flagsdef.hxx>
+
+ScCharDlg::ScCharDlg(weld::Window* pParent, const SfxItemSet* pAttr, const SfxObjectShell* pDocShell, bool bDrawText)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/chardialog.ui", "CharDialog", pAttr)
+ , m_rDocShell(*pDocShell)
+ , m_bDrawText(bDrawText)
+{
+ AddTabPage("font", RID_SVXPAGE_CHAR_NAME);
+ AddTabPage("fonteffects", RID_SVXPAGE_CHAR_EFFECTS);
+ AddTabPage("position", RID_SVXPAGE_CHAR_POSITION);
+
+ if (bDrawText)
+ AddTabPage("background", RID_SVXPAGE_BKG);
+ else
+ RemoveTabPage("background");
+}
+
+void ScCharDlg::PageCreated(const OUString& rId, SfxTabPage &rPage)
+{
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ if (rId == "font")
+ {
+ SvxFontListItem aItem(*static_cast<const SvxFontListItem*>(
+ ( m_rDocShell.GetItem( SID_ATTR_CHAR_FONTLIST) ) ) );
+
+ aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST));
+ rPage.PageCreated(aSet);
+ }
+ else if (rId == "fonteffects")
+ {
+ // Allow CaseMap in drawings, but not in normal text
+ if (!m_bDrawText)
+ aSet.Put (SfxUInt16Item(SID_DISABLE_CTL,DISABLE_CASEMAP));
+ rPage.PageCreated(aSet);
+ }
+ else if (rId == "background")
+ {
+ aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR)));
+ rPage.PageCreated(aSet);
+ }
+}
+
+ScParagraphDlg::ScParagraphDlg(weld::Window* pParent, const SfxItemSet* pAttr)
+ : SfxTabDialogController(pParent, "modules/scalc/ui/paradialog.ui", "ParagraphDialog", pAttr)
+{
+ 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);
+}
+
+void ScParagraphDlg::PageCreated(const OUString& rId, SfxTabPage &rPage)
+{
+ if (rId == "labelTP_TABULATOR")
+ {
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ TabulatorDisableFlags const nFlags((TabulatorDisableFlags::TypeMask &~TabulatorDisableFlags::TypeLeft) |
+ (TabulatorDisableFlags::FillMask &~TabulatorDisableFlags::FillNone));
+ aSet.Put(SfxUInt16Item(SID_SVXTABULATORTABPAGE_DISABLEFLAGS, static_cast<sal_uInt16>(nFlags)));
+ rPage.PageCreated(aSet);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/miscdlgs/warnbox.cxx b/sc/source/ui/miscdlgs/warnbox.cxx
new file mode 100644
index 0000000000..8bb0a37d12
--- /dev/null
+++ b/sc/source/ui/miscdlgs/warnbox.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 <warnbox.hxx>
+
+#include <scmod.hxx>
+#include <inputopt.hxx>
+
+ScReplaceWarnBox::ScReplaceWarnBox(weld::Window* pParent)
+ : MessageDialogController(pParent, "modules/scalc/ui/checkwarningdialog.ui",
+ "CheckWarningDialog", "ask")
+ // By default, the check box is ON, and the user needs to un-check it to
+ // disable all future warnings.
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+{
+ m_xDialog->set_default_response(RET_YES);
+}
+
+short ScReplaceWarnBox::run()
+{
+ short nRet = RET_YES;
+ if (SC_MOD()->GetInputOptions().GetReplaceCellsWarn())
+ {
+ nRet = MessageDialogController::run();
+ if (!m_xWarningOnBox->get_active())
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputOptions aInputOpt(pScMod->GetInputOptions());
+ aInputOpt.SetReplaceCellsWarn(false);
+ pScMod->SetInputOptions(aInputOpt);
+ }
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/namedlg/namedefdlg.cxx b/sc/source/ui/namedlg/namedefdlg.cxx
new file mode 100644
index 0000000000..3304f4d39b
--- /dev/null
+++ b/sc/source/ui/namedlg/namedefdlg.cxx
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <namedefdlg.hxx>
+
+#include <formula/errorcodes.hxx>
+#include <sfx2/app.hxx>
+#include <unotools/charclass.hxx>
+
+#include <compiler.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <globalnames.hxx>
+#include <rangenam.hxx>
+#include <reffact.hxx>
+#include <undorangename.hxx>
+#include <tabvwsh.hxx>
+#include <tokenarray.hxx>
+
+ScNameDefDlg::ScNameDefDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ const ScViewData& rViewData, std::map<OUString, ScRangeName*>&& aRangeMap,
+ const ScAddress& aCursorPos, const bool bUndo )
+ : ScAnyRefDlgController( pB, pCW, pParent, "modules/scalc/ui/definename.ui", "DefineNameDialog")
+ , mbUndo( bUndo )
+ , mrDoc(rViewData.GetDocument())
+ , mpDocShell ( rViewData.GetDocShell() )
+ , maCursorPos( aCursorPos )
+ , maGlobalNameStr ( ScResId(STR_GLOBAL_SCOPE) )
+ , maErrInvalidNameStr( ScResId(STR_ERR_NAME_INVALID))
+ , maErrInvalidNameCellRefStr( ScResId(STR_ERR_NAME_INVALID_CELL_REF))
+ , maErrNameInUse ( ScResId(STR_ERR_NAME_EXISTS))
+ , maRangeMap( std::move(aRangeMap) )
+ , m_xEdName(m_xBuilder->weld_entry("edit"))
+ , m_xEdRange(new formula::RefEdit(m_xBuilder->weld_entry("range")))
+ , m_xRbRange(new formula::RefButton(m_xBuilder->weld_button("refbutton")))
+ , m_xLbScope(m_xBuilder->weld_combo_box("scope"))
+ , m_xBtnRowHeader(m_xBuilder->weld_check_button("rowheader"))
+ , m_xBtnColHeader(m_xBuilder->weld_check_button("colheader"))
+ , m_xBtnPrintArea(m_xBuilder->weld_check_button("printarea"))
+ , m_xBtnCriteria(m_xBuilder->weld_check_button("filter"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xFtInfo(m_xBuilder->weld_label("label"))
+ , m_xFtRange(m_xBuilder->weld_label("label3"))
+{
+ m_xEdRange->SetReferences(this, m_xFtRange.get());
+ m_xRbRange->SetReferences(this, m_xEdRange.get());
+ maStrInfoDefault = m_xFtInfo->get_label();
+
+ // Initialize scope list.
+ m_xLbScope->append_text(maGlobalNameStr);
+ m_xLbScope->set_active(0);
+ SCTAB n = mrDoc.GetTableCount();
+ for (SCTAB i = 0; i < n; ++i)
+ {
+ OUString aTabName;
+ mrDoc.GetName(i, aTabName);
+ m_xLbScope->append_text(aTabName);
+ }
+
+ m_xBtnCancel->connect_clicked( LINK( this, ScNameDefDlg, CancelBtnHdl));
+ m_xBtnAdd->connect_clicked( LINK( this, ScNameDefDlg, AddBtnHdl ));
+ m_xEdName->connect_changed( LINK( this, ScNameDefDlg, NameModifyHdl ));
+ m_xEdRange->SetGetFocusHdl( LINK( this, ScNameDefDlg, AssignGetFocusHdl ) );
+
+ m_xBtnAdd->set_sensitive(false); // empty name is invalid
+
+ ScRange aRange;
+
+ rViewData.GetSimpleArea( aRange );
+ OUString aAreaStr(aRange.Format(mrDoc, ScRefFlags::RANGE_ABS_3D,
+ ScAddress::Details(mrDoc.GetAddressConvention(), 0, 0)));
+
+ m_xEdRange->SetText( aAreaStr );
+
+ m_xEdName->grab_focus();
+ m_xEdName->select_region(0, -1);
+}
+
+ScNameDefDlg::~ScNameDefDlg()
+{
+}
+
+void ScNameDefDlg::CancelPushed()
+{
+ if (mbUndo)
+ response(RET_CANCEL);
+ else
+ {
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ pViewSh->SwitchBetweenRefDialogs(this);
+ }
+}
+
+bool ScNameDefDlg::IsFormulaValid()
+{
+ ScCompiler aComp(mrDoc, maCursorPos, mrDoc.GetGrammar());
+ std::unique_ptr<ScTokenArray> pCode = aComp.CompileString(m_xEdRange->GetText());
+ if (pCode->GetCodeError() != FormulaError::NONE)
+ {
+ //TODO: info message
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+bool ScNameDefDlg::IsNameValid()
+{
+ OUString aScope = m_xLbScope->get_active_text();
+ OUString aName = m_xEdName->get_text();
+
+ bool bIsNameValid = true;
+ OUString aHelpText = maStrInfoDefault;
+
+ ScRangeName* pRangeName = nullptr;
+ if(aScope == maGlobalNameStr)
+ {
+ const auto iter = maRangeMap.find(STR_GLOBAL_RANGE_NAME);
+ assert(iter != maRangeMap.end());
+ pRangeName = iter->second;
+ }
+ else
+ {
+ const auto iter = maRangeMap.find(aScope);
+ assert(iter != maRangeMap.end());
+ pRangeName = iter->second;
+ }
+
+ ScRangeData::IsNameValidType eType;
+ if ( aName.isEmpty() )
+ {
+ bIsNameValid = false;
+ }
+ else if ((eType = ScRangeData::IsNameValid(aName, mrDoc))
+ != ScRangeData::IsNameValidType::NAME_VALID)
+ {
+ if (eType == ScRangeData::IsNameValidType::NAME_INVALID_BAD_STRING)
+ {
+ aHelpText = maErrInvalidNameStr;
+ }
+ else if (eType == ScRangeData::IsNameValidType::NAME_INVALID_CELL_REF)
+ {
+ aHelpText = maErrInvalidNameCellRefStr;
+ }
+ bIsNameValid = false;
+ }
+ else if (pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(aName)))
+ {
+ aHelpText = maErrNameInUse;
+ bIsNameValid = false;
+ }
+
+ if (!IsFormulaValid())
+ {
+ bIsNameValid = false;
+ }
+
+ m_xEdName->set_tooltip_text(aHelpText);
+ m_xEdName->set_message_type(bIsNameValid || aName.isEmpty() ? weld::EntryMessageType::Normal
+ : weld::EntryMessageType::Error);
+ m_xBtnAdd->set_sensitive(bIsNameValid);
+ return bIsNameValid;
+}
+
+void ScNameDefDlg::AddPushed()
+{
+ OUString aScope = m_xLbScope->get_active_text();
+ OUString aName = m_xEdName->get_text();
+ OUString aExpression = m_xEdRange->GetText();
+
+ if (aName.isEmpty())
+ {
+ return;
+ }
+ if (aScope.isEmpty())
+ {
+ return;
+ }
+
+ ScRangeName* pRangeName = nullptr;
+ if(aScope == maGlobalNameStr)
+ {
+ const auto iter = maRangeMap.find(STR_GLOBAL_RANGE_NAME);
+ assert(iter != maRangeMap.end());
+ pRangeName = iter->second;
+ }
+ else
+ {
+ const auto iter = maRangeMap.find(aScope);
+ assert(iter != maRangeMap.end());
+ pRangeName = iter->second;
+ }
+ if (!pRangeName)
+ return;
+
+ if (!IsNameValid()) //should not happen, but make sure we don't break anything
+ return;
+ else
+ {
+ ScRangeData::Type nType = ScRangeData::Type::Name;
+
+ ScRangeData* pNewEntry = new ScRangeData( mrDoc,
+ aName,
+ aExpression,
+ maCursorPos,
+ nType );
+
+ if ( m_xBtnRowHeader->get_active() ) nType |= ScRangeData::Type::RowHeader;
+ if ( m_xBtnColHeader->get_active() ) nType |= ScRangeData::Type::ColHeader;
+ if ( m_xBtnPrintArea->get_active() ) nType |= ScRangeData::Type::PrintArea;
+ if ( m_xBtnCriteria->get_active() ) nType |= ScRangeData::Type::Criteria;
+
+ pNewEntry->AddType(nType);
+
+ // aExpression valid?
+ if ( FormulaError::NONE == pNewEntry->GetErrCode() )
+ {
+ if ( !pRangeName->insert( pNewEntry, false /*bReuseFreeIndex*/ ) )
+ pNewEntry = nullptr;
+
+ if (mbUndo)
+ {
+ // this means we called directly through the menu
+
+ SCTAB nTab;
+ // if no table with that name is found, assume global range name
+ if (!mrDoc.GetTable(aScope, nTab))
+ nTab = -1;
+
+ assert( pNewEntry); // undo of no insertion smells fishy
+ if (pNewEntry)
+ mpDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAddRangeData>( mpDocShell, pNewEntry, nTab) );
+
+ // set table stream invalid, otherwise RangeName won't be saved if no other
+ // call invalidates the stream
+ if (nTab != -1)
+ mrDoc.SetStreamValid(nTab, false);
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ mpDocShell->SetDocumentModified();
+ Close();
+ }
+ else
+ {
+ maName = aName;
+ maScope = aScope;
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ pViewSh->SwitchBetweenRefDialogs(this);
+ }
+ }
+ else
+ {
+ delete pNewEntry;
+ m_xEdRange->GrabFocus();
+ m_xEdRange->SelectAll();
+ }
+ }
+}
+
+void ScNameDefDlg::GetNewData(OUString& rName, OUString& rScope)
+{
+ rName = maName;
+ rScope = maScope;
+}
+
+bool ScNameDefDlg::IsRefInputMode() const
+{
+ return m_xEdRange->GetWidget()->get_sensitive();
+}
+
+void ScNameDefDlg::RefInputDone( bool bForced)
+{
+ ScAnyRefDlgController::RefInputDone(bForced);
+ IsNameValid();
+}
+
+void ScNameDefDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (m_xEdRange->GetWidget()->get_sensitive())
+ {
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_xEdRange.get());
+ OUString aRefStr(rRef.Format(rDocP, ScRefFlags::RANGE_ABS_3D,
+ ScAddress::Details(rDocP.GetAddressConvention(), 0, 0)));
+ m_xEdRange->SetRefString( aRefStr );
+ }
+}
+
+void ScNameDefDlg::Close()
+{
+ DoClose( ScNameDefDlgWrapper::GetChildWindowId() );
+}
+
+void ScNameDefDlg::SetActive()
+{
+ m_xEdRange->GrabFocus();
+ RefInputDone();
+}
+
+IMPL_LINK_NOARG(ScNameDefDlg, CancelBtnHdl, weld::Button&, void)
+{
+ CancelPushed();
+}
+
+IMPL_LINK_NOARG(ScNameDefDlg, AddBtnHdl, weld::Button&, void)
+{
+ AddPushed();
+};
+
+IMPL_LINK_NOARG(ScNameDefDlg, NameModifyHdl, weld::Entry&, void)
+{
+ IsNameValid();
+}
+
+IMPL_LINK_NOARG(ScNameDefDlg, AssignGetFocusHdl, formula::RefEdit&, void)
+{
+ IsNameValid();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/namedlg/namedlg.cxx b/sc/source/ui/namedlg/namedlg.cxx
new file mode 100644
index 0000000000..a6a182cd82
--- /dev/null
+++ b/sc/source/ui/namedlg/namedlg.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <global.hxx>
+#include <reffact.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <docfunc.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <namedlg.hxx>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+
+#include <globalnames.hxx>
+#include <tokenarray.hxx>
+
+#include <vcl/svapp.hxx>
+#include <formula/errorcodes.hxx>
+#include <unotools/charclass.hxx>
+
+#include <map>
+
+//logic
+
+ScNameDlg::ScNameDlg( SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent,
+ ScViewData& rViewData,
+ const ScAddress& aCursorPos,
+ std::map<OUString, ScRangeName> *const pRangeMap)
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/managenamesdialog.ui",
+ "ManageNamesDialog")
+
+ , maGlobalNameStr(ScResId(STR_GLOBAL_SCOPE))
+ , maErrInvalidNameStr(ScResId(STR_ERR_NAME_INVALID))
+ , maErrNameInUse(ScResId(STR_ERR_NAME_EXISTS))
+ , maStrMultiSelect(ScResId(STR_MULTI_SELECT))
+
+ , mrViewData(rViewData)
+ , mrDoc(rViewData.GetDocument())
+ , maCursorPos(aCursorPos)
+ , mbDataChanged(false)
+ , mbCloseWithoutUndo(false)
+
+ , m_xEdName(m_xBuilder->weld_entry("name"))
+ , m_xFtAssign(m_xBuilder->weld_label("label3"))
+ , m_xEdAssign(new formula::RefEdit(m_xBuilder->weld_entry("range")))
+ , m_xRbAssign(new formula::RefButton(m_xBuilder->weld_button("assign")))
+ , m_xLbScope(m_xBuilder->weld_combo_box("scope"))
+ , m_xBtnPrintArea(m_xBuilder->weld_check_button("printrange"))
+ , m_xBtnColHeader(m_xBuilder->weld_check_button("colheader"))
+ , m_xBtnCriteria(m_xBuilder->weld_check_button("filter"))
+ , m_xBtnRowHeader(m_xBuilder->weld_check_button("rowheader"))
+ , m_xBtnAdd(m_xBuilder->weld_button("add"))
+ , m_xBtnDelete(m_xBuilder->weld_button("delete"))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xFtInfo(m_xBuilder->weld_label("info"))
+{
+ m_xEdAssign->SetReferences(this, m_xFtAssign.get());
+ m_xRbAssign->SetReferences(this, m_xEdAssign.get());
+ maStrInfoDefault = m_xFtInfo->get_label();
+
+ if (!pRangeMap)
+ {
+ std::map<OUString, ScRangeName*> aRangeMap;
+ mrDoc.GetRangeNameMap(aRangeMap);
+ for (const auto& [aTemp, pRangeName] : aRangeMap)
+ {
+ m_RangeMap.insert(std::make_pair(aTemp, *pRangeName));
+ }
+ }
+ else
+ {
+ m_RangeMap.swap(*pRangeMap);
+ }
+ Init();
+}
+
+ScNameDlg::~ScNameDlg()
+{
+}
+
+void ScNameDlg::Init()
+{
+ //init UI
+
+ std::unique_ptr<weld::TreeView> xTreeView(m_xBuilder->weld_tree_view("names"));
+ xTreeView->set_size_request(xTreeView->get_approximate_digit_width() * 75,
+ xTreeView->get_height_rows(10));
+ m_xRangeManagerTable.reset(new ScRangeManagerTable(std::move(xTreeView), m_RangeMap, maCursorPos));
+
+ m_xRangeManagerTable->connect_changed( LINK( this, ScNameDlg, SelectionChangedHdl_Impl ) );
+
+ m_xBtnOk->connect_clicked( LINK( this, ScNameDlg, OkBtnHdl ) );
+ m_xBtnCancel->connect_clicked( LINK( this, ScNameDlg, CancelBtnHdl ) );
+ m_xBtnAdd->connect_clicked( LINK( this, ScNameDlg, AddBtnHdl ) );
+ m_xEdAssign->SetGetFocusHdl( LINK( this, ScNameDlg, AssignGetFocusHdl ) );
+ m_xEdAssign->SetModifyHdl ( LINK( this, ScNameDlg, RefEdModifyHdl ) );
+ m_xEdName->connect_changed( LINK( this, ScNameDlg, EdModifyHdl ) );
+ m_xLbScope->connect_changed( LINK(this, ScNameDlg, ScopeChangedHdl) );
+ m_xBtnDelete->connect_clicked( LINK( this, ScNameDlg, RemoveBtnHdl ) );
+ m_xBtnPrintArea->connect_toggled( LINK(this, ScNameDlg, EdModifyCheckBoxHdl ) );
+ m_xBtnCriteria->connect_toggled( LINK(this, ScNameDlg, EdModifyCheckBoxHdl ) );
+ m_xBtnRowHeader->connect_toggled( LINK(this, ScNameDlg, EdModifyCheckBoxHdl ) );
+ m_xBtnColHeader->connect_toggled( LINK(this, ScNameDlg, EdModifyCheckBoxHdl ) );
+
+ // Initialize scope list.
+ m_xLbScope->append_text(maGlobalNameStr);
+ m_xLbScope->set_active(0);
+ SCTAB n = mrDoc.GetTableCount();
+ for (SCTAB i = 0; i < n; ++i)
+ {
+ OUString aTabName;
+ mrDoc.GetName(i, aTabName);
+ m_xLbScope->append_text(aTabName);
+ }
+
+ CheckForEmptyTable();
+
+ if (m_xRangeManagerTable->n_children())
+ {
+ m_xRangeManagerTable->set_cursor(0);
+ m_xRangeManagerTable->CheckForFormulaString();
+ SelectionChanged();
+ }
+
+}
+
+bool ScNameDlg::IsRefInputMode() const
+{
+ return m_xEdAssign->GetWidget()->get_sensitive();
+}
+
+void ScNameDlg::RefInputDone( bool bForced)
+{
+ ScAnyRefDlgController::RefInputDone(bForced);
+ RefEdModifyHdl(*m_xEdAssign);
+}
+
+void ScNameDlg::SetReference( const ScRange& rRef, ScDocument& rDocP )
+{
+ if (m_xEdAssign->GetWidget()->get_sensitive())
+ {
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart(m_xEdAssign.get());
+ OUString aRefStr(rRef.Format(rDocP, ScRefFlags::RANGE_ABS_3D,
+ ScAddress::Details(rDocP.GetAddressConvention(), 0, 0)));
+ m_xEdAssign->SetRefString( aRefStr );
+ }
+}
+
+void ScNameDlg::Close()
+{
+ if (mbDataChanged && !mbCloseWithoutUndo)
+ mrViewData.GetDocFunc().ModifyAllRangeNames(m_RangeMap);
+ DoClose(ScNameDlgWrapper::GetChildWindowId());
+}
+
+void ScNameDlg::CheckForEmptyTable()
+{
+ if (!m_xRangeManagerTable->n_children())
+ {
+ m_xBtnDelete->set_sensitive(false);
+ m_xEdAssign->GetWidget()->set_sensitive(false);
+ m_xRbAssign->GetWidget()->set_sensitive(false);
+ m_xEdName->set_sensitive(false);
+ m_xLbScope->set_sensitive(false);
+
+ m_xBtnCriteria->set_sensitive(false);
+ m_xBtnPrintArea->set_sensitive(false);
+ m_xBtnColHeader->set_sensitive(false);
+ m_xBtnRowHeader->set_sensitive(false);
+ }
+ else
+ {
+ m_xBtnDelete->set_sensitive(true);
+ m_xEdAssign->GetWidget()->set_sensitive(true);
+ m_xRbAssign->GetWidget()->set_sensitive(true);
+ m_xEdName->set_sensitive(true);
+ m_xLbScope->set_sensitive(true);
+
+ m_xBtnCriteria->set_sensitive(true);
+ m_xBtnPrintArea->set_sensitive(true);
+ m_xBtnColHeader->set_sensitive(true);
+ m_xBtnRowHeader->set_sensitive(true);
+ }
+}
+
+void ScNameDlg::SetActive()
+{
+ m_xEdAssign->GrabFocus();
+ RefInputDone();
+}
+
+void ScNameDlg::UpdateChecks(const ScRangeData* pData)
+{
+ // remove handlers, we only want the handlers to process
+ // user input and not when we are syncing the controls with our internal
+ // model ( also UpdateChecks is called already from some other event
+ // handlers, triggering handlers while already processing a handler can
+ // ( and does in this case ) corrupt the internal data
+
+ m_xBtnCriteria->connect_toggled( Link<weld::Toggleable&,void>() );
+ m_xBtnPrintArea->connect_toggled( Link<weld::Toggleable&,void>() );
+ m_xBtnColHeader->connect_toggled( Link<weld::Toggleable&,void>() );
+ m_xBtnRowHeader->connect_toggled( Link<weld::Toggleable&,void>() );
+
+ m_xBtnCriteria->set_active( pData->HasType( ScRangeData::Type::Criteria ) );
+ m_xBtnPrintArea->set_active( pData->HasType( ScRangeData::Type::PrintArea ) );
+ m_xBtnColHeader->set_active( pData->HasType( ScRangeData::Type::ColHeader ) );
+ m_xBtnRowHeader->set_active( pData->HasType( ScRangeData::Type::RowHeader ) );
+
+ // Restore handlers so user input is processed again
+ Link<weld::Toggleable&,void> aToggleHandler = LINK( this, ScNameDlg, EdModifyCheckBoxHdl );
+ m_xBtnCriteria->connect_toggled( aToggleHandler );
+ m_xBtnPrintArea->connect_toggled( aToggleHandler );
+ m_xBtnColHeader->connect_toggled( aToggleHandler );
+ m_xBtnRowHeader->connect_toggled( aToggleHandler );
+}
+
+bool ScNameDlg::IsNameValid()
+{
+ OUString aScope = m_xLbScope->get_active_text();
+ OUString aName = m_xEdName->get_text();
+ aName = aName.trim();
+
+ if (aName.isEmpty())
+ return false;
+
+ ScRangeName* pRangeName = GetRangeName( aScope );
+
+ if (ScRangeData::IsNameValid(aName, mrDoc) != ScRangeData::IsNameValidType::NAME_VALID)
+ {
+ m_xFtInfo->set_label_type(weld::LabelType::Error);
+ m_xFtInfo->set_label(maErrInvalidNameStr);
+ return false;
+ }
+ else if (pRangeName && pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(aName)))
+ {
+ m_xFtInfo->set_label_type(weld::LabelType::Error);
+ m_xFtInfo->set_label(maErrNameInUse);
+ return false;
+ }
+ m_xFtInfo->set_label( maStrInfoDefault );
+ return true;
+}
+
+bool ScNameDlg::IsFormulaValid()
+{
+ ScCompiler aComp(mrDoc, maCursorPos, mrDoc.GetGrammar());
+ std::unique_ptr<ScTokenArray> pCode = aComp.CompileString(m_xEdAssign->GetText());
+ if (pCode->GetCodeError() != FormulaError::NONE)
+ {
+ m_xFtInfo->set_label_type(weld::LabelType::Error);
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+ScRangeName* ScNameDlg::GetRangeName(const OUString& rScope)
+{
+ if (rScope == maGlobalNameStr)
+ {
+ const auto iter = m_RangeMap.find(STR_GLOBAL_RANGE_NAME);
+ assert(iter != m_RangeMap.end());
+ return &iter->second;
+ }
+ else
+ {
+ const auto iter = m_RangeMap.find(rScope);
+ assert(iter != m_RangeMap.end());
+ return &iter->second;
+ }
+}
+
+void ScNameDlg::ShowOptions(const ScRangeNameLine& rLine)
+{
+ ScRangeName* pRangeName = GetRangeName(rLine.aScope);
+ ScRangeData* pData = pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(rLine.aName));
+ if (pData)
+ {
+ UpdateChecks(pData);
+ }
+}
+
+void ScNameDlg::AddPushed()
+{
+ mbCloseWithoutUndo = true;
+ ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
+ pViewSh->SwitchBetweenRefDialogs(this);
+}
+
+void ScNameDlg::SetEntry(const OUString& rName, const OUString& rScope)
+{
+ if (!rName.isEmpty())
+ {
+ mbDataChanged = true;
+ ScRangeNameLine aLine;
+ aLine.aName = rName;
+ aLine.aScope = rScope;
+ m_xRangeManagerTable->SetEntry(aLine);
+ }
+}
+
+void ScNameDlg::RemovePushed()
+{
+ std::vector<ScRangeNameLine> aEntries = m_xRangeManagerTable->GetSelectedEntries();
+ m_xRangeManagerTable->DeleteSelectedEntries();
+ for (const auto& rEntry : aEntries)
+ {
+ ScRangeName* pRangeName = GetRangeName(rEntry.aScope);
+ ScRangeData* pData = pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(rEntry.aName));
+ OSL_ENSURE(pData, "table and model should be in sync");
+ // be safe and check for possible problems
+ if (pData)
+ pRangeName->erase(*pData);
+
+ mbDataChanged = true;
+ }
+ CheckForEmptyTable();
+}
+
+void ScNameDlg::NameModified()
+{
+ ScRangeNameLine aLine;
+ m_xRangeManagerTable->GetCurrentLine(aLine);
+ OUString aOldName = aLine.aName;
+ OUString aNewName = m_xEdName->get_text();
+ aNewName = aNewName.trim();
+ m_xFtInfo->set_label_type(weld::LabelType::Normal);
+ if (aNewName != aOldName)
+ {
+ if (!IsNameValid())
+ return;
+ }
+ else
+ {
+ m_xFtInfo->set_label( maStrInfoDefault );
+ }
+
+ if (!IsFormulaValid())
+ {
+ //TODO: implement an info text
+ return;
+ }
+
+ OUString aOldScope = aLine.aScope;
+ //empty table
+ if (aOldScope.isEmpty())
+ return;
+ OUString aExpr = m_xEdAssign->GetText();
+ OUString aNewScope = m_xLbScope->get_active_text();
+
+ ScRangeName* pOldRangeName = GetRangeName( aOldScope );
+ ScRangeData* pData = pOldRangeName->findByUpperName( ScGlobal::getCharClass().uppercase(aOldName) );
+ ScRangeName* pNewRangeName = GetRangeName( aNewScope );
+ OSL_ENSURE(pData, "model and table should be in sync");
+ // be safe and check for range data
+ if (!pData)
+ return;
+
+ // Assign new index (0) only if the scope is changed, else keep the
+ // existing index.
+ sal_uInt16 nIndex = (aNewScope != aOldScope ? 0 : pData->GetIndex());
+
+ pOldRangeName->erase(*pData);
+ m_xRangeManagerTable->BlockUpdate();
+ m_xRangeManagerTable->DeleteSelectedEntries();
+ ScRangeData::Type nType = ScRangeData::Type::Name;
+ if ( m_xBtnRowHeader->get_active() ) nType |= ScRangeData::Type::RowHeader;
+ if ( m_xBtnColHeader->get_active() ) nType |= ScRangeData::Type::ColHeader;
+ if ( m_xBtnPrintArea->get_active() ) nType |= ScRangeData::Type::PrintArea;
+ if ( m_xBtnCriteria->get_active() ) nType |= ScRangeData::Type::Criteria;
+
+ ScRangeData* pNewEntry = new ScRangeData( mrDoc, aNewName, aExpr,
+ maCursorPos, nType);
+ pNewEntry->SetIndex( nIndex);
+ pNewRangeName->insert(pNewEntry, false /*bReuseFreeIndex*/);
+ aLine.aName = aNewName;
+ aLine.aExpression = aExpr;
+ aLine.aScope = aNewScope;
+ m_xRangeManagerTable->addEntry(aLine, true);
+ // tdf#128137 process pending async row change events while UpdatesBlocked in place
+ Application::Reschedule(true);
+ m_xRangeManagerTable->UnblockUpdate();
+ mbDataChanged = true;
+}
+
+void ScNameDlg::SelectionChanged()
+{
+ //don't update if we have just modified due to user input
+ if (m_xRangeManagerTable->UpdatesBlocked())
+ {
+ return;
+ }
+
+ if (m_xRangeManagerTable->IsMultiSelection())
+ {
+ m_xEdName->set_text(maStrMultiSelect);
+ m_xEdAssign->SetText(maStrMultiSelect);
+
+ m_xEdName->set_sensitive(false);
+ m_xEdAssign->GetWidget()->set_sensitive(false);
+ m_xRbAssign->GetWidget()->set_sensitive(false);
+ m_xLbScope->set_sensitive(false);
+ m_xBtnRowHeader->set_sensitive(false);
+ m_xBtnColHeader->set_sensitive(false);
+ m_xBtnPrintArea->set_sensitive(false);
+ m_xBtnCriteria->set_sensitive(false);
+ }
+ else
+ {
+ ScRangeNameLine aLine;
+ m_xRangeManagerTable->GetCurrentLine(aLine);
+ m_xEdAssign->SetText(aLine.aExpression);
+ m_xEdName->set_text(aLine.aName);
+ m_xLbScope->set_active_text(aLine.aScope);
+ ShowOptions(aLine);
+ m_xBtnDelete->set_sensitive(true);
+ m_xEdName->set_sensitive(true);
+ m_xEdAssign->GetWidget()->set_sensitive(true);
+ m_xRbAssign->GetWidget()->set_sensitive(true);
+ m_xLbScope->set_sensitive(true);
+ m_xBtnRowHeader->set_sensitive(true);
+ m_xBtnColHeader->set_sensitive(true);
+ m_xBtnPrintArea->set_sensitive(true);
+ m_xBtnCriteria->set_sensitive(true);
+ }
+}
+
+void ScNameDlg::ScopeChanged()
+{
+ NameModified();
+}
+
+void ScNameDlg::GetRangeNames(std::map<OUString, ScRangeName>& rRangeMap)
+{
+ m_RangeMap.swap(rRangeMap);
+}
+
+IMPL_LINK_NOARG(ScNameDlg, OkBtnHdl, weld::Button&, void)
+{
+ response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ScNameDlg, CancelBtnHdl, weld::Button&, void)
+{
+ mbCloseWithoutUndo = true;
+ response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(ScNameDlg, AddBtnHdl, weld::Button&, void)
+{
+ AddPushed();
+}
+
+IMPL_LINK_NOARG(ScNameDlg, RemoveBtnHdl, weld::Button&, void)
+{
+ RemovePushed();
+}
+
+IMPL_LINK_NOARG(ScNameDlg, EdModifyCheckBoxHdl, weld::Toggleable&, void)
+{
+ NameModified();
+}
+
+IMPL_LINK_NOARG(ScNameDlg, EdModifyHdl, weld::Entry&, void)
+{
+ NameModified();
+}
+
+IMPL_LINK_NOARG(ScNameDlg, RefEdModifyHdl, formula::RefEdit&, void)
+{
+ NameModified();
+}
+
+IMPL_LINK_NOARG(ScNameDlg, AssignGetFocusHdl, formula::RefEdit&, void)
+{
+ RefEdModifyHdl(*m_xEdAssign);
+}
+
+IMPL_LINK_NOARG(ScNameDlg, SelectionChangedHdl_Impl, weld::TreeView&, void)
+{
+ SelectionChanged();
+}
+
+IMPL_LINK_NOARG(ScNameDlg, ScopeChangedHdl, weld::ComboBox&, void)
+{
+ ScopeChanged();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/namedlg/namemgrtable.cxx b/sc/source/ui/namedlg/namemgrtable.cxx
new file mode 100644
index 0000000000..5b23306653
--- /dev/null
+++ b/sc/source/ui/namedlg/namemgrtable.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/.
+ */
+
+//ScRangeManagerTable
+#include <memory>
+#include <global.hxx>
+#include <globstr.hrc>
+#include <o3tl/safeint.hxx>
+#include <scresid.hxx>
+#include <globalnames.hxx>
+#include <namemgrtable.hxx>
+#include <rangenam.hxx>
+
+#include <unotools/charclass.hxx>
+#include <vcl/weld.hxx>
+#include <tools/link.hxx>
+
+void ScRangeManagerTable::GetCurrentLine(ScRangeNameLine& rLine)
+{
+ std::unique_ptr<weld::TreeIter> xCurrentEntry(m_xTreeView->make_iterator());
+ if (m_xTreeView->get_cursor(xCurrentEntry.get()))
+ GetLine(rLine, *xCurrentEntry);
+}
+
+void ScRangeManagerTable::DeleteSelectedEntries()
+{
+ std::vector<int> aRows = m_xTreeView->get_selected_rows();
+ std::sort(aRows.begin(), aRows.end());
+ for (auto it = aRows.rbegin(); it != aRows.rend(); ++it)
+ m_xTreeView->remove(*it);
+}
+
+bool ScRangeManagerTable::IsMultiSelection() const
+{
+ return m_xTreeView->count_selected_rows() > 1;
+}
+
+void ScRangeManagerTable::SetEntry(const ScRangeNameLine& rLine)
+{
+ for (int i = 0, nEntryCount = m_xTreeView->n_children(); i < nEntryCount; ++i)
+ {
+ if (rLine.aName == m_xTreeView->get_text(i, 0)
+ && rLine.aScope == m_xTreeView->get_text(i, 2))
+ {
+ m_xTreeView->set_cursor(i);
+ }
+ }
+}
+
+ScRangeManagerTable::ScRangeManagerTable(std::unique_ptr<weld::TreeView> xTreeView,
+ const std::map<OUString, ScRangeName>& rRangeMap,
+ const ScAddress& rPos)
+ : m_xTreeView(std::move(xTreeView))
+ , maGlobalString(ScResId(STR_GLOBAL_SCOPE))
+ , m_RangeMap(rRangeMap)
+ , maPos(rPos)
+ , m_nId(0)
+ , mbNeedUpdate(true)
+{
+ auto nColWidth = m_xTreeView->get_size_request().Width() / 7;
+ std::vector<int> aWidths{ o3tl::narrowing<int>(nColWidth * 2),
+ o3tl::narrowing<int>(nColWidth * 3) };
+ m_xTreeView->set_column_fixed_widths(aWidths);
+
+ Init();
+ m_xTreeView->set_selection_mode(SelectionMode::Multiple);
+ m_xTreeView->connect_size_allocate(LINK(this, ScRangeManagerTable, SizeAllocHdl));
+ m_xTreeView->connect_visible_range_changed(LINK(this, ScRangeManagerTable, VisRowsScrolledHdl));
+}
+
+IMPL_LINK_NOARG(ScRangeManagerTable, VisRowsScrolledHdl, weld::TreeView&, void)
+{
+ CheckForFormulaString();
+}
+
+const ScRangeData* ScRangeManagerTable::findRangeData(const ScRangeNameLine& rLine)
+{
+ const ScRangeName* pRangeName;
+ if (rLine.aScope == maGlobalString)
+ {
+ const auto iter = m_RangeMap.find(STR_GLOBAL_RANGE_NAME);
+ assert(iter != m_RangeMap.end());
+ pRangeName = &iter->second;
+ }
+ else
+ {
+ const auto iter = m_RangeMap.find(rLine.aScope);
+ assert(iter != m_RangeMap.end());
+ pRangeName = &iter->second;
+ }
+
+ return pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(rLine.aName));
+}
+
+void ScRangeManagerTable::CheckForFormulaString()
+{
+ if (UpdatesBlocked())
+ return;
+
+ auto lambda = [this](weld::TreeIter& rEntry) {
+ OUString sId(m_xTreeView->get_id(rEntry));
+ std::map<OUString, bool>::const_iterator itr = maCalculatedFormulaEntries.find(sId);
+ if (itr == maCalculatedFormulaEntries.end() || !itr->second)
+ {
+ ScRangeNameLine aLine;
+ GetLine(aLine, rEntry);
+ const ScRangeData* pData = findRangeData(aLine);
+ OUString aFormulaString = pData->GetSymbol(maPos);
+ m_xTreeView->set_text(rEntry, aFormulaString, 1);
+ maCalculatedFormulaEntries.insert(std::pair<OUString, bool>(sId, true));
+ }
+ return false;
+ };
+
+ // ensure all visible entries are up to date
+ m_xTreeView->visible_foreach(lambda);
+ // and ensure all selected entries are up to date
+ m_xTreeView->selected_foreach(lambda);
+}
+
+IMPL_LINK_NOARG(ScRangeManagerTable, SizeAllocHdl, const Size&, void) { CheckForFormulaString(); }
+
+void ScRangeManagerTable::addEntry(const ScRangeNameLine& rLine, bool bSetCurEntry)
+{
+ int nRow = m_xTreeView->n_children();
+ m_xTreeView->append();
+ m_xTreeView->set_text(nRow, rLine.aName, 0);
+ m_xTreeView->set_text(nRow, rLine.aExpression, 1);
+ m_xTreeView->set_text(nRow, rLine.aScope, 2);
+ // just unique to track which one has been cached by maCalculatedFormulaEntries
+ m_xTreeView->set_id(nRow, OUString::number(m_nId++));
+ if (bSetCurEntry)
+ m_xTreeView->set_cursor(nRow);
+}
+
+void ScRangeManagerTable::GetLine(ScRangeNameLine& rLine, const weld::TreeIter& rEntry)
+{
+ rLine.aName = m_xTreeView->get_text(rEntry, 0);
+ rLine.aExpression = m_xTreeView->get_text(rEntry, 1);
+ rLine.aScope = m_xTreeView->get_text(rEntry, 2);
+}
+
+void ScRangeManagerTable::Init()
+{
+ m_xTreeView->freeze();
+ m_xTreeView->clear();
+ for (auto const& itr : m_RangeMap)
+ {
+ const ScRangeName& rLocalRangeName = itr.second;
+ ScRangeNameLine aLine;
+ if (itr.first == STR_GLOBAL_RANGE_NAME)
+ aLine.aScope = maGlobalString;
+ else
+ aLine.aScope = itr.first;
+ for (const auto& rEntry : rLocalRangeName)
+ {
+ // Database and hidden named ranges are not shown in the Manage Names dialog
+ if (!rEntry.second->HasType(ScRangeData::Type::Database)
+ && !rEntry.second->HasType(ScRangeData::Type::Hidden))
+ {
+ aLine.aName = rEntry.second->GetName();
+ addEntry(aLine, false);
+ }
+ }
+ }
+ m_xTreeView->thaw();
+}
+
+std::vector<ScRangeNameLine> ScRangeManagerTable::GetSelectedEntries()
+{
+ std::vector<ScRangeNameLine> aSelectedEntries;
+ m_xTreeView->selected_foreach([this, &aSelectedEntries](weld::TreeIter& rEntry) {
+ ScRangeNameLine aLine;
+ GetLine(aLine, rEntry);
+ aSelectedEntries.push_back(aLine);
+ return false;
+ });
+ return aSelectedEntries;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/namedlg/namepast.cxx b/sc/source/ui/namedlg/namepast.cxx
new file mode 100644
index 0000000000..1d144f3fdf
--- /dev/null
+++ b/sc/source/ui/namedlg/namepast.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <namepast.hxx>
+#include <docsh.hxx>
+#include <rangenam.hxx>
+#include <viewdata.hxx>
+#include <scui_def.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <compiler.hxx>
+
+ScNamePasteDlg::ScNamePasteDlg(weld::Window* pParent, ScDocShell* pShell)
+ : GenericDialogController(pParent, "modules/scalc/ui/insertname.ui", "InsertNameDialog")
+ , m_xBtnPasteAll(m_xBuilder->weld_button("pasteall"))
+ , m_xBtnPaste(m_xBuilder->weld_button("paste"))
+ , m_xBtnClose(m_xBuilder->weld_button("close"))
+{
+ ScDocument& rDoc = pShell->GetDocument();
+ m_aSheetSep = OUString(rDoc.GetSheetSeparator());
+ std::map<OUString, ScRangeName*> aCopyMap;
+ rDoc.GetRangeNameMap(aCopyMap);
+ for (const auto & [ aTemp, pName ] : aCopyMap)
+ {
+ m_RangeMap.insert(std::make_pair(aTemp, *pName));
+ }
+
+ ScAddress aPos;
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ aPos = ScAddress(pViewData->GetCurX(), pViewData->GetCurY(), pViewData->GetTabNo());
+
+ std::unique_ptr<weld::TreeView> xTreeView(m_xBuilder->weld_tree_view("ctrl"));
+ xTreeView->set_size_request(xTreeView->get_approximate_digit_width() * 75,
+ xTreeView->get_height_rows(10));
+ m_xTable.reset(new ScRangeManagerTable(std::move(xTreeView), m_RangeMap, aPos));
+
+ m_xBtnPaste->connect_clicked(LINK(this, ScNamePasteDlg, ButtonHdl));
+ m_xBtnPasteAll->connect_clicked(LINK(this, ScNamePasteDlg, ButtonHdl));
+ m_xBtnClose->connect_clicked(LINK(this, ScNamePasteDlg, ButtonHdl));
+
+ if (!m_xTable->n_children())
+ {
+ m_xBtnPaste->set_sensitive(false);
+ m_xBtnPasteAll->set_sensitive(false);
+ }
+}
+
+ScNamePasteDlg::~ScNamePasteDlg() {}
+
+IMPL_LINK(ScNamePasteDlg, ButtonHdl, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xBtnPasteAll.get())
+ {
+ m_xDialog->response(BTN_PASTE_LIST);
+ }
+ else if (&rButton == m_xBtnPaste.get())
+ {
+ const OUString aGlobalScope(ScResId(STR_GLOBAL_SCOPE));
+ std::vector<ScRangeNameLine> aSelectedLines = m_xTable->GetSelectedEntries();
+ for (const auto& rLine : aSelectedLines)
+ {
+ if (rLine.aScope == aGlobalScope)
+ maSelectedNames.push_back(rLine.aName);
+ else
+ {
+ OUString aSheet(rLine.aScope);
+ ScCompiler::CheckTabQuotes(aSheet);
+ maSelectedNames.push_back(aSheet + m_aSheetSep + rLine.aName);
+ }
+ }
+ m_xDialog->response(BTN_PASTE_NAME);
+ }
+ else if (&rButton == m_xBtnClose.get())
+ {
+ m_xDialog->response(BTN_PASTE_CLOSE);
+ }
+}
+
+const std::vector<OUString>& ScNamePasteDlg::GetSelectedNames() const { return maSelectedNames; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/navipi/content.cxx b/sc/source/ui/navipi/content.cxx
new file mode 100644
index 0000000000..89d7764255
--- /dev/null
+++ b/sc/source/ui/navipi/content.cxx
@@ -0,0 +1,1548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+
+#include <content.hxx>
+#include <navipi.hxx>
+#include <global.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <tablink.hxx>
+#include <drwlayer.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <lnktrans.hxx>
+#include <strings.hrc>
+#include <scresid.hxx>
+#include <bitmaps.hlst>
+#include <arealink.hxx>
+#include <navicfg.hxx>
+#include <navsett.hxx>
+#include <postit.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <clipparam.hxx>
+#include <markdata.hxx>
+
+using namespace com::sun::star;
+
+// order of the categories in navigator -------------------------------------
+
+const ScContentId pTypeList[int(ScContentId::LAST) + 1] =
+{
+ ScContentId::ROOT, // ROOT (0) has to be at the front
+ ScContentId::TABLE,
+ ScContentId::RANGENAME,
+ ScContentId::DBAREA,
+ ScContentId::AREALINK,
+ ScContentId::GRAPHIC,
+ ScContentId::OLEOBJECT,
+ ScContentId::NOTE,
+ ScContentId::DRAWING
+};
+
+constexpr OUString aContentBmps[]=
+{
+ RID_BMP_CONTENT_TABLE,
+ RID_BMP_CONTENT_RANGENAME,
+ RID_BMP_CONTENT_DBAREA,
+ RID_BMP_CONTENT_GRAPHIC,
+ RID_BMP_CONTENT_OLEOBJECT,
+ RID_BMP_CONTENT_NOTE,
+ RID_BMP_CONTENT_AREALINK,
+ RID_BMP_CONTENT_DRAWING
+};
+
+ScDocShell* ScContentTree::GetManualOrCurrent()
+{
+ ScDocShell* pSh = nullptr;
+ if ( !aManualDoc.isEmpty() )
+ {
+ SfxObjectShell* pObjSh = SfxObjectShell::GetFirst( checkSfxObjectShell<ScDocShell> );
+ while ( pObjSh && !pSh )
+ {
+ if ( pObjSh->GetTitle() == aManualDoc )
+ pSh = dynamic_cast<ScDocShell*>( pObjSh );
+ pObjSh = SfxObjectShell::GetNext( *pObjSh, checkSfxObjectShell<ScDocShell> );
+ }
+ }
+ else
+ {
+ // only current when manual isn't set
+ // (so it's detected when the documents don't exists any longer)
+
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+ if ( pViewSh )
+ {
+ SfxObjectShell* pObjSh = pViewSh->GetViewFrame().GetObjectShell();
+ pSh = dynamic_cast<ScDocShell*>( pObjSh );
+ }
+ }
+
+ return pSh;
+}
+
+// ScContentTree
+
+ScContentTree::ScContentTree(std::unique_ptr<weld::TreeView> xTreeView, ScNavigatorDlg* pNavigatorDlg)
+ : m_xTreeView(std::move(xTreeView))
+ , m_xScratchIter(m_xTreeView->make_iterator())
+ , m_xTransferObj(new ScLinkTransferObj)
+ , pParentWindow(pNavigatorDlg)
+ , nRootType(ScContentId::ROOT)
+ , bIsInNavigatorDlg(false)
+ , m_bFreeze(false)
+ , m_nAsyncMouseReleaseId(nullptr)
+{
+ for (sal_uInt16 i = 0; i <= int(ScContentId::LAST); ++i)
+ pPosList[pTypeList[i]] = i; // inverse for searching
+
+ m_aRootNodes[ScContentId::ROOT] = nullptr;
+ for (sal_uInt16 i = 1; i < int(ScContentId::LAST); ++i)
+ InitRoot(static_cast<ScContentId>(i));
+
+ m_xTreeView->connect_row_activated(LINK(this, ScContentTree, ContentDoubleClickHdl));
+ m_xTreeView->connect_mouse_release(LINK(this, ScContentTree, MouseReleaseHdl));
+ m_xTreeView->connect_key_press(LINK(this, ScContentTree, KeyInputHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, ScContentTree, CommandHdl));
+ m_xTreeView->connect_query_tooltip(LINK(this, ScContentTree, QueryTooltipHdl));
+
+ rtl::Reference<TransferDataContainer> xHelper(m_xTransferObj);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+
+ m_xTreeView->connect_drag_begin(LINK(this, ScContentTree, DragBeginHdl));
+
+ m_xTreeView->set_selection_mode( SelectionMode::Single );
+
+ m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
+ m_xTreeView->get_text_height() * 13);
+}
+
+ScContentTree::~ScContentTree()
+{
+ if (m_nAsyncMouseReleaseId)
+ {
+ Application::RemoveUserEvent(m_nAsyncMouseReleaseId);
+ m_nAsyncMouseReleaseId = nullptr;
+ }
+}
+
+const TranslateId SCSTR_CONTENT_ARY[] =
+{
+ SCSTR_CONTENT_ROOT,
+ SCSTR_CONTENT_TABLE,
+ SCSTR_CONTENT_RANGENAME,
+ SCSTR_CONTENT_DBAREA,
+ SCSTR_CONTENT_GRAPHIC,
+ SCSTR_CONTENT_OLEOBJECT,
+ SCSTR_CONTENT_NOTE,
+ SCSTR_CONTENT_AREALINK,
+ SCSTR_CONTENT_DRAWING
+};
+
+void ScContentTree::InitRoot( ScContentId nType )
+{
+ if ( nType == ScContentId::ROOT )
+ return;
+
+ if ( nRootType != ScContentId::ROOT && nRootType != nType ) // hidden ?
+ {
+ m_aRootNodes[nType] = nullptr;
+ return;
+ }
+
+ auto const & aImage = aContentBmps[static_cast<int>(nType) - 1];
+ OUString aName(ScResId(SCSTR_CONTENT_ARY[static_cast<int>(nType)]));
+ // back to the correct position:
+ sal_uInt16 nPos = nRootType != ScContentId::ROOT ? 0 : pPosList[nType]-1;
+ m_aRootNodes[nType] = m_xTreeView->make_iterator();
+ m_xTreeView->insert(nullptr, nPos, &aName, nullptr, nullptr, nullptr, false, m_aRootNodes[nType].get());
+ m_xTreeView->set_image(*m_aRootNodes[nType], aImage);
+}
+
+void ScContentTree::ClearAll()
+{
+ //There are one method in Control::SetUpdateMode(), and one override method SvTreeListBox::SetUpdateMode(). Here although
+ //SvTreeListBox::SetUpdateMode() is called in refresh method, it only call SvTreeListBox::SetUpdateMode(), not Control::SetUpdateMode().
+ //In m_xTreeView->clear(), Broadcast( LISTACTION_CLEARED ) will be called and finally, it will be trapped into the event yield() loop. And
+ //the InitRoot() method won't be called. Then if a user click or press key to update the navigator tree, crash happens.
+ //So the solution is to disable the UpdateMode of Control, then call Clear(), then recover the update mode
+ bool bWasFrozen = m_bFreeze;
+ if (!bWasFrozen)
+ freeze();
+ m_xTreeView->clear();
+ if (!bWasFrozen)
+ thaw();
+ for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
+ InitRoot(static_cast<ScContentId>(i));
+}
+
+void ScContentTree::ClearType(ScContentId nType)
+{
+ if (nType == ScContentId::ROOT)
+ ClearAll();
+ else
+ {
+ weld::TreeIter* pParent = m_aRootNodes[nType].get();
+ if (!pParent || m_xTreeView->iter_has_child(*pParent)) // not if no children existing
+ {
+ if (pParent)
+ m_xTreeView->remove(*pParent); // with all children
+ InitRoot( nType ); // if needed insert anew
+ }
+ }
+}
+
+void ScContentTree::InsertContent( ScContentId nType, const OUString& rValue )
+{
+ weld::TreeIter* pParent = m_aRootNodes[nType].get();
+ if (pParent)
+ {
+ m_xTreeView->insert(pParent, -1, &rValue, nullptr, nullptr, nullptr, false, m_xScratchIter.get());
+ m_xTreeView->set_sensitive(*m_xScratchIter, true);
+ }
+ else
+ {
+ OSL_FAIL("InsertContent without parent");
+ }
+}
+
+void ScContentTree::GetEntryIndexes(ScContentId& rnRootIndex, sal_uLong& rnChildIndex, const weld::TreeIter* pEntry) const
+{
+ rnRootIndex = ScContentId::ROOT;
+ rnChildIndex = SC_CONTENT_NOCHILD;
+
+ if( !pEntry )
+ return;
+
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(pEntry));
+ if (!m_xTreeView->iter_parent(*xParent))
+ xParent.reset();
+ bool bFound = false;
+ for( int i = 1; !bFound && (i <= int(ScContentId::LAST)); ++i )
+ {
+ ScContentId nRoot = static_cast<ScContentId>(i);
+ if (!m_aRootNodes[nRoot])
+ continue;
+ if (m_xTreeView->iter_compare(*pEntry, *m_aRootNodes[nRoot]) == 0)
+ {
+ rnRootIndex = nRoot;
+ rnChildIndex = ~0UL;
+ bFound = true;
+ }
+ else if (xParent && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[nRoot]) == 0)
+ {
+ rnRootIndex = nRoot;
+
+ // search the entry in all child entries of the parent
+ sal_uLong nEntry = 0;
+ std::unique_ptr<weld::TreeIter> xIterEntry(m_xTreeView->make_iterator(xParent.get()));
+ bool bIterEntry = m_xTreeView->iter_children(*xIterEntry);
+ while (!bFound && bIterEntry)
+ {
+ if (m_xTreeView->iter_compare(*pEntry, *xIterEntry) == 0)
+ {
+ rnChildIndex = nEntry;
+ bFound = true; // exit the while loop
+ }
+ bIterEntry = m_xTreeView->iter_next_sibling(*xIterEntry);
+ ++nEntry;
+ }
+
+ bFound = true; // exit the for loop
+ }
+ }
+}
+
+sal_uLong ScContentTree::GetChildIndex(const weld::TreeIter* pEntry) const
+{
+ ScContentId nRoot;
+ sal_uLong nChild;
+ GetEntryIndexes(nRoot, nChild, pEntry);
+ return nChild;
+}
+
+static OUString lcl_GetDBAreaRange( const ScDocument* pDoc, const OUString& rDBName )
+{
+ OUString aRet;
+ if (pDoc)
+ {
+ ScDBCollection* pDbNames = pDoc->GetDBCollection();
+ const ScDBData* pData = pDbNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
+ if (pData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+ aRet = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS_3D);
+ }
+ }
+ return aRet;
+}
+
+IMPL_LINK_NOARG(ScContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
+{
+ ScContentId nType;
+ sal_uLong nChild;
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xEntry.get()))
+ xEntry.reset();
+ GetEntryIndexes(nType, nChild, xEntry.get());
+
+ if (xEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD))
+ {
+ OUString aText(m_xTreeView->get_text(*xEntry));
+
+ if ( !aManualDoc.isEmpty() )
+ pParentWindow->SetCurrentDoc( aManualDoc );
+
+ switch( nType )
+ {
+ case ScContentId::TABLE:
+ {
+ // tdf#133159 store current config before changing sheet
+ // plausible that this should be done for all cases, but this
+ // is the known case that needs it
+ StoreNavigatorSettings();
+ pParentWindow->SetCurrentTableStr( aText );
+ }
+ break;
+
+ case ScContentId::RANGENAME:
+ pParentWindow->SetCurrentCellStr( aText );
+ break;
+
+ case ScContentId::DBAREA:
+ {
+ // If the same names of area and DB exists, then
+ // SID_CURRENTCELL takes the area name.
+ // Therefore for DB areas access them directly via address.
+
+ OUString aRangeStr = lcl_GetDBAreaRange( GetSourceDocument(), aText );
+ if (!aRangeStr.isEmpty())
+ pParentWindow->SetCurrentCellStr( aRangeStr );
+ }
+ break;
+
+ case ScContentId::OLEOBJECT:
+ case ScContentId::GRAPHIC:
+ case ScContentId::DRAWING:
+ pParentWindow->SetCurrentObject( aText );
+ break;
+
+ case ScContentId::NOTE:
+ {
+ ScAddress aPos = GetNotePos( nChild );
+ pParentWindow->SetCurrentTable( aPos.Tab() );
+ pParentWindow->SetCurrentCell( aPos.Col(), aPos.Row() );
+ }
+ break;
+
+ case ScContentId::AREALINK:
+ {
+ const ScAreaLink* pLink = GetLink(nChild);
+ ScDocument* pSrcDoc = GetSourceDocument();
+ if (pLink && pSrcDoc)
+ {
+ const ScRange& aRange = pLink->GetDestArea();
+ OUString aRangeStr(aRange.Format(*pSrcDoc, ScRefFlags::RANGE_ABS_3D, pSrcDoc->GetAddressConvention()));
+ pParentWindow->SetCurrentCellStr( aRangeStr );
+ }
+ }
+ break;
+ default: break;
+ }
+
+ ScNavigatorDlg::ReleaseFocus(); // set focus into document
+ }
+
+ return false;
+}
+
+void ScContentTree::LaunchAsyncStoreNavigatorSettings()
+{
+ if (!m_nAsyncMouseReleaseId)
+ m_nAsyncMouseReleaseId = Application::PostUserEvent(LINK(this, ScContentTree, AsyncStoreNavigatorSettings));
+}
+
+IMPL_LINK_NOARG(ScContentTree, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ LaunchAsyncStoreNavigatorSettings();
+ return false;
+}
+
+IMPL_LINK_NOARG(ScContentTree, AsyncStoreNavigatorSettings, void*, void)
+{
+ m_nAsyncMouseReleaseId = nullptr;
+ StoreNavigatorSettings();
+}
+
+IMPL_LINK(ScContentTree, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bUsed = false;
+
+ const vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ if (aCode.GetCode() == KEY_RETURN)
+ {
+ switch (aCode.GetModifier())
+ {
+ case KEY_MOD1:
+ ToggleRoot(); // toggle root mode (as in Writer)
+ bUsed = true;
+ break;
+ case 0:
+ {
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xEntry.get()))
+ xEntry.reset();
+ if (xEntry)
+ {
+ ScContentId nType;
+ sal_uLong nChild;
+ GetEntryIndexes(nType, nChild, xEntry.get());
+
+ if (nType != ScContentId::ROOT && nChild == SC_CONTENT_NOCHILD)
+ {
+ if (m_xTreeView->get_row_expanded(*xEntry))
+ m_xTreeView->collapse_row(*xEntry);
+ else
+ m_xTreeView->expand_row(*xEntry);
+ }
+ else
+ ContentDoubleClickHdl(*m_xTreeView); // select content as if double clicked
+ }
+
+ bUsed = true;
+ }
+ break;
+ }
+ }
+ //Make KEY_SPACE has same function as DoubleClick, and realize
+ //multi-selection.
+ if ( bIsInNavigatorDlg )
+ {
+ if(aCode.GetCode() == KEY_SPACE )
+ {
+ bUsed = true;
+ ScContentId nType;
+ sal_uLong nChild;
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xEntry.get()))
+ xEntry.reset();
+ GetEntryIndexes(nType, nChild, xEntry.get());
+
+ if (xEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD))
+ {
+ OUString aText(m_xTreeView->get_text(*xEntry));
+ if (!aManualDoc.isEmpty())
+ pParentWindow->SetCurrentDoc( aManualDoc );
+ switch (nType)
+ {
+ case ScContentId::OLEOBJECT:
+ case ScContentId::GRAPHIC:
+ case ScContentId::DRAWING:
+ {
+ ScDrawView* pScDrawView = nullptr;
+ ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
+ if (pScTabViewShell)
+ pScDrawView = pScTabViewShell->GetViewData().GetScDrawView();
+ if (pScDrawView)
+ {
+ pScDrawView->SelectCurrentViewObject(aText);
+ bool bHasMakredObject = false;
+ weld::TreeIter* pParent = m_aRootNodes[nType].get();
+ std::unique_ptr<weld::TreeIter> xBeginEntry(m_xTreeView->make_iterator(pParent));
+ bool bBeginEntry = false;
+ if (pParent)
+ bBeginEntry = m_xTreeView->iter_children(*xBeginEntry);
+ while (bBeginEntry)
+ {
+ OUString aTempText(m_xTreeView->get_text(*xBeginEntry));
+ if( pScDrawView->GetObjectIsMarked( pScDrawView->GetObjectByName( aTempText ) ) )
+ {
+ bHasMakredObject = true;
+ break;
+ }
+ bBeginEntry = m_xTreeView->iter_next(*xBeginEntry);
+ }
+ if (!bHasMakredObject && pScTabViewShell)
+ pScTabViewShell->SetDrawShell(false);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (!bUsed)
+ {
+ if (aCode.GetCode() == KEY_F5)
+ StoreNavigatorSettings();
+ else
+ LaunchAsyncStoreNavigatorSettings();
+ }
+
+ return bUsed;
+}
+
+IMPL_LINK(ScContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ bool bDone = false;
+
+ switch ( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ // drag-and-drop mode
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/scalc/ui/dropmenu.ui"));
+ std::unique_ptr<weld::Menu> xPop(xBuilder->weld_menu("contextmenu"));
+ std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu("dragmodesubmenu"));
+
+ switch (pParentWindow->GetDropMode())
+ {
+ case 0:
+ xDropMenu->set_active("hyperlink", true);
+ break;
+ case 1:
+ xDropMenu->set_active("link", true);
+ break;
+ case 2:
+ xDropMenu->set_active("copy", true);
+ break;
+ }
+
+ // displayed document
+ std::unique_ptr<weld::Menu> xDocMenu(xBuilder->weld_menu("displaymenu"));
+ sal_uInt16 i=0;
+ OUString sActive;
+ OUString sId;
+ // loaded documents
+ ScDocShell* pCurrentSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ while ( pSh )
+ {
+ if ( dynamic_cast<const ScDocShell*>( pSh) != nullptr )
+ {
+ OUString aName = pSh->GetTitle();
+ OUString aEntry = aName;
+ if ( pSh == pCurrentSh )
+ aEntry += pParentWindow->aStrActive;
+ else
+ aEntry += pParentWindow->aStrNotActive;
+ ++i;
+ sId = "document" + OUString::number(i);
+ xDocMenu->append_radio(sId, aEntry);
+ if (aName == aManualDoc)
+ sActive = sId;
+ }
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+ // "active window"
+ ++i;
+ sId = "document" + OUString::number(i);
+ xDocMenu->append_radio(sId, pParentWindow->aStrActiveWin);
+ if (aManualDoc.isEmpty())
+ sActive = sId;
+ xDocMenu->set_active(sActive, true);
+
+ OUString sIdent = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1)));
+ if (sIdent == "hyperlink")
+ pParentWindow->SetDropMode(0);
+ else if (sIdent == "link")
+ pParentWindow->SetDropMode(1);
+ else if (sIdent == "copy")
+ pParentWindow->SetDropMode(2);
+ else if (sIdent.startsWith("document"))
+ {
+ OUString aName = xDocMenu->get_label(sIdent);
+ SelectDoc(aName);
+ }
+ }
+ break;
+ default: break;
+ }
+
+ return bDone;
+}
+
+IMPL_LINK(ScContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
+{
+ OUString aHelpText;
+
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
+ if (!m_xTreeView->iter_parent(*xParent))
+ xParent.reset();
+
+ if (!xParent) // Top-Level ?
+ {
+ aHelpText = OUString::number(m_xTreeView->iter_n_children(rEntry)) +
+ " " + m_xTreeView->get_text(rEntry);
+ }
+ else if (m_aRootNodes[ScContentId::NOTE] && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[ScContentId::NOTE]) == 0)
+ {
+ aHelpText = m_xTreeView->get_text(rEntry); // notes as help text
+ }
+ else if (m_aRootNodes[ScContentId::AREALINK] && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[ScContentId::AREALINK]) == 0)
+ {
+ auto nIndex = GetChildIndex(&rEntry);
+ if (nIndex != SC_CONTENT_NOCHILD)
+ {
+ const ScAreaLink* pLink = GetLink(nIndex);
+ if (pLink)
+ {
+ aHelpText = pLink->GetFile(); // source file as help text
+ }
+ }
+ }
+
+ return aHelpText;
+}
+
+ScDocument* ScContentTree::GetSourceDocument()
+{
+ ScDocShell* pSh = GetManualOrCurrent();
+ if (pSh)
+ return &pSh->GetDocument();
+ return nullptr;
+}
+
+void ScContentTree::Refresh( ScContentId nType )
+{
+ // if nothing has changed the cancel right away (against flicker)
+ if ( nType == ScContentId::NOTE )
+ if (!NoteStringsChanged())
+ return;
+ if ( nType == ScContentId::GRAPHIC )
+ if (!DrawNamesChanged(ScContentId::GRAPHIC))
+ return;
+ if ( nType == ScContentId::OLEOBJECT )
+ if (!DrawNamesChanged(ScContentId::OLEOBJECT))
+ return;
+ if ( nType == ScContentId::DRAWING )
+ if (!DrawNamesChanged(ScContentId::DRAWING))
+ return;
+
+ freeze();
+
+ ClearType( nType );
+
+ if ( nType == ScContentId::ROOT || nType == ScContentId::TABLE )
+ GetTableNames();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::RANGENAME )
+ GetAreaNames();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::DBAREA )
+ GetDbNames();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::GRAPHIC )
+ GetGraphicNames();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::OLEOBJECT )
+ GetOleNames();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::DRAWING )
+ GetDrawingNames();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::NOTE )
+ GetNoteStrings();
+ if ( nType == ScContentId::ROOT || nType == ScContentId::AREALINK )
+ GetLinkNames();
+
+ thaw();
+
+ ApplyNavigatorSettings();
+}
+
+void ScContentTree::GetTableNames()
+{
+ if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::TABLE ) // hidden ?
+ return;
+
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return;
+
+ OUString aName;
+ SCTAB nCount = pDoc->GetTableCount();
+ for ( SCTAB i=0; i<nCount; i++ )
+ {
+ pDoc->GetName( i, aName );
+ InsertContent( ScContentId::TABLE, aName );
+ }
+}
+
+namespace {
+
+OUString createLocalRangeName(std::u16string_view rName, std::u16string_view rTableName)
+{
+ return OUString::Concat(rName) + " (" + rTableName + ")";
+}
+}
+
+void ScContentTree::GetAreaNames()
+{
+ if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::RANGENAME ) // hidden ?
+ return;
+
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return;
+
+ ScRange aDummy;
+ std::set<OUString> aSet;
+ ScRangeName* pRangeNames = pDoc->GetRangeName();
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (rEntry.second->IsValidReference(aDummy))
+ aSet.insert(rEntry.second->GetName());
+ }
+ for (SCTAB i = 0; i < pDoc->GetTableCount(); ++i)
+ {
+ ScRangeName* pLocalRangeName = pDoc->GetRangeName(i);
+ if (pLocalRangeName && !pLocalRangeName->empty())
+ {
+ OUString aTableName;
+ pDoc->GetName(i, aTableName);
+ for (const auto& rEntry : *pLocalRangeName)
+ {
+ if (rEntry.second->IsValidReference(aDummy))
+ aSet.insert(createLocalRangeName(rEntry.second->GetName(), aTableName));
+ }
+ }
+ }
+
+ for (const auto& rItem : aSet)
+ {
+ InsertContent(ScContentId::RANGENAME, rItem);
+ }
+}
+
+void ScContentTree::GetDbNames()
+{
+ if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::DBAREA ) // hidden ?
+ return;
+
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return;
+
+ ScDBCollection* pDbNames = pDoc->GetDBCollection();
+ const ScDBCollection::NamedDBs& rDBs = pDbNames->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ const OUString& aStrName = rxDB->GetName();
+ InsertContent(ScContentId::DBAREA, aStrName);
+ }
+}
+
+bool ScContentTree::IsPartOfType( ScContentId nContentType, SdrObjKind nObjIdentifier )
+{
+ bool bRet = false;
+ switch ( nContentType )
+ {
+ case ScContentId::GRAPHIC:
+ bRet = ( nObjIdentifier == SdrObjKind::Graphic );
+ break;
+ case ScContentId::OLEOBJECT:
+ bRet = ( nObjIdentifier == SdrObjKind::OLE2 );
+ break;
+ case ScContentId::DRAWING:
+ bRet = ( nObjIdentifier != SdrObjKind::Graphic && nObjIdentifier != SdrObjKind::OLE2 ); // everything else
+ break;
+ default:
+ OSL_FAIL("unknown content type");
+ }
+ return bRet;
+}
+
+constexpr int MAX_TREE_NODES = 1000;
+
+void ScContentTree::GetDrawNames( ScContentId nType )
+{
+ if (!bIsInNavigatorDlg)
+ return;
+
+ if ( nRootType != ScContentId::ROOT && nRootType != nType ) // hidden ?
+ return;
+
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return;
+
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ if (!pDrawLayer)
+ return;
+
+ ScDocShell* pShell = pDoc->GetDocumentShell();
+ if (!pShell)
+ return;
+
+ // iterate in flat mode for groups
+ SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
+
+ std::vector<OUString> aNames;
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ continue;
+ SdrObjListIter aIter(pPage, eIter);
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (IsPartOfType(nType, pObject->GetObjIdentifier()))
+ {
+ OUString aName = ScDrawLayer::GetVisibleName(pObject);
+ if (!aName.isEmpty())
+ aNames.push_back(aName);
+ if (aNames.size() > MAX_TREE_NODES)
+ {
+ SAL_WARN("sc", "too many tree nodes, ignoring the rest");
+ break;
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+
+ weld::TreeIter* pParent = m_aRootNodes[nType].get();
+ assert(pParent && "InsertContent without parent");
+ // insert all of these in one go under pParent
+ m_xTreeView->bulk_insert_for_each(aNames.size(), [this, &aNames](weld::TreeIter& rIter, int nIndex) {
+ m_xTreeView->set_text(rIter, aNames[nIndex], 0);
+ m_xTreeView->set_sensitive(rIter, true);
+ }, pParent);
+}
+
+void ScContentTree::GetGraphicNames()
+{
+ GetDrawNames( ScContentId::GRAPHIC );
+}
+
+void ScContentTree::GetOleNames()
+{
+ GetDrawNames( ScContentId::OLEOBJECT );
+}
+
+void ScContentTree::GetDrawingNames()
+{
+ GetDrawNames( ScContentId::DRAWING );
+}
+
+void ScContentTree::GetLinkNames()
+{
+ if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::AREALINK ) // hidden ?
+ return;
+
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return;
+
+ sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ OSL_ENSURE(pLinkManager, "no LinkManager on document?");
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto pScAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
+ InsertContent( ScContentId::AREALINK, pScAreaLink->GetSource() );
+
+ // insert in list the names of source areas
+ }
+}
+
+const ScAreaLink* ScContentTree::GetLink( sal_uLong nIndex )
+{
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return nullptr;
+
+ sal_uLong nFound = 0;
+ sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ OSL_ENSURE(pLinkManager, "no LinkManager on document?");
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto pAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
+ {
+ if (nFound == nIndex)
+ return pAreaLink;
+ ++nFound;
+ }
+ }
+
+ OSL_FAIL("link not found");
+ return nullptr;
+}
+
+static OUString lcl_NoteString( const ScPostIt& rNote )
+{
+ return rNote.GetText().replace('\n', ' ');
+}
+
+void ScContentTree::GetNoteStrings()
+{
+ if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::NOTE ) // hidden ?
+ return;
+
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return;
+
+ // loop over cell notes
+ std::vector<sc::NoteEntry> aEntries;
+ pDoc->GetAllNoteEntries(aEntries);
+ weld::TreeIter* pParent = m_aRootNodes[ScContentId::NOTE].get();
+ for (const auto& rEntry : aEntries)
+ {
+ OUString aValue = lcl_NoteString(*rEntry.mpNote);
+ m_xTreeView->insert(pParent, -1, &aValue, nullptr, nullptr, nullptr, false, m_xScratchIter.get());
+ m_xTreeView->set_sensitive(*m_xScratchIter, true);
+ }
+}
+
+ScAddress ScContentTree::GetNotePos( sal_uLong nIndex )
+{
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return ScAddress();
+
+ return pDoc->GetNotePosition(nIndex);
+}
+
+bool ScContentTree::NoteStringsChanged()
+{
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return false;
+
+ weld::TreeIter* pParent = m_aRootNodes[ScContentId::NOTE].get();
+ if (!pParent)
+ return false;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
+ bool bEntry = m_xTreeView->iter_children(*xEntry);
+
+ std::vector<sc::NoteEntry> aEntries;
+ pDoc->GetAllNoteEntries(aEntries);
+ for (const auto& rEntry : aEntries)
+ {
+ const ScPostIt* pNote = rEntry.mpNote;
+ if (!bEntry)
+ return true;
+
+ if (lcl_NoteString(*pNote) != m_xTreeView->get_text(*xEntry))
+ return true;
+
+ bEntry = m_xTreeView->iter_next_sibling(*xEntry);
+ }
+
+ return bEntry;
+}
+
+bool ScContentTree::DrawNamesChanged( ScContentId nType )
+{
+ ScDocument* pDoc = GetSourceDocument();
+ if (!pDoc)
+ return false;
+
+ weld::TreeIter* pParent = m_aRootNodes[nType].get();
+ if (!pParent)
+ return false;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
+ bool bEntry = m_xTreeView->iter_children(*xEntry);
+
+ // iterate in flat mode for groups
+ SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
+
+ bool bEqual = true;
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ ScDocShell* pShell = pDoc->GetDocumentShell();
+ if (pDrawLayer && pShell)
+ {
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount && bEqual; nTab++)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, eIter );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && bEqual)
+ {
+ if ( IsPartOfType( nType, pObject->GetObjIdentifier() ) )
+ {
+ if ( !bEntry )
+ bEqual = false;
+ else
+ {
+ if (ScDrawLayer::GetVisibleName(pObject) != m_xTreeView->get_text(*xEntry))
+ bEqual = false;
+
+ bEntry = m_xTreeView->iter_next_sibling(*xEntry);
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+
+ if ( bEntry )
+ bEqual = false; // anything else
+
+ return !bEqual;
+}
+
+static bool lcl_GetRange( const ScDocument& rDoc, ScContentId nType, const OUString& rName, ScRange& rRange )
+{
+ bool bFound = false;
+
+ if ( nType == ScContentId::RANGENAME )
+ {
+ ScRangeName* pList = rDoc.GetRangeName();
+ if (pList)
+ {
+ const ScRangeData* p = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
+ if (p && p->IsValidReference(rRange))
+ bFound = true;
+ }
+ }
+ else if ( nType == ScContentId::DBAREA )
+ {
+ ScDBCollection* pList = rDoc.GetDBCollection();
+ if (pList)
+ {
+ const ScDBData* p = pList->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rName));
+ if (p)
+ {
+ SCTAB nTab;
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ p->GetArea(nTab, nCol1, nRow1, nCol2, nRow2);
+ rRange = ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
+ bFound = true;
+ }
+ }
+ }
+
+ return bFound;
+}
+
+static bool lcl_DoDragObject( ScDocShell* pSrcShell, std::u16string_view rName, ScContentId nType, weld::TreeView& rTreeView )
+{
+ bool bDisallow = true;
+
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+ ScDrawLayer* pModel = rSrcDoc.GetDrawLayer();
+ if (pModel)
+ {
+ bool bOle = ( nType == ScContentId::OLEOBJECT );
+ bool bGraf = ( nType == ScContentId::GRAPHIC );
+ SdrObjKind nDrawId = bOle ? SdrObjKind::OLE2 : ( bGraf ? SdrObjKind::Graphic : SdrObjKind::Group );
+ SCTAB nTab = 0;
+ SdrObject* pObject = pModel->GetNamedObject( rName, nDrawId, nTab );
+ if (pObject)
+ {
+ SdrView aEditView(*pModel);
+ aEditView.ShowSdrPage(aEditView.GetModel().GetPage(nTab));
+ SdrPageView* pPV = aEditView.GetSdrPageView();
+ aEditView.MarkObj(pObject, pPV);
+
+ // tdf125520 this is a D&D-start potentially with an OLE object. If
+ // so, we need to do similar as e.g. in ScDrawView::BeginDrag so that
+ // the temporary SdrModel for transfer does have a GetPersist() so
+ // that the EmbeddedObjectContainer gets copied. We need no CheckOle
+ // here, test is simpler.
+ ScDocShellRef aDragShellRef;
+ if(SdrObjKind::OLE2 == pObject->GetObjIdentifier())
+ {
+ aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aDragShellRef->DoInitNew();
+ }
+
+ ScDrawLayer::SetGlobalDrawPersist(aDragShellRef.get());
+ std::unique_ptr<SdrModel> pDragModel(aEditView.CreateMarkedObjModel());
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ TransferableObjectDescriptor aObjDesc;
+ pSrcShell->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pSrcShell->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pDragModel), pSrcShell, std::move(aObjDesc) );
+
+ pTransferObj->SetDragSourceObj( *pObject, nTab );
+ pTransferObj->SetDragSourceFlags(ScDragSrc::Navigator);
+
+ SC_MOD()->SetDragObject( nullptr, pTransferObj.get() );
+
+ rtl::Reference<TransferDataContainer> xHelper(pTransferObj);
+ rTreeView.enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+
+ bDisallow = false;
+ }
+ }
+
+ return bDisallow;
+}
+
+static bool lcl_DoDragCells( ScDocShell* pSrcShell, const ScRange& rRange, ScDragSrc nFlags, weld::TreeView& rTreeView )
+{
+ bool bDisallow = true;
+
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+ ScMarkData aMark(rSrcDoc.GetSheetLimits());
+ aMark.SelectTable( rRange.aStart.Tab(), true );
+ aMark.SetMarkArea( rRange );
+
+ if ( !rSrcDoc.HasSelectedBlockMatrixFragment( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(),
+ aMark ) )
+ {
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ ScClipParam aClipParam(rRange, false);
+ rSrcDoc.CopyToClip(aClipParam, pClipDoc.get(), &aMark, false, false);
+ // pClipDoc->ExtendMerge( rRange, sal_True );
+
+ TransferableObjectDescriptor aObjDesc;
+ pSrcShell->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pSrcShell->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+
+ pTransferObj->SetDragSource( pSrcShell, aMark );
+ pTransferObj->SetDragSourceFlags( nFlags );
+
+ SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D
+
+ rtl::Reference<TransferDataContainer> xHelper(pTransferObj);
+ rTreeView.enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+
+ bDisallow = false;
+ }
+
+ return bDisallow;
+}
+
+IMPL_LINK(ScContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = true;
+
+ StoreNavigatorSettings();
+
+ bool bDisallow = true;
+
+ ScModule* pScMod = SC_MOD();
+
+ ScContentId nType;
+ sal_uLong nChild;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xEntry.get()))
+ xEntry.reset();
+
+ GetEntryIndexes(nType, nChild, xEntry.get());
+
+ if( xEntry &&
+ (nChild != SC_CONTENT_NOCHILD) &&
+ (nType != ScContentId::ROOT) &&
+ (nType != ScContentId::NOTE) &&
+ (nType != ScContentId::AREALINK) )
+ {
+ OUString aText(m_xTreeView->get_text(*xEntry));
+
+ ScDocument* pLocalDoc = nullptr; // for URL drop
+ OUString aDocName;
+ ScDocShell* pDocSh = GetManualOrCurrent();
+ if (pDocSh)
+ {
+ if (pDocSh->HasName())
+ aDocName = pDocSh->GetMedium()->GetName();
+ else
+ pLocalDoc = &pDocSh->GetDocument(); // drop only in this document
+ }
+
+ bool bDoLinkTrans = false; // use ScLinkTransferObj
+ OUString aLinkURL; // for ScLinkTransferObj
+ OUString aLinkText;
+
+ sal_uInt16 nDropMode = pParentWindow->GetDropMode();
+ switch ( nDropMode )
+ {
+ case SC_DROPMODE_URL:
+ {
+ OUString aUrl = aDocName + "#" + aText;
+
+ pScMod->SetDragJump( pLocalDoc, aUrl, aText );
+
+ if (!aDocName.isEmpty())
+ {
+ // provide URL to outside only if the document has a name
+ // (without name, only internal D&D via SetDragJump)
+
+ aLinkURL = aUrl;
+ aLinkText = aText;
+ }
+ bDoLinkTrans = true;
+ }
+ break;
+ case SC_DROPMODE_LINK:
+ {
+ if ( !aDocName.isEmpty() ) // link only to named documents
+ {
+ // for internal D&D, set flag to insert a link
+
+ switch ( nType )
+ {
+ case ScContentId::TABLE:
+ pScMod->SetDragLink( aDocName, aText, OUString() );
+ bDoLinkTrans = true;
+ break;
+ case ScContentId::RANGENAME:
+ case ScContentId::DBAREA:
+ pScMod->SetDragLink( aDocName, OUString(), aText );
+ bDoLinkTrans = true;
+ break;
+
+ // other types cannot be linked
+ default: break;
+ }
+ }
+ }
+ break;
+ case SC_DROPMODE_COPY:
+ {
+ ScDocShell* pSrcShell = GetManualOrCurrent();
+ if ( pSrcShell )
+ {
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+ if ( nType == ScContentId::RANGENAME || nType == ScContentId::DBAREA )
+ {
+ ScRange aRange;
+ if ( lcl_GetRange( rSrcDoc, nType, aText, aRange ) )
+ {
+ bDisallow = lcl_DoDragCells( pSrcShell, aRange, ScDragSrc::Navigator, *m_xTreeView );
+ }
+ }
+ else if ( nType == ScContentId::TABLE )
+ {
+ SCTAB nTab;
+ if ( rSrcDoc.GetTable( aText, nTab ) )
+ {
+ ScRange aRange(0, 0, nTab, rSrcDoc.MaxCol(), rSrcDoc.MaxRow(), nTab);
+ bDisallow = lcl_DoDragCells( pSrcShell, aRange, (ScDragSrc::Navigator | ScDragSrc::Table), *m_xTreeView );
+ }
+ }
+ else if ( nType == ScContentId::GRAPHIC || nType == ScContentId::OLEOBJECT ||
+ nType == ScContentId::DRAWING )
+ {
+ bDisallow = lcl_DoDragObject( pSrcShell, aText, nType, *m_xTreeView );
+
+ // during ExecuteDrag the navigator can be deleted
+ // -> don't access member anymore !!!
+ }
+ }
+ }
+ break;
+ }
+
+ if (bDoLinkTrans)
+ {
+ if (!aLinkURL.isEmpty())
+ m_xTransferObj->SetLinkURL(aLinkURL, aLinkText);
+
+ rtl::Reference<TransferDataContainer> xHelper(m_xTransferObj);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+
+ bDisallow = false;
+ }
+ }
+
+ return bDisallow;
+}
+
+void ScContentTree::SetRootType( ScContentId nNew )
+{
+ if ( nNew != nRootType )
+ {
+ nRootType = nNew;
+ Refresh();
+
+ ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg();
+ rCfg.SetRootType( nRootType );
+ }
+}
+
+void ScContentTree::ToggleRoot() // after selection
+{
+ ScContentId nNew = ScContentId::ROOT;
+ if ( nRootType == ScContentId::ROOT )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ if (m_xTreeView->get_cursor(xEntry.get()))
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xEntry.get()));
+ if (!m_xTreeView->iter_parent(*xParent))
+ xParent.reset();
+
+ for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
+ {
+ if (!m_aRootNodes[static_cast<ScContentId>(i)])
+ continue;
+ if ((m_xTreeView->iter_compare(*xEntry, *m_aRootNodes[static_cast<ScContentId>(i)]) == 0) ||
+ (xParent && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[static_cast<ScContentId>(i)]) == 0))
+ {
+ nNew = static_cast<ScContentId>(i);
+ }
+ }
+ }
+ }
+
+ SetRootType( nNew );
+}
+
+void ScContentTree::ResetManualDoc()
+{
+ aManualDoc.clear();
+
+ ActiveDocChanged();
+}
+
+bool ScContentTree::ActiveDocChanged()
+{
+ bool bRefreshed = false;
+
+ if (aManualDoc.isEmpty())
+ {
+ Refresh(); // content only if automatic
+ bRefreshed = true;
+ }
+
+ // if flag active Listbox must be updated
+
+ OUString aCurrent;
+
+ ScDocShell* pSh = GetManualOrCurrent();
+ if (pSh)
+ aCurrent = pSh->GetTitle();
+ else
+ {
+ // document is no longer available
+
+ aManualDoc.clear(); // again automatically
+ Refresh();
+ bRefreshed = true;
+ pSh = GetManualOrCurrent(); // should be active now
+ if (pSh)
+ aCurrent = pSh->GetTitle();
+ }
+
+ pParentWindow->GetDocNames( &aCurrent ); // select
+
+ return bRefreshed;
+}
+
+void ScContentTree::SetManualDoc(const OUString& rName)
+{
+ aManualDoc = rName;
+ Refresh();
+ pParentWindow->GetDocNames( &aManualDoc ); // select
+}
+
+void ScContentTree::SelectDoc(const OUString& rName) // rName like shown in Menu/Listbox
+{
+ if ( rName == pParentWindow->aStrActiveWin )
+ {
+ ResetManualDoc();
+ return;
+ }
+
+ // omit "active" or "inactive"
+
+ OUString aRealName = rName;
+ sal_Int32 nLen = rName.getLength();
+ sal_Int32 nActiveStart = nLen - pParentWindow->aStrActive.getLength();
+ if ( rName.subView( nActiveStart ) == pParentWindow->aStrActive )
+ aRealName = rName.copy( 0, nActiveStart );
+ sal_Int32 nNotActiveStart = nLen - pParentWindow->aStrNotActive.getLength();
+ if ( rName.subView( nNotActiveStart ) == pParentWindow->aStrNotActive )
+ aRealName = rName.copy( 0, nNotActiveStart );
+
+ bool bLoaded = false;
+
+ // Is it a normally loaded document?
+
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ while ( pSh && !bLoaded )
+ {
+ if ( dynamic_cast<const ScDocShell*>( pSh) != nullptr )
+ if ( pSh->GetTitle() == aRealName )
+ bLoaded = true;
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+
+ if (bLoaded)
+ {
+ SetManualDoc(aRealName);
+ }
+ else
+ {
+ OSL_FAIL("SelectDoc: not found");
+ }
+}
+
+void ScContentTree::SelectEntryByName(const ScContentId nRoot, std::u16string_view rName)
+{
+ weld::TreeIter* pParent = m_aRootNodes[nRoot].get();
+
+ if (pParent || !m_xTreeView->iter_has_child(*pParent))
+ return;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
+ bool bEntry = m_xTreeView->iter_children(*xEntry);
+
+ while (bEntry)
+ {
+ if (m_xTreeView->get_text(*xEntry) == rName)
+ {
+ m_xTreeView->select(*xEntry);
+ m_xTreeView->set_cursor(*xEntry);
+
+ // Scroll to the selected item
+ m_xTreeView->scroll_to_row(*xEntry);
+
+ StoreNavigatorSettings();
+
+ return;
+ }
+ bEntry = m_xTreeView->iter_next(*xEntry);
+ }
+}
+
+void ScContentTree::ApplyNavigatorSettings()
+{
+ const ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
+ if( !pSettings )
+ return;
+
+ ScContentId nRootSel = pSettings->GetRootSelected();
+ auto nChildSel = pSettings->GetChildSelected();
+
+ // tdf#133079 ensure Sheet root is selected if nothing
+ // else would be
+ if (nRootSel == ScContentId::ROOT)
+ {
+ nRootSel = ScContentId::TABLE;
+ nChildSel = SC_CONTENT_NOCHILD;
+ }
+
+ for( int i = 1; i <= int(ScContentId::LAST); ++i )
+ {
+ ScContentId nEntry = static_cast<ScContentId>(i);
+ if( m_aRootNodes[ nEntry ] )
+ {
+ // gray or ungray
+ if (!m_xTreeView->iter_has_child(*m_aRootNodes[nEntry]))
+ m_xTreeView->set_sensitive(*m_aRootNodes[nEntry], false);
+ else
+ m_xTreeView->set_sensitive(*m_aRootNodes[nEntry], true);
+
+ // expand
+ bool bExp = pSettings->IsExpanded( nEntry );
+ if (bExp != m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]))
+ {
+ if( bExp )
+ m_xTreeView->expand_row(*m_aRootNodes[nEntry]);
+ else
+ m_xTreeView->collapse_row(*m_aRootNodes[nEntry]);
+ }
+
+ // select
+ if( nRootSel == nEntry )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry;
+ if (bExp && (nChildSel != SC_CONTENT_NOCHILD))
+ {
+ xEntry = m_xTreeView->make_iterator(m_aRootNodes[nEntry].get());
+ if (!m_xTreeView->iter_children(*xEntry) || !m_xTreeView->iter_nth_sibling(*xEntry, nChildSel))
+ xEntry.reset();
+ }
+ m_xTreeView->select(xEntry ? *xEntry : *m_aRootNodes[nEntry]);
+ m_xTreeView->set_cursor(xEntry ? *xEntry : *m_aRootNodes[nEntry]);
+ }
+ }
+ }
+}
+
+void ScContentTree::StoreNavigatorSettings()
+{
+ if (m_nAsyncMouseReleaseId)
+ {
+ Application::RemoveUserEvent(m_nAsyncMouseReleaseId);
+ m_nAsyncMouseReleaseId = nullptr;
+ }
+
+ ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
+ if( !pSettings )
+ return;
+
+ for( int i = 1; i <= int(ScContentId::LAST); ++i )
+ {
+ ScContentId nEntry = static_cast<ScContentId>(i);
+ bool bExp = m_aRootNodes[nEntry] && m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]);
+ pSettings->SetExpanded( nEntry, bExp );
+ }
+
+ std::unique_ptr<weld::TreeIter> xCurEntry(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xCurEntry.get()))
+ xCurEntry.reset();
+
+ ScContentId nRoot;
+ sal_uLong nChild;
+ GetEntryIndexes(nRoot, nChild, xCurEntry.get());
+
+ pSettings->SetRootSelected( nRoot );
+ pSettings->SetChildSelected( nChild );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/navipi/navcitem.cxx b/sc/source/ui/navipi/navcitem.cxx
new file mode 100644
index 0000000000..871fbd445e
--- /dev/null
+++ b/sc/source/ui/navipi/navcitem.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/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <osl/diagnose.h>
+
+#include <navcitem.hxx>
+#include <navipi.hxx>
+#include <viewdata.hxx>
+#include <sc.hrc>
+
+ScNavigatorControllerItem::ScNavigatorControllerItem( sal_uInt16 nIdP,
+ ScNavigatorDlg& rDlg,
+ SfxBindings& rBindings )
+ : SfxControllerItem ( nIdP, rBindings ),
+ rNavigatorDlg ( rDlg )
+{
+}
+
+void ScNavigatorControllerItem::StateChangedAtToolBoxControl( sal_uInt16 /* nSID */, SfxItemState /* eState */,
+ const SfxPoolItem* pItem )
+{
+ switch( GetId() )
+ {
+ case SID_CURRENTCELL:
+ if ( pItem )
+ {
+ ScViewData* pViewData = rNavigatorDlg.GetViewData();
+ const SfxStringItem* pCellPosItem = dynamic_cast<const SfxStringItem*>( pItem );
+
+ OSL_ENSURE( pCellPosItem, "SfxStringItem expected!" );
+
+ if (pCellPosItem && pViewData)
+ {
+ const OUString& aAddress( pCellPosItem->GetValue() );
+ ScAddress aScAddress;
+ aScAddress.Parse(aAddress, pViewData->GetDocument());
+
+ SCCOL nCol = aScAddress.Col()+1;
+ SCROW nRow = aScAddress.Row()+1;
+
+ rNavigatorDlg.UpdateColumn( &nCol );
+ rNavigatorDlg.UpdateRow ( &nRow );
+ }
+ }
+ break;
+
+ case SID_CURRENTTAB:
+ if ( pItem )
+ {
+ const SfxUInt16Item* pTabItem = dynamic_cast< const SfxUInt16Item *>( pItem );
+
+ OSL_ENSURE( pTabItem, "SfxUInt16Item expected!" );
+
+ // table for Basic is 1-based
+ if ( pTabItem && pTabItem->GetValue() )
+ {
+ SCTAB nTab = pTabItem->GetValue() - 1;
+
+ rNavigatorDlg.UpdateTable( &nTab );
+ rNavigatorDlg.UpdateColumn();
+ rNavigatorDlg.UpdateRow();
+ }
+ }
+ break;
+
+ case SID_CURRENTDOC:
+
+ // nothing is done via SfxHintId::DocChanged
+
+ break;
+
+ case SID_SELECT_SCENARIO:
+ rNavigatorDlg.m_xWndScenarios->NotifyState(pItem);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/navipi/navipi.cxx b/sc/source/ui/navipi/navipi.cxx
new file mode 100644
index 0000000000..5fc6fee821
--- /dev/null
+++ b/sc/source/ui/navipi/navipi.cxx
@@ -0,0 +1,965 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/navigat.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/charclass.hxx>
+
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <navicfg.hxx>
+#include <navcitem.hxx>
+#include <navipi.hxx>
+#include <navsett.hxx>
+#include <markdata.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+using namespace com::sun::star;
+
+// maximum values for UI
+static SCCOL SCNAV_MAXCOL(const ScSheetLimits& rLimits) { return rLimits.GetMaxColCount(); }
+static sal_Int32 SCNAV_COLDIGITS(const ScSheetLimits& rLimits)
+{
+ return static_cast<sal_Int32>( floor( log10( static_cast<double>(SCNAV_MAXCOL(rLimits)))) ) + 1; // 1...256...18278
+}
+static sal_Int32 SCNAV_COLLETTERS(const ScSheetLimits& rLimits)
+{
+ return ::ScColToAlpha(SCNAV_MAXCOL(rLimits)).getLength(); // A...IV...ZZZ
+}
+
+static SCROW SCNAV_MAXROW(const ScSheetLimits& rSheetLimits)
+{
+ return rSheetLimits.GetMaxRowCount();
+}
+
+void ScNavigatorDlg::ReleaseFocus()
+{
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+
+ if ( pCurSh )
+ {
+ vcl::Window* pShellWnd = pCurSh->GetWindow();
+ if ( pShellWnd )
+ pShellWnd->GrabFocus();
+ }
+}
+
+namespace
+{
+ SCCOL NumToAlpha(const ScSheetLimits& rSheetLimits, SCCOL nColNo, OUString& rStr)
+ {
+ if ( nColNo > SCNAV_MAXCOL(rSheetLimits) )
+ nColNo = SCNAV_MAXCOL(rSheetLimits);
+ else if ( nColNo < 1 )
+ nColNo = 1;
+
+ ::ScColToAlpha( rStr, nColNo - 1);
+
+ return nColNo;
+ }
+
+ SCCOL AlphaToNum(const ScDocument& rDoc, OUString& rStr)
+ {
+ SCCOL nColumn = 0;
+
+ if ( CharClass::isAsciiAlpha( rStr) )
+ {
+ rStr = rStr.toAsciiUpperCase();
+
+ if (::AlphaToCol( rDoc, nColumn, rStr))
+ ++nColumn;
+
+ if ( (rStr.getLength() > SCNAV_COLLETTERS(rDoc.GetSheetLimits())) ||
+ (nColumn > SCNAV_MAXCOL(rDoc.GetSheetLimits())) )
+ {
+ nColumn = SCNAV_MAXCOL(rDoc.GetSheetLimits());
+ NumToAlpha( rDoc.GetSheetLimits(), nColumn, rStr );
+ }
+ }
+ else
+ rStr.clear();
+
+ return nColumn;
+ }
+
+ SCCOL NumStrToAlpha(const ScSheetLimits& rSheetLimits, OUString& rStr)
+ {
+ SCCOL nColumn = 0;
+
+ if ( CharClass::isAsciiNumeric(rStr) )
+ nColumn = NumToAlpha( rSheetLimits, static_cast<SCCOL>(rStr.toInt32()), rStr );
+ else
+ rStr.clear();
+
+ return nColumn;
+ }
+}
+
+IMPL_LINK(ScNavigatorDlg, ParseRowInputHdl, int*, result, bool)
+{
+ SCCOL nCol(0);
+
+ OUString aStrCol = m_xEdCol->get_text();
+
+ if (!aStrCol.isEmpty())
+ {
+ if (ScViewData* pData = GetViewData())
+ {
+ ScDocument& rDoc = pData->GetDocument();
+
+ if ( CharClass::isAsciiNumeric(aStrCol) )
+ nCol = NumStrToAlpha( rDoc.GetSheetLimits(), aStrCol );
+ else
+ nCol = AlphaToNum( rDoc, aStrCol );
+ }
+ }
+
+ *result = nCol;
+ return true;
+}
+
+IMPL_LINK_NOARG(ScNavigatorDlg, ExecuteColHdl, weld::Entry&, bool)
+{
+ ReleaseFocus();
+
+ SCROW nRow = m_xEdRow->get_value();
+ SCCOL nCol = m_xEdCol->get_value();
+
+ if ( (nCol > 0) && (nRow > 0) )
+ SetCurrentCell(nCol - 1, nRow - 1);
+
+ return true;
+}
+
+IMPL_LINK_NOARG(ScNavigatorDlg, FormatRowOutputHdl, weld::SpinButton&, void)
+{
+ OUString aStr;
+ ::ScColToAlpha(aStr, m_xEdCol->get_value() - 1);
+ m_xEdCol->set_text(aStr);
+}
+
+IMPL_LINK_NOARG(ScNavigatorDlg, ExecuteRowHdl, weld::Entry&, bool)
+{
+ ReleaseFocus();
+
+ SCCOL nCol = m_xEdCol->get_value();
+ SCROW nRow = m_xEdRow->get_value();
+
+ if ( (nCol > 0) && (nRow > 0) )
+ SetCurrentCell(nCol - 1, nRow - 1);
+
+ return true;
+}
+
+IMPL_LINK(ScNavigatorDlg, DocumentSelectHdl, weld::ComboBox&, rListBox, void)
+{
+ ScNavigatorDlg::ReleaseFocus();
+
+ OUString aDocName = rListBox.get_active_text();
+ m_xLbEntries->SelectDoc(aDocName);
+}
+
+IMPL_LINK(ScNavigatorDlg, ToolBoxSelectHdl, const OUString&, rSelId, void)
+{
+ // Switch the mode?
+ if (rSelId == "contents" || rSelId == "scenarios")
+ {
+ NavListMode eOldMode = eListMode;
+ NavListMode eNewMode;
+
+ if (rSelId == "scenarios")
+ {
+ if (eOldMode == NAV_LMODE_SCENARIOS)
+ eNewMode = NAV_LMODE_AREAS;
+ else
+ eNewMode = NAV_LMODE_SCENARIOS;
+ }
+ else // on/off
+ {
+ if (eOldMode == NAV_LMODE_NONE)
+ eNewMode = NAV_LMODE_AREAS;
+ else
+ eNewMode = NAV_LMODE_NONE;
+ }
+ SetListMode(eNewMode);
+ UpdateButtons();
+ }
+ else if (rSelId == "dragmode")
+ m_xTbxCmd2->set_menu_item_active("dragmode", !m_xTbxCmd2->get_menu_item_active("dragmode"));
+ else
+ {
+ if (rSelId == "datarange")
+ MarkDataArea();
+ else if (rSelId == "start")
+ StartOfDataArea();
+ else if (rSelId == "end")
+ EndOfDataArea();
+ else if (rSelId == "toggle")
+ {
+ m_xLbEntries->ToggleRoot();
+ UpdateButtons();
+ }
+ }
+}
+
+IMPL_LINK(ScNavigatorDlg, ToolBoxDropdownClickHdl, const OUString&, rCommand, void)
+{
+ if (!m_xTbxCmd2->get_menu_item_active(rCommand))
+ return;
+
+ // the popup menu of the drop mode has to be called in the
+ // click (button down) and not in the select (button up)
+ if (rCommand != "dragmode")
+ return;
+
+ switch (GetDropMode())
+ {
+ case 0:
+ m_xDragModeMenu->set_active("hyperlink", true);
+ break;
+ case 1:
+ m_xDragModeMenu->set_active("link", true);
+ break;
+ case 2:
+ m_xDragModeMenu->set_active("copy", true);
+ break;
+ }
+}
+
+IMPL_LINK(ScNavigatorDlg, MenuSelectHdl, const OUString&, rIdent, void)
+{
+ if (rIdent == u"hyperlink")
+ SetDropMode(0);
+ else if (rIdent == u"link")
+ SetDropMode(1);
+ else if (rIdent == u"copy")
+ SetDropMode(2);
+}
+
+void ScNavigatorDlg::UpdateButtons()
+{
+ NavListMode eMode = eListMode;
+ m_xTbxCmd2->set_item_active("scenarios", eMode == NAV_LMODE_SCENARIOS);
+ m_xTbxCmd1->set_item_active("contents", eMode != NAV_LMODE_NONE);
+
+ // the toggle button:
+ if (eMode == NAV_LMODE_SCENARIOS || eMode == NAV_LMODE_NONE)
+ {
+ m_xTbxCmd2->set_item_sensitive("toggle", false);
+ m_xTbxCmd2->set_item_active("toggle", false);
+ }
+ else
+ {
+ m_xTbxCmd2->set_item_sensitive("toggle", true);
+ bool bRootSet = m_xLbEntries->GetRootType() != ScContentId::ROOT;
+ m_xTbxCmd2->set_item_active("toggle", bRootSet);
+ }
+
+ OUString sImageId;
+ switch (nDropMode)
+ {
+ case SC_DROPMODE_URL:
+ sImageId = RID_BMP_DROP_URL;
+ break;
+ case SC_DROPMODE_LINK:
+ sImageId = RID_BMP_DROP_LINK;
+ break;
+ case SC_DROPMODE_COPY:
+ sImageId = RID_BMP_DROP_COPY;
+ break;
+ }
+ m_xTbxCmd2->set_item_icon_name("dragmode", sImageId);
+}
+
+ScNavigatorSettings::ScNavigatorSettings()
+ : mnRootSelected(ScContentId::ROOT)
+ , mnChildSelected(SC_CONTENT_NOCHILD)
+{
+ maExpandedVec.fill(false);
+}
+
+class ScNavigatorWin : public SfxNavigator
+{
+private:
+ std::unique_ptr<ScNavigatorDlg> m_xNavigator;
+public:
+ ScNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* pMgr,
+ vcl::Window* pParent, SfxChildWinInfo* pInfo);
+ virtual void StateChanged(StateChangedType nStateChange) override;
+ virtual void dispose() override
+ {
+ m_xNavigator.reset();
+ SfxNavigator::dispose();
+ }
+ virtual ~ScNavigatorWin() override
+ {
+ disposeOnce();
+ }
+};
+
+ScNavigatorWin::ScNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr,
+ vcl::Window* _pParent, SfxChildWinInfo* pInfo)
+ : SfxNavigator(_pBindings, _pMgr, _pParent, pInfo)
+{
+ m_xNavigator = std::make_unique<ScNavigatorDlg>(_pBindings, m_xContainer.get(), this);
+ SetMinOutputSizePixel(GetOptimalSize());
+}
+
+ScNavigatorDlg::ScNavigatorDlg(SfxBindings* pB, weld::Widget* pParent, SfxNavigator* pNavigatorDlg)
+ : PanelLayout(pParent, "NavigatorPanel", "modules/scalc/ui/navigatorpanel.ui")
+ , rBindings(*pB)
+ , m_xEdCol(m_xBuilder->weld_spin_button("column"))
+ , m_xEdRow(m_xBuilder->weld_spin_button("row"))
+ , m_xTbxCmd1(m_xBuilder->weld_toolbar("toolbox1"))
+ , m_xTbxCmd2(m_xBuilder->weld_toolbar("toolbox2"))
+ , m_xLbEntries(new ScContentTree(m_xBuilder->weld_tree_view("contentbox"), this))
+ , m_xScenarioBox(m_xBuilder->weld_widget("scenariobox"))
+ , m_xWndScenarios(new ScScenarioWindow(*m_xBuilder,
+ ScResId(SCSTR_QHLP_SCEN_LISTBOX), ScResId(SCSTR_QHLP_SCEN_COMMENT)))
+ , m_xLbDocuments(m_xBuilder->weld_combo_box("documents"))
+ , m_xDragModeMenu(m_xBuilder->weld_menu("dragmodemenu"))
+ , m_xNavigatorDlg(pNavigatorDlg)
+ , aContentIdle("ScNavigatorDlg aContentIdle")
+ , aStrActiveWin(ScResId(SCSTR_ACTIVEWIN))
+ , pViewData(nullptr )
+ , eListMode(NAV_LMODE_NONE)
+ , nDropMode(SC_DROPMODE_URL)
+ , nCurCol(0)
+ , nCurRow(0)
+ , nCurTab(0)
+{
+ UpdateInitShow();
+
+ UpdateSheetLimits();
+ m_xEdRow->set_width_chars(5);
+ //max rows is 1,000,000, which is too long for typical use
+ m_xEdRow->connect_activate(LINK(this, ScNavigatorDlg, ExecuteRowHdl));
+
+ m_xEdCol->connect_activate(LINK(this, ScNavigatorDlg, ExecuteColHdl));
+ m_xEdCol->connect_output(LINK(this, ScNavigatorDlg, FormatRowOutputHdl));
+ m_xEdCol->connect_input(LINK(this, ScNavigatorDlg, ParseRowInputHdl));
+
+ m_xTbxCmd1->connect_clicked(LINK(this, ScNavigatorDlg, ToolBoxSelectHdl));
+ m_xTbxCmd2->connect_clicked(LINK(this, ScNavigatorDlg, ToolBoxSelectHdl));
+
+ m_xTbxCmd2->set_item_menu("dragmode", m_xDragModeMenu.get());
+ m_xDragModeMenu->connect_activate(LINK(this, ScNavigatorDlg, MenuSelectHdl));
+ m_xTbxCmd2->connect_menu_toggled(LINK(this, ScNavigatorDlg, ToolBoxDropdownClickHdl));
+
+ ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg();
+ nDropMode = rCfg.GetDragMode();
+
+ m_xLbDocuments->set_size_request(42, -1); // set a nominal width so it takes width of surroundings
+ m_xLbDocuments->connect_changed(LINK(this, ScNavigatorDlg, DocumentSelectHdl));
+ aStrActive = " (" + ScResId(SCSTR_ACTIVE) + ")"; // " (active)"
+ aStrNotActive = " (" + ScResId(SCSTR_NOTACTIVE) + ")"; // " (not active)"
+
+ rBindings.ENTERREGISTRATIONS();
+
+ mvBoundItems[0].reset(new ScNavigatorControllerItem(SID_CURRENTCELL,*this,rBindings));
+ mvBoundItems[1].reset(new ScNavigatorControllerItem(SID_CURRENTTAB,*this,rBindings));
+ mvBoundItems[2].reset(new ScNavigatorControllerItem(SID_CURRENTDOC,*this,rBindings));
+ mvBoundItems[3].reset(new ScNavigatorControllerItem(SID_SELECT_SCENARIO,*this,rBindings));
+
+ rBindings.LEAVEREGISTRATIONS();
+
+ StartListening( *(SfxGetpApp()) );
+ StartListening( rBindings );
+
+ // was a category chosen as root?
+ ScContentId nLastRoot = rCfg.GetRootType();
+ if ( nLastRoot != ScContentId::ROOT )
+ m_xLbEntries->SetRootType( nLastRoot );
+
+ GetDocNames(nullptr);
+
+ UpdateButtons();
+
+ UpdateColumn();
+ UpdateRow();
+ UpdateTable(nullptr);
+ m_xLbEntries->hide();
+ m_xScenarioBox->hide();
+
+ aContentIdle.SetInvokeHandler( LINK( this, ScNavigatorDlg, TimeHdl ) );
+ aContentIdle.SetPriority( TaskPriority::LOWEST );
+
+ m_xLbEntries->SetNavigatorDlgFlag(true);
+
+ // if scenario was active, switch on
+ NavListMode eNavMode = static_cast<NavListMode>(rCfg.GetListMode());
+ if (eNavMode == NAV_LMODE_SCENARIOS)
+ m_xTbxCmd2->set_item_active("scenarios", true);
+ else
+ eNavMode = NAV_LMODE_AREAS;
+ SetListMode(eNavMode);
+
+ if(comphelper::LibreOfficeKit::isActive())
+ {
+ m_xBuilder->weld_container("gridbuttons")->hide();
+ m_xLbDocuments->hide();
+ }
+}
+
+weld::Window* ScNavigatorDlg::GetFrameWeld() const
+{
+ if (m_xNavigatorDlg)
+ return m_xNavigatorDlg->GetFrameWeld();
+ return PanelLayout::GetFrameWeld();
+}
+
+void ScNavigatorDlg::UpdateSheetLimits()
+{
+ if (ScViewData* pData = GetViewData())
+ {
+ ScDocument& rDoc = pData->GetDocument();
+ m_xEdRow->set_range(1, SCNAV_MAXROW(rDoc.GetSheetLimits()));
+ m_xEdCol->set_range(1, SCNAV_MAXCOL(rDoc.GetSheetLimits()));
+ m_xEdCol->set_width_chars(SCNAV_COLDIGITS(rDoc.GetSheetLimits())); // 1...256...18278 or A...IV...ZZZ
+ }
+}
+
+void ScNavigatorDlg::UpdateInitShow()
+{
+ // When the navigator is displayed in the sidebar, or is otherwise
+ // docked, it has the whole deck to fill. Therefore hide the button that
+ // hides all controls below the top two rows of buttons.
+ m_xTbxCmd1->set_item_visible("contents", ParentIsFloatingWindow(m_xNavigatorDlg));
+}
+
+void ScNavigatorWin::StateChanged(StateChangedType nStateChange)
+{
+ SfxNavigator::StateChanged(nStateChange);
+ if (nStateChange == StateChangedType::InitShow)
+ m_xNavigator->UpdateInitShow();
+}
+
+ScNavigatorDlg::~ScNavigatorDlg()
+{
+ aContentIdle.Stop();
+
+ for (auto & p : mvBoundItems)
+ p.reset();
+ moMarkArea.reset();
+
+ EndListening( *(SfxGetpApp()) );
+ EndListening( rBindings );
+
+ m_xEdCol.reset();
+ m_xEdRow.reset();
+ m_xTbxCmd1.reset();
+ m_xTbxCmd2.reset();
+ m_xDragModeMenu.reset();
+ m_xLbEntries.reset();
+ m_xWndScenarios.reset();
+ m_xScenarioBox.reset();
+ m_xLbDocuments.reset();
+}
+
+void ScNavigatorDlg::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
+ {
+ // This is for when the document might change and the navigator
+ // wants to update for the new document, which isn't a scenario
+ // that happens in online.
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ const SfxEventHint& rEventHint = static_cast<const SfxEventHint&>(rHint);
+ if (rEventHint.GetEventId() == SfxEventHintId::ActivateDoc)
+ {
+ UpdateSheetLimits();
+ bool bRefreshed = m_xLbEntries->ActiveDocChanged();
+ // UpdateAll just possibly calls Refresh (and always
+ // ContentUpdated) so if ActiveDocChanged already called Refresh
+ // skip re-calling it
+ if (bRefreshed)
+ ContentUpdated();
+ else
+ UpdateAll();
+ }
+ }
+ else
+ {
+ const SfxHintId nHintId = rHint.GetId();
+
+ if (nHintId == SfxHintId::ScDocNameChanged)
+ {
+ m_xLbEntries->ActiveDocChanged();
+ }
+ else if (NAV_LMODE_NONE == eListMode)
+ {
+ // Table not any more
+ }
+ else
+ {
+ switch ( nHintId )
+ {
+ case SfxHintId::ScTablesChanged:
+ m_xLbEntries->Refresh( ScContentId::TABLE );
+ break;
+
+ case SfxHintId::ScDbAreasChanged:
+ m_xLbEntries->Refresh( ScContentId::DBAREA );
+ break;
+
+ case SfxHintId::ScAreasChanged:
+ m_xLbEntries->Refresh( ScContentId::RANGENAME );
+ break;
+
+ case SfxHintId::ScDrawChanged:
+ m_xLbEntries->Refresh( ScContentId::GRAPHIC );
+ m_xLbEntries->Refresh( ScContentId::OLEOBJECT );
+ m_xLbEntries->Refresh( ScContentId::DRAWING );
+ break;
+
+ case SfxHintId::ScAreaLinksChanged:
+ m_xLbEntries->Refresh( ScContentId::AREALINK );
+ break;
+
+ // SfxHintId::DocChanged not only at document change
+
+ case SfxHintId::ScNavigatorUpdateAll:
+ UpdateAll();
+ break;
+
+ case SfxHintId::ScDataChanged:
+ case SfxHintId::ScAnyDataChanged:
+ aContentIdle.Start(); // Do not search notes immediately
+ break;
+ case SfxHintId::ScSelectionChanged:
+ UpdateSelection();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+IMPL_LINK( ScNavigatorDlg, TimeHdl, Timer*, pIdle, void )
+{
+ if ( pIdle != &aContentIdle )
+ return;
+
+ m_xLbEntries->Refresh( ScContentId::NOTE );
+}
+
+void ScNavigatorDlg::SetDropMode(sal_uInt16 nNew)
+{
+ nDropMode = nNew;
+ UpdateButtons();
+ ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg();
+ rCfg.SetDragMode(nDropMode);
+}
+
+void ScNavigatorDlg::SetCurrentCell( SCCOL nColNo, SCROW nRowNo )
+{
+ if ((nColNo+1 == nCurCol) && (nRowNo+1 == nCurRow))
+ return;
+
+ // SID_CURRENTCELL == Item #0 clear cache, so it's possible
+ // setting the current cell even in combined areas
+ mvBoundItems[0]->ClearCache();
+
+ ScAddress aScAddress( nColNo, nRowNo, 0 );
+ OUString aAddr(aScAddress.Format(ScRefFlags::ADDR_ABS));
+
+ bool bUnmark = false;
+ if ( GetViewData() )
+ bUnmark = !pViewData->GetMarkData().IsCellMarked( nColNo, nRowNo );
+
+ SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
+ SfxBoolItem aUnmarkItem( FN_PARAM_1, bUnmark ); // cancel selection
+
+ rBindings.GetDispatcher()->ExecuteList(SID_CURRENTCELL,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aPosItem, &aUnmarkItem });
+}
+
+void ScNavigatorDlg::SetCurrentCellStr( const OUString& rName )
+{
+ mvBoundItems[0]->ClearCache();
+ SfxStringItem aNameItem( SID_CURRENTCELL, rName );
+
+ rBindings.GetDispatcher()->ExecuteList(SID_CURRENTCELL,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aNameItem });
+}
+
+void ScNavigatorDlg::SetCurrentTable( SCTAB nTabNo )
+{
+ if ( nTabNo != nCurTab )
+ {
+ // Table for basic is base-1
+ SfxUInt16Item aTabItem( SID_CURRENTTAB, static_cast<sal_uInt16>(nTabNo) + 1 );
+ rBindings.GetDispatcher()->ExecuteList(SID_CURRENTTAB,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aTabItem });
+ }
+}
+
+void ScNavigatorDlg::SetCurrentTableStr( std::u16string_view rName )
+{
+ if (!GetViewData()) return;
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ OUString aTabName;
+ SCTAB nLastSheet = 0;
+
+ for (SCTAB i = 0; i<nCount; i++)
+ {
+ rDoc.GetName(i, aTabName);
+ if (aTabName == rName)
+ {
+ // Check if this is a Scenario sheet and if so select the sheet
+ // where it belongs to, which is the previous non-Scenario sheet.
+ if (rDoc.IsScenario(i))
+ {
+ SetCurrentTable(nLastSheet);
+ return;
+ }
+ else
+ {
+ SetCurrentTable(i);
+ return;
+ }
+ }
+ else
+ {
+ if (!rDoc.IsScenario(i))
+ nLastSheet = i;
+ }
+ }
+}
+
+void ScNavigatorDlg::SetCurrentObject( const OUString& rName )
+{
+ SfxStringItem aNameItem( SID_CURRENTOBJECT, rName );
+ rBindings.GetDispatcher()->ExecuteList( SID_CURRENTOBJECT,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aNameItem });
+}
+
+void ScNavigatorDlg::SetCurrentDoc( const OUString& rDocName ) // activate
+{
+ SfxStringItem aDocItem( SID_CURRENTDOC, rDocName );
+ rBindings.GetDispatcher()->ExecuteList( SID_CURRENTDOC,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aDocItem });
+}
+
+void ScNavigatorDlg::UpdateSelection()
+{
+ ScTabViewShell* pViewSh = GetTabViewShell();
+ if( !pViewSh )
+ return;
+
+ uno::Reference< drawing::XShapes > xShapes = pViewSh->getSelectedXShapes();
+ if( !xShapes )
+ return;
+
+ uno::Reference< container::XIndexAccess > xIndexAccess(
+ xShapes, uno::UNO_QUERY_THROW );
+ if( xIndexAccess->getCount() > 1 )
+ return;
+ uno::Reference< drawing::XShape > xShape;
+ if( xIndexAccess->getByIndex(0) >>= xShape )
+ {
+ uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY_THROW );
+ OUString sName = xNamed->getName();
+ if (!sName.isEmpty())
+ {
+ m_xLbEntries->SelectEntryByName( ScContentId::DRAWING, sName );
+ }
+ }
+}
+
+ScTabViewShell* ScNavigatorDlg::GetTabViewShell()
+{
+ return dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+}
+
+ScNavigatorSettings* ScNavigatorDlg::GetNavigatorSettings()
+{
+ // Don't store the settings pointer here, because the settings belong to
+ // the view, and the view may be closed while the navigator is open (reload).
+ // If the pointer is cached here again later for performance reasons, it has to
+ // be forgotten when the view is closed.
+
+ ScTabViewShell* pViewSh = GetTabViewShell();
+ return pViewSh ? pViewSh->GetNavigatorSettings() : nullptr;
+}
+
+ScViewData* ScNavigatorDlg::GetViewData()
+{
+ ScTabViewShell* pViewSh = GetTabViewShell();
+ pViewData = pViewSh ? &pViewSh->GetViewData() : nullptr;
+ return pViewData;
+}
+
+void ScNavigatorDlg::UpdateColumn( const SCCOL* pCol )
+{
+ if ( pCol )
+ nCurCol = *pCol;
+ else if ( GetViewData() )
+ nCurCol = pViewData->GetCurX() + 1;
+
+ m_xEdCol->set_value(nCurCol);
+}
+
+void ScNavigatorDlg::UpdateRow( const SCROW* pRow )
+{
+ if ( pRow )
+ nCurRow = *pRow;
+ else if ( GetViewData() )
+ nCurRow = pViewData->GetCurY() + 1;
+
+ m_xEdRow->set_value(nCurRow);
+}
+
+void ScNavigatorDlg::UpdateTable( const SCTAB* pTab )
+{
+ if ( pTab )
+ nCurTab = *pTab;
+ else if ( GetViewData() )
+ nCurTab = pViewData->GetTabNo();
+}
+
+void ScNavigatorDlg::UpdateAll()
+{
+ switch (eListMode)
+ {
+ case NAV_LMODE_AREAS:
+ m_xLbEntries->Refresh();
+ break;
+ case NAV_LMODE_NONE:
+ //! ???
+ break;
+ default:
+ break;
+ }
+ ContentUpdated(); // not again
+}
+
+void ScNavigatorDlg::ContentUpdated()
+{
+ aContentIdle.Stop();
+}
+
+void ScNavigatorDlg::SetListMode(NavListMode eMode)
+{
+ if (eMode != eListMode)
+ {
+ bool bForceParentResize = ParentIsFloatingWindow(m_xNavigatorDlg) &&
+ (eMode == NAV_LMODE_NONE || eListMode == NAV_LMODE_NONE);
+ SfxNavigator* pNav = bForceParentResize ? m_xNavigatorDlg.get() : nullptr;
+ if (pNav && eMode == NAV_LMODE_NONE) //save last normal size on minimizing
+ aExpandedSize = pNav->GetSizePixel();
+
+ eListMode = eMode;
+
+ switch (eMode)
+ {
+ case NAV_LMODE_NONE:
+ ShowList(false);
+ break;
+ case NAV_LMODE_AREAS:
+ m_xLbEntries->Refresh();
+ ShowList(true);
+ break;
+ case NAV_LMODE_SCENARIOS:
+ ShowScenarios();
+ break;
+ }
+
+ UpdateButtons();
+
+ if (eMode != NAV_LMODE_NONE)
+ {
+ ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg();
+ rCfg.SetListMode( static_cast<sal_uInt16>(eMode) );
+ }
+
+ if (pNav)
+ {
+ pNav->InvalidateChildSizeCache();
+ Size aOptimalSize(pNav->GetOptimalSize());
+ Size aNewSize(pNav->GetOutputSizePixel());
+ aNewSize.setHeight( eMode == NAV_LMODE_NONE ? aOptimalSize.Height() : aExpandedSize.Height() );
+ pNav->SetMinOutputSizePixel(aOptimalSize);
+ pNav->SetOutputSizePixel(aNewSize);
+ }
+ }
+
+ if (moMarkArea)
+ UnmarkDataArea();
+}
+
+void ScNavigatorDlg::ShowList(bool bShow)
+{
+ if (bShow)
+ {
+ m_xLbEntries->show();
+ m_xLbDocuments->show();
+ }
+ else
+ {
+ m_xLbEntries->hide();
+ m_xLbDocuments->hide();
+ }
+ m_xScenarioBox->hide();
+}
+
+void ScNavigatorDlg::ShowScenarios()
+{
+ rBindings.Invalidate( SID_SELECT_SCENARIO );
+ rBindings.Update( SID_SELECT_SCENARIO );
+
+ m_xScenarioBox->show();
+ m_xLbDocuments->show();
+ m_xLbEntries->hide();
+}
+
+// documents for Dropdown-Listbox
+void ScNavigatorDlg::GetDocNames( const OUString* pManualSel )
+{
+ m_xLbDocuments->clear();
+ m_xLbDocuments->freeze();
+
+ ScDocShell* pCurrentSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+
+ OUString aSelEntry;
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ while ( pSh )
+ {
+ if ( dynamic_cast<const ScDocShell*>( pSh) != nullptr )
+ {
+ OUString aName = pSh->GetTitle();
+ OUString aEntry = aName;
+ if (pSh == pCurrentSh)
+ aEntry += aStrActive;
+ else
+ aEntry += aStrNotActive;
+ m_xLbDocuments->append_text(aEntry);
+
+ if ( pManualSel ? ( aName == *pManualSel )
+ : ( pSh == pCurrentSh ) )
+ aSelEntry = aEntry; // complete entry for selection
+ }
+
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+
+ m_xLbDocuments->append_text(aStrActiveWin);
+
+ m_xLbDocuments->thaw();
+
+ m_xLbDocuments->set_active_text(aSelEntry);
+}
+
+void ScNavigatorDlg::MarkDataArea()
+{
+ ScTabViewShell* pViewSh = GetTabViewShell();
+
+ if ( !pViewSh )
+ return;
+
+ if ( !moMarkArea )
+ moMarkArea.emplace();
+
+ pViewSh->MarkDataArea();
+ const ScRange& aMarkRange = pViewSh->GetViewData().GetMarkData().GetMarkArea();
+ moMarkArea->nColStart = aMarkRange.aStart.Col();
+ moMarkArea->nRowStart = aMarkRange.aStart.Row();
+ moMarkArea->nColEnd = aMarkRange.aEnd.Col();
+ moMarkArea->nRowEnd = aMarkRange.aEnd.Row();
+ moMarkArea->nTab = aMarkRange.aStart.Tab();
+}
+
+void ScNavigatorDlg::UnmarkDataArea()
+{
+ ScTabViewShell* pViewSh = GetTabViewShell();
+
+ if ( pViewSh )
+ {
+ pViewSh->Unmark();
+ moMarkArea.reset();
+ }
+}
+
+void ScNavigatorDlg::StartOfDataArea()
+{
+ // pMarkArea evaluate ???
+
+ if ( GetViewData() )
+ {
+ ScMarkData& rMark = pViewData->GetMarkData();
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+
+ SCCOL nCol = aMarkRange.aStart.Col();
+ SCROW nRow = aMarkRange.aStart.Row();
+
+ if ( (nCol+1 != m_xEdCol->get_value()) || (nRow+1 != m_xEdRow->get_value()) )
+ SetCurrentCell( nCol, nRow );
+ }
+}
+
+void ScNavigatorDlg::EndOfDataArea()
+{
+ // pMarkArea evaluate ???
+
+ if ( GetViewData() )
+ {
+ ScMarkData& rMark = pViewData->GetMarkData();
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+
+ SCCOL nCol = aMarkRange.aEnd.Col();
+ SCROW nRow = aMarkRange.aEnd.Row();
+
+ if ( (nCol+1 != m_xEdCol->get_value()) || (nRow+1 != m_xEdRow->get_value()) )
+ SetCurrentCell( nCol, nRow );
+ }
+}
+
+SFX_IMPL_DOCKINGWINDOW(ScNavigatorWrapper, SID_NAVIGATOR);
+
+ScNavigatorWrapper::ScNavigatorWrapper(vcl::Window *_pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo* pInfo)
+ : SfxNavigatorWrapper(_pParent, nId)
+{
+ SetWindow(VclPtr<ScNavigatorWin>::Create(pBindings, this, _pParent, pInfo));
+ Initialize();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/navipi/scenwnd.cxx b/sc/source/ui/navipi/scenwnd.cxx
new file mode 100644
index 0000000000..22ee5148fc
--- /dev/null
+++ b/sc/source/ui/navipi/scenwnd.cxx
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <navipi.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <helpids.h>
+
+// class ScScenarioWindow ------------------------------------------------
+
+void ScScenarioWindow::UpdateEntries( const std::vector<OUString> &rNewEntryList )
+{
+ m_xLbScenario->clear();
+ m_aEntries.clear();
+
+ switch( rNewEntryList.size() )
+ {
+ case 0:
+ // no scenarios in current sheet
+ SetComment( OUString() );
+ break;
+
+ case 1:
+ // sheet is a scenario container, comment only
+ SetComment( rNewEntryList[0] );
+ break;
+
+ default:
+ {
+ // sheet contains scenarios
+ assert(rNewEntryList.size() % 3 == 0 && "ScScenarioListBox::UpdateEntries - wrong list size");
+ m_xLbScenario->freeze();
+
+ std::vector<OUString>::const_iterator iter;
+ for (iter = rNewEntryList.begin(); iter != rNewEntryList.end(); ++iter)
+ {
+ ScenarioEntry aEntry;
+
+ // first entry of a triple is the scenario name
+ aEntry.maName = *iter;
+
+ // second entry of a triple is the scenario comment
+ ++iter;
+ aEntry.maComment = *iter;
+
+ // third entry of a triple is the protection ("0" = not protected, "1" = protected)
+ ++iter;
+ aEntry.mbProtected = !(*iter).isEmpty() && (*iter)[0] != '0';
+
+ m_aEntries.push_back( aEntry );
+ m_xLbScenario->append_text(aEntry.maName);
+ }
+ m_xLbScenario->thaw();
+ m_xLbScenario->unselect_all();
+ SetComment(OUString());
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScScenarioWindow, SelectHdl, weld::TreeView&, void)
+{
+ if (const ScenarioEntry* pEntry = GetSelectedScenarioEntry())
+ SetComment(pEntry->maComment);
+}
+
+IMPL_LINK_NOARG(ScScenarioWindow, DoubleClickHdl, weld::TreeView&, bool)
+{
+ SelectScenario();
+ return true;
+}
+
+IMPL_LINK(ScScenarioWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled = false;
+
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ switch( aCode.GetCode() )
+ {
+ case KEY_RETURN:
+ SelectScenario();
+ bHandled = true;
+ break;
+ case KEY_DELETE:
+ DeleteScenario();
+ bHandled = true;
+ break;
+ }
+
+ return bHandled;
+}
+
+IMPL_LINK(ScScenarioWindow, ContextMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ bool bHandled = false;
+
+ if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
+ {
+ if (const ScenarioEntry* pEntry = GetSelectedScenarioEntry())
+ {
+ if (!pEntry->mbProtected)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xLbScenario.get(), "modules/scalc/ui/scenariomenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+ OUString sIdent(xPopup->popup_at_rect(m_xLbScenario.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+ if (sIdent == "delete")
+ DeleteScenario();
+ else if (sIdent == "edit")
+ EditScenario();
+ }
+ }
+ bHandled = true;
+ }
+
+ return bHandled;
+}
+
+const ScScenarioWindow::ScenarioEntry* ScScenarioWindow::GetSelectedScenarioEntry() const
+{
+ size_t nPos = m_xLbScenario->get_selected_index();
+ return (nPos < m_aEntries.size()) ? &m_aEntries[ nPos ] : nullptr;
+}
+
+void ScScenarioWindow::ExecuteScenarioSlot(sal_uInt16 nSlotId)
+{
+ if( SfxViewFrame* pViewFrm = SfxViewFrame::Current() )
+ {
+ SfxStringItem aStringItem(nSlotId, m_xLbScenario->get_selected_text());
+ pViewFrm->GetDispatcher()->ExecuteList(nSlotId,
+ SfxCallMode::SLOT | SfxCallMode::RECORD, { &aStringItem } );
+ }
+}
+
+void ScScenarioWindow::SelectScenario()
+{
+ if (m_xLbScenario->get_selected_index() != -1)
+ ExecuteScenarioSlot(SID_SELECT_SCENARIO);
+}
+
+void ScScenarioWindow::EditScenario()
+{
+ if (m_xLbScenario->get_selected_index() != -1)
+ ExecuteScenarioSlot(SID_EDIT_SCENARIO);
+}
+
+void ScScenarioWindow::DeleteScenario()
+{
+ if (m_xLbScenario->get_selected_index() != -1)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xLbScenario.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_QUERY_DELSCENARIO)));
+ xQueryBox->set_default_response(RET_YES);
+ if (xQueryBox->run() == RET_YES)
+ ExecuteScenarioSlot(SID_DELETE_SCENARIO);
+ }
+}
+
+// class ScScenarioWindow ------------------------------------------------
+
+ScScenarioWindow::ScScenarioWindow(weld::Builder& rBuilder, const OUString& aQH_List,
+ const OUString& aQH_Comment)
+ : m_xLbScenario(rBuilder.weld_tree_view("scenariolist"))
+ , m_xEdComment(rBuilder.weld_text_view("scenariotext"))
+{
+ m_xLbScenario->set_help_id(HID_SC_SCENWIN_TOP);
+ m_xEdComment->set_help_id(HID_SC_SCENWIN_BOTTOM);
+
+ m_xLbScenario->set_tooltip_text(aQH_List);
+ m_xEdComment->set_tooltip_text(aQH_Comment);
+
+ m_xLbScenario->connect_changed(LINK(this, ScScenarioWindow, SelectHdl));
+ m_xLbScenario->connect_row_activated(LINK(this, ScScenarioWindow, DoubleClickHdl));
+ m_xLbScenario->connect_key_press(LINK(this, ScScenarioWindow, KeyInputHdl));
+ m_xLbScenario->connect_popup_menu(LINK(this, ScScenarioWindow, ContextMenuHdl));
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (pViewFrm)
+ {
+ SfxBindings& rBindings = pViewFrm->GetBindings();
+ rBindings.Invalidate( SID_SELECT_SCENARIO );
+ rBindings.Update( SID_SELECT_SCENARIO );
+ }
+}
+
+ScScenarioWindow::~ScScenarioWindow()
+{
+}
+
+void ScScenarioWindow::NotifyState( const SfxPoolItem* pState )
+{
+ if( pState )
+ {
+ m_xLbScenario->set_sensitive(true);
+
+ if ( auto pStringItem = dynamic_cast<const SfxStringItem*>( pState) )
+ {
+ const OUString& aNewEntry( pStringItem->GetValue() );
+
+ if (!aNewEntry.isEmpty())
+ m_xLbScenario->select_text(aNewEntry);
+ else
+ m_xLbScenario->unselect_all();
+ }
+ else if ( auto pStringListItem = dynamic_cast<const SfxStringListItem*>( pState) )
+ {
+ UpdateEntries(pStringListItem->GetList());
+ }
+ }
+ else
+ {
+ m_xLbScenario->set_sensitive(false);
+ m_xLbScenario->unselect_all();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/calcoptionsdlg.cxx b/sc/source/ui/optdlg/calcoptionsdlg.cxx
new file mode 100644
index 0000000000..ec65fb9d80
--- /dev/null
+++ b/sc/source/ui/optdlg/calcoptionsdlg.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/.
+ */
+
+#include <sal/config.h>
+
+#include <officecfg/Office/Calc.hxx>
+
+#include <calcconfig.hxx>
+#include "calcoptionsdlg.hxx"
+
+namespace {
+
+formula::FormulaGrammar::AddressConvention toAddressConvention(sal_Int32 nPos)
+{
+ switch (nPos)
+ {
+ case 1:
+ return formula::FormulaGrammar::CONV_OOO;
+ case 2:
+ return formula::FormulaGrammar::CONV_XL_A1;
+ case 3:
+ return formula::FormulaGrammar::CONV_XL_R1C1;
+ case 4:
+ return formula::FormulaGrammar::CONV_A1_XL_A1;
+ case 0:
+ default:
+ ;
+ }
+
+ return formula::FormulaGrammar::CONV_UNSPECIFIED;
+}
+
+sal_Int32 toSelectedItem( formula::FormulaGrammar::AddressConvention eConv )
+{
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_OOO:
+ return 1;
+ case formula::FormulaGrammar::CONV_XL_A1:
+ return 2;
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ return 3;
+ case formula::FormulaGrammar::CONV_A1_XL_A1:
+ return 4;
+ default:
+ ;
+ }
+ return 0;
+}
+
+}
+
+ScCalcOptionsDialog::ScCalcOptionsDialog(weld::Window* pParent, const ScCalcConfig& rConfig, bool bWriteConfig)
+ : GenericDialogController(pParent, "modules/scalc/ui/formulacalculationoptions.ui", "FormulaCalculationOptions")
+ , maConfig(rConfig)
+ , mbSelectedEmptyStringAsZero(rConfig.mbEmptyStringAsZero)
+ , mbWriteConfig(bWriteConfig)
+ , mxEmptyAsZero(m_xBuilder->weld_check_button("checkEmptyAsZero"))
+ , mxConversion(m_xBuilder->weld_combo_box("comboConversion"))
+ , mxCurrentDocOnly(m_xBuilder->weld_check_button("current_doc"))
+ , mxSyntax(m_xBuilder->weld_combo_box("comboSyntaxRef"))
+{
+ mxConversion->set_active(static_cast<int>(rConfig.meStringConversion));
+ mxConversion->connect_changed(LINK(this, ScCalcOptionsDialog, ConversionModifiedHdl));
+ mxConversion->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::StringConversion::isReadOnly() );
+
+ mxEmptyAsZero->set_active(rConfig.mbEmptyStringAsZero);
+ mxEmptyAsZero->connect_toggled(LINK(this, ScCalcOptionsDialog, AsZeroModifiedHdl));
+ CoupleEmptyAsZeroToStringConversion();
+ mxEmptyAsZero->set_sensitive ( !officecfg::Office::Calc::Formula::Syntax::EmptyStringAsZero::isReadOnly() );
+
+ mxSyntax->set_active(toSelectedItem(rConfig.meStringRefAddressSyntax));
+ mxSyntax->connect_changed(LINK(this, ScCalcOptionsDialog, SyntaxModifiedHdl));
+ mxSyntax->set_sensitive ( !officecfg::Office::Calc::Formula::Syntax::StringRefAddressSyntax::isReadOnly() );
+
+ mxCurrentDocOnly->set_active(!mbWriteConfig);
+ mxCurrentDocOnly->connect_toggled(LINK(this, ScCalcOptionsDialog, CurrentDocOnlyHdl));
+}
+
+ScCalcOptionsDialog::~ScCalcOptionsDialog()
+{
+}
+
+void ScCalcOptionsDialog::CoupleEmptyAsZeroToStringConversion()
+{
+ switch (maConfig.meStringConversion)
+ {
+ case ScCalcConfig::StringConversion::ILLEGAL:
+ maConfig.mbEmptyStringAsZero = false;
+ mxEmptyAsZero->set_active(false);
+ mxEmptyAsZero->set_sensitive(false);
+ break;
+ case ScCalcConfig::StringConversion::ZERO:
+ maConfig.mbEmptyStringAsZero = true;
+ mxEmptyAsZero->set_active(true);
+ mxEmptyAsZero->set_sensitive(false);
+ break;
+ case ScCalcConfig::StringConversion::UNAMBIGUOUS:
+ case ScCalcConfig::StringConversion::LOCALE:
+ // Reset to the value the user selected before.
+ maConfig.mbEmptyStringAsZero = mbSelectedEmptyStringAsZero;
+ mxEmptyAsZero->set_sensitive(true);
+ mxEmptyAsZero->set_active(mbSelectedEmptyStringAsZero);
+ break;
+ }
+}
+
+IMPL_LINK(ScCalcOptionsDialog, AsZeroModifiedHdl, weld::Toggleable&, rCheckBox, void )
+{
+ maConfig.mbEmptyStringAsZero = mbSelectedEmptyStringAsZero = rCheckBox.get_active();
+}
+
+IMPL_LINK(ScCalcOptionsDialog, ConversionModifiedHdl, weld::ComboBox&, rConv, void)
+{
+ maConfig.meStringConversion = static_cast<ScCalcConfig::StringConversion>(rConv.get_active());
+ CoupleEmptyAsZeroToStringConversion();
+}
+
+IMPL_LINK(ScCalcOptionsDialog, SyntaxModifiedHdl, weld::ComboBox&, rSyntax, void)
+{
+ maConfig.SetStringRefSyntax(toAddressConvention(rSyntax.get_active()));
+}
+
+IMPL_LINK(ScCalcOptionsDialog, CurrentDocOnlyHdl, weld::Toggleable&, rCheckBox, void)
+{
+ mbWriteConfig = !rCheckBox.get_active();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/calcoptionsdlg.hxx b/sc/source/ui/optdlg/calcoptionsdlg.hxx
new file mode 100644
index 0000000000..97d7bb21c4
--- /dev/null
+++ b/sc/source/ui/optdlg/calcoptionsdlg.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/.
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <calcconfig.hxx>
+
+class ScCalcOptionsDialog : public weld::GenericDialogController
+{
+public:
+ ScCalcOptionsDialog(weld::Window* pParent, const ScCalcConfig& rConfig, bool bWriteConfig);
+ virtual ~ScCalcOptionsDialog() override;
+
+ DECL_LINK(AsZeroModifiedHdl, weld::Toggleable&, void);
+ DECL_LINK(ConversionModifiedHdl, weld::ComboBox&, void);
+ DECL_LINK(SyntaxModifiedHdl, weld::ComboBox&, void);
+ DECL_LINK(CurrentDocOnlyHdl, weld::Toggleable&, void);
+
+ const ScCalcConfig& GetConfig() const { return maConfig; }
+ bool GetWriteCalcConfig() const { return mbWriteConfig; }
+
+private:
+ void CoupleEmptyAsZeroToStringConversion();
+
+private:
+ ScCalcConfig maConfig;
+ bool mbSelectedEmptyStringAsZero;
+ bool mbWriteConfig;
+
+ std::unique_ptr<weld::CheckButton> mxEmptyAsZero;
+ std::unique_ptr<weld::ComboBox> mxConversion;
+ std::unique_ptr<weld::CheckButton> mxCurrentDocOnly;
+ std::unique_ptr<weld::ComboBox> mxSyntax;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/opredlin.cxx b/sc/source/ui/optdlg/opredlin.cxx
new file mode 100644
index 0000000000..5aa9045364
--- /dev/null
+++ b/sc/source/ui/optdlg/opredlin.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svx/colorbox.hxx>
+
+#include <appoptio.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <svx/svxids.hrc>
+
+#include <opredlin.hxx>
+
+ScRedlineOptionsTabPage::ScRedlineOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optchangespage.ui", "OptChangesPage", &rSet)
+ , m_xContentColorLB(new ColorListBox(m_xBuilder->weld_menu_button("changes"),
+ [this]{ return GetDialogController()->getDialog(); }))
+ , m_xRemoveColorLB(new ColorListBox(m_xBuilder->weld_menu_button("deletions"),
+ [this]{ return GetDialogController()->getDialog(); }))
+ , m_xInsertColorLB(new ColorListBox(m_xBuilder->weld_menu_button("entries"),
+ [this]{ return GetDialogController()->getDialog(); }))
+ , m_xMoveColorLB(new ColorListBox(m_xBuilder->weld_menu_button("insertions"),
+ [this]{ return GetDialogController()->getDialog(); }))
+{
+ m_xContentColorLB->SetSlotId(SID_AUTHOR_COLOR);
+ m_xRemoveColorLB->SetSlotId(SID_AUTHOR_COLOR);
+ m_xInsertColorLB->SetSlotId(SID_AUTHOR_COLOR);
+ m_xMoveColorLB->SetSlotId(SID_AUTHOR_COLOR);
+}
+
+ScRedlineOptionsTabPage::~ScRedlineOptionsTabPage()
+{
+ m_xContentColorLB.reset();
+ m_xRemoveColorLB.reset();
+ m_xInsertColorLB.reset();
+ m_xMoveColorLB.reset();
+}
+
+std::unique_ptr<SfxTabPage> ScRedlineOptionsTabPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<ScRedlineOptionsTabPage>( pPage, pController, *rSet );
+}
+
+OUString ScRedlineOptionsTabPage::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label1", "label2", "label3", "label4", "label5" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScRedlineOptionsTabPage::FillItemSet( SfxItemSet* /* rSet */ )
+{
+ ScAppOptions aAppOptions=SC_MOD()->GetAppOptions();
+
+ Color nNew = m_xContentColorLB->GetSelectEntryColor();
+ aAppOptions.SetTrackContentColor(nNew);
+
+ nNew = m_xMoveColorLB->GetSelectEntryColor();
+ aAppOptions.SetTrackMoveColor(nNew);
+
+ nNew = m_xInsertColorLB->GetSelectEntryColor();
+ aAppOptions.SetTrackInsertColor(nNew);
+
+ nNew = m_xRemoveColorLB->GetSelectEntryColor();
+ aAppOptions.SetTrackDeleteColor(nNew);
+
+ SC_MOD()->SetAppOptions(aAppOptions);
+
+ // repaint (if everything would be done by Items (how it should be),
+ // this wouldn't be necessary)
+ ScDocShell* pDocSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current() );
+ if (pDocSh)
+ pDocSh->PostPaintGridAll();
+
+ return false;
+}
+
+void ScRedlineOptionsTabPage::Reset( const SfxItemSet* /* rSet */ )
+{
+ ScAppOptions aAppOptions=SC_MOD()->GetAppOptions();
+
+ Color nColor = aAppOptions.GetTrackContentColor();
+ m_xContentColorLB->SelectEntry(nColor);
+
+ nColor = aAppOptions.GetTrackMoveColor();
+ m_xMoveColorLB->SelectEntry(nColor);
+
+ nColor = aAppOptions.GetTrackInsertColor();
+ m_xInsertColorLB->SelectEntry(nColor);
+
+ nColor = aAppOptions.GetTrackDeleteColor();
+ m_xRemoveColorLB->SelectEntry(nColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpcalc.cxx b/sc/source/ui/optdlg/tpcalc.cxx
new file mode 100644
index 0000000000..356fcd9bd1
--- /dev/null
+++ b/sc/source/ui/optdlg/tpcalc.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 .
+ */
+
+ #undef SC_DLLIMPLEMENTATION
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/numformat.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docoptio.hxx>
+#include <sc.hrc>
+#include <officecfg/Office/Calc.hxx>
+#include <svtools/restartdialog.hxx>
+
+#include <tpcalc.hxx>
+
+ScTpCalcOptions::ScTpCalcOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optcalculatepage.ui", "OptCalculatePage", &rCoreAttrs)
+ , pOldOptions(new ScDocOptions(
+ rCoreAttrs.Get(SID_SCDOCOPTIONS).GetDocOptions()))
+ , pLocalOptions(new ScDocOptions)
+ , m_xBtnIterate(m_xBuilder->weld_check_button("iterate"))
+ , m_xFtSteps(m_xBuilder->weld_label("stepsft"))
+ , m_xEdSteps(m_xBuilder->weld_spin_button("steps"))
+ , m_xFtEps(m_xBuilder->weld_label("minchangeft"))
+ , m_xEdEps(new ScDoubleField(m_xBuilder->weld_entry("minchange")))
+ , m_xBtnDateStd(m_xBuilder->weld_radio_button("datestd"))
+ , m_xBtnDateSc10(m_xBuilder->weld_radio_button("datesc10"))
+ , m_xBtnDate1904(m_xBuilder->weld_radio_button("date1904"))
+ , m_xBtnCase(m_xBuilder->weld_check_button("case"))
+ , m_xBtnCalc(m_xBuilder->weld_check_button("calc"))
+ , m_xBtnMatch(m_xBuilder->weld_check_button("match"))
+ , m_xBtnWildcards(m_xBuilder->weld_radio_button("formulawildcards"))
+ , m_xBtnRegex(m_xBuilder->weld_radio_button("formularegex"))
+ , m_xBtnLiteral(m_xBuilder->weld_radio_button("formulaliteral"))
+ , m_xBtnLookUp(m_xBuilder->weld_check_button("lookup"))
+ , m_xBtnGeneralPrec(m_xBuilder->weld_check_button("generalprec"))
+ , m_xFtPrec(m_xBuilder->weld_label("precft"))
+ , m_xEdPrec(m_xBuilder->weld_spin_button("prec"))
+ , m_xBtnThread(m_xBuilder->weld_check_button("threadingenabled"))
+{
+ Init();
+ SetExchangeSupport();
+}
+
+ScTpCalcOptions::~ScTpCalcOptions()
+{
+}
+
+void ScTpCalcOptions::Init()
+{
+ m_xBtnIterate->connect_toggled( LINK( this, ScTpCalcOptions, CheckClickHdl ) );
+ m_xBtnGeneralPrec->connect_toggled( LINK(this, ScTpCalcOptions, CheckClickHdl) );
+ m_xBtnDateStd->connect_toggled( LINK( this, ScTpCalcOptions, RadioClickHdl ) );
+ m_xBtnDateSc10->connect_toggled( LINK( this, ScTpCalcOptions, RadioClickHdl ) );
+ m_xBtnDate1904->connect_toggled( LINK( this, ScTpCalcOptions, RadioClickHdl ) );
+ m_xBtnThread->connect_toggled( LINK( this, ScTpCalcOptions, CheckClickHdl ) );
+}
+
+std::unique_ptr<SfxTabPage> ScTpCalcOptions::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet )
+{
+ return std::make_unique<ScTpCalcOptions>( pPage, pController, *rAttrSet );
+}
+
+void ScTpCalcOptions::Reset(const SfxItemSet* rCoreAttrs)
+{
+ sal_uInt16 d,m;
+ sal_Int16 y;
+
+ pOldOptions.reset(new ScDocOptions(
+ rCoreAttrs->Get(SID_SCDOCOPTIONS).GetDocOptions()));
+
+ *pLocalOptions = *pOldOptions;
+
+ m_xBtnCase->set_active( !pLocalOptions->IsIgnoreCase() );
+ m_xBtnCase->set_sensitive( !officecfg::Office::Calc::Calculate::Other::CaseSensitive::isReadOnly() );
+ m_xBtnCalc->set_active( pLocalOptions->IsCalcAsShown() );
+ m_xBtnCalc->set_sensitive( !officecfg::Office::Calc::Calculate::Other::Precision::isReadOnly() );
+ m_xBtnMatch->set_active( pLocalOptions->IsMatchWholeCell() );
+ m_xBtnMatch->set_sensitive( !officecfg::Office::Calc::Calculate::Other::SearchCriteria::isReadOnly() );
+ bool bWildcards = pLocalOptions->IsFormulaWildcardsEnabled();
+ bool bRegex = pLocalOptions->IsFormulaRegexEnabled();
+ // If both, Wildcards and Regex, are set then Wildcards shall take
+ // precedence. This is also how other code calling Search handles it. Both
+ // simultaneously couldn't be set using UI but editing the configuration.
+ if (bWildcards && bRegex)
+ bRegex = false;
+ m_xBtnWildcards->set_active( bWildcards );
+ m_xBtnRegex->set_active( bRegex );
+ m_xBtnWildcards->set_sensitive( !officecfg::Office::Calc::Calculate::Other::Wildcards::isReadOnly() );
+ m_xBtnRegex->set_sensitive( !officecfg::Office::Calc::Calculate::Other::RegularExpressions::isReadOnly() );
+ m_xBtnLiteral->set_active( !bWildcards && !bRegex );
+ m_xBtnLiteral->set_sensitive( m_xBtnWildcards->get_sensitive() || m_xBtnRegex->get_sensitive() );
+ // if either regex or wildcards radio button is set and read-only, disable all three
+ if ( (!m_xBtnWildcards->get_sensitive() && bWildcards) || (!m_xBtnRegex->get_sensitive() && bRegex) )
+ {
+ m_xBtnWildcards->set_sensitive( false );
+ m_xBtnRegex->set_sensitive( false );
+ m_xBtnLiteral->set_sensitive( false );
+ }
+ m_xBtnLookUp->set_active( pLocalOptions->IsLookUpColRowNames() );
+ m_xBtnLookUp->set_sensitive( !officecfg::Office::Calc::Calculate::Other::FindLabel::isReadOnly() );
+ m_xBtnIterate->set_active( pLocalOptions->IsIter() );
+ m_xEdSteps->set_value( pLocalOptions->GetIterCount() );
+ m_xEdEps->SetValue( pLocalOptions->GetIterEps(), 6 );
+
+ pLocalOptions->GetDate( d, m, y );
+
+ switch ( y )
+ {
+ case 1899:
+ m_xBtnDateStd->set_active(true);
+ break;
+ case 1900:
+ m_xBtnDateSc10->set_active(true);
+ break;
+ case 1904:
+ m_xBtnDate1904->set_active(true);
+ break;
+ }
+
+ sal_uInt16 nPrec = pLocalOptions->GetStdPrecision();
+ if (nPrec == SvNumberFormatter::UNLIMITED_PRECISION)
+ {
+ m_xFtPrec->set_sensitive(false);
+ m_xEdPrec->set_sensitive(false);
+ m_xBtnGeneralPrec->set_active(false);
+ m_xEdPrec->set_value(0);
+ }
+ else
+ {
+ m_xBtnGeneralPrec->set_active(true);
+ m_xFtPrec->set_sensitive(true);
+ m_xEdPrec->set_sensitive(true);
+ m_xEdPrec->set_value(nPrec);
+ }
+
+ m_xBtnThread->set_sensitive( !officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::isReadOnly() );
+ m_xBtnThread->set_active( officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get() );
+
+ CheckClickHdl(*m_xBtnIterate);
+}
+
+OUString ScTpCalcOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[]
+ = { "label5", "label1", "precft", "label2", "stepsft", "minchangeft", "label4", "label3" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString checkButton[]
+ = { "case", "calc", "match", "lookup", "generalprec", "iterate", "threadingenabled" };
+
+ for (const auto& check : checkButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_check_button(check))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString radioButton[] = { "formulawildcards", "formularegex", "formulaliteral",
+ "datestd", "datesc10", "date1904" };
+
+ for (const auto& radio : radioButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_radio_button(radio))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpCalcOptions::FillItemSet( SfxItemSet* rCoreAttrs )
+{
+ // every other options are updated in handlers
+ pLocalOptions->SetIterCount( static_cast<sal_uInt16>(m_xEdSteps->get_value()) );
+ pLocalOptions->SetIgnoreCase( !m_xBtnCase->get_active() );
+ pLocalOptions->SetCalcAsShown( m_xBtnCalc->get_active() );
+ pLocalOptions->SetMatchWholeCell( m_xBtnMatch->get_active() );
+ pLocalOptions->SetFormulaWildcardsEnabled( m_xBtnWildcards->get_active() );
+ pLocalOptions->SetFormulaRegexEnabled( m_xBtnRegex->get_active() );
+ pLocalOptions->SetLookUpColRowNames( m_xBtnLookUp->get_active() );
+
+ if (m_xBtnGeneralPrec->get_active())
+ pLocalOptions->SetStdPrecision(
+ static_cast<sal_uInt16>(m_xEdPrec->get_value()) );
+ else
+ pLocalOptions->SetStdPrecision( SvNumberFormatter::UNLIMITED_PRECISION );
+
+ bool bShouldEnableThreading = m_xBtnThread->get_active();
+ if (bShouldEnableThreading != officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::get())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> xBatch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::set(bShouldEnableThreading, xBatch);
+ xBatch->commit();
+ SolarMutexGuard aGuard;
+ if (svtools::executeRestartDialog(
+ comphelper::getProcessComponentContext(), GetFrameWeld(),
+ svtools::RESTART_REASON_THREADING))
+ GetDialogController()->response(RET_OK);
+ }
+ if ( *pLocalOptions != *pOldOptions )
+ {
+ rCoreAttrs->Put( ScTpCalcItem( SID_SCDOCOPTIONS, *pLocalOptions ) );
+ return true;
+ }
+ else
+ return false;
+}
+
+DeactivateRC ScTpCalcOptions::DeactivatePage( SfxItemSet* pSetP )
+{
+ DeactivateRC nReturn = DeactivateRC::KeepPage;
+
+ double fEps;
+ if( m_xEdEps->GetValue( fEps ) && (fEps > 0.0) )
+ {
+ pLocalOptions->SetIterEps( fEps );
+ nReturn = DeactivateRC::LeavePage;
+ }
+
+ if ( nReturn == DeactivateRC::KeepPage )
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(), VclMessageType::Warning,
+ VclButtonsType::Ok, ScResId(STR_INVALID_EPS)));
+ xBox->run();
+
+ m_xEdEps->grab_focus();
+ }
+ else if ( pSetP )
+ FillItemSet( pSetP );
+
+ return nReturn;
+}
+
+// Handler:
+
+IMPL_LINK( ScTpCalcOptions, RadioClickHdl, weld::Toggleable&, rBtn, void )
+{
+ if (!rBtn.get_active())
+ return;
+ if (m_xBtnDateStd->get_active())
+ {
+ pLocalOptions->SetDate( 30, 12, 1899 );
+ }
+ else if (m_xBtnDateSc10->get_active())
+ {
+ pLocalOptions->SetDate( 1, 1, 1900 );
+ }
+ else if (m_xBtnDate1904->get_active())
+ {
+ pLocalOptions->SetDate( 1, 1, 1904 );
+ }
+}
+
+IMPL_LINK(ScTpCalcOptions, CheckClickHdl, weld::Toggleable&, rBtn, void)
+{
+ if (&rBtn == m_xBtnGeneralPrec.get())
+ {
+ if (rBtn.get_active())
+ {
+ m_xEdPrec->set_sensitive(true);
+ m_xFtPrec->set_sensitive(true);
+ }
+ else
+ {
+ m_xEdPrec->set_sensitive(false);
+ m_xFtPrec->set_sensitive(false);
+ }
+ }
+ else if (&rBtn == m_xBtnIterate.get())
+ {
+ if (rBtn.get_active())
+ {
+ pLocalOptions->SetIter( true );
+ m_xFtSteps->set_sensitive(true); m_xEdSteps->set_sensitive(true);
+ m_xFtEps->set_sensitive(true); m_xEdEps->set_sensitive(true);
+ }
+ else
+ {
+ pLocalOptions->SetIter( false );
+ m_xFtSteps->set_sensitive(false); m_xEdSteps->set_sensitive(false);
+ m_xFtEps->set_sensitive(false); m_xEdEps->set_sensitive(false);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpcompatibility.cxx b/sc/source/ui/optdlg/tpcompatibility.cxx
new file mode 100644
index 0000000000..cc54635f33
--- /dev/null
+++ b/sc/source/ui/optdlg/tpcompatibility.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/.
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+
+#include <tpcompatibility.hxx>
+#include <sc.hrc>
+#include <optutil.hxx>
+
+ScTpCompatOptions::ScTpCompatOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rCoreAttrs)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optcompatibilitypage.ui", "OptCompatibilityPage", &rCoreAttrs)
+ , m_xLbKeyBindings(m_xBuilder->weld_combo_box("keybindings"))
+ , m_xBtnLink(m_xBuilder->weld_check_button("cellLinkCB"))
+{
+}
+
+ScTpCompatOptions::~ScTpCompatOptions()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTpCompatOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rCoreAttrs)
+{
+ return std::make_unique<ScTpCompatOptions>(pPage, pController, *rCoreAttrs);
+}
+
+OUString ScTpCompatOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label1", "label2" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ // id "keybindings" GtkComboBoxText is not included
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpCompatOptions::FillItemSet(SfxItemSet *rCoreAttrs)
+{
+ bool bRet = false;
+ if (m_xLbKeyBindings->get_value_changed_from_saved())
+ {
+ rCoreAttrs->Put(
+ SfxUInt16Item(
+ SID_SC_OPT_KEY_BINDING_COMPAT, m_xLbKeyBindings->get_active()));
+ bRet = true;
+ }
+ if (m_xBtnLink->get_state_changed_from_saved())
+ {
+ rCoreAttrs->Put(SfxBoolItem(SID_SC_OPT_LINKS, m_xBtnLink->get_active()));
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void ScTpCompatOptions::Reset(const SfxItemSet *rCoreAttrs)
+{
+ if (const SfxUInt16Item* p16Item = rCoreAttrs->GetItemIfSet(SID_SC_OPT_KEY_BINDING_COMPAT))
+ {
+ ScOptionsUtil::KeyBindingType eKeyB =
+ static_cast<ScOptionsUtil::KeyBindingType>(p16Item->GetValue());
+
+ switch (eKeyB)
+ {
+ case ScOptionsUtil::KEY_DEFAULT:
+ m_xLbKeyBindings->set_active(0);
+ break;
+ case ScOptionsUtil::KEY_OOO_LEGACY:
+ m_xLbKeyBindings->set_active(1);
+ break;
+ default:
+ ;
+ }
+ }
+ m_xLbKeyBindings->save_value();
+
+ if (const SfxBoolItem* pbItem = rCoreAttrs->GetItemIfSet(SID_SC_OPT_LINKS))
+ {
+ m_xBtnLink->set_active(pbItem->GetValue());
+ }
+ m_xBtnLink->save_state();
+}
+
+DeactivateRC ScTpCompatOptions::DeactivatePage(SfxItemSet* /*pSet*/)
+{
+ return DeactivateRC::KeepPage;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpdefaults.cxx b/sc/source/ui/optdlg/tpdefaults.cxx
new file mode 100644
index 0000000000..4528da5e48
--- /dev/null
+++ b/sc/source/ui/optdlg/tpdefaults.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/.
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <tpdefaults.hxx>
+#include <sc.hrc>
+#include <defaultsoptions.hxx>
+#include <document.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include <config_features.h>
+
+ScTpDefaultsOptions::ScTpDefaultsOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rCoreSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optdefaultpage.ui", "OptDefaultPage", &rCoreSet)
+ , m_xEdNSheets(m_xBuilder->weld_spin_button("sheetsnumber"))
+ , m_xEdNSheetsImg(m_xBuilder->weld_widget("locksheetsnumber"))
+ , m_xEdSheetPrefix(m_xBuilder->weld_entry("sheetprefix"))
+ , m_xEdSheetPrefixImg(m_xBuilder->weld_widget("locksheetprefix"))
+ , m_xEdJumboSheets(m_xBuilder->weld_check_button("jumbo_sheets"))
+ , m_xEdJumboSheetsImg(m_xBuilder->weld_widget("lockjumbo_sheets"))
+{
+ m_xEdNSheets->connect_changed( LINK(this, ScTpDefaultsOptions, NumModifiedHdl) );
+ m_xEdSheetPrefix->connect_changed( LINK(this, ScTpDefaultsOptions, PrefixModifiedHdl) );
+ m_xEdSheetPrefix->connect_focus_in( LINK(this, ScTpDefaultsOptions, PrefixEditOnFocusHdl) );
+#if HAVE_FEATURE_JUMBO_SHEETS
+ if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
+#endif
+ m_xEdJumboSheets->hide();
+}
+
+ScTpDefaultsOptions::~ScTpDefaultsOptions()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTpDefaultsOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rCoreAttrs)
+{
+ return std::make_unique<ScTpDefaultsOptions>(pPage, pController, *rCoreAttrs);
+}
+
+OUString ScTpDefaultsOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label1", "textsheetsnumber", "textsheetprefix" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ sAllStrings += m_xEdJumboSheets->get_label() + " ";
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpDefaultsOptions::FillItemSet(SfxItemSet *rCoreSet)
+{
+ bool bRet = false;
+ ScDefaultsOptions aOpt;
+
+ SCTAB nTabCount = static_cast<SCTAB>(m_xEdNSheets->get_value());
+ OUString aSheetPrefix = m_xEdSheetPrefix->get_text();
+ bool bJumboSheets = m_xEdJumboSheets->get_state();
+
+ if ( m_xEdNSheets->get_value_changed_from_saved()
+ || m_xEdSheetPrefix->get_saved_value() != aSheetPrefix
+ || m_xEdJumboSheets->get_saved_state() != (bJumboSheets ? TRISTATE_TRUE : TRISTATE_FALSE) )
+ {
+ aOpt.SetInitTabCount( nTabCount );
+ aOpt.SetInitTabPrefix( aSheetPrefix );
+#if HAVE_FEATURE_JUMBO_SHEETS
+ aOpt.SetInitJumboSheets( bJumboSheets );
+#endif
+ rCoreSet->Put( ScTpDefaultsItem( std::move(aOpt) ) );
+ bRet = true;
+ }
+ return bRet;
+}
+
+void ScTpDefaultsOptions::Reset(const SfxItemSet* rCoreSet)
+{
+ ScDefaultsOptions aOpt;
+
+ if(const ScTpDefaultsItem* pDefaultsItem = rCoreSet->GetItemIfSet(SID_SCDEFAULTSOPTIONS, false))
+ aOpt = pDefaultsItem->GetDefaultsOptions();
+
+ m_xEdNSheets->set_value(aOpt.GetInitTabCount());
+ m_xEdSheetPrefix->set_text( aOpt.GetInitTabPrefix() );
+ m_xEdJumboSheets->set_state( aOpt.GetInitJumboSheets() ? TRISTATE_TRUE : TRISTATE_FALSE );
+
+ bool bReadOnly = officecfg::Office::Calc::Defaults::Sheet::SheetCount::isReadOnly();
+ m_xEdNSheets->set_sensitive(!bReadOnly);
+ m_xEdNSheetsImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Defaults::Sheet::SheetPrefix::isReadOnly();
+ m_xEdSheetPrefix->set_sensitive(!bReadOnly);
+ m_xEdSheetPrefixImg->set_visible(bReadOnly);
+
+ if (m_xEdJumboSheets->is_visible())
+ {
+ bReadOnly = officecfg::Office::Calc::Defaults::Sheet::JumboSheets::isReadOnly();
+ m_xEdJumboSheets->set_sensitive(!bReadOnly);
+ m_xEdJumboSheetsImg->set_visible(bReadOnly);
+ }
+
+ m_xEdNSheets->save_value();
+ m_xEdSheetPrefix->save_value();
+ m_xEdJumboSheets->save_state();
+}
+
+DeactivateRC ScTpDefaultsOptions::DeactivatePage(SfxItemSet* /*pSet*/)
+{
+ return DeactivateRC::KeepPage;
+}
+
+void ScTpDefaultsOptions::CheckNumSheets()
+{
+ auto nVal = m_xEdNSheets->get_value();
+ if (nVal > MAXINITTAB)
+ m_xEdNSheets->set_value(MAXINITTAB);
+ if (nVal < MININITTAB)
+ m_xEdNSheets->set_value(MININITTAB);
+}
+
+void ScTpDefaultsOptions::CheckPrefix()
+{
+ OUString aSheetPrefix = m_xEdSheetPrefix->get_text();
+
+ if (!aSheetPrefix.isEmpty() && !ScDocument::ValidTabName(aSheetPrefix))
+ {
+ // Revert to last good Prefix and also select it to
+ // indicate something illegal was typed
+ m_xEdSheetPrefix->set_text(maOldPrefixValue);
+ m_xEdSheetPrefix->select_region(0, -1);
+ }
+ else
+ {
+ OnFocusPrefixInput();
+ }
+}
+
+void ScTpDefaultsOptions::OnFocusPrefixInput()
+{
+ // Store Prefix in case we need to revert
+ maOldPrefixValue = m_xEdSheetPrefix->get_text();
+}
+
+IMPL_LINK_NOARG(ScTpDefaultsOptions, NumModifiedHdl, weld::Entry&, void)
+{
+ CheckNumSheets();
+}
+
+IMPL_LINK_NOARG(ScTpDefaultsOptions, PrefixModifiedHdl, weld::Entry&, void)
+{
+ CheckPrefix();
+}
+
+IMPL_LINK_NOARG(ScTpDefaultsOptions, PrefixEditOnFocusHdl, weld::Widget&, void)
+{
+ OnFocusPrefixInput();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpformula.cxx b/sc/source/ui/optdlg/tpformula.cxx
new file mode 100644
index 0000000000..ab8cc38c12
--- /dev/null
+++ b/sc/source/ui/optdlg/tpformula.cxx
@@ -0,0 +1,448 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <global.hxx>
+#include <tpformula.hxx>
+#include <formulaopt.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <scresid.hxx>
+#include <formula/grammar.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include "calcoptionsdlg.hxx"
+
+#include <unotools/localedatawrapper.hxx>
+
+ScTpFormulaOptions::ScTpFormulaOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optformula.ui", "OptFormula", &rCoreAttrs)
+ , mnDecSep(0)
+ , mxLbFormulaSyntax(m_xBuilder->weld_combo_box("formulasyntax"))
+ , mxCbEnglishFuncName(m_xBuilder->weld_check_button("englishfuncname"))
+ , mxBtnCustomCalcDefault(m_xBuilder->weld_radio_button("calcdefault"))
+ , mxBtnCustomCalcCustom(m_xBuilder->weld_radio_button("calccustom"))
+ , mxBtnCustomCalcDetails(m_xBuilder->weld_button("details"))
+ , mxEdSepFuncArg(m_xBuilder->weld_entry("function"))
+ , mxEdSepArrayCol(m_xBuilder->weld_entry("arraycolumn"))
+ , mxEdSepArrayRow(m_xBuilder->weld_entry("arrayrow"))
+ , mxBtnSepReset(m_xBuilder->weld_button("reset"))
+ , mxLbOOXMLRecalcOptions(m_xBuilder->weld_combo_box("ooxmlrecalc"))
+ , mxLbODFRecalcOptions(m_xBuilder->weld_combo_box("odfrecalc"))
+{
+ mxLbFormulaSyntax->append_text(ScResId(SCSTR_FORMULA_SYNTAX_CALC_A1));
+ mxLbFormulaSyntax->append_text(ScResId(SCSTR_FORMULA_SYNTAX_XL_A1));
+ mxLbFormulaSyntax->append_text(ScResId(SCSTR_FORMULA_SYNTAX_XL_R1C1));
+
+ Link<weld::Button&,void> aLink2 = LINK( this, ScTpFormulaOptions, ButtonHdl );
+ mxBtnSepReset->connect_clicked(aLink2);
+ mxBtnCustomCalcDetails->connect_clicked(aLink2);
+
+ Link<weld::Toggleable&,void> aToggleLink = LINK( this, ScTpFormulaOptions, ToggleHdl );
+ mxBtnCustomCalcDefault->connect_toggled(aToggleLink);
+ mxBtnCustomCalcCustom->connect_toggled(aToggleLink);
+
+ mxEdSepFuncArg->connect_insert_text(LINK( this, ScTpFormulaOptions, SepInsertTextHdl ));
+ mxEdSepArrayCol->connect_insert_text(LINK( this, ScTpFormulaOptions, ColSepInsertTextHdl ));
+ mxEdSepArrayRow->connect_insert_text(LINK( this, ScTpFormulaOptions, RowSepInsertTextHdl ));
+
+ Link<weld::Entry&,void> aLink = LINK( this, ScTpFormulaOptions, SepModifyHdl );
+ mxEdSepFuncArg->connect_changed(aLink);
+ mxEdSepArrayCol->connect_changed(aLink);
+ mxEdSepArrayRow->connect_changed(aLink);
+
+ Link<weld::Widget&,void> aLink3 = LINK( this, ScTpFormulaOptions, SepEditOnFocusHdl );
+ mxEdSepFuncArg->connect_focus_in(aLink3);
+ mxEdSepArrayCol->connect_focus_in(aLink3);
+ mxEdSepArrayRow->connect_focus_in(aLink3);
+
+ // Get the decimal separator for current locale.
+ OUString aSep = ScGlobal::getLocaleData().getNumDecimalSep();
+ mnDecSep = aSep.isEmpty() ? u'.' : aSep[0];
+
+ maSavedDocOptions = rCoreAttrs.Get(SID_SCDOCOPTIONS).GetDocOptions();
+}
+
+ScTpFormulaOptions::~ScTpFormulaOptions()
+{
+}
+
+void ScTpFormulaOptions::ResetSeparators()
+{
+ OUString aFuncArg, aArrayCol, aArrayRow;
+ ScFormulaOptions::GetDefaultFormulaSeparators(aFuncArg, aArrayCol, aArrayRow);
+ mxEdSepFuncArg->set_text(aFuncArg);
+ mxEdSepArrayCol->set_text(aArrayCol);
+ mxEdSepArrayRow->set_text(aArrayRow);
+}
+
+void ScTpFormulaOptions::OnFocusSeparatorInput(weld::Entry* pEdit)
+{
+ if (!pEdit)
+ return;
+
+ // Make sure the entire text is selected.
+ pEdit->select_region(0, -1);
+ OUString sSepValue = pEdit->get_text();
+ if (!sSepValue.isEmpty())
+ maOldSepValue = sSepValue;
+}
+
+void ScTpFormulaOptions::UpdateCustomCalcRadioButtons(bool bDefault)
+{
+ if (bDefault)
+ {
+ mxBtnCustomCalcDefault->set_active(true);
+ mxBtnCustomCalcCustom->set_active(false);
+ mxBtnCustomCalcDetails->set_sensitive(false);
+ }
+ else
+ {
+ mxBtnCustomCalcDefault->set_active(false);
+ mxBtnCustomCalcCustom->set_active(true);
+ mxBtnCustomCalcDetails->set_sensitive(true);
+ }
+}
+
+void ScTpFormulaOptions::LaunchCustomCalcSettings()
+{
+ ScCalcOptionsDialog aDlg(GetFrameWeld(), maCurrentConfig, maCurrentDocOptions.IsWriteCalcConfig());
+ if (aDlg.run() == RET_OK)
+ {
+ maCurrentConfig = aDlg.GetConfig();
+ maCurrentDocOptions.SetWriteCalcConfig(aDlg.GetWriteCalcConfig());
+ }
+}
+
+bool ScTpFormulaOptions::IsValidSeparator(std::u16string_view aSep, bool bArray) const
+{
+ if (aSep.size() != 1)
+ // Must be one-character long.
+ return false;
+
+ const sal_Unicode c = aSep[0];
+
+ if (c == mnDecSep)
+ // decimal separator is not allowed.
+ return false;
+
+ if (c <= 0x20 || c == 0x7f)
+ // Disallow non-printables including space and DEL.
+ return false;
+
+ if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'))
+ // Disallow alphanumeric.
+ return false;
+
+ if (bArray)
+ {
+ switch (c)
+ {
+ case '+':
+ case '-':
+ case '{':
+ case '}':
+ case '"':
+ // All following just to prevent confusion, they are not
+ // evaluated in inline arrays and theoretically would be
+ // possible.
+ case '%':
+ case '/':
+ case '*':
+ case '=':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '(':
+ case ')':
+ case '\'':
+ // Disallowed characters. Anything else we want to disallow ?
+ return false;
+ }
+ }
+ else if (c <= 0x7f)
+ {
+ switch (c)
+ {
+ default:
+ // Anything bad except the knowns.
+ return false;
+ case ';':
+ case ',':
+ ; // nothing
+ }
+ }
+ else
+ {
+ // Any Unicode character, would have to ask the compiler's localized
+ // symbol map whether it's a known symbol but not a separator
+ // (ocSep,ocArrayRowSep,ocArrayColSep), which we're about to set here.
+ // But really..
+ return false;
+ }
+
+ return true;
+}
+
+IMPL_LINK( ScTpFormulaOptions, ButtonHdl, weld::Button&, rBtn, void )
+{
+ if (&rBtn == mxBtnSepReset.get())
+ ResetSeparators();
+ else if (&rBtn == mxBtnCustomCalcDetails.get())
+ LaunchCustomCalcSettings();
+}
+
+IMPL_LINK( ScTpFormulaOptions, ToggleHdl, weld::Toggleable&, rBtn, void )
+{
+ if (!rBtn.get_active())
+ return;
+ if (mxBtnCustomCalcDefault->get_active())
+ UpdateCustomCalcRadioButtons(true);
+ else if (mxBtnCustomCalcCustom->get_active())
+ UpdateCustomCalcRadioButtons(false);
+}
+
+IMPL_LINK(ScTpFormulaOptions, SepInsertTextHdl, OUString&, rTest, bool)
+{
+ if (!IsValidSeparator(rTest, false) && !maOldSepValue.isEmpty())
+ // Invalid separator. Restore the old value.
+ rTest = maOldSepValue;
+ return true;
+}
+
+IMPL_LINK(ScTpFormulaOptions, RowSepInsertTextHdl, OUString&, rTest, bool)
+{
+ // Invalid separator or same as ColStr - Restore the old value.
+ if ((!IsValidSeparator(rTest, true) || rTest == mxEdSepArrayCol->get_text()) && !maOldSepValue.isEmpty())
+ rTest = maOldSepValue;
+ return true;
+}
+
+IMPL_LINK(ScTpFormulaOptions, ColSepInsertTextHdl, OUString&, rTest, bool)
+{
+ // Invalid separator or same as RowStr - Restore the old value.
+ if ((!IsValidSeparator(rTest, true) || rTest == mxEdSepArrayRow->get_text()) && !maOldSepValue.isEmpty())
+ rTest = maOldSepValue;
+ return true;
+}
+
+IMPL_LINK( ScTpFormulaOptions, SepModifyHdl, weld::Entry&, rEdit, void )
+{
+ OnFocusSeparatorInput(&rEdit);
+}
+
+IMPL_LINK( ScTpFormulaOptions, SepEditOnFocusHdl, weld::Widget&, rControl, void )
+{
+ OnFocusSeparatorInput(dynamic_cast<weld::Entry*>(&rControl));
+}
+
+std::unique_ptr<SfxTabPage> ScTpFormulaOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet)
+{
+ return std::make_unique<ScTpFormulaOptions>(pPage, pController, *rCoreSet);
+}
+
+OUString ScTpFormulaOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label1", "formulasyntaxlabel",
+ "label3", "label6",
+ "label7", "label8",
+ "label2", "label4",
+ "label9", "label10" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString radioButton[] = { "calcdefault", "calccustom" };
+
+ for (const auto& radio : radioButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_radio_button(radio))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString buttons[] = { "reset", "details" };
+
+ for (const auto& btn : buttons)
+ {
+ if (const auto& pString = m_xBuilder->weld_button(btn))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ // check button
+ sAllStrings += mxCbEnglishFuncName->get_label() + " ";
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpFormulaOptions::FillItemSet(SfxItemSet* rCoreSet)
+{
+ bool bRet = false;
+ ScFormulaOptions aOpt;
+ bool bEnglishFuncName = mxCbEnglishFuncName->get_active();
+ sal_Int16 aSyntaxPos = mxLbFormulaSyntax->get_active();
+ OUString aSep = mxEdSepFuncArg->get_text();
+ OUString aSepArrayCol = mxEdSepArrayCol->get_text();
+ OUString aSepArrayRow = mxEdSepArrayRow->get_text();
+ sal_Int16 nOOXMLRecalcMode = mxLbOOXMLRecalcOptions->get_active();
+ sal_Int16 nODFRecalcMode = mxLbODFRecalcOptions->get_active();
+
+ if (mxBtnCustomCalcDefault->get_active())
+ {
+ // When Default is selected, reset all the calc config settings to default.
+ maCurrentConfig.reset();
+ }
+
+ if ( mxLbFormulaSyntax->get_saved_value() != mxLbFormulaSyntax->get_text(aSyntaxPos)
+ || mxCbEnglishFuncName->get_saved_state() != (bEnglishFuncName ? 1 : 0)
+ || mxEdSepFuncArg->get_saved_value() != aSep
+ || mxEdSepArrayCol->get_saved_value() != aSepArrayCol
+ || mxEdSepArrayRow->get_saved_value() != aSepArrayRow
+ || mxLbOOXMLRecalcOptions->get_saved_value() != mxLbOOXMLRecalcOptions->get_text(nOOXMLRecalcMode)
+ || mxLbODFRecalcOptions->get_saved_value() != mxLbODFRecalcOptions->get_text(nODFRecalcMode)
+ || maSavedConfig != maCurrentConfig
+ || maSavedDocOptions != maCurrentDocOptions )
+ {
+ ::formula::FormulaGrammar::Grammar eGram = ::formula::FormulaGrammar::GRAM_DEFAULT;
+
+ switch (aSyntaxPos)
+ {
+ case 0:
+ eGram = ::formula::FormulaGrammar::GRAM_NATIVE;
+ break;
+ case 1:
+ eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1;
+ break;
+ case 2:
+ eGram = ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1;
+ break;
+ }
+
+ ScRecalcOptions eOOXMLRecalc = static_cast<ScRecalcOptions>(nOOXMLRecalcMode);
+ ScRecalcOptions eODFRecalc = static_cast<ScRecalcOptions>(nODFRecalcMode);
+
+ aOpt.SetFormulaSyntax(eGram);
+ aOpt.SetUseEnglishFuncName(bEnglishFuncName);
+ aOpt.SetFormulaSepArg(aSep);
+ aOpt.SetFormulaSepArrayCol(aSepArrayCol);
+ aOpt.SetFormulaSepArrayRow(aSepArrayRow);
+ aOpt.SetCalcConfig(maCurrentConfig);
+ aOpt.SetOOXMLRecalcOptions(eOOXMLRecalc);
+ aOpt.SetODFRecalcOptions(eODFRecalc);
+ aOpt.SetWriteCalcConfig( maCurrentDocOptions.IsWriteCalcConfig());
+
+ rCoreSet->Put( ScTpFormulaItem( std::move(aOpt) ) );
+ rCoreSet->Put( ScTpCalcItem( SID_SCDOCOPTIONS, maCurrentDocOptions ) );
+
+ bRet = true;
+ }
+ return bRet;
+}
+
+void ScTpFormulaOptions::Reset(const SfxItemSet* rCoreSet)
+{
+ ScFormulaOptions aOpt;
+ if(const ScTpFormulaItem* pItem = rCoreSet->GetItemIfSet(SID_SCFORMULAOPTIONS, false))
+ aOpt = pItem->GetFormulaOptions();
+
+ // formula grammar.
+ ::formula::FormulaGrammar::Grammar eGram = aOpt.GetFormulaSyntax();
+
+ switch (eGram)
+ {
+ case ::formula::FormulaGrammar::GRAM_NATIVE:
+ mxLbFormulaSyntax->set_active(0);
+ break;
+ case ::formula::FormulaGrammar::GRAM_NATIVE_XL_A1:
+ mxLbFormulaSyntax->set_active(1);
+ break;
+ case ::formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1:
+ mxLbFormulaSyntax->set_active(2);
+ break;
+ default:
+ mxLbFormulaSyntax->set_active(0);
+ }
+
+ mxLbFormulaSyntax->save_value();
+ mxLbFormulaSyntax->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::Grammar::isReadOnly() );
+
+ ScRecalcOptions eOOXMLRecalc = aOpt.GetOOXMLRecalcOptions();
+ mxLbOOXMLRecalcOptions->set_active(static_cast<sal_uInt16>(eOOXMLRecalc));
+ mxLbOOXMLRecalcOptions->save_value();
+ mxLbOOXMLRecalcOptions->set_sensitive( !officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::isReadOnly() );
+
+ ScRecalcOptions eODFRecalc = aOpt.GetODFRecalcOptions();
+ mxLbODFRecalcOptions->set_active(static_cast<sal_uInt16>(eODFRecalc));
+ mxLbODFRecalcOptions->save_value();
+ mxLbODFRecalcOptions->set_sensitive( !officecfg::Office::Calc::Formula::Load::ODFRecalcMode::isReadOnly() );
+
+ // english function name.
+ mxCbEnglishFuncName->set_active( aOpt.GetUseEnglishFuncName() );
+ mxCbEnglishFuncName->save_state();
+ mxCbEnglishFuncName->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::EnglishFunctionName::isReadOnly() );
+
+ // Separators
+ OUString aSep = aOpt.GetFormulaSepArg();
+ OUString aSepArrayRow = aOpt.GetFormulaSepArrayRow();
+ OUString aSepArrayCol = aOpt.GetFormulaSepArrayCol();
+
+ if (IsValidSeparator(aSep, false) && IsValidSeparator(aSepArrayRow, true) && IsValidSeparator(aSepArrayCol, true))
+ {
+ // Each and all separators must be valid.
+ mxEdSepFuncArg->set_text(aSep);
+ mxEdSepArrayCol->set_text(aSepArrayCol);
+ mxEdSepArrayRow->set_text(aSepArrayRow);
+
+ mxEdSepFuncArg->save_value();
+ mxEdSepArrayCol->save_value();
+ mxEdSepArrayRow->save_value();
+ }
+ else
+ ResetSeparators();
+
+ mxEdSepFuncArg->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::SeparatorArg::isReadOnly() );
+ mxEdSepArrayCol->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayCol::isReadOnly() );
+ mxEdSepArrayRow->set_sensitive( !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayRow::isReadOnly() );
+ mxBtnSepReset->set_sensitive ( !officecfg::Office::Calc::Formula::Syntax::SeparatorArg::isReadOnly() &&
+ !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayCol::isReadOnly() &&
+ !officecfg::Office::Calc::Formula::Syntax::SeparatorArrayRow::isReadOnly() );
+
+ // detailed calc settings.
+ ScFormulaOptions aDefaults;
+
+ maSavedConfig = aOpt.GetCalcConfig();
+ bool bDefault = aDefaults.GetCalcConfig() == maSavedConfig;
+ UpdateCustomCalcRadioButtons(bDefault);
+
+ maCurrentConfig = maSavedConfig;
+
+ maCurrentDocOptions = maSavedDocOptions;
+}
+
+DeactivateRC ScTpFormulaOptions::DeactivatePage(SfxItemSet* /*pSet*/)
+{
+ // What's this method for ?
+ return DeactivateRC::KeepPage;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpprint.cxx b/sc/source/ui/optdlg/tpprint.cxx
new file mode 100644
index 0000000000..44324ffc2a
--- /dev/null
+++ b/sc/source/ui/optdlg/tpprint.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svl/eitem.hxx>
+
+#include <tpprint.hxx>
+#include <printopt.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+
+ScTpPrintOptions::ScTpPrintOptions( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rCoreAttrs )
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optdlg.ui", "optCalcPrintPage", &rCoreAttrs )
+ , m_xSkipEmptyPagesCB(m_xBuilder->weld_check_button("suppressCB"))
+ , m_xSelectedSheetsCB(m_xBuilder->weld_check_button("printCB"))
+ , m_xForceBreaksCB(m_xBuilder->weld_check_button("forceBreaksCB"))
+{
+}
+
+ScTpPrintOptions::~ScTpPrintOptions()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTpPrintOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet)
+{
+ return std::make_unique<ScTpPrintOptions>(pPage, pController, *rAttrSet);
+}
+
+DeactivateRC ScTpPrintOptions::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+void ScTpPrintOptions::Reset( const SfxItemSet* rCoreSet )
+{
+ ScPrintOptions aOptions;
+
+ if(const ScTpPrintItem* pItem = rCoreSet->GetItemIfSet(SID_SCPRINTOPTIONS, false))
+ aOptions = pItem->GetPrintOptions();
+ else
+ {
+ // when called from print dialog and no options set, use configuration
+ aOptions = SC_MOD()->GetPrintOptions();
+ }
+
+ if ( const SfxBoolItem* pItem = rCoreSet->GetItemIfSet( SID_PRINT_SELECTEDSHEET, false))
+ {
+ bool bChecked = pItem->GetValue();
+ m_xSelectedSheetsCB->set_active( bChecked );
+ }
+ else
+ {
+ m_xSelectedSheetsCB->set_active( !aOptions.GetAllSheets() );
+ }
+
+ m_xSkipEmptyPagesCB->set_active( aOptions.GetSkipEmpty() );
+ m_xSkipEmptyPagesCB->save_state();
+ m_xSelectedSheetsCB->save_state();
+ m_xForceBreaksCB->set_active( aOptions.GetForceBreaks() );
+ m_xForceBreaksCB->save_state();
+}
+
+OUString ScTpPrintOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label1", "label2" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString checkButton[] = { "suppressCB", "forceBreaksCB", "printCB" };
+
+ for (const auto& check : checkButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_check_button(check))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpPrintOptions::FillItemSet( SfxItemSet* rCoreAttrs )
+{
+ rCoreAttrs->ClearItem( SID_PRINT_SELECTEDSHEET );
+
+ bool bSkipEmptyChanged = m_xSkipEmptyPagesCB->get_state_changed_from_saved();
+ bool bSelectedSheetsChanged = m_xSelectedSheetsCB->get_state_changed_from_saved();
+ bool bForceBreaksChanged = m_xForceBreaksCB->get_state_changed_from_saved();
+
+ if ( bSkipEmptyChanged || bSelectedSheetsChanged || bForceBreaksChanged )
+ {
+ ScPrintOptions aOpt;
+ aOpt.SetSkipEmpty( m_xSkipEmptyPagesCB->get_active() );
+ aOpt.SetAllSheets( !m_xSelectedSheetsCB->get_active() );
+ aOpt.SetForceBreaks( m_xForceBreaksCB->get_active() );
+ rCoreAttrs->Put( ScTpPrintItem( aOpt ) );
+ if ( bSelectedSheetsChanged )
+ {
+ rCoreAttrs->Put( SfxBoolItem( SID_PRINT_SELECTEDSHEET, m_xSelectedSheetsCB->get_active() ) );
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpusrlst.cxx b/sc/source/ui/optdlg/tpusrlst.cxx
new file mode 100644
index 0000000000..128b8ea936
--- /dev/null
+++ b/sc/source/ui/optdlg/tpusrlst.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 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <comphelper/string.hxx>
+#include <tools/lineend.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <document.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <uiitems.hxx>
+#include <userlist.hxx>
+#include <rangeutl.hxx>
+#include <crdlg.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <tpusrlst.hxx>
+#include <scui_def.hxx>
+
+#define CR u'\x000D'
+#define LF u'\x000A'
+
+const sal_Unicode cDelimiter = ',';
+
+// Benutzerdefinierte Listen:
+
+ScTpUserLists::ScTpUserLists( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rCoreAttrs )
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/optsortlists.ui", "OptSortLists",
+ &rCoreAttrs )
+ , mxFtLists(m_xBuilder->weld_label("listslabel"))
+ , mxLbLists(m_xBuilder->weld_tree_view("lists"))
+ , mxFtEntries(m_xBuilder->weld_label("entrieslabel"))
+ , mxEdEntries(m_xBuilder->weld_text_view("entries"))
+ , mxFtCopyFrom(m_xBuilder->weld_label("copyfromlabel"))
+ , mxEdCopyFrom(m_xBuilder->weld_entry("copyfrom"))
+ , mxBtnNew(m_xBuilder->weld_button("new"))
+ , mxBtnDiscard(m_xBuilder->weld_button("discard"))
+ , mxBtnAdd(m_xBuilder->weld_button("add"))
+ , mxBtnModify(m_xBuilder->weld_button("modify"))
+ , mxBtnRemove(m_xBuilder->weld_button("delete"))
+ , mxBtnCopy(m_xBuilder->weld_button("copy"))
+ , aStrQueryRemove ( ScResId( STR_QUERYREMOVE ) )
+ , aStrCopyList ( ScResId( STR_COPYLIST ) )
+ , aStrCopyFrom ( ScResId( STR_COPYFROM ) )
+ , aStrCopyErr ( ScResId( STR_COPYERR ) )
+ , nWhichUserLists ( GetWhich( SID_SCUSERLISTS ) )
+ , pDoc ( nullptr )
+ , pViewData ( nullptr )
+ , bModifyMode ( false )
+ , bCancelMode ( false )
+ , bCopyDone ( false )
+ , nCancelPos ( 0 )
+{
+ SetExchangeSupport();
+ Init();
+ Reset(&rCoreAttrs);
+}
+
+ScTpUserLists::~ScTpUserLists()
+{
+}
+
+void ScTpUserLists::Init()
+{
+ SfxViewShell* pSh = SfxViewShell::Current();
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( pSh );
+
+ mxLbLists->connect_changed ( LINK( this, ScTpUserLists, LbSelectHdl ) );
+ mxBtnNew->connect_clicked ( LINK( this, ScTpUserLists, BtnClickHdl ) );
+ mxBtnDiscard->connect_clicked ( LINK( this, ScTpUserLists, BtnClickHdl ) );
+ mxBtnAdd->connect_clicked ( LINK( this, ScTpUserLists, BtnClickHdl ) );
+ mxBtnModify->connect_clicked ( LINK( this, ScTpUserLists, BtnClickHdl ) );
+ mxBtnRemove->connect_clicked ( LINK( this, ScTpUserLists, BtnClickHdl ) );
+ mxEdEntries->connect_changed ( LINK( this, ScTpUserLists, EdEntriesModHdl ) );
+
+ if ( pViewSh )
+ {
+ SCTAB nStartTab = 0;
+ SCTAB nEndTab = 0;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+
+ pViewData = &pViewSh->GetViewData();
+ pDoc = &pViewData->GetDocument();
+
+ pViewData->GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+
+ aStrSelectedArea = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab
+ ).Format(*pDoc, ScRefFlags::RANGE_ABS_3D);
+
+ mxBtnCopy->connect_clicked ( LINK( this, ScTpUserLists, BtnClickHdl ) );
+ mxBtnCopy->set_sensitive(true);
+ }
+ else
+ {
+ mxBtnCopy->set_sensitive(false);
+ mxFtCopyFrom->set_sensitive(false);
+ mxEdCopyFrom->set_sensitive(false);
+ }
+
+}
+
+std::unique_ptr<SfxTabPage> ScTpUserLists::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet )
+{
+ return std::make_unique<ScTpUserLists>(pPage, pController, *rAttrSet);
+}
+
+void ScTpUserLists::Reset( const SfxItemSet* rCoreAttrs )
+{
+ const ScUserListItem& rUserListItem = static_cast<const ScUserListItem&>(
+ rCoreAttrs->Get( nWhichUserLists ));
+ const ScUserList* pCoreList = rUserListItem.GetUserList();
+
+ OSL_ENSURE( pCoreList, "UserList not found :-/" );
+
+ if ( pCoreList )
+ {
+ if ( !pUserLists )
+ pUserLists.reset( new ScUserList( *pCoreList ) );
+ else
+ *pUserLists = *pCoreList;
+
+ if ( UpdateUserListBox() > 0 )
+ {
+ mxLbLists->select( 0 );
+ UpdateEntries( 0 );
+ }
+ }
+ else if ( !pUserLists )
+ pUserLists.reset( new ScUserList );
+
+ mxEdCopyFrom->set_text( aStrSelectedArea );
+
+ if ( mxLbLists->n_children() == 0 )
+ {
+ mxFtLists->set_sensitive(false);
+ mxLbLists->set_sensitive(false);
+ mxFtEntries->set_sensitive(false);
+ mxEdEntries->set_sensitive(false);
+ mxBtnRemove->set_sensitive(false);
+ }
+
+ mxBtnNew->show();
+ mxBtnDiscard->hide();
+ mxBtnAdd->show();
+ mxBtnModify->hide();
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+
+ if ( !bCopyDone && pViewData )
+ {
+ mxFtCopyFrom->set_sensitive(true);
+ mxEdCopyFrom->set_sensitive(true);
+ mxBtnCopy->set_sensitive(true);
+ }
+}
+
+OUString ScTpUserLists::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "listslabel", "entrieslabel", "copyfromlabel" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString buttons[] = { "new", "discard", "add", "modify", "delete", "copy" };
+
+ for (const auto& btn : buttons)
+ {
+ if (const auto& pString = m_xBuilder->weld_button(btn))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpUserLists::FillItemSet( SfxItemSet* rCoreAttrs )
+{
+ // Changes aren't saved?
+ // -> simulate click of Add-Button
+
+ if ( bModifyMode || bCancelMode )
+ BtnClickHdl(*mxBtnAdd);
+
+ const ScUserListItem& rUserListItem = static_cast<const ScUserListItem&>(
+ GetItemSet().Get( nWhichUserLists ));
+
+ ScUserList* pCoreList = rUserListItem.GetUserList();
+ bool bDataModified = false;
+
+ if ( (pUserLists == nullptr) && (pCoreList == nullptr) )
+ {
+ bDataModified = false;
+ }
+ else if ( pUserLists != nullptr )
+ {
+ if ( pCoreList != nullptr )
+ bDataModified = (*pUserLists != *pCoreList);
+ else
+ bDataModified = true;
+ }
+
+ if ( bDataModified )
+ {
+ ScUserListItem aULItem( nWhichUserLists );
+
+ if ( pUserLists )
+ aULItem.SetUserList( *pUserLists );
+
+ rCoreAttrs->Put( aULItem );
+ }
+
+ return bDataModified;
+}
+
+DeactivateRC ScTpUserLists::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+size_t ScTpUserLists::UpdateUserListBox()
+{
+ mxLbLists->clear();
+
+ if ( !pUserLists ) return 0;
+
+ size_t nCount = pUserLists->size();
+ OUString aEntry;
+
+ for ( size_t i=0; i<nCount; ++i )
+ {
+ aEntry = (*pUserLists)[i].GetString();
+ OSL_ENSURE( !aEntry.isEmpty(), "Empty UserList-entry :-/" );
+ mxLbLists->append_text( aEntry );
+ }
+
+ return nCount;
+}
+
+void ScTpUserLists::UpdateEntries( size_t nList )
+{
+ if ( !pUserLists ) return;
+
+ if ( nList < pUserLists->size() )
+ {
+ const ScUserListData& rList = (*pUserLists)[nList];
+ std::size_t nSubCount = rList.GetSubCount();
+ OUStringBuffer aEntryListStr;
+
+ for ( size_t i=0; i<nSubCount; i++ )
+ {
+ if ( i!=0 )
+ aEntryListStr.append(CR);
+ aEntryListStr.append(rList.GetSubStr(i));
+ }
+
+ mxEdEntries->set_text(convertLineEnd(aEntryListStr.makeStringAndClear(), GetSystemLineEnd()));
+ }
+ else
+ {
+ OSL_FAIL( "Invalid ListIndex :-/" );
+ }
+}
+
+void ScTpUserLists::MakeListStr( OUString& rListStr )
+{
+ if (rListStr.isEmpty())
+ return;
+
+ OUStringBuffer aStr;
+
+ for(sal_Int32 nIdx=0; nIdx>=0;)
+ {
+ aStr.append(comphelper::string::strip(o3tl::getToken(rListStr, 0, LF, nIdx), ' '));
+ aStr.append(cDelimiter);
+ }
+
+ aStr.strip(cDelimiter);
+ sal_Int32 nLen = aStr.getLength();
+
+ rListStr.clear();
+
+ // delete all duplicates of cDelimiter
+ sal_Int32 c = 0;
+ while ( c < nLen )
+ {
+ rListStr += OUStringChar(aStr[c]);
+ ++c;
+
+ if ((c < nLen) && (aStr[c] == cDelimiter))
+ {
+ rListStr += OUStringChar(aStr[c]);
+
+ while ((c < nLen) && (aStr[c] == cDelimiter))
+ ++c;
+ }
+ }
+
+}
+
+void ScTpUserLists::AddNewList( const OUString& rEntriesStr )
+{
+ OUString theEntriesStr( rEntriesStr );
+
+ if ( !pUserLists )
+ pUserLists.reset( new ScUserList );
+
+ MakeListStr( theEntriesStr );
+
+ pUserLists->emplace_back(theEntriesStr);
+}
+
+void ScTpUserLists::CopyListFromArea( const ScRefAddress& rStartPos,
+ const ScRefAddress& rEndPos )
+{
+ if ( bCopyDone ) return;
+
+ SCTAB nTab = rStartPos.Tab();
+ SCCOL nStartCol = rStartPos.Col();
+ SCROW nStartRow = rStartPos.Row();
+ SCCOL nEndCol = rEndPos.Col();
+ SCROW nEndRow = rEndPos.Row();
+ sal_uInt16 nCellDir = SCRET_COLS;
+
+ if ( (nStartCol != nEndCol) && (nStartRow != nEndRow) )
+ {
+ ScColOrRowDlg aDialog(GetFrameWeld(), aStrCopyList, aStrCopyFrom);
+ nCellDir = aDialog.run();
+ }
+ else if ( nStartCol != nEndCol )
+ nCellDir = SCRET_ROWS;
+ else
+ nCellDir = SCRET_COLS;
+
+ if ( nCellDir != RET_CANCEL )
+ {
+ bool bValueIgnored = false;
+
+ if ( nCellDir == SCRET_COLS )
+ {
+ for ( SCCOL col=nStartCol; col<=nEndCol; col++ )
+ {
+ OUStringBuffer aStrList;
+ for ( SCROW row=nStartRow; row<=nEndRow; row++ )
+ {
+ if ( pDoc->HasStringData( col, row, nTab ) )
+ {
+ OUString aStrField = pDoc->GetString(col, row, nTab);
+
+ if ( !aStrField.isEmpty() )
+ {
+ aStrList.append(aStrField + "\n");
+ }
+ }
+ else
+ bValueIgnored = true;
+ }
+ if ( !aStrList.isEmpty() )
+ AddNewList( aStrList.makeStringAndClear() );
+ }
+ }
+ else
+ {
+ for ( SCROW row=nStartRow; row<=nEndRow; row++ )
+ {
+ OUStringBuffer aStrList;
+ for ( SCCOL col=nStartCol; col<=nEndCol; col++ )
+ {
+ if ( pDoc->HasStringData( col, row, nTab ) )
+ {
+ OUString aStrField = pDoc->GetString(col, row, nTab);
+
+ if ( !aStrField.isEmpty() )
+ {
+ aStrList.append(aStrField + "\n");
+ }
+ }
+ else
+ bValueIgnored = true;
+ }
+ if ( !aStrList.isEmpty() )
+ AddNewList( aStrList.makeStringAndClear() );
+ }
+ }
+
+ if ( bValueIgnored )
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ aStrCopyErr));
+ xInfoBox->run();
+ }
+ }
+
+ bCopyDone = true;
+
+}
+
+void ScTpUserLists::ModifyList( size_t nSelList,
+ const OUString& rEntriesStr )
+{
+ if ( !pUserLists ) return;
+
+ OUString theEntriesStr( rEntriesStr );
+
+ MakeListStr( theEntriesStr );
+
+ (*pUserLists)[nSelList].SetString( theEntriesStr );
+}
+
+void ScTpUserLists::RemoveList( size_t nList )
+{
+ if (pUserLists && nList < pUserLists->size())
+ pUserLists->EraseData(nList);
+}
+
+// Handler:
+
+IMPL_LINK( ScTpUserLists, LbSelectHdl, weld::TreeView&, rLb, void )
+{
+ if ( &rLb != mxLbLists.get() )
+ return;
+
+ sal_Int32 nSelPos = mxLbLists->get_selected_index();
+ if ( nSelPos == -1 )
+ return;
+
+ if ( !mxFtEntries->get_sensitive() ) mxFtEntries->set_sensitive(true);
+ if ( !mxEdEntries->get_sensitive() ) mxEdEntries->set_sensitive(true);
+ if ( !mxBtnRemove->get_sensitive() ) mxBtnRemove->set_sensitive(true);
+ if ( mxBtnAdd->get_sensitive() )
+ {
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+ }
+
+ UpdateEntries( nSelPos );
+}
+
+IMPL_LINK( ScTpUserLists, BtnClickHdl, weld::Button&, rBtn, void )
+{
+ if (&rBtn == mxBtnNew.get() || &rBtn == mxBtnDiscard.get())
+ {
+ if ( !bCancelMode )
+ {
+ nCancelPos = ( mxLbLists->n_children() > 0 )
+ ? mxLbLists->get_selected_index()
+ : 0;
+ mxLbLists->unselect_all();
+ mxFtLists->set_sensitive(false);
+ mxLbLists->set_sensitive(false);
+ mxFtEntries->set_sensitive(true);
+ mxEdEntries->set_sensitive(true);
+ mxEdEntries->set_text( OUString() );
+ mxEdEntries->grab_focus();
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+ mxBtnRemove->set_sensitive(false);
+
+ if ( mxBtnCopy->get_sensitive() )
+ {
+ mxBtnCopy->set_sensitive(false);
+ mxFtCopyFrom->set_sensitive(false);
+ mxEdCopyFrom->set_sensitive(false);
+ }
+ mxBtnNew->hide();
+ mxBtnDiscard->show();
+ bCancelMode = true;
+ }
+ else // if ( bCancelMode )
+ {
+ if ( mxLbLists->n_children() > 0 )
+ {
+ mxLbLists->select( nCancelPos );
+ LbSelectHdl( *mxLbLists );
+ mxFtLists->set_sensitive(true);
+ mxLbLists->set_sensitive(true);
+ }
+ else
+ {
+ mxFtEntries->set_sensitive(false);
+ mxEdEntries->set_sensitive(false);
+ mxEdEntries->set_text( OUString() );
+ mxBtnRemove->set_sensitive(false);
+ }
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+
+ if ( pViewData && !bCopyDone )
+ {
+ mxBtnCopy->set_sensitive(true);
+ mxFtCopyFrom->set_sensitive(true);
+ mxEdCopyFrom->set_sensitive(true);
+ }
+ mxBtnNew->show();
+ mxBtnDiscard->hide();
+ bCancelMode = false;
+ bModifyMode = false;
+ }
+ }
+ else if (&rBtn == mxBtnAdd.get() || &rBtn == mxBtnModify.get())
+ {
+ OUString theEntriesStr( mxEdEntries->get_text() );
+
+ if ( !bModifyMode )
+ {
+ if ( !theEntriesStr.isEmpty() )
+ {
+ AddNewList( theEntriesStr );
+ UpdateUserListBox();
+ mxLbLists->select( mxLbLists->n_children()-1 );
+ LbSelectHdl( *mxLbLists );
+ mxFtLists->set_sensitive(true);
+ mxLbLists->set_sensitive(true);
+ }
+ else
+ {
+ if ( mxLbLists->n_children() > 0 )
+ {
+ mxLbLists->select( nCancelPos );
+ LbSelectHdl( *mxLbLists );
+ mxLbLists->set_sensitive(true);
+ mxLbLists->set_sensitive(true);
+ }
+ }
+
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+ mxBtnRemove->set_sensitive(true);
+ mxBtnNew->show();
+ mxBtnDiscard->hide();
+ bCancelMode = false;
+ }
+ else // if ( bModifyMode )
+ {
+ sal_Int32 nSelList = mxLbLists->get_selected_index();
+
+ OSL_ENSURE( nSelList != -1 , "Modify without List :-/" );
+
+ if ( !theEntriesStr.isEmpty() )
+ {
+ ModifyList( nSelList, theEntriesStr );
+ UpdateUserListBox();
+ mxLbLists->select( nSelList );
+ }
+ else
+ {
+ mxLbLists->select( 0 );
+ LbSelectHdl( *mxLbLists );
+ }
+
+ mxBtnNew->show();
+ mxBtnDiscard->hide();
+ bCancelMode = false;
+ mxBtnAdd->show();
+ mxBtnModify->show();
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+ bModifyMode = false;
+ mxBtnRemove->set_sensitive(true);
+ mxFtLists->set_sensitive(true);
+ mxLbLists->set_sensitive(true);
+ }
+
+ if ( pViewData && !bCopyDone )
+ {
+ mxBtnCopy->set_sensitive(true);
+ mxFtCopyFrom->set_sensitive(true);
+ mxEdCopyFrom->set_sensitive(true);
+ }
+ }
+ else if ( &rBtn == mxBtnRemove.get() )
+ {
+ if ( mxLbLists->n_children() > 0 )
+ {
+ sal_Int32 nRemovePos = mxLbLists->get_selected_index();
+ OUString aMsg = o3tl::getToken(aStrQueryRemove, 0, '#' )
+ + mxLbLists->get_text( nRemovePos )
+ + o3tl::getToken(aStrQueryRemove, 1, '#' );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMsg));
+ xQueryBox->set_default_response(RET_YES);
+
+ if (RET_YES == xQueryBox->run())
+ {
+ RemoveList( nRemovePos );
+ UpdateUserListBox();
+
+ if ( mxLbLists->n_children() > 0 )
+ {
+ mxLbLists->select(
+ ( nRemovePos >= mxLbLists->n_children() )
+ ? mxLbLists->n_children()-1
+ : nRemovePos );
+ LbSelectHdl( *mxLbLists );
+ }
+ else
+ {
+ mxFtLists->set_sensitive(false);
+ mxLbLists->set_sensitive(false);
+ mxFtEntries->set_sensitive(false);
+ mxEdEntries->set_sensitive(false);
+ mxEdEntries->set_text( OUString() );
+ mxBtnRemove->set_sensitive(false);
+ }
+ }
+
+ if ( pViewData && !bCopyDone && !mxBtnCopy->get_sensitive() )
+ {
+ mxBtnCopy->set_sensitive(true);
+ mxFtCopyFrom->set_sensitive(true);
+ mxEdCopyFrom->set_sensitive(true);
+ }
+ }
+ }
+ else if ( pViewData && (&rBtn == mxBtnCopy.get()) )
+ {
+ if ( bCopyDone )
+ return;
+
+ ScRefAddress theStartPos;
+ ScRefAddress theEndPos;
+ OUString theAreaStr( mxEdCopyFrom->get_text() );
+ bool bAreaOk = false;
+
+ if ( !theAreaStr.isEmpty() )
+ {
+ bAreaOk = ScRangeUtil::IsAbsArea( theAreaStr,
+ *pDoc,
+ pViewData->GetTabNo(),
+ &theAreaStr,
+ &theStartPos,
+ &theEndPos,
+ pDoc->GetAddressConvention() );
+ if ( !bAreaOk )
+ {
+ bAreaOk = ScRangeUtil::IsAbsPos( theAreaStr,
+ *pDoc,
+ pViewData->GetTabNo(),
+ &theAreaStr,
+ &theStartPos,
+ pDoc->GetAddressConvention() );
+ theEndPos = theStartPos;
+ }
+ }
+
+ if ( bAreaOk )
+ {
+ CopyListFromArea( theStartPos, theEndPos );
+ UpdateUserListBox();
+ mxLbLists->select( mxLbLists->n_children()-1 );
+ LbSelectHdl( *mxLbLists );
+ mxEdCopyFrom->set_text( theAreaStr );
+ mxEdCopyFrom->set_sensitive(false);
+ mxBtnCopy->set_sensitive(false);
+ mxFtCopyFrom->set_sensitive(false);
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_TABREF)));
+
+ xBox->run();
+ mxEdCopyFrom->grab_focus();
+ mxEdCopyFrom->select_region(0, -1);
+ }
+ }
+}
+
+IMPL_LINK( ScTpUserLists, EdEntriesModHdl, weld::TextView&, rEd, void )
+{
+ if ( &rEd != mxEdEntries.get() )
+ return;
+
+ if ( mxBtnCopy->get_sensitive() )
+ {
+ mxBtnCopy->set_sensitive(false);
+ mxFtCopyFrom->set_sensitive(false);
+ mxEdCopyFrom->set_sensitive(false);
+ }
+
+ if ( !mxEdEntries->get_text().isEmpty() )
+ {
+ if ( !bCancelMode && !bModifyMode )
+ {
+ mxBtnNew->hide();
+ mxBtnDiscard->show();
+ bCancelMode = true;
+ mxBtnAdd->hide();
+ mxBtnAdd->set_sensitive(true);
+ mxBtnModify->show();
+ mxBtnModify->set_sensitive(true);
+ bModifyMode = true;
+ mxBtnRemove->set_sensitive(false);
+ mxFtLists->set_sensitive(false);
+ mxLbLists->set_sensitive(false);
+ }
+ else // if ( bCancelMode || bModifyMode )
+ {
+ if ( !mxBtnAdd->get_sensitive() )
+ {
+ mxBtnAdd->set_sensitive(true);
+ mxBtnModify->set_sensitive(true);
+ }
+ }
+ }
+ else
+ {
+ if ( mxBtnAdd->get_sensitive() )
+ {
+ mxBtnAdd->set_sensitive(false);
+ mxBtnModify->set_sensitive(false);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/optdlg/tpview.cxx b/sc/source/ui/optdlg/tpview.cxx
new file mode 100644
index 0000000000..73f305dd7f
--- /dev/null
+++ b/sc/source/ui/optdlg/tpview.cxx
@@ -0,0 +1,840 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <officecfg/Office/Calc.hxx>
+#include <tpview.hxx>
+#include <global.hxx>
+#include <viewopti.hxx>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <units.hrc>
+#include <appoptio.hxx>
+#include <scmod.hxx>
+#include <svl/eitem.hxx>
+#include <svtools/unitconv.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+ScTpContentOptions::ScTpContentOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/tpviewpage.ui", "TpViewPage", &rArgSet)
+ , m_xGridLB(m_xBuilder->weld_combo_box("grid"))
+ , m_xGridImg(m_xBuilder->weld_widget("lockgrid"))
+ , m_xBreakCB(m_xBuilder->weld_check_button("break"))
+ , m_xBreakImg(m_xBuilder->weld_widget("lockbreak"))
+ , m_xGuideLineCB(m_xBuilder->weld_check_button("guideline"))
+ , m_xGuideLineImg(m_xBuilder->weld_widget("lockguideline"))
+ , m_xFormulaCB(m_xBuilder->weld_check_button("formula"))
+ , m_xFormulaImg(m_xBuilder->weld_widget("lockformula"))
+ , m_xNilCB(m_xBuilder->weld_check_button("nil"))
+ , m_xNilImg(m_xBuilder->weld_widget("locknil"))
+ , m_xAnnotCB(m_xBuilder->weld_check_button("annot"))
+ , m_xAnnotImg(m_xBuilder->weld_widget("lockannot"))
+ , m_xFormulaMarkCB(m_xBuilder->weld_check_button("formulamark"))
+ , m_xFormulaMarkImg(m_xBuilder->weld_widget("lockformulamark"))
+ , m_xValueCB(m_xBuilder->weld_check_button("value"))
+ , m_xValueImg(m_xBuilder->weld_widget("lockvalue"))
+ , m_xColRowHighCB(m_xBuilder->weld_check_button("colrowhigh"))
+ , m_xColRowHighImg(m_xBuilder->weld_widget("lockcolrowhigh"))
+ , m_xAnchorCB(m_xBuilder->weld_check_button("anchor"))
+ , m_xAnchorImg(m_xBuilder->weld_widget("lockanchor"))
+ , m_xRangeFindCB(m_xBuilder->weld_check_button("rangefind"))
+ , m_xRangeFindImg(m_xBuilder->weld_widget("lockrangefind"))
+ , m_xObjGrfLB(m_xBuilder->weld_combo_box("objgrf"))
+ , m_xObjGrfImg(m_xBuilder->weld_widget("lockobjgrf"))
+ , m_xDiagramLB(m_xBuilder->weld_combo_box("diagram"))
+ , m_xDiagramImg(m_xBuilder->weld_widget("lockdiagram"))
+ , m_xDrawLB(m_xBuilder->weld_combo_box("draw"))
+ , m_xDrawImg(m_xBuilder->weld_widget("lockdraw"))
+ , m_xSyncZoomCB(m_xBuilder->weld_check_button("synczoom"))
+ , m_xSyncZoomImg(m_xBuilder->weld_widget("locksynczoom"))
+ , m_xRowColHeaderCB(m_xBuilder->weld_check_button("rowcolheader"))
+ , m_xRowColHeaderImg(m_xBuilder->weld_widget("lockrowcolheader"))
+ , m_xHScrollCB(m_xBuilder->weld_check_button("hscroll"))
+ , m_xHScrollImg(m_xBuilder->weld_widget("lockhscroll"))
+ , m_xVScrollCB(m_xBuilder->weld_check_button("vscroll"))
+ , m_xVScrollImg(m_xBuilder->weld_widget("lockvscroll"))
+ , m_xTblRegCB(m_xBuilder->weld_check_button("tblreg"))
+ , m_xTblRegImg(m_xBuilder->weld_widget("locktblreg"))
+ , m_xOutlineCB(m_xBuilder->weld_check_button("outline"))
+ , m_xOutlineImg(m_xBuilder->weld_widget("lockoutline"))
+ , m_xSummaryCB(m_xBuilder->weld_check_button("cbSummary"))
+ , m_xSummaryImg(m_xBuilder->weld_widget("lockcbSummary"))
+ , m_xThemedCursorRB(m_xBuilder->weld_radio_button("rbThemedCursor"))
+ , m_xSystemCursorRB(m_xBuilder->weld_radio_button("rbSystemCursor"))
+ , m_xCursorImg(m_xBuilder->weld_widget("lockCursor"))
+{
+ SetExchangeSupport();
+ Link<weld::ComboBox&,void> aSelObjHdl(LINK( this, ScTpContentOptions, SelLbObjHdl ) );
+ m_xObjGrfLB->connect_changed(aSelObjHdl);
+ m_xDiagramLB->connect_changed(aSelObjHdl);
+ m_xDrawLB->connect_changed(aSelObjHdl);
+ m_xGridLB->connect_changed( LINK( this, ScTpContentOptions, GridHdl ) );
+
+ Link<weld::Toggleable&, void> aCBHdl(LINK( this, ScTpContentOptions, CBHdl ) );
+ m_xFormulaCB->connect_toggled(aCBHdl);
+ m_xNilCB->connect_toggled(aCBHdl);
+ m_xAnnotCB->connect_toggled(aCBHdl);
+ m_xAnnotCB->set_accessible_description(ScResId(STR_A11Y_DESC_ANNOT));
+ m_xFormulaMarkCB->connect_toggled(aCBHdl);
+ m_xValueCB->connect_toggled(aCBHdl);
+ m_xColRowHighCB->connect_toggled(aCBHdl);
+ m_xAnchorCB->connect_toggled(aCBHdl);
+
+ m_xVScrollCB->connect_toggled(aCBHdl);
+ m_xHScrollCB->connect_toggled(aCBHdl);
+ m_xTblRegCB->connect_toggled(aCBHdl);
+ m_xOutlineCB->connect_toggled(aCBHdl);
+ m_xBreakCB->connect_toggled(aCBHdl);
+ m_xGuideLineCB->connect_toggled(aCBHdl);
+ m_xRowColHeaderCB->connect_toggled(aCBHdl);
+ m_xSummaryCB->connect_toggled(aCBHdl);
+ m_xThemedCursorRB->connect_toggled(aCBHdl);
+}
+
+ScTpContentOptions::~ScTpContentOptions()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTpContentOptions::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScTpContentOptions>(pPage, pController, *rCoreSet);
+}
+
+OUString ScTpContentOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label4", "label5", "label3", "label1", "grid_label",
+ "lbCursor", "label2", "objgrf_label", "diagram_label", "draw_label" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString checkButton[]
+ = { "formula", "nil", "annot", "formulamark", "value", "anchor",
+ "rangefind", "rowcolheader", "hscroll", "vscroll", "tblreg", "outline",
+ "cbSummary", "synczoom", "break", "guideline" };
+
+ for (const auto& check : checkButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_check_button(check))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpContentOptions::FillItemSet( SfxItemSet* rCoreSet )
+{
+ bool bRet = false;
+ if( m_xFormulaCB->get_state_changed_from_saved() ||
+ m_xNilCB->get_state_changed_from_saved() ||
+ m_xAnnotCB->get_state_changed_from_saved() ||
+ m_xFormulaMarkCB->get_state_changed_from_saved() ||
+ m_xValueCB->get_state_changed_from_saved() ||
+ m_xAnchorCB->get_state_changed_from_saved() ||
+ m_xObjGrfLB->get_value_changed_from_saved() ||
+ m_xDiagramLB->get_value_changed_from_saved() ||
+ m_xDrawLB->get_value_changed_from_saved() ||
+ m_xGridLB->get_value_changed_from_saved() ||
+ m_xRowColHeaderCB->get_state_changed_from_saved() ||
+ m_xHScrollCB->get_state_changed_from_saved() ||
+ m_xVScrollCB->get_state_changed_from_saved() ||
+ m_xTblRegCB->get_state_changed_from_saved() ||
+ m_xOutlineCB->get_state_changed_from_saved() ||
+ m_xBreakCB->get_state_changed_from_saved() ||
+ m_xSummaryCB->get_state_changed_from_saved() ||
+ m_xThemedCursorRB->get_state_changed_from_saved() ||
+ m_xGuideLineCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(ScTpViewItem(*m_xLocalOptions));
+ bRet = true;
+ }
+ if(m_xRangeFindCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_INPUT_RANGEFINDER, m_xRangeFindCB->get_active()));
+ bRet = true;
+ }
+ if(m_xSyncZoomCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_OPT_SYNCZOOM, m_xSyncZoomCB->get_active()));
+ bRet = true;
+ }
+ if (m_xColRowHighCB->get_state_changed_from_saved())
+ {
+ auto pChange(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::set(m_xColRowHighCB->get_active(), pChange);
+ pChange->commit();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void ScTpContentOptions::Reset( const SfxItemSet* rCoreSet )
+{
+ if(const ScTpViewItem* pViewItem = rCoreSet->GetItemIfSet(SID_SCVIEWOPTIONS, false))
+ m_xLocalOptions.reset( new ScViewOptions( pViewItem->GetViewOptions() ) );
+ else
+ m_xLocalOptions.reset( new ScViewOptions );
+ m_xFormulaCB ->set_active(m_xLocalOptions->GetOption(VOPT_FORMULAS));
+ m_xNilCB ->set_active(m_xLocalOptions->GetOption(VOPT_NULLVALS));
+ m_xAnnotCB ->set_active(m_xLocalOptions->GetOption(VOPT_NOTES));
+ m_xFormulaMarkCB->set_active(m_xLocalOptions->GetOption(VOPT_FORMULAS_MARKS));
+ m_xValueCB ->set_active(m_xLocalOptions->GetOption(VOPT_SYNTAX));
+ m_xColRowHighCB->set_active(officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get());
+ m_xAnchorCB ->set_active(m_xLocalOptions->GetOption(VOPT_ANCHOR));
+
+ m_xObjGrfLB ->set_active( static_cast<sal_uInt16>(m_xLocalOptions->GetObjMode(VOBJ_TYPE_OLE)) );
+ m_xDiagramLB ->set_active( static_cast<sal_uInt16>(m_xLocalOptions->GetObjMode(VOBJ_TYPE_CHART)) );
+ m_xDrawLB ->set_active( static_cast<sal_uInt16>(m_xLocalOptions->GetObjMode(VOBJ_TYPE_DRAW)) );
+
+ m_xRowColHeaderCB->set_active( m_xLocalOptions->GetOption(VOPT_HEADER) );
+ m_xHScrollCB->set_active( m_xLocalOptions->GetOption(VOPT_HSCROLL) );
+ m_xVScrollCB->set_active( m_xLocalOptions->GetOption(VOPT_VSCROLL) );
+ m_xTblRegCB ->set_active( m_xLocalOptions->GetOption(VOPT_TABCONTROLS) );
+ m_xOutlineCB->set_active( m_xLocalOptions->GetOption(VOPT_OUTLINER) );
+ m_xSummaryCB->set_active( m_xLocalOptions->GetOption(VOPT_SUMMARY) );
+ if ( m_xLocalOptions->GetOption(VOPT_THEMEDCURSOR) )
+ m_xThemedCursorRB->set_active( true );
+ else
+ m_xSystemCursorRB->set_active( true );
+
+ InitGridOpt();
+
+ m_xBreakCB->set_active( m_xLocalOptions->GetOption(VOPT_PAGEBREAKS) );
+ m_xGuideLineCB->set_active( m_xLocalOptions->GetOption(VOPT_HELPLINES) );
+
+ if(const SfxBoolItem* pFinderItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_RANGEFINDER, false))
+ m_xRangeFindCB->set_active(pFinderItem->GetValue());
+ if(const SfxBoolItem* pZoomItem = rCoreSet->GetItemIfSet(SID_SC_OPT_SYNCZOOM, false))
+ m_xSyncZoomCB->set_active(pZoomItem->GetValue());
+
+ bool bReadOnly = officecfg::Office::Calc::Layout::Line::GridLine::isReadOnly() ||
+ officecfg::Office::Calc::Layout::Line::GridOnColoredCells::isReadOnly();
+ m_xGridLB->set_sensitive(!bReadOnly);
+ m_xGridImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Line::PageBreak::isReadOnly();
+ m_xBreakCB->set_sensitive(!bReadOnly);
+ m_xBreakImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Line::Guide::isReadOnly();
+ m_xGuideLineCB->set_sensitive(!bReadOnly);
+ m_xGuideLineImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::Formula::isReadOnly();
+ m_xFormulaCB->set_sensitive(!bReadOnly);
+ m_xFormulaImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::ZeroValue::isReadOnly();
+ m_xNilCB->set_sensitive(!bReadOnly);
+ m_xNilImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::NoteTag::isReadOnly();
+ m_xAnnotCB->set_sensitive(!bReadOnly);
+ m_xAnnotImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::FormulaMark::isReadOnly();
+ m_xFormulaMarkCB->set_sensitive(!bReadOnly);
+ m_xFormulaMarkImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::ValueHighlighting::isReadOnly();
+ m_xValueCB->set_sensitive(!bReadOnly);
+ m_xValueImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::isReadOnly();
+ m_xColRowHighCB->set_sensitive(!bReadOnly);
+ m_xColRowHighImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::Anchor::isReadOnly();
+ m_xAnchorCB->set_sensitive(!bReadOnly);
+ m_xAnchorImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Input::ShowReference::isReadOnly();
+ m_xRangeFindCB->set_sensitive(!bReadOnly);
+ m_xRangeFindImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::ObjectGraphic::isReadOnly();
+ m_xObjGrfLB->set_sensitive(!bReadOnly);
+ m_xObjGrfImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::Chart::isReadOnly();
+ m_xDiagramLB->set_sensitive(!bReadOnly);
+ m_xDiagramImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Content::Display::DrawingObject::isReadOnly();
+ m_xDrawLB->set_sensitive(!bReadOnly);
+ m_xDrawImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Zoom::Synchronize::isReadOnly();
+ m_xSyncZoomCB->set_sensitive(!bReadOnly);
+ m_xSyncZoomImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::ColumnRowHeader::isReadOnly();
+ m_xRowColHeaderCB->set_sensitive(!bReadOnly);
+ m_xRowColHeaderImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::HorizontalScroll::isReadOnly();
+ m_xHScrollCB->set_sensitive(!bReadOnly);
+ m_xHScrollImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::VerticalScroll::isReadOnly();
+ m_xVScrollCB->set_sensitive(!bReadOnly);
+ m_xVScrollImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::SheetTab::isReadOnly();
+ m_xTblRegCB->set_sensitive(!bReadOnly);
+ m_xTblRegImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::OutlineSymbol::isReadOnly();
+ m_xOutlineCB->set_sensitive(!bReadOnly);
+ m_xOutlineImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::SearchSummary::isReadOnly();
+ m_xSummaryCB->set_sensitive(!bReadOnly);
+ m_xSummaryImg->set_visible(bReadOnly);
+
+ bReadOnly = officecfg::Office::Calc::Layout::Window::ThemedCursor::isReadOnly();
+ m_xThemedCursorRB->set_sensitive(!bReadOnly);
+ m_xSystemCursorRB->set_sensitive(!bReadOnly);
+ m_xCursorImg->set_visible(bReadOnly);
+
+ m_xRangeFindCB->save_state();
+ m_xSyncZoomCB->save_state();
+
+ m_xFormulaCB->save_state();
+ m_xNilCB->save_state();
+ m_xAnnotCB->save_state();
+ m_xFormulaMarkCB->save_state();
+ m_xValueCB->save_state();
+ m_xColRowHighCB->save_state();
+ m_xAnchorCB->save_state();
+ m_xObjGrfLB->save_value();
+ m_xDiagramLB->save_value();
+ m_xDrawLB->save_value();
+ m_xRowColHeaderCB->save_state();
+ m_xHScrollCB->save_state();
+ m_xVScrollCB->save_state();
+ m_xTblRegCB->save_state();
+ m_xOutlineCB->save_state();
+ m_xGridLB->save_value();
+ m_xBreakCB->save_state();
+ m_xGuideLineCB->save_state();
+ m_xSummaryCB->save_state();
+ m_xThemedCursorRB->save_state();
+}
+
+void ScTpContentOptions::ActivatePage( const SfxItemSet& rSet)
+{
+ if(const ScTpViewItem* pViewItem = rSet.GetItemIfSet(SID_SCVIEWOPTIONS, false))
+ *m_xLocalOptions = pViewItem->GetViewOptions();
+}
+
+DeactivateRC ScTpContentOptions::DeactivatePage( SfxItemSet* pSetP )
+{
+ if(pSetP)
+ FillItemSet(pSetP);
+ return DeactivateRC::LeavePage;
+}
+
+IMPL_LINK( ScTpContentOptions, SelLbObjHdl, weld::ComboBox&, rLb, void )
+{
+ const sal_Int32 nSelPos = rLb.get_active();
+ ScVObjMode eMode = ScVObjMode(nSelPos);
+ ScVObjType eType = VOBJ_TYPE_OLE;
+
+ if ( &rLb == m_xDiagramLB.get() )
+ eType = VOBJ_TYPE_CHART;
+ else if ( &rLb == m_xDrawLB.get() )
+ eType = VOBJ_TYPE_DRAW;
+
+ m_xLocalOptions->SetObjMode( eType, eMode );
+}
+
+IMPL_LINK( ScTpContentOptions, CBHdl, weld::Toggleable&, rBtn, void )
+{
+ ScViewOption eOption = VOPT_FORMULAS;
+ bool bChecked = rBtn.get_active();
+
+ if (m_xFormulaCB.get() == &rBtn ) eOption = VOPT_FORMULAS;
+ else if ( m_xNilCB.get() == &rBtn ) eOption = VOPT_NULLVALS;
+ else if ( m_xAnnotCB.get() == &rBtn ) eOption = VOPT_NOTES;
+ else if ( m_xFormulaMarkCB.get() == &rBtn ) eOption = VOPT_FORMULAS_MARKS;
+ else if ( m_xValueCB.get() == &rBtn ) eOption = VOPT_SYNTAX;
+ else if ( m_xAnchorCB.get() == &rBtn ) eOption = VOPT_ANCHOR;
+ else if ( m_xVScrollCB.get() == &rBtn ) eOption = VOPT_VSCROLL;
+ else if ( m_xHScrollCB.get() == &rBtn ) eOption = VOPT_HSCROLL;
+ else if ( m_xTblRegCB.get() == &rBtn ) eOption = VOPT_TABCONTROLS;
+ else if ( m_xOutlineCB.get() == &rBtn ) eOption = VOPT_OUTLINER;
+ else if ( m_xBreakCB.get() == &rBtn ) eOption = VOPT_PAGEBREAKS;
+ else if ( m_xGuideLineCB.get() == &rBtn ) eOption = VOPT_HELPLINES;
+ else if ( m_xRowColHeaderCB.get() == &rBtn ) eOption = VOPT_HEADER;
+ else if ( m_xSummaryCB.get() == &rBtn ) eOption = VOPT_SUMMARY;
+ else if ( m_xThemedCursorRB.get() == &rBtn ) eOption = VOPT_THEMEDCURSOR;
+
+ m_xLocalOptions->SetOption( eOption, bChecked );
+}
+
+void ScTpContentOptions::InitGridOpt()
+{
+ bool bGrid = m_xLocalOptions->GetOption( VOPT_GRID );
+ bool bGridOnTop = m_xLocalOptions->GetOption( VOPT_GRID_ONTOP );
+ sal_Int32 nSelPos = 0;
+
+ if ( bGrid || bGridOnTop )
+ {
+ if ( !bGridOnTop )
+ nSelPos = 0;
+ else
+ nSelPos = 1;
+ }
+ else
+ nSelPos = 2;
+
+ m_xGridLB->set_active (nSelPos);
+}
+
+IMPL_LINK( ScTpContentOptions, GridHdl, weld::ComboBox&, rLb, void )
+{
+ sal_Int32 nSelPos = rLb.get_active();
+ bool bGrid = ( nSelPos <= 1 );
+ bool bGridOnTop = ( nSelPos == 1 );
+
+ m_xLocalOptions->SetOption( VOPT_GRID, bGrid );
+ m_xLocalOptions->SetOption( VOPT_GRID_ONTOP, bGridOnTop );
+}
+
+ScTpLayoutOptions::ScTpLayoutOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rArgSet)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/scgeneralpage.ui", "ScGeneralPage", &rArgSet)
+ , pDoc(nullptr)
+ , m_xUnitLB(m_xBuilder->weld_combo_box("unitlb"))
+ , m_xUnitImg(m_xBuilder->weld_widget("lockunitlb"))
+ , m_xTabMF(m_xBuilder->weld_metric_spin_button("tabmf", FieldUnit::CM))
+ , m_xTabImg(m_xBuilder->weld_widget("locktabmf"))
+ , m_xAlwaysRB(m_xBuilder->weld_radio_button("alwaysrb"))
+ , m_xRequestRB(m_xBuilder->weld_radio_button("requestrb"))
+ , m_xNeverRB(m_xBuilder->weld_radio_button("neverrb"))
+ , m_xUpdateLinksImg(m_xBuilder->weld_widget("lockupdatelinks"))
+ , m_xAlignCB(m_xBuilder->weld_check_button("aligncb"))
+ , m_xAlignImg(m_xBuilder->weld_widget("lockaligncb"))
+ , m_xAlignLB(m_xBuilder->weld_combo_box("alignlb"))
+ , m_xEditModeCB(m_xBuilder->weld_check_button("editmodecb"))
+ , m_xEditModeImg(m_xBuilder->weld_widget("lockeditmodecb"))
+ , m_xFormatCB(m_xBuilder->weld_check_button("formatcb"))
+ , m_xFormatImg(m_xBuilder->weld_widget("lockformatcb"))
+ , m_xExpRefCB(m_xBuilder->weld_check_button("exprefcb"))
+ , m_xExpRefImg(m_xBuilder->weld_widget("lockexprefcb"))
+ , m_xSortRefUpdateCB(m_xBuilder->weld_check_button("sortrefupdatecb"))
+ , m_xSortRefUpdateImg(m_xBuilder->weld_widget("locksortrefupdatecb"))
+ , m_xMarkHdrCB(m_xBuilder->weld_check_button("markhdrcb"))
+ , m_xMarkHdrImg(m_xBuilder->weld_widget("lockmarkhdrcb"))
+ , m_xReplWarnCB(m_xBuilder->weld_check_button("replwarncb"))
+ , m_xReplWarnImg(m_xBuilder->weld_widget("lockreplwarncb"))
+ , m_xLegacyCellSelectionCB(m_xBuilder->weld_check_button("legacy_cell_selection_cb"))
+ , m_xLegacyCellSelectionImg(m_xBuilder->weld_widget("locklegacy_cell"))
+ , m_xEnterPasteModeCB(m_xBuilder->weld_check_button("enter_paste_mode_cb"))
+ , m_xEnterPasteModeImg(m_xBuilder->weld_widget("lockenter_paste"))
+{
+ SetExchangeSupport();
+
+ m_xUnitLB->connect_changed( LINK( this, ScTpLayoutOptions, MetricHdl ) );
+ m_xAlignCB->connect_toggled(LINK(this, ScTpLayoutOptions, AlignHdl));
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(SCSTR_UNIT); ++i)
+ {
+ OUString sMetric = ScResId(SCSTR_UNIT[i].first);
+ FieldUnit eFUnit = SCSTR_UNIT[i].second;
+
+ switch ( eFUnit )
+ {
+ case FieldUnit::MM:
+ case FieldUnit::CM:
+ case FieldUnit::POINT:
+ case FieldUnit::PICA:
+ case FieldUnit::INCH:
+ {
+ // only use these metrics
+ m_xUnitLB->append(OUString::number(static_cast<sal_uInt32>(eFUnit)), sMetric);
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+}
+
+ScTpLayoutOptions::~ScTpLayoutOptions()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTpLayoutOptions::Create( weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet* rCoreSet )
+{
+ auto xNew = std::make_unique<ScTpLayoutOptions>(pPage, pController, *rCoreSet);
+
+ ScDocShell* pDocSh = dynamic_cast< ScDocShell *>( SfxObjectShell::Current() );
+ if (pDocSh!=nullptr)
+ xNew->pDoc = &pDocSh->GetDocument();
+ return xNew;
+}
+
+OUString ScTpLayoutOptions::GetAllStrings()
+{
+ OUString sAllStrings;
+ OUString labels[] = { "label1", "label4", "label5", "label6", "label3" };
+
+ for (const auto& label : labels)
+ {
+ if (const auto& pString = m_xBuilder->weld_label(label))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString checkButton[] = { "aligncb", "editmodecb", "enter_paste_mode_cb",
+ "formatcb", "exprefcb", "sortrefupdatecb",
+ "markhdrcb", "replwarncb", "legacy_cell_selection_cb" };
+
+ for (const auto& check : checkButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_check_button(check))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ OUString radioButton[] = { "alwaysrb", "requestrb", "neverrb" };
+
+ for (const auto& radio : radioButton)
+ {
+ if (const auto& pString = m_xBuilder->weld_radio_button(radio))
+ sAllStrings += pString->get_label() + " ";
+ }
+
+ return sAllStrings.replaceAll("_", "");
+}
+
+bool ScTpLayoutOptions::FillItemSet( SfxItemSet* rCoreSet )
+{
+ bool bRet = true;
+ if (m_xUnitLB->get_value_changed_from_saved())
+ {
+ const sal_Int32 nMPos = m_xUnitLB->get_active();
+ sal_uInt16 nFieldUnit = m_xUnitLB->get_id(nMPos).toUInt32();
+ rCoreSet->Put( SfxUInt16Item( SID_ATTR_METRIC, nFieldUnit ) );
+ bRet = true;
+ }
+
+ if (m_xTabMF->get_value_changed_from_saved())
+ {
+ rCoreSet->Put(SfxUInt16Item(SID_ATTR_DEFTABSTOP,
+ sal::static_int_cast<sal_uInt16>( m_xTabMF->denormalize(m_xTabMF->get_value(FieldUnit::TWIP)) )));
+ bRet = true;
+ }
+
+ ScLkUpdMode nSet=LM_ALWAYS;
+
+ if (m_xRequestRB->get_active())
+ {
+ nSet=LM_ON_DEMAND;
+ }
+ else if (m_xNeverRB->get_active())
+ {
+ nSet=LM_NEVER;
+ }
+
+ if (m_xRequestRB->get_state_changed_from_saved() ||
+ m_xNeverRB->get_state_changed_from_saved() )
+ {
+ if(pDoc)
+ pDoc->SetLinkMode(nSet);
+ ScAppOptions aAppOptions=SC_MOD()->GetAppOptions();
+ aAppOptions.SetLinkMode(nSet );
+ SC_MOD()->SetAppOptions(aAppOptions);
+ bRet = true;
+ }
+ if (m_xAlignCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_INPUT_SELECTION, m_xAlignCB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xAlignLB->get_value_changed_from_saved())
+ {
+ rCoreSet->Put(SfxUInt16Item(SID_SC_INPUT_SELECTIONPOS, m_xAlignLB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xEditModeCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_INPUT_EDITMODE, m_xEditModeCB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xFormatCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_INPUT_FMT_EXPAND, m_xFormatCB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xExpRefCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_INPUT_REF_EXPAND, m_xExpRefCB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xSortRefUpdateCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_OPT_SORT_REF_UPDATE, m_xSortRefUpdateCB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xMarkHdrCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put(SfxBoolItem(SID_SC_INPUT_MARK_HEADER, m_xMarkHdrCB->get_active()));
+ bRet = true;
+ }
+
+ if (m_xReplWarnCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put( SfxBoolItem( SID_SC_INPUT_REPLCELLSWARN, m_xReplWarnCB->get_active() ) );
+ bRet = true;
+ }
+
+ if (m_xLegacyCellSelectionCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put( SfxBoolItem( SID_SC_INPUT_LEGACY_CELL_SELECTION, m_xLegacyCellSelectionCB->get_active() ) );
+ bRet = true;
+ }
+
+ if (m_xEnterPasteModeCB->get_state_changed_from_saved())
+ {
+ rCoreSet->Put( SfxBoolItem( SID_SC_INPUT_ENTER_PASTE_MODE, m_xEnterPasteModeCB->get_active() ) );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void ScTpLayoutOptions::Reset( const SfxItemSet* rCoreSet )
+{
+ m_xUnitLB->set_active(-1);
+ if ( rCoreSet->GetItemState( SID_ATTR_METRIC ) >= SfxItemState::DEFAULT )
+ {
+ const SfxUInt16Item& rItem = rCoreSet->Get( SID_ATTR_METRIC );
+ FieldUnit eFieldUnit = static_cast<FieldUnit>(rItem.GetValue());
+
+ for (sal_Int32 i = 0, nEntryCount = m_xUnitLB->get_count(); i < nEntryCount; ++i)
+ {
+ if (m_xUnitLB->get_id(i).toUInt32() == static_cast<sal_uInt32>(eFieldUnit))
+ {
+ m_xUnitLB->set_active(i);
+ break;
+ }
+ }
+ ::SetFieldUnit(*m_xTabMF, eFieldUnit);
+ }
+
+ bool bReadOnly = false;
+ MeasurementSystem eSys = ScGlobal::getLocaleData().getMeasurementSystemEnum();
+ if (eSys == MeasurementSystem::Metric)
+ {
+ bReadOnly = officecfg::Office::Calc::Layout::Other::MeasureUnit::Metric::isReadOnly();
+ }
+ else
+ {
+ bReadOnly = officecfg::Office::Calc::Layout::Other::MeasureUnit::NonMetric::isReadOnly();
+ }
+ m_xUnitLB->set_sensitive(!bReadOnly);
+ m_xUnitImg->set_visible(bReadOnly);
+
+ if(const SfxUInt16Item* pTabStopItem = rCoreSet->GetItemIfSet(SID_ATTR_DEFTABSTOP, false))
+ m_xTabMF->set_value(m_xTabMF->normalize(pTabStopItem->GetValue()), FieldUnit::TWIP);
+
+ if (eSys == MeasurementSystem::Metric)
+ {
+ bReadOnly = officecfg::Office::Calc::Layout::Other::TabStop::Metric::isReadOnly();
+ }
+ else
+ {
+ bReadOnly = officecfg::Office::Calc::Layout::Other::TabStop::NonMetric::isReadOnly();
+ }
+ m_xTabMF->set_sensitive(!bReadOnly);
+ m_xTabImg->set_visible(bReadOnly);
+
+ m_xUnitLB->save_value();
+ m_xTabMF->save_value();
+
+ ScLkUpdMode nSet=LM_UNKNOWN;
+
+ if(pDoc!=nullptr)
+ {
+ nSet=pDoc->GetLinkMode();
+ }
+
+ if(nSet==LM_UNKNOWN)
+ {
+ ScAppOptions aAppOptions=SC_MOD()->GetAppOptions();
+ nSet=aAppOptions.GetLinkMode();
+ }
+
+ switch(nSet)
+ {
+ case LM_ALWAYS: m_xAlwaysRB->set_active(true); break;
+ case LM_NEVER: m_xNeverRB->set_active(true); break;
+ case LM_ON_DEMAND: m_xRequestRB->set_active(true); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (officecfg::Office::Calc::Content::Update::Link::isReadOnly())
+ {
+ m_xAlwaysRB->set_sensitive(false);
+ m_xNeverRB->set_sensitive(false);
+ m_xRequestRB->set_sensitive(false);
+ m_xUpdateLinksImg->set_visible(true);
+ }
+ if(const SfxBoolItem* pSelectionItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_SELECTION, false))
+ m_xAlignCB->set_active(pSelectionItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::MoveSelection::isReadOnly();
+ m_xAlignCB->set_sensitive(!bReadOnly);
+ m_xAlignImg->set_visible(bReadOnly);
+
+ if(const SfxUInt16Item* pPosItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_SELECTIONPOS, false))
+ m_xAlignLB->set_active(pPosItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::MoveSelectionDirection::isReadOnly();
+ m_xAlignCB->set_sensitive(!bReadOnly);
+
+ if(const SfxBoolItem* pEditModeItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_EDITMODE, false))
+ m_xEditModeCB->set_active(pEditModeItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::SwitchToEditMode::isReadOnly();
+ m_xEditModeCB->set_sensitive(!bReadOnly);
+ m_xEditModeImg->set_visible(bReadOnly);
+
+ if(const SfxBoolItem* pExpandItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_FMT_EXPAND, false))
+ m_xFormatCB->set_active(pExpandItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::ExpandFormatting::isReadOnly();
+ m_xFormatCB->set_sensitive(!bReadOnly);
+ m_xFormatImg->set_visible(bReadOnly);
+
+ if(const SfxBoolItem* pExpandItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_REF_EXPAND, false))
+ m_xExpRefCB->set_active(pExpandItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::ExpandReference::isReadOnly();
+ m_xExpRefCB->set_sensitive(!bReadOnly);
+ m_xExpRefImg->set_visible(bReadOnly);
+
+ if (const SfxBoolItem* pUpdateItem = rCoreSet->GetItemIfSet(SID_SC_OPT_SORT_REF_UPDATE))
+ m_xSortRefUpdateCB->set_active(pUpdateItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::UpdateReferenceOnSort::isReadOnly();
+ m_xSortRefUpdateCB->set_sensitive(!bReadOnly);
+ m_xSortRefUpdateImg->set_visible(bReadOnly);
+
+ if(const SfxBoolItem* pHeaderItem = rCoreSet->GetItemIfSet(SID_SC_INPUT_MARK_HEADER, false))
+ m_xMarkHdrCB->set_active(pHeaderItem->GetValue());
+
+ bReadOnly = officecfg::Office::Calc::Input::HighlightSelection::isReadOnly();
+ m_xMarkHdrCB->set_sensitive(!bReadOnly);
+ m_xMarkHdrImg->set_visible(bReadOnly);
+
+ if( const SfxBoolItem* pWarnItem = rCoreSet->GetItemIfSet( SID_SC_INPUT_REPLCELLSWARN, false ) )
+ m_xReplWarnCB->set_active( pWarnItem->GetValue() );
+
+ bReadOnly = officecfg::Office::Calc::Input::ReplaceCellsWarning::isReadOnly();
+ m_xReplWarnCB->set_sensitive(!bReadOnly);
+ m_xReplWarnImg->set_visible(bReadOnly);
+
+ if( const SfxBoolItem* pSelectionItem = rCoreSet->GetItemIfSet( SID_SC_INPUT_LEGACY_CELL_SELECTION, false ) )
+ m_xLegacyCellSelectionCB->set_active( pSelectionItem->GetValue() );
+
+ bReadOnly = officecfg::Office::Calc::Input::LegacyCellSelection::isReadOnly();
+ m_xLegacyCellSelectionCB->set_sensitive(!bReadOnly);
+ m_xLegacyCellSelectionImg->set_visible(bReadOnly);
+
+ if( const SfxBoolItem* pPasteModeItem = rCoreSet->GetItemIfSet( SID_SC_INPUT_ENTER_PASTE_MODE, false ) )
+ m_xEnterPasteModeCB->set_active( pPasteModeItem->GetValue() );
+
+ bReadOnly = officecfg::Office::Calc::Input::EnterPasteMode::isReadOnly();
+ m_xEnterPasteModeCB->set_sensitive(!bReadOnly);
+ m_xEnterPasteModeImg->set_visible(bReadOnly);
+
+ m_xAlignCB->save_state();
+ m_xAlignLB->save_value();
+ m_xEditModeCB->save_state();
+ m_xFormatCB->save_state();
+
+ m_xExpRefCB->save_state();
+ m_xSortRefUpdateCB->save_state();
+ m_xMarkHdrCB->save_state();
+ m_xReplWarnCB->save_state();
+
+ m_xLegacyCellSelectionCB->save_state();
+ m_xEnterPasteModeCB->save_state();
+
+ AlignHdl(*m_xAlignCB);
+
+ m_xAlwaysRB->save_state();
+ m_xNeverRB->save_state();
+ m_xRequestRB->save_state();
+}
+
+void ScTpLayoutOptions::ActivatePage( const SfxItemSet& /* rCoreSet */ )
+{
+}
+
+DeactivateRC ScTpLayoutOptions::DeactivatePage( SfxItemSet* pSetP )
+{
+ if(pSetP)
+ FillItemSet(pSetP);
+ return DeactivateRC::LeavePage;
+}
+
+IMPL_LINK_NOARG(ScTpLayoutOptions, MetricHdl, weld::ComboBox&, void)
+{
+ const sal_Int32 nMPos = m_xUnitLB->get_active();
+ if (nMPos != -1)
+ {
+ FieldUnit eFieldUnit = static_cast<FieldUnit>(m_xUnitLB->get_id(nMPos).toUInt32());
+ sal_Int64 nVal =
+ m_xTabMF->denormalize( m_xTabMF->get_value( FieldUnit::TWIP ) );
+ ::SetFieldUnit( *m_xTabMF, eFieldUnit );
+ m_xTabMF->set_value( m_xTabMF->normalize( nVal ), FieldUnit::TWIP );
+ }
+}
+
+IMPL_LINK(ScTpLayoutOptions, AlignHdl, weld::Toggleable&, rBox, void)
+{
+ m_xAlignLB->set_sensitive(rBox.get_active() &&
+ !officecfg::Office::Calc::Input::MoveSelectionDirection::isReadOnly());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/pagedlg/areasdlg.cxx b/sc/source/ui/pagedlg/areasdlg.cxx
new file mode 100644
index 0000000000..b90a3a664b
--- /dev/null
+++ b/sc/source/ui/pagedlg/areasdlg.cxx
@@ -0,0 +1,787 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rangelst.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/charclass.hxx>
+
+#include <areasdlg.hxx>
+#include <rangenam.hxx>
+#include <reffact.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <compiler.hxx>
+#include <markdata.hxx>
+
+// List box positions for print range (PR)
+enum {
+ SC_AREASDLG_PR_ENTIRE = 1,
+ SC_AREASDLG_PR_USER = 2,
+ SC_AREASDLG_PR_SELECT = 3
+};
+
+// List box positions for repeat ranges (RR)
+enum {
+ SC_AREASDLG_RR_NONE = 0,
+ SC_AREASDLG_RR_USER = 1,
+ SC_AREASDLG_RR_OFFSET = 2
+};
+
+namespace
+{
+ void ERRORBOX(weld::Window* pParent, TranslateId rId)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(rId)));
+ xBox->run();
+ }
+}
+
+// global functions (->at the end of the file):
+
+static bool lcl_CheckRepeatString( std::u16string_view aStr, const ScDocument& rDoc, bool bIsRow, ScRange* pRange );
+static void lcl_GetRepeatRangeString( std::optional<ScRange> oRange, const ScDocument& rDoc, bool bIsRow, OUString& rStr );
+
+#if 0
+// this method is useful when debugging address flags.
+static void printAddressFlags(ScRefFlags nFlag)
+{
+ if ((nFlag & ScRefFlags::COL_ABS ) == ScRefFlags::COL_ABS ) printf("ScRefFlags::COL_ABS \n");
+ if ((nFlag & ScRefFlags::ROW_ABS ) == ScRefFlags::ROW_ABS ) printf("ScRefFlags::ROW_ABS \n");
+ if ((nFlag & ScRefFlags::TAB_ABS ) == ScRefFlags::TAB_ABS ) printf("ScRefFlags::TAB_ABS \n");
+ if ((nFlag & ScRefFlags::TAB_3D ) == ScRefFlags::TAB_3D ) printf("ScRefFlags::TAB_3D \n");
+ if ((nFlag & ScRefFlags::COL2_ABS ) == ScRefFlags::COL2_ABS ) printf("ScRefFlags::COL2_ABS \n");
+ if ((nFlag & ScRefFlags::ROW2_ABS ) == ScRefFlags::ROW2_ABS ) printf("ScRefFlags::ROW2_ABS \n");
+ if ((nFlag & ScRefFlags::TAB2_ABS ) == ScRefFlags::TAB2_ABS ) printf("ScRefFlags::TAB2_ABS \n");
+ if ((nFlag & ScRefFlags::TAB2_3D ) == ScRefFlags::TAB2_3D ) printf("ScRefFlags::TAB2_3D \n");
+ if ((nFlag & ScRefFlags::ROW_VALID ) == ScRefFlags::ROW_VALID ) printf("ScRefFlags::ROW_VALID \n");
+ if ((nFlag & ScRefFlags::COL_VALID ) == ScRefFlags::COL_VALID ) printf("ScRefFlags::COL_VALID \n");
+ if ((nFlag & ScRefFlags::TAB_VALID ) == ScRefFlags::TAB_VALID ) printf("ScRefFlags::TAB_VALID \n");
+ if ((nFlag & ScRefFlags::FORCE_DOC ) == ScRefFlags::FORCE_DOC ) printf("ScRefFlags::FORCE_DOC \n");
+ if ((nFlag & ScRefFlags::ROW2_VALID ) == ScRefFlags::ROW2_VALID ) printf("ScRefFlags::ROW2_VALID \n");
+ if ((nFlag & ScRefFlags::COL2_VALID ) == ScRefFlags::COL2_VALID ) printf("ScRefFlags::COL2_VALID \n");
+ if ((nFlag & ScRefFlags::TAB2_VALID ) == ScRefFlags::TAB2_VALID ) printf("ScRefFlags::TAB2_VALID \n");
+ if ((nFlag & ScRefFlags::VALID ) == ScRefFlags::VALID ) printf("ScRefFlags::VALID \n");
+ if ((nFlag & ScRefFlags::ADDR_ABS ) == ScRefFlags::ADDR_ABS ) printf("ScRefFlags::ADDR_ABS \n");
+ if ((nFlag & ScRefFlags::RANGE_ABS ) == ScRefFlags::RANGE_ABS ) printf("ScRefFlags::RANGE_ABS \n");
+ if ((nFlag & ScRefFlags::ADDR_ABS_3D ) == ScRefFlags::ADDR_ABS_3D ) printf("ScRefFlags::ADDR_ABS_3D \n");
+ if ((nFlag & ScRefFlags::RANGE_ABS_3D ) == ScRefFlags::RANGE_ABS_3D ) printf("ScRefFlags::RANGE_ABS_3D \n");
+}
+#endif
+
+
+ScPrintAreasDlg::ScPrintAreasDlg(SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent)
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/printareasdialog.ui", "PrintAreasDialog")
+ , bDlgLostFocus(false)
+ , pDoc(nullptr)
+ , pViewData(nullptr)
+ , nCurTab(0)
+ , m_xLbPrintArea(m_xBuilder->weld_combo_box("lbprintarea"))
+ , m_xEdPrintArea(new formula::RefEdit(m_xBuilder->weld_entry("edprintarea")))
+ , m_xRbPrintArea(new formula::RefButton(m_xBuilder->weld_button("rbprintarea")))
+ , m_xLbRepeatRow(m_xBuilder->weld_combo_box("lbrepeatrow"))
+ , m_xEdRepeatRow(new formula::RefEdit(m_xBuilder->weld_entry("edrepeatrow")))
+ , m_xRbRepeatRow(new formula::RefButton(m_xBuilder->weld_button("rbrepeatrow")))
+ , m_xLbRepeatCol(m_xBuilder->weld_combo_box("lbrepeatcol"))
+ , m_xEdRepeatCol(new formula::RefEdit(m_xBuilder->weld_entry("edrepeatcol")))
+ , m_xRbRepeatCol(new formula::RefButton(m_xBuilder->weld_button("rbrepeatcol")))
+ , m_xBtnOk(m_xBuilder->weld_button("ok"))
+ , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+ , m_xPrintFrame(m_xBuilder->weld_frame("printframe"))
+ , m_xRowFrame(m_xBuilder->weld_frame("rowframe"))
+ , m_xColFrame(m_xBuilder->weld_frame("colframe"))
+ , m_xPrintFrameFT(m_xPrintFrame->weld_label_widget())
+ , m_xRowFrameFT(m_xRowFrame->weld_label_widget())
+ , m_xColFrameFT(m_xColFrame->weld_label_widget())
+{
+ m_xEdPrintArea->SetReferences(this, m_xPrintFrameFT.get());
+ m_pRefInputEdit = m_xEdPrintArea.get();
+ m_xRbPrintArea->SetReferences(this, m_xEdPrintArea.get());
+
+ m_xEdRepeatRow->SetReferences(this, m_xRowFrameFT.get());
+ m_xRbRepeatRow->SetReferences(this, m_xEdRepeatRow.get());
+
+ m_xEdRepeatCol->SetReferences(this, m_xColFrameFT.get());
+ m_xRbRepeatCol->SetReferences(this, m_xEdRepeatCol.get());
+
+ ScTabViewShell* pScViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ ScDocShell* pScDocSh = dynamic_cast<ScDocShell*>(SfxObjectShell::Current());
+ assert(pScDocSh && "Current DocumentShell not found :-(");
+
+ pDoc = &pScDocSh->GetDocument();
+
+ if ( pScViewSh )
+ {
+ pViewData = &pScViewSh->GetViewData();
+ nCurTab = pViewData->GetTabNo();
+ }
+
+ Impl_Reset();
+
+ //@BugID 54702 Enable/Disable only in base class
+ //SFX_APPWINDOW->Enable();
+}
+
+ScPrintAreasDlg::~ScPrintAreasDlg()
+{
+}
+
+void ScPrintAreasDlg::Close()
+{
+ DoClose( ScPrintAreasDlgWrapper::GetChildWindowId() );
+}
+
+bool ScPrintAreasDlg::IsTableLocked() const
+{
+ // Printing areas are per table, therefore it makes no sense,
+ // to switch the table during input
+
+ return true;
+}
+
+void ScPrintAreasDlg::SetReference( const ScRange& rRef, ScDocument& /* rDoc */ )
+{
+ if ( !m_pRefInputEdit )
+ return;
+
+ if ( rRef.aStart != rRef.aEnd )
+ RefInputStart( m_pRefInputEdit );
+
+ OUString aStr;
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ if (m_xEdPrintArea.get() == m_pRefInputEdit)
+ {
+ aStr = rRef.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
+ OUString aVal = m_xEdPrintArea->GetText();
+ Selection aSel = m_xEdPrintArea->GetSelection();
+ aSel.Normalize();
+ aVal = aVal.replaceAt( aSel.Min(), aSel.Len(), aStr );
+ Selection aNewSel( aSel.Min(), aSel.Min()+aStr.getLength() );
+ m_xEdPrintArea->SetRefString( aVal );
+ m_xEdPrintArea->SetSelection( aNewSel );
+ }
+ else
+ {
+ bool bRow = ( m_xEdRepeatRow.get() == m_pRefInputEdit );
+ lcl_GetRepeatRangeString(rRef, *pDoc, bRow, aStr);
+ m_pRefInputEdit->SetRefString( aStr );
+ }
+ Impl_ModifyHdl( *m_pRefInputEdit );
+}
+
+void ScPrintAreasDlg::AddRefEntry()
+{
+ if (m_pRefInputEdit == m_xEdPrintArea.get())
+ {
+ const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
+ OUString aVal = m_xEdPrintArea->GetText() + OUStringChar(sep);
+ m_xEdPrintArea->SetText(aVal);
+
+ sal_Int32 nLen = aVal.getLength();
+ m_xEdPrintArea->SetSelection( Selection( nLen, nLen ) );
+
+ Impl_ModifyHdl( *m_xEdPrintArea );
+ }
+}
+
+void ScPrintAreasDlg::Deactivate()
+{
+ bDlgLostFocus = true;
+}
+
+void ScPrintAreasDlg::SetActive()
+{
+ if ( bDlgLostFocus )
+ {
+ bDlgLostFocus = false;
+
+ if ( m_pRefInputEdit )
+ {
+ m_pRefInputEdit->GrabFocus();
+ Impl_ModifyHdl( *m_pRefInputEdit );
+ }
+ }
+ else
+ m_xDialog->grab_focus();
+
+ RefInputDone();
+}
+
+void ScPrintAreasDlg::Impl_Reset()
+{
+ OUString aStrRange;
+ std::optional<ScRange> oRepeatColRange = pDoc->GetRepeatColRange( nCurTab );
+ std::optional<ScRange> oRepeatRowRange = pDoc->GetRepeatRowRange( nCurTab );
+
+ m_xEdPrintArea->SetModifyHdl (LINK( this, ScPrintAreasDlg, Impl_ModifyHdl));
+ m_xEdRepeatRow->SetModifyHdl (LINK( this, ScPrintAreasDlg, Impl_ModifyHdl));
+ m_xEdRepeatCol->SetModifyHdl (LINK( this, ScPrintAreasDlg, Impl_ModifyHdl));
+ m_xEdPrintArea->SetGetFocusHdl(LINK( this, ScPrintAreasDlg, Impl_GetEditFocusHdl));
+ m_xEdRepeatRow->SetGetFocusHdl(LINK( this, ScPrintAreasDlg, Impl_GetEditFocusHdl));
+ m_xEdRepeatCol->SetGetFocusHdl(LINK( this, ScPrintAreasDlg, Impl_GetEditFocusHdl));
+ m_xLbPrintArea->connect_focus_in(LINK( this, ScPrintAreasDlg, Impl_GetFocusHdl));
+ m_xLbRepeatRow->connect_focus_in(LINK( this, ScPrintAreasDlg, Impl_GetFocusHdl));
+ m_xLbRepeatCol->connect_focus_in(LINK( this, ScPrintAreasDlg, Impl_GetFocusHdl));
+ m_xLbPrintArea->connect_changed(LINK( this, ScPrintAreasDlg, Impl_SelectHdl));
+ m_xLbRepeatRow->connect_changed(LINK( this, ScPrintAreasDlg, Impl_SelectHdl));
+ m_xLbRepeatCol->connect_changed(LINK( this, ScPrintAreasDlg, Impl_SelectHdl));
+ m_xBtnOk->connect_clicked(LINK( this, ScPrintAreasDlg, Impl_BtnHdl));
+ m_xBtnCancel->connect_clicked(LINK( this, ScPrintAreasDlg, Impl_BtnHdl));
+
+ Impl_FillLists();
+
+ // printing area
+
+ aStrRange.clear();
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+ const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
+ sal_uInt16 nRangeCount = pDoc->GetPrintRangeCount( nCurTab );
+ for (sal_uInt16 i=0; i<nRangeCount; i++)
+ {
+ const ScRange* pPrintRange = pDoc->GetPrintRange( nCurTab, i );
+ if (pPrintRange)
+ {
+ if ( !aStrRange.isEmpty() )
+ aStrRange += OUStringChar(sep);
+ aStrRange += pPrintRange->Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
+ }
+ }
+ m_xEdPrintArea->SetText( aStrRange );
+
+ // repeat row
+
+ lcl_GetRepeatRangeString(oRepeatRowRange, *pDoc, true, aStrRange);
+ m_xEdRepeatRow->SetText( aStrRange );
+
+ // repeat column
+
+ lcl_GetRepeatRangeString(oRepeatColRange, *pDoc, false, aStrRange);
+ m_xEdRepeatCol->SetText( aStrRange );
+
+ Impl_ModifyHdl( *m_xEdPrintArea );
+ Impl_ModifyHdl( *m_xEdRepeatRow );
+ Impl_ModifyHdl( *m_xEdRepeatCol );
+ if( pDoc->IsPrintEntireSheet( nCurTab ) )
+ m_xLbPrintArea->set_active(SC_AREASDLG_PR_ENTIRE);
+
+ m_xEdPrintArea->SaveValue(); // save for FillItemSet():
+ m_xEdRepeatRow->SaveValue();
+ m_xEdRepeatCol->SaveValue();
+}
+
+bool ScPrintAreasDlg::Impl_GetItem( const formula::RefEdit* pEd, SfxStringItem& rItem )
+{
+ OUString aRangeStr = pEd->GetText();
+ bool bDataChanged = pEd->IsValueChangedFromSaved();
+
+ if ( !aRangeStr.isEmpty() && m_xEdPrintArea.get() != pEd )
+ {
+ ScRange aRange;
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+ lcl_CheckRepeatString(aRangeStr, *pDoc, m_xEdRepeatRow.get() == pEd, &aRange);
+ aRangeStr = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
+ }
+
+ rItem.SetValue( aRangeStr );
+
+ return bDataChanged;
+}
+
+bool ScPrintAreasDlg::Impl_CheckRefStrings()
+{
+ bool bOk = false;
+ OUString aStrPrintArea = m_xEdPrintArea->GetText();
+ OUString aStrRepeatRow = m_xEdRepeatRow->GetText();
+ OUString aStrRepeatCol = m_xEdRepeatCol->GetText();
+
+ bool bPrintAreaOk = true;
+ if ( !aStrPrintArea.isEmpty() )
+ {
+ const ScRefFlags nValidAddr = ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
+ const ScRefFlags nValidRange = nValidAddr | ScRefFlags::ROW2_VALID | ScRefFlags::COL2_VALID;
+ const formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+ const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
+
+ ScAddress aAddr;
+ ScRange aRange;
+ for ( sal_Int32 nIdx = 0; nIdx >= 0; )
+ {
+ const OUString aOne = aStrPrintArea.getToken(0, sep, nIdx);
+ ScRefFlags nResult = aRange.Parse( aOne, *pDoc, eConv );
+ if ((nResult & nValidRange) != nValidRange)
+ {
+ ScRefFlags nAddrResult = aAddr.Parse( aOne, *pDoc, eConv );
+ if ((nAddrResult & nValidAddr) != nValidAddr)
+ {
+ bPrintAreaOk = false;
+ break;
+ }
+ }
+ }
+ }
+
+ bool bRepeatRowOk = aStrRepeatRow.isEmpty();
+ if ( !bRepeatRowOk )
+ bRepeatRowOk = lcl_CheckRepeatString(aStrRepeatRow, *pDoc, true, nullptr);
+
+ bool bRepeatColOk = aStrRepeatCol.isEmpty();
+ if ( !bRepeatColOk )
+ bRepeatColOk = lcl_CheckRepeatString(aStrRepeatCol, *pDoc, false, nullptr);
+
+ // error messages
+
+ bOk = (bPrintAreaOk && bRepeatRowOk && bRepeatColOk);
+
+ if ( !bOk )
+ {
+ formula::RefEdit* pEd = nullptr;
+
+ if ( !bPrintAreaOk ) pEd = m_xEdPrintArea.get();
+ else if ( !bRepeatRowOk ) pEd = m_xEdRepeatRow.get();
+ else if ( !bRepeatColOk ) pEd = m_xEdRepeatCol.get();
+
+ ERRORBOX(m_xDialog.get(), STR_INVALID_TABREF);
+
+ OSL_ASSERT(pEd);
+
+ if (pEd)
+ pEd->GrabFocus();
+ }
+
+ return bOk;
+}
+
+void ScPrintAreasDlg::Impl_FillLists()
+{
+
+ // Get selection and remember String in PrintArea-ListBox
+
+ ScRange aRange;
+ OUString aStrRange;
+ bool bSimple = true;
+
+ if ( pViewData )
+ bSimple = (pViewData->GetSimpleArea( aRange ) == SC_MARK_SIMPLE);
+
+ formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();
+
+ if ( bSimple )
+ aStrRange = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
+ else
+ {
+ ScRangeListRef aList( new ScRangeList );
+ pViewData->GetMarkData().FillRangeListWithMarks( aList.get(), false );
+ aList->Format(aStrRange, ScRefFlags::RANGE_ABS, *pDoc, eConv);
+ }
+
+ m_xLbPrintArea->set_id(SC_AREASDLG_PR_SELECT, aStrRange);
+
+ // Get ranges and remember in ListBoxen
+
+ ScRangeName* pRangeNames = pDoc->GetRangeName();
+
+ if (!pRangeNames || pRangeNames->empty())
+ // No range names to process.
+ return;
+
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (!rEntry.second->HasType(ScRangeData::Type::AbsArea )
+ && !rEntry.second->HasType(ScRangeData::Type::RefArea)
+ && !rEntry.second->HasType(ScRangeData::Type::AbsPos ))
+ continue;
+
+ OUString aName = rEntry.second->GetName();
+ OUString aSymbol = rEntry.second->GetSymbol();
+ if (aRange.ParseAny(aSymbol, *pDoc, eConv) & ScRefFlags::VALID)
+ {
+ if (rEntry.second->HasType(ScRangeData::Type::PrintArea))
+ {
+ aSymbol = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS, eConv);
+ m_xLbPrintArea->append(aSymbol, aName);
+ }
+
+ if (rEntry.second->HasType(ScRangeData::Type::RowHeader))
+ {
+ lcl_GetRepeatRangeString(aRange, *pDoc, true, aSymbol);
+ m_xLbRepeatRow->append(aSymbol, aName);
+ }
+
+ if (rEntry.second->HasType(ScRangeData::Type::ColHeader))
+ {
+ lcl_GetRepeatRangeString(aRange, *pDoc, false, aSymbol);
+ m_xLbRepeatCol->append(aSymbol, aName);
+ }
+ }
+ }
+}
+
+// Handler:
+
+IMPL_LINK(ScPrintAreasDlg, Impl_BtnHdl, weld::Button&, rBtn, void)
+{
+ if (m_xBtnOk.get() == &rBtn)
+ {
+ if ( Impl_CheckRefStrings() )
+ {
+ SfxStringItem aPrintArea( SID_CHANGE_PRINTAREA, "" );
+ SfxStringItem aRepeatRow( FN_PARAM_2, "" );
+ SfxStringItem aRepeatCol( FN_PARAM_3, "" );
+
+ // Printing area changed?
+
+ // first try the list box, if "Entire sheet" is selected
+ bool bEntireSheet = (m_xLbPrintArea->get_active() == SC_AREASDLG_PR_ENTIRE);
+ SfxBoolItem aEntireSheet( FN_PARAM_4, bEntireSheet );
+
+ bool bDataChanged = bEntireSheet != pDoc->IsPrintEntireSheet( nCurTab );
+ if( !bEntireSheet )
+ {
+ // if new list box selection is not "Entire sheet", get the edit field contents
+ bDataChanged |= Impl_GetItem( m_xEdPrintArea.get(), aPrintArea );
+ }
+
+ // Repeat row changed?
+
+ bDataChanged |= Impl_GetItem( m_xEdRepeatRow.get(), aRepeatRow );
+
+ // Repeat column changed?
+
+ bDataChanged |= Impl_GetItem( m_xEdRepeatCol.get(), aRepeatCol );
+
+ if ( bDataChanged )
+ {
+ SetDispatcherLock( false );
+ SwitchToDocument();
+ GetBindings().GetDispatcher()->ExecuteList(SID_CHANGE_PRINTAREA,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aPrintArea, &aRepeatRow, &aRepeatCol, &aEntireSheet });
+ }
+
+ response(RET_OK);
+ }
+ }
+ else if (m_xBtnCancel.get() == &rBtn)
+ response(RET_CANCEL);
+}
+
+IMPL_LINK(ScPrintAreasDlg, Impl_GetEditFocusHdl, formula::RefEdit&, rCtrl, void)
+{
+ m_pRefInputEdit = &rCtrl;
+}
+
+IMPL_LINK(ScPrintAreasDlg, Impl_GetFocusHdl, weld::Widget&, rCtrl, void)
+{
+ if (&rCtrl == m_xLbPrintArea.get())
+ m_pRefInputEdit = m_xEdPrintArea.get();
+ else if (&rCtrl == m_xLbRepeatRow.get())
+ m_pRefInputEdit = m_xEdRepeatRow.get();
+ else if (&rCtrl == m_xLbRepeatCol.get())
+ m_pRefInputEdit = m_xEdRepeatCol.get();
+}
+
+IMPL_LINK( ScPrintAreasDlg, Impl_SelectHdl, weld::ComboBox&, rLb, void )
+{
+ const sal_Int32 nSelPos = rLb.get_active();
+ formula::RefEdit* pEd = nullptr;
+
+ // list box positions of specific entries, default to "repeat row/column" list boxes
+ sal_Int32 nAllSheetPos = SC_AREASDLG_RR_NONE;
+ sal_Int32 nFirstCustomPos = SC_AREASDLG_RR_OFFSET;
+
+ // find edit field for list box, and list box positions
+ if (&rLb == m_xLbPrintArea.get())
+ {
+ pEd = m_xEdPrintArea.get();
+ nAllSheetPos = SC_AREASDLG_PR_ENTIRE;
+ nFirstCustomPos = SC_AREASDLG_PR_SELECT; // "Selection" and following
+ }
+ else if (&rLb == m_xLbRepeatCol.get())
+ pEd = m_xEdRepeatCol.get();
+ else if (&rLb == m_xLbRepeatRow.get())
+ pEd = m_xEdRepeatRow.get();
+ else
+ return;
+
+ // fill edit field according to list box selection
+ if( (nSelPos == 0) || (nSelPos == nAllSheetPos) )
+ pEd->SetText( OUString() );
+ else if( nSelPos >= nFirstCustomPos )
+ pEd->SetText(rLb.get_id(nSelPos));
+}
+
+IMPL_LINK( ScPrintAreasDlg, Impl_ModifyHdl, formula::RefEdit&, rEd, void )
+{
+ weld::ComboBox* pLb = nullptr;
+
+ // list box positions of specific entries, default to "repeat row/column" list boxes
+ sal_Int32 nUserDefPos = SC_AREASDLG_RR_USER;
+ sal_Int32 nFirstCustomPos = SC_AREASDLG_RR_OFFSET;
+
+ if( &rEd == m_xEdPrintArea.get() )
+ {
+ pLb = m_xLbPrintArea.get();
+ nUserDefPos = SC_AREASDLG_PR_USER;
+ nFirstCustomPos = SC_AREASDLG_PR_SELECT; // "Selection" and following
+ }
+ else if( &rEd == m_xEdRepeatCol.get() )
+ pLb = m_xLbRepeatCol.get();
+ else if( &rEd == m_xEdRepeatRow.get() )
+ pLb = m_xLbRepeatRow.get();
+ else
+ return;
+
+ // set list box selection according to edit field
+ const sal_Int32 nEntryCount = pLb->get_count();
+ OUString aStrEd( rEd.GetText() );
+ OUString aEdUpper = aStrEd.toAsciiUpperCase();
+
+ if ( (nEntryCount > nFirstCustomPos) && !aStrEd.isEmpty() )
+ {
+ bool bFound = false;
+ sal_Int32 i;
+
+ for ( i=nFirstCustomPos; i<nEntryCount && !bFound; i++ )
+ {
+ const OUString& rSymbol = pLb->get_id(i);
+ bFound = (rSymbol == aStrEd || rSymbol == aEdUpper);
+ }
+
+ pLb->set_active( bFound ? i-1 : nUserDefPos );
+ }
+ else
+ pLb->set_active( !aStrEd.isEmpty() ? nUserDefPos : 0 );
+}
+
+// global functions:
+
+// TODO: It might make sense to move these functions to address.?xx. -kohei
+
+static bool lcl_CheckOne_OOO( const ScDocument& rDoc, const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
+{
+ // Allowed syntax for rStr:
+ // Row: [$]1-MAXTAB
+ // Col: [$]A-IV
+
+ OUString aStr = rStr;
+ sal_Int32 nLen = aStr.getLength();
+ SCCOLROW nNum = 0;
+ bool bStrOk = ( nLen > 0 ) && ( bIsRow ? ( nLen < 6 ) : ( nLen < 4 ) );
+
+ if ( bStrOk )
+ {
+ if ( '$' == aStr[0] )
+ aStr = aStr.copy( 1 );
+
+ if ( bIsRow )
+ {
+ bStrOk = CharClass::isAsciiNumeric(aStr);
+
+ if ( bStrOk )
+ {
+ sal_Int32 n = aStr.toInt32();
+
+ bStrOk = (n > 0) && ( n <= rDoc.GetSheetLimits().GetMaxRowCount() );
+ if ( bStrOk )
+ nNum = static_cast<SCCOLROW>(n - 1);
+ }
+ }
+ else
+ {
+ SCCOL nCol = 0;
+ bStrOk = ::AlphaToCol(rDoc, nCol, aStr);
+ nNum = nCol;
+ }
+ }
+
+ if ( bStrOk )
+ rVal = nNum;
+
+ return bStrOk;
+}
+
+static bool lcl_CheckOne_XL_A1( const ScDocument& rDoc, const OUString& rStr, bool bIsRow, SCCOLROW& rVal )
+{
+ // XL A1 style is identical to OOO one for print range formats.
+ return lcl_CheckOne_OOO(rDoc, rStr, bIsRow, rVal);
+}
+
+static bool lcl_CheckOne_XL_R1C1( const ScDocument& rDoc, std::u16string_view aStr, bool bIsRow, SCCOLROW& rVal )
+{
+ sal_Int32 nLen = aStr.size();
+ if (nLen <= 1)
+ // There must be at least two characters.
+ return false;
+
+ const sal_Unicode preUpper = bIsRow ? 'R' : 'C';
+ const sal_Unicode preLower = bIsRow ? 'r' : 'c';
+ if (aStr[0] != preUpper && aStr[0] != preLower)
+ return false;
+
+ std::u16string_view aNumStr = aStr.substr(1);
+ if (!CharClass::isAsciiNumeric(aNumStr))
+ return false;
+
+ sal_Int32 nNum = o3tl::toInt32(aNumStr);
+
+ if (nNum <= 0)
+ return false;
+
+ if ((bIsRow && nNum > rDoc.GetSheetLimits().GetMaxRowCount()) ||
+ (!bIsRow && nNum > rDoc.GetSheetLimits().GetMaxColCount()))
+ return false;
+
+ rVal = static_cast<SCCOLROW>(nNum-1);
+ return true;
+}
+
+static bool lcl_CheckRepeatOne( const ScDocument& rDoc, const OUString& rStr, formula::FormulaGrammar::AddressConvention eConv, bool bIsRow, SCCOLROW& rVal )
+{
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_OOO:
+ return lcl_CheckOne_OOO(rDoc, rStr, bIsRow, rVal);
+ case formula::FormulaGrammar::CONV_XL_A1:
+ return lcl_CheckOne_XL_A1(rDoc, rStr, bIsRow, rVal);
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ return lcl_CheckOne_XL_R1C1(rDoc, rStr, bIsRow, rVal);
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return false;
+}
+
+static bool lcl_CheckRepeatString( std::u16string_view aStr, const ScDocument& rDoc, bool bIsRow, ScRange* pRange )
+{
+ // Row: [valid row] rsep [valid row]
+ // Col: [valid col] rsep [valid col]
+
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ const sal_Unicode rsep = ScCompiler::GetNativeSymbolChar(ocRange);
+
+ if (pRange)
+ {
+ // initialize the range value.
+ pRange->aStart.SetCol(0);
+ pRange->aStart.SetRow(0);
+ pRange->aEnd.SetCol(0);
+ pRange->aEnd.SetRow(0);
+ }
+
+ OUString aBuf;
+ SCCOLROW nVal = 0;
+ sal_Int32 nLen = aStr.size();
+ bool bEndPos = false;
+ for( sal_Int32 i = 0; i < nLen; ++i )
+ {
+ const sal_Unicode c = aStr[i];
+ if (c == rsep)
+ {
+ if (bEndPos)
+ // We aren't supposed to have more than one range separator.
+ return false;
+
+ // range separator
+ if (aBuf.isEmpty())
+ return false;
+
+ bool bRes = lcl_CheckRepeatOne(rDoc, aBuf, eConv, bIsRow, nVal);
+ if (!bRes)
+ return false;
+
+ if (pRange)
+ {
+ if (bIsRow)
+ {
+ pRange->aStart.SetRow(static_cast<SCROW>(nVal));
+ pRange->aEnd.SetRow(static_cast<SCROW>(nVal));
+ }
+ else
+ {
+ pRange->aStart.SetCol(static_cast<SCCOL>(nVal));
+ pRange->aEnd.SetCol(static_cast<SCCOL>(nVal));
+ }
+ }
+
+ aBuf.clear();
+ bEndPos = true;
+ }
+ else
+ aBuf += OUStringChar(c);
+ }
+
+ if (!aBuf.isEmpty())
+ {
+ bool bRes = lcl_CheckRepeatOne(rDoc, aBuf, eConv, bIsRow, nVal);
+ if (!bRes)
+ return false;
+
+ if (pRange)
+ {
+ if (bIsRow)
+ {
+ if (!bEndPos)
+ pRange->aStart.SetRow(static_cast<SCROW>(nVal));
+ pRange->aEnd.SetRow(static_cast<SCROW>(nVal));
+ }
+ else
+ {
+ if (!bEndPos)
+ pRange->aStart.SetCol(static_cast<SCCOL>(nVal));
+ pRange->aEnd.SetCol(static_cast<SCCOL>(nVal));
+ }
+ }
+ }
+
+ return true;
+}
+
+static void lcl_GetRepeatRangeString( std::optional<ScRange> oRange, const ScDocument& rDoc, bool bIsRow, OUString& rStr )
+{
+ rStr.clear();
+ if (!oRange)
+ return;
+
+ const formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ const ScAddress& rStart = oRange->aStart;
+ const ScAddress& rEnd = oRange->aEnd;
+
+ const ScRefFlags nFmt = bIsRow
+ ? (ScRefFlags::ROW_VALID | ScRefFlags::ROW_ABS)
+ : (ScRefFlags::COL_VALID | ScRefFlags::COL_ABS);
+ rStr += rStart.Format(nFmt, &rDoc, eConv);
+ if ((bIsRow && rStart.Row() != rEnd.Row()) || (!bIsRow && rStart.Col() != rEnd.Col()))
+ {
+ rStr += ScCompiler::GetNativeSymbol(ocRange);
+ rStr += rEnd.Format(nFmt, &rDoc, eConv);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/pagedlg/hfedtdlg.cxx b/sc/source/ui/pagedlg/hfedtdlg.cxx
new file mode 100644
index 0000000000..6b873e8f57
--- /dev/null
+++ b/sc/source/ui/pagedlg/hfedtdlg.cxx
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+
+#include <hfedtdlg.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scuitphfedit.hxx>
+#include <svx/svxids.hrc>
+#include <svx/pageitem.hxx>
+
+ScHFEditDlg::ScHFEditDlg(weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle,
+ const OUString& rUIXMLDescription,
+ const OUString& rID)
+ : SfxTabDialogController(pParent, rUIXMLDescription, rID, &rCoreSet)
+{
+ eNumType = rCoreSet.Get(ATTR_PAGE).GetNumType();
+
+ OUString aTmp = m_xDialog->get_title() +
+ " (" + ScResId( STR_PAGESTYLE ) + ": " + rPageStyle + ")";
+
+ m_xDialog->set_title(aTmp);
+}
+
+ScHFEditHeaderDlg::ScHFEditHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/headerdialog.ui", "HeaderDialog")
+{
+ AddTabPage("headerfirst", ScFirstHeaderEditPage::Create, nullptr);
+ AddTabPage("headerright", ScRightHeaderEditPage::Create, nullptr);
+ AddTabPage("headerleft", ScLeftHeaderEditPage::Create, nullptr);
+}
+
+ScHFEditFooterDlg::ScHFEditFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/footerdialog.ui", "FooterDialog" )
+{
+ AddTabPage("footerfirst", ScFirstFooterEditPage::Create, nullptr);
+ AddTabPage("footerright", ScRightFooterEditPage::Create, nullptr);
+ AddTabPage("footerleft", ScLeftFooterEditPage::Create, nullptr);
+}
+
+ScHFEditSharedFirstHeaderDlg::ScHFEditSharedFirstHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/sharedfirstheaderdialog.ui", "SharedFirstHeaderDialog" )
+{
+ AddTabPage("headerright", ScRightHeaderEditPage::Create, nullptr);
+ AddTabPage("headerleft", ScLeftHeaderEditPage::Create, nullptr);
+}
+
+ScHFEditSharedFirstFooterDlg::ScHFEditSharedFirstFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/sharedfirstfooterdialog.ui", "SharedFirstFooterDialog" )
+{
+ AddTabPage("footerright", ScRightFooterEditPage::Create, nullptr);
+ AddTabPage("footerleft", ScLeftFooterEditPage::Create, nullptr);
+}
+
+ScHFEditSharedLeftHeaderDlg::ScHFEditSharedLeftHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/sharedleftheaderdialog.ui", "SharedLeftHeaderDialog" )
+{
+ AddTabPage("headerfirst", ScFirstHeaderEditPage::Create, nullptr);
+ AddTabPage("headerright", ScRightHeaderEditPage::Create, nullptr);
+}
+
+ScHFEditSharedLeftFooterDlg::ScHFEditSharedLeftFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/sharedleftfooterdialog.ui", "SharedLeftFooterDialog" )
+{
+ AddTabPage("footerfirst", ScFirstFooterEditPage::Create, nullptr);
+ AddTabPage("footerright", ScRightFooterEditPage::Create, nullptr);
+}
+
+ScHFEditFirstHeaderDlg::ScHFEditFirstHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/firstheaderdialog.ui", "FirstHeaderDialog" )
+{
+ AddTabPage("headerfirst", ScFirstHeaderEditPage::Create, nullptr);
+}
+
+ScHFEditLeftHeaderDlg::ScHFEditLeftHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/leftheaderdialog.ui", "LeftHeaderDialog" )
+{
+ AddTabPage("headerleft", ScLeftHeaderEditPage::Create, nullptr);
+}
+
+ScHFEditRightHeaderDlg::ScHFEditRightHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/rightheaderdialog.ui", "RightHeaderDialog" )
+{
+ AddTabPage("headerright", ScRightHeaderEditPage::Create, nullptr);
+}
+
+ScHFEditFirstFooterDlg::ScHFEditFirstFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/firstfooterdialog.ui", "FirstFooterDialog" )
+{
+ AddTabPage("footerfirst", ScFirstFooterEditPage::Create, nullptr);
+}
+
+ScHFEditLeftFooterDlg::ScHFEditLeftFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/leftfooterdialog.ui", "LeftFooterDialog" )
+{
+ AddTabPage("footerleft", ScLeftFooterEditPage::Create, nullptr);
+}
+
+ScHFEditRightFooterDlg::ScHFEditRightFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/rightfooterdialog.ui", "RightFooterDialog" )
+{
+ AddTabPage("footerright", ScRightFooterEditPage::Create, nullptr);
+}
+
+ScHFEditSharedHeaderDlg::ScHFEditSharedHeaderDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/sharedheaderdialog.ui", "SharedHeaderDialog" )
+{
+ AddTabPage("headerfirst", ScFirstHeaderEditPage::Create, nullptr);
+ AddTabPage("header", ScRightHeaderEditPage::Create, nullptr);
+ AddTabPage("footerfirst", ScFirstFooterEditPage::Create, nullptr);
+ AddTabPage("footerright", ScRightFooterEditPage::Create, nullptr);
+ AddTabPage("footerleft", ScLeftFooterEditPage::Create, nullptr);
+}
+
+ScHFEditSharedFooterDlg::ScHFEditSharedFooterDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/sharedfooterdialog.ui", "SharedFooterDialog" )
+{
+ AddTabPage("headerfirst", ScFirstFooterEditPage::Create, nullptr);
+ AddTabPage("headerright", ScRightHeaderEditPage::Create, nullptr);
+ AddTabPage("headerleft", ScLeftHeaderEditPage::Create, nullptr);
+ AddTabPage("footerfirst", ScFirstFooterEditPage::Create, nullptr);
+ AddTabPage("footer", ScRightFooterEditPage::Create, nullptr);
+}
+
+ScHFEditAllDlg::ScHFEditAllDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/allheaderfooterdialog.ui", "AllHeaderFooterDialog" )
+{
+ AddTabPage("headerfirst", ScFirstHeaderEditPage::Create, nullptr);
+ AddTabPage("headerright", ScRightHeaderEditPage::Create, nullptr);
+ AddTabPage("headerleft", ScLeftHeaderEditPage::Create, nullptr);
+ AddTabPage("footerfirst", ScFirstFooterEditPage::Create, nullptr);
+ AddTabPage("footerright", ScRightFooterEditPage::Create, nullptr);
+ AddTabPage("footerleft", ScLeftFooterEditPage::Create, nullptr);
+}
+
+ScHFEditActiveDlg::ScHFEditActiveDlg(
+ weld::Window* pParent,
+ const SfxItemSet& rCoreSet,
+ std::u16string_view rPageStyle)
+ : ScHFEditDlg( pParent, rCoreSet, rPageStyle,
+ "modules/scalc/ui/headerfooterdialog.ui", "HeaderFooterDialog" )
+{
+ const SvxPageItem& rPageItem =
+ rCoreSet.Get(
+ rCoreSet.GetPool()->GetWhich(SID_ATTR_PAGE) );
+
+ bool bRightPage = SvxPageUsage::Left != rPageItem.GetPageUsage();
+
+ if ( bRightPage )
+ {
+ AddTabPage("header", ScRightHeaderEditPage::Create, nullptr);
+ AddTabPage("footer", ScRightFooterEditPage::Create, nullptr);
+ }
+ else
+ {
+ // #69193a# respect "shared" setting
+
+ bool bShareHeader = rCoreSet.Get(ATTR_PAGE_HEADERSET).GetItemSet().
+ Get(ATTR_PAGE_SHARED).GetValue();
+ if ( bShareHeader )
+ AddTabPage("header", ScRightHeaderEditPage::Create, nullptr);
+ else
+ AddTabPage("header", ScLeftHeaderEditPage::Create, nullptr);
+
+ bool bShareFooter = rCoreSet.Get(ATTR_PAGE_FOOTERSET).GetItemSet().
+ Get(ATTR_PAGE_SHARED).GetValue();
+ if ( bShareFooter )
+ AddTabPage("footer", ScRightFooterEditPage::Create, nullptr);
+ else
+ AddTabPage("footer", ScLeftFooterEditPage::Create, nullptr);
+ }
+}
+
+void ScHFEditDlg::PageCreated(const OUString& /* rId */, SfxTabPage& rPage)
+{
+ // Can only be a ScHFEditPage...
+
+ static_cast<ScHFEditPage&>(rPage).SetNumType(eNumType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/pagedlg/scuitphfedit.cxx b/sc/source/ui/pagedlg/scuitphfedit.cxx
new file mode 100644
index 0000000000..d82c923e56
--- /dev/null
+++ b/sc/source/ui/pagedlg/scuitphfedit.cxx
@@ -0,0 +1,856 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <osl/time.h>
+#include <sfx2/tabdlg.hxx>
+#include <vcl/settings.hxx>
+
+#include <unotools/useroptions.hxx>
+
+#include <editutil.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+
+#include <scuitphfedit.hxx>
+#include <memory>
+
+
+ScHFEditPage::ScHFEditPage(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet& rCoreAttrs,
+ TypedWhichId<ScPageHFItem> nWhichId,
+ bool bHeader)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/headerfootercontent.ui", "HeaderFooterContent", &rCoreAttrs)
+ , nWhich( nWhichId )
+ , m_bDropDownActive(false)
+ , m_nTimeToggled(-1)
+ , m_xFtDefinedHF(m_xBuilder->weld_label(!bHeader ? "labelFT_F_DEFINED" : "labelFT_H_DEFINED"))
+ , m_xLbDefined(m_xBuilder->weld_combo_box("comboLB_DEFINED"))
+ , m_xFtCustomHF(m_xBuilder->weld_label(!bHeader ? "labelFT_F_CUSTOM" : "labelFT_H_CUSTOM"))
+ , m_xBtnText(m_xBuilder->weld_button("buttonBTN_TEXT"))
+ , m_xBtnFile(m_xBuilder->weld_menu_button("buttonBTN_FILE"))
+ , m_xBtnTable(m_xBuilder->weld_button("buttonBTN_TABLE"))
+ , m_xBtnPage(m_xBuilder->weld_button("buttonBTN_PAGE"))
+ , m_xBtnLastPage(m_xBuilder->weld_button("buttonBTN_PAGES"))
+ , m_xBtnDate(m_xBuilder->weld_button("buttonBTN_DATE"))
+ , m_xBtnTime(m_xBuilder->weld_button("buttonBTN_TIME"))
+ , m_xFtConfidential(m_xBuilder->weld_label("labelSTR_HF_CONFIDENTIAL"))
+ , m_xFtPage(m_xBuilder->weld_label("labelSTR_PAGE"))
+ , m_xFtOfQuestion(m_xBuilder->weld_label("labelSTR_HF_OF_QUESTION"))
+ , m_xFtOf(m_xBuilder->weld_label("labelSTR_HF_OF"))
+ , m_xFtNone(m_xBuilder->weld_label("labelSTR_HF_NONE_IN_BRACKETS"))
+ , m_xFtCreatedBy(m_xBuilder->weld_label("labelSTR_HF_CREATED_BY"))
+ , m_xFtCustomized(m_xBuilder->weld_label("labelSTR_HF_CUSTOMIZED"))
+ , m_xLeft(m_xBuilder->weld_widget("labelFT_LEFT"))
+ , m_xRight(m_xBuilder->weld_widget("labelFT_RIGHT"))
+ , m_xWndLeft(new ScEditWindow(Left, pController->getDialog()))
+ , m_xWndCenter(new ScEditWindow(Center, pController->getDialog()))
+ , m_xWndRight(new ScEditWindow(Right, pController->getDialog()))
+ , m_xWndLeftWnd(new weld::CustomWeld(*m_xBuilder, "textviewWND_LEFT", *m_xWndLeft))
+ , m_xWndCenterWnd(new weld::CustomWeld(*m_xBuilder, "textviewWND_CENTER", *m_xWndCenter))
+ , m_xWndRightWnd(new weld::CustomWeld(*m_xBuilder, "textviewWND_RIGHT", *m_xWndRight))
+ , m_pEditFocus(nullptr)
+{
+ // tdf#114695 override natural size with a small value
+ // we expect this to get stretched to some larger but
+ // limited size based on surrounding widgets
+ m_xLbDefined->set_size_request(m_xLbDefined->get_approximate_digit_width() * 20, -1);
+
+ //! use default style from current document?
+ //! if font color is used, header/footer background color must be set
+
+ ScPatternAttr aPatAttr( rCoreAttrs.GetPool() );
+
+ m_xLbDefined->connect_popup_toggled( LINK( this, ScHFEditPage, ListToggleHdl_Impl) );
+ m_xLbDefined->connect_changed( LINK( this, ScHFEditPage, ListHdl_Impl ) );
+ m_xBtnFile->connect_selected( LINK( this, ScHFEditPage, MenuHdl ) );
+ m_xBtnText->connect_clicked( LINK( this, ScHFEditPage, ClickHdl ) );
+ m_xBtnPage->connect_clicked( LINK( this, ScHFEditPage, ClickHdl ) );
+ m_xBtnLastPage->connect_clicked( LINK( this, ScHFEditPage, ClickHdl ) );
+ m_xBtnDate->connect_clicked( LINK( this, ScHFEditPage, ClickHdl ) );
+ m_xBtnTime->connect_clicked( LINK( this, ScHFEditPage, ClickHdl ) );
+ m_xBtnTable->connect_clicked( LINK( this, ScHFEditPage, ClickHdl ) );
+
+ m_xFtDefinedHF->show();
+ m_xFtCustomHF->show();
+
+ //swap left/right areas and their labels in RTL mode
+ if( AllSettings::GetLayoutRTL() )
+ {
+ sal_Int32 nOldLeftAttach = m_xLeft->get_grid_left_attach();
+ sal_Int32 nOldRightAttach = m_xRight->get_grid_left_attach();
+ m_xLeft->set_grid_left_attach(nOldRightAttach);
+ m_xRight->set_grid_left_attach(nOldLeftAttach);
+
+ nOldLeftAttach = m_xWndLeftWnd->get_grid_left_attach();
+ nOldRightAttach = m_xWndRightWnd->get_grid_left_attach();
+ m_xWndLeftWnd->set_grid_left_attach(nOldRightAttach);
+ m_xWndRightWnd->set_grid_left_attach(nOldLeftAttach);
+ }
+ m_xWndLeft->SetFont( aPatAttr );
+ m_xWndCenter->SetFont( aPatAttr );
+ m_xWndRight->SetFont( aPatAttr );
+
+ m_xWndLeft->SetObjectSelectHdl( LINK(this,ScHFEditPage,ObjectSelectHdl) );
+ m_xWndCenter->SetObjectSelectHdl( LINK(this,ScHFEditPage,ObjectSelectHdl) );
+ m_xWndRight->SetObjectSelectHdl( LINK(this,ScHFEditPage,ObjectSelectHdl) );
+ auto setEditFocus = [this](ScEditWindow & rEdit) { this->m_pEditFocus = &rEdit; };
+ m_xWndLeft->SetGetFocusHdl(setEditFocus);
+ m_xWndCenter->SetGetFocusHdl(setEditFocus);
+ m_xWndRight->SetGetFocusHdl(setEditFocus);
+
+ m_xWndLeft->GrabFocus();
+ m_pEditFocus = m_xWndLeft.get(); // there's no event from grab_focus()
+
+ InitPreDefinedList();
+
+}
+
+IMPL_LINK_NOARG( ScHFEditPage, ObjectSelectHdl, ScEditWindow&, void )
+{
+ m_xBtnText->grab_focus();
+}
+
+ScHFEditPage::~ScHFEditPage()
+{
+}
+
+void ScHFEditPage::SetNumType(SvxNumType eNumType)
+{
+ m_xWndLeft->SetNumType(eNumType);
+ m_xWndCenter->SetNumType(eNumType);
+ m_xWndRight->SetNumType(eNumType);
+}
+
+void ScHFEditPage::Reset( const SfxItemSet* rCoreSet )
+{
+ const ScPageHFItem* pItem = rCoreSet->GetItemIfSet(nWhich);
+ if ( !pItem )
+ return;
+
+ if( const EditTextObject* pLeft = pItem->GetLeftArea() )
+ m_xWndLeft->SetText( *pLeft );
+ if( const EditTextObject* pCenter = pItem->GetCenterArea() )
+ m_xWndCenter->SetText( *pCenter );
+ if( const EditTextObject* pRight = pItem->GetRightArea() )
+ m_xWndRight->SetText( *pRight );
+
+ SetSelectDefinedList();
+}
+
+bool ScHFEditPage::FillItemSet( SfxItemSet* rCoreSet )
+{
+ ScPageHFItem aItem( nWhich );
+ std::unique_ptr<EditTextObject> pLeft = m_xWndLeft->CreateTextObject();
+ std::unique_ptr<EditTextObject> pCenter = m_xWndCenter->CreateTextObject();
+ std::unique_ptr<EditTextObject> pRight = m_xWndRight->CreateTextObject();
+
+ aItem.SetLeftArea ( *pLeft );
+ aItem.SetCenterArea( *pCenter );
+ aItem.SetRightArea ( *pRight );
+
+ rCoreSet->Put( aItem );
+
+ return true;
+}
+
+void ScHFEditPage::InitPreDefinedList()
+{
+ SvtUserOptions aUserOpt;
+
+ std::optional<Color> pTxtColour;
+ std::optional<Color> pFldColour;
+ std::optional<FontLineStyle> pFldLineStyle;
+
+ // Get the all field values at the outset.
+ OUString aPageFieldValue(m_xWndLeft->GetEditEngine()->CalcFieldValue(SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), 0,0, pTxtColour, pFldColour, pFldLineStyle));
+ OUString aSheetFieldValue(m_xWndLeft->GetEditEngine()->CalcFieldValue(SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), 0,0, pTxtColour, pFldColour, pFldLineStyle));
+ OUString aFileFieldValue(m_xWndLeft->GetEditEngine()->CalcFieldValue(SvxFieldItem(SvxFileField(), EE_FEATURE_FIELD), 0,0, pTxtColour, pFldColour, pFldLineStyle));
+ OUString aExtFileFieldValue(m_xWndLeft->GetEditEngine()->CalcFieldValue(SvxFieldItem(SvxExtFileField(), EE_FEATURE_FIELD), 0,0, pTxtColour, pFldColour, pFldLineStyle));
+ OUString aDateFieldValue(m_xWndLeft->GetEditEngine()->CalcFieldValue(SvxFieldItem(SvxDateField(), EE_FEATURE_FIELD), 0,0, pTxtColour, pFldColour, pFldLineStyle));
+
+ m_xLbDefined->clear();
+
+ m_xLbDefined->append_text(m_xFtNone->get_label());
+
+ OUString aPageEntry(m_xFtPage->get_label() + " " + aPageFieldValue);
+ m_xLbDefined->append_text(aPageEntry);
+
+ OUString aPageOfEntry(aPageEntry + " " + m_xFtOfQuestion->get_label());
+ m_xLbDefined->append_text( aPageOfEntry);
+
+ m_xLbDefined->append_text(aSheetFieldValue);
+
+ OUString aConfidentialEntry(aUserOpt.GetCompany() + " " + m_xFtConfidential->get_label() + ", " + aDateFieldValue + ", " + aPageEntry);
+ m_xLbDefined->append_text( aConfidentialEntry);
+
+ OUString aFileNamePageEntry(aFileFieldValue + ", " + aPageEntry);
+ m_xLbDefined->append_text( aFileNamePageEntry);
+
+ m_xLbDefined->append_text( aExtFileFieldValue);
+
+ OUString aPageSheetNameEntry(aPageEntry + ", " + aSheetFieldValue);
+ m_xLbDefined->append_text( aPageSheetNameEntry);
+
+ OUString aPageFileNameEntry(aPageEntry + ", " + aFileFieldValue);
+ m_xLbDefined->append_text( aPageFileNameEntry);
+
+ OUString aPagePathNameEntry(aPageEntry + ", " + aExtFileFieldValue);
+ m_xLbDefined->append_text( aPagePathNameEntry);
+
+ OUString aUserNameEntry(aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName() + ", " + aPageEntry + ", " + aDateFieldValue);
+ m_xLbDefined->append_text( aUserNameEntry);
+
+ OUString aCreatedByEntry = m_xFtCreatedBy->get_label() + " " + aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName() + ", " +
+ aDateFieldValue + ", " + aPageEntry;
+ m_xLbDefined->append_text( aCreatedByEntry);
+}
+
+void ScHFEditPage::InsertToDefinedList()
+{
+ const sal_Int32 nCount = m_xLbDefined->get_count();
+ if(nCount == eEntryCount)
+ {
+ m_xLbDefined->append_text( m_xFtCustomized->get_label() );
+ m_xLbDefined->set_active(eEntryCount);
+ }
+}
+
+void ScHFEditPage::RemoveFromDefinedList()
+{
+ const sal_Int32 nCount = m_xLbDefined->get_count();
+ if(nCount > eEntryCount )
+ m_xLbDefined->remove( nCount-1);
+}
+
+// determine if the header/footer exists in our predefined list and set select to it.
+void ScHFEditPage::SetSelectDefinedList()
+{
+ SvtUserOptions aUserOpt;
+
+ // default to customized
+ ScHFEntryId eSelectEntry = eEntryCount;
+
+ std::unique_ptr< EditTextObject > pLeftObj;
+ std::unique_ptr< EditTextObject > pCenterObj;
+ std::unique_ptr< EditTextObject > pRightObj;
+
+ OUString aLeftEntry;
+ OUString aCenterEntry;
+ OUString aRightEntry;
+
+ pLeftObj = m_xWndLeft->GetEditEngine()->CreateTextObject();
+ pCenterObj = m_xWndCenter->GetEditEngine()->CreateTextObject();
+ pRightObj = m_xWndRight->GetEditEngine()->CreateTextObject();
+
+ bool bFound = false;
+
+ const sal_Int32 nCount = m_xLbDefined->get_count();
+ for(sal_Int32 i = 0; i < nCount && !bFound; ++i)
+ {
+ switch(static_cast<ScHFEntryId>(i))
+ {
+ case eNoneEntry:
+ {
+ aLeftEntry = pLeftObj->GetText(0);
+ aCenterEntry = pCenterObj->GetText(0);
+ aRightEntry = pRightObj->GetText(0);
+ if(aLeftEntry.isEmpty() && aCenterEntry.isEmpty()
+ && aRightEntry.isEmpty())
+ {
+ eSelectEntry = eNoneEntry;
+ bFound = true;
+ }
+ }
+ break;
+
+ case ePageEntry:
+ {
+ aLeftEntry = pLeftObj->GetText(0);
+ aRightEntry = pRightObj->GetText(0);
+ if(aLeftEntry.isEmpty() && aRightEntry.isEmpty())
+ {
+ if(IsPageEntry(m_xWndCenter->GetEditEngine(), pCenterObj.get()))
+ {
+ eSelectEntry = ePageEntry;
+ bFound = true;
+ }
+ }
+ }
+ break;
+
+ //TODO
+ case ePagesEntry:
+ {
+ }
+ break;
+
+ case eSheetEntry:
+ {
+ aLeftEntry = pLeftObj->GetText(0);
+ aRightEntry = pRightObj->GetText(0);
+ if(aLeftEntry.isEmpty() && aRightEntry.isEmpty())
+ {
+ if(pCenterObj->IsFieldObject())
+ {
+ const SvxFieldItem* pFieldItem = pCenterObj->GetField();
+ if(pFieldItem)
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if(dynamic_cast<const SvxTableField*>( pField))
+ {
+ eSelectEntry = eSheetEntry;
+ bFound = true;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case eConfidentialEntry:
+ {
+ if(IsDateEntry(pCenterObj.get()) && IsPageEntry(m_xWndRight->GetEditEngine(), pRightObj.get()))
+ {
+ OUString aConfidentialEntry(aUserOpt.GetCompany() + " " + m_xFtConfidential->get_label());
+ if(aConfidentialEntry == m_xWndLeft->GetEditEngine()->GetText(0))
+ {
+ eSelectEntry = eConfidentialEntry;
+ bFound = true;
+ }
+ }
+ }
+ break;
+
+ //TODO
+ case eFileNamePageEntry:
+ {
+ }
+ break;
+
+ case eExtFileNameEntry:
+ {
+ aLeftEntry = pLeftObj->GetText(0);
+ aRightEntry = pRightObj->GetText(0);
+ if(IsExtFileNameEntry(pCenterObj.get()) && aLeftEntry.isEmpty()
+ && aRightEntry.isEmpty())
+ {
+ eSelectEntry = eExtFileNameEntry;
+ bFound = true;
+ }
+ }
+ break;
+
+ //TODO
+ case ePageSheetEntry:
+ {
+ }
+ break;
+
+ //TODO
+ case ePageFileNameEntry:
+ {
+ }
+ break;
+
+ case ePageExtFileNameEntry:
+ {
+ aLeftEntry = pLeftObj->GetText(0);
+ if(IsPageEntry(m_xWndCenter->GetEditEngine(), pCenterObj.get()) &&
+ IsExtFileNameEntry(pRightObj.get()) && aLeftEntry.isEmpty())
+ {
+ eSelectEntry = ePageExtFileNameEntry;
+ bFound = true;
+ }
+ }
+ break;
+
+ case eUserNameEntry:
+ {
+ if(IsDateEntry(pRightObj.get()) && IsPageEntry(m_xWndCenter->GetEditEngine(), pCenterObj.get()))
+ {
+ OUString aUserNameEntry(aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName());
+
+ if(aUserNameEntry == m_xWndLeft->GetEditEngine()->GetText(0))
+ {
+ eSelectEntry = eUserNameEntry;
+ bFound = true;
+ }
+ }
+ }
+ break;
+
+ case eCreatedByEntry:
+ {
+ if(IsDateEntry(pCenterObj.get()) && IsPageEntry(m_xWndRight->GetEditEngine(), pRightObj.get()))
+ {
+ OUString aCreatedByEntry(m_xFtCreatedBy->get_label() + " " + aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName());
+
+ if(aCreatedByEntry == m_xWndLeft->GetEditEngine()->GetText(0))
+ {
+ eSelectEntry = eCreatedByEntry;
+ bFound = true;
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if(eSelectEntry == eEntryCount)
+ InsertToDefinedList();
+
+ m_xLbDefined->set_active( sal::static_int_cast<sal_uInt16>( eSelectEntry ) );
+}
+
+bool ScHFEditPage::IsPageEntry(EditEngine*pEngine, const EditTextObject* pTextObj)
+{
+ if(!pEngine || !pTextObj)
+ return false;
+
+ bool bReturn = false;
+
+ if(!pTextObj->IsFieldObject())
+ {
+ std::vector<sal_Int32> aPosList;
+ pEngine->GetPortions(0,aPosList);
+ if(aPosList.size() == 2)
+ {
+ OUString aPageEntry(m_xFtPage->get_label() + " ");
+ ESelection aSel(0,0,0,0);
+ aSel.nEndPos = aPageEntry.getLength();
+ if(aPageEntry == pEngine->GetText(aSel))
+ {
+ aSel.nStartPos = aSel.nEndPos;
+ aSel.nEndPos++;
+ std::unique_ptr< EditTextObject > pPageObj = pEngine->CreateTextObject(aSel);
+ if(pPageObj && pPageObj->IsFieldObject() )
+ {
+ const SvxFieldItem* pFieldItem = pPageObj->GetField();
+ if(pFieldItem)
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if(dynamic_cast<const SvxPageField*>( pField))
+ bReturn = true;
+ }
+ }
+ }
+ }
+ }
+ return bReturn;
+}
+
+bool ScHFEditPage::IsDateEntry(const EditTextObject* pTextObj)
+{
+ if(!pTextObj)
+ return false;
+
+ bool bReturn = false;
+ if(pTextObj->IsFieldObject())
+ {
+ const SvxFieldItem* pFieldItem = pTextObj->GetField();
+ if(pFieldItem)
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if(dynamic_cast<const SvxDateField*>( pField))
+ bReturn = true;
+ }
+ }
+ return bReturn;
+}
+
+bool ScHFEditPage::IsExtFileNameEntry(const EditTextObject* pTextObj)
+{
+ if(!pTextObj)
+ return false;
+ bool bReturn = false;
+ if(pTextObj->IsFieldObject())
+ {
+ const SvxFieldItem* pFieldItem = pTextObj->GetField();
+ if(pFieldItem)
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if(dynamic_cast<const SvxExtFileField*>( pField))
+ bReturn = true;
+ }
+ }
+ return bReturn;
+}
+
+void ScHFEditPage::ProcessDefinedListSel(ScHFEntryId eSel, bool bTravelling)
+{
+ SvtUserOptions aUserOpt;
+ std::unique_ptr< EditTextObject > pTextObj;
+
+ switch(eSel)
+ {
+ case eNoneEntry:
+ ClearTextAreas();
+ if(!bTravelling)
+ m_xWndLeft->GrabFocus();
+ break;
+
+ case ePageEntry:
+ {
+ ClearTextAreas();
+ OUString aPageEntry( m_xFtPage->get_label() + " " );
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ m_xWndCenter->InsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD) );
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ }
+ break;
+
+ case ePagesEntry:
+ {
+ ClearTextAreas();
+ ESelection aSel(0,0,0,0);
+ OUString aPageEntry( m_xFtPage->get_label() + " ");
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ aSel.nEndPos = aPageEntry.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField(SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ ++aSel.nEndPos;
+
+ OUString aPageOfEntry(" " + m_xFtOf->get_label() + " ");
+ m_xWndCenter->GetEditEngine()->QuickInsertText(aPageOfEntry,ESelection(aSel.nEndPara,aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ aSel.nEndPos = aSel.nEndPos + aPageOfEntry.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField(SvxFieldItem(SvxPagesField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara,aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ pTextObj = m_xWndCenter->GetEditEngine()->CreateTextObject();
+ m_xWndCenter->SetText(*pTextObj);
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ }
+ break;
+
+ case eSheetEntry:
+ ClearTextAreas();
+ m_xWndCenter->InsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD) );
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ break;
+
+ case eConfidentialEntry:
+ {
+ ClearTextAreas();
+ OUString aConfidentialEntry(aUserOpt.GetCompany() + " " + m_xFtConfidential->get_label());
+ m_xWndLeft->GetEditEngine()->SetTextCurrentDefaults(aConfidentialEntry);
+ m_xWndCenter->InsertField( SvxFieldItem(SvxDateField(Date( Date::SYSTEM ),SvxDateType::Var), EE_FEATURE_FIELD) );
+
+ OUString aPageEntry( m_xFtPage->get_label() + " ");
+ m_xWndRight->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ m_xWndRight->InsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD) );
+ if(!bTravelling)
+ m_xWndRight->GrabFocus();
+ }
+ break;
+
+ case eFileNamePageEntry:
+ {
+ ClearTextAreas();
+ ESelection aSel(0,0,0,0);
+ m_xWndCenter->GetEditEngine()->QuickInsertField(SvxFieldItem( SvxFileField(), EE_FEATURE_FIELD ), aSel );
+ ++aSel.nEndPos;
+ OUString aPageEntry(", " + m_xFtPage->get_label() + " ");
+ m_xWndCenter->GetEditEngine()->QuickInsertText(aPageEntry, ESelection(aSel.nEndPara,aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ aSel.nStartPos = aSel.nEndPos;
+ aSel.nEndPos = aSel.nEndPos + aPageEntry.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField(SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara,aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ pTextObj = m_xWndCenter->GetEditEngine()->CreateTextObject();
+ m_xWndCenter->SetText(*pTextObj);
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ }
+ break;
+
+ case eExtFileNameEntry:
+ ClearTextAreas();
+ m_xWndCenter->InsertField( SvxFieldItem( SvxExtFileField(
+ OUString(), SvxFileType::Var, SvxFileFormat::PathFull ), EE_FEATURE_FIELD ) );
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ break;
+
+ case ePageSheetEntry:
+ {
+ ClearTextAreas();
+ ESelection aSel(0,0,0,0);
+ OUString aPageEntry( m_xFtPage->get_label() + " " );
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ aSel.nEndPos = aPageEntry.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField(SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ ++aSel.nEndPos;
+
+ OUString aCommaSpace(", ");
+ m_xWndCenter->GetEditEngine()->QuickInsertText(aCommaSpace,ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ aSel.nEndPos = aSel.nEndPos + aCommaSpace.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ pTextObj = m_xWndCenter->GetEditEngine()->CreateTextObject();
+ m_xWndCenter->SetText(*pTextObj);
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ }
+ break;
+
+ case ePageFileNameEntry:
+ {
+ ClearTextAreas();
+ ESelection aSel(0,0,0,0);
+ OUString aPageEntry( m_xFtPage->get_label() + " " );
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ aSel.nEndPos = aPageEntry.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField(SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ ++aSel.nEndPos;
+ OUString aCommaSpace(", ");
+ m_xWndCenter->GetEditEngine()->QuickInsertText(aCommaSpace,ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ aSel.nEndPos = aSel.nEndPos + aCommaSpace.getLength();
+ m_xWndCenter->GetEditEngine()->QuickInsertField( SvxFieldItem(SvxFileField(), EE_FEATURE_FIELD), ESelection(aSel.nEndPara, aSel.nEndPos, aSel.nEndPara, aSel.nEndPos));
+ pTextObj = m_xWndCenter->GetEditEngine()->CreateTextObject();
+ m_xWndCenter->SetText(*pTextObj);
+ if(!bTravelling)
+ m_xWndCenter->GrabFocus();
+ }
+ break;
+
+ case ePageExtFileNameEntry:
+ {
+ ClearTextAreas();
+ OUString aPageEntry( m_xFtPage->get_label() + " " );
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ m_xWndCenter->InsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD) );
+ m_xWndRight->InsertField( SvxFieldItem( SvxExtFileField(
+ OUString(), SvxFileType::Var, SvxFileFormat::PathFull ), EE_FEATURE_FIELD ) );
+ if(!bTravelling)
+ m_xWndRight->GrabFocus();
+ }
+ break;
+
+ case eUserNameEntry:
+ {
+ ClearTextAreas();
+ OUString aUserNameEntry(aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName());
+ m_xWndLeft->GetEditEngine()->SetTextCurrentDefaults(aUserNameEntry);
+ OUString aPageEntry( m_xFtPage->get_label() + " ");
+ //aPageEntry += " ";
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ m_xWndCenter->InsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD) );
+ m_xWndRight->InsertField( SvxFieldItem(SvxDateField(Date( Date::SYSTEM ),SvxDateType::Var), EE_FEATURE_FIELD) );
+ if(!bTravelling)
+ m_xWndRight->GrabFocus();
+ }
+ break;
+
+ case eCreatedByEntry:
+ {
+ ClearTextAreas();
+ OUString aCreatedByEntry( m_xFtCreatedBy->get_label() + " " + aUserOpt.GetFirstName() + " " + aUserOpt.GetLastName());
+ m_xWndLeft->GetEditEngine()->SetTextCurrentDefaults(aCreatedByEntry);
+ m_xWndCenter->InsertField( SvxFieldItem(SvxDateField(Date( Date::SYSTEM ),SvxDateType::Var), EE_FEATURE_FIELD) );
+ OUString aPageEntry( m_xFtPage->get_label() + " " );
+ m_xWndRight->GetEditEngine()->SetTextCurrentDefaults(aPageEntry);
+ m_xWndRight->InsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD) );
+ if(!bTravelling)
+ m_xWndRight->GrabFocus();
+ }
+ break;
+
+ default :
+ break;
+ }
+}
+
+void ScHFEditPage::ClearTextAreas()
+{
+ m_xWndLeft->GetEditEngine()->SetTextCurrentDefaults(OUString());
+ m_xWndLeft->Invalidate();
+ m_xWndCenter->GetEditEngine()->SetTextCurrentDefaults(OUString());
+ m_xWndCenter->Invalidate();
+ m_xWndRight->GetEditEngine()->SetTextCurrentDefaults(OUString());
+ m_xWndRight->Invalidate();
+}
+
+// Handler:
+IMPL_LINK_NOARG(ScHFEditPage, ListToggleHdl_Impl, weld::ComboBox&, void)
+{
+ m_bDropDownActive = !m_bDropDownActive;
+ TimeValue aNow;
+ osl_getSystemTime(&aNow);
+ m_nTimeToggled = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec;
+}
+
+IMPL_LINK_NOARG(ScHFEditPage, ListHdl_Impl, weld::ComboBox&, void)
+{
+ ScHFEntryId eSel = static_cast<ScHFEntryId>(m_xLbDefined->get_active());
+
+ TimeValue aNow;
+ osl_getSystemTime(&aNow);
+ sal_Int64 nNow = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec;
+
+ // order of dropdown vs select not guaranteed
+ bool bDiscrepancy = m_xLbDefined->get_popup_shown() != m_bDropDownActive;
+ if (bDiscrepancy)
+ ListToggleHdl_Impl(*m_xLbDefined);
+
+ bool bFocusToTarget = !m_xLbDefined->get_popup_shown() && m_nTimeToggled != -1 && (nNow - m_nTimeToggled < 800000000);
+ ProcessDefinedListSel(eSel, !bFocusToTarget);
+ // check if we need to remove the customized entry.
+ if (!m_bDropDownActive && eSel < eEntryCount)
+ RemoveFromDefinedList();
+
+ // keep balanced
+ if (bDiscrepancy)
+ ListToggleHdl_Impl(*m_xLbDefined);
+}
+
+IMPL_LINK( ScHFEditPage, ClickHdl, weld::Button&, rBtn, void )
+{
+ if (!m_pEditFocus)
+ return;
+
+ if (&rBtn == m_xBtnText.get())
+ {
+ m_pEditFocus->SetCharAttributes();
+ }
+ else
+ {
+ if ( &rBtn == m_xBtnPage.get() )
+ m_pEditFocus->InsertField(SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD));
+ else if ( &rBtn == m_xBtnLastPage.get() )
+ m_pEditFocus->InsertField(SvxFieldItem(SvxPagesField(), EE_FEATURE_FIELD));
+ else if ( &rBtn == m_xBtnDate.get() )
+ m_pEditFocus->InsertField(SvxFieldItem(SvxDateField(Date(Date::SYSTEM),SvxDateType::Var), EE_FEATURE_FIELD));
+ else if ( &rBtn == m_xBtnTime.get() )
+ m_pEditFocus->InsertField(SvxFieldItem(SvxTimeField(), EE_FEATURE_FIELD));
+ else if ( &rBtn == m_xBtnTable.get() )
+ m_pEditFocus->InsertField(SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD));
+ }
+ InsertToDefinedList();
+ m_pEditFocus->GrabFocus();
+}
+
+IMPL_LINK(ScHFEditPage, MenuHdl, const OUString&, rSelectedId, void)
+{
+ if (!m_pEditFocus)
+ return;
+
+ if (rSelectedId == "title")
+ {
+ m_pEditFocus->InsertField(SvxFieldItem(SvxFileField(), EE_FEATURE_FIELD));
+ }
+ else if (rSelectedId == "filename")
+ {
+ m_pEditFocus->InsertField( SvxFieldItem( SvxExtFileField(
+ OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt ), EE_FEATURE_FIELD ) );
+ }
+ else if (rSelectedId == "pathname")
+ {
+ m_pEditFocus->InsertField( SvxFieldItem( SvxExtFileField(
+ OUString(), SvxFileType::Var, SvxFileFormat::PathFull ), EE_FEATURE_FIELD ) );
+ }
+}
+
+
+ScFirstHeaderEditPage::ScFirstHeaderEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet )
+ : ScHFEditPage( pPage, pController,
+ rCoreSet,
+ SID_SCATTR_PAGE_HEADERFIRST,
+ true )
+ {}
+
+std::unique_ptr<SfxTabPage> ScFirstHeaderEditPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScFirstHeaderEditPage>( pPage, pController, *rCoreSet );
+}
+
+
+ScRightHeaderEditPage::ScRightHeaderEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet )
+ : ScHFEditPage( pPage, pController,
+ rCoreSet,
+ SID_SCATTR_PAGE_HEADERRIGHT,
+ true )
+ {}
+
+std::unique_ptr<SfxTabPage> ScRightHeaderEditPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScRightHeaderEditPage>( pPage, pController, *rCoreSet );
+}
+
+
+ScLeftHeaderEditPage::ScLeftHeaderEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet )
+ : ScHFEditPage( pPage, pController,
+ rCoreSet,
+ SID_SCATTR_PAGE_HEADERLEFT,
+ true )
+ {}
+
+std::unique_ptr<SfxTabPage> ScLeftHeaderEditPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScLeftHeaderEditPage>( pPage, pController, *rCoreSet );
+}
+
+
+ScFirstFooterEditPage::ScFirstFooterEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet )
+ : ScHFEditPage( pPage, pController,
+ rCoreSet,
+ SID_SCATTR_PAGE_FOOTERFIRST,
+ false )
+ {}
+
+std::unique_ptr<SfxTabPage> ScFirstFooterEditPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScFirstFooterEditPage>( pPage, pController, *rCoreSet );
+}
+
+
+ScRightFooterEditPage::ScRightFooterEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet )
+ : ScHFEditPage( pPage, pController,
+ rCoreSet,
+ SID_SCATTR_PAGE_FOOTERRIGHT,
+ false )
+ {}
+
+std::unique_ptr<SfxTabPage> ScRightFooterEditPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScRightFooterEditPage>( pPage, pController, *rCoreSet );
+}
+
+
+ScLeftFooterEditPage::ScLeftFooterEditPage( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet )
+ : ScHFEditPage( pPage, pController,
+ rCoreSet,
+ SID_SCATTR_PAGE_FOOTERLEFT,
+ false )
+ {}
+
+std::unique_ptr<SfxTabPage> ScLeftFooterEditPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet )
+{
+ return std::make_unique<ScLeftFooterEditPage>( pPage, pController, *rCoreSet );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/pagedlg/tphf.cxx b/sc/source/ui/pagedlg/tphf.cxx
new file mode 100644
index 0000000000..cdf4802af3
--- /dev/null
+++ b/sc/source/ui/pagedlg/tphf.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scitems.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <svl/style.hxx>
+#include <svx/pageitem.hxx>
+
+#include <attrib.hxx>
+#include <tphf.hxx>
+#include <scres.hrc>
+#include <scabstdlg.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <styledlg.hxx>
+#include <scuitphfedit.hxx>
+#include <memory>
+#include <helpids.h>
+
+
+ScHFPage::ScHFPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet, sal_uInt16 nSetId)
+ : SvxHFPage(pPage, pController, rSet, nSetId)
+ , aDataSet(*rSet.GetPool(), svl::Items<ATTR_PAGE, ATTR_PAGE, ATTR_PAGE_HEADERLEFT, ATTR_PAGE_FOOTERFIRST>)
+ , nPageUsage(SvxPageUsage::All)
+ , pStyleDlg(nullptr)
+ , m_xBtnEdit(m_xBuilder->weld_button("buttonEdit"))
+{
+ SetExchangeSupport();
+
+ SfxViewShell* pSh = SfxViewShell::Current();
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( pSh );
+ m_xBtnEdit->show();
+
+ aDataSet.Put( rSet );
+
+ if ( pViewSh )
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ aStrPageStyle = rDoc.GetPageStyle( rViewData.GetTabNo() );
+ }
+
+ m_xBtnEdit->connect_clicked(LINK(this, ScHFPage, BtnHdl));
+ m_xTurnOnBox->connect_toggled(LINK(this, ScHFPage, TurnOnHdl));
+
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ m_xBtnEdit->set_help_id(HID_SC_HEADER_EDIT);
+ else
+ m_xBtnEdit->set_help_id(HID_SC_FOOTER_EDIT);
+}
+
+ScHFPage::~ScHFPage()
+{
+ pStyleDlg = nullptr;
+}
+
+void ScHFPage::Reset( const SfxItemSet* rSet )
+{
+ SvxHFPage::Reset( rSet );
+ TurnOnHdl(*m_xTurnOnBox);
+}
+
+bool ScHFPage::FillItemSet( SfxItemSet* rOutSet )
+{
+ bool bResult = SvxHFPage::FillItemSet( rOutSet );
+
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ {
+ rOutSet->Put( aDataSet.Get( ATTR_PAGE_HEADERLEFT ) );
+ rOutSet->Put( aDataSet.Get( ATTR_PAGE_HEADERRIGHT ) );
+ rOutSet->Put( aDataSet.Get( ATTR_PAGE_HEADERFIRST ) );
+ }
+ else
+ {
+ rOutSet->Put( aDataSet.Get( ATTR_PAGE_FOOTERLEFT ) );
+ rOutSet->Put( aDataSet.Get( ATTR_PAGE_FOOTERRIGHT ) );
+ rOutSet->Put( aDataSet.Get( ATTR_PAGE_FOOTERFIRST ) );
+ }
+
+ return bResult;
+}
+
+void ScHFPage::ActivatePage( const SfxItemSet& rSet )
+{
+ sal_uInt16 nPageWhich = GetWhich( SID_ATTR_PAGE );
+ const SvxPageItem& rPageItem = static_cast<const SvxPageItem&>(
+ rSet.Get(nPageWhich));
+
+ nPageUsage = rPageItem.GetPageUsage();
+
+ if ( pStyleDlg )
+ aStrPageStyle = pStyleDlg->GetStyleSheet().GetName();
+
+ aDataSet.Put( rSet.Get(ATTR_PAGE) );
+
+ SvxHFPage::ActivatePage( rSet );
+}
+
+DeactivateRC ScHFPage::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( DeactivateRC::LeavePage == SvxHFPage::DeactivatePage( pSetP ) )
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScHFPage, TurnOnHdl, weld::Toggleable&, void)
+{
+ SvxHFPage::TurnOnHdl(*m_xTurnOnBox);
+
+ if (m_xTurnOnBox->get_active())
+ m_xBtnEdit->set_sensitive(true);
+ else
+ m_xBtnEdit->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScHFPage, BtnHdl, weld::Button&, void)
+{
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+
+ if ( !pViewSh )
+ {
+ OSL_FAIL( "Current ViewShell not found." );
+ return;
+ }
+
+ if ( (m_xCntSharedBox->get_sensitive() && !m_xCntSharedBox->get_active()) ||
+ (m_xCntSharedFirstBox->get_sensitive() && !m_xCntSharedFirstBox->get_active()) )
+ {
+ sal_uInt16 nResId;
+
+ if ( m_xCntSharedBox->get_sensitive() && !m_xCntSharedBox->get_active() &&
+ m_xCntSharedFirstBox->get_sensitive() && !m_xCntSharedFirstBox->get_active() )
+ {
+ nResId = ( nId == SID_ATTR_PAGE_HEADERSET )
+ ? RID_SCDLG_HFED_HEADER
+ : RID_SCDLG_HFED_FOOTER;
+ }
+ else if (m_xCntSharedBox->get_sensitive() && !m_xCntSharedBox->get_active())
+ {
+ nResId = ( nId == SID_ATTR_PAGE_HEADERSET )
+ ? RID_SCDLG_HFEDIT_SHAREDFIRSTHEADER
+ : RID_SCDLG_HFEDIT_SHAREDFIRSTFOOTER;
+ }
+ else
+ {
+ OSL_ENSURE( m_xCntSharedFirstBox->get_sensitive() && !m_xCntSharedFirstBox->get_active(), "This should be logically impossible." );
+ nResId = ( nId == SID_ATTR_PAGE_HEADERSET )
+ ? RID_SCDLG_HFEDIT_SHAREDLEFTHEADER
+ : RID_SCDLG_HFEDIT_SHAREDLEFTFOOTER;
+ }
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScHFEditDlg(
+ GetFrameWeld(), aDataSet, aStrPageStyle, nResId));
+ pDlg->StartExecuteAsync([this, pDlg](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ aDataSet.Put( *pDlg->GetOutputItemSet() );
+ }
+
+ pDlg->disposeOnce();
+ });
+ }
+ else
+ {
+ OUString aText;
+ SfxSingleTabDialogController aDlg(GetFrameWeld(), &aDataSet);
+ bool bRightPage = m_xCntSharedBox->get_active() || (SvxPageUsage::Left != nPageUsage);
+
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ {
+ aText = ScResId( STR_PAGEHEADER );
+ if ( bRightPage )
+ aDlg.SetTabPage(ScRightHeaderEditPage::Create(aDlg.get_content_area(), &aDlg, &aDataSet));
+ else
+ aDlg.SetTabPage(ScLeftHeaderEditPage::Create(aDlg.get_content_area(), &aDlg, &aDataSet));
+ }
+ else
+ {
+ aText = ScResId( STR_PAGEFOOTER );
+ if ( bRightPage )
+ aDlg.SetTabPage(ScRightFooterEditPage::Create(aDlg.get_content_area(), &aDlg, &aDataSet));
+ else
+ aDlg.SetTabPage(ScLeftFooterEditPage::Create(aDlg.get_content_area(), &aDlg, &aDataSet));
+ }
+
+ SvxNumType eNumType = aDataSet.Get(ATTR_PAGE).GetNumType();
+ static_cast<ScHFEditPage*>(aDlg.GetTabPage())->SetNumType(eNumType);
+
+ aText += " (" + ScResId( STR_PAGESTYLE ) +
+ ": " + aStrPageStyle + ")";
+
+ aDlg.set_title(aText);
+
+ if (aDlg.run() == RET_OK)
+ {
+ aDataSet.Put(*aDlg.GetOutputItemSet());
+ }
+ }
+}
+
+
+ScHeaderPage::ScHeaderPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+ : ScHFPage(pPage, pController, rSet, SID_ATTR_PAGE_HEADERSET)
+{
+}
+
+std::unique_ptr<SfxTabPage> ScHeaderPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet)
+{
+ return std::make_unique<ScHeaderPage>(pPage, pController, *rCoreSet);
+}
+
+WhichRangesContainer ScHeaderPage::GetRanges()
+{
+ return SvxHeaderPage::GetRanges();
+}
+
+
+ScFooterPage::ScFooterPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+ : ScHFPage( pPage, pController, rSet, SID_ATTR_PAGE_FOOTERSET )
+{
+}
+
+std::unique_ptr<SfxTabPage> ScFooterPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet)
+{
+ return std::make_unique<ScFooterPage>(pPage, pController, *rCoreSet);
+}
+
+WhichRangesContainer ScFooterPage::GetRanges()
+{
+ return SvxHeaderPage::GetRanges();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/pagedlg/tphfedit.cxx b/sc/source/ui/pagedlg/tphfedit.cxx
new file mode 100644
index 0000000000..617f2208a9
--- /dev/null
+++ b/sc/source/ui/pagedlg/tphfedit.cxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/editobj.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxdlg.hxx>
+
+#include <tphfedit.hxx>
+#include <editutil.hxx>
+#include <global.hxx>
+#include <patattr.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <tabvwsh.hxx>
+#include <prevwsh.hxx>
+#include <AccessibleEditObject.hxx>
+
+#include <scabstdlg.hxx>
+#include <memory>
+
+
+static void lcl_GetFieldData( ScHeaderFieldData& rData )
+{
+ SfxViewShell* pShell = SfxViewShell::Current();
+ if (pShell)
+ {
+ if (auto pTabViewShell = dynamic_cast<ScTabViewShell*>( pShell))
+ pTabViewShell->FillFieldData(rData);
+ else if (auto pPreviewShell = dynamic_cast<ScPreviewShell*>( pShell))
+ pPreviewShell->FillFieldData(rData);
+ }
+}
+
+
+ScEditWindow::ScEditWindow(ScEditWindowLocation eLoc, weld::Window* pDialog)
+ : eLocation(eLoc)
+ , mbRTL(ScGlobal::IsSystemRTL())
+ , mpDialog(pDialog)
+{
+}
+
+void ScEditWindow::makeEditEngine()
+{
+ m_xEditEngine.reset(new ScHeaderEditEngine(EditEngine::CreatePool().get()));
+}
+
+ScHeaderEditEngine* ScEditWindow::GetEditEngine() const
+{
+ return static_cast<ScHeaderEditEngine*>(m_xEditEngine.get());
+}
+
+void ScEditWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ Size aSize = rDevice.LogicToPixel(Size(80, 120), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+
+ WeldEditView::SetDrawingArea(pDrawingArea);
+
+ ScHeaderFieldData aData;
+ lcl_GetFieldData(aData);
+ // fields
+ GetEditEngine()->SetData(aData);
+ if (mbRTL)
+ m_xEditEngine->SetDefaultHorizontalTextDirection(EEHorizontalTextDirection::R2L);
+
+ Color aBgColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ rDevice.SetBackground(aBgColor);
+ m_xEditView->SetBackgroundColor(aBgColor);
+
+ if (auto tmpAcc = mxAcc.get())
+ {
+ OUString sName;
+ switch (eLocation)
+ {
+ case Left:
+ sName = ScResId(STR_ACC_LEFTAREA_NAME);
+ break;
+ case Center:
+ sName = ScResId(STR_ACC_CENTERAREA_NAME);
+ break;
+ case Right:
+ sName = ScResId(STR_ACC_RIGHTAREA_NAME);
+ break;
+ }
+
+ tmpAcc->InitAcc(nullptr, m_xEditView.get(),
+ sName, pDrawingArea->get_tooltip_text());
+ }
+
+ SetCursor(m_xEditView->GetCursor());
+}
+
+ScEditWindow::~ScEditWindow()
+{
+ // delete Accessible object before deleting EditEngine and EditView
+ if (auto tmp = mxAcc.get())
+ tmp->dispose();
+}
+
+void ScEditWindow::SetNumType(SvxNumType eNumType)
+{
+ ScHeaderEditEngine* pEditEngine = GetEditEngine();
+ pEditEngine->SetNumType(eNumType);
+ pEditEngine->UpdateFields();
+}
+
+std::unique_ptr<EditTextObject> ScEditWindow::CreateTextObject()
+{
+ // reset paragraph attributes
+ // (GetAttribs at creation of format dialog always returns the set items)
+
+ const SfxItemSet& rEmpty = m_xEditEngine->GetEmptyItemSet();
+ sal_Int32 nParCnt = m_xEditEngine->GetParagraphCount();
+ for (sal_Int32 i=0; i<nParCnt; i++)
+ m_xEditEngine->SetParaAttribs( i, rEmpty );
+
+ return m_xEditEngine->CreateTextObject();
+}
+
+void ScEditWindow::SetFont( const ScPatternAttr& rPattern )
+{
+ auto pSet = std::make_unique<SfxItemSet>( m_xEditEngine->GetEmptyItemSet() );
+ rPattern.FillEditItemSet( pSet.get() );
+ // FillEditItemSet adjusts font height to 1/100th mm,
+ // but for header/footer twips is needed, as in the PatternAttr:
+ pSet->Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ pSet->Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ pSet->Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ // font color used, suitable header/footer background color set in ScEditWindow::SetDrawingArea
+ Color aFgColor = svtools::ColorConfig().GetColorValue(svtools::FONTCOLOR, false).nColor;
+ if (aFgColor == COL_AUTO) {
+ Color aBgColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ aFgColor = aBgColor.IsDark() ? COL_WHITE : COL_BLACK;
+ }
+ pSet->Put(SvxColorItem(aFgColor, EE_CHAR_COLOR));
+ if (mbRTL)
+ pSet->Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+ GetEditEngine()->SetDefaults( std::move(pSet) );
+}
+
+void ScEditWindow::SetText( const EditTextObject& rTextObject )
+{
+ GetEditEngine()->SetTextCurrentDefaults(rTextObject);
+}
+
+void ScEditWindow::InsertField( const SvxFieldItem& rFld )
+{
+ m_xEditView->InsertField( rFld );
+}
+
+void ScEditWindow::SetCharAttributes()
+{
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+
+ ScTabViewShell* pTabViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+
+ OSL_ENSURE( pDocSh, "Current DocShell not found" );
+ OSL_ENSURE( pViewSh, "Current ViewShell not found" );
+
+ if ( !(pDocSh && pViewSh) )
+ return;
+
+ if(pTabViewSh!=nullptr) pTabViewSh->SetInFormatDialog(true);
+
+ SfxItemSet aSet( m_xEditView->GetAttribs() );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScCharDlg(
+ mpDialog, &aSet, pDocSh, false));
+ pDlg->SetText( ScResId( STR_TEXTATTRS ) );
+ if ( pDlg->Execute() == RET_OK )
+ {
+ aSet.ClearItem();
+ aSet.Put( *pDlg->GetOutputItemSet() );
+ m_xEditView->SetAttribs( aSet );
+ }
+
+ if(pTabViewSh!=nullptr) pTabViewSh->SetInFormatDialog(false);
+}
+
+bool ScEditWindow::KeyInput( const KeyEvent& rKEvt )
+{
+ if ( !rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsShift() &&
+ rKEvt.GetKeyCode().IsMod2() && rKEvt.GetKeyCode().GetCode() == KEY_DOWN )
+ {
+ aObjectSelectLink.Call(*this);
+ return true;
+ }
+ return WeldEditView::KeyInput(rKEvt);
+}
+
+void ScEditWindow::GetFocus()
+{
+ assert(m_GetFocusLink);
+ m_GetFocusLink(*this);
+
+ if (auto tmp = mxAcc.get())
+ tmp->GotFocus();
+
+ WeldEditView::GetFocus();
+}
+
+void ScEditWindow::LoseFocus()
+{
+ if (auto xTemp = mxAcc.get())
+ xTemp->LostFocus();
+ else
+ mxAcc = nullptr;
+ WeldEditView::LoseFocus();
+}
+
+bool ScEditWindow::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ bool bHadFocus = HasFocus();
+ bool bRet = WeldEditView::MouseButtonDown(rMEvt);
+ if (!bHadFocus)
+ {
+ assert(comphelper::LibreOfficeKit::isActive() || HasFocus());
+ GetFocus();
+ }
+ return bRet;
+}
+
+css::uno::Reference< css::accessibility::XAccessible > ScEditWindow::CreateAccessible()
+{
+ rtl::Reference<ScAccessibleEditControlObject> tmp = new ScAccessibleEditControlObject(this, ScAccessibleEditObject::EditControl);
+ mxAcc = tmp.get();
+ return tmp;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/pagedlg/tptable.cxx b/sc/source/ui/pagedlg/tptable.cxx
new file mode 100644
index 0000000000..131d2bceb4
--- /dev/null
+++ b/sc/source/ui/pagedlg/tptable.cxx
@@ -0,0 +1,522 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <scitems.hxx>
+
+#include <osl/diagnose.h>
+
+#include <tptable.hxx>
+#include <global.hxx>
+#include <attrib.hxx>
+#include <bitmaps.hlst>
+
+// Static Data
+
+const WhichRangesContainer ScTablePage::pPageTableRanges(
+ svl::Items<ATTR_PAGE_NOTES, ATTR_PAGE_FIRSTPAGENO>);
+
+static bool lcl_PutVObjModeItem(TypedWhichId<ScViewObjectModeItem> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::Toggleable& rBtn);
+
+static bool lcl_PutScaleItem( TypedWhichId<SfxUInt16Item> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::ComboBox& rListBox,
+ sal_uInt16 nLBEntry,
+ const weld::MetricSpinButton& rEd,
+ sal_uInt16 nValue );
+
+static bool lcl_PutScaleItem2( TypedWhichId<ScPageScaleToItem> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::ComboBox& rListBox,
+ sal_uInt16 nLBEntry,
+ const weld::SpinButton& rEd1,
+ sal_uInt16 nOrigScalePageWidth,
+ const weld::SpinButton& rEd2,
+ sal_uInt16 nOrigScalePageHeight );
+
+static bool lcl_PutScaleItem3( TypedWhichId<SfxUInt16Item> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::ComboBox& rListBox,
+ sal_uInt16 nLBEntry,
+ const weld::SpinButton& rEd,
+ sal_uInt16 nValue );
+
+static bool lcl_PutBoolItem( TypedWhichId<SfxBoolItem> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ bool bIsChecked,
+ bool bSavedValue );
+
+namespace {
+
+bool WAS_DEFAULT(sal_uInt16 w, SfxItemSet const & s)
+{ return SfxItemState::DEFAULT==s.GetItemState(w); }
+
+}
+
+// List box entries "Scaling mode"
+#define SC_TPTABLE_SCALE_PERCENT 0
+#define SC_TPTABLE_SCALE_TO 1
+#define SC_TPTABLE_SCALE_TO_PAGES 2
+
+ScTablePage::ScTablePage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreAttrs)
+ : SfxTabPage(pPage, pController, "modules/scalc/ui/sheetprintpage.ui", "SheetPrintPage", &rCoreAttrs)
+ , m_nOrigScalePageWidth(0)
+ , m_nOrigScalePageHeight(0)
+ , m_xBtnTopDown(m_xBuilder->weld_radio_button("radioBTN_TOPDOWN"))
+ , m_xBtnLeftRight(m_xBuilder->weld_radio_button("radioBTN_LEFTRIGHT"))
+ , m_xBmpPageDir(m_xBuilder->weld_image("imageBMP_PAGEDIR"))
+ , m_xBtnPageNo(m_xBuilder->weld_check_button("checkBTN_PAGENO"))
+ , m_xEdPageNo(m_xBuilder->weld_spin_button("spinED_PAGENO"))
+ , m_xBtnHeaders(m_xBuilder->weld_check_button("checkBTN_HEADER"))
+ , m_xBtnGrid(m_xBuilder->weld_check_button("checkBTN_GRID"))
+ , m_xBtnNotes(m_xBuilder->weld_check_button("checkBTN_NOTES"))
+ , m_xBtnObjects(m_xBuilder->weld_check_button("checkBTN_OBJECTS"))
+ , m_xBtnCharts(m_xBuilder->weld_check_button("checkBTN_CHARTS"))
+ , m_xBtnDrawings(m_xBuilder->weld_check_button("checkBTN_DRAWINGS"))
+ , m_xBtnFormulas(m_xBuilder->weld_check_button("checkBTN_FORMULAS"))
+ , m_xBtnNullVals(m_xBuilder->weld_check_button("checkBTN_NULLVALS"))
+ , m_xLbScaleMode(m_xBuilder->weld_combo_box("comboLB_SCALEMODE"))
+ , m_xBxScaleAll(m_xBuilder->weld_widget("boxSCALEALL"))
+ , m_xEdScaleAll(m_xBuilder->weld_metric_spin_button("spinED_SCALEALL", FieldUnit::PERCENT))
+ , m_xGrHeightWidth(m_xBuilder->weld_widget("gridWH"))
+ , m_xEdScalePageWidth(m_xBuilder->weld_spin_button("spinED_SCALEPAGEWIDTH"))
+ , m_xCbScalePageWidth(m_xBuilder->weld_check_button("labelWP"))
+ , m_xEdScalePageHeight(m_xBuilder->weld_spin_button("spinED_SCALEPAGEHEIGHT"))
+ , m_xCbScalePageHeight(m_xBuilder->weld_check_button("labelHP"))
+ , m_xBxScalePageNum(m_xBuilder->weld_widget("boxNP"))
+ , m_xEdScalePageNum(m_xBuilder->weld_spin_button("spinED_SCALEPAGENUM"))
+{
+ SetExchangeSupport();
+
+ m_xBtnPageNo->connect_toggled(LINK(this,ScTablePage,PageNoHdl));
+ m_xBtnTopDown->connect_toggled(LINK(this,ScTablePage,PageDirHdl));
+ m_xBtnLeftRight->connect_toggled(LINK(this,ScTablePage,PageDirHdl));
+ m_xLbScaleMode->connect_changed(LINK(this,ScTablePage,ScaleHdl));
+ m_xCbScalePageWidth->connect_toggled(LINK(this, ScTablePage, ToggleHdl));
+ m_xCbScalePageHeight->connect_toggled(LINK(this, ScTablePage, ToggleHdl));
+}
+
+void ScTablePage::ShowImage()
+{
+ OUString aImg(m_xBtnLeftRight->get_active() ? BMP_LEFTRIGHT : BMP_TOPDOWN);
+ m_xBmpPageDir->set_from_icon_name(aImg);
+}
+
+ScTablePage::~ScTablePage()
+{
+}
+
+std::unique_ptr<SfxTabPage> ScTablePage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rCoreSet)
+{
+ return std::make_unique<ScTablePage>(pPage, pController, *rCoreSet);
+}
+
+void ScTablePage::Reset( const SfxItemSet* rCoreSet )
+{
+ bool bTopDown = rCoreSet->Get(SID_SCATTR_PAGE_TOPDOWN).GetValue();
+
+ // sal_Bool flags
+ m_xBtnNotes->set_active( rCoreSet->Get(SID_SCATTR_PAGE_NOTES).GetValue() );
+ m_xBtnGrid->set_active( rCoreSet->Get(SID_SCATTR_PAGE_GRID).GetValue() );
+ m_xBtnHeaders->set_active( rCoreSet->Get(SID_SCATTR_PAGE_HEADERS).GetValue() );
+ m_xBtnFormulas->set_active( rCoreSet->Get(SID_SCATTR_PAGE_FORMULAS).GetValue() );
+ m_xBtnNullVals->set_active( rCoreSet->Get(SID_SCATTR_PAGE_NULLVALS).GetValue() );
+ m_xBtnTopDown->set_active( bTopDown );
+ m_xBtnLeftRight->set_active( !bTopDown );
+
+ // first printed page:
+ sal_uInt16 nPage = rCoreSet->Get(SID_SCATTR_PAGE_FIRSTPAGENO).GetValue();
+ m_xBtnPageNo->set_active( nPage != 0 );
+ m_xEdPageNo->set_value( (nPage != 0) ? nPage : 1 );
+ PageNoHdl(nullptr);
+
+ // object representation:
+ m_xBtnCharts->set_active( rCoreSet->Get(SID_SCATTR_PAGE_CHARTS).GetValue() == VOBJ_MODE_SHOW );
+ m_xBtnObjects->set_active( rCoreSet->Get(SID_SCATTR_PAGE_OBJECTS).GetValue() == VOBJ_MODE_SHOW );
+ m_xBtnDrawings->set_active( rCoreSet->Get(SID_SCATTR_PAGE_DRAWINGS).GetValue() == VOBJ_MODE_SHOW );
+
+ // scaling:
+ constexpr auto nWhichPageScale = SID_SCATTR_PAGE_SCALE;
+ if ( rCoreSet->GetItemState( nWhichPageScale ) >= SfxItemState::DEFAULT )
+ {
+ sal_uInt16 nScale = rCoreSet->Get(nWhichPageScale).GetValue();
+ if( nScale > 0 )
+ m_xLbScaleMode->set_active(SC_TPTABLE_SCALE_PERCENT);
+ m_xEdScaleAll->set_value((nScale > 0) ? nScale : 100, FieldUnit::PERCENT);
+ }
+
+ constexpr auto nWhichScaleTo = SID_SCATTR_PAGE_SCALETO;
+ if ( rCoreSet->GetItemState( nWhichScaleTo ) >= SfxItemState::DEFAULT )
+ {
+ const ScPageScaleToItem& rItem = rCoreSet->Get( nWhichScaleTo );
+ sal_uInt16 nWidth = rItem.GetWidth();
+ sal_uInt16 nHeight = rItem.GetHeight();
+
+ /* width==0 and height==0 is invalid state, used as "not selected".
+ Dialog shows width=height=1 then. */
+ if (nWidth || nHeight)
+ m_xLbScaleMode->set_active(SC_TPTABLE_SCALE_TO);
+ else
+ nWidth = nHeight = 1;
+
+ if (nWidth)
+ m_xEdScalePageWidth->set_value(nWidth);
+ else
+ m_xEdScalePageWidth->set_text(OUString());
+
+ m_xEdScalePageWidth->set_sensitive(nWidth != 0);
+ m_xCbScalePageWidth->set_active(nWidth != 0);
+
+ if(nHeight)
+ m_xEdScalePageHeight->set_value(nHeight);
+ else
+ m_xEdScalePageHeight->set_text(OUString());
+
+ m_xEdScalePageHeight->set_sensitive(nHeight != 0);
+ m_xCbScalePageHeight->set_active(nHeight != 0);
+ }
+
+ constexpr auto nWhichScale = SID_SCATTR_PAGE_SCALETOPAGES;
+ if ( rCoreSet->GetItemState( nWhichScale ) >= SfxItemState::DEFAULT )
+ {
+ sal_uInt16 nPages = rCoreSet->Get(nWhichScale).GetValue();
+ if( nPages > 0 )
+ m_xLbScaleMode->set_active(SC_TPTABLE_SCALE_TO_PAGES);
+ m_xEdScalePageNum->set_value( (nPages > 0) ? nPages : 1 );
+ }
+
+ if (m_xLbScaleMode->get_active() == -1)
+ {
+ // fall back to 100%
+ OSL_FAIL( "ScTablePage::Reset - missing scaling item" );
+ m_xLbScaleMode->set_active(SC_TPTABLE_SCALE_PERCENT);
+ m_xEdScaleAll->set_value(100, FieldUnit::PERCENT);
+ }
+
+ PageDirHdl(*m_xBtnTopDown);
+ ScaleHdl(*m_xLbScaleMode);
+
+ // remember for FillItemSet
+ m_xBtnFormulas->save_state();
+ m_xBtnNullVals->save_state();
+ m_xBtnNotes->save_state();
+ m_xBtnGrid->save_state();
+ m_xBtnHeaders->save_state();
+ m_xBtnTopDown->save_state();
+ m_xBtnLeftRight->save_state();
+ m_xLbScaleMode->save_value();
+ m_xBtnCharts->save_state();
+ m_xBtnObjects->save_state();
+ m_xBtnDrawings->save_state();
+ m_xBtnPageNo->save_state();
+ m_xEdPageNo->save_value();
+ m_xEdScaleAll->save_value();
+ m_nOrigScalePageWidth = m_xEdScalePageWidth->get_sensitive() ? m_xEdScalePageWidth->get_value() : 0;
+ m_nOrigScalePageHeight = m_xEdScalePageHeight->get_sensitive() ? m_xEdScalePageHeight->get_value() : 0;
+ m_xEdScalePageNum->save_value();
+}
+
+bool ScTablePage::FillItemSet( SfxItemSet* rCoreSet )
+{
+ const SfxItemSet& rOldSet = GetItemSet();
+ constexpr TypedWhichId<SfxUInt16Item> nWhichPageNo = SID_SCATTR_PAGE_FIRSTPAGENO;
+ bool bDataChanged = false;
+
+ // sal_Bool flags
+ bDataChanged |= lcl_PutBoolItem( SID_SCATTR_PAGE_NOTES,
+ *rCoreSet, rOldSet,
+ m_xBtnNotes->get_active(),
+ m_xBtnNotes->get_saved_state() != TRISTATE_FALSE );
+
+ bDataChanged |= lcl_PutBoolItem( SID_SCATTR_PAGE_GRID,
+ *rCoreSet, rOldSet,
+ m_xBtnGrid->get_active(),
+ m_xBtnGrid->get_saved_state() != TRISTATE_FALSE );
+
+ bDataChanged |= lcl_PutBoolItem( SID_SCATTR_PAGE_HEADERS,
+ *rCoreSet, rOldSet,
+ m_xBtnHeaders->get_active(),
+ m_xBtnHeaders->get_saved_state() != TRISTATE_FALSE );
+
+ bDataChanged |= lcl_PutBoolItem( SID_SCATTR_PAGE_TOPDOWN,
+ *rCoreSet, rOldSet,
+ m_xBtnTopDown->get_active(),
+ m_xBtnTopDown->get_saved_state() != TRISTATE_FALSE );
+
+ bDataChanged |= lcl_PutBoolItem( SID_SCATTR_PAGE_FORMULAS,
+ *rCoreSet, rOldSet,
+ m_xBtnFormulas->get_active(),
+ m_xBtnFormulas->get_saved_state() != TRISTATE_FALSE );
+
+ bDataChanged |= lcl_PutBoolItem( SID_SCATTR_PAGE_NULLVALS,
+ *rCoreSet, rOldSet,
+ m_xBtnNullVals->get_active(),
+ m_xBtnNullVals->get_saved_state() != TRISTATE_FALSE );
+
+ // first printed page:
+ bool bUseValue = m_xBtnPageNo->get_active();
+
+ if ( WAS_DEFAULT(nWhichPageNo,rOldSet)
+ && ( (!bUseValue && 0 == m_xBtnPageNo->get_saved_state())
+ || ( bUseValue && 1 == m_xBtnPageNo->get_saved_state()
+ && ! m_xEdPageNo->get_value_changed_from_saved() ) ) )
+ {
+ rCoreSet->ClearItem( nWhichPageNo );
+ }
+ else
+ {
+ sal_uInt16 nPage = static_cast<sal_uInt16>( m_xBtnPageNo->get_active()
+ ? m_xEdPageNo->get_value()
+ : 0 );
+
+ rCoreSet->Put( SfxUInt16Item( nWhichPageNo, nPage ) );
+ bDataChanged = true;
+ }
+
+ // object representation:
+ bDataChanged |= lcl_PutVObjModeItem( SID_SCATTR_PAGE_CHARTS,
+ *rCoreSet, rOldSet, *m_xBtnCharts );
+
+ bDataChanged |= lcl_PutVObjModeItem( SID_SCATTR_PAGE_OBJECTS,
+ *rCoreSet, rOldSet, *m_xBtnObjects );
+
+ bDataChanged |= lcl_PutVObjModeItem( SID_SCATTR_PAGE_DRAWINGS,
+ *rCoreSet, rOldSet, *m_xBtnDrawings );
+
+ // scaling:
+ if( !m_xEdScalePageWidth->get_sensitive() && !m_xEdScalePageHeight->get_sensitive() )
+ {
+ m_xLbScaleMode->set_active(SC_TPTABLE_SCALE_PERCENT);
+ m_xEdScaleAll->set_value(100, FieldUnit::PERCENT);
+ }
+
+ bDataChanged |= lcl_PutScaleItem( SID_SCATTR_PAGE_SCALE,
+ *rCoreSet, rOldSet,
+ *m_xLbScaleMode, SC_TPTABLE_SCALE_PERCENT,
+ *m_xEdScaleAll, static_cast<sal_uInt16>(m_xEdScaleAll->get_value(FieldUnit::PERCENT)) );
+
+ bDataChanged |= lcl_PutScaleItem2( SID_SCATTR_PAGE_SCALETO,
+ *rCoreSet, rOldSet,
+ *m_xLbScaleMode, SC_TPTABLE_SCALE_TO,
+ *m_xEdScalePageWidth, m_nOrigScalePageWidth,
+ *m_xEdScalePageHeight, m_nOrigScalePageHeight );
+
+ bDataChanged |= lcl_PutScaleItem3( SID_SCATTR_PAGE_SCALETOPAGES,
+ *rCoreSet, rOldSet,
+ *m_xLbScaleMode, SC_TPTABLE_SCALE_TO_PAGES,
+ *m_xEdScalePageNum, static_cast<sal_uInt16>(m_xEdScalePageNum->get_value()) );
+
+ return bDataChanged;
+}
+
+DeactivateRC ScTablePage::DeactivatePage( SfxItemSet* pSetP )
+{
+ if ( pSetP )
+ FillItemSet( pSetP );
+
+ return DeactivateRC::LeavePage;
+}
+
+// Handler:
+
+IMPL_LINK_NOARG(ScTablePage, PageDirHdl, weld::Toggleable&, void)
+{
+ ShowImage();
+}
+
+IMPL_LINK(ScTablePage, PageNoHdl, weld::Toggleable&, rBtn, void)
+{
+ PageNoHdl(&rBtn);
+}
+
+void ScTablePage::PageNoHdl(const weld::Toggleable* pBtn)
+{
+ if (m_xBtnPageNo->get_active())
+ {
+ m_xEdPageNo->set_sensitive(true);
+ if (pBtn)
+ m_xEdPageNo->grab_focus();
+ }
+ else
+ m_xEdPageNo->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(ScTablePage, ScaleHdl, weld::ComboBox&, void)
+{
+ // controls for Box "Reduce/enlarge"
+ m_xBxScaleAll->set_visible(m_xLbScaleMode->get_active() == SC_TPTABLE_SCALE_PERCENT);
+
+ // controls for Grid "Scale to width/height"
+ m_xGrHeightWidth->set_visible(m_xLbScaleMode->get_active() == SC_TPTABLE_SCALE_TO);
+
+ // controls for Box "Scale to pages"
+ m_xBxScalePageNum->set_visible(m_xLbScaleMode->get_active() == SC_TPTABLE_SCALE_TO_PAGES);
+}
+
+IMPL_LINK(ScTablePage, ToggleHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox == m_xCbScalePageWidth.get())
+ {
+ if (!rBox.get_active())
+ {
+ m_xEdScalePageWidth->set_text(OUString());
+ m_xEdScalePageWidth->set_sensitive(false);
+ }
+ else
+ {
+ m_xEdScalePageWidth->set_value(1);
+ m_xEdScalePageWidth->set_sensitive(true);
+ }
+ }
+ else
+ {
+ if (!rBox.get_active())
+ {
+ m_xEdScalePageHeight->set_text(OUString());
+ m_xEdScalePageHeight->set_sensitive(false);
+ }
+ else
+ {
+ m_xEdScalePageHeight->set_value(1);
+ m_xEdScalePageHeight->set_sensitive(true);
+ }
+ }
+}
+
+// Helper functions for FillItemSet:
+
+static bool lcl_PutBoolItem( TypedWhichId<SfxBoolItem> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ bool bIsChecked,
+ bool bSavedValue )
+{
+ bool bDataChanged = ( bSavedValue == bIsChecked
+ && WAS_DEFAULT(nWhich,rOldSet) );
+
+ if ( bDataChanged )
+ rCoreSet.ClearItem(nWhich);
+ else
+ rCoreSet.Put( SfxBoolItem( nWhich, bIsChecked ) );
+
+ return bDataChanged;
+}
+
+static bool lcl_PutVObjModeItem( TypedWhichId<ScViewObjectModeItem> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::Toggleable& rBtn )
+{
+ bool bIsChecked = rBtn.get_active();
+ bool bDataChanged = rBtn.get_saved_state() == (bIsChecked ? 1 : 0)
+ && WAS_DEFAULT(nWhich,rOldSet);
+
+ if ( bDataChanged )
+ rCoreSet.ClearItem( nWhich );
+
+ else
+ rCoreSet.Put( ScViewObjectModeItem( nWhich, bIsChecked
+ ? VOBJ_MODE_SHOW
+ : VOBJ_MODE_HIDE ) );
+ return bDataChanged;
+}
+
+static bool lcl_PutScaleItem( TypedWhichId<SfxUInt16Item> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::ComboBox& rListBox,
+ sal_uInt16 nLBEntry,
+ const weld::MetricSpinButton& rEd,
+ sal_uInt16 nValue )
+{
+ bool bIsSel = (rListBox.get_active() == nLBEntry);
+ bool bDataChanged = (rListBox.get_value_changed_from_saved()) ||
+ rEd.get_value_changed_from_saved() ||
+ !WAS_DEFAULT( nWhich, rOldSet );
+
+ if( bDataChanged )
+ rCoreSet.Put( SfxUInt16Item( nWhich, bIsSel ? nValue : 0 ) );
+ else
+ rCoreSet.ClearItem( nWhich );
+
+ return bDataChanged;
+}
+
+static bool lcl_PutScaleItem2( TypedWhichId<ScPageScaleToItem> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::ComboBox& rListBox,
+ sal_uInt16 nLBEntry,
+ const weld::SpinButton& rEd1,
+ sal_uInt16 nOrigScalePageWidth,
+ const weld::SpinButton& rEd2,
+ sal_uInt16 nOrigScalePageHeight )
+{
+ sal_uInt16 nValue1 = rEd1.get_sensitive() ? rEd1.get_value() : 0;
+ sal_uInt16 nValue2 = rEd2.get_sensitive() ? rEd2.get_value() : 0;
+ bool bIsSel = (rListBox.get_active() == nLBEntry);
+ bool bDataChanged = (rListBox.get_value_changed_from_saved()) ||
+ nValue1 != nOrigScalePageWidth ||
+ nValue1 != nOrigScalePageHeight ||
+ !WAS_DEFAULT( nWhich, rOldSet );
+
+ if( bDataChanged )
+ {
+ ScPageScaleToItem aItem;
+ if( bIsSel )
+ aItem.Set( nValue1, nValue2 );
+ rCoreSet.Put( aItem );
+ }
+ else
+ rCoreSet.ClearItem( nWhich );
+
+ return bDataChanged;
+}
+
+static bool lcl_PutScaleItem3( TypedWhichId<SfxUInt16Item> nWhich,
+ SfxItemSet& rCoreSet,
+ const SfxItemSet& rOldSet,
+ const weld::ComboBox& rListBox,
+ sal_uInt16 nLBEntry,
+ const weld::SpinButton& rEd,
+ sal_uInt16 nValue )
+{
+ bool bIsSel = (rListBox.get_active() == nLBEntry);
+ bool bDataChanged = (rListBox.get_value_changed_from_saved()) ||
+ rEd.get_value_changed_from_saved() ||
+ !WAS_DEFAULT( nWhich, rOldSet );
+
+ if( bDataChanged )
+ rCoreSet.Put( SfxUInt16Item( nWhich, bIsSel ? nValue : 0 ) );
+ else
+ rCoreSet.ClearItem( nWhich );
+
+ return bDataChanged;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/AlignmentPropertyPanel.cxx b/sc/source/ui/sidebar/AlignmentPropertyPanel.cxx
new file mode 100644
index 0000000000..66f7e35871
--- /dev/null
+++ b/sc/source/ui/sidebar/AlignmentPropertyPanel.cxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "AlignmentPropertyPanel.hxx"
+#include <editeng/justifyitem.hxx>
+#include <sc.hrc>
+#include <attrib.hxx>
+#include <scitems.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <svx/rotmodit.hxx>
+#include <svtools/unitconv.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+// namespace open
+
+namespace sc::sidebar {
+
+AlignmentPropertyPanel::AlignmentPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+ : PanelLayout(pParent, "AlignmentPropertyPanel", "modules/scalc/ui/sidebaralignment.ui")
+ , mxFTLeftIndent(m_xBuilder->weld_label("leftindentlabel"))
+ , mxMFLeftIndent(m_xBuilder->weld_metric_spin_button("leftindent", FieldUnit::POINT))
+ , mxCBXWrapText(m_xBuilder->weld_check_button("wraptext"))
+ , mxCBXMergeCell(m_xBuilder->weld_check_button("mergecells"))
+ , mxFtRotate(m_xBuilder->weld_label("orientationlabel"))
+ , mxMtrAngle(m_xBuilder->weld_metric_spin_button("orientationdegrees", FieldUnit::DEGREE))
+ , mxRefEdgeBottom(m_xBuilder->weld_toggle_button("bottom"))
+ , mxRefEdgeTop(m_xBuilder->weld_toggle_button("top"))
+ , mxRefEdgeStd(m_xBuilder->weld_toggle_button("standard"))
+ , mxCBStacked(m_xBuilder->weld_check_button("stacked"))
+ , mxTextOrientBox(m_xBuilder->weld_widget("textorientbox"))
+ , mxHorizontalAlign(m_xBuilder->weld_toolbar("horizontalalignment"))
+ , mxHorizontalAlignDispatch(new ToolbarUnoDispatcher(*mxHorizontalAlign, *m_xBuilder, rxFrame))
+ , mxVertAlign(m_xBuilder->weld_toolbar("verticalalignment"))
+ , mxVertAlignDispatch(new ToolbarUnoDispatcher(*mxVertAlign, *m_xBuilder, rxFrame))
+ , mxWriteDirection(m_xBuilder->weld_toolbar("writedirection"))
+ , mxWriteDirectionDispatch(new ToolbarUnoDispatcher(*mxWriteDirection, *m_xBuilder, rxFrame))
+ , mxIndentButtons(m_xBuilder->weld_toolbar("indentbuttons"))
+ , mxIndentButtonsDispatch(new ToolbarUnoDispatcher(*mxIndentButtons, *m_xBuilder, rxFrame))
+ , maAlignHorControl(SID_H_ALIGNCELL, *pBindings, *this)
+ , maLeftIndentControl(SID_ATTR_ALIGN_INDENT, *pBindings, *this)
+ , maMergeCellControl(FID_MERGE_TOGGLE, *pBindings, *this)
+ , maWrapTextControl(SID_ATTR_ALIGN_LINEBREAK, *pBindings, *this)
+ , maAngleControl(SID_ATTR_ALIGN_DEGREES, *pBindings, *this)
+ , maVrtStackControl(SID_ATTR_ALIGN_STACKED, *pBindings, *this)
+ , maRefEdgeControl(SID_ATTR_ALIGN_LOCKPOS, *pBindings, *this)
+ , mbMultiDisable(false)
+ , mpBindings(pBindings)
+{
+ Initialize();
+}
+
+AlignmentPropertyPanel::~AlignmentPropertyPanel()
+{
+ mxIndentButtonsDispatch.reset();
+ mxIndentButtons.reset();
+ mxWriteDirectionDispatch.reset();
+ mxWriteDirection.reset();
+ mxVertAlignDispatch.reset();
+ mxVertAlign.reset();
+ mxHorizontalAlignDispatch.reset();
+ mxHorizontalAlign.reset();
+
+ mxFTLeftIndent.reset();
+ mxMFLeftIndent.reset();
+ mxCBXWrapText.reset();
+ mxCBXMergeCell.reset();
+ mxFtRotate.reset();
+ mxMtrAngle.reset();
+ mxCBStacked.reset();
+ mxRefEdgeBottom.reset();
+ mxRefEdgeTop.reset();
+ mxRefEdgeStd.reset();
+ mxTextOrientBox.reset();
+
+ maAlignHorControl.dispose();
+ maLeftIndentControl.dispose();
+ maMergeCellControl.dispose();
+ maWrapTextControl.dispose();
+ maAngleControl.dispose();
+ maVrtStackControl.dispose();
+ maRefEdgeControl.dispose();
+}
+
+void AlignmentPropertyPanel::Initialize()
+{
+ mxFTLeftIndent->set_sensitive(false);
+ mxMFLeftIndent->set_sensitive(false);
+ Link<weld::MetricSpinButton&,void> aLink = LINK(this, AlignmentPropertyPanel, MFLeftIndentMdyHdl);
+ mxMFLeftIndent->connect_value_changed( aLink );
+
+ mxCBXMergeCell->connect_toggled( LINK(this, AlignmentPropertyPanel, CBOXMergnCellClkHdl) );
+
+ mxCBXWrapText->connect_toggled( LINK(this, AlignmentPropertyPanel, CBOXWrapTextClkHdl) );
+
+ //rotation
+ mxMtrAngle->connect_value_changed(LINK( this, AlignmentPropertyPanel, AngleModifiedHdl));
+ mxCBStacked->connect_toggled(LINK(this, AlignmentPropertyPanel, ClickStackHdl));
+
+ Link<weld::Button&,void> aLink2 = LINK(this, AlignmentPropertyPanel, ReferenceEdgeHdl);
+ mxRefEdgeBottom->connect_clicked(aLink2);
+ mxRefEdgeTop->connect_clicked(aLink2);
+ mxRefEdgeStd->connect_clicked(aLink2);
+}
+
+IMPL_LINK(AlignmentPropertyPanel, ReferenceEdgeHdl, weld::Button&, rToggle, void)
+{
+ SvxRotateMode eMode;
+ if (&rToggle == mxRefEdgeBottom.get())
+ eMode = SVX_ROTATE_MODE_BOTTOM;
+ else if (&rToggle == mxRefEdgeTop.get())
+ eMode = SVX_ROTATE_MODE_TOP;
+ else /*if (&rToggle == mxRefEdgeStd.get())*/
+ eMode = SVX_ROTATE_MODE_STANDARD;
+
+ mxRefEdgeBottom->set_active(eMode == SVX_ROTATE_MODE_BOTTOM);
+ mxRefEdgeTop->set_active(eMode == SVX_ROTATE_MODE_TOP);
+ mxRefEdgeStd->set_active(eMode == SVX_ROTATE_MODE_STANDARD);
+
+ SvxRotateModeItem aItem(eMode, ATTR_ROTATE_MODE);
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_ALIGN_LOCKPOS,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG( AlignmentPropertyPanel, AngleModifiedHdl, weld::MetricSpinButton&, void )
+{
+ Degree100 nAngle(mxMtrAngle->get_value(FieldUnit::DEGREE) * 100);
+ ScRotateValueItem aAngleItem(nAngle);
+
+ GetBindings()->GetDispatcher()->ExecuteList(
+ SID_ATTR_ALIGN_DEGREES, SfxCallMode::RECORD, { &aAngleItem });
+}
+
+IMPL_LINK_NOARG( AlignmentPropertyPanel, ClickStackHdl, weld::Toggleable&, void )
+{
+ bool bVertical = mxCBStacked->get_active();
+ ScVerticalStackCell aStackItem(bVertical);
+ GetBindings()->GetDispatcher()->ExecuteList(
+ SID_ATTR_ALIGN_STACKED, SfxCallMode::RECORD, { &aStackItem });
+}
+
+IMPL_LINK_NOARG(AlignmentPropertyPanel, MFLeftIndentMdyHdl, weld::MetricSpinButton&, void)
+{
+ sal_uInt16 nVal = mxMFLeftIndent->get_value(FieldUnit::NONE);
+ ScIndentItem aItem(static_cast<sal_uInt16>(CalcToUnit(nVal, MapUnit::MapTwip)));
+
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_ALIGN_INDENT,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(AlignmentPropertyPanel, CBOXMergnCellClkHdl, weld::Toggleable&, void)
+{
+ bool bState = mxCBXMergeCell->get_active();
+
+ if( bState)
+ GetBindings()->GetDispatcher()->Execute(FID_MERGE_ON, SfxCallMode::RECORD);
+ else
+ GetBindings()->GetDispatcher()->Execute(FID_MERGE_OFF, SfxCallMode::RECORD);
+ GetBindings()->Invalidate(FID_MERGE_TOGGLE,true);
+}
+
+IMPL_LINK_NOARG(AlignmentPropertyPanel, CBOXWrapTextClkHdl, weld::Toggleable&, void)
+{
+ bool bState = mxCBXWrapText->get_active();
+ ScLineBreakCell aItem(bState);
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_ALIGN_LINEBREAK,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+std::unique_ptr<PanelLayout> AlignmentPropertyPanel::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 AlignmentPropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to AlignmentPropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to AlignmentPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<AlignmentPropertyPanel>(pParent, rxFrame, pBindings);
+}
+
+void AlignmentPropertyPanel::HandleContextChange(
+ const vcl::EnumContext& rContext)
+{
+ if (maContext == rContext)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ maContext = rContext;
+}
+
+void AlignmentPropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch(nSID)
+ {
+ case SID_H_ALIGNCELL:
+ {
+ SvxCellHorJustify meHorAlignState = SvxCellHorJustify::Standard;
+ if(eState >= SfxItemState::DEFAULT)
+ if (auto pItem = dynamic_cast<const SvxHorJustifyItem*>( pState) )
+ meHorAlignState = pItem->GetValue();
+
+ if( meHorAlignState == SvxCellHorJustify::Repeat )
+ {
+ mxFtRotate->set_sensitive(false);
+ mxMtrAngle->set_sensitive(false);
+ }
+ else
+ {
+ mxFtRotate->set_sensitive(!mbMultiDisable);
+ mxMtrAngle->set_sensitive(!mbMultiDisable);
+ }
+
+ mxFTLeftIndent->set_sensitive( meHorAlignState == SvxCellHorJustify::Left );
+ mxMFLeftIndent->set_sensitive( meHorAlignState == SvxCellHorJustify::Left );
+ }
+ break;
+ case SID_ATTR_ALIGN_INDENT:
+ if(eState >= SfxItemState::DEFAULT && dynamic_cast<const SfxUInt16Item*>( pState) )
+ {
+ const SfxUInt16Item* pItem = static_cast<const SfxUInt16Item*>(pState);
+ sal_uInt16 nVal = pItem->GetValue();
+ mxMFLeftIndent->set_value( CalcToPoint(nVal, MapUnit::MapTwip, 1), FieldUnit::NONE );
+ }
+ else
+ {
+ mxMFLeftIndent->set_value(0, FieldUnit::NONE);
+ mxMFLeftIndent->set_text(OUString());
+ }
+ break;
+ case FID_MERGE_TOGGLE:
+ if(eState >= SfxItemState::DEFAULT && dynamic_cast<const SfxBoolItem*>( pState) )
+ {
+ mxCBXMergeCell->set_sensitive(true);
+ const SfxBoolItem* pItem = static_cast<const SfxBoolItem*>(pState);
+ mxCBXMergeCell->set_active(pItem->GetValue());
+ }
+ else
+ {
+ mxCBXMergeCell->set_active(false);
+ mxCBXMergeCell->set_sensitive(false);
+ }
+ break;
+
+ case SID_ATTR_ALIGN_LINEBREAK:
+ if(eState == SfxItemState::DISABLED)
+ {
+ mxCBXWrapText->set_active(false);
+ mxCBXWrapText->set_sensitive(false);
+ }
+ else
+ {
+ mxCBXWrapText->set_sensitive(true);
+ if(eState >= SfxItemState::DEFAULT && dynamic_cast<const ScLineBreakCell*>( pState) )
+ {
+ const ScLineBreakCell* pItem = static_cast<const ScLineBreakCell*>(pState);
+ mxCBXWrapText->set_active(pItem->GetValue());
+ }
+ else if(eState == SfxItemState::DONTCARE)
+ {
+ mxCBXWrapText->set_state(TRISTATE_INDET);
+ }
+ }
+ break;
+ case SID_ATTR_ALIGN_STACKED:
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const SfxBoolItem* pStackItem = static_cast<const ScVerticalStackCell*>(pState);
+ mbMultiDisable = pStackItem->GetValue();
+ mxCBStacked->set_active(mbMultiDisable);
+ mxTextOrientBox->set_sensitive(!mbMultiDisable);
+ }
+ else
+ {
+ mbMultiDisable = true;
+ mxTextOrientBox->set_sensitive(false);
+ mxCBStacked->set_state(TRISTATE_INDET);
+ }
+ break;
+ case SID_ATTR_ALIGN_LOCKPOS:
+ if( eState >= SfxItemState::DEFAULT)
+ {
+ const SvxRotateModeItem* pItem = static_cast<const SvxRotateModeItem*>(pState);
+ SvxRotateMode eMode = pItem->GetValue();
+ mxRefEdgeBottom->set_active(eMode == SVX_ROTATE_MODE_BOTTOM);
+ mxRefEdgeTop->set_active(eMode == SVX_ROTATE_MODE_TOP);
+ mxRefEdgeStd->set_active(eMode == SVX_ROTATE_MODE_STANDARD);
+ }
+ break;
+ case SID_ATTR_ALIGN_DEGREES:
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ Degree100 nTmp = static_cast<const ScRotateValueItem*>(pState)->GetValue();
+ mxMtrAngle->set_value(toDegrees(nTmp), FieldUnit::DEGREE);
+ }
+ else
+ {
+ mxMtrAngle->set_text( OUString() );
+ }
+ break;
+ }
+}
+
+// namespace close
+
+} // end of namespace ::sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/AlignmentPropertyPanel.hxx b/sc/source/ui/sidebar/AlignmentPropertyPanel.hxx
new file mode 100644
index 0000000000..7de9cc6330
--- /dev/null
+++ b/sc/source/ui/sidebar/AlignmentPropertyPanel.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/weldutils.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <vcl/EnumContext.hxx>
+
+namespace sc::sidebar {
+
+class AlignmentPropertyPanel
+: public PanelLayout,
+ public ::sfx2::sidebar::IContextChangeReceiver,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ 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
+ AlignmentPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+ virtual ~AlignmentPropertyPanel() override;
+
+private:
+ //ui controls
+ std::unique_ptr<weld::Label> mxFTLeftIndent;
+ std::unique_ptr<weld::MetricSpinButton> mxMFLeftIndent;
+ std::unique_ptr<weld::CheckButton> mxCBXWrapText;
+ std::unique_ptr<weld::CheckButton> mxCBXMergeCell;
+ std::unique_ptr<weld::Label> mxFtRotate;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrAngle;
+ std::unique_ptr<weld::ToggleButton> mxRefEdgeBottom;
+ std::unique_ptr<weld::ToggleButton> mxRefEdgeTop;
+ std::unique_ptr<weld::ToggleButton> mxRefEdgeStd;
+ std::unique_ptr<weld::CheckButton> mxCBStacked;
+ std::unique_ptr<weld::Widget> mxTextOrientBox;
+
+ std::unique_ptr<weld::Toolbar> mxHorizontalAlign;
+ std::unique_ptr<ToolbarUnoDispatcher> mxHorizontalAlignDispatch;
+
+ std::unique_ptr<weld::Toolbar> mxVertAlign;
+ std::unique_ptr<ToolbarUnoDispatcher> mxVertAlignDispatch;
+
+ std::unique_ptr<weld::Toolbar> mxWriteDirection;
+ std::unique_ptr<ToolbarUnoDispatcher> mxWriteDirectionDispatch;
+
+ std::unique_ptr<weld::Toolbar> mxIndentButtons;
+ std::unique_ptr<ToolbarUnoDispatcher> mxIndentButtonsDispatch;
+
+ ::sfx2::sidebar::ControllerItem maAlignHorControl;
+ ::sfx2::sidebar::ControllerItem maLeftIndentControl;
+ ::sfx2::sidebar::ControllerItem maMergeCellControl;
+ ::sfx2::sidebar::ControllerItem maWrapTextControl;
+ ::sfx2::sidebar::ControllerItem maAngleControl;
+ ::sfx2::sidebar::ControllerItem maVrtStackControl;
+ ::sfx2::sidebar::ControllerItem maRefEdgeControl;
+
+ bool mbMultiDisable : 1;
+
+ vcl::EnumContext maContext;
+ SfxBindings* mpBindings;
+
+ DECL_LINK( MFLeftIndentMdyHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( CBOXMergnCellClkHdl, weld::Toggleable&, void );
+ DECL_LINK( CBOXWrapTextClkHdl, weld::Toggleable&, void );
+ DECL_LINK( AngleModifiedHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ClickStackHdl, weld::Toggleable&, void );
+ DECL_LINK( ReferenceEdgeHdl, weld::Button&, void );
+
+ void Initialize();
+};
+
+} // end of namespace ::sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellAppearancePropertyPanel.cxx b/sc/source/ui/sidebar/CellAppearancePropertyPanel.cxx
new file mode 100644
index 0000000000..7d4081eda5
--- /dev/null
+++ b/sc/source/ui/sidebar/CellAppearancePropertyPanel.cxx
@@ -0,0 +1,506 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "CellAppearancePropertyPanel.hxx"
+#include <sc.hrc>
+#include <bitmaps.hlst>
+#include <sfx2/bindings.hxx>
+#include <sfx2/weldutils.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include "CellLineStyleControl.hxx"
+#include "CellBorderStyleControl.hxx"
+
+using namespace css;
+using namespace css::uno;
+
+constexpr OUString SETBORDERSTYLE = u"SetBorderStyle"_ustr;
+constexpr OUString LINESTYLE = u"LineStyle"_ustr;
+
+// namespace open
+
+namespace sc::sidebar {
+
+CellAppearancePropertyPanel::CellAppearancePropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+: PanelLayout(pParent, "CellAppearancePropertyPanel", "modules/scalc/ui/sidebarcellappearance.ui"),
+
+ mxTBCellBorder(m_xBuilder->weld_toolbar("cellbordertype")),
+ mxTBCellBackground(m_xBuilder->weld_toolbar("cellbackgroundcolor")),
+ mxBackColorDispatch(new ToolbarUnoDispatcher(*mxTBCellBackground, *m_xBuilder, rxFrame)),
+ mxTBLineStyle(m_xBuilder->weld_toolbar("borderlinestyle")),
+ mxTBLineColor(m_xBuilder->weld_toolbar("borderlinecolor")),
+ mxLineColorDispatch(new ToolbarUnoDispatcher(*mxTBLineColor, *m_xBuilder, rxFrame)),
+
+ mbCellBorderPopoverCreated(false),
+ mbLinePopoverCreated(false),
+
+ maLineStyleControl(SID_FRAME_LINESTYLE, *pBindings, *this),
+ maBorderOuterControl(SID_ATTR_BORDER_OUTER, *pBindings, *this),
+ maBorderInnerControl(SID_ATTR_BORDER_INNER, *pBindings, *this),
+ maGridShowControl(FID_TAB_TOGGLE_GRID, *pBindings, *this),
+ maBorderTLBRControl(SID_ATTR_BORDER_DIAG_TLBR, *pBindings, *this),
+ maBorderBLTRControl(SID_ATTR_BORDER_DIAG_BLTR, *pBindings, *this),
+
+ maIMGCellBorder(StockImage::Yes, RID_BMP_CELL_BORDER),
+ msIMGCellBorder(RID_BMP_CELL_BORDER),
+ msIMGLineStyle1(RID_BMP_LINE_STYLE1),
+ msIMGLineStyle2(RID_BMP_LINE_STYLE2),
+ msIMGLineStyle3(RID_BMP_LINE_STYLE3),
+ msIMGLineStyle4(RID_BMP_LINE_STYLE4),
+ msIMGLineStyle5(RID_BMP_LINE_STYLE5),
+ msIMGLineStyle6(RID_BMP_LINE_STYLE6),
+ msIMGLineStyle7(RID_BMP_LINE_STYLE7),
+ msIMGLineStyle8(RID_BMP_LINE_STYLE8),
+ msIMGLineStyle9(RID_BMP_LINE_STYLE9),
+
+ mnInWidth(0),
+ mnOutWidth(0),
+ mnDistance(0),
+ mnDiagTLBRInWidth(0),
+ mnDiagTLBROutWidth(0),
+ mnDiagTLBRDistance(0),
+ mnDiagBLTRInWidth(0),
+ mnDiagBLTROutWidth(0),
+ mnDiagBLTRDistance(0),
+ mbBorderStyleAvailable(true),
+ mbLeft(false),
+ mbRight(false),
+ mbTop(false),
+ mbBottom(false),
+ mbVer(false),
+ mbHor(false),
+ mbOuterBorder(false),
+ mbInnerBorder(false),
+ mbDiagTLBR(false),
+ mbDiagBLTR(false),
+ mpBindings(pBindings)
+{
+ Initialize();
+}
+
+CellAppearancePropertyPanel::~CellAppearancePropertyPanel()
+{
+ mxCellBorderPopoverContainer.reset();
+ mxTBCellBorder.reset();
+ mxBackColorDispatch.reset();
+ mxTBCellBackground.reset();
+ mxLinePopoverContainer.reset();
+ mxTBLineStyle.reset();
+ mxLineColorDispatch.reset();
+ mxTBLineColor.reset();
+
+ maLineStyleControl.dispose();
+ maBorderOuterControl.dispose();
+ maBorderInnerControl.dispose();
+ maGridShowControl.dispose();
+ maBorderTLBRControl.dispose();
+ maBorderBLTRControl.dispose();
+}
+
+void CellAppearancePropertyPanel::Initialize()
+{
+ mxTBCellBorder->set_item_icon_name(SETBORDERSTYLE, msIMGCellBorder);
+ mxCellBorderPopoverContainer.reset(new ToolbarPopupContainer(mxTBCellBorder.get()));
+ mxTBCellBorder->set_item_popover(SETBORDERSTYLE, mxCellBorderPopoverContainer->getTopLevel());
+ mxTBCellBorder->connect_clicked(LINK(this, CellAppearancePropertyPanel, TbxCellBorderSelectHdl));
+ mxTBCellBorder->connect_menu_toggled(LINK(this, CellAppearancePropertyPanel, TbxCellBorderMenuHdl));
+
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle1);
+ mxLinePopoverContainer.reset(new ToolbarPopupContainer(mxTBLineStyle.get()));
+ mxTBLineStyle->set_item_popover(LINESTYLE, mxLinePopoverContainer->getTopLevel());
+ mxTBLineStyle->connect_clicked(LINK(this, CellAppearancePropertyPanel, TbxLineStyleSelectHdl));
+ mxTBLineStyle->connect_menu_toggled(LINK(this, CellAppearancePropertyPanel, TbxLineStyleMenuHdl));
+ mxTBLineStyle->set_sensitive(false);
+
+ mxTBLineColor->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(CellAppearancePropertyPanel, TbxCellBorderSelectHdl, const OUString&, void)
+{
+ mxTBCellBorder->set_menu_item_active(SETBORDERSTYLE, !mxTBCellBorder->get_menu_item_active(SETBORDERSTYLE));
+}
+
+IMPL_LINK_NOARG(CellAppearancePropertyPanel, TbxCellBorderMenuHdl, const OUString&, void)
+{
+ if (!mxTBCellBorder->get_menu_item_active(SETBORDERSTYLE))
+ return;
+ if (!mbCellBorderPopoverCreated)
+ {
+ mxCellBorderPopoverContainer->setPopover(std::make_unique<CellBorderStylePopup>(mxTBCellBorder.get(), SETBORDERSTYLE, GetBindings()->GetDispatcher()));
+ mbCellBorderPopoverCreated = true;
+ }
+ mxCellBorderPopoverContainer->getPopover()->GrabFocus();
+}
+
+IMPL_LINK_NOARG(CellAppearancePropertyPanel, TbxLineStyleSelectHdl, const OUString&, void)
+{
+ mxTBLineStyle->set_menu_item_active(LINESTYLE, !mxTBLineStyle->get_menu_item_active(LINESTYLE));
+}
+
+IMPL_LINK_NOARG(CellAppearancePropertyPanel, TbxLineStyleMenuHdl, const OUString&, void)
+{
+ if (!mxTBLineStyle->get_menu_item_active(LINESTYLE))
+ return;
+ if (!mbLinePopoverCreated)
+ {
+ mxLinePopoverContainer->setPopover(std::make_unique<CellLineStylePopup>(mxTBLineStyle.get(), LINESTYLE, GetBindings()->GetDispatcher()));
+ mbLinePopoverCreated = true;
+ }
+ auto pPopup = static_cast<CellLineStylePopup*>(mxLinePopoverContainer->getPopover());
+ pPopup->SetLineStyleSelect(mnOutWidth, mnInWidth, mnDistance);
+ pPopup->GrabFocus();
+}
+
+std::unique_ptr<PanelLayout> CellAppearancePropertyPanel::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 CellAppearancePropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to CellAppearancePropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to CellAppearancePropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<CellAppearancePropertyPanel>(pParent, rxFrame, pBindings);
+}
+
+void CellAppearancePropertyPanel::HandleContextChange(const vcl::EnumContext& rContext)
+{
+ if (maContext == rContext)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ maContext = rContext;
+}
+
+void CellAppearancePropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch(nSID)
+ {
+ case SID_FRAME_LINESTYLE:
+ mbBorderStyleAvailable = false;
+ if( eState == SfxItemState::DONTCARE )
+ {
+ mbBorderStyleAvailable = true;
+ mnInWidth = 0;
+ mnOutWidth = 0;
+ mnDistance = 0;
+ }
+ else if(eState >= SfxItemState::DEFAULT)
+ {
+ const SvxLineItem* pSvxLineItem = dynamic_cast< const SvxLineItem* >(pState);
+ if(pSvxLineItem)
+ {
+ const editeng::SvxBorderLine* pLineItem = pSvxLineItem->GetLine();
+ mnInWidth = pLineItem->GetInWidth();
+ mnOutWidth = pLineItem->GetOutWidth();
+ mnDistance = pLineItem->GetDistance();
+ mbBorderStyleAvailable = !(mnInWidth == 0 && mnOutWidth == 0 && mnDistance == 0);
+ }
+ }
+ SetStyleIcon();
+ break;
+ case SID_ATTR_BORDER_OUTER:
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const SvxBoxItem* pBoxItem = dynamic_cast< const SvxBoxItem* >(pState);
+
+ if(pBoxItem)
+ {
+ mbLeft=false;
+ mbRight=false;
+ mbTop=false;
+ mbBottom=false;
+
+ if(pBoxItem->GetLeft())
+ mbLeft = true;
+
+ if(pBoxItem->GetRight())
+ mbRight = true;
+
+ if(pBoxItem->GetTop())
+ mbTop = true;
+
+ if(pBoxItem->GetBottom())
+ mbBottom = true;
+
+ if(!AllSettings::GetLayoutRTL())
+ UpdateCellBorder(mbTop, mbBottom, mbLeft, mbRight, mbVer, mbHor, mbDiagTLBR, mbDiagBLTR);
+ else
+ UpdateCellBorder(mbTop, mbBottom, mbRight, mbLeft, mbVer, mbHor, mbDiagTLBR, mbDiagBLTR);
+
+ if(mbLeft || mbRight || mbTop || mbBottom)
+ mbOuterBorder = true;
+ else
+ mbOuterBorder = false;
+
+ UpdateControlState();
+ }
+ }
+ break;
+ case SID_ATTR_BORDER_INNER:
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const SvxBoxInfoItem* pBoxInfoItem = dynamic_cast< const SvxBoxInfoItem* >(pState);
+ if(pBoxInfoItem)
+ {
+ bool bLeft(false), bRight(false), bTop(false), bBottom(false);
+
+ mbVer = false;
+ mbHor = false;
+
+ if(!pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::VERT ) || pBoxInfoItem->GetVert())
+ mbVer = true;
+
+ if(!pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::HORI ) || pBoxInfoItem->GetHori())
+ mbHor = true;
+
+ if(!pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::LEFT ) || mbLeft)
+ bLeft = true;
+
+ if(!pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) || mbRight)
+ bRight = true;
+
+ if(!pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::TOP ) || mbTop)
+ bTop = true;
+
+ if(!pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) || mbBottom)
+ bBottom = true;
+
+ if(!AllSettings::GetLayoutRTL())
+ UpdateCellBorder(bTop, bBottom, bLeft, bRight, mbVer, mbHor, mbDiagTLBR, mbDiagBLTR);
+ else
+ UpdateCellBorder(bTop, bBottom, bRight, bLeft, mbVer, mbHor, mbDiagTLBR, mbDiagBLTR);
+
+ if(mbVer || mbHor || bLeft || bRight || bTop || bBottom)
+ mbInnerBorder = true;
+ else
+ mbInnerBorder = false;
+
+ UpdateControlState();
+ }
+ }
+ break;
+ case SID_ATTR_BORDER_DIAG_TLBR:
+ mbDiagTLBR = false;
+ if( eState == SfxItemState::DONTCARE )
+ {
+ mbDiagTLBR = true;
+ mnDiagTLBRInWidth = mnDiagTLBROutWidth = mnDiagTLBRDistance = 0;
+ }
+ else if(eState >= SfxItemState::DEFAULT)
+ {
+ const SvxLineItem* pItem = dynamic_cast< const SvxLineItem* >(pState);
+ if(pItem)
+ {
+ const editeng::SvxBorderLine* aLine = pItem->GetLine();
+ if(aLine)
+ {
+ mnDiagTLBRInWidth = aLine->GetInWidth();
+ mnDiagTLBROutWidth = aLine->GetOutWidth();
+ mnDiagTLBRDistance = aLine->GetDistance();
+
+ mbDiagTLBR = !(mnDiagTLBRInWidth == 0 && mnDiagTLBROutWidth == 0 && mnDiagTLBRDistance == 0);
+ }
+ }
+ }
+ UpdateCellBorder(mbTop, mbBottom, mbLeft, mbRight, mbVer, mbHor, mbDiagTLBR, mbDiagBLTR);
+ UpdateControlState();
+ break;
+ case SID_ATTR_BORDER_DIAG_BLTR:
+ mbDiagBLTR = false;
+ if( eState == SfxItemState::DONTCARE )
+ {
+ mbDiagBLTR = true;
+ mnDiagBLTRInWidth = mnDiagBLTROutWidth = mnDiagBLTRDistance = 0;
+ }
+ else if(eState >= SfxItemState::DEFAULT)
+ {
+ const SvxLineItem* pItem = dynamic_cast< const SvxLineItem* >(pState);
+ if(pItem)
+ {
+ const editeng::SvxBorderLine* aLine = pItem->GetLine();
+
+ if(aLine)
+ {
+ mnDiagBLTRInWidth = aLine->GetInWidth();
+ mnDiagBLTROutWidth = aLine->GetOutWidth();
+ mnDiagBLTRDistance = aLine->GetDistance();
+
+ mbDiagBLTR = !(mnDiagBLTRInWidth == 0 && mnDiagBLTROutWidth == 0 && mnDiagBLTRDistance == 0);
+ }
+ }
+ }
+ UpdateCellBorder(mbTop, mbBottom, mbLeft, mbRight, mbVer, mbHor, mbDiagTLBR, mbDiagBLTR);
+ UpdateControlState();
+ break;
+ }
+}
+
+void CellAppearancePropertyPanel::SetStyleIcon()
+{
+ //FIXME: update for new line border possibilities
+ if(mnOutWidth == SvxBorderLineWidth::Hairline && mnInWidth == 0 && mnDistance == 0) //1
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle1);
+ else if(mnOutWidth == SvxBorderLineWidth::Medium && mnInWidth == 0 && mnDistance == 0) //2
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle2);
+ else if(mnOutWidth == SvxBorderLineWidth::Thick && mnInWidth == 0 && mnDistance == 0) //3
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle3);
+ else if(mnOutWidth == SvxBorderLineWidth::ExtraThick && mnInWidth == 0 && mnDistance == 0) //4
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle4);
+ else if(mnOutWidth == SvxBorderLineWidth::Hairline && mnInWidth == SvxBorderLineWidth::Hairline && mnDistance == SvxBorderLineWidth::Thin) //5
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle5);
+ else if(mnOutWidth == SvxBorderLineWidth::Hairline && mnInWidth == SvxBorderLineWidth::Hairline && mnDistance == SvxBorderLineWidth::Medium) //6
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle6);
+ else if(mnOutWidth == SvxBorderLineWidth::Thin && mnInWidth == SvxBorderLineWidth::Medium && mnDistance == SvxBorderLineWidth::Thin) //7
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle7);
+ else if(mnOutWidth == SvxBorderLineWidth::Medium && mnInWidth == SvxBorderLineWidth::Hairline && mnDistance == SvxBorderLineWidth::Medium) //8
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle8);
+ else if(mnOutWidth == SvxBorderLineWidth::Medium && mnInWidth == SvxBorderLineWidth::Medium && mnDistance == SvxBorderLineWidth::Medium) //9
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle9);
+ else
+ mxTBLineStyle->set_item_icon_name(LINESTYLE, msIMGLineStyle1);
+}
+
+void CellAppearancePropertyPanel::UpdateControlState()
+{
+ if(mbOuterBorder || mbInnerBorder || mbDiagTLBR || mbDiagBLTR)
+ {
+ mxTBLineColor->set_sensitive(true);
+ mxTBLineStyle->set_sensitive(true);
+
+ //set line style state
+ if( mbBorderStyleAvailable && !mbDiagTLBR && !mbDiagBLTR )
+ {
+ }
+ else if( !mbBorderStyleAvailable && mbDiagTLBR && !mbDiagBLTR )
+ {
+ mnInWidth = mnDiagTLBRInWidth;
+ mnOutWidth = mnDiagTLBROutWidth;
+ mnDistance = mnDiagTLBRDistance;
+ }
+ else if ( !mbBorderStyleAvailable && !mbDiagTLBR && mbDiagBLTR )
+ {
+ mnInWidth = mnDiagBLTRInWidth;
+ mnOutWidth = mnDiagBLTROutWidth;
+ mnDistance = mnDiagBLTRDistance;
+ }
+ else if( !mbBorderStyleAvailable && mbDiagTLBR && mbDiagBLTR)
+ {
+ if( mnDiagTLBRInWidth == mnDiagBLTRInWidth && mnDiagTLBROutWidth == mnDiagBLTROutWidth && mnDiagTLBRDistance == mnDiagBLTRDistance)
+ {
+ mnInWidth = mnDiagTLBRInWidth;
+ mnOutWidth = mnDiagTLBROutWidth;
+ mnDistance = mnDiagTLBRDistance;
+ }
+ else
+ {
+ mnInWidth = 0;
+ mnOutWidth = 0;
+ mnDistance = 0;
+ }
+ }
+ else if( mbBorderStyleAvailable && mbDiagTLBR && !mbDiagBLTR )
+ {
+ if( mnDiagTLBRInWidth != mnInWidth || mnDiagTLBROutWidth != mnOutWidth || mnDiagTLBRDistance != mnDistance)
+ {
+ mnInWidth = 0;
+ mnOutWidth = 0;
+ mnDistance = 0;
+ }
+ }
+ else if( mbBorderStyleAvailable && !mbDiagTLBR && mbDiagBLTR )
+ {
+ if( mnDiagBLTRInWidth != mnInWidth || mnDiagBLTROutWidth != mnOutWidth || mnDiagBLTRDistance != mnDistance )
+ {
+ mnInWidth = 0;
+ mnOutWidth = 0;
+ mnDistance = 0;
+ }
+ }
+ else
+ {
+ mnInWidth = 0;
+ mnOutWidth = 0;
+ mnDistance = 0;
+ }
+ SetStyleIcon();
+ }
+ else
+ {
+ mxTBLineColor->set_sensitive(false);
+ mxTBLineStyle->set_sensitive(false);
+ }
+}
+
+void CellAppearancePropertyPanel::UpdateCellBorder(bool bTop, bool bBot, bool bLeft, bool bRight,
+ bool bVer, bool bHor, bool bTLBR, bool bBLTR)
+{
+ const Size aBmpSize = maIMGCellBorder.GetBitmapEx().GetSizePixel();
+
+ if (aBmpSize.Width() == 43 && aBmpSize.Height() == 43)
+ {
+ ScopedVclPtr<VirtualDevice> pVirDev(mxTBCellBorder->create_virtual_device());
+ pVirDev->SetOutputSizePixel(aBmpSize);
+ pVirDev->SetLineColor( ::Application::GetSettings().GetStyleSettings().GetFieldTextColor() ) ;
+ pVirDev->SetFillColor(COL_BLACK);
+ pVirDev->DrawImage(Point(0, 0), maIMGCellBorder);
+ Point aTL(2, 1), aTR(42,1), aBL(2, 41), aBR(42, 41), aHL(2,21), aHR(42, 21), aVT(22,1), aVB(22, 41);
+ if(bLeft)
+ pVirDev->DrawLine( aTL,aBL );
+ if(bRight)
+ pVirDev->DrawLine( aTR,aBR );
+ if(bTop)
+ pVirDev->DrawLine( aTL,aTR );
+ if(bBot)
+ pVirDev->DrawLine( aBL,aBR );
+ if(bVer)
+ pVirDev->DrawLine( aVT,aVB );
+ if(bHor)
+ pVirDev->DrawLine( aHL,aHR );
+ if(bTLBR)
+ pVirDev->DrawLine( aTL,aBR );
+ if(bBLTR)
+ pVirDev->DrawLine( aBL,aTR );
+ mxTBCellBorder->set_item_image(SETBORDERSTYLE, pVirDev);
+ }
+ else
+ mxTBCellBorder->set_item_icon_name(SETBORDERSTYLE, msIMGCellBorder);
+}
+// namespace close
+
+} // end of namespace ::sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellAppearancePropertyPanel.hxx b/sc/source/ui/sidebar/CellAppearancePropertyPanel.hxx
new file mode 100644
index 0000000000..8ee0212399
--- /dev/null
+++ b/sc/source/ui/sidebar/CellAppearancePropertyPanel.hxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/image.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+
+class ToolbarUnoDispatcher;
+class ToolbarPopupContainer;
+
+namespace sc::sidebar {
+
+class CellAppearancePropertyPanel
+: public PanelLayout,
+ public ::sfx2::sidebar::IContextChangeReceiver,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+private:
+ friend class CellLineStylePopup;
+ friend class CellBorderStylePopup;
+
+public:
+ 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
+ CellAppearancePropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+ virtual ~CellAppearancePropertyPanel() override;
+
+private:
+ //ui controls
+
+ std::unique_ptr<ToolbarPopupContainer> mxCellBorderPopoverContainer;
+ std::unique_ptr<weld::Toolbar> mxTBCellBorder;
+ std::unique_ptr<weld::Toolbar> mxTBCellBackground;
+ std::unique_ptr<ToolbarUnoDispatcher> mxBackColorDispatch;
+ std::unique_ptr<ToolbarPopupContainer> mxLinePopoverContainer;
+ std::unique_ptr<weld::Toolbar> mxTBLineStyle;
+ std::unique_ptr<weld::Toolbar> mxTBLineColor;
+ std::unique_ptr<ToolbarUnoDispatcher> mxLineColorDispatch;
+
+ bool mbCellBorderPopoverCreated;
+ bool mbLinePopoverCreated;
+
+ ::sfx2::sidebar::ControllerItem maLineStyleControl;
+ ::sfx2::sidebar::ControllerItem maBorderOuterControl;
+ ::sfx2::sidebar::ControllerItem maBorderInnerControl;
+ ::sfx2::sidebar::ControllerItem maGridShowControl;
+ ::sfx2::sidebar::ControllerItem maBorderTLBRControl;
+ ::sfx2::sidebar::ControllerItem maBorderBLTRControl;
+
+ // images
+ Image maIMGCellBorder;
+ OUString msIMGCellBorder;
+ OUString msIMGLineStyle1;
+ OUString msIMGLineStyle2;
+ OUString msIMGLineStyle3;
+ OUString msIMGLineStyle4;
+ OUString msIMGLineStyle5;
+ OUString msIMGLineStyle6;
+ OUString msIMGLineStyle7;
+ OUString msIMGLineStyle8;
+ OUString msIMGLineStyle9;
+
+ // BorderStyle defines
+ sal_uInt16 mnInWidth;
+ sal_uInt16 mnOutWidth;
+ sal_uInt16 mnDistance;
+ sal_uInt16 mnDiagTLBRInWidth;
+ sal_uInt16 mnDiagTLBROutWidth;
+ sal_uInt16 mnDiagTLBRDistance;
+ sal_uInt16 mnDiagBLTRInWidth;
+ sal_uInt16 mnDiagBLTROutWidth;
+ sal_uInt16 mnDiagBLTRDistance;
+
+ bool mbBorderStyleAvailable : 1;
+
+ // CellBorder defines
+ bool mbLeft : 1;
+ bool mbRight : 1;
+ bool mbTop : 1;
+ bool mbBottom : 1;
+ bool mbVer : 1;
+ bool mbHor : 1;
+
+ bool mbOuterBorder : 1; // mbLeft || mbRight || mbTop || mbBottom
+ bool mbInnerBorder : 1; // mbVer || mbHor || bLeft || bRight || bTop || bBottom
+
+ bool mbDiagTLBR : 1;
+ bool mbDiagBLTR : 1;
+
+ vcl::EnumContext maContext;
+ SfxBindings* mpBindings;
+
+ DECL_LINK(TbxCellBorderSelectHdl, const OUString&, void);
+ DECL_LINK(TbxCellBorderMenuHdl, const OUString&, void);
+ DECL_LINK(TbxLineStyleSelectHdl, const OUString&, void);
+ DECL_LINK(TbxLineStyleMenuHdl, const OUString&, void);
+
+ void Initialize();
+ void SetStyleIcon();
+ void UpdateControlState();
+ void UpdateCellBorder(bool bTop, bool bBot, bool bLeft, bool bRight, bool bVer, bool bHor, bool bTLBR, bool bBLTR);
+};
+
+} // end of namespace ::sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellBorderStyleControl.cxx b/sc/source/ui/sidebar/CellBorderStyleControl.cxx
new file mode 100644
index 0000000000..e3b9e6a023
--- /dev/null
+++ b/sc/source/ui/sidebar/CellBorderStyleControl.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 "CellBorderStyleControl.hxx"
+#include <editeng/boxitem.hxx>
+#include <editeng/borderline.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/settings.hxx>
+#include <editeng/lineitem.hxx>
+#include <svl/itemset.hxx>
+#include <memory>
+
+namespace sc::sidebar {
+
+#define FRM_VALID_LEFT 0x01
+#define FRM_VALID_RIGHT 0x02
+#define FRM_VALID_TOP 0x04
+#define FRM_VALID_BOTTOM 0x08
+#define FRM_VALID_HINNER 0x10
+#define FRM_VALID_VINNER 0x20
+#define FRM_VALID_OUTER 0x0f
+#define FRM_VALID_ALL 0xff
+
+CellBorderStylePopup::CellBorderStylePopup(weld::Toolbar* pParent, const OUString& rId, SfxDispatcher* pDispatcher)
+ : WeldToolbarPopup(nullptr, pParent, "modules/scalc/ui/floatingborderstyle.ui", "FloatingBorderStyle")
+ , maToolButton(pParent, rId)
+ , mpDispatcher(pDispatcher)
+ , mxTBBorder1(m_xBuilder->weld_toolbar("border1"))
+ , mxTBBorder2(m_xBuilder->weld_toolbar("border2"))
+ , mxTBBorder3(m_xBuilder->weld_toolbar("border3"))
+ , mxTBBorder4(m_xBuilder->weld_toolbar("border4"))
+{
+ Initialize();
+}
+
+void CellBorderStylePopup::GrabFocus()
+{
+ mxTBBorder1->grab_focus();
+}
+
+CellBorderStylePopup::~CellBorderStylePopup()
+{
+}
+
+void CellBorderStylePopup::Initialize()
+{
+ mxTBBorder1->connect_clicked ( LINK(this, CellBorderStylePopup, TB1SelectHdl) );
+
+ mxTBBorder2->connect_clicked ( LINK(this, CellBorderStylePopup, TB2and3SelectHdl) );
+ mxTBBorder3->connect_clicked ( LINK(this, CellBorderStylePopup, TB2and3SelectHdl) );
+
+ mxTBBorder4->connect_clicked ( LINK(this, CellBorderStylePopup, TB4SelectHdl) );
+}
+
+IMPL_LINK(CellBorderStylePopup, TB1SelectHdl, const OUString&, rId, void)
+{
+ SvxBoxItem aBorderOuter( SID_ATTR_BORDER_OUTER );
+ SvxBoxInfoItem aBorderInner( SID_ATTR_BORDER_INNER );
+ editeng::SvxBorderLine theDefLine(nullptr, SvxBorderLineWidth::Thin);
+ editeng::SvxBorderLine *pLeft = nullptr, *pRight = nullptr, *pTop = nullptr, *pBottom = nullptr;
+ sal_uInt8 nValidFlags = 0;
+
+ if (rId == "none")
+ {
+ nValidFlags |= FRM_VALID_ALL;
+ SvxLineItem aLineItem1( SID_ATTR_BORDER_DIAG_BLTR );
+ SvxLineItem aLineItem2( SID_ATTR_BORDER_DIAG_TLBR );
+ aLineItem1.SetLine( nullptr ); //modify
+ aLineItem2.SetLine( nullptr ); //modify
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER_DIAG_BLTR, SfxCallMode::RECORD, { &aLineItem1 });
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER_DIAG_TLBR, SfxCallMode::RECORD, { &aLineItem2 });
+ }
+ else if (rId == "all")
+ {
+ pLeft = pRight = pTop = pBottom = &theDefLine;
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::VERT );
+ nValidFlags |= FRM_VALID_ALL;
+ }
+ else if (rId == "outside")
+ {
+ pLeft = pRight = pTop = pBottom = &theDefLine;
+ nValidFlags |= FRM_VALID_OUTER;
+ }
+ else if (rId == "thickbox")
+ {
+ theDefLine.SetWidth(SvxBorderLineWidth::Thick);
+ pLeft = pRight = pTop = pBottom = &theDefLine;
+ nValidFlags |= FRM_VALID_OUTER;
+ }
+
+ aBorderOuter.SetLine( pLeft, SvxBoxItemLine::LEFT );
+ aBorderOuter.SetLine( pRight, SvxBoxItemLine::RIGHT );
+ aBorderOuter.SetLine( pTop, SvxBoxItemLine::TOP );
+ aBorderOuter.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
+
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::TOP, 0 != (nValidFlags&FRM_VALID_TOP ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, 0 != (nValidFlags&FRM_VALID_BOTTOM ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, 0 != (nValidFlags&FRM_VALID_LEFT));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, 0 != (nValidFlags&FRM_VALID_RIGHT ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::HORI, 0 != (nValidFlags&FRM_VALID_HINNER ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::VERT, 0 != (nValidFlags&FRM_VALID_VINNER));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISABLE, false );
+
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER, SfxCallMode::RECORD, { &aBorderOuter, &aBorderInner });
+
+ maToolButton.set_inactive();
+}
+
+IMPL_LINK(CellBorderStylePopup, TB2and3SelectHdl, const OUString&, rId, void)
+{
+ if (rId == "diagup")
+ {
+ editeng::SvxBorderLine aTmp( nullptr, SvxBorderLineWidth::Thin );
+ SvxLineItem aLineItem( SID_ATTR_BORDER_DIAG_BLTR );
+ aLineItem.SetLine( &aTmp );
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER_DIAG_BLTR, SfxCallMode::RECORD, { &aLineItem });
+ }
+ else if (rId == "diagdown")
+ {
+ editeng::SvxBorderLine aTmp( nullptr, SvxBorderLineWidth::Thin );
+ SvxLineItem aLineItem( SID_ATTR_BORDER_DIAG_TLBR );
+ aLineItem.SetLine( &aTmp );
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER_DIAG_TLBR, SfxCallMode::RECORD, { &aLineItem });
+ }
+ else
+ {
+ SvxBoxItem aBorderOuter( SID_ATTR_BORDER_OUTER );
+ SvxBoxInfoItem aBorderInner( SID_ATTR_BORDER_INNER );
+ editeng::SvxBorderLine theDefLine(nullptr, SvxBorderLineWidth::Thin);
+ editeng::SvxBorderLine *pLeft = nullptr,
+ *pRight = nullptr,
+ *pTop = nullptr,
+ *pBottom = nullptr;
+ sal_uInt8 nValidFlags = 0;
+ if (rId == "left")
+ {
+ pLeft = &theDefLine;
+ nValidFlags |= FRM_VALID_LEFT;
+ }
+ else if (rId == "right")
+ {
+ if(!AllSettings::GetLayoutRTL())
+ {
+ pRight = &theDefLine;
+ nValidFlags |= FRM_VALID_RIGHT;
+ }
+ else
+ {
+ pLeft = &theDefLine;
+ nValidFlags |= FRM_VALID_LEFT;
+ }
+ }
+ else if (rId == "top")
+ {
+ pTop = &theDefLine;
+ nValidFlags |= FRM_VALID_TOP;
+ }
+ else if (rId == "bottom")
+ {
+ pBottom = &theDefLine;
+ nValidFlags |= FRM_VALID_BOTTOM;
+ }
+ else if (rId == "topbottom")
+ {
+ pTop = pBottom = &theDefLine;
+ nValidFlags |= FRM_VALID_BOTTOM|FRM_VALID_TOP;
+ }
+ else if (rId == "leftright")
+ {
+ pLeft = pRight = &theDefLine;
+ nValidFlags |= FRM_VALID_RIGHT|FRM_VALID_LEFT;
+ }
+ aBorderOuter.SetLine( pLeft, SvxBoxItemLine::LEFT );
+ aBorderOuter.SetLine( pRight, SvxBoxItemLine::RIGHT );
+ aBorderOuter.SetLine( pTop, SvxBoxItemLine::TOP );
+ aBorderOuter.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
+
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::TOP, 0 != (nValidFlags&FRM_VALID_TOP ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, 0 != (nValidFlags&FRM_VALID_BOTTOM ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, 0 != (nValidFlags&FRM_VALID_LEFT));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, 0 != (nValidFlags&FRM_VALID_RIGHT ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::HORI, 0 != (nValidFlags&FRM_VALID_HINNER ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::VERT, 0 != (nValidFlags&FRM_VALID_VINNER));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISABLE, false );
+
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER, SfxCallMode::RECORD, { &aBorderOuter, &aBorderInner});
+ }
+
+ maToolButton.set_inactive();
+}
+
+IMPL_LINK(CellBorderStylePopup, TB4SelectHdl, const OUString&, rId, void)
+{
+ SvxBoxItem aBorderOuter( SID_ATTR_BORDER_OUTER );
+ SvxBoxInfoItem aBorderInner( SID_ATTR_BORDER_INNER );
+ std::unique_ptr<editeng::SvxBorderLine> pTop;
+ std::unique_ptr<editeng::SvxBorderLine> pBottom;
+ sal_uInt8 nValidFlags = 0;
+ using namespace ::com::sun::star::table::BorderLineStyle;
+
+ //FIXME: properly adapt to new line border model
+
+ if (rId == "thickbottom")
+ {
+ pBottom.reset(new editeng::SvxBorderLine(nullptr, SvxBorderLineWidth::Thick));
+ nValidFlags |= FRM_VALID_BOTTOM;
+ }
+ else if (rId == "doublebottom")
+ {
+ pBottom.reset(new editeng::SvxBorderLine(nullptr));
+ pBottom->GuessLinesWidths(SvxBorderLineStyle::DOUBLE, SvxBorderLineWidth::Hairline,
+ SvxBorderLineWidth::Hairline, SvxBorderLineWidth::Thin);
+ nValidFlags |= FRM_VALID_BOTTOM;
+ }
+ else if (rId == "topthickbottom")
+ {
+ pBottom.reset(new editeng::SvxBorderLine(nullptr, SvxBorderLineWidth::Thick));
+ pTop.reset(new editeng::SvxBorderLine(nullptr, SvxBorderLineWidth::Thin));
+ nValidFlags |= FRM_VALID_BOTTOM|FRM_VALID_TOP;
+ }
+ else if (rId == "topdoublebottom")
+ {
+ pBottom.reset(new editeng::SvxBorderLine(nullptr));
+ pBottom->GuessLinesWidths(SvxBorderLineStyle::DOUBLE, SvxBorderLineWidth::Hairline,
+ SvxBorderLineWidth::Hairline, SvxBorderLineWidth::Thin);
+ pTop.reset(new editeng::SvxBorderLine(nullptr, SvxBorderLineWidth::Thin));
+ nValidFlags |= FRM_VALID_BOTTOM|FRM_VALID_TOP;
+ }
+
+ aBorderOuter.SetLine( pTop.get(), SvxBoxItemLine::TOP );
+ aBorderOuter.SetLine( pBottom.get(), SvxBoxItemLine::BOTTOM );
+ aBorderOuter.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ aBorderOuter.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::TOP, 0 != (nValidFlags&FRM_VALID_TOP ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, 0 != (nValidFlags&FRM_VALID_BOTTOM ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, 0 != (nValidFlags&FRM_VALID_LEFT ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, 0 != (nValidFlags&FRM_VALID_RIGHT ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::HORI, 0 != (nValidFlags&FRM_VALID_HINNER ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::VERT, 0 != (nValidFlags&FRM_VALID_VINNER));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISABLE, false );
+
+ mpDispatcher->ExecuteList(
+ SID_ATTR_BORDER, SfxCallMode::RECORD, { &aBorderOuter, &aBorderInner });
+
+ pTop.reset();
+ pBottom.reset();
+
+ maToolButton.set_inactive();
+}
+
+} // end of namespace sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellBorderStyleControl.hxx b/sc/source/ui/sidebar/CellBorderStyleControl.hxx
new file mode 100644
index 0000000000..95b6cb9b44
--- /dev/null
+++ b/sc/source/ui/sidebar/CellBorderStyleControl.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <svtools/toolbarmenu.hxx>
+#include <svx/colorwindow.hxx>
+
+class SfxDispatcher;
+
+namespace sc::sidebar
+{
+class CellBorderStylePopup : public WeldToolbarPopup
+{
+private:
+ MenuOrToolMenuButton maToolButton;
+ SfxDispatcher* mpDispatcher;
+ std::unique_ptr<weld::Toolbar> mxTBBorder1;
+ std::unique_ptr<weld::Toolbar> mxTBBorder2;
+ std::unique_ptr<weld::Toolbar> mxTBBorder3;
+ std::unique_ptr<weld::Toolbar> mxTBBorder4;
+
+ void Initialize();
+
+ DECL_LINK(TB1SelectHdl, const OUString&, void);
+ DECL_LINK(TB2and3SelectHdl, const OUString&, void);
+ DECL_LINK(TB4SelectHdl, const OUString&, void);
+
+public:
+ CellBorderStylePopup(weld::Toolbar* pParent, const OUString& rId, SfxDispatcher* pDispatcher);
+ virtual void GrabFocus() override;
+ virtual ~CellBorderStylePopup() override;
+};
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellLineStyleControl.cxx b/sc/source/ui/sidebar/CellLineStyleControl.cxx
new file mode 100644
index 0000000000..e9ea1ad34e
--- /dev/null
+++ b/sc/source/ui/sidebar/CellLineStyleControl.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 "CellLineStyleControl.hxx"
+#include "CellLineStyleValueSet.hxx"
+#include <vcl/i18nhelp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/lineitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/svxids.hrc>
+#include <svl/itemset.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+namespace sc::sidebar {
+
+CellLineStylePopup::CellLineStylePopup(weld::Toolbar* pParent, const OUString& rId, SfxDispatcher* pDispatcher)
+ : WeldToolbarPopup(nullptr, pParent, "modules/scalc/ui/floatinglinestyle.ui", "FloatingLineStyle")
+ , maToolButton(pParent, rId)
+ , mpDispatcher(pDispatcher)
+ , mxCellLineStyleValueSet(new sc::sidebar::CellLineStyleValueSet)
+ , mxCellLineStyleValueSetWin(new weld::CustomWeld(*m_xBuilder, "linestylevalueset", *mxCellLineStyleValueSet))
+ , mxPushButtonMoreOptions(m_xBuilder->weld_button("more"))
+{
+ Initialize();
+}
+
+CellLineStylePopup::~CellLineStylePopup()
+{
+}
+
+void CellLineStylePopup::Initialize()
+{
+ mxPushButtonMoreOptions->connect_clicked(LINK(this, CellLineStylePopup, PBClickHdl));
+
+ mxCellLineStyleValueSet->SetStyle(mxCellLineStyleValueSet->GetStyle()| WB_3DLOOK | WB_NO_DIRECTSELECT);
+
+ for(sal_uInt16 i = 1 ; i <= CELL_LINE_STYLE_ENTRIES ; i++)
+ {
+ mxCellLineStyleValueSet->InsertItem(i);
+ }
+
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper();
+ maStr[0] = ScResId(STR_BORDER_HAIRLINE).replaceFirst("%s", rI18nHelper.GetNum(5, 2));
+ maStr[1] = ScResId(STR_BORDER_VERY_THIN).replaceFirst("%s", rI18nHelper.GetNum(50, 2));
+ maStr[2] = ScResId(STR_BORDER_THIN).replaceFirst("%s", rI18nHelper.GetNum(75, 2));
+ maStr[3] = ScResId(STR_BORDER_MEDIUM).replaceFirst("%s", rI18nHelper.GetNum(150, 2));
+ maStr[4] = ScResId(STR_BORDER_THICK).replaceFirst("%s", rI18nHelper.GetNum(225, 2));
+ maStr[5] = ScResId(STR_BORDER_EXTRA_THICK).replaceFirst("%s", rI18nHelper.GetNum(450, 2));
+
+ // Numbers in pt are the total width of the double line (inner + outer + distance)
+ maStr[6] = ScResId(STR_BORDER_DOUBLE_1).replaceFirst("%s", rI18nHelper.GetNum(110, 2));
+ maStr[7] = ScResId(STR_BORDER_DOUBLE_1).replaceFirst("%s", rI18nHelper.GetNum(235, 2));
+ maStr[8] = ScResId(STR_BORDER_DOUBLE_2).replaceFirst("%s", rI18nHelper.GetNum(300, 2));
+ maStr[9] = ScResId(STR_BORDER_DOUBLE_3).replaceFirst("%s", rI18nHelper.GetNum(305, 2));
+ maStr[10] = ScResId(STR_BORDER_DOUBLE_4).replaceFirst("%s", rI18nHelper.GetNum(450, 2));
+ mxCellLineStyleValueSet->SetUnit(&maStr[0]);
+
+ for (sal_uInt16 i = 1; i <= CELL_LINE_STYLE_ENTRIES; ++i)
+ {
+ mxCellLineStyleValueSet->SetItemText(i, maStr[i-1]);
+ }
+
+ SetAllNoSel();
+ mxCellLineStyleValueSet->SetSelectHdl(LINK(this, CellLineStylePopup, VSSelectHdl));
+}
+
+void CellLineStylePopup::GrabFocus()
+{
+ mxCellLineStyleValueSet->GrabFocus();
+}
+
+void CellLineStylePopup::SetAllNoSel()
+{
+ mxCellLineStyleValueSet->SelectItem(0);
+ mxCellLineStyleValueSet->SetNoSelection();
+ mxCellLineStyleValueSet->SetFormat();
+ mxCellLineStyleValueSet->Invalidate();
+}
+
+IMPL_LINK_NOARG(CellLineStylePopup, VSSelectHdl, ValueSet*, void)
+{
+ const sal_uInt16 iPos(mxCellLineStyleValueSet->GetSelectedItemId());
+ SvxLineItem aLineItem(SID_FRAME_LINESTYLE);
+ SvxBorderLineStyle nStyle = SvxBorderLineStyle::SOLID;
+ sal_uInt16 n1 = 0;
+ sal_uInt16 n2 = 0;
+ sal_uInt16 n3 = 0;
+
+ //FIXME: fully for new border line possibilities
+
+ switch(iPos)
+ {
+ case 1:
+ n1 = SvxBorderLineWidth::Hairline;
+ break;
+ case 2:
+ n1 = SvxBorderLineWidth::VeryThin;
+ break;
+ case 3:
+ n1 = SvxBorderLineWidth::Thin;
+ break;
+ case 4:
+ n1 = SvxBorderLineWidth::Medium;
+ break;
+ case 5:
+ n1 = SvxBorderLineWidth::Thick;
+ break;
+ case 6:
+ n1 = SvxBorderLineWidth::ExtraThick;
+ break;
+ case 7:
+ n1 = SvxBorderLineWidth::Hairline;
+ n2 = SvxBorderLineWidth::Hairline;
+ n3 = SvxBorderLineWidth::Medium;
+ nStyle = SvxBorderLineStyle::DOUBLE;
+ break;
+ case 8:
+ n1 = SvxBorderLineWidth::Hairline;
+ n2 = SvxBorderLineWidth::Hairline;
+ n3 = SvxBorderLineWidth::Thick;
+ nStyle = SvxBorderLineStyle::DOUBLE;
+ break;
+ case 9:
+ n1 = SvxBorderLineWidth::Thin;
+ n2 = SvxBorderLineWidth::Medium;
+ n3 = SvxBorderLineWidth::Thin;
+ nStyle = SvxBorderLineStyle::DOUBLE;
+ break;
+ case 10:
+ n1 = SvxBorderLineWidth::Medium;
+ n2 = SvxBorderLineWidth::Hairline;
+ n3 = SvxBorderLineWidth::Medium;
+ nStyle = SvxBorderLineStyle::DOUBLE;
+ break;
+ case 11:
+ n1 = SvxBorderLineWidth::Medium;
+ n2 = SvxBorderLineWidth::Medium;
+ n3 = SvxBorderLineWidth::Medium;
+ nStyle = SvxBorderLineStyle::DOUBLE;
+ break;
+ default:
+ break;
+ }
+
+ editeng::SvxBorderLine aTmp;
+ aTmp.GuessLinesWidths(nStyle, n1, n2, n3);
+ aLineItem.SetLine( &aTmp );
+ mpDispatcher->ExecuteList(
+ SID_FRAME_LINESTYLE, SfxCallMode::RECORD, { &aLineItem });
+ SetAllNoSel();
+
+ maToolButton.set_inactive();
+}
+
+IMPL_LINK_NOARG(CellLineStylePopup, PBClickHdl, weld::Button&, void)
+{
+ mpDispatcher->Execute(SID_CELL_FORMAT_BORDER, SfxCallMode::ASYNCHRON);
+ maToolButton.set_inactive();
+}
+
+void CellLineStylePopup::SetLineStyleSelect(sal_uInt16 out, sal_uInt16 in, sal_uInt16 dis)
+{
+ mxCellLineStyleValueSet->GrabFocus();
+ SetAllNoSel();
+
+ //FIXME: fully for new border line possibilities
+
+ if(out == SvxBorderLineWidth::Hairline && in == 0 && dis == 0) //1
+ {
+ mxCellLineStyleValueSet->SetSelItem(1);
+ }
+ else if(out == SvxBorderLineWidth::VeryThin && in == 0 && dis == 0) //2
+ {
+ mxCellLineStyleValueSet->SetSelItem(2);
+ }
+ else if(out == SvxBorderLineWidth::Thin && in == 0 && dis == 0) //3
+ {
+ mxCellLineStyleValueSet->SetSelItem(3);
+ }
+ else if(out == SvxBorderLineWidth::Medium && in == 0 && dis == 0) //4
+ {
+ mxCellLineStyleValueSet->SetSelItem(4);
+ }
+ else if(out == SvxBorderLineWidth::Thick && in == 0 && dis == 0) //5
+ {
+ mxCellLineStyleValueSet->SetSelItem(5);
+ }
+ else if(out == SvxBorderLineWidth::ExtraThick && in == 0 && dis == 0) //6
+ {
+ mxCellLineStyleValueSet->SetSelItem(6);
+ }
+ else if(out == SvxBorderLineWidth::Hairline && in == SvxBorderLineWidth::Hairline && dis == SvxBorderLineWidth::Thin) //7
+ {
+ mxCellLineStyleValueSet->SetSelItem(7);
+ }
+ else if(out == SvxBorderLineWidth::Hairline && in == SvxBorderLineWidth::Hairline && dis == SvxBorderLineWidth::Medium) //8
+ {
+ mxCellLineStyleValueSet->SetSelItem(8);
+ }
+ else if(out == SvxBorderLineWidth::Thin && in == SvxBorderLineWidth::Medium && dis == SvxBorderLineWidth::Thin) //9
+ {
+ mxCellLineStyleValueSet->SetSelItem(9);
+ }
+ else if(out == SvxBorderLineWidth::Medium && in == SvxBorderLineWidth::Hairline && dis == SvxBorderLineWidth::Medium) //10
+ {
+ mxCellLineStyleValueSet->SetSelItem(10);
+ }
+ else if(out == SvxBorderLineWidth::Medium && in == SvxBorderLineWidth::Medium && dis == SvxBorderLineWidth::Medium) //11
+ {
+ mxCellLineStyleValueSet->SetSelItem(11);
+ }
+
+ else
+ {
+ mxCellLineStyleValueSet->SetSelItem(0);
+ mxPushButtonMoreOptions->grab_focus();
+ }
+ mxCellLineStyleValueSet->SetFormat();
+ mxCellLineStyleValueSet->Invalidate();
+}
+
+} // end of namespace sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellLineStyleControl.hxx b/sc/source/ui/sidebar/CellLineStyleControl.hxx
new file mode 100644
index 0000000000..0955592e49
--- /dev/null
+++ b/sc/source/ui/sidebar/CellLineStyleControl.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 .
+ */
+#pragma once
+
+#include <svtools/toolbarmenu.hxx>
+#include <svx/colorwindow.hxx>
+#include "CellLineStyleValueSet.hxx"
+
+class SfxDispatcher;
+
+namespace sc::sidebar
+{
+class CellLineStylePopup : public WeldToolbarPopup
+{
+private:
+ MenuOrToolMenuButton maToolButton;
+ SfxDispatcher* mpDispatcher;
+ std::unique_ptr<CellLineStyleValueSet> mxCellLineStyleValueSet;
+ std::unique_ptr<weld::CustomWeld> mxCellLineStyleValueSetWin;
+ std::unique_ptr<weld::Button> mxPushButtonMoreOptions;
+ OUString maStr[CELL_LINE_STYLE_ENTRIES];
+
+ void Initialize();
+ void SetAllNoSel();
+
+ DECL_LINK(VSSelectHdl, ValueSet*, void);
+ DECL_LINK(PBClickHdl, weld::Button&, void);
+
+public:
+ CellLineStylePopup(weld::Toolbar* pParent, const OUString& rId, SfxDispatcher* pDispatcher);
+ void SetLineStyleSelect(sal_uInt16 out, sal_uInt16 in, sal_uInt16 dis);
+ virtual void GrabFocus() override;
+ virtual ~CellLineStylePopup() override;
+};
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellLineStyleValueSet.cxx b/sc/source/ui/sidebar/CellLineStyleValueSet.cxx
new file mode 100644
index 0000000000..d7c9e9b3ce
--- /dev/null
+++ b/sc/source/ui/sidebar/CellLineStyleValueSet.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 "CellLineStyleValueSet.hxx"
+#include <i18nlangtag/mslangid.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+namespace sc::sidebar {
+
+CellLineStyleValueSet::CellLineStyleValueSet()
+ : ValueSet(nullptr)
+ , mnMaxTextWidth(0)
+ , nSelItem(0)
+{
+}
+
+CellLineStyleValueSet::~CellLineStyleValueSet()
+{
+}
+
+void CellLineStyleValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ValueSet::SetDrawingArea(pDrawingArea);
+ Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(120, 12 * CELL_LINE_STYLE_ENTRIES),
+ MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+
+ SetColCount();
+ SetLineCount(CELL_LINE_STYLE_ENTRIES);
+}
+
+void CellLineStyleValueSet::SetUnit(const OUString* str)
+{
+ for (int i = 0; i < CELL_LINE_STYLE_ENTRIES; ++i)
+ {
+ maStrUnit[i] = str[i];
+ }
+}
+
+void CellLineStyleValueSet::SetSelItem(sal_uInt16 nSel)
+{
+ nSelItem = nSel;
+ if(nSel == 0)
+ {
+ SelectItem(1);
+ SetNoSelection();
+ }
+ else
+ {
+ SelectItem(nSelItem);
+ GrabFocus();
+ }
+}
+
+tools::Long CellLineStyleValueSet::GetMaxTextWidth(const vcl::RenderContext* pDev)
+{
+ if (mnMaxTextWidth > 0)
+ return mnMaxTextWidth;
+
+ for (int i = 0; i < CELL_LINE_STYLE_ENTRIES; ++i)
+ {
+ mnMaxTextWidth = std::max(pDev->GetTextWidth(maStrUnit[i]), mnMaxTextWidth);
+ }
+ return mnMaxTextWidth;
+}
+
+void CellLineStyleValueSet::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();
+
+ 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 );
+
+ 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);
+ }
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ //draw text
+ if (nSelItem == nItemId )
+ aFont.SetColor(COL_WHITE);
+ else
+ aFont.SetColor(rStyleSettings.GetFieldTextColor()); //high contrast
+
+ pDev->SetFont(aFont);
+ tools::Long nTextWidth = GetMaxTextWidth(pDev);
+ tools::Long nTLX = aBLPos.X() + 5, nTLY = aBLPos.Y() + ( nRectHeight - nItemId )/2;
+ tools::Long nTRX = aBLPos.X() + nRectWidth - nTextWidth - 15, nTRY = aBLPos.Y() + ( nRectHeight - nItemId )/2;
+ Point aStart(aBLPos.X() + nRectWidth - nTextWidth - 5 , aBLPos.Y() + nRectHeight/6);
+ pDev->DrawText(aStart, maStrUnit[nItemId - 1]); //can't set DrawTextFlags::EndEllipsis here, or the text will disappear
+
+ //draw line
+ if( nSelItem == nItemId )
+ {
+ pDev->SetFillColor(COL_WHITE);
+ pDev->SetLineColor(COL_WHITE);
+ }
+ else
+ {
+ pDev->SetFillColor(rStyleSettings.GetFieldTextColor()); //high contrast
+ pDev->SetLineColor(rStyleSettings.GetFieldTextColor()); //high contrast
+ }
+
+ switch( nItemId )
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY , nTRX, nTRY + nItemId * 2 - 1 ));
+ break;
+ case 7:
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY , nTRX, nTRY + 1 ));
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY + 3 , nTRX, nTRY + 4 ));
+ break;
+ case 8:
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY , nTRX, nTRY + 1 ));
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY + 5 , nTRX, nTRY + 6 ));
+ break;
+ case 9:
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY , nTRX, nTRY + 1 ));
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY + 3 , nTRX, nTRY + 6 ));
+ break;
+ case 10:
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY , nTRX, nTRY + 3 ));
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY + 5 , nTRX, nTRY + 6 ));
+ break;
+ case 11:
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY , nTRX, nTRY + 3 ));
+ pDev->DrawRect(tools::Rectangle(nTLX, nTLY + 5 , nTRX, nTRY + 8 ));
+ break;
+ }
+
+ Invalidate( aRect );
+ pDev->SetLineColor(aOldColor);
+ pDev->SetFillColor(aOldFillColor);
+ pDev->SetFont(aOldFont);
+
+}
+
+} // end of namespace sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/CellLineStyleValueSet.hxx b/sc/source/ui/sidebar/CellLineStyleValueSet.hxx
new file mode 100644
index 0000000000..79dd08fb39
--- /dev/null
+++ b/sc/source/ui/sidebar/CellLineStyleValueSet.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 .
+ */
+#pragma once
+
+#include <svtools/valueset.hxx>
+#include <tools/long.hxx>
+
+#define CELL_LINE_STYLE_ENTRIES 11
+
+namespace sc::sidebar
+{
+class CellLineStyleValueSet : public ValueSet
+{
+private:
+ tools::Long mnMaxTextWidth;
+ sal_uInt16 nSelItem;
+ OUString maStrUnit[CELL_LINE_STYLE_ENTRIES];
+
+public:
+ CellLineStyleValueSet();
+ virtual ~CellLineStyleValueSet() override;
+
+ void SetUnit(const OUString* str);
+ void SetSelItem(sal_uInt16 nSel);
+ tools::Long GetMaxTextWidth(const vcl::RenderContext* pDev);
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ virtual void UserDraw(const UserDrawEvent& rUDEvt) override;
+};
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/NumberFormatControl.cxx b/sc/source/ui/sidebar/NumberFormatControl.cxx
new file mode 100644
index 0000000000..1060f0f5db
--- /dev/null
+++ b/sc/source/ui/sidebar/NumberFormatControl.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 <NumberFormatControl.hxx>
+#include <cbnumberformat.hxx>
+#include <svl/intitem.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace sc;
+
+SFX_IMPL_TOOLBOX_CONTROL(ScNumberFormatControl, SfxUInt16Item);
+
+ScNumberFormatControl::ScNumberFormatControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+ : SfxToolBoxControl(nSlotId, nId, rTbx)
+{
+}
+
+ScNumberFormatControl::~ScNumberFormatControl()
+{
+}
+
+void ScNumberFormatControl::StateChangedAtToolBoxControl(sal_uInt16, SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox& rTbx = GetToolBox();
+ ScNumberFormat* pComboBox = static_cast<ScNumberFormat*>(rTbx.GetItemWindow(nId));
+
+ DBG_ASSERT( pComboBox, "Control not found!" );
+
+ if(SfxItemState::DISABLED == eState)
+ pComboBox->Disable();
+ else
+ pComboBox->Enable();
+
+ rTbx.EnableItem(nId, SfxItemState::DISABLED != eState);
+
+ switch(eState)
+ {
+ case SfxItemState::DEFAULT:
+ {
+ const SfxUInt16Item* pItem = static_cast<const SfxUInt16Item*>(pState);
+ sal_uInt16 nVal = pItem->GetValue();
+ pComboBox->set_active(nVal);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+VclPtr<InterimItemWindow> ScNumberFormatControl::CreateItemWindow( vcl::Window *pParent )
+{
+ VclPtr<ScNumberFormat> pControl = VclPtr<ScNumberFormat>::Create(pParent);
+ pControl->Show();
+
+ return pControl;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/NumberFormatPropertyPanel.cxx b/sc/source/ui/sidebar/NumberFormatPropertyPanel.cxx
new file mode 100644
index 0000000000..c604d41b06
--- /dev/null
+++ b/sc/source/ui/sidebar/NumberFormatPropertyPanel.cxx
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "NumberFormatPropertyPanel.hxx"
+#include <sc.hrc>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemset.hxx>
+#include <svx/numfmtsh.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sc::sidebar {
+
+NumberFormatPropertyPanel::NumberFormatPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+ : PanelLayout(pParent,"NumberFormatPropertyPanel", "modules/scalc/ui/sidebarnumberformat.ui")
+ , mxLbCategory(m_xBuilder->weld_combo_box("numberformatcombobox"))
+ , mxTBCategory(m_xBuilder->weld_toolbar("numberformat"))
+ , mxCategoryDispatch(new ToolbarUnoDispatcher(*mxTBCategory, *m_xBuilder, rxFrame))
+ , mxFtDecimals(m_xBuilder->weld_label("decimalplaceslabel"))
+ , mxEdDecimals(m_xBuilder->weld_spin_button("decimalplaces"))
+ , mxFtDenominator(m_xBuilder->weld_label("denominatorplaceslabel"))
+ , mxEdDenominator(m_xBuilder->weld_spin_button("denominatorplaces"))
+ , mxFtLeadZeroes(m_xBuilder->weld_label("leadingzeroeslabel"))
+ , mxEdLeadZeroes(m_xBuilder->weld_spin_button("leadingzeroes"))
+ , mxBtnNegRed(m_xBuilder->weld_check_button("negativenumbersred"))
+ , mxBtnThousand(m_xBuilder->weld_check_button("thousandseparator"))
+ , mxBtnEngineering(m_xBuilder->weld_check_button("engineeringnotation"))
+ , maNumFormatControl(SID_NUMBER_TYPE_FORMAT, *pBindings, *this)
+ , maFormatControl(SID_NUMBER_FORMAT, *pBindings, *this)
+ , mnCategorySelected(0)
+ , mpBindings(pBindings)
+{
+ Initialize();
+}
+
+NumberFormatPropertyPanel::~NumberFormatPropertyPanel()
+{
+ mxLbCategory.reset();
+ mxCategoryDispatch.reset();
+ mxTBCategory.reset();
+ mxFtDecimals.reset();
+ mxEdDecimals.reset();
+ mxFtDenominator.reset();
+ mxEdDenominator.reset();
+ mxFtLeadZeroes.reset();
+ mxEdLeadZeroes.reset();
+ mxBtnNegRed.reset();
+ mxBtnThousand.reset();
+ mxBtnEngineering.reset();
+
+ maNumFormatControl.dispose();
+ maFormatControl.dispose();
+}
+
+void NumberFormatPropertyPanel::Initialize()
+{
+ mxLbCategory->connect_changed( LINK(this, NumberFormatPropertyPanel, NumFormatSelectHdl) );
+ mxLbCategory->set_active(0);
+
+ Link<weld::SpinButton&,void> aLink = LINK(this, NumberFormatPropertyPanel, NumFormatValueHdl);
+
+ mxEdDecimals->connect_value_changed( aLink );
+ mxEdDenominator->connect_value_changed( aLink );
+ mxEdLeadZeroes->connect_value_changed( aLink );
+
+ mxBtnNegRed->connect_toggled( LINK(this, NumberFormatPropertyPanel, NumFormatValueClickHdl) );
+ mxBtnThousand->connect_toggled( LINK(this, NumberFormatPropertyPanel, NumFormatValueClickHdl) );
+ mxBtnEngineering->connect_toggled( LINK(this, NumberFormatPropertyPanel, NumFormatValueClickHdl) );
+}
+
+IMPL_LINK( NumberFormatPropertyPanel, NumFormatSelectHdl, weld::ComboBox&, rBox, void )
+{
+ const sal_Int32 nVal = rBox.get_active();
+ if( nVal != mnCategorySelected )
+ {
+ SfxUInt16Item aItem( SID_NUMBER_TYPE_FORMAT, nVal );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_NUMBER_TYPE_FORMAT,
+ SfxCallMode::RECORD, { &aItem });
+ mnCategorySelected = nVal;
+ }
+}
+
+IMPL_LINK_NOARG( NumberFormatPropertyPanel, NumFormatValueClickHdl, weld::Toggleable&, void )
+{
+ NumFormatValueHdl(*mxEdDecimals);
+}
+
+IMPL_LINK_NOARG( NumberFormatPropertyPanel, NumFormatValueHdl, weld::SpinButton&, void )
+{
+ OUString aFormat;
+ OUString sBreak = ",";
+ bool bThousand = ( mxBtnThousand->get_visible() && mxBtnThousand->get_sensitive() && mxBtnThousand->get_active() )
+ || ( mxBtnEngineering->get_visible() && mxBtnEngineering->get_sensitive() && mxBtnEngineering->get_active() );
+ bool bNegRed = mxBtnNegRed->get_sensitive() && mxBtnNegRed->get_active();
+ sal_uInt16 nPrecision = (mxEdDecimals->get_sensitive() && mxEdDecimals->get_visible())
+ ? static_cast<sal_uInt16>(mxEdDecimals->get_value())
+ : (mxEdDenominator->get_sensitive() && mxEdDenominator->get_visible())
+ ? static_cast<sal_uInt16>(mxEdDenominator->get_value())
+ : sal_uInt16(0);
+ sal_uInt16 nLeadZeroes = (mxEdLeadZeroes->get_sensitive())
+ ? static_cast<sal_uInt16>(mxEdLeadZeroes->get_value())
+ : sal_uInt16(0);
+
+ OUString sThousand = OUString::number(static_cast<sal_Int32>(bThousand));
+ OUString sNegRed = OUString::number(static_cast<sal_Int32>(bNegRed));
+ OUString sPrecision = OUString::number(nPrecision);
+ OUString sLeadZeroes = OUString::number(nLeadZeroes);
+
+ aFormat += sThousand +
+ sBreak +
+ sNegRed +
+ sBreak +
+ sPrecision +
+ sBreak +
+ sLeadZeroes +
+ sBreak;
+
+ SfxStringItem aItem( SID_NUMBER_FORMAT, aFormat );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_NUMBER_FORMAT,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+std::unique_ptr<PanelLayout> NumberFormatPropertyPanel::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 NumberFormatPropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to NumberFormatPropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to NumberFormatPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<NumberFormatPropertyPanel>(pParent, rxFrame, pBindings);
+}
+
+void NumberFormatPropertyPanel::HandleContextChange(
+ const vcl::EnumContext& rContext)
+{
+ if(maContext == rContext)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ maContext = rContext;
+}
+
+void NumberFormatPropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch(nSID)
+ {
+ case SID_NUMBER_TYPE_FORMAT:
+ {
+ if( eState >= SfxItemState::DEFAULT)
+ {
+ const SfxUInt16Item* pItem = static_cast<const SfxUInt16Item*>(pState);
+ sal_uInt16 nVal = pItem->GetValue();
+ mnCategorySelected = nVal;
+ mxLbCategory->set_active(nVal);
+ // There is an offset between category list enum and listbox in side panel
+ SvxNumberFormatCategory nCategory = static_cast< SvxNumberFormatCategory >( nVal + 1 );
+ if (nCategory <= CAT_FRACTION && // General, Number, Percent, Currency, Time, Scientific, Fraction
+ nCategory != CAT_DATE ) // not Date
+ {
+ // For scientific, Thousand separator is replaced by Engineering notation
+ bool bIsScientific ( nCategory == CAT_SCIENTIFIC );
+ // For fraction, Decimal places is replaced by Denominator places
+ bool bIsFraction ( nCategory == CAT_FRACTION );
+ // For Time, Decimal places and NegRed available
+ bool bIsTime ( nCategory == CAT_TIME );
+ mxBtnThousand->set_visible( !bIsScientific );
+ mxBtnThousand->set_sensitive( !bIsScientific && !bIsTime );
+ mxBtnThousand->set_active(false);
+ mxBtnEngineering->set_visible(bIsScientific);
+ mxBtnEngineering->set_sensitive(bIsScientific);
+ mxBtnEngineering->set_active(false);
+ mxBtnNegRed->set_sensitive(true);
+ mxFtDenominator->set_visible(bIsFraction);
+ mxEdDenominator->set_visible(bIsFraction);
+ mxFtDenominator->set_sensitive(bIsFraction);
+ mxEdDenominator->set_sensitive(bIsFraction);
+ mxFtDecimals->set_visible(!bIsFraction);
+ mxEdDecimals->set_visible(!bIsFraction);
+ mxFtDecimals->set_sensitive(!bIsFraction);
+ mxEdDecimals->set_sensitive(!bIsFraction);
+ mxFtLeadZeroes->set_sensitive( !bIsTime );
+ mxEdLeadZeroes->set_sensitive( !bIsTime );
+ }
+ else
+ DisableControls();
+ }
+ else
+ {
+ DisableControls();
+ mxLbCategory->set_active(-1);
+ mnCategorySelected = 0;
+ }
+ }
+ break;
+ case SID_NUMBER_FORMAT:
+ {
+ bool bThousand = false;
+ bool bNegRed = false;
+ sal_uInt16 nPrecision = 0;
+ sal_uInt16 nLeadZeroes = 0;
+ bool bNatNum12 = false;
+ SvxNumberFormatCategory nCategory = static_cast< SvxNumberFormatCategory >( mnCategorySelected + 1 );
+ if( eState >= SfxItemState::DEFAULT)
+ {
+ const SfxStringItem* pItem = static_cast<const SfxStringItem*>(pState);
+ const OUString& aCode = pItem->GetValue();
+ sal_Int32 nIndex = 0;
+ sal_Int32 aFormat[5] = {0};
+ for (sal_Int32 & rn : aFormat)
+ {
+ rn = o3tl::toInt32(o3tl::getToken(aCode, 0, ',', nIndex));
+ if (nIndex<0)
+ break;
+ }
+ bThousand = static_cast<bool>(aFormat[0]);
+ bNegRed = static_cast<bool>(aFormat[1]);
+ nPrecision = static_cast<sal_uInt16>(aFormat[2]);
+ nLeadZeroes = static_cast<sal_uInt16>(aFormat[3]);
+ bNatNum12 = static_cast< bool >( aFormat[4] );
+ }
+ else
+ {
+ bThousand = false;
+ bNegRed = false;
+ nPrecision = 0;
+ nLeadZeroes = 1;
+ }
+ if ( nCategory == CAT_NUMBER ||
+ nCategory == CAT_PERCENT ||
+ nCategory == CAT_CURRENCY ||
+ nCategory == CAT_FRACTION )
+ mxBtnThousand->set_sensitive( !bNatNum12 );
+ if ( mxBtnThousand->get_visible() )
+ mxBtnThousand->set_active(bThousand);
+ else if ( mxBtnEngineering->get_visible() )
+ mxBtnEngineering->set_active(bThousand);
+ mxBtnNegRed->set_active(bNegRed);
+ if ( mxLbCategory->get_active() == 0 )
+ mxEdDecimals->set_text(""); // tdf#44399
+ else if ( mxEdDecimals->get_visible() )
+ mxEdDecimals->set_value(nPrecision);
+ else if ( mxEdDenominator->get_visible() )
+ mxEdDenominator->set_value(nPrecision);
+ mxEdLeadZeroes->set_value(nLeadZeroes);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void NumberFormatPropertyPanel::DisableControls()
+{
+ mxBtnEngineering->hide();
+ mxBtnThousand->show();
+ mxBtnThousand->set_sensitive(false);
+ mxBtnNegRed->set_sensitive(false);
+ mxFtDenominator->hide();
+ mxEdDenominator->hide();
+ mxFtDecimals->show();
+ mxEdDecimals->show();
+ mxFtDecimals->set_sensitive(false);
+ mxEdDecimals->set_sensitive(false);
+ mxFtLeadZeroes->set_sensitive(false);
+ mxEdLeadZeroes->set_sensitive(false);
+}
+
+} // end of namespace ::sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/NumberFormatPropertyPanel.hxx b/sc/source/ui/sidebar/NumberFormatPropertyPanel.hxx
new file mode 100644
index 0000000000..7f16dffb92
--- /dev/null
+++ b/sc/source/ui/sidebar/NumberFormatPropertyPanel.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 .
+ */
+#pragma once
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/weldutils.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <vcl/EnumContext.hxx>
+
+namespace sc::sidebar {
+
+class NumberFormatPropertyPanel
+: public PanelLayout,
+ public ::sfx2::sidebar::IContextChangeReceiver,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+public:
+ 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
+ NumberFormatPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+ virtual ~NumberFormatPropertyPanel() override;
+private:
+ //ui controls
+ std::unique_ptr<weld::ComboBox> mxLbCategory;
+ std::unique_ptr<weld::Toolbar> mxTBCategory;
+ std::unique_ptr<ToolbarUnoDispatcher> mxCategoryDispatch;
+ std::unique_ptr<weld::Label> mxFtDecimals;
+ std::unique_ptr<weld::SpinButton> mxEdDecimals;
+ std::unique_ptr<weld::Label> mxFtDenominator;
+ std::unique_ptr<weld::SpinButton> mxEdDenominator;
+ std::unique_ptr<weld::Label> mxFtLeadZeroes;
+ std::unique_ptr<weld::SpinButton> mxEdLeadZeroes;
+ std::unique_ptr<weld::CheckButton> mxBtnNegRed;
+ std::unique_ptr<weld::CheckButton> mxBtnThousand;
+ std::unique_ptr<weld::CheckButton> mxBtnEngineering;
+
+ ::sfx2::sidebar::ControllerItem maNumFormatControl;
+ ::sfx2::sidebar::ControllerItem maFormatControl;
+
+ sal_Int32 mnCategorySelected;
+
+ vcl::EnumContext maContext;
+ SfxBindings* mpBindings;
+
+ DECL_LINK(NumFormatSelectHdl, weld::ComboBox&, void);
+ DECL_LINK(NumFormatValueHdl, weld::SpinButton&, void);
+ DECL_LINK(NumFormatValueClickHdl, weld::Toggleable&, void);
+
+ void Initialize();
+ void DisableControls();
+};
+
+} // end of namespace ::sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/ScPanelFactory.cxx b/sc/source/ui/sidebar/ScPanelFactory.cxx
new file mode 100644
index 0000000000..bbcff315c2
--- /dev/null
+++ b/sc/source/ui/sidebar/ScPanelFactory.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 "ScPanelFactory.hxx"
+
+#include "AlignmentPropertyPanel.hxx"
+#include "CellAppearancePropertyPanel.hxx"
+#include "NumberFormatPropertyPanel.hxx"
+#include <navipi.hxx>
+#include <dwfunctr.hxx>
+
+#include <sfx2/sidebar/SidebarPanelBase.hxx>
+#include <vcl/weldutils.hxx>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sc::sidebar {
+
+ScPanelFactory::ScPanelFactory()
+{
+}
+
+ScPanelFactory::~ScPanelFactory()
+{
+}
+
+Reference<ui::XUIElement> SAL_CALL ScPanelFactory::createUIElement (
+ const OUString& rsResourceURL,
+ const ::css::uno::Sequence<css::beans::PropertyValue>& rArguments)
+{
+ Reference<ui::XUIElement> xElement;
+
+ try
+ {
+ const ::comphelper::NamedValueCollection aArguments (rArguments);
+ Reference<frame::XFrame> xFrame (aArguments.getOrDefault("Frame", Reference<frame::XFrame>()));
+ Reference<awt::XWindow> xParentWindow (aArguments.getOrDefault("ParentWindow", Reference<awt::XWindow>()));
+ const sal_uInt64 nBindingsValue (aArguments.getOrDefault("SfxBindings", sal_uInt64(0)));
+ SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue);
+
+ 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);
+
+ sal_Int32 nMinimumSize = -1;
+ std::unique_ptr<PanelLayout> xPanel;
+ if (rsResourceURL.endsWith("/AlignmentPropertyPanel"))
+ xPanel = AlignmentPropertyPanel::Create( pParent, xFrame, pBindings );
+ else if (rsResourceURL.endsWith("/CellAppearancePropertyPanel"))
+ xPanel = CellAppearancePropertyPanel::Create( pParent, xFrame, pBindings );
+ else if (rsResourceURL.endsWith("/NumberFormatPropertyPanel"))
+ xPanel = NumberFormatPropertyPanel::Create( pParent, xFrame, pBindings );
+ else if (rsResourceURL.endsWith("/NavigatorPanel"))
+ {
+ xPanel = std::make_unique<ScNavigatorDlg>(pBindings, pParent, nullptr);
+ nMinimumSize = 0;
+ }
+ else if (rsResourceURL.endsWith("/FunctionsPanel"))
+ {
+ xPanel = std::make_unique<ScFunctionWin>(pParent);
+ nMinimumSize = 0;
+ }
+
+ if (xPanel)
+ xElement = sfx2::sidebar::SidebarPanelBase::Create(
+ rsResourceURL,
+ xFrame,
+ std::move(xPanel),
+ ui::LayoutSize(nMinimumSize,-1,-1));
+ }
+ catch (const uno::RuntimeException &)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "ScPanelFactory::createUIElement exception",
+ nullptr, anyEx);
+ }
+
+ return xElement;
+}
+
+OUString ScPanelFactory::getImplementationName()
+{
+ return "org.apache.openoffice.comp.sc.sidebar.ScPanelFactory";
+}
+
+sal_Bool ScPanelFactory::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> ScPanelFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.UIElementFactory" };
+}
+
+} // end of namespace sc::sidebar
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ScPanelFactory_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new sc::sidebar::ScPanelFactory());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sidebar/ScPanelFactory.hxx b/sc/source/ui/sidebar/ScPanelFactory.hxx
new file mode 100644
index 0000000000..b2901e2abe
--- /dev/null
+++ b/sc/source/ui/sidebar/ScPanelFactory.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 .
+ */
+#pragma once
+
+#include <comphelper/compbase.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/XUIElementFactory.hpp>
+
+namespace sc::sidebar {
+
+typedef comphelper::WeakComponentImplHelper <
+ css::ui::XUIElementFactory, css::lang::XServiceInfo
+ > PanelFactoryInterfaceBase;
+
+class ScPanelFactory final : public PanelFactoryInterfaceBase
+{
+public:
+ // noncopyable
+ ScPanelFactory(const ScPanelFactory&) = delete;
+ const ScPanelFactory& operator=(const ScPanelFactory&) = delete;
+
+ ScPanelFactory();
+ virtual ~ScPanelFactory() override;
+
+ // 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;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+} // end of namespace sc::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sparklines/SparklineAttributes.cxx b/sc/source/ui/sparklines/SparklineAttributes.cxx
new file mode 100644
index 0000000000..8a615c1f7b
--- /dev/null
+++ b/sc/source/ui/sparklines/SparklineAttributes.cxx
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <SparklineAttributes.hxx>
+
+namespace sc
+{
+/** Holder of sparkline attributes */
+class SparklineAttributes::Implementation
+{
+public:
+ model::ComplexColor m_aColorSeries;
+ model::ComplexColor m_aColorNegative;
+ model::ComplexColor m_aColorAxis;
+ model::ComplexColor m_aColorMarkers;
+ model::ComplexColor m_aColorFirst;
+ model::ComplexColor m_aColorLast;
+ model::ComplexColor m_aColorHigh;
+ model::ComplexColor m_aColorLow;
+
+ AxisType m_eMinAxisType;
+ AxisType m_eMaxAxisType;
+
+ double m_fLineWeight; // In pt
+
+ SparklineType m_eType;
+
+ bool m_bDateAxis;
+
+ DisplayEmptyCellsAs m_eDisplayEmptyCellsAs; // span, gap, zero
+
+ bool m_bMarkers;
+ bool m_bHigh;
+ bool m_bLow;
+ bool m_bFirst;
+ bool m_bLast;
+ bool m_bNegative;
+ bool m_bDisplayXAxis;
+ bool m_bDisplayHidden;
+ bool m_bRightToLeft;
+
+ std::optional<double> m_aManualMax;
+ std::optional<double> m_aManualMin;
+ static constexpr Color COL_STANDARD_RED = 0xff0000;
+ static constexpr Color COL_STANDARD_BLUE = 0x2a6099;
+
+ Implementation()
+ : m_aColorSeries(model::ComplexColor::createRGB(COL_STANDARD_BLUE))
+ , m_aColorNegative(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_aColorAxis(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_aColorMarkers(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_aColorFirst(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_aColorLast(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_aColorHigh(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_aColorLow(model::ComplexColor::createRGB(COL_STANDARD_RED))
+ , m_eMinAxisType(AxisType::Individual)
+ , m_eMaxAxisType(AxisType::Individual)
+ , m_fLineWeight(0.75)
+ , m_eType(SparklineType::Line)
+ , m_bDateAxis(false)
+ , m_eDisplayEmptyCellsAs(DisplayEmptyCellsAs::Zero)
+ , m_bMarkers(false)
+ , m_bHigh(false)
+ , m_bLow(false)
+ , m_bFirst(false)
+ , m_bLast(false)
+ , m_bNegative(false)
+ , m_bDisplayXAxis(false)
+ , m_bDisplayHidden(false)
+ , m_bRightToLeft(false)
+ {
+ }
+
+ Implementation(Implementation const& pOther)
+ : m_aColorSeries(pOther.m_aColorSeries)
+ , m_aColorNegative(pOther.m_aColorNegative)
+ , m_aColorAxis(pOther.m_aColorAxis)
+ , m_aColorMarkers(pOther.m_aColorMarkers)
+ , m_aColorFirst(pOther.m_aColorFirst)
+ , m_aColorLast(pOther.m_aColorLast)
+ , m_aColorHigh(pOther.m_aColorHigh)
+ , m_aColorLow(pOther.m_aColorLow)
+ , m_eMinAxisType(pOther.m_eMinAxisType)
+ , m_eMaxAxisType(pOther.m_eMaxAxisType)
+ , m_fLineWeight(pOther.m_fLineWeight)
+ , m_eType(pOther.m_eType)
+ , m_bDateAxis(pOther.m_bDateAxis)
+ , m_eDisplayEmptyCellsAs(pOther.m_eDisplayEmptyCellsAs)
+ , m_bMarkers(pOther.m_bMarkers)
+ , m_bHigh(pOther.m_bHigh)
+ , m_bLow(pOther.m_bLow)
+ , m_bFirst(pOther.m_bFirst)
+ , m_bLast(pOther.m_bLast)
+ , m_bNegative(pOther.m_bNegative)
+ , m_bDisplayXAxis(pOther.m_bDisplayXAxis)
+ , m_bDisplayHidden(pOther.m_bDisplayHidden)
+ , m_bRightToLeft(pOther.m_bRightToLeft)
+ , m_aManualMax(pOther.m_aManualMax)
+ , m_aManualMin(pOther.m_aManualMin)
+ {
+ }
+
+ bool operator==(const Implementation& rImpl) const
+ {
+ return (m_aColorSeries == rImpl.m_aColorSeries)
+ && (m_aColorNegative == rImpl.m_aColorNegative)
+ && (m_aColorAxis == rImpl.m_aColorAxis) && (m_aColorMarkers == rImpl.m_aColorMarkers)
+ && (m_aColorFirst == rImpl.m_aColorFirst) && (m_aColorLast == rImpl.m_aColorLast)
+ && (m_aColorHigh == rImpl.m_aColorHigh) && (m_aColorLow == rImpl.m_aColorLow)
+ && (m_eMinAxisType == rImpl.m_eMinAxisType)
+ && (m_eMaxAxisType == rImpl.m_eMaxAxisType) && (m_fLineWeight == rImpl.m_fLineWeight)
+ && (m_eType == rImpl.m_eType) && (m_bDateAxis == rImpl.m_bDateAxis)
+ && (m_eDisplayEmptyCellsAs == rImpl.m_eDisplayEmptyCellsAs)
+ && (m_bMarkers == rImpl.m_bMarkers) && (m_bHigh == rImpl.m_bHigh)
+ && (m_bLow == rImpl.m_bLow) && (m_bFirst == rImpl.m_bFirst)
+ && (m_bLast == rImpl.m_bLast) && (m_bNegative == rImpl.m_bNegative)
+ && (m_bDisplayXAxis == rImpl.m_bDisplayXAxis)
+ && (m_bDisplayHidden == rImpl.m_bDisplayHidden)
+ && (m_bRightToLeft == rImpl.m_bRightToLeft) && (m_aManualMax == rImpl.m_aManualMax)
+ && (m_aManualMin == rImpl.m_aManualMin);
+ }
+};
+
+SparklineAttributes::SparklineAttributes() = default;
+
+SparklineAttributes::~SparklineAttributes() = default;
+
+SparklineAttributes::SparklineAttributes(SparklineAttributes const&) = default;
+
+SparklineAttributes::SparklineAttributes(SparklineAttributes&&) = default;
+
+SparklineAttributes& SparklineAttributes::operator=(SparklineAttributes const&) = default;
+
+SparklineAttributes& SparklineAttributes::operator=(SparklineAttributes&&) = default;
+
+bool SparklineAttributes::operator==(SparklineAttributes const& rOther) const
+{
+ return m_aImplementation == rOther.m_aImplementation;
+}
+
+void SparklineAttributes::resetColors()
+{
+ m_aImplementation->m_aColorSeries = model::ComplexColor();
+ m_aImplementation->m_aColorNegative = model::ComplexColor();
+ m_aImplementation->m_aColorAxis = model::ComplexColor();
+ m_aImplementation->m_aColorMarkers = model::ComplexColor();
+ m_aImplementation->m_aColorFirst = model::ComplexColor();
+ m_aImplementation->m_aColorLast = model::ComplexColor();
+ m_aImplementation->m_aColorHigh = model::ComplexColor();
+ m_aImplementation->m_aColorLow = model::ComplexColor();
+}
+
+model::ComplexColor SparklineAttributes::getColorSeries() const
+{
+ return m_aImplementation->m_aColorSeries;
+}
+
+void SparklineAttributes::setColorSeries(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorSeries = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorNegative() const
+{
+ return m_aImplementation->m_aColorNegative;
+}
+
+void SparklineAttributes::setColorNegative(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorNegative = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorAxis() const
+{
+ return m_aImplementation->m_aColorAxis;
+}
+
+void SparklineAttributes::setColorAxis(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorAxis = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorMarkers() const
+{
+ return m_aImplementation->m_aColorMarkers;
+}
+void SparklineAttributes::setColorMarkers(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorMarkers = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorFirst() const
+{
+ return m_aImplementation->m_aColorFirst;
+}
+void SparklineAttributes::setColorFirst(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorFirst = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorLast() const
+{
+ return m_aImplementation->m_aColorLast;
+}
+void SparklineAttributes::setColorLast(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorLast = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorHigh() const
+{
+ return m_aImplementation->m_aColorHigh;
+}
+void SparklineAttributes::setColorHigh(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorHigh = rColor;
+}
+
+model::ComplexColor SparklineAttributes::getColorLow() const
+{
+ return m_aImplementation->m_aColorLow;
+}
+void SparklineAttributes::setColorLow(model::ComplexColor const& rColor)
+{
+ m_aImplementation->m_aColorLow = rColor;
+}
+
+AxisType SparklineAttributes::getMinAxisType() const { return m_aImplementation->m_eMinAxisType; }
+void SparklineAttributes::setMinAxisType(AxisType eAxisType)
+{
+ m_aImplementation->m_eMinAxisType = eAxisType;
+}
+
+AxisType SparklineAttributes::getMaxAxisType() const { return m_aImplementation->m_eMaxAxisType; }
+void SparklineAttributes::setMaxAxisType(AxisType eAxisType)
+{
+ m_aImplementation->m_eMaxAxisType = eAxisType;
+}
+
+double SparklineAttributes::getLineWeight() const { return m_aImplementation->m_fLineWeight; }
+void SparklineAttributes::setLineWeight(double nWeight)
+{
+ m_aImplementation->m_fLineWeight = nWeight;
+}
+
+SparklineType SparklineAttributes::getType() const { return m_aImplementation->m_eType; }
+void SparklineAttributes::setType(SparklineType eType) { m_aImplementation->m_eType = eType; }
+
+bool SparklineAttributes::isDateAxis() const { return m_aImplementation->m_bDateAxis; }
+void SparklineAttributes::setDateAxis(bool bValue) { m_aImplementation->m_bDateAxis = bValue; }
+
+DisplayEmptyCellsAs SparklineAttributes::getDisplayEmptyCellsAs() const
+{
+ return m_aImplementation->m_eDisplayEmptyCellsAs;
+}
+void SparklineAttributes::setDisplayEmptyCellsAs(DisplayEmptyCellsAs eValue)
+{
+ m_aImplementation->m_eDisplayEmptyCellsAs = eValue;
+}
+
+bool SparklineAttributes::isMarkers() const { return m_aImplementation->m_bMarkers; }
+void SparklineAttributes::setMarkers(bool bValue) { m_aImplementation->m_bMarkers = bValue; }
+
+bool SparklineAttributes::isHigh() const { return m_aImplementation->m_bHigh; }
+void SparklineAttributes::setHigh(bool bValue) { m_aImplementation->m_bHigh = bValue; }
+
+bool SparklineAttributes::isLow() const { return m_aImplementation->m_bLow; }
+void SparklineAttributes::setLow(bool bValue) { m_aImplementation->m_bLow = bValue; }
+
+bool SparklineAttributes::isFirst() const { return m_aImplementation->m_bFirst; }
+void SparklineAttributes::setFirst(bool bValue) { m_aImplementation->m_bFirst = bValue; }
+
+bool SparklineAttributes::isLast() const { return m_aImplementation->m_bLast; }
+void SparklineAttributes::setLast(bool bValue) { m_aImplementation->m_bLast = bValue; }
+
+bool SparklineAttributes::isNegative() const { return m_aImplementation->m_bNegative; }
+void SparklineAttributes::setNegative(bool bValue) { m_aImplementation->m_bNegative = bValue; }
+
+bool SparklineAttributes::shouldDisplayXAxis() const { return m_aImplementation->m_bDisplayXAxis; }
+void SparklineAttributes::setDisplayXAxis(bool bValue)
+{
+ m_aImplementation->m_bDisplayXAxis = bValue;
+}
+
+bool SparklineAttributes::shouldDisplayHidden() const
+{
+ return m_aImplementation->m_bDisplayHidden;
+}
+void SparklineAttributes::setDisplayHidden(bool bValue)
+{
+ m_aImplementation->m_bDisplayHidden = bValue;
+}
+
+bool SparklineAttributes::isRightToLeft() const { return m_aImplementation->m_bRightToLeft; }
+void SparklineAttributes::setRightToLeft(bool bValue)
+{
+ m_aImplementation->m_bRightToLeft = bValue;
+}
+
+std::optional<double> SparklineAttributes::getManualMax() const
+{
+ return m_aImplementation->m_aManualMax;
+}
+void SparklineAttributes::setManualMax(std::optional<double> aValue)
+{
+ m_aImplementation->m_aManualMax = aValue;
+}
+
+std::optional<double> SparklineAttributes::getManualMin() const
+{
+ return m_aImplementation->m_aManualMin;
+}
+void SparklineAttributes::setManualMin(std::optional<double> aValue)
+{
+ m_aImplementation->m_aManualMin = aValue;
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sparklines/SparklineData.cxx b/sc/source/ui/sparklines/SparklineData.cxx
new file mode 100644
index 0000000000..a126acc10b
--- /dev/null
+++ b/sc/source/ui/sparklines/SparklineData.cxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <SparklineData.hxx>
+
+namespace sc
+{
+RangeOrientation calculateOrientation(sal_Int32 nOutputSize, ScRange const& rInputRange)
+{
+ sal_Int32 nRowSize = rInputRange.aEnd.Row() - rInputRange.aStart.Row();
+ sal_Int32 nColSize = rInputRange.aEnd.Col() - rInputRange.aStart.Col();
+
+ auto eInputOrientation = RangeOrientation::Unknown;
+ if (nOutputSize == nRowSize)
+ eInputOrientation = RangeOrientation::Row;
+ else if (nOutputSize == nColSize)
+ eInputOrientation = RangeOrientation::Col;
+ return eInputOrientation;
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sparklines/SparklineGroup.cxx b/sc/source/ui/sparklines/SparklineGroup.cxx
new file mode 100644
index 0000000000..a7fe2fe468
--- /dev/null
+++ b/sc/source/ui/sparklines/SparklineGroup.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/.
+ *
+ */
+
+#include <SparklineGroup.hxx>
+#include <utility>
+
+namespace sc
+{
+SparklineGroup::SparklineGroup(SparklineAttributes aSparklineAttributes)
+ : m_aAttributes(std::move(aSparklineAttributes))
+ , m_aGUID(tools::Guid::Generate)
+{
+}
+
+SparklineGroup::SparklineGroup()
+ : m_aGUID(tools::Guid::Generate)
+{
+}
+
+SparklineGroup::SparklineGroup(SparklineGroup const& pOtherSparkline)
+ : m_aAttributes(pOtherSparkline.m_aAttributes)
+ , m_aGUID(pOtherSparkline.m_aGUID)
+{
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/sparklines/SparklineList.cxx b/sc/source/ui/sparklines/SparklineList.cxx
new file mode 100644
index 0000000000..1cae308961
--- /dev/null
+++ b/sc/source/ui/sparklines/SparklineList.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/.
+ *
+ */
+
+#include <SparklineList.hxx>
+
+namespace sc
+{
+SparklineList::SparklineList() = default;
+
+void SparklineList::addSparkline(std::shared_ptr<Sparkline> const& pSparkline)
+{
+ auto pWeakGroup = std::weak_ptr<SparklineGroup>(pSparkline->getSparklineGroup());
+
+ auto[iterator, bInserted]
+ = m_aSparklineGroupMap.try_emplace(pWeakGroup, std::vector<std::weak_ptr<Sparkline>>());
+ iterator->second.push_back(std::weak_ptr<Sparkline>(pSparkline));
+ if (bInserted)
+ m_aSparklineGroups.push_back(pWeakGroup);
+}
+
+void SparklineList::removeSparkline(std::shared_ptr<Sparkline> const& pSparkline)
+{
+ auto pWeakGroup = std::weak_ptr<SparklineGroup>(pSparkline->getSparklineGroup());
+ auto iteratorGroup = m_aSparklineGroupMap.find(pWeakGroup);
+ if (iteratorGroup != m_aSparklineGroupMap.end())
+ {
+ auto& rWeakSparklines = iteratorGroup->second;
+
+ for (auto iterator = rWeakSparklines.begin(); iterator != rWeakSparklines.end();)
+ {
+ auto pCurrentSparkline = iterator->lock();
+
+ if (pCurrentSparkline && pCurrentSparkline != pSparkline)
+ {
+ iterator++;
+ }
+ else
+ {
+ iterator = rWeakSparklines.erase(iterator);
+ }
+ }
+ }
+}
+
+std::vector<std::shared_ptr<SparklineGroup>> SparklineList::getSparklineGroups()
+{
+ std::vector<std::shared_ptr<SparklineGroup>> toReturn;
+
+ for (auto iterator = m_aSparklineGroups.begin(); iterator != m_aSparklineGroups.end();)
+ {
+ auto pWeakGroup = *iterator;
+ if (auto pSparklineGroup = pWeakGroup.lock())
+ {
+ toReturn.push_back(pSparklineGroup);
+ iterator++;
+ }
+ else
+ {
+ iterator = m_aSparklineGroups.erase(iterator);
+ }
+ }
+ return toReturn;
+}
+
+std::vector<std::shared_ptr<Sparkline>>
+SparklineList::getSparklinesFor(std::shared_ptr<SparklineGroup> const& pSparklineGroup)
+{
+ std::vector<std::shared_ptr<Sparkline>> toReturn;
+
+ std::weak_ptr<SparklineGroup> pWeakGroup(pSparklineGroup);
+ auto iteratorGroup = m_aSparklineGroupMap.find(pWeakGroup);
+
+ if (iteratorGroup == m_aSparklineGroupMap.end())
+ return toReturn;
+
+ auto& rWeakSparklines = iteratorGroup->second;
+
+ for (auto iterator = rWeakSparklines.begin(); iterator != rWeakSparklines.end();)
+ {
+ if (auto aSparkline = iterator->lock())
+ {
+ toReturn.push_back(aSparkline);
+ iterator++;
+ }
+ else
+ {
+ iterator = rWeakSparklines.erase(iterator);
+ }
+ }
+
+ return toReturn;
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/styleui/styledlg.cxx b/sc/source/ui/styleui/styledlg.cxx
new file mode 100644
index 0000000000..c54b496ea6
--- /dev/null
+++ b/sc/source/ui/styleui/styledlg.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svx/drawitem.hxx>
+#include <svx/ofaitem.hxx>
+#include <svx/svdview.hxx>
+#include <svx/numinf.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <svl/style.hxx>
+#include <svl/cjkoptions.hxx>
+#include <osl/diagnose.h>
+
+#include <styledlg.hxx>
+#include <tabpages.hxx>
+#include <tphf.hxx>
+#include <tptable.hxx>
+#include <svx/svxids.hrc>
+#include <svx/dialogs.hrc>
+#include <svl/intitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <svx/flagsdef.hxx>
+
+ScStyleDlg::ScStyleDlg(weld::Window* pParent,
+ SfxStyleSheetBase& rStyleBase,
+ bool bPage)
+ : SfxStyleDialogController(pParent,
+ bPage ?
+ OUString("modules/scalc/ui/pagetemplatedialog.ui") :
+ OUString("modules/scalc/ui/paratemplatedialog.ui"),
+ bPage ?
+ OUString("PageTemplateDialog") :
+ OUString("ParaTemplateDialog"),
+ rStyleBase )
+ , m_bPage(bPage)
+{
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ if (m_bPage) // page styles
+ {
+ AddTabPage("page", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PAGE ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_PAGE ) );
+ AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ) );
+ AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BKG ) );
+ AddTabPage("header", &ScHeaderPage::Create, &ScHeaderPage::GetRanges );
+ AddTabPage("footer", &ScFooterPage::Create, &ScFooterPage::GetRanges );
+ AddTabPage("sheet", &ScTablePage::Create, &ScTablePage::GetRanges );
+ }
+ else // cell format styles
+ {
+ AddTabPage("numbers", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUMBERFORMAT ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_NUMBERFORMAT ));
+ AddTabPage("font", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_NAME ));
+ AddTabPage("fonteffects", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_CHAR_EFFECTS ));
+ AddTabPage("alignment", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_ALIGNMENT ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_ALIGNMENT ));
+ if ( SvtCJKOptions::IsAsianTypographyEnabled() )
+ {
+ AddTabPage("asiantypo", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PARA_ASIAN), pFact->GetTabPageRangesFunc(RID_SVXPAGE_PARA_ASIAN));
+ }
+ else
+ RemoveTabPage("asiantypo");
+ AddTabPage("borders", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BORDER ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BORDER ));
+ AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), pFact->GetTabPageRangesFunc( RID_SVXPAGE_BKG ));
+ AddTabPage("protection", &ScTabPageProtection::Create, &ScTabPageProtection::GetRanges);
+ }
+}
+
+void ScStyleDlg::PageCreated(const OUString& rPageId, SfxTabPage& rTabPage)
+{
+ if (m_bPage)
+ {
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ if (rPageId == "page")
+ {
+ aSet.Put (SfxUInt16Item(SID_ENUM_PAGE_MODE, SVX_PAGE_MODE_CENTER));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "header" || rPageId == "footer")
+ {
+ static_cast<ScHFPage&>(rTabPage).SetStyleDlg( this );
+ static_cast<ScHFPage&>(rTabPage).SetPageStyle( GetStyleSheet().GetName() );
+ static_cast<ScHFPage&>(rTabPage).DisableDeleteQueryBox();
+ }
+ else if (rPageId == "background")
+ {
+ aSet.Put (SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_SELECTOR)));
+ rTabPage.PageCreated(aSet);
+ }
+ }
+ else if (SfxObjectShell* pDocSh = SfxObjectShell::Current())
+ {
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ if (rPageId == "numbers")
+ {
+ const SfxPoolItem* pInfoItem
+ = pDocSh->GetItem( SID_ATTR_NUMBERFORMAT_INFO );
+
+ OSL_ENSURE( pInfoItem, "NumberInfoItem not found!" );
+
+ aSet.Put ( static_cast<const SvxNumberInfoItem&>(*pInfoItem) );
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "font")
+ {
+ const SfxPoolItem* pInfoItem
+ = pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST );
+
+ OSL_ENSURE( pInfoItem, "FontListItem not found!" );
+
+ aSet.Put (SvxFontListItem(static_cast<const SvxFontListItem&>(*pInfoItem).GetFontList(), SID_ATTR_CHAR_FONTLIST));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "background")
+ {
+ rTabPage.PageCreated(aSet);
+ }
+ }
+}
+
+void ScStyleDlg::RefreshInputSet()
+{
+ SfxItemSet* pItemSet = GetInputSetImpl();
+ pItemSet->ClearItem();
+ pItemSet->SetParent( GetStyleSheet().GetItemSet().GetParent() );
+}
+
+ScDrawStyleDlg::ScDrawStyleDlg(weld::Window* pParent, SfxStyleSheetBase& rStyleBase, SdrView* pView)
+ : SfxStyleDialogController(pParent, "modules/scalc/ui/drawtemplatedialog.ui", "DrawTemplateDialog", rStyleBase)
+ , mpView(pView)
+{
+ AddTabPage("line", RID_SVXPAGE_LINE);
+ AddTabPage("area", RID_SVXPAGE_AREA);
+ AddTabPage("shadowing", RID_SVXPAGE_SHADOW);
+ AddTabPage("transparency", RID_SVXPAGE_TRANSPARENCE);
+ AddTabPage("font", RID_SVXPAGE_CHAR_NAME);
+ AddTabPage("fonteffect", RID_SVXPAGE_CHAR_EFFECTS);
+ AddTabPage("background", RID_SVXPAGE_BKG);
+ AddTabPage("indents", RID_SVXPAGE_STD_PARAGRAPH);
+ AddTabPage("text", RID_SVXPAGE_TEXTATTR);
+ AddTabPage("animation", RID_SVXPAGE_TEXTANIMATION);
+ AddTabPage("dimensioning", RID_SVXPAGE_MEASURE);
+ AddTabPage("alignment", RID_SVXPAGE_ALIGN_PARAGRAPH);
+ AddTabPage("tabs", RID_SVXPAGE_TABULATOR);
+ if (SvtCJKOptions::IsAsianTypographyEnabled())
+ AddTabPage("asiantypo", RID_SVXPAGE_PARA_ASIAN);
+ else
+ RemoveTabPage("asiantypo");
+}
+
+void ScDrawStyleDlg::PageCreated(const OUString& rPageId, SfxTabPage& rTabPage)
+{
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+ SdrModel& rModel = mpView->GetModel();
+
+ if (rPageId == "line")
+ {
+ aSet.Put(SvxColorListItem(rModel.GetColorList(), SID_COLOR_TABLE));
+ aSet.Put(SvxDashListItem(rModel.GetDashList(), SID_DASH_LIST));
+ aSet.Put(SvxLineEndListItem(rModel.GetLineEndList(), SID_LINEEND_LIST));
+ aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "area")
+ {
+ aSet.Put(SvxColorListItem(rModel.GetColorList(), SID_COLOR_TABLE));
+ aSet.Put(SvxGradientListItem(rModel.GetGradientList(), SID_GRADIENT_LIST));
+ aSet.Put(SvxHatchListItem(rModel.GetHatchList(), SID_HATCH_LIST));
+ aSet.Put(SvxBitmapListItem(rModel.GetBitmapList(), SID_BITMAP_LIST));
+ aSet.Put(SvxPatternListItem(rModel.GetPatternList(), SID_PATTERN_LIST));
+ aSet.Put(SfxUInt16Item(SID_PAGE_TYPE, 0));
+ aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1));
+ aSet.Put(SfxUInt16Item(SID_TABPAGE_POS, 0));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "shadowing")
+ {
+ aSet.Put(SvxColorListItem(rModel.GetColorList(), SID_COLOR_TABLE));
+ aSet.Put(SfxUInt16Item(SID_PAGE_TYPE, 0));
+ aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "transparency")
+ {
+ aSet.Put (SfxUInt16Item(SID_PAGE_TYPE, 0));
+ aSet.Put (SfxUInt16Item(SID_DLG_TYPE, 1));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "font")
+ {
+ if (SfxObjectShell* pDocSh = SfxObjectShell::Current())
+ {
+ SvxFontListItem aItem(*static_cast<const SvxFontListItem*>(
+ pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST)));
+
+ aSet.Put(SvxFontListItem(aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST));
+ }
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "fonteffect")
+ {
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "background")
+ {
+ aSet.Put(SfxUInt32Item(SID_FLAG_TYPE, static_cast<sal_uInt32>(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR)));
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "text")
+ {
+ rTabPage.PageCreated(aSet);
+ }
+ else if (rPageId == "dimensioning")
+ {
+ aSet.Put(OfaPtrItem(SID_OBJECT_LIST, mpView));
+ rTabPage.PageCreated(aSet);
+ }
+}
+
+void ScDrawStyleDlg::RefreshInputSet()
+{
+ SfxItemSet* pItemSet = GetInputSetImpl();
+ pItemSet->ClearItem();
+ pItemSet->SetParent( GetStyleSheet().GetItemSet().GetParent() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/styleui/template.cur b/sc/source/ui/styleui/template.cur
new file mode 100644
index 0000000000..0fb6a1f5d0
--- /dev/null
+++ b/sc/source/ui/styleui/template.cur
Binary files differ
diff --git a/sc/source/ui/theme/ThemeColorChanger.cxx b/sc/source/ui/theme/ThemeColorChanger.cxx
new file mode 100644
index 0000000000..c9b88652dc
--- /dev/null
+++ b/sc/source/ui/theme/ThemeColorChanger.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <ThemeColorChanger.hxx>
+
+#include <sal/config.h>
+
+#include <docmodel/uno/UnoComplexColor.hxx>
+#include <docmodel/theme/Theme.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/borderline.hxx>
+#include <svx/svditer.hxx>
+#include <svx/theme/ThemeColorChangerCommon.hxx>
+
+#include <undodraw.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <scitems.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <document.hxx>
+#include <address.hxx>
+#include <dociter.hxx>
+#include <docfunc.hxx>
+#include <tabvwsh.hxx>
+#include <undostyl.hxx>
+#include <undoblk.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+
+#include <undo/UndoThemeChange.hxx>
+
+namespace sc
+{
+ThemeColorChanger::ThemeColorChanger(ScDocShell& rDocShell)
+ : m_rDocShell(rDocShell)
+{
+}
+
+ThemeColorChanger::~ThemeColorChanger() = default;
+
+namespace
+{
+bool changeBorderLine(editeng::SvxBorderLine* pBorderLine, model::ColorSet const& rColorSet)
+{
+ if (!pBorderLine)
+ return false;
+
+ model::ComplexColor const& rComplexColor = pBorderLine->getComplexColor();
+ if (rComplexColor.isValidThemeType())
+ {
+ Color aColor = rColorSet.resolveColor(rComplexColor);
+ pBorderLine->SetColor(aColor);
+ return true;
+ }
+ return false;
+}
+
+bool changeCellItems(SfxItemSet& rItemSet, model::ColorSet const& rColorSet)
+{
+ const SfxPoolItem* pItem = nullptr;
+ bool bChanged = false;
+ if (rItemSet.HasItem(ATTR_FONT_COLOR, &pItem))
+ {
+ auto const* pColorItem = static_cast<const SvxColorItem*>(pItem);
+ model::ComplexColor const& rComplexColor = pColorItem->getComplexColor();
+ if (rComplexColor.isValidThemeType())
+ {
+ Color aColor = rColorSet.resolveColor(rComplexColor);
+
+ SvxColorItem aColorItem(*pColorItem);
+ aColorItem.setColor(aColor);
+ rItemSet.Put(aColorItem);
+ bChanged = true;
+ }
+ }
+ if (rItemSet.HasItem(ATTR_BACKGROUND, &pItem))
+ {
+ auto const* pBrushItem = static_cast<const SvxBrushItem*>(pItem);
+ model::ComplexColor const& rComplexColor = pBrushItem->getComplexColor();
+ if (rComplexColor.isValidThemeType())
+ {
+ Color aColor = rColorSet.resolveColor(rComplexColor);
+
+ SvxBrushItem aNewBrushItem(*pBrushItem);
+ aNewBrushItem.SetColor(aColor);
+ rItemSet.Put(aNewBrushItem);
+ bChanged = true;
+ }
+ }
+ if (rItemSet.HasItem(ATTR_BORDER, &pItem))
+ {
+ auto const* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
+ SvxBoxItem rNewItem(*pBoxItem);
+ bool bLineChanged = false;
+
+ bLineChanged = changeBorderLine(rNewItem.GetBottom(), rColorSet) || bChanged;
+ bLineChanged = changeBorderLine(rNewItem.GetTop(), rColorSet) || bChanged;
+ bLineChanged = changeBorderLine(rNewItem.GetLeft(), rColorSet) || bChanged;
+ bLineChanged = changeBorderLine(rNewItem.GetRight(), rColorSet) || bChanged;
+
+ if (bLineChanged)
+ {
+ rItemSet.Put(rNewItem);
+ bChanged = true;
+ }
+ }
+ return bChanged;
+}
+
+bool changeStyles(ScDocShell& rDocShell, model::ColorSet const& rColorSet)
+{
+ ScDocument& rDocument = rDocShell.GetDocument();
+ ScStyleSheetPool* pPool = rDocument.GetStyleSheetPool();
+ ScStyleSheet* pStyle = nullptr;
+ bool bChanged = false;
+
+ // Paragraph style color change
+ pStyle = static_cast<ScStyleSheet*>(pPool->First(SfxStyleFamily::Para));
+ while (pStyle)
+ {
+ ScStyleSaveData aOldData;
+ aOldData.InitFromStyle(pStyle);
+
+ auto rItemSet = pStyle->GetItemSet();
+ if (changeCellItems(rItemSet, rColorSet))
+ {
+ if (rDocument.IsUndoEnabled())
+ {
+ ScStyleSaveData aNewData;
+ aNewData.InitFromStyle(pStyle);
+ rDocShell.GetUndoManager()->AddUndoAction(std::make_unique<ScUndoModifyStyle>(
+ &rDocShell, SfxStyleFamily::Para, aOldData, aNewData));
+ }
+ static_cast<SfxStyleSheet*>(pStyle)->Broadcast(SfxHint(SfxHintId::DataChanged));
+ bChanged = true;
+ }
+
+ pStyle = static_cast<ScStyleSheet*>(pPool->Next());
+ }
+
+ return bChanged;
+}
+
+bool changeSheets(ScDocShell& rDocShell, ScTabViewShell* pViewShell, ScDrawLayer* pModel,
+ model::ColorSet const& rColorSet)
+{
+ ScDocument& rDocument = rDocShell.GetDocument();
+ bool bChanged = false;
+
+ for (SCTAB nTab = 0; nTab < rDocument.GetTableCount(); nTab++)
+ {
+ // Change Cell / Text attributes
+ {
+ ScDocAttrIterator aAttributeIterator(rDocument, nTab, 0, 0, rDocument.MaxCol(),
+ rDocument.MaxRow());
+ SCCOL nCol = 0;
+ SCROW nRow1 = 0;
+ SCROW nRow2 = 0;
+
+ while (const ScPatternAttr* pPattern = aAttributeIterator.GetNext(nCol, nRow1, nRow2))
+ {
+ if (!pPattern || !pPattern->IsVisible())
+ continue;
+
+ ScPatternAttr aNewPattern(*pPattern);
+ auto& rItemSet = aNewPattern.GetItemSet();
+ bool bItemChanged = changeCellItems(rItemSet, rColorSet);
+ bChanged = bChanged || bItemChanged;
+
+ if (bItemChanged && rDocument.IsUndoEnabled())
+ {
+ ScRange aRange(nCol, nRow1, nTab, nCol, nRow2, nTab);
+ ScMarkData aMark(rDocument.GetSheetLimits());
+ aMark.SetMarkArea(aRange);
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo(rDocument, nTab, nTab);
+ pUndoDoc->AddUndoTab(nTab, nTab);
+
+ aMark.MarkToMulti();
+
+ rDocument.CopyToDocument(aRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc,
+ &aMark);
+
+ auto pUndo = std::make_unique<ScUndoSelectionAttr>(
+ &rDocShell, aMark, aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), aRange.aEnd.Col(), aRange.aEnd.Row(),
+ aRange.aEnd.Tab(), std::move(pUndoDoc), true, &aNewPattern);
+
+ ScEditDataArray* pDataArray = pUndo->GetDataArray();
+
+ rDocument.ApplySelectionPattern(aNewPattern, aMark, pDataArray);
+
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
+ }
+ }
+ }
+
+ // Change all SdrObjects
+ {
+ pModel->BeginCalcUndo(true);
+ SdrView* pView = nullptr;
+ if (pViewShell)
+ pView = pViewShell->GetScDrawView();
+
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups);
+ for (SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next())
+ {
+ svx::theme::updateSdrObject(rColorSet, pObject, pView, rDocShell.GetUndoManager());
+ }
+
+ std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
+
+ if (pUndo)
+ {
+ bChanged = true;
+ auto pUndoDraw = std::make_unique<ScUndoDraw>(std::move(pUndo), &rDocShell);
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDraw));
+ }
+ }
+ }
+
+ return bChanged;
+}
+
+model::ComplexColor modifyComplexColor(model::ComplexColor const& rComplexColor,
+ model::ColorSet const& rColorSet)
+{
+ model::ComplexColor aComplexColor(rComplexColor);
+
+ if (aComplexColor.isValidThemeType())
+ {
+ Color aColor = rColorSet.resolveColor(aComplexColor);
+ aComplexColor.setFinalColor(aColor);
+ }
+ return aComplexColor;
+}
+
+void changeSparklines(ScDocShell& rDocShell, model::ColorSet const& rColorSet)
+{
+ ScDocument& rDocument = rDocShell.GetDocument();
+ auto& rDocFunc = rDocShell.GetDocFunc();
+ for (SCTAB nTab = 0; nTab < rDocument.GetTableCount(); ++nTab)
+ {
+ auto* pSparklineList = rDocument.GetSparklineList(nTab);
+ if (pSparklineList && !pSparklineList->getSparklineGroups().empty())
+ {
+ auto const& rSparklineGroups = pSparklineList->getSparklineGroups();
+ for (auto const& rSparklineGroup : rSparklineGroups)
+ {
+ auto aAttributes = rSparklineGroup->getAttributes();
+
+ aAttributes.setColorAxis(modifyComplexColor(aAttributes.getColorAxis(), rColorSet));
+ aAttributes.setColorSeries(
+ modifyComplexColor(aAttributes.getColorSeries(), rColorSet));
+ aAttributes.setColorNegative(
+ modifyComplexColor(aAttributes.getColorNegative(), rColorSet));
+ aAttributes.setColorMarkers(
+ modifyComplexColor(aAttributes.getColorMarkers(), rColorSet));
+ aAttributes.setColorHigh(modifyComplexColor(aAttributes.getColorHigh(), rColorSet));
+ aAttributes.setColorLow(modifyComplexColor(aAttributes.getColorLow(), rColorSet));
+ aAttributes.setColorFirst(
+ modifyComplexColor(aAttributes.getColorFirst(), rColorSet));
+ aAttributes.setColorLast(modifyComplexColor(aAttributes.getColorLast(), rColorSet));
+ rDocFunc.ChangeSparklineGroupAttributes(rSparklineGroup, aAttributes);
+ }
+ }
+ }
+}
+
+std::shared_ptr<model::Theme> getTheme(ScDocShell& rDocShell)
+{
+ ScDrawLayer* pModel = rDocShell.GetDocument().GetDrawLayer();
+
+ auto pTheme = pModel->getTheme();
+ if (!pTheme)
+ {
+ pTheme = std::make_shared<model::Theme>("Office");
+ pModel->setTheme(pTheme);
+ }
+ return pTheme;
+}
+
+void changeThemeColorInTheDocModel(ScDocShell& rDocShell,
+ std::shared_ptr<model::ColorSet> const& pColorSet)
+{
+ auto pTheme = getTheme(rDocShell);
+ std::shared_ptr<model::ColorSet> pNewColorSet = pColorSet;
+ std::shared_ptr<model::ColorSet> pOldColorSet = pTheme->getColorSet();
+ pTheme->setColorSet(pNewColorSet);
+
+ ScDocument& rDocument = rDocShell.GetDocument();
+ if (rDocument.IsUndoEnabled())
+ {
+ auto pUndoThemeChange
+ = std::make_unique<sc::UndoThemeChange>(rDocShell, pOldColorSet, pNewColorSet);
+ rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoThemeChange));
+ }
+}
+
+} // end anonymous ns
+
+void ThemeColorChanger::apply(std::shared_ptr<model::ColorSet> const& pColorSet)
+{
+ // Can't change to an empty color set
+ if (!pColorSet)
+ return;
+
+ m_rDocShell.MakeDrawLayer();
+
+ ScDocShellModificator aModificator(m_rDocShell);
+ ScDocument& rDocument = m_rDocShell.GetDocument();
+ auto pUndoManager = m_rDocShell.GetUndoManager();
+
+ const bool bUndo(rDocument.IsUndoEnabled());
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ViewShellId nViewShellId(-1);
+ if (pViewShell)
+ nViewShellId = pViewShell->GetViewShellId();
+
+ if (bUndo)
+ {
+ OUString aUndo = ScResId(STR_UNDO_THEME_COLOR_CHANGE);
+ pUndoManager->EnterListAction(aUndo, aUndo, 0, nViewShellId);
+ }
+
+ bool bChanged = false;
+ bChanged = changeStyles(m_rDocShell, *pColorSet) || bChanged;
+ bChanged
+ = changeSheets(m_rDocShell, pViewShell, rDocument.GetDrawLayer(), *pColorSet) || bChanged;
+ changeSparklines(m_rDocShell, *pColorSet);
+
+ changeThemeColorInTheDocModel(m_rDocShell, pColorSet);
+
+ if (bUndo)
+ {
+ pUndoManager->LeaveListAction();
+ }
+
+ m_rDocShell.SetDrawModified();
+ aModificator.SetDocumentModified();
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/uitest/uiobject.cxx b/sc/source/ui/uitest/uiobject.cxx
new file mode 100644
index 0000000000..e38035b2fc
--- /dev/null
+++ b/sc/source/ui/uitest/uiobject.cxx
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <rangeutl.hxx>
+#include <gridwin.hxx>
+
+#include <viewdata.hxx>
+#include <viewfunc.hxx>
+#include <dbfunc.hxx>
+#include <tabvwsh.hxx>
+#include <drwlayer.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <appoptio.hxx>
+#include <scmod.hxx>
+#include <fudraw.hxx>
+#include <postit.hxx>
+
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <sal/log.hxx>
+
+namespace {
+
+ScAddress get_address_from_string(const ScDocument& rDoc, std::u16string_view aStr)
+{
+ ScAddress aAddr;
+ sal_Int32 nOffset = 0;
+ ScRangeStringConverter::GetAddressFromString(aAddr, aStr, rDoc, formula::FormulaGrammar::CONV_OOO, nOffset);
+ return aAddr;
+}
+
+ScRange get_range_from_string(const ScDocument& rDoc, std::u16string_view aStr)
+{
+ ScRange aRange;
+ sal_Int32 nOffset = 0;
+ ScRangeStringConverter::GetRangeFromString(aRange, aStr, rDoc, formula::FormulaGrammar::CONV_OOO, nOffset);
+
+ return aRange;
+}
+
+}
+
+ScGridWinUIObject::ScGridWinUIObject(const VclPtr<ScGridWindow>& xGridWin):
+ WindowUIObject(xGridWin),
+ mxGridWindow(xGridWin)
+{
+}
+
+StringMap ScGridWinUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+
+ aMap["SelectedTable"] = OUString::number(mxGridWindow->getViewData().GetTabNo());
+ aMap["CurrentColumn"] = OUString::number(mxGridWindow->getViewData().GetCurX());
+ aMap["CurrentRow"] = OUString::number(mxGridWindow->getViewData().GetCurY());
+
+ ScSplitPos eAlign = mxGridWindow->getViewData().GetActivePart();
+ ScHSplitPos eAlignX = WhichH(eAlign);
+ ScVSplitPos eAlignY = WhichV(eAlign);
+ aMap["TopVisibleRow"] = OUString::number(mxGridWindow->getViewData().GetPosY(eAlignY));
+ aMap["TopVisibleColumn"] = OUString::number(mxGridWindow->getViewData().GetPosX(eAlignX));
+
+ ScRangeList aMarkedArea = mxGridWindow->getViewData().GetMarkData().GetMarkedRanges();
+ OUString aMarkedAreaString;
+ ScRangeStringConverter::GetStringFromRangeList(aMarkedAreaString, &aMarkedArea, &mxGridWindow->getViewData().GetDocument(), formula::FormulaGrammar::CONV_OOO);
+
+ aMap["MarkedArea"] = aMarkedAreaString;
+
+ ScDocument& rDoc = mxGridWindow->getViewData().GetDocument();
+ ScAddress aPos( mxGridWindow->getViewData().GetCurX() , mxGridWindow->getViewData().GetCurY() , mxGridWindow->getViewData().GetTabNo() );
+ if ( rDoc.HasNote( aPos ) )
+ {
+ ScPostIt* pNote = rDoc.GetNote(aPos);
+ assert(pNote);
+ aMap["CurrentCellCommentText"] = pNote->GetText();
+ }
+
+ SCCOL nCol;
+ SCROW nRow;
+ aMap["CurrentTableHasData"] = OUString::boolean( rDoc.GetDataStart( mxGridWindow->getViewData().GetTabNo(), nCol, nRow ) );
+ nCol = mxGridWindow->getViewData().GetCurX();
+ nRow = 0;
+ aMap["CurrentColumnHasData"] = OUString::boolean( rDoc.GetPrintAreaVer( mxGridWindow->getViewData().GetTabNo(), nCol, nCol, nRow, true ) );
+ nRow = mxGridWindow->getViewData().GetCurY();
+ nCol = 0;
+ aMap["CurrentRowHasData"] = OUString::boolean( rDoc.GetPrintAreaHor( mxGridWindow->getViewData().GetTabNo(), nRow, nRow, nCol ) );
+
+ ScAppOptions aOpt = SC_MOD()->GetAppOptions();
+ aMap["Zoom"] = OUString::number( aOpt.GetZoom() );
+ return aMap;
+}
+
+ScDBFunc* ScGridWinUIObject::getDBFunc()
+{
+ ScViewData& rViewData = mxGridWindow->getViewData();
+ ScDBFunc* pFunc = rViewData.GetView();
+
+ return pFunc;
+}
+
+ScDrawView* ScGridWinUIObject::getDrawView()
+{
+ ScViewData& rViewData = mxGridWindow->getViewData();
+ ScDrawView* pDrawView = rViewData.GetScDrawView();
+
+ return pDrawView;
+}
+
+ScTabViewShell* ScGridWinUIObject::getViewShell()
+{
+ ScViewData& rViewData = mxGridWindow->getViewData();
+ ScTabViewShell* pViewShell = rViewData.GetViewShell();
+
+ return pViewShell;
+}
+
+ScViewFunc* ScGridWinUIObject::getViewFunc()
+{
+ ScViewData& rViewData = mxGridWindow->getViewData();
+ ScViewFunc* pViewFunc = rViewData.GetView();
+
+ return pViewFunc;
+}
+
+void ScGridWinUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SELECT")
+ {
+ bool bExtend = false;
+ if (rParameters.find("EXTEND") != rParameters.end())
+ {
+ auto itr = rParameters.find("EXTEND");
+ if (itr->second.equalsIgnoreAsciiCase("true") || itr->second == "1")
+ bExtend = true;
+ }
+
+ if (rParameters.find("CELL") != rParameters.end())
+ {
+ auto itr = rParameters.find("CELL");
+ const OUString& rStr = itr->second;
+ ScAddress aAddr = get_address_from_string(mxGridWindow->getViewData().GetDocument(), rStr);
+ ScDBFunc* pFunc = getDBFunc();
+ pFunc->MarkRange(ScRange(aAddr), true, bExtend);
+ mxGridWindow->CursorChanged();
+ }
+ else if (rParameters.find("RANGE") != rParameters.end())
+ {
+ auto itr = rParameters.find("RANGE");
+ const OUString rStr = itr->second;
+ ScRange aRange = get_range_from_string(mxGridWindow->getViewData().GetDocument(), rStr);
+ ScDBFunc* pFunc = getDBFunc();
+ pFunc->MarkRange(aRange, true, bExtend);
+ mxGridWindow->CursorChanged();
+ }
+ else if (rParameters.find("TABLE") != rParameters.end())
+ {
+ auto itr = rParameters.find("TABLE");
+ const OUString rStr = itr->second;
+ sal_Int32 nTab = rStr.toUInt32();
+ ScTabView* pTabView = mxGridWindow->getViewData().GetView();
+ if (pTabView)
+ {
+ ScDocument& rDoc = mxGridWindow->getViewData().GetDocument();
+ if( nTab < rDoc.GetTableCount() )
+ pTabView->SetTabNo(nTab);
+ else
+ {
+ SAL_WARN("sc.uitest", "incorrect table number");
+ }
+ }
+ }
+ else if (rParameters.find("OBJECT") != rParameters.end())
+ {
+ auto itr = rParameters.find("OBJECT");
+ const OUString rStr = itr->second;
+
+ ScDrawView* pDrawView = getDrawView();
+ pDrawView->SelectObject(rStr);
+ }
+ else
+ {
+ SAL_WARN("sc.uitest", "unknown selection method");
+ }
+ }
+ else if (rAction == "DESELECT")
+ {
+ if (rParameters.find("OBJECT") != rParameters.end())
+ {
+ ScDrawView* pDrawView = getDrawView();
+ pDrawView->UnmarkAll();
+
+ ScTabViewShell* pViewShell = getViewShell();
+ pViewShell->SetDrawShell(false);
+ }
+ }
+ else if (rAction == "ACTIVATE")
+ {
+ ScDrawView* pDrawView = getDrawView();
+ const SdrMarkList& rMarkList = pDrawView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ ScTabViewShell* pViewShell = getViewShell();
+ pViewShell->ActivateObject(static_cast<SdrOle2Obj*>(pObj), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY);
+ }
+ else
+ {
+ SAL_WARN("sc.uitest", "can't activate non-ole objects");
+ }
+ }
+ else
+ SAL_WARN("sc.uitest", "can't activate the current selection");
+ }
+ else if (rAction == "LAUNCH")
+ {
+ if ( rParameters.find("AUTOFILTER") != rParameters.end())
+ {
+ auto itrCol = rParameters.find("COL");
+ if (itrCol == rParameters.end())
+ {
+ SAL_WARN("sc.uitest", "missing COL parameter");
+ return;
+ }
+
+ auto itrRow = rParameters.find("ROW");
+ if (itrRow == rParameters.end())
+ {
+ SAL_WARN("sc.uitest", "missing ROW parameter");
+ return;
+ }
+ SCROW nRow = itrRow->second.toUInt32();
+ SCCOL nCol = itrCol->second.toUInt32();
+ mxGridWindow->LaunchAutoFilterMenu(nCol, nRow);
+ }
+ else if ( rParameters.find("PIVOTTABLE") != rParameters.end())
+ {
+ auto itrCol = rParameters.find("COL");
+ if (itrCol == rParameters.end())
+ {
+ SAL_WARN("sc.uitest", "missing COL parameter");
+ return;
+ }
+
+ auto itrRow = rParameters.find("ROW");
+ if (itrRow == rParameters.end())
+ {
+ SAL_WARN("sc.uitest", "missing ROW parameter");
+ return;
+ }
+ SCROW nRow = itrRow->second.toUInt32();
+ SCCOL nCol = itrCol->second.toUInt32();
+ mxGridWindow->LaunchDPFieldMenu(nCol, nRow);
+ }
+ else if ( rParameters.find("SELECTMENU") != rParameters.end())
+ {
+ auto itrCol = rParameters.find("COL");
+ if (itrCol == rParameters.end())
+ {
+ SAL_WARN("sc.uitest", "missing COL parameter");
+ return;
+ }
+
+ auto itrRow = rParameters.find("ROW");
+ if (itrRow == rParameters.end())
+ {
+ SAL_WARN("sc.uitest", "missing ROW parameter");
+ return;
+ }
+ SCROW nRow = itrRow->second.toUInt32();
+ SCCOL nCol = itrCol->second.toUInt32();
+ mxGridWindow->LaunchDataSelectMenu(nCol, nRow);
+ }
+ }
+ else if (rAction == "COMMENT")
+ {
+ if ( rParameters.find("OPEN") != rParameters.end() )
+ {
+ ScViewFunc* pViewFunc = getViewFunc();
+ pViewFunc->EditNote();
+ }
+ else if ( rParameters.find("CLOSE") != rParameters.end() )
+ {
+ FuDraw* pDraw = dynamic_cast<FuDraw*>(getViewFunc()->GetDrawFuncPtr());
+ assert(pDraw);
+ ScViewData& rViewData = mxGridWindow->getViewData();
+ rViewData.GetDispatcher().Execute( pDraw->GetSlotID() , SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else if ( rParameters.find("SETTEXT") != rParameters.end() )
+ {
+ auto itr = rParameters.find("SETTEXT");
+ const OUString rStr = itr->second;
+ ScDocument& rDoc = mxGridWindow->getViewData().GetDocument();
+ ScAddress aPos( mxGridWindow->getViewData().GetCurX() , mxGridWindow->getViewData().GetCurY() , mxGridWindow->getViewData().GetTabNo() );
+ rDoc.GetOrCreateNote( aPos )->SetText( aPos , rStr );
+ }
+ }
+ else if (rAction == "SIDEBAR")
+ {
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ DBG_ASSERT(pViewFrm, "ScGridWinUIObject::execute: no viewframe");
+ pViewFrm->ShowChildWindow(SID_SIDEBAR);
+
+ auto itr = rParameters.find("PANEL");
+ if (itr != rParameters.end())
+ {
+ OUString aVal = itr->second;
+ ::sfx2::sidebar::Sidebar::ShowPanel(aVal, pViewFrm->GetFrame().GetFrameInterface());
+ }
+ }
+ else if (rAction == "SET")
+ {
+ if (rParameters.find("ZOOM") != rParameters.end())
+ {
+ auto itr = rParameters.find("ZOOM");
+ OUString aVal = itr->second;
+ sal_Int32 nVal = aVal.toInt32();
+ ScTabViewShell* pViewShell = getViewShell();
+ ScModule* pScMod = SC_MOD();
+ if( nVal )
+ {
+ ScAppOptions aNewOpt = pScMod->GetAppOptions();
+ aNewOpt.SetZoom( nVal );
+ pScMod->SetAppOptions( aNewOpt );
+ Fraction aFract( nVal, 100 );
+ pViewShell->SetZoom( aFract, aFract, true );
+ pViewShell->PaintGrid();
+ pViewShell->PaintTop();
+ pViewShell->PaintLeft();
+ }
+ }
+ }
+ else
+ {
+ WindowUIObject::execute(rAction, rParameters);
+ }
+}
+
+namespace {
+
+ScDrawLayer* get_draw_layer(VclPtr<ScGridWindow> const & xGridWindow)
+{
+ return xGridWindow->getViewData().GetDocument().GetDrawLayer();
+}
+
+SdrPage* get_draw_page(VclPtr<ScGridWindow> const & xGridWindow, SCTAB nTab)
+{
+ ScDrawLayer* pDrawLayer = get_draw_layer(xGridWindow);
+
+ return pDrawLayer->GetPage(nTab);
+}
+
+std::set<OUString> collect_charts(VclPtr<ScGridWindow> const & xGridWindow)
+{
+ SCTAB nTab = xGridWindow->getViewData().GetTabNo();
+ SdrPage* pPage = get_draw_page(xGridWindow, nTab);
+
+ std::set<OUString> aRet;
+
+ if (!pPage)
+ return aRet;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (pObject->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ aRet.insert(static_cast<SdrOle2Obj*>(pObject)->GetPersistName());
+ }
+ pObject = aIter.Next();
+ }
+
+ return aRet;
+}
+
+}
+
+std::set<OUString> ScGridWinUIObject::get_children() const
+{
+ std::set<OUString> aChildren = collect_charts(mxGridWindow);
+ return aChildren;
+}
+
+std::unique_ptr<UIObject> ScGridWinUIObject::get_child(const OUString& /*rID*/)
+{
+ return nullptr;
+}
+
+std::unique_ptr<UIObject> ScGridWinUIObject::create(vcl::Window* pWindow)
+{
+ ScGridWindow* pGridWin = dynamic_cast<ScGridWindow*>(pWindow);
+ assert(pGridWin);
+ return std::unique_ptr<UIObject>(new ScGridWinUIObject(pGridWin));
+}
+
+OUString ScGridWinUIObject::get_name() const
+{
+ return "ScGridWinUIObject";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoDeleteSparkline.cxx b/sc/source/ui/undo/UndoDeleteSparkline.cxx
new file mode 100644
index 0000000000..6c9df18090
--- /dev/null
+++ b/sc/source/ui/undo/UndoDeleteSparkline.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/.
+ *
+ */
+
+#include <undo/UndoDeleteSparkline.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+
+namespace sc
+{
+UndoDeleteSparkline::UndoDeleteSparkline(ScDocShell& rDocShell, ScAddress const& rSparklinePosition)
+ : ScSimpleUndo(&rDocShell)
+ , maSparklinePosition(rSparklinePosition)
+{
+}
+
+UndoDeleteSparkline::~UndoDeleteSparkline() {}
+
+void UndoDeleteSparkline::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+ auto pSparkline = rDocument.GetSparkline(maSparklinePosition);
+ if (!pSparkline)
+ {
+ rDocument.CreateSparkline(maSparklinePosition, mpSparklineGroup);
+ }
+ else
+ {
+ SAL_WARN("sc", "Can't undo deletion if the sparkline at that address already exists.");
+ }
+
+ pDocShell->PostPaintCell(maSparklinePosition);
+
+ EndUndo();
+}
+
+void UndoDeleteSparkline::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+ if (auto pSparkline = rDocument.GetSparkline(maSparklinePosition))
+ {
+ mpSparklineGroup = pSparkline->getSparklineGroup();
+ rDocument.DeleteSparkline(maSparklinePosition);
+ }
+ else
+ {
+ SAL_WARN("sc", "Can't delete a sparkline that donesn't exist.");
+ }
+
+ pDocShell->PostPaintCell(maSparklinePosition);
+
+ EndRedo();
+}
+
+void UndoDeleteSparkline::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoDeleteSparkline::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoDeleteSparkline::GetComment() const { return ScResId(STR_UNDO_DELETE_SPARKLINE); }
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoDeleteSparklineGroup.cxx b/sc/source/ui/undo/UndoDeleteSparklineGroup.cxx
new file mode 100644
index 0000000000..71702560b0
--- /dev/null
+++ b/sc/source/ui/undo/UndoDeleteSparklineGroup.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/.
+ *
+ */
+
+#include <undo/UndoDeleteSparklineGroup.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <document.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineList.hxx>
+#include <SparklineGroup.hxx>
+#include <utility>
+
+namespace sc
+{
+UndoDeleteSparklineGroup::UndoDeleteSparklineGroup(
+ ScDocShell& rDocShell, std::shared_ptr<sc::SparklineGroup> pSparklineGroup, SCTAB nTab)
+ : ScSimpleUndo(&rDocShell)
+ , mpSparklineGroup(std::move(pSparklineGroup))
+ , mnTab(nTab)
+{
+}
+
+UndoDeleteSparklineGroup::~UndoDeleteSparklineGroup() {}
+
+void UndoDeleteSparklineGroup::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+
+ for (auto const& pSparkline : maSparklines)
+ {
+ ScAddress aAddress(pSparkline->getColumn(), pSparkline->getRow(), mnTab);
+ auto* pNewSparkline = rDocument.CreateSparkline(aAddress, mpSparklineGroup);
+ pNewSparkline->setInputRange(pSparkline->getInputRange());
+ }
+
+ pDocShell->PostPaintGridAll();
+
+ EndUndo();
+}
+
+void UndoDeleteSparklineGroup::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+ auto* pList = rDocument.GetSparklineList(mnTab);
+ if (pList)
+ {
+ maSparklines = pList->getSparklinesFor(mpSparklineGroup);
+
+ for (auto const& pSparkline : maSparklines)
+ {
+ ScAddress aAddress(pSparkline->getColumn(), pSparkline->getRow(), mnTab);
+ rDocument.DeleteSparkline(aAddress);
+ }
+ }
+ pDocShell->PostPaintGridAll();
+
+ EndRedo();
+}
+
+void UndoDeleteSparklineGroup::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoDeleteSparklineGroup::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoDeleteSparklineGroup::GetComment() const
+{
+ return ScResId(STR_UNDO_DELETE_SPARKLINE_GROUP);
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoEditSparkline.cxx b/sc/source/ui/undo/UndoEditSparkline.cxx
new file mode 100644
index 0000000000..f33b7fa7aa
--- /dev/null
+++ b/sc/source/ui/undo/UndoEditSparkline.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/.
+ *
+ */
+
+#include <undo/UndoEditSparkline.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <utility>
+
+namespace sc
+{
+UndoEditSparkline::UndoEditSparkline(ScDocShell& rDocShell,
+ std::shared_ptr<sc::Sparkline> pSparkline, SCTAB nTab,
+ ScRangeList aDataRange)
+ : ScSimpleUndo(&rDocShell)
+ , mpSparkline(std::move(pSparkline))
+ , mnTab(nTab)
+ , maOldDataRange(mpSparkline->getInputRange())
+ , maNewDataRange(std::move(aDataRange))
+{
+}
+
+UndoEditSparkline::~UndoEditSparkline() = default;
+
+void UndoEditSparkline::Undo()
+{
+ BeginUndo();
+
+ mpSparkline->setInputRange(maOldDataRange);
+
+ pDocShell->PostPaintCell(ScAddress(mpSparkline->getColumn(), mpSparkline->getRow(), mnTab));
+
+ EndUndo();
+}
+
+void UndoEditSparkline::Redo()
+{
+ BeginRedo();
+
+ mpSparkline->setInputRange(maNewDataRange);
+
+ pDocShell->PostPaintCell(ScAddress(mpSparkline->getColumn(), mpSparkline->getRow(), mnTab));
+
+ EndRedo();
+}
+
+void UndoEditSparkline::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoEditSparkline::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoEditSparkline::GetComment() const { return ScResId(STR_UNDO_EDIT_SPARKLINE); }
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoEditSparklineGroup.cxx b/sc/source/ui/undo/UndoEditSparklineGroup.cxx
new file mode 100644
index 0000000000..dbe7a7b599
--- /dev/null
+++ b/sc/source/ui/undo/UndoEditSparklineGroup.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/.
+ *
+ */
+
+#include <undo/UndoEditSparklineGroup.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+#include <utility>
+
+namespace sc
+{
+UndoEditSparklneGroup::UndoEditSparklneGroup(
+ ScDocShell& rDocShell, std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup,
+ sc::SparklineAttributes aAttributes)
+ : ScSimpleUndo(&rDocShell)
+ , m_pSparklineGroup(pSparklineGroup)
+ , m_aNewAttributes(std::move(aAttributes))
+ , m_aOriginalAttributes(pSparklineGroup->getAttributes())
+{
+}
+
+UndoEditSparklneGroup::~UndoEditSparklneGroup() = default;
+
+void UndoEditSparklneGroup::Undo()
+{
+ BeginUndo();
+
+ m_pSparklineGroup->setAttributes(m_aOriginalAttributes);
+ pDocShell->PostPaintGridAll();
+
+ EndUndo();
+}
+
+void UndoEditSparklneGroup::Redo()
+{
+ BeginRedo();
+
+ m_pSparklineGroup->setAttributes(m_aNewAttributes);
+ pDocShell->PostPaintGridAll();
+
+ EndRedo();
+}
+
+void UndoEditSparklneGroup::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoEditSparklneGroup::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoEditSparklneGroup::GetComment() const
+{
+ return ScResId(STR_UNDO_EDIT_SPARKLINE_GROUP);
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoGroupSparklines.cxx b/sc/source/ui/undo/UndoGroupSparklines.cxx
new file mode 100644
index 0000000000..c8570995b6
--- /dev/null
+++ b/sc/source/ui/undo/UndoGroupSparklines.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/.
+ *
+ */
+
+#include <undo/UndoGroupSparklines.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+#include <utility>
+
+namespace sc
+{
+UndoGroupSparklines::UndoGroupSparklines(ScDocShell& rDocShell, ScRange const& rRange,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup)
+ : ScSimpleUndo(&rDocShell)
+ , m_aRange(rRange)
+ , m_pSparklineGroup(std::move(pSparklineGroup))
+{
+}
+
+UndoGroupSparklines::~UndoGroupSparklines() = default;
+
+void UndoGroupSparklines::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+
+ for (auto& rUndoData : m_aUndoData)
+ {
+ rDocument.DeleteSparkline(rUndoData.m_aAddress);
+ auto* pCreated
+ = rDocument.CreateSparkline(rUndoData.m_aAddress, rUndoData.m_pSparklineGroup);
+ pCreated->setInputRange(rUndoData.m_aDataRangeList);
+ }
+
+ m_aUndoData.clear();
+
+ pDocShell->PostPaint(m_aRange, PaintPartFlags::All);
+
+ EndUndo();
+}
+
+void UndoGroupSparklines::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+
+ for (ScAddress aAddress = m_aRange.aStart; aAddress.Col() <= m_aRange.aEnd.Col();
+ aAddress.IncCol())
+ {
+ aAddress.SetRow(m_aRange.aStart.Row());
+ for (; aAddress.Row() <= m_aRange.aEnd.Row(); aAddress.IncRow())
+ {
+ if (auto pSparkline = rDocument.GetSparkline(aAddress))
+ {
+ m_aUndoData.emplace_back(aAddress, pSparkline->getInputRange(),
+ pSparkline->getSparklineGroup());
+
+ rDocument.DeleteSparkline(aAddress);
+ auto* pCreated = rDocument.CreateSparkline(aAddress, m_pSparklineGroup);
+ pCreated->setInputRange(pSparkline->getInputRange());
+ }
+ }
+ }
+
+ pDocShell->PostPaint(m_aRange, PaintPartFlags::All);
+
+ EndRedo();
+}
+
+void UndoGroupSparklines::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoGroupSparklines::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoGroupSparklines::GetComment() const { return ScResId(STR_UNDO_GROUP_SPARKLINES); }
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoInsertSparkline.cxx b/sc/source/ui/undo/UndoInsertSparkline.cxx
new file mode 100644
index 0000000000..708f3d746f
--- /dev/null
+++ b/sc/source/ui/undo/UndoInsertSparkline.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 <undo/UndoInsertSparkline.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineData.hxx>
+#include <utility>
+
+namespace sc
+{
+UndoInsertSparkline::UndoInsertSparkline(ScDocShell& rDocShell,
+ std::vector<SparklineData> aSparklineDataVector,
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup)
+ : ScSimpleUndo(&rDocShell)
+ , maSparklineDataVector(std::move(aSparklineDataVector))
+ , mpSparklineGroup(std::move(pSparklineGroup))
+{
+}
+
+UndoInsertSparkline::~UndoInsertSparkline() {}
+
+void UndoInsertSparkline::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+ ScRangeList aRanges;
+ for (auto const& rSparklineData : maSparklineDataVector)
+ {
+ rDocument.DeleteSparkline(rSparklineData.maPosition);
+ aRanges.push_back(ScRange(rSparklineData.maPosition));
+ }
+
+ pDocShell->PostPaint(aRanges, PaintPartFlags::All);
+
+ EndUndo();
+}
+
+void UndoInsertSparkline::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+ ScRangeList aRanges;
+ for (auto const& rSparklineData : maSparklineDataVector)
+ {
+ auto* pCreated = rDocument.CreateSparkline(rSparklineData.maPosition, mpSparklineGroup);
+ pCreated->setInputRange(rSparklineData.maData);
+ aRanges.push_back(ScRange(rSparklineData.maPosition));
+ }
+
+ pDocShell->PostPaint(aRanges, PaintPartFlags::All);
+
+ EndRedo();
+}
+
+void UndoInsertSparkline::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoInsertSparkline::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoInsertSparkline::GetComment() const
+{
+ return ScResId(STR_UNDO_INSERT_SPARKLINE_GROUP);
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoThemeChange.cxx b/sc/source/ui/undo/UndoThemeChange.cxx
new file mode 100644
index 0000000000..a977055070
--- /dev/null
+++ b/sc/source/ui/undo/UndoThemeChange.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <undo/UndoThemeChange.hxx>
+#include <docmodel/theme/Theme.hxx>
+#include <svx/svdpage.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+
+namespace sc
+{
+UndoThemeChange::UndoThemeChange(ScDocShell& rDocShell,
+ std::shared_ptr<model::ColorSet> const& pOldColorSet,
+ std::shared_ptr<model::ColorSet> const& pNewColorSet)
+ : ScSimpleUndo(&rDocShell)
+ , mpOldColorSet(pOldColorSet)
+ , mpNewColorSet(pNewColorSet)
+{
+}
+
+UndoThemeChange::~UndoThemeChange() = default;
+
+namespace
+{
+std::shared_ptr<model::Theme> getTheme(ScDocShell& rDocShell)
+{
+ ScDrawLayer* pModel = rDocShell.GetDocument().GetDrawLayer();
+
+ auto pTheme = pModel->getTheme();
+ if (!pTheme)
+ {
+ pTheme = std::make_shared<model::Theme>("Office");
+ pModel->setTheme(pTheme);
+ }
+ return pTheme;
+}
+}
+
+void UndoThemeChange::Undo()
+{
+ BeginUndo();
+
+ auto pTheme = getTheme(*pDocShell);
+ pTheme->setColorSet(mpOldColorSet);
+
+ EndUndo();
+}
+
+void UndoThemeChange::Redo()
+{
+ BeginUndo();
+
+ auto pTheme = getTheme(*pDocShell);
+ pTheme->setColorSet(mpNewColorSet);
+
+ EndRedo();
+}
+
+void UndoThemeChange::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoThemeChange::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoThemeChange::GetComment() const { return ScResId(STR_UNDO_THEME_CHANGE); }
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/UndoUngroupSparklines.cxx b/sc/source/ui/undo/UndoUngroupSparklines.cxx
new file mode 100644
index 0000000000..fe0201eb63
--- /dev/null
+++ b/sc/source/ui/undo/UndoUngroupSparklines.cxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <undo/UndoUngroupSparklines.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+
+namespace sc
+{
+UndoUngroupSparklines::UndoUngroupSparklines(ScDocShell& rDocShell, ScRange const& rRange)
+ : ScSimpleUndo(&rDocShell)
+ , m_aRange(rRange)
+{
+}
+
+UndoUngroupSparklines::~UndoUngroupSparklines() = default;
+
+void UndoUngroupSparklines::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+
+ for (SparklineUndoData& rUndoData : m_aUndoData)
+ {
+ rDocument.DeleteSparkline(rUndoData.m_aAddress);
+ auto* pCreated
+ = rDocument.CreateSparkline(rUndoData.m_aAddress, rUndoData.m_pSparklineGroup);
+ pCreated->setInputRange(rUndoData.m_aDataRangeList);
+ }
+
+ m_aUndoData.clear();
+
+ pDocShell->PostPaint(m_aRange, PaintPartFlags::All);
+
+ EndUndo();
+}
+
+void UndoUngroupSparklines::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+
+ for (ScAddress aAddress = m_aRange.aStart; aAddress.Col() <= m_aRange.aEnd.Col();
+ aAddress.IncCol())
+ {
+ aAddress.SetRow(m_aRange.aStart.Row());
+ for (; aAddress.Row() <= m_aRange.aEnd.Row(); aAddress.IncRow())
+ {
+ if (auto pSparkline = rDocument.GetSparkline(aAddress))
+ {
+ auto const& rpGroup = pSparkline->getSparklineGroup();
+ m_aUndoData.emplace_back(aAddress, pSparkline->getInputRange(), rpGroup);
+ auto pSparklineGroupCopy
+ = std::make_shared<sc::SparklineGroup>(rpGroup->getAttributes());
+ rDocument.DeleteSparkline(aAddress);
+ auto* pCreated = rDocument.CreateSparkline(aAddress, pSparklineGroupCopy);
+ pCreated->setInputRange(pSparkline->getInputRange());
+ }
+ }
+ }
+
+ pDocShell->PostPaint(m_aRange, PaintPartFlags::All);
+
+ EndRedo();
+}
+
+void UndoUngroupSparklines::Repeat(SfxRepeatTarget& /*rTarget*/) {}
+
+bool UndoUngroupSparklines::CanRepeat(SfxRepeatTarget& /*rTarget*/) const { return false; }
+
+OUString UndoUngroupSparklines::GetComment() const { return ScResId(STR_UNDO_UNGROUP_SPARKLINES); }
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/areasave.cxx b/sc/source/ui/undo/areasave.cxx
new file mode 100644
index 0000000000..bc224bba65
--- /dev/null
+++ b/sc/source/ui/undo/areasave.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 <sfx2/linkmgr.hxx>
+
+#include <areasave.hxx>
+#include <arealink.hxx>
+#include <document.hxx>
+#include <documentlinkmgr.hxx>
+#include <docsh.hxx>
+
+ScAreaLinkSaver::ScAreaLinkSaver( const ScAreaLink& rSource ) :
+ aFileName ( rSource.GetFile() ),
+ aFilterName ( rSource.GetFilter() ),
+ aOptions ( rSource.GetOptions() ),
+ aSourceArea ( rSource.GetSource() ),
+ aDestArea ( rSource.GetDestArea() ),
+ nRefreshDelaySeconds ( rSource.GetRefreshDelaySeconds() ) // seconds
+{
+}
+
+bool ScAreaLinkSaver::IsEqualSource( const ScAreaLink& rCompare ) const
+{
+ return ( aFileName == rCompare.GetFile() &&
+ aFilterName == rCompare.GetFilter() &&
+ aOptions == rCompare.GetOptions() &&
+ aSourceArea == rCompare.GetSource() &&
+ nRefreshDelaySeconds == rCompare.GetRefreshDelaySeconds() );
+}
+
+bool ScAreaLinkSaver::IsEqual( const ScAreaLink& rCompare ) const
+{
+ return ( IsEqualSource( rCompare ) &&
+ aDestArea == rCompare.GetDestArea() );
+}
+
+void ScAreaLinkSaver::WriteToLink( ScAreaLink& rLink ) const
+{
+ rLink.SetDestArea( aDestArea );
+}
+
+void ScAreaLinkSaver::InsertNewLink( ScDocument* pDoc )
+{
+ // (see ScUndoRemoveAreaLink::Undo)
+
+ sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ ScDocShell* pObjSh = pDoc->GetDocumentShell();
+
+ if ( pLinkManager && pObjSh )
+ {
+ ScAreaLink* pLink = new ScAreaLink( pObjSh, aFileName, aFilterName, aOptions,
+ aSourceArea, aDestArea.aStart, nRefreshDelaySeconds );
+ pLink->SetInCreate( true );
+ pLink->SetDestArea( aDestArea );
+ OUString aTmp1(aFilterName), aTmp2(aSourceArea);
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aTmp1, &aTmp2 );
+ pLink->Update();
+ pLink->SetInCreate( false );
+ }
+}
+
+ScAreaLinkSaveCollection::ScAreaLinkSaveCollection() {}
+
+ScAreaLinkSaveCollection::~ScAreaLinkSaveCollection() {}
+
+bool ScAreaLinkSaveCollection::IsEqual( const ScDocument* pDoc ) const
+{
+ // IsEqual can be checked in sequence.
+ // Neither ref-update nor removing links will change the order.
+
+ const sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ if (pLinkManager)
+ {
+ size_t nPos = 0;
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nLinkCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nLinkCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto pAreaLink = dynamic_cast<ScAreaLink*>( pBase))
+ {
+ if ( nPos >= size() || !(*this)[nPos].IsEqual( *pAreaLink ) )
+ return false;
+
+ ++nPos;
+ }
+ }
+ if ( nPos < size() )
+ return false; // fewer links in the document than in the save collection
+ }
+
+ return true;
+}
+
+static ScAreaLink* lcl_FindLink( const ::sfx2::SvBaseLinks& rLinks, const ScAreaLinkSaver& rSaver )
+{
+ sal_uInt16 nLinkCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nLinkCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if ( auto pAreaLink = dynamic_cast<ScAreaLink*>( pBase) )
+ if ( rSaver.IsEqualSource( *pAreaLink ) )
+ return pAreaLink; // found
+ }
+ return nullptr; // not found
+}
+
+void ScAreaLinkSaveCollection::Restore( ScDocument* pDoc )
+{
+ // The save collection may contain additional entries that are not in the document.
+ // They must be inserted again.
+ // Entries from the save collection must be searched via source data, as the order
+ // of links changes if deleted entries are re-added to the link manager (always at the end).
+
+ sfx2::LinkManager* pLinkManager = pDoc->GetDocLinkManager().getLinkManager(false);
+ if (!pLinkManager)
+ return;
+
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ size_t nSaveCount = size();
+ for (size_t nPos=0; nPos<nSaveCount; ++nPos)
+ {
+ ScAreaLinkSaver& rSaver = (*this)[nPos];
+ ScAreaLink* pLink = lcl_FindLink( rLinks, rSaver );
+ if ( pLink )
+ rSaver.WriteToLink( *pLink ); // restore output position
+ else
+ rSaver.InsertNewLink( pDoc ); // re-insert deleted link
+ }
+}
+
+std::unique_ptr<ScAreaLinkSaveCollection> ScAreaLinkSaveCollection::CreateFromDoc( const ScDocument* pDoc )
+{
+ std::unique_ptr<ScAreaLinkSaveCollection> pColl;
+
+ sfx2::LinkManager* pLinkManager = const_cast<ScDocument*>(pDoc)->GetLinkManager();
+ if (pLinkManager)
+ {
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nLinkCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nLinkCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto pAreaLink = dynamic_cast<ScAreaLink*>( pBase))
+ {
+ if (!pColl)
+ pColl.reset(new ScAreaLinkSaveCollection);
+
+ pColl->push_back( ScAreaLinkSaver( *pAreaLink ) );
+ }
+ }
+ }
+
+ return pColl;
+}
+
+ScAreaLinkSaver& ScAreaLinkSaveCollection::operator [](size_t nIndex)
+{
+ return maData[nIndex];
+}
+
+const ScAreaLinkSaver& ScAreaLinkSaveCollection::operator [](size_t nIndex) const
+{
+ return maData[nIndex];
+}
+
+size_t ScAreaLinkSaveCollection::size() const
+{
+ return maData.size();
+}
+
+void ScAreaLinkSaveCollection::push_back(const ScAreaLinkSaver& p)
+{
+ maData.push_back(p);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/refundo.cxx b/sc/source/ui/undo/refundo.cxx
new file mode 100644
index 0000000000..d87098720f
--- /dev/null
+++ b/sc/source/ui/undo/refundo.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 <refundo.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <detdata.hxx>
+#include <prnsave.hxx>
+#include <chartlis.hxx>
+#include <dpobject.hxx>
+#include <areasave.hxx>
+#include <unoreflist.hxx>
+#include <scopetools.hxx>
+#include <refupdatecontext.hxx>
+
+ScRefUndoData::ScRefUndoData( const ScDocument* pDoc ) :
+ pPrintRanges(pDoc->CreatePrintRangeSaver())
+{
+ const ScDBCollection* pOldDBColl = pDoc->GetDBCollection();
+ if (pOldDBColl && !pOldDBColl->empty())
+ pDBCollection.reset(new ScDBCollection(*pOldDBColl));
+
+ const ScRangeName* pOldRanges = pDoc->GetRangeName();
+ if (pOldRanges && !pOldRanges->empty())
+ pRangeName.reset(new ScRangeName(*pOldRanges));
+
+ // when handling Pivot solely keep the range?
+
+ const ScDPCollection* pOldDP = pDoc->GetDPCollection();
+ if (pOldDP && pOldDP->GetCount())
+ pDPCollection.reset(new ScDPCollection(*pOldDP));
+
+ const ScDetOpList* pOldDetOp = pDoc->GetDetOpList();
+ if (pOldDetOp && pOldDetOp->Count())
+ pDetOpList.reset(new ScDetOpList(*pOldDetOp));
+
+ const ScChartListenerCollection* pOldChartLisColl = pDoc->GetChartListenerCollection();
+ if (pOldChartLisColl)
+ pChartListenerCollection.reset(new ScChartListenerCollection(*pOldChartLisColl));
+
+ pAreaLinks = ScAreaLinkSaveCollection::CreateFromDoc(pDoc); // returns NULL if empty
+
+ const_cast<ScDocument*>(pDoc)->BeginUnoRefUndo();
+}
+
+ScRefUndoData::~ScRefUndoData()
+{
+ pDBCollection.reset();
+ pRangeName.reset();
+ pPrintRanges.reset();
+ pDPCollection.reset();
+ pDetOpList.reset();
+ pChartListenerCollection.reset();
+ pAreaLinks.reset();
+}
+
+void ScRefUndoData::DeleteUnchanged( const ScDocument* pDoc )
+{
+ if (pDBCollection)
+ {
+ ScDBCollection* pNewDBColl = pDoc->GetDBCollection();
+ if ( pNewDBColl && *pDBCollection == *pNewDBColl )
+ pDBCollection.reset();
+ }
+ if (pRangeName)
+ {
+ ScRangeName* pNewRanges = pDoc->GetRangeName();
+ if ( pNewRanges && *pRangeName == *pNewRanges )
+ pRangeName.reset();
+ }
+
+ if (pPrintRanges)
+ {
+ std::unique_ptr<ScPrintRangeSaver> pNewRanges = pDoc->CreatePrintRangeSaver();
+ if ( pNewRanges && *pPrintRanges == *pNewRanges )
+ pPrintRanges.reset();
+ }
+
+ if (pDPCollection)
+ {
+ ScDPCollection* pNewDP = const_cast<ScDocument*>(pDoc)->GetDPCollection(); //! const
+ if ( pNewDP && pDPCollection->RefsEqual(*pNewDP) )
+ pDPCollection.reset();
+ }
+
+ if (pDetOpList)
+ {
+ ScDetOpList* pNewDetOp = pDoc->GetDetOpList();
+ if ( pNewDetOp && *pDetOpList == *pNewDetOp )
+ pDetOpList.reset();
+ }
+
+ if ( pChartListenerCollection )
+ {
+ ScChartListenerCollection* pNewChartListenerCollection =
+ pDoc->GetChartListenerCollection();
+ if ( pNewChartListenerCollection &&
+ *pChartListenerCollection == *pNewChartListenerCollection )
+ pChartListenerCollection.reset();
+ }
+
+ if (pAreaLinks)
+ {
+ if ( pAreaLinks->IsEqual( pDoc ) )
+ pAreaLinks.reset();
+ }
+
+ if ( pDoc->HasUnoRefUndo() )
+ {
+ pUnoRefs = const_cast<ScDocument*>(pDoc)->EndUnoRefUndo();
+ if ( pUnoRefs && pUnoRefs->IsEmpty() )
+ {
+ pUnoRefs.reset();
+ }
+ }
+}
+
+void ScRefUndoData::DoUndo( ScDocument* pDoc, bool bUndoRefFirst )
+{
+ if (pDBCollection)
+ pDoc->SetDBCollection( std::unique_ptr<ScDBCollection>(new ScDBCollection(*pDBCollection)) );
+ if (pRangeName)
+ pDoc->SetRangeName( std::unique_ptr<ScRangeName>(new ScRangeName(*pRangeName)) );
+
+ if (pPrintRanges)
+ pDoc->RestorePrintRanges(*pPrintRanges);
+
+ if (pDPCollection)
+ {
+ ScDPCollection* pDocDP = pDoc->GetDPCollection();
+ if (pDocDP)
+ pDPCollection->WriteRefsTo( *pDocDP );
+ }
+
+ if (pDetOpList)
+ pDoc->SetDetOpList( std::unique_ptr<ScDetOpList>(new ScDetOpList(*pDetOpList)) );
+
+ // bUndoRefFirst is bSetChartRangeLists
+ if ( pChartListenerCollection )
+ pDoc->SetChartListenerCollection( std::make_unique<ScChartListenerCollection>(
+ *pChartListenerCollection ), bUndoRefFirst );
+
+ if (pDBCollection || pRangeName)
+ {
+ sc::AutoCalcSwitch aACSwitch(*pDoc, false);
+ pDoc->CompileAll();
+
+ sc::SetFormulaDirtyContext aCxt;
+ pDoc->SetAllFormulasDirty(aCxt);
+ }
+
+ if (pAreaLinks)
+ pAreaLinks->Restore( pDoc );
+
+ if ( pUnoRefs )
+ pUnoRefs->Undo( pDoc );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/target.cxx b/sc/source/ui/undo/target.cxx
new file mode 100644
index 0000000000..ee220b633f
--- /dev/null
+++ b/sc/source/ui/undo/target.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 <target.hxx>
+
+ScTabViewTarget::~ScTabViewTarget() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undobase.cxx b/sc/source/ui/undo/undobase.cxx
new file mode 100644
index 0000000000..c9a2336042
--- /dev/null
+++ b/sc/source/ui/undo/undobase.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 <utility>
+#include <vcl/virdev.hxx>
+#include <svx/svdundo.hxx>
+
+#include <undobase.hxx>
+#include <undocell.hxx>
+#include <refundo.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <undoolk.hxx>
+#include <undodraw.hxx>
+#include <dbdata.hxx>
+#include <attrib.hxx>
+#include <queryparam.hxx>
+#include <subtotalparam.hxx>
+#include <rowheightcontext.hxx>
+#include <column.hxx>
+#include <sortparam.hxx>
+#include <columnspanset.hxx>
+#include <undomanager.hxx>
+
+
+ScSimpleUndo::ScSimpleUndo( ScDocShell* pDocSh ) :
+ pDocShell( pDocSh ),
+ mnViewShellId(-1)
+{
+ if (ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell())
+ mnViewShellId = pViewShell->GetViewShellId();
+}
+
+ViewShellId ScSimpleUndo::GetViewShellId() const
+{
+ return mnViewShellId;
+}
+
+bool ScSimpleUndo::SetViewMarkData( const ScMarkData& rMarkData )
+{
+ if ( IsPaintLocked() )
+ return false;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( !pViewShell )
+ return false;
+
+ pViewShell->SetMarkData( rMarkData );
+ return true;
+}
+
+bool ScSimpleUndo::Merge( SfxUndoAction *pNextAction )
+{
+ // A SdrUndoGroup for updating detective arrows can belong
+ // to each Undo-Action.
+ // DetectiveRefresh is always called next,
+ // the SdrUndoGroup is encapsulated in a ScUndoDraw action.
+ // AddUndoAction is only called with bTryMerg=sal_True
+ // for automatic update.
+
+ if ( !pDetectiveUndo && dynamic_cast<const ScUndoDraw*>( pNextAction) != nullptr )
+ {
+ // Take SdrUndoAction from ScUndoDraw Action,
+ // ScUndoDraw is later deleted by the UndoManager
+
+ ScUndoDraw* pCalcUndo = static_cast<ScUndoDraw*>(pNextAction);
+ pDetectiveUndo = pCalcUndo->ReleaseDrawUndo();
+ return true;
+ }
+
+ return false;
+}
+
+void ScSimpleUndo::BeginUndo()
+{
+ pDocShell->SetInUndo( true );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->HideAllCursors(); // for example due to merged cells
+
+ // detective updates happened last, must be undone first
+ if (pDetectiveUndo)
+ pDetectiveUndo->Undo();
+}
+
+namespace
+{
+ class DisableUndoGuard
+ {
+ private:
+ ScDocument& m_rDoc;
+ bool m_bUndoEnabled;
+ public:
+ explicit DisableUndoGuard(ScDocShell *pDocShell)
+ : m_rDoc(pDocShell->GetDocument())
+ , m_bUndoEnabled(m_rDoc.IsUndoEnabled())
+ {
+ m_rDoc.EnableUndo(false);
+ }
+
+ ~DisableUndoGuard()
+ {
+ m_rDoc.EnableUndo(m_bUndoEnabled);
+ }
+ };
+}
+
+void ScSimpleUndo::EndUndo()
+{
+ {
+ // rhbz#1352881 Temporarily turn off undo generation during
+ // SetDocumentModified
+ DisableUndoGuard aGuard(pDocShell);
+ pDocShell->SetDocumentModified();
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->UpdateAutoFillMark();
+ pViewShell->UpdateInputHandler();
+ pViewShell->ShowAllCursors();
+ }
+
+ pDocShell->SetInUndo( false );
+}
+
+void ScSimpleUndo::BeginRedo()
+{
+ pDocShell->SetInUndo( true ); //! own Flag for Redo?
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->HideAllCursors(); // for example due to merged cells
+}
+
+void ScSimpleUndo::EndRedo()
+{
+ if (pDetectiveUndo)
+ pDetectiveUndo->Redo();
+
+ {
+ // rhbz#1352881 Temporarily turn off undo generation during
+ // SetDocumentModified
+ DisableUndoGuard aGuard(pDocShell);
+ pDocShell->SetDocumentModified();
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->UpdateAutoFillMark();
+ pViewShell->UpdateInputHandler();
+ pViewShell->ShowAllCursors();
+ }
+
+ pDocShell->SetInUndo( false );
+}
+
+void ScSimpleUndo::BroadcastChanges( const ScRange& rRange )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
+}
+
+namespace {
+
+class SpanBroadcaster : public sc::ColumnSpanSet::ColumnAction
+{
+ ScDocument& mrDoc;
+ SCTAB mnCurTab;
+ SCCOL mnCurCol;
+
+public:
+ explicit SpanBroadcaster( ScDocument& rDoc ) : mrDoc(rDoc), mnCurTab(-1), mnCurCol(-1) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mnCurTab = pCol->GetTab();
+ mnCurCol = pCol->GetCol();
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ ScRange aRange(mnCurCol, nRow1, mnCurTab, mnCurCol, nRow2, mnCurTab);
+ mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ };
+};
+
+}
+
+void ScSimpleUndo::BroadcastChanges( const DataSpansType& rSpans )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SpanBroadcaster aBroadcaster(rDoc);
+
+ for (const auto& rEntry : rSpans)
+ {
+ const sc::ColumnSpanSet& rSet = *rEntry.second;
+ rSet.executeColumnAction(rDoc, aBroadcaster);
+ }
+}
+
+void ScSimpleUndo::ShowTable( SCTAB nTab )
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo( nTab );
+}
+
+void ScSimpleUndo::ShowTable( const ScRange& rRange )
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ SCTAB nStart = rRange.aStart.Tab();
+ SCTAB nEnd = rRange.aEnd.Tab();
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ if ( nTab < nStart || nTab > nEnd ) // if not in range:
+ pViewShell->SetTabNo( nStart ); // at beginning of the range
+ }
+}
+
+ScBlockUndo::ScBlockUndo( ScDocShell* pDocSh, const ScRange& rRange,
+ ScBlockUndoMode eBlockMode ) :
+ ScSimpleUndo( pDocSh ),
+ aBlockRange( rRange ),
+ eMode( eBlockMode )
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScBlockUndo::~ScBlockUndo()
+{
+ pDrawUndo.reset();
+}
+
+void ScBlockUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+ EnableDrawAdjust( &pDocShell->GetDocument(), false );
+}
+
+void ScBlockUndo::EndUndo()
+{
+ if (eMode == SC_UNDO_AUTOHEIGHT)
+ AdjustHeight();
+
+ EnableDrawAdjust( &pDocShell->GetDocument(), true );
+ DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() );
+
+ ShowBlock();
+ ScSimpleUndo::EndUndo();
+}
+
+void ScBlockUndo::EndRedo()
+{
+ if (eMode == SC_UNDO_AUTOHEIGHT)
+ AdjustHeight();
+
+ ShowBlock();
+ ScSimpleUndo::EndRedo();
+}
+
+bool ScBlockUndo::AdjustHeight()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ Fraction aZoomX( 1, 1 );
+ Fraction aZoomY = aZoomX;
+ double nPPTX, nPPTY;
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ ScViewData& rData = pViewShell->GetViewData();
+ nPPTX = rData.GetPPTX();
+ nPPTY = rData.GetPPTY();
+ aZoomX = rData.GetZoomX();
+ aZoomY = rData.GetZoomY();
+ }
+ else
+ {
+ // Leave zoom at 100
+ nPPTX = ScGlobal::nScreenPPTX;
+ nPPTY = ScGlobal::nScreenPPTY;
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, pVirtDev);
+ bool bRet = rDoc.SetOptimalHeight(
+ aCxt, aBlockRange.aStart.Row(), aBlockRange.aEnd.Row(), aBlockRange.aStart.Tab(), true);
+
+ if (bRet)
+ {
+ // tdf#76183: recalculate objects' positions
+ rDoc.SetDrawPageSize(aBlockRange.aStart.Tab());
+
+ pDocShell->PostPaint( 0, aBlockRange.aStart.Row(), aBlockRange.aStart.Tab(),
+ rDoc.MaxCol(), rDoc.MaxRow(), aBlockRange.aEnd.Tab(),
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+ return bRet;
+}
+
+void ScBlockUndo::ShowBlock()
+{
+ if ( IsPaintLocked() )
+ return;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (!pViewShell)
+ return;
+
+ ShowTable( aBlockRange ); // with multiple sheets in range each of them is good
+ pViewShell->MoveCursorAbs( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
+ SC_FOLLOW_JUMP, false, false );
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ ScRange aRange = aBlockRange;
+ aRange.aStart.SetTab( nTab );
+ aRange.aEnd.SetTab( nTab );
+ pViewShell->MarkRange( aRange );
+
+ // not through SetMarkArea to MarkData, due to possibly lacking paint
+}
+
+ScMultiBlockUndo::ScMultiBlockUndo(
+ ScDocShell* pDocSh, ScRangeList aRanges) :
+ ScSimpleUndo(pDocSh),
+ maBlockRanges(std::move(aRanges))
+{
+ mpDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScMultiBlockUndo::~ScMultiBlockUndo()
+{
+ mpDrawUndo.reset();
+}
+
+void ScMultiBlockUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+ EnableDrawAdjust(&pDocShell->GetDocument(), false);
+}
+
+void ScMultiBlockUndo::EndUndo()
+{
+ EnableDrawAdjust(&pDocShell->GetDocument(), true);
+ DoSdrUndoAction(mpDrawUndo.get(), &pDocShell->GetDocument());
+
+ ShowBlock();
+ ScSimpleUndo::EndUndo();
+}
+
+void ScMultiBlockUndo::EndRedo()
+{
+ ShowBlock();
+ ScSimpleUndo::EndRedo();
+}
+
+void ScMultiBlockUndo::ShowBlock()
+{
+ if ( IsPaintLocked() )
+ return;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (!pViewShell)
+ return;
+
+ if (maBlockRanges.empty())
+ return;
+
+ // Move to the sheet of the first range.
+ ScRange aRange = maBlockRanges.front();
+ ShowTable(aRange);
+ pViewShell->MoveCursorAbs(
+ aRange.aStart.Col(), aRange.aStart.Row(), SC_FOLLOW_JUMP, false, false);
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ aRange.aStart.SetTab(nTab);
+ aRange.aEnd.SetTab(nTab);
+ pViewShell->MarkRange(aRange, false);
+
+ for (size_t i = 1, n = maBlockRanges.size(); i < n; ++i)
+ {
+ aRange = maBlockRanges[i];
+ aRange.aStart.SetTab(nTab);
+ aRange.aEnd.SetTab(nTab);
+ pViewShell->MarkRange(aRange, false, true);
+ }
+}
+
+ScMoveUndo::ScMoveUndo( ScDocShell* pDocSh, ScDocumentUniquePtr pRefDoc, std::unique_ptr<ScRefUndoData> pRefData ) :
+ ScSimpleUndo( pDocSh ),
+ pRefUndoDoc( std::move(pRefDoc) ),
+ pRefUndoData( std::move(pRefData) )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (pRefUndoData)
+ pRefUndoData->DeleteUnchanged(&rDoc);
+ pDrawUndo = GetSdrUndoAction( &rDoc );
+}
+
+ScMoveUndo::~ScMoveUndo()
+{
+ pRefUndoData.reset();
+ pRefUndoDoc.reset();
+ pDrawUndo.reset();
+}
+
+void ScMoveUndo::UndoRef()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRange aRange(0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),pRefUndoDoc->GetTableCount()-1);
+ pRefUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::FORMULA, false, rDoc, nullptr, false);
+ if (pRefUndoData)
+ pRefUndoData->DoUndo( &rDoc, false );
+}
+
+void ScMoveUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+
+ EnableDrawAdjust( &pDocShell->GetDocument(), false );
+}
+
+void ScMoveUndo::EndUndo()
+{
+ DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() ); // must also be called when pointer is null
+
+ if (pRefUndoDoc)
+ UndoRef();
+
+ EnableDrawAdjust( &pDocShell->GetDocument(), true );
+
+ ScSimpleUndo::EndUndo();
+}
+
+ScDBFuncUndo::ScDBFuncUndo( ScDocShell* pDocSh, const ScRange& rOriginal ) :
+ ScSimpleUndo( pDocSh ),
+ aOriginalRange( rOriginal )
+{
+ pAutoDBRange = pDocSh->GetOldAutoDBRange();
+}
+
+ScDBFuncUndo::~ScDBFuncUndo()
+{
+ pAutoDBRange.reset();
+}
+
+void ScDBFuncUndo::BeginUndo()
+{
+ ScSimpleUndo::BeginUndo();
+ DoSdrUndoAction( nullptr, &pDocShell->GetDocument() );
+}
+
+void ScDBFuncUndo::EndUndo()
+{
+ ScSimpleUndo::EndUndo();
+
+ if ( !pAutoDBRange )
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = rDoc.GetVisibleTab();
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(nTab);
+ if (!pNoNameData )
+ return;
+
+ SCCOL nRangeX1;
+ SCROW nRangeY1;
+ SCCOL nRangeX2;
+ SCROW nRangeY2;
+ SCTAB nRangeTab;
+ pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
+
+ *pNoNameData = *pAutoDBRange;
+
+ if ( pAutoDBRange->HasAutoFilter() )
+ {
+ // restore AutoFilter buttons
+ pAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ rDoc.ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
+ pDocShell->PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid );
+ }
+}
+
+void ScDBFuncUndo::BeginRedo()
+{
+ RedoSdrUndoAction( nullptr );
+ if ( pAutoDBRange )
+ {
+ // move the database range to this function's position again (see ScDocShell::GetDBData)
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDBData* pNoNameData = rDoc.GetAnonymousDBData(aOriginalRange.aStart.Tab());
+ if ( pNoNameData )
+ {
+
+ SCCOL nRangeX1;
+ SCROW nRangeY1;
+ SCCOL nRangeX2;
+ SCROW nRangeY2;
+ SCTAB nRangeTab;
+ pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+ pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 );
+
+ pNoNameData->SetSortParam( ScSortParam() );
+ pNoNameData->SetQueryParam( ScQueryParam() );
+ pNoNameData->SetSubTotalParam( ScSubTotalParam() );
+
+ pNoNameData->SetArea( aOriginalRange.aStart.Tab(),
+ aOriginalRange.aStart.Col(), aOriginalRange.aStart.Row(),
+ aOriginalRange.aEnd.Col(), aOriginalRange.aEnd.Row() );
+
+ pNoNameData->SetByRow( true );
+ pNoNameData->SetAutoFilter( false );
+ // header is always set with the operation in redo
+ }
+ }
+
+ ScSimpleUndo::BeginRedo();
+}
+
+void ScDBFuncUndo::EndRedo()
+{
+ ScSimpleUndo::EndRedo();
+}
+
+ScUndoWrapper::ScUndoWrapper( std::unique_ptr<SfxUndoAction> pUndo ) :
+ pWrappedUndo( std::move(pUndo) ),
+ mnViewShellId( -1 )
+{
+ if (pWrappedUndo)
+ mnViewShellId = pWrappedUndo->GetViewShellId();
+}
+
+ScUndoWrapper::~ScUndoWrapper()
+{
+}
+
+void ScUndoWrapper::ForgetWrappedUndo()
+{
+ pWrappedUndo = nullptr; // don't delete in dtor - pointer must be stored outside
+}
+
+OUString ScUndoWrapper::GetComment() const
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->GetComment();
+ return OUString();
+}
+
+ViewShellId ScUndoWrapper::GetViewShellId() const
+{
+ return mnViewShellId;
+}
+
+OUString ScUndoWrapper::GetRepeatComment(SfxRepeatTarget& rTarget) const
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->GetRepeatComment(rTarget);
+ return OUString();
+}
+
+bool ScUndoWrapper::Merge( SfxUndoAction* pNextAction )
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->Merge(pNextAction);
+ else
+ return false;
+}
+
+void ScUndoWrapper::Undo()
+{
+ if (pWrappedUndo)
+ pWrappedUndo->Undo();
+}
+
+void ScUndoWrapper::Redo()
+{
+ if (pWrappedUndo)
+ pWrappedUndo->Redo();
+}
+
+void ScUndoWrapper::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (pWrappedUndo)
+ pWrappedUndo->Repeat(rTarget);
+}
+
+bool ScUndoWrapper::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ if (pWrappedUndo)
+ return pWrappedUndo->CanRepeat(rTarget);
+ else
+ return false;
+}
+
+ScUndoManager::~ScUndoManager() {}
+
+/**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action.
+ */
+bool ScUndoManager::IsViewUndoActionIndependent(const SfxViewShell* pView, sal_uInt16& rOffset) const
+{
+ if (GetUndoActionCount() <= 1)
+ {
+ // Single or less undo, owned by another view.
+ return false;
+ }
+
+ if (!pView)
+ {
+ return false;
+ }
+
+ // Last undo action that doesn't belong to the view.
+ const SfxUndoAction* pTopAction = GetUndoAction();
+
+ ViewShellId nViewId = pView->GetViewShellId();
+
+ // Earlier undo action that belongs to the view, but is not the top one.
+ const SfxUndoAction* pViewAction = nullptr;
+ size_t nOffset = 0;
+ for (size_t i = 0; i < GetUndoActionCount(); ++i)
+ {
+ const SfxUndoAction* pAction = GetUndoAction(i);
+ if (pAction->GetViewShellId() == nViewId)
+ {
+ pViewAction = pAction;
+ nOffset = i;
+ break;
+ }
+ }
+
+ if (!pViewAction)
+ {
+ // Found no earlier undo action that belongs to the view.
+ return false;
+ }
+
+ std::optional<ScRange> topRange = getAffectedRangeFromUndo(pTopAction);
+ if (!topRange)
+ return false;
+
+ std::optional<ScRange> viewRange = getAffectedRangeFromUndo(pViewAction);
+ if (!viewRange)
+ return false;
+
+ if (topRange->Intersects(*viewRange))
+ return false;
+
+ for (size_t i = 0; i < GetRedoActionCount(); ++i)
+ {
+ auto pRedoAction = getScSimpleUndo(GetRedoAction(i));
+ if (!pRedoAction)
+ {
+ return false;
+ }
+ std::optional<ScRange> redoRange = getAffectedRangeFromUndo(pRedoAction);
+ if (!redoRange || (redoRange->Intersects(*viewRange) && pRedoAction->GetViewShellId() != nViewId))
+ {
+ // Dependent redo action and owned by another view.
+ return false;
+ }
+ }
+
+ rOffset = nOffset;
+ return true;
+}
+
+std::optional<ScRange> ScUndoManager::getAffectedRangeFromUndo(const SfxUndoAction* pAction)
+{
+ auto pSimpleUndo = getScSimpleUndo(pAction);
+ if (!pSimpleUndo)
+ return std::nullopt;
+ return pSimpleUndo->getAffectedRange();
+}
+
+const ScSimpleUndo* ScUndoManager::getScSimpleUndo(const SfxUndoAction* pAction)
+{
+ const ScSimpleUndo* pSimpleUndo = dynamic_cast<const ScSimpleUndo*>(pAction);
+ if (pSimpleUndo)
+ return pSimpleUndo;
+ auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction);
+ if (!pListAction)
+ return nullptr;
+ if (pListAction->maUndoActions.size() > 1)
+ return nullptr;
+ return dynamic_cast<ScSimpleUndo*>(pListAction->maUndoActions[0].pAction.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoblk.cxx b/sc/source/ui/undo/undoblk.cxx
new file mode 100644
index 0000000000..d352ba143b
--- /dev/null
+++ b/sc/source/ui/undo/undoblk.cxx
@@ -0,0 +1,2440 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <utility>
+#include <vcl/virdev.hxx>
+#include <editeng/boxitem.hxx>
+#include <sfx2/app.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <undoblk.hxx>
+#include <undoutil.hxx>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <rangenam.hxx>
+#include <rangeutl.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <target.hxx>
+#include <docpool.hxx>
+#include <docfunc.hxx>
+#include <attrib.hxx>
+#include <chgtrack.hxx>
+#include <transobj.hxx>
+#include <refundo.hxx>
+#include <undoolk.hxx>
+#include <clipparam.hxx>
+#include <rowheightcontext.hxx>
+#include <refupdatecontext.hxx>
+#include <validat.hxx>
+#include <gridwin.hxx>
+#include <columnspanset.hxx>
+
+#include <memory>
+#include <set>
+
+// TODO:
+/*A*/ // SetOptimalHeight on Document, if no View
+/*B*/ // linked sheets
+/*C*/ // ScArea
+//? // check later
+
+ScUndoInsertCells::ScUndoInsertCells( ScDocShell* pNewDocShell,
+ const ScRange& rRange,
+ SCTAB nNewCount, std::unique_ptr<SCTAB[]> pNewTabs, std::unique_ptr<SCTAB[]> pNewScenarios,
+ InsCellCmd eNewCmd, ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData,
+ bool bNewPartOfPaste ) :
+ ScMoveUndo( pNewDocShell, std::move(pUndoDocument), std::move(pRefData) ),
+ aEffRange( rRange ),
+ nCount( nNewCount ),
+ pTabs( std::move(pNewTabs) ),
+ pScenarios( std::move(pNewScenarios) ),
+ eCmd( eNewCmd ),
+ bPartOfPaste( bNewPartOfPaste )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER) // whole row?
+ {
+ aEffRange.aStart.SetCol(0);
+ aEffRange.aEnd.SetCol(rDoc.MaxCol());
+ }
+
+ if (eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER) // whole column?
+ {
+ aEffRange.aStart.SetRow(0);
+ aEffRange.aEnd.SetRow(rDoc.MaxRow());
+ }
+
+ SetChangeTrack();
+}
+
+ScUndoInsertCells::~ScUndoInsertCells()
+{
+}
+
+OUString ScUndoInsertCells::GetComment() const
+{
+ return ScResId( pPasteUndo ? STR_UNDO_PASTE : STR_UNDO_INSERTCELLS );
+}
+
+bool ScUndoInsertCells::Merge( SfxUndoAction* pNextAction )
+{
+ // If a paste undo action has already been added, append (detective) action there.
+ if ( pPasteUndo )
+ return pPasteUndo->Merge( pNextAction );
+
+ if ( bPartOfPaste )
+ if ( auto pWrapper = dynamic_cast<ScUndoWrapper*>( pNextAction) )
+ {
+ SfxUndoAction* pWrappedAction = pWrapper->GetWrappedUndo();
+ if ( dynamic_cast<const ScUndoPaste*>( pWrappedAction) )
+ {
+ // Store paste action if this is part of paste with inserting cells.
+ // A list action isn't used because Repeat wouldn't work (insert wrong cells).
+
+ pPasteUndo.reset( pWrappedAction );
+ pWrapper->ForgetWrappedUndo(); // pWrapper is deleted by UndoManager
+ return true;
+ }
+ }
+
+ // Call base class for detective handling
+ return ScMoveUndo::Merge( pNextAction );
+}
+
+void ScUndoInsertCells::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ pChangeTrack->AppendInsert( aEffRange );
+ nEndChangeAction = pChangeTrack->GetActionMax();
+ }
+ else
+ nEndChangeAction = 0;
+}
+
+void ScUndoInsertCells::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB i;
+
+ if ( bUndo )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nEndChangeAction, nEndChangeAction );
+ }
+ else
+ SetChangeTrack();
+
+ // refresh of merged cells has to be after inserting/deleting
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ switch (eCmd)
+ {
+ case INS_INSROWS_BEFORE:
+ case INS_INSROWS_AFTER:
+ case INS_CELLSDOWN:
+ for( i=0; i<nCount; i++ )
+ {
+
+ if (bUndo)
+ rDoc.DeleteRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Row(), static_cast<SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1));
+ else
+ rDoc.InsertRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Row(), static_cast<SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1));
+
+ if (pViewShell)
+ {
+ const tools::Long nSign = bUndo ? -1 : 1;
+ pViewShell->OnLOKInsertDeleteRow(aEffRange.aStart.Row(), nSign * (aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1));
+ }
+ }
+ break;
+ case INS_INSCOLS_BEFORE:
+ case INS_INSCOLS_AFTER:
+ case INS_CELLSRIGHT:
+ for( i=0; i<nCount; i++ )
+ {
+ if (bUndo)
+ rDoc.DeleteCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Col(), static_cast<SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1));
+ else
+ rDoc.InsertCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Col(), static_cast<SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1));
+
+ if (pViewShell)
+ {
+ const tools::Long nSign = bUndo ? -1 : 1;
+ pViewShell->OnLOKInsertDeleteColumn(aEffRange.aStart.Col(), nSign * (aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1));
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ ScRange aWorkRange( aEffRange );
+ if ( eCmd == INS_CELLSRIGHT ) // only "shift right" requires refresh of the moved area
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol());
+ for( i=0; i<nCount; i++ )
+ {
+ if ( rDoc.HasAttrib( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
+ aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i], HasAttrFlags::Merged ) )
+ {
+ SCCOL nEndCol = aWorkRange.aEnd.Col();
+ SCROW nEndRow = aWorkRange.aEnd.Row();
+ rDoc.ExtendMerge( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), nEndCol, nEndRow, pTabs[i], true );
+ }
+ }
+
+ // Undo for displaced attributes?
+
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+
+ switch (eCmd)
+ {
+ case INS_INSROWS_BEFORE:
+ case INS_INSROWS_AFTER:
+ nPaint |= PaintPartFlags::Left;
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ break;
+ case INS_CELLSDOWN:
+ for( i=0; i<nCount; i++ )
+ {
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ if ( pDocShell->AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i] ))
+ {
+ aWorkRange.aStart.SetCol(0);
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol());
+ nPaint |= PaintPartFlags::Left;
+ }
+ }
+ break;
+ case INS_INSCOLS_BEFORE:
+ case INS_INSCOLS_AFTER:
+ nPaint |= PaintPartFlags::Top; // top bar
+ [[fallthrough]];
+ case INS_CELLSRIGHT:
+ for( i=0; i<nCount; i++ )
+ {
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol()); // to the far right
+ if ( pDocShell->AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i]) )
+ { // AdjustDraw does not paint PaintPartFlags::Top,
+ aWorkRange.aStart.SetCol(0); // thus solved like this
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Left;
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ for( i=0; i<nCount; i++ )
+ {
+ pDocShell->PostPaint( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
+ aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i]+pScenarios[i], nPaint );
+ }
+ pDocShell->PostDataChanged();
+ if (!pViewShell)
+ return;
+
+ pViewShell->CellContentChanged();
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ bool bColsAffected = (eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER || eCmd == INS_CELLSRIGHT);
+ bool bRowsAffected = (eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN);
+
+ if (bColsAffected)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, COLUMN_HEADER, nTab);
+
+ if (bRowsAffected)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, ROW_HEADER, nTab);
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell,
+ bColsAffected, bRowsAffected,
+ true /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+}
+
+void ScUndoInsertCells::Undo()
+{
+ if ( pPasteUndo )
+ pPasteUndo->Undo(); // undo paste first
+
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas in UpdateReference
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (SCTAB i = 0; i < nCount; ++i)
+ rDoc.SetDrawPageSize(pTabs[i]);
+}
+
+void ScUndoInsertCells::Redo()
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas in UpdateReference
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+
+ if ( pPasteUndo )
+ pPasteUndo->Redo(); // redo paste last
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (SCTAB i = 0; i < nCount; ++i)
+ rDoc.SetDrawPageSize(pTabs[i]);
+}
+
+void ScUndoInsertCells::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr)
+ {
+ if ( pPasteUndo )
+ {
+ // Repeat for paste with inserting cells is handled completely
+ // by the Paste undo action
+
+ pPasteUndo->Repeat( rTarget );
+ }
+ else
+ static_cast<ScTabViewTarget&>(rTarget).GetViewShell()->InsertCells( eCmd );
+ }
+}
+
+bool ScUndoInsertCells::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoDeleteCells::ScUndoDeleteCells( ScDocShell* pNewDocShell,
+ const ScRange& rRange,
+ SCTAB nNewCount, std::unique_ptr<SCTAB[]> pNewTabs, std::unique_ptr<SCTAB[]> pNewScenarios,
+ DelCellCmd eNewCmd, ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData ) :
+ ScMoveUndo( pNewDocShell, std::move(pUndoDocument), std::move(pRefData) ),
+ aEffRange( rRange ),
+ nCount( nNewCount ),
+ pTabs( std::move(pNewTabs) ),
+ pScenarios( std::move(pNewScenarios) ),
+ eCmd( eNewCmd )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (eCmd == DelCellCmd::Rows) // whole row?
+ {
+ aEffRange.aStart.SetCol(0);
+ aEffRange.aEnd.SetCol(rDoc.MaxCol());
+ }
+
+ if (eCmd == DelCellCmd::Cols) // whole column?
+ {
+ aEffRange.aStart.SetRow(0);
+ aEffRange.aEnd.SetRow(rDoc.MaxRow());
+ }
+
+ SetChangeTrack();
+}
+
+ScUndoDeleteCells::~ScUndoDeleteCells()
+{
+}
+
+OUString ScUndoDeleteCells::GetComment() const
+{
+ return ScResId( STR_UNDO_DELETECELLS ); // "Delete"
+}
+
+void ScUndoDeleteCells::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->AppendDeleteRange( aEffRange, pRefUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction );
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoDeleteCells::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB i;
+
+ if ( bUndo )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+ }
+ else
+ SetChangeTrack();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ switch (eCmd)
+ {
+ case DelCellCmd::Rows:
+ case DelCellCmd::CellsUp:
+ for( i=0; i<nCount; i++ )
+ {
+ if (bUndo)
+ rDoc.InsertRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Row(), static_cast<SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1));
+ else
+ rDoc.DeleteRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Row(), static_cast<SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1));
+
+ if (pViewShell)
+ {
+ const tools::Long nSign = bUndo ? 1 : -1;
+ pViewShell->OnLOKInsertDeleteRow(aEffRange.aStart.Row(), nSign * (aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1));
+ }
+ }
+ break;
+ case DelCellCmd::Cols:
+ case DelCellCmd::CellsLeft:
+ for( i=0; i<nCount; i++ )
+ {
+ if (bUndo)
+ rDoc.InsertCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Col(), static_cast<SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1));
+ else
+ rDoc.DeleteCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
+ aEffRange.aStart.Col(), static_cast<SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1));
+
+ if (pViewShell)
+ {
+ const tools::Long nSign = bUndo ? 1 : -1;
+ pViewShell->OnLOKInsertDeleteColumn(aEffRange.aStart.Col(), nSign * (aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1));
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // if Undo, restore references
+ for( i=0; i<nCount && bUndo; i++ )
+ {
+ pRefUndoDoc->CopyToDocument(aEffRange.aStart.Col(), aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Col(), aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
+ InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, rDoc);
+ }
+
+ ScRange aWorkRange( aEffRange );
+ if ( eCmd == DelCellCmd::CellsLeft ) // only "shift left" requires refresh of the moved area
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol());
+
+ for( i=0; i<nCount; i++ )
+ {
+ if ( rDoc.HasAttrib( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
+ aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i], HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ // #i51445# old merge flag attributes must be deleted also for single cells,
+ // not only for whole columns/rows
+
+ if ( !bUndo )
+ {
+ if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol());
+ if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+ aMarkData.SelectOneTable( aWorkRange.aStart.Tab() );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr() );
+ rDoc.ApplyPatternArea( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(),
+ aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(),
+ aMarkData, aPattern );
+ }
+
+ SCCOL nEndCol = aWorkRange.aEnd.Col();
+ SCROW nEndRow = aWorkRange.aEnd.Row();
+ rDoc.ExtendMerge( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), nEndCol, nEndRow, pTabs[i], true );
+ }
+ }
+
+ // paint
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ switch (eCmd)
+ {
+ case DelCellCmd::Rows:
+ nPaint |= PaintPartFlags::Left;
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ break;
+ case DelCellCmd::CellsUp:
+ for( i=0; i<nCount; i++ )
+ {
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ if ( pDocShell->AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i] ))
+ {
+ aWorkRange.aStart.SetCol(0);
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol());
+ nPaint |= PaintPartFlags::Left;
+ }
+ }
+ break;
+ case DelCellCmd::Cols:
+ nPaint |= PaintPartFlags::Top; // top bar
+ [[fallthrough]];
+ case DelCellCmd::CellsLeft:
+ for( i=0; i<nCount; i++ )
+ {
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol()); // to the far right
+ if ( pDocShell->AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i] ) )
+ {
+ aWorkRange.aStart.SetCol(0);
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Left;
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ for( i=0; i<nCount; i++ )
+ {
+ pDocShell->PostPaint( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
+ aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i]+pScenarios[i], nPaint, SC_PF_LINES );
+ }
+ // Selection not until EndUndo
+
+ pDocShell->PostDataChanged();
+ // CellContentChanged comes with the selection
+
+ if (!pViewShell)
+ return;
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ bool bColsAffected = (eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::CellsLeft);
+ bool bRowsAffected = (eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp);
+
+ if (bColsAffected)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, COLUMN_HEADER, nTab);
+
+ if (bRowsAffected)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, ROW_HEADER, nTab);
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell,
+ bColsAffected, bRowsAffected,
+ true /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+
+}
+
+void ScUndoDeleteCells::Undo()
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ // Now that DBData have been restored in ScMoveUndo::EndUndo() via its
+ // pRefUndoDoc we can apply the AutoFilter buttons.
+ // Add one row for cases undoing deletion right above a cut AutoFilter
+ // range so the buttons are removed.
+ SCROW nRefreshEndRow = std::min<SCROW>( aEffRange.aEnd.Row() + 1, rDoc.MaxRow());
+ for (SCTAB i=0; i < nCount; ++i)
+ {
+ rDoc.RefreshAutoFilter( aEffRange.aStart.Col(), aEffRange.aStart.Row(),
+ aEffRange.aEnd.Col(), nRefreshEndRow, pTabs[i]);
+ }
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ // Selection not until EndUndo
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ for( SCTAB i=0; i<nCount; i++ )
+ {
+ pViewShell->MarkRange( ScRange(aEffRange.aStart.Col(), aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Col(), aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i]) );
+ }
+ }
+
+ for (SCTAB i = 0; i < nCount; ++i)
+ rDoc.SetDrawPageSize(pTabs[i]);
+}
+
+void ScUndoDeleteCells::Redo()
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
+ BeginRedo();
+ DoChange( false);
+ EndRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ for (SCTAB i=0; i < nCount; ++i)
+ {
+ rDoc.RefreshAutoFilter( aEffRange.aStart.Col(), aEffRange.aStart.Row(),
+ aEffRange.aEnd.Col(), aEffRange.aEnd.Row(), pTabs[i]);
+ }
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->DoneBlockMode(); // current way
+
+ for (SCTAB i = 0; i < nCount; ++i)
+ rDoc.SetDrawPageSize(pTabs[i]);
+}
+
+void ScUndoDeleteCells::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget) )
+ pViewTarget->GetViewShell()->DeleteCells( eCmd );
+}
+
+bool ScUndoDeleteCells::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+// delete cells in multiselection
+ScUndoDeleteMulti::ScUndoDeleteMulti(
+ ScDocShell* pNewDocShell,
+ bool bNewRows, bool bNeedsRefresh, SCTAB nNewTab,
+ std::vector<sc::ColRowSpan>&& rSpans,
+ ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData ) :
+ ScMoveUndo( pNewDocShell, std::move(pUndoDocument), std::move(pRefData) ),
+ mbRows(bNewRows),
+ mbRefresh(bNeedsRefresh),
+ nTab( nNewTab ),
+ maSpans(std::move(rSpans))
+{
+ SetChangeTrack();
+}
+
+ScUndoDeleteMulti::~ScUndoDeleteMulti()
+{
+}
+
+OUString ScUndoDeleteMulti::GetComment() const
+{
+ return ScResId( STR_UNDO_DELETECELLS ); // like DeleteCells
+}
+
+void ScUndoDeleteMulti::DoChange() const
+{
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ PaintPartFlags nPaint;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (mbRows)
+ {
+ nStartCol = 0;
+ nStartRow = static_cast<SCROW>(maSpans[0].mnStart);
+ nPaint = PaintPartFlags::Grid | PaintPartFlags::Left;
+ }
+ else
+ {
+ nStartCol = static_cast<SCCOL>(maSpans[0].mnStart);
+ nStartRow = 0;
+ nPaint = PaintPartFlags::Grid | PaintPartFlags::Top;
+ }
+
+ if (mbRefresh)
+ {
+ SCCOL nEndCol = rDoc.MaxCol();
+ SCROW nEndRow = rDoc.MaxRow();
+ rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
+ rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
+ }
+
+ pDocShell->PostPaint( nStartCol, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, nPaint );
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ ShowTable( nTab );
+}
+
+void ScUndoDeleteMulti::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nStartChangeAction = pChangeTrack->GetActionMax() + 1;
+ ScRange aRange( 0, 0, nTab, 0, 0, nTab );
+ if (mbRows)
+ aRange.aEnd.SetCol( rDoc.MaxCol() );
+ else
+ aRange.aEnd.SetRow( rDoc.MaxRow() );
+ // delete in reverse
+ std::vector<sc::ColRowSpan>::const_reverse_iterator ri = maSpans.rbegin(), riEnd = maSpans.rend();
+ for (; ri != riEnd; ++ri)
+ {
+ SCCOLROW nEnd = ri->mnEnd;
+ SCCOLROW nStart = ri->mnStart;
+ if (mbRows)
+ {
+ aRange.aStart.SetRow( nStart );
+ aRange.aEnd.SetRow( nEnd );
+ }
+ else
+ {
+ aRange.aStart.SetCol( static_cast<SCCOL>(nStart) );
+ aRange.aEnd.SetCol( static_cast<SCCOL>(nEnd) );
+ }
+ sal_uLong nDummyStart;
+ pChangeTrack->AppendDeleteRange( aRange, pRefUndoDoc.get(),
+ nDummyStart, nEndChangeAction );
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoDeleteMulti::Undo()
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ // reverse delete -> forward insert
+ for (const auto& rSpan : maSpans)
+ {
+ SCCOLROW nStart = rSpan.mnStart;
+ SCCOLROW nEnd = rSpan.mnEnd;
+ if (mbRows)
+ rDoc.InsertRow( 0,nTab, rDoc.MaxCol(),nTab, nStart,static_cast<SCSIZE>(nEnd-nStart+1) );
+ else
+ rDoc.InsertCol( 0,nTab, rDoc.MaxRow(),nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd-nStart+1) );
+ }
+
+ for (const auto& rSpan : maSpans)
+ {
+ SCCOLROW nStart = rSpan.mnStart;
+ SCCOLROW nEnd = rSpan.mnEnd;
+ if (mbRows)
+ pRefUndoDoc->CopyToDocument(0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::ALL, false, rDoc);
+ else
+ pRefUndoDoc->CopyToDocument(static_cast<SCCOL>(nStart),0,nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL, false, rDoc);
+ }
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ DoChange();
+
+ //! redrawing the selection is not possible at the moment
+ //! since no data for selection exist
+
+ EndUndo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoDeleteMulti::Redo()
+{
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ // reverse delete
+ std::vector<sc::ColRowSpan>::const_reverse_iterator ri = maSpans.rbegin(), riEnd = maSpans.rend();
+ for (; ri != riEnd; ++ri)
+ {
+ SCCOLROW nEnd = ri->mnEnd;
+ SCCOLROW nStart = ri->mnStart;
+ if (mbRows)
+ rDoc.DeleteRow( 0,nTab, rDoc.MaxCol(),nTab, nStart,static_cast<SCSIZE>(nEnd-nStart+1) );
+ else
+ rDoc.DeleteCol( 0,nTab, rDoc.MaxRow(),nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd-nStart+1) );
+ }
+
+ SetChangeTrack();
+
+ DoChange();
+
+ EndRedo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoDeleteMulti::Repeat(SfxRepeatTarget& rTarget)
+{
+ // if single selection
+ if (auto pTabViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pTabViewTarget->GetViewShell()->DeleteCells( DelCellCmd::Rows );
+}
+
+bool ScUndoDeleteMulti::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoCut::ScUndoCut(ScDocShell* pNewDocShell, const ScRange& aRange, const ScAddress& aOldEnd,
+ const ScMarkData& rMark, ScDocumentUniquePtr pNewUndoDoc)
+ : ScBlockUndo(pNewDocShell, ScRange(aRange.aStart, aOldEnd), SC_UNDO_AUTOHEIGHT)
+ , aMarkData(rMark)
+ , pUndoDoc(std::move(pNewUndoDoc))
+ , aExtendedRange(aRange)
+{
+ SetChangeTrack();
+}
+
+ScUndoCut::~ScUndoCut()
+{
+}
+
+OUString ScUndoCut::GetComment() const
+{
+ return ScResId( STR_UNDO_CUT ); // "cut"
+}
+
+void ScUndoCut::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->AppendContentRange( aBlockRange, pUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction, SC_CACM_CUT );
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoCut::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sal_uInt16 nExtFlags = 0;
+
+ // do not undo/redo objects and note captions, they are handled via drawing undo
+ InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
+
+ if (bUndo) // only for Undo
+ {
+ // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScRange aCopyRange = aExtendedRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pUndoDoc->CopyToDocument(aCopyRange, nUndoFlags, false, rDoc);
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ BroadcastChanges(aCopyRange);
+ }
+ else // only for Redo
+ {
+ pDocShell->UpdatePaintExt( nExtFlags, aExtendedRange );
+ rDoc.DeleteArea( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
+ aBlockRange.aEnd.Col(), aBlockRange.aEnd.Row(), aMarkData, nUndoFlags );
+ SetChangeTrack();
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( !( pViewShell && pViewShell->AdjustBlockHeight() ) )
+/*A*/ pDocShell->PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
+
+ if ( !bUndo ) // draw redo after updating row heights
+ RedoSdrUndoAction( pDrawUndo.get() ); //! include in ScBlockUndo?
+
+ pDocShell->PostDataChanged();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+void ScUndoCut::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoCut::Redo()
+{
+ BeginRedo();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
+ DoChange( false );
+ EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
+ EndRedo();
+}
+
+void ScUndoCut::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->CutToClip();
+}
+
+bool ScUndoCut::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoPaste::ScUndoPaste( ScDocShell* pNewDocShell, const ScRangeList& rRanges,
+ const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
+ InsertDeleteFlags nNewFlags,
+ std::unique_ptr<ScRefUndoData> pRefData,
+ bool bRedoIsFilled, const ScUndoPasteOptions* pOptions ) :
+ ScMultiBlockUndo( pNewDocShell, rRanges ),
+ aMarkData( rMark ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ pRedoDoc( std::move(pNewRedoDoc) ),
+ nFlags( nNewFlags ),
+ pRefUndoData( std::move(pRefData) ),
+ bRedoFilled( bRedoIsFilled )
+{
+ if ( pRefUndoData )
+ pRefUndoData->DeleteUnchanged( &pDocShell->GetDocument() );
+
+ if ( pOptions )
+ aPasteOptions = *pOptions; // used only for Repeat
+
+ SetChangeTrack();
+}
+
+ScUndoPaste::~ScUndoPaste()
+{
+ pUndoDoc.reset();
+ pRedoDoc.reset();
+ pRefUndoData.reset();
+ pRefRedoData.reset();
+}
+
+OUString ScUndoPaste::GetComment() const
+{
+ return ScResId( STR_UNDO_PASTE ); // "paste"
+}
+
+void ScUndoPaste::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack && (nFlags & InsertDeleteFlags::CONTENTS) )
+ {
+ for (size_t i = 0, n = maBlockRanges.size(); i < n; ++i)
+ {
+ pChangeTrack->AppendContentRange(maBlockRanges[i], pUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction, SC_CACM_PASTE );
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoPaste::DoChange(bool bUndo)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ // RefUndoData for redo is created before first undo
+ // (with DeleteUnchanged after the DoUndo call)
+ bool bCreateRedoData = ( bUndo && pRefUndoData && !pRefRedoData );
+ if ( bCreateRedoData )
+ pRefRedoData.reset( new ScRefUndoData( &rDoc ) );
+
+ ScRefUndoData* pWorkRefData = bUndo ? pRefUndoData.get() : pRefRedoData.get();
+
+ // Always back-up either all or none of the content for Undo
+ InsertDeleteFlags nUndoFlags = InsertDeleteFlags::NONE;
+ if (nFlags & InsertDeleteFlags::CONTENTS)
+ nUndoFlags |= InsertDeleteFlags::CONTENTS;
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ nUndoFlags |= InsertDeleteFlags::ATTRIB;
+
+ // do not undo/redo objects and note captions, they are handled via drawing undo
+ nUndoFlags &= ~InsertDeleteFlags::OBJECTS;
+ nUndoFlags |= InsertDeleteFlags::NOCAPTIONS;
+
+ bool bPaintAll = false;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( bUndo && !bRedoFilled )
+ {
+ if (!pRedoDoc)
+ {
+ bool bColInfo = true;
+ bool bRowInfo = true;
+ for (size_t i = 0, n = maBlockRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = maBlockRanges[i];
+ bColInfo &= (r.aStart.Row() == 0 && r.aEnd.Row() == rDoc.MaxRow());
+ bRowInfo &= (r.aStart.Col() == 0 && r.aEnd.Col() == rDoc.MaxCol());
+ if (!bColInfo && !bRowInfo)
+ break;
+ }
+
+ pRedoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ pRedoDoc->InitUndoSelected( rDoc, aMarkData, bColInfo, bRowInfo );
+ }
+ // read "redo" data from the document in the first undo
+ // all sheets - CopyToDocument skips those that don't exist in pRedoDoc
+ for (size_t i = 0, n = maBlockRanges.size(); i < n; ++i)
+ {
+ ScRange aCopyRange = maBlockRanges[i];
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument(aCopyRange, nUndoFlags, false, *pRedoDoc);
+ bRedoFilled = true;
+ }
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt(nExtFlags, maBlockRanges.Combine());
+
+ rDoc.ForgetNoteCaptions(maBlockRanges, false);
+ aMarkData.MarkToMulti();
+ rDoc.DeleteSelection(nUndoFlags, aMarkData, false); // no broadcasting here
+ for (size_t i = 0, n = maBlockRanges.size(); i < n; ++i)
+ rDoc.BroadcastCells(maBlockRanges[i], SfxHintId::ScDataChanged);
+
+ aMarkData.MarkToSimple();
+
+ SCTAB nFirstSelected = aMarkData.GetFirstSelected();
+
+ if ( !bUndo && pRedoDoc ) // Redo: UndoToDocument before handling RefData
+ {
+ for (size_t i = 0, n = maBlockRanges.size(); i < n; ++i)
+ {
+ ScRange aRange = maBlockRanges[i];
+ aRange.aStart.SetTab(nFirstSelected);
+ aRange.aEnd.SetTab(nFirstSelected);
+ pRedoDoc->UndoToDocument(aRange, nUndoFlags, false, rDoc);
+ for (const auto& rTab : aMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (rTab == nFirstSelected)
+ continue;
+
+ aRange.aStart.SetTab(rTab);
+ aRange.aEnd.SetTab(rTab);
+ pRedoDoc->CopyToDocument(aRange, nUndoFlags, false, rDoc);
+ }
+ }
+ }
+
+ if (pWorkRefData)
+ {
+ pWorkRefData->DoUndo( &rDoc, true ); // true = bSetChartRangeLists for SetChartListenerCollection
+ if (!maBlockRanges.empty() &&
+ rDoc.RefreshAutoFilter(0, 0, rDoc.MaxCol(), rDoc.MaxRow(), maBlockRanges[0].aStart.Tab()))
+ bPaintAll = true;
+ }
+
+ if ( bCreateRedoData && pRefRedoData )
+ pRefRedoData->DeleteUnchanged( &rDoc );
+
+ if (bUndo) // Undo: UndoToDocument after handling RefData
+ {
+ for (size_t i = 0, n = maBlockRanges.size(); i < n; ++i)
+ {
+ ScRange aRange = maBlockRanges[i];
+ for (const auto& rTab : aMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+ aRange.aStart.SetTab(rTab);
+ aRange.aEnd.SetTab(rTab);
+ pUndoDoc->UndoToDocument(aRange, nUndoFlags, false, rDoc);
+ }
+ }
+ }
+
+ if ( bUndo )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+ }
+ else
+ SetChangeTrack();
+
+ ScRangeList aDrawRanges(maBlockRanges);
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+
+ // For sheet geometry invalidation.
+ bool bColsAffected = false;
+ bool bRowsAffected = false;
+
+ for (size_t i = 0, n = aDrawRanges.size(); i < n; ++i)
+ {
+ ScRange& rDrawRange = aDrawRanges[i];
+ rDoc.ExtendMerge(rDrawRange, true); // only needed for single sheet (text/rtf etc.)
+ ScRangeList aRangeList(rDrawRange);
+ ScMarkData aData(rDoc.GetSheetLimits(), aRangeList);
+ if (bPaintAll)
+ {
+ rDrawRange.aStart.SetCol(0);
+ rDrawRange.aStart.SetRow(0);
+ rDrawRange.aEnd.SetCol(rDoc.MaxCol());
+ rDrawRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Top | PaintPartFlags::Left;
+ if (pViewShell)
+ pViewShell->AdjustBlockHeight(false, &aData);
+ }
+ else
+ {
+ if (maBlockRanges[i].aStart.Row() == 0 && maBlockRanges[i].aEnd.Row() == rDoc.MaxRow()) // whole column
+ {
+ nPaint |= PaintPartFlags::Top;
+ rDrawRange.aEnd.SetCol(rDoc.MaxCol());
+ bColsAffected = true;
+ }
+ if (maBlockRanges[i].aStart.Col() == 0 && maBlockRanges[i].aEnd.Col() == rDoc.MaxCol()) // whole row
+ {
+ nPaint |= PaintPartFlags::Left;
+ rDrawRange.aEnd.SetRow(rDoc.MaxRow());
+ bRowsAffected = true;
+ }
+ if (pViewShell && pViewShell->AdjustBlockHeight(false, &aData))
+ {
+ rDrawRange.aStart.SetCol(0);
+ rDrawRange.aStart.SetRow(0);
+ rDrawRange.aEnd.SetCol(rDoc.MaxCol());
+ rDrawRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Left;
+ }
+ pDocShell->UpdatePaintExt(nExtFlags, rDrawRange);
+ }
+ }
+
+ if ( !bUndo ) // draw redo after updating row heights
+ RedoSdrUndoAction(mpDrawUndo.get());
+
+ pDocShell->PostPaint(aDrawRanges, nPaint, nExtFlags);
+
+ pDocShell->PostDataChanged();
+ if (!pViewShell)
+ return;
+
+ pViewShell->CellContentChanged();
+
+ if (bColsAffected || bRowsAffected)
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell,
+ bColsAffected, bRowsAffected,
+ true /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, aDrawRanges[0].aStart.Tab());
+}
+
+void ScUndoPaste::Undo()
+{
+ BeginUndo();
+ DoChange(true);
+ if (!maBlockRanges.empty())
+ ShowTable(maBlockRanges.front());
+ EndUndo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoPaste::Redo()
+{
+ BeginRedo();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
+ DoChange( false );
+ EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
+ EndRedo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoPaste::Repeat(SfxRepeatTarget& rTarget)
+{
+ auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget);
+ if (!pViewTarget)
+ return;
+
+ ScTabViewShell* pViewSh = pViewTarget->GetViewShell();
+ // keep a reference in case the clipboard is changed during PasteFromClip
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pViewSh->GetViewData().GetActiveWin()));
+ if (pOwnClip)
+ {
+ pViewSh->PasteFromClip( nFlags, pOwnClip->GetDocument(),
+ aPasteOptions.nFunction, aPasteOptions.bSkipEmptyCells, aPasteOptions.bTranspose,
+ aPasteOptions.bAsLink, aPasteOptions.eMoveMode, InsertDeleteFlags::NONE,
+ true ); // allow warning dialog
+ }
+}
+
+bool ScUndoPaste::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoDragDrop::ScUndoDragDrop( ScDocShell* pNewDocShell,
+ const ScRange& rRange, const ScAddress& aNewDestPos, bool bNewCut,
+ ScDocumentUniquePtr pUndoDocument, bool bScenario ) :
+ ScMoveUndo( pNewDocShell, std::move(pUndoDocument), nullptr ),
+ mnPaintExtFlags( 0 ),
+ aSrcRange( rRange ),
+ bCut( bNewCut ),
+ bKeepScenarioFlags( bScenario )
+{
+ ScAddress aDestEnd(aNewDestPos);
+ aDestEnd.IncRow(aSrcRange.aEnd.Row() - aSrcRange.aStart.Row());
+ aDestEnd.IncCol(aSrcRange.aEnd.Col() - aSrcRange.aStart.Col());
+ aDestEnd.IncTab(aSrcRange.aEnd.Tab() - aSrcRange.aStart.Tab());
+
+ bool bIncludeFiltered = bCut;
+ if ( !bIncludeFiltered )
+ {
+ // find number of non-filtered rows
+ SCROW nPastedCount = pDocShell->GetDocument().CountNonFilteredRows(
+ aSrcRange.aStart.Row(), aSrcRange.aEnd.Row(), aSrcRange.aStart.Tab());
+
+ if ( nPastedCount == 0 )
+ nPastedCount = 1;
+ aDestEnd.SetRow( aNewDestPos.Row() + nPastedCount - 1 );
+ }
+
+ aDestRange.aStart = aNewDestPos;
+ aDestRange.aEnd = aDestEnd;
+
+ SetChangeTrack();
+}
+
+ScUndoDragDrop::~ScUndoDragDrop()
+{
+}
+
+OUString ScUndoDragDrop::GetComment() const
+{ // "Move" : "Copy"
+ return bCut ?
+ ScResId( STR_UNDO_MOVE ) :
+ ScResId( STR_UNDO_COPY );
+}
+
+void ScUndoDragDrop::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ if ( bCut )
+ {
+ nStartChangeAction = pChangeTrack->GetActionMax() + 1;
+ pChangeTrack->AppendMove( aSrcRange, aDestRange, pRefUndoDoc.get() );
+ nEndChangeAction = pChangeTrack->GetActionMax();
+ }
+ else
+ pChangeTrack->AppendContentRange( aDestRange, pRefUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction );
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoDragDrop::PaintArea( ScRange aRange, sal_uInt16 nExtFlags ) const
+{
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ if (pViewShell)
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ ScViewData& rViewData = pViewShell->GetViewData();
+ sc::RowHeightContext aCxt(
+ rDoc.MaxRow(), rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomX(), rViewData.GetZoomY(),
+ pVirtDev);
+
+ if (rDoc.SetOptimalHeight(aCxt, aRange.aStart.Row(), aRange.aEnd.Row(), aRange.aStart.Tab(), true))
+ {
+ // tdf#76183: recalculate objects' positions
+ rDoc.SetDrawPageSize(aRange.aStart.Tab());
+ aRange.aStart.SetCol(0);
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Left;
+ }
+ }
+
+ if ( bKeepScenarioFlags )
+ {
+ // Copy scenario -> also paint scenario boarder
+ aRange.aStart.SetCol(0);
+ aRange.aStart.SetRow(0);
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ }
+
+ // column/row info (width/height) included if whole columns/rows were copied
+ if ( aSrcRange.aStart.Col() == 0 && aSrcRange.aEnd.Col() == rDoc.MaxCol() )
+ {
+ nPaint |= PaintPartFlags::Left;
+ aRange.aEnd.SetRow(rDoc.MaxRow());
+ }
+ if ( aSrcRange.aStart.Row() == 0 && aSrcRange.aEnd.Row() == rDoc.MaxRow() )
+ {
+ nPaint |= PaintPartFlags::Top;
+ aRange.aEnd.SetCol(rDoc.MaxCol());
+ }
+
+ pDocShell->PostPaint( aRange, nPaint, nExtFlags );
+}
+
+void ScUndoDragDrop::DoUndo( ScRange aRange )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ // Database range before data, so that the Autofilter button match up in ExtendMerge
+
+ ScRange aPaintRange = aRange;
+ rDoc.ExtendMerge( aPaintRange ); // before deleting
+
+ pDocShell->UpdatePaintExt(mnPaintExtFlags, aPaintRange);
+
+ // do not undo objects and note captions, they are handled via drawing undo
+ InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
+
+ // Additionally discard/forget caption ownership during deletion, as
+ // Drag&Drop is a special case in that the Undo holds captions of the
+ // transferred target range, which would get deleted and
+ // SdrGroupUndo::Undo() would attempt to access invalidated captions and
+ // crash, tdf#92995
+ InsertDeleteFlags nDelFlags = nUndoFlags | InsertDeleteFlags::FORGETCAPTIONS;
+
+ rDoc.DeleteAreaTab( aRange, nDelFlags );
+ pRefUndoDoc->CopyToDocument(aRange, nUndoFlags, false, rDoc);
+ if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) )
+ rDoc.ExtendMerge( aRange, true );
+
+ aPaintRange.aEnd.SetCol( std::max( aPaintRange.aEnd.Col(), aRange.aEnd.Col() ) );
+ aPaintRange.aEnd.SetRow( std::max( aPaintRange.aEnd.Row(), aRange.aEnd.Row() ) );
+
+ pDocShell->UpdatePaintExt(mnPaintExtFlags, aPaintRange);
+ maPaintRanges.Join(aPaintRange);
+}
+
+void ScUndoDragDrop::Undo()
+{
+ mnPaintExtFlags = 0;
+ maPaintRanges.RemoveAll();
+
+ BeginUndo();
+
+ if (bCut)
+ {
+ // During undo, we move cells from aDestRange to aSrcRange.
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SCCOL nColDelta = aSrcRange.aStart.Col() - aDestRange.aStart.Col();
+ SCROW nRowDelta = aSrcRange.aStart.Row() - aDestRange.aStart.Row();
+ SCTAB nTabDelta = aSrcRange.aStart.Tab() - aDestRange.aStart.Tab();
+
+ sc::RefUpdateContext aCxt(rDoc);
+ aCxt.meMode = URM_MOVE;
+ aCxt.maRange = aSrcRange;
+ aCxt.mnColDelta = nColDelta;
+ aCxt.mnRowDelta = nRowDelta;
+ aCxt.mnTabDelta = nTabDelta;
+
+ // Global range names.
+ ScRangeName* pName = rDoc.GetRangeName();
+ if (pName)
+ pName->UpdateReference(aCxt);
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ // Sheet-local range names.
+ pName = rDoc.GetRangeName(nTab);
+ if (pName)
+ pName->UpdateReference(aCxt, nTab);
+ }
+
+ ScValidationDataList* pValidList = rDoc.GetValidationList();
+ if (pValidList)
+ {
+ // Update the references of validation entries.
+ pValidList->UpdateReference(aCxt);
+ }
+
+ DoUndo(aDestRange);
+ DoUndo(aSrcRange);
+
+ rDoc.BroadcastCells(aSrcRange, SfxHintId::ScDataChanged, false);
+ }
+ else
+ DoUndo(aDestRange);
+
+ for (size_t i = 0; i < maPaintRanges.size(); ++i)
+ {
+ const ScRange& r = maPaintRanges[i];
+ PaintArea(r, mnPaintExtFlags);
+ }
+
+ EndUndo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoDragDrop::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+
+ EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
+
+ // do not undo/redo objects and note captions, they are handled via drawing undo
+ constexpr InsertDeleteFlags nRedoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
+
+ /* TODO: Redoing note captions is quite tricky due to the fact that a
+ helper clip document is used. While (re-)pasting the contents to the
+ destination area, the original pointers to the captions created while
+ dropping have to be restored. A simple CopyFromClip() would create new
+ caption objects that are not tracked by drawing undo, and the captions
+ restored by drawing redo would live without cell note objects pointing
+ to them. So, first, CopyToClip() and CopyFromClip() are called without
+ cloning the caption objects. This leads to cell notes pointing to the
+ wrong captions from source area that will be removed by drawing redo
+ later. Second, the pointers to the new captions have to be restored.
+ Sadly, currently these pointers are not stored anywhere but in the list
+ of drawing undo actions. */
+
+ SCTAB nTab;
+ ScMarkData aSourceMark(rDoc.GetSheetLimits());
+ for (nTab=aSrcRange.aStart.Tab(); nTab<=aSrcRange.aEnd.Tab(); nTab++)
+ aSourceMark.SelectTable( nTab, true );
+
+ // do not clone objects and note captions into clipdoc (see above)
+ // but at least copy notes
+ ScClipParam aClipParam(aSrcRange, bCut);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bKeepScenarioFlags, false);
+
+ if (bCut)
+ {
+ ScRange aSrcPaintRange = aSrcRange;
+ rDoc.ExtendMerge( aSrcPaintRange ); // before deleting
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt( nExtFlags, aSrcPaintRange );
+ rDoc.DeleteAreaTab( aSrcRange, nRedoFlags );
+ PaintArea( aSrcPaintRange, nExtFlags );
+ }
+
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ for (nTab=aDestRange.aStart.Tab(); nTab<=aDestRange.aEnd.Tab(); nTab++)
+ aDestMark.SelectTable( nTab, true );
+
+ bool bIncludeFiltered = bCut;
+ // TODO: restore old note captions instead of cloning new captions...
+ rDoc.CopyFromClip( aDestRange, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, nullptr, pClipDoc.get(), true, false, bIncludeFiltered );
+
+ if (bCut)
+ for (nTab=aSrcRange.aStart.Tab(); nTab<=aSrcRange.aEnd.Tab(); nTab++)
+ rDoc.RefreshAutoFilter( aSrcRange.aStart.Col(), aSrcRange.aStart.Row(),
+ aSrcRange.aEnd.Col(), aSrcRange.aEnd.Row(), nTab );
+
+ // skipped rows and merged cells don't mix
+ if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
+ pDocShell->GetDocFunc().UnmergeCells( aDestRange, false, nullptr );
+
+ for (nTab=aDestRange.aStart.Tab(); nTab<=aDestRange.aEnd.Tab(); nTab++)
+ {
+ SCCOL nEndCol = aDestRange.aEnd.Col();
+ SCROW nEndRow = aDestRange.aEnd.Row();
+ rDoc.ExtendMerge( aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ nEndCol, nEndRow, nTab, true );
+ PaintArea( ScRange( aDestRange.aStart.Col(), aDestRange.aStart.Row(), nTab,
+ nEndCol, nEndRow, nTab ), 0 );
+ }
+
+ SetChangeTrack();
+
+ pClipDoc.reset();
+ ShowTable( aDestRange.aStart.Tab() );
+
+ RedoSdrUndoAction( pDrawUndo.get() ); //! include in ScBlockUndo?
+ EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
+
+ EndRedo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoDragDrop::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoDragDrop::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // not possible
+}
+
+// Insert list containing range names
+// (Insert|Name|Insert =>[List])
+ScUndoListNames::ScUndoListNames(ScDocShell* pNewDocShell, const ScRange& rRange,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc)
+ : ScBlockUndo(pNewDocShell, rRange, SC_UNDO_AUTOHEIGHT)
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , xRedoDoc(std::move(pNewRedoDoc))
+{
+}
+
+OUString ScUndoListNames::GetComment() const
+{
+ return ScResId( STR_UNDO_LISTNAMES );
+}
+
+void ScUndoListNames::DoChange( ScDocument* pSrcDoc ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.DeleteAreaTab( aBlockRange, InsertDeleteFlags::ALL );
+ pSrcDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ALL, false, rDoc);
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid );
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+void ScUndoListNames::Undo()
+{
+ BeginUndo();
+ DoChange(xUndoDoc.get());
+ EndUndo();
+}
+
+void ScUndoListNames::Redo()
+{
+ BeginRedo();
+ DoChange(xRedoDoc.get());
+ EndRedo();
+}
+
+void ScUndoListNames::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pTabViewTarget = dynamic_cast<ScTabViewTarget*>(&rTarget))
+ pTabViewTarget->GetViewShell()->InsertNameList();
+}
+
+bool ScUndoListNames::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoConditionalFormat::ScUndoConditionalFormat(ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pUndoDoc, ScDocumentUniquePtr pRedoDoc, const ScRange& rRange):
+ ScSimpleUndo( pNewDocShell ),
+ mpUndoDoc(std::move(pUndoDoc)),
+ mpRedoDoc(std::move(pRedoDoc)),
+ maRange(rRange)
+{
+}
+
+ScUndoConditionalFormat::~ScUndoConditionalFormat()
+{
+}
+
+OUString ScUndoConditionalFormat::GetComment() const
+{
+ return ScResId( STR_UNDO_CONDFORMAT );
+}
+
+void ScUndoConditionalFormat::Undo()
+{
+ DoChange(mpUndoDoc.get());
+}
+
+void ScUndoConditionalFormat::Redo()
+{
+ DoChange(mpRedoDoc.get());
+}
+
+void ScUndoConditionalFormat::DoChange(ScDocument* pSrcDoc)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.DeleteAreaTab( maRange, InsertDeleteFlags::ALL );
+ pSrcDoc->CopyToDocument(maRange, InsertDeleteFlags::ALL, false, rDoc);
+ pDocShell->PostPaint( maRange, PaintPartFlags::Grid );
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+void ScUndoConditionalFormat::Repeat(SfxRepeatTarget& )
+{
+}
+
+bool ScUndoConditionalFormat::CanRepeat(SfxRepeatTarget& ) const
+{
+ return false;
+}
+
+ScUndoConditionalFormatList::ScUndoConditionalFormatList(ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pUndoDoc, ScDocumentUniquePtr pRedoDoc, SCTAB nTab):
+ ScSimpleUndo( pNewDocShell ),
+ mpUndoDoc(std::move(pUndoDoc)),
+ mpRedoDoc(std::move(pRedoDoc)),
+ mnTab(nTab)
+{
+}
+
+ScUndoConditionalFormatList::~ScUndoConditionalFormatList()
+{
+}
+
+OUString ScUndoConditionalFormatList::GetComment() const
+{
+ return ScResId( STR_UNDO_CONDFORMAT_LIST );
+}
+
+void ScUndoConditionalFormatList::Undo()
+{
+ DoChange(mpUndoDoc.get());
+}
+
+void ScUndoConditionalFormatList::Redo()
+{
+ DoChange(mpRedoDoc.get());
+}
+
+void ScUndoConditionalFormatList::DoChange(const ScDocument* pSrcDoc)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ if (pSrcDoc == mpUndoDoc.get())
+ {
+ mpRedoDoc->GetCondFormList(mnTab)->RemoveFromDocument(rDoc);
+ mpUndoDoc->GetCondFormList(mnTab)->AddToDocument(rDoc);
+ }
+ else
+ {
+ mpUndoDoc->GetCondFormList(mnTab)->RemoveFromDocument(rDoc);
+ mpRedoDoc->GetCondFormList(mnTab)->AddToDocument(rDoc);
+ }
+ rDoc.SetCondFormList(new ScConditionalFormatList(rDoc, *pSrcDoc->GetCondFormList(mnTab)), mnTab);
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+void ScUndoConditionalFormatList::Repeat(SfxRepeatTarget& )
+{
+}
+
+bool ScUndoConditionalFormatList::CanRepeat(SfxRepeatTarget& ) const
+{
+ return false;
+}
+
+ScUndoUseScenario::ScUndoUseScenario( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+/*C*/ const ScArea& rDestArea,
+ ScDocumentUniquePtr pNewUndoDoc,
+ OUString aNewName ) :
+ ScSimpleUndo( pNewDocShell ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ aMarkData( rMark ),
+ aName(std::move( aNewName ))
+{
+ aRange.aStart.SetCol(rDestArea.nColStart);
+ aRange.aStart.SetRow(rDestArea.nRowStart);
+ aRange.aStart.SetTab(rDestArea.nTab);
+ aRange.aEnd.SetCol(rDestArea.nColEnd);
+ aRange.aEnd.SetRow(rDestArea.nRowEnd);
+ aRange.aEnd.SetTab(rDestArea.nTab);
+}
+
+ScUndoUseScenario::~ScUndoUseScenario()
+{
+}
+
+OUString ScUndoUseScenario::GetComment() const
+{
+ return ScResId( STR_UNDO_USESCENARIO );
+}
+
+void ScUndoUseScenario::Undo()
+{
+ BeginUndo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->DoneBlockMode();
+ pViewShell->InitOwnBlockMode( aRange );
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.DeleteSelection( InsertDeleteFlags::ALL, aMarkData );
+ pUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::ALL, true, rDoc, &aMarkData);
+
+ // scenario table
+ bool bFrame = false;
+ SCTAB nTab = aRange.aStart.Tab();
+ SCTAB nEndTab = nTab;
+ while ( pUndoDoc->HasTable(nEndTab+1) && pUndoDoc->IsScenario(nEndTab+1) )
+ ++nEndTab;
+ for (SCTAB i = nTab+1; i<=nEndTab; i++)
+ {
+ // Flags always
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ pUndoDoc->GetScenarioData( i, aComment, aColor, nScenFlags );
+ rDoc.SetScenarioData( i, aComment, aColor, nScenFlags );
+ bool bActive = pUndoDoc->IsActiveScenario( i );
+ rDoc.SetActiveScenario( i, bActive );
+ // For copy-back scenario also consider content
+ if ( nScenFlags & ScScenarioFlags::TwoWay )
+ {
+ rDoc.DeleteAreaTab( 0,0, rDoc.MaxCol(),rDoc.MaxRow(), i, InsertDeleteFlags::ALL );
+ pUndoDoc->CopyToDocument(0,0,i, rDoc.MaxCol(),rDoc.MaxRow(),i, InsertDeleteFlags::ALL,false, rDoc);
+ }
+ if ( nScenFlags & ScScenarioFlags::ShowFrame )
+ bFrame = true;
+ }
+
+ // if visible borders, then paint all
+ if (bFrame)
+ pDocShell->PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid | PaintPartFlags::Extras );
+ else
+ pDocShell->PostPaint( aRange, PaintPartFlags::Grid | PaintPartFlags::Extras );
+ pDocShell->PostDataChanged();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ ShowTable( aRange.aStart.Tab() );
+
+ EndUndo();
+}
+
+void ScUndoUseScenario::Redo()
+{
+ SCTAB nTab = aRange.aStart.Tab();
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->SetTabNo( nTab );
+ pViewShell->DoneBlockMode();
+ pViewShell->InitOwnBlockMode( aRange );
+ }
+
+ pDocShell->UseScenario( nTab, aName, false );
+
+ EndRedo();
+}
+
+void ScUndoUseScenario::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ OUString aTemp = aName;
+ pViewTarget->GetViewShell()->UseScenario(aTemp);
+ }
+}
+
+bool ScUndoUseScenario::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScViewData& rViewData = pViewTarget->GetViewShell()->GetViewData();
+ return !rViewData.GetDocument().IsScenario( rViewData.GetTabNo() );
+ }
+ return false;
+}
+
+ScUndoSelectionStyle::ScUndoSelectionStyle( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ const ScRange& rRange,
+ OUString aName,
+ ScDocumentUniquePtr pNewUndoDoc ) :
+ ScSimpleUndo( pNewDocShell ),
+ aMarkData( rMark ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ aStyleName(std::move( aName )),
+ aRange( rRange )
+{
+ aMarkData.MarkToMulti();
+}
+
+ScUndoSelectionStyle::~ScUndoSelectionStyle()
+{
+}
+
+OUString ScUndoSelectionStyle::GetComment() const
+{
+ return ScResId( STR_UNDO_APPLYCELLSTYLE );
+}
+
+void ScUndoSelectionStyle::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SetViewMarkData( aMarkData );
+
+ ScRange aWorkRange( aRange );
+ if ( rDoc.HasAttrib( aWorkRange, HasAttrFlags::Merged ) ) // Merged cells?
+ rDoc.ExtendMerge( aWorkRange, true );
+
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt( nExtFlags, aWorkRange );
+
+ if (bUndo) // if Undo then push back all old data again
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScRange aCopyRange = aWorkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ATTRIB, true, rDoc, &aMarkData);
+ }
+ else // if Redo, then reapply style
+ {
+ ScStyleSheetPool* pStlPool = rDoc.GetStyleSheetPool();
+ ScStyleSheet* pStyleSheet =
+ static_cast<ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
+ if (!pStyleSheet)
+ {
+ OSL_FAIL("StyleSheet not found");
+ return;
+ }
+ rDoc.ApplySelectionStyle( *pStyleSheet, aMarkData );
+ }
+
+ pDocShell->UpdatePaintExt( nExtFlags, aWorkRange );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( !( pViewShell && pViewShell->AdjustBlockHeight() ) )
+/*A*/ pDocShell->PostPaint( aWorkRange, PaintPartFlags::Grid | PaintPartFlags::Extras, nExtFlags );
+
+ ShowTable( aWorkRange.aStart.Tab() );
+}
+
+void ScUndoSelectionStyle::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoSelectionStyle::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoSelectionStyle::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (dynamic_cast<const ScTabViewTarget*>( &rTarget) == nullptr)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStlPool = rDoc.GetStyleSheetPool();
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( pStlPool->
+ Find( aStyleName, SfxStyleFamily::Para ));
+ if (!pStyleSheet)
+ {
+ OSL_FAIL("StyleSheet not found");
+ return;
+ }
+
+ ScTabViewShell& rViewShell = *static_cast<ScTabViewTarget&>(rTarget).GetViewShell();
+ rViewShell.SetStyleSheetToMarked( pStyleSheet );
+}
+
+bool ScUndoSelectionStyle::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoEnterMatrix::ScUndoEnterMatrix( ScDocShell* pNewDocShell, const ScRange& rArea,
+ ScDocumentUniquePtr pNewUndoDoc, OUString aForm ) :
+ ScBlockUndo( pNewDocShell, rArea, SC_UNDO_SIMPLE ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ aFormula(std::move( aForm ))
+{
+ SetChangeTrack();
+}
+
+ScUndoEnterMatrix::~ScUndoEnterMatrix()
+{
+}
+
+OUString ScUndoEnterMatrix::GetComment() const
+{
+ return ScResId( STR_UNDO_ENTERMATRIX );
+}
+
+void ScUndoEnterMatrix::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->AppendContentRange( aBlockRange, pUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction );
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoEnterMatrix::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.DeleteAreaTab( aBlockRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ pUndoDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid );
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ EndUndo();
+}
+
+void ScUndoEnterMatrix::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SelectOneTable( aBlockRange.aStart.Tab() );
+ aDestMark.SetMarkArea( aBlockRange );
+
+ rDoc.InsertMatrixFormula( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
+ aBlockRange.aEnd.Col(), aBlockRange.aEnd.Row(),
+ aDestMark, aFormula );
+
+ SetChangeTrack();
+
+ EndRedo();
+}
+
+void ScUndoEnterMatrix::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pTabViewTarget = dynamic_cast<ScTabViewTarget*>(&rTarget))
+ {
+ OUString aTemp = aFormula;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pTabViewTarget->GetViewShell()->EnterMatrix(aTemp, rDoc.GetGrammar());
+ }
+}
+
+bool ScUndoEnterMatrix::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+static ScRange lcl_GetMultiMarkRange( const ScMarkData& rMark )
+{
+ OSL_ENSURE( rMark.IsMultiMarked(), "wrong mark type" );
+ return rMark.GetMultiMarkArea();
+}
+
+ScUndoIndent::ScUndoIndent( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, bool bIncrement ) :
+ ScBlockUndo( pNewDocShell, lcl_GetMultiMarkRange(rMark), SC_UNDO_AUTOHEIGHT ),
+ aMarkData( rMark ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ bIsIncrement( bIncrement )
+{
+}
+
+ScUndoIndent::~ScUndoIndent()
+{
+}
+
+OUString ScUndoIndent::GetComment() const
+{
+ TranslateId pId = bIsIncrement ? STR_UNDO_INC_INDENT : STR_UNDO_DEC_INDENT;
+ return ScResId(pId);
+}
+
+void ScUndoIndent::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScRange aCopyRange = aBlockRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ATTRIB, true, rDoc, &aMarkData);
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndUndo();
+}
+
+void ScUndoIndent::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.ChangeSelectionIndent( bIsIncrement, aMarkData );
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndRedo();
+}
+
+void ScUndoIndent::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->ChangeIndent( bIsIncrement );
+}
+
+bool ScUndoIndent::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoTransliterate::ScUndoTransliterate( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, TransliterationFlags nType ) :
+ ScBlockUndo( pNewDocShell, lcl_GetMultiMarkRange(rMark), SC_UNDO_AUTOHEIGHT ),
+ aMarkData( rMark ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ nTransliterationType( nType )
+{
+}
+
+ScUndoTransliterate::~ScUndoTransliterate()
+{
+}
+
+OUString ScUndoTransliterate::GetComment() const
+{
+ return ScResId( STR_UNDO_TRANSLITERATE );
+}
+
+void ScUndoTransliterate::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScRange aCopyRange = aBlockRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, rDoc, &aMarkData);
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndUndo();
+}
+
+void ScUndoTransliterate::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.TransliterateText( aMarkData, nTransliterationType );
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndRedo();
+}
+
+void ScUndoTransliterate::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->TransliterateText( nTransliterationType );
+}
+
+bool ScUndoTransliterate::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoClearItems::ScUndoClearItems( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, const sal_uInt16* pW ) :
+ ScBlockUndo( pNewDocShell, lcl_GetMultiMarkRange(rMark), SC_UNDO_AUTOHEIGHT ),
+ aMarkData( rMark ),
+ pUndoDoc( std::move(pNewUndoDoc) )
+{
+ OSL_ENSURE( pW, "ScUndoClearItems: Which-Pointer is Null" );
+
+ sal_uInt16 nCount = 0;
+ while ( pW[nCount] )
+ ++nCount;
+ pWhich.reset( new sal_uInt16[nCount+1] );
+ for (sal_uInt16 i=0; i<=nCount; i++)
+ pWhich[i] = pW[i];
+}
+
+ScUndoClearItems::~ScUndoClearItems()
+{
+}
+
+OUString ScUndoClearItems::GetComment() const
+{
+ return ScResId( STR_UNDO_DELETECONTENTS );
+}
+
+void ScUndoClearItems::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pUndoDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ATTRIB, true, rDoc, &aMarkData);
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndUndo();
+}
+
+void ScUndoClearItems::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.ClearSelectionItems( pWhich.get(), aMarkData );
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndRedo();
+}
+
+void ScUndoClearItems::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScViewData& rViewData = pViewTarget->GetViewShell()->GetViewData();
+ rViewData.GetDocFunc().ClearItems( rViewData.GetMarkData(), pWhich.get(), false );
+ }
+}
+
+bool ScUndoClearItems::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+// remove all line breaks of a table
+ScUndoRemoveBreaks::ScUndoRemoveBreaks( ScDocShell* pNewDocShell,
+ SCTAB nNewTab, ScDocumentUniquePtr pNewUndoDoc ) :
+ ScSimpleUndo( pNewDocShell ),
+ nTab( nNewTab ),
+ pUndoDoc( std::move(pNewUndoDoc) )
+{
+}
+
+ScUndoRemoveBreaks::~ScUndoRemoveBreaks()
+{
+}
+
+OUString ScUndoRemoveBreaks::GetComment() const
+{
+ return ScResId( STR_UNDO_REMOVEBREAKS );
+}
+
+void ScUndoRemoveBreaks::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ pUndoDoc->CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::NONE, false, rDoc);
+ if (pViewShell)
+ pViewShell->UpdatePageBreakData( true );
+ pDocShell->PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid );
+
+ EndUndo();
+}
+
+void ScUndoRemoveBreaks::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ rDoc.RemoveManualBreaks(nTab);
+ rDoc.UpdatePageBreaks(nTab);
+ if (pViewShell)
+ pViewShell->UpdatePageBreakData( true );
+ pDocShell->PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid );
+
+ EndRedo();
+}
+
+void ScUndoRemoveBreaks::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+ rViewShell.RemoveManualBreaks();
+ }
+}
+
+bool ScUndoRemoveBreaks::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoRemoveMerge::ScUndoRemoveMerge( ScDocShell* pNewDocShell,
+ const ScCellMergeOption& rOption, ScDocumentUniquePtr pNewUndoDoc ) :
+ ScBlockUndo( pNewDocShell, rOption.getFirstSingleRange(), SC_UNDO_SIMPLE ),
+ pUndoDoc( std::move(pNewUndoDoc) )
+{
+ maOptions.push_back( rOption);
+}
+
+ScUndoRemoveMerge::ScUndoRemoveMerge( ScDocShell* pNewDocShell,
+ const ScRange& rRange, ScDocumentUniquePtr pNewUndoDoc ) :
+ ScBlockUndo( pNewDocShell, rRange, SC_UNDO_SIMPLE ),
+ pUndoDoc( std::move(pNewUndoDoc) )
+{
+}
+
+ScUndoRemoveMerge::~ScUndoRemoveMerge()
+{
+}
+
+OUString ScUndoRemoveMerge::GetComment() const
+{
+ return ScResId( STR_UNDO_REMERGE ); // "remove merge"
+}
+
+ScDocument* ScUndoRemoveMerge::GetUndoDoc()
+{
+ return pUndoDoc.get();
+}
+
+void ScUndoRemoveMerge::AddCellMergeOption( const ScCellMergeOption& rOption )
+{
+ maOptions.push_back( rOption);
+}
+
+void ScUndoRemoveMerge::Undo()
+{
+ using ::std::set;
+
+ SetCurTab();
+ BeginUndo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (const auto & rOption : maOptions)
+ {
+ for (const auto& rTab : rOption.maTabs)
+ {
+ OSL_ENSURE(pUndoDoc, "NULL pUndoDoc!");
+ if (!pUndoDoc)
+ continue;
+ // There is no need to extend merge area because it's already been extended.
+ ScRange aRange = rOption.getSingleRange(rTab);
+ rDoc.DeleteAreaTab(aRange, InsertDeleteFlags::ATTRIB);
+ pUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::ATTRIB, false, rDoc);
+
+ bool bDidPaint = false;
+ if ( pViewShell )
+ {
+ pViewShell->SetTabNo(rTab);
+ bDidPaint = pViewShell->AdjustRowHeight(rOption.mnStartRow, rOption.mnEndRow, true);
+ }
+ if (!bDidPaint)
+ ScUndoUtil::PaintMore(pDocShell, aRange);
+ }
+ }
+
+ EndUndo();
+}
+
+void ScUndoRemoveMerge::Redo()
+{
+ using ::std::set;
+
+ SetCurTab();
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ for (const auto & rOption : maOptions)
+ {
+ for (const SCTAB nTab : rOption.maTabs)
+ {
+ // There is no need to extend merge area because it's already been extended.
+ ScRange aRange = rOption.getSingleRange(nTab);
+
+ const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetDefaultItem( ATTR_MERGE );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( rDefAttr );
+ rDoc.ApplyPatternAreaTab( rOption.mnStartCol, rOption.mnStartRow,
+ rOption.mnEndCol, rOption.mnEndRow, nTab,
+ aPattern );
+
+ rDoc.RemoveFlagsTab( rOption.mnStartCol, rOption.mnStartRow,
+ rOption.mnEndCol, rOption.mnEndRow, nTab,
+ ScMF::Hor | ScMF::Ver );
+
+ rDoc.ExtendMerge(aRange, true);
+
+ // Paint
+
+ bool bDidPaint = false;
+ if ( pViewShell )
+ {
+ pViewShell->SetTabNo(nTab);
+ bDidPaint = pViewShell->AdjustRowHeight(rOption.mnStartRow, rOption.mnEndRow, true);
+ }
+ if (!bDidPaint)
+ ScUndoUtil::PaintMore(pDocShell, aRange);
+ }
+ }
+
+ EndRedo();
+}
+
+void ScUndoRemoveMerge::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->RemoveMerge();
+}
+
+bool ScUndoRemoveMerge::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+void ScUndoRemoveMerge::SetCurTab()
+{
+ SCTAB nCurTab = ScDocShell::GetCurTab();
+ aBlockRange.aStart.SetTab(nCurTab);
+ aBlockRange.aEnd.SetTab(nCurTab);
+}
+
+/** set only border, for ScRangeList (StarOne) */
+static ScRange lcl_TotalRange( const ScRangeList& rRanges )
+{
+ ScRange aTotal;
+ if ( !rRanges.empty() )
+ {
+ aTotal = rRanges[ 0 ];
+ for ( size_t i = 1, nCount = rRanges.size(); i < nCount; ++i )
+ {
+ ScRange const & rRange = rRanges[ i ];
+ if (rRange.aStart.Col() < aTotal.aStart.Col()) aTotal.aStart.SetCol(rRange.aStart.Col());
+ if (rRange.aStart.Row() < aTotal.aStart.Row()) aTotal.aStart.SetRow(rRange.aStart.Row());
+ if (rRange.aStart.Tab() < aTotal.aStart.Tab()) aTotal.aStart.SetTab(rRange.aStart.Tab());
+ if (rRange.aEnd.Col() > aTotal.aEnd.Col() ) aTotal.aEnd.SetCol( rRange.aEnd.Col() );
+ if (rRange.aEnd.Row() > aTotal.aEnd.Row() ) aTotal.aEnd.SetRow( rRange.aEnd.Row() );
+ if (rRange.aEnd.Tab() > aTotal.aEnd.Tab() ) aTotal.aEnd.SetTab(rRange.aEnd.Tab() );
+ }
+ }
+ return aTotal;
+}
+
+ScUndoBorder::ScUndoBorder(ScDocShell* pNewDocShell,
+ const ScRangeList& rRangeList, ScDocumentUniquePtr pNewUndoDoc,
+ const SvxBoxItem& rNewOuter, const SvxBoxInfoItem& rNewInner)
+ : ScBlockUndo(pNewDocShell, lcl_TotalRange(rRangeList), SC_UNDO_SIMPLE)
+ , xUndoDoc(std::move(pNewUndoDoc))
+{
+ xRanges.reset(new ScRangeList(rRangeList));
+ xOuter.reset(new SvxBoxItem(rNewOuter));
+ xInner.reset(new SvxBoxInfoItem(rNewInner));
+}
+
+OUString ScUndoBorder::GetComment() const
+{
+ return ScResId( STR_UNDO_SELATTRLINES ); //! own string?
+}
+
+void ScUndoBorder::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+ aMarkData.MarkFromRangeList(*xRanges, false);
+ xUndoDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ATTRIB, true, rDoc, &aMarkData);
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndUndo();
+}
+
+void ScUndoBorder::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument(); // call function at docfunc
+ size_t nCount = xRanges->size();
+ for (size_t i = 0; i < nCount; ++i )
+ {
+ ScRange const & rRange = (*xRanges)[i];
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( rRange );
+ aMark.SelectTable( nTab, true );
+
+ rDoc.ApplySelectionFrame(aMark, *xOuter, xInner.get());
+ }
+ for (size_t i = 0; i < nCount; ++i)
+ pDocShell->PostPaint( (*xRanges)[i], PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ EndRedo();
+}
+
+void ScUndoBorder::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ //TODO later (when the function has moved from cellsuno to docfunc)
+}
+
+bool ScUndoBorder::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // See above
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoblk2.cxx b/sc/source/ui/undo/undoblk2.cxx
new file mode 100644
index 0000000000..52ee421cc3
--- /dev/null
+++ b/sc/source/ui/undo/undoblk2.cxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <undoblk.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <olinetab.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <target.hxx>
+#include <columnspanset.hxx>
+
+#include <undoolk.hxx>
+
+#include <svx/svdundo.hxx>
+
+/** Change column widths or row heights */
+ScUndoWidthOrHeight::ScUndoWidthOrHeight( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOLROW nNewStart, SCTAB nNewStartTab, SCCOLROW nNewEnd, SCTAB nNewEndTab,
+ ScDocumentUniquePtr pNewUndoDoc, std::vector<sc::ColRowSpan>&& rRanges,
+ std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ ScSizeMode eNewMode, sal_uInt16 nNewSizeTwips, bool bNewWidth ) :
+ ScSimpleUndo( pNewDocShell ),
+ aMarkData( rMark ),
+ nStart( nNewStart ),
+ nEnd( nNewEnd ),
+ nStartTab( nNewStartTab ),
+ nEndTab( nNewEndTab ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ pUndoTab( std::move(pNewUndoTab) ),
+ maRanges( std::move(rRanges) ),
+ nNewSize( nNewSizeTwips ),
+ bWidth( bNewWidth ),
+ eMode( eNewMode )
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScUndoWidthOrHeight::~ScUndoWidthOrHeight()
+{
+ pUndoDoc.reset();
+ pUndoTab.reset();
+ pDrawUndo.reset();
+}
+
+OUString ScUndoWidthOrHeight::GetComment() const
+{
+ // [ "optimal " ] "Column width" | "row height"
+ return ( bWidth ?
+ ( ( eMode == SC_SIZE_OPTIMAL )?
+ ScResId( STR_UNDO_OPTCOLWIDTH ) :
+ ScResId( STR_UNDO_COLWIDTH )
+ ) :
+ ( ( eMode == SC_SIZE_OPTIMAL )?
+ ScResId( STR_UNDO_OPTROWHEIGHT ) :
+ ScResId( STR_UNDO_ROWHEIGHT )
+ ) );
+}
+
+void ScUndoWidthOrHeight::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SCCOLROW nPaintStart = nStart > 0 ? nStart-1 : static_cast<SCCOLROW>(0);
+
+ if (eMode==SC_SIZE_OPTIMAL)
+ {
+ if ( SetViewMarkData( aMarkData ) )
+ nPaintStart = 0; // paint all, because of changed selection
+ }
+
+ //! outlines from all tables?
+ if (pUndoTab) // Outlines are included when saving ?
+ rDoc.SetOutlineTable( nStartTab, pUndoTab.get() );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : aMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+
+ if (pViewShell)
+ pViewShell->OnLOKSetWidthOrHeight(nStart, bWidth);
+
+ if (bWidth) // Width
+ {
+ pUndoDoc->CopyToDocument(static_cast<SCCOL>(nStart), 0, rTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), rTab, InsertDeleteFlags::NONE,
+ false, rDoc);
+ rDoc.UpdatePageBreaks( rTab );
+ pDocShell->PostPaint( static_cast<SCCOL>(nPaintStart), 0, rTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), rTab, PaintPartFlags::Grid | PaintPartFlags::Top );
+ }
+ else // Height
+ {
+ pUndoDoc->CopyToDocument(0, nStart, rTab, rDoc.MaxCol(), nEnd, rTab, InsertDeleteFlags::NONE, false, rDoc);
+ rDoc.UpdatePageBreaks( rTab );
+ pDocShell->PostPaint( 0, nPaintStart, rTab, rDoc.MaxCol(), rDoc.MaxRow(), rTab, PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+ }
+
+ DoSdrUndoAction( pDrawUndo.get(), &rDoc );
+
+ if (pViewShell)
+ {
+ SCTAB nCurrentTab = pViewShell->GetViewData().GetTabNo();
+ bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell, bWidth /* bColumns */, !bWidth /* bRows */,
+ true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
+ false /* bGroups */, nCurrentTab);
+ pViewShell->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
+
+ if ( nCurrentTab < nStartTab || nCurrentTab > nEndTab )
+ pViewShell->SetTabNo( nStartTab );
+ }
+
+ EndUndo();
+}
+
+void ScUndoWidthOrHeight::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ bool bPaintAll = false;
+ if (eMode==SC_SIZE_OPTIMAL)
+ {
+ if ( SetViewMarkData( aMarkData ) )
+ bPaintAll = true; // paint all, because of changed selection
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ if ( nTab < nStartTab || nTab > nEndTab )
+ pViewShell->SetTabNo( nStartTab );
+
+ // SetWidthOrHeight changes current sheet!
+ pViewShell->SetWidthOrHeight(
+ bWidth, maRanges, eMode, nNewSize, false, &aMarkData);
+ }
+
+ // paint grid if selection was changed directly at the MarkData
+ if (bPaintAll)
+ pDocShell->PostPaint( 0, 0, nStartTab, rDoc.MaxCol(), rDoc.MaxRow(), nEndTab, PaintPartFlags::Grid );
+
+ EndRedo();
+}
+
+void ScUndoWidthOrHeight::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->SetMarkedWidthOrHeight( bWidth, eMode, nNewSize );
+}
+
+bool ScUndoWidthOrHeight::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoblk3.cxx b/sc/source/ui/undo/undoblk3.cxx
new file mode 100644
index 0000000000..bc967d96d4
--- /dev/null
+++ b/sc/source/ui/undo/undoblk3.cxx
@@ -0,0 +1,1743 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <scitems.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <utility>
+#include <vcl/virdev.hxx>
+#include <sfx2/app.hxx>
+#include <svx/svdundo.hxx>
+#include <osl/diagnose.h>
+
+#include <undoblk.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <arealink.hxx>
+#include <patattr.hxx>
+#include <target.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <undoolk.hxx>
+#include <undoutil.hxx>
+#include <chgtrack.hxx>
+#include <paramisc.hxx>
+#include <postit.hxx>
+#include <progress.hxx>
+#include <editutil.hxx>
+#include <editdataarray.hxx>
+#include <rowheightcontext.hxx>
+
+// TODO:
+/*A*/ // SetOptimalHeight on Document, when no View
+
+ScUndoDeleteContents::ScUndoDeleteContents(
+ ScDocShell* pNewDocShell,
+ const ScMarkData& rMark, const ScRange& rRange,
+ ScDocumentUniquePtr&& pNewUndoDoc, bool bNewMulti,
+ InsertDeleteFlags nNewFlags, bool bObjects )
+ : ScSimpleUndo( pNewDocShell ),
+ aRange ( rRange ),
+ aMarkData ( rMark ),
+ pUndoDoc ( std::move(pNewUndoDoc) ),
+ nFlags ( nNewFlags ),
+ bMulti ( bNewMulti ) // unnecessary
+{
+ if (bObjects)
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+
+ if ( !(aMarkData.IsMarked() || aMarkData.IsMultiMarked()) ) // if no cell is selected:
+ aMarkData.SetMarkArea( aRange ); // select cell under cursor
+
+ SetChangeTrack();
+}
+
+ScUndoDeleteContents::~ScUndoDeleteContents()
+{
+ pUndoDoc.reset();
+ pDrawUndo.reset();
+}
+
+OUString ScUndoDeleteContents::GetComment() const
+{
+ return ScResId( STR_UNDO_DELETECONTENTS ); // "Delete"
+}
+
+void ScUndoDeleteContents::SetDataSpans( const std::shared_ptr<DataSpansType>& pSpans )
+{
+ mpDataSpans = pSpans;
+}
+
+void ScUndoDeleteContents::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack && (nFlags & InsertDeleteFlags::CONTENTS) )
+ pChangeTrack->AppendContentRange( aRange, pUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction );
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoDeleteContents::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SetViewMarkData( aMarkData );
+
+ sal_uInt16 nExtFlags = 0;
+
+ if (bUndo) // only Undo
+ {
+ InsertDeleteFlags nUndoFlags = InsertDeleteFlags::NONE; // copy either all or none of the content
+ if (nFlags & InsertDeleteFlags::CONTENTS) // (Only the correct ones have been copied into UndoDoc)
+ nUndoFlags |= InsertDeleteFlags::CONTENTS;
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ nUndoFlags |= InsertDeleteFlags::ATTRIB;
+ if (nFlags & InsertDeleteFlags::EDITATTR) // Edit-Engine attribute
+ nUndoFlags |= InsertDeleteFlags::STRING; // -> Cells will be changed
+ if (nFlags & InsertDeleteFlags::SPARKLINES)
+ nUndoFlags |= InsertDeleteFlags::SPARKLINES;
+ // do not create clones of note captions, they will be restored via drawing undo
+ nUndoFlags |= InsertDeleteFlags::NOCAPTIONS;
+
+ ScRange aCopyRange = aRange;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+
+ pUndoDoc->CopyToDocument(aCopyRange, nUndoFlags, bMulti, rDoc, &aMarkData);
+
+ DoSdrUndoAction( pDrawUndo.get(), &rDoc );
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ pDocShell->UpdatePaintExt( nExtFlags, aRange ); // content after the change
+ }
+ else // only Redo
+ {
+ pDocShell->UpdatePaintExt( nExtFlags, aRange ); // content before the change
+
+ aMarkData.MarkToMulti();
+ RedoSdrUndoAction( pDrawUndo.get() );
+ // do not delete objects and note captions, they have been removed via drawing undo
+ InsertDeleteFlags nRedoFlags = (nFlags & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
+ rDoc.DeleteSelection( nRedoFlags, aMarkData );
+ aMarkData.MarkToSimple();
+
+ SetChangeTrack();
+ }
+
+ if (nFlags & InsertDeleteFlags::CONTENTS)
+ {
+ // Broadcast only when the content changes. fdo#74687
+ if (mpDataSpans)
+ BroadcastChanges(*mpDataSpans);
+ else
+ BroadcastChanges(aRange);
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( !( pViewShell && pViewShell->AdjustRowHeight(
+ aRange.aStart.Row(), aRange.aEnd.Row(), true ) ) )
+/*A*/ pDocShell->PostPaint( aRange, PaintPartFlags::Grid | PaintPartFlags::Extras, nExtFlags );
+
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ ShowTable( aRange );
+}
+
+void ScUndoDeleteContents::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocShell, aRange, "undo");
+}
+
+void ScUndoDeleteContents::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocShell, aRange, "redo");
+}
+
+void ScUndoDeleteContents::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->DeleteContents( nFlags );
+}
+
+bool ScUndoDeleteContents::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoFillTable::ScUndoFillTable( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, bool bNewMulti, SCTAB nSrc,
+ InsertDeleteFlags nFlg, ScPasteFunc nFunc, bool bSkip, bool bLink )
+ : ScSimpleUndo( pNewDocShell ),
+ aRange ( nStartX, nStartY, nStartZ, nEndX, nEndY, nEndZ ),
+ aMarkData ( rMark ),
+ pUndoDoc ( std::move(pNewUndoDoc) ),
+ nFlags ( nFlg ),
+ nFunction ( nFunc ),
+ nSrcTab ( nSrc ),
+ bMulti ( bNewMulti ),
+ bSkipEmpty ( bSkip ),
+ bAsLink ( bLink )
+{
+ SetChangeTrack();
+}
+
+ScUndoFillTable::~ScUndoFillTable()
+{
+}
+
+OUString ScUndoFillTable::GetComment() const
+{
+ return ScResId( STR_FILL_TAB );
+}
+
+void ScUndoFillTable::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ SCTAB nTabCount = pDocShell->GetDocument().GetTableCount();
+ ScRange aWorkRange(aRange);
+ nStartChangeAction = 0;
+ sal_uLong nTmpAction;
+ for (const auto& rTab : aMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+ if (rTab != nSrcTab)
+ {
+ aWorkRange.aStart.SetTab(rTab);
+ aWorkRange.aEnd.SetTab(rTab);
+ pChangeTrack->AppendContentRange( aWorkRange, pUndoDoc.get(),
+ nTmpAction, nEndChangeAction );
+ if ( !nStartChangeAction )
+ nStartChangeAction = nTmpAction;
+ }
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoFillTable::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SetViewMarkData( aMarkData );
+
+ if (bUndo) // only Undo
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ ScRange aWorkRange(aRange);
+ for (const auto& rTab : aMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+ if (rTab != nSrcTab)
+ {
+ aWorkRange.aStart.SetTab(rTab);
+ aWorkRange.aEnd.SetTab(rTab);
+ if (bMulti)
+ rDoc.DeleteSelectionTab( rTab, InsertDeleteFlags::ALL, aMarkData );
+ else
+ rDoc.DeleteAreaTab( aWorkRange, InsertDeleteFlags::ALL );
+ pUndoDoc->CopyToDocument(aWorkRange, InsertDeleteFlags::ALL, bMulti, rDoc, &aMarkData);
+ }
+ }
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+ }
+ else // only Redo
+ {
+ aMarkData.MarkToMulti();
+ rDoc.FillTabMarked( nSrcTab, aMarkData, nFlags, nFunction, bSkipEmpty, bAsLink );
+ aMarkData.MarkToSimple();
+ SetChangeTrack();
+ }
+
+ pDocShell->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Extras);
+ pDocShell->PostDataChanged();
+
+ // CellContentChanged comes with the selection
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ SCTAB nTab = pViewShell->GetViewData().GetTabNo();
+ if ( !aMarkData.GetTableSelect(nTab) )
+ pViewShell->SetTabNo( nSrcTab );
+
+ pViewShell->DoneBlockMode(); // causes problems otherwise since selection is on the wrong sheet.
+ }
+}
+
+void ScUndoFillTable::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoFillTable::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoFillTable::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->FillTab( nFlags, nFunction, bSkipEmpty, bAsLink );
+}
+
+bool ScUndoFillTable::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoSelectionAttr::ScUndoSelectionAttr( ScDocShell* pNewDocShell,
+ const ScMarkData& rMark,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, bool bNewMulti,
+ const ScPatternAttr* pNewApply,
+ const SvxBoxItem* pNewOuter, const SvxBoxInfoItem* pNewInner,
+ const ScRange* pRangeCover )
+ : ScSimpleUndo( pNewDocShell ),
+ aMarkData ( rMark ),
+ aRange ( nStartX, nStartY, nStartZ, nEndX, nEndY, nEndZ ),
+ mpDataArray(new ScEditDataArray),
+ pUndoDoc ( std::move(pNewUndoDoc) ),
+ bMulti ( bNewMulti )
+{
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ pApplyPattern = const_cast<ScPatternAttr*>(&pPool->DirectPutItemInPool( *pNewApply ));
+ pLineOuter = pNewOuter ? const_cast<SvxBoxItem*>( &pPool->DirectPutItemInPool( *pNewOuter ) ) : nullptr;
+ pLineInner = pNewInner ? const_cast<SvxBoxInfoItem*>( &pPool->DirectPutItemInPool( *pNewInner ) ) : nullptr;
+ aRangeCover = pRangeCover ? *pRangeCover : aRange;
+}
+
+ScUndoSelectionAttr::~ScUndoSelectionAttr()
+{
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ pPool->DirectRemoveItemFromPool(*pApplyPattern);
+ if (pLineOuter)
+ pPool->DirectRemoveItemFromPool(*pLineOuter);
+ if (pLineInner)
+ pPool->DirectRemoveItemFromPool(*pLineInner);
+
+ pUndoDoc.reset();
+}
+
+OUString ScUndoSelectionAttr::GetComment() const
+{
+ //"Attribute" "/Lines"
+ return ScResId( pLineOuter ? STR_UNDO_SELATTRLINES : STR_UNDO_SELATTR );
+}
+
+ScEditDataArray* ScUndoSelectionAttr::GetDataArray()
+{
+ return mpDataArray.get();
+}
+
+void ScUndoSelectionAttr::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SetViewMarkData( aMarkData );
+
+ ScRange aEffRange( aRangeCover );
+ if ( rDoc.HasAttrib( aEffRange, HasAttrFlags::Merged ) ) // merged cells?
+ rDoc.ExtendMerge( aEffRange );
+
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt( nExtFlags, aEffRange );
+
+ ChangeEditData(bUndo);
+
+ if (bUndo) // only for Undo
+ {
+ ScRange aCopyRange = aRangeCover;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, rDoc, &aMarkData);
+ }
+ else // only for Redo
+ {
+ aMarkData.MarkToMulti();
+ rDoc.ApplySelectionPattern( *pApplyPattern, aMarkData );
+ aMarkData.MarkToSimple();
+
+ if (pLineOuter)
+ rDoc.ApplySelectionFrame(aMarkData, *pLineOuter, pLineInner);
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( !( pViewShell && pViewShell->AdjustBlockHeight() ) )
+/*A*/ pDocShell->PostPaint( aEffRange, PaintPartFlags::Grid | PaintPartFlags::Extras, nExtFlags );
+
+ ShowTable( aRange );
+}
+
+void ScUndoSelectionAttr::ChangeEditData( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (const ScEditDataArray::Item* pItem = mpDataArray->First(); pItem; pItem = mpDataArray->Next())
+ {
+ ScAddress aPos(pItem->GetCol(), pItem->GetRow(), pItem->GetTab());
+ if (rDoc.GetCellType(aPos) != CELLTYPE_EDIT)
+ continue;
+
+ if (bUndo)
+ {
+ if (pItem->GetOldData())
+ rDoc.SetEditText(aPos, *pItem->GetOldData(), nullptr);
+ else
+ rDoc.SetEmptyCell(aPos);
+ }
+ else
+ {
+ if (pItem->GetNewData())
+ rDoc.SetEditText(aPos, *pItem->GetNewData(), nullptr);
+ else
+ rDoc.SetEmptyCell(aPos);
+ }
+ }
+}
+
+void ScUndoSelectionAttr::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoSelectionAttr::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoSelectionAttr::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+ if (pLineOuter)
+ rViewShell.ApplyPatternLines(*pApplyPattern, *pLineOuter, pLineInner);
+ else
+ rViewShell.ApplySelectionPattern( *pApplyPattern );
+ }
+}
+
+bool ScUndoSelectionAttr::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoAutoFill::ScUndoAutoFill( ScDocShell* pNewDocShell,
+ const ScRange& rRange, const ScRange& rSourceArea,
+ ScDocumentUniquePtr pNewUndoDoc, const ScMarkData& rMark,
+ FillDir eNewFillDir, FillCmd eNewFillCmd, FillDateCmd eNewFillDateCmd,
+ double fNewStartValue, double fNewStepValue, double fNewMaxValue )
+ : ScBlockUndo( pNewDocShell, rRange, SC_UNDO_AUTOHEIGHT ),
+ aSource ( rSourceArea ),
+ aMarkData ( rMark ),
+ pUndoDoc ( std::move(pNewUndoDoc) ),
+ eFillDir ( eNewFillDir ),
+ eFillCmd ( eNewFillCmd ),
+ eFillDateCmd ( eNewFillDateCmd ),
+ fStartValue ( fNewStartValue ),
+ fStepValue ( fNewStepValue ),
+ fMaxValue ( fNewMaxValue )
+{
+ SetChangeTrack();
+}
+
+ScUndoAutoFill::~ScUndoAutoFill()
+{
+}
+
+OUString ScUndoAutoFill::GetComment() const
+{
+ return ScResId( STR_UNDO_AUTOFILL ); //"Fill"
+}
+
+void ScUndoAutoFill::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->AppendContentRange( aBlockRange, pUndoDoc.get(),
+ nStartChangeAction, nEndChangeAction );
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoAutoFill::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : aMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+ ScRange aWorkRange = aBlockRange;
+ aWorkRange.aStart.SetTab(rTab);
+ aWorkRange.aEnd.SetTab(rTab);
+
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt( nExtFlags, aWorkRange );
+ rDoc.DeleteAreaTab( aWorkRange, InsertDeleteFlags::AUTOFILL );
+ pUndoDoc->CopyToDocument(aWorkRange, InsertDeleteFlags::AUTOFILL, false, rDoc);
+
+ // Actually we'd only need to broadcast the cells inserted during
+ // CopyToDocument(), as DeleteAreaTab() broadcasts deleted cells. For
+ // this we'd need to either record the span sets or let
+ // CopyToDocument() broadcast.
+ BroadcastChanges( aWorkRange);
+
+ rDoc.ExtendMerge( aWorkRange, true );
+ pDocShell->PostPaint( aWorkRange, PaintPartFlags::Grid, nExtFlags );
+ }
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ EndUndo();
+}
+
+void ScUndoAutoFill::Redo()
+{
+ BeginRedo();
+
+//! Select sheet
+
+ SCCOLROW nCount = 0;
+ switch (eFillDir)
+ {
+ case FILL_TO_BOTTOM:
+ nCount = aBlockRange.aEnd.Row() - aSource.aEnd.Row();
+ break;
+ case FILL_TO_RIGHT:
+ nCount = aBlockRange.aEnd.Col() - aSource.aEnd.Col();
+ break;
+ case FILL_TO_TOP:
+ nCount = aSource.aStart.Row() - aBlockRange.aStart.Row();
+ break;
+ case FILL_TO_LEFT:
+ nCount = aSource.aStart.Col() - aBlockRange.aStart.Col();
+ break;
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( fStartValue != MAXDOUBLE )
+ {
+ SCCOL nValX = (eFillDir == FILL_TO_LEFT) ? aSource.aEnd.Col() : aSource.aStart.Col();
+ SCROW nValY = (eFillDir == FILL_TO_TOP ) ? aSource.aEnd.Row() : aSource.aStart.Row();
+ SCTAB nTab = aSource.aStart.Tab();
+ rDoc.SetValue( nValX, nValY, nTab, fStartValue );
+ }
+ sal_uLong nProgCount;
+ if (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP)
+ nProgCount = aSource.aEnd.Col() - aSource.aStart.Col() + 1;
+ else
+ nProgCount = aSource.aEnd.Row() - aSource.aStart.Row() + 1;
+ nProgCount *= nCount;
+ ScProgress aProgress( rDoc.GetDocumentShell(),
+ ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
+
+ rDoc.Fill( aSource.aStart.Col(), aSource.aStart.Row(),
+ aSource.aEnd.Col(), aSource.aEnd.Row(), &aProgress,
+ aMarkData, nCount,
+ eFillDir, eFillCmd, eFillDateCmd,
+ fStepValue, fMaxValue );
+
+ SetChangeTrack();
+
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid );
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ EndRedo();
+}
+
+void ScUndoAutoFill::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+ if (eFillCmd==FILL_SIMPLE)
+ rViewShell.FillSimple( eFillDir );
+ else
+ rViewShell.FillSeries( eFillDir, eFillCmd, eFillDateCmd,
+ fStartValue, fStepValue, fMaxValue );
+ }
+}
+
+bool ScUndoAutoFill::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoMerge::ScUndoMerge(ScDocShell* pNewDocShell, ScCellMergeOption aOption,
+ bool bMergeContents, ScDocumentUniquePtr pUndoDoc, std::unique_ptr<SdrUndoAction> pDrawUndo)
+ : ScSimpleUndo(pNewDocShell)
+ , maOption(std::move(aOption))
+ , mbMergeContents(bMergeContents)
+ , mxUndoDoc(std::move(pUndoDoc))
+ , mpDrawUndo(std::move(pDrawUndo))
+{
+}
+
+ScUndoMerge::~ScUndoMerge()
+{
+ mpDrawUndo.reset();
+}
+
+OUString ScUndoMerge::GetComment() const
+{
+ return ScResId( STR_UNDO_MERGE );
+}
+
+void ScUndoMerge::DoChange( bool bUndo ) const
+{
+ using ::std::set;
+
+ if (maOption.maTabs.empty())
+ // Nothing to do.
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ScRange aCurRange = maOption.getSingleRange(ScDocShell::GetCurTab());
+ ScUndoUtil::MarkSimpleBlock(pDocShell, aCurRange);
+
+ for (const SCTAB nTab : maOption.maTabs)
+ {
+ ScRange aRange = maOption.getSingleRange(nTab);
+
+ if (bUndo)
+ // remove merge (contents are copied back below from undo document)
+ rDoc.RemoveMerge( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aStart.Tab() );
+ else
+ {
+ // repeat merge, but do not remove note captions (will be done by drawing redo below)
+ rDoc.DoMerge( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ aRange.aStart.Tab(), false );
+
+ if (maOption.mbCenter)
+ {
+ rDoc.ApplyAttr( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(),
+ SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
+ rDoc.ApplyAttr( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(),
+ SvxVerJustifyItem( SvxCellVerJustify::Center, ATTR_VER_JUSTIFY ) );
+ }
+ }
+
+ // undo -> copy back deleted contents
+ if (bUndo && mxUndoDoc)
+ {
+ // If there are note captions to be deleted during Undo they were
+ // kept or moved during the merge and copied to the Undo document
+ // without cloning the caption. Forget the target area's caption
+ // pointer that is identical to the one in the Undo document
+ // instead of deleting it.
+ rDoc.DeleteAreaTab( aRange,
+ InsertDeleteFlags::CONTENTS | InsertDeleteFlags::NOCAPTIONS | InsertDeleteFlags::FORGETCAPTIONS );
+ mxUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, rDoc);
+ }
+
+ // redo -> merge contents again
+ else if (!bUndo && mbMergeContents)
+ {
+ rDoc.DoMergeContents( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ aRange.aStart.Tab() );
+ }
+
+ if (bUndo)
+ DoSdrUndoAction( mpDrawUndo.get(), &rDoc );
+ else
+ RedoSdrUndoAction( mpDrawUndo.get() );
+
+ bool bDidPaint = false;
+ if ( pViewShell )
+ {
+ pViewShell->SetTabNo(nTab);
+ bDidPaint = pViewShell->AdjustRowHeight(maOption.mnStartRow, maOption.mnEndRow, true);
+ }
+
+ if (!bDidPaint)
+ ScUndoUtil::PaintMore(pDocShell, aRange);
+
+ rDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ }
+
+ ShowTable(aCurRange);
+}
+
+void ScUndoMerge::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoMerge::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoMerge::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+ rViewShell.MergeCells( false, false, false, 0 );
+ }
+}
+
+bool ScUndoMerge::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoAutoFormat::ScUndoAutoFormat( ScDocShell* pNewDocShell,
+ const ScRange& rRange, ScDocumentUniquePtr pNewUndoDoc,
+ const ScMarkData& rMark, bool bNewSize, sal_uInt16 nNewFormatNo )
+ : ScBlockUndo( pNewDocShell, rRange, bNewSize ? SC_UNDO_MANUALHEIGHT : SC_UNDO_AUTOHEIGHT ),
+ pUndoDoc ( std::move(pNewUndoDoc) ),
+ aMarkData ( rMark ),
+ bSize ( bNewSize ),
+ nFormatNo ( nNewFormatNo )
+{
+}
+
+ScUndoAutoFormat::~ScUndoAutoFormat()
+{
+}
+
+OUString ScUndoAutoFormat::GetComment() const
+{
+ return ScResId( STR_UNDO_AUTOFORMAT ); //"Auto-Format"
+}
+
+void ScUndoAutoFormat::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ rDoc.DeleteArea( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
+ aBlockRange.aEnd.Col(), aBlockRange.aEnd.Row(),
+ aMarkData, InsertDeleteFlags::ATTRIB );
+ ScRange aCopyRange = aBlockRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ATTRIB, false, rDoc, &aMarkData);
+
+ // cell heights and widths (InsertDeleteFlags::NONE)
+ if (bSize)
+ {
+ SCCOL nStartX = aBlockRange.aStart.Col();
+ SCROW nStartY = aBlockRange.aStart.Row();
+ SCTAB nStartZ = aBlockRange.aStart.Tab();
+ SCCOL nEndX = aBlockRange.aEnd.Col();
+ SCROW nEndY = aBlockRange.aEnd.Row();
+ SCTAB nEndZ = aBlockRange.aEnd.Tab();
+
+ pUndoDoc->CopyToDocument( nStartX, 0, 0, nEndX, rDoc.MaxRow(), nTabCount-1,
+ InsertDeleteFlags::NONE, false, rDoc, &aMarkData );
+ pUndoDoc->CopyToDocument( 0, nStartY, 0, rDoc.MaxCol(), nEndY, nTabCount-1,
+ InsertDeleteFlags::NONE, false, rDoc, &aMarkData );
+ pDocShell->PostPaint( 0, 0, nStartZ, rDoc.MaxCol(), rDoc.MaxRow(), nEndZ,
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top, SC_PF_LINES );
+ }
+ else
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES );
+
+ EndUndo();
+}
+
+void ScUndoAutoFormat::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SCCOL nStartX = aBlockRange.aStart.Col();
+ SCROW nStartY = aBlockRange.aStart.Row();
+ SCTAB nStartZ = aBlockRange.aStart.Tab();
+ SCCOL nEndX = aBlockRange.aEnd.Col();
+ SCROW nEndY = aBlockRange.aEnd.Row();
+ SCTAB nEndZ = aBlockRange.aEnd.Tab();
+
+ rDoc.AutoFormat( nStartX, nStartY, nEndX, nEndY, nFormatNo, aMarkData );
+
+ if (bSize)
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ Fraction aZoomX(1,1);
+ Fraction aZoomY = aZoomX;
+ double nPPTX,nPPTY;
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ ScViewData& rData = pViewShell->GetViewData();
+ nPPTX = rData.GetPPTX();
+ nPPTY = rData.GetPPTY();
+ aZoomX = rData.GetZoomX();
+ aZoomY = rData.GetZoomY();
+ }
+ else
+ {
+ // Keep zoom at 100
+ nPPTX = ScGlobal::nScreenPPTX;
+ nPPTY = ScGlobal::nScreenPPTY;
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, pVirtDev);
+ for (SCTAB nTab=nStartZ; nTab<=nEndZ; nTab++)
+ {
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SelectOneTable( nTab );
+ aDestMark.SetMarkArea( ScRange( nStartX, nStartY, nTab, nEndX, nEndY, nTab ) );
+ aDestMark.MarkToMulti();
+
+ // as SC_SIZE_VISOPT
+ for (SCROW nRow=nStartY; nRow<=nEndY; nRow++)
+ {
+ CRFlags nOld = rDoc.GetRowFlags(nRow,nTab);
+ bool bHidden = rDoc.RowHidden(nRow, nTab);
+ if ( !bHidden && ( nOld & CRFlags::ManualSize ) )
+ rDoc.SetRowFlags( nRow, nTab, nOld & ~CRFlags::ManualSize );
+ }
+
+ bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartY, nEndY, nTab, true);
+
+ for (SCCOL nCol=nStartX; nCol<=nEndX; nCol++)
+ if (!rDoc.ColHidden(nCol, nTab))
+ {
+ sal_uInt16 nThisSize = STD_EXTRA_WIDTH + rDoc.GetOptimalColWidth( nCol, nTab,
+ pVirtDev, nPPTX, nPPTY, aZoomX, aZoomY, false/*bFormula*/,
+ &aDestMark );
+ rDoc.SetColWidth( nCol, nTab, nThisSize );
+ rDoc.ShowCol( nCol, nTab, true );
+ }
+
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ rDoc.SetDrawPageSize(nTab);
+ }
+
+ pDocShell->PostPaint( 0, 0, nStartZ,
+ rDoc.MaxCol(), rDoc.MaxRow(), nEndZ,
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top, SC_PF_LINES);
+ }
+ else
+ pDocShell->PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES );
+
+ EndRedo();
+}
+
+void ScUndoAutoFormat::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->AutoFormat( nFormatNo );
+}
+
+bool ScUndoAutoFormat::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoReplace::ScUndoReplace( ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ OUString aNewUndoStr, ScDocumentUniquePtr pNewUndoDoc,
+ const SvxSearchItem* pItem )
+ : ScSimpleUndo( pNewDocShell ),
+ aCursorPos ( nCurX, nCurY, nCurZ ),
+ aMarkData ( rMark ),
+ aUndoStr (std::move( aNewUndoStr )),
+ pUndoDoc ( std::move(pNewUndoDoc) )
+{
+ pSearchItem.reset( new SvxSearchItem( *pItem ) );
+ SetChangeTrack();
+}
+
+ScUndoReplace::~ScUndoReplace()
+{
+ pUndoDoc.reset();
+ pSearchItem.reset();
+}
+
+void ScUndoReplace::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ if ( pUndoDoc )
+ { //! UndoDoc includes only the changed cells,
+ // that is why an Iterator can be used
+ pChangeTrack->AppendContentsIfInRefDoc( *pUndoDoc,
+ nStartChangeAction, nEndChangeAction );
+ }
+ else
+ {
+ nStartChangeAction = pChangeTrack->GetActionMax() + 1;
+ ScChangeActionContent* pContent = new ScChangeActionContent(
+ ScRange( aCursorPos) );
+ ScCellValue aCell;
+ aCell.assign(rDoc, aCursorPos);
+ pContent->SetOldValue( aUndoStr, &rDoc );
+ pContent->SetNewValue(aCell, &rDoc);
+ pChangeTrack->Append( pContent );
+ nEndChangeAction = pChangeTrack->GetActionMax();
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+OUString ScUndoReplace::GetComment() const
+{
+ return ScResId( STR_UNDO_REPLACE ); // "Replace"
+}
+
+void ScUndoReplace::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ShowTable( aCursorPos.Tab() );
+
+ if (pUndoDoc) // only for ReplaceAll !!
+ {
+ OSL_ENSURE(pSearchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL,
+ "ScUndoReplace:: Wrong Mode");
+
+ SetViewMarkData( aMarkData );
+
+//! selected sheet
+//! select range ?
+
+ // Undo document has no row/column information, thus copy with
+ // bColRowFlags = FALSE to not destroy Outline groups
+
+ InsertDeleteFlags nUndoFlags = (pSearchItem->GetPattern()) ? InsertDeleteFlags::ATTRIB : InsertDeleteFlags::CONTENTS;
+ pUndoDoc->CopyToDocument( 0, 0, 0,
+ rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
+ nUndoFlags, false, rDoc, nullptr, false ); // without row flags
+ pDocShell->PostPaintGridAll();
+ }
+ else if (pSearchItem->GetPattern() &&
+ pSearchItem->GetCommand() == SvxSearchCmd::REPLACE)
+ {
+ OUString aTempStr = pSearchItem->GetSearchString(); // toggle
+ pSearchItem->SetSearchString(pSearchItem->GetReplaceString());
+ pSearchItem->SetReplaceString(aTempStr);
+ rDoc.ReplaceStyle( *pSearchItem,
+ aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(),
+ aMarkData);
+ pSearchItem->SetReplaceString(pSearchItem->GetSearchString());
+ pSearchItem->SetSearchString(aTempStr);
+ if (pViewShell)
+ pViewShell->MoveCursorAbs( aCursorPos.Col(), aCursorPos.Row(),
+ SC_FOLLOW_JUMP, false, false );
+ pDocShell->PostPaintGridAll();
+ }
+ else if (pSearchItem->GetCellType() == SvxSearchCellType::NOTE)
+ {
+ ScPostIt* pNote = rDoc.GetNote(aCursorPos);
+ OSL_ENSURE( pNote, "ScUndoReplace::Undo - cell does not contain a note" );
+ if (pNote)
+ pNote->SetText( aCursorPos, aUndoStr );
+ if (pViewShell)
+ pViewShell->MoveCursorAbs( aCursorPos.Col(), aCursorPos.Row(),
+ SC_FOLLOW_JUMP, false, false );
+ }
+ else
+ {
+ // aUndoStr may contain line breaks
+ if ( aUndoStr.indexOf('\n') != -1 )
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aUndoStr);
+ rDoc.SetEditText(aCursorPos, rEngine.CreateTextObject());
+ }
+ else
+ rDoc.SetString( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aUndoStr );
+ if (pViewShell)
+ pViewShell->MoveCursorAbs( aCursorPos.Col(), aCursorPos.Row(),
+ SC_FOLLOW_JUMP, false, false );
+ pDocShell->PostPaintGridAll();
+ }
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ EndUndo();
+}
+
+void ScUndoReplace::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ if (pViewShell)
+ pViewShell->MoveCursorAbs( aCursorPos.Col(), aCursorPos.Row(),
+ SC_FOLLOW_JUMP, false, false );
+ if (pUndoDoc)
+ {
+ if (pViewShell)
+ {
+ SetViewMarkData( aMarkData );
+
+ pViewShell->SearchAndReplace( pSearchItem.get(), false, true );
+ }
+ }
+ else if (pSearchItem->GetPattern() &&
+ pSearchItem->GetCommand() == SvxSearchCmd::REPLACE)
+ {
+ rDoc.ReplaceStyle( *pSearchItem,
+ aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(),
+ aMarkData);
+ pDocShell->PostPaintGridAll();
+ }
+ else
+ if (pViewShell)
+ pViewShell->SearchAndReplace( pSearchItem.get(), false, true );
+
+ SetChangeTrack();
+
+ EndRedo();
+}
+
+void ScUndoReplace::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->SearchAndReplace( pSearchItem.get(), true, false );
+}
+
+bool ScUndoReplace::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+// multi-operation (only simple blocks)
+ScUndoTabOp::ScUndoTabOp( ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ, ScDocumentUniquePtr pNewUndoDoc,
+ const ScRefAddress& rFormulaCell,
+ const ScRefAddress& rFormulaEnd,
+ const ScRefAddress& rRowCell,
+ const ScRefAddress& rColCell,
+ ScTabOpParam::Mode eMode )
+ : ScSimpleUndo( pNewDocShell ),
+ aRange ( nStartX, nStartY, nStartZ, nEndX, nEndY, nEndZ ),
+ pUndoDoc ( std::move(pNewUndoDoc) ),
+ theFormulaCell ( rFormulaCell ),
+ theFormulaEnd ( rFormulaEnd ),
+ theRowCell ( rRowCell ),
+ theColCell ( rColCell ),
+ meMode(eMode)
+{
+}
+
+ScUndoTabOp::~ScUndoTabOp()
+{
+}
+
+OUString ScUndoTabOp::GetComment() const
+{
+ return ScResId( STR_UNDO_TABOP ); // "Multiple operation"
+}
+
+void ScUndoTabOp::Undo()
+{
+ BeginUndo();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aRange );
+
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt( nExtFlags, aRange );
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.DeleteAreaTab( aRange,InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ pUndoDoc->CopyToDocument( aRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc );
+ pDocShell->PostPaint( aRange, PaintPartFlags::Grid, nExtFlags );
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+
+ EndUndo();
+}
+
+void ScUndoTabOp::Redo()
+{
+ BeginRedo();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aRange );
+
+ ScTabOpParam aParam(theFormulaCell, theFormulaEnd, theRowCell, theColCell, meMode);
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->TabOp( aParam, false);
+
+ EndRedo();
+}
+
+void ScUndoTabOp::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoTabOp::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoConversion::ScUndoConversion(
+ ScDocShell* pNewDocShell, const ScMarkData& rMark,
+ SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScDocumentUniquePtr pNewUndoDoc,
+ SCCOL nNewX, SCROW nNewY, SCTAB nNewZ, ScDocumentUniquePtr pNewRedoDoc,
+ ScConversionParam aConvParam ) :
+ ScSimpleUndo( pNewDocShell ),
+ aMarkData( rMark ),
+ aCursorPos( nCurX, nCurY, nCurZ ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ aNewCursorPos( nNewX, nNewY, nNewZ ),
+ pRedoDoc( std::move(pNewRedoDoc) ),
+ maConvParam(std::move( aConvParam ))
+{
+ SetChangeTrack();
+}
+
+ScUndoConversion::~ScUndoConversion()
+{
+ pUndoDoc.reset();
+ pRedoDoc.reset();
+}
+
+void ScUndoConversion::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ if ( pUndoDoc )
+ pChangeTrack->AppendContentsIfInRefDoc( *pUndoDoc,
+ nStartChangeAction, nEndChangeAction );
+ else
+ {
+ OSL_FAIL( "ScUndoConversion::SetChangeTrack: no UndoDoc" );
+ nStartChangeAction = nEndChangeAction = 0;
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+OUString ScUndoConversion::GetComment() const
+{
+ OUString aText;
+ switch( maConvParam.GetType() )
+ {
+ case SC_CONVERSION_SPELLCHECK: aText = ScResId( STR_UNDO_SPELLING ); break;
+ case SC_CONVERSION_HANGULHANJA: aText = ScResId( STR_UNDO_HANGULHANJA ); break;
+ case SC_CONVERSION_CHINESE_TRANSL: aText = ScResId( STR_UNDO_CHINESE_TRANSLATION ); break;
+ default: OSL_FAIL( "ScUndoConversion::GetComment - unknown conversion type" );
+ }
+ return aText;
+}
+
+void ScUndoConversion::DoChange( ScDocument* pRefDoc, const ScAddress& rCursorPos )
+{
+ if (pRefDoc)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ShowTable( rCursorPos.Tab() );
+
+ SetViewMarkData( aMarkData );
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ // Undo/Redo-doc has only selected tables
+
+ bool bMulti = aMarkData.IsMultiMarked();
+ pRefDoc->CopyToDocument( 0, 0, 0,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTabCount-1,
+ InsertDeleteFlags::CONTENTS, bMulti, rDoc, &aMarkData );
+
+ // Reset the spell checking results to re-check on paint, otherwise
+ // we show the previous spelling markers (or lack thereof on misspellings).
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ pViewData->GetActiveWin()->ResetAutoSpell();
+ pDocShell->PostPaintGridAll();
+ }
+ else
+ {
+ OSL_FAIL("no Un-/RedoDoc for Un-/RedoSpelling");
+ }
+}
+
+void ScUndoConversion::Undo()
+{
+ BeginUndo();
+ DoChange( pUndoDoc.get(), aCursorPos );
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+ EndUndo();
+}
+
+void ScUndoConversion::Redo()
+{
+ BeginRedo();
+ DoChange( pRedoDoc.get(), aNewCursorPos );
+ SetChangeTrack();
+ EndRedo();
+}
+
+void ScUndoConversion::Repeat( SfxRepeatTarget& rTarget )
+{
+ if( auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget) )
+ pViewTarget->GetViewShell()->DoSheetConversion( maConvParam );
+}
+
+bool ScUndoConversion::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoRefConversion::ScUndoRefConversion( ScDocShell* pNewDocShell,
+ const ScRange& aMarkRange, const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc, bool bNewMulti) :
+ScSimpleUndo( pNewDocShell ),
+aMarkData ( rMark ),
+pUndoDoc ( std::move(pNewUndoDoc) ),
+pRedoDoc ( std::move(pNewRedoDoc) ),
+aRange ( aMarkRange ),
+bMulti ( bNewMulti )
+{
+ assert(pUndoDoc && pRedoDoc);
+ SetChangeTrack();
+}
+
+ScUndoRefConversion::~ScUndoRefConversion()
+{
+ pUndoDoc.reset();
+ pRedoDoc.reset();
+}
+
+OUString ScUndoRefConversion::GetComment() const
+{
+ return ScResId( STR_UNDO_ENTERDATA ); // "Input"
+}
+
+void ScUndoRefConversion::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->AppendContentsIfInRefDoc( *pUndoDoc,
+ nStartChangeAction, nEndChangeAction );
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoRefConversion::DoChange( ScDocument* pRefDoc)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ShowTable(aRange);
+
+ SetViewMarkData( aMarkData );
+
+ ScRange aCopyRange = aRange;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ pRefDoc->CopyToDocument( aCopyRange, InsertDeleteFlags::ALL, bMulti, rDoc, &aMarkData );
+ pDocShell->PostPaint( aRange, PaintPartFlags::Grid);
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+void ScUndoRefConversion::Undo()
+{
+ BeginUndo();
+ if (pUndoDoc)
+ DoChange(pUndoDoc.get());
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+ EndUndo();
+}
+
+void ScUndoRefConversion::Redo()
+{
+ BeginRedo();
+ if (pRedoDoc)
+ DoChange(pRedoDoc.get());
+ SetChangeTrack();
+ EndRedo();
+}
+
+void ScUndoRefConversion::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->DoRefConversion();
+}
+
+bool ScUndoRefConversion::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoRefreshLink::ScUndoRefreshLink(ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pNewUndoDoc)
+ : ScSimpleUndo(pNewDocShell)
+ , xUndoDoc(std::move(pNewUndoDoc))
+{
+}
+
+OUString ScUndoRefreshLink::GetComment() const
+{
+ return ScResId( STR_UNDO_UPDATELINK );
+}
+
+void ScUndoRefreshLink::Undo()
+{
+ BeginUndo();
+
+ bool bMakeRedo = !xRedoDoc;
+ if (bMakeRedo)
+ xRedoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+
+ bool bFirst = true;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nCount; nTab++)
+ if (xUndoDoc->HasTable(nTab))
+ {
+ ScRange aRange(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab);
+ if (bMakeRedo)
+ {
+ if (bFirst)
+ xRedoDoc->InitUndo(rDoc, nTab, nTab, true, true);
+ else
+ xRedoDoc->AddUndoTab(nTab, nTab, true, true);
+ bFirst = false;
+ rDoc.CopyToDocument(aRange, InsertDeleteFlags::ALL, false, *xRedoDoc);
+ xRedoDoc->SetLink(nTab,
+ rDoc.GetLinkMode(nTab),
+ rDoc.GetLinkDoc(nTab),
+ rDoc.GetLinkFlt(nTab),
+ rDoc.GetLinkOpt(nTab),
+ rDoc.GetLinkTab(nTab),
+ rDoc.GetLinkRefreshDelay(nTab));
+ xRedoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
+ }
+
+ rDoc.DeleteAreaTab( aRange,InsertDeleteFlags::ALL );
+ xUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::ALL, false, rDoc);
+ rDoc.SetLink(nTab, xUndoDoc->GetLinkMode(nTab), xUndoDoc->GetLinkDoc(nTab),
+ xUndoDoc->GetLinkFlt(nTab), xUndoDoc->GetLinkOpt(nTab),
+ xUndoDoc->GetLinkTab(nTab),
+ xUndoDoc->GetLinkRefreshDelay(nTab) );
+ rDoc.SetTabBgColor(nTab, xUndoDoc->GetTabBgColor(nTab));
+ }
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->PostPaintExtras();
+
+ EndUndo();
+}
+
+void ScUndoRefreshLink::Redo()
+{
+ OSL_ENSURE(xRedoDoc, "No RedoDoc for ScUndoRefreshLink::Redo");
+
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nCount; nTab++)
+ if (xRedoDoc->HasTable(nTab))
+ {
+ ScRange aRange(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab);
+
+ rDoc.DeleteAreaTab( aRange, InsertDeleteFlags::ALL );
+ xRedoDoc->CopyToDocument(aRange, InsertDeleteFlags::ALL, false, rDoc);
+ rDoc.SetLink(nTab,
+ xRedoDoc->GetLinkMode(nTab),
+ xRedoDoc->GetLinkDoc(nTab),
+ xRedoDoc->GetLinkFlt(nTab),
+ xRedoDoc->GetLinkOpt(nTab),
+ xRedoDoc->GetLinkTab(nTab),
+ xRedoDoc->GetLinkRefreshDelay(nTab) );
+ rDoc.SetTabBgColor(nTab, xRedoDoc->GetTabBgColor(nTab));
+ }
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->PostPaintExtras();
+
+ EndUndo();
+}
+
+void ScUndoRefreshLink::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoRefreshLink::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+static ScAreaLink* lcl_FindAreaLink( const sfx2::LinkManager* pLinkManager, std::u16string_view rDoc,
+ std::u16string_view rFlt, std::u16string_view rOpt,
+ std::u16string_view rSrc, const ScRange& rDest )
+{
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nCount = pLinkManager->GetLinks().size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto pAreaLink = dynamic_cast<ScAreaLink*>( pBase))
+ if ( pAreaLink->IsEqual( rDoc, rFlt, rOpt, rSrc, rDest ) )
+ return pAreaLink;
+ }
+
+ OSL_FAIL("ScAreaLink not found");
+ return nullptr;
+}
+
+ScUndoInsertAreaLink::ScUndoInsertAreaLink( ScDocShell* pShell,
+ OUString aDoc,
+ OUString aFlt, OUString aOpt,
+ OUString aArea, const ScRange& rDestRange,
+ sal_uLong nRefresh )
+ : ScSimpleUndo ( pShell ),
+ aDocName (std::move( aDoc )),
+ aFltName (std::move( aFlt )),
+ aOptions (std::move( aOpt )),
+ aAreaName (std::move( aArea )),
+ aRange ( rDestRange ),
+ nRefreshDelay ( nRefresh )
+{
+}
+
+ScUndoInsertAreaLink::~ScUndoInsertAreaLink()
+{
+}
+
+OUString ScUndoInsertAreaLink::GetComment() const
+{
+ return ScResId( STR_UNDO_INSERTAREALINK );
+}
+
+void ScUndoInsertAreaLink::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ ScAreaLink* pLink = lcl_FindAreaLink( pLinkManager, aDocName, aFltName, aOptions,
+ aAreaName, aRange );
+ if (pLink)
+ pLinkManager->Remove( pLink );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+}
+
+void ScUndoInsertAreaLink::Redo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ ScAreaLink* pLink = new ScAreaLink( pDocShell, aDocName, aFltName, aOptions,
+ aAreaName, aRange.aStart, nRefreshDelay );
+ pLink->SetInCreate( true );
+ pLink->SetDestArea( aRange );
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aDocName, &aFltName, &aAreaName );
+ pLink->Update();
+ pLink->SetInCreate( false );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+}
+
+void ScUndoInsertAreaLink::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoInsertAreaLink::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoRemoveAreaLink::ScUndoRemoveAreaLink( ScDocShell* pShell,
+ OUString aDoc, OUString aFlt, OUString aOpt,
+ OUString aArea, const ScRange& rDestRange,
+ sal_uLong nRefresh )
+ : ScSimpleUndo ( pShell ),
+ aDocName (std::move( aDoc )),
+ aFltName (std::move( aFlt )),
+ aOptions (std::move( aOpt )),
+ aAreaName (std::move( aArea )),
+ aRange ( rDestRange ),
+ nRefreshDelay ( nRefresh )
+{
+}
+
+ScUndoRemoveAreaLink::~ScUndoRemoveAreaLink()
+{
+}
+
+OUString ScUndoRemoveAreaLink::GetComment() const
+{
+ return ScResId( STR_UNDO_REMOVELINK ); //! own text ??
+}
+
+void ScUndoRemoveAreaLink::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ ScAreaLink* pLink = new ScAreaLink( pDocShell, aDocName, aFltName, aOptions,
+ aAreaName, aRange.aStart, nRefreshDelay );
+ pLink->SetInCreate( true );
+ pLink->SetDestArea( aRange );
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aDocName, &aFltName, &aAreaName );
+ pLink->Update();
+ pLink->SetInCreate( false );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+}
+
+void ScUndoRemoveAreaLink::Redo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ ScAreaLink* pLink = lcl_FindAreaLink( pLinkManager, aDocName, aFltName, aOptions,
+ aAreaName, aRange );
+ if (pLink)
+ pLinkManager->Remove( pLink );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+}
+
+void ScUndoRemoveAreaLink::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoRemoveAreaLink::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoUpdateAreaLink::ScUndoUpdateAreaLink( ScDocShell* pShell,
+ OUString aOldD, OUString aOldF, OUString aOldO,
+ OUString aOldA, const ScRange& rOldR, sal_uLong nOldRD,
+ OUString aNewD, OUString aNewF, OUString aNewO,
+ OUString aNewA, const ScRange& rNewR, sal_uLong nNewRD,
+ ScDocumentUniquePtr pUndo, ScDocumentUniquePtr pRedo, bool bDoInsert )
+ : ScSimpleUndo( pShell ),
+ aOldDoc (std::move( aOldD )),
+ aOldFlt (std::move( aOldF )),
+ aOldOpt (std::move( aOldO )),
+ aOldArea (std::move( aOldA )),
+ aOldRange ( rOldR ),
+ aNewDoc (std::move( aNewD )),
+ aNewFlt (std::move( aNewF )),
+ aNewOpt (std::move( aNewO )),
+ aNewArea (std::move( aNewA )),
+ aNewRange ( rNewR ),
+ xUndoDoc ( std::move(pUndo) ),
+ xRedoDoc ( std::move(pRedo) ),
+ nOldRefresh ( nOldRD ),
+ nNewRefresh ( nNewRD ),
+ bWithInsert ( bDoInsert )
+{
+ OSL_ENSURE( aOldRange.aStart == aNewRange.aStart, "AreaLink moved ?" );
+}
+
+OUString ScUndoUpdateAreaLink::GetComment() const
+{
+ return ScResId( STR_UNDO_UPDATELINK ); //! own text ??
+}
+
+void ScUndoUpdateAreaLink::DoChange( const bool bUndo ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SCCOL nEndX = std::max( aOldRange.aEnd.Col(), aNewRange.aEnd.Col() );
+ SCROW nEndY = std::max( aOldRange.aEnd.Row(), aNewRange.aEnd.Row() );
+ SCTAB nEndZ = std::max( aOldRange.aEnd.Tab(), aNewRange.aEnd.Tab() ); //?
+
+ if ( bUndo )
+ {
+ if ( bWithInsert )
+ {
+ rDoc.FitBlock( aNewRange, aOldRange );
+ rDoc.DeleteAreaTab( aOldRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ xUndoDoc->UndoToDocument(aOldRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+ }
+ else
+ {
+ ScRange aCopyRange( aOldRange.aStart, ScAddress(nEndX,nEndY,nEndZ) );
+ rDoc.DeleteAreaTab( aCopyRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ xUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+ }
+ }
+ else
+ {
+ if ( bWithInsert )
+ {
+ rDoc.FitBlock( aOldRange, aNewRange );
+ rDoc.DeleteAreaTab( aNewRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ xRedoDoc->CopyToDocument(aNewRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+ }
+ else
+ {
+ ScRange aCopyRange( aOldRange.aStart, ScAddress(nEndX,nEndY,nEndZ) );
+ rDoc.DeleteAreaTab( aCopyRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ xRedoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+ }
+ }
+
+ ScRange aWorkRange( aNewRange.aStart, ScAddress( nEndX, nEndY, nEndZ ) );
+ rDoc.ExtendMerge( aWorkRange, true );
+
+ // Paint
+
+ if ( aNewRange.aEnd.Col() != aOldRange.aEnd.Col() )
+ aWorkRange.aEnd.SetCol(rDoc.MaxCol());
+ if ( aNewRange.aEnd.Row() != aOldRange.aEnd.Row() )
+ aWorkRange.aEnd.SetRow(rDoc.MaxRow());
+
+ if ( !pDocShell->AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), aWorkRange.aStart.Tab() ) )
+ pDocShell->PostPaint( aWorkRange, PaintPartFlags::Grid );
+
+ pDocShell->PostDataChanged();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+void ScUndoUpdateAreaLink::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+ ScAreaLink* pLink = lcl_FindAreaLink( pLinkManager, aNewDoc, aNewFlt, aNewOpt,
+ aNewArea, aNewRange );
+ if (pLink)
+ {
+ pLink->SetSource( aOldDoc, aOldFlt, aOldOpt, aOldArea ); // old data in Link
+ pLink->SetDestArea( aOldRange );
+ pLink->SetRefreshDelay( nOldRefresh );
+ }
+
+ DoChange(true);
+}
+
+void ScUndoUpdateAreaLink::Redo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+ ScAreaLink* pLink = lcl_FindAreaLink( pLinkManager, aOldDoc, aOldFlt, aOldOpt,
+ aOldArea, aOldRange );
+ if (pLink)
+ {
+ pLink->SetSource( aNewDoc, aNewFlt, aNewOpt, aNewArea ); // new values in link
+ pLink->SetDestArea( aNewRange );
+ pLink->SetRefreshDelay( nNewRefresh );
+ }
+
+ DoChange(false);
+}
+
+void ScUndoUpdateAreaLink::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoUpdateAreaLink::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undocell.cxx b/sc/source/ui/undo/undocell.cxx
new file mode 100644
index 0000000000..84e4a95bcb
--- /dev/null
+++ b/sc/source/ui/undo/undocell.cxx
@@ -0,0 +1,1053 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <undocell.hxx>
+
+#include <scitems.hxx>
+#include <editeng/editobj.hxx>
+#include <sfx2/app.hxx>
+#include <svx/svdocapt.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <document.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <formulacell.hxx>
+#include <target.hxx>
+#include <undoolk.hxx>
+#include <detdata.hxx>
+#include <stlpool.hxx>
+#include <printfun.hxx>
+#include <rangenam.hxx>
+#include <chgtrack.hxx>
+#include <stringutil.hxx>
+#include <utility>
+
+namespace HelperNotifyChanges
+{
+ static void NotifyIfChangesListeners(const ScDocShell& rDocShell, const ScAddress &rPos,
+ const ScUndoEnterData::ValuesType &rOldValues, const OUString& rType = OUString("cell-change"))
+ {
+ ScModelObj* pModelObj = rDocShell.GetModel();
+ if (pModelObj)
+ {
+ ScRangeList aChangeRanges;
+
+ for (const auto & rOldValue : rOldValues)
+ {
+ aChangeRanges.push_back( ScRange(rPos.Col(), rPos.Row(), rOldValue.mnTab));
+ }
+
+ if (getMustPropagateChangesModel(pModelObj))
+ Notify(*pModelObj, aChangeRanges, rType);
+ if (pModelObj) // possibly need to invalidate getCellArea results
+ {
+ Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
+ ? OUString("data-area-invalidate") : OUString("data-area-extend"));
+ }
+ }
+ }
+}
+
+
+ScUndoCursorAttr::ScUndoCursorAttr( ScDocShell* pNewDocShell,
+ SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
+ const ScPatternAttr* pOldPat, const ScPatternAttr* pNewPat,
+ const ScPatternAttr* pApplyPat ) :
+ ScSimpleUndo( pNewDocShell ),
+ nCol( nNewCol ),
+ nRow( nNewRow ),
+ nTab( nNewTab ),
+ pOldEditData( static_cast<EditTextObject*>(nullptr) ),
+ pNewEditData( static_cast<EditTextObject*>(nullptr) )
+{
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ pNewPattern = const_cast<ScPatternAttr*>( &pPool->DirectPutItemInPool( *pNewPat ) );
+ pOldPattern = const_cast<ScPatternAttr*>( &pPool->DirectPutItemInPool( *pOldPat ) );
+ pApplyPattern = const_cast<ScPatternAttr*>( &pPool->DirectPutItemInPool( *pApplyPat ) );
+}
+
+ScUndoCursorAttr::~ScUndoCursorAttr()
+{
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ pPool->DirectRemoveItemFromPool(*pNewPattern);
+ pPool->DirectRemoveItemFromPool(*pOldPattern);
+ pPool->DirectRemoveItemFromPool(*pApplyPattern);
+}
+
+OUString ScUndoCursorAttr::GetComment() const
+{
+ //! own text for automatic attribution
+ return ScResId( STR_UNDO_CURSORATTR ); // "Attribute"
+}
+
+void ScUndoCursorAttr::SetEditData( std::unique_ptr<EditTextObject> pOld, std::unique_ptr<EditTextObject> pNew )
+{
+ pOldEditData = std::move(pOld);
+ pNewEditData = std::move(pNew);
+}
+
+void ScUndoCursorAttr::DoChange( const ScPatternAttr* pWhichPattern, const std::unique_ptr<EditTextObject>& pEditData ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScAddress aPos(nCol, nRow, nTab);
+ rDoc.SetPattern( nCol, nRow, nTab, *pWhichPattern );
+
+ if (rDoc.GetCellType(aPos) == CELLTYPE_EDIT && pEditData)
+ rDoc.SetEditText(aPos, *pEditData, nullptr);
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->SetTabNo( nTab );
+ pViewShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_JUMP, false, false );
+ pViewShell->AdjustBlockHeight();
+ }
+
+ const SfxItemSet& rApplySet = pApplyPattern->GetItemSet();
+ bool bPaintExt = ( rApplySet.GetItemState( ATTR_SHADOW ) != SfxItemState::DEFAULT ||
+ rApplySet.GetItemState( ATTR_CONDITIONAL ) != SfxItemState::DEFAULT );
+ bool bPaintRows = ( rApplySet.GetItemState( ATTR_HOR_JUSTIFY ) != SfxItemState::DEFAULT );
+
+ sal_uInt16 nFlags = SC_PF_TESTMERGE;
+ if (bPaintExt)
+ nFlags |= SC_PF_LINES;
+ if (bPaintRows)
+ nFlags |= SC_PF_WHOLEROWS;
+ pDocShell->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nFlags );
+}
+
+void ScUndoCursorAttr::Undo()
+{
+ BeginUndo();
+ DoChange(pOldPattern, pOldEditData);
+ EndUndo();
+}
+
+void ScUndoCursorAttr::Redo()
+{
+ BeginRedo();
+ DoChange(pNewPattern, pNewEditData);
+ EndRedo();
+}
+
+void ScUndoCursorAttr::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->ApplySelectionPattern( *pApplyPattern );
+}
+
+bool ScUndoCursorAttr::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoEnterData::Value::Value() : mnTab(-1), mbHasFormat(false), mnFormat(0) {}
+
+ScUndoEnterData::ScUndoEnterData(
+ ScDocShell* pNewDocShell, const ScAddress& rPos, ValuesType& rOldValues,
+ OUString aNewStr, std::unique_ptr<EditTextObject> pObj ) :
+ ScSimpleUndo( pNewDocShell ),
+ maNewString(std::move(aNewStr)),
+ mpNewEditData(std::move(pObj)),
+ mnEndChangeAction(0),
+ maPos(rPos)
+{
+ maOldValues.swap(rOldValues);
+
+ SetChangeTrack();
+}
+
+OUString ScUndoEnterData::GetComment() const
+{
+ return ScResId( STR_UNDO_ENTERDATA ); // "Input"
+}
+
+void ScUndoEnterData::DoChange() const
+{
+ // only when needed (old or new Edit cell, or Attribute)?
+ bool bHeightChanged = false;
+ for (const auto & i : maOldValues)
+ {
+ if (pDocShell->AdjustRowHeight(maPos.Row(), maPos.Row(), i.mnTab))
+ bHeightChanged = true;
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ if (comphelper::LibreOfficeKit::isActive() && bHeightChanged)
+ {
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, ROW_HEADER, maPos.Tab());
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell, false /* bColumns */, true /* bRows */, true /* bSizes*/,
+ false /* bHidden */, false /* bFiltered */, false /* bGroups */, maPos.Tab());
+ }
+ pViewShell->SetTabNo(maPos.Tab());
+ pViewShell->MoveCursorAbs(maPos.Col(), maPos.Row(), SC_FOLLOW_JUMP, false, false);
+ }
+
+ pDocShell->PostDataChanged();
+}
+
+void ScUndoEnterData::SetChangeTrack()
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ mnEndChangeAction = pChangeTrack->GetActionMax() + 1;
+ ScAddress aPos(maPos);
+ for (const Value & rOldValue : maOldValues)
+ {
+ aPos.SetTab(rOldValue.mnTab);
+ sal_uLong nFormat = 0;
+ if (rOldValue.mbHasFormat)
+ nFormat = rOldValue.mnFormat;
+ pChangeTrack->AppendContent(aPos, rOldValue.maCell, nFormat);
+ }
+ if ( mnEndChangeAction > pChangeTrack->GetActionMax() )
+ mnEndChangeAction = 0; // nothing is appended
+ }
+ else
+ mnEndChangeAction = 0;
+}
+
+void ScUndoEnterData::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (const Value & rVal : maOldValues)
+ {
+ ScCellValue aNewCell;
+ aNewCell.assign(rVal.maCell, rDoc, ScCloneFlags::StartListening);
+ ScAddress aPos = maPos;
+ aPos.SetTab(rVal.mnTab);
+ aNewCell.release(rDoc, aPos);
+
+ if (rVal.mbHasFormat)
+ rDoc.ApplyAttr(maPos.Col(), maPos.Row(), rVal.mnTab,
+ SfxUInt32Item(ATTR_VALUE_FORMAT, rVal.mnFormat));
+ else
+ {
+ auto pPattern = std::make_unique<ScPatternAttr>(*rDoc.GetPattern(maPos.Col(), maPos.Row(), rVal.mnTab));
+ pPattern->GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
+ rDoc.SetPattern(maPos.Col(), maPos.Row(), rVal.mnTab, std::move(pPattern));
+ }
+ pDocShell->PostPaintCell(maPos.Col(), maPos.Row(), rVal.mnTab);
+ }
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ size_t nCount = maOldValues.size();
+ if ( pChangeTrack && mnEndChangeAction >= sal::static_int_cast<sal_uLong>(nCount) )
+ pChangeTrack->Undo( mnEndChangeAction - nCount + 1, mnEndChangeAction );
+
+ DoChange();
+ EndUndo();
+
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocShell, maPos, maOldValues, "undo");
+}
+
+void ScUndoEnterData::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (const Value & rOldValue : maOldValues)
+ {
+ SCTAB nTab = rOldValue.mnTab;
+ if (mpNewEditData)
+ {
+ ScAddress aPos = maPos;
+ aPos.SetTab(nTab);
+ // edit text will be cloned.
+ rDoc.SetEditText(aPos, *mpNewEditData, nullptr);
+ }
+ else
+ rDoc.SetString(maPos.Col(), maPos.Row(), nTab, maNewString);
+
+ pDocShell->PostPaintCell(maPos.Col(), maPos.Row(), nTab);
+ }
+
+ SetChangeTrack();
+
+ DoChange();
+ EndRedo();
+
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocShell, maPos, maOldValues, "redo");
+}
+
+void ScUndoEnterData::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ OUString aTemp = maNewString;
+ pViewTarget->GetViewShell()->EnterDataAtCursor( aTemp );
+ }
+}
+
+bool ScUndoEnterData::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoEnterValue::ScUndoEnterValue(
+ ScDocShell* pNewDocShell, const ScAddress& rNewPos,
+ ScCellValue aUndoCell, double nVal ) :
+ ScSimpleUndo( pNewDocShell ),
+ aPos ( rNewPos ),
+ maOldCell(std::move(aUndoCell)),
+ nValue ( nVal )
+{
+ SetChangeTrack();
+}
+
+ScUndoEnterValue::~ScUndoEnterValue()
+{
+}
+
+OUString ScUndoEnterValue::GetComment() const
+{
+ return ScResId( STR_UNDO_ENTERDATA ); // "Input"
+}
+
+void ScUndoEnterValue::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ nEndChangeAction = pChangeTrack->GetActionMax() + 1;
+ pChangeTrack->AppendContent(aPos, maOldCell);
+ if ( nEndChangeAction > pChangeTrack->GetActionMax() )
+ nEndChangeAction = 0; // nothing is appended
+ }
+ else
+ nEndChangeAction = 0;
+}
+
+void ScUndoEnterValue::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScCellValue aNewCell;
+ aNewCell.assign(maOldCell, rDoc, ScCloneFlags::StartListening);
+ aNewCell.release(rDoc, aPos);
+
+ pDocShell->PostPaintCell( aPos );
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nEndChangeAction, nEndChangeAction );
+
+ EndUndo();
+}
+
+void ScUndoEnterValue::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.SetValue( aPos.Col(), aPos.Row(), aPos.Tab(), nValue );
+ pDocShell->PostPaintCell( aPos );
+
+ SetChangeTrack();
+
+ EndRedo();
+}
+
+void ScUndoEnterValue::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoEnterValue::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoSetCell::ScUndoSetCell( ScDocShell* pDocSh, const ScAddress& rPos, ScCellValue aOldVal, ScCellValue aNewVal ) :
+ ScSimpleUndo(pDocSh), maPos(rPos), maOldValue(std::move(aOldVal)), maNewValue(std::move(aNewVal)), mnEndChangeAction(0)
+{
+ SetChangeTrack();
+}
+
+ScUndoSetCell::~ScUndoSetCell() {}
+
+void ScUndoSetCell::Undo()
+{
+ BeginUndo();
+ SetValue(maOldValue);
+ MoveCursorToCell();
+ pDocShell->PostPaintCell(maPos);
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if (pChangeTrack)
+ pChangeTrack->Undo(mnEndChangeAction, mnEndChangeAction);
+
+ EndUndo();
+}
+
+void ScUndoSetCell::Redo()
+{
+ BeginRedo();
+ SetValue(maNewValue);
+ MoveCursorToCell();
+ pDocShell->PostPaintCell(maPos);
+ SetChangeTrack();
+ EndRedo();
+}
+
+void ScUndoSetCell::Repeat( SfxRepeatTarget& /*rTarget*/ )
+{
+ // Makes no sense.
+}
+
+bool ScUndoSetCell::CanRepeat( SfxRepeatTarget& /*rTarget*/ ) const
+{
+ return false;
+}
+
+OUString ScUndoSetCell::GetComment() const
+{
+ return ScResId(STR_UNDO_ENTERDATA); // "Input"
+}
+
+void ScUndoSetCell::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if (pChangeTrack)
+ {
+ mnEndChangeAction = pChangeTrack->GetActionMax() + 1;
+
+ pChangeTrack->AppendContent(maPos, maOldValue);
+
+ if (mnEndChangeAction > pChangeTrack->GetActionMax())
+ mnEndChangeAction = 0; // Nothing is appended
+ }
+ else
+ mnEndChangeAction = 0;
+}
+
+void ScUndoSetCell::SetValue( const ScCellValue& rVal )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ switch (rVal.getType())
+ {
+ case CELLTYPE_NONE:
+ // empty cell
+ rDoc.SetEmptyCell(maPos);
+ break;
+ case CELLTYPE_VALUE:
+ rDoc.SetValue(maPos, rVal.getDouble());
+ break;
+ case CELLTYPE_STRING:
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ // Undo only cell content, without setting any number format.
+ aParam.meSetTextNumFormat = ScSetStringParam::Keep;
+ rDoc.SetString(maPos, rVal.getSharedString()->getString(), &aParam);
+ }
+ break;
+ case CELLTYPE_EDIT:
+ rDoc.SetEditText(maPos, rVal.getEditText()->Clone());
+ break;
+ case CELLTYPE_FORMULA:
+ rDoc.SetFormulaCell(maPos, rVal.getFormula()->Clone());
+ break;
+ default:
+ ;
+ }
+}
+
+void ScUndoSetCell::MoveCursorToCell()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if ( pViewShell )
+ {
+ pViewShell->SetTabNo( maPos.Tab() );
+ pViewShell->MoveCursorAbs( maPos.Col(), maPos.Row(), SC_FOLLOW_JUMP, false, false );
+ }
+}
+
+ScUndoPageBreak::ScUndoPageBreak( ScDocShell* pNewDocShell,
+ SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
+ bool bNewColumn, bool bNewInsert ) :
+ ScSimpleUndo( pNewDocShell ),
+ nCol( nNewCol ),
+ nRow( nNewRow ),
+ nTab( nNewTab ),
+ bColumn( bNewColumn ),
+ bInsert( bNewInsert )
+{
+}
+
+ScUndoPageBreak::~ScUndoPageBreak()
+{
+}
+
+OUString ScUndoPageBreak::GetComment() const
+{
+ //"Column break" | "Row break" "insert" | "delete"
+ return bColumn ?
+ ( bInsert ?
+ ScResId( STR_UNDO_INSCOLBREAK ) :
+ ScResId( STR_UNDO_DELCOLBREAK )
+ ) :
+ ( bInsert ?
+ ScResId( STR_UNDO_INSROWBREAK ) :
+ ScResId( STR_UNDO_DELROWBREAK )
+ );
+}
+
+void ScUndoPageBreak::DoChange( bool bInsertP ) const
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ if (pViewShell)
+ {
+ pViewShell->SetTabNo( nTab );
+ pViewShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_JUMP, false, false );
+
+ if (bInsertP)
+ pViewShell->InsertPageBreak(bColumn, false);
+ else
+ pViewShell->DeletePageBreak(bColumn, false);
+
+ pDocShell->GetDocument().InvalidatePageBreaks(nTab);
+ }
+}
+
+void ScUndoPageBreak::Undo()
+{
+ BeginUndo();
+ DoChange(!bInsert);
+ EndUndo();
+}
+
+void ScUndoPageBreak::Redo()
+{
+ BeginRedo();
+ DoChange(bInsert);
+ EndRedo();
+}
+
+void ScUndoPageBreak::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+
+ if (bInsert)
+ rViewShell.InsertPageBreak(bColumn);
+ else
+ rViewShell.DeletePageBreak(bColumn);
+ }
+}
+
+bool ScUndoPageBreak::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoPrintZoom::ScUndoPrintZoom( ScDocShell* pNewDocShell,
+ SCTAB nT, sal_uInt16 nOS, sal_uInt16 nOP, sal_uInt16 nNS, sal_uInt16 nNP ) :
+ ScSimpleUndo( pNewDocShell ),
+ nTab( nT ),
+ nOldScale( nOS ),
+ nOldPages( nOP ),
+ nNewScale( nNS ),
+ nNewPages( nNP )
+{
+}
+
+ScUndoPrintZoom::~ScUndoPrintZoom()
+{
+}
+
+OUString ScUndoPrintZoom::GetComment() const
+{
+ return ScResId( STR_UNDO_PRINTSCALE );
+}
+
+void ScUndoPrintZoom::DoChange( bool bUndo )
+{
+ sal_uInt16 nScale = bUndo ? nOldScale : nNewScale;
+ sal_uInt16 nPages = bUndo ? nOldPages : nNewPages;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OUString aStyleName = rDoc.GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, nScale ) );
+ rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, nPages ) );
+
+ ScPrintFunc aPrintFunc( pDocShell, pDocShell->GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ }
+}
+
+void ScUndoPrintZoom::Undo()
+{
+ BeginUndo();
+ DoChange(true);
+ EndUndo();
+}
+
+void ScUndoPrintZoom::Redo()
+{
+ BeginRedo();
+ DoChange(false);
+ EndRedo();
+}
+
+void ScUndoPrintZoom::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+ ScViewData& rViewData = rViewShell.GetViewData();
+ rViewData.GetDocShell()->SetPrintZoom( rViewData.GetTabNo(), nNewScale, nNewPages );
+ }
+}
+
+bool ScUndoPrintZoom::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoThesaurus::ScUndoThesaurus(
+ ScDocShell* pNewDocShell, SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
+ ScCellValue aOldText, ScCellValue aNewText ) :
+ ScSimpleUndo( pNewDocShell ),
+ nCol( nNewCol ),
+ nRow( nNewRow ),
+ nTab( nNewTab ),
+ maOldText(std::move(aOldText)),
+ maNewText(std::move(aNewText))
+{
+ SetChangeTrack(maOldText);
+}
+
+ScUndoThesaurus::~ScUndoThesaurus() {}
+
+OUString ScUndoThesaurus::GetComment() const
+{
+ return ScResId( STR_UNDO_THESAURUS ); // "Thesaurus"
+}
+
+void ScUndoThesaurus::SetChangeTrack( const ScCellValue& rOldCell )
+{
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ nEndChangeAction = pChangeTrack->GetActionMax() + 1;
+ pChangeTrack->AppendContent(ScAddress(nCol, nRow, nTab), rOldCell);
+ if ( nEndChangeAction > pChangeTrack->GetActionMax() )
+ nEndChangeAction = 0; // nothing is appended
+ }
+ else
+ nEndChangeAction = 0;
+}
+
+void ScUndoThesaurus::DoChange( bool bUndo, const ScCellValue& rText )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->SetTabNo( nTab );
+ pViewShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_JUMP, false, false );
+ }
+
+ ScAddress aPos(nCol, nRow, nTab);
+ rText.commit(rDoc, aPos);
+ if (!bUndo)
+ SetChangeTrack(maOldText);
+
+ pDocShell->PostPaintCell( nCol, nRow, nTab );
+}
+
+void ScUndoThesaurus::Undo()
+{
+ BeginUndo();
+ DoChange(true, maOldText);
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nEndChangeAction, nEndChangeAction );
+ EndUndo();
+}
+
+void ScUndoThesaurus::Redo()
+{
+ BeginRedo();
+ DoChange(false, maNewText);
+ EndRedo();
+}
+
+void ScUndoThesaurus::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->DoThesaurus();
+}
+
+bool ScUndoThesaurus::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+
+ScUndoReplaceNote::ScUndoReplaceNote( ScDocShell& rDocShell, const ScAddress& rPos,
+ const ScNoteData& rNoteData, bool bInsert, std::unique_ptr<SdrUndoAction> pDrawUndo ) :
+ ScSimpleUndo( &rDocShell ),
+ maPos( rPos ),
+ mpDrawUndo( std::move(pDrawUndo) )
+{
+ OSL_ENSURE( rNoteData.mxCaption, "ScUndoReplaceNote::ScUndoReplaceNote - missing note caption" );
+ if (bInsert)
+ {
+ maNewData = rNoteData;
+ }
+ else
+ {
+ maOldData = rNoteData;
+ }
+}
+
+ScUndoReplaceNote::ScUndoReplaceNote( ScDocShell& rDocShell, const ScAddress& rPos,
+ ScNoteData aOldData, ScNoteData aNewData, std::unique_ptr<SdrUndoAction> pDrawUndo ) :
+ ScSimpleUndo( &rDocShell ),
+ maPos( rPos ),
+ maOldData(std::move( aOldData )),
+ maNewData(std::move( aNewData )),
+ mpDrawUndo( std::move(pDrawUndo) )
+{
+ OSL_ENSURE( maOldData.mxCaption || maNewData.mxCaption, "ScUndoReplaceNote::ScUndoReplaceNote - missing note captions" );
+ OSL_ENSURE( !maOldData.mxInitData && !maNewData.mxInitData, "ScUndoReplaceNote::ScUndoReplaceNote - unexpected uninitialized note" );
+}
+
+ScUndoReplaceNote::~ScUndoReplaceNote()
+{
+ mpDrawUndo.reset();
+}
+
+void ScUndoReplaceNote::Undo()
+{
+ BeginUndo();
+ DoSdrUndoAction( mpDrawUndo.get(), &pDocShell->GetDocument() );
+ /* Undo insert -> remove new note.
+ Undo remove -> insert old note.
+ Undo replace -> remove new note, insert old note. */
+ DoRemoveNote( maNewData );
+ DoInsertNote( maOldData );
+ pDocShell->PostPaintCell( maPos );
+ EndUndo();
+}
+
+void ScUndoReplaceNote::Redo()
+{
+ BeginRedo();
+ RedoSdrUndoAction( mpDrawUndo.get() );
+ /* Redo insert -> insert new note.
+ Redo remove -> remove old note.
+ Redo replace -> remove old note, insert new note. */
+ DoRemoveNote( maOldData );
+ DoInsertNote( maNewData );
+ pDocShell->PostPaintCell( maPos );
+ EndRedo();
+}
+
+void ScUndoReplaceNote::Repeat( SfxRepeatTarget& /*rTarget*/ )
+{
+}
+
+bool ScUndoReplaceNote::CanRepeat( SfxRepeatTarget& /*rTarget*/ ) const
+{
+ return false;
+}
+
+OUString ScUndoReplaceNote::GetComment() const
+{
+ return ScResId( maNewData.mxCaption ?
+ (maOldData.mxCaption ? STR_UNDO_EDITNOTE : STR_UNDO_INSERTNOTE) : STR_UNDO_DELETENOTE );
+}
+
+void ScUndoReplaceNote::DoInsertNote( const ScNoteData& rNoteData )
+{
+ if( rNoteData.mxCaption )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OSL_ENSURE( !rDoc.GetNote(maPos), "ScUndoReplaceNote::DoInsertNote - unexpected cell note" );
+ ScPostIt* pNote = new ScPostIt( rDoc, maPos, rNoteData, false );
+ rDoc.SetNote( maPos, std::unique_ptr<ScPostIt>(pNote) );
+ ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, rDoc, maPos, pNote);
+ }
+}
+
+void ScUndoReplaceNote::DoRemoveNote( const ScNoteData& rNoteData )
+{
+ if( !rNoteData.mxCaption )
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OSL_ENSURE( rDoc.GetNote(maPos), "ScUndoReplaceNote::DoRemoveNote - missing cell note" );
+ if( std::unique_ptr<ScPostIt> pNote = rDoc.ReleaseNote( maPos ) )
+ {
+ /* Forget pointer to caption object to suppress removing the
+ caption object from the drawing layer while deleting pNote
+ (removing the caption is done by a drawing undo action). */
+ pNote->ForgetCaption();
+ ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Remove, rDoc, maPos, pNote.get());
+ }
+}
+
+ScUndoShowHideNote::ScUndoShowHideNote( ScDocShell& rDocShell, const ScAddress& rPos, bool bShow ) :
+ ScSimpleUndo( &rDocShell ),
+ maPos( rPos ),
+ mbShown( bShow )
+{
+}
+
+ScUndoShowHideNote::~ScUndoShowHideNote()
+{
+}
+
+void ScUndoShowHideNote::Undo()
+{
+ BeginUndo();
+ if( ScPostIt* pNote = pDocShell->GetDocument().GetNote(maPos) )
+ pNote->ShowCaption( maPos, !mbShown );
+ EndUndo();
+}
+
+void ScUndoShowHideNote::Redo()
+{
+ BeginRedo();
+ if( ScPostIt* pNote = pDocShell->GetDocument().GetNote(maPos) )
+ pNote->ShowCaption( maPos, mbShown );
+ EndRedo();
+}
+
+void ScUndoShowHideNote::Repeat( SfxRepeatTarget& /*rTarget*/ )
+{
+}
+
+bool ScUndoShowHideNote::CanRepeat( SfxRepeatTarget& /*rTarget*/ ) const
+{
+ return false;
+}
+
+OUString ScUndoShowHideNote::GetComment() const
+{
+ return ScResId( mbShown ? STR_UNDO_SHOWNOTE : STR_UNDO_HIDENOTE );
+}
+
+ScUndoDetective::ScUndoDetective( ScDocShell* pNewDocShell,
+ std::unique_ptr<SdrUndoAction> pDraw, const ScDetOpData* pOperation,
+ std::unique_ptr<ScDetOpList> pUndoList ) :
+ ScSimpleUndo( pNewDocShell ),
+ pOldList ( std::move(pUndoList) ),
+ nAction ( 0 ),
+ pDrawUndo ( std::move(pDraw) )
+{
+ bIsDelete = ( pOperation == nullptr );
+ if (!bIsDelete)
+ {
+ nAction = static_cast<sal_uInt16>(pOperation->GetOperation());
+ aPos = pOperation->GetPos();
+ }
+}
+
+ScUndoDetective::~ScUndoDetective()
+{
+ pDrawUndo.reset();
+ pOldList.reset();
+}
+
+OUString ScUndoDetective::GetComment() const
+{
+ TranslateId pId = STR_UNDO_DETDELALL;
+ if ( !bIsDelete )
+ switch ( static_cast<ScDetOpType>(nAction) )
+ {
+ case SCDETOP_ADDSUCC: pId = STR_UNDO_DETADDSUCC; break;
+ case SCDETOP_DELSUCC: pId = STR_UNDO_DETDELSUCC; break;
+ case SCDETOP_ADDPRED: pId = STR_UNDO_DETADDPRED; break;
+ case SCDETOP_DELPRED: pId = STR_UNDO_DETDELPRED; break;
+ case SCDETOP_ADDERROR: pId = STR_UNDO_DETADDERROR; break;
+ }
+
+ return ScResId(pId);
+}
+
+void ScUndoDetective::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ DoSdrUndoAction(pDrawUndo.get(), &rDoc);
+
+ if (bIsDelete)
+ {
+ if ( pOldList )
+ rDoc.SetDetOpList( std::unique_ptr<ScDetOpList>(new ScDetOpList(*pOldList)) );
+ }
+ else
+ {
+ // Remove entry from list
+
+ ScDetOpList* pList = rDoc.GetDetOpList();
+ if (pList && pList->Count())
+ {
+ ScDetOpDataVector& rVec = pList->GetDataVector();
+ ScDetOpDataVector::iterator it = rVec.begin() + rVec.size() - 1;
+ if ( it->GetOperation() == static_cast<ScDetOpType>(nAction) && it->GetPos() == aPos )
+ rVec.erase( it);
+ else
+ {
+ OSL_FAIL("Detective entry could not be found in list");
+ }
+ }
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->RecalcPPT(); //! use broadcast instead?
+
+ EndUndo();
+}
+
+void ScUndoDetective::Redo()
+{
+ BeginRedo();
+
+ RedoSdrUndoAction(pDrawUndo.get());
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ if (bIsDelete)
+ rDoc.ClearDetectiveOperations();
+ else
+ rDoc.AddDetectiveOperation( ScDetOpData( aPos, static_cast<ScDetOpType>(nAction) ) );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->RecalcPPT(); //! use broadcast instead?
+
+ EndRedo();
+}
+
+void ScUndoDetective::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoDetective::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoRangeNames::ScUndoRangeNames( ScDocShell* pNewDocShell,
+ std::unique_ptr<ScRangeName> pOld, std::unique_ptr<ScRangeName> pNew, SCTAB nTab ) :
+ ScSimpleUndo( pNewDocShell ),
+ pOldRanges ( std::move(pOld) ),
+ pNewRanges ( std::move(pNew) ),
+ mnTab ( nTab )
+{
+}
+
+ScUndoRangeNames::~ScUndoRangeNames()
+{
+ pOldRanges.reset();
+ pNewRanges.reset();
+}
+
+OUString ScUndoRangeNames::GetComment() const
+{
+ return ScResId( STR_UNDO_RANGENAMES );
+}
+
+void ScUndoRangeNames::DoChange( bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.PreprocessRangeNameUpdate();
+
+ if ( bUndo )
+ {
+ auto p = std::make_unique<ScRangeName>(*pOldRanges);
+ if (mnTab >= 0)
+ rDoc.SetRangeName( mnTab, std::move(p) );
+ else
+ rDoc.SetRangeName( std::move(p) );
+ }
+ else
+ {
+ auto p = std::make_unique<ScRangeName>(*pNewRanges);
+ if (mnTab >= 0)
+ rDoc.SetRangeName( mnTab, std::move(p) );
+ else
+ rDoc.SetRangeName( std::move(p) );
+ }
+
+ rDoc.CompileHybridFormula();
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+}
+
+void ScUndoRangeNames::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoRangeNames::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoRangeNames::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoRangeNames::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undocell2.cxx b/sc/source/ui/undo/undocell2.cxx
new file mode 100644
index 0000000000..2222afa422
--- /dev/null
+++ b/sc/source/ui/undo/undocell2.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/.
+ */
+
+#include <undocell.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <cellvalues.hxx>
+#include <formulacell.hxx>
+
+namespace sc {
+
+UndoSetCells::UndoSetCells( ScDocShell* pDocSh, const ScAddress& rTopPos ) :
+ ScSimpleUndo(pDocSh), maTopPos(rTopPos) {}
+
+UndoSetCells::~UndoSetCells() {}
+
+void UndoSetCells::DoChange( const CellValues& rValues )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.CopyCellValuesFrom(maTopPos, rValues);
+
+ ScRange aRange(maTopPos);
+ aRange.aEnd.IncRow(rValues.size());
+ BroadcastChanges(aRange);
+ pDocShell->PostPaintGridAll();
+}
+
+void UndoSetCells::Undo()
+{
+ BeginUndo();
+ DoChange(maOldValues);
+ EndUndo();
+}
+
+void UndoSetCells::Redo()
+{
+ BeginRedo();
+ DoChange(maNewValues);
+ EndRedo();
+}
+
+bool UndoSetCells::CanRepeat( SfxRepeatTarget& ) const
+{
+ return false;
+}
+
+OUString UndoSetCells::GetComment() const
+{
+ // "Input"
+ return ScResId(STR_UNDO_ENTERDATA);
+}
+
+void UndoSetCells::SetNewValues( const std::vector<double>& rVals )
+{
+ maNewValues.assign(rVals);
+}
+
+void UndoSetCells::SetNewValues( const std::vector<ScFormulaCell*>& rVals )
+{
+ maNewValues.assign(rVals);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoconvert.cxx b/sc/source/ui/undo/undoconvert.cxx
new file mode 100644
index 0000000000..ea9facfc22
--- /dev/null
+++ b/sc/source/ui/undo/undoconvert.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/.
+ */
+
+#include <undoconvert.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <undoutil.hxx>
+
+namespace sc {
+
+UndoFormulaToValue::UndoFormulaToValue( ScDocShell* pDocSh, TableValues& rUndoValues ) :
+ ScSimpleUndo(pDocSh)
+{
+ maUndoValues.swap(rUndoValues);
+}
+
+OUString UndoFormulaToValue::GetComment() const
+{
+ return ScResId(STR_UNDO_FORMULA_TO_VALUE);
+}
+
+void UndoFormulaToValue::Undo()
+{
+ Execute();
+}
+
+void UndoFormulaToValue::Redo()
+{
+ Execute();
+}
+
+void UndoFormulaToValue::Execute()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.SwapNonEmpty(maUndoValues);
+
+ ScUndoUtil::MarkSimpleBlock(pDocShell, maUndoValues.getRange());
+
+ pDocShell->PostPaint(maUndoValues.getRange(), PaintPartFlags::Grid);
+ pDocShell->PostDataChanged();
+ rDoc.BroadcastCells(maUndoValues.getRange(), SfxHintId::ScDataChanged);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undodat.cxx b/sc/source/ui/undo/undodat.cxx
new file mode 100644
index 0000000000..498060839a
--- /dev/null
+++ b/sc/source/ui/undo/undodat.cxx
@@ -0,0 +1,1926 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/app.hxx>
+#include <svx/svdundo.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <undodat.hxx>
+#include <undoutil.hxx>
+#include <undoolk.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <olinetab.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <globalnames.hxx>
+#include <target.hxx>
+#include <dbdocfun.hxx>
+#include <olinefun.hxx>
+#include <dpobject.hxx>
+#include <attrib.hxx>
+#include <hints.hxx>
+#include <chgtrack.hxx>
+#include <refundo.hxx>
+#include <markdata.hxx>
+#include <utility>
+
+// Show or hide outline groups
+
+ScUndoDoOutline::ScUndoDoOutline( ScDocShell* pNewDocShell,
+ SCCOLROW nNewStart, SCCOLROW nNewEnd, SCTAB nNewTab,
+ ScDocumentUniquePtr pNewUndoDoc, bool bNewColumns,
+ sal_uInt16 nNewLevel, sal_uInt16 nNewEntry, bool bNewShow ) :
+ ScSimpleUndo( pNewDocShell ),
+ nStart( nNewStart ),
+ nEnd( nNewEnd ),
+ nTab( nNewTab ),
+ pUndoDoc( std::move(pNewUndoDoc) ),
+ bColumns( bNewColumns ),
+ nLevel( nNewLevel ),
+ nEntry( nNewEntry ),
+ bShow( bNewShow )
+{
+}
+
+ScUndoDoOutline::~ScUndoDoOutline()
+{
+}
+
+OUString ScUndoDoOutline::GetComment() const
+{ // Show outline" "Hide outline"
+ return bShow ?
+ ScResId( STR_UNDO_DOOUTLINE ) :
+ ScResId( STR_UNDO_REDOOUTLINE );
+}
+
+void ScUndoDoOutline::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ // sheet has to be switched over (#46952#)!
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ // perform the inverse function
+
+ if (bShow)
+ pViewShell->HideOutline( bColumns, nLevel, nEntry, false, false );
+ else
+ pViewShell->ShowOutline( bColumns, nLevel, nEntry, false, false );
+
+ // Original column/row status
+ if (bColumns)
+ pUndoDoc->CopyToDocument(static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, rDoc);
+ else
+ pUndoDoc->CopyToDocument(0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, bColumns, !bColumns,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ pViewShell->UpdateScrollBars();
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top);
+
+ EndUndo();
+}
+
+void ScUndoDoOutline::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ // sheet has to be switched over (#46952#)!
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ if (bShow)
+ pViewShell->ShowOutline( bColumns, nLevel, nEntry, false );
+ else
+ pViewShell->HideOutline( bColumns, nLevel, nEntry, false );
+
+ EndRedo();
+}
+
+void ScUndoDoOutline::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoDoOutline::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // is not possible
+}
+
+/** Make or delete outline groups */
+ScUndoMakeOutline::ScUndoMakeOutline( ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ std::unique_ptr<ScOutlineTable> pNewUndoTab, bool bNewColumns, bool bNewMake ) :
+ ScSimpleUndo( pNewDocShell ),
+ aBlockStart( nStartX, nStartY, nStartZ ),
+ aBlockEnd( nEndX, nEndY, nEndZ ),
+ pUndoTable( std::move(pNewUndoTab) ),
+ bColumns( bNewColumns ),
+ bMake( bNewMake )
+{
+}
+
+ScUndoMakeOutline::~ScUndoMakeOutline()
+{
+}
+
+OUString ScUndoMakeOutline::GetComment() const
+{ // "Grouping" "Undo grouping"
+ return bMake ?
+ ScResId( STR_UNDO_MAKEOUTLINE ) :
+ ScResId( STR_UNDO_REMAKEOUTLINE );
+}
+
+void ScUndoMakeOutline::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTab = aBlockStart.Tab();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aBlockStart, aBlockEnd );
+
+ rDoc.SetOutlineTable( nTab, pUndoTable.get() );
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top|PaintPartFlags::Size);
+
+ ScTabViewShell::notifyAllViewsHeaderInvalidation( pViewShell, bColumns ? COLUMN_HEADER : ROW_HEADER, nTab );
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell,
+ bColumns /* bColumns */, !bColumns /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+
+ EndUndo();
+}
+
+void ScUndoMakeOutline::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aBlockStart, aBlockEnd );
+
+ if (bMake)
+ pViewShell->MakeOutline( bColumns, false );
+ else
+ pViewShell->RemoveOutline( bColumns, false );
+
+ pDocShell->PostPaint(0,0,aBlockStart.Tab(),rDoc.MaxCol(),rDoc.MaxRow(),aBlockEnd.Tab(),PaintPartFlags::Grid);
+
+ EndRedo();
+}
+
+void ScUndoMakeOutline::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+
+ if (bMake)
+ rViewShell.MakeOutline( bColumns );
+ else
+ rViewShell.RemoveOutline( bColumns );
+ }
+}
+
+bool ScUndoMakeOutline::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoOutlineLevel::ScUndoOutlineLevel( ScDocShell* pNewDocShell,
+ SCCOLROW nNewStart, SCCOLROW nNewEnd, SCTAB nNewTab,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ bool bNewColumns, sal_uInt16 nNewLevel )
+ : ScSimpleUndo(pNewDocShell)
+ , nStart(nNewStart)
+ , nEnd(nNewEnd)
+ , nTab(nNewTab)
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , xUndoTable(std::move(pNewUndoTab))
+ , bColumns(bNewColumns)
+ , nLevel(nNewLevel)
+{
+}
+
+OUString ScUndoOutlineLevel::GetComment() const
+{ // "Select outline level"
+ return ScResId( STR_UNDO_OUTLINELEVEL );
+}
+
+void ScUndoOutlineLevel::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ // Original Outline table
+
+ rDoc.SetOutlineTable(nTab, xUndoTable.get());
+
+ // Original column/row status
+
+ if (bColumns)
+ xUndoDoc->CopyToDocument(static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, rDoc);
+ else
+ xUndoDoc->CopyToDocument(0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, bColumns, !bColumns,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ pViewShell->UpdateScrollBars();
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top);
+
+ EndUndo();
+}
+
+void ScUndoOutlineLevel::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ // sheet has to be switched on or off before this (#46952#) !!!
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pViewShell->SelectLevel( bColumns, nLevel, false );
+
+ EndRedo();
+}
+
+void ScUndoOutlineLevel::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->SelectLevel( bColumns, nLevel );
+}
+
+bool ScUndoOutlineLevel::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+/** show/hide outline over block marks */
+ScUndoOutlineBlock::ScUndoOutlineBlock( ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab, bool bNewShow ) :
+ ScSimpleUndo( pNewDocShell ),
+ aBlockStart( nStartX, nStartY, nStartZ ),
+ aBlockEnd( nEndX, nEndY, nEndZ ),
+ xUndoDoc(std::move(pNewUndoDoc)),
+ xUndoTable(std::move(pNewUndoTab)),
+ bShow( bNewShow )
+{
+}
+
+OUString ScUndoOutlineBlock::GetComment() const
+{ // "Show outline" "Hide outline"
+ return bShow ?
+ ScResId( STR_UNDO_DOOUTLINEBLK ) :
+ ScResId( STR_UNDO_REDOOUTLINEBLK );
+}
+
+void ScUndoOutlineBlock::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTab = aBlockStart.Tab();
+
+ // Original Outline table
+ rDoc.SetOutlineTable(nTab, xUndoTable.get());
+
+ // Original column/row status
+ SCCOLROW nStartCol = aBlockStart.Col();
+ SCCOLROW nEndCol = aBlockEnd.Col();
+ SCCOLROW nStartRow = aBlockStart.Row();
+ SCCOLROW nEndRow = aBlockEnd.Row();
+
+ if (!bShow)
+ { // Size of the hidden blocks
+ size_t nLevel;
+ xUndoTable->GetColArray().FindTouchedLevel(nStartCol, nEndCol, nLevel);
+ xUndoTable->GetColArray().ExtendBlock(nLevel, nStartCol, nEndCol);
+ xUndoTable->GetRowArray().FindTouchedLevel(nStartRow, nEndRow, nLevel);
+ xUndoTable->GetRowArray().ExtendBlock(nLevel, nStartRow, nEndRow);
+ }
+
+ xUndoDoc->CopyToDocument(static_cast<SCCOL>(nStartCol), 0, nTab,
+ static_cast<SCCOL>(nEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, rDoc);
+ xUndoDoc->CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, true /* bColumns */, true /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ pViewShell->UpdateScrollBars();
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top);
+
+
+ pViewShell->OnLOKShowHideColRow(/*columns: */ true, nStartCol - 1);
+ pViewShell->OnLOKShowHideColRow(/*columns: */ false, nStartRow - 1);
+
+ EndUndo();
+}
+
+void ScUndoOutlineBlock::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aBlockStart, aBlockEnd );
+ if (bShow)
+ pViewShell->ShowMarkedOutlines( false );
+ else
+ pViewShell->HideMarkedOutlines( false );
+
+ EndRedo();
+}
+
+void ScUndoOutlineBlock::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+
+ if (bShow)
+ rViewShell.ShowMarkedOutlines();
+ else
+ rViewShell.HideMarkedOutlines();
+ }
+}
+
+bool ScUndoOutlineBlock::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoRemoveAllOutlines::ScUndoRemoveAllOutlines(ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab)
+ : ScSimpleUndo(pNewDocShell)
+ , aBlockStart(nStartX, nStartY, nStartZ)
+ , aBlockEnd(nEndX, nEndY, nEndZ)
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , xUndoTable(std::move(pNewUndoTab))
+{
+}
+
+OUString ScUndoRemoveAllOutlines::GetComment() const
+{ // "Remove outlines"
+ return ScResId( STR_UNDO_REMOVEALLOTLNS );
+}
+
+void ScUndoRemoveAllOutlines::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTab = aBlockStart.Tab();
+
+ // Original Outline table
+ rDoc.SetOutlineTable(nTab, xUndoTable.get());
+
+ // Original column/row status
+ SCCOL nStartCol = aBlockStart.Col();
+ SCCOL nEndCol = aBlockEnd.Col();
+ SCROW nStartRow = aBlockStart.Row();
+ SCROW nEndRow = aBlockEnd.Row();
+
+ xUndoDoc->CopyToDocument(nStartCol, 0, nTab, nEndCol, rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, rDoc);
+ xUndoDoc->CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ rDoc.UpdatePageBreaks( nTab );
+
+ pViewShell->UpdateScrollBars();
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top|PaintPartFlags::Size);
+
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, BOTH_HEADERS, nTab);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell,
+ true /* bColumns */, true /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+
+ EndUndo();
+}
+
+void ScUndoRemoveAllOutlines::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ // sheet has to be switched over (#46952#)!
+
+ SCTAB nTab = aBlockStart.Tab();
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pViewShell->RemoveAllOutlines( false );
+
+ EndRedo();
+}
+
+void ScUndoRemoveAllOutlines::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->RemoveAllOutlines();
+}
+
+bool ScUndoRemoveAllOutlines::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoAutoOutline::ScUndoAutoOutline(ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab)
+ : ScSimpleUndo(pNewDocShell)
+ , aBlockStart(nStartX, nStartY, nStartZ)
+ , aBlockEnd(nEndX, nEndY, nEndZ)
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , xUndoTable(std::move(pNewUndoTab))
+{
+}
+
+OUString ScUndoAutoOutline::GetComment() const
+{
+ return ScResId( STR_UNDO_AUTOOUTLINE );
+}
+
+void ScUndoAutoOutline::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTab = aBlockStart.Tab();
+
+ // Original outline table
+ rDoc.SetOutlineTable(nTab, xUndoTable.get());
+
+ // Original column/row status
+ if (xUndoDoc && xUndoTable)
+ {
+ SCCOLROW nStartCol;
+ SCCOLROW nStartRow;
+ SCCOLROW nEndCol;
+ SCCOLROW nEndRow;
+ xUndoTable->GetColArray().GetRange(nStartCol, nEndCol);
+ xUndoTable->GetRowArray().GetRange(nStartRow, nEndRow);
+
+ xUndoDoc->CopyToDocument(static_cast<SCCOL>(nStartCol), 0, nTab,
+ static_cast<SCCOL>(nEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false,
+ rDoc);
+ xUndoDoc->CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ pViewShell->UpdateScrollBars();
+ }
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top|PaintPartFlags::Size);
+
+ EndUndo();
+}
+
+void ScUndoAutoOutline::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ SCTAB nTab = aBlockStart.Tab();
+ if (pViewShell)
+ {
+ // sheet has to be switched on or off before this (#46952#) !!!
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+ }
+
+ ScRange aRange( aBlockStart.Col(), aBlockStart.Row(), nTab,
+ aBlockEnd.Col(), aBlockEnd.Row(), nTab );
+ ScOutlineDocFunc aFunc( *pDocShell );
+ aFunc.AutoOutline( aRange, false );
+
+ // Select in View
+ // If it was called with a multi selection,
+ // then this is now the enclosing range...
+
+ if (pViewShell)
+ pViewShell->MarkRange( aRange );
+
+ EndRedo();
+}
+
+void ScUndoAutoOutline::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->AutoOutline();
+}
+
+bool ScUndoAutoOutline::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoSubTotals::ScUndoSubTotals(ScDocShell* pNewDocShell, SCTAB nNewTab,
+ const ScSubTotalParam& rNewParam, SCROW nNewEndY,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ std::unique_ptr<ScRangeName> pNewUndoRange, std::unique_ptr<ScDBCollection> pNewUndoDB)
+ : ScDBFuncUndo(pNewDocShell, ScRange(rNewParam.nCol1, rNewParam.nRow1, nNewTab,
+ rNewParam.nCol2, rNewParam.nRow2, nNewTab))
+ , nTab(nNewTab)
+ , aParam(rNewParam)
+ , nNewEndRow(nNewEndY)
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , xUndoTable(std::move(pNewUndoTab))
+ , xUndoRange(std::move(pNewUndoRange))
+ , xUndoDB(std::move(pNewUndoDB))
+{
+}
+
+OUString ScUndoSubTotals::GetComment() const
+{ // "Subtotals"
+ return ScResId( STR_UNDO_SUBTOTALS );
+}
+
+void ScUndoSubTotals::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ if (nNewEndRow > aParam.nRow2)
+ {
+ rDoc.DeleteRow( 0,nTab, rDoc.MaxCol(),nTab, aParam.nRow2+1, static_cast<SCSIZE>(nNewEndRow-aParam.nRow2) );
+ }
+ else if (nNewEndRow < aParam.nRow2)
+ {
+ rDoc.InsertRow( 0,nTab, rDoc.MaxCol(),nTab, nNewEndRow+1, static_cast<SCSIZE>(aParam.nRow2-nNewEndRow) );
+ }
+
+ // Original Outline table
+ rDoc.SetOutlineTable(nTab, xUndoTable.get());
+
+ // Original column/row status
+ if (xUndoTable)
+ {
+ SCCOLROW nStartCol;
+ SCCOLROW nStartRow;
+ SCCOLROW nEndCol;
+ SCCOLROW nEndRow;
+ xUndoTable->GetColArray().GetRange(nStartCol, nEndCol);
+ xUndoTable->GetRowArray().GetRange(nStartRow, nEndRow);
+
+ xUndoDoc->CopyToDocument(static_cast<SCCOL>(nStartCol), 0, nTab,
+ static_cast<SCCOL>(nEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false,
+ rDoc);
+ xUndoDoc->CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ pViewShell->UpdateScrollBars();
+ }
+
+ // Original data and references
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, 0, aParam.nRow1+1, nTab,
+ rDoc.MaxCol(), aParam.nRow2, nTab );
+
+ rDoc.DeleteAreaTab( 0,aParam.nRow1+1, rDoc.MaxCol(),aParam.nRow2, nTab, InsertDeleteFlags::ALL );
+
+ xUndoDoc->CopyToDocument(0, aParam.nRow1+1, nTab, rDoc.MaxCol(), aParam.nRow2, nTab,
+ InsertDeleteFlags::NONE, false, rDoc); // Flags
+ xUndoDoc->UndoToDocument(0, aParam.nRow1+1, nTab, rDoc.MaxCol(), aParam.nRow2, nTab,
+ InsertDeleteFlags::ALL, false, rDoc);
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aParam.nCol1,aParam.nRow1,nTab,
+ aParam.nCol2,aParam.nRow2,nTab );
+
+ if (xUndoRange)
+ rDoc.SetRangeName(std::unique_ptr<ScRangeName>(new ScRangeName(*xUndoRange)));
+ if (xUndoDB)
+ rDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(*xUndoDB)), true);
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top|PaintPartFlags::Size);
+ pDocShell->PostDataChanged();
+
+ EndUndo();
+}
+
+void ScUndoSubTotals::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aParam.nCol1,aParam.nRow1,nTab,
+ aParam.nCol2,aParam.nRow2,nTab );
+ pViewShell->DoSubTotals( aParam, false );
+
+ EndRedo();
+}
+
+void ScUndoSubTotals::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoSubTotals::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // is not possible due to column numbers
+}
+
+ScUndoQuery::ScUndoQuery( ScDocShell* pNewDocShell, SCTAB nNewTab, const ScQueryParam& rParam,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScDBCollection> pNewUndoDB,
+ const ScRange* pOld, bool bSize, const ScRange* pAdvSrc ) :
+ ScDBFuncUndo( pNewDocShell, ScRange( rParam.nCol1, rParam.nRow1, nNewTab,
+ rParam.nCol2, rParam.nRow2, nNewTab ) ),
+ nTab( nNewTab ),
+ aQueryParam( rParam ),
+ xUndoDoc( std::move(pNewUndoDoc) ),
+ xUndoDB( std::move(pNewUndoDB) ),
+ bIsAdvanced( false ),
+ bDestArea( false ),
+ bDoSize( bSize )
+{
+ if ( pOld )
+ {
+ bDestArea = true;
+ aOldDest = *pOld;
+ }
+ if ( pAdvSrc )
+ {
+ bIsAdvanced = true;
+ aAdvSource = *pAdvSrc;
+ }
+
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScUndoQuery::~ScUndoQuery()
+{
+ pDrawUndo.reset();
+}
+
+OUString ScUndoQuery::GetComment() const
+{ // "Filter";
+ return ScResId( STR_UNDO_QUERY );
+}
+
+void ScUndoQuery::Undo()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (ScTabViewShell::isAnyEditViewInRange(pViewShell, /*bColumns*/ false, aQueryParam.nRow1, aQueryParam.nRow2))
+ return;
+
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ bool bCopy = !aQueryParam.bInplace;
+ SCCOL nDestEndCol = 0;
+ SCROW nDestEndRow = 0;
+ if (bCopy)
+ {
+ nDestEndCol = aQueryParam.nDestCol + ( aQueryParam.nCol2-aQueryParam.nCol1 );
+ nDestEndRow = aQueryParam.nDestRow + ( aQueryParam.nRow2-aQueryParam.nRow1 );
+
+ ScDBData* pData = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pData)
+ {
+ ScRange aNewDest;
+ pData->GetArea( aNewDest );
+ nDestEndCol = aNewDest.aEnd.Col();
+ nDestEndRow = aNewDest.aEnd.Row();
+ }
+
+ if ( bDoSize && bDestArea )
+ {
+ // aDestRange is the old range
+ rDoc.FitBlock( ScRange(
+ aQueryParam.nDestCol, aQueryParam.nDestRow, aQueryParam.nDestTab,
+ nDestEndCol, nDestEndRow, aQueryParam.nDestTab ),
+ aOldDest );
+ }
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell,
+ aQueryParam.nDestCol, aQueryParam.nDestRow, aQueryParam.nDestTab,
+ nDestEndCol, nDestEndRow, aQueryParam.nDestTab );
+ rDoc.DeleteAreaTab( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ nDestEndCol, nDestEndRow, aQueryParam.nDestTab, InsertDeleteFlags::ALL );
+
+ pViewShell->DoneBlockMode();
+
+ xUndoDoc->CopyToDocument(aQueryParam.nDestCol, aQueryParam.nDestRow, aQueryParam.nDestTab,
+ nDestEndCol, nDestEndRow, aQueryParam.nDestTab,
+ InsertDeleteFlags::ALL, false, rDoc);
+ // Attributes are always copied (#49287#)
+
+ // rest of the old range
+ if ( bDestArea && !bDoSize )
+ {
+ rDoc.DeleteAreaTab( aOldDest, InsertDeleteFlags::ALL );
+ xUndoDoc->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, rDoc);
+ }
+ }
+ else
+ xUndoDoc->CopyToDocument(0, aQueryParam.nRow1, nTab, rDoc.MaxCol(), aQueryParam.nRow2, nTab,
+ InsertDeleteFlags::NONE, false, rDoc);
+
+ if (xUndoDB)
+ rDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(*xUndoDB )), true);
+
+ if (!bCopy)
+ {
+ rDoc.InvalidatePageBreaks(nTab);
+ rDoc.UpdatePageBreaks( nTab );
+ }
+
+ ScRange aDirtyRange( 0 , aQueryParam.nRow1, nTab,
+ rDoc.MaxCol(), aQueryParam.nRow2, nTab );
+ rDoc.SetDirty( aDirtyRange, true );
+
+ DoSdrUndoAction( pDrawUndo.get(), &rDoc );
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+
+ // invalidate cache positions and update cursor and selection
+ pViewShell->OnLOKShowHideColRow(/*bColumns*/ false, aQueryParam.nRow1 - 1);
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, ROW_HEADER, nTab);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pViewShell,
+ false /* bColumns */, true /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ false /* bGroups */, nTab);
+
+ // Paint
+
+ if (bCopy)
+ {
+ SCCOL nEndX = nDestEndCol;
+ SCROW nEndY = nDestEndRow;
+ if (bDestArea)
+ {
+ if ( aOldDest.aEnd.Col() > nEndX )
+ nEndX = aOldDest.aEnd.Col();
+ if ( aOldDest.aEnd.Row() > nEndY )
+ nEndY = aOldDest.aEnd.Row();
+ }
+ if (bDoSize)
+ nEndY = rDoc.MaxRow();
+ pDocShell->PostPaint( aQueryParam.nDestCol, aQueryParam.nDestRow, aQueryParam.nDestTab,
+ nEndX, nEndY, aQueryParam.nDestTab, PaintPartFlags::Grid );
+ }
+ else
+ pDocShell->PostPaint( 0, aQueryParam.nRow1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ pDocShell->PostDataChanged();
+
+ EndUndo();
+}
+
+void ScUndoQuery::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ if ( bIsAdvanced )
+ pViewShell->Query( aQueryParam, &aAdvSource, false );
+ else
+ pViewShell->Query( aQueryParam, nullptr, false );
+
+ EndRedo();
+}
+
+void ScUndoQuery::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoQuery::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // does not work due to column numbers
+}
+
+// Show or hide AutoFilter buttons (doesn't include filter settings)
+
+ScUndoAutoFilter::ScUndoAutoFilter( ScDocShell* pNewDocShell, const ScRange& rRange,
+ OUString aName, bool bSet ) :
+ ScDBFuncUndo( pNewDocShell, rRange ),
+ aDBName(std::move( aName )),
+ bFilterSet( bSet )
+{
+}
+
+ScUndoAutoFilter::~ScUndoAutoFilter()
+{
+}
+
+OUString ScUndoAutoFilter::GetComment() const
+{
+ return ScResId( STR_UNDO_QUERY ); // same as ScUndoQuery
+}
+
+void ScUndoAutoFilter::DoChange( bool bUndo )
+{
+ bool bNewFilter = bUndo ? !bFilterSet : bFilterSet;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDBData* pDBData=nullptr;
+ if (aDBName == STR_DB_LOCAL_NONAME)
+ {
+ SCTAB nTab = aOriginalRange.aStart.Tab();
+ pDBData = rDoc.GetAnonymousDBData(nTab);
+ }
+ else
+ {
+ ScDBCollection* pColl = rDoc.GetDBCollection();
+ pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aDBName));
+ }
+
+ if ( !pDBData )
+ return;
+
+ pDBData->SetAutoFilter( bNewFilter );
+
+ SCCOL nRangeX1;
+ SCROW nRangeY1;
+ SCCOL nRangeX2;
+ SCROW nRangeY2;
+ SCTAB nRangeTab;
+ pDBData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
+
+ if ( bNewFilter )
+ rDoc.ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
+ else
+ rDoc.RemoveFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto );
+
+ pDocShell->PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid );
+}
+
+void ScUndoAutoFilter::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoAutoFilter::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoAutoFilter::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoAutoFilter::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+// change database sections (dialog)
+ScUndoDBData::ScUndoDBData( ScDocShell* pNewDocShell,
+ std::unique_ptr<ScDBCollection> pNewUndoColl,
+ std::unique_ptr<ScDBCollection> pNewRedoColl ) :
+ ScSimpleUndo( pNewDocShell ),
+ pUndoColl( std::move(pNewUndoColl) ),
+ pRedoColl( std::move(pNewRedoColl) )
+{
+}
+
+ScUndoDBData::~ScUndoDBData()
+{
+}
+
+OUString ScUndoDBData::GetComment() const
+{ // "Change database range";
+ return ScResId( STR_UNDO_DBDATA );
+}
+
+void ScUndoDBData::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ bool bOldAutoCalc = rDoc.GetAutoCalc();
+ rDoc.SetAutoCalc( false ); // Avoid unnecessary calculations
+ rDoc.PreprocessDBDataUpdate();
+ rDoc.SetDBCollection( std::unique_ptr<ScDBCollection>(new ScDBCollection(*pUndoColl)), true );
+ rDoc.CompileHybridFormula();
+ rDoc.SetAutoCalc( bOldAutoCalc );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+
+ EndUndo();
+}
+
+void ScUndoDBData::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ bool bOldAutoCalc = rDoc.GetAutoCalc();
+ rDoc.SetAutoCalc( false ); // Avoid unnecessary calculations
+ rDoc.PreprocessDBDataUpdate();
+ rDoc.SetDBCollection( std::unique_ptr<ScDBCollection>(new ScDBCollection(*pRedoColl)), true );
+ rDoc.CompileHybridFormula();
+ rDoc.SetAutoCalc( bOldAutoCalc );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+
+ EndRedo();
+}
+
+void ScUndoDBData::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoDBData::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // is not possible
+}
+
+ScUndoImportData::ScUndoImportData( ScDocShell* pNewDocShell, SCTAB nNewTab,
+ const ScImportParam& rParam, SCCOL nNewEndX, SCROW nNewEndY,
+ SCCOL nNewFormula,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
+ std::unique_ptr<ScDBData> pNewUndoData, std::unique_ptr<ScDBData> pNewRedoData ) :
+ ScSimpleUndo( pNewDocShell ),
+ nTab( nNewTab ),
+ aImportParam( rParam ),
+ nEndCol( nNewEndX ),
+ nEndRow( nNewEndY ),
+ xUndoDoc(std::move(pNewUndoDoc)),
+ xRedoDoc(std::move(pNewRedoDoc)),
+ xUndoDBData(std::move(pNewUndoData)),
+ xRedoDBData(std::move(pNewRedoData)),
+ nFormulaCols( nNewFormula ),
+ bRedoFilled( false )
+{
+ // redo doc doesn't contain imported data (but everything else)
+}
+
+OUString ScUndoImportData::GetComment() const
+{
+ return ScResId( STR_UNDO_IMPORTDATA );
+}
+
+void ScUndoImportData::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aImportParam.nCol1,aImportParam.nRow1,nTab,
+ nEndCol,nEndRow,nTab );
+
+ SCTAB nTable;
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ ScDBData* pCurrentData = nullptr;
+ if (xUndoDBData && xRedoDBData)
+ {
+ xRedoDBData->GetArea( nTable, nCol1, nRow1, nCol2, nRow2 );
+ pCurrentData = ScUndoUtil::GetOldDBData(xRedoDBData.get(), &rDoc, nTab,
+ nCol1, nRow1, nCol2, nRow2);
+
+ if ( !bRedoFilled )
+ {
+ // read redo data from document at first undo
+ // imported data is deleted later anyway,
+ // so now delete each column after copying to save memory (#41216#)
+
+ bool bOldAutoCalc = rDoc.GetAutoCalc();
+ rDoc.SetAutoCalc( false ); // outside of the loop
+ for (SCCOL nCopyCol = nCol1; nCopyCol <= nCol2; nCopyCol++)
+ {
+ rDoc.CopyToDocument(nCopyCol,nRow1,nTab, nCopyCol,nRow2,nTab,
+ InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE, false, *xRedoDoc);
+ rDoc.DeleteAreaTab(nCopyCol, nRow1, nCopyCol, nRow2, nTab, InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE);
+ }
+ rDoc.SetAutoCalc( bOldAutoCalc );
+ bRedoFilled = true;
+ }
+ }
+ bool bMoveCells = xUndoDBData && xRedoDBData &&
+ xRedoDBData->IsDoSize(); // the same in old and new
+ if (bMoveCells)
+ {
+ // Undo: first delete the new data, then FitBlock backwards
+
+ ScRange aOld, aNew;
+ xUndoDBData->GetArea(aOld);
+ xRedoDBData->GetArea(aNew);
+
+ rDoc.DeleteAreaTab( aNew.aStart.Col(), aNew.aStart.Row(),
+ aNew.aEnd.Col(), aNew.aEnd.Row(), nTab, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+
+ aOld.aEnd.SetCol( aOld.aEnd.Col() + nFormulaCols ); // FitBlock also for formulas
+ aNew.aEnd.SetCol( aNew.aEnd.Col() + nFormulaCols );
+ rDoc.FitBlock( aNew, aOld, false ); // backwards
+ }
+ else
+ rDoc.DeleteAreaTab( aImportParam.nCol1,aImportParam.nRow1,
+ nEndCol,nEndRow, nTab, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+
+ xUndoDoc->CopyToDocument(aImportParam.nCol1,aImportParam.nRow1,nTab,
+ nEndCol+nFormulaCols,nEndRow,nTab,
+ InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+
+ if (pCurrentData)
+ {
+ *pCurrentData = *xUndoDBData;
+
+ xUndoDBData->GetArea(nTable, nCol1, nRow1, nCol2, nRow2);
+ ScUndoUtil::MarkSimpleBlock( pDocShell, nCol1, nRow1, nTable, nCol2, nRow2, nTable );
+ }
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ if (bMoveCells)
+ pDocShell->PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid );
+ else
+ pDocShell->PostPaint( aImportParam.nCol1,aImportParam.nRow1,nTab,
+ nEndCol,nEndRow,nTab, PaintPartFlags::Grid );
+ pDocShell->PostDataChanged();
+
+ EndUndo();
+}
+
+void ScUndoImportData::Redo()
+{
+ BeginRedo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aImportParam.nCol1,aImportParam.nRow1,nTab,
+ nEndCol,nEndRow,nTab );
+
+ SCTAB nTable;
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ ScDBData* pCurrentData = nullptr;
+ if (xUndoDBData && xRedoDBData)
+ {
+ xUndoDBData->GetArea( nTable, nCol1, nRow1, nCol2, nRow2 );
+ pCurrentData = ScUndoUtil::GetOldDBData(xUndoDBData.get(), &rDoc, nTab,
+ nCol1, nRow1, nCol2, nRow2);
+ }
+ bool bMoveCells = xUndoDBData && xRedoDBData &&
+ xRedoDBData->IsDoSize(); // the same in old and new
+ if (bMoveCells)
+ {
+ // Redo: FitBlock, then delete data (needed for CopyToDocument)
+
+ ScRange aOld, aNew;
+ xUndoDBData->GetArea(aOld);
+ xRedoDBData->GetArea(aNew);
+
+ aOld.aEnd.SetCol( aOld.aEnd.Col() + nFormulaCols ); // FitBlock also for formulas
+ aNew.aEnd.SetCol( aNew.aEnd.Col() + nFormulaCols );
+ rDoc.FitBlock( aOld, aNew );
+
+ rDoc.DeleteAreaTab( aNew.aStart.Col(), aNew.aStart.Row(),
+ aNew.aEnd.Col(), aNew.aEnd.Row(), nTab, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+
+ xRedoDoc->CopyToDocument(aNew, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc); // including formulas
+ }
+ else
+ {
+ rDoc.DeleteAreaTab( aImportParam.nCol1,aImportParam.nRow1,
+ nEndCol,nEndRow, nTab, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
+ xRedoDoc->CopyToDocument(aImportParam.nCol1,aImportParam.nRow1,nTab,
+ nEndCol,nEndRow,nTab, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, rDoc);
+ }
+
+ if (pCurrentData)
+ {
+ *pCurrentData = *xRedoDBData;
+
+ xRedoDBData->GetArea(nTable, nCol1, nRow1, nCol2, nRow2);
+ ScUndoUtil::MarkSimpleBlock( pDocShell, nCol1, nRow1, nTable, nCol2, nRow2, nTable );
+ }
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ if (bMoveCells)
+ pDocShell->PostPaint( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid );
+ else
+ pDocShell->PostPaint( aImportParam.nCol1,aImportParam.nRow1,nTab,
+ nEndCol,nEndRow,nTab, PaintPartFlags::Grid );
+ pDocShell->PostDataChanged();
+
+ EndRedo();
+}
+
+void ScUndoImportData::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell& rViewShell = *pViewTarget->GetViewShell();
+
+ SCTAB nDummy;
+ ScImportParam aNewParam(aImportParam);
+ ScDBData* pDBData = rViewShell.GetDBData();
+ pDBData->GetArea( nDummy, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+
+ rViewShell.ImportData( aNewParam );
+ }
+}
+
+bool ScUndoImportData::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ // Repeat only for import using a database range, then xUndoDBData is set
+
+ if (xUndoDBData)
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+ else
+ return false; // Address book
+}
+
+ScUndoRepeatDB::ScUndoRepeatDB( ScDocShell* pNewDocShell, SCTAB nNewTab,
+ SCCOL nStartX, SCROW nStartY, SCCOL nEndX, SCROW nEndY,
+ SCROW nResultEndRow, SCCOL nCurX, SCROW nCurY,
+ ScDocumentUniquePtr pNewUndoDoc, std::unique_ptr<ScOutlineTable> pNewUndoTab,
+ std::unique_ptr<ScRangeName> pNewUndoRange, std::unique_ptr<ScDBCollection> pNewUndoDB,
+ const ScRange* pOldQ, const ScRange* pNewQ ) :
+ ScSimpleUndo( pNewDocShell ),
+ aBlockStart( nStartX,nStartY,nNewTab ),
+ aBlockEnd( nEndX,nEndY,nNewTab ),
+ nNewEndRow( nResultEndRow ),
+ aCursorPos( nCurX,nCurY,nNewTab ),
+ xUndoDoc(std::move(pNewUndoDoc)),
+ xUndoTable(std::move(pNewUndoTab)),
+ xUndoRange(std::move(pNewUndoRange)),
+ xUndoDB(std::move(pNewUndoDB)),
+ bQuerySize( false )
+{
+ if ( pOldQ && pNewQ )
+ {
+ aOldQuery = *pOldQ;
+ aNewQuery = *pNewQ;
+ bQuerySize = true;
+ }
+}
+
+OUString ScUndoRepeatDB::GetComment() const
+{
+ return ScResId( STR_UNDO_REPEATDB );
+}
+
+void ScUndoRepeatDB::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTab = aBlockStart.Tab();
+
+ if (bQuerySize)
+ {
+ rDoc.FitBlock( aNewQuery, aOldQuery, false );
+
+ if ( aNewQuery.aEnd.Col() == aOldQuery.aEnd.Col() )
+ {
+ SCCOL nFormulaCols = 0;
+ SCCOL nCol = aOldQuery.aEnd.Col() + 1;
+ SCROW nRow = aOldQuery.aStart.Row() + 1; // test the header
+ while ( nCol <= rDoc.MaxCol() &&
+ rDoc.GetCellType(ScAddress( nCol, nRow, nTab )) == CELLTYPE_FORMULA )
+ {
+ ++nCol;
+ ++nFormulaCols;
+ }
+
+ if ( nFormulaCols > 0 )
+ {
+ ScRange aOldForm = aOldQuery;
+ aOldForm.aStart.SetCol( aOldQuery.aEnd.Col() + 1 );
+ aOldForm.aEnd.SetCol( aOldQuery.aEnd.Col() + nFormulaCols );
+ ScRange aNewForm = aOldForm;
+ aNewForm.aEnd.SetRow( aNewQuery.aEnd.Row() );
+ rDoc.FitBlock( aNewForm, aOldForm, false );
+ }
+ }
+ }
+
+ // TODO Data from Filter in other range are still missing!
+
+ if (nNewEndRow > aBlockEnd.Row())
+ {
+ rDoc.DeleteRow( 0,nTab, rDoc.MaxCol(),nTab, aBlockEnd.Row()+1, static_cast<SCSIZE>(nNewEndRow-aBlockEnd.Row()) );
+ }
+ else if (nNewEndRow < aBlockEnd.Row())
+ {
+ rDoc.InsertRow( 0,nTab, rDoc.MaxCol(),nTab, nNewEndRow+1, static_cast<SCSIZE>(nNewEndRow-aBlockEnd.Row()) );
+ }
+
+ // Original Outline table
+ rDoc.SetOutlineTable(nTab, xUndoTable.get());
+
+ // Original column/row status
+ if (xUndoTable)
+ {
+ SCCOLROW nStartCol;
+ SCCOLROW nStartRow;
+ SCCOLROW nEndCol;
+ SCCOLROW nEndRow;
+ xUndoTable->GetColArray().GetRange(nStartCol, nEndCol);
+ xUndoTable->GetRowArray().GetRange(nStartRow, nEndRow);
+
+ xUndoDoc->CopyToDocument(static_cast<SCCOL>(nStartCol), 0, nTab,
+ static_cast<SCCOL>(nEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false,
+ rDoc);
+ xUndoDoc->CopyToDocument(0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ pViewShell->UpdateScrollBars();
+ }
+
+ // Original data and references
+ ScUndoUtil::MarkSimpleBlock( pDocShell, 0, aBlockStart.Row(), nTab,
+ rDoc.MaxCol(), aBlockEnd.Row(), nTab );
+ rDoc.DeleteAreaTab( 0, aBlockStart.Row(),
+ rDoc.MaxCol(), aBlockEnd.Row(), nTab, InsertDeleteFlags::ALL );
+
+ xUndoDoc->CopyToDocument(0, aBlockStart.Row(), nTab, rDoc.MaxCol(), aBlockEnd.Row(), nTab,
+ InsertDeleteFlags::NONE, false, rDoc); // Flags
+ xUndoDoc->UndoToDocument(0, aBlockStart.Row(), nTab, rDoc.MaxCol(), aBlockEnd.Row(), nTab,
+ InsertDeleteFlags::ALL, false, rDoc);
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aBlockStart.Col(),aBlockStart.Row(),nTab,
+ aBlockEnd.Col(),aBlockEnd.Row(),nTab );
+
+ if (xUndoRange)
+ rDoc.SetRangeName(std::unique_ptr<ScRangeName>(new ScRangeName(*xUndoRange)));
+ if (xUndoDB)
+ rDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(*xUndoDB)), true);
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, false /* bColumns */, true /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ false /* bGroups */, nTab);
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::Grid|PaintPartFlags::Left|PaintPartFlags::Top|PaintPartFlags::Size);
+ pDocShell->PostDataChanged();
+
+ EndUndo();
+}
+
+void ScUndoRepeatDB::Redo()
+{
+ BeginRedo();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ SCTAB nTab = aBlockStart.Tab();
+
+ SCTAB nVisTab = pViewShell->GetViewData().GetTabNo();
+ if ( nVisTab != nTab )
+ pViewShell->SetTabNo( nTab );
+
+ ScUndoUtil::MarkSimpleBlock( pDocShell, aBlockStart.Col(),aBlockStart.Row(),nTab,
+ aBlockEnd.Col(),aBlockEnd.Row(),nTab );
+ pViewShell->SetCursor( aCursorPos.Col(), aCursorPos.Row() );
+
+ pViewShell->RepeatDB( false );
+
+ EndRedo();
+}
+
+void ScUndoRepeatDB::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->RepeatDB();
+}
+
+bool ScUndoRepeatDB::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoDataPilot::ScUndoDataPilot( ScDocShell* pNewDocShell,
+ ScDocumentUniquePtr pOldDoc, ScDocumentUniquePtr pNewDoc,
+ const ScDPObject* pOldObj, const ScDPObject* pNewObj, bool bMove )
+ : ScSimpleUndo(pNewDocShell)
+ , xOldUndoDoc(std::move(pOldDoc))
+ , xNewUndoDoc(std::move(pNewDoc))
+ , bAllowMove( bMove)
+{
+ if (pOldObj)
+ xOldDPObject.reset(new ScDPObject(*pOldObj));
+ if (pNewObj)
+ xNewDPObject.reset(new ScDPObject(*pNewObj));
+}
+
+OUString ScUndoDataPilot::GetComment() const
+{
+ TranslateId pResId;
+ if (xOldUndoDoc && xNewUndoDoc)
+ pResId = STR_UNDO_PIVOT_MODIFY;
+ else if (xNewUndoDoc)
+ pResId = STR_UNDO_PIVOT_NEW;
+ else
+ pResId = STR_UNDO_PIVOT_DELETE;
+
+ return ScResId(pResId);
+}
+
+void ScUndoDataPilot::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScRange aOldRange;
+ ScRange aNewRange;
+
+ if (xNewDPObject && xNewUndoDoc)
+ {
+ aNewRange = xNewDPObject->GetOutRange();
+ rDoc.DeleteAreaTab( aNewRange, InsertDeleteFlags::ALL );
+ xNewUndoDoc->CopyToDocument(aNewRange, InsertDeleteFlags::ALL, false, rDoc);
+ }
+ if (xOldDPObject && xOldUndoDoc)
+ {
+ aOldRange = xOldDPObject->GetOutRange();
+ rDoc.DeleteAreaTab(aOldRange, InsertDeleteFlags::ALL);
+ xOldUndoDoc->CopyToDocument(aOldRange, InsertDeleteFlags::ALL, false, rDoc);
+ }
+
+ // update objects in collection
+ if (xNewDPObject)
+ {
+ // find updated object
+ //! find by name!
+
+ ScDPObject* pDocObj = rDoc.GetDPAtCursor(
+ aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aStart.Tab() );
+ OSL_ENSURE(pDocObj, "DPObject not found");
+ if (pDocObj)
+ {
+ if (xOldDPObject)
+ {
+ // restore old settings
+ xOldDPObject->WriteSourceDataTo( *pDocObj );
+ ScDPSaveData* pData = xOldDPObject->GetSaveData();
+ if (pData)
+ pDocObj->SetSaveData(*pData);
+ pDocObj->SetOutRange(xOldDPObject->GetOutRange());
+ xOldDPObject->WriteTempDataTo( *pDocObj );
+ }
+ else
+ {
+ // delete inserted object
+ rDoc.GetDPCollection()->FreeTable(pDocObj);
+ }
+ }
+ }
+ else if (xOldDPObject)
+ {
+ // re-insert deleted object
+ rDoc.GetDPCollection()->InsertNewTable(std::make_unique<ScDPObject>(*xOldDPObject));
+ }
+
+ if (xNewUndoDoc)
+ pDocShell->PostPaint(aNewRange, PaintPartFlags::Grid, SC_PF_LINES);
+ if (xOldUndoDoc)
+ pDocShell->PostPaint(aOldRange, PaintPartFlags::Grid, SC_PF_LINES);
+ pDocShell->PostDataChanged();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ //! set current sheet
+ }
+
+ if (xNewDPObject)
+ {
+ // notify API objects
+ rDoc.BroadcastUno(ScDataPilotModifiedHint(xNewDPObject->GetName()));
+ }
+
+ EndUndo();
+}
+
+void ScUndoDataPilot::Redo()
+{
+ BeginRedo();
+
+ //! copy output data instead of repeating the change,
+ //! in case external data have changed!
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScDPObject* pSourceObj = nullptr;
+ if (xOldDPObject)
+ {
+ // find object to modify
+ //! find by name!
+
+ ScRange aOldRange = xOldDPObject->GetOutRange();
+ pSourceObj = rDoc.GetDPAtCursor(
+ aOldRange.aStart.Col(), aOldRange.aStart.Row(), aOldRange.aStart.Tab() );
+ OSL_ENSURE(pSourceObj, "DPObject not found");
+ }
+
+ ScDBDocFunc aFunc( *pDocShell );
+ aFunc.DataPilotUpdate(pSourceObj, xNewDPObject.get(), false, false, bAllowMove); // no new undo action
+
+ EndRedo();
+}
+
+void ScUndoDataPilot::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ //! allow deletion
+}
+
+bool ScUndoDataPilot::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ //! allow deletion
+ return false;
+}
+
+ScUndoConsolidate::ScUndoConsolidate( ScDocShell* pNewDocShell, const ScArea& rArea,
+ const ScConsolidateParam& rPar, ScDocumentUniquePtr pNewUndoDoc,
+ bool bReference, SCROW nInsCount, std::unique_ptr<ScOutlineTable> pTab,
+ std::unique_ptr<ScDBData> pData )
+ : ScSimpleUndo(pNewDocShell)
+ , aDestArea(rArea)
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , aParam(rPar)
+ , bInsRef(bReference)
+ , nInsertCount(nInsCount)
+ , xUndoTab(std::move(pTab))
+ , xUndoData(std::move(pData))
+{
+}
+
+OUString ScUndoConsolidate::GetComment() const
+{
+ return ScResId( STR_UNDO_CONSOLIDATE );
+}
+
+void ScUndoConsolidate::Undo()
+{
+ BeginUndo();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = aDestArea.nTab;
+
+ ScRange aOldRange;
+ if (xUndoData)
+ xUndoData->GetArea(aOldRange);
+
+ if (bInsRef)
+ {
+ rDoc.DeleteRow( 0,nTab, rDoc.MaxCol(),nTab, aDestArea.nRowStart, nInsertCount );
+ rDoc.SetOutlineTable(nTab, xUndoTab.get());
+
+ // Row status
+ xUndoDoc->CopyToDocument(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, rDoc);
+
+ // Data and references
+ rDoc.DeleteAreaTab( 0,aDestArea.nRowStart, rDoc.MaxCol(),aDestArea.nRowEnd, nTab, InsertDeleteFlags::ALL );
+ xUndoDoc->UndoToDocument(0, aDestArea.nRowStart, nTab,
+ rDoc.MaxCol(), aDestArea.nRowEnd, nTab,
+ InsertDeleteFlags::ALL, false, rDoc);
+
+ // Original range
+ if (xUndoData)
+ {
+ rDoc.DeleteAreaTab(aOldRange, InsertDeleteFlags::ALL);
+ xUndoDoc->CopyToDocument(aOldRange, InsertDeleteFlags::ALL, false, rDoc);
+ }
+
+ pDocShell->PostPaint( 0,aDestArea.nRowStart,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Size );
+ }
+ else
+ {
+ rDoc.DeleteAreaTab( aDestArea.nColStart,aDestArea.nRowStart,
+ aDestArea.nColEnd,aDestArea.nRowEnd, nTab, InsertDeleteFlags::ALL );
+ xUndoDoc->CopyToDocument(aDestArea.nColStart, aDestArea.nRowStart, nTab,
+ aDestArea.nColEnd, aDestArea.nRowEnd, nTab,
+ InsertDeleteFlags::ALL, false, rDoc);
+
+ // Original range
+ if (xUndoData)
+ {
+ rDoc.DeleteAreaTab(aOldRange, InsertDeleteFlags::ALL);
+ xUndoDoc->CopyToDocument(aOldRange, InsertDeleteFlags::ALL, false, rDoc);
+ }
+
+ SCCOL nEndX = aDestArea.nColEnd;
+ SCROW nEndY = aDestArea.nRowEnd;
+ if (xUndoData)
+ {
+ if ( aOldRange.aEnd.Col() > nEndX )
+ nEndX = aOldRange.aEnd.Col();
+ if ( aOldRange.aEnd.Row() > nEndY )
+ nEndY = aOldRange.aEnd.Row();
+ }
+ pDocShell->PostPaint( aDestArea.nColStart, aDestArea.nRowStart, nTab,
+ nEndX, nEndY, nTab, PaintPartFlags::Grid );
+ }
+
+ // Adjust Database range again
+ if (xUndoData)
+ {
+ ScDBCollection* pColl = rDoc.GetDBCollection();
+ if (pColl)
+ {
+ ScDBData* pDocData = pColl->getNamedDBs().findByUpperName(xUndoData->GetUpperName());
+ if (pDocData)
+ *pDocData = *xUndoData;
+ }
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ SCTAB nViewTab = pViewShell->GetViewData().GetTabNo();
+ if ( nViewTab != nTab )
+ pViewShell->SetTabNo( nTab );
+ }
+
+ EndUndo();
+}
+
+void ScUndoConsolidate::Redo()
+{
+ BeginRedo();
+
+ pDocShell->DoConsolidate( aParam, false );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ SCTAB nViewTab = pViewShell->GetViewData().GetTabNo();
+ if ( nViewTab != aParam.nTab )
+ pViewShell->SetTabNo( aParam.nTab );
+ }
+
+ EndRedo();
+}
+
+void ScUndoConsolidate::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoConsolidate::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+// Change source data of Chart
+void ScUndoChartData::Init()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ aOldRangeListRef = new ScRangeList;
+ rDoc.GetOldChartParameters( aChartName, *aOldRangeListRef, bOldColHeaders, bOldRowHeaders );
+}
+
+ScUndoChartData::ScUndoChartData( ScDocShell* pNewDocShell, OUString aName,
+ const ScRange& rNew, bool bColHdr, bool bRowHdr,
+ bool bAdd ) :
+ ScSimpleUndo( pNewDocShell ),
+ aChartName(std::move( aName )),
+ bOldColHeaders(false),
+ bOldRowHeaders(false),
+ bNewColHeaders( bColHdr ),
+ bNewRowHeaders( bRowHdr ),
+ bAddRange( bAdd )
+{
+ aNewRangeListRef = new ScRangeList;
+ aNewRangeListRef->push_back( rNew );
+
+ Init();
+}
+
+ScUndoChartData::ScUndoChartData( ScDocShell* pNewDocShell, OUString aName,
+ ScRangeListRef xNew, bool bColHdr, bool bRowHdr,
+ bool bAdd ) :
+ ScSimpleUndo( pNewDocShell ),
+ aChartName(std::move( aName )),
+ bOldColHeaders(false),
+ bOldRowHeaders(false),
+ aNewRangeListRef(std::move( xNew )),
+ bNewColHeaders( bColHdr ),
+ bNewRowHeaders( bRowHdr ),
+ bAddRange( bAdd )
+{
+ Init();
+}
+
+ScUndoChartData::~ScUndoChartData()
+{
+}
+
+OUString ScUndoChartData::GetComment() const
+{
+ return ScResId( STR_UNDO_CHARTDATA );
+}
+
+void ScUndoChartData::Undo()
+{
+ BeginUndo();
+
+ pDocShell->GetDocument().UpdateChartArea( aChartName, aOldRangeListRef,
+ bOldColHeaders, bOldRowHeaders, false );
+
+ EndUndo();
+}
+
+void ScUndoChartData::Redo()
+{
+ BeginRedo();
+
+ pDocShell->GetDocument().UpdateChartArea( aChartName, aNewRangeListRef,
+ bNewColHeaders, bNewRowHeaders, bAddRange );
+
+ EndRedo();
+}
+
+void ScUndoChartData::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoChartData::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoDataForm::ScUndoDataForm( ScDocShell* pNewDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ,
+ const ScMarkData& rMark,
+ ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
+ std::unique_ptr<ScRefUndoData> pRefData )
+ : ScBlockUndo(pNewDocShell, ScRange( nStartX, nStartY, nStartZ, nEndX, nEndY, nEndZ ), SC_UNDO_SIMPLE)
+ , mxMarkData(new ScMarkData(rMark))
+ , xUndoDoc(std::move(pNewUndoDoc))
+ , xRedoDoc(std::move(pNewRedoDoc))
+ , xRefUndoData(std::move(pRefData))
+ , bRedoFilled(false)
+{
+ // pFill1,pFill2,pFill3 are there so the ctor calls for simple paste (without cutting)
+ // don't have to be changed and branched for 641.
+ // They can be removed later.
+
+ if (!mxMarkData->IsMarked()) // no cell marked:
+ mxMarkData->SetMarkArea(aBlockRange); // mark paste block
+
+ if (xRefUndoData)
+ xRefUndoData->DeleteUnchanged(&pDocShell->GetDocument());
+}
+
+OUString ScUndoDataForm::GetComment() const
+{
+ return ScResId( STR_UNDO_PASTE );
+}
+
+void ScUndoDataForm::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ ShowTable( aBlockRange );
+ EndUndo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoDataForm::Redo()
+{
+ BeginRedo();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
+ DoChange( false );
+ EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
+ EndRedo();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+void ScUndoDataForm::Repeat(SfxRepeatTarget& /*rTarget*/)
+{
+}
+
+bool ScUndoDataForm::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return (dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr);
+}
+
+void ScUndoDataForm::DoChange( const bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ // RefUndoData for redo is created before first undo
+ // (with DeleteUnchanged after the DoUndo call)
+ bool bCreateRedoData = (bUndo && xRefUndoData && !xRefRedoData);
+ if (bCreateRedoData)
+ xRefRedoData.reset(new ScRefUndoData(&rDoc));
+
+ ScRefUndoData* pWorkRefData = bUndo ? xRefUndoData.get() : xRefRedoData.get();
+
+ bool bPaintAll = false;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( bUndo && !bRedoFilled )
+ {
+ if (!xRedoDoc)
+ {
+ bool bColInfo = ( aBlockRange.aStart.Row()==0 && aBlockRange.aEnd.Row()==rDoc.MaxRow() );
+ bool bRowInfo = ( aBlockRange.aStart.Col()==0 && aBlockRange.aEnd.Col()==rDoc.MaxCol() );
+
+ xRedoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ xRedoDoc->InitUndoSelected(rDoc, *mxMarkData, bColInfo, bRowInfo);
+ }
+ // read "redo" data from the document in the first undo
+ // all sheets - CopyToDocument skips those that don't exist in pRedoDoc
+ ScRange aCopyRange = aBlockRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::VALUE, false, *xRedoDoc);
+ bRedoFilled = true;
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ pDocShell->UpdatePaintExt( nExtFlags, aBlockRange );
+
+ for ( sal_uInt16 i=0; i <= ( aBlockRange.aEnd.Col() - aBlockRange.aStart.Col() ); i++ )
+ {
+ OUString aOldString = xUndoDoc->GetString(
+ aBlockRange.aStart.Col()+i, aBlockRange.aStart.Row(), aBlockRange.aStart.Tab());
+ rDoc.SetString( aBlockRange.aStart.Col()+i , aBlockRange.aStart.Row() , aBlockRange.aStart.Tab() , aOldString );
+ }
+
+ if (pWorkRefData)
+ {
+ pWorkRefData->DoUndo( &rDoc, true ); // TRUE = bSetChartRangeLists for SetChartListenerCollection
+ if ( rDoc.RefreshAutoFilter( 0,0, rDoc.MaxCol(),rDoc.MaxRow(), aBlockRange.aStart.Tab() ) )
+ bPaintAll = true;
+ }
+
+ if (bCreateRedoData && xRefRedoData)
+ xRefRedoData->DeleteUnchanged(&rDoc);
+
+ if ( bUndo )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( 0, 0 );
+ }
+
+ ScRange aDrawRange( aBlockRange );
+ rDoc.ExtendMerge( aDrawRange, true ); // only needed for single sheet (text/rtf etc.)
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ if (bPaintAll)
+ {
+ aDrawRange.aStart.SetCol(0);
+ aDrawRange.aStart.SetRow(0);
+ aDrawRange.aEnd.SetCol(rDoc.MaxCol());
+ aDrawRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Top | PaintPartFlags::Left;
+/*A*/ if (pViewShell)
+ pViewShell->AdjustBlockHeight(false);
+ }
+ else
+ {
+ if ( aBlockRange.aStart.Row() == 0 && aBlockRange.aEnd.Row() == rDoc.MaxRow() ) // whole column
+ {
+ nPaint |= PaintPartFlags::Top;
+ aDrawRange.aEnd.SetCol(rDoc.MaxCol());
+ }
+ if ( aBlockRange.aStart.Col() == 0 && aBlockRange.aEnd.Col() == rDoc.MaxCol() ) // whole row
+ {
+ nPaint |= PaintPartFlags::Left;
+ aDrawRange.aEnd.SetRow(rDoc.MaxRow());
+ }
+/*A*/ if (pViewShell && pViewShell->AdjustBlockHeight(false))
+ {
+ aDrawRange.aStart.SetCol(0);
+ aDrawRange.aStart.SetRow(0);
+ aDrawRange.aEnd.SetCol(rDoc.MaxCol());
+ aDrawRange.aEnd.SetRow(rDoc.MaxRow());
+ nPaint |= PaintPartFlags::Left;
+ }
+ pDocShell->UpdatePaintExt( nExtFlags, aDrawRange );
+ }
+
+ if ( !bUndo ) // draw redo after updating row heights
+ RedoSdrUndoAction( pDrawUndo.get() ); //! include in ScBlockUndo?
+
+ pDocShell->PostPaint( aDrawRange, nPaint, nExtFlags );
+
+ pDocShell->PostDataChanged();
+ if (pViewShell)
+ pViewShell->CellContentChanged();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undodraw.cxx b/sc/source/ui/undo/undodraw.cxx
new file mode 100644
index 0000000000..7204f16437
--- /dev/null
+++ b/sc/source/ui/undo/undodraw.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 <undodraw.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+
+
+ScUndoDraw::ScUndoDraw( std::unique_ptr<SfxUndoAction> pUndo, ScDocShell* pDocSh ) :
+ pDrawUndo( std::move(pUndo) ),
+ pDocShell( pDocSh ),
+ mnViewShellId( -1 )
+{
+ if (ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell())
+ mnViewShellId = pViewShell->GetViewShellId();
+}
+
+ScUndoDraw::~ScUndoDraw()
+{
+}
+
+OUString ScUndoDraw::GetComment() const
+{
+ if (pDrawUndo)
+ return pDrawUndo->GetComment();
+ return OUString();
+}
+
+ViewShellId ScUndoDraw::GetViewShellId() const
+{
+ return mnViewShellId;
+}
+
+OUString ScUndoDraw::GetRepeatComment(SfxRepeatTarget& rTarget) const
+{
+ if (pDrawUndo)
+ return pDrawUndo->GetRepeatComment(rTarget);
+ return OUString();
+}
+
+bool ScUndoDraw::Merge( SfxUndoAction* pNextAction )
+{
+ if (pDrawUndo)
+ return pDrawUndo->Merge(pNextAction);
+ else
+ return false;
+}
+
+void ScUndoDraw::UpdateSubShell()
+{
+ // #i26822# remove the draw shell if the selected object has been removed
+ ScTabViewShell* pViewShell = pDocShell->GetBestViewShell();
+ if (pViewShell)
+ pViewShell->UpdateDrawShell();
+}
+
+void ScUndoDraw::Undo()
+{
+ if (pDrawUndo)
+ {
+ pDrawUndo->Undo();
+ pDocShell->SetDrawModified();
+ UpdateSubShell();
+ }
+}
+
+void ScUndoDraw::Redo()
+{
+ if (pDrawUndo)
+ {
+ pDrawUndo->Redo();
+ pDocShell->SetDrawModified();
+ UpdateSubShell();
+ }
+}
+
+void ScUndoDraw::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (pDrawUndo)
+ pDrawUndo->Repeat(rTarget);
+}
+
+bool ScUndoDraw::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ if (pDrawUndo)
+ return pDrawUndo->CanRepeat(rTarget);
+ else
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoolk.cxx b/sc/source/ui/undo/undoolk.cxx
new file mode 100644
index 0000000000..519ae20a77
--- /dev/null
+++ b/sc/source/ui/undo/undoolk.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/svdundo.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <undoolk.hxx>
+
+std::unique_ptr<SdrUndoAction> GetSdrUndoAction(ScDocument* pDoc)
+{
+ ScDrawLayer* pLayer = pDoc->GetDrawLayer();
+ if (pLayer)
+ return pLayer->GetCalcUndo(); // must exist
+ else
+ return nullptr;
+}
+
+void DoSdrUndoAction(SdrUndoAction* pUndoAction, ScDocument* pDoc)
+{
+ if (pUndoAction)
+ pUndoAction->Undo();
+ else
+ {
+ // if no drawing layer existed when the action was created,
+ // but it was created after that, there is no draw undo action,
+ // and after undo there might be a drawing layer with a wrong page count.
+ // The drawing layer must have been empty in that case, so any missing
+ // pages can just be created now.
+
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ if (pDrawLayer)
+ {
+ SCTAB nTabCount = pDoc->GetTableCount();
+ SCTAB nPages = static_cast<SCTAB>(pDrawLayer->GetPageCount());
+ while (nPages < nTabCount)
+ {
+ pDrawLayer->ScAddPage(nPages);
+ ++nPages;
+ }
+ }
+ }
+}
+
+void RedoSdrUndoAction(SdrUndoAction* pUndoAction)
+{
+ // DoSdrUndoAction/RedoSdrUndoAction is called even if the pointer is null
+ if (pUndoAction)
+ pUndoAction->Redo();
+}
+
+void EnableDrawAdjust(ScDocument* pDoc, bool bEnable)
+{
+ ScDrawLayer* pLayer = pDoc->GetDrawLayer();
+ if (pLayer)
+ pLayer->EnableAdjust(bEnable);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undorangename.cxx b/sc/source/ui/undo/undorangename.cxx
new file mode 100644
index 0000000000..7dceff763f
--- /dev/null
+++ b/sc/source/ui/undo/undorangename.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <undorangename.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <sfx2/app.hxx>
+
+#include <memory>
+#include <utility>
+
+ScUndoAllRangeNames::ScUndoAllRangeNames(
+ ScDocShell* pDocSh,
+ const std::map<OUString, ScRangeName*>& rOldNames,
+ const std::map<OUString, ScRangeName>& rNewNames)
+ : ScSimpleUndo(pDocSh)
+{
+ for (const auto& [rName, pRangeName] : rOldNames)
+ {
+ m_OldNames.insert(std::make_pair(rName, *pRangeName));
+ }
+
+ for (auto const& it : rNewNames)
+ {
+ m_NewNames.insert(std::make_pair(it.first, it.second));
+ }
+}
+
+ScUndoAllRangeNames::~ScUndoAllRangeNames()
+{
+}
+
+void ScUndoAllRangeNames::Undo()
+{
+ DoChange(m_OldNames);
+}
+
+void ScUndoAllRangeNames::Redo()
+{
+ DoChange(m_NewNames);
+}
+
+void ScUndoAllRangeNames::Repeat(SfxRepeatTarget& /*rTarget*/)
+{
+}
+
+bool ScUndoAllRangeNames::CanRepeat(SfxRepeatTarget& /*rTarget*/) const
+{
+ return false;
+}
+
+OUString ScUndoAllRangeNames::GetComment() const
+{
+ return ScResId(STR_UNDO_RANGENAMES);
+}
+
+void ScUndoAllRangeNames::DoChange(const std::map<OUString, ScRangeName>& rNames)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.PreprocessAllRangeNamesUpdate(rNames);
+ rDoc.SetAllRangeNames(rNames);
+ rDoc.CompileHybridFormula();
+
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
+}
+
+ScUndoAddRangeData::ScUndoAddRangeData(ScDocShell* pDocSh, const ScRangeData* pRangeData, SCTAB nTab) :
+ ScSimpleUndo(pDocSh),
+ mpRangeData(new ScRangeData(*pRangeData)),
+ mnTab(nTab)
+{
+
+}
+
+ScUndoAddRangeData::~ScUndoAddRangeData()
+{
+}
+
+void ScUndoAddRangeData::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangeName* pRangeName = nullptr;
+ if (mnTab == -1)
+ {
+ pRangeName = rDoc.GetRangeName();
+ }
+ else
+ {
+ pRangeName = rDoc.GetRangeName( mnTab );
+ }
+ pRangeName->erase(*mpRangeData);
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+
+}
+
+void ScUndoAddRangeData::Redo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangeName* pRangeName = nullptr;
+ if (mnTab == -1)
+ {
+ pRangeName = rDoc.GetRangeName();
+ }
+ else
+ {
+ pRangeName = rDoc.GetRangeName( mnTab );
+ }
+ pRangeName->insert(new ScRangeData(*mpRangeData));
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+}
+
+void ScUndoAddRangeData::Repeat(SfxRepeatTarget& /*rTarget*/)
+{
+}
+
+bool ScUndoAddRangeData::CanRepeat(SfxRepeatTarget& /*rTarget*/) const
+{
+ return false;
+}
+
+OUString ScUndoAddRangeData::GetComment() const
+{
+ return ScResId(STR_UNDO_RANGENAMES);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undosort.cxx b/sc/source/ui/undo/undosort.cxx
new file mode 100644
index 0000000000..4a88447202
--- /dev/null
+++ b/sc/source/ui/undo/undosort.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/.
+ */
+
+#include <undosort.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <undoutil.hxx>
+#include <utility>
+
+namespace sc {
+
+UndoSort::UndoSort( ScDocShell* pDocSh, ReorderParam aParam ) :
+ ScSimpleUndo(pDocSh), maParam(std::move(aParam)) {}
+
+OUString UndoSort::GetComment() const
+{
+ return ScResId(STR_UNDO_SORT);
+}
+
+void UndoSort::Undo()
+{
+ BeginUndo();
+ Execute(true);
+ EndUndo();
+}
+
+void UndoSort::Redo()
+{
+ BeginRedo();
+ Execute(false);
+ EndRedo();
+}
+
+void UndoSort::Execute( bool bUndo )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sc::ReorderParam aParam = maParam;
+ if (bUndo)
+ aParam.reverse();
+ rDoc.Reorder(aParam);
+
+ ScRange aOverallRange( maParam.maSortRange);
+ if (maParam.maDataAreaExtras.anyExtrasWanted())
+ {
+ aOverallRange.aStart.SetCol( maParam.maDataAreaExtras.mnStartCol);
+ aOverallRange.aStart.SetRow( maParam.maDataAreaExtras.mnStartRow);
+ aOverallRange.aEnd.SetCol( maParam.maDataAreaExtras.mnEndCol);
+ aOverallRange.aEnd.SetRow( maParam.maDataAreaExtras.mnEndRow);
+ }
+
+ if (maParam.mbHasHeaders)
+ {
+ ScRange aMarkRange( aOverallRange);
+ if (maParam.mbByRow)
+ {
+ if (aMarkRange.aStart.Row() > 0)
+ aMarkRange.aStart.IncRow(-1);
+ }
+ else
+ {
+ if (aMarkRange.aStart.Col() > 0)
+ aMarkRange.aStart.IncCol(-1);
+ }
+ ScUndoUtil::MarkSimpleBlock(pDocShell, aMarkRange);
+ }
+ else
+ {
+ ScUndoUtil::MarkSimpleBlock(pDocShell, aOverallRange);
+ }
+
+ rDoc.SetDirty(maParam.maSortRange, true);
+ if (!aParam.mbUpdateRefs)
+ rDoc.BroadcastCells(aParam.maSortRange, SfxHintId::ScDataChanged);
+
+ pDocShell->PostPaint(aOverallRange, PaintPartFlags::Grid);
+ pDocShell->PostDataChanged();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undostyl.cxx b/sc/source/ui/undo/undostyl.cxx
new file mode 100644
index 0000000000..0c1ab96c2b
--- /dev/null
+++ b/sc/source/ui/undo/undostyl.cxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/itemset.hxx>
+#include <utility>
+#include <vcl/virdev.hxx>
+#include <osl/diagnose.h>
+
+#include <undostyl.hxx>
+#include <docsh.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <printfun.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+// modify style (cell or page style)
+
+ScStyleSaveData::ScStyleSaveData()
+{
+}
+
+ScStyleSaveData::ScStyleSaveData( const ScStyleSaveData& rOther ) :
+ aName( rOther.aName ),
+ aParent( rOther.aParent )
+{
+ if (rOther.moItems)
+ moItems.emplace(*rOther.moItems);
+}
+
+ScStyleSaveData& ScStyleSaveData::operator=( const ScStyleSaveData& rOther )
+{
+ if (this != &rOther)
+ {
+ aName = rOther.aName;
+ aParent = rOther.aParent;
+ if (rOther.moItems)
+ moItems.emplace(*rOther.moItems);
+ else
+ moItems.reset();
+ }
+ return *this;
+}
+
+void ScStyleSaveData::InitFromStyle( const SfxStyleSheetBase* pSource )
+{
+ if ( pSource )
+ {
+ aName = pSource->GetName();
+ aParent = pSource->GetParent();
+ moItems.emplace(const_cast<SfxStyleSheetBase*>(pSource)->GetItemSet());
+ }
+ else
+ {
+ aName.clear();
+ aParent.clear();
+ moItems.reset();
+ }
+}
+
+ScUndoModifyStyle::ScUndoModifyStyle( ScDocShell* pDocSh, SfxStyleFamily eFam,
+ const ScStyleSaveData& rOld, const ScStyleSaveData& rNew ) :
+ ScSimpleUndo( pDocSh ),
+ eFamily( eFam ),
+ aOldData( rOld ),
+ aNewData( rNew )
+{
+}
+
+ScUndoModifyStyle::~ScUndoModifyStyle()
+{
+}
+
+OUString ScUndoModifyStyle::GetComment() const
+{
+ if (eFamily == SfxStyleFamily::Frame)
+ return ScResId(STR_UNDO_EDITGRAPHICSTYLE);
+ if (eFamily == SfxStyleFamily::Page)
+ return ScResId(STR_UNDO_EDITPAGESTYLE);
+
+ return ScResId(STR_UNDO_EDITCELLSTYLE);
+}
+
+static void lcl_DocStyleChanged( ScDocument* pDoc, const SfxStyleSheetBase* pStyle, bool bRemoved )
+{
+ //! move to document or docshell
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ double nPPTX = aLogic.X() / 1000.0;
+ double nPPTY = aLogic.Y() / 1000.0;
+ Fraction aZoom(1,1);
+ pDoc->StyleSheetChanged( pStyle, bRemoved, pVDev, nPPTX, nPPTY, aZoom, aZoom );
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+}
+
+void ScUndoModifyStyle::DoChange( ScDocShell* pDocSh, const OUString& rName,
+ SfxStyleFamily eStyleFamily, const ScStyleSaveData& rData )
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScStyleSheetPool* pStlPool = rDoc.GetStyleSheetPool();
+ const OUString& aNewName = rData.GetName();
+ bool bDelete = aNewName.isEmpty(); // no new name -> delete style
+ bool bNew = ( rName.isEmpty() && !bDelete ); // creating new style
+
+ SfxStyleSheetBase* pStyle = nullptr;
+ if ( !rName.isEmpty() )
+ {
+ // find old style to modify
+ pStyle = pStlPool->Find( rName, eStyleFamily );
+ OSL_ENSURE( pStyle, "style not found" );
+
+ if ( pStyle && !bDelete )
+ {
+ // set new name
+ pStyle->SetName( aNewName );
+ }
+ }
+ else if ( !bDelete )
+ {
+ // create style (with new name)
+ pStyle = &pStlPool->Make( aNewName, eStyleFamily, SfxStyleSearchBits::UserDefined );
+
+ if ( eStyleFamily == SfxStyleFamily::Para )
+ rDoc.GetPool()->CellStyleCreated( aNewName, rDoc );
+ }
+
+ if ( pStyle )
+ {
+ if ( bDelete )
+ {
+ if ( eStyleFamily == SfxStyleFamily::Para )
+ lcl_DocStyleChanged( &rDoc, pStyle, true ); // TRUE: remove usage of style
+ else if ( eStyleFamily == SfxStyleFamily::Page )
+ rDoc.RemovePageStyleInUse( rName );
+
+ // delete style
+ pStlPool->Remove( pStyle );
+ }
+ else
+ {
+ // modify style
+
+ const OUString& aNewParent = rData.GetParent();
+ if ( aNewParent != pStyle->GetParent() )
+ pStyle->SetParent( aNewParent );
+
+ SfxItemSet& rStyleSet = pStyle->GetItemSet();
+ const std::optional<SfxItemSet>& pNewSet = rData.GetItems();
+ OSL_ENSURE( pNewSet, "no ItemSet for style" );
+ if (pNewSet)
+ rStyleSet.Set( *pNewSet, false );
+
+ if ( eStyleFamily == SfxStyleFamily::Para )
+ {
+ lcl_DocStyleChanged( &rDoc, pStyle, false ); // cell styles: row heights
+ }
+ else if ( eStyleFamily == SfxStyleFamily::Page )
+ {
+ // page styles
+
+ if ( bNew && aNewName != rName )
+ rDoc.RenamePageStyleInUse( rName, aNewName );
+
+ if (pNewSet)
+ rDoc.ModifyStyleSheet( *pStyle, *pNewSet );
+
+ pDocSh->PageStyleModified( aNewName, true );
+ }
+ else
+ static_cast<SfxStyleSheet*>(pStyle)->Broadcast(SfxHint(SfxHintId::DataChanged));
+ }
+ }
+
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+
+ //! undo/redo document modifications for deleted styles
+ //! undo/redo modifications of number formatter
+}
+
+void ScUndoModifyStyle::Undo()
+{
+ BeginUndo();
+ DoChange( pDocShell, aNewData.GetName(), eFamily, aOldData );
+ EndUndo();
+}
+
+void ScUndoModifyStyle::Redo()
+{
+ BeginRedo();
+ DoChange( pDocShell, aOldData.GetName(), eFamily, aNewData );
+ EndRedo();
+}
+
+void ScUndoModifyStyle::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoModifyStyle::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // no repeat possible
+}
+
+// apply page style
+
+ScUndoApplyPageStyle::ApplyStyleEntry::ApplyStyleEntry( SCTAB nTab, OUString aOldStyle ) :
+ mnTab( nTab ),
+ maOldStyle(std::move( aOldStyle ))
+{
+}
+
+ScUndoApplyPageStyle::ScUndoApplyPageStyle( ScDocShell* pDocSh, OUString aNewStyle ) :
+ ScSimpleUndo( pDocSh ),
+ maNewStyle(std::move( aNewStyle ))
+{
+}
+
+ScUndoApplyPageStyle::~ScUndoApplyPageStyle()
+{
+}
+
+void ScUndoApplyPageStyle::AddSheetAction( SCTAB nTab, const OUString& rOldStyle )
+{
+ maEntries.emplace_back( nTab, rOldStyle );
+}
+
+OUString ScUndoApplyPageStyle::GetComment() const
+{
+ return ScResId( STR_UNDO_APPLYPAGESTYLE );
+}
+
+void ScUndoApplyPageStyle::Undo()
+{
+ BeginUndo();
+ for( const auto& rEntry : maEntries )
+ {
+ pDocShell->GetDocument().SetPageStyle( rEntry.mnTab, rEntry.maOldStyle );
+ ScPrintFunc( pDocShell, pDocShell->GetPrinter(), rEntry.mnTab ).UpdatePages();
+ }
+ EndUndo();
+}
+
+void ScUndoApplyPageStyle::Redo()
+{
+ BeginRedo();
+ for( const auto& rEntry : maEntries )
+ {
+ pDocShell->GetDocument().SetPageStyle( rEntry.mnTab, maNewStyle );
+ ScPrintFunc( pDocShell, pDocShell->GetPrinter(), rEntry.mnTab ).UpdatePages();
+ }
+ EndRedo();
+}
+
+void ScUndoApplyPageStyle::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ //! set same page style to current tab
+}
+
+bool ScUndoApplyPageStyle::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undotab.cxx b/sc/source/ui/undo/undotab.cxx
new file mode 100644
index 0000000000..4237aab463
--- /dev/null
+++ b/sc/source/ui/undo/undotab.cxx
@@ -0,0 +1,1570 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/hint.hxx>
+#include <osl/diagnose.h>
+
+#include <undotab.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <undoolk.hxx>
+#include <target.hxx>
+#include <uiitems.hxx>
+#include <prnsave.hxx>
+#include <printfun.hxx>
+#include <chgtrack.hxx>
+#include <tabprotection.hxx>
+#include <utility>
+#include <viewdata.hxx>
+#include <progress.hxx>
+#include <markdata.hxx>
+#include <refundo.hxx>
+
+// for ScUndoRenameObject - might me moved to another file later
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <drwlayer.hxx>
+#include <scresid.hxx>
+#include <sheetevents.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <tools/json_writer.hxx>
+
+#include <memory>
+#include <vector>
+
+using namespace com::sun::star;
+using ::std::unique_ptr;
+using ::std::vector;
+
+
+ScUndoInsertTab::ScUndoInsertTab( ScDocShell* pNewDocShell,
+ SCTAB nTabNum,
+ bool bApp,
+ OUString aNewName) :
+ ScSimpleUndo( pNewDocShell ),
+ sNewName(std::move( aNewName )),
+ nTab( nTabNum ),
+ bAppend( bApp )
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+ SetChangeTrack();
+}
+
+ScUndoInsertTab::~ScUndoInsertTab()
+{
+ pDrawUndo.reset();
+}
+
+OUString ScUndoInsertTab::GetComment() const
+{
+ if (bAppend)
+ return ScResId( STR_UNDO_APPEND_TAB );
+ else
+ return ScResId( STR_UNDO_INSERT_TAB );
+}
+
+void ScUndoInsertTab::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
+ pChangeTrack->AppendInsert( aRange );
+ nEndChangeAction = pChangeTrack->GetActionMax();
+ }
+ else
+ nEndChangeAction = 0;
+}
+
+void ScUndoInsertTab::Undo()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ pViewShell->SetTabNo(nTab);
+
+ pDocShell->SetInUndo( true ); //! BeginUndo
+ bDrawIsInUndo = true;
+ pViewShell->DeleteTable( nTab, false );
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( false ); //! EndUndo
+
+ DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() );
+
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nEndChangeAction, nEndChangeAction );
+
+ // SetTabNo(...,sal_True) for all views to sync with drawing layer pages
+ pDocShell->Broadcast( SfxHint( SfxHintId::ScForceSetTab ) );
+}
+
+void ScUndoInsertTab::Redo()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ RedoSdrUndoAction( pDrawUndo.get() ); // Draw Redo first
+
+ pDocShell->SetInUndo( true ); //! BeginRedo
+ bDrawIsInUndo = true;
+ if (bAppend)
+ pViewShell->AppendTable( sNewName, false );
+ else
+ {
+ pViewShell->SetTabNo(nTab);
+ pViewShell->InsertTable( sNewName, nTab, false );
+ }
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( false ); //! EndRedo
+
+ SetChangeTrack();
+}
+
+void ScUndoInsertTab::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->GetViewData().GetDispatcher().
+ Execute(FID_INS_TABLE, SfxCallMode::SLOT | SfxCallMode::RECORD);
+}
+
+bool ScUndoInsertTab::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoInsertTables::ScUndoInsertTables( ScDocShell* pNewDocShell,
+ SCTAB nTabNum,
+ std::vector<OUString>&& newNameList) :
+ ScSimpleUndo( pNewDocShell ),
+ aNameList( std::move(newNameList) ),
+ nTab( nTabNum )
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+
+ SetChangeTrack();
+}
+
+ScUndoInsertTables::~ScUndoInsertTables()
+{
+ pDrawUndo.reset();
+}
+
+OUString ScUndoInsertTables::GetComment() const
+{
+ return ScResId( STR_UNDO_INSERT_TAB );
+}
+
+void ScUndoInsertTables::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ nStartChangeAction = pChangeTrack->GetActionMax() + 1;
+ nEndChangeAction = 0;
+ ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
+ for( size_t i = 0; i < aNameList.size(); i++ )
+ {
+ aRange.aStart.SetTab( sal::static_int_cast<SCTAB>( nTab + i ) );
+ aRange.aEnd.SetTab( sal::static_int_cast<SCTAB>( nTab + i ) );
+ pChangeTrack->AppendInsert( aRange );
+ nEndChangeAction = pChangeTrack->GetActionMax();
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+void ScUndoInsertTables::Undo()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ pViewShell->SetTabNo(nTab);
+
+ pDocShell->SetInUndo( true ); //! BeginUndo
+ bDrawIsInUndo = true;
+
+ pViewShell->DeleteTables( nTab, static_cast<SCTAB>(aNameList.size()) );
+
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( false ); //! EndUndo
+
+ DoSdrUndoAction( pDrawUndo.get(), &pDocShell->GetDocument() );
+
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ // SetTabNo(...,sal_True) for all views to sync with drawing layer pages
+ pDocShell->Broadcast( SfxHint( SfxHintId::ScForceSetTab ) );
+}
+
+void ScUndoInsertTables::Redo()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ RedoSdrUndoAction( pDrawUndo.get() ); // Draw Redo first
+
+ pDocShell->SetInUndo( true ); //! BeginRedo
+ bDrawIsInUndo = true;
+ pViewShell->InsertTables( aNameList, nTab, static_cast<SCTAB>(aNameList.size()),false );
+
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( false ); //! EndRedo
+
+ SetChangeTrack();
+}
+
+void ScUndoInsertTables::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->GetViewData().GetDispatcher().
+ Execute(FID_INS_TABLE, SfxCallMode::SLOT | SfxCallMode::RECORD);
+}
+
+bool ScUndoInsertTables::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoDeleteTab::ScUndoDeleteTab( ScDocShell* pNewDocShell, const vector<SCTAB> &aTab,
+ ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData ) :
+ ScMoveUndo( pNewDocShell, std::move(pUndoDocument), std::move(pRefData) )
+{
+ theTabs.insert(theTabs.end(), aTab.begin(), aTab.end() );
+ SetChangeTrack();
+}
+
+ScUndoDeleteTab::~ScUndoDeleteTab()
+{
+ theTabs.clear();
+}
+
+OUString ScUndoDeleteTab::GetComment() const
+{
+ return ScResId( STR_UNDO_DELETE_TAB );
+}
+
+void ScUndoDeleteTab::SetChangeTrack()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ {
+ sal_uLong nTmpChangeAction;
+ nStartChangeAction = pChangeTrack->GetActionMax() + 1;
+ nEndChangeAction = 0;
+ ScRange aRange( 0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), 0 );
+ for ( size_t i = 0; i < theTabs.size(); ++i )
+ {
+ aRange.aStart.SetTab( theTabs[i] );
+ aRange.aEnd.SetTab( theTabs[i] );
+ pChangeTrack->AppendDeleteRange( aRange, pRefUndoDoc.get(),
+ nTmpChangeAction, nEndChangeAction, static_cast<short>(i) );
+ }
+ }
+ else
+ nStartChangeAction = nEndChangeAction = 0;
+}
+
+static SCTAB lcl_GetVisibleTabBefore( const ScDocument& rDoc, SCTAB nTab )
+{
+ while ( nTab > 0 && !rDoc.IsVisible( nTab ) )
+ --nTab;
+
+ return nTab;
+}
+
+void ScUndoDeleteTab::Undo()
+{
+ BeginUndo();
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ bool bLink = false;
+ OUString aName;
+
+ for(SCTAB nTab: theTabs)
+ {
+ pRefUndoDoc->GetName( nTab, aName );
+
+ bDrawIsInUndo = true;
+ bool bOk = rDoc.InsertTab(nTab, aName, false, true);
+ bDrawIsInUndo = false;
+ if (bOk)
+ {
+ pRefUndoDoc->CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::ALL,false, rDoc);
+
+ OUString aOldName;
+ pRefUndoDoc->GetName( nTab, aOldName );
+ rDoc.RenameTab( nTab, aOldName );
+ if (pRefUndoDoc->IsLinked(nTab))
+ {
+ rDoc.SetLink( nTab, pRefUndoDoc->GetLinkMode(nTab), pRefUndoDoc->GetLinkDoc(nTab),
+ pRefUndoDoc->GetLinkFlt(nTab), pRefUndoDoc->GetLinkOpt(nTab),
+ pRefUndoDoc->GetLinkTab(nTab), pRefUndoDoc->GetLinkRefreshDelay(nTab) );
+ bLink = true;
+ }
+
+ if ( pRefUndoDoc->IsScenario(nTab) )
+ {
+ rDoc.SetScenario( nTab, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ pRefUndoDoc->GetScenarioData( nTab, aComment, aColor, nScenFlags );
+ rDoc.SetScenarioData( nTab, aComment, aColor, nScenFlags );
+ bool bActive = pRefUndoDoc->IsActiveScenario( nTab );
+ rDoc.SetActiveScenario( nTab, bActive );
+ }
+ rDoc.SetVisible( nTab, pRefUndoDoc->IsVisible( nTab ) );
+ rDoc.SetTabBgColor( nTab, pRefUndoDoc->GetTabBgColor(nTab) );
+ auto pSheetEvents = pRefUndoDoc->GetSheetEvents( nTab );
+ rDoc.SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
+ rDoc.SetLayoutRTL( nTab, pRefUndoDoc->IsLayoutRTL( nTab ) );
+
+ if ( pRefUndoDoc->IsTabProtected( nTab ) )
+ rDoc.SetTabProtection(nTab, pRefUndoDoc->GetTabProtection(nTab));
+ }
+ }
+ if (bLink)
+ {
+ pDocShell->UpdateLinks(); // update Link Manager
+ }
+
+ EndUndo(); // Draw-Undo has to be called before Broadcast!
+
+ ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
+
+ for(SCTAB nTab: theTabs)
+ {
+ pDocShell->Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab) );
+ }
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+
+ pDocShell->PostPaint(0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::All ); // incl. extras
+
+ // not ShowTable due to SetTabNo(..., sal_True):
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo( lcl_GetVisibleTabBefore( rDoc, theTabs[0] ), true );
+}
+
+void ScUndoDeleteTab::Redo()
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ pViewShell->SetTabNo( lcl_GetVisibleTabBefore( pDocShell->GetDocument(), theTabs.front() ) );
+
+ RedoSdrUndoAction( pDrawUndo.get() ); // Draw Redo first
+
+ pDocShell->SetInUndo( true ); //! BeginRedo
+ bDrawIsInUndo = true;
+ pViewShell->DeleteTables( theTabs, false );
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( true ); //! EndRedo
+
+ SetChangeTrack();
+
+ // SetTabNo(...,sal_True) for all views to sync with drawing layer pages
+ pDocShell->Broadcast( SfxHint( SfxHintId::ScForceSetTab ) );
+}
+
+void ScUndoDeleteTab::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ ScTabViewShell* pViewShell = pViewTarget->GetViewShell();
+ pViewShell->DeleteTable( pViewShell->GetViewData().GetTabNo() );
+ }
+}
+
+bool ScUndoDeleteTab::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoRenameTab::ScUndoRenameTab( ScDocShell* pNewDocShell,
+ SCTAB nT,
+ const OUString& rOldName,
+ const OUString& rNewName) :
+ ScSimpleUndo( pNewDocShell ),
+ nTab ( nT )
+{
+ sOldName = rOldName;
+ sNewName = rNewName;
+}
+
+ScUndoRenameTab::~ScUndoRenameTab()
+{
+}
+
+OUString ScUndoRenameTab::GetComment() const
+{
+ return ScResId( STR_UNDO_RENAME_TAB );
+}
+
+void ScUndoRenameTab::DoChange( SCTAB nTabP, const OUString& rName ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.RenameTab( nTabP, rName );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); // Navigator
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) ); // Also Name Box
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->PostPaintExtras();
+ pDocShell->PostDataChanged();
+
+ // The sheet name might be used in a formula ...
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->UpdateInputHandler();
+}
+
+void ScUndoRenameTab::Undo()
+{
+ DoChange(nTab, sOldName);
+}
+
+void ScUndoRenameTab::Redo()
+{
+ DoChange(nTab, sNewName);
+}
+
+void ScUndoRenameTab::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoRenameTab::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoMoveTab::ScUndoMoveTab(
+ ScDocShell* pNewDocShell, std::unique_ptr<vector<SCTAB>> pOldTabs, std::unique_ptr<vector<SCTAB>> pNewTabs,
+ std::unique_ptr<vector<OUString>> pOldNames, std::unique_ptr<vector<OUString>> pNewNames) :
+ ScSimpleUndo( pNewDocShell ),
+ mpOldTabs(std::move(pOldTabs)), mpNewTabs(std::move(pNewTabs)),
+ mpOldNames(std::move(pOldNames)), mpNewNames(std::move(pNewNames))
+{
+ // The sizes differ. Something is wrong.
+ assert(!mpOldNames || mpOldTabs->size() == mpOldNames->size());
+ // The sizes differ. Something is wrong.
+ assert(!mpNewNames || mpNewTabs->size() == mpNewNames->size());
+}
+
+ScUndoMoveTab::~ScUndoMoveTab()
+{
+}
+
+OUString ScUndoMoveTab::GetComment() const
+{
+ return ScResId( STR_UNDO_MOVE_TAB );
+}
+
+void ScUndoMoveTab::DoChange( bool bUndo ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ if (bUndo) // UnDo
+ {
+ size_t i = mpNewTabs->size();
+ ScProgress aProgress(pDocShell, ScResId(STR_UNDO_MOVE_TAB),
+ i * rDoc.GetCodeCount(), true);
+ for (; i > 0; --i)
+ {
+ SCTAB nDestTab = (*mpNewTabs)[i-1];
+ SCTAB nOldTab = (*mpOldTabs)[i-1];
+ if (nDestTab > MAXTAB) // appended ?
+ nDestTab = rDoc.GetTableCount() - 1;
+
+ rDoc.MoveTab( nDestTab, nOldTab, &aProgress );
+ pViewShell->GetViewData().MoveTab( nDestTab, nOldTab );
+ pViewShell->SetTabNo( nOldTab, true );
+ if (mpOldNames)
+ {
+ const OUString& rOldName = (*mpOldNames)[i-1];
+ rDoc.RenameTab(nOldTab, rOldName);
+ }
+ }
+ }
+ else
+ {
+ size_t n = mpNewTabs->size();
+ ScProgress aProgress(pDocShell, ScResId(STR_UNDO_MOVE_TAB),
+ n * rDoc.GetCodeCount(), true);
+ for (size_t i = 0; i < n; ++i)
+ {
+ SCTAB nDestTab = (*mpNewTabs)[i];
+ SCTAB nNewTab = nDestTab;
+ SCTAB nOldTab = (*mpOldTabs)[i];
+ if (nDestTab > MAXTAB) // appended ?
+ nDestTab = rDoc.GetTableCount() - 1;
+
+ rDoc.MoveTab( nOldTab, nNewTab, &aProgress );
+ pViewShell->GetViewData().MoveTab( nOldTab, nNewTab );
+ pViewShell->SetTabNo( nDestTab, true );
+ if (mpNewNames)
+ {
+ const OUString& rNewName = (*mpNewNames)[i];
+ rDoc.RenameTab(nNewTab, rNewName);
+ }
+ }
+ }
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); // Navigator
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->PostPaintExtras();
+ pDocShell->PostDataChanged();
+}
+
+void ScUndoMoveTab::Undo()
+{
+ DoChange( true );
+}
+
+void ScUndoMoveTab::Redo()
+{
+ DoChange( false );
+}
+
+void ScUndoMoveTab::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // No Repeat ! ? !
+}
+
+bool ScUndoMoveTab::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoCopyTab::ScUndoCopyTab(
+ ScDocShell* pNewDocShell,
+ std::unique_ptr<vector<SCTAB>> pOldTabs, std::unique_ptr<vector<SCTAB>> pNewTabs,
+ std::unique_ptr<vector<OUString>> pNewNames) :
+ ScSimpleUndo( pNewDocShell ),
+ mpOldTabs(std::move(pOldTabs)),
+ mpNewTabs(std::move(pNewTabs)),
+ mpNewNames(std::move(pNewNames))
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+
+ // The sizes differ. Something is wrong.
+ assert(!mpNewNames || mpNewTabs->size() == mpNewNames->size());
+}
+
+ScUndoCopyTab::~ScUndoCopyTab()
+{
+ pDrawUndo.reset();
+}
+
+OUString ScUndoCopyTab::GetComment() const
+{
+ return ScResId( STR_UNDO_COPY_TAB );
+}
+
+void ScUndoCopyTab::DoChange() const
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ if (pViewShell)
+ pViewShell->SetTabNo((*mpOldTabs)[0],true);
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->PostPaintExtras();
+ pDocShell->PostDataChanged();
+}
+
+void ScUndoCopyTab::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ DoSdrUndoAction( pDrawUndo.get(), &rDoc ); // before the sheets are deleted
+
+ vector<SCTAB>::const_reverse_iterator itr, itrEnd = mpNewTabs->rend();
+ for (itr = mpNewTabs->rbegin(); itr != itrEnd; ++itr)
+ {
+ SCTAB nDestTab = *itr;
+ if (nDestTab > MAXTAB) // append?
+ nDestTab = rDoc.GetTableCount() - 1;
+
+ bDrawIsInUndo = true;
+ rDoc.DeleteTab(nDestTab);
+ bDrawIsInUndo = false;
+ }
+
+ // ScTablesHint broadcasts after all sheets have been deleted,
+ // so sheets and draw pages are in sync!
+
+ for (itr = mpNewTabs->rbegin(); itr != itrEnd; ++itr)
+ {
+ SCTAB nDestTab = *itr;
+ if (nDestTab > MAXTAB) // append?
+ nDestTab = rDoc.GetTableCount() - 1;
+
+ pDocShell->Broadcast( ScTablesHint( SC_TAB_DELETED, nDestTab ) );
+ }
+
+ DoChange();
+}
+
+void ScUndoCopyTab::Redo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ SCTAB nDestTab = 0;
+ for (size_t i = 0, n = mpNewTabs->size(); i < n; ++i)
+ {
+ nDestTab = (*mpNewTabs)[i];
+ SCTAB nNewTab = nDestTab;
+ SCTAB nOldTab = (*mpOldTabs)[i];
+ if (nDestTab > MAXTAB) // appended ?
+ nDestTab = rDoc.GetTableCount() - 1;
+
+ bDrawIsInUndo = true;
+ rDoc.CopyTab( nOldTab, nNewTab );
+ bDrawIsInUndo = false;
+
+ pViewShell->GetViewData().MoveTab( nOldTab, nNewTab );
+
+ SCTAB nAdjSource = nOldTab;
+ if ( nNewTab <= nOldTab )
+ ++nAdjSource; // new position of source table after CopyTab
+
+ if ( rDoc.IsScenario(nAdjSource) )
+ {
+ rDoc.SetScenario(nNewTab, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ rDoc.GetScenarioData(nAdjSource, aComment, aColor, nScenFlags );
+ rDoc.SetScenarioData(nNewTab, aComment, aColor, nScenFlags );
+ bool bActive = rDoc.IsActiveScenario(nAdjSource);
+ rDoc.SetActiveScenario(nNewTab, bActive );
+ bool bVisible = rDoc.IsVisible(nAdjSource);
+ rDoc.SetVisible(nNewTab,bVisible );
+ }
+
+ if ( rDoc.IsTabProtected( nAdjSource ) )
+ rDoc.CopyTabProtection(nAdjSource, nNewTab);
+
+ if (mpNewNames)
+ {
+ const OUString& rName = (*mpNewNames)[i];
+ rDoc.RenameTab(nNewTab, rName);
+ }
+ }
+
+ RedoSdrUndoAction( pDrawUndo.get() ); // after the sheets are inserted
+
+ pViewShell->SetTabNo( nDestTab, true ); // after draw-undo
+
+ DoChange();
+
+}
+
+void ScUndoCopyTab::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // no Repeat ! ? !
+}
+
+bool ScUndoCopyTab::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoTabColor::ScUndoTabColor(
+ ScDocShell* pNewDocShell, SCTAB nT, const Color& aOTabBgColor, const Color& aNTabBgColor) :
+ ScSimpleUndo( pNewDocShell )
+{
+ ScUndoTabColorInfo aInfo(nT);
+ aInfo.maOldTabBgColor = aOTabBgColor;
+ aInfo.maNewTabBgColor = aNTabBgColor;
+ aTabColorList.push_back(aInfo);
+}
+
+ScUndoTabColor::ScUndoTabColor(
+ ScDocShell* pNewDocShell,
+ ScUndoTabColorInfo::List&& rUndoTabColorList) :
+ ScSimpleUndo(pNewDocShell),
+ aTabColorList(std::move(rUndoTabColorList))
+{
+}
+
+ScUndoTabColor::~ScUndoTabColor()
+{
+}
+
+OUString ScUndoTabColor::GetComment() const
+{
+ if (aTabColorList.size() > 1)
+ return ScResId(STR_UNDO_SET_MULTI_TAB_BG_COLOR);
+ return ScResId(STR_UNDO_SET_TAB_BG_COLOR);
+}
+
+void ScUndoTabColor::DoChange(bool bUndoType) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ size_t nTabColorCount = aTabColorList.size();
+ for (size_t i = 0; i < nTabColorCount; ++i)
+ {
+ const ScUndoTabColorInfo& rTabColor = aTabColorList[i];
+ rDoc.SetTabBgColor(rTabColor.mnTabId,
+ bUndoType ? rTabColor.maOldTabBgColor : rTabColor.maNewTabBgColor);
+ }
+
+ pDocShell->PostPaintExtras();
+ ScDocShellModificator aModificator( *pDocShell );
+ aModificator.SetDocumentModified();
+}
+
+void ScUndoTabColor::Undo()
+{
+ DoChange(true);
+}
+
+void ScUndoTabColor::Redo()
+{
+ DoChange(false);
+}
+
+void ScUndoTabColor::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoTabColor::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoMakeScenario::ScUndoMakeScenario( ScDocShell* pNewDocShell,
+ SCTAB nSrc, SCTAB nDest,
+ OUString aN, OUString aC,
+ const Color& rCol, ScScenarioFlags nF,
+ const ScMarkData& rMark ) :
+ ScSimpleUndo( pNewDocShell ),
+ mpMarkData(new ScMarkData(rMark)),
+ nSrcTab( nSrc ),
+ nDestTab( nDest ),
+ aName(std::move( aN )),
+ aComment(std::move( aC )),
+ aColor( rCol ),
+ nFlags( nF )
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScUndoMakeScenario::~ScUndoMakeScenario()
+{
+ pDrawUndo.reset();
+}
+
+OUString ScUndoMakeScenario::GetComment() const
+{
+ return ScResId( STR_UNDO_MAKESCENARIO );
+}
+
+void ScUndoMakeScenario::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ pDocShell->SetInUndo( true );
+ bDrawIsInUndo = true;
+ rDoc.DeleteTab( nDestTab );
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( false );
+
+ DoSdrUndoAction( pDrawUndo.get(), &rDoc );
+
+ pDocShell->PostPaint(0,0,nDestTab,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::All);
+ pDocShell->PostDataChanged();
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo( nSrcTab, true );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+
+ // SetTabNo(...,sal_True) for all views to sync with drawing layer pages
+ pDocShell->Broadcast( SfxHint( SfxHintId::ScForceSetTab ) );
+}
+
+void ScUndoMakeScenario::Redo()
+{
+ SetViewMarkData(*mpMarkData);
+
+ RedoSdrUndoAction( pDrawUndo.get() ); // Draw Redo first
+
+ pDocShell->SetInUndo( true );
+ bDrawIsInUndo = true;
+
+ pDocShell->MakeScenario( nSrcTab, aName, aComment, aColor, nFlags, *mpMarkData, false );
+
+ bDrawIsInUndo = false;
+ pDocShell->SetInUndo( false );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo( nDestTab, true );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+}
+
+void ScUndoMakeScenario::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ {
+ pViewTarget->GetViewShell()->MakeScenario( aName, aComment, aColor, nFlags );
+ }
+}
+
+bool ScUndoMakeScenario::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoImportTab::ScUndoImportTab(ScDocShell* pShell,
+ SCTAB nNewTab, SCTAB nNewCount)
+ : ScSimpleUndo(pShell)
+ , nTab(nNewTab)
+ , nCount(nNewCount)
+{
+ pDrawUndo = GetSdrUndoAction( &pDocShell->GetDocument() );
+}
+
+ScUndoImportTab::~ScUndoImportTab()
+{
+ pDrawUndo.reset();
+}
+
+OUString ScUndoImportTab::GetComment() const
+{
+ return ScResId( STR_UNDO_INSERT_TAB );
+}
+
+void ScUndoImportTab::DoChange() const
+{
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if (pViewShell)
+ {
+ if(nTab<nTabCount)
+ {
+ pViewShell->SetTabNo(nTab,true);
+ }
+ else
+ {
+ pViewShell->SetTabNo(nTab-1,true);
+ }
+ }
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); // Navigator
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB,
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras );
+}
+
+void ScUndoImportTab::Undo()
+{
+ // Inserted range names, etc.
+
+ SCTAB i;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bMakeRedo = !xRedoDoc;
+ if (bMakeRedo)
+ {
+ xRedoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ xRedoDoc->InitUndo(rDoc, nTab,nTab+nCount-1, true, true);
+
+ OUString aOldName;
+ for (i=0; i<nCount; i++)
+ {
+ SCTAB nTabPos=nTab+i;
+
+ rDoc.CopyToDocument(0,0,nTabPos, rDoc.MaxCol(),rDoc.MaxRow(),nTabPos, InsertDeleteFlags::ALL,false, *xRedoDoc);
+ rDoc.GetName( nTabPos, aOldName );
+ xRedoDoc->RenameTab(nTabPos, aOldName);
+ xRedoDoc->SetTabBgColor(nTabPos, rDoc.GetTabBgColor(nTabPos));
+
+ if ( rDoc.IsScenario(nTabPos) )
+ {
+ xRedoDoc->SetScenario(nTabPos, true);
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ rDoc.GetScenarioData(nTabPos, aComment, aColor, nScenFlags );
+ xRedoDoc->SetScenarioData(nTabPos, aComment, aColor, nScenFlags);
+ bool bActive = rDoc.IsActiveScenario(nTabPos);
+ xRedoDoc->SetActiveScenario(nTabPos, bActive);
+ bool bVisible = rDoc.IsVisible(nTabPos);
+ xRedoDoc->SetVisible(nTabPos, bVisible);
+ }
+
+ if ( rDoc.IsTabProtected( nTabPos ) )
+ xRedoDoc->SetTabProtection(nTabPos, rDoc.GetTabProtection(nTabPos));
+ }
+
+ }
+
+ DoSdrUndoAction( pDrawUndo.get(), &rDoc ); // before the sheets are deleted
+
+ bDrawIsInUndo = true;
+ for (i=0; i<nCount; i++)
+ rDoc.DeleteTab( nTab );
+ bDrawIsInUndo = false;
+
+ DoChange();
+}
+
+void ScUndoImportTab::Redo()
+{
+ if (!xRedoDoc)
+ {
+ OSL_FAIL("Where is my Redo Document?");
+ return;
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OUString aName;
+ SCTAB i;
+ for (i=0; i<nCount; i++) // first insert all sheets (#63304#)
+ {
+ SCTAB nTabPos=nTab+i;
+ xRedoDoc->GetName(nTabPos, aName);
+ bDrawIsInUndo = true;
+ rDoc.InsertTab(nTabPos,aName);
+ bDrawIsInUndo = false;
+ }
+ for (i=0; i<nCount; i++) // then copy into inserted sheets
+ {
+ SCTAB nTabPos=nTab+i;
+ xRedoDoc->CopyToDocument(0,0,nTabPos, rDoc.MaxCol(),rDoc.MaxRow(),nTabPos, InsertDeleteFlags::ALL,false, rDoc);
+ rDoc.SetTabBgColor(nTabPos, xRedoDoc->GetTabBgColor(nTabPos));
+
+ if (xRedoDoc->IsScenario(nTabPos))
+ {
+ rDoc.SetScenario(nTabPos, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ xRedoDoc->GetScenarioData(nTabPos, aComment, aColor, nScenFlags );
+ rDoc.SetScenarioData(nTabPos, aComment, aColor, nScenFlags );
+ bool bActive = xRedoDoc->IsActiveScenario(nTabPos);
+ rDoc.SetActiveScenario(nTabPos, bActive );
+ bool bVisible = xRedoDoc->IsVisible(nTabPos);
+ rDoc.SetVisible(nTabPos,bVisible );
+ }
+
+ if (xRedoDoc->IsTabProtected(nTabPos))
+ rDoc.SetTabProtection(nTabPos, xRedoDoc->GetTabProtection(nTabPos));
+ }
+
+ RedoSdrUndoAction( pDrawUndo.get() ); // after the sheets are inserted
+
+ DoChange();
+}
+
+void ScUndoImportTab::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->GetViewData().GetDispatcher().
+ Execute(FID_INS_TABLE, SfxCallMode::SLOT | SfxCallMode::RECORD);
+}
+
+bool ScUndoImportTab::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+ScUndoRemoveLink::ScUndoRemoveLink( ScDocShell* pShell, OUString _aDocName ) :
+ ScSimpleUndo( pShell ),
+ aDocName(std::move( _aDocName )),
+ nRefreshDelay( 0 ),
+ nCount( 0 )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pTabs.reset( new SCTAB[nTabCount] );
+ pModes.reset( new ScLinkMode[nTabCount] );
+ pTabNames.reset( new OUString[nTabCount] );
+
+ for (SCTAB i=0; i<nTabCount; i++)
+ {
+ ScLinkMode nMode = rDoc.GetLinkMode(i);
+ if (nMode != ScLinkMode::NONE)
+ if (rDoc.GetLinkDoc(i) == aDocName)
+ {
+ if (!nCount)
+ {
+ aFltName = rDoc.GetLinkFlt(i);
+ aOptions = rDoc.GetLinkOpt(i);
+ nRefreshDelay = rDoc.GetLinkRefreshDelay(i);
+ }
+ else
+ {
+ OSL_ENSURE(aFltName == rDoc.GetLinkFlt(i) &&
+ aOptions == rDoc.GetLinkOpt(i),
+ "different Filter for a Document?");
+ }
+ pTabs[nCount] = i;
+ pModes[nCount] = nMode;
+ pTabNames[nCount] = rDoc.GetLinkTab(i);
+ ++nCount;
+ }
+ }
+}
+
+ScUndoRemoveLink::~ScUndoRemoveLink()
+{
+}
+
+OUString ScUndoRemoveLink::GetComment() const
+{
+ return ScResId( STR_UNDO_REMOVELINK );
+}
+
+void ScUndoRemoveLink::DoChange( bool bLink ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ if (bLink) // establish link
+ rDoc.SetLink( pTabs[i], pModes[i], aDocName, aFltName, aOptions, pTabNames[i], nRefreshDelay );
+ else // remove link
+ rDoc.SetLink( pTabs[i], ScLinkMode::NONE, "", "", "", "", 0 );
+ pDocShell->UpdateLinks();
+}
+
+void ScUndoRemoveLink::Undo()
+{
+ DoChange( true );
+}
+
+void ScUndoRemoveLink::Redo()
+{
+ DoChange( false );
+}
+
+void ScUndoRemoveLink::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoRemoveLink::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoShowHideTab::ScUndoShowHideTab( ScDocShell* pShell, std::vector<SCTAB>&& newUndoTabs, bool bNewShow ) :
+ ScSimpleUndo( pShell ),
+ undoTabs( std::move(newUndoTabs) ),
+ bShow( bNewShow )
+{
+}
+
+ScUndoShowHideTab::~ScUndoShowHideTab()
+{
+}
+
+void ScUndoShowHideTab::DoChange( bool bShowP ) const
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+
+ for(const SCTAB& nTab : undoTabs)
+ {
+ rDoc.SetVisible( nTab, bShowP );
+ if (pViewShell)
+ pViewShell->SetTabNo(nTab,true);
+ }
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pDocShell->SetDocumentModified();
+}
+
+void ScUndoShowHideTab::Undo()
+{
+ DoChange(!bShow);
+}
+
+void ScUndoShowHideTab::Redo()
+{
+ DoChange(bShow);
+}
+
+void ScUndoShowHideTab::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->GetViewData().GetDispatcher().
+ Execute( bShow ? FID_TABLE_SHOW : FID_TABLE_HIDE,
+ SfxCallMode::SLOT | SfxCallMode::RECORD);
+}
+
+bool ScUndoShowHideTab::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+OUString ScUndoShowHideTab::GetComment() const
+{
+ TranslateId pId;
+ if (undoTabs.size() > 1)
+ {
+ pId = bShow ? STR_UNDO_SHOWTABS : STR_UNDO_HIDETABS;
+ }
+ else
+ {
+ pId = bShow ? STR_UNDO_SHOWTAB : STR_UNDO_HIDETAB;
+ }
+
+ return ScResId(pId);
+}
+
+ScUndoDocProtect::ScUndoDocProtect(ScDocShell* pShell, unique_ptr<ScDocProtection> && pProtectSettings) :
+ ScSimpleUndo(pShell),
+ mpProtectSettings(std::move(pProtectSettings))
+{
+}
+
+ScUndoDocProtect::~ScUndoDocProtect()
+{
+}
+
+void ScUndoDocProtect::DoProtect(bool bProtect)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ if (bProtect)
+ {
+ // set protection.
+ unique_ptr<ScDocProtection> pCopy(new ScDocProtection(*mpProtectSettings));
+ pCopy->setProtected(true);
+ rDoc.SetDocProtection(pCopy.get());
+ }
+ else
+ {
+ // remove protection.
+ rDoc.SetDocProtection(nullptr);
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ pViewShell->UpdateLayerLocks();
+ pViewShell->UpdateInputHandler(true); // so that input can be immediately entered again
+ }
+
+ pDocShell->PostPaintGridAll();
+}
+
+void ScUndoDocProtect::Undo()
+{
+ BeginUndo();
+ DoProtect(!mpProtectSettings->isProtected());
+ EndUndo();
+}
+
+void ScUndoDocProtect::Redo()
+{
+ BeginRedo();
+ DoProtect(mpProtectSettings->isProtected());
+ EndRedo();
+}
+
+void ScUndoDocProtect::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoDocProtect::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // makes no sense
+}
+
+OUString ScUndoDocProtect::GetComment() const
+{
+ TranslateId pId = mpProtectSettings->isProtected() ? STR_UNDO_PROTECT_DOC : STR_UNDO_UNPROTECT_DOC;
+ return ScResId(pId);
+}
+
+ScUndoTabProtect::ScUndoTabProtect(ScDocShell* pShell, SCTAB nTab, unique_ptr<ScTableProtection> && pProtectSettings) :
+ ScSimpleUndo(pShell),
+ mnTab(nTab),
+ mpProtectSettings(std::move(pProtectSettings))
+{
+}
+
+ScUndoTabProtect::~ScUndoTabProtect()
+{
+}
+
+void ScUndoTabProtect::DoProtect(bool bProtect)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ if (bProtect)
+ {
+ // set protection.
+ unique_ptr<ScTableProtection> pCopy(new ScTableProtection(*mpProtectSettings));
+ pCopy->setProtected(true);
+ rDoc.SetTabProtection(mnTab, pCopy.get());
+ }
+ else
+ {
+ // remove protection.
+ rDoc.SetTabProtection(mnTab, nullptr);
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ {
+ if (ScTabView* pTabView = pViewShell->GetViewData().GetView())
+ pTabView->SetTabProtectionSymbol( mnTab, bProtect);
+ pViewShell->UpdateLayerLocks();
+ pViewShell->UpdateInputHandler(true); // so that input can be immediately entered again
+ }
+
+ pDocShell->PostPaintGridAll();
+}
+
+void ScUndoTabProtect::Undo()
+{
+ BeginUndo();
+ DoProtect(!mpProtectSettings->isProtected());
+ EndUndo();
+}
+
+void ScUndoTabProtect::Redo()
+{
+ BeginRedo();
+ DoProtect(mpProtectSettings->isProtected());
+ EndRedo();
+}
+
+void ScUndoTabProtect::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoTabProtect::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // makes no sense
+}
+
+OUString ScUndoTabProtect::GetComment() const
+{
+ TranslateId pId = mpProtectSettings->isProtected() ? STR_UNDO_PROTECT_TAB : STR_UNDO_UNPROTECT_TAB;
+ return ScResId(pId);
+}
+
+ScUndoPrintRange::ScUndoPrintRange( ScDocShell* pShell, SCTAB nNewTab,
+ std::unique_ptr<ScPrintRangeSaver> pOld, std::unique_ptr<ScPrintRangeSaver> pNew ) :
+ ScSimpleUndo( pShell ),
+ nTab( nNewTab ),
+ pOldRanges( std::move(pOld) ),
+ pNewRanges( std::move(pNew) )
+{
+}
+
+ScUndoPrintRange::~ScUndoPrintRange()
+{
+ pOldRanges.reset();
+ pNewRanges.reset();
+}
+
+void ScUndoPrintRange::DoChange(bool bUndo)
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (bUndo)
+ rDoc.RestorePrintRanges( *pOldRanges );
+ else
+ rDoc.RestorePrintRanges( *pNewRanges );
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo( nTab );
+
+ ScPrintFunc( pDocShell, pDocShell->GetPrinter(), nTab ).UpdatePages();
+
+ if (pViewShell && comphelper::LibreOfficeKit::isActive())
+ {
+ tools::JsonWriter aJsonWriter;
+ if (bUndo)
+ pOldRanges->GetPrintRangesInfo(aJsonWriter);
+ else
+ pNewRanges->GetPrintRangesInfo(aJsonWriter);
+
+ const OString message = aJsonWriter.finishAndGetAsOString();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_PRINT_RANGES, message);
+ }
+
+ pDocShell->PostPaint( ScRange(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab), PaintPartFlags::Grid );
+}
+
+void ScUndoPrintRange::Undo()
+{
+ BeginUndo();
+ DoChange( true );
+ EndUndo();
+}
+
+void ScUndoPrintRange::Redo()
+{
+ BeginRedo();
+ DoChange( false );
+ EndRedo();
+}
+
+void ScUndoPrintRange::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // makes no sense
+}
+
+bool ScUndoPrintRange::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false; // makes no sense
+}
+
+OUString ScUndoPrintRange::GetComment() const
+{
+ return ScResId( STR_UNDO_PRINTRANGES );
+}
+
+ScUndoScenarioFlags::ScUndoScenarioFlags(ScDocShell* pNewDocShell, SCTAB nT,
+ OUString aON, OUString aNN, OUString aOC, OUString aNC,
+ const Color& rOCol, const Color& rNCol, ScScenarioFlags nOF, ScScenarioFlags nNF) :
+ ScSimpleUndo( pNewDocShell ),
+ nTab ( nT ),
+ aOldName (std::move( aON )),
+ aNewName (std::move( aNN )),
+ aOldComment (std::move( aOC )),
+ aNewComment (std::move( aNC )),
+ aOldColor ( rOCol ),
+ aNewColor ( rNCol ),
+ nOldFlags (nOF),
+ nNewFlags (nNF)
+{
+}
+
+ScUndoScenarioFlags::~ScUndoScenarioFlags()
+{
+}
+
+OUString ScUndoScenarioFlags::GetComment() const
+{
+ return ScResId( STR_UNDO_EDITSCENARIO );
+}
+
+void ScUndoScenarioFlags::Undo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.RenameTab( nTab, aOldName );
+ rDoc.SetScenarioData( nTab, aOldComment, aOldColor, nOldFlags );
+
+ pDocShell->PostPaintGridAll();
+ // The sheet name might be used in a formula ...
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->UpdateInputHandler();
+
+ if ( aOldName != aNewName )
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+}
+
+void ScUndoScenarioFlags::Redo()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.RenameTab( nTab, aNewName );
+ rDoc.SetScenarioData( nTab, aNewComment, aNewColor, nNewFlags );
+
+ pDocShell->PostPaintGridAll();
+ // The sheet name might be used in a formula ...
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->UpdateInputHandler();
+
+ if ( aOldName != aNewName )
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+}
+
+void ScUndoScenarioFlags::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+ // Repeat makes no sense
+}
+
+bool ScUndoScenarioFlags::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+// (move to different file?)
+ScUndoRenameObject::ScUndoRenameObject( ScDocShell* pNewDocShell, OUString aPN,
+ OUString aON, OUString aNN ) :
+ ScSimpleUndo( pNewDocShell ),
+ aPersistName(std::move( aPN )),
+ aOldName (std::move( aON )),
+ aNewName (std::move( aNN ))
+{
+}
+
+ScUndoRenameObject::~ScUndoRenameObject()
+{
+}
+
+OUString ScUndoRenameObject::GetComment() const
+{
+ // string resource shared with title for dialog
+ return ScResId(SCSTR_RENAMEOBJECT);
+}
+
+SdrObject* ScUndoRenameObject::GetObject()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if ( pDrawLayer )
+ {
+ sal_uInt16 nCount = pDrawLayer->GetPageCount();
+ for (sal_uInt16 nTab=0; nTab<nCount; nTab++)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(nTab);
+ assert(pPage && "Page ?");
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 &&
+ static_cast<SdrOle2Obj*>(pObject)->GetPersistName() == aPersistName )
+ {
+ return pObject;
+ }
+
+ pObject = aIter.Next();
+ }
+ }
+ }
+ OSL_FAIL("Object not found");
+ return nullptr;
+}
+
+void ScUndoRenameObject::Undo()
+{
+ BeginUndo();
+ SdrObject* pObj = GetObject();
+ if ( pObj )
+ pObj->SetName( aOldName );
+ EndUndo();
+}
+
+void ScUndoRenameObject::Redo()
+{
+ BeginRedo();
+ SdrObject* pObj = GetObject();
+ if ( pObj )
+ pObj->SetName( aNewName );
+ EndRedo();
+}
+
+void ScUndoRenameObject::Repeat(SfxRepeatTarget& /* rTarget */)
+{
+}
+
+bool ScUndoRenameObject::CanRepeat(SfxRepeatTarget& /* rTarget */) const
+{
+ return false;
+}
+
+ScUndoLayoutRTL::ScUndoLayoutRTL( ScDocShell* pShell, SCTAB nNewTab, bool bNewRTL ) :
+ ScSimpleUndo( pShell ),
+ nTab( nNewTab ),
+ bRTL( bNewRTL )
+{
+}
+
+ScUndoLayoutRTL::~ScUndoLayoutRTL()
+{
+}
+
+void ScUndoLayoutRTL::DoChange( bool bNew )
+{
+ pDocShell->SetInUndo( true );
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.SetLayoutRTL(nTab, bNew, ScObjectHandling::MirrorRTLMode);
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->SetTabNo(nTab,true);
+
+ pDocShell->SetDocumentModified();
+
+ pDocShell->SetInUndo( false );
+}
+
+void ScUndoLayoutRTL::Undo()
+{
+ DoChange(!bRTL);
+}
+
+void ScUndoLayoutRTL::Redo()
+{
+ DoChange(bRTL);
+}
+
+void ScUndoLayoutRTL::Repeat(SfxRepeatTarget& rTarget)
+{
+ if (auto pViewTarget = dynamic_cast<ScTabViewTarget*>( &rTarget))
+ pViewTarget->GetViewShell()->GetViewData().GetDispatcher().
+ Execute( FID_TAB_RTL, SfxCallMode::SLOT | SfxCallMode::RECORD);
+}
+
+bool ScUndoLayoutRTL::CanRepeat(SfxRepeatTarget& rTarget) const
+{
+ return dynamic_cast<const ScTabViewTarget*>( &rTarget) != nullptr;
+}
+
+OUString ScUndoLayoutRTL::GetComment() const
+{
+ return ScResId( STR_UNDO_TAB_RTL );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/undo/undoutil.cxx b/sc/source/ui/undo/undoutil.cxx
new file mode 100644
index 0000000000..de9a50ef82
--- /dev/null
+++ b/sc/source/ui/undo/undoutil.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 <undoutil.hxx>
+
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <globalnames.hxx>
+#include <global.hxx>
+#include <markdata.hxx>
+#include <osl/diagnose.h>
+
+void ScUndoUtil::MarkSimpleBlock( const ScDocShell* pDocShell,
+ SCCOL nStartX, SCROW nStartY, SCTAB nStartZ,
+ SCCOL nEndX, SCROW nEndY, SCTAB nEndZ )
+{
+ if ( pDocShell->IsPaintLocked() )
+ return;
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (!pViewShell)
+ return;
+
+ SCTAB nViewTab = pViewShell->GetViewData().GetTabNo();
+ if ( nViewTab < nStartZ || nViewTab > nEndZ )
+ pViewShell->SetTabNo( nStartZ );
+
+ const ScRange aMarkRange( nStartX, nStartY, nStartZ, nEndX, nEndY, nEndZ);
+ pViewShell->DoneBlockMode();
+ pViewShell->MoveCursorAbs( nStartX, nStartY, SC_FOLLOW_JUMP, false, false );
+ pViewShell->InitOwnBlockMode( aMarkRange );
+ pViewShell->GetViewData().GetMarkData().SetMarkArea( aMarkRange );
+ pViewShell->MarkDataChanged();
+}
+
+void ScUndoUtil::MarkSimpleBlock( const ScDocShell* pDocShell,
+ const ScAddress& rBlockStart,
+ const ScAddress& rBlockEnd )
+{
+ MarkSimpleBlock( pDocShell, rBlockStart.Col(), rBlockStart.Row(), rBlockStart.Tab(),
+ rBlockEnd.Col(), rBlockEnd.Row(), rBlockEnd.Tab() );
+}
+
+void ScUndoUtil::MarkSimpleBlock( const ScDocShell* pDocShell,
+ const ScRange& rRange )
+{
+ MarkSimpleBlock( pDocShell, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab() );
+}
+
+ScDBData* ScUndoUtil::GetOldDBData( const ScDBData* pUndoData, ScDocument* pDoc, SCTAB nTab,
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ ScDBData* pRet = pDoc->GetDBAtArea( nTab, nCol1, nRow1, nCol2, nRow2 );
+
+ if (!pRet)
+ {
+ bool bWasTemp = false;
+ if ( pUndoData )
+ {
+ const OUString& aName = pUndoData->GetName();
+ if ( aName == STR_DB_LOCAL_NONAME )
+ bWasTemp = true;
+ }
+ OSL_ENSURE(bWasTemp, "Undo: didn't find database range");
+ pRet = pDoc->GetAnonymousDBData(nTab);
+ if (!pRet)
+ {
+ pRet = new ScDBData( STR_DB_LOCAL_NONAME, nTab,
+ nCol1,nRow1, nCol2,nRow2, true,
+ pDoc->HasColHeader( nCol1,nRow1,nCol2,nRow2,nTab ) );
+ pDoc->SetAnonymousDBData(nTab, std::unique_ptr<ScDBData>(pRet));
+ }
+ }
+
+ return pRet;
+}
+
+void ScUndoUtil::PaintMore( ScDocShell* pDocShell,
+ const ScRange& rRange )
+{
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ SCROW nRow2 = rRange.aEnd.Row();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (nCol1 > 0) --nCol1;
+ if (nRow1 > 0) --nRow1;
+ if (nCol2<rDoc.MaxCol()) ++nCol2;
+ if (nRow2<rDoc.MaxRow()) ++nRow2;
+
+ pDocShell->PostPaint( nCol1,nRow1,rRange.aStart.Tab(),
+ nCol2,nRow2,rRange.aEnd.Tab(), PaintPartFlags::Grid );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/ChartRangeSelectionListener.cxx b/sc/source/ui/unoobj/ChartRangeSelectionListener.cxx
new file mode 100644
index 0000000000..eae58aee6f
--- /dev/null
+++ b/sc/source/ui/unoobj/ChartRangeSelectionListener.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 <ChartRangeSelectionListener.hxx>
+
+#include <com/sun/star/chart2/data/XRangeHighlighter.hpp>
+
+#include <tabvwsh.hxx>
+#include <unonames.hxx>
+#include <miscuno.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+
+SC_SIMPLE_SERVICE_INFO( ScChartRangeSelectionListener, "ScChartRangeSelectionListener",
+ SC_SERVICENAME_CHRANGEHILIGHT )
+
+ScChartRangeSelectionListener::ScChartRangeSelectionListener( ScTabViewShell * pViewShell ) :
+ m_pViewShell( pViewShell )
+{}
+
+ScChartRangeSelectionListener::~ScChartRangeSelectionListener()
+{}
+
+// ____ XModifyListener ____
+void SAL_CALL ScChartRangeSelectionListener::selectionChanged( const lang::EventObject& aEvent )
+{
+ Reference< chart2::data::XRangeHighlighter > xRangeHighlighter( aEvent.Source, uno::UNO_QUERY );
+ if( xRangeHighlighter.is())
+ {
+ Sequence< chart2::data::HighlightedRange > aRanges( xRangeHighlighter->getSelectedRanges());
+
+ // search the view on which the chart is active
+
+ if( m_pViewShell )
+ {
+ m_pViewShell->DoChartSelection( aRanges );
+ }
+ }
+}
+
+// ____ XEventListener ____
+void SAL_CALL ScChartRangeSelectionListener::disposing( const lang::EventObject& /*Source*/ )
+{
+}
+
+// ____ WeakComponentImplHelperBase ____
+void ScChartRangeSelectionListener::disposing(std::unique_lock<std::mutex>&)
+{
+ m_pViewShell = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/ChartTools.cxx b/sc/source/ui/unoobj/ChartTools.cxx
new file mode 100644
index 0000000000..e9fdc996a3
--- /dev/null
+++ b/sc/source/ui/unoobj/ChartTools.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/.
+ *
+ */
+
+#include <ChartTools.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+
+#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+
+using namespace css;
+
+namespace sc::tools {
+
+namespace {
+
+uno::Reference<chart2::data::XPivotTableDataProvider>
+getPivotTableDataProvider(const SdrOle2Obj* pOleObject)
+{
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider;
+
+ const uno::Reference<embed::XEmbeddedObject>& xObject = pOleObject->GetObjRef();
+ if (xObject.is())
+ {
+ uno::Reference<chart2::XChartDocument> xChartDoc(xObject->getComponent(), uno::UNO_QUERY);
+ if (xChartDoc.is())
+ {
+ xPivotTableDataProvider.set(uno::Reference<chart2::data::XPivotTableDataProvider>(
+ xChartDoc->getDataProvider(), uno::UNO_QUERY));
+ }
+ }
+ return xPivotTableDataProvider;
+}
+
+OUString getAssociatedPivotTableName(const SdrOle2Obj* pOleObject)
+{
+ OUString aPivotTableName;
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider;
+ xPivotTableDataProvider.set(getPivotTableDataProvider(pOleObject));
+ if (xPivotTableDataProvider.is())
+ aPivotTableName = xPivotTableDataProvider->getPivotTableName();
+ return aPivotTableName;
+}
+
+} // end anonymous namespace
+
+ChartIterator::ChartIterator(ScDocShell* pDocShell, SCTAB nTab, ChartSourceType eChartSourceType)
+ : m_eChartSourceType(eChartSourceType)
+{
+ if (!pDocShell)
+ return;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (!pDrawLayer)
+ return;
+ SdrPage* pPage = pDrawLayer->GetPage(sal_uInt16(nTab));
+ if (!pPage)
+ return;
+ m_oIterator.emplace(pPage, SdrIterMode::DeepNoGroups);
+}
+
+SdrOle2Obj* ChartIterator::next()
+{
+ if (!m_oIterator)
+ return nullptr;
+
+ SdrObject* pObject = m_oIterator->Next();
+ while (pObject)
+ {
+ if (pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject))
+ {
+ SdrOle2Obj* pOleObject = static_cast<SdrOle2Obj*>(pObject);
+
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider;
+ xPivotTableDataProvider.set(getPivotTableDataProvider(pOleObject));
+
+ if (xPivotTableDataProvider.is() && m_eChartSourceType == ChartSourceType::PIVOT_TABLE)
+ return pOleObject;
+ else if (!xPivotTableDataProvider.is() && m_eChartSourceType == ChartSourceType::CELL_RANGE)
+ return pOleObject;
+ }
+ pObject = m_oIterator->Next();
+ }
+ return nullptr;
+}
+
+SdrOle2Obj* findChartsByName(ScDocShell* pDocShell, SCTAB nTab, std::u16string_view rName, ChartSourceType eChartSourceType)
+{
+ if (!pDocShell)
+ return nullptr;
+
+ ChartIterator aIterator(pDocShell, nTab, eChartSourceType);
+
+ SdrOle2Obj* pObject = aIterator.next();
+ while (pObject)
+ {
+ uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef();
+ if (xObject.is())
+ {
+ OUString aObjectName = pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject);
+ if (aObjectName == rName)
+ return pObject;
+ }
+ pObject = aIterator.next();
+ }
+ return nullptr;
+}
+
+SdrOle2Obj* getChartByIndex(ScDocShell* pDocShell, SCTAB nTab, ::tools::Long nIndex, ChartSourceType eChartSourceType)
+{
+ if (!pDocShell)
+ return nullptr;
+
+ ChartIterator aIterator(pDocShell, nTab, eChartSourceType);
+
+ SdrOle2Obj* pObject = aIterator.next();
+ ::tools::Long i = 0;
+ while (pObject)
+ {
+ if (i == nIndex)
+ {
+ return pObject;
+ }
+
+ i++;
+ pObject = aIterator.next();
+ }
+ return nullptr;
+}
+
+std::vector<SdrOle2Obj*> getAllPivotChartsConnectedTo(std::u16string_view sPivotTableName, ScDocShell* pDocShell)
+{
+ std::vector<SdrOle2Obj*> aObjects;
+
+ ScDocument& rDocument = pDocShell->GetDocument();
+ ScDrawLayer* pModel = rDocument.GetDrawLayer();
+ if (!pModel)
+ return aObjects;
+
+ sal_uInt16 nPageCount = pModel->GetPageCount();
+ for (sal_uInt16 nPageNo = 0; nPageNo < nPageCount; nPageNo++)
+ {
+ SdrPage* pPage = pModel->GetPage(nPageNo);
+ if (!pPage)
+ continue;
+
+ sc::tools::ChartIterator aIterator(pDocShell, nPageNo, ChartSourceType::PIVOT_TABLE);
+ SdrOle2Obj* pObject = aIterator.next();
+ while (pObject)
+ {
+ if (sPivotTableName == getAssociatedPivotTableName(pObject))
+ {
+ aObjects.push_back(pObject);
+ }
+ pObject = aIterator.next();
+ }
+ }
+ return aObjects;
+}
+
+} // end sc::tools
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/PivotTableDataProvider.cxx b/sc/source/ui/unoobj/PivotTableDataProvider.cxx
new file mode 100644
index 0000000000..e3c800128b
--- /dev/null
+++ b/sc/source/ui/unoobj/PivotTableDataProvider.cxx
@@ -0,0 +1,906 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <limits>
+#include <memory>
+#include <sal/config.h>
+
+#include <PivotTableDataProvider.hxx>
+#include <PivotTableDataSource.hxx>
+#include <PivotTableDataSequence.hxx>
+
+#include <miscuno.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <unonames.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <dpobject.hxx>
+#include <hints.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/objsh.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <com/sun/star/sheet/XDataPilotResults.hpp>
+#include <com/sun/star/sheet/DataResultFlags.hpp>
+
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
+#include <com/sun/star/sheet/XLevelsSupplier.hpp>
+#include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/XMembersSupplier.hpp>
+
+#include <com/sun/star/chart/ChartDataChangeEvent.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+
+#include <unordered_map>
+
+using namespace css;
+
+namespace sc
+{
+namespace
+{
+constexpr OUStringLiteral constIdCategories(u"categories");
+constexpr OUStringLiteral constIdLabel(u"label");
+constexpr OUStringLiteral constIdData(u"data");
+
+std::span<const SfxItemPropertyMapEntry> lcl_GetDataProviderPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_USE_INTERNAL_DATA_PROVIDER, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aDataProviderPropertyMap_Impl;
+}
+
+uno::Reference<frame::XModel> lcl_GetXModel(const ScDocument * pDoc)
+{
+ uno::Reference<frame::XModel> xModel;
+ ScDocShell* pObjSh(pDoc ? pDoc->GetDocumentShell() : nullptr);
+ if (pObjSh)
+ xModel.set(pObjSh->GetModel());
+ return xModel;
+}
+
+OUString lcl_identifierForData(sal_Int32 index)
+{
+ return "PT@" + constIdData + " " + OUString::number(index);
+}
+
+OUString lcl_identifierForLabel(sal_Int32 index)
+{
+ return "PT@" + constIdLabel + " " + OUString::number(index);
+}
+
+OUString lcl_identifierForCategories()
+{
+ return "PT@" + constIdCategories;
+}
+
+std::vector<OUString> lcl_getVisiblePageMembers(const uno::Reference<uno::XInterface> & xLevel)
+{
+ std::vector<OUString> aResult;
+ if (!xLevel.is())
+ return aResult;
+
+ uno::Reference<sheet::XMembersSupplier> xMembersSupplier(xLevel, uno::UNO_QUERY);
+ if (!xMembersSupplier.is())
+ return aResult;
+
+ uno::Reference<sheet::XMembersAccess> xMembersAccess = xMembersSupplier->getMembers();
+ if (!xMembersAccess.is())
+ return aResult;
+
+ const css::uno::Sequence<OUString> aMembersNames = xMembersAccess->getElementNames();
+ for (OUString const & rMemberNames : aMembersNames)
+ {
+ uno::Reference<beans::XPropertySet> xProperties(xMembersAccess->getByName(rMemberNames), uno::UNO_QUERY);
+ if (!xProperties.is())
+ continue;
+
+ OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xProperties, SC_UNO_DP_LAYOUTNAME, OUString());
+ if (aCaption.isEmpty())
+ aCaption = rMemberNames;
+
+ bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xProperties, SC_UNO_DP_ISVISIBLE);
+
+ if (bVisible)
+ aResult.push_back(aCaption);
+ }
+
+ return aResult;
+}
+
+} // end anonymous namespace
+
+SC_SIMPLE_SERVICE_INFO(PivotTableDataProvider, "PivotTableDataProvider", SC_SERVICENAME_CHART_PIVOTTABLE_DATAPROVIDER)
+
+// DataProvider ==============================================================
+
+PivotTableDataProvider::PivotTableDataProvider(ScDocument& rDoc)
+ : m_pDocument(&rDoc)
+ , m_aPropSet(lcl_GetDataProviderPropertyMap())
+ , m_bIncludeHiddenCells(true)
+ , m_bNeedsUpdate(true)
+ , m_xContext(comphelper::getProcessComponentContext())
+{
+ rDoc.AddUnoObject(*this);
+}
+
+PivotTableDataProvider::~PivotTableDataProvider()
+{
+ SolarMutexGuard g;
+
+ if (m_pDocument)
+ m_pDocument->RemoveUnoObject( *this);
+}
+
+void PivotTableDataProvider::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pDocument = nullptr;
+ }
+ else if (m_pDocument)
+ {
+ if (auto pDataPilotHint = dynamic_cast<const ScDataPilotModifiedHint*>(&rHint))
+ {
+ if (pDataPilotHint->GetName() == m_sPivotTableName)
+ {
+ m_bNeedsUpdate = true;
+ for (uno::Reference<util::XModifyListener> const & xListener : m_aValueListeners)
+ {
+ css::chart::ChartDataChangeEvent aEvent(getXWeak(),
+ css::chart::ChartDataChangeType_ALL,
+ 0, 0, 0, 0);
+ xListener->modified(aEvent);
+ }
+ }
+ }
+ }
+}
+
+sal_Bool SAL_CALL PivotTableDataProvider::createDataSourcePossible(const uno::Sequence<beans::PropertyValue>& /*aArguments*/)
+{
+ SolarMutexGuard aGuard;
+ if (!m_pDocument)
+ return false;
+
+ if (m_sPivotTableName.isEmpty())
+ return false;
+
+ ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
+ return bool(pDPCollection->GetByName(m_sPivotTableName));
+}
+
+uno::Reference<chart2::data::XDataSource> SAL_CALL
+ PivotTableDataProvider::createDataSource(const uno::Sequence<beans::PropertyValue>& aArguments)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pDocument)
+ throw uno::RuntimeException();
+
+ bool bOrientCol = true;
+ OUString aRangeRepresentation;
+
+ for (beans::PropertyValue const & rProperty : aArguments)
+ {
+ if (rProperty.Name == "DataRowSource")
+ {
+ chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
+ if (!(rProperty.Value >>= eSource))
+ {
+ sal_Int32 nSource(0);
+ if (rProperty.Value >>= nSource)
+ eSource = chart::ChartDataRowSource(nSource);
+ }
+ bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
+ }
+ else if (rProperty.Name == "CellRangeRepresentation")
+ rProperty.Value >>= aRangeRepresentation;
+ }
+
+ uno::Reference<chart2::data::XDataSource> xResult;
+
+ if (aRangeRepresentation == lcl_identifierForCategories())
+ xResult = createCategoriesDataSource(bOrientCol);
+ else
+ xResult = createValuesDataSource();
+
+ return xResult;
+}
+
+uno::Reference<chart2::data::XLabeledDataSequence>
+ PivotTableDataProvider::newLabeledDataSequence()
+{
+ uno::Reference<chart2::data::XLabeledDataSequence> xResult;
+ if (!m_xContext.is())
+ return xResult;
+ xResult.set(chart2::data::LabeledDataSequence::create(m_xContext), uno::UNO_QUERY_THROW);
+ return xResult;
+}
+
+uno::Reference<chart2::data::XDataSource>
+PivotTableDataProvider::createCategoriesDataSource(bool bOrientationIsColumn)
+{
+ if (m_bNeedsUpdate)
+ collectPivotTableData();
+
+ uno::Reference<chart2::data::XDataSource> xDataSource;
+ std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences;
+
+ std::vector<std::vector<ValueAndFormat>> const & rCategoriesVector = bOrientationIsColumn ? m_aCategoriesColumnOrientation
+ : m_aCategoriesRowOrientation;
+
+ for (std::vector<ValueAndFormat> const & rCategories : rCategoriesVector)
+ {
+ uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
+ rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument,
+ lcl_identifierForCategories(), std::vector(rCategories)));
+ pSequence->setRole("categories");
+ xResult->setValues(uno::Reference<chart2::data::XDataSequence>(pSequence));
+
+ aLabeledSequences.push_back(xResult);
+ }
+
+ xDataSource.set(new PivotTableDataSource(std::move(aLabeledSequences)));
+ return xDataSource;
+}
+
+void PivotTableDataProvider::collectPivotTableData()
+{
+ ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
+ ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName);
+ if (!pDPObject)
+ return;
+
+ m_aCategoriesColumnOrientation.clear();
+ m_aCategoriesRowOrientation.clear();
+ m_aLabels.clear();
+ m_aDataRowVector.clear();
+ m_aColumnFields.clear();
+ m_aRowFields.clear();
+ m_aPageFields.clear();
+ m_aDataFields.clear();
+ m_aFieldOutputDescriptionMap.clear();
+
+ uno::Reference<sheet::XDataPilotResults> xDPResults(pDPObject->GetSource(), uno::UNO_QUERY);
+ if (!xDPResults.is())
+ return;
+ const uno::Sequence<uno::Sequence<sheet::DataResult>> xDataResultsSequence = xDPResults->getResults();
+
+ std::unordered_set<size_t> aValidRowIndex;
+
+ size_t nRowIndex = 0;
+ for (uno::Sequence<sheet::DataResult> const & xDataResults : xDataResultsSequence)
+ {
+ std::vector<ValueAndFormat> aRow;
+ bool bRowEmpty = true;
+ // First pass - collect a row of valid data and track if the row is empty
+ for (sheet::DataResult const & rDataResult : xDataResults)
+ {
+ if (rDataResult.Flags & css::sheet::DataResultFlags::SUBTOTAL)
+ continue;
+ if (rDataResult.Flags == 0 || rDataResult.Flags & css::sheet::DataResultFlags::HASDATA)
+ {
+ aRow.emplace_back(rDataResult.Flags ? rDataResult.Value
+ : std::numeric_limits<double>::quiet_NaN(), 0);
+ if (rDataResult.Flags != 0) // set as valid only if we have data
+ {
+ bRowEmpty = false;
+ // We need to remember all valid (non-empty) row indices
+ aValidRowIndex.insert(nRowIndex);
+ }
+ }
+ }
+ // Second pass: add to collection only non-empty rows
+ if (!bRowEmpty)
+ {
+ size_t nColumnIndex = 0;
+ for (ValueAndFormat const & aValue : aRow)
+ {
+ if (nColumnIndex >= m_aDataRowVector.size())
+ m_aDataRowVector.resize(nColumnIndex + 1);
+ m_aDataRowVector[nColumnIndex].push_back(aValue);
+ nColumnIndex++;
+ }
+ }
+ nRowIndex++;
+ }
+
+ uno::Reference<sheet::XDimensionsSupplier> xDimensionsSupplier(pDPObject->GetSource());
+ uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess(xDimensionsSupplier->getDimensions());
+
+ std::unordered_map<OUString, sal_Int32> aDataFieldNumberFormatMap;
+ std::vector<OUString> aDataFieldNamesVectors;
+
+ std::unordered_map<OUString, OUString> aDataFieldCaptionNames;
+
+ sheet::DataPilotFieldOrientation eDataFieldOrientation = sheet::DataPilotFieldOrientation_HIDDEN;
+
+ for (sal_Int32 nDim = 0; nDim < xDims->getCount(); nDim++)
+ {
+ uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xDimProp(xDim, uno::UNO_QUERY);
+ uno::Reference<sheet::XHierarchiesSupplier> xDimSupp(xDim, uno::UNO_QUERY);
+
+ if (!xDimProp.is() || !xDimSupp.is())
+ continue;
+
+ sheet::DataPilotFieldOrientation eDimOrient =
+ ScUnoHelpFunctions::GetEnumProperty(xDimProp, SC_UNO_DP_ORIENTATION,
+ sheet::DataPilotFieldOrientation_HIDDEN);
+
+ if (eDimOrient == sheet::DataPilotFieldOrientation_HIDDEN)
+ continue;
+
+ uno::Reference<container::XIndexAccess> xHierarchies = new ScNameToIndexAccess(xDimSupp->getHierarchies());
+ sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_USEDHIERARCHY);
+ if (nHierarchy >= xHierarchies->getCount())
+ nHierarchy = 0;
+
+ uno::Reference<sheet::XLevelsSupplier> xLevelsSupplier(xHierarchies->getByIndex(nHierarchy),
+ uno::UNO_QUERY);
+
+ if (!xLevelsSupplier.is())
+ continue;
+
+ uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess(xLevelsSupplier->getLevels());
+
+ for (tools::Long nLevel = 0; nLevel < xLevels->getCount(); nLevel++)
+ {
+ uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLevel), uno::UNO_QUERY);
+ uno::Reference<container::XNamed> xLevelName(xLevel, uno::UNO_QUERY);
+ uno::Reference<sheet::XDataPilotMemberResults> xLevelResult(xLevel, uno::UNO_QUERY );
+
+ if (xLevelName.is() && xLevelResult.is())
+ {
+ bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_ISDATALAYOUT);
+ sal_Int32 nDimPos = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_POSITION);
+ sal_Int32 nNumberFormat = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_NUMBERFO);
+ bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
+
+ switch (eDimOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ {
+ m_aColumnFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
+
+ const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults();
+ size_t i = 0;
+ OUString sCaption;
+ OUString sName;
+ for (sheet::MemberResult const & rMember : aSequence)
+ {
+ // Skip grandtotals and subtotals
+ if (rMember.Flags & sheet::MemberResultFlags::SUBTOTAL ||
+ rMember.Flags & sheet::MemberResultFlags::GRANDTOTAL)
+ continue;
+ if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER ||
+ rMember.Flags & sheet::MemberResultFlags::CONTINUE)
+ {
+ if (!(rMember.Flags & sheet::MemberResultFlags::CONTINUE))
+ {
+ sCaption = rMember.Caption;
+ sName = rMember.Name;
+ }
+
+ if (i >= m_aLabels.size())
+ m_aLabels.resize(i + 1);
+
+ if (o3tl::make_unsigned(nDimPos) >= m_aLabels[i].size())
+ m_aLabels[i].resize(nDimPos + 1);
+ m_aLabels[i][nDimPos] = ValueAndFormat(sCaption);
+
+ if (bIsDataLayout)
+ {
+ // Remember data fields to determine the number format of data
+ aDataFieldNamesVectors.push_back(sName);
+ eDataFieldOrientation = sheet::DataPilotFieldOrientation_COLUMN;
+ // Remember the caption name
+ aDataFieldCaptionNames[rMember.Name] = rMember.Caption;
+ }
+ i++;
+ }
+ }
+ }
+ break;
+
+ case sheet::DataPilotFieldOrientation_ROW:
+ {
+ m_aRowFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
+
+ const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults();
+
+ size_t i = 0;
+ size_t nEachIndex = 0;
+ std::unique_ptr<ValueAndFormat> pItem;
+
+ for (sheet::MemberResult const & rMember : aSequence)
+ {
+ bool bFound = aValidRowIndex.find(nEachIndex) != aValidRowIndex.end();
+
+ nEachIndex++;
+
+ bool bHasContinueFlag = rMember.Flags & sheet::MemberResultFlags::CONTINUE;
+
+ if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER || bHasContinueFlag)
+ {
+ if (!bHasContinueFlag)
+ {
+ // Chart2 does not use number format for labels, so use the display string.
+ pItem.reset(new ValueAndFormat(rMember.Caption));
+ }
+
+ if (bFound)
+ {
+ assert(pItem && "bHasContinueFlag must be false on this or some preceding element");
+
+ if (i >= m_aCategoriesRowOrientation.size())
+ m_aCategoriesRowOrientation.resize(i + 1);
+
+ if (o3tl::make_unsigned(nDimPos) >= m_aCategoriesColumnOrientation.size())
+ m_aCategoriesColumnOrientation.resize(nDimPos + 1);
+ m_aCategoriesColumnOrientation[nDimPos].push_back(*pItem);
+
+ if (o3tl::make_unsigned(nDimPos) >= m_aCategoriesRowOrientation[i].size())
+ m_aCategoriesRowOrientation[i].resize(nDimPos + 1);
+ m_aCategoriesRowOrientation[i][nDimPos] = *pItem;
+
+ if (bIsDataLayout)
+ {
+ // Remember data fields to determine the number format of data
+ aDataFieldNamesVectors.push_back(rMember.Name);
+ eDataFieldOrientation = sheet::DataPilotFieldOrientation_ROW;
+
+ // Remember the caption name
+ aDataFieldCaptionNames[rMember.Name] = rMember.Caption;
+ }
+
+ // Set to empty so the sub categories are set to empty when they continue
+ pItem.reset(new ValueAndFormat);
+ i++;
+ }
+ }
+ }
+ }
+ break;
+
+ case sheet::DataPilotFieldOrientation_PAGE:
+ {
+ m_aPageFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
+
+ // Resolve filtering
+ OUString aFieldOutputDescription;
+ if (bHasHiddenMember)
+ {
+ std::vector<OUString> aMembers = lcl_getVisiblePageMembers(xLevel);
+
+ if (aMembers.size() == 1)
+ aFieldOutputDescription = aMembers[0];
+ else
+ aFieldOutputDescription = ScResId(SCSTR_MULTIPLE);
+ }
+ else
+ {
+ aFieldOutputDescription = ScResId(SCSTR_ALL);
+ }
+ m_aFieldOutputDescriptionMap[nDim] = aFieldOutputDescription;
+ }
+ break;
+
+ case sheet::DataPilotFieldOrientation_DATA:
+ {
+ aDataFieldNumberFormatMap[xLevelName->getName()] = nNumberFormat;
+ m_aDataFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // Transform the name of data fields
+ for (chart2::data::PivotTableFieldEntry& rDataFields : m_aDataFields)
+ {
+ rDataFields.Name = aDataFieldCaptionNames[rDataFields.Name];
+ }
+
+ // Apply number format to the data
+ if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_ROW)
+ {
+ for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector)
+ {
+ size_t i = 0;
+ for (ValueAndFormat & rItem : rDataRow)
+ {
+ OUString sName = aDataFieldNamesVectors[i];
+ sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName];
+ rItem.m_nNumberFormat = nNumberFormat;
+ i++;
+ }
+ }
+ }
+ else if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_COLUMN)
+ {
+ size_t i = 0;
+ for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector)
+ {
+ OUString sName = aDataFieldNamesVectors[i];
+ sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName];
+ for (ValueAndFormat & rItem : rDataRow)
+ {
+ rItem.m_nNumberFormat = nNumberFormat;
+ }
+ i++;
+ }
+ }
+
+ // Sort fields so it respects the order of how it is represented in the pivot table
+
+ auto funcDimensionPositionSortCompare = [] (chart2::data::PivotTableFieldEntry const & entry1,
+ chart2::data::PivotTableFieldEntry const & entry2)
+ {
+ return entry1.DimensionPositionIndex < entry2.DimensionPositionIndex;
+ };
+
+ std::sort(m_aColumnFields.begin(), m_aColumnFields.end(), funcDimensionPositionSortCompare);
+ std::sort(m_aRowFields.begin(), m_aRowFields.end(), funcDimensionPositionSortCompare);
+ std::sort(m_aPageFields.begin(), m_aPageFields.end(), funcDimensionPositionSortCompare);
+ std::sort(m_aDataFields.begin(), m_aDataFields.end(), funcDimensionPositionSortCompare);
+
+ // Mark that we updated the data
+ m_bNeedsUpdate = false;
+}
+
+uno::Reference<chart2::data::XDataSequence>
+PivotTableDataProvider::assignValuesToDataSequence(size_t nIndex)
+{
+ uno::Reference<chart2::data::XDataSequence> xDataSequence;
+ if (nIndex >= m_aDataRowVector.size())
+ return xDataSequence;
+
+ OUString sDataID = lcl_identifierForData(nIndex);
+
+ std::vector<ValueAndFormat> const & rRowOfData = m_aDataRowVector[nIndex];
+ rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument, sDataID, std::vector(rRowOfData)));
+ pSequence->setRole("values-y");
+ xDataSequence = pSequence;
+ return xDataSequence;
+}
+
+uno::Reference<chart2::data::XDataSequence>
+PivotTableDataProvider::assignLabelsToDataSequence(size_t nIndex)
+{
+ uno::Reference<chart2::data::XDataSequence> xDataSequence;
+
+ OUString sLabelID = lcl_identifierForLabel(nIndex);
+
+ OUStringBuffer aLabel;
+ bool bFirst = true;
+
+ if (m_aLabels.empty())
+ {
+ aLabel = ScResId(STR_PIVOT_TOTAL);
+ }
+ else if (nIndex < m_aLabels.size())
+ {
+ for (ValueAndFormat const & rItem : m_aLabels[nIndex])
+ {
+ if (bFirst)
+ {
+ aLabel.append(rItem.m_aString);
+ bFirst = false;
+ }
+ else
+ {
+ aLabel.append(" - " + rItem.m_aString);
+ }
+ }
+ }
+
+ std::vector<ValueAndFormat> aLabelVector { ValueAndFormat(aLabel.makeStringAndClear()) };
+
+ rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument,
+ sLabelID, std::move(aLabelVector)));
+ pSequence->setRole("values-y");
+ xDataSequence = pSequence;
+ return xDataSequence;
+}
+
+css::uno::Reference<css::chart2::data::XDataSequence>
+ PivotTableDataProvider::assignFirstCategoriesToDataSequence()
+{
+ uno::Reference<chart2::data::XDataSequence> xDataSequence;
+
+ if (m_aCategoriesColumnOrientation.empty())
+ return xDataSequence;
+
+ std::vector<ValueAndFormat> const & rCategories = m_aCategoriesColumnOrientation.back();
+
+ rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument,
+ lcl_identifierForCategories(), std::vector(rCategories)));
+ pSequence->setRole("categories");
+ xDataSequence = pSequence;
+
+ return xDataSequence;
+}
+
+uno::Reference<chart2::data::XDataSource>
+ PivotTableDataProvider::createValuesDataSource()
+{
+ if (m_bNeedsUpdate)
+ collectPivotTableData();
+
+ uno::Reference<chart2::data::XDataSource> xDataSource;
+ std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences;
+
+ // Fill first sequence of categories
+ {
+ uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
+ xResult->setValues(assignFirstCategoriesToDataSequence());
+ aLabeledSequences.push_back(xResult);
+ }
+
+ // Fill values and labels
+ {
+ for (size_t i = 0; i < m_aDataRowVector.size(); ++i)
+ {
+ uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
+ xResult->setValues(assignValuesToDataSequence(i));
+ xResult->setLabel(assignLabelsToDataSequence(i));
+ aLabeledSequences.push_back(xResult);
+ }
+ }
+
+ xDataSource.set(new PivotTableDataSource(std::move(aLabeledSequences)));
+ return xDataSource;
+}
+
+
+uno::Sequence<beans::PropertyValue> SAL_CALL PivotTableDataProvider::detectArguments(
+ const uno::Reference<chart2::data::XDataSource> & xDataSource)
+{
+ if (!m_pDocument ||!xDataSource.is())
+ return uno::Sequence<beans::PropertyValue>();
+
+ return comphelper::InitPropertySequence({
+ { "CellRangeRepresentation", uno::Any(OUString("PivotChart")) },
+ { "DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) },
+ { "FirstCellAsLabel", uno::Any(false) },
+ { "HasCategories", uno::Any(true) }
+ });
+}
+
+sal_Bool SAL_CALL PivotTableDataProvider::createDataSequenceByRangeRepresentationPossible(const OUString& /*aRangeRepresentation*/)
+{
+ return false;
+}
+
+uno::Reference<chart2::data::XDataSequence> SAL_CALL
+ PivotTableDataProvider::createDataSequenceByRangeRepresentation(const OUString& /*rRangeRepresentation*/)
+{
+ uno::Reference<chart2::data::XDataSequence> xDataSequence;
+ return xDataSequence;
+}
+
+uno::Reference<chart2::data::XDataSequence> SAL_CALL
+ PivotTableDataProvider::createDataSequenceByValueArray(const OUString& /*aRole*/,
+ const OUString& /*aRangeRepresentation*/,
+ const OUString& /*aRoleQualifier*/)
+{
+ return uno::Reference<chart2::data::XDataSequence>();
+}
+
+uno::Reference<sheet::XRangeSelection> SAL_CALL PivotTableDataProvider::getRangeSelection()
+{
+ uno::Reference<sheet::XRangeSelection> xResult;
+
+ uno::Reference<frame::XModel> xModel(lcl_GetXModel(m_pDocument));
+ if (xModel.is())
+ xResult.set(xModel->getCurrentController(), uno::UNO_QUERY);
+
+ return xResult;
+}
+
+// XPivotTableDataProvider ========================================================
+
+uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getColumnFields()
+{
+ return comphelper::containerToSequence(m_aColumnFields);
+}
+
+uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getRowFields()
+{
+ return comphelper::containerToSequence(m_aRowFields);
+}
+
+uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getPageFields()
+{
+ return comphelper::containerToSequence(m_aPageFields);
+}
+
+uno::Sequence<chart2::data::PivotTableFieldEntry> PivotTableDataProvider::getDataFields()
+{
+ return comphelper::containerToSequence(m_aDataFields);
+}
+
+OUString PivotTableDataProvider::getPivotTableName()
+{
+ return m_sPivotTableName;
+}
+
+void PivotTableDataProvider::setPivotTableName(const OUString& sPivotTableName)
+{
+ ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
+ ScDPObject* pDPObject = pDPCollection->GetByName(sPivotTableName);
+ if (pDPObject)
+ m_sPivotTableName = sPivotTableName;
+}
+
+sal_Bool PivotTableDataProvider::hasPivotTable()
+{
+ if (m_sPivotTableName.isEmpty())
+ return false;
+
+ ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
+ ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName);
+
+ if (pDPObject)
+ return true;
+
+ return false;
+}
+
+uno::Reference<chart2::data::XDataSequence>
+ PivotTableDataProvider::createDataSequenceOfValuesByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+
+ if (m_bNeedsUpdate)
+ collectPivotTableData();
+
+ return assignValuesToDataSequence(size_t(nIndex));
+}
+
+uno::Reference<css::chart2::data::XDataSequence>
+ PivotTableDataProvider::createDataSequenceOfLabelsByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+
+ if (m_bNeedsUpdate)
+ collectPivotTableData();
+
+ return assignLabelsToDataSequence(size_t(nIndex));
+}
+
+uno::Reference<css::chart2::data::XDataSequence>
+ PivotTableDataProvider::createDataSequenceOfCategories()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_bNeedsUpdate)
+ collectPivotTableData();
+
+ return assignFirstCategoriesToDataSequence();
+}
+
+OUString PivotTableDataProvider::getFieldOutputDescription(sal_Int32 nDimensionIndex)
+{
+ if (nDimensionIndex < 0)
+ return OUString();
+ return m_aFieldOutputDescriptionMap[size_t(nDimensionIndex)];
+}
+
+// XModifyBroadcaster ========================================================
+
+void SAL_CALL PivotTableDataProvider::addModifyListener(const uno::Reference< util::XModifyListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+
+ m_aValueListeners.emplace_back(aListener);
+}
+
+void SAL_CALL PivotTableDataProvider::removeModifyListener(const uno::Reference<util::XModifyListener>& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ sal_uInt16 nCount = m_aValueListeners.size();
+ for (sal_uInt16 n = nCount; n--;)
+ {
+ uno::Reference<util::XModifyListener>& rObject = m_aValueListeners[n];
+ if (rObject == aListener)
+ {
+ m_aValueListeners.erase(m_aValueListeners.begin() + n);
+ }
+ }
+}
+
+// DataProvider XPropertySet ========================================================
+
+uno::Reference< beans::XPropertySetInfo> SAL_CALL
+ PivotTableDataProvider::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
+ return aRef;
+}
+
+void SAL_CALL PivotTableDataProvider::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue)
+{
+ if (rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS)
+ throw beans::UnknownPropertyException(rPropertyName);
+
+ if (!(rValue >>= m_bIncludeHiddenCells))
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL PivotTableDataProvider::getPropertyValue(const OUString& rPropertyName)
+{
+ uno::Any aRet;
+ if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS)
+ aRet <<= m_bIncludeHiddenCells;
+ else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
+ {
+ // This is a read-only property.
+ aRet <<= m_pDocument->PastingDrawFromOtherDoc();
+ }
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+ return aRet;
+}
+
+void SAL_CALL PivotTableDataProvider::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+void SAL_CALL PivotTableDataProvider::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XPropertyChangeListener>& /*rListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+void SAL_CALL PivotTableDataProvider::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+void SAL_CALL PivotTableDataProvider::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/ )
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/PivotTableDataSequence.cxx b/sc/source/ui/unoobj/PivotTableDataSequence.cxx
new file mode 100644
index 0000000000..dc81d03978
--- /dev/null
+++ b/sc/source/ui/unoobj/PivotTableDataSequence.cxx
@@ -0,0 +1,278 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <PivotTableDataSequence.hxx>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <miscuno.hxx>
+#include <document.hxx>
+#include <unonames.hxx>
+
+using namespace css;
+
+namespace sc
+{
+
+SC_SIMPLE_SERVICE_INFO( PivotTableDataSequence, "PivotTableDataSequence", "com.sun.star.chart2.data.DataSequence")
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetDataSequencePropertyMap()
+{
+ static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] =
+ {
+ { SC_UNONAME_HIDDENVALUES, 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), 0, 0 },
+ { SC_UNONAME_ROLE, 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(), 0, 0 },
+ { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aDataSequencePropertyMap_Impl;
+}
+
+PivotTableDataSequence::PivotTableDataSequence(ScDocument* pDocument, OUString sID,
+ std::vector<ValueAndFormat>&& rData)
+ : m_pDocument(pDocument)
+ , m_aID(std::move(sID))
+ , m_aData(std::move(rData))
+ , m_aPropSet(lcl_GetDataSequencePropertyMap())
+{
+ if (m_pDocument)
+ m_pDocument->AddUnoObject(*this);
+}
+
+PivotTableDataSequence::~PivotTableDataSequence()
+{
+ SolarMutexGuard g;
+
+ if (m_pDocument)
+ m_pDocument->RemoveUnoObject(*this);
+}
+
+void PivotTableDataSequence::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pDocument = nullptr;
+ }
+}
+
+uno::Sequence<uno::Any> SAL_CALL PivotTableDataSequence::getData()
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pDocument)
+ throw uno::RuntimeException();
+
+ uno::Sequence<uno::Any> aSeq(m_aData.size());
+ auto pSeq = aSeq.getArray();
+
+ size_t i = 0;
+ for (ValueAndFormat const & rItem : m_aData)
+ {
+ if (rItem.m_eType == ValueType::Numeric)
+ pSeq[i] <<= double(rItem.m_fValue);
+ else if (rItem.m_eType == ValueType::String)
+ pSeq[i] <<= rItem.m_aString;
+
+ i++;
+ }
+ return aSeq;
+}
+
+// XNumericalDataSequence --------------------------------------------------
+
+uno::Sequence<double> SAL_CALL PivotTableDataSequence::getNumericalData()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pDocument)
+ throw uno::RuntimeException();
+
+ uno::Sequence<double> aSeq(m_aData.size());
+ auto pSeq = aSeq.getArray();
+
+ size_t i = 0;
+ for (ValueAndFormat const & rItem : m_aData)
+ {
+ pSeq[i] = rItem.m_fValue;
+ i++;
+ }
+ return aSeq;
+}
+
+// XTextualDataSequence --------------------------------------------------
+
+uno::Sequence<OUString> SAL_CALL PivotTableDataSequence::getTextualData()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pDocument)
+ throw uno::RuntimeException();
+
+ uno::Sequence<OUString> aSeq(m_aData.size());
+ auto pSeq = aSeq.getArray();
+
+ size_t i = 0;
+ for (ValueAndFormat const & rItem : m_aData)
+ {
+ if (rItem.m_eType == ValueType::String)
+ pSeq[i] = rItem.m_aString;
+ i++;
+ }
+ return aSeq;
+}
+
+OUString SAL_CALL PivotTableDataSequence::getSourceRangeRepresentation()
+{
+ return m_aID;
+}
+
+uno::Sequence<OUString> SAL_CALL PivotTableDataSequence::generateLabel(chart2::data::LabelOrigin /*eOrigin*/)
+{
+ SolarMutexGuard aGuard;
+ if (!m_pDocument)
+ throw uno::RuntimeException();
+
+ uno::Sequence<OUString> aSeq;
+ return aSeq;
+}
+
+sal_Int32 SAL_CALL PivotTableDataSequence::getNumberFormatKeyByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if (nIndex == -1 && !m_aData.empty())
+ {
+ return m_aData[0].m_nNumberFormat;
+ }
+ else if (nIndex < 0 && o3tl::make_unsigned(nIndex) >= m_aData.size())
+ {
+ SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'.");
+ return 0;
+ }
+ return m_aData[size_t(nIndex)].m_nNumberFormat;
+}
+
+// XCloneable ================================================================
+
+uno::Reference<util::XCloneable> SAL_CALL PivotTableDataSequence::createClone()
+{
+ SolarMutexGuard aGuard;
+
+ rtl::Reference<PivotTableDataSequence> pClone(new PivotTableDataSequence(m_pDocument, m_aID, std::vector(m_aData)));
+ pClone->setRole(m_aRole);
+
+ return pClone;
+}
+
+// XModifyBroadcaster ========================================================
+
+void SAL_CALL PivotTableDataSequence::addModifyListener(const uno::Reference<util::XModifyListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+ m_aValueListeners.emplace_back(aListener);
+}
+
+void SAL_CALL PivotTableDataSequence::removeModifyListener(const uno::Reference<util::XModifyListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+
+ sal_uInt16 nCount = m_aValueListeners.size();
+ for (sal_uInt16 n = nCount; n--; )
+ {
+ uno::Reference<util::XModifyListener>& rObj = m_aValueListeners[n];
+ if (rObj == aListener)
+ {
+ m_aValueListeners.erase(m_aValueListeners.begin() + n);
+ }
+ }
+}
+
+// DataSequence XPropertySet -------------------------------------------------
+
+uno::Reference< beans::XPropertySetInfo> SAL_CALL PivotTableDataSequence::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef = new SfxItemPropertySetInfo(m_aPropSet.getPropertyMap());
+ return aRef;
+}
+
+void SAL_CALL PivotTableDataSequence::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue)
+{
+ if (rPropertyName == SC_UNONAME_ROLE)
+ {
+ if (!(rValue >>= m_aRole))
+ throw lang::IllegalArgumentException();
+ }
+ else if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS
+ || rPropertyName == SC_UNONAME_HIDDENVALUES
+ || rPropertyName == SC_UNONAME_TIME_BASED
+ || rPropertyName == SC_UNONAME_HAS_STRING_LABEL)
+ {}
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+}
+
+uno::Any SAL_CALL PivotTableDataSequence::getPropertyValue(const OUString& rPropertyName)
+{
+ uno::Any aReturn;
+ if (rPropertyName == SC_UNONAME_ROLE)
+ aReturn <<= m_aRole;
+ else if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS)
+ aReturn <<= false;
+ else if (rPropertyName == SC_UNONAME_HIDDENVALUES)
+ {
+ css::uno::Sequence<sal_Int32> aHiddenValues;
+ aReturn <<= aHiddenValues;
+ }
+ else if (rPropertyName == SC_UNONAME_TIME_BASED)
+ {
+ aReturn <<= false;
+ }
+ else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL)
+ {
+ aReturn <<= false;
+ }
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+ return aReturn;
+}
+
+void SAL_CALL PivotTableDataSequence::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+void SAL_CALL PivotTableDataSequence::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+void SAL_CALL PivotTableDataSequence::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+void SAL_CALL PivotTableDataSequence::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
+{
+ OSL_FAIL("Not yet implemented");
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/PivotTableDataSource.cxx b/sc/source/ui/unoobj/PivotTableDataSource.cxx
new file mode 100644
index 0000000000..5c47f0d127
--- /dev/null
+++ b/sc/source/ui/unoobj/PivotTableDataSource.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/.
+ */
+
+#include <PivotTableDataSource.hxx>
+
+#include <sal/config.h>
+
+#include <miscuno.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace css;
+
+namespace sc
+{
+
+SC_SIMPLE_SERVICE_INFO(PivotTableDataSource, "PivotTableDataSource", "com.sun.star.chart2.data.DataSource")
+
+PivotTableDataSource::PivotTableDataSource(std::vector<css::uno::Reference<css::chart2::data::XLabeledDataSequence>>&& xLabeledSequence)
+ : m_xLabeledSequence(std::move(xLabeledSequence))
+{
+}
+
+PivotTableDataSource::~PivotTableDataSource()
+{
+}
+
+void PivotTableDataSource::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& /*rHint*/)
+{
+}
+
+uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> SAL_CALL
+ PivotTableDataSource::getDataSequences()
+{
+ SolarMutexGuard aGuard;
+
+ return comphelper::containerToSequence(m_xLabeledSequence);
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/TablePivotChart.cxx b/sc/source/ui/unoobj/TablePivotChart.cxx
new file mode 100644
index 0000000000..107d6d5baa
--- /dev/null
+++ b/sc/source/ui/unoobj/TablePivotChart.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 <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <svx/svdoole2.hxx>
+#include <svtools/embedhlp.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <miscuno.hxx>
+#include <docsh.hxx>
+
+#include <TablePivotChart.hxx>
+#include <ChartTools.hxx>
+
+using namespace css;
+
+namespace sc
+{
+
+SC_SIMPLE_SERVICE_INFO(TablePivotChart, "TablePivotChart", "com.sun.star.table.TablePivotChart")
+
+TablePivotChart::TablePivotChart(ScDocShell* pDocShell, SCTAB nTab, OUString aName)
+ : m_pDocShell(pDocShell)
+ , m_nTab(nTab)
+ , m_aChartName(std::move(aName))
+{
+ if (m_pDocShell)
+ m_pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+TablePivotChart::~TablePivotChart()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDocShell)
+ m_pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void TablePivotChart::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ m_pDocShell = nullptr;
+}
+
+// XEmbeddedObjectSupplier
+
+uno::Reference<lang::XComponent> SAL_CALL TablePivotChart::getEmbeddedObject()
+{
+ SolarMutexGuard aGuard;
+ SdrOle2Obj* pObject = sc::tools::findChartsByName(m_pDocShell, m_nTab, m_aChartName, sc::tools::ChartSourceType::PIVOT_TABLE);
+ if (pObject && svt::EmbeddedObjectRef::TryRunningState(pObject->GetObjRef()))
+ return uno::Reference<lang::XComponent>(pObject->GetObjRef()->getComponent(), uno::UNO_QUERY);
+ return nullptr;
+}
+
+// XNamed
+
+OUString SAL_CALL TablePivotChart::getName()
+{
+ return m_aChartName;
+}
+
+void SAL_CALL TablePivotChart::setName(OUString const & /* aName */)
+{
+ throw uno::RuntimeException(); // name cannot be changed
+}
+
+// XTablePivotChart
+
+OUString SAL_CALL TablePivotChart::getPivotTableName()
+{
+ SolarMutexGuard aGuard;
+
+ SdrOle2Obj* pObject = sc::tools::findChartsByName(m_pDocShell, m_nTab, m_aChartName, sc::tools::ChartSourceType::PIVOT_TABLE);
+ if (!pObject)
+ return OUString();
+
+ uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef();
+ if (!xObject.is())
+ return OUString();
+
+ uno::Reference<chart2::XChartDocument> xChartDoc(xObject->getComponent(), uno::UNO_QUERY);
+ if (!xChartDoc.is())
+ return OUString();
+
+ uno::Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xChartDoc->getDataProvider(), uno::UNO_QUERY);
+ if (!xPivotTableDataProvider.is())
+ return OUString();
+
+ return xPivotTableDataProvider->getPivotTableName();
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/TablePivotCharts.cxx b/sc/source/ui/unoobj/TablePivotCharts.cxx
new file mode 100644
index 0000000000..f69a8298c3
--- /dev/null
+++ b/sc/source/ui/unoobj/TablePivotCharts.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tools/gen.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdundo.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/classids.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/globname.hxx>
+#include <svtools/embedhlp.hxx>
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+
+#include <TablePivotChart.hxx>
+#include <TablePivotCharts.hxx>
+#include <PivotTableDataProvider.hxx>
+#include <ChartTools.hxx>
+
+#include <miscuno.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+
+using namespace css;
+
+namespace sc
+{
+
+SC_SIMPLE_SERVICE_INFO(TablePivotCharts, "TablePivotCharts", "com.sun.star.table.TablePivotCharts")
+
+TablePivotCharts::TablePivotCharts(ScDocShell* pDocShell, SCTAB nTab)
+ : m_pDocShell(pDocShell)
+ , m_nTab(nTab)
+{
+ m_pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+TablePivotCharts::~TablePivotCharts()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pDocShell)
+ m_pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void TablePivotCharts::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ m_pDocShell = nullptr;
+}
+
+// XTablePivotCharts
+void SAL_CALL TablePivotCharts::addNewByName(OUString const & rName,
+ const awt::Rectangle& aRect,
+ OUString const & rDataPilotName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pDocShell)
+ return;
+
+ ScDocument& rDoc = m_pDocShell->GetDocument();
+ ScDrawLayer* pModel = m_pDocShell->MakeDrawLayer();
+ SdrPage* pPage = pModel->GetPage(sal_uInt16(m_nTab));
+ if (!pPage)
+ return;
+
+ // chart can't be inserted if any ole object with that name exists on any table
+ // (empty string: generate valid name)
+
+ OUString aName = rName;
+ SCTAB nDummy;
+ if (!aName.isEmpty() && pModel->GetNamedObject(aName, SdrObjKind::OLE2, nDummy))
+ {
+ // object exists - only RuntimeException is specified
+ throw uno::RuntimeException();
+ }
+
+ uno::Reference<embed::XEmbeddedObject> xObject;
+
+ if (SvtModuleOptions().IsChart())
+ xObject = m_pDocShell->GetEmbeddedObjectContainer().CreateEmbeddedObject(SvGlobalName(SO3_SCH_CLASSID).GetByteSequence(), aName);
+
+ if (!xObject.is())
+ return;
+
+ Point aRectPos(aRect.X, aRect.Y);
+ bool bLayoutRTL = rDoc.IsLayoutRTL(m_nTab);
+ if ((aRectPos.X() < 0 && !bLayoutRTL) || (aRectPos.X() > 0 && bLayoutRTL))
+ aRectPos.setX( 0 );
+
+ if (aRectPos.Y() < 0)
+ aRectPos.setY( 0 );
+
+ Size aRectSize(aRect.Width, aRect.Height);
+ if (aRectSize.Width() <= 0)
+ aRectSize.setWidth( 5000 ); // default size
+
+ if (aRectSize.Height() <= 0)
+ aRectSize.setHeight( 5000 );
+
+ ::tools::Rectangle aInsRect(aRectPos, aRectSize);
+
+ sal_Int64 nAspect(embed::Aspects::MSOLE_CONTENT);
+ MapUnit aMapUnit(VCLUnoHelper::UnoEmbed2VCLMapUnit(xObject->getMapUnit(nAspect)));
+ Size aSize(aInsRect.GetSize());
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit));
+ awt::Size aAwtSize;
+ aAwtSize.Width = aSize.Width();
+ aAwtSize.Height = aSize.Height();
+
+ rtl::Reference<sc::PivotTableDataProvider> pPivotTableDataProvider(new sc::PivotTableDataProvider(rDoc));
+ pPivotTableDataProvider->setPivotTableName(rDataPilotName);
+
+ uno::Reference<chart2::data::XDataProvider> xDataProvider(pPivotTableDataProvider);
+
+ uno::Reference<chart2::data::XDataReceiver> xReceiver;
+
+ if (xObject.is())
+ xReceiver.set(xObject->getComponent(), uno::UNO_QUERY);
+
+ if (xReceiver.is())
+ {
+ xReceiver->attachDataProvider(xDataProvider);
+
+ uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(cppu::getXWeak(m_pDocShell->GetModel()), uno::UNO_QUERY);
+ xReceiver->attachNumberFormatsSupplier(xNumberFormatsSupplier);
+
+ uno::Sequence<beans::PropertyValue> aArgs( comphelper::InitPropertySequence({
+ { "CellRangeRepresentation", uno::Any(rDataPilotName) },
+ { "HasCategories", uno::Any(true) },
+ { "DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) }
+ }));
+ xReceiver->setArguments(aArgs);
+ }
+
+ rtl::Reference<SdrOle2Obj> pObject = new SdrOle2Obj(
+ *pModel,
+ svt::EmbeddedObjectRef(xObject, embed::Aspects::MSOLE_CONTENT),
+ aName,
+ aInsRect);
+
+ if (xObject.is())
+ xObject->setVisualAreaSize(nAspect, aAwtSize);
+
+ pPage->InsertObject(pObject.get());
+ pModel->AddUndo(std::make_unique<SdrUndoInsertObj>(*pObject));
+}
+
+void SAL_CALL TablePivotCharts::removeByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ SdrOle2Obj* pObject = sc::tools::findChartsByName(m_pDocShell, m_nTab, rName, sc::tools::ChartSourceType::PIVOT_TABLE);
+ if (pObject)
+ {
+ ScDocument& rDoc = m_pDocShell->GetDocument();
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ SdrPage* pPage = pModel->GetPage(sal_uInt16(m_nTab));
+ pModel->AddUndo(std::make_unique<SdrUndoDelObj>(*pObject));
+ pPage->RemoveObject(pObject->GetOrdNum());
+ }
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL TablePivotCharts::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nCount = 0;
+
+ if (!m_pDocShell)
+ return nCount;
+
+ sc::tools::ChartIterator aIterator(m_pDocShell, m_nTab, sc::tools::ChartSourceType::PIVOT_TABLE);
+
+ SdrOle2Obj* pOleObject = aIterator.next();
+ while (pOleObject)
+ {
+ if (pOleObject->GetObjRef().is())
+ nCount++;
+ pOleObject = aIterator.next();
+ }
+ return nCount;
+}
+
+uno::Any SAL_CALL TablePivotCharts::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ SdrOle2Obj* pObject = sc::tools::getChartByIndex(m_pDocShell, m_nTab, nIndex,
+ sc::tools::ChartSourceType::PIVOT_TABLE);
+ if (!pObject)
+ throw lang::IndexOutOfBoundsException();
+
+ OUString aName;
+ uno::Reference<embed::XEmbeddedObject> xObject = pObject->GetObjRef();
+ if (xObject.is())
+ aName = m_pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject);
+
+ if (aName.isEmpty())
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference<table::XTablePivotChart> xChart(new TablePivotChart(m_pDocShell, m_nTab, aName));
+ if (!xChart.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xChart);
+}
+
+uno::Type SAL_CALL TablePivotCharts::getElementType()
+{
+ return cppu::UnoType<table::XTablePivotChart>::get();
+}
+
+sal_Bool SAL_CALL TablePivotCharts::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return getCount() != 0;
+}
+
+uno::Any SAL_CALL TablePivotCharts::getByName(OUString const & rName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!sc::tools::findChartsByName(m_pDocShell, m_nTab, rName, sc::tools::ChartSourceType::PIVOT_TABLE))
+ throw container::NoSuchElementException();
+
+ uno::Reference<table::XTablePivotChart> xChart(new TablePivotChart(m_pDocShell, m_nTab, rName));
+ if (!xChart.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xChart);
+}
+
+uno::Sequence<OUString> SAL_CALL TablePivotCharts::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ std::vector<OUString> aElements;
+ sc::tools::ChartIterator aIterator(m_pDocShell, m_nTab, sc::tools::ChartSourceType::PIVOT_TABLE);
+
+ SdrOle2Obj* pOleObject = aIterator.next();
+ while (pOleObject)
+ {
+ uno::Reference<embed::XEmbeddedObject> xObject = pOleObject->GetObjRef();
+ if (xObject.is())
+ {
+ OUString aName = m_pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName(xObject);
+ aElements.push_back(aName);
+ }
+ pOleObject = aIterator.next();
+ }
+ return comphelper::containerToSequence(aElements);
+}
+
+sal_Bool SAL_CALL TablePivotCharts::hasByName(OUString const & rName)
+{
+ SolarMutexGuard aGuard;
+
+ return sc::tools::findChartsByName(m_pDocShell, m_nTab, rName, sc::tools::ChartSourceType::PIVOT_TABLE) != nullptr;
+}
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/addruno.cxx b/sc/source/ui/unoobj/addruno.cxx
new file mode 100644
index 0000000000..7a1c2b8b80
--- /dev/null
+++ b/sc/source/ui/unoobj/addruno.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/table/CellAddress.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+
+#include <docsh.hxx>
+#include <unonames.hxx>
+#include <miscuno.hxx>
+#include <convuno.hxx>
+#include <addruno.hxx>
+
+using namespace com::sun::star;
+
+ScAddressConversionObj::ScAddressConversionObj(ScDocShell* pDocSh, bool _bIsRange) :
+ pDocShell( pDocSh ),
+ nRefSheet( 0 ),
+ bIsRange( _bIsRange )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScAddressConversionObj::~ScAddressConversionObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScAddressConversionObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // invalid
+ }
+}
+
+bool ScAddressConversionObj::ParseUIString( const OUString& rUIString, ::formula::FormulaGrammar::AddressConvention eConv )
+{
+ if (!pDocShell)
+ return false;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bSuccess = false;
+ if ( bIsRange )
+ {
+ ScRefFlags nResult = aRange.ParseAny( rUIString, rDoc, eConv );
+ if ( nResult & ScRefFlags::VALID )
+ {
+ if ( ( nResult & ScRefFlags::TAB_3D ) == ScRefFlags::ZERO )
+ aRange.aStart.SetTab( static_cast<SCTAB>(nRefSheet) );
+ if ( ( nResult & ScRefFlags::TAB2_3D ) == ScRefFlags::ZERO )
+ aRange.aEnd.SetTab( aRange.aStart.Tab() );
+ // different sheets are not supported in CellRangeAddress
+ if ( aRange.aStart.Tab() == aRange.aEnd.Tab() )
+ bSuccess = true;
+ }
+ }
+ else
+ {
+ ScRefFlags nResult = aRange.aStart.Parse( rUIString, rDoc, eConv );
+ if ( nResult & ScRefFlags::VALID )
+ {
+ if ( ( nResult & ScRefFlags::TAB_3D ) == ScRefFlags::ZERO )
+ aRange.aStart.SetTab( static_cast<SCTAB>(nRefSheet) );
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAddressConversionObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+
+ if ( bIsRange )
+ {
+ static const SfxItemPropertyMapEntry aPropertyMap[] =
+ {
+ { SC_UNONAME_ADDRESS, 0, cppu::UnoType<table::CellRangeAddress>::get(), 0, 0 },
+ { SC_UNONAME_PERSREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_REFSHEET, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_UIREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef(new SfxItemPropertySetInfo( aPropertyMap ));
+ return aRef;
+ }
+ else
+ {
+ static const SfxItemPropertyMapEntry aPropertyMap[] =
+ {
+ { SC_UNONAME_ADDRESS, 0, cppu::UnoType<table::CellAddress>::get(), 0, 0 },
+ { SC_UNONAME_PERSREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_REFSHEET, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_UIREPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_XLA1REPR, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef(new SfxItemPropertySetInfo( aPropertyMap ));
+ return aRef;
+ }
+}
+
+void SAL_CALL ScAddressConversionObj::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ bool bSuccess = false;
+ if ( aPropertyName == SC_UNONAME_ADDRESS )
+ {
+ // read the cell/range address from API struct
+ if ( bIsRange )
+ {
+ table::CellRangeAddress aRangeAddress;
+ if ( aValue >>= aRangeAddress )
+ {
+ ScUnoConversion::FillScRange( aRange, aRangeAddress );
+ bSuccess = true;
+ }
+ }
+ else
+ {
+ table::CellAddress aCellAddress;
+ if ( aValue >>= aCellAddress )
+ {
+ ScUnoConversion::FillScAddress( aRange.aStart, aCellAddress );
+ bSuccess = true;
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_REFSHEET )
+ {
+ // set the reference sheet
+ sal_Int32 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ {
+ nRefSheet = nIntVal;
+ bSuccess = true;
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_UIREPR )
+ {
+ // parse the UI representation string
+ OUString sRepresentation;
+ if (aValue >>= sRepresentation)
+ {
+ bSuccess = ParseUIString( sRepresentation );
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_PERSREPR || aPropertyName == SC_UNONAME_XLA1REPR )
+ {
+ ::formula::FormulaGrammar::AddressConvention eConv = aPropertyName == SC_UNONAME_XLA1REPR ?
+ ::formula::FormulaGrammar::CONV_XL_A1 : ::formula::FormulaGrammar::CONV_OOO;
+
+ // parse the file format string
+ OUString sRepresentation;
+ if (aValue >>= sRepresentation)
+ {
+ OUString aUIString(sRepresentation);
+
+ // cell or range: strip a single "." at the start
+ if ( aUIString[0]== '.' )
+ aUIString = aUIString.copy( 1 );
+
+ if ( bIsRange )
+ {
+ // range: also strip a "." after the last colon
+ sal_Int32 nColon = aUIString.lastIndexOf( ':' );
+ if ( nColon >= 0 && nColon < aUIString.getLength() - 1 &&
+ aUIString[nColon+1] == '.' )
+ aUIString = aUIString.replaceAt( nColon+1, 1, u"" );
+ }
+
+ // parse the rest like a UI string
+ bSuccess = ParseUIString( aUIString, eConv );
+ }
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ if ( !bSuccess )
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL ScAddressConversionObj::getPropertyValue( const OUString& aPropertyName )
+{
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ uno::Any aRet;
+
+ if ( aPropertyName == SC_UNONAME_ADDRESS )
+ {
+ if ( bIsRange )
+ {
+ table::CellRangeAddress aRangeAddress;
+ ScUnoConversion::FillApiRange( aRangeAddress, aRange );
+ aRet <<= aRangeAddress;
+ }
+ else
+ {
+ table::CellAddress aCellAddress;
+ ScUnoConversion::FillApiAddress( aCellAddress, aRange.aStart );
+ aRet <<= aCellAddress;
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_REFSHEET )
+ {
+ aRet <<= nRefSheet;
+ }
+ else if ( aPropertyName == SC_UNONAME_UIREPR )
+ {
+ // generate UI representation string - include sheet only if different from ref sheet
+ OUString aFormatStr;
+ ScRefFlags nFlags = ScRefFlags::VALID;
+ if ( aRange.aStart.Tab() != nRefSheet )
+ nFlags |= ScRefFlags::TAB_3D;
+ if ( bIsRange )
+ aFormatStr = aRange.Format(rDoc, nFlags);
+ else
+ aFormatStr = aRange.aStart.Format(nFlags, &rDoc);
+ aRet <<= aFormatStr;
+ }
+ else if ( aPropertyName == SC_UNONAME_PERSREPR || aPropertyName == SC_UNONAME_XLA1REPR )
+ {
+ ::formula::FormulaGrammar::AddressConvention eConv = aPropertyName == SC_UNONAME_XLA1REPR ?
+ ::formula::FormulaGrammar::CONV_XL_A1 : ::formula::FormulaGrammar::CONV_OOO;
+
+ // generate file format string - always include sheet
+ OUString aFormatStr(aRange.aStart.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc, eConv));
+ if ( bIsRange )
+ {
+ // manually concatenate range so both parts always have the sheet name
+ aFormatStr += ":";
+ ScRefFlags nFlags = ScRefFlags::VALID;
+ if( eConv != ::formula::FormulaGrammar::CONV_XL_A1 )
+ nFlags |= ScRefFlags::TAB_3D;
+ OUString aSecond(aRange.aEnd.Format(nFlags, &rDoc, eConv));
+ aFormatStr += aSecond ;
+ }
+ aRet <<= aFormatStr;
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAddressConversionObj )
+
+// lang::XServiceInfo
+
+OUString SAL_CALL ScAddressConversionObj::getImplementationName()
+{
+ return "ScAddressConversionObj";
+}
+
+sal_Bool SAL_CALL ScAddressConversionObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScAddressConversionObj::getSupportedServiceNames()
+{
+ if (bIsRange)
+ return {SC_SERVICENAME_RANGEADDRESS};
+ else
+ return {SC_SERVICENAME_CELLADDRESS};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/afmtuno.cxx b/sc/source/ui/unoobj/afmtuno.cxx
new file mode 100644
index 0000000000..fce07d2b96
--- /dev/null
+++ b/sc/source/ui/unoobj/afmtuno.cxx
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/memberids.h>
+#include <osl/diagnose.h>
+#include <svl/poolitem.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <svx/unomid.hxx>
+#include <unowids.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/TableBorder2.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+
+#include <attrib.hxx>
+#include <afmtuno.hxx>
+#include <miscuno.hxx>
+#include <autoform.hxx>
+#include <scdll.hxx>
+#include <unonames.hxx>
+#include <cellsuno.hxx>
+
+using namespace ::com::sun::star;
+
+// an AutoFormat has always 16 entries
+#define SC_AF_FIELD_COUNT 16
+
+// AutoFormat map only for PropertySetInfo without Which-IDs
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetAutoFormatMap()
+{
+ static const SfxItemPropertyMapEntry aAutoFormatMap_Impl[] =
+ {
+ { SC_UNONAME_INCBACK, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_INCBORD, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_INCFONT, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_INCJUST, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_INCNUM, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_INCWIDTH, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aAutoFormatMap_Impl;
+}
+
+//! number format (String/Language) ??? (in XNumberFormat only ReadOnly)
+//! table::TableBorder ??!?
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetAutoFieldMap()
+{
+ static const SfxItemPropertyMapEntry aAutoFieldMap_Impl[] =
+ {
+ { SC_UNONAME_CELLBACK, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ { SC_UNONAME_CCOLOR, ATTR_FONT_COLOR, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT, cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CFONT, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, ::cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, ::cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, ::cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, ::cppu::UnoType<table::CellHoriJustify>::get(), 0, 0 },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, ::cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ return aAutoFieldMap_Impl;
+}
+
+constexpr OUString SCAUTOFORMATSOBJ_SERVICE = u"com.sun.star.sheet.TableAutoFormats"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScAutoFormatFieldObj, "ScAutoFormatFieldObj", "com.sun.star.sheet.TableAutoFormatField" )
+SC_SIMPLE_SERVICE_INFO( ScAutoFormatObj, "ScAutoFormatObj", "com.sun.star.sheet.TableAutoFormat" )
+SC_SIMPLE_SERVICE_INFO( ScAutoFormatsObj, "stardiv.StarCalc.ScAutoFormatsObj", SCAUTOFORMATSOBJ_SERVICE )
+
+static bool lcl_FindAutoFormatIndex( const ScAutoFormat& rFormats, std::u16string_view rName, sal_uInt16& rOutIndex )
+{
+ ScAutoFormat::const_iterator itBeg = rFormats.begin(), itEnd = rFormats.end();
+ for (ScAutoFormat::const_iterator it = itBeg; it != itEnd; ++it)
+ {
+ const ScAutoFormatData *const pEntry = it->second.get();
+ const OUString& aEntryName = pEntry->GetName();
+ if ( aEntryName == rName )
+ {
+ size_t nPos = std::distance(itBeg, it);
+ rOutIndex = nPos;
+ return true;
+ }
+ }
+ return false;
+}
+
+ScAutoFormatsObj::ScAutoFormatsObj()
+{
+ //! This object should only exist once and it must be known to Auto-Format-Data,
+ //! so that changes can be broadcasted
+}
+
+ScAutoFormatsObj::~ScAutoFormatsObj()
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ScAutoFormatsObj_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ return cppu::acquire(new ScAutoFormatsObj);
+}
+
+// XTableAutoFormats
+
+rtl::Reference<ScAutoFormatObj> ScAutoFormatsObj::GetObjectByIndex_Impl(sal_uInt16 nIndex)
+{
+ if (nIndex < ScGlobal::GetOrCreateAutoFormat()->size())
+ return new ScAutoFormatObj(nIndex);
+
+ return nullptr; // wrong index
+}
+
+rtl::Reference<ScAutoFormatObj> ScAutoFormatsObj::GetObjectByName_Impl(std::u16string_view aName)
+{
+ sal_uInt16 nIndex;
+ if (lcl_FindAutoFormatIndex(
+ *ScGlobal::GetOrCreateAutoFormat(), aName, nIndex ))
+ return GetObjectByIndex_Impl(nIndex);
+ return nullptr;
+}
+
+// container::XNameContainer
+
+void SAL_CALL ScAutoFormatsObj::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ // Reflection need not be uno::XInterface, can be any interface...
+ uno::Reference< uno::XInterface > xInterface(aElement, uno::UNO_QUERY);
+ if ( xInterface.is() )
+ {
+ ScAutoFormatObj* pFormatObj = dynamic_cast<ScAutoFormatObj*>( xInterface.get() );
+ if ( pFormatObj && !pFormatObj->IsInserted() )
+ {
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+
+ sal_uInt16 nDummy;
+ if (lcl_FindAutoFormatIndex( *pFormats, aName, nDummy ))
+ {
+ throw container::ElementExistException();
+ }
+
+ std::unique_ptr<ScAutoFormatData> pNew(new ScAutoFormatData());
+ pNew->SetName( aName );
+
+ if (pFormats->insert(std::move(pNew)) != pFormats->end())
+ {
+ //! notify to other objects
+ pFormats->Save();
+
+ sal_uInt16 nNewIndex;
+ if (lcl_FindAutoFormatIndex( *pFormats, aName, nNewIndex ))
+ {
+ pFormatObj->InitFormat( nNewIndex ); // can be used now
+ bDone = true;
+ }
+ }
+ else
+ {
+ OSL_FAIL("AutoFormat could not be inserted");
+ throw uno::RuntimeException();
+ }
+ }
+ }
+
+ if (!bDone)
+ {
+ // other errors are handled above
+ throw lang::IllegalArgumentException();
+ }
+}
+
+void SAL_CALL ScAutoFormatsObj::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ //! combine?
+ removeByName( aName );
+ insertByName( aName, aElement );
+}
+
+void SAL_CALL ScAutoFormatsObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+
+ ScAutoFormat::iterator it = pFormats->find(aName);
+ if (it == pFormats->end())
+ {
+ throw container::NoSuchElementException();
+ }
+ pFormats->erase(it);
+
+ //! notify to other objects
+ pFormats->Save(); // save immediately
+
+}
+
+// container::XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScAutoFormatsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.TableAutoFormatEnumeration");
+}
+
+// container::XIndexAccess
+
+sal_Int32 SAL_CALL ScAutoFormatsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return ScGlobal::GetOrCreateAutoFormat()->size();
+}
+
+uno::Any SAL_CALL ScAutoFormatsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< container::XNamed > xFormat(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
+ if (!xFormat.is())
+ throw lang::IndexOutOfBoundsException();
+ return uno::Any(xFormat);
+}
+
+uno::Type SAL_CALL ScAutoFormatsObj::getElementType()
+{
+ return cppu::UnoType<container::XNamed>::get(); // must match getByIndex
+}
+
+sal_Bool SAL_CALL ScAutoFormatsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// container::XNameAccess
+
+uno::Any SAL_CALL ScAutoFormatsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< container::XNamed > xFormat(GetObjectByName_Impl(aName));
+ if (!xFormat.is())
+ throw container::NoSuchElementException();
+ return uno::Any(xFormat);
+}
+
+uno::Sequence<OUString> SAL_CALL ScAutoFormatsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+ uno::Sequence<OUString> aSeq(pFormats->size());
+ OUString* pAry = aSeq.getArray();
+ size_t i = 0;
+ for (const auto& rEntry : *pFormats)
+ {
+ pAry[i] = rEntry.second->GetName();
+ ++i;
+ }
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScAutoFormatsObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nDummy;
+ return lcl_FindAutoFormatIndex(
+ *ScGlobal::GetOrCreateAutoFormat(), aName, nDummy );
+}
+
+ScAutoFormatObj::ScAutoFormatObj(sal_uInt16 nIndex) :
+ aPropSet( lcl_GetAutoFormatMap() ),
+ nFormatIndex( nIndex )
+{
+}
+
+ScAutoFormatObj::~ScAutoFormatObj()
+{
+ // If an AutoFormat object is released, then eventually changes are saved
+ // so that they become visible in e.g Writer
+
+ if (IsInserted())
+ {
+ ScAutoFormat* pFormats = ScGlobal::GetAutoFormat();
+ if ( pFormats && pFormats->IsSaveLater() )
+ pFormats->Save();
+
+ // Save() resets flag SaveLater
+ }
+}
+
+void ScAutoFormatObj::InitFormat( sal_uInt16 nNewIndex )
+{
+ OSL_ENSURE( nFormatIndex == SC_AFMTOBJ_INVALID, "ScAutoFormatObj::InitFormat is multiple" );
+ nFormatIndex = nNewIndex;
+}
+
+// XTableAutoFormat
+
+rtl::Reference<ScAutoFormatFieldObj> ScAutoFormatObj::GetObjectByIndex_Impl(sal_uInt16 nIndex)
+{
+ if ( IsInserted() && nIndex < SC_AF_FIELD_COUNT )
+ return new ScAutoFormatFieldObj( nFormatIndex, nIndex );
+
+ return nullptr;
+}
+
+// container::XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScAutoFormatObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.TableAutoFormatEnumeration");
+}
+
+// container::XIndexAccess
+
+sal_Int32 SAL_CALL ScAutoFormatObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ if (IsInserted())
+ return SC_AF_FIELD_COUNT; // always 16 elements
+ else
+ return 0;
+}
+
+uno::Any SAL_CALL ScAutoFormatObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if ( nIndex < 0 || nIndex >= getCount() )
+ throw lang::IndexOutOfBoundsException();
+
+ if (IsInserted())
+ return uno::Any(uno::Reference< beans::XPropertySet >(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex))));
+ return uno::Any();
+}
+
+uno::Type SAL_CALL ScAutoFormatObj::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get(); // must match getByIndex
+}
+
+sal_Bool SAL_CALL ScAutoFormatObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// container::XNamed
+
+OUString SAL_CALL ScAutoFormatObj::getName()
+{
+ SolarMutexGuard aGuard;
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+ if (IsInserted() && nFormatIndex < pFormats->size())
+ return pFormats->findByIndex(nFormatIndex)->GetName();
+
+ return OUString();
+}
+
+void SAL_CALL ScAutoFormatObj::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+
+ sal_uInt16 nDummy;
+ if (!IsInserted() || nFormatIndex >= pFormats->size() ||
+ lcl_FindAutoFormatIndex( *pFormats, aNewName, nDummy ))
+ {
+ // not inserted or name exists
+ throw uno::RuntimeException();
+ }
+
+ ScAutoFormat::iterator it = pFormats->begin();
+ std::advance(it, nFormatIndex);
+ ScAutoFormatData *const pData = it->second.get();
+ OSL_ENSURE(pData,"AutoFormat data not available");
+
+ std::unique_ptr<ScAutoFormatData> pNew(new ScAutoFormatData(*pData));
+ pNew->SetName( aNewName );
+
+ pFormats->erase(it);
+ it = pFormats->insert(std::move(pNew));
+ if (it != pFormats->end())
+ {
+ ScAutoFormat::iterator itBeg = pFormats->begin();
+ nFormatIndex = std::distance(itBeg, it);
+
+ //! notify to other objects
+ pFormats->SetSaveLater(true);
+ }
+ else
+ {
+ OSL_FAIL("AutoFormat could not be inserted");
+ nFormatIndex = 0; //! old index invalid
+ }
+}
+
+// beans::XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAutoFormatObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScAutoFormatObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+ if (!(IsInserted() && nFormatIndex < pFormats->size()))
+ return;
+
+ ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex);
+ OSL_ENSURE(pData,"AutoFormat data not available");
+
+ bool bBool = false;
+ if (aPropertyName == SC_UNONAME_INCBACK && (aValue >>= bBool))
+ pData->SetIncludeBackground( bBool );
+ else if (aPropertyName == SC_UNONAME_INCBORD && (aValue >>= bBool))
+ pData->SetIncludeFrame( bBool );
+ else if (aPropertyName == SC_UNONAME_INCFONT && (aValue >>= bBool))
+ pData->SetIncludeFont( bBool );
+ else if (aPropertyName == SC_UNONAME_INCJUST && (aValue >>= bBool))
+ pData->SetIncludeJustify( bBool );
+ else if (aPropertyName == SC_UNONAME_INCNUM && (aValue >>= bBool))
+ pData->SetIncludeValueFormat( bBool );
+ else if (aPropertyName == SC_UNONAME_INCWIDTH && (aValue >>= bBool))
+ pData->SetIncludeWidthHeight( bBool );
+
+ // else error
+
+ //! notify to other objects
+ pFormats->SetSaveLater(true);
+}
+
+uno::Any SAL_CALL ScAutoFormatObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aAny;
+
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+ if (IsInserted() && nFormatIndex < pFormats->size())
+ {
+ ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex);
+ OSL_ENSURE(pData,"AutoFormat data not available");
+
+ bool bValue;
+ bool bError = false;
+
+ if (aPropertyName == SC_UNONAME_INCBACK)
+ bValue = pData->GetIncludeBackground();
+ else if (aPropertyName == SC_UNONAME_INCBORD)
+ bValue = pData->GetIncludeFrame();
+ else if (aPropertyName == SC_UNONAME_INCFONT)
+ bValue = pData->GetIncludeFont();
+ else if (aPropertyName == SC_UNONAME_INCJUST)
+ bValue = pData->GetIncludeJustify();
+ else if (aPropertyName == SC_UNONAME_INCNUM)
+ bValue = pData->GetIncludeValueFormat();
+ else if (aPropertyName == SC_UNONAME_INCWIDTH)
+ bValue = pData->GetIncludeWidthHeight();
+ else
+ bError = true; // unknown property
+
+ if (!bError)
+ aAny <<= bValue;
+ }
+
+ return aAny;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAutoFormatObj )
+
+ScAutoFormatFieldObj::ScAutoFormatFieldObj(sal_uInt16 nFormat, sal_uInt16 nField) :
+ aPropSet( lcl_GetAutoFieldMap() ),
+ nFormatIndex( nFormat ),
+ nFieldIndex( nField )
+{
+}
+
+ScAutoFormatFieldObj::~ScAutoFormatFieldObj()
+{
+}
+
+// beans::XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAutoFormatFieldObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScAutoFormatFieldObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+ const SfxItemPropertyMapEntry* pEntry =
+ aPropSet.getPropertyMap().getByName( aPropertyName );
+
+ if ( !(pEntry && pEntry->nWID && nFormatIndex < pFormats->size()) )
+ return;
+
+ ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex);
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ if( const SfxPoolItem* pItem = pData->GetItem( nFieldIndex, pEntry->nWID ) )
+ {
+ bool bDone = false;
+
+ switch( pEntry->nWID )
+ {
+ case ATTR_STACKED:
+ {
+ table::CellOrientation eOrient;
+ if( aValue >>= eOrient )
+ {
+ switch( eOrient )
+ {
+ case table::CellOrientation_STANDARD:
+ pData->PutItem( nFieldIndex, ScVerticalStackCell( false ) );
+ break;
+ case table::CellOrientation_TOPBOTTOM:
+ pData->PutItem( nFieldIndex, ScVerticalStackCell( false ) );
+ pData->PutItem( nFieldIndex, ScRotateValueItem( 27000_deg100 ) );
+ break;
+ case table::CellOrientation_BOTTOMTOP:
+ pData->PutItem( nFieldIndex, ScVerticalStackCell( false ) );
+ pData->PutItem( nFieldIndex, ScRotateValueItem( 9000_deg100 ) );
+ break;
+ case table::CellOrientation_STACKED:
+ pData->PutItem( nFieldIndex, ScVerticalStackCell( true ) );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ bDone = true;
+ }
+ }
+ break;
+ default:
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+ bDone = pNewItem->PutValue( aValue, pEntry->nMemberId );
+ if (bDone)
+ pData->PutItem( nFieldIndex, *pNewItem );
+ }
+
+ if (bDone)
+ //! Notify to other objects?
+ pFormats->SetSaveLater(true);
+ }
+ }
+ else
+ {
+ switch (pEntry->nWID)
+ {
+ case SC_WID_UNO_TBLBORD:
+ {
+ table::TableBorder aBorder;
+ if ( aValue >>= aBorder ) // empty = nothing to do
+ {
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder );
+ pData->PutItem( nFieldIndex, aOuter );
+
+ //! Notify for other objects?
+ pFormats->SetSaveLater(true);
+ }
+ }
+ break;
+ case SC_WID_UNO_TBLBORD2:
+ {
+ table::TableBorder2 aBorder2;
+ if ( aValue >>= aBorder2 ) // empty = nothing to do
+ {
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder2 );
+ pData->PutItem( nFieldIndex, aOuter );
+
+ //! Notify for other objects?
+ pFormats->SetSaveLater(true);
+ }
+ }
+ break;
+ }
+ }
+}
+
+uno::Any SAL_CALL ScAutoFormatFieldObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aVal;
+
+ ScAutoFormat* pFormats = ScGlobal::GetOrCreateAutoFormat();
+ const SfxItemPropertyMapEntry* pEntry =
+ aPropSet.getPropertyMap().getByName( aPropertyName );
+
+ if ( pEntry && pEntry->nWID && nFormatIndex < pFormats->size() )
+ {
+ const ScAutoFormatData* pData = pFormats->findByIndex(nFormatIndex);
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ if( const SfxPoolItem* pItem = pData->GetItem( nFieldIndex, pEntry->nWID ) )
+ {
+ switch( pEntry->nWID )
+ {
+ case ATTR_STACKED:
+ {
+ const ScRotateValueItem* pRotItem = pData->GetItem( nFieldIndex, ATTR_ROTATE_VALUE );
+ Degree100 nRot = pRotItem ? pRotItem->GetValue() : 0_deg100;
+ bool bStacked = static_cast<const ScVerticalStackCell*>(pItem)->GetValue();
+ SvxOrientationItem( nRot, bStacked, TypedWhichId<SvxOrientationItem>(0) ).QueryValue( aVal );
+ }
+ break;
+ default:
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+ }
+ }
+ }
+ else
+ {
+ switch (pEntry->nWID)
+ {
+ case SC_WID_UNO_TBLBORD:
+ case SC_WID_UNO_TBLBORD2:
+ {
+ const SfxPoolItem* pItem = pData->GetItem(nFieldIndex, ATTR_BORDER);
+ if (pItem)
+ {
+ SvxBoxItem aOuter(*static_cast<const SvxBoxItem*>(pItem));
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+
+ if (pEntry->nWID == SC_WID_UNO_TBLBORD2)
+ ScHelperFunctions::AssignTableBorder2ToAny( aVal, aOuter, aInner);
+ else
+ ScHelperFunctions::AssignTableBorderToAny( aVal, aOuter, aInner);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return aVal;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAutoFormatFieldObj )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/appluno.cxx b/sc/source/ui/unoobj/appluno.cxx
new file mode 100644
index 0000000000..3b12dc9da7
--- /dev/null
+++ b/sc/source/ui/unoobj/appluno.cxx
@@ -0,0 +1,619 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <appluno.hxx>
+#include <sal/types.h>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <formula/funcvarargs.h>
+
+#include <vcl/svapp.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <miscuno.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <inputopt.hxx>
+#include <printopt.hxx>
+#include <userlist.hxx>
+#include <scdll.hxx>
+#include <unonames.hxx>
+#include <funcdesc.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/sheet/FunctionArgument.hpp>
+#include <memory>
+
+using namespace com::sun::star;
+
+// Special value for zoom
+//! somewhere central
+#define SC_ZOOMVAL_OPTIMAL (-1)
+#define SC_ZOOMVAL_WHOLEPAGE (-2)
+#define SC_ZOOMVAL_PAGEWIDTH (-3)
+
+// Number of PropertyValues in a function description
+#define SC_FUNCDESC_PROPCOUNT 5
+
+// everything without Which-ID, map only for PropertySetInfo
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetSettingsPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aSettingsPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_DOAUTOCP, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ENTERED, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_EXPREF, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_EXTFMT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_LINKUPD, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNONAME_MARKHDR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_METRIC, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNONAME_MOVEDIR, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNONAME_MOVESEL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_PRALLSH, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_PREMPTY, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_RANGEFIN, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_SCALE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNONAME_STBFUNC, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNONAME_ULISTS, 0, cppu::UnoType<uno::Sequence<OUString>>::get(), 0, 0},
+ { SC_UNONAME_PRMETRICS,0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_USETABCOL,0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_REPLWARN, 0, cppu::UnoType<bool>::get(), 0, 0},
+ };
+ return aSettingsPropertyMap_Impl;
+}
+
+constexpr OUString SCFUNCTIONLISTOBJ_SERVICE = u"com.sun.star.sheet.FunctionDescriptions"_ustr;
+constexpr OUString SCRECENTFUNCTIONSOBJ_SERVICE = u"com.sun.star.sheet.RecentFunctions"_ustr;
+constexpr OUString SCSPREADSHEETSETTINGS_SERVICE = u"com.sun.star.sheet.GlobalSheetSettings"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScFunctionListObj, "stardiv.StarCalc.ScFunctionListObj", SCFUNCTIONLISTOBJ_SERVICE )
+SC_SIMPLE_SERVICE_INFO( ScRecentFunctionsObj, "stardiv.StarCalc.ScRecentFunctionsObj", SCRECENTFUNCTIONSOBJ_SERVICE )
+SC_SIMPLE_SERVICE_INFO( ScSpreadsheetSettings, "stardiv.StarCalc.ScSpreadsheetSettings", SCSPREADSHEETSETTINGS_SERVICE )
+
+
+ScSpreadsheetSettings::ScSpreadsheetSettings() :
+ aPropSet( lcl_GetSettingsPropertyMap() )
+{
+}
+
+ScSpreadsheetSettings::~ScSpreadsheetSettings()
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScSpreadsheetSettings_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ return cppu::acquire(new ScSpreadsheetSettings());
+}
+
+
+bool ScSpreadsheetSettings::getPropertyBool(const OUString& aPropertyName)
+{
+ uno::Any any = getPropertyValue(aPropertyName);
+ bool b = false;
+ any >>= b;
+ return b;
+}
+
+sal_Int16 ScSpreadsheetSettings::getPropertyInt16(const OUString& aPropertyName)
+{
+ uno::Any any = getPropertyValue(aPropertyName);
+ sal_Int16 b = 0;
+ any >>= b;
+ return b;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSpreadsheetSettings::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScSpreadsheetSettings::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ ScModule* pScMod = SC_MOD();
+ ScAppOptions aAppOpt(pScMod->GetAppOptions());
+ ScInputOptions aInpOpt(pScMod->GetInputOptions());
+ bool bSaveApp = false;
+ bool bSaveInp = false;
+ // print options aren't loaded until needed
+
+ if (aPropertyName == SC_UNONAME_DOAUTOCP)
+ {
+ aAppOpt.SetAutoComplete( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveApp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_ENTERED)
+ {
+ aInpOpt.SetEnterEdit( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_EXPREF)
+ {
+ aInpOpt.SetExpandRefs( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_EXTFMT)
+ {
+ aInpOpt.SetExtendFormat( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_LINKUPD)
+ {
+ // XXX NOTE: this is not css::document::Settings property
+ // LinkUpdateMode but css::sheet::XGlobalSheetSettings attribute
+ // LinkUpdateMode.
+ sal_Int16 n;
+ if (!(aValue >>= n) || n < 0 || n >= ScLkUpdMode::LM_UNKNOWN)
+ {
+ throw css::lang::IllegalArgumentException(
+ ("LinkUpdateMode property value must be a SHORT with a value in the range of 0--2"
+ " as documented for css::sheet::XGlobalSheetSettings attribute LinkUpdateMode"),
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ }
+ aAppOpt.SetLinkMode( static_cast<ScLkUpdMode>(n) );
+ bSaveApp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_MARKHDR)
+ {
+ aInpOpt.SetMarkHeader( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_MOVESEL)
+ {
+ aInpOpt.SetMoveSelection( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_RANGEFIN)
+ {
+ aInpOpt.SetRangeFinder( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_USETABCOL)
+ {
+ aInpOpt.SetUseTabCol( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_PRMETRICS)
+ {
+ aInpOpt.SetTextWysiwyg( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_REPLWARN)
+ {
+ aInpOpt.SetReplaceCellsWarn( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_METRIC)
+ {
+ aAppOpt.SetAppMetric( static_cast<FieldUnit>(ScUnoHelpFunctions::GetInt16FromAny( aValue )) );
+ bSaveApp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_MOVEDIR)
+ {
+ aInpOpt.SetMoveDir( ScUnoHelpFunctions::GetInt16FromAny( aValue ) );
+ bSaveInp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_SCALE)
+ {
+ short nVal = ScUnoHelpFunctions::GetInt16FromAny( aValue );
+ if ( nVal < 0 )
+ {
+ SvxZoomType eType = SvxZoomType::PERCENT;
+ switch (nVal)
+ {
+ case SC_ZOOMVAL_OPTIMAL: eType = SvxZoomType::OPTIMAL; break;
+ case SC_ZOOMVAL_WHOLEPAGE: eType = SvxZoomType::WHOLEPAGE; break;
+ case SC_ZOOMVAL_PAGEWIDTH: eType = SvxZoomType::PAGEWIDTH; break;
+ }
+ aAppOpt.SetZoomType( eType );
+ }
+ else if ( nVal >= MINZOOM && nVal <= MAXZOOM )
+ {
+ aAppOpt.SetZoom( nVal );
+ aAppOpt.SetZoomType( SvxZoomType::PERCENT );
+ }
+ bSaveApp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_STBFUNC)
+ {
+ aAppOpt.SetStatusFunc( ScUnoHelpFunctions::GetInt16FromAny( aValue ) );
+ bSaveApp = true;
+ }
+ else if (aPropertyName == SC_UNONAME_ULISTS)
+ {
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ uno::Sequence<OUString> aSeq;
+ if ( pUserList && ( aValue >>= aSeq ) )
+ {
+ // directly change the active list
+ // ScGlobal::SetUseTabCol does not do much else
+
+ pUserList->clear();
+ for (const OUString& aEntry : std::as_const(aSeq))
+ {
+ pUserList->emplace_back(aEntry);
+ }
+ bSaveApp = true; // List with App-Options are saved
+ }
+ }
+ else if (aPropertyName == SC_UNONAME_PRALLSH)
+ {
+ ScPrintOptions aPrintOpt(pScMod->GetPrintOptions());
+ aPrintOpt.SetAllSheets( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ pScMod->SetPrintOptions( aPrintOpt );
+ }
+ else if (aPropertyName == SC_UNONAME_PREMPTY)
+ {
+ ScPrintOptions aPrintOpt(pScMod->GetPrintOptions());
+ aPrintOpt.SetSkipEmpty( !ScUnoHelpFunctions::GetBoolFromAny( aValue ) ); // reversed
+ pScMod->SetPrintOptions( aPrintOpt );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScPrintOptions ) ); // update previews
+ }
+
+ if ( bSaveApp )
+ pScMod->SetAppOptions( aAppOpt );
+ if ( bSaveInp )
+ pScMod->SetInputOptions( aInpOpt );
+}
+
+uno::Any SAL_CALL ScSpreadsheetSettings::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ ScModule* pScMod = SC_MOD();
+ ScAppOptions aAppOpt = pScMod->GetAppOptions();
+ const ScInputOptions& aInpOpt = pScMod->GetInputOptions();
+ // print options aren't loaded until needed
+
+ if (aPropertyName == SC_UNONAME_DOAUTOCP) aRet <<= aAppOpt.GetAutoComplete();
+ else if (aPropertyName == SC_UNONAME_ENTERED ) aRet <<= aInpOpt.GetEnterEdit();
+ else if (aPropertyName == SC_UNONAME_EXPREF ) aRet <<= aInpOpt.GetExpandRefs();
+ else if (aPropertyName == SC_UNONAME_EXTFMT ) aRet <<= aInpOpt.GetExtendFormat();
+ else if (aPropertyName == SC_UNONAME_LINKUPD ) aRet <<= static_cast<sal_Int16>(aAppOpt.GetLinkMode());
+ else if (aPropertyName == SC_UNONAME_MARKHDR ) aRet <<= aInpOpt.GetMarkHeader();
+ else if (aPropertyName == SC_UNONAME_MOVESEL ) aRet <<= aInpOpt.GetMoveSelection();
+ else if (aPropertyName == SC_UNONAME_RANGEFIN ) aRet <<= aInpOpt.GetRangeFinder();
+ else if (aPropertyName == SC_UNONAME_USETABCOL ) aRet <<= aInpOpt.GetUseTabCol();
+ else if (aPropertyName == SC_UNONAME_PRMETRICS ) aRet <<= aInpOpt.GetTextWysiwyg();
+ else if (aPropertyName == SC_UNONAME_REPLWARN ) aRet <<= aInpOpt.GetReplaceCellsWarn();
+ else if (aPropertyName == SC_UNONAME_METRIC ) aRet <<= static_cast<sal_Int16>(aAppOpt.GetAppMetric());
+ else if (aPropertyName == SC_UNONAME_MOVEDIR ) aRet <<= static_cast<sal_Int16>(aInpOpt.GetMoveDir());
+ else if (aPropertyName == SC_UNONAME_STBFUNC ) aRet <<= static_cast<sal_Int16>(aAppOpt.GetStatusFunc());
+ else if (aPropertyName == SC_UNONAME_SCALE )
+ {
+ sal_Int16 nZoomVal = 0;
+ switch ( aAppOpt.GetZoomType() )
+ {
+ case SvxZoomType::PERCENT: nZoomVal = aAppOpt.GetZoom(); break;
+ case SvxZoomType::OPTIMAL: nZoomVal = SC_ZOOMVAL_OPTIMAL; break;
+ case SvxZoomType::WHOLEPAGE: nZoomVal = SC_ZOOMVAL_WHOLEPAGE; break;
+ case SvxZoomType::PAGEWIDTH: nZoomVal = SC_ZOOMVAL_PAGEWIDTH; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ aRet <<= nZoomVal;
+ }
+ else if (aPropertyName == SC_UNONAME_ULISTS )
+ {
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (pUserList)
+ {
+ size_t nCount = pUserList->size();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ OUString aEntry((*pUserList)[i].GetString());
+ pAry[i] = aEntry;
+ }
+ aRet <<= aSeq;
+ }
+ }
+ else if (aPropertyName == SC_UNONAME_PRALLSH )
+ aRet <<= pScMod->GetPrintOptions().GetAllSheets();
+ else if (aPropertyName == SC_UNONAME_PREMPTY )
+ aRet <<= !pScMod->GetPrintOptions().GetSkipEmpty(); // reversed
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSpreadsheetSettings )
+
+ScRecentFunctionsObj::ScRecentFunctionsObj()
+{
+}
+
+ScRecentFunctionsObj::~ScRecentFunctionsObj()
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ScRecentFunctionsObj_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ return cppu::acquire(new ScRecentFunctionsObj());
+}
+
+// XRecentFunctions
+
+uno::Sequence<sal_Int32> SAL_CALL ScRecentFunctionsObj::getRecentFunctionIds()
+{
+ SolarMutexGuard aGuard;
+ const ScAppOptions& rOpt = SC_MOD()->GetAppOptions();
+ sal_uInt16 nCount = rOpt.GetLRUFuncListCount();
+ const sal_uInt16* pFuncs = rOpt.GetLRUFuncList();
+ if (pFuncs)
+ {
+ uno::Sequence<sal_Int32> aSeq(nCount);
+ sal_Int32* pAry = aSeq.getArray();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ pAry[i] = pFuncs[i];
+ return aSeq;
+ }
+ return {};
+}
+
+void SAL_CALL ScRecentFunctionsObj::setRecentFunctionIds(
+ const uno::Sequence<sal_Int32>& aRecentFunctionIds )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nCount = static_cast<sal_uInt16>(std::min( aRecentFunctionIds.getLength(), sal_Int32(LRU_MAX) ));
+ const sal_Int32* pAry = aRecentFunctionIds.getConstArray();
+
+ std::unique_ptr<sal_uInt16[]> pFuncs(nCount ? new sal_uInt16[nCount] : nullptr);
+ for (sal_uInt16 i=0; i<nCount; i++)
+ pFuncs[i] = static_cast<sal_uInt16>(pAry[i]); //! check for valid values?
+
+ ScModule* pScMod = SC_MOD();
+ ScAppOptions aNewOpts(pScMod->GetAppOptions());
+ aNewOpts.SetLRUFuncList(pFuncs.get(), nCount);
+ pScMod->SetAppOptions(aNewOpts);
+}
+
+sal_Int32 SAL_CALL ScRecentFunctionsObj::getMaxRecentFunctions()
+{
+ return LRU_MAX;
+}
+
+ScFunctionListObj::ScFunctionListObj()
+{
+}
+
+ScFunctionListObj::~ScFunctionListObj()
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ScFunctionListObj_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ return cppu::acquire(new ScFunctionListObj());
+}
+
+static void lcl_FillSequence( uno::Sequence<beans::PropertyValue>& rSequence, const ScFuncDesc& rDesc )
+{
+ rDesc.initArgumentInfo(); // full argument info is needed
+
+ OSL_ENSURE( rSequence.getLength() == SC_FUNCDESC_PROPCOUNT, "Wrong count" );
+
+ beans::PropertyValue* pArray = rSequence.getArray();
+
+ pArray[0].Name = SC_UNONAME_ID;
+ pArray[0].Value <<= static_cast<sal_Int32>(rDesc.nFIndex);
+
+ pArray[1].Name = SC_UNONAME_CATEGORY;
+ pArray[1].Value <<= static_cast<sal_Int32>(rDesc.nCategory);
+
+ pArray[2].Name = SC_UNONAME_NAME;
+ if (rDesc.mxFuncName)
+ pArray[2].Value <<= *rDesc.mxFuncName;
+
+ pArray[3].Name = SC_UNONAME_DESCRIPTION;
+ if (rDesc.mxFuncDesc)
+ pArray[3].Value <<= *rDesc.mxFuncDesc;
+
+ pArray[4].Name = SC_UNONAME_ARGUMENTS;
+ if (rDesc.maDefArgNames.empty() || rDesc.maDefArgDescs.empty() || !rDesc.pDefArgFlags)
+ return;
+
+ sal_uInt16 nCount = rDesc.nArgCount;
+ if (nCount >= PAIRED_VAR_ARGS)
+ nCount -= PAIRED_VAR_ARGS - 2;
+ else if (nCount >= VAR_ARGS)
+ nCount -= VAR_ARGS - 1;
+ sal_uInt16 nSeqCount = rDesc.GetSuppressedArgCount();
+ if (nSeqCount >= PAIRED_VAR_ARGS)
+ nSeqCount -= PAIRED_VAR_ARGS - 2;
+ else if (nSeqCount >= VAR_ARGS)
+ nSeqCount -= VAR_ARGS - 1;
+
+ if (!nSeqCount)
+ return;
+
+ uno::Sequence<sheet::FunctionArgument> aArgSeq(nSeqCount);
+ sheet::FunctionArgument* pArgAry = aArgSeq.getArray();
+ for (sal_uInt16 i=0, j=0; i<nCount; i++)
+ {
+ sheet::FunctionArgument aArgument;
+ aArgument.Name = rDesc.maDefArgNames[i];
+ aArgument.Description = rDesc.maDefArgDescs[i];
+ aArgument.IsOptional = rDesc.pDefArgFlags[i].bOptional;
+ pArgAry[j++] = aArgument;
+ }
+ pArray[4].Value <<= aArgSeq;
+}
+
+// XFunctionDescriptions
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScFunctionListObj::getById( sal_Int32 nId )
+{
+ SolarMutexGuard aGuard;
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( !pFuncList )
+ throw uno::RuntimeException(); // should not happen
+
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pFuncList->GetCount());
+ for (sal_uInt16 nIndex=0; nIndex<nCount; nIndex++)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex);
+ if ( pDesc && pDesc->nFIndex == nId )
+ {
+ uno::Sequence<beans::PropertyValue> aSeq( SC_FUNCDESC_PROPCOUNT );
+ lcl_FillSequence( aSeq, *pDesc );
+ return aSeq;
+ }
+ }
+
+ throw lang::IllegalArgumentException(); // not found
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL ScFunctionListObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( !pFuncList )
+ throw uno::RuntimeException(); // should not happen
+
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pFuncList->GetCount());
+ for (sal_uInt16 nIndex=0; nIndex<nCount; nIndex++)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex);
+ //! Case-insensitive???
+ if ( pDesc && pDesc->mxFuncName && aName == *pDesc->mxFuncName )
+ {
+ uno::Sequence<beans::PropertyValue> aSeq( SC_FUNCDESC_PROPCOUNT );
+ lcl_FillSequence( aSeq, *pDesc );
+ return uno::Any(aSeq);
+ }
+ }
+
+ throw container::NoSuchElementException(); // not found
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScFunctionListObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nCount = 0;
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( pFuncList )
+ nCount = static_cast<sal_Int32>(pFuncList->GetCount());
+ return nCount;
+}
+
+uno::Any SAL_CALL ScFunctionListObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( !pFuncList )
+ throw uno::RuntimeException(); // should not happen
+
+ if ( nIndex >= 0 && o3tl::make_unsigned(nIndex) < pFuncList->GetCount() )
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex);
+ if ( pDesc )
+ {
+ uno::Sequence<beans::PropertyValue> aSeq( SC_FUNCDESC_PROPCOUNT );
+ lcl_FillSequence( aSeq, *pDesc );
+ return uno::Any(aSeq);
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException(); // illegal index
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScFunctionListObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.FunctionDescriptionEnumeration");
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScFunctionListObj::getElementType()
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL ScFunctionListObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() > 0 );
+}
+
+uno::Sequence<OUString> SAL_CALL ScFunctionListObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( pFuncList )
+ {
+ sal_uInt32 nCount = pFuncList->GetCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+ for (sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex);
+ if ( pDesc && pDesc->mxFuncName )
+ pAry[nIndex] = *pDesc->mxFuncName;
+ }
+ return aSeq;
+ }
+ return {};
+}
+
+sal_Bool SAL_CALL ScFunctionListObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( pFuncList )
+ {
+ sal_uInt32 nCount = pFuncList->GetCount();
+ for (sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction(nIndex);
+ //! Case-insensitive???
+ if ( pDesc && pDesc->mxFuncName && aName == *pDesc->mxFuncName )
+ return true;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/celllistsource.cxx b/sc/source/ui/unoobj/celllistsource.cxx
new file mode 100644
index 0000000000..bca1e0a695
--- /dev/null
+++ b/sc/source/ui/unoobj/celllistsource.cxx
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "celllistsource.hxx"
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/FormulaResult.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace calc
+{
+
+#define PROP_HANDLE_RANGE_ADDRESS 1
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::table;
+ using namespace ::com::sun::star::text;
+ using namespace ::com::sun::star::sheet;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::binding;
+
+ OCellListSource::OCellListSource( const Reference< XSpreadsheetDocument >& _rxDocument )
+ :OCellListSource_Base( m_aMutex )
+ ,OCellListSource_PBase( OCellListSource_Base::rBHelper )
+ ,m_xDocument( _rxDocument )
+ ,m_aListEntryListeners( m_aMutex )
+ ,m_bInitialized( false )
+ {
+ OSL_PRECOND( m_xDocument.is(), "OCellListSource::OCellListSource: invalid document!" );
+
+ // register our property at the base class
+ registerPropertyNoMember(
+ "CellRange",
+ PROP_HANDLE_RANGE_ADDRESS,
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY,
+ cppu::UnoType<CellRangeAddress>::get(),
+ css::uno::Any(CellRangeAddress())
+ );
+ }
+
+ OCellListSource::~OCellListSource( )
+ {
+ if ( !OCellListSource_Base::rBHelper.bDisposed )
+ {
+ acquire(); // prevent duplicate dtor
+ dispose();
+ }
+ }
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OCellListSource, OCellListSource_Base, OCellListSource_PBase )
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellListSource, OCellListSource_Base, OCellListSource_PBase )
+
+ void SAL_CALL OCellListSource::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->removeModifyListener( this );
+ }
+
+ EventObject aDisposeEvent( *this );
+ m_aListEntryListeners.disposeAndClear( aDisposeEvent );
+
+ WeakComponentImplHelperBase::disposing();
+
+ // TODO: clean up here whatever you need to clean up (e.g. revoking listeners etc.)
+ }
+
+ Reference< XPropertySetInfo > SAL_CALL OCellListSource::getPropertySetInfo( )
+ {
+ return createPropertySetInfo( getInfoHelper() ) ;
+ }
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL OCellListSource::getInfoHelper()
+ {
+ return *OCellListSource_PABase::getArrayHelper();
+ }
+
+ ::cppu::IPropertyArrayHelper* OCellListSource::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper(aProps);
+ }
+
+ void SAL_CALL OCellListSource::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ OSL_ENSURE( _nHandle == PROP_HANDLE_RANGE_ADDRESS, "OCellListSource::getFastPropertyValue: invalid handle!" );
+ // we only have this one property...
+
+ _rValue <<= getRangeAddress( );
+ }
+
+ void OCellListSource::checkDisposed( ) const
+ {
+ if ( OCellListSource_Base::rBHelper.bInDispose || OCellListSource_Base::rBHelper.bDisposed )
+ throw DisposedException();
+ // TODO: is it worth having an error message here?
+ }
+
+ void OCellListSource::checkInitialized()
+ {
+ if ( !m_bInitialized )
+ throw NotInitializedException("CellListSource is not initialized", getXWeak());
+ }
+
+ OUString SAL_CALL OCellListSource::getImplementationName( )
+ {
+ return "com.sun.star.comp.sheet.OCellListSource";
+ }
+
+ sal_Bool SAL_CALL OCellListSource::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL OCellListSource::getSupportedServiceNames( )
+ {
+ return {"com.sun.star.table.CellRangeListSource",
+ "com.sun.star.form.binding.ListEntrySource"};
+ }
+
+ CellRangeAddress OCellListSource::getRangeAddress( ) const
+ {
+ OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" );
+
+ CellRangeAddress aAddress;
+ Reference< XCellRangeAddressable > xRangeAddress( m_xRange, UNO_QUERY );
+ if ( xRangeAddress.is() )
+ aAddress = xRangeAddress->getRangeAddress( );
+ return aAddress;
+ }
+
+ OUString OCellListSource::getCellTextContent_noCheck( sal_Int32 _nRangeRelativeRow, css::uno::Any* pAny )
+ {
+ OUString sText;
+
+ OSL_PRECOND( m_xRange.is(), "OCellListSource::getRangeAddress: invalid range!" );
+
+ if (!m_xRange.is())
+ return sText;
+
+ Reference< XCell > xCell( m_xRange->getCellByPosition( 0, _nRangeRelativeRow ));
+ if (!xCell.is())
+ {
+ if (pAny)
+ *pAny <<= sText;
+ return sText;
+ }
+
+ Reference< XTextRange > xCellText;
+ xCellText.set( xCell, UNO_QUERY);
+
+ if (xCellText.is())
+ sText = xCellText->getString(); // formatted output string
+
+ if (pAny)
+ {
+ switch (xCell->getType())
+ {
+ case CellContentType_VALUE:
+ *pAny <<= xCell->getValue();
+ break;
+ case CellContentType_TEXT:
+ *pAny <<= sText;
+ break;
+ case CellContentType_FORMULA:
+ if (xCell->getError())
+ *pAny <<= sText; // Err:... or #...!
+ else
+ {
+ Reference< XPropertySet > xProp( xCell, UNO_QUERY);
+ if (xProp.is())
+ {
+ sal_Int32 nResultType;
+ if ((xProp->getPropertyValue("FormulaResultType2") >>= nResultType) &&
+ nResultType == FormulaResult::VALUE)
+ *pAny <<= xCell->getValue();
+ else
+ *pAny <<= sText;
+ }
+ }
+ break;
+ case CellContentType_EMPTY:
+ *pAny <<= OUString();
+ break;
+ default:
+ ; // nothing, if actually occurred it would result in #N/A being displayed if selected
+ }
+ }
+
+ return sText;
+ }
+
+ sal_Int32 SAL_CALL OCellListSource::getListEntryCount( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed();
+ checkInitialized();
+
+ CellRangeAddress aAddress( getRangeAddress( ) );
+ return aAddress.EndRow - aAddress.StartRow + 1;
+ }
+
+ OUString SAL_CALL OCellListSource::getListEntry( sal_Int32 _nPosition )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed();
+ checkInitialized();
+
+ if ( _nPosition >= getListEntryCount() )
+ throw IndexOutOfBoundsException();
+
+ return getCellTextContent_noCheck( _nPosition, nullptr );
+ }
+
+ Sequence< OUString > SAL_CALL OCellListSource::getAllListEntries( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed();
+ checkInitialized();
+
+ Sequence< OUString > aAllEntries( getListEntryCount() );
+ OUString* pAllEntries = aAllEntries.getArray();
+ for ( sal_Int32 i = 0; i < aAllEntries.getLength(); ++i )
+ {
+ *pAllEntries++ = getCellTextContent_noCheck( i, nullptr );
+ }
+
+ return aAllEntries;
+ }
+
+ Sequence< OUString > SAL_CALL OCellListSource::getAllListEntriesTyped( Sequence< Any >& rDataValues )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed();
+ checkInitialized();
+
+ const sal_Int32 nCount = getListEntryCount();
+ Sequence< OUString > aAllEntries( nCount );
+ rDataValues = Sequence< Any >( nCount );
+ OUString* pAllEntries = aAllEntries.getArray();
+ Any* pDataValues = rDataValues.getArray();
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ *pAllEntries++ = getCellTextContent_noCheck( i, pDataValues++ );
+ }
+
+ return aAllEntries;
+ }
+
+ void SAL_CALL OCellListSource::addListEntryListener( const Reference< XListEntryListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed();
+ checkInitialized();
+
+ if ( !_rxListener.is() )
+ throw NullPointerException();
+
+ m_aListEntryListeners.addInterface( _rxListener );
+ }
+
+ void SAL_CALL OCellListSource::removeListEntryListener( const Reference< XListEntryListener >& _rxListener )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ checkDisposed();
+ checkInitialized();
+
+ if ( !_rxListener.is() )
+ throw NullPointerException();
+
+ m_aListEntryListeners.removeInterface( _rxListener );
+ }
+
+ void SAL_CALL OCellListSource::modified( const EventObject& /* aEvent */ )
+ {
+ notifyModified();
+ }
+
+ void OCellListSource::notifyModified()
+ {
+ EventObject aEvent;
+ aEvent.Source.set(*this);
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aListEntryListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->allEntriesChanged( aEvent );
+ }
+ catch( const RuntimeException& )
+ {
+ // silent this
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::notifyModified: caught a (non-runtime) exception!" );
+ }
+ }
+
+ }
+
+ void SAL_CALL OCellListSource::disposing( const EventObject& aEvent )
+ {
+ Reference<XInterface> xRangeInt( m_xRange, UNO_QUERY );
+ if ( xRangeInt == aEvent.Source )
+ {
+ // release references to range object
+ m_xRange.clear();
+ }
+ }
+
+ void SAL_CALL OCellListSource::initialize( const Sequence< Any >& _rArguments )
+ {
+ if ( m_bInitialized )
+ throw RuntimeException("CellListSource is already initialized", getXWeak());
+
+ // get the cell address
+ CellRangeAddress aRangeAddress;
+ bool bFoundAddress = false;
+
+ for ( const Any& rArg : _rArguments )
+ {
+ NamedValue aValue;
+ if ( rArg >>= aValue )
+ {
+ if ( aValue.Name == "CellRange" )
+ {
+ if ( aValue.Value >>= aRangeAddress )
+ {
+ bFoundAddress = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !bFoundAddress )
+ throw RuntimeException("Cell not found", getXWeak());
+
+ // determine the range we're bound to
+ try
+ {
+ if ( m_xDocument.is() )
+ {
+ // first the sheets collection
+ Reference< XIndexAccess > xSheets(m_xDocument->getSheets( ), UNO_QUERY);
+ OSL_ENSURE( xSheets.is(), "OCellListSource::initialize: could not retrieve the sheets!" );
+
+ if ( xSheets.is() )
+ {
+ // the concrete sheet
+ Reference< XCellRange > xSheet(xSheets->getByIndex( aRangeAddress.Sheet ), UNO_QUERY);
+ OSL_ENSURE( xSheet.is(), "OCellListSource::initialize: NULL sheet, but no exception!" );
+
+ // the concrete cell
+ if ( xSheet.is() )
+ {
+ m_xRange.set(xSheet->getCellRangeByPosition(
+ aRangeAddress.StartColumn, aRangeAddress.StartRow,
+ aRangeAddress.EndColumn, aRangeAddress.EndRow));
+ OSL_ENSURE( Reference< XCellRangeAddressable >( m_xRange, UNO_QUERY ).is(), "OCellListSource::initialize: either NULL range, or cell without address access!" );
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "OCellListSource::initialize: caught an exception while retrieving the cell object!" );
+ }
+
+ if ( !m_xRange.is() )
+ throw RuntimeException("Failed to retrieve cell range", getXWeak());
+
+ Reference<XModifyBroadcaster> xBroadcaster( m_xRange, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->addModifyListener( this );
+ }
+
+ // TODO: add as XEventListener to the cell range, so we get notified when it dies,
+ // and can dispose ourself then
+
+ // TODO: somehow add as listener so we get notified when the address of the cell range changes
+ // We need to forward this as change in our CellRange property to our property change listeners
+
+ // TODO: somehow add as listener to the cells in the range, so that we get notified
+ // when their content changes. We need to forward this to our list entry listeners then
+
+ // TODO: somehow add as listener so that we get notified of insertions and removals of rows in our
+ // range. In this case, we need to fire a change in our CellRange property, and additionally
+ // notify our XListEntryListeners
+
+ m_bInitialized = true;
+ }
+
+} // namespace calc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/celllistsource.hxx b/sc/source/ui/unoobj/celllistsource.hxx
new file mode 100644
index 0000000000..c919c18ec2
--- /dev/null
+++ b/sc/source/ui/unoobj/celllistsource.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/form/binding/XListEntryTypedSource.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/uno3.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+namespace com::sun::star::table { class XCellRange; }
+
+namespace calc
+{
+
+ //= OCellListSource
+
+ class OCellListSource;
+ // the base for our interfaces
+ typedef ::cppu::WeakComponentImplHelper < css::form::binding::XListEntryTypedSource
+ , css::util::XModifyListener
+ , css::lang::XServiceInfo
+ , css::lang::XInitialization
+ > OCellListSource_Base;
+ // the base for the property handling
+ typedef ::comphelper::OPropertyContainer OCellListSource_PBase;
+ // the second base for property handling
+ typedef ::comphelper::OPropertyArrayUsageHelper< OCellListSource >
+ OCellListSource_PABase;
+
+ class OCellListSource :public ::cppu::BaseMutex
+ ,public OCellListSource_Base // order matters! before OCellListSource_PBase, so rBHelper gets initialized
+ ,public OCellListSource_PBase
+ ,public OCellListSource_PABase
+ {
+ private:
+ css::uno::Reference< css::sheet::XSpreadsheetDocument >
+ m_xDocument; /// the document where our cell lives
+ css::uno::Reference< css::table::XCellRange >
+ m_xRange; /// the range of cells we're bound to
+ ::comphelper::OInterfaceContainerHelper3<css::form::binding::XListEntryListener>
+ m_aListEntryListeners; /// our listeners
+ bool m_bInitialized; /// has XInitialization::initialize been called?
+
+ public:
+ explicit OCellListSource(
+ const css::uno::Reference< css::sheet::XSpreadsheetDocument >& _rxDocument
+ );
+
+ using OCellListSource_PBase::getFastPropertyValue;
+
+ protected:
+ virtual ~OCellListSource( ) override;
+
+ protected:
+ // XInterface
+ DECLARE_XINTERFACE()
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XListEntrySource
+ virtual sal_Int32 SAL_CALL getListEntryCount( ) override;
+ virtual OUString SAL_CALL getListEntry( sal_Int32 Position ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getAllListEntries( ) override;
+ virtual void SAL_CALL addListEntryListener( const css::uno::Reference< css::form::binding::XListEntryListener >& Listener ) override;
+ virtual void SAL_CALL removeListEntryListener( const css::uno::Reference< css::form::binding::XListEntryListener >& Listener ) override;
+
+ // XListEntryTypedSource
+ virtual css::uno::Sequence< OUString > SAL_CALL getAllListEntriesTyped( css::uno::Sequence< css::uno::Any >& rDataValues ) override;
+
+ // OComponentHelper/XComponent
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+ // OPropertySetHelper
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override;
+
+ // ::comphelper::OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ private:
+ void checkDisposed( ) const;
+ void checkInitialized();
+
+ /** retrieves the actual address of our cell range
+ @precond
+ our m_xRange is not <NULL/>
+ */
+ css::table::CellRangeAddress
+ getRangeAddress( ) const;
+
+ /** retrieves the text of a cell within our range
+ @param _nRangeRelativeRow
+ the relative row index of the cell within our range
+ @param pAny
+ if not <NULL/> then the underlying data value is returned in the Any
+ @precond
+ our m_xRange is not <NULL/>
+ */
+ OUString
+ getCellTextContent_noCheck(
+ sal_Int32 _nRangeRelativeRow,
+ css::uno::Any* pAny
+ );
+
+ void notifyModified();
+
+ private:
+ OCellListSource( const OCellListSource& ) = delete;
+ OCellListSource& operator=( const OCellListSource& ) = delete;
+ };
+
+} // namespace calc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/cellsuno.cxx b/sc/source/ui/unoobj/cellsuno.cxx
new file mode 100644
index 0000000000..3325b0bbcf
--- /dev/null
+++ b/sc/source/ui/unoobj/cellsuno.cxx
@@ -0,0 +1,9266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/svdpool.hxx>
+
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/unoipset.hxx>
+#include <editeng/langitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <svl/numformat.hxx>
+#include <svl/srchitem.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <svx/unomid.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/unotext.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <float.h>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/TableBorder2.hpp>
+#include <com/sun/star/sheet/CellFlags.hpp>
+#include <com/sun/star/sheet/FormulaResult.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/beans/TolerantPropertySetResultType.hpp>
+#include <com/sun/star/beans/SetPropertyTolerantFailed.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/text/textfield/Type.hpp>
+#include <com/sun/star/sheet/XConditionalFormats.hpp>
+#include <com/sun/star/util/XComplexColor.hpp>
+
+#include <autoform.hxx>
+#include <cellvalue.hxx>
+#include <cellmergeoption.hxx>
+#include <cellsuno.hxx>
+#include <cursuno.hxx>
+#include <textuno.hxx>
+#include <editsrc.hxx>
+#include <notesuno.hxx>
+#include <fielduno.hxx>
+#include <docuno.hxx>
+#include <datauno.hxx>
+#include <dapiuno.hxx>
+#include <chartuno.hxx>
+#include <fmtuno.hxx>
+#include <miscuno.hxx>
+#include <convuno.hxx>
+#include <srchuno.hxx>
+#include <nameuno.hxx>
+#include <targuno.hxx>
+#include <tokenuno.hxx>
+#include <eventuno.hxx>
+#include <docsh.hxx>
+#include <markdata.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <docfunc.hxx>
+#include <dbdocfun.hxx>
+#include <olinefun.hxx>
+#include <hints.hxx>
+#include <formulacell.hxx>
+#include <undotab.hxx>
+#include <undoblk.hxx>
+#include <stlsheet.hxx>
+#include <dbdata.hxx>
+#include <attrib.hxx>
+#include <chartarr.hxx>
+#include <chartlis.hxx>
+#include <drwlayer.hxx>
+#include <printfun.hxx>
+#include <prnsave.hxx>
+#include <tablink.hxx>
+#include <dociter.hxx>
+#include <rangeutl.hxx>
+#include <conditio.hxx>
+#include <validat.hxx>
+#include <sc.hrc>
+#include <cellform.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <unonames.hxx>
+#include <styleuno.hxx>
+#include <rangeseq.hxx>
+#include <unowids.hxx>
+#include <paramisc.hxx>
+#include <queryentry.hxx>
+#include <formula/errorcodes.hxx>
+#include <unoreflist.hxx>
+#include <formula/grammar.hxx>
+#include <editeng/escapementitem.hxx>
+#include <stringutil.hxx>
+#include <formulaiter.hxx>
+#include <tokenarray.hxx>
+#include <stylehelper.hxx>
+#include <dputil.hxx>
+#include <sortparam.hxx>
+#include <condformatuno.hxx>
+#include <TablePivotCharts.hxx>
+#include <table.hxx>
+#include <refundo.hxx>
+#include <columnspanset.hxx>
+#include <CommonProperties.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+
+// The names in the maps must be sorted according to strcmp!
+//! Instead of Which-ID 0 use special IDs and do not compare via names!
+
+// Left/Right/Top/BottomBorder are mapped directly to the core items,
+// not collected/applied to the borders of a range -> ATTR_BORDER can be used directly
+
+static const SfxItemPropertySet* lcl_GetCellsPropertySet()
+{
+ static const SfxItemPropertyMapEntry aCellsPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNONAME_HYPERLINK, ATTR_HYPERLINK, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_FORMATID, SC_WID_UNO_FORMATID, cppu::UnoType<sal_uInt64>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aCellsPropertySet( aCellsPropertyMap_Impl );
+ return &aCellsPropertySet;
+}
+
+// CellRange contains all entries from Cells, plus its own entries
+// with Which-ID 0 (those are needed only for getPropertySetInfo).
+
+static const SfxItemPropertySet* lcl_GetRangePropertySet()
+{
+ static const SfxItemPropertyMapEntry aRangePropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNONAME_FORMATID, SC_WID_UNO_FORMATID, cppu::UnoType<sal_uInt64>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aRangePropertySet( aRangePropertyMap_Impl );
+ return &aRangePropertySet;
+}
+
+// Cell contains entries from CellRange, plus its own entries
+// with Which-ID 0 (those are needed only for getPropertySetInfo).
+
+static const SfxItemPropertySet* lcl_GetCellPropertySet()
+{
+ static const SfxItemPropertyMapEntry aCellPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_FORMLOC, SC_WID_UNO_FORMLOC, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_FORMRT, SC_WID_UNO_FORMRT, cppu::UnoType<table::CellContentType>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_CELLCONTENTTYPE, SC_WID_UNO_CELLCONTENTTYPE, cppu::UnoType<table::CellContentType>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_FORMRT2, SC_WID_UNO_FORMRT2, cppu::UnoType<sal_Int32>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_ESCAPEMENT, EE_CHAR_ESCAPEMENT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_HYPERLINK, ATTR_HYPERLINK, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_FORMATID, SC_WID_UNO_FORMATID, cppu::UnoType<sal_uInt64>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aCellPropertySet( aCellPropertyMap_Impl );
+ return &aCellPropertySet;
+}
+
+// Column and Row contain all entries from CellRange, plus its own entries
+// with Which-ID 0 (those are needed only for getPropertySetInfo).
+
+static const SfxItemPropertySet* lcl_GetColumnPropertySet()
+{
+ static const SfxItemPropertyMapEntry aColumnPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_MANPAGE, SC_WID_UNO_MANPAGE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NEWPAGE, SC_WID_UNO_NEWPAGE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLVIS, SC_WID_UNO_CELLVIS, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ { SC_UNONAME_OWIDTH, SC_WID_UNO_OWIDTH, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLWID, SC_WID_UNO_CELLWID, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aColumnPropertySet( aColumnPropertyMap_Impl );
+ return &aColumnPropertySet;
+}
+
+static const SfxItemPropertySet* lcl_GetRowPropertySet()
+{
+ static const SfxItemPropertyMapEntry aRowPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_CELLHGT, SC_WID_UNO_CELLHGT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_CELLFILT, SC_WID_UNO_CELLFILT,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_MANPAGE, SC_WID_UNO_MANPAGE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NEWPAGE, SC_WID_UNO_NEWPAGE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLVIS, SC_WID_UNO_CELLVIS, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ { SC_UNONAME_OHEIGHT, SC_WID_UNO_OHEIGHT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aRowPropertySet( aRowPropertyMap_Impl );
+ return &aRowPropertySet;
+}
+
+static const SfxItemPropertySet* lcl_GetSheetPropertySet()
+{
+ static const SfxItemPropertyMapEntry aSheetPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ABSNAME, SC_WID_UNO_ABSNAME, cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_AUTOPRINT,SC_WID_UNO_AUTOPRINT,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_BORDCOL, SC_WID_UNO_BORDCOL, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ { SC_UNONAME_CELLPRO, ATTR_PROTECTION, cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ { SC_UNONAME_CELLSTYL, SC_WID_UNO_CELLSTYL,cppu::UnoType<OUString>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ { SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ { SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ { SC_UNONAME_CFONT, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_COVER, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CPOST, ATTR_FONT_POSTURE, cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ { SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ { SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ { SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ { SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ { SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ { SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ { SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHCOLHDR, SC_WID_UNO_CHCOLHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CHROWHDR, SC_WID_UNO_CHROWHDR,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CONDFMT, SC_WID_UNO_CONDFMT, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDLOC, SC_WID_UNO_CONDLOC, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_CONDXML, SC_WID_UNO_CONDXML, cppu::UnoType<sheet::XSheetConditionalEntries>::get(), 0, 0 },
+ { SC_UNONAME_COPYBACK, SC_WID_UNO_COPYBACK,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_COPYFORM, SC_WID_UNO_COPYFORM,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_COPYSTYL, SC_WID_UNO_COPYSTYL,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ { SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ISACTIVE, SC_WID_UNO_ISACTIVE,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ { SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLVIS, SC_WID_UNO_CELLVIS, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_LINKDISPBIT, SC_WID_UNO_LINKDISPBIT,cppu::UnoType<awt::XBitmap>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_LINKDISPNAME, SC_WID_UNO_LINKDISPNAME,cppu::UnoType<OUString>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ { SC_UNONAME_CELLORI, ATTR_STACKED, cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ { SC_UNONAME_PAGESTL, SC_WID_UNO_PAGESTL, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PBMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PINDENT, ATTR_INDENT, cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ { SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ { SC_UNONAME_PLMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PRMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_PTMARGIN, ATTR_MARGIN, cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ { SC_UNONAME_POS, SC_WID_UNO_POS, cppu::UnoType<awt::Point>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_PRINTBORD,SC_WID_UNO_PRINTBORD,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_PROTECT, SC_WID_UNO_PROTECT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHADOW, ATTR_SHADOW, cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_SHOWBORD, SC_WID_UNO_SHOWBORD,cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SIZE, SC_WID_UNO_SIZE, cppu::UnoType<awt::Size>::get(), 0 | beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TBLBORD2, SC_WID_UNO_TBLBORD2, cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ { SC_UNONAME_TABLAYOUT,SC_WID_UNO_TABLAYOUT,cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNONAME_CONDFORMAT, SC_WID_UNO_CONDFORMAT, cppu::UnoType<sheet::XConditionalFormats>::get(), 0, 0},
+ { SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ { SC_UNONAME_VALIDAT, SC_WID_UNO_VALIDAT, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALILOC, SC_WID_UNO_VALILOC, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_VALIXML, SC_WID_UNO_VALIXML, cppu::UnoType<beans::XPropertySet>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNONAME_TABCOLOR, SC_WID_UNO_TABCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNO_CODENAME, SC_WID_UNO_CODENAME, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNO_NAMEDRANGES, SC_WID_UNO_NAMES, cppu::UnoType<sheet::XNamedRanges>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aSheetPropertySet( aSheetPropertyMap_Impl );
+ return &aSheetPropertySet;
+}
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetEditPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aEditPropertyMap_Impl[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ SVX_UNOEDIT_NUMBERING_PROPERTY, // for completeness of service ParagraphProperties
+ { SC_UNONAME_TEXTUSER, EE_CHAR_XMLATTRIBS, cppu::UnoType<container::XNameContainer>::get(), 0, 0},
+ { SC_UNONAME_USERDEF, EE_PARA_XMLATTRIBS, cppu::UnoType<container::XNameContainer>::get(), 0, 0},
+ };
+ return aEditPropertyMap_Impl;
+}
+static const SvxItemPropertySet* lcl_GetEditPropertySet()
+{
+ static SvxItemPropertySet aEditPropertySet( lcl_GetEditPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool() );
+ return &aEditPropertySet;
+}
+
+constexpr OUString SCCHARPROPERTIES_SERVICE = u"com.sun.star.style.CharacterProperties"_ustr;
+constexpr OUString SCPARAPROPERTIES_SERVICE = u"com.sun.star.style.ParagraphProperties"_ustr;
+constexpr OUString SCCELLPROPERTIES_SERVICE = u"com.sun.star.table.CellProperties"_ustr;
+constexpr OUString SCCELLRANGE_SERVICE = u"com.sun.star.table.CellRange"_ustr;
+constexpr OUString SCCELL_SERVICE = u"com.sun.star.table.Cell"_ustr;
+constexpr OUString SCSHEETCELLRANGES_SERVICE = u"com.sun.star.sheet.SheetCellRanges"_ustr;
+constexpr OUString SCSHEETCELLRANGE_SERVICE = u"com.sun.star.sheet.SheetCellRange"_ustr;
+constexpr OUString SCSPREADSHEET_SERVICE = u"com.sun.star.sheet.Spreadsheet"_ustr;
+constexpr OUString SCSHEETCELL_SERVICE = u"com.sun.star.sheet.SheetCell"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScCellFormatsEnumeration, "ScCellFormatsEnumeration", "com.sun.star.sheet.CellFormatRangesEnumeration" )
+SC_SIMPLE_SERVICE_INFO( ScCellFormatsObj, "ScCellFormatsObj", "com.sun.star.sheet.CellFormatRanges" )
+SC_SIMPLE_SERVICE_INFO( ScUniqueCellFormatsEnumeration, "ScUniqueCellFormatsEnumeration", "com.sun.star.sheet.UniqueCellFormatRangesEnumeration" )
+SC_SIMPLE_SERVICE_INFO( ScUniqueCellFormatsObj, "ScUniqueCellFormatsObj", "com.sun.star.sheet.UniqueCellFormatRanges" )
+SC_SIMPLE_SERVICE_INFO( ScCellRangesBase, "ScCellRangesBase", "stardiv.unknown" )
+SC_SIMPLE_SERVICE_INFO( ScCellsEnumeration, "ScCellsEnumeration", "com.sun.star.sheet.CellsEnumeration" )
+SC_SIMPLE_SERVICE_INFO( ScCellsObj, "ScCellsObj", "com.sun.star.sheet.Cells" )
+SC_SIMPLE_SERVICE_INFO( ScTableColumnObj, "ScTableColumnObj", "com.sun.star.table.TableColumn" )
+SC_SIMPLE_SERVICE_INFO( ScTableRowObj, "ScTableRowObj", "com.sun.star.table.TableRow" )
+
+//! move ScLinkListener into another file !!!
+
+ScLinkListener::~ScLinkListener()
+{
+}
+
+void ScLinkListener::Notify( const SfxHint& rHint )
+{
+ aLink.Call( rHint );
+}
+
+static void lcl_CopyProperties( beans::XPropertySet& rDest, beans::XPropertySet& rSource )
+{
+ uno::Reference<beans::XPropertySetInfo> xInfo(rSource.getPropertySetInfo());
+ if (xInfo.is())
+ {
+ const uno::Sequence<beans::Property> aSeq(xInfo->getProperties());
+ for (const beans::Property& rProp : aSeq)
+ {
+ OUString aName(rProp.Name);
+ rDest.setPropertyValue( aName, rSource.getPropertyValue( aName ) );
+ }
+ }
+}
+
+static SCTAB lcl_FirstTab( const ScRangeList& rRanges )
+{
+ if (rRanges.empty())
+ throw std::out_of_range("empty range");
+ const ScRange & rFirst = rRanges[0];
+ return rFirst.aStart.Tab();
+}
+
+static bool lcl_WholeSheet( const ScDocument& rDoc, const ScRangeList& rRanges )
+{
+ if ( rRanges.size() == 1 )
+ {
+ const ScRange & rRange = rRanges[0];
+ if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
+ rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() )
+ return true;
+ }
+ return false;
+}
+
+namespace {
+template<typename BorderLineType>
+const ::editeng::SvxBorderLine* lcl_getBorderLine(
+ ::editeng::SvxBorderLine& rLine, const BorderLineType& rStruct )
+{
+ // Convert from 1/100mm to Twips.
+ if (!SvxBoxItem::LineToSvxLine( rStruct, rLine, true))
+ return nullptr;
+
+ if ( rLine.GetOutWidth() || rLine.GetInWidth() || rLine.GetDistance() )
+ return &rLine;
+ else
+ return nullptr;
+}
+}
+
+const ::editeng::SvxBorderLine* ScHelperFunctions::GetBorderLine(
+ ::editeng::SvxBorderLine& rLine, const table::BorderLine& rStruct )
+{
+ return lcl_getBorderLine( rLine, rStruct);
+}
+
+const ::editeng::SvxBorderLine* ScHelperFunctions::GetBorderLine(
+ ::editeng::SvxBorderLine& rLine, const table::BorderLine2& rStruct )
+{
+ return lcl_getBorderLine( rLine, rStruct);
+}
+
+namespace {
+template<typename TableBorderType>
+void lcl_fillBoxItems( SvxBoxItem& rOuter, SvxBoxInfoItem& rInner, const TableBorderType& rBorder )
+{
+ ::editeng::SvxBorderLine aLine;
+ rOuter.SetAllDistances(o3tl::toTwips(rBorder.Distance, o3tl::Length::mm100));
+ rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.TopLine ), SvxBoxItemLine::TOP );
+ rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.BottomLine ), SvxBoxItemLine::BOTTOM );
+ rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.LeftLine ), SvxBoxItemLine::LEFT );
+ rOuter.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.RightLine ), SvxBoxItemLine::RIGHT );
+ rInner.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.HorizontalLine ), SvxBoxInfoItemLine::HORI );
+ rInner.SetLine( ScHelperFunctions::GetBorderLine( aLine, rBorder.VerticalLine ), SvxBoxInfoItemLine::VERT );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::TOP, rBorder.IsTopLineValid );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, rBorder.IsBottomLineValid );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, rBorder.IsLeftLineValid );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, rBorder.IsRightLineValid );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::HORI, rBorder.IsHorizontalLineValid );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::VERT, rBorder.IsVerticalLineValid );
+ rInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, rBorder.IsDistanceValid );
+ rInner.SetTable( true );
+}
+}
+
+void ScHelperFunctions::FillBoxItems( SvxBoxItem& rOuter, SvxBoxInfoItem& rInner, const table::TableBorder& rBorder )
+{
+ lcl_fillBoxItems( rOuter, rInner, rBorder);
+}
+
+void ScHelperFunctions::FillBoxItems( SvxBoxItem& rOuter, SvxBoxInfoItem& rInner, const table::TableBorder2& rBorder )
+{
+ lcl_fillBoxItems( rOuter, rInner, rBorder);
+}
+
+void ScHelperFunctions::FillBorderLine( table::BorderLine& rStruct, const ::editeng::SvxBorderLine* pLine )
+{
+ // Convert from Twips to 1/100mm.
+ rStruct = SvxBoxItem::SvxLineToLine( pLine, true);
+}
+
+void ScHelperFunctions::FillBorderLine( table::BorderLine2& rStruct, const ::editeng::SvxBorderLine* pLine )
+{
+ rStruct = SvxBoxItem::SvxLineToLine( pLine, true);
+}
+
+namespace {
+template<typename TableBorderItem>
+void lcl_fillTableBorder( TableBorderItem& rBorder, const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner,
+ bool bInvalidateHorVerDist )
+{
+ ScHelperFunctions::FillBorderLine( rBorder.TopLine, rOuter.GetTop() );
+ ScHelperFunctions::FillBorderLine( rBorder.BottomLine, rOuter.GetBottom() );
+ ScHelperFunctions::FillBorderLine( rBorder.LeftLine, rOuter.GetLeft() );
+ ScHelperFunctions::FillBorderLine( rBorder.RightLine, rOuter.GetRight() );
+ ScHelperFunctions::FillBorderLine( rBorder.HorizontalLine, rInner.GetHori() );
+ ScHelperFunctions::FillBorderLine( rBorder.VerticalLine, rInner.GetVert() );
+
+ rBorder.Distance = rOuter.GetSmallestDistance();
+ rBorder.IsTopLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::TOP);
+ rBorder.IsBottomLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ rBorder.IsLeftLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ rBorder.IsRightLineValid = rInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT);
+ rBorder.IsHorizontalLineValid = !bInvalidateHorVerDist && rInner.IsValid(SvxBoxInfoItemValidFlags::HORI);
+ rBorder.IsVerticalLineValid = !bInvalidateHorVerDist && rInner.IsValid(SvxBoxInfoItemValidFlags::VERT);
+ rBorder.IsDistanceValid = !bInvalidateHorVerDist && rInner.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
+}
+}
+
+void ScHelperFunctions::AssignTableBorderToAny( uno::Any& rAny,
+ const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner, bool bInvalidateHorVerDist )
+{
+ table::TableBorder aBorder;
+ lcl_fillTableBorder( aBorder, rOuter, rInner, bInvalidateHorVerDist);
+ rAny <<= aBorder;
+}
+
+void ScHelperFunctions::AssignTableBorder2ToAny( uno::Any& rAny,
+ const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner, bool bInvalidateHorVerDist )
+{
+ table::TableBorder2 aBorder;
+ lcl_fillTableBorder( aBorder, rOuter, rInner, bInvalidateHorVerDist);
+ rAny <<= aBorder;
+}
+
+//! move lcl_ApplyBorder to docfunc !
+
+void ScHelperFunctions::ApplyBorder( ScDocShell* pDocShell, const ScRangeList& rRanges,
+ const SvxBoxItem& rOuter, const SvxBoxInfoItem& rInner )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDocumentUniquePtr pUndoDoc;
+ if (bUndo)
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ size_t nCount = rRanges.size();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ ScRange const & rRange = rRanges[ i ];
+ SCTAB nTab = rRange.aStart.Tab();
+
+ if (bUndo)
+ {
+ if ( i==0 )
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ else
+ pUndoDoc->AddUndoTab( nTab, nTab );
+ rDoc.CopyToDocument(rRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc);
+ }
+
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( rRange );
+ aMark.SelectTable( nTab, true );
+
+ rDoc.ApplySelectionFrame(aMark, rOuter, &rInner);
+ // don't need RowHeight if there is only a border
+ }
+
+ if (bUndo)
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoBorder>( pDocShell, rRanges, std::move(pUndoDoc), rOuter, rInner ) );
+ }
+
+ for (size_t i = 0; i < nCount; ++i )
+ pDocShell->PostPaint( rRanges[ i ], PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ pDocShell->SetDocumentModified();
+}
+
+//! move lcl_PutDataArray to docfunc?
+//! merge loop with ScFunctionAccess::callFunction
+
+static bool lcl_PutDataArray( ScDocShell& rDocShell, const ScRange& rRange,
+ const uno::Sequence< uno::Sequence<uno::Any> >& aData )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if ( !rDoc.IsBlockEditable( nTab, nStartCol,nStartRow, nEndCol,nEndRow ) )
+ {
+ //! error message
+ return false;
+ }
+
+ sal_Int32 nCols = 0;
+ sal_Int32 nRows = aData.getLength();
+ if ( nRows )
+ nCols = aData[0].getLength();
+
+ if ( nCols != nEndCol-nStartCol+1 || nRows != nEndRow-nStartRow+1 )
+ {
+ //! error message?
+ return false;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bUndo )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(rRange, InsertDeleteFlags::CONTENTS|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc);
+ }
+
+ rDoc.DeleteAreaTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, InsertDeleteFlags::CONTENTS );
+
+ bool bError = false;
+ SCROW nDocRow = nStartRow;
+ for (const uno::Sequence<uno::Any>& rColSeq : aData)
+ {
+ if ( rColSeq.getLength() == nCols )
+ {
+ SCCOL nDocCol = nStartCol;
+ for (const uno::Any& rElement : rColSeq)
+ {
+ ScAddress aPos(nDocCol, nDocRow, nTab);
+
+ switch( rElement.getValueTypeClass() )
+ {
+ case uno::TypeClass_VOID:
+ {
+ // void = "no value"
+ rDoc.SetError( nDocCol, nDocRow, nTab, FormulaError::NotAvailable );
+ }
+ break;
+
+ // #87871# accept integer types because Basic passes a floating point
+ // variable as byte, short or long if it's an integer number.
+ case uno::TypeClass_BYTE:
+ case uno::TypeClass_SHORT:
+ case uno::TypeClass_UNSIGNED_SHORT:
+ case uno::TypeClass_LONG:
+ case uno::TypeClass_UNSIGNED_LONG:
+ case uno::TypeClass_FLOAT:
+ case uno::TypeClass_DOUBLE:
+ {
+ double fVal(0.0);
+ rElement >>= fVal;
+ rDoc.SetValue(aPos, fVal);
+ }
+ break;
+
+ case uno::TypeClass_STRING:
+ {
+ OUString aUStr;
+ rElement >>= aUStr;
+ if ( !aUStr.isEmpty() )
+ {
+ // tdf#146454 - check for a multiline string since setting an edit
+ // or string cell is in magnitudes slower than setting a plain string
+ if (ScStringUtil::isMultiline(aUStr))
+ {
+ rEngine.SetTextCurrentDefaults(aUStr);
+ rDoc.SetEditText(aPos, rEngine.CreateTextObject());
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(aPos, aUStr, &aParam);
+ }
+ }
+ }
+ break;
+
+ // accept Sequence<FormulaToken> for formula cells
+ case uno::TypeClass_SEQUENCE:
+ {
+ uno::Sequence< sheet::FormulaToken > aTokens;
+ if ( rElement >>= aTokens )
+ {
+ ScTokenArray aTokenArray(rDoc);
+ ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, aTokens );
+ rDoc.SetFormula(aPos, aTokenArray);
+ }
+ else
+ bError = true;
+ }
+ break;
+
+ default:
+ bError = true; // invalid type
+ }
+ ++nDocCol;
+ }
+ }
+ else
+ bError = true; // wrong size
+
+ ++nDocRow;
+ }
+
+ bool bHeight = rDocShell.AdjustRowHeight( nStartRow, nEndRow, nTab );
+
+ if ( pUndoDoc )
+ {
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SelectOneTable( nTab );
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPaste>(
+ &rDocShell, ScRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab),
+ aDestMark, std::move(pUndoDoc), nullptr, InsertDeleteFlags::CONTENTS, nullptr, false));
+ }
+
+ if (!bHeight)
+ rDocShell.PostPaint( rRange, PaintPartFlags::Grid ); // AdjustRowHeight may have painted already
+
+ rDocShell.SetDocumentModified();
+
+ return !bError;
+}
+
+static bool lcl_PutFormulaArray( ScDocShell& rDocShell, const ScRange& rRange,
+ const uno::Sequence< uno::Sequence<OUString> >& aData,
+ const formula::FormulaGrammar::Grammar eGrammar )
+{
+ ScDocument& rDoc = rDocShell.GetDocument();
+ SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if ( !rDoc.IsBlockEditable( nTab, nStartCol,nStartRow, nEndCol,nEndRow ) )
+ {
+ //! error message
+ return false;
+ }
+
+ sal_Int32 nCols = 0;
+ sal_Int32 nRows = aData.getLength();
+ if ( nRows )
+ nCols = aData[0].getLength();
+
+ if ( nCols != nEndCol-nStartCol+1 || nRows != nEndRow-nStartRow+1 )
+ {
+ //! error message?
+ return false;
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bUndo )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument(rRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc);
+ }
+
+ rDoc.DeleteAreaTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, InsertDeleteFlags::CONTENTS );
+
+ bool bError = false;
+ SCROW nDocRow = nStartRow;
+ for (const uno::Sequence<OUString>& rColSeq : aData)
+ {
+ if ( rColSeq.getLength() == nCols )
+ {
+ SCCOL nDocCol = nStartCol;
+ for (const OUString& aText : rColSeq)
+ {
+ ScAddress aPos( nDocCol, nDocRow, nTab );
+
+ ScInputStringType aRes =
+ ScStringUtil::parseInputString(
+ *rDoc.GetFormatTable(), aText, LANGUAGE_ENGLISH_US);
+ switch (aRes.meType)
+ {
+ case ScInputStringType::Formula:
+ rDoc.SetFormula(aPos, aRes.maText, eGrammar);
+ break;
+ case ScInputStringType::Number:
+ rDoc.SetValue(aPos, aRes.mfValue);
+ break;
+ case ScInputStringType::Text:
+ rDoc.SetTextCell(aPos, aRes.maText);
+ break;
+ default:
+ ;
+ }
+
+ ++nDocCol;
+ }
+ }
+ else
+ bError = true; // wrong size
+
+ ++nDocRow;
+ }
+
+ bool bHeight = rDocShell.AdjustRowHeight( nStartRow, nEndRow, nTab );
+
+ if ( pUndoDoc )
+ {
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SelectOneTable( nTab );
+ rDocShell.GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPaste>( &rDocShell,
+ ScRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab), aDestMark,
+ std::move(pUndoDoc), nullptr, InsertDeleteFlags::CONTENTS, nullptr, false));
+ }
+
+ if (!bHeight)
+ rDocShell.PostPaint( rRange, PaintPartFlags::Grid ); // AdjustRowHeight may have painted already
+
+ rDocShell.SetDocumentModified();
+
+ return !bError;
+}
+
+// used in ScCellRangeObj::getFormulaArray and ScCellObj::GetInputString_Impl
+static OUString lcl_GetInputString( ScDocument& rDoc, const ScAddress& rPos, bool bEnglish )
+{
+ ScRefCellValue aCell(rDoc, rPos);
+ if (aCell.isEmpty())
+ return OUString();
+
+ OUString aVal;
+
+ CellType eType = aCell.getType();
+ if (eType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pForm = aCell.getFormula();
+ return pForm->GetFormula( formula::FormulaGrammar::mapAPItoGrammar( bEnglish, false));
+ }
+
+ SvNumberFormatter* pFormatter = bEnglish ? ScGlobal::GetEnglishFormatter() :
+ rDoc.GetFormatTable();
+ // Since the English formatter was constructed with
+ // LANGUAGE_ENGLISH_US the "General" format has index key 0,
+ // we don't have to query.
+ sal_uInt32 nNumFmt = bEnglish ? 0 : rDoc.GetNumberFormat(rPos);
+
+ if (eType == CELLTYPE_EDIT)
+ {
+ // GetString on EditCell turns breaks into spaces,
+ // but we need the breaks here
+ const EditTextObject* pData = aCell.getEditText();
+ if (pData)
+ {
+ EditEngine& rEngine = rDoc.GetEditEngine();
+ rEngine.SetText(*pData);
+ aVal = rEngine.GetText();
+ }
+ }
+ else
+ aVal = ScCellFormat::GetInputString(aCell, nNumFmt, *pFormatter, rDoc);
+
+ // if applicable, prepend ' like in ScTabViewShell::UpdateInputHandler
+ if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT )
+ {
+ double fDummy;
+ OUString aTempString = aVal;
+ bool bIsNumberFormat(pFormatter->IsNumberFormat(aTempString, nNumFmt, fDummy));
+ if ( bIsNumberFormat )
+ aTempString = "'" + aTempString;
+ else if ( aTempString.startsWith("'") )
+ {
+ // if the string starts with a "'", add another one because setFormula
+ // strips one (like text input, except for "text" number formats)
+ if ( bEnglish || ( pFormatter->GetType(nNumFmt) != SvNumFormatType::TEXT ) )
+ aTempString = "'" + aTempString;
+ }
+ aVal = aTempString;
+ }
+ return aVal;
+}
+
+ScCellRangesBase::ScCellRangesBase(ScDocShell* pDocSh, const ScRange& rR) :
+ pPropSet(lcl_GetCellsPropertySet()),
+ pDocShell( pDocSh ),
+ nObjectId( 0 ),
+ bChartColAsHdr( false ),
+ bChartRowAsHdr( false ),
+ bCursorOnly( false ),
+ bGotDataChangedHint( false ),
+ aValueListeners( 0 )
+{
+ ScRange aCellRange(rR);
+ aCellRange.PutInOrder();
+ aRanges.push_back( aCellRange );
+
+ if (pDocShell) // Null if created with createInstance
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.AddUnoObject(*this);
+ nObjectId = rDoc.GetNewUnoId();
+ }
+}
+
+ScCellRangesBase::ScCellRangesBase(ScDocShell* pDocSh, ScRangeList aR) :
+ pPropSet(lcl_GetCellsPropertySet()),
+ pDocShell( pDocSh ),
+ aRanges(std::move( aR )),
+ nObjectId( 0 ),
+ bChartColAsHdr( false ),
+ bChartRowAsHdr( false ),
+ bCursorOnly( false ),
+ bGotDataChangedHint( false ),
+ aValueListeners( 0 )
+{
+ if (pDocShell) // Null if created with createInstance
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.AddUnoObject(*this);
+ nObjectId = rDoc.GetNewUnoId();
+ }
+}
+
+ScCellRangesBase::~ScCellRangesBase()
+{
+ SolarMutexGuard g;
+
+ // call RemoveUnoObject first, so no notification can happen
+ // during ForgetCurrentAttrs
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+
+ ForgetCurrentAttrs();
+ ForgetMarkData();
+
+ pValueListener.reset();
+
+ //! unregister XChartDataChangeEventListener ??
+ //! (ChartCollection will then hold this object as well!)
+}
+
+void ScCellRangesBase::ForgetCurrentAttrs()
+{
+ pCurrentFlat.reset();
+ pCurrentDeep.reset();
+ moCurrentDataSet.reset();
+ moNoDfltCurrentDataSet.reset();
+
+ // #i62483# pMarkData can remain unchanged, is deleted only if the range changes (RefChanged)
+}
+
+void ScCellRangesBase::ForgetMarkData()
+{
+ pMarkData.reset();
+}
+
+const ScPatternAttr* ScCellRangesBase::GetCurrentAttrsFlat()
+{
+ // get and cache direct cell attributes for this object's range
+
+ if ( !pCurrentFlat && pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pCurrentFlat = rDoc.CreateSelectionPattern( *GetMarkData(), false );
+ }
+ return pCurrentFlat.get();
+}
+
+const ScPatternAttr* ScCellRangesBase::GetCurrentAttrsDeep()
+{
+ // get and cache cell attributes (incl. styles) for this object's range
+
+ if ( !pCurrentDeep && pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pCurrentDeep = rDoc.CreateSelectionPattern( *GetMarkData() );
+ }
+ return pCurrentDeep.get();
+}
+
+SfxItemSet* ScCellRangesBase::GetCurrentDataSet(bool bNoDflt)
+{
+ if(!moCurrentDataSet)
+ {
+ const ScPatternAttr* pPattern = GetCurrentAttrsDeep();
+ if ( pPattern )
+ {
+ // replace Dontcare with Default, so that we always have a reflection
+ moCurrentDataSet.emplace( pPattern->GetItemSet() );
+ moNoDfltCurrentDataSet.emplace( pPattern->GetItemSet() );
+ moCurrentDataSet->ClearInvalidItems();
+ }
+ }
+ if (bNoDflt)
+ {
+ if (moNoDfltCurrentDataSet)
+ return &*moNoDfltCurrentDataSet;
+ }
+ else
+ {
+ if (moCurrentDataSet)
+ return &*moCurrentDataSet;
+ }
+ return nullptr;
+}
+
+const ScMarkData* ScCellRangesBase::GetMarkData()
+{
+ if (!pMarkData)
+ {
+ pMarkData.reset( new ScMarkData(GetDocument()->GetSheetLimits(), aRanges) );
+ }
+ return pMarkData.get();
+}
+
+void ScCellRangesBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::Dying )
+ {
+ // if the document dies, must reset to avoid crash in dtor!
+ ForgetCurrentAttrs();
+ pDocShell = nullptr; // invalid
+
+ // fdo#72695: if UNO object is already dead, don't revive it with event
+ if ( m_refCount > 0 && !aValueListeners.empty() )
+ {
+ // dispose listeners
+
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+ for (uno::Reference<util::XModifyListener> & xValueListener : aValueListeners)
+ xValueListener->disposing( aEvent );
+
+ aValueListeners.clear();
+
+ // The listeners can't have the last ref to this, as it's still held
+ // by the DocShell.
+ }
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ // document content changed -> forget cached attributes
+ ForgetCurrentAttrs();
+
+ if ( bGotDataChangedHint && pDocShell )
+ {
+ // This object was notified of content changes, so one call
+ // for each listener is generated now.
+ // The calls can't be executed directly because the document's
+ // UNO broadcaster list must not be modified.
+ // Instead, add to the document's list of listener calls,
+ // which will be executed directly after the broadcast of
+ // SfxHintId::DataChanged.
+
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+
+ // the EventObject holds a Ref to this object until after the listener calls
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for (const uno::Reference<util::XModifyListener> & xValueListener : aValueListeners)
+ rDoc.AddUnoListenerCall( xValueListener, aEvent );
+
+ bGotDataChangedHint = false;
+ }
+ }
+ else if ( nId == SfxHintId::ScCalcAll )
+ {
+ // broadcast from DoHardRecalc - set bGotDataChangedHint
+ // (SfxHintId::DataChanged follows separately)
+
+ if ( !aValueListeners.empty() )
+ bGotDataChangedHint = true;
+ }
+ else if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ std::unique_ptr<ScRangeList> pUndoRanges;
+ if ( rDoc.HasUnoRefUndo() )
+ pUndoRanges.reset(new ScRangeList( aRanges ));
+
+ if ( aRanges.UpdateReference( pRefHint->GetMode(), &rDoc, pRefHint->GetRange(),
+ pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ) )
+ {
+ if ( pRefHint->GetMode() == URM_INSDEL
+ && aRanges.size() == 1
+ && dynamic_cast<ScTableSheetObj*>(this)
+ )
+ {
+ // #101755#; the range size of a sheet does not change
+ ScRange & rR = aRanges.front();
+ rR.aStart.SetCol(0);
+ rR.aStart.SetRow(0);
+ rR.aEnd.SetCol(rDoc.MaxCol());
+ rR.aEnd.SetRow(rDoc.MaxRow());
+ }
+ RefChanged();
+
+ // any change of the range address is broadcast to value (modify) listeners
+ if ( !aValueListeners.empty() )
+ bGotDataChangedHint = true;
+
+ if ( pUndoRanges )
+ rDoc.AddUnoRefChange( nObjectId, *pUndoRanges );
+ }
+ }
+ else if ( auto pUndoHint = dynamic_cast<const ScUnoRefUndoHint*>(&rHint) )
+ {
+ if ( pUndoHint->GetObjectId() == nObjectId )
+ {
+ // restore ranges from hint
+
+ aRanges = pUndoHint->GetRanges();
+
+ RefChanged();
+ if ( !aValueListeners.empty() )
+ bGotDataChangedHint = true; // need to broadcast the undo, too
+ }
+ }
+}
+
+void ScCellRangesBase::RefChanged()
+{
+ //! adjust XChartDataChangeEventListener
+
+ if ( pValueListener && !aValueListeners.empty() )
+ {
+ pValueListener->EndListeningAll();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i )
+ rDoc.StartListeningArea( aRanges[ i ], false, pValueListener.get() );
+ }
+
+ ForgetCurrentAttrs();
+ ForgetMarkData();
+}
+
+ScDocument* ScCellRangesBase::GetDocument() const
+{
+ if (pDocShell)
+ return &pDocShell->GetDocument();
+ else
+ return nullptr;
+}
+
+void ScCellRangesBase::InitInsertRange(ScDocShell* pDocSh, const ScRange& rR)
+{
+ if ( pDocShell || !pDocSh )
+ return;
+
+ pDocShell = pDocSh;
+
+ ScRange aCellRange(rR);
+ aCellRange.PutInOrder();
+ aRanges.RemoveAll();
+ aRanges.push_back( aCellRange );
+
+ pDocShell->GetDocument().AddUnoObject(*this);
+
+ RefChanged(); // adjust range in range object
+}
+
+void ScCellRangesBase::AddRange(const ScRange& rRange, const bool bMergeRanges)
+{
+ if (bMergeRanges)
+ aRanges.Join(rRange);
+ else
+ aRanges.push_back(rRange);
+ RefChanged();
+}
+
+void ScCellRangesBase::SetNewRange(const ScRange& rNew)
+{
+ ScRange aCellRange(rNew);
+ aCellRange.PutInOrder();
+
+ aRanges.RemoveAll();
+ aRanges.push_back( aCellRange );
+ RefChanged();
+}
+
+void ScCellRangesBase::SetNewRanges(const ScRangeList& rNew)
+{
+ aRanges = rNew;
+ RefChanged();
+}
+
+void ScCellRangesBase::SetCursorOnly( bool bSet )
+{
+ // set for a selection object that is created from the cursor position
+ // without anything selected (may contain several sheets)
+
+ bCursorOnly = bSet;
+}
+
+void ScCellRangesBase::PaintGridRanges_Impl( )
+{
+ for (size_t i = 0, nCount = aRanges.size(); i < nCount; ++i)
+ pDocShell->PostPaint( aRanges[ i ], PaintPartFlags::Grid );
+}
+
+// XSheetOperation
+
+double SAL_CALL ScCellRangesBase::computeFunction( sheet::GeneralFunction nFunction )
+{
+ SolarMutexGuard aGuard;
+ ScMarkData aMark(*GetMarkData());
+ aMark.MarkToSimple();
+ if (!aMark.IsMarked())
+ aMark.SetMarkNegative(true); // so we can enter dummy position
+
+ ScAddress aDummy; // if not marked, ignored if it is negative
+ double fVal;
+ ScSubTotalFunc eFunc = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(nFunction));
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( !rDoc.GetSelectionFunction( eFunc, aDummy, aMark, fVal ) )
+ {
+ throw uno::RuntimeException(); //! own exception?
+ }
+
+ return fVal;
+}
+
+void SAL_CALL ScCellRangesBase::clearContents( sal_Int32 nContentFlags )
+{
+ SolarMutexGuard aGuard;
+ if ( !aRanges.empty() )
+ {
+ // only for clearContents: EDITATTR is only used if no contents are deleted
+ InsertDeleteFlags nDelFlags = static_cast<InsertDeleteFlags>(nContentFlags) & InsertDeleteFlags::ALL;
+ if ( ( nDelFlags & InsertDeleteFlags::EDITATTR ) && ( nDelFlags & InsertDeleteFlags::CONTENTS ) == InsertDeleteFlags::NONE )
+ nDelFlags |= InsertDeleteFlags::EDITATTR;
+
+ pDocShell->GetDocFunc().DeleteContents( *GetMarkData(), nDelFlags, true, true );
+ }
+ // otherwise nothing to do
+}
+
+// XPropertyState
+
+const SfxItemPropertyMap& ScCellRangesBase::GetItemPropertyMap()
+{
+ return pPropSet->getPropertyMap();
+}
+
+static void lcl_GetPropertyWhich( const SfxItemPropertyMapEntry* pEntry,
+ sal_uInt16& rItemWhich )
+{
+ // Which-ID of the affected items also when the item can't handle
+ // the property by itself
+ if ( !pEntry )
+ return;
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ rItemWhich = pEntry->nWID;
+ else
+ switch ( pEntry->nWID )
+ {
+ case SC_WID_UNO_TBLBORD:
+ case SC_WID_UNO_TBLBORD2:
+ rItemWhich = ATTR_BORDER;
+ break;
+ case SC_WID_UNO_CONDFMT:
+ case SC_WID_UNO_CONDLOC:
+ case SC_WID_UNO_CONDXML:
+ rItemWhich = ATTR_CONDITIONAL;
+ break;
+ case SC_WID_UNO_VALIDAT:
+ case SC_WID_UNO_VALILOC:
+ case SC_WID_UNO_VALIXML:
+ rItemWhich = ATTR_VALIDDATA;
+ break;
+ }
+
+}
+
+beans::PropertyState ScCellRangesBase::GetOnePropertyState( sal_uInt16 nItemWhich, const SfxItemPropertyMapEntry* pEntry )
+{
+ beans::PropertyState eRet = beans::PropertyState_DIRECT_VALUE;
+ if ( nItemWhich ) // item wid (from map or special case)
+ {
+ // For items that contain several properties (like background),
+ // "ambiguous" is returned too often here
+
+ // for PropertyState, don't look at styles
+ const ScPatternAttr* pPattern = GetCurrentAttrsFlat();
+ if ( pPattern )
+ {
+ SfxItemState eState = pPattern->GetItemSet().GetItemState( nItemWhich, false );
+
+ if ( nItemWhich == ATTR_VALUE_FORMAT && eState == SfxItemState::DEFAULT )
+ eState = pPattern->GetItemSet().GetItemState( ATTR_LANGUAGE_FORMAT, false );
+
+ if ( eState == SfxItemState::SET )
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ else if ( eState == SfxItemState::DEFAULT )
+ eRet = beans::PropertyState_DEFAULT_VALUE;
+ else if ( eState == SfxItemState::DONTCARE )
+ eRet = beans::PropertyState_AMBIGUOUS_VALUE;
+ else
+ {
+ OSL_FAIL("unknown ItemState");
+ }
+ }
+ }
+ else if ( pEntry )
+ {
+ if ( pEntry->nWID == SC_WID_UNO_CHCOLHDR || pEntry->nWID == SC_WID_UNO_CHROWHDR || pEntry->nWID == SC_WID_UNO_ABSNAME )
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ else if ( pEntry->nWID == SC_WID_UNO_CELLSTYL )
+ {
+ // a style is always set, there's no default state
+ const ScStyleSheet* pStyle = pDocShell->GetDocument().GetSelectionStyle(*GetMarkData());
+ if (pStyle)
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ else
+ eRet = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_NUMRULES )
+ eRet = beans::PropertyState_DEFAULT_VALUE; // numbering rules are always default
+ }
+ return eRet;
+}
+
+beans::PropertyState SAL_CALL ScCellRangesBase::getPropertyState( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if ( aRanges.empty() )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMap& rMap = GetItemPropertyMap(); // from derived class
+ sal_uInt16 nItemWhich = 0;
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName( aPropertyName );
+ lcl_GetPropertyWhich( pEntry, nItemWhich );
+ return GetOnePropertyState( nItemWhich, pEntry );
+}
+
+uno::Sequence<beans::PropertyState> SAL_CALL ScCellRangesBase::getPropertyStates(
+ const uno::Sequence<OUString>& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+
+ uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength());
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(),
+ [this, &rPropertyMap](const auto& rName) -> beans::PropertyState {
+ sal_uInt16 nItemWhich = 0;
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( rName );
+ lcl_GetPropertyWhich( pEntry, nItemWhich );
+ return GetOnePropertyState(nItemWhich, pEntry);
+ });
+ return aRet;
+}
+
+void SAL_CALL ScCellRangesBase::setPropertyToDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if ( !pDocShell )
+ return;
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+ sal_uInt16 nItemWhich = 0;
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ lcl_GetPropertyWhich( pEntry, nItemWhich );
+ if ( nItemWhich ) // item wid (from map or special case)
+ {
+ if ( !aRanges.empty() ) // empty = nothing to do
+ {
+ //! for items that have multiple properties (e.g. background)
+ //! too much will be reset
+ //! for ATTR_ROTATE_VALUE, reset ATTR_ORIENTATION as well?
+
+ sal_uInt16 aWIDs[3];
+ aWIDs[0] = nItemWhich;
+ if ( nItemWhich == ATTR_VALUE_FORMAT )
+ {
+ aWIDs[1] = ATTR_LANGUAGE_FORMAT; // language for number formats
+ aWIDs[2] = 0;
+ }
+ else
+ aWIDs[1] = 0;
+ pDocShell->GetDocFunc().ClearItems( *GetMarkData(), aWIDs, true );
+ }
+ }
+ else if ( pEntry )
+ {
+ if ( pEntry->nWID == SC_WID_UNO_CHCOLHDR )
+ bChartColAsHdr = false;
+ else if ( pEntry->nWID == SC_WID_UNO_CHROWHDR )
+ bChartRowAsHdr = false;
+ else if ( pEntry->nWID == SC_WID_UNO_CELLSTYL )
+ {
+ OUString aStyleName( ScResId( STR_STYLENAME_STANDARD ) );
+ pDocShell->GetDocFunc().ApplyStyle( *GetMarkData(), aStyleName, true );
+ }
+ }
+}
+
+uno::Any SAL_CALL ScCellRangesBase::getPropertyDefault( const OUString& aPropertyName )
+{
+ //! bundle with getPropertyValue
+
+ SolarMutexGuard aGuard;
+ uno::Any aAny;
+
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( pEntry )
+ {
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ const ScPatternAttr* pPattern = rDoc.GetDefPattern();
+ if ( pPattern )
+ {
+ const SfxItemSet& rSet = pPattern->GetItemSet();
+
+ switch ( pEntry->nWID ) // for item-specific handling
+ {
+ case ATTR_VALUE_FORMAT:
+ // default has no language set
+ aAny <<= static_cast<sal_Int32>( static_cast<const SfxUInt32Item&>(rSet.Get(pEntry->nWID)).GetValue() );
+ break;
+ case ATTR_INDENT:
+ aAny <<= static_cast<sal_Int16>( convertTwipToMm100(static_cast<const ScIndentItem&>(
+ rSet.Get(pEntry->nWID)).GetValue()) );
+ break;
+ default:
+ pPropSet->getPropertyValue(aPropertyName, rSet, aAny);
+ }
+ }
+ }
+ else
+ switch ( pEntry->nWID )
+ {
+ case SC_WID_UNO_CHCOLHDR:
+ case SC_WID_UNO_CHROWHDR:
+ aAny <<= false;
+ break;
+ case SC_WID_UNO_CELLSTYL:
+ aAny <<= ScStyleNameConversion::DisplayToProgrammaticName(
+ ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para );
+ break;
+ case SC_WID_UNO_TBLBORD:
+ case SC_WID_UNO_TBLBORD2:
+ {
+ const ScPatternAttr* pPattern = rDoc.GetDefPattern();
+ if ( pPattern )
+ {
+ if (pEntry->nWID == SC_WID_UNO_TBLBORD2)
+ ScHelperFunctions::AssignTableBorder2ToAny( aAny,
+ pPattern->GetItem(ATTR_BORDER),
+ pPattern->GetItem(ATTR_BORDER_INNER) );
+ else
+ ScHelperFunctions::AssignTableBorderToAny( aAny,
+ pPattern->GetItem(ATTR_BORDER),
+ pPattern->GetItem(ATTR_BORDER_INNER) );
+ }
+ }
+ break;
+ case SC_WID_UNO_CONDFMT:
+ case SC_WID_UNO_CONDLOC:
+ case SC_WID_UNO_CONDXML:
+ {
+ bool bEnglish = ( pEntry->nWID != SC_WID_UNO_CONDLOC );
+ bool bXML = ( pEntry->nWID == SC_WID_UNO_CONDXML );
+ formula::FormulaGrammar::Grammar eGrammar = (bXML ?
+ rDoc.GetStorageGrammar() :
+ formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML));
+
+ aAny <<= uno::Reference<sheet::XSheetConditionalEntries>(
+ new ScTableConditionalFormat( &rDoc, 0, aRanges[0].aStart.Tab(), eGrammar ));
+ }
+ break;
+ case SC_WID_UNO_VALIDAT:
+ case SC_WID_UNO_VALILOC:
+ case SC_WID_UNO_VALIXML:
+ {
+ bool bEnglish = ( pEntry->nWID != SC_WID_UNO_VALILOC );
+ bool bXML = ( pEntry->nWID == SC_WID_UNO_VALIXML );
+ formula::FormulaGrammar::Grammar eGrammar = (bXML ?
+ rDoc.GetStorageGrammar() :
+ formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML));
+
+ aAny <<= uno::Reference<beans::XPropertySet>(
+ new ScTableValidationObj( rDoc, 0, eGrammar ));
+ }
+ break;
+ case SC_WID_UNO_NUMRULES:
+ {
+ aAny <<= ScStyleObj::CreateEmptyNumberingRules();
+ }
+ break;
+ }
+ }
+ }
+
+ return aAny;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellRangesBase::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( pPropSet->getPropertyMap() ));
+ return aRef;
+}
+
+static void lcl_SetCellProperty( const SfxItemPropertyMapEntry& rEntry, const uno::Any& rValue,
+ ScPatternAttr& rPattern, const ScDocument &rDoc,
+ sal_uInt16& rFirstItemId, sal_uInt16& rSecondItemId )
+{
+ rFirstItemId = rEntry.nWID;
+ rSecondItemId = 0;
+
+ SfxItemSet& rSet = rPattern.GetItemSet();
+ switch ( rEntry.nWID )
+ {
+ case ATTR_VALUE_FORMAT:
+ {
+ // language for number formats
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uLong nOldFormat = rSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType eOldLang = rSet.Get( ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ nOldFormat = pFormatter->GetFormatForLanguageIfBuiltIn( nOldFormat, eOldLang );
+
+ sal_Int32 nIntVal = 0;
+ if ( !(rValue >>= nIntVal) )
+ throw lang::IllegalArgumentException();
+
+ sal_uLong nNewFormat = static_cast<sal_uLong>(nIntVal);
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+
+ const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
+ LanguageType eNewLang =
+ pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ if ( eNewLang != eOldLang && eNewLang != LANGUAGE_DONTKNOW )
+ {
+ rSet.Put( SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
+
+ // if only language is changed,
+ // don't touch number format attribute
+ sal_uLong nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
+ if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
+ nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
+ {
+ rFirstItemId = 0; // don't use ATTR_VALUE_FORMAT value
+ }
+
+ rSecondItemId = ATTR_LANGUAGE_FORMAT;
+ }
+
+ }
+ break;
+ case ATTR_INDENT:
+ {
+ sal_Int16 nIntVal = 0;
+ if ( !(rValue >>= nIntVal) )
+ throw lang::IllegalArgumentException();
+
+ rSet.Put(ScIndentItem(o3tl::toTwips(nIntVal, o3tl::Length::mm100)));
+
+ }
+ break;
+ case ATTR_ROTATE_VALUE:
+ {
+ sal_Int32 nRotVal = 0;
+ if ( !(rValue >>= nRotVal) )
+ throw lang::IllegalArgumentException();
+
+ // stored value is always between 0 and 360 deg.
+ nRotVal %= 36000;
+ if ( nRotVal < 0 )
+ nRotVal += 36000;
+
+ rSet.Put( ScRotateValueItem( Degree100(nRotVal) ) );
+
+ }
+ break;
+ case ATTR_STACKED:
+ {
+ table::CellOrientation eOrient;
+ if( rValue >>= eOrient )
+ {
+ switch( eOrient )
+ {
+ case table::CellOrientation_STANDARD:
+ rSet.Put( ScVerticalStackCell( false ) );
+ break;
+ case table::CellOrientation_TOPBOTTOM:
+ rSet.Put( ScVerticalStackCell( false ) );
+ rSet.Put( ScRotateValueItem( 27000_deg100 ) );
+ rSecondItemId = ATTR_ROTATE_VALUE;
+ break;
+ case table::CellOrientation_BOTTOMTOP:
+ rSet.Put( ScVerticalStackCell( false ) );
+ rSet.Put( ScRotateValueItem( 9000_deg100 ) );
+ rSecondItemId = ATTR_ROTATE_VALUE;
+ break;
+ case table::CellOrientation_STACKED:
+ rSet.Put( ScVerticalStackCell( true ) );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ break;
+ default:
+ {
+ lcl_GetCellsPropertySet()->setPropertyValue(rEntry, rValue, rSet);
+ }
+ }
+}
+
+void SAL_CALL ScCellRangesBase::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell || aRanges.empty() )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ SetOnePropertyValue( pEntry, aValue );
+}
+
+void ScCellRangesBase::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue )
+{
+ if ( !pEntry )
+ return;
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ if ( !aRanges.empty() ) // empty = nothing to do
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ // For parts of compound items with multiple properties (e.g. background)
+ // the old item has to be first fetched from the document.
+ //! But we can't recognize this case here
+ //! -> an extra flag in PropertyMap entry, or something like that???
+ //! fetch the item directly from its position in the range?
+ // ClearInvalidItems, so that in any case we have an item with the correct type
+
+ ScPatternAttr aPattern( *GetCurrentAttrsDeep() );
+ SfxItemSet& rSet = aPattern.GetItemSet();
+ rSet.ClearInvalidItems();
+
+ sal_uInt16 nFirstItem, nSecondItem;
+ lcl_SetCellProperty( *pEntry, aValue, aPattern, rDoc, nFirstItem, nSecondItem );
+
+ for (sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++)
+ if ( nWhich != nFirstItem && nWhich != nSecondItem )
+ rSet.ClearItem(nWhich);
+
+ pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), aPattern, true );
+ }
+ }
+ else // implemented here
+ switch ( pEntry->nWID )
+ {
+ case EE_CHAR_ESCAPEMENT: // Specifically for xlsx import
+ {
+ sal_Int32 nValue = 0;
+ aValue >>= nValue;
+ if (nValue)
+ {
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ ScRange const & rRange = aRanges[i];
+
+ /* TODO: Iterate through the range */
+ ScAddress aAddr = rRange.aStart;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRefCellValue aCell(rDoc, aAddr);
+
+ OUString aStr = aCell.getString(&rDoc);
+ EditEngine aEngine( rDoc.GetEnginePool() );
+ aEngine.SetEditTextObjectPool(rDoc.GetEditPool());
+
+ /* EE_CHAR_ESCAPEMENT seems to be set on the cell _only_ when
+ * there are no other attribs for the cell.
+ * So, it is safe to overwrite the complete attribute set.
+ * If there is a need - getting CellType and processing
+ * the attributes could be considered.
+ */
+ SfxItemSet aAttr = aEngine.GetEmptyItemSet();
+ aEngine.SetText(aStr);
+ if( nValue < 0 ) // Subscript
+ aAttr.Put( SvxEscapementItem( SvxEscapement::Subscript, EE_CHAR_ESCAPEMENT ) );
+ else // Superscript
+ aAttr.Put( SvxEscapementItem( SvxEscapement::Superscript, EE_CHAR_ESCAPEMENT ) );
+ aEngine.QuickSetAttribs(aAttr, ESelection(0, 0, 0, aStr.getLength()));
+
+ // The cell will own the text object instance.
+ rDoc.SetEditText(aRanges[0].aStart, aEngine.CreateTextObject());
+ }
+ }
+ }
+ break;
+ case SC_WID_UNO_CHCOLHDR:
+ // chart header flags are set for this object, not stored with document
+ bChartColAsHdr = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ break;
+ case SC_WID_UNO_CHROWHDR:
+ bChartRowAsHdr = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ break;
+ case SC_WID_UNO_CELLSTYL:
+ {
+ OUString aStrVal;
+ aValue >>= aStrVal;
+ OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName(
+ aStrVal, SfxStyleFamily::Para ));
+ pDocShell->GetDocFunc().ApplyStyle( *GetMarkData(), aString, true );
+ }
+ break;
+ case SC_WID_UNO_TBLBORD:
+ {
+ table::TableBorder aBorder;
+ if ( !aRanges.empty() && ( aValue >>= aBorder ) ) // empty = nothing to do
+ {
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder );
+
+ ScHelperFunctions::ApplyBorder( pDocShell, aRanges, aOuter, aInner ); //! docfunc
+ }
+ }
+ break;
+ case SC_WID_UNO_TBLBORD2:
+ {
+ table::TableBorder2 aBorder2;
+ if ( !aRanges.empty() && ( aValue >>= aBorder2 ) ) // empty = nothing to do
+ {
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ ScHelperFunctions::FillBoxItems( aOuter, aInner, aBorder2 );
+
+ ScHelperFunctions::ApplyBorder( pDocShell, aRanges, aOuter, aInner ); //! docfunc
+ }
+ }
+ break;
+ case SC_WID_UNO_CONDFMT:
+ case SC_WID_UNO_CONDLOC:
+ case SC_WID_UNO_CONDXML:
+ {
+ uno::Reference<sheet::XSheetConditionalEntries> xInterface(aValue, uno::UNO_QUERY);
+ if ( !aRanges.empty() && xInterface.is() ) // empty = nothing to do
+ {
+ ScTableConditionalFormat* pFormat =
+ dynamic_cast<ScTableConditionalFormat*>( xInterface.get() );
+ if (pFormat)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bEnglish = ( pEntry->nWID != SC_WID_UNO_CONDLOC );
+ bool bXML = ( pEntry->nWID == SC_WID_UNO_CONDXML );
+ formula::FormulaGrammar::Grammar eGrammar = (bXML ?
+ formula::FormulaGrammar::GRAM_UNSPECIFIED :
+ formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML));
+
+ SCTAB nTab = aRanges.front().aStart.Tab();
+ // To remove conditional formats for all cells in aRanges we need to:
+ // Remove conditional format data from cells' attributes
+ rDoc.RemoveCondFormatData( aRanges, nTab, 0 );
+ // And also remove ranges from conditional formats list
+ for (size_t i = 0; i < aRanges.size(); ++i)
+ {
+ rDoc.GetCondFormList( aRanges[i].aStart.Tab() )->DeleteArea(
+ aRanges[i].aStart.Col(), aRanges[i].aStart.Row(),
+ aRanges[i].aEnd.Col(), aRanges[i].aEnd.Row() );
+ }
+
+ // Then we can apply new conditional format if there is one
+ if (pFormat->getCount())
+ {
+ auto pNew = std::make_unique<ScConditionalFormat>( 0, &rDoc ); // Index will be set on inserting
+ pFormat->FillFormat( *pNew, rDoc, eGrammar );
+ pNew->SetRange( aRanges );
+ pDocShell->GetDocFunc().ReplaceConditionalFormat( 0, std::move(pNew), nTab, aRanges );
+ }
+
+ // and repaint
+ for (size_t i = 0; i < aRanges.size(); ++i)
+ pDocShell->PostPaint(aRanges[i], PaintPartFlags::Grid);
+ pDocShell->SetDocumentModified();
+ }
+ }
+ }
+ break;
+ case SC_WID_UNO_VALIDAT:
+ case SC_WID_UNO_VALILOC:
+ case SC_WID_UNO_VALIXML:
+ {
+ uno::Reference<beans::XPropertySet> xInterface(aValue, uno::UNO_QUERY);
+ if ( !aRanges.empty() && xInterface.is() ) // empty = nothing to do
+ {
+ ScTableValidationObj* pValidObj =
+ dynamic_cast<ScTableValidationObj*>( xInterface.get() );
+ if (pValidObj)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bEnglish = ( pEntry->nWID != SC_WID_UNO_VALILOC );
+ bool bXML = ( pEntry->nWID == SC_WID_UNO_VALIXML );
+ formula::FormulaGrammar::Grammar eGrammar = (bXML ?
+ formula::FormulaGrammar::GRAM_UNSPECIFIED :
+ formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML));
+
+ std::unique_ptr<ScValidationData> pNewData(
+ pValidObj->CreateValidationData( rDoc, eGrammar ));
+ sal_uInt32 nIndex = rDoc.AddValidationEntry( *pNewData );
+ pNewData.reset();
+
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nIndex ) );
+ pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), aPattern, true );
+ }
+ }
+ }
+ break;
+ // SC_WID_UNO_NUMRULES is ignored...
+ }
+}
+
+uno::Any SAL_CALL ScCellRangesBase::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell || aRanges.empty() )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+ GetOnePropertyValue( pEntry, aAny );
+ return aAny;
+}
+
+void ScCellRangesBase::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny )
+{
+ if ( !pEntry )
+ return;
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ SfxItemSet* pDataSet = GetCurrentDataSet();
+ if ( pDataSet )
+ {
+ switch ( pEntry->nWID ) // for special handling of items
+ {
+ case ATTR_VALUE_FORMAT:
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ sal_uLong nOldFormat =
+ pDataSet->Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType eOldLang =
+ pDataSet->Get( ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ nOldFormat = rDoc.GetFormatTable()->
+ GetFormatForLanguageIfBuiltIn( nOldFormat, eOldLang );
+ rAny <<= static_cast<sal_Int32>(nOldFormat);
+ }
+ break;
+ case ATTR_INDENT:
+ rAny <<= static_cast<sal_Int16>( convertTwipToMm100(static_cast<const ScIndentItem&>(
+ pDataSet->Get(pEntry->nWID)).GetValue()) );
+ break;
+ case ATTR_STACKED:
+ {
+ Degree100 nRot = pDataSet->Get(ATTR_ROTATE_VALUE).GetValue();
+ bool bStacked = static_cast<const ScVerticalStackCell&>(pDataSet->Get(pEntry->nWID)).GetValue();
+ SvxOrientationItem( nRot, bStacked, TypedWhichId<SvxOrientationItem>(0) ).QueryValue( rAny );
+ }
+ break;
+ default:
+ pPropSet->getPropertyValue(*pEntry, *pDataSet, rAny);
+ }
+ }
+ }
+ else // implemented here
+ switch ( pEntry->nWID )
+ {
+ case SC_WID_UNO_CHCOLHDR:
+ rAny <<= bChartColAsHdr;
+ break;
+ case SC_WID_UNO_CHROWHDR:
+ rAny <<= bChartRowAsHdr;
+ break;
+ case SC_WID_UNO_CELLSTYL:
+ {
+ OUString aStyleName;
+ const ScStyleSheet* pStyle = pDocShell->GetDocument().GetSelectionStyle(*GetMarkData());
+ if (pStyle)
+ aStyleName = pStyle->GetName();
+ rAny <<= ScStyleNameConversion::DisplayToProgrammaticName(
+ aStyleName, SfxStyleFamily::Para );
+ }
+ break;
+ case SC_WID_UNO_TBLBORD:
+ case SC_WID_UNO_TBLBORD2:
+ {
+ //! loop through all ranges
+ if ( !aRanges.empty() )
+ {
+ const ScRange & rFirst = aRanges[ 0 ];
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( rFirst );
+ aMark.SelectTable( rFirst.aStart.Tab(), true );
+ rDoc.GetSelectionFrame( aMark, aOuter, aInner );
+
+ if (pEntry->nWID == SC_WID_UNO_TBLBORD2)
+ ScHelperFunctions::AssignTableBorder2ToAny( rAny, aOuter, aInner);
+ else
+ ScHelperFunctions::AssignTableBorderToAny( rAny, aOuter, aInner);
+ }
+ }
+ break;
+ case SC_WID_UNO_CONDFMT:
+ case SC_WID_UNO_CONDLOC:
+ case SC_WID_UNO_CONDXML:
+ {
+ const ScPatternAttr* pPattern = GetCurrentAttrsDeep();
+ if ( pPattern )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bEnglish = ( pEntry->nWID != SC_WID_UNO_CONDLOC );
+ bool bXML = ( pEntry->nWID == SC_WID_UNO_CONDXML );
+ formula::FormulaGrammar::Grammar eGrammar = (bXML ?
+ rDoc.GetStorageGrammar() :
+ formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML));
+ const ScCondFormatIndexes& rIndex =
+ pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+ sal_uLong nIndex = 0;
+ if(!rIndex.empty())
+ nIndex = rIndex[0];
+ rAny <<= uno::Reference<sheet::XSheetConditionalEntries>(
+ new ScTableConditionalFormat( &rDoc, nIndex, aRanges.front().aStart.Tab(), eGrammar ));
+ }
+ }
+ break;
+ case SC_WID_UNO_VALIDAT:
+ case SC_WID_UNO_VALILOC:
+ case SC_WID_UNO_VALIXML:
+ {
+ const ScPatternAttr* pPattern = GetCurrentAttrsDeep();
+ if ( pPattern )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bEnglish = ( pEntry->nWID != SC_WID_UNO_VALILOC );
+ bool bXML = ( pEntry->nWID == SC_WID_UNO_VALIXML );
+ formula::FormulaGrammar::Grammar eGrammar = (bXML ?
+ rDoc.GetStorageGrammar() :
+ formula::FormulaGrammar::mapAPItoGrammar( bEnglish, bXML));
+ sal_uLong nIndex =
+ pPattern->GetItem(ATTR_VALIDDATA).GetValue();
+ rAny <<= uno::Reference<beans::XPropertySet>(
+ new ScTableValidationObj( rDoc, nIndex, eGrammar ));
+ }
+ }
+ break;
+ case SC_WID_UNO_NUMRULES:
+ {
+ // always return empty numbering rules object
+ rAny <<= ScStyleObj::CreateEmptyNumberingRules();
+ }
+ break;
+ case SC_WID_UNO_ABSNAME:
+ {
+ OUString sRet;
+ aRanges.Format(sRet, ScRefFlags::RANGE_ABS_3D, pDocShell->GetDocument());
+ rAny <<= sRet;
+ }
+ break;
+ case SC_WID_UNO_FORMATID:
+ {
+ const ScPatternAttr* pPattern = GetCurrentAttrsFlat();
+ rAny <<= pPattern->GetPAKey();
+ }
+ break;
+ }
+}
+
+void SAL_CALL ScCellRangesBase::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SolarMutexGuard aGuard;
+ if ( aRanges.empty() )
+ throw uno::RuntimeException();
+
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScCellRangesBase::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SolarMutexGuard aGuard;
+ if ( aRanges.empty() )
+ throw uno::RuntimeException();
+
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScCellRangesBase::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScCellRangesBase::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ OSL_FAIL("not implemented");
+}
+
+// XMultiPropertySet
+
+void SAL_CALL ScCellRangesBase::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames,
+ const uno::Sequence< uno::Any >& aValues )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nCount(aPropertyNames.getLength());
+ sal_Int32 nValues(aValues.getLength());
+ if (nCount != nValues)
+ throw lang::IllegalArgumentException();
+
+ if ( !(pDocShell && nCount) )
+ return;
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+ const OUString* pNames = aPropertyNames.getConstArray();
+ const uno::Any* pValues = aValues.getConstArray();
+
+ std::unique_ptr<const SfxItemPropertyMapEntry*[]> pEntryArray(new const SfxItemPropertyMapEntry*[nCount]);
+
+ sal_Int32 i;
+ for(i = 0; i < nCount; i++)
+ {
+ // first loop: find all properties in map, but handle only CellStyle
+ // (CellStyle must be set before any other cell properties)
+
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( pNames[i] );
+ pEntryArray[i] = pEntry;
+ if (pEntry)
+ {
+ if ( pEntry->nWID == SC_WID_UNO_CELLSTYL )
+ {
+ try
+ {
+ SetOnePropertyValue( pEntry, pValues[i] );
+ }
+ catch ( lang::IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "exception when setting cell style"); // not supposed to happen
+ }
+ }
+ }
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ std::unique_ptr<ScPatternAttr> pOldPattern;
+ std::unique_ptr<ScPatternAttr> pNewPattern;
+
+ for(i = 0; i < nCount; i++)
+ {
+ // second loop: handle other properties
+
+ const SfxItemPropertyMapEntry* pEntry = pEntryArray[i];
+ if ( pEntry )
+ {
+ if ( IsScItemWid( pEntry->nWID ) ) // can be handled by SfxItemPropertySet
+ {
+ if ( !pOldPattern )
+ {
+ pOldPattern.reset(new ScPatternAttr( *GetCurrentAttrsDeep() ));
+ pOldPattern->GetItemSet().ClearInvalidItems();
+ pNewPattern.reset(new ScPatternAttr( rDoc.GetPool() ));
+ }
+
+ // collect items in pNewPattern, apply with one call after the loop
+
+ sal_uInt16 nFirstItem, nSecondItem;
+ lcl_SetCellProperty( *pEntry, pValues[i], *pOldPattern, rDoc, nFirstItem, nSecondItem );
+
+ // put only affected items into new set
+ if ( nFirstItem )
+ pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nFirstItem ) );
+ if ( nSecondItem )
+ pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nSecondItem ) );
+ }
+ else if ( pEntry->nWID != SC_WID_UNO_CELLSTYL ) // CellStyle is handled above
+ {
+ // call virtual method to set a single property
+ SetOnePropertyValue( pEntry, pValues[i] );
+ }
+ }
+ }
+
+ if ( pNewPattern && !aRanges.empty() )
+ pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), *pNewPattern, true );
+}
+
+uno::Sequence<uno::Any> SAL_CALL ScCellRangesBase::getPropertyValues(
+ const uno::Sequence< OUString >& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+
+ uno::Sequence<uno::Any> aRet(aPropertyNames.getLength());
+ uno::Any* pProperties = aRet.getArray();
+ for(sal_Int32 i = 0; i < aPropertyNames.getLength(); i++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyNames[i] );
+ GetOnePropertyValue( pEntry, pProperties[i] );
+ }
+ return aRet;
+}
+
+void SAL_CALL ScCellRangesBase::addPropertiesChangeListener( const uno::Sequence< OUString >& /* aPropertyNames */,
+ const uno::Reference< beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScCellRangesBase::removePropertiesChangeListener( const uno::Reference< beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScCellRangesBase::firePropertiesChangeEvent( const uno::Sequence< OUString >& /* aPropertyNames */,
+ const uno::Reference< beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+IMPL_LINK( ScCellRangesBase, ValueListenerHdl, const SfxHint&, rHint, void )
+{
+ if ( pDocShell && (rHint.GetId() == SfxHintId::ScDataChanged))
+ {
+ // This may be called several times for a single change, if several formulas
+ // in the range are notified. So only a flag is set that is checked when
+ // SfxHintId::DataChanged is received.
+
+ bGotDataChangedHint = true;
+ }
+}
+
+// XTolerantMultiPropertySet
+uno::Sequence< beans::SetPropertyTolerantFailed > SAL_CALL ScCellRangesBase::setPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames,
+ const uno::Sequence< uno::Any >& aValues )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nCount(aPropertyNames.getLength());
+ sal_Int32 nValues(aValues.getLength());
+ if (nCount != nValues)
+ throw lang::IllegalArgumentException();
+
+ if ( pDocShell && nCount )
+ {
+ uno::Sequence < beans::SetPropertyTolerantFailed > aReturns(nCount);
+ beans::SetPropertyTolerantFailed* pReturns = aReturns.getArray();
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+ const OUString* pNames = aPropertyNames.getConstArray();
+ const uno::Any* pValues = aValues.getConstArray();
+
+ std::unique_ptr<const SfxItemPropertyMapEntry*[]> pMapArray(new const SfxItemPropertyMapEntry*[nCount]);
+
+ sal_Int32 i;
+ for(i = 0; i < nCount; i++)
+ {
+ // first loop: find all properties in map, but handle only CellStyle
+ // (CellStyle must be set before any other cell properties)
+
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( pNames[i] );
+ pMapArray[i] = pEntry;
+ if (pEntry)
+ {
+ if ( pEntry->nWID == SC_WID_UNO_CELLSTYL )
+ {
+ try
+ {
+ SetOnePropertyValue( pEntry, pValues[i] );
+ }
+ catch ( lang::IllegalArgumentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "exception when setting cell style"); // not supposed to happen
+ }
+ }
+ }
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ std::unique_ptr<ScPatternAttr> pOldPattern;
+ std::unique_ptr<ScPatternAttr> pNewPattern;
+
+ sal_Int32 nFailed(0);
+ for(i = 0; i < nCount; i++)
+ {
+ // second loop: handle other properties
+
+ const SfxItemPropertyMapEntry* pEntry = pMapArray[i];
+ if ( pEntry && ((pEntry->nFlags & beans::PropertyAttribute::READONLY) == 0))
+ {
+ if ( IsScItemWid( pEntry->nWID ) ) // can be handled by SfxItemPropertySet
+ {
+ if ( !pOldPattern )
+ {
+ pOldPattern.reset(new ScPatternAttr( *GetCurrentAttrsDeep() ));
+ pOldPattern->GetItemSet().ClearInvalidItems();
+ pNewPattern.reset(new ScPatternAttr( rDoc.GetPool() ));
+ }
+
+ // collect items in pNewPattern, apply with one call after the loop
+ try
+ {
+ sal_uInt16 nFirstItem, nSecondItem;
+ lcl_SetCellProperty( *pEntry, pValues[i], *pOldPattern, rDoc, nFirstItem, nSecondItem );
+
+ // put only affected items into new set
+ if ( nFirstItem )
+ pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nFirstItem ) );
+ if ( nSecondItem )
+ pNewPattern->GetItemSet().Put( pOldPattern->GetItemSet().Get( nSecondItem ) );
+ }
+ catch ( lang::IllegalArgumentException& )
+ {
+ pReturns[nFailed].Name = pNames[i];
+ pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT;
+ }
+ }
+ else if ( pEntry->nWID != SC_WID_UNO_CELLSTYL ) // CellStyle is handled above
+ {
+ // call virtual method to set a single property
+ try
+ {
+ SetOnePropertyValue( pEntry, pValues[i] );
+ }
+ catch ( lang::IllegalArgumentException& )
+ {
+ pReturns[nFailed].Name = pNames[i];
+ pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT;
+ }
+ }
+ }
+ else
+ {
+ pReturns[nFailed].Name = pNames[i];
+ if (pEntry)
+ pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ else
+ pReturns[nFailed++].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ }
+
+ if ( pNewPattern && !aRanges.empty() )
+ pDocShell->GetDocFunc().ApplyAttributes( *GetMarkData(), *pNewPattern, true );
+
+ aReturns.realloc(nFailed);
+
+ return aReturns;
+ }
+ return uno::Sequence < beans::SetPropertyTolerantFailed >();
+}
+
+uno::Sequence< beans::GetPropertyTolerantResult > SAL_CALL ScCellRangesBase::getPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nCount(aPropertyNames.getLength());
+ uno::Sequence < beans::GetPropertyTolerantResult > aReturns(nCount);
+ beans::GetPropertyTolerantResult* pReturns = aReturns.getArray();
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+
+ for(sal_Int32 i = 0; i < nCount; i++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyNames[i] );
+ if (!pEntry)
+ {
+ pReturns[i].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ else
+ {
+ sal_uInt16 nItemWhich = 0;
+ lcl_GetPropertyWhich( pEntry, nItemWhich );
+ pReturns[i].State = GetOnePropertyState( nItemWhich, pEntry );
+ GetOnePropertyValue( pEntry, pReturns[i].Value );
+ pReturns[i].Result = beans::TolerantPropertySetResultType::SUCCESS;
+ }
+ }
+ return aReturns;
+}
+
+uno::Sequence< beans::GetDirectPropertyTolerantResult > SAL_CALL ScCellRangesBase::getDirectPropertyValuesTolerant( const uno::Sequence< OUString >& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nCount(aPropertyNames.getLength());
+ uno::Sequence < beans::GetDirectPropertyTolerantResult > aReturns(nCount);
+ beans::GetDirectPropertyTolerantResult* pReturns = aReturns.getArray();
+
+ const SfxItemPropertyMap& rPropertyMap = GetItemPropertyMap(); // from derived class
+
+ sal_Int32 j = 0;
+ for(sal_Int32 i = 0; i < nCount; i++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyNames[i] );
+ if (!pEntry)
+ {
+ pReturns[i].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ else
+ {
+ sal_uInt16 nItemWhich = 0;
+ lcl_GetPropertyWhich( pEntry, nItemWhich );
+ pReturns[j].State = GetOnePropertyState( nItemWhich, pEntry );
+ if (pReturns[j].State == beans::PropertyState_DIRECT_VALUE)
+ {
+ GetOnePropertyValue( pEntry, pReturns[j].Value );
+ pReturns[j].Result = beans::TolerantPropertySetResultType::SUCCESS;
+ pReturns[j].Name = aPropertyNames[i];
+ ++j;
+ }
+ }
+ }
+ if (j < nCount)
+ aReturns.realloc(j);
+ return aReturns;
+}
+
+// XIndent
+
+void SAL_CALL ScCellRangesBase::decrementIndent()
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell && !aRanges.empty() )
+ {
+ //#97041#; put only MultiMarked ScMarkData in ChangeIndent
+ ScMarkData aMarkData(*GetMarkData());
+ aMarkData.MarkToMulti();
+ pDocShell->GetDocFunc().ChangeIndent( aMarkData, false, true );
+ }
+}
+
+void SAL_CALL ScCellRangesBase::incrementIndent()
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell && !aRanges.empty() )
+ {
+ //#97041#; put only MultiMarked ScMarkData in ChangeIndent
+ ScMarkData aMarkData(*GetMarkData());
+ aMarkData.MarkToMulti();
+ pDocShell->GetDocFunc().ChangeIndent( aMarkData, true, true );
+ }
+}
+
+// XChartData
+
+std::unique_ptr<ScMemChart> ScCellRangesBase::CreateMemChart_Impl() const
+{
+ if ( pDocShell && !aRanges.empty() )
+ {
+ ScRangeListRef xChartRanges;
+ if ( aRanges.size() == 1 )
+ {
+ // set useful table limit (only occupied data area)
+ // (only here - Listeners are registered for the whole area)
+ //! check immediately if a ScTableSheetObj?
+
+ const ScDocument & rDoc = pDocShell->GetDocument();
+ const ScRange & rRange = aRanges[0];
+ if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
+ rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() )
+ {
+ SCTAB nTab = rRange.aStart.Tab();
+
+ SCCOL nStartX;
+ SCROW nStartY; // Get start
+ if (!pDocShell->GetDocument().GetDataStart( nTab, nStartX, nStartY ))
+ {
+ nStartX = 0;
+ nStartY = 0;
+ }
+
+ SCCOL nEndX;
+ SCROW nEndY; // Get end
+ if (!pDocShell->GetDocument().GetTableArea( nTab, nEndX, nEndY ))
+ {
+ nEndX = 0;
+ nEndY = 0;
+ }
+
+ xChartRanges = new ScRangeList( ScRange( nStartX, nStartY, nTab, nEndX, nEndY, nTab ) );
+ }
+ }
+ if (!xChartRanges.is()) // otherwise take Ranges directly
+ xChartRanges = new ScRangeList(aRanges);
+ ScChartArray aArr( pDocShell->GetDocument(), xChartRanges );
+
+ // RowAsHdr = ColHeaders and vice versa
+ aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr );
+
+ return aArr.CreateMemChart();
+ }
+ return nullptr;
+}
+
+uno::Sequence< uno::Sequence<double> > SAL_CALL ScCellRangesBase::getData()
+{
+ SolarMutexGuard aGuard;
+ std::unique_ptr<ScMemChart> pMemChart(CreateMemChart_Impl());
+ if ( pMemChart )
+ {
+ sal_Int32 nColCount = pMemChart->GetColCount();
+ sal_Int32 nRowCount = static_cast<sal_Int32>(pMemChart->GetRowCount());
+
+ uno::Sequence< uno::Sequence<double> > aRowSeq( nRowCount );
+ uno::Sequence<double>* pRowAry = aRowSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ {
+ uno::Sequence<double> aColSeq( nColCount );
+ double* pColAry = aColSeq.getArray();
+ for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
+ pColAry[nCol] = pMemChart->GetData( nCol, nRow );
+
+ pRowAry[nRow] = aColSeq;
+ }
+
+ return aRowSeq;
+ }
+
+ return {};
+}
+
+ScRangeListRef ScCellRangesBase::GetLimitedChartRanges_Impl( sal_Int32 nDataColumns, sal_Int32 nDataRows ) const
+{
+ if ( aRanges.size() == 1 )
+ {
+ const ScDocument & rDoc = pDocShell->GetDocument();
+ const ScRange & rRange = aRanges[0];
+ if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() &&
+ rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() )
+ {
+ // if aRanges is a complete sheet, limit to given size
+
+ SCTAB nTab = rRange.aStart.Tab();
+
+ sal_Int32 nEndColumn = nDataColumns - 1 + ( bChartColAsHdr ? 1 : 0 );
+ if ( nEndColumn < 0 )
+ nEndColumn = 0;
+ if ( nEndColumn > rDoc.MaxCol() )
+ nEndColumn = rDoc.MaxCol();
+
+ sal_Int32 nEndRow = nDataRows - 1 + ( bChartRowAsHdr ? 1 : 0 );
+ if ( nEndRow < 0 )
+ nEndRow = 0;
+ if ( nEndRow > rDoc.MaxRow() )
+ nEndRow = rDoc.MaxRow();
+
+ ScRangeListRef xChartRanges = new ScRangeList( ScRange( 0, 0, nTab, static_cast<SCCOL>(nEndColumn), static_cast<SCROW>(nEndRow), nTab ) );
+ return xChartRanges;
+ }
+ }
+
+ return new ScRangeList(aRanges); // as-is
+}
+
+void SAL_CALL ScCellRangesBase::setData( const uno::Sequence< uno::Sequence<double> >& aData )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ sal_Int32 nRowCount = aData.getLength();
+ sal_Int32 nColCount = nRowCount ? aData[0].getLength() : 0;
+ ScRangeListRef xChartRanges = GetLimitedChartRanges_Impl( nColCount, nRowCount );
+ if ( pDocShell && xChartRanges.is() )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChartArray aArr( rDoc, xChartRanges );
+ aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); // RowAsHdr = ColHeaders
+ const ScChartPositionMap* pPosMap = aArr.GetPositionMap();
+ if (pPosMap)
+ {
+ if ( pPosMap->GetColCount() == static_cast<SCCOL>(nColCount) &&
+ pPosMap->GetRowCount() == static_cast<SCROW>(nRowCount) )
+ {
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ const uno::Sequence<double>& rRowSeq = aData[nRow];
+ const double* pArray = rRowSeq.getConstArray();
+ nColCount = rRowSeq.getLength();
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ {
+ const ScAddress* pPos = pPosMap->GetPosition(
+ sal::static_int_cast<SCCOL>(nCol),
+ sal::static_int_cast<SCROW>(nRow) );
+ if (pPos)
+ {
+ double fVal = pArray[nCol];
+ if ( fVal == DBL_MIN )
+ rDoc.SetEmptyCell(*pPos);
+ else
+ rDoc.SetValue(*pPos, pArray[nCol]);
+ }
+ }
+ }
+
+ //! undo
+ PaintGridRanges_Impl();
+ pDocShell->SetDocumentModified();
+ ForceChartListener_Impl(); // call listeners for this object synchronously
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellRangesBase::getRowDescriptions()
+{
+ SolarMutexGuard aGuard;
+ std::unique_ptr<ScMemChart> pMemChart(CreateMemChart_Impl());
+ if ( pMemChart )
+ {
+ sal_Int32 nRowCount = static_cast<sal_Int32>(pMemChart->GetRowCount());
+ uno::Sequence<OUString> aSeq( nRowCount );
+ OUString* pAry = aSeq.getArray();
+ for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
+ pAry[nRow] = pMemChart->GetRowText(nRow);
+
+ return aSeq;
+ }
+ return {};
+}
+
+void SAL_CALL ScCellRangesBase::setRowDescriptions(
+ const uno::Sequence<OUString>& aRowDescriptions )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if ( bChartColAsHdr )
+ {
+ sal_Int32 nRowCount = aRowDescriptions.getLength();
+ ScRangeListRef xChartRanges = GetLimitedChartRanges_Impl( 1, nRowCount );
+ if ( pDocShell && xChartRanges.is() )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChartArray aArr( rDoc, xChartRanges );
+ aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); // RowAsHdr = ColHeaders
+ const ScChartPositionMap* pPosMap = aArr.GetPositionMap();
+ if (pPosMap)
+ {
+ if ( pPosMap->GetRowCount() == static_cast<SCROW>(nRowCount) )
+ {
+ const OUString* pArray = aRowDescriptions.getConstArray();
+ for (sal_Int32 nRow=0; nRow<nRowCount; nRow++)
+ {
+ const ScAddress* pPos = pPosMap->GetRowHeaderPosition(
+ static_cast<SCSIZE>(nRow) );
+ if (pPos)
+ {
+ const OUString& aStr = pArray[nRow];
+ if (aStr.isEmpty())
+ rDoc.SetEmptyCell(*pPos);
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(*pPos, aStr, &aParam);
+ }
+ }
+ }
+
+ //! undo
+ PaintGridRanges_Impl();
+ pDocShell->SetDocumentModified();
+ ForceChartListener_Impl(); // call listeners for this object synchronously
+ bDone = true;
+ }
+ }
+ }
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellRangesBase::getColumnDescriptions()
+{
+ SolarMutexGuard aGuard;
+ std::unique_ptr<ScMemChart> pMemChart(CreateMemChart_Impl());
+ if ( pMemChart )
+ {
+ sal_Int32 nColCount = pMemChart->GetColCount();
+ uno::Sequence<OUString> aSeq( nColCount );
+ OUString* pAry = aSeq.getArray();
+ for (sal_Int32 nCol = 0; nCol < nColCount; nCol++)
+ pAry[nCol] = pMemChart->GetColText(nCol);
+
+ return aSeq;
+ }
+ return {};
+}
+
+void SAL_CALL ScCellRangesBase::setColumnDescriptions(
+ const uno::Sequence<OUString>& aColumnDescriptions )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if ( bChartRowAsHdr )
+ {
+ sal_Int32 nColCount = aColumnDescriptions.getLength();
+ ScRangeListRef xChartRanges = GetLimitedChartRanges_Impl( nColCount, 1 );
+ if ( pDocShell && xChartRanges.is() )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChartArray aArr( rDoc, xChartRanges );
+ aArr.SetHeaders( bChartRowAsHdr, bChartColAsHdr ); // RowAsHdr = ColHeaders
+ const ScChartPositionMap* pPosMap = aArr.GetPositionMap();
+ if (pPosMap)
+ {
+ if ( pPosMap->GetColCount() == static_cast<SCCOL>(nColCount) )
+ {
+ const OUString* pArray = aColumnDescriptions.getConstArray();
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ {
+ const ScAddress* pPos = pPosMap->GetColHeaderPosition(
+ sal::static_int_cast<SCCOL>(nCol) );
+ if (pPos)
+ {
+ const OUString& aStr = pArray[nCol];
+ if (aStr.isEmpty())
+ rDoc.SetEmptyCell(*pPos);
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rDoc.SetString(*pPos, aStr, &aParam);
+ }
+ }
+ }
+
+ //! undo
+ PaintGridRanges_Impl();
+ pDocShell->SetDocumentModified();
+ ForceChartListener_Impl(); // call listeners for this object synchronously
+ bDone = true;
+ }
+ }
+ }
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException();
+}
+
+void ScCellRangesBase::ForceChartListener_Impl()
+{
+ // call Update immediately so the caller to setData etc. can
+ // recognize the listener call
+
+ if (!pDocShell)
+ return;
+
+ ScChartListenerCollection* pColl = pDocShell->GetDocument().GetChartListenerCollection();
+ if (!pColl)
+ return;
+
+ ScChartListenerCollection::ListenersType& rListeners = pColl->getListeners();
+ for (auto const& it : rListeners)
+ {
+ ScChartListener *const p = it.second.get();
+ assert(p);
+ if (p->GetUnoSource() == static_cast<chart::XChartData*>(this) && p->IsDirty())
+ p->Update();
+ }
+}
+
+void SAL_CALL ScCellRangesBase::addChartDataChangeEventListener( const uno::Reference<
+ chart::XChartDataChangeEventListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+ if ( !pDocShell || aRanges.empty() )
+ return;
+
+ //! test for duplicates ?
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangeListRef aRangesRef( new ScRangeList(aRanges) );
+ ScChartListenerCollection* pColl = rDoc.GetChartListenerCollection();
+ OUString aName = pColl->getUniqueName(u"__Uno");
+ if (aName.isEmpty())
+ // failed to create unique name.
+ return;
+
+ ScChartListener* pListener = new ScChartListener( aName, rDoc, aRangesRef );
+ pListener->SetUno( aListener, this );
+ pColl->insert( pListener );
+ pListener->StartListeningTo();
+}
+
+void SAL_CALL ScCellRangesBase::removeChartDataChangeEventListener( const uno::Reference<
+ chart::XChartDataChangeEventListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell && !aRanges.empty() )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScChartListenerCollection* pColl = rDoc.GetChartListenerCollection();
+ pColl->FreeUno( aListener, this );
+ }
+}
+
+double SAL_CALL ScCellRangesBase::getNotANumber()
+{
+ // use DBL_MIN in ScChartArray, because Chart wants it so
+ return DBL_MIN;
+}
+
+sal_Bool SAL_CALL ScCellRangesBase::isNotANumber( double nNumber )
+{
+ // use DBL_MIN in ScChartArray, because Chart wants it so
+ return (nNumber == DBL_MIN);
+}
+
+// XModifyBroadcaster
+
+void SAL_CALL ScCellRangesBase::addModifyListener(const uno::Reference<util::XModifyListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+ if ( aRanges.empty() )
+ throw uno::RuntimeException();
+
+ aValueListeners.emplace_back( aListener );
+
+ if ( aValueListeners.size() == 1 )
+ {
+ if (!pValueListener)
+ pValueListener.reset( new ScLinkListener( LINK( this, ScCellRangesBase, ValueListenerHdl ) ) );
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ for ( size_t i = 0, nCount = aRanges.size(); i < nCount; i++)
+ rDoc.StartListeningArea( aRanges[ i ], false, pValueListener.get() );
+
+ acquire(); // don't lose this object (one ref for all listeners)
+ }
+}
+
+void SAL_CALL ScCellRangesBase::removeModifyListener( const uno::Reference<util::XModifyListener>& aListener )
+{
+
+ SolarMutexGuard aGuard;
+ if ( aRanges.empty() )
+ throw uno::RuntimeException();
+
+ rtl::Reference<ScCellRangesBase> xSelfHold(this); // in case the listeners have the last ref
+
+ sal_uInt16 nCount = aValueListeners.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ {
+ uno::Reference<util::XModifyListener>& rObj = aValueListeners[n];
+ if ( rObj == aListener )
+ {
+ aValueListeners.erase( aValueListeners.begin() + n );
+
+ if ( aValueListeners.empty() )
+ {
+ if (pValueListener)
+ pValueListener->EndListeningAll();
+
+ release(); // release the ref for the listeners
+ }
+
+ break;
+ }
+ }
+}
+
+// XCellRangesQuery
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryVisibleCells()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ //! Separate for all tables, if markings separated per table
+ SCTAB nTab = lcl_FirstTab(aRanges);
+
+ ScMarkData aMarkData(*GetMarkData());
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCCOL nCol = 0, nLastCol;
+ while (nCol <= rDoc.MaxCol())
+ {
+ if (rDoc.ColHidden(nCol, nTab, nullptr, &nLastCol))
+ // hidden columns. Deselect them.
+ aMarkData.SetMultiMarkArea(ScRange(nCol, 0, nTab, nLastCol, rDoc.MaxRow(), nTab), false);
+
+ nCol = nLastCol + 1;
+ }
+
+ SCROW nRow = 0, nLastRow;
+ while (nRow <= rDoc.MaxRow())
+ {
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ // These rows are hidden. Deselect them.
+ aMarkData.SetMultiMarkArea(ScRange(0, nRow, nTab, rDoc.MaxCol(), nLastRow, nTab), false);
+
+ nRow = nLastRow + 1;
+ }
+
+ ScRangeList aNewRanges;
+ aMarkData.FillRangeListWithMarks( &aNewRanges, false );
+ return new ScCellRangesObj( pDocShell, aNewRanges );
+ }
+
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryEmptyCells()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScMarkData aMarkData(*GetMarkData());
+
+ // mark occupied cells
+ for (size_t i = 0, nCount = aRanges.size(); i < nCount; ++i)
+ {
+ ScRange const & rRange = aRanges[ i ];
+
+ ScCellIterator aIter(rDoc, rRange);
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ // notes count as non-empty
+ if (!aIter.isEmpty())
+ aMarkData.SetMultiMarkArea(aIter.GetPos(), false);
+ }
+ }
+
+ ScRangeList aNewRanges;
+ // IsMultiMarked is not enough (will not be reset during deselecting)
+ //if (aMarkData.HasAnyMultiMarks()) // #i20044# should be set for all empty range
+ aMarkData.FillRangeListWithMarks( &aNewRanges, false );
+
+ return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty
+ }
+
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryContentCells(
+ sal_Int16 nContentFlags )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+
+ // select matching cells
+ for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i )
+ {
+ ScRange const & rRange = aRanges[ i ];
+
+ ScCellIterator aIter(rDoc, rRange);
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ bool bAdd = false;
+ switch (aIter.getType())
+ {
+ case CELLTYPE_STRING:
+ if ( nContentFlags & sheet::CellFlags::STRING )
+ bAdd = true;
+ break;
+ case CELLTYPE_EDIT:
+ if ( (nContentFlags & sheet::CellFlags::STRING) || (nContentFlags & sheet::CellFlags::FORMATTED) )
+ bAdd = true;
+ break;
+ case CELLTYPE_FORMULA:
+ if ( nContentFlags & sheet::CellFlags::FORMULA )
+ bAdd = true;
+ break;
+ case CELLTYPE_VALUE:
+ if ( (nContentFlags & (sheet::CellFlags::VALUE|sheet::CellFlags::DATETIME))
+ == (sheet::CellFlags::VALUE|sheet::CellFlags::DATETIME) )
+ bAdd = true;
+ else
+ {
+ // date/time identification
+
+ sal_uLong nIndex = static_cast<sal_uLong>(rDoc.GetAttr(
+ aIter.GetPos(), ATTR_VALUE_FORMAT)->GetValue());
+ SvNumFormatType nTyp = rDoc.GetFormatTable()->GetType(nIndex);
+ if ((nTyp == SvNumFormatType::DATE) || (nTyp == SvNumFormatType::TIME) ||
+ (nTyp == SvNumFormatType::DATETIME))
+ {
+ if ( nContentFlags & sheet::CellFlags::DATETIME )
+ bAdd = true;
+ }
+ else
+ {
+ if ( nContentFlags & sheet::CellFlags::VALUE )
+ bAdd = true;
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (bAdd)
+ aMarkData.SetMultiMarkArea(aIter.GetPos());
+ }
+ }
+
+ if (nContentFlags & sheet::CellFlags::ANNOTATION)
+ {
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetNotesInRange(aRanges, aNotes);
+
+ for (const auto& i : aNotes)
+ {
+ aMarkData.SetMultiMarkArea(i.maPos);
+ }
+ }
+
+ ScRangeList aNewRanges;
+ if (aMarkData.IsMultiMarked())
+ aMarkData.FillRangeListWithMarks( &aNewRanges, false );
+
+ return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty
+ }
+
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryFormulaCells(
+ sal_Int32 nResultFlags )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+
+ // select matching cells
+ for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i )
+ {
+ ScRange const & rRange = aRanges[ i ];
+
+ ScCellIterator aIter(rDoc, rRange);
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ if (aIter.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aIter.getFormulaCell();
+ bool bAdd = false;
+ if (pFCell->GetErrCode() != FormulaError::NONE)
+ {
+ if ( nResultFlags & sheet::FormulaResult::ERROR )
+ bAdd = true;
+ }
+ else if (pFCell->IsValue())
+ {
+ if ( nResultFlags & sheet::FormulaResult::VALUE )
+ bAdd = true;
+ }
+ else // String
+ {
+ if ( nResultFlags & sheet::FormulaResult::STRING )
+ bAdd = true;
+ }
+
+ if (bAdd)
+ aMarkData.SetMultiMarkArea(aIter.GetPos());
+ }
+ }
+ }
+
+ ScRangeList aNewRanges;
+ if (aMarkData.IsMultiMarked())
+ aMarkData.FillRangeListWithMarks( &aNewRanges, false );
+
+ return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty
+ }
+
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellRanges> ScCellRangesBase::QueryDifferences_Impl(
+ const table::CellAddress& aCompare, bool bColumnDiff)
+{
+ if (pDocShell)
+ {
+ size_t nRangeCount = aRanges.size();
+ size_t i;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+
+ SCCOLROW nCmpPos = bColumnDiff ? static_cast<SCCOLROW>(aCompare.Row) : static_cast<SCCOLROW>(aCompare.Column);
+
+ // first select everything, where at all something is in the comparison column
+ // (in the second step the selection is cancelled for equal cells)
+
+ SCTAB nTab = lcl_FirstTab(aRanges); //! for all tables, if markings per table
+ ScRange aCmpRange, aCellRange;
+ if (bColumnDiff)
+ aCmpRange = ScRange( 0,nCmpPos,nTab, rDoc.MaxCol(),nCmpPos,nTab );
+ else
+ aCmpRange = ScRange( static_cast<SCCOL>(nCmpPos),0,nTab, static_cast<SCCOL>(nCmpPos),rDoc.MaxRow(),nTab );
+ ScCellIterator aCmpIter(rDoc, aCmpRange);
+ for (bool bHasCell = aCmpIter.first(); bHasCell; bHasCell = aCmpIter.next())
+ {
+ SCCOLROW nCellPos = bColumnDiff ? static_cast<SCCOLROW>(aCmpIter.GetPos().Col()) : static_cast<SCCOLROW>(aCmpIter.GetPos().Row());
+ if (bColumnDiff)
+ aCellRange = ScRange( static_cast<SCCOL>(nCellPos),0,nTab,
+ static_cast<SCCOL>(nCellPos),rDoc.MaxRow(),nTab );
+ else
+ aCellRange = ScRange( 0,nCellPos,nTab, rDoc.MaxCol(),nCellPos,nTab );
+
+ for (i=0; i<nRangeCount; i++)
+ {
+ ScRange aRange( aRanges[ i ] );
+ if ( aRange.Intersects( aCellRange ) )
+ {
+ if (bColumnDiff)
+ {
+ aRange.aStart.SetCol(static_cast<SCCOL>(nCellPos));
+ aRange.aEnd.SetCol(static_cast<SCCOL>(nCellPos));
+ }
+ else
+ {
+ aRange.aStart.SetRow(nCellPos);
+ aRange.aEnd.SetRow(nCellPos);
+ }
+ aMarkData.SetMultiMarkArea( aRange );
+ }
+ }
+ }
+
+ // compare all not empty cells with the comparison column and accordingly
+ // select or cancel
+
+ ScAddress aCmpAddr;
+ for (i=0; i<nRangeCount; i++)
+ {
+ ScRange const & rRange = aRanges[ i ];
+
+ ScCellIterator aIter( rDoc, rRange );
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ if (bColumnDiff)
+ aCmpAddr = ScAddress( aIter.GetPos().Col(), nCmpPos, aIter.GetPos().Tab() );
+ else
+ aCmpAddr = ScAddress( static_cast<SCCOL>(nCmpPos), aIter.GetPos().Row(), aIter.GetPos().Tab() );
+
+ ScRange aOneRange(aIter.GetPos());
+ if (!aIter.equalsWithoutFormat(aCmpAddr))
+ aMarkData.SetMultiMarkArea( aOneRange );
+ else
+ aMarkData.SetMultiMarkArea( aOneRange, false ); // deselect
+ }
+ }
+
+ ScRangeList aNewRanges;
+ if (aMarkData.IsMultiMarked())
+ aMarkData.FillRangeListWithMarks( &aNewRanges, false );
+
+ return new ScCellRangesObj( pDocShell, aNewRanges ); // aNewRanges can be empty
+ }
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellRanges > SAL_CALL ScCellRangesBase::queryColumnDifferences(
+ const table::CellAddress& aCompare )
+{
+ SolarMutexGuard aGuard;
+ return QueryDifferences_Impl( aCompare, true );
+}
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryRowDifferences(
+ const table::CellAddress& aCompare )
+{
+ SolarMutexGuard aGuard;
+ return QueryDifferences_Impl( aCompare, false );
+}
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryIntersection(
+ const table::CellRangeAddress& aRange )
+{
+ SolarMutexGuard aGuard;
+ ScRange aMask( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet,
+ static_cast<SCCOL>(aRange.EndColumn), static_cast<SCROW>(aRange.EndRow), aRange.Sheet );
+
+ ScRangeList aNew;
+ for ( size_t i = 0, nCount = aRanges.size(); i < nCount; ++i )
+ {
+ ScRange aTemp( aRanges[ i ] );
+ if ( aTemp.Intersects( aMask ) )
+ aNew.Join( ScRange( std::max( aTemp.aStart.Col(), aMask.aStart.Col() ),
+ std::max( aTemp.aStart.Row(), aMask.aStart.Row() ),
+ std::max( aTemp.aStart.Tab(), aMask.aStart.Tab() ),
+ std::min( aTemp.aEnd.Col(), aMask.aEnd.Col() ),
+ std::min( aTemp.aEnd.Row(), aMask.aEnd.Row() ),
+ std::min( aTemp.aEnd.Tab(), aMask.aEnd.Tab() ) ) );
+ }
+
+ return new ScCellRangesObj( pDocShell, aNew ); // can be empty
+}
+
+// XFormulaQuery
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryPrecedents(
+ sal_Bool bRecursive )
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScRangeList aNewRanges(aRanges);
+ bool bFound;
+ do
+ {
+ bFound = false;
+
+ // aMarkData uses aNewRanges, not aRanges, so GetMarkData can't be used
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+ aMarkData.MarkFromRangeList( aNewRanges, false );
+
+ for (size_t nR = 0, nCount = aNewRanges.size(); nR<nCount; ++nR)
+ {
+ ScRange const & rRange = aNewRanges[ nR];
+ ScCellIterator aIter(rDoc, rRange);
+ for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScDetectiveRefIter aRefIter(rDoc, aIter.getFormulaCell());
+ ScRange aRefRange;
+ while ( aRefIter.GetNextRef( aRefRange) )
+ {
+ if ( bRecursive && !bFound && !aMarkData.IsAllMarked( aRefRange ) )
+ bFound = true;
+ aMarkData.SetMultiMarkArea(aRefRange);
+ }
+ }
+ }
+
+ aMarkData.FillRangeListWithMarks( &aNewRanges, true );
+ }
+ while ( bRecursive && bFound );
+
+ return new ScCellRangesObj( pDocShell, aNewRanges );
+ }
+
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellRanges> SAL_CALL ScCellRangesBase::queryDependents(
+ sal_Bool bRecursive )
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScRangeList aNewRanges(aRanges);
+ bool bFound;
+ do
+ {
+ bFound = false;
+
+ // aMarkData uses aNewRanges, not aRanges, so GetMarkData can't be used
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+ aMarkData.MarkFromRangeList( aNewRanges, false );
+
+ SCTAB nTab = lcl_FirstTab(aNewRanges); //! all tables
+
+ ScCellIterator aCellIter( rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab) );
+ for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
+ {
+ if (aCellIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ bool bMark = false;
+ ScDetectiveRefIter aIter(rDoc, aCellIter.getFormulaCell());
+ ScRange aRefRange;
+ while ( aIter.GetNextRef( aRefRange) && !bMark )
+ {
+ size_t nRangesCount = aNewRanges.size();
+ for (size_t nR = 0; nR < nRangesCount; ++nR)
+ {
+ ScRange const & rRange = aNewRanges[ nR ];
+ if (rRange.Intersects(aRefRange))
+ {
+ bMark = true; // depending on part of Range
+ break;
+ }
+ }
+ }
+ if (bMark)
+ {
+ ScRange aCellRange(aCellIter.GetPos());
+ if ( bRecursive && !bFound && !aMarkData.IsAllMarked( aCellRange ) )
+ bFound = true;
+ aMarkData.SetMultiMarkArea(aCellRange);
+ }
+ }
+
+ aMarkData.FillRangeListWithMarks( &aNewRanges, true );
+ }
+ while ( bRecursive && bFound );
+
+ return new ScCellRangesObj( pDocShell, aNewRanges );
+ }
+
+ return nullptr;
+}
+
+// XSearchable
+
+uno::Reference<util::XSearchDescriptor> SAL_CALL ScCellRangesBase::createSearchDescriptor()
+{
+ return new ScCellSearchObj;
+}
+
+uno::Reference<container::XIndexAccess> SAL_CALL ScCellRangesBase::findAll(
+ const uno::Reference<util::XSearchDescriptor>& xDesc )
+{
+ SolarMutexGuard aGuard;
+ // should we return Null if nothing is found(?)
+ uno::Reference<container::XIndexAccess> xRet;
+ if ( pDocShell && xDesc.is() )
+ {
+ ScCellSearchObj* pSearch = dynamic_cast<ScCellSearchObj*>( xDesc.get() );
+ if (pSearch)
+ {
+ SvxSearchItem* pSearchItem = pSearch->GetSearchItem();
+ if (pSearchItem)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pSearchItem->SetCommand( SvxSearchCmd::FIND_ALL );
+ // always only within this object
+ pSearchItem->SetSelection( !lcl_WholeSheet(rDoc, aRanges) );
+
+ ScMarkData aMark(*GetMarkData());
+
+ OUString aDummyUndo;
+ ScRangeList aMatchedRanges;
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+ SCTAB nTab = 0;
+ bool bMatchedRangesWereClamped = false;
+ bool bFound = rDoc.SearchAndReplace(
+ *pSearchItem, nCol, nRow, nTab, aMark, aMatchedRanges, aDummyUndo, nullptr, bMatchedRangesWereClamped);
+ if (bFound)
+ {
+ // on findAll always CellRanges no matter how much has been found
+ xRet.set(new ScCellRangesObj( pDocShell, aMatchedRanges ));
+ }
+ }
+ }
+ }
+ return xRet;
+}
+
+uno::Reference<uno::XInterface> ScCellRangesBase::Find_Impl(
+ const uno::Reference<util::XSearchDescriptor>& xDesc,
+ const ScAddress* pLastPos )
+{
+ uno::Reference<uno::XInterface> xRet;
+ if ( pDocShell && xDesc.is() )
+ {
+ ScCellSearchObj* pSearch = dynamic_cast<ScCellSearchObj*>( xDesc.get() );
+ if (pSearch)
+ {
+ SvxSearchItem* pSearchItem = pSearch->GetSearchItem();
+ if (pSearchItem)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pSearchItem->SetCommand( SvxSearchCmd::FIND );
+ // only always in this object
+ pSearchItem->SetSelection( !lcl_WholeSheet(rDoc, aRanges) );
+
+ ScMarkData aMark(*GetMarkData());
+
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ if (pLastPos)
+ pLastPos->GetVars( nCol, nRow, nTab );
+ else
+ {
+ nTab = lcl_FirstTab(aRanges); //! multiple sheets?
+ rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow );
+ }
+
+ OUString aDummyUndo;
+ ScRangeList aMatchedRanges;
+ bool bMatchedRangesWereClamped;
+ bool bFound = rDoc.SearchAndReplace(
+ *pSearchItem, nCol, nRow, nTab, aMark, aMatchedRanges, aDummyUndo, nullptr, bMatchedRangesWereClamped);
+ if (bFound)
+ {
+ ScAddress aFoundPos( nCol, nRow, nTab );
+ xRet.set(cppu::getXWeak(new ScCellObj( pDocShell, aFoundPos )));
+ }
+ }
+ }
+ }
+ return xRet;
+}
+
+uno::Reference<uno::XInterface> SAL_CALL ScCellRangesBase::findFirst(
+ const uno::Reference<util::XSearchDescriptor>& xDesc )
+{
+ SolarMutexGuard aGuard;
+ return Find_Impl( xDesc, nullptr );
+}
+
+uno::Reference<uno::XInterface> SAL_CALL ScCellRangesBase::findNext(
+ const uno::Reference<uno::XInterface>& xStartAt,
+ const uno::Reference<util::XSearchDescriptor >& xDesc )
+{
+ SolarMutexGuard aGuard;
+ if ( xStartAt.is() )
+ {
+ ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xStartAt.get() );
+ if ( pRangesImp && pRangesImp->GetDocShell() == pDocShell )
+ {
+ const ScRangeList& rStartRanges = pRangesImp->GetRangeList();
+ if ( rStartRanges.size() == 1 )
+ {
+ ScAddress aStartPos = rStartRanges[ 0 ].aStart;
+ return Find_Impl( xDesc, &aStartPos );
+ }
+ }
+ }
+ return nullptr;
+}
+
+// XReplaceable
+
+uno::Reference<util::XReplaceDescriptor> SAL_CALL ScCellRangesBase::createReplaceDescriptor()
+{
+ return new ScCellSearchObj;
+}
+
+sal_Int32 SAL_CALL ScCellRangesBase::replaceAll( const uno::Reference<util::XSearchDescriptor>& xDesc )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt64 nReplaced = 0;
+ if ( pDocShell && xDesc.is() )
+ {
+ ScCellSearchObj* pSearch = dynamic_cast<ScCellSearchObj*>( xDesc.get() );
+ if (pSearch)
+ {
+ SvxSearchItem* pSearchItem = pSearch->GetSearchItem();
+ if (pSearchItem)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ pSearchItem->SetCommand( SvxSearchCmd::REPLACE_ALL );
+ // only always in this object
+ pSearchItem->SetSelection( !lcl_WholeSheet(rDoc, aRanges) );
+
+ ScMarkData aMark(*GetMarkData());
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bProtected = !pDocShell->IsEditable();
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+ if ( rDoc.IsTabProtected(rTab) )
+ bProtected = true;
+ }
+ if (bProtected)
+ {
+ //! Exception, or what?
+ }
+ else
+ {
+ SCTAB nTab = aMark.GetFirstSelected(); // do not use if SearchAndReplace
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+
+ OUString aUndoStr;
+ ScDocumentUniquePtr pUndoDoc;
+ if (bUndo)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ }
+ for (const auto& rTab : aMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+ if (rTab != nTab && bUndo)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+ std::unique_ptr<ScMarkData> pUndoMark;
+ if (bUndo)
+ pUndoMark.reset(new ScMarkData(aMark));
+
+ bool bFound = false;
+ if (bUndo)
+ {
+ ScRangeList aMatchedRanges;
+ bool bMatchedRangesWereClamped;
+ bFound = rDoc.SearchAndReplace(
+ *pSearchItem, nCol, nRow, nTab, aMark, aMatchedRanges, aUndoStr, pUndoDoc.get(), bMatchedRangesWereClamped );
+ }
+ if (bFound)
+ {
+ nReplaced = pUndoDoc->GetCellCount();
+
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoReplace>( pDocShell, *pUndoMark, nCol, nRow, nTab,
+ aUndoStr, std::move(pUndoDoc), pSearchItem ) );
+
+ pDocShell->PostPaintGridAll();
+ pDocShell->SetDocumentModified();
+ }
+ }
+ }
+ }
+ }
+ return nReplaced;
+}
+
+ScCellRangesObj::ScCellRangesObj(ScDocShell* pDocSh, const ScRangeList& rR)
+ : ScCellRangesBase(pDocSh, rR)
+{
+}
+
+ScCellRangesObj::~ScCellRangesObj()
+{
+}
+
+void ScCellRangesObj::RefChanged()
+{
+ ScCellRangesBase::RefChanged();
+}
+
+uno::Any SAL_CALL ScCellRangesObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XSheetCellRangeContainer*>(this),
+ static_cast<sheet::XSheetCellRanges*>(this),
+ static_cast<container::XIndexAccess*>(this),
+ static_cast<container::XElementAccess*>(static_cast<container::XIndexAccess*>(this)),
+ static_cast<container::XEnumerationAccess*>(this),
+ static_cast<container::XNameContainer*>(this),
+ static_cast<container::XNameReplace*>(this),
+ static_cast<container::XNameAccess*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScCellRangesBase::queryInterface( rType );
+}
+
+void SAL_CALL ScCellRangesObj::acquire() noexcept
+{
+ ScCellRangesBase::acquire();
+}
+
+void SAL_CALL ScCellRangesObj::release() noexcept
+{
+ ScCellRangesBase::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScCellRangesObj::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences(
+ ScCellRangesBase::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<sheet::XSheetCellRangeContainer>::get(),
+ cppu::UnoType<container::XNameContainer>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get()
+ } );
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScCellRangesObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XCellRanges
+
+rtl::Reference<ScCellRangeObj> ScCellRangesObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const
+{
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ if ( pDocSh && nIndex >= 0 && nIndex < sal::static_int_cast<sal_Int32>(rRanges.size()) )
+ {
+ ScRange const & rRange = rRanges[ nIndex ];
+ if ( rRange.aStart == rRange.aEnd )
+ return new ScCellObj( pDocSh, rRange.aStart );
+ else
+ return new ScCellRangeObj( pDocSh, rRange );
+ }
+
+ return nullptr; // no DocShell or wrong index
+}
+
+uno::Sequence<table::CellRangeAddress> SAL_CALL ScCellRangesObj::getRangeAddresses()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ size_t nCount = rRanges.size();
+ if ( pDocSh && nCount )
+ {
+ table::CellRangeAddress aRangeAddress;
+ uno::Sequence<table::CellRangeAddress> aSeq(nCount);
+ table::CellRangeAddress* pAry = aSeq.getArray();
+ for ( size_t i=0; i < nCount; i++)
+ {
+ ScUnoConversion::FillApiRange( aRangeAddress, rRanges[ i ] );
+ pAry[i] = aRangeAddress;
+ }
+ return aSeq;
+ }
+
+ return {}; // can be empty
+}
+
+uno::Reference<container::XEnumerationAccess> SAL_CALL ScCellRangesObj::getCells()
+{
+ SolarMutexGuard aGuard;
+
+ // getCells with empty range list is possible (no exception),
+ // the resulting enumeration just has no elements
+ // (same behaviour as a valid range with no cells)
+ // This is handled in ScCellsEnumeration ctor.
+
+ const ScRangeList& rRanges = GetRangeList();
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ return new ScCellsObj( pDocSh, rRanges );
+ return nullptr;
+}
+
+OUString SAL_CALL ScCellRangesObj::getRangeAddressesAsString()
+{
+ SolarMutexGuard aGuard;
+ OUString aString;
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ if (pDocSh)
+ rRanges.Format( aString, ScRefFlags::VALID | ScRefFlags::TAB_3D, pDocSh->GetDocument() );
+ return aString;
+}
+
+// XSheetCellRangeContainer
+
+void SAL_CALL ScCellRangesObj::addRangeAddress( const table::CellRangeAddress& rRange,
+ sal_Bool bMergeRanges )
+{
+ SolarMutexGuard aGuard;
+ ScRange aRange(static_cast<SCCOL>(rRange.StartColumn),
+ static_cast<SCROW>(rRange.StartRow),
+ static_cast<SCTAB>(rRange.Sheet),
+ static_cast<SCCOL>(rRange.EndColumn),
+ static_cast<SCROW>(rRange.EndRow),
+ static_cast<SCTAB>(rRange.Sheet));
+ AddRange(aRange, bMergeRanges);
+}
+
+static void lcl_RemoveNamedEntry( std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries, const ScRange& rRange )
+{
+ sal_uInt16 nCount = rNamedEntries.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ if ( rNamedEntries[n].GetRange() == rRange )
+ rNamedEntries.erase( rNamedEntries.begin() + n );
+}
+
+void SAL_CALL ScCellRangesObj::removeRangeAddress( const table::CellRangeAddress& rRange )
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+
+ ScRangeList aSheetRanges;
+ ScRangeList aNotSheetRanges;
+ for (size_t i = 0; i < rRanges.size(); ++i)
+ {
+ if (rRanges[ i].aStart.Tab() == rRange.Sheet)
+ {
+ aSheetRanges.push_back( rRanges[ i ] );
+ }
+ else
+ {
+ aNotSheetRanges.push_back( rRanges[ i ] );
+ }
+ }
+ ScMarkData aMarkData(GetDocument()->GetSheetLimits());
+ aMarkData.MarkFromRangeList( aSheetRanges, false );
+ ScRange aRange(static_cast<SCCOL>(rRange.StartColumn),
+ static_cast<SCROW>(rRange.StartRow),
+ static_cast<SCTAB>(rRange.Sheet),
+ static_cast<SCCOL>(rRange.EndColumn),
+ static_cast<SCROW>(rRange.EndRow),
+ static_cast<SCTAB>(rRange.Sheet));
+ if (aMarkData.GetTableSelect( aRange.aStart.Tab() ))
+ {
+ aMarkData.MarkToMulti();
+ if (!aMarkData.IsAllMarked( aRange ) )
+ throw container::NoSuchElementException();
+
+ aMarkData.SetMultiMarkArea( aRange, false );
+ lcl_RemoveNamedEntry(m_aNamedEntries, aRange);
+
+ }
+ SetNewRanges(aNotSheetRanges);
+ ScRangeList aNew;
+ aMarkData.FillRangeListWithMarks( &aNew, false );
+ for ( size_t j = 0; j < aNew.size(); ++j)
+ {
+ AddRange(aNew[ j ], false);
+ }
+}
+
+void SAL_CALL ScCellRangesObj::addRangeAddresses( const uno::Sequence<table::CellRangeAddress >& rRanges,
+ sal_Bool bMergeRanges )
+{
+ SolarMutexGuard aGuard;
+ for (const table::CellRangeAddress& rRange : rRanges)
+ {
+ ScRange aRange(static_cast<SCCOL>(rRange.StartColumn),
+ static_cast<SCROW>(rRange.StartRow),
+ static_cast<SCTAB>(rRange.Sheet),
+ static_cast<SCCOL>(rRange.EndColumn),
+ static_cast<SCROW>(rRange.EndRow),
+ static_cast<SCTAB>(rRange.Sheet));
+ AddRange(aRange, bMergeRanges);
+ }
+}
+
+void SAL_CALL ScCellRangesObj::removeRangeAddresses( const uno::Sequence<table::CellRangeAddress >& rRangeSeq )
+{
+ // use sometimes a better/faster implementation
+ for (const table::CellRangeAddress& rRange : rRangeSeq)
+ {
+ removeRangeAddress(rRange);
+ }
+}
+
+// XNameContainer
+
+static void lcl_RemoveNamedEntry( std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries, std::u16string_view rName )
+{
+ sal_uInt16 nCount = rNamedEntries.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ if ( rNamedEntries[n].GetName() == rName )
+ rNamedEntries.erase( rNamedEntries.begin() + n );
+}
+
+void SAL_CALL ScCellRangesObj::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ bool bDone = false;
+
+ //! Type of aElement can be some specific interface instead of XInterface
+
+ uno::Reference<uno::XInterface> xInterface(aElement, uno::UNO_QUERY);
+ if ( pDocSh && xInterface.is() )
+ {
+ ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xInterface.get() );
+ if ( pRangesImp && pRangesImp->GetDocShell() == pDocSh )
+ {
+ // if explicit name is given and already existing, throw exception
+
+ if ( !aName.isEmpty() )
+ {
+ size_t nNamedCount = m_aNamedEntries.size();
+ for (size_t n = 0; n < nNamedCount; n++)
+ {
+ if (m_aNamedEntries[n].GetName() == aName)
+ throw container::ElementExistException();
+ }
+ }
+
+ ScRangeList aNew(GetRangeList());
+ const ScRangeList& rAddRanges = pRangesImp->GetRangeList();
+ size_t nAddCount = rAddRanges.size();
+ for ( size_t i = 0; i < nAddCount; i++ )
+ aNew.Join( rAddRanges[ i ] );
+ SetNewRanges(aNew);
+ bDone = true;
+
+ if ( !aName.isEmpty() && nAddCount == 1 )
+ {
+ // if a name is given, also insert into list of named entries
+ // (only possible for a single range)
+ // name is not in m_aNamedEntries (tested above)
+ m_aNamedEntries.emplace_back( ScNamedEntry{aName, rAddRanges[ 0 ]} );
+ }
+ }
+ }
+
+ if (!bDone)
+ {
+ // invalid element - double names are handled above
+ throw lang::IllegalArgumentException();
+ }
+}
+
+static bool lcl_FindRangeByName( const ScRangeList& rRanges, ScDocShell* pDocSh,
+ std::u16string_view rName, size_t& rIndex )
+{
+ if (pDocSh)
+ {
+ OUString aRangeStr;
+ ScDocument& rDoc = pDocSh->GetDocument();
+ for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ )
+ {
+ aRangeStr = rRanges[ i ].Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D);
+ if ( aRangeStr == rName )
+ {
+ rIndex = i;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool lcl_FindRangeOrEntry( const std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries,
+ const ScRangeList& rRanges, ScDocShell* pDocSh,
+ const OUString& rName, ScRange& rFound )
+{
+ // exact range in list?
+
+ size_t nIndex = 0;
+ if ( lcl_FindRangeByName( rRanges, pDocSh, rName, nIndex ) )
+ {
+ rFound = rRanges[ nIndex ];
+ return true;
+ }
+
+ // range contained in selection? (sheet must be specified)
+
+ ScRange aCellRange;
+ ScRefFlags nParse = aCellRange.ParseAny( rName, pDocSh->GetDocument() );
+ if ( (nParse & ( ScRefFlags::VALID | ScRefFlags::TAB_3D ))
+ == ( ScRefFlags::VALID | ScRefFlags::TAB_3D ))
+ {
+ ScMarkData aMarkData(pDocSh->GetDocument().GetSheetLimits());
+ aMarkData.MarkFromRangeList( rRanges, false );
+ if ( aMarkData.IsAllMarked( aCellRange ) )
+ {
+ rFound = aCellRange;
+ return true;
+ }
+ }
+
+ // named entry in this object?
+
+ for (const auto & rNamedEntry : rNamedEntries)
+ if ( rNamedEntry.GetName() == rName )
+ {
+ // test if named entry is contained in rRanges
+
+ const ScRange& rComp = rNamedEntry.GetRange();
+ ScMarkData aMarkData(pDocSh->GetDocument().GetSheetLimits());
+ aMarkData.MarkFromRangeList( rRanges, false );
+ if ( aMarkData.IsAllMarked( rComp ) )
+ {
+ rFound = rComp;
+ return true;
+ }
+ }
+
+ return false; // not found
+}
+
+void SAL_CALL ScCellRangesObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ size_t nIndex = 0;
+ if ( lcl_FindRangeByName( rRanges, pDocSh, aName, nIndex ) )
+ {
+ // skip a single range
+ ScRangeList aNew;
+ for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ )
+ if (i != nIndex)
+ aNew.push_back( rRanges[ i ] );
+ SetNewRanges(aNew);
+ bDone = true;
+ }
+ else if (pDocSh)
+ {
+ // deselect any ranges (parsed or named entry)
+ ScRangeList aDiff;
+ bool bValid = ( aDiff.Parse( aName, pDocSh->GetDocument() ) & ScRefFlags::VALID )
+ == ScRefFlags::VALID;
+ if (!bValid)
+ {
+ sal_uInt16 nCount = m_aNamedEntries.size();
+ for (sal_uInt16 n=0; n<nCount && !bValid; n++)
+ if (m_aNamedEntries[n].GetName() == aName)
+ {
+ aDiff.RemoveAll();
+ aDiff.push_back(m_aNamedEntries[n].GetRange());
+ bValid = true;
+ }
+ }
+ if ( bValid )
+ {
+ ScMarkData aMarkData(GetDocument()->GetSheetLimits());
+ aMarkData.MarkFromRangeList( rRanges, false );
+
+ for ( size_t i = 0, nDiffCount = aDiff.size(); i < nDiffCount; i++ )
+ {
+ ScRange const & rDiffRange = aDiff[ i ];
+ if (aMarkData.GetTableSelect( rDiffRange.aStart.Tab() ))
+ aMarkData.SetMultiMarkArea( rDiffRange, false );
+ }
+
+ ScRangeList aNew;
+ aMarkData.FillRangeListWithMarks( &aNew, false );
+ SetNewRanges(aNew);
+
+ bDone = true; //! error if range was not selected before?
+ }
+ }
+
+ if (!m_aNamedEntries.empty())
+ lcl_RemoveNamedEntry(m_aNamedEntries, aName);
+
+ if (!bDone)
+ throw container::NoSuchElementException(); // not found
+}
+
+// XNameReplace
+
+void SAL_CALL ScCellRangesObj::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ //! combine?
+ removeByName( aName );
+ insertByName( aName, aElement );
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL ScCellRangesObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ ScRange aRange;
+ if (!lcl_FindRangeOrEntry(m_aNamedEntries, rRanges,
+ pDocSh, aName, aRange))
+ throw container::NoSuchElementException();
+
+ uno::Reference<table::XCellRange> xRange;
+ if ( aRange.aStart == aRange.aEnd )
+ xRange.set(new ScCellObj( pDocSh, aRange.aStart ));
+ else
+ xRange.set(new ScCellRangeObj( pDocSh, aRange ));
+ aRet <<= xRange;
+
+ return aRet;
+}
+
+static bool lcl_FindEntryName( const std::vector<ScCellRangesObj::ScNamedEntry>& rNamedEntries,
+ const ScRange& rRange, OUString& rName )
+{
+ sal_uInt16 nCount = rNamedEntries.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ if (rNamedEntries[i].GetRange() == rRange)
+ {
+ rName = rNamedEntries[i].GetName();
+ return true;
+ }
+ return false;
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellRangesObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ if (pDocSh)
+ {
+ OUString aRangeStr;
+ ScDocument& rDoc = pDocSh->GetDocument();
+ size_t nCount = rRanges.size();
+
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+ for (size_t i=0; i < nCount; i++)
+ {
+ // use given name if for exactly this range, otherwise just format
+ ScRange const & rRange = rRanges[ i ];
+ if (m_aNamedEntries.empty() ||
+ !lcl_FindEntryName(m_aNamedEntries, rRange, aRangeStr))
+ {
+ aRangeStr = rRange.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D);
+ }
+ pAry[i] = aRangeStr;
+ }
+ return aSeq;
+ }
+ return {};
+}
+
+sal_Bool SAL_CALL ScCellRangesObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ const ScRangeList& rRanges = GetRangeList();
+ ScRange aRange;
+ return lcl_FindRangeOrEntry(m_aNamedEntries, rRanges, pDocSh,
+ aName, aRange);
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScCellRangesObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.SheetCellRangesEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScCellRangesObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ return rRanges.size();
+}
+
+uno::Any SAL_CALL ScCellRangesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XCellRange> xRange(GetObjectByIndex_Impl(nIndex));
+ if (!xRange.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xRange);
+
+}
+
+uno::Type SAL_CALL ScCellRangesObj::getElementType()
+{
+ return cppu::UnoType<table::XCellRange>::get();
+}
+
+sal_Bool SAL_CALL ScCellRangesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ return !rRanges.empty();
+}
+
+// XServiceInfo
+OUString SAL_CALL ScCellRangesObj::getImplementationName()
+{
+ return "ScCellRangesObj";
+}
+
+sal_Bool SAL_CALL ScCellRangesObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellRangesObj::getSupportedServiceNames()
+{
+ return {SCSHEETCELLRANGES_SERVICE,
+ SCCELLPROPERTIES_SERVICE,
+ SCCHARPROPERTIES_SERVICE,
+ SCPARAPROPERTIES_SERVICE};
+}
+
+uno::Reference<table::XCellRange> ScCellRangeObj::CreateRangeFromDoc( const ScDocument& rDoc, const ScRange& rR )
+{
+ if ( ScDocShell* pDocShell = rDoc.GetDocumentShell() )
+ return new ScCellRangeObj( pDocShell, rR );
+ return nullptr;
+}
+
+ScCellRangeObj::ScCellRangeObj(ScDocShell* pDocSh, const ScRange& rR) :
+ ScCellRangesBase( pDocSh, rR ),
+ pRangePropSet( lcl_GetRangePropertySet() ),
+ aRange( rR )
+{
+ aRange.PutInOrder(); // beginning / end correct
+}
+
+ScCellRangeObj::~ScCellRangeObj()
+{
+}
+
+void ScCellRangeObj::RefChanged()
+{
+ ScCellRangesBase::RefChanged();
+
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE(rRanges.size() == 1, "What ranges ?!?!");
+ if ( !rRanges.empty() )
+ {
+ const ScRange & rFirst = rRanges[0];
+ aRange = rFirst;
+ aRange.PutInOrder();
+ }
+}
+
+uno::Any SAL_CALL ScCellRangeObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XCellRangeAddressable*>(this),
+ static_cast<table::XCellRange*>(this),
+ static_cast<sheet::XSheetCellRange*>(this),
+ static_cast<sheet::XArrayFormulaRange*>(this),
+ static_cast<sheet::XArrayFormulaTokens*>(this),
+ static_cast<sheet::XCellRangeData*>(this),
+ static_cast<sheet::XCellRangeFormula*>(this),
+ static_cast<sheet::XMultipleOperation*>(this),
+ static_cast<util::XMergeable*>(this),
+ static_cast<sheet::XCellSeries*>(this),
+ static_cast<table::XAutoFormattable*>(this),
+ static_cast<util::XSortable*>(this),
+ static_cast<sheet::XSheetFilterableEx*>(this),
+ static_cast<sheet::XSheetFilterable*>(this),
+ static_cast<sheet::XSubTotalCalculatable*>(this),
+ static_cast<table::XColumnRowRange*>(this),
+ static_cast<util::XImportable*>(this),
+ static_cast<sheet::XCellFormatRangesSupplier*>(this),
+ static_cast<sheet::XUniqueCellFormatRangesSupplier*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScCellRangesBase::queryInterface( rType );
+}
+
+void SAL_CALL ScCellRangeObj::acquire() noexcept
+{
+ ScCellRangesBase::acquire();
+}
+
+void SAL_CALL ScCellRangeObj::release() noexcept
+{
+ ScCellRangesBase::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScCellRangeObj::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences(
+ ScCellRangesBase::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<sheet::XCellRangeAddressable>::get(),
+ cppu::UnoType<sheet::XSheetCellRange>::get(),
+ cppu::UnoType<sheet::XArrayFormulaRange>::get(),
+ cppu::UnoType<sheet::XArrayFormulaTokens>::get(),
+ cppu::UnoType<sheet::XCellRangeData>::get(),
+ cppu::UnoType<sheet::XCellRangeFormula>::get(),
+ cppu::UnoType<sheet::XMultipleOperation>::get(),
+ cppu::UnoType<util::XMergeable>::get(),
+ cppu::UnoType<sheet::XCellSeries>::get(),
+ cppu::UnoType<table::XAutoFormattable>::get(),
+ cppu::UnoType<util::XSortable>::get(),
+ cppu::UnoType<sheet::XSheetFilterableEx>::get(),
+ cppu::UnoType<sheet::XSubTotalCalculatable>::get(),
+ cppu::UnoType<table::XColumnRowRange>::get(),
+ cppu::UnoType<util::XImportable>::get(),
+ cppu::UnoType<sheet::XCellFormatRangesSupplier>::get(),
+ cppu::UnoType<sheet::XUniqueCellFormatRangesSupplier>::get()
+ } );
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScCellRangeObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XCellRange
+
+// ColumnCount / RowCount vanished
+//! are used in Writer for tables ???
+
+uno::Reference<table::XCell> ScCellRangeObj::GetCellByPosition_Impl(
+ sal_Int32 nColumn, sal_Int32 nRow )
+{
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ throw uno::RuntimeException();
+
+ if ( nColumn >= 0 && nRow >= 0 )
+ {
+ sal_Int32 nPosX = aRange.aStart.Col() + nColumn;
+ sal_Int32 nPosY = aRange.aStart.Row() + nRow;
+
+ if ( nPosX <= aRange.aEnd.Col() && nPosY <= aRange.aEnd.Row() )
+ {
+ ScAddress aNew( static_cast<SCCOL>(nPosX), static_cast<SCROW>(nPosY), aRange.aStart.Tab() );
+ return new ScCellObj( pDocSh, aNew );
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Reference<table::XCell> SAL_CALL ScCellRangeObj::getCellByPosition(
+ sal_Int32 nColumn, sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+
+ return GetCellByPosition_Impl(nColumn, nRow);
+}
+
+uno::Reference<table::XCellRange> SAL_CALL ScCellRangeObj::getCellRangeByPosition(
+ sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ SolarMutexGuard aGuard;
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ throw uno::RuntimeException();
+
+ if ( nLeft >= 0 && nTop >= 0 && nRight >= 0 && nBottom >= 0 )
+ {
+ sal_Int32 nStartX = aRange.aStart.Col() + nLeft;
+ sal_Int32 nStartY = aRange.aStart.Row() + nTop;
+ sal_Int32 nEndX = aRange.aStart.Col() + nRight;
+ sal_Int32 nEndY = aRange.aStart.Row() + nBottom;
+
+ if ( nStartX <= nEndX && nEndX <= aRange.aEnd.Col() &&
+ nStartY <= nEndY && nEndY <= aRange.aEnd.Row() )
+ {
+ ScRange aNew( static_cast<SCCOL>(nStartX), static_cast<SCROW>(nStartY), aRange.aStart.Tab(),
+ static_cast<SCCOL>(nEndX), static_cast<SCROW>(nEndY), aRange.aEnd.Tab() );
+ return new ScCellRangeObj( pDocSh, aNew );
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Reference<table::XCellRange> SAL_CALL ScCellRangeObj::getCellRangeByName(
+ const OUString& aName )
+{
+ return getCellRangeByName( aName, ScAddress::detailsOOOa1 );
+}
+
+uno::Reference<table::XCellRange> ScCellRangeObj::getCellRangeByName(
+ const OUString& aName, const ScAddress::Details& rDetails )
+{
+ // name refers to the whole document (with the range's table as default),
+ // valid only if the range is within this range
+
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = aRange.aStart.Tab();
+
+ ScRange aCellRange;
+ bool bFound = false;
+ ScRefFlags nParse = aCellRange.ParseAny( aName, rDoc, rDetails );
+ if ( nParse & ScRefFlags::VALID )
+ {
+ if ( !(nParse & ScRefFlags::TAB_3D) ) // no sheet specified -> this sheet
+ {
+ aCellRange.aStart.SetTab(nTab);
+ aCellRange.aEnd.SetTab(nTab);
+ }
+ bFound = true;
+ }
+ else
+ {
+ if ( ScRangeUtil::MakeRangeFromName( aName, rDoc, nTab, aCellRange, RUTL_NAMES, rDetails) ||
+ ScRangeUtil::MakeRangeFromName( aName, rDoc, nTab, aCellRange, RUTL_DBASE, rDetails))
+ bFound = true;
+ }
+
+ if (bFound) // valid only if within this object's range
+ {
+ if (!aRange.Contains(aCellRange))
+ bFound = false;
+ }
+
+ if (bFound)
+ {
+ if ( aCellRange.aStart == aCellRange.aEnd )
+ return new ScCellObj( pDocSh, aCellRange.aStart );
+ else
+ return new ScCellRangeObj( pDocSh, aCellRange );
+ }
+ }
+
+ throw uno::RuntimeException();
+}
+
+// XColumnRowRange
+
+uno::Reference<table::XTableColumns> SAL_CALL ScCellRangeObj::getColumns()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ return new ScTableColumnsObj( pDocSh, aRange.aStart.Tab(),
+ aRange.aStart.Col(), aRange.aEnd.Col() );
+
+ OSL_FAIL("Document invalid");
+ return nullptr;
+}
+
+uno::Reference<table::XTableRows> SAL_CALL ScCellRangeObj::getRows()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ return new ScTableRowsObj( pDocSh, aRange.aStart.Tab(),
+ aRange.aStart.Row(), aRange.aEnd.Row() );
+
+ OSL_FAIL("Document invalid");
+ return nullptr;
+}
+
+// XAddressableCellRange
+
+table::CellRangeAddress SAL_CALL ScCellRangeObj::getRangeAddress()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aRet;
+ ScUnoConversion::FillApiRange( aRet, aRange );
+ return aRet;
+}
+
+// XSheetCellRange
+
+uno::Reference<sheet::XSpreadsheet> SAL_CALL ScCellRangeObj::getSpreadsheet()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ return new ScTableSheetObj( pDocSh, aRange.aStart.Tab() );
+
+ OSL_FAIL("Document invalid");
+ return nullptr;
+}
+
+// XArrayFormulaRange
+
+OUString SAL_CALL ScCellRangeObj::getArrayFormula()
+{
+ SolarMutexGuard aGuard;
+
+ // Matrix formula if clearly part of a matrix (so when start and end of
+ // the block belong to the same matrix) else empty string.
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return OUString();
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScRefCellValue aCell1(rDoc, aRange.aStart);
+ ScRefCellValue aCell2(rDoc, aRange.aEnd);
+ if (aCell1.getType() == CELLTYPE_FORMULA && aCell2.getType() == CELLTYPE_FORMULA)
+ {
+ const ScFormulaCell* pFCell1 = aCell1.getFormula();
+ const ScFormulaCell* pFCell2 = aCell2.getFormula();
+ ScAddress aStart1;
+ ScAddress aStart2;
+ if (pFCell1->GetMatrixOrigin(rDoc, aStart1) && pFCell2->GetMatrixOrigin(rDoc, aStart2))
+ {
+ if (aStart1 == aStart2) // both the same matrix
+ return pFCell1->GetFormula(); // it doesn't matter from which cell
+ }
+ }
+ return OUString();
+}
+
+void ScCellRangeObj::SetArrayFormula_Impl(const OUString& rFormula,
+ const formula::FormulaGrammar::Grammar eGrammar)
+{
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return;
+
+ if ( !rFormula.isEmpty() )
+ {
+ if ( dynamic_cast<ScTableSheetObj*>( this ) )
+ {
+ // don't set array formula for sheet object
+ throw uno::RuntimeException();
+ }
+
+ pDocSh->GetDocFunc().EnterMatrix( aRange, nullptr, nullptr, rFormula, true, true, OUString()/*rFormulaNmsp*/, eGrammar );
+ }
+ else
+ {
+ // empty string -> erase array formula
+ ScMarkData aMark(GetDocument()->GetSheetLimits());
+ aMark.SetMarkArea( aRange );
+ aMark.SelectTable( aRange.aStart.Tab(), true );
+ pDocSh->GetDocFunc().DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, true );
+ }
+}
+
+void SAL_CALL ScCellRangeObj::setArrayFormula( const OUString& aFormula )
+{
+ SolarMutexGuard aGuard;
+ // GRAM_API for API compatibility.
+ SetArrayFormula_Impl( aFormula, formula::FormulaGrammar::GRAM_API);
+}
+
+// XArrayFormulaTokens
+uno::Sequence<sheet::FormulaToken> SAL_CALL ScCellRangeObj::getArrayTokens()
+{
+ SolarMutexGuard aGuard;
+
+ // same cell logic as in getArrayFormula
+
+ uno::Sequence<sheet::FormulaToken> aSequence;
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return aSequence;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScRefCellValue aCell1(rDoc, aRange.aStart);
+ ScRefCellValue aCell2(rDoc, aRange.aEnd);
+ if (aCell1.getType() == CELLTYPE_FORMULA && aCell2.getType() == CELLTYPE_FORMULA)
+ {
+ const ScFormulaCell* pFCell1 = aCell1.getFormula();
+ const ScFormulaCell* pFCell2 = aCell2.getFormula();
+ ScAddress aStart1;
+ ScAddress aStart2;
+ if (pFCell1->GetMatrixOrigin(rDoc, aStart1) && pFCell2->GetMatrixOrigin(rDoc, aStart2))
+ {
+ if (aStart1 == aStart2)
+ {
+ const ScTokenArray* pTokenArray = pFCell1->GetCode();
+ if (pTokenArray)
+ ScTokenConversion::ConvertToTokenSequence(rDoc, aSequence, *pTokenArray);
+ }
+ }
+ }
+
+ return aSequence;
+}
+
+void SAL_CALL ScCellRangeObj::setArrayTokens( const uno::Sequence<sheet::FormulaToken>& rTokens )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ if ( rTokens.hasElements() )
+ {
+ if ( dynamic_cast<ScTableSheetObj*>( this ) )
+ {
+ throw uno::RuntimeException();
+ }
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScTokenArray aTokenArray(rDoc);
+ (void)ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, rTokens );
+
+ // Actually GRAM_API is a don't-care here because of the token
+ // array being set, it fits with other API compatibility grammars
+ // though.
+ pDocSh->GetDocFunc().EnterMatrix( aRange, nullptr, &aTokenArray, OUString(), true, true, OUString(), formula::FormulaGrammar::GRAM_API );
+ }
+ else
+ {
+ // empty sequence -> erase array formula
+ ScMarkData aMark(pDocSh->GetDocument().GetSheetLimits());
+ aMark.SetMarkArea( aRange );
+ aMark.SelectTable( aRange.aStart.Tab(), true );
+ pDocSh->GetDocFunc().DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, true );
+ }
+}
+
+// XCellRangeData
+
+uno::Sequence< uno::Sequence<uno::Any> > SAL_CALL ScCellRangeObj::getDataArray()
+{
+ SolarMutexGuard aGuard;
+
+ if ( dynamic_cast<ScTableSheetObj*>( this ) )
+ {
+ // don't create a data array for the sheet
+ throw uno::RuntimeException();
+ }
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ uno::Any aAny;
+ // bAllowNV = TRUE: errors as void
+ if ( ScRangeToSequence::FillMixedArray( aAny, pDocSh->GetDocument(), aRange, true ) )
+ {
+ uno::Sequence< uno::Sequence<uno::Any> > aSeq;
+ if ( aAny >>= aSeq )
+ return aSeq; // success
+ }
+ }
+
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScCellRangeObj::setDataArray(
+ const uno::Sequence< uno::Sequence<uno::Any> >& aArray )
+{
+ SolarMutexGuard aGuard;
+
+ bool bDone = false;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ //! move lcl_PutDataArray to docfunc?
+ bDone = lcl_PutDataArray( *pDocSh, aRange, aArray );
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+// XCellRangeFormula
+
+uno::Sequence< uno::Sequence<OUString> > SAL_CALL ScCellRangeObj::getFormulaArray()
+{
+ SolarMutexGuard aGuard;
+
+ if ( dynamic_cast<ScTableSheetObj*>( this ) )
+ {
+ // don't create a data array for the sheet
+ throw uno::RuntimeException();
+ }
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ SCCOL nColCount = nEndCol + 1 - nStartCol;
+ SCROW nRowCount = nEndRow + 1 - nStartRow;
+ SCTAB nTab = aRange.aStart.Tab();
+
+ uno::Sequence< uno::Sequence<OUString> > aRowSeq( nRowCount );
+ uno::Sequence<OUString>* pRowAry = aRowSeq.getArray();
+ for (SCROW nRowIndex = 0; nRowIndex < nRowCount; nRowIndex++)
+ {
+ uno::Sequence<OUString> aColSeq( nColCount );
+ OUString* pColAry = aColSeq.getArray();
+ for (SCCOL nColIndex = 0; nColIndex < nColCount; nColIndex++)
+ pColAry[nColIndex] = lcl_GetInputString( pDocSh->GetDocument(),
+ ScAddress( nStartCol+nColIndex, nStartRow+nRowIndex, nTab ), true );
+
+ pRowAry[nRowIndex] = aColSeq;
+ }
+
+ return aRowSeq;
+ }
+
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScCellRangeObj::setFormulaArray(
+ const uno::Sequence< uno::Sequence<OUString> >& aArray )
+{
+ SolarMutexGuard aGuard;
+
+ bool bDone = false;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ ScExternalRefManager::ApiGuard aExtRefGuard(pDocSh->GetDocument());
+
+ // GRAM_API for API compatibility.
+ bDone = lcl_PutFormulaArray( *pDocSh, aRange, aArray, formula::FormulaGrammar::GRAM_API );
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+// XMultipleOperation
+
+void SAL_CALL ScCellRangeObj::setTableOperation( const table::CellRangeAddress& aFormulaRange,
+ sheet::TableOperationMode nMode,
+ const table::CellAddress& aColumnCell,
+ const table::CellAddress& aRowCell )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return;
+
+ bool bError = false;
+ ScTabOpParam aParam;
+ aParam.aRefFormulaCell = ScRefAddress( static_cast<SCCOL>(aFormulaRange.StartColumn),
+ static_cast<SCROW>(aFormulaRange.StartRow), aFormulaRange.Sheet );
+ aParam.aRefFormulaEnd = ScRefAddress( static_cast<SCCOL>(aFormulaRange.EndColumn),
+ static_cast<SCROW>(aFormulaRange.EndRow), aFormulaRange.Sheet );
+ aParam.aRefRowCell = ScRefAddress( static_cast<SCCOL>(aRowCell.Column),
+ static_cast<SCROW>(aRowCell.Row), aRowCell.Sheet );
+ aParam.aRefColCell = ScRefAddress( static_cast<SCCOL>(aColumnCell.Column),
+ static_cast<SCROW>(aColumnCell.Row), aColumnCell.Sheet );
+
+ switch (nMode)
+ {
+ case sheet::TableOperationMode_COLUMN:
+ aParam.meMode = ScTabOpParam::Column;
+ break;
+ case sheet::TableOperationMode_ROW:
+ aParam.meMode = ScTabOpParam::Row;
+ break;
+ case sheet::TableOperationMode_BOTH:
+ aParam.meMode = ScTabOpParam::Both;
+ break;
+ default:
+ bError = true;
+ }
+
+ if (!bError)
+ pDocSh->GetDocFunc().TabOp( aRange, nullptr, aParam, true, true );
+}
+
+// XMergeable
+
+void SAL_CALL ScCellRangeObj::merge( sal_Bool bMerge )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScCellMergeOption aMergeOption(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), false);
+ aMergeOption.maTabs.insert(aRange.aStart.Tab());
+ if ( bMerge )
+ pDocSh->GetDocFunc().MergeCells( aMergeOption, false, true, true );
+ else
+ pDocSh->GetDocFunc().UnmergeCells( aMergeOption, true, nullptr );
+
+ //! Catch error?
+}
+
+sal_Bool SAL_CALL ScCellRangeObj::getIsMerged()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ return pDocSh && pDocSh->GetDocument().HasAttrib( aRange, HasAttrFlags::Merged );
+}
+
+// XCellSeries
+
+void SAL_CALL ScCellRangeObj::fillSeries( sheet::FillDirection nFillDirection,
+ sheet::FillMode nFillMode, sheet::FillDateMode nFillDateMode,
+ double fStep, double fEndValue )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ bool bError = false;
+
+ FillDir eDir = FILL_TO_BOTTOM;
+ switch (nFillDirection)
+ {
+ case sheet::FillDirection_TO_BOTTOM:
+ eDir = FILL_TO_BOTTOM;
+ break;
+ case sheet::FillDirection_TO_RIGHT:
+ eDir = FILL_TO_RIGHT;
+ break;
+ case sheet::FillDirection_TO_TOP:
+ eDir = FILL_TO_TOP;
+ break;
+ case sheet::FillDirection_TO_LEFT:
+ eDir = FILL_TO_LEFT;
+ break;
+ default:
+ bError = true;
+ }
+
+ FillCmd eCmd = FILL_SIMPLE;
+ switch ( nFillMode )
+ {
+ case sheet::FillMode_SIMPLE:
+ eCmd = FILL_SIMPLE;
+ break;
+ case sheet::FillMode_LINEAR:
+ eCmd = FILL_LINEAR;
+ break;
+ case sheet::FillMode_GROWTH:
+ eCmd = FILL_GROWTH;
+ break;
+ case sheet::FillMode_DATE:
+ eCmd = FILL_DATE;
+ break;
+ case sheet::FillMode_AUTO:
+ eCmd = FILL_AUTO;
+ break;
+ default:
+ bError = true;
+ }
+
+ FillDateCmd eDateCmd = FILL_DAY;
+ switch ( nFillDateMode )
+ {
+ case sheet::FillDateMode_FILL_DATE_DAY:
+ eDateCmd = FILL_DAY;
+ break;
+ case sheet::FillDateMode_FILL_DATE_WEEKDAY:
+ eDateCmd = FILL_WEEKDAY;
+ break;
+ case sheet::FillDateMode_FILL_DATE_MONTH:
+ eDateCmd = FILL_MONTH;
+ break;
+ case sheet::FillDateMode_FILL_DATE_YEAR:
+ eDateCmd = FILL_YEAR;
+ break;
+ default:
+ bError = true;
+ }
+
+ if (!bError)
+ pDocSh->GetDocFunc().FillSeries( aRange, nullptr, eDir, eCmd, eDateCmd,
+ MAXDOUBLE, fStep, fEndValue, true );
+}
+
+void SAL_CALL ScCellRangeObj::fillAuto( sheet::FillDirection nFillDirection,
+ sal_Int32 nSourceCount )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !(pDocSh && nSourceCount) )
+ return;
+
+ ScRange aSourceRange(aRange);
+ SCCOLROW nCount = 0; // "Dest-Count"
+ FillDir eDir = FILL_TO_BOTTOM;
+ bool bError = false;
+ switch (nFillDirection)
+ {
+ case sheet::FillDirection_TO_BOTTOM:
+ aSourceRange.aEnd.SetRow( static_cast<SCROW>( aSourceRange.aStart.Row() + nSourceCount - 1 ) );
+ nCount = aRange.aEnd.Row() - aSourceRange.aEnd.Row();
+ eDir = FILL_TO_BOTTOM;
+ break;
+ case sheet::FillDirection_TO_RIGHT:
+ aSourceRange.aEnd.SetCol( static_cast<SCCOL>( aSourceRange.aStart.Col() + nSourceCount - 1 ) );
+ nCount = aRange.aEnd.Col() - aSourceRange.aEnd.Col();
+ eDir = FILL_TO_RIGHT;
+ break;
+ case sheet::FillDirection_TO_TOP:
+ aSourceRange.aStart.SetRow( static_cast<SCROW>( aSourceRange.aEnd.Row() - nSourceCount + 1 ) );
+ nCount = aSourceRange.aStart.Row() - aRange.aStart.Row();
+ eDir = FILL_TO_TOP;
+ break;
+ case sheet::FillDirection_TO_LEFT:
+ aSourceRange.aStart.SetCol( static_cast<SCCOL>( aSourceRange.aEnd.Col() - nSourceCount + 1 ) );
+ nCount = aSourceRange.aStart.Col() - aRange.aStart.Col();
+ eDir = FILL_TO_LEFT;
+ break;
+ default:
+ bError = true;
+ }
+ const ScDocument& rDoc = pDocSh->GetDocument();
+ if (nCount < 0 || nCount > rDoc.MaxRow()) // overflow
+ bError = true;
+
+ if (!bError)
+ pDocSh->GetDocFunc().FillAuto( aSourceRange, nullptr, eDir, nCount, true );
+}
+
+// XAutoFormattable
+
+void SAL_CALL ScCellRangeObj::autoFormat( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat();
+ ScAutoFormat::const_iterator it = pAutoFormat->find(aName);
+ if (it == pAutoFormat->end())
+ throw lang::IllegalArgumentException();
+
+ ScAutoFormat::const_iterator itBeg = pAutoFormat->begin();
+ size_t nIndex = std::distance(itBeg, it);
+ pDocSh->GetDocFunc().AutoFormat(aRange, nullptr, nIndex, true);
+
+ }
+}
+
+// XSortable
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScCellRangeObj::createSortDescriptor()
+{
+ SolarMutexGuard aGuard;
+ ScSortParam aParam;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ // create DB-Area only during execution; API always the exact area
+ ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark );
+ if (pData)
+ {
+ pData->GetSortParam(aParam);
+
+ // SortDescriptor contains the counted fields inside the area
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOLROW nFieldStart = aParam.bByRow ?
+ static_cast<SCCOLROW>(aDBRange.aStart.Col()) :
+ static_cast<SCCOLROW>(aDBRange.aStart.Row());
+ for (sal_uInt16 i=0; i<aParam.GetSortKeyCount(); i++)
+ if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nFieldStart )
+ aParam.maKeyState[i].nField -= nFieldStart;
+ }
+ }
+
+ uno::Sequence<beans::PropertyValue> aSeq( ScSortDescriptor::GetPropertyCount() );
+ ScSortDescriptor::FillProperties( aSeq, aParam );
+ return aSeq;
+}
+
+void SAL_CALL ScCellRangeObj::sort( const uno::Sequence<beans::PropertyValue>& aDescriptor )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return;
+
+ sal_uInt16 i;
+ ScSortParam aParam;
+ ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area
+ if (pData)
+ {
+ // get old settings if not everything is set anew
+ pData->GetSortParam(aParam);
+ SCCOLROW nOldStart = aParam.bByRow ?
+ static_cast<SCCOLROW>(aRange.aStart.Col()) :
+ static_cast<SCCOLROW>(aRange.aStart.Row());
+ for (i=0; i<aParam.GetSortKeyCount(); i++)
+ if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nOldStart )
+ aParam.maKeyState[i].nField -= nOldStart;
+ }
+
+ ScSortDescriptor::FillSortParam( aParam, aDescriptor );
+
+ // SortDescriptor contains the counted fields inside the area
+ // ByRow can be changed during execution of FillSortParam
+ SCCOLROW nFieldStart = aParam.bByRow ?
+ static_cast<SCCOLROW>(aRange.aStart.Col()) :
+ static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nFieldEnd = aParam.bByRow ?
+ static_cast<SCCOLROW>(aRange.aEnd.Col()) :
+ static_cast<SCCOLROW>(aRange.aEnd.Row());
+ for (i=0; i<aParam.GetSortKeyCount(); i++)
+ {
+ aParam.maKeyState[i].nField += nFieldStart;
+ // tdf#103632 - sanity check poorly behaved macros.
+ if (aParam.maKeyState[i].nField > nFieldEnd)
+ aParam.maKeyState[i].nField = nFieldEnd;
+ }
+
+ SCTAB nTab = aRange.aStart.Tab();
+ aParam.nCol1 = aRange.aStart.Col();
+ aParam.nRow1 = aRange.aStart.Row();
+ aParam.nCol2 = aRange.aEnd.Col();
+ aParam.nRow2 = aRange.aEnd.Row();
+
+ pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area
+
+ ScDBDocFunc aFunc(*pDocSh); // area must be created
+ (void)aFunc.Sort( nTab, aParam, true, true, true );
+}
+
+// XFilterable
+
+uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScCellRangeObj::createFilterDescriptor(
+ sal_Bool bEmpty )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ rtl::Reference<ScFilterDescriptor> pNew = new ScFilterDescriptor(pDocSh);
+ if ( !bEmpty && pDocSh )
+ {
+ // create DB-Area only during execution; API always the exact area
+ ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark );
+ if (pData)
+ {
+ ScQueryParam aParam;
+ pData->GetQueryParam(aParam);
+ // FilterDescriptor contains the counted fields inside the area
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOLROW nFieldStart = aParam.bByRow ?
+ static_cast<SCCOLROW>(aDBRange.aStart.Col()) :
+ static_cast<SCCOLROW>(aDBRange.aStart.Row());
+ SCSIZE nCount = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.bDoQuery && rEntry.nField >= nFieldStart)
+ rEntry.nField -= nFieldStart;
+ }
+ pNew->SetParam(aParam);
+ }
+ }
+ return pNew;
+}
+
+void SAL_CALL ScCellRangeObj::filter( const uno::Reference<sheet::XSheetFilterDescriptor>& xDescriptor )
+{
+ SolarMutexGuard aGuard;
+
+ if (!xDescriptor.is()) return;
+
+ // This could be theoretically an unknown object, so only use the
+ // public XSheetFilterDescriptor interface to copy the data into a
+ // ScFilterDescriptor object:
+ //! if it already a ScFilterDescriptor is, direct via getImplementation?
+
+ ScDocShell* pDocSh = GetDocShell();
+ rtl::Reference<ScFilterDescriptor> xImpl(new ScFilterDescriptor(pDocSh));
+ uno::Reference< sheet::XSheetFilterDescriptor2 > xDescriptor2( xDescriptor, uno::UNO_QUERY );
+ if ( xDescriptor2.is() )
+ {
+ xImpl->setFilterFields2( xDescriptor2->getFilterFields2() );
+ }
+ else
+ {
+ xImpl->setFilterFields( xDescriptor->getFilterFields() );
+ }
+ // the rest are now properties...
+
+ uno::Reference<beans::XPropertySet> xPropSet( xDescriptor, uno::UNO_QUERY );
+ if (xPropSet.is())
+ lcl_CopyProperties(*xImpl, *xPropSet);
+
+ if (!pDocSh)
+ return;
+
+ ScQueryParam aParam = xImpl->GetParam();
+ // FilterDescriptor contains the counted fields inside the area
+ SCCOLROW nFieldStart = aParam.bByRow ?
+ static_cast<SCCOLROW>(aRange.aStart.Col()) :
+ static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCSIZE nCount = aParam.GetEntryCount();
+ svl::SharedStringPool& rPool = pDocSh->GetDocument().GetSharedStringPool();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.bDoQuery)
+ {
+ rEntry.nField += nFieldStart;
+ // dialog always shows the string -> must match the value
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ rItems.resize(1);
+ ScQueryEntry::Item& rItem = rItems.front();
+ if (rItem.meType != ScQueryEntry::ByString)
+ {
+ OUString aStr;
+ pDocSh->GetDocument().GetFormatTable()->GetInputLineString(rItem.mfVal, 0, aStr);
+ rItem.maString = rPool.intern(aStr);
+ }
+ }
+ }
+
+ SCTAB nTab = aRange.aStart.Tab();
+ aParam.nCol1 = aRange.aStart.Col();
+ aParam.nRow1 = aRange.aStart.Row();
+ aParam.nCol2 = aRange.aEnd.Col();
+ aParam.nRow2 = aRange.aEnd.Row();
+
+ pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area
+
+ //! keep source range in filter descriptor
+ //! if created by createFilterDescriptorByObject ???
+
+ ScDBDocFunc aFunc(*pDocSh);
+ aFunc.Query( nTab, aParam, nullptr, true, true ); // area must be created
+}
+
+//! get/setAutoFilter as properties!!!
+
+// XAdvancedFilterSource
+
+uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScCellRangeObj::createFilterDescriptorByObject(
+ const uno::Reference<sheet::XSheetFilterable>& xObject )
+{
+ SolarMutexGuard aGuard;
+
+ // this here is not the area, which will be filtered, instead the area
+ // with the query
+
+ uno::Reference<sheet::XCellRangeAddressable> xAddr( xObject, uno::UNO_QUERY );
+
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh || !xAddr.is() )
+ {
+ OSL_FAIL("no document or no area");
+ return nullptr;
+ }
+
+ //! check if xObject is in the same document
+
+ rtl::Reference<ScFilterDescriptor> pNew(new ScFilterDescriptor(pDocSh)); //! instead from object?
+
+ ScQueryParam aParam = pNew->GetParam();
+ aParam.bHasHeader = true;
+
+ table::CellRangeAddress aDataAddress(xAddr->getRangeAddress());
+ aParam.nCol1 = static_cast<SCCOL>(aDataAddress.StartColumn);
+ aParam.nRow1 = static_cast<SCROW>(aDataAddress.StartRow);
+ aParam.nCol2 = static_cast<SCCOL>(aDataAddress.EndColumn);
+ aParam.nRow2 = static_cast<SCROW>(aDataAddress.EndRow);
+ aParam.nTab = aDataAddress.Sheet;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ if (!rDoc.CreateQueryParam(aRange, aParam))
+ return nullptr;
+
+ // FilterDescriptor contains the counted fields inside the area
+ SCCOLROW nFieldStart = aParam.bByRow ?
+ static_cast<SCCOLROW>(aDataAddress.StartColumn) :
+ static_cast<SCCOLROW>(aDataAddress.StartRow);
+ SCSIZE nCount = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.bDoQuery && rEntry.nField >= nFieldStart)
+ rEntry.nField -= nFieldStart;
+ }
+
+ pNew->SetParam( aParam );
+ return pNew;
+}
+
+// XSubTotalSource
+
+uno::Reference<sheet::XSubTotalDescriptor> SAL_CALL ScCellRangeObj::createSubTotalDescriptor(
+ sal_Bool bEmpty )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<ScSubTotalDescriptor> pNew = new ScSubTotalDescriptor;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !bEmpty && pDocSh )
+ {
+ // create DB-Area only during execution; API always the exact area
+ ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark );
+ if (pData)
+ {
+ ScSubTotalParam aParam;
+ pData->GetSubTotalParam(aParam);
+ // SubTotalDescriptor contains the counted fields inside the area
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOL nFieldStart = aDBRange.aStart.Col();
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ if ( aParam.bGroupActive[i] )
+ {
+ if ( aParam.nField[i] >= nFieldStart )
+ aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] - nFieldStart );
+ for (SCCOL j=0; j<aParam.nSubTotals[i]; j++)
+ if ( aParam.pSubTotals[i][j] >= nFieldStart )
+ aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] - nFieldStart );
+ }
+ }
+ pNew->SetParam(aParam);
+ }
+ }
+ return pNew;
+}
+
+void SAL_CALL ScCellRangeObj::applySubTotals(
+ const uno::Reference<sheet::XSubTotalDescriptor>& xDescriptor,
+ sal_Bool bReplace)
+{
+ SolarMutexGuard aGuard;
+
+ if (!xDescriptor.is()) return;
+
+ ScDocShell* pDocSh = GetDocShell();
+ ScSubTotalDescriptorBase* pImp =
+ dynamic_cast<ScSubTotalDescriptorBase*>( xDescriptor.get() );
+
+ if (!(pDocSh && pImp))
+ return;
+
+ ScSubTotalParam aParam;
+ pImp->GetData(aParam); // virtual method of base class
+
+ // SubTotalDescriptor contains the counted fields inside the area
+ SCCOL nFieldStart = aRange.aStart.Col();
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ if ( aParam.bGroupActive[i] )
+ {
+ aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] + nFieldStart );
+ for (SCCOL j=0; j<aParam.nSubTotals[i]; j++)
+ aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] + nFieldStart );
+ }
+ }
+
+ aParam.bReplace = bReplace;
+
+ SCTAB nTab = aRange.aStart.Tab();
+ aParam.nCol1 = aRange.aStart.Col();
+ aParam.nRow1 = aRange.aStart.Row();
+ aParam.nCol2 = aRange.aEnd.Col();
+ aParam.nRow2 = aRange.aEnd.Row();
+
+ pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area
+
+ ScDBDocFunc aFunc(*pDocSh);
+ aFunc.DoSubTotals( nTab, aParam, true, true ); // area must be created
+}
+
+void SAL_CALL ScCellRangeObj::removeSubTotals()
+{
+ SolarMutexGuard aGuard;
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return;
+
+ ScSubTotalParam aParam;
+ ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark );
+ if (pData)
+ pData->GetSubTotalParam(aParam); // also keep field entries during remove
+
+ aParam.bRemoveOnly = true;
+
+ SCTAB nTab = aRange.aStart.Tab();
+ aParam.nCol1 = aRange.aStart.Col();
+ aParam.nRow1 = aRange.aStart.Row();
+ aParam.nCol2 = aRange.aEnd.Col();
+ aParam.nRow2 = aRange.aEnd.Row();
+
+ pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area
+
+ ScDBDocFunc aFunc(*pDocSh);
+ aFunc.DoSubTotals( nTab, aParam, true, true ); // are must be created
+}
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScCellRangeObj::createImportDescriptor( sal_Bool bEmpty )
+{
+ SolarMutexGuard aGuard;
+ ScImportParam aParam;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !bEmpty && pDocSh )
+ {
+ // create DB-Area only during execution; API always the exact area
+ ScDBData* pData = pDocSh->GetDBData( aRange, SC_DB_OLD, ScGetDBSelection::ForceMark );
+ if (pData)
+ pData->GetImportParam(aParam);
+ }
+
+ uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() );
+ ScImportDescriptor::FillProperties( aSeq, aParam );
+ return aSeq;
+}
+
+void SAL_CALL ScCellRangeObj::doImport( const uno::Sequence<beans::PropertyValue>& aDescriptor )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return;
+
+ ScImportParam aParam;
+ ScImportDescriptor::FillImportParam( aParam, aDescriptor );
+
+ SCTAB nTab = aRange.aStart.Tab();
+ aParam.nCol1 = aRange.aStart.Col();
+ aParam.nRow1 = aRange.aStart.Row();
+ aParam.nCol2 = aRange.aEnd.Col();
+ aParam.nRow2 = aRange.aEnd.Row();
+
+ //! TODO: could we get passed a valid result set by any means?
+
+ pDocSh->GetDBData( aRange, SC_DB_MAKE, ScGetDBSelection::ForceMark ); // if needed create area
+
+ ScDBDocFunc aFunc(*pDocSh); // are must be created
+ aFunc.DoImport( nTab, aParam, nullptr ); //! Api-Flag as parameter
+}
+
+// XCellFormatRangesSupplier
+
+uno::Reference<container::XIndexAccess> SAL_CALL ScCellRangeObj::getCellFormatRanges()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return new ScCellFormatsObj( pDocSh, aRange );
+ return nullptr;
+}
+
+// XUniqueCellFormatRangesSupplier
+
+uno::Reference<container::XIndexAccess> SAL_CALL ScCellRangeObj::getUniqueCellFormatRanges()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return new ScUniqueCellFormatsObj( pDocSh, aRange );
+ return nullptr;
+}
+
+// XPropertySet extended for Range-Properties
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellRangeObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( pRangePropSet->getPropertyMap() ));
+ return aRef;
+}
+
+void ScCellRangeObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue )
+{
+ // Range has only Position and Size in addition to ScCellRangesBase, both are ReadOnly
+ // -> nothing to do here
+
+ ScCellRangesBase::SetOnePropertyValue( pEntry, aValue );
+}
+
+void ScCellRangeObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny )
+{
+ if ( !pEntry )
+ return;
+
+ if ( pEntry->nWID == SC_WID_UNO_POS )
+ {
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ // GetMMRect converts using HMM_PER_TWIPS, like the DrawingLayer
+ tools::Rectangle aMMRect(pDocSh->GetDocument().GetMMRect(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() ));
+ awt::Point aPos( aMMRect.Left(), aMMRect.Top() );
+ rAny <<= aPos;
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_SIZE )
+ {
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ // GetMMRect converts using HMM_PER_TWIPS, like the DrawingLayer
+ tools::Rectangle aMMRect = pDocSh->GetDocument().GetMMRect(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() );
+ Size aSize(aMMRect.GetSize());
+ awt::Size aAwtSize( aSize.Width(), aSize.Height() );
+ rAny <<= aAwtSize;
+ }
+ }
+ else
+ ScCellRangesBase::GetOnePropertyValue( pEntry, rAny );
+}
+
+const SfxItemPropertyMap& ScCellRangeObj::GetItemPropertyMap()
+{
+ return pRangePropSet->getPropertyMap();
+}
+
+// XServiceInfo
+
+OUString SAL_CALL ScCellRangeObj::getImplementationName()
+{
+ return "ScCellRangeObj";
+}
+
+sal_Bool SAL_CALL ScCellRangeObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellRangeObj::getSupportedServiceNames()
+{
+ return {SCSHEETCELLRANGE_SERVICE,
+ SCCELLRANGE_SERVICE,
+ SCCELLPROPERTIES_SERVICE,
+ SCCHARPROPERTIES_SERVICE,
+ SCPARAPROPERTIES_SERVICE};
+}
+
+const SvxItemPropertySet* ScCellObj::GetEditPropertySet()
+{
+ return lcl_GetEditPropertySet();
+}
+
+const SfxItemPropertyMap& ScCellObj::GetCellPropertyMap()
+{
+ return lcl_GetCellPropertySet()->getPropertyMap();
+}
+
+ScCellObj::ScCellObj(ScDocShell* pDocSh, const ScAddress& rP) :
+ ScCellRangeObj( pDocSh, ScRange(rP,rP) ),
+ pCellPropSet( lcl_GetCellPropertySet() ),
+ aCellPos( rP ),
+ nActionLockCount( 0 )
+{
+ // pUnoText is allocated on demand (GetUnoText)
+ // can't be aggregated because getString/setString is handled here
+}
+
+SvxUnoText& ScCellObj::GetUnoText()
+{
+ if (!mxUnoText.is())
+ {
+ mxUnoText.set(new ScCellTextObj(GetDocShell(), aCellPos));
+ if (nActionLockCount)
+ {
+ ScCellEditSource* pEditSource =
+ static_cast<ScCellEditSource*> (mxUnoText->GetEditSource());
+ if (pEditSource)
+ pEditSource->SetDoUpdateData(false);
+ }
+ }
+ return *mxUnoText;
+}
+
+ScCellObj::~ScCellObj()
+{
+}
+
+void ScCellObj::RefChanged()
+{
+ ScCellRangeObj::RefChanged();
+
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE(rRanges.size() == 1, "What ranges ?!?!");
+ if ( !rRanges.empty() )
+ {
+ aCellPos = rRanges[ 0 ].aStart;
+ }
+}
+
+uno::Any SAL_CALL ScCellObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<table::XCell*>(this),
+ static_cast<table::XCell2*>(this),
+ static_cast<sheet::XFormulaTokens*>(this),
+ static_cast<sheet::XCellAddressable*>(this),
+ static_cast<text::XText*>(this),
+ static_cast<text::XSimpleText*>(this),
+ static_cast<text::XTextRange*>(this),
+ static_cast<container::XEnumerationAccess*>(this),
+ static_cast<container::XElementAccess*>(this),
+ static_cast<sheet::XSheetAnnotationAnchor*>(this),
+ static_cast<text::XTextFieldsSupplier*>(this),
+ static_cast<document::XActionLockable*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScCellRangeObj::queryInterface( rType );
+}
+
+void SAL_CALL ScCellObj::acquire() noexcept
+{
+ ScCellRangeObj::acquire();
+}
+
+void SAL_CALL ScCellObj::release() noexcept
+{
+ ScCellRangeObj::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScCellObj::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences(
+ ScCellRangeObj::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<table::XCell>::get(),
+ cppu::UnoType<sheet::XCellAddressable>::get(),
+ cppu::UnoType<text::XText>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<sheet::XSheetAnnotationAnchor>::get(),
+ cppu::UnoType<text::XTextFieldsSupplier>::get(),
+ cppu::UnoType<document::XActionLockable>::get(),
+ cppu::UnoType<sheet::XFormulaTokens>::get(),
+ cppu::UnoType<table::XCell2>::get()
+ } );
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScCellObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// helper methods
+
+OUString ScCellObj::GetInputString_Impl(bool bEnglish) const // for getFormula / FormulaLocal
+{
+ if (GetDocShell())
+ return lcl_GetInputString( GetDocShell()->GetDocument(), aCellPos, bEnglish );
+ return OUString();
+}
+
+OUString ScCellObj::GetOutputString_Impl() const
+{
+ ScDocShell* pDocSh = GetDocShell();
+ OUString aVal;
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScRefCellValue aCell(rDoc, aCellPos);
+
+ aVal = ScCellFormat::GetOutputString(rDoc, aCellPos, aCell);
+ }
+ return aVal;
+}
+
+void ScCellObj::SetString_Impl(const OUString& rString, bool bInterpret, bool bEnglish)
+{
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ // GRAM_API for API compatibility.
+ (void)pDocSh->GetDocFunc().SetCellText(
+ aCellPos, rString, bInterpret, bEnglish, true, formula::FormulaGrammar::GRAM_API );
+ }
+}
+
+double ScCellObj::GetValue_Impl() const
+{
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return pDocSh->GetDocument().GetValue( aCellPos );
+
+ return 0.0;
+}
+
+void ScCellObj::SetValue_Impl(double fValue)
+{
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ pDocSh->GetDocFunc().SetValueCell(aCellPos, fValue, false);
+}
+
+// only for XML import
+
+void ScCellObj::InputEnglishString( const OUString& rText )
+{
+ // This is like a mixture of setFormula and property FormulaLocal:
+ // The cell's number format is checked for "text", a new cell format may be set,
+ // but all parsing is in English.
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nOldFormat = rDoc.GetNumberFormat( aCellPos );
+ if (pFormatter->GetType(nOldFormat) == SvNumFormatType::TEXT)
+ {
+ SetString_Impl(rText, false, false); // text cell
+ return;
+ }
+
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+
+ ScInputStringType aRes =
+ ScStringUtil::parseInputString(*pFormatter, rText, LANGUAGE_ENGLISH_US);
+
+ if (aRes.meType != ScInputStringType::Unknown)
+ {
+ if ((nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && aRes.mnFormatType != SvNumFormatType::ALL)
+ {
+ // apply a format for the recognized type and the old format's language
+ sal_uInt32 nNewFormat = ScGlobal::GetStandardFormat(*pFormatter, nOldFormat, aRes.mnFormatType);
+ if (nNewFormat != nOldFormat)
+ {
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ // ATTR_LANGUAGE_FORMAT remains unchanged
+ rFunc.ApplyAttributes( *GetMarkData(), aPattern, true );
+ }
+ }
+ }
+ switch (aRes.meType)
+ {
+ case ScInputStringType::Formula:
+ rFunc.SetFormulaCell(
+ aCellPos,
+ new ScFormulaCell(rDoc, aCellPos, aRes.maText, formula::FormulaGrammar::GRAM_API),
+ false);
+ break;
+ case ScInputStringType::Number:
+ rFunc.SetValueCell(aCellPos, aRes.mfValue, false);
+ break;
+ case ScInputStringType::Text:
+ rFunc.SetStringOrEditCell(aCellPos, aRes.maText, false);
+ break;
+ default:
+ SetString_Impl(rText, false, false); // probably empty string
+ }
+}
+
+// XText
+
+uno::Reference<text::XTextCursor> SAL_CALL ScCellObj::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ return new ScCellTextCursor( *this );
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL ScCellObj::createTextCursorByRange(
+ const uno::Reference<text::XTextRange>& aTextPosition )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<SvxUnoTextCursor> pCursor = new ScCellTextCursor( *this );
+
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( aTextPosition );
+ if(pRange)
+ pCursor->SetSelection( pRange->GetSelection() );
+ else
+ {
+ ScCellTextCursor* pOther = comphelper::getFromUnoTunnel<ScCellTextCursor>( aTextPosition );
+ if(!pOther)
+ throw uno::RuntimeException();
+
+ pCursor->SetSelection( pOther->GetSelection() );
+
+ }
+
+ return pCursor;
+}
+
+OUString SAL_CALL ScCellObj::getString()
+{
+ SolarMutexGuard aGuard;
+ return GetOutputString_Impl();
+}
+
+void SAL_CALL ScCellObj::setString( const OUString& aText )
+{
+ SolarMutexGuard aGuard;
+ SetString_Impl(aText, false, false); // always text
+
+ // don't create pUnoText here if not there
+ if (mxUnoText.is())
+ mxUnoText->SetSelection(ESelection( 0,0, 0,aText.getLength() ));
+}
+
+void SAL_CALL ScCellObj::insertString( const uno::Reference<text::XTextRange>& xRange,
+ const OUString& aString, sal_Bool bAbsorb )
+{
+ // special handling for ScCellTextCursor is no longer needed,
+ // SvxUnoText::insertString checks for SvxUnoTextRangeBase instead of SvxUnoTextRange
+
+ SolarMutexGuard aGuard;
+ GetUnoText().insertString(xRange, aString, bAbsorb);
+}
+
+void SAL_CALL ScCellObj::insertControlCharacter( const uno::Reference<text::XTextRange>& xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ GetUnoText().insertControlCharacter(xRange, nControlCharacter, bAbsorb);
+}
+
+void SAL_CALL ScCellObj::insertTextContent( const uno::Reference<text::XTextRange >& xRange,
+ const uno::Reference<text::XTextContent >& xContent,
+ sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh && xContent.is() )
+ {
+ ScEditFieldObj* pCellField = dynamic_cast<ScEditFieldObj*>(xContent.get());
+ SvxUnoTextRangeBase* pTextRange = comphelper::getFromUnoTunnel<ScCellTextCursor>( xRange );
+
+ if ( pCellField && !pCellField->IsInserted() && pTextRange )
+ {
+ SvxEditSource* pEditSource = pTextRange->GetEditSource();
+ ESelection aSelection(pTextRange->GetSelection());
+
+ if (!bAbsorb)
+ {
+ // do not replace -> append
+ aSelection.Adjust();
+ aSelection.nStartPara = aSelection.nEndPara;
+ aSelection.nStartPos = aSelection.nEndPos;
+ }
+
+ if (pCellField->GetFieldType() == text::textfield::Type::TABLE)
+ pCellField->setPropertyValue(SC_UNONAME_TABLEPOS, uno::Any(sal_Int32(aCellPos.Tab())));
+
+ SvxFieldItem aItem = pCellField->CreateFieldItem();
+ SvxTextForwarder* pForwarder = pEditSource->GetTextForwarder();
+ pForwarder->QuickInsertField( aItem, aSelection );
+ pEditSource->UpdateData();
+
+ // new selection: a digit
+ aSelection.Adjust();
+ aSelection.nEndPara = aSelection.nStartPara;
+ aSelection.nEndPos = aSelection.nStartPos + 1;
+ uno::Reference<text::XTextRange> xParent(this);
+ pCellField->InitDoc(
+ xParent, std::make_unique<ScCellEditSource>(pDocSh, aCellPos), aSelection);
+
+ // for bAbsorb=FALSE, the new selection must be behind the inserted content
+ // (the xml filter relies on this)
+ if (!bAbsorb)
+ aSelection.nStartPos = aSelection.nEndPos;
+
+ pTextRange->SetSelection( aSelection );
+
+ return;
+ }
+ }
+ GetUnoText().insertTextContent(xRange, xContent, bAbsorb);
+}
+
+void SAL_CALL ScCellObj::removeTextContent( const uno::Reference<text::XTextContent>& xContent )
+{
+ SolarMutexGuard aGuard;
+ if ( xContent.is() )
+ {
+ ScEditFieldObj* pCellField = dynamic_cast<ScEditFieldObj*>(xContent.get());
+ if ( pCellField && pCellField->IsInserted() )
+ {
+ //! Check if field is in this cell
+ pCellField->DeleteField();
+ return;
+ }
+ }
+ GetUnoText().removeTextContent(xContent);
+}
+
+uno::Reference<text::XText> SAL_CALL ScCellObj::getText()
+{
+ return this;
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScCellObj::getStart()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getStart();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScCellObj::getEnd()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getEnd();
+}
+
+uno::Reference<container::XEnumeration> SAL_CALL ScCellObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().createEnumeration();
+}
+
+uno::Type SAL_CALL ScCellObj::getElementType()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getElementType();
+}
+
+sal_Bool SAL_CALL ScCellObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().hasElements();
+}
+
+// XCell
+
+OUString SAL_CALL ScCellObj::getFormula()
+{
+ SolarMutexGuard aGuard;
+ return GetInputString_Impl( true /* English */ );
+}
+
+void SAL_CALL ScCellObj::setFormula( const OUString& aFormula )
+{
+ SolarMutexGuard aGuard;
+ SetString_Impl(aFormula, true, true); // Interpret as English
+}
+
+double SAL_CALL ScCellObj::getValue()
+{
+ SolarMutexGuard aGuard;
+ return GetValue_Impl();
+}
+
+void SAL_CALL ScCellObj::setValue( double nValue )
+{
+ SolarMutexGuard aGuard;
+ SetValue_Impl(nValue);
+}
+
+void SAL_CALL ScCellObj::setFormulaString( const OUString& aFormula)
+{
+ SolarMutexGuard aGuard;
+ ScDocShell *pDocSh = GetDocShell();
+ if( pDocSh )
+ {
+ ScFormulaCell* pCell = new ScFormulaCell( pDocSh->GetDocument(), aCellPos );
+ pCell->SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE );
+ pDocSh->GetDocFunc().SetFormulaCell(aCellPos, pCell, false);
+ }
+}
+void SAL_CALL ScCellObj::setFormulaResult( double nValue )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ ScRefCellValue aCell(pDocSh->GetDocument(), aCellPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pCell = aCell.getFormula();
+ pCell->SetHybridDouble( nValue );
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ }
+ }
+}
+
+table::CellContentType SAL_CALL ScCellObj::getType()
+{
+ SolarMutexGuard aGuard;
+ table::CellContentType eRet = table::CellContentType_EMPTY;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ CellType eCalcType = pDocSh->GetDocument().GetCellType( aCellPos );
+ switch (eCalcType)
+ {
+ case CELLTYPE_VALUE:
+ eRet = table::CellContentType_VALUE;
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ eRet = table::CellContentType_TEXT;
+ break;
+ case CELLTYPE_FORMULA:
+ eRet = table::CellContentType_FORMULA;
+ break;
+ default:
+ eRet = table::CellContentType_EMPTY;
+ }
+ }
+ else
+ {
+ OSL_FAIL("no DocShell"); //! Exception or so?
+ }
+
+ return eRet;
+}
+
+sal_Int32 ScCellObj::GetResultType_Impl() const
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 eRet = sheet::FormulaResult::STRING;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ {
+ if (pDocSh->GetDocument().GetCellType(aCellPos) == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = pDocSh->GetDocument().GetFormulaCell(aCellPos);
+ if (!pFCell)
+ {
+ // should throw instead of default string?
+ }
+ else if (pFCell->GetErrCode() != FormulaError::NONE )
+ {
+ eRet = sheet::FormulaResult::ERROR;
+ }
+ else if (pFCell->IsValue())
+ {
+ eRet = sheet::FormulaResult::VALUE;
+ }
+ else
+ {
+ eRet = sheet::FormulaResult::STRING;
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("no DocShell");
+ }
+
+ return eRet;
+}
+
+table::CellContentType ScCellObj::GetContentType_Impl()
+{
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScRefCellValue aCell(pDocSh->GetDocument(), aCellPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ bool bValue = aCell.getFormula()->IsValue();
+ return bValue ? table::CellContentType_VALUE : table::CellContentType_TEXT;
+ }
+ }
+ return getType();
+}
+
+sal_Int32 SAL_CALL ScCellObj::getError()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ {
+ OSL_FAIL("no DocShell"); //! Exception or so?
+ return 0;
+ }
+
+ FormulaError nError = FormulaError::NONE;
+ ScRefCellValue aCell(pDocSh->GetDocument(), aCellPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ nError = aCell.getFormula()->GetErrCode();
+
+ return static_cast<sal_Int32>(nError);
+}
+
+// XFormulaTokens
+
+uno::Sequence<sheet::FormulaToken> SAL_CALL ScCellObj::getTokens()
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<sheet::FormulaToken> aSequence;
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return aSequence;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScRefCellValue aCell(rDoc, aCellPos);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScTokenArray* pTokenArray = aCell.getFormula()->GetCode();
+ if (pTokenArray)
+ ScTokenConversion::ConvertToTokenSequence(rDoc, aSequence, *pTokenArray);
+ }
+ return aSequence;
+}
+
+void SAL_CALL ScCellObj::setTokens( const uno::Sequence<sheet::FormulaToken>& rTokens )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScTokenArray aTokenArray(rDoc);
+ (void)ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, rTokens );
+
+ ScFormulaCell* pNewCell = new ScFormulaCell(rDoc, aCellPos, aTokenArray);
+ (void)pDocSh->GetDocFunc().SetFormulaCell(aCellPos, pNewCell, false);
+ }
+}
+
+// XCellAddressable
+
+table::CellAddress SAL_CALL ScCellObj::getCellAddress()
+{
+ SolarMutexGuard aGuard;
+ table::CellAddress aAdr;
+ aAdr.Sheet = aCellPos.Tab();
+ aAdr.Column = aCellPos.Col();
+ aAdr.Row = aCellPos.Row();
+ return aAdr;
+}
+
+// XSheetAnnotationAnchor
+
+uno::Reference<sheet::XSheetAnnotation> SAL_CALL ScCellObj::getAnnotation()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return new ScAnnotationObj( pDocSh, aCellPos );
+
+ OSL_FAIL("getAnnotation without DocShell");
+ return nullptr;
+}
+
+// XFieldTypesSupplier
+
+uno::Reference<container::XEnumerationAccess> SAL_CALL ScCellObj::getTextFields()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ uno::Reference<text::XTextRange> xContent(this);
+ return new ScCellFieldsObj(xContent, pDocSh, aCellPos);
+ }
+
+ return nullptr;
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScCellObj::getTextFieldMasters()
+{
+ // there is no such thing in Calc (?)
+ return nullptr;
+}
+
+// XPropertySet extended for Cell-Properties
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( pCellPropSet->getPropertyMap() ));
+ return aRef;
+}
+
+void ScCellObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue )
+{
+ if ( !pEntry )
+ return;
+
+ if ( pEntry->nWID == SC_WID_UNO_FORMLOC )
+ {
+ OUString aStrVal;
+ aValue >>= aStrVal;
+ SetString_Impl(aStrVal, true, false); // interpret locally
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_FORMRT || pEntry->nWID == SC_WID_UNO_FORMRT2
+ || pEntry->nWID == SC_WID_UNO_CELLCONTENTTYPE )
+ {
+ // Read-Only
+ //! Exception or so...
+ }
+ else
+ ScCellRangeObj::SetOnePropertyValue( pEntry, aValue );
+}
+
+void ScCellObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny )
+{
+ if ( !pEntry )
+ return;
+
+ if ( pEntry->nWID == SC_WID_UNO_FORMLOC )
+ {
+ // sal_False = local
+ rAny <<= GetInputString_Impl(false);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_FORMRT2 )
+ {
+ sal_Int32 eType = GetResultType_Impl();
+ rAny <<= eType;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLCONTENTTYPE || pEntry->nWID == SC_WID_UNO_FORMRT )
+ {
+ table::CellContentType eType = GetContentType_Impl();
+ rAny <<= eType;
+ }
+ else
+ ScCellRangeObj::GetOnePropertyValue(pEntry, rAny);
+}
+
+const SfxItemPropertyMap& ScCellObj::GetItemPropertyMap()
+{
+ return pCellPropSet->getPropertyMap();
+}
+
+// XServiceInfo
+
+OUString SAL_CALL ScCellObj::getImplementationName()
+{
+ return "ScCellObj";
+}
+
+sal_Bool SAL_CALL ScCellObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellObj::getSupportedServiceNames()
+{
+ return {SCSHEETCELL_SERVICE,
+ SCCELL_SERVICE,
+ SCCELLPROPERTIES_SERVICE,
+ SCCHARPROPERTIES_SERVICE,
+ SCPARAPROPERTIES_SERVICE,
+ SCSHEETCELLRANGE_SERVICE,
+ SCCELLRANGE_SERVICE};
+}
+
+// XActionLockable
+
+sal_Bool SAL_CALL ScCellObj::isActionLocked()
+{
+ SolarMutexGuard aGuard;
+ return nActionLockCount != 0;
+}
+
+void SAL_CALL ScCellObj::addActionLock()
+{
+ SolarMutexGuard aGuard;
+ if (!nActionLockCount)
+ {
+ if (mxUnoText.is())
+ {
+ ScCellEditSource* pEditSource =
+ static_cast<ScCellEditSource*> (mxUnoText->GetEditSource());
+ if (pEditSource)
+ pEditSource->SetDoUpdateData(false);
+ }
+ }
+ nActionLockCount++;
+}
+
+void SAL_CALL ScCellObj::removeActionLock()
+{
+ SolarMutexGuard aGuard;
+ if (nActionLockCount <= 0)
+ return;
+
+ nActionLockCount--;
+ if (nActionLockCount)
+ return;
+
+ if (mxUnoText.is())
+ {
+ ScCellEditSource* pEditSource =
+ static_cast<ScCellEditSource*> (mxUnoText->GetEditSource());
+ if (pEditSource)
+ {
+ pEditSource->SetDoUpdateData(true);
+ if (pEditSource->IsDirty())
+ pEditSource->UpdateData();
+ }
+ }
+}
+
+void SAL_CALL ScCellObj::setActionLocks( sal_Int16 nLock )
+{
+ SolarMutexGuard aGuard;
+ if (mxUnoText.is())
+ {
+ ScCellEditSource* pEditSource =
+ static_cast<ScCellEditSource*> (mxUnoText->GetEditSource());
+ if (pEditSource)
+ {
+ pEditSource->SetDoUpdateData(nLock == 0);
+ if ((nActionLockCount > 0) && (nLock == 0) && pEditSource->IsDirty())
+ pEditSource->UpdateData();
+ }
+ }
+ nActionLockCount = nLock;
+}
+
+sal_Int16 SAL_CALL ScCellObj::resetActionLocks()
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nRet(nActionLockCount);
+ if (mxUnoText.is())
+ {
+ ScCellEditSource* pEditSource =
+ static_cast<ScCellEditSource*> (mxUnoText->GetEditSource());
+ if (pEditSource)
+ {
+ pEditSource->SetDoUpdateData(true);
+ if (pEditSource->IsDirty())
+ pEditSource->UpdateData();
+ }
+ }
+ nActionLockCount = 0;
+ return nRet;
+}
+
+static ScRange MaxDocRange(ScDocShell* pDocSh, SCTAB nTab)
+{
+ const SCCOL nMaxcol = pDocSh ? pDocSh->GetDocument().MaxCol() : MAXCOL;
+ const SCROW nMaxRow = pDocSh ? pDocSh->GetDocument().MaxRow() : MAXROW;
+ return ScRange(0, 0, nTab, nMaxcol, nMaxRow, nTab);
+}
+
+ScTableSheetObj::ScTableSheetObj( ScDocShell* pDocSh, SCTAB nTab ) :
+ ScCellRangeObj( pDocSh, MaxDocRange(pDocSh, nTab) ),
+ pSheetPropSet(lcl_GetSheetPropertySet())
+{
+}
+
+ScTableSheetObj::~ScTableSheetObj()
+{
+}
+
+void ScTableSheetObj::InitInsertSheet(ScDocShell* pDocSh, SCTAB nTab)
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+ InitInsertRange( pDocSh, ScRange(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab) );
+}
+
+uno::Any SAL_CALL ScTableSheetObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XSpreadsheet*>(this),
+ static_cast<container::XNamed*>(this),
+ static_cast<sheet::XSheetPageBreak*>(this),
+ static_cast<sheet::XCellRangeMovement*>(this),
+ static_cast<table::XTableChartsSupplier*>(this),
+ static_cast<sheet::XDataPilotTablesSupplier*>(this),
+ static_cast<sheet::XScenariosSupplier*>(this),
+ static_cast<sheet::XSheetAnnotationsSupplier*>(this),
+ static_cast<drawing::XDrawPageSupplier*>(this),
+ static_cast<sheet::XPrintAreas*>(this),
+ static_cast<sheet::XSheetAuditing*>(this),
+ static_cast<sheet::XSheetOutline*>(this),
+ static_cast<util::XProtectable*>(this),
+ static_cast<sheet::XScenario*>(this),
+ static_cast<sheet::XScenarioEnhanced*>(this),
+ static_cast<sheet::XSheetLinkable*>(this),
+ static_cast<sheet::XExternalSheetName*>(this),
+ static_cast<document::XEventsSupplier*>(this),
+ static_cast<table::XTablePivotChartsSupplier*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScCellRangeObj::queryInterface( rType );
+}
+
+void SAL_CALL ScTableSheetObj::acquire() noexcept
+{
+ ScCellRangeObj::acquire();
+}
+
+void SAL_CALL ScTableSheetObj::release() noexcept
+{
+ ScCellRangeObj::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScTableSheetObj::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes = comphelper::concatSequences(
+ ScCellRangeObj::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<sheet::XSpreadsheet>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ cppu::UnoType<sheet::XSheetPageBreak>::get(),
+ cppu::UnoType<sheet::XCellRangeMovement>::get(),
+ cppu::UnoType<table::XTableChartsSupplier>::get(),
+ cppu::UnoType<sheet::XDataPilotTablesSupplier>::get(),
+ cppu::UnoType<sheet::XScenariosSupplier>::get(),
+ cppu::UnoType<sheet::XSheetAnnotationsSupplier>::get(),
+ cppu::UnoType<drawing::XDrawPageSupplier>::get(),
+ cppu::UnoType<sheet::XPrintAreas>::get(),
+ cppu::UnoType<sheet::XSheetAuditing>::get(),
+ cppu::UnoType<sheet::XSheetOutline>::get(),
+ cppu::UnoType<util::XProtectable>::get(),
+ cppu::UnoType<sheet::XScenario>::get(),
+ cppu::UnoType<sheet::XScenarioEnhanced>::get(),
+ cppu::UnoType<sheet::XSheetLinkable>::get(),
+ cppu::UnoType<sheet::XExternalSheetName>::get(),
+ cppu::UnoType<document::XEventsSupplier>::get(),
+ cppu::UnoType<table::XTablePivotChartsSupplier>::get()
+ } );
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScTableSheetObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// Helper functions
+
+SCTAB ScTableSheetObj::GetTab_Impl() const
+{
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE(rRanges.size() == 1, "What ranges ?!?!");
+ if ( !rRanges.empty() )
+ {
+ return rRanges[ 0 ].aStart.Tab();
+ }
+ return 0;
+}
+
+// former XSheet
+
+uno::Reference<table::XTableCharts> SAL_CALL ScTableSheetObj::getCharts()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return new ScChartsObj( pDocSh, GetTab_Impl() );
+
+ OSL_FAIL("no document");
+ return nullptr;
+}
+
+uno::Reference<table::XTablePivotCharts> SAL_CALL ScTableSheetObj::getPivotCharts()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if (pDocSh)
+ return new sc::TablePivotCharts(pDocSh, GetTab_Impl());
+
+ OSL_FAIL("no document");
+ return nullptr;
+}
+
+uno::Reference<sheet::XDataPilotTables> SAL_CALL ScTableSheetObj::getDataPilotTables()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return new ScDataPilotTablesObj(*pDocSh, GetTab_Impl());
+
+ OSL_FAIL("no document");
+ return nullptr;
+}
+
+uno::Reference<sheet::XScenarios> SAL_CALL ScTableSheetObj::getScenarios()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+
+ if ( pDocSh )
+ return new ScScenariosObj( pDocSh, GetTab_Impl() );
+
+ OSL_FAIL("no document");
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetAnnotations> SAL_CALL ScTableSheetObj::getAnnotations()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+
+ if ( pDocSh )
+ return new ScAnnotationsObj( pDocSh, GetTab_Impl() );
+
+ OSL_FAIL("no document");
+ return nullptr;
+}
+
+uno::Reference<table::XCellRange> SAL_CALL ScTableSheetObj::getCellRangeByName(
+ const OUString& rRange )
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::getCellRangeByName( rRange );
+}
+
+uno::Reference<sheet::XSheetCellCursor> SAL_CALL ScTableSheetObj::createCursor()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ //! single cell or whole table??????
+ const ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ return new ScCellCursorObj( pDocSh, ScRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ) );
+ }
+ return nullptr;
+}
+
+uno::Reference<sheet::XSheetCellCursor> SAL_CALL ScTableSheetObj::createCursorByRange(
+ const uno::Reference<sheet::XSheetCellRange>& xCellRange )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh && xCellRange.is() )
+ {
+ ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xCellRange.get() );
+ if (pRangesImp)
+ {
+ const ScRangeList& rRanges = pRangesImp->GetRangeList();
+ SAL_WARN_IF( rRanges.size() != 1, "sc", "ScTableSheetObj::createCursorByRange: Range? Ranges?");
+ if (rRanges.empty())
+ return nullptr;
+ return new ScCellCursorObj( pDocSh, rRanges[ 0 ] );
+ }
+ }
+ return nullptr;
+}
+
+// XSheetCellRange
+
+uno::Reference<sheet::XSpreadsheet> SAL_CALL ScTableSheetObj::getSpreadsheet()
+{
+ return this; //!???
+}
+
+// XCellRange
+
+uno::Reference<table::XCell> SAL_CALL ScTableSheetObj::getCellByPosition(
+ sal_Int32 nColumn, sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::GetCellByPosition_Impl(nColumn, nRow);
+}
+
+uno::Reference<table::XCellRange> SAL_CALL ScTableSheetObj::getCellRangeByPosition(
+ sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::getCellRangeByPosition(nLeft,nTop,nRight,nBottom);
+}
+
+uno::Sequence<sheet::TablePageBreakData> SAL_CALL ScTableSheetObj::getColumnPageBreaks()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ Size aSize(rDoc.GetPageSize( nTab ));
+ if (aSize.Width() && aSize.Height()) // effective size already set?
+ rDoc.UpdatePageBreaks( nTab );
+ else
+ {
+ // update breaks like in ScDocShell::PageStyleModified:
+ ScPrintFunc aPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ }
+
+ SCCOL nCount = 0;
+ for (SCCOL nCol : rDoc.GetColumnsRange(nTab, 0, rDoc.MaxCol()))
+ if (rDoc.HasColBreak(nCol, nTab) != ScBreakType::NONE)
+ ++nCount;
+
+ sheet::TablePageBreakData aData;
+ uno::Sequence<sheet::TablePageBreakData> aSeq(nCount);
+ sheet::TablePageBreakData* pAry = aSeq.getArray();
+ sal_uInt16 nPos = 0;
+ for (SCCOL nCol : rDoc.GetColumnsRange(nTab, 0, rDoc.MaxCol()))
+ {
+ ScBreakType nBreak = rDoc.HasColBreak(nCol, nTab);
+ if (nBreak != ScBreakType::NONE)
+ {
+ aData.Position = nCol;
+ aData.ManualBreak = bool(nBreak & ScBreakType::Manual);
+ pAry[nPos] = aData;
+ ++nPos;
+ }
+ }
+ return aSeq;
+ }
+ return {};
+}
+
+uno::Sequence<sheet::TablePageBreakData> SAL_CALL ScTableSheetObj::getRowPageBreaks()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ Size aSize(rDoc.GetPageSize( nTab ));
+ if (aSize.Width() && aSize.Height()) // effective size already set?
+ rDoc.UpdatePageBreaks( nTab );
+ else
+ {
+ // update breaks like in ScDocShell::PageStyleModified:
+ ScPrintFunc aPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ }
+ return rDoc.GetRowBreakData(nTab);
+ }
+ return {};
+}
+
+void SAL_CALL ScTableSheetObj::removeAllManualPageBreaks()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ //! DocFunc function, also for ScViewFunc::RemoveManualBreaks
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo (rDoc.IsUndoEnabled());
+ SCTAB nTab = GetTab_Impl();
+
+ if (bUndo)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::NONE, false, *pUndoDoc);
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveBreaks>( pDocSh, nTab, std::move(pUndoDoc) ) );
+ }
+
+ rDoc.RemoveManualBreaks(nTab);
+ rDoc.UpdatePageBreaks(nTab);
+
+ //? UpdatePageBreakData( sal_True );
+ pDocSh->SetDocumentModified();
+ pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), PaintPartFlags::Grid);
+}
+
+// XNamed
+
+OUString SAL_CALL ScTableSheetObj::getName()
+{
+ SolarMutexGuard aGuard;
+ OUString aName;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ pDocSh->GetDocument().GetName( GetTab_Impl(), aName );
+ return aName;
+}
+
+void SAL_CALL ScTableSheetObj::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ pDocSh->GetDocFunc().RenameTable( GetTab_Impl(), aNewName, true, true );
+ }
+}
+
+// XDrawPageSupplier
+
+uno::Reference<drawing::XDrawPage> SAL_CALL ScTableSheetObj::getDrawPage()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDrawLayer* pDrawLayer = pDocSh->MakeDrawLayer();
+ OSL_ENSURE(pDrawLayer,"Cannot create Draw-Layer");
+
+ SCTAB nTab = GetTab_Impl();
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Draw-Page not found");
+ if (pPage)
+ return uno::Reference<drawing::XDrawPage> (pPage->getUnoPage(), uno::UNO_QUERY);
+
+ // The DrawPage object will register itself as a Listener at SdrModel
+ // and should receive all action from there
+ }
+ return nullptr;
+}
+
+// XCellMovement
+
+void SAL_CALL ScTableSheetObj::insertCells( const table::CellRangeAddress& rRangeAddress,
+ sheet::CellInsertMode nMode )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ bool bDo = true;
+ InsCellCmd eCmd = INS_NONE;
+ switch (nMode)
+ {
+ case sheet::CellInsertMode_NONE: bDo = false; break;
+ case sheet::CellInsertMode_DOWN: eCmd = INS_CELLSDOWN; break;
+ case sheet::CellInsertMode_RIGHT: eCmd = INS_CELLSRIGHT; break;
+ case sheet::CellInsertMode_ROWS: eCmd = INS_INSROWS_BEFORE; break;
+ case sheet::CellInsertMode_COLUMNS: eCmd = INS_INSCOLS_BEFORE; break;
+ default:
+ OSL_FAIL("insertCells: wrong mode");
+ bDo = false;
+ }
+
+ if (bDo)
+ {
+ OSL_ENSURE( rRangeAddress.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" );
+ ScRange aScRange;
+ ScUnoConversion::FillScRange( aScRange, rRangeAddress );
+ (void)pDocSh->GetDocFunc().InsertCells( aScRange, nullptr, eCmd, true, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::removeRange( const table::CellRangeAddress& rRangeAddress,
+ sheet::CellDeleteMode nMode )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ bool bDo = true;
+ DelCellCmd eCmd = DelCellCmd::NONE;
+ switch (nMode)
+ {
+ case sheet::CellDeleteMode_NONE: bDo = false; break;
+ case sheet::CellDeleteMode_UP: eCmd = DelCellCmd::CellsUp; break;
+ case sheet::CellDeleteMode_LEFT: eCmd = DelCellCmd::CellsLeft; break;
+ case sheet::CellDeleteMode_ROWS: eCmd = DelCellCmd::Rows; break;
+ case sheet::CellDeleteMode_COLUMNS: eCmd = DelCellCmd::Cols; break;
+ default:
+ OSL_FAIL("deleteCells: wrong mode");
+ bDo = false;
+ }
+
+ if (bDo)
+ {
+ OSL_ENSURE( rRangeAddress.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" );
+ ScRange aScRange;
+ ScUnoConversion::FillScRange( aScRange, rRangeAddress );
+ (void)pDocSh->GetDocFunc().DeleteCells( aScRange, nullptr, eCmd, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::moveRange( const table::CellAddress& aDestination,
+ const table::CellRangeAddress& aSource )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ OSL_ENSURE( aSource.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" );
+ ScRange aSourceRange;
+ ScUnoConversion::FillScRange( aSourceRange, aSource );
+ ScAddress aDestPos( static_cast<SCCOL>(aDestination.Column), static_cast<SCROW>(aDestination.Row), aDestination.Sheet );
+ (void)pDocSh->GetDocFunc().MoveBlock( aSourceRange, aDestPos, true, true, true, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::copyRange( const table::CellAddress& aDestination,
+ const table::CellRangeAddress& aSource )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ OSL_ENSURE( aSource.Sheet == GetTab_Impl(), "wrong table in CellRangeAddress" );
+ ScRange aSourceRange;
+ ScUnoConversion::FillScRange( aSourceRange, aSource );
+ ScAddress aDestPos( static_cast<SCCOL>(aDestination.Column), static_cast<SCROW>(aDestination.Row), aDestination.Sheet );
+ (void)pDocSh->GetDocFunc().MoveBlock( aSourceRange, aDestPos, false, true, true, true );
+ }
+}
+
+// XPrintAreas
+
+void ScTableSheetObj::PrintAreaUndo_Impl( std::unique_ptr<ScPrintRangeSaver> pOldRanges )
+{
+ // page break and undo
+ ScDocShell* pDocSh = GetDocShell();
+
+ if(!pDocSh)
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const bool bUndo(rDoc.IsUndoEnabled());
+ const SCTAB nTab(GetTab_Impl());
+
+ if(bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPrintRange>(
+ pDocSh,
+ nTab,
+ std::move(pOldRanges),
+ rDoc.CreatePrintRangeSaver())); // create new ranges
+ }
+
+ ScPrintFunc(pDocSh, pDocSh->GetPrinter(), nTab).UpdatePages();
+ SfxBindings* pBindings = pDocSh->GetViewBindings();
+
+ if(pBindings)
+ {
+ pBindings->Invalidate(SID_DELETE_PRINTAREA);
+ }
+
+ pDocSh->SetDocumentModified();
+}
+
+uno::Sequence<table::CellRangeAddress> SAL_CALL ScTableSheetObj::getPrintAreas()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ sal_uInt16 nCount = rDoc.GetPrintRangeCount( nTab );
+
+ table::CellRangeAddress aRangeAddress;
+ uno::Sequence<table::CellRangeAddress> aSeq(nCount);
+ table::CellRangeAddress* pAry = aSeq.getArray();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ const ScRange* pRange = rDoc.GetPrintRange( nTab, i );
+ OSL_ENSURE(pRange,"where is the printing area");
+ if (pRange)
+ {
+ ScUnoConversion::FillApiRange( aRangeAddress, *pRange );
+ aRangeAddress.Sheet = nTab; // core does not care about sheet index
+ pAry[i] = aRangeAddress;
+ }
+ }
+ return aSeq;
+ }
+ return uno::Sequence<table::CellRangeAddress>();
+}
+
+void SAL_CALL ScTableSheetObj::setPrintAreas(
+ const uno::Sequence<table::CellRangeAddress>& aPrintAreas )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges;
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ if ( rDoc.IsUndoEnabled() )
+ pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ sal_uInt16 nCount = static_cast<sal_uInt16>(aPrintAreas.getLength());
+ rDoc.ClearPrintRanges( nTab );
+ if (nCount)
+ {
+ ScRange aPrintRange;
+ for (const table::CellRangeAddress& rPrintArea : aPrintAreas)
+ {
+ ScUnoConversion::FillScRange( aPrintRange, rPrintArea );
+ rDoc.AddPrintRange( nTab, aPrintRange );
+ }
+ }
+
+ if ( rDoc.IsUndoEnabled() )
+ PrintAreaUndo_Impl( std::move(pOldRanges) ); // Undo, Page Breaks, Modified etc.
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::getPrintTitleColumns()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ return rDoc.GetRepeatColRange(nTab).has_value();
+ }
+ return false;
+}
+
+void SAL_CALL ScTableSheetObj::setPrintTitleColumns( sal_Bool bPrintTitleColumns )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ if ( bPrintTitleColumns )
+ {
+ if ( !rDoc.GetRepeatColRange( nTab ) ) // do not change existing area
+ {
+ rDoc.SetRepeatColRange( nTab, ScRange( 0, 0, nTab, 0, 0, nTab ) ); // enable
+ }
+ }
+ else
+ rDoc.SetRepeatColRange( nTab, std::nullopt ); // disable
+
+ PrintAreaUndo_Impl( std::move(pOldRanges) ); // undo, page break, modified etc.
+
+ //! save last set area during switch off and recreate during switch on ???
+}
+
+table::CellRangeAddress SAL_CALL ScTableSheetObj::getTitleColumns()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aRet;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ std::optional<ScRange> oRange = rDoc.GetRepeatColRange(nTab);
+ if (oRange)
+ {
+ ScUnoConversion::FillApiRange( aRet, *oRange );
+ aRet.Sheet = nTab; // core does not care about sheet index
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL ScTableSheetObj::setTitleColumns( const table::CellRangeAddress& aTitleColumns )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ ScRange aNew;
+ ScUnoConversion::FillScRange( aNew, aTitleColumns );
+ rDoc.SetRepeatColRange( nTab, std::move(aNew) ); // also always enable
+
+ PrintAreaUndo_Impl( std::move(pOldRanges) ); // undo, page breaks, modified etc.
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::getPrintTitleRows()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ return rDoc.GetRepeatRowRange(nTab).has_value();
+ }
+ return false;
+}
+
+void SAL_CALL ScTableSheetObj::setPrintTitleRows( sal_Bool bPrintTitleRows )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ if ( bPrintTitleRows )
+ {
+ if ( !rDoc.GetRepeatRowRange( nTab ) ) // do not change existing area
+ {
+ rDoc.SetRepeatRowRange( nTab, ScRange(0, 0, nTab, 0, 0, nTab) ); // enable
+ }
+ }
+ else
+ rDoc.SetRepeatRowRange( nTab, std::nullopt ); // disable
+
+ PrintAreaUndo_Impl( std::move(pOldRanges) ); // undo, page breaks, modified etc.
+
+ //! save last set area during switch off and recreate during switch on ???
+}
+
+table::CellRangeAddress SAL_CALL ScTableSheetObj::getTitleRows()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aRet;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ std::optional<ScRange> oRange = rDoc.GetRepeatRowRange(nTab);
+ if (oRange)
+ {
+ ScUnoConversion::FillApiRange( aRet, *oRange );
+ aRet.Sheet = nTab; // core does not care about sheet index
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL ScTableSheetObj::setTitleRows( const table::CellRangeAddress& aTitleRows )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ ScRange aNew;
+ ScUnoConversion::FillScRange( aNew, aTitleRows );
+ rDoc.SetRepeatRowRange( nTab, std::move(aNew) ); // also always enable
+
+ PrintAreaUndo_Impl( std::move(pOldRanges) ); // Undo, page breaks, modified etc.
+}
+
+// XSheetLinkable
+
+sheet::SheetLinkMode SAL_CALL ScTableSheetObj::getLinkMode()
+{
+ SolarMutexGuard aGuard;
+ sheet::SheetLinkMode eRet = sheet::SheetLinkMode_NONE;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScLinkMode nMode = pDocSh->GetDocument().GetLinkMode( GetTab_Impl() );
+ if ( nMode == ScLinkMode::NORMAL )
+ eRet = sheet::SheetLinkMode_NORMAL;
+ else if ( nMode == ScLinkMode::VALUE )
+ eRet = sheet::SheetLinkMode_VALUE;
+ }
+ return eRet;
+}
+
+void SAL_CALL ScTableSheetObj::setLinkMode( sheet::SheetLinkMode nLinkMode )
+{
+ SolarMutexGuard aGuard;
+
+ //! search for filter and options in old link
+
+ OUString aUrl(getLinkUrl());
+ OUString aSheet(getLinkSheetName());
+
+ link( aUrl, aSheet, "", "", nLinkMode );
+}
+
+OUString SAL_CALL ScTableSheetObj::getLinkUrl()
+{
+ SolarMutexGuard aGuard;
+ OUString aFile;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ aFile = pDocSh->GetDocument().GetLinkDoc( GetTab_Impl() );
+ return aFile;
+}
+
+void SAL_CALL ScTableSheetObj::setLinkUrl( const OUString& aLinkUrl )
+{
+ SolarMutexGuard aGuard;
+
+ //! search for filter and options in old link
+
+ sheet::SheetLinkMode eMode = getLinkMode();
+ OUString aSheet(getLinkSheetName());
+
+ link( aLinkUrl, aSheet, "", "", eMode );
+}
+
+OUString SAL_CALL ScTableSheetObj::getLinkSheetName()
+{
+ SolarMutexGuard aGuard;
+ OUString aSheet;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ aSheet = pDocSh->GetDocument().GetLinkTab( GetTab_Impl() );
+ return aSheet;
+}
+
+void SAL_CALL ScTableSheetObj::setLinkSheetName( const OUString& aLinkSheetName )
+{
+ SolarMutexGuard aGuard;
+
+ //! search for filter and options in old link
+
+ sheet::SheetLinkMode eMode = getLinkMode();
+ OUString aUrl(getLinkUrl());
+
+ link( aUrl, aLinkSheetName, "", "", eMode );
+}
+
+void SAL_CALL ScTableSheetObj::link( const OUString& aUrl, const OUString& aSheetName,
+ const OUString& aFilterName, const OUString& aFilterOptions,
+ sheet::SheetLinkMode nMode )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ OUString aFileString = aUrl;
+ OUString aFilterString = aFilterName;
+ OUString aOptString = aFilterOptions;
+
+ aFileString = ScGlobal::GetAbsDocName( aFileString, pDocSh );
+ if (aFilterString.isEmpty())
+ ScDocumentLoader::GetFilterName( aFileString, aFilterString, aOptString, true, false );
+
+ // remove application prefix from filter name here, so the filter options
+ // aren't reset when the filter name is changed in ScTableLink::DataChanged
+ ScDocumentLoader::RemoveAppPrefix( aFilterString );
+
+ ScLinkMode nLinkMode = ScLinkMode::NONE;
+ if ( nMode == sheet::SheetLinkMode_NORMAL )
+ nLinkMode = ScLinkMode::NORMAL;
+ else if ( nMode == sheet::SheetLinkMode_VALUE )
+ nLinkMode = ScLinkMode::VALUE;
+
+ rDoc.SetLink( nTab, nLinkMode, aFileString, aFilterString, aOptString, aSheetName, 0/*nRefresh*/ );
+
+ pDocSh->UpdateLinks(); // if needed add or delete link
+ SfxBindings* pBindings = pDocSh->GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate(SID_LINKS);
+
+ //! undo of link data on the table
+
+ if ( !(nLinkMode != ScLinkMode::NONE && rDoc.IsExecuteLinkEnabled()) ) // update link
+ return;
+
+ // Always update link also if already exists
+ //! update only on the affected table???
+
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+ sal_uInt16 nCount = pLinkManager->GetLinks().size();
+ for ( sal_uInt16 i=0; i<nCount; i++ )
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
+ if (auto pTabLink = dynamic_cast<ScTableLink*>( pBase))
+ {
+ if ( aFileString == pTabLink->GetFileName() )
+ pTabLink->Update(); // include Paint&Undo
+
+ //! The file name should only exists once (?)
+ }
+ }
+
+ //! notify ScSheetLinkObj objects!!!
+}
+
+// XSheetAuditing
+
+sal_Bool SAL_CALL ScTableSheetObj::hideDependents( const table::CellAddress& aPosition )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ SCTAB nTab = GetTab_Impl();
+ OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" );
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab );
+ return pDocSh->GetDocFunc().DetectiveDelSucc( aPos );
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::hidePrecedents( const table::CellAddress& aPosition )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ SCTAB nTab = GetTab_Impl();
+ OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" );
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab );
+ return pDocSh->GetDocFunc().DetectiveDelPred( aPos );
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::showDependents( const table::CellAddress& aPosition )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ SCTAB nTab = GetTab_Impl();
+ OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" );
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab );
+ return pDocSh->GetDocFunc().DetectiveAddSucc( aPos );
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::showPrecedents( const table::CellAddress& aPosition )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ SCTAB nTab = GetTab_Impl();
+ OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" );
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab );
+ return pDocSh->GetDocFunc().DetectiveAddPred( aPos );
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::showErrors( const table::CellAddress& aPosition )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ SCTAB nTab = GetTab_Impl();
+ OSL_ENSURE( aPosition.Sheet == nTab, "wrong table in CellAddress" );
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab );
+ return pDocSh->GetDocFunc().DetectiveAddError( aPos );
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::showInvalid()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return pDocSh->GetDocFunc().DetectiveMarkInvalid( GetTab_Impl() );
+ return false;
+}
+
+void SAL_CALL ScTableSheetObj::clearArrows()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ pDocSh->GetDocFunc().DetectiveDelAll( GetTab_Impl() );
+}
+
+// XSheetOutline
+
+void SAL_CALL ScTableSheetObj::group( const table::CellRangeAddress& rGroupRange,
+ table::TableOrientation nOrientation )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ bool bColumns = ( nOrientation == table::TableOrientation_COLUMNS );
+ ScRange aGroupRange;
+ ScUnoConversion::FillScRange( aGroupRange, rGroupRange );
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.MakeOutline( aGroupRange, bColumns, true, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::ungroup( const table::CellRangeAddress& rGroupRange,
+ table::TableOrientation nOrientation )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ bool bColumns = ( nOrientation == table::TableOrientation_COLUMNS );
+ ScRange aGroupRange;
+ ScUnoConversion::FillScRange( aGroupRange, rGroupRange );
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.RemoveOutline( aGroupRange, bColumns, true, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::autoOutline( const table::CellRangeAddress& rCellRange )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScRange aFormulaRange;
+ ScUnoConversion::FillScRange( aFormulaRange, rCellRange );
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.AutoOutline( aFormulaRange, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::clearOutline()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ SCTAB nTab = GetTab_Impl();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.RemoveAllOutlines( nTab, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::hideDetail( const table::CellRangeAddress& rCellRange )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScRange aMarkRange;
+ ScUnoConversion::FillScRange( aMarkRange, rCellRange );
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.HideMarkedOutlines( aMarkRange, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::showDetail( const table::CellRangeAddress& rCellRange )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScRange aMarkRange;
+ ScUnoConversion::FillScRange( aMarkRange, rCellRange );
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.ShowMarkedOutlines( aMarkRange, true );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::showLevel( sal_Int16 nLevel, table::TableOrientation nOrientation )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ bool bColumns = ( nOrientation == table::TableOrientation_COLUMNS );
+ SCTAB nTab = GetTab_Impl();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.SelectLevel( nTab, bColumns, nLevel, true, true );
+ }
+}
+
+// XProtectable
+
+void SAL_CALL ScTableSheetObj::protect( const OUString& aPassword )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ // #i108245# if already protected, don't change anything
+ if ( pDocSh && !pDocSh->GetDocument().IsTabProtected( GetTab_Impl() ) )
+ {
+ pDocSh->GetDocFunc().Protect( GetTab_Impl(), aPassword );
+ }
+}
+
+void SAL_CALL ScTableSheetObj::unprotect( const OUString& aPassword )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ bool bDone = pDocSh->GetDocFunc().Unprotect( GetTab_Impl(), aPassword, true );
+ if (!bDone)
+ throw lang::IllegalArgumentException();
+ }
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::isProtected()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return pDocSh->GetDocument().IsTabProtected( GetTab_Impl() );
+
+ OSL_FAIL("no DocShell"); //! Exception or so?
+ return false;
+}
+
+// XScenario
+
+sal_Bool SAL_CALL ScTableSheetObj::getIsScenario()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return pDocSh->GetDocument().IsScenario( GetTab_Impl() );
+
+ return false;
+}
+
+OUString SAL_CALL ScTableSheetObj::getScenarioComment()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ pDocSh->GetDocument().GetScenarioData( GetTab_Impl(), aComment, aColor, nFlags );
+ return aComment;
+ }
+ return OUString();
+}
+
+void SAL_CALL ScTableSheetObj::setScenarioComment( const OUString& aScenarioComment )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+
+ aComment = aScenarioComment;
+
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+}
+
+void SAL_CALL ScTableSheetObj::addRanges( const uno::Sequence<table::CellRangeAddress>& rScenRanges )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ if (!rDoc.IsScenario(nTab))
+ return;
+
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+ aMarkData.SelectTable( nTab, true );
+
+ for (const table::CellRangeAddress& rRange : rScenRanges)
+ {
+ OSL_ENSURE( rRange.Sheet == nTab, "addRanges with wrong Tab" );
+ ScRange aOneRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), nTab,
+ static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), nTab );
+
+ aMarkData.SetMultiMarkArea( aOneRange );
+ }
+
+ // Scenario ranges are tagged with attribute
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) );
+ aPattern.GetItemSet().Put( ScProtectionAttr( true ) );
+ pDocSh->GetDocFunc().ApplyAttributes( aMarkData, aPattern, true );
+}
+
+void SAL_CALL ScTableSheetObj::apply()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ OUString aName;
+ rDoc.GetName( nTab, aName ); // scenario name
+
+ SCTAB nDestTab = nTab;
+ while ( nDestTab > 0 && rDoc.IsScenario(nDestTab) )
+ --nDestTab;
+
+ if ( !rDoc.IsScenario(nDestTab) )
+ pDocSh->UseScenario( nDestTab, aName );
+
+ //! otherwise error or so
+}
+
+// XScenarioEnhanced
+
+uno::Sequence< table::CellRangeAddress > SAL_CALL ScTableSheetObj::getRanges( )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ const ScRangeList* pRangeList = rDoc.GetScenarioRanges(nTab);
+ if (pRangeList)
+ {
+ size_t nCount = pRangeList->size();
+ uno::Sequence< table::CellRangeAddress > aRetRanges( nCount );
+ table::CellRangeAddress* pAry = aRetRanges.getArray();
+ for( size_t nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ const ScRange & rRange = (*pRangeList)[nIndex];
+ pAry->StartColumn = rRange.aStart.Col();
+ pAry->StartRow = rRange.aStart.Row();
+ pAry->EndColumn = rRange.aEnd.Col();
+ pAry->EndRow = rRange.aEnd.Row();
+ pAry->Sheet = rRange.aStart.Tab();
+ ++pAry;
+ }
+ return aRetRanges;
+ }
+ }
+ return uno::Sequence< table::CellRangeAddress > ();
+}
+
+// XExternalSheetName
+
+void ScTableSheetObj::setExternalName( const OUString& aUrl, const OUString& aSheetName )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const SCTAB nTab = GetTab_Impl();
+ const OUString aAbsDocName( ScGlobal::GetAbsDocName( aUrl, pDocSh ) );
+ const OUString aDocTabName( ScGlobal::GetDocTabName( aAbsDocName, aSheetName ) );
+ if ( !rDoc.RenameTab( nTab, aDocTabName, true /*bExternalDocument*/ ) )
+ {
+ throw container::ElementExistException( OUString(), *this );
+ }
+ }
+}
+
+// XEventsSupplier
+
+uno::Reference<container::XNameReplace> SAL_CALL ScTableSheetObj::getEvents()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ return new ScSheetEventsObj( pDocSh, GetTab_Impl() );
+
+ return nullptr;
+}
+
+// XPropertySet extended for Sheet-Properties
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableSheetObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( pSheetPropSet->getPropertyMap() ));
+ return aRef;
+}
+
+void ScTableSheetObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue )
+{
+ if ( !pEntry )
+ return;
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ // for Item WIDs, call ScCellRangesBase directly
+ ScCellRangesBase::SetOnePropertyValue(pEntry, aValue);
+ return;
+ }
+
+ // own properties
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return; //! Exception or so?
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+
+ if ( pEntry->nWID == SC_WID_UNO_PAGESTL )
+ {
+ OUString aStrVal;
+ aValue >>= aStrVal;
+ OUString aNewStr(ScStyleNameConversion::ProgrammaticToDisplayName(
+ aStrVal, SfxStyleFamily::Page ));
+
+ //! Undo? (also if SID_STYLE_APPLY on View)
+
+ if ( rDoc.GetPageStyle( nTab ) != aNewStr )
+ {
+ rDoc.SetPageStyle( nTab, aNewStr );
+ if (!rDoc.IsImportingXML())
+ {
+ ScPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab ).UpdatePages();
+
+ SfxBindings* pBindings = pDocSh->GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( SID_STYLE_FAMILY4 );
+ pBindings->Invalidate( SID_STATUS_PAGESTYLE );
+ pBindings->Invalidate( FID_RESET_PRINTZOOM );
+ pBindings->Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ pBindings->Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ }
+ }
+ pDocSh->SetDocumentModified();
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLVIS )
+ {
+ bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ rFunc.SetTableVisible( nTab, bVis, true );
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_ISACTIVE )
+ {
+ if (rDoc.IsScenario(nTab))
+ rDoc.SetActiveScenario( nTab, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_BORDCOL )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ Color aColor;
+ if (aValue >>= aColor)
+ {
+ OUString aName;
+ OUString aComment;
+ ScScenarioFlags nFlags;
+ Color aTmp;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aTmp, nFlags );
+
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_PROTECT )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+ bool bModify(false);
+
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ if (!(nFlags & ScScenarioFlags::Protected))
+ {
+ nFlags |= ScScenarioFlags::Protected;
+ bModify = true;
+ }
+ }
+ else
+ {
+ if (nFlags & ScScenarioFlags::Protected)
+ {
+ nFlags &= ~ScScenarioFlags::Protected;
+ bModify = true;
+ }
+ }
+
+ if (bModify)
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_SHOWBORD )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+ bool bModify(false);
+
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ if (!(nFlags & ScScenarioFlags::ShowFrame))
+ {
+ nFlags |= ScScenarioFlags::ShowFrame;
+ bModify = true;
+ }
+ }
+ else
+ {
+ if (nFlags & ScScenarioFlags::ShowFrame)
+ {
+ nFlags &= ~ScScenarioFlags::ShowFrame;
+ bModify = true;
+ }
+ }
+
+ if (bModify)
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_PRINTBORD )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+ bool bModify(false);
+
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ if (!(nFlags & ScScenarioFlags::PrintFrame))
+ {
+ nFlags |= ScScenarioFlags::PrintFrame;
+ bModify = true;
+ }
+ }
+ else
+ {
+ if (nFlags & ScScenarioFlags::PrintFrame)
+ {
+ nFlags &= ~ScScenarioFlags::PrintFrame;
+ bModify = true;
+ }
+ }
+
+ if (bModify)
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_COPYBACK )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+ bool bModify(false);
+
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ if (!(nFlags & ScScenarioFlags::TwoWay))
+ {
+ nFlags |= ScScenarioFlags::TwoWay;
+ bModify = true;
+ }
+ }
+ else
+ {
+ if (nFlags & ScScenarioFlags::TwoWay)
+ {
+ nFlags &= ~ScScenarioFlags::TwoWay;
+ bModify = true;
+ }
+ }
+
+ if (bModify)
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_COPYSTYL )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+ bool bModify(false);
+
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ if (!(nFlags & ScScenarioFlags::Attrib))
+ {
+ nFlags |= ScScenarioFlags::Attrib;
+ bModify = true;
+ }
+ }
+ else
+ {
+ if (nFlags & ScScenarioFlags::Attrib)
+ {
+ nFlags &= ~ScScenarioFlags::Attrib;
+ bModify = true;
+ }
+ }
+
+ if (bModify)
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_COPYFORM )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aName;
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetName( nTab, aName );
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+ bool bModify(false);
+
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ if (nFlags & ScScenarioFlags::Value)
+ {
+ nFlags &= ~ScScenarioFlags::Value;
+ bModify = true;
+ }
+ }
+ else
+ {
+ if (!(nFlags & ScScenarioFlags::Value))
+ {
+ nFlags |= ScScenarioFlags::Value;
+ bModify = true;
+ }
+ }
+
+ if (bModify)
+ pDocSh->ModifyScenario( nTab, aName, aComment, aColor, nFlags );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_TABLAYOUT )
+ {
+ sal_Int16 nValue = 0;
+ if (aValue >>= nValue)
+ {
+ if (nValue == css::text::WritingMode2::RL_TB)
+ rFunc.SetLayoutRTL(nTab, true);
+ else
+ rFunc.SetLayoutRTL(nTab, false);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_AUTOPRINT )
+ {
+ bool bAutoPrint = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bAutoPrint)
+ rDoc.SetPrintEntireSheet( nTab ); // clears all print ranges
+ else
+ {
+ if (rDoc.IsPrintEntireSheet( nTab ))
+ rDoc.ClearPrintRanges( nTab ); // if this flag is true, there are no PrintRanges, so Clear clears only the flag.
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_TABCOLOR )
+ {
+ Color aColor = COL_AUTO;
+ if ( aValue >>= aColor )
+ {
+ if ( rDoc.GetTabBgColor( nTab ) != aColor )
+ rFunc.SetTabBgColor( nTab, aColor, true, true );
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CODENAME )
+ {
+ OUString aCodeName;
+ if (aValue >>= aCodeName)
+ {
+ pDocSh->GetDocument().SetCodeName( GetTab_Impl(), aCodeName );
+ }
+ }
+ else if (pEntry->nWID == SC_WID_UNO_CONDFORMAT)
+ {
+ uno::Reference<sheet::XConditionalFormats> xCondFormat;
+ if (aValue >>= xCondFormat)
+ {
+ // how to set the format correctly
+ }
+ }
+ else
+ ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID
+}
+
+void ScTableSheetObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry,
+ uno::Any& rAny )
+{
+ if ( !pEntry )
+ return;
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ throw uno::RuntimeException();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetTab_Impl();
+
+ if ( pEntry->nWID == SC_WID_UNO_NAMES )
+ {
+ rAny <<= uno::Reference<sheet::XNamedRanges>(new ScLocalNamedRangesObj(pDocSh, this));
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_PAGESTL )
+ {
+ rAny <<= ScStyleNameConversion::DisplayToProgrammaticName(
+ rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page );
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLVIS )
+ {
+ bool bVis = rDoc.IsVisible( nTab );
+ rAny <<= bVis;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_LINKDISPBIT )
+ {
+ // no target bitmaps for individual entries (would be all equal)
+ // ScLinkTargetTypeObj::SetLinkTargetBitmap( aAny, SC_LINKTARGETTYPE_SHEET );
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_LINKDISPNAME )
+ {
+ // LinkDisplayName for hyperlink dialog
+ rAny <<= getName(); // sheet name
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_ISACTIVE )
+ {
+ if (rDoc.IsScenario(nTab))
+ rAny <<= rDoc.IsActiveScenario( nTab );
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_BORDCOL )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioData( nTab, aComment, aColor, nFlags );
+
+ rAny <<= aColor;
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_PROTECT )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioFlags(nTab, nFlags);
+
+ rAny <<= ((nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_SHOWBORD )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioFlags(nTab, nFlags);
+
+ rAny <<= ((nFlags & ScScenarioFlags::ShowFrame) != ScScenarioFlags::NONE);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_PRINTBORD )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioFlags(nTab, nFlags);
+
+ rAny <<= ((nFlags & ScScenarioFlags::PrintFrame) != ScScenarioFlags::NONE);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_COPYBACK )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioFlags(nTab, nFlags);
+
+ rAny <<= ((nFlags & ScScenarioFlags::TwoWay) != ScScenarioFlags::NONE);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_COPYSTYL )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioFlags(nTab, nFlags);
+
+ rAny <<= ((nFlags & ScScenarioFlags::Attrib) != ScScenarioFlags::NONE);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_COPYFORM )
+ {
+ if (rDoc.IsScenario(nTab))
+ {
+ ScScenarioFlags nFlags;
+ rDoc.GetScenarioFlags(nTab, nFlags);
+
+ rAny <<= !(nFlags & ScScenarioFlags::Value);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_TABLAYOUT )
+ {
+ if (rDoc.IsLayoutRTL(nTab))
+ rAny <<= sal_Int16(css::text::WritingMode2::RL_TB);
+ else
+ rAny <<= sal_Int16(css::text::WritingMode2::LR_TB);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_AUTOPRINT )
+ {
+ bool bAutoPrint = rDoc.IsPrintEntireSheet( nTab );
+ rAny <<= bAutoPrint;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_TABCOLOR )
+ {
+ rAny <<= rDoc.GetTabBgColor(nTab);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CODENAME )
+ {
+ OUString aCodeName;
+ pDocSh->GetDocument().GetCodeName(GetTab_Impl(), aCodeName);
+ rAny <<= aCodeName;
+ }
+ else if (pEntry->nWID == SC_WID_UNO_CONDFORMAT)
+ {
+ rAny <<= uno::Reference<sheet::XConditionalFormats>(new ScCondFormatsObj(pDocSh, nTab));
+ }
+ else
+ ScCellRangeObj::GetOnePropertyValue(pEntry, rAny);
+}
+
+const SfxItemPropertyMap& ScTableSheetObj::GetItemPropertyMap()
+{
+ return pSheetPropSet->getPropertyMap();
+}
+
+// XServiceInfo
+
+OUString SAL_CALL ScTableSheetObj::getImplementationName()
+{
+ return "ScTableSheetObj";
+}
+
+sal_Bool SAL_CALL ScTableSheetObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScTableSheetObj::getSupportedServiceNames()
+{
+ return {SCSPREADSHEET_SERVICE,
+ SCSHEETCELLRANGE_SERVICE,
+ SCCELLRANGE_SERVICE,
+ SCCELLPROPERTIES_SERVICE,
+ SCCHARPROPERTIES_SERVICE,
+ SCPARAPROPERTIES_SERVICE,
+ SCLINKTARGET_SERVICE};
+}
+
+ScTableColumnObj::ScTableColumnObj( ScDocShell* pDocSh, SCCOL nCol, SCTAB nTab ) :
+ ScCellRangeObj( pDocSh, ScRange(nCol,0,nTab, nCol, pDocSh->GetDocument().MaxRow(),nTab) ),
+ pColPropSet(lcl_GetColumnPropertySet())
+{
+}
+
+ScTableColumnObj::~ScTableColumnObj()
+{
+}
+
+uno::Any SAL_CALL ScTableColumnObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<container::XNamed*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScCellRangeObj::queryInterface( rType );
+}
+
+void SAL_CALL ScTableColumnObj::acquire() noexcept
+{
+ ScCellRangeObj::acquire();
+}
+
+void SAL_CALL ScTableColumnObj::release() noexcept
+{
+ ScCellRangeObj::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScTableColumnObj::getTypes()
+{
+ return comphelper::concatSequences(
+ ScCellRangeObj::getTypes(),
+ uno::Sequence<uno::Type> { cppu::UnoType<container::XNamed>::get() } );
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScTableColumnObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XNamed
+
+OUString SAL_CALL ScTableColumnObj::getName()
+{
+ SolarMutexGuard aGuard;
+
+ const ScRange& rRange = GetRange();
+ OSL_ENSURE(rRange.aStart.Col() == rRange.aEnd.Col(), "too many columns");
+ SCCOL nCol = rRange.aStart.Col();
+
+ return ScColToAlpha( nCol ); // from global.hxx
+}
+
+void SAL_CALL ScTableColumnObj::setName( const OUString& /* aNewName */ )
+{
+ throw uno::RuntimeException(); // read-only
+}
+
+// XPropertySet extended for Column-Properties
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableColumnObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( pColPropSet->getPropertyMap() ));
+ return aRef;
+}
+
+void ScTableColumnObj::SetOnePropertyValue(const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue)
+{
+ if ( !pEntry )
+ return;
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ // for Item WIDs, call ScCellRangesBase directly
+ ScCellRangesBase::SetOnePropertyValue(pEntry, aValue);
+ return;
+ }
+
+ // own properties
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return; //! Exception or so?
+ const ScRange& rRange = GetRange();
+ OSL_ENSURE(rRange.aStart.Col() == rRange.aEnd.Col(), "Too many columns");
+ SCCOL nCol = rRange.aStart.Col();
+ SCTAB nTab = rRange.aStart.Tab();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+
+ std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(nCol,nCol));
+
+ if ( pEntry->nWID == SC_WID_UNO_CELLWID )
+ {
+ sal_Int32 nNewWidth = 0;
+ if ( aValue >>= nNewWidth )
+ {
+ // property is 1/100mm, column width is twips
+ nNewWidth = o3tl::toTwips(nNewWidth, o3tl::Length::mm100);
+ rFunc.SetWidthOrHeight(
+ true, aColArr, nTab, SC_SIZE_ORIGINAL, nNewWidth, true, true);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLVIS )
+ {
+ bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT;
+ rFunc.SetWidthOrHeight(true, aColArr, nTab, eMode, 0, true, true);
+ // SC_SIZE_DIRECT with size 0 will hide
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_OWIDTH )
+ {
+ bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bOpt)
+ rFunc.SetWidthOrHeight(
+ true, aColArr, nTab, SC_SIZE_OPTIMAL, STD_EXTRA_WIDTH, true, true);
+ // sal_False on columns currently without effect
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE || pEntry->nWID == SC_WID_UNO_MANPAGE )
+ {
+ bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bSet)
+ rFunc.InsertPageBreak( true, rRange.aStart, true, true );
+ else
+ rFunc.RemovePageBreak( true, rRange.aStart, true, true );
+ }
+ else
+ ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID
+}
+
+void ScTableColumnObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny )
+{
+ if ( !pEntry )
+ return;
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ throw uno::RuntimeException();
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const ScRange& rRange = GetRange();
+ OSL_ENSURE(rRange.aStart.Col() == rRange.aEnd.Col(), "too many columns");
+ SCCOL nCol = rRange.aStart.Col();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ if ( pEntry->nWID == SC_WID_UNO_CELLWID )
+ {
+ // for hidden column, return original height
+ sal_uInt16 nWidth = rDoc.GetOriginalWidth( nCol, nTab );
+ // property is 1/100mm, column width is twips
+ nWidth = static_cast<sal_uInt16>(convertTwipToMm100(nWidth));
+ rAny <<= static_cast<sal_Int32>(nWidth);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLVIS )
+ {
+ bool bHidden = rDoc.ColHidden(nCol, nTab);
+ rAny <<= !bHidden;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_OWIDTH )
+ {
+ //! at the moment always set ??!?!
+ bool bOpt = !(rDoc.GetColFlags( nCol, nTab ) & CRFlags::ManualSize);
+ rAny <<= bOpt;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasColBreak(nCol, nTab);
+ rAny <<= nBreak != ScBreakType::NONE;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_MANPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasColBreak(nCol, nTab);
+ rAny <<= bool(nBreak & ScBreakType::Manual);
+ }
+ else
+ ScCellRangeObj::GetOnePropertyValue(pEntry, rAny);
+}
+
+const SfxItemPropertyMap& ScTableColumnObj::GetItemPropertyMap()
+{
+ return pColPropSet->getPropertyMap();
+}
+
+ScTableRowObj::ScTableRowObj(ScDocShell* pDocSh, SCROW nRow, SCTAB nTab) :
+ ScCellRangeObj( pDocSh, ScRange(0,nRow,nTab, pDocSh->GetDocument().MaxCol(),nRow,nTab) ),
+ pRowPropSet(lcl_GetRowPropertySet())
+{
+}
+
+ScTableRowObj::~ScTableRowObj()
+{
+}
+
+// XPropertySet extended for Row-Properties
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableRowObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( pRowPropSet->getPropertyMap() ));
+ return aRef;
+}
+
+void ScTableRowObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, const uno::Any& aValue )
+{
+ if ( !pEntry )
+ return;
+
+ if ( IsScItemWid( pEntry->nWID ) )
+ {
+ // for Item WIDs, call ScCellRangesBase directly
+ ScCellRangesBase::SetOnePropertyValue(pEntry, aValue);
+ return;
+ }
+
+ // own properties
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ return; //! Exception or so?
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const ScRange& rRange = GetRange();
+ OSL_ENSURE(rRange.aStart.Row() == rRange.aEnd.Row(), "too many rows");
+ SCROW nRow = rRange.aStart.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+
+ std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(nRow,nRow));
+
+ if ( pEntry->nWID == SC_WID_UNO_CELLHGT )
+ {
+ sal_Int32 nNewHeight = 0;
+ if ( aValue >>= nNewHeight )
+ {
+ // property is 1/100mm, row height is twips
+ nNewHeight = o3tl::toTwips(nNewHeight, o3tl::Length::mm100);
+ rFunc.SetWidthOrHeight(
+ false, aRowArr, nTab, SC_SIZE_ORIGINAL, nNewHeight, true, true);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLVIS )
+ {
+ bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT;
+ rFunc.SetWidthOrHeight(false, aRowArr, nTab, eMode, 0, true, true);
+ // SC_SIZE_DIRECT with size zero will hide
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLFILT )
+ {
+ bool bFil = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ // SC_SIZE_DIRECT with size zero will hide
+ rDoc.SetRowFiltered(nRow, nRow, nTab, bFil);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_OHEIGHT )
+ {
+ bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bOpt)
+ rFunc.SetWidthOrHeight(false, aRowArr, nTab, SC_SIZE_OPTIMAL, 0, true, true);
+ else
+ {
+ // set current height again manually
+ sal_uInt16 nHeight = rDoc.GetOriginalHeight( nRow, nTab );
+ rFunc.SetWidthOrHeight(false, aRowArr, nTab, SC_SIZE_ORIGINAL, nHeight, true, true);
+ }
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE || pEntry->nWID == SC_WID_UNO_MANPAGE )
+ {
+ bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bSet)
+ rFunc.InsertPageBreak( false, rRange.aStart, true, true );
+ else
+ rFunc.RemovePageBreak( false, rRange.aStart, true, true );
+ }
+ else
+ ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID
+}
+
+void ScTableRowObj::GetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry, uno::Any& rAny )
+{
+ if ( !pEntry )
+ return;
+
+ ScDocShell* pDocSh = GetDocShell();
+ if (!pDocSh)
+ throw uno::RuntimeException();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const ScRange& rRange = GetRange();
+ OSL_ENSURE(rRange.aStart.Row() == rRange.aEnd.Row(), "too many rows");
+ SCROW nRow = rRange.aStart.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ if ( pEntry->nWID == SC_WID_UNO_CELLHGT )
+ {
+ // for hidden row, return original height
+ sal_uInt16 nHeight = rDoc.GetOriginalHeight( nRow, nTab );
+ // property is 1/100mm, row height is twips
+ nHeight = static_cast<sal_uInt16>(convertTwipToMm100(nHeight));
+ rAny <<= static_cast<sal_Int32>(nHeight);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLVIS )
+ {
+ bool bHidden = rDoc.RowHidden(nRow, nTab);
+ rAny <<= !bHidden;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_CELLFILT )
+ {
+ bool bVis = rDoc.RowFiltered(nRow, nTab);
+ rAny <<= bVis;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_OHEIGHT )
+ {
+ bool bOpt = !(rDoc.GetRowFlags( nRow, nTab ) & CRFlags::ManualSize);
+ rAny <<= bOpt;
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_NEWPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasRowBreak(nRow, nTab);
+ rAny <<= (nBreak != ScBreakType::NONE);
+ }
+ else if ( pEntry->nWID == SC_WID_UNO_MANPAGE )
+ {
+ bool bBreak(rDoc.HasRowBreak(nRow, nTab) & ScBreakType::Manual);
+ rAny <<= bBreak;
+ }
+ else
+ ScCellRangeObj::GetOnePropertyValue(pEntry, rAny);
+}
+
+const SfxItemPropertyMap& ScTableRowObj::GetItemPropertyMap()
+{
+ return pRowPropSet->getPropertyMap();
+}
+
+ScCellsObj::ScCellsObj(ScDocShell* pDocSh, ScRangeList aR) :
+ pDocShell( pDocSh ),
+ aRanges(std::move( aR ))
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScCellsObj::~ScCellsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScCellsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ aRanges.UpdateReference( pRefHint->GetMode(), &pDocShell->GetDocument(), pRefHint->GetRange(),
+ pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() );
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScCellsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScCellsEnumeration( pDocShell, aRanges );
+ return nullptr;
+}
+
+uno::Type SAL_CALL ScCellsObj::getElementType()
+{
+ return cppu::UnoType<table::XCell>::get();
+}
+
+sal_Bool SAL_CALL ScCellsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ bool bHas = false;
+ if ( pDocShell )
+ {
+ //! faster if test ourself?
+
+ uno::Reference<container::XEnumeration> xEnum(new ScCellsEnumeration( pDocShell, aRanges ));
+ bHas = xEnum->hasMoreElements();
+ }
+ return bHas;
+}
+
+ScCellsEnumeration::ScCellsEnumeration(ScDocShell* pDocSh, ScRangeList aR) :
+ pDocShell( pDocSh ),
+ aRanges(std::move( aR )),
+ bAtEnd( false )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.AddUnoObject(*this);
+
+ if ( aRanges.empty() )
+ bAtEnd = true;
+ else
+ {
+ SCTAB nTab = aRanges[ 0 ].aStart.Tab();
+ aPos = ScAddress(0,0,nTab);
+ CheckPos_Impl(); // set aPos on first matching cell
+ }
+}
+
+void ScCellsEnumeration::CheckPos_Impl()
+{
+ if (!pDocShell)
+ return;
+
+ bool bFound = false;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRefCellValue aCell(rDoc, aPos);
+ if (!aCell.isEmpty())
+ {
+ if (!pMark)
+ {
+ pMark.reset( new ScMarkData(rDoc.GetSheetLimits()) );
+ pMark->MarkFromRangeList(aRanges, false);
+ pMark->MarkToMulti(); // needed for GetNextMarkedCell
+ }
+ bFound = pMark->IsCellMarked(aPos.Col(), aPos.Row());
+ }
+ if (!bFound)
+ Advance_Impl();
+}
+
+ScCellsEnumeration::~ScCellsEnumeration()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+ pMark.reset();
+}
+
+void ScCellsEnumeration::Advance_Impl()
+{
+ OSL_ENSURE(!bAtEnd,"too much Advance_Impl");
+ if (!pMark)
+ {
+ pMark.reset( new ScMarkData(pDocShell->GetDocument().GetSheetLimits()) );
+ pMark->MarkFromRangeList( aRanges, false );
+ pMark->MarkToMulti(); // needed for GetNextMarkedCell
+ }
+
+ SCCOL nCol = aPos.Col();
+ SCROW nRow = aPos.Row();
+ SCTAB nTab = aPos.Tab();
+ bool bFound = pDocShell->GetDocument().GetNextMarkedCell( nCol, nRow, nTab, *pMark );
+ if (bFound)
+ aPos.Set( nCol, nRow, nTab );
+ else
+ bAtEnd = true; // nothing will follow
+}
+
+void ScCellsEnumeration::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const ScUpdateRefHint* pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint);
+ if ( pRefHint )
+ {
+ if (pDocShell)
+ {
+ aRanges.UpdateReference( pRefHint->GetMode(), &pDocShell->GetDocument(), pRefHint->GetRange(),
+ pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() );
+
+ pMark.reset(); // recreate from moved area
+
+ if (!bAtEnd) // adjust aPos
+ {
+ ScRangeList aNew { ScRange(aPos) };
+ aNew.UpdateReference( pRefHint->GetMode(), &pDocShell->GetDocument(), pRefHint->GetRange(),
+ pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() );
+ if (aNew.size()==1)
+ {
+ aPos = aNew[ 0 ].aStart;
+ CheckPos_Impl();
+ }
+ }
+ }
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+// XEnumeration
+
+sal_Bool SAL_CALL ScCellsEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return !bAtEnd;
+}
+
+uno::Any SAL_CALL ScCellsEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell && !bAtEnd)
+ {
+ // interface must match ScCellsObj::getElementType
+
+ ScAddress aTempPos(aPos);
+ Advance_Impl();
+ return uno::Any(uno::Reference<table::XCell>(new ScCellObj( pDocShell, aTempPos )));
+ }
+
+ throw container::NoSuchElementException(); // no more elements
+}
+
+ScCellFormatsObj::ScCellFormatsObj(ScDocShell* pDocSh, const ScRange& rRange) :
+ pDocShell( pDocSh ),
+ aTotalRange( rRange )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.AddUnoObject(*this);
+
+ OSL_ENSURE( aTotalRange.aStart.Tab() == aTotalRange.aEnd.Tab(), "different tables" );
+}
+
+ScCellFormatsObj::~ScCellFormatsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScCellFormatsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! aTotalRange...
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+rtl::Reference<ScCellRangeObj> ScCellFormatsObj::GetObjectByIndex_Impl(tools::Long nIndex) const
+{
+ //! access the AttrArrays directly !!!!
+
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ tools::Long nPos = 0;
+ ScAttrRectIterator aIter( rDoc, aTotalRange.aStart.Tab(),
+ aTotalRange.aStart.Col(), aTotalRange.aStart.Row(),
+ aTotalRange.aEnd.Col(), aTotalRange.aEnd.Row() );
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ while ( aIter.GetNext( nCol1, nCol2, nRow1, nRow2 ) )
+ {
+ if ( nPos == nIndex )
+ {
+ SCTAB nTab = aTotalRange.aStart.Tab();
+ ScRange aNext( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+
+ if ( aNext.aStart == aNext.aEnd )
+ return new ScCellObj( pDocShell, aNext.aStart );
+ else
+ return new ScCellRangeObj( pDocShell, aNext );
+ }
+ ++nPos;
+ }
+ }
+ return {};
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScCellFormatsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ //! access the AttrArrays directly !!!!
+
+ tools::Long nCount = 0;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScAttrRectIterator aIter( rDoc, aTotalRange.aStart.Tab(),
+ aTotalRange.aStart.Col(), aTotalRange.aStart.Row(),
+ aTotalRange.aEnd.Col(), aTotalRange.aEnd.Row() );
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ while ( aIter.GetNext( nCol1, nCol2, nRow1, nRow2 ) )
+ ++nCount;
+ }
+ return nCount;
+}
+
+uno::Any SAL_CALL ScCellFormatsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<table::XCellRange> xRange(GetObjectByIndex_Impl(nIndex));
+ if (!xRange.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xRange);
+
+}
+
+uno::Type SAL_CALL ScCellFormatsObj::getElementType()
+{
+ return cppu::UnoType<table::XCellRange>::get();
+}
+
+sal_Bool SAL_CALL ScCellFormatsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 ); //! always greater then zero ??
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScCellFormatsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScCellFormatsEnumeration( pDocShell, aTotalRange );
+ return nullptr;
+}
+
+ScCellFormatsEnumeration::ScCellFormatsEnumeration(ScDocShell* pDocSh, const ScRange& rRange) :
+ pDocShell( pDocSh ),
+ nTab( rRange.aStart.Tab() ),
+ bAtEnd( false ),
+ bDirty( false )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.AddUnoObject(*this);
+
+ OSL_ENSURE( rRange.aStart.Tab() == rRange.aEnd.Tab(),
+ "CellFormatsEnumeration: different tables" );
+
+ pIter.reset( new ScAttrRectIterator( rDoc, nTab,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() ) );
+ Advance_Impl();
+}
+
+ScCellFormatsEnumeration::~ScCellFormatsEnumeration()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScCellFormatsEnumeration::Advance_Impl()
+{
+ OSL_ENSURE(!bAtEnd,"too many Advance_Impl");
+
+ if ( pIter )
+ {
+ if ( bDirty )
+ {
+ pIter->DataChanged(); // new search for AttrArray-Index
+ bDirty = false;
+ }
+
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ if ( pIter->GetNext( nCol1, nCol2, nRow1, nRow2 ) )
+ aNext = ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+ else
+ bAtEnd = true;
+ }
+ else
+ bAtEnd = true; // document vanished or so
+}
+
+rtl::Reference<ScCellRangeObj> ScCellFormatsEnumeration::NextObject_Impl()
+{
+ rtl::Reference<ScCellRangeObj> pRet;
+ if (pDocShell && !bAtEnd)
+ {
+ if ( aNext.aStart == aNext.aEnd )
+ pRet = new ScCellObj( pDocShell, aNext.aStart );
+ else
+ pRet = new ScCellRangeObj( pDocShell, aNext );
+ Advance_Impl();
+ }
+ return pRet;
+}
+
+void ScCellFormatsEnumeration::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! and now???
+ }
+ else
+ {
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ pIter.reset();
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ bDirty = true; // AttrArray-Index possibly invalid
+ }
+ }
+}
+
+// XEnumeration
+
+sal_Bool SAL_CALL ScCellFormatsEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return !bAtEnd;
+}
+
+uno::Any SAL_CALL ScCellFormatsEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+
+ if ( bAtEnd || !pDocShell )
+ throw container::NoSuchElementException(); // no more elements
+
+ // interface must match ScCellFormatsObj::getElementType
+
+ return uno::Any(uno::Reference<table::XCellRange> (NextObject_Impl()));
+}
+
+ScUniqueCellFormatsObj::~ScUniqueCellFormatsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScUniqueCellFormatsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! aTotalRange...
+ }
+ else
+ {
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr;
+ }
+}
+
+// Fill the list of formats from the document
+
+namespace {
+
+// hash code to access the range lists by ScPatternAttr pointer
+struct ScPatternHashCode
+{
+ size_t operator()( const ScPatternAttr* pPattern ) const
+ {
+ return reinterpret_cast<size_t>(pPattern);
+ }
+};
+
+}
+
+// Hash map to find a range by its start row
+typedef std::unordered_map< SCROW, ScRange > ScRowRangeHashMap;
+
+namespace {
+
+// Hash map entry.
+// The Join method depends on the column-wise order of ScAttrRectIterator
+class ScUniqueFormatsEntry
+{
+ enum EntryState { STATE_EMPTY, STATE_SINGLE, STATE_COMPLEX };
+
+ EntryState eState;
+ ScRange aSingleRange;
+ ScRowRangeHashMap aJoinedRanges; // "active" ranges to be merged
+ std::vector<ScRange> aCompletedRanges; // ranges that will no longer be touched
+ ScRangeListRef aReturnRanges; // result as ScRangeList for further use
+
+public:
+ ScUniqueFormatsEntry() : eState( STATE_EMPTY ) {}
+
+ void Join( const ScRange& rNewRange );
+ const ScRangeList& GetRanges();
+ void Clear() { aReturnRanges.clear(); } // aJoinedRanges and aCompletedRanges are cleared in GetRanges
+};
+
+}
+
+void ScUniqueFormatsEntry::Join( const ScRange& rNewRange )
+{
+ // Special-case handling for single range
+
+ if ( eState == STATE_EMPTY )
+ {
+ aSingleRange = rNewRange;
+ eState = STATE_SINGLE;
+ return;
+ }
+ if ( eState == STATE_SINGLE )
+ {
+ if ( aSingleRange.aStart.Row() == rNewRange.aStart.Row() &&
+ aSingleRange.aEnd.Row() == rNewRange.aEnd.Row() &&
+ aSingleRange.aEnd.Col() + 1 == rNewRange.aStart.Col() )
+ {
+ aSingleRange.aEnd.SetCol( rNewRange.aEnd.Col() );
+ return; // still a single range
+ }
+
+ SCROW nSingleRow = aSingleRange.aStart.Row();
+ aJoinedRanges.emplace( nSingleRow, aSingleRange );
+ eState = STATE_COMPLEX;
+ // continue normally
+ }
+
+ // This is called in the order of ScAttrRectIterator results.
+ // rNewRange can only be joined with an existing entry if it's the same rows, starting in the next column.
+ // If the old entry for the start row extends to a different end row, or ends in a different column, it
+ // can be moved to aCompletedRanges because it can't be joined with following iterator results.
+ // Everything happens within one sheet, so Tab can be ignored.
+
+ SCROW nStartRow = rNewRange.aStart.Row();
+ ScRowRangeHashMap::iterator aIter( aJoinedRanges.find( nStartRow ) ); // find the active entry for the start row
+ if ( aIter != aJoinedRanges.end() )
+ {
+ ScRange& rOldRange = aIter->second;
+ if ( rOldRange.aEnd.Row() == rNewRange.aEnd.Row() &&
+ rOldRange.aEnd.Col() + 1 == rNewRange.aStart.Col() )
+ {
+ // extend existing range
+ rOldRange.aEnd.SetCol( rNewRange.aEnd.Col() );
+ }
+ else
+ {
+ // move old range to aCompletedRanges, keep rNewRange for joining
+ aCompletedRanges.push_back( rOldRange );
+ rOldRange = rNewRange; // replace in hash map
+ }
+ }
+ else
+ {
+ // keep rNewRange for joining
+ aJoinedRanges.emplace( nStartRow, rNewRange );
+ }
+}
+
+const ScRangeList& ScUniqueFormatsEntry::GetRanges()
+{
+ if ( eState == STATE_SINGLE )
+ {
+ aReturnRanges = new ScRangeList( aSingleRange );
+ return *aReturnRanges;
+ }
+
+ // move remaining entries from aJoinedRanges to aCompletedRanges
+
+ for ( const auto& rEntry : aJoinedRanges )
+ aCompletedRanges.push_back( rEntry.second );
+ aJoinedRanges.clear();
+
+ // sort all ranges for a predictable API result
+
+ std::sort( aCompletedRanges.begin(), aCompletedRanges.end() );
+
+ // fill and return ScRangeList
+
+ aReturnRanges = new ScRangeList;
+ aReturnRanges->insert( aReturnRanges->end(), aCompletedRanges.begin(), aCompletedRanges.end() );
+ aCompletedRanges.clear();
+
+ return *aReturnRanges;
+}
+
+namespace {
+
+// function object to sort the range lists by start of first range
+struct ScUniqueFormatsOrder
+{
+ bool operator()( const ScRangeList& rList1, const ScRangeList& rList2 ) const
+ {
+ // all range lists have at least one entry
+ OSL_ENSURE( !rList1.empty() && !rList2.empty(), "ScUniqueFormatsOrder: empty list" );
+
+ // compare start positions using ScAddress comparison operator
+ return ( rList1[ 0 ].aStart < rList2[ 0 ].aStart );
+ }
+};
+
+}
+
+ScUniqueCellFormatsObj::ScUniqueCellFormatsObj(ScDocShell* pDocSh, const ScRange& rTotalRange) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+
+ OSL_ENSURE( rTotalRange.aStart.Tab() == rTotalRange.aEnd.Tab(), "different tables" );
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = rTotalRange.aStart.Tab();
+ ScAttrRectIterator aIter( rDoc, nTab,
+ rTotalRange.aStart.Col(), rTotalRange.aStart.Row(),
+ rTotalRange.aEnd.Col(), rTotalRange.aEnd.Row() );
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+
+ // Collect the ranges for each format in a hash map, to avoid nested loops
+
+ std::unordered_map< const ScPatternAttr*, ScUniqueFormatsEntry, ScPatternHashCode > aHashMap;
+ while (aIter.GetNext( nCol1, nCol2, nRow1, nRow2 ) )
+ {
+ ScRange aRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+ const ScPatternAttr* pPattern = rDoc.GetPattern(nCol1, nRow1, nTab);
+ aHashMap[pPattern].Join( aRange );
+ }
+
+ // Fill the vector aRangeLists with the range lists from the hash map
+
+ aRangeLists.reserve( aHashMap.size() );
+ for ( auto& rMapEntry : aHashMap )
+ {
+ ScUniqueFormatsEntry& rEntry = rMapEntry.second;
+ const ScRangeList& rRanges = rEntry.GetRanges();
+ aRangeLists.push_back( rRanges ); // copy ScRangeList
+ rEntry.Clear(); // free memory, don't hold both copies of all ranges
+ }
+
+ // Sort the vector by first range's start position, to avoid random shuffling
+ // due to using the ScPatterAttr pointers
+
+ ::std::sort( aRangeLists.begin(), aRangeLists.end(), ScUniqueFormatsOrder() );
+}
+
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScUniqueCellFormatsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ return aRangeLists.size();
+}
+
+uno::Any SAL_CALL ScUniqueCellFormatsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if(o3tl::make_unsigned(nIndex) >= aRangeLists.size())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(uno::Reference<sheet::XSheetCellRangeContainer>(new ScCellRangesObj(pDocShell, aRangeLists[nIndex])));
+
+}
+
+uno::Type SAL_CALL ScUniqueCellFormatsObj::getElementType()
+{
+ return cppu::UnoType<sheet::XSheetCellRangeContainer>::get();
+}
+
+sal_Bool SAL_CALL ScUniqueCellFormatsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( !aRangeLists.empty() );
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScUniqueCellFormatsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScUniqueCellFormatsEnumeration( pDocShell, std::vector(aRangeLists) );
+ return nullptr;
+}
+
+ScUniqueCellFormatsEnumeration::ScUniqueCellFormatsEnumeration(ScDocShell* pDocSh, std::vector<ScRangeList>&& rRangeLists) :
+ aRangeLists(std::move(rRangeLists)),
+ pDocShell( pDocSh ),
+ nCurrentPosition(0)
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScUniqueCellFormatsEnumeration::~ScUniqueCellFormatsEnumeration()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScUniqueCellFormatsEnumeration::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! and now ???
+ }
+ else
+ {
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr;
+ }
+}
+
+// XEnumeration
+
+sal_Bool SAL_CALL ScUniqueCellFormatsEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return o3tl::make_unsigned(nCurrentPosition) < aRangeLists.size();
+}
+
+uno::Any SAL_CALL ScUniqueCellFormatsEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+
+ if ( !hasMoreElements() || !pDocShell )
+ throw container::NoSuchElementException(); // no more elements
+
+ // interface type must match ScCellFormatsObj::getElementType
+
+ return uno::Any(uno::Reference<sheet::XSheetCellRangeContainer>(new ScCellRangesObj(pDocShell, aRangeLists[nCurrentPosition++])));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/cellvaluebinding.cxx b/sc/source/ui/unoobj/cellvaluebinding.cxx
new file mode 100644
index 0000000000..fd8b43f957
--- /dev/null
+++ b/sc/source/ui/unoobj/cellvaluebinding.cxx
@@ -0,0 +1,576 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "cellvaluebinding.hxx"
+#include <rtl/math.hxx>
+#include <com/sun/star/form/binding/IncompatibleTypesException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/sheet/FormulaResult.hpp>
+#include <com/sun/star/sheet/XCellAddressable.hpp>
+#include <com/sun/star/sheet/XCellRangeData.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/types.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace calc
+{
+
+#define PROP_HANDLE_BOUND_CELL 1
+
+ namespace lang = ::com::sun::star::lang;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::table;
+ using namespace ::com::sun::star::text;
+ using namespace ::com::sun::star::sheet;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::binding;
+
+ OCellValueBinding::OCellValueBinding( const Reference< XSpreadsheetDocument >& _rxDocument, bool _bListPos )
+ :OCellValueBinding_Base( m_aMutex )
+ ,OCellValueBinding_PBase( OCellValueBinding_Base::rBHelper )
+ ,m_xDocument( _rxDocument )
+ ,m_aModifyListeners( m_aMutex )
+ ,m_bInitialized( false )
+ ,m_bListPos( _bListPos )
+ {
+ // register our property at the base class
+ registerPropertyNoMember(
+ "BoundCell",
+ PROP_HANDLE_BOUND_CELL,
+ PropertyAttribute::BOUND | PropertyAttribute::READONLY,
+ cppu::UnoType<CellAddress>::get(),
+ css::uno::Any(CellAddress())
+ );
+
+ // TODO: implement a ReadOnly property as required by the service,
+ // which probably maps to the cell being locked
+ }
+
+ OCellValueBinding::~OCellValueBinding( )
+ {
+ if ( !OCellValueBinding_Base::rBHelper.bDisposed )
+ {
+ acquire(); // prevent duplicate dtor
+ dispose();
+ }
+ }
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCellValueBinding, OCellValueBinding_Base, OCellValueBinding_PBase )
+
+ void SAL_CALL OCellValueBinding::disposing()
+ {
+ Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->removeModifyListener( this );
+ }
+
+ WeakComponentImplHelperBase::disposing();
+
+ // TODO: clean up here whatever you need to clean up (e.g. deregister as XEventListener
+ // for the cell)
+ }
+
+ Reference< XPropertySetInfo > SAL_CALL OCellValueBinding::getPropertySetInfo( )
+ {
+ return createPropertySetInfo( getInfoHelper() ) ;
+ }
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL OCellValueBinding::getInfoHelper()
+ {
+ return *OCellValueBinding_PABase::getArrayHelper();
+ }
+
+ ::cppu::IPropertyArrayHelper* OCellValueBinding::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper(aProps);
+ }
+
+ void SAL_CALL OCellValueBinding::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ OSL_ENSURE( _nHandle == PROP_HANDLE_BOUND_CELL, "OCellValueBinding::getFastPropertyValue: invalid handle!" );
+ // we only have this one property...
+
+ _rValue.clear();
+ Reference< XCellAddressable > xCellAddress( m_xCell, UNO_QUERY );
+ if ( xCellAddress.is() )
+ _rValue <<= xCellAddress->getCellAddress( );
+ }
+
+ Sequence< Type > SAL_CALL OCellValueBinding::getSupportedValueTypes( )
+ {
+ checkDisposed( );
+ checkInitialized( );
+
+ sal_Int32 nCount = m_xCellText.is() ? 3 : m_xCell.is() ? 1 : 0;
+ if ( m_bListPos )
+ ++nCount;
+
+ Sequence< Type > aTypes( nCount );
+ if ( m_xCell.is() )
+ {
+ auto pTypes = aTypes.getArray();
+
+ // an XCell can be used to set/get "double" values
+ pTypes[0] = ::cppu::UnoType<double>::get();
+ if ( m_xCellText.is() )
+ {
+ // an XTextRange can be used to set/get "string" values
+ pTypes[1] = ::cppu::UnoType<OUString>::get();
+ // and additionally, we use it to handle booleans
+ pTypes[2] = ::cppu::UnoType<sal_Bool>::get();
+ }
+
+ // add sal_Int32 only if constructed as ListPositionCellBinding
+ if ( m_bListPos )
+ pTypes[nCount-1] = cppu::UnoType<sal_Int32>::get();
+ }
+
+ return aTypes;
+ }
+
+ sal_Bool SAL_CALL OCellValueBinding::supportsType( const Type& aType )
+ {
+ checkDisposed( );
+ checkInitialized( );
+
+ // look up in our sequence
+ const Sequence< Type > aSupportedTypes( getSupportedValueTypes() );
+ for ( auto const & i : aSupportedTypes )
+ if ( aType == i )
+ return true;
+
+ return false;
+ }
+
+ Any SAL_CALL OCellValueBinding::getValue( const Type& aType )
+ {
+ checkDisposed( );
+ checkInitialized( );
+ checkValueType( aType );
+
+ Any aReturn;
+ switch ( aType.getTypeClass() )
+ {
+ case TypeClass_STRING:
+ OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::getValue: don't have a text!" );
+ if ( m_xCellText.is() )
+ aReturn <<= m_xCellText->getString();
+ else
+ aReturn <<= OUString();
+ break;
+
+ case TypeClass_BOOLEAN:
+ OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
+ if ( m_xCell.is() )
+ {
+ // check if the cell has a numeric value (this might go into a helper function):
+
+ bool bHasValue = false;
+ CellContentType eCellType = m_xCell->getType();
+ if ( eCellType == CellContentType_VALUE )
+ bHasValue = true;
+ else if ( eCellType == CellContentType_FORMULA )
+ {
+ // check if the formula result is a value
+ if ( m_xCell->getError() == 0 )
+ {
+ Reference<XPropertySet> xProp( m_xCell, UNO_QUERY );
+ if ( xProp.is() )
+ {
+ sal_Int32 nResultType;
+ if ( (xProp->getPropertyValue("FormulaResultType2") >>= nResultType)
+ && nResultType == FormulaResult::VALUE )
+ bHasValue = true;
+ }
+ }
+ }
+
+ if ( bHasValue )
+ {
+ // 0 is "unchecked", any other value is "checked", regardless of number format
+ double nCellValue = m_xCell->getValue();
+ bool bBoolValue = ( nCellValue != 0.0 );
+ aReturn <<= bBoolValue;
+ }
+ // empty cells, text cells and text or error formula results: leave return value empty
+ }
+ break;
+
+ case TypeClass_DOUBLE:
+ OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
+ if ( m_xCell.is() )
+ aReturn <<= m_xCell->getValue();
+ else
+ aReturn <<= double(0);
+ break;
+
+ case TypeClass_LONG:
+ OSL_ENSURE( m_xCell.is(), "OCellValueBinding::getValue: don't have a double value supplier!" );
+ if ( m_xCell.is() )
+ {
+ // The list position value in the cell is 1-based.
+ // We subtract 1 from any cell value (no special handling for 0 or negative values).
+
+ sal_Int32 nValue = static_cast<sal_Int32>(rtl::math::approxFloor( m_xCell->getValue() ));
+ --nValue;
+
+ aReturn <<= nValue;
+ }
+ else
+ aReturn <<= sal_Int32(0);
+ break;
+
+ default:
+ OSL_FAIL( "OCellValueBinding::getValue: unreachable code!" );
+ // a type other than double and string should never have survived the checkValueType
+ // above
+ }
+ return aReturn;
+ }
+
+ void SAL_CALL OCellValueBinding::setValue( const Any& aValue )
+ {
+ checkDisposed( );
+ checkInitialized( );
+ if ( aValue.hasValue() )
+ checkValueType( aValue.getValueType() );
+
+ switch ( aValue.getValueType().getTypeClass() )
+ {
+ case TypeClass_STRING:
+ {
+ OSL_ENSURE( m_xCellText.is(), "OCellValueBinding::setValue: don't have a text!" );
+
+ OUString sText;
+ aValue >>= sText;
+ if ( m_xCellText.is() )
+ m_xCellText->setString( sText );
+ }
+ break;
+
+ case TypeClass_BOOLEAN:
+ {
+ OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
+
+ // boolean is stored as values 0 or 1
+ // TODO: set the number format to boolean if no format is set?
+
+ bool bValue( false );
+ aValue >>= bValue;
+ double nCellValue = bValue ? 1.0 : 0.0;
+
+ if ( m_xCell.is() )
+ m_xCell->setValue( nCellValue );
+
+ setBooleanFormat();
+ }
+ break;
+
+ case TypeClass_DOUBLE:
+ {
+ OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
+
+ double nValue = 0;
+ aValue >>= nValue;
+ if ( m_xCell.is() )
+ m_xCell->setValue( nValue );
+ }
+ break;
+
+ case TypeClass_LONG:
+ {
+ OSL_ENSURE( m_xCell.is(), "OCellValueBinding::setValue: don't have a double value supplier!" );
+
+ sal_Int32 nValue = 0;
+ aValue >>= nValue; // list index from control layer (0-based)
+ ++nValue; // the list position value in the cell is 1-based
+ if ( m_xCell.is() )
+ m_xCell->setValue( nValue );
+ }
+ break;
+
+ case TypeClass_VOID:
+ {
+ // #N/A error value can only be set using XCellRangeData
+
+ Reference<XCellRangeData> xData( m_xCell, UNO_QUERY );
+ OSL_ENSURE( xData.is(), "OCellValueBinding::setValue: don't have XCellRangeData!" );
+ if ( xData.is() )
+ {
+ Sequence<Any> aInner(1); // one empty element
+ Sequence< Sequence<Any> > aOuter( &aInner, 1 ); // one row
+ xData->setDataArray( aOuter );
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "OCellValueBinding::setValue: unreachable code!" );
+ // a type other than double and string should never have survived the checkValueType
+ // above
+ }
+ }
+
+ void OCellValueBinding::setBooleanFormat()
+ {
+ // set boolean number format if not already set
+
+ OUString sPropName( "NumberFormat" );
+ Reference<XPropertySet> xCellProp( m_xCell, UNO_QUERY );
+ Reference<XNumberFormatsSupplier> xSupplier( m_xDocument, UNO_QUERY );
+ if ( !(xSupplier.is() && xCellProp.is()) )
+ return;
+
+ Reference<XNumberFormats> xFormats(xSupplier->getNumberFormats());
+ Reference<XNumberFormatTypes> xTypes( xFormats, UNO_QUERY );
+ if ( !xTypes.is() )
+ return;
+
+ lang::Locale aLocale;
+ bool bWasBoolean = false;
+
+ sal_Int32 nOldIndex = ::comphelper::getINT32( xCellProp->getPropertyValue( sPropName ) );
+ Reference<XPropertySet> xOldFormat;
+ try
+ {
+ xOldFormat.set(xFormats->getByKey( nOldIndex ));
+ }
+ catch ( Exception& )
+ {
+ // non-existing format - can happen, use defaults
+ }
+ if ( xOldFormat.is() )
+ {
+ // use the locale of the existing format
+ xOldFormat->getPropertyValue("Locale") >>= aLocale;
+
+ sal_Int16 nOldType = ::comphelper::getINT16(
+ xOldFormat->getPropertyValue("Type") );
+ if ( nOldType & NumberFormat::LOGICAL )
+ bWasBoolean = true;
+ }
+
+ if ( !bWasBoolean )
+ {
+ sal_Int32 nNewIndex = xTypes->getStandardFormat( NumberFormat::LOGICAL, aLocale );
+ xCellProp->setPropertyValue( sPropName, Any( nNewIndex ) );
+ }
+ }
+
+ void OCellValueBinding::checkDisposed( ) const
+ {
+ if ( OCellValueBinding_Base::rBHelper.bInDispose || OCellValueBinding_Base::rBHelper.bDisposed )
+ throw DisposedException();
+ // TODO: is it worth having an error message here?
+ }
+
+ void OCellValueBinding::checkInitialized()
+ {
+ if ( !m_bInitialized )
+ throw NotInitializedException("CellValueBinding is not initialized", getXWeak());
+ }
+
+ void OCellValueBinding::checkValueType( const Type& _rType ) const
+ {
+ OCellValueBinding* pNonConstThis = const_cast< OCellValueBinding* >( this );
+ if ( !pNonConstThis->supportsType( _rType ) )
+ {
+ OUString sMessage = "The given type (" +
+ _rType.getTypeName() +
+ ") is not supported by this binding.";
+ // TODO: localize this error message
+
+ throw IncompatibleTypesException( sMessage, *pNonConstThis );
+ // TODO: alternatively use a type converter service for this?
+ }
+ }
+
+ OUString SAL_CALL OCellValueBinding::getImplementationName( )
+ {
+ return "com.sun.star.comp.sheet.OCellValueBinding";
+ }
+
+ sal_Bool SAL_CALL OCellValueBinding::supportsService( const OUString& _rServiceName )
+ {
+ return cppu::supportsService(this, _rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL OCellValueBinding::getSupportedServiceNames( )
+ {
+ Sequence< OUString > aServices( m_bListPos ? 3 : 2 );
+ auto pServices = aServices.getArray();
+ pServices[ 0 ] = "com.sun.star.table.CellValueBinding";
+ pServices[ 1 ] = "com.sun.star.form.binding.ValueBinding";
+ if ( m_bListPos )
+ pServices[ 2 ] = "com.sun.star.table.ListPositionCellBinding";
+ return aServices;
+ }
+
+ void SAL_CALL OCellValueBinding::addModifyListener( const Reference< XModifyListener >& _rxListener )
+ {
+ if ( _rxListener.is() )
+ m_aModifyListeners.addInterface( _rxListener );
+ }
+
+ void SAL_CALL OCellValueBinding::removeModifyListener( const Reference< XModifyListener >& _rxListener )
+ {
+ if ( _rxListener.is() )
+ m_aModifyListeners.removeInterface( _rxListener );
+ }
+
+ void OCellValueBinding::notifyModified()
+ {
+ EventObject aEvent;
+ aEvent.Source.set(*this);
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aModifyListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->modified( aEvent );
+ }
+ catch( const RuntimeException& )
+ {
+ // silent this
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::notifyModified: caught a (non-runtime) exception!" );
+ }
+ }
+ }
+
+ void SAL_CALL OCellValueBinding::modified( const EventObject& /* aEvent */ )
+ {
+ notifyModified();
+ }
+
+ void SAL_CALL OCellValueBinding::disposing( const EventObject& aEvent )
+ {
+ Reference<XInterface> xCellInt( m_xCell, UNO_QUERY );
+ if ( xCellInt == aEvent.Source )
+ {
+ // release references to cell object
+ m_xCell.clear();
+ m_xCellText.clear();
+ }
+ }
+
+ void SAL_CALL OCellValueBinding::initialize( const Sequence< Any >& _rArguments )
+ {
+ if ( m_bInitialized )
+ throw RuntimeException("CellValueBinding is already initialized", getXWeak());
+
+ // get the cell address
+ CellAddress aAddress;
+ bool bFoundAddress = false;
+
+ for ( const Any& rArg : _rArguments )
+ {
+ NamedValue aValue;
+ if ( rArg >>= aValue )
+ {
+ if ( aValue.Name == "BoundCell" )
+ {
+ if ( aValue.Value >>= aAddress )
+ {
+ bFoundAddress = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !bFoundAddress )
+ throw RuntimeException("Cell not found", getXWeak());
+
+ // get the cell object
+ try
+ {
+ // first the sheets collection
+ Reference< XIndexAccess > xSheets;
+ if ( m_xDocument.is() )
+ xSheets.set(m_xDocument->getSheets( ), css::uno::UNO_QUERY);
+ OSL_ENSURE( xSheets.is(), "OCellValueBinding::initialize: could not retrieve the sheets!" );
+
+ if ( xSheets.is() )
+ {
+ // the concrete sheet
+ Reference< XCellRange > xSheet(xSheets->getByIndex( aAddress.Sheet ), UNO_QUERY);
+ OSL_ENSURE( xSheet.is(), "OCellValueBinding::initialize: NULL sheet, but no exception!" );
+
+ // the concrete cell
+ if ( xSheet.is() )
+ {
+ m_xCell.set(xSheet->getCellByPosition( aAddress.Column, aAddress.Row ));
+ Reference< XCellAddressable > xAddressAccess( m_xCell, UNO_QUERY );
+ OSL_ENSURE( xAddressAccess.is(), "OCellValueBinding::initialize: either NULL cell, or cell without address access!" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "OCellValueBinding::initialize: caught an exception while retrieving the cell object!" );
+ }
+
+ if ( !m_xCell.is() )
+ throw RuntimeException("Failed to retrieve cell object", getXWeak());
+
+ m_xCellText.set(m_xCell, css::uno::UNO_QUERY);
+
+ Reference<XModifyBroadcaster> xBroadcaster( m_xCell, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->addModifyListener( this );
+ }
+
+ // TODO: add as XEventListener to the cell, so we get notified when it dies,
+ // and can dispose ourself then
+
+ // TODO: somehow add as listener so we get notified when the address of the cell changes
+ // We need to forward this as change in our BoundCell property to our property change listeners
+
+ // TODO: be an XModifyBroadcaster, so that changes in our cell can be notified
+ // to the BindableValue which is/will be bound to this instance.
+
+ m_bInitialized = true;
+ // TODO: place your code here
+ }
+
+} // namespace calc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/cellvaluebinding.hxx b/sc/source/ui/unoobj/cellvaluebinding.hxx
new file mode 100644
index 0000000000..511303f7ed
--- /dev/null
+++ b/sc/source/ui/unoobj/cellvaluebinding.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/uno3.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+namespace com::sun::star::table { class XCell; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+namespace com::sun::star::text { class XTextRange; }
+
+namespace calc
+{
+
+ //= OCellValueBinding
+
+ class OCellValueBinding;
+ // the base for our interfaces
+ typedef ::cppu::WeakComponentImplHelper < css::form::binding::XValueBinding
+ , css::lang::XServiceInfo
+ , css::util::XModifyBroadcaster
+ , css::util::XModifyListener
+ , css::lang::XInitialization
+ > OCellValueBinding_Base;
+ // the base for the property handling
+ typedef ::comphelper::OPropertyContainer OCellValueBinding_PBase;
+ // the second base for property handling
+ typedef ::comphelper::OPropertyArrayUsageHelper< OCellValueBinding >
+ OCellValueBinding_PABase;
+
+ class OCellValueBinding :public ::cppu::BaseMutex
+ ,public OCellValueBinding_Base // order matters! before OCellValueBinding_PBase, so rBHelper gets initialized
+ ,public OCellValueBinding_PBase
+ ,public OCellValueBinding_PABase
+ {
+ private:
+ css::uno::Reference< css::sheet::XSpreadsheetDocument >
+ m_xDocument; /// the document where our cell lives
+ css::uno::Reference< css::table::XCell >
+ m_xCell; /// the cell we're bound to, for double value access
+ css::uno::Reference< css::text::XTextRange >
+ m_xCellText; /// the cell we're bound to, for text access
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener>
+ m_aModifyListeners; /// our modify listeners
+ bool m_bInitialized; /// has XInitialization::initialize been called?
+ bool m_bListPos; /// constructed as ListPositionCellBinding?
+
+ public:
+ OCellValueBinding(
+ const css::uno::Reference< css::sheet::XSpreadsheetDocument >& _rxDocument,
+ bool _bListPos
+ );
+
+ using OCellValueBinding_PBase::getFastPropertyValue;
+
+ protected:
+ virtual ~OCellValueBinding( ) override;
+
+ protected:
+ // XInterface
+ DECLARE_XINTERFACE()
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XValueBinding
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getSupportedValueTypes( ) override;
+ virtual sal_Bool SAL_CALL supportsType( const css::uno::Type& aType ) override;
+ virtual css::uno::Any SAL_CALL getValue( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL setValue( const css::uno::Any& aValue ) override;
+
+ // OComponentHelper/XComponent
+ virtual void SAL_CALL disposing() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+ // OPropertySetHelper
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& _rValue, sal_Int32 _nHandle ) const override;
+
+ // ::comphelper::OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const 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;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ private:
+ void checkDisposed( ) const;
+ void checkValueType( const css::uno::Type& _rType ) const;
+ void checkInitialized();
+
+ /** notifies our modify listeners
+ @precond
+ our mutex is <em>not</em> locked
+ */
+ void notifyModified();
+
+ void setBooleanFormat();
+
+ private:
+ OCellValueBinding( const OCellValueBinding& ) = delete;
+ OCellValueBinding& operator=( const OCellValueBinding& ) = delete;
+ };
+
+} // namespace calc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/chart2uno.cxx b/sc/source/ui/unoobj/chart2uno.cxx
new file mode 100644
index 0000000000..e20a473f06
--- /dev/null
+++ b/sc/source/ui/unoobj/chart2uno.cxx
@@ -0,0 +1,3517 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <utility>
+
+#include <chart2uno.hxx>
+#include <miscuno.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <formulacell.hxx>
+#include <unonames.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <rangeutl.hxx>
+#include <hints.hxx>
+#include <unoreflist.hxx>
+#include <compiler.hxx>
+#include <reftokenhelper.hxx>
+#include <chartlis.hxx>
+#include <tokenuno.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <brdcst.hxx>
+#include <mtvelements.hxx>
+
+#include <formula/opcode.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstring.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <comphelper/extract.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <limits>
+
+SC_SIMPLE_SERVICE_INFO( ScChart2DataProvider, "ScChart2DataProvider",
+ "com.sun.star.chart2.data.DataProvider")
+SC_SIMPLE_SERVICE_INFO( ScChart2DataSource, "ScChart2DataSource",
+ "com.sun.star.chart2.data.DataSource")
+SC_SIMPLE_SERVICE_INFO( ScChart2DataSequence, "ScChart2DataSequence",
+ "com.sun.star.chart2.data.DataSequence")
+
+using namespace ::com::sun::star;
+using namespace ::formula;
+using ::com::sun::star::uno::Sequence;
+using ::std::unique_ptr;
+using ::std::vector;
+using ::std::distance;
+using ::std::shared_ptr;
+
+namespace
+{
+std::span<const SfxItemPropertyMapEntry> lcl_GetDataProviderPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_USE_INTERNAL_DATA_PROVIDER, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aDataProviderPropertyMap_Impl;
+}
+
+std::span<const SfxItemPropertyMapEntry> lcl_GetDataSequencePropertyMap()
+{
+ static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] =
+ {
+ { SC_UNONAME_HIDDENVALUES, 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), 0, 0 },
+ { SC_UNONAME_ROLE, 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(), 0, 0 },
+ { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aDataSequencePropertyMap_Impl;
+}
+
+struct lcl_appendTableNumber
+{
+ explicit lcl_appendTableNumber( OUStringBuffer & rBuffer ) :
+ m_rBuffer( rBuffer )
+ {}
+ void operator() ( SCTAB nTab )
+ {
+ // there is no append with SCTAB or sal_Int16
+ m_rBuffer.append( static_cast< sal_Int32 >( nTab ));
+ m_rBuffer.append( ' ' );
+ }
+private:
+ OUStringBuffer & m_rBuffer;
+};
+
+OUString lcl_createTableNumberList( const ::std::vector< SCTAB > & rTableVector )
+{
+ OUStringBuffer aBuffer;
+ ::std::for_each( rTableVector.begin(), rTableVector.end(), lcl_appendTableNumber( aBuffer ));
+ // remove last trailing ' '
+ if( !aBuffer.isEmpty() )
+ aBuffer.setLength( aBuffer.getLength() - 1 );
+ return aBuffer.makeStringAndClear();
+}
+
+uno::Reference< frame::XModel > lcl_GetXModel( const ScDocument * pDoc )
+{
+ uno::Reference< frame::XModel > xModel;
+ ScDocShell * pObjSh( pDoc ? pDoc->GetDocumentShell() : nullptr );
+ if( pObjSh )
+ xModel.set( pObjSh->GetModel());
+ return xModel;
+}
+
+struct TokenTable
+{
+ SCROW mnRowCount;
+ SCCOL mnColCount;
+ vector<std::unique_ptr<FormulaToken>> maTokens;
+
+ // noncopyable
+ TokenTable(const TokenTable&) = delete;
+ const TokenTable& operator=(const TokenTable&) = delete;
+
+ TokenTable()
+ : mnRowCount(0)
+ , mnColCount(0)
+ {
+ }
+
+ void init( SCCOL nColCount, SCROW nRowCount )
+ {
+ mnColCount = nColCount;
+ mnRowCount = nRowCount;
+ maTokens.reserve(mnColCount*mnRowCount);
+ }
+ void clear()
+ {
+ for (auto & rToken : maTokens)
+ rToken.reset();
+ }
+
+ void push_back( std::unique_ptr<FormulaToken> pToken )
+ {
+ maTokens.push_back( std::move(pToken) );
+ OSL_ENSURE( maTokens.size()<= o3tl::make_unsigned( mnColCount*mnRowCount ), "too many tokens" );
+ }
+
+ sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const
+ {
+ OSL_ENSURE( nCol<mnColCount, "wrong column index" );
+ OSL_ENSURE( nRow<mnRowCount, "wrong row index" );
+ sal_uInt32 nRet = static_cast<sal_uInt32>(nCol*mnRowCount + nRow);
+ OSL_ENSURE( maTokens.size()>= o3tl::make_unsigned( mnColCount*mnRowCount ), "too few tokens" );
+ return nRet;
+ }
+
+ vector<ScTokenRef> getColRanges(const ScDocument* pDoc, SCCOL nCol) const;
+ vector<ScTokenRef> getRowRanges(const ScDocument* pDoc, SCROW nRow) const;
+ vector<ScTokenRef> getAllRanges(const ScDocument* pDoc) const;
+};
+
+vector<ScTokenRef> TokenTable::getColRanges(const ScDocument* pDoc, SCCOL nCol) const
+{
+ if (nCol >= mnColCount)
+ return vector<ScTokenRef>();
+ if( mnRowCount<=0 )
+ return vector<ScTokenRef>();
+
+ vector<ScTokenRef> aTokens;
+ sal_uInt32 nLast = getIndex(nCol, mnRowCount-1);
+ for (sal_uInt32 i = getIndex(nCol, 0); i <= nLast; ++i)
+ {
+ FormulaToken* p = maTokens[i].get();
+ if (!p)
+ continue;
+
+ ScTokenRef pCopy(p->Clone());
+ ScRefTokenHelper::join(pDoc, aTokens, pCopy, ScAddress());
+ }
+ return aTokens;
+}
+
+vector<ScTokenRef> TokenTable::getRowRanges(const ScDocument* pDoc, SCROW nRow) const
+{
+ if (nRow >= mnRowCount)
+ return vector<ScTokenRef>();
+ if( mnColCount<=0 )
+ return vector<ScTokenRef>();
+
+ vector<ScTokenRef> aTokens;
+ sal_uInt32 nLast = getIndex(mnColCount-1, nRow);
+ for (sal_uInt32 i = getIndex(0, nRow); i <= nLast; i += mnRowCount)
+ {
+ FormulaToken* p = maTokens[i].get();
+ if (!p)
+ continue;
+
+ ScTokenRef p2(p->Clone());
+ ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress());
+ }
+ return aTokens;
+}
+
+vector<ScTokenRef> TokenTable::getAllRanges(const ScDocument* pDoc) const
+{
+ vector<ScTokenRef> aTokens;
+ sal_uInt32 nStop = mnColCount*mnRowCount;
+ for (sal_uInt32 i = 0; i < nStop; i++)
+ {
+ FormulaToken* p = maTokens[i].get();
+ if (!p)
+ continue;
+
+ ScTokenRef p2(p->Clone());
+ ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress());
+ }
+ return aTokens;
+}
+
+typedef std::map<SCROW, std::unique_ptr<FormulaToken>> FormulaTokenMap;
+typedef std::map<sal_uInt32, FormulaTokenMap> FormulaTokenMapMap;
+
+class Chart2PositionMap
+{
+public:
+ Chart2PositionMap(SCCOL nColCount, SCROW nRowCount,
+ bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols,
+ ScDocument* pDoc );
+ ~Chart2PositionMap();
+
+ SCCOL getDataColCount() const { return mnDataColCount; }
+ SCROW getDataRowCount() const { return mnDataRowCount; }
+
+ vector<ScTokenRef> getLeftUpperCornerRanges() const;
+ vector<ScTokenRef> getAllColHeaderRanges() const;
+ vector<ScTokenRef> getAllRowHeaderRanges() const;
+
+ vector<ScTokenRef> getColHeaderRanges(SCCOL nChartCol) const;
+ vector<ScTokenRef> getRowHeaderRanges(SCROW nChartRow) const;
+
+ vector<ScTokenRef> getDataColRanges(SCCOL nCol) const;
+ vector<ScTokenRef> getDataRowRanges(SCROW nRow) const;
+
+private:
+ const ScDocument* mpDoc;
+ SCCOL mnDataColCount;
+ SCROW mnDataRowCount;
+
+ TokenTable maLeftUpperCorner; //nHeaderColCount*nHeaderRowCount
+ TokenTable maColHeaders; //mnDataColCount*nHeaderRowCount
+ TokenTable maRowHeaders; //nHeaderColCount*mnDataRowCount
+ TokenTable maData;//mnDataColCount*mnDataRowCount
+};
+
+Chart2PositionMap::Chart2PositionMap(SCCOL nAllColCount, SCROW nAllRowCount,
+ bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols, ScDocument* pDoc)
+{
+ mpDoc = pDoc;
+ // if bFillRowHeader is true, at least the first column serves as a row header.
+ // If more than one column is pure text all the first pure text columns are used as header.
+ // Likewise, if bFillColumnHeader is true, at least the first row serves as a column header.
+ // If more than one row is pure text all the first pure text rows are used as header.
+
+ SCROW nHeaderRowCount = (bFillColumnHeader && nAllColCount && nAllRowCount) ? 1 : 0;
+ SCCOL nHeaderColCount = (bFillRowHeader && nAllColCount && nAllRowCount) ? 1 : 0;
+
+ if( pDoc && (nHeaderColCount || nHeaderRowCount ) )
+ {
+ //check whether there is more than one text column or row that should be added to the headers
+ SCROW nMaxHeaderRow = nAllRowCount;
+ SCCOL nCol = 0;
+ for (auto it = rCols.begin(); it != rCols.end(); ++it, ++nCol)
+ {
+ // Skip header columns
+ if (nCol < nHeaderColCount)
+ continue;
+
+ const auto& rCol = *it;
+
+ bool bFoundValuesInCol = false;
+ bool bFoundAnythingInCol = false;
+ SCROW nRow = 0;
+ for (auto it2 = rCol.second.begin(); it2 != rCol.second.end(); ++it2, ++nRow)
+ {
+ const auto& rCell = *it2;
+
+ // Skip header rows
+ if (nRow < nHeaderRowCount || !rCell.second)
+ continue;
+
+ ScRange aRange;
+ bool bExternal = false;
+ StackVar eType = rCell.second->GetType();
+ if( eType==svExternal || eType==svExternalSingleRef || eType==svExternalDoubleRef || eType==svExternalName )
+ bExternal = true;//lllll todo correct?
+ ScTokenRef pSharedToken(rCell.second->Clone());
+ ScRefTokenHelper::getRangeFromToken(pDoc, aRange, pSharedToken, ScAddress(), bExternal);
+ SCCOL nCol1=0, nCol2=0;
+ SCROW nRow1=0, nRow2=0;
+ SCTAB nTab1=0, nTab2=0;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
+ if ( pDoc->HasValueData( nCol1, nRow1, nTab1 ) )
+ {
+ // Found some numeric data
+ bFoundValuesInCol = true;
+ nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
+ break;
+ }
+ if ( pDoc->HasData( nCol1, nRow1, nTab1 ) )
+ {
+ // Found some other data (non-numeric)
+ bFoundAnythingInCol = true;
+ }
+ else
+ {
+ // If cell is empty, it belongs to data
+ nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
+ }
+ }
+
+ if (nHeaderColCount && !bFoundValuesInCol && bFoundAnythingInCol && nCol == nHeaderColCount)
+ {
+ // There is no values in row, but some data. And this column is next to header
+ // So lets put it to header
+ nHeaderColCount++;
+ }
+ }
+
+ if (nHeaderRowCount)
+ {
+ nHeaderRowCount = nMaxHeaderRow;
+ }
+ }
+
+ mnDataColCount = nAllColCount - nHeaderColCount;
+ mnDataRowCount = nAllRowCount - nHeaderRowCount;
+
+ maLeftUpperCorner.init(nHeaderColCount,nHeaderRowCount);
+ maColHeaders.init(mnDataColCount,nHeaderRowCount);
+ maRowHeaders.init(nHeaderColCount,mnDataRowCount);
+ maData.init(mnDataColCount,mnDataRowCount);
+
+ FormulaTokenMapMap::iterator it1 = rCols.begin();
+ for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol)
+ {
+ if (it1 != rCols.end())
+ {
+ FormulaTokenMap& rCol = it1->second;
+ FormulaTokenMap::iterator it2 = rCol.begin();
+ for (SCROW nRow = 0; nRow < nAllRowCount; ++nRow)
+ {
+ std::unique_ptr<FormulaToken> pToken;
+ if (it2 != rCol.end())
+ {
+ pToken = std::move(it2->second);
+ ++it2;
+ }
+
+ if( nCol < nHeaderColCount )
+ {
+ if( nRow < nHeaderRowCount )
+ maLeftUpperCorner.push_back(std::move(pToken));
+ else
+ maRowHeaders.push_back(std::move(pToken));
+ }
+ else if( nRow < nHeaderRowCount )
+ maColHeaders.push_back(std::move(pToken));
+ else
+ maData.push_back(std::move(pToken));
+ }
+ ++it1;
+ }
+ }
+}
+
+Chart2PositionMap::~Chart2PositionMap()
+{
+ maLeftUpperCorner.clear();
+ maColHeaders.clear();
+ maRowHeaders.clear();
+ maData.clear();
+}
+
+vector<ScTokenRef> Chart2PositionMap::getLeftUpperCornerRanges() const
+{
+ return maLeftUpperCorner.getAllRanges(mpDoc);
+}
+vector<ScTokenRef> Chart2PositionMap::getAllColHeaderRanges() const
+{
+ return maColHeaders.getAllRanges(mpDoc);
+}
+vector<ScTokenRef> Chart2PositionMap::getAllRowHeaderRanges() const
+{
+ return maRowHeaders.getAllRanges(mpDoc);
+}
+vector<ScTokenRef> Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const
+{
+ return maColHeaders.getColRanges(mpDoc, nCol);
+}
+vector<ScTokenRef> Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const
+{
+ return maRowHeaders.getRowRanges(mpDoc, nRow);
+}
+
+vector<ScTokenRef> Chart2PositionMap::getDataColRanges(SCCOL nCol) const
+{
+ return maData.getColRanges(mpDoc, nCol);
+}
+
+vector<ScTokenRef> Chart2PositionMap::getDataRowRanges(SCROW nRow) const
+{
+ return maData.getRowRanges(mpDoc, nRow);
+}
+
+/**
+ * Designed to be a drop-in replacement for ScChartPositioner, in order to
+ * handle external references.
+ */
+class Chart2Positioner
+{
+ enum GlueType
+ {
+ GLUETYPE_NA,
+ GLUETYPE_NONE,
+ GLUETYPE_COLS,
+ GLUETYPE_ROWS,
+ GLUETYPE_BOTH
+ };
+
+public:
+ Chart2Positioner(const Chart2Positioner&) = delete;
+ const Chart2Positioner& operator=(const Chart2Positioner&) = delete;
+
+ Chart2Positioner(ScDocument* pDoc, const vector<ScTokenRef>& rRefTokens) :
+ mrRefTokens(rRefTokens),
+ meGlue(GLUETYPE_NA),
+ mnStartCol(0),
+ mnStartRow(0),
+ mpDoc(pDoc),
+ mbColHeaders(false),
+ mbRowHeaders(false),
+ mbDummyUpperLeft(false)
+ {
+ }
+
+ void setHeaders(bool bColHeaders, bool bRowHeaders)
+ {
+ mbColHeaders = bColHeaders;
+ mbRowHeaders = bRowHeaders;
+ }
+
+ Chart2PositionMap* getPositionMap()
+ {
+ createPositionMap();
+ return mpPositionMap.get();
+ }
+
+private:
+ void invalidateGlue();
+ void glueState();
+ void calcGlueState(SCCOL nCols, SCROW nRows);
+ void createPositionMap();
+
+private:
+ const vector<ScTokenRef>& mrRefTokens;
+ std::unique_ptr<Chart2PositionMap> mpPositionMap;
+ GlueType meGlue;
+ SCCOL mnStartCol;
+ SCROW mnStartRow;
+ ScDocument* mpDoc;
+ bool mbColHeaders:1;
+ bool mbRowHeaders:1;
+ bool mbDummyUpperLeft:1;
+};
+
+void Chart2Positioner::invalidateGlue()
+{
+ meGlue = GLUETYPE_NA;
+ mpPositionMap.reset();
+}
+
+void Chart2Positioner::glueState()
+{
+ if (meGlue != GLUETYPE_NA)
+ return;
+
+ mbDummyUpperLeft = false;
+ if (mrRefTokens.size() <= 1)
+ {
+ // Source data consists of only one data range.
+ const ScTokenRef& p = mrRefTokens.front();
+ ScComplexRefData aData;
+ if (ScRefTokenHelper::getDoubleRefDataFromToken(aData, p))
+ {
+ if (aData.Ref1.Tab() == aData.Ref2.Tab())
+ meGlue = GLUETYPE_NONE;
+ else
+ meGlue = GLUETYPE_COLS;
+ mnStartCol = aData.Ref1.Col();
+ mnStartRow = aData.Ref1.Row();
+ }
+ else
+ {
+ invalidateGlue();
+ mnStartCol = 0;
+ mnStartRow = 0;
+ }
+ return;
+ }
+
+ ScComplexRefData aData;
+ if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, mrRefTokens.front()))
+ {
+ SAL_WARN("sc", "Chart2Positioner::glueState getDoubleRefDataFromToken failed");
+ invalidateGlue();
+ mnStartCol = 0;
+ mnStartRow = 0;
+ return;
+ }
+ mnStartCol = aData.Ref1.Col();
+ mnStartRow = aData.Ref1.Row();
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ for (const auto& rxToken : mrRefTokens)
+ {
+ ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
+ SCCOLROW n1 = aData.Ref1.Col();
+ SCCOLROW n2 = aData.Ref2.Col();
+ if (n1 > mpDoc->MaxCol())
+ n1 = mpDoc->MaxCol();
+ if (n2 > mpDoc->MaxCol())
+ n2 = mpDoc->MaxCol();
+ if (n1 < mnStartCol)
+ mnStartCol = static_cast<SCCOL>(n1);
+ if (n2 > nEndCol)
+ nEndCol = static_cast<SCCOL>(n2);
+
+ n1 = aData.Ref1.Row();
+ n2 = aData.Ref2.Row();
+ if (n1 > mpDoc->MaxRow())
+ n1 = mpDoc->MaxRow();
+ if (n2 > mpDoc->MaxRow())
+ n2 = mpDoc->MaxRow();
+
+ if (n1 < mnStartRow)
+ mnStartRow = static_cast<SCROW>(n1);
+ if (n2 > nEndRow)
+ nEndRow = static_cast<SCROW>(n2);
+ }
+
+ if (mnStartCol == nEndCol)
+ {
+ // All source data is in a single column.
+ meGlue = GLUETYPE_ROWS;
+ return;
+ }
+
+ if (mnStartRow == nEndRow)
+ {
+ // All source data is in a single row.
+ meGlue = GLUETYPE_COLS;
+ return;
+ }
+
+ // total column size
+ SCCOL nC = nEndCol - mnStartCol + 1;
+
+ // total row size
+ SCROW nR = nEndRow - mnStartRow + 1;
+
+ // #i103540# prevent invalid vector size
+ if ((nC <= 0) || (nR <= 0))
+ {
+ invalidateGlue();
+ mnStartCol = 0;
+ mnStartRow = 0;
+ return;
+ }
+
+ calcGlueState(nC, nR);
+}
+
+enum State { Hole = 0, Occupied = 1, Free = 2, Glue = 3 };
+
+void Chart2Positioner::calcGlueState(SCCOL nColSize, SCROW nRowSize)
+{
+ // TODO: This code can use some space optimization. Using an array to
+ // store individual cell's states is terribly inefficient esp for large
+ // data ranges; let's use flat_segment_tree to reduce memory usage here.
+
+ sal_uInt32 nCR = static_cast<sal_uInt32>(nColSize*nRowSize);
+
+ vector<State> aCellStates(nCR, Hole);
+
+ // Mark all referenced cells "occupied".
+ for (const auto& rxToken : mrRefTokens)
+ {
+ ScComplexRefData aData;
+ ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
+ SCCOL nCol1 = aData.Ref1.Col() - mnStartCol;
+ SCCOL nCol2 = aData.Ref2.Col() - mnStartCol;
+ SCROW nRow1 = aData.Ref1.Row() - mnStartRow;
+ SCROW nRow2 = aData.Ref2.Row() - mnStartRow;
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ size_t i = nCol*nRowSize + nRow;
+ aCellStates[i] = Occupied;
+ }
+ }
+
+ // If at least one cell in either the first column or first row is empty,
+ // we don't glue at all unless the whole column or row is empty; we expect
+ // all cells in the first column / row to be fully populated. If we have
+ // empty column or row, then we do glue by the column or row,
+ // respectively.
+
+ bool bGlue = true;
+ bool bGlueCols = false;
+ for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol)
+ {
+ for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
+ {
+ size_t i = nCol*nRowSize + nRow;
+ if (aCellStates[i] == Occupied)
+ {
+ if (nCol == 0 || nRow == 0)
+ break;
+
+ bGlue = false;
+ }
+ else
+ aCellStates[i] = Free;
+ }
+ size_t nLast = (nCol+1)*nRowSize - 1; // index for the last cell in the column.
+ if (bGlue && aCellStates[nLast] == Free)
+ {
+ // Whole column is empty.
+ aCellStates[nLast] = Glue;
+ bGlueCols = true;
+ }
+ }
+
+ bool bGlueRows = false;
+ for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
+ {
+ size_t i = nRow;
+ for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol, i += nRowSize)
+ {
+ if (aCellStates[i] == Occupied)
+ {
+ if (nCol == 0 || nRow == 0)
+ break;
+
+ bGlue = false;
+ }
+ else
+ aCellStates[i] = Free;
+ }
+ i = (nColSize-1)*nRowSize + nRow; // index for the row position in the last column.
+ if (bGlue && aCellStates[i] == Free)
+ {
+ // Whole row is empty.
+ aCellStates[i] = Glue;
+ bGlueRows = true;
+ }
+ }
+
+ size_t i = 1;
+ for (sal_uInt32 n = 1; bGlue && n < nCR; ++n, ++i)
+ if (aCellStates[i] == Hole)
+ bGlue = false;
+
+ if (bGlue)
+ {
+ if (bGlueCols && bGlueRows)
+ meGlue = GLUETYPE_BOTH;
+ else if (bGlueRows)
+ meGlue = GLUETYPE_ROWS;
+ else
+ meGlue = GLUETYPE_COLS;
+ if (aCellStates.front() != Occupied)
+ mbDummyUpperLeft = true;
+ }
+ else
+ meGlue = GLUETYPE_NONE;
+}
+
+void Chart2Positioner::createPositionMap()
+{
+ if (meGlue == GLUETYPE_NA && mpPositionMap)
+ mpPositionMap.reset();
+
+ if (mpPositionMap)
+ return;
+
+ glueState();
+
+ bool bNoGlue = (meGlue == GLUETYPE_NONE);
+ FormulaTokenMapMap aCols;
+ SCROW nNoGlueRow = 0;
+ for (const ScTokenRef& pToken : mrRefTokens)
+ {
+ bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
+ sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
+ svl::SharedString aTabName = svl::SharedString::getEmptyString();
+ if (bExternal)
+ aTabName = pToken->GetString();
+
+ ScComplexRefData aData;
+ if( !ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken) )
+ break;
+ const ScSingleRefData& s = aData.Ref1;
+ const ScSingleRefData& e = aData.Ref2;
+ SCCOL nCol1 = s.Col(), nCol2 = e.Col();
+ SCROW nRow1 = s.Row(), nRow2 = e.Row();
+ SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
+
+ for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
+ {
+ // columns on secondary sheets are appended; we treat them as if
+ // all columns are on the same sheet. TODO: We can't assume that
+ // the column range is 16-bit; remove that restriction.
+ sal_uInt32 nInsCol = (static_cast<sal_uInt32>(nTab) << 16) |
+ (bNoGlue ? 0 : static_cast<sal_uInt32>(nCol1));
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol)
+ {
+ FormulaTokenMap& rCol = aCols[nInsCol];
+
+ auto nInsRow = bNoGlue ? nNoGlueRow : nRow1;
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow, ++nInsRow)
+ {
+ ScSingleRefData aCellData;
+ aCellData.InitFlags();
+ aCellData.SetFlag3D(true);
+ aCellData.SetColRel(false);
+ aCellData.SetRowRel(false);
+ aCellData.SetTabRel(false);
+ aCellData.SetAbsCol(nCol);
+ aCellData.SetAbsRow(nRow);
+ aCellData.SetAbsTab(nTab);
+
+ auto& rCell = rCol[nInsRow];
+ if (!rCell)
+ {
+ if (bExternal)
+ rCell.reset(new ScExternalSingleRefToken(nFileId, aTabName, aCellData));
+ else
+ rCell.reset(new ScSingleRefToken(mpDoc->GetSheetLimits(), aCellData));
+ }
+ }
+ }
+ }
+ nNoGlueRow += nRow2 - nRow1 + 1;
+ }
+
+ bool bFillRowHeader = mbRowHeaders;
+ bool bFillColumnHeader = mbColHeaders;
+
+ SCSIZE nAllColCount = static_cast<SCSIZE>(aCols.size());
+ SCSIZE nAllRowCount = 0;
+ if (!aCols.empty())
+ {
+ FormulaTokenMap& rCol = aCols.begin()->second;
+ if (mbDummyUpperLeft)
+ rCol.try_emplace( 0, nullptr ); // dummy for labeling
+ nAllRowCount = static_cast<SCSIZE>(rCol.size());
+ }
+
+ if( nAllColCount!=0 && nAllRowCount!=0 )
+ {
+ if (bNoGlue)
+ {
+ FormulaTokenMap& rFirstCol = aCols.begin()->second;
+ for (const auto& rFirstColEntry : rFirstCol)
+ {
+ SCROW nKey = rFirstColEntry.first;
+ for (auto& rEntry : aCols)
+ {
+ FormulaTokenMap& rCol = rEntry.second;
+ rCol.try_emplace( nKey, nullptr );
+ }
+ }
+ }
+ }
+ mpPositionMap.reset(
+ new Chart2PositionMap(
+ static_cast<SCCOL>(nAllColCount), static_cast<SCROW>(nAllRowCount),
+ bFillRowHeader, bFillColumnHeader, aCols, mpDoc));
+}
+
+/**
+ * Function object to create a range string from a token list.
+ */
+class Tokens2RangeString
+{
+public:
+ Tokens2RangeString(ScDocument& rDoc, FormulaGrammar::Grammar eGram, sal_Unicode cRangeSep) :
+ mpRangeStr(std::make_shared<OUStringBuffer>()),
+ mpDoc(&rDoc),
+ meGrammar(eGram),
+ mcRangeSep(cRangeSep),
+ mbFirst(true)
+ {
+ }
+
+ void operator() (const ScTokenRef& rToken)
+ {
+ ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), meGrammar);
+ OUString aStr;
+ aCompiler.CreateStringFromToken(aStr, rToken.get());
+ if (mbFirst)
+ mbFirst = false;
+ else
+ mpRangeStr->append(mcRangeSep);
+ mpRangeStr->append(aStr);
+ }
+
+ void getString(OUString& rStr)
+ {
+ rStr = mpRangeStr->makeStringAndClear();
+ }
+
+private:
+ shared_ptr<OUStringBuffer> mpRangeStr;
+ ScDocument* mpDoc;
+ FormulaGrammar::Grammar meGrammar;
+ sal_Unicode mcRangeSep;
+ bool mbFirst;
+};
+
+/**
+ * Function object to convert a list of tokens into a string form suitable
+ * for ODF export. In ODF, a range is expressed as
+ *
+ * (start cell address):(end cell address)
+ *
+ * and each address doesn't include any '$' symbols.
+ */
+class Tokens2RangeStringXML
+{
+public:
+ explicit Tokens2RangeStringXML(ScDocument& rDoc) :
+ mpRangeStr(std::make_shared<OUStringBuffer>()),
+ mpDoc(&rDoc),
+ mbFirst(true)
+ {
+ }
+
+ void operator() (const ScTokenRef& rToken)
+ {
+ if (mbFirst)
+ mbFirst = false;
+ else
+ mpRangeStr->append(mcRangeSep);
+
+ ScTokenRef aStart, aEnd;
+ bool bValidToken = splitRangeToken(*mpDoc, rToken, aStart, aEnd);
+ // Check there is a valid reference in named range
+ if (!bValidToken && rToken->GetType() == svIndex && rToken->GetOpCode() == ocName)
+ {
+ ScRangeData* pNameRange = mpDoc->FindRangeNameBySheetAndIndex(rToken->GetSheet(), rToken->GetIndex());
+ if (pNameRange->HasReferences())
+ {
+ const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
+ bValidToken = splitRangeToken(*mpDoc, aTempToken, aStart, aEnd);
+ }
+ }
+
+ OSL_ENSURE(bValidToken, "invalid token");
+ if (!bValidToken)
+ return;
+
+ ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), FormulaGrammar::GRAM_ENGLISH);
+ {
+ OUString aStr;
+ aCompiler.CreateStringFromToken(aStr, aStart.get());
+ mpRangeStr->append(aStr);
+ }
+ mpRangeStr->append(mcAddrSep);
+ {
+ OUString aStr;
+ aCompiler.CreateStringFromToken(aStr, aEnd.get());
+ mpRangeStr->append(aStr);
+ }
+ }
+
+ void getString(OUString& rStr)
+ {
+ rStr = mpRangeStr->makeStringAndClear();
+ }
+
+private:
+ static bool splitRangeToken(const ScDocument& rDoc, const ScTokenRef& pToken, ScTokenRef& rStart, ScTokenRef& rEnd)
+ {
+ ScComplexRefData aData;
+ bool bIsRefToken = ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken);
+ OSL_ENSURE(bIsRefToken, "invalid token");
+ if (!bIsRefToken)
+ return false;
+ bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
+ sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
+ svl::SharedString aTabName = svl::SharedString::getEmptyString();
+ if (bExternal)
+ aTabName = pToken->GetString();
+
+ // In saving to XML, we don't prepend address with '$'.
+ setRelative(aData.Ref1);
+ setRelative(aData.Ref2);
+
+ // In XML, the range must explicitly specify sheet name.
+ aData.Ref1.SetFlag3D(true);
+ aData.Ref2.SetFlag3D(true);
+
+ if (bExternal)
+ rStart.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref1));
+ else
+ rStart.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref1));
+
+ if (bExternal)
+ rEnd.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref2));
+ else
+ rEnd.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref2));
+ return true;
+ }
+
+ static void setRelative(ScSingleRefData& rData)
+ {
+ rData.SetColRel(true);
+ rData.SetRowRel(true);
+ rData.SetTabRel(true);
+ }
+
+private:
+ shared_ptr<OUStringBuffer> mpRangeStr;
+ ScDocument* mpDoc;
+ static const sal_Unicode mcRangeSep = ' ';
+ static const sal_Unicode mcAddrSep = ':';
+ bool mbFirst;
+};
+
+void lcl_convertTokensToString(OUString& rStr, const vector<ScTokenRef>& rTokens, ScDocument& rDoc)
+{
+ const sal_Unicode cRangeSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ FormulaGrammar::Grammar eGrammar = rDoc.GetGrammar();
+ Tokens2RangeString func(rDoc, eGrammar, cRangeSep);
+ func = ::std::for_each(rTokens.begin(), rTokens.end(), func);
+ func.getString(rStr);
+}
+
+} // anonymous namespace
+
+// DataProvider ==============================================================
+
+ScChart2DataProvider::ScChart2DataProvider( ScDocument* pDoc )
+ : m_pDocument( pDoc)
+ , m_aPropSet(lcl_GetDataProviderPropertyMap())
+ , m_bIncludeHiddenCells( true)
+{
+ if ( m_pDocument )
+ m_pDocument->AddUnoObject( *this);
+}
+
+ScChart2DataProvider::~ScChart2DataProvider()
+{
+ SolarMutexGuard g;
+
+ if ( m_pDocument )
+ m_pDocument->RemoveUnoObject( *this);
+}
+
+void ScChart2DataProvider::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ m_pDocument = nullptr;
+ }
+}
+
+sal_Bool SAL_CALL ScChart2DataProvider::createDataSourcePossible( const uno::Sequence< beans::PropertyValue >& aArguments )
+{
+ SolarMutexGuard aGuard;
+ if( ! m_pDocument )
+ return false;
+
+ OUString aRangeRepresentation;
+ for(const auto& rArgument : aArguments)
+ {
+ if ( rArgument.Name == "CellRangeRepresentation" )
+ {
+ rArgument.Value >>= aRangeRepresentation;
+ }
+ }
+
+ vector<ScTokenRef> aTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ return !aTokens.empty();
+}
+
+namespace
+{
+
+uno::Reference< chart2::data::XLabeledDataSequence > lcl_createLabeledDataSequenceFromTokens(
+ vector< ScTokenRef > && aValueTokens, vector< ScTokenRef > && aLabelTokens,
+ ScDocument* pDoc, bool bIncludeHiddenCells )
+{
+ uno::Reference< chart2::data::XLabeledDataSequence > xResult;
+ bool bHasValues = !aValueTokens.empty();
+ bool bHasLabel = !aLabelTokens.empty();
+ if( bHasValues || bHasLabel )
+ {
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ if ( xContext.is() )
+ {
+ xResult.set( chart2::data::LabeledDataSequence::create(xContext), uno::UNO_QUERY_THROW );
+ }
+ if ( bHasValues )
+ {
+ uno::Reference< chart2::data::XDataSequence > xSeq( new ScChart2DataSequence( pDoc, std::move(aValueTokens), bIncludeHiddenCells ) );
+ xResult->setValues( xSeq );
+ }
+ if ( bHasLabel )
+ {
+ //Labels should always include hidden cells, regardless of the bIncludeHiddenCells setting
+ uno::Reference< chart2::data::XDataSequence > xLabelSeq( new ScChart2DataSequence( pDoc, std::move(aLabelTokens), true ) );
+ xResult->setLabel( xLabelSeq );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ return xResult;
+}
+
+/**
+ * Check the current list of reference tokens, and add the upper left
+ * corner of the minimum range that encloses all ranges if certain
+ * conditions are met.
+ *
+ * @param rRefTokens list of reference tokens
+ *
+ * @return true if the corner was added, false otherwise.
+ */
+bool lcl_addUpperLeftCornerIfMissing(const ScDocument* pDoc, vector<ScTokenRef>& rRefTokens,
+ SCROW nCornerRowCount, SCCOL nCornerColumnCount)
+{
+ using ::std::max;
+ using ::std::min;
+
+ if (rRefTokens.empty())
+ return false;
+
+ SCCOL nMinCol = pDoc->GetSheetLimits().GetMaxColCount();
+ SCROW nMinRow = pDoc->GetSheetLimits().GetMaxRowCount();
+ SCCOL nMaxCol = 0;
+ SCROW nMaxRow = 0;
+ SCTAB nTab = 0;
+
+ sal_uInt16 nFileId = 0;
+ svl::SharedString aExtTabName;
+ bool bExternal = false;
+
+ vector<ScTokenRef>::const_iterator itr = rRefTokens.begin(), itrEnd = rRefTokens.end();
+
+ // Get the first ref token.
+ ScTokenRef pToken = *itr;
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rData = *pToken->GetSingleRef();
+ nMinCol = rData.Col();
+ nMinRow = rData.Row();
+ nMaxCol = rData.Col();
+ nMaxRow = rData.Row();
+ nTab = rData.Tab();
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rData = *pToken->GetDoubleRef();
+ nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col());
+ nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row());
+ nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col());
+ nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row());
+ nTab = rData.Ref1.Tab();
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rData = *pToken->GetSingleRef();
+ nMinCol = rData.Col();
+ nMinRow = rData.Row();
+ nMaxCol = rData.Col();
+ nMaxRow = rData.Row();
+ nTab = rData.Tab();
+ nFileId = pToken->GetIndex();
+ aExtTabName = pToken->GetString();
+ bExternal = true;
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rData = *pToken->GetDoubleRef();
+ nMinCol = min(rData.Ref1.Col(), rData.Ref2.Col());
+ nMinRow = min(rData.Ref1.Row(), rData.Ref2.Row());
+ nMaxCol = max(rData.Ref1.Col(), rData.Ref2.Col());
+ nMaxRow = max(rData.Ref1.Row(), rData.Ref2.Row());
+ nTab = rData.Ref1.Tab();
+ nFileId = pToken->GetIndex();
+ aExtTabName = pToken->GetString();
+ bExternal = true;
+ }
+ break;
+ default:
+ ;
+ }
+
+ // Determine the minimum range enclosing all data ranges. Also make sure
+ // that they are all on the same table.
+
+ for (++itr; itr != itrEnd; ++itr)
+ {
+ pToken = *itr;
+ switch (pToken->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rData = *pToken->GetSingleRef();
+
+ nMinCol = min(nMinCol, rData.Col());
+ nMinRow = min(nMinRow, rData.Row());
+ nMaxCol = max(nMaxCol, rData.Col());
+ nMaxRow = max(nMaxRow, rData.Row());
+ if (nTab != rData.Tab() || bExternal)
+ return false;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rData = *pToken->GetDoubleRef();
+
+ nMinCol = min(nMinCol, rData.Ref1.Col());
+ nMinCol = min(nMinCol, rData.Ref2.Col());
+ nMinRow = min(nMinRow, rData.Ref1.Row());
+ nMinRow = min(nMinRow, rData.Ref2.Row());
+
+ nMaxCol = max(nMaxCol, rData.Ref1.Col());
+ nMaxCol = max(nMaxCol, rData.Ref2.Col());
+ nMaxRow = max(nMaxRow, rData.Ref1.Row());
+ nMaxRow = max(nMaxRow, rData.Ref2.Row());
+
+ if (nTab != rData.Ref1.Tab() || bExternal)
+ return false;
+ }
+ break;
+ case svExternalSingleRef:
+ {
+ if (!bExternal)
+ return false;
+
+ if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
+ return false;
+
+ const ScSingleRefData& rData = *pToken->GetSingleRef();
+
+ nMinCol = min(nMinCol, rData.Col());
+ nMinRow = min(nMinRow, rData.Row());
+ nMaxCol = max(nMaxCol, rData.Col());
+ nMaxRow = max(nMaxRow, rData.Row());
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ if (!bExternal)
+ return false;
+
+ if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
+ return false;
+
+ const ScComplexRefData& rData = *pToken->GetDoubleRef();
+
+ nMinCol = min(nMinCol, rData.Ref1.Col());
+ nMinCol = min(nMinCol, rData.Ref2.Col());
+ nMinRow = min(nMinRow, rData.Ref1.Row());
+ nMinRow = min(nMinRow, rData.Ref2.Row());
+
+ nMaxCol = max(nMaxCol, rData.Ref1.Col());
+ nMaxCol = max(nMaxCol, rData.Ref2.Col());
+ nMaxRow = max(nMaxRow, rData.Ref1.Row());
+ nMaxRow = max(nMaxRow, rData.Ref2.Row());
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ const auto & rSheetLimits = pDoc->GetSheetLimits();
+ if (nMinRow >= nMaxRow || nMinCol >= nMaxCol ||
+ nMinRow >= rSheetLimits.GetMaxRowCount() || nMinCol >= rSheetLimits.GetMaxColCount() ||
+ nMaxRow >= rSheetLimits.GetMaxRowCount() || nMaxCol >= rSheetLimits.GetMaxColCount())
+ {
+ // Invalid range. Bail out.
+ return false;
+ }
+
+ // Check if the following conditions are met:
+
+ // 1) The upper-left corner cell is not included.
+ // 2) The three adjacent cells of that corner cell are included.
+
+ bool bRight = false, bBottom = false, bDiagonal = false;
+ for (const auto& rxToken : rRefTokens)
+ {
+ switch (rxToken->GetType())
+ {
+ case svSingleRef:
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rData = *rxToken->GetSingleRef();
+ if (rData.Col() == nMinCol && rData.Row() == nMinRow)
+ // The corner cell is contained.
+ return false;
+
+ if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow)
+ bRight = true;
+
+ if (rData.Col() == nMinCol && rData.Row() == nMinRow+nCornerRowCount)
+ bBottom = true;
+
+ if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow+nCornerRowCount)
+ bDiagonal = true;
+ }
+ break;
+ case svDoubleRef:
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rData = *rxToken->GetDoubleRef();
+ const ScSingleRefData& r1 = rData.Ref1;
+ const ScSingleRefData& r2 = rData.Ref2;
+ if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
+ r1.Row() <= nMinRow && nMinRow <= r2.Row())
+ // The corner cell is contained.
+ return false;
+
+ if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
+ r1.Row() <= nMinRow && nMinRow <= r2.Row())
+ bRight = true;
+
+ if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
+ r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
+ bBottom = true;
+
+ if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
+ r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
+ bDiagonal = true;
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ if (!bRight || !bBottom || !bDiagonal)
+ // Not all the adjacent cells are included. Bail out.
+ return false;
+
+ ScSingleRefData aData;
+ aData.InitFlags();
+ aData.SetFlag3D(true);
+ aData.SetAbsCol(nMinCol);
+ aData.SetAbsRow(nMinRow);
+ aData.SetAbsTab(nTab);
+
+ if( nCornerRowCount==1 && nCornerColumnCount==1 )
+ {
+ if (bExternal)
+ {
+ ScTokenRef pCorner(
+ new ScExternalSingleRefToken(nFileId, aExtTabName, aData));
+ ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
+ }
+ else
+ {
+ ScTokenRef pCorner(new ScSingleRefToken(pDoc->GetSheetLimits(), aData));
+ ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
+ }
+ }
+ else
+ {
+ ScSingleRefData aDataEnd(aData);
+ aDataEnd.IncCol(nCornerColumnCount-1);
+ aDataEnd.IncRow(nCornerRowCount-1);
+ ScComplexRefData r;
+ r.Ref1=aData;
+ r.Ref2=aDataEnd;
+ if (bExternal)
+ {
+ ScTokenRef pCorner(
+ new ScExternalDoubleRefToken(nFileId, aExtTabName, r));
+ ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
+ }
+ else
+ {
+ ScTokenRef pCorner(new ScDoubleRefToken(pDoc->GetSheetLimits(), r));
+ ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
+ }
+ }
+
+ return true;
+}
+
+#define SHRINK_RANGE_THRESHOLD 10000
+
+class ShrinkRefTokenToDataRange
+{
+ ScDocument* mpDoc;
+public:
+ explicit ShrinkRefTokenToDataRange(ScDocument* pDoc) : mpDoc(pDoc) {}
+ void operator() (const ScTokenRef& rRef)
+ {
+ if (ScRefTokenHelper::isExternalRef(rRef))
+ return;
+
+ // Don't assume an ScDoubleRefToken if it isn't. It can be at least an
+ // ScSingleRefToken, then there isn't anything to shrink.
+ if (rRef->GetType() != svDoubleRef)
+ return;
+
+ ScComplexRefData& rData = *rRef->GetDoubleRef();
+ ScSingleRefData& s = rData.Ref1;
+ ScSingleRefData& e = rData.Ref2;
+
+ if(abs((e.Col()-s.Col())*(e.Row()-s.Row())) < SHRINK_RANGE_THRESHOLD)
+ return;
+
+ SCCOL nMinCol = mpDoc->MaxCol(), nMaxCol = 0;
+ SCROW nMinRow = mpDoc->MaxRow(), nMaxRow = 0;
+
+ // Determine the smallest range that encompasses the data ranges of all sheets.
+ SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
+ for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
+ {
+ SCCOL nCol1 = 0, nCol2 = mpDoc->MaxCol();
+ SCROW nRow1 = 0, nRow2 = mpDoc->MaxRow();
+ mpDoc->ShrinkToDataArea(nTab, nCol1, nRow1, nCol2, nRow2);
+ nMinCol = std::min(nMinCol, nCol1);
+ nMinRow = std::min(nMinRow, nRow1);
+ nMaxCol = std::max(nMaxCol, nCol2);
+ nMaxRow = std::max(nMaxRow, nRow2);
+ }
+
+ // Shrink range to the data range if applicable.
+ if (s.Col() < nMinCol)
+ s.SetAbsCol(nMinCol);
+ if (s.Row() < nMinRow)
+ s.SetAbsRow(nMinRow);
+ if (e.Col() > nMaxCol)
+ e.SetAbsCol(nMaxCol);
+ if (e.Row() > nMaxRow)
+ e.SetAbsRow(nMaxRow);
+ }
+};
+
+void shrinkToDataRange(ScDocument* pDoc, vector<ScTokenRef>& rRefTokens)
+{
+ std::for_each(rRefTokens.begin(), rRefTokens.end(), ShrinkRefTokenToDataRange(pDoc));
+}
+
+}
+
+uno::Reference< chart2::data::XDataSource> SAL_CALL
+ScChart2DataProvider::createDataSource(
+ const uno::Sequence< beans::PropertyValue >& aArguments )
+{
+ SolarMutexGuard aGuard;
+ if ( ! m_pDocument )
+ throw uno::RuntimeException();
+
+ uno::Reference< chart2::data::XDataSource> xResult;
+ bool bLabel = true;
+ bool bCategories = false;
+ bool bOrientCol = true;
+ OUString aRangeRepresentation;
+ uno::Sequence< sal_Int32 > aSequenceMapping;
+ bool bTimeBased = false;
+ for(const auto& rArgument : aArguments)
+ {
+ if ( rArgument.Name == "DataRowSource" )
+ {
+ chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
+ if( ! (rArgument.Value >>= eSource))
+ {
+ sal_Int32 nSource(0);
+ if( rArgument.Value >>= nSource )
+ eSource = static_cast< chart::ChartDataRowSource >( nSource );
+ }
+ bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
+ }
+ else if ( rArgument.Name == "FirstCellAsLabel" )
+ {
+ bLabel = ::cppu::any2bool(rArgument.Value);
+ }
+ else if ( rArgument.Name == "HasCategories" )
+ {
+ bCategories = ::cppu::any2bool(rArgument.Value);
+ }
+ else if ( rArgument.Name == "CellRangeRepresentation" )
+ {
+ rArgument.Value >>= aRangeRepresentation;
+ }
+ else if ( rArgument.Name == "SequenceMapping" )
+ {
+ rArgument.Value >>= aSequenceMapping;
+ }
+ else if ( rArgument.Name == "TimeBased" )
+ {
+ rArgument.Value >>= bTimeBased;
+ }
+ }
+
+ vector<ScTokenRef> aRefTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ if (aRefTokens.empty())
+ // Invalid range representation. Bail out.
+ throw lang::IllegalArgumentException();
+
+ SCTAB nTimeBasedStart = MAXTAB;
+ SCTAB nTimeBasedEnd = 0;
+ if(bTimeBased)
+ {
+ // limit to first sheet
+ for(const auto& rxToken : aRefTokens)
+ {
+ if (rxToken->GetType() != svDoubleRef)
+ continue;
+
+ ScComplexRefData& rData = *rxToken->GetDoubleRef();
+ ScSingleRefData& s = rData.Ref1;
+ ScSingleRefData& e = rData.Ref2;
+
+ nTimeBasedStart = std::min(nTimeBasedStart, s.Tab());
+ nTimeBasedEnd = std::min(nTimeBasedEnd, e.Tab());
+
+ if(s.Tab() != e.Tab())
+ e.SetAbsTab(s.Tab());
+ }
+ }
+
+ if(!bTimeBased)
+ shrinkToDataRange(m_pDocument, aRefTokens);
+
+ if (bLabel)
+ lcl_addUpperLeftCornerIfMissing(m_pDocument, aRefTokens, 1, 1); //#i90669#
+
+ bool bColHeaders = (bOrientCol ? bLabel : bCategories );
+ bool bRowHeaders = (bOrientCol ? bCategories : bLabel );
+
+ Chart2Positioner aChPositioner(m_pDocument, aRefTokens);
+ aChPositioner.setHeaders(bColHeaders, bRowHeaders);
+
+ const Chart2PositionMap* pChartMap = aChPositioner.getPositionMap();
+ if (!pChartMap)
+ // No chart position map instance. Bail out.
+ return xResult;
+
+ rtl::Reference<ScChart2DataSource> pDS;
+ ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqs;
+
+ // Fill Categories
+ if( bCategories )
+ {
+ vector<ScTokenRef> aValueTokens;
+ if (bOrientCol)
+ aValueTokens = pChartMap->getAllRowHeaderRanges();
+ else
+ aValueTokens = pChartMap->getAllColHeaderRanges();
+
+ vector<ScTokenRef> aLabelTokens(
+ pChartMap->getLeftUpperCornerRanges());
+
+ uno::Reference< chart2::data::XLabeledDataSequence > xCategories = lcl_createLabeledDataSequenceFromTokens(
+ std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
+ if ( xCategories.is() )
+ {
+ aSeqs.push_back( xCategories );
+ }
+ }
+
+ // Fill Series (values and label)
+ sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ vector<ScTokenRef> aValueTokens;
+ vector<ScTokenRef> aLabelTokens;
+ if (bOrientCol)
+ {
+ aValueTokens = pChartMap->getDataColRanges(static_cast<SCCOL>(i));
+ aLabelTokens = pChartMap->getColHeaderRanges(static_cast<SCCOL>(i));
+ }
+ else
+ {
+ aValueTokens = pChartMap->getDataRowRanges(static_cast<SCROW>(i));
+ aLabelTokens = pChartMap->getRowHeaderRanges(static_cast<SCROW>(i));
+ }
+ uno::Reference< chart2::data::XLabeledDataSequence > xChartSeries = lcl_createLabeledDataSequenceFromTokens(
+ std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
+ if ( xChartSeries.is() && xChartSeries->getValues().is() && xChartSeries->getValues()->getData().hasElements() )
+ {
+ aSeqs.push_back( xChartSeries );
+ }
+ }
+
+ pDS = new ScChart2DataSource(m_pDocument);
+
+ //reorder labeled sequences according to aSequenceMapping
+ ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVector;
+ aSeqVector.reserve(aSeqs.size());
+ for (auto const& aSeq : aSeqs)
+ {
+ aSeqVector.push_back(aSeq);
+ }
+
+ for( const sal_Int32 nNewIndex : std::as_const(aSequenceMapping) )
+ {
+ // note: assuming that the values in the sequence mapping are always non-negative
+ ::std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >::size_type nOldIndex( static_cast< sal_uInt32 >( nNewIndex ) );
+ if( nOldIndex < aSeqVector.size() )
+ {
+ pDS->AddLabeledSequence( aSeqVector[nOldIndex] );
+ aSeqVector[nOldIndex] = nullptr;
+ }
+ }
+
+ for(const uno::Reference< chart2::data::XLabeledDataSequence >& xSeq : aSeqVector)
+ {
+ if ( xSeq.is() )
+ {
+ pDS->AddLabeledSequence( xSeq );
+ }
+ }
+
+ xResult.set( pDS );
+ return xResult;
+}
+
+namespace
+{
+
+/**
+ * Function object to create a list of table numbers from a token list.
+ */
+class InsertTabNumber
+{
+public:
+ InsertTabNumber() :
+ mpTabNumVector(std::make_shared<vector<SCTAB>>())
+ {
+ }
+
+ void operator() (const ScTokenRef& pToken) const
+ {
+ if (!ScRefTokenHelper::isRef(pToken))
+ return;
+
+ const ScSingleRefData& r = *pToken->GetSingleRef();
+ mpTabNumVector->push_back(r.Tab());
+ }
+
+ void getVector(vector<SCTAB>& rVector)
+ {
+ mpTabNumVector->swap(rVector);
+ }
+private:
+ shared_ptr< vector<SCTAB> > mpTabNumVector;
+};
+
+class RangeAnalyzer
+{
+public:
+ RangeAnalyzer();
+ void initRangeAnalyzer( const ScDocument* pDoc, const vector<ScTokenRef>& rTokens );
+ void analyzeRange( sal_Int32& rnDataInRows, sal_Int32& rnDataInCols,
+ bool& rbRowSourceAmbiguous ) const;
+ bool inSameSingleRow( const RangeAnalyzer& rOther );
+ bool inSameSingleColumn( const RangeAnalyzer& rOther );
+ SCROW getRowCount() const { return mnRowCount; }
+ SCCOL getColumnCount() const { return mnColumnCount; }
+
+private:
+ bool mbEmpty;
+ bool mbAmbiguous;
+ SCROW mnRowCount;
+ SCCOL mnColumnCount;
+
+ SCCOL mnStartColumn;
+ SCROW mnStartRow;
+};
+
+RangeAnalyzer::RangeAnalyzer()
+ : mbEmpty(true)
+ , mbAmbiguous(false)
+ , mnRowCount(0)
+ , mnColumnCount(0)
+ , mnStartColumn(-1)
+ , mnStartRow(-1)
+{
+}
+
+void RangeAnalyzer::initRangeAnalyzer( const ScDocument* pDoc, const vector<ScTokenRef>& rTokens )
+{
+ mnRowCount=0;
+ mnColumnCount=0;
+ mnStartColumn = -1;
+ mnStartRow = -1;
+ mbAmbiguous=false;
+ if( rTokens.empty() )
+ {
+ mbEmpty=true;
+ return;
+ }
+ mbEmpty=false;
+
+ for (const ScTokenRef& aRefToken : rTokens)
+ {
+ StackVar eVar = aRefToken->GetType();
+ if (eVar == svDoubleRef || eVar == svExternalDoubleRef)
+ {
+ const ScComplexRefData& r = *aRefToken->GetDoubleRef();
+ if (r.Ref1.Tab() == r.Ref2.Tab())
+ {
+ mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(r.Ref2.Col() - r.Ref1.Col())+1));
+ mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(r.Ref2.Row() - r.Ref1.Row())+1));
+ if( mnStartColumn == -1 )
+ {
+ mnStartColumn = r.Ref1.Col();
+ mnStartRow = r.Ref1.Row();
+ }
+ else
+ {
+ if (mnStartColumn != r.Ref1.Col() && mnStartRow != r.Ref1.Row())
+ mbAmbiguous=true;
+ }
+ }
+ else
+ mbAmbiguous=true;
+ }
+ else if (eVar == svSingleRef || eVar == svExternalSingleRef)
+ {
+ const ScSingleRefData& r = *aRefToken->GetSingleRef();
+ mnColumnCount = std::max<SCCOL>( mnColumnCount, 1);
+ mnRowCount = std::max<SCROW>( mnRowCount, 1);
+ if( mnStartColumn == -1 )
+ {
+ mnStartColumn = r.Col();
+ mnStartRow = r.Row();
+ }
+ else
+ {
+ if (mnStartColumn != r.Col() && mnStartRow != r.Row())
+ mbAmbiguous=true;
+ }
+ }
+ else if (eVar == svIndex && aRefToken->GetOpCode() == ocName)
+ {
+ ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(aRefToken->GetSheet(), aRefToken->GetIndex());
+ ScRange aRange;
+ if (pNameRange->IsReference(aRange))
+ {
+ mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(aRange.aEnd.Col() - aRange.aStart.Col()) + 1));
+ mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(aRange.aEnd.Row() - aRange.aStart.Row()) + 1));
+ if (mnStartColumn == -1)
+ {
+ mnStartColumn = aRange.aStart.Col();
+ mnStartRow = aRange.aStart.Row();
+ }
+ else
+ {
+ if (mnStartColumn != aRange.aStart.Col() && mnStartRow != aRange.aStart.Row())
+ mbAmbiguous = true;
+ }
+ }
+ else
+ mbAmbiguous = true;
+ }
+ else
+ mbAmbiguous=true;
+ }
+}
+
+void RangeAnalyzer::analyzeRange( sal_Int32& rnDataInRows,
+ sal_Int32& rnDataInCols,
+ bool& rbRowSourceAmbiguous ) const
+{
+ if(!mbEmpty && !mbAmbiguous)
+ {
+ if( mnRowCount==1 && mnColumnCount>1 )
+ ++rnDataInRows;
+ else if( mnColumnCount==1 && mnRowCount>1 )
+ ++rnDataInCols;
+ else if( mnRowCount>1 && mnColumnCount>1 )
+ rbRowSourceAmbiguous = true;
+ }
+ else if( !mbEmpty )
+ rbRowSourceAmbiguous = true;
+}
+
+bool RangeAnalyzer::inSameSingleRow( const RangeAnalyzer& rOther )
+{
+ return mnStartRow==rOther.mnStartRow &&
+ mnRowCount==1 && rOther.mnRowCount==1;
+}
+
+bool RangeAnalyzer::inSameSingleColumn( const RangeAnalyzer& rOther )
+{
+ return mnStartColumn==rOther.mnStartColumn &&
+ mnColumnCount==1 && rOther.mnColumnCount==1;
+}
+
+std::pair<OUString, OUString> constructKey(const uno::Reference< chart2::data::XLabeledDataSequence>& xNew)
+{
+ std::pair<OUString, OUString> aKey;
+ if( xNew->getLabel().is() )
+ aKey.first = xNew->getLabel()->getSourceRangeRepresentation();
+ if( xNew->getValues().is() )
+ aKey.second = xNew->getValues()->getSourceRangeRepresentation();
+ return aKey;
+}
+
+
+} //end anonymous namespace
+
+uno::Sequence< beans::PropertyValue > SAL_CALL ScChart2DataProvider::detectArguments(
+ const uno::Reference< chart2::data::XDataSource >& xDataSource )
+{
+ ::std::vector< beans::PropertyValue > aResult;
+ bool bRowSourceDetected = false;
+ bool bFirstCellAsLabel = false;
+ bool bHasCategories = false;
+ OUString sRangeRep;
+
+ bool bHasCategoriesLabels = false;
+ vector<ScTokenRef> aAllCategoriesValuesTokens;
+ vector<ScTokenRef> aAllSeriesLabelTokens;
+
+ chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS;
+
+ vector<ScTokenRef> aAllTokens;
+
+ // parse given data source and collect infos
+ {
+ SolarMutexGuard aGuard;
+ OSL_ENSURE( m_pDocument, "No Document -> no detectArguments" );
+ if(!m_pDocument ||!xDataSource.is())
+ return comphelper::containerToSequence( aResult );
+
+ sal_Int32 nDataInRows = 0;
+ sal_Int32 nDataInCols = 0;
+ bool bRowSourceAmbiguous = false;
+
+ const Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
+ const sal_Int32 nCount( aSequences.getLength());
+ RangeAnalyzer aPrevLabel,aPrevValues;
+ for( const uno::Reference< chart2::data::XLabeledDataSequence >& xLS : aSequences )
+ {
+ if( xLS.is() )
+ {
+ bool bThisIsCategories = false;
+ if(!bHasCategories)
+ {
+ uno::Reference< beans::XPropertySet > xSeqProp( xLS->getValues(), uno::UNO_QUERY );
+ OUString aRole;
+ if( xSeqProp.is() && (xSeqProp->getPropertyValue("Role") >>= aRole) &&
+ aRole == "categories" )
+ bThisIsCategories = bHasCategories = true;
+ }
+
+ RangeAnalyzer aLabel,aValues;
+ // label
+ uno::Reference< chart2::data::XDataSequence > xLabel( xLS->getLabel());
+ if( xLabel.is())
+ {
+ bFirstCellAsLabel = true;
+ vector<ScTokenRef> aTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aTokens, xLabel->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ aLabel.initRangeAnalyzer(m_pDocument, aTokens);
+ for (const auto& rxToken : aTokens)
+ {
+ if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName)
+ {
+ ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex());
+ if (pNameRange->HasReferences())
+ {
+ const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
+ ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress());
+ }
+ else
+ ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
+ }
+ else
+ ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
+ if(!bThisIsCategories)
+ ScRefTokenHelper::join(m_pDocument, aAllSeriesLabelTokens, rxToken, ScAddress());
+ }
+ if(bThisIsCategories)
+ bHasCategoriesLabels=true;
+ }
+ // values
+ uno::Reference< chart2::data::XDataSequence > xValues( xLS->getValues());
+ if( xValues.is())
+ {
+ vector<ScTokenRef> aTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aTokens, xValues->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ aValues.initRangeAnalyzer(m_pDocument, aTokens);
+ for (const auto& rxToken : aTokens)
+ {
+ if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName)
+ {
+ ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex());
+ if (pNameRange->HasReferences())
+ {
+ const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
+ ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress());
+ }
+ else
+ ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
+ }
+ else
+ ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
+ if(bThisIsCategories)
+ ScRefTokenHelper::join(m_pDocument, aAllCategoriesValuesTokens, rxToken, ScAddress());
+ }
+ }
+ //detect row source
+ if(!bThisIsCategories || nCount==1) //categories might span multiple rows *and* columns, so they should be used for detection only if nothing else is available
+ {
+ if (!bRowSourceAmbiguous)
+ {
+ aValues.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
+ aLabel.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
+ if (nDataInRows > 1 && nDataInCols > 1)
+ bRowSourceAmbiguous = true;
+ else if( !bRowSourceAmbiguous && !nDataInRows && !nDataInCols )
+ {
+ if( aValues.inSameSingleColumn( aLabel ) )
+ nDataInCols++;
+ else if( aValues.inSameSingleRow( aLabel ) )
+ nDataInRows++;
+ else
+ {
+ //#i86188# also detect a single column split into rows correctly
+ if( aValues.inSameSingleColumn( aPrevValues ) )
+ nDataInRows++;
+ else if( aValues.inSameSingleRow( aPrevValues ) )
+ nDataInCols++;
+ else if( aLabel.inSameSingleColumn( aPrevLabel ) )
+ nDataInRows++;
+ else if( aLabel.inSameSingleRow( aPrevLabel ) )
+ nDataInCols++;
+ }
+ }
+ }
+ }
+ aPrevValues=aValues;
+ aPrevLabel=aLabel;
+ }
+ }
+
+ if (!bRowSourceAmbiguous)
+ {
+ bRowSourceDetected = true;
+ eRowSource = ( nDataInCols > 0
+ ? chart::ChartDataRowSource_COLUMNS
+ : chart::ChartDataRowSource_ROWS );
+ }
+ else
+ {
+ // set DataRowSource to the better of the two ambiguities
+ eRowSource = ( nDataInRows > nDataInCols
+ ? chart::ChartDataRowSource_ROWS
+ : chart::ChartDataRowSource_COLUMNS );
+ }
+
+ }
+
+ // TableNumberList
+ {
+ vector<SCTAB> aTableNumVector;
+ InsertTabNumber func;
+ func = ::std::for_each(aAllTokens.begin(), aAllTokens.end(), func);
+ func.getVector(aTableNumVector);
+ aResult.emplace_back( "TableNumberList", -1,
+ uno::Any( lcl_createTableNumberList( aTableNumVector ) ),
+ beans::PropertyState_DIRECT_VALUE );
+ }
+
+ if( bRowSourceDetected )
+ {
+ // DataRowSource (calculated before)
+ aResult.emplace_back( "DataRowSource", -1,
+ uno::Any( eRowSource ), beans::PropertyState_DIRECT_VALUE );
+ // HasCategories
+ aResult.emplace_back( "HasCategories", -1,
+ uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE );
+ // FirstCellAsLabel
+ aResult.emplace_back( "FirstCellAsLabel", -1,
+ uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE );
+ }
+
+ // Add the left upper corner to the range if it is missing.
+ if (bRowSourceDetected && bFirstCellAsLabel && bHasCategories && !bHasCategoriesLabels )
+ {
+ RangeAnalyzer aTop,aLeft;
+ if( eRowSource==chart::ChartDataRowSource_COLUMNS )
+ {
+ aTop.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens);
+ aLeft.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens);
+ }
+ else
+ {
+ aTop.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens);
+ aLeft.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens);
+ }
+ lcl_addUpperLeftCornerIfMissing(m_pDocument, aAllTokens, aTop.getRowCount(), aLeft.getColumnCount());//e.g. #i91212#
+ }
+
+ // Get range string.
+ lcl_convertTokensToString(sRangeRep, aAllTokens, *m_pDocument);
+
+ // add cell range property
+ aResult.emplace_back( "CellRangeRepresentation", -1,
+ uno::Any( sRangeRep ), beans::PropertyState_DIRECT_VALUE );
+
+ //Sequence Mapping
+ bool const bSequencesReordered = true;//todo detect this above or detect this sequence mapping cheaper ...
+ if( bSequencesReordered && bRowSourceDetected )
+ {
+ bool bDifferentIndexes = false;
+
+ std::vector< sal_Int32 > aSequenceMappingVector;
+
+ uno::Reference< chart2::data::XDataSource > xCompareDataSource;
+ try
+ {
+ xCompareDataSource.set( createDataSource( comphelper::containerToSequence( aResult ) ) );
+ }
+ catch( const lang::IllegalArgumentException & )
+ {
+ // creation of data source to compare didn't work, so we cannot
+ // create a sequence mapping
+ }
+
+ if( xDataSource.is() && xCompareDataSource.is() )
+ {
+ const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> >& aOldSequences =
+ xCompareDataSource->getDataSequences();
+ const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> >& aNewSequences =
+ xDataSource->getDataSequences();
+
+ std::map<std::pair<OUString, OUString>,sal_Int32> aOldEntryToIndex;
+ for( sal_Int32 nIndex = 0, n = aOldSequences.getLength(); nIndex < n; nIndex++ )
+ {
+ const uno::Reference< chart2::data::XLabeledDataSequence>& xOld( aOldSequences[nIndex] );
+ if( xOld.is() )
+ {
+ std::pair<OUString, OUString> aKey = constructKey(xOld);
+ aOldEntryToIndex[aKey] = nIndex;
+ }
+ }
+
+ for( sal_Int32 nNewIndex = 0, n = aNewSequences.getLength(); nNewIndex < n; nNewIndex++ )
+ {
+ const uno::Reference< chart2::data::XLabeledDataSequence>& xNew( aNewSequences[nNewIndex] );
+ if( !xNew.is() )
+ continue;
+
+ std::pair<OUString, OUString> aKey = constructKey(xNew);
+ if (aOldEntryToIndex.find(aKey) == aOldEntryToIndex.end())
+ continue;
+
+ sal_Int32 nOldIndex = aOldEntryToIndex[aKey];
+ if( nOldIndex != nNewIndex )
+ bDifferentIndexes = true;
+
+ aSequenceMappingVector.push_back(nOldIndex);
+ }
+ }
+
+ if( bDifferentIndexes && !aSequenceMappingVector.empty() )
+ {
+ aResult.emplace_back( "SequenceMapping", -1,
+ uno::Any( comphelper::containerToSequence(aSequenceMappingVector) )
+ , beans::PropertyState_DIRECT_VALUE );
+ }
+ }
+
+ return comphelper::containerToSequence( aResult );
+}
+
+sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& aRangeRepresentation )
+{
+ SolarMutexGuard aGuard;
+ if( ! m_pDocument )
+ return false;
+
+ vector<ScTokenRef> aTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ return !aTokens.empty();
+}
+
+uno::Reference< chart2::data::XDataSequence > SAL_CALL
+ ScChart2DataProvider::createDataSequenceByRangeRepresentation(
+ const OUString& aRangeRepresentation )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< chart2::data::XDataSequence > xResult;
+
+ OSL_ENSURE( m_pDocument, "No Document -> no createDataSequenceByRangeRepresentation" );
+ if(!m_pDocument || aRangeRepresentation.isEmpty())
+ return xResult;
+
+ vector<ScTokenRef> aRefTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ if (aRefTokens.empty())
+ return xResult;
+
+ shrinkToDataRange(m_pDocument, aRefTokens);
+
+ xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells));
+
+ return xResult;
+}
+
+uno::Reference<chart2::data::XDataSequence> SAL_CALL
+ScChart2DataProvider::createDataSequenceByValueArray(
+ const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/,
+ const OUString& /*aRoleQualifier*/ )
+{
+ return uno::Reference<chart2::data::XDataSequence>();
+}
+
+uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::getRangeSelection()
+{
+ uno::Reference< sheet::XRangeSelection > xResult;
+
+ uno::Reference< frame::XModel > xModel( lcl_GetXModel( m_pDocument ));
+ if( xModel.is())
+ xResult.set( xModel->getCurrentController(), uno::UNO_QUERY );
+
+ return xResult;
+}
+
+sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByFormulaTokensPossible(
+ const Sequence<sheet::FormulaToken>& aTokens )
+{
+ if (!aTokens.hasElements())
+ return false;
+
+ ScTokenArray aCode(*m_pDocument);
+ if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
+ return false;
+
+ sal_uInt16 n = aCode.GetLen();
+ if (!n)
+ return false;
+
+ formula::FormulaTokenArrayPlainIterator aIter(aCode);
+ const formula::FormulaToken* pFirst = aIter.First();
+ const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
+ for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ switch (p->GetType())
+ {
+ case svSep:
+ {
+ switch (p->GetOpCode())
+ {
+ case ocSep:
+ // separators are allowed.
+ break;
+ case ocOpen:
+ if (p != pFirst)
+ // open paran is allowed only as the first token.
+ return false;
+ break;
+ case ocClose:
+ if (p != pLast)
+ // close paren is allowed only as the last token.
+ return false;
+ break;
+ default:
+ return false;
+ }
+ }
+ break;
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+uno::Reference<chart2::data::XDataSequence> SAL_CALL
+ScChart2DataProvider::createDataSequenceByFormulaTokens(
+ const Sequence<sheet::FormulaToken>& aTokens )
+{
+ uno::Reference<chart2::data::XDataSequence> xResult;
+ if (!aTokens.hasElements())
+ return xResult;
+
+ ScTokenArray aCode(*m_pDocument);
+ if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
+ return xResult;
+
+ sal_uInt16 n = aCode.GetLen();
+ if (!n)
+ return xResult;
+
+ vector<ScTokenRef> aRefTokens;
+ formula::FormulaTokenArrayPlainIterator aIter(aCode);
+ const formula::FormulaToken* pFirst = aIter.First();
+ const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
+ for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ switch (p->GetType())
+ {
+ case svSep:
+ {
+ switch (p->GetOpCode())
+ {
+ case ocSep:
+ // separators are allowed.
+ break;
+ case ocOpen:
+ if (p != pFirst)
+ // open paran is allowed only as the first token.
+ throw lang::IllegalArgumentException();
+ break;
+ case ocClose:
+ if (p != pLast)
+ // close paren is allowed only as the last token.
+ throw lang::IllegalArgumentException();
+ break;
+ default:
+ throw lang::IllegalArgumentException();
+ }
+ }
+ break;
+ case svIndex:
+ case svString:
+ case svSingleRef:
+ case svDoubleRef:
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ {
+ ScTokenRef pNew(p->Clone());
+ aRefTokens.push_back(pNew);
+ }
+ break;
+ default:
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ if (aRefTokens.empty())
+ return xResult;
+
+ shrinkToDataRange(m_pDocument, aRefTokens);
+
+ xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells));
+ return xResult;
+}
+
+// XRangeXMLConversion ---------------------------------------------------
+
+OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const OUString& sRangeRepresentation )
+{
+ OUString aRet;
+ if (!m_pDocument)
+ return aRet;
+
+ if (sRangeRepresentation.isEmpty())
+ // Empty data range is allowed.
+ return aRet;
+
+ vector<ScTokenRef> aRefTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aRefTokens, sRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
+ if (aRefTokens.empty())
+ {
+ SAL_WARN("sc", "convertRangeToXML throw IllegalArgumentException from input of: " << sRangeRepresentation);
+ throw lang::IllegalArgumentException();
+ }
+
+ Tokens2RangeStringXML converter(*m_pDocument);
+ converter = ::std::for_each(aRefTokens.begin(), aRefTokens.end(), converter);
+ converter.getString(aRet);
+
+ return aRet;
+}
+
+OUString SAL_CALL ScChart2DataProvider::convertRangeFromXML( const OUString& sXMLRange )
+{
+ if (!m_pDocument)
+ {
+ // #i74062# When loading flat XML, this is called before the referenced sheets are in the document,
+ // so the conversion has to take place directly with the strings, without looking up the sheets.
+
+ OUStringBuffer sRet;
+ sal_Int32 nOffset = 0;
+ while( nOffset >= 0 )
+ {
+ OUString sToken;
+ ScRangeStringConverter::GetTokenByOffset( sToken, sXMLRange, nOffset );
+ if( nOffset >= 0 )
+ {
+ // convert one address (remove dots)
+
+ OUString aUIString(sToken);
+
+ sal_Int32 nIndex = ScRangeStringConverter::IndexOf( sToken, ':', 0 );
+ if ( nIndex >= 0 && nIndex < aUIString.getLength() - 1 &&
+ aUIString[nIndex + 1] == '.' )
+ aUIString = aUIString.replaceAt( nIndex + 1, 1, u"" );
+
+ if ( aUIString[0] == '.' )
+ aUIString = aUIString.copy( 1 );
+
+ if( !sRet.isEmpty() )
+ sRet.append( ';' );
+ sRet.append( aUIString );
+ }
+ }
+
+ return sRet.makeStringAndClear();
+ }
+
+ OUString aRet;
+ ScRangeStringConverter::GetStringFromXMLRangeString(aRet, sXMLRange, *m_pDocument);
+ return aRet;
+}
+
+// DataProvider XPropertySet -------------------------------------------------
+
+uno::Reference< beans::XPropertySetInfo> SAL_CALL
+ScChart2DataProvider::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
+ return aRef;
+}
+
+void SAL_CALL ScChart2DataProvider::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ if ( rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS )
+ throw beans::UnknownPropertyException(rPropertyName);
+
+ if ( !(rValue >>= m_bIncludeHiddenCells))
+ throw lang::IllegalArgumentException();
+
+}
+
+uno::Any SAL_CALL ScChart2DataProvider::getPropertyValue(
+ const OUString& rPropertyName)
+{
+ uno::Any aRet;
+ if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
+ aRet <<= m_bIncludeHiddenCells;
+ else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
+ {
+ // This is a read-only property.
+ aRet <<= m_pDocument->PastingDrawFromOtherDoc();
+ }
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+ return aRet;
+}
+
+void SAL_CALL ScChart2DataProvider::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
+{
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void SAL_CALL ScChart2DataProvider::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
+{
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void SAL_CALL ScChart2DataProvider::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
+{
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void SAL_CALL ScChart2DataProvider::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ )
+{
+ OSL_FAIL( "Not yet implemented" );
+}
+
+// DataSource ================================================================
+
+ScChart2DataSource::ScChart2DataSource( ScDocument* pDoc)
+ : m_pDocument( pDoc)
+{
+ if ( m_pDocument )
+ m_pDocument->AddUnoObject( *this);
+}
+
+ScChart2DataSource::~ScChart2DataSource()
+{
+ SolarMutexGuard g;
+
+ if ( m_pDocument )
+ m_pDocument->RemoveUnoObject( *this);
+}
+
+void ScChart2DataSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ m_pDocument = nullptr;
+ }
+}
+
+uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > SAL_CALL
+ScChart2DataSource::getDataSequences()
+{
+ SolarMutexGuard aGuard;
+ return comphelper::containerToSequence(m_aLabeledSequences);
+}
+
+void ScChart2DataSource::AddLabeledSequence(const uno::Reference < chart2::data::XLabeledDataSequence >& xNew)
+{
+ m_aLabeledSequences.push_back(xNew);
+}
+
+// DataSequence ==============================================================
+
+ScChart2DataSequence::Item::Item()
+ : mfValue(std::numeric_limits<double>::quiet_NaN())
+ , mbIsValue(false)
+{
+}
+
+ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence& rParent) :
+ mrParent(rParent)
+{
+}
+
+ScChart2DataSequence::HiddenRangeListener::~HiddenRangeListener()
+{
+}
+
+void ScChart2DataSequence::HiddenRangeListener::notify()
+{
+ mrParent.setDataChangedHint(true);
+}
+
+ScChart2DataSequence::ScChart2DataSequence( ScDocument* pDoc,
+ vector<ScTokenRef>&& rTokens,
+ bool bIncludeHiddenCells )
+ : m_xDataArray(new std::vector<Item>)
+ , m_bIncludeHiddenCells( bIncludeHiddenCells)
+ , m_nObjectId( 0 )
+ , m_pDocument( pDoc)
+ , m_aTokens(std::move(rTokens))
+ , m_aPropSet(lcl_GetDataSequencePropertyMap())
+ , m_bGotDataChangedHint(false)
+ , m_bExtDataRebuildQueued(false)
+ , mbTimeBased(false)
+ , mnTimeBasedStart(0)
+ , mnTimeBasedEnd(0)
+ , mnCurrentTab(0)
+{
+ if ( m_pDocument )
+ {
+ m_pDocument->AddUnoObject( *this);
+ m_nObjectId = m_pDocument->GetNewUnoId();
+ }
+ // FIXME: real implementation of identifier and it's mapping to ranges.
+ // Reuse ScChartListener?
+
+ // BM: don't use names of named ranges but the UI range strings
+// String aStr;
+// rRangeList->Format( aStr, ScRefFlags::RANGE_ABS_3D, m_pDocument );
+// m_aIdentifier = aStr;
+
+// m_aIdentifier = "ID_";
+// static sal_Int32 nID = 0;
+// m_aIdentifier += OUString::valueOf( ++nID);
+}
+
+/** called from Clone() */
+ScChart2DataSequence::ScChart2DataSequence(ScDocument* pDoc, const ScChart2DataSequence& r)
+ : m_xDataArray(r.m_xDataArray)
+ , m_aHiddenValues(r.m_aHiddenValues)
+ , m_aRole(r.m_aRole)
+ , m_bIncludeHiddenCells( r.m_bIncludeHiddenCells)
+ , m_nObjectId( 0 )
+ , m_pDocument( pDoc)
+ , m_aPropSet(lcl_GetDataSequencePropertyMap())
+ , m_bGotDataChangedHint(false)
+ , m_bExtDataRebuildQueued(false)
+ , mbTimeBased(false)
+ , mnTimeBasedStart(0)
+ , mnTimeBasedEnd(0)
+ , mnCurrentTab(0)
+{
+ assert(pDoc);
+
+ // Clone tokens.
+ m_aTokens.reserve(r.m_aTokens.size());
+ for (const auto& rxToken : r.m_aTokens)
+ {
+ ScTokenRef p(rxToken->Clone());
+ m_aTokens.push_back(p);
+ }
+
+ m_pDocument->AddUnoObject( *this);
+ m_nObjectId = m_pDocument->GetNewUnoId();
+
+ if (r.m_oRangeIndices)
+ m_oRangeIndices = *r.m_oRangeIndices;
+
+ if (!r.m_pExtRefListener)
+ return;
+
+ // Re-register all external files that the old instance was
+ // listening to.
+
+ ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
+ m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
+ const std::unordered_set<sal_uInt16>& rFileIds = r.m_pExtRefListener->getAllFileIds();
+ for (const auto& rFileId : rFileIds)
+ {
+ pRefMgr->addLinkListener(rFileId, m_pExtRefListener.get());
+ m_pExtRefListener->addFileId(rFileId);
+ }
+}
+
+ScChart2DataSequence::~ScChart2DataSequence()
+{
+ SolarMutexGuard g;
+
+ if ( m_pDocument )
+ {
+ m_pDocument->RemoveUnoObject( *this);
+ if (m_pHiddenListener)
+ {
+ ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
+ if (pCLC)
+ pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
+ }
+ StopListeningToAllExternalRefs();
+ }
+
+ m_pValueListener.reset();
+}
+
+void ScChart2DataSequence::RefChanged()
+{
+ if( !m_pValueListener || m_aValueListeners.empty() )
+ return;
+
+ m_pValueListener->EndListeningAll();
+
+ if( !m_pDocument )
+ return;
+
+ ScChartListenerCollection* pCLC = nullptr;
+ if (m_pHiddenListener)
+ {
+ pCLC = m_pDocument->GetChartListenerCollection();
+ if (pCLC)
+ pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
+ }
+
+ for (const auto& rxToken : m_aTokens)
+ {
+ ScRange aRange;
+ if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
+ continue;
+
+ m_pDocument->StartListeningArea(aRange, false, m_pValueListener.get());
+ if (pCLC)
+ pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
+ }
+}
+
+void ScChart2DataSequence::BuildDataCache()
+{
+ m_bExtDataRebuildQueued = false;
+
+ if (!m_xDataArray->empty())
+ return;
+
+ StopListeningToAllExternalRefs();
+
+ ::std::vector<sal_Int32> aHiddenValues;
+ sal_Int32 nDataCount = 0;
+
+ for (const auto& rxToken : m_aTokens)
+ {
+ if (ScRefTokenHelper::isExternalRef(rxToken))
+ {
+ nDataCount += FillCacheFromExternalRef(rxToken);
+ }
+ else
+ {
+ ScRange aRange;
+ if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
+ continue;
+
+ SCCOL nLastCol = -1;
+ SCROW nLastRow = -1;
+ for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
+ {
+ for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
+ {
+ sc::ColumnBlockPosition hint;
+ m_pDocument->InitColumnBlockPosition( hint, nTab, nCol );
+ for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
+ {
+ if (nRow == aRange.aEnd.Row())
+ {
+ // Excel behavior: if the last row is the totals row, the data
+ // is not added to the chart. If it's not the last row, the data
+ // is added like normal.
+ const auto* pData = m_pDocument->GetDBAtCursor(
+ nCol, nRow, nTab,
+ ScDBDataPortion::AREA
+ );
+ if (pData && pData->HasTotals())
+ {
+ ScRange aTempRange;
+ pData->GetArea(aTempRange);
+ if (aTempRange.aEnd.Row() == nRow)
+ {
+ // Current row is totals row, skip
+ break;
+ }
+ }
+ }
+ bool bColHidden = m_pDocument->ColHidden(nCol, nTab, nullptr, &nLastCol);
+ bool bRowHidden = m_pDocument->RowHidden(nRow, nTab, nullptr, &nLastRow);
+
+ if (bColHidden || bRowHidden)
+ {
+ // hidden cell
+ aHiddenValues.push_back(nDataCount-1);
+
+ if( !m_bIncludeHiddenCells )
+ continue;
+ }
+
+ Item aItem;
+
+ ScAddress aAdr(nCol, nRow, nTab);
+ aItem.maString = m_pDocument->GetString(aAdr);
+
+ ScRefCellValue aCell(*m_pDocument, aAdr, hint);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ aItem.mfValue = aCell.getValue();
+ aItem.mbIsValue = true;
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ FormulaError nErr = pFCell->GetErrCode();
+ if (nErr != FormulaError::NONE)
+ break;
+
+ if (pFCell->IsValue())
+ {
+ aItem.mfValue = pFCell->GetValue();
+ aItem.mbIsValue = true;
+ }
+ }
+ break;
+ case CELLTYPE_EDIT:
+ case CELLTYPE_NONE:
+ case CELLTYPE_STRING:
+ default:
+ ; // do nothing
+ }
+
+ aItem.mAddress = ScAddress(nCol, nRow, nTab);
+
+ m_xDataArray->push_back(std::move(aItem));
+ ++nDataCount;
+ }
+ }
+ }
+ }
+ }
+
+ // convert the hidden cell list to sequence.
+ m_aHiddenValues.realloc(aHiddenValues.size());
+ std::copy(
+ aHiddenValues.begin(), aHiddenValues.end(), m_aHiddenValues.getArray());
+
+ // Clear the data series cache when the array is re-built.
+ m_aMixedDataCache.realloc(0);
+}
+
+void ScChart2DataSequence::RebuildDataCache()
+{
+ if (!m_bExtDataRebuildQueued)
+ {
+ m_xDataArray.reset(new std::vector<Item>);
+ m_pDocument->BroadcastUno(ScHint(SfxHintId::ScDataChanged, ScAddress()));
+ m_bExtDataRebuildQueued = true;
+ m_bGotDataChangedHint = true;
+ }
+}
+
+sal_Int32 ScChart2DataSequence::FillCacheFromExternalRef(const ScTokenRef& pToken)
+{
+ ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
+ ScRange aRange;
+ if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, pToken, ScAddress(), true))
+ return 0;
+
+ sal_uInt16 nFileId = pToken->GetIndex();
+ OUString aTabName = pToken->GetString().getString();
+ ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, aTabName, aRange, nullptr);
+ if (!pArray)
+ // no external data exists for this range.
+ return 0;
+
+ // Start listening for this external document.
+ ExternalRefListener* pExtRefListener = GetExtRefListener();
+ pRefMgr->addLinkListener(nFileId, pExtRefListener);
+ pExtRefListener->addFileId(nFileId);
+
+ m_xDataArray.reset(new std::vector<Item>);
+ ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, aTabName, false);
+ sal_Int32 nDataCount = 0;
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ // Cached external range is always represented as a single
+ // matrix token, although that might change in the future when
+ // we introduce a new token type to store multi-table range
+ // data.
+
+ if (p->GetType() != svMatrix)
+ {
+ OSL_FAIL("Cached array is not a matrix token.");
+ continue;
+ }
+
+ const ScMatrix* pMat = p->GetMatrix();
+ SCSIZE nCSize, nRSize;
+ pMat->GetDimensions(nCSize, nRSize);
+ for (SCSIZE nC = 0; nC < nCSize; ++nC)
+ {
+ for (SCSIZE nR = 0; nR < nRSize; ++nR)
+ {
+ if (pMat->IsValue(nC, nR) || pMat->IsBoolean(nC, nR))
+ {
+ Item aItem;
+
+ aItem.mbIsValue = true;
+ aItem.mfValue = pMat->GetDouble(nC, nR);
+
+ SvNumberFormatter* pFormatter = m_pDocument->GetFormatTable();
+ if (pFormatter)
+ {
+ const double fVal = aItem.mfValue;
+ const Color* pColor = nullptr;
+ sal_uInt32 nFmt = 0;
+ if (pTable)
+ {
+ // Get the correct format index from the cache.
+ SCCOL nCol = aRange.aStart.Col() + static_cast<SCCOL>(nC);
+ SCROW nRow = aRange.aStart.Row() + static_cast<SCROW>(nR);
+ pTable->getCell(nCol, nRow, &nFmt);
+ }
+ pFormatter->GetOutputString(fVal, nFmt, aItem.maString, &pColor);
+ }
+
+ m_xDataArray->push_back(std::move(aItem));
+ ++nDataCount;
+ }
+ else if (pMat->IsStringOrEmpty(nC, nR))
+ {
+ Item aItem;
+
+ aItem.mbIsValue = false;
+ aItem.maString = pMat->GetString(nC, nR).getString();
+
+ m_xDataArray->push_back(std::move(aItem));
+ ++nDataCount;
+ }
+ }
+ }
+ }
+ return nDataCount;
+}
+
+void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList& rRanges)
+{
+ if (!m_oRangeIndices)
+ return;
+
+ for ( size_t i = 0, nCount = rRanges.size(); i < nCount; ++i )
+ {
+ ScTokenRef pToken;
+ const ScRange & rRange = rRanges[i];
+
+ ScRefTokenHelper::getTokenFromRange(m_pDocument, pToken, rRange);
+ sal_uInt32 nOrigPos = (*m_oRangeIndices)[i];
+ m_aTokens[nOrigPos] = pToken;
+ }
+
+ RefChanged();
+
+ // any change of the range address is broadcast to value (modify) listeners
+ if ( !m_aValueListeners.empty() )
+ m_bGotDataChangedHint = true;
+}
+
+ScChart2DataSequence::ExternalRefListener* ScChart2DataSequence::GetExtRefListener()
+{
+ if (!m_pExtRefListener)
+ m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
+
+ return m_pExtRefListener.get();
+}
+
+void ScChart2DataSequence::StopListeningToAllExternalRefs()
+{
+ if (!m_pExtRefListener)
+ return;
+
+ const std::unordered_set<sal_uInt16>& rFileIds = m_pExtRefListener->getAllFileIds();
+ ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
+ for (const auto& rFileId : rFileIds)
+ pRefMgr->removeLinkListener(rFileId, m_pExtRefListener.get());
+
+ m_pExtRefListener.reset();
+}
+
+
+void ScChart2DataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ // Create a range list from the token list, have the range list
+ // updated, and bring the change back to the token list.
+
+ ScRangeList aRanges;
+ m_oRangeIndices.emplace();
+ vector<ScTokenRef>::const_iterator itrBeg = m_aTokens.begin(), itrEnd = m_aTokens.end();
+ for (vector<ScTokenRef>::const_iterator itr = itrBeg ;itr != itrEnd; ++itr)
+ {
+ if (!ScRefTokenHelper::isExternalRef(*itr))
+ {
+ ScRange aRange;
+ ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, *itr, ScAddress());
+ aRanges.push_back(aRange);
+ sal_uInt32 nPos = distance(itrBeg, itr);
+ m_oRangeIndices->push_back(nPos);
+ }
+ }
+
+ assert(m_oRangeIndices->size() == aRanges.size() &&
+ "range list and range index list have different sizes.");
+
+ unique_ptr<ScRangeList> pUndoRanges;
+ if ( m_pDocument->HasUnoRefUndo() )
+ pUndoRanges.reset(new ScRangeList(aRanges));
+
+ const ScUpdateRefHint& rRef = static_cast<const ScUpdateRefHint&>(rHint);
+ bool bChanged = aRanges.UpdateReference(
+ rRef.GetMode(), m_pDocument, rRef.GetRange(), rRef.GetDx(), rRef.GetDy(), rRef.GetDz());
+
+ if (bChanged)
+ {
+ // TODO: This should be an assert, but tdf#144537 triggers it.
+ SAL_WARN_IF(m_oRangeIndices->size() == aRanges.size(),
+ "sc.ui", "range list and range index list have different sizes after the reference update.");
+
+ // Bring the change back from the range list to the token list.
+ UpdateTokensFromRanges(aRanges);
+
+ if (pUndoRanges)
+ m_pDocument->AddUnoRefChange(m_nObjectId, *pUndoRanges);
+ }
+ }
+ else if ( auto pUndoHint = dynamic_cast<const ScUnoRefUndoHint*>(&rHint) )
+ {
+ do
+ {
+ if (pUndoHint->GetObjectId() != m_nObjectId)
+ break;
+
+ // The hint object provides the old ranges. Restore the old state
+ // from these ranges.
+
+ if (!m_oRangeIndices || m_oRangeIndices->empty())
+ {
+ assert(false && " faulty range indices");
+ break;
+ }
+
+ const ScRangeList& rRanges = pUndoHint->GetRanges();
+
+ size_t nCount = rRanges.size();
+ if (nCount != m_oRangeIndices->size())
+ {
+ assert(false && "range count and range index count differ.");
+ break;
+ }
+
+ UpdateTokensFromRanges(rRanges);
+ }
+ while (false);
+ }
+ else
+ {
+ const SfxHintId nId = rHint.GetId();
+ if ( nId ==SfxHintId::Dying )
+ {
+ m_pDocument = nullptr;
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ // delayed broadcast as in ScCellRangesBase
+
+ if ( m_bGotDataChangedHint && m_pDocument )
+ {
+ m_xDataArray.reset(new std::vector<Item>);
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+
+ if( m_pDocument )
+ {
+ for (const uno::Reference<util::XModifyListener> & xListener: m_aValueListeners)
+ m_pDocument->AddUnoListenerCall( xListener, aEvent );
+ }
+
+ m_bGotDataChangedHint = false;
+ }
+ }
+ else if ( nId == SfxHintId::ScCalcAll )
+ {
+ // broadcast from DoHardRecalc - set m_bGotDataChangedHint
+ // (SfxHintId::DataChanged follows separately)
+
+ if ( !m_aValueListeners.empty() )
+ m_bGotDataChangedHint = true;
+ }
+ else if (nId == SfxHintId::ScClearCache)
+ {
+ // necessary after import
+ m_xDataArray.reset(new std::vector<Item>);
+ }
+ }
+}
+
+IMPL_LINK( ScChart2DataSequence, ValueListenerHdl, const SfxHint&, rHint, void )
+{
+ if ( m_pDocument && (rHint.GetId() == SfxHintId::ScDataChanged) )
+ {
+ // This may be called several times for a single change, if several formulas
+ // in the range are notified. So only a flag is set that is checked when
+ // SfxHintId::DataChanged is received.
+
+ setDataChangedHint(true);
+ }
+}
+
+ScChart2DataSequence::ExternalRefListener::ExternalRefListener(
+ ScChart2DataSequence& rParent, ScDocument* pDoc) :
+ mrParent(rParent),
+ mpDoc(pDoc)
+{
+}
+
+ScChart2DataSequence::ExternalRefListener::~ExternalRefListener()
+{
+ if (!mpDoc || mpDoc->IsInDtorClear())
+ // The document is being destroyed. Do nothing.
+ return;
+
+ // Make sure to remove all pointers to this object.
+ mpDoc->GetExternalRefManager()->removeLinkListener(this);
+}
+
+void ScChart2DataSequence::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
+{
+ switch (eType)
+ {
+ case ScExternalRefManager::LINK_MODIFIED:
+ {
+ if (maFileIds.count(nFileId))
+ // We are listening to this external document.
+ mrParent.RebuildDataCache();
+ }
+ break;
+ case ScExternalRefManager::LINK_BROKEN:
+ maFileIds.erase(nFileId);
+ break;
+ case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE:
+ mpDoc = nullptr;
+ break;
+ }
+}
+
+void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId)
+{
+ maFileIds.insert(nFileId);
+}
+
+uno::Sequence< uno::Any> SAL_CALL ScChart2DataSequence::getData()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pDocument)
+ throw uno::RuntimeException();
+
+ BuildDataCache();
+
+ if (!m_aMixedDataCache.hasElements())
+ {
+ // Build a cache for the 1st time...
+
+ sal_Int32 nCount = m_xDataArray->size();
+ m_aMixedDataCache.realloc(nCount);
+ uno::Any* pArr = m_aMixedDataCache.getArray();
+ for (const Item &rItem : *m_xDataArray)
+ {
+ if (rItem.mbIsValue)
+ *pArr <<= rItem.mfValue;
+ else if (rItem.maString.isEmpty())
+ {
+ ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
+ if (aCell.isEmpty())
+ *pArr = uno::Any();
+ else
+ *pArr <<= rItem.maString;
+ }
+ else
+ *pArr <<= rItem.maString;
+ ++pArr;
+ }
+ }
+ return m_aMixedDataCache;
+}
+
+// XNumericalDataSequence --------------------------------------------------
+
+uno::Sequence< double > SAL_CALL ScChart2DataSequence::getNumericalData()
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pDocument)
+ throw uno::RuntimeException();
+
+ BuildDataCache();
+
+ sal_Int32 nCount = m_xDataArray->size();
+ uno::Sequence<double> aSeq(nCount);
+ double* pArr = aSeq.getArray();
+ for (const Item& rItem : *m_xDataArray)
+ {
+ *pArr = rItem.mbIsValue ? rItem.mfValue : std::numeric_limits<double>::quiet_NaN();
+ ++pArr;
+ }
+
+ return aSeq;
+}
+
+// XTextualDataSequence --------------------------------------------------
+
+uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::getTextualData()
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<OUString> aSeq;
+ if ( !m_pDocument )
+ throw uno::RuntimeException();
+
+ BuildDataCache();
+
+ sal_Int32 nCount = m_xDataArray->size();
+ if ( nCount > 0 )
+ {
+ aSeq = uno::Sequence<OUString>(nCount);
+ OUString* pArr = aSeq.getArray();
+ for (const Item& rItem : *m_xDataArray)
+ {
+ *pArr = rItem.maString;
+ ++pArr;
+ }
+ }
+ else if ( m_aTokens.front() )
+ {
+ if( m_aTokens.front()->GetType() == svString )
+ {
+ aSeq = uno::Sequence<OUString> { m_aTokens.front()->GetString().getString() };
+ }
+ }
+
+ return aSeq;
+}
+
+OUString SAL_CALL ScChart2DataSequence::getSourceRangeRepresentation()
+{
+ SolarMutexGuard aGuard;
+ OUString aStr;
+ OSL_ENSURE( m_pDocument, "No Document -> no SourceRangeRepresentation" );
+ if (m_pDocument)
+ lcl_convertTokensToString(aStr, m_aTokens, *m_pDocument);
+
+ return aStr;
+}
+
+namespace {
+
+/**
+ * This function object is used to accumulatively count the numbers of
+ * columns and rows in all reference tokens.
+ */
+class AccumulateRangeSize
+{
+public:
+ AccumulateRangeSize(const ScDocument* pDoc) :
+ mpDoc(pDoc), mnCols(0), mnRows(0) {}
+
+ void operator() (const ScTokenRef& pToken)
+ {
+ ScRange r;
+ bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
+ ScRefTokenHelper::getRangeFromToken(mpDoc, r, pToken, ScAddress(), bExternal);
+ r.PutInOrder();
+ mnCols += r.aEnd.Col() - r.aStart.Col() + 1;
+ mnRows += r.aEnd.Row() - r.aStart.Row() + 1;
+ }
+
+ SCCOL getCols() const { return mnCols; }
+ SCROW getRows() const { return mnRows; }
+private:
+ const ScDocument* mpDoc;
+ SCCOL mnCols;
+ SCROW mnRows;
+};
+
+/**
+ * This function object is used to generate label strings from a list of
+ * reference tokens.
+ */
+class GenerateLabelStrings
+{
+public:
+ GenerateLabelStrings(const ScDocument* pDoc, sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) :
+ mpDoc(pDoc),
+ mpLabels(std::make_shared<Sequence<OUString>>(nSize)),
+ meOrigin(eOrigin),
+ mnCount(0),
+ mbColumn(bColumn) {}
+
+ void operator() (const ScTokenRef& pToken)
+ {
+ bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
+ ScRange aRange;
+ ScRefTokenHelper::getRangeFromToken(mpDoc, aRange, pToken, ScAddress(), bExternal);
+ OUString* pArr = mpLabels->getArray();
+ if (mbColumn)
+ {
+ for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
+ {
+ if ( meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
+ {
+ OUString aString = ScResId(STR_COLUMN) + " ";
+ ScAddress aPos( nCol, 0, 0 );
+ OUString aColStr(aPos.Format(ScRefFlags::COL_VALID));
+ aString += aColStr;
+ pArr[mnCount] = aString;
+ }
+ else //only indices for categories
+ pArr[mnCount] = OUString::number( mnCount+1 );
+ ++mnCount;
+ }
+ }
+ else
+ {
+ for (sal_Int32 nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
+ {
+ if (meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
+ {
+ OUString aString = ScResId(STR_ROW) +
+ " " + OUString::number( nRow+1 );
+ pArr[mnCount] = aString;
+ }
+ else //only indices for categories
+ pArr[mnCount] = OUString::number( mnCount+1 );
+ ++mnCount;
+ }
+ }
+ }
+
+ const Sequence<OUString>& getLabels() const { return *mpLabels; }
+
+private:
+ const ScDocument* mpDoc;
+ shared_ptr< Sequence<OUString> > mpLabels;
+ chart2::data::LabelOrigin meOrigin;
+ sal_Int32 mnCount;
+ bool mbColumn;
+};
+
+}
+
+uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin)
+{
+ SolarMutexGuard aGuard;
+ if ( !m_pDocument)
+ throw uno::RuntimeException();
+
+ // Determine the total size of all ranges.
+ AccumulateRangeSize func(m_pDocument);
+ func = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), func);
+ SCCOL nCols = func.getCols();
+ SCROW nRows = func.getRows();
+
+ // Determine whether this is column-major or row-major.
+ bool bColumn = true;
+ if ((eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) ||
+ (eOrigin == chart2::data::LabelOrigin_LONG_SIDE))
+ {
+ if (nRows > nCols)
+ {
+ bColumn = eOrigin == chart2::data::LabelOrigin_SHORT_SIDE;
+ }
+ else if (nCols > nRows)
+ {
+ bColumn = eOrigin != chart2::data::LabelOrigin_SHORT_SIDE;
+ }
+ else
+ return Sequence<OUString>();
+ }
+
+ // Generate label strings based on the info so far.
+ sal_Int32 nCount = bColumn ? nCols : nRows;
+ GenerateLabelStrings genLabels(m_pDocument, nCount, eOrigin, bColumn);
+ genLabels = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), genLabels);
+ Sequence<OUString> aSeq = genLabels.getLabels();
+
+ return aSeq;
+}
+
+namespace {
+
+sal_uInt32 getDisplayNumberFormat(const ScDocument* pDoc, const ScAddress& rPos)
+{
+ sal_uInt32 nFormat = pDoc->GetNumberFormat(rPos); // original format from cell.
+ return nFormat;
+}
+
+}
+
+::sal_Int32 SAL_CALL ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ BuildDataCache();
+
+ if (nIndex == -1)
+ {
+ // return format of first non-empty cell
+ // TODO: use nicer heuristic
+ for (const Item& rItem : *m_xDataArray)
+ {
+ ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
+ if (!aCell.isEmpty() && aCell.hasNumeric())
+ {
+ return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, rItem.mAddress));
+ }
+ }
+
+ // we could not find a non-empty cell
+ return 0;
+ }
+
+ if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= m_xDataArray->size())
+ {
+ SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'.");
+ return 0;
+ }
+
+ return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, m_xDataArray->at(nIndex).mAddress));
+}
+
+// XCloneable ================================================================
+
+uno::Reference< util::XCloneable > SAL_CALL ScChart2DataSequence::createClone()
+{
+ SolarMutexGuard aGuard;
+
+ rtl::Reference<ScChart2DataSequence> p(new ScChart2DataSequence(m_pDocument, *this));
+ return p;
+}
+
+// XModifyBroadcaster ========================================================
+
+void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ // like ScCellRangesBase::addModifyListener
+ SolarMutexGuard aGuard;
+ if (m_aTokens.empty())
+ return;
+
+ ScRangeList aRanges;
+ ScRefTokenHelper::getRangeListFromTokens(m_pDocument, aRanges, m_aTokens, ScAddress());
+ m_aValueListeners.emplace_back( aListener );
+
+ if ( m_aValueListeners.size() != 1 )
+ return;
+
+ if (!m_pValueListener)
+ m_pValueListener.reset(new ScLinkListener( LINK( this, ScChart2DataSequence, ValueListenerHdl ) ));
+
+ if (!m_pHiddenListener)
+ m_pHiddenListener.reset(new HiddenRangeListener(*this));
+
+ if( m_pDocument )
+ {
+ ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
+ for (const auto& rxToken : m_aTokens)
+ {
+ ScRange aRange;
+ if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
+ continue;
+
+ m_pDocument->StartListeningArea( aRange, false, m_pValueListener.get() );
+ if (pCLC)
+ pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
+ }
+ }
+
+ acquire(); // don't lose this object (one ref for all listeners)
+}
+
+void SAL_CALL ScChart2DataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
+{
+ // like ScCellRangesBase::removeModifyListener
+
+ SolarMutexGuard aGuard;
+ if (m_aTokens.empty())
+ return;
+
+ rtl::Reference<ScChart2DataSequence> xSelfHold(this); // in case the listeners have the last ref
+
+ sal_uInt16 nCount = m_aValueListeners.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ {
+ uno::Reference<util::XModifyListener>& rObj = m_aValueListeners[n];
+ if ( rObj == aListener )
+ {
+ m_aValueListeners.erase( m_aValueListeners.begin() + n );
+
+ if ( m_aValueListeners.empty() )
+ {
+ if (m_pValueListener)
+ m_pValueListener->EndListeningAll();
+
+ if (m_pHiddenListener && m_pDocument)
+ {
+ ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
+ if (pCLC)
+ pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
+ }
+
+ release(); // release the ref for the listeners
+ }
+
+ break;
+ }
+ }
+}
+
+// DataSequence XPropertySet -------------------------------------------------
+
+uno::Reference< beans::XPropertySetInfo> SAL_CALL
+ScChart2DataSequence::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
+ return aRef;
+}
+
+void SAL_CALL ScChart2DataSequence::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ if ( rPropertyName == SC_UNONAME_ROLE )
+ {
+ if ( !(rValue >>= m_aRole))
+ throw lang::IllegalArgumentException();
+ }
+ else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
+ {
+ bool bOldValue = m_bIncludeHiddenCells;
+ if ( !(rValue >>= m_bIncludeHiddenCells))
+ throw lang::IllegalArgumentException();
+ if( bOldValue != m_bIncludeHiddenCells )
+ m_xDataArray.reset(new std::vector<Item>);//data array is dirty now
+ }
+ else if( rPropertyName == "TimeBased" )
+ {
+ bool bTimeBased = mbTimeBased;
+ rValue>>= bTimeBased;
+ mbTimeBased = bTimeBased;
+ }
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+ // TODO: support optional properties
+}
+
+uno::Any SAL_CALL ScChart2DataSequence::getPropertyValue(const OUString& rPropertyName)
+{
+ uno::Any aRet;
+ if ( rPropertyName == SC_UNONAME_ROLE )
+ aRet <<= m_aRole;
+ else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
+ aRet <<= m_bIncludeHiddenCells;
+ else if ( rPropertyName == SC_UNONAME_HIDDENVALUES )
+ {
+ // This property is read-only thus cannot be set externally via
+ // setPropertyValue(...).
+ BuildDataCache();
+ aRet <<= m_aHiddenValues;
+ }
+ else if (rPropertyName == SC_UNONAME_TIME_BASED)
+ {
+ aRet <<= mbTimeBased;
+ }
+ else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL)
+ {
+ // Read-only property. It returns whether or not the label value is a
+ // direct user input, rather than an indirect reference.
+ bool bHasStringLabel = false;
+ if (m_aTokens.size() == 1)
+ {
+ const formula::FormulaToken& rToken = *m_aTokens[0];
+ bHasStringLabel = rToken.GetType() == formula::svString;
+ }
+ aRet <<= bHasStringLabel;
+ }
+ else
+ throw beans::UnknownPropertyException(rPropertyName);
+ // TODO: support optional properties
+ return aRet;
+}
+
+void SAL_CALL ScChart2DataSequence::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
+{
+ // FIXME: real implementation
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void SAL_CALL ScChart2DataSequence::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
+{
+ // FIXME: real implementation
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void SAL_CALL ScChart2DataSequence::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
+{
+ // FIXME: real implementation
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void SAL_CALL ScChart2DataSequence::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
+{
+ // FIXME: real implementation
+ OSL_FAIL( "Not yet implemented" );
+}
+
+void ScChart2DataSequence::setDataChangedHint(bool b)
+{
+ m_bGotDataChangedHint = b;
+}
+
+sal_Bool ScChart2DataSequence::switchToNext(sal_Bool bWrap)
+{
+ if(!mbTimeBased)
+ return true;
+
+ if(mnCurrentTab >= mnTimeBasedEnd)
+ {
+ if(bWrap)
+ setToPointInTime(0);
+ return false;
+ }
+
+ for(const auto& rxToken : m_aTokens)
+ {
+ if (rxToken->GetType() != svDoubleRef)
+ continue;
+
+ ScComplexRefData& rData = *rxToken->GetDoubleRef();
+ ScSingleRefData& s = rData.Ref1;
+ ScSingleRefData& e = rData.Ref2;
+
+ s.IncTab(1);
+ e.IncTab(1);
+ }
+
+ ++mnCurrentTab;
+
+ RebuildDataCache();
+
+ return true;
+}
+
+void ScChart2DataSequence::setRange(sal_Int32 nStart, sal_Int32 nEnd)
+{
+ mnTimeBasedStart = nStart;
+ mnTimeBasedEnd = nEnd;
+ mnCurrentTab = mnTimeBasedStart;
+}
+
+sal_Bool ScChart2DataSequence::setToPointInTime(sal_Int32 nPoint)
+{
+ if(nPoint > mnTimeBasedEnd - mnTimeBasedStart)
+ return false;
+
+ SCTAB nTab = mnTimeBasedStart + nPoint;
+ for(const auto& rxToken : m_aTokens)
+ {
+ if (rxToken->GetType() != svDoubleRef)
+ continue;
+
+ ScComplexRefData& rData = *rxToken->GetDoubleRef();
+ ScSingleRefData& s = rData.Ref1;
+ ScSingleRefData& e = rData.Ref2;
+
+ s.SetAbsTab(nTab);
+ e.SetAbsTab(nTab);
+ }
+
+ mnCurrentTab = nTab;
+
+ RebuildDataCache();
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/chartuno.cxx b/sc/source/ui/unoobj/chartuno.cxx
new file mode 100644
index 0000000000..aa0554b27f
--- /dev/null
+++ b/sc/source/ui/unoobj/chartuno.cxx
@@ -0,0 +1,739 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+
+#include <osl/diagnose.h>
+#include <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdundo.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <comphelper/classids.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/globname.hxx>
+#include <svtools/embedhlp.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <ChartTools.hxx>
+#include <chartuno.hxx>
+#include <miscuno.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <undodat.hxx>
+#include <chartlis.hxx>
+#include <chart2uno.hxx>
+#include <convuno.hxx>
+
+using namespace css;
+
+#define PROP_HANDLE_RELATED_CELLRANGES 1
+
+SC_SIMPLE_SERVICE_INFO( ScChartObj, "ScChartObj", "com.sun.star.table.TableChart" )
+SC_SIMPLE_SERVICE_INFO( ScChartsObj, "ScChartsObj", "com.sun.star.table.TableCharts" )
+
+ScChartsObj::ScChartsObj(ScDocShell* pDocSh, SCTAB nT) :
+ pDocShell( pDocSh ),
+ nTab( nT )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScChartsObj::~ScChartsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScChartsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! update reference
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+rtl::Reference<ScChartObj> ScChartsObj::GetObjectByIndex_Impl(tools::Long nIndex) const
+{
+ if ( pDocShell )
+ {
+ OUString aName;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (pDrawLayer)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage, "Page not found");
+ if (pPage)
+ {
+ tools::Long nPos = 0;
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject) )
+ {
+ if ( nPos == nIndex )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObject)->GetObjRef();
+ if ( xObj.is() )
+ aName = pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xObj );
+ break; // stop searching
+ }
+ ++nPos;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+
+ if (!aName.isEmpty())
+ return new ScChartObj( pDocShell, nTab, aName );
+ }
+
+ return nullptr;
+}
+
+rtl::Reference<ScChartObj> ScChartsObj::GetObjectByName_Impl(const OUString& aName) const
+{
+ if (sc::tools::findChartsByName(pDocShell, nTab, aName, sc::tools::ChartSourceType::CELL_RANGE))
+ return new ScChartObj( pDocShell, nTab, aName );
+ if (sc::tools::findChartsByName(pDocShell, nTab, aName, sc::tools::ChartSourceType::PIVOT_TABLE))
+ return new ScChartObj( pDocShell, nTab, aName );
+ return nullptr;
+}
+
+// XTableCharts
+
+void SAL_CALL ScChartsObj::addNewByName( const OUString& rName,
+ const awt::Rectangle& aRect,
+ const uno::Sequence<table::CellRangeAddress>& aRanges,
+ sal_Bool bColumnHeaders, sal_Bool bRowHeaders )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pModel = pDocShell->MakeDrawLayer();
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"addChart: no Page");
+ if (!pPage)
+ return;
+
+ // chart can't be inserted if any ole object with that name exists on any table
+ // (empty string: generate valid name)
+
+ OUString aName = rName;
+ SCTAB nDummy;
+ if ( !aName.isEmpty() && pModel->GetNamedObject( aName, SdrObjKind::OLE2, nDummy ) )
+ {
+ // object exists - only RuntimeException is specified
+ throw uno::RuntimeException();
+ }
+
+ ScRangeList* pList = new ScRangeList;
+ for (const table::CellRangeAddress& rRange : aRanges)
+ {
+ ScRange aRange( static_cast<SCCOL>(rRange.StartColumn), rRange.StartRow, rRange.Sheet,
+ static_cast<SCCOL>(rRange.EndColumn), rRange.EndRow, rRange.Sheet );
+ pList->push_back( aRange );
+ }
+ ScRangeListRef xNewRanges( pList );
+
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ if ( SvtModuleOptions().IsChart() )
+ xObj = pDocShell->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aName );
+ if ( !xObj.is() )
+ return;
+
+ // adjust rectangle
+ //! error/exception, if empty/invalid ???
+ Point aRectPos( aRect.X, aRect.Y );
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( ( aRectPos.X() < 0 && !bLayoutRTL ) || ( aRectPos.X() > 0 && bLayoutRTL ) )
+ aRectPos.setX( 0 );
+
+ if (aRectPos.Y() < 0)
+ aRectPos.setY( 0 );
+
+ Size aRectSize( aRect.Width, aRect.Height );
+ if (aRectSize.Width() <= 0)
+ aRectSize.setWidth( 5000 ); // default size
+
+ if (aRectSize.Height() <= 0)
+ aRectSize.setHeight( 5000 );
+ tools::Rectangle aInsRect( aRectPos, aRectSize );
+
+ sal_Int64 nAspect(embed::Aspects::MSOLE_CONTENT);
+ MapUnit aMapUnit(VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ));
+ Size aSize(aInsRect.GetSize());
+ aSize = OutputDevice::LogicToLogic( aSize, MapMode( MapUnit::Map100thMM ), MapMode( aMapUnit ) );
+ awt::Size aSz;
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+
+ // Calc -> DataProvider
+ uno::Reference< chart2::data::XDataProvider > xDataProvider = new
+ ScChart2DataProvider( &rDoc );
+ // Chart -> DataReceiver
+ uno::Reference< chart2::data::XDataReceiver > xReceiver;
+ if( xObj.is())
+ xReceiver.set( xObj->getComponent(), uno::UNO_QUERY );
+ if( xReceiver.is())
+ {
+ // Range in UI representation.
+ OUString sRangeStr;
+ xNewRanges->Format(sRangeStr, ScRefFlags::RANGE_ABS_3D, rDoc, rDoc.GetAddressConvention());
+
+ // connect
+ if( !sRangeStr.isEmpty() )
+ xReceiver->attachDataProvider( xDataProvider );
+ else
+ sRangeStr = "all";
+
+ uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( cppu::getXWeak(pDocShell->GetModel()), uno::UNO_QUERY );
+ xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier );
+
+ // set arguments
+ uno::Sequence< beans::PropertyValue > aArgs{
+ beans::PropertyValue(
+ "CellRangeRepresentation", -1,
+ uno::Any( sRangeStr ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "HasCategories", -1,
+ uno::Any( bRowHeaders ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "FirstCellAsLabel", -1,
+ uno::Any( bColumnHeaders ), beans::PropertyState_DIRECT_VALUE ),
+ beans::PropertyValue(
+ "DataRowSource", -1,
+ uno::Any( chart::ChartDataRowSource_COLUMNS ), beans::PropertyState_DIRECT_VALUE )
+ };
+ xReceiver->setArguments( aArgs );
+ }
+
+ ScChartListener* pChartListener =
+ new ScChartListener( aName, rDoc, xNewRanges );
+ rDoc.GetChartListenerCollection()->insert( pChartListener );
+ pChartListener->StartListeningTo();
+
+ rtl::Reference<SdrOle2Obj> pObj = new SdrOle2Obj(
+ *pModel,
+ ::svt::EmbeddedObjectRef(xObj, embed::Aspects::MSOLE_CONTENT),
+ aName,
+ aInsRect);
+
+ // set VisArea
+ if( xObj.is())
+ xObj->setVisualAreaSize( nAspect, aSz );
+
+ // #i121334# This call will change the chart's default background fill from white to transparent.
+ // Add here again if this is wanted (see task description for details)
+ // ChartHelper::AdaptDefaultsForChart( xObj );
+
+ pPage->InsertObject( pObj.get() );
+ pModel->AddUndo( std::make_unique<SdrUndoInsertObj>( *pObj ) );
+}
+
+void SAL_CALL ScChartsObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ SdrOle2Obj* pObj = sc::tools::findChartsByName(pDocShell, nTab, aName, sc::tools::ChartSourceType::CELL_RANGE);
+ if (pObj)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.GetChartListenerCollection()->removeByName(aName);
+ ScDrawLayer* pModel = rDoc.GetDrawLayer(); // is not zero
+ SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab)); // is not zero
+
+ pModel->AddUndo( std::make_unique<SdrUndoDelObj>( *pObj ) );
+ pPage->RemoveObject( pObj->GetOrdNum() );
+
+ //! Notify etc.???
+ }
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScChartsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.table.TableChartsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScChartsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nCount = 0;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (pDrawLayer)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage, "Page not found");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject) )
+ ++nCount;
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+ return nCount;
+}
+
+uno::Any SAL_CALL ScChartsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XTableChart> xChart(GetObjectByIndex_Impl(nIndex));
+ if (!xChart.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xChart);
+}
+
+uno::Type SAL_CALL ScChartsObj::getElementType()
+{
+ return cppu::UnoType<table::XTableChart>::get();
+}
+
+sal_Bool SAL_CALL ScChartsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return getCount() != 0;
+}
+
+uno::Any SAL_CALL ScChartsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XTableChart> xChart(GetObjectByName_Impl(aName));
+ if (!xChart.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xChart);
+}
+
+uno::Sequence<OUString> SAL_CALL ScChartsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ tools::Long nCount = getCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+
+ tools::Long nPos = 0;
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (pDrawLayer)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage, "Page not found");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject) )
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObject)->GetObjRef();
+ if ( xObj.is() )
+ aName = pDocShell->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xObj );
+
+ OSL_ENSURE(nPos<nCount, "oops, miscounted?");
+ pAry[nPos++] = aName;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ OSL_ENSURE(nPos==nCount, "hey, miscounted?");
+
+ return aSeq;
+ }
+ return {};
+}
+
+sal_Bool SAL_CALL ScChartsObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ SdrOle2Obj* aOle2Obj = sc::tools::findChartsByName(pDocShell, nTab, aName,
+ sc::tools::ChartSourceType::CELL_RANGE);
+ return aOle2Obj != nullptr;
+}
+
+ScChartObj::ScChartObj(ScDocShell* pDocSh, SCTAB nT, OUString aN)
+ :ScChartObj_Base( m_aMutex )
+ ,ScChartObj_PBase( ScChartObj_Base::rBHelper )
+ ,pDocShell( pDocSh )
+ ,nTab( nT )
+ ,aChartName(std::move( aN ))
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+
+ registerPropertyNoMember( "RelatedCellRanges",
+ PROP_HANDLE_RELATED_CELLRANGES, beans::PropertyAttribute::MAYBEVOID,
+ cppu::UnoType<uno::Sequence<table::CellRangeAddress>>::get(),
+ css::uno::Any(uno::Sequence<table::CellRangeAddress>()) );
+}
+
+ScChartObj::~ScChartObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScChartObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! update reference
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+void ScChartObj::GetData_Impl( ScRangeListRef& rRanges, bool& rColHeaders, bool& rRowHeaders ) const
+{
+ bool bFound = false;
+
+ if( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ uno::Reference< chart2::XChartDocument > xChartDoc( rDoc.GetChartByName( aChartName ) );
+ if( xChartDoc.is() )
+ {
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY );
+ uno::Reference< chart2::data::XDataProvider > xProvider = xChartDoc->getDataProvider();
+ if( xReceiver.is() && xProvider.is() )
+ {
+ const uno::Sequence< beans::PropertyValue > aArgs( xProvider->detectArguments( xReceiver->getUsedData() ) );
+
+ OUString aRanges;
+ chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS;
+ bool bHasCategories=false;
+ bool bFirstCellAsLabel=false;
+ for (const beans::PropertyValue& rProp : aArgs)
+ {
+ OUString aPropName(rProp.Name);
+
+ if (aPropName == "CellRangeRepresentation")
+ rProp.Value >>= aRanges;
+ else if (aPropName == "DataRowSource")
+ eDataRowSource = static_cast<chart::ChartDataRowSource>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ));
+ else if (aPropName == "HasCategories")
+ bHasCategories = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == "FirstCellAsLabel")
+ bFirstCellAsLabel = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ }
+
+ if( chart::ChartDataRowSource_COLUMNS == eDataRowSource )
+ {
+ rColHeaders=bFirstCellAsLabel;
+ rRowHeaders=bHasCategories;
+ }
+ else
+ {
+ rColHeaders=bHasCategories;
+ rRowHeaders=bFirstCellAsLabel;
+ }
+ // Range in UI representation.
+ rRanges->Parse( aRanges, rDoc, rDoc.GetAddressConvention());
+ }
+ bFound = true;
+ }
+ }
+ if( !bFound )
+ {
+ rRanges = nullptr;
+ rColHeaders = false;
+ rRowHeaders = false;
+ }
+}
+
+void ScChartObj::Update_Impl( const ScRangeListRef& rRanges, bool bColHeaders, bool bRowHeaders )
+{
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoChartData>( pDocShell, aChartName, rRanges, bColHeaders, bRowHeaders, false ) );
+ }
+ rDoc.UpdateChartArea( aChartName, rRanges, bColHeaders, bRowHeaders, false );
+ }
+}
+
+// ::comphelper::OPropertySetHelper
+
+::cppu::IPropertyArrayHelper& ScChartObj::getInfoHelper()
+{
+ return *ScChartObj_PABase::getArrayHelper();
+}
+
+void ScChartObj::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const uno::Any& rValue )
+{
+ switch ( nHandle )
+ {
+ case PROP_HANDLE_RELATED_CELLRANGES:
+ {
+ uno::Sequence< table::CellRangeAddress > aCellRanges;
+ if ( rValue >>= aCellRanges )
+ {
+ ScRangeListRef rRangeList = new ScRangeList();
+ for ( table::CellRangeAddress const & aCellRange : std::as_const(aCellRanges) )
+ {
+ ScRange aRange;
+ ScUnoConversion::FillScRange( aRange, aCellRange );
+ rRangeList->push_back( aRange );
+ }
+ if ( pDocShell )
+ {
+ ScChartListenerCollection* pCollection = pDocShell->GetDocument().GetChartListenerCollection();
+ if ( pCollection )
+ {
+ pCollection->ChangeListening( aChartName, rRangeList );
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void ScChartObj::getFastPropertyValue( uno::Any& rValue, sal_Int32 nHandle ) const
+{
+ switch ( nHandle )
+ {
+ case PROP_HANDLE_RELATED_CELLRANGES:
+ {
+ if (!pDocShell)
+ break;
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScChartListenerCollection* pCollection = rDoc.GetChartListenerCollection();
+ if (!pCollection)
+ break;
+
+ ScChartListener* pListener = pCollection->findByName(aChartName);
+ if (!pListener)
+ break;
+
+ const ScRangeListRef& rRangeList = pListener->GetRangeList();
+ if (!rRangeList.is())
+ break;
+
+ size_t nCount = rRangeList->size();
+ uno::Sequence<table::CellRangeAddress> aCellRanges(nCount);
+ table::CellRangeAddress* pCellRanges = aCellRanges.getArray();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ ScRange const & rRange = (*rRangeList)[i];
+ table::CellRangeAddress aCellRange;
+ ScUnoConversion::FillApiRange(aCellRange, rRange);
+ pCellRanges[i] = aCellRange;
+ }
+ rValue <<= aCellRanges;
+ }
+ break;
+ default:
+ ;
+ }
+}
+
+// ::comphelper::OPropertyArrayUsageHelper
+
+::cppu::IPropertyArrayHelper* ScChartObj::createArrayHelper() const
+{
+ uno::Sequence< beans::Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+}
+
+// XInterface
+
+IMPLEMENT_FORWARD_XINTERFACE2( ScChartObj, ScChartObj_Base, ScChartObj_PBase )
+
+// XTypeProvider
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( ScChartObj, ScChartObj_Base, ScChartObj_PBase )
+
+// XTableChart
+
+sal_Bool SAL_CALL ScChartObj::getHasColumnHeaders()
+{
+ SolarMutexGuard aGuard;
+ ScRangeListRef xRanges = new ScRangeList;
+ bool bColHeaders, bRowHeaders;
+ GetData_Impl( xRanges, bColHeaders, bRowHeaders );
+ return bColHeaders;
+}
+
+void SAL_CALL ScChartObj::setHasColumnHeaders( sal_Bool bHasColumnHeaders )
+{
+ SolarMutexGuard aGuard;
+ ScRangeListRef xRanges = new ScRangeList;
+ bool bOldColHeaders, bOldRowHeaders;
+ GetData_Impl( xRanges, bOldColHeaders, bOldRowHeaders );
+ if ( bOldColHeaders != bool(bHasColumnHeaders) )
+ Update_Impl( xRanges, bHasColumnHeaders, bOldRowHeaders );
+}
+
+sal_Bool SAL_CALL ScChartObj::getHasRowHeaders()
+{
+ SolarMutexGuard aGuard;
+ ScRangeListRef xRanges = new ScRangeList;
+ bool bColHeaders, bRowHeaders;
+ GetData_Impl( xRanges, bColHeaders, bRowHeaders );
+ return bRowHeaders;
+}
+
+void SAL_CALL ScChartObj::setHasRowHeaders( sal_Bool bHasRowHeaders )
+{
+ SolarMutexGuard aGuard;
+ ScRangeListRef xRanges = new ScRangeList;
+ bool bOldColHeaders, bOldRowHeaders;
+ GetData_Impl( xRanges, bOldColHeaders, bOldRowHeaders );
+ if ( bOldRowHeaders != bool(bHasRowHeaders) )
+ Update_Impl( xRanges, bOldColHeaders, bHasRowHeaders );
+}
+
+uno::Sequence<table::CellRangeAddress> SAL_CALL ScChartObj::getRanges()
+{
+ SolarMutexGuard aGuard;
+ ScRangeListRef xRanges = new ScRangeList;
+ bool bColHeaders, bRowHeaders;
+ GetData_Impl( xRanges, bColHeaders, bRowHeaders );
+ if ( xRanges.is() )
+ {
+ size_t nCount = xRanges->size();
+
+ table::CellRangeAddress aRangeAddress;
+ uno::Sequence<table::CellRangeAddress> aSeq(nCount);
+ table::CellRangeAddress* pAry = aSeq.getArray();
+ for (size_t i = 0; i < nCount; i++)
+ {
+ ScRange const & rRange = (*xRanges)[i];
+
+ aRangeAddress.Sheet = rRange.aStart.Tab();
+ aRangeAddress.StartColumn = rRange.aStart.Col();
+ aRangeAddress.StartRow = rRange.aStart.Row();
+ aRangeAddress.EndColumn = rRange.aEnd.Col();
+ aRangeAddress.EndRow = rRange.aEnd.Row();
+
+ pAry[i] = aRangeAddress;
+ }
+ return aSeq;
+ }
+
+ OSL_FAIL("ScChartObj::getRanges: no Ranges");
+ return uno::Sequence<table::CellRangeAddress>();
+}
+
+void SAL_CALL ScChartObj::setRanges( const uno::Sequence<table::CellRangeAddress>& aRanges )
+{
+ SolarMutexGuard aGuard;
+ ScRangeListRef xOldRanges = new ScRangeList;
+ bool bColHeaders, bRowHeaders;
+ GetData_Impl( xOldRanges, bColHeaders, bRowHeaders );
+
+ ScRangeList* pList = new ScRangeList;
+ for (const table::CellRangeAddress& rRange : aRanges)
+ {
+ ScRange aRange( static_cast<SCCOL>(rRange.StartColumn), rRange.StartRow, rRange.Sheet,
+ static_cast<SCCOL>(rRange.EndColumn), rRange.EndRow, rRange.Sheet );
+ pList->push_back( aRange );
+ }
+ ScRangeListRef xNewRanges( pList );
+
+ if ( !xOldRanges.is() || *xOldRanges != *xNewRanges )
+ Update_Impl( xNewRanges, bColHeaders, bRowHeaders );
+}
+
+// XEmbeddedObjectSupplier
+
+uno::Reference<lang::XComponent> SAL_CALL ScChartObj::getEmbeddedObject()
+{
+ SolarMutexGuard aGuard;
+ SdrOle2Obj* pObject = sc::tools::findChartsByName(pDocShell, nTab, aChartName,
+ sc::tools::ChartSourceType::CELL_RANGE);
+ if ( pObject && svt::EmbeddedObjectRef::TryRunningState( pObject->GetObjRef() ) )
+ {
+ //TODO/LATER: is it OK that something is returned for *all* objects, not only own objects?
+ return uno::Reference < lang::XComponent > ( pObject->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ }
+
+ return nullptr;
+}
+
+// XNamed
+
+OUString SAL_CALL ScChartObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return aChartName;
+}
+
+void SAL_CALL ScChartObj::setName( const OUString& /* aName */ )
+{
+ throw uno::RuntimeException(); // name cannot be changed
+}
+
+// XPropertySet
+
+uno::Reference< beans::XPropertySetInfo > ScChartObj::getPropertySetInfo()
+{
+ return createPropertySetInfo( getInfoHelper() ) ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/condformatuno.cxx b/sc/source/ui/unoobj/condformatuno.cxx
new file mode 100644
index 0000000000..945752a3c5
--- /dev/null
+++ b/sc/source/ui/unoobj/condformatuno.cxx
@@ -0,0 +1,1899 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <algorithm>
+#include <memory>
+#include <condformatuno.hxx>
+
+#include <document.hxx>
+#include <conditio.hxx>
+#include <colorscale.hxx>
+#include <docsh.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+
+#include <cellsuno.hxx>
+#include <convuno.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/sheet/DataBarAxis.hpp>
+#include <com/sun/star/sheet/IconSetType.hpp>
+#include <com/sun/star/sheet/ConditionFormatOperator.hpp>
+#include <com/sun/star/sheet/DataBarEntryType.hpp>
+#include <com/sun/star/sheet/ColorScaleEntryType.hpp>
+#include <com/sun/star/sheet/IconSetFormatEntry.hpp>
+#include <com/sun/star/sheet/ConditionEntryType.hpp>
+#include <com/sun/star/sheet/DateType.hpp>
+
+namespace {
+
+enum CondFormatProperties
+{
+ ID,
+ CondFormat_Range
+};
+
+std::span<const SfxItemPropertyMapEntry> getCondFormatPropset()
+{
+ static const SfxItemPropertyMapEntry aCondFormatPropertyMap_Impl[] =
+ {
+ {u"ID"_ustr, ID, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ {u"Range"_ustr, CondFormat_Range, cppu::UnoType<sheet::XSheetCellRanges>::get(), 0, 0},
+ };
+ return aCondFormatPropertyMap_Impl;
+}
+
+enum ConditionEntryProperties
+{
+ StyleName,
+ Formula1,
+ Formula2,
+ Operator
+};
+
+std::span<const SfxItemPropertyMapEntry> getConditionEntryrPropSet()
+{
+ static const SfxItemPropertyMapEntry aConditionEntryPropertyMap_Impl[] =
+ {
+ {u"StyleName"_ustr, StyleName, cppu::UnoType<OUString>::get(), 0, 0},
+ {u"Formula1"_ustr, Formula1, cppu::UnoType<OUString>::get(), 0, 0},
+ {u"Formula2"_ustr, Formula2, cppu::UnoType<OUString>::get(), 0, 0},
+ {u"Operator"_ustr, Operator, cppu::UnoType<decltype(sheet::ConditionFormatOperator::EQUAL)>::get(), 0, 0 },
+ };
+ return aConditionEntryPropertyMap_Impl;
+}
+
+struct ConditionEntryApiMap
+{
+ ScConditionMode eMode;
+ sal_Int32 nApiMode;
+};
+
+ConditionEntryApiMap const aConditionEntryMap[] =
+{
+ {ScConditionMode::Equal, sheet::ConditionFormatOperator::EQUAL},
+ {ScConditionMode::Less, sheet::ConditionFormatOperator::LESS},
+ {ScConditionMode::Greater, sheet::ConditionFormatOperator::GREATER},
+ {ScConditionMode::EqLess, sheet::ConditionFormatOperator::LESS_EQUAL},
+ {ScConditionMode::EqGreater, sheet::ConditionFormatOperator::GREATER_EQUAL},
+ {ScConditionMode::NotEqual, sheet::ConditionFormatOperator::NOT_EQUAL},
+ {ScConditionMode::Between, sheet::ConditionFormatOperator::BETWEEN},
+ {ScConditionMode::NotBetween, sheet::ConditionFormatOperator::NOT_BETWEEN},
+ {ScConditionMode::Duplicate, sheet::ConditionFormatOperator::DUPLICATE},
+ {ScConditionMode::NotDuplicate, sheet::ConditionFormatOperator::UNIQUE},
+ {ScConditionMode::Direct, sheet::ConditionFormatOperator::EXPRESSION},
+ {ScConditionMode::Top10, sheet::ConditionFormatOperator::TOP_N_ELEMENTS},
+ {ScConditionMode::Bottom10, sheet::ConditionFormatOperator::BOTTOM_N_ELEMENTS},
+ {ScConditionMode::TopPercent, sheet::ConditionFormatOperator::TOP_N_PERCENT},
+ {ScConditionMode::BottomPercent, sheet::ConditionFormatOperator::BOTTOM_N_PERCENT},
+ {ScConditionMode::AboveAverage, sheet::ConditionFormatOperator::ABOVE_AVERAGE},
+ {ScConditionMode::BelowAverage, sheet::ConditionFormatOperator::BELOW_AVERAGE},
+ {ScConditionMode::AboveEqualAverage, sheet::ConditionFormatOperator::ABOVE_EQUAL_AVERAGE},
+ {ScConditionMode::BelowEqualAverage, sheet::ConditionFormatOperator::BELOW_EQUAL_AVERAGE},
+ {ScConditionMode::Error, sheet::ConditionFormatOperator::ERROR},
+ {ScConditionMode::NoError, sheet::ConditionFormatOperator::NO_ERROR},
+ {ScConditionMode::BeginsWith, sheet::ConditionFormatOperator::BEGINS_WITH},
+ {ScConditionMode::EndsWith, sheet::ConditionFormatOperator::ENDS_WITH},
+ {ScConditionMode::ContainsText, sheet::ConditionFormatOperator::CONTAINS},
+ {ScConditionMode::NotContainsText, sheet::ConditionFormatOperator::NOT_CONTAINS},
+ {ScConditionMode::NONE, sheet::ConditionFormatOperator::EQUAL},
+};
+
+enum ColorScaleProperties
+{
+ ColorScaleEntries
+};
+
+std::span<const SfxItemPropertyMapEntry> getColorScalePropSet()
+{
+ static const SfxItemPropertyMapEntry aColorScalePropertyMap_Impl[] =
+ {
+ {u"ColorScaleEntries"_ustr, ColorScaleEntries, cppu::UnoType<uno::Sequence< sheet::XColorScaleEntry >>::get(), 0, 0 },
+ };
+ return aColorScalePropertyMap_Impl;
+}
+
+struct ColorScaleEntryTypeApiMap
+{
+ ScColorScaleEntryType eType;
+ sal_Int32 nApiType;
+};
+
+ColorScaleEntryTypeApiMap const aColorScaleEntryTypeMap[] =
+{
+ { COLORSCALE_MIN, sheet::ColorScaleEntryType::COLORSCALE_MIN },
+ { COLORSCALE_MAX, sheet::ColorScaleEntryType::COLORSCALE_MAX },
+ { COLORSCALE_VALUE, sheet::ColorScaleEntryType::COLORSCALE_VALUE },
+ { COLORSCALE_FORMULA, sheet::ColorScaleEntryType::COLORSCALE_FORMULA },
+ { COLORSCALE_PERCENT, sheet::ColorScaleEntryType::COLORSCALE_PERCENT },
+ { COLORSCALE_PERCENTILE, sheet::ColorScaleEntryType::COLORSCALE_PERCENTILE }
+};
+
+enum DataBarProperties
+{
+ AxisPosition,
+ UseGradient,
+ UseNegativeColor,
+ DataBar_ShowValue,
+ DataBar_Color,
+ AxisColor,
+ NegativeColor,
+ DataBarEntries,
+ MinimumLength,
+ MaximumLength
+};
+
+std::span<const SfxItemPropertyMapEntry> getDataBarPropSet()
+{
+ static const SfxItemPropertyMapEntry aDataBarPropertyMap_Impl[] =
+ {
+ {u"AxisPosition"_ustr, AxisPosition, cppu::UnoType<decltype(sheet::DataBarAxis::AXIS_AUTOMATIC)>::get(), 0, 0 },
+ {u"UseGradient"_ustr, UseGradient, cppu::UnoType<bool>::get(), 0, 0 },
+ {u"UseNegativeColor"_ustr, UseNegativeColor, cppu::UnoType<bool>::get(), 0, 0 },
+ {u"ShowValue"_ustr, DataBar_ShowValue, cppu::UnoType<bool>::get(), 0, 0 },
+ {u"Color"_ustr, DataBar_Color, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ {u"AxisColor"_ustr, AxisColor, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ {u"NegativeColor"_ustr, NegativeColor, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ {u"DataBarEntries"_ustr, DataBarEntries, cppu::UnoType<uno::Sequence< sheet::XDataBarEntry >>::get(), 0, 0 },
+ {u"MinimumLength"_ustr, MinimumLength, cppu::UnoType<double>::get(), 0, 0 },
+ {u"MaximumLength"_ustr, MaximumLength, cppu::UnoType<double>::get(), 0, 0 },
+ };
+ return aDataBarPropertyMap_Impl;
+}
+
+struct DataBarAxisApiMap
+{
+ databar::ScAxisPosition ePos;
+ sal_Int32 nApiPos;
+};
+
+DataBarAxisApiMap const aDataBarAxisMap[] =
+{
+ { databar::NONE, sheet::DataBarAxis::AXIS_NONE },
+ { databar::AUTOMATIC, sheet::DataBarAxis::AXIS_AUTOMATIC },
+ { databar::MIDDLE, sheet::DataBarAxis::AXIS_MIDDLE }
+};
+
+struct DataBarEntryTypeApiMap
+{
+ ScColorScaleEntryType eType;
+ sal_Int32 nApiType;
+};
+
+DataBarEntryTypeApiMap const aDataBarEntryTypeMap[] =
+{
+ { COLORSCALE_AUTO, sheet::DataBarEntryType::DATABAR_AUTO },
+ { COLORSCALE_MIN, sheet::DataBarEntryType::DATABAR_MIN },
+ { COLORSCALE_MAX, sheet::DataBarEntryType::DATABAR_MAX },
+ { COLORSCALE_VALUE, sheet::DataBarEntryType::DATABAR_VALUE },
+ { COLORSCALE_FORMULA, sheet::DataBarEntryType::DATABAR_FORMULA },
+ { COLORSCALE_PERCENT, sheet::DataBarEntryType::DATABAR_PERCENT },
+ { COLORSCALE_PERCENTILE, sheet::DataBarEntryType::DATABAR_PERCENTILE }
+};
+
+enum IconSetProperties
+{
+ Icons,
+ Reverse,
+ ShowValue,
+ IconSetEntries
+};
+
+std::span<const SfxItemPropertyMapEntry> getIconSetPropSet()
+{
+ static const SfxItemPropertyMapEntry aIconSetPropertyMap_Impl[] =
+ {
+ {u"Icons"_ustr, Icons, cppu::UnoType<decltype(sheet::IconSetType::ICONSET_3SYMBOLS)>::get(), 0, 0 },
+ {u"Reverse"_ustr, Reverse, cppu::UnoType<bool>::get(), 0, 0 },
+ {u"ShowValue"_ustr, ShowValue, cppu::UnoType<bool>::get(), 0, 0 },
+ {u"IconSetEntries"_ustr, IconSetEntries, cppu::UnoType<uno::Sequence< sheet::XIconSetEntry >>::get(), 0, 0 },
+ };
+ return aIconSetPropertyMap_Impl;
+}
+
+struct IconSetTypeApiMap
+{
+ ScIconSetType eType;
+ sal_Int32 nApiType;
+};
+
+const IconSetTypeApiMap aIconSetApiMap[] =
+{
+ { IconSet_3Arrows, sheet::IconSetType::ICONSET_3ARROWS },
+ { IconSet_3ArrowsGray, sheet::IconSetType::ICONSET_3ARROWS_GRAY },
+ { IconSet_3Flags, sheet::IconSetType::ICONSET_3FLAGS },
+ { IconSet_3TrafficLights1, sheet::IconSetType::ICONSET_3TRAFFICLIGHTS1 },
+ { IconSet_3TrafficLights2, sheet::IconSetType::ICONSET_3TRAFFICLIGHTS2 },
+ { IconSet_3Signs, sheet::IconSetType::ICONSET_3SIGNS },
+ { IconSet_3Symbols, sheet::IconSetType::ICONSET_3SYMBOLS },
+ { IconSet_3Symbols2, sheet::IconSetType::ICONSET_3SYMBOLS2 },
+ { IconSet_3Smilies, sheet::IconSetType::ICONSET_3SMILIES },
+ { IconSet_3ColorSmilies, sheet::IconSetType::ICONSET_3COLOR_SIMILIES },
+ { IconSet_4Arrows, sheet::IconSetType::ICONSET_4ARROWS },
+ { IconSet_4ArrowsGray, sheet::IconSetType::ICONSET_4ARROWS_GRAY },
+ { IconSet_4Rating, sheet::IconSetType::ICONSET_4RATING },
+ { IconSet_4RedToBlack, sheet::IconSetType::ICONSET_4RED_TO_BLACK },
+ { IconSet_4TrafficLights, sheet::IconSetType::ICONSET_4TRAFFICLIGHTS },
+ { IconSet_5Arrows, sheet::IconSetType::ICONSET_5ARROWS },
+ { IconSet_5ArrowsGray, sheet::IconSetType::ICONSET_4ARROWS_GRAY },
+ { IconSet_5Ratings, sheet::IconSetType::ICONSET_5RATINGS },
+ { IconSet_5Quarters, sheet::IconSetType::ICONSET_5QUARTERS },
+};
+
+struct IconSetEntryTypeApiMap
+{
+ ScColorScaleEntryType eType;
+ sal_Int32 nApiType;
+};
+
+IconSetEntryTypeApiMap const aIconSetEntryTypeMap[] =
+{
+ { COLORSCALE_MIN, sheet::IconSetFormatEntry::ICONSET_MIN },
+ { COLORSCALE_VALUE, sheet::IconSetFormatEntry::ICONSET_VALUE },
+ { COLORSCALE_FORMULA, sheet::IconSetFormatEntry::ICONSET_FORMULA },
+ { COLORSCALE_PERCENT, sheet::IconSetFormatEntry::ICONSET_PERCENT },
+ { COLORSCALE_PERCENTILE, sheet::IconSetFormatEntry::ICONSET_PERCENTILE }
+};
+
+enum DateProperties
+{
+ Date_StyleName,
+ DateType
+};
+
+std::span<const SfxItemPropertyMapEntry> getCondDatePropSet()
+{
+ static const SfxItemPropertyMapEntry aCondDatePropertyMap_Impl[] =
+ {
+ {u"StyleName"_ustr, StyleName, cppu::UnoType<OUString>::get(), 0, 0},
+ {u"DateType"_ustr, Icons, cppu::UnoType<decltype(sheet::DateType::TODAY)>::get(), 0, 0 },
+ };
+ return aCondDatePropertyMap_Impl;
+}
+
+struct DateTypeApiMap
+{
+ condformat::ScCondFormatDateType eType;
+ sal_Int32 nApiType;
+};
+
+DateTypeApiMap const aDateTypeApiMap[] =
+{
+ { condformat::TODAY, sheet::DateType::TODAY },
+ { condformat::YESTERDAY, sheet::DateType::YESTERDAY },
+ { condformat::TOMORROW, sheet::DateType::TOMORROW },
+ { condformat::LAST7DAYS, sheet::DateType::LAST7DAYS },
+ { condformat::THISWEEK, sheet::DateType::THISWEEK },
+ { condformat::LASTWEEK, sheet::DateType::LASTWEEK },
+ { condformat::NEXTWEEK, sheet::DateType::NEXTWEEK },
+ { condformat::THISMONTH, sheet::DateType::THISMONTH },
+ { condformat::LASTMONTH, sheet::DateType::LASTMONTH },
+ { condformat::NEXTMONTH, sheet::DateType::NEXTMONTH },
+ { condformat::THISYEAR, sheet::DateType::THISYEAR },
+ { condformat::LASTYEAR, sheet::DateType::LASTYEAR },
+ { condformat::NEXTYEAR, sheet::DateType::NEXTYEAR }
+};
+
+}
+
+ScCondFormatsObj::ScCondFormatsObj(ScDocShell* pDocShell, SCTAB nTab):
+ mnTab(nTab),
+ mpDocShell(pDocShell)
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScCondFormatsObj::~ScCondFormatsObj()
+{
+ if (mpDocShell)
+ mpDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScCondFormatsObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpDocShell = nullptr;
+ }
+}
+
+sal_Int32 ScCondFormatsObj::createByRange(const uno::Reference< sheet::XSheetCellRanges >& xRanges)
+{
+ SolarMutexGuard aGuard;
+ if (!mpDocShell)
+ throw lang::IllegalArgumentException();
+
+ if (!xRanges.is())
+ throw lang::IllegalArgumentException();
+
+ const uno::Sequence<table::CellRangeAddress> aRanges =
+ xRanges->getRangeAddresses();
+
+ ScRangeList aCoreRange;
+ for (const auto& rRange : aRanges)
+ {
+ ScRange aRange;
+ ScUnoConversion::FillScRange(aRange, rRange);
+ aCoreRange.Join(aRange);
+ }
+
+ if (aCoreRange.empty())
+ throw lang::IllegalArgumentException();
+
+ SCTAB nTab = aCoreRange[0].aStart.Tab();
+
+ auto pNewFormat = std::make_unique<ScConditionalFormat>(0, &mpDocShell->GetDocument());
+ pNewFormat->SetRange(aCoreRange);
+ return mpDocShell->GetDocument().AddCondFormat(std::move(pNewFormat), nTab);
+}
+
+void ScCondFormatsObj::removeByID(const sal_Int32 nID)
+{
+ SolarMutexGuard aGuard;
+ ScConditionalFormatList* pFormatList = getCoreObject();
+ pFormatList->erase(nID);
+}
+
+uno::Sequence<uno::Reference<sheet::XConditionalFormat> > ScCondFormatsObj::getConditionalFormats()
+{
+ SolarMutexGuard aGuard;
+ ScConditionalFormatList* pFormatList = getCoreObject();
+ size_t n = pFormatList->size();
+ uno::Sequence<uno::Reference<sheet::XConditionalFormat> > aCondFormats(n);
+ std::transform(pFormatList->begin(), pFormatList->end(), aCondFormats.getArray(),
+ [this](const auto& rFormat)
+ { return uno::Reference(new ScCondFormatObj(mpDocShell, this, rFormat->GetKey())); });
+
+ return aCondFormats;
+}
+
+sal_Int32 ScCondFormatsObj::getLength()
+{
+ SolarMutexGuard aGuard;
+ ScConditionalFormatList* pFormatList = getCoreObject();
+ return pFormatList->size();
+}
+
+ScConditionalFormatList* ScCondFormatsObj::getCoreObject()
+{
+ if (!mpDocShell)
+ throw uno::RuntimeException();
+
+ ScConditionalFormatList* pList = mpDocShell->GetDocument().GetCondFormList(mnTab);
+ if (!pList)
+ throw uno::RuntimeException();
+
+ return pList;
+}
+
+namespace {
+
+uno::Reference<beans::XPropertySet> createConditionEntry(const ScFormatEntry* pEntry,
+ rtl::Reference<ScCondFormatObj> const & xParent)
+{
+ switch (pEntry->GetType())
+ {
+ case ScFormatEntry::Type::Condition:
+ case ScFormatEntry::Type::ExtCondition:
+ return new ScConditionEntryObj(xParent,
+ static_cast<const ScCondFormatEntry*>(pEntry));
+ break;
+ case ScFormatEntry::Type::Colorscale:
+ return new ScColorScaleFormatObj(xParent,
+ static_cast<const ScColorScaleFormat*>(pEntry));
+ break;
+ case ScFormatEntry::Type::Databar:
+ return new ScDataBarFormatObj(xParent,
+ static_cast<const ScDataBarFormat*>(pEntry));
+ break;
+ case ScFormatEntry::Type::Iconset:
+ return new ScIconSetFormatObj(xParent,
+ static_cast<const ScIconSetFormat*>(pEntry));
+ break;
+ case ScFormatEntry::Type::Date:
+ return new ScCondDateFormatObj(xParent,
+ static_cast<const ScCondDateFormatEntry*>(pEntry));
+ break;
+ default:
+ break;
+ }
+ return uno::Reference<beans::XPropertySet>();
+}
+
+}
+
+ScCondFormatObj::ScCondFormatObj(ScDocShell* pDocShell, rtl::Reference<ScCondFormatsObj> xCondFormats,
+ sal_Int32 nKey):
+ mxCondFormatList(std::move(xCondFormats)),
+ mpDocShell(pDocShell),
+ maPropSet(getCondFormatPropset()),
+ mnKey(nKey)
+{
+}
+
+ScCondFormatObj::~ScCondFormatObj()
+{
+}
+
+ScConditionalFormat* ScCondFormatObj::getCoreObject()
+{
+ ScConditionalFormatList* pList = mxCondFormatList->getCoreObject();
+ ScConditionalFormat* pFormat = pList->GetFormat(mnKey);
+ if (!pFormat)
+ throw uno::RuntimeException();
+
+ return pFormat;
+}
+
+ScDocShell* ScCondFormatObj::getDocShell()
+{
+ return mpDocShell;
+}
+
+void ScCondFormatObj::createEntry(const sal_Int32 nType, const sal_Int32 nPos)
+{
+ SolarMutexGuard aGuard;
+ ScConditionalFormat* pFormat = getCoreObject();
+ if (nPos > sal_Int32(pFormat->size()))
+ throw lang::IllegalArgumentException();
+
+ ScFormatEntry* pNewEntry = nullptr;
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ switch (nType)
+ {
+ case sheet::ConditionEntryType::CONDITION:
+ pNewEntry = new ScCondFormatEntry(ScConditionMode::Equal, "", "",
+ rDoc, pFormat->GetRange().GetTopLeftCorner(), "");
+ break;
+ case sheet::ConditionEntryType::COLORSCALE:
+ pNewEntry = new ScColorScaleFormat(&rDoc);
+ static_cast<ScColorScaleFormat*>(pNewEntry)->EnsureSize();
+ break;
+ case sheet::ConditionEntryType::DATABAR:
+ pNewEntry = new ScDataBarFormat(&rDoc);
+ static_cast<ScDataBarFormat*>(pNewEntry)->EnsureSize();
+ break;
+ case sheet::ConditionEntryType::ICONSET:
+ pNewEntry = new ScIconSetFormat(&rDoc);
+ static_cast<ScIconSetFormat*>(pNewEntry)->EnsureSize();
+ break;
+ case sheet::ConditionEntryType::DATE:
+ pNewEntry = new ScCondDateFormatEntry(&rDoc);
+ break;
+ default:
+ SAL_WARN("sc", "unknown conditional format type");
+ throw lang::IllegalArgumentException();
+ }
+
+ pFormat->AddEntry(pNewEntry);
+}
+
+void ScCondFormatObj::removeByIndex(const sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if (getCoreObject()->size() >= o3tl::make_unsigned(nIndex))
+ throw lang::IllegalArgumentException();
+
+ getCoreObject()->RemoveEntry(nIndex);
+}
+
+uno::Type ScCondFormatObj::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+}
+
+sal_Bool ScCondFormatObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ ScConditionalFormat* pFormat = getCoreObject();
+ return !pFormat->IsEmpty();
+}
+
+sal_Int32 ScCondFormatObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ ScConditionalFormat* pFormat = getCoreObject();
+
+ return pFormat->size();
+}
+
+uno::Any ScCondFormatObj::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if (getCoreObject()->size() <= o3tl::make_unsigned(nIndex))
+ throw lang::IllegalArgumentException();
+
+ const ScFormatEntry* pEntry = getCoreObject()->GetEntry(nIndex);
+ uno::Reference<beans::XPropertySet> xCondEntry =
+ createConditionEntry(pEntry, this);
+ return uno::Any(xCondEntry);
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCondFormatObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap()));
+ return aRef;
+}
+
+void SAL_CALL ScCondFormatObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch(pEntry->nWID)
+ {
+ case ID:
+ throw lang::IllegalArgumentException();
+ break;
+ case CondFormat_Range:
+ {
+ uno::Reference<sheet::XSheetCellRanges> xRange;
+ if (aValue >>= xRange)
+ {
+ ScConditionalFormat* pFormat = getCoreObject();
+ const uno::Sequence<table::CellRangeAddress> aRanges =
+ xRange->getRangeAddresses();
+ ScRangeList aTargetRange;
+ for (const auto& rRange : aRanges)
+ {
+ ScRange aRange;
+ ScUnoConversion::FillScRange(aRange, rRange);
+ aTargetRange.Join(aRange);
+ }
+ pFormat->SetRange(aTargetRange);
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unknown property");
+ }
+}
+
+uno::Any SAL_CALL ScCondFormatObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+ switch(pEntry->nWID)
+ {
+ case ID:
+ aAny <<= sal_Int32(getCoreObject()->GetKey());
+ break;
+ case CondFormat_Range:
+ {
+ const ScRangeList& rRange = getCoreObject()->GetRange();
+ uno::Reference<sheet::XSheetCellRanges> xRange;
+ xRange.set(new ScCellRangesObj(mpDocShell, rRange));
+ aAny <<= xRange;
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unknown property");
+ }
+ return aAny;
+}
+
+void SAL_CALL ScCondFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScCondFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScCondFormatObj::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScCondFormatObj::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+namespace {
+
+bool isObjectStillAlive(const ScConditionalFormat* pFormat, const ScFormatEntry* pEntry)
+{
+ for(size_t i = 0, n= pFormat->size(); i < n; ++i)
+ {
+ if (pFormat->GetEntry(i) == pEntry)
+ return true;
+ }
+ return false;
+}
+
+}
+
+ScConditionEntryObj::ScConditionEntryObj(rtl::Reference<ScCondFormatObj> const & xParent,
+ const ScCondFormatEntry* pFormat):
+ mpDocShell(xParent->getDocShell()),
+ mxParent(xParent),
+ maPropSet(getConditionEntryrPropSet()),
+ mpFormat(pFormat)
+{
+}
+
+ScConditionEntryObj::~ScConditionEntryObj()
+{
+}
+
+ScCondFormatEntry* ScConditionEntryObj::getCoreObject()
+{
+ ScConditionalFormat* pFormat = mxParent->getCoreObject();
+ if (isObjectStillAlive(pFormat, mpFormat))
+ return const_cast<ScCondFormatEntry*>(mpFormat);
+
+ throw lang::IllegalArgumentException();
+}
+
+sal_Int32 ScConditionEntryObj::getType()
+{
+ return sheet::ConditionEntryType::CONDITION;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScConditionEntryObj::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScConditionEntryObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch(pEntry->nWID)
+ {
+ case StyleName:
+ {
+ OUString aStyleName;
+ if ((aValue >>= aStyleName) && !aStyleName.isEmpty())
+ getCoreObject()->UpdateStyleName(aStyleName);
+ }
+ break;
+ case Formula1:
+ {
+ OUString aFormula;
+ if ((aValue >>= aFormula) && !aFormula.isEmpty())
+ {
+ ScCompiler aComp(mpDocShell->GetDocument(), getCoreObject()->GetSrcPos());
+ aComp.SetGrammar(mpDocShell->GetDocument().GetGrammar());
+ std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aFormula));
+ getCoreObject()->SetFormula1(*pArr);
+ }
+ }
+ break;
+ case Formula2:
+ {
+ OUString aFormula;
+ if ((aValue >>= aFormula) && !aFormula.isEmpty())
+ {
+ ScCompiler aComp(mpDocShell->GetDocument(), getCoreObject()->GetSrcPos());
+ aComp.SetGrammar(mpDocShell->GetDocument().GetGrammar());
+ std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aFormula));
+ getCoreObject()->SetFormula2(*pArr);
+ }
+ }
+ break;
+ case Operator:
+ {
+ sal_Int32 nVal;
+ if (aValue >>= nVal)
+ {
+ for (ConditionEntryApiMap const & rEntry : aConditionEntryMap)
+ {
+ if (rEntry.nApiMode == nVal)
+ {
+ getCoreObject()->SetOperation(rEntry.eMode);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unsupported property");
+ }
+}
+
+uno::Any SAL_CALL ScConditionEntryObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+ switch(pEntry->nWID)
+ {
+ case StyleName:
+ aAny <<= getCoreObject()->GetStyle();
+ break;
+ case Formula1:
+ {
+ ScAddress aCursor = getCoreObject()->GetSrcPos();
+ OUString aFormula = getCoreObject()->GetExpression(aCursor, 0);
+ aAny <<= aFormula;
+ }
+ break;
+ case Formula2:
+ {
+ ScAddress aCursor = getCoreObject()->GetSrcPos();
+ OUString aFormula = getCoreObject()->GetExpression(aCursor, 1);
+ aAny <<= aFormula;
+ }
+ break;
+ case Operator:
+ {
+ ScConditionMode eMode = getCoreObject()->GetOperation();
+ for (ConditionEntryApiMap const & rEntry : aConditionEntryMap)
+ {
+ if (rEntry.eMode == eMode)
+ {
+ aAny <<= rEntry.nApiMode;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unsupported property");
+ }
+ return aAny;
+}
+
+void SAL_CALL ScConditionEntryObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScConditionEntryObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScConditionEntryObj::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScConditionEntryObj::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+ScColorScaleFormatObj::ScColorScaleFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScColorScaleFormat* pFormat):
+ mxParent(std::move(xParent)),
+ maPropSet(getColorScalePropSet()),
+ mpFormat(pFormat)
+{
+}
+
+ScColorScaleFormatObj::~ScColorScaleFormatObj()
+{
+}
+
+ScColorScaleFormat* ScColorScaleFormatObj::getCoreObject()
+{
+ ScConditionalFormat* pFormat = mxParent->getCoreObject();
+ if (isObjectStillAlive(pFormat, mpFormat))
+ return const_cast<ScColorScaleFormat*>(mpFormat);
+
+ throw lang::IllegalArgumentException();
+}
+
+sal_Int32 ScColorScaleFormatObj::getType()
+{
+ return sheet::ConditionEntryType::COLORSCALE;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScColorScaleFormatObj::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+namespace {
+
+void setColorScaleEntry(ScColorScaleEntry* pEntry, uno::Reference<sheet::XColorScaleEntry> const & xEntry)
+{
+ ScColorScaleEntryType eType = ScColorScaleEntryType();
+ sal_Int32 nApiType = xEntry->getType();
+ bool bFound = false;
+ for (ColorScaleEntryTypeApiMap const & rEntry : aColorScaleEntryTypeMap)
+ {
+ if (rEntry.nApiType == nApiType)
+ {
+ eType = rEntry.eType;
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ throw lang::IllegalArgumentException();
+
+ pEntry->SetType(eType);
+ pEntry->SetColor(Color(ColorTransparency, xEntry->getColor()));
+ switch (eType)
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ break;
+ default:
+ {
+ double nVal = xEntry->getFormula().toDouble();
+ pEntry->SetValue(nVal);
+ }
+ break;
+ }
+}
+
+}
+
+void SAL_CALL ScColorScaleFormatObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch(pEntry->nWID)
+ {
+ case ColorScaleEntries:
+ {
+ uno::Sequence<uno::Reference<sheet::XColorScaleEntry> > aEntries;
+ if (!(aValue >>= aEntries))
+ throw lang::IllegalArgumentException();
+
+ if (aEntries.getLength() < 2)
+ throw lang::IllegalArgumentException();
+
+ // TODO: we need to make sure that there are enough entries
+ size_t n = size_t(aEntries.getLength());
+ for (size_t i = 0; i < n; ++i)
+ {
+ setColorScaleEntry(getCoreObject()->GetEntry(i), aEntries[i]);
+ }
+
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unknown property");
+ }
+}
+
+uno::Any SAL_CALL ScColorScaleFormatObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+
+ switch(pEntry->nWID)
+ {
+ case ColorScaleEntries:
+ {
+ uno::Sequence<uno::Reference<sheet::XColorScaleEntry> > aEntries(getCoreObject()->size());
+ auto aEntriesRange = asNonConstRange(aEntries);
+ for (size_t i = 0; i < getCoreObject()->size(); ++i)
+ {
+ aEntriesRange[i] = new ScColorScaleEntryObj(this, i);
+ }
+ aAny <<= aEntries;
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unknown property");
+ }
+
+ return aAny;
+}
+
+void SAL_CALL ScColorScaleFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScColorScaleFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScColorScaleFormatObj::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScColorScaleFormatObj::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+ScColorScaleEntryObj::ScColorScaleEntryObj(rtl::Reference<ScColorScaleFormatObj> xParent,
+ size_t nPos):
+ mxParent(std::move(xParent)),
+ mnPos(nPos)
+{
+}
+
+ScColorScaleEntryObj::~ScColorScaleEntryObj()
+{
+}
+
+ScColorScaleEntry* ScColorScaleEntryObj::getCoreObject()
+{
+ ScColorScaleFormat* pFormat = mxParent->getCoreObject();
+ if (pFormat->size() <= mnPos)
+ throw lang::IllegalArgumentException();
+
+ return pFormat->GetEntry(mnPos);
+}
+
+sal_Int32 ScColorScaleEntryObj::getColor()
+{
+ Color aColor = getCoreObject()->GetColor();
+ return sal_Int32(aColor);
+}
+
+void ScColorScaleEntryObj::setColor(sal_Int32 aColor)
+{
+ getCoreObject()->SetColor(Color(ColorTransparency, aColor));
+}
+
+sal_Int32 ScColorScaleEntryObj::getType()
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ for (ColorScaleEntryTypeApiMap const & rEntry : aColorScaleEntryTypeMap)
+ {
+ if (rEntry.eType == pEntry->GetType())
+ {
+ return rEntry.nApiType;
+ }
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+void ScColorScaleEntryObj::setType(sal_Int32 nType)
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ for (ColorScaleEntryTypeApiMap const & rEntry : aColorScaleEntryTypeMap)
+ {
+ if (rEntry.nApiType == nType)
+ {
+ pEntry->SetType(rEntry.eType);
+ return;
+ }
+ }
+ throw lang::IllegalArgumentException();
+}
+
+OUString ScColorScaleEntryObj::getFormula()
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ switch (pEntry->GetType())
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ break;
+ default:
+ return OUString::number(pEntry->GetValue());
+ }
+
+ return OUString();
+}
+
+void ScColorScaleEntryObj::setFormula(const OUString& rFormula)
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ switch (pEntry->GetType())
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ // pEntry->SetFormula(rFormula);
+ break;
+ default:
+ pEntry->SetValue(rFormula.toDouble());
+ break;
+ }
+}
+
+
+ScDataBarFormatObj::ScDataBarFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScDataBarFormat* pFormat):
+ mxParent(std::move(xParent)),
+ maPropSet(getDataBarPropSet()),
+ mpFormat(pFormat)
+{
+}
+
+ScDataBarFormatObj::~ScDataBarFormatObj()
+{
+}
+
+ScDataBarFormat* ScDataBarFormatObj::getCoreObject()
+{
+ ScConditionalFormat* pFormat = mxParent->getCoreObject();
+ if (isObjectStillAlive(pFormat, mpFormat))
+ return const_cast<ScDataBarFormat*>(mpFormat);
+
+ throw lang::IllegalArgumentException();
+}
+
+sal_Int32 ScDataBarFormatObj::getType()
+{
+ return sheet::ConditionEntryType::DATABAR;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDataBarFormatObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+namespace {
+
+void setDataBarEntry(ScColorScaleEntry* pEntry, uno::Reference<sheet::XDataBarEntry> const & xEntry)
+{
+ ScColorScaleEntryType eType = ScColorScaleEntryType();
+ sal_Int32 nApiType = xEntry->getType();
+ bool bFound = false;
+ for (DataBarEntryTypeApiMap const & rEntry : aDataBarEntryTypeMap)
+ {
+ if (rEntry.nApiType == nApiType)
+ {
+ eType = rEntry.eType;
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ throw lang::IllegalArgumentException();
+
+ pEntry->SetType(eType);
+ switch (eType)
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ break;
+ default:
+ {
+ double nVal = xEntry->getFormula().toDouble();
+ pEntry->SetValue(nVal);
+ }
+ break;
+ }
+}
+
+}
+
+void SAL_CALL ScDataBarFormatObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch(pEntry->nWID)
+ {
+ case AxisPosition:
+ {
+ sal_Int32 nVal;
+ if (aValue >>= nVal)
+ {
+ for (DataBarAxisApiMap const & rEntry : aDataBarAxisMap)
+ {
+ if (rEntry.nApiPos == nVal)
+ {
+ getCoreObject()->GetDataBarData()->meAxisPosition =
+ rEntry.ePos;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case UseGradient:
+ {
+ bool bUseGradient = true;
+ if (aValue >>= bUseGradient)
+ {
+ getCoreObject()->GetDataBarData()->mbGradient = bUseGradient;
+ }
+ }
+ break;
+ case UseNegativeColor:
+ {
+ bool bUseNegativeColor = false;
+ if (aValue >>= bUseNegativeColor)
+ {
+ getCoreObject()->GetDataBarData()->mbNeg = bUseNegativeColor;
+ if (bUseNegativeColor && !getCoreObject()->GetDataBarData()->mxNegativeColor)
+ {
+ getCoreObject()->GetDataBarData()->mxNegativeColor = COL_AUTO;
+ }
+ }
+ }
+ break;
+ case DataBar_ShowValue:
+ {
+ bool bShowValue = true;
+ if (aValue >>= bShowValue)
+ {
+ getCoreObject()->GetDataBarData()->mbOnlyBar = !bShowValue;
+ }
+ }
+ break;
+ case DataBar_Color:
+ {
+ Color nColor = COL_AUTO;
+ if (aValue >>= nColor)
+ {
+ getCoreObject()->GetDataBarData()->maPositiveColor = nColor;
+ }
+ }
+ break;
+ case AxisColor:
+ {
+ Color nAxisColor = COL_AUTO;
+ if (aValue >>= nAxisColor)
+ {
+ getCoreObject()->GetDataBarData()->maAxisColor = nAxisColor;
+ }
+ }
+ break;
+ case NegativeColor:
+ {
+ Color nNegativeColor = COL_AUTO;
+ if (!(aValue >>= nNegativeColor) || !getCoreObject()->GetDataBarData()->mbNeg)
+ throw lang::IllegalArgumentException();
+
+ getCoreObject()->GetDataBarData()->mxNegativeColor = nNegativeColor;
+
+ }
+ break;
+ case DataBarEntries:
+ {
+ uno::Sequence<uno::Reference<sheet::XDataBarEntry> > aEntries;
+ if (!(aValue >>= aEntries))
+ throw lang::IllegalArgumentException();
+
+ if (aEntries.getLength() != 2)
+ throw lang::IllegalArgumentException();
+
+ setDataBarEntry(getCoreObject()->GetDataBarData()->mpLowerLimit.get(),
+ aEntries[0]);
+ setDataBarEntry(getCoreObject()->GetDataBarData()->mpUpperLimit.get(),
+ aEntries[1]);
+
+ }
+ break;
+ case MinimumLength:
+ {
+ double nLength = 0;
+ if (!(aValue >>= nLength) || nLength >= 100 || nLength < 0)
+ throw lang::IllegalArgumentException();
+ getCoreObject()->GetDataBarData()->mnMinLength = nLength;
+
+ }
+ break;
+ case MaximumLength:
+ {
+ double nLength = 0;
+ if (!(aValue >>= nLength) || nLength > 100 || nLength <= 0)
+ throw lang::IllegalArgumentException();
+ getCoreObject()->GetDataBarData()->mnMaxLength = nLength;
+
+ }
+ break;
+ }
+}
+
+uno::Any SAL_CALL ScDataBarFormatObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+ switch(pEntry->nWID)
+ {
+ case AxisPosition:
+ {
+ databar::ScAxisPosition ePos = getCoreObject()->GetDataBarData()->meAxisPosition;
+ sal_Int32 nApiPos = sheet::DataBarAxis::AXIS_NONE;
+ for (DataBarAxisApiMap const & rEntry : aDataBarAxisMap)
+ {
+ if (rEntry.ePos == ePos)
+ {
+ nApiPos = rEntry.nApiPos;
+ }
+ }
+
+ aAny <<= nApiPos;
+ }
+ break;
+ case UseGradient:
+ {
+ aAny <<= getCoreObject()->GetDataBarData()->mbGradient;
+ }
+ break;
+ case UseNegativeColor:
+ {
+ aAny <<= getCoreObject()->GetDataBarData()->mbNeg;
+ }
+ break;
+ case DataBar_ShowValue:
+ {
+ aAny <<= !getCoreObject()->GetDataBarData()->mbOnlyBar;
+ }
+ break;
+ case DataBar_Color:
+ {
+ aAny <<= getCoreObject()->GetDataBarData()->maPositiveColor;
+ }
+ break;
+ case AxisColor:
+ {
+ aAny <<= getCoreObject()->GetDataBarData()->maAxisColor;
+ }
+ break;
+ case NegativeColor:
+ {
+ if (getCoreObject()->GetDataBarData()->mbNeg && getCoreObject()->GetDataBarData()->mxNegativeColor)
+ {
+ aAny <<= *getCoreObject()->GetDataBarData()->mxNegativeColor;
+ }
+ }
+ break;
+ case DataBarEntries:
+ {
+ uno::Sequence<uno::Reference<sheet::XDataBarEntry> > aEntries
+ {
+ new ScDataBarEntryObj(this, 0),
+ new ScDataBarEntryObj(this, 1)
+ };
+ aAny <<= aEntries;
+ }
+ break;
+ }
+ return aAny;
+}
+
+void SAL_CALL ScDataBarFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScDataBarFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScDataBarFormatObj::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScDataBarFormatObj::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+ScDataBarEntryObj::ScDataBarEntryObj(rtl::Reference<ScDataBarFormatObj> xParent,
+ size_t nPos):
+ mxParent(std::move(xParent)),
+ mnPos(nPos)
+{
+}
+
+ScDataBarEntryObj::~ScDataBarEntryObj()
+{
+}
+
+ScColorScaleEntry* ScDataBarEntryObj::getCoreObject()
+{
+ ScDataBarFormat* pFormat = mxParent->getCoreObject();
+ ScColorScaleEntry* pEntry;
+ if (mnPos == 0)
+ pEntry = pFormat->GetDataBarData()->mpLowerLimit.get();
+ else
+ pEntry = pFormat->GetDataBarData()->mpUpperLimit.get();
+
+ return pEntry;
+}
+
+sal_Int32 ScDataBarEntryObj::getType()
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ for (DataBarEntryTypeApiMap const & rEntry : aDataBarEntryTypeMap)
+ {
+ if (rEntry.eType == pEntry->GetType())
+ {
+ return rEntry.nApiType;
+ }
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+void ScDataBarEntryObj::setType(sal_Int32 nType)
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ for (DataBarEntryTypeApiMap const & rEntry : aDataBarEntryTypeMap)
+ {
+ if (rEntry.nApiType == nType)
+ {
+ pEntry->SetType(rEntry.eType);
+ return;
+ }
+ }
+ throw lang::IllegalArgumentException();
+}
+
+OUString ScDataBarEntryObj::getFormula()
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ switch (pEntry->GetType())
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ break;
+ default:
+ return OUString::number(pEntry->GetValue());
+ }
+
+ return OUString();
+}
+
+void ScDataBarEntryObj::setFormula(const OUString& rFormula)
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ switch (pEntry->GetType())
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ // pEntry->SetFormula(rFormula);
+ break;
+ default:
+ pEntry->SetValue(rFormula.toDouble());
+ break;
+ }
+}
+
+
+ScIconSetFormatObj::ScIconSetFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScIconSetFormat* pFormat):
+ mxParent(std::move(xParent)),
+ maPropSet(getIconSetPropSet()),
+ mpFormat(pFormat)
+{
+}
+
+ScIconSetFormatObj::~ScIconSetFormatObj()
+{
+}
+
+ScIconSetFormat* ScIconSetFormatObj::getCoreObject()
+{
+ ScConditionalFormat* pFormat = mxParent->getCoreObject();
+ if (isObjectStillAlive(pFormat, mpFormat))
+ return const_cast<ScIconSetFormat*>(mpFormat);
+
+ throw lang::IllegalArgumentException();
+}
+
+sal_Int32 ScIconSetFormatObj::getType()
+{
+ return sheet::ConditionEntryType::ICONSET;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScIconSetFormatObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+namespace {
+
+void setIconSetEntry(ScIconSetFormat* pFormat, uno::Reference<sheet::XIconSetEntry> const & xEntry, size_t nPos)
+{
+ ScIconSetFormatData* pData = pFormat->GetIconSetData();
+ ScColorScaleEntryType eType = ScColorScaleEntryType();
+ sal_Int32 nApiType = xEntry->getType();
+ bool bFound = false;
+ for (IconSetEntryTypeApiMap const & rEntry : aIconSetEntryTypeMap)
+ {
+ if (rEntry.nApiType == nApiType)
+ {
+ eType = rEntry.eType;
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ throw lang::IllegalArgumentException();
+
+ pData->m_Entries[nPos]->SetType(eType);
+ switch (eType)
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ break;
+ default:
+ {
+ double nVal = xEntry->getFormula().toDouble();
+ pData->m_Entries[nPos]->SetValue(nVal);
+ }
+ break;
+ }
+}
+
+}
+
+void SAL_CALL ScIconSetFormatObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch(pEntry->nWID)
+ {
+ case ShowValue:
+ {
+ bool bShowValue = true;
+ aValue >>= bShowValue;
+ getCoreObject()->GetIconSetData()->mbShowValue = bShowValue;
+ }
+ break;
+ case Reverse:
+ {
+ bool bReverse = false;
+ aValue >>= bReverse;
+ getCoreObject()->GetIconSetData()->mbReverse = bReverse;
+ }
+ break;
+ case Icons:
+ {
+ sal_Int32 nApiType = -1;
+ aValue >>= nApiType;
+ ScIconSetType eType = IconSet_3Arrows;
+ bool bFound = false;
+ for (const IconSetTypeApiMap & rEntry : aIconSetApiMap)
+ {
+ if (rEntry.nApiType == nApiType)
+ {
+ eType = rEntry.eType;
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ // TODO: we need to make sure that there are enough entries
+ getCoreObject()->GetIconSetData()->eIconSetType = eType;
+ }
+ break;
+ case IconSetEntries:
+ {
+ uno::Sequence<uno::Reference<sheet::XIconSetEntry> > aEntries;
+ if (!(aValue >>= aEntries))
+ throw lang::IllegalArgumentException();
+
+ // TODO: we need to check that the number of entries
+ // corresponds to the icon type
+ sal_Int32 nLength = aEntries.getLength();
+ for (size_t i = 0; i < o3tl::make_unsigned(nLength); ++i)
+ {
+ setIconSetEntry(getCoreObject(), aEntries[i], i);
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+uno::Any SAL_CALL ScIconSetFormatObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+
+ switch(pEntry->nWID)
+ {
+ case ShowValue:
+ aAny <<= getCoreObject()->GetIconSetData()->mbShowValue;
+ break;
+ case Reverse:
+ aAny <<= getCoreObject()->GetIconSetData()->mbReverse;
+ break;
+ case Icons:
+ {
+ ScIconSetType eType = getCoreObject()->GetIconSetData()->eIconSetType;
+ for (const IconSetTypeApiMap & rEntry : aIconSetApiMap)
+ {
+ if (rEntry.eType == eType)
+ {
+ aAny <<= rEntry.nApiType;
+ break;
+ }
+ }
+ }
+ break;
+ case IconSetEntries:
+ {
+ size_t nSize = getCoreObject()->size();
+ uno::Sequence<uno::Reference<sheet::XIconSetEntry> > aEntries(nSize);
+ auto aEntriesRange = asNonConstRange(aEntries);
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ aEntriesRange[i] = new ScIconSetEntryObj(this, i);
+ }
+ aAny <<= aEntries;
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unknown property");
+ }
+ return aAny;
+}
+
+void SAL_CALL ScIconSetFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScIconSetFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScIconSetFormatObj::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScIconSetFormatObj::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+ScIconSetEntryObj::ScIconSetEntryObj(rtl::Reference<ScIconSetFormatObj> xParent,
+ size_t nPos):
+ mxParent(std::move(xParent)),
+ mnPos(nPos)
+{
+}
+
+ScIconSetEntryObj::~ScIconSetEntryObj()
+{
+}
+
+ScColorScaleEntry* ScIconSetEntryObj::getCoreObject()
+{
+ ScIconSetFormat* pFormat = mxParent->getCoreObject();
+ if (pFormat->GetIconSetData()->m_Entries.size() <= mnPos)
+ throw lang::IllegalArgumentException();
+
+ return pFormat->GetIconSetData()->m_Entries[mnPos].get();
+}
+
+sal_Int32 ScIconSetEntryObj::getType()
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ // the first entry always is minimum
+ if (mnPos == 0)
+ return sheet::IconSetFormatEntry::ICONSET_MIN;
+
+ for (IconSetEntryTypeApiMap const & rEntry : aIconSetEntryTypeMap)
+ {
+ if (rEntry.eType == pEntry->GetType())
+ {
+ return rEntry.nApiType;
+ }
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+void ScIconSetEntryObj::setType(sal_Int32 nType)
+{
+ // first entry is always MIN
+ if (mnPos == 0)
+ return;
+
+ ScColorScaleEntry* pEntry = getCoreObject();
+ for (IconSetEntryTypeApiMap const & rEntry : aIconSetEntryTypeMap)
+ {
+ if (rEntry.nApiType == nType)
+ {
+ pEntry->SetType(rEntry.eType);
+ return;
+ }
+ }
+ throw lang::IllegalArgumentException();
+}
+
+OUString ScIconSetEntryObj::getFormula()
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ switch (pEntry->GetType())
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ break;
+ default:
+ return OUString::number(pEntry->GetValue());
+ }
+
+ return OUString();
+}
+
+void ScIconSetEntryObj::setFormula(const OUString& rFormula)
+{
+ ScColorScaleEntry* pEntry = getCoreObject();
+ switch (pEntry->GetType())
+ {
+ case COLORSCALE_FORMULA:
+ // TODO: Implement
+ // pEntry->SetFormula(rFormula);
+ break;
+ default:
+ pEntry->SetValue(rFormula.toDouble());
+ break;
+ }
+}
+
+ScCondDateFormatObj::ScCondDateFormatObj(rtl::Reference<ScCondFormatObj> xParent,
+ const ScCondDateFormatEntry* pFormat):
+ mxParent(std::move(xParent)),
+ maPropSet(getCondDatePropSet()),
+ mpFormat(pFormat)
+{
+}
+
+ScCondDateFormatObj::~ScCondDateFormatObj()
+{
+}
+
+ScCondDateFormatEntry* ScCondDateFormatObj::getCoreObject()
+{
+ ScConditionalFormat* pFormat = mxParent->getCoreObject();
+ if (isObjectStillAlive(pFormat, mpFormat))
+ return const_cast<ScCondDateFormatEntry*>(mpFormat);
+
+ throw lang::IllegalArgumentException();
+}
+
+sal_Int32 ScCondDateFormatObj::getType()
+{
+ return sheet::ConditionEntryType::DATE;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCondDateFormatObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScCondDateFormatObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ switch(pEntry->nWID)
+ {
+ case Date_StyleName:
+ {
+ OUString aStyleName;
+ if (!(aValue >>= aStyleName))
+ throw lang::IllegalArgumentException();
+
+ getCoreObject()->SetStyleName(aStyleName);
+
+ }
+ break;
+ case DateType:
+ {
+ sal_Int32 nApiType = -1;
+ if (!(aValue >>= nApiType))
+ throw lang::IllegalArgumentException();
+
+ for (DateTypeApiMap const & rEntry : aDateTypeApiMap)
+ {
+ if (rEntry.nApiType == nApiType)
+ {
+ getCoreObject()->SetDateType(rEntry.eType);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+uno::Any SAL_CALL ScCondDateFormatObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMap& rPropertyMap = maPropSet.getPropertyMap(); // from derived class
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aAny;
+
+ switch(pEntry->nWID)
+ {
+ case Date_StyleName:
+ {
+ OUString aStyleName = getCoreObject()->GetStyleName();
+ aAny <<= aStyleName;
+ }
+ break;
+ case DateType:
+ {
+ condformat::ScCondFormatDateType eType = getCoreObject()->GetDateType();
+ for (DateTypeApiMap const & rEntry : aDateTypeApiMap)
+ {
+ if (rEntry.eType == eType)
+ {
+ aAny <<= rEntry.nApiType;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sc", "unknown property");
+ }
+ return aAny;
+}
+
+void SAL_CALL ScCondDateFormatObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScCondDateFormatObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener>& /* aListener */)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScCondDateFormatObj::addVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+void SAL_CALL ScCondDateFormatObj::removeVetoableChangeListener( const OUString&,
+ const uno::Reference<beans::XVetoableChangeListener>&)
+{
+ SAL_WARN("sc", "not implemented");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/confuno.cxx b/sc/source/ui/unoobj/confuno.cxx
new file mode 100644
index 0000000000..4c69e65645
--- /dev/null
+++ b/sc/source/ui/unoobj/confuno.cxx
@@ -0,0 +1,661 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include <config_features.h>
+
+#include <confuno.hxx>
+#include <unonames.hxx>
+#include <docsh.hxx>
+#include <miscuno.hxx>
+#include <forbiuno.hxx>
+#include <appoptio.hxx>
+#include <viewopti.hxx>
+#include <docpool.hxx>
+#include <sc.hrc>
+#include <scmod.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/document/LinkUpdateModes.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <formula/grammar.hxx>
+#include <sfx2/printer.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/stream.hxx>
+
+using namespace com::sun::star;
+
+constexpr OUString SCSAVEVERSION = u"SaveVersionOnClose"_ustr;
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetConfigPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aConfigPropertyMap_Impl[] =
+ {
+ { SC_UNO_SHOWZERO, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWNOTES, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWFORMULASMARKS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWGRID, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_GRIDCOLOR, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_SHOWPAGEBR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_LINKUPD, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_COLROWHDR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHEETTABS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_OUTLSYMB, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SNAPTORASTER, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_RASTERVIS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_RASTERRESX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_RASTERRESY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_RASTERSUBX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_RASTERSUBY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_RASTERSYNC, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_AUTOCALC, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_PRINTERNAME, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNO_PRINTERSETUP, 0, cppu::UnoType<uno::Sequence<sal_Int8>>::get(), 0, 0},
+ { SC_UNO_PRINTERPAPER, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_APPLYDOCINF, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_SAVE_THUMBNAIL, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_FORBIDDEN, 0, cppu::UnoType<i18n::XForbiddenCharacters>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_CHARCOMP, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_ASIANKERN, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SCSAVEVERSION, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_UPDTEMPL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ /*Stampit enable/disable print cancel */
+ { SC_UNO_ALLOWPRINTJOBCANCEL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_LOADREADONLY, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHAREDOC, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_MODIFYPASSWORDINFO, 0, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0},
+ { SC_UNO_MODIFYPASSWORDHASH, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_EMBED_FONTS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_EMBED_ONLY_USED_FONTS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_EMBED_FONT_SCRIPT_LATIN, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_EMBED_FONT_SCRIPT_ASIAN, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_EMBED_FONT_SCRIPT_COMPLEX, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_IMAGE_PREFERRED_DPI, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_SYNTAXSTRINGREF, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ };
+ return aConfigPropertyMap_Impl;
+}
+
+ScDocumentConfiguration::ScDocumentConfiguration(ScDocShell* pDocSh)
+ : pDocShell(pDocSh) ,
+ aPropSet ( lcl_GetConfigPropertyMap() )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDocumentConfiguration::~ScDocumentConfiguration()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDocumentConfiguration::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update does not matter here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDocumentConfiguration::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScDocumentConfiguration::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if(!pDocShell)
+ throw uno::RuntimeException();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bUpdateHeights = false;
+
+ ScViewOptions aViewOpt(rDoc.GetViewOptions());
+
+ /*Stampit enable/disable print cancel */
+ if ( aPropertyName == SC_UNO_ALLOWPRINTJOBCANCEL )
+ pDocShell->Stamp_SetPrintCancelState( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ /*Stampit enable/disable print cancel */
+
+ else if ( aPropertyName == SC_UNO_SHOWZERO )
+ aViewOpt.SetOption(VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWNOTES )
+ aViewOpt.SetOption(VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS )
+ aViewOpt.SetOption(VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWGRID )
+ aViewOpt.SetOption(VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_GRIDCOLOR )
+ {
+ Color aColor;
+ if (aValue >>= aColor)
+ aViewOpt.SetGridColor(aColor, OUString());
+ }
+ else if ( aPropertyName == SC_UNO_SHOWPAGEBR )
+ aViewOpt.SetOption(VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNONAME_LINKUPD )
+ {
+ // XXX NOTE: this is the css::document::Settings property
+ // LinkUpdateMode, not the css::sheet::XGlobalSheetSettings
+ // attribute LinkUpdateMode.
+ sal_Int16 n;
+ if (!(aValue >>= n) || n < css::document::LinkUpdateModes::NEVER ||
+ n > css::document::LinkUpdateModes::GLOBAL_SETTING)
+ {
+ throw css::lang::IllegalArgumentException(
+ ("LinkUpdateMode property value must be a SHORT with a value in"
+ " the range of the css::document::LinkUpdateModes constants"),
+ css::uno::Reference<css::uno::XInterface>(), -1);
+ }
+ ScLkUpdMode eMode;
+ switch (n)
+ {
+ case css::document::LinkUpdateModes::NEVER:
+ eMode = LM_NEVER;
+ break;
+ case css::document::LinkUpdateModes::MANUAL:
+ eMode = LM_ON_DEMAND;
+ break;
+ case css::document::LinkUpdateModes::AUTO:
+ eMode = LM_ALWAYS;
+ break;
+ case css::document::LinkUpdateModes::GLOBAL_SETTING:
+ default:
+ eMode = SC_MOD()->GetAppOptions().GetLinkMode();
+ break;
+ }
+ rDoc.SetLinkMode( eMode );
+ }
+ else if ( aPropertyName == SC_UNO_COLROWHDR )
+ aViewOpt.SetOption(VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHEETTABS )
+ aViewOpt.SetOption(VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_OUTLSYMB )
+ aViewOpt.SetOption(VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_AUTOCALC )
+ rDoc.SetAutoCalc( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_PRINTERNAME )
+ {
+ OUString sPrinterName;
+ if ( !(aValue >>= sPrinterName) )
+ throw lang::IllegalArgumentException();
+
+ // #i75610# if the name is empty, do nothing (don't create any printer)
+ if ( !sPrinterName.isEmpty() && pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ SfxPrinter* pPrinter = pDocShell->GetPrinter();
+ if (!pPrinter)
+ throw uno::RuntimeException();
+
+ if (pPrinter->GetName() != sPrinterName)
+ {
+ VclPtrInstance<SfxPrinter> pNewPrinter( pPrinter->GetOptions().Clone(), sPrinterName );
+ if (pNewPrinter->IsKnown())
+ pDocShell->SetPrinter( pNewPrinter, SfxPrinterChangeFlags::PRINTER );
+ else
+ pNewPrinter.disposeAndClear();
+ }
+
+ }
+
+ }
+ else if ( aPropertyName == SC_UNO_PRINTERSETUP )
+ {
+ uno::Sequence<sal_Int8> aSequence;
+ if ( aValue >>= aSequence )
+ {
+ sal_uInt32 nSize = aSequence.getLength();
+ // #i75610# if the sequence is empty, do nothing (don't create any printer)
+ if ( nSize != 0 )
+ {
+ SvMemoryStream aStream (aSequence.getArray(), nSize, StreamMode::READ );
+ aStream.Seek ( STREAM_SEEK_TO_BEGIN );
+ auto pSet = std::make_unique<SfxItemSetFixed
+ <SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
+ SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
+ SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET,
+ SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS>>( *rDoc.GetPool());
+
+ SfxPrinter* pPrinter = pDocShell->GetPrinter();
+ bool bPreferPrinterPapersize = false;
+ if ( pPrinter )
+ bPreferPrinterPapersize = pPrinter->GetPrinterSettingsPreferred();
+
+ VclPtr<SfxPrinter> pTempPrinter = SfxPrinter::Create( aStream, std::move(pSet) );
+ pTempPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize );
+ pDocShell->SetPrinter( pTempPrinter );
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNO_PRINTERPAPER )
+ {
+ bool bPreferPrinterPapersize;
+ if( aValue >>= bPreferPrinterPapersize )
+ {
+ if( pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ SfxPrinter *pTempPrinter = pDocShell->GetPrinter( true );
+ if (pTempPrinter)
+ pTempPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize );
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNO_APPLYDOCINF )
+ {
+ bool bTmp=true;
+ if ( aValue >>= bTmp )
+ pDocShell->SetUseUserData( bTmp );
+ }
+ else if ( aPropertyName == SC_UNO_SAVE_THUMBNAIL)
+ {
+ bool bTmp = true;
+ if (aValue >>= bTmp)
+ pDocShell->SetUseThumbnailSave( bTmp );
+ }
+ else if ( aPropertyName == SC_UNO_FORBIDDEN )
+ {
+ // read-only - should not be set
+ }
+ else if ( aPropertyName == SC_UNO_CHARCOMP )
+ {
+ // Int16 contains CharacterCompressionType values
+ sal_Int16 nUno = ScUnoHelpFunctions::GetInt16FromAny( aValue );
+ rDoc.SetAsianCompression( static_cast<CharCompressType>(nUno) );
+ bUpdateHeights = true;
+ }
+ else if ( aPropertyName == SC_UNO_ASIANKERN )
+ {
+ rDoc.SetAsianKerning( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ bUpdateHeights = true;
+ }
+ else if ( aPropertyName == SCSAVEVERSION )
+ {
+ bool bTmp=false;
+ if ( aValue >>= bTmp )
+ pDocShell->SetSaveVersionOnClose( bTmp );
+ }
+ else if ( aPropertyName == SC_UNO_UPDTEMPL )
+ {
+ bool bTmp=true;
+ if ( aValue >>= bTmp )
+ pDocShell->SetQueryLoadTemplate( bTmp );
+ }
+ else if ( aPropertyName == SC_UNO_LOADREADONLY )
+ {
+ bool bTmp=false;
+ if ( aValue >>= bTmp )
+ pDocShell->SetLoadReadonly( bTmp );
+ }
+ else if ( aPropertyName == SC_UNO_SHAREDOC )
+ {
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ bool bDocShared = false;
+ if ( aValue >>= bDocShared )
+ {
+ pDocShell->SetSharedXMLFlag( bDocShared );
+ }
+#endif
+ }
+ else if ( aPropertyName == SC_UNO_MODIFYPASSWORDINFO )
+ {
+ uno::Sequence< beans::PropertyValue > aInfo;
+ if ( !( aValue >>= aInfo ) )
+ throw lang::IllegalArgumentException(
+ "Value of type Sequence<PropertyValue> expected!",
+ uno::Reference< uno::XInterface >(),
+ 2 );
+
+ if ( !pDocShell->SetModifyPasswordInfo( aInfo ) )
+ throw beans::PropertyVetoException(
+ "The hash is not allowed to be changed now!" );
+ }
+ else if (aPropertyName == SC_UNO_MODIFYPASSWORDHASH)
+ {
+ sal_Int32 nHash;
+ if (!(aValue >>= nHash))
+ throw lang::IllegalArgumentException("Value of type sal_Int32 expected!",
+ uno::Reference<uno::XInterface>(), 2);
+
+ if (!pDocShell->SetModifyPasswordHash(nHash))
+ throw beans::PropertyVetoException("The hash is not allowed to be changed now!");
+ }
+ else if (aPropertyName == SC_UNO_EMBED_FONTS)
+ {
+ bool bVal = aValue.has<bool>() && aValue.get<bool>();
+ rDoc.SetEmbedFonts(bVal);
+ }
+ else if (aPropertyName == SC_UNO_EMBED_ONLY_USED_FONTS)
+ {
+ bool bVal = aValue.has<bool>() && aValue.get<bool>();
+ rDoc.SetEmbedUsedFontsOnly(bVal);
+ }
+ else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_LATIN)
+ {
+ bool bVal = aValue.has<bool>() && aValue.get<bool>();
+ rDoc.SetEmbedFontScriptLatin(bVal);
+ }
+ else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_ASIAN)
+ {
+ bool bVal = aValue.has<bool>() && aValue.get<bool>();
+ rDoc.SetEmbedFontScriptAsian(bVal);
+ }
+ else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_COMPLEX)
+ {
+ bool bVal = aValue.has<bool>() && aValue.get<bool>();
+ rDoc.SetEmbedFontScriptComplex(bVal);
+ }
+ else if ( aPropertyName == SC_UNO_SYNTAXSTRINGREF )
+ {
+ ScCalcConfig aCalcConfig = rDoc.GetCalcConfig();
+ sal_Int16 nUno = 0;
+
+ if( aValue >>= nUno )
+ {
+ switch (nUno)
+ {
+ case 0: // CONV_OOO
+ case 2: // CONV_XL_A1
+ case 3: // CONV_XL_R1C1
+ case 7: // CONV_A1_XL_A1
+ aCalcConfig.SetStringRefSyntax( static_cast<formula::FormulaGrammar::AddressConvention>( nUno ) );
+ break;
+ default:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_UNSPECIFIED );
+ break;
+
+ }
+ rDoc.SetCalcConfig( aCalcConfig );
+ }
+ }
+ else if (aPropertyName == SC_UNO_IMAGE_PREFERRED_DPI)
+ {
+ if (aValue.has<sal_Int32>())
+ {
+ rDoc.SetImagePreferredDPI(aValue.get<sal_Int32>());
+ }
+ }
+ else
+ {
+ ScGridOptions aGridOpt(aViewOpt.GetGridOptions());
+ if ( aPropertyName == SC_UNO_SNAPTORASTER )
+ aGridOpt.SetUseGridSnap( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_RASTERVIS )
+ aGridOpt.SetGridVisible( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_RASTERRESX )
+ aGridOpt.SetFieldDrawX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) );
+ else if ( aPropertyName == SC_UNO_RASTERRESY )
+ aGridOpt.SetFieldDrawY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) );
+ else if ( aPropertyName == SC_UNO_RASTERSUBX )
+ aGridOpt.SetFieldDivisionX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) );
+ else if ( aPropertyName == SC_UNO_RASTERSUBY )
+ aGridOpt.SetFieldDivisionY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( aValue ) ) );
+ else if ( aPropertyName == SC_UNO_RASTERSYNC )
+ aGridOpt.SetSynchronize( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+ aViewOpt.SetGridOptions(aGridOpt);
+ }
+ rDoc.SetViewOptions(aViewOpt);
+
+ if ( bUpdateHeights && !rDoc.IsImportingXML() )
+ {
+ // update automatic row heights and repaint
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if ( !pDocShell->AdjustRowHeight( 0, rDoc.MaxRow(), nTab ) )
+ pDocShell->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), PaintPartFlags::Grid);
+ pDocShell->SetDocumentModified();
+ }
+
+}
+
+uno::Any SAL_CALL ScDocumentConfiguration::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if(!pDocShell)
+ throw uno::RuntimeException();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const ScViewOptions& aViewOpt = rDoc.GetViewOptions();
+
+ /*Stampit enable/disable print cancel */
+ if ( aPropertyName == SC_UNO_ALLOWPRINTJOBCANCEL )
+ aRet <<= pDocShell->Stamp_GetPrintCancelState();
+ /*Stampit enable/disable print cancel */
+
+ else if ( aPropertyName == SC_UNO_SHOWZERO )
+ aRet <<= aViewOpt.GetOption( VOPT_NULLVALS );
+ else if ( aPropertyName == SC_UNO_SHOWNOTES )
+ aRet <<= aViewOpt.GetOption( VOPT_NOTES );
+ else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS )
+ aRet <<= aViewOpt.GetOption( VOPT_FORMULAS_MARKS );
+ else if ( aPropertyName == SC_UNO_SHOWGRID )
+ aRet <<= aViewOpt.GetOption( VOPT_GRID );
+ else if ( aPropertyName == SC_UNO_GRIDCOLOR )
+ {
+ OUString aColorName;
+ Color aColor = aViewOpt.GetGridColor(&aColorName);
+ aRet <<= aColor;
+ }
+ else if ( aPropertyName == SC_UNO_SHOWPAGEBR )
+ aRet <<= aViewOpt.GetOption( VOPT_PAGEBREAKS );
+ else if ( aPropertyName == SC_UNONAME_LINKUPD )
+ {
+ sal_Int16 nLUM;
+ switch (rDoc.GetLinkMode())
+ {
+ case LM_ALWAYS:
+ nLUM = css::document::LinkUpdateModes::AUTO;
+ break;
+ case LM_NEVER:
+ nLUM = css::document::LinkUpdateModes::NEVER;
+ break;
+ case LM_ON_DEMAND:
+ nLUM = css::document::LinkUpdateModes::MANUAL;
+ break;
+ case LM_UNKNOWN:
+ default:
+ nLUM = css::document::LinkUpdateModes::GLOBAL_SETTING;
+ break;
+ }
+ aRet <<= nLUM;
+ }
+ else if ( aPropertyName == SC_UNO_COLROWHDR )
+ aRet <<= aViewOpt.GetOption( VOPT_HEADER );
+ else if ( aPropertyName == SC_UNO_SHEETTABS )
+ aRet <<= aViewOpt.GetOption( VOPT_TABCONTROLS );
+ else if ( aPropertyName == SC_UNO_OUTLSYMB )
+ aRet <<= aViewOpt.GetOption( VOPT_OUTLINER );
+ else if ( aPropertyName == SC_UNO_AUTOCALC )
+ aRet <<= rDoc.GetAutoCalc();
+ else if ( aPropertyName == SC_UNO_PRINTERNAME )
+ {
+ // #i75610# don't create the printer, return empty string if no printer created yet
+ // (as in SwXDocumentSettings)
+ SfxPrinter* pPrinter = rDoc.GetPrinter( false );
+ if (pPrinter)
+ aRet <<= pPrinter->GetName();
+ else
+ aRet <<= OUString();
+ }
+ else if ( aPropertyName == SC_UNO_PRINTERSETUP )
+ {
+ // #i75610# don't create the printer, return empty sequence if no printer created yet
+ // (as in SwXDocumentSettings)
+ SfxPrinter* pPrinter = rDoc.GetPrinter( false );
+ if (pPrinter)
+ {
+ SvMemoryStream aStream;
+ pPrinter->Store( aStream );
+ aRet <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aStream.GetData() ),
+ aStream.TellEnd() );
+ }
+ else
+ aRet <<= uno::Sequence<sal_Int8>();
+ }
+ else if ( aPropertyName == SC_UNO_PRINTERPAPER)
+ {
+ SfxPrinter *pTempPrinter = pDocShell->GetPrinter( false );
+ aRet <<= pTempPrinter && pTempPrinter->GetPrinterSettingsPreferred();
+
+ }
+ else if ( aPropertyName == SC_UNO_APPLYDOCINF )
+ aRet <<= pDocShell->IsUseUserData();
+ else if ( aPropertyName == SC_UNO_SAVE_THUMBNAIL )
+ aRet <<= pDocShell->IsUseThumbnailSave();
+ else if ( aPropertyName == SC_UNO_FORBIDDEN )
+ {
+ aRet <<= uno::Reference<i18n::XForbiddenCharacters>(new ScForbiddenCharsObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_CHARCOMP )
+ aRet <<= static_cast<sal_Int16> ( rDoc.GetAsianCompression() );
+ else if ( aPropertyName == SC_UNO_ASIANKERN )
+ aRet <<= rDoc.GetAsianKerning();
+ else if ( aPropertyName == SCSAVEVERSION )
+ aRet <<= pDocShell->IsSaveVersionOnClose();
+ else if ( aPropertyName == SC_UNO_UPDTEMPL )
+ aRet <<= pDocShell->IsQueryLoadTemplate();
+ else if ( aPropertyName == SC_UNO_LOADREADONLY )
+ aRet <<= pDocShell->IsLoadReadonly();
+ else if ( aPropertyName == SC_UNO_SHAREDOC )
+ {
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ aRet <<= pDocShell->HasSharedXMLFlagSet();
+#endif
+ }
+ else if ( aPropertyName == SC_UNO_MODIFYPASSWORDINFO )
+ aRet <<= pDocShell->GetModifyPasswordInfo();
+ else if (aPropertyName == SC_UNO_MODIFYPASSWORDHASH)
+ aRet <<= pDocShell->GetModifyPasswordHash();
+ else if (aPropertyName == SC_UNO_EMBED_FONTS)
+ aRet <<= rDoc.IsEmbedFonts();
+ else if (aPropertyName == SC_UNO_EMBED_ONLY_USED_FONTS)
+ aRet <<= rDoc.IsEmbedUsedFontsOnly();
+ else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_LATIN)
+ aRet <<= rDoc.IsEmbedFontScriptLatin();
+ else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_ASIAN)
+ aRet <<= rDoc.IsEmbedFontScriptAsian();
+ else if (aPropertyName == SC_UNO_EMBED_FONT_SCRIPT_COMPLEX)
+ aRet <<= rDoc.IsEmbedFontScriptComplex();
+ else if ( aPropertyName == SC_UNO_SYNTAXSTRINGREF )
+ {
+ ScCalcConfig aCalcConfig = rDoc.GetCalcConfig();
+ formula::FormulaGrammar::AddressConvention eConv = aCalcConfig.meStringRefAddressSyntax;
+
+ // don't save "unspecified" string ref syntax ... query formula grammar
+ // and save that instead
+ if( eConv == formula::FormulaGrammar::CONV_UNSPECIFIED)
+ {
+ eConv = rDoc.GetAddressConvention();
+ }
+
+ // write if it has been read|imported or explicitly changed
+ // or if ref syntax isn't what would be native for our file format
+ // i.e. CalcA1 in this case
+ if ( aCalcConfig.mbHasStringRefSyntax ||
+ (eConv != formula::FormulaGrammar::CONV_OOO) )
+ {
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_OOO:
+ case formula::FormulaGrammar::CONV_XL_A1:
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ case formula::FormulaGrammar::CONV_A1_XL_A1:
+ aRet <<= static_cast<sal_Int16>( eConv );
+ break;
+
+ case formula::FormulaGrammar::CONV_UNSPECIFIED:
+ case formula::FormulaGrammar::CONV_ODF:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ case formula::FormulaGrammar::CONV_LOTUS_A1:
+ case formula::FormulaGrammar::CONV_LAST:
+ {
+ aRet <<= sal_Int16(9999);
+ break;
+ }
+ }
+ }
+ }
+ else if (aPropertyName == SC_UNO_IMAGE_PREFERRED_DPI)
+ {
+ aRet <<= rDoc.GetImagePreferredDPI();
+ }
+ else
+ {
+ const ScGridOptions& aGridOpt = aViewOpt.GetGridOptions();
+ if ( aPropertyName == SC_UNO_SNAPTORASTER )
+ aRet <<= aGridOpt.GetUseGridSnap();
+ else if ( aPropertyName == SC_UNO_RASTERVIS )
+ aRet <<= aGridOpt.GetGridVisible();
+ else if ( aPropertyName == SC_UNO_RASTERRESX )
+ aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDrawX() );
+ else if ( aPropertyName == SC_UNO_RASTERRESY )
+ aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDrawY() );
+ else if ( aPropertyName == SC_UNO_RASTERSUBX )
+ aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDivisionX() );
+ else if ( aPropertyName == SC_UNO_RASTERSUBY )
+ aRet <<= static_cast<sal_Int32> ( aGridOpt.GetFieldDivisionY() );
+ else if ( aPropertyName == SC_UNO_RASTERSYNC )
+ aRet <<= aGridOpt.GetSynchronize();
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+ }
+
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDocumentConfiguration )
+
+// XServiceInfo
+OUString SAL_CALL ScDocumentConfiguration::getImplementationName()
+{
+ return "ScDocumentConfiguration";
+}
+
+sal_Bool SAL_CALL ScDocumentConfiguration::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScDocumentConfiguration::getSupportedServiceNames()
+{
+ return {"com.sun.star.comp.SpreadsheetSettings",
+ "com.sun.star.document.Settings"};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/convuno.cxx b/sc/source/ui/unoobj/convuno.cxx
new file mode 100644
index 0000000000..d6ce8d9e1f
--- /dev/null
+++ b/sc/source/ui/unoobj/convuno.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <i18nlangtag/languagetag.hxx>
+
+#include <convuno.hxx>
+
+using namespace com::sun::star;
+
+// everything is static...
+
+LanguageType ScUnoConversion::GetLanguage(const lang::Locale& rLocale)
+{
+ // empty language -> LANGUAGE_SYSTEM
+ if (rLocale.Language.isEmpty())
+ return LANGUAGE_SYSTEM;
+
+ LanguageType eRet = LanguageTag::convertToLanguageType(rLocale, false);
+ if (eRet == LANGUAGE_NONE)
+ eRet = LANGUAGE_SYSTEM; //! or throw an exception?
+
+ return eRet;
+}
+
+void ScUnoConversion::FillLocale(lang::Locale& rLocale, LanguageType eLang)
+{
+ rLocale = LanguageTag::convertToLocale(eLang);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/cursuno.cxx b/sc/source/ui/unoobj/cursuno.cxx
new file mode 100644
index 0000000000..f2b9142f37
--- /dev/null
+++ b/sc/source/ui/unoobj/cursuno.cxx
@@ -0,0 +1,453 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cursuno.hxx>
+#include <cellsuno.hxx>
+#include <docsh.hxx>
+#include <markdata.hxx>
+#include <miscuno.hxx>
+
+using namespace com::sun::star;
+
+constexpr OUString SCSHEETCELLCURSOR_SERVICE = u"com.sun.star.sheet.SheetCellCursor"_ustr;
+constexpr OUString SCCELLCURSOR_SERVICE = u"com.sun.star.table.CellCursor"_ustr;
+
+ScCellCursorObj::ScCellCursorObj(ScDocShell* pDocSh, const ScRange& rR) :
+ ScCellRangeObj( pDocSh, rR )
+{
+}
+
+ScCellCursorObj::~ScCellCursorObj()
+{
+}
+
+uno::Any SAL_CALL ScCellCursorObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XSheetCellCursor*>(this),
+ static_cast<sheet::XUsedAreaCursor*>(this),
+ static_cast<table::XCellCursor*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScCellRangeObj::queryInterface( rType );
+}
+
+void SAL_CALL ScCellCursorObj::acquire() noexcept
+{
+ ScCellRangeObj::acquire();
+}
+
+void SAL_CALL ScCellCursorObj::release() noexcept
+{
+ ScCellRangeObj::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScCellCursorObj::getTypes()
+{
+ return comphelper::concatSequences(
+ ScCellRangeObj::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<sheet::XSheetCellCursor>::get(),
+ cppu::UnoType<sheet::XUsedAreaCursor>::get(),
+ cppu::UnoType<table::XCellCursor>::get()
+ } );
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScCellCursorObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XSheetCellCursor
+
+void SAL_CALL ScCellCursorObj::collapseToCurrentRegion()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ] );
+
+ aOneRange.PutInOrder();
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ SCCOL nStartCol = aOneRange.aStart.Col();
+ SCROW nStartRow = aOneRange.aStart.Row();
+ SCCOL nEndCol = aOneRange.aEnd.Col();
+ SCROW nEndRow = aOneRange.aEnd.Row();
+ SCTAB nTab = aOneRange.aStart.Tab();
+
+ pDocSh->GetDocument().GetDataArea(
+ nTab, nStartCol, nStartRow, nEndCol, nEndRow, true, false );
+
+ ScRange aNew( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
+ SetNewRange( aNew );
+}
+
+void SAL_CALL ScCellCursorObj::collapseToCurrentArray()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ] );
+
+ aOneRange.PutInOrder();
+ ScAddress aCursor(aOneRange.aStart); // use the start address of the range
+
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScRange aMatrix;
+
+ // finding the matrix range is now in GetMatrixFormulaRange in the document
+ if ( rDoc.GetMatrixFormulaRange( aCursor, aMatrix ) )
+ {
+ SetNewRange( aMatrix );
+ }
+ }
+ // that's a Bug, that this assertion comes; the API Reference says, that
+ // if there is no Matrix, the Range is left unchanged; they say nothing
+ // about an exception
+ /*if (!bFound)
+ {
+ OSL_FAIL("no matrix");
+ //! Exception, or what?
+ }*/
+}
+
+void SAL_CALL ScCellCursorObj::collapseToMergedArea()
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ {
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aNewRange( rRanges[ 0 ] );
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ rDoc.ExtendOverlapped( aNewRange );
+ rDoc.ExtendMerge( aNewRange ); // after ExtendOverlapped!
+
+ SetNewRange( aNewRange );
+ }
+}
+
+void SAL_CALL ScCellCursorObj::expandToEntireColumns()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aNewRange( rRanges[ 0 ] );
+
+ aNewRange.aStart.SetRow( 0 );
+ aNewRange.aEnd.SetRow( GetDocShell()->GetDocument().MaxRow() );
+
+ SetNewRange( aNewRange );
+}
+
+void SAL_CALL ScCellCursorObj::expandToEntireRows()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aNewRange( rRanges[ 0 ] );
+
+ aNewRange.aStart.SetCol( 0 );
+ aNewRange.aEnd.SetCol( GetDocShell()->GetDocument().MaxCol() );
+
+ SetNewRange( aNewRange );
+}
+
+void SAL_CALL ScCellCursorObj::collapseToSize( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ SolarMutexGuard aGuard;
+ if ( nColumns <= 0 || nRows <= 0 )
+ {
+ OSL_FAIL("Empty range not allowed");
+ //! and then?
+ }
+ else
+ {
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aNewRange( rRanges[ 0 ] );
+
+ aNewRange.PutInOrder(); //! really?
+
+ const auto & rDoc = GetDocShell()->GetDocument();
+ tools::Long nEndX = aNewRange.aStart.Col() + nColumns - 1;
+ tools::Long nEndY = aNewRange.aStart.Row() + nRows - 1;
+ if ( nEndX < 0 ) nEndX = 0;
+ if ( nEndX > rDoc.MaxCol() ) nEndX = rDoc.MaxCol();
+ if ( nEndY < 0 ) nEndY = 0;
+ if ( nEndY > rDoc.MaxRow() ) nEndY = rDoc.MaxRow();
+ //! error/exception or so, if too big/small
+
+ aNewRange.aEnd.SetCol(static_cast<SCCOL>(nEndX));
+ aNewRange.aEnd.SetRow(static_cast<SCROW>(nEndY));
+
+ aNewRange.PutInOrder(); //! really?
+
+ SetNewRange( aNewRange );
+ }
+}
+
+// XUsedAreaCursor
+
+void SAL_CALL ScCellCursorObj::gotoStartOfUsedArea(sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aNewRange( rRanges[0] );
+ SCTAB nTab = aNewRange.aStart.Tab();
+
+ SCCOL nUsedX = 0; // fetch the beginning
+ SCROW nUsedY = 0;
+ if (!pDocSh->GetDocument().GetDataStart( nTab, nUsedX, nUsedY ))
+ {
+ nUsedX = 0;
+ nUsedY = 0;
+ }
+
+ aNewRange.aStart.SetCol( nUsedX );
+ aNewRange.aStart.SetRow( nUsedY );
+ if (!bExpand)
+ aNewRange.aEnd = aNewRange.aStart;
+ SetNewRange( aNewRange );
+}
+
+void SAL_CALL ScCellCursorObj::gotoEndOfUsedArea( sal_Bool bExpand )
+{
+ SolarMutexGuard aGuard;
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aNewRange( rRanges[ 0 ]);
+ SCTAB nTab = aNewRange.aStart.Tab();
+
+ SCCOL nUsedX = 0; // fetch the end
+ SCROW nUsedY = 0;
+ if (!pDocSh->GetDocument().GetTableArea( nTab, nUsedX, nUsedY, true ))
+ {
+ nUsedX = 0;
+ nUsedY = 0;
+ }
+
+ aNewRange.aEnd.SetCol( nUsedX );
+ aNewRange.aEnd.SetRow( nUsedY );
+ if (!bExpand)
+ aNewRange.aStart = aNewRange.aEnd;
+ SetNewRange( aNewRange );
+}
+
+// XCellCursor
+
+void SAL_CALL ScCellCursorObj::gotoStart()
+{
+ // this is similar to collapseToCurrentRegion
+ //! something like gotoEdge with 4 possible directions is needed
+
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ]);
+
+ aOneRange.PutInOrder();
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ SCCOL nStartCol = aOneRange.aStart.Col();
+ SCROW nStartRow = aOneRange.aStart.Row();
+ SCCOL nEndCol = aOneRange.aEnd.Col();
+ SCROW nEndRow = aOneRange.aEnd.Row();
+ SCTAB nTab = aOneRange.aStart.Tab();
+
+ pDocSh->GetDocument().GetDataArea(
+ nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, false );
+
+ ScRange aNew( nStartCol, nStartRow, nTab );
+ SetNewRange( aNew );
+}
+
+void SAL_CALL ScCellCursorObj::gotoEnd()
+{
+ // this is similar to collapseToCurrentRegion
+ //! something like gotoEdge with 4 possible directions is needed
+
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ] );
+
+ aOneRange.PutInOrder();
+ ScDocShell* pDocSh = GetDocShell();
+ if ( !pDocSh )
+ return;
+
+ SCCOL nStartCol = aOneRange.aStart.Col();
+ SCROW nStartRow = aOneRange.aStart.Row();
+ SCCOL nEndCol = aOneRange.aEnd.Col();
+ SCROW nEndRow = aOneRange.aEnd.Row();
+ SCTAB nTab = aOneRange.aStart.Tab();
+
+ pDocSh->GetDocument().GetDataArea(
+ nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, false );
+
+ ScRange aNew( nEndCol, nEndRow, nTab );
+ SetNewRange( aNew );
+}
+
+void SAL_CALL ScCellCursorObj::gotoNext()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ] );
+
+ aOneRange.PutInOrder();
+ ScAddress aCursor(aOneRange.aStart); // always use start of block
+
+ ScMarkData aMark(GetDocument()->GetSheetLimits()); // not used with bMarked=FALSE
+ SCCOL nNewX = aCursor.Col();
+ SCROW nNewY = aCursor.Row();
+ SCTAB nTab = aCursor.Tab();
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ pDocSh->GetDocument().GetNextPos( nNewX,nNewY, nTab, 1,0, false,true, aMark );
+ //! otherwise exception or so
+
+ SetNewRange( ScRange( nNewX, nNewY, nTab ) );
+}
+
+void SAL_CALL ScCellCursorObj::gotoPrevious()
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ] );
+
+ aOneRange.PutInOrder();
+ ScAddress aCursor(aOneRange.aStart); // always use start of block
+
+ ScMarkData aMark(GetDocument()->GetSheetLimits()); // not used with bMarked=FALSE
+ SCCOL nNewX = aCursor.Col();
+ SCROW nNewY = aCursor.Row();
+ SCTAB nTab = aCursor.Tab();
+ ScDocShell* pDocSh = GetDocShell();
+ if ( pDocSh )
+ pDocSh->GetDocument().GetNextPos( nNewX,nNewY, nTab, -1,0, false,true, aMark );
+ //! otherwise exception or so
+
+ SetNewRange( ScRange( nNewX, nNewY, nTab ) );
+}
+
+void SAL_CALL ScCellCursorObj::gotoOffset( sal_Int32 nColumnOffset, sal_Int32 nRowOffset )
+{
+ SolarMutexGuard aGuard;
+ const ScRangeList& rRanges = GetRangeList();
+ OSL_ENSURE( rRanges.size() == 1, "Range? Ranges?" );
+ ScRange aOneRange( rRanges[ 0 ] );
+ aOneRange.PutInOrder();
+
+ const auto & rDoc = GetDocShell()->GetDocument();
+ if ( aOneRange.aStart.Col() + nColumnOffset >= 0 &&
+ aOneRange.aEnd.Col() + nColumnOffset <= rDoc.MaxCol() &&
+ aOneRange.aStart.Row() + nRowOffset >= 0 &&
+ aOneRange.aEnd.Row() + nRowOffset <= rDoc.MaxRow() )
+ {
+ ScRange aNew( static_cast<SCCOL>(aOneRange.aStart.Col() + nColumnOffset),
+ static_cast<SCROW>(aOneRange.aStart.Row() + nRowOffset),
+ aOneRange.aStart.Tab(),
+ static_cast<SCCOL>(aOneRange.aEnd.Col() + nColumnOffset),
+ static_cast<SCROW>(aOneRange.aEnd.Row() + nRowOffset),
+ aOneRange.aEnd.Tab() );
+ SetNewRange( aNew );
+ }
+}
+
+// XSheetCellRange
+
+uno::Reference<sheet::XSpreadsheet> SAL_CALL ScCellCursorObj::getSpreadsheet()
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::getSpreadsheet();
+}
+
+// XCellRange
+
+uno::Reference<table::XCell> SAL_CALL ScCellCursorObj::getCellByPosition(
+ sal_Int32 nColumn, sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::getCellByPosition(nColumn,nRow);
+}
+
+uno::Reference<table::XCellRange> SAL_CALL ScCellCursorObj::getCellRangeByPosition(
+ sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::getCellRangeByPosition(nLeft,nTop,nRight,nBottom);
+}
+
+uno::Reference<table::XCellRange> SAL_CALL ScCellCursorObj::getCellRangeByName(
+ const OUString& rRange )
+{
+ SolarMutexGuard aGuard;
+ return ScCellRangeObj::getCellRangeByName(rRange);
+}
+
+// XServiceInfo
+
+OUString SAL_CALL ScCellCursorObj::getImplementationName()
+{
+ return "ScCellCursorObj";
+}
+
+sal_Bool SAL_CALL ScCellCursorObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellCursorObj::getSupportedServiceNames()
+{
+ // SheetCellCursor should be first (?)
+ return comphelper::concatSequences<OUString>(
+ { SCSHEETCELLCURSOR_SERVICE, SCCELLCURSOR_SERVICE },
+ ScCellRangeObj::getSupportedServiceNames());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/dapiuno.cxx b/sc/source/ui/unoobj/dapiuno.cxx
new file mode 100644
index 0000000000..f172716c11
--- /dev/null
+++ b/sc/source/ui/unoobj/dapiuno.cxx
@@ -0,0 +1,3344 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <cmath>
+
+#include <o3tl/safeint.hxx>
+#include <svl/hint.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+#include <dapiuno.hxx>
+#include <datauno.hxx>
+#include <miscuno.hxx>
+#include <convuno.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <rangeutl.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <dpsave.hxx>
+#include <dbdocfun.hxx>
+#include <unonames.hxx>
+#include <dpdimsave.hxx>
+#include <hints.hxx>
+#include <dputil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <generalfunction.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/NullPointerException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/XLevelsSupplier.hpp>
+#include <com/sun/star/sheet/XMembersAccess.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp>
+#include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+
+#include <comphelper/extract.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::sheet;
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+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::container::ElementExistException;
+using ::com::sun::star::container::NoSuchElementException;
+using ::com::sun::star::container::XEnumeration;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::container::XNameAccess;
+using ::com::sun::star::container::XNamed;
+
+using ::com::sun::star::beans::UnknownPropertyException;
+using ::com::sun::star::beans::XPropertyChangeListener;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::beans::XPropertySetInfo;
+using ::com::sun::star::beans::XVetoableChangeListener;
+
+using ::com::sun::star::lang::IllegalArgumentException;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using ::com::sun::star::lang::NullPointerException;
+
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+
+namespace {
+
+std::span<const SfxItemPropertyMapEntry> lcl_GetDataPilotDescriptorBaseMap()
+{
+ static const SfxItemPropertyMapEntry aDataPilotDescriptorBaseMap_Impl[] =
+ {
+ { SC_UNO_DP_COLGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_DRILLDOWN, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_GRANDTOTAL_NAME,0,cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 },
+ { SC_UNO_DP_IGNORE_EMPTYROWS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_IMPORTDESC, 0, cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(), 0, 0 },
+ { SC_UNO_DP_REPEATEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_ROWGRAND, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_SERVICEARG, 0, cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get(), 0, 0 },
+ { SC_UNO_DP_SHOWFILTER, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_DP_SOURCESERVICE, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ return aDataPilotDescriptorBaseMap_Impl;
+}
+
+std::span<const SfxItemPropertyMapEntry> lcl_GetDataPilotFieldMap()
+{
+ using namespace ::com::sun::star::beans::PropertyAttribute;
+ static const SfxItemPropertyMapEntry aDataPilotFieldMap_Impl[] =
+ {
+ { SC_UNONAME_AUTOSHOW, 0, cppu::UnoType<DataPilotFieldAutoShowInfo>::get(), MAYBEVOID, 0 },
+ { SC_UNONAME_FUNCTION, 0, cppu::UnoType<GeneralFunction>::get(), 0, 0 },
+ { SC_UNONAME_FUNCTION2, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNONAME_GROUPINFO, 0, cppu::UnoType<DataPilotFieldGroupInfo>::get(), MAYBEVOID, 0 },
+ { SC_UNONAME_HASAUTOSHOW, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_HASLAYOUTINFO,0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_HASREFERENCE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_HASSORTINFO, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_ISGROUP, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_LAYOUTINFO, 0, cppu::UnoType<DataPilotFieldLayoutInfo>::get(), MAYBEVOID, 0 },
+ { SC_UNONAME_ORIENT, 0, cppu::UnoType<DataPilotFieldOrientation>::get(), MAYBEVOID, 0 },
+ { SC_UNONAME_REFERENCE, 0, cppu::UnoType<DataPilotFieldReference>::get(), MAYBEVOID, 0 },
+ { SC_UNONAME_SELPAGE, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_SHOWEMPTY, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_SORTINFO, 0, cppu::UnoType<DataPilotFieldSortInfo>::get(), MAYBEVOID, 0 },
+ { SC_UNONAME_SUBTOTALS, 0, cppu::UnoType<Sequence<GeneralFunction>>::get(), 0, 0 },
+ { SC_UNONAME_SUBTOTALS2, 0, cppu::UnoType<Sequence<sal_Int16>>::get(), 0, 0 },
+ { SC_UNONAME_USESELPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aDataPilotFieldMap_Impl;
+}
+
+std::span<const SfxItemPropertyMapEntry> lcl_GetDataPilotItemMap()
+{
+ static const SfxItemPropertyMapEntry aDataPilotItemMap_Impl[] =
+ {
+ { SC_UNONAME_ISHIDDEN, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_POS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_SHOWDETAIL, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aDataPilotItemMap_Impl;
+}
+
+bool lclCheckValidDouble( double fValue, bool bAuto )
+{
+ return bAuto || std::isfinite( fValue );
+}
+
+bool lclCheckMinMaxStep( const DataPilotFieldGroupInfo& rInfo )
+{
+ return
+ lclCheckValidDouble( rInfo.Start, rInfo.HasAutoStart ) &&
+ lclCheckValidDouble( rInfo.End, rInfo.HasAutoEnd ) &&
+ (rInfo.HasAutoStart || rInfo.HasAutoEnd || (rInfo.Start <= rInfo.End)) &&
+ lclCheckValidDouble( rInfo.Step, false ) &&
+ (0.0 <= rInfo.Step);
+}
+
+} // namespace
+
+SC_SIMPLE_SERVICE_INFO( ScDataPilotDescriptor, "ScDataPilotDescriptor", "stardiv::one::sheet::DataPilotDescriptor" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldObj, "ScDataPilotFieldObj", "com.sun.star.sheet.DataPilotField" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldsObj, "ScDataPilotFieldsObj", "com.sun.star.sheet.DataPilotFields" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotTableObj, "ScDataPilotTableObj", "com.sun.star.sheet.DataPilotTable" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotTablesObj, "ScDataPilotTablesObj", "com.sun.star.sheet.DataPilotTables" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotItemsObj, "ScDataPilotItemsObj", "com.sun.star.sheet.DataPilotItems" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotItemObj, "ScDataPilotItemObj", "com.sun.star.sheet.DataPilotItem" )
+
+SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldGroupsObj, "ScDataPilotFieldGroupsObj", "com.sun.star.sheet.DataPilotFieldGroups" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldGroupObj, "ScDataPilotFieldGroupObj", "com.sun.star.sheet.DataPilotFieldGroup" )
+SC_SIMPLE_SERVICE_INFO( ScDataPilotFieldGroupItemObj, "ScDataPilotFieldGroupItemObj", "com.sun.star.sheet.DataPilotFieldGroupItem" )
+
+// name that is used in the API for the data layout field
+constexpr OUString SC_DATALAYOUT_NAME = u"Data"_ustr;
+
+ScGeneralFunction ScDataPilotConversion::FirstFunc( PivotFunc nBits )
+{
+ if ( nBits & PivotFunc::Sum ) return ScGeneralFunction::SUM;
+ if ( nBits & PivotFunc::Count ) return ScGeneralFunction::COUNT;
+ if ( nBits & PivotFunc::Average ) return ScGeneralFunction::AVERAGE;
+ if ( nBits & PivotFunc::Median ) return ScGeneralFunction::MEDIAN;
+ if ( nBits & PivotFunc::Max ) return ScGeneralFunction::MAX;
+ if ( nBits & PivotFunc::Min ) return ScGeneralFunction::MIN;
+ if ( nBits & PivotFunc::Product ) return ScGeneralFunction::PRODUCT;
+ if ( nBits & PivotFunc::CountNum ) return ScGeneralFunction::COUNTNUMS;
+ if ( nBits & PivotFunc::StdDev ) return ScGeneralFunction::STDEV;
+ if ( nBits & PivotFunc::StdDevP ) return ScGeneralFunction::STDEVP;
+ if ( nBits & PivotFunc::StdVar ) return ScGeneralFunction::VAR;
+ if ( nBits & PivotFunc::StdVarP ) return ScGeneralFunction::VARP;
+ if ( nBits & PivotFunc::Auto ) return ScGeneralFunction::AUTO;
+ return ScGeneralFunction::NONE;
+}
+
+PivotFunc ScDataPilotConversion::FunctionBit( sal_Int16 eFunc )
+{
+ PivotFunc nRet = PivotFunc::NONE; // 0
+ switch (eFunc)
+ {
+ case GeneralFunction2::SUM: nRet = PivotFunc::Sum; break;
+ case GeneralFunction2::COUNT: nRet = PivotFunc::Count; break;
+ case GeneralFunction2::AVERAGE: nRet = PivotFunc::Average; break;
+ case GeneralFunction2::MEDIAN: nRet = PivotFunc::Median; break;
+ case GeneralFunction2::MAX: nRet = PivotFunc::Max; break;
+ case GeneralFunction2::MIN: nRet = PivotFunc::Min; break;
+ case GeneralFunction2::PRODUCT: nRet = PivotFunc::Product; break;
+ case GeneralFunction2::COUNTNUMS: nRet = PivotFunc::CountNum; break;
+ case GeneralFunction2::STDEV: nRet = PivotFunc::StdDev; break;
+ case GeneralFunction2::STDEVP: nRet = PivotFunc::StdDevP; break;
+ case GeneralFunction2::VAR: nRet = PivotFunc::StdVar; break;
+ case GeneralFunction2::VARP: nRet = PivotFunc::StdVarP; break;
+ case GeneralFunction2::AUTO: nRet = PivotFunc::Auto; break;
+ default:
+ {
+ assert(false);
+ }
+ }
+ return nRet;
+}
+
+void ScDataPilotConversion::FillGroupInfo( DataPilotFieldGroupInfo& rInfo, const ScDPNumGroupInfo& rGroupInfo )
+{
+ rInfo.HasDateValues = rGroupInfo.mbDateValues;
+ rInfo.HasAutoStart = rGroupInfo.mbAutoStart;
+ rInfo.Start = rGroupInfo.mfStart;
+ rInfo.HasAutoEnd = rGroupInfo.mbAutoEnd;
+ rInfo.End = rGroupInfo.mfEnd;
+ rInfo.Step = rGroupInfo.mfStep;
+}
+
+static ScDPObject* lcl_GetDPObject( ScDocShell* pDocShell, SCTAB nTab, std::u16string_view rName )
+{
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDPCollection* pColl = rDoc.GetDPCollection();
+ if ( pColl )
+ {
+ size_t nCount = pColl->GetCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ ScDPObject& rDPObj = (*pColl)[i];
+ if ( rDPObj.GetOutRange().aStart.Tab() == nTab &&
+ rDPObj.GetName() == rName )
+ return &rDPObj;
+ }
+ }
+ }
+ return nullptr; // not found
+}
+
+static OUString lcl_CreatePivotName( ScDocShell* pDocShell )
+{
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDPCollection* pColl = rDoc.GetDPCollection();
+ if ( pColl )
+ return pColl->CreateNewName();
+ }
+ return OUString(); // shouldn't happen
+}
+
+static sal_Int32 lcl_GetObjectIndex( ScDPObject* pDPObj, const ScFieldIdentifier& rFieldId )
+{
+ // used for items - nRepeat in identifier can be ignored
+ if ( pDPObj )
+ {
+ sal_Int32 nCount = pDPObj->GetDimCount();
+ for ( sal_Int32 nDim = 0; nDim < nCount; ++nDim )
+ {
+ bool bIsDataLayout = false;
+ OUString aDimName( pDPObj->GetDimName( nDim, bIsDataLayout ) );
+ if ( rFieldId.mbDataLayout ? bIsDataLayout : (aDimName == rFieldId.maFieldName) )
+ return nDim;
+ }
+ }
+ return -1; // none
+}
+
+ScDataPilotTablesObj::ScDataPilotTablesObj(ScDocShell& rDocSh, SCTAB nT) :
+ pDocShell( &rDocSh ),
+ nTab( nT )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDataPilotTablesObj::~ScDataPilotTablesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDataPilotTablesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! update of references
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XDataPilotTables
+
+rtl::Reference<ScDataPilotTableObj> ScDataPilotTablesObj::GetObjectByIndex_Impl( sal_Int32 nIndex )
+{
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDPCollection* pColl = rDoc.GetDPCollection();
+ if ( pColl )
+ {
+ // count tables on this sheet
+ sal_Int32 nFound = 0;
+ size_t nCount = pColl->GetCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ ScDPObject& rDPObj = (*pColl)[i];
+ if ( rDPObj.GetOutRange().aStart.Tab() == nTab )
+ {
+ if ( nFound == nIndex )
+ {
+ return new ScDataPilotTableObj(*pDocShell, nTab, rDPObj.GetName());
+ }
+ ++nFound;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+rtl::Reference<ScDataPilotTableObj> ScDataPilotTablesObj::GetObjectByName_Impl(const OUString& rName)
+{
+ if (hasByName(rName))
+ return new ScDataPilotTableObj(*pDocShell, nTab, rName);
+ return nullptr;
+}
+
+Reference<XDataPilotDescriptor> SAL_CALL ScDataPilotTablesObj::createDataPilotDescriptor()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScDataPilotDescriptor(*pDocShell);
+ return nullptr;
+}
+
+static bool lcl_IsDuplicated(const Reference<XPropertySet>& rDimProps)
+{
+ try
+ {
+ Any aAny = rDimProps->getPropertyValue( SC_UNO_DP_ORIGINAL );
+ Reference< XNamed > xOriginal( aAny, UNO_QUERY );
+ return xOriginal.is();
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+}
+
+static OUString lcl_GetOriginalName(const Reference< XNamed >& rDim)
+{
+ Reference< XNamed > xOriginal;
+
+ Reference< XPropertySet > xDimProps(rDim, UNO_QUERY);
+ if ( xDimProps.is() )
+ {
+ try
+ {
+ Any aAny = xDimProps->getPropertyValue(SC_UNO_DP_ORIGINAL);
+ aAny >>= xOriginal;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ if ( !xOriginal.is() )
+ xOriginal = rDim;
+
+ return xOriginal->getName();
+}
+
+void SAL_CALL ScDataPilotTablesObj::insertNewByName( const OUString& aNewName,
+ const CellAddress& aOutputAddress,
+ const Reference<XDataPilotDescriptor>& xDescriptor )
+{
+ SolarMutexGuard aGuard;
+ if (!xDescriptor.is()) return;
+
+ if ( !aNewName.isEmpty() && hasByName( aNewName ) )
+ throw IllegalArgumentException("Name \"" + aNewName + "\" already exists", getXWeak(), 0);
+
+ if (!pDocShell)
+ throw RuntimeException("DocShell is null", getXWeak());
+
+ auto pImp = dynamic_cast<ScDataPilotDescriptorBase*>( xDescriptor.get() );
+ if (!pImp)
+ throw RuntimeException("Failed to get ScDataPilotDescriptor", getXWeak());
+
+ ScDPObject* pNewObj = pImp->GetDPObject();
+ if (!pNewObj)
+ throw RuntimeException("Failed to get DPObject", getXWeak());
+
+ ScRange aOutputRange(static_cast<SCCOL>(aOutputAddress.Column), static_cast<SCROW>(aOutputAddress.Row), static_cast<SCTAB>(aOutputAddress.Sheet),
+ static_cast<SCCOL>(aOutputAddress.Column), static_cast<SCROW>(aOutputAddress.Row), static_cast<SCTAB>(aOutputAddress.Sheet));
+ pNewObj->SetOutRange(aOutputRange);
+ OUString aName = aNewName;
+ if (aName.isEmpty())
+ aName = lcl_CreatePivotName( pDocShell );
+ pNewObj->SetName(aName);
+ OUString aTag = xDescriptor->getTag();
+ pNewObj->SetTag(aTag);
+
+ // todo: handle double fields (for more information see ScDPObject)
+
+ ScDBDocFunc aFunc(*pDocShell);
+ if (!aFunc.CreatePivotTable(*pNewObj, true, true))
+ throw RuntimeException("Failed to create pivot table", getXWeak());
+}
+
+void SAL_CALL ScDataPilotTablesObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = lcl_GetDPObject( pDocShell, nTab, aName );
+ if (!pDPObj || !pDocShell)
+ throw RuntimeException(); // no other exceptions specified
+
+ ScDBDocFunc aFunc(*pDocShell);
+ aFunc.RemovePivotTable(*pDPObj, true, true); // remove - incl. undo etc.
+
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > SAL_CALL ScDataPilotTablesObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.DataPilotTablesEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDataPilotTablesObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDPCollection* pColl = rDoc.GetDPCollection();
+ if ( pColl )
+ {
+ // count tables on this sheet
+
+ sal_uInt16 nFound = 0;
+ size_t nCount = pColl->GetCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ ScDPObject& rDPObj = (*pColl)[i];
+ if ( rDPObj.GetOutRange().aStart.Tab() == nTab )
+ ++nFound;
+ }
+ return nFound;
+ }
+ }
+
+ return 0;
+}
+
+Any SAL_CALL ScDataPilotTablesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ Reference<XDataPilotTable2> xTable(GetObjectByIndex_Impl(nIndex));
+ if (!xTable.is())
+ throw IndexOutOfBoundsException();
+ return Any( xTable );
+}
+
+uno::Type SAL_CALL ScDataPilotTablesObj::getElementType()
+{
+ return cppu::UnoType<XDataPilotTable2>::get();
+}
+
+sal_Bool SAL_CALL ScDataPilotTablesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XNameAccess
+
+Any SAL_CALL ScDataPilotTablesObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ Reference<XDataPilotTable2> xTable(GetObjectByName_Impl(aName));
+ if (!xTable.is())
+ throw NoSuchElementException();
+ return Any( xTable );
+}
+
+Sequence<OUString> SAL_CALL ScDataPilotTablesObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDPCollection* pColl = rDoc.GetDPCollection();
+ if ( pColl )
+ {
+ // count tables on this sheet
+
+ sal_uInt16 nFound = 0;
+ size_t nCount = pColl->GetCount();
+ size_t i;
+ for (i=0; i<nCount; ++i)
+ {
+ ScDPObject& rDPObj = (*pColl)[i];
+ if ( rDPObj.GetOutRange().aStart.Tab() == nTab )
+ ++nFound;
+ }
+
+ sal_uInt16 nPos = 0;
+ Sequence<OUString> aSeq(nFound);
+ OUString* pAry = aSeq.getArray();
+ for (i=0; i<nCount; ++i)
+ {
+ ScDPObject& rDPObj = (*pColl)[i];
+ if ( rDPObj.GetOutRange().aStart.Tab() == nTab )
+ pAry[nPos++] = rDPObj.GetName();
+ }
+
+ return aSeq;
+ }
+ }
+ return {};
+}
+
+sal_Bool SAL_CALL ScDataPilotTablesObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDPCollection* pColl = rDoc.GetDPCollection();
+ if ( pColl )
+ {
+ size_t nCount = pColl->GetCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ ScDPObject& rDPObj = (*pColl)[i];
+ if ( rDPObj.GetOutRange().aStart.Tab() == nTab &&
+ rDPObj.GetName() == aName )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ScDataPilotDescriptorBase::ScDataPilotDescriptorBase(ScDocShell& rDocSh) :
+ maPropSet( lcl_GetDataPilotDescriptorBaseMap() ),
+ pDocShell( &rDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDataPilotDescriptorBase::~ScDataPilotDescriptorBase()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDataPilotDescriptorBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! update of references ?
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XDataPilotDescriptor
+
+CellRangeAddress SAL_CALL ScDataPilotDescriptorBase::getSourceRange()
+{
+ SolarMutexGuard aGuard;
+
+ ScDPObject* pDPObject(GetDPObject());
+ if (!pDPObject)
+ throw RuntimeException("Failed to get DPObject", getXWeak());
+
+ CellRangeAddress aRet;
+ if (pDPObject->IsSheetData())
+ ScUnoConversion::FillApiRange( aRet, pDPObject->GetSheetDesc()->GetSourceRange() );
+ return aRet;
+}
+
+void SAL_CALL ScDataPilotDescriptorBase::setSourceRange( const CellRangeAddress& aSourceRange )
+{
+ SolarMutexGuard aGuard;
+
+ ScDPObject* pDPObject = GetDPObject();
+ if (!pDPObject)
+ throw RuntimeException("Failed to get DPObject", getXWeak());
+
+ ScSheetSourceDesc aSheetDesc(&pDocShell->GetDocument());
+ if (pDPObject->IsSheetData())
+ aSheetDesc = *pDPObject->GetSheetDesc();
+
+ ScRange aRange;
+ ScUnoConversion::FillScRange(aRange, aSourceRange);
+ aSheetDesc.SetSourceRange(aRange);
+ pDPObject->SetSheetDesc( aSheetDesc );
+ SetDPObject( pDPObject );
+}
+
+Reference<XSheetFilterDescriptor> SAL_CALL ScDataPilotDescriptorBase::getFilterDescriptor()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFilterDescriptor( pDocShell, this );
+}
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getDataPilotFields()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFieldsObj( *this );
+}
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getColumnFields()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_COLUMN );
+}
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getRowFields()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_ROW );
+}
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getPageFields()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_PAGE );
+}
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getDataFields()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_DATA );
+}
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotDescriptorBase::getHiddenFields()
+{
+ SolarMutexGuard aGuard;
+ return new ScDataPilotFieldsObj( *this, DataPilotFieldOrientation_HIDDEN );
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL ScDataPilotDescriptorBase::getPropertySetInfo( )
+{
+ SolarMutexGuard aGuard;
+ static Reference<XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() );
+ return aRef;
+}
+
+void SAL_CALL ScDataPilotDescriptorBase::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObject = GetDPObject();
+ if (!pDPObject)
+ return;
+
+ ScDPSaveData* pOldData = pDPObject->GetSaveData();
+ OSL_ENSURE(pOldData, "Here should be a SaveData");
+ if ( pOldData )
+ {
+ ScDPSaveData aNewData( *pOldData );
+
+ if ( aPropertyName == SC_UNO_DP_COLGRAND )
+ {
+ aNewData.SetColumnGrand(::cppu::any2bool( aValue ));
+ }
+ else if ( aPropertyName == SC_UNO_DP_IGNORE_EMPTYROWS )
+ {
+ aNewData.SetIgnoreEmptyRows(::cppu::any2bool( aValue ));
+ }
+ else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
+ {
+ aNewData.SetRepeatIfEmpty(::cppu::any2bool( aValue ));
+ }
+ else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
+ {
+ aNewData.SetRowGrand(::cppu::any2bool( aValue ));
+ }
+ else if ( aPropertyName == SC_UNO_DP_SHOWFILTER )
+ {
+ aNewData.SetFilterButton(::cppu::any2bool( aValue ));
+ }
+ else if ( aPropertyName == SC_UNO_DP_DRILLDOWN )
+ {
+ aNewData.SetDrillDown(::cppu::any2bool( aValue ));
+ }
+ else if ( aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME )
+ {
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ aNewData.SetGrandTotalName(aStrVal);
+ }
+ else if ( aPropertyName == SC_UNO_DP_IMPORTDESC )
+ {
+ uno::Sequence<beans::PropertyValue> aArgSeq;
+ if ( aValue >>= aArgSeq )
+ {
+ ScImportSourceDesc aImportDesc(&pDocShell->GetDocument());
+
+ const ScImportSourceDesc* pOldDesc = pDPObject->GetImportSourceDesc();
+ if (pOldDesc)
+ aImportDesc = *pOldDesc;
+
+ ScImportParam aParam;
+ ScImportDescriptor::FillImportParam( aParam, aArgSeq );
+
+ sheet::DataImportMode nNewType = sheet::DataImportMode_NONE;
+ if ( aParam.bImport )
+ {
+ if ( aParam.bSql )
+ nNewType = sheet::DataImportMode_SQL;
+ else if ( aParam.nType == ScDbQuery )
+ nNewType = sheet::DataImportMode_QUERY;
+ else
+ nNewType = sheet::DataImportMode_TABLE;
+ }
+ aImportDesc.nType = nNewType;
+ aImportDesc.aDBName = aParam.aDBName;
+ aImportDesc.aObject = aParam.aStatement;
+ aImportDesc.bNative = aParam.bNative;
+
+ pDPObject->SetImportDesc( aImportDesc );
+ }
+ }
+ else if ( aPropertyName == SC_UNO_DP_SOURCESERVICE )
+ {
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ {
+ ScDPServiceDesc aServiceDesc("", "", "", "", "");
+
+ const ScDPServiceDesc* pOldDesc = pDPObject->GetDPServiceDesc();
+ if (pOldDesc)
+ aServiceDesc = *pOldDesc;
+
+ aServiceDesc.aServiceName = aStrVal;
+
+ pDPObject->SetServiceData( aServiceDesc );
+ }
+ }
+ else if ( aPropertyName == SC_UNO_DP_SERVICEARG )
+ {
+ uno::Sequence<beans::PropertyValue> aArgSeq;
+ if ( aValue >>= aArgSeq )
+ {
+ ScDPServiceDesc aServiceDesc("", "", "", "", "");
+
+ const ScDPServiceDesc* pOldDesc = pDPObject->GetDPServiceDesc();
+ if (pOldDesc)
+ aServiceDesc = *pOldDesc;
+
+ OUString aStrVal;
+ for (const beans::PropertyValue& rProp : std::as_const(aArgSeq))
+ {
+ OUString aPropName(rProp.Name);
+
+ if (aPropName == SC_UNO_DP_SOURCENAME)
+ {
+ if ( rProp.Value >>= aStrVal )
+ aServiceDesc.aParSource = aStrVal;
+ }
+ else if (aPropName == SC_UNO_DP_OBJECTNAME)
+ {
+ if ( rProp.Value >>= aStrVal )
+ aServiceDesc.aParName = aStrVal;
+ }
+ else if (aPropName == SC_UNO_DP_USERNAME)
+ {
+ if ( rProp.Value >>= aStrVal )
+ aServiceDesc.aParUser = aStrVal;
+ }
+ else if (aPropName == SC_UNO_DP_PASSWORD)
+ {
+ if ( rProp.Value >>= aStrVal )
+ aServiceDesc.aParPass = aStrVal;
+ }
+ }
+
+ pDPObject->SetServiceData( aServiceDesc );
+ }
+ }
+ else
+ throw UnknownPropertyException(aPropertyName);
+
+ pDPObject->SetSaveData( aNewData );
+ }
+
+ SetDPObject(pDPObject);
+}
+
+Any SAL_CALL ScDataPilotDescriptorBase::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ Any aRet;
+
+ ScDPObject* pDPObject(GetDPObject());
+ if (pDPObject)
+ {
+ ScDPSaveData* pOldData = pDPObject->GetSaveData();
+ OSL_ENSURE(pOldData, "Here should be a SaveData");
+ if ( pOldData )
+ {
+ ScDPSaveData aNewData( *pOldData );
+
+ if ( aPropertyName == SC_UNO_DP_COLGRAND )
+ {
+ aRet <<= aNewData.GetColumnGrand();
+ }
+ else if ( aPropertyName == SC_UNO_DP_IGNORE_EMPTYROWS )
+ {
+ aRet <<= aNewData.GetIgnoreEmptyRows();
+ }
+ else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
+ {
+ aRet <<= aNewData.GetRepeatIfEmpty();
+ }
+ else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
+ {
+ aRet <<= aNewData.GetRowGrand();
+ }
+ else if ( aPropertyName == SC_UNO_DP_SHOWFILTER )
+ {
+ aRet <<= aNewData.GetFilterButton();
+ }
+ else if ( aPropertyName == SC_UNO_DP_DRILLDOWN )
+ {
+ aRet <<= aNewData.GetDrillDown();
+ }
+ else if ( aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME )
+ {
+ const std::optional<OUString> & pGrandTotalName = aNewData.GetGrandTotalName();
+ if (pGrandTotalName)
+ aRet <<= *pGrandTotalName; // same behavior as in ScDPSource
+ }
+ else if ( aPropertyName == SC_UNO_DP_IMPORTDESC )
+ {
+ const ScImportSourceDesc* pImportDesc = pDPObject->GetImportSourceDesc();
+ if ( pImportDesc )
+ {
+ // fill ScImportParam so ScImportDescriptor::FillProperties can be used
+ ScImportParam aParam;
+ aParam.bImport = ( pImportDesc->nType != sheet::DataImportMode_NONE );
+ aParam.aDBName = pImportDesc->aDBName;
+ aParam.aStatement = pImportDesc->aObject;
+ aParam.bNative = pImportDesc->bNative;
+ aParam.bSql = ( pImportDesc->nType == sheet::DataImportMode_SQL );
+ aParam.nType = static_cast<sal_uInt8>(( pImportDesc->nType == sheet::DataImportMode_QUERY ) ? ScDbQuery : ScDbTable);
+
+ uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() );
+ ScImportDescriptor::FillProperties( aSeq, aParam );
+ aRet <<= aSeq;
+ }
+ else
+ {
+ // empty sequence
+ uno::Sequence<beans::PropertyValue> aEmpty(0);
+ aRet <<= aEmpty;
+ }
+ }
+ else if ( aPropertyName == SC_UNO_DP_SOURCESERVICE )
+ {
+ OUString aServiceName;
+ const ScDPServiceDesc* pServiceDesc = pDPObject->GetDPServiceDesc();
+ if (pServiceDesc)
+ aServiceName = pServiceDesc->aServiceName;
+ aRet <<= aServiceName; // empty string if no ServiceDesc set
+ }
+ else if ( aPropertyName == SC_UNO_DP_SERVICEARG )
+ {
+ const ScDPServiceDesc* pServiceDesc = pDPObject->GetDPServiceDesc();
+ if (pServiceDesc)
+ {
+ uno::Sequence<beans::PropertyValue> aSeq( comphelper::InitPropertySequence({
+ { SC_UNO_DP_SOURCENAME, Any(pServiceDesc->aParSource) },
+ { SC_UNO_DP_OBJECTNAME, Any(pServiceDesc->aParName) },
+ { SC_UNO_DP_USERNAME, Any(pServiceDesc->aParUser) },
+ { SC_UNO_DP_PASSWORD, Any(pServiceDesc->aParPass) }
+ }));
+ aRet <<= aSeq;
+ }
+ else
+ {
+ // empty sequence
+ uno::Sequence<beans::PropertyValue> aEmpty;
+ aRet <<= aEmpty;
+ }
+ }
+ else
+ throw UnknownPropertyException(aPropertyName);
+ }
+ }
+
+ return aRet;
+}
+
+void SAL_CALL ScDataPilotDescriptorBase::addPropertyChangeListener(
+ const OUString& /* aPropertyName */, const Reference<XPropertyChangeListener >& /* xListener */ )
+{
+}
+
+void SAL_CALL ScDataPilotDescriptorBase::removePropertyChangeListener(
+ const OUString& /* aPropertyName */, const Reference<XPropertyChangeListener >& /* aListener */ )
+{
+}
+
+void SAL_CALL ScDataPilotDescriptorBase::addVetoableChangeListener(
+ const OUString& /* PropertyName */, const Reference<XVetoableChangeListener >& /* aListener */ )
+{
+}
+
+void SAL_CALL ScDataPilotDescriptorBase::removeVetoableChangeListener(
+ const OUString& /* PropertyName */, const Reference<XVetoableChangeListener >& /* aListener */ )
+{
+}
+
+// XDataPilotDataLayoutFieldSupplier
+
+Reference< XDataPilotField > SAL_CALL ScDataPilotDescriptorBase::getDataLayoutField()
+{
+ SolarMutexGuard aGuard;
+ if( ScDPObject* pDPObject = GetDPObject() )
+ {
+ if( ScDPSaveData* pSaveData = pDPObject->GetSaveData() )
+ {
+ if( pSaveData->GetDataLayoutDimension() )
+ {
+ ScFieldIdentifier aFieldId( SC_DATALAYOUT_NAME, true );
+ return new ScDataPilotFieldObj( *this, aFieldId );
+ }
+ }
+ }
+ return nullptr;
+}
+
+ScDataPilotTableObj::ScDataPilotTableObj(ScDocShell& rDocSh, SCTAB nT, OUString aN) :
+ ScDataPilotDescriptorBase( rDocSh ),
+ nTab( nT ),
+ aName(std::move( aN )),
+ aModifyListeners( 0 )
+{
+}
+
+ScDataPilotTableObj::~ScDataPilotTableObj()
+{
+}
+
+Any SAL_CALL ScDataPilotTableObj::queryInterface( const uno::Type& rType )
+{
+ // since we manually do resolve the query for XDataPilotTable2
+ // we also need to do the same for XDataPilotTable
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<XDataPilotTable*>(this),
+ static_cast<XDataPilotTable2*>(this),
+ static_cast<XModifyBroadcaster*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return ScDataPilotDescriptorBase::queryInterface( rType );
+}
+
+void SAL_CALL ScDataPilotTableObj::acquire() noexcept
+{
+ ScDataPilotDescriptorBase::acquire();
+}
+
+void SAL_CALL ScDataPilotTableObj::release() noexcept
+{
+ ScDataPilotDescriptorBase::release();
+}
+
+Sequence< uno::Type > SAL_CALL ScDataPilotTableObj::getTypes()
+{
+ return comphelper::concatSequences(
+ ScDataPilotDescriptorBase::getTypes(),
+ Sequence< uno::Type >
+ {
+ cppu::UnoType<XDataPilotTable2>::get(),
+ cppu::UnoType<XModifyBroadcaster>::get()
+ } );
+}
+
+Sequence<sal_Int8> SAL_CALL ScDataPilotTableObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+ScDPObject* ScDataPilotTableObj::GetDPObject() const
+{
+ return lcl_GetDPObject(GetDocShell(), nTab, aName);
+}
+
+void ScDataPilotTableObj::SetDPObject( ScDPObject* pDPObject )
+{
+ ScDocShell* pDocSh = GetDocShell();
+ ScDPObject* pDPObj = lcl_GetDPObject(pDocSh, nTab, aName);
+ if ( pDPObj && pDocSh )
+ {
+ ScDBDocFunc aFunc(*pDocSh);
+ aFunc.DataPilotUpdate( pDPObj, pDPObject, true, true );
+ }
+}
+
+// "rest of XDataPilotDescriptor"
+
+OUString SAL_CALL ScDataPilotTableObj::getName()
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName);
+ if (pDPObj)
+ return pDPObj->GetName();
+ return OUString();
+}
+
+void SAL_CALL ScDataPilotTableObj::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName);
+ if (pDPObj)
+ {
+ //! test for existing names !!!
+
+ pDPObj->SetName( aNewName ); //! Undo - DBDocFunc ???
+ aName = aNewName;
+
+ // DataPilotUpdate would do too much (output table is not changed)
+ GetDocShell()->SetDocumentModified();
+ }
+}
+
+OUString SAL_CALL ScDataPilotTableObj::getTag()
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName);
+ if (pDPObj)
+ return pDPObj->GetTag();
+ return OUString();
+}
+
+void SAL_CALL ScDataPilotTableObj::setTag( const OUString& aNewTag )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName);
+ if (pDPObj)
+ {
+ pDPObj->SetTag( aNewTag ); //! Undo - DBDocFunc ???
+
+ // DataPilotUpdate would do too much (output table is not changed)
+ GetDocShell()->SetDocumentModified();
+ }
+}
+
+// XDataPilotTable
+
+CellRangeAddress SAL_CALL ScDataPilotTableObj::getOutputRange()
+{
+ SolarMutexGuard aGuard;
+ CellRangeAddress aRet;
+ ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName);
+ if (pDPObj)
+ {
+ ScRange aRange(pDPObj->GetOutRange());
+ aRet.Sheet = aRange.aStart.Tab();
+ aRet.StartColumn = aRange.aStart.Col();
+ aRet.StartRow = aRange.aStart.Row();
+ aRet.EndColumn = aRange.aEnd.Col();
+ aRet.EndRow = aRange.aEnd.Row();
+ }
+ return aRet;
+}
+
+void SAL_CALL ScDataPilotTableObj::refresh()
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName);
+ if (pDPObj)
+ {
+ ScDBDocFunc aFunc(*GetDocShell());
+ aFunc.RefreshPivotTables(pDPObj, true);
+ }
+}
+
+Sequence< Sequence<Any> > SAL_CALL ScDataPilotTableObj::getDrillDownData(const CellAddress& aAddr)
+{
+ SolarMutexGuard aGuard;
+ Sequence< Sequence<Any> > aTabData;
+ ScAddress aAddr2(static_cast<SCCOL>(aAddr.Column), static_cast<SCROW>(aAddr.Row), aAddr.Sheet);
+ ScDPObject* pObj = GetDPObject();
+ if (!pObj)
+ throw RuntimeException("Failed to get DPObject", getXWeak());
+
+ pObj->GetDrillDownData(aAddr2, aTabData);
+ return aTabData;
+}
+
+DataPilotTablePositionData SAL_CALL ScDataPilotTableObj::getPositionData(const CellAddress& aAddr)
+{
+ SolarMutexGuard aGuard;
+ DataPilotTablePositionData aPosData;
+ ScAddress aAddr2(static_cast<SCCOL>(aAddr.Column), static_cast<SCROW>(aAddr.Row), aAddr.Sheet);
+ ScDPObject* pObj = GetDPObject();
+ if (!pObj)
+ throw RuntimeException("Failed to get DPObject", getXWeak());
+
+ pObj->GetPositionData(aAddr2, aPosData);
+ return aPosData;
+}
+
+void SAL_CALL ScDataPilotTableObj::insertDrillDownSheet(const CellAddress& aAddr)
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = GetDPObject();
+ if (!pDPObj)
+ throw RuntimeException("Failed to get DPObject", getXWeak());
+ ScTabViewShell* pViewSh = GetDocShell()->GetBestViewShell();
+ if (!pViewSh)
+ throw RuntimeException("Failed to get ViewShell", getXWeak());
+
+ Sequence<DataPilotFieldFilter> aFilters;
+ pDPObj->GetDataFieldPositionData(
+ ScAddress(static_cast<SCCOL>(aAddr.Column), static_cast<SCROW>(aAddr.Row), aAddr.Sheet), aFilters);
+ pViewSh->ShowDataPilotSourceData(*pDPObj, aFilters);
+}
+
+CellRangeAddress SAL_CALL ScDataPilotTableObj::getOutputRangeByType( sal_Int32 nType )
+{
+ SolarMutexGuard aGuard;
+ if (nType < 0 || nType > DataPilotOutputRangeType::RESULT)
+ throw IllegalArgumentException("nType must be between 0 and " +
+ OUString::number(DataPilotOutputRangeType::RESULT) + ", got " + OUString::number(nType),
+ getXWeak(), 0);
+
+ CellRangeAddress aRet;
+ if (ScDPObject* pDPObj = lcl_GetDPObject(GetDocShell(), nTab, aName))
+ ScUnoConversion::FillApiRange( aRet, pDPObj->GetOutputRangeByType( nType ) );
+ return aRet;
+}
+
+void SAL_CALL ScDataPilotTableObj::addModifyListener( const uno::Reference<util::XModifyListener>& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ aModifyListeners.emplace_back( aListener );
+
+ if ( aModifyListeners.size() == 1 )
+ {
+ acquire(); // don't lose this object (one ref for all listeners)
+ }
+}
+
+void SAL_CALL ScDataPilotTableObj::removeModifyListener( const uno::Reference<util::XModifyListener>& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ rtl::Reference<ScDataPilotTableObj> xSelfHold(this); // in case the listeners have the last ref
+
+ sal_uInt16 nCount = aModifyListeners.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ {
+ uno::Reference<util::XModifyListener>& rObj = aModifyListeners[n];
+ if ( rObj == aListener )
+ {
+ aModifyListeners.erase( aModifyListeners.begin() + n );
+
+ if ( aModifyListeners.empty() )
+ {
+ release(); // release the ref for the listeners
+ }
+
+ break;
+ }
+ }
+}
+
+void ScDataPilotTableObj::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( auto pDataPilotHint = dynamic_cast<const ScDataPilotModifiedHint*>(&rHint) )
+ {
+ if (pDataPilotHint->GetName() == aName)
+ Refreshed_Impl();
+ }
+ else if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ ScRange aRange( 0, 0, nTab );
+ ScRangeList aRanges( aRange );
+ if ( aRanges.UpdateReference( pRefHint->GetMode(), &GetDocShell()->GetDocument(), pRefHint->GetRange(),
+ pRefHint->GetDx(), pRefHint->GetDy(), pRefHint->GetDz() ) &&
+ aRanges.size() == 1 )
+ {
+ nTab = aRanges.front().aStart.Tab();
+ }
+ }
+
+ ScDataPilotDescriptorBase::Notify( rBC, rHint );
+}
+
+void ScDataPilotTableObj::Refreshed_Impl()
+{
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+
+ // the EventObject holds a Ref to this object until after the listener calls
+
+ ScDocument& rDoc = GetDocShell()->GetDocument();
+ for (const uno::Reference<util::XModifyListener> & xModifyListener : aModifyListeners)
+ rDoc.AddUnoListenerCall( xModifyListener, aEvent );
+}
+
+ScDataPilotDescriptor::ScDataPilotDescriptor(ScDocShell& rDocSh) :
+ ScDataPilotDescriptorBase( rDocSh ),
+ mpDPObject(new ScDPObject(&rDocSh.GetDocument()))
+{
+ ScDPSaveData aSaveData;
+ // set defaults like in ScPivotParam constructor
+ aSaveData.SetColumnGrand( true );
+ aSaveData.SetRowGrand( true );
+ aSaveData.SetIgnoreEmptyRows( false );
+ aSaveData.SetRepeatIfEmpty( false );
+ mpDPObject->SetSaveData(aSaveData);
+ ScSheetSourceDesc aSheetDesc(&rDocSh.GetDocument());
+ mpDPObject->SetSheetDesc(aSheetDesc);
+}
+
+ScDataPilotDescriptor::~ScDataPilotDescriptor()
+{
+}
+
+ScDPObject* ScDataPilotDescriptor::GetDPObject() const
+{
+ return mpDPObject.get();
+}
+
+void ScDataPilotDescriptor::SetDPObject( ScDPObject* pDPObject )
+{
+ if (mpDPObject.get() != pDPObject)
+ {
+ mpDPObject.reset( pDPObject );
+ OSL_FAIL("replace DPObject should not happen");
+ }
+}
+
+// "rest of XDataPilotDescriptor"
+
+OUString SAL_CALL ScDataPilotDescriptor::getName()
+{
+ SolarMutexGuard aGuard;
+ return mpDPObject->GetName();
+}
+
+void SAL_CALL ScDataPilotDescriptor::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ mpDPObject->SetName( aNewName );
+}
+
+OUString SAL_CALL ScDataPilotDescriptor::getTag()
+{
+ SolarMutexGuard aGuard;
+ return mpDPObject->GetTag();
+}
+
+void SAL_CALL ScDataPilotDescriptor::setTag( const OUString& aNewTag )
+{
+ SolarMutexGuard aGuard;
+ mpDPObject->SetTag( aNewTag );
+}
+
+ScDataPilotChildObjBase::ScDataPilotChildObjBase( ScDataPilotDescriptorBase& rParent ) :
+ mxParent( &rParent )
+{
+}
+
+ScDataPilotChildObjBase::ScDataPilotChildObjBase( ScDataPilotDescriptorBase& rParent, ScFieldIdentifier aFieldId ) :
+ mxParent( &rParent ),
+ maFieldId(std::move( aFieldId ))
+{
+}
+
+ScDataPilotChildObjBase::~ScDataPilotChildObjBase()
+{
+}
+
+ScDPObject* ScDataPilotChildObjBase::GetDPObject() const
+{
+ return mxParent->GetDPObject();
+}
+
+void ScDataPilotChildObjBase::SetDPObject( ScDPObject* pDPObject )
+{
+ mxParent->SetDPObject( pDPObject );
+}
+
+ScDPSaveDimension* ScDataPilotChildObjBase::GetDPDimension( ScDPObject** ppDPObject ) const
+{
+ if( ScDPObject* pDPObj = GetDPObject() )
+ {
+ if( ppDPObject ) *ppDPObject = pDPObj;
+ if( ScDPSaveData* pSaveData = pDPObj->GetSaveData() )
+ {
+ if( maFieldId.mbDataLayout )
+ return pSaveData->GetDataLayoutDimension();
+
+ if( maFieldId.mnFieldIdx == 0 )
+ return pSaveData->GetDimensionByName( maFieldId.maFieldName );
+
+ // find dimension with specified index (search in duplicated dimensions)
+ const ScDPSaveData::DimsType& rDims = pSaveData->GetDimensions();
+
+ sal_Int32 nFoundIdx = 0;
+ for (auto const& it : rDims)
+ {
+ if (it->IsDataLayout())
+ continue;
+
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(it->GetName());
+ if (aSrcName == maFieldId.maFieldName)
+ {
+ if( nFoundIdx == maFieldId.mnFieldIdx )
+ return it.get();
+ ++nFoundIdx;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+sal_Int32 ScDataPilotChildObjBase::GetMemberCount() const
+{
+ sal_Int32 nRet = 0;
+ Reference<XNameAccess> xMembersNA = GetMembers();
+ if (xMembersNA.is())
+ {
+ Reference< XIndexAccess > xMembersIA( new ScNameToIndexAccess( xMembersNA ) );
+ nRet = xMembersIA->getCount();
+ }
+ return nRet;
+}
+
+Reference< XMembersAccess > ScDataPilotChildObjBase::GetMembers() const
+{
+ Reference< XMembersAccess > xMembersNA;
+ if( ScDPObject* pDPObj = GetDPObject() )
+ pDPObj->GetMembersNA( lcl_GetObjectIndex( pDPObj, maFieldId ), xMembersNA );
+ return xMembersNA;
+}
+
+ScDocShell* ScDataPilotChildObjBase::GetDocShell() const
+{
+ return mxParent->GetDocShell();
+}
+
+ScDataPilotFieldsObj::ScDataPilotFieldsObj( ScDataPilotDescriptorBase& rParent ) :
+ ScDataPilotChildObjBase( rParent )
+{
+}
+
+ScDataPilotFieldsObj::ScDataPilotFieldsObj( ScDataPilotDescriptorBase& rParent, DataPilotFieldOrientation eOrient ) :
+ ScDataPilotChildObjBase( rParent ),
+ maOrient( eOrient )
+{
+}
+
+ScDataPilotFieldsObj::~ScDataPilotFieldsObj()
+{
+}
+
+static sal_Int32 lcl_GetFieldCount( const Reference<XDimensionsSupplier>& rSource, const Any& rOrient )
+{
+ if (!rSource.is())
+ throw NullPointerException();
+
+ sal_Int32 nRet = 0;
+
+ Reference<XNameAccess> xDimsName(rSource->getDimensions());
+ Reference<XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
+ sal_Int32 nIntCount = xIntDims->getCount();
+ for (sal_Int32 i = 0; i < nIntCount; ++i)
+ {
+ Reference<XPropertySet> xDim(xIntDims->getByIndex(i), UNO_QUERY);
+ const bool bMatch = xDim
+ && (rOrient.hasValue()
+ // all fields of the specified orientation, including duplicated
+ ? (xDim->getPropertyValue(SC_UNO_DP_ORIENTATION) == rOrient)
+ // count all non-duplicated fields
+ : !lcl_IsDuplicated(xDim));
+ if (bMatch)
+ ++nRet;
+ }
+
+ return nRet;
+}
+
+static bool lcl_GetFieldDataByIndex( const Reference<XDimensionsSupplier>& rSource,
+ const Any& rOrient, SCSIZE nIndex, ScFieldIdentifier& rFieldId )
+{
+ if (!rSource.is())
+ throw NullPointerException();
+
+ bool bOk = false;
+ SCSIZE nPos = 0;
+ sal_Int32 nDimIndex = 0;
+
+ Reference<XNameAccess> xDimsName(rSource->getDimensions());
+ Reference<XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
+ sal_Int32 nIntCount = xIntDims->getCount();
+ Reference<XPropertySet> xDim;
+ for (sal_Int32 i = 0; i < nIntCount; ++i)
+ {
+ xDim.set(xIntDims->getByIndex(i), UNO_QUERY);
+ const bool bMatch = xDim
+ && (rOrient.hasValue()
+ ? (xDim->getPropertyValue(SC_UNO_DP_ORIENTATION) == rOrient)
+ : !lcl_IsDuplicated(xDim));
+ if (bMatch)
+ {
+ if (nPos == nIndex)
+ {
+ bOk = true;
+ nDimIndex = i;
+ break;
+ }
+ else
+ ++nPos;
+ }
+ }
+
+ if ( bOk )
+ {
+ xDim.set( xIntDims->getByIndex(nDimIndex), UNO_QUERY );
+ Reference<XNamed> xDimName( xDim, UNO_QUERY );
+ if ( xDimName.is() )
+ {
+ OUString sOriginalName( lcl_GetOriginalName( xDimName ) );
+ rFieldId.maFieldName = sOriginalName;
+ rFieldId.mbDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDim,
+ SC_UNO_DP_ISDATALAYOUT );
+
+ sal_Int32 nRepeat = 0;
+ if ( rOrient.hasValue() && lcl_IsDuplicated( xDim ) )
+ {
+ // find the repeat count
+ // (this relies on the original dimension always being before the duplicates)
+
+ Reference<XNamed> xPrevName;
+ for (sal_Int32 i = 0; i < nDimIndex; ++i)
+ {
+ xPrevName.set( xIntDims->getByIndex(i), UNO_QUERY );
+ if ( xPrevName.is() && lcl_GetOriginalName( xPrevName ) == sOriginalName )
+ ++nRepeat;
+ }
+ }
+ rFieldId.mnFieldIdx = nRepeat;
+ }
+ else
+ bOk = false;
+ }
+
+ return bOk;
+}
+
+static bool lcl_GetFieldDataByName( ScDPObject* pDPObj, const OUString& rFieldName, ScFieldIdentifier& rFieldId )
+{
+ // "By name" is always the first match.
+ // The name "Data" always refers to the data layout field.
+ rFieldId.maFieldName = rFieldName;
+ rFieldId.mnFieldIdx = 0;
+ rFieldId.mbDataLayout = rFieldName == SC_DATALAYOUT_NAME;
+
+ pDPObj->GetSource(); // IsDimNameInUse doesn't update source data
+
+ // check if the named field exists (not for data layout)
+ return rFieldId.mbDataLayout || pDPObj->IsDimNameInUse( rFieldName );
+}
+
+// XDataPilotFields
+
+rtl::Reference<ScDataPilotFieldObj> ScDataPilotFieldsObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) const
+{
+ if (ScDPObject* pObj = GetDPObject())
+ {
+ ScFieldIdentifier aFieldId;
+ if (lcl_GetFieldDataByIndex( pObj->GetSource(), maOrient, nIndex, aFieldId ))
+ return new ScDataPilotFieldObj( *mxParent, aFieldId, maOrient );
+ }
+ return nullptr;
+}
+
+rtl::Reference<ScDataPilotFieldObj> ScDataPilotFieldsObj::GetObjectByName_Impl(const OUString& aName) const
+{
+ if (ScDPObject* pDPObj = GetDPObject())
+ {
+ ScFieldIdentifier aFieldId;
+ if (lcl_GetFieldDataByName( pDPObj, aName, aFieldId ))
+ return new ScDataPilotFieldObj( *mxParent, aFieldId, maOrient );
+ }
+ return nullptr;
+}
+
+// XEnumerationAccess
+
+Reference<XEnumeration> SAL_CALL ScDataPilotFieldsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.DataPilotFieldsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDataPilotFieldsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = GetDPObject();
+ return pDPObj ? lcl_GetFieldCount( pDPObj->GetSource(), maOrient ) : 0;
+}
+
+Any SAL_CALL ScDataPilotFieldsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ Reference< XPropertySet > xField( GetObjectByIndex_Impl( nIndex ) );
+ if (!xField.is())
+ throw IndexOutOfBoundsException();
+ return Any( xField );
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScDataPilotFieldsObj::getElementType()
+{
+ return cppu::UnoType<XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL ScDataPilotFieldsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XNameAccess
+
+Any SAL_CALL ScDataPilotFieldsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ Reference<XPropertySet> xField(GetObjectByName_Impl(aName));
+ if (!xField.is())
+ throw NoSuchElementException();
+ return Any( xField );
+}
+
+Sequence<OUString> SAL_CALL ScDataPilotFieldsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if (ScDPObject* pDPObj = GetDPObject())
+ {
+ Sequence< OUString > aSeq( lcl_GetFieldCount( pDPObj->GetSource(), maOrient ) );
+ OUString* pAry = aSeq.getArray();
+
+ const ScDPSaveData::DimsType& rDimensions = pDPObj->GetSaveData()->GetDimensions();
+ for (auto const& it : rDimensions)
+ {
+ if(maOrient.hasValue() && (it->GetOrientation() == maOrient.get< DataPilotFieldOrientation >()))
+ {
+ *pAry = it->GetName();
+ ++pAry;
+ }
+ }
+ return aSeq;
+ }
+ return Sequence<OUString>();
+}
+
+sal_Bool SAL_CALL ScDataPilotFieldsObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ return GetObjectByName_Impl(aName) != nullptr;
+}
+
+ScDataPilotFieldObj::ScDataPilotFieldObj(
+ ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId ) :
+ ScDataPilotChildObjBase( rParent, rFieldId ),
+ maPropSet( lcl_GetDataPilotFieldMap() )
+{
+}
+
+ScDataPilotFieldObj::ScDataPilotFieldObj( ScDataPilotDescriptorBase& rParent,
+ const ScFieldIdentifier& rFieldId, Any aOrient ) :
+ ScDataPilotChildObjBase( rParent, rFieldId ),
+ maPropSet( lcl_GetDataPilotFieldMap() ),
+ maOrient(std::move( aOrient ))
+{
+}
+
+ScDataPilotFieldObj::~ScDataPilotFieldObj()
+{
+}
+
+// XNamed
+
+OUString SAL_CALL ScDataPilotFieldObj::getName()
+{
+ SolarMutexGuard aGuard;
+ OUString aName;
+ if( ScDPSaveDimension* pDim = GetDPDimension() )
+ {
+ if( pDim->IsDataLayout() )
+ aName = SC_DATALAYOUT_NAME;
+ else
+ {
+ const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
+ if (pLayoutName)
+ aName = *pLayoutName;
+ else
+ aName = pDim->GetName();
+ }
+ }
+ return aName;
+}
+
+void SAL_CALL ScDataPilotFieldObj::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ ScDPSaveDimension* pDim = GetDPDimension( &pDPObj );
+ if( pDim && !pDim->IsDataLayout() )
+ {
+ pDim->SetLayoutName(rName);
+ SetDPObject( pDPObj );
+ }
+}
+
+// XPropertySet
+
+Reference<XPropertySetInfo> SAL_CALL ScDataPilotFieldObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static Reference<XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScDataPilotFieldObj::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if ( aPropertyName == SC_UNONAME_FUNCTION )
+ {
+ // #i109350# use GetEnumFromAny because it also allows sal_Int32
+ ScGeneralFunction eFunction = static_cast<ScGeneralFunction>(ScUnoHelpFunctions::GetEnumFromAny( aValue ));
+ setFunction( eFunction );
+ }
+ else if ( aPropertyName == SC_UNONAME_FUNCTION2 )
+ {
+ ScGeneralFunction eFunction = static_cast<ScGeneralFunction>(ScUnoHelpFunctions::GetInt16FromAny( aValue ));
+ setFunction( eFunction );
+ }
+ else if ( aPropertyName == SC_UNONAME_SUBTOTALS )
+ {
+ uno::Sequence<sheet::GeneralFunction> aSeq;
+ if( aValue >>= aSeq)
+ {
+ std::vector< ScGeneralFunction > aSubTotals(aSeq.getLength());
+ std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.begin(),
+ [](const sheet::GeneralFunction& rValue) -> ScGeneralFunction {
+ const int nValAsInt = static_cast<int>(rValue);
+ return static_cast<ScGeneralFunction>(nValAsInt);
+ });
+ setSubtotals( aSubTotals );
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_SUBTOTALS2 )
+ {
+ Sequence< sal_Int16 > aSeq;
+ if( aValue >>= aSeq )
+ {
+ std::vector< ScGeneralFunction > aSubTotals(aSeq.getLength());
+ std::transform(std::cbegin(aSeq), std::cend(aSeq), aSubTotals.begin(),
+ [](sal_Int16 nValue) -> ScGeneralFunction { return static_cast<ScGeneralFunction>(nValue); });
+ setSubtotals( aSubTotals );
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_ORIENT )
+ {
+ //! test for correct enum type?
+ DataPilotFieldOrientation eOrient = static_cast<DataPilotFieldOrientation>(ScUnoHelpFunctions::GetEnumFromAny( aValue ));
+ setOrientation( eOrient );
+ }
+ else if ( aPropertyName == SC_UNONAME_SELPAGE )
+ {
+ OUString sCurrentPage;
+ if (aValue >>= sCurrentPage)
+ setCurrentPage(sCurrentPage);
+ }
+ else if ( aPropertyName == SC_UNONAME_USESELPAGE )
+ {
+ setUseCurrentPage(cppu::any2bool(aValue));
+ }
+ else if ( aPropertyName == SC_UNONAME_HASAUTOSHOW )
+ {
+ if (!cppu::any2bool(aValue))
+ setAutoShowInfo(nullptr);
+ }
+ else if ( aPropertyName == SC_UNONAME_AUTOSHOW )
+ {
+ DataPilotFieldAutoShowInfo aInfo;
+ if (aValue >>= aInfo)
+ setAutoShowInfo(&aInfo);
+ }
+ else if ( aPropertyName == SC_UNONAME_HASLAYOUTINFO )
+ {
+ if (!cppu::any2bool(aValue))
+ setLayoutInfo(nullptr);
+ }
+ else if ( aPropertyName == SC_UNONAME_LAYOUTINFO )
+ {
+ DataPilotFieldLayoutInfo aInfo;
+ if (aValue >>= aInfo)
+ setLayoutInfo(&aInfo);
+ }
+ else if ( aPropertyName == SC_UNONAME_HASREFERENCE )
+ {
+ if (!cppu::any2bool(aValue))
+ setReference(nullptr);
+ }
+ else if ( aPropertyName == SC_UNONAME_REFERENCE )
+ {
+ DataPilotFieldReference aRef;
+ if (aValue >>= aRef)
+ setReference(&aRef);
+ }
+ else if ( aPropertyName == SC_UNONAME_HASSORTINFO )
+ {
+ if (!cppu::any2bool(aValue))
+ setSortInfo(nullptr);
+ }
+ else if ( aPropertyName == SC_UNONAME_SORTINFO )
+ {
+ DataPilotFieldSortInfo aInfo;
+ if (aValue >>= aInfo)
+ setSortInfo(&aInfo);
+ }
+ else if ( aPropertyName == SC_UNONAME_ISGROUP )
+ {
+ if (!cppu::any2bool(aValue))
+ setGroupInfo(nullptr);
+ }
+ else if ( aPropertyName == SC_UNONAME_GROUPINFO )
+ {
+ DataPilotFieldGroupInfo aInfo;
+ if (aValue >>= aInfo)
+ setGroupInfo(&aInfo);
+ }
+ else if ( aPropertyName == SC_UNONAME_SHOWEMPTY )
+ {
+ setShowEmpty(cppu::any2bool(aValue));
+ }
+ else if ( aPropertyName == SC_UNONAME_REPEATITEMLABELS )
+ {
+ setRepeatItemLabels(cppu::any2bool(aValue));
+ }
+ else if (aPropertyName == SC_UNONAME_NAME)
+ {
+ OUString sName;
+ if (aValue >>= sName)
+ setName(sName);
+ }
+}
+
+Any SAL_CALL ScDataPilotFieldObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ Any aRet;
+
+ if ( aPropertyName == SC_UNONAME_FUNCTION )
+ {
+ sheet::GeneralFunction eVal;
+ sal_Int16 nFunction = getFunction();
+ if (nFunction == sheet::GeneralFunction2::MEDIAN)
+ {
+ eVal = sheet::GeneralFunction_NONE;
+ }
+ else
+ {
+ eVal = static_cast<sheet::GeneralFunction>(nFunction);
+ }
+ aRet <<= eVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_FUNCTION2 )
+ aRet <<= getFunction();
+ else if ( aPropertyName == SC_UNONAME_SUBTOTALS )
+ {
+ const uno::Sequence<sal_Int16> aSeq = getSubtotals();
+ uno::Sequence<sheet::GeneralFunction> aNewSeq(aSeq.getLength());
+ std::transform(aSeq.begin(), aSeq.end(), aNewSeq.getArray(),
+ [](sal_Int16 nFunc) -> sheet::GeneralFunction {
+ if (nFunc == sheet::GeneralFunction2::MEDIAN)
+ return sheet::GeneralFunction_NONE;
+ return static_cast<sheet::GeneralFunction>(nFunc);
+ });
+ aRet <<= aNewSeq;
+ }
+ else if ( aPropertyName == SC_UNONAME_SUBTOTALS2 )
+ {
+ aRet <<= getSubtotals();
+ }
+ else if ( aPropertyName == SC_UNONAME_ORIENT )
+ aRet <<= getOrientation();
+ else if ( aPropertyName == SC_UNONAME_SELPAGE )
+ aRet <<= OUString();
+ else if ( aPropertyName == SC_UNONAME_USESELPAGE )
+ aRet <<= false;
+ else if ( aPropertyName == SC_UNONAME_HASAUTOSHOW )
+ aRet <<= (getAutoShowInfo() != nullptr);
+ else if ( aPropertyName == SC_UNONAME_AUTOSHOW )
+ {
+ const DataPilotFieldAutoShowInfo* pInfo = getAutoShowInfo();
+ if (pInfo)
+ aRet <<= *pInfo;
+ }
+ else if ( aPropertyName == SC_UNONAME_HASLAYOUTINFO )
+ aRet <<= (getLayoutInfo() != nullptr);
+ else if ( aPropertyName == SC_UNONAME_LAYOUTINFO )
+ {
+ const DataPilotFieldLayoutInfo* pInfo = getLayoutInfo();
+ if (pInfo)
+ aRet <<= *pInfo;
+ }
+ else if ( aPropertyName == SC_UNONAME_HASREFERENCE )
+ aRet <<= (getReference() != nullptr);
+ else if ( aPropertyName == SC_UNONAME_REFERENCE )
+ {
+ const DataPilotFieldReference* pRef = getReference();
+ if (pRef)
+ aRet <<= *pRef;
+ }
+ else if ( aPropertyName == SC_UNONAME_HASSORTINFO )
+ aRet <<= (getSortInfo() != nullptr);
+ else if ( aPropertyName == SC_UNONAME_SORTINFO )
+ {
+ const DataPilotFieldSortInfo* pInfo = getSortInfo();
+ if (pInfo)
+ aRet <<= *pInfo;
+ }
+ else if ( aPropertyName == SC_UNONAME_ISGROUP )
+ aRet <<= hasGroupInfo();
+ else if ( aPropertyName == SC_UNONAME_GROUPINFO )
+ {
+ aRet <<= getGroupInfo();
+ }
+ else if ( aPropertyName == SC_UNONAME_SHOWEMPTY )
+ aRet <<= getShowEmpty();
+ else if ( aPropertyName == SC_UNONAME_REPEATITEMLABELS )
+ aRet <<= getRepeatItemLabels();
+ else if (aPropertyName == SC_UNONAME_NAME)
+ aRet <<= getName();
+
+ return aRet;
+}
+
+// XDatePilotField
+
+Reference<XIndexAccess> SAL_CALL ScDataPilotFieldObj::getItems()
+{
+ SolarMutexGuard aGuard;
+ if (!mxItems.is())
+ mxItems.set( new ScDataPilotItemsObj( *mxParent, maFieldId ) );
+ return mxItems;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDataPilotFieldObj )
+
+DataPilotFieldOrientation ScDataPilotFieldObj::getOrientation() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim ? pDim->GetOrientation() : DataPilotFieldOrientation_HIDDEN;
+}
+
+void ScDataPilotFieldObj::setOrientation(DataPilotFieldOrientation eNew)
+{
+ SolarMutexGuard aGuard;
+ if (maOrient.hasValue() && (eNew == maOrient.get< DataPilotFieldOrientation >()))
+ return;
+
+ ScDPObject* pDPObj = nullptr;
+ ScDPSaveDimension* pDim = GetDPDimension( &pDPObj );
+ if(!pDim)
+ return;
+
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+
+ /* If the field was taken from getDataPilotFields(), don't reset the
+ orientation for an existing use, but create a duplicated field
+ instead (for "Data" orientation only). */
+ if ( !maOrient.hasValue() && !maFieldId.mbDataLayout &&
+ (pDim->GetOrientation() != DataPilotFieldOrientation_HIDDEN) &&
+ (eNew == DataPilotFieldOrientation_DATA) )
+ {
+
+ ScDPSaveDimension* pNewDim = nullptr;
+
+ // look for existing duplicate with orientation "hidden"
+
+ sal_Int32 nFound = 0;
+ const ScDPSaveData::DimsType& rDimensions = pSaveData->GetDimensions();
+ for (auto const& it : rDimensions)
+ {
+ if ( !it->IsDataLayout() && (it->GetName() == maFieldId.maFieldName) )
+ {
+ if ( it->GetOrientation() == DataPilotFieldOrientation_HIDDEN )
+ {
+ pNewDim = it.get(); // use this one
+ break;
+ }
+ else
+ ++nFound; // count existing non-hidden occurrences
+ }
+ }
+
+ if ( !pNewDim ) // if none found, create a new duplicated dimension
+ pNewDim = &pSaveData->DuplicateDimension( *pDim );
+
+ maFieldId.mnFieldIdx = nFound; // keep accessing the new one
+ pDim = pNewDim;
+ }
+
+ pDim->SetOrientation(eNew);
+
+ // move changed field behind all other fields (make it the last field in dimension)
+ pSaveData->SetPosition( pDim, pSaveData->GetDimensions().size() );
+
+ SetDPObject( pDPObj );
+
+ maOrient <<= eNew; // modifying the same object's orientation again doesn't create another duplicate
+}
+
+sal_Int16 ScDataPilotFieldObj::getFunction() const
+{
+ SolarMutexGuard aGuard;
+ sal_Int16 eRet = GeneralFunction2::NONE;
+ if( ScDPSaveDimension* pDim = GetDPDimension() )
+ {
+ if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA )
+ {
+ // for non-data fields, property Function is the subtotals
+ tools::Long nSubCount = pDim->GetSubTotalsCount();
+ if ( nSubCount > 0 )
+ eRet = static_cast<sal_Int16>(pDim->GetSubTotalFunc(0)); // always use the first one
+ // else keep NONE
+ }
+ else
+ eRet = static_cast<sal_Int16>(pDim->GetFunction());
+ }
+ return eRet;
+}
+
+void ScDataPilotFieldObj::setFunction(ScGeneralFunction eNewFunc)
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ ScDPSaveDimension* pDim = GetDPDimension( &pDPObj );
+ if(!pDim)
+ return;
+
+ if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA )
+ {
+ // for non-data fields, property Function is the subtotals
+ std::vector<ScGeneralFunction> nSubTotalFuncs;
+ if ( eNewFunc != ScGeneralFunction::NONE )
+ {
+ nSubTotalFuncs.push_back( eNewFunc );
+ }
+ pDim->SetSubTotals( std::move(nSubTotalFuncs) );
+ }
+ else
+ pDim->SetFunction( eNewFunc );
+ SetDPObject( pDPObj );
+}
+
+Sequence< sal_Int16 > ScDataPilotFieldObj::getSubtotals() const
+{
+ SolarMutexGuard aGuard;
+ Sequence< sal_Int16 > aRet;
+ if( ScDPSaveDimension* pDim = GetDPDimension() )
+ {
+ if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA )
+ {
+ // for non-data fields, property Functions is the sequence of subtotals
+ sal_Int32 nCount = static_cast< sal_Int32 >( pDim->GetSubTotalsCount() );
+ if ( nCount > 0 )
+ {
+ aRet.realloc( nCount );
+ auto pRet = aRet.getArray();
+ for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx )
+ pRet[ nIdx ] = static_cast<sal_Int16>(pDim->GetSubTotalFunc( nIdx ));
+ }
+ }
+ }
+ return aRet;
+}
+
+void ScDataPilotFieldObj::setSubtotals( const std::vector< ScGeneralFunction >& rSubtotals )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ ScDPSaveDimension* pDim = GetDPDimension( &pDPObj );
+ if(!pDim)
+ return;
+
+ if( pDim->GetOrientation() != DataPilotFieldOrientation_DATA )
+ {
+ sal_Int32 nCount = rSubtotals.size();
+ if( nCount == 1 )
+ {
+ // count 1: all values are allowed (including NONE and AUTO)
+ std::vector<ScGeneralFunction> nTmpFuncs;
+ if( rSubtotals[ 0 ] != ScGeneralFunction::NONE )
+ {
+ nTmpFuncs.push_back( rSubtotals[ 0 ] );
+ }
+ pDim->SetSubTotals( std::move(nTmpFuncs) );
+ }
+ else if( nCount > 1 )
+ {
+ // set multiple functions, ignore NONE and AUTO in this case
+ ::std::vector< ScGeneralFunction > aSubt;
+ for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ ScGeneralFunction eFunc = rSubtotals[ nIdx ];
+ if( (eFunc != ScGeneralFunction::NONE) && (eFunc != ScGeneralFunction::AUTO) )
+ {
+ // do not insert functions twice
+ if( ::std::find( aSubt.begin(), aSubt.end(), eFunc ) == aSubt.end() )
+ aSubt.push_back( eFunc );
+ }
+ }
+ // set values from vector to ScDPSaveDimension
+ pDim->SetSubTotals( std::move(aSubt) );
+ }
+ }
+ SetDPObject( pDPObj );
+}
+
+void ScDataPilotFieldObj::setCurrentPage( const OUString& rPage )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetCurrentPage( &rPage );
+ SetDPObject( pDPObj );
+ }
+}
+
+void ScDataPilotFieldObj::setUseCurrentPage( bool bUse )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ ScDPSaveDimension* pDim = GetDPDimension( &pDPObj );
+ if(!pDim)
+ return;
+
+ if( bUse )
+ {
+ /* It is somehow useless to set the property "HasSelectedPage" to
+ true, because it is still needed to set an explicit page name. */
+ const OUString aPage;
+ pDim->SetCurrentPage( &aPage );
+ }
+ else
+ pDim->SetCurrentPage( nullptr );
+ SetDPObject( pDPObj );
+}
+
+const DataPilotFieldAutoShowInfo* ScDataPilotFieldObj::getAutoShowInfo() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim ? pDim->GetAutoShowInfo() : nullptr;
+}
+
+void ScDataPilotFieldObj::setAutoShowInfo( const DataPilotFieldAutoShowInfo* pInfo )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetAutoShowInfo( pInfo );
+ SetDPObject( pDPObj );
+ }
+}
+
+const DataPilotFieldLayoutInfo* ScDataPilotFieldObj::getLayoutInfo() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim ? pDim->GetLayoutInfo() : nullptr;
+}
+
+void ScDataPilotFieldObj::setLayoutInfo( const DataPilotFieldLayoutInfo* pInfo )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetLayoutInfo( pInfo );
+ SetDPObject( pDPObj );
+ }
+}
+
+const DataPilotFieldReference* ScDataPilotFieldObj::getReference() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim ? pDim->GetReferenceValue() : nullptr;
+}
+
+void ScDataPilotFieldObj::setReference( const DataPilotFieldReference* pInfo )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetReferenceValue( pInfo );
+ SetDPObject( pDPObj );
+ }
+}
+
+const DataPilotFieldSortInfo* ScDataPilotFieldObj::getSortInfo() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim ? pDim->GetSortInfo() : nullptr;
+}
+
+void ScDataPilotFieldObj::setSortInfo( const DataPilotFieldSortInfo* pInfo )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetSortInfo( pInfo );
+ SetDPObject( pDPObj );
+ }
+}
+
+bool ScDataPilotFieldObj::getShowEmpty() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim && pDim->GetShowEmpty();
+}
+
+void ScDataPilotFieldObj::setShowEmpty( bool bShow )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetShowEmpty( bShow );
+ SetDPObject( pDPObj );
+ }
+}
+
+bool ScDataPilotFieldObj::getRepeatItemLabels() const
+{
+ SolarMutexGuard aGuard;
+ ScDPSaveDimension* pDim = GetDPDimension();
+ return pDim && pDim->GetRepeatItemLabels();
+}
+
+void ScDataPilotFieldObj::setRepeatItemLabels( bool bShow )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ pDim->SetRepeatItemLabels( bShow );
+ SetDPObject( pDPObj );
+ }
+}
+
+bool ScDataPilotFieldObj::hasGroupInfo() const
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ if( const ScDPDimensionSaveData* pDimData = pDPObj->GetSaveData()->GetExistingDimensionData() )
+ return pDimData->GetNamedGroupDim( pDim->GetName() ) || pDimData->GetNumGroupDim( pDim->GetName() );
+ return false;
+}
+
+DataPilotFieldGroupInfo ScDataPilotFieldObj::getGroupInfo()
+{
+ SolarMutexGuard aGuard;
+ DataPilotFieldGroupInfo aInfo;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ if( const ScDPDimensionSaveData* pDimData = pDPObj->GetSaveData()->GetExistingDimensionData() )
+ {
+ if( const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( pDim->GetName() ) )
+ {
+ // grouped by ...
+ aInfo.GroupBy = pGroupDim->GetDatePart();
+
+ // find source field
+ try
+ {
+ Reference< XNameAccess > xFields( mxParent->getDataPilotFields(), UNO_QUERY_THROW );
+ aInfo.SourceField.set( xFields->getByName( pGroupDim->GetSourceDimName() ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+
+ ScDataPilotConversion::FillGroupInfo( aInfo, pGroupDim->GetDateInfo() );
+ if( pGroupDim->GetDatePart() == 0 )
+ {
+ // fill vector of group and group member information
+ ScFieldGroups aGroups;
+ for( sal_Int32 nIdx = 0, nCount = pGroupDim->GetGroupCount(); nIdx < nCount; ++nIdx )
+ {
+ const ScDPSaveGroupItem& rGroup = pGroupDim->GetGroupByIndex( nIdx );
+ ScFieldGroup aGroup;
+ aGroup.maName = rGroup.GetGroupName();
+ for( sal_Int32 nMemIdx = 0, nMemCount = rGroup.GetElementCount(); nMemIdx < nMemCount; ++nMemIdx )
+ if (const OUString* pMem = rGroup.GetElementByIndex(nMemIdx))
+ aGroup.maMembers.push_back( *pMem );
+ aGroups.push_back( aGroup );
+ }
+ aInfo.Groups = new ScDataPilotFieldGroupsObj( std::move(aGroups) );
+ }
+ }
+ else if( const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( pDim->GetName() ) )
+ {
+ if (pNumGroupDim->GetDatePart())
+ {
+ ScDataPilotConversion::FillGroupInfo( aInfo, pNumGroupDim->GetDateInfo() );
+ aInfo.GroupBy = pNumGroupDim->GetDatePart();
+ }
+ else
+ {
+ ScDataPilotConversion::FillGroupInfo( aInfo, pNumGroupDim->GetInfo() );
+ }
+ }
+ }
+ }
+ return aInfo;
+}
+
+void ScDataPilotFieldObj::setGroupInfo( const DataPilotFieldGroupInfo* pInfo )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ if( /*ScDPSaveDimension* pDim =*/ !GetDPDimension( &pDPObj ) )
+ return;
+
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if( pInfo && lclCheckMinMaxStep( *pInfo ) )
+ {
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = pInfo->HasDateValues;
+ aInfo.mbAutoStart = pInfo->HasAutoStart;
+ aInfo.mbAutoEnd = pInfo->HasAutoEnd;
+ aInfo.mfStart = pInfo->Start;
+ aInfo.mfEnd = pInfo->End;
+ aInfo.mfStep = pInfo->Step;
+ Reference< XNamed > xNamed( pInfo->SourceField, UNO_QUERY );
+ if( xNamed.is() )
+ {
+ ScDPSaveGroupDimension aGroupDim( xNamed->getName(), getName() );
+ if( pInfo->GroupBy )
+ aGroupDim.SetDateInfo(aInfo, pInfo->GroupBy);
+ else
+ {
+ Reference<XIndexAccess> xIndex(pInfo->Groups, UNO_QUERY);
+ if (xIndex.is())
+ {
+ sal_Int32 nCount(xIndex->getCount());
+ for(sal_Int32 i = 0; i < nCount; i++)
+ {
+ Reference<XNamed> xGroupNamed(xIndex->getByIndex(i), UNO_QUERY);
+ if (xGroupNamed.is())
+ {
+ ScDPSaveGroupItem aItem(xGroupNamed->getName());
+ Reference<XIndexAccess> xGroupIndex(xGroupNamed, UNO_QUERY);
+ if (xGroupIndex.is())
+ {
+ sal_Int32 nItemCount(xGroupIndex->getCount());
+ for (sal_Int32 j = 0; j < nItemCount; ++j)
+ {
+ Reference<XNamed> xItemNamed(xGroupIndex->getByIndex(j), UNO_QUERY);
+ if (xItemNamed.is())
+ aItem.AddElement(xItemNamed->getName());
+ }
+ }
+ aGroupDim.AddGroupItem(aItem);
+ }
+ }
+ }
+ }
+
+ // get dimension savedata or create new if none
+ ScDPDimensionSaveData& rDimSaveData = *pSaveData->GetDimensionData();
+ rDimSaveData.ReplaceGroupDimension( aGroupDim );
+ }
+ else // no source field in group info -> numeric group
+ {
+ ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData(); // created if not there
+
+ ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( getName() );
+ if ( pExisting )
+ {
+ if (pInfo->GroupBy)
+ pExisting->SetDateInfo(aInfo, pInfo->GroupBy);
+ // modify existing group dimension
+ pExisting->SetGroupInfo( aInfo );
+ }
+ else if (pInfo->GroupBy)
+ {
+ // create new group dimension
+ ScDPSaveNumGroupDimension aNumGroupDim( getName(), aInfo, pInfo->GroupBy );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+ else
+ {
+ // create new group dimension
+ ScDPSaveNumGroupDimension aNumGroupDim( getName(), aInfo );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+ }
+ }
+ else // null passed as argument
+ {
+ pSaveData->SetDimensionData( nullptr );
+ }
+
+ pDPObj->SetSaveData( *pSaveData );
+ SetDPObject( pDPObj );
+}
+
+// XDataPilotFieldGrouping
+Reference< XDataPilotField > SAL_CALL ScDataPilotFieldObj::createNameGroup( const Sequence< OUString >& rItems )
+{
+ SolarMutexGuard aGuard;
+
+ if( !rItems.hasElements() )
+ throw IllegalArgumentException("rItems is empty", getXWeak(), 0);
+
+ Reference< XMembersAccess > xMembers = GetMembers();
+ if (!xMembers.is())
+ {
+ SAL_WARN("sc.ui", "Cannot access members of the field object.");
+ throw RuntimeException("Cannot access members of the field object", getXWeak());
+ }
+
+ for (const OUString& aEntryName : rItems)
+ {
+ if (!xMembers->hasByName(aEntryName))
+ {
+ SAL_WARN("sc.ui", "There is no member with that name: " + aEntryName + ".");
+ throw IllegalArgumentException("There is no member with name \"" + aEntryName + "\"", getXWeak(), 0);
+ }
+ }
+
+ Reference< XDataPilotField > xRet;
+ OUString sNewDim;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ const OUString& aDimName = pDim->GetName();
+
+ ScDPSaveData aSaveData = *pDPObj->GetSaveData();
+ ScDPDimensionSaveData* pDimData = aSaveData.GetDimensionData(); // created if not there
+
+ // find original base
+ OUString aBaseDimName( aDimName );
+ const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
+ if ( pBaseGroupDim )
+ {
+ // any entry's SourceDimName is the original base
+ aBaseDimName = pBaseGroupDim->GetSourceDimName();
+ }
+
+ // find existing group dimension
+ // (using the selected dim, can be intermediate group dim)
+ ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
+
+ // remove the selected items from their groups
+ // (empty groups are removed, too)
+ if ( pGroupDimension )
+ {
+ for (const OUString& aEntryName : rItems)
+ {
+ if ( pBaseGroupDim )
+ {
+ // for each selected (intermediate) group, remove all its items
+ // (same logic as for adding, below)
+ const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
+ if ( pBaseGroup )
+ pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements
+ else
+ pGroupDimension->RemoveFromGroups( aEntryName );
+ }
+ else
+ pGroupDimension->RemoveFromGroups( aEntryName );
+ }
+ }
+
+ std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim;
+ if ( !pGroupDimension )
+ {
+ // create a new group dimension
+ sNewDim = pDimData->CreateGroupDimName( aBaseDimName, *pDPObj, false, nullptr );
+ pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, sNewDim ));
+
+ pGroupDimension = pNewGroupDim.get(); // make changes to the new dim if none existed
+
+ if ( pBaseGroupDim )
+ {
+ // If it's a higher-order group dimension, pre-allocate groups for all
+ // non-selected original groups, so the individual base members aren't
+ // used for automatic groups (this would make the original groups hard
+ // to find).
+ //! Also do this when removing groups?
+ //! Handle this case dynamically with automatic groups?
+
+ tools::Long nGroupCount = pBaseGroupDim->GetGroupCount();
+ for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ )
+ {
+ const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
+
+ if (comphelper::findValue(rItems, rBaseGroup.GetGroupName()) == -1) //! ignore case?
+ {
+ // add an additional group for each item that is not in the selection
+ ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() );
+ aGroup.AddElementsFromGroup( rBaseGroup );
+ pGroupDimension->AddGroupItem( aGroup );
+ }
+ }
+ }
+ }
+ OUString aGroupDimName = pGroupDimension->GetGroupDimName();
+
+ OUString aGroupName = pGroupDimension->CreateGroupName( ScResId(STR_PIVOT_GROUP) );
+ ScDPSaveGroupItem aGroup( aGroupName );
+ for (const OUString& aEntryName : rItems)
+ {
+ if ( pBaseGroupDim )
+ {
+ // for each selected (intermediate) group, add all its items
+ const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
+ if ( pBaseGroup )
+ aGroup.AddElementsFromGroup( *pBaseGroup );
+ else
+ aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself
+ }
+ else
+ aGroup.AddElement( aEntryName ); // no group dimension, add all items directly
+ }
+
+ pGroupDimension->AddGroupItem( aGroup );
+
+ if ( pNewGroupDim )
+ {
+ pDimData->AddGroupDimension( *pNewGroupDim );
+ pNewGroupDim.reset(); // AddGroupDimension copies the object
+ // don't access pGroupDimension after here
+ }
+ pGroupDimension = nullptr;
+
+ // set orientation
+ ScDPSaveDimension* pSaveDimension = aSaveData.GetDimensionByName( aGroupDimName );
+ if ( pSaveDimension->GetOrientation() == DataPilotFieldOrientation_HIDDEN )
+ {
+ ScDPSaveDimension* pOldDimension = aSaveData.GetDimensionByName( aDimName );
+ pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
+ aSaveData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
+ }
+
+ // apply changes
+ pDPObj->SetSaveData( aSaveData );
+ ScDBDocFunc(*GetDocShell()).RefreshPivotTableGroups(pDPObj);
+ }
+
+ // if new grouping field has been created (on first group), return it
+ if( !sNewDim.isEmpty() )
+ {
+ Reference< XNameAccess > xFields(mxParent->getDataPilotFields(), UNO_QUERY);
+ if (xFields.is())
+ {
+ try
+ {
+ xRet.set(xFields->getByName(sNewDim), UNO_QUERY);
+ SAL_WARN_IF(!xRet.is(), "sc.ui", "there is a name, so there should be also a field");
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ SAL_WARN("sc.ui", "Cannot find field with that name: " + sNewDim + ".");
+ // Avoid throwing exception that's not specified in the method signature.
+ throw css::lang::WrappedTargetRuntimeException(
+ "Cannot find field with name \"" + sNewDim + "\"",
+ getXWeak(), anyEx );
+ }
+ }
+ }
+ return xRet;
+}
+
+Reference < XDataPilotField > SAL_CALL ScDataPilotFieldObj::createDateGroup( const DataPilotFieldGroupInfo& rInfo )
+{
+ SolarMutexGuard aGuard;
+ using namespace ::com::sun::star::sheet::DataPilotFieldGroupBy;
+
+ if( !rInfo.HasDateValues )
+ throw IllegalArgumentException("HasDateValues is not set", getXWeak(), 0);
+ if( !lclCheckMinMaxStep( rInfo ) )
+ throw IllegalArgumentException("min/max/step", getXWeak(), 0);
+
+ // only a single date flag is allowed
+ if( (rInfo.GroupBy == 0) || (rInfo.GroupBy > YEARS) || ((rInfo.GroupBy & (rInfo.GroupBy - 1)) != 0) )
+ throw IllegalArgumentException("Invalid GroupBy value: " + OUString::number(rInfo.GroupBy), getXWeak(), 0);
+
+ // step must be zero, if something else than DAYS is specified
+ if( rInfo.Step >= ((rInfo.GroupBy == DAYS) ? 32768.0 : 1.0) )
+ throw IllegalArgumentException("Invalid step value: " + OUString::number(rInfo.Step), getXWeak(), 0);
+
+ OUString aGroupDimName;
+ ScDPObject* pDPObj = nullptr;
+ if( ScDPSaveDimension* pDim = GetDPDimension( &pDPObj ) )
+ {
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = (rInfo.GroupBy == DAYS) && (rInfo.Step >= 1.0);
+ aInfo.mbAutoStart = rInfo.HasAutoStart;
+ aInfo.mbAutoEnd = rInfo.HasAutoEnd;
+ aInfo.mfStart = rInfo.Start;
+ aInfo.mfEnd = rInfo.End;
+ aInfo.mfStep = std::trunc( rInfo.Step );
+
+ // create a local copy of the entire save data (will be written back below)
+ ScDPSaveData aSaveData = *pDPObj->GetSaveData();
+ // get or create dimension save data
+ ScDPDimensionSaveData& rDimData = *aSaveData.GetDimensionData();
+
+ // find source dimension name
+ const OUString& rDimName = pDim->GetName();
+ const ScDPSaveGroupDimension* pGroupDim = rDimData.GetNamedGroupDim( rDimName );
+ OUString aSrcDimName = pGroupDim ? pGroupDim->GetSourceDimName() : rDimName;
+
+ // find a group dimension for the base field, or get numeric grouping
+ pGroupDim = rDimData.GetFirstNamedGroupDim( aSrcDimName );
+ const ScDPSaveNumGroupDimension* pNumGroupDim = rDimData.GetNumGroupDim( aSrcDimName );
+
+ // do not group by dates, if named groups or numeric grouping is present
+ bool bHasNamedGrouping = pGroupDim && !pGroupDim->GetDateInfo().mbEnable;
+ bool bHasNumGrouping = pNumGroupDim && pNumGroupDim->GetInfo().mbEnable && !pNumGroupDim->GetInfo().mbDateValues && !pNumGroupDim->GetDateInfo().mbEnable;
+ if( bHasNamedGrouping || bHasNumGrouping )
+ throw IllegalArgumentException();
+
+ if( aInfo.mbDateValues ) // create day ranges grouping
+ {
+ // first remove all named group dimensions
+ while( pGroupDim )
+ {
+ OUString aGroupDimName2 = pGroupDim->GetGroupDimName();
+ // find next group dimension before deleting this group
+ pGroupDim = rDimData.GetNextNamedGroupDim( aGroupDimName2 );
+ // remove from dimension save data
+ rDimData.RemoveGroupDimension( aGroupDimName2 );
+ // also remove save data settings for the dimension that no longer exists
+ aSaveData.RemoveDimensionByName( aGroupDimName2 );
+ }
+ // create or replace the number grouping dimension
+ ScDPSaveNumGroupDimension aNumGroupDim( aSrcDimName, aInfo );
+ rDimData.ReplaceNumGroupDimension( aNumGroupDim );
+ }
+ else // create date grouping
+ {
+ // collect all existing date flags
+ sal_Int32 nDateParts = rDimData.CollectDateParts( aSrcDimName );
+ if( nDateParts == 0 )
+ {
+ // insert numeric group dimension, if no date groups exist yet (or replace day range grouping)
+ ScDPSaveNumGroupDimension aNumGroupDim( aSrcDimName, aInfo, rInfo.GroupBy );
+ rDimData.ReplaceNumGroupDimension( aNumGroupDim );
+ }
+ else if( (nDateParts & rInfo.GroupBy) == 0 ) // do nothing if date field exists already
+ {
+ // create new named group dimension for additional date groups
+ aGroupDimName = rDimData.CreateDateGroupDimName( rInfo.GroupBy, *pDPObj, true, nullptr );
+ ScDPSaveGroupDimension aGroupDim( aSrcDimName, aGroupDimName, aInfo, rInfo.GroupBy );
+ rDimData.AddGroupDimension( aGroupDim );
+
+ // set orientation of new named group dimension
+ ScDPSaveDimension& rSaveDim = *aSaveData.GetDimensionByName( aGroupDimName );
+ if( rSaveDim.GetOrientation() == DataPilotFieldOrientation_HIDDEN )
+ {
+ ScDPSaveDimension& rOldDim = *aSaveData.GetDimensionByName( aSrcDimName );
+ rSaveDim.SetOrientation( rOldDim.GetOrientation() );
+ aSaveData.SetPosition( &rSaveDim, 0 ); //! before (immediate) base
+ }
+ }
+ }
+
+ // apply changes
+ pDPObj->SetSaveData( aSaveData );
+ ScDBDocFunc(*GetDocShell()).RefreshPivotTableGroups(pDPObj);
+ }
+
+ // return the UNO object of the new dimension, after writing back saved data
+ Reference< XDataPilotField > xRet;
+ if( !aGroupDimName.isEmpty() )
+ try
+ {
+ Reference< XNameAccess > xFields( mxParent->getDataPilotFields(), UNO_QUERY_THROW );
+ xRet.set( xFields->getByName( aGroupDimName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRet;
+}
+
+namespace {
+
+bool lclExtractGroupMembers( ScFieldGroupMembers& rMembers, const Any& rElement )
+{
+ // allow empty value to create a new group
+ if( !rElement.hasValue() )
+ return true;
+
+ // try to extract a simple sequence of strings
+ Sequence< OUString > aSeq;
+ if( rElement >>= aSeq )
+ {
+ if( aSeq.hasElements() )
+ rMembers.insert( rMembers.end(), std::cbegin(aSeq), std::cend(aSeq) );
+ return true;
+ }
+
+ // try to use XIndexAccess providing objects that support XNamed
+ Reference< XIndexAccess > xItemsIA( rElement, UNO_QUERY );
+ if( xItemsIA.is() )
+ {
+ for( sal_Int32 nIdx = 0, nCount = xItemsIA->getCount(); nIdx < nCount; ++nIdx )
+ {
+ try // getByIndex() should not throw, but we cannot be sure
+ {
+ Reference< XNamed > xItemName( xItemsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ rMembers.push_back( xItemName->getName() );
+ }
+ catch( Exception& )
+ {
+ // ignore exceptions, go ahead with next element in the array
+ }
+ }
+ return true;
+ }
+
+ // nothing valid inside the Any -> return false
+ return false;
+}
+
+} // namespace
+
+ScDataPilotFieldGroupsObj::ScDataPilotFieldGroupsObj( ScFieldGroups&& rGroups ) :
+ maGroups( std::move(rGroups) )
+{
+}
+
+ScDataPilotFieldGroupsObj::~ScDataPilotFieldGroupsObj()
+{
+}
+
+// XNameAccess
+
+Any SAL_CALL ScDataPilotFieldGroupsObj::getByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ if( implFindByName( rName ) == maGroups.end() )
+ throw NoSuchElementException();
+ return Any( Reference< XNameAccess >( new ScDataPilotFieldGroupObj( *this, rName ) ) );
+}
+
+Sequence< OUString > SAL_CALL ScDataPilotFieldGroupsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ Sequence< OUString > aSeq;
+ if( !maGroups.empty() )
+ {
+ aSeq.realloc( static_cast< sal_Int32 >( maGroups.size() ) );
+ OUString* pName = aSeq.getArray();
+ for( const auto& rGroup : maGroups )
+ {
+ *pName = rGroup.maName;
+ ++pName;
+ }
+ }
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDataPilotFieldGroupsObj::hasByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ return implFindByName( rName ) != maGroups.end();
+}
+
+// XNameReplace
+
+void SAL_CALL ScDataPilotFieldGroupsObj::replaceByName( const OUString& rName, const Any& rElement )
+{
+ SolarMutexGuard aGuard;
+
+ if( rName.isEmpty() )
+ throw IllegalArgumentException("Name is empty", getXWeak(), 0);
+
+ ScFieldGroups::iterator aIt = implFindByName( rName );
+ if( aIt == maGroups.end() )
+ throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak());
+
+ // read all item names provided by the passed object
+ ScFieldGroupMembers aMembers;
+ if( !lclExtractGroupMembers( aMembers, rElement ) )
+ throw IllegalArgumentException("Invalid element object", getXWeak(), 0);
+
+ // copy and forget, faster than vector assignment
+ aIt->maMembers.swap( aMembers );
+}
+
+// XNameContainer
+
+void SAL_CALL ScDataPilotFieldGroupsObj::insertByName( const OUString& rName, const Any& rElement )
+{
+ SolarMutexGuard aGuard;
+
+ if( rName.isEmpty() )
+ throw IllegalArgumentException("Name is empty", getXWeak(), 0);
+
+ ScFieldGroups::iterator aIt = implFindByName( rName );
+ if( aIt != maGroups.end() )
+ throw ElementExistException("Name \"" + rName + "\" already exists", getXWeak());
+
+ // read all item names provided by the passed object
+ ScFieldGroupMembers aMembers;
+ if( !lclExtractGroupMembers( aMembers, rElement ) )
+ throw IllegalArgumentException("Invalid element object", getXWeak(), 0);
+
+ // create the new entry if no error has been occurred
+ maGroups.emplace_back();
+ ScFieldGroup& rGroup = maGroups.back();
+ rGroup.maName = rName;
+ rGroup.maMembers.swap( aMembers );
+}
+
+void SAL_CALL ScDataPilotFieldGroupsObj::removeByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+
+ if( rName.isEmpty() )
+ throw IllegalArgumentException("Name is empty", getXWeak(), 0);
+
+ ScFieldGroups::iterator aIt = implFindByName( rName );
+ if( aIt == maGroups.end() )
+ throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak());
+
+ maGroups.erase( aIt );
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDataPilotFieldGroupsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return static_cast< sal_Int32 >( maGroups.size() );
+}
+
+Any SAL_CALL ScDataPilotFieldGroupsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= maGroups.size()))
+ throw IndexOutOfBoundsException();
+ return Any( Reference< XNameAccess >( new ScDataPilotFieldGroupObj( *this, maGroups[ nIndex ].maName ) ) );
+}
+
+// XEnumerationAccess
+
+Reference<XEnumeration> SAL_CALL ScDataPilotFieldGroupsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration( this, "com.sun.star.sheet.DataPilotFieldGroupsEnumeration" );
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScDataPilotFieldGroupsObj::getElementType()
+{
+ return cppu::UnoType<XNameAccess>::get();
+}
+
+sal_Bool SAL_CALL ScDataPilotFieldGroupsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return !maGroups.empty();
+}
+
+// implementation
+
+ScFieldGroup& ScDataPilotFieldGroupsObj::getFieldGroup( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ ScFieldGroups::iterator aIt = implFindByName( rName );
+ if( aIt == maGroups.end() )
+ throw RuntimeException("Field Group with name \"" + rName + "\" not found", getXWeak());
+ return *aIt;
+}
+
+void ScDataPilotFieldGroupsObj::renameFieldGroup( const OUString& rOldName, const OUString& rNewName )
+{
+ SolarMutexGuard aGuard;
+ ScFieldGroups::iterator aOldIt = implFindByName( rOldName );
+ ScFieldGroups::iterator aNewIt = implFindByName( rNewName );
+ if( aOldIt == maGroups.end() )
+ throw RuntimeException("Field Group with name \"" + rOldName + "\" not found", getXWeak());
+ // new name must not exist yet
+ if( (aNewIt != maGroups.end()) && (aNewIt != aOldIt) )
+ throw RuntimeException("Field Group with name \"" + rOldName + "\" already exists", getXWeak());
+ aOldIt->maName = rNewName;
+}
+
+ScFieldGroups::iterator ScDataPilotFieldGroupsObj::implFindByName( const OUString& rName )
+{
+ return std::find_if(maGroups.begin(), maGroups.end(),
+ [&rName](const ScFieldGroup& rGroup) { return rGroup.maName == rName; });
+}
+
+namespace {
+
+OUString lclExtractMember( const Any& rElement )
+{
+ if( rElement.has< OUString >() )
+ return rElement.get< OUString >();
+
+ Reference< XNamed > xNamed( rElement, UNO_QUERY );
+ if( xNamed.is() )
+ return xNamed->getName();
+
+ return OUString();
+}
+
+} // namespace
+
+ScDataPilotFieldGroupObj::ScDataPilotFieldGroupObj( ScDataPilotFieldGroupsObj& rParent, OUString aGroupName ) :
+ mxParent( &rParent ),
+ maGroupName(std::move( aGroupName ))
+{
+}
+
+ScDataPilotFieldGroupObj::~ScDataPilotFieldGroupObj()
+{
+}
+
+// XNameAccess
+
+Any SAL_CALL ScDataPilotFieldGroupObj::getByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers;
+ ScFieldGroupMembers::iterator aIt = ::std::find( rMembers.begin(), rMembers.end(), rName );
+ if( aIt == rMembers.end() )
+ throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak());
+ return Any( Reference< XNamed >( new ScDataPilotFieldGroupItemObj( *this, *aIt ) ) );
+}
+
+Sequence< OUString > SAL_CALL ScDataPilotFieldGroupObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ return ::comphelper::containerToSequence( mxParent->getFieldGroup( maGroupName ).maMembers );
+}
+
+sal_Bool SAL_CALL ScDataPilotFieldGroupObj::hasByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers;
+ return ::std::find( rMembers.begin(), rMembers.end(), rName ) != rMembers.end();
+}
+
+// XNameReplace
+
+void SAL_CALL ScDataPilotFieldGroupObj::replaceByName( const OUString& rName, const Any& rElement )
+{
+ SolarMutexGuard aGuard;
+
+ // it should be possible to quickly rename an item -> accept string or XNamed
+ OUString aNewName = lclExtractMember( rElement );
+ if( rName.isEmpty() || aNewName.isEmpty() )
+ throw IllegalArgumentException("Name is empty", getXWeak(), 0);
+ if( rName == aNewName )
+ return;
+
+ ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers;
+ ScFieldGroupMembers::iterator aOldIt = ::std::find( rMembers.begin(), rMembers.end(), rName );
+ ScFieldGroupMembers::iterator aNewIt = ::std::find( rMembers.begin(), rMembers.end(), aNewName );
+ if( aOldIt == rMembers.end() )
+ throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak());
+ if( aNewIt != rMembers.end() )
+ throw IllegalArgumentException("Name \"" + rName + "\" already exists", getXWeak(), 0);
+ *aOldIt = aNewName;
+}
+
+// XNameContainer
+
+void SAL_CALL ScDataPilotFieldGroupObj::insertByName( const OUString& rName, const Any& /*rElement*/ )
+{
+ SolarMutexGuard aGuard;
+
+ // we will ignore the passed element and just try to insert the name
+ if( rName.isEmpty() )
+ throw IllegalArgumentException("Name is empty", getXWeak(), 0);
+
+ ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers;
+ ScFieldGroupMembers::iterator aIt = ::std::find( rMembers.begin(), rMembers.end(), rName );
+ if( aIt != rMembers.end() )
+ throw IllegalArgumentException("Name \"" + rName + "\" already exists", getXWeak(), 0);
+ rMembers.push_back( rName );
+}
+
+void SAL_CALL ScDataPilotFieldGroupObj::removeByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+
+ if( rName.isEmpty() )
+ throw IllegalArgumentException("Name is empty", getXWeak(), 0);
+ ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers;
+ ScFieldGroupMembers::iterator aIt = ::std::find( rMembers.begin(), rMembers.end(), rName );
+ if( aIt == rMembers.end() )
+ throw NoSuchElementException("Name \"" + rName + "\" not found", getXWeak());
+ rMembers.erase( aIt );
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDataPilotFieldGroupObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return static_cast< sal_Int32 >( mxParent->getFieldGroup( maGroupName ).maMembers.size() );
+}
+
+Any SAL_CALL ScDataPilotFieldGroupObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ScFieldGroupMembers& rMembers = mxParent->getFieldGroup( maGroupName ).maMembers;
+ if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= rMembers.size()))
+ throw IndexOutOfBoundsException();
+ return Any( Reference< XNamed >( new ScDataPilotFieldGroupItemObj( *this, rMembers[ nIndex ] ) ) );
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > SAL_CALL ScDataPilotFieldGroupObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration( this, "com.sun.star.sheet.DataPilotFieldGroupEnumeration" );
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScDataPilotFieldGroupObj::getElementType()
+{
+ return cppu::UnoType<XNamed>::get();
+}
+
+sal_Bool SAL_CALL ScDataPilotFieldGroupObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return !mxParent->getFieldGroup( maGroupName ).maMembers.empty();
+}
+
+// XNamed
+
+OUString SAL_CALL ScDataPilotFieldGroupObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return maGroupName;
+}
+
+void SAL_CALL ScDataPilotFieldGroupObj::setName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ mxParent->renameFieldGroup( maGroupName, rName );
+ // if call to renameFieldGroup() did not throw, remember the new name
+ maGroupName = rName;
+}
+
+ScDataPilotFieldGroupItemObj::ScDataPilotFieldGroupItemObj( ScDataPilotFieldGroupObj& rParent, OUString aName ) :
+ mxParent( &rParent ),
+ maName(std::move( aName ))
+{
+}
+
+ScDataPilotFieldGroupItemObj::~ScDataPilotFieldGroupItemObj()
+{
+}
+
+// XNamed
+
+OUString SAL_CALL ScDataPilotFieldGroupItemObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return maName;
+}
+
+void SAL_CALL ScDataPilotFieldGroupItemObj::setName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+ mxParent->replaceByName( maName, Any( rName ) );
+ // if call to replaceByName() did not throw, remember the new name
+ maName = rName;
+}
+
+ScDataPilotItemsObj::ScDataPilotItemsObj( ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId ) :
+ ScDataPilotChildObjBase( rParent, rFieldId )
+{
+}
+
+ScDataPilotItemsObj::~ScDataPilotItemsObj()
+{
+}
+
+// XDataPilotItems
+
+ScDataPilotItemObj* ScDataPilotItemsObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) const
+{
+ return ((0 <= nIndex) && (nIndex < GetMemberCount())) ?
+ new ScDataPilotItemObj( *mxParent, maFieldId, nIndex ) : nullptr;
+}
+
+// XNameAccess
+
+Any SAL_CALL ScDataPilotItemsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ Reference<XNameAccess> xMembers = GetMembers();
+ if (xMembers.is())
+ {
+ Reference<XIndexAccess> xMembersIndex(new ScNameToIndexAccess( xMembers ));
+ sal_Int32 nCount = xMembersIndex->getCount();
+ sal_Int32 nItem = 0;
+ while (nItem < nCount)
+ {
+ Reference<XNamed> xMember(xMembersIndex->getByIndex(nItem), UNO_QUERY);
+ if (xMember.is() && (aName == xMember->getName()))
+ {
+ return Any( Reference< XPropertySet >( GetObjectByIndex_Impl( nItem ) ) );
+ }
+ ++nItem;
+ }
+ throw NoSuchElementException("Name \"" + aName + "\" not found", getXWeak());
+ }
+ return Any();
+}
+
+Sequence<OUString> SAL_CALL ScDataPilotItemsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ Sequence< OUString > aSeq;
+ if( ScDPObject* pDPObj = GetDPObject() )
+ pDPObj->GetMemberNames( lcl_GetObjectIndex( pDPObj, maFieldId ), aSeq );
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScDataPilotItemsObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ bool bFound = false;
+ Reference<XNameAccess> xMembers = GetMembers();
+ if (xMembers.is())
+ {
+ Reference<XIndexAccess> xMembersIndex(new ScNameToIndexAccess( xMembers ));
+ sal_Int32 nCount = xMembersIndex->getCount();
+ sal_Int32 nItem = 0;
+ while (nItem < nCount && !bFound )
+ {
+ Reference<XNamed> xMember(xMembersIndex->getByIndex(nItem), UNO_QUERY);
+ if (xMember.is() && aName == xMember->getName())
+ bFound = true;
+ else
+ nItem++;
+ }
+ }
+ return bFound;
+}
+
+// XEnumerationAccess
+
+Reference<XEnumeration> SAL_CALL ScDataPilotItemsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.DataPilotItemsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDataPilotItemsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return GetMemberCount();
+}
+
+Any SAL_CALL ScDataPilotItemsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ Reference< XPropertySet > xItem( GetObjectByIndex_Impl( nIndex ) );
+ if (!xItem.is())
+ throw IndexOutOfBoundsException();
+ return Any( xItem );
+}
+
+uno::Type SAL_CALL ScDataPilotItemsObj::getElementType()
+{
+ return cppu::UnoType<XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL ScDataPilotItemsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+ScDataPilotItemObj::ScDataPilotItemObj( ScDataPilotDescriptorBase& rParent, const ScFieldIdentifier& rFieldId, sal_Int32 nIndex ) :
+ ScDataPilotChildObjBase( rParent, rFieldId ),
+ maPropSet( lcl_GetDataPilotItemMap() ),
+ mnIndex( nIndex )
+{
+}
+
+ScDataPilotItemObj::~ScDataPilotItemObj()
+{
+}
+
+ // XNamed
+OUString SAL_CALL ScDataPilotItemObj::getName()
+{
+ SolarMutexGuard aGuard;
+ OUString sRet;
+ Reference<XNameAccess> xMembers = GetMembers();
+ if (xMembers.is())
+ {
+ Reference<XIndexAccess> xMembersIndex(new ScNameToIndexAccess( xMembers ));
+ sal_Int32 nCount = xMembersIndex->getCount();
+ if (mnIndex < nCount)
+ {
+ Reference<XNamed> xMember(xMembersIndex->getByIndex(mnIndex), UNO_QUERY);
+ sRet = xMember->getName();
+ }
+ }
+ return sRet;
+}
+
+void SAL_CALL ScDataPilotItemObj::setName( const OUString& /* aName */ )
+{
+}
+
+ // XPropertySet
+Reference< XPropertySetInfo >
+ SAL_CALL ScDataPilotItemObj::getPropertySetInfo( )
+{
+ SolarMutexGuard aGuard;
+ static Reference<XPropertySetInfo> aRef =
+ new SfxItemPropertySetInfo( maPropSet.getPropertyMap() );
+ return aRef;
+}
+
+void SAL_CALL ScDataPilotItemObj::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScDPObject* pDPObj = nullptr;
+ ScDPSaveDimension* pDim = GetDPDimension( &pDPObj );
+ if(!pDim)
+ return;
+
+ Reference<XNameAccess> xMembers = GetMembers();
+ if( !xMembers.is() )
+ return;
+
+ Reference<XIndexAccess> xMembersIndex( new ScNameToIndexAccess( xMembers ) );
+ sal_Int32 nCount = xMembersIndex->getCount();
+ if( mnIndex >= nCount )
+ return;
+
+ Reference<XNamed> xMember(xMembersIndex->getByIndex(mnIndex), UNO_QUERY);
+ OUString sName(xMember->getName());
+ ScDPSaveMember* pMember = pDim->GetMemberByName(sName);
+ if (!pMember)
+ return;
+
+ bool bGetNewIndex = false;
+ if ( aPropertyName == SC_UNONAME_SHOWDETAIL )
+ pMember->SetShowDetails(cppu::any2bool(aValue));
+ else if ( aPropertyName == SC_UNONAME_ISHIDDEN )
+ pMember->SetIsVisible(!cppu::any2bool(aValue));
+ else if ( aPropertyName == SC_UNONAME_POS )
+ {
+ sal_Int32 nNewPos = 0;
+ if ( !( aValue >>= nNewPos ) || nNewPos < 0 || nNewPos >= nCount )
+ throw IllegalArgumentException();
+
+ pDim->SetMemberPosition( sName, nNewPos );
+ // get new effective index (depends on sorting mode, which isn't modified)
+ bGetNewIndex = true;
+
+ }
+ SetDPObject( pDPObj );
+
+ if ( bGetNewIndex ) // after SetDPObject, get the new index
+ {
+ Sequence< OUString > aItemNames = xMembers->getElementNames();
+ sal_Int32 nItemCount = aItemNames.getLength();
+ for (sal_Int32 nItem=0; nItem<nItemCount; ++nItem)
+ if (aItemNames[nItem] == sName)
+ mnIndex = nItem;
+ }
+}
+
+Any SAL_CALL ScDataPilotItemObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ Any aRet;
+ if( ScDPSaveDimension* pDim = GetDPDimension() )
+ {
+ Reference< XNameAccess > xMembers = GetMembers();
+ if( xMembers.is() )
+ {
+ Reference< XIndexAccess > xMembersIndex( new ScNameToIndexAccess( xMembers ) );
+ sal_Int32 nCount = xMembersIndex->getCount();
+ if( mnIndex < nCount )
+ {
+ Reference< XNamed > xMember( xMembersIndex->getByIndex( mnIndex ), UNO_QUERY );
+ OUString sName( xMember->getName() );
+ ScDPSaveMember* pMember = pDim->GetExistingMemberByName( sName );
+ if ( aPropertyName == SC_UNONAME_SHOWDETAIL )
+ {
+ if (pMember && pMember->HasShowDetails())
+ {
+ aRet <<= pMember->GetShowDetails();
+ }
+ else
+ {
+ Reference< XPropertySet > xMemberProps( xMember, UNO_QUERY );
+ if( xMemberProps.is() )
+ aRet = xMemberProps->getPropertyValue( SC_UNO_DP_SHOWDETAILS );
+ else
+ aRet <<= true;
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_ISHIDDEN )
+ {
+ if (pMember && pMember->HasIsVisible())
+ {
+ aRet <<= !pMember->GetIsVisible();
+ }
+ else
+ {
+ Reference< XPropertySet > xMemberProps( xMember, UNO_QUERY );
+ if( xMemberProps.is() )
+ aRet <<= !cppu::any2bool( xMemberProps->getPropertyValue( SC_UNO_DP_ISVISIBLE ) );
+ else
+ aRet <<= false;
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_POS )
+ {
+ aRet <<= mnIndex;
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL ScDataPilotItemObj::addPropertyChangeListener(
+ const OUString& /* aPropertyName */, const Reference< XPropertyChangeListener >& /* xListener */ )
+{
+}
+
+void SAL_CALL ScDataPilotItemObj::removePropertyChangeListener(
+ const OUString& /* aPropertyName */, const Reference< XPropertyChangeListener >& /* aListener */ )
+{
+}
+
+void SAL_CALL ScDataPilotItemObj::addVetoableChangeListener(
+ const OUString& /* PropertyName */, const Reference< XVetoableChangeListener >& /* aListener */ )
+{
+}
+
+void SAL_CALL ScDataPilotItemObj::removeVetoableChangeListener(
+ const OUString& /* PropertyName */, const Reference< XVetoableChangeListener >& /* aListener */ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/datauno.cxx b/sc/source/ui/unoobj/datauno.cxx
new file mode 100644
index 0000000000..1bbd661a4f
--- /dev/null
+++ b/sc/source/ui/unoobj/datauno.cxx
@@ -0,0 +1,2384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <datauno.hxx>
+
+#include <svl/hint.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/util/SortField.hpp>
+#include <com/sun/star/table/TableSortField.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/table/TableOrientation.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/FilterFieldType.hpp>
+#include <com/sun/star/sheet/FilterOperator2.hpp>
+#include <com/sun/star/sheet/TableFilterField2.hpp>
+
+#include <dapiuno.hxx>
+#include <cellsuno.hxx>
+#include <miscuno.hxx>
+#include <targuno.hxx>
+#include <rangeutl.hxx>
+#include <dbdata.hxx>
+#include <docsh.hxx>
+#include <dbdocfun.hxx>
+#include <unonames.hxx>
+#include <globalnames.hxx>
+#include <convuno.hxx>
+#include <hints.hxx>
+#include <attrib.hxx>
+#include <dpshttab.hxx>
+#include <queryentry.hxx>
+#include <dputil.hxx>
+#include <sortparam.hxx>
+#include <dpobject.hxx>
+#include <filterentries.hxx>
+
+#include <comphelper/extract.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+using namespace css::sheet;
+
+// everything without Which-ID, map only for PropertySetInfo
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetSubTotalPropertyMap()
+{
+ // some old property names are for 5.2 compatibility
+
+ static const SfxItemPropertyMapEntry aSubTotalPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_BINDFMT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_CASE, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ENABSORT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ENUSLIST, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_FORMATS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_INSBRK, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ISCASE, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_MAXFLD, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNONAME_SORTASC, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ULIST, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_UINDEX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNONAME_USINDEX, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ };
+ return aSubTotalPropertyMap_Impl;
+}
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetFilterPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aFilterPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_CONTHDR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_COPYOUT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ISCASE, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_MAXFLD, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNONAME_ORIENT, 0, cppu::UnoType<table::TableOrientation>::get(), 0, 0},
+ { SC_UNONAME_OUTPOS, 0, cppu::UnoType<table::CellAddress>::get(), 0, 0},
+ { SC_UNONAME_SAVEOUT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_SKIPDUP, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_USEREGEX, 0, cppu::UnoType<bool>::get(), 0, 0},
+ };
+ return aFilterPropertyMap_Impl;
+}
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetDBRangePropertyMap()
+{
+ static const SfxItemPropertyMapEntry aDBRangePropertyMap_Impl[] =
+ {
+ { SC_UNONAME_AUTOFLT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_FLTCRT, 0, cppu::UnoType<table::CellRangeAddress>::get(), 0, 0},
+ { SC_UNONAME_FROMSELECT,0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_ISUSER, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_KEEPFORM, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_LINKDISPBIT, 0, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_LINKDISPNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_MOVCELLS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_REFPERIOD, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNONAME_STRIPDAT, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_TOKENINDEX,0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_USEFLTCRT,0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_TOTALSROW,0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_CONTHDR ,0, cppu::UnoType<bool>::get(), 0, 0},
+ };
+ return aDBRangePropertyMap_Impl;
+}
+
+SC_SIMPLE_SERVICE_INFO( ScConsolidationDescriptor, "ScConsolidationDescriptor", "com.sun.star.sheet.ConsolidationDescriptor" )
+SC_SIMPLE_SERVICE_INFO( ScDatabaseRangesObj, "ScDatabaseRangesObj", "com.sun.star.sheet.DatabaseRanges" )
+SC_SIMPLE_SERVICE_INFO( ScFilterDescriptorBase, "ScFilterDescriptorBase", "com.sun.star.sheet.SheetFilterDescriptor" )
+SC_SIMPLE_SERVICE_INFO( ScSubTotalDescriptorBase, "ScSubTotalDescriptorBase", "com.sun.star.sheet.SubTotalDescriptor" )
+SC_SIMPLE_SERVICE_INFO( ScSubTotalFieldObj, "ScSubTotalFieldObj", "com.sun.star.sheet.SubTotalField" )
+
+sheet::GeneralFunction ScDataUnoConversion::SubTotalToGeneral( ScSubTotalFunc eSubTotal )
+{
+ sheet::GeneralFunction eGeneral;
+ switch (eSubTotal)
+ {
+ case SUBTOTAL_FUNC_NONE: eGeneral = sheet::GeneralFunction_NONE; break;
+ case SUBTOTAL_FUNC_AVE: eGeneral = sheet::GeneralFunction_AVERAGE; break;
+ case SUBTOTAL_FUNC_CNT: eGeneral = sheet::GeneralFunction_COUNTNUMS; break;
+ case SUBTOTAL_FUNC_CNT2: eGeneral = sheet::GeneralFunction_COUNT; break;
+ case SUBTOTAL_FUNC_MAX: eGeneral = sheet::GeneralFunction_MAX; break;
+ case SUBTOTAL_FUNC_MIN: eGeneral = sheet::GeneralFunction_MIN; break;
+ case SUBTOTAL_FUNC_PROD: eGeneral = sheet::GeneralFunction_PRODUCT; break;
+ case SUBTOTAL_FUNC_STD: eGeneral = sheet::GeneralFunction_STDEV; break;
+ case SUBTOTAL_FUNC_STDP: eGeneral = sheet::GeneralFunction_STDEVP; break;
+ case SUBTOTAL_FUNC_SUM: eGeneral = sheet::GeneralFunction_SUM; break;
+ case SUBTOTAL_FUNC_VAR: eGeneral = sheet::GeneralFunction_VAR; break;
+ case SUBTOTAL_FUNC_VARP: eGeneral = sheet::GeneralFunction_VARP; break;
+ default:
+ OSL_FAIL("SubTotalToGeneral: wrong enum");
+ eGeneral = sheet::GeneralFunction_NONE;
+ break;
+ }
+ return eGeneral;
+}
+
+void ScImportDescriptor::FillProperties( uno::Sequence<beans::PropertyValue>& rSeq, const ScImportParam& rParam )
+{
+ OSL_ENSURE( rSeq.getLength() == GetPropertyCount(), "wrong Count" );
+
+ beans::PropertyValue* pArray = rSeq.getArray();
+
+ sheet::DataImportMode eMode = sheet::DataImportMode_NONE;
+ if ( rParam.bImport )
+ {
+ if ( rParam.bSql )
+ eMode = sheet::DataImportMode_SQL;
+ else if ( rParam.nType == ScDbQuery )
+ eMode = sheet::DataImportMode_QUERY;
+ else
+ eMode = sheet::DataImportMode_TABLE; // type always ScDbQuery or ScDbTable
+ }
+
+ svx::ODataAccessDescriptor aDescriptor;
+ aDescriptor.setDataSource(rParam.aDBName);
+ if (aDescriptor.has( svx::DataAccessDescriptorProperty::DataSource ))
+ {
+ pArray[0].Name = SC_UNONAME_DBNAME;
+ pArray[0].Value <<= rParam.aDBName;
+ }
+ else if (aDescriptor.has( svx::DataAccessDescriptorProperty::ConnectionResource ))
+ {
+ pArray[0].Name = SC_UNONAME_CONRES;
+ pArray[0].Value <<= rParam.aDBName;
+ }
+
+ pArray[1].Name = SC_UNONAME_SRCTYPE;
+ pArray[1].Value <<= eMode;
+
+ pArray[2].Name = SC_UNONAME_SRCOBJ;
+ pArray[2].Value <<= rParam.aStatement;
+
+ pArray[3].Name = SC_UNONAME_ISNATIVE;
+ pArray[3].Value <<= rParam.bNative;
+}
+
+void ScImportDescriptor::FillImportParam( ScImportParam& rParam, const uno::Sequence<beans::PropertyValue>& rSeq )
+{
+ OUString aStrVal;
+ for (const beans::PropertyValue& rProp : rSeq)
+ {
+ OUString aPropName(rProp.Name);
+
+ if (aPropName == SC_UNONAME_ISNATIVE)
+ rParam.bNative = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_DBNAME)
+ {
+ if ( rProp.Value >>= aStrVal )
+ rParam.aDBName = aStrVal;
+ }
+ else if (aPropName == SC_UNONAME_CONRES)
+ {
+ if ( rProp.Value >>= aStrVal )
+ rParam.aDBName = aStrVal;
+ }
+ else if (aPropName == SC_UNONAME_SRCOBJ)
+ {
+ if ( rProp.Value >>= aStrVal )
+ rParam.aStatement = aStrVal;
+ }
+ else if (aPropName == SC_UNONAME_SRCTYPE)
+ {
+ //! test for correct enum type?
+ sheet::DataImportMode eMode = static_cast<sheet::DataImportMode>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ));
+ switch (eMode)
+ {
+ case sheet::DataImportMode_NONE:
+ rParam.bImport = false;
+ break;
+ case sheet::DataImportMode_SQL:
+ rParam.bImport = true;
+ rParam.bSql = true;
+ break;
+ case sheet::DataImportMode_TABLE:
+ rParam.bImport = true;
+ rParam.bSql = false;
+ rParam.nType = ScDbTable;
+ break;
+ case sheet::DataImportMode_QUERY:
+ rParam.bImport = true;
+ rParam.bSql = false;
+ rParam.nType = ScDbQuery;
+ break;
+ default:
+ OSL_FAIL("wrong mode");
+ rParam.bImport = false;
+ }
+ }
+ }
+}
+
+void ScSortDescriptor::FillProperties( uno::Sequence<beans::PropertyValue>& rSeq, const ScSortParam& rParam )
+{
+ OSL_ENSURE( rSeq.getLength() == GetPropertyCount(), "wrong count" );
+
+ beans::PropertyValue* pArray = rSeq.getArray();
+
+ // gather Uno values together
+
+ table::CellAddress aOutPos;
+ aOutPos.Sheet = rParam.nDestTab;
+ aOutPos.Column = rParam.nDestCol;
+ aOutPos.Row = rParam.nDestRow;
+
+ sal_uInt16 nSortCount = 0;
+ while ( nSortCount < rParam.GetSortKeyCount() && rParam.maKeyState[nSortCount].bDoSort )
+ ++nSortCount;
+
+ uno::Sequence<table::TableSortField> aFields(nSortCount);
+ if (nSortCount)
+ {
+ table::TableSortField* pFieldArray = aFields.getArray();
+ for (sal_uInt16 i=0; i<nSortCount; i++)
+ {
+ pFieldArray[i].Field = rParam.maKeyState[i].nField;
+ pFieldArray[i].IsAscending = rParam.maKeyState[i].bAscending;
+ pFieldArray[i].FieldType = table::TableSortFieldType_AUTOMATIC; // always automatic
+ pFieldArray[i].IsCaseSensitive = rParam.bCaseSens;
+ pFieldArray[i].CollatorLocale = rParam.aCollatorLocale;
+ pFieldArray[i].CollatorAlgorithm = rParam.aCollatorAlgorithm;
+ }
+ }
+
+ // fill the sequence
+
+ pArray[0].Name = SC_UNONAME_ISSORTCOLUMNS;
+ pArray[0].Value <<= !rParam.bByRow;
+
+ pArray[1].Name = SC_UNONAME_CONTHDR;
+ pArray[1].Value <<= rParam.bHasHeader;
+
+ pArray[2].Name = SC_UNONAME_MAXFLD;
+ pArray[2].Value <<= static_cast<sal_Int32>( rParam.GetSortKeyCount() );
+
+ pArray[3].Name = SC_UNONAME_SORTFLD;
+ pArray[3].Value <<= aFields;
+
+ pArray[4].Name = SC_UNONAME_BINDFMT;
+ pArray[4].Value <<= rParam.aDataAreaExtras.mbCellFormats;
+
+ pArray[5].Name = SC_UNONAME_COPYOUT;
+ pArray[5].Value <<= !rParam.bInplace;
+
+ pArray[6].Name = SC_UNONAME_OUTPOS;
+ pArray[6].Value <<= aOutPos;
+
+ pArray[7].Name = SC_UNONAME_ISULIST;
+ pArray[7].Value <<= rParam.bUserDef;
+
+ pArray[8].Name = SC_UNONAME_UINDEX;
+ pArray[8].Value <<= static_cast<sal_Int32>( rParam.nUserIndex );
+}
+
+void ScSortDescriptor::FillSortParam( ScSortParam& rParam, const uno::Sequence<beans::PropertyValue>& rSeq )
+{
+ sal_Int32 nSortSize = static_cast<sal_Int32>(rParam.GetSortKeyCount());
+
+ for (const beans::PropertyValue& rProp : rSeq)
+ {
+ OUString aPropName(rProp.Name);
+
+ if (aPropName == SC_UNONAME_ORIENT)
+ {
+ //! test for correct enum type?
+ table::TableOrientation eOrient = static_cast<table::TableOrientation>(ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ));
+ rParam.bByRow = ( eOrient != table::TableOrientation_COLUMNS );
+ }
+ else if (aPropName == SC_UNONAME_ISSORTCOLUMNS)
+ {
+ rParam.bByRow = !::cppu::any2bool(rProp.Value);
+ }
+ else if (aPropName == SC_UNONAME_CONTHDR)
+ rParam.bHasHeader = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_MAXFLD)
+ {
+ sal_Int32 nVal;
+ if ( (rProp.Value >>= nVal) && nVal > nSortSize )
+ {
+ //! specify exceptions
+ //! throw lang::IllegalArgumentException();
+ }
+ }
+ else if (aPropName == SC_UNONAME_SORTFLD)
+ {
+ uno::Sequence<util::SortField> aSeq;
+ uno::Sequence<table::TableSortField> aNewSeq;
+ if ( rProp.Value >>= aSeq )
+ {
+ sal_Int32 nCount = aSeq.getLength();
+ sal_Int32 i;
+ if ( nCount > static_cast<sal_Int32>( rParam.GetSortKeyCount() ) )
+ {
+ // tdf#105301 - increase the size of the sorting keys
+ nSortSize = nCount;
+ rParam.maKeyState.resize(nCount);
+ }
+ const util::SortField* pFieldArray = aSeq.getConstArray();
+ for (i=0; i<nCount; i++)
+ {
+ rParam.maKeyState[i].nField = static_cast<SCCOLROW>( pFieldArray[i].Field );
+ rParam.maKeyState[i].bAscending = pFieldArray[i].SortAscending;
+
+ // FieldType is ignored
+ rParam.maKeyState[i].bDoSort = true;
+ }
+ for (i=nCount; i<nSortSize; i++)
+ rParam.maKeyState[i].bDoSort = false;
+ }
+ else if ( rProp.Value >>= aNewSeq )
+ {
+ sal_Int32 nCount = aNewSeq.getLength();
+ sal_Int32 i;
+ if ( nCount > nSortSize )
+ {
+ nCount = nSortSize;
+ rParam.maKeyState.resize(nCount);
+ }
+ const table::TableSortField* pFieldArray = aNewSeq.getConstArray();
+ for (i=0; i<nCount; i++)
+ {
+ rParam.maKeyState[i].nField = static_cast<SCCOLROW>( pFieldArray[i].Field );
+ rParam.maKeyState[i].bAscending = pFieldArray[i].IsAscending;
+
+ // only one is possible, sometime we should make it possible to have different for every entry
+ rParam.bCaseSens = pFieldArray[i].IsCaseSensitive;
+ rParam.aCollatorLocale = pFieldArray[i].CollatorLocale;
+ rParam.aCollatorAlgorithm = pFieldArray[i].CollatorAlgorithm;
+
+ // FieldType is ignored
+ rParam.maKeyState[i].bDoSort = true;
+ }
+ for (i=nCount; i<nSortSize; i++)
+ rParam.maKeyState[i].bDoSort = false;
+ }
+ }
+ else if (aPropName == SC_UNONAME_ISCASE)
+ {
+ rParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ }
+ else if (aPropName == SC_UNONAME_BINDFMT)
+ rParam.aDataAreaExtras.mbCellFormats = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_COPYOUT)
+ rParam.bInplace = !ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_OUTPOS)
+ {
+ table::CellAddress aAddress;
+ if ( rProp.Value >>= aAddress )
+ {
+ rParam.nDestTab = aAddress.Sheet;
+ rParam.nDestCol = static_cast<SCCOL>(aAddress.Column);
+ rParam.nDestRow = static_cast<SCROW>(aAddress.Row);
+ }
+ }
+ else if (aPropName == SC_UNONAME_ISULIST)
+ rParam.bUserDef = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_UINDEX)
+ {
+ sal_Int32 nVal = 0;
+ if ( rProp.Value >>= nVal )
+ rParam.nUserIndex = static_cast<sal_uInt16>(nVal);
+ }
+ else if (aPropName == SC_UNONAME_COLLLOC)
+ {
+ rProp.Value >>= rParam.aCollatorLocale;
+ }
+ else if (aPropName == SC_UNONAME_COLLALG)
+ {
+ OUString sStr;
+ if ( rProp.Value >>= sStr )
+ rParam.aCollatorAlgorithm = sStr;
+ }
+ }
+}
+
+ScSubTotalFieldObj::ScSubTotalFieldObj( ScSubTotalDescriptorBase* pDesc, sal_uInt16 nP ) :
+ xParent( pDesc ),
+ nPos( nP )
+{
+ OSL_ENSURE(pDesc, "ScSubTotalFieldObj: Parent is 0");
+}
+
+ScSubTotalFieldObj::~ScSubTotalFieldObj()
+{
+}
+
+// XSubTotalField
+
+sal_Int32 SAL_CALL ScSubTotalFieldObj::getGroupColumn()
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ xParent->GetData(aParam);
+
+ return aParam.nField[nPos];
+}
+
+void SAL_CALL ScSubTotalFieldObj::setGroupColumn( sal_Int32 nGroupColumn )
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ xParent->GetData(aParam);
+
+ aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn);
+
+ xParent->PutData(aParam);
+}
+
+uno::Sequence<sheet::SubTotalColumn> SAL_CALL ScSubTotalFieldObj::getSubTotalColumns()
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ xParent->GetData(aParam);
+
+ SCCOL nCount = aParam.nSubTotals[nPos];
+ uno::Sequence<sheet::SubTotalColumn> aSeq(nCount);
+ sheet::SubTotalColumn* pAry = aSeq.getArray();
+ for (SCCOL i=0; i<nCount; i++)
+ {
+ pAry[i].Column = aParam.pSubTotals[nPos][i];
+ pAry[i].Function = ScDataUnoConversion::SubTotalToGeneral(
+ aParam.pFunctions[nPos][i] );
+ }
+ return aSeq;
+}
+
+void SAL_CALL ScSubTotalFieldObj::setSubTotalColumns(
+ const uno::Sequence<sheet::SubTotalColumn>& aSubTotalColumns )
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ xParent->GetData(aParam);
+
+ sal_uInt32 nColCount = aSubTotalColumns.getLength();
+ if ( nColCount <= sal::static_int_cast<sal_uInt32>(SCCOL_MAX) )
+ {
+ SCCOL nCount = static_cast<SCCOL>(nColCount);
+ aParam.nSubTotals[nPos] = nCount;
+ if (nCount != 0)
+ {
+ aParam.pSubTotals[nPos].reset(new SCCOL[nCount]);
+ aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]);
+
+ const sheet::SubTotalColumn* pAry = aSubTotalColumns.getConstArray();
+ for (SCCOL i=0; i<nCount; i++)
+ {
+ aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column);
+ aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function));
+ }
+ }
+ else
+ {
+ aParam.pSubTotals[nPos].reset();
+ aParam.pFunctions[nPos].reset();
+ }
+ }
+ //! otherwise exception or so? (too many columns)
+
+ xParent->PutData(aParam);
+}
+
+ScSubTotalDescriptorBase::ScSubTotalDescriptorBase() :
+ aPropSet( lcl_GetSubTotalPropertyMap() )
+{
+}
+
+ScSubTotalDescriptorBase::~ScSubTotalDescriptorBase()
+{
+}
+
+// XSubTotalDescriptor
+
+rtl::Reference<ScSubTotalFieldObj> ScSubTotalDescriptorBase::GetObjectByIndex_Impl(sal_uInt16 nIndex)
+{
+ if ( nIndex < getCount() )
+ return new ScSubTotalFieldObj( this, nIndex );
+ return nullptr;
+}
+
+void SAL_CALL ScSubTotalDescriptorBase::clear()
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ GetData(aParam);
+
+ for (bool & rn : aParam.bGroupActive)
+ rn = false;
+
+ //! notify the field objects???
+
+ PutData(aParam);
+}
+
+void SAL_CALL ScSubTotalDescriptorBase::addNew(
+ const uno::Sequence<sheet::SubTotalColumn>& aSubTotalColumns,
+ sal_Int32 nGroupColumn )
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ GetData(aParam);
+
+ sal_uInt16 nPos = 0;
+ while ( nPos < MAXSUBTOTAL && aParam.bGroupActive[nPos] )
+ ++nPos;
+
+ sal_uInt32 nColCount = aSubTotalColumns.getLength();
+
+ if ( nPos >= MAXSUBTOTAL || nColCount > sal::static_int_cast<sal_uInt32>(SCCOL_MAX) )
+ // too many fields / columns
+ throw uno::RuntimeException(); // no other exceptions specified
+
+ aParam.bGroupActive[nPos] = true;
+ aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn);
+
+ aParam.pSubTotals[nPos].reset();
+ aParam.pFunctions[nPos].reset();
+
+ SCCOL nCount = static_cast<SCCOL>(nColCount);
+ aParam.nSubTotals[nPos] = nCount;
+ if (nCount != 0)
+ {
+ aParam.pSubTotals[nPos].reset(new SCCOL[nCount]);
+ aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]);
+
+ const sheet::SubTotalColumn* pAry = aSubTotalColumns.getConstArray();
+ for (SCCOL i=0; i<nCount; i++)
+ {
+ aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column);
+ aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function));
+ }
+ }
+ else
+ {
+ aParam.pSubTotals[nPos].reset();
+ aParam.pFunctions[nPos].reset();
+ }
+
+ PutData(aParam);
+}
+
+// flags/settings as properties
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScSubTotalDescriptorBase::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.SubTotalFieldsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScSubTotalDescriptorBase::getCount()
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ GetData(aParam);
+
+ sal_uInt16 nCount = 0;
+ while ( nCount < MAXSUBTOTAL && aParam.bGroupActive[nCount] )
+ ++nCount;
+ return nCount;
+}
+
+uno::Any SAL_CALL ScSubTotalDescriptorBase::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XSubTotalField> xField(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
+ if (!xField.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xField);
+}
+
+uno::Type SAL_CALL ScSubTotalDescriptorBase::getElementType()
+{
+ return cppu::UnoType<sheet::XSubTotalField>::get();
+}
+
+sal_Bool SAL_CALL ScSubTotalDescriptorBase::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSubTotalDescriptorBase::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScSubTotalDescriptorBase::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ GetData(aParam);
+
+ // some old property names are for 5.2 compatibility
+
+ if (aPropertyName == SC_UNONAME_CASE || aPropertyName == SC_UNONAME_ISCASE )
+ aParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_FORMATS || aPropertyName == SC_UNONAME_BINDFMT )
+ aParam.bIncludePattern = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_ENABSORT )
+ aParam.bDoSort = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_SORTASC )
+ aParam.bAscending = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_INSBRK )
+ aParam.bPagebreak = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_ULIST || aPropertyName == SC_UNONAME_ENUSLIST )
+ aParam.bUserDef = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_UINDEX || aPropertyName == SC_UNONAME_USINDEX )
+ {
+ sal_Int32 nVal = 0;
+ if ( aValue >>= nVal )
+ aParam.nUserIndex = static_cast<sal_uInt16>(nVal);
+ }
+ else if (aPropertyName == SC_UNONAME_MAXFLD )
+ {
+ sal_Int32 nVal = 0;
+ if ( (aValue >>= nVal) && nVal > sal::static_int_cast<sal_Int32>(MAXSUBTOTAL) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ PutData(aParam);
+}
+
+uno::Any SAL_CALL ScSubTotalDescriptorBase::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ ScSubTotalParam aParam;
+ GetData(aParam);
+
+ uno::Any aRet;
+
+ // some old property names are for 5.2 compatibility
+
+ if (aPropertyName == SC_UNONAME_CASE || aPropertyName == SC_UNONAME_ISCASE )
+ aRet <<= aParam.bCaseSens;
+ else if (aPropertyName == SC_UNONAME_FORMATS || aPropertyName == SC_UNONAME_BINDFMT )
+ aRet <<= aParam.bIncludePattern;
+ else if (aPropertyName == SC_UNONAME_ENABSORT )
+ aRet <<= aParam.bDoSort;
+ else if (aPropertyName == SC_UNONAME_SORTASC )
+ aRet <<= aParam.bAscending;
+ else if (aPropertyName == SC_UNONAME_INSBRK )
+ aRet <<= aParam.bPagebreak;
+ else if (aPropertyName == SC_UNONAME_ULIST || aPropertyName == SC_UNONAME_ENUSLIST )
+ aRet <<= aParam.bUserDef;
+ else if (aPropertyName == SC_UNONAME_UINDEX || aPropertyName == SC_UNONAME_USINDEX )
+ aRet <<= static_cast<sal_Int32>(aParam.nUserIndex);
+ else if (aPropertyName == SC_UNONAME_MAXFLD )
+ aRet <<= sal_Int32(MAXSUBTOTAL);
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSubTotalDescriptorBase )
+
+ScSubTotalDescriptor::ScSubTotalDescriptor()
+{
+}
+
+ScSubTotalDescriptor::~ScSubTotalDescriptor()
+{
+}
+
+void ScSubTotalDescriptor::GetData( ScSubTotalParam& rParam ) const
+{
+ rParam = aStoredParam; // query for interface
+}
+
+void ScSubTotalDescriptor::PutData( const ScSubTotalParam& rParam )
+{
+ aStoredParam = rParam; // set by the interface
+}
+
+void ScSubTotalDescriptor::SetParam( const ScSubTotalParam& rNew )
+{
+ aStoredParam = rNew; // set from outside
+}
+
+ScRangeSubTotalDescriptor::ScRangeSubTotalDescriptor(ScDatabaseRangeObj* pPar) :
+ mxParent(pPar)
+{
+}
+
+ScRangeSubTotalDescriptor::~ScRangeSubTotalDescriptor()
+{
+}
+
+void ScRangeSubTotalDescriptor::GetData( ScSubTotalParam& rParam ) const
+{
+ if (mxParent.is())
+ mxParent->GetSubTotalParam( rParam );
+}
+
+void ScRangeSubTotalDescriptor::PutData( const ScSubTotalParam& rParam )
+{
+ if (mxParent.is())
+ mxParent->SetSubTotalParam( rParam );
+}
+
+ScConsolidationDescriptor::ScConsolidationDescriptor()
+{
+}
+
+ScConsolidationDescriptor::~ScConsolidationDescriptor()
+{
+}
+
+void ScConsolidationDescriptor::SetParam( const ScConsolidateParam& rNew )
+{
+ aParam = rNew;
+}
+
+// XConsolidationDescriptor
+
+sheet::GeneralFunction SAL_CALL ScConsolidationDescriptor::getFunction()
+{
+ SolarMutexGuard aGuard;
+ return ScDataUnoConversion::SubTotalToGeneral(aParam.eFunction);
+}
+
+void SAL_CALL ScConsolidationDescriptor::setFunction( sheet::GeneralFunction nFunction )
+{
+ SolarMutexGuard aGuard;
+ aParam.eFunction = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(nFunction));
+}
+
+uno::Sequence<table::CellRangeAddress> SAL_CALL ScConsolidationDescriptor::getSources()
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nCount = aParam.nDataAreaCount;
+ if (!aParam.pDataAreas)
+ nCount = 0;
+ table::CellRangeAddress aRange;
+ uno::Sequence<table::CellRangeAddress> aSeq(nCount);
+ table::CellRangeAddress* pAry = aSeq.getArray();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ScArea const & rArea = aParam.pDataAreas[i];
+ aRange.Sheet = rArea.nTab;
+ aRange.StartColumn = rArea.nColStart;
+ aRange.StartRow = rArea.nRowStart;
+ aRange.EndColumn = rArea.nColEnd;
+ aRange.EndRow = rArea.nRowEnd;
+ pAry[i] = aRange;
+ }
+ return aSeq;
+}
+
+void SAL_CALL ScConsolidationDescriptor::setSources(
+ const uno::Sequence<table::CellRangeAddress>& aSources )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nCount = static_cast<sal_uInt16>(aSources.getLength());
+ if (nCount)
+ {
+ const table::CellRangeAddress* pAry = aSources.getConstArray();
+ std::unique_ptr<ScArea[]> pNew(new ScArea[nCount]);
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++)
+ pNew[i] = ScArea( pAry[i].Sheet,
+ static_cast<SCCOL>(pAry[i].StartColumn), pAry[i].StartRow,
+ static_cast<SCCOL>(pAry[i].EndColumn), pAry[i].EndRow );
+
+ aParam.SetAreas( std::move(pNew), nCount ); // copy everything
+ }
+ else
+ aParam.ClearDataAreas();
+}
+
+table::CellAddress SAL_CALL ScConsolidationDescriptor::getStartOutputPosition()
+{
+ SolarMutexGuard aGuard;
+ table::CellAddress aPos;
+ aPos.Column = aParam.nCol;
+ aPos.Row = aParam.nRow;
+ aPos.Sheet = aParam.nTab;
+ return aPos;
+}
+
+void SAL_CALL ScConsolidationDescriptor::setStartOutputPosition(
+ const table::CellAddress& aStartOutputPosition )
+{
+ SolarMutexGuard aGuard;
+ aParam.nCol = static_cast<SCCOL>(aStartOutputPosition.Column);
+ aParam.nRow = static_cast<SCROW>(aStartOutputPosition.Row);
+ aParam.nTab = aStartOutputPosition.Sheet;
+}
+
+sal_Bool SAL_CALL ScConsolidationDescriptor::getUseColumnHeaders()
+{
+ SolarMutexGuard aGuard;
+ return aParam.bByCol;
+}
+
+void SAL_CALL ScConsolidationDescriptor::setUseColumnHeaders( sal_Bool bUseColumnHeaders )
+{
+ SolarMutexGuard aGuard;
+ aParam.bByCol = bUseColumnHeaders;
+}
+
+sal_Bool SAL_CALL ScConsolidationDescriptor::getUseRowHeaders()
+{
+ SolarMutexGuard aGuard;
+ return aParam.bByRow;
+}
+
+void SAL_CALL ScConsolidationDescriptor::setUseRowHeaders( sal_Bool bUseRowHeaders )
+{
+ SolarMutexGuard aGuard;
+ aParam.bByRow = bUseRowHeaders;
+}
+
+sal_Bool SAL_CALL ScConsolidationDescriptor::getInsertLinks()
+{
+ SolarMutexGuard aGuard;
+ return aParam.bReferenceData;
+}
+
+void SAL_CALL ScConsolidationDescriptor::setInsertLinks( sal_Bool bInsertLinks )
+{
+ SolarMutexGuard aGuard;
+ aParam.bReferenceData = bInsertLinks;
+}
+
+ScFilterDescriptorBase::ScFilterDescriptorBase(ScDocShell* pDocShell) :
+ aPropSet( lcl_GetFilterPropertyMap() ),
+ pDocSh(pDocShell)
+{
+ if (pDocSh)
+ pDocSh->GetDocument().AddUnoObject(*this);
+}
+
+ScFilterDescriptorBase::~ScFilterDescriptorBase()
+{
+ SolarMutexGuard g;
+
+ if (pDocSh)
+ pDocSh->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScFilterDescriptorBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocSh = nullptr; // invalid
+ }
+}
+
+// XSheetFilterDescriptor and XSheetFilterDescriptor2
+
+uno::Sequence<sheet::TableFilterField> SAL_CALL ScFilterDescriptorBase::getFilterFields()
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+
+ SCSIZE nEntries = aParam.GetEntryCount(); // allocated entries in Param
+ SCSIZE nCount = 0; // active
+ while ( nCount < nEntries &&
+ aParam.GetEntry(nCount).bDoQuery )
+ ++nCount;
+
+ sheet::TableFilterField aField;
+ uno::Sequence<sheet::TableFilterField> aSeq(static_cast<sal_Int32>(nCount));
+ sheet::TableFilterField* pAry = aSeq.getArray();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.GetQueryItems().empty())
+ continue;
+
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItems().front();
+
+ aField.Connection = (rEntry.eConnect == SC_AND) ? sheet::FilterConnection_AND :
+ sheet::FilterConnection_OR;
+ aField.Field = rEntry.nField;
+ aField.IsNumeric = rItem.meType != ScQueryEntry::ByString;
+ aField.StringValue = rItem.maString.getString();
+ aField.NumericValue = rItem.mfVal;
+
+ switch (rEntry.eOp) // ScQueryOp
+ {
+ case SC_EQUAL:
+ {
+ aField.Operator = sheet::FilterOperator_EQUAL;
+ if (rEntry.IsQueryByEmpty())
+ {
+ aField.Operator = sheet::FilterOperator_EMPTY;
+ aField.NumericValue = 0;
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aField.Operator = sheet::FilterOperator_NOT_EMPTY;
+ aField.NumericValue = 0;
+ }
+ }
+ break;
+ case SC_LESS: aField.Operator = sheet::FilterOperator_LESS; break;
+ case SC_GREATER: aField.Operator = sheet::FilterOperator_GREATER; break;
+ case SC_LESS_EQUAL: aField.Operator = sheet::FilterOperator_LESS_EQUAL; break;
+ case SC_GREATER_EQUAL: aField.Operator = sheet::FilterOperator_GREATER_EQUAL; break;
+ case SC_NOT_EQUAL: aField.Operator = sheet::FilterOperator_NOT_EQUAL; break;
+ case SC_TOPVAL: aField.Operator = sheet::FilterOperator_TOP_VALUES; break;
+ case SC_BOTVAL: aField.Operator = sheet::FilterOperator_BOTTOM_VALUES; break;
+ case SC_TOPPERC: aField.Operator = sheet::FilterOperator_TOP_PERCENT; break;
+ case SC_BOTPERC: aField.Operator = sheet::FilterOperator_BOTTOM_PERCENT; break;
+ default:
+ OSL_FAIL("wrong filter enum");
+ aField.Operator = sheet::FilterOperator_EMPTY;
+ }
+ pAry[i] = aField;
+ }
+ return aSeq;
+}
+
+namespace {
+
+template<typename T>
+void convertQueryEntryToUno(const ScQueryEntry& rEntry, T& rField)
+{
+ rField.Connection = (rEntry.eConnect == SC_AND) ? sheet::FilterConnection_AND : sheet::FilterConnection_OR;
+ rField.Field = rEntry.nField;
+
+ switch (rEntry.eOp) // ScQueryOp
+ {
+ case SC_EQUAL: rField.Operator = sheet::FilterOperator2::EQUAL; break;
+ case SC_LESS: rField.Operator = sheet::FilterOperator2::LESS; break;
+ case SC_GREATER: rField.Operator = sheet::FilterOperator2::GREATER; break;
+ case SC_LESS_EQUAL: rField.Operator = sheet::FilterOperator2::LESS_EQUAL; break;
+ case SC_GREATER_EQUAL: rField.Operator = sheet::FilterOperator2::GREATER_EQUAL; break;
+ case SC_NOT_EQUAL: rField.Operator = sheet::FilterOperator2::NOT_EQUAL; break;
+ case SC_TOPVAL: rField.Operator = sheet::FilterOperator2::TOP_VALUES; break;
+ case SC_BOTVAL: rField.Operator = sheet::FilterOperator2::BOTTOM_VALUES; break;
+ case SC_TOPPERC: rField.Operator = sheet::FilterOperator2::TOP_PERCENT; break;
+ case SC_BOTPERC: rField.Operator = sheet::FilterOperator2::BOTTOM_PERCENT; break;
+ case SC_CONTAINS: rField.Operator = sheet::FilterOperator2::CONTAINS; break;
+ case SC_DOES_NOT_CONTAIN: rField.Operator = sheet::FilterOperator2::DOES_NOT_CONTAIN; break;
+ case SC_BEGINS_WITH: rField.Operator = sheet::FilterOperator2::BEGINS_WITH; break;
+ case SC_DOES_NOT_BEGIN_WITH: rField.Operator = sheet::FilterOperator2::DOES_NOT_BEGIN_WITH; break;
+ case SC_ENDS_WITH: rField.Operator = sheet::FilterOperator2::ENDS_WITH; break;
+ case SC_DOES_NOT_END_WITH: rField.Operator = sheet::FilterOperator2::DOES_NOT_END_WITH; break;
+ default:
+ OSL_FAIL("Unknown filter operator value.");
+ rField.Operator = sheet::FilterOperator2::EMPTY;
+ }
+}
+
+template<typename T>
+void convertUnoToQueryEntry(const T& rField, ScQueryEntry& rEntry)
+{
+ rEntry.bDoQuery = true;
+ rEntry.eConnect = (rField.Connection == sheet::FilterConnection_AND) ? SC_AND : SC_OR;
+ rEntry.nField = rField.Field;
+
+ switch (rField.Operator) // FilterOperator
+ {
+ case sheet::FilterOperator2::EQUAL: rEntry.eOp = SC_EQUAL; break;
+ case sheet::FilterOperator2::LESS: rEntry.eOp = SC_LESS; break;
+ case sheet::FilterOperator2::GREATER: rEntry.eOp = SC_GREATER; break;
+ case sheet::FilterOperator2::LESS_EQUAL: rEntry.eOp = SC_LESS_EQUAL; break;
+ case sheet::FilterOperator2::GREATER_EQUAL: rEntry.eOp = SC_GREATER_EQUAL; break;
+ case sheet::FilterOperator2::NOT_EQUAL: rEntry.eOp = SC_NOT_EQUAL; break;
+ case sheet::FilterOperator2::TOP_VALUES: rEntry.eOp = SC_TOPVAL; break;
+ case sheet::FilterOperator2::BOTTOM_VALUES: rEntry.eOp = SC_BOTVAL; break;
+ case sheet::FilterOperator2::TOP_PERCENT: rEntry.eOp = SC_TOPPERC; break;
+ case sheet::FilterOperator2::BOTTOM_PERCENT: rEntry.eOp = SC_BOTPERC; break;
+ case sheet::FilterOperator2::CONTAINS: rEntry.eOp = SC_CONTAINS; break;
+ case sheet::FilterOperator2::DOES_NOT_CONTAIN: rEntry.eOp = SC_DOES_NOT_CONTAIN; break;
+ case sheet::FilterOperator2::BEGINS_WITH: rEntry.eOp = SC_BEGINS_WITH; break;
+ case sheet::FilterOperator2::DOES_NOT_BEGIN_WITH: rEntry.eOp = SC_DOES_NOT_BEGIN_WITH;break;
+ case sheet::FilterOperator2::ENDS_WITH: rEntry.eOp = SC_ENDS_WITH; break;
+ case sheet::FilterOperator2::DOES_NOT_END_WITH: rEntry.eOp = SC_DOES_NOT_END_WITH; break;
+ case sheet::FilterOperator2::EMPTY:
+ rEntry.SetQueryByEmpty();
+ break;
+ case sheet::FilterOperator2::NOT_EMPTY:
+ rEntry.SetQueryByNonEmpty();
+ break;
+ default:
+ OSL_FAIL("Unknown filter operator type.");
+ rEntry.eOp = SC_EQUAL;
+ }
+}
+
+void fillQueryParam(
+ ScQueryParam& rParam, ScDocument* pDoc,
+ const uno::Sequence<sheet::TableFilterField2>& aFilterFields)
+{
+ size_t nCount = static_cast<size_t>(aFilterFields.getLength());
+ rParam.Resize(nCount);
+
+ const sheet::TableFilterField2* pAry = aFilterFields.getConstArray();
+ svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ ScQueryEntry& rEntry = rParam.GetEntry(i);
+ convertUnoToQueryEntry(pAry[i], rEntry);
+
+ if (pAry[i].Operator != sheet::FilterOperator2::EMPTY && pAry[i].Operator != sheet::FilterOperator2::NOT_EMPTY)
+ {
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ rItems.resize(1);
+ ScQueryEntry::Item& rItem = rItems.front();
+ rItem.meType = pAry[i].IsNumeric ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ rItem.mfVal = pAry[i].NumericValue;
+ rItem.maString = rPool.intern(pAry[i].StringValue);
+
+ if (rItem.meType == ScQueryEntry::ByValue)
+ {
+ OUString aStr;
+ pDoc->GetFormatTable()->GetInputLineString(rItem.mfVal, 0, aStr);
+ rItem.maString = rPool.intern(aStr);
+ }
+ }
+ }
+
+ size_t nParamCount = rParam.GetEntryCount(); // if below eight Param isn't resized
+ for (size_t i = nCount; i < nParamCount; ++i)
+ rParam.GetEntry(i).bDoQuery = false; // reset surplus fields
+}
+
+void fillQueryParam(
+ ScQueryParam& rParam, ScDocument* pDoc,
+ const uno::Sequence<sheet::TableFilterField3>& aFilterFields)
+{
+ size_t nCount = static_cast<size_t>(aFilterFields.getLength());
+ rParam.Resize(nCount);
+
+ svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
+ const sheet::TableFilterField3* pAry = aFilterFields.getConstArray();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ ScQueryEntry& rEntry = rParam.GetEntry(i);
+ convertUnoToQueryEntry(pAry[i], rEntry);
+
+ if (pAry[i].Operator != sheet::FilterOperator2::EMPTY && pAry[i].Operator != sheet::FilterOperator2::NOT_EMPTY)
+ {
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ rItems.clear();
+ const uno::Sequence<sheet::FilterFieldValue>& rVals = pAry[i].Values;
+ for (const auto& rVal : rVals)
+ {
+ ScQueryEntry::Item aItem;
+ switch (rVal.FilterType)
+ {
+ case FilterFieldType::NUMERIC:
+ aItem.meType = ScQueryEntry::ByValue;
+ break;
+ case FilterFieldType::STRING:
+ aItem.meType = ScQueryEntry::ByString;
+ break;
+ case FilterFieldType::DATE:
+ aItem.meType = ScQueryEntry::ByDate;
+ break;
+ case FilterFieldType::TEXT_COLOR:
+ aItem.meType = ScQueryEntry::ByTextColor;
+ break;
+ case FilterFieldType::BACKGROUND_COLOR:
+ aItem.meType = ScQueryEntry::ByBackgroundColor;
+ break;
+ }
+ aItem.mfVal = rVal.NumericValue;
+ aItem.maString = rPool.intern(rVal.StringValue);
+
+ if (aItem.meType == ScQueryEntry::ByValue)
+ {
+ OUString aStr;
+ pDoc->GetFormatTable()->GetInputLineString(aItem.mfVal, 0, aStr);
+ aItem.maString = rPool.intern(aStr);
+ }
+ else if (aItem.meType == ScQueryEntry::ByTextColor
+ || aItem.meType == ScQueryEntry::ByBackgroundColor)
+ {
+ aItem.maColor = Color(ColorTransparency, rVal.ColorValue);
+ }
+
+ // filter all dates starting with the given date filter YYYY or YYYY-MM and filter all datetimes
+ // starting with the given datetime filter YYYY-MM-DD, YYYY-MM-DD HH, or YYYY-MM-DD HH:MM
+ if( aItem.meType == ScQueryEntry::ByDate && aItem.maString.getLength() < 19 )
+ {
+ ScFilterEntries aFilterEntries;
+ pDoc->GetFilterEntries(rEntry.nField, rParam.nRow1, rParam.nTab, aFilterEntries);
+ for( const auto& rFilter : aFilterEntries )
+ {
+ if( rFilter.GetString().startsWith(rVal.StringValue) )
+ {
+ aItem.maString = rPool.intern(rFilter.GetString());
+ rItems.push_back(aItem);
+ }
+ }
+ }
+ else
+ {
+ rItems.push_back(aItem);
+ }
+ }
+ }
+ }
+
+ size_t nParamCount = rParam.GetEntryCount(); // if below eight Param isn't resized
+ for (size_t i = nCount; i < nParamCount; ++i)
+ rParam.GetEntry(i).bDoQuery = false; // reset surplus fields
+}
+
+}
+
+uno::Sequence<sheet::TableFilterField2> SAL_CALL ScFilterDescriptorBase::getFilterFields2()
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+
+ SCSIZE nEntries = aParam.GetEntryCount(); // allocated entries in Param
+ SCSIZE nCount = 0; // active
+ while ( nCount < nEntries &&
+ aParam.GetEntry(nCount).bDoQuery )
+ ++nCount;
+
+ sheet::TableFilterField2 aField;
+ uno::Sequence<sheet::TableFilterField2> aSeq(static_cast<sal_Int32>(nCount));
+ sheet::TableFilterField2* pAry = aSeq.getArray();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ convertQueryEntryToUno(rEntry, aField);
+
+ bool bByEmpty = false;
+ if (aField.Operator == sheet::FilterOperator2::EQUAL)
+ {
+ if (rEntry.IsQueryByEmpty())
+ {
+ aField.Operator = sheet::FilterOperator2::EMPTY;
+ aField.NumericValue = 0;
+ bByEmpty = true;
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aField.Operator = sheet::FilterOperator2::NOT_EMPTY;
+ aField.NumericValue = 0;
+ bByEmpty = true;
+ }
+ }
+
+ if (!bByEmpty && !rEntry.GetQueryItems().empty())
+ {
+ const ScQueryEntry::Item& rItem = rEntry.GetQueryItems().front();
+ aField.IsNumeric = rItem.meType != ScQueryEntry::ByString;
+ aField.StringValue = rItem.maString.getString();
+ aField.NumericValue = rItem.mfVal;
+ }
+
+ pAry[i] = aField;
+ }
+ return aSeq;
+}
+
+uno::Sequence<sheet::TableFilterField3> SAL_CALL ScFilterDescriptorBase::getFilterFields3()
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+
+ SCSIZE nEntries = aParam.GetEntryCount(); // allocated entries in Param
+ SCSIZE nCount = 0; // active
+ while ( nCount < nEntries &&
+ aParam.GetEntry(nCount).bDoQuery )
+ ++nCount;
+
+ sheet::TableFilterField3 aField;
+ uno::Sequence<sheet::TableFilterField3> aSeq(static_cast<sal_Int32>(nCount));
+ sheet::TableFilterField3* pAry = aSeq.getArray();
+ for (SCSIZE i = 0; i < nCount; ++i)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ convertQueryEntryToUno(rEntry, aField);
+
+ bool bByEmpty = false;
+ if (aField.Operator == sheet::FilterOperator2::EQUAL)
+ {
+ if (rEntry.IsQueryByEmpty())
+ {
+ aField.Operator = sheet::FilterOperator2::EMPTY;
+ aField.Values.realloc(1);
+ aField.Values.getArray()[0].NumericValue = 0;
+ bByEmpty = true;
+ }
+ else if (rEntry.IsQueryByNonEmpty())
+ {
+ aField.Operator = sheet::FilterOperator2::NOT_EMPTY;
+ aField.Values.realloc(1);
+ aField.Values.getArray()[0].NumericValue = 0;
+ bByEmpty = true;
+ }
+ }
+
+ if (!bByEmpty)
+ {
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ size_t nItemCount = rItems.size();
+ aField.Values.realloc(nItemCount);
+ auto pValues = aField.Values.getArray();
+ size_t j = 0;
+ for (const auto& rItem : rItems)
+ {
+ pValues[j].IsNumeric = rItem.meType != ScQueryEntry::ByString;
+ pValues[j].StringValue = rItem.maString.getString();
+ pValues[j].NumericValue = rItem.mfVal;
+ ++j;
+ }
+ }
+
+ pAry[i] = aField;
+ }
+ return aSeq;
+}
+
+void SAL_CALL ScFilterDescriptorBase::setFilterFields(
+ const uno::Sequence<sheet::TableFilterField>& aFilterFields )
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+
+ SCSIZE nCount = static_cast<SCSIZE>(aFilterFields.getLength());
+ aParam.Resize( nCount );
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ const sheet::TableFilterField* pAry = aFilterFields.getConstArray();
+ SCSIZE i;
+ for (i=0; i<nCount; i++)
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ rItems.resize(1);
+ ScQueryEntry::Item& rItem = rItems.front();
+ rEntry.bDoQuery = true;
+ rEntry.eConnect = (pAry[i].Connection == sheet::FilterConnection_AND) ? SC_AND : SC_OR;
+ rEntry.nField = pAry[i].Field;
+ rItem.meType = pAry[i].IsNumeric ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ rItem.mfVal = pAry[i].NumericValue;
+ rItem.maString = rPool.intern(pAry[i].StringValue);
+
+ if (rItem.meType != ScQueryEntry::ByString)
+ {
+ OUString aStr;
+ rDoc.GetFormatTable()->GetInputLineString(rItem.mfVal, 0, aStr);
+ rItem.maString = rPool.intern(aStr);
+ }
+
+ switch (pAry[i].Operator) // FilterOperator
+ {
+ case sheet::FilterOperator_EQUAL: rEntry.eOp = SC_EQUAL; break;
+ case sheet::FilterOperator_LESS: rEntry.eOp = SC_LESS; break;
+ case sheet::FilterOperator_GREATER: rEntry.eOp = SC_GREATER; break;
+ case sheet::FilterOperator_LESS_EQUAL: rEntry.eOp = SC_LESS_EQUAL; break;
+ case sheet::FilterOperator_GREATER_EQUAL: rEntry.eOp = SC_GREATER_EQUAL; break;
+ case sheet::FilterOperator_NOT_EQUAL: rEntry.eOp = SC_NOT_EQUAL; break;
+ case sheet::FilterOperator_TOP_VALUES: rEntry.eOp = SC_TOPVAL; break;
+ case sheet::FilterOperator_BOTTOM_VALUES: rEntry.eOp = SC_BOTVAL; break;
+ case sheet::FilterOperator_TOP_PERCENT: rEntry.eOp = SC_TOPPERC; break;
+ case sheet::FilterOperator_BOTTOM_PERCENT: rEntry.eOp = SC_BOTPERC; break;
+ case sheet::FilterOperator_EMPTY:
+ rEntry.SetQueryByEmpty();
+ break;
+ case sheet::FilterOperator_NOT_EMPTY:
+ rEntry.SetQueryByNonEmpty();
+ break;
+ default:
+ OSL_FAIL("Wrong query enum");
+ rEntry.eOp = SC_EQUAL;
+ }
+ }
+
+ SCSIZE nParamCount = aParam.GetEntryCount(); // if below eight Param isn't resized
+ for (i=nCount; i<nParamCount; i++)
+ aParam.GetEntry(i).bDoQuery = false; // reset surplus fields
+
+ PutData(aParam);
+}
+
+void SAL_CALL ScFilterDescriptorBase::setFilterFields2(
+ const uno::Sequence<sheet::TableFilterField2>& aFilterFields )
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+ fillQueryParam(aParam, &pDocSh->GetDocument(), aFilterFields);
+ PutData(aParam);
+}
+
+void SAL_CALL ScFilterDescriptorBase::setFilterFields3(
+ const uno::Sequence<sheet::TableFilterField3>& aFilterFields )
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+ fillQueryParam(aParam, &pDocSh->GetDocument(), aFilterFields);
+ PutData(aParam);
+}
+
+// Rest sind Properties
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFilterDescriptorBase::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScFilterDescriptorBase::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+
+ if (aPropertyName == SC_UNONAME_CONTHDR)
+ aParam.bHasHeader = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_COPYOUT)
+ aParam.bInplace = !(ScUnoHelpFunctions::GetBoolFromAny( aValue ));
+ else if (aPropertyName == SC_UNONAME_ISCASE)
+ aParam.bCaseSens = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_MAXFLD)
+ {
+ // silently ignored
+ }
+ else if (aPropertyName == SC_UNONAME_ORIENT)
+ {
+ //! test for correct enum type?
+ table::TableOrientation eOrient = static_cast<table::TableOrientation>(ScUnoHelpFunctions::GetEnumFromAny( aValue ));
+ aParam.bByRow = ( eOrient != table::TableOrientation_COLUMNS );
+ }
+ else if (aPropertyName == SC_UNONAME_OUTPOS)
+ {
+ table::CellAddress aAddress;
+ if ( aValue >>= aAddress )
+ {
+ aParam.nDestTab = aAddress.Sheet;
+ aParam.nDestCol = static_cast<SCCOL>(aAddress.Column);
+ aParam.nDestRow = static_cast<SCROW>(aAddress.Row);
+ }
+ }
+ else if (aPropertyName == SC_UNONAME_SAVEOUT)
+ aParam.bDestPers = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if (aPropertyName == SC_UNONAME_SKIPDUP)
+ aParam.bDuplicate = !(ScUnoHelpFunctions::GetBoolFromAny( aValue ));
+ else if (aPropertyName == SC_UNONAME_USEREGEX)
+ aParam.eSearchType = ScUnoHelpFunctions::GetBoolFromAny( aValue ) ? utl::SearchParam::SearchType::Regexp :
+ utl::SearchParam::SearchType::Normal;
+
+ PutData(aParam);
+}
+
+uno::Any SAL_CALL ScFilterDescriptorBase::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ ScQueryParam aParam;
+ GetData(aParam);
+
+ uno::Any aRet;
+
+ if (aPropertyName == SC_UNONAME_CONTHDR )
+ aRet <<= aParam.bHasHeader;
+ else if (aPropertyName == SC_UNONAME_COPYOUT )
+ aRet <<= !(aParam.bInplace);
+ else if (aPropertyName == SC_UNONAME_ISCASE )
+ aRet <<= aParam.bCaseSens;
+ else if (aPropertyName == SC_UNONAME_MAXFLD )
+ aRet <<= static_cast<sal_Int32>(aParam.GetEntryCount());
+ else if (aPropertyName == SC_UNONAME_ORIENT )
+ {
+ table::TableOrientation eOrient = aParam.bByRow ? table::TableOrientation_ROWS :
+ table::TableOrientation_COLUMNS;
+ aRet <<= eOrient;
+ }
+ else if (aPropertyName == SC_UNONAME_OUTPOS )
+ {
+ table::CellAddress aOutPos;
+ aOutPos.Sheet = aParam.nDestTab;
+ aOutPos.Column = aParam.nDestCol;
+ aOutPos.Row = aParam.nDestRow;
+ aRet <<= aOutPos;
+ }
+ else if (aPropertyName == SC_UNONAME_SAVEOUT )
+ aRet <<= aParam.bDestPers;
+ else if (aPropertyName == SC_UNONAME_SKIPDUP )
+ aRet <<= !(aParam.bDuplicate);
+ else if (aPropertyName == SC_UNONAME_USEREGEX )
+ aRet <<= (aParam.eSearchType == utl::SearchParam::SearchType::Regexp);
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFilterDescriptorBase )
+
+ScFilterDescriptor::ScFilterDescriptor(ScDocShell* pDocShell)
+ :
+ ScFilterDescriptorBase(pDocShell)
+{
+}
+
+ScFilterDescriptor::~ScFilterDescriptor()
+{
+}
+
+void ScFilterDescriptor::GetData( ScQueryParam& rParam ) const
+{
+ rParam = aStoredParam; // query for interface
+}
+
+void ScFilterDescriptor::PutData( const ScQueryParam& rParam )
+{
+ aStoredParam = rParam; // set by the interface
+}
+
+void ScFilterDescriptor::SetParam( const ScQueryParam& rNew )
+{
+ aStoredParam = rNew; // set from outside
+}
+
+ScRangeFilterDescriptor::ScRangeFilterDescriptor(ScDocShell* pDocShell, ScDatabaseRangeObj* pPar) :
+ ScFilterDescriptorBase(pDocShell),
+ mxParent(pPar)
+{
+}
+
+ScRangeFilterDescriptor::~ScRangeFilterDescriptor()
+{
+}
+
+void ScRangeFilterDescriptor::GetData( ScQueryParam& rParam ) const
+{
+ if (mxParent.is())
+ mxParent->GetQueryParam( rParam );
+}
+
+void ScRangeFilterDescriptor::PutData( const ScQueryParam& rParam )
+{
+ if (mxParent.is())
+ mxParent->SetQueryParam( rParam );
+}
+
+ScDataPilotFilterDescriptor::ScDataPilotFilterDescriptor(ScDocShell* pDocShell, ScDataPilotDescriptorBase* pPar) :
+ ScFilterDescriptorBase(pDocShell),
+ mxParent(pPar)
+{
+}
+
+ScDataPilotFilterDescriptor::~ScDataPilotFilterDescriptor()
+{
+}
+
+void ScDataPilotFilterDescriptor::GetData( ScQueryParam& rParam ) const
+{
+ if (mxParent.is())
+ {
+ ScDPObject* pDPObj = mxParent->GetDPObject();
+ if (pDPObj && pDPObj->IsSheetData())
+ rParam = pDPObj->GetSheetDesc()->GetQueryParam();
+ }
+}
+
+void ScDataPilotFilterDescriptor::PutData( const ScQueryParam& rParam )
+{
+ if (!mxParent.is())
+ return;
+
+ ScDPObject* pDPObj = mxParent->GetDPObject();
+ if (pDPObj)
+ {
+ ScSheetSourceDesc aSheetDesc(&mxParent->GetDocShell()->GetDocument());
+ if (pDPObj->IsSheetData())
+ aSheetDesc = *pDPObj->GetSheetDesc();
+ aSheetDesc.SetQueryParam(rParam);
+ pDPObj->SetSheetDesc(aSheetDesc);
+ mxParent->SetDPObject(pDPObj);
+ }
+}
+
+ScDatabaseRangeObj::ScDatabaseRangeObj(ScDocShell* pDocSh, OUString aNm) :
+ pDocShell( pDocSh ),
+ aName(std::move( aNm )),
+ aPropSet( lcl_GetDBRangePropertyMap() ),
+ bIsUnnamed(false),
+ aTab( 0 )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDatabaseRangeObj::ScDatabaseRangeObj(ScDocShell* pDocSh, const SCTAB nTab) :
+ pDocShell( pDocSh ),
+ aName(STR_DB_LOCAL_NONAME),
+ aPropSet( lcl_GetDBRangePropertyMap() ),
+ bIsUnnamed(true),
+ aTab( nTab )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDatabaseRangeObj::~ScDatabaseRangeObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDatabaseRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr;
+ else if ( auto pRefreshHint = dynamic_cast<const ScDBRangeRefreshedHint*>(&rHint) )
+ {
+ ScDBData* pDBData = GetDBData_Impl();
+ ScImportParam aParam;
+ pDBData->GetImportParam(aParam);
+ if (aParam == pRefreshHint->GetImportParam())
+ Refreshed_Impl();
+ }
+}
+
+// Help functions
+
+ScDBData* ScDatabaseRangeObj::GetDBData_Impl() const
+{
+ ScDBData* pRet = nullptr;
+ if (pDocShell)
+ {
+ if (bIsUnnamed)
+ {
+ pRet = pDocShell->GetDocument().GetAnonymousDBData(aTab);
+ }
+ else
+ {
+ ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
+ if (pNames)
+ {
+ ScDBData* p = pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (p)
+ pRet = p;
+ }
+ }
+ }
+ return pRet;
+}
+
+// XNamed
+
+OUString SAL_CALL ScDatabaseRangeObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return aName;
+}
+
+void SAL_CALL ScDatabaseRangeObj::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDBDocFunc aFunc(*pDocShell);
+ bool bOk = aFunc.RenameDBRange( aName, aNewName );
+ if (bOk)
+ aName = aNewName;
+ }
+}
+
+// XDatabaseRange
+
+table::CellRangeAddress SAL_CALL ScDatabaseRangeObj::getDataArea()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aAddress;
+ ScDBData* pData = GetDBData_Impl();
+ if (pData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+ aAddress.Sheet = aRange.aStart.Tab();
+ aAddress.StartColumn = aRange.aStart.Col();
+ aAddress.StartRow = aRange.aStart.Row();
+ aAddress.EndColumn = aRange.aEnd.Col();
+ aAddress.EndRow = aRange.aEnd.Row();
+ }
+ return aAddress;
+}
+
+void SAL_CALL ScDatabaseRangeObj::setDataArea( const table::CellRangeAddress& aDataArea )
+{
+ SolarMutexGuard aGuard;
+ ScDBData* pData = GetDBData_Impl();
+ if ( pDocShell && pData )
+ {
+ ScDBData aNewData( *pData );
+ //! MoveTo ???
+ aNewData.SetArea( aDataArea.Sheet, static_cast<SCCOL>(aDataArea.StartColumn), static_cast<SCROW>(aDataArea.StartRow),
+ static_cast<SCCOL>(aDataArea.EndColumn), static_cast<SCROW>(aDataArea.EndRow) );
+ ScDBDocFunc aFunc(*pDocShell);
+ aFunc.ModifyDBData(aNewData);
+ }
+}
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScDatabaseRangeObj::getSortDescriptor()
+{
+ SolarMutexGuard aGuard;
+ ScSortParam aParam;
+ const ScDBData* pData = GetDBData_Impl();
+ if (pData)
+ {
+ pData->GetSortParam(aParam);
+
+ // SortDescriptor contains the counted fields inside the area
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOLROW nFieldStart = aParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row());
+ for (sal_uInt16 i=0; i<aParam.GetSortKeyCount(); i++)
+ if ( aParam.maKeyState[i].bDoSort && aParam.maKeyState[i].nField >= nFieldStart )
+ aParam.maKeyState[i].nField -= nFieldStart;
+ }
+
+ uno::Sequence<beans::PropertyValue> aSeq( ScSortDescriptor::GetPropertyCount() );
+ ScSortDescriptor::FillProperties( aSeq, aParam );
+ return aSeq;
+}
+
+void ScDatabaseRangeObj::GetQueryParam(ScQueryParam& rQueryParam) const
+{
+ const ScDBData* pData = GetDBData_Impl();
+ if (!pData)
+ return;
+
+ pData->GetQueryParam(rQueryParam);
+
+ // FilterDescriptor contains the counted fields inside the area
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOLROW nFieldStart = rQueryParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row());
+ SCSIZE nCount = rQueryParam.GetEntryCount();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ ScQueryEntry& rEntry = rQueryParam.GetEntry(i);
+ if (rEntry.bDoQuery && rEntry.nField >= nFieldStart)
+ rEntry.nField -= nFieldStart;
+ }
+}
+
+void ScDatabaseRangeObj::SetQueryParam(const ScQueryParam& rQueryParam)
+{
+ const ScDBData* pData = GetDBData_Impl();
+ if (!pData)
+ return;
+
+ // FilterDescriptor contains the counted fields inside the area
+ ScQueryParam aParam(rQueryParam);
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOLROW nFieldStart = aParam.bByRow ? static_cast<SCCOLROW>(aDBRange.aStart.Col()) : static_cast<SCCOLROW>(aDBRange.aStart.Row());
+
+ SCSIZE nCount = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nCount; i++)
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.bDoQuery)
+ rEntry.nField += nFieldStart;
+ }
+
+ ScDBData aNewData( *pData );
+ aNewData.SetQueryParam(aParam);
+ aNewData.SetHeader(aParam.bHasHeader); // not in ScDBData::SetQueryParam
+ ScDBDocFunc aFunc(*pDocShell);
+ aFunc.ModifyDBData(aNewData);
+}
+
+uno::Reference<sheet::XSheetFilterDescriptor> SAL_CALL ScDatabaseRangeObj::getFilterDescriptor()
+{
+ SolarMutexGuard aGuard;
+ return new ScRangeFilterDescriptor(pDocShell, this);
+}
+
+void ScDatabaseRangeObj::GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const
+{
+ const ScDBData* pData = GetDBData_Impl();
+ if (!pData)
+ return;
+
+ pData->GetSubTotalParam(rSubTotalParam);
+
+ // FilterDescriptor contains the counted fields inside the area
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOL nFieldStart = aDBRange.aStart.Col();
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ if ( rSubTotalParam.bGroupActive[i] )
+ {
+ if ( rSubTotalParam.nField[i] >= nFieldStart )
+ rSubTotalParam.nField[i] = sal::static_int_cast<SCCOL>( rSubTotalParam.nField[i] - nFieldStart );
+ for (SCCOL j=0; j<rSubTotalParam.nSubTotals[i]; j++)
+ if ( rSubTotalParam.pSubTotals[i][j] >= nFieldStart )
+ rSubTotalParam.pSubTotals[i][j] =
+ sal::static_int_cast<SCCOL>( rSubTotalParam.pSubTotals[i][j] - nFieldStart );
+ }
+ }
+}
+
+void ScDatabaseRangeObj::SetSubTotalParam(const ScSubTotalParam& rSubTotalParam)
+{
+ const ScDBData* pData = GetDBData_Impl();
+ if (!pData)
+ return;
+
+ // FilterDescriptor contains the counted fields inside the area
+ ScSubTotalParam aParam(rSubTotalParam);
+ ScRange aDBRange;
+ pData->GetArea(aDBRange);
+ SCCOL nFieldStart = aDBRange.aStart.Col();
+ for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++)
+ {
+ if ( aParam.bGroupActive[i] )
+ {
+ aParam.nField[i] = sal::static_int_cast<SCCOL>( aParam.nField[i] + nFieldStart );
+ for (SCCOL j=0; j<aParam.nSubTotals[i]; j++)
+ aParam.pSubTotals[i][j] = sal::static_int_cast<SCCOL>( aParam.pSubTotals[i][j] + nFieldStart );
+ }
+ }
+
+ ScDBData aNewData( *pData );
+ aNewData.SetSubTotalParam(aParam);
+ ScDBDocFunc aFunc(*pDocShell);
+ aFunc.ModifyDBData(aNewData);
+}
+
+uno::Reference<sheet::XSubTotalDescriptor> SAL_CALL ScDatabaseRangeObj::getSubTotalDescriptor()
+{
+ SolarMutexGuard aGuard;
+ return new ScRangeSubTotalDescriptor(this);
+}
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScDatabaseRangeObj::getImportDescriptor()
+{
+ SolarMutexGuard aGuard;
+ ScImportParam aParam;
+ const ScDBData* pData = GetDBData_Impl();
+ if (pData)
+ pData->GetImportParam(aParam);
+
+ uno::Sequence<beans::PropertyValue> aSeq( ScImportDescriptor::GetPropertyCount() );
+ ScImportDescriptor::FillProperties( aSeq, aParam );
+ return aSeq;
+}
+
+// XRefreshable
+
+void SAL_CALL ScDatabaseRangeObj::refresh()
+{
+ SolarMutexGuard aGuard;
+ ScDBData* pData = GetDBData_Impl();
+ if ( !(pDocShell && pData) )
+ return;
+
+ ScDBDocFunc aFunc(*pDocShell);
+
+ // repeat import?
+ bool bContinue = true;
+ ScImportParam aImportParam;
+ pData->GetImportParam( aImportParam );
+ if (aImportParam.bImport && !pData->HasImportSelection())
+ {
+ SCTAB nTab;
+ SCCOL nDummyCol;
+ SCROW nDummyRow;
+ pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
+ bContinue = aFunc.DoImport( nTab, aImportParam, nullptr ); //! Api-Flag as parameter
+ }
+
+ // if no error then internal operations (sort, query, subtotal)
+ if (bContinue)
+ aFunc.RepeatDB( pData->GetName(), true, bIsUnnamed, aTab );
+}
+
+void SAL_CALL ScDatabaseRangeObj::addRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ aRefreshListeners.emplace_back( xListener );
+
+ // hold one additional ref to keep this object alive as long as there are listeners
+ if ( aRefreshListeners.size() == 1 )
+ acquire();
+}
+
+void SAL_CALL ScDatabaseRangeObj::removeRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nCount = aRefreshListeners.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ {
+ uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
+ if ( rObj == xListener )
+ {
+ aRefreshListeners.erase( aRefreshListeners.begin() + n );
+ if ( aRefreshListeners.empty() )
+ release(); // release ref for listeners
+ break;
+ }
+ }
+}
+
+void ScDatabaseRangeObj::Refreshed_Impl()
+{
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+ for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
+ xRefreshListener->refreshed( aEvent );
+}
+
+// XCellRangeSource
+
+uno::Reference<table::XCellRange> SAL_CALL ScDatabaseRangeObj::getReferredCells()
+{
+ SolarMutexGuard aGuard;
+ ScDBData* pData = GetDBData_Impl();
+ if ( pData )
+ {
+ //! static function to create ScCellObj/ScCellRange on ScCellRangeObj ???
+ ScRange aRange;
+
+ pData->GetArea(aRange);
+ if ( aRange.aStart == aRange.aEnd )
+ return new ScCellObj( pDocShell, aRange.aStart );
+ else
+ return new ScCellRangeObj( pDocShell, aRange );
+ }
+ return nullptr;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDatabaseRangeObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScDatabaseRangeObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ ScDBData* pData = GetDBData_Impl();
+ if ( !(pDocShell && pData) )
+ return;
+
+ ScDBData aNewData( *pData );
+ bool bDo = true;
+
+ if ( aPropertyName == SC_UNONAME_KEEPFORM )
+ aNewData.SetKeepFmt( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNONAME_MOVCELLS )
+ aNewData.SetDoSize( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNONAME_STRIPDAT )
+ aNewData.SetStripData( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNONAME_AUTOFLT )
+ {
+ bool bAutoFilter(ScUnoHelpFunctions::GetBoolFromAny( aValue ));
+ aNewData.SetAutoFilter(bAutoFilter);
+ ScRange aRange;
+ aNewData.GetArea(aRange);
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (bAutoFilter)
+ rDoc.ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), ScMF::Auto );
+ else if (!bAutoFilter)
+ rDoc.RemoveFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), ScMF::Auto );
+ ScRange aPaintRange(aRange.aStart, aRange.aEnd);
+ aPaintRange.aEnd.SetRow(aPaintRange.aStart.Row());
+ pDocShell->PostPaint(aPaintRange, PaintPartFlags::Grid);
+ }
+ else if (aPropertyName == SC_UNONAME_USEFLTCRT )
+ {
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ {
+ // only here to set bIsAdvanced in ScDBData
+ ScRange aRange;
+ (void)aNewData.GetAdvancedQuerySource(aRange);
+ aNewData.SetAdvancedQuerySource(&aRange);
+ }
+ else
+ aNewData.SetAdvancedQuerySource(nullptr);
+ }
+ else if (aPropertyName == SC_UNONAME_FLTCRT )
+ {
+ table::CellRangeAddress aRange;
+ if (aValue >>= aRange)
+ {
+ ScRange aCoreRange;
+ ScUnoConversion::FillScRange(aCoreRange, aRange);
+
+ aNewData.SetAdvancedQuerySource(&aCoreRange);
+ }
+ }
+ else if (aPropertyName == SC_UNONAME_FROMSELECT )
+ {
+ aNewData.SetImportSelection(::cppu::any2bool(aValue));
+ }
+ else if (aPropertyName == SC_UNONAME_REFPERIOD )
+ {
+ sal_Int32 nRefresh = 0;
+ if (aValue >>= nRefresh)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ aNewData.SetRefreshDelay(nRefresh);
+ if (rDoc.GetDBCollection())
+ {
+ aNewData.SetRefreshHandler( rDoc.GetDBCollection()->GetRefreshHandler() );
+ aNewData.SetRefreshControl( &rDoc.GetRefreshTimerControlAddress() );
+ }
+ }
+ }
+ else if (aPropertyName == SC_UNONAME_CONRES )
+ {
+ }
+ else if ( aPropertyName == SC_UNONAME_TOTALSROW )
+ aNewData.SetTotals( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNONAME_CONTHDR )
+ aNewData.SetHeader( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else
+ bDo = false;
+
+ if (bDo)
+ {
+ ScDBDocFunc aFunc(*pDocShell);
+ aFunc.ModifyDBData(aNewData);
+ }
+}
+
+uno::Any SAL_CALL ScDatabaseRangeObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ ScDBData* pData = GetDBData_Impl();
+ if ( pData )
+ {
+ if ( aPropertyName == SC_UNONAME_KEEPFORM )
+ aRet <<= pData->IsKeepFmt();
+ else if ( aPropertyName == SC_UNONAME_MOVCELLS )
+ aRet <<= pData->IsDoSize();
+ else if ( aPropertyName == SC_UNONAME_STRIPDAT )
+ aRet <<= pData->IsStripData();
+ else if ( aPropertyName == SC_UNONAME_ISUSER )
+ {
+ // all database ranges except "unnamed" are user defined
+ aRet <<= (pData->GetName() != STR_DB_LOCAL_NONAME);
+ }
+ else if ( aPropertyName == SC_UNO_LINKDISPBIT )
+ {
+ // no target bitmaps for individual entries (would be all equal)
+ // ScLinkTargetTypeObj::SetLinkTargetBitmap( aRet, SC_LINKTARGETTYPE_DBAREA );
+ }
+ else if ( aPropertyName == SC_UNO_LINKDISPNAME )
+ aRet <<= aName;
+ else if (aPropertyName == SC_UNONAME_AUTOFLT )
+ {
+ bool bAutoFilter(GetDBData_Impl()->HasAutoFilter());
+
+ aRet <<= bAutoFilter;
+ }
+ else if (aPropertyName == SC_UNONAME_USEFLTCRT )
+ {
+ ScRange aRange;
+ bool bIsAdvancedSource(GetDBData_Impl()->GetAdvancedQuerySource(aRange));
+
+ aRet <<= bIsAdvancedSource;
+ }
+ else if (aPropertyName == SC_UNONAME_FLTCRT )
+ {
+ table::CellRangeAddress aRange;
+ ScRange aCoreRange;
+ if (GetDBData_Impl()->GetAdvancedQuerySource(aCoreRange))
+ ScUnoConversion::FillApiRange(aRange, aCoreRange);
+
+ aRet <<= aRange;
+ }
+ else if (aPropertyName == SC_UNONAME_FROMSELECT )
+ {
+ aRet <<= GetDBData_Impl()->HasImportSelection();
+ }
+ else if (aPropertyName == SC_UNONAME_REFPERIOD )
+ {
+ sal_Int32 nRefresh(GetDBData_Impl()->GetRefreshDelaySeconds());
+ aRet <<= nRefresh;
+ }
+ else if (aPropertyName == SC_UNONAME_CONRES )
+ {
+ }
+ else if (aPropertyName == SC_UNONAME_TOKENINDEX )
+ {
+ // get index for use in formula tokens (read-only)
+ aRet <<= static_cast<sal_Int32>(GetDBData_Impl()->GetIndex());
+ }
+ else if (aPropertyName == SC_UNONAME_TOTALSROW )
+ {
+ bool bTotals(GetDBData_Impl()->HasTotals());
+
+ aRet <<= bTotals;
+ }
+ else if (aPropertyName == SC_UNONAME_CONTHDR )
+ {
+ bool bHeader(GetDBData_Impl()->HasHeader());
+
+ aRet <<= bHeader;
+ }
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDatabaseRangeObj )
+
+// XServiceInfo
+OUString SAL_CALL ScDatabaseRangeObj::getImplementationName()
+{
+ return "ScDatabaseRangeObj";
+}
+
+sal_Bool SAL_CALL ScDatabaseRangeObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScDatabaseRangeObj::getSupportedServiceNames()
+{
+ return {"com.sun.star.sheet.DatabaseRange",
+ SCLINKTARGET_SERVICE};
+}
+
+ScDatabaseRangesObj::ScDatabaseRangesObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDatabaseRangesObj::~ScDatabaseRangesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDatabaseRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update does not matter here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+// XDatabaseRanges
+
+rtl::Reference<ScDatabaseRangeObj> ScDatabaseRangesObj::GetObjectByIndex_Impl(size_t nIndex)
+{
+ if (!pDocShell)
+ return nullptr;
+
+ ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
+ if (!pNames)
+ return nullptr;
+
+ const ScDBCollection::NamedDBs& rDBs = pNames->getNamedDBs();
+ if (rDBs.empty() || nIndex >= rDBs.size())
+ return nullptr;
+
+ ScDBCollection::NamedDBs::const_iterator itr = rDBs.begin();
+ ::std::advance(itr, nIndex); // boundary check is done above.
+ return new ScDatabaseRangeObj(pDocShell, (*itr)->GetName());
+}
+
+rtl::Reference<ScDatabaseRangeObj> ScDatabaseRangesObj::GetObjectByName_Impl(const OUString& aName)
+{
+ if ( pDocShell && hasByName(aName) )
+ {
+ return new ScDatabaseRangeObj( pDocShell, aName );
+ }
+ return nullptr;
+}
+
+void SAL_CALL ScDatabaseRangesObj::addNewByName( const OUString& aName,
+ const table::CellRangeAddress& aRange )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ ScDBDocFunc aFunc(*pDocShell);
+
+ ScRange aNameRange( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet,
+ static_cast<SCCOL>(aRange.EndColumn), static_cast<SCROW>(aRange.EndRow), aRange.Sheet );
+ bDone = aFunc.AddDBRange( aName, aNameRange );
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScDatabaseRangesObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ ScDBDocFunc aFunc(*pDocShell);
+ bDone = aFunc.DeleteDBRange( aName );
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScDatabaseRangesObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.DatabaseRangesEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDatabaseRangesObj::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ //! need to omit "unnamed"?
+
+ if (pDocShell)
+ {
+ ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
+ if (pNames)
+ return static_cast<sal_Int32>(pNames->getNamedDBs().size());
+ }
+ return 0;
+}
+
+uno::Any SAL_CALL ScDatabaseRangesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ if (nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference<sheet::XDatabaseRange> xRange(GetObjectByIndex_Impl(static_cast<size_t>(nIndex)));
+ if (!xRange.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xRange);
+}
+
+uno::Type SAL_CALL ScDatabaseRangesObj::getElementType()
+{
+ return cppu::UnoType<sheet::XDatabaseRange>::get();
+}
+
+sal_Bool SAL_CALL ScDatabaseRangesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL ScDatabaseRangesObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XDatabaseRange> xRange(GetObjectByName_Impl(aName));
+ if (!xRange.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xRange);
+}
+
+uno::Sequence<OUString> SAL_CALL ScDatabaseRangesObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ //! need to omit "unnamed"?
+
+ if (pDocShell)
+ {
+ ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
+ if (pNames)
+ {
+ const ScDBCollection::NamedDBs& rDBs = pNames->getNamedDBs();
+ uno::Sequence<OUString> aSeq(rDBs.size());
+ auto aSeqRange = asNonConstRange(aSeq);
+ size_t i = 0;
+ for (const auto& rDB : rDBs)
+ {
+ aSeqRange[i] = rDB->GetName();
+ ++i;
+ }
+
+ return aSeq;
+ }
+ }
+ return {};
+}
+
+sal_Bool SAL_CALL ScDatabaseRangesObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ //! need to omit "unnamed"?
+
+ if (pDocShell)
+ {
+ ScDBCollection* pNames = pDocShell->GetDocument().GetDBCollection();
+ if (pNames)
+ return pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aName)) != nullptr;
+ }
+ return false;
+}
+
+ScUnnamedDatabaseRangesObj::ScUnnamedDatabaseRangesObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScUnnamedDatabaseRangesObj::~ScUnnamedDatabaseRangesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScUnnamedDatabaseRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update does not matter here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+// XUnnamedDatabaseRanges
+
+void ScUnnamedDatabaseRangesObj::setByTable( const table::CellRangeAddress& aRange )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ if ( pDocShell->GetDocument().GetTableCount() <= aRange.Sheet )
+ throw lang::IndexOutOfBoundsException();
+
+ ScDBDocFunc aFunc(*pDocShell);
+ ScRange aUnnamedRange( static_cast<SCCOL>(aRange.StartColumn), static_cast<SCROW>(aRange.StartRow), aRange.Sheet,
+ static_cast<SCCOL>(aRange.EndColumn), static_cast<SCROW>(aRange.EndRow), aRange.Sheet );
+ bDone = aFunc.AddDBRange( STR_DB_LOCAL_NONAME, aUnnamedRange );
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+uno::Any ScUnnamedDatabaseRangesObj::getByTable( sal_Int32 nTab )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ throw uno::RuntimeException();
+
+ if ( pDocShell->GetDocument().GetTableCount() <= nTab )
+ throw lang::IndexOutOfBoundsException();
+ uno::Reference<sheet::XDatabaseRange> xRange(
+ new ScDatabaseRangeObj(pDocShell, static_cast<SCTAB>(nTab)));
+ if (!xRange.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xRange);
+}
+
+sal_Bool ScUnnamedDatabaseRangesObj::hasByTable( sal_Int32 nTab )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ if (pDocShell->GetDocument().GetTableCount() <= nTab)
+ throw lang::IndexOutOfBoundsException();
+ if (pDocShell->GetDocument().GetAnonymousDBData(static_cast<SCTAB>(nTab)))
+ return true;
+ return false;
+ }
+ else
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/defltuno.cxx b/sc/source/ui/unoobj/defltuno.cxx
new file mode 100644
index 0000000000..516fc8c6c7
--- /dev/null
+++ b/sc/source/ui/unoobj/defltuno.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/memberids.h>
+#include <svl/hint.hxx>
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <scitems.hxx>
+#include <defltuno.hxx>
+#include <miscuno.hxx>
+#include <docsh.hxx>
+#include <docpool.hxx>
+#include <unonames.hxx>
+#include <docoptio.hxx>
+
+#include <limits>
+
+class SvxFontItem;
+using namespace ::com::sun::star;
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetDocDefaultsMap()
+{
+ static const SfxItemPropertyMapEntry aDocDefaultsMap_Impl[] =
+ {
+ { SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ { SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ { SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ { SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ { SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ { SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE, cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ { SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT, cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ { SC_UNO_STANDARDDEC, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNO_TABSTOPDIS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ return aDocDefaultsMap_Impl;
+}
+
+using sc::TwipsToEvenHMM;
+
+SC_SIMPLE_SERVICE_INFO( ScDocDefaultsObj, "ScDocDefaultsObj", "com.sun.star.sheet.Defaults" )
+
+ScDocDefaultsObj::ScDocDefaultsObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh ),
+ aPropertyMap(lcl_GetDocDefaultsMap())
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDocDefaultsObj::~ScDocDefaultsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDocDefaultsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // document gone
+ }
+}
+
+void ScDocDefaultsObj::ItemsChanged()
+{
+ if (pDocShell)
+ {
+ //! if not in XML import, adjust row heights
+ const auto & rDoc = pDocShell->GetDocument();
+ pDocShell->PostPaint(ScRange(0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB), PaintPartFlags::Grid);
+ }
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDocDefaultsObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef = new SfxItemPropertySetInfo(
+ aPropertyMap );
+ return aRef;
+}
+
+void SAL_CALL ScDocDefaultsObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+ if(!pEntry->nWID)
+ {
+ if(aPropertyName ==SC_UNO_STANDARDDEC)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDocOptions aDocOpt(rDoc.GetDocOptions());
+ sal_Int16 nValue = 0;
+ if (aValue >>= nValue)
+ {
+ aDocOpt.SetStdPrecision(static_cast<sal_uInt16> (nValue));
+ rDoc.SetDocOptions(aDocOpt);
+ }
+ }
+ else if (aPropertyName == SC_UNO_TABSTOPDIS)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDocOptions aDocOpt(rDoc.GetDocOptions());
+ sal_Int32 nValue = 0;
+ if (aValue >>= nValue)
+ {
+ aDocOpt.SetTabDistance(o3tl::toTwips(nValue, o3tl::Length::mm100));
+ rDoc.SetDocOptions(aDocOpt);
+ }
+ }
+ }
+ else if ( pEntry->nWID == ATTR_FONT_LANGUAGE ||
+ pEntry->nWID == ATTR_CJK_FONT_LANGUAGE ||
+ pEntry->nWID == ATTR_CTL_FONT_LANGUAGE )
+ {
+ // for getPropertyValue the PoolDefaults are sufficient,
+ // but setPropertyValue has to be handled differently
+
+ lang::Locale aLocale;
+ if ( aValue >>= aLocale )
+ {
+ LanguageType eNew;
+ if (!aLocale.Language.isEmpty() || !aLocale.Country.isEmpty())
+ eNew = LanguageTag::convertToLanguageType( aLocale, false);
+ else
+ eNew = LANGUAGE_NONE;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+
+ if ( pEntry->nWID == ATTR_CJK_FONT_LANGUAGE )
+ eCjk = eNew;
+ else if ( pEntry->nWID == ATTR_CTL_FONT_LANGUAGE )
+ eCtl = eNew;
+ else
+ eLatin = eNew;
+
+ rDoc.SetLanguage( eLatin, eCjk, eCtl );
+ }
+ }
+ else
+ {
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ std::unique_ptr<SfxPoolItem> pNewItem(pPool->GetDefaultItem(pEntry->nWID).Clone());
+
+ if( !pNewItem->PutValue( aValue, pEntry->nMemberId ) )
+ throw lang::IllegalArgumentException();
+
+ pPool->SetPoolDefaultItem( *pNewItem );
+
+ ItemsChanged();
+ }
+}
+
+uno::Any SAL_CALL ScDocDefaultsObj::getPropertyValue( const OUString& aPropertyName )
+{
+ // use pool default if set
+
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ uno::Any aRet;
+ const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ if (!pEntry->nWID)
+ {
+ if(aPropertyName == SC_UNO_STANDARDDEC)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const ScDocOptions& aDocOpt = rDoc.GetDocOptions();
+ sal_uInt16 nPrec = aDocOpt.GetStdPrecision();
+ // the max value of unsigned 16-bit integer is used as the flag
+ // value for unlimited precision, c.f.
+ // SvNumberFormatter::UNLIMITED_PRECISION.
+ if (nPrec <= ::std::numeric_limits<sal_Int16>::max())
+ aRet <<= static_cast<sal_Int16> (nPrec);
+ }
+ else if (aPropertyName == SC_UNO_TABSTOPDIS)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const ScDocOptions& aDocOpt = rDoc.GetDocOptions();
+ sal_Int32 nValue (TwipsToEvenHMM(aDocOpt.GetTabDistance()));
+ aRet <<= nValue;
+ }
+ }
+ else
+ {
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ const SfxPoolItem& rItem = pPool->GetDefaultItem( pEntry->nWID );
+ rItem.QueryValue( aRet, pEntry->nMemberId );
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDocDefaultsObj )
+
+// XPropertyState
+
+beans::PropertyState SAL_CALL ScDocDefaultsObj::getPropertyState( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ beans::PropertyState eRet = beans::PropertyState_DEFAULT_VALUE;
+
+ sal_uInt16 nWID = pEntry->nWID;
+ if ( nWID == ATTR_FONT || nWID == ATTR_CJK_FONT || nWID == ATTR_CTL_FONT || !nWID )
+ {
+ // static default for font is system-dependent,
+ // so font default is always treated as "direct value".
+
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ // check if pool default is set
+
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ if ( pPool->GetPoolDefaultItem( nWID ) != nullptr )
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+
+ return eRet;
+}
+
+uno::Sequence<beans::PropertyState> SAL_CALL ScDocDefaultsObj::getPropertyStates(
+ const uno::Sequence<OUString>& aPropertyNames )
+{
+ // the simple way: call getPropertyState
+
+ SolarMutexGuard aGuard;
+ uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength());
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(),
+ [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); });
+ return aRet;
+}
+
+void SAL_CALL ScDocDefaultsObj::setPropertyToDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ if (pEntry->nWID)
+ {
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ pPool->ResetPoolDefaultItem( pEntry->nWID );
+
+ ItemsChanged();
+ }
+}
+
+uno::Any SAL_CALL ScDocDefaultsObj::getPropertyDefault( const OUString& aPropertyName )
+{
+ // always use static default
+
+ SolarMutexGuard aGuard;
+
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = aPropertyMap.getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ uno::Any aRet;
+ if (pEntry->nWID)
+ {
+ ScDocumentPool* pPool = pDocShell->GetDocument().GetPool();
+ const SfxPoolItem* pItem = pPool->GetItem2Default( pEntry->nWID );
+ if (pItem)
+ pItem->QueryValue( aRet, pEntry->nMemberId );
+ }
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/dispuno.cxx b/sc/source/ui/unoobj/dispuno.cxx
new file mode 100644
index 0000000000..536d271d64
--- /dev/null
+++ b/sc/source/ui/unoobj/dispuno.cxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/viewfrm.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svl/hint.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+
+#include <dispuno.hxx>
+#include <tabvwsh.hxx>
+#include <dbdocfun.hxx>
+#include <dbdata.hxx>
+
+using namespace com::sun::star;
+
+const char cURLInsertColumns[] = ".uno:DataSourceBrowser/InsertColumns"; //data into text
+constexpr OUString cURLDocDataSource = u".uno:DataSourceBrowser/DocumentDataSource"_ustr;
+
+static uno::Reference<view::XSelectionSupplier> lcl_GetSelectionSupplier( const SfxViewShell* pViewShell )
+{
+ if ( pViewShell )
+ {
+ SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+ return uno::Reference<view::XSelectionSupplier>( rViewFrame.GetFrame().GetController(), uno::UNO_QUERY );
+ }
+ return uno::Reference<view::XSelectionSupplier>();
+}
+
+ScDispatchProviderInterceptor::ScDispatchProviderInterceptor(ScTabViewShell* pViewSh) :
+ pViewShell( pViewSh )
+{
+ if ( !pViewShell )
+ return;
+
+ m_xIntercepted.set(uno::Reference<frame::XDispatchProviderInterception>(pViewShell->GetViewFrame().GetFrame().GetFrameInterface(), uno::UNO_QUERY));
+ if (m_xIntercepted.is())
+ {
+ osl_atomic_increment( &m_refCount );
+
+ m_xIntercepted->registerDispatchProviderInterceptor(
+ static_cast<frame::XDispatchProviderInterceptor*>(this));
+ // this should make us the top-level dispatch-provider for the component, via a call to our
+ // setDispatchProvider we should have got a fallback for requests we (i.e. our master) cannot fulfill
+ uno::Reference<lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
+ if (xInterceptedComponent.is())
+ xInterceptedComponent->addEventListener(static_cast<lang::XEventListener*>(this));
+
+ osl_atomic_decrement( &m_refCount );
+ }
+
+ StartListening(*pViewShell);
+}
+
+ScDispatchProviderInterceptor::~ScDispatchProviderInterceptor()
+{
+ if (pViewShell)
+ EndListening(*pViewShell);
+}
+
+void ScDispatchProviderInterceptor::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pViewShell = nullptr;
+}
+
+// XDispatchProvider
+
+uno::Reference<frame::XDispatch> SAL_CALL ScDispatchProviderInterceptor::queryDispatch(
+ const util::URL& aURL, const OUString& aTargetFrameName,
+ sal_Int32 nSearchFlags )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<frame::XDispatch> xResult;
+ // create some dispatch ...
+ if ( pViewShell && (
+ aURL.Complete == cURLInsertColumns ||
+ aURL.Complete == cURLDocDataSource ) )
+ {
+ if (!m_xMyDispatch.is())
+ m_xMyDispatch = new ScDispatch( pViewShell );
+ xResult = m_xMyDispatch;
+ }
+
+ // ask our slave provider
+ if (!xResult.is() && m_xSlaveDispatcher.is())
+ xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+
+ return xResult;
+}
+
+uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL
+ ScDispatchProviderInterceptor::queryDispatches(
+ const uno::Sequence<frame::DispatchDescriptor>& aDescripts )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< uno::Reference< frame::XDispatch> > aReturn(aDescripts.getLength());
+ std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(),
+ [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> {
+ return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
+ return aReturn;
+}
+
+// XDispatchProviderInterceptor
+
+uno::Reference<frame::XDispatchProvider> SAL_CALL
+ ScDispatchProviderInterceptor::getSlaveDispatchProvider()
+{
+ SolarMutexGuard aGuard;
+ return m_xSlaveDispatcher;
+}
+
+void SAL_CALL ScDispatchProviderInterceptor::setSlaveDispatchProvider(
+ const uno::Reference<frame::XDispatchProvider>& xNewDispatchProvider )
+{
+ SolarMutexGuard aGuard;
+ m_xSlaveDispatcher.set(xNewDispatchProvider);
+}
+
+uno::Reference<frame::XDispatchProvider> SAL_CALL
+ ScDispatchProviderInterceptor::getMasterDispatchProvider()
+{
+ SolarMutexGuard aGuard;
+ return m_xMasterDispatcher;
+}
+
+void SAL_CALL ScDispatchProviderInterceptor::setMasterDispatchProvider(
+ const uno::Reference<frame::XDispatchProvider>& xNewSupplier )
+{
+ SolarMutexGuard aGuard;
+ m_xMasterDispatcher.set(xNewSupplier);
+}
+
+// XEventListener
+
+void SAL_CALL ScDispatchProviderInterceptor::disposing( const lang::EventObject& /* Source */ )
+{
+ SolarMutexGuard aGuard;
+
+ if (m_xIntercepted.is())
+ {
+ m_xIntercepted->releaseDispatchProviderInterceptor(
+ static_cast<frame::XDispatchProviderInterceptor*>(this));
+ uno::Reference<lang::XComponent> xInterceptedComponent(m_xIntercepted, uno::UNO_QUERY);
+ if (xInterceptedComponent.is())
+ xInterceptedComponent->removeEventListener(static_cast<lang::XEventListener*>(this));
+
+ m_xMyDispatch = nullptr;
+ }
+ m_xIntercepted = nullptr;
+}
+
+ScDispatch::ScDispatch(ScTabViewShell* pViewSh) :
+ pViewShell( pViewSh ),
+ bListeningToView( false )
+{
+ if (pViewShell)
+ StartListening(*pViewShell);
+}
+
+ScDispatch::~ScDispatch()
+{
+ if (pViewShell)
+ EndListening(*pViewShell);
+
+ if (bListeningToView && pViewShell)
+ {
+ uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell ));
+ if ( xSupplier.is() )
+ xSupplier->removeSelectionChangeListener(this);
+ }
+}
+
+void ScDispatch::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pViewShell = nullptr;
+}
+
+// XDispatch
+
+void SAL_CALL ScDispatch::dispatch( const util::URL& aURL,
+ const uno::Sequence<beans::PropertyValue>& aArgs )
+{
+ SolarMutexGuard aGuard;
+
+ bool bDone = false;
+ if ( pViewShell && aURL.Complete == cURLInsertColumns )
+ {
+ ScViewData& rViewData = pViewShell->GetViewData();
+ ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+
+ ScDBDocFunc aFunc( *rViewData.GetDocShell() );
+ aFunc.DoImportUno( aPos, aArgs );
+ bDone = true;
+ }
+ // cURLDocDataSource is never dispatched
+
+ if (!bDone)
+ throw uno::RuntimeException();
+}
+
+static void lcl_FillDataSource( frame::FeatureStateEvent& rEvent, const ScImportParam& rParam )
+{
+ rEvent.IsEnabled = rParam.bImport;
+
+ svx::ODataAccessDescriptor aDescriptor;
+ if ( rParam.bImport )
+ {
+ sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND :
+ ( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY :
+ sdb::CommandType::TABLE );
+
+ aDescriptor.setDataSource(rParam.aDBName);
+ aDescriptor[svx::DataAccessDescriptorProperty::Command] <<= rParam.aStatement;
+ aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= nType;
+ }
+ else
+ {
+ // descriptor has to be complete anyway
+
+ aDescriptor[svx::DataAccessDescriptorProperty::DataSource] <<= OUString();
+ aDescriptor[svx::DataAccessDescriptorProperty::Command] <<= OUString();
+ aDescriptor[svx::DataAccessDescriptorProperty::CommandType] <<= sal_Int32(sdb::CommandType::TABLE);
+ }
+ rEvent.State <<= aDescriptor.createPropertyValueSequence();
+}
+
+void SAL_CALL ScDispatch::addStatusListener(
+ const uno::Reference<frame::XStatusListener>& xListener,
+ const util::URL& aURL)
+{
+ SolarMutexGuard aGuard;
+
+ if (!pViewShell)
+ throw uno::RuntimeException();
+
+ // initial state
+ frame::FeatureStateEvent aEvent;
+ aEvent.IsEnabled = true;
+ aEvent.Source = getXWeak();
+ aEvent.FeatureURL = aURL;
+
+ if ( aURL.Complete == cURLDocDataSource )
+ {
+ aDataSourceListeners.emplace_back( xListener );
+
+ if (!bListeningToView)
+ {
+ uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell ));
+ if ( xSupplier.is() )
+ xSupplier->addSelectionChangeListener(this);
+ bListeningToView = true;
+ }
+
+ ScDBData* pDBData = pViewShell->GetDBData(false,SC_DB_OLD);
+ if ( pDBData )
+ pDBData->GetImportParam( aLastImport );
+ lcl_FillDataSource( aEvent, aLastImport ); // modifies State, IsEnabled
+ }
+ //! else add to listener for "enabled" changes?
+
+ xListener->statusChanged( aEvent );
+}
+
+void SAL_CALL ScDispatch::removeStatusListener(
+ const uno::Reference<frame::XStatusListener>& xListener,
+ const util::URL& aURL )
+{
+ SolarMutexGuard aGuard;
+
+ if ( aURL.Complete != cURLDocDataSource )
+ return;
+
+ sal_uInt16 nCount = aDataSourceListeners.size();
+ for ( sal_uInt16 n=nCount; n--; )
+ {
+ uno::Reference<frame::XStatusListener>& rObj = aDataSourceListeners[n];
+ if ( rObj == xListener )
+ {
+ aDataSourceListeners.erase( aDataSourceListeners.begin() + n );
+ break;
+ }
+ }
+
+ if ( aDataSourceListeners.empty() && pViewShell )
+ {
+ uno::Reference<view::XSelectionSupplier> xSupplier(lcl_GetSelectionSupplier( pViewShell ));
+ if ( xSupplier.is() )
+ xSupplier->removeSelectionChangeListener(this);
+ bListeningToView = false;
+ }
+}
+
+// XSelectionChangeListener
+
+void SAL_CALL ScDispatch::selectionChanged( const css::lang::EventObject& /* aEvent */ )
+{
+ // currently only called for URL cURLDocDataSource
+
+ if ( !pViewShell )
+ return;
+
+ ScImportParam aNewImport;
+ ScDBData* pDBData = pViewShell->GetDBData(false,SC_DB_OLD);
+ if ( pDBData )
+ pDBData->GetImportParam( aNewImport );
+
+ // notify listeners only if data source has changed
+ if ( !(aNewImport.bImport != aLastImport.bImport ||
+ aNewImport.aDBName != aLastImport.aDBName ||
+ aNewImport.aStatement != aLastImport.aStatement ||
+ aNewImport.bSql != aLastImport.bSql ||
+ aNewImport.nType != aLastImport.nType) )
+ return;
+
+ frame::FeatureStateEvent aEvent;
+ aEvent.Source = getXWeak();
+ aEvent.FeatureURL.Complete = cURLDocDataSource;
+
+ lcl_FillDataSource( aEvent, aNewImport ); // modifies State, IsEnabled
+
+ for (uno::Reference<frame::XStatusListener> & xDataSourceListener : aDataSourceListeners)
+ xDataSourceListener->statusChanged( aEvent );
+
+ aLastImport = aNewImport;
+}
+
+// XEventListener
+
+void SAL_CALL ScDispatch::disposing( const css::lang::EventObject& rSource )
+{
+ uno::Reference<view::XSelectionSupplier> xSupplier(rSource.Source, uno::UNO_QUERY);
+ xSupplier->removeSelectionChangeListener(this);
+ bListeningToView = false;
+
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+ for (uno::Reference<frame::XStatusListener> & xDataSourceListener : aDataSourceListeners)
+ xDataSourceListener->disposing( aEvent );
+
+ pViewShell = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
new file mode 100644
index 0000000000..c906f39336
--- /dev/null
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -0,0 +1,4984 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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_opencl.h>
+
+#include <scitems.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/memberids.h>
+#include <editeng/outliner.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/fmview.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svxids.hrc>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/propertysequence.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include <svl/numuno.hxx>
+#include <svl/hint.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/unopage.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <vcl/print.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/json_writer.hxx>
+#include <tools/multisel.hxx>
+#include <tools/UnitConversion.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+
+#include <float.h>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/XTheme.hpp>
+#include <com/sun/star/sheet/XNamedRanges.hpp>
+#include <com/sun/star/sheet/XLabelRanges.hpp>
+#include <com/sun/star/sheet/XSelectedSheetsSupplier.hpp>
+#include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp>
+#include <com/sun/star/i18n/XForbiddenCharacters.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+#include <com/sun/star/script/XInvocation.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#if HAVE_FEATURE_OPENCL
+#include <opencl/platforminfo.hxx>
+#endif
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <sfx2/LokControlHandler.hxx>
+#include <docmodel/uno/UnoTheme.hxx>
+#include <docmodel/theme/Theme.hxx>
+
+#include <cellsuno.hxx>
+#include <columnspanset.hxx>
+#include <convuno.hxx>
+#include <datauno.hxx>
+#include <docfunc.hxx>
+#include <docoptio.hxx>
+#include <docsh.hxx>
+#include <docuno.hxx>
+#include <drwlayer.hxx>
+#include <forbiuno.hxx>
+#include <formulagroup.hxx>
+#include <gridwin.hxx>
+#include <hints.hxx>
+#include <inputhdl.hxx>
+#include <inputopt.hxx>
+#include <interpre.hxx>
+#include <linkuno.hxx>
+#include <markdata.hxx>
+#include <miscuno.hxx>
+#include <nameuno.hxx>
+#include <notesuno.hxx>
+#include <optuno.hxx>
+#include <pfuncache.hxx>
+#include <postit.hxx>
+#include <printfun.hxx>
+#include <rangeutl.hxx>
+#include <scmod.hxx>
+#include <scresid.hxx>
+#include <servuno.hxx>
+#include <shapeuno.hxx>
+#include <sheetevents.hxx>
+#include <styleuno.hxx>
+#include <tabvwsh.hxx>
+#include <targuno.hxx>
+#include <unonames.hxx>
+#include <ViewSettingsSequenceDefines.hxx>
+#include <editsh.hxx>
+#include <drawsh.hxx>
+#include <drtxtob.hxx>
+#include <transobj.hxx>
+#include <chgtrack.hxx>
+#include <table.hxx>
+#include <appoptio.hxx>
+#include <formulaopt.hxx>
+
+#include <strings.hrc>
+
+using namespace com::sun::star;
+
+// #i111553# provides the name of the VBA constant for this document type (e.g. 'ThisExcelDoc' for Calc)
+constexpr OUString SC_UNO_VBAGLOBNAME = u"VBAGlobalConstantName"_ustr;
+
+// no Which-ID here, map only for PropertySetInfo
+
+//! rename this, those are no longer only options
+static std::span<const SfxItemPropertyMapEntry> lcl_GetDocOptPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aDocOptPropertyMap_Impl[] =
+ {
+ { SC_UNO_APPLYFMDES, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_AREALINKS, 0, cppu::UnoType<sheet::XAreaLinks>::get(), 0, 0},
+ { SC_UNO_AUTOCONTFOC, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_BASICLIBRARIES, 0, cppu::UnoType<script::XLibraryContainer>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_DIALOGLIBRARIES, 0, cppu::UnoType<script::XLibraryContainer>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_VBAGLOBNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_CALCASSHOWN, PROP_UNO_CALCASSHOWN, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_CLOCAL, 0, cppu::UnoType<lang::Locale>::get(), 0, 0},
+ { SC_UNO_CJK_CLOCAL, 0, cppu::UnoType<lang::Locale>::get(), 0, 0},
+ { SC_UNO_CTL_CLOCAL, 0, cppu::UnoType<lang::Locale>::get(), 0, 0},
+ { SC_UNO_COLLABELRNG, 0, cppu::UnoType<sheet::XLabelRanges>::get(), 0, 0},
+ { SC_UNO_DDELINKS, 0, cppu::UnoType<container::XNameAccess>::get(), 0, 0},
+ { SC_UNO_DEFTABSTOP, PROP_UNO_DEFTABSTOP, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_EXTERNALDOCLINKS, 0, cppu::UnoType<sheet::XExternalDocLinks>::get(), 0, 0},
+ { SC_UNO_FORBIDDEN, 0, cppu::UnoType<i18n::XForbiddenCharacters>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_HASDRAWPAGES, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_IGNORECASE, PROP_UNO_IGNORECASE, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ITERENABLED, PROP_UNO_ITERENABLED, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ITERCOUNT, PROP_UNO_ITERCOUNT, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_ITEREPSILON, PROP_UNO_ITEREPSILON, cppu::UnoType<double>::get(), 0, 0},
+ { SC_UNO_LOOKUPLABELS, PROP_UNO_LOOKUPLABELS, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_MATCHWHOLE, PROP_UNO_MATCHWHOLE, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_NAMEDRANGES, 0, cppu::UnoType<sheet::XNamedRanges>::get(), 0, 0},
+ { SC_UNO_THEME, 0, cppu::UnoType<util::XTheme>::get(), 0, 0},
+ { SC_UNO_DATABASERNG, 0, cppu::UnoType<sheet::XDatabaseRanges>::get(), 0, 0},
+ { SC_UNO_NULLDATE, PROP_UNO_NULLDATE, cppu::UnoType<util::Date>::get(), 0, 0},
+ { SC_UNO_ROWLABELRNG, 0, cppu::UnoType<sheet::XLabelRanges>::get(), 0, 0},
+ { SC_UNO_SHEETLINKS, 0, cppu::UnoType<container::XNameAccess>::get(), 0, 0},
+ { SC_UNO_SPELLONLINE, PROP_UNO_SPELLONLINE, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_STANDARDDEC, PROP_UNO_STANDARDDEC, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_REGEXENABLED, PROP_UNO_REGEXENABLED, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_WILDCARDSENABLED, PROP_UNO_WILDCARDSENABLED, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_RUNTIMEUID, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_HASVALIDSIGNATURES, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_ISLOADED, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ISUNDOENABLED, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_RECORDCHANGES, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ISRECORDCHANGESPROTECTED,0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::READONLY, 0},
+ { SC_UNO_ISADJUSTHEIGHTENABLED, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ISEXECUTELINKENABLED, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ISCHANGEREADONLYENABLED, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_REFERENCEDEVICE, 0, cppu::UnoType<awt::XDevice>::get(), beans::PropertyAttribute::READONLY, 0},
+ {u"BuildId"_ustr, 0, ::cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNO_CODENAME, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNO_INTEROPGRABBAG, 0, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), 0, 0},
+ };
+ return aDocOptPropertyMap_Impl;
+}
+
+//! StandardDecimals as property and from NumberFormatter ????????
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetColumnsPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aColumnsPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_MANPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NEWPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLVIS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_OWIDTH, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLWID, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ return aColumnsPropertyMap_Impl;
+}
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetRowsPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aRowsPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_CELLHGT, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_CELLFILT, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_OHEIGHT, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_MANPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NEWPAGE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLVIS, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_CELLBACK, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ { SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ // not sorted, not used with SfxItemPropertyMapEntry::GetByName
+ };
+ return aRowsPropertyMap_Impl;
+}
+
+constexpr OUString SCMODELOBJ_SERVICE = u"com.sun.star.sheet.SpreadsheetDocument"_ustr;
+constexpr OUString SCDOCSETTINGS_SERVICE = u"com.sun.star.sheet.SpreadsheetDocumentSettings"_ustr;
+constexpr OUString SCDOC_SERVICE = u"com.sun.star.document.OfficeDocument"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScAnnotationsObj, "ScAnnotationsObj", "com.sun.star.sheet.CellAnnotations" )
+SC_SIMPLE_SERVICE_INFO( ScDrawPagesObj, "ScDrawPagesObj", "com.sun.star.drawing.DrawPages" )
+SC_SIMPLE_SERVICE_INFO( ScScenariosObj, "ScScenariosObj", "com.sun.star.sheet.Scenarios" )
+SC_SIMPLE_SERVICE_INFO( ScSpreadsheetSettingsObj, "ScSpreadsheetSettingsObj", "com.sun.star.sheet.SpreadsheetDocumentSettings" )
+SC_SIMPLE_SERVICE_INFO( ScTableColumnsObj, "ScTableColumnsObj", "com.sun.star.table.TableColumns" )
+SC_SIMPLE_SERVICE_INFO( ScTableRowsObj, "ScTableRowsObj", "com.sun.star.table.TableRows" )
+SC_SIMPLE_SERVICE_INFO( ScTableSheetsObj, "ScTableSheetsObj", "com.sun.star.sheet.Spreadsheets" )
+
+class ScPrintUIOptions : public vcl::PrinterOptionsHelper
+{
+public:
+ ScPrintUIOptions();
+ void SetDefaults();
+};
+
+ScPrintUIOptions::ScPrintUIOptions()
+{
+ const ScPrintOptions& rPrintOpt = SC_MOD()->GetPrintOptions();
+ sal_Int32 nContent = rPrintOpt.GetAllSheets() ? 0 : 1;
+ bool bSuppress = rPrintOpt.GetSkipEmpty();
+
+ sal_Int32 nNumProps= 10, nIdx = 0;
+
+ m_aUIProperties.resize(nNumProps);
+
+ // load the writer PrinterOptions into the custom tab
+ m_aUIProperties[nIdx].Name = "OptionsUIFile";
+ m_aUIProperties[nIdx++].Value <<= OUString("modules/scalc/ui/printeroptions.ui");
+
+ // create Section for spreadsheet (results in an extra tab page in dialog)
+ SvtModuleOptions aOpt;
+ OUString aAppGroupname( ScResId( SCSTR_PRINTOPT_PRODNAME ) );
+ aAppGroupname = aAppGroupname.replaceFirst( "%s", aOpt.GetModuleName( SvtModuleOptions::EModule::CALC ) );
+ m_aUIProperties[nIdx++].Value = setGroupControlOpt("tabcontrol-page2", aAppGroupname, OUString());
+
+ // show subgroup for pages
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("pages", ScResId( SCSTR_PRINTOPT_PAGES ), OUString());
+
+ // create a bool option for empty pages
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("suppressemptypages", ScResId( SCSTR_PRINTOPT_SUPPRESSEMPTY ),
+ ".HelpID:vcl:PrintDialog:IsSuppressEmptyPages:CheckBox",
+ "IsSuppressEmptyPages",
+ bSuppress);
+ // show Subgroup for print content
+ vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt;
+ aPrintRangeOpt.maGroupHint = "PrintRange";
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("printrange", ScResId( SCSTR_PRINTOPT_PAGES ),
+ OUString(),
+ aPrintRangeOpt);
+
+ // create a choice for the content to create
+ uno::Sequence< OUString > aChoices{
+ ScResId( SCSTR_PRINTOPT_ALLSHEETS ),
+ ScResId( SCSTR_PRINTOPT_SELECTEDSHEETS ),
+ ScResId( SCSTR_PRINTOPT_SELECTEDCELLS )};
+ uno::Sequence< OUString > aHelpIds{
+ ".HelpID:vcl:PrintDialog:PrintContent:ListBox"};
+ m_aUIProperties[nIdx++].Value = setChoiceListControlOpt( "printextrabox", OUString(),
+ aHelpIds, "PrintContent",
+ aChoices, nContent );
+
+ // show Subgroup for print range
+ aPrintRangeOpt.mbInternalOnly = true;
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("fromwhich", ScResId( SCSTR_PRINTOPT_FROMWHICH ),
+ OUString(),
+ aPrintRangeOpt);
+
+ // create a choice for the range to print
+ OUString aPrintRangeName( "PrintRange" );
+ aChoices = { ScResId( SCSTR_PRINTOPT_PRINTALLPAGES ), ScResId( SCSTR_PRINTOPT_PRINTPAGES ) };
+ aHelpIds = { ".HelpID:vcl:PrintDialog:PrintRange:RadioButton:0",
+ ".HelpID:vcl:PrintDialog:PrintRange:RadioButton:1" };
+ uno::Sequence< OUString > aWidgetIds{ "rbAllPages", "rbRangePages" };
+ m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(),
+ aHelpIds,
+ aPrintRangeName,
+ aChoices,
+ 0 );
+
+ // create an Edit dependent on "Pages" selected
+ vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt( aPrintRangeName, 1, true );
+ m_aUIProperties[nIdx++].Value = setEditControlOpt("pagerange", OUString(),
+ ".HelpID:vcl:PrintDialog:PageRange:Edit",
+ "PageRange", OUString(), aPageRangeOpt);
+
+ vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintRangeName, 0, true);
+ m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("evenoddbox",
+ OUString(),
+ uno::Sequence<OUString>(),
+ "EvenOdd",
+ uno::Sequence<OUString>(),
+ 0,
+ uno::Sequence< sal_Bool >(),
+ aEvenOddOpt);
+
+ assert(nIdx == nNumProps);
+}
+
+void ScPrintUIOptions::SetDefaults()
+{
+ // re-initialize the default values from print options
+
+ const ScPrintOptions& rPrintOpt = SC_MOD()->GetPrintOptions();
+ sal_Int32 nContent = rPrintOpt.GetAllSheets() ? 0 : 1;
+ bool bSuppress = rPrintOpt.GetSkipEmpty();
+
+ for (beans::PropertyValue & rPropValue : m_aUIProperties)
+ {
+ uno::Sequence<beans::PropertyValue> aUIProp;
+ if ( rPropValue.Value >>= aUIProp )
+ {
+ for (auto& rProp : asNonConstRange(aUIProp))
+ {
+ OUString aName = rProp.Name;
+ if ( aName == "Property" )
+ {
+ beans::PropertyValue aPropertyValue;
+ if ( rProp.Value >>= aPropertyValue )
+ {
+ if ( aPropertyValue.Name == "PrintContent" )
+ {
+ aPropertyValue.Value <<= nContent;
+ rProp.Value <<= aPropertyValue;
+ }
+ else if ( aPropertyValue.Name == "IsSuppressEmptyPages" )
+ {
+ aPropertyValue.Value <<= bSuppress;
+ rProp.Value <<= aPropertyValue;
+ }
+ }
+ }
+ }
+ rPropValue.Value <<= aUIProp;
+ }
+ }
+}
+
+void ScModelObj::CreateAndSet(ScDocShell* pDocSh)
+{
+ if (pDocSh)
+ pDocSh->SetBaseModel( new ScModelObj(pDocSh) );
+}
+
+SdrModel& ScModelObj::getSdrModelFromUnoModel() const
+{
+ ScDocument& rDoc(pDocShell->GetDocument());
+
+ if(!rDoc.GetDrawLayer())
+ {
+ rDoc.InitDrawLayer();
+ }
+
+ return *rDoc.GetDrawLayer(); // TTTT should be reference
+}
+
+ScModelObj::ScModelObj( ScDocShell* pDocSh ) :
+ SfxBaseModel( pDocSh ),
+ aPropSet( lcl_GetDocOptPropertyMap() ),
+ pDocShell( pDocSh ),
+ maChangesListeners( m_aMutex )
+{
+ // pDocShell may be NULL if this is the base of a ScDocOptionsObj
+ if ( pDocShell )
+ {
+ pDocShell->GetDocument().AddUnoObject(*this); // SfxModel is derived from SfxListener
+ }
+}
+
+ScModelObj::~ScModelObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+
+ if (xNumberAgg.is())
+ xNumberAgg->setDelegator(uno::Reference<uno::XInterface>());
+
+ pPrintFuncCache.reset();
+ pPrinterOptions.reset();
+}
+
+uno::Reference< uno::XAggregation> const & ScModelObj::GetFormatter()
+{
+ // pDocShell may be NULL if this is the base of a ScDocOptionsObj
+ if ( !xNumberAgg.is() && pDocShell )
+ {
+ // setDelegator changes RefCount, so we'd better hold the reference ourselves
+ // (directly in m_refCount, so we don't delete ourselves with release())
+ osl_atomic_increment( &m_refCount );
+ // we need a reference to SvNumberFormatsSupplierObj during queryInterface,
+ // otherwise it'll be deleted
+ uno::Reference<util::XNumberFormatsSupplier> xFormatter(
+ new SvNumberFormatsSupplierObj(pDocShell->GetDocument().GetThreadedContext().GetFormatTable() ));
+ {
+ xNumberAgg.set(uno::Reference<uno::XAggregation>( xFormatter, uno::UNO_QUERY ));
+ // extra block to force deletion of the temporary before setDelegator
+ }
+
+ // during setDelegator no additional reference should exist
+ xFormatter = nullptr;
+
+ if (xNumberAgg.is())
+ xNumberAgg->setDelegator( getXWeak() );
+ osl_atomic_decrement( &m_refCount );
+ } // if ( !xNumberAgg.is() )
+ return xNumberAgg;
+}
+
+ScDocument* ScModelObj::GetDocument() const
+{
+ if (pDocShell)
+ return &pDocShell->GetDocument();
+ return nullptr;
+}
+
+SfxObjectShell* ScModelObj::GetEmbeddedObject() const
+{
+ return pDocShell;
+}
+
+void ScModelObj::UpdateAllRowHeights()
+{
+ if (pDocShell)
+ pDocShell->UpdateAllRowHeights();
+}
+
+void ScModelObj::BeforeXMLLoading()
+{
+ if (pDocShell)
+ pDocShell->BeforeXMLLoading();
+}
+
+void ScModelObj::AfterXMLLoading()
+{
+ if (pDocShell)
+ pDocShell->AfterXMLLoading(true);
+}
+
+ScSheetSaveData* ScModelObj::GetSheetSaveData()
+{
+ if (pDocShell)
+ return pDocShell->GetSheetSaveData();
+ return nullptr;
+}
+
+ScFormatSaveData* ScModelObj::GetFormatSaveData()
+{
+ if (pDocShell)
+ return pDocShell->GetFormatSaveData();
+ return nullptr;
+}
+
+void ScModelObj::RepaintRange( const ScRange& rRange )
+{
+ if (pDocShell)
+ pDocShell->PostPaint( rRange, PaintPartFlags::Grid );
+}
+
+void ScModelObj::RepaintRange( const ScRangeList& rRange )
+{
+ if (pDocShell)
+ pDocShell->PostPaint(rRange, PaintPartFlags::Grid, SC_PF_TESTMERGE);
+}
+
+static ScViewData* lcl_getViewMatchingDocZoomTab(const Fraction& rZoomX,
+ const Fraction& rZoomY,
+ const SCTAB nTab,
+ const ViewShellDocId& rDocId)
+{
+ constexpr size_t nMaxIter = 5;
+ size_t nIter = 0;
+ for (SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ pViewShell && nIter < nMaxIter;
+ (pViewShell = SfxViewShell::GetNext(*pViewShell)), ++nIter)
+ {
+ if (pViewShell->GetDocId() != rDocId)
+ continue;
+
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (!pTabViewShell)
+ continue;
+
+ ScViewData& rData = pTabViewShell->GetViewData();
+ if (rData.GetTabNo() == nTab && rData.GetZoomX() == rZoomX && rData.GetZoomY() == rZoomY)
+ return &rData;
+ }
+
+ return nullptr;
+}
+
+void ScModelObj::paintTile( VirtualDevice& rDevice,
+ int nOutputWidth, int nOutputHeight,
+ int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight )
+{
+ ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false);
+
+ // FIXME: Can this happen? What should we do?
+ if (!pViewShell)
+ return;
+
+ ScViewData* pActiveViewData = &pViewShell->GetViewData();
+ Fraction aFracX(o3tl::toTwips(nOutputWidth, o3tl::Length::px), nTileWidth);
+ Fraction aFracY(o3tl::toTwips(nOutputHeight, o3tl::Length::px), nTileHeight);
+
+ // Try to find a view that matches the tile-zoom requested by iterating over
+ // first few shells. This is to avoid switching of zooms in ScGridWindow::PaintTile
+ // and hence avoid grid-offset recomputation on all shapes which is not cheap.
+ ScViewData* pViewData = lcl_getViewMatchingDocZoomTab(aFracX, aFracY,
+ pActiveViewData->GetTabNo(), pViewShell->GetDocId());
+ if (!pViewData)
+ pViewData = pActiveViewData;
+
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+
+ // update the size of the area we are painting
+ // FIXME we want to use only the minimal necessary size, like the
+ // following; but for the moment there is too many problems with that and
+ // interaction with editeng used for the cell editing
+ //Size aTileSize(nOutputWidth, nOutputHeight);
+ //if (pGridWindow->GetOutputSizePixel() != aTileSize)
+ // pGridWindow->SetOutputSizePixel(Size(nOutputWidth, nOutputHeight));
+ // so instead for now, set the viewport size to document size
+
+ // Fetch the document size and the tiled rendering area together,
+ // because the tiled rendering area is not cheap to compute, and we want
+ // to pass it down to ScGridWindow::PaintFile to avoid computing twice.
+ SCCOL nTiledRenderingAreaEndCol = 0;
+ SCROW nTiledRenderingAreaEndRow = 0;
+ Size aDocSize = getDocumentSize(nTiledRenderingAreaEndCol, nTiledRenderingAreaEndRow);
+
+ pGridWindow->SetOutputSizePixel(Size(aDocSize.Width() * pViewData->GetPPTX(), aDocSize.Height() * pViewData->GetPPTY()));
+
+ pGridWindow->PaintTile( rDevice, nOutputWidth, nOutputHeight,
+ nTilePosX, nTilePosY, nTileWidth, nTileHeight,
+ nTiledRenderingAreaEndCol, nTiledRenderingAreaEndRow );
+
+ // Draw Form controls
+ ScDrawLayer* pDrawLayer = pDocShell->GetDocument().GetDrawLayer();
+ SdrPage* pPage = pDrawLayer->GetPage(sal_uInt16(pViewData->GetTabNo()));
+ SdrView* pDrawView = pViewData->GetViewShell()->GetScDrawView();
+ tools::Rectangle aTileRect(Point(nTilePosX, nTilePosY), Size(nTileWidth, nTileHeight));
+ Size aOutputSize(nOutputWidth, nOutputHeight);
+ LokControlHandler::paintControlTile(pPage, pDrawView, *pGridWindow, rDevice, aOutputSize, aTileRect);
+}
+
+void ScModelObj::setPart( int nPart, bool /*bAllowChangeFocus*/ )
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScTabView* pTabView = pViewData->GetView();
+ if (!pTabView)
+ return;
+
+ if (SdrView* pDrawView = pViewData->GetViewShell()->GetScDrawView())
+ pDrawView->SetNegativeX(comphelper::LibreOfficeKit::isActive() &&
+ pViewData->GetDocument().IsLayoutRTL(nPart));
+
+ pTabView->SelectTabPage(nPart + 1);
+}
+
+int ScModelObj::getParts()
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ return rDoc.GetTableCount();
+}
+
+int ScModelObj::getPart()
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ return pViewData ? pViewData->GetViewShell()->getPart() : 0;
+}
+
+OUString ScModelObj::getPartInfo( int nPart )
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return OUString();
+
+ const bool bIsVisible = pViewData->GetDocument().IsVisible(nPart);
+ //FIXME: Implement IsSelected().
+ const bool bIsSelected = false; //pViewData->GetDocument()->IsSelected(nPart);
+ const bool bIsRTLLayout = pViewData->GetDocument().IsLayoutRTL(nPart);
+
+ OUString aPartInfo = "{ \"visible\": \"" +
+ OUString::number(static_cast<unsigned int>(bIsVisible)) +
+ "\", \"selected\": \"" +
+ OUString::number(static_cast<unsigned int>(bIsSelected)) +
+ "\", \"rtllayout\": \"" +
+ OUString::number(static_cast<unsigned int>(bIsRTLLayout)) +
+ "\" }";
+ return aPartInfo;
+}
+
+OUString ScModelObj::getPartName( int nPart )
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return OUString();
+
+ OUString sTabName;
+ pViewData->GetDocument().GetName(nPart, sTabName);
+ return sTabName;
+}
+
+OUString ScModelObj::getPartHash( int nPart )
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return OUString();
+
+ sal_Int64 nHashCode;
+ return (pViewData->GetDocument().GetHashCode(nPart, nHashCode) ? OUString::number(nHashCode) : OUString());
+}
+
+VclPtr<vcl::Window> ScModelObj::getDocWindow()
+{
+ SolarMutexGuard aGuard;
+
+ ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false);
+
+ // FIXME: Can this happen? What should we do?
+ if (!pViewShell)
+ return VclPtr<vcl::Window>();
+
+ if (VclPtr<vcl::Window> pWindow = SfxLokHelper::getInPlaceDocWindow(pViewShell))
+ return pWindow;
+
+ return pViewShell->GetViewData().GetActiveWin();
+}
+
+Size ScModelObj::getDocumentSize()
+{
+ SCCOL nTiledRenderingAreaEndCol = 0;
+ SCROW nTiledRenderingAreaEndRow = 0;
+ return getDocumentSize(nTiledRenderingAreaEndCol, nTiledRenderingAreaEndRow);
+}
+
+Size ScModelObj::getDocumentSize(SCCOL& rnTiledRenderingAreaEndCol, SCROW& rnTiledRenderingAreaEndRow)
+{
+ Size aSize(10, 10); // minimum size
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return aSize;
+
+ SCTAB nTab = pViewData->GetTabNo();
+ rnTiledRenderingAreaEndCol = 0;
+ rnTiledRenderingAreaEndRow = 0;
+ const ScDocument& rDoc = pDocShell->GetDocument();
+
+ rDoc.GetTiledRenderingArea(nTab, rnTiledRenderingAreaEndCol, rnTiledRenderingAreaEndRow);
+
+ const ScDocument* pThisDoc = &rDoc;
+ const double fPPTX = pViewData->GetPPTX();
+ const double fPPTY = pViewData->GetPPTY();
+
+ auto GetColWidthPx = [pThisDoc, fPPTX, nTab](SCCOL nCol) {
+ const sal_uInt16 nSize = pThisDoc->GetColWidth(nCol, nTab);
+ return ScViewData::ToPixel(nSize, fPPTX);
+ };
+
+ tools::Long nDocWidthPixel = pViewData->GetLOKWidthHelper().computePosition(rnTiledRenderingAreaEndCol, GetColWidthPx);
+ tools::Long nDocHeightPixel = pThisDoc->GetScaledRowHeight(0, rnTiledRenderingAreaEndRow, nTab, fPPTY);
+
+ if (nDocWidthPixel > 0 && nDocHeightPixel > 0)
+ {
+ // convert to twips
+ aSize.setWidth(nDocWidthPixel / fPPTX);
+ aSize.setHeight(nDocHeightPixel / fPPTY);
+ }
+ else
+ {
+ // convert to twips
+ aSize.setWidth(rDoc.GetColWidth(0, rnTiledRenderingAreaEndCol, nTab));
+ aSize.setHeight(rDoc.GetRowHeight(0, rnTiledRenderingAreaEndRow, nTab));
+ }
+
+ return aSize;
+}
+
+Size ScModelObj::getDataArea(long nPart)
+{
+ Size aSize(1, 1);
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData || !pDocShell)
+ return aSize;
+
+ SCTAB nTab = nPart;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScTable* pTab = rDoc.FetchTable(nTab);
+ if (!pTab)
+ return aSize;
+
+ pTab->GetCellArea(nEndCol, nEndRow);
+ aSize = Size(nEndCol, nEndRow);
+
+ return aSize;
+}
+
+void ScModelObj::postKeyEvent(int nType, int nCharCode, int nKeyCode)
+{
+ SolarMutexGuard aGuard;
+ SfxLokHelper::postKeyEventAsync(getDocWindow(), nType, nCharCode, nKeyCode);
+}
+
+void ScModelObj::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
+{
+ SolarMutexGuard aGuard;
+
+ ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false);
+
+ // FIXME: Can this happen? What should we do?
+ if (!pViewShell)
+ return;
+
+ ScViewData* pViewData = &pViewShell->GetViewData();
+
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+
+ if (!pGridWindow)
+ return;
+
+ SCTAB nTab = pViewData->GetTabNo();
+ const ScDocument& rDoc = pDocShell->GetDocument();
+ bool bDrawNegativeX = rDoc.IsNegativePage(nTab);
+ if (SfxLokHelper::testInPlaceComponentMouseEventHit(pViewShell, nType, nX, nY, nCount,
+ nButtons, nModifier, pViewData->GetPPTX(),
+ pViewData->GetPPTY(), bDrawNegativeX))
+ return;
+
+ Point aPointTwip(nX, nY);
+
+ // Check if a control is hit
+ Point aPointHMM = o3tl::convert(aPointTwip, o3tl::Length::twip, o3tl::Length::mm100);
+ Point aPointHMMDraw(bDrawNegativeX ? -aPointHMM.X() : aPointHMM.X(), aPointHMM.Y());
+ ScDrawLayer* pDrawLayer = pDocShell->GetDocument().GetDrawLayer();
+ SdrPage* pPage = pDrawLayer->GetPage(sal_uInt16(nTab));
+ SdrView* pDrawView = pViewData->GetViewShell()->GetScDrawView();
+ if (LokControlHandler::postMouseEvent(pPage, pDrawView, *pGridWindow, nType, aPointHMMDraw, nCount, nButtons, nModifier))
+ return;
+
+ if (!pGridWindow->HasChildPathFocus(true))
+ pGridWindow->GrabFocus();
+
+ // Calc operates in pixels...
+ const Point aPosition(nX * pViewData->GetPPTX() + pGridWindow->GetOutOffXPixel(),
+ nY * pViewData->GetPPTY() + pGridWindow->GetOutOffYPixel());
+
+ VclEventId aEvent = VclEventId::NONE;
+ MouseEvent aData(aPosition, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
+ aData.setLogicPosition(aPointHMM);
+ switch (nType)
+ {
+ case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
+ aEvent = VclEventId::WindowMouseButtonDown;
+ break;
+ case LOK_MOUSEEVENT_MOUSEBUTTONUP:
+ aEvent = VclEventId::WindowMouseButtonUp;
+ break;
+ case LOK_MOUSEEVENT_MOUSEMOVE:
+ aEvent = VclEventId::WindowMouseMove;
+ break;
+ default:
+ break;
+ }
+
+ Application::LOKHandleMouseEvent(aEvent, pGridWindow, &aData);
+}
+
+void ScModelObj::setTextSelection(int nType, int nX, int nY)
+{
+ SolarMutexGuard aGuard;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScTabViewShell* pViewShell = pViewData->GetViewShell();
+
+ LokChartHelper aChartHelper(pViewShell);
+ if (aChartHelper.setTextSelection(nType, nX, nY))
+ return;
+
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewShell);
+ ScDrawView* pDrawView = pViewData->GetScDrawView();
+
+ bool bHandled = false;
+
+ if (pInputHandler && pInputHandler->IsInputMode())
+ {
+ // forwarding to editeng - we are editing the cell content
+ EditView* pTableView = pInputHandler->GetTableView();
+ assert(pTableView);
+
+ Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY));
+
+ if (pTableView->GetOutputArea().Contains(aPoint))
+ {
+ switch (nType)
+ {
+ case LOK_SETTEXTSELECTION_START:
+ pTableView->SetCursorLogicPosition(aPoint, /*bPoint=*/false, /*bClearMark=*/false);
+ break;
+ case LOK_SETTEXTSELECTION_END:
+ pTableView->SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/false);
+ break;
+ case LOK_SETTEXTSELECTION_RESET:
+ pTableView->SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/true);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ bHandled = true;
+ }
+ }
+ else if (pDrawView && pDrawView->IsTextEdit())
+ {
+ // forwarding to editeng - we are editing the text in shape
+ OutlinerView* pOutlinerView = pDrawView->GetTextEditOutlinerView();
+ EditView& rEditView = pOutlinerView->GetEditView();
+
+ Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY));
+ switch (nType)
+ {
+ case LOK_SETTEXTSELECTION_START:
+ rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/false, /*bClearMark=*/false);
+ break;
+ case LOK_SETTEXTSELECTION_END:
+ rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/false);
+ break;
+ case LOK_SETTEXTSELECTION_RESET:
+ rEditView.SetCursorLogicPosition(aPoint, /*bPoint=*/true, /*bClearMark=*/true);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ bHandled = true;
+ }
+
+ if (!bHandled)
+ {
+ // just update the cell selection
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+ if (!pGridWindow)
+ return;
+
+ // move the cell selection handles
+ pGridWindow->SetCellSelectionPixel(nType, nX * pViewData->GetPPTX(), nY * pViewData->GetPPTY());
+ }
+}
+
+uno::Reference<datatransfer::XTransferable> ScModelObj::getSelection()
+{
+ SolarMutexGuard aGuard;
+
+ TransferableDataHelper aDataHelper;
+ uno::Reference<datatransfer::XTransferable> xTransferable;
+
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ {
+ if ( ScEditShell * pShell = dynamic_cast<ScEditShell*>( pViewData->GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ) )
+ xTransferable = pShell->GetEditView()->GetTransferable();
+ else if ( nullptr != dynamic_cast<ScDrawTextObjectBar*>( pViewData->GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ))
+ {
+ ScDrawView* pView = pViewData->GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ if (pOutView)
+ xTransferable = pOutView->GetEditView().GetTransferable();
+ }
+ else if ( ScDrawShell * pDrawShell = dynamic_cast<ScDrawShell*>( pViewData->GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) ) )
+ xTransferable = pDrawShell->GetDrawView()->CopyToTransferable();
+ else
+ xTransferable = pViewData->GetViewShell()->CopyToTransferable();
+ }
+
+ if (!xTransferable.is())
+ xTransferable.set( aDataHelper.GetTransferable() );
+
+ return xTransferable;
+}
+
+void ScModelObj::setGraphicSelection(int nType, int nX, int nY)
+{
+ SolarMutexGuard aGuard;
+
+ ScTabViewShell* pViewShell = pDocShell->GetBestViewShell(false);
+
+ // FIXME: Can this happen? What should we do?
+ if (!pViewShell)
+ return;
+
+ ScViewData* pViewData = &pViewShell->GetViewData();
+
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+
+ double fPPTX = pViewData->GetPPTX();
+ double fPPTY = pViewData->GetPPTY();
+
+ pViewShell = pViewData->GetViewShell();
+ LokChartHelper aChartHelper(pViewShell);
+ if (aChartHelper.setGraphicSelection(nType, nX, nY, fPPTX, fPPTY))
+ return;
+
+ int nPixelX = nX * fPPTX;
+ int nPixelY = nY * fPPTY;
+
+ switch (nType)
+ {
+ case LOK_SETGRAPHICSELECTION_START:
+ {
+ MouseEvent aClickEvent(Point(nPixelX, nPixelY), 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ pGridWindow->MouseButtonDown(aClickEvent);
+ MouseEvent aMoveEvent(Point(nPixelX, nPixelY), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ pGridWindow->MouseMove(aMoveEvent);
+ }
+ break;
+ case LOK_SETGRAPHICSELECTION_END:
+ {
+ MouseEvent aMoveEvent(Point(nPixelX, nPixelY), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ pGridWindow->MouseMove(aMoveEvent);
+ MouseEvent aClickEvent(Point(nPixelX, nPixelY), 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ pGridWindow->MouseButtonUp(aClickEvent);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void ScModelObj::resetSelection()
+{
+ SolarMutexGuard aGuard;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScTabViewShell* pViewShell = pViewData->GetViewShell();
+
+ // deselect the shapes & texts
+ ScDrawView* pDrawView = pViewShell->GetScDrawView();
+ if (pDrawView)
+ {
+ pDrawView->ScEndTextEdit();
+ pDrawView->UnmarkAll();
+ }
+ else
+ pViewShell->Unmark();
+
+ // and hide the cell and text selection
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""_ostr);
+}
+
+void ScModelObj::setClipboard(const uno::Reference<datatransfer::clipboard::XClipboard>& xClipboard)
+{
+ SolarMutexGuard aGuard;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ pViewData->GetActiveWin()->SetClipboard(xClipboard);
+}
+
+bool ScModelObj::isMimeTypeSupported()
+{
+ SolarMutexGuard aGuard;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return false;
+
+
+ TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(pViewData->GetActiveWin()));
+ return EditEngine::HasValidData(aDataHelper.GetTransferable());
+}
+
+static void lcl_sendLOKDocumentBackground(const ScViewData* pViewData)
+{
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const ScPatternAttr *pAttr = rDoc.GetDefPattern();
+ const SfxPoolItem& rItem = pAttr->GetItem(ATTR_BACKGROUND);
+ const SvxBrushItem& rBackground = static_cast<const SvxBrushItem&>(rItem);
+ const Color& rColor = rBackground.GetColor();
+
+ ScTabViewShell* pViewShell = pViewData->GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR, rColor.AsRGBHexString().toUtf8());
+}
+
+void ScModelObj::setClientZoom(int nTilePixelWidth_, int nTilePixelHeight_, int nTileTwipWidth_, int nTileTwipHeight_)
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ // Currently in LOK clients the doc background cannot be changed, so send this sparingly as possible but for every view.
+ // FIXME: Find a better place to trigger this callback where it would be called just once per view creation.
+ // Doing this in ScTabViewShell init code does not work because callbacks do not work at that point for the first view.
+ lcl_sendLOKDocumentBackground(pViewData);
+
+ const Fraction newZoomX(o3tl::toTwips(nTilePixelWidth_, o3tl::Length::px), nTileTwipWidth_);
+ const Fraction newZoomY(o3tl::toTwips(nTilePixelHeight_, o3tl::Length::px), nTileTwipHeight_);
+
+ double fDeltaPPTX = std::abs(ScGlobal::nScreenPPTX * static_cast<double>(newZoomX) - pViewData->GetPPTX());
+ double fDeltaPPTY = std::abs(ScGlobal::nScreenPPTY * static_cast<double>(newZoomY) - pViewData->GetPPTY());
+ constexpr double fEps = 1E-08;
+
+ if (pViewData->GetZoomX() == newZoomX && pViewData->GetZoomY() == newZoomY && fDeltaPPTX < fEps && fDeltaPPTY < fEps)
+ return;
+
+ pViewData->SetZoom(newZoomX, newZoomY, true);
+
+ // refresh our view's take on other view's cursors & selections
+ pViewData->GetActiveWin()->updateKitOtherCursors();
+ pViewData->GetActiveWin()->updateOtherKitSelections();
+
+ if (ScDrawView* pDrawView = pViewData->GetScDrawView())
+ pDrawView->resetGridOffsetsForAllSdrPageViews();
+}
+
+void ScModelObj::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter)
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScTabView* pTabView = pViewData->GetView();
+ if (!pTabView)
+ return;
+
+ pTabView->getRowColumnHeaders(rRectangle, rJsonWriter);
+}
+
+OString ScModelObj::getSheetGeometryData(bool bColumns, bool bRows, bool bSizes, bool bHidden,
+ bool bFiltered, bool bGroups)
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return ""_ostr;
+
+ ScTabView* pTabView = pViewData->GetView();
+ if (!pTabView)
+ return ""_ostr;
+
+ return pTabView->getSheetGeometryData(bColumns, bRows, bSizes, bHidden, bFiltered, bGroups);
+}
+
+void ScModelObj::getCellCursor(tools::JsonWriter& rJsonWriter)
+{
+ SolarMutexGuard aGuard;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+ if (!pGridWindow)
+ return;
+
+ rJsonWriter.put("commandName", ".uno:CellCursor");
+ rJsonWriter.put("commandValues", pGridWindow->getCellCursor());
+}
+
+PointerStyle ScModelObj::getPointer()
+{
+ SolarMutexGuard aGuard;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return PointerStyle::Arrow;
+
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+ if (!pGridWindow)
+ return PointerStyle::Arrow;
+
+ return pGridWindow->GetPointer();
+}
+
+void ScModelObj::getTrackedChanges(tools::JsonWriter& rJson)
+{
+ if (pDocShell)
+ {
+ if (ScChangeTrack* pChangeTrack = pDocShell->GetDocument().GetChangeTrack())
+ pChangeTrack->GetChangeTrackInfo(rJson);
+ }
+}
+
+void ScModelObj::setClientVisibleArea(const tools::Rectangle& rRectangle)
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ // set the PgUp/PgDown offset
+ pViewData->ForcePageUpDownOffset(rRectangle.GetHeight());
+
+ // Store the visible area so that we can use at places like shape insertion
+ pViewData->setLOKVisibleArea(rRectangle);
+
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ ScTabView* pTabView = pViewData->GetView();
+ if (pTabView)
+ pTabView->extendTiledAreaIfNeeded();
+ }
+}
+
+void ScModelObj::setOutlineState(bool bColumn, int nLevel, int nIndex, bool bHidden)
+{
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if (!pViewData)
+ return;
+
+ ScDBFunc* pFunc = pViewData->GetView();
+
+ if (pFunc)
+ pFunc->SetOutlineState(bColumn, nLevel, nIndex, bHidden);
+}
+
+void ScModelObj::getPostIts(tools::JsonWriter& rJsonWriter)
+{
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+
+ auto commentsNode = rJsonWriter.startArray("comments");
+ for (const sc::NoteEntry& aNote : aNotes)
+ {
+ auto commentNode = rJsonWriter.startStruct();
+
+ rJsonWriter.put("id", aNote.mpNote->GetId());
+ rJsonWriter.put("tab", aNote.maPos.Tab());
+ rJsonWriter.put("author", aNote.mpNote->GetAuthor());
+ rJsonWriter.put("dateTime", aNote.mpNote->GetDate());
+ rJsonWriter.put("text", aNote.mpNote->GetText());
+
+ // Calculating the cell cursor position
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ {
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+ if (pGridWindow)
+ {
+ rJsonWriter.put("cellRange", ScPostIt::NoteRangeToJsonString(rDoc, aNote.maPos));
+ }
+ }
+ }
+}
+
+OString ScPostIt::NoteRangeToJsonString(const ScDocument& rDoc, const ScAddress& rPos)
+{
+ SCCOL nX(rPos.Col());
+ SCROW nY(rPos.Row());
+ OString aStartCellAddress(OString::number(nX) + " " + OString::number(nY));
+ const ScPatternAttr* pMarkPattern = rDoc.GetPattern(nX, nY, rPos.Tab());
+ if (pMarkPattern && pMarkPattern->GetItemSet().GetItemState(ATTR_MERGE, false) == SfxItemState::SET)
+ {
+ SCCOL nCol = pMarkPattern->GetItem(ATTR_MERGE).GetColMerge();
+ if (nCol > 1)
+ nX += nCol - 1;
+ SCROW nRow = pMarkPattern->GetItem(ATTR_MERGE).GetRowMerge();
+ if (nRow > 1)
+ nY += nRow - 1;
+ }
+ OString aEndCellAddress(OString::number(nX) + " " + OString::number(nY));
+ return aStartCellAddress + " " + aEndCellAddress;
+}
+
+void ScModelObj::getPostItsPos(tools::JsonWriter& rJsonWriter)
+{
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+
+ auto commentsNode = rJsonWriter.startArray("commentsPos");
+ for (const sc::NoteEntry& aNote : aNotes)
+ {
+ auto commentNode = rJsonWriter.startStruct();
+
+ rJsonWriter.put("id", aNote.mpNote->GetId());
+ rJsonWriter.put("tab", aNote.maPos.Tab());
+
+ // Calculating the cell cursor position
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ {
+ ScGridWindow* pGridWindow = pViewData->GetActiveWin();
+ if (pGridWindow)
+ {
+ rJsonWriter.put("cellRange", ScPostIt::NoteRangeToJsonString(rDoc, aNote.maPos));
+ }
+ }
+ }
+}
+
+void ScModelObj::completeFunction(const OUString& rFunctionName)
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ {
+ assert(!rFunctionName.isEmpty());
+ pHdl->LOKPasteFunctionData(rFunctionName);
+ }
+}
+
+OString ScModelObj::getViewRenderState(SfxViewShell* pViewShell)
+{
+ OStringBuffer aState;
+ ScViewData* pViewData = nullptr;
+
+ if (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast< ScTabViewShell*>(pViewShell);
+ if (pTabViewShell)
+ pViewData = &pTabViewShell->GetViewData();
+ }
+ else
+ {
+ pViewData = ScDocShell::GetViewData();
+ }
+
+ if (pViewData)
+ {
+ aState.append(';');
+
+ const ScViewOptions& aViewOptions = pViewData->GetOptions();
+ OString aThemeName = OUStringToOString(aViewOptions.GetColorSchemeName(), RTL_TEXTENCODING_UTF8);
+ aState.append(aThemeName);
+ }
+
+ return aState.makeStringAndClear();
+}
+
+void ScModelObj::initializeForTiledRendering(const css::uno::Sequence<css::beans::PropertyValue>& rArguments)
+{
+ SolarMutexGuard aGuard;
+
+ // enable word autocompletion
+ ScAppOptions aAppOptions(SC_MOD()->GetAppOptions());
+ aAppOptions.SetAutoComplete(true);
+ SC_MOD()->SetAppOptions(aAppOptions);
+
+ for (const beans::PropertyValue& rValue : rArguments)
+ {
+ if (rValue.Name == ".uno:SpellOnline" && rValue.Value.has<bool>())
+ {
+ ScDocOptions options = GetDocument()->GetDocOptions();
+ options.SetAutoSpell(rValue.Value.get<bool>());
+ GetDocument()->SetDocOptions(options);
+ }
+ }
+
+ // show us the text exactly
+ ScInputOptions aInputOptions(SC_MOD()->GetInputOptions());
+ aInputOptions.SetTextWysiwyg(true);
+ aInputOptions.SetReplaceCellsWarn(false);
+ SC_MOD()->SetInputOptions(aInputOptions);
+ pDocShell->CalcOutputFactor();
+
+ // when the "This document may contain formatting or content that cannot
+ // be saved..." dialog appears, it is auto-cancelled with tiled rendering,
+ // causing 'Save' being disabled; so let's always save to the original
+ // format
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Save::Document::WarnAlienFormat::set(false, xChanges);
+ xChanges->commit();
+}
+
+uno::Any SAL_CALL ScModelObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast< sheet::XSpreadsheetDocument *>(this),
+ static_cast< document::XActionLockable *>(this),
+ static_cast< sheet::XCalculatable *>(this),
+ static_cast< util::XProtectable *>(this),
+ static_cast< drawing::XDrawPagesSupplier *>(this),
+ static_cast< sheet::XGoalSeek *>(this),
+ static_cast< sheet::XConsolidatable *>(this),
+ static_cast< sheet::XDocumentAuditing *>(this),
+ static_cast< style::XStyleFamiliesSupplier *>(this),
+ static_cast< view::XRenderable *>(this),
+ static_cast< document::XLinkTargetSupplier *>(this),
+ static_cast< beans::XPropertySet *>(this),
+ static_cast< lang::XMultiServiceFactory *>(this),
+ static_cast< lang::XServiceInfo *>(this),
+ static_cast< util::XChangesNotifier *>(this),
+ static_cast< sheet::opencl::XOpenCLSelection *>(this),
+ static_cast< chart2::XDataProviderAccess *>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ uno::Any aRet(SfxBaseModel::queryInterface( rType ));
+ if ( !aRet.hasValue()
+ && rType != cppu::UnoType<css::document::XDocumentEventBroadcaster>::get()
+ && rType != cppu::UnoType<css::frame::XController>::get()
+ && rType != cppu::UnoType<css::frame::XFrame>::get()
+ && rType != cppu::UnoType<css::script::XInvocation>::get()
+ && rType != cppu::UnoType<css::beans::XFastPropertySet>::get()
+ && rType != cppu::UnoType<css::awt::XWindow>::get())
+ {
+ GetFormatter();
+ if ( xNumberAgg.is() )
+ aRet = xNumberAgg->queryAggregation( rType );
+ }
+
+ return aRet;
+}
+
+void SAL_CALL ScModelObj::acquire() noexcept
+{
+ SfxBaseModel::acquire();
+}
+
+void SAL_CALL ScModelObj::release() noexcept
+{
+ SfxBaseModel::release();
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScModelObj::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes = [&]()
+ {
+ uno::Sequence<uno::Type> aAggTypes;
+ if ( GetFormatter().is() )
+ {
+ const uno::Type& rProvType = cppu::UnoType<lang::XTypeProvider>::get();
+ uno::Any aNumProv(xNumberAgg->queryAggregation(rProvType));
+ if(auto xNumProv
+ = o3tl::tryAccess<uno::Reference<lang::XTypeProvider>>(aNumProv))
+ {
+ aAggTypes = (*xNumProv)->getTypes();
+ }
+ }
+ return comphelper::concatSequences(
+ SfxBaseModel::getTypes(),
+ aAggTypes,
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<sheet::XSpreadsheetDocument>::get(),
+ cppu::UnoType<document::XActionLockable>::get(),
+ cppu::UnoType<sheet::XCalculatable>::get(),
+ cppu::UnoType<util::XProtectable>::get(),
+ cppu::UnoType<drawing::XDrawPagesSupplier>::get(),
+ cppu::UnoType<sheet::XGoalSeek>::get(),
+ cppu::UnoType<sheet::XConsolidatable>::get(),
+ cppu::UnoType<sheet::XDocumentAuditing>::get(),
+ cppu::UnoType<style::XStyleFamiliesSupplier>::get(),
+ cppu::UnoType<view::XRenderable>::get(),
+ cppu::UnoType<document::XLinkTargetSupplier>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<lang::XMultiServiceFactory>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<util::XChangesNotifier>::get(),
+ cppu::UnoType<sheet::opencl::XOpenCLSelection>::get(),
+ } );
+ }();
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScModelObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void ScModelObj::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ // Not interested in reference update hints here
+
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // has become invalid
+ if (xNumberAgg.is())
+ {
+ SvNumberFormatsSupplierObj* pNumFmt =
+ comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(
+ uno::Reference<util::XNumberFormatsSupplier>(xNumberAgg, uno::UNO_QUERY) );
+ if ( pNumFmt )
+ pNumFmt->SetNumberFormatter( nullptr );
+ }
+
+ pPrintFuncCache.reset(); // must be deleted because it has a pointer to the DocShell
+ m_pPrintState.reset();
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ // cached data for rendering become invalid when contents change
+ // (if a broadcast is added to SetDrawModified, is has to be tested here, too)
+
+ pPrintFuncCache.reset();
+ m_pPrintState.reset();
+
+ // handle "OnCalculate" sheet events (search also for VBA event handlers)
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( rDoc.GetVbaEventProcessor().is() )
+ {
+ // If the VBA event processor is set, HasAnyCalcNotification is much faster than HasAnySheetEventScript
+ if ( rDoc.HasAnyCalcNotification() && rDoc.HasAnySheetEventScript( ScSheetEventId::CALCULATE, true ) )
+ HandleCalculateEvents();
+ }
+ else
+ {
+ if ( rDoc.HasAnySheetEventScript( ScSheetEventId::CALCULATE ) )
+ HandleCalculateEvents();
+ }
+ }
+ }
+
+ // always call parent - SfxBaseModel might need to handle the same hints again
+ SfxBaseModel::Notify( rBC, rHint ); // SfxBaseModel is derived from SfxListener
+}
+
+// XSpreadsheetDocument
+
+uno::Reference<sheet::XSpreadsheets> SAL_CALL ScModelObj::getSheets()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScTableSheetsObj(pDocShell);
+ return nullptr;
+}
+
+css::uno::Reference< ::css::chart2::data::XDataProvider > SAL_CALL ScModelObj::createDataProvider()
+{
+ if (pDocShell)
+ {
+ return css::uno::Reference< ::css::chart2::data::XDataProvider > (
+ ScServiceProvider::MakeInstance(ScServiceProvider::Type::CHDATAPROV, pDocShell), uno::UNO_QUERY);
+ }
+ return nullptr;
+}
+
+// XStyleFamiliesSupplier
+
+uno::Reference<container::XNameAccess> SAL_CALL ScModelObj::getStyleFamilies()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScStyleFamiliesObj(pDocShell);
+ return nullptr;
+}
+
+// XRenderable
+
+static OutputDevice* lcl_GetRenderDevice( const uno::Sequence<beans::PropertyValue>& rOptions )
+{
+ OutputDevice* pRet = nullptr;
+ for (const beans::PropertyValue& rProp : rOptions)
+ {
+ const OUString & rPropName = rProp.Name;
+
+ if (rPropName == SC_UNONAME_RENDERDEV)
+ {
+ uno::Reference<awt::XDevice> xRenderDevice(rProp.Value, uno::UNO_QUERY);
+ if ( xRenderDevice.is() )
+ {
+ VCLXDevice* pDevice = dynamic_cast<VCLXDevice*>( xRenderDevice.get() );
+ if ( pDevice )
+ {
+ pRet = pDevice->GetOutputDevice().get();
+ pRet->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ }
+ }
+ }
+ }
+ return pRet;
+}
+
+static bool lcl_ParseTarget( const OUString& rTarget, ScRange& rTargetRange, tools::Rectangle& rTargetRect,
+ bool& rIsSheet, ScDocument& rDoc, SCTAB nSourceTab )
+{
+ // test in same order as in SID_CURRENTCELL execute
+
+ ScAddress aAddress;
+ SCTAB nNameTab;
+ sal_Int32 nNumeric = 0;
+
+ bool bRangeValid = false;
+ bool bRectValid = false;
+
+ if ( rTargetRange.Parse( rTarget, rDoc ) & ScRefFlags::VALID )
+ {
+ bRangeValid = true; // range reference
+ }
+ else if ( aAddress.Parse( rTarget, rDoc ) & ScRefFlags::VALID )
+ {
+ rTargetRange = aAddress;
+ bRangeValid = true; // cell reference
+ }
+ else if ( ScRangeUtil::MakeRangeFromName( rTarget, rDoc, nSourceTab, rTargetRange ) ||
+ ScRangeUtil::MakeRangeFromName( rTarget, rDoc, nSourceTab, rTargetRange, RUTL_DBASE ) )
+ {
+ bRangeValid = true; // named range or database range
+ }
+ else if ( comphelper::string::isdigitAsciiString(rTarget) &&
+ ( nNumeric = rTarget.toInt32() ) > 0 && nNumeric <= rDoc.MaxRow()+1 )
+ {
+ // row number is always mapped to cell A(row) on the same sheet
+ rTargetRange = ScAddress( 0, static_cast<SCROW>(nNumeric-1), nSourceTab ); // target row number is 1-based
+ bRangeValid = true; // row number
+ }
+ else if ( rDoc.GetTable( rTarget, nNameTab ) )
+ {
+ rTargetRange = ScAddress(0,0,nNameTab);
+ bRangeValid = true; // sheet name
+ rIsSheet = true; // needs special handling (first page of the sheet)
+ }
+ else
+ {
+ // look for named drawing object
+
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if ( pDrawLayer )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !bRangeValid; i++)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(i));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bRangeValid)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rTarget )
+ {
+ rTargetRect = pObject->GetLogicRect(); // 1/100th mm
+ rTargetRange = rDoc.GetRange( i, rTargetRect ); // underlying cells
+ bRangeValid = bRectValid = true; // rectangle is valid
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+ }
+ if ( bRangeValid && !bRectValid )
+ {
+ // get rectangle for cell range
+ rTargetRect = rDoc.GetMMRect( rTargetRange.aStart.Col(), rTargetRange.aStart.Row(),
+ rTargetRange.aEnd.Col(), rTargetRange.aEnd.Row(),
+ rTargetRange.aStart.Tab() );
+ }
+
+ return bRangeValid;
+}
+
+bool ScModelObj::FillRenderMarkData( const uno::Any& aSelection,
+ const uno::Sequence< beans::PropertyValue >& rOptions,
+ ScMarkData& rMark,
+ ScPrintSelectionStatus& rStatus, OUString& rPagesStr,
+ bool& rbRenderToGraphic ) const
+{
+ OSL_ENSURE( !rMark.IsMarked() && !rMark.IsMultiMarked(), "FillRenderMarkData: MarkData must be empty" );
+ OSL_ENSURE( pDocShell, "FillRenderMarkData: DocShell must be set" );
+
+ bool bDone = false;
+
+ uno::Reference<frame::XController> xView;
+
+ // defaults when no options are passed: all sheets, include empty pages
+ bool bSelectedSheetsOnly = false;
+ bool bSuppressEmptyPages = true;
+
+ bool bHasPrintContent = false;
+ sal_Int32 nPrintContent = 0; // all sheets / selected sheets / selected cells
+ sal_Int32 nPrintRange = 0; // all pages / pages
+ sal_Int32 nEOContent = 0; // even pages / odd pages
+ OUString aPageRange; // "pages" edit value
+
+ for( const auto& rOption : rOptions )
+ {
+ if ( rOption.Name == "IsOnlySelectedSheets" )
+ {
+ rOption.Value >>= bSelectedSheetsOnly;
+ }
+ else if ( rOption.Name == "IsSuppressEmptyPages" )
+ {
+ rOption.Value >>= bSuppressEmptyPages;
+ }
+ else if ( rOption.Name == "PageRange" )
+ {
+ rOption.Value >>= aPageRange;
+ }
+ else if ( rOption.Name == "PrintRange" )
+ {
+ rOption.Value >>= nPrintRange;
+ }
+ else if ( rOption.Name == "EvenOdd" )
+ {
+ rOption.Value >>= nEOContent;
+ }
+ else if ( rOption.Name == "PrintContent" )
+ {
+ bHasPrintContent = true;
+ rOption.Value >>= nPrintContent;
+ }
+ else if ( rOption.Name == "View" )
+ {
+ rOption.Value >>= xView;
+ }
+ else if ( rOption.Name == "RenderToGraphic" )
+ {
+ rOption.Value >>= rbRenderToGraphic;
+ }
+ }
+
+ // "Print Content" selection wins over "Selected Sheets" option
+ if ( bHasPrintContent )
+ bSelectedSheetsOnly = ( nPrintContent != 0 );
+
+ uno::Reference<uno::XInterface> xInterface(aSelection, uno::UNO_QUERY);
+ if ( xInterface.is() )
+ {
+ ScCellRangesBase* pSelObj = dynamic_cast<ScCellRangesBase*>( xInterface.get() );
+ uno::Reference< drawing::XShapes > xShapes( xInterface, uno::UNO_QUERY );
+ if ( pSelObj && pSelObj->GetDocShell() == pDocShell )
+ {
+ bool bSheet = ( dynamic_cast<ScTableSheetObj*>( pSelObj ) != nullptr );
+ bool bCursor = pSelObj->IsCursorOnly();
+ const ScRangeList& rRanges = pSelObj->GetRangeList();
+
+ rMark.MarkFromRangeList( rRanges, false );
+ rMark.MarkToSimple();
+
+ if ( rMark.IsMultiMarked() )
+ {
+ // #i115266# copy behavior of old printing:
+ // treat multiple selection like a single selection with the enclosing range
+ const ScRange& aMultiMarkArea = rMark.GetMultiMarkArea();
+ rMark.ResetMark();
+ rMark.SetMarkArea( aMultiMarkArea );
+ }
+
+ if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ // a sheet object is treated like an empty selection: print the used area of the sheet
+
+ if ( bCursor || bSheet ) // nothing selected -> use whole tables
+ {
+ rMark.ResetMark(); // doesn't change table selection
+ rStatus.SetMode( ScPrintSelectionMode::Cursor );
+ }
+ else
+ rStatus.SetMode( ScPrintSelectionMode::Range );
+
+ rStatus.SetRanges( rRanges );
+ bDone = true;
+ }
+ // multi selection isn't supported
+ }
+ else if( xShapes.is() )
+ {
+ //print a selected ole object
+ // multi selection isn't supported yet
+ uno::Reference< drawing::XShape > xShape( xShapes->getByIndex(0), uno::UNO_QUERY );
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape );
+ if( pSdrObj && pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ tools::Rectangle aObjRect = pSdrObj->GetCurrentBoundRect();
+ SCTAB nCurrentTab = ScDocShell::GetCurTab();
+ ScRange aRange = rDoc.GetRange( nCurrentTab, aObjRect );
+ rMark.SetMarkArea( aRange );
+
+ if( rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ rStatus.SetMode( ScPrintSelectionMode::RangeExclusivelyOleAndDrawObjects );
+ bDone = true;
+ }
+ }
+ }
+ else if ( comphelper::getFromUnoTunnel<ScModelObj>( xInterface ) == this )
+ {
+ // render the whole document
+ // -> no selection, all sheets
+
+ SCTAB nTabCount = pDocShell->GetDocument().GetTableCount();
+ for (SCTAB nTab = 0; nTab < nTabCount; nTab++)
+ rMark.SelectTable( nTab, true );
+ rStatus.SetMode( ScPrintSelectionMode::Document );
+ bDone = true;
+ }
+ // other selection types aren't supported
+ }
+
+ // restrict to selected sheets if a view is available
+ uno::Reference<sheet::XSelectedSheetsSupplier> xSelectedSheets(xView, uno::UNO_QUERY);
+ if (bSelectedSheetsOnly && pDocShell && xSelectedSheets.is())
+ {
+ const uno::Sequence<sal_Int32> aSelected = xSelectedSheets->getSelectedSheets();
+ ScMarkData::MarkedTabsType aSelectedTabs;
+ SCTAB nMaxTab = pDocShell->GetDocument().GetTableCount() -1;
+ for (const auto& rSelected : aSelected)
+ {
+ SCTAB nSelected = static_cast<SCTAB>(rSelected);
+ if (ValidTab(nSelected, nMaxTab))
+ aSelectedTabs.insert(nSelected);
+ }
+ rMark.SetSelectedTabs(aSelectedTabs);
+ }
+
+ ScPrintOptions aNewOptions;
+ aNewOptions.SetSkipEmpty( bSuppressEmptyPages );
+ aNewOptions.SetAllSheets( !bSelectedSheetsOnly );
+ rStatus.SetOptions( aNewOptions );
+
+ // "PrintRange" enables (1) or disables (0) the "PageRange" edit
+ if ( nPrintRange == 1 )
+ rPagesStr = aPageRange;
+ else
+ rPagesStr.clear();
+
+ return bDone;
+}
+
+sal_Int32 SAL_CALL ScModelObj::getRendererCount(const uno::Any& aSelection,
+ const uno::Sequence<beans::PropertyValue>& rOptions)
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ {
+ throw lang::DisposedException( OUString(),
+ static_cast< sheet::XSpreadsheetDocument* >(this) );
+ }
+
+ ScMarkData aMark(GetDocument()->GetSheetLimits());
+ ScPrintSelectionStatus aStatus;
+ OUString aPagesStr;
+ bool bRenderToGraphic = false;
+ if ( !FillRenderMarkData( aSelection, rOptions, aMark, aStatus, aPagesStr, bRenderToGraphic ) )
+ return 0;
+
+ // The same ScPrintFuncCache object in pPrintFuncCache is used as long as
+ // the same selection is used (aStatus) and the document isn't changed
+ // (pPrintFuncCache is cleared in Notify handler)
+
+ if ( !pPrintFuncCache || !pPrintFuncCache->IsSameSelection( aStatus ) )
+ {
+ pPrintFuncCache.reset(new ScPrintFuncCache( pDocShell, aMark, aStatus ));
+ }
+ sal_Int32 nPages = pPrintFuncCache->GetPageCount();
+
+ m_pPrintState.reset();
+ maValidPages.clear();
+
+ sal_Int32 nContent = 0;
+ sal_Int32 nEOContent = 0;
+ bool bSinglePageSheets = false;
+ for ( const auto& rValue : rOptions)
+ {
+ if ( rValue.Name == "PrintRange" )
+ {
+ rValue.Value >>= nContent;
+ }
+ else if ( rValue.Name == "SinglePageSheets" )
+ {
+ rValue.Value >>= bSinglePageSheets;
+ }
+ else if ( rValue.Name == "EvenOdd" )
+ {
+ rValue.Value >>= nEOContent;
+ }
+ }
+
+ if (bSinglePageSheets)
+ {
+ return pDocShell->GetDocument().GetTableCount();
+ }
+
+ bool bIsPrintEvenPages = (nEOContent != 1 && nContent == 0) || nContent != 0;
+ bool bIsPrintOddPages = (nEOContent != 2 && nContent == 0) || nContent != 0;
+
+ for ( sal_Int32 nPage = 1; nPage <= nPages; nPage++ )
+ {
+ if ( (bIsPrintEvenPages && IsOnEvenPage( nPage )) || (bIsPrintOddPages && !IsOnEvenPage( nPage )) )
+ maValidPages.push_back( nPage );
+ }
+
+ sal_Int32 nSelectCount = static_cast<sal_Int32>( maValidPages.size() );
+
+ if ( nEOContent == 1 || nEOContent == 2 ) // even pages / odd pages
+ return nSelectCount;
+
+ if ( !aPagesStr.isEmpty() )
+ {
+ StringRangeEnumerator aRangeEnum( aPagesStr, 0, nPages-1 );
+ nSelectCount = aRangeEnum.size();
+ }
+ return (nSelectCount > 0) ? nSelectCount : 1;
+}
+
+static sal_Int32 lcl_GetRendererNum( sal_Int32 nSelRenderer, std::u16string_view rPagesStr, sal_Int32 nTotalPages )
+{
+ if ( rPagesStr.empty() )
+ return nSelRenderer;
+
+ StringRangeEnumerator aRangeEnum( rPagesStr, 0, nTotalPages-1 );
+ StringRangeEnumerator::Iterator aIter = aRangeEnum.begin();
+ StringRangeEnumerator::Iterator aEnd = aRangeEnum.end();
+ for ( ; nSelRenderer > 0 && aIter != aEnd; --nSelRenderer )
+ ++aIter;
+
+ return *aIter; // returns -1 if reached the end
+}
+
+static bool lcl_renderSelectionToGraphic( bool bRenderToGraphic, const ScPrintSelectionStatus& rStatus )
+{
+ return bRenderToGraphic && rStatus.GetMode() == ScPrintSelectionMode::Range;
+}
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScModelObj::getRenderer( sal_Int32 nSelRenderer,
+ const uno::Any& aSelection, const uno::Sequence<beans::PropertyValue>& rOptions )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ {
+ throw lang::DisposedException( OUString(),
+ static_cast< sheet::XSpreadsheetDocument* >(this) );
+ }
+
+ ScMarkData aMark(pDocShell->GetDocument().GetSheetLimits());
+ ScPrintSelectionStatus aStatus;
+ OUString aPagesStr;
+ // #i115266# if FillRenderMarkData fails, keep nTotalPages at 0, but still handle getRenderer(0) below
+ tools::Long nTotalPages = 0;
+ bool bRenderToGraphic = false;
+ bool bSinglePageSheets = false;
+ if ( FillRenderMarkData( aSelection, rOptions, aMark, aStatus, aPagesStr, bRenderToGraphic ) )
+ {
+ if ( !pPrintFuncCache || !pPrintFuncCache->IsSameSelection( aStatus ) )
+ {
+ pPrintFuncCache.reset(new ScPrintFuncCache( pDocShell, aMark, aStatus ));
+ }
+ nTotalPages = pPrintFuncCache->GetPageCount();
+ }
+
+ for ( const auto& rValue : rOptions)
+ {
+ if ( rValue.Name == "SinglePageSheets" )
+ {
+ rValue.Value >>= bSinglePageSheets;
+ break;
+ }
+ }
+
+ if (bSinglePageSheets)
+ nTotalPages = pDocShell->GetDocument().GetTableCount();
+
+ sal_Int32 nRenderer = lcl_GetRendererNum( nSelRenderer, aPagesStr, nTotalPages );
+
+ if ( nRenderer < 0 )
+ {
+ if ( nSelRenderer != 0 )
+ throw lang::IllegalArgumentException();
+
+ // getRenderer(0) is used to query the settings, so it must always return something
+
+ awt::Size aPageSize;
+ if (lcl_renderSelectionToGraphic( bRenderToGraphic, aStatus))
+ {
+ assert( aMark.IsMarked());
+ const ScRange& aRange = aMark.GetMarkArea();
+ tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab()));
+ aPageSize.Width = aMMRect.GetWidth();
+ aPageSize.Height = aMMRect.GetHeight();
+ }
+ else
+ {
+ SCTAB const nCurTab = 0; //! use current sheet from view?
+ ScPrintFunc aDefaultFunc( pDocShell, pDocShell->GetPrinter(), nCurTab );
+ Size aTwips = aDefaultFunc.GetPageSize();
+ aPageSize.Width = convertTwipToMm100(aTwips.Width());
+ aPageSize.Height = convertTwipToMm100(aTwips.Height());
+ }
+
+ uno::Sequence<beans::PropertyValue> aSequence( comphelper::InitPropertySequence({
+ { SC_UNONAME_PAGESIZE, uno::Any(aPageSize) }
+ }));
+
+ if( ! pPrinterOptions )
+ pPrinterOptions.reset(new ScPrintUIOptions);
+ else
+ pPrinterOptions->SetDefaults();
+ pPrinterOptions->appendPrintUIOptions( aSequence );
+ return aSequence;
+
+ }
+
+ // printer is used as device (just for page layout), draw view is not needed
+
+ SCTAB nTab;
+ if (bSinglePageSheets)
+ nTab = nSelRenderer;
+ else if ( !maValidPages.empty() )
+ nTab = pPrintFuncCache->GetTabForPage( maValidPages.at( nRenderer )-1 );
+ else
+ nTab = pPrintFuncCache->GetTabForPage( nRenderer );
+
+
+ ScRange aRange;
+ const ScRange* pSelRange = nullptr;
+ if ( bSinglePageSheets )
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ const ScDocument* pDocument = &pDocShell->GetDocument();
+ pDocument->GetDataStart( nTab, nStartCol, nStartRow );
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDocument->GetPrintArea( nTab, nEndCol, nEndRow );
+
+ aRange.aStart = ScAddress(nStartCol, nStartRow, nTab);
+ aRange.aEnd = ScAddress(nEndCol, nEndRow, nTab);
+
+ table::CellRangeAddress aRangeAddress( nTab,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab()));
+
+ const awt::Size aPageSize(aMMRect.GetWidth(), aMMRect.GetHeight());
+ const awt::Point aCalcPagePos(aMMRect.Left(), aMMRect.Top());
+
+ uno::Sequence<beans::PropertyValue> aSequence
+ {
+ comphelper::makePropertyValue(SC_UNONAME_PAGESIZE, aPageSize),
+ // #i111158# all positions are relative to the whole page, including non-printable area
+ comphelper::makePropertyValue(SC_UNONAME_INC_NP_AREA, true),
+ comphelper::makePropertyValue(SC_UNONAME_SOURCERANGE, aRangeAddress),
+ comphelper::makePropertyValue(SC_UNONAME_CALCPAGESIZE, aPageSize), // TODO aPageSize too ?
+ comphelper::makePropertyValue(SC_UNONAME_CALCPAGEPOS, aCalcPagePos)
+ };
+
+ if( ! pPrinterOptions )
+ pPrinterOptions.reset(new ScPrintUIOptions);
+ else
+ pPrinterOptions->SetDefaults();
+ pPrinterOptions->appendPrintUIOptions( aSequence );
+ return aSequence;
+ }
+ else if ( aMark.IsMarked() )
+ {
+ aRange = aMark.GetMarkArea();
+ pSelRange = &aRange;
+ }
+
+ awt::Size aPageSize;
+ bool bWasCellRange = false;
+ ScRange aCellRange;
+ if (lcl_renderSelectionToGraphic( bRenderToGraphic, aStatus))
+ {
+ bWasCellRange = true;
+ aCellRange = aRange;
+ tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab()));
+ aPageSize.Width = aMMRect.GetWidth();
+ aPageSize.Height = aMMRect.GetHeight();
+ }
+ else
+ {
+ std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc;
+ if (m_pPrintState && m_pPrintState->nPrintTab == nTab)
+ pPrintFunc.reset(new ScPrintFunc(pDocShell, pDocShell->GetPrinter(), *m_pPrintState, &aStatus.GetOptions()));
+ else
+ pPrintFunc.reset(new ScPrintFunc(pDocShell, pDocShell->GetPrinter(), nTab,
+ pPrintFuncCache->GetFirstAttr(nTab), nTotalPages, pSelRange, &aStatus.GetOptions()));
+ pPrintFunc->SetRenderFlag( true );
+
+ sal_Int32 nContent = 0;
+ sal_Int32 nEOContent = 0;
+ for ( const auto& rValue : rOptions)
+ {
+ if ( rValue.Name == "PrintRange" )
+ {
+ rValue.Value >>= nContent;
+ }
+ else if ( rValue.Name == "EvenOdd" )
+ {
+ rValue.Value >>= nEOContent;
+ }
+ }
+
+ MultiSelection aPage;
+ aPage.SetTotalRange( Range(0,RANGE_MAX) );
+
+ bool bOddOrEven = (nContent == 0 && nEOContent == 1) || (nContent == 1 && nEOContent == 2); // even pages or odd pages
+ // tdf#127682 when odd/even allow nRenderer of 0 even when maValidPages is empty
+ // to allow PrinterController::abortJob to spool an empty page as part of
+ // its abort procedure
+ if (bOddOrEven && !maValidPages.empty())
+ aPage.Select( maValidPages.at(nRenderer) );
+ else
+ aPage.Select( nRenderer+1 );
+
+ tools::Long nDisplayStart = pPrintFuncCache->GetDisplayStart( nTab );
+ tools::Long nTabStart = pPrintFuncCache->GetTabStart( nTab );
+
+ (void)pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, false, nullptr );
+
+ bWasCellRange = pPrintFunc->GetLastSourceRange( aCellRange );
+ Size aTwips = pPrintFunc->GetPageSize();
+
+ if (!m_pPrintState)
+ {
+ m_pPrintState.reset(new ScPrintState());
+ pPrintFunc->GetPrintState(*m_pPrintState, true);
+ }
+
+ aPageSize.Width = convertTwipToMm100(aTwips.Width());
+ aPageSize.Height = convertTwipToMm100(aTwips.Height());
+ }
+
+ tools::Long nPropCount = bWasCellRange ? 5 : 4;
+ uno::Sequence<beans::PropertyValue> aSequence(nPropCount);
+ beans::PropertyValue* pArray = aSequence.getArray();
+ pArray[0].Name = SC_UNONAME_PAGESIZE;
+ pArray[0].Value <<= aPageSize;
+ // #i111158# all positions are relative to the whole page, including non-printable area
+ pArray[1].Name = SC_UNONAME_INC_NP_AREA;
+ pArray[1].Value <<= true;
+ if ( bWasCellRange )
+ {
+ table::CellRangeAddress aRangeAddress( nTab,
+ aCellRange.aStart.Col(), aCellRange.aStart.Row(),
+ aCellRange.aEnd.Col(), aCellRange.aEnd.Row() );
+ tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect(
+ aCellRange.aStart.Col(), aCellRange.aStart.Row(),
+ aCellRange.aEnd.Col(), aCellRange.aEnd.Row(), aCellRange.aStart.Tab()));
+
+ const awt::Size aCalcPageSize(aMMRect.GetWidth(), aMMRect.GetHeight());
+ const awt::Point aCalcPagePos(aMMRect.Left(), aMMRect.Top());
+
+ pArray[2].Name = SC_UNONAME_SOURCERANGE;
+ pArray[2].Value <<= aRangeAddress;
+ pArray[3].Name = SC_UNONAME_CALCPAGESIZE;
+ pArray[3].Value <<= aCalcPageSize;
+ pArray[4].Name = SC_UNONAME_CALCPAGEPOS;
+ pArray[4].Value <<= aCalcPagePos;
+ }
+
+ if( ! pPrinterOptions )
+ pPrinterOptions.reset(new ScPrintUIOptions);
+ else
+ pPrinterOptions->SetDefaults();
+ pPrinterOptions->appendPrintUIOptions( aSequence );
+ return aSequence;
+}
+
+void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelection,
+ const uno::Sequence<beans::PropertyValue>& rOptions )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ {
+ throw lang::DisposedException( OUString(),
+ static_cast< sheet::XSpreadsheetDocument* >(this) );
+ }
+
+ ScMarkData aMark(pDocShell->GetDocument().GetSheetLimits());
+ ScPrintSelectionStatus aStatus;
+ OUString aPagesStr;
+ bool bRenderToGraphic = false;
+ bool bSinglePageSheets = false;
+ if ( !FillRenderMarkData( aSelection, rOptions, aMark, aStatus, aPagesStr, bRenderToGraphic ) )
+ throw lang::IllegalArgumentException();
+
+ if ( !pPrintFuncCache || !pPrintFuncCache->IsSameSelection( aStatus ) )
+ {
+ pPrintFuncCache.reset(new ScPrintFuncCache( pDocShell, aMark, aStatus ));
+ }
+ tools::Long nTotalPages = pPrintFuncCache->GetPageCount();
+
+ for ( const auto& rValue : rOptions)
+ {
+ if ( rValue.Name == "SinglePageSheets" )
+ {
+ rValue.Value >>= bSinglePageSheets;
+ break;
+ }
+ }
+
+ if (bSinglePageSheets)
+ nTotalPages = pDocShell->GetDocument().GetTableCount();
+
+ sal_Int32 nRenderer = lcl_GetRendererNum( nSelRenderer, aPagesStr, nTotalPages );
+ if ( nRenderer < 0 )
+ throw lang::IllegalArgumentException();
+
+ OutputDevice* pDev = lcl_GetRenderDevice( rOptions );
+ if ( !pDev )
+ throw lang::IllegalArgumentException();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ ScRange aRange;
+ const ScRange* pSelRange = nullptr;
+ if ( bSinglePageSheets )
+ {
+ awt::Size aPageSize;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ rDoc.GetDataStart( nSelRenderer, nStartCol, nStartRow );
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ rDoc.GetPrintArea( nSelRenderer, nEndCol, nEndRow );
+
+ aRange.aStart = ScAddress(nStartCol, nStartRow, nSelRenderer);
+ aRange.aEnd = ScAddress(nEndCol, nEndRow, nSelRenderer);
+
+ tools::Rectangle aMMRect( pDocShell->GetDocument().GetMMRect(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab()));
+
+ aPageSize.Width = aMMRect.GetWidth();
+ aPageSize.Height = aMMRect.GetHeight();
+
+ //Set visible tab
+ SCTAB nVisTab = rDoc.GetVisibleTab();
+ if (nVisTab != nSelRenderer)
+ {
+ nVisTab = nSelRenderer;
+ rDoc.SetVisibleTab(nVisTab);
+ }
+
+ pDocShell->DoDraw(pDev, Point(0,0), Size(aPageSize.Width, aPageSize.Height), JobSetup());
+
+ return;
+ }
+ else if ( aMark.IsMarked() )
+ {
+ aRange = aMark.GetMarkArea();
+ pSelRange = &aRange;
+ }
+
+ if (lcl_renderSelectionToGraphic( bRenderToGraphic, aStatus))
+ {
+ // Similar to as in and when calling ScTransferObj::PaintToDev()
+
+ tools::Rectangle aBound( Point(), pDev->GetOutputSize());
+
+ ScViewData aViewData(rDoc);
+
+ aViewData.SetTabNo( aRange.aStart.Tab() );
+ aViewData.SetScreen( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() );
+
+ const double nPrintFactor = 1.0; /* XXX: currently (2017-08-28) is not evaluated */
+ // The bMetaFile argument maybe could be
+ // pDev->GetConnectMetaFile() != nullptr
+ // but for some yet unknown reason does not draw cell content if true.
+ ScPrintFunc::DrawToDev( rDoc, pDev, nPrintFactor, aBound, &aViewData, false /*bMetaFile*/ );
+
+ return;
+ }
+
+ struct DrawViewKeeper
+ {
+ std::unique_ptr<FmFormView> mpDrawView;
+ DrawViewKeeper() {}
+ ~DrawViewKeeper()
+ {
+ if (mpDrawView)
+ {
+ mpDrawView->HideSdrPage();
+ mpDrawView.reset();
+ }
+ }
+ } aDrawViewKeeper;
+
+ SCTAB nTab;
+ if ( !maValidPages.empty() )
+ nTab = pPrintFuncCache->GetTabForPage( maValidPages.at( nRenderer )-1 );
+ else
+ nTab = pPrintFuncCache->GetTabForPage( nRenderer );
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+
+ if( pModel )
+ {
+ aDrawViewKeeper.mpDrawView.reset( new FmFormView(
+ *pModel,
+ pDev) );
+ aDrawViewKeeper.mpDrawView->ShowSdrPage(aDrawViewKeeper.mpDrawView->GetModel().GetPage(nTab));
+ aDrawViewKeeper.mpDrawView->SetPrintPreview();
+ }
+
+ // to increase performance, ScPrintState might be used here for subsequent
+ // pages of the same sheet
+
+
+ std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc;
+ if (m_pPrintState && m_pPrintState->nPrintTab == nTab
+ && ! pSelRange) // tdf#120161 use selection to set required printed area
+ pPrintFunc.reset(new ScPrintFunc(pDev, pDocShell, *m_pPrintState, &aStatus.GetOptions()));
+ else
+ pPrintFunc.reset(new ScPrintFunc(pDev, pDocShell, nTab, pPrintFuncCache->GetFirstAttr(nTab), nTotalPages, pSelRange, &aStatus.GetOptions()));
+
+ pPrintFunc->SetDrawView( aDrawViewKeeper.mpDrawView.get() );
+ pPrintFunc->SetRenderFlag( true );
+ if( aStatus.GetMode() == ScPrintSelectionMode::RangeExclusivelyOleAndDrawObjects )
+ pPrintFunc->SetExclusivelyDrawOleAndDrawObjects();
+
+ sal_Int32 nContent = 0;
+ sal_Int32 nEOContent = 0;
+ for ( const auto& rValue : rOptions)
+ {
+ if ( rValue.Name == "PrintRange" )
+ {
+ rValue.Value >>= nContent;
+ }
+ else if ( rValue.Name == "EvenOdd" )
+ {
+ rValue.Value >>= nEOContent;
+ }
+ }
+
+ MultiSelection aPage;
+ aPage.SetTotalRange( Range(0,RANGE_MAX) );
+
+ bool bOddOrEven = (nContent == 0 && nEOContent == 1) || (nContent == 0 && nEOContent == 2); // even pages or odd pages
+ // tdf#127682 when odd/even allow nRenderer of 0 even when maValidPages is empty
+ // to allow PrinterController::abortJob to spool an empty page as part of
+ // its abort procedure
+ if (bOddOrEven && !maValidPages.empty())
+ aPage.Select( maValidPages.at( nRenderer ) );
+ else
+ aPage.Select( nRenderer+1 );
+
+ tools::Long nDisplayStart = pPrintFuncCache->GetDisplayStart( nTab );
+ tools::Long nTabStart = pPrintFuncCache->GetTabStart( nTab );
+
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(pDev->GetExtOutDevData() );
+ if ( nRenderer == nTabStart )
+ {
+ if (pPDFData)
+ {
+ css::lang::Locale const docLocale(Application::GetSettings().GetLanguageTag().getLocale());
+ pPDFData->SetDocumentLocale(docLocale);
+ }
+
+ // first page of a sheet: add outline item for the sheet name
+
+ if ( pPDFData && pPDFData->GetIsExportBookmarks() )
+ {
+ // the sheet starts at the top of the page
+ tools::Rectangle aArea( pDev->PixelToLogic( tools::Rectangle( 0,0,0,0 ) ) );
+ sal_Int32 nDestID = pPDFData->CreateDest( aArea );
+ OUString aTabName;
+ rDoc.GetName( nTab, aTabName );
+ // top-level
+ pPDFData->CreateOutlineItem( -1/*nParent*/, aTabName, nDestID );
+ }
+ // #i56629# add the named destination stuff
+ if( pPDFData && pPDFData->GetIsExportNamedDestinations() )
+ {
+ tools::Rectangle aArea( pDev->PixelToLogic( tools::Rectangle( 0,0,0,0 ) ) );
+ OUString aTabName;
+ rDoc.GetName( nTab, aTabName );
+ //need the PDF page number here
+ pPDFData->CreateNamedDest( aTabName, aArea );
+ }
+ }
+
+ (void)pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, true, nullptr );
+
+ if (!m_pPrintState)
+ {
+ m_pPrintState.reset(new ScPrintState());
+ pPrintFunc->GetPrintState(*m_pPrintState, true);
+ }
+
+ // resolve the hyperlinks for PDF export
+
+ if ( !pPDFData || pPDFData->GetBookmarks().empty() )
+ return;
+
+ // iterate over the hyperlinks that were output for this page
+
+ std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
+ for ( const auto& rBookmark : rBookmarks )
+ {
+ OUString aBookmark = rBookmark.aBookmark;
+ if ( aBookmark.toChar() == '#' )
+ {
+ // try to resolve internal link
+
+ OUString aTarget( aBookmark.copy( 1 ) );
+
+ ScRange aTargetRange;
+ tools::Rectangle aTargetRect; // 1/100th mm
+ bool bIsSheet = false;
+ bool bValid = lcl_ParseTarget( aTarget, aTargetRange, aTargetRect, bIsSheet, rDoc, nTab );
+
+ if ( bValid )
+ {
+ sal_Int32 nPage = -1;
+ tools::Rectangle aArea;
+ if ( bIsSheet )
+ {
+ // Get first page for sheet (if nothing from that sheet is printed,
+ // this page can show a different sheet)
+ nPage = pPrintFuncCache->GetTabStart( aTargetRange.aStart.Tab() );
+ aArea = pDev->PixelToLogic( tools::Rectangle( 0,0,0,0 ) );
+ }
+ else
+ {
+ pPrintFuncCache->InitLocations( aMark, pDev ); // does nothing if already initialized
+
+ ScPrintPageLocation aLocation;
+ if ( pPrintFuncCache->FindLocation( aTargetRange.aStart, aLocation ) )
+ {
+ nPage = aLocation.nPage;
+
+ // get the rectangle of the page's cell range in 1/100th mm
+ ScRange aLocRange = aLocation.aCellRange;
+ tools::Rectangle aLocationMM = rDoc.GetMMRect(
+ aLocRange.aStart.Col(), aLocRange.aStart.Row(),
+ aLocRange.aEnd.Col(), aLocRange.aEnd.Row(),
+ aLocRange.aStart.Tab() );
+ tools::Rectangle aLocationPixel = aLocation.aRectangle;
+
+ // Scale and move the target rectangle from aLocationMM to aLocationPixel,
+ // to get the target rectangle in pixels.
+ assert(aLocationPixel.GetWidth() != 0 && aLocationPixel.GetHeight() != 0);
+
+ Fraction aScaleX( aLocationPixel.GetWidth(), aLocationMM.GetWidth() );
+ Fraction aScaleY( aLocationPixel.GetHeight(), aLocationMM.GetHeight() );
+
+ tools::Long nX1 = aLocationPixel.Left() + static_cast<tools::Long>( Fraction( aTargetRect.Left() - aLocationMM.Left(), 1 ) * aScaleX );
+ tools::Long nX2 = aLocationPixel.Left() + static_cast<tools::Long>( Fraction( aTargetRect.Right() - aLocationMM.Left(), 1 ) * aScaleX );
+ tools::Long nY1 = aLocationPixel.Top() + static_cast<tools::Long>( Fraction( aTargetRect.Top() - aLocationMM.Top(), 1 ) * aScaleY );
+ tools::Long nY2 = aLocationPixel.Top() + static_cast<tools::Long>( Fraction( aTargetRect.Bottom() - aLocationMM.Top(), 1 ) * aScaleY );
+
+ if ( nX1 > aLocationPixel.Right() ) nX1 = aLocationPixel.Right();
+ if ( nX2 > aLocationPixel.Right() ) nX2 = aLocationPixel.Right();
+ if ( nY1 > aLocationPixel.Bottom() ) nY1 = aLocationPixel.Bottom();
+ if ( nY2 > aLocationPixel.Bottom() ) nY2 = aLocationPixel.Bottom();
+
+ // The link target area is interpreted using the device's MapMode at
+ // the time of the CreateDest call, so PixelToLogic can be used here,
+ // regardless of the MapMode that is actually selected.
+ aArea = pDev->PixelToLogic( tools::Rectangle( nX1, nY1, nX2, nY2 ) );
+ }
+ }
+
+ if ( nPage >= 0 )
+ pPDFData->SetLinkDest( rBookmark.nLinkId, pPDFData->CreateDest( aArea, nPage ) );
+ }
+ }
+ else
+ {
+ // external link, use as-is
+ pPDFData->SetLinkURL( rBookmark.nLinkId, aBookmark );
+ }
+ }
+ rBookmarks.clear();
+}
+
+// XLinkTargetSupplier
+
+uno::Reference<container::XNameAccess> SAL_CALL ScModelObj::getLinks()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScLinkTargetTypesObj(pDocShell);
+ return nullptr;
+}
+
+// XActionLockable
+
+sal_Bool SAL_CALL ScModelObj::isActionLocked()
+{
+ SolarMutexGuard aGuard;
+ bool bLocked = false;
+ if (pDocShell)
+ bLocked = ( pDocShell->GetLockCount() != 0 );
+ return bLocked;
+}
+
+void SAL_CALL ScModelObj::addActionLock()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ pDocShell->LockDocument();
+}
+
+void SAL_CALL ScModelObj::removeActionLock()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ pDocShell->UnlockDocument();
+}
+
+void SAL_CALL ScModelObj::setActionLocks( sal_Int16 nLock )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ pDocShell->SetLockCount(nLock);
+}
+
+sal_Int16 SAL_CALL ScModelObj::resetActionLocks()
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nRet = 0;
+ if (pDocShell)
+ {
+ nRet = pDocShell->GetLockCount();
+ pDocShell->SetLockCount(0);
+ }
+ return nRet;
+}
+
+void SAL_CALL ScModelObj::lockControllers()
+{
+ SolarMutexGuard aGuard;
+ SfxBaseModel::lockControllers();
+ if (pDocShell)
+ pDocShell->LockPaint();
+}
+
+void SAL_CALL ScModelObj::unlockControllers()
+{
+ SolarMutexGuard aGuard;
+ if (hasControllersLocked())
+ {
+ SfxBaseModel::unlockControllers();
+ if (pDocShell)
+ pDocShell->UnlockPaint();
+ }
+}
+
+// XCalculate
+
+void SAL_CALL ScModelObj::calculate()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ comphelper::ProfileZone aZone("calculate");
+ pDocShell->DoRecalc(true);
+ }
+ else
+ {
+ OSL_FAIL("no DocShell"); //! throw exception?
+ }
+}
+
+void SAL_CALL ScModelObj::calculateAll()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ comphelper::ProfileZone aZone("calculateAll");
+ pDocShell->DoHardRecalc();
+ }
+ else
+ {
+ OSL_FAIL("no DocShell"); //! throw exception?
+ }
+}
+
+sal_Bool SAL_CALL ScModelObj::isAutomaticCalculationEnabled()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return pDocShell->GetDocument().GetAutoCalc();
+
+ OSL_FAIL("no DocShell"); //! throw exception?
+ return false;
+}
+
+void SAL_CALL ScModelObj::enableAutomaticCalculation( sal_Bool bEnabledIn )
+{
+ bool bEnabled(bEnabledIn);
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( rDoc.GetAutoCalc() != bEnabled )
+ {
+ rDoc.SetAutoCalc( bEnabled );
+ pDocShell->SetDocumentModified();
+ }
+ }
+ else
+ {
+ OSL_FAIL("no DocShell"); //! throw exception?
+ }
+}
+
+// XProtectable
+
+void SAL_CALL ScModelObj::protect( const OUString& aPassword )
+{
+ SolarMutexGuard aGuard;
+ // #i108245# if already protected, don't change anything
+ if ( pDocShell && !pDocShell->GetDocument().IsDocProtected() )
+ {
+ pDocShell->GetDocFunc().Protect( TABLEID_DOC, aPassword );
+ }
+}
+
+void SAL_CALL ScModelObj::unprotect( const OUString& aPassword )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ bool bDone = pDocShell->GetDocFunc().Unprotect( TABLEID_DOC, aPassword, true );
+ if (!bDone)
+ throw lang::IllegalArgumentException();
+ }
+}
+
+sal_Bool SAL_CALL ScModelObj::isProtected()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return pDocShell->GetDocument().IsDocProtected();
+
+ OSL_FAIL("no DocShell"); //! throw exception?
+ return false;
+}
+
+// XDrawPagesSupplier
+
+uno::Reference<drawing::XDrawPages> SAL_CALL ScModelObj::getDrawPages()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return new ScDrawPagesObj(pDocShell);
+
+ OSL_FAIL("no DocShell"); //! throw exception?
+ return nullptr;
+}
+
+// XGoalSeek
+
+sheet::GoalResult SAL_CALL ScModelObj::seekGoal(
+ const table::CellAddress& aFormulaPosition,
+ const table::CellAddress& aVariablePosition,
+ const OUString& aGoalValue )
+{
+ SolarMutexGuard aGuard;
+ sheet::GoalResult aResult;
+ aResult.Divergence = DBL_MAX; // not found
+ if (pDocShell)
+ {
+ weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
+ ScDocument& rDoc = pDocShell->GetDocument();
+ double fValue = 0.0;
+ bool bFound = rDoc.Solver(
+ static_cast<SCCOL>(aFormulaPosition.Column), static_cast<SCROW>(aFormulaPosition.Row), aFormulaPosition.Sheet,
+ static_cast<SCCOL>(aVariablePosition.Column), static_cast<SCROW>(aVariablePosition.Row), aVariablePosition.Sheet,
+ aGoalValue, fValue );
+ aResult.Result = fValue;
+ if (bFound)
+ aResult.Divergence = 0.0; //! this is a lie
+ }
+ return aResult;
+}
+
+// XConsolidatable
+
+uno::Reference<sheet::XConsolidationDescriptor> SAL_CALL ScModelObj::createConsolidationDescriptor(
+ sal_Bool bEmpty )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<ScConsolidationDescriptor> pNew = new ScConsolidationDescriptor;
+ if ( pDocShell && !bEmpty )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const ScConsolidateParam* pParam = rDoc.GetConsolidateDlgData();
+ if (pParam)
+ pNew->SetParam( *pParam );
+ }
+ return pNew;
+}
+
+void SAL_CALL ScModelObj::consolidate(
+ const uno::Reference<sheet::XConsolidationDescriptor>& xDescriptor )
+{
+ SolarMutexGuard aGuard;
+ // in theory, this could also be a different object, so use only
+ // public XConsolidationDescriptor interface to copy the data into
+ // ScConsolidationDescriptor object
+ //! but if this already is ScConsolidationDescriptor, do it directly via getImplementation?
+
+ rtl::Reference< ScConsolidationDescriptor > xImpl(new ScConsolidationDescriptor);
+ xImpl->setFunction( xDescriptor->getFunction() );
+ xImpl->setSources( xDescriptor->getSources() );
+ xImpl->setStartOutputPosition( xDescriptor->getStartOutputPosition() );
+ xImpl->setUseColumnHeaders( xDescriptor->getUseColumnHeaders() );
+ xImpl->setUseRowHeaders( xDescriptor->getUseRowHeaders() );
+ xImpl->setInsertLinks( xDescriptor->getInsertLinks() );
+
+ if (pDocShell)
+ {
+ const ScConsolidateParam& rParam = xImpl->GetParam();
+ pDocShell->DoConsolidate( rParam );
+ pDocShell->GetDocument().SetConsolidateDlgData( std::unique_ptr<ScConsolidateParam>(new ScConsolidateParam(rParam)) );
+ }
+}
+
+// XDocumentAuditing
+
+void SAL_CALL ScModelObj::refreshArrows()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ pDocShell->GetDocFunc().DetectiveRefresh();
+}
+
+// XViewDataSupplier
+uno::Reference< container::XIndexAccess > SAL_CALL ScModelObj::getViewData( )
+{
+ uno::Reference < container::XIndexAccess > xRet( SfxBaseModel::getViewData() );
+
+ if( !xRet.is() )
+ {
+ SolarMutexGuard aGuard;
+ if (pDocShell && pDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED)
+ {
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xCont = new comphelper::IndexedPropertyValuesContainer();
+ xRet = xCont;
+
+ OUString sName;
+ pDocShell->GetDocument().GetName( pDocShell->GetDocument().GetVisibleTab(), sName );
+ SCCOL nPosLeft = pDocShell->GetDocument().GetPosLeft();
+ SCROW nPosTop = pDocShell->GetDocument().GetPosTop();
+ uno::Sequence< beans::PropertyValue > aSeq{
+ comphelper::makePropertyValue(SC_ACTIVETABLE, sName),
+ comphelper::makePropertyValue(SC_POSITIONLEFT, nPosLeft),
+ comphelper::makePropertyValue(SC_POSITIONTOP, nPosTop)
+ };
+ xCont->insertByIndex( 0, uno::Any( aSeq ) );
+ }
+ }
+
+ return xRet;
+}
+
+// XPropertySet (Doc-Options)
+//! provide them also to the application?
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScModelObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScModelObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const ScDocOptions& rOldOpt = rDoc.GetDocOptions();
+ ScDocOptions aNewOpt = rOldOpt;
+ // Don't recalculate while loading XML, when the formula text is stored
+ // Recalculation after loading is handled separately.
+ bool bHardRecalc = !rDoc.IsImportingXML();
+
+ bool bOpt = ScDocOptionsHelper::setPropertyValue( aNewOpt, aPropSet.getPropertyMap(), aPropertyName, aValue );
+ if (bOpt)
+ {
+ // done...
+ if ( aPropertyName == SC_UNO_IGNORECASE ||
+ aPropertyName == SC_UNONAME_REGEXP ||
+ aPropertyName == SC_UNONAME_WILDCARDS ||
+ aPropertyName == SC_UNO_LOOKUPLABELS )
+ bHardRecalc = false;
+ }
+ else if ( aPropertyName == SC_UNONAME_CLOCAL )
+ {
+ lang::Locale aLocale;
+ if ( aValue >>= aLocale )
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ eLatin = ScUnoConversion::GetLanguage(aLocale);
+ rDoc.SetLanguage( eLatin, eCjk, eCtl );
+ }
+ }
+ else if ( aPropertyName == SC_UNO_CODENAME )
+ {
+ OUString sCodeName;
+ if ( aValue >>= sCodeName )
+ rDoc.SetCodeName( sCodeName );
+ }
+ else if ( aPropertyName == SC_UNO_CJK_CLOCAL )
+ {
+ lang::Locale aLocale;
+ if ( aValue >>= aLocale )
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ eCjk = ScUnoConversion::GetLanguage(aLocale);
+ rDoc.SetLanguage( eLatin, eCjk, eCtl );
+ }
+ }
+ else if ( aPropertyName == SC_UNO_CTL_CLOCAL )
+ {
+ lang::Locale aLocale;
+ if ( aValue >>= aLocale )
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ eCtl = ScUnoConversion::GetLanguage(aLocale);
+ rDoc.SetLanguage( eLatin, eCjk, eCtl );
+ }
+ }
+ else if ( aPropertyName == SC_UNO_APPLYFMDES )
+ {
+ // model is created if not there
+ ScDrawLayer* pModel = pDocShell->MakeDrawLayer();
+ pModel->SetOpenInDesignMode( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+
+ SfxBindings* pBindings = pDocShell->GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_FM_OPEN_READONLY );
+ }
+ else if ( aPropertyName == SC_UNO_AUTOCONTFOC )
+ {
+ // model is created if not there
+ ScDrawLayer* pModel = pDocShell->MakeDrawLayer();
+ pModel->SetAutoControlFocus( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+
+ SfxBindings* pBindings = pDocShell->GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_FM_AUTOCONTROLFOCUS );
+ }
+ else if ( aPropertyName == SC_UNO_ISLOADED )
+ {
+ pDocShell->SetEmpty( !ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ }
+ else if ( aPropertyName == SC_UNO_ISUNDOENABLED )
+ {
+ bool bUndoEnabled = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ rDoc.EnableUndo( bUndoEnabled );
+ pDocShell->GetUndoManager()->SetMaxUndoActionCount(
+ bUndoEnabled
+ ? officecfg::Office::Common::Undo::Steps::get() : 0);
+ }
+ else if ( aPropertyName == SC_UNO_RECORDCHANGES )
+ {
+ bool bRecordChangesEnabled = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+
+ bool bChangeAllowed = true;
+ if (!bRecordChangesEnabled)
+ bChangeAllowed = !pDocShell->HasChangeRecordProtection();
+
+ if (bChangeAllowed)
+ pDocShell->SetChangeRecording(bRecordChangesEnabled);
+ }
+ else if ( aPropertyName == SC_UNO_ISADJUSTHEIGHTENABLED )
+ {
+ if( ScUnoHelpFunctions::GetBoolFromAny( aValue ) )
+ rDoc.UnlockAdjustHeight();
+ else
+ rDoc.LockAdjustHeight();
+ }
+ else if ( aPropertyName == SC_UNO_ISEXECUTELINKENABLED )
+ {
+ rDoc.EnableExecuteLink( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ }
+ else if ( aPropertyName == SC_UNO_ISCHANGEREADONLYENABLED )
+ {
+ rDoc.EnableChangeReadOnly( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ }
+ else if ( aPropertyName == "BuildId" )
+ {
+ aValue >>= maBuildId;
+ }
+ else if ( aPropertyName == "SavedObject" ) // set from chart after saving
+ {
+ OUString aObjName;
+ aValue >>= aObjName;
+ if ( !aObjName.isEmpty() )
+ rDoc.RestoreChartListener( aObjName );
+ }
+ else if ( aPropertyName == SC_UNO_INTEROPGRABBAG )
+ {
+ setGrabBagItem(aValue);
+ }
+ else if (aPropertyName == SC_UNO_THEME)
+ {
+ SdrModel& rSdrModel = getSdrModelFromUnoModel();
+ uno::Reference<util::XTheme> xTheme;
+ if (aValue >>= xTheme)
+ {
+ auto& rUnoTheme = dynamic_cast<UnoTheme&>(*xTheme);
+ rSdrModel.setTheme(rUnoTheme.getTheme());
+ }
+ }
+
+ if ( aNewOpt != rOldOpt )
+ {
+ rDoc.SetDocOptions( aNewOpt );
+ //! Recalc only for options that need it?
+ if ( bHardRecalc )
+ pDocShell->DoHardRecalc();
+ pDocShell->SetDocumentModified();
+ }
+}
+
+uno::Any SAL_CALL ScModelObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ const ScDocOptions& rOpt = rDoc.GetDocOptions();
+ aRet = ScDocOptionsHelper::getPropertyValue( rOpt, aPropSet.getPropertyMap(), aPropertyName );
+ if ( aRet.hasValue() )
+ {
+ // done...
+ }
+ else if ( aPropertyName == SC_UNONAME_CLOCAL )
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+
+ lang::Locale aLocale;
+ ScUnoConversion::FillLocale( aLocale, eLatin );
+ aRet <<= aLocale;
+ }
+ else if ( aPropertyName == SC_UNO_CODENAME )
+ {
+ aRet <<= rDoc.GetCodeName();
+ }
+
+ else if ( aPropertyName == SC_UNO_CJK_CLOCAL )
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+
+ lang::Locale aLocale;
+ ScUnoConversion::FillLocale( aLocale, eCjk );
+ aRet <<= aLocale;
+ }
+ else if ( aPropertyName == SC_UNO_CTL_CLOCAL )
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+
+ lang::Locale aLocale;
+ ScUnoConversion::FillLocale( aLocale, eCtl );
+ aRet <<= aLocale;
+ }
+ else if ( aPropertyName == SC_UNO_NAMEDRANGES )
+ {
+ aRet <<= uno::Reference<sheet::XNamedRanges>(new ScGlobalNamedRangesObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_DATABASERNG )
+ {
+ aRet <<= uno::Reference<sheet::XDatabaseRanges>(new ScDatabaseRangesObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_UNNAMEDDBRNG )
+ {
+ aRet <<= uno::Reference<sheet::XUnnamedDatabaseRanges>(new ScUnnamedDatabaseRangesObj(pDocShell));
+ }
+ else if ( aPropertyName == SC_UNO_COLLABELRNG )
+ {
+ aRet <<= uno::Reference<sheet::XLabelRanges>(new ScLabelRangesObj( pDocShell, true ));
+ }
+ else if ( aPropertyName == SC_UNO_ROWLABELRNG )
+ {
+ aRet <<= uno::Reference<sheet::XLabelRanges>(new ScLabelRangesObj( pDocShell, false ));
+ }
+ else if ( aPropertyName == SC_UNO_AREALINKS )
+ {
+ aRet <<= uno::Reference<sheet::XAreaLinks>(new ScAreaLinksObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_DDELINKS )
+ {
+ aRet <<= uno::Reference<container::XNameAccess>(new ScDDELinksObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_EXTERNALDOCLINKS )
+ {
+ aRet <<= uno::Reference<sheet::XExternalDocLinks>(new ScExternalDocLinksObj(pDocShell));
+ }
+ else if ( aPropertyName == SC_UNO_SHEETLINKS )
+ {
+ aRet <<= uno::Reference<container::XNameAccess>(new ScSheetLinksObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_APPLYFMDES )
+ {
+ // default for no model is TRUE
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ bool bOpenInDesign = pModel == nullptr || pModel->GetOpenInDesignMode();
+ aRet <<= bOpenInDesign;
+ }
+ else if ( aPropertyName == SC_UNO_AUTOCONTFOC )
+ {
+ // default for no model is FALSE
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ bool bAutoControlFocus = pModel && pModel->GetAutoControlFocus();
+ aRet <<= bAutoControlFocus;
+ }
+ else if ( aPropertyName == SC_UNO_FORBIDDEN )
+ {
+ aRet <<= uno::Reference<i18n::XForbiddenCharacters>(new ScForbiddenCharsObj( pDocShell ));
+ }
+ else if ( aPropertyName == SC_UNO_HASDRAWPAGES )
+ {
+ aRet <<= (pDocShell->GetDocument().GetDrawLayer() != nullptr);
+ }
+ else if ( aPropertyName == SC_UNO_BASICLIBRARIES )
+ {
+ aRet <<= pDocShell->GetBasicContainer();
+ }
+ else if ( aPropertyName == SC_UNO_DIALOGLIBRARIES )
+ {
+ aRet <<= pDocShell->GetDialogContainer();
+ }
+ else if ( aPropertyName == SC_UNO_VBAGLOBNAME )
+ {
+ /* #i111553# This property provides the name of the constant that
+ will be used to store this model in the global Basic manager.
+ That constant will be equivalent to 'ThisComponent' but for
+ each application, so e.g. a 'ThisExcelDoc' and a 'ThisWordDoc'
+ constant can co-exist, as required by VBA. */
+ aRet <<= OUString( "ThisExcelDoc" );
+ }
+ else if ( aPropertyName == SC_UNO_RUNTIMEUID )
+ {
+ aRet <<= getRuntimeUID();
+ }
+ else if ( aPropertyName == SC_UNO_HASVALIDSIGNATURES )
+ {
+ aRet <<= hasValidSignatures();
+ }
+ else if ( aPropertyName == SC_UNO_ISLOADED )
+ {
+ aRet <<= !pDocShell->IsEmpty();
+ }
+ else if ( aPropertyName == SC_UNO_ISUNDOENABLED )
+ {
+ aRet <<= rDoc.IsUndoEnabled();
+ }
+ else if ( aPropertyName == SC_UNO_RECORDCHANGES )
+ {
+ aRet <<= pDocShell->IsChangeRecording();
+ }
+ else if ( aPropertyName == SC_UNO_ISRECORDCHANGESPROTECTED )
+ {
+ aRet <<= pDocShell->HasChangeRecordProtection();
+ }
+ else if ( aPropertyName == SC_UNO_ISADJUSTHEIGHTENABLED )
+ {
+ aRet <<= !( rDoc.IsAdjustHeightLocked() );
+ }
+ else if ( aPropertyName == SC_UNO_ISEXECUTELINKENABLED )
+ {
+ aRet <<= rDoc.IsExecuteLinkEnabled();
+ }
+ else if ( aPropertyName == SC_UNO_ISCHANGEREADONLYENABLED )
+ {
+ aRet <<= rDoc.IsChangeReadOnlyEnabled();
+ }
+ else if ( aPropertyName == SC_UNO_REFERENCEDEVICE )
+ {
+ rtl::Reference<VCLXDevice> pXDev = new VCLXDevice();
+ pXDev->SetOutputDevice( rDoc.GetRefDevice() );
+ aRet <<= uno::Reference< awt::XDevice >( pXDev );
+ }
+ else if ( aPropertyName == "BuildId" )
+ {
+ aRet <<= maBuildId;
+ }
+ else if ( aPropertyName == "InternalDocument" )
+ {
+ aRet <<= (pDocShell->GetCreateMode() == SfxObjectCreateMode::INTERNAL);
+ }
+ else if ( aPropertyName == SC_UNO_INTEROPGRABBAG )
+ {
+ getGrabBagItem(aRet);
+ }
+ else if (aPropertyName == SC_UNO_THEME)
+ {
+ SdrModel& rSdrModel = getSdrModelFromUnoModel();
+ css::uno::Reference<css::util::XTheme> xTheme;
+ auto pTheme = rSdrModel.getTheme();
+ if (pTheme)
+ xTheme = model::theme::createXTheme(pTheme);
+ aRet <<= xTheme;
+ }
+ }
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScModelObj )
+
+// XMultiServiceFactory
+
+css::uno::Reference<css::uno::XInterface> ScModelObj::create(
+ OUString const & aServiceSpecifier,
+ css::uno::Sequence<css::uno::Any> const * arguments)
+{
+ using ServiceType = ScServiceProvider::Type;
+
+ uno::Reference<uno::XInterface> xRet;
+ ServiceType nType = ScServiceProvider::GetProviderType(aServiceSpecifier);
+ if ( nType != ServiceType::INVALID )
+ {
+ // drawing layer tables must be kept as long as the model is alive
+ // return stored instance if already set
+ switch ( nType )
+ {
+ case ServiceType::GRADTAB: xRet.set(xDrawGradTab); break;
+ case ServiceType::HATCHTAB: xRet.set(xDrawHatchTab); break;
+ case ServiceType::BITMAPTAB: xRet.set(xDrawBitmapTab); break;
+ case ServiceType::TRGRADTAB: xRet.set(xDrawTrGradTab); break;
+ case ServiceType::MARKERTAB: xRet.set(xDrawMarkerTab); break;
+ case ServiceType::DASHTAB: xRet.set(xDrawDashTab); break;
+ case ServiceType::CHDATAPROV: xRet.set(xChartDataProv); break;
+ case ServiceType::VBAOBJECTPROVIDER: xRet.set(xObjProvider); break;
+ default: break;
+ }
+
+ // #i64497# If a chart is in a temporary document during clipboard paste,
+ // there should be no data provider, so that own data is used
+ bool bCreate =
+ ( nType != ServiceType::CHDATAPROV ||
+ ( pDocShell->GetCreateMode() != SfxObjectCreateMode::INTERNAL ));
+ // this should never happen, i.e. the temporary document should never be
+ // loaded, because this unlinks the data
+ assert(bCreate);
+
+ if ( !xRet.is() && bCreate )
+ {
+ xRet.set(ScServiceProvider::MakeInstance( nType, pDocShell ));
+
+ // store created instance
+ switch ( nType )
+ {
+ case ServiceType::GRADTAB: xDrawGradTab.set(xRet); break;
+ case ServiceType::HATCHTAB: xDrawHatchTab.set(xRet); break;
+ case ServiceType::BITMAPTAB: xDrawBitmapTab.set(xRet); break;
+ case ServiceType::TRGRADTAB: xDrawTrGradTab.set(xRet); break;
+ case ServiceType::MARKERTAB: xDrawMarkerTab.set(xRet); break;
+ case ServiceType::DASHTAB: xDrawDashTab.set(xRet); break;
+ case ServiceType::CHDATAPROV: xChartDataProv.set(xRet); break;
+ case ServiceType::VBAOBJECTPROVIDER: xObjProvider.set(xRet); break;
+ default: break;
+ }
+ }
+ }
+ else
+ {
+ // we offload everything we don't know to SvxFmMSFactory,
+ // it'll throw exception if this isn't okay ...
+
+ try
+ {
+ xRet = arguments == nullptr
+ ? SvxFmMSFactory::createInstance(aServiceSpecifier)
+ : SvxFmMSFactory::createInstanceWithArguments(
+ aServiceSpecifier, *arguments);
+ // extra block to force deletion of the temporary before ScShapeObj ctor (setDelegator)
+ }
+ catch ( lang::ServiceNotRegisteredException & )
+ {
+ }
+
+ // if the drawing factory created a shape, a ScShapeObj has to be used
+ // to support own properties like ImageMap:
+
+ uno::Reference<drawing::XShape> xShape( xRet, uno::UNO_QUERY );
+ if ( xShape.is() )
+ {
+ xRet.clear(); // for aggregation, xShape must be the object's only ref
+ new ScShapeObj( xShape ); // aggregates object and modifies xShape
+ xRet.set(xShape);
+ }
+ }
+ return xRet;
+}
+
+uno::Reference<uno::XInterface> SAL_CALL ScModelObj::createInstance(
+ const OUString& aServiceSpecifier )
+{
+ SolarMutexGuard aGuard;
+ return create(aServiceSpecifier, nullptr);
+}
+
+uno::Reference<uno::XInterface> SAL_CALL ScModelObj::createInstanceWithArguments(
+ const OUString& ServiceSpecifier,
+ const uno::Sequence<uno::Any>& aArgs )
+{
+ //! distinguish between own services and those of drawing layer?
+
+ SolarMutexGuard aGuard;
+ uno::Reference<uno::XInterface> xInt(create(ServiceSpecifier, &aArgs));
+
+ if ( aArgs.hasElements() )
+ {
+ // used only for cell value binding so far - it can be initialized after creating
+
+ uno::Reference<lang::XInitialization> xInit( xInt, uno::UNO_QUERY );
+ if ( xInit.is() )
+ xInit->initialize( aArgs );
+ }
+
+ return xInt;
+}
+
+uno::Sequence<OUString> SAL_CALL ScModelObj::getAvailableServiceNames()
+{
+ SolarMutexGuard aGuard;
+
+ return comphelper::concatSequences( ScServiceProvider::GetAllServiceNames(),
+ SvxFmMSFactory::getAvailableServiceNames() );
+}
+
+// XServiceInfo
+OUString SAL_CALL ScModelObj::getImplementationName()
+{
+ return "ScModelObj";
+ /* // Matching the .component information:
+ return OUString( "com.sun.star.comp.Calc.SpreadsheetDocument" );
+ */
+}
+
+sal_Bool SAL_CALL ScModelObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScModelObj::getSupportedServiceNames()
+{
+ return {SCMODELOBJ_SERVICE, SCDOCSETTINGS_SERVICE, SCDOC_SERVICE};
+}
+
+// XUnoTunnel
+
+sal_Int64 SAL_CALL ScModelObj::getSomething(
+ const uno::Sequence<sal_Int8 >& rId )
+{
+ if ( comphelper::isUnoTunnelId<ScModelObj>(rId) )
+ {
+ return comphelper::getSomething_cast(this);
+ }
+
+ if ( comphelper::isUnoTunnelId<SfxObjectShell>(rId) )
+ {
+ return comphelper::getSomething_cast(pDocShell);
+ }
+
+ // aggregated number formats supplier has XUnoTunnel, too
+ // interface from aggregated object must be obtained via queryAggregation
+
+ sal_Int64 nRet = SfxBaseModel::getSomething( rId );
+ if ( nRet )
+ return nRet;
+
+ if ( GetFormatter().is() )
+ {
+ const uno::Type& rTunnelType = cppu::UnoType<lang::XUnoTunnel>::get();
+ uno::Any aNumTunnel(xNumberAgg->queryAggregation(rTunnelType));
+ if(auto xTunnelAgg = o3tl::tryAccess<uno::Reference<lang::XUnoTunnel>>(
+ aNumTunnel))
+ {
+ return (*xTunnelAgg)->getSomething( rId );
+ }
+ }
+
+ return 0;
+}
+
+const uno::Sequence<sal_Int8>& ScModelObj::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theScModelObjUnoTunnelId;
+ return theScModelObjUnoTunnelId.getSeq();
+}
+
+// XChangesNotifier
+
+void ScModelObj::addChangesListener( const uno::Reference< util::XChangesListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+ maChangesListeners.addInterface( aListener );
+}
+
+void ScModelObj::removeChangesListener( const uno::Reference< util::XChangesListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+ maChangesListeners.removeInterface( aListener );
+}
+
+bool ScModelObj::HasChangesListeners() const
+{
+ if ( maChangesListeners.getLength() > 0 )
+ return true;
+
+ // "change" event set in any sheet?
+ return pDocShell && pDocShell->GetDocument().HasAnySheetEventScript(ScSheetEventId::CHANGE);
+}
+
+namespace
+{
+
+void lcl_dataAreaInvalidation(ScModelObj* pModel,
+ const ScRangeList& rRanges,
+ bool bInvalidateDataArea, bool bExtendDataArea)
+{
+ size_t nRangeCount = rRanges.size();
+
+ for ( size_t nIndex = 0; nIndex < nRangeCount; ++nIndex )
+ {
+ ScRange const & rRange = rRanges[ nIndex ];
+ ScAddress const & rEnd = rRange.aEnd;
+ SCTAB nTab = rEnd.Tab();
+
+ bool bAreaExtended = false;
+
+ if (bExtendDataArea)
+ {
+ const Size aCurrentDataArea = pModel->getDataArea( nTab );
+
+ SCCOL nLastCol = aCurrentDataArea.Width();
+ SCROW nLastRow = aCurrentDataArea.Height();
+
+ bAreaExtended = rEnd.Col() > nLastCol || rEnd.Row() > nLastRow;
+ }
+
+ bool bInvalidate = bAreaExtended || bInvalidateDataArea;
+ if ( bInvalidate )
+ {
+ if ( comphelper::LibreOfficeKit::isActive() )
+ SfxLokHelper::notifyPartSizeChangedAllViews( pModel, nTab );
+ }
+ }
+}
+
+};
+
+void ScModelObj::NotifyChanges( const OUString& rOperation, const ScRangeList& rRanges,
+ const uno::Sequence< beans::PropertyValue >& rProperties )
+{
+ OUString aOperation = rOperation;
+ bool bIsDataAreaInvalidateType = aOperation == "data-area-invalidate";
+ bool bIsDataAreaExtendType = aOperation == "data-area-extend";
+
+ bool bInvalidateDataArea = bIsDataAreaInvalidateType
+ || HelperNotifyChanges::isDataAreaInvalidateType(aOperation);
+ bool bExtendDataArea = bIsDataAreaExtendType || aOperation == "cell-change";
+
+ if ( pDocShell )
+ {
+ lcl_dataAreaInvalidation(this, rRanges, bInvalidateDataArea, bExtendDataArea);
+
+ // check if we were called only to update data area
+ if (bIsDataAreaInvalidateType || bIsDataAreaExtendType)
+ return;
+
+ // backward-compatibility Operation conversion
+ // FIXME: make sure it can be passed
+ if (rOperation == "delete-content" || rOperation == "undo"
+ || rOperation == "redo" || rOperation == "paste")
+ aOperation = "cell-change";
+ }
+
+ if ( pDocShell && HasChangesListeners() )
+ {
+ util::ChangesEvent aEvent;
+ aEvent.Source.set(getXWeak());
+ aEvent.Base <<= aEvent.Source;
+
+ size_t nRangeCount = rRanges.size();
+ aEvent.Changes.realloc( static_cast< sal_Int32 >( nRangeCount ) );
+ auto pChanges = aEvent.Changes.getArray();
+ for ( size_t nIndex = 0; nIndex < nRangeCount; ++nIndex )
+ {
+ uno::Reference< table::XCellRange > xRangeObj;
+
+ ScRange const & rRange = rRanges[ nIndex ];
+ if ( rRange.aStart == rRange.aEnd )
+ {
+ xRangeObj.set( new ScCellObj( pDocShell, rRange.aStart ) );
+ }
+ else
+ {
+ xRangeObj.set( new ScCellRangeObj( pDocShell, rRange ) );
+ }
+
+ util::ElementChange& rChange = pChanges[ static_cast< sal_Int32 >( nIndex ) ];
+ rChange.Accessor <<= aOperation;
+ rChange.Element <<= rProperties;
+ rChange.ReplacedElement <<= xRangeObj;
+ }
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter( maChangesListeners );
+ while ( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->changesOccurred( aEvent );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ // handle sheet events
+ //! separate method with ScMarkData? Then change HasChangesListeners back.
+ if ( !(aOperation == "cell-change" && pDocShell) )
+ return;
+
+ ScMarkData aMarkData(pDocShell->GetDocument().GetSheetLimits());
+ aMarkData.MarkFromRangeList( rRanges, false );
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const SCTAB& nTab : aMarkData)
+ {
+ if (nTab >= nTabCount)
+ break;
+ const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab);
+ if (pEvents)
+ {
+ const OUString* pScript = pEvents->GetScript(ScSheetEventId::CHANGE);
+ if (pScript)
+ {
+ ScRangeList aTabRanges; // collect ranges on this sheet
+ size_t nRangeCount = rRanges.size();
+ for ( size_t nIndex = 0; nIndex < nRangeCount; ++nIndex )
+ {
+ ScRange const & rRange = rRanges[ nIndex ];
+ if ( rRange.aStart.Tab() == nTab )
+ aTabRanges.push_back( rRange );
+ }
+ size_t nTabRangeCount = aTabRanges.size();
+ if ( nTabRangeCount > 0 )
+ {
+ uno::Reference<uno::XInterface> xTarget;
+ if ( nTabRangeCount == 1 )
+ {
+ ScRange const & rRange = aTabRanges[ 0 ];
+ if ( rRange.aStart == rRange.aEnd )
+ xTarget.set( cppu::getXWeak( new ScCellObj( pDocShell, rRange.aStart ) ) );
+ else
+ xTarget.set( cppu::getXWeak( new ScCellRangeObj( pDocShell, rRange ) ) );
+ }
+ else
+ xTarget.set( cppu::getXWeak( new ScCellRangesObj( pDocShell, aTabRanges ) ) );
+
+ uno::Sequence<uno::Any> aParams{ uno::Any(xTarget) };
+
+ uno::Any aRet;
+ uno::Sequence<sal_Int16> aOutArgsIndex;
+ uno::Sequence<uno::Any> aOutArgs;
+
+ /*ErrCode eRet =*/ pDocShell->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs );
+ }
+ }
+ }
+ }
+}
+
+void ScModelObj::HandleCalculateEvents()
+{
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ // don't call events before the document is visible
+ // (might also set a flag on SfxEventHintId::LoadFinished and only disable while loading)
+ if ( rDoc.IsDocVisible() )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab = 0; nTab < nTabCount; nTab++)
+ {
+ if (rDoc.HasCalcNotification(nTab))
+ {
+ if (const ScSheetEvents* pEvents = rDoc.GetSheetEvents( nTab ))
+ {
+ if (const OUString* pScript = pEvents->GetScript(ScSheetEventId::CALCULATE))
+ {
+ uno::Any aRet;
+ uno::Sequence<uno::Any> aParams;
+ uno::Sequence<sal_Int16> aOutArgsIndex;
+ uno::Sequence<uno::Any> aOutArgs;
+ pDocShell->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs );
+ }
+ }
+
+ try
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(nTab) };
+ xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( ScSheetEventId::CALCULATE ), aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+ rDoc.ResetCalcNotifications();
+}
+
+// XOpenCLSelection
+
+sal_Bool ScModelObj::isOpenCLEnabled()
+{
+ return ScCalcConfig::isOpenCLEnabled();
+}
+
+void ScModelObj::enableOpenCL(sal_Bool bEnable)
+{
+ if (ScCalcConfig::isOpenCLEnabled() == static_cast<bool>(bEnable))
+ return;
+ if (ScCalcConfig::getForceCalculationType() != ForceCalculationNone)
+ return;
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::UseOpenCL::set(bEnable, batch);
+ batch->commit();
+
+ ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig();
+ if (bEnable)
+ aConfig.setOpenCLConfigToDefault();
+ ScInterpreter::SetGlobalConfig(aConfig);
+
+#if HAVE_FEATURE_OPENCL
+ sc::FormulaGroupInterpreter::switchOpenCLDevice(u"", true);
+#endif
+
+ ScDocument* pDoc = GetDocument();
+ pDoc->CheckVectorizationState();
+
+}
+
+void ScModelObj::enableAutomaticDeviceSelection(sal_Bool bForce)
+{
+ ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig();
+ aConfig.mbOpenCLAutoSelect = true;
+ ScInterpreter::SetGlobalConfig(aConfig);
+ ScFormulaOptions aOptions = SC_MOD()->GetFormulaOptions();
+ aOptions.SetCalcConfig(aConfig);
+ SC_MOD()->SetFormulaOptions(aOptions);
+#if !HAVE_FEATURE_OPENCL
+ (void) bForce;
+#else
+ sc::FormulaGroupInterpreter::switchOpenCLDevice(u"", true, bForce);
+#endif
+}
+
+void ScModelObj::disableAutomaticDeviceSelection()
+{
+ ScCalcConfig aConfig = ScInterpreter::GetGlobalConfig();
+ aConfig.mbOpenCLAutoSelect = false;
+ ScInterpreter::SetGlobalConfig(aConfig);
+ ScFormulaOptions aOptions = SC_MOD()->GetFormulaOptions();
+ aOptions.SetCalcConfig(aConfig);
+ SC_MOD()->SetFormulaOptions(aOptions);
+}
+
+void ScModelObj::selectOpenCLDevice( sal_Int32 nPlatform, sal_Int32 nDevice )
+{
+ if(nPlatform < 0 || nDevice < 0)
+ throw uno::RuntimeException();
+
+#if !HAVE_FEATURE_OPENCL
+ throw uno::RuntimeException();
+#else
+ std::vector<OpenCLPlatformInfo> aPlatformInfo;
+ sc::FormulaGroupInterpreter::fillOpenCLInfo(aPlatformInfo);
+ if(o3tl::make_unsigned(nPlatform) >= aPlatformInfo.size())
+ throw uno::RuntimeException();
+
+ if(o3tl::make_unsigned(nDevice) >= aPlatformInfo[nPlatform].maDevices.size())
+ throw uno::RuntimeException();
+
+ OUString aDeviceString = aPlatformInfo[nPlatform].maVendor + " " + aPlatformInfo[nPlatform].maDevices[nDevice].maName;
+ sc::FormulaGroupInterpreter::switchOpenCLDevice(aDeviceString, false);
+#endif
+}
+
+sal_Int32 ScModelObj::getPlatformID()
+{
+#if !HAVE_FEATURE_OPENCL
+ return -1;
+#else
+ sal_Int32 nPlatformId;
+ sal_Int32 nDeviceId;
+ sc::FormulaGroupInterpreter::getOpenCLDeviceInfo(nDeviceId, nPlatformId);
+ return nPlatformId;
+#endif
+}
+
+sal_Int32 ScModelObj::getDeviceID()
+{
+#if !HAVE_FEATURE_OPENCL
+ return -1;
+#else
+ sal_Int32 nPlatformId;
+ sal_Int32 nDeviceId;
+ sc::FormulaGroupInterpreter::getOpenCLDeviceInfo(nDeviceId, nPlatformId);
+ return nDeviceId;
+#endif
+}
+
+uno::Sequence< sheet::opencl::OpenCLPlatform > ScModelObj::getOpenCLPlatforms()
+{
+#if !HAVE_FEATURE_OPENCL
+ return uno::Sequence<sheet::opencl::OpenCLPlatform>();
+#else
+ std::vector<OpenCLPlatformInfo> aPlatformInfo;
+ sc::FormulaGroupInterpreter::fillOpenCLInfo(aPlatformInfo);
+
+ uno::Sequence<sheet::opencl::OpenCLPlatform> aRet(aPlatformInfo.size());
+ auto aRetRange = asNonConstRange(aRet);
+ for(size_t i = 0; i < aPlatformInfo.size(); ++i)
+ {
+ aRetRange[i].Name = aPlatformInfo[i].maName;
+ aRetRange[i].Vendor = aPlatformInfo[i].maVendor;
+
+ aRetRange[i].Devices.realloc(aPlatformInfo[i].maDevices.size());
+ auto pDevices = aRetRange[i].Devices.getArray();
+ for(size_t j = 0; j < aPlatformInfo[i].maDevices.size(); ++j)
+ {
+ const OpenCLDeviceInfo& rDevice = aPlatformInfo[i].maDevices[j];
+ pDevices[j].Name = rDevice.maName;
+ pDevices[j].Vendor = rDevice.maVendor;
+ pDevices[j].Driver = rDevice.maDriver;
+ }
+ }
+
+ return aRet;
+#endif
+}
+
+namespace {
+
+/// @throws css::uno::RuntimeException
+void setOpcodeSubsetTest(bool bFlag)
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::OpenCLSubsetOnly::set(bFlag, batch);
+ batch->commit();
+}
+
+}
+
+void ScModelObj::enableOpcodeSubsetTest()
+{
+ setOpcodeSubsetTest(true);
+}
+
+void ScModelObj::disableOpcodeSubsetTest()
+{
+ setOpcodeSubsetTest(false);
+}
+
+sal_Bool ScModelObj::isOpcodeSubsetTested()
+{
+ return officecfg::Office::Calc::Formula::Calculation::OpenCLSubsetOnly::get();
+}
+
+void ScModelObj::setFormulaCellNumberLimit( sal_Int32 number )
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::set(number, batch);
+ batch->commit();
+}
+
+sal_Int32 ScModelObj::getFormulaCellNumberLimit()
+{
+ return officecfg::Office::Calc::Formula::Calculation::OpenCLMinimumDataSize::get();
+}
+
+ScDrawPagesObj::ScDrawPagesObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDrawPagesObj::~ScDrawPagesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDrawPagesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // we don't care about update of references here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+uno::Reference<drawing::XDrawPage> ScDrawPagesObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const
+{
+ if (pDocShell)
+ {
+ ScDrawLayer* pDrawLayer = pDocShell->MakeDrawLayer();
+ OSL_ENSURE(pDrawLayer,"Cannot create Draw-Layer");
+ if ( pDrawLayer && nIndex >= 0 && nIndex < pDocShell->GetDocument().GetTableCount() )
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nIndex));
+ OSL_ENSURE(pPage,"Draw-Page not found");
+ if (pPage)
+ {
+ return uno::Reference<drawing::XDrawPage> (pPage->getUnoPage(), uno::UNO_QUERY);
+ }
+ }
+ }
+ return nullptr;
+}
+
+// XDrawPages
+
+uno::Reference<drawing::XDrawPage> SAL_CALL ScDrawPagesObj::insertNewByIndex( sal_Int32 nPos )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<drawing::XDrawPage> xRet;
+ if (pDocShell)
+ {
+ OUString aNewName;
+ pDocShell->GetDocument().CreateValidTabName(aNewName);
+ if ( pDocShell->GetDocFunc().InsertTable( static_cast<SCTAB>(nPos),
+ aNewName, true, true ) )
+ xRet.set(GetObjectByIndex_Impl( nPos ));
+ }
+ return xRet;
+}
+
+void SAL_CALL ScDrawPagesObj::remove( const uno::Reference<drawing::XDrawPage>& xPage )
+{
+ SolarMutexGuard aGuard;
+ SvxDrawPage* pImp = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage );
+ if ( pDocShell && pImp )
+ {
+ SdrPage* pPage = pImp->GetSdrPage();
+ if (pPage)
+ {
+ SCTAB nPageNum = static_cast<SCTAB>(pPage->GetPageNum());
+ pDocShell->GetDocFunc().DeleteTable( nPageNum, true );
+ }
+ }
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDrawPagesObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return pDocShell->GetDocument().GetTableCount();
+ return 0;
+}
+
+uno::Any SAL_CALL ScDrawPagesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<drawing::XDrawPage> xPage(GetObjectByIndex_Impl(nIndex));
+ if (!xPage.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xPage);
+}
+
+uno::Type SAL_CALL ScDrawPagesObj::getElementType()
+{
+ return cppu::UnoType<drawing::XDrawPage>::get();
+}
+
+sal_Bool SAL_CALL ScDrawPagesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+ScTableSheetsObj::ScTableSheetsObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScTableSheetsObj::~ScTableSheetsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScTableSheetsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // we don't care about update of references here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XSpreadsheets
+
+rtl::Reference<ScTableSheetObj> ScTableSheetsObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const
+{
+ if ( pDocShell && nIndex >= 0 && nIndex < pDocShell->GetDocument().GetTableCount() )
+ return new ScTableSheetObj( pDocShell, static_cast<SCTAB>(nIndex) );
+
+ return nullptr;
+}
+
+rtl::Reference<ScTableSheetObj> ScTableSheetsObj::GetObjectByName_Impl(const OUString& aName) const
+{
+ if (pDocShell)
+ {
+ SCTAB nIndex;
+ if ( pDocShell->GetDocument().GetTable( aName, nIndex ) )
+ return new ScTableSheetObj( pDocShell, nIndex );
+ }
+ return nullptr;
+}
+
+void SAL_CALL ScTableSheetsObj::insertNewByName( const OUString& aName, sal_Int16 nPosition )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ bDone = pDocShell->GetDocFunc().InsertTable( nPosition, aName, true, true );
+ }
+ if (!bDone)
+ throw uno::RuntimeException("ScTableSheetsObj::insertNewByName(): Illegal object name or bad index. Duplicate name?"); // no other exceptions specified
+}
+
+void SAL_CALL ScTableSheetsObj::moveByName( const OUString& aName, sal_Int16 nDestination )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ SCTAB nSource;
+ if ( pDocShell->GetDocument().GetTable( aName, nSource ) )
+ bDone = pDocShell->MoveTable( nSource, nDestination, false, true );
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScTableSheetsObj::copyByName( const OUString& aName,
+ const OUString& aCopy, sal_Int16 nDestination )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ SCTAB nSource;
+ if ( pDocShell->GetDocument().GetTable( aName, nSource ) )
+ {
+ bDone = pDocShell->MoveTable( nSource, nDestination, true, true );
+ if (bDone)
+ {
+ // #i92477# any index past the last sheet means "append" in MoveTable
+ SCTAB nResultTab = static_cast<SCTAB>(nDestination);
+ SCTAB nTabCount = pDocShell->GetDocument().GetTableCount(); // count after copying
+ if (nResultTab >= nTabCount)
+ nResultTab = nTabCount - 1;
+
+ bDone = pDocShell->GetDocFunc().RenameTable( nResultTab, aCopy,
+ true, true );
+ }
+ }
+ }
+ if (!bDone)
+ throw uno::RuntimeException("ScTableSheetsObj::copyByName(): Illegal object name or bad index. Duplicate name?"); // no other exceptions specified
+}
+
+void SAL_CALL ScTableSheetsObj::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ bool bIllArg = false;
+
+ //! Type of aElement can be some specific interface instead of XInterface
+
+ if ( pDocShell )
+ {
+ uno::Reference<uno::XInterface> xInterface(aElement, uno::UNO_QUERY);
+ if ( xInterface.is() )
+ {
+ ScTableSheetObj* pSheetObj = dynamic_cast<ScTableSheetObj*>( xInterface.get() );
+ if ( pSheetObj && !pSheetObj->GetDocShell() ) // not inserted yet?
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nDummy;
+ if ( rDoc.GetTable( aName, nDummy ) )
+ {
+ // name already exists
+ throw container::ElementExistException();
+ }
+ SCTAB nPosition = rDoc.GetTableCount();
+ bDone = pDocShell->GetDocFunc().InsertTable( nPosition, aName,
+ true, true );
+ if (bDone)
+ pSheetObj->InitInsertSheet( pDocShell, nPosition );
+ // set document and new range in the object
+ }
+ else
+ bIllArg = true;
+ }
+ else
+ bIllArg = true;
+ }
+
+ if (!bDone)
+ {
+ if (bIllArg)
+ throw lang::IllegalArgumentException();
+ else
+ throw uno::RuntimeException(); // ElementExistException is handled above
+ }
+}
+
+void SAL_CALL ScTableSheetsObj::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ bool bIllArg = false;
+
+ //! Type of aElement can be some specific interface instead of XInterface
+
+ if ( pDocShell )
+ {
+ uno::Reference<uno::XInterface> xInterface(aElement, uno::UNO_QUERY);
+ if ( xInterface.is() )
+ {
+ ScTableSheetObj* pSheetObj = dynamic_cast<ScTableSheetObj*>( xInterface.get() );
+ if ( pSheetObj && !pSheetObj->GetDocShell() ) // not inserted yet?
+ {
+ SCTAB nPosition;
+ if ( !pDocShell->GetDocument().GetTable( aName, nPosition ) )
+ {
+ // not found
+ throw container::NoSuchElementException();
+ }
+
+ if ( pDocShell->GetDocFunc().DeleteTable( nPosition, true ) )
+ {
+ // InsertTable can't really go wrong now
+ bDone = pDocShell->GetDocFunc().InsertTable( nPosition, aName, true, true );
+ if (bDone)
+ pSheetObj->InitInsertSheet( pDocShell, nPosition );
+ }
+
+ }
+ else
+ bIllArg = true;
+ }
+ else
+ bIllArg = true;
+ }
+
+ if (!bDone)
+ {
+ if (bIllArg)
+ throw lang::IllegalArgumentException();
+ else
+ throw uno::RuntimeException(); // NoSuchElementException is handled above
+ }
+}
+
+void SAL_CALL ScTableSheetsObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ SCTAB nIndex;
+ if ( !pDocShell->GetDocument().GetTable( aName, nIndex ) )
+ throw container::NoSuchElementException(); // not found
+ bDone = pDocShell->GetDocFunc().DeleteTable( nIndex, true );
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException(); // NoSuchElementException is handled above
+}
+
+sal_Int32 ScTableSheetsObj::importSheet(
+ const uno::Reference < sheet::XSpreadsheetDocument > & xDocSrc,
+ const OUString& srcName, const sal_Int32 nDestPosition )
+{
+ //pDocShell is the destination
+ ScDocument& rDocDest = pDocShell->GetDocument();
+
+ // Source document docShell
+ if ( !xDocSrc.is() )
+ throw uno::RuntimeException();
+ ScModelObj* pObj = comphelper::getFromUnoTunnel<ScModelObj>(xDocSrc);
+ ScDocShell* pDocShellSrc = static_cast<ScDocShell*>(pObj->GetEmbeddedObject());
+
+ // SourceSheet Position and does srcName exists ?
+ SCTAB nIndexSrc;
+ if ( !pDocShellSrc->GetDocument().GetTable( srcName, nIndexSrc ) )
+ throw lang::IllegalArgumentException();
+
+ // Check the validity of destination index.
+ SCTAB nCount = rDocDest.GetTableCount();
+ SCTAB nIndexDest = static_cast<SCTAB>(nDestPosition);
+ if (nIndexDest > nCount || nIndexDest < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ // Transfer Tab
+ pDocShell->TransferTab(
+ *pDocShellSrc, nIndexSrc, nIndexDest, true/*bInsertNew*/, true/*bNotifyAndPaint*/ );
+
+ return nIndexDest;
+}
+
+// XCellRangesAccess
+
+uno::Reference< table::XCell > SAL_CALL ScTableSheetsObj::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow, sal_Int32 nSheet )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<ScTableSheetObj> xSheet = GetObjectByIndex_Impl(static_cast<sal_uInt16>(nSheet));
+ if (! xSheet.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return xSheet->getCellByPosition(nColumn, nRow);
+}
+
+uno::Reference< table::XCellRange > SAL_CALL ScTableSheetsObj::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom, sal_Int32 nSheet )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<ScTableSheetObj> xSheet = GetObjectByIndex_Impl(static_cast<sal_uInt16>(nSheet));
+ if (! xSheet.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return xSheet->getCellRangeByPosition(nLeft, nTop, nRight, nBottom);
+}
+
+uno::Sequence < uno::Reference< table::XCellRange > > SAL_CALL ScTableSheetsObj::getCellRangesByName( const OUString& aRange )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence < uno::Reference < table::XCellRange > > xRet;
+
+ ScRangeList aRangeList;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (!ScRangeStringConverter::GetRangeListFromString( aRangeList, aRange, rDoc, ::formula::FormulaGrammar::CONV_OOO, ';' ))
+ throw lang::IllegalArgumentException();
+
+ size_t nCount = aRangeList.size();
+ if (!nCount)
+ throw lang::IllegalArgumentException();
+
+ xRet.realloc(nCount);
+ auto pRet = xRet.getArray();
+ for( size_t nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ const ScRange & rRange = aRangeList[ nIndex ];
+ pRet[nIndex] = new ScCellRangeObj(pDocShell, rRange);
+ }
+
+ return xRet;
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScTableSheetsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.SpreadsheetsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScTableSheetsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ return pDocShell->GetDocument().GetTableCount();
+ return 0;
+}
+
+uno::Any SAL_CALL ScTableSheetsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XSpreadsheet> xSheet(GetObjectByIndex_Impl(nIndex));
+ if (!xSheet.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xSheet);
+
+// return uno::Any();
+}
+
+uno::Type SAL_CALL ScTableSheetsObj::getElementType()
+{
+ return cppu::UnoType<sheet::XSpreadsheet>::get();
+}
+
+sal_Bool SAL_CALL ScTableSheetsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL ScTableSheetsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XSpreadsheet> xSheet(GetObjectByName_Impl(aName));
+ if (!xSheet.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xSheet);
+}
+
+uno::Sequence<OUString> SAL_CALL ScTableSheetsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ OUString aName;
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ rDoc.GetName( i, aName );
+ pAry[i] = aName;
+ }
+ return aSeq;
+ }
+ return uno::Sequence<OUString>();
+}
+
+sal_Bool SAL_CALL ScTableSheetsObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ SCTAB nIndex;
+ if ( pDocShell->GetDocument().GetTable( aName, nIndex ) )
+ return true;
+ }
+ return false;
+}
+
+ScTableColumnsObj::ScTableColumnsObj(ScDocShell* pDocSh, SCTAB nT, SCCOL nSC, SCCOL nEC) :
+ pDocShell( pDocSh ),
+ nTab ( nT ),
+ nStartCol( nSC ),
+ nEndCol ( nEC )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScTableColumnsObj::~ScTableColumnsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScTableColumnsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! update of references for sheet and its start/end
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XTableColumns
+
+rtl::Reference<ScTableColumnObj> ScTableColumnsObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const
+{
+ SCCOL nCol = static_cast<SCCOL>(nIndex) + nStartCol;
+ if ( pDocShell && nCol <= nEndCol )
+ return new ScTableColumnObj( pDocShell, nCol, nTab );
+
+ return nullptr; // wrong index
+}
+
+rtl::Reference<ScTableColumnObj> ScTableColumnsObj::GetObjectByName_Impl(std::u16string_view aName) const
+{
+ SCCOL nCol = 0;
+ if (pDocShell && ::AlphaToCol(pDocShell->GetDocument(), nCol, aName))
+ if (nCol >= nStartCol && nCol <= nEndCol)
+ return new ScTableColumnObj( pDocShell, nCol, nTab );
+
+ return nullptr;
+}
+
+void SAL_CALL ScTableColumnsObj::insertByIndex( sal_Int32 nPosition, sal_Int32 nCount )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if ( pDocShell )
+ {
+ const ScDocument& rDoc = pDocShell->GetDocument();
+ if ( nCount > 0 && nPosition >= 0 && nStartCol+nPosition <= nEndCol &&
+ nStartCol+nPosition+nCount-1 <= rDoc.MaxCol() )
+ {
+ ScRange aRange( static_cast<SCCOL>(nStartCol+nPosition), 0, nTab,
+ static_cast<SCCOL>(nStartCol+nPosition+nCount-1), rDoc.MaxRow(), nTab );
+ bDone = pDocShell->GetDocFunc().InsertCells( aRange, nullptr, INS_INSCOLS_BEFORE, true, true );
+ }
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScTableColumnsObj::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ // the range to be deleted has to lie within the object
+ if ( pDocShell )
+ {
+ const ScDocument& rDoc = pDocShell->GetDocument();
+ if ( nCount > 0 && nIndex >= 0 && nStartCol+nIndex+nCount-1 <= nEndCol )
+ {
+ ScRange aRange( static_cast<SCCOL>(nStartCol+nIndex), 0, nTab,
+ static_cast<SCCOL>(nStartCol+nIndex+nCount-1), rDoc.MaxRow(), nTab );
+ bDone = pDocShell->GetDocFunc().DeleteCells( aRange, nullptr, DelCellCmd::Cols, true );
+ }
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScTableColumnsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.table.TableColumnsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScTableColumnsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return nEndCol - nStartCol + 1;
+}
+
+uno::Any SAL_CALL ScTableColumnsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XCellRange> xColumn(GetObjectByIndex_Impl(nIndex));
+ if (!xColumn.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xColumn);
+
+}
+
+uno::Type SAL_CALL ScTableColumnsObj::getElementType()
+{
+ return cppu::UnoType<table::XCellRange>::get();
+}
+
+sal_Bool SAL_CALL ScTableColumnsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+uno::Any SAL_CALL ScTableColumnsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XCellRange> xColumn(GetObjectByName_Impl(aName));
+ if (!xColumn.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xColumn);
+}
+
+uno::Sequence<OUString> SAL_CALL ScTableColumnsObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ SCCOL nCount = nEndCol - nStartCol + 1;
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+ for (SCCOL i=0; i<nCount; i++)
+ pAry[i] = ::ScColToAlpha( nStartCol + i );
+
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScTableColumnsObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ SCCOL nCol = 0;
+ if (pDocShell && ::AlphaToCol(pDocShell->GetDocument(), nCol, aName))
+ if (nCol >= nStartCol && nCol <= nEndCol)
+ return true;
+
+ return false; // not found
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableColumnsObj::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( lcl_GetColumnsPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScTableColumnsObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ throw uno::RuntimeException();
+
+ std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(nStartCol,nEndCol));
+ ScDocFunc& rFunc = pDocShell->GetDocFunc();
+
+ if ( aPropertyName == SC_UNONAME_CELLWID )
+ {
+ sal_Int32 nNewWidth = 0;
+ if ( aValue >>= nNewWidth )
+ rFunc.SetWidthOrHeight(
+ true, aColArr, nTab, SC_SIZE_ORIGINAL, o3tl::toTwips(nNewWidth, o3tl::Length::mm100), true, true);
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLVIS )
+ {
+ bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT;
+ rFunc.SetWidthOrHeight(true, aColArr, nTab, eMode, 0, true, true);
+ // SC_SIZE_DIRECT with size 0: hide
+ }
+ else if ( aPropertyName == SC_UNONAME_OWIDTH )
+ {
+ bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bOpt)
+ rFunc.SetWidthOrHeight(
+ true, aColArr, nTab, SC_SIZE_OPTIMAL, STD_EXTRA_WIDTH, true, true);
+ // sal_False for columns currently has no effect
+ }
+ else if ( aPropertyName == SC_UNONAME_NEWPAGE || aPropertyName == SC_UNONAME_MANPAGE )
+ {
+ //! single function to set/remove all breaks?
+ bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
+ if (bSet)
+ rFunc.InsertPageBreak( true, ScAddress(nCol,0,nTab), true, true );
+ else
+ rFunc.RemovePageBreak( true, ScAddress(nCol,0,nTab), true, true );
+ }
+}
+
+uno::Any SAL_CALL ScTableColumnsObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ throw uno::RuntimeException();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ uno::Any aAny;
+
+ //! loop over all columns for current state?
+
+ if ( aPropertyName == SC_UNONAME_CELLWID )
+ {
+ // for hidden column, return original height
+ sal_uInt16 nWidth = rDoc.GetOriginalWidth( nStartCol, nTab );
+ aAny <<= static_cast<sal_Int32>(convertTwipToMm100(nWidth));
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLVIS )
+ {
+ bool bVis = !rDoc.ColHidden(nStartCol, nTab);
+ aAny <<= bVis;
+ }
+ else if ( aPropertyName == SC_UNONAME_OWIDTH )
+ {
+ bool bOpt = !(rDoc.GetColFlags( nStartCol, nTab ) & CRFlags::ManualSize);
+ aAny <<= bOpt;
+ }
+ else if ( aPropertyName == SC_UNONAME_NEWPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasColBreak(nStartCol, nTab);
+ aAny <<= (nBreak != ScBreakType::NONE);
+ }
+ else if ( aPropertyName == SC_UNONAME_MANPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasColBreak(nStartCol, nTab);
+ aAny <<= bool(nBreak & ScBreakType::Manual);
+ }
+
+ return aAny;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScTableColumnsObj )
+
+ScTableRowsObj::ScTableRowsObj(ScDocShell* pDocSh, SCTAB nT, SCROW nSR, SCROW nER) :
+ pDocShell( pDocSh ),
+ nTab ( nT ),
+ nStartRow( nSR ),
+ nEndRow ( nER )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScTableRowsObj::~ScTableRowsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScTableRowsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! update of references for sheet and its start/end
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XTableRows
+
+rtl::Reference<ScTableRowObj> ScTableRowsObj::GetObjectByIndex_Impl(sal_Int32 nIndex) const
+{
+ SCROW nRow = static_cast<SCROW>(nIndex) + nStartRow;
+ if ( pDocShell && nRow <= nEndRow )
+ return new ScTableRowObj( pDocShell, nRow, nTab );
+
+ return nullptr; // wrong index
+}
+
+void SAL_CALL ScTableRowsObj::insertByIndex( sal_Int32 nPosition, sal_Int32 nCount )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if ( pDocShell )
+ {
+ const ScDocument& rDoc = pDocShell->GetDocument();
+ if ( nCount > 0 && nPosition >= 0 && nStartRow+nPosition <= nEndRow &&
+ nStartRow+nPosition+nCount-1 <= rDoc.MaxRow() )
+ {
+ ScRange aRange( 0, static_cast<SCROW>(nStartRow+nPosition), nTab,
+ rDoc.MaxCol(), static_cast<SCROW>(nStartRow+nPosition+nCount-1), nTab );
+ bDone = pDocShell->GetDocFunc().InsertCells( aRange, nullptr, INS_INSROWS_BEFORE, true, true );
+ }
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScTableRowsObj::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ // the range to be deleted has to lie within the object
+ if ( pDocShell && nCount > 0 && nIndex >= 0 && nStartRow+nIndex+nCount-1 <= nEndRow )
+ {
+ const ScDocument& rDoc = pDocShell->GetDocument();
+ ScRange aRange( 0, static_cast<SCROW>(nStartRow+nIndex), nTab,
+ rDoc.MaxCol(), static_cast<SCROW>(nStartRow+nIndex+nCount-1), nTab );
+ bDone = pDocShell->GetDocFunc().DeleteCells( aRange, nullptr, DelCellCmd::Rows, true );
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScTableRowsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.table.TableRowsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScTableRowsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return nEndRow - nStartRow + 1;
+}
+
+uno::Any SAL_CALL ScTableRowsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XCellRange> xRow(GetObjectByIndex_Impl(nIndex));
+ if (!xRow.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xRow);
+}
+
+uno::Type SAL_CALL ScTableRowsObj::getElementType()
+{
+ return cppu::UnoType<table::XCellRange>::get();
+}
+
+sal_Bool SAL_CALL ScTableRowsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableRowsObj::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( lcl_GetRowsPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScTableRowsObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ throw uno::RuntimeException();
+
+ ScDocFunc& rFunc = pDocShell->GetDocFunc();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(nStartRow,nEndRow));
+
+ if ( aPropertyName == SC_UNONAME_OHEIGHT )
+ {
+ sal_Int32 nNewHeight = 0;
+ if ( rDoc.IsImportingXML() && ( aValue >>= nNewHeight ) )
+ {
+ // used to set the stored row height for rows with optimal height when loading.
+
+ // TODO: It's probably cleaner to use a different property name
+ // for this.
+ rDoc.SetRowHeightOnly( nStartRow, nEndRow, nTab, o3tl::toTwips(nNewHeight, o3tl::Length::mm100) );
+ }
+ else
+ {
+ bool bOpt = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ if (bOpt)
+ rFunc.SetWidthOrHeight(false, aRowArr, nTab, SC_SIZE_OPTIMAL, 0, true, true);
+ else
+ {
+ //! manually set old heights again?
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLHGT )
+ {
+ sal_Int32 nNewHeight = 0;
+ if ( aValue >>= nNewHeight )
+ {
+ if (rDoc.IsImportingXML())
+ {
+ // TODO: This is a band-aid fix. Eventually we need to
+ // re-work ods' style import to get it to set styles to
+ // ScDocument directly.
+ rDoc.SetRowHeightOnly( nStartRow, nEndRow, nTab, o3tl::toTwips(nNewHeight, o3tl::Length::mm100) );
+ rDoc.SetManualHeight( nStartRow, nEndRow, nTab, true );
+ }
+ else
+ rFunc.SetWidthOrHeight(
+ false, aRowArr, nTab, SC_SIZE_ORIGINAL, o3tl::toTwips(nNewHeight, o3tl::Length::mm100), true, true);
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLVIS )
+ {
+ bool bVis = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ ScSizeMode eMode = bVis ? SC_SIZE_SHOW : SC_SIZE_DIRECT;
+ rFunc.SetWidthOrHeight(false, aRowArr, nTab, eMode, 0, true, true);
+ // SC_SIZE_DIRECT with size 0: hide
+ }
+ else if ( aPropertyName == SC_UNONAME_VISFLAG )
+ {
+ // #i116460# Shortcut to only set the flag, without drawing layer update etc.
+ // Should only be used from import filters.
+ rDoc.SetRowHidden(nStartRow, nEndRow, nTab, !ScUnoHelpFunctions::GetBoolFromAny( aValue ));
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLFILT )
+ {
+ //! undo etc.
+ if (ScUnoHelpFunctions::GetBoolFromAny( aValue ))
+ rDoc.SetRowFiltered(nStartRow, nEndRow, nTab, true);
+ else
+ rDoc.SetRowFiltered(nStartRow, nEndRow, nTab, false);
+ }
+ else if ( aPropertyName == SC_UNONAME_NEWPAGE || aPropertyName == SC_UNONAME_MANPAGE )
+ {
+ //! single function to set/remove all breaks?
+ bool bSet = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
+ if (bSet)
+ rFunc.InsertPageBreak( false, ScAddress(0,nRow,nTab), true, true );
+ else
+ rFunc.RemovePageBreak( false, ScAddress(0,nRow,nTab), true, true );
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLBACK || aPropertyName == SC_UNONAME_CELLTRAN )
+ {
+ // #i57867# Background color is specified for row styles in the file format,
+ // so it has to be supported along with the row properties (import only).
+
+ // Use ScCellRangeObj to set the property for all cells in the rows
+ // (this means, the "row attribute" must be set before individual cell attributes).
+
+ ScRange aRange( 0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab );
+ uno::Reference<beans::XPropertySet> xRangeObj = new ScCellRangeObj( pDocShell, aRange );
+ xRangeObj->setPropertyValue( aPropertyName, aValue );
+ }
+}
+
+uno::Any SAL_CALL ScTableRowsObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ throw uno::RuntimeException();
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ uno::Any aAny;
+
+ //! loop over all rows for current state?
+
+ if ( aPropertyName == SC_UNONAME_CELLHGT )
+ {
+ // for hidden row, return original height
+ sal_uInt16 nHeight = rDoc.GetOriginalHeight( nStartRow, nTab );
+ aAny <<= static_cast<sal_Int32>(convertTwipToMm100(nHeight));
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLVIS )
+ {
+ SCROW nLastRow;
+ bool bVis = !rDoc.RowHidden(nStartRow, nTab, nullptr, &nLastRow);
+ aAny <<= bVis;
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLFILT )
+ {
+ bool bVis = rDoc.RowFiltered(nStartRow, nTab);
+ aAny <<= bVis;
+ }
+ else if ( aPropertyName == SC_UNONAME_OHEIGHT )
+ {
+ bool bOpt = !(rDoc.GetRowFlags( nStartRow, nTab ) & CRFlags::ManualSize);
+ aAny <<= bOpt;
+ }
+ else if ( aPropertyName == SC_UNONAME_NEWPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasRowBreak(nStartRow, nTab);
+ aAny <<= (nBreak != ScBreakType::NONE);
+ }
+ else if ( aPropertyName == SC_UNONAME_MANPAGE )
+ {
+ ScBreakType nBreak = rDoc.HasRowBreak(nStartRow, nTab);
+ aAny <<= bool(nBreak & ScBreakType::Manual);
+ }
+ else if ( aPropertyName == SC_UNONAME_CELLBACK || aPropertyName == SC_UNONAME_CELLTRAN )
+ {
+ // Use ScCellRangeObj to get the property from the cell range
+ // (for completeness only, this is not used by the XML filter).
+
+ ScRange aRange( 0, nStartRow, nTab, rDoc.MaxCol(), nEndRow, nTab );
+ uno::Reference<beans::XPropertySet> xRangeObj = new ScCellRangeObj( pDocShell, aRange );
+ aAny = xRangeObj->getPropertyValue( aPropertyName );
+ }
+
+ return aAny;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScTableRowsObj )
+
+ScSpreadsheetSettingsObj::~ScSpreadsheetSettingsObj()
+{
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSpreadsheetSettingsObj::getPropertySetInfo()
+{
+ return nullptr;
+}
+
+void SAL_CALL ScSpreadsheetSettingsObj::setPropertyValue(
+ const OUString& /* aPropertyName */, const uno::Any& /* aValue */ )
+{
+}
+
+uno::Any SAL_CALL ScSpreadsheetSettingsObj::getPropertyValue( const OUString& /* aPropertyName */ )
+{
+ return uno::Any();
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSpreadsheetSettingsObj )
+
+ScAnnotationsObj::ScAnnotationsObj(ScDocShell* pDocSh, SCTAB nT) :
+ pDocShell( pDocSh ),
+ nTab( nT )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScAnnotationsObj::~ScAnnotationsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScAnnotationsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! adjust nTab when updating references!!!
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+bool ScAnnotationsObj::GetAddressByIndex_Impl( sal_Int32 nIndex, ScAddress& rPos ) const
+{
+ if (!pDocShell)
+ return false;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rPos = rDoc.GetNotePosition(nIndex, nTab);
+ return rPos.IsValid();
+}
+
+rtl::Reference<ScAnnotationObj> ScAnnotationsObj::GetObjectByIndex_Impl( sal_Int32 nIndex ) const
+{
+ if (pDocShell)
+ {
+ ScAddress aPos;
+ if ( GetAddressByIndex_Impl( nIndex, aPos ) )
+ return new ScAnnotationObj( pDocShell, aPos );
+ }
+ return nullptr;
+}
+
+// XSheetAnnotations
+
+void SAL_CALL ScAnnotationsObj::insertNew(
+ const table::CellAddress& aPosition, const OUString& rText )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ OSL_ENSURE( aPosition.Sheet == nTab, "addAnnotation with a wrong Sheet" );
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), nTab );
+ pDocShell->GetDocFunc().ReplaceNote( aPos, rText, nullptr, nullptr, true );
+ }
+}
+
+void SAL_CALL ScAnnotationsObj::removeByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScAddress aPos;
+ if ( GetAddressByIndex_Impl( nIndex, aPos ) )
+ {
+ ScMarkData aMarkData(pDocShell->GetDocument().GetSheetLimits());
+ aMarkData.SelectTable( aPos.Tab(), true );
+ aMarkData.SetMultiMarkArea( ScRange(aPos) );
+
+ pDocShell->GetDocFunc().DeleteContents( aMarkData, InsertDeleteFlags::NOTE, true, true );
+ }
+ }
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScAnnotationsObj::createEnumeration()
+{
+ //! iterate directly (more efficiently)?
+
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.CellAnnotationsEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScAnnotationsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nCount = 0;
+ if (pDocShell)
+ {
+ const ScDocument& rDoc = pDocShell->GetDocument();
+ for (SCCOL nCol : rDoc.GetAllocatedColumnsRange(nTab, 0, rDoc.MaxCol()))
+ nCount += rDoc.GetNoteCount(nTab, nCol);
+ }
+ return nCount;
+}
+
+uno::Any SAL_CALL ScAnnotationsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XSheetAnnotation> xAnnotation(GetObjectByIndex_Impl(nIndex));
+ if (!xAnnotation.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xAnnotation);
+}
+
+uno::Type SAL_CALL ScAnnotationsObj::getElementType()
+{
+ return cppu::UnoType<sheet::XSheetAnnotation>::get();
+}
+
+sal_Bool SAL_CALL ScAnnotationsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+ScScenariosObj::ScScenariosObj(ScDocShell* pDocSh, SCTAB nT) :
+ pDocShell( pDocSh ),
+ nTab ( nT )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScScenariosObj::~ScScenariosObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScScenariosObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! update of references for sheet and its start/end
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XScenarios
+
+bool ScScenariosObj::GetScenarioIndex_Impl( std::u16string_view rName, SCTAB& rIndex )
+{
+ //! Case-insensitive ????
+
+ if ( pDocShell )
+ {
+ OUString aTabName;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nCount = static_cast<SCTAB>(getCount());
+ for (SCTAB i=0; i<nCount; i++)
+ if (rDoc.GetName( nTab+i+1, aTabName ))
+ if (aTabName == rName)
+ {
+ rIndex = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+rtl::Reference<ScTableSheetObj> ScScenariosObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
+{
+ sal_uInt16 nCount = static_cast<sal_uInt16>(getCount());
+ if ( pDocShell && nIndex >= 0 && nIndex < nCount )
+ return new ScTableSheetObj( pDocShell, nTab+static_cast<SCTAB>(nIndex)+1 );
+
+ return nullptr; // no document or wrong index
+}
+
+rtl::Reference<ScTableSheetObj> ScScenariosObj::GetObjectByName_Impl(std::u16string_view aName)
+{
+ SCTAB nIndex;
+ if ( pDocShell && GetScenarioIndex_Impl( aName, nIndex ) )
+ return new ScTableSheetObj( pDocShell, nTab+nIndex+1 );
+
+ return nullptr; // not found
+}
+
+void SAL_CALL ScScenariosObj::addNewByName( const OUString& aName,
+ const uno::Sequence<table::CellRangeAddress>& aRanges,
+ const OUString& aComment )
+{
+ SolarMutexGuard aGuard;
+ if ( !pDocShell )
+ return;
+
+ ScMarkData aMarkData(pDocShell->GetDocument().GetSheetLimits());
+ aMarkData.SelectTable( nTab, true );
+
+ for (const table::CellRangeAddress& rRange : aRanges)
+ {
+ OSL_ENSURE( rRange.Sheet == nTab, "addScenario with a wrong Tab" );
+ ScRange aRange( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), nTab,
+ static_cast<SCCOL>(rRange.EndColumn), static_cast<SCROW>(rRange.EndRow), nTab );
+
+ aMarkData.SetMultiMarkArea( aRange );
+ }
+
+ ScScenarioFlags const nFlags = ScScenarioFlags::ShowFrame | ScScenarioFlags::PrintFrame
+ | ScScenarioFlags::TwoWay | ScScenarioFlags::Protected;
+
+ pDocShell->MakeScenario( nTab, aName, aComment, COL_LIGHTGRAY, nFlags, aMarkData );
+}
+
+void SAL_CALL ScScenariosObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ SCTAB nIndex;
+ if ( pDocShell && GetScenarioIndex_Impl( aName, nIndex ) )
+ pDocShell->GetDocFunc().DeleteTable( nTab+nIndex+1, true );
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScScenariosObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.ScenariosEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScScenariosObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ SCTAB nCount = 0;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if (!rDoc.IsScenario(nTab))
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nNext = nTab + 1;
+ while (nNext < nTabCount && rDoc.IsScenario(nNext))
+ {
+ ++nCount;
+ ++nNext;
+ }
+ }
+ }
+ return nCount;
+}
+
+uno::Any SAL_CALL ScScenariosObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XScenario> xScen(GetObjectByIndex_Impl(nIndex));
+ if (!xScen.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xScen);
+}
+
+uno::Type SAL_CALL ScScenariosObj::getElementType()
+{
+ return cppu::UnoType<sheet::XScenario>::get();
+}
+
+sal_Bool SAL_CALL ScScenariosObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+uno::Any SAL_CALL ScScenariosObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XScenario> xScen(GetObjectByName_Impl(aName));
+ if (!xScen.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xScen);
+}
+
+uno::Sequence<OUString> SAL_CALL ScScenariosObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ SCTAB nCount = static_cast<SCTAB>(getCount());
+ uno::Sequence<OUString> aSeq(nCount);
+
+ if ( pDocShell ) // otherwise Count = 0
+ {
+ OUString aTabName;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OUString* pAry = aSeq.getArray();
+ for (SCTAB i=0; i<nCount; i++)
+ if (rDoc.GetName( nTab+i+1, aTabName ))
+ pAry[i] = aTabName;
+ }
+
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScScenariosObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ SCTAB nIndex;
+ return GetScenarioIndex_Impl( aName, nIndex );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/drdefuno.cxx b/sc/source/ui/unoobj/drdefuno.cxx
new file mode 100644
index 0000000000..fa2387d28b
--- /dev/null
+++ b/sc/source/ui/unoobj/drdefuno.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 <drdefuno.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+
+#include <svx/unoprov.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+
+ScDrawDefaultsObj::ScDrawDefaultsObj(ScDocShell* pDocSh) :
+ SvxUnoDrawPool( nullptr, SvxPropertySetInfoPool::getDrawingDefaults() ),
+ pDocShell( pDocSh )
+{
+ // SvxUnoDrawPool is initialized without model,
+ // draw layer is created on demand in getModelPool
+
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDrawDefaultsObj::~ScDrawDefaultsObj() noexcept
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDrawDefaultsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // document gone
+ }
+}
+
+SfxItemPool* ScDrawDefaultsObj::getModelPool( bool bReadOnly ) noexcept
+{
+ SfxItemPool* pRet = nullptr;
+
+ try
+ {
+ if ( pDocShell )
+ {
+ ScDrawLayer* pModel = bReadOnly ?
+ pDocShell->GetDocument().GetDrawLayer() :
+ pDocShell->MakeDrawLayer();
+ if ( pModel )
+ pRet = &pModel->GetItemPool();
+ }
+ }
+ catch (...)
+ {
+ }
+
+ if ( !pRet )
+ pRet = SvxUnoDrawPool::getModelPool( bReadOnly ); // uses default pool
+
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/editsrc.cxx b/sc/source/ui/unoobj/editsrc.cxx
new file mode 100644
index 0000000000..f96f9c0201
--- /dev/null
+++ b/sc/source/ui/unoobj/editsrc.cxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <utility>
+
+#include <editsrc.hxx>
+
+#include <editeng/unofored.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outliner.hxx>
+#include <textuno.hxx>
+#include <editutil.hxx>
+#include <docsh.hxx>
+#include <hints.hxx>
+#include <postit.hxx>
+#include <AccessibleText.hxx>
+
+ScHeaderFooterEditSource::ScHeaderFooterEditSource(ScHeaderFooterTextData& rData) :
+ mrTextData(rData) {}
+
+ScHeaderFooterEditSource::~ScHeaderFooterEditSource() {}
+
+ScEditEngineDefaulter* ScHeaderFooterEditSource::GetEditEngine()
+{
+ return mrTextData.GetEditEngine();
+}
+
+std::unique_ptr<SvxEditSource> ScHeaderFooterEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new ScHeaderFooterEditSource(mrTextData));
+}
+
+SvxTextForwarder* ScHeaderFooterEditSource::GetTextForwarder()
+{
+ return mrTextData.GetTextForwarder();
+}
+
+void ScHeaderFooterEditSource::UpdateData()
+{
+ mrTextData.UpdateData();
+}
+
+ScCellEditSource::ScCellEditSource(ScDocShell* pDocSh, const ScAddress& rP) :
+ pCellTextData(new ScCellTextData(pDocSh, rP)) {}
+
+ScCellEditSource::~ScCellEditSource()
+{
+}
+
+std::unique_ptr<SvxEditSource> ScCellEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new ScCellEditSource(pCellTextData->GetDocShell(), pCellTextData->GetCellPos()));
+}
+
+SvxTextForwarder* ScCellEditSource::GetTextForwarder()
+{
+ return pCellTextData->GetTextForwarder();
+}
+
+void ScCellEditSource::UpdateData()
+{
+ pCellTextData->UpdateData();
+}
+
+void ScCellEditSource::SetDoUpdateData(bool bValue)
+{
+ pCellTextData->SetDoUpdate(bValue);
+}
+
+bool ScCellEditSource::IsDirty() const
+{
+ return pCellTextData->IsDirty();
+}
+
+ScEditEngineDefaulter* ScCellEditSource::GetEditEngine()
+{
+ return pCellTextData->GetEditEngine();
+}
+
+ScAnnotationEditSource::ScAnnotationEditSource(ScDocShell* pDocSh, const ScAddress& rP) :
+ pDocShell( pDocSh ),
+ aCellPos( rP ),
+ bDataValid( false )
+{
+ if (pDocShell)
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScAnnotationEditSource::~ScAnnotationEditSource()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+
+ pForwarder.reset();
+ pEditEngine.reset();
+}
+
+std::unique_ptr<SvxEditSource> ScAnnotationEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new ScAnnotationEditSource( pDocShell, aCellPos ));
+}
+
+SdrObject* ScAnnotationEditSource::GetCaptionObj()
+{
+ ScPostIt* pNote = pDocShell->GetDocument().GetNote(aCellPos);
+ return pNote ? pNote->GetOrCreateCaption( aCellPos ) : nullptr;
+}
+
+SvxTextForwarder* ScAnnotationEditSource::GetTextForwarder()
+{
+ if (!pEditEngine)
+ {
+ // notes don't have fields
+ if ( pDocShell )
+ {
+ pEditEngine.reset( new ScNoteEditEngine( pDocShell->GetDocument().GetNoteEngine() ) );
+ }
+ else
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ pEditEngine.reset( new ScEditEngineDefaulter( pEnginePool.get(), true ) );
+ }
+ pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) );
+ }
+
+ if (bDataValid)
+ return pForwarder.get();
+
+ if ( pDocShell )
+ if ( ScPostIt* pNote = pDocShell->GetDocument().GetNote(aCellPos) )
+ if ( const EditTextObject* pEditObj = pNote->GetEditTextObject() )
+ pEditEngine->SetTextCurrentDefaults( *pEditObj ); // incl. breaks (line, etc.)
+
+ bDataValid = true;
+ return pForwarder.get();
+}
+
+void ScAnnotationEditSource::UpdateData()
+{
+ if ( !(pDocShell && pEditEngine) )
+ return;
+
+ ScDocShellModificator aModificator( *pDocShell );
+
+ if( SdrObject* pObj = GetCaptionObj() )
+ {
+ OutlinerParaObject aOPO( pEditEngine->CreateTextObject() );
+ aOPO.SetOutlinerMode( OutlinerMode::TextObject );
+ pObj->NbcSetOutlinerParaObject( std::move(aOPO) );
+ pObj->ActionChanged();
+ }
+
+ //! Undo !!!
+
+ aModificator.SetDocumentModified();
+
+ // SetDocumentModified will reset bDataValid
+}
+
+void ScAnnotationEditSource::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! reference update
+ }
+ else
+ {
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+
+ pForwarder.reset();
+ pEditEngine.reset(); // EditEngine uses document's pool
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ bDataValid = false; // text must be retrieved again
+ }
+}
+
+ScSimpleEditSource::ScSimpleEditSource( SvxTextForwarder* pForw ) :
+ pForwarder( pForw )
+{
+ // The same forwarder (and EditEngine) is shared by all children of the same Text object.
+ // Text range and cursor keep a reference to their parent text, so the text object is
+ // always alive and the forwarder is valid as long as there are children.
+}
+
+ScSimpleEditSource::~ScSimpleEditSource()
+{
+}
+
+std::unique_ptr<SvxEditSource> ScSimpleEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new ScSimpleEditSource( pForwarder ));
+}
+
+SvxTextForwarder* ScSimpleEditSource::GetTextForwarder()
+{
+ return pForwarder;
+}
+
+void ScSimpleEditSource::UpdateData()
+{
+ // nothing
+}
+
+ScAccessibilityEditSource::ScAccessibilityEditSource( ::std::unique_ptr < ScAccessibleTextData > && pAccessibleCellTextData )
+ : mpAccessibleTextData(std::move(pAccessibleCellTextData))
+{
+}
+
+ScAccessibilityEditSource::~ScAccessibilityEditSource()
+{
+}
+
+std::unique_ptr<SvxEditSource> ScAccessibilityEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new ScAccessibilityEditSource(::std::unique_ptr < ScAccessibleTextData > (mpAccessibleTextData->Clone())));
+}
+
+SvxTextForwarder* ScAccessibilityEditSource::GetTextForwarder()
+{
+ return mpAccessibleTextData->GetTextForwarder();
+}
+
+SvxViewForwarder* ScAccessibilityEditSource::GetViewForwarder()
+{
+ return mpAccessibleTextData->GetViewForwarder();
+}
+
+SvxEditViewForwarder* ScAccessibilityEditSource::GetEditViewForwarder( bool bCreate )
+{
+ return mpAccessibleTextData->GetEditViewForwarder(bCreate);
+}
+
+void ScAccessibilityEditSource::UpdateData()
+{
+ mpAccessibleTextData->UpdateData();
+}
+
+SfxBroadcaster& ScAccessibilityEditSource::GetBroadcaster() const
+{
+ return mpAccessibleTextData->GetBroadcaster();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/eventuno.cxx b/sc/source/ui/unoobj/eventuno.cxx
new file mode 100644
index 0000000000..88a3876378
--- /dev/null
+++ b/sc/source/ui/unoobj/eventuno.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 <eventuno.hxx>
+#include <miscuno.hxx>
+#include <docsh.hxx>
+#include <sheetevents.hxx>
+#include <unonames.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+
+SC_SIMPLE_SERVICE_INFO( ScSheetEventsObj, "ScSheetEventsObj", "com.sun.star.document.Events" )
+
+ScSheetEventsObj::ScSheetEventsObj(ScDocShell* pDocSh, SCTAB nT) :
+ mpDocShell( pDocSh ),
+ mnTab( nT )
+{
+ mpDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScSheetEventsObj::~ScSheetEventsObj()
+{
+ SolarMutexGuard g;
+
+ if (mpDocShell)
+ mpDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScSheetEventsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! reference update
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpDocShell = nullptr;
+ }
+}
+
+static ScSheetEventId lcl_GetEventFromName( std::u16string_view aName )
+{
+ for (sal_Int32 nEvent=0; nEvent<static_cast<sal_Int32>(ScSheetEventId::COUNT); ++nEvent)
+ if ( aName == ScSheetEvents::GetEventName(static_cast<ScSheetEventId>(nEvent)) )
+ return static_cast<ScSheetEventId>(nEvent);
+
+ return ScSheetEventId::NOTFOUND; // not found
+}
+
+// XNameReplace
+
+void SAL_CALL ScSheetEventsObj::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ if (!mpDocShell)
+ throw uno::RuntimeException();
+
+ ScSheetEventId nEvent = lcl_GetEventFromName(aName);
+ if (nEvent == ScSheetEventId::NOTFOUND)
+ throw container::NoSuchElementException();
+
+ std::unique_ptr<ScSheetEvents> pNewEvents(new ScSheetEvents);
+ const ScSheetEvents* pOldEvents = mpDocShell->GetDocument().GetSheetEvents(mnTab);
+ if (pOldEvents)
+ *pNewEvents = *pOldEvents;
+
+ OUString aScript;
+ if ( aElement.hasValue() ) // empty Any -> reset event
+ {
+ uno::Sequence<beans::PropertyValue> aPropSeq;
+ if ( aElement >>= aPropSeq )
+ {
+ for (const beans::PropertyValue& rProp : std::as_const(aPropSeq))
+ {
+ if ( rProp.Name == SC_UNO_EVENTTYPE )
+ {
+ OUString aEventType;
+ if ( rProp.Value >>= aEventType )
+ {
+ // only "Script" is supported
+ if ( aEventType != SC_UNO_SCRIPT )
+ throw lang::IllegalArgumentException();
+ }
+ }
+ else if ( rProp.Name == SC_UNO_SCRIPT )
+ rProp.Value >>= aScript;
+ }
+ }
+ }
+ if (!aScript.isEmpty())
+ pNewEvents->SetScript( nEvent, &aScript );
+ else
+ pNewEvents->SetScript( nEvent, nullptr ); // reset
+
+ mpDocShell->GetDocument().SetSheetEvents( mnTab, std::move(pNewEvents) );
+ mpDocShell->SetDocumentModified();
+}
+
+// XNameAccess
+
+uno::Any SAL_CALL ScSheetEventsObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ ScSheetEventId nEvent = lcl_GetEventFromName(aName);
+ if (nEvent == ScSheetEventId::NOTFOUND)
+ throw container::NoSuchElementException();
+
+ const OUString* pScript = nullptr;
+ if (mpDocShell)
+ {
+ const ScSheetEvents* pEvents = mpDocShell->GetDocument().GetSheetEvents(mnTab);
+ if (pEvents)
+ pScript = pEvents->GetScript(nEvent);
+ }
+
+ uno::Any aRet;
+ if (pScript)
+ {
+ uno::Sequence<beans::PropertyValue> aPropSeq( comphelper::InitPropertySequence({
+ { "EventType", uno::Any( OUString("Script") ) },
+ { "Script", uno::Any( *pScript ) }
+ }));
+ aRet <<= aPropSeq;
+ }
+ // empty Any if nothing was set
+ return aRet;
+}
+
+uno::Sequence<OUString> SAL_CALL ScSheetEventsObj::getElementNames()
+{
+ auto aNames = uno::Sequence<OUString>(int(ScSheetEventId::COUNT));
+ auto pNames = aNames.getArray();
+ for (sal_Int32 nEvent=0; nEvent<int(ScSheetEventId::COUNT); ++nEvent)
+ pNames[nEvent] = ScSheetEvents::GetEventName(static_cast<ScSheetEventId>(nEvent));
+ return aNames;
+}
+
+sal_Bool SAL_CALL ScSheetEventsObj::hasByName( const OUString& aName )
+{
+ ScSheetEventId nEvent = lcl_GetEventFromName(aName);
+ return (nEvent != ScSheetEventId::NOTFOUND);
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScSheetEventsObj::getElementType()
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL ScSheetEventsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if (mpDocShell)
+ return true;
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/exceldetect.cxx b/sc/source/ui/unoobj/exceldetect.cxx
new file mode 100644
index 0000000000..0c4373356a
--- /dev/null
+++ b/sc/source/ui/unoobj/exceldetect.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/.
+ */
+
+#include "exceldetect.hxx"
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <sot/storage.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace com::sun::star;
+using utl::MediaDescriptor;
+
+ScExcelBiffDetect::ScExcelBiffDetect() {}
+ScExcelBiffDetect::~ScExcelBiffDetect() {}
+
+OUString ScExcelBiffDetect::getImplementationName()
+{
+ return "com.sun.star.comp.calc.ExcelBiffFormatDetector";
+}
+
+sal_Bool ScExcelBiffDetect::supportsService( const OUString& aName )
+{
+ return cppu::supportsService(this, aName);
+}
+
+uno::Sequence<OUString> ScExcelBiffDetect::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ExtendedTypeDetection" };
+}
+
+namespace {
+
+bool hasStream(const uno::Reference<io::XInputStream>& xInStream, const OUString& rName)
+{
+ SfxMedium aMedium;
+ aMedium.UseInteractionHandler(false);
+ aMedium.setStreamToLoadFrom(xInStream, true);
+ SvStream* pStream = aMedium.GetInStream();
+ if (!pStream)
+ return false;
+
+ sal_uInt64 const nSize = pStream->TellEnd();
+ pStream->Seek(0);
+
+ if (!nSize)
+ {
+ // 0-size stream. Failed.
+ return false;
+ }
+
+ try
+ {
+ tools::SvRef<SotStorage> xStorage = new SotStorage(pStream, false);
+ if (!xStorage.is() || xStorage->GetError())
+ return false;
+ return xStorage->IsStream(rName);
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("sc", "hasStream");
+ }
+
+ return false;
+}
+
+/**
+ * We detect BIFF 2, 3 and 4 file types together since the only thing that
+ * set them apart is the BOF ID.
+ */
+bool isExcel40(const uno::Reference<io::XInputStream>& xInStream)
+{
+ SfxMedium aMedium;
+ aMedium.UseInteractionHandler(false);
+ aMedium.setStreamToLoadFrom(xInStream, true);
+ SvStream* pStream = aMedium.GetInStream();
+ if (!pStream)
+ return false;
+
+ sal_uInt64 const nSize = pStream->TellEnd();
+ pStream->Seek(0);
+
+ if (nSize < 4)
+ return false;
+
+ sal_uInt16 nBofId, nBofSize;
+ pStream->ReadUInt16( nBofId ).ReadUInt16( nBofSize );
+
+ switch (nBofId)
+ {
+ case 0x0009: // Excel 2.1 worksheet (BIFF 2)
+ case 0x0209: // Excel 3.0 worksheet (BIFF 3)
+ case 0x0409: // Excel 4.0 worksheet (BIFF 4)
+ case 0x0809: // Excel 5.0 worksheet (BIFF 5), some apps create such files (fdo#70100)
+ break;
+ default:
+ return false;
+ }
+
+ if (nBofSize < 4 || 16 < nBofSize)
+ // BOF record must be sized between 4 and 16 for BIFF 2, 3 and 4.
+ return false;
+
+ sal_uInt64 const nPos = pStream->Tell();
+ if (nSize - nPos < nBofSize)
+ // BOF record doesn't have required bytes.
+ return false;
+
+ return true;
+}
+
+bool isTemplate(std::u16string_view rType)
+{
+ return rType.find(u"_VorlageTemplate") != std::u16string_view::npos;
+}
+
+}
+
+OUString ScExcelBiffDetect::detect( uno::Sequence<beans::PropertyValue>& lDescriptor )
+{
+ MediaDescriptor aMediaDesc(lDescriptor);
+ OUString aType;
+ aMediaDesc[MediaDescriptor::PROP_TYPENAME] >>= aType;
+ if (aType.isEmpty())
+ // Type is not given. We can't proceed.
+ return OUString();
+
+ aMediaDesc.addInputStream();
+ uno::Reference<io::XInputStream> xInStream(aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY);
+ if (!xInStream.is())
+ // No input stream.
+ return OUString();
+
+ if (aType == "calc_MS_Excel_97" || aType == "calc_MS_Excel_97_VorlageTemplate")
+ {
+ // See if this stream is an Excel 97/XP/2003 (BIFF8) stream.
+ if (!hasStream(xInStream, "Workbook"))
+ // BIFF8 is expected to contain a stream named "Workbook".
+ return OUString();
+
+ aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 97 Vorlage/Template") : OUString("MS Excel 97");
+ }
+
+ else if (aType == "calc_MS_Excel_95" || aType == "calc_MS_Excel_95_VorlageTemplate")
+ {
+ // See if this stream is an Excel 95 (BIFF5) stream.
+ if (!hasStream(xInStream, "Book"))
+ return OUString();
+
+ aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 95 Vorlage/Template") : OUString("MS Excel 95");
+ }
+
+ else if (aType == "calc_MS_Excel_5095" || aType == "calc_MS_Excel_5095_VorlageTemplate")
+ {
+ // See if this stream is an Excel 5.0/95 stream.
+ if (!hasStream(xInStream, "Book"))
+ return OUString();
+
+ aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 5.0/95 Vorlage/Template") : OUString("MS Excel 5.0/95");
+ }
+
+ else if (aType == "calc_MS_Excel_40" || aType == "calc_MS_Excel_40_VorlageTemplate")
+ {
+ // See if this stream is an Excel 4.0 stream.
+ if (!isExcel40(xInStream))
+ return OUString();
+
+ aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? OUString("MS Excel 4.0 Vorlage/Template") : OUString("MS Excel 4.0");
+ }
+
+ else
+ // Nothing to detect.
+ return OUString();
+
+ aMediaDesc >> lDescriptor;
+ return aType;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_calc_ExcelBiffFormatDetector_get_implementation(css::uno::XComponentContext* /*context*/,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScExcelBiffDetect);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/exceldetect.hxx b/sc/source/ui/unoobj/exceldetect.hxx
new file mode 100644
index 0000000000..7189637526
--- /dev/null
+++ b/sc/source/ui/unoobj/exceldetect.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+
+class ScExcelBiffDetect
+ : public cppu::WeakImplHelper<css::document::XExtendedFilterDetection, css::lang::XServiceInfo>
+{
+public:
+ explicit ScExcelBiffDetect();
+ virtual ~ScExcelBiffDetect() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& aName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XExtendedFilterDetection
+ virtual OUString SAL_CALL
+ detect(css::uno::Sequence<css::beans::PropertyValue>& lDescriptor) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/fielduno.cxx b/sc/source/ui/unoobj/fielduno.cxx
new file mode 100644
index 0000000000..bdd3271a8b
--- /dev/null
+++ b/sc/source/ui/unoobj/fielduno.cxx
@@ -0,0 +1,1289 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <fielduno.hxx>
+#include <textuno.hxx>
+#include <miscuno.hxx>
+#include <docsh.hxx>
+#include <hints.hxx>
+#include <editsrc.hxx>
+#include <unonames.hxx>
+#include <editutil.hxx>
+
+#include <svl/hint.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <editeng/eeitem.hxx>
+
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <com/sun/star/text/textfield/Type.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+
+// no Which-ID here, map only for PropertySetInfo
+
+const SfxItemPropertySet* getDateTimePropertySet()
+{
+ static const SfxItemPropertyMapEntry aMapContent[] =
+ {
+ { SC_UNONAME_DATETIME, 0, cppu::UnoType<util::DateTime>::get(), 0, 0 },
+ { SC_UNONAME_ISFIXED, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_ISDATE, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNONAME_NUMFMT, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aMap(aMapContent);
+ return &aMap;
+}
+
+const SfxItemPropertySet* getEmptyPropertySet()
+{
+ static SfxItemPropertySet aMap({});
+ return &aMap;
+}
+
+const SfxItemPropertySet* lcl_GetURLPropertySet()
+{
+ static const SfxItemPropertyMapEntry aURLPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ANCTYPE, 0, cppu::UnoType<text::TextContentAnchorType>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ANCTYPES, 0, cppu::UnoType<uno::Sequence<text::TextContentAnchorType>>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_REPR, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNONAME_TARGET, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNONAME_TEXTWRAP, 0, cppu::UnoType<text::WrapTextMode>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_URL, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ };
+ static SfxItemPropertySet aURLPropertySet_Impl( aURLPropertyMap_Impl );
+ return &aURLPropertySet_Impl;
+}
+
+const SfxItemPropertySet* lcl_GetHeaderFieldPropertySet()
+{
+ static const SfxItemPropertyMapEntry aHeaderFieldPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ANCTYPE, 0, cppu::UnoType<text::TextContentAnchorType>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ANCTYPES, 0, cppu::UnoType<uno::Sequence<text::TextContentAnchorType>>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TEXTWRAP, 0, cppu::UnoType<text::WrapTextMode>::get(), beans::PropertyAttribute::READONLY, 0 },
+ };
+ static SfxItemPropertySet aHeaderFieldPropertySet_Impl( aHeaderFieldPropertyMap_Impl );
+ return &aHeaderFieldPropertySet_Impl;
+}
+
+const SfxItemPropertySet* lcl_GetFileFieldPropertySet()
+{
+ static const SfxItemPropertyMapEntry aFileFieldPropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ANCTYPE, 0, cppu::UnoType<text::TextContentAnchorType>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ANCTYPES, 0, cppu::UnoType<uno::Sequence<text::TextContentAnchorType>>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_FILEFORM, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { SC_UNONAME_TEXTWRAP, 0, cppu::UnoType<text::WrapTextMode>::get(), beans::PropertyAttribute::READONLY, 0 },
+ };
+ static SfxItemPropertySet aFileFieldPropertySet_Impl( aFileFieldPropertyMap_Impl );
+ return &aFileFieldPropertySet_Impl;
+}
+
+SvxFileFormat lcl_UnoToSvxFileFormat( sal_Int16 nUnoValue )
+{
+ switch( nUnoValue )
+ {
+ case text::FilenameDisplayFormat::FULL: return SvxFileFormat::PathFull;
+ case text::FilenameDisplayFormat::PATH: return SvxFileFormat::PathOnly;
+ case text::FilenameDisplayFormat::NAME: return SvxFileFormat::NameOnly;
+ default:
+ return SvxFileFormat::NameAndExt;
+ }
+}
+
+sal_Int16 lcl_SvxToUnoFileFormat( SvxFileFormat nSvxValue )
+{
+ switch( nSvxValue )
+ {
+ case SvxFileFormat::NameAndExt: return text::FilenameDisplayFormat::NAME_AND_EXT;
+ case SvxFileFormat::PathFull: return text::FilenameDisplayFormat::FULL;
+ case SvxFileFormat::PathOnly: return text::FilenameDisplayFormat::PATH;
+ default:
+ return text::FilenameDisplayFormat::NAME;
+ }
+}
+
+}
+
+SC_SIMPLE_SERVICE_INFO( ScCellFieldsObj, "ScCellFieldsObj", "com.sun.star.text.TextFields" )
+SC_SIMPLE_SERVICE_INFO( ScHeaderFieldsObj, "ScHeaderFieldsObj", "com.sun.star.text.TextFields" )
+
+namespace {
+
+enum ScUnoCollectMode
+{
+ SC_UNO_COLLECT_NONE,
+ SC_UNO_COLLECT_COUNT,
+ SC_UNO_COLLECT_FINDINDEX,
+ SC_UNO_COLLECT_FINDPOS
+};
+
+/**
+ * This class exists solely to allow searching through field items. TODO:
+ * Look into providing the same functionality directly in EditEngine, to
+ * avoid having this class altogether.
+ */
+class ScUnoEditEngine : public ScEditEngineDefaulter
+{
+ ScUnoCollectMode eMode;
+ sal_uInt16 nFieldCount;
+ sal_Int32 mnFieldType;
+ std::unique_ptr<SvxFieldData>
+ pFound; // local copy
+ sal_Int32 nFieldPar;
+ sal_Int32 nFieldPos;
+ sal_uInt16 nFieldIndex;
+
+public:
+ explicit ScUnoEditEngine(ScEditEngineDefaulter* pSource);
+
+ virtual OUString CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos,
+ std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor, std::optional<FontLineStyle>& rFldLineStyle ) override;
+
+ sal_uInt16 CountFields();
+ SvxFieldData* FindByIndex(sal_uInt16 nIndex);
+ SvxFieldData* FindByPos(sal_Int32 nPar, sal_Int32 nPos, sal_Int32 nType);
+
+ sal_Int32 GetFieldPar() const { return nFieldPar; }
+ sal_Int32 GetFieldPos() const { return nFieldPos; }
+};
+
+}
+
+ScUnoEditEngine::ScUnoEditEngine(ScEditEngineDefaulter* pSource)
+ : ScEditEngineDefaulter(*pSource)
+ , eMode(SC_UNO_COLLECT_NONE)
+ , nFieldCount(0)
+ , mnFieldType(text::textfield::Type::UNSPECIFIED)
+ , nFieldPar(0)
+ , nFieldPos(0)
+ , nFieldIndex(0)
+{
+ std::unique_ptr<EditTextObject> pData = pSource->CreateTextObject();
+ SetTextCurrentDefaults( *pData );
+}
+
+OUString ScUnoEditEngine::CalcFieldValue( const SvxFieldItem& rField,
+ sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor, std::optional<FontLineStyle>& rFldLineStyle )
+{
+ OUString aRet(EditEngine::CalcFieldValue( rField, nPara, nPos, rTxtColor, rFldColor, rFldLineStyle ));
+ if (eMode != SC_UNO_COLLECT_NONE)
+ {
+ const SvxFieldData* pFieldData = rField.GetField();
+ if ( pFieldData )
+ {
+ if (mnFieldType == text::textfield::Type::UNSPECIFIED || pFieldData->GetClassId() == mnFieldType)
+ {
+ if ( eMode == SC_UNO_COLLECT_FINDINDEX && !pFound && nFieldCount == nFieldIndex )
+ {
+ pFound = pFieldData->Clone();
+ nFieldPar = nPara;
+ nFieldPos = nPos;
+ }
+ if ( eMode == SC_UNO_COLLECT_FINDPOS && !pFound &&
+ nPara == nFieldPar && nPos == nFieldPos )
+ {
+ pFound = pFieldData->Clone();
+ nFieldIndex = nFieldCount;
+ }
+ ++nFieldCount;
+ }
+ }
+ }
+ return aRet;
+}
+
+sal_uInt16 ScUnoEditEngine::CountFields()
+{
+ eMode = SC_UNO_COLLECT_COUNT;
+ mnFieldType = text::textfield::Type::UNSPECIFIED;
+ nFieldCount = 0;
+ UpdateFields();
+ eMode = SC_UNO_COLLECT_NONE;
+
+ return nFieldCount;
+}
+
+SvxFieldData* ScUnoEditEngine::FindByIndex(sal_uInt16 nIndex)
+{
+ eMode = SC_UNO_COLLECT_FINDINDEX;
+ nFieldIndex = nIndex;
+ mnFieldType = text::textfield::Type::UNSPECIFIED;
+ nFieldCount = 0;
+ UpdateFields();
+ eMode = SC_UNO_COLLECT_NONE;
+
+ return pFound.get();
+}
+
+SvxFieldData* ScUnoEditEngine::FindByPos(sal_Int32 nPar, sal_Int32 nPos, sal_Int32 nType)
+{
+ eMode = SC_UNO_COLLECT_FINDPOS;
+ nFieldPar = nPar;
+ nFieldPos = nPos;
+ mnFieldType = nType;
+ nFieldCount = 0;
+ UpdateFields();
+ mnFieldType = text::textfield::Type::UNSPECIFIED;
+ eMode = SC_UNO_COLLECT_NONE;
+
+ return pFound.get();
+}
+
+ScCellFieldsObj::ScCellFieldsObj(
+ uno::Reference<text::XTextRange> xContent,
+ ScDocShell* pDocSh, const ScAddress& rPos) :
+ mxContent(std::move(xContent)),
+ pDocShell( pDocSh ),
+ aCellPos( rPos )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+
+ mpEditSource.reset( new ScCellEditSource( pDocShell, aCellPos ) );
+}
+
+ScCellFieldsObj::~ScCellFieldsObj()
+{
+ {
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+
+ mpEditSource.reset();
+ }
+
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+
+ std::unique_lock g(aMutex);
+ if (maRefreshListeners.getLength(g))
+ {
+ lang::EventObject aEvent;
+ aEvent.Source.set(getXWeak());
+ maRefreshListeners.disposeAndClear(g, aEvent);
+ }
+}
+
+void ScCellFieldsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ //! update of references
+ }
+ else if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+
+ // EditSource registered itself as a listener
+}
+
+// XIndexAccess (via XTextFields)
+
+uno::Reference<text::XTextField> ScCellFieldsObj::GetObjectByIndex_Impl(sal_Int32 Index) const
+{
+ //! Field functions have to be passed to the forwarder !!!
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+ SvxFieldData* pData = aTempEngine.FindByIndex(static_cast<sal_uInt16>(Index));
+ if (!pData)
+ return uno::Reference<text::XTextField>();
+
+ sal_Int32 nPar = aTempEngine.GetFieldPar();
+ sal_Int32 nPos = aTempEngine.GetFieldPos();
+ ESelection aSelection( nPar, nPos, nPar, nPos+1 ); // Field size is 1 character
+
+ sal_Int32 eType = pData->GetClassId();
+ uno::Reference<text::XTextField> xRet(
+ new ScEditFieldObj(mxContent, std::make_unique<ScCellEditSource>(pDocShell, aCellPos), eType, aSelection));
+ return xRet;
+}
+
+sal_Int32 SAL_CALL ScCellFieldsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ //! Field functions have to be passed to the forwarder !!!
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+
+ return aTempEngine.CountFields(); // count the fields, we don't care about their type in the cell
+}
+
+uno::Any SAL_CALL ScCellFieldsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<text::XTextField> xField(GetObjectByIndex_Impl(nIndex));
+ if (!xField.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xField);
+}
+
+uno::Type SAL_CALL ScCellFieldsObj::getElementType()
+{
+ return cppu::UnoType<text::XTextField>::get();
+}
+
+sal_Bool SAL_CALL ScCellFieldsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+uno::Reference<container::XEnumeration> SAL_CALL ScCellFieldsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.text.TextFieldEnumeration");
+}
+
+void SAL_CALL ScCellFieldsObj::addContainerListener(
+ const uno::Reference<container::XContainerListener>& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScCellFieldsObj::removeContainerListener(
+ const uno::Reference<container::XContainerListener>& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+// XRefreshable
+void SAL_CALL ScCellFieldsObj::refresh( )
+{
+ std::unique_lock g(aMutex);
+ if (maRefreshListeners.getLength(g))
+ {
+ // Call all listeners.
+ lang::EventObject aEvent;
+ aEvent.Source.set(uno::Reference< util::XRefreshable >(this));
+ maRefreshListeners.notifyEach( g, &util::XRefreshListener::refreshed, aEvent );
+ }
+}
+
+void SAL_CALL ScCellFieldsObj::addRefreshListener( const uno::Reference< util::XRefreshListener >& xListener )
+{
+ if (xListener.is())
+ {
+ std::unique_lock g(aMutex);
+ maRefreshListeners.addInterface(g, xListener);
+ }
+}
+
+void SAL_CALL ScCellFieldsObj::removeRefreshListener( const uno::Reference<util::XRefreshListener >& xListener )
+{
+ if (xListener.is())
+ {
+ std::unique_lock g(aMutex);
+ maRefreshListeners.removeInterface(g, xListener);
+ }
+}
+
+ScHeaderFieldsObj::ScHeaderFieldsObj(ScHeaderFooterTextData& rData) :
+ mrData(rData)
+{
+ mpEditSource.reset( new ScHeaderFooterEditSource(rData) );
+}
+
+ScHeaderFieldsObj::~ScHeaderFieldsObj()
+{
+ mpEditSource.reset();
+
+ // increment refcount to prevent double call of dtor
+ osl_atomic_increment( &m_refCount );
+
+ std::unique_lock g(aMutex);
+ if (maRefreshListeners.getLength(g))
+ {
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+ maRefreshListeners.disposeAndClear(g, aEvent);
+ }
+}
+
+// XIndexAccess (via XTextFields)
+
+uno::Reference<text::XTextField> ScHeaderFieldsObj::GetObjectByIndex_Impl(sal_Int32 Index) const
+{
+ //! Field functions have to be passed to the forwarder !!!
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+
+ SvxFieldData* pData = aTempEngine.FindByIndex(static_cast<sal_uInt16>(Index));
+ if (!pData)
+ return nullptr;
+
+ // Get the parent text range instance.
+ uno::Reference<text::XTextRange> xTextRange;
+ uno::Reference<sheet::XHeaderFooterContent> xContentObj = mrData.GetContentObj();
+ if (!xContentObj.is())
+ throw uno::RuntimeException("");
+
+ rtl::Reference<ScHeaderFooterContentObj> pContentObj = ScHeaderFooterContentObj::getImplementation(xContentObj);
+ uno::Reference<text::XText> xText;
+
+ switch ( mrData.GetPart() )
+ {
+ case ScHeaderFooterPart::LEFT:
+ xText = pContentObj->getLeftText();
+ break;
+ case ScHeaderFooterPart::CENTER:
+ xText = pContentObj->getCenterText();
+ break;
+ case ScHeaderFooterPart::RIGHT:
+ xText = pContentObj->getRightText();
+ break;
+ }
+
+ xTextRange = xText;
+
+ sal_Int32 nPar = aTempEngine.GetFieldPar();
+ sal_Int32 nPos = aTempEngine.GetFieldPos();
+ ESelection aSelection( nPar, nPos, nPar, nPos+1 ); // Field size is 1 character
+
+ sal_Int32 eRealType = pData->GetClassId();
+ uno::Reference<text::XTextField> xRet(
+ new ScEditFieldObj(xTextRange, std::make_unique<ScHeaderFooterEditSource>(mrData), eRealType, aSelection));
+ return xRet;
+}
+
+sal_Int32 SAL_CALL ScHeaderFieldsObj::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ //! Field functions have to be passed to the forwarder !!!
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+ return aTempEngine.CountFields();
+}
+
+uno::Any SAL_CALL ScHeaderFieldsObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<text::XTextField> xField(GetObjectByIndex_Impl(nIndex));
+ if (!xField.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xField);
+}
+
+uno::Type SAL_CALL ScHeaderFieldsObj::getElementType()
+{
+ return cppu::UnoType<text::XTextField>::get();
+}
+
+sal_Bool SAL_CALL ScHeaderFieldsObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+uno::Reference<container::XEnumeration> SAL_CALL ScHeaderFieldsObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.text.TextFieldEnumeration");
+}
+
+void SAL_CALL ScHeaderFieldsObj::addContainerListener(
+ const uno::Reference<container::XContainerListener>& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL ScHeaderFieldsObj::removeContainerListener(
+ const uno::Reference<container::XContainerListener>& /* xListener */ )
+{
+ OSL_FAIL("not implemented");
+}
+
+// XRefreshable
+void SAL_CALL ScHeaderFieldsObj::refresh( )
+{
+ std::unique_lock g(aMutex);
+ if (maRefreshListeners.getLength(g))
+ {
+ // Call all listeners.
+ lang::EventObject aEvent;
+ aEvent.Source.set(uno::Reference< util::XRefreshable >(this));
+ maRefreshListeners.notifyEach( g, &util::XRefreshListener::refreshed, aEvent);
+ }
+}
+
+void SAL_CALL ScHeaderFieldsObj::addRefreshListener( const uno::Reference< util::XRefreshListener >& xListener )
+{
+ if (xListener.is())
+ {
+ std::unique_lock g(aMutex);
+ maRefreshListeners.addInterface(g, xListener);
+ }
+}
+
+void SAL_CALL ScHeaderFieldsObj::removeRefreshListener( const uno::Reference<util::XRefreshListener >& xListener )
+{
+ if (xListener.is())
+ {
+ std::unique_lock g(aMutex);
+ maRefreshListeners.removeInterface(g, xListener);
+ }
+}
+
+SvxFieldData& ScEditFieldObj::getData()
+{
+ if (!mpData)
+ {
+ switch (meType)
+ {
+ case text::textfield::Type::DATE:
+ mpData.reset(new SvxDateField);
+ break;
+ case text::textfield::Type::EXTENDED_FILE:
+ mpData.reset(
+ new SvxExtFileField(OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt));
+ break;
+ case text::textfield::Type::PAGE:
+ mpData.reset(new SvxPageField);
+ break;
+ case text::textfield::Type::PAGES:
+ mpData.reset(new SvxPagesField);
+ break;
+ case text::textfield::Type::TABLE:
+ mpData.reset(new SvxTableField);
+ break;
+ case text::textfield::Type::TIME:
+ mpData.reset(new SvxTimeField);
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ {
+ if (mbIsDate)
+ mpData.reset(new SvxDateField);
+ else
+ mpData.reset(new SvxExtTimeField);
+ }
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ mpData.reset(new SvxFileField);
+ break;
+ case text::textfield::Type::URL:
+ mpData.reset(
+ new SvxURLField(OUString(), OUString(), SvxURLFormat::AppDefault));
+ break;
+ default:
+ mpData.reset(new SvxFieldData);
+ }
+ }
+ return *mpData;
+}
+
+void ScEditFieldObj::setPropertyValueURL(const OUString& rName, const css::uno::Any& rVal)
+{
+ OUString aStrVal;
+ if (mpEditSource)
+ {
+ // Edit engine instance already exists for this field item. Use it.
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+
+ // don't care about the type (only URLs can be found in the cells)
+ SvxFieldData* pField = aTempEngine.FindByPos(
+ aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED);
+ OSL_ENSURE(pField,"setPropertyValue: Field not found");
+ if (!pField)
+ return;
+
+ if (pField->GetClassId() != text::textfield::Type::URL)
+ // Make sure this is indeed a URL field.
+ return;
+
+ SvxURLField* pURL = static_cast<SvxURLField*>(pField);
+
+ if (rName == SC_UNONAME_URL)
+ {
+ if (rVal >>= aStrVal)
+ pURL->SetURL(aStrVal);
+ }
+ else if (rName == SC_UNONAME_REPR)
+ {
+ if (rVal >>= aStrVal)
+ pURL->SetRepresentation(aStrVal);
+ }
+ else if (rName == SC_UNONAME_TARGET)
+ {
+ if (rVal >>= aStrVal)
+ pURL->SetTargetFrame(aStrVal);
+ }
+ else
+ throw beans::UnknownPropertyException(rName);
+
+ pEditEngine->QuickInsertField( SvxFieldItem(*pField, EE_FEATURE_FIELD), aSelection );
+ mpEditSource->UpdateData();
+ return;
+ }
+
+ // Edit engine instance not yet present. Store the item data for later use.
+ SvxURLField& rData = static_cast<SvxURLField&>(getData());
+ if (rName == SC_UNONAME_URL)
+ {
+ if (rVal >>= aStrVal)
+ rData.SetURL(aStrVal);
+ }
+ else if (rName == SC_UNONAME_REPR)
+ {
+ if (rVal >>= aStrVal)
+ rData.SetRepresentation(aStrVal);
+ }
+ else if (rName == SC_UNONAME_TARGET)
+ {
+ if (rVal >>= aStrVal)
+ rData.SetTargetFrame(aStrVal);
+ }
+ else
+ throw beans::UnknownPropertyException(rName);
+}
+
+uno::Any ScEditFieldObj::getPropertyValueURL(const OUString& rName)
+{
+ uno::Any aRet;
+
+ // anchor type is always "as character", text wrap always "none"
+
+ if (mpEditSource)
+ {
+ //! Field functions have to be passed to the forwarder !!!
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+
+ // don't care about the type (only URLs can be found in the cells)
+ const SvxFieldData* pField = aTempEngine.FindByPos(
+ aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED);
+ OSL_ENSURE(pField,"getPropertyValue: Field not found");
+ if (!pField)
+ throw uno::RuntimeException();
+
+ if (pField->GetClassId() != text::textfield::Type::URL)
+ throw uno::RuntimeException();
+
+ const SvxURLField* pURL = static_cast<const SvxURLField*>(pField);
+
+ if (rName == SC_UNONAME_URL)
+ aRet <<= pURL->GetURL();
+ else if (rName == SC_UNONAME_REPR)
+ aRet <<= pURL->GetRepresentation();
+ else if (rName == SC_UNONAME_TARGET)
+ aRet <<= pURL->GetTargetFrame();
+ else
+ throw beans::UnknownPropertyException(rName);
+ }
+ else // not inserted yet
+ {
+ const SvxURLField& rURL = static_cast<const SvxURLField&>(getData());
+
+ if (rName == SC_UNONAME_URL)
+ aRet <<= rURL.GetURL();
+ else if (rName == SC_UNONAME_REPR)
+ aRet <<= rURL.GetRepresentation();
+ else if (rName == SC_UNONAME_TARGET)
+ aRet <<= rURL.GetTargetFrame();
+ else
+ throw beans::UnknownPropertyException(rName);
+ }
+ return aRet;
+}
+
+void ScEditFieldObj::setPropertyValueFile(const OUString& rName, const uno::Any& rVal)
+{
+ if (rName != SC_UNONAME_FILEFORM)
+ throw beans::UnknownPropertyException(rName);
+
+ sal_Int16 nIntVal = 0;
+ if (!(rVal >>= nIntVal))
+ return;
+
+ SvxFileFormat eFormat = lcl_UnoToSvxFileFormat(nIntVal);
+ if (mpEditSource)
+ {
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+ SvxFieldData* pField = aTempEngine.FindByPos(
+ aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::EXTENDED_FILE);
+ OSL_ENSURE(pField, "setPropertyValueFile: Field not found");
+ if (pField)
+ {
+ SvxExtFileField* pExtFile = static_cast<SvxExtFileField*>(pField); // local to the ScUnoEditEngine
+ pExtFile->SetFormat(eFormat);
+ pEditEngine->QuickInsertField(SvxFieldItem(*pField, EE_FEATURE_FIELD), aSelection);
+ mpEditSource->UpdateData();
+ }
+ }
+ else
+ {
+ SvxExtFileField& rExtFile = static_cast<SvxExtFileField&>(getData());
+ rExtFile.SetFormat(eFormat);
+ }
+
+}
+
+uno::Any ScEditFieldObj::getPropertyValueFile(const OUString& rName)
+{
+ uno::Any aRet;
+ if (rName != SC_UNONAME_FILEFORM)
+ throw beans::UnknownPropertyException(rName);
+
+ SvxFileFormat eFormat = SvxFileFormat::NameAndExt;
+ const SvxFieldData* pField = nullptr;
+ if (mpEditSource)
+ {
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+ pField = aTempEngine.FindByPos(
+ aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::EXTENDED_FILE);
+ }
+ else
+ pField = &getData();
+
+ OSL_ENSURE(pField, "setPropertyValueFile: Field not found");
+ if (!pField)
+ throw uno::RuntimeException();
+
+ const SvxExtFileField* pExtFile = static_cast<const SvxExtFileField*>(pField);
+ eFormat = pExtFile->GetFormat();
+ sal_Int16 nIntVal = lcl_SvxToUnoFileFormat(eFormat);
+ aRet <<= nIntVal;
+
+ return aRet;
+}
+
+void ScEditFieldObj::setPropertyValueDateTime(const OUString& rName, const uno::Any& rVal)
+{
+ if (mpEditSource)
+ {
+ // Field already inserted.
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+ SvxFieldData* pField = aTempEngine.FindByPos(aSelection.nStartPara, aSelection.nStartPos, meType);
+ if (!pField)
+ return;
+
+ switch (meType)
+ {
+ case text::textfield::Type::DATE:
+ {
+ SvxDateField* p = static_cast<SvxDateField*>(pField);
+ if (rName == SC_UNONAME_ISDATE)
+ {
+ // Do nothing for now.
+ }
+ else if (rName == SC_UNONAME_ISFIXED)
+ {
+ SvxDateType eType = rVal.get<bool>() ? SvxDateType::Fix : SvxDateType::Var;
+ p->SetType(eType);
+ }
+ else if (rName == SC_UNONAME_DATETIME)
+ {
+ maDateTime = rVal.get<util::DateTime>();
+ Date aDate(maDateTime.Day, maDateTime.Month, maDateTime.Year);
+ p->SetFixDate(aDate);
+ }
+ else if (rName == SC_UNONAME_NUMFMT)
+ {
+ mnNumFormat = rVal.get<sal_Int32>();
+ p->SetFormat(static_cast<SvxDateFormat>(mnNumFormat));
+ }
+ else
+ throw beans::UnknownPropertyException(rName);
+ }
+ break;
+ case text::textfield::Type::TIME:
+ {
+ // SvxTimeField doesn't have any attributes.
+ if (rName != SC_UNONAME_ISDATE && rName != SC_UNONAME_ISFIXED &&
+ rName != SC_UNONAME_DATETIME && rName != SC_UNONAME_NUMFMT)
+ throw beans::UnknownPropertyException(rName);
+ }
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ {
+ SvxExtTimeField* p = static_cast<SvxExtTimeField*>(pField);
+ if (rName == SC_UNONAME_ISDATE)
+ {
+ // Do nothing for now.
+ }
+ else if (rName == SC_UNONAME_ISFIXED)
+ {
+ SvxTimeType eType = rVal.get<bool>() ? SvxTimeType::Fix : SvxTimeType::Var;
+ p->SetType(eType);
+ }
+ else if (rName == SC_UNONAME_DATETIME)
+ {
+ maDateTime = rVal.get<util::DateTime>();
+ tools::Time aTime(maDateTime);
+ p->SetFixTime(aTime);
+ }
+ else if (rName == SC_UNONAME_NUMFMT)
+ {
+ mnNumFormat = rVal.get<sal_Int32>();
+ p->SetFormat(static_cast<SvxTimeFormat>(mnNumFormat));
+ }
+ else
+ throw beans::UnknownPropertyException(rName);
+ }
+ break;
+ default:
+ throw beans::UnknownPropertyException(rName);
+ }
+ }
+ else
+ {
+ if (rName == SC_UNONAME_ISDATE)
+ mbIsDate = rVal.get<bool>();
+ else if (rName == SC_UNONAME_ISFIXED)
+ mbIsFixed = rVal.get<bool>();
+ else if (rName == SC_UNONAME_DATETIME)
+ maDateTime = rVal.get<util::DateTime>();
+ else if (rName == SC_UNONAME_NUMFMT)
+ mnNumFormat = rVal.get<sal_Int32>();
+ else
+ throw beans::UnknownPropertyException(rName);
+ }
+}
+
+uno::Any ScEditFieldObj::getPropertyValueDateTime(const OUString& rName)
+{
+ if (mpEditSource)
+ {
+ // Field already inserted.
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+ SvxFieldData* pField = aTempEngine.FindByPos(aSelection.nStartPara, aSelection.nStartPos, meType);
+ if (!pField)
+ throw uno::RuntimeException();
+
+ switch (meType)
+ {
+ case text::textfield::Type::DATE:
+ {
+ SvxDateField* p = static_cast<SvxDateField*>(pField);
+ if (rName == SC_UNONAME_ISDATE)
+ return uno::Any(true);
+
+ if (rName == SC_UNONAME_ISFIXED)
+ return uno::Any(p->GetType() == SvxDateType::Fix);
+
+ if (rName == SC_UNONAME_DATETIME)
+ {
+ Date aD(p->GetFixDate());
+ maDateTime.Year = aD.GetYear();
+ maDateTime.Month = aD.GetMonth();
+ maDateTime.Day = aD.GetDay();
+ maDateTime.Hours = 0;
+ maDateTime.Minutes = 0;
+ maDateTime.Seconds = 0;
+ maDateTime.NanoSeconds = 0;
+ return uno::Any(maDateTime);
+ }
+
+ if (rName == SC_UNONAME_NUMFMT)
+ return uno::Any(static_cast<sal_Int32>(p->GetFormat()));
+ }
+ break;
+ case text::textfield::Type::TIME:
+ {
+ // SvxTimeField doesn't have any attributes.
+ if (rName == SC_UNONAME_ISDATE)
+ return uno::Any(false);
+
+ if (rName == SC_UNONAME_ISFIXED)
+ return uno::Any(false);
+
+ if (rName == SC_UNONAME_DATETIME)
+ // This is the best we can do.
+ return uno::Any(maDateTime);
+
+ if (rName == SC_UNONAME_NUMFMT)
+ // Same as above.
+ return uno::Any(sal_Int32(0));
+ }
+ break;
+ case text::textfield::Type::EXTENDED_TIME:
+ {
+ SvxExtTimeField* p = static_cast<SvxExtTimeField*>(pField);
+ if (rName == SC_UNONAME_ISDATE)
+ return uno::Any(false);
+
+ if (rName == SC_UNONAME_ISFIXED)
+ return uno::Any(p->GetType() == SvxTimeType::Fix);
+
+ if (rName == SC_UNONAME_DATETIME)
+ {
+ tools::Time aT(p->GetFixTime());
+ maDateTime.Year = 0;
+ maDateTime.Month = 0;
+ maDateTime.Day = 0;
+ maDateTime.Hours = aT.GetHour();
+ maDateTime.Minutes = aT.GetMin();
+ maDateTime.Seconds = aT.GetSec();
+ maDateTime.NanoSeconds = aT.GetNanoSec();
+ return uno::Any(maDateTime);
+ }
+
+ if (rName == SC_UNONAME_NUMFMT)
+ return uno::Any(static_cast<sal_Int32>(p->GetFormat()));
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ {
+ if (rName == SC_UNONAME_ISDATE)
+ return uno::Any(mbIsDate);
+
+ if (rName == SC_UNONAME_ISFIXED)
+ return uno::Any(mbIsFixed);
+
+ if (rName == SC_UNONAME_DATETIME)
+ return uno::Any(maDateTime);
+
+ if (rName == SC_UNONAME_NUMFMT)
+ return uno::Any(mnNumFormat);
+ }
+
+ throw beans::UnknownPropertyException(rName);
+}
+
+void ScEditFieldObj::setPropertyValueSheet(const OUString& rName, const uno::Any& rVal)
+{
+ if (mpEditSource)
+ {
+ // Edit engine instance already exists for this field item. Use it.
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+
+ // don't care about the type (only URLs can be found in the cells)
+ SvxFieldData* pField = aTempEngine.FindByPos(
+ aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED);
+ OSL_ENSURE(pField,"setPropertyValue: Field not found");
+ if (!pField)
+ return;
+
+ if (pField->GetClassId() != text::textfield::Type::TABLE)
+ // Make sure this is indeed a URL field.
+ return;
+
+ SvxTableField* p = static_cast<SvxTableField*>(pField);
+
+ if (rName != SC_UNONAME_TABLEPOS)
+ throw beans::UnknownPropertyException(rName);
+
+ sal_Int32 nTab = rVal.get<sal_Int32>();
+ p->SetTab(nTab);
+
+
+ pEditEngine->QuickInsertField(SvxFieldItem(*pField, EE_FEATURE_FIELD), aSelection);
+ mpEditSource->UpdateData();
+ return;
+ }
+
+ // Edit engine instance not yet present. Store the item data for later use.
+ SvxTableField& r = static_cast<SvxTableField&>(getData());
+ if (rName != SC_UNONAME_TABLEPOS)
+ throw beans::UnknownPropertyException(rName);
+
+ sal_Int32 nTab = rVal.get<sal_Int32>();
+ r.SetTab(nTab);
+}
+
+ScEditFieldObj::ScEditFieldObj(
+ uno::Reference<text::XTextRange> xContent,
+ std::unique_ptr<ScEditSource> pEditSrc, sal_Int32 eType, const ESelection& rSel) :
+ pPropSet(nullptr),
+ mpEditSource(std::move(pEditSrc)),
+ aSelection(rSel),
+ meType(eType), mpContent(std::move(xContent)), mnNumFormat(0), mbIsDate(false), mbIsFixed(false)
+{
+ switch (meType)
+ {
+ case text::textfield::Type::DOCINFO_TITLE:
+ pPropSet = getEmptyPropertySet();
+ break;
+ case text::textfield::Type::EXTENDED_FILE:
+ pPropSet = lcl_GetFileFieldPropertySet();
+ break;
+ case text::textfield::Type::URL:
+ pPropSet = lcl_GetURLPropertySet();
+ break;
+ case text::textfield::Type::DATE:
+ case text::textfield::Type::TIME:
+ case text::textfield::Type::EXTENDED_TIME:
+ pPropSet = getDateTimePropertySet();
+ break;
+ default:
+ pPropSet = lcl_GetHeaderFieldPropertySet();
+ }
+
+ if (meType == text::textfield::Type::DATE)
+ mbIsDate = true;
+}
+
+void ScEditFieldObj::InitDoc(
+ const uno::Reference<text::XTextRange>& rContent, std::unique_ptr<ScEditSource> pEditSrc, const ESelection& rSel)
+{
+ if (!mpEditSource)
+ {
+ mpContent = rContent;
+ mpData.reset();
+
+ aSelection = rSel;
+ mpEditSource = std::move( pEditSrc );
+ }
+}
+
+ScEditFieldObj::~ScEditFieldObj()
+{
+}
+
+SvxFieldItem ScEditFieldObj::CreateFieldItem()
+{
+ OSL_ENSURE( !mpEditSource, "CreateFieldItem with inserted field" );
+ return SvxFieldItem(getData(), EE_FEATURE_FIELD);
+}
+
+void ScEditFieldObj::DeleteField()
+{
+ if (mpEditSource)
+ {
+ SvxTextForwarder* pForwarder = mpEditSource->GetTextForwarder();
+ pForwarder->QuickInsertText( OUString(), aSelection );
+ mpEditSource->UpdateData();
+
+ aSelection.nEndPara = aSelection.nStartPara;
+ aSelection.nEndPos = aSelection.nStartPos;
+
+ //! Broadcast in order to adjust selection in other objects
+ //! (also for other actions)
+ }
+}
+
+bool ScEditFieldObj::IsInserted() const
+{
+ return mpEditSource != nullptr;
+}
+
+// XTextField
+
+OUString SAL_CALL ScEditFieldObj::getPresentation( sal_Bool bShowCommand )
+{
+ SolarMutexGuard aGuard;
+
+ if (!mpEditSource)
+ return OUString();
+
+ //! Field functions have to be passed to the forwarder !!!
+ ScEditEngineDefaulter* pEditEngine = mpEditSource->GetEditEngine();
+ ScUnoEditEngine aTempEngine(pEditEngine);
+
+ // don't care about the type (only URLs can be found in the cells)
+ const SvxFieldData* pField = aTempEngine.FindByPos(
+ aSelection.nStartPara, aSelection.nStartPos, text::textfield::Type::UNSPECIFIED);
+ OSL_ENSURE(pField,"getPresentation: Field not found");
+ if (!pField)
+ return OUString();
+
+ switch (meType)
+ {
+ case text::textfield::Type::URL:
+ {
+ if (pField->GetClassId() != text::textfield::Type::URL)
+ // Not a URL field, but URL is expected.
+ throw uno::RuntimeException();
+
+ const SvxURLField* pURL = static_cast<const SvxURLField*>(pField);
+ return bShowCommand ? pURL->GetURL() : pURL->GetRepresentation();
+ }
+ break;
+ default:
+ ;
+ }
+ return OUString();
+}
+
+// XTextContent
+
+void SAL_CALL ScEditFieldObj::attach( const uno::Reference<text::XTextRange>& xTextRange )
+{
+ SolarMutexGuard aGuard;
+ if (xTextRange.is())
+ {
+ uno::Reference<text::XText> xText(xTextRange->getText());
+ if (xText.is())
+ {
+ xText->insertTextContent( xTextRange, this, true );
+ }
+ }
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScEditFieldObj::getAnchor()
+{
+ SolarMutexGuard aGuard;
+ return mpContent;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScEditFieldObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<beans::XPropertySetInfo> aRef = pPropSet->getPropertySetInfo();
+ return aRef;
+}
+
+void SAL_CALL ScEditFieldObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if (aPropertyName == SC_UNONAME_ANCHOR)
+ {
+ aValue >>= mpContent;
+ return;
+ }
+
+ switch (meType)
+ {
+ case text::textfield::Type::URL:
+ setPropertyValueURL(aPropertyName, aValue);
+ break;
+ case text::textfield::Type::EXTENDED_FILE:
+ setPropertyValueFile(aPropertyName, aValue);
+ break;
+ case text::textfield::Type::DATE:
+ case text::textfield::Type::TIME:
+ case text::textfield::Type::EXTENDED_TIME:
+ setPropertyValueDateTime(aPropertyName, aValue);
+ break;
+ case text::textfield::Type::TABLE:
+ setPropertyValueSheet(aPropertyName, aValue);
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ default:
+ throw beans::UnknownPropertyException(OUString::number(meType));
+ }
+}
+
+uno::Any SAL_CALL ScEditFieldObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if (aPropertyName == SC_UNONAME_TEXTFIELD_TYPE)
+ return uno::Any(meType);
+
+ if (aPropertyName == SC_UNONAME_ANCHOR)
+ return uno::Any(mpContent);
+
+ if (aPropertyName == SC_UNONAME_ANCTYPE)
+ {
+ uno::Any aRet;
+ aRet <<= text::TextContentAnchorType_AS_CHARACTER;
+ return aRet;
+ }
+ if (aPropertyName == SC_UNONAME_ANCTYPES)
+ {
+ uno::Any aRet;
+ uno::Sequence<text::TextContentAnchorType> aSeq { text::TextContentAnchorType_AS_CHARACTER };
+ aRet <<= aSeq;
+ return aRet;
+ }
+ if (aPropertyName == SC_UNONAME_TEXTWRAP)
+ {
+ uno::Any aRet;
+ aRet <<= text::WrapTextMode_NONE;
+ return aRet;
+ }
+
+ switch (meType)
+ {
+ case text::textfield::Type::URL:
+ return getPropertyValueURL(aPropertyName);
+ case text::textfield::Type::EXTENDED_FILE:
+ return getPropertyValueFile(aPropertyName);
+ case text::textfield::Type::DATE:
+ case text::textfield::Type::TIME:
+ case text::textfield::Type::EXTENDED_TIME:
+ return getPropertyValueDateTime(aPropertyName);
+ case text::textfield::Type::DOCINFO_TITLE:
+ default:
+ throw beans::UnknownPropertyException(OUString::number(meType));
+ }
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScEditFieldObj )
+
+// XServiceInfo
+
+OUString SAL_CALL ScEditFieldObj::getImplementationName()
+{
+ return "ScEditFieldObj";
+}
+
+sal_Bool SAL_CALL ScEditFieldObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScEditFieldObj::getSupportedServiceNames()
+{
+ return {"com.sun.star.text.TextField",
+ "com.sun.star.text.TextContent"};
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScEditFieldObj::getTypes()
+{
+ return comphelper::concatSequences(
+ ScEditFieldObj_Base::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<text::XTextField>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get()
+ } );
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScEditFieldObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/filtuno.cxx b/sc/source/ui/unoobj/filtuno.cxx
new file mode 100644
index 0000000000..fa520e23ae
--- /dev/null
+++ b/sc/source/ui/unoobj/filtuno.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 <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <connectivity/dbtools.hxx>
+#include <osl/diagnose.h>
+
+#include <filtuno.hxx>
+#include <miscuno.hxx>
+#include <scdll.hxx>
+#include <imoptdlg.hxx>
+#include <asciiopt.hxx>
+#include <docsh.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <scabstdlg.hxx>
+#include <i18nlangtag/lang.h>
+
+#include <optutil.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace connectivity::dbase;
+
+constexpr OUString SCFILTEROPTIONSOBJ_SERVICE = u"com.sun.star.ui.dialogs.FilterOptionsDialog"_ustr;
+constexpr OUStringLiteral SCFILTEROPTIONSOBJ_IMPLNAME = u"com.sun.star.comp.Calc.FilterOptionsDialog";
+
+SC_SIMPLE_SERVICE_INFO( ScFilterOptionsObj, SCFILTEROPTIONSOBJ_IMPLNAME, SCFILTEROPTIONSOBJ_SERVICE )
+
+constexpr OUStringLiteral SC_UNONAME_FILENAME = u"URL";
+constexpr OUStringLiteral SC_UNONAME_FILTERNAME = u"FilterName";
+constexpr OUString SC_UNONAME_FILTEROPTIONS = u"FilterOptions"_ustr;
+constexpr OUStringLiteral SC_UNONAME_INPUTSTREAM = u"InputStream";
+
+constexpr OUString DBF_CHAR_SET = u"CharSet"_ustr;
+constexpr OUString DBF_SEP_PATH_IMPORT = u"Office.Calc/Dialogs/DBFImport"_ustr;
+constexpr OUString DBF_SEP_PATH_EXPORT = u"Office.Calc/Dialogs/DBFExport"_ustr;
+
+namespace
+{
+
+ enum class charsetSource
+ {
+ charset_from_file,
+ charset_from_user_setting,
+ charset_default
+ };
+
+ charsetSource load_CharSet(rtl_TextEncoding &nCharSet, bool bExport, SvStream* dbf_Stream)
+ {
+ if (dbf_Stream && dbfReadCharset(nCharSet, dbf_Stream))
+ {
+ return charsetSource::charset_from_file;
+ }
+
+ Sequence<Any> aValues;
+ const Any *pProperties;
+ Sequence<OUString> aNames { DBF_CHAR_SET };
+ ScLinkConfigItem aItem( bExport ? DBF_SEP_PATH_EXPORT : DBF_SEP_PATH_IMPORT );
+
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getConstArray();
+
+ if( pProperties[0].hasValue() )
+ {
+ sal_Int32 nChar = 0;
+ pProperties[0] >>= nChar;
+ if( nChar >= 0)
+ {
+ nCharSet = static_cast<rtl_TextEncoding>(nChar);
+ return charsetSource::charset_from_user_setting;
+ }
+ }
+
+ // Default choice
+ nCharSet = RTL_TEXTENCODING_IBM_850;
+ return charsetSource::charset_default;
+ }
+
+ void save_CharSet( rtl_TextEncoding nCharSet, bool bExport )
+ {
+ Sequence<Any> aValues;
+ Any *pProperties;
+ Sequence<OUString> aNames { DBF_CHAR_SET };
+ ScLinkConfigItem aItem( bExport ? DBF_SEP_PATH_EXPORT : DBF_SEP_PATH_IMPORT );
+
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getArray();
+ pProperties[0] <<= static_cast<sal_Int32>(nCharSet);
+
+ aItem.PutProperties(aNames, aValues);
+ }
+}
+
+ScFilterOptionsObj::ScFilterOptionsObj() :
+ bExport( false )
+{
+}
+
+ScFilterOptionsObj::~ScFilterOptionsObj()
+{
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_FilterOptionsDialog_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ return cppu::acquire(new ScFilterOptionsObj);
+}
+
+// XPropertyAccess
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScFilterOptionsObj::getPropertyValues()
+{
+ return comphelper::InitPropertySequence({
+ { SC_UNONAME_FILTEROPTIONS, Any(aFilterOptions) }
+ });
+}
+
+void SAL_CALL ScFilterOptionsObj::setPropertyValues( const uno::Sequence<beans::PropertyValue>& aProps )
+{
+ for (const beans::PropertyValue& rProp : aProps)
+ {
+ OUString aPropName(rProp.Name);
+
+ if ( aPropName == SC_UNONAME_FILENAME )
+ rProp.Value >>= aFileName;
+ else if ( aPropName == SC_UNONAME_FILTERNAME )
+ rProp.Value >>= aFilterName;
+ else if ( aPropName == SC_UNONAME_FILTEROPTIONS )
+ rProp.Value >>= aFilterOptions;
+ else if ( aPropName == SC_UNONAME_INPUTSTREAM )
+ rProp.Value >>= xInputStream;
+ }
+}
+
+// XExecutableDialog
+
+void SAL_CALL ScFilterOptionsObj::setTitle( const OUString& /* aTitle */ )
+{
+ // not used
+}
+
+sal_Int16 SAL_CALL ScFilterOptionsObj::execute()
+{
+ sal_Int16 nRet = ui::dialogs::ExecutableDialogResults::CANCEL;
+
+ OUString aFilterString( aFilterName );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ if ( !bExport && aFilterString == ScDocShell::GetAsciiFilterName() )
+ {
+ // ascii import is special...
+
+ INetURLObject aURL( aFileName );
+ // tdf#132421 - don't URL encode filename for the import ASCII dialog title
+ OUString aPrivDatName(aURL.GetLastName(INetURLObject::DecodeMechanism::Unambiguous));
+ std::unique_ptr<SvStream> pInStream;
+ if ( xInputStream.is() )
+ pInStream = utl::UcbStreamHelper::CreateStream( xInputStream );
+
+ ScopedVclPtr<AbstractScImportAsciiDlg> pDlg(pFact->CreateScImportAsciiDlg(Application::GetFrameWeld(xDialogParent), aPrivDatName,
+ pInStream.get(), SC_IMPORTFILE));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions( aOptions );
+ pDlg->SaveParameters();
+ aFilterOptions = aOptions.WriteToString();
+ nRet = ui::dialogs::ExecutableDialogResults::OK;
+ }
+ }
+ else if ( aFilterString == ScDocShell::GetWebQueryFilterName() || aFilterString == ScDocShell::GetHtmlFilterName() )
+ {
+ if (bExport)
+ nRet = ui::dialogs::ExecutableDialogResults::OK; // export HTML without dialog
+ else
+ {
+ // HTML import.
+ ScopedVclPtr<AbstractScTextImportOptionsDlg> pDlg(
+ pFact->CreateScTextImportOptionsDlg(Application::GetFrameWeld(xDialogParent)));
+
+ if (pDlg->Execute() == RET_OK)
+ {
+ LanguageType eLang = pDlg->GetLanguageType();
+ OUStringBuffer aBuf;
+
+ aBuf.append(static_cast<sal_Int32>(static_cast<sal_uInt16>(eLang)));
+ aBuf.append(' ');
+ aBuf.append(pDlg->IsDateConversionSet() ? u'1' : u'0');
+ aBuf.append(' ');
+ aBuf.append(pDlg->IsScientificConversionSet() ? u'1' : u'0');
+ aFilterOptions = aBuf.makeStringAndClear();
+ nRet = ui::dialogs::ExecutableDialogResults::OK;
+ }
+ }
+ }
+ else
+ {
+ bool bDBEnc = false;
+ bool bAscii = false;
+ bool skipDialog = false;
+
+ sal_Unicode const cStrDel = '"';
+ sal_Unicode cAsciiDel = ';';
+ rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
+
+ OUString aTitle;
+ bool bIncludeBOM = false;
+
+ if ( aFilterString == ScDocShell::GetAsciiFilterName() )
+ {
+ // ascii export (import is handled above)
+
+ INetURLObject aURL( aFileName );
+ OUString aExt(aURL.getExtension());
+ if (aExt.equalsIgnoreAsciiCase("CSV"))
+ cAsciiDel = ',';
+ else
+ cAsciiDel = '\t';
+
+ aTitle = ScResId( STR_EXPORT_ASCII );
+ bAscii = true;
+
+ ScAsciiOptions aOptions;
+ aOptions.ReadFromString(aFilterOptions);
+ bIncludeBOM = aOptions.GetIncludeBOM();
+ }
+ else if ( aFilterString == ScDocShell::GetLotusFilterName() )
+ {
+ // lotus is only imported
+ OSL_ENSURE( !bExport, "Filter Options for Lotus Export is not implemented" );
+
+ aTitle = ScResId( STR_IMPORT_LOTUS );
+ eEncoding = RTL_TEXTENCODING_IBM_437;
+ }
+ else if ( aFilterString == ScDocShell::GetDBaseFilterName() )
+ {
+ if ( bExport )
+ {
+ // dBase export
+ aTitle = ScResId( STR_EXPORT_DBF );
+ }
+ else
+ {
+ // dBase import
+ aTitle = ScResId( STR_IMPORT_DBF );
+ }
+
+ std::unique_ptr<SvStream> pInStream;
+ if ( xInputStream.is() )
+ pInStream = utl::UcbStreamHelper::CreateStream( xInputStream );
+ switch(load_CharSet( eEncoding, bExport, pInStream.get()))
+ {
+ case charsetSource::charset_from_file:
+ skipDialog = true;
+ break;
+ case charsetSource::charset_from_user_setting:
+ case charsetSource::charset_default:
+ break;
+ }
+ bDBEnc = true;
+ // pInStream goes out of scope, the stream is automatically closed
+ }
+ else if ( aFilterString == ScDocShell::GetDifFilterName() )
+ {
+ if ( bExport )
+ {
+ // DIF export
+ aTitle = ScResId( STR_EXPORT_DIF );
+ }
+ else
+ {
+ // DIF import
+ aTitle = ScResId( STR_IMPORT_DIF );
+ }
+ // common for DIF import/export
+ eEncoding = RTL_TEXTENCODING_MS_1252;
+ }
+
+ ScImportOptions aOptions( cAsciiDel, cStrDel, eEncoding);
+ aOptions.bIncludeBOM = bIncludeBOM;
+ if(skipDialog)
+ {
+ // TODO: check we are not missing some of the stuff that ScImportOptionsDlg::GetImportOptions
+ // (file sc/source/ui/dbgui/scuiimoptdlg.cxx) does
+ // that is, if the dialog sets options that are not selected by the user (!)
+ // then we are missing them here.
+ // Then we may need to rip them out of the dialog.
+ // Or we actually change the dialog to not display if skipDialog==true
+ // in that case, add an argument skipDialog to CreateScImportOptionsDlg
+ nRet = ui::dialogs::ExecutableDialogResults::OK;
+ }
+ else
+ {
+ ScopedVclPtr<AbstractScImportOptionsDlg> pDlg(pFact->CreateScImportOptionsDlg(Application::GetFrameWeld(xDialogParent),
+ bAscii, &aOptions, &aTitle,
+ bDBEnc, !bExport));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ pDlg->SaveImportOptions();
+ pDlg->GetImportOptions( aOptions );
+ save_CharSet( aOptions.eCharSet, bExport );
+ nRet = ui::dialogs::ExecutableDialogResults::OK;
+ }
+ }
+ if (nRet == ui::dialogs::ExecutableDialogResults::OK)
+ {
+ if ( bAscii )
+ aFilterOptions = aOptions.BuildString();
+ else
+ aFilterOptions = aOptions.aStrFont;
+ }
+ }
+
+ xInputStream.clear(); // don't hold the stream longer than necessary
+
+ return nRet;
+}
+
+// XImporter
+
+void SAL_CALL ScFilterOptionsObj::setTargetDocument( const uno::Reference<lang::XComponent>& /* xDoc */ )
+{
+ bExport = false;
+}
+
+// XExporter
+
+void SAL_CALL ScFilterOptionsObj::setSourceDocument( const uno::Reference<lang::XComponent>& /* xDoc */ )
+{
+ bExport = true;
+}
+
+// XInitialization
+
+void SAL_CALL ScFilterOptionsObj::initialize(const uno::Sequence<uno::Any>& rArguments)
+{
+ ::comphelper::NamedValueCollection aProperties(rArguments);
+ if (aProperties.has("ParentWindow"))
+ aProperties.get("ParentWindow") >>= xDialogParent;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/fmtuno.cxx b/sc/source/ui/unoobj/fmtuno.cxx
new file mode 100644
index 0000000000..1b91855917
--- /dev/null
+++ b/sc/source/ui/unoobj/fmtuno.cxx
@@ -0,0 +1,913 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/style.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/sheet/ConditionOperator2.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+
+#include <fmtuno.hxx>
+#include <miscuno.hxx>
+#include <validat.hxx>
+#include <document.hxx>
+#include <unonames.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <stylehelper.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::formula;
+
+// map only for PropertySetInfo
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetValidatePropertyMap()
+{
+ static const SfxItemPropertyMapEntry aValidatePropertyMap_Impl[] =
+ {
+ { SC_UNONAME_ERRALSTY, 0, cppu::UnoType<sheet::ValidationAlertStyle>::get(), 0, 0},
+ { SC_UNONAME_ERRMESS, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNONAME_ERRTITLE, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNONAME_IGNOREBL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_INPMESS, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNONAME_INPTITLE, 0, cppu::UnoType<OUString>::get(), 0, 0},
+ { SC_UNONAME_SHOWERR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_SHOWINP, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNONAME_SHOWLIST, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNONAME_TYPE, 0, cppu::UnoType<sheet::ValidationType>::get(), 0, 0},
+ };
+ return aValidatePropertyMap_Impl;
+}
+
+SC_SIMPLE_SERVICE_INFO( ScTableConditionalEntry, "ScTableConditionalEntry", "com.sun.star.sheet.TableConditionalEntry" )
+SC_SIMPLE_SERVICE_INFO( ScTableConditionalFormat, "ScTableConditionalFormat", "com.sun.star.sheet.TableConditionalFormat" )
+SC_SIMPLE_SERVICE_INFO( ScTableValidationObj, "ScTableValidationObj", "com.sun.star.sheet.TableValidation" )
+
+static sal_Int32 lcl_ConditionModeToOperatorNew( ScConditionMode eMode )
+{
+ sal_Int32 eOper = sheet::ConditionOperator2::NONE;
+ switch (eMode)
+ {
+ case ScConditionMode::Equal: eOper = sheet::ConditionOperator2::EQUAL; break;
+ case ScConditionMode::Less: eOper = sheet::ConditionOperator2::LESS; break;
+ case ScConditionMode::Greater: eOper = sheet::ConditionOperator2::GREATER; break;
+ case ScConditionMode::EqLess: eOper = sheet::ConditionOperator2::LESS_EQUAL; break;
+ case ScConditionMode::EqGreater: eOper = sheet::ConditionOperator2::GREATER_EQUAL; break;
+ case ScConditionMode::NotEqual: eOper = sheet::ConditionOperator2::NOT_EQUAL; break;
+ case ScConditionMode::Between: eOper = sheet::ConditionOperator2::BETWEEN; break;
+ case ScConditionMode::NotBetween: eOper = sheet::ConditionOperator2::NOT_BETWEEN; break;
+ case ScConditionMode::Direct: eOper = sheet::ConditionOperator2::FORMULA; break;
+ case ScConditionMode::Duplicate: eOper = sheet::ConditionOperator2::DUPLICATE; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return eOper;
+}
+
+static sheet::ConditionOperator lcl_ConditionModeToOperator( ScConditionMode eMode )
+{
+ sheet::ConditionOperator eOper = sheet::ConditionOperator_NONE;
+ switch (eMode)
+ {
+ case ScConditionMode::Equal: eOper = sheet::ConditionOperator_EQUAL; break;
+ case ScConditionMode::Less: eOper = sheet::ConditionOperator_LESS; break;
+ case ScConditionMode::Greater: eOper = sheet::ConditionOperator_GREATER; break;
+ case ScConditionMode::EqLess: eOper = sheet::ConditionOperator_LESS_EQUAL; break;
+ case ScConditionMode::EqGreater: eOper = sheet::ConditionOperator_GREATER_EQUAL; break;
+ case ScConditionMode::NotEqual: eOper = sheet::ConditionOperator_NOT_EQUAL; break;
+ case ScConditionMode::Between: eOper = sheet::ConditionOperator_BETWEEN; break;
+ case ScConditionMode::NotBetween: eOper = sheet::ConditionOperator_NOT_BETWEEN; break;
+ case ScConditionMode::Direct: eOper = sheet::ConditionOperator_FORMULA; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return eOper;
+}
+
+static ScConditionMode lcl_ConditionOperatorToMode( sheet::ConditionOperator eOper )
+{
+ ScConditionMode eMode = ScConditionMode::NONE;
+ switch (eOper)
+ {
+ case sheet::ConditionOperator_EQUAL: eMode = ScConditionMode::Equal; break;
+ case sheet::ConditionOperator_LESS: eMode = ScConditionMode::Less; break;
+ case sheet::ConditionOperator_GREATER: eMode = ScConditionMode::Greater; break;
+ case sheet::ConditionOperator_LESS_EQUAL: eMode = ScConditionMode::EqLess; break;
+ case sheet::ConditionOperator_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break;
+ case sheet::ConditionOperator_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break;
+ case sheet::ConditionOperator_BETWEEN: eMode = ScConditionMode::Between; break;
+ case sheet::ConditionOperator_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break;
+ case sheet::ConditionOperator_FORMULA: eMode = ScConditionMode::Direct; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return eMode;
+}
+
+ScCondFormatEntryItem::ScCondFormatEntryItem() :
+ meGrammar1( FormulaGrammar::GRAM_UNSPECIFIED ),
+ meGrammar2( FormulaGrammar::GRAM_UNSPECIFIED ),
+ meMode( ScConditionMode::NONE )
+{
+}
+
+ScTableConditionalFormat::ScTableConditionalFormat(
+ const ScDocument* pDoc, sal_uLong nKey, SCTAB nTab, FormulaGrammar::Grammar eGrammar)
+{
+ // read the entry from the document...
+
+ if ( !(pDoc && nKey) )
+ return;
+
+ ScConditionalFormatList* pList = pDoc->GetCondFormList(nTab);
+ if (!pList)
+ return;
+
+ const ScConditionalFormat* pFormat = pList->GetFormat( nKey );
+ if (!pFormat)
+ return;
+
+ // During save to XML.
+ if (pDoc->IsInExternalReferenceMarking())
+ pFormat->MarkUsedExternalReferences();
+
+ size_t nEntryCount = pFormat->size();
+ for (size_t i=0; i<nEntryCount; i++)
+ {
+ ScCondFormatEntryItem aItem;
+ const ScFormatEntry* pFrmtEntry = pFormat->GetEntry(i);
+ if(pFrmtEntry->GetType() != ScFormatEntry::Type::Condition &&
+ pFrmtEntry->GetType() != ScFormatEntry::Type::ExtCondition)
+ continue;
+
+ const ScCondFormatEntry* pFormatEntry = static_cast<const ScCondFormatEntry*>(pFrmtEntry);
+ aItem.meMode = pFormatEntry->GetOperation();
+ aItem.maPos = pFormatEntry->GetValidSrcPos();
+ aItem.maExpr1 = pFormatEntry->GetExpression(aItem.maPos, 0, 0, eGrammar);
+ aItem.maExpr2 = pFormatEntry->GetExpression(aItem.maPos, 1, 0, eGrammar);
+ aItem.meGrammar1 = aItem.meGrammar2 = eGrammar;
+ aItem.maStyle = pFormatEntry->GetStyle();
+
+ AddEntry_Impl(aItem);
+ }
+}
+
+namespace {
+
+FormulaGrammar::Grammar lclResolveGrammar( FormulaGrammar::Grammar eExtGrammar, FormulaGrammar::Grammar eIntGrammar )
+{
+ if( eExtGrammar != FormulaGrammar::GRAM_UNSPECIFIED )
+ return eExtGrammar;
+ OSL_ENSURE( eIntGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "lclResolveGrammar - unspecified grammar, using GRAM_API" );
+ return (eIntGrammar == FormulaGrammar::GRAM_UNSPECIFIED) ? FormulaGrammar::GRAM_API : eIntGrammar;
+}
+
+} // namespace
+
+void ScTableConditionalFormat::FillFormat( ScConditionalFormat& rFormat,
+ ScDocument& rDoc, FormulaGrammar::Grammar eGrammar) const
+{
+ // ScConditionalFormat = Core-Struktur, has to be empty
+
+ OSL_ENSURE( rFormat.IsEmpty(), "FillFormat: format not empty" );
+
+ for (const auto & i : maEntries)
+ {
+ ScCondFormatEntryItem aData;
+ i->GetData(aData);
+
+ FormulaGrammar::Grammar eGrammar1 = lclResolveGrammar( eGrammar, aData.meGrammar1 );
+ FormulaGrammar::Grammar eGrammar2 = lclResolveGrammar( eGrammar, aData.meGrammar2 );
+
+ ScCondFormatEntry* pCoreEntry = new ScCondFormatEntry( aData.meMode, aData.maExpr1, aData.maExpr2,
+ rDoc, aData.maPos, aData.maStyle, aData.maExprNmsp1, aData.maExprNmsp2, eGrammar1, eGrammar2 );
+
+ if ( !aData.maPosStr.isEmpty() )
+ pCoreEntry->SetSrcString( aData.maPosStr );
+
+ if ( aData.maTokens1.hasElements() )
+ {
+ ScTokenArray aTokenArray(rDoc);
+ if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aData.maTokens1) )
+ pCoreEntry->SetFormula1(aTokenArray);
+ }
+
+ if ( aData.maTokens2.hasElements() )
+ {
+ ScTokenArray aTokenArray(rDoc);
+ if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aData.maTokens2) )
+ pCoreEntry->SetFormula2(aTokenArray);
+ }
+ rFormat.AddEntry( pCoreEntry );
+ }
+}
+
+ScTableConditionalFormat::~ScTableConditionalFormat()
+{
+}
+
+void ScTableConditionalFormat::AddEntry_Impl(const ScCondFormatEntryItem& aEntry)
+{
+ rtl::Reference<ScTableConditionalEntry> pNew = new ScTableConditionalEntry(aEntry);
+ maEntries.emplace_back(pNew);
+}
+
+// XSheetConditionalFormat
+
+ScTableConditionalEntry* ScTableConditionalFormat::GetObjectByIndex_Impl(sal_uInt16 nIndex) const
+{
+ return nIndex < maEntries.size() ? maEntries[nIndex].get() : nullptr;
+}
+
+void SAL_CALL ScTableConditionalFormat::addNew(
+ const uno::Sequence<beans::PropertyValue >& aConditionalEntry )
+{
+ SolarMutexGuard aGuard;
+ ScCondFormatEntryItem aEntry;
+ aEntry.meMode = ScConditionMode::NONE;
+
+ for (const beans::PropertyValue& rProp : aConditionalEntry)
+ {
+ if ( rProp.Name == SC_UNONAME_OPERATOR )
+ {
+ sal_Int32 eOper = ScUnoHelpFunctions::GetEnumFromAny( rProp.Value );
+ aEntry.meMode = ScConditionEntry::GetModeFromApi( static_cast<sheet::ConditionOperator>(eOper) );
+ }
+ else if ( rProp.Name == SC_UNONAME_FORMULA1 )
+ {
+ OUString aStrVal;
+ uno::Sequence<sheet::FormulaToken> aTokens;
+ if ( rProp.Value >>= aStrVal )
+ aEntry.maExpr1 = aStrVal;
+ else if ( rProp.Value >>= aTokens )
+ {
+ aEntry.maExpr1.clear();
+ aEntry.maTokens1 = aTokens;
+ }
+ }
+ else if ( rProp.Name == SC_UNONAME_FORMULA2 )
+ {
+ OUString aStrVal;
+ uno::Sequence<sheet::FormulaToken> aTokens;
+ if ( rProp.Value >>= aStrVal )
+ aEntry.maExpr2 = aStrVal;
+ else if ( rProp.Value >>= aTokens )
+ {
+ aEntry.maExpr2.clear();
+ aEntry.maTokens2 = aTokens;
+ }
+ }
+ else if ( rProp.Name == SC_UNONAME_SOURCEPOS )
+ {
+ table::CellAddress aAddress;
+ if ( rProp.Value >>= aAddress )
+ aEntry.maPos = ScAddress( static_cast<SCCOL>(aAddress.Column), static_cast<SCROW>(aAddress.Row), aAddress.Sheet );
+ }
+ else if ( rProp.Name == SC_UNONAME_SOURCESTR )
+ {
+ OUString aStrVal;
+ if ( rProp.Value >>= aStrVal )
+ aEntry.maPosStr = aStrVal;
+ }
+ else if ( rProp.Name == SC_UNONAME_STYLENAME )
+ {
+ OUString aStrVal;
+ if ( rProp.Value >>= aStrVal )
+ aEntry.maStyle = ScStyleNameConversion::ProgrammaticToDisplayName(
+ aStrVal, SfxStyleFamily::Para );
+ }
+ else if ( rProp.Name == SC_UNONAME_FORMULANMSP1 )
+ {
+ OUString aStrVal;
+ if ( rProp.Value >>= aStrVal )
+ aEntry.maExprNmsp1 = aStrVal;
+ }
+ else if ( rProp.Name == SC_UNONAME_FORMULANMSP2 )
+ {
+ OUString aStrVal;
+ if ( rProp.Value >>= aStrVal )
+ aEntry.maExprNmsp2 = aStrVal;
+ }
+ else if ( rProp.Name == SC_UNONAME_GRAMMAR1 )
+ {
+ sal_Int32 nVal = 0;
+ if ( rProp.Value >>= nVal )
+ aEntry.meGrammar1 = static_cast< FormulaGrammar::Grammar >( nVal );
+ }
+ else if ( rProp.Name == SC_UNONAME_GRAMMAR2 )
+ {
+ sal_Int32 nVal = 0;
+ if ( rProp.Value >>= nVal )
+ aEntry.meGrammar2 = static_cast< FormulaGrammar::Grammar >( nVal );
+ }
+ else
+ {
+ OSL_FAIL("wrong property");
+ //! Exception...
+ }
+ }
+
+ AddEntry_Impl(aEntry);
+}
+
+void SAL_CALL ScTableConditionalFormat::removeByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if (nIndex >= 0 && o3tl::make_unsigned(nIndex) < maEntries.size())
+ {
+ maEntries.erase(maEntries.begin()+nIndex);
+ }
+}
+
+void SAL_CALL ScTableConditionalFormat::clear()
+{
+ SolarMutexGuard aGuard;
+ maEntries.clear();
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScTableConditionalFormat::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.TableConditionalEntryEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScTableConditionalFormat::getCount()
+{
+ SolarMutexGuard aGuard;
+ return maEntries.size();
+}
+
+uno::Any SAL_CALL ScTableConditionalFormat::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XSheetConditionalEntry> xEntry(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
+ if (!xEntry.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xEntry);
+}
+
+uno::Type SAL_CALL ScTableConditionalFormat::getElementType()
+{
+ return cppu::UnoType<sheet::XSheetConditionalEntry>::get();
+}
+
+sal_Bool SAL_CALL ScTableConditionalFormat::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// conditional format entries have no real names
+// -> generate name from index
+
+static OUString lcl_GetEntryNameFromIndex( sal_Int32 nIndex )
+{
+ OUString aRet = "Entry" + OUString::number( nIndex );
+ return aRet;
+}
+
+uno::Any SAL_CALL ScTableConditionalFormat::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<sheet::XSheetConditionalEntry> xEntry;
+ tools::Long nCount = maEntries.size();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( aName == lcl_GetEntryNameFromIndex(i) )
+ {
+ xEntry.set(GetObjectByIndex_Impl(static_cast<sal_uInt16>(i)));
+ break;
+ }
+
+ if (!xEntry.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xEntry);
+}
+
+uno::Sequence<OUString> SAL_CALL ScTableConditionalFormat::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ tools::Long nCount = maEntries.size();
+ uno::Sequence<OUString> aNames(nCount);
+ OUString* pArray = aNames.getArray();
+ for (tools::Long i=0; i<nCount; i++)
+ pArray[i] = lcl_GetEntryNameFromIndex(i);
+
+ return aNames;
+}
+
+sal_Bool SAL_CALL ScTableConditionalFormat::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ tools::Long nCount = maEntries.size();
+ for (tools::Long i=0; i<nCount; i++)
+ if ( aName == lcl_GetEntryNameFromIndex(i) )
+ return true;
+
+ return false;
+}
+
+ScTableConditionalEntry::ScTableConditionalEntry(ScCondFormatEntryItem aItem) :
+ aData(std::move( aItem ))
+{
+ // #i113668# only store the settings, keep no reference to parent object
+}
+
+ScTableConditionalEntry::~ScTableConditionalEntry()
+{
+}
+
+void ScTableConditionalEntry::GetData(ScCondFormatEntryItem& rData) const
+{
+ rData = aData;
+}
+
+// XSheetCondition
+
+sheet::ConditionOperator SAL_CALL ScTableConditionalEntry::getOperator()
+{
+ SolarMutexGuard aGuard;
+ return lcl_ConditionModeToOperator( aData.meMode );
+}
+
+void SAL_CALL ScTableConditionalEntry::setOperator( sheet::ConditionOperator nOperator )
+{
+ SolarMutexGuard aGuard;
+ aData.meMode = lcl_ConditionOperatorToMode( nOperator );
+}
+
+sal_Int32 SAL_CALL ScTableConditionalEntry::getConditionOperator()
+{
+ SolarMutexGuard aGuard;
+ return lcl_ConditionModeToOperatorNew( aData.meMode );
+}
+
+void SAL_CALL ScTableConditionalEntry::setConditionOperator( sal_Int32 nOperator )
+{
+ SolarMutexGuard aGuard;
+ aData.meMode = ScConditionEntry::GetModeFromApi( static_cast<sheet::ConditionOperator>(nOperator) );
+}
+
+OUString SAL_CALL ScTableConditionalEntry::getFormula1()
+{
+ SolarMutexGuard aGuard;
+ return aData.maExpr1;
+}
+
+void SAL_CALL ScTableConditionalEntry::setFormula1( const OUString& aFormula1 )
+{
+ SolarMutexGuard aGuard;
+ aData.maExpr1 = aFormula1;
+}
+
+OUString SAL_CALL ScTableConditionalEntry::getFormula2()
+{
+ SolarMutexGuard aGuard;
+ return aData.maExpr2;
+}
+
+void SAL_CALL ScTableConditionalEntry::setFormula2( const OUString& aFormula2 )
+{
+ SolarMutexGuard aGuard;
+ aData.maExpr2 = aFormula2;
+}
+
+table::CellAddress SAL_CALL ScTableConditionalEntry::getSourcePosition()
+{
+ SolarMutexGuard aGuard;
+ table::CellAddress aRet;
+ aRet.Column = aData.maPos.Col();
+ aRet.Row = aData.maPos.Row();
+ aRet.Sheet = aData.maPos.Tab();
+ return aRet;
+}
+
+void SAL_CALL ScTableConditionalEntry::setSourcePosition( const table::CellAddress& aSourcePosition )
+{
+ SolarMutexGuard aGuard;
+ aData.maPos.Set( static_cast<SCCOL>(aSourcePosition.Column), static_cast<SCROW>(aSourcePosition.Row), aSourcePosition.Sheet );
+}
+
+// XSheetConditionalEntry
+
+OUString SAL_CALL ScTableConditionalEntry::getStyleName()
+{
+ SolarMutexGuard aGuard;
+ return ScStyleNameConversion::DisplayToProgrammaticName( aData.maStyle, SfxStyleFamily::Para );
+}
+
+void SAL_CALL ScTableConditionalEntry::setStyleName( const OUString& aStyleName )
+{
+ SolarMutexGuard aGuard;
+ aData.maStyle = ScStyleNameConversion::ProgrammaticToDisplayName( aStyleName, SfxStyleFamily::Para );
+}
+
+ScTableValidationObj::ScTableValidationObj(const ScDocument& rDoc, sal_uInt32 nKey,
+ const formula::FormulaGrammar::Grammar eGrammar) :
+ aPropSet( lcl_GetValidatePropertyMap() )
+{
+ // read the entry from the document...
+
+ bool bFound = false;
+ if (nKey)
+ {
+ const ScValidationData* pData = rDoc.GetValidationEntry( nKey );
+ if (pData)
+ {
+ nMode = pData->GetOperation();
+ aSrcPos = pData->GetValidSrcPos(); // valid pos for expressions
+ aExpr1 = pData->GetExpression( aSrcPos, 0, 0, eGrammar );
+ aExpr2 = pData->GetExpression( aSrcPos, 1, 0, eGrammar );
+ meGrammar1 = meGrammar2 = eGrammar;
+ nValMode = sal::static_int_cast<sal_uInt16>( pData->GetDataMode() );
+ bIgnoreBlank = pData->IsIgnoreBlank();
+ nShowList = pData->GetListType();
+ bShowInput = pData->GetInput( aInputTitle, aInputMessage );
+ ScValidErrorStyle eStyle;
+ bShowError = pData->GetErrMsg( aErrorTitle, aErrorMessage, eStyle );
+ nErrorStyle = sal::static_int_cast<sal_uInt16>( eStyle );
+
+ // During save to XML, sheet::ValidationType_ANY formulas are not
+ // saved, even if in the list, see
+ // ScMyValidationsContainer::GetCondition(), so shall not mark
+ // anything in use.
+ if (nValMode != SC_VALID_ANY && rDoc.IsInExternalReferenceMarking())
+ pData->MarkUsedExternalReferences();
+
+ bFound = true;
+ }
+ }
+ if (!bFound)
+ ClearData_Impl(); // Defaults
+}
+
+ScValidationData* ScTableValidationObj::CreateValidationData( ScDocument& rDoc,
+ formula::FormulaGrammar::Grammar eGrammar ) const
+{
+ // ScValidationData = Core-Struktur
+
+ FormulaGrammar::Grammar eGrammar1 = lclResolveGrammar( eGrammar, meGrammar1 );
+ FormulaGrammar::Grammar eGrammar2 = lclResolveGrammar( eGrammar, meGrammar2 );
+
+ ScValidationData* pRet = new ScValidationData( static_cast<ScValidationMode>(nValMode),
+ nMode,
+ aExpr1, aExpr2, rDoc, aSrcPos,
+ maExprNmsp1, maExprNmsp2,
+ eGrammar1, eGrammar2 );
+ pRet->SetIgnoreBlank(bIgnoreBlank);
+ pRet->SetListType(nShowList);
+
+ if ( aTokens1.hasElements() )
+ {
+ ScTokenArray aTokenArray(rDoc);
+ if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aTokens1) )
+ pRet->SetFormula1(aTokenArray);
+ }
+
+ if ( aTokens2.hasElements() )
+ {
+ ScTokenArray aTokenArray(rDoc);
+ if ( ScTokenConversion::ConvertToTokenArray(rDoc, aTokenArray, aTokens2) )
+ pRet->SetFormula2(aTokenArray);
+ }
+
+ // set strings for error / input even if disabled (and disable afterwards)
+ pRet->SetInput( aInputTitle, aInputMessage );
+ if (!bShowInput)
+ pRet->ResetInput();
+ pRet->SetError( aErrorTitle, aErrorMessage, static_cast<ScValidErrorStyle>(nErrorStyle) );
+ if (!bShowError)
+ pRet->ResetError();
+
+ if ( !aPosString.isEmpty() )
+ pRet->SetSrcString( aPosString );
+
+ return pRet;
+}
+
+void ScTableValidationObj::ClearData_Impl()
+{
+ nMode = ScConditionMode::NONE;
+ nValMode = SC_VALID_ANY;
+ bIgnoreBlank = true;
+ nShowList = sheet::TableValidationVisibility::UNSORTED;
+ bShowInput = false;
+ bShowError = false;
+ nErrorStyle = SC_VALERR_STOP;
+ aSrcPos.Set(0,0,0);
+ aExpr1.clear();
+ aExpr2.clear();
+ maExprNmsp1.clear();
+ maExprNmsp2.clear();
+ meGrammar1 = meGrammar2 = FormulaGrammar::GRAM_UNSPECIFIED; // will be overridden when needed
+ aInputTitle.clear();
+ aInputMessage.clear();
+ aErrorTitle.clear();
+ aErrorMessage.clear();
+}
+
+ScTableValidationObj::~ScTableValidationObj()
+{
+}
+
+// XSheetCondition
+
+sheet::ConditionOperator SAL_CALL ScTableValidationObj::getOperator()
+{
+ SolarMutexGuard aGuard;
+ return lcl_ConditionModeToOperator( nMode );
+}
+
+void SAL_CALL ScTableValidationObj::setOperator( sheet::ConditionOperator nOperator )
+{
+ SolarMutexGuard aGuard;
+ nMode = lcl_ConditionOperatorToMode( nOperator );
+}
+
+sal_Int32 SAL_CALL ScTableValidationObj::getConditionOperator()
+{
+ SolarMutexGuard aGuard;
+ return lcl_ConditionModeToOperatorNew( nMode );
+}
+
+void SAL_CALL ScTableValidationObj::setConditionOperator( sal_Int32 nOperator )
+{
+ SolarMutexGuard aGuard;
+ nMode = ScConditionEntry::GetModeFromApi( static_cast<css::sheet::ConditionOperator>(nOperator) );
+}
+
+OUString SAL_CALL ScTableValidationObj::getFormula1()
+{
+ SolarMutexGuard aGuard;
+ return aExpr1;
+}
+
+void SAL_CALL ScTableValidationObj::setFormula1( const OUString& aFormula1 )
+{
+ SolarMutexGuard aGuard;
+ aExpr1 = aFormula1;
+}
+
+OUString SAL_CALL ScTableValidationObj::getFormula2()
+{
+ SolarMutexGuard aGuard;
+ return aExpr2;
+}
+
+void SAL_CALL ScTableValidationObj::setFormula2( const OUString& aFormula2 )
+{
+ SolarMutexGuard aGuard;
+ aExpr2 = aFormula2;
+}
+
+table::CellAddress SAL_CALL ScTableValidationObj::getSourcePosition()
+{
+ SolarMutexGuard aGuard;
+ table::CellAddress aRet;
+ aRet.Column = aSrcPos.Col();
+ aRet.Row = aSrcPos.Row();
+ aRet.Sheet = aSrcPos.Tab();
+ return aRet;
+}
+
+void SAL_CALL ScTableValidationObj::setSourcePosition( const table::CellAddress& aSourcePosition )
+{
+ SolarMutexGuard aGuard;
+ aSrcPos.Set( static_cast<SCCOL>(aSourcePosition.Column), static_cast<SCROW>(aSourcePosition.Row), aSourcePosition.Sheet );
+}
+
+uno::Sequence<sheet::FormulaToken> SAL_CALL ScTableValidationObj::getTokens( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ if (nIndex >= 2 || nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return nIndex == 0 ? aTokens1 : aTokens2;
+}
+
+void SAL_CALL ScTableValidationObj::setTokens( sal_Int32 nIndex, const uno::Sequence<sheet::FormulaToken>& aTokens )
+{
+ SolarMutexGuard aGuard;
+ if (nIndex >= 2 || nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ if (nIndex == 0)
+ {
+ aTokens1 = aTokens;
+ aExpr1.clear();
+ }
+ else if (nIndex == 1)
+ {
+ aTokens2 = aTokens;
+ aExpr2.clear();
+ }
+}
+
+sal_Int32 SAL_CALL ScTableValidationObj::getCount()
+{
+ return 2;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTableValidationObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScTableValidationObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if ( aPropertyName == SC_UNONAME_SHOWINP ) bShowInput = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if ( aPropertyName == SC_UNONAME_SHOWERR ) bShowError = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if ( aPropertyName == SC_UNONAME_IGNOREBL ) bIgnoreBlank = ScUnoHelpFunctions::GetBoolFromAny( aValue );
+ else if ( aPropertyName == SC_UNONAME_SHOWLIST ) aValue >>= nShowList;
+ else if ( aPropertyName == SC_UNONAME_INPTITLE )
+ {
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ aInputTitle = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_INPMESS )
+ {
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ aInputMessage = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_ERRTITLE )
+ {
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ aErrorTitle = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_ERRMESS )
+ {
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ aErrorMessage = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_TYPE )
+ {
+ sheet::ValidationType eType = static_cast<sheet::ValidationType>(ScUnoHelpFunctions::GetEnumFromAny( aValue ));
+ switch (eType)
+ {
+ case sheet::ValidationType_ANY: nValMode = SC_VALID_ANY; break;
+ case sheet::ValidationType_WHOLE: nValMode = SC_VALID_WHOLE; break;
+ case sheet::ValidationType_DECIMAL: nValMode = SC_VALID_DECIMAL; break;
+ case sheet::ValidationType_DATE: nValMode = SC_VALID_DATE; break;
+ case sheet::ValidationType_TIME: nValMode = SC_VALID_TIME; break;
+ case sheet::ValidationType_TEXT_LEN: nValMode = SC_VALID_TEXTLEN; break;
+ case sheet::ValidationType_LIST: nValMode = SC_VALID_LIST; break;
+ case sheet::ValidationType_CUSTOM: nValMode = SC_VALID_CUSTOM; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_ERRALSTY )
+ {
+ sheet::ValidationAlertStyle eStyle = static_cast<sheet::ValidationAlertStyle>(ScUnoHelpFunctions::GetEnumFromAny( aValue ));
+ switch (eStyle)
+ {
+ case sheet::ValidationAlertStyle_STOP: nErrorStyle = SC_VALERR_STOP; break;
+ case sheet::ValidationAlertStyle_WARNING: nErrorStyle = SC_VALERR_WARNING; break;
+ case sheet::ValidationAlertStyle_INFO: nErrorStyle = SC_VALERR_INFO; break;
+ case sheet::ValidationAlertStyle_MACRO: nErrorStyle = SC_VALERR_MACRO; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_SOURCESTR )
+ {
+ // internal - only for XML filter, not in PropertySetInfo, only set
+
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ aPosString = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_FORMULANMSP1 )
+ {
+ // internal - only for XML filter, not in PropertySetInfo, only set
+
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ maExprNmsp1 = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_FORMULANMSP2 )
+ {
+ // internal - only for XML filter, not in PropertySetInfo, only set
+
+ OUString aStrVal;
+ if ( aValue >>= aStrVal )
+ maExprNmsp2 = aStrVal;
+ }
+ else if ( aPropertyName == SC_UNONAME_GRAMMAR1 )
+ {
+ // internal - only for XML filter, not in PropertySetInfo, only set
+
+ sal_Int32 nVal = 0;
+ if ( aValue >>= nVal )
+ meGrammar1 = static_cast< FormulaGrammar::Grammar >(nVal);
+ }
+ else if ( aPropertyName == SC_UNONAME_GRAMMAR2 )
+ {
+ // internal - only for XML filter, not in PropertySetInfo, only set
+
+ sal_Int32 nVal = 0;
+ if ( aValue >>= nVal )
+ meGrammar2 = static_cast< FormulaGrammar::Grammar >(nVal);
+ }
+}
+
+uno::Any SAL_CALL ScTableValidationObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if ( aPropertyName == SC_UNONAME_SHOWINP ) aRet <<= bShowInput;
+ else if ( aPropertyName == SC_UNONAME_SHOWERR ) aRet <<= bShowError;
+ else if ( aPropertyName == SC_UNONAME_IGNOREBL ) aRet <<= bIgnoreBlank;
+ else if ( aPropertyName == SC_UNONAME_SHOWLIST ) aRet <<= nShowList;
+ else if ( aPropertyName == SC_UNONAME_INPTITLE ) aRet <<= aInputTitle;
+ else if ( aPropertyName == SC_UNONAME_INPMESS ) aRet <<= aInputMessage;
+ else if ( aPropertyName == SC_UNONAME_ERRTITLE ) aRet <<= aErrorTitle;
+ else if ( aPropertyName == SC_UNONAME_ERRMESS ) aRet <<= aErrorMessage;
+ else if ( aPropertyName == SC_UNONAME_TYPE )
+ {
+ sheet::ValidationType eType = sheet::ValidationType_ANY;
+ switch (nValMode)
+ {
+ case SC_VALID_ANY: eType = sheet::ValidationType_ANY; break;
+ case SC_VALID_WHOLE: eType = sheet::ValidationType_WHOLE; break;
+ case SC_VALID_DECIMAL: eType = sheet::ValidationType_DECIMAL; break;
+ case SC_VALID_DATE: eType = sheet::ValidationType_DATE; break;
+ case SC_VALID_TIME: eType = sheet::ValidationType_TIME; break;
+ case SC_VALID_TEXTLEN: eType = sheet::ValidationType_TEXT_LEN; break;
+ case SC_VALID_LIST: eType = sheet::ValidationType_LIST; break;
+ case SC_VALID_CUSTOM: eType = sheet::ValidationType_CUSTOM; break;
+ }
+ aRet <<= eType;
+ }
+ else if ( aPropertyName == SC_UNONAME_ERRALSTY )
+ {
+ sheet::ValidationAlertStyle eStyle = sheet::ValidationAlertStyle_STOP;
+ switch (nErrorStyle)
+ {
+ case SC_VALERR_STOP: eStyle = sheet::ValidationAlertStyle_STOP; break;
+ case SC_VALERR_WARNING: eStyle = sheet::ValidationAlertStyle_WARNING; break;
+ case SC_VALERR_INFO: eStyle = sheet::ValidationAlertStyle_INFO; break;
+ case SC_VALERR_MACRO: eStyle = sheet::ValidationAlertStyle_MACRO; break;
+ }
+ aRet <<= eStyle;
+ }
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScTableValidationObj )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/forbiuno.cxx b/sc/source/ui/unoobj/forbiuno.cxx
new file mode 100644
index 0000000000..0928170c55
--- /dev/null
+++ b/sc/source/ui/unoobj/forbiuno.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 <editeng/forbiddencharacterstable.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/svapp.hxx>
+
+#include <forbiuno.hxx>
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+
+static std::shared_ptr<SvxForbiddenCharactersTable> lcl_GetForbidden( ScDocShell* pDocSh )
+{
+ std::shared_ptr<SvxForbiddenCharactersTable> xRet;
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ xRet = rDoc.GetForbiddenCharacters();
+ if (!xRet)
+ {
+ // create an empty SvxForbiddenCharactersTable for SvxUnoForbiddenCharsTable,
+ // so changes can be stored.
+ xRet = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(comphelper::getProcessComponentContext());
+ rDoc.SetForbiddenCharacters( xRet );
+ }
+ }
+ return xRet;
+}
+
+ScForbiddenCharsObj::ScForbiddenCharsObj( ScDocShell* pDocSh ) :
+ SvxUnoForbiddenCharsTable( lcl_GetForbidden( pDocSh ) ),
+ pDocShell( pDocSh )
+{
+ if (pDocShell)
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScForbiddenCharsObj::~ScForbiddenCharsObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScForbiddenCharsObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // document gone
+ }
+}
+
+void ScForbiddenCharsObj::onChange()
+{
+ if (pDocShell)
+ {
+ pDocShell->GetDocument().SetForbiddenCharacters( mxForbiddenChars );
+ pDocShell->PostPaintGridAll();
+ pDocShell->SetDocumentModified();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/funcuno.cxx b/sc/source/ui/unoobj/funcuno.cxx
new file mode 100644
index 0000000000..6f5226452b
--- /dev/null
+++ b/sc/source/ui/unoobj/funcuno.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 <cppuhelper/supportsservice.hxx>
+#include <sfx2/app.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+
+#include <funcuno.hxx>
+#include <miscuno.hxx>
+#include <cellsuno.hxx>
+#include <scdll.hxx>
+#include <document.hxx>
+#include <compiler.hxx>
+#include <formula/errorcodes.hxx>
+#include <callform.hxx>
+#include <addincol.hxx>
+#include <rangeseq.hxx>
+#include <formulacell.hxx>
+#include <docoptio.hxx>
+#include <optuno.hxx>
+#include <markdata.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+#include <clipparam.hxx>
+#include <stringutil.hxx>
+#include <tokenarray.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+// registered as implementation for service FunctionAccess,
+// also supports service SpreadsheetDocumentSettings (to set null date etc.)
+
+constexpr OUString SCFUNCTIONACCESS_SERVICE = u"com.sun.star.sheet.FunctionAccess"_ustr;
+constexpr OUString SCDOCSETTINGS_SERVICE = u"com.sun.star.sheet.SpreadsheetDocumentSettings"_ustr;
+
+// helper to use cached document if not in use, temporary document otherwise
+
+namespace {
+
+class ScTempDocSource
+{
+private:
+ ScTempDocCache& rCache;
+ ScDocumentUniquePtr pTempDoc;
+
+ static ScDocument* CreateDocument(); // create and initialize doc
+
+public:
+ explicit ScTempDocSource( ScTempDocCache& rDocCache );
+ ~ScTempDocSource() COVERITY_NOEXCEPT_FALSE;
+
+ ScDocument* GetDocument();
+};
+
+}
+
+ScDocument* ScTempDocSource::CreateDocument()
+{
+ ScDocument* pDoc = new ScDocument( SCDOCMODE_FUNCTIONACCESS );
+ pDoc->MakeTable( 0 );
+ return pDoc;
+}
+
+ScTempDocSource::ScTempDocSource( ScTempDocCache& rDocCache ) :
+ rCache( rDocCache )
+{
+ if ( rCache.IsInUse() )
+ pTempDoc.reset(CreateDocument());
+ else
+ {
+ rCache.SetInUse( true );
+ if ( !rCache.GetDocument() )
+ rCache.SetDocument( CreateDocument() );
+ }
+}
+
+ScTempDocSource::~ScTempDocSource() COVERITY_NOEXCEPT_FALSE
+{
+ if ( !pTempDoc )
+ rCache.SetInUse( false );
+}
+
+ScDocument* ScTempDocSource::GetDocument()
+{
+ if ( pTempDoc )
+ return pTempDoc.get();
+ else
+ return rCache.GetDocument();
+}
+
+ScTempDocCache::ScTempDocCache()
+ : bInUse(false)
+{
+}
+
+void ScTempDocCache::SetDocument( ScDocument* pNew )
+{
+ OSL_ENSURE(!xDoc, "ScTempDocCache::SetDocument: already set");
+ xDoc.reset(pNew);
+}
+
+void ScTempDocCache::Clear()
+{
+ OSL_ENSURE( !bInUse, "ScTempDocCache::Clear: bInUse" );
+ xDoc.reset();
+}
+
+// copy results from one document into another
+//! merge this with ScAreaLink::Refresh
+//! copy directly without a clipboard document?
+
+static bool lcl_CopyData( ScDocument* pSrcDoc, const ScRange& rSrcRange,
+ ScDocument* pDestDoc, const ScAddress& rDestPos )
+{
+ SCTAB nSrcTab = rSrcRange.aStart.Tab();
+ SCTAB nDestTab = rDestPos.Tab();
+
+ ScRange aNewRange( rDestPos, ScAddress(
+ rSrcRange.aEnd.Col() - rSrcRange.aStart.Col() + rDestPos.Col(),
+ rSrcRange.aEnd.Row() - rSrcRange.aStart.Row() + rDestPos.Row(),
+ nDestTab ) );
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ ScMarkData aSourceMark(pSrcDoc->GetSheetLimits());
+ aSourceMark.SelectOneTable( nSrcTab ); // for CopyToClip
+ aSourceMark.SetMarkArea( rSrcRange );
+ ScClipParam aClipParam(rSrcRange, false);
+ pSrcDoc->CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, false, false);
+
+ if ( pClipDoc->HasAttrib( 0,0,nSrcTab, pClipDoc->MaxCol(), pClipDoc->MaxRow(),nSrcTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ ScPatternAttr aPattern( pSrcDoc->GetPool() );
+ aPattern.GetItemSet().Put( ScMergeAttr() ); // Defaults
+ aPattern.GetItemSet().Put( ScMergeFlagAttr() );
+ pClipDoc->ApplyPatternAreaTab( 0,0, pClipDoc->MaxCol(), pClipDoc->MaxRow(), nSrcTab, aPattern );
+ }
+
+ ScMarkData aDestMark(pDestDoc->GetSheetLimits());
+ aDestMark.SelectOneTable( nDestTab );
+ aDestMark.SetMarkArea( aNewRange );
+ pDestDoc->CopyFromClip( aNewRange, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::FORMULA, nullptr, pClipDoc.get(), false );
+
+ return true;
+}
+
+ScFunctionAccess::ScFunctionAccess() :
+ aPropertyMap( ScDocOptionsHelper::GetPropertyMap() ),
+ mbArray( true ), // default according to behaviour of older Office versions
+ mbValid( true )
+{
+ StartListening( *SfxGetpApp() ); // for SfxHintId::Deinitializing
+}
+
+ScFunctionAccess::~ScFunctionAccess()
+{
+ pOptions.reset();
+ {
+ // SfxBroadcaster::RemoveListener checks DBG_TESTSOLARMUTEX():
+ SolarMutexGuard g;
+ EndListeningAll();
+ }
+}
+
+void ScFunctionAccess::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Deinitializing )
+ {
+ // document must not be used anymore
+ aDocCache.Clear();
+ mbValid = false;
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ScFunctionAccess_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ return cppu::acquire(new ScFunctionAccess);
+}
+
+// XServiceInfo
+OUString SAL_CALL ScFunctionAccess::getImplementationName()
+{
+ return "stardiv.StarCalc.ScFunctionAccess";
+}
+
+sal_Bool SAL_CALL ScFunctionAccess::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScFunctionAccess::getSupportedServiceNames()
+{
+ return {SCFUNCTIONACCESS_SERVICE, SCDOCSETTINGS_SERVICE};
+}
+
+// XPropertySet (document settings)
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFunctionAccess::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropertyMap ));
+ return aRef;
+}
+
+void SAL_CALL ScFunctionAccess::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if ( aPropertyName == "IsArrayFunction" )
+ {
+ if( !(aValue >>= mbArray) )
+ throw lang::IllegalArgumentException();
+ }
+ else
+ {
+ if ( !pOptions )
+ pOptions.reset( new ScDocOptions() );
+
+ // options aren't initialized from configuration - always get the same default behaviour
+
+ bool bDone = ScDocOptionsHelper::setPropertyValue( *pOptions, aPropertyMap, aPropertyName, aValue );
+ if (!bDone)
+ throw beans::UnknownPropertyException(aPropertyName);
+ }
+}
+
+uno::Any SAL_CALL ScFunctionAccess::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ if ( aPropertyName == "IsArrayFunction" )
+ return uno::Any( mbArray );
+
+ if ( !pOptions )
+ pOptions.reset( new ScDocOptions() );
+
+ // options aren't initialized from configuration - always get the same default behaviour
+
+ return ScDocOptionsHelper::getPropertyValue( *pOptions, aPropertyMap, aPropertyName );
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFunctionAccess )
+
+// XFunctionAccess
+
+static bool lcl_AddFunctionToken( ScTokenArray& rArray, const OUString& rName,const ScCompiler& rCompiler )
+{
+ // function names are always case-insensitive
+ OUString aUpper = ScGlobal::getCharClass().uppercase(rName);
+
+ // same options as in ScCompiler::IsOpCode:
+ // 1. built-in function name
+
+ OpCode eOp = rCompiler.GetEnglishOpCode( aUpper );
+ if ( eOp != ocNone )
+ {
+ rArray.AddOpCode( eOp );
+ return true;
+ }
+
+ // 2. old add in functions
+
+ if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper))
+ {
+ rArray.AddExternal(aUpper.getStr());
+ return true;
+ }
+
+ // 3. new (uno) add in functions
+
+ OUString aIntName =
+ ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
+ if (!aIntName.isEmpty())
+ {
+ rArray.AddExternal(aIntName.getStr()); // international name
+ return true;
+ }
+
+ return false; // no valid function name
+}
+
+static void lcl_AddRef( ScTokenArray& rArray, sal_Int32 nStartRow, sal_Int32 nColCount, sal_Int32 nRowCount )
+{
+ ScComplexRefData aRef;
+ aRef.InitRange(ScRange(0,nStartRow,0,nColCount-1,nStartRow+nRowCount-1,0));
+ rArray.AddDoubleReference(aRef);
+}
+
+namespace {
+
+class SimpleVisitor
+{
+protected:
+ bool mbArgError;
+ ScDocument* mpDoc;
+public:
+ explicit SimpleVisitor( ScDocument* pDoc ) : mbArgError( false ), mpDoc( pDoc ) {}
+ // could possibly just get away with JUST the following overload
+ // 1) virtual void visitElem( long& nCol, long& nRow, const double& elem )
+ // 2) virtual void visitElem( long& nCol, long& nRow, const OUString& elem )
+ // 3) virtual void visitElem( long& nCol, long& nRow, const uno::Any& elem )
+ // the other types methods are here just to reflect the orig code and for
+ // completeness.
+
+ void visitElem( sal_Int32 nCol, sal_Int32 nRow, sal_Int16 elem )
+ {
+ mpDoc->SetValue( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), 0, elem );
+ }
+ void visitElem( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 elem )
+ {
+ mpDoc->SetValue( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), 0, elem );
+ }
+ void visitElem( sal_Int32 nCol, sal_Int32 nRow, const double& elem )
+ {
+ mpDoc->SetValue( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), 0, elem );
+ }
+ void visitElem( sal_Int32 nCol, sal_Int32 nRow, const OUString& elem )
+ {
+ if (!elem.isEmpty())
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ mpDoc->SetString(ScAddress(nCol,nRow,0), elem, &aParam);
+ }
+ }
+ void visitElem( sal_Int32 nCol, sal_Int32 nRow, const uno::Any& rElement )
+ {
+ uno::TypeClass eElemClass = rElement.getValueTypeClass();
+ if ( eElemClass == uno::TypeClass_VOID )
+ {
+ // leave empty
+ }
+ else if ( eElemClass == uno::TypeClass_BYTE ||
+ eElemClass == uno::TypeClass_SHORT ||
+ eElemClass == uno::TypeClass_UNSIGNED_SHORT ||
+ eElemClass == uno::TypeClass_LONG ||
+ eElemClass == uno::TypeClass_UNSIGNED_LONG ||
+ eElemClass == uno::TypeClass_FLOAT ||
+ eElemClass == uno::TypeClass_DOUBLE )
+ {
+ // accept integer types because Basic passes a floating point
+ // variable as byte, short or long if it's an integer number.
+ double fVal(0.0);
+ rElement >>= fVal;
+ visitElem( nCol, nRow, fVal );
+ }
+ else if ( eElemClass == uno::TypeClass_STRING )
+ {
+ OUString aUStr;
+ rElement >>= aUStr;
+ visitElem( nCol, nRow, aUStr );
+ }
+ else
+ mbArgError = true;
+ }
+ bool hasArgError() const { return mbArgError; }
+};
+
+template< class seq >
+class SequencesContainer
+{
+ uno::Sequence< uno::Sequence< seq > > maSeq;
+
+ sal_Int32& mrDocRow;
+ bool mbOverflow;
+ bool mbArgError;
+ ScDocument* mpDoc;
+ ScTokenArray& mrTokenArr;
+
+public:
+ SequencesContainer( const uno::Any& rArg, ScTokenArray& rTokenArr, sal_Int32& rDocRow, ScDocument* pDoc ) :
+ mrDocRow( rDocRow ), mbOverflow(false), mbArgError(false), mpDoc( pDoc ), mrTokenArr( rTokenArr )
+ {
+ rArg >>= maSeq;
+ }
+
+ void process()
+ {
+ SimpleVisitor aVisitor(mpDoc);
+ sal_Int32 nStartRow = mrDocRow;
+ sal_Int32 nRowCount = maSeq.getLength();
+ sal_Int32 nMaxColCount = 0;
+ for ( const uno::Sequence< seq >& rRow : std::as_const(maSeq) )
+ {
+ sal_Int32 nColCount = rRow.getLength();
+ if ( nColCount > nMaxColCount )
+ nMaxColCount = nColCount;
+ for (sal_Int32 nCol=0; nCol<nColCount; nCol++)
+ if ( nCol <= mpDoc->MaxCol() && mrDocRow <= mpDoc->MaxRow() )
+ aVisitor.visitElem( nCol, mrDocRow, rRow[ nCol ] );
+ else
+ mbOverflow=true;
+ mrDocRow++;
+ }
+ mbArgError = aVisitor.hasArgError();
+ if (!mbOverflow)
+ {
+ if (nRowCount && nMaxColCount)
+ lcl_AddRef( mrTokenArr, nStartRow, nMaxColCount, nRowCount );
+ else if (nRowCount == 1 && !nMaxColCount)
+ // Empty Sequence<Sequence<Any>> is omitted argument.
+ mrTokenArr.AddOpCode( ocMissing);
+ }
+ }
+ bool getOverflow() const { return mbOverflow; }
+ bool getArgError() const { return mbArgError; }
+};
+
+template <class T>
+class ArrayOfArrayProc
+{
+public:
+static void processSequences( ScDocument* pDoc, const uno::Any& rArg, ScTokenArray& rTokenArr,
+ sal_Int32& rDocRow, bool& rArgErr, bool& rOverflow )
+{
+ SequencesContainer< T > aContainer( rArg, rTokenArr, rDocRow, pDoc );
+ aContainer.process();
+ rArgErr = aContainer.getArgError();
+ rOverflow = aContainer.getOverflow();
+}
+};
+
+}
+
+uno::Any SAL_CALL ScFunctionAccess::callFunction( const OUString& aName,
+ const uno::Sequence<uno::Any>& aArguments )
+{
+ SolarMutexGuard aGuard;
+
+ if (!mbValid)
+ throw uno::RuntimeException();
+
+ // use cached document if not in use, temporary document otherwise
+ // (deleted in ScTempDocSource dtor)
+ ScTempDocSource aSource( aDocCache );
+ ScDocument* pDoc = aSource.GetDocument();
+ const static SCTAB nTempSheet = 1;
+ // Create an extra tab to contain the Function Cell
+ // this will allow full rows to be used.
+ if ( !pDoc->HasTable( nTempSheet ) )
+ pDoc->MakeTable( nTempSheet );
+
+ /// TODO: check
+ ScAddress aAdr;
+ ScCompiler aCompiler(*pDoc, aAdr, pDoc->GetGrammar());
+
+ // find function
+
+ ScTokenArray aTokenArr(*pDoc);
+ if ( !lcl_AddFunctionToken( aTokenArr, aName,aCompiler ) )
+ {
+ // function not found
+ throw container::NoSuchElementException();
+ }
+
+ // set options (null date, etc.)
+
+ if ( pOptions )
+ pDoc->SetDocOptions( *pOptions );
+
+ // add arguments to token array
+
+ bool bArgErr = false;
+ bool bOverflow = false;
+ sal_Int32 nDocRow = 0;
+ tools::Long nArgCount = aArguments.getLength();
+ const uno::Any* pArgArr = aArguments.getConstArray();
+
+ svl::SharedStringPool& rSPool = pDoc->GetSharedStringPool();
+ aTokenArr.AddOpCode(ocOpen);
+ for (tools::Long nPos=0; nPos<nArgCount; nPos++)
+ {
+ if ( nPos > 0 )
+ aTokenArr.AddOpCode(ocSep);
+
+ const uno::Any& rArg = pArgArr[nPos];
+
+ uno::TypeClass eClass = rArg.getValueTypeClass();
+ const uno::Type& aType = rArg.getValueType();
+ if ( eClass == uno::TypeClass_BYTE ||
+ eClass == uno::TypeClass_BOOLEAN ||
+ eClass == uno::TypeClass_SHORT ||
+ eClass == uno::TypeClass_UNSIGNED_SHORT ||
+ eClass == uno::TypeClass_LONG ||
+ eClass == uno::TypeClass_UNSIGNED_LONG ||
+ eClass == uno::TypeClass_FLOAT ||
+ eClass == uno::TypeClass_DOUBLE )
+ {
+ // accept integer types because Basic passes a floating point
+ // variable as byte, short or long if it's an integer number.
+ double fVal = 0;
+ rArg >>= fVal;
+ aTokenArr.AddDouble( fVal );
+ }
+ else if ( eClass == uno::TypeClass_STRING )
+ {
+ OUString aUStr;
+ rArg >>= aUStr;
+ aTokenArr.AddString(rSPool.intern(aUStr));
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int16> >>::get() ) )
+ {
+ ArrayOfArrayProc<sal_Int16>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<sal_Int32> >>::get() ) )
+ {
+ ArrayOfArrayProc<sal_Int32>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<double> >>::get() ) )
+ {
+ ArrayOfArrayProc<double>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<OUString> >>::get() ) )
+ {
+ ArrayOfArrayProc<OUString>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence< uno::Sequence<uno::Any> >>::get() ) )
+ {
+ ArrayOfArrayProc<uno::Any>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
+ }
+ else if ( aType.equals( cppu::UnoType<table::XCellRange>::get()) )
+ {
+ // currently, only our own cell ranges are supported
+
+ uno::Reference<table::XCellRange> xRange(rArg, uno::UNO_QUERY);
+ ScCellRangesBase* pImpl = dynamic_cast<ScCellRangesBase*>( xRange.get() );
+ if ( pImpl )
+ {
+ ScDocument* pSrcDoc = pImpl->GetDocument();
+ const ScRangeList& rRanges = pImpl->GetRangeList();
+ if ( pSrcDoc && rRanges.size() == 1 )
+ {
+ ScRange const & rSrcRange = rRanges[ 0 ];
+
+ sal_Int32 nStartRow = nDocRow;
+ sal_Int32 nColCount = rSrcRange.aEnd.Col() - rSrcRange.aStart.Col() + 1;
+ sal_Int32 nRowCount = rSrcRange.aEnd.Row() - rSrcRange.aStart.Row() + 1;
+
+ if ( nStartRow + nRowCount > pDoc->GetSheetLimits().GetMaxRowCount() )
+ bOverflow = true;
+ else
+ {
+ // copy data
+ if ( !lcl_CopyData( pSrcDoc, rSrcRange, pDoc, ScAddress( 0, static_cast<SCROW>(nDocRow), 0 ) ) )
+ bOverflow = true;
+ }
+
+ nDocRow += nRowCount;
+ if ( !bOverflow )
+ lcl_AddRef( aTokenArr, nStartRow, nColCount, nRowCount );
+ }
+ else
+ bArgErr = true;
+ }
+ else
+ bArgErr = true;
+ }
+ else
+ bArgErr = true; // invalid type
+ }
+ aTokenArr.AddOpCode(ocClose);
+ aTokenArr.AddOpCode(ocStop);
+
+ // execute formula
+
+ uno::Any aRet;
+ if ( !bArgErr && !bOverflow && nDocRow <= pDoc->GetSheetLimits().GetMaxRowCount() )
+ {
+ ScAddress aFormulaPos( 0, 0, nTempSheet );
+ // GRAM_API doesn't really matter for the token array but fits with
+ // other API compatibility grammars.
+ ScFormulaCell* pFormula = new ScFormulaCell(
+ *pDoc, aFormulaPos, aTokenArr, formula::FormulaGrammar::GRAM_API,
+ mbArray ? ScMatrixMode::Formula : ScMatrixMode::NONE );
+ pFormula = pDoc->SetFormulaCell(aFormulaPos, pFormula);
+ if (mbArray && pFormula)
+ pFormula->SetMatColsRows(1,1); // the cell dimensions (only one cell)
+
+ // call GetMatrix before GetErrCode because GetMatrix always recalculates
+ // if there is no matrix result
+
+ const ScMatrix* pMat = (mbArray && pFormula) ? pFormula->GetMatrix() : nullptr;
+ FormulaError nErrCode = pFormula ? pFormula->GetErrCode() : FormulaError::IllegalArgument;
+ if ( nErrCode == FormulaError::NONE )
+ {
+ if ( pMat )
+ {
+ // array result
+ ScRangeToSequence::FillMixedArray( aRet, pMat );
+ }
+ else if ( pFormula->IsValue() )
+ {
+ // numeric value
+ aRet <<= pFormula->GetValue();
+ }
+ else
+ {
+ // string result
+ OUString aStrVal = pFormula->GetString().getString();
+ aRet <<= aStrVal;
+ }
+ }
+ else if ( nErrCode == FormulaError::NotAvailable )
+ {
+ // #N/A: leave result empty, no exception
+ }
+ else
+ {
+ // any other error: IllegalArgumentException
+ bArgErr = true;
+ }
+
+ pDoc->DeleteAreaTab( 0, 0, pDoc->MaxCol(), pDoc->MaxRow(), 0, InsertDeleteFlags::ALL );
+ pDoc->DeleteAreaTab( 0, 0, 0, 0, nTempSheet, InsertDeleteFlags::ALL );
+ }
+
+ if (bOverflow)
+ throw uno::RuntimeException();
+
+ if (bArgErr)
+ throw lang::IllegalArgumentException();
+
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/linkuno.cxx b/sc/source/ui/unoobj/linkuno.cxx
new file mode 100644
index 0000000000..b8c6a9c7e3
--- /dev/null
+++ b/sc/source/ui/unoobj/linkuno.cxx
@@ -0,0 +1,1688 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <comphelper/sequence.hxx>
+#include <formula/token.hxx>
+#include <svl/hint.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <linkuno.hxx>
+#include <miscuno.hxx>
+#include <convuno.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <tablink.hxx>
+#include <arealink.hxx>
+#include <hints.hxx>
+#include <unonames.hxx>
+#include <rangeseq.hxx>
+#include <scmatrix.hxx>
+#include <documentlinkmgr.hxx>
+
+#include <string_view>
+#include <vector>
+
+using namespace com::sun::star;
+using namespace formula;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::lang::IllegalArgumentException;
+using ::com::sun::star::uno::RuntimeException;
+using ::std::vector;
+
+// used for sheet- and area link:
+static std::span<const SfxItemPropertyMapEntry> lcl_GetSheetLinkMap()
+{
+ static const SfxItemPropertyMapEntry aSheetLinkMap_Impl[] =
+ {
+ { SC_UNONAME_FILTER, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_FILTOPT, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_LINKURL, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_REFDELAY, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_REFPERIOD, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ return aSheetLinkMap_Impl;
+}
+
+SC_SIMPLE_SERVICE_INFO( ScAreaLinkObj, "ScAreaLinkObj", "com.sun.star.sheet.CellAreaLink" )
+SC_SIMPLE_SERVICE_INFO( ScAreaLinksObj, "ScAreaLinksObj", "com.sun.star.sheet.CellAreaLinks" )
+SC_SIMPLE_SERVICE_INFO( ScDDELinkObj, "ScDDELinkObj", "com.sun.star.sheet.DDELink" )
+SC_SIMPLE_SERVICE_INFO( ScDDELinksObj, "ScDDELinksObj", "com.sun.star.sheet.DDELinks" )
+SC_SIMPLE_SERVICE_INFO( ScSheetLinkObj, "ScSheetLinkObj", "com.sun.star.sheet.SheetLink" )
+SC_SIMPLE_SERVICE_INFO( ScSheetLinksObj, "ScSheetLinksObj", "com.sun.star.sheet.SheetLinks" )
+
+ScSheetLinkObj::ScSheetLinkObj(ScDocShell* pDocSh, OUString aName) :
+ aPropSet( lcl_GetSheetLinkMap() ),
+ pDocShell( pDocSh ),
+ aFileName(std::move( aName ))
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScSheetLinkObj::~ScSheetLinkObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScSheetLinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! notify if links in document are changed
+ // UpdateRef is not needed here
+
+ if ( auto pRefreshHint = dynamic_cast<const ScLinkRefreshedHint*>(&rHint) )
+ {
+ if ( pRefreshHint->GetLinkType() == ScLinkRefType::SHEET && pRefreshHint->GetUrl() == aFileName )
+ Refreshed_Impl();
+ }
+ else
+ {
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // pointer is invalid
+ }
+}
+
+ScTableLink* ScSheetLinkObj::GetLink_Impl() const
+{
+ if (pDocShell)
+ {
+ sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
+ size_t nCount = pLinkManager->GetLinks().size();
+ for (size_t i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
+ if (auto pTabLink = dynamic_cast<ScTableLink*>( pBase))
+ {
+ if ( pTabLink->GetFileName() == aFileName )
+ return pTabLink;
+ }
+ }
+ }
+ return nullptr; // not found
+}
+
+// XNamed
+
+OUString SAL_CALL ScSheetLinkObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return getFileName(); // Name is the same as filename (URL)
+}
+
+void SAL_CALL ScSheetLinkObj::setName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ setFileName(aName); // Name is the same as filename (URL)
+}
+
+// XRefreshable
+
+void SAL_CALL ScSheetLinkObj::refresh()
+{
+ SolarMutexGuard aGuard;
+ ScTableLink* pLink = GetLink_Impl();
+ if (pLink)
+ pLink->Refresh( pLink->GetFileName(), pLink->GetFilterName(), nullptr, pLink->GetRefreshDelaySeconds() );
+}
+
+void SAL_CALL ScSheetLinkObj::addRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ aRefreshListeners.push_back( xListener );
+
+ // hold one additional ref to keep this object alive as long as there are listeners
+ if ( aRefreshListeners.size() == 1 )
+ acquire();
+}
+
+void SAL_CALL ScSheetLinkObj::removeRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ size_t nCount = aRefreshListeners.size();
+ for ( size_t n=nCount; n--; )
+ {
+ uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
+ if ( rObj == xListener )
+ {
+ aRefreshListeners.erase( aRefreshListeners.begin() + n );
+ if ( aRefreshListeners.empty() )
+ release(); // release ref for listeners
+ break;
+ }
+ }
+}
+
+void ScSheetLinkObj::Refreshed_Impl()
+{
+ lang::EventObject aEvent;
+ aEvent.Source.set(getXWeak());
+ for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
+ xRefreshListener->refreshed( aEvent );
+}
+
+void ScSheetLinkObj::ModifyRefreshDelay_Impl( sal_Int32 nRefresh )
+{
+ ScTableLink* pLink = GetLink_Impl();
+ if( pLink )
+ pLink->SetRefreshDelay( static_cast<sal_uLong>(nRefresh) );
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScSheetLinkObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScSheetLinkObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ OUString aValStr;
+ if ( aPropertyName == SC_UNONAME_LINKURL )
+ {
+ if ( aValue >>= aValStr )
+ setFileName( aValStr );
+ }
+ else if ( aPropertyName == SC_UNONAME_FILTER )
+ {
+ if ( aValue >>= aValStr )
+ setFilter( aValStr );
+ }
+ else if ( aPropertyName == SC_UNONAME_FILTOPT )
+ {
+ if ( aValue >>= aValStr )
+ setFilterOptions( aValStr );
+ }
+ else if ( aPropertyName == SC_UNONAME_REFPERIOD )
+ {
+ sal_Int32 nRefresh = 0;
+ if ( aValue >>= nRefresh )
+ setRefreshDelay( nRefresh );
+ }
+ else if ( aPropertyName == SC_UNONAME_REFDELAY )
+ {
+ sal_Int32 nRefresh = 0;
+ if ( aValue >>= nRefresh )
+ setRefreshDelay( nRefresh );
+ }
+}
+
+uno::Any SAL_CALL ScSheetLinkObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNONAME_LINKURL )
+ aRet <<= getFileName();
+ else if ( aPropertyName == SC_UNONAME_FILTER )
+ aRet <<= getFilter();
+ else if ( aPropertyName == SC_UNONAME_FILTOPT )
+ aRet <<= getFilterOptions();
+ else if ( aPropertyName == SC_UNONAME_REFPERIOD )
+ aRet <<= getRefreshDelay();
+ else if ( aPropertyName == SC_UNONAME_REFDELAY )
+ aRet <<= getRefreshDelay();
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScSheetLinkObj )
+
+// internal:
+
+OUString ScSheetLinkObj::getFileName() const
+{
+ SolarMutexGuard aGuard;
+ return aFileName;
+}
+
+void ScSheetLinkObj::setFileName(const OUString& rNewName)
+{
+ SolarMutexGuard aGuard;
+ ScTableLink* pLink = GetLink_Impl();
+ if (!pLink)
+ return;
+
+ // pLink->Refresh with a new file name confuses sfx2::LinkManager
+ // therefore we transplant the sheets manually and create new links with UpdateLinks
+
+ OUString aNewStr(ScGlobal::GetAbsDocName( rNewName, pDocShell ));
+
+ // first transplant the sheets
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if ( rDoc.IsLinked(nTab) && rDoc.GetLinkDoc(nTab) == aFileName ) // old file
+ rDoc.SetLink( nTab, rDoc.GetLinkMode(nTab), aNewStr,
+ rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
+ rDoc.GetLinkTab(nTab),
+ rDoc.GetLinkRefreshDelay(nTab) ); // only change the file
+
+ // update links
+ //! Undo !!!
+
+ pDocShell->UpdateLinks(); // remove old links, possibly set up new ones
+
+ // copy data
+
+ aFileName = aNewStr;
+ pLink = GetLink_Impl(); // new link with new name
+ if (pLink)
+ pLink->Update(); // incl. paint & undo for data
+}
+
+OUString ScSheetLinkObj::getFilter() const
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ ScTableLink* pLink = GetLink_Impl();
+ if (pLink)
+ aRet = pLink->GetFilterName();
+ return aRet;
+}
+
+void ScSheetLinkObj::setFilter(const OUString& rFilter)
+{
+ SolarMutexGuard aGuard;
+ ScTableLink* pLink = GetLink_Impl();
+ if (pLink)
+ {
+ pLink->Refresh( aFileName, rFilter, nullptr, pLink->GetRefreshDelaySeconds() );
+ }
+}
+
+OUString ScSheetLinkObj::getFilterOptions() const
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ ScTableLink* pLink = GetLink_Impl();
+ if (pLink)
+ aRet = pLink->GetOptions();
+ return aRet;
+}
+
+void ScSheetLinkObj::setFilterOptions(const OUString& FilterOptions)
+{
+ SolarMutexGuard aGuard;
+ ScTableLink* pLink = GetLink_Impl();
+ if (pLink)
+ {
+ OUString aOptStr(FilterOptions);
+ pLink->Refresh( aFileName, pLink->GetFilterName(), &aOptStr, pLink->GetRefreshDelaySeconds() );
+ }
+}
+
+sal_Int32 ScSheetLinkObj::getRefreshDelay() const
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nRet = 0;
+ ScTableLink* pLink = GetLink_Impl();
+ if (pLink)
+ nRet = pLink->GetRefreshDelaySeconds();
+ return nRet;
+}
+
+void ScSheetLinkObj::setRefreshDelay(sal_Int32 nRefreshDelay)
+{
+ SolarMutexGuard aGuard;
+ ModifyRefreshDelay_Impl( nRefreshDelay );
+}
+
+ScSheetLinksObj::ScSheetLinksObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScSheetLinksObj::~ScSheetLinksObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScSheetLinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // we don't care about update of references here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XSheetLinks
+
+rtl::Reference<ScSheetLinkObj> ScSheetLinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
+{
+ if (!pDocShell)
+ return nullptr;
+
+ typedef std::unordered_set<OUString> StrSetType;
+ StrSetType aNames;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ sal_Int32 nCount = 0;
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ if (!rDoc.IsLinked(nTab))
+ continue;
+
+ OUString aLinkDoc = rDoc.GetLinkDoc(nTab);
+ if (aNames.insert(aLinkDoc).second)
+ {
+ // unique document name.
+ if (nCount == nIndex)
+ return new ScSheetLinkObj( pDocShell, aLinkDoc );
+ ++nCount;
+ }
+ }
+
+ return nullptr; // no document or index too large
+}
+
+rtl::Reference<ScSheetLinkObj> ScSheetLinksObj::GetObjectByName_Impl(const OUString& aName)
+{
+ // Name is the same as file name
+
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if (rDoc.IsLinked(nTab))
+ {
+ //! case-insensitive ???
+ OUString aLinkDoc = rDoc.GetLinkDoc( nTab );
+ if ( aLinkDoc == aName )
+ return new ScSheetLinkObj( pDocShell, aName );
+ }
+ }
+
+ return nullptr;
+}
+
+// XEnumerationAccess
+uno::Reference<container::XEnumeration> SAL_CALL ScSheetLinksObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.SheetLinksEnumeration");
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL ScSheetLinksObj::getCount()
+{
+ typedef std::unordered_set<OUString> StrSetType;
+
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ return 0;
+
+ sal_Int32 nCount = 0;
+
+ StrSetType aNames;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ if (!rDoc.IsLinked(nTab))
+ continue;
+
+ OUString aLinkDoc = rDoc.GetLinkDoc(nTab);
+ if (aNames.insert(aLinkDoc).second)
+ ++nCount;
+ }
+ return nCount;
+}
+
+uno::Any SAL_CALL ScSheetLinksObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<beans::XPropertySet> xLink(GetObjectByIndex_Impl(nIndex));
+ if (!xLink.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xLink);
+}
+
+uno::Type SAL_CALL ScSheetLinksObj::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL ScSheetLinksObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+uno::Any SAL_CALL ScSheetLinksObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<beans::XPropertySet> xLink(GetObjectByName_Impl(aName));
+ if (!xLink.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xLink);
+}
+
+sal_Bool SAL_CALL ScSheetLinksObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ // Name is the same as file name
+
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if (rDoc.IsLinked(nTab))
+ {
+ //! case-insensitive ???
+ OUString aLinkDoc(rDoc.GetLinkDoc( nTab ));
+ if ( aLinkDoc == aName )
+ return true;
+ }
+ }
+ return false;
+}
+
+uno::Sequence<OUString> SAL_CALL ScSheetLinksObj::getElementNames()
+{
+ typedef std::unordered_set<OUString> StrSetType;
+
+ SolarMutexGuard aGuard;
+ // Name is the same as file name
+
+ if (!pDocShell)
+ return uno::Sequence<OUString>();
+
+ StrSetType aNames;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ sal_Int32 nLinkCount = getCount();
+ uno::Sequence<OUString> aSeq(nLinkCount);
+ OUString* pAry = aSeq.getArray();
+ size_t nPos = 0;
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ if (!rDoc.IsLinked(nTab))
+ continue;
+
+ OUString aLinkDoc = rDoc.GetLinkDoc(nTab);
+ if (aNames.insert(aLinkDoc).second)
+ pAry[nPos++] = aLinkDoc;
+ }
+ OSL_ENSURE( nPos==static_cast<size_t>(nLinkCount), "verzaehlt" );
+ return aSeq;
+}
+
+static ScAreaLink* lcl_GetAreaLink( ScDocShell* pDocShell, size_t nPos )
+{
+ if (pDocShell)
+ {
+ sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
+ size_t nTotalCount = pLinkManager->GetLinks().size();
+ size_t nAreaCount = 0;
+ for (size_t i=0; i<nTotalCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
+ if (auto pAreaLink = dynamic_cast<ScAreaLink*>( pBase))
+ {
+ if ( nAreaCount == nPos )
+ return pAreaLink;
+ ++nAreaCount;
+ }
+ }
+ }
+ return nullptr; // not found
+}
+
+ScAreaLinkObj::ScAreaLinkObj(ScDocShell* pDocSh, size_t nP) :
+ aPropSet( lcl_GetSheetLinkMap() ),
+ pDocShell( pDocSh ),
+ nPos( nP )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScAreaLinkObj::~ScAreaLinkObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScAreaLinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! notify if links in document are changed
+ // UpdateRef is not needed here
+
+ if ( auto pRefreshedHint = dynamic_cast<const ScLinkRefreshedHint*>(&rHint) )
+ {
+ if ( pRefreshedHint->GetLinkType() == ScLinkRefType::AREA )
+ {
+ // get this link to compare dest position
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if ( pLink && pLink->GetDestArea().aStart == pRefreshedHint->GetDestPos() )
+ Refreshed_Impl();
+ }
+ }
+ else
+ {
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // pointer is invalid
+ }
+}
+
+// XFileLink
+
+void ScAreaLinkObj::Modify_Impl( const OUString* pNewFile, const OUString* pNewFilter,
+ const OUString* pNewOptions, const OUString* pNewSource,
+ const table::CellRangeAddress* pNewDest )
+{
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (!pLink)
+ return;
+
+ OUString aFile (pLink->GetFile());
+ OUString aFilter (pLink->GetFilter());
+ OUString aOptions (pLink->GetOptions());
+ OUString aSource (pLink->GetSource());
+ ScRange aDest (pLink->GetDestArea());
+ sal_Int32 nRefreshDelaySeconds = pLink->GetRefreshDelaySeconds();
+
+ //! Undo delete
+ //! Undo merge
+
+ sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
+ pLinkManager->Remove( pLink );
+ pLink = nullptr; // deleted along with remove
+
+ bool bFitBlock = true; // move, if the size changes with update
+ if (pNewFile)
+ {
+ aFile = ScGlobal::GetAbsDocName( *pNewFile, pDocShell ); //! in InsertAreaLink?
+ }
+ if (pNewFilter)
+ aFilter = *pNewFilter;
+ if (pNewOptions)
+ aOptions = *pNewOptions;
+ if (pNewSource)
+ aSource = *pNewSource;
+ if (pNewDest)
+ {
+ ScUnoConversion::FillScRange( aDest, *pNewDest );
+ bFitBlock = false; // new range was specified -> do not move the content
+ }
+ pDocShell->GetDocFunc().InsertAreaLink( aFile, aFilter, aOptions, aSource,
+ aDest, nRefreshDelaySeconds, bFitBlock, true );
+}
+
+void ScAreaLinkObj::ModifyRefreshDelay_Impl( sal_Int32 nRefreshDelaySeconds )
+{
+ ScAreaLink* pLink = lcl_GetAreaLink( pDocShell, nPos );
+ if( pLink )
+ pLink->SetRefreshDelay( nRefreshDelaySeconds );
+}
+
+// XRefreshable
+
+void SAL_CALL ScAreaLinkObj::refresh()
+{
+ SolarMutexGuard aGuard;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ pLink->Refresh( pLink->GetFile(), pLink->GetFilter(), pLink->GetSource(), pLink->GetRefreshDelaySeconds() );
+}
+
+void SAL_CALL ScAreaLinkObj::addRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ aRefreshListeners.push_back( xListener );
+
+ // hold one additional ref to keep this object alive as long as there are listeners
+ if ( aRefreshListeners.size() == 1 )
+ acquire();
+}
+
+void SAL_CALL ScAreaLinkObj::removeRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ size_t nCount = aRefreshListeners.size();
+ for ( size_t n=nCount; n--; )
+ {
+ uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
+ if ( rObj == xListener )
+ {
+ aRefreshListeners.erase( aRefreshListeners.begin() + n );
+ if ( aRefreshListeners.empty() )
+ release(); // release ref for listeners
+ break;
+ }
+
+ if(n == 0)
+ break;
+ }
+}
+
+void ScAreaLinkObj::Refreshed_Impl()
+{
+ lang::EventObject aEvent;
+ aEvent.Source.set(getXWeak());
+ for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
+ xRefreshListener->refreshed( aEvent );
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScAreaLinkObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScAreaLinkObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ OUString aValStr;
+ if ( aPropertyName == SC_UNONAME_LINKURL )
+ {
+ if ( aValue >>= aValStr )
+ setFileName( aValStr );
+ }
+ else if ( aPropertyName == SC_UNONAME_FILTER )
+ {
+ if ( aValue >>= aValStr )
+ setFilter( aValStr );
+ }
+ else if ( aPropertyName == SC_UNONAME_FILTOPT )
+ {
+ if ( aValue >>= aValStr )
+ setFilterOptions( aValStr );
+ }
+ else if ( aPropertyName == SC_UNONAME_REFPERIOD )
+ {
+ sal_Int32 nRefresh = 0;
+ if ( aValue >>= nRefresh )
+ setRefreshDelay( nRefresh );
+ }
+ else if ( aPropertyName == SC_UNONAME_REFDELAY )
+ {
+ sal_Int32 nRefresh = 0;
+ if ( aValue >>= nRefresh )
+ setRefreshDelay( nRefresh );
+ }
+}
+
+uno::Any SAL_CALL ScAreaLinkObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNONAME_LINKURL )
+ aRet <<= getFileName();
+ else if ( aPropertyName == SC_UNONAME_FILTER )
+ aRet <<= getFilter();
+ else if ( aPropertyName == SC_UNONAME_FILTOPT )
+ aRet <<= getFilterOptions();
+ else if ( aPropertyName == SC_UNONAME_REFPERIOD )
+ aRet <<= getRefreshDelay();
+ else if ( aPropertyName == SC_UNONAME_REFDELAY )
+ aRet <<= getRefreshDelay();
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScAreaLinkObj )
+
+// internal:
+
+OUString ScAreaLinkObj::getFileName() const
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ aRet = pLink->GetFile();
+ return aRet;
+}
+
+void ScAreaLinkObj::setFileName(const OUString& rNewName)
+{
+ SolarMutexGuard aGuard;
+ Modify_Impl( &rNewName, nullptr, nullptr, nullptr, nullptr );
+}
+
+OUString ScAreaLinkObj::getFilter() const
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ aRet = pLink->GetFilter();
+ return aRet;
+}
+
+void ScAreaLinkObj::setFilter(const OUString& Filter)
+{
+ SolarMutexGuard aGuard;
+ Modify_Impl( nullptr, &Filter, nullptr, nullptr, nullptr );
+}
+
+OUString ScAreaLinkObj::getFilterOptions() const
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ aRet = pLink->GetOptions();
+ return aRet;
+}
+
+void ScAreaLinkObj::setFilterOptions(const OUString& FilterOptions)
+{
+ SolarMutexGuard aGuard;
+ Modify_Impl( nullptr, nullptr, &FilterOptions, nullptr, nullptr );
+}
+
+sal_Int32 ScAreaLinkObj::getRefreshDelay() const
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nRet = 0;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ nRet = pLink->GetRefreshDelaySeconds();
+ return nRet;
+}
+
+void ScAreaLinkObj::setRefreshDelay(sal_Int32 nRefreshDelay)
+{
+ SolarMutexGuard aGuard;
+ ModifyRefreshDelay_Impl( nRefreshDelay );
+}
+
+// XAreaLink
+
+OUString SAL_CALL ScAreaLinkObj::getSourceArea()
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ aRet = pLink->GetSource();
+ return aRet;
+}
+
+void SAL_CALL ScAreaLinkObj::setSourceArea( const OUString& aSourceArea )
+{
+ SolarMutexGuard aGuard;
+ Modify_Impl( nullptr, nullptr, nullptr, &aSourceArea, nullptr );
+}
+
+table::CellRangeAddress SAL_CALL ScAreaLinkObj::getDestArea()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aRet;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, nPos);
+ if (pLink)
+ ScUnoConversion::FillApiRange( aRet, pLink->GetDestArea() );
+ return aRet;
+}
+
+void SAL_CALL ScAreaLinkObj::setDestArea( const table::CellRangeAddress& aDestArea )
+{
+ SolarMutexGuard aGuard;
+ Modify_Impl( nullptr, nullptr, nullptr, nullptr, &aDestArea );
+}
+
+ScAreaLinksObj::ScAreaLinksObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScAreaLinksObj::~ScAreaLinksObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScAreaLinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // we don't care about update of references here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XAreaLinks
+
+rtl::Reference<ScAreaLinkObj> ScAreaLinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
+{
+ if ( pDocShell && nIndex >= 0 && nIndex < getCount() )
+ return new ScAreaLinkObj( pDocShell, static_cast<size_t>(nIndex) );
+
+ return nullptr; // not found
+}
+
+void SAL_CALL ScAreaLinksObj::insertAtPosition( const table::CellAddress& aDestPos,
+ const OUString& aFileName,
+ const OUString& aSourceArea,
+ const OUString& aFilter,
+ const OUString& aFilterOptions )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ OUString aFileStr (aFileName);
+ ScAddress aDestAddr( static_cast<SCCOL>(aDestPos.Column), static_cast<SCROW>(aDestPos.Row), aDestPos.Sheet );
+
+ aFileStr = ScGlobal::GetAbsDocName( aFileStr, pDocShell ); //! in InsertAreaLink ???
+ pDocShell->GetDocFunc().InsertAreaLink( aFileStr, aFilter, aFilterOptions,
+ aSourceArea, ScRange(aDestAddr),
+ /*nRefreshDelaySeconds*/0, false, true ); // don't move contents
+ }
+}
+
+void SAL_CALL ScAreaLinksObj::removeByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ScAreaLink* pLink = lcl_GetAreaLink(pDocShell, static_cast<size_t>(nIndex));
+ if (pLink)
+ {
+ //! SetAddUndo or what
+
+ sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
+ pLinkManager->Remove( pLink );
+ }
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScAreaLinksObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.CellAreaLinksEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScAreaLinksObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nAreaCount = 0;
+ if (pDocShell)
+ {
+ sfx2::LinkManager* pLinkManager = pDocShell->GetDocument().GetLinkManager();
+ size_t nTotalCount = pLinkManager->GetLinks().size();
+ for (size_t i=0; i<nTotalCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
+ if (dynamic_cast<const ScAreaLink*>( pBase) != nullptr)
+ ++nAreaCount;
+ }
+ }
+ return nAreaCount;
+}
+
+uno::Any SAL_CALL ScAreaLinksObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XAreaLink> xLink(GetObjectByIndex_Impl(nIndex));
+ if (!xLink.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xLink);
+
+}
+
+uno::Type SAL_CALL ScAreaLinksObj::getElementType()
+{
+ return cppu::UnoType<sheet::XAreaLink>::get();
+}
+
+sal_Bool SAL_CALL ScAreaLinksObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+ScDDELinkObj::ScDDELinkObj(ScDocShell* pDocSh, OUString aA,
+ OUString aT, OUString aI) :
+ pDocShell( pDocSh ),
+ aAppl(std::move( aA )),
+ aTopic(std::move( aT )),
+ aItem(std::move( aI ))
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDDELinkObj::~ScDDELinkObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDDELinkObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! notify if links in document are changed
+ // UpdateRef is not needed here
+
+ if ( auto pRefreshedHint = dynamic_cast<const ScLinkRefreshedHint*>(&rHint) )
+ {
+ if ( pRefreshedHint->GetLinkType() == ScLinkRefType::DDE &&
+ pRefreshedHint->GetDdeAppl() == aAppl &&
+ pRefreshedHint->GetDdeTopic() == aTopic &&
+ pRefreshedHint->GetDdeItem() == aItem ) //! mode is ignored
+ Refreshed_Impl();
+ }
+ else
+ {
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // pointer is invalid
+ }
+}
+
+// XNamed
+
+static OUString lcl_BuildDDEName( std::u16string_view rAppl, std::u16string_view rTopic, std::u16string_view rItem )
+{
+ // Appl|Topic!Item (like Excel)
+ OUString aRet = OUString::Concat(rAppl) + "|" + rTopic + "!" + rItem;
+ return aRet;
+}
+
+OUString SAL_CALL ScDDELinkObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return lcl_BuildDDEName( aAppl, aTopic, aItem );
+}
+
+void SAL_CALL ScDDELinkObj::setName( const OUString& /* aName */ )
+{
+ // name can't be changed (formulas wouldn't find the link)
+ throw uno::RuntimeException();
+}
+
+// XDDELink
+
+OUString SAL_CALL ScDDELinkObj::getApplication()
+{
+ //! Test if the link is still in the document?
+
+ return aAppl;
+}
+
+OUString SAL_CALL ScDDELinkObj::getTopic()
+{
+ //! Test if the link is still in the document?
+
+ return aTopic;
+}
+
+OUString SAL_CALL ScDDELinkObj::getItem()
+{
+ //! Test if the link is still in the document?
+
+ return aItem;
+}
+
+// XRefreshable
+
+void SAL_CALL ScDDELinkObj::refresh()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ sc::DocumentLinkManager& rMgr = pDocShell->GetDocument().GetDocLinkManager();
+ rMgr.updateDdeLink(aAppl, aTopic, aItem);
+ }
+}
+
+void SAL_CALL ScDDELinkObj::addRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ aRefreshListeners.push_back( xListener );
+
+ // hold one additional ref to keep this object alive as long as there are listeners
+ if ( aRefreshListeners.size() == 1 )
+ acquire();
+}
+
+void SAL_CALL ScDDELinkObj::removeRefreshListener(
+ const uno::Reference<util::XRefreshListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ size_t nCount = aRefreshListeners.size();
+ for ( size_t n=nCount; n--; )
+ {
+ uno::Reference<util::XRefreshListener>& rObj = aRefreshListeners[n];
+ if ( rObj == xListener )
+ {
+ aRefreshListeners.erase( aRefreshListeners.begin() + n );
+ if ( aRefreshListeners.empty() )
+ release(); // release ref for listeners
+ break;
+ }
+ }
+}
+
+// XDDELinkResults
+
+uno::Sequence< uno::Sequence< uno::Any > > ScDDELinkObj::getResults( )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< uno::Sequence< uno::Any > > aReturn;
+ bool bSuccess = false;
+
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ size_t nPos = 0;
+ if ( rDoc.FindDdeLink( aAppl, aTopic, aItem, SC_DDE_IGNOREMODE, nPos ) )
+ {
+ const ScMatrix* pMatrix = rDoc.GetDdeLinkResultMatrix( nPos );
+ if ( pMatrix )
+ {
+ uno::Any aAny;
+ if ( ScRangeToSequence::FillMixedArray( aAny, pMatrix, true ) )
+ {
+ aAny >>= aReturn;
+ }
+ }
+ bSuccess = true;
+ }
+ }
+
+ if ( !bSuccess )
+ {
+ throw uno::RuntimeException(
+ "ScDDELinkObj::getResults: failed to get results!" );
+ }
+
+ return aReturn;
+}
+
+void ScDDELinkObj::setResults( const uno::Sequence< uno::Sequence< uno::Any > >& aResults )
+{
+ SolarMutexGuard aGuard;
+ bool bSuccess = false;
+
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ size_t nPos = 0;
+ if ( rDoc.FindDdeLink( aAppl, aTopic, aItem, SC_DDE_IGNOREMODE, nPos ) )
+ {
+ ScMatrixRef xMatrix = ScSequenceToMatrix::CreateMixedMatrix( Any(aResults) );
+ bSuccess = rDoc.SetDdeLinkResultMatrix( nPos, xMatrix );
+ }
+ }
+
+ if ( !bSuccess )
+ {
+ throw uno::RuntimeException(
+ "ScDDELinkObj::setResults: failed to set results!" );
+ }
+}
+
+void ScDDELinkObj::Refreshed_Impl()
+{
+ lang::EventObject aEvent;
+ aEvent.Source.set(getXWeak());
+ for (uno::Reference<util::XRefreshListener> & xRefreshListener : aRefreshListeners)
+ xRefreshListener->refreshed( aEvent );
+}
+
+ScDDELinksObj::ScDDELinksObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScDDELinksObj::~ScDDELinksObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScDDELinksObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // we don't care about update of references here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XDDELinks
+
+rtl::Reference<ScDDELinkObj> ScDDELinksObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
+{
+ if (pDocShell)
+ {
+ OUString aAppl, aTopic, aItem;
+ if ( pDocShell->GetDocument().GetDdeLinkData( static_cast<size_t>(nIndex), aAppl, aTopic, aItem ) )
+ return new ScDDELinkObj( pDocShell, aAppl, aTopic, aItem );
+ }
+ return nullptr;
+}
+
+rtl::Reference<ScDDELinkObj> ScDDELinksObj::GetObjectByName_Impl(std::u16string_view aName)
+{
+ if (pDocShell)
+ {
+ OUString aAppl, aTopic, aItem;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ size_t nCount = rDoc.GetDocLinkManager().getDdeLinkCount();
+ for (size_t i=0; i<nCount; i++)
+ {
+ rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem );
+ if ( lcl_BuildDDEName(aAppl, aTopic, aItem) == aName )
+ return new ScDDELinkObj( pDocShell, aAppl, aTopic, aItem );
+ }
+ }
+ return nullptr;
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScDDELinksObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.DDELinksEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScDDELinksObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nAreaCount = 0;
+ if (pDocShell)
+ nAreaCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount();
+ return nAreaCount;
+}
+
+uno::Any SAL_CALL ScDDELinksObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XDDELink> xLink(GetObjectByIndex_Impl(nIndex));
+ if (!xLink.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xLink);
+}
+
+uno::Type SAL_CALL ScDDELinksObj::getElementType()
+{
+ return cppu::UnoType<sheet::XDDELink>::get();
+}
+
+sal_Bool SAL_CALL ScDDELinksObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+uno::Any SAL_CALL ScDDELinksObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XDDELink> xLink(GetObjectByName_Impl(aName));
+ if (!xLink.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xLink);
+}
+
+uno::Sequence<OUString> SAL_CALL ScDDELinksObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ OUString aAppl, aTopic, aItem;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ size_t nCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount();
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+
+ for (size_t i=0; i<nCount; i++)
+ {
+ rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem );
+ pAry[i] = lcl_BuildDDEName(aAppl, aTopic, aItem);
+ }
+ return aSeq;
+ }
+ return uno::Sequence<OUString>();
+}
+
+sal_Bool SAL_CALL ScDDELinksObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ OUString aAppl, aTopic, aItem;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ size_t nCount = pDocShell->GetDocument().GetDocLinkManager().getDdeLinkCount();
+ for (size_t i=0; i<nCount; i++)
+ {
+ rDoc.GetDdeLinkData( i, aAppl, aTopic, aItem );
+ if ( lcl_BuildDDEName(aAppl, aTopic, aItem) == aName )
+ return true;
+ }
+ }
+ return false;
+}
+
+// XDDELinks
+
+uno::Reference< sheet::XDDELink > ScDDELinksObj::addDDELink(
+ const OUString& aApplication, const OUString& aTopic,
+ const OUString& aItem, css::sheet::DDELinkMode nMode )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< sheet::XDDELink > xLink;
+
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sal_uInt8 nMod = SC_DDE_DEFAULT;
+ switch ( nMode )
+ {
+ case sheet::DDELinkMode_DEFAULT:
+ {
+ nMod = SC_DDE_DEFAULT;
+ }
+ break;
+ case sheet::DDELinkMode_ENGLISH:
+ {
+ nMod = SC_DDE_ENGLISH;
+ }
+ break;
+ case sheet::DDELinkMode_TEXT:
+ {
+ nMod = SC_DDE_TEXT;
+ }
+ break;
+ default:
+ {
+ }
+ break;
+ }
+
+ if ( rDoc.CreateDdeLink( aApplication, aTopic, aItem, nMod, ScMatrixRef() ) )
+ {
+ const OUString aName( lcl_BuildDDEName( aApplication, aTopic, aItem ) );
+ xLink.set( GetObjectByName_Impl( aName ) );
+ }
+ }
+
+ if ( !xLink.is() )
+ {
+ throw uno::RuntimeException(
+ "ScDDELinksObj::addDDELink: cannot add DDE link!" );
+ }
+
+ return xLink;
+}
+
+ScExternalSheetCacheObj::ScExternalSheetCacheObj(ScDocShell* pDocShell, ScExternalRefCache::TableTypeRef pTable, size_t nIndex) :
+ mpDocShell(pDocShell),
+ mpTable(std::move(pTable)),
+ mnIndex(nIndex)
+{
+}
+
+ScExternalSheetCacheObj::~ScExternalSheetCacheObj()
+{
+}
+
+void SAL_CALL ScExternalSheetCacheObj::setCellValue(sal_Int32 nCol, sal_Int32 nRow, const Any& rValue)
+{
+ SolarMutexGuard aGuard;
+ if (nRow < 0 || nCol < 0)
+ throw IllegalArgumentException();
+
+ ScExternalRefCache::TokenRef pToken;
+ double fVal = 0.0;
+ OUString aVal;
+ if (rValue >>= fVal)
+ pToken.reset(new FormulaDoubleToken(fVal));
+ else if (rValue >>= aVal)
+ {
+ svl::SharedStringPool& rPool = mpDocShell->GetDocument().GetSharedStringPool();
+ svl::SharedString aSS = rPool.intern(aVal);
+ pToken.reset(new FormulaStringToken(std::move(aSS)));
+ }
+ else
+ // unidentified value type.
+ return;
+
+ mpTable->setCell(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), pToken);
+}
+
+Any SAL_CALL ScExternalSheetCacheObj::getCellValue(sal_Int32 nCol, sal_Int32 nRow)
+{
+ SolarMutexGuard aGuard;
+ if (nRow < 0 || nCol < 0)
+ throw IllegalArgumentException();
+
+ FormulaToken* pToken = mpTable->getCell(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow)).get();
+ if (!pToken)
+ throw IllegalArgumentException();
+
+ Any aValue;
+ switch (pToken->GetType())
+ {
+ case svDouble:
+ {
+ double fVal = pToken->GetDouble();
+ aValue <<= fVal;
+ }
+ break;
+ case svString:
+ {
+ OUString aVal = pToken->GetString().getString();
+ aValue <<= aVal;
+ }
+ break;
+ default:
+ throw IllegalArgumentException();
+ }
+ return aValue;
+}
+
+Sequence< sal_Int32 > SAL_CALL ScExternalSheetCacheObj::getAllRows()
+{
+ SolarMutexGuard aGuard;
+ vector<SCROW> aRows;
+ mpTable->getAllRows(aRows);
+ size_t nSize = aRows.size();
+ Sequence<sal_Int32> aRowsSeq(nSize);
+ auto aRowsSeqRange = asNonConstRange(aRowsSeq);
+ for (size_t i = 0; i < nSize; ++i)
+ aRowsSeqRange[i] = aRows[i];
+
+ return aRowsSeq;
+}
+
+Sequence< sal_Int32 > SAL_CALL ScExternalSheetCacheObj::getAllColumns(sal_Int32 nRow)
+{
+ SolarMutexGuard aGuard;
+ if (nRow < 0)
+ throw IllegalArgumentException();
+
+ vector<SCCOL> aCols;
+ mpTable->getAllCols(static_cast<SCROW>(nRow), aCols);
+ size_t nSize = aCols.size();
+ Sequence<sal_Int32> aColsSeq(nSize);
+ auto aColsSeqRange = asNonConstRange(aColsSeq);
+ for (size_t i = 0; i < nSize; ++i)
+ aColsSeqRange[i] = aCols[i];
+
+ return aColsSeq;
+}
+
+sal_Int32 SAL_CALL ScExternalSheetCacheObj::getTokenIndex()
+{
+ return static_cast< sal_Int32 >( mnIndex );
+}
+
+ScExternalDocLinkObj::ScExternalDocLinkObj(ScDocShell* pDocShell, ScExternalRefManager* pRefMgr, sal_uInt16 nFileId) :
+ mpDocShell(pDocShell), mpRefMgr(pRefMgr), mnFileId(nFileId)
+{
+}
+
+ScExternalDocLinkObj::~ScExternalDocLinkObj()
+{
+}
+
+uno::Reference< sheet::XExternalSheetCache > SAL_CALL ScExternalDocLinkObj::addSheetCache(
+ const OUString& aSheetName, sal_Bool bDynamicCache )
+{
+ SolarMutexGuard aGuard;
+ size_t nIndex = 0;
+ ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aSheetName, true, &nIndex);
+ if (!bDynamicCache)
+ // Set the whole table cached to prevent access to the source document.
+ pTable->setWholeTableCached();
+
+ uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex));
+ return aSheetCache;
+}
+
+Any SAL_CALL ScExternalDocLinkObj::getByName(const OUString &aName)
+{
+ SolarMutexGuard aGuard;
+ size_t nIndex = 0;
+ ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aName, false, &nIndex);
+ if (!pTable)
+ throw container::NoSuchElementException();
+
+ uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex));
+
+ return Any(aSheetCache);
+}
+
+Sequence< OUString > SAL_CALL ScExternalDocLinkObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ vector<OUString> aTabNames;
+ mpRefMgr->getAllCachedTableNames(mnFileId, aTabNames);
+
+ // #i116940# be consistent with getByName: include only table names which have a cache already
+ vector<OUString> aValidNames;
+ std::copy_if(aTabNames.begin(), aTabNames.end(), std::back_inserter(aValidNames),
+ [&](const OUString& rTabName) { return mpRefMgr->getCacheTable(mnFileId, rTabName, false); });
+
+ Sequence<OUString> aSeq(comphelper::containerToSequence(aValidNames));
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScExternalDocLinkObj::hasByName(const OUString &aName)
+{
+ SolarMutexGuard aGuard;
+
+ // #i116940# be consistent with getByName: allow only table names which have a cache already
+ ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aName, false);
+ return bool(pTable);
+}
+
+sal_Int32 SAL_CALL ScExternalDocLinkObj::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ // #i116940# be consistent with getByName: count only table names which have a cache already
+ return getElementNames().getLength();
+}
+
+Any SAL_CALL ScExternalDocLinkObj::getByIndex(sal_Int32 nApiIndex)
+{
+ SolarMutexGuard aGuard;
+
+ // #i116940# Can't use nApiIndex as index for the ref manager, because the API counts only
+ // the entries which have a cache already. Quick solution: Use getElementNames.
+ Sequence< OUString > aNames( getElementNames() );
+ if (nApiIndex < 0 || nApiIndex >= aNames.getLength())
+ throw lang::IndexOutOfBoundsException();
+
+ size_t nIndex = 0;
+ ScExternalRefCache::TableTypeRef pTable = mpRefMgr->getCacheTable(mnFileId, aNames[nApiIndex], false, &nIndex);
+ if (!pTable)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference< sheet::XExternalSheetCache > aSheetCache(new ScExternalSheetCacheObj(mpDocShell, pTable, nIndex));
+
+ return Any(aSheetCache);
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL ScExternalDocLinkObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< container::XEnumeration > aRef(
+ new ScIndexEnumeration(this, "com.sun.star.sheet.ExternalDocLink"));
+ return aRef;
+}
+
+uno::Type SAL_CALL ScExternalDocLinkObj::getElementType()
+{
+ return cppu::UnoType<sheet::XExternalDocLink>::get();
+}
+
+sal_Bool SAL_CALL ScExternalDocLinkObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ // #i116940# be consistent with getByName: count only table names which have a cache already
+ return getElementNames().hasElements();
+}
+
+sal_Int32 SAL_CALL ScExternalDocLinkObj::getTokenIndex()
+{
+ return static_cast<sal_Int32>(mnFileId);
+}
+
+ScExternalDocLinksObj::ScExternalDocLinksObj(ScDocShell* pDocShell) :
+ mpDocShell(pDocShell),
+ mpRefMgr(pDocShell->GetDocument().GetExternalRefManager())
+{
+}
+
+ScExternalDocLinksObj::~ScExternalDocLinksObj()
+{
+}
+
+uno::Reference< sheet::XExternalDocLink > SAL_CALL ScExternalDocLinksObj::addDocLink(
+ const OUString& aDocName )
+{
+ SolarMutexGuard aGuard;
+ OUString aDocUrl( ScGlobal::GetAbsDocName( aDocName, mpDocShell));
+ sal_uInt16 nFileId = mpRefMgr->getExternalFileId(aDocUrl);
+ uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId));
+ return aDocLink;
+}
+
+Any SAL_CALL ScExternalDocLinksObj::getByName(const OUString &aName)
+{
+ SolarMutexGuard aGuard;
+ OUString aDocUrl( ScGlobal::GetAbsDocName( aName, mpDocShell));
+ if (!mpRefMgr->hasExternalFile(aDocUrl))
+ throw container::NoSuchElementException();
+
+ sal_uInt16 nFileId = mpRefMgr->getExternalFileId(aDocUrl);
+ uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId));
+
+ return Any(aDocLink);
+}
+
+Sequence< OUString > SAL_CALL ScExternalDocLinksObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 n = mpRefMgr->getExternalFileCount();
+ Sequence<OUString> aSeq(n);
+ auto aSeqRange = asNonConstRange(aSeq);
+ for (sal_uInt16 i = 0; i < n; ++i)
+ {
+ const OUString* pName = mpRefMgr->getExternalFileName(i);
+ aSeqRange[i] = pName ? *pName : OUString();
+ }
+
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScExternalDocLinksObj::hasByName(const OUString &aName)
+{
+ SolarMutexGuard aGuard;
+ return mpRefMgr->hasExternalFile(aName);
+}
+
+sal_Int32 SAL_CALL ScExternalDocLinksObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ return mpRefMgr->getExternalFileCount();
+}
+
+Any SAL_CALL ScExternalDocLinksObj::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if (nIndex > ::std::numeric_limits<sal_uInt16>::max() || nIndex < ::std::numeric_limits<sal_uInt16>::min())
+ throw lang::IndexOutOfBoundsException();
+
+ sal_uInt16 nFileId = static_cast<sal_uInt16>(nIndex);
+
+ if (!mpRefMgr->hasExternalFile(nFileId))
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference< sheet::XExternalDocLink > aDocLink(new ScExternalDocLinkObj(mpDocShell, mpRefMgr, nFileId));
+ return Any(aDocLink);
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL ScExternalDocLinksObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< container::XEnumeration > aRef(
+ new ScIndexEnumeration(this, "com.sun.star.sheet.ExternalDocLinks"));
+ return aRef;
+}
+
+uno::Type SAL_CALL ScExternalDocLinksObj::getElementType()
+{
+ return cppu::UnoType<sheet::XExternalDocLinks>::get();
+}
+
+sal_Bool SAL_CALL ScExternalDocLinksObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return mpRefMgr->getExternalFileCount() > 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/listenercalls.cxx b/sc/source/ui/unoobj/listenercalls.cxx
new file mode 100644
index 0000000000..7ff7c7df09
--- /dev/null
+++ b/sc/source/ui/unoobj/listenercalls.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 <com/sun/star/util/XModifyListener.hpp>
+#include <osl/diagnose.h>
+
+#include <listenercalls.hxx>
+
+using namespace com::sun::star;
+
+ScUnoListenerCalls::ScUnoListenerCalls() {}
+
+ScUnoListenerCalls::~ScUnoListenerCalls()
+{
+ OSL_ENSURE(aEntries.empty(), "unhandled listener calls remaining");
+}
+
+void ScUnoListenerCalls::Add(const uno::Reference<util::XModifyListener>& rListener,
+ const lang::EventObject& rEvent)
+{
+ if (rListener.is())
+ aEntries.emplace_back(rListener, rEvent);
+}
+
+void ScUnoListenerCalls::ExecuteAndClear()
+{
+ // Execute all stored calls and remove them from the list.
+ // During each modified() call, Add may be called again.
+ // These new calls are executed here, too.
+
+ std::list<ScUnoListenerEntry>::iterator aItr(aEntries.begin());
+ while (aItr != aEntries.end())
+ {
+ ScUnoListenerEntry aEntry = *aItr;
+ try
+ {
+ aEntry.xListener->modified(aEntry.aEvent);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ // the listener is an external object and may throw a RuntimeException
+ // for reasons we don't know
+ }
+
+ // New calls that are added during the modified() call are appended to the end
+ // of aEntries, so the loop will catch them, too (as long as erase happens
+ // after modified).
+
+ aItr = aEntries.erase(aItr);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/miscuno.cxx b/sc/source/ui/unoobj/miscuno.cxx
new file mode 100644
index 0000000000..5c875bc0b7
--- /dev/null
+++ b/sc/source/ui/unoobj/miscuno.cxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/any.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <miscuno.hxx>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+
+SC_SIMPLE_SERVICE_INFO( ScNameToIndexAccess, "ScNameToIndexAccess", "stardiv.unknown" )
+
+bool ScUnoHelpFunctions::GetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp,
+ const OUString& rName, bool bDefault )
+{
+ bool bRet = bDefault;
+ if ( xProp.is() )
+ {
+ try
+ {
+ xProp->getPropertyValue( rName ) >>= bRet;
+ }
+ catch(uno::Exception&)
+ {
+ // keep default
+ }
+ }
+ return bRet;
+}
+
+sal_Int16 ScUnoHelpFunctions::GetShortProperty( const css::uno::Reference< css::beans::XPropertySet>& xProp,
+ const OUString& rName, sal_Int16 nDefault )
+{
+ sal_Int16 nRet = nDefault;
+ if ( xProp.is() )
+ {
+ try
+ {
+ xProp->getPropertyValue( rName ) >>= nRet;
+ }
+ catch(uno::Exception&)
+ {
+ // keep default
+ }
+ }
+ return nRet;
+}
+
+sal_Int32 ScUnoHelpFunctions::GetLongProperty( const uno::Reference<beans::XPropertySet>& xProp,
+ const OUString& rName )
+{
+ sal_Int32 nRet = 0;
+ if ( xProp.is() )
+ {
+ try
+ {
+ //! type conversion???
+ xProp->getPropertyValue( rName ) >>= nRet;
+ }
+ catch(uno::Exception&)
+ {
+ // keep default
+ }
+ }
+ return nRet;
+}
+
+sal_Int32 ScUnoHelpFunctions::GetEnumPropertyImpl( const uno::Reference<beans::XPropertySet>& xProp,
+ const OUString& rName, sal_Int32 nDefault )
+{
+ sal_Int32 nRet = nDefault;
+ if ( xProp.is() )
+ {
+ try
+ {
+ uno::Any aAny(xProp->getPropertyValue( rName ));
+
+ if ( aAny.getValueTypeClass() == uno::TypeClass_ENUM )
+ {
+ //! get enum value from any???
+ nRet = *static_cast<sal_Int32 const *>(aAny.getValue());
+ }
+ else
+ {
+ //! type conversion???
+ aAny >>= nRet;
+ }
+ }
+ catch(uno::Exception&)
+ {
+ // keep default
+ }
+ }
+ return nRet;
+}
+
+OUString ScUnoHelpFunctions::GetStringProperty(
+ const Reference<beans::XPropertySet>& xProp, const OUString& rName, const OUString& rDefault )
+{
+ OUString aRet = rDefault;
+ if (!xProp.is())
+ return aRet;
+
+ try
+ {
+ Any any = xProp->getPropertyValue(rName);
+ any >>= aRet;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return aRet;
+}
+
+bool ScUnoHelpFunctions::GetBoolFromAny( const uno::Any& aAny )
+{
+ auto b = o3tl::tryAccess<bool>(aAny);
+ return b.has_value() && *b;
+}
+
+sal_Int16 ScUnoHelpFunctions::GetInt16FromAny( const uno::Any& aAny )
+{
+ sal_Int16 nRet = 0;
+ if ( aAny >>= nRet )
+ return nRet;
+ return 0;
+}
+
+sal_Int32 ScUnoHelpFunctions::GetInt32FromAny( const uno::Any& aAny )
+{
+ sal_Int32 nRet = 0;
+ if ( aAny >>= nRet )
+ return nRet;
+ return 0;
+}
+
+sal_Int32 ScUnoHelpFunctions::GetEnumFromAny( const uno::Any& aAny )
+{
+ sal_Int32 nRet = 0;
+ if ( aAny.getValueTypeClass() == uno::TypeClass_ENUM )
+ nRet = *static_cast<sal_Int32 const *>(aAny.getValue());
+ else
+ aAny >>= nRet;
+ return nRet;
+}
+
+void ScUnoHelpFunctions::SetOptionalPropertyValue(
+ const Reference<beans::XPropertySet>& rPropSet, const char* pPropName, const Any& rVal )
+{
+ SetOptionalPropertyValue(rPropSet, OUString::createFromAscii(pPropName), rVal);
+}
+
+void ScUnoHelpFunctions::SetOptionalPropertyValue(
+ const Reference<beans::XPropertySet>& rPropSet, const OUString& sPropName, const Any& rVal )
+{
+ try
+ {
+ rPropSet->setPropertyValue(sPropName, rVal);
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ // ignored - not supported.
+ }
+}
+
+ScIndexEnumeration::ScIndexEnumeration(uno::Reference<container::XIndexAccess> xInd,
+ OUString aServiceName) :
+ xIndex(std::move( xInd )),
+ sServiceName(std::move(aServiceName)),
+ nPos( 0 )
+{
+}
+
+ScIndexEnumeration::~ScIndexEnumeration()
+{
+}
+
+// XEnumeration
+
+sal_Bool SAL_CALL ScIndexEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return ( nPos < xIndex->getCount() );
+}
+
+uno::Any SAL_CALL ScIndexEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+ uno::Any aReturn;
+ try
+ {
+ aReturn = xIndex->getByIndex(nPos++);
+ }
+ catch (lang::IndexOutOfBoundsException&)
+ {
+ throw container::NoSuchElementException();
+ }
+ return aReturn;
+}
+
+OUString SAL_CALL ScIndexEnumeration::getImplementationName()
+{
+ return "ScIndexEnumeration";
+}
+
+sal_Bool SAL_CALL ScIndexEnumeration::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString >
+ SAL_CALL ScIndexEnumeration::getSupportedServiceNames()
+{
+ return { sServiceName };
+}
+
+ScNameToIndexAccess::ScNameToIndexAccess( css::uno::Reference<
+ css::container::XNameAccess> xNameObj ) :
+ xNameAccess(std::move( xNameObj ))
+{
+ //! test for XIndexAccess interface at rNameObj, use that instead!
+
+ if ( xNameAccess.is() )
+ aNames = xNameAccess->getElementNames();
+}
+
+ScNameToIndexAccess::~ScNameToIndexAccess()
+{
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScNameToIndexAccess::getCount( )
+{
+ return aNames.getLength();
+}
+
+css::uno::Any SAL_CALL ScNameToIndexAccess::getByIndex( sal_Int32 nIndex )
+{
+ if ( xNameAccess.is() && nIndex >= 0 && nIndex < aNames.getLength() )
+ return xNameAccess->getByName( aNames.getConstArray()[nIndex] );
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XElementAccess
+
+css::uno::Type SAL_CALL ScNameToIndexAccess::getElementType( )
+{
+ if ( xNameAccess.is() )
+ return xNameAccess->getElementType();
+ else
+ return uno::Type();
+}
+
+sal_Bool SAL_CALL ScNameToIndexAccess::hasElements( )
+{
+ return getCount() > 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/nameuno.cxx b/sc/source/ui/unoobj/nameuno.cxx
new file mode 100644
index 0000000000..6a43a4c05f
--- /dev/null
+++ b/sc/source/ui/unoobj/nameuno.cxx
@@ -0,0 +1,1147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <o3tl/safeint.hxx>
+#include <svl/hint.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/charclass.hxx>
+
+#include <nameuno.hxx>
+#include <miscuno.hxx>
+#include <cellsuno.hxx>
+#include <convuno.hxx>
+#include <targuno.hxx>
+#include <tokenuno.hxx>
+#include <tokenarray.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <rangenam.hxx>
+#include <unonames.hxx>
+
+#include <scui_def.hxx>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetNamedRangeMap()
+{
+ static const SfxItemPropertyMapEntry aNamedRangeMap_Impl[] =
+ {
+ { SC_UNO_LINKDISPBIT, 0, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_LINKDISPNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_TOKENINDEX, 0, cppu::UnoType<sal_Int32>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNONAME_ISSHAREDFMLA, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aNamedRangeMap_Impl;
+}
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetNamedRangesMap()
+{
+ static const SfxItemPropertyMapEntry aNamedRangesMap_Impl[] =
+ {
+ { SC_UNO_MODIFY_BROADCAST, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ };
+ return aNamedRangesMap_Impl;
+}
+
+constexpr OUString SCNAMEDRANGEOBJ_SERVICE = u"com.sun.star.sheet.NamedRange"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScLabelRangeObj, "ScLabelRangeObj", "com.sun.star.sheet.LabelRange" )
+SC_SIMPLE_SERVICE_INFO( ScLabelRangesObj, "ScLabelRangesObj", "com.sun.star.sheet.LabelRanges" )
+SC_SIMPLE_SERVICE_INFO( ScNamedRangesObj, "ScNamedRangesObj", "com.sun.star.sheet.NamedRanges" )
+
+// Database named ranges are not considered by getCount, hasByName, removeByName and getElementNames
+// Note that hidden named ranges are considered by these methods
+static bool lcl_UserVisibleName(const ScRangeData& rData)
+{
+ //! as method to ScRangeData
+ return !rData.HasType(ScRangeData::Type::Database);
+}
+
+ScNamedRangeObj::ScNamedRangeObj( rtl::Reference< ScNamedRangesObj > xParent, ScDocShell* pDocSh, OUString aNm, Reference<container::XNamed> const & xSheet):
+ mxParent(std::move(xParent)),
+ pDocShell( pDocSh ),
+ aName(std::move( aNm )),
+ mxSheet( xSheet )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScNamedRangeObj::~ScNamedRangeObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScNamedRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update is of no interest
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // became invalid
+}
+
+// Helper functions
+
+ScRangeData* ScNamedRangeObj::GetRangeData_Impl()
+{
+ ScRangeData* pRet = nullptr;
+ if (pDocShell)
+ {
+ ScRangeName* pNames;
+ SCTAB nTab = GetTab_Impl();
+ if (nTab >= 0)
+ pNames = pDocShell->GetDocument().GetRangeName(nTab);
+ else
+ pNames = pDocShell->GetDocument().GetRangeName();
+ if (pNames)
+ {
+ pRet = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (pRet)
+ pRet->ValidateTabRefs(); // adjust relative tab refs to valid tables
+ }
+ }
+ return pRet;
+}
+
+SCTAB ScNamedRangeObj::GetTab_Impl()
+{
+ if (mxSheet.is())
+ {
+ if (!pDocShell)
+ return -2;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab;
+ OUString sName = mxSheet->getName();
+ bool bFound = rDoc.GetTable(sName, nTab);
+ assert(bFound); (void)bFound; // fouled up?
+ return nTab;
+ }
+ else
+ return -1;//global range name
+}
+
+// sheet::XNamedRange
+
+void ScNamedRangeObj::Modify_Impl( const OUString* pNewName, const ScTokenArray* pNewTokens, const OUString* pNewContent,
+ const ScAddress* pNewPos, const ScRangeData::Type* pNewType,
+ const formula::FormulaGrammar::Grammar eGrammar )
+{
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangeName* pNames;
+ SCTAB nTab = GetTab_Impl();
+ if (nTab >= 0)
+ pNames = rDoc.GetRangeName(nTab);
+ else
+ pNames = rDoc.GetRangeName();
+ if (!pNames)
+ return;
+
+ const ScRangeData* pOld = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (!pOld)
+ return;
+
+ std::unique_ptr<ScRangeName> pNewRanges(new ScRangeName(*pNames));
+
+ OUString aInsName = pOld->GetName();
+ if (pNewName)
+ aInsName = *pNewName;
+
+ // Content string based => no problems with changed positions and such.
+ OUString aContent = pOld->GetSymbol(eGrammar);
+ if (pNewContent)
+ aContent = *pNewContent;
+
+ ScAddress aPos = pOld->GetPos();
+ if (pNewPos)
+ aPos = *pNewPos;
+
+ ScRangeData::Type nType = pOld->GetType();
+ if (pNewType)
+ nType = *pNewType;
+
+ ScRangeData* pNew = nullptr;
+ if (pNewTokens)
+ pNew = new ScRangeData( rDoc, aInsName, *pNewTokens, aPos, nType );
+ else
+ pNew = new ScRangeData( rDoc, aInsName, aContent, aPos, nType, eGrammar );
+
+ pNew->SetIndex( pOld->GetIndex() );
+
+ pNewRanges->erase(*pOld);
+ if (pNewRanges->insert(pNew))
+ {
+ pDocShell->GetDocFunc().SetNewRangeNames(std::move(pNewRanges), mxParent->IsModifyAndBroadcast(), nTab);
+
+ aName = aInsName; //! broadcast?
+ }
+ else
+ {
+ pNew = nullptr; //! uno::Exception/Error or something
+ }
+}
+
+OUString SAL_CALL ScNamedRangeObj::getName()
+{
+ SolarMutexGuard aGuard;
+ return aName;
+}
+
+void SAL_CALL ScNamedRangeObj::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ //! adapt formulas ?????
+
+ OUString aNewStr(aNewName);
+ // GRAM_API for API compatibility.
+ Modify_Impl( &aNewStr, nullptr, nullptr, nullptr, nullptr,formula::FormulaGrammar::GRAM_API );
+
+ if ( aName != aNewStr ) // some error occurred...
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+OUString SAL_CALL ScNamedRangeObj::getContent()
+{
+ SolarMutexGuard aGuard;
+ OUString aContent;
+ ScRangeData* pData = GetRangeData_Impl();
+ if (pData)
+ // GRAM_API for API compatibility.
+ aContent = pData->GetSymbol(formula::FormulaGrammar::GRAM_API);
+ return aContent;
+}
+
+void SAL_CALL ScNamedRangeObj::setContent( const OUString& aContent )
+{
+ SolarMutexGuard aGuard;
+ OUString aContStr(aContent);
+ // GRAM_API for API compatibility.
+ Modify_Impl( nullptr, nullptr, &aContStr, nullptr, nullptr,formula::FormulaGrammar::GRAM_API );
+}
+
+table::CellAddress SAL_CALL ScNamedRangeObj::getReferencePosition()
+{
+ SolarMutexGuard aGuard;
+ ScAddress aPos;
+ ScRangeData* pData = GetRangeData_Impl();
+ if (pData)
+ aPos = pData->GetPos();
+ table::CellAddress aAddress;
+ aAddress.Column = aPos.Col();
+ aAddress.Row = aPos.Row();
+ aAddress.Sheet = aPos.Tab();
+ if (pDocShell)
+ {
+ SCTAB nDocTabs = pDocShell->GetDocument().GetTableCount();
+ if ( aAddress.Sheet >= nDocTabs && nDocTabs > 0 )
+ {
+ // Even after ValidateTabRefs, the position can be invalid if
+ // the content points to preceding tables. The resulting string
+ // is invalid in any case, so the position is just shifted.
+ aAddress.Sheet = nDocTabs - 1;
+ }
+ }
+ return aAddress;
+}
+
+void SAL_CALL ScNamedRangeObj::setReferencePosition( const table::CellAddress& aReferencePosition )
+{
+ SolarMutexGuard aGuard;
+ ScAddress aPos( static_cast<SCCOL>(aReferencePosition.Column), static_cast<SCROW>(aReferencePosition.Row), aReferencePosition.Sheet );
+ // GRAM_API for API compatibility.
+ Modify_Impl( nullptr, nullptr, nullptr, &aPos, nullptr,formula::FormulaGrammar::GRAM_API );
+}
+
+sal_Int32 SAL_CALL ScNamedRangeObj::getType()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nType=0;
+ ScRangeData* pData = GetRangeData_Impl();
+ if (pData)
+ {
+ // do not return internal ScRangeData::Type flags
+ if ( pData->HasType(ScRangeData::Type::Criteria) ) nType |= sheet::NamedRangeFlag::FILTER_CRITERIA;
+ if ( pData->HasType(ScRangeData::Type::PrintArea) ) nType |= sheet::NamedRangeFlag::PRINT_AREA;
+ if ( pData->HasType(ScRangeData::Type::ColHeader) ) nType |= sheet::NamedRangeFlag::COLUMN_HEADER;
+ if ( pData->HasType(ScRangeData::Type::RowHeader) ) nType |= sheet::NamedRangeFlag::ROW_HEADER;
+ if ( pData->HasType(ScRangeData::Type::Hidden) ) nType |= sheet::NamedRangeFlag::HIDDEN;
+ }
+ return nType;
+}
+
+void SAL_CALL ScNamedRangeObj::setType( sal_Int32 nUnoType )
+{
+ SolarMutexGuard aGuard;
+ ScRangeData::Type nNewType = ScRangeData::Type::Name;
+ if ( nUnoType & sheet::NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria;
+ if ( nUnoType & sheet::NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea;
+ if ( nUnoType & sheet::NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::HIDDEN ) nNewType |= ScRangeData::Type::Hidden;
+
+ // GRAM_API for API compatibility.
+ Modify_Impl( nullptr, nullptr, nullptr, nullptr, &nNewType,formula::FormulaGrammar::GRAM_API );
+}
+
+// XFormulaTokens
+
+uno::Sequence<sheet::FormulaToken> SAL_CALL ScNamedRangeObj::getTokens()
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<sheet::FormulaToken> aSequence;
+ ScRangeData* pData = GetRangeData_Impl();
+ if (pData && pDocShell)
+ {
+ ScTokenArray* pTokenArray = pData->GetCode();
+ if ( pTokenArray )
+ ScTokenConversion::ConvertToTokenSequence( pDocShell->GetDocument(), aSequence, *pTokenArray );
+ }
+ return aSequence;
+}
+
+void SAL_CALL ScNamedRangeObj::setTokens( const uno::Sequence<sheet::FormulaToken>& rTokens )
+{
+ SolarMutexGuard aGuard;
+ if( pDocShell )
+ {
+ ScTokenArray aTokenArray(pDocShell->GetDocument());
+ (void)ScTokenConversion::ConvertToTokenArray( pDocShell->GetDocument(), aTokenArray, rTokens );
+ // GRAM_API for API compatibility.
+ Modify_Impl( nullptr, &aTokenArray, nullptr, nullptr, nullptr, formula::FormulaGrammar::GRAM_API );
+ }
+}
+
+// XCellRangeSource
+
+uno::Reference<table::XCellRange> SAL_CALL ScNamedRangeObj::getReferredCells()
+{
+ SolarMutexGuard aGuard;
+ ScRange aRange;
+ ScRangeData* pData = GetRangeData_Impl();
+ if ( pData && pData->IsValidReference( aRange ) )
+ {
+ //! static function to create ScCellObj/ScCellRangeObj at ScCellRangeObj ???
+
+ if ( aRange.aStart == aRange.aEnd )
+ return new ScCellObj( pDocShell, aRange.aStart );
+ else
+ return new ScCellRangeObj( pDocShell, aRange );
+ }
+ return nullptr;
+}
+
+// beans::XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScNamedRangeObj::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetNamedRangeMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScNamedRangeObj::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& /*aValue*/ )
+{
+ if ( rPropertyName == SC_UNONAME_ISSHAREDFMLA )
+ {
+ // Ignore this.
+ }
+}
+
+uno::Any SAL_CALL ScNamedRangeObj::getPropertyValue( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if ( rPropertyName == SC_UNO_LINKDISPBIT )
+ {
+ // no target bitmaps for individual entries (would be all equal)
+ // ScLinkTargetTypeObj::SetLinkTargetBitmap( aRet, SC_LINKTARGETTYPE_RANGENAME );
+ }
+ else if ( rPropertyName == SC_UNO_LINKDISPNAME )
+ aRet <<= aName;
+ else if ( rPropertyName == SC_UNONAME_TOKENINDEX )
+ {
+ // get index for use in formula tokens (read-only)
+ ScRangeData* pData = GetRangeData_Impl();
+ if (pData)
+ aRet <<= static_cast<sal_Int32>(pData->GetIndex());
+ }
+ else if ( rPropertyName == SC_UNONAME_ISSHAREDFMLA )
+ {
+ if (GetRangeData_Impl())
+ aRet <<= false;
+ }
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScNamedRangeObj )
+
+// lang::XServiceInfo
+
+OUString SAL_CALL ScNamedRangeObj::getImplementationName()
+{
+ return "ScNamedRangeObj";
+}
+
+sal_Bool SAL_CALL ScNamedRangeObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScNamedRangeObj::getSupportedServiceNames()
+{
+ return {SCNAMEDRANGEOBJ_SERVICE, SCLINKTARGET_SERVICE};
+}
+
+ScNamedRangesObj::ScNamedRangesObj(ScDocShell* pDocSh) :
+ mbModifyAndBroadcast(true),
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScNamedRangesObj::~ScNamedRangesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScNamedRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update is of no interest
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// sheet::XNamedRanges
+
+void SAL_CALL ScNamedRangesObj::addNewByName( const OUString& aName,
+ const OUString& aContent, const table::CellAddress& aPosition,
+ sal_Int32 nUnoType )
+{
+ SolarMutexGuard aGuard;
+ ScAddress aPos( static_cast<SCCOL>(aPosition.Column), static_cast<SCROW>(aPosition.Row), aPosition.Sheet );
+
+ ScRangeData::Type nNewType = ScRangeData::Type::Name;
+ if ( nUnoType & sheet::NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria;
+ if ( nUnoType & sheet::NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea;
+ if ( nUnoType & sheet::NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::HIDDEN ) nNewType |= ScRangeData::Type::Hidden;
+
+ bool bDone = false;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ // tdf#119457 - check for a valid range name and cell reference
+ switch (ScRangeData::IsNameValid(aName, rDoc))
+ {
+ case ScRangeData::IsNameValidType::NAME_INVALID_CELL_REF:
+ throw uno::RuntimeException(
+ "Invalid name. Reference to a cell, or a range of cells not allowed",
+ getXWeak());
+ break;
+ case ScRangeData::IsNameValidType::NAME_INVALID_BAD_STRING:
+ throw uno::RuntimeException(
+ "Invalid name. Start with a letter, use only letters, numbers and underscore",
+ getXWeak());
+ break;
+ case ScRangeData::IsNameValidType::NAME_VALID:
+ if (ScRangeName* pNames = GetRangeName_Impl();
+ pNames
+ && !pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName)))
+ {
+ std::unique_ptr<ScRangeName> pNewRanges(new ScRangeName( *pNames ));
+ // GRAM_API for API compatibility.
+ ScRangeData* pNew = new ScRangeData( rDoc, aName, aContent,
+ aPos, nNewType,formula::FormulaGrammar::GRAM_API );
+ if ( pNewRanges->insert(pNew) )
+ {
+ pDocShell->GetDocFunc().SetNewRangeNames(std::move(pNewRanges), mbModifyAndBroadcast, GetTab_Impl());
+ bDone = true;
+ }
+ else
+ {
+ pNew = nullptr;
+ }
+ }
+ }
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScNamedRangesObj::addNewFromTitles( const table::CellRangeAddress& aSource,
+ sheet::Border aBorder )
+{
+ SolarMutexGuard aGuard;
+ //! this cannot be an enum, because multiple bits can be set !!!
+
+ bool bTop = ( aBorder == sheet::Border_TOP );
+ bool bLeft = ( aBorder == sheet::Border_LEFT );
+ bool bBottom = ( aBorder == sheet::Border_BOTTOM );
+ bool bRight = ( aBorder == sheet::Border_RIGHT );
+
+ ScRange aRange;
+ ScUnoConversion::FillScRange( aRange, aSource );
+
+ CreateNameFlags nFlags = CreateNameFlags::NONE;
+ if (bTop) nFlags |= CreateNameFlags::Top;
+ if (bLeft) nFlags |= CreateNameFlags::Left;
+ if (bBottom) nFlags |= CreateNameFlags::Bottom;
+ if (bRight) nFlags |= CreateNameFlags::Right;
+
+ if (nFlags != CreateNameFlags::NONE)
+ pDocShell->GetDocFunc().CreateNames( aRange, nFlags, true, GetTab_Impl() );
+}
+
+void SAL_CALL ScNamedRangesObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ ScRangeName* pNames = GetRangeName_Impl();
+ if (pNames)
+ {
+ const ScRangeData* pData = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (pData && lcl_UserVisibleName(*pData))
+ {
+ std::unique_ptr<ScRangeName> pNewRanges(new ScRangeName(*pNames));
+ pNewRanges->erase(*pData);
+ pDocShell->GetDocFunc().SetNewRangeNames( std::move(pNewRanges), mbModifyAndBroadcast, GetTab_Impl());
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+void SAL_CALL ScNamedRangesObj::outputList( const table::CellAddress& aOutputPosition )
+{
+ SolarMutexGuard aGuard;
+ ScAddress aPos( static_cast<SCCOL>(aOutputPosition.Column), static_cast<SCROW>(aOutputPosition.Row), aOutputPosition.Sheet );
+ if (pDocShell)
+ pDocShell->GetDocFunc().InsertNameList( aPos, true );
+}
+
+// container::XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScNamedRangesObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.NamedRangesEnumeration");
+}
+
+// container::XIndexAccess
+
+sal_Int32 SAL_CALL ScNamedRangesObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ tools::Long nRet = 0;
+ if (pDocShell)
+ {
+ ScRangeName* pNames = GetRangeName_Impl();
+ if (pNames)
+ {
+ for (const auto& rName : *pNames)
+ if (lcl_UserVisibleName(*rName.second))
+ ++nRet;
+ }
+ }
+ return nRet;
+}
+
+uno::Any SAL_CALL ScNamedRangesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< sheet::XNamedRange > xRange(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
+ if ( !xRange.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xRange);
+}
+
+uno::Type SAL_CALL ScNamedRangesObj::getElementType()
+{
+ return cppu::UnoType<sheet::XNamedRange>::get(); // must be suitable for getByIndex
+}
+
+sal_Bool SAL_CALL ScNamedRangesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+Reference<beans::XPropertySetInfo> SAL_CALL ScNamedRangesObj::getPropertySetInfo()
+{
+ static Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo(lcl_GetNamedRangesMap()));
+ return aRef;
+}
+
+void SAL_CALL ScNamedRangesObj::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& aValue )
+{
+ if ( rPropertyName == SC_UNO_MODIFY_BROADCAST )
+ {
+ aValue >>= mbModifyAndBroadcast;
+ }
+}
+
+Any SAL_CALL ScNamedRangesObj::getPropertyValue( const OUString& rPropertyName )
+{
+ Any aRet;
+ if ( rPropertyName == SC_UNO_MODIFY_BROADCAST )
+ {
+ aRet <<= mbModifyAndBroadcast;
+ }
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScNamedRangesObj )
+
+uno::Any SAL_CALL ScNamedRangesObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< sheet::XNamedRange > xRange(GetObjectByName_Impl(aName));
+ if ( !xRange.is() )
+ throw container::NoSuchElementException();
+
+ return uno::Any(xRange);
+}
+
+uno::Sequence<OUString> SAL_CALL ScNamedRangesObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScRangeName* pNames = GetRangeName_Impl();
+ if (pNames)
+ {
+ tools::Long nVisCount = getCount(); // names with lcl_UserVisibleName
+ uno::Sequence<OUString> aSeq(nVisCount);
+ OUString* pAry = aSeq.getArray();
+ sal_uInt16 nVisPos = 0;
+ for (const auto& rName : *pNames)
+ {
+ if (lcl_UserVisibleName(*rName.second))
+ pAry[nVisPos++] = rName.second->GetName();
+ }
+ return aSeq;
+ }
+ }
+ return {};
+}
+
+sal_Bool SAL_CALL ScNamedRangesObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScRangeName* pNames = GetRangeName_Impl();
+ if (pNames)
+ {
+ const ScRangeData* pData = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(aName));
+ if (pData && lcl_UserVisibleName(*pData))
+ return true;
+ }
+ }
+ return false;
+}
+
+/** called from the XActionLockable interface methods on initial locking */
+void ScNamedRangesObj::lock()
+{
+ pDocShell->GetDocument().PreprocessRangeNameUpdate();
+}
+
+/** called from the XActionLockable interface methods on final unlock */
+void ScNamedRangesObj::unlock()
+{
+ pDocShell->GetDocument().CompileHybridFormula();
+}
+
+// document::XActionLockable
+
+sal_Bool ScNamedRangesObj::isActionLocked()
+{
+ SolarMutexGuard aGuard;
+ return pDocShell->GetDocument().GetNamedRangesLockCount() != 0;
+}
+
+void ScNamedRangesObj::addActionLock()
+{
+ SolarMutexGuard aGuard;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount();
+ ++nLockCount;
+ if ( nLockCount == 1 )
+ {
+ lock();
+ }
+ rDoc.SetNamedRangesLockCount( nLockCount );
+}
+
+void ScNamedRangesObj::removeActionLock()
+{
+ SolarMutexGuard aGuard;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount();
+ if ( nLockCount > 0 )
+ {
+ --nLockCount;
+ if ( nLockCount == 0 )
+ {
+ unlock();
+ }
+ rDoc.SetNamedRangesLockCount( nLockCount );
+ }
+}
+
+void ScNamedRangesObj::setActionLocks( sal_Int16 nLock )
+{
+ SolarMutexGuard aGuard;
+ if ( nLock < 0 )
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount();
+ if ( nLock == 0 && nLockCount > 0 )
+ {
+ unlock();
+ }
+ if ( nLock > 0 && nLockCount == 0 )
+ {
+ lock();
+ }
+ rDoc.SetNamedRangesLockCount( nLock );
+}
+
+sal_Int16 ScNamedRangesObj::resetActionLocks()
+{
+ SolarMutexGuard aGuard;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ sal_Int16 nLockCount = rDoc.GetNamedRangesLockCount();
+ if ( nLockCount > 0 )
+ {
+ unlock();
+ }
+ rDoc.SetNamedRangesLockCount( 0 );
+ return nLockCount;
+}
+
+ScGlobalNamedRangesObj::ScGlobalNamedRangesObj(ScDocShell* pDocSh)
+ : ScNamedRangesObj(pDocSh)
+{
+
+}
+
+ScGlobalNamedRangesObj::~ScGlobalNamedRangesObj()
+{
+
+}
+
+rtl::Reference<ScNamedRangeObj> ScGlobalNamedRangesObj::GetObjectByIndex_Impl(sal_uInt16 nIndex)
+{
+ if (!pDocShell)
+ return nullptr;
+
+ ScRangeName* pNames = pDocShell->GetDocument().GetRangeName();
+ if (!pNames)
+ return nullptr;
+
+ sal_uInt16 nPos = 0;
+ for (const auto& rName : *pNames)
+ {
+ if (lcl_UserVisibleName(*rName.second))
+ {
+ if (nPos == nIndex)
+ return new ScNamedRangeObj(this, pDocShell, rName.second->GetName());
+ }
+ ++nPos;
+ }
+ return nullptr;
+}
+
+rtl::Reference<ScNamedRangeObj> ScGlobalNamedRangesObj::GetObjectByName_Impl(const OUString& aName)
+{
+ if ( pDocShell && hasByName(aName) )
+ return new ScNamedRangeObj(this, pDocShell, aName);
+ return nullptr;
+}
+
+ScRangeName* ScGlobalNamedRangesObj::GetRangeName_Impl()
+{
+ return pDocShell->GetDocument().GetRangeName();
+}
+
+SCTAB ScGlobalNamedRangesObj::GetTab_Impl()
+{
+ return -1;
+}
+
+ScLocalNamedRangesObj::ScLocalNamedRangesObj( ScDocShell* pDocSh, uno::Reference<container::XNamed> xSheet )
+ : ScNamedRangesObj(pDocSh),
+ mxSheet(std::move(xSheet))
+{
+
+}
+
+ScLocalNamedRangesObj::~ScLocalNamedRangesObj()
+{
+
+}
+
+rtl::Reference<ScNamedRangeObj> ScLocalNamedRangesObj::GetObjectByName_Impl(const OUString& aName)
+{
+ if ( pDocShell && hasByName( aName ) )
+ return new ScNamedRangeObj( this, pDocShell, aName, mxSheet);
+ return nullptr;
+
+}
+
+rtl::Reference<ScNamedRangeObj> ScLocalNamedRangesObj::GetObjectByIndex_Impl( sal_uInt16 nIndex )
+{
+ if (!pDocShell)
+ return nullptr;
+
+ OUString aName = mxSheet->getName();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab;
+ if (!rDoc.GetTable(aName, nTab))
+ return nullptr;
+
+ ScRangeName* pNames = rDoc.GetRangeName( nTab );
+ if (!pNames)
+ return nullptr;
+
+ sal_uInt16 nPos = 0;
+ for (const auto& rName : *pNames)
+ {
+ if (lcl_UserVisibleName(*rName.second))
+ {
+ if (nPos == nIndex)
+ return new ScNamedRangeObj(this, pDocShell, rName.second->GetName(), mxSheet);
+ }
+ ++nPos;
+ }
+ return nullptr;
+}
+
+ScRangeName* ScLocalNamedRangesObj::GetRangeName_Impl()
+{
+ SCTAB nTab = GetTab_Impl();
+ return pDocShell->GetDocument().GetRangeName( nTab );
+}
+
+SCTAB ScLocalNamedRangesObj::GetTab_Impl()
+{
+ SCTAB nTab;
+ (void)pDocShell->GetDocument().GetTable(mxSheet->getName(), nTab);
+ return nTab;
+}
+
+ScLabelRangeObj::ScLabelRangeObj(ScDocShell* pDocSh, bool bCol, const ScRange& rR) :
+ pDocShell( pDocSh ),
+ bColumn( bCol ),
+ aRange( rR )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScLabelRangeObj::~ScLabelRangeObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScLabelRangeObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ //! Ref-Update !!!
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // became invalid
+}
+
+// Helper functions
+
+ScRangePair* ScLabelRangeObj::GetData_Impl()
+{
+ ScRangePair* pRet = nullptr;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangePairList* pList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges();
+ if (pList)
+ pRet = pList->Find( aRange );
+ }
+ return pRet;
+}
+
+void ScLabelRangeObj::Modify_Impl( const ScRange* pLabel, const ScRange* pData )
+{
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangePairList* pOldList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges();
+ if (!pOldList)
+ return;
+
+ ScRangePairListRef xNewList(pOldList->Clone());
+ ScRangePair* pEntry = xNewList->Find( aRange );
+ if (!pEntry)
+ return;
+
+ if ( pLabel )
+ pEntry->GetRange(0) = *pLabel;
+ if ( pData )
+ pEntry->GetRange(1) = *pData;
+
+ xNewList->Join( *pEntry, true );
+
+ if (bColumn)
+ rDoc.GetColNameRangesRef() = xNewList;
+ else
+ rDoc.GetRowNameRangesRef() = xNewList;
+
+ rDoc.CompileColRowNameFormula();
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid );
+ pDocShell->SetDocumentModified();
+
+ //! Undo ?!?! (here and from dialog)
+
+ if ( pLabel )
+ aRange = *pLabel; // adapt object to find range again
+}
+
+// sheet::XLabelRange
+
+table::CellRangeAddress SAL_CALL ScLabelRangeObj::getLabelArea()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aRet;
+ ScRangePair* pData = GetData_Impl();
+ if (pData)
+ ScUnoConversion::FillApiRange( aRet, pData->GetRange(0) );
+ return aRet;
+}
+
+void SAL_CALL ScLabelRangeObj::setLabelArea( const table::CellRangeAddress& aLabelArea )
+{
+ SolarMutexGuard aGuard;
+ ScRange aLabelRange;
+ ScUnoConversion::FillScRange( aLabelRange, aLabelArea );
+ Modify_Impl( &aLabelRange, nullptr );
+}
+
+table::CellRangeAddress SAL_CALL ScLabelRangeObj::getDataArea()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aRet;
+ ScRangePair* pData = GetData_Impl();
+ if (pData)
+ ScUnoConversion::FillApiRange( aRet, pData->GetRange(1) );
+ return aRet;
+}
+
+void SAL_CALL ScLabelRangeObj::setDataArea( const table::CellRangeAddress& aDataArea )
+{
+ SolarMutexGuard aGuard;
+ ScRange aDataRange;
+ ScUnoConversion::FillScRange( aDataRange, aDataArea );
+ Modify_Impl( nullptr, &aDataRange );
+}
+
+ScLabelRangesObj::ScLabelRangesObj(ScDocShell* pDocSh, bool bCol) :
+ pDocShell( pDocSh ),
+ bColumn( bCol )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScLabelRangesObj::~ScLabelRangesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScLabelRangesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update is of no interest
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// sheet::XLabelRanges
+
+rtl::Reference<ScLabelRangeObj> ScLabelRangesObj::GetObjectByIndex_Impl(size_t nIndex)
+{
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangePairList* pList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges();
+ if ( pList && nIndex < pList->size() )
+ {
+ ScRangePair & rData = (*pList)[nIndex];
+ return new ScLabelRangeObj( pDocShell, bColumn, rData.GetRange(0) );
+ }
+ }
+ return nullptr;
+}
+
+void SAL_CALL ScLabelRangesObj::addNew( const table::CellRangeAddress& aLabelArea,
+ const table::CellRangeAddress& aDataArea )
+{
+ SolarMutexGuard aGuard;
+ if (!pDocShell)
+ return;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangePairList* pOldList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges();
+ if (!pOldList)
+ return;
+
+ ScRangePairListRef xNewList(pOldList->Clone());
+
+ ScRange aLabelRange;
+ ScRange aDataRange;
+ ScUnoConversion::FillScRange( aLabelRange, aLabelArea );
+ ScUnoConversion::FillScRange( aDataRange, aDataArea );
+ xNewList->Join( ScRangePair( aLabelRange, aDataRange ) );
+
+ if (bColumn)
+ rDoc.GetColNameRangesRef() = xNewList;
+ else
+ rDoc.GetRowNameRangesRef() = xNewList;
+
+ rDoc.CompileColRowNameFormula();
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid );
+ pDocShell->SetDocumentModified();
+
+ //! Undo ?!?! (here and from dialog)
+}
+
+void SAL_CALL ScLabelRangesObj::removeByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangePairList* pOldList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges();
+
+ if ( pOldList && nIndex >= 0 && o3tl::make_unsigned(nIndex) < pOldList->size() )
+ {
+ ScRangePairListRef xNewList(pOldList->Clone());
+
+ xNewList->Remove( nIndex );
+
+ if (bColumn)
+ rDoc.GetColNameRangesRef() = xNewList;
+ else
+ rDoc.GetRowNameRangesRef() = xNewList;
+
+ rDoc.CompileColRowNameFormula();
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid );
+ pDocShell->SetDocumentModified();
+ bDone = true;
+
+ //! Undo ?!?! (here and from dialog)
+ }
+ }
+ if (!bDone)
+ throw uno::RuntimeException(); // no other exceptions specified
+}
+
+// container::XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScLabelRangesObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.LabelRangesEnumeration");
+}
+
+// container::XIndexAccess
+
+sal_Int32 SAL_CALL ScLabelRangesObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScRangePairList* pList = bColumn ? rDoc.GetColNameRanges() : rDoc.GetRowNameRanges();
+ if (pList)
+ return pList->size();
+ }
+ return 0;
+}
+
+uno::Any SAL_CALL ScLabelRangesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< sheet::XLabelRange > xRange(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
+ if ( !xRange.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xRange);
+}
+
+uno::Type SAL_CALL ScLabelRangesObj::getElementType()
+{
+ return cppu::UnoType<sheet::XLabelRange>::get(); // must be suitable for getByIndex
+}
+
+sal_Bool SAL_CALL ScLabelRangesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/notesuno.cxx b/sc/source/ui/unoobj/notesuno.cxx
new file mode 100644
index 0000000000..4e99431dd4
--- /dev/null
+++ b/sc/source/ui/unoobj/notesuno.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 <notesuno.hxx>
+
+#include <vcl/svapp.hxx>
+#include <svl/hint.hxx>
+#include <editeng/unoipset.hxx>
+#include <editeng/unotext.hxx>
+#include <editeng/unoprnms.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdocapt.hxx>
+
+#include <postit.hxx>
+#include <cellsuno.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <editsrc.hxx>
+#include <miscuno.hxx>
+
+using namespace com::sun::star;
+
+static const SvxItemPropertySet* lcl_GetAnnotationPropertySet()
+{
+ static const SfxItemPropertyMapEntry aAnnotationPropertyMap_Impl[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ SVX_UNOEDIT_NUMBERING_PROPERTY, // for completeness of service ParagraphProperties
+ };
+ static SvxItemPropertySet aAnnotationPropertySet_Impl( aAnnotationPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() );
+ return &aAnnotationPropertySet_Impl;
+}
+
+SC_SIMPLE_SERVICE_INFO( ScAnnotationObj, "ScAnnotationObj", "com.sun.star.sheet.CellAnnotation" )
+//SC_SIMPLE_SERVICE_INFO( ScAnnotationShapeObj, "ScAnnotationShapeObj", "com.sun.star.sheet.CellAnnotationShape" )
+
+ScAnnotationObj::ScAnnotationObj(ScDocShell* pDocSh, const ScAddress& rPos) :
+ pDocShell( pDocSh ),
+ aCellPos( rPos )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+
+ // pUnoText is allocated on demand (GetUnoText)
+ // can't be aggregated because getString/setString is handled here
+}
+
+ScAnnotationObj::~ScAnnotationObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScAnnotationObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // became invalid
+ }
+}
+
+// XChild
+
+uno::Reference<uno::XInterface> SAL_CALL ScAnnotationObj::getParent()
+{
+ SolarMutexGuard aGuard;
+
+ // parent of note is the related cell
+ //! find and reset existing object ???
+
+ if (pDocShell)
+ return cppu::getXWeak(new ScCellObj( pDocShell, aCellPos ));
+
+ return nullptr;
+}
+
+void SAL_CALL ScAnnotationObj::setParent( const uno::Reference<uno::XInterface>& /* Parent */ )
+{
+ // ain't there
+ //! exception or what ??!
+}
+
+// XSimpleText
+
+uno::Reference<text::XTextCursor> SAL_CALL ScAnnotationObj::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ // notes does not need special treatment
+ return GetUnoText().createTextCursor();
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL ScAnnotationObj::createTextCursorByRange(
+ const uno::Reference<text::XTextRange>& aTextPosition )
+{
+ SolarMutexGuard aGuard;
+ // notes does not need special treatment
+ return GetUnoText().createTextCursorByRange(aTextPosition);
+}
+
+OUString SAL_CALL ScAnnotationObj::getString()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getString();
+}
+
+void SAL_CALL ScAnnotationObj::setString( const OUString& aText )
+{
+ SolarMutexGuard aGuard;
+ GetUnoText().setString(aText);
+}
+
+void SAL_CALL ScAnnotationObj::insertString( const uno::Reference<text::XTextRange>& xRange,
+ const OUString& aString, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ GetUnoText().insertString( xRange, aString, bAbsorb );
+}
+
+void SAL_CALL ScAnnotationObj::insertControlCharacter( const uno::Reference<text::XTextRange>& xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ GetUnoText().insertControlCharacter( xRange, nControlCharacter, bAbsorb );
+}
+
+uno::Reference<text::XText> SAL_CALL ScAnnotationObj::getText()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getText();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScAnnotationObj::getStart()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getStart();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScAnnotationObj::getEnd()
+{
+ SolarMutexGuard aGuard;
+ return GetUnoText().getEnd();
+}
+
+// XSheetAnnotation
+
+table::CellAddress SAL_CALL ScAnnotationObj::getPosition()
+{
+ SolarMutexGuard aGuard;
+ table::CellAddress aAdr;
+ aAdr.Sheet = aCellPos.Tab();
+ aAdr.Column = aCellPos.Col();
+ aAdr.Row = aCellPos.Row();
+ return aAdr;
+}
+
+OUString SAL_CALL ScAnnotationObj::getAuthor()
+{
+ SolarMutexGuard aGuard;
+ const ScPostIt* pNote = ImplGetNote();
+ return pNote ? pNote->GetAuthor() : OUString();
+}
+
+OUString SAL_CALL ScAnnotationObj::getDate()
+{
+ SolarMutexGuard aGuard;
+ const ScPostIt* pNote = ImplGetNote();
+ return pNote ? pNote->GetDate() : OUString();
+}
+
+sal_Bool SAL_CALL ScAnnotationObj::getIsVisible()
+{
+ SolarMutexGuard aGuard;
+ const ScPostIt* pNote = ImplGetNote();
+ return pNote && pNote->IsCaptionShown();
+}
+
+void SAL_CALL ScAnnotationObj::setIsVisible( sal_Bool bIsVisible )
+{
+ SolarMutexGuard aGuard;
+ // show/hide note with undo action
+ if( pDocShell )
+ pDocShell->GetDocFunc().ShowNote( aCellPos, bIsVisible );
+}
+
+// XSheetAnnotationShapeSupplier
+uno::Reference < drawing::XShape > SAL_CALL ScAnnotationObj::getAnnotationShape()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference < drawing::XShape > xShape;
+ if( const ScPostIt* pNote = ImplGetNote() )
+ if( SdrObject* pCaption = pNote->GetOrCreateCaption( aCellPos ) )
+ xShape.set( pCaption->getUnoShape(), uno::UNO_QUERY );
+ return xShape;
+}
+
+SvxUnoText& ScAnnotationObj::GetUnoText()
+{
+ if (!pUnoText.is())
+ {
+ ScAnnotationEditSource aEditSource( pDocShell, aCellPos );
+ pUnoText = new SvxUnoText( &aEditSource, lcl_GetAnnotationPropertySet(),
+ uno::Reference<text::XText>() );
+ }
+ return *pUnoText;
+}
+
+const ScPostIt* ScAnnotationObj::ImplGetNote() const
+{
+ return pDocShell ? pDocShell->GetDocument().GetNote(aCellPos) : nullptr;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/optuno.cxx b/sc/source/ui/unoobj/optuno.cxx
new file mode 100644
index 0000000000..09e17add09
--- /dev/null
+++ b/sc/source/ui/unoobj/optuno.cxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/util/Date.hpp>
+
+#include <optuno.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <docoptio.hxx>
+
+using namespace com::sun::star;
+
+std::span<const SfxItemPropertyMapEntry> ScDocOptionsHelper::GetPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aMap[] =
+ {
+ { SC_UNO_CALCASSHOWN, PROP_UNO_CALCASSHOWN , cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_DEFTABSTOP, PROP_UNO_DEFTABSTOP , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_IGNORECASE, PROP_UNO_IGNORECASE , cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ITERENABLED, PROP_UNO_ITERENABLED , cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_ITERCOUNT, PROP_UNO_ITERCOUNT , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_ITEREPSILON, PROP_UNO_ITEREPSILON , cppu::UnoType<double>::get(), 0, 0},
+ { SC_UNO_LOOKUPLABELS, PROP_UNO_LOOKUPLABELS, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_MATCHWHOLE, PROP_UNO_MATCHWHOLE , cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_NULLDATE, PROP_UNO_NULLDATE , cppu::UnoType<util::Date>::get(), 0, 0},
+ { SC_UNO_SPELLONLINE, PROP_UNO_SPELLONLINE , cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_STANDARDDEC, PROP_UNO_STANDARDDEC , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_REGEXENABLED, PROP_UNO_REGEXENABLED, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_WILDCARDSENABLED, PROP_UNO_WILDCARDSENABLED, cppu::UnoType<bool>::get(), 0, 0},
+ };
+ return aMap;
+}
+
+bool ScDocOptionsHelper::setPropertyValue( ScDocOptions& rOptions,
+ const SfxItemPropertyMap& rPropMap,
+ std::u16string_view aPropertyName, const uno::Any& aValue )
+{
+ //! use map (with new identifiers)
+
+ const SfxItemPropertyMapEntry* pEntry = rPropMap.getByName(aPropertyName );
+ if( !pEntry || !pEntry->nWID )
+ return false;
+ switch( pEntry->nWID )
+ {
+ case PROP_UNO_CALCASSHOWN :
+ rOptions.SetCalcAsShown( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_DEFTABSTOP :
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ rOptions.SetTabDistance( nIntVal );
+ }
+ break;
+ case PROP_UNO_IGNORECASE :
+ rOptions.SetIgnoreCase( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_ITERENABLED:
+ rOptions.SetIter( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_ITERCOUNT :
+ {
+ sal_Int32 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ rOptions.SetIterCount( static_cast<sal_uInt16>(nIntVal) );
+ }
+ break;
+ case PROP_UNO_ITEREPSILON :
+ {
+ double fDoubleVal = 0;
+ if ( aValue >>= fDoubleVal )
+ rOptions.SetIterEps( fDoubleVal );
+ }
+ break;
+ case PROP_UNO_LOOKUPLABELS :
+ rOptions.SetLookUpColRowNames( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_MATCHWHOLE :
+ rOptions.SetMatchWholeCell( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_NULLDATE:
+ {
+ util::Date aDate;
+ if ( aValue >>= aDate )
+ rOptions.SetDate( aDate.Day, aDate.Month, aDate.Year );
+ }
+ break;
+ case PROP_UNO_SPELLONLINE:
+ rOptions.SetAutoSpell( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_STANDARDDEC:
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ rOptions.SetStdPrecision( nIntVal );
+ }
+ break;
+ case PROP_UNO_REGEXENABLED:
+ rOptions.SetFormulaRegexEnabled( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ case PROP_UNO_WILDCARDSENABLED:
+ rOptions.SetFormulaWildcardsEnabled( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ break;
+ default:;
+ }
+ return true;
+}
+
+uno::Any ScDocOptionsHelper::getPropertyValue(
+ const ScDocOptions& rOptions,
+ const SfxItemPropertyMap& rPropMap,
+ std::u16string_view aPropertyName )
+{
+ uno::Any aRet;
+ const SfxItemPropertyMapEntry* pEntry = rPropMap.getByName( aPropertyName );
+ if( !pEntry || !pEntry->nWID )
+ return aRet;
+ switch( pEntry->nWID )
+ {
+ case PROP_UNO_CALCASSHOWN :
+ aRet <<= rOptions.IsCalcAsShown();
+ break;
+ case PROP_UNO_DEFTABSTOP :
+ aRet <<= static_cast<sal_Int16>( rOptions.GetTabDistance() );
+ break;
+ case PROP_UNO_IGNORECASE :
+ aRet <<= rOptions.IsIgnoreCase();
+ break;
+ case PROP_UNO_ITERENABLED:
+ aRet <<= rOptions.IsIter();
+ break;
+ case PROP_UNO_ITERCOUNT:
+ aRet <<= static_cast<sal_Int32>( rOptions.GetIterCount() );
+ break;
+ case PROP_UNO_ITEREPSILON:
+ aRet <<= rOptions.GetIterEps();
+ break;
+ case PROP_UNO_LOOKUPLABELS:
+ aRet <<= rOptions.IsLookUpColRowNames();
+ break;
+ case PROP_UNO_MATCHWHOLE:
+ aRet <<= rOptions.IsMatchWholeCell();
+ break;
+ case PROP_UNO_NULLDATE:
+ {
+ sal_uInt16 nD, nM;
+ sal_Int16 nY;
+ rOptions.GetDate( nD, nM, nY );
+ util::Date aDate( nD, nM, nY );
+ aRet <<= aDate;
+ }
+ break;
+ case PROP_UNO_SPELLONLINE:
+ aRet <<= rOptions.IsAutoSpell();
+ break;
+ case PROP_UNO_STANDARDDEC :
+ aRet <<= static_cast<sal_Int16>( rOptions.GetStdPrecision() );
+ break;
+ case PROP_UNO_REGEXENABLED:
+ aRet <<= rOptions.IsFormulaRegexEnabled();
+ break;
+ case PROP_UNO_WILDCARDSENABLED:
+ aRet <<= rOptions.IsFormulaWildcardsEnabled();
+ break;
+ default:;
+ }
+ return aRet;
+}
+
+ScDocOptionsObj::ScDocOptionsObj( const ScDocOptions& rOpt ) :
+ ScModelObj( nullptr ),
+ aOptions( rOpt )
+{
+}
+
+ScDocOptionsObj::~ScDocOptionsObj()
+{
+}
+
+void SAL_CALL ScDocOptionsObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ bool bDone = ScDocOptionsHelper::setPropertyValue( aOptions, GetPropertySet().getPropertyMap(), aPropertyName, aValue );
+
+ if (!bDone)
+ ScModelObj::setPropertyValue( aPropertyName, aValue );
+}
+
+uno::Any SAL_CALL ScDocOptionsObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet(ScDocOptionsHelper::getPropertyValue( aOptions, GetPropertySet().getPropertyMap(), aPropertyName ));
+ if ( !aRet.hasValue() )
+ aRet = ScModelObj::getPropertyValue( aPropertyName );
+
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/pageuno.cxx b/sc/source/ui/unoobj/pageuno.cxx
new file mode 100644
index 0000000000..3ca903e63e
--- /dev/null
+++ b/sc/source/ui/unoobj/pageuno.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 <cppuhelper/supportsservice.hxx>
+#include <rtl/ustring.hxx>
+#include <pageuno.hxx>
+#include <shapeuno.hxx>
+
+using namespace ::com::sun::star;
+
+ScPageObj::ScPageObj( SdrPage* pPage ) :
+ SvxDrawPage( pPage )
+{
+}
+
+ScPageObj::~ScPageObj() noexcept
+{
+}
+
+uno::Reference<drawing::XShape > ScPageObj::CreateShape( SdrObject *pObj ) const
+{
+ uno::Reference<drawing::XShape> xShape(SvxDrawPage::CreateShape( pObj ));
+
+ new ScShapeObj( xShape ); // aggregates object and modifies xShape
+
+ return xShape;
+}
+
+OUString SAL_CALL ScPageObj::getImplementationName()
+{
+ return "ScPageObj";
+}
+
+sal_Bool SAL_CALL ScPageObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScPageObj::getSupportedServiceNames()
+{
+ return { "com.sun.star.sheet.SpreadsheetDrawPage" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/scdetect.cxx b/sc/source/ui/unoobj/scdetect.cxx
new file mode 100644
index 0000000000..e5fc5848e1
--- /dev/null
+++ b/sc/source/ui/unoobj/scdetect.cxx
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "scdetect.hxx"
+
+#include <sal/macros.h>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <unotools/mediadescriptor.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+
+using namespace ::com::sun::star;
+using utl::MediaDescriptor;
+
+namespace {
+
+// table with search pattern
+// meaning of the sequences
+// 0x00??: the exact byte 0x?? must be at that place
+// 0x0100: read over a byte (don't care)
+// 0x02nn: a byte of 0xnn variations follows
+// 0x8000: recognition finished
+
+#define M_DC 0x0100
+#define M_ALT(CNT) (0x0200+(CNT))
+#define M_END 0x8000
+
+const sal_uInt16 pLotus[] = // Lotus 1/1A/2
+ { 0x0000, 0x0000, 0x0002, 0x0000,
+ M_ALT(2), 0x0004, 0x0006,
+ 0x0004, M_END };
+
+const sal_uInt16 pLotusNew[] = // Lotus >= 9.7
+ { 0x0000, 0x0000, M_DC, 0x0000, // Rec# + Len (0x1a)
+ M_ALT(3), 0x0003, 0x0004, 0x0005, // File Revision Code 97->ME
+ 0x0010, 0x0004, 0x0000, 0x0000,
+ M_END };
+
+const sal_uInt16 pLotus2[] = // Lotus >3
+ { 0x0000, 0x0000, 0x001A, 0x0000, // Rec# + Len (26)
+ M_ALT(2), 0x0000, 0x0002, // File Revision Code
+ 0x0010,
+ 0x0004, 0x0000, // File Revision Subcode
+ M_END };
+
+const sal_uInt16 pQPro[] =
+ { 0x0000, 0x0000, 0x0002, 0x0000,
+ M_ALT(4), 0x0001, 0x0002, // WB1, WB2
+ 0x0006, 0x0007, // QPro 6/7 (?)
+ 0x0010,
+ M_END };
+
+const sal_uInt16 pDIF1[] = // DIF with CR-LF
+ {
+ 'T', 'A', 'B', 'L', 'E',
+ M_DC, M_DC,
+ '0', ',', '1',
+ M_DC, M_DC,
+ '\"',
+ M_END };
+
+const sal_uInt16 pDIF2[] = // DIF with CR or LF
+ {
+ 'T', 'A', 'B', 'L', 'E',
+ M_DC,
+ '0', ',', '1',
+ M_DC,
+ '\"',
+ M_END };
+
+const sal_uInt16 pSylk[] = // Sylk
+ {
+ 'I', 'D', ';',
+ M_ALT(3), 'P', 'N', 'E', // 'P' plus undocumented Excel extensions 'N' and 'E'
+ M_END };
+
+bool detectThisFormat(SvStream& rStr, const sal_uInt16* pSearch)
+{
+ sal_uInt8 nByte;
+ rStr.Seek( 0 ); // in the beginning everything was bad...
+ rStr.ReadUChar( nByte );
+ bool bSync = true;
+ while( !rStr.eof() && bSync )
+ {
+ sal_uInt16 nMuster = *pSearch;
+
+ if( nMuster < 0x0100 )
+ { // compare bytes
+ if( static_cast<sal_uInt8>(nMuster) != nByte )
+ bSync = false;
+ }
+ else if( nMuster & M_DC )
+ { // don't care
+ }
+ else if( nMuster & M_ALT(0) )
+ { // alternative Bytes
+ sal_uInt8 nCntAlt = static_cast<sal_uInt8>(nMuster);
+ bSync = false; // first unsynchron
+ while( nCntAlt > 0 )
+ {
+ pSearch++;
+ if( static_cast<sal_uInt8>(*pSearch) == nByte )
+ bSync = true; // only now synchronization
+ nCntAlt--;
+ }
+ }
+ else if( nMuster & M_END )
+ { // Format detected
+ return true;
+ }
+
+ pSearch++;
+ rStr.ReadUChar( nByte );
+ }
+
+ return false;
+}
+
+}
+
+ScFilterDetect::ScFilterDetect()
+{
+}
+
+ScFilterDetect::~ScFilterDetect()
+{
+}
+
+#if 0
+// This method is no longer used, but I do want to keep this for now to see
+// if we could transfer this check to the now centralized ascii detection
+// code in the filter module.
+static sal_Bool lcl_MayBeAscii( SvStream& rStream )
+{
+ // ASCII/CSV is considered possible if there are no null bytes, or a Byte
+ // Order Mark is present, or if, for Unicode UCS2/UTF-16, all null bytes
+ // are on either even or uneven byte positions.
+
+ rStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ const size_t nBufSize = 2048;
+ sal_uInt16 aBuffer[ nBufSize ];
+ sal_uInt8* pByte = reinterpret_cast<sal_uInt8*>(aBuffer);
+ sal_uLong nBytesRead = rStream.Read( pByte, nBufSize*2);
+
+ if ( nBytesRead >= 2 && (aBuffer[0] == 0xfffe || aBuffer[0] == 0xfeff) )
+ {
+ // Unicode BOM file may contain null bytes.
+ return sal_True;
+ }
+
+ const sal_uInt16* p = aBuffer;
+ sal_uInt16 nMask = 0xffff;
+ nBytesRead /= 2;
+ while( nBytesRead-- && nMask )
+ {
+ sal_uInt16 nVal = *p++ & nMask;
+ if (!(nVal & 0x00ff))
+ nMask &= 0xff00;
+ if (!(nVal & 0xff00))
+ nMask &= 0x00ff;
+ }
+
+ return nMask != 0;
+}
+#endif
+
+static bool lcl_MayBeDBase( SvStream& rStream )
+{
+ // Look for dbf marker, see connectivity/source/inc/dbase/DTable.hxx
+ // DBFType for values.
+ const sal_uInt8 nValidMarks[] = {
+ 0x03, 0x04, 0x05, 0x30, 0x31, 0x43, 0xB3, 0x83, 0x8b, 0x8e, 0xf5 };
+ sal_uInt8 nMark;
+ rStream.Seek(STREAM_SEEK_TO_BEGIN);
+ rStream.ReadUChar( nMark );
+ bool bValidMark = false;
+ for (size_t i=0; i < SAL_N_ELEMENTS(nValidMarks) && !bValidMark; ++i)
+ {
+ if (nValidMarks[i] == nMark)
+ bValidMark = true;
+ }
+ if ( !bValidMark )
+ return false;
+
+ const size_t nHeaderBlockSize = 32;
+ // Empty dbf is >= 32*2+1 bytes in size.
+ const size_t nEmptyDbf = nHeaderBlockSize * 2 + 1;
+
+ sal_uInt64 nSize = rStream.TellEnd();
+ if ( nSize < nEmptyDbf )
+ return false;
+
+ // count of records at 4
+ rStream.Seek(4);
+ sal_uInt32 nRecords(0);
+ rStream.ReadUInt32(nRecords);
+
+ // length of header starts at 8
+ rStream.Seek(8);
+ sal_uInt16 nHeaderLen;
+ rStream.ReadUInt16( nHeaderLen );
+
+ // size of record at 10
+ sal_uInt16 nRecordSize(0);
+ rStream.ReadUInt16(nRecordSize);
+
+ if ( nHeaderLen < nEmptyDbf || nSize < nHeaderLen )
+ return false;
+
+ // see DTable.cxx ODbaseTable::readHeader()
+ if (0 == nRecordSize)
+ return false;
+
+ // see DTable.cxx ODbaseTable::construct() line 546
+ if (0 == nRecords)
+ {
+ nRecords = (nSize - nHeaderLen) / nRecordSize;
+ }
+
+ // tdf#84834 sanity check of size
+ // tdf#106423: a dbf file can have 0 record, so no need to check nRecords
+ if (nSize < nHeaderLen + nRecords * sal_uInt64(nRecordSize))
+ return false;
+
+ // Last byte of header must be 0x0d, this is how it's specified.
+ // #i9581#,#i26407# but some applications don't follow the specification
+ // and pad the header with one byte 0x00 to reach an
+ // even boundary. Some (#i88577# ) even pad more or pad using a 0x1a ^Z
+ // control character (#i8857#). This results in:
+ // Last byte of header must be 0x0d on 32 bytes boundary.
+ sal_uInt16 nBlocks = (nHeaderLen - 1) / nHeaderBlockSize;
+ sal_uInt8 nEndFlag = 0;
+ while ( nBlocks > 1 && nEndFlag != 0x0d ) {
+ rStream.Seek( nBlocks-- * nHeaderBlockSize );
+ rStream.ReadUChar( nEndFlag );
+ }
+
+ return ( 0x0d == nEndFlag );
+}
+
+OUString SAL_CALL ScFilterDetect::detect( uno::Sequence<beans::PropertyValue>& lDescriptor )
+{
+ MediaDescriptor aMediaDesc( lDescriptor );
+ OUString aTypeName = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME, OUString() );
+ uno::Reference< io::XInputStream > xStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY );
+ if ( !xStream.is() )
+ return OUString();
+
+ SfxMedium aMedium;
+ aMedium.UseInteractionHandler( false );
+ aMedium.setStreamToLoadFrom( xStream, true );
+
+ SvStream* pStream = aMedium.GetInStream();
+ if ( !pStream || pStream->GetError() )
+ // No stream, no detection.
+ return OUString();
+
+ const char* pSearchFilterName = nullptr;
+ if (aTypeName == "calc_Lotus")
+ {
+ if (!detectThisFormat(*pStream, pLotus) && !detectThisFormat(*pStream, pLotusNew) && !detectThisFormat(*pStream, pLotus2))
+ return OUString();
+
+ pSearchFilterName = "Lotus";
+ }
+ else if (aTypeName == "calc_QPro")
+ {
+ if (!detectThisFormat(*pStream, pQPro))
+ return OUString();
+
+ pSearchFilterName = "Quattro Pro 6.0";
+ }
+ else if (aTypeName == "calc_SYLK")
+ {
+ if (!detectThisFormat(*pStream, pSylk))
+ return OUString();
+
+ pSearchFilterName = "SYLK";
+ }
+ else if (aTypeName == "calc_DIF")
+ {
+ if (!detectThisFormat(*pStream, pDIF1) && !detectThisFormat(*pStream, pDIF2))
+ return OUString();
+
+ pSearchFilterName = "DIF";
+ }
+ else if (aTypeName == "calc_dBase")
+ {
+ if (!lcl_MayBeDBase(*pStream))
+ return OUString();
+
+ pSearchFilterName = "dBase";
+ }
+ else
+ return OUString();
+
+ SfxFilterMatcher aMatcher("scalc");
+ std::shared_ptr<const SfxFilter> pFilter = aMatcher.GetFilter4FilterName(OUString::createFromAscii(pSearchFilterName));
+
+ if (!pFilter)
+ return OUString();
+
+ aMediaDesc[MediaDescriptor::PROP_FILTERNAME] <<= pFilter->GetName();
+ aMediaDesc >> lDescriptor;
+ return aTypeName;
+}
+
+OUString SAL_CALL ScFilterDetect::getImplementationName()
+{
+ return "com.sun.star.comp.calc.FormatDetector";
+}
+
+sal_Bool ScFilterDetect::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence<OUString> ScFilterDetect::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ExtendedTypeDetection" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_calc_FormatDetector_get_implementation(css::uno::XComponentContext* /*context*/,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScFilterDetect);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/scdetect.hxx b/sc/source/ui/unoobj/scdetect.hxx
new file mode 100644
index 0000000000..697bd8d58e
--- /dev/null
+++ b/sc/source/ui/unoobj/scdetect.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star {
+ namespace beans { struct PropertyValue; }
+}
+
+class ScFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo >
+{
+public:
+ explicit ScFilterDetect();
+ virtual ~ScFilterDetect() override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XExtendedFilterDetect
+
+ virtual OUString SAL_CALL detect( css::uno::Sequence<css::beans::PropertyValue>& lDescriptor ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/servuno.cxx b/sc/source/ui/unoobj/servuno.cxx
new file mode 100644
index 0000000000..4326817027
--- /dev/null
+++ b/sc/source/ui/unoobj/servuno.cxx
@@ -0,0 +1,625 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <config_features.h>
+
+#include <sal/macros.h>
+#include <svtools/unoimap.hxx>
+#include <svx/unofill.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/text/textfield/Type.hpp>
+
+#include <editsrc.hxx>
+#include <servuno.hxx>
+#include <unonames.hxx>
+#include <appluno.hxx>
+#include <cellsuno.hxx>
+#include <fielduno.hxx>
+#include <styleuno.hxx>
+#include <afmtuno.hxx>
+#include <defltuno.hxx>
+#include <drdefuno.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <confuno.hxx>
+#include <shapeuno.hxx>
+#include "cellvaluebinding.hxx"
+#include "celllistsource.hxx"
+#include <addruno.hxx>
+#include <chart2uno.hxx>
+#include <tokenuno.hxx>
+#include <PivotTableDataProvider.hxx>
+
+// Support creation of GraphicStorageHandler and EmbeddedObjectResolver
+#include <svx/xmleohlp.hxx>
+#include <svx/xmlgrhlp.hxx>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/document/XCodeNameQuery.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <svx/unomod.hxx>
+#include <vbahelper/vbaaccesshelper.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <basic/basmgr.hxx>
+#include <sfx2/app.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+
+using namespace ::com::sun::star;
+
+#if HAVE_FEATURE_SCRIPTING
+
+static bool isInVBAMode( ScDocShell& rDocSh )
+{
+ uno::Reference<script::XLibraryContainer> xLibContainer = rDocSh.GetBasicContainer();
+ uno::Reference<script::vba::XVBACompatibility> xVBACompat( xLibContainer, uno::UNO_QUERY );
+ if ( xVBACompat.is() )
+ return xVBACompat->getVBACompatibilityMode();
+ return false;
+}
+
+#endif
+
+namespace {
+
+#if HAVE_FEATURE_SCRIPTING
+class ScVbaObjectForCodeNameProvider : public ::cppu::WeakImplHelper< container::XNameAccess >
+{
+ uno::Any maWorkbook;
+ uno::Any maCachedObject;
+ ScDocShell* mpDocShell;
+public:
+ explicit ScVbaObjectForCodeNameProvider( ScDocShell* pDocShell ) : mpDocShell( pDocShell )
+ {
+ uno::Sequence< uno::Any > aArgs{
+ // access the application object ( parent for workbook )
+ uno::Any(ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.Application", {} )),
+ uno::Any(uno::Reference(static_cast<css::sheet::XSpreadsheetDocument*>(mpDocShell->GetModel())))
+ };
+ maWorkbook <<= ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.excel.Workbook", aArgs );
+ }
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ SolarMutexGuard aGuard;
+ maCachedObject = uno::Any(); // clear cached object
+
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ // aName is generated from the stream name which can be different ( case-wise )
+ // from the code name
+ if( aName.equalsIgnoreAsciiCase( rDoc.GetCodeName() ) )
+ maCachedObject = maWorkbook;
+ else
+ {
+ OUString sCodeName;
+ SCTAB nCount = rDoc.GetTableCount();
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ rDoc.GetCodeName( i, sCodeName );
+ // aName is generated from the stream name which can be different ( case-wise )
+ // from the code name
+ if( sCodeName.equalsIgnoreAsciiCase( aName ) )
+ {
+ OUString sSheetName;
+ if( rDoc.GetName( i, sSheetName ) )
+ {
+ rtl::Reference< ScModelObj > xSpreadDoc( mpDocShell->GetModel() );
+ uno::Reference<sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xSheets, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xIndexAccess->getByIndex( i ), uno::UNO_QUERY_THROW );
+ uno::Sequence< uno::Any > aArgs{ maWorkbook, uno::Any(uno::Reference< frame::XModel >(xSpreadDoc)), uno::Any(sSheetName) };
+ // use the convenience function
+ maCachedObject <<= ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.excel.Worksheet", aArgs );
+ break;
+ }
+ }
+ }
+ }
+ return maCachedObject.hasValue();
+
+ }
+ css::uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ SolarMutexGuard aGuard;
+ if ( !hasByName( aName ) )
+ throw css::container::NoSuchElementException();
+ return maCachedObject;
+ }
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ SolarMutexGuard aGuard;
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ uno::Sequence< OUString > aNames( nCount + 1 );
+ auto pNames = aNames.getArray();
+ SCTAB index = 0;
+ OUString sCodeName;
+ for( ; index < nCount; ++index )
+ {
+ rDoc.GetCodeName( index, sCodeName );
+ pNames[ index ] = sCodeName;
+ }
+ pNames[ index ] = rDoc.GetCodeName();
+ return aNames;
+ }
+ // XElemenAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override { return uno::Type(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override { return true; }
+
+};
+
+class ScVbaCodeNameProvider : public ::cppu::WeakImplHelper< document::XCodeNameQuery >
+{
+ ScDocShell& mrDocShell;
+public:
+ explicit ScVbaCodeNameProvider( ScDocShell& rDocShell ) : mrDocShell(rDocShell) {}
+ // XCodeNameQuery
+ OUString SAL_CALL getCodeNameForObject( const uno::Reference< uno::XInterface >& xIf ) override
+ {
+ SolarMutexGuard aGuard;
+ OUString sCodeName;
+
+ // need to find the page ( and index ) for this control
+ uno::Reference< container::XIndexAccess > xIndex( mrDocShell.GetModel()->getDrawPages(), uno::UNO_QUERY_THROW );
+ sal_Int32 nLen = xIndex->getCount();
+ bool bMatched = false;
+ for ( sal_Int32 index = 0; index < nLen; ++index )
+ {
+ try
+ {
+ uno::Reference< form::XFormsSupplier > xFormSupplier( xIndex->getByIndex( index ), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xFormIndex( xFormSupplier->getForms(), uno::UNO_QUERY_THROW );
+ // get the www-standard container
+ uno::Reference< container::XIndexAccess > xFormControls( xFormIndex->getByIndex(0), uno::UNO_QUERY_THROW );
+ sal_Int32 nCntrls = xFormControls->getCount();
+ for( sal_Int32 cIndex = 0; cIndex < nCntrls; ++cIndex )
+ {
+ uno::Reference< uno::XInterface > xControl( xFormControls->getByIndex( cIndex ), uno::UNO_QUERY_THROW );
+ bMatched = ( xControl == xIf );
+ if ( bMatched )
+ {
+ OUString sName;
+ mrDocShell.GetDocument().GetCodeName( static_cast<SCTAB>( index ), sName );
+ sCodeName = sName;
+ }
+ }
+ }
+ catch( uno::Exception& ) {}
+ if ( bMatched )
+ break;
+ }
+ // Probably should throw here ( if !bMatched )
+ return sCodeName;
+ }
+
+ OUString SAL_CALL getCodeNameForContainer( const uno::Reference<uno::XInterface>& xContainer ) override
+ {
+ SolarMutexGuard aGuard;
+ uno::Reference<container::XIndexAccess> xIndex(mrDocShell.GetModel()->getDrawPages(), uno::UNO_QUERY_THROW);
+
+ for (sal_Int32 i = 0, n = xIndex->getCount(); i < n; ++i)
+ {
+ try
+ {
+ uno::Reference<form::XFormsSupplier> xFormSupplier(xIndex->getByIndex(i), uno::UNO_QUERY_THROW);
+ uno::Reference<container::XIndexAccess> xFormIndex(xFormSupplier->getForms(), uno::UNO_QUERY_THROW);
+ // get the www-standard container
+ uno::Reference<container::XIndexAccess> xFormControls(xFormIndex->getByIndex(0), uno::UNO_QUERY_THROW);
+ if (xFormControls == xContainer)
+ {
+ OUString aName;
+ if (mrDocShell.GetDocument().GetCodeName(static_cast<SCTAB>(i), aName))
+ return aName;
+ }
+ }
+ catch( uno::Exception& ) {}
+ }
+ return OUString();
+ }
+};
+
+#endif
+
+using Type = ScServiceProvider::Type;
+
+struct ProvNamesId_Type
+{
+ OUString pName;
+ ScServiceProvider::Type nType;
+};
+
+const ProvNamesId_Type aProvNamesId[] =
+{
+ { "com.sun.star.sheet.Spreadsheet", Type::SHEET },
+ { "com.sun.star.text.TextField.URL", Type::URLFIELD },
+ { "com.sun.star.text.TextField.PageNumber", Type::PAGEFIELD },
+ { "com.sun.star.text.TextField.PageCount", Type::PAGESFIELD },
+ { "com.sun.star.text.TextField.Date", Type::DATEFIELD },
+ { "com.sun.star.text.TextField.Time", Type::TIMEFIELD },
+ { "com.sun.star.text.TextField.DateTime", Type::EXT_TIMEFIELD },
+ { "com.sun.star.text.TextField.DocInfo.Title", Type::TITLEFIELD },
+ { "com.sun.star.text.TextField.FileName", Type::FILEFIELD },
+ { "com.sun.star.text.TextField.SheetName", Type::SHEETFIELD },
+ { "com.sun.star.style.CellStyle", Type::CELLSTYLE },
+ { "com.sun.star.style.PageStyle", Type::PAGESTYLE },
+ { "com.sun.star.style.GraphicStyle", Type::GRAPHICSTYLE },
+ { "com.sun.star.sheet.TableAutoFormat", Type::AUTOFORMAT },
+ { "com.sun.star.sheet.TableAutoFormats", Type::AUTOFORMATS },
+ { "com.sun.star.sheet.SheetCellRanges", Type::CELLRANGES },
+ { "com.sun.star.sheet.FunctionDescriptions", Type::FUNCTIONDESCRIPTIONS },
+ { "com.sun.star.sheet.GlobalSheetSettings", Type::GLOBALSHEETSETTINGS },
+ { "com.sun.star.sheet.RecentFunctions", Type::RECENTFUNCTIONS },
+ { "com.sun.star.drawing.GradientTable", Type::GRADTAB },
+ { "com.sun.star.drawing.HatchTable", Type::HATCHTAB },
+ { "com.sun.star.drawing.BitmapTable", Type::BITMAPTAB },
+ { "com.sun.star.drawing.TransparencyGradientTable", Type::TRGRADTAB },
+ { "com.sun.star.drawing.MarkerTable", Type::MARKERTAB },
+ { "com.sun.star.drawing.DashTable", Type::DASHTAB },
+ { "com.sun.star.text.NumberingRules", Type::NUMRULES },
+ { "com.sun.star.sheet.Defaults", Type::DOCDEFLTS },
+ { "com.sun.star.drawing.Defaults", Type::DRAWDEFLTS },
+ { "com.sun.star.comp.SpreadsheetSettings", Type::DOCSPRSETT },
+ { "com.sun.star.document.Settings", Type::DOCCONF },
+ { "com.sun.star.image.ImageMapRectangleObject", Type::IMAP_RECT },
+ { "com.sun.star.image.ImageMapCircleObject", Type::IMAP_CIRC },
+ { "com.sun.star.image.ImageMapPolygonObject", Type::IMAP_POLY },
+
+ // Support creation of GraphicStorageHandler and EmbeddedObjectResolver
+ { "com.sun.star.document.ExportGraphicStorageHandler", Type::EXPORT_GRAPHIC_STORAGE_HANDLER },
+ { "com.sun.star.document.ImportGraphicStorageHandler", Type::IMPORT_GRAPHIC_STORAGE_HANDLER },
+ { "com.sun.star.document.ExportEmbeddedObjectResolver", Type::EXPORT_EOR },
+ { "com.sun.star.document.ImportEmbeddedObjectResolver", Type::IMPORT_EOR },
+
+ { SC_SERVICENAME_VALBIND, Type::VALBIND },
+ { SC_SERVICENAME_LISTCELLBIND, Type::LISTCELLBIND },
+ { SC_SERVICENAME_LISTSOURCE, Type::LISTSOURCE },
+ { SC_SERVICENAME_CELLADDRESS, Type::CELLADDRESS },
+ { SC_SERVICENAME_RANGEADDRESS, Type::RANGEADDRESS },
+
+ { "com.sun.star.sheet.DocumentSettings",Type::SHEETDOCSET },
+
+ { SC_SERVICENAME_CHDATAPROV, Type::CHDATAPROV },
+ { SC_SERVICENAME_CHART_PIVOTTABLE_DATAPROVIDER, Type::CHART_PIVOTTABLE_DATAPROVIDER },
+ { SC_SERVICENAME_FORMULAPARS, Type::FORMULAPARS },
+ { SC_SERVICENAME_OPCODEMAPPER, Type::OPCODEMAPPER },
+ { "ooo.vba.VBAObjectModuleObjectProvider", Type::VBAOBJECTPROVIDER },
+ { "ooo.vba.VBACodeNameProvider", Type::VBACODENAMEPROVIDER },
+ { "ooo.vba.VBAGlobals", Type::VBAGLOBALS },
+
+ // case-correct versions of the service names (#i102468#)
+ { "com.sun.star.text.textfield.URL", Type::URLFIELD },
+ { "com.sun.star.text.textfield.PageNumber", Type::PAGEFIELD },
+ { "com.sun.star.text.textfield.PageCount", Type::PAGESFIELD },
+ { "com.sun.star.text.textfield.Date", Type::DATEFIELD },
+ { "com.sun.star.text.textfield.Time", Type::TIMEFIELD },
+ { "com.sun.star.text.textfield.DateTime", Type::EXT_TIMEFIELD },
+ { "com.sun.star.text.textfield.docinfo.Title", Type::TITLEFIELD },
+ { "com.sun.star.text.textfield.FileName", Type::FILEFIELD },
+ { "com.sun.star.text.textfield.SheetName", Type::SHEETFIELD },
+ { "ooo.vba.VBAGlobals", Type::VBAGLOBALS },
+};
+
+// old service names that were in 567 still work in createInstance,
+// in case some macro is still using them
+const ProvNamesId_Type aOldNames[] =
+{
+ { "stardiv.one.text.TextField.URL", Type::URLFIELD },
+ { "stardiv.one.text.TextField.PageNumber", Type::PAGEFIELD },
+ { "stardiv.one.text.TextField.PageCount", Type::PAGESFIELD },
+ { "stardiv.one.text.TextField.Date", Type::DATEFIELD },
+ { "stardiv.one.text.TextField.Time", Type::TIMEFIELD },
+ { "stardiv.one.text.TextField.DocumentTitle", Type::TITLEFIELD },
+ { "stardiv.one.text.TextField.FileName", Type::FILEFIELD },
+ { "stardiv.one.text.TextField.SheetName", Type::SHEETFIELD },
+ { "stardiv.one.style.CellStyle", Type::CELLSTYLE },
+ { "stardiv.one.style.PageStyle", Type::PAGESTYLE },
+};
+
+sal_Int32 getFieldType(ScServiceProvider::Type nOldType)
+{
+ switch (nOldType)
+ {
+ case Type::URLFIELD:
+ return text::textfield::Type::URL;
+ case Type::PAGEFIELD:
+ return text::textfield::Type::PAGE;
+ case Type::PAGESFIELD:
+ return text::textfield::Type::PAGES;
+ case Type::DATEFIELD:
+ return text::textfield::Type::DATE;
+ case Type::TIMEFIELD:
+ return text::textfield::Type::TIME;
+ case Type::EXT_TIMEFIELD:
+ return text::textfield::Type::EXTENDED_TIME;
+ case Type::TITLEFIELD:
+ return text::textfield::Type::DOCINFO_TITLE;
+ case Type::FILEFIELD:
+ return text::textfield::Type::EXTENDED_FILE;
+ case Type::SHEETFIELD:
+ return text::textfield::Type::TABLE;
+ default:
+ ;
+ }
+
+ return text::textfield::Type::URL; // default to URL for no reason whatsoever.
+}
+
+} // namespace
+
+
+ScServiceProvider::Type ScServiceProvider::GetProviderType(std::u16string_view rServiceName)
+{
+ if (!rServiceName.empty())
+ {
+ for (const ProvNamesId_Type & i : aProvNamesId)
+ {
+ if (rServiceName == i.pName)
+ {
+ return i.nType;
+ }
+ }
+
+ for (const ProvNamesId_Type & rOldName : aOldNames)
+ {
+ if (rServiceName == rOldName.pName)
+ {
+ OSL_FAIL("old service name used");
+ return rOldName.nType;
+ }
+ }
+ }
+ return Type::INVALID;
+}
+
+uno::Reference<uno::XInterface> ScServiceProvider::MakeInstance(
+ Type nType, ScDocShell* pDocShell )
+{
+ uno::Reference<uno::XInterface> xRet;
+
+ switch (nType)
+ {
+ case Type::SHEET:
+ // not inserted yet - DocShell=Null
+ xRet.set(static_cast<sheet::XSpreadsheet*>(new ScTableSheetObj(nullptr,0)));
+ break;
+ case Type::URLFIELD:
+ case Type::PAGEFIELD:
+ case Type::PAGESFIELD:
+ case Type::DATEFIELD:
+ case Type::TIMEFIELD:
+ case Type::EXT_TIMEFIELD:
+ case Type::TITLEFIELD:
+ case Type::FILEFIELD:
+ case Type::SHEETFIELD:
+ {
+ uno::Reference<text::XTextRange> xNullContent;
+ xRet.set(static_cast<text::XTextField*>(
+ new ScEditFieldObj(xNullContent, nullptr, getFieldType(nType), ESelection())));
+ } break;
+ case Type::CELLSTYLE:
+ xRet.set(static_cast<style::XStyle*>(new ScStyleObj( nullptr, SfxStyleFamily::Para, OUString() )));
+ break;
+ case Type::PAGESTYLE:
+ xRet.set(static_cast<style::XStyle*>(new ScStyleObj( nullptr, SfxStyleFamily::Page, OUString() )));
+ break;
+ case Type::GRAPHICSTYLE:
+ if (pDocShell)
+ {
+ pDocShell->MakeDrawLayer();
+ xRet.set(static_cast<style::XStyle*>(new ScStyleObj( nullptr, SfxStyleFamily::Frame, OUString() )));
+ }
+ break;
+ case Type::AUTOFORMAT:
+ xRet.set(static_cast<container::XIndexAccess*>(new ScAutoFormatObj( SC_AFMTOBJ_INVALID )));
+ break;
+ case Type::AUTOFORMATS:
+ xRet.set(static_cast<container::XIndexAccess*>(new ScAutoFormatsObj()));
+ break;
+ case Type::CELLRANGES:
+ // isn't inserted, rather filled
+ // -> DocShell must be set, but empty ranges
+ if (pDocShell)
+ xRet.set(static_cast<sheet::XSheetCellRanges*>(new ScCellRangesObj( pDocShell, ScRangeList() )));
+ break;
+ case Type::FUNCTIONDESCRIPTIONS:
+ xRet.set(static_cast<sheet::XFunctionDescriptions*>(new ScFunctionListObj()));
+ break;
+ case Type::GLOBALSHEETSETTINGS:
+ xRet.set(static_cast<sheet::XGlobalSheetSettings*>(new ScSpreadsheetSettings()));
+ break;
+ case Type::RECENTFUNCTIONS:
+ xRet.set(static_cast<sheet::XRecentFunctions*>(new ScRecentFunctionsObj()));
+ break;
+ case Type::DOCDEFLTS:
+ if (pDocShell)
+ xRet.set(static_cast<beans::XPropertySet*>(new ScDocDefaultsObj( pDocShell )));
+ break;
+ case Type::DRAWDEFLTS:
+ if (pDocShell)
+ xRet.set(static_cast<beans::XPropertySet*>(new ScDrawDefaultsObj( pDocShell )));
+ break;
+
+ // Drawing layer tables are not in SvxUnoDrawMSFactory,
+ // because SvxUnoDrawMSFactory doesn't have a SdrModel pointer.
+ // Drawing layer is always allocated if not there (MakeDrawLayer).
+
+ case Type::GRADTAB:
+ if (pDocShell)
+ xRet.set(SvxUnoGradientTable_createInstance( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::HATCHTAB:
+ if (pDocShell)
+ xRet.set(SvxUnoHatchTable_createInstance( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::BITMAPTAB:
+ if (pDocShell)
+ xRet.set(SvxUnoBitmapTable_createInstance( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::TRGRADTAB:
+ if (pDocShell)
+ xRet.set(SvxUnoTransGradientTable_createInstance( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::MARKERTAB:
+ if (pDocShell)
+ xRet.set(SvxUnoMarkerTable_createInstance( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::DASHTAB:
+ if (pDocShell)
+ xRet.set(SvxUnoDashTable_createInstance( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::NUMRULES:
+ if (pDocShell)
+ xRet.set(SvxCreateNumRule( pDocShell->MakeDrawLayer() ));
+ break;
+ case Type::DOCSPRSETT:
+ case Type::SHEETDOCSET:
+ case Type::DOCCONF:
+ if (pDocShell)
+ xRet.set(static_cast<beans::XPropertySet*>(new ScDocumentConfiguration(pDocShell)));
+ break;
+ case Type::IMAP_RECT:
+ xRet.set(SvUnoImageMapRectangleObject_createInstance( ScShapeObj::GetSupportedMacroItems() ));
+ break;
+ case Type::IMAP_CIRC:
+ xRet.set(SvUnoImageMapCircleObject_createInstance( ScShapeObj::GetSupportedMacroItems() ));
+ break;
+ case Type::IMAP_POLY:
+ xRet.set(SvUnoImageMapPolygonObject_createInstance( ScShapeObj::GetSupportedMacroItems() ));
+ break;
+
+ // Support creation of GraphicStorageHandler and EmbeddedObjectResolver
+ case Type::EXPORT_GRAPHIC_STORAGE_HANDLER:
+ xRet.set(getXWeak(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Write )));
+ break;
+ case Type::IMPORT_GRAPHIC_STORAGE_HANDLER:
+ xRet.set(getXWeak(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Read )));
+ break;
+ case Type::EXPORT_EOR:
+ if (pDocShell)
+ xRet.set(getXWeak(new SvXMLEmbeddedObjectHelper( *pDocShell, SvXMLEmbeddedObjectHelperMode::Write )));
+ break;
+ case Type::IMPORT_EOR:
+ if (pDocShell)
+ xRet.set(getXWeak(new SvXMLEmbeddedObjectHelper( *pDocShell, SvXMLEmbeddedObjectHelperMode::Read )));
+ break;
+ case Type::VALBIND:
+ case Type::LISTCELLBIND:
+ if (pDocShell)
+ {
+ bool bListPos = ( nType == Type::LISTCELLBIND );
+ uno::Reference<sheet::XSpreadsheetDocument> xDoc( pDocShell->GetBaseModel(), uno::UNO_QUERY );
+ xRet.set(*new calc::OCellValueBinding( xDoc, bListPos ));
+ }
+ break;
+ case Type::LISTSOURCE:
+ if (pDocShell)
+ {
+ uno::Reference<sheet::XSpreadsheetDocument> xDoc( pDocShell->GetBaseModel(), uno::UNO_QUERY );
+ xRet.set(*new calc::OCellListSource( xDoc ));
+ }
+ break;
+ case Type::CELLADDRESS:
+ case Type::RANGEADDRESS:
+ if (pDocShell)
+ {
+ bool bIsRange = ( nType == Type::RANGEADDRESS );
+ xRet.set(*new ScAddressConversionObj( pDocShell, bIsRange ));
+ }
+ break;
+ case Type::CHDATAPROV:
+ if (pDocShell)
+ xRet = *new ScChart2DataProvider( &pDocShell->GetDocument() );
+ break;
+ case Type::CHART_PIVOTTABLE_DATAPROVIDER:
+ if (pDocShell)
+ xRet = *new sc::PivotTableDataProvider(pDocShell->GetDocument());
+ break;
+ case Type::FORMULAPARS:
+ if (pDocShell)
+ xRet.set(static_cast<sheet::XFormulaParser*>(new ScFormulaParserObj( pDocShell )));
+ break;
+ case Type::OPCODEMAPPER:
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScAddress aAddress;
+ ScCompiler* pComp = new ScCompiler(rDoc, aAddress, rDoc.GetGrammar());
+ xRet.set(static_cast<sheet::XFormulaOpCodeMapper*>(new ScFormulaOpCodeMapperObj(::std::unique_ptr<formula::FormulaCompiler> (pComp))));
+ break;
+ }
+ break;
+#if HAVE_FEATURE_SCRIPTING
+ case Type::VBAOBJECTPROVIDER:
+ if (pDocShell && pDocShell->GetDocument().IsInVBAMode())
+ {
+ xRet.set(static_cast<container::XNameAccess*>(new ScVbaObjectForCodeNameProvider( pDocShell )));
+ }
+ break;
+ case Type::VBACODENAMEPROVIDER:
+ if ( pDocShell && isInVBAMode( *pDocShell ) )
+ {
+ xRet.set(static_cast<document::XCodeNameQuery*>(new ScVbaCodeNameProvider(*pDocShell)));
+ }
+ break;
+ case Type::VBAGLOBALS:
+ if (pDocShell)
+ {
+ uno::Any aGlobs;
+ if ( !pDocShell->GetBasicManager()->GetGlobalUNOConstant( "VBAGlobals", aGlobs ) )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference(static_cast<css::sheet::XSpreadsheetDocument*>(pDocShell->GetModel()))) };
+ xRet = ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( "ooo.vba.excel.Globals", aArgs );
+ pDocShell->GetBasicManager()->SetGlobalUNOConstant( "VBAGlobals", uno::Any( xRet ) );
+ BasicManager* pAppMgr = SfxApplication::GetBasicManager();
+ if ( pAppMgr )
+ pAppMgr->SetGlobalUNOConstant( "ThisExcelDoc", aArgs[ 0 ] );
+
+ // create the VBA document event processor
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents(
+ ::ooo::vba::createVBAUnoAPIServiceWithArgs( pDocShell, "com.sun.star.script.vba.VBASpreadsheetEventProcessor", aArgs ), uno::UNO_QUERY );
+ pDocShell->GetDocument().SetVbaEventProcessor( xVbaEvents );
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return xRet;
+}
+
+uno::Sequence<OUString> ScServiceProvider::GetAllServiceNames()
+{
+ const sal_uInt16 nEntries = SAL_N_ELEMENTS(aProvNamesId);
+ uno::Sequence<OUString> aRet(nEntries);
+ OUString* pArray = aRet.getArray();
+ for (sal_uInt16 i = 0; i < nEntries; i++)
+ {
+ pArray[i] = aProvNamesId[i].pName;
+ }
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/shapeuno.cxx b/sc/source/ui/unoobj/shapeuno.cxx
new file mode 100644
index 0000000000..5d1ecadb08
--- /dev/null
+++ b/sc/source/ui/unoobj/shapeuno.cxx
@@ -0,0 +1,1469 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <svtools/unoevent.hxx>
+#include <svtools/unoimap.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/event.hxx>
+#include <editeng/unofield.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+
+#include <shapeuno.hxx>
+#include <cellsuno.hxx>
+#include <textuno.hxx>
+#include <fielduno.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <unonames.hxx>
+#include <styleuno.hxx>
+
+using namespace ::com::sun::star;
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetShapeMap()
+{
+ static const SfxItemPropertyMapEntry aShapeMap_Impl[] =
+ {
+ { SC_UNONAME_ANCHOR, 0, cppu::UnoType<uno::XInterface>::get(), 0, 0 },
+ { SC_UNONAME_RESIZE_WITH_CELL, 0, cppu::UnoType<sal_Bool>::get(), 0, 0 },
+ { SC_UNONAME_HORIPOS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_IMAGEMAP, 0, cppu::UnoType<container::XIndexContainer>::get(), 0, 0 },
+ { SC_UNONAME_VERTPOS, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { SC_UNONAME_MOVEPROTECT, 0, cppu::UnoType<sal_Bool>::get(), 0, 0 },
+ { SC_UNONAME_HYPERLINK, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_URL, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { SC_UNONAME_STYLE, 0, cppu::UnoType<style::XStyle>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ return aShapeMap_Impl;
+}
+
+const SvEventDescription* ScShapeObj::GetSupportedMacroItems()
+{
+ static const SvEventDescription aMacroDescriptionsImpl[] =
+ {
+ { SvMacroItemId::NONE, nullptr }
+ };
+ return aMacroDescriptionsImpl;
+}
+ScMacroInfo* ScShapeObj_getShapeHyperMacroInfo( const ScShapeObj* pShape, bool bCreate = false )
+{
+ if( pShape )
+ if( SdrObject* pObj = pShape->GetSdrObject() )
+ return ScDrawLayer::GetMacroInfo( pObj, bCreate );
+ return nullptr;
+}
+
+ScShapeObj::ScShapeObj( uno::Reference<drawing::XShape>& xShape ) :
+ pShapePropertySet(nullptr),
+ pShapePropertyState(nullptr),
+ bIsTextShape(false),
+ bIsNoteCaption(false)
+{
+ osl_atomic_increment( &m_refCount );
+
+ {
+ mxShapeAgg.set( xShape, uno::UNO_QUERY );
+ // extra block to force deletion of the temporary before setDelegator
+ }
+
+ if (mxShapeAgg.is())
+ {
+ xShape = nullptr; // during setDelegator, mxShapeAgg must be the only ref
+
+ mxShapeAgg->setDelegator( getXWeak() );
+
+ xShape.set(uno::Reference<drawing::XShape>( mxShapeAgg, uno::UNO_QUERY ));
+
+ bIsTextShape = ( comphelper::getFromUnoTunnel<SvxUnoTextBase>( mxShapeAgg ) != nullptr );
+ }
+
+ {
+ SdrObject* pObj = GetSdrObject();
+ if ( pObj )
+ {
+ bIsNoteCaption = ScDrawLayer::IsNoteCaption( pObj );
+ }
+ }
+
+ osl_atomic_decrement( &m_refCount );
+}
+
+ScShapeObj::~ScShapeObj()
+{
+// if (mxShapeAgg.is())
+// mxShapeAgg->setDelegator(uno::Reference<uno::XInterface>());
+}
+
+// XInterface
+
+uno::Any SAL_CALL ScShapeObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = ScShapeObj_Base::queryInterface( rType );
+
+ if ( !aRet.hasValue() && bIsTextShape )
+ aRet = ScShapeObj_TextBase::queryInterface( rType );
+
+ if ( !aRet.hasValue() && bIsNoteCaption )
+ aRet = ScShapeObj_ChildBase::queryInterface( rType );
+
+ if ( !aRet.hasValue() && mxShapeAgg.is() )
+ aRet = mxShapeAgg->queryAggregation( rType );
+
+ return aRet;
+}
+
+void SAL_CALL ScShapeObj::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ScShapeObj::release() noexcept
+{
+ OWeakObject::release();
+}
+
+void ScShapeObj::GetShapePropertySet()
+{
+ // #i61908# Store the result of queryAggregation in a member.
+ // The reference in mxShapeAgg is kept for this object's lifetime, so the pointer is always valid.
+
+ if (!pShapePropertySet)
+ {
+ uno::Reference<beans::XPropertySet> xProp;
+ if ( mxShapeAgg.is() )
+ mxShapeAgg->queryAggregation( cppu::UnoType<beans::XPropertySet>::get()) >>= xProp;
+ pShapePropertySet = xProp.get();
+ }
+}
+
+void ScShapeObj::GetShapePropertyState()
+{
+ // #i61908# Store the result of queryAggregation in a member.
+ // The reference in mxShapeAgg is kept for this object's lifetime, so the pointer is always valid.
+
+ if (!pShapePropertyState)
+ {
+ uno::Reference<beans::XPropertyState> xState;
+ if ( mxShapeAgg.is() )
+ mxShapeAgg->queryAggregation( cppu::UnoType<beans::XPropertyState>::get()) >>= xState;
+ pShapePropertyState = xState.get();
+ }
+}
+
+static uno::Reference<lang::XComponent> lcl_GetComponent( const uno::Reference<uno::XAggregation>& xAgg )
+{
+ uno::Reference<lang::XComponent> xRet;
+ if ( xAgg.is() )
+ xAgg->queryAggregation( cppu::UnoType<lang::XComponent>::get()) >>= xRet;
+ return xRet;
+}
+
+static uno::Reference<text::XText> lcl_GetText( const uno::Reference<uno::XAggregation>& xAgg )
+{
+ uno::Reference<text::XText> xRet;
+ if ( xAgg.is() )
+ xAgg->queryAggregation( cppu::UnoType<text::XText>::get()) >>= xRet;
+ return xRet;
+}
+
+static uno::Reference<text::XSimpleText> lcl_GetSimpleText( const uno::Reference<uno::XAggregation>& xAgg )
+{
+ uno::Reference<text::XSimpleText> xRet;
+ if ( xAgg.is() )
+ xAgg->queryAggregation( cppu::UnoType<text::XSimpleText>::get()) >>= xRet;
+ return xRet;
+}
+
+static uno::Reference<text::XTextRange> lcl_GetTextRange( const uno::Reference<uno::XAggregation>& xAgg )
+{
+ uno::Reference<text::XTextRange> xRet;
+ if ( xAgg.is() )
+ xAgg->queryAggregation( cppu::UnoType<text::XTextRange>::get()) >>= xRet;
+ return xRet;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScShapeObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+
+ // #i61527# cache property set info for this object
+ if ( !mxPropSetInfo.is() )
+ {
+ // mix own and aggregated properties:
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ {
+ uno::Reference<beans::XPropertySetInfo> xAggInfo(pShapePropertySet->getPropertySetInfo());
+ const uno::Sequence<beans::Property> aPropSeq(xAggInfo->getProperties());
+ mxPropSetInfo.set(new SfxExtItemPropertySetInfo( lcl_GetShapeMap(), aPropSeq ));
+ }
+ }
+ return mxPropSetInfo;
+}
+
+static bool lcl_GetPageNum( const SdrPage* pPage, SdrModel& rModel, SCTAB& rNum )
+{
+ sal_uInt16 nCount = rModel.GetPageCount();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ if ( rModel.GetPage(i) == pPage )
+ {
+ rNum = static_cast<SCTAB>(i);
+ return true;
+ }
+
+ return false;
+}
+
+static bool lcl_GetCaptionPoint( const uno::Reference< drawing::XShape >& xShape, awt::Point& rCaptionPoint )
+{
+ bool bReturn = false;
+ OUString sType(xShape->getShapeType());
+ bool bCaptionShape( sType == "com.sun.star.drawing.CaptionShape" );
+ if (bCaptionShape)
+ {
+ uno::Reference < beans::XPropertySet > xShapeProp (xShape, uno::UNO_QUERY);
+ if (xShapeProp.is())
+ {
+ xShapeProp->getPropertyValue("CaptionPoint") >>= rCaptionPoint;
+ bReturn = true;
+ }
+ }
+ return bReturn;
+}
+
+static ScRange lcl_GetAnchorCell( const uno::Reference< drawing::XShape >& xShape, const ScDocument* pDoc, SCTAB nTab,
+ awt::Point& rUnoPoint, awt::Size& rUnoSize, awt::Point& rCaptionPoint )
+{
+ ScRange aReturn;
+ rUnoPoint = xShape->getPosition();
+ bool bCaptionShape(lcl_GetCaptionPoint(xShape, rCaptionPoint));
+ if (pDoc->IsNegativePage(nTab))
+ {
+ rUnoSize = xShape->getSize();
+ rUnoPoint.X += rUnoSize.Width; // the right top point is base
+ if (bCaptionShape)
+ {
+ if (rCaptionPoint.X > 0 && rCaptionPoint.X > rUnoSize.Width)
+ rUnoPoint.X += rCaptionPoint.X - rUnoSize.Width;
+ if (rCaptionPoint.Y < 0)
+ rUnoPoint.Y += rCaptionPoint.Y;
+ }
+ aReturn = pDoc->GetRange( nTab, tools::Rectangle( VCLPoint(rUnoPoint), VCLPoint(rUnoPoint) ));
+ }
+ else
+ {
+ if (bCaptionShape)
+ {
+ if (rCaptionPoint.X < 0)
+ rUnoPoint.X += rCaptionPoint.X;
+ if (rCaptionPoint.Y < 0)
+ rUnoPoint.Y += rCaptionPoint.Y;
+ }
+ aReturn = pDoc->GetRange( nTab, tools::Rectangle( VCLPoint(rUnoPoint), VCLPoint(rUnoPoint) ));
+ }
+
+ return aReturn;
+}
+
+static awt::Point lcl_GetRelativePos( const uno::Reference< drawing::XShape >& xShape, const ScDocument* pDoc, SCTAB nTab, ScRange& rRange,
+ awt::Size& rUnoSize, awt::Point& rCaptionPoint)
+{
+ awt::Point aUnoPoint;
+ rRange = lcl_GetAnchorCell(xShape, pDoc, nTab, aUnoPoint, rUnoSize, rCaptionPoint);
+ tools::Rectangle aRect(pDoc->GetMMRect( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab() ));
+ Point aPoint = pDoc->IsNegativePage(nTab) ? aRect.TopRight() : aRect.TopLeft();
+ aUnoPoint.X -= aPoint.X();
+ aUnoPoint.Y -= aPoint.Y();
+ return aUnoPoint;
+}
+
+void SAL_CALL ScShapeObj::setPropertyValue(const OUString& aPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+
+ if ( aPropertyName == SC_UNONAME_ANCHOR )
+ {
+ uno::Reference<sheet::XCellRangeAddressable> xRangeAdd(aValue, uno::UNO_QUERY);
+ if (!xRangeAdd.is())
+ throw lang::IllegalArgumentException("only XCell or XSpreadsheet objects allowed", getXWeak(), 0);
+
+ SdrObject *pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+
+ if ( pPage )
+ {
+ ScDocument* pDoc(rModel.GetDocument());
+
+ if ( pDoc )
+ {
+ if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ table::CellRangeAddress aAddress = xRangeAdd->getRangeAddress();
+ if (nTab == aAddress.Sheet)
+ {
+ tools::Rectangle aRect(pDoc->GetMMRect( static_cast<SCCOL>(aAddress.StartColumn), static_cast<SCROW>(aAddress.StartRow),
+ static_cast<SCCOL>(aAddress.EndColumn), static_cast<SCROW>(aAddress.EndRow), aAddress.Sheet ));
+ awt::Point aRelPoint;
+ uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY );
+ if (xShape.is())
+ {
+ Point aPoint;
+ Point aEndPoint;
+ if (pDoc->IsNegativePage(nTab))
+ {
+ aPoint = aRect.TopRight();
+ aEndPoint = aRect.BottomLeft();
+ }
+ else
+ {
+ aPoint = aRect.TopLeft();
+ aEndPoint = aRect.BottomRight();
+ }
+ awt::Size aUnoSize;
+ awt::Point aCaptionPoint;
+ ScRange aRange;
+ aRelPoint = lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint );
+ awt::Point aUnoPoint(aRelPoint);
+
+ aUnoPoint.X += aPoint.X();
+ aUnoPoint.Y += aPoint.Y();
+
+ if ( aUnoPoint.Y > aEndPoint.Y() )
+ aUnoPoint.Y = aEndPoint.Y() - 2;
+ if (pDoc->IsNegativePage(nTab))
+ {
+ if ( aUnoPoint.X < aEndPoint.X() )
+ aUnoPoint.X = aEndPoint.X() + 2;
+ aUnoPoint.X -= aUnoSize.Width;
+ // remove difference to caption point
+ if (aCaptionPoint.X > 0 && aCaptionPoint.X > aUnoSize.Width)
+ aUnoPoint.X -= aCaptionPoint.X - aUnoSize.Width;
+ }
+ else
+ {
+ if ( aUnoPoint.X > aEndPoint.X() )
+ aUnoPoint.X = aEndPoint.X() - 2;
+ if (aCaptionPoint.X < 0)
+ aUnoPoint.X -= aCaptionPoint.X;
+ }
+ if (aCaptionPoint.Y < 0)
+ aUnoPoint.Y -= aCaptionPoint.Y;
+
+ xShape->setPosition(aUnoPoint);
+ pDocSh->SetModified();
+ }
+
+ if (aAddress.StartRow != aAddress.EndRow) //should be a Spreadsheet
+ {
+ OSL_ENSURE(aAddress.StartRow == 0 && aAddress.EndRow == pDoc->MaxRow() &&
+ aAddress.StartColumn == 0 && aAddress.EndColumn == pDoc->MaxCol(), "here should be a XSpreadsheet");
+ ScDrawLayer::SetPageAnchored(*pObj);
+ }
+ else
+ {
+ OSL_ENSURE(aAddress.StartRow == aAddress.EndRow &&
+ aAddress.StartColumn == aAddress.EndColumn, "here should be a XCell");
+ ScDrawObjData aAnchor;
+ aAnchor.maStart = ScAddress(aAddress.StartColumn, aAddress.StartRow, aAddress.Sheet);
+ aAnchor.maStartOffset = Point(aRelPoint.X, aRelPoint.Y);
+ ScDrawObjData* pDrawObjData = ScDrawLayer::GetObjData(pObj);
+ if (pDrawObjData)
+ aAnchor.mbResizeWithCell = pDrawObjData->mbResizeWithCell;
+ //Uno sets the Anchor in terms of the unrotated shape, not much we can do
+ //about that since uno also displays the shape geometry in terms of the unrotated
+ //shape. #TODO think about changing the anchoring behaviour here too
+ //Currently we've only got a start anchor, not an end-anchor, so generate that now
+ ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, aAnchor, *pDoc, aAddress.Sheet);
+ ScDrawLayer::SetCellAnchored(*pObj, aAnchor);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else if ( aPropertyName == SC_UNONAME_RESIZE_WITH_CELL )
+ {
+ SdrObject* pObj = GetSdrObject();
+ if (!pObj)
+ return;
+ ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj);
+
+ // Nothing to do if anchored to page
+ if (aAnchorType == SCA_PAGE)
+ return;
+
+ ScDrawObjData* pDrawObjData = ScDrawLayer::GetObjData(pObj);
+ if (!pDrawObjData)
+ return;
+
+ aValue >>= pDrawObjData->mbResizeWithCell;
+ ScDrawLayer::SetCellAnchored(*pObj, *pDrawObjData);
+ }
+ else if ( aPropertyName == SC_UNONAME_IMAGEMAP )
+ {
+ SdrObject* pObj = GetSdrObject();
+ if ( pObj )
+ {
+ ImageMap aImageMap;
+ uno::Reference< uno::XInterface > xImageMapInt(aValue, uno::UNO_QUERY);
+
+ if( !xImageMapInt.is() || !SvUnoImageMap_fillImageMap( xImageMapInt, aImageMap ) )
+ throw lang::IllegalArgumentException();
+
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObj);
+ if( pIMapInfo )
+ {
+ // replace existing image map
+ pIMapInfo->SetImageMap( aImageMap );
+ }
+ else
+ {
+ // insert new user data with image map
+ pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(aImageMap) ));
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_HORIPOS )
+ {
+ sal_Int32 nPos = 0;
+ if (aValue >>= nPos)
+ {
+ SdrObject *pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+
+ if ( pPage )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ ScDocument* pDoc = rModel.GetDocument();
+ if ( pDoc )
+ {
+ if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() )
+ {
+ uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY );
+ if (xShape.is())
+ {
+ if (ScDrawLayer::GetAnchorType(*pObj) == SCA_PAGE)
+ {
+ awt::Point aPoint(xShape->getPosition());
+ awt::Size aSize(xShape->getSize());
+ awt::Point aCaptionPoint;
+ if (pDoc->IsNegativePage(nTab))
+ {
+ nPos *= -1;
+ nPos -= aSize.Width;
+ }
+ if (lcl_GetCaptionPoint(xShape, aCaptionPoint))
+ {
+ if (pDoc->IsNegativePage(nTab))
+ {
+ if (aCaptionPoint.X > 0 && aCaptionPoint.X > aSize.Width)
+ nPos -= aCaptionPoint.X - aSize.Width;
+ }
+ else
+ {
+ if (aCaptionPoint.X < 0)
+ nPos -= aCaptionPoint.X;
+ }
+ }
+ aPoint.X = nPos;
+ xShape->setPosition(aPoint);
+ pDocSh->SetModified();
+ }
+ else if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL
+ || ScDrawLayer::GetAnchorType(*pObj)
+ == SCA_CELL_RESIZE)
+ {
+ awt::Size aUnoSize;
+ awt::Point aCaptionPoint;
+ ScRange aRange;
+ awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint ));
+ tools::Rectangle aRect(pDoc->GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() ));
+ if (pDoc->IsNegativePage(nTab))
+ {
+ aUnoPoint.X = -nPos;
+ Point aPoint(aRect.TopRight());
+ Point aEndPoint(aRect.BottomLeft());
+ aUnoPoint.X += aPoint.X();
+ if (aUnoPoint.X < aEndPoint.X())
+ aUnoPoint.X = aEndPoint.X() + 2;
+ aUnoPoint.X -= aUnoSize.Width;
+ if (aCaptionPoint.X > 0 && aCaptionPoint.X > aUnoSize.Width)
+ aUnoPoint.X -= aCaptionPoint.X - aUnoSize.Width;
+ }
+ else
+ {
+ aUnoPoint.X = nPos;
+ Point aPoint(aRect.TopLeft());
+ Point aEndPoint(aRect.BottomRight());
+ aUnoPoint.X += aPoint.X();
+ if (aUnoPoint.X > aEndPoint.X())
+ aUnoPoint.X = aEndPoint.X() - 2;
+ if (aCaptionPoint.X < 0)
+ aUnoPoint.X -= aCaptionPoint.X;
+ }
+ aUnoPoint.Y = xShape->getPosition().Y;
+ xShape->setPosition(aUnoPoint);
+ pDocSh->SetModified();
+ }
+ else
+ {
+ OSL_FAIL("unknown anchor type");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_VERTPOS )
+ {
+ sal_Int32 nPos = 0;
+ if (aValue >>= nPos)
+ {
+ SdrObject *pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+
+ if ( pPage )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ ScDocument* pDoc = rModel.GetDocument();
+ if ( pDoc )
+ {
+ if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() )
+ {
+ uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY );
+ if (xShape.is())
+ {
+ if (ScDrawLayer::GetAnchorType(*pObj) == SCA_PAGE)
+ {
+ awt::Point aPoint = xShape->getPosition();
+ awt::Point aCaptionPoint;
+ if (lcl_GetCaptionPoint(xShape, aCaptionPoint))
+ {
+ if (aCaptionPoint.Y < 0)
+ nPos -= aCaptionPoint.Y;
+ }
+ aPoint.Y = nPos;
+ xShape->setPosition(aPoint);
+ pDocSh->SetModified();
+ }
+ else if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL
+ || ScDrawLayer::GetAnchorType(*pObj)
+ == SCA_CELL_RESIZE)
+ {
+ awt::Size aUnoSize;
+ awt::Point aCaptionPoint;
+ ScRange aRange;
+ awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint ));
+ tools::Rectangle aRect(pDoc->GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aStart.Tab() ));
+ Point aPoint(aRect.TopRight());
+ Point aEndPoint(aRect.BottomLeft());
+ aUnoPoint.Y = nPos;
+ aUnoPoint.Y += aPoint.Y();
+ if (aUnoPoint.Y > aEndPoint.Y())
+ aUnoPoint.Y = aEndPoint.Y() - 2;
+ if (aCaptionPoint.Y < 0)
+ aUnoPoint.Y -= aCaptionPoint.Y;
+ aUnoPoint.X = xShape->getPosition().X;
+ xShape->setPosition(aUnoPoint);
+ pDocSh->SetModified();
+ }
+ else
+ {
+ OSL_FAIL("unknown anchor type");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_HYPERLINK ||
+ aPropertyName == SC_UNONAME_URL )
+ {
+ OUString sHyperlink;
+ SdrObject* pObj = GetSdrObject();
+ if (pObj && (aValue >>= sHyperlink))
+ pObj->setHyperlink(sHyperlink);
+ }
+ else if ( aPropertyName == SC_UNONAME_MOVEPROTECT )
+ {
+ if( SdrObject* pObj = GetSdrObject() )
+ {
+ bool aProt = false;
+ if( aValue >>= aProt )
+ pObj->SetMoveProtect( aProt );
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_STYLE )
+ {
+ if (SdrObject* pObj = GetSdrObject())
+ {
+ uno::Reference<style::XStyle> xStyle(aValue, uno::UNO_QUERY);
+ auto pStyleSheetObj = dynamic_cast<ScStyleObj*>(xStyle.get());
+ if (!pStyleSheetObj)
+ throw lang::IllegalArgumentException();
+
+ auto pStyleSheet = pStyleSheetObj->GetStyle_Impl();
+ auto pOldStyleSheet = pObj->GetStyleSheet();
+
+ if (pStyleSheet != pOldStyleSheet)
+ pObj->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), false);
+ }
+ }
+ else
+ {
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ pShapePropertySet->setPropertyValue( aPropertyName, aValue );
+ }
+}
+
+uno::Any SAL_CALL ScShapeObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aAny;
+ if ( aPropertyName == SC_UNONAME_ANCHOR )
+ {
+ SdrObject *pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+
+ if ( pPage )
+ {
+ ScDocument* pDoc = rModel.GetDocument();
+ if ( pDoc )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() )
+ {
+ uno::Reference< uno::XInterface > xAnchor;
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjDataTab(pObj, nTab))
+ xAnchor.set(cppu::getXWeak(new ScCellObj( pDocSh, pAnchor->maStart)));
+ else
+ xAnchor.set(cppu::getXWeak(new ScTableSheetObj( pDocSh, nTab )));
+ aAny <<= xAnchor;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (aPropertyName == SC_UNONAME_RESIZE_WITH_CELL)
+ {
+ bool bIsResizeWithCell = false;
+ SdrObject* pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScAnchorType anchorType = ScDrawLayer::GetAnchorType(*pObj);
+ bIsResizeWithCell = (anchorType == SCA_CELL_RESIZE);
+ }
+ aAny <<= bIsResizeWithCell;
+ }
+ else if ( aPropertyName == SC_UNONAME_IMAGEMAP )
+ {
+ uno::Reference< uno::XInterface > xImageMap;
+ SdrObject* pObj = GetSdrObject();
+ if ( pObj )
+ {
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(GetSdrObject());
+ if( pIMapInfo )
+ {
+ const ImageMap& rIMap = pIMapInfo->GetImageMap();
+ xImageMap.set(SvUnoImageMap_createInstance( rIMap, GetSupportedMacroItems() ));
+ }
+ else
+ xImageMap = SvUnoImageMap_createInstance();
+ }
+ aAny <<= uno::Reference< container::XIndexContainer >::query( xImageMap );
+ }
+ else if ( aPropertyName == SC_UNONAME_HORIPOS )
+ {
+ SdrObject *pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+
+ if ( pPage )
+ {
+ ScDocument* pDoc = rModel.GetDocument();
+ if ( pDoc )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY );
+ if (xShape.is())
+ {
+ if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL
+ || ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL_RESIZE)
+ {
+ awt::Size aUnoSize;
+ awt::Point aCaptionPoint;
+ ScRange aRange;
+ awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint ));
+ if (pDoc->IsNegativePage(nTab))
+ aUnoPoint.X *= -1;
+ aAny <<= aUnoPoint.X;
+ }
+ else
+ {
+ awt::Point aCaptionPoint;
+ awt::Point aUnoPoint(xShape->getPosition());
+ awt::Size aUnoSize(xShape->getSize());
+ if (pDoc->IsNegativePage(nTab))
+ {
+ aUnoPoint.X *= -1;
+ aUnoPoint.X -= aUnoSize.Width;
+ }
+ if (lcl_GetCaptionPoint(xShape, aCaptionPoint))
+ {
+ if (pDoc->IsNegativePage(nTab))
+ {
+ if (aCaptionPoint.X > 0 && aCaptionPoint.X > aUnoSize.Width)
+ aUnoPoint.X -= aCaptionPoint.X - aUnoSize.Width;
+ }
+ else
+ {
+ if (aCaptionPoint.X < 0)
+ aUnoPoint.X += aCaptionPoint.X;
+ }
+ }
+ aAny <<= aUnoPoint.X;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_VERTPOS )
+ {
+ SdrObject *pObj = GetSdrObject();
+ if (pObj)
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+
+ if ( pPage )
+ {
+ ScDocument* pDoc = rModel.GetDocument();
+ if ( pDoc )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ uno::Reference<drawing::XShape> xShape( mxShapeAgg, uno::UNO_QUERY );
+ if (xShape.is())
+ {
+ if (ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL
+ || ScDrawLayer::GetAnchorType(*pObj) == SCA_CELL_RESIZE)
+ {
+ awt::Size aUnoSize;
+ awt::Point aCaptionPoint;
+ ScRange aRange;
+ awt::Point aUnoPoint(lcl_GetRelativePos( xShape, pDoc, nTab, aRange, aUnoSize, aCaptionPoint ));
+
+ aAny <<= aUnoPoint.Y;
+ }
+ else
+ {
+ awt::Point aUnoPoint(xShape->getPosition());
+ awt::Point aCaptionPoint;
+ if (lcl_GetCaptionPoint(xShape, aCaptionPoint))
+ {
+ if (aCaptionPoint.Y < 0)
+ aUnoPoint.Y += aCaptionPoint.Y;
+ }
+ aAny <<= aUnoPoint.Y;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( aPropertyName == SC_UNONAME_HYPERLINK ||
+ aPropertyName == SC_UNONAME_URL )
+ {
+ OUString sHlink;
+ if (SdrObject* pObj = GetSdrObject())
+ sHlink = pObj->getHyperlink();
+ aAny <<= sHlink;
+ }
+ else if ( aPropertyName == SC_UNONAME_MOVEPROTECT )
+ {
+ bool aProt = false;
+ if ( SdrObject* pObj = GetSdrObject() )
+ aProt = pObj->IsMoveProtect();
+ aAny <<= aProt;
+ }
+ else if ( aPropertyName == SC_UNONAME_STYLE )
+ {
+ if (SdrObject* pObj = GetSdrObject())
+ {
+ if (auto pStyleSheet = pObj->GetStyleSheet())
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ ScDocument* pDoc = rModel.GetDocument();
+ aAny <<= uno::Reference<style::XStyle>(new ScStyleObj(
+ pDoc ? pDoc->GetDocumentShell() : nullptr,
+ SfxStyleFamily::Frame, pStyleSheet->GetName()));
+ }
+ }
+ }
+ else
+ {
+ if(!pShapePropertySet) //performance consideration
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ aAny = pShapePropertySet->getPropertyValue( aPropertyName );
+ }
+
+ return aAny;
+}
+
+void SAL_CALL ScShapeObj::addPropertyChangeListener( const OUString& aPropertyName,
+ const uno::Reference<beans::XPropertyChangeListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ pShapePropertySet->addPropertyChangeListener( aPropertyName, aListener );
+}
+
+void SAL_CALL ScShapeObj::removePropertyChangeListener( const OUString& aPropertyName,
+ const uno::Reference<beans::XPropertyChangeListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ pShapePropertySet->removePropertyChangeListener( aPropertyName, aListener );
+}
+
+void SAL_CALL ScShapeObj::addVetoableChangeListener( const OUString& aPropertyName,
+ const uno::Reference<beans::XVetoableChangeListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ pShapePropertySet->addVetoableChangeListener( aPropertyName, aListener );
+}
+
+void SAL_CALL ScShapeObj::removeVetoableChangeListener( const OUString& aPropertyName,
+ const uno::Reference<beans::XVetoableChangeListener>& aListener)
+{
+ SolarMutexGuard aGuard;
+
+ GetShapePropertySet();
+ if (pShapePropertySet)
+ pShapePropertySet->removeVetoableChangeListener( aPropertyName, aListener );
+}
+
+// XPropertyState
+
+beans::PropertyState SAL_CALL ScShapeObj::getPropertyState( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ beans::PropertyState eRet = beans::PropertyState_DIRECT_VALUE;
+ if ( aPropertyName == SC_UNONAME_IMAGEMAP )
+ {
+ // ImageMap is always "direct"
+ }
+ else if ( aPropertyName == SC_UNONAME_ANCHOR )
+ {
+ // Anchor is always "direct"
+ }
+ else if ( aPropertyName == SC_UNONAME_HORIPOS )
+ {
+ // HoriPos is always "direct"
+ }
+ else if ( aPropertyName == SC_UNONAME_VERTPOS )
+ {
+ // VertPos is always "direct"
+ }
+ else
+ {
+ GetShapePropertyState();
+ if (pShapePropertyState)
+ eRet = pShapePropertyState->getPropertyState( aPropertyName );
+ }
+
+ return eRet;
+}
+
+uno::Sequence<beans::PropertyState> SAL_CALL ScShapeObj::getPropertyStates(
+ const uno::Sequence<OUString>& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ // simple loop to get own and aggregated states
+
+ uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength());
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(),
+ [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); });
+ return aRet;
+}
+
+void SAL_CALL ScShapeObj::setPropertyToDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ if ( aPropertyName == SC_UNONAME_IMAGEMAP )
+ {
+ SdrObject* pObj = GetSdrObject();
+ if ( pObj )
+ {
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObj);
+ if( pIMapInfo )
+ {
+ ImageMap aEmpty;
+ pIMapInfo->SetImageMap( aEmpty ); // replace with empty image map
+ }
+ else
+ {
+ // nothing to do (no need to insert user data for an empty map)
+ }
+ }
+ }
+ else
+ {
+ GetShapePropertyState();
+ if (pShapePropertyState)
+ pShapePropertyState->setPropertyToDefault( aPropertyName );
+ }
+}
+
+uno::Any SAL_CALL ScShapeObj::getPropertyDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aAny;
+ if ( aPropertyName == SC_UNONAME_IMAGEMAP )
+ {
+ // default: empty ImageMap
+ uno::Reference< uno::XInterface > xImageMap(SvUnoImageMap_createInstance());
+ aAny <<= uno::Reference< container::XIndexContainer >::query( xImageMap );
+ }
+ else
+ {
+ GetShapePropertyState();
+ if (pShapePropertyState)
+ aAny = pShapePropertyState->getPropertyDefault( aPropertyName );
+ }
+
+ return aAny;
+}
+
+// XTextContent
+
+void SAL_CALL ScShapeObj::attach( const uno::Reference<text::XTextRange>& /* xTextRange */ )
+{
+ throw lang::IllegalArgumentException(); // anchor cannot be changed
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScShapeObj::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XTextRange> xRet;
+
+ SdrObject* pObj = GetSdrObject();
+ if( pObj )
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+ ScDocument* pDoc = rModel.GetDocument();
+
+ if ( pPage && pDoc )
+ {
+ if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ Point aPos(pObj->GetCurrentBoundRect().TopLeft());
+ ScRange aRange(pDoc->GetRange( nTab, tools::Rectangle( aPos, aPos ) ));
+
+ // anchor is always the cell
+
+ xRet.set(new ScCellObj( pDocSh, aRange.aStart ));
+ }
+ }
+ }
+ }
+
+ return xRet;
+}
+
+// XComponent
+
+void SAL_CALL ScShapeObj::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<lang::XComponent> xAggComp(lcl_GetComponent(mxShapeAgg));
+ if ( xAggComp.is() )
+ xAggComp->dispose();
+}
+
+void SAL_CALL ScShapeObj::addEventListener(
+ const uno::Reference<lang::XEventListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<lang::XComponent> xAggComp(lcl_GetComponent(mxShapeAgg));
+ if ( xAggComp.is() )
+ xAggComp->addEventListener(xListener);
+}
+
+void SAL_CALL ScShapeObj::removeEventListener(
+ const uno::Reference<lang::XEventListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<lang::XComponent> xAggComp(lcl_GetComponent(mxShapeAgg));
+ if ( xAggComp.is() )
+ xAggComp->removeEventListener(xListener);
+}
+
+// XText
+// (special handling for ScCellFieldObj)
+
+static void lcl_CopyOneProperty( beans::XPropertySet& rDest, beans::XPropertySet& rSource, const OUString& aNameStr )
+{
+ try
+ {
+ rDest.setPropertyValue( aNameStr, rSource.getPropertyValue( aNameStr ) );
+ }
+ catch (uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Exception in text field");
+ }
+}
+
+void SAL_CALL ScShapeObj::insertTextContent( const uno::Reference<text::XTextRange>& xRange,
+ const uno::Reference<text::XTextContent>& xContent,
+ sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XTextContent> xEffContent;
+
+ ScEditFieldObj* pCellField = dynamic_cast<ScEditFieldObj*>( xContent.get() );
+ if ( pCellField )
+ {
+ // createInstance("TextField.URL") from the document creates a ScCellFieldObj.
+ // To insert it into drawing text, a SvxUnoTextField is needed instead.
+ // The ScCellFieldObj object is left in non-inserted state.
+
+ rtl::Reference<SvxUnoTextField> pDrawField = new SvxUnoTextField( text::textfield::Type::URL );
+ xEffContent.set(pDrawField);
+ lcl_CopyOneProperty( *pDrawField, *pCellField, SC_UNONAME_URL );
+ lcl_CopyOneProperty( *pDrawField, *pCellField, SC_UNONAME_REPR );
+ lcl_CopyOneProperty( *pDrawField, *pCellField, SC_UNONAME_TARGET );
+ }
+ else
+ xEffContent.set(xContent);
+
+ uno::Reference<text::XText> xAggText(lcl_GetText(mxShapeAgg));
+ if ( xAggText.is() )
+ xAggText->insertTextContent( xRange, xEffContent, bAbsorb );
+}
+
+void SAL_CALL ScShapeObj::removeTextContent( const uno::Reference<text::XTextContent>& xContent )
+{
+ SolarMutexGuard aGuard;
+
+ // ScCellFieldObj can't be used here.
+
+ uno::Reference<text::XText> xAggText(lcl_GetText(mxShapeAgg));
+ if ( xAggText.is() )
+ xAggText->removeTextContent( xContent );
+}
+
+// XSimpleText (parent of XText)
+// Use own SvxUnoTextCursor subclass - everything is just passed to aggregated object
+
+uno::Reference<text::XTextCursor> SAL_CALL ScShapeObj::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+
+ if ( mxShapeAgg.is() )
+ {
+ // ScDrawTextCursor must be used to ensure the ScShapeObj is returned by getText
+
+ SvxUnoTextBase* pText = comphelper::getFromUnoTunnel<SvxUnoTextBase>( mxShapeAgg );
+ if (pText)
+ return new ScDrawTextCursor( this, *pText );
+ }
+
+ return uno::Reference<text::XTextCursor>();
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL ScShapeObj::createTextCursorByRange(
+ const uno::Reference<text::XTextRange>& aTextPosition )
+{
+ SolarMutexGuard aGuard;
+
+ if ( mxShapeAgg.is() && aTextPosition.is() )
+ {
+ // ScDrawTextCursor must be used to ensure the ScShapeObj is returned by getText
+
+ SvxUnoTextBase* pText = comphelper::getFromUnoTunnel<SvxUnoTextBase>( mxShapeAgg );
+ SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextRangeBase>( aTextPosition );
+ if ( pText && pRange )
+ {
+ rtl::Reference<SvxUnoTextCursor> pCursor = new ScDrawTextCursor( this, *pText );
+ pCursor->SetSelection( pRange->GetSelection() );
+ return pCursor;
+ }
+ }
+
+ return uno::Reference<text::XTextCursor>();
+}
+
+void SAL_CALL ScShapeObj::insertString( const uno::Reference<text::XTextRange>& xRange,
+ const OUString& aString, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XSimpleText> xAggSimpleText(lcl_GetSimpleText(mxShapeAgg));
+ if ( !xAggSimpleText.is() )
+ throw uno::RuntimeException();
+
+ xAggSimpleText->insertString( xRange, aString, bAbsorb );
+}
+
+void SAL_CALL ScShapeObj::insertControlCharacter( const uno::Reference<text::XTextRange>& xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XSimpleText> xAggSimpleText(lcl_GetSimpleText(mxShapeAgg));
+ if ( !xAggSimpleText.is() )
+ throw uno::RuntimeException();
+
+ xAggSimpleText->insertControlCharacter( xRange, nControlCharacter, bAbsorb );
+}
+
+// XTextRange
+// (parent of XSimpleText)
+
+uno::Reference<text::XText> SAL_CALL ScShapeObj::getText()
+{
+ return this;
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScShapeObj::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg));
+ if ( !xAggTextRange.is() )
+ throw uno::RuntimeException();
+
+ return xAggTextRange->getStart();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScShapeObj::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg));
+ if ( !xAggTextRange.is() )
+ throw uno::RuntimeException();
+
+ return xAggTextRange->getEnd();
+}
+
+OUString SAL_CALL ScShapeObj::getString()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg));
+ if ( !xAggTextRange.is() )
+ throw uno::RuntimeException();
+
+ return xAggTextRange->getString();
+}
+
+void SAL_CALL ScShapeObj::setString( const OUString& aText )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<text::XTextRange> xAggTextRange(lcl_GetTextRange(mxShapeAgg));
+ if ( !xAggTextRange.is() )
+ throw uno::RuntimeException();
+
+ xAggTextRange->setString( aText );
+}
+
+// XChild
+
+uno::Reference< uno::XInterface > SAL_CALL ScShapeObj::getParent()
+{
+ SolarMutexGuard aGuard;
+
+ // receive cell position from caption object (parent of a note caption is the note cell)
+ SdrObject* pObj = GetSdrObject();
+ if( pObj )
+ {
+ ScDrawLayer& rModel(static_cast< ScDrawLayer& >(pObj->getSdrModelFromSdrObject()));
+ SdrPage* pPage(pObj->getSdrPageFromSdrObject());
+ ScDocument* pDoc = rModel.GetDocument();
+
+ if ( pPage && pDoc )
+ {
+ if ( ScDocShell* pDocSh = pDoc->GetDocumentShell() )
+ {
+ SCTAB nTab = 0;
+ if ( lcl_GetPageNum( pPage, rModel, nTab ) )
+ {
+ const ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObj, nTab );
+ if( pCaptData )
+ return cppu::getXWeak( new ScCellObj( pDocSh, pCaptData->maStart ) );
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+void SAL_CALL ScShapeObj::setParent( const uno::Reference< uno::XInterface >& )
+{
+ throw lang::NoSupportException();
+}
+
+// XTypeProvider
+
+uno::Sequence<uno::Type> SAL_CALL ScShapeObj::getTypes()
+{
+ uno::Sequence< uno::Type > aBaseTypes( ScShapeObj_Base::getTypes() );
+
+ uno::Sequence< uno::Type > aTextTypes;
+ if ( bIsTextShape )
+ aTextTypes = ScShapeObj_TextBase::getTypes();
+
+ uno::Reference<lang::XTypeProvider> xBaseProvider;
+ if ( mxShapeAgg.is() )
+ mxShapeAgg->queryAggregation( cppu::UnoType<lang::XTypeProvider>::get()) >>= xBaseProvider;
+ OSL_ENSURE( xBaseProvider.is(), "ScShapeObj: No XTypeProvider from aggregated shape!" );
+
+ uno::Sequence< uno::Type > aAggTypes;
+ if( xBaseProvider.is() )
+ aAggTypes = xBaseProvider->getTypes();
+
+ return ::comphelper::concatSequences( aBaseTypes, aTextTypes, aAggTypes );
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScShapeObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+SdrObject* ScShapeObj::GetSdrObject() const noexcept
+{
+ if(mxShapeAgg.is())
+ return SdrObject::getSdrObjectFromXShape( mxShapeAgg );
+ return nullptr;
+}
+
+constexpr OUString SC_EVENTACC_ONCLICK = u"OnClick"_ustr;
+constexpr OUString SC_EVENTACC_SCRIPT = u"Script"_ustr;
+constexpr OUString SC_EVENTACC_EVENTTYPE = u"EventType"_ustr;
+
+class ShapeUnoEventAccessImpl : public ::cppu::WeakImplHelper< container::XNameReplace >
+{
+private:
+ ScShapeObj* mpShape;
+
+ ScMacroInfo* getInfo( bool bCreate )
+ {
+ return ScShapeObj_getShapeHyperMacroInfo( mpShape, bCreate );
+ }
+
+public:
+ explicit ShapeUnoEventAccessImpl( ScShapeObj* pShape ): mpShape( pShape )
+ {
+ }
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ if ( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+ uno::Sequence< beans::PropertyValue > aProperties;
+ aElement >>= aProperties;
+ bool isEventType = false;
+ for( const beans::PropertyValue& rProperty : std::as_const(aProperties) )
+ {
+ if ( rProperty.Name == SC_EVENTACC_EVENTTYPE )
+ {
+ isEventType = true;
+ continue;
+ }
+ if ( isEventType && (rProperty.Name == SC_EVENTACC_SCRIPT) )
+ {
+ OUString sValue;
+ if ( rProperty.Value >>= sValue )
+ {
+ ScMacroInfo* pInfo = getInfo( true );
+ OSL_ENSURE( pInfo, "shape macro info could not be created!" );
+ if ( !pInfo )
+ break;
+ pInfo->SetMacro( sValue );
+ }
+ }
+ }
+ }
+
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ uno::Sequence< beans::PropertyValue > aProperties;
+ ScMacroInfo* pInfo = getInfo(false);
+
+ if ( aName != SC_EVENTACC_ONCLICK )
+ {
+ throw container::NoSuchElementException();
+ }
+
+ if ( pInfo && !pInfo->GetMacro().isEmpty() )
+ {
+ aProperties = { comphelper::makePropertyValue(SC_EVENTACC_EVENTTYPE,
+ SC_EVENTACC_SCRIPT),
+ comphelper::makePropertyValue(SC_EVENTACC_SCRIPT, pInfo->GetMacro()) };
+ }
+
+ return uno::Any( aProperties );
+ }
+
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames() override
+ {
+ uno::Sequence<OUString> aSeq { SC_EVENTACC_ONCLICK };
+ return aSeq;
+ }
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ return aName == SC_EVENTACC_ONCLICK;
+ }
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ // elements are always present (but contained property sequences may be empty)
+ return true;
+ }
+};
+
+::uno::Reference< container::XNameReplace > SAL_CALL
+ScShapeObj::getEvents( )
+{
+ return new ShapeUnoEventAccessImpl( this );
+}
+
+OUString SAL_CALL ScShapeObj::getImplementationName( )
+{
+ return "com.sun.star.comp.sc.ScShapeObj";
+}
+
+sal_Bool SAL_CALL ScShapeObj::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL ScShapeObj::getSupportedServiceNames( )
+{
+ uno::Reference<lang::XServiceInfo> xSI;
+ if ( mxShapeAgg.is() )
+ mxShapeAgg->queryAggregation( cppu::UnoType<lang::XServiceInfo>::get() ) >>= xSI;
+
+ uno::Sequence< OUString > aSupported;
+ if ( xSI.is() )
+ aSupported = xSI->getSupportedServiceNames();
+
+ aSupported.realloc( aSupported.getLength() + 1 );
+ aSupported.getArray()[ aSupported.getLength() - 1 ] = "com.sun.star.sheet.Shape";
+
+ if( bIsNoteCaption )
+ {
+ aSupported.realloc( aSupported.getLength() + 1 );
+ aSupported.getArray()[ aSupported.getLength() - 1 ] = "com.sun.star.sheet.CellAnnotationShape";
+ }
+
+ return aSupported;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/srchuno.cxx b/sc/source/ui/unoobj/srchuno.cxx
new file mode 100644
index 0000000000..a5b59a330c
--- /dev/null
+++ b/sc/source/ui/unoobj/srchuno.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 <scitems.hxx>
+#include <svl/srchitem.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <srchuno.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+
+using namespace com::sun::star;
+
+//! SearchWords searches in whole cells - rename it ???
+
+// SfxItemPropertyMapEntry only for GetPropertySetInfo
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetSearchPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aSearchPropertyMap_Impl[] =
+ {
+ { SC_UNO_SRCHBACK, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHBYROW, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHCASE, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHREGEXP, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHWILDCARD, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHSIM, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHSIMADD, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_SRCHSIMEX, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_SRCHSIMREL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHSIMREM, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_SRCHSTYLES, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHTYPE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0}, // enum TableSearch is gone
+ { SC_UNO_SRCHWORDS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SRCHWCESCCHAR, 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ };
+ return aSearchPropertyMap_Impl;
+}
+
+constexpr OUString SCSEARCHDESCRIPTOR_SERVICE = u"com.sun.star.util.SearchDescriptor"_ustr;
+constexpr OUString SCREPLACEDESCRIPTOR_SERVICE = u"com.sun.star.util.ReplaceDescriptor"_ustr;
+
+ScCellSearchObj::ScCellSearchObj() :
+ aPropSet(lcl_GetSearchPropertyMap()),
+ pSearchItem( new SvxSearchItem( SCITEM_SEARCHDATA ) )
+{
+ // Defaults:
+ pSearchItem->SetWordOnly(false);
+ pSearchItem->SetExact(false);
+ pSearchItem->SetMatchFullHalfWidthForms(false);
+ pSearchItem->SetUseAsianOptions(false); // or all asian bits would have to be handled
+ pSearchItem->SetBackward(false);
+ pSearchItem->SetSelection(false);
+ pSearchItem->SetRegExp(false);
+ pSearchItem->SetWildcard(false);
+ pSearchItem->SetPattern(false);
+ pSearchItem->SetLevenshtein(false);
+ pSearchItem->SetLEVRelaxed(false);
+ pSearchItem->SetLEVOther(2);
+ pSearchItem->SetLEVShorter(2);
+ pSearchItem->SetLEVLonger(2);
+ // Calc-Flags
+ pSearchItem->SetRowDirection(false);
+ pSearchItem->SetCellType(SvxSearchCellType::FORMULA);
+
+ // Selection-Flag will be set when this is called
+}
+
+ScCellSearchObj::~ScCellSearchObj()
+{
+}
+
+// XSearchDescriptor
+
+OUString SAL_CALL ScCellSearchObj::getSearchString()
+{
+ SolarMutexGuard aGuard;
+ return pSearchItem->GetSearchString();
+}
+
+void SAL_CALL ScCellSearchObj::setSearchString( const OUString& aString )
+{
+ SolarMutexGuard aGuard;
+ pSearchItem->SetSearchString( aString );
+}
+
+// XReplaceDescriptor
+
+OUString SAL_CALL ScCellSearchObj::getReplaceString()
+{
+ SolarMutexGuard aGuard;
+ return pSearchItem->GetReplaceString();
+}
+
+void SAL_CALL ScCellSearchObj::setReplaceString( const OUString& aReplaceString )
+{
+ SolarMutexGuard aGuard;
+ pSearchItem->SetReplaceString( aReplaceString );
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScCellSearchObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScCellSearchObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if (aPropertyName == SC_UNO_SRCHBACK) pSearchItem->SetBackward( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHBYROW) pSearchItem->SetRowDirection( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHCASE) pSearchItem->SetExact( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHREGEXP) pSearchItem->SetRegExp( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHWILDCARD) pSearchItem->SetWildcard( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHSIM) pSearchItem->SetLevenshtein( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHSIMREL) pSearchItem->SetLEVRelaxed( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHSTYLES) pSearchItem->SetPattern( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHWORDS) pSearchItem->SetWordOnly( ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHSIMADD) pSearchItem->SetLEVLonger( ScUnoHelpFunctions::GetInt16FromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHSIMEX) pSearchItem->SetLEVOther( ScUnoHelpFunctions::GetInt16FromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHSIMREM) pSearchItem->SetLEVShorter( ScUnoHelpFunctions::GetInt16FromAny( aValue ) );
+ else if (aPropertyName == SC_UNO_SRCHTYPE) pSearchItem->SetCellType( static_cast<SvxSearchCellType>(ScUnoHelpFunctions::GetInt16FromAny( aValue )) );
+ else if (aPropertyName == SC_UNO_SRCHFILTERED) pSearchItem->SetSearchFiltered( ScUnoHelpFunctions::GetBoolFromAny(aValue) );
+ else if (aPropertyName == SC_UNO_SRCHFORMATTED) pSearchItem->SetSearchFormatted( ScUnoHelpFunctions::GetBoolFromAny(aValue) );
+ else if (aPropertyName == SC_UNO_SRCHWCESCCHAR) pSearchItem->SetWildcardEscapeCharacter( ScUnoHelpFunctions::GetInt32FromAny(aValue) );
+}
+
+uno::Any SAL_CALL ScCellSearchObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if (aPropertyName == SC_UNO_SRCHBACK) aRet <<= pSearchItem->GetBackward();
+ else if (aPropertyName == SC_UNO_SRCHBYROW) aRet <<= pSearchItem->GetRowDirection();
+ else if (aPropertyName == SC_UNO_SRCHCASE) aRet <<= pSearchItem->GetExact();
+ else if (aPropertyName == SC_UNO_SRCHREGEXP) aRet <<= pSearchItem->GetRegExp();
+ else if (aPropertyName == SC_UNO_SRCHWILDCARD) aRet <<= pSearchItem->GetWildcard();
+ else if (aPropertyName == SC_UNO_SRCHSIM) aRet <<= pSearchItem->IsLevenshtein();
+ else if (aPropertyName == SC_UNO_SRCHSIMREL) aRet <<= pSearchItem->IsLEVRelaxed();
+ else if (aPropertyName == SC_UNO_SRCHSTYLES) aRet <<= pSearchItem->GetPattern();
+ else if (aPropertyName == SC_UNO_SRCHWORDS) aRet <<= pSearchItem->GetWordOnly();
+ else if (aPropertyName == SC_UNO_SRCHSIMADD) aRet <<= static_cast<sal_Int16>(pSearchItem->GetLEVLonger());
+ else if (aPropertyName == SC_UNO_SRCHSIMEX) aRet <<= static_cast<sal_Int16>(pSearchItem->GetLEVOther());
+ else if (aPropertyName == SC_UNO_SRCHSIMREM) aRet <<= static_cast<sal_Int16>(pSearchItem->GetLEVShorter());
+ else if (aPropertyName == SC_UNO_SRCHTYPE) aRet <<= static_cast<sal_Int16>(pSearchItem->GetCellType());
+ else if (aPropertyName == SC_UNO_SRCHFILTERED) aRet <<= pSearchItem->IsSearchFiltered();
+ else if (aPropertyName == SC_UNO_SRCHFORMATTED) aRet <<= pSearchItem->IsSearchFormatted();
+ else if (aPropertyName == SC_UNO_SRCHWCESCCHAR) aRet <<= pSearchItem->GetWildcardEscapeCharacter();
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScCellSearchObj )
+
+// XServiceInfo
+
+OUString SAL_CALL ScCellSearchObj::getImplementationName()
+{
+ return "ScCellSearchObj";
+}
+
+sal_Bool SAL_CALL ScCellSearchObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScCellSearchObj::getSupportedServiceNames()
+{
+ return {SCSEARCHDESCRIPTOR_SERVICE, SCREPLACEDESCRIPTOR_SERVICE};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/styleuno.cxx b/sc/source/ui/unoobj/styleuno.cxx
new file mode 100644
index 0000000000..8dd6fc58f8
--- /dev/null
+++ b/sc/source/ui/unoobj/styleuno.cxx
@@ -0,0 +1,2069 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/memberids.h>
+#include <svx/algitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/numitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/pbinitem.hxx>
+#include <svx/unomid.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <editeng/unonrule.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <utility>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <svl/intitem.hxx>
+#include <svl/zformat.hxx>
+#include <tools/fract.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/table/BorderLine.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/TableBorder2.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/style/PageStyleLayout.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <CommonProperties.hxx>
+#include <styleuno.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <stlpool.hxx>
+#include <docpool.hxx>
+#include <miscuno.hxx>
+#include <tablink.hxx>
+#include <unonames.hxx>
+#include <unowids.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <cellsuno.hxx>
+#include <stylehelper.hxx>
+
+using namespace ::com::sun::star;
+
+static const SfxItemPropertySet* lcl_GetGraphicStyleSet()
+{
+ static const SfxItemPropertyMapEntry aGraphicStyleMap_Impl[] =
+ {
+ SVX_UNOEDIT_NUMBERING_PROPERTY,
+ SHADOW_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ FILL_PROPERTIES
+ EDGERADIUS_PROPERTIES
+ TEXT_PROPERTIES_DEFAULTS
+ CONNECTOR_PROPERTIES
+ SPECIAL_DIMENSIONING_PROPERTIES_DEFAULTS
+ {SC_UNONAME_HIDDEN, ATTR_HIDDEN, cppu::UnoType<sal_Bool>::get(), 0, 0 },
+ {SC_UNONAME_DISPNAME, SC_WID_UNO_DISPNAME, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ };
+ static SfxItemPropertySet aGraphicStyleSet_Impl( aGraphicStyleMap_Impl );
+ return &aGraphicStyleSet_Impl;
+}
+
+static const SfxItemPropertySet* lcl_GetCellStyleSet()
+{
+ static const SfxItemPropertyMapEntry aCellStyleMap_Impl[] =
+ {
+ {SC_UNONAME_ASIANVERT,ATTR_VERTICAL_ASIAN,cppu::UnoType<bool>::get(), 0, 0 },
+ CELL_BORDER_PROPERTIES
+ CELL_BACKGROUND_COLOR_PROPERTIES
+ {SC_UNONAME_CELLPRO, ATTR_PROTECTION, ::cppu::UnoType<util::CellProtection>::get(), 0, 0 },
+ CHAR_COLOR_PROPERTIES
+ {SC_UNONAME_COUTL, ATTR_FONT_CONTOUR, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_CCROSS, ATTR_FONT_CROSSEDOUT,cppu::UnoType<bool>::get(), 0, MID_CROSSED_OUT },
+ {SC_UNONAME_CEMPHAS, ATTR_FONT_EMPHASISMARK,cppu::UnoType<sal_Int16>::get(), 0, MID_EMPHASIS },
+ {SC_UNONAME_CFONT, ATTR_FONT, ::cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ {SC_UNONAME_CFCHARS, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ {SC_UNO_CJK_CFCHARS, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ {SC_UNO_CTL_CFCHARS, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_CHAR_SET },
+ {SC_UNONAME_CFFAMIL, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ {SC_UNO_CJK_CFFAMIL, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ {SC_UNO_CTL_CFFAMIL, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_FAMILY },
+ {SC_UNONAME_CFNAME, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ {SC_UNO_CJK_CFNAME, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ {SC_UNO_CTL_CFNAME, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_FAMILY_NAME },
+ {SC_UNONAME_CFPITCH, ATTR_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ {SC_UNO_CJK_CFPITCH, ATTR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ {SC_UNO_CTL_CFPITCH, ATTR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), 0, MID_FONT_PITCH },
+ {SC_UNONAME_CFSTYLE, ATTR_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ {SC_UNO_CJK_CFSTYLE, ATTR_CJK_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ {SC_UNO_CTL_CFSTYLE, ATTR_CTL_FONT, cppu::UnoType<OUString>::get(), 0, MID_FONT_STYLE_NAME },
+ {SC_UNONAME_CHEIGHT, ATTR_FONT_HEIGHT, ::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ {SC_UNO_CJK_CHEIGHT, ATTR_CJK_FONT_HEIGHT,::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ {SC_UNO_CTL_CHEIGHT, ATTR_CTL_FONT_HEIGHT,::cppu::UnoType<float>::get(), 0, MID_FONTHEIGHT | CONVERT_TWIPS },
+ {SC_UNONAME_CLOCAL, ATTR_FONT_LANGUAGE, ::cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ {SC_UNO_CJK_CLOCAL, ATTR_CJK_FONT_LANGUAGE,::cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ {SC_UNO_CTL_CLOCAL, ATTR_CTL_FONT_LANGUAGE,::cppu::UnoType<lang::Locale>::get(), 0, MID_LANG_LOCALE },
+ {SC_UNONAME_COVER, ATTR_FONT_OVERLINE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ {SC_UNONAME_COVRLCOL, ATTR_FONT_OVERLINE, cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ {SC_UNONAME_COVRLHAS, ATTR_FONT_OVERLINE, cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ {SC_UNONAME_CPOST, ATTR_FONT_POSTURE, ::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ {SC_UNO_CJK_CPOST, ATTR_CJK_FONT_POSTURE,::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ {SC_UNO_CTL_CPOST, ATTR_CTL_FONT_POSTURE,::cppu::UnoType<awt::FontSlant>::get(), 0, MID_POSTURE },
+ {SC_UNONAME_CRELIEF, ATTR_FONT_RELIEF, cppu::UnoType<sal_Int16>::get(), 0, MID_RELIEF },
+ {SC_UNONAME_CSHADD, ATTR_FONT_SHADOWED, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_CSTRIKE, ATTR_FONT_CROSSEDOUT,cppu::UnoType<sal_Int16>::get(), 0, MID_CROSS_OUT },
+ {SC_UNONAME_CUNDER, ATTR_FONT_UNDERLINE,::cppu::UnoType<sal_Int16>::get(), 0, MID_TL_STYLE },
+ {SC_UNONAME_CUNDLCOL, ATTR_FONT_UNDERLINE,cppu::UnoType<sal_Int32>::get(), 0, MID_TL_COLOR },
+ {SC_UNONAME_CUNDLHAS, ATTR_FONT_UNDERLINE,cppu::UnoType<bool>::get(), 0, MID_TL_HASCOLOR },
+ {SC_UNONAME_CWEIGHT, ATTR_FONT_WEIGHT, ::cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ {SC_UNO_CJK_CWEIGHT, ATTR_CJK_FONT_WEIGHT,::cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ {SC_UNO_CTL_CWEIGHT, ATTR_CTL_FONT_WEIGHT,::cppu::UnoType<float>::get(), 0, MID_WEIGHT },
+ {SC_UNONAME_CWORDMOD, ATTR_FONT_WORDLINE, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_DIAGONAL_BLTR, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_DIAGONAL_BLTR2, ATTR_BORDER_BLTR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_DIAGONAL_TLBR, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_DIAGONAL_TLBR2, ATTR_BORDER_TLBR, ::cppu::UnoType<table::BorderLine2>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_DISPNAME, SC_WID_UNO_DISPNAME,::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ {SC_UNONAME_CELLHJUS, ATTR_HOR_JUSTIFY, ::cppu::UnoType<table::CellHoriJustify>::get(), 0, MID_HORJUST_HORJUST },
+ {SC_UNONAME_CELLHJUS_METHOD, ATTR_HOR_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNONAME_CELLTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ {SC_UNONAME_WRAP, ATTR_LINEBREAK, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_NUMFMT, ATTR_VALUE_FORMAT, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+// {SC_UNONAME_NUMRULES, SC_WID_UNO_NUMRULES,cppu::UnoType<container::XIndexReplace>::get(), 0, 0 },
+ {SC_UNONAME_CELLORI, ATTR_STACKED, ::cppu::UnoType<table::CellOrientation>::get(), 0, 0 },
+ {SC_UNONAME_PADJUST, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ {SC_UNONAME_PBMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_LO_MARGIN | CONVERT_TWIPS },
+ {SC_UNONAME_PINDENT, ATTR_INDENT, ::cppu::UnoType<sal_Int16>::get(), 0, 0 }, //! CONVERT_TWIPS
+ {SC_UNONAME_PISCHDIST,ATTR_SCRIPTSPACE, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_PISFORBID,ATTR_FORBIDDEN_RULES,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_PISHANG, ATTR_HANGPUNCTUATION,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_PISHYPHEN,ATTR_HYPHENATE, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_PLASTADJ, ATTR_HOR_JUSTIFY, ::cppu::UnoType<sal_Int16>::get(), 0, MID_HORJUST_ADJUST },
+ {SC_UNONAME_PLMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_L_MARGIN | CONVERT_TWIPS },
+ {SC_UNONAME_PRMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_R_MARGIN | CONVERT_TWIPS },
+ {SC_UNONAME_PTMARGIN, ATTR_MARGIN, ::cppu::UnoType<sal_Int32>::get(), 0, MID_MARGIN_UP_MARGIN | CONVERT_TWIPS },
+ {SC_UNONAME_ROTANG, ATTR_ROTATE_VALUE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNONAME_ROTREF, ATTR_ROTATE_MODE, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNONAME_SHADOW, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_SHRINK_TO_FIT, ATTR_SHRINKTOFIT, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD, ::cppu::UnoType<table::TableBorder>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_TBLBORD, SC_WID_UNO_TBLBORD2, ::cppu::UnoType<table::TableBorder2>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ {SC_UNONAME_CELLVJUS, ATTR_VER_JUSTIFY, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNONAME_CELLVJUS_METHOD, ATTR_VER_JUSTIFY_METHOD, ::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNONAME_HIDDEN, ATTR_HIDDEN, cppu::UnoType<sal_Bool>::get(), 0, 0 },
+ {SC_UNONAME_HYPERLINK, ATTR_HYPERLINK, cppu::UnoType<OUString>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aCellStyleSet_Impl( aCellStyleMap_Impl );
+ return &aCellStyleSet_Impl;
+}
+
+// map with all site attributes including header and footer attributes
+
+static const SfxItemPropertySet * lcl_GetPageStyleSet()
+{
+ static const SfxItemPropertyMapEntry aPageStyleMap_Impl[] =
+ {
+ {SC_UNO_PAGE_BACKCOLOR, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ {SC_UNO_PAGE_GRAPHICFILT, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_FILTER },
+ {SC_UNO_PAGE_GRAPHICLOC, ATTR_BACKGROUND, ::cppu::UnoType<style::GraphicLocation>::get(), 0, MID_GRAPHIC_POSITION },
+ {SC_UNO_PAGE_GRAPHICURL, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_URL },
+ {SC_UNO_PAGE_GRAPHIC, ATTR_BACKGROUND, ::cppu::UnoType<graphic::XGraphic>::get(), 0, MID_GRAPHIC },
+ {SC_UNO_PAGE_BACKTRANS, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ {OLD_UNO_PAGE_BACKCOLOR, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ {SC_UNO_PAGE_BORDERDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_BOTTBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, BOTTOM_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_BOTTBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_BOTTMARGIN, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_LO_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_CENTERHOR, ATTR_PAGE_HORCENTER,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_CENTERVER, ATTR_PAGE_VERCENTER,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_DISPNAME, SC_WID_UNO_DISPNAME,::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ {SC_UNO_PAGE_FIRSTPAGE, ATTR_PAGE_FIRSTPAGENO,::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNO_PAGE_FIRSTFTRSHARED, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FIRSTHDRSHARED, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FIRSTFTRCONT, ATTR_PAGE_FOOTERFIRST,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 },
+ {SC_UNO_PAGE_FIRSTHDRCONT, ATTR_PAGE_HEADERFIRST,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 },
+
+ {SC_UNO_PAGE_FTRBACKCOL, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRGRFFILT, SC_WID_UNO_FOOTERSET,::cppu::UnoType<OUString>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRGRFLOC, SC_WID_UNO_FOOTERSET,::cppu::UnoType<style::GraphicLocation>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRGRFURL, SC_WID_UNO_FOOTERSET,::cppu::UnoType<OUString>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRGRF, SC_WID_UNO_FOOTERSET,::cppu::UnoType<graphic::XGraphic>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRBACKTRAN, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {OLD_UNO_PAGE_FTRBACKCOL, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRBODYDIST, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRBRDDIST, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRBOTTBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRBOTTBDIS, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {OLD_UNO_PAGE_FTRDYNAMIC, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRHEIGHT, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRDYNAMIC, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRON, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRSHARED, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRLEFTBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRLEFTBDIS, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRLEFTMAR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {OLD_UNO_PAGE_FTRON, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRRIGHTBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRRIGHTBDIS,SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRRIGHTMAR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRSHADOW, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 },
+ {OLD_UNO_PAGE_FTRSHARED, SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRTOPBOR, SC_WID_UNO_FOOTERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRTOPBDIS, SC_WID_UNO_FOOTERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+
+ {SC_UNO_PAGE_HDRBACKCOL, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRGRFFILT, SC_WID_UNO_HEADERSET,::cppu::UnoType<OUString>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRGRFLOC, SC_WID_UNO_HEADERSET,::cppu::UnoType<style::GraphicLocation>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRGRFURL, SC_WID_UNO_HEADERSET,::cppu::UnoType<OUString>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRGRF, SC_WID_UNO_HEADERSET,::cppu::UnoType<graphic::XGraphic>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRBACKTRAN, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {OLD_UNO_PAGE_HDRBACKCOL, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRBODYDIST, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRBRDDIST, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRBOTTBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRBOTTBDIS, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {OLD_UNO_PAGE_HDRDYNAMIC, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRHEIGHT, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRDYNAMIC, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRON, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRSHARED, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRLEFTBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRLEFTBDIS, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRLEFTMAR, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {OLD_UNO_PAGE_HDRON, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRRIGHTBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRRIGHTBDIS,SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRRIGHTMAR, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRSHADOW, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 },
+ {OLD_UNO_PAGE_HDRSHARED, SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRTOPBOR, SC_WID_UNO_HEADERSET,::cppu::UnoType<table::BorderLine>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRTOPBDIS, SC_WID_UNO_HEADERSET,::cppu::UnoType<sal_Int32>::get(), 0, 0 },
+
+ {SC_UNO_PAGE_HEIGHT, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_HEIGHT | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_BACKTRANS, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ {SC_UNO_PAGE_LANDSCAPE, ATTR_PAGE, cppu::UnoType<bool>::get(), 0, MID_PAGE_ORIENTATION },
+ {SC_UNO_PAGE_LEFTBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, LEFT_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_LEFTBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_LEFTMARGIN, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_L_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_LEFTFTRCONT, ATTR_PAGE_FOOTERLEFT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 },
+ {SC_UNO_PAGE_LEFTHDRCONT, ATTR_PAGE_HEADERLEFT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 },
+ {SC_UNO_PAGE_NUMBERTYPE, ATTR_PAGE, ::cppu::UnoType<sal_Int16>::get(), 0, MID_PAGE_NUMTYPE },
+ {SC_UNO_PAGE_SCALEVAL, ATTR_PAGE_SCALE, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNO_PAGE_SYTLELAYOUT, ATTR_PAGE, ::cppu::UnoType<style::PageStyleLayout>::get(), 0, MID_PAGE_LAYOUT },
+ {SC_UNO_PAGE_PRINTANNOT, ATTR_PAGE_NOTES, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTCHARTS, ATTR_PAGE_CHARTS, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTDOWN, ATTR_PAGE_TOPDOWN, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTDRAW, ATTR_PAGE_DRAWINGS, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTFORMUL, ATTR_PAGE_FORMULAS, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTGRID, ATTR_PAGE_GRID, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTHEADER, ATTR_PAGE_HEADERS, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTOBJS, ATTR_PAGE_OBJECTS, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PRINTZERO, ATTR_PAGE_NULLVALS, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_PAPERTRAY, ATTR_PAGE_PAPERBIN, ::cppu::UnoType<OUString>::get(), 0, 0 },
+ {SC_UNO_PAGE_RIGHTBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, RIGHT_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_RIGHTBRDDIST,ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_RIGHTMARGIN, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_R_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_RIGHTFTRCON, ATTR_PAGE_FOOTERRIGHT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 },
+ {SC_UNO_PAGE_RIGHTHDRCON, ATTR_PAGE_HEADERRIGHT,cppu::UnoType<sheet::XHeaderFooterContent>::get(), 0, 0 },
+ {SC_UNO_PAGE_SCALETOPAG, ATTR_PAGE_SCALETOPAGES,::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNO_PAGE_SCALETOX, ATTR_PAGE_SCALETO, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNO_PAGE_SCALETOY, ATTR_PAGE_SCALETO, ::cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNO_PAGE_SHADOWFORM, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ {SC_UNO_PAGE_SIZE, ATTR_PAGE_SIZE, ::cppu::UnoType<awt::Size>::get(), 0, MID_SIZE_SIZE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_TOPBORDER, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, TOP_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_TOPBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_TOPMARGIN, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_UP_MARGIN | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_FTRBACKTRAN,SC_WID_UNO_FOOTERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {OLD_UNO_PAGE_HDRBACKTRAN,SC_WID_UNO_HEADERSET,cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNONAME_USERDEF, ATTR_USERDEF, cppu::UnoType<container::XNameContainer>::get(), 0, 0 },
+ {SC_UNO_PAGE_WIDTH, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_WIDTH | CONVERT_TWIPS },
+ {SC_UNONAME_WRITING, ATTR_WRITINGDIR, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ {SC_UNONAME_HIDDEN, ATTR_HIDDEN, cppu::UnoType<sal_Bool>::get(), 0, 0 },
+ };
+ static SfxItemPropertySet aPageStyleSet_Impl( aPageStyleMap_Impl );
+ return &aPageStyleSet_Impl;
+}
+
+// map with content of the Header-Item-Sets
+
+static const SfxItemPropertyMap* lcl_GetHeaderStyleMap()
+{
+ static const SfxItemPropertyMapEntry aHeaderStyleMap_Impl[] =
+ {
+ {SC_UNO_PAGE_HDRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ {SC_UNO_PAGE_HDRGRFFILT, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_FILTER },
+ {SC_UNO_PAGE_HDRGRFLOC, ATTR_BACKGROUND, ::cppu::UnoType<style::GraphicLocation>::get(), 0, MID_GRAPHIC_POSITION },
+ {SC_UNO_PAGE_HDRGRFURL, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_URL },
+ {SC_UNO_PAGE_HDRGRF, ATTR_BACKGROUND, ::cppu::UnoType<graphic::XGraphic>::get(), 0, MID_GRAPHIC },
+ {SC_UNO_PAGE_HDRBACKTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ {OLD_UNO_PAGE_HDRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ {SC_UNO_PAGE_HDRBODYDIST, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_LO_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRBOTTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, BOTTOM_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRBOTTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_HDRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRHEIGHT, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_HEIGHT | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FIRSTHDRSHARED, ATTR_PAGE_SHARED_FIRST, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRLEFTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, LEFT_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRLEFTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRLEFTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_L_MARGIN | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_HDRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRRIGHTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, RIGHT_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRRIGHTBDIS,ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRRIGHTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_R_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRSHADOW, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_HDRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_HDRTOPBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, TOP_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_HDRTOPBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_HDRBACKTRAN,ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ };
+ static SfxItemPropertyMap aHeaderStyleMap( aHeaderStyleMap_Impl );
+ return &aHeaderStyleMap;
+}
+
+// map with content of the Footer-Item-Sets
+
+static const SfxItemPropertyMap* lcl_GetFooterStyleMap()
+{
+ static const SfxItemPropertyMapEntry aFooterStyleMap_Impl[] =
+ {
+ {SC_UNO_PAGE_FTRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ {SC_UNO_PAGE_FTRGRFFILT, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_FILTER },
+ {SC_UNO_PAGE_FTRGRFLOC, ATTR_BACKGROUND, ::cppu::UnoType<style::GraphicLocation>::get(), 0, MID_GRAPHIC_POSITION },
+ {SC_UNO_PAGE_FTRGRFURL, ATTR_BACKGROUND, ::cppu::UnoType<OUString>::get(), 0, MID_GRAPHIC_URL },
+ {SC_UNO_PAGE_FTRGRF, ATTR_BACKGROUND, ::cppu::UnoType<graphic::XGraphic>::get(), 0, MID_GRAPHIC },
+ {SC_UNO_PAGE_FTRBACKTRAN, ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ {OLD_UNO_PAGE_FTRBACKCOL, ATTR_BACKGROUND, ::cppu::UnoType<sal_Int32>::get(), 0, MID_BACK_COLOR },
+ {SC_UNO_PAGE_FTRBODYDIST, ATTR_ULSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_UP_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRBRDDIST, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRBOTTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, BOTTOM_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRBOTTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_FTRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRHEIGHT, ATTR_PAGE_SIZE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_SIZE_HEIGHT | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRDYNAMIC, ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FIRSTFTRSHARED, ATTR_PAGE_SHARED_FIRST, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRLEFTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, LEFT_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRLEFTBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRLEFTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_L_MARGIN | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_FTRON, ATTR_PAGE_ON, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRRIGHTBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, RIGHT_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRRIGHTBDIS,ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRRIGHTMAR, ATTR_LRSPACE, ::cppu::UnoType<sal_Int32>::get(), 0, MID_R_MARGIN | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRSHADOW, ATTR_SHADOW, ::cppu::UnoType<table::ShadowFormat>::get(), 0, 0 | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_FTRSHARED, ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), 0, 0 },
+ {SC_UNO_PAGE_FTRTOPBOR, ATTR_BORDER, ::cppu::UnoType<table::BorderLine>::get(), 0, TOP_BORDER | CONVERT_TWIPS },
+ {SC_UNO_PAGE_FTRTOPBDIS, ATTR_BORDER, ::cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE | CONVERT_TWIPS },
+ {OLD_UNO_PAGE_FTRBACKTRAN,ATTR_BACKGROUND, cppu::UnoType<bool>::get(), 0, MID_GRAPHIC_TRANSPARENT },
+ };
+ static SfxItemPropertyMap aFooterStyleMap( aFooterStyleMap_Impl );
+ return &aFooterStyleMap;
+}
+
+// access index on the style types: 0 = Cell, 1 = Page, 2 = Drawing
+
+#define SC_STYLE_FAMILY_COUNT 3
+
+constexpr OUString SC_FAMILYNAME_CELL = u"CellStyles"_ustr;
+constexpr OUString SC_FAMILYNAME_PAGE = u"PageStyles"_ustr;
+constexpr OUString SC_FAMILYNAME_GRAPHIC = u"GraphicStyles"_ustr;
+
+const SfxStyleFamily aStyleFamilyTypes[SC_STYLE_FAMILY_COUNT] = { SfxStyleFamily::Para, SfxStyleFamily::Page, SfxStyleFamily::Frame };
+
+constexpr OUString SCSTYLE_SERVICE = u"com.sun.star.style.Style"_ustr;
+constexpr OUString SCCELLSTYLE_SERVICE = u"com.sun.star.style.CellStyle"_ustr;
+constexpr OUString SCPAGESTYLE_SERVICE = u"com.sun.star.style.PageStyle"_ustr;
+constexpr OUString SCGRAPHICSTYLE_SERVICE = u"com.sun.star.style.GraphicStyle"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScStyleFamiliesObj, "ScStyleFamiliesObj", "com.sun.star.style.StyleFamilies" )
+SC_SIMPLE_SERVICE_INFO( ScStyleFamilyObj, "ScStyleFamilyObj", "com.sun.star.style.StyleFamily" )
+
+constexpr OUString SC_PAPERBIN_DEFAULTNAME = u"[From printer settings]"_ustr;
+
+static bool lcl_AnyTabProtected( const ScDocument& rDoc )
+{
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount; i++)
+ if (rDoc.IsTabProtected(i))
+ return true;
+ return false;
+}
+
+ScStyleFamiliesObj::ScStyleFamiliesObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScStyleFamiliesObj::~ScStyleFamiliesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScStyleFamiliesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update does not matter here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr;
+ }
+}
+
+// XStyleFamilies
+
+rtl::Reference<ScStyleFamilyObj> ScStyleFamiliesObj::GetObjectByType_Impl(SfxStyleFamily nType) const
+{
+ if ( pDocShell )
+ {
+ if ( nType == SfxStyleFamily::Para )
+ return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Para );
+ else if ( nType == SfxStyleFamily::Page )
+ return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Page );
+ else if ( nType == SfxStyleFamily::Frame )
+ return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Frame );
+ }
+ OSL_FAIL("getStyleFamilyByType: no DocShell or wrong SfxStyleFamily");
+ return nullptr;
+}
+
+rtl::Reference<ScStyleFamilyObj> ScStyleFamiliesObj::GetObjectByIndex_Impl(sal_uInt32 nIndex) const
+{
+ if ( nIndex < SC_STYLE_FAMILY_COUNT )
+ return GetObjectByType_Impl(aStyleFamilyTypes[nIndex]);
+
+ return nullptr; // invalid index
+}
+
+rtl::Reference<ScStyleFamilyObj> ScStyleFamiliesObj::GetObjectByName_Impl(std::u16string_view aName) const
+{
+ if ( pDocShell )
+ {
+ if ( aName == SC_FAMILYNAME_CELL )
+ return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Para );
+ else if ( aName == SC_FAMILYNAME_PAGE )
+ return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Page );
+ else if ( aName == SC_FAMILYNAME_GRAPHIC )
+ return new ScStyleFamilyObj( pDocShell, SfxStyleFamily::Frame );
+ }
+ // no assertion - called directly from getByName
+ return nullptr;
+}
+
+// container::XIndexAccess
+
+sal_Int32 SAL_CALL ScStyleFamiliesObj::getCount()
+{
+ return SC_STYLE_FAMILY_COUNT;
+}
+
+uno::Any SAL_CALL ScStyleFamiliesObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< container::XNameContainer > xFamily(GetObjectByIndex_Impl(nIndex));
+ if (!xFamily.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xFamily);
+}
+
+uno::Type SAL_CALL ScStyleFamiliesObj::getElementType()
+{
+ return cppu::UnoType<container::XNameContainer>::get(); // has to fit to getByIndex
+}
+
+sal_Bool SAL_CALL ScStyleFamiliesObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// container::XNameAccess
+
+uno::Any SAL_CALL ScStyleFamiliesObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< container::XNameContainer > xFamily(GetObjectByName_Impl(aName));
+ if (!xFamily.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xFamily);
+}
+
+uno::Sequence<OUString> SAL_CALL ScStyleFamiliesObj::getElementNames()
+{
+ return {SC_FAMILYNAME_CELL, SC_FAMILYNAME_PAGE, SC_FAMILYNAME_GRAPHIC};
+}
+
+sal_Bool SAL_CALL ScStyleFamiliesObj::hasByName( const OUString& aName )
+{
+ return aName == SC_FAMILYNAME_CELL || aName == SC_FAMILYNAME_PAGE || aName == SC_FAMILYNAME_GRAPHIC;
+}
+
+// style::XStyleLoader
+
+void SAL_CALL ScStyleFamiliesObj::loadStylesFromURL( const OUString& aURL,
+ const uno::Sequence<beans::PropertyValue>& aOptions )
+{
+ //! use aOptions (like Writer)
+ //! set flag to disable filter option dialogs when importing
+
+ OUString aFilter; // empty - detect
+ OUString aFiltOpt;
+ uno::Reference<io::XInputStream> xInputStream;
+ if (aURL == "private:stream")
+ {
+ for (const auto& rProp : aOptions)
+ {
+ if (rProp.Name == "InputStream")
+ {
+ rProp.Value >>= xInputStream;
+ if (!xInputStream.is())
+ {
+ throw lang::IllegalArgumentException(
+ "Parameter 'InputStream' could not be converted "
+ "to type 'com::sun::star::io::XInputStream'",
+ nullptr, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ ScDocumentLoader aLoader( aURL, aFilter, aFiltOpt, 0, nullptr, xInputStream );
+
+ ScDocShell* pSource = aLoader.GetDocShell();
+
+ loadStylesFromDocShell(pSource, aOptions);
+}
+
+uno::Sequence<beans::PropertyValue> SAL_CALL ScStyleFamiliesObj::getStyleLoaderOptions()
+{
+ // return defaults for options (?)
+ return comphelper::InitPropertySequence({
+ { SC_UNONAME_OVERWSTL, uno::Any(true) },
+ { SC_UNONAME_LOADCELL, uno::Any(true) },
+ { SC_UNONAME_LOADPAGE, uno::Any(true) }
+ });
+}
+
+// style::XStyleLoader2
+
+void SAL_CALL ScStyleFamiliesObj::loadStylesFromDocument( const uno::Reference < lang::XComponent > & aSourceComponent,
+ const uno::Sequence<beans::PropertyValue>& aOptions )
+{
+ // Source document docShell
+ if ( !aSourceComponent.is() )
+ throw uno::RuntimeException();
+
+ ScDocShell* pDocShellSrc = dynamic_cast<ScDocShell*> (SfxObjectShell::GetShellFromComponent(aSourceComponent));
+
+ loadStylesFromDocShell(pDocShellSrc, aOptions);
+}
+
+// private
+
+void ScStyleFamiliesObj::loadStylesFromDocShell( ScDocShell* pSource,
+ const uno::Sequence<beans::PropertyValue>& aOptions )
+{
+
+ if ( !(pSource && pDocShell) )
+ return;
+
+ // collect options
+
+ bool bLoadReplace = true; // defaults
+ bool bLoadCellStyles = true;
+ bool bLoadPageStyles = true;
+
+ for (const beans::PropertyValue& rProp : aOptions)
+ {
+ OUString aPropName(rProp.Name);
+
+ if (aPropName == SC_UNONAME_OVERWSTL)
+ bLoadReplace = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_LOADCELL)
+ bLoadCellStyles = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_LOADPAGE)
+ bLoadPageStyles = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ }
+
+ pDocShell->LoadStylesArgs( *pSource, bLoadReplace, bLoadCellStyles, bLoadPageStyles );
+ pDocShell->SetDocumentModified(); // paint is inside LoadStyles
+}
+
+ScStyleFamilyObj::ScStyleFamilyObj(ScDocShell* pDocSh, SfxStyleFamily eFam) :
+ pDocShell( pDocSh ),
+ eFamily( eFam )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScStyleFamilyObj::~ScStyleFamilyObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScStyleFamilyObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update does not matter here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // has become invalid
+ }
+}
+
+// XStyleFamily
+
+rtl::Reference<ScStyleObj> ScStyleFamilyObj::GetObjectByIndex_Impl(sal_Int32 nIndex)
+{
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+
+ SfxStyleSheetIterator aIter( pStylePool, eFamily );
+ if ( nIndex < aIter.Count() )
+ {
+ SfxStyleSheetBase* pStyle = aIter[nIndex];
+ if ( pStyle )
+ {
+ return new ScStyleObj( pDocShell, eFamily, pStyle->GetName() );
+ }
+ }
+ }
+ return nullptr;
+}
+
+rtl::Reference<ScStyleObj> ScStyleFamilyObj::GetObjectByName_Impl(const OUString& aName)
+{
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if ( pStylePool->Find( aName, eFamily ) )
+ return new ScStyleObj( pDocShell, eFamily, aName );
+ }
+ return nullptr;
+}
+
+void SAL_CALL ScStyleFamilyObj::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ bool bDone = false;
+ // reflection does not need to be uno::XInterface, can be any interface...
+ uno::Reference< uno::XInterface > xInterface(aElement, uno::UNO_QUERY);
+ if ( xInterface.is() )
+ {
+ ScStyleObj* pStyleObj = dynamic_cast<ScStyleObj*>( xInterface.get() );
+ if ( pStyleObj && pStyleObj->GetFamily() == eFamily &&
+ !pStyleObj->IsInserted() ) // not yet inserted?
+ {
+ OUString aNameStr(ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily ));
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+
+ //! DocFunc function ???
+ //! Undo ?????????????
+
+ if ( pStylePool->Find( aNameStr, eFamily ) ) // not available yet
+ throw container::ElementExistException();
+
+ (void)pStylePool->Make( aNameStr, eFamily, SfxStyleSearchBits::UserDefined );
+
+ if ( eFamily == SfxStyleFamily::Para && !rDoc.IsImportingXML() )
+ rDoc.GetPool()->CellStyleCreated( aNameStr, rDoc );
+
+ pStyleObj->InitDoc( pDocShell, aNameStr ); // object can be used
+
+ if (!rDoc.IsImportingXML())
+ pDocShell->SetDocumentModified(); // new style not used yet
+ bDone = true;
+
+ }
+ }
+
+ if (!bDone)
+ {
+ // other errors are handled above
+ throw lang::IllegalArgumentException();
+ }
+}
+
+void SAL_CALL ScStyleFamilyObj::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ //! combine?
+ removeByName( aName );
+ insertByName( aName, aElement );
+}
+
+void SAL_CALL ScStyleFamilyObj::removeByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ bool bFound = false;
+ if ( pDocShell )
+ {
+ OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily ));
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+
+ //! DocFunc function??
+ //! Undo ?????????????
+
+ SfxStyleSheetBase* pStyle = pStylePool->Find( aString, eFamily );
+ if (pStyle)
+ {
+ bFound = true;
+ if ( eFamily == SfxStyleFamily::Para )
+ {
+ // like ScViewFunc::RemoveStyleSheetInUse
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ double nPPTX = aLogic.X() / 1000.0;
+ double nPPTY = aLogic.Y() / 1000.0;
+ Fraction aZoom(1,1);
+ rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom );
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ pDocShell->SetDocumentModified();
+
+ pStylePool->Remove( pStyle );
+
+ //! InvalidateAttribs(); // Bindings-Invalidate
+ }
+ else if ( eFamily == SfxStyleFamily::Page )
+ {
+ if ( rDoc.RemovePageStyleInUse( aString ) )
+ pDocShell->PageStyleModified( ScResId(STR_STYLENAME_STANDARD), true );
+
+ pStylePool->Remove( pStyle );
+
+ SfxBindings* pBindings = pDocShell->GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_STYLE_FAMILY4 );
+ pDocShell->SetDocumentModified();
+ }
+ else
+ {
+ pStylePool->Remove( pStyle );
+
+ SfxBindings* pBindings = pDocShell->GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_STYLE_FAMILY3 );
+ pDocShell->SetDocumentModified();
+ }
+ }
+ }
+
+ if (!bFound)
+ throw container::NoSuchElementException();
+}
+
+// container::XIndexAccess
+
+sal_Int32 SAL_CALL ScStyleFamilyObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+
+ SfxStyleSheetIterator aIter( pStylePool, eFamily );
+ return aIter.Count();
+ }
+ return 0;
+}
+
+uno::Any SAL_CALL ScStyleFamilyObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< style::XStyle > xObj(GetObjectByIndex_Impl(nIndex));
+ if (!xObj.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xObj);
+}
+
+uno::Type SAL_CALL ScStyleFamilyObj::getElementType()
+{
+ return cppu::UnoType<style::XStyle>::get(); // has to fit to getByIndex
+}
+
+sal_Bool SAL_CALL ScStyleFamilyObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// container::XNameAccess
+
+uno::Any SAL_CALL ScStyleFamilyObj::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< style::XStyle > xObj(
+ GetObjectByName_Impl( ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily ) ));
+ if (!xObj.is())
+ throw container::NoSuchElementException();
+
+ return uno::Any(xObj);
+}
+
+uno::Sequence<OUString> SAL_CALL ScStyleFamilyObj::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+
+ SfxStyleSheetIterator aIter( pStylePool, eFamily );
+ sal_uInt16 nCount = aIter.Count();
+
+ uno::Sequence<OUString> aSeq(nCount);
+ OUString* pAry = aSeq.getArray();
+ SfxStyleSheetBase* pStyle = aIter.First();
+ sal_uInt16 nPos = 0;
+ while (pStyle)
+ {
+ OSL_ENSURE( nPos < nCount, "Count is wrong" );
+ if (nPos < nCount)
+ pAry[nPos++] = ScStyleNameConversion::DisplayToProgrammaticName(
+ pStyle->GetName(), eFamily );
+ pStyle = aIter.Next();
+ }
+ return aSeq;
+ }
+ return uno::Sequence<OUString>();
+}
+
+sal_Bool SAL_CALL ScStyleFamilyObj::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+ if ( pDocShell )
+ {
+ OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( aName, eFamily ));
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if ( pStylePool->Find( aString, eFamily ) )
+ return true;
+ }
+ return false;
+}
+
+// XPropertySet
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL ScStyleFamilyObj::getPropertySetInfo( )
+{
+ OSL_FAIL( "###unexpected!" );
+ return uno::Reference< beans::XPropertySetInfo >();
+}
+
+void SAL_CALL ScStyleFamilyObj::setPropertyValue( const OUString&, const uno::Any& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+uno::Any SAL_CALL ScStyleFamilyObj::getPropertyValue( const OUString& sPropertyName )
+{
+ uno::Any aRet;
+
+ if ( sPropertyName != "DisplayName" )
+ {
+ throw beans::UnknownPropertyException( "unknown property: " + sPropertyName, getXWeak() );
+ }
+
+ SolarMutexGuard aGuard;
+ TranslateId pResId;
+ switch ( eFamily )
+ {
+ case SfxStyleFamily::Para:
+ pResId = STR_STYLE_FAMILY_CELL; break;
+ case SfxStyleFamily::Page:
+ pResId = STR_STYLE_FAMILY_PAGE; break;
+ case SfxStyleFamily::Frame:
+ pResId = STR_STYLE_FAMILY_GRAPHICS; break;
+ default:
+ OSL_FAIL( "ScStyleFamilyObj::getPropertyValue(): invalid family" );
+ }
+ if (pResId)
+ {
+ OUString sDisplayName(ScResId(pResId));
+ aRet <<= sDisplayName;
+ }
+
+ return aRet;
+}
+
+void SAL_CALL ScStyleFamilyObj::addPropertyChangeListener( const OUString&, const uno::Reference< beans::XPropertyChangeListener >& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+void SAL_CALL ScStyleFamilyObj::removePropertyChangeListener( const OUString&, const uno::Reference< beans::XPropertyChangeListener >& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+void SAL_CALL ScStyleFamilyObj::addVetoableChangeListener( const OUString&, const uno::Reference< beans::XVetoableChangeListener >& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+void SAL_CALL ScStyleFamilyObj::removeVetoableChangeListener( const OUString&, const uno::Reference< beans::XVetoableChangeListener >& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+// default ctor is needed for reflection
+
+ScStyleObj::ScStyleObj(ScDocShell* pDocSh, SfxStyleFamily eFam, OUString aName)
+ : pDocShell(pDocSh)
+ , eFamily(eFam)
+ , aStyleName(std::move(aName))
+ , pStyle_cached(nullptr)
+{
+ if (eFam == SfxStyleFamily::Para)
+ pPropSet = lcl_GetCellStyleSet();
+ else if (eFam == SfxStyleFamily::Page)
+ pPropSet = lcl_GetPageStyleSet();
+ else
+ pPropSet = lcl_GetGraphicStyleSet();
+
+ // if create by ServiceProvider then pDocShell is NULL
+
+ if (pDocShell)
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+void ScStyleObj::InitDoc( ScDocShell* pNewDocSh, const OUString& rNewName )
+{
+ if ( pNewDocSh && !pDocShell )
+ {
+ aStyleName = rNewName;
+ pDocShell = pNewDocSh;
+ pDocShell->GetDocument().AddUnoObject(*this);
+ }
+}
+
+ScStyleObj::~ScStyleObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScStyleObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ // reference update does not matter here
+
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // has become invalid
+ }
+}
+
+SfxStyleSheetBase* ScStyleObj::GetStyle_Impl( bool bUseCachedValue )
+{
+ if ( bUseCachedValue )
+ return pStyle_cached;
+
+ pStyle_cached = nullptr;
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ pStyle_cached = pStylePool->Find( aStyleName, eFamily );
+ }
+ return pStyle_cached;
+}
+
+// style::XStyle
+
+sal_Bool SAL_CALL ScStyleObj::isUserDefined()
+{
+ SolarMutexGuard aGuard;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if (pStyle)
+ return pStyle->IsUserDefined();
+ return false;
+}
+
+sal_Bool SAL_CALL ScStyleObj::isInUse()
+{
+ SolarMutexGuard aGuard;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if (pStyle)
+ return pStyle->IsUsed();
+ return false;
+}
+
+OUString SAL_CALL ScStyleObj::getParentStyle()
+{
+ SolarMutexGuard aGuard;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if (pStyle)
+ return ScStyleNameConversion::DisplayToProgrammaticName( pStyle->GetParent(), eFamily );
+ return OUString();
+}
+
+void SAL_CALL ScStyleObj::setParentStyle( const OUString& rParentStyle )
+{
+ SolarMutexGuard aGuard;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if (!pStyle)
+ return;
+
+ // cell styles cannot be modified if any sheet is protected
+ if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) )
+ return; //! exception?
+
+ //! DocFunc function??
+ //! Undo ?????????????
+
+ OUString aString(ScStyleNameConversion::ProgrammaticToDisplayName( rParentStyle, eFamily ));
+ bool bOk = pStyle->SetParent( aString );
+ if (!bOk)
+ return;
+
+ // as by setPropertyValue
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( eFamily == SfxStyleFamily::Para )
+ {
+ // update line height
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ Point aLogic = pVDev->LogicToPixel( Point(1000,1000), MapMode(MapUnit::MapTwip));
+ double nPPTX = aLogic.X() / 1000.0;
+ double nPPTY = aLogic.Y() / 1000.0;
+ Fraction aZoom(1,1);
+ rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom );
+
+ if (!rDoc.IsImportingXML())
+ {
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ pDocShell->SetDocumentModified();
+ }
+ }
+ else if ( eFamily == SfxStyleFamily::Page )
+ {
+ //! ModifyStyleSheet on document (save old values)
+
+ pDocShell->PageStyleModified( aStyleName, true );
+ }
+ else
+ static_cast<SfxStyleSheet*>(GetStyle_Impl())->Broadcast(SfxHint(SfxHintId::DataChanged));
+}
+
+// container::XNamed
+
+OUString SAL_CALL ScStyleObj::getName()
+{
+ SolarMutexGuard aGuard;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if (pStyle)
+ return ScStyleNameConversion::DisplayToProgrammaticName( pStyle->GetName(), eFamily );
+ return OUString();
+}
+
+void SAL_CALL ScStyleObj::setName( const OUString& aNewName )
+{
+ SolarMutexGuard aGuard;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if (!pStyle)
+ return;
+
+ // cell styles cannot be renamed if any sheet is protected
+ if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) )
+ return; //! exception?
+
+ //! DocFunc function??
+ //! Undo ?????????????
+
+ bool bOk = pStyle->SetName( aNewName );
+ if (!bOk)
+ return;
+
+ aStyleName = aNewName; //! notify other objects for this style?
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( eFamily == SfxStyleFamily::Para && !rDoc.IsImportingXML() )
+ rDoc.GetPool()->CellStyleCreated( aNewName, rDoc );
+
+ // cell styles = 2, drawing styles = 3, page styles = 4
+ sal_uInt16 nId = eFamily == SfxStyleFamily::Para ? SID_STYLE_FAMILY2 :
+ (eFamily == SfxStyleFamily::Page ? SID_STYLE_FAMILY4 : SID_STYLE_FAMILY3);
+ SfxBindings* pBindings = pDocShell->GetViewBindings();
+ if (pBindings)
+ {
+ pBindings->Invalidate( nId );
+ pBindings->Invalidate( SID_STYLE_APPLY );
+ }
+}
+
+uno::Reference<container::XIndexReplace> ScStyleObj::CreateEmptyNumberingRules()
+{
+ SvxNumRule aRule( SvxNumRuleFlags::NONE, 0, true ); // nothing supported
+ return SvxCreateNumRule( aRule );
+}
+
+// beans::XPropertyState
+
+const SfxItemSet* ScStyleObj::GetStyleItemSet_Impl( std::u16string_view rPropName,
+ const SfxItemPropertyMapEntry*& rpResultEntry )
+{
+ SfxStyleSheetBase* pStyle = GetStyle_Impl( true );
+ if ( pStyle )
+ {
+ const SfxItemPropertyMapEntry* pEntry = nullptr;
+ if ( eFamily == SfxStyleFamily::Page )
+ {
+ pEntry = lcl_GetHeaderStyleMap()->getByName( rPropName );
+ if ( pEntry ) // only item-WIDs in header/footer map
+ {
+ rpResultEntry = pEntry;
+ return &pStyle->GetItemSet().Get(ATTR_PAGE_HEADERSET).GetItemSet();
+ }
+ pEntry = lcl_GetFooterStyleMap()->getByName( rPropName );
+ if ( pEntry ) // only item-WIDs in header/footer map
+ {
+ rpResultEntry = pEntry;
+ return &pStyle->GetItemSet().Get(ATTR_PAGE_FOOTERSET).GetItemSet();
+ }
+ }
+ pEntry = pPropSet->getPropertyMap().getByName( rPropName );
+ if ( pEntry )
+ {
+ rpResultEntry = pEntry;
+ return &pStyle->GetItemSet();
+ }
+ }
+
+ rpResultEntry = nullptr;
+ return nullptr;
+}
+
+beans::PropertyState ScStyleObj::getPropertyState_Impl( std::u16string_view aPropertyName )
+{
+ beans::PropertyState eRet = beans::PropertyState_DIRECT_VALUE;
+
+ const SfxItemPropertyMapEntry* pResultEntry = nullptr;
+ const SfxItemSet* pItemSet = GetStyleItemSet_Impl( aPropertyName, pResultEntry );
+
+ if ( pItemSet && pResultEntry )
+ {
+ sal_uInt16 nWhich = pResultEntry->nWID;
+ if ( nWhich == SC_WID_UNO_TBLBORD || nWhich == SC_WID_UNO_TBLBORD2 )
+ {
+ nWhich = ATTR_BORDER;
+ }
+ if ( nWhich == OWN_ATTR_FILLBMP_MODE )
+ {
+ if ( pItemSet->GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET ||
+ pItemSet->GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET )
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ eRet = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ }
+ else if ( nWhich == SDRATTR_TEXTDIRECTION )
+ {
+ eRet = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else if ( IsScItemWid( nWhich ) || eFamily == SfxStyleFamily::Frame )
+ {
+ SfxItemState eState = pItemSet->GetItemState( nWhich, false );
+
+// // if no rotate value is set, look at orientation
+// //! also for a fixed value of 0 (in case orientation is ambiguous)?
+// if ( nWhich == ATTR_ROTATE_VALUE && eState == SfxItemState::DEFAULT )
+// eState = pItemSet->GetItemState( ATTR_ORIENTATION, sal_False );
+
+ if ( eState == SfxItemState::SET )
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ else if ( eState == SfxItemState::DEFAULT )
+ eRet = beans::PropertyState_DEFAULT_VALUE;
+ else
+ {
+ assert(eFamily == SfxStyleFamily::Frame);
+ eRet = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ }
+ }
+ return eRet;
+}
+
+beans::PropertyState SAL_CALL ScStyleObj::getPropertyState( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ return getPropertyState_Impl( aPropertyName );
+}
+
+uno::Sequence<beans::PropertyState> SAL_CALL ScStyleObj::getPropertyStates( const uno::Sequence<OUString>& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ uno::Sequence<beans::PropertyState> aRet( aPropertyNames.getLength() );
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aRet.getArray(),
+ [this](const OUString& rName) -> beans::PropertyState { return getPropertyState_Impl(rName); });
+ return aRet;
+}
+
+void SAL_CALL ScStyleObj::setPropertyToDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMap().getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ setPropertyValue_Impl( aPropertyName, pEntry, nullptr );
+}
+
+uno::Any ScStyleObj::getPropertyDefault_Impl( std::u16string_view aPropertyName )
+{
+ uno::Any aAny;
+
+ const SfxItemPropertyMapEntry* pResultEntry = nullptr;
+ const SfxItemSet* pStyleSet = GetStyleItemSet_Impl( aPropertyName, pResultEntry );
+
+ if ( pStyleSet && pResultEntry )
+ {
+ sal_uInt16 nWhich = pResultEntry->nWID;
+
+ if ( IsScItemWid( nWhich ) )
+ {
+ // Default is default from ItemPool, not from Standard-Style,
+ // so it is the same as in setPropertyToDefault
+ SfxItemSet aEmptySet( *pStyleSet->GetPool(), pStyleSet->GetRanges() );
+ // default items with wrong Slot-ID are not functional in SfxItemPropertySet3
+ //! change Slot-IDs...
+ if ( aEmptySet.GetPool()->GetSlotId(nWhich) == nWhich &&
+ aEmptySet.GetItemState(nWhich, false) == SfxItemState::DEFAULT )
+ {
+ aEmptySet.Put( aEmptySet.Get( nWhich ) );
+ }
+ const SfxItemSet* pItemSet = &aEmptySet;
+
+ switch ( nWhich ) // special item handling
+ {
+ case ATTR_VALUE_FORMAT:
+ // default has no language set
+ aAny <<= sal_Int32( static_cast<const SfxUInt32Item&>(pItemSet->Get(nWhich)).GetValue() );
+ break;
+ case ATTR_INDENT:
+ aAny <<= sal_Int16( convertTwipToMm100(static_cast<const ScIndentItem&>(
+ pItemSet->Get(nWhich)).GetValue()) );
+ break;
+ case ATTR_PAGE_SCALE:
+ case ATTR_PAGE_SCALETOPAGES:
+ case ATTR_PAGE_FIRSTPAGENO:
+ aAny <<= sal_Int16( static_cast<const SfxUInt16Item&>(pItemSet->Get(nWhich)).GetValue() );
+ break;
+ case ATTR_PAGE_CHARTS:
+ case ATTR_PAGE_OBJECTS:
+ case ATTR_PAGE_DRAWINGS:
+ //! define sal_Bool-MID for ScViewObjectModeItem?
+ aAny <<= static_cast<const ScViewObjectModeItem&>(pItemSet->Get(nWhich)).GetValue() == VOBJ_MODE_SHOW;
+ break;
+ case ATTR_PAGE_SCALETO:
+ {
+ const ScPageScaleToItem aItem(static_cast<const ScPageScaleToItem&>(pItemSet->Get(nWhich)));
+ if ( aPropertyName == SC_UNO_PAGE_SCALETOX )
+ aAny <<= static_cast<sal_Int16>(aItem.GetWidth());
+ else
+ aAny <<= static_cast<sal_Int16>(aItem.GetHeight());
+ }
+ break;
+ default:
+ pPropSet->getPropertyValue( *pResultEntry, *pItemSet, aAny );
+ }
+ }
+ else if ( IsScUnoWid( nWhich ) )
+ {
+ SfxItemSet aEmptySet( *pStyleSet->GetPool(), pStyleSet->GetRanges() );
+ const SfxItemSet* pItemSet = &aEmptySet;
+ switch ( nWhich )
+ {
+ case SC_WID_UNO_TBLBORD:
+ case SC_WID_UNO_TBLBORD2:
+ {
+ const SfxPoolItem& rItem = pItemSet->Get(ATTR_BORDER);
+ SvxBoxItem aOuter(static_cast<const SvxBoxItem&>(rItem));
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ if (nWhich == SC_WID_UNO_TBLBORD2)
+ ScHelperFunctions::AssignTableBorder2ToAny(aAny, aOuter, aInner, true);
+ else
+ ScHelperFunctions::AssignTableBorderToAny(aAny, aOuter, aInner, true);
+ }
+ break;
+ }
+ }
+ else if ( nWhich == SDRATTR_TEXTDIRECTION )
+ {
+ aAny <<= false;
+ }
+ else if ( nWhich == OWN_ATTR_FILLBMP_MODE )
+ {
+ aAny <<= css::drawing::BitmapMode_REPEAT;
+ }
+ else if ( nWhich != OWN_ATTR_TEXTCOLUMNS )
+ {
+ SfxItemSet aItemSet(*pStyleSet->GetPool(), pStyleSet->GetRanges());
+ aAny = SvxItemPropertySet_getPropertyValue(pResultEntry, aItemSet);
+ }
+ }
+ return aAny;
+}
+
+uno::Any SAL_CALL ScStyleObj::getPropertyDefault( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ return getPropertyDefault_Impl( aPropertyName );
+}
+
+uno::Sequence<uno::Any> SAL_CALL ScStyleObj::getPropertyDefaults( const uno::Sequence<OUString>& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ uno::Sequence<uno::Any> aSequence( aPropertyNames.getLength() );
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aSequence.getArray(),
+ [this](const OUString& rName) -> uno::Any { return getPropertyDefault_Impl(rName); });
+ return aSequence;
+}
+
+// XMultiPropertySet
+
+void SAL_CALL ScStyleObj::setPropertyValues( const uno::Sequence< OUString >& aPropertyNames,
+ const uno::Sequence< uno::Any >& aValues )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ if ( aValues.getLength() != aPropertyNames.getLength() )
+ throw lang::IllegalArgumentException();
+
+ const OUString* pNames = aPropertyNames.getConstArray();
+ const uno::Any* pValues = aValues.getConstArray();
+ const SfxItemPropertyMap& rPropertyMap = pPropSet->getPropertyMap();
+ for ( sal_Int32 i = 0; i < aPropertyNames.getLength(); i++ )
+ {
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( pNames[i] );
+ setPropertyValue_Impl( pNames[i], pEntry, &pValues[i] );
+ }
+}
+
+uno::Sequence<uno::Any> SAL_CALL ScStyleObj::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ uno::Sequence<uno::Any> aSequence( aPropertyNames.getLength() );
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aSequence.getArray(),
+ [this](const OUString& rName) -> uno::Any { return getPropertyValue_Impl(rName); });
+ return aSequence;
+}
+
+void SAL_CALL ScStyleObj::addPropertiesChangeListener( const uno::Sequence<OUString>& /* aPropertyNames */,
+ const uno::Reference<beans::XPropertiesChangeListener>& /* xListener */ )
+{
+ // no bound properties
+}
+
+void SAL_CALL ScStyleObj::removePropertiesChangeListener(
+ const uno::Reference<beans::XPropertiesChangeListener>& /* xListener */ )
+{
+ // no bound properties
+}
+
+void SAL_CALL ScStyleObj::firePropertiesChangeEvent( const uno::Sequence<OUString>& /* aPropertyNames */,
+ const uno::Reference<beans::XPropertiesChangeListener>& /* xListener */ )
+{
+ // no bound properties
+}
+
+// XMultiPropertyStates
+// getPropertyStates already defined for XPropertyState
+
+void SAL_CALL ScStyleObj::setAllPropertiesToDefault()
+{
+ SolarMutexGuard aGuard;
+
+ SfxStyleSheetBase* pStyle = GetStyle_Impl();
+ if ( !pStyle )
+ return;
+
+ // cell styles cannot be modified if any sheet is protected
+ if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) )
+ throw uno::RuntimeException();
+
+ SfxItemSet& rSet = pStyle->GetItemSet();
+ rSet.ClearItem(); // set all items to default
+
+ //! merge with SetOneProperty
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ if ( eFamily == SfxStyleFamily::Para )
+ {
+ // row heights
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ double nPPTX = aLogic.X() / 1000.0;
+ double nPPTY = aLogic.Y() / 1000.0;
+ Fraction aZoom(1,1);
+ rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom );
+
+ if (!rDoc.IsImportingXML())
+ {
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ pDocShell->SetDocumentModified();
+ }
+ }
+ else if ( eFamily == SfxStyleFamily::Page )
+ {
+ // #i22448# apply the default BoxInfoItem for page styles again
+ // (same content as in ScStyleSheet::GetItemSet, to control the dialog)
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+ aBoxInfoItem.SetTable( false );
+ aBoxInfoItem.SetDist( true );
+ aBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ rSet.Put( aBoxInfoItem );
+
+ pDocShell->PageStyleModified( aStyleName, true );
+ }
+ else
+ static_cast<SfxStyleSheet*>(GetStyle_Impl())->Broadcast(SfxHint(SfxHintId::DataChanged));
+}
+
+void SAL_CALL ScStyleObj::setPropertiesToDefault( const uno::Sequence<OUString>& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ const SfxItemPropertyMap& rPropertyMap = pPropSet->getPropertyMap();
+ for ( const OUString& rName : aPropertyNames )
+ {
+ const SfxItemPropertyMapEntry* pEntry = rPropertyMap.getByName( rName );
+ setPropertyValue_Impl( rName, pEntry, nullptr );
+ }
+}
+
+// beans::XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScStyleObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ return pPropSet->getPropertySetInfo();
+}
+
+void SAL_CALL ScStyleObj::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMap().getByName( aPropertyName );
+ if ( !pEntry )
+ throw beans::UnknownPropertyException(aPropertyName);
+
+ setPropertyValue_Impl( aPropertyName, pEntry, &aValue );
+}
+
+void ScStyleObj::setPropertyValue_Impl( std::u16string_view rPropertyName, const SfxItemPropertyMapEntry* pEntry, const uno::Any* pValue )
+{
+ SfxStyleSheetBase* pStyle = GetStyle_Impl( true );
+ if ( !(pStyle && pEntry) )
+ return;
+
+ // cell styles cannot be modified if any sheet is protected
+ if ( eFamily == SfxStyleFamily::Para && lcl_AnyTabProtected( pDocShell->GetDocument() ) )
+ throw uno::RuntimeException();
+
+ SfxItemSet& rSet = pStyle->GetItemSet(); // change directly in active Style
+ bool bDone = false;
+ if ( eFamily == SfxStyleFamily::Page )
+ {
+ if(pEntry->nWID == SC_WID_UNO_HEADERSET)
+ {
+ const SfxItemPropertyMapEntry* pHeaderEntry = lcl_GetHeaderStyleMap()->getByName( rPropertyName );
+ if ( pHeaderEntry ) // only item-WIDs in header/footer map
+ {
+ SvxSetItem aNewHeader( rSet.Get(ATTR_PAGE_HEADERSET) );
+ if (pValue)
+ pPropSet->setPropertyValue( *pHeaderEntry, *pValue, aNewHeader.GetItemSet() );
+ else
+ aNewHeader.GetItemSet().ClearItem( pHeaderEntry->nWID );
+ rSet.Put( aNewHeader );
+ bDone = true;
+ }
+ }
+ else if(pEntry->nWID == SC_WID_UNO_FOOTERSET)
+ {
+ const SfxItemPropertyMapEntry* pFooterEntry = lcl_GetFooterStyleMap()->getByName( rPropertyName );
+ if ( pFooterEntry ) // only item-WIDs in header/footer map
+ {
+ SvxSetItem aNewFooter( rSet.Get(ATTR_PAGE_FOOTERSET) );
+ if (pValue)
+ pPropSet->setPropertyValue( *pFooterEntry, *pValue, aNewFooter.GetItemSet() );
+ else
+ aNewFooter.GetItemSet().ClearItem( pFooterEntry->nWID );
+ rSet.Put( aNewFooter );
+ bDone = true;
+ }
+ }
+ }
+ if (!bDone)
+ {
+ if (IsScItemWid(pEntry->nWID))
+ {
+ if (pValue)
+ {
+ switch (pEntry->nWID) // special item handling
+ {
+ case ATTR_VALUE_FORMAT:
+ {
+ // language for number formats
+ SvNumberFormatter* pFormatter
+ = pDocShell->GetDocument().GetFormatTable();
+ sal_uInt32 nOldFormat = rSet.Get(ATTR_VALUE_FORMAT).GetValue();
+ LanguageType eOldLang
+ = rSet.Get(ATTR_LANGUAGE_FORMAT).GetLanguage();
+ pFormatter->GetFormatForLanguageIfBuiltIn(nOldFormat, eOldLang);
+
+ sal_uInt32 nNewFormat = 0;
+ *pValue >>= nNewFormat;
+ rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nNewFormat));
+
+ const SvNumberformat* pNewEntry = pFormatter->GetEntry(nNewFormat);
+ LanguageType eNewLang
+ = pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ if (eNewLang != eOldLang && eNewLang != LANGUAGE_DONTKNOW)
+ rSet.Put(SvxLanguageItem(eNewLang, ATTR_LANGUAGE_FORMAT));
+
+ //! keep default state of number format if only language changed?
+ }
+ break;
+ case ATTR_INDENT:
+ {
+ sal_Int16 nVal = 0;
+ *pValue >>= nVal;
+ rSet.Put(ScIndentItem(o3tl::toTwips(nVal, o3tl::Length::mm100)));
+ }
+ break;
+ case ATTR_ROTATE_VALUE:
+ {
+ sal_Int32 nRotVal = 0;
+ if (*pValue >>= nRotVal)
+ {
+ // stored value is always between 0 and 360 deg.
+ nRotVal %= 36000;
+ if (nRotVal < 0)
+ nRotVal += 36000;
+ rSet.Put(ScRotateValueItem(Degree100(nRotVal)));
+ }
+ }
+ break;
+ case ATTR_STACKED:
+ {
+ table::CellOrientation eOrient;
+ if (*pValue >>= eOrient)
+ {
+ switch (eOrient)
+ {
+ case table::CellOrientation_STANDARD:
+ rSet.Put(ScVerticalStackCell(false));
+ break;
+ case table::CellOrientation_TOPBOTTOM:
+ rSet.Put(ScVerticalStackCell(false));
+ rSet.Put(ScRotateValueItem(27000_deg100));
+ break;
+ case table::CellOrientation_BOTTOMTOP:
+ rSet.Put(ScVerticalStackCell(false));
+ rSet.Put(ScRotateValueItem(9000_deg100));
+ break;
+ case table::CellOrientation_STACKED:
+ rSet.Put(ScVerticalStackCell(true));
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ break;
+ case ATTR_PAGE_SCALE:
+ case ATTR_PAGE_SCALETOPAGES:
+ {
+ rSet.ClearItem(ATTR_PAGE_SCALETOPAGES);
+ rSet.ClearItem(ATTR_PAGE_SCALE);
+ rSet.ClearItem(ATTR_PAGE_SCALETO);
+ sal_Int16 nVal = 0;
+ *pValue >>= nVal;
+ rSet.Put(SfxUInt16Item(pEntry->nWID, nVal));
+ }
+ break;
+ case ATTR_PAGE_FIRSTPAGENO:
+ {
+ sal_Int16 nVal = 0;
+ *pValue >>= nVal;
+ rSet.Put(SfxUInt16Item(ATTR_PAGE_FIRSTPAGENO, nVal));
+ }
+ break;
+ case ATTR_PAGE_CHARTS:
+ case ATTR_PAGE_OBJECTS:
+ case ATTR_PAGE_DRAWINGS:
+ {
+ bool bBool = false;
+ *pValue >>= bBool;
+ //! need to define sal_Bool-MID for ScViewObjectModeItem?
+ rSet.Put(ScViewObjectModeItem(
+ pEntry->nWID, bBool ? VOBJ_MODE_SHOW : VOBJ_MODE_HIDE));
+ }
+ break;
+ case ATTR_PAGE_PAPERBIN:
+ {
+ sal_uInt8 nTray = PAPERBIN_PRINTER_SETTINGS;
+ bool bFound = false;
+
+ OUString aName;
+ if (*pValue >>= aName)
+ {
+ if (aName == SC_PAPERBIN_DEFAULTNAME)
+ bFound = true;
+ else
+ {
+ Printer* pPrinter = pDocShell->GetPrinter();
+ if (pPrinter)
+ {
+ const sal_uInt16 nCount = pPrinter->GetPaperBinCount();
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ if (aName == pPrinter->GetPaperBinName(i))
+ {
+ nTray = static_cast<sal_uInt8>(i);
+ bFound = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!bFound)
+ throw lang::IllegalArgumentException();
+
+ rSet.Put(SvxPaperBinItem(ATTR_PAGE_PAPERBIN, nTray));
+
+ }
+ break;
+ case ATTR_PAGE_SCALETO:
+ {
+ sal_Int16 nPages = 0;
+ if (*pValue >>= nPages)
+ {
+ ScPageScaleToItem aItem = rSet.Get(ATTR_PAGE_SCALETO);
+ if (rPropertyName == SC_UNO_PAGE_SCALETOX)
+ aItem.SetWidth(static_cast<sal_uInt16>(nPages));
+ else
+ aItem.SetHeight(static_cast<sal_uInt16>(nPages));
+ rSet.Put(aItem);
+ rSet.ClearItem(ATTR_PAGE_SCALETOPAGES);
+ rSet.ClearItem(ATTR_PAGE_SCALE);
+ }
+ }
+ break;
+ case ATTR_HIDDEN:
+ {
+ bool bHidden = false;
+ if (*pValue >>= bHidden)
+ pStyle->SetHidden(bHidden);
+ }
+ break;
+ default:
+ // default items with wrong Slot-ID are not working in SfxItemPropertySet3
+ //! change Slot-IDs...
+ if (rSet.GetPool()->GetSlotId(pEntry->nWID) == pEntry->nWID
+ && rSet.GetItemState(pEntry->nWID, false) == SfxItemState::DEFAULT)
+ {
+ rSet.Put(rSet.Get(pEntry->nWID));
+ }
+ pPropSet->setPropertyValue(*pEntry, *pValue, rSet);
+ }
+ }
+ else
+ {
+ rSet.ClearItem(pEntry->nWID);
+ // language for number formats
+ if (pEntry->nWID == ATTR_VALUE_FORMAT)
+ rSet.ClearItem(ATTR_LANGUAGE_FORMAT);
+
+ //! for ATTR_ROTATE_VALUE, also reset ATTR_ORIENTATION?
+ }
+ }
+ else if (IsScUnoWid(pEntry->nWID))
+ {
+ switch (pEntry->nWID)
+ {
+ case SC_WID_UNO_TBLBORD:
+ {
+ if (pValue)
+ {
+ table::TableBorder aBorder;
+ if (*pValue >>= aBorder)
+ {
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ ScHelperFunctions::FillBoxItems(aOuter, aInner, aBorder);
+ rSet.Put(aOuter);
+ }
+ }
+ else
+ {
+ rSet.ClearItem(ATTR_BORDER);
+ }
+ }
+ break;
+ case SC_WID_UNO_TBLBORD2:
+ {
+ if (pValue)
+ {
+ table::TableBorder2 aBorder2;
+ if (*pValue >>= aBorder2)
+ {
+ SvxBoxItem aOuter(ATTR_BORDER);
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ ScHelperFunctions::FillBoxItems(aOuter, aInner, aBorder2);
+ rSet.Put(aOuter);
+ }
+ }
+ else
+ {
+ rSet.ClearItem(ATTR_BORDER);
+ }
+ }
+ break;
+ }
+ }
+ else if (pEntry->nWID == OWN_ATTR_FILLBMP_MODE)
+ {
+ css::drawing::BitmapMode eMode;
+ if (!pValue)
+ {
+ rSet.ClearItem(XATTR_FILLBMP_STRETCH);
+ rSet.ClearItem(XATTR_FILLBMP_TILE);
+ }
+ else if (*pValue >>= eMode)
+ {
+ rSet.Put(XFillBmpStretchItem(eMode == css::drawing::BitmapMode_STRETCH));
+ rSet.Put(XFillBmpTileItem(eMode == css::drawing::BitmapMode_REPEAT));
+ }
+ }
+ else if(pEntry->nMemberId == MID_NAME &&
+ (pEntry->nWID == XATTR_FILLBITMAP || pEntry->nWID == XATTR_FILLGRADIENT ||
+ pEntry->nWID == XATTR_FILLHATCH || pEntry->nWID == XATTR_FILLFLOATTRANSPARENCE ||
+ pEntry->nWID == XATTR_LINESTART || pEntry->nWID == XATTR_LINEEND || pEntry->nWID == XATTR_LINEDASH))
+ {
+ OUString aTempName;
+ if (*pValue >>= aTempName)
+ SvxShape::SetFillAttribute(pEntry->nWID, aTempName, rSet);
+ }
+ else if(pEntry->nWID == SDRATTR_TEXTDIRECTION)
+ {
+ return; // not yet implemented for styles
+ }
+ else if(!SvxUnoTextRangeBase::SetPropertyValueHelper(pEntry, *pValue, rSet))
+ {
+ SvxItemPropertySet_setPropertyValue(pEntry, *pValue, rSet);
+ }
+ }
+
+ //! DocFunc-??
+ //! Undo ??
+
+ if ( eFamily == SfxStyleFamily::Para )
+ {
+ // If we are loading, we can delay line height calculation, because we are going to re-calc all of those
+ // after load.
+ if (pDocShell && !pDocShell->IsLoading())
+ {
+ // update line height
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ Point aLogic = pVDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ double nPPTX = aLogic.X() / 1000.0;
+ double nPPTY = aLogic.Y() / 1000.0;
+ Fraction aZoom(1,1);
+ ScDocument& rDoc = pDocShell->GetDocument();
+ rDoc.StyleSheetChanged( pStyle, false, pVDev, nPPTX, nPPTY, aZoom, aZoom );
+
+ if (!rDoc.IsImportingXML())
+ {
+ pDocShell->PostPaint( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ pDocShell->SetDocumentModified();
+ }
+ }
+ }
+ else if ( eFamily == SfxStyleFamily::Page )
+ {
+ //! ModifyStyleSheet on document (save old values)
+
+ pDocShell->PageStyleModified( aStyleName, true );
+ }
+ else
+ static_cast<SfxStyleSheet*>(GetStyle_Impl())->Broadcast(SfxHint(SfxHintId::DataChanged));
+}
+
+uno::Any ScStyleObj::getPropertyValue_Impl( std::u16string_view aPropertyName )
+{
+ uno::Any aAny;
+ SfxStyleSheetBase* pStyle = GetStyle_Impl( true );
+
+ if ( aPropertyName == SC_UNONAME_DISPNAME ) // read-only
+ {
+ // core always has the display name
+ if ( pStyle )
+ aAny <<= pStyle->GetName();
+ }
+ else
+ {
+ const SfxItemPropertyMapEntry* pResultEntry = nullptr;
+ const SfxItemSet* pItemSet = GetStyleItemSet_Impl( aPropertyName, pResultEntry );
+
+ if ( pItemSet && pResultEntry )
+ {
+ sal_uInt16 nWhich = pResultEntry->nWID;
+
+ if ( IsScItemWid( nWhich ) )
+ {
+ switch ( nWhich ) // for special item handling
+ {
+ case ATTR_VALUE_FORMAT:
+ if ( pDocShell )
+ {
+ sal_uInt32 nOldFormat =
+ pItemSet->Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType eOldLang =
+ pItemSet->Get( ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ nOldFormat = pDocShell->GetDocument().GetFormatTable()->
+ GetFormatForLanguageIfBuiltIn( nOldFormat, eOldLang );
+ aAny <<= nOldFormat;
+ }
+ break;
+ case ATTR_INDENT:
+ aAny <<= sal_Int16( convertTwipToMm100(static_cast<const ScIndentItem&>(
+ pItemSet->Get(nWhich)).GetValue()) );
+ break;
+ case ATTR_STACKED:
+ {
+ Degree100 nRot = pItemSet->Get(ATTR_ROTATE_VALUE).GetValue();
+ bool bStacked = static_cast<const ScVerticalStackCell&>(pItemSet->Get(nWhich)).GetValue();
+ SvxOrientationItem( nRot, bStacked, TypedWhichId<SvxOrientationItem>(0) ).QueryValue( aAny );
+ }
+ break;
+ case ATTR_PAGE_SCALE:
+ case ATTR_PAGE_SCALETOPAGES:
+ case ATTR_PAGE_FIRSTPAGENO:
+ aAny <<= sal_Int16( static_cast<const SfxUInt16Item&>(pItemSet->Get(nWhich)).GetValue() );
+ break;
+ case ATTR_PAGE_CHARTS:
+ case ATTR_PAGE_OBJECTS:
+ case ATTR_PAGE_DRAWINGS:
+ //! define sal_Bool-MID for ScViewObjectModeItem?
+ aAny <<= static_cast<const ScViewObjectModeItem&>(pItemSet->Get(nWhich)).GetValue() == VOBJ_MODE_SHOW;
+ break;
+ case ATTR_PAGE_PAPERBIN:
+ {
+ // property PrinterPaperTray is the name of the tray
+
+ sal_uInt8 nValue = static_cast<const SvxPaperBinItem&>(pItemSet->Get(nWhich)).GetValue();
+ OUString aName;
+ if ( nValue == PAPERBIN_PRINTER_SETTINGS )
+ aName = SC_PAPERBIN_DEFAULTNAME;
+ else
+ {
+ Printer* pPrinter = pDocShell->GetPrinter();
+ if (pPrinter)
+ aName = pPrinter->GetPaperBinName( nValue );
+ }
+ aAny <<= aName;
+ }
+ break;
+ case ATTR_PAGE_SCALETO:
+ {
+ const ScPageScaleToItem& aItem(pItemSet->Get(ATTR_PAGE_SCALETO));
+ if ( aPropertyName == SC_UNO_PAGE_SCALETOX )
+ aAny <<= static_cast<sal_Int16>(aItem.GetWidth());
+ else
+ aAny <<= static_cast<sal_Int16>(aItem.GetHeight());
+ }
+ break;
+ case ATTR_HIDDEN:
+ {
+ bool bHidden = pStyle && pStyle->IsHidden();
+ aAny <<= bHidden;
+ }
+ break;
+ default:
+ // Default-Items with wrong Slot-ID don't work in SfxItemPropertySet3
+ //! change Slot-IDs...
+ if ( pItemSet->GetPool()->GetSlotId(nWhich) == nWhich &&
+ pItemSet->GetItemState(nWhich, false) == SfxItemState::DEFAULT )
+ {
+ SfxItemSet aNoEmptySet( *pItemSet );
+ aNoEmptySet.Put( aNoEmptySet.Get( nWhich ) );
+ pPropSet->getPropertyValue( *pResultEntry, aNoEmptySet, aAny );
+ }
+ else
+ pPropSet->getPropertyValue( *pResultEntry, *pItemSet, aAny );
+ }
+ }
+ else if ( IsScUnoWid( nWhich ) )
+ {
+ switch ( nWhich )
+ {
+ case SC_WID_UNO_TBLBORD:
+ case SC_WID_UNO_TBLBORD2:
+ {
+ const SfxPoolItem& rItem = pItemSet->Get(ATTR_BORDER);
+ SvxBoxItem aOuter(static_cast<const SvxBoxItem&>(rItem));
+ SvxBoxInfoItem aInner(ATTR_BORDER_INNER);
+ if (nWhich == SC_WID_UNO_TBLBORD2)
+ ScHelperFunctions::AssignTableBorder2ToAny(aAny, aOuter, aInner,
+ true);
+ else
+ ScHelperFunctions::AssignTableBorderToAny(aAny, aOuter, aInner,
+ true);
+ }
+ break;
+ }
+ }
+ else if ( nWhich == SDRATTR_TEXTDIRECTION )
+ {
+ aAny <<= false;
+ }
+ else if ( nWhich == OWN_ATTR_FILLBMP_MODE )
+ {
+ const XFillBmpStretchItem* pStretchItem = pItemSet->GetItem<XFillBmpStretchItem>(XATTR_FILLBMP_STRETCH);
+ const XFillBmpTileItem* pTileItem = pItemSet->GetItem<XFillBmpTileItem>(XATTR_FILLBMP_TILE);
+
+ if ( pStretchItem && pTileItem )
+ {
+ if ( pTileItem->GetValue() )
+ aAny <<= css::drawing::BitmapMode_REPEAT;
+ else if ( pStretchItem->GetValue() )
+ aAny <<= css::drawing::BitmapMode_STRETCH;
+ else
+ aAny <<= css::drawing::BitmapMode_NO_REPEAT;
+ }
+ }
+ else if ( nWhich != OWN_ATTR_TEXTCOLUMNS )
+ {
+ if (!SvxUnoTextRangeBase::GetPropertyValueHelper(*pItemSet, pResultEntry, aAny))
+ aAny = SvxItemPropertySet_getPropertyValue(pResultEntry, *pItemSet);
+
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ if (pResultEntry->aType == ::cppu::UnoType<sal_Int16>::get() &&
+ aAny.getValueType() == ::cppu::UnoType<sal_Int32>::get())
+ {
+ aAny <<= static_cast<sal_Int16>(aAny.get<sal_Int32>());
+ }
+ }
+ }
+ }
+
+ return aAny;
+}
+
+uno::Any SAL_CALL ScStyleObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ GetStyle_Impl();
+
+ return getPropertyValue_Impl( aPropertyName );
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScStyleObj )
+
+// lang::XServiceInfo
+
+OUString SAL_CALL ScStyleObj::getImplementationName()
+{
+ return "ScStyleObj";
+}
+
+sal_Bool SAL_CALL ScStyleObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScStyleObj::getSupportedServiceNames()
+{
+ if (eFamily == SfxStyleFamily::Page)
+ return {SCSTYLE_SERVICE, SCPAGESTYLE_SERVICE};
+
+ if (eFamily == SfxStyleFamily::Frame)
+ return {SCSTYLE_SERVICE, SCGRAPHICSTYLE_SERVICE};
+
+ return {SCSTYLE_SERVICE, SCCELLSTYLE_SERVICE};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/targuno.cxx b/sc/source/ui/unoobj/targuno.cxx
new file mode 100644
index 0000000000..635c918f36
--- /dev/null
+++ b/sc/source/ui/unoobj/targuno.cxx
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/hint.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <targuno.hxx>
+#include <miscuno.hxx>
+#include <docuno.hxx>
+#include <datauno.hxx>
+#include <nameuno.hxx>
+#include <docsh.hxx>
+#include <content.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <unonames.hxx>
+
+using namespace ::com::sun::star;
+
+const TranslateId aTypeResIds[SC_LINKTARGETTYPE_COUNT] =
+{
+ SCSTR_CONTENT_TABLE, // SC_LINKTARGETTYPE_SHEET
+ SCSTR_CONTENT_RANGENAME, // SC_LINKTARGETTYPE_RANGENAME
+ SCSTR_CONTENT_DBAREA // SC_LINKTARGETTYPE_DBAREA
+};
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetLinkTargetMap()
+{
+ static const SfxItemPropertyMapEntry aLinkTargetMap_Impl[] =
+ {
+ { SC_UNO_LINKDISPBIT, 0, cppu::UnoType<awt::XBitmap>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { SC_UNO_LINKDISPNAME, 0, cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ };
+ return aLinkTargetMap_Impl;
+}
+
+// service for ScLinkTargetTypeObj is not defined
+// must not support document::LinkTarget because the target type cannot be used as a target
+
+SC_SIMPLE_SERVICE_INFO( ScLinkTargetTypesObj, "ScLinkTargetTypesObj", "com.sun.star.document.LinkTargets" )
+SC_SIMPLE_SERVICE_INFO( ScLinkTargetTypeObj, "ScLinkTargetTypeObj", "com.sun.star.document.LinkTargetSupplier" )
+SC_SIMPLE_SERVICE_INFO( ScLinkTargetsObj, "ScLinkTargetsObj", "com.sun.star.document.LinkTargets" )
+
+ScLinkTargetTypesObj::ScLinkTargetTypesObj(ScDocShell* pDocSh) :
+ pDocShell( pDocSh )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+
+ for (sal_uInt16 i=0; i<SC_LINKTARGETTYPE_COUNT; i++)
+ aNames[i] = ScResId(aTypeResIds[i]);
+}
+
+ScLinkTargetTypesObj::~ScLinkTargetTypesObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScLinkTargetTypesObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // document gone
+}
+
+// container::XNameAccess
+
+uno::Any SAL_CALL ScLinkTargetTypesObj::getByName(const OUString& aName)
+{
+ if (pDocShell)
+ {
+ for (sal_uInt16 i=0; i<SC_LINKTARGETTYPE_COUNT; i++)
+ if ( aNames[i] == aName )
+ return uno::Any(uno::Reference< beans::XPropertySet >(new ScLinkTargetTypeObj( pDocShell, i )));
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScLinkTargetTypesObj::getElementNames()
+{
+ uno::Sequence<OUString> aRet(SC_LINKTARGETTYPE_COUNT);
+ OUString* pArray = aRet.getArray();
+ for (sal_uInt16 i=0; i<SC_LINKTARGETTYPE_COUNT; i++)
+ pArray[i] = aNames[i];
+ return aRet;
+}
+
+sal_Bool SAL_CALL ScLinkTargetTypesObj::hasByName(const OUString& aName)
+{
+ for (const auto & i : aNames)
+ if ( i == aName )
+ return true;
+ return false;
+}
+
+// container::XElementAccess
+
+uno::Type SAL_CALL ScLinkTargetTypesObj::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL ScLinkTargetTypesObj::hasElements()
+{
+ return true;
+}
+
+ScLinkTargetTypeObj::ScLinkTargetTypeObj(ScDocShell* pDocSh, sal_uInt16 nT) :
+ pDocShell( pDocSh ),
+ nType( nT )
+{
+ pDocShell->GetDocument().AddUnoObject(*this);
+ aName = ScResId(aTypeResIds[nType]); //! on demand?
+}
+
+ScLinkTargetTypeObj::~ScLinkTargetTypeObj()
+{
+ SolarMutexGuard g;
+
+ if (pDocShell)
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScLinkTargetTypeObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pDocShell = nullptr; // document gone
+}
+
+// document::XLinkTargetSupplier
+
+uno::Reference< container::XNameAccess > SAL_CALL ScLinkTargetTypeObj::getLinks()
+{
+ uno::Reference< container::XNameAccess > xCollection;
+
+ if ( pDocShell )
+ {
+ switch ( nType )
+ {
+ case SC_LINKTARGETTYPE_SHEET:
+ xCollection.set(new ScTableSheetsObj(pDocShell));
+ break;
+ case SC_LINKTARGETTYPE_RANGENAME:
+ xCollection.set(new ScGlobalNamedRangesObj(pDocShell));
+ break;
+ case SC_LINKTARGETTYPE_DBAREA:
+ xCollection.set(new ScDatabaseRangesObj(pDocShell));
+ break;
+ default:
+ OSL_FAIL("invalid type");
+ }
+ }
+
+ // wrap collection in ScLinkTargetsObj because service document::LinkTargets requires
+ // beans::XPropertySet as ElementType in container::XNameAccess.
+ if ( xCollection.is() )
+ return new ScLinkTargetsObj( xCollection );
+ return nullptr;
+}
+
+// beans::XPropertySet
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL ScLinkTargetTypeObj::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetLinkTargetMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScLinkTargetTypeObj::setPropertyValue(const OUString& /* aPropertyName */,
+ const uno::Any& /* aValue */)
+{
+ // everything is read-only
+ //! exception?
+}
+
+constexpr OUString aContentBmps[]=
+{
+ RID_BMP_CONTENT_TABLE,
+ RID_BMP_CONTENT_RANGENAME,
+ RID_BMP_CONTENT_DBAREA,
+ RID_BMP_CONTENT_GRAPHIC,
+ RID_BMP_CONTENT_OLEOBJECT,
+ RID_BMP_CONTENT_NOTE,
+ RID_BMP_CONTENT_AREALINK,
+ RID_BMP_CONTENT_DRAWING
+};
+
+void ScLinkTargetTypeObj::SetLinkTargetBitmap( uno::Any& rRet, sal_uInt16 nType )
+{
+ ScContentId nImgId = ScContentId::ROOT;
+ switch ( nType )
+ {
+ case SC_LINKTARGETTYPE_SHEET:
+ nImgId = ScContentId::TABLE;
+ break;
+ case SC_LINKTARGETTYPE_RANGENAME:
+ nImgId = ScContentId::RANGENAME;
+ break;
+ case SC_LINKTARGETTYPE_DBAREA:
+ nImgId = ScContentId::DBAREA;
+ break;
+ }
+ if (nImgId != ScContentId::ROOT)
+ {
+ BitmapEx aBitmapEx { aContentBmps[static_cast<int>(nImgId) -1 ] };
+ rRet <<= VCLUnoHelper::CreateBitmap(aBitmapEx);
+ }
+}
+
+uno::Any SAL_CALL ScLinkTargetTypeObj::getPropertyValue(const OUString& PropertyName)
+{
+ uno::Any aRet;
+ if ( PropertyName == SC_UNO_LINKDISPBIT )
+ SetLinkTargetBitmap( aRet, nType );
+ else if ( PropertyName == SC_UNO_LINKDISPNAME )
+ aRet <<= aName;
+
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScLinkTargetTypeObj )
+
+ScLinkTargetsObj::ScLinkTargetsObj( uno::Reference< container::XNameAccess > xColl ) :
+ xCollection(std::move( xColl ))
+{
+ OSL_ENSURE( xCollection.is(), "ScLinkTargetsObj: NULL" );
+}
+
+ScLinkTargetsObj::~ScLinkTargetsObj()
+{
+}
+
+// container::XNameAccess
+
+uno::Any SAL_CALL ScLinkTargetsObj::getByName(const OUString& aName)
+{
+ uno::Reference<beans::XPropertySet> xProp(xCollection->getByName(aName), uno::UNO_QUERY);
+ if (xProp.is())
+ return uno::Any(xProp);
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence<OUString> SAL_CALL ScLinkTargetsObj::getElementNames()
+{
+ return xCollection->getElementNames();
+}
+
+sal_Bool SAL_CALL ScLinkTargetsObj::hasByName(const OUString& aName)
+{
+ return xCollection->hasByName(aName);
+}
+
+// container::XElementAccess
+
+uno::Type SAL_CALL ScLinkTargetsObj::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL ScLinkTargetsObj::hasElements()
+{
+ return xCollection->hasElements();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/textuno.cxx b/sc/source/ui/unoobj/textuno.cxx
new file mode 100644
index 0000000000..6102cdb9fa
--- /dev/null
+++ b/sc/source/ui/unoobj/textuno.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdobj.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/unofored.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <editeng/unoipset.hxx>
+#include <textuno.hxx>
+#include <fielduno.hxx>
+#include <editsrc.hxx>
+#include <docsh.hxx>
+#include <editutil.hxx>
+#include <miscuno.hxx>
+#include <cellsuno.hxx>
+#include <cellvalue.hxx>
+#include <cellform.hxx>
+#include <patattr.hxx>
+#include <docfunc.hxx>
+#include <scmod.hxx>
+
+using namespace com::sun::star;
+
+static const SvxItemPropertySet * lcl_GetHdFtPropertySet()
+{
+ static const SvxItemPropertySet aHdFtPropertySet_Impl = [] {
+ static SfxItemPropertyMapEntry aHdFtPropertyMap_Impl[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ SVX_UNOEDIT_NUMBERING_PROPERTY, // for completeness of service ParagraphProperties
+ };
+
+ // modify PropertyMap to include CONVERT_TWIPS flag for font height
+ // (headers/footers are in twips)
+
+ for (auto & rEntry : aHdFtPropertyMap_Impl)
+ {
+ if ( ( rEntry.nWID == EE_CHAR_FONTHEIGHT ||
+ rEntry.nWID == EE_CHAR_FONTHEIGHT_CJK ||
+ rEntry.nWID == EE_CHAR_FONTHEIGHT_CTL ) &&
+ rEntry.nMemberId == MID_FONTHEIGHT )
+ {
+ rEntry.nMemberId |= CONVERT_TWIPS;
+ }
+ }
+
+ return SvxItemPropertySet(aHdFtPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool());
+ }();
+ return &aHdFtPropertySet_Impl;
+}
+
+SC_SIMPLE_SERVICE_INFO( ScHeaderFooterContentObj, "ScHeaderFooterContentObj", "com.sun.star.sheet.HeaderFooterContent" )
+SC_SIMPLE_SERVICE_INFO( ScHeaderFooterTextObj, "ScHeaderFooterTextObj", "stardiv.one.Text.Text" )
+
+ScHeaderFooterContentObj::ScHeaderFooterContentObj()
+{
+}
+
+ScHeaderFooterContentObj::~ScHeaderFooterContentObj() {}
+
+const EditTextObject* ScHeaderFooterContentObj::GetLeftEditObject() const
+{
+ return mxLeftText->GetTextObject();
+}
+
+const EditTextObject* ScHeaderFooterContentObj::GetCenterEditObject() const
+{
+ return mxCenterText->GetTextObject();
+}
+
+const EditTextObject* ScHeaderFooterContentObj::GetRightEditObject() const
+{
+ return mxRightText->GetTextObject();
+}
+
+// XHeaderFooterContent
+
+uno::Reference<text::XText> SAL_CALL ScHeaderFooterContentObj::getLeftText()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<text::XText> xInt(*mxLeftText, uno::UNO_QUERY);
+ return xInt;
+}
+
+uno::Reference<text::XText> SAL_CALL ScHeaderFooterContentObj::getCenterText()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<text::XText> xInt(*mxCenterText, uno::UNO_QUERY);
+ return xInt;
+}
+
+uno::Reference<text::XText> SAL_CALL ScHeaderFooterContentObj::getRightText()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<text::XText> xInt(*mxRightText, uno::UNO_QUERY);
+ return xInt;
+}
+
+rtl::Reference<ScHeaderFooterContentObj> ScHeaderFooterContentObj::getImplementation(
+ const uno::Reference<sheet::XHeaderFooterContent>& rObj)
+{
+ return dynamic_cast<ScHeaderFooterContentObj*>(rObj.get());
+}
+
+void ScHeaderFooterContentObj::Init( const EditTextObject* pLeft,
+ const EditTextObject* pCenter,
+ const EditTextObject* pRight )
+{
+ uno::Reference<css::sheet::XHeaderFooterContent> xThis(this);
+ mxLeftText = rtl::Reference<ScHeaderFooterTextObj>(new ScHeaderFooterTextObj(xThis, ScHeaderFooterPart::LEFT, pLeft));
+ mxCenterText = rtl::Reference<ScHeaderFooterTextObj>(new ScHeaderFooterTextObj(xThis, ScHeaderFooterPart::CENTER, pCenter));
+ mxRightText = rtl::Reference<ScHeaderFooterTextObj>(new ScHeaderFooterTextObj(xThis, ScHeaderFooterPart::RIGHT, pRight));
+}
+
+ScHeaderFooterTextData::ScHeaderFooterTextData(
+ uno::WeakReference<sheet::XHeaderFooterContent> xContent, ScHeaderFooterPart nP, const EditTextObject* pTextObj) :
+ mpTextObj(pTextObj ? pTextObj->Clone() : nullptr),
+ xContentObj(std::move( xContent )),
+ nPart( nP ),
+ bDataValid(false)
+{
+}
+
+ScHeaderFooterTextData::~ScHeaderFooterTextData()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ pForwarder.reset();
+ pEditEngine.reset();
+}
+
+SvxTextForwarder* ScHeaderFooterTextData::GetTextForwarder()
+{
+ if (!pEditEngine)
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ std::unique_ptr<ScHeaderEditEngine> pHdrEngine(new ScHeaderEditEngine( pEnginePool.get() ));
+
+ pHdrEngine->EnableUndo( false );
+ pHdrEngine->SetRefMapMode(MapMode(MapUnit::MapTwip));
+
+ // default font must be set, independently of document
+ // -> use global pool from module
+
+ SfxItemSet aDefaults( pHdrEngine->GetEmptyItemSet() );
+ const ScPatternAttr& rPattern = SC_MOD()->GetPool().GetDefaultItem(ATTR_PATTERN);
+ rPattern.FillEditItemSet( &aDefaults );
+ // FillEditItemSet adjusts font height to 1/100th mm,
+ // but for header/footer twips is needed, as in the PatternAttr:
+ aDefaults.Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ aDefaults.Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) ) ;
+ aDefaults.Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ pHdrEngine->SetDefaults( aDefaults );
+
+ ScHeaderFieldData aData;
+ ScHeaderFooterTextObj::FillDummyFieldData( aData );
+ pHdrEngine->SetData( aData );
+
+ pEditEngine = std::move(pHdrEngine);
+ pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) );
+ }
+
+ if (bDataValid)
+ return pForwarder.get();
+
+ if (mpTextObj)
+ pEditEngine->SetTextCurrentDefaults(*mpTextObj);
+
+ bDataValid = true;
+ return pForwarder.get();
+}
+
+void ScHeaderFooterTextData::UpdateData()
+{
+ if (pEditEngine)
+ {
+ mpTextObj = pEditEngine->CreateTextObject();
+ }
+}
+
+void ScHeaderFooterTextData::UpdateData(EditEngine& rEditEngine)
+{
+ mpTextObj = rEditEngine.CreateTextObject();
+ bDataValid = false;
+}
+
+ScHeaderFooterTextObj::ScHeaderFooterTextObj(
+ const uno::WeakReference<sheet::XHeaderFooterContent>& xContent, ScHeaderFooterPart nP, const EditTextObject* pTextObj) :
+ aTextData(xContent, nP, pTextObj)
+{
+ // ScHeaderFooterTextData acquires rContent
+ // pUnoText is created on demand (getString/setString work without it)
+}
+
+void ScHeaderFooterTextObj::CreateUnoText_Impl()
+{
+ if (!mxUnoText.is())
+ {
+ // can't be aggregated because getString/setString is handled here
+ ScHeaderFooterEditSource aEditSrc(aTextData);
+ mxUnoText.set(new SvxUnoText(&aEditSrc, lcl_GetHdFtPropertySet(), uno::Reference<text::XText>()));
+ }
+}
+
+ScHeaderFooterTextObj::~ScHeaderFooterTextObj() {}
+
+const EditTextObject* ScHeaderFooterTextObj::GetTextObject() const
+{
+ return aTextData.GetTextObject();
+}
+
+const SvxUnoText& ScHeaderFooterTextObj::GetUnoText()
+{
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return *mxUnoText;
+}
+
+// XText
+
+uno::Reference<text::XTextCursor> SAL_CALL ScHeaderFooterTextObj::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ return new ScHeaderFooterTextCursor( this );
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL ScHeaderFooterTextObj::createTextCursorByRange(
+ const uno::Reference<text::XTextRange>& aTextPosition )
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->createTextCursorByRange(aTextPosition);
+ //! like ScCellObj::createTextCursorByRange, if SvxUnoTextRange_getReflection available
+}
+
+void ScHeaderFooterTextObj::FillDummyFieldData( ScHeaderFieldData& rData )
+{
+ OUString aDummy("???");
+ rData.aTitle = aDummy;
+ rData.aLongDocName = aDummy;
+ rData.aShortDocName = aDummy;
+ rData.aTabName = aDummy;
+ rData.nPageNo = 1;
+ rData.nTotalPages = 99;
+}
+
+OUString SAL_CALL ScHeaderFooterTextObj::getString()
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ const EditTextObject* pData;
+
+ uno::Reference<css::sheet::XHeaderFooterContent> xContentObj = aTextData.GetContentObj();
+ if (!xContentObj.is())
+ throw css::uno::RuntimeException(
+ "ScHeaderFooterTextObj::getString: no ContentObj");
+
+ rtl::Reference<ScHeaderFooterContentObj> pObj = ScHeaderFooterContentObj::getImplementation(xContentObj);
+
+ switch ( aTextData.GetPart() )
+ {
+ case ScHeaderFooterPart::LEFT:
+ pData = pObj->GetLeftEditObject();
+ break;
+ case ScHeaderFooterPart::CENTER:
+ pData = pObj->GetCenterEditObject();
+ break;
+ case ScHeaderFooterPart::RIGHT:
+ pData = pObj->GetRightEditObject();
+ break;
+ default:
+ SAL_WARN("sc.ui","unexpected enum value of ScHeaderFooterPart");
+ pData = nullptr;
+ }
+
+ if (pData)
+ {
+ // for pure text, no font info is needed in pool defaults
+ ScHeaderEditEngine aEditEngine( EditEngine::CreatePool().get() );
+
+ ScHeaderFieldData aData;
+ FillDummyFieldData( aData );
+ aEditEngine.SetData( aData );
+
+ aEditEngine.SetTextCurrentDefaults(*pData);
+ aRet = ScEditUtil::GetSpaceDelimitedString( aEditEngine );
+ }
+ return aRet;
+}
+
+void SAL_CALL ScHeaderFooterTextObj::setString( const OUString& aText )
+{
+ SolarMutexGuard aGuard;
+
+ // for pure text, no font info is needed in pool defaults
+ ScHeaderEditEngine aEditEngine(EditEngine::CreatePool().get());
+ aEditEngine.SetTextCurrentDefaults( aText );
+ aTextData.UpdateData(aEditEngine);
+}
+
+void SAL_CALL ScHeaderFooterTextObj::insertString( const uno::Reference<text::XTextRange>& xRange,
+ const OUString& aString, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ mxUnoText->insertString( xRange, aString, bAbsorb );
+}
+
+void SAL_CALL ScHeaderFooterTextObj::insertControlCharacter(
+ const uno::Reference<text::XTextRange>& xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ mxUnoText->insertControlCharacter( xRange, nControlCharacter, bAbsorb );
+}
+
+void SAL_CALL ScHeaderFooterTextObj::insertTextContent(
+ const uno::Reference<text::XTextRange >& xRange,
+ const uno::Reference<text::XTextContent >& xContent,
+ sal_Bool bAbsorb )
+{
+ SolarMutexGuard aGuard;
+ if ( xContent.is() && xRange.is() )
+ {
+ ScEditFieldObj* pHeaderField = dynamic_cast<ScEditFieldObj*>( xContent.get() );
+
+ SvxUnoTextRangeBase* pTextRange =
+ comphelper::getFromUnoTunnel<ScHeaderFooterTextCursor>( xRange );
+
+ if ( pHeaderField && !pHeaderField->IsInserted() && pTextRange )
+ {
+ SvxEditSource* pEditSource = pTextRange->GetEditSource();
+ ESelection aSelection(pTextRange->GetSelection());
+
+ if (!bAbsorb)
+ {
+ // don't replace -> append at end
+ aSelection.Adjust();
+ aSelection.nStartPara = aSelection.nEndPara;
+ aSelection.nStartPos = aSelection.nEndPos;
+ }
+
+ SvxFieldItem aItem(pHeaderField->CreateFieldItem());
+
+ SvxTextForwarder* pForwarder = pEditSource->GetTextForwarder();
+ pForwarder->QuickInsertField( aItem, aSelection );
+ pEditSource->UpdateData();
+
+ // new selection: a digit
+ aSelection.Adjust();
+ aSelection.nEndPara = aSelection.nStartPara;
+ aSelection.nEndPos = aSelection.nStartPos + 1;
+
+ uno::Reference<text::XTextRange> xTextRange;
+ switch ( aTextData.GetPart() )
+ {
+ case ScHeaderFooterPart::LEFT:
+ xTextRange = aTextData.GetContentObj()->getLeftText();
+ break;
+ case ScHeaderFooterPart::CENTER:
+ xTextRange = aTextData.GetContentObj()->getCenterText();
+ break;
+ case ScHeaderFooterPart::RIGHT:
+ xTextRange = aTextData.GetContentObj()->getRightText();
+ break;
+ }
+
+ pHeaderField->InitDoc(xTextRange, std::make_unique<ScHeaderFooterEditSource>(aTextData), aSelection);
+
+ // for bAbsorb=FALSE, the new selection must be behind the inserted content
+ // (the xml filter relies on this)
+ if (!bAbsorb)
+ aSelection.nStartPos = aSelection.nEndPos;
+
+ pTextRange->SetSelection( aSelection );
+
+ return;
+ }
+ }
+
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ mxUnoText->insertTextContent( xRange, xContent, bAbsorb );
+}
+
+void SAL_CALL ScHeaderFooterTextObj::removeTextContent(
+ const uno::Reference<text::XTextContent>& xContent )
+{
+ SolarMutexGuard aGuard;
+ if ( xContent.is() )
+ {
+ ScEditFieldObj* pHeaderField = dynamic_cast<ScEditFieldObj*>(xContent.get());
+ if ( pHeaderField && pHeaderField->IsInserted() )
+ {
+ //! check if the field is in this cell
+ pHeaderField->DeleteField();
+ return;
+ }
+ }
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ mxUnoText->removeTextContent( xContent );
+}
+
+uno::Reference<text::XText> SAL_CALL ScHeaderFooterTextObj::getText()
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->getText();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextObj::getStart()
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->getStart();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextObj::getEnd()
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->getEnd();
+}
+
+// XTextFieldsSupplier
+
+uno::Reference<container::XEnumerationAccess> SAL_CALL ScHeaderFooterTextObj::getTextFields()
+{
+ SolarMutexGuard aGuard;
+ // all fields
+ return new ScHeaderFieldsObj(aTextData);
+}
+
+uno::Reference<container::XNameAccess> SAL_CALL ScHeaderFooterTextObj::getTextFieldMasters()
+{
+ // this does not exists in Calc (?)
+ return nullptr;
+}
+
+// XTextRangeMover
+
+void SAL_CALL ScHeaderFooterTextObj::moveTextRange(
+ const uno::Reference<text::XTextRange>& xRange,
+ sal_Int16 nParagraphs )
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ mxUnoText->moveTextRange( xRange, nParagraphs );
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScHeaderFooterTextObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->createEnumeration();
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScHeaderFooterTextObj::getElementType()
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->getElementType();
+}
+
+sal_Bool SAL_CALL ScHeaderFooterTextObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if (!mxUnoText.is())
+ CreateUnoText_Impl();
+ return mxUnoText->hasElements();
+}
+
+ScCellTextCursor::ScCellTextCursor(ScCellObj& rText) :
+ SvxUnoTextCursor( rText.GetUnoText() ),
+ mxTextObj( &rText )
+{
+}
+
+ScCellTextCursor::~ScCellTextCursor() noexcept
+{
+}
+
+// SvxUnoTextCursor methods reimplemented here to return the right objects:
+
+uno::Reference<text::XText> SAL_CALL ScCellTextCursor::getText()
+{
+ return mxTextObj;
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScCellTextCursor::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ //! use other object for range than cursor?
+
+ rtl::Reference<ScCellTextCursor> pNew = new ScCellTextCursor( *this );
+
+ ESelection aNewSel(GetSelection());
+ aNewSel.nEndPara = aNewSel.nStartPara;
+ aNewSel.nEndPos = aNewSel.nStartPos;
+ pNew->SetSelection( aNewSel );
+
+ return static_cast<SvxUnoTextRangeBase*>(pNew.get());
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScCellTextCursor::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ //! use other object for range than cursor?
+
+ rtl::Reference<ScCellTextCursor> pNew = new ScCellTextCursor( *this );
+
+ ESelection aNewSel(GetSelection());
+ aNewSel.nStartPara = aNewSel.nEndPara;
+ aNewSel.nStartPos = aNewSel.nEndPos;
+ pNew->SetSelection( aNewSel );
+
+ return static_cast<SvxUnoTextRangeBase*>(pNew.get());
+}
+
+// XUnoTunnel
+
+UNO3_GETIMPLEMENTATION2_IMPL(ScCellTextCursor, SvxUnoTextCursor);
+
+ScHeaderFooterTextCursor::ScHeaderFooterTextCursor(rtl::Reference<ScHeaderFooterTextObj> const & rText) :
+ SvxUnoTextCursor( rText->GetUnoText() ),
+ rTextObj( rText )
+{}
+
+ScHeaderFooterTextCursor::~ScHeaderFooterTextCursor() noexcept {};
+
+// SvxUnoTextCursor methods reimplemented here to return the right objects:
+
+uno::Reference<text::XText> SAL_CALL ScHeaderFooterTextCursor::getText()
+{
+ SolarMutexGuard aGuard;
+ return rTextObj;
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextCursor::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ //! use other object for range than cursor?
+
+ rtl::Reference<ScHeaderFooterTextCursor> pNew = new ScHeaderFooterTextCursor( *this );
+
+ ESelection aNewSel(GetSelection());
+ aNewSel.nEndPara = aNewSel.nStartPara;
+ aNewSel.nEndPos = aNewSel.nStartPos;
+ pNew->SetSelection( aNewSel );
+
+ return static_cast<SvxUnoTextRangeBase*>(pNew.get());
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScHeaderFooterTextCursor::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ //! use other object for range than cursor?
+
+ rtl::Reference<ScHeaderFooterTextCursor> pNew = new ScHeaderFooterTextCursor( *this );
+
+ ESelection aNewSel(GetSelection());
+ aNewSel.nStartPara = aNewSel.nEndPara;
+ aNewSel.nStartPos = aNewSel.nEndPos;
+ pNew->SetSelection( aNewSel );
+
+ return static_cast<SvxUnoTextRangeBase*>(pNew.get());
+}
+
+// XUnoTunnel
+
+UNO3_GETIMPLEMENTATION2_IMPL(ScHeaderFooterTextCursor, SvxUnoTextCursor);
+
+ScDrawTextCursor::ScDrawTextCursor( uno::Reference<text::XText> xParent,
+ const SvxUnoTextBase& rText ) :
+ SvxUnoTextCursor( rText ),
+ xParentText(std::move( xParent ))
+
+{
+}
+
+ScDrawTextCursor::~ScDrawTextCursor() noexcept
+{
+}
+
+// SvxUnoTextCursor methods reimplemented here to return the right objects:
+
+uno::Reference<text::XText> SAL_CALL ScDrawTextCursor::getText()
+{
+ SolarMutexGuard aGuard;
+ return xParentText;
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScDrawTextCursor::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ //! use other object for range than cursor?
+
+ rtl::Reference<ScDrawTextCursor> pNew = new ScDrawTextCursor( *this );
+
+ ESelection aNewSel(GetSelection());
+ aNewSel.nEndPara = aNewSel.nStartPara;
+ aNewSel.nEndPos = aNewSel.nStartPos;
+ pNew->SetSelection( aNewSel );
+
+ return static_cast<SvxUnoTextRangeBase*>(pNew.get());
+}
+
+uno::Reference<text::XTextRange> SAL_CALL ScDrawTextCursor::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ //! use other object for range than cursor?
+
+ rtl::Reference<ScDrawTextCursor> pNew = new ScDrawTextCursor( *this );
+
+ ESelection aNewSel(GetSelection());
+ aNewSel.nStartPara = aNewSel.nEndPara;
+ aNewSel.nStartPos = aNewSel.nEndPos;
+ pNew->SetSelection( aNewSel );
+
+ return static_cast<SvxUnoTextRangeBase*>(pNew.get());
+}
+
+// XUnoTunnel
+
+UNO3_GETIMPLEMENTATION2_IMPL(ScDrawTextCursor, SvxUnoTextCursor);
+
+ScSimpleEditSourceHelper::ScSimpleEditSourceHelper()
+{
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->SetDefaultMetric( MapUnit::Map100thMM );
+ pEnginePool->FreezeIdRanges();
+
+ pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) ); // TRUE: become owner of pool
+ pForwarder.reset( new SvxEditEngineForwarder( *pEditEngine ) );
+ pOriginalSource.reset( new ScSimpleEditSource( pForwarder.get() ) );
+}
+
+ScSimpleEditSourceHelper::~ScSimpleEditSourceHelper()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ pOriginalSource.reset();
+ pForwarder.reset();
+ pEditEngine.reset();
+}
+
+ScEditEngineTextObj::ScEditEngineTextObj() :
+ SvxUnoText( GetOriginalSource(), ScCellObj::GetEditPropertySet(), uno::Reference<text::XText>() )
+{
+}
+
+ScEditEngineTextObj::~ScEditEngineTextObj() noexcept
+{
+}
+
+void ScEditEngineTextObj::SetText( const EditTextObject& rTextObject )
+{
+ GetEditEngine()->SetTextCurrentDefaults( rTextObject );
+
+ ESelection aSel;
+ ::GetSelection( aSel, GetEditSource()->GetTextForwarder() );
+ SetSelection( aSel );
+}
+
+std::unique_ptr<EditTextObject> ScEditEngineTextObj::CreateTextObject()
+{
+ return GetEditEngine()->CreateTextObject();
+}
+
+ScCellTextData::ScCellTextData(ScDocShell* pDocSh, const ScAddress& rP) :
+ pDocShell( pDocSh ),
+ aCellPos( rP ),
+ bDataValid( false ),
+ bInUpdate( false ),
+ bDirty( false ),
+ bDoUpdate( true )
+{
+ if (pDocShell)
+ pDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScCellTextData::~ScCellTextData()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ if (pDocShell)
+ {
+ pDocShell->GetDocument().RemoveUnoObject(*this);
+ pDocShell->GetDocument().DisposeFieldEditEngine(pEditEngine);
+ }
+ else
+ pEditEngine.reset();
+
+ pForwarder.reset();
+
+ pOriginalSource.reset();
+}
+
+ScCellEditSource* ScCellTextData::GetOriginalSource()
+{
+ if (!pOriginalSource)
+ pOriginalSource.reset( new ScCellEditSource(pDocShell, aCellPos) );
+ return pOriginalSource.get();
+}
+
+SvxTextForwarder* ScCellTextData::GetTextForwarder()
+{
+ if (!pEditEngine)
+ {
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pEditEngine = rDoc.CreateFieldEditEngine();
+ }
+ else
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) );
+ }
+ // currently, GetPortions doesn't work if UpdateMode is sal_False,
+ // this will be fixed (in EditEngine) by src600
+// pEditEngine->SetUpdateMode( sal_False );
+ pEditEngine->EnableUndo( false );
+ if (pDocShell)
+ pEditEngine->SetRefDevice(pDocShell->GetRefDevice());
+ else
+ pEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) );
+ }
+
+ if (bDataValid)
+ return pForwarder.get();
+
+ if (pDocShell)
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ SfxItemSet aDefaults( pEditEngine->GetEmptyItemSet() );
+ if( const ScPatternAttr* pPattern =
+ rDoc.GetPattern( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab() ) )
+ {
+ pPattern->FillEditItemSet( &aDefaults );
+ pPattern->FillEditParaItems( &aDefaults ); // including alignment etc. (for reading)
+ }
+
+ ScRefCellValue aCell(rDoc, aCellPos);
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ const EditTextObject* pObj = aCell.getEditText();
+ pEditEngine->SetTextNewDefaults(*pObj, aDefaults);
+ }
+ else
+ {
+ sal_uInt32 nFormat = rDoc.GetNumberFormat(aCellPos);
+ OUString aText = ScCellFormat::GetInputString(aCell, nFormat, *rDoc.GetFormatTable(), rDoc);
+ if (!aText.isEmpty())
+ pEditEngine->SetTextNewDefaults(aText, aDefaults);
+ else
+ pEditEngine->SetDefaults(aDefaults);
+ }
+ }
+
+ bDataValid = true;
+ return pForwarder.get();
+}
+
+void ScCellTextData::UpdateData()
+{
+ if ( bDoUpdate )
+ {
+ OSL_ENSURE(pEditEngine != nullptr, "no EditEngine for UpdateData()");
+ if ( pDocShell && pEditEngine )
+ {
+ // during the own UpdateData call, bDataValid must not be reset,
+ // or things like attributes after the text would be lost
+ // (are not stored in the cell)
+ bInUpdate = true; // prevents bDataValid from being reset
+ pDocShell->GetDocFunc().PutData(aCellPos, *pEditEngine, true); // always as text
+
+ bInUpdate = false;
+ bDirty = false;
+ }
+ }
+ else
+ bDirty = true;
+}
+
+void ScCellTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::Dying )
+ {
+ pDocShell = nullptr; // invalid now
+
+ pForwarder.reset();
+ pEditEngine.reset(); // EditEngine uses document's pool
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ if (!bInUpdate) // not for own UpdateData calls
+ bDataValid = false; // text has to be read from the cell again
+ }
+}
+
+ScCellTextObj::ScCellTextObj(ScDocShell* pDocSh, const ScAddress& rP) :
+ ScCellTextData( pDocSh, rP ),
+ SvxUnoText( GetOriginalSource(), ScCellObj::GetEditPropertySet(), uno::Reference<text::XText>() )
+{
+}
+
+ScCellTextObj::~ScCellTextObj() COVERITY_NOEXCEPT_FALSE
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/tokenuno.cxx b/sc/source/ui/unoobj/tokenuno.cxx
new file mode 100644
index 0000000000..90b0a87e30
--- /dev/null
+++ b/sc/source/ui/unoobj/tokenuno.cxx
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <tokenuno.hxx>
+
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/ExternalReference.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/AddressConvention.hpp>
+#include <com/sun/star/sheet/NameToken.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+
+#include <svl/itemprop.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/string.hxx>
+
+#include <miscuno.hxx>
+#include <convuno.hxx>
+#include <unonames.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <docsh.hxx>
+#include <rangeseq.hxx>
+#include <externalrefmgr.hxx>
+
+using namespace ::formula;
+using namespace ::com::sun::star;
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetFormulaParserMap()
+{
+ static const SfxItemPropertyMapEntry aFormulaParserMap_Impl[] =
+ {
+ { SC_UNO_COMPILEFAP, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_COMPILEENGLISH, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_IGNORELEADING, 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { SC_UNO_FORMULACONVENTION, 0, cppu::UnoType<decltype(sheet::AddressConvention::UNSPECIFIED)>::get(), 0, 0 },
+ { SC_UNO_OPCODEMAP, 0, cppu::UnoType<uno::Sequence< sheet::FormulaOpCodeMapEntry >>::get(), 0, 0 },
+ };
+ return aFormulaParserMap_Impl;
+}
+
+const formula::FormulaGrammar::AddressConvention aConvMap[] = {
+ formula::FormulaGrammar::CONV_OOO, // <- AddressConvention::OOO
+ formula::FormulaGrammar::CONV_XL_A1, // <- AddressConvention::XL_A1
+ formula::FormulaGrammar::CONV_XL_R1C1, // <- AddressConvention::XL_R1C1
+ formula::FormulaGrammar::CONV_XL_OOX, // <- AddressConvention::XL_OOX
+ formula::FormulaGrammar::CONV_LOTUS_A1 // <- AddressConvention::LOTUS_A1
+};
+// sal_Int16 because of comparison of integer expressions below.
+constexpr sal_Int16 nConvMapCount = SAL_N_ELEMENTS(aConvMap);
+
+
+SC_SIMPLE_SERVICE_INFO( ScFormulaParserObj, "ScFormulaParserObj", SC_SERVICENAME_FORMULAPARS )
+
+ScFormulaParserObj::ScFormulaParserObj(ScDocShell* pDocSh) :
+ mpDocShell( pDocSh ),
+ mnConv( sheet::AddressConvention::UNSPECIFIED ),
+ mbEnglish( false ),
+ mbIgnoreSpaces( true ),
+ mbCompileFAP( false ),
+ mbRefConventionChartOOXML( false )
+{
+ mpDocShell->GetDocument().AddUnoObject(*this);
+}
+
+ScFormulaParserObj::~ScFormulaParserObj()
+{
+ SolarMutexGuard g;
+
+ if (mpDocShell)
+ mpDocShell->GetDocument().RemoveUnoObject(*this);
+}
+
+void ScFormulaParserObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ mpDocShell = nullptr;
+}
+
+// XFormulaParser
+
+void ScFormulaParserObj::SetCompilerFlags( ScCompiler& rCompiler ) const
+{
+ formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
+ if (mnConv >= 0 && mnConv < nConvMapCount)
+ eConv = aConvMap[mnConv];
+
+ // If mxOpCodeMap is not empty it overrides mbEnglish, and vice versa. We
+ // don't need to initialize things twice.
+ if (mxOpCodeMap)
+ rCompiler.SetFormulaLanguage( mxOpCodeMap );
+ else
+ {
+ const sal_Int32 nFormulaLanguage = (eConv == formula::FormulaGrammar::CONV_XL_OOX ?
+ sheet::FormulaLanguage::OOXML :
+ (mbEnglish ? sheet::FormulaLanguage::ENGLISH : sheet::FormulaLanguage::NATIVE));
+ ScCompiler::OpCodeMapPtr xMap = rCompiler.GetOpCodeMap( nFormulaLanguage);
+ rCompiler.SetFormulaLanguage( xMap);
+ }
+
+ rCompiler.SetRefConvention( eConv );
+ rCompiler.EnableJumpCommandReorder(!mbCompileFAP);
+ rCompiler.EnableStopOnError(!mbCompileFAP);
+
+ rCompiler.SetExternalLinks(maExternalLinks);
+ rCompiler.SetRefConventionChartOOXML(mbRefConventionChartOOXML);
+}
+
+uno::Sequence<sheet::FormulaToken> SAL_CALL ScFormulaParserObj::parseFormula(
+ const OUString& aFormula, const table::CellAddress& rReferencePos )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<sheet::FormulaToken> aRet;
+
+ if (mpDocShell)
+ {
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ ScExternalRefManager::ApiGuard aExtRefGuard(rDoc);
+
+ ScAddress aRefPos( ScAddress::UNINITIALIZED );
+ ScUnoConversion::FillScAddress( aRefPos, rReferencePos );
+ ScCompiler aCompiler( rDoc, aRefPos, rDoc.GetGrammar());
+ SetCompilerFlags( aCompiler );
+
+ std::unique_ptr<ScTokenArray> pCode = aCompiler.CompileString( aFormula );
+ ScTokenConversion::ConvertToTokenSequence( rDoc, aRet, *pCode );
+ }
+
+ return aRet;
+}
+
+OUString SAL_CALL ScFormulaParserObj::printFormula(
+ const uno::Sequence<sheet::FormulaToken>& aTokens, const table::CellAddress& rReferencePos )
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+
+ if (mpDocShell)
+ {
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ ScTokenArray aCode(rDoc);
+ (void)ScTokenConversion::ConvertToTokenArray( rDoc, aCode, aTokens );
+ ScAddress aRefPos( ScAddress::UNINITIALIZED );
+ ScUnoConversion::FillScAddress( aRefPos, rReferencePos );
+ ScCompiler aCompiler(rDoc, aRefPos, aCode, rDoc.GetGrammar());
+ SetCompilerFlags( aCompiler );
+
+ OUStringBuffer aBuffer;
+ aCompiler.CreateStringFromTokenArray( aBuffer );
+ aRet = aBuffer.makeStringAndClear();
+ }
+
+ return aRet;
+}
+
+// XPropertySet
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFormulaParserObj::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetFormulaParserMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScFormulaParserObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if ( aPropertyName == SC_UNO_COMPILEFAP )
+ {
+ aValue >>= mbCompileFAP;
+ }
+ else if ( aPropertyName == SC_UNO_COMPILEENGLISH )
+ {
+ bool bOldEnglish = mbEnglish;
+ if (!(aValue >>= mbEnglish))
+ throw lang::IllegalArgumentException();
+
+ // Need to recreate the symbol map to change English property
+ // because the map is const. So for performance reasons set
+ // CompileEnglish _before_ OpCodeMap!
+ if (mxOpCodeMap && mbEnglish != bOldEnglish)
+ {
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
+ mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish);
+ }
+
+ }
+ else if ( aPropertyName == SC_UNO_FORMULACONVENTION )
+ {
+ aValue >>= mnConv;
+
+ bool bOldEnglish = mbEnglish;
+ if (mnConv >= 0 && mnConv < nConvMapCount
+ && aConvMap[mnConv] == formula::FormulaGrammar::CONV_XL_OOX)
+ mbEnglish = true;
+
+ // Same as for SC_UNO_COMPILEENGLISH, though an OpCodeMap should not
+ // had been set for CONV_XL_OOX.
+ if (mxOpCodeMap && mbEnglish != bOldEnglish)
+ {
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
+ mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish);
+ }
+ }
+ else if ( aPropertyName == SC_UNO_IGNORELEADING )
+ {
+ aValue >>= mbIgnoreSpaces;
+ }
+ else if ( aPropertyName == SC_UNO_OPCODEMAP )
+ {
+ if (!(aValue >>= maOpCodeMapping))
+ throw lang::IllegalArgumentException();
+
+ ScDocument& rDoc = mpDocShell->GetDocument();
+ ScCompiler aCompiler(rDoc, ScAddress(), rDoc.GetGrammar());
+ mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish);
+
+ }
+ else if ( aPropertyName == SC_UNO_EXTERNALLINKS )
+ {
+ if (!(aValue >>= maExternalLinks))
+ throw lang::IllegalArgumentException();
+ }
+ else if ( aPropertyName == SC_UNO_REF_CONV_CHARTOOXML )
+ {
+ if (!(aValue >>= mbRefConventionChartOOXML))
+ throw lang::IllegalArgumentException();
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+}
+
+uno::Any SAL_CALL ScFormulaParserObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if ( aPropertyName == SC_UNO_COMPILEFAP )
+ {
+ aRet <<= mbCompileFAP;
+ }
+ else if ( aPropertyName == SC_UNO_COMPILEENGLISH )
+ {
+ aRet <<= mbEnglish;
+ }
+ else if ( aPropertyName == SC_UNO_FORMULACONVENTION )
+ {
+ aRet <<= mnConv;
+ }
+ else if ( aPropertyName == SC_UNO_IGNORELEADING )
+ {
+ aRet <<= mbIgnoreSpaces;
+ }
+ else if ( aPropertyName == SC_UNO_OPCODEMAP )
+ {
+ aRet <<= maOpCodeMapping;
+ }
+ else if ( aPropertyName == SC_UNO_EXTERNALLINKS )
+ {
+ aRet <<= maExternalLinks;
+ }
+ else if ( aPropertyName == SC_UNO_REF_CONV_CHARTOOXML )
+ {
+ aRet <<= mbRefConventionChartOOXML;
+ }
+ else
+ throw beans::UnknownPropertyException(aPropertyName);
+ return aRet;
+}
+
+SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFormulaParserObj )
+
+static void lcl_ExternalRefToApi( sheet::SingleReference& rAPI, const ScSingleRefData& rRef )
+{
+ rAPI.Column = 0;
+ rAPI.Row = 0;
+ rAPI.Sheet = 0;
+ rAPI.RelativeColumn = 0;
+ rAPI.RelativeRow = 0;
+ rAPI.RelativeSheet = 0;
+
+ sal_Int32 nFlags = 0;
+ if ( rRef.IsColRel() )
+ {
+ nFlags |= sheet::ReferenceFlags::COLUMN_RELATIVE;
+ rAPI.RelativeColumn = rRef.Col();
+ }
+ else
+ rAPI.Column = rRef.Col();
+
+ if ( rRef.IsRowRel() )
+ {
+ nFlags |= sheet::ReferenceFlags::ROW_RELATIVE;
+ rAPI.RelativeRow = rRef.Row();
+ }
+ else
+ rAPI.Row = rRef.Row();
+
+ if ( rRef.IsColDeleted() ) nFlags |= sheet::ReferenceFlags::COLUMN_DELETED;
+ if ( rRef.IsRowDeleted() ) nFlags |= sheet::ReferenceFlags::ROW_DELETED;
+ if ( rRef.IsFlag3D() ) nFlags |= sheet::ReferenceFlags::SHEET_3D;
+ if ( rRef.IsRelName() ) nFlags |= sheet::ReferenceFlags::RELATIVE_NAME;
+ rAPI.Flags = nFlags;
+}
+
+static void lcl_SingleRefToApi( sheet::SingleReference& rAPI, const ScSingleRefData& rRef )
+{
+ sal_Int32 nFlags = 0;
+ if ( rRef.IsColRel() )
+ {
+ nFlags |= sheet::ReferenceFlags::COLUMN_RELATIVE;
+ rAPI.RelativeColumn = rRef.Col();
+ rAPI.Column = 0;
+ }
+ else
+ {
+ rAPI.RelativeColumn = 0;
+ rAPI.Column = rRef.Col();
+ }
+
+ if ( rRef.IsRowRel() )
+ {
+ nFlags |= sheet::ReferenceFlags::ROW_RELATIVE;
+ rAPI.RelativeRow = rRef.Row();
+ rAPI.Row = 0;
+ }
+ else
+ {
+ rAPI.RelativeRow = 0;
+ rAPI.Row = rRef.Row();
+ }
+
+ if ( rRef.IsTabRel() )
+ {
+ nFlags |= sheet::ReferenceFlags::SHEET_RELATIVE;
+ rAPI.RelativeSheet = rRef.Tab();
+ rAPI.Sheet = 0;
+ }
+ else
+ {
+ rAPI.RelativeSheet = 0;
+ rAPI.Sheet = rRef.Tab();
+ }
+
+ if ( rRef.IsColDeleted() ) nFlags |= sheet::ReferenceFlags::COLUMN_DELETED;
+ if ( rRef.IsRowDeleted() ) nFlags |= sheet::ReferenceFlags::ROW_DELETED;
+ if ( rRef.IsTabDeleted() ) nFlags |= sheet::ReferenceFlags::SHEET_DELETED;
+ if ( rRef.IsFlag3D() ) nFlags |= sheet::ReferenceFlags::SHEET_3D;
+ if ( rRef.IsRelName() ) nFlags |= sheet::ReferenceFlags::RELATIVE_NAME;
+ rAPI.Flags = nFlags;
+}
+
+bool ScTokenConversion::ConvertToTokenArray( ScDocument& rDoc,
+ ScTokenArray& rTokenArray, const uno::Sequence<sheet::FormulaToken>& rSequence )
+{
+ return !rTokenArray.Fill(rSequence, rDoc.GetSharedStringPool(), rDoc.GetExternalRefManager());
+}
+
+void ScTokenConversion::ConvertToTokenSequence( const ScDocument& rDoc,
+ uno::Sequence<sheet::FormulaToken>& rSequence, const ScTokenArray& rTokenArray )
+{
+ sal_Int32 nLen = static_cast<sal_Int32>(rTokenArray.GetLen());
+ formula::FormulaToken** pTokens = rTokenArray.GetArray();
+ if ( pTokens )
+ {
+ rSequence.realloc(nLen);
+ auto pSequence = rSequence.getArray();
+ for (sal_Int32 nPos=0; nPos<nLen; nPos++)
+ {
+ const formula::FormulaToken& rToken = *pTokens[nPos];
+ sheet::FormulaToken& rAPI = pSequence[nPos];
+
+ OpCode eOpCode = rToken.GetOpCode();
+ // eOpCode may be changed in the following switch/case
+ switch ( rToken.GetType() )
+ {
+ case svByte:
+ // Only the count of spaces is stored as "long". Parameter count is ignored.
+ if ( eOpCode == ocSpaces )
+ rAPI.Data <<= static_cast<sal_Int32>(rToken.GetByte());
+ else if (eOpCode == ocWhitespace)
+ {
+ // Convention is one character repeated.
+ if (rToken.GetByte() == 1)
+ rAPI.Data <<= OUString( rToken.GetChar());
+ else
+ {
+ OUStringBuffer aBuf( rToken.GetByte());
+ comphelper::string::padToLength( aBuf, rToken.GetByte(), rToken.GetChar());
+ rAPI.Data <<= aBuf.makeStringAndClear();
+ }
+ }
+ else
+ rAPI.Data.clear(); // no data
+ break;
+ case formula::svDouble:
+ rAPI.Data <<= rToken.GetDouble();
+ break;
+ case formula::svString:
+ rAPI.Data <<= rToken.GetString().getString();
+ break;
+ case svExternal:
+ // Function name is stored as string.
+ // Byte (parameter count) is ignored.
+ rAPI.Data <<= rToken.GetExternal();
+ break;
+ case svSingleRef:
+ {
+ sheet::SingleReference aSingleRef;
+ lcl_SingleRefToApi( aSingleRef, *rToken.GetSingleRef() );
+ rAPI.Data <<= aSingleRef;
+ }
+ break;
+ case formula::svDoubleRef:
+ {
+ sheet::ComplexReference aCompRef;
+ lcl_SingleRefToApi( aCompRef.Reference1, *rToken.GetSingleRef() );
+ lcl_SingleRefToApi( aCompRef.Reference2, *rToken.GetSingleRef2() );
+ rAPI.Data <<= aCompRef;
+ }
+ break;
+ case svIndex:
+ {
+ sheet::NameToken aNameToken;
+ aNameToken.Index = static_cast<sal_Int32>( rToken.GetIndex() );
+ aNameToken.Sheet = rToken.GetSheet();
+ rAPI.Data <<= aNameToken;
+ }
+ break;
+ case svMatrix:
+ if (!ScRangeToSequence::FillMixedArray( rAPI.Data, rToken.GetMatrix(), true))
+ rAPI.Data.clear();
+ break;
+ case svExternalSingleRef:
+ {
+ sheet::SingleReference aSingleRef;
+ lcl_ExternalRefToApi( aSingleRef, *rToken.GetSingleRef() );
+ size_t nCacheId;
+ rDoc.GetExternalRefManager()->getCacheTable(
+ rToken.GetIndex(), rToken.GetString().getString(), false, &nCacheId);
+ aSingleRef.Sheet = static_cast< sal_Int32 >( nCacheId );
+ sheet::ExternalReference aExtRef;
+ aExtRef.Index = rToken.GetIndex();
+ aExtRef.Reference <<= aSingleRef;
+ rAPI.Data <<= aExtRef;
+ eOpCode = ocPush;
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ sheet::ComplexReference aComplRef;
+ lcl_ExternalRefToApi( aComplRef.Reference1, *rToken.GetSingleRef() );
+ lcl_ExternalRefToApi( aComplRef.Reference2, *rToken.GetSingleRef2() );
+ size_t nCacheId;
+ rDoc.GetExternalRefManager()->getCacheTable(
+ rToken.GetIndex(), rToken.GetString().getString(), false, &nCacheId);
+ aComplRef.Reference1.Sheet = static_cast< sal_Int32 >( nCacheId );
+ // NOTE: This assumes that cached sheets are in consecutive order!
+ aComplRef.Reference2.Sheet =
+ aComplRef.Reference1.Sheet +
+ (rToken.GetSingleRef2()->Tab() - rToken.GetSingleRef()->Tab());
+ sheet::ExternalReference aExtRef;
+ aExtRef.Index = rToken.GetIndex();
+ aExtRef.Reference <<= aComplRef;
+ rAPI.Data <<= aExtRef;
+ eOpCode = ocPush;
+ }
+ break;
+ case svExternalName:
+ {
+ sheet::ExternalReference aExtRef;
+ aExtRef.Index = rToken.GetIndex();
+ aExtRef.Reference <<= rToken.GetString().getString();
+ rAPI.Data <<= aExtRef;
+ eOpCode = ocPush;
+ }
+ break;
+ default:
+ SAL_WARN("sc", "ScTokenConversion::ConvertToTokenSequence: unhandled token type " << StackVarEnumToString(rToken.GetType()));
+ [[fallthrough]];
+ case svJump: // occurs with ocIf, ocChoose
+ case svError: // seems to be fairly common, and probably not exceptional and not worth a warning?
+ case svMissing: // occurs with ocMissing
+ case svSep: // occurs with ocSep, ocOpen, ocClose, ocArray*
+ rAPI.Data.clear(); // no data
+ }
+ rAPI.OpCode = static_cast<sal_Int32>(eOpCode); //! assuming equal values for the moment
+ }
+ }
+ else
+ rSequence.realloc(0);
+}
+
+ScFormulaOpCodeMapperObj::ScFormulaOpCodeMapperObj(::std::unique_ptr<formula::FormulaCompiler> && _pCompiler)
+: formula::FormulaOpCodeMapperObj(std::move(_pCompiler))
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/unodoc.cxx b/sc/source/ui/unoobj/unodoc.cxx
new file mode 100644
index 0000000000..bed297df7c
--- /dev/null
+++ b/sc/source/ui/unoobj/unodoc.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 <sfx2/sfxmodelfactory.hxx>
+
+#include <scdll.hxx>
+#include <vcl/svapp.hxx>
+
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_SpreadsheetDocument_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& args)
+{
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ css::uno::Reference<css::uno::XInterface> xInterface = sfx2::createSfxModelInstance(args,
+ [](SfxModelFlags _nCreationFlags)
+ {
+ SfxObjectShell* pShell = new ScDocShell( _nCreationFlags );
+ return uno::Reference< uno::XInterface >( pShell->GetModel() );
+ });
+ xInterface->acquire();
+ return xInterface.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/unoreflist.cxx b/sc/source/ui/unoobj/unoreflist.cxx
new file mode 100644
index 0000000000..fff4d0fd9d
--- /dev/null
+++ b/sc/source/ui/unoobj/unoreflist.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 <unoreflist.hxx>
+#include <document.hxx>
+#include <utility>
+
+ScUnoRefList::ScUnoRefList()
+{
+}
+
+ScUnoRefList::~ScUnoRefList()
+{
+}
+
+void ScUnoRefList::Add( sal_Int64 nId, const ScRangeList& rOldRanges )
+{
+ aEntries.emplace_back( nId, rOldRanges );
+}
+
+void ScUnoRefList::Undo( ScDocument* pDoc )
+{
+ for (const auto & entry: aEntries)
+ {
+ ScUnoRefUndoHint aHint(entry);
+ pDoc->BroadcastUno( aHint );
+ }
+}
+
+ScUnoRefUndoHint::ScUnoRefUndoHint( ScUnoRefEntry aRefEntry ) :
+ aEntry(std::move( aRefEntry ))
+{
+}
+
+ScUnoRefUndoHint::~ScUnoRefUndoHint()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/viewuno.cxx b/sc/source/ui/unoobj/viewuno.cxx
new file mode 100644
index 0000000000..2d066bcfc2
--- /dev/null
+++ b/sc/source/ui/unoobj/viewuno.cxx
@@ -0,0 +1,2220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/MouseButton.hpp>
+#include <com/sun/star/drawing/ShapeCollection.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+#include <com/sun/star/view/DocumentZoomType.hpp>
+
+#include <editeng/outliner.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/fmshell.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+
+#include <drawsh.hxx>
+#include <drtxtob.hxx>
+#include <transobj.hxx>
+#include <editsh.hxx>
+#include <viewuno.hxx>
+#include <cellsuno.hxx>
+#include <miscuno.hxx>
+#include <tabvwsh.hxx>
+#include <prevwsh.hxx>
+#include <docsh.hxx>
+#include <drwlayer.hxx>
+#include <attrib.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+#include <sc.hrc>
+#include <unonames.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <gridwin.hxx>
+#include <sheetevents.hxx>
+#include <markdata.hxx>
+#include <scextopt.hxx>
+#include <preview.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <formatsh.hxx>
+#include <sfx2/app.hxx>
+#include <scitems.hxx>
+
+using namespace com::sun::star;
+
+//! Clipping Marks
+
+// no Which-ID here, Map only for PropertySetInfo
+
+static std::span<const SfxItemPropertyMapEntry> lcl_GetViewOptPropertyMap()
+{
+ static const SfxItemPropertyMapEntry aViewOptPropertyMap_Impl[] =
+ {
+ { OLD_UNO_COLROWHDR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_GRIDCOLOR, 0, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { SC_UNO_COLROWHDR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_HORSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHEETTABS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_VERTSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_HIDESPELL, 0, cppu::UnoType<bool>::get(), 0, 0}, /* deprecated #i91949 */
+ { OLD_UNO_HORSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_OUTLSYMB, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_VALUEHIGH, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { OLD_UNO_OUTLSYMB, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { OLD_UNO_SHEETTABS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWANCHOR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWCHARTS, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_SHOWDRAW, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_SHOWFORM, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWGRID, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWHELP, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWNOTES, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWFORMULASMARKS, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWOBJ, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_SHOWPAGEBR, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_SHOWZERO, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { OLD_UNO_VALUEHIGH, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { OLD_UNO_VERTSCROLL, 0, cppu::UnoType<bool>::get(), 0, 0},
+ { SC_UNO_VISAREA, 0, cppu::UnoType<awt::Rectangle>::get(), 0, 0},
+ { SC_UNO_ZOOMTYPE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_ZOOMVALUE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { SC_UNO_VISAREASCREEN,0, cppu::UnoType<awt::Rectangle>::get(), 0, 0},
+ { SC_UNO_FORMULABARHEIGHT,0,cppu::UnoType<sal_Int16>::get(), 0, 0},
+ };
+ return aViewOptPropertyMap_Impl;
+}
+
+constexpr OUString SCTABVIEWOBJ_SERVICE = u"com.sun.star.sheet.SpreadsheetView"_ustr;
+constexpr OUString SCVIEWSETTINGS_SERVICE = u"com.sun.star.sheet.SpreadsheetViewSettings"_ustr;
+
+SC_SIMPLE_SERVICE_INFO( ScViewPaneBase, "ScViewPaneObj", "com.sun.star.sheet.SpreadsheetViewPane" )
+
+ScViewPaneBase::ScViewPaneBase(ScTabViewShell* pViewSh, sal_uInt16 nP) :
+ pViewShell( pViewSh ),
+ nPane( nP )
+{
+ if (pViewShell)
+ StartListening(*pViewShell);
+}
+
+ScViewPaneBase::~ScViewPaneBase()
+{
+ SolarMutexGuard g;
+
+ if (pViewShell)
+ EndListening(*pViewShell);
+}
+
+void ScViewPaneBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ pViewShell = nullptr;
+}
+
+uno::Any SAL_CALL ScViewPaneBase::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XViewPane*>(this),
+ static_cast<sheet::XCellRangeReferrer*>(this),
+ static_cast<view::XFormLayerAccess*>(this),
+ static_cast<view::XControlAccess*>(this),
+ static_cast<lang::XServiceInfo*>(this),
+ static_cast<lang::XTypeProvider*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return uno::Any(); // OWeakObject is in derived objects
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScViewPaneBase::getTypes()
+{
+ static const uno::Sequence<uno::Type> aTypes
+ {
+ cppu::UnoType<sheet::XViewPane>::get(),
+ cppu::UnoType<sheet::XCellRangeReferrer>::get(),
+ cppu::UnoType<view::XFormLayerAccess>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ };
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScViewPaneBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XViewPane
+
+sal_Int32 SAL_CALL ScViewPaneBase::getFirstVisibleColumn()
+{
+ SolarMutexGuard aGuard;
+ if (pViewShell)
+ {
+ ScViewData& rViewData = pViewShell->GetViewData();
+ ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ?
+ rViewData.GetActivePart() :
+ static_cast<ScSplitPos>(nPane);
+ ScHSplitPos eWhichH = WhichH( eWhich );
+
+ return rViewData.GetPosX( eWhichH );
+ }
+ OSL_FAIL("no View ?!?"); //! Exception?
+ return 0;
+}
+
+void SAL_CALL ScViewPaneBase::setFirstVisibleColumn(sal_Int32 nFirstVisibleColumn)
+{
+ SolarMutexGuard aGuard;
+ if (pViewShell)
+ {
+ ScViewData& rViewData = pViewShell->GetViewData();
+ ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ?
+ rViewData.GetActivePart() :
+ static_cast<ScSplitPos>(nPane);
+ ScHSplitPos eWhichH = WhichH( eWhich );
+
+ tools::Long nDeltaX = static_cast<tools::Long>(nFirstVisibleColumn) - rViewData.GetPosX( eWhichH );
+ pViewShell->ScrollX( nDeltaX, eWhichH );
+ }
+}
+
+sal_Int32 SAL_CALL ScViewPaneBase::getFirstVisibleRow()
+{
+ SolarMutexGuard aGuard;
+ if (pViewShell)
+ {
+ ScViewData& rViewData = pViewShell->GetViewData();
+ ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ?
+ rViewData.GetActivePart() :
+ static_cast<ScSplitPos>(nPane);
+ ScVSplitPos eWhichV = WhichV( eWhich );
+
+ return rViewData.GetPosY( eWhichV );
+ }
+ OSL_FAIL("no View ?!?"); //! Exception?
+ return 0;
+}
+
+void SAL_CALL ScViewPaneBase::setFirstVisibleRow( sal_Int32 nFirstVisibleRow )
+{
+ SolarMutexGuard aGuard;
+ if (pViewShell)
+ {
+ ScViewData& rViewData = pViewShell->GetViewData();
+ ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ?
+ rViewData.GetActivePart() :
+ static_cast<ScSplitPos>(nPane);
+ ScVSplitPos eWhichV = WhichV( eWhich );
+
+ tools::Long nDeltaY = static_cast<tools::Long>(nFirstVisibleRow) - rViewData.GetPosY( eWhichV );
+ pViewShell->ScrollY( nDeltaY, eWhichV );
+ }
+}
+
+table::CellRangeAddress SAL_CALL ScViewPaneBase::getVisibleRange()
+{
+ SolarMutexGuard aGuard;
+ table::CellRangeAddress aAdr;
+ if (pViewShell)
+ {
+ ScViewData& rViewData = pViewShell->GetViewData();
+ ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ?
+ rViewData.GetActivePart() :
+ static_cast<ScSplitPos>(nPane);
+ ScHSplitPos eWhichH = WhichH( eWhich );
+ ScVSplitPos eWhichV = WhichV( eWhich );
+
+ // VisibleCellsX returns only completely visible cells
+ // VisibleRange in Excel also partially visible ones
+ //! do the same ???
+
+ SCCOL nVisX = rViewData.VisibleCellsX( eWhichH );
+ SCROW nVisY = rViewData.VisibleCellsY( eWhichV );
+ if (!nVisX) nVisX = 1; // there has to be something in the range
+ if (!nVisY) nVisY = 1;
+ aAdr.Sheet = rViewData.GetTabNo();
+ aAdr.StartColumn = rViewData.GetPosX( eWhichH );
+ aAdr.StartRow = rViewData.GetPosY( eWhichV );
+ aAdr.EndColumn = aAdr.StartColumn + nVisX - 1;
+ aAdr.EndRow = aAdr.StartRow + nVisY - 1;
+ }
+ return aAdr;
+}
+
+// XCellRangeSource
+
+uno::Reference<table::XCellRange> SAL_CALL ScViewPaneBase::getReferredCells()
+{
+ SolarMutexGuard aGuard;
+ if (pViewShell)
+ {
+ ScDocShell* pDocSh = pViewShell->GetViewData().GetDocShell();
+
+ table::CellRangeAddress aAdr(getVisibleRange()); //! helper function with ScRange?
+ ScRange aRange( static_cast<SCCOL>(aAdr.StartColumn), static_cast<SCROW>(aAdr.StartRow), aAdr.Sheet,
+ static_cast<SCCOL>(aAdr.EndColumn), static_cast<SCROW>(aAdr.EndRow), aAdr.Sheet );
+ if ( aRange.aStart == aRange.aEnd )
+ return new ScCellObj( pDocSh, aRange.aStart );
+ else
+ return new ScCellRangeObj( pDocSh, aRange );
+ }
+
+ return nullptr;
+}
+
+namespace
+{
+ bool lcl_prepareFormShellCall( ScTabViewShell* _pViewShell, sal_uInt16 _nPane, FmFormShell*& _rpFormShell, vcl::Window*& _rpWindow, SdrView*& _rpSdrView )
+ {
+ if ( !_pViewShell )
+ return false;
+
+ ScViewData& rViewData = _pViewShell->GetViewData();
+ ScSplitPos eWhich = ( _nPane == SC_VIEWPANE_ACTIVE ) ?
+ rViewData.GetActivePart() :
+ static_cast<ScSplitPos>(_nPane);
+ _rpWindow = _pViewShell->GetWindowByPos( eWhich );
+ _rpSdrView = _pViewShell->GetScDrawView();
+ _rpFormShell = _pViewShell->GetFormShell();
+ return ( _rpFormShell != nullptr ) && ( _rpSdrView != nullptr )&& ( _rpWindow != nullptr );
+ }
+}
+
+// XFormLayerAccess
+uno::Reference< form::runtime::XFormController > SAL_CALL ScViewPaneBase::getFormController( const uno::Reference< form::XForm >& Form )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< form::runtime::XFormController > xController;
+
+ vcl::Window* pWindow( nullptr );
+ SdrView* pSdrView( nullptr );
+ FmFormShell* pFormShell( nullptr );
+ if ( lcl_prepareFormShellCall( pViewShell, nPane, pFormShell, pWindow, pSdrView ) )
+ xController = FmFormShell::GetFormController( Form, *pSdrView, *pWindow->GetOutDev() );
+
+ return xController;
+}
+
+sal_Bool SAL_CALL ScViewPaneBase::isFormDesignMode( )
+{
+ SolarMutexGuard aGuard;
+
+ bool bIsFormDesignMode( true );
+
+ FmFormShell* pFormShell( pViewShell ? pViewShell->GetFormShell() : nullptr );
+ if ( pFormShell )
+ bIsFormDesignMode = pFormShell->IsDesignMode();
+
+ return bIsFormDesignMode;
+}
+
+void SAL_CALL ScViewPaneBase::setFormDesignMode( sal_Bool DesignMode )
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window* pWindow( nullptr );
+ SdrView* pSdrView( nullptr );
+ FmFormShell* pFormShell( nullptr );
+ if ( lcl_prepareFormShellCall( pViewShell, nPane, pFormShell, pWindow, pSdrView ) )
+ pFormShell->SetDesignMode( DesignMode );
+}
+
+// XControlAccess
+
+uno::Reference<awt::XControl> SAL_CALL ScViewPaneBase::getControl(
+ const uno::Reference<awt::XControlModel>& xModel )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<awt::XControl> xRet;
+
+ vcl::Window* pWindow( nullptr );
+ SdrView* pSdrView( nullptr );
+ FmFormShell* pFormShell( nullptr );
+ if ( lcl_prepareFormShellCall( pViewShell, nPane, pFormShell, pWindow, pSdrView ) )
+ pFormShell->GetFormControl( xModel, *pSdrView, *pWindow->GetOutDev(), xRet );
+
+ if ( !xRet.is() )
+ throw container::NoSuchElementException(); // no control found
+
+ return xRet;
+}
+
+awt::Rectangle ScViewPaneBase::GetVisArea() const
+{
+ awt::Rectangle aVisArea;
+ if (pViewShell)
+ {
+ ScSplitPos eWhich = ( nPane == SC_VIEWPANE_ACTIVE ) ?
+ pViewShell->GetViewData().GetActivePart() :
+ static_cast<ScSplitPos>(nPane);
+ ScGridWindow* pWindow = static_cast<ScGridWindow*>(pViewShell->GetWindowByPos(eWhich));
+ ScDocument& rDoc = pViewShell->GetViewData().GetDocument();
+ if (pWindow)
+ {
+ ScHSplitPos eWhichH = ((eWhich == SC_SPLIT_TOPLEFT) || (eWhich == SC_SPLIT_BOTTOMLEFT)) ?
+ SC_SPLIT_LEFT : SC_SPLIT_RIGHT;
+ ScVSplitPos eWhichV = ((eWhich == SC_SPLIT_TOPLEFT) || (eWhich == SC_SPLIT_TOPRIGHT)) ?
+ SC_SPLIT_TOP : SC_SPLIT_BOTTOM;
+ ScAddress aCell(pViewShell->GetViewData().GetPosX(eWhichH),
+ pViewShell->GetViewData().GetPosY(eWhichV),
+ pViewShell->GetViewData().GetTabNo());
+ tools::Rectangle aCellRect( rDoc.GetMMRect( aCell.Col(), aCell.Row(), aCell.Col(), aCell.Row(), aCell.Tab() ) );
+ Size aVisSize( pWindow->PixelToLogic( pWindow->GetSizePixel(), pWindow->GetDrawMapMode( true ) ) );
+ Point aVisPos( aCellRect.TopLeft() );
+ if ( rDoc.IsLayoutRTL( aCell.Tab() ) )
+ {
+ aVisPos = aCellRect.TopRight();
+ aVisPos.AdjustX( -(aVisSize.Width()) );
+ }
+ tools::Rectangle aVisRect( aVisPos, aVisSize );
+ aVisArea = AWTRectangle(aVisRect);
+ }
+ }
+ return aVisArea;
+}
+
+ScViewPaneObj::ScViewPaneObj(ScTabViewShell* pViewSh, sal_uInt16 nP) :
+ ScViewPaneBase( pViewSh, nP )
+{
+}
+
+ScViewPaneObj::~ScViewPaneObj()
+{
+}
+
+uno::Any SAL_CALL ScViewPaneObj::queryInterface( const uno::Type& rType )
+{
+ // ScViewPaneBase has everything except OWeakObject
+
+ uno::Any aRet(ScViewPaneBase::queryInterface( rType ));
+ if (!aRet.hasValue())
+ aRet = OWeakObject::queryInterface( rType );
+ return aRet;
+}
+
+void SAL_CALL ScViewPaneObj::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ScViewPaneObj::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// We need default ctor for SMART_REFLECTION_IMPLEMENTATION
+
+ScTabViewObj::ScTabViewObj( ScTabViewShell* pViewSh ) :
+ ScViewPaneBase( pViewSh, SC_VIEWPANE_ACTIVE ),
+ SfxBaseController( pViewSh ),
+ aPropSet( lcl_GetViewOptPropertyMap() ),
+ aMouseClickHandlers( 0 ),
+ aActivationListeners( 0 ),
+ nPreviousTab( 0 ),
+ bDrawSelModeSet(false),
+ bFilteredRangeSelection(false),
+ mbLeftMousePressed(false)
+{
+ if (pViewSh)
+ nPreviousTab = pViewSh->GetViewData().GetTabNo();
+}
+
+ScTabViewObj::~ScTabViewObj()
+{
+ //! Listening or something along that line
+ if (!aMouseClickHandlers.empty())
+ {
+ acquire();
+ EndMouseListening();
+ }
+ if (!aActivationListeners.empty())
+ {
+ acquire();
+ EndActivationListening();
+ }
+}
+
+uno::Any SAL_CALL ScTabViewObj::queryInterface( const uno::Type& rType )
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XSpreadsheetView*>(this),
+ static_cast<sheet::XEnhancedMouseClickBroadcaster*>(this),
+ static_cast<sheet::XActivationBroadcaster*>(this),
+ static_cast<container::XEnumerationAccess*>(this),
+ static_cast<container::XIndexAccess*>(this),
+ static_cast<container::XElementAccess*>(static_cast<container::XIndexAccess*>(this)),
+ static_cast<view::XSelectionSupplier*>(this),
+ static_cast<beans::XPropertySet*>(this),
+ static_cast<sheet::XViewSplitable*>(this),
+ static_cast<sheet::XViewFreezable*>(this),
+ static_cast<sheet::XRangeSelection*>(this),
+ static_cast<sheet::XSelectedSheetsSupplier*>(this),
+ static_cast<datatransfer::XTransferableSupplier*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ uno::Any aRet(ScViewPaneBase::queryInterface( rType ));
+ if (!aRet.hasValue())
+ aRet = SfxBaseController::queryInterface( rType );
+ return aRet;
+}
+
+void SAL_CALL ScTabViewObj::acquire() noexcept
+{
+ SfxBaseController::acquire();
+}
+
+void SAL_CALL ScTabViewObj::release() noexcept
+{
+ SfxBaseController::release();
+}
+
+static void lcl_CallActivate( ScDocShell* pDocSh, SCTAB nTab, ScSheetEventId nEvent )
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+ // when deleting a sheet, nPreviousTab can be invalid
+ // (could be handled with reference updates)
+ if (!rDoc.HasTable(nTab))
+ return;
+
+ const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab);
+ if (pEvents)
+ {
+ const OUString* pScript = pEvents->GetScript(nEvent);
+ if (pScript)
+ {
+ uno::Any aRet;
+ uno::Sequence<uno::Any> aParams;
+ uno::Sequence<sal_Int16> aOutArgsIndex;
+ uno::Sequence<uno::Any> aOutArgs;
+ /*ErrCode eRet =*/ pDocSh->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs );
+ }
+ }
+
+ // execute VBA event handlers
+ try
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ // the parameter is the clicked object, as in the mousePressed call above
+ uno::Sequence< uno::Any > aArgs{ uno::Any(nTab) };
+ xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( nEvent ), aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ScTabViewObj::SheetChanged( bool bSameTabButMoved )
+{
+ if ( !GetViewShell() )
+ return;
+
+ ScViewData& rViewData = GetViewShell()->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ if (!aActivationListeners.empty())
+ {
+ sheet::ActivationEvent aEvent;
+ uno::Reference< sheet::XSpreadsheetView > xView(this);
+ aEvent.Source.set(xView, uno::UNO_QUERY);
+ aEvent.ActiveSheet = new ScTableSheetObj(pDocSh, rViewData.GetTabNo());
+ // Listener's handler may remove it from the listeners list
+ for (size_t i = aActivationListeners.size(); i > 0; --i)
+ {
+ try
+ {
+ aActivationListeners[i - 1]->activeSpreadsheetChanged( aEvent );
+ }
+ catch( uno::Exception& )
+ {
+ aActivationListeners.erase(aActivationListeners.begin() + (i - 1));
+ }
+ }
+ }
+
+ /* Handle sheet events, but do not trigger event handlers, if the old
+ active sheet gets re-activated after inserting/deleting/moving a sheet. */
+ SCTAB nNewTab = rViewData.GetTabNo();
+ if ( !bSameTabButMoved && (nNewTab != nPreviousTab) )
+ {
+ lcl_CallActivate( pDocSh, nPreviousTab, ScSheetEventId::UNFOCUS );
+ lcl_CallActivate( pDocSh, nNewTab, ScSheetEventId::FOCUS );
+ }
+ nPreviousTab = nNewTab;
+}
+
+uno::Sequence<uno::Type> SAL_CALL ScTabViewObj::getTypes()
+{
+ return comphelper::concatSequences(
+ ScViewPaneBase::getTypes(),
+ SfxBaseController::getTypes(),
+ uno::Sequence<uno::Type>
+ {
+ cppu::UnoType<sheet::XSpreadsheetView>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<container::XIndexAccess>::get(),
+ cppu::UnoType<view::XSelectionSupplier>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<sheet::XViewSplitable>::get(),
+ cppu::UnoType<sheet::XViewFreezable>::get(),
+ cppu::UnoType<sheet::XRangeSelection>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<sheet::XEnhancedMouseClickBroadcaster>::get(),
+ cppu::UnoType<sheet::XActivationBroadcaster>::get(),
+ cppu::UnoType<datatransfer::XTransferableSupplier>::get()
+ } );
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScTabViewObj::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XDocumentView
+
+static bool lcl_TabInRanges( SCTAB nTab, const ScRangeList& rRanges )
+{
+ for (size_t i = 0, nCount = rRanges.size(); i < nCount; ++i)
+ {
+ const ScRange & rRange = rRanges[ i ];
+ if ( nTab >= rRange.aStart.Tab() && nTab <= rRange.aEnd.Tab() )
+ return true;
+ }
+ return false;
+}
+
+static void lcl_ShowObject( ScTabViewShell& rViewSh, const ScDrawView& rDrawView, const SdrObject* pSelObj )
+{
+ bool bFound = false;
+ SCTAB nObjectTab = 0;
+
+ SdrModel& rModel = rDrawView.GetModel();
+ sal_uInt16 nPageCount = rModel.GetPageCount();
+ for (sal_uInt16 i=0; i<nPageCount && !bFound; i++)
+ {
+ SdrPage* pPage = rModel.GetPage(i);
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !bFound)
+ {
+ if ( pObject == pSelObj )
+ {
+ bFound = true;
+ nObjectTab = static_cast<SCTAB>(i);
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+
+ if (bFound)
+ {
+ rViewSh.SetTabNo( nObjectTab );
+ rViewSh.ScrollToObject( pSelObj );
+ }
+}
+
+sal_Bool SAL_CALL ScTabViewObj::select( const uno::Any& aSelection )
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+
+ if ( !pViewSh )
+ return false;
+
+ //! Type of aSelection can be some specific interface instead of XInterface
+
+ bool bRet = false;
+ uno::Reference<uno::XInterface> xInterface(aSelection, uno::UNO_QUERY);
+ if ( !xInterface.is() ) //clear all selections
+ {
+ ScDrawView* pDrawView = pViewSh->GetScDrawView();
+ if (pDrawView)
+ {
+ pDrawView->ScEndTextEdit();
+ pDrawView->UnmarkAll();
+ }
+ else //#102232#; if there is no DrawView remove range selection
+ pViewSh->Unmark();
+ bRet = true;
+ }
+
+ if (bDrawSelModeSet) // remove DrawSelMode if set by API; if necessary it will be set again later
+ {
+ pViewSh->SetDrawSelMode(false);
+ pViewSh->UpdateLayerLocks();
+ bDrawSelModeSet = false;
+ }
+
+ if (bRet)
+ return bRet;
+
+ ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xInterface.get() );
+ uno::Reference<drawing::XShapes> xShapeColl( xInterface, uno::UNO_QUERY );
+ uno::Reference<drawing::XShape> xShapeSel( xInterface, uno::UNO_QUERY );
+ SvxShape* pShapeImp = comphelper::getFromUnoTunnel<SvxShape>( xShapeSel );
+
+ if (pRangesImp) // Cell ranges
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ if ( rViewData.GetDocShell() == pRangesImp->GetDocShell() )
+ {
+ // perhaps remove drawing selection first
+ // (MarkListHasChanged removes sheet selection)
+
+ ScDrawView* pDrawView = pViewSh->GetScDrawView();
+ if (pDrawView)
+ {
+ pDrawView->ScEndTextEdit();
+ pDrawView->UnmarkAll();
+ }
+ FuPoor* pFunc = pViewSh->GetDrawFuncPtr();
+ if ( pFunc && pFunc->GetSlotID() != SID_OBJECT_SELECT )
+ {
+ // execute the slot of drawing function again -> switch off
+ SfxDispatcher* pDisp = pViewSh->GetDispatcher();
+ if (pDisp)
+ pDisp->Execute( pFunc->GetSlotID(), SfxCallMode::SYNCHRON );
+ }
+ pViewSh->SetDrawShell(false);
+ pViewSh->SetDrawSelMode(false); // after Dispatcher-Execute
+
+ // select ranges
+
+ const ScRangeList& rRanges = pRangesImp->GetRangeList();
+ size_t nRangeCount = rRanges.size();
+ // for empty range list, remove selection (cursor remains where it was)
+ if ( nRangeCount == 0 )
+ pViewSh->Unmark();
+ else if ( nRangeCount == 1 )
+ pViewSh->MarkRange( rRanges[ 0 ] );
+ else
+ {
+ // multiselection
+
+ const ScRange & rFirst = rRanges[ 0 ];
+ if ( !lcl_TabInRanges( rViewData.GetTabNo(), rRanges ) )
+ pViewSh->SetTabNo( rFirst.aStart.Tab() );
+ pViewSh->DoneBlockMode();
+ pViewSh->InitOwnBlockMode( rFirst ); /* TODO: or even the overall range? */
+ rViewData.GetMarkData().MarkFromRangeList( rRanges, true );
+ pViewSh->MarkDataChanged();
+ rViewData.GetDocShell()->PostPaintGridAll(); // Marks (old&new)
+ pViewSh->AlignToCursor( rFirst.aStart.Col(), rFirst.aStart.Row(),
+ SC_FOLLOW_JUMP );
+ pViewSh->SetCursor( rFirst.aStart.Col(), rFirst.aStart.Row() );
+
+ //! method of the view to select RangeList
+ }
+ bRet = true;
+ }
+ }
+ else if ( pShapeImp || xShapeColl.is() ) // Drawing-Layer
+ {
+ ScDrawView* pDrawView = pViewSh->GetScDrawView();
+ if (pDrawView)
+ {
+ pDrawView->ScEndTextEdit();
+ pDrawView->UnmarkAll();
+
+ if (pShapeImp) // single shape
+ {
+ SdrObject *pObj = pShapeImp->GetSdrObject();
+ if (pObj)
+ {
+ lcl_ShowObject( *pViewSh, *pDrawView, pObj );
+ SdrPageView* pPV = pDrawView->GetSdrPageView();
+ if ( pPV && pObj->getSdrPageFromSdrObject() == pPV->GetPage() )
+ {
+ pDrawView->MarkObj( pObj, pPV );
+ bRet = true;
+ }
+ }
+ }
+ else // Shape-Collection (xShapeColl is not 0)
+ {
+ // We'll switch to the sheet where the first object is
+ // and select all objects on that sheet
+ //!?throw exception when objects are on different sheets?
+
+ tools::Long nCount = xShapeColl->getCount();
+ if (nCount)
+ {
+ SdrPageView* pPV = nullptr;
+ bool bAllMarked(true);
+ for ( tools::Long i = 0; i < nCount; i++ )
+ {
+ uno::Reference<drawing::XShape> xShapeInt(xShapeColl->getByIndex(i), uno::UNO_QUERY);
+ if (xShapeInt.is())
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShapeInt );
+ if (pObj)
+ {
+ if (!bDrawSelModeSet && (pObj->GetLayer() == SC_LAYER_BACK))
+ {
+ pViewSh->SetDrawSelMode(true);
+ pViewSh->UpdateLayerLocks();
+ bDrawSelModeSet = true;
+ }
+ if (!pPV) // first object
+ {
+ lcl_ShowObject( *pViewSh, *pDrawView, pObj );
+ pPV = pDrawView->GetSdrPageView();
+ }
+ if ( pPV && pObj->getSdrPageFromSdrObject() == pPV->GetPage() )
+ {
+ if (pDrawView->IsObjMarkable( pObj, pPV ))
+ pDrawView->MarkObj( pObj, pPV );
+ else
+ bAllMarked = false;
+ }
+ }
+ }
+ }
+ if (bAllMarked)
+ bRet = true;
+ }
+ else
+ bRet = true; // empty XShapes (all shapes are deselected)
+ }
+
+ if (bRet)
+ pViewSh->SetDrawShell(true);
+ }
+ }
+
+ if (!bRet)
+ throw lang::IllegalArgumentException();
+
+ return bRet;
+}
+
+uno::Reference<drawing::XShapes> ScTabViewShell::getSelectedXShapes()
+{
+ uno::Reference<drawing::XShapes> xShapes;
+ SdrView* pSdrView = GetScDrawView();
+ if (pSdrView)
+ {
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+ if (nMarkCount)
+ {
+ // generate ShapeCollection (like in SdXImpressView::getSelection in Draw)
+ // XInterfaceRef will be returned and it has to be UsrObject-XInterface
+ xShapes = drawing::ShapeCollection::create(comphelper::getProcessComponentContext());
+
+ for (size_t i = 0; i < nMarkCount; ++i)
+ {
+ SdrObject* pDrawObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if (pDrawObj)
+ {
+ uno::Reference<drawing::XShape> xShape( pDrawObj->getUnoShape(), uno::UNO_QUERY );
+ if (xShape.is())
+ xShapes->add(xShape);
+ }
+ }
+ }
+ }
+ return xShapes;
+}
+
+uno::Any SAL_CALL ScTabViewObj::getSelection()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ rtl::Reference<ScCellRangesBase> pObj;
+ if (pViewSh)
+ {
+ // is something selected in drawing layer?
+ uno::Reference<uno::XInterface> xRet(pViewSh->getSelectedXShapes());
+ if (xRet.is())
+ return uno::Any(xRet);
+
+ // otherwise sheet (cell) selection
+
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabs = rMark.GetSelectCount();
+
+ ScRange aRange;
+ ScMarkType eMarkType = rViewData.GetSimpleArea(aRange);
+ if ( nTabs == 1 && (eMarkType == SC_MARK_SIMPLE) )
+ {
+ // tdf#154803 - check if range is entirely merged
+ ScDocument& rDoc = pDocSh->GetDocument();
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(aRange.aStart, ATTR_MERGE);
+ SCCOL nColSpan = 1;
+ SCROW nRowSpan = 1;
+ if (pMergeAttr && pMergeAttr->IsMerged())
+ {
+ nColSpan = pMergeAttr->GetColMerge();
+ nRowSpan = pMergeAttr->GetRowMerge();
+ }
+ // tdf#147122 - return cell object when a simple selection is entirely merged
+ if (aRange.aStart == aRange.aEnd
+ || (aRange.aEnd.Col() - aRange.aStart.Col() == nColSpan - 1
+ && aRange.aEnd.Row() - aRange.aStart.Row() == nRowSpan - 1))
+ pObj = new ScCellObj( pDocSh, aRange.aStart );
+ else
+ pObj = new ScCellRangeObj( pDocSh, aRange );
+ }
+ else if ( nTabs == 1 && (eMarkType == SC_MARK_SIMPLE_FILTERED) )
+ {
+ ScMarkData aFilteredMark( rMark );
+ ScViewUtil::UnmarkFiltered( aFilteredMark, pDocSh->GetDocument());
+ ScRangeList aRangeList;
+ aFilteredMark.FillRangeListWithMarks( &aRangeList, false);
+ // Theoretically a selection may start and end on a filtered row.
+ switch ( aRangeList.size() )
+ {
+ case 0:
+ // No unfiltered row, we have to return some object, so
+ // here is one with no ranges.
+ pObj = new ScCellRangesObj( pDocSh, aRangeList );
+ break;
+ case 1:
+ {
+ const ScRange& rRange = aRangeList[ 0 ];
+ if (rRange.aStart == rRange.aEnd)
+ pObj = new ScCellObj( pDocSh, rRange.aStart );
+ else
+ pObj = new ScCellRangeObj( pDocSh, rRange );
+ }
+ break;
+ default:
+ pObj = new ScCellRangesObj( pDocSh, aRangeList );
+ }
+ }
+ else // multiselection
+ {
+ ScRangeListRef xRanges;
+ rViewData.GetMultiArea( xRanges );
+
+ // if there are more sheets, copy ranges
+ //! should this happen in ScMarkData::FillRangeListWithMarks already?
+ if ( nTabs > 1 )
+ rMark.ExtendRangeListTables( xRanges.get() );
+
+ pObj = new ScCellRangesObj( pDocSh, *xRanges );
+ }
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ // remember if the selection was from the cursor position without anything selected
+ // (used when rendering the selection)
+
+ pObj->SetCursorOnly( true );
+ }
+ }
+
+ return uno::Any(uno::Reference(cppu::getXWeak(pObj.get())));
+}
+
+// XEnumerationAccess
+
+uno::Reference<container::XEnumeration> SAL_CALL ScTabViewObj::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return new ScIndexEnumeration(this, "com.sun.star.sheet.SpreadsheetViewPanesEnumeration");
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScTabViewObj::getCount()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ sal_uInt16 nPanes = 0;
+ if (pViewSh)
+ {
+ nPanes = 1;
+ ScViewData& rViewData = pViewSh->GetViewData();
+ if ( rViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ nPanes *= 2;
+ if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ nPanes *= 2;
+ }
+ return nPanes;
+}
+
+uno::Any SAL_CALL ScTabViewObj::getByIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<sheet::XViewPane> xPane(GetObjectByIndex_Impl(static_cast<sal_uInt16>(nIndex)));
+ if (!xPane.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(xPane);
+}
+
+uno::Type SAL_CALL ScTabViewObj::getElementType()
+{
+ return cppu::UnoType<sheet::XViewPane>::get();
+}
+
+sal_Bool SAL_CALL ScTabViewObj::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return ( getCount() != 0 );
+}
+
+// XSpreadsheetView
+
+rtl::Reference<ScViewPaneObj> ScTabViewObj::GetObjectByIndex_Impl(sal_uInt16 nIndex) const
+{
+ static const ScSplitPos ePosHV[4] =
+ { SC_SPLIT_TOPLEFT, SC_SPLIT_BOTTOMLEFT, SC_SPLIT_TOPRIGHT, SC_SPLIT_BOTTOMRIGHT };
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScSplitPos eWhich = SC_SPLIT_BOTTOMLEFT; // default position
+ bool bError = false;
+ ScViewData& rViewData = pViewSh->GetViewData();
+ bool bHor = ( rViewData.GetHSplitMode() != SC_SPLIT_NONE );
+ bool bVer = ( rViewData.GetVSplitMode() != SC_SPLIT_NONE );
+ if ( bHor && bVer )
+ {
+ // bottom left, bottom right, top left, top right - like in Excel
+ if ( nIndex < 4 )
+ eWhich = ePosHV[nIndex];
+ else
+ bError = true;
+ }
+ else if ( bHor )
+ {
+ if ( nIndex > 1 )
+ bError = true;
+ else if ( nIndex == 1 )
+ eWhich = SC_SPLIT_BOTTOMRIGHT;
+ // otherwise SC_SPLIT_BOTTOMLEFT
+ }
+ else if ( bVer )
+ {
+ if ( nIndex > 1 )
+ bError = true;
+ else if ( nIndex == 0 )
+ eWhich = SC_SPLIT_TOPLEFT;
+ // otherwise SC_SPLIT_BOTTOMLEFT
+ }
+ else if ( nIndex > 0 )
+ bError = true; // not split: only 0 is valid
+
+ if (!bError)
+ return new ScViewPaneObj( pViewSh, sal::static_int_cast<sal_uInt16>(eWhich) );
+ }
+
+ return nullptr;
+}
+
+uno::Reference<sheet::XSpreadsheet> SAL_CALL ScTabViewObj::getActiveSheet()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ SCTAB nTab = rViewData.GetTabNo();
+ return new ScTableSheetObj( rViewData.GetDocShell(), nTab );
+ }
+ return nullptr;
+}
+
+// support expand (but not replace) the active sheet
+void SAL_CALL ScTabViewObj::setActiveSheet( const uno::Reference<sheet::XSpreadsheet>& xActiveSheet )
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("setActiveSheet");
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ if ( !(pViewSh && xActiveSheet.is()) )
+ return;
+
+ // XSpreadsheet and ScCellRangesBase -> has to be the same sheet
+
+ ScCellRangesBase* pRangesImp = dynamic_cast<ScCellRangesBase*>( xActiveSheet.get() );
+ if ( pRangesImp && pViewSh->GetViewData().GetDocShell() == pRangesImp->GetDocShell() )
+ {
+ const ScRangeList& rRanges = pRangesImp->GetRangeList();
+ if ( rRanges.size() == 1 )
+ {
+ SCTAB nNewTab = rRanges[ 0 ].aStart.Tab();
+ if ( pViewSh->GetViewData().GetDocument().HasTable(nNewTab) )
+ pViewSh->SetTabNo( nNewTab );
+ }
+ }
+}
+
+uno::Reference< uno::XInterface > ScTabViewObj::GetClickedObject(const Point& rPoint) const
+{
+ uno::Reference< uno::XInterface > xTarget;
+ if (GetViewShell())
+ {
+ SCCOL nX;
+ SCROW nY;
+ ScViewData& rData = GetViewShell()->GetViewData();
+ ScSplitPos eSplitMode = rData.GetActivePart();
+ SCTAB nTab(rData.GetTabNo());
+ rData.GetPosFromPixel( rPoint.X(), rPoint.Y(), eSplitMode, nX, nY);
+
+ ScAddress aCellPos (nX, nY, nTab);
+ rtl::Reference<ScCellObj> pCellObj = new ScCellObj(rData.GetDocShell(), aCellPos);
+
+ xTarget.set(uno::Reference<table::XCell>(pCellObj), uno::UNO_QUERY);
+
+ ScDocument& rDoc = rData.GetDocument();
+ if (ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer())
+ {
+ SdrPage* pDrawPage = nullptr;
+ if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
+ pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+
+ SdrView* pDrawView = GetViewShell()->GetScDrawView();
+
+ if (pDrawPage && pDrawView && pDrawView->GetSdrPageView())
+ {
+ vcl::Window* pActiveWin = rData.GetActiveWin();
+ Point aPos = pActiveWin->PixelToLogic(rPoint);
+
+ double fHitLog = pActiveWin->PixelToLogic(Size(pDrawView->GetHitTolerancePixel(),0)).Width();
+
+ for (const rtl::Reference<SdrObject>& pObj : *pDrawPage)
+ {
+ if (SdrObjectPrimitiveHit(*pObj, aPos, {fHitLog, fHitLog}, *pDrawView->GetSdrPageView(), nullptr, false))
+ {
+ xTarget.set(pObj->getUnoShape(), uno::UNO_QUERY);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return xTarget;
+}
+
+bool ScTabViewObj::IsMouseListening() const
+{
+ if ( !aMouseClickHandlers.empty() )
+ return true;
+
+ // also include sheet events, because MousePressed must be called for them
+ ScViewData& rViewData = GetViewShell()->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ return
+ rDoc.HasSheetEventScript( nTab, ScSheetEventId::RIGHTCLICK, true ) ||
+ rDoc.HasSheetEventScript( nTab, ScSheetEventId::DOUBLECLICK, true ) ||
+ rDoc.HasSheetEventScript( nTab, ScSheetEventId::SELECT, true );
+
+}
+
+bool ScTabViewObj::MousePressed( const awt::MouseEvent& e )
+{
+ bool bReturn(false);
+ if ( e.Buttons == css::awt::MouseButton::LEFT )
+ mbLeftMousePressed = true;
+
+ uno::Reference< uno::XInterface > xTarget = GetClickedObject(Point(e.X, e.Y));
+ if (!aMouseClickHandlers.empty() && xTarget.is())
+ {
+ awt::EnhancedMouseEvent aMouseEvent;
+
+ aMouseEvent.Buttons = e.Buttons;
+ aMouseEvent.X = e.X;
+ aMouseEvent.Y = e.Y;
+ aMouseEvent.ClickCount = e.ClickCount;
+ aMouseEvent.PopupTrigger = e.PopupTrigger;
+ aMouseEvent.Target = xTarget;
+ aMouseEvent.Modifiers = e.Modifiers;
+
+ // Listener's handler may remove it from the listeners list
+ for (size_t i = aMouseClickHandlers.size(); i > 0; --i)
+ {
+ try
+ {
+ if (!aMouseClickHandlers[i - 1]->mousePressed(aMouseEvent))
+ bReturn = true;
+ }
+ catch ( uno::Exception& )
+ {
+ aMouseClickHandlers.erase(aMouseClickHandlers.begin() + (i - 1));
+ }
+ }
+ }
+
+ // handle sheet events
+ bool bDoubleClick = ( e.Buttons == awt::MouseButton::LEFT && e.ClickCount == 2 );
+ bool bRightClick = ( e.Buttons == awt::MouseButton::RIGHT && e.ClickCount == 1 );
+ if ( ( bDoubleClick || bRightClick ) && !bReturn && xTarget.is())
+ {
+ ScSheetEventId nEvent = bDoubleClick ? ScSheetEventId::DOUBLECLICK : ScSheetEventId::RIGHTCLICK;
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab);
+ if (pEvents)
+ {
+ const OUString* pScript = pEvents->GetScript(nEvent);
+ if (pScript)
+ {
+ // the macro parameter is the clicked object, as in the mousePressed call above
+ uno::Sequence<uno::Any> aParams{ uno::Any(xTarget) };
+
+ uno::Any aRet;
+ uno::Sequence<sal_Int16> aOutArgsIndex;
+ uno::Sequence<uno::Any> aOutArgs;
+
+ /*ErrCode eRet =*/ pDocSh->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs );
+
+ // look for a boolean return value of true
+ bool bRetValue = false;
+ if ((aRet >>= bRetValue) && bRetValue)
+ bReturn = true;
+ }
+ }
+
+ // execute VBA event handler
+ if (!bReturn && xTarget.is()) try
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ // the parameter is the clicked object, as in the mousePressed call above
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xTarget) };
+ xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( nEvent ), aArgs );
+ }
+ catch( util::VetoException& )
+ {
+ bReturn = true;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+
+ return bReturn;
+}
+
+bool ScTabViewObj::MouseReleased( const awt::MouseEvent& e )
+{
+ if ( e.Buttons == css::awt::MouseButton::LEFT )
+ {
+ try
+ {
+ ScTabViewShell* pViewSh = GetViewShell();
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs{ getSelection() };
+ xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( ScSheetEventId::SELECT ), aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ mbLeftMousePressed = false;
+ }
+
+ bool bReturn(false);
+
+ if (!aMouseClickHandlers.empty())
+ {
+ uno::Reference< uno::XInterface > xTarget = GetClickedObject(Point(e.X, e.Y));
+
+ if (xTarget.is())
+ {
+ awt::EnhancedMouseEvent aMouseEvent;
+
+ aMouseEvent.Buttons = e.Buttons;
+ aMouseEvent.X = e.X;
+ aMouseEvent.Y = e.Y;
+ aMouseEvent.ClickCount = e.ClickCount;
+ aMouseEvent.PopupTrigger = e.PopupTrigger;
+ aMouseEvent.Target = xTarget;
+ aMouseEvent.Modifiers = e.Modifiers;
+
+ // Listener's handler may remove it from the listeners list
+ for (size_t i = aMouseClickHandlers.size(); i > 0; --i)
+ {
+ try
+ {
+ if (!aMouseClickHandlers[i - 1]->mouseReleased( aMouseEvent ))
+ bReturn = true;
+ }
+ catch ( uno::Exception& )
+ {
+ aMouseClickHandlers.erase(aMouseClickHandlers.begin() + (i - 1));
+ }
+ }
+ }
+ }
+ return bReturn;
+}
+
+// XEnhancedMouseClickBroadcaster
+
+void ScTabViewObj::EndMouseListening()
+{
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+ for (const auto& rListener : aMouseClickHandlers)
+ {
+ try
+ {
+ rListener->disposing(aEvent);
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ aMouseClickHandlers.clear();
+}
+
+void ScTabViewObj::EndActivationListening()
+{
+ lang::EventObject aEvent;
+ aEvent.Source = getXWeak();
+ for (const auto& rListener : aActivationListeners)
+ {
+ try
+ {
+ rListener->disposing(aEvent);
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ aActivationListeners.clear();
+}
+
+void SAL_CALL ScTabViewObj::addEnhancedMouseClickHandler( const uno::Reference< awt::XEnhancedMouseClickHandler >& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ if (aListener.is())
+ {
+ aMouseClickHandlers.push_back( aListener );
+ }
+}
+
+void SAL_CALL ScTabViewObj::removeEnhancedMouseClickHandler( const uno::Reference< awt::XEnhancedMouseClickHandler >& aListener )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nCount = aMouseClickHandlers.size();
+ std::erase(aMouseClickHandlers, aListener);
+ if (aMouseClickHandlers.empty() && (nCount > 0)) // only if last listener removed
+ EndMouseListening();
+}
+
+// XActivationBroadcaster
+
+void SAL_CALL ScTabViewObj::addActivationEventListener( const uno::Reference< sheet::XActivationEventListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ if (aListener.is())
+ {
+ aActivationListeners.push_back( aListener );
+ }
+}
+
+void SAL_CALL ScTabViewObj::removeActivationEventListener( const uno::Reference< sheet::XActivationEventListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+ sal_uInt16 nCount = aActivationListeners.size();
+ std::erase(aActivationListeners, aListener);
+ if (aActivationListeners.empty() && (nCount > 0)) // only if last listener removed
+ EndActivationListening();
+}
+
+sal_Int16 ScTabViewObj::GetZoom() const
+{
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ const Fraction& rZoomY = pViewSh->GetViewData().GetZoomY(); // Y will be shown
+ return static_cast<sal_Int16>(tools::Long( rZoomY * 100 ));
+ }
+ return 0;
+}
+
+void ScTabViewObj::SetZoom(sal_Int16 nZoom)
+{
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (!pViewSh)
+ return;
+
+ if ( nZoom != GetZoom() && nZoom != 0 )
+ {
+ if (!pViewSh->GetViewData().IsPagebreakMode())
+ {
+ ScModule* pScMod = SC_MOD();
+ ScAppOptions aNewOpt(pScMod->GetAppOptions());
+ aNewOpt.SetZoom( nZoom );
+ aNewOpt.SetZoomType( pViewSh->GetViewData().GetView()->GetZoomType() );
+ pScMod->SetAppOptions( aNewOpt );
+ }
+ }
+ Fraction aFract( nZoom, 100 );
+ pViewSh->SetZoom( aFract, aFract, true );
+ pViewSh->PaintGrid();
+ pViewSh->PaintTop();
+ pViewSh->PaintLeft();
+ pViewSh->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOM );
+ pViewSh->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER );
+ pViewSh->GetViewFrame().GetBindings().Invalidate(SID_ZOOM_IN);
+ pViewSh->GetViewFrame().GetBindings().Invalidate(SID_ZOOM_OUT);
+}
+
+sal_Int16 ScTabViewObj::GetZoomType() const
+{
+ sal_Int16 aZoomType = view::DocumentZoomType::OPTIMAL;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ SvxZoomType eZoomType = pViewSh->GetViewData().GetView()->GetZoomType();
+ switch (eZoomType)
+ {
+ case SvxZoomType::PERCENT:
+ aZoomType = view::DocumentZoomType::BY_VALUE;
+ break;
+ case SvxZoomType::OPTIMAL:
+ aZoomType = view::DocumentZoomType::OPTIMAL;
+ break;
+ case SvxZoomType::WHOLEPAGE:
+ aZoomType = view::DocumentZoomType::ENTIRE_PAGE;
+ break;
+ case SvxZoomType::PAGEWIDTH:
+ aZoomType = view::DocumentZoomType::PAGE_WIDTH;
+ break;
+ case SvxZoomType::PAGEWIDTH_NOBORDER:
+ aZoomType = view::DocumentZoomType::PAGE_WIDTH_EXACT;
+ break;
+ }
+ }
+ return aZoomType;
+}
+
+void ScTabViewObj::SetZoomType(sal_Int16 aZoomType)
+{
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (!pViewSh)
+ return;
+
+ ScDBFunc* pView = pViewSh->GetViewData().GetView();
+ if (!pView)
+ return;
+
+ SvxZoomType eZoomType;
+ switch (aZoomType)
+ {
+ case view::DocumentZoomType::BY_VALUE:
+ eZoomType = SvxZoomType::PERCENT;
+ break;
+ case view::DocumentZoomType::OPTIMAL:
+ eZoomType = SvxZoomType::OPTIMAL;
+ break;
+ case view::DocumentZoomType::ENTIRE_PAGE:
+ eZoomType = SvxZoomType::WHOLEPAGE;
+ break;
+ case view::DocumentZoomType::PAGE_WIDTH:
+ eZoomType = SvxZoomType::PAGEWIDTH;
+ break;
+ case view::DocumentZoomType::PAGE_WIDTH_EXACT:
+ eZoomType = SvxZoomType::PAGEWIDTH_NOBORDER;
+ break;
+ default:
+ eZoomType = SvxZoomType::OPTIMAL;
+ }
+ sal_Int16 nZoom(GetZoom());
+ sal_Int16 nOldZoom(nZoom);
+ if ( eZoomType == SvxZoomType::PERCENT )
+ {
+ if ( nZoom < MINZOOM ) nZoom = MINZOOM;
+ if ( nZoom > MAXZOOM ) nZoom = MAXZOOM;
+ }
+ else
+ nZoom = pView->CalcZoom( eZoomType, nOldZoom );
+
+ switch ( eZoomType )
+ {
+ case SvxZoomType::WHOLEPAGE:
+ case SvxZoomType::PAGEWIDTH:
+ pView->SetZoomType( eZoomType, true );
+ break;
+
+ default:
+ pView->SetZoomType( SvxZoomType::PERCENT, true );
+ }
+ SetZoom( nZoom );
+}
+
+sal_Bool SAL_CALL ScTabViewObj::getIsWindowSplit()
+{
+ SolarMutexGuard aGuard;
+ // what menu slot SID_WINDOW_SPLIT does
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ return ( rViewData.GetHSplitMode() == SC_SPLIT_NORMAL ||
+ rViewData.GetVSplitMode() == SC_SPLIT_NORMAL );
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL ScTabViewObj::hasFrozenPanes()
+{
+ SolarMutexGuard aGuard;
+ // what menu slot SID_WINDOW_FIX does
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ return ( rViewData.GetHSplitMode() == SC_SPLIT_FIX ||
+ rViewData.GetVSplitMode() == SC_SPLIT_FIX );
+ }
+
+ return false;
+}
+
+sal_Int32 SAL_CALL ScTabViewObj::getSplitHorizontal()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ if ( rViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ return rViewData.GetHSplitPos();
+ }
+ return 0;
+}
+
+sal_Int32 SAL_CALL ScTabViewObj::getSplitVertical()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ return rViewData.GetVSplitPos();
+ }
+ return 0;
+}
+
+sal_Int32 SAL_CALL ScTabViewObj::getSplitColumn()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ if ( rViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ {
+ tools::Long nSplit = rViewData.GetHSplitPos();
+
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ ePos = SC_SPLIT_TOPLEFT;
+
+ SCCOL nCol;
+ SCROW nRow;
+ rViewData.GetPosFromPixel( nSplit, 0, ePos, nCol, nRow, false );
+ if ( nCol > 0 )
+ return nCol;
+ }
+ }
+ return 0;
+}
+
+sal_Int32 SAL_CALL ScTabViewObj::getSplitRow()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ if ( rViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ tools::Long nSplit = rViewData.GetVSplitPos();
+
+ // split vertically
+ SCCOL nCol;
+ SCROW nRow;
+ rViewData.GetPosFromPixel( 0, nSplit, SC_SPLIT_TOPLEFT, nCol, nRow, false );
+ if ( nRow > 0 )
+ return nRow;
+ }
+ }
+ return 0;
+}
+
+void SAL_CALL ScTabViewObj::splitAtPosition( sal_Int32 nPixelX, sal_Int32 nPixelY )
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ pViewSh->SplitAtPixel( Point( nPixelX, nPixelY ) );
+ pViewSh->FreezeSplitters( false );
+ pViewSh->InvalidateSplit();
+ }
+}
+
+void SAL_CALL ScTabViewObj::freezeAtPosition( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (!pViewSh)
+ return;
+
+ // first, remove them all -> no stress with scrolling in the meantime
+
+ pViewSh->RemoveSplit();
+
+ Point aWinStart;
+ vcl::Window* pWin = pViewSh->GetWindowByPos( SC_SPLIT_BOTTOMLEFT );
+ if (pWin)
+ aWinStart = pWin->GetPosPixel();
+
+ ScViewData& rViewData = pViewSh->GetViewData();
+ Point aSplit(rViewData.GetScrPos( static_cast<SCCOL>(nColumns), static_cast<SCROW>(nRows), SC_SPLIT_BOTTOMLEFT, true ));
+ aSplit += aWinStart;
+
+ pViewSh->SplitAtPixel( aSplit );
+ pViewSh->FreezeSplitters( true );
+ pViewSh->InvalidateSplit();
+}
+
+void SAL_CALL ScTabViewObj::addSelectionChangeListener(
+ const uno::Reference<view::XSelectionChangeListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ aSelectionChgListeners.push_back( xListener );
+}
+
+void SAL_CALL ScTabViewObj::removeSelectionChangeListener(
+ const uno::Reference< view::XSelectionChangeListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ auto it = std::find(aSelectionChgListeners.begin(), aSelectionChgListeners.end(), xListener); //! why the hassle with queryInterface?
+ if (it != aSelectionChgListeners.end())
+ aSelectionChgListeners.erase(it);
+}
+
+void ScTabViewObj::SelectionChanged()
+{
+ // Selection changed so end any style preview
+ // Note: executing this slot through the dispatcher
+ // will cause the style dialog to be raised so we go
+ // direct here
+ ScFormatShell aShell( GetViewShell()->GetViewData() );
+ SfxAllItemSet reqList( SfxGetpApp()->GetPool() );
+ SfxRequest aReq( SID_STYLE_END_PREVIEW, SfxCallMode::SLOT, reqList );
+ aShell.ExecuteStyle( aReq );
+ lang::EventObject aEvent;
+ aEvent.Source.set(getXWeak());
+ for (const auto& rListener : aSelectionChgListeners)
+ rListener->selectionChanged( aEvent );
+
+ // handle sheet events
+ ScTabViewShell* pViewSh = GetViewShell();
+ ScViewData& rViewData = pViewSh->GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ const ScSheetEvents* pEvents = rDoc.GetSheetEvents(nTab);
+ if (pEvents)
+ {
+ const OUString* pScript = pEvents->GetScript(ScSheetEventId::SELECT);
+ if (pScript)
+ {
+ // the macro parameter is the selection as returned by getSelection
+ uno::Sequence<uno::Any> aParams{ getSelection() };
+ uno::Any aRet;
+ uno::Sequence<sal_Int16> aOutArgsIndex;
+ uno::Sequence<uno::Any> aOutArgs;
+ /*ErrCode eRet =*/ pDocSh->CallXScript( *pScript, aParams, aRet, aOutArgsIndex, aOutArgs );
+ }
+ }
+
+ SfxApplication::Get()->Broadcast( SfxHint( SfxHintId::ScSelectionChanged ) );
+
+ if ( mbLeftMousePressed ) // selection still in progress
+ return;
+
+ try
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( rDoc.GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs{ getSelection() };
+ xVbaEvents->processVbaEvent( ScSheetEvents::GetVbaSheetEventId( ScSheetEventId::SELECT ), aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+// XPropertySet (view options)
+//! provide those also in application?
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL ScTabViewObj::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ static uno::Reference<beans::XPropertySetInfo> aRef(
+ new SfxItemPropertySetInfo( aPropSet.getPropertyMap() ));
+ return aRef;
+}
+
+void SAL_CALL ScTabViewObj::setPropertyValue(
+ const OUString& aPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ if ( aPropertyName == SC_UNO_FILTERED_RANGE_SELECTION )
+ {
+ bFilteredRangeSelection = ScUnoHelpFunctions::GetBoolFromAny(aValue);
+ return;
+ }
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (!pViewSh)
+ return;
+
+ ScViewData& rViewData = pViewSh->GetViewData();
+ const ScViewOptions& rOldOpt = pViewSh->GetViewData().GetOptions();
+ ScViewOptions aNewOpt(rOldOpt);
+
+ if ( aPropertyName == SC_UNO_COLROWHDR || aPropertyName == OLD_UNO_COLROWHDR )
+ aNewOpt.SetOption( VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_HORSCROLL || aPropertyName == OLD_UNO_HORSCROLL )
+ aNewOpt.SetOption( VOPT_HSCROLL, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_OUTLSYMB || aPropertyName == OLD_UNO_OUTLSYMB )
+ aNewOpt.SetOption( VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHEETTABS || aPropertyName == OLD_UNO_SHEETTABS )
+ aNewOpt.SetOption( VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWANCHOR )
+ aNewOpt.SetOption( VOPT_ANCHOR, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWFORM )
+ aNewOpt.SetOption( VOPT_FORMULAS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWGRID )
+ aNewOpt.SetOption( VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWHELP )
+ aNewOpt.SetOption( VOPT_HELPLINES, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWNOTES )
+ aNewOpt.SetOption( VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS )
+ aNewOpt.SetOption( VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWPAGEBR )
+ aNewOpt.SetOption( VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWZERO )
+ aNewOpt.SetOption( VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_VALUEHIGH || aPropertyName == OLD_UNO_VALUEHIGH )
+ aNewOpt.SetOption( VOPT_SYNTAX, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_VERTSCROLL || aPropertyName == OLD_UNO_VERTSCROLL )
+ aNewOpt.SetOption( VOPT_VSCROLL, ScUnoHelpFunctions::GetBoolFromAny( aValue ) );
+ else if ( aPropertyName == SC_UNO_SHOWOBJ )
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ {
+ //#i80528# adapt to new range eventually
+ if(sal_Int16(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int16(VOBJ_MODE_SHOW);
+
+ aNewOpt.SetObjMode( VOBJ_TYPE_OLE, static_cast<ScVObjMode>(nIntVal));
+ }
+ }
+ else if ( aPropertyName == SC_UNO_SHOWCHARTS )
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ {
+ //#i80528# adapt to new range eventually
+ if(sal_Int16(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int16(VOBJ_MODE_SHOW);
+
+ aNewOpt.SetObjMode( VOBJ_TYPE_CHART, static_cast<ScVObjMode>(nIntVal));
+ }
+ }
+ else if ( aPropertyName == SC_UNO_SHOWDRAW )
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ {
+ //#i80528# adapt to new range eventually
+ if(sal_Int16(VOBJ_MODE_HIDE) < nIntVal) nIntVal = sal_Int16(VOBJ_MODE_SHOW);
+
+ aNewOpt.SetObjMode( VOBJ_TYPE_DRAW, static_cast<ScVObjMode>(nIntVal));
+ }
+ }
+ else if ( aPropertyName == SC_UNO_GRIDCOLOR )
+ {
+ Color nIntVal;
+ if ( aValue >>= nIntVal )
+ aNewOpt.SetGridColor( nIntVal, OUString() );
+ }
+ else if ( aPropertyName == SC_UNO_ZOOMTYPE )
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ SetZoomType(nIntVal);
+ }
+ else if ( aPropertyName == SC_UNO_ZOOMVALUE )
+ {
+ sal_Int16 nIntVal = 0;
+ if ( aValue >>= nIntVal )
+ SetZoom(nIntVal);
+ }
+ else if ( aPropertyName == SC_UNO_FORMULABARHEIGHT )
+ {
+ sal_Int16 nIntVal = ScUnoHelpFunctions::GetInt16FromAny(aValue);
+ if (nIntVal > 0)
+ {
+ rViewData.SetFormulaBarLines(nIntVal);
+ // Notify formula bar about changed lines
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if (pInputHdl)
+ {
+ ScInputWindow* pInputWin = pInputHdl->GetInputWindow();
+ if (pInputWin)
+ pInputWin->NumLinesChanged();
+ }
+ }
+ }
+
+ // Options are set on the view and document (for new views),
+ // so that they remain during saving.
+ //! In the app (module) we need an extra options to tune that
+ //! (for new documents)
+
+ if ( aNewOpt == rOldOpt )
+ return;
+
+ rViewData.SetOptions( aNewOpt );
+ rViewData.GetDocument().SetViewOptions( aNewOpt );
+ rViewData.GetDocShell()->SetDocumentModified(); //! really?
+
+ pViewSh->UpdateFixPos();
+ pViewSh->PaintGrid();
+ pViewSh->PaintTop();
+ pViewSh->PaintLeft();
+ pViewSh->PaintExtras();
+ pViewSh->InvalidateBorder();
+
+ SfxBindings& rBindings = pViewSh->GetViewFrame().GetBindings();
+ rBindings.Invalidate( FID_TOGGLEHEADERS ); // -> check in menu
+ rBindings.Invalidate( FID_TOGGLESYNTAX );
+}
+
+uno::Any SAL_CALL ScTabViewObj::getPropertyValue( const OUString& aPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if ( aPropertyName == SC_UNO_FILTERED_RANGE_SELECTION )
+ {
+ aRet <<= bFilteredRangeSelection;
+ return aRet;
+ }
+
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ {
+ ScViewData& rViewData = pViewSh->GetViewData();
+ const ScViewOptions& rOpt = rViewData.GetOptions();
+
+ if ( aPropertyName == SC_UNO_COLROWHDR || aPropertyName == OLD_UNO_COLROWHDR )
+ aRet <<= rOpt.GetOption( VOPT_HEADER );
+ else if ( aPropertyName == SC_UNO_HORSCROLL || aPropertyName == OLD_UNO_HORSCROLL )
+ aRet <<= rOpt.GetOption( VOPT_HSCROLL );
+ else if ( aPropertyName == SC_UNO_OUTLSYMB || aPropertyName == OLD_UNO_OUTLSYMB )
+ aRet <<= rOpt.GetOption( VOPT_OUTLINER );
+ else if ( aPropertyName == SC_UNO_SHEETTABS || aPropertyName == OLD_UNO_SHEETTABS )
+ aRet <<= rOpt.GetOption( VOPT_TABCONTROLS );
+ else if ( aPropertyName == SC_UNO_SHOWANCHOR ) aRet <<= rOpt.GetOption( VOPT_ANCHOR );
+ else if ( aPropertyName == SC_UNO_SHOWFORM ) aRet <<= rOpt.GetOption( VOPT_FORMULAS );
+ else if ( aPropertyName == SC_UNO_SHOWGRID ) aRet <<= rOpt.GetOption( VOPT_GRID );
+ else if ( aPropertyName == SC_UNO_SHOWHELP ) aRet <<= rOpt.GetOption( VOPT_HELPLINES );
+ else if ( aPropertyName == SC_UNO_SHOWNOTES ) aRet <<= rOpt.GetOption( VOPT_NOTES );
+ else if ( aPropertyName == SC_UNO_SHOWFORMULASMARKS ) aRet <<= rOpt.GetOption( VOPT_FORMULAS_MARKS );
+ else if ( aPropertyName == SC_UNO_SHOWPAGEBR ) aRet <<= rOpt.GetOption( VOPT_PAGEBREAKS );
+ else if ( aPropertyName == SC_UNO_SHOWZERO ) aRet <<= rOpt.GetOption( VOPT_NULLVALS );
+ else if ( aPropertyName == SC_UNO_VALUEHIGH || aPropertyName == OLD_UNO_VALUEHIGH )
+ aRet <<= rOpt.GetOption( VOPT_SYNTAX );
+ else if ( aPropertyName == SC_UNO_VERTSCROLL || aPropertyName == OLD_UNO_VERTSCROLL )
+ aRet <<= rOpt.GetOption( VOPT_VSCROLL );
+ else if ( aPropertyName == SC_UNO_SHOWOBJ ) aRet <<= static_cast<sal_Int16>( rOpt.GetObjMode( VOBJ_TYPE_OLE ) );
+ else if ( aPropertyName == SC_UNO_SHOWCHARTS ) aRet <<= static_cast<sal_Int16>( rOpt.GetObjMode( VOBJ_TYPE_CHART ) );
+ else if ( aPropertyName == SC_UNO_SHOWDRAW ) aRet <<= static_cast<sal_Int16>( rOpt.GetObjMode( VOBJ_TYPE_DRAW ) );
+ else if ( aPropertyName == SC_UNO_GRIDCOLOR ) aRet <<= rOpt.GetGridColor();
+ else if ( aPropertyName == SC_UNO_VISAREA ) aRet <<= GetVisArea();
+ else if ( aPropertyName == SC_UNO_ZOOMTYPE ) aRet <<= GetZoomType();
+ else if ( aPropertyName == SC_UNO_ZOOMVALUE ) aRet <<= GetZoom();
+ else if ( aPropertyName == SC_UNO_FORMULABARHEIGHT ) aRet <<= rViewData.GetFormulaBarLines();
+ else if ( aPropertyName == SC_UNO_VISAREASCREEN )
+ {
+ vcl::Window* pActiveWin = rViewData.GetActiveWin();
+ if ( pActiveWin )
+ {
+ AbsoluteScreenPixelRectangle aRect = pActiveWin->GetWindowExtentsAbsolute();
+ aRet <<= AWTRectangle( aRect );
+ }
+ }
+ }
+
+ return aRet;
+}
+
+void SAL_CALL ScTabViewObj::addPropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ aPropertyChgListeners.push_back( xListener );
+}
+
+void SAL_CALL ScTabViewObj::removePropertyChangeListener( const OUString& /* aPropertyName */,
+ const uno::Reference<beans::XPropertyChangeListener >& xListener )
+{
+ SolarMutexGuard aGuard;
+ auto it = std::find(aPropertyChgListeners.begin(), aPropertyChgListeners.end(), xListener); //! Why the nonsense with queryInterface?
+ if (it != aPropertyChgListeners.end())
+ aPropertyChgListeners.erase(it);
+}
+
+void SAL_CALL ScTabViewObj::addVetoableChangeListener( const OUString& /* PropertyName */,
+ const uno::Reference<beans::XVetoableChangeListener >& /* aListener */ )
+{
+}
+
+void SAL_CALL ScTabViewObj::removeVetoableChangeListener( const OUString& /* PropertyName */,
+ const uno::Reference<beans::XVetoableChangeListener >& /* aListener */ )
+{
+}
+
+void ScTabViewObj::VisAreaChanged()
+{
+ beans::PropertyChangeEvent aEvent;
+ aEvent.Source.set(getXWeak());
+ for (const auto& rListener : aPropertyChgListeners)
+ rListener->propertyChange( aEvent );
+}
+
+// XRangeSelection
+
+void SAL_CALL ScTabViewObj::startRangeSelection(
+ const uno::Sequence<beans::PropertyValue>& aArguments )
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (!pViewSh)
+ return;
+
+ OUString aInitVal, aTitle;
+ bool bCloseOnButtonUp = false;
+ bool bSingleCell = false;
+ bool bMultiSelection = false;
+
+ OUString aStrVal;
+ for (const beans::PropertyValue& rProp : aArguments)
+ {
+ OUString aPropName(rProp.Name);
+
+ if (aPropName == SC_UNONAME_CLOSEONUP )
+ bCloseOnButtonUp = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_TITLE )
+ {
+ if ( rProp.Value >>= aStrVal )
+ aTitle = aStrVal;
+ }
+ else if (aPropName == SC_UNONAME_INITVAL )
+ {
+ if ( rProp.Value >>= aStrVal )
+ aInitVal = aStrVal;
+ }
+ else if (aPropName == SC_UNONAME_SINGLECELL )
+ bSingleCell = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ else if (aPropName == SC_UNONAME_MULTISEL )
+ bMultiSelection = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value );
+ }
+
+ pViewSh->StartSimpleRefDialog( aTitle, aInitVal, bCloseOnButtonUp, bSingleCell, bMultiSelection );
+}
+
+void SAL_CALL ScTabViewObj::abortRangeSelection()
+{
+ SolarMutexGuard aGuard;
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (pViewSh)
+ pViewSh->StopSimpleRefDialog();
+}
+
+void SAL_CALL ScTabViewObj::addRangeSelectionListener(
+ const uno::Reference<sheet::XRangeSelectionListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ aRangeSelListeners.push_back( xListener );
+}
+
+void SAL_CALL ScTabViewObj::removeRangeSelectionListener(
+ const uno::Reference<sheet::XRangeSelectionListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ auto it = std::find(aRangeSelListeners.begin(), aRangeSelListeners.end(), xListener);
+ if (it != aRangeSelListeners.end())
+ aRangeSelListeners.erase(it);
+}
+
+void SAL_CALL ScTabViewObj::addRangeSelectionChangeListener(
+ const uno::Reference<sheet::XRangeSelectionChangeListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ aRangeChgListeners.push_back( xListener );
+}
+
+void SAL_CALL ScTabViewObj::removeRangeSelectionChangeListener(
+ const uno::Reference<sheet::XRangeSelectionChangeListener>& xListener )
+{
+ SolarMutexGuard aGuard;
+ auto it = std::find(aRangeChgListeners.begin(), aRangeChgListeners.end(), xListener);
+ if (it != aRangeChgListeners.end())
+ aRangeChgListeners.erase(it);
+}
+
+void ScTabViewObj::RangeSelDone( const OUString& rText )
+{
+ sheet::RangeSelectionEvent aEvent;
+ aEvent.Source.set(getXWeak());
+ aEvent.RangeDescriptor = rText;
+
+ // copy on the stack because listener could remove itself
+ auto const listeners(aRangeSelListeners);
+
+ for (const auto& rListener : listeners)
+ rListener->done( aEvent );
+}
+
+void ScTabViewObj::RangeSelAborted( const OUString& rText )
+{
+ sheet::RangeSelectionEvent aEvent;
+ aEvent.Source.set(getXWeak());
+ aEvent.RangeDescriptor = rText;
+
+ // copy on the stack because listener could remove itself
+ auto const listeners(aRangeSelListeners);
+
+ for (const auto& rListener : listeners)
+ rListener->aborted( aEvent );
+}
+
+void ScTabViewObj::RangeSelChanged( const OUString& rText )
+{
+ sheet::RangeSelectionEvent aEvent;
+ aEvent.Source.set(getXWeak());
+ aEvent.RangeDescriptor = rText;
+
+ // copy on the stack because listener could remove itself
+ auto const listener(aRangeChgListeners);
+
+ for (const auto& rListener : listener)
+ rListener->descriptorChanged( aEvent );
+}
+
+// XServiceInfo
+OUString SAL_CALL ScTabViewObj::getImplementationName()
+{
+ return "ScTabViewObj";
+}
+
+sal_Bool SAL_CALL ScTabViewObj::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL ScTabViewObj::getSupportedServiceNames()
+{
+ return {SCTABVIEWOBJ_SERVICE, SCVIEWSETTINGS_SERVICE};
+}
+
+// XUnoTunnel
+
+css::uno::Reference< css::datatransfer::XTransferable > SAL_CALL ScTabViewObj::getTransferable()
+{
+ SolarMutexGuard aGuard;
+ ScEditShell* pShell = dynamic_cast<ScEditShell*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) );
+ if (pShell)
+ return pShell->GetEditView()->GetTransferable();
+
+ ScDrawTextObjectBar* pTextShell = dynamic_cast<ScDrawTextObjectBar*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) );
+ if (pTextShell)
+ {
+ ScViewData& rViewData = GetViewShell()->GetViewData();
+ ScDrawView* pView = rViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ if (pOutView)
+ return pOutView->GetEditView().GetTransferable();
+ }
+
+ ScDrawShell* pDrawShell = dynamic_cast<ScDrawShell*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) );
+ if (pDrawShell)
+ return pDrawShell->GetDrawView()->CopyToTransferable();
+
+ return GetViewShell()->CopyToTransferable();
+}
+
+void SAL_CALL ScTabViewObj::insertTransferable( const css::uno::Reference< css::datatransfer::XTransferable >& xTrans )
+{
+ SolarMutexGuard aGuard;
+ ScEditShell* pShell = dynamic_cast<ScEditShell*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) );
+ if (pShell)
+ pShell->GetEditView()->InsertText( xTrans, OUString(), false );
+ else
+ {
+ ScDrawTextObjectBar* pTextShell = dynamic_cast<ScDrawTextObjectBar*>( GetViewShell()->GetViewFrame().GetDispatcher()->GetShell(0) );
+ if (pTextShell)
+ {
+ ScViewData& rViewData = GetViewShell()->GetViewData();
+ ScDrawView* pView = rViewData.GetScDrawView();
+ OutlinerView* pOutView = pView->GetTextEditOutlinerView();
+ if ( pOutView )
+ {
+ pOutView->GetEditView().InsertText( xTrans, OUString(), false );
+ return;
+ }
+ }
+
+ GetViewShell()->PasteFromTransferable( xTrans );
+ }
+}
+
+namespace {
+
+uno::Sequence<sal_Int32> toSequence(const ScMarkData::MarkedTabsType& rSelected)
+{
+ uno::Sequence<sal_Int32> aRet(rSelected.size());
+ auto aRetRange = asNonConstRange(aRet);
+ size_t i = 0;
+ for (const auto& rTab : rSelected)
+ {
+ aRetRange[i] = static_cast<sal_Int32>(rTab);
+ ++i;
+ }
+
+ return aRet;
+}
+
+}
+
+uno::Sequence<sal_Int32> ScTabViewObj::getSelectedSheets()
+{
+ ScTabViewShell* pViewSh = GetViewShell();
+ if (!pViewSh)
+ return uno::Sequence<sal_Int32>();
+
+ ScViewData& rViewData = pViewSh->GetViewData();
+
+ // #i95280# when printing from the shell, the view is never activated,
+ // so Excel view settings must also be evaluated here.
+ ScExtDocOptions* pExtOpt = rViewData.GetDocument().GetExtDocOptions();
+ if (pExtOpt && pExtOpt->IsChanged())
+ {
+ pViewSh->GetViewData().ReadExtOptions(*pExtOpt); // Excel view settings
+ pViewSh->SetTabNo(pViewSh->GetViewData().GetTabNo(), true);
+ pExtOpt->SetChanged(false);
+ }
+
+ return toSequence(rViewData.GetMarkData().GetSelectedTabs());
+}
+
+ScPreviewObj::ScPreviewObj(ScPreviewShell* pViewSh) :
+ SfxBaseController(pViewSh),
+ mpViewShell(pViewSh)
+{
+ if (mpViewShell)
+ StartListening(*mpViewShell);
+}
+
+ScPreviewObj::~ScPreviewObj()
+{
+ if (mpViewShell)
+ EndListening(*mpViewShell);
+}
+
+uno::Any ScPreviewObj::queryInterface(const uno::Type& rType)
+{
+ uno::Any aReturn = ::cppu::queryInterface(rType,
+ static_cast<sheet::XSelectedSheetsSupplier*>(this));
+ if ( aReturn.hasValue() )
+ return aReturn;
+ return SfxBaseController::queryInterface(rType);
+}
+
+void ScPreviewObj::acquire() noexcept
+{
+ SfxBaseController::acquire();
+}
+
+void ScPreviewObj::release() noexcept
+{
+ SfxBaseController::release();
+}
+
+void ScPreviewObj::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ mpViewShell = nullptr;
+}
+
+uno::Sequence<sal_Int32> ScPreviewObj::getSelectedSheets()
+{
+ ScPreview* p = mpViewShell ? mpViewShell->GetPreview() : nullptr;
+ if (!p)
+ return uno::Sequence<sal_Int32>();
+
+ return toSequence(p->GetSelectedTabs());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/warnpassword.cxx b/sc/source/ui/unoobj/warnpassword.cxx
new file mode 100644
index 0000000000..b8ccbffa2e
--- /dev/null
+++ b/sc/source/ui/unoobj/warnpassword.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <warnpassword.hxx>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <sfx2/docfile.hxx>
+#include <ucbhelper/simpleinteractionrequest.hxx>
+#include <com/sun/star/task/InteractionClassification.hpp>
+#include <com/sun/star/ucb/InteractiveAppException.hpp>
+#include <svx/svxerr.hxx>
+#include <rtl/ref.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::task::InteractionClassification_QUERY;
+using ::com::sun::star::task::XInteractionHandler;
+using ::com::sun::star::ucb::InteractiveAppException;
+
+bool ScWarnPassword::WarningOnPassword( SfxMedium& rMedium )
+{
+ bool bReturn = true;
+ Reference< XInteractionHandler > xHandler( rMedium.GetInteractionHandler());
+ if( xHandler.is() )
+ {
+ Any aException( InteractiveAppException("",
+ Reference <XInterface> (),
+ InteractionClassification_QUERY,
+ sal_uInt32(ERRCODE_SVX_EXPORT_FILTER_CRYPT)));
+
+ rtl::Reference< ucbhelper::SimpleInteractionRequest > xRequest
+ = new ucbhelper::SimpleInteractionRequest(
+ aException,
+ ContinuationFlags::Approve | ContinuationFlags::Disapprove );
+
+ xHandler->handle( xRequest );
+
+ const ContinuationFlags nResp = xRequest->getResponse();
+
+ if ( nResp == ContinuationFlags::Disapprove )
+ bReturn = false;
+ }
+ return bReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/excelvbahelper.cxx b/sc/source/ui/vba/excelvbahelper.cxx
new file mode 100644
index 0000000000..3224171677
--- /dev/null
+++ b/sc/source/ui/vba/excelvbahelper.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 "excelvbahelper.hxx"
+
+#include <basic/basmgr.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <osl/file.hxx>
+#include <tools/urlobj.hxx>
+#include <vbahelper/vbahelper.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/XSheetCellRange.hpp>
+#include <com/sun/star/sheet/GlobalSheetSettings.hpp>
+#include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+
+#include <document.hxx>
+#include <docuno.hxx>
+#include <tabvwsh.hxx>
+#include <transobj.hxx>
+#include <cellsuno.hxx>
+#include <gridwin.hxx>
+
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
+#include <com/sun/star/script/ModuleInfo.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+namespace ooo::vba::excel {
+
+uno::Reference< sheet::XUnnamedDatabaseRanges >
+GetUnnamedDataBaseRanges( const ScDocShell* pShell )
+{
+ uno::Reference< frame::XModel > xModel;
+ if ( pShell )
+ xModel.set( pShell->GetModel(), uno::UNO_SET_THROW );
+ uno::Reference< beans::XPropertySet > xModelProps( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XUnnamedDatabaseRanges > xUnnamedDBRanges( xModelProps->getPropertyValue("UnnamedDatabaseRanges"), uno::UNO_QUERY_THROW );
+ return xUnnamedDBRanges;
+}
+
+// returns the XDatabaseRange for the autofilter on sheet (nSheet)
+// also populates sName with the name of range
+uno::Reference< sheet::XDatabaseRange >
+GetAutoFiltRange( const ScDocShell* pShell, sal_Int16 nSheet )
+{
+ uno::Reference< sheet::XUnnamedDatabaseRanges > xUnnamedDBRanges( GetUnnamedDataBaseRanges( pShell ), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XDatabaseRange > xDataBaseRange;
+ if (xUnnamedDBRanges->hasByTable( nSheet ) )
+ {
+ uno::Reference< sheet::XDatabaseRange > xDBRange( xUnnamedDBRanges->getByTable( nSheet ) , uno::UNO_QUERY_THROW );
+ bool bHasAuto = false;
+ uno::Reference< beans::XPropertySet > xProps( xDBRange, uno::UNO_QUERY_THROW );
+ xProps->getPropertyValue("AutoFilter") >>= bHasAuto;
+ if ( bHasAuto )
+ {
+ xDataBaseRange=xDBRange;
+ }
+ }
+ return xDataBaseRange;
+}
+
+ScDocShell* GetDocShellFromRange( const uno::Reference< uno::XInterface >& xRange )
+{
+ ScCellRangesBase* pScCellRangesBase = dynamic_cast<ScCellRangesBase*>( xRange.get() );
+ if ( !pScCellRangesBase )
+ {
+ throw uno::RuntimeException("Failed to access underlying doc shell uno range object" );
+ }
+ return pScCellRangesBase->GetDocShell();
+}
+
+uno::Reference< XHelperInterface >
+getUnoSheetModuleObj( const uno::Reference< table::XCellRange >& xRange )
+{
+ uno::Reference< sheet::XSheetCellRange > xSheetRange( xRange, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xSheetRange->getSpreadsheet(), uno::UNO_SET_THROW );
+ return getUnoSheetModuleObj( xSheet );
+}
+
+void implSetZoom( const uno::Reference< frame::XModel >& xModel, sal_Int16 nZoom, std::vector< SCTAB >& nTabs )
+{
+ ScTabViewShell* pViewSh = excel::getBestViewShell( xModel );
+ Fraction aFract( nZoom, 100 );
+ pViewSh->GetViewData().SetZoom( aFract, aFract, nTabs );
+ pViewSh->RefreshZoom();
+}
+
+namespace {
+
+class PasteCellsWarningReseter
+{
+private:
+ bool bInitialWarningState;
+ /// @throws uno::RuntimeException
+ static uno::Reference< sheet::XGlobalSheetSettings > const & getGlobalSheetSettings()
+ {
+ static uno::Reference< sheet::XGlobalSheetSettings > xProps = sheet::GlobalSheetSettings::create( comphelper::getProcessComponentContext() );
+ return xProps;
+ }
+
+ /// @throws uno::RuntimeException
+ static bool getReplaceCellsWarning()
+ {
+ return getGlobalSheetSettings()->getReplaceCellsWarning();
+ }
+
+ /// @throws uno::RuntimeException
+ static void setReplaceCellsWarning( bool bState )
+ {
+ getGlobalSheetSettings()->setReplaceCellsWarning( bState );
+ }
+public:
+ /// @throws uno::RuntimeException
+ PasteCellsWarningReseter()
+ {
+ bInitialWarningState = getReplaceCellsWarning();
+ if ( bInitialWarningState )
+ setReplaceCellsWarning( false );
+ }
+ ~PasteCellsWarningReseter()
+ {
+ if ( bInitialWarningState )
+ {
+ // don't allow dtor to throw
+ try
+ {
+ setReplaceCellsWarning( true );
+ }
+ catch ( uno::Exception& /*e*/ ){}
+ }
+ }
+};
+
+}
+
+void
+implnPaste( const uno::Reference< frame::XModel>& xModel )
+{
+ PasteCellsWarningReseter resetWarningBox;
+ ScTabViewShell* pViewShell = getBestViewShell( xModel );
+ if ( pViewShell )
+ {
+ pViewShell->PasteFromSystem();
+ pViewShell->CellContentChanged();
+ }
+}
+
+void
+implnCopy( const uno::Reference< frame::XModel>& xModel )
+{
+ ScTabViewShell* pViewShell = getBestViewShell( xModel );
+ ScDocShell* pDocShell = getDocShell( xModel );
+ if ( !(pViewShell && pDocShell) )
+ return;
+
+ pViewShell->CopyToClip(nullptr,false,false,true);
+
+ // mark the copied transfer object so it is used in ScVbaRange::Insert
+ uno::Reference<datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin()));
+ ScTransferObj* pClipObj = ScTransferObj::GetOwnClipboard(xTransferable);
+ if (pClipObj)
+ {
+ pClipObj->SetUseInApi( true );
+ pDocShell->SetClipData(xTransferable);
+ }
+}
+
+void
+implnCut( const uno::Reference< frame::XModel>& xModel )
+{
+ ScTabViewShell* pViewShell = getBestViewShell( xModel );
+ ScDocShell* pDocShell = getDocShell( xModel );
+ if ( !(pViewShell && pDocShell) )
+ return;
+
+ pViewShell->CutToClip();
+
+ // mark the copied transfer object so it is used in ScVbaRange::Insert
+ uno::Reference<datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin()));
+ ScTransferObj* pClipObj = ScTransferObj::GetOwnClipboard(xTransferable);
+ if (pClipObj)
+ {
+ pClipObj->SetUseInApi( true );
+ pDocShell->SetClipData(xTransferable);
+ }
+}
+
+void implnPasteSpecial( const uno::Reference< frame::XModel>& xModel, InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bTranspose)
+{
+ PasteCellsWarningReseter resetWarningBox;
+
+ ScTabViewShell* pTabViewShell = getBestViewShell(xModel);
+ if (!pTabViewShell)
+ return;
+
+ ScDocShell* pDocShell = getDocShell(xModel);
+ if (!pDocShell)
+ return;
+
+ ScViewData& rView = pTabViewShell->GetViewData();
+ vcl::Window* pWin = rView.GetActiveWin();
+ if (!pWin)
+ return;
+
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if (pOwnClip)
+ {
+ pTabViewShell->PasteFromClip(nFlags, pOwnClip->GetDocument(),
+ nFunction, bSkipEmpty, bTranspose, false,
+ INS_NONE, InsertDeleteFlags::NONE, true);
+
+ pTabViewShell->CellContentChanged();
+ }
+}
+
+ScDocShell*
+getDocShell( const css::uno::Reference< css::frame::XModel>& xModel )
+{
+ uno::Reference< uno::XInterface > xIf( xModel, uno::UNO_QUERY_THROW );
+ ScModelObj* pModel = comphelper::getFromUnoTunnel< ScModelObj >( xIf );
+ ScDocShell* pDocShell = nullptr;
+ if ( pModel )
+ pDocShell = static_cast<ScDocShell*>(pModel->GetEmbeddedObject());
+ return pDocShell;
+
+}
+
+ScTabViewShell*
+getBestViewShell( const css::uno::Reference< css::frame::XModel>& xModel )
+{
+ ScDocShell* pDocShell = getDocShell( xModel );
+ if ( pDocShell )
+ return pDocShell->GetBestViewShell();
+ return nullptr;
+}
+
+ScTabViewShell*
+getCurrentBestViewShell( const uno::Reference< uno::XComponentContext >& xContext )
+{
+ uno::Reference< frame::XModel > xModel = getCurrentExcelDoc( xContext );
+ return getBestViewShell( xModel );
+}
+
+SfxViewFrame*
+getViewFrame( const uno::Reference< frame::XModel >& xModel )
+{
+ ScTabViewShell* pViewShell = getBestViewShell( xModel );
+ if ( pViewShell )
+ return &pViewShell->GetViewFrame();
+ return nullptr;
+}
+
+uno::Reference< XHelperInterface >
+getUnoSheetModuleObj( const uno::Reference< sheet::XSpreadsheet >& xSheet )
+{
+ uno::Reference< beans::XPropertySet > xProps( xSheet, uno::UNO_QUERY_THROW );
+ OUString sCodeName;
+ xProps->getPropertyValue("CodeName") >>= sCodeName;
+ // #TODO #FIXME ideally we should 'throw' here if we don't get a valid parent, but... it is possible
+ // to create a module ( and use 'Option VBASupport 1' ) for a calc document, in this scenario there
+ // are *NO* special document module objects ( of course being able to switch between vba/non vba mode at
+ // the document in the future could fix this, especially IF the switching of the vba mode takes care to
+ // create the special document module objects if they don't exist.
+ return getUnoDocModule( sCodeName, GetDocShellFromRange( xSheet ) );
+}
+
+uno::Reference< XHelperInterface >
+getUnoSheetModuleObj( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges )
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( xRanges, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
+ uno::Reference< table::XCellRange > xRange( xEnum->nextElement(), uno::UNO_QUERY_THROW );
+ return getUnoSheetModuleObj( xRange );
+}
+
+uno::Reference< XHelperInterface >
+getUnoSheetModuleObj( const uno::Reference< table::XCell >& xCell )
+{
+ uno::Reference< sheet::XSheetCellRange > xSheetRange( xCell, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xSheetRange->getSpreadsheet(), uno::UNO_SET_THROW );
+ return getUnoSheetModuleObj( xSheet );
+}
+
+uno::Reference< XHelperInterface >
+getUnoSheetModuleObj( const uno::Reference< frame::XModel >& xModel, SCTAB nTab )
+{
+ uno::Reference< sheet::XSpreadsheetDocument > xDoc( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xSheets( xDoc->getSheets(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xSheets->getByIndex( nTab ), uno::UNO_QUERY_THROW );
+ return getUnoSheetModuleObj( xSheet );
+}
+
+void setUpDocumentModules( const uno::Reference< sheet::XSpreadsheetDocument >& xDoc )
+{
+ uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
+ ScDocShell* pShell = excel::getDocShell( xModel );
+ if ( !pShell )
+ return;
+
+ OUString aPrjName( "Standard" );
+ pShell->GetBasicManager()->SetName( aPrjName );
+
+ /* Set library container to VBA compatibility mode. This will create
+ the VBA Globals object and store it in the Basic manager of the
+ document. */
+ uno::Reference<script::XLibraryContainer> xLibContainer = pShell->GetBasicContainer();
+ uno::Reference<script::vba::XVBACompatibility> xVBACompat( xLibContainer, uno::UNO_QUERY_THROW );
+ xVBACompat->setVBACompatibilityMode( true );
+
+ if( xLibContainer.is() )
+ {
+ if( !xLibContainer->hasByName( aPrjName ) )
+ xLibContainer->createLibrary( aPrjName );
+ uno::Any aLibAny = xLibContainer->getByName( aPrjName );
+ uno::Reference< container::XNameContainer > xLib;
+ aLibAny >>= xLib;
+ if( xLib.is() )
+ {
+ uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess( pShell->GetModel()->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), uno::UNO_QUERY_THROW );
+ // set up the module info for the workbook and sheets in the newly created
+ // spreadsheet
+ ScDocument& rDoc = pShell->GetDocument();
+ OUString sCodeName = rDoc.GetCodeName();
+ if ( sCodeName.isEmpty() )
+ {
+ sCodeName = "ThisWorkbook";
+ rDoc.SetCodeName( sCodeName );
+ }
+
+ std::vector< OUString > sDocModuleNames { sCodeName };
+
+ for ( SCTAB index = 0; index < rDoc.GetTableCount(); index++)
+ {
+ OUString aName;
+ rDoc.GetCodeName( index, aName );
+ sDocModuleNames.push_back( aName );
+ }
+
+ for ( const auto& rName : sDocModuleNames )
+ {
+ script::ModuleInfo sModuleInfo;
+
+ uno::Any aName= xVBACodeNamedObjectAccess->getByName( rName );
+ sModuleInfo.ModuleObject.set( aName, uno::UNO_QUERY );
+ sModuleInfo.ModuleType = script::ModuleType::DOCUMENT;
+ xVBAModuleInfo->insertModuleInfo( rName, sModuleInfo );
+ if( xLib->hasByName( rName ) )
+ xLib->replaceByName( rName, uno::Any( OUString( "Option VBASupport 1\n") ) );
+ else
+ xLib->insertByName( rName, uno::Any( OUString( "Option VBASupport 1\n" ) ) );
+ }
+ }
+ }
+
+ /* Trigger the Workbook_Open event, event processor will register
+ itself as listener for specific events. */
+ try
+ {
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( pShell->GetDocument().GetVbaEventProcessor(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs;
+ xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKBOOK_OPEN, aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ExportAsFixedFormatHelper(
+ const uno::Reference< frame::XModel >& xModel, const css::uno::Reference< XApplication >& xApplication,
+ const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish)
+{
+ OUString sType;
+ if ((Type >>= sType) && (sType.equalsIgnoreAsciiCase(u"xlTypeXPS") || sType == "1"))
+ {
+ /* xlTypePDF 0 "PDF" - Portable Document Format file(.pdf)
+ xlTypeXPS 1 "XPS" - XPS Document(.xps) --> not supported in LibreOffice */
+ return;
+ }
+
+ OUString sFileName;
+ FileName >>= sFileName;
+ OUString sRelURL;;
+ osl::FileBase::getFileURLFromSystemPath(sFileName, sRelURL);
+ // detect if there is no path then we need
+ // to use the current folder
+ INetURLObject aURL(sRelURL);
+ OUString sURL;
+ sURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ if (sURL.isEmpty())
+ {
+ // need to add cur dir ( of this workbook ) or else the 'Work' dir
+ sURL = xModel->getURL();
+
+ if (sURL.isEmpty())
+ {
+ // not path available from 'this' document
+ // need to add the 'document'/work directory then
+ OUString sWorkPath = xApplication->getDefaultFilePath();
+ OUString sWorkURL;
+ osl::FileBase::getFileURLFromSystemPath(sWorkPath, sWorkURL);
+ aURL.SetURL(sWorkURL);
+ }
+ else
+ {
+ if (!sFileName.isEmpty())
+ {
+ aURL.SetURL(INetURLObject::GetAbsURL(sURL, sRelURL));
+ }
+ else
+ {
+ aURL.SetURL(sURL);
+ if (aURL.removeExtension())
+ aURL.setExtension(u"pdf");
+ }
+ }
+ sURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+
+ }
+
+ sal_Int32 nTo = 0;
+ sal_Int32 nFrom = 0;
+ From >>= nFrom;
+ To >>= nTo;
+
+ OUString sRange("-");
+
+ css::uno::Sequence<css::beans::PropertyValue> aFilterData;
+ if (nFrom || nTo)
+ {
+ if (nFrom)
+ sRange = OUString::number(nFrom) + sRange;
+ if (nTo)
+ sRange += OUString::number(nTo);
+
+ aFilterData.realloc(aFilterData.getLength() + 1);
+ aFilterData.getArray()[aFilterData.getLength() - 1] = comphelper::makePropertyValue("PageRange", sRange);
+ }
+
+ OUString sQuality;
+ if (Quality >>= sQuality)
+ {
+ if (sQuality.equalsIgnoreAsciiCase(u"xlQualityMinimum") || sQuality == "1")
+ {
+ aFilterData.realloc(aFilterData.getLength() + 1);
+ aFilterData.getArray()[aFilterData.getLength() - 1] = comphelper::makePropertyValue("Quality", sal_Int32(70));
+ }
+ else if (sQuality.equalsIgnoreAsciiCase(u"xlQualityStandard") || sQuality == "0")
+ {
+ aFilterData.realloc(aFilterData.getLength() + 1);
+ aFilterData.getArray()[aFilterData.getLength() - 1] = comphelper::makePropertyValue("UseLosslessCompression", true);
+ }
+ else
+ {
+ /* Name Value Description
+ xlQualityMinimum 1 Minimum quality
+ xlQualityStandard 0 Standard quality */
+ }
+ }
+
+ // init set of params for storeToURL() call
+ css::uno::Sequence<css::beans::PropertyValue> storeProps{
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("FilterName", OUString("calc_pdf_Export")),
+ comphelper::makePropertyValue("URL", sURL)
+ };
+
+ bool bIncludeDocProperties = true;
+ if ((IncludeDocProperties >>= bIncludeDocProperties) && !bIncludeDocProperties)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(xModel, uno::UNO_QUERY);
+ if (xDPS.is())
+ {
+ uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
+ uno::Reference<util::XCloneable> xCloneable(xDocProps, uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xOldDocProps(xCloneable->createClone(), uno::UNO_QUERY_THROW);
+
+ // reset doc properties to default temporary
+ xDocProps->resetUserData(OUString());
+
+ uno::Reference< frame::XStorable > xStor(xModel, uno::UNO_QUERY_THROW);
+ try {
+ xStor->storeToURL(sURL, storeProps);
+ }
+ catch (const uno::Exception&)
+ {
+ SetDocInfoState(xModel, xOldDocProps);
+ throw;
+ }
+
+ SetDocInfoState(xModel, xOldDocProps);
+ }
+ }
+ else
+ {
+ uno::Reference< frame::XStorable > xStor(xModel, uno::UNO_QUERY_THROW);
+ xStor->storeToURL(sURL, storeProps);
+ }
+
+ bool bOpenAfterPublish = false;
+ if ((OpenAfterPublish >>= bOpenAfterPublish) && bOpenAfterPublish)
+ {
+ uno::Reference<css::system::XSystemShellExecute> xSystemShellExecute(css::system::SystemShellExecute::create(::comphelper::getProcessComponentContext()));
+ xSystemShellExecute->execute(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), "", css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+}
+
+void SetDocInfoState(
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Reference< css::document::XDocumentProperties>& i_xOldDocProps)
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> const
+ xModelDocPropsSupplier(xModel, uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> const xDocPropsToFill =
+ xModelDocPropsSupplier->getDocumentProperties();
+ uno::Reference< beans::XPropertySet > const xPropSet(
+ i_xOldDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+
+ uno::Reference< util::XModifiable > xModifiable(xModel, uno::UNO_QUERY);
+ if (!xModifiable.is())
+ throw uno::RuntimeException();
+
+ bool bIsModified = xModifiable->isModified();
+
+ try
+ {
+ uno::Reference< beans::XPropertySet > const xSet(
+ xDocPropsToFill->getUserDefinedProperties(), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertyContainer > xContainer(xSet, uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo();
+ const uno::Sequence< beans::Property > lProps = xSetInfo->getProperties();
+ for (const beans::Property& rProp : lProps)
+ {
+ uno::Any aValue = xPropSet->getPropertyValue(rProp.Name);
+ if (rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE)
+ {
+ try
+ {
+ // QUESTION: DefaultValue?!
+ xContainer->addProperty(rProp.Name, rProp.Attributes, aValue);
+ }
+ catch (beans::PropertyExistException const&) {}
+ try
+ {
+ // it is possible that the propertysets from XML and binary files differ; we shouldn't break then
+ xSet->setPropertyValue(rProp.Name, aValue);
+ }
+ catch (const uno::Exception&) {}
+ }
+ }
+
+ // sigh... have to set these manually I'm afraid...
+ xDocPropsToFill->setAuthor(i_xOldDocProps->getAuthor());
+ xDocPropsToFill->setGenerator(i_xOldDocProps->getGenerator());
+ xDocPropsToFill->setCreationDate(i_xOldDocProps->getCreationDate());
+ xDocPropsToFill->setTitle(i_xOldDocProps->getTitle());
+ xDocPropsToFill->setSubject(i_xOldDocProps->getSubject());
+ xDocPropsToFill->setDescription(i_xOldDocProps->getDescription());
+ xDocPropsToFill->setKeywords(i_xOldDocProps->getKeywords());
+ xDocPropsToFill->setModifiedBy(i_xOldDocProps->getModifiedBy());
+ xDocPropsToFill->setModificationDate(i_xOldDocProps->getModificationDate());
+ xDocPropsToFill->setPrintedBy(i_xOldDocProps->getPrintedBy());
+ xDocPropsToFill->setPrintDate(i_xOldDocProps->getPrintDate());
+ xDocPropsToFill->setAutoloadURL(i_xOldDocProps->getAutoloadURL());
+ xDocPropsToFill->setAutoloadSecs(i_xOldDocProps->getAutoloadSecs());
+ xDocPropsToFill->setDefaultTarget(i_xOldDocProps->getDefaultTarget());
+ xDocPropsToFill->setEditingCycles(i_xOldDocProps->getEditingCycles());
+ xDocPropsToFill->setEditingDuration(i_xOldDocProps->getEditingDuration());
+ }
+ catch (const uno::Exception&) {}
+
+ // set the modified flag back if required
+ if (bIsModified != bool(xModifiable->isModified()))
+ xModifiable->setModified(bIsModified);
+}
+
+SfxItemSet*
+ScVbaCellRangeAccess::GetDataSet( ScCellRangesBase* pRangeObj )
+{
+ return pRangeObj ? pRangeObj->GetCurrentDataSet( true ) : nullptr;
+}
+
+} // namespace ooo::vba::excel
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/excelvbahelper.hxx b/sc/source/ui/vba/excelvbahelper.hxx
new file mode 100644
index 0000000000..542991aca0
--- /dev/null
+++ b/sc/source/ui/vba/excelvbahelper.hxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <ooo/vba/excel/XApplication.hpp>
+
+#include <vector>
+#include <global.hxx>
+
+namespace com::sun::star::frame { class XModel; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace com::sun::star::sheet { class XDatabaseRange; }
+namespace com::sun::star::sheet { class XUnnamedDatabaseRanges; }
+namespace com::sun::star::table { class XCell; }
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::sheet { class XSheetCellRangeContainer; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+namespace ooo::vba { class XHelperInterface; }
+
+class ScCellRangesBase;
+class ScTabViewShell;
+class SfxViewFrame;
+
+namespace ooo::vba::excel {
+
+// nTabs empty means apply zoom to all sheets
+void implSetZoom( const css::uno::Reference< css::frame::XModel >& xModel, sal_Int16 nZoom, std::vector< SCTAB >& nTabs );
+void implnCopy( const css::uno::Reference< css::frame::XModel>& xModel );
+void implnPaste ( const css::uno::Reference< css::frame::XModel>& xModel );
+void implnCut( const css::uno::Reference< css::frame::XModel>& xModel );
+void implnPasteSpecial( const css::uno::Reference< css::frame::XModel>& xModel, InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bTranspose);
+ScTabViewShell* getBestViewShell( const css::uno::Reference< css::frame::XModel>& xModel ) ;
+ScDocShell* getDocShell( const css::uno::Reference< css::frame::XModel>& xModel ) ;
+ScTabViewShell* getCurrentBestViewShell( const css::uno::Reference< css::uno::XComponentContext >& xContext );
+SfxViewFrame* getViewFrame( const css::uno::Reference< css::frame::XModel >& xModel );
+
+/// @throws css::uno::RuntimeException
+css::uno::Reference< css::sheet::XUnnamedDatabaseRanges > GetUnnamedDataBaseRanges( const ScDocShell* pShell );
+
+/// @throws css::uno::RuntimeException
+css::uno::Reference< css::sheet::XDatabaseRange > GetAutoFiltRange( const ScDocShell* pShell, sal_Int16 nSheet );
+/// @throws css::uno::RuntimeException
+css::uno::Reference< ooo::vba::XHelperInterface > getUnoSheetModuleObj( const css::uno::Reference< css::sheet::XSpreadsheet >& xSheet );
+/// @throws css::uno::RuntimeException
+css::uno::Reference< ooo::vba::XHelperInterface > getUnoSheetModuleObj( const css::uno::Reference< css::sheet::XSheetCellRangeContainer >& xRanges );
+/// @throws css::uno::RuntimeException
+css::uno::Reference< ooo::vba::XHelperInterface > getUnoSheetModuleObj( const css::uno::Reference< css::table::XCellRange >& xRange );
+/// @throws css::uno::RuntimeException
+css::uno::Reference< ooo::vba::XHelperInterface > getUnoSheetModuleObj( const css::uno::Reference< css::table::XCell >& xCell );
+/// @throws css::uno::RuntimeException
+css::uno::Reference< ooo::vba::XHelperInterface > getUnoSheetModuleObj( const css::uno::Reference< css::frame::XModel >& xModel, SCTAB nTab );
+
+/// @throws css::uno::RuntimeException
+ScDocShell* GetDocShellFromRange( const css::uno::Reference< css::uno::XInterface >& xRange );
+void setUpDocumentModules( const css::uno::Reference< css::sheet::XSpreadsheetDocument >& xDoc );
+
+void ExportAsFixedFormatHelper(
+ const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< ooo::vba::excel::XApplication >& xApplication,
+ const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish);
+
+void SetDocInfoState(
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::document::XDocumentProperties>& i_xOldDocInfo);
+
+class ScVbaCellRangeAccess
+{
+public:
+ static SfxItemSet* GetDataSet( ScCellRangesBase* pRangeObj );
+};
+
+// Extracts an implementation object (via XUnoTunnel) from a UNO object.
+// Will throw if unsuccessful.
+/// @throws css::uno::RuntimeException
+template < typename ImplObject >
+ ImplObject* getImplFromDocModuleWrapper( const css::uno::Reference< css::uno::XInterface >& rxWrapperIf )
+ {
+ ImplObject* pObj = comphelper::getFromUnoTunnel<ImplObject>(rxWrapperIf);
+ if ( !pObj )
+ throw css::uno::RuntimeException("Internal error, can't extract implementation object", rxWrapperIf );
+ return pObj;
+ }
+
+} // namespace ooo::vba::excel
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaapplication.cxx b/sc/source/ui/vba/vbaapplication.cxx
new file mode 100644
index 0000000000..f951cbd458
--- /dev/null
+++ b/sc/source/ui/vba/vbaapplication.cxx
@@ -0,0 +1,1579 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XDesktop.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/script/BasicErrorException.hpp>
+#include <com/sun/star/sheet/XCalculatable.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XNamedRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+#include <com/sun/star/task/XStatusIndicatorSupplier.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/util/PathSettings.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <ooo/vba/XCommandBars.hpp>
+#include <ooo/vba/excel/XApplicationOutgoing.hpp>
+#include <ooo/vba/excel/XlCalculation.hpp>
+#include <ooo/vba/excel/XlMousePointer.hpp>
+#include <ooo/vba/office/MsoShapeType.hpp>
+#include <ooo/vba/office/MsoAutoShapeType.hpp>
+#include <ooo/vba/office/MsoFileDialogType.hpp>
+
+#include "vbaapplication.hxx"
+#include "vbaworkbooks.hxx"
+#include "vbaworkbook.hxx"
+#include "vbarange.hxx"
+#include "vbawsfunction.hxx"
+#include "vbadialogs.hxx"
+#include "vbawindow.hxx"
+#include "vbawindows.hxx"
+#include "vbamenubars.hxx"
+#include <tabvwsh.hxx>
+#include <gridwin.hxx>
+#include "vbanames.hxx"
+#include <vbahelper/vbashape.hxx>
+#include "vbatextboxshape.hxx"
+#include "vbaovalshape.hxx"
+#include "vbalineshape.hxx"
+#include "vbaassistant.hxx"
+#include <sc.hrc>
+#include <macromgr.hxx>
+#include "vbafiledialog.hxx"
+#include "vbafiledialogitems.hxx"
+
+#include <osl/file.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/app.hxx>
+#include <vcl/svapp.hxx>
+
+#include <comphelper/diagnose_ex.hxx>
+
+#include <basic/sbx.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbuno.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sberrors.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppu/unotype.hxx>
+
+#include <convuno.hxx>
+#include <cellsuno.hxx>
+#include <unonames.hxx>
+#include <docsh.hxx>
+#include "excelvbahelper.hxx"
+#include <basic/sbxobj.hxx>
+
+#include <viewutil.hxx>
+#include <docoptio.hxx>
+#include <scmod.hxx>
+#include <scdll.hxx>
+
+#include <list>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_QUERY;
+
+/** Global application settings shared by all open workbooks. */
+struct ScVbaAppSettings
+{
+ bool mbDisplayAlerts;
+ bool mbEnableEvents;
+ bool mbExcel4Menus;
+ bool mbDisplayNoteIndicator;
+ bool mbShowWindowsInTaskbar;
+ bool mbEnableCancelKey;
+ explicit ScVbaAppSettings();
+};
+
+ScVbaAppSettings::ScVbaAppSettings() :
+ mbDisplayAlerts( true ),
+ mbEnableEvents( true ),
+ mbExcel4Menus( false ),
+ mbDisplayNoteIndicator( true ),
+ mbShowWindowsInTaskbar( true ),
+ mbEnableCancelKey( false )
+{
+}
+
+namespace {
+
+ScVbaAppSettings& ScVbaStaticAppSettings()
+{
+ static ScVbaAppSettings SINGLETON;
+ return SINGLETON;
+}
+
+class ScVbaApplicationOutgoingConnectionPoint : public cppu::WeakImplHelper<XConnectionPoint>
+{
+private:
+ ScVbaApplication* mpApp;
+
+public:
+ ScVbaApplicationOutgoingConnectionPoint( ScVbaApplication* pApp );
+
+ // XConnectionPoint
+ sal_uInt32 SAL_CALL Advise(const uno::Reference< XSink >& Sink ) override;
+ void SAL_CALL Unadvise( sal_uInt32 Cookie ) override;
+};
+
+}
+
+sal_uInt32
+ScVbaApplication::AddSink( const uno::Reference< XSink >& xSink )
+{
+ {
+ SolarMutexGuard aGuard;
+ ScDLL::Init();
+ }
+ // No harm in potentially calling this several times
+ SC_MOD()->RegisterAutomationApplicationEventsCaller( uno::Reference< XSinkCaller >(this) );
+ mvSinks.push_back(xSink);
+ return mvSinks.size();
+}
+
+void
+ScVbaApplication::RemoveSink( sal_uInt32 nNumber )
+{
+ if (nNumber < 1 || nNumber > mvSinks.size())
+ return;
+
+ mvSinks[nNumber-1] = uno::Reference< XSink >();
+}
+
+ScVbaApplication::ScVbaApplication( const uno::Reference<uno::XComponentContext >& xContext ) :
+ ScVbaApplication_BASE( xContext ),
+ mrAppSettings( ScVbaStaticAppSettings() ),
+ m_nDialogType(0)
+{
+}
+
+ScVbaApplication::~ScVbaApplication()
+{
+}
+
+/*static*/ bool ScVbaApplication::getDocumentEventsEnabled()
+{
+ return ScVbaStaticAppSettings().mbEnableEvents;
+}
+
+OUString SAL_CALL
+ScVbaApplication::getExactName( const OUString& aApproximateName )
+{
+ uno::Reference< beans::XExactName > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ return xWSF->getExactName( aApproximateName );
+}
+
+uno::Reference< beans::XIntrospectionAccess > SAL_CALL
+ScVbaApplication::getIntrospection()
+{
+ uno::Reference< script::XInvocation > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ return xWSF->getIntrospection();
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::invoke( const OUString& FunctionName, const uno::Sequence< uno::Any >& Params, uno::Sequence< sal_Int16 >& OutParamIndex, uno::Sequence< uno::Any >& OutParam)
+{
+ /* When calling the functions directly at the Application object, no runtime
+ errors are thrown, but the error is inserted into the return value. */
+ uno::Any aAny;
+ try
+ {
+ uno::Reference< script::XInvocation > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ aAny = xWSF->invoke( FunctionName, Params, OutParamIndex, OutParam );
+ }
+ catch (const uno::Exception&)
+ {
+ aAny <<= script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), 1000, OUString() );
+ }
+ return aAny;
+}
+
+void SAL_CALL
+ScVbaApplication::setValue( const OUString& PropertyName, const uno::Any& Value )
+{
+ uno::Reference< script::XInvocation > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ xWSF->setValue( PropertyName, Value );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::getValue( const OUString& PropertyName )
+{
+ uno::Reference< script::XInvocation > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ return xWSF->getValue( PropertyName );
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::hasMethod( const OUString& Name )
+{
+ uno::Reference< script::XInvocation > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ return xWSF->hasMethod( Name );
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::hasProperty( const OUString& Name )
+{
+ uno::Reference< script::XInvocation > xWSF( new ScVbaWSFunction( this, mxContext ) );
+ return xWSF->hasProperty( Name );
+}
+
+uno::Reference< excel::XWorkbook >
+ScVbaApplication::getActiveWorkbook()
+{
+ uno::Reference< frame::XModel > xModel( getCurrentExcelDoc( mxContext ), uno::UNO_SET_THROW );
+ uno::Reference< excel::XWorkbook > xWorkbook( getVBADocument( xModel ), uno::UNO_QUERY );
+ if( xWorkbook.is() ) return xWorkbook;
+ // #i116936# getVBADocument() may return null in documents without global VBA mode enabled
+ return new ScVbaWorkbook( this, mxContext, xModel );
+}
+
+uno::Reference< excel::XWorkbook > SAL_CALL
+ScVbaApplication::getThisWorkbook()
+{
+ uno::Reference< frame::XModel > xModel( getThisExcelDoc( mxContext ), uno::UNO_SET_THROW );
+ uno::Reference< excel::XWorkbook > xWorkbook( getVBADocument( xModel ), uno::UNO_QUERY );
+ if( xWorkbook.is() ) return xWorkbook;
+ // #i116936# getVBADocument() may return null in documents without global VBA mode enabled
+ return new ScVbaWorkbook( this, mxContext, xModel );
+}
+
+uno::Reference< XAssistant > SAL_CALL
+ScVbaApplication::getAssistant()
+{
+ return uno::Reference< XAssistant >( new ScVbaAssistant( this, mxContext ) );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::getSelection()
+{
+ uno::Reference< frame::XModel > xModel( getCurrentDocument() );
+
+ Reference< view::XSelectionSupplier > xSelSupp( xModel->getCurrentController(), UNO_QUERY_THROW );
+ Reference< beans::XPropertySet > xPropSet( xSelSupp, UNO_QUERY_THROW );
+ OUString aPropName( SC_UNO_FILTERED_RANGE_SELECTION );
+ uno::Any aOldVal = xPropSet->getPropertyValue( aPropName );
+ uno::Any any;
+ any <<= false;
+ xPropSet->setPropertyValue( aPropName, any );
+ uno::Reference<uno::XInterface> aSelection(xSelSupp->getSelection(), uno::UNO_QUERY);
+ xPropSet->setPropertyValue( aPropName, aOldVal );
+
+ if (!aSelection.is())
+ {
+ throw uno::RuntimeException( "failed to obtain current selection" );
+ }
+
+ uno::Reference< lang::XServiceInfo > xServiceInfo( aSelection, uno::UNO_QUERY_THROW );
+ OUString sImplementationName = xServiceInfo->getImplementationName();
+
+ if( sImplementationName.equalsIgnoreAsciiCase("com.sun.star.drawing.SvxShapeCollection") )
+ {
+ uno::Reference< drawing::XShapes > xShapes( aSelection, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xShapes, uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XShape > xShape( xIndexAccess->getByIndex(0), uno::UNO_QUERY_THROW );
+ // if ScVbaShape::getType( xShape ) == office::MsoShapeType::msoAutoShape
+ // and the uno object implements the com.sun.star.drawing.Text service
+ // return a textboxshape object
+ sal_Int32 nType = ScVbaShape::getType( xShape );
+ if ( nType == office::MsoShapeType::msoAutoShape )
+ {
+ // TODO Oval with text box
+ if( ScVbaShape::getAutoShapeType( xShape ) == office::MsoAutoShapeType::msoShapeOval )
+ {
+ return uno::Any( uno::Reference< msforms::XOval >(new ScVbaOvalShape( mxContext, xShape, xShapes, xModel ) ) );
+ }
+
+
+ uno::Reference< lang::XServiceInfo > xShapeServiceInfo( xShape, uno::UNO_QUERY_THROW );
+ if ( xShapeServiceInfo->supportsService("com.sun.star.drawing.Text") )
+ {
+ return uno::Any( uno::Reference< msforms::XTextBoxShape >(
+ new ScVbaTextBoxShape( mxContext, xShape, xShapes, xModel ) ) );
+ }
+ }
+ else if ( nType == office::MsoShapeType::msoLine )
+ {
+ return uno::Any( uno::Reference< msforms::XLine >( new ScVbaLineShape(
+ mxContext, xShape, xShapes, xModel ) ) );
+ }
+ return uno::Any( uno::Reference< msforms::XShape >(new ScVbaShape( this, mxContext, xShape, xShapes, xModel, ScVbaShape::getType( xShape ) ) ) );
+ }
+ else if( xServiceInfo->supportsService("com.sun.star.sheet.SheetCellRange") ||
+ xServiceInfo->supportsService("com.sun.star.sheet.SheetCellRanges") )
+ {
+ uno::Reference< table::XCellRange > xRange( aSelection, ::uno::UNO_QUERY);
+ if ( !xRange.is() )
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( aSelection, ::uno::UNO_QUERY);
+ if ( xRanges.is() )
+ return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( excel::getUnoSheetModuleObj( xRanges ), mxContext, xRanges ) ) );
+
+ }
+ return uno::Any( uno::Reference< excel::XRange >(new ScVbaRange( excel::getUnoSheetModuleObj( xRange ), mxContext, xRange ) ) );
+ }
+ else
+ {
+ throw uno::RuntimeException( sImplementationName + " not supported" );
+ }
+}
+
+uno::Reference< excel::XRange >
+ScVbaApplication::getActiveCell()
+{
+ uno::Reference< sheet::XSpreadsheetView > xView( getCurrentDocument()->getCurrentController(), uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xRange( xView->getActiveSheet(), ::uno::UNO_QUERY_THROW);
+ ScTabViewShell* pViewShell = excel::getCurrentBestViewShell(mxContext);
+ if ( !pViewShell )
+ throw uno::RuntimeException("No ViewShell available" );
+ ScViewData& rTabView = pViewShell->GetViewData();
+
+ sal_Int32 nCursorX = rTabView.GetCurX();
+ sal_Int32 nCursorY = rTabView.GetCurY();
+
+ // #i117392# excel::getUnoSheetModuleObj() may return null in documents without global VBA mode enabled
+ return new ScVbaRange( excel::getUnoSheetModuleObj( xRange ), mxContext, xRange->getCellRangeByPosition( nCursorX, nCursorY, nCursorX, nCursorY ) );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::GetOpenFilename(const uno::Any& /*aFileFilter*/, const uno::Any& /*aFilterIndex*/, const uno::Any& aTitle, const uno::Any& /*aButtonText*/, const uno::Any& aMultiSelect)
+{
+ // TODO - take all parameters into account
+ uno::Reference<excel::XFileDialog> xDialog(new ScVbaFileDialog(this, mxContext, office::MsoFileDialogType::msoFileDialogFilePicker));
+ xDialog->setTitle(aTitle);
+ xDialog->setAllowMultiSelect(aMultiSelect);
+
+ bool bMultiSelect = false;
+ aMultiSelect >>= bMultiSelect;
+
+ if (xDialog->Show() == 0)
+ {
+ // return FALSE when canceled
+ return uno::Any(false);
+ }
+
+ uno::Reference<excel::XFileDialogSelectedItems> xItems = xDialog->getSelectedItems();
+ auto* pItems = dynamic_cast<ScVbaFileDialogSelectedItems*>(xItems.get());
+
+ // Check, if the implementation of XFileDialogSelectedItems is what we expect
+ if (!pItems)
+ throw uno::RuntimeException("Unexpected XFileDialogSelectedItems implementation");
+
+ auto const & rItemVector = pItems->getItems();
+
+ if (!bMultiSelect) // only 1 selection allowed - return path
+ {
+ OUString aPath;
+ if (!rItemVector.empty())
+ aPath = rItemVector.at(0);
+ return uno::Any(aPath);
+ }
+ else
+ {
+ // convert to sequence
+ return uno::Any(comphelper::containerToSequence(rItemVector));
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::International( sal_Int32 /*Index*/ )
+{
+ // complete stub for now
+ // #TODO flesh out some of the Indices we could handle
+ uno::Any aRet;
+ return aRet;
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::FileDialog( const uno::Any& DialogType )
+{
+ sal_Int32 nType = 0;
+ DialogType >>= nType;
+
+ if( !m_xFileDialog || nType != m_nDialogType )
+ {
+ m_nDialogType = nType;
+ m_xFileDialog = uno::Reference<excel::XFileDialog> ( new ScVbaFileDialog( this, mxContext, nType ));
+ }
+ return uno::Any( m_xFileDialog );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Workbooks( const uno::Any& aIndex )
+{
+ uno::Reference< XCollection > xWorkBooks( new ScVbaWorkbooks( this, mxContext ) );
+ if ( aIndex.getValueTypeClass() == uno::TypeClass_VOID )
+ {
+ // void then somebody did Workbooks.something in vba
+ return uno::Any( xWorkBooks );
+ }
+
+ return xWorkBooks->Item( aIndex, uno::Any() );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Worksheets( const uno::Any& aIndex )
+{
+ uno::Reference< excel::XWorkbook > xWorkbook( getActiveWorkbook(), uno::UNO_SET_THROW );
+ return xWorkbook->Worksheets( aIndex );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::WorksheetFunction( )
+{
+ return uno::Any( uno::Reference< script::XInvocation >( new ScVbaWSFunction( this, mxContext ) ) );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Evaluate( const OUString& Name )
+{
+ // #TODO Evaluate allows other things to be evaluated, e.g. functions
+ // I think ( like SIN(3) etc. ) need to investigate that
+ // named Ranges also? e.g. [MyRange] if so need a list of named ranges
+ uno::Any aVoid;
+ return uno::Any( getActiveWorkbook()->getActiveSheet()->Range( uno::Any( Name ), aVoid ) );
+}
+
+uno::Any
+ScVbaApplication::Dialogs( const uno::Any &aIndex )
+{
+ uno::Reference< excel::XDialogs > xDialogs( new ScVbaDialogs( uno::Reference< XHelperInterface >( this ), mxContext, getCurrentDocument() ) );
+ if( !aIndex.hasValue() )
+ return uno::Any( xDialogs );
+ return xDialogs->Item( aIndex );
+}
+
+uno::Reference< excel::XWindow > SAL_CALL
+ScVbaApplication::getActiveWindow()
+{
+ uno::Reference< frame::XModel > xModel = getCurrentDocument();
+ uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
+ uno::Reference< XHelperInterface > xParent( getActiveWorkbook(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XWindow > xWin( new ScVbaWindow( xParent, mxContext, xModel, xController ) );
+ return xWin;
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::getCutCopyMode()
+{
+ //# FIXME TODO, implementation
+ uno::Any result;
+ result <<= false;
+ return result;
+}
+
+void SAL_CALL
+ScVbaApplication::setCutCopyMode( const uno::Any& /* _cutcopymode */ )
+{
+ //# FIXME TODO, implementation
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::getStatusBar()
+{
+ return uno::Any( !getDisplayStatusBar() );
+}
+
+css::uno::Any SAL_CALL ScVbaApplication::getWindowState()
+{
+ return getActiveWindow()->getWindowState();
+}
+
+void SAL_CALL ScVbaApplication::setWindowState(const css::uno::Any& rWindowState)
+{
+ getActiveWindow()->setWindowState(rWindowState);
+}
+
+void SAL_CALL
+ScVbaApplication::setStatusBar( const uno::Any& _statusbar )
+{
+ OUString sText;
+ bool bDefault = false;
+ uno::Reference< frame::XModel > xModel( getCurrentDocument(), uno::UNO_SET_THROW );
+ uno::Reference< task::XStatusIndicatorSupplier > xStatusIndicatorSupplier( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
+ uno::Reference< task::XStatusIndicator > xStatusIndicator( xStatusIndicatorSupplier->getStatusIndicator(), uno::UNO_SET_THROW );
+ if( _statusbar >>= sText )
+ {
+ setDisplayStatusBar( true );
+ if ( !sText.isEmpty() )
+ xStatusIndicator->start( sText, 100 );
+ else
+ xStatusIndicator->end(); // restore normal state for empty text
+ }
+ else if( _statusbar >>= bDefault )
+ {
+ if( !bDefault )
+ {
+ xStatusIndicator->end();
+ setDisplayStatusBar( true );
+ }
+ }
+ else
+ throw uno::RuntimeException("Invalid parameter. It should be a string or False" );
+}
+
+::sal_Int32 SAL_CALL
+ScVbaApplication::getCalculation()
+{
+ // TODO: in Excel, this is an application-wide setting
+ uno::Reference<sheet::XCalculatable> xCalc(getCurrentDocument(), uno::UNO_QUERY_THROW);
+ if(xCalc->isAutomaticCalculationEnabled())
+ return excel::XlCalculation::xlCalculationAutomatic;
+ else
+ return excel::XlCalculation::xlCalculationManual;
+}
+
+void SAL_CALL
+ScVbaApplication::setCalculation( ::sal_Int32 _calculation )
+{
+ // TODO: in Excel, this is an application-wide setting
+ uno::Reference< sheet::XCalculatable > xCalc(getCurrentDocument(), uno::UNO_QUERY_THROW);
+ switch(_calculation)
+ {
+ case excel::XlCalculation::xlCalculationManual:
+ xCalc->enableAutomaticCalculation(false);
+ break;
+ case excel::XlCalculation::xlCalculationAutomatic:
+ case excel::XlCalculation::xlCalculationSemiautomatic:
+ xCalc->enableAutomaticCalculation(true);
+ break;
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Windows( const uno::Any& aIndex )
+{
+ uno::Reference< excel::XWindows > xWindows( new ScVbaWindows( this, mxContext ) );
+ if ( aIndex.getValueTypeClass() == uno::TypeClass_VOID )
+ return uno::Any( xWindows );
+ return xWindows->Item( aIndex, uno::Any() );
+}
+void SAL_CALL
+ScVbaApplication::wait( double time )
+{
+ StarBASIC* pBasic = SfxApplication::GetBasic();
+ SbxArrayRef aArgs = new SbxArray;
+ SbxVariableRef aRef = new SbxVariable;
+ aRef->PutDouble( time );
+ aArgs->Put(aRef.get(), 1);
+ SbMethod* pMeth = static_cast<SbMethod*>(pBasic->GetRtl()->Find( "WaitUntil", SbxClassType::Method ));
+
+ if ( pMeth )
+ {
+ pMeth->SetParameters( aArgs.get() );
+ SbxVariableRef refTemp = pMeth;
+ // forces a broadcast
+ SbxVariableRef pNew = new SbxMethod( *static_cast<SbxMethod*>(pMeth));
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Range( const uno::Any& Cell1, const uno::Any& Cell2 )
+{
+ uno::Reference< excel::XRange > xVbRange = ScVbaRange::ApplicationRange( mxContext, Cell1, Cell2 );
+ return uno::Any( xVbRange );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Names( const css::uno::Any& aIndex )
+{
+ uno::Reference< frame::XModel > xModel( getCurrentDocument(), uno::UNO_SET_THROW );
+ uno::Reference< beans::XPropertySet > xPropertySet( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XNamedRanges > xNamedRanges( xPropertySet->getPropertyValue(
+ "NamedRanges" ), uno::UNO_QUERY_THROW );
+
+ css::uno::Reference< excel::XNames > xNames ( new ScVbaNames( this , mxContext , xNamedRanges , xModel ) );
+ if ( aIndex.getValueTypeClass() == uno::TypeClass_VOID )
+ {
+ return uno::Any( xNames );
+ }
+ return xNames->Item( aIndex, uno::Any() );
+}
+
+uno::Reference< excel::XWorksheet > SAL_CALL
+ScVbaApplication::getActiveSheet()
+{
+ uno::Reference< excel::XWorksheet > result;
+ uno::Reference< excel::XWorkbook > xWorkbook = getActiveWorkbook();
+ if ( xWorkbook.is() )
+ {
+ uno::Reference< excel::XWorksheet > xWorksheet =
+ xWorkbook->getActiveSheet();
+ if ( xWorksheet.is() )
+ {
+ result = xWorksheet;
+ }
+ }
+
+ if ( !result.is() )
+ {
+ // Fixme - check if this is reasonable/desired behavior
+ throw uno::RuntimeException("No activeSheet available" );
+ }
+ return result;
+
+}
+
+/*******************************************************************************
+ * In msdn:
+ * Reference Optional Variant. The destination. Can be a Range
+ * object, a string that contains a cell reference in R1C1-style notation,
+ * or a string that contains a Visual Basic procedure name.
+ * Scroll Optional Variant. True to scroll, False to not scroll through
+ * the window. The default is False.
+ * Parser is split to three parts, Range, R1C1 string and procedure name.
+ * by test excel, it seems Scroll no effect. ???
+*******************************************************************************/
+void SAL_CALL
+ScVbaApplication::GoTo( const uno::Any& Reference, const uno::Any& Scroll )
+{
+ //test Scroll is a boolean
+ bool bScroll = false;
+ //R1C1-style string or a string of procedure name.
+
+ if( Scroll.hasValue() )
+ {
+ bool aScroll = false;
+ if( !(Scroll >>= aScroll) )
+ throw uno::RuntimeException("second parameter should be boolean" );
+
+ bScroll = aScroll;
+
+ }
+
+ OUString sRangeName;
+ if( Reference >>= sRangeName )
+ {
+ uno::Reference< frame::XModel > xModel( getCurrentDocument(), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XSpreadsheetView > xSpreadsheet(
+ xModel->getCurrentController(), uno::UNO_QUERY_THROW );
+
+ ScTabViewShell* pShell = excel::getCurrentBestViewShell( mxContext );
+ ScGridWindow* gridWindow = static_cast<ScGridWindow*>(pShell->GetWindow());
+ try
+ {
+ uno::Reference< excel::XRange > xVbaSheetRange = ScVbaRange::getRangeObjectForName(
+ mxContext, sRangeName, excel::getDocShell( xModel ), formula::FormulaGrammar::CONV_XL_R1C1 );
+
+ if( bScroll )
+ {
+ xVbaSheetRange->Select();
+ uno::Reference< excel::XWindow > xWindow = getActiveWindow();
+ ScSplitPos eWhich = pShell->GetViewData().GetActivePart();
+ sal_Int32 nValueX = pShell->GetViewData().GetPosX(WhichH(eWhich));
+ sal_Int32 nValueY = pShell->GetViewData().GetPosY(WhichV(eWhich));
+ xWindow->SmallScroll( uno::Any( static_cast<sal_Int16>(xVbaSheetRange->getRow() - 1) ),
+ uno::Any( static_cast<sal_Int16>(nValueY) ),
+ uno::Any( static_cast<sal_Int16>(xVbaSheetRange->getColumn() - 1) ),
+ uno::Any( static_cast<sal_Int16>(nValueX) ) );
+ gridWindow->GrabFocus();
+ }
+ else
+ {
+ xVbaSheetRange->Select();
+ gridWindow->GrabFocus();
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ //maybe this should be a procedure name
+ //TODO for procedure name
+ //browse::XBrowseNodeFactory is a singleton. OUString( "/singletons/com.sun.star.script.browse.theBrowseNodeFactory")
+ //and the createView( browse::BrowseNodeFactoryViewTypes::MACROSELECTOR ) to get a root browse::XBrowseNode.
+ //for query XInvocation interface.
+ //but how to directly get the XInvocation?
+ throw uno::RuntimeException("invalid reference for range name, it should be procedure name" );
+ }
+ return;
+ }
+ uno::Reference< excel::XRange > xRange;
+ if( Reference >>= xRange )
+ {
+ uno::Reference< excel::XRange > xVbaRange( Reference, uno::UNO_QUERY );
+ ScTabViewShell* pShell = excel::getCurrentBestViewShell( mxContext );
+ ScGridWindow* gridWindow = static_cast<ScGridWindow*>(pShell->GetWindow());
+ if ( xVbaRange.is() )
+ {
+ //TODO bScroll should be used. At this time, it does not have effect
+ if( bScroll )
+ {
+ xVbaRange->Select();
+ uno::Reference< excel::XWindow > xWindow = getActiveWindow();
+ ScSplitPos eWhich = pShell->GetViewData().GetActivePart();
+ sal_Int32 nValueX = pShell->GetViewData().GetPosX(WhichH(eWhich));
+ sal_Int32 nValueY = pShell->GetViewData().GetPosY(WhichV(eWhich));
+ xWindow->SmallScroll( uno::Any( static_cast<sal_Int16>(xVbaRange->getRow() - 1) ),
+ uno::Any( static_cast<sal_Int16>(nValueY) ),
+ uno::Any( static_cast<sal_Int16>(xVbaRange->getColumn() - 1) ),
+ uno::Any( static_cast<sal_Int16>(nValueX) ) );
+ gridWindow->GrabFocus();
+ }
+ else
+ {
+ xVbaRange->Select();
+ gridWindow->GrabFocus();
+ }
+ }
+ return;
+ }
+ throw uno::RuntimeException("invalid reference or name" );
+}
+
+sal_Int32 SAL_CALL
+ScVbaApplication::getCursor()
+{
+ PointerStyle nPointerStyle = getPointerStyle(getCurrentDocument());
+
+ switch( nPointerStyle )
+ {
+ case PointerStyle::Arrow:
+ return excel::XlMousePointer::xlNorthwestArrow;
+ case PointerStyle::Null:
+ return excel::XlMousePointer::xlDefault;
+ case PointerStyle::Wait:
+ return excel::XlMousePointer::xlWait;
+ case PointerStyle::Text:
+ return excel::XlMousePointer::xlIBeam;
+ default:
+ return excel::XlMousePointer::xlDefault;
+ }
+}
+
+void SAL_CALL
+ScVbaApplication::setCursor( sal_Int32 _cursor )
+{
+ try
+ {
+ uno::Reference< frame::XModel > xModel( getCurrentDocument(), uno::UNO_SET_THROW );
+ switch( _cursor )
+ {
+ case excel::XlMousePointer::xlNorthwestArrow:
+ {
+ setCursorHelper( xModel, PointerStyle::Arrow, false );
+ break;
+ }
+ case excel::XlMousePointer::xlWait:
+ case excel::XlMousePointer::xlIBeam:
+ {
+ PointerStyle nPointer( static_cast< PointerStyle >( _cursor ) );
+ //It will set the edit window, toobar and statusbar's mouse pointer.
+ setCursorHelper( xModel, nPointer, true );
+ break;
+ }
+ case excel::XlMousePointer::xlDefault:
+ {
+ setCursorHelper( xModel, PointerStyle::Null, false );
+ break;
+ }
+ default:
+ throw uno::RuntimeException("Unknown value for Cursor pointer" );
+ // TODO: isn't this a flaw in the API? It should be allowed to throw an
+ // IllegalArgumentException, or so
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sc.ui");
+ }
+}
+
+// #TODO perhaps we should switch the return type depending of the filter
+// type, e.g. return Calc for Calc and Excel if it's an imported doc
+OUString SAL_CALL
+ScVbaApplication::getName()
+{
+ return "Microsoft Excel";
+}
+
+// #TODO #FIXME get/setDisplayAlerts are just stub impl
+// here just the status of the switch is set
+// the function that throws an error message needs to
+// evaluate this switch in order to know whether it has to disable the
+// error message thrown by OpenOffice
+
+void SAL_CALL
+ScVbaApplication::setDisplayAlerts(sal_Bool displayAlerts)
+{
+ mrAppSettings.mbDisplayAlerts = displayAlerts;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getDisplayAlerts()
+{
+ return mrAppSettings.mbDisplayAlerts;
+}
+
+void SAL_CALL
+ScVbaApplication::setEnableEvents(sal_Bool bEnable)
+{
+ mrAppSettings.mbEnableEvents = bEnable;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getEnableEvents()
+{
+ return mrAppSettings.mbEnableEvents;
+}
+
+void SAL_CALL
+ScVbaApplication::setEnableCancelKey(sal_Bool bEnable)
+{
+ // Stub, does nothing
+ mrAppSettings.mbEnableCancelKey = bEnable;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getEnableCancelKey()
+{
+ return mrAppSettings.mbEnableCancelKey;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getDisplayFullScreen()
+{
+ SfxViewShell* pShell = excel::getCurrentBestViewShell( mxContext );
+ if ( pShell )
+ return ScViewUtil::IsFullScreen( *pShell );
+ return false;
+}
+
+void SAL_CALL
+ScVbaApplication::setDisplayFullScreen( sal_Bool bSet )
+{
+ // #FIXME calling ScViewUtil::SetFullScreen( *pShell, bSet );
+ // directly results in a strange crash, using dispatch instead
+ if ( bSet != getDisplayFullScreen() )
+ dispatchRequests( getCurrentDocument(), ".uno:FullScreen" );
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getDisplayScrollBars()
+{
+ ScTabViewShell* pShell = excel::getCurrentBestViewShell( mxContext );
+ if ( pShell )
+ {
+ return ( pShell->GetViewData().IsHScrollMode() && pShell->GetViewData().IsVScrollMode() );
+ }
+ return true;
+}
+
+void SAL_CALL
+ScVbaApplication::setDisplayScrollBars( sal_Bool bSet )
+{
+ // use uno here as it does all he repainting etc. magic
+ uno::Reference< sheet::XSpreadsheetView > xView( getCurrentDocument()->getCurrentController(), uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xView, uno::UNO_QUERY );
+ xProps->setPropertyValue("HasVerticalScrollBar", uno::Any( bSet ) );
+ xProps->setPropertyValue("HasHorizontalScrollBar", uno::Any( bSet ) );
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getDisplayExcel4Menus()
+{
+ return mrAppSettings.mbExcel4Menus;
+}
+
+void SAL_CALL
+ScVbaApplication::setDisplayExcel4Menus( sal_Bool bSet )
+{
+ mrAppSettings.mbExcel4Menus = bSet;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getDisplayNoteIndicator()
+{
+ return mrAppSettings.mbDisplayNoteIndicator;
+}
+
+void SAL_CALL
+ScVbaApplication::setDisplayNoteIndicator( sal_Bool bSet )
+{
+ mrAppSettings.mbDisplayNoteIndicator = bSet;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getShowWindowsInTaskbar()
+{
+ return mrAppSettings.mbShowWindowsInTaskbar;
+}
+
+void SAL_CALL
+ScVbaApplication::setShowWindowsInTaskbar( sal_Bool bSet )
+{
+ mrAppSettings.mbShowWindowsInTaskbar = bSet;
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getIteration()
+{
+ return SC_MOD()->GetDocOptions().IsIter();
+}
+
+void SAL_CALL
+ScVbaApplication::setIteration( sal_Bool bSet )
+{
+ uno::Reference< lang::XMultiComponentFactory > xSMgr(
+ mxContext->getServiceManager(), uno::UNO_SET_THROW );
+
+ uno::Reference< frame::XDesktop > xDesktop
+ (xSMgr->createInstanceWithContext( "com.sun.star.frame.Desktop" , mxContext), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration > xComponents = xDesktop->getComponents()->createEnumeration();
+ while ( xComponents->hasMoreElements() )
+ {
+ uno::Reference< lang::XServiceInfo > xServiceInfo( xComponents->nextElement(), uno::UNO_QUERY );
+ if ( xServiceInfo.is() && xServiceInfo->supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
+ {
+ uno::Reference< beans::XPropertySet > xProps( xServiceInfo, uno::UNO_QUERY );
+ if ( xProps.is() )
+ xProps->setPropertyValue( SC_UNO_ITERENABLED, uno::Any( bSet ) );
+ }
+ }
+ ScDocOptions aOpts( SC_MOD()->GetDocOptions() );
+ aOpts.SetIter( bSet );
+ SC_MOD()->SetDocOptions( aOpts );
+}
+
+void SAL_CALL
+ScVbaApplication::Calculate()
+{
+ uno::Reference< frame::XModel > xModel( getCurrentDocument(), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XCalculatable > xCalculatable( getCurrentDocument(), uno::UNO_QUERY_THROW );
+ xCalculatable->calculateAll();
+}
+
+/// @throws uno::RuntimeException
+static uno::Reference< util::XPathSettings > const & lcl_getPathSettingsService( const uno::Reference< uno::XComponentContext >& xContext )
+{
+ static uno::Reference< util::XPathSettings > xPathSettings( util::PathSettings::create( xContext ) );
+ return xPathSettings;
+}
+
+OUString ScVbaApplication::getOfficePath( const OUString& _sPathType )
+{
+ OUString sRetPath;
+ const uno::Reference< util::XPathSettings >& xProps = lcl_getPathSettingsService( mxContext );
+ try
+ {
+ OUString sUrl;
+ xProps->getPropertyValue( _sPathType ) >>= sUrl;
+
+ // if it's a list of paths then use the last one
+ sal_Int32 nIndex = sUrl.lastIndexOf( ';' ) ;
+ if ( nIndex > 0 )
+ sUrl = sUrl.copy( nIndex + 1 );
+ ::osl::File::getSystemPathFromFileURL( sUrl, sRetPath );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::runtimeexception(ERRCODE_BASIC_METHOD_FAILED);
+ }
+ return sRetPath;
+}
+
+void SAL_CALL
+ScVbaApplication::setDefaultFilePath( const OUString& DefaultFilePath )
+{
+ const uno::Reference< util::XPathSettings >& xProps = lcl_getPathSettingsService( mxContext );
+ OUString aURL;
+ osl::FileBase::getFileURLFromSystemPath( DefaultFilePath, aURL );
+ xProps->setWork( aURL );
+}
+
+OUString SAL_CALL
+ScVbaApplication::getDefaultFilePath()
+{
+ return getOfficePath( "Work");
+}
+
+OUString SAL_CALL
+ScVbaApplication::getLibraryPath()
+{
+ return getOfficePath( "Basic");
+}
+
+OUString SAL_CALL
+ScVbaApplication::getTemplatesPath()
+{
+ return getOfficePath( "Template");
+}
+
+OUString SAL_CALL
+ScVbaApplication::getPathSeparator()
+{
+ return OUString( sal_Unicode(SAL_PATHDELIMITER) );
+}
+
+OUString SAL_CALL
+ScVbaApplication::getOperatingSystem()
+{
+ // TODO Solution should contain the version number of the operating system
+ // too.
+#if defined(_WIN32)
+ return "Windows";
+#elif defined(MACOSX)
+ return "Macintosh";
+#elif defined(UNX)
+ // M. Office is not available on Unix systems, so it is not documented.
+ return "Unix";
+#else
+ return OUString("Unknown");
+#endif
+}
+
+// Helpers for Intersect and Union
+
+namespace {
+
+typedef ::std::list< ScRange > ListOfScRange;
+
+/** Appends all ranges of a VBA Range object in the passed Any to the list of ranges.
+
+ @throws script::BasicErrorException
+ @throws uno::RuntimeException
+*/
+void lclAddToListOfScRange( ListOfScRange& rList, const uno::Any& rArg )
+{
+ if( !rArg.hasValue() )
+ return;
+
+ uno::Reference< excel::XRange > xRange( rArg, uno::UNO_QUERY_THROW );
+ uno::Reference< XCollection > xCol( xRange->Areas( uno::Any() ), uno::UNO_QUERY_THROW );
+ for( sal_Int32 nIdx = 1, nCount = xCol->getCount(); nIdx <= nCount; ++nIdx )
+ {
+ uno::Reference< excel::XRange > xAreaRange( xCol->Item( uno::Any( nIdx ), uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( xAreaRange->getCellRange(), uno::UNO_QUERY_THROW );
+ ScRange aScRange;
+ ScUnoConversion::FillScRange( aScRange, xAddressable->getRangeAddress() );
+ rList.push_back( aScRange );
+ }
+}
+
+/** Returns true, if the passed ranges can be expressed by a single range. The
+ new range will be contained in r1 then, the range r2 can be removed. */
+bool lclTryJoin( ScRange& r1, const ScRange& r2 )
+{
+ // 1) r2 is completely inside r1
+ if( r1.Contains( r2 ) )
+ return true;
+
+ // 2) r1 is completely inside r2
+ if( r2.Contains( r1 ) )
+ {
+ r1 = r2;
+ return true;
+ }
+
+ SCCOL n1L = r1.aStart.Col();
+ SCCOL n1R = r1.aEnd.Col();
+ SCROW n1T = r1.aStart.Row();
+ SCROW n1B = r1.aEnd.Row();
+ SCCOL n2L = r2.aStart.Col();
+ SCCOL n2R = r2.aEnd.Col();
+ SCROW n2T = r2.aStart.Row();
+ SCROW n2B = r2.aEnd.Row();
+
+ // 3) r1 and r2 have equal upper and lower border
+ if( (n1T == n2T) && (n1B == n2B) )
+ {
+ // check that r1 overlaps or touches r2
+ if( ((n1L < n2L) && (n2L - 1 <= n1R)) || ((n2L < n1L) && (n1L - 1 <= n2R)) )
+ {
+ r1.aStart.SetCol( ::std::min( n1L, n2L ) );
+ r1.aEnd.SetCol( ::std::max( n1R, n2R ) );
+ return true;
+ }
+ return false;
+ }
+
+ // 4) r1 and r2 have equal left and right border
+ if( (n1L == n2L) && (n1R == n2R) )
+ {
+ // check that r1 overlaps or touches r2
+ if( ((n1T < n2T) && (n2T + 1 <= n1B)) || ((n2T < n1T) && (n1T + 1 <= n2B)) )
+ {
+ r1.aStart.SetRow( ::std::min( n1T, n2T ) );
+ r1.aEnd.SetRow( ::std::max( n1B, n2B ) );
+ return true;
+ }
+ return false;
+ }
+
+ // 5) cannot join these ranges
+ return false;
+}
+
+/** Strips out ranges that are contained by other ranges, joins ranges that can be joined
+ together (aligned borders, e.g. A4:D10 and B4:E10 would be combined to A4:E10. */
+void lclJoinRanges( ListOfScRange& rList )
+{
+ ListOfScRange::iterator aOuterIt = rList.begin();
+ while( aOuterIt != rList.end() )
+ {
+ bool bAnyErased = false; // true = any range erased from rList
+ ListOfScRange::iterator aInnerIt = rList.begin();
+ while( aInnerIt != rList.end() )
+ {
+ bool bInnerErased = false; // true = aInnerIt erased from rList
+ // do not compare a range with itself
+ if( (aOuterIt != aInnerIt) && lclTryJoin( *aOuterIt, *aInnerIt ) )
+ {
+ // aOuterIt points to joined range, aInnerIt will be removed
+ aInnerIt = rList.erase( aInnerIt );
+ bInnerErased = bAnyErased = true;
+ }
+ /* If aInnerIt has been erased from rList, it already points to
+ the next element (return value of list::erase()). */
+ if( !bInnerErased )
+ ++aInnerIt;
+ }
+ // if any range has been erased, repeat outer loop with the same range
+ if( !bAnyErased )
+ ++aOuterIt;
+ }
+}
+
+/** Intersects the passed list with all ranges of a VBA Range object in the passed Any.
+
+ @throws script::BasicErrorException
+ @throws uno::RuntimeException
+*/
+void lclIntersectRanges( ListOfScRange& rList, const uno::Any& rArg )
+{
+ // extract the ranges from the passed argument, will throw on invalid data
+ ListOfScRange aList2;
+ lclAddToListOfScRange( aList2, rArg );
+ // do nothing, if the passed list is already empty
+ if( rList.empty() || aList2.empty() )
+ return;
+
+ // save original list in a local
+ ListOfScRange aList1;
+ aList1.swap( rList );
+ // join ranges from passed argument
+ lclJoinRanges( aList2 );
+ // calculate intersection of the ranges in both lists
+ for( const auto& rOuterItem : aList1 )
+ {
+ for( const auto& rInnerItem : aList2 )
+ {
+ if( rOuterItem.Intersects( rInnerItem ) )
+ {
+ ScRange aIsectRange(
+ std::max( rOuterItem.aStart.Col(), rInnerItem.aStart.Col() ),
+ std::max( rOuterItem.aStart.Row(), rInnerItem.aStart.Row() ),
+ std::max( rOuterItem.aStart.Tab(), rInnerItem.aStart.Tab() ),
+ std::min( rOuterItem.aEnd.Col(), rInnerItem.aEnd.Col() ),
+ std::min( rOuterItem.aEnd.Row(), rInnerItem.aEnd.Row() ),
+ std::min( rOuterItem.aEnd.Tab(), rInnerItem.aEnd.Tab() ) );
+ rList.push_back( aIsectRange );
+ }
+ }
+ }
+ // again, join the result ranges
+ lclJoinRanges( rList );
+}
+
+/** Creates a VBA Range object from the passed list of ranges.
+
+ @throws uno::RuntimeException
+*/
+uno::Reference< excel::XRange > lclCreateVbaRange(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const ListOfScRange& rList )
+{
+ ScDocShell* pDocShell = excel::getDocShell( rxModel );
+ if( !pDocShell )
+ throw uno::RuntimeException();
+
+ ScRangeList aCellRanges;
+ for( const auto& rItem : rList )
+ aCellRanges.push_back( rItem );
+
+ if( aCellRanges.size() == 1 )
+ {
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pDocShell, aCellRanges.front() ) );
+ return new ScVbaRange( excel::getUnoSheetModuleObj( xRange ), rxContext, xRange );
+ }
+ if( aCellRanges.size() > 1 )
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocShell, aCellRanges ) );
+ return new ScVbaRange( excel::getUnoSheetModuleObj( xRanges ), rxContext, xRanges );
+ }
+ return nullptr;
+}
+
+} // namespace
+
+uno::Reference< excel::XRange > SAL_CALL ScVbaApplication::Intersect(
+ const uno::Reference< excel::XRange >& rArg1, const uno::Reference< excel::XRange >& rArg2,
+ const uno::Any& rArg3, const uno::Any& rArg4, const uno::Any& rArg5, const uno::Any& rArg6,
+ const uno::Any& rArg7, const uno::Any& rArg8, const uno::Any& rArg9, const uno::Any& rArg10,
+ const uno::Any& rArg11, const uno::Any& rArg12, const uno::Any& rArg13, const uno::Any& rArg14,
+ const uno::Any& rArg15, const uno::Any& rArg16, const uno::Any& rArg17, const uno::Any& rArg18,
+ const uno::Any& rArg19, const uno::Any& rArg20, const uno::Any& rArg21, const uno::Any& rArg22,
+ const uno::Any& rArg23, const uno::Any& rArg24, const uno::Any& rArg25, const uno::Any& rArg26,
+ const uno::Any& rArg27, const uno::Any& rArg28, const uno::Any& rArg29, const uno::Any& rArg30 )
+{
+ if( !rArg1.is() || !rArg2.is() )
+ DebugHelper::basicexception( ERRCODE_BASIC_BAD_PARAMETER, {} );
+
+ // initialize the result list with 1st parameter, join its ranges together
+ ListOfScRange aList;
+ lclAddToListOfScRange( aList, uno::Any( rArg1 ) );
+ lclJoinRanges( aList );
+
+ // process all other parameters, this updates the list with intersection
+ lclIntersectRanges( aList, uno::Any( rArg2 ) );
+ lclIntersectRanges( aList, rArg3 );
+ lclIntersectRanges( aList, rArg4 );
+ lclIntersectRanges( aList, rArg5 );
+ lclIntersectRanges( aList, rArg6 );
+ lclIntersectRanges( aList, rArg7 );
+ lclIntersectRanges( aList, rArg8 );
+ lclIntersectRanges( aList, rArg9 );
+ lclIntersectRanges( aList, rArg10 );
+ lclIntersectRanges( aList, rArg11 );
+ lclIntersectRanges( aList, rArg12 );
+ lclIntersectRanges( aList, rArg13 );
+ lclIntersectRanges( aList, rArg14 );
+ lclIntersectRanges( aList, rArg15 );
+ lclIntersectRanges( aList, rArg16 );
+ lclIntersectRanges( aList, rArg17 );
+ lclIntersectRanges( aList, rArg18 );
+ lclIntersectRanges( aList, rArg19 );
+ lclIntersectRanges( aList, rArg20 );
+ lclIntersectRanges( aList, rArg21 );
+ lclIntersectRanges( aList, rArg22 );
+ lclIntersectRanges( aList, rArg23 );
+ lclIntersectRanges( aList, rArg24 );
+ lclIntersectRanges( aList, rArg25 );
+ lclIntersectRanges( aList, rArg26 );
+ lclIntersectRanges( aList, rArg27 );
+ lclIntersectRanges( aList, rArg28 );
+ lclIntersectRanges( aList, rArg29 );
+ lclIntersectRanges( aList, rArg30 );
+
+ // create the VBA Range object
+ return lclCreateVbaRange( mxContext, getCurrentDocument(), aList );
+}
+
+uno::Reference< excel::XRange > SAL_CALL ScVbaApplication::Union(
+ const uno::Reference< excel::XRange >& rArg1, const uno::Reference< excel::XRange >& rArg2,
+ const uno::Any& rArg3, const uno::Any& rArg4, const uno::Any& rArg5, const uno::Any& rArg6,
+ const uno::Any& rArg7, const uno::Any& rArg8, const uno::Any& rArg9, const uno::Any& rArg10,
+ const uno::Any& rArg11, const uno::Any& rArg12, const uno::Any& rArg13, const uno::Any& rArg14,
+ const uno::Any& rArg15, const uno::Any& rArg16, const uno::Any& rArg17, const uno::Any& rArg18,
+ const uno::Any& rArg19, const uno::Any& rArg20, const uno::Any& rArg21, const uno::Any& rArg22,
+ const uno::Any& rArg23, const uno::Any& rArg24, const uno::Any& rArg25, const uno::Any& rArg26,
+ const uno::Any& rArg27, const uno::Any& rArg28, const uno::Any& rArg29, const uno::Any& rArg30 )
+{
+ if( !rArg1.is() || !rArg2.is() )
+ DebugHelper::basicexception( ERRCODE_BASIC_BAD_PARAMETER, {} );
+
+ ListOfScRange aList;
+ lclAddToListOfScRange( aList, uno::Any( rArg1 ) );
+ lclAddToListOfScRange( aList, uno::Any( rArg2 ) );
+ lclAddToListOfScRange( aList, rArg3 );
+ lclAddToListOfScRange( aList, rArg4 );
+ lclAddToListOfScRange( aList, rArg5 );
+ lclAddToListOfScRange( aList, rArg6 );
+ lclAddToListOfScRange( aList, rArg7 );
+ lclAddToListOfScRange( aList, rArg8 );
+ lclAddToListOfScRange( aList, rArg9 );
+ lclAddToListOfScRange( aList, rArg10 );
+ lclAddToListOfScRange( aList, rArg11 );
+ lclAddToListOfScRange( aList, rArg12 );
+ lclAddToListOfScRange( aList, rArg13 );
+ lclAddToListOfScRange( aList, rArg14 );
+ lclAddToListOfScRange( aList, rArg15 );
+ lclAddToListOfScRange( aList, rArg16 );
+ lclAddToListOfScRange( aList, rArg17 );
+ lclAddToListOfScRange( aList, rArg18 );
+ lclAddToListOfScRange( aList, rArg19 );
+ lclAddToListOfScRange( aList, rArg20 );
+ lclAddToListOfScRange( aList, rArg21 );
+ lclAddToListOfScRange( aList, rArg22 );
+ lclAddToListOfScRange( aList, rArg23 );
+ lclAddToListOfScRange( aList, rArg24 );
+ lclAddToListOfScRange( aList, rArg25 );
+ lclAddToListOfScRange( aList, rArg26 );
+ lclAddToListOfScRange( aList, rArg27 );
+ lclAddToListOfScRange( aList, rArg28 );
+ lclAddToListOfScRange( aList, rArg29 );
+ lclAddToListOfScRange( aList, rArg30 );
+
+ // simply join together all ranges as much as possible, strip out covered ranges etc.
+ lclJoinRanges( aList );
+
+ // create the VBA Range object
+ return lclCreateVbaRange( mxContext, getCurrentDocument(), aList );
+}
+
+double SAL_CALL
+ScVbaApplication::InchesToPoints( double Inches )
+{
+ return o3tl::convert(Inches, o3tl::Length::in, o3tl::Length::pt);
+}
+
+double SAL_CALL
+ScVbaApplication::CentimetersToPoints( double Centimeters )
+{
+ return o3tl::convert(Centimeters, o3tl::Length::cm, o3tl::Length::pt);
+}
+
+void
+ScVbaApplication::Volatile( const uno::Any& aVolatile )
+{
+ bool bVolatile = true;
+ aVolatile >>= bVolatile;
+ SbMethod* pMeth = StarBASIC::GetActiveMethod();
+ if ( pMeth )
+ {
+ uno::Reference< frame::XModel > xModel( getCurrentDocument() );
+ if ( ScDocShell* pShell = excel::getDocShell( xModel ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ rDoc.GetMacroManager()->SetUserFuncVolatile( pMeth->GetName(), bVolatile);
+ }
+ }
+
+// this is bound to break when loading the document
+}
+
+sal_Bool SAL_CALL
+ScVbaApplication::getDisplayFormulaBar()
+{
+ bool bRes = false;
+ ScTabViewShell* pViewShell = excel::getCurrentBestViewShell( mxContext );
+ if ( pViewShell )
+ {
+ SfxBoolItem sfxFormBar( FID_TOGGLEINPUTLINE);
+ SfxAllItemSet reqList( SfxGetpApp()->GetPool() );
+ reqList.Put( sfxFormBar );
+
+ pViewShell->GetState( reqList );
+ if ( const SfxBoolItem *pItem = reqList.GetItemIfSet( FID_TOGGLEINPUTLINE, false ) )
+ bRes = pItem->GetValue();
+ }
+ return bRes;
+}
+
+void SAL_CALL
+ScVbaApplication::setDisplayFormulaBar( sal_Bool _displayformulabar )
+{
+ ScTabViewShell* pViewShell = excel::getCurrentBestViewShell( mxContext );
+ if ( pViewShell && ( _displayformulabar != getDisplayFormulaBar() ) )
+ {
+ SfxAllItemSet reqList( SfxGetpApp()->GetPool() );
+ SfxRequest aReq( FID_TOGGLEINPUTLINE, SfxCallMode::SLOT, reqList );
+ pViewShell->Execute( aReq );
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Caller( const uno::Any& /*aIndex*/ )
+{
+ StarBASIC* pBasic = SfxApplication::GetBasic();
+ SbMethod* pMeth = static_cast<SbMethod*>(pBasic->GetRtl()->Find( "FuncCaller", SbxClassType::Method ));
+ uno::Any aRet;
+ if ( pMeth )
+ {
+ SbxVariableRef refTemp = pMeth;
+ // forces a broadcast
+ SbxVariableRef pNew = new SbxMethod( *static_cast<SbxMethod*>(pMeth));
+ aRet = sbxToUnoValue( pNew.get() );
+ }
+ return aRet;
+}
+
+uno::Reference< frame::XModel >
+ScVbaApplication::getCurrentDocument()
+{
+ return getCurrentExcelDoc(mxContext);
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::MenuBars( const uno::Any& aIndex )
+{
+ uno::Reference< XCommandBars > xCommandBars( CommandBars( uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< XCollection > xMenuBars( new ScVbaMenuBars( this, mxContext, xCommandBars ) );
+ if ( aIndex.hasValue() )
+ {
+ return xMenuBars->Item( aIndex, uno::Any() );
+ }
+
+ return uno::Any( xMenuBars );
+}
+
+uno::Any SAL_CALL
+ScVbaApplication::Rows( const uno::Any& aIndex )
+{
+ uno::Reference< excel::XWorksheet > xWorksheet = getActiveSheet();
+ if ( xWorksheet.is() )
+ return uno::Any( xWorksheet->Rows( aIndex ) );
+ return uno::Any();
+}
+
+void SAL_CALL ScVbaApplication::OnKey( const OUString& Key, const uno::Any& Procedure )
+{
+ try
+ {
+ // Perhaps we can catch some excel specific
+ // related behaviour here
+ VbaApplicationBase::OnKey( Key, Procedure );
+ }
+ catch( container::NoSuchElementException& )
+ {
+ // #TODO special handling for unhandled
+ // bindings
+ }
+}
+
+void SAL_CALL ScVbaApplication::setScreenUpdating(sal_Bool bUpdate)
+{
+ VbaApplicationBase::setScreenUpdating( bUpdate );
+
+ uno::Reference< frame::XModel > xModel( getCurrentExcelDoc( mxContext ), uno::UNO_SET_THROW );
+
+ ScDocShell* pDocShell = excel::getDocShell( xModel );
+ if (!pDocShell)
+ return;
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ if( bUpdate )
+ {
+ // Since setting ScreenUpdating from user code might be unpaired, avoid calling function,
+ // that asserts correct lock/unlock order and number, when not locked.
+ if(rDoc.IsAdjustHeightLocked())
+ rDoc.UnlockAdjustHeight();
+ if( !rDoc.IsAdjustHeightLocked() )
+ pDocShell->UpdateAllRowHeights();
+ }
+ else
+ {
+ rDoc.LockAdjustHeight();
+ }
+}
+
+void SAL_CALL ScVbaApplication::Undo()
+{
+ uno::Reference< frame::XModel > xModel( getThisExcelDoc( mxContext ), uno::UNO_SET_THROW );
+
+ ScTabViewShell* pViewShell = excel::getBestViewShell( xModel );
+ if ( pViewShell )
+ dispatchExecute( pViewShell, SID_UNDO );
+}
+
+// XInterfaceWithIID
+
+OUString SAL_CALL
+ScVbaApplication::getIID()
+{
+ return "{82154425-0FBF-11d4-8313-005004526AB4}";
+}
+
+// XConnectable
+
+OUString SAL_CALL
+ScVbaApplication::GetIIDForClassItselfNotCoclass()
+{
+ return "{82154426-0FBF-11D4-8313-005004526AB4}";
+}
+
+TypeAndIID SAL_CALL
+ScVbaApplication::GetConnectionPoint()
+{
+ TypeAndIID aResult =
+ { cppu::UnoType<excel::XApplicationOutgoing>::get(),
+ "{82154427-0FBF-11D4-8313-005004526AB4}"
+ };
+
+ return aResult;
+}
+
+uno::Reference<XConnectionPoint> SAL_CALL
+ScVbaApplication::FindConnectionPoint()
+{
+ uno::Reference<XConnectionPoint> xCP(new ScVbaApplicationOutgoingConnectionPoint(this));
+ return xCP;
+}
+
+// XSinkCaller
+
+void SAL_CALL
+ScVbaApplication::CallSinks( const OUString& Method, uno::Sequence< uno::Any >& Arguments )
+{
+ for (auto& i : mvSinks)
+ {
+ if (i.is())
+ i->Call(Method, Arguments);
+ }
+}
+
+OUString
+ScVbaApplication::getServiceImplName()
+{
+ return "ScVbaApplication";
+}
+
+uno::Sequence< OUString >
+ScVbaApplication::getServiceNames()
+{
+ static uno::Sequence< OUString > aServiceNames
+ {
+ "ooo.vba.excel.Application"
+ };
+ return aServiceNames;
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScVbaApplication_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new ScVbaApplication(context));
+}
+
+
+// ScVbaApplicationOutgoingConnectionPoint
+
+ScVbaApplicationOutgoingConnectionPoint::ScVbaApplicationOutgoingConnectionPoint( ScVbaApplication* pApp ) :
+ mpApp(pApp)
+{
+}
+
+// XConnectionPoint
+sal_uInt32 SAL_CALL
+ScVbaApplicationOutgoingConnectionPoint::Advise( const uno::Reference< XSink >& Sink )
+{
+ return mpApp->AddSink(Sink);
+}
+
+void SAL_CALL
+ScVbaApplicationOutgoingConnectionPoint::Unadvise( sal_uInt32 Cookie )
+{
+ mpApp->RemoveSink( Cookie );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaapplication.hxx b/sc/source/ui/vba/vbaapplication.hxx
new file mode 100644
index 0000000000..4a049100b6
--- /dev/null
+++ b/sc/source/ui/vba/vbaapplication.hxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <vector>
+
+#include <ooo/vba/XSinkCaller.hpp>
+#include <ooo/vba/excel/XApplication.hpp>
+
+#include <vbahelper/vbaapplicationbase.hxx>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+namespace ooo::vba { class XSink; }
+namespace ooo::vba::excel { class XFileDialog; }
+
+typedef cppu::ImplInheritanceHelper< VbaApplicationBase, ov::excel::XApplication, ov::XSinkCaller > ScVbaApplication_BASE;
+
+struct ScVbaAppSettings;
+
+class ScVbaApplication : public ScVbaApplication_BASE
+{
+private:
+ // note: member variables moved to struct "ScVbaAppSettings", see cxx file, to be shared by all application instances
+ ScVbaAppSettings& mrAppSettings;
+
+ // must be stored in order to get result paths from the same instance
+ css::uno::Reference< ov::excel::XFileDialog > m_xFileDialog;
+ sal_Int32 m_nDialogType;
+
+ /// @throws css::uno::RuntimeException
+ OUString getOfficePath( const OUString& sPath );
+
+ std::vector<css::uno::Reference< ooo::vba::XSink >> mvSinks;
+
+protected:
+ virtual css::uno::Reference< css::frame::XModel > getCurrentDocument() override;
+
+public:
+ explicit ScVbaApplication( const css::uno::Reference< css::uno::XComponentContext >& m_xContext );
+ virtual ~ScVbaApplication() override;
+
+ /** Returns true, if VBA document events are enabled. */
+ static bool getDocumentEventsEnabled();
+
+ sal_uInt32 AddSink( const css::uno::Reference< ooo::vba::XSink >& xSink );
+ void RemoveSink( sal_uInt32 nNumber );
+
+ // XExactName
+ virtual OUString SAL_CALL getExactName( const OUString& aApproximateName ) override;
+
+ // XInvocation
+ virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection() override;
+ virtual css::uno::Any SAL_CALL invoke(const OUString& FunctionName, const css::uno::Sequence< css::uno::Any >& Params, css::uno::Sequence< sal_Int16 >& OutParamIndex, css::uno::Sequence< css::uno::Any >& OutParam) override;
+ virtual void SAL_CALL setValue(const OUString& PropertyName, const css::uno::Any& Value) override;
+ virtual css::uno::Any SAL_CALL getValue(const OUString& PropertyName) override;
+ virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override;
+ virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override;
+
+ // XApplication
+ virtual void SAL_CALL setDefaultFilePath( const OUString& DefaultFilePath ) override;
+ virtual OUString SAL_CALL getDefaultFilePath() override;
+ virtual OUString SAL_CALL getPathSeparator() override;
+ virtual OUString SAL_CALL getLibraryPath() override;
+ virtual OUString SAL_CALL getTemplatesPath() override;
+ virtual OUString SAL_CALL getOperatingSystem() override;
+
+ virtual OUString SAL_CALL getName() override;
+ virtual sal_Bool SAL_CALL getDisplayAlerts() override;
+ virtual void SAL_CALL setDisplayAlerts( sal_Bool displayAlerts ) override;
+ virtual ::sal_Int32 SAL_CALL getCalculation() override;
+ virtual void SAL_CALL setCalculation( ::sal_Int32 _calculation ) override;
+ virtual css::uno::Any SAL_CALL getSelection() override;
+ virtual css::uno::Reference< ov::excel::XWorkbook > SAL_CALL getActiveWorkbook() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getActiveCell() override;
+ virtual css::uno::Reference< ov::excel::XWindow > SAL_CALL getActiveWindow() override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL getActiveSheet() override;
+ virtual sal_Bool SAL_CALL getDisplayFormulaBar() override;
+ virtual void SAL_CALL setDisplayFormulaBar(sal_Bool _displayformulabar) override;
+
+ virtual css::uno::Reference< ov::XAssistant > SAL_CALL getAssistant() override;
+ virtual css::uno::Reference< ov::excel::XWorkbook > SAL_CALL getThisWorkbook() override;
+
+ virtual css::uno::Any SAL_CALL GetOpenFilename(const css::uno::Any& FileFilter, const css::uno::Any& FilterIndex, const css::uno::Any& Title, const css::uno::Any& ButtonText, const css::uno::Any& MultiSelect) override;
+ virtual css::uno::Any SAL_CALL International( sal_Int32 Index ) override;
+ virtual css::uno::Any SAL_CALL FileDialog( const css::uno::Any& DialogType ) override;
+ virtual css::uno::Any SAL_CALL Workbooks( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Worksheets( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL WorksheetFunction( ) override;
+ virtual css::uno::Any SAL_CALL Evaluate( const OUString& Name ) override;
+ virtual css::uno::Any SAL_CALL Dialogs( const css::uno::Any& DialogIndex ) override;
+ virtual css::uno::Any SAL_CALL getCutCopyMode() override;
+ virtual void SAL_CALL setCutCopyMode( const css::uno::Any& _cutcopymode ) override;
+ virtual css::uno::Any SAL_CALL getStatusBar() override;
+ virtual void SAL_CALL setStatusBar( const css::uno::Any& _statusbar ) override;
+ virtual css::uno::Any SAL_CALL getWindowState() override;
+ virtual void SAL_CALL setWindowState(const css::uno::Any& rWindowState) override;
+ virtual ::sal_Int32 SAL_CALL getCursor() override;
+ virtual void SAL_CALL setCursor( ::sal_Int32 _cursor ) override;
+ virtual void SAL_CALL OnKey( const OUString& Key, const css::uno::Any& Procedure ) override;
+ virtual void SAL_CALL setScreenUpdating( sal_Bool bUpdate ) override;
+ virtual sal_Bool SAL_CALL getEnableEvents() override;
+ virtual void SAL_CALL setEnableEvents( sal_Bool bEnable ) override;
+ virtual sal_Bool SAL_CALL getEnableCancelKey() override;
+ virtual void SAL_CALL setEnableCancelKey( sal_Bool bEnable ) override;
+
+ virtual sal_Bool SAL_CALL getDisplayFullScreen() override;
+ virtual void SAL_CALL setDisplayFullScreen( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL getDisplayScrollBars() override;
+ virtual void SAL_CALL setDisplayScrollBars( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL getDisplayExcel4Menus() override;
+ virtual void SAL_CALL setDisplayExcel4Menus( sal_Bool bSet ) override;
+
+ virtual sal_Bool SAL_CALL getDisplayNoteIndicator() override;
+ virtual void SAL_CALL setDisplayNoteIndicator( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL getShowWindowsInTaskbar() override;
+ virtual void SAL_CALL setShowWindowsInTaskbar( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL getIteration() override;
+ virtual void SAL_CALL setIteration( sal_Bool bSet ) override;
+
+ virtual css::uno::Any SAL_CALL Windows( const css::uno::Any& aIndex ) override;
+ virtual void SAL_CALL wait( double time ) override;
+ virtual css::uno::Any SAL_CALL Range( const css::uno::Any& Cell1, const css::uno::Any& Cell2 ) override;
+ virtual css::uno::Any SAL_CALL Names( const css::uno::Any& aIndex ) override;
+ virtual void SAL_CALL GoTo( const css::uno::Any& Reference, const css::uno::Any& Scroll ) override;
+ virtual void SAL_CALL Calculate() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Intersect( const css::uno::Reference< ov::excel::XRange >& Arg1, const css::uno::Reference< ov::excel::XRange >& Arg2, const css::uno::Any& Arg3, const css::uno::Any& Arg4, const css::uno::Any& Arg5, const css::uno::Any& Arg6, const css::uno::Any& Arg7, const css::uno::Any& Arg8, const css::uno::Any& Arg9, const css::uno::Any& Arg10, const css::uno::Any& Arg11, const css::uno::Any& Arg12, const css::uno::Any& Arg13, const css::uno::Any& Arg14, const css::uno::Any& Arg15, const css::uno::Any& Arg16, const css::uno::Any& Arg17, const css::uno::Any& Arg18, const css::uno::Any& Arg19, const css::uno::Any& Arg20, const css::uno::Any& Arg21, const css::uno::Any& Arg22, const css::uno::Any& Arg23, const css::uno::Any& Arg24, const css::uno::Any& Arg25, const css::uno::Any& Arg26, const css::uno::Any& Arg27, const css::uno::Any& Arg28, const css::uno::Any& Arg29, const css::uno::Any& Arg30 ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Union( const css::uno::Reference< ov::excel::XRange >& Arg1, const css::uno::Reference< ov::excel::XRange >& Arg2, const css::uno::Any& Arg3, const css::uno::Any& Arg4, const css::uno::Any& Arg5, const css::uno::Any& Arg6, const css::uno::Any& Arg7, const css::uno::Any& Arg8, const css::uno::Any& Arg9, const css::uno::Any& Arg10, const css::uno::Any& Arg11, const css::uno::Any& Arg12, const css::uno::Any& Arg13, const css::uno::Any& Arg14, const css::uno::Any& Arg15, const css::uno::Any& Arg16, const css::uno::Any& Arg17, const css::uno::Any& Arg18, const css::uno::Any& Arg19, const css::uno::Any& Arg20, const css::uno::Any& Arg21, const css::uno::Any& Arg22, const css::uno::Any& Arg23, const css::uno::Any& Arg24, const css::uno::Any& Arg25, const css::uno::Any& Arg26, const css::uno::Any& Arg27, const css::uno::Any& Arg28, const css::uno::Any& Arg29, const css::uno::Any& Arg30 ) override;
+ virtual double SAL_CALL CentimetersToPoints( double Centimeters ) override;
+ virtual double SAL_CALL InchesToPoints( double Inches ) override;
+ virtual void SAL_CALL Volatile( const css::uno::Any& Volatile ) override;
+ virtual css::uno::Any SAL_CALL MenuBars( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Rows( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Caller( const css::uno::Any& aIndex ) override;
+ virtual void SAL_CALL Undo() override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+ // XInterfaceWithIID
+ virtual OUString SAL_CALL getIID() override;
+
+ // XConnectable
+ virtual OUString SAL_CALL GetIIDForClassItselfNotCoclass() override;
+ virtual ov::TypeAndIID SAL_CALL GetConnectionPoint() override;
+ virtual css::uno::Reference<ov::XConnectionPoint> SAL_CALL FindConnectionPoint() override;
+
+ // XSinkCaller
+ virtual void SAL_CALL CallSinks( const OUString& Method, css::uno::Sequence< css::uno::Any >& Arguments ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaassistant.cxx b/sc/source/ui/vba/vbaassistant.cxx
new file mode 100644
index 0000000000..ea13fbcd62
--- /dev/null
+++ b/sc/source/ui/vba/vbaassistant.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 <ooo/vba/office/MsoAnimationType.hpp>
+
+#include"vbaassistant.hxx"
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+using namespace ooo::vba::office::MsoAnimationType;
+
+constexpr OUStringLiteral g_sName = u"Clippit";
+
+ScVbaAssistant::ScVbaAssistant( const uno::Reference< XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext ): ScVbaAssistantImpl_BASE(rParent, rContext)
+{
+ m_bIsVisible = false;
+ m_nPointsLeft = 795;
+ m_nPointsTop = 248;
+ m_nAnimation = msoAnimationIdle;
+}
+
+ScVbaAssistant::~ScVbaAssistant()
+{
+}
+
+sal_Bool SAL_CALL ScVbaAssistant::getVisible()
+{
+ return m_bIsVisible;
+}
+
+void SAL_CALL ScVbaAssistant::setVisible( sal_Bool bVisible )
+{
+ m_bIsVisible = bVisible;
+}
+
+sal_Bool SAL_CALL ScVbaAssistant::getOn()
+{
+ return false;
+}
+
+void SAL_CALL ScVbaAssistant::setOn( sal_Bool bOn )
+{
+ setVisible( bOn );
+}
+
+::sal_Int32 SAL_CALL
+ScVbaAssistant::getTop()
+{
+ return m_nPointsTop;
+}
+void SAL_CALL
+ScVbaAssistant::setTop( ::sal_Int32 _top )
+{
+ m_nPointsTop = _top;
+}
+::sal_Int32 SAL_CALL
+ScVbaAssistant::getLeft()
+{
+ return m_nPointsLeft;
+}
+void SAL_CALL
+ScVbaAssistant::setLeft( ::sal_Int32 _left )
+{
+ m_nPointsLeft = _left;
+}
+::sal_Int32 SAL_CALL
+ScVbaAssistant::getAnimation()
+{
+ return m_nAnimation;
+}
+void SAL_CALL
+ScVbaAssistant::setAnimation( ::sal_Int32 _animation )
+{
+ m_nAnimation = _animation;
+}
+
+OUString SAL_CALL
+ScVbaAssistant::Name( )
+{
+ return g_sName;
+}
+
+OUString
+ScVbaAssistant::getServiceImplName()
+{
+ return "ScVbaAssistant";
+}
+
+uno::Sequence< OUString >
+ScVbaAssistant::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.Assistant"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaassistant.hxx b/sc/source/ui/vba/vbaassistant.hxx
new file mode 100644
index 0000000000..674e644451
--- /dev/null
+++ b/sc/source/ui/vba/vbaassistant.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 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/XAssistant.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef ::cppu::WeakImplHelper< ov::XAssistant > Assistant;
+typedef InheritedHelperInterfaceImpl< Assistant > ScVbaAssistantImpl_BASE;
+
+class ScVbaAssistant : public ScVbaAssistantImpl_BASE
+{
+private:
+ bool m_bIsVisible;
+ sal_Int32 m_nPointsLeft;
+ sal_Int32 m_nPointsTop;
+ sal_Int32 m_nAnimation;
+public:
+ ScVbaAssistant( const css::uno::Reference< ov::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext );
+ virtual ~ScVbaAssistant() override;
+ // XAssistant
+ virtual sal_Bool SAL_CALL getOn() override;
+ virtual void SAL_CALL setOn( sal_Bool _on ) override;
+ virtual sal_Bool SAL_CALL getVisible() override;
+ virtual void SAL_CALL setVisible( sal_Bool _visible ) override;
+ virtual ::sal_Int32 SAL_CALL getTop() override;
+ virtual void SAL_CALL setTop( ::sal_Int32 _top ) override;
+ virtual ::sal_Int32 SAL_CALL getLeft() override;
+ virtual void SAL_CALL setLeft( ::sal_Int32 _left ) override;
+ virtual ::sal_Int32 SAL_CALL getAnimation() override;
+ virtual void SAL_CALL setAnimation( ::sal_Int32 _animation ) override;
+
+ virtual OUString SAL_CALL Name( ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaaxes.cxx b/sc/source/ui/vba/vbaaxes.cxx
new file mode 100644
index 0000000000..66155bccfc
--- /dev/null
+++ b/sc/source/ui/vba/vbaaxes.cxx
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbaaxes.hxx"
+#include "vbaaxis.hxx"
+#include "vbachart.hxx"
+#include <basic/sberrors.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/script/BasicErrorException.hpp>
+#include <ooo/vba/excel/XlAxisType.hpp>
+#include <ooo/vba/excel/XlAxisGroup.hpp>
+#include <ooo/vba/excel/XAxis.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+using namespace ::ooo::vba::excel::XlAxisType;
+using namespace ::ooo::vba::excel::XlAxisGroup;
+
+// each 'Item' in the Axes collection is indexed via 2 indexes, group and type.
+// We need to 'flatten' this into a single index in order to be able to wrap
+// iteration over the set of Axis(s) in a XIndexAccess implementation
+
+typedef ::std::pair<sal_Int32, sal_Int32 > AxesCoordinate; // type and group combination
+
+namespace {
+
+class EnumWrapper : public EnumerationHelper_BASE
+{
+ uno::Reference<container::XIndexAccess > m_xIndexAccess;
+ sal_Int32 nIndex;
+public:
+ explicit EnumWrapper( uno::Reference< container::XIndexAccess > xIndexAccess ) : m_xIndexAccess(std::move( xIndexAccess )), nIndex( 0 ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( nIndex < m_xIndexAccess->getCount() );
+ }
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( nIndex < m_xIndexAccess->getCount() )
+ return m_xIndexAccess->getByIndex( nIndex++ );
+ throw container::NoSuchElementException();
+ }
+};
+
+}
+
+uno::Reference< excel::XAxis >
+ScVbaAxes::createAxis( const uno::Reference< excel::XChart >& xChart, const uno::Reference< uno::XComponentContext >& xContext, sal_Int32 nType, sal_Int32 nAxisGroup )
+{
+ ScVbaChart* pChart = static_cast< ScVbaChart* >( xChart.get() );
+ if ( !pChart )
+ throw uno::RuntimeException("Object failure, can't access chart implementation" );
+
+ uno::Reference< beans::XPropertySet > xAxisPropertySet;
+ if ((nType == xlCategory) || (nType == xlSeriesAxis) || (nType == xlValue))
+ {
+ if ((nAxisGroup != xlPrimary) && (nAxisGroup != xlSecondary))
+ DebugHelper::runtimeexception(ERRCODE_BASIC_METHOD_FAILED);
+ xAxisPropertySet.set( pChart->getAxisPropertySet(nType, nAxisGroup), uno::UNO_SET_THROW );
+ }
+ else
+ DebugHelper::runtimeexception(ERRCODE_BASIC_METHOD_FAILED);
+ return new ScVbaAxis( pChart, xContext, xAxisPropertySet, nType, nAxisGroup);
+}
+
+namespace {
+
+class AxisIndexWrapper : public ::cppu::WeakImplHelper< container::XIndexAccess >
+{
+ // if necessary for better performance we could change this into a map and cache the
+ // indices -> Axis, currently we create a new Axis object
+ // on each getByIndex
+ uno::Reference< uno::XComponentContext > mxContext;
+ std::vector< AxesCoordinate > mCoordinates;
+ rtl::Reference< ScVbaChart > mxChart;
+public:
+ AxisIndexWrapper( uno::Reference< uno::XComponentContext > xContext, rtl::Reference< ScVbaChart > xChart ) : mxContext(std::move( xContext )), mxChart(std::move( xChart ))
+ {
+ if ( !mxChart.is() )
+ return;
+
+ ScVbaChart* pChart = mxChart.get();
+ // primary
+ bool bBool = false;
+ uno::Reference< beans::XPropertySet > xDiagramPropertySet( pChart->xDiagramPropertySet() );
+ if ( ( xDiagramPropertySet->getPropertyValue("HasXAxis") >>= bBool ) && bBool )
+ mCoordinates.emplace_back( xlPrimary, xlCategory );
+ if ( ( xDiagramPropertySet->getPropertyValue("HasYAxis") >>= bBool ) && bBool )
+ mCoordinates.emplace_back( xlPrimary, xlSeriesAxis );
+
+ if ( pChart->is3D() )
+ mCoordinates.emplace_back( xlPrimary, xlValue );
+
+ // secondary
+ if ( ( xDiagramPropertySet->getPropertyValue("HasSecondaryXAxis") >>= bBool ) && bBool )
+ mCoordinates.emplace_back( xlSecondary, xlCategory );
+ if ( ( xDiagramPropertySet->getPropertyValue("HasSecondaryYAxis") >>= bBool ) && bBool )
+ mCoordinates.emplace_back( xlSecondary, xlSeriesAxis );
+
+ }
+ virtual ::sal_Int32 SAL_CALL getCount() override { return mCoordinates.size(); }
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ try
+ {
+ AxesCoordinate dIndexes = mCoordinates[ Index ];
+ return uno::Any( ScVbaAxes::createAxis( mxChart, mxContext, dIndexes.second, dIndexes.first ) );
+ }
+ catch (const css::script::BasicErrorException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException(
+ "Error Getting Index!",
+ getXWeak(),
+ anyEx );
+ }
+ }
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<excel::XAxis>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ return ( !mCoordinates.empty() );
+ }
+};
+
+uno::Reference< container::XIndexAccess > createIndexWrapper( const rtl::Reference< ScVbaChart >& xChart, const uno::Reference< uno::XComponentContext >& xContext )
+{
+ return new AxisIndexWrapper( xContext, xChart );
+}
+
+}
+
+// #FIXME The collection semantics will never work as this object is not yet initialised correctly
+ScVbaAxes::ScVbaAxes( const uno::Reference< XHelperInterface >& xParent,const uno::Reference< uno::XComponentContext > & xContext, const rtl::Reference< ScVbaChart >& xChart ) : ScVbaAxes_BASE( xParent, xContext, createIndexWrapper( xChart, xContext )), moChartParent( xChart )
+{
+}
+
+uno::Type SAL_CALL
+ScVbaAxes::getElementType()
+{
+ return cppu::UnoType<excel::XAxes>::get();
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+ScVbaAxes::createEnumeration()
+{
+ return new EnumWrapper( m_xIndexAccess );
+}
+
+uno::Any SAL_CALL
+ScVbaAxes::Item( const css::uno::Any& _nType, const css::uno::Any& _oAxisGroup)
+{
+ // #TODO map the possible index combinations to a container::XIndexAccess wrapper impl
+ // using a vector of valid std::pair maybe?
+ // body helper api port bits
+ sal_Int32 nAxisGroup = xlPrimary;
+ sal_Int32 nType = -1;
+ if ( !_nType.hasValue() || !( _nType >>= nType ) )
+ throw uno::RuntimeException("Axes::Item Failed to extract type" );
+
+ if ( _oAxisGroup.hasValue() )
+ _oAxisGroup >>= nAxisGroup ;
+
+ return uno::Any( createAxis( moChartParent, mxContext, nType, nAxisGroup ) );
+}
+
+uno::Any
+ScVbaAxes::createCollectionObject(const css::uno::Any& aSource)
+{
+ return aSource; // pass through ( it's already an XAxis object
+}
+
+OUString
+ScVbaAxes::getServiceImplName()
+{
+ return "ScVbaAxes";
+}
+
+uno::Sequence< OUString >
+ScVbaAxes::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Axes"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaaxes.hxx b/sc/source/ui/vba/vbaaxes.hxx
new file mode 100644
index 0000000000..62f3971746
--- /dev/null
+++ b/sc/source/ui/vba/vbaaxes.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XAxes.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+#include <rtl/ref.hxx>
+
+namespace ooo::vba::excel { class XAxis; }
+namespace ooo::vba::excel { class XChart; }
+class ScVbaChart;
+
+typedef CollTestImplHelper< ov::excel::XAxes > ScVbaAxes_BASE;
+class ScVbaAxes : public ScVbaAxes_BASE
+{
+ rtl::Reference< ScVbaChart > moChartParent; // not the true parent I guess
+public:
+ ScVbaAxes( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const rtl::Reference< ScVbaChart >& xChart );
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ // XCollection
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& aIndex, const css::uno::Any& aIndex2 ) override;
+ virtual css::uno::Any createCollectionObject(const css::uno::Any&) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+ /// @throws css::uno::RuntimeException
+ /// @throws css::script::BasicErrorException
+ static css::uno::Reference< ov::excel::XAxis > createAxis( const css::uno::Reference< ov::excel::XChart >& xChart, const css::uno::Reference< css::uno::XComponentContext >& xContext, sal_Int32 nType, sal_Int32 nAxisGroup );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaaxis.cxx b/sc/source/ui/vba/vbaaxis.cxx
new file mode 100644
index 0000000000..76cf9fe066
--- /dev/null
+++ b/sc/source/ui/vba/vbaaxis.cxx
@@ -0,0 +1,664 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbaaxis.hxx"
+#include <ooo/vba/excel/XlAxisCrosses.hpp>
+#include <ooo/vba/excel/XlAxisType.hpp>
+#include <ooo/vba/excel/XlScaleType.hpp>
+#include <utility>
+#include "vbaaxistitle.hxx"
+#include "vbachart.hxx"
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+using namespace ::ooo::vba::excel::XlAxisCrosses;
+using namespace ::ooo::vba::excel::XlAxisType;
+using namespace ::ooo::vba::excel::XlScaleType;
+
+constexpr OUString ORIGIN(u"Origin"_ustr);
+constexpr OUString AUTOORIGIN(u"AutoOrigin"_ustr);
+constexpr OUString VBA_MIN(u"Max"_ustr);
+constexpr OUStringLiteral VBA_MAX(u"Min");
+ScVbaChart*
+ScVbaAxis::getChartPtr()
+{
+ ScVbaChart* pChart = moChartParent.get();
+ if ( !pChart )
+ throw uno::RuntimeException("Can't access parent chart impl" );
+ return pChart;
+}
+
+bool
+ScVbaAxis::isValueAxis()
+{
+ if ( getType() == xlCategory )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return true;
+}
+
+ScVbaAxis::ScVbaAxis( const rtl::Reference< ScVbaChart >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ uno::Reference< beans::XPropertySet > _xPropertySet,
+ sal_Int32 _nType, sal_Int32 _nGroup )
+ : ScVbaAxis_BASE( xParent, xContext ),
+ moChartParent(xParent),
+ mxPropertySet(std::move( _xPropertySet )),
+ mnType( _nType ), mnGroup( _nGroup ),
+ maShapeHelper( uno::Reference< drawing::XShape >( mxPropertySet, uno::UNO_QUERY ) ),
+ bCrossesAreCustomized( false )
+{
+ setType(_nType);
+ setCrosses(xlAxisCrossesAutomatic);
+}
+
+void SAL_CALL
+ScVbaAxis::Delete( )
+{
+ uno::Reference< lang::XComponent > xComponent( mxPropertySet, uno::UNO_QUERY_THROW );
+ xComponent->dispose();
+}
+
+ uno::Reference< ::ooo::vba::excel::XAxisTitle > SAL_CALL
+ScVbaAxis::getAxisTitle( )
+{
+ uno::Reference< excel::XAxisTitle > xAxisTitle;
+ try
+ {
+ ScVbaChart* pChart = getChartPtr();
+
+ if (getHasTitle() )
+ {
+ int nType = getType();
+ switch(nType)
+ {
+ case xlCategory:
+ xAxisTitle = new ScVbaAxisTitle(this, mxContext, pChart->xAxisXSupplier->getXAxisTitle());
+ break;
+ case xlSeriesAxis:
+ xAxisTitle = new ScVbaAxisTitle(this, mxContext, pChart->xAxisZSupplier->getZAxisTitle());
+ break;
+ default: // xlValue:
+ xAxisTitle = new ScVbaAxisTitle(this, mxContext, pChart->xAxisYSupplier->getYAxisTitle());
+ break;
+ }
+ }
+ }
+ catch (const uno::Exception& e)
+ {
+ DebugHelper::basicexception(e);
+ }
+ return xAxisTitle;
+
+}
+
+void SAL_CALL
+ScVbaAxis::setDisplayUnit( ::sal_Int32 /*DisplayUnit*/ )
+{
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+}
+
+::sal_Int32 SAL_CALL
+ScVbaAxis::getDisplayUnit( )
+{
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ return -1;
+}
+
+void SAL_CALL
+ScVbaAxis::setCrosses( ::sal_Int32 _nCrosses )
+{
+ try
+ {
+ double fNum = 0.0;
+ switch (_nCrosses)
+ {
+ case xlAxisCrossesAutomatic: //Microsoft Excel sets the axis crossing point.
+ mxPropertySet->setPropertyValue(AUTOORIGIN, uno::Any( true ) );
+ bCrossesAreCustomized = false;
+ return;
+ case xlAxisCrossesMinimum: // The axis crosses at the minimum value.
+ mxPropertySet->getPropertyValue(VBA_MIN) >>= fNum;
+ setCrossesAt( fNum );
+ bCrossesAreCustomized = false;
+ break;
+ case xlAxisCrossesMaximum: // The axis crosses at the maximum value.
+ mxPropertySet->getPropertyValue(VBA_MAX) >>= fNum;
+ setCrossesAt(fNum);
+ bCrossesAreCustomized = false;
+ break;
+ default: //xlAxisCrossesCustom
+ bCrossesAreCustomized = true;
+ break;
+ }
+ mxPropertySet->setPropertyValue(AUTOORIGIN, uno::Any(false) );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+::sal_Int32 SAL_CALL
+ScVbaAxis::getCrosses( )
+{
+ sal_Int32 nCrosses = xlAxisCrossesCustom;
+ try
+ {
+ bool bisAutoOrigin = false;
+ mxPropertySet->getPropertyValue(AUTOORIGIN) >>= bisAutoOrigin;
+ if (bisAutoOrigin)
+ nCrosses = xlAxisCrossesAutomatic;
+ else
+ {
+ if (bCrossesAreCustomized)
+ nCrosses = xlAxisCrossesCustom;
+ else
+ {
+ double forigin = 0.0;
+ mxPropertySet->getPropertyValue(ORIGIN) >>= forigin;
+ double fmin = 0.0;
+ mxPropertySet->getPropertyValue(VBA_MIN) >>= fmin;
+ if (forigin == fmin)
+ nCrosses = xlAxisCrossesMinimum;
+ else
+ nCrosses = xlAxisCrossesMaximum;
+ }
+ }
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return nCrosses;
+}
+
+ void SAL_CALL
+ScVbaAxis::setCrossesAt( double _fCrossesAt )
+{
+ try
+ {
+ setMaximumScaleIsAuto( false );
+ setMinimumScaleIsAuto( false );
+ mxPropertySet->setPropertyValue(ORIGIN, uno::Any(_fCrossesAt));
+ }
+ catch (const uno::Exception& e)
+ {
+ DebugHelper::basicexception(e);
+ }
+}
+
+ double SAL_CALL
+ScVbaAxis::getCrossesAt( )
+{
+ double fCrosses = 0.0;
+ try
+ {
+ mxPropertySet->getPropertyValue(ORIGIN) >>= fCrosses;
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return fCrosses;
+}
+
+void SAL_CALL
+ScVbaAxis::setType( ::sal_Int32 _nType )
+{
+ mnType = _nType;
+}
+
+::sal_Int32 SAL_CALL
+ScVbaAxis::getType( )
+{
+ return mnType;
+}
+
+void SAL_CALL
+ScVbaAxis::setHasTitle( sal_Bool _bHasTitle )
+{
+ try
+ {
+ ScVbaChart* pChart = getChartPtr();
+ sal_Int32 nType = getType();
+ switch(nType)
+ {
+ case xlCategory:
+ pChart->mxDiagramPropertySet->setPropertyValue("HasXAxisTitle", uno::Any(_bHasTitle));
+ break;
+ case xlSeriesAxis:
+ pChart->mxDiagramPropertySet->setPropertyValue("HasZAxisTitle", uno::Any(_bHasTitle));
+ break;
+ default: // xlValue:
+ pChart->mxDiagramPropertySet->setPropertyValue("HasYAxisTitle", uno::Any(_bHasTitle));
+ }
+
+ }
+ catch (const uno::Exception& e)
+ {
+ DebugHelper::basicexception(e);
+ }
+}
+
+ sal_Bool SAL_CALL
+ScVbaAxis::getHasTitle( )
+{
+ bool bHasTitle = false;
+ try
+ {
+ ScVbaChart* pChart = getChartPtr();
+ int nType = getType();
+ switch(nType)
+ {
+ case xlCategory:
+ pChart->mxDiagramPropertySet->getPropertyValue("HasXAxisTitle") >>= bHasTitle;
+ break;
+ case xlSeriesAxis:
+ pChart->mxDiagramPropertySet->getPropertyValue("HasZAxisTitle") >>= bHasTitle;
+ break;
+ default: // xlValue:
+ pChart->mxDiagramPropertySet->getPropertyValue("HasYAxisTitle") >>= bHasTitle;
+ }
+ }
+ catch (const uno::Exception& e)
+ {
+ DebugHelper::basicexception(e);
+ }
+ return bHasTitle;
+}
+
+void SAL_CALL
+ScVbaAxis::setMinorUnit( double _fMinorUnit )
+{
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->setPropertyValue("StepHelp", uno::Any(_fMinorUnit));
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+double SAL_CALL
+ScVbaAxis::getMinorUnit( )
+{
+ double fMinor = 1.0;
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->getPropertyValue("StepHelp") >>= fMinor;
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return fMinor;
+}
+
+void SAL_CALL
+ScVbaAxis::setMinorUnitIsAuto( sal_Bool _bMinorUnitIsAuto )
+{
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->setPropertyValue("AutoStepHelp", uno::Any(_bMinorUnitIsAuto));
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+ sal_Bool SAL_CALL
+ScVbaAxis::getMinorUnitIsAuto( )
+{
+ bool bIsAuto = false;
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->getPropertyValue("AutoStepHelp") >>= bIsAuto;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return bIsAuto;
+}
+
+void SAL_CALL
+ScVbaAxis::setReversePlotOrder( sal_Bool /*ReversePlotOrder*/ )
+{
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+}
+
+sal_Bool SAL_CALL
+ScVbaAxis::getReversePlotOrder( )
+{
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ return false;
+}
+
+void SAL_CALL
+ScVbaAxis::setMajorUnit( double _fMajorUnit )
+{
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->setPropertyValue("StepMain", uno::Any(_fMajorUnit));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+double SAL_CALL
+ScVbaAxis::getMajorUnit( )
+{
+ double fMax = 1.0;
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->getPropertyValue("StepMain") >>= fMax;
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return fMax;
+}
+
+void SAL_CALL
+ScVbaAxis::setMajorUnitIsAuto( sal_Bool _bMajorUnitIsAuto )
+{
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->setPropertyValue("AutoStepMain", uno::Any( _bMajorUnitIsAuto ));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+sal_Bool SAL_CALL
+ScVbaAxis::getMajorUnitIsAuto( )
+{
+ bool bIsAuto = false;
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->getPropertyValue("AutoStepMain") >>= bIsAuto;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return bIsAuto;
+}
+
+void SAL_CALL
+ScVbaAxis::setMaximumScale( double _fMaximumScale )
+{
+ try
+ {
+ if ( isValueAxis() )
+ {
+ mxPropertySet->setPropertyValue("Max", uno::Any(_fMaximumScale));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+double SAL_CALL
+ScVbaAxis::getMaximumScale( )
+{
+ double fMax = 1.0;
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->getPropertyValue("Max") >>= fMax;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return fMax;
+
+}
+
+void SAL_CALL
+ScVbaAxis::setMaximumScaleIsAuto( sal_Bool _bMaximumScaleIsAuto )
+{
+ try
+ {
+ if ( isValueAxis() )
+ mxPropertySet->setPropertyValue("AutoMax", uno::Any( _bMaximumScaleIsAuto ));
+
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+sal_Bool SAL_CALL
+ScVbaAxis::getMaximumScaleIsAuto( )
+{
+ bool bIsAuto = false;
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->getPropertyValue("AutoMax") >>= bIsAuto;
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception( ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return bIsAuto;
+}
+
+void SAL_CALL
+ScVbaAxis::setMinimumScale( double _fMinimumScale )
+{
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->setPropertyValue("Min", uno::Any( _fMinimumScale ) );
+ }
+ catch ( uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+double SAL_CALL
+ScVbaAxis::getMinimumScale( )
+{
+ double fMin = 0.0;
+ try
+ {
+ if (isValueAxis())
+ mxPropertySet->getPropertyValue("Min") >>= fMin;
+ }
+ catch (const uno::Exception& e)
+ {
+ DebugHelper::basicexception(e);
+ }
+ return fMin;
+}
+
+void SAL_CALL
+ScVbaAxis::setMinimumScaleIsAuto( sal_Bool _bMinimumScaleIsAuto )
+{
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->setPropertyValue("AutoMin", uno::Any(_bMinimumScaleIsAuto));
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+sal_Bool SAL_CALL
+ScVbaAxis::getMinimumScaleIsAuto( )
+{
+ bool bIsAuto = false;
+ try
+ {
+ if (isValueAxis())
+ {
+ mxPropertySet->getPropertyValue("AutoMin") >>= bIsAuto;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return bIsAuto;
+}
+
+::sal_Int32 SAL_CALL
+ScVbaAxis::getAxisGroup( )
+{
+ return mnGroup;
+}
+
+void SAL_CALL
+ScVbaAxis::setScaleType( ::sal_Int32 _nScaleType )
+{
+ try
+ {
+ if (isValueAxis())
+ {
+ switch (_nScaleType)
+ {
+ case xlScaleLinear:
+ mxPropertySet->setPropertyValue("Logarithmic", uno::Any( false ) );
+ break;
+ case xlScaleLogarithmic:
+ mxPropertySet->setPropertyValue("Logarithmic", uno::Any( true ) );
+ break;
+ default:
+ // According to MS the parameter is ignored and no Error is thrown
+ break;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+::sal_Int32 SAL_CALL
+ScVbaAxis::getScaleType( )
+{
+ sal_Int32 nScaleType = xlScaleLinear;
+ try
+ {
+ if (isValueAxis())
+ {
+ bool bisLogarithmic = false;
+ mxPropertySet->getPropertyValue( "Logarithmic" ) >>= bisLogarithmic;
+ if (bisLogarithmic)
+ nScaleType = xlScaleLogarithmic;
+ else
+ nScaleType = xlScaleLinear;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return nScaleType;
+}
+
+double SAL_CALL
+ScVbaAxis::getHeight( )
+{
+ return maShapeHelper.getHeight();
+}
+
+void SAL_CALL ScVbaAxis::setHeight( double height )
+{
+ maShapeHelper.setHeight( height );
+}
+double SAL_CALL ScVbaAxis::getWidth( )
+{
+ return maShapeHelper.getWidth( );
+}
+void SAL_CALL ScVbaAxis::setWidth( double width )
+{
+ maShapeHelper.setWidth( width );
+}
+double SAL_CALL ScVbaAxis::getTop( )
+{
+ return maShapeHelper.getTop( );
+}
+void SAL_CALL ScVbaAxis::setTop( double top )
+{
+ maShapeHelper.setTop( top );
+}
+double SAL_CALL ScVbaAxis::getLeft( )
+{
+ return maShapeHelper.getLeft( );
+}
+void SAL_CALL ScVbaAxis::setLeft( double left )
+{
+ maShapeHelper.setLeft( left );
+}
+
+OUString
+ScVbaAxis::getServiceImplName()
+{
+ return "ScVbaAxis";
+}
+
+uno::Sequence< OUString >
+ScVbaAxis::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Axis"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaaxis.hxx b/sc/source/ui/vba/vbaaxis.hxx
new file mode 100644
index 0000000000..8fcb9a3990
--- /dev/null
+++ b/sc/source/ui/vba/vbaaxis.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 .
+ */
+#pragma once
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <ooo/vba/excel/XAxis.hpp>
+#include <ooo/vba/excel/XChart.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+
+class ScVbaChart;
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XAxis > ScVbaAxis_BASE;
+class ScVbaAxis : public ScVbaAxis_BASE
+{
+ rtl::Reference< ScVbaChart > moChartParent;
+ css::uno::Reference< css::beans::XPropertySet > mxPropertySet;
+ sal_Int32 mnType;
+ sal_Int32 mnGroup;
+ ov::ShapeHelper maShapeHelper;
+
+ bool bCrossesAreCustomized;
+ /// @throws css::uno::RuntimeException
+ ScVbaChart* getChartPtr();
+ /// @throws css::script::BasicErrorException
+ bool isValueAxis();
+
+public:
+ ScVbaAxis( const rtl::Reference< ScVbaChart >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, css::uno::Reference< css::beans::XPropertySet > _xPropertySet, sal_Int32 _nType, sal_Int32 _nGroup );
+ // Methods
+ virtual void SAL_CALL Delete( ) override;
+ virtual css::uno::Reference< ::ooo::vba::excel::XAxisTitle > SAL_CALL getAxisTitle( ) override;
+ virtual void SAL_CALL setDisplayUnit( ::sal_Int32 DisplayUnit ) override;
+ virtual ::sal_Int32 SAL_CALL getDisplayUnit( ) override;
+ virtual void SAL_CALL setCrosses( ::sal_Int32 Crosses ) override;
+ virtual ::sal_Int32 SAL_CALL getCrosses( ) override;
+ virtual void SAL_CALL setCrossesAt( double CrossesAt ) override;
+ virtual double SAL_CALL getCrossesAt( ) override;
+ virtual void SAL_CALL setType( ::sal_Int32 Type ) override;
+ virtual ::sal_Int32 SAL_CALL getType( ) override;
+ virtual void SAL_CALL setHasTitle( sal_Bool HasTitle ) override;
+ virtual sal_Bool SAL_CALL getHasTitle( ) override;
+ virtual void SAL_CALL setMinorUnit( double MinorUnit ) override;
+ virtual double SAL_CALL getMinorUnit( ) override;
+ virtual void SAL_CALL setMinorUnitIsAuto( sal_Bool MinorUnitIsAuto ) override;
+ virtual sal_Bool SAL_CALL getMinorUnitIsAuto( ) override;
+ virtual void SAL_CALL setReversePlotOrder( sal_Bool ReversePlotOrder ) override;
+ virtual sal_Bool SAL_CALL getReversePlotOrder( ) override;
+ virtual void SAL_CALL setMajorUnit( double MajorUnit ) override;
+ virtual double SAL_CALL getMajorUnit( ) override;
+ virtual void SAL_CALL setMajorUnitIsAuto( sal_Bool MajorUnitIsAuto ) override;
+ virtual sal_Bool SAL_CALL getMajorUnitIsAuto( ) override;
+ virtual void SAL_CALL setMaximumScale( double MaximumScale ) override;
+ virtual double SAL_CALL getMaximumScale( ) override;
+ virtual void SAL_CALL setMaximumScaleIsAuto( sal_Bool MaximumScaleIsAuto ) override;
+ virtual sal_Bool SAL_CALL getMaximumScaleIsAuto( ) override;
+ virtual void SAL_CALL setMinimumScale( double MinimumScale ) override;
+ virtual double SAL_CALL getMinimumScale( ) override;
+ virtual void SAL_CALL setMinimumScaleIsAuto( sal_Bool MinimumScaleIsAuto ) override;
+ virtual sal_Bool SAL_CALL getMinimumScaleIsAuto( ) override;
+ virtual ::sal_Int32 SAL_CALL getAxisGroup( ) override;
+ virtual void SAL_CALL setScaleType( ::sal_Int32 ScaleType ) override;
+ virtual ::sal_Int32 SAL_CALL getScaleType( ) override;
+ virtual double SAL_CALL getHeight( ) override;
+ virtual void SAL_CALL setHeight( double height ) override;
+ virtual double SAL_CALL getWidth( ) override;
+ virtual void SAL_CALL setWidth( double width ) override;
+ virtual double SAL_CALL getTop( ) override;
+ virtual void SAL_CALL setTop( double top ) override;
+ virtual double SAL_CALL getLeft( ) override;
+ virtual void SAL_CALL setLeft( double left ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaaxistitle.cxx b/sc/source/ui/vba/vbaaxistitle.cxx
new file mode 100644
index 0000000000..5336ef7a76
--- /dev/null
+++ b/sc/source/ui/vba/vbaaxistitle.cxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbaaxistitle.hxx"
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+ScVbaAxisTitle::ScVbaAxisTitle( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< drawing::XShape >& _xTitleShape ) : AxisTitleBase( xParent, xContext, _xTitleShape )
+{
+}
+
+OUString
+ScVbaAxisTitle::getServiceImplName()
+{
+ return "ScVbaAxisTitle";
+}
+
+uno::Sequence< OUString >
+ScVbaAxisTitle::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames = comphelper::concatSequences(
+ AxisTitleBase::getServiceNames(),
+ uno::Sequence<OUString> { "ooo.vba.excel.AxisTitle" } );
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaaxistitle.hxx b/sc/source/ui/vba/vbaaxistitle.hxx
new file mode 100644
index 0000000000..9226a58a3b
--- /dev/null
+++ b/sc/source/ui/vba/vbaaxistitle.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "vbatitle.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XAxisTitle.hpp>
+
+typedef TitleImpl< cppu::WeakImplHelper< ov::excel::XAxisTitle > > AxisTitleBase;
+
+class ScVbaAxisTitle : public AxisTitleBase
+{
+public:
+ ScVbaAxisTitle( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::drawing::XShape >& _xTitleShape );
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaborders.cxx b/sc/source/ui/vba/vbaborders.cxx
new file mode 100644
index 0000000000..157994adbc
--- /dev/null
+++ b/sc/source/ui/vba/vbaborders.cxx
@@ -0,0 +1,592 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbaborders.hxx"
+
+#include <sal/macros.h>
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XlBordersIndex.hpp>
+#include <ooo/vba/excel/XlBorderWeight.hpp>
+#include <ooo/vba/excel/XlLineStyle.hpp>
+#include <ooo/vba/excel/XlColorIndex.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <utility>
+
+#include "vbapalette.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+using namespace ::ooo::vba::excel;
+
+typedef ::cppu::WeakImplHelper<container::XIndexAccess > RangeBorders_Base;
+typedef InheritedHelperInterfaceWeakImpl<excel::XBorder > ScVbaBorder_Base;
+
+// #TODO sort these indexes to match the order in which Excel iterates over the
+// borders, the enumeration will match the order in this list
+const sal_Int16 supportedIndexTable[] = { XlBordersIndex::xlEdgeLeft, XlBordersIndex::xlEdgeTop, XlBordersIndex::xlEdgeBottom, XlBordersIndex::xlEdgeRight, XlBordersIndex::xlDiagonalDown, XlBordersIndex::xlDiagonalUp, XlBordersIndex::xlInsideVertical, XlBordersIndex::xlInsideHorizontal };
+
+constexpr OUString sTableBorder = u"TableBorder"_ustr;
+
+// Equiv widths in 1/100 mm
+const sal_Int32 OOLineThin = 26;
+const sal_Int32 OOLineMedium = 88;
+const sal_Int32 OOLineThick = 141;
+const sal_Int32 OOLineHairline = 2;
+
+namespace {
+
+class ScVbaBorder : public ScVbaBorder_Base
+{
+private:
+ uno::Reference< beans::XPropertySet > m_xProps;
+ sal_Int32 m_LineType;
+ ScVbaPalette m_Palette;
+ void setBorderLine( const table::BorderLine& rBorderLine )
+ {
+ table::TableBorder aTableBorder;
+ m_xProps->getPropertyValue( sTableBorder ) >>= aTableBorder;
+
+ switch ( m_LineType )
+ {
+ case XlBordersIndex::xlEdgeLeft:
+ aTableBorder.IsLeftLineValid = true;
+ aTableBorder.LeftLine= rBorderLine;
+ break;
+ case XlBordersIndex::xlEdgeTop:
+ aTableBorder.IsTopLineValid = true;
+ aTableBorder.TopLine = rBorderLine;
+ break;
+
+ case XlBordersIndex::xlEdgeBottom:
+ aTableBorder.IsBottomLineValid = true;
+ aTableBorder.BottomLine = rBorderLine;
+ break;
+ case XlBordersIndex::xlEdgeRight:
+ aTableBorder.IsRightLineValid = true;
+ aTableBorder.RightLine = rBorderLine;
+ break;
+ case XlBordersIndex::xlInsideVertical:
+ aTableBorder.IsVerticalLineValid = true;
+ aTableBorder.VerticalLine = rBorderLine;
+ break;
+ case XlBordersIndex::xlInsideHorizontal:
+ aTableBorder.IsHorizontalLineValid = true;
+ aTableBorder.HorizontalLine = rBorderLine;
+ break;
+ case XlBordersIndex::xlDiagonalDown:
+ case XlBordersIndex::xlDiagonalUp:
+ // #TODO have to ignore at the moment, would be
+ // nice to investigate what we can do here
+ break;
+ default:
+ return;
+ }
+ m_xProps->setPropertyValue( sTableBorder, uno::Any(aTableBorder) );
+ }
+
+ bool getBorderLine( table::BorderLine& rBorderLine )
+ {
+ table::TableBorder aTableBorder;
+ m_xProps->getPropertyValue( sTableBorder ) >>= aTableBorder;
+ switch ( m_LineType )
+ {
+ case XlBordersIndex::xlEdgeLeft:
+ if ( aTableBorder.IsLeftLineValid )
+ rBorderLine = aTableBorder.LeftLine;
+ break;
+ case XlBordersIndex::xlEdgeTop:
+ if ( aTableBorder.IsTopLineValid )
+ rBorderLine = aTableBorder.TopLine;
+ break;
+
+ case XlBordersIndex::xlEdgeBottom:
+ if ( aTableBorder.IsBottomLineValid )
+ rBorderLine = aTableBorder.BottomLine;
+ break;
+ case XlBordersIndex::xlEdgeRight:
+ if ( aTableBorder.IsRightLineValid )
+ rBorderLine = aTableBorder.RightLine;
+ break;
+ case XlBordersIndex::xlInsideVertical:
+ if ( aTableBorder.IsVerticalLineValid )
+ rBorderLine = aTableBorder.VerticalLine;
+ break;
+ case XlBordersIndex::xlInsideHorizontal:
+ if ( aTableBorder.IsHorizontalLineValid )
+ rBorderLine = aTableBorder.HorizontalLine;
+ break;
+
+ case XlBordersIndex::xlDiagonalDown:
+ case XlBordersIndex::xlDiagonalUp:
+ // #TODO have to ignore at the moment, would be
+ // nice to investigate what we can do here
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+protected:
+ virtual OUString getServiceImplName() override
+ {
+ return "ScVbaBorder";
+ }
+ virtual css::uno::Sequence<OUString> getServiceNames() override
+ {
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Border"
+ };
+ return aServiceNames;
+ }
+public:
+ ScVbaBorder( const uno::Reference< beans::XPropertySet > & xProps, const uno::Reference< uno::XComponentContext >& xContext, sal_Int32 lineType, const ScVbaPalette& rPalette) : ScVbaBorder_Base( uno::Reference< XHelperInterface >( xProps, uno::UNO_QUERY ), xContext ), m_xProps( xProps ), m_LineType( lineType ), m_Palette( rPalette ) {}
+
+ // XBorder
+ uno::Any SAL_CALL getColor() override
+ {
+ table::BorderLine aBorderLine;
+ if ( getBorderLine( aBorderLine ) )
+ return uno::Any( OORGBToXLRGB( Color(ColorTransparency, aBorderLine.Color) ) );
+ throw uno::RuntimeException("No Implementation available" );
+ }
+ void SAL_CALL setColor( const uno::Any& _color ) override
+ {
+ sal_Int32 nColor = 0;
+ _color >>= nColor;
+ table::BorderLine aBorderLine;
+ if ( !getBorderLine( aBorderLine ) )
+ throw uno::RuntimeException("No Implementation available" );
+
+ aBorderLine.Color = XLRGBToOORGB( nColor );
+ setBorderLine( aBorderLine );
+
+ }
+
+ uno::Any SAL_CALL getColorIndex() override
+ {
+ sal_Int32 nColor = 0;
+ XLRGBToOORGB( getColor() ) >>= nColor;
+ uno::Reference< container::XIndexAccess > xIndex = m_Palette.getPalette();
+ sal_Int32 nElems = xIndex->getCount();
+ sal_Int32 nIndex = -1;
+ for ( sal_Int32 count=0; count<nElems; ++count )
+ {
+ sal_Int32 nPaletteColor = 0;
+ xIndex->getByIndex( count ) >>= nPaletteColor;
+ if ( nPaletteColor == nColor )
+ {
+ nIndex = count + 1;
+ break;
+ }
+ }
+ return uno::Any(nIndex);
+ }
+
+ void SAL_CALL setColorIndex( const uno::Any& _colorindex ) override
+ {
+ sal_Int32 nColor = 0;
+ _colorindex >>= nColor;
+ if ( !nColor || nColor == XlColorIndex::xlColorIndexAutomatic )
+ nColor = 1;
+ setColor( OORGBToXLRGB( m_Palette.getPalette()->getByIndex( --nColor ) ) );
+ }
+ uno::Any SAL_CALL getWeight() override
+ {
+ table::BorderLine aBorderLine;
+ if ( getBorderLine( aBorderLine ) )
+ {
+ switch ( aBorderLine.OuterLineWidth )
+ {
+ case 0: // Thin = default OO thickness
+ case OOLineThin:
+ return uno::Any( XlBorderWeight::xlThin );
+ case OOLineMedium:
+ return uno::Any( XlBorderWeight::xlMedium );
+ case OOLineThick:
+ return uno::Any( XlBorderWeight::xlThick );
+ case OOLineHairline:
+ return uno::Any( XlBorderWeight::xlHairline );
+ default:
+ break;
+ }
+ }
+ throw uno::RuntimeException("Method failed" );
+ }
+ void SAL_CALL setWeight( const uno::Any& _weight ) override
+ {
+ sal_Int32 nWeight = 0;
+ _weight >>= nWeight;
+ table::BorderLine aBorderLine;
+ if ( !getBorderLine( aBorderLine ) )
+ throw uno::RuntimeException("Method failed" );
+
+ switch ( nWeight )
+ {
+ case XlBorderWeight::xlThin:
+ aBorderLine.OuterLineWidth = OOLineThin;
+ break;
+ case XlBorderWeight::xlMedium:
+ aBorderLine.OuterLineWidth = OOLineMedium;
+ break;
+ case XlBorderWeight::xlThick:
+ aBorderLine.OuterLineWidth = OOLineThick;
+ break;
+ case XlBorderWeight::xlHairline:
+ aBorderLine.OuterLineWidth = OOLineHairline;
+ break;
+ default:
+ throw uno::RuntimeException("Bad param" );
+ }
+ setBorderLine( aBorderLine );
+
+ }
+
+ void SAL_CALL setTintAndShade( const uno::Any& /*rAny*/ ) override
+ {
+ // TODO implement
+ }
+ uno::Any SAL_CALL getTintAndShade() override
+ {
+ // TODO implement
+ return uno::Any(static_cast<double>(0));
+ }
+
+ uno::Any SAL_CALL getLineStyle() override
+ {
+ // always return xlContinuous;
+ return uno::Any( XlLineStyle::xlContinuous );
+ }
+ void SAL_CALL setLineStyle( const uno::Any& _linestyle ) override
+ {
+ // Urk no choice but to silently ignore we don't support this attribute
+ // #TODO would be nice to support the excel line styles
+ sal_Int32 nLineStyle = 0;
+ _linestyle >>= nLineStyle;
+ table::BorderLine aBorderLine;
+ if ( !getBorderLine( aBorderLine ) )
+ throw uno::RuntimeException("Method failed" );
+
+ switch ( nLineStyle )
+ {
+ case XlLineStyle::xlContinuous:
+ case XlLineStyle::xlDash:
+ case XlLineStyle::xlDashDot:
+ case XlLineStyle::xlDashDotDot:
+ case XlLineStyle::xlDot:
+ case XlLineStyle::xlDouble:
+ case XlLineStyle::xlLineStyleNone:
+ case XlLineStyle::xlSlantDashDot:
+ break;
+ default:
+ throw uno::RuntimeException("Bad param" );
+ }
+ setBorderLine( aBorderLine );
+
+ }
+};
+
+class RangeBorders : public RangeBorders_Base
+{
+private:
+ uno::Reference< table::XCellRange > m_xRange;
+ uno::Reference< uno::XComponentContext > m_xContext;
+ ScVbaPalette m_Palette;
+ sal_Int32 getTableIndex( sal_Int32 nConst )
+ {
+ // okay return position of the index in the table
+ sal_Int32 nIndexes = getCount();
+ sal_Int32 realIndex = 0;
+ const sal_Int16* pTableEntry = supportedIndexTable;
+ for ( ; realIndex < nIndexes; ++realIndex, ++pTableEntry )
+ {
+ if ( *pTableEntry == nConst )
+ return realIndex;
+ }
+ return getCount(); // error condition
+ }
+public:
+ RangeBorders( uno::Reference< table::XCellRange > xRange, uno::Reference< uno::XComponentContext > xContext, const ScVbaPalette& rPalette ) : m_xRange(std::move( xRange )), m_xContext(std::move( xContext )), m_Palette( rPalette )
+ {
+ }
+ // XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount( ) override
+ {
+ return SAL_N_ELEMENTS( supportedIndexTable );
+ }
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+
+ sal_Int32 nIndex = getTableIndex( Index );
+ if ( nIndex >= 0 && nIndex < getCount() )
+ {
+ uno::Reference< beans::XPropertySet > xProps( m_xRange, uno::UNO_QUERY_THROW );
+ return uno::Any( uno::Reference< excel::XBorder >( new ScVbaBorder( xProps, m_xContext, supportedIndexTable[ nIndex ], m_Palette )) );
+ }
+ throw lang::IndexOutOfBoundsException();
+ }
+ virtual uno::Type SAL_CALL getElementType( ) override
+ {
+ return cppu::UnoType<excel::XBorder>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ return true;
+ }
+};
+
+}
+
+static uno::Reference< container::XIndexAccess >
+rangeToBorderIndexAccess( const uno::Reference< table::XCellRange >& xRange, const uno::Reference< uno::XComponentContext > & xContext, const ScVbaPalette& rPalette )
+{
+ return new RangeBorders( xRange, xContext, rPalette );
+}
+
+namespace {
+
+class RangeBorderEnumWrapper : public EnumerationHelper_BASE
+{
+ uno::Reference<container::XIndexAccess > m_xIndexAccess;
+ sal_Int32 m_nIndex;
+public:
+ explicit RangeBorderEnumWrapper( uno::Reference< container::XIndexAccess > xIndexAccess ) : m_xIndexAccess(std::move( xIndexAccess )), m_nIndex( 0 ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( m_nIndex < m_xIndexAccess->getCount() );
+ }
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( m_nIndex < m_xIndexAccess->getCount() )
+ return m_xIndexAccess->getByIndex( m_nIndex++ );
+ throw container::NoSuchElementException();
+ }
+};
+
+}
+
+ScVbaBorders::ScVbaBorders( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< table::XCellRange >& xRange,
+ const ScVbaPalette& rPalette )
+ : ScVbaBorders_BASE( xParent, xContext, rangeToBorderIndexAccess( xRange ,xContext, rPalette ) ), bRangeIsSingleCell( false )
+{
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW );
+ if ( xColumnRowRange->getRows()->getCount() == 1 && xColumnRowRange->getColumns()->getCount() == 1 )
+ bRangeIsSingleCell = true;
+ m_xProps.set( xRange, uno::UNO_QUERY_THROW );
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaBorders::createEnumeration()
+{
+ return new RangeBorderEnumWrapper( m_xIndexAccess );
+}
+
+uno::Any
+ScVbaBorders::createCollectionObject( const css::uno::Any& aSource )
+{
+ return aSource; // it's already a Border object
+}
+
+uno::Type
+ScVbaBorders::getElementType()
+{
+ return cppu::UnoType<excel::XBorders>::get();
+}
+
+uno::Any
+ScVbaBorders::getItemByIntIndex( const sal_Int32 nIndex )
+{
+ return createCollectionObject( m_xIndexAccess->getByIndex( nIndex ) );
+}
+
+uno::Any SAL_CALL ScVbaBorders::getColor()
+{
+ sal_Int32 count = getCount();
+ uno::Any color;
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ if( XlBordersIndex::xlDiagonalDown != supportedIndexTable[i] && XlBordersIndex::xlDiagonalUp != supportedIndexTable[i] )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ if( color.hasValue() )
+ {
+ if( color != xBorder->getColor() )
+ return uno::Any( uno::Reference< uno::XInterface >() );
+ }
+ else
+ color = xBorder->getColor();
+ }
+ }
+ return color;
+}
+void SAL_CALL ScVbaBorders::setColor( const uno::Any& _color )
+{
+ sal_Int32 count = getCount();
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ xBorder->setColor( _color );
+ }
+}
+uno::Any SAL_CALL ScVbaBorders::getColorIndex()
+{
+ sal_Int32 count = getCount();
+ uno::Any nColorIndex;
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ if( XlBordersIndex::xlDiagonalDown != supportedIndexTable[i] && XlBordersIndex::xlDiagonalUp != supportedIndexTable[i] )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ if( nColorIndex.hasValue() )
+ {
+ if( nColorIndex != xBorder->getColorIndex() )
+ return uno::Any( uno::Reference< uno::XInterface >() );
+ }
+ else
+ nColorIndex = xBorder->getColorIndex();
+ }
+ }
+ return nColorIndex;
+}
+void SAL_CALL ScVbaBorders::setColorIndex( const uno::Any& _colorindex )
+{
+ sal_Int32 count = getCount();
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ xBorder->setColorIndex( _colorindex );
+ }
+}
+
+static bool
+lcl_areAllLineWidthsSame( const table::TableBorder& maTableBorder, bool bIsCell )
+{
+
+ bool bRes = false;
+ if (bIsCell)
+ {
+ bRes = ((maTableBorder.TopLine.OuterLineWidth == maTableBorder.BottomLine.OuterLineWidth) &&
+(maTableBorder.TopLine.OuterLineWidth == maTableBorder.LeftLine.OuterLineWidth) &&
+(maTableBorder.TopLine.OuterLineWidth == maTableBorder.RightLine.OuterLineWidth));
+ }
+ else
+ {
+ bRes = ((maTableBorder.TopLine.OuterLineWidth == maTableBorder.BottomLine.OuterLineWidth) &&
+(maTableBorder.TopLine.OuterLineWidth == maTableBorder.LeftLine.OuterLineWidth) &&
+(maTableBorder.TopLine.OuterLineWidth == maTableBorder.HorizontalLine.OuterLineWidth) &&
+(maTableBorder.TopLine.OuterLineWidth == maTableBorder.VerticalLine.OuterLineWidth) &&
+(maTableBorder.TopLine.OuterLineWidth == maTableBorder.RightLine.OuterLineWidth));
+ }
+ return bRes;
+}
+
+uno::Any SAL_CALL ScVbaBorders::getLineStyle()
+{
+ table::TableBorder aTableBorder;
+ m_xProps->getPropertyValue( sTableBorder ) >>= aTableBorder;
+
+ sal_Int32 aLinestyle = XlLineStyle::xlLineStyleNone;
+
+ if ( lcl_areAllLineWidthsSame( aTableBorder, bRangeIsSingleCell ))
+ {
+ if (aTableBorder.TopLine.LineDistance != 0)
+ {
+ aLinestyle = XlLineStyle::xlDouble;
+ }
+ else if ( aTableBorder.TopLine.OuterLineWidth != 0 )
+ {
+ aLinestyle = XlLineStyle::xlContinuous;
+ }
+ }
+ return uno::Any( aLinestyle );
+}
+void SAL_CALL ScVbaBorders::setLineStyle( const uno::Any& _linestyle )
+{
+ sal_Int32 count = getCount();
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ xBorder->setLineStyle( _linestyle );
+ }
+}
+uno::Any SAL_CALL ScVbaBorders::getWeight()
+{
+ sal_Int32 count = getCount();
+ uno::Any weight;
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ if( XlBordersIndex::xlDiagonalDown != supportedIndexTable[i] && XlBordersIndex::xlDiagonalUp != supportedIndexTable[i] )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ if( weight.hasValue() )
+ {
+ if( weight != xBorder->getWeight() )
+ return uno::Any( uno::Reference< uno::XInterface >() );
+ }
+ else
+ weight = xBorder->getWeight();
+ }
+ }
+ return weight;
+}
+
+uno::Any SAL_CALL ScVbaBorders::getTintAndShade()
+{
+ // TODO implement
+ return uno::Any(static_cast<double>(0));
+}
+
+void SAL_CALL ScVbaBorders::setTintAndShade(const uno::Any& /*rAny*/)
+{
+ // TODO implement
+}
+
+void SAL_CALL ScVbaBorders::setWeight( const uno::Any& _weight )
+{
+ sal_Int32 count = getCount();
+ for( sal_Int32 i = 0; i < count ; i++ )
+ {
+ uno::Reference< XBorder > xBorder( getItemByIntIndex( supportedIndexTable[i] ), uno::UNO_QUERY_THROW );
+ xBorder->setWeight( _weight );
+ }
+}
+
+OUString
+ScVbaBorders::getServiceImplName()
+{
+ return "ScVbaBorders";
+}
+
+uno::Sequence< OUString >
+ScVbaBorders::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Borders"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaborders.hxx b/sc/source/ui/vba/vbaborders.hxx
new file mode 100644
index 0000000000..6d3621101c
--- /dev/null
+++ b/sc/source/ui/vba/vbaborders.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XBorders.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::uno { class XComponentContext; }
+namespace com::sun::star::table { class XCellRange; }
+
+typedef CollTestImplHelper< ov::excel::XBorders > ScVbaBorders_BASE;
+class ScVbaPalette;
+class ScVbaBorders : public ScVbaBorders_BASE
+{
+ // XEnumerationAccess
+ virtual css::uno::Any getItemByIntIndex( const sal_Int32 nIndex ) override;
+ bool bRangeIsSingleCell;
+ css::uno::Reference< css::beans::XPropertySet > m_xProps;
+public:
+ ScVbaBorders( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const css::uno::Reference< css::table::XCellRange >& xRange,
+ const ScVbaPalette& rPalette );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XBorders
+
+ // ScVbaCollectionBaseImpl
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ virtual css::uno::Any SAL_CALL getColor() override;
+ virtual void SAL_CALL setColor( const css::uno::Any& _color ) override;
+ virtual css::uno::Any SAL_CALL getColorIndex() override;
+ virtual void SAL_CALL setColorIndex( const css::uno::Any& _colorindex ) override;
+ virtual css::uno::Any SAL_CALL getLineStyle() override;
+ virtual void SAL_CALL setLineStyle( const css::uno::Any& _linestyle ) override;
+ virtual css::uno::Any SAL_CALL getWeight() override;
+ virtual void SAL_CALL setWeight( const css::uno::Any& ) override;
+ virtual css::uno::Any SAL_CALL getTintAndShade() override;
+ virtual void SAL_CALL setTintAndShade( const css::uno::Any& ) override;
+ // xxxxBASE
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacharacters.cxx b/sc/source/ui/vba/vbacharacters.cxx
new file mode 100644
index 0000000000..087bfbd1aa
--- /dev/null
+++ b/sc/source/ui/vba/vbacharacters.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 "vbacharacters.hxx"
+
+#include "vbafont.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <utility>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaCharacters::ScVbaCharacters( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const ScVbaPalette& dPalette,
+ uno::Reference< text::XSimpleText> xRange,
+ const css::uno::Any& Start,
+ const css::uno::Any& Length,
+ bool Replace )
+ : ScVbaCharacters_BASE( xParent, xContext ),
+ m_xSimpleText(std::move(xRange)), m_aPalette( dPalette), bReplace( Replace )
+{
+ sal_Int16 nLength(-1);
+ sal_Int16 nStart(1);
+ Start >>= nStart;
+ if ( nStart < 1 )
+ nStart = 1; // silently correct user error ( as ms )
+ nStart--; // OOo is 0 based
+ Length >>=nLength;
+ uno::Reference< text::XTextCursor > xTextCursor( m_xSimpleText->createTextCursor(), uno::UNO_SET_THROW );
+ xTextCursor->collapseToStart();
+ if ( nStart )
+ {
+ if ( ( nStart + 1 ) > m_xSimpleText->getString().getLength() )
+ //nStart = m_xSimpleText->getString().getLength();
+ xTextCursor->gotoEnd( false );
+ xTextCursor->goRight( nStart, false );
+ }
+ if ( nLength < 0 ) // expand to end
+ xTextCursor->gotoEnd( true );
+ else
+ xTextCursor->goRight( nLength, true );
+ m_xTextRange.set( xTextCursor, uno::UNO_QUERY_THROW );
+
+}
+
+OUString SAL_CALL
+ScVbaCharacters::getCaption()
+{
+ return m_xTextRange->getString();
+}
+void SAL_CALL
+ScVbaCharacters::setCaption( const OUString& _caption )
+{
+ m_xTextRange->setString( _caption );
+
+}
+
+::sal_Int32 SAL_CALL
+ScVbaCharacters::getCount()
+{
+ return getCaption().getLength();
+}
+
+OUString SAL_CALL
+ScVbaCharacters::getText()
+{
+ return getCaption();
+}
+void SAL_CALL
+ScVbaCharacters::setText( const OUString& _text )
+{
+ setCaption( _text );
+}
+uno::Reference< excel::XFont > SAL_CALL
+ScVbaCharacters::getFont()
+{
+ uno::Reference< beans::XPropertySet > xProps( m_xTextRange, uno::UNO_QUERY_THROW );
+ return uno::Reference< excel::XFont >( new ScVbaFont( this, mxContext, m_aPalette, xProps ) );
+}
+void SAL_CALL
+ScVbaCharacters::setFont( const uno::Reference< excel::XFont >& /*_font*/ )
+{
+ // #TODO #FIXME needs implementation, or can't be done?
+ throw uno::RuntimeException("Not Implemented" );
+}
+
+// Methods
+void SAL_CALL
+ScVbaCharacters::Insert( const OUString& rString )
+{
+ m_xSimpleText->insertString( m_xTextRange, rString, bReplace );
+}
+
+void SAL_CALL
+ScVbaCharacters::Delete( )
+{
+ // #FIXME #TODO is this a bit suspect? I wonder should the contents
+ // of the cell be deleted from the parent ( range )
+ m_xSimpleText->setString(OUString());
+}
+
+OUString
+ScVbaCharacters::getServiceImplName()
+{
+ return "ScVbaCharacters";
+}
+
+uno::Sequence< OUString >
+ScVbaCharacters::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Characters"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacharacters.hxx b/sc/source/ui/vba/vbacharacters.hxx
new file mode 100644
index 0000000000..3cb4043257
--- /dev/null
+++ b/sc/source/ui/vba/vbacharacters.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XCharacters.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/text/XSimpleText.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+#include "vbapalette.hxx"
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XCharacters > ScVbaCharacters_BASE;
+
+class ScVbaCharacters : public ScVbaCharacters_BASE
+{
+private:
+ css::uno::Reference< css::text::XTextRange > m_xTextRange;
+ css::uno::Reference< css::text::XSimpleText > m_xSimpleText;
+ ScVbaPalette m_aPalette;
+ // Add because of MSO has different behavior.
+ bool bReplace;
+public:
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ ScVbaCharacters( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const ScVbaPalette& dPalette, css::uno::Reference< css::text::XSimpleText > xRange, const css::uno::Any& Start, const css::uno::Any& Length, bool bReplace = false );
+
+ // Attributes
+ virtual OUString SAL_CALL getCaption() override;
+ virtual void SAL_CALL setCaption( const OUString& _caption ) override;
+ virtual ::sal_Int32 SAL_CALL getCount() override;
+ virtual OUString SAL_CALL getText() override;
+ virtual void SAL_CALL setText( const OUString& _text ) override;
+ virtual css::uno::Reference< ov::excel::XFont > SAL_CALL getFont() override;
+ virtual void SAL_CALL setFont( const css::uno::Reference< ov::excel::XFont >& _font ) override;
+
+ // Methods
+ virtual void SAL_CALL Insert( const OUString& String ) override;
+ virtual void SAL_CALL Delete( ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbachart.cxx b/sc/source/ui/vba/vbachart.cxx
new file mode 100644
index 0000000000..d4cf3a0e1e
--- /dev/null
+++ b/sc/source/ui/vba/vbachart.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 "vbachart.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/chart/XAxisXSupplier.hpp>
+#include <com/sun/star/chart/XAxisYSupplier.hpp>
+#include <com/sun/star/chart/XAxisZSupplier.hpp>
+#include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
+#include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
+#include <com/sun/star/chart/XChartDataArray.hpp>
+#include <com/sun/star/chart/ChartSymbolType.hpp>
+#include <com/sun/star/chart/ChartSolidType.hpp>
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <ooo/vba/excel/XlChartType.hpp>
+#include <ooo/vba/excel/XlRowCol.hpp>
+#include <ooo/vba/excel/XlAxisType.hpp>
+#include <ooo/vba/excel/XlAxisGroup.hpp>
+
+#include <basic/sberrors.hxx>
+#include "vbachartobject.hxx"
+#include "vbarange.hxx"
+#include "vbacharttitle.hxx"
+#include "vbaaxes.hxx"
+#include <document.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+using namespace ::ooo::vba::excel::XlChartType;
+using namespace ::ooo::vba::excel::XlRowCol;
+using namespace ::ooo::vba::excel::XlAxisType;
+using namespace ::ooo::vba::excel::XlAxisGroup;
+
+constexpr OUStringLiteral CHART_NAME(u"Name");
+// #TODO move this constant to vbaseries.[ch]xx ( when it exists )
+constexpr OUStringLiteral DEFAULTSERIESPREFIX(u"Series");
+constexpr OUString DATAROWSOURCE(u"DataRowSource"_ustr);
+constexpr OUString UPDOWN(u"UpDown"_ustr);
+constexpr OUString VOLUME(u"Volume"_ustr);
+constexpr OUString LINES(u"Lines"_ustr);
+constexpr OUString SPLINETYPE(u"SplineType"_ustr);
+constexpr OUString SYMBOLTYPE(u"SymbolType"_ustr);
+constexpr OUString DEEP(u"Deep"_ustr);
+constexpr OUString SOLIDTYPE(u"SolidType"_ustr);
+constexpr OUString VERTICAL(u"Vertical"_ustr);
+constexpr OUString PERCENT(u"Percent"_ustr);
+constexpr OUString STACKED(u"Stacked"_ustr);
+constexpr OUString DIM3D(u"Dim3D"_ustr);
+constexpr OUString HASMAINTITLE(u"HasMainTitle"_ustr);
+constexpr OUString HASLEGEND(u"HasLegend"_ustr);
+
+ScVbaChart::ScVbaChart( const css::uno::Reference< ov::XHelperInterface >& _xParent, const css::uno::Reference< css::uno::XComponentContext >& _xContext, const css::uno::Reference< css::lang::XComponent >& _xChartComponent, css::uno::Reference< css::table::XTableChart > _xTableChart ) : ChartImpl_BASE( _xParent, _xContext ), mxTableChart(std::move( _xTableChart ))
+{
+ mxChartDocument.set( _xChartComponent, uno::UNO_QUERY_THROW ) ;
+ // #TODO is it possible that the XPropertySet interface is not set
+ // code in setPlotBy seems to indicate that this is possible? but
+ // additionally there is no check in most of the places where it is used
+ // ( and therefore could possibly be NULL )
+ // I'm going to let it throw for the moment ( npower )
+ mxDiagramPropertySet.set( mxChartDocument->getDiagram(), uno::UNO_QUERY_THROW );
+ mxChartPropertySet.set( _xChartComponent, uno::UNO_QUERY_THROW ) ;
+}
+
+OUString SAL_CALL
+ScVbaChart::getName()
+{
+ OUString sName;
+ uno::Reference< beans::XPropertySet > xProps( mxChartDocument, uno::UNO_QUERY_THROW );
+ try
+ {
+ xProps->getPropertyValue( CHART_NAME ) >>= sName;
+ }
+ catch( const uno::Exception & ) // swallow exceptions
+ {
+ }
+ return sName;
+}
+
+uno::Any SAL_CALL
+ScVbaChart::SeriesCollection(const uno::Any&)
+{
+ return uno::Any();
+}
+
+::sal_Int32 SAL_CALL
+ScVbaChart::getChartType()
+{
+ sal_Int32 nChartType = -1;
+ try
+ {
+ OUString sDiagramType = mxChartDocument->getDiagram()->getDiagramType();
+ if ( sDiagramType == "com.sun.star.chart.AreaDiagram" )
+ {
+ if (is3D())
+ {
+ nChartType = getStackedType(xl3DAreaStacked, xl3DAreaStacked100, xl3DArea);
+ }
+ else
+ {
+ nChartType = getStackedType(xlAreaStacked, xlAreaStacked100, xlArea);
+ }
+ }
+ else if ( sDiagramType == "com.sun.star.chart.PieDiagram" )
+ {
+ if (is3D())
+ nChartType = xl3DPie;
+ else
+ nChartType = xlPie; /*TODO XlChartType xlPieExploded, XlChartType xlPieOfPie */
+ }
+ else if ( sDiagramType == "com.sun.star.chart.BarDiagram" )
+ {
+ sal_Int32 nSolidType = chart::ChartSolidType::RECTANGULAR_SOLID;
+ if (mxDiagramPropertySet->getPropertySetInfo()->hasPropertyByName(SOLIDTYPE))
+ { //in 2D diagrams 'SolidType' may not be set
+ if (is3D())
+ mxDiagramPropertySet->getPropertyValue(SOLIDTYPE) >>= nSolidType;
+ }
+ switch (nSolidType)
+ {
+ case chart::ChartSolidType::CONE:
+ nChartType = getSolidType(xlConeCol, xlConeColStacked, xlConeColStacked100, xlConeColClustered, xlConeBarStacked, xlConeBarStacked100, xlConeBarClustered);
+ break;
+ case chart::ChartSolidType::CYLINDER:
+ nChartType = getSolidType(xlCylinderCol, xlCylinderColStacked, xlCylinderColStacked100, xlCylinderColClustered, xlCylinderBarStacked, xlCylinderBarStacked100, xlCylinderBarClustered);
+ break;
+ case chart::ChartSolidType::PYRAMID:
+ nChartType = getSolidType(xlPyramidCol, xlPyramidColStacked, xlPyramidColStacked100, xlPyramidColClustered, xlPyramidBarStacked, xlPyramidBarStacked100, xlPyramidBarClustered);
+ break;
+ default: // RECTANGULAR_SOLID
+ if (is3D())
+ {
+ nChartType = getSolidType(xl3DColumn, xl3DColumnStacked, xl3DColumnStacked100, xl3DColumnClustered, xl3DBarStacked, xl3DBarStacked100, xl3DBarClustered);
+ }
+ else
+ {
+ nChartType = getSolidType(xlColumnClustered, xlColumnStacked, xlColumnStacked100, xlColumnClustered, xlBarStacked, xlBarStacked100, xlBarClustered);
+ }
+ break;
+ }
+ }
+ else if ( sDiagramType == "com.sun.star.chart.StockDiagram" )
+ {
+ bool bVolume = false;
+ mxDiagramPropertySet->getPropertyValue(VOLUME) >>= bVolume;
+ if (bVolume)
+ {
+ nChartType = getStockUpDownValue(xlStockVOHLC, xlStockVHLC);
+ }
+ else
+ {
+ nChartType = getStockUpDownValue(xlStockOHLC, xlStockHLC);
+ }
+ }
+ else if ( sDiagramType == "com.sun.star.chart.XYDiagram" )
+ {
+ bool bHasLines = false;
+ mxDiagramPropertySet->getPropertyValue(LINES) >>= bHasLines;
+ sal_Int32 nSplineType = 0;
+ mxDiagramPropertySet->getPropertyValue(SPLINETYPE) >>= nSplineType;
+ if (nSplineType == 1)
+ {
+ nChartType = getMarkerType(xlXYScatterSmooth, xlXYScatterSmoothNoMarkers);
+ }
+ else if (bHasLines)
+ {
+ nChartType = getMarkerType(xlXYScatterLines, xlXYScatterLinesNoMarkers);
+ }
+ else
+ {
+ nChartType = xlXYScatter;
+ }
+ }
+ else if ( sDiagramType == "com.sun.star.chart.LineDiagram" )
+ {
+ if (is3D())
+ {
+ nChartType = xl3DLine;
+ }
+ else if (hasMarkers())
+ {
+ nChartType = getStackedType(xlLineMarkersStacked, xlLineMarkersStacked100, xlLineMarkers);
+ }
+ else
+ {
+ nChartType = getStackedType(xlLineStacked, xlLineStacked100, xlLine);
+ }
+ }
+ else if ( sDiagramType == "com.sun.star.chart.DonutDiagram" )
+ {
+ nChartType = xlDoughnut; // TODO DoughnutExploded ??
+ }
+ else if ( sDiagramType == "com.sun.star.chart.NetDiagram" )
+ {
+ nChartType = getMarkerType(xlRadarMarkers, xlRadar);
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return nChartType;
+}
+
+void SAL_CALL
+ScVbaChart::setChartType( ::sal_Int32 _nChartType )
+{
+ try
+ {
+ switch (_nChartType)
+ {
+ case xlColumnClustered:
+ case xlColumnStacked:
+ case xlColumnStacked100:
+ case xl3DColumnClustered:
+ case xl3DColumnStacked:
+ case xl3DColumnStacked100:
+ case xl3DColumn:
+ case xlBarClustered:
+ case xlBarStacked:
+ case xlBarStacked100:
+ case xl3DBarClustered:
+ case xl3DBarStacked:
+ case xl3DBarStacked100:
+ case xlConeColClustered:
+ case xlConeColStacked:
+ case xlConeColStacked100:
+ case xlConeBarClustered:
+ case xlConeBarStacked:
+ case xlConeBarStacked100:
+ case xlConeCol:
+ case xlPyramidColClustered:
+ case xlPyramidColStacked:
+ case xlPyramidColStacked100:
+ case xlPyramidBarClustered:
+ case xlPyramidBarStacked:
+ case xlPyramidBarStacked100:
+ case xlPyramidCol:
+ case xlCylinderColClustered:
+ case xlCylinderColStacked:
+ case xlCylinderColStacked100:
+ case xlCylinderBarClustered:
+ case xlCylinderBarStacked:
+ case xlCylinderBarStacked100:
+ case xlCylinderCol:
+ case xlSurface: // not possible
+ case xlSurfaceWireframe:
+ case xlSurfaceTopView:
+ case xlSurfaceTopViewWireframe:
+ setDiagram( "com.sun.star.chart.BarDiagram");
+ break;
+ case xlLine:
+ case xl3DLine:
+ case xlLineStacked:
+ case xlLineStacked100:
+ case xlLineMarkers:
+ case xlLineMarkersStacked:
+ case xlLineMarkersStacked100:
+ setDiagram( "com.sun.star.chart.LineDiagram");
+ break;
+ case xl3DArea:
+ case xlArea:
+ case xlAreaStacked:
+ case xlAreaStacked100:
+ case xl3DAreaStacked:
+ case xl3DAreaStacked100:
+ setDiagram( "com.sun.star.chart.AreaDiagram" );
+ break;
+ case xlDoughnut:
+ case xlDoughnutExploded:
+ setDiagram( "com.sun.star.chart.DonutDiagram" );
+ break;
+ case xlStockHLC:
+ case xlStockOHLC:
+ case xlStockVHLC:
+ case xlStockVOHLC:
+ setDiagram( "com.sun.star.chart.StockDiagram");
+ mxDiagramPropertySet->setPropertyValue( UPDOWN, uno::Any((_nChartType == xlStockOHLC) || (_nChartType == xlStockVOHLC)));
+ mxDiagramPropertySet->setPropertyValue( VOLUME, uno::Any((_nChartType == xlStockVHLC) || (_nChartType == xlStockVOHLC)));
+ break;
+
+ case xlPieOfPie: // not possible
+ case xlPieExploded: // SegmentOffset on ChartDataPointProperties -> get from XDiagram //How does Excel do this?
+ case xl3DPieExploded:
+ case xl3DPie:
+ case xlPie:
+ case xlBarOfPie: // not possible (Zoom pie)
+ setDiagram( "com.sun.star.chart.PieDiagram");
+ break;
+
+ case xlRadar:
+ case xlRadarMarkers:
+ case xlRadarFilled:
+ setDiagram( "com.sun.star.chart.NetDiagram");
+ break;
+ case xlXYScatter:
+ case xlBubble: // not possible
+ case xlBubble3DEffect: // not possible
+ case xlXYScatterLines:
+ case xlXYScatterLinesNoMarkers:
+ case xlXYScatterSmooth:
+ case xlXYScatterSmoothNoMarkers:
+ setDiagram( "com.sun.star.chart.XYDiagram");
+ switch(_nChartType)
+ {
+ case xlXYScatter:
+ case xlBubble: // not possible
+ case xlBubble3DEffect: // not possible
+ mxDiagramPropertySet->setPropertyValue(LINES, uno::Any( false ));
+ break;
+ case xlXYScatterLines:
+ case xlXYScatterLinesNoMarkers:
+ mxDiagramPropertySet->setPropertyValue(LINES, uno::Any( true ));
+ break;
+ case xlXYScatterSmooth:
+ case xlXYScatterSmoothNoMarkers:
+ mxDiagramPropertySet->setPropertyValue(SPLINETYPE, uno::Any( sal_Int32(1)));
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_CONVERSION), OUString() );
+ }
+
+ switch (_nChartType)
+ {
+ case xlLineMarkers:
+ case xlLineMarkersStacked:
+ case xlLineMarkersStacked100:
+ case xlRadarMarkers:
+ case xlXYScatterLines:
+ case xlXYScatterSmooth:
+ case xlXYScatter:
+ case xlBubble: // not possible
+ case xlBubble3DEffect: // not possible
+ mxDiagramPropertySet->setPropertyValue(SYMBOLTYPE, uno::Any( chart::ChartSymbolType::AUTO));
+ break;
+ default:
+ if (mxDiagramPropertySet->getPropertySetInfo()->hasPropertyByName(SYMBOLTYPE))
+ {
+ mxDiagramPropertySet->setPropertyValue(SYMBOLTYPE, uno::Any(chart::ChartSymbolType::NONE));
+ }
+ break;
+ }
+
+ switch (_nChartType)
+ {
+ case xlConeCol:
+ case xlPyramidCol:
+ case xlCylinderCol:
+ case xl3DColumn:
+ case xlSurface: // not possible
+ case xlSurfaceWireframe:
+ case xlSurfaceTopView:
+ case xlSurfaceTopViewWireframe:
+ mxDiagramPropertySet->setPropertyValue(DEEP,uno::Any( true ));
+ break;
+ default:
+ if (mxDiagramPropertySet->getPropertySetInfo()->hasPropertyByName(DEEP))
+ {
+ mxDiagramPropertySet->setPropertyValue(DEEP, uno::Any( false));
+ }
+ break;
+ }
+
+ switch (_nChartType)
+ {
+ case xlConeColClustered:
+ case xlConeColStacked:
+ case xlConeColStacked100:
+ case xlConeBarClustered:
+ case xlConeBarStacked:
+ case xlConeBarStacked100:
+ case xlConeCol:
+ mxDiagramPropertySet->setPropertyValue(SOLIDTYPE, uno::Any(chart::ChartSolidType::CONE));
+ break;
+ case xlPyramidColClustered:
+ case xlPyramidColStacked:
+ case xlPyramidColStacked100:
+ case xlPyramidBarClustered:
+ case xlPyramidBarStacked:
+ case xlPyramidBarStacked100:
+ case xlPyramidCol:
+ mxDiagramPropertySet->setPropertyValue(SOLIDTYPE, uno::Any(chart::ChartSolidType::PYRAMID));
+ break;
+ case xlCylinderColClustered:
+ case xlCylinderColStacked:
+ case xlCylinderColStacked100:
+ case xlCylinderBarClustered:
+ case xlCylinderBarStacked:
+ case xlCylinderBarStacked100:
+ case xlCylinderCol:
+ mxDiagramPropertySet->setPropertyValue(SOLIDTYPE, uno::Any(chart::ChartSolidType::CYLINDER));
+ break;
+ default:
+ if (mxDiagramPropertySet->getPropertySetInfo()->hasPropertyByName(SOLIDTYPE))
+ {
+ mxDiagramPropertySet->setPropertyValue(SOLIDTYPE, uno::Any(chart::ChartSolidType::RECTANGULAR_SOLID));
+ }
+ break;
+ }
+
+ switch ( _nChartType)
+ {
+ case xlConeCol:
+ case xlConeColClustered:
+ case xlConeColStacked:
+ case xlConeColStacked100:
+ case xlPyramidColClustered:
+ case xlPyramidColStacked:
+ case xlPyramidColStacked100:
+ case xlCylinderColClustered:
+ case xlCylinderColStacked:
+ case xlCylinderColStacked100:
+ case xlColumnClustered:
+ case xlColumnStacked:
+ case xlColumnStacked100:
+ case xl3DColumnClustered:
+ case xl3DColumnStacked:
+ case xl3DColumnStacked100:
+ case xlSurface: // not possible
+ case xlSurfaceWireframe:
+ case xlSurfaceTopView:
+ case xlSurfaceTopViewWireframe:
+ mxDiagramPropertySet->setPropertyValue(VERTICAL, uno::Any( true));
+ break;
+ default:
+ if (mxDiagramPropertySet->getPropertySetInfo()->hasPropertyByName(VERTICAL))
+ {
+ mxDiagramPropertySet->setPropertyValue(VERTICAL, uno::Any(false));
+ }
+ break;
+ }
+
+ switch (_nChartType)
+ {
+ case xlColumnStacked:
+ case xl3DColumnStacked:
+ case xlBarStacked:
+ case xl3DBarStacked:
+ case xlLineStacked:
+ case xlLineMarkersStacked:
+ case xlAreaStacked:
+ case xl3DAreaStacked:
+ case xlCylinderColStacked:
+ case xlCylinderBarStacked:
+ case xlConeColStacked:
+ case xlConeBarStacked:
+ case xlPyramidColStacked:
+ case xlPyramidBarStacked:
+ mxDiagramPropertySet->setPropertyValue(PERCENT, uno::Any( false ));
+ mxDiagramPropertySet->setPropertyValue(STACKED, uno::Any( true ));
+ break;
+ case xlPyramidColStacked100:
+ case xlPyramidBarStacked100:
+ case xlConeColStacked100:
+ case xlConeBarStacked100:
+ case xlCylinderBarStacked100:
+ case xlCylinderColStacked100:
+ case xl3DAreaStacked100:
+ case xlLineMarkersStacked100:
+ case xlAreaStacked100:
+ case xlLineStacked100:
+ case xl3DBarStacked100:
+ case xlBarStacked100:
+ case xl3DColumnStacked100:
+ case xlColumnStacked100:
+ mxDiagramPropertySet->setPropertyValue(STACKED, uno::Any( true));
+ mxDiagramPropertySet->setPropertyValue(PERCENT, uno::Any( true ));
+ break;
+ default:
+ mxDiagramPropertySet->setPropertyValue(PERCENT, uno::Any( false));
+ mxDiagramPropertySet->setPropertyValue(STACKED, uno::Any( false));
+ break;
+ }
+ switch (_nChartType)
+ {
+ case xl3DArea:
+ case xl3DAreaStacked:
+ case xl3DAreaStacked100:
+ case xl3DBarClustered:
+ case xl3DBarStacked:
+ case xl3DBarStacked100:
+ case xl3DColumn:
+ case xl3DColumnClustered:
+ case xl3DColumnStacked:
+ case xl3DColumnStacked100:
+ case xl3DLine:
+ case xl3DPie:
+ case xl3DPieExploded:
+ case xlConeColClustered:
+ case xlConeColStacked:
+ case xlConeColStacked100:
+ case xlConeBarClustered:
+ case xlConeBarStacked:
+ case xlConeBarStacked100:
+ case xlConeCol:
+ case xlPyramidColClustered:
+ case xlPyramidColStacked:
+ case xlPyramidColStacked100:
+ case xlPyramidBarClustered:
+ case xlPyramidBarStacked:
+ case xlPyramidBarStacked100:
+ case xlPyramidCol:
+ case xlCylinderColClustered:
+ case xlCylinderColStacked:
+ case xlCylinderColStacked100:
+ case xlCylinderBarClustered:
+ case xlCylinderBarStacked:
+ case xlCylinderBarStacked100:
+ case xlCylinderCol:
+ mxDiagramPropertySet->setPropertyValue(DIM3D, uno::Any( true));
+ break;
+ default:
+ if (mxDiagramPropertySet->getPropertySetInfo()->hasPropertyByName(DIM3D))
+ {
+ mxDiagramPropertySet->setPropertyValue(DIM3D, uno::Any( false));
+ }
+ break;
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+void SAL_CALL
+ScVbaChart::Activate()
+{
+ // #TODO how are Chart sheets handled ( I know we don't even consider
+ // them in the worksheets/sheets collections ), but...???
+ // note: in vba for excel the parent of a Chart sheet is a workbook,
+ // e.g. 'ThisWorkbook'
+ uno::Reference< XHelperInterface > xParent( getParent() );
+ ScVbaChartObject* pChartObj = static_cast< ScVbaChartObject* >( xParent.get() );
+ if ( !pChartObj )
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), "no ChartObject as parent" );
+
+ pChartObj->Activate();
+
+}
+
+void SAL_CALL
+ScVbaChart::setSourceData( const css::uno::Reference< ::ooo::vba::excel::XRange >& _xCalcRange, const css::uno::Any& _aPlotBy )
+{
+ try
+ {
+ table::CellRangeAddress aSingleRangeAddress;
+
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( _xCalcRange->getCellRange(), uno::UNO_QUERY_THROW );
+ aSingleRangeAddress = xAddressable->getRangeAddress();
+
+ mxTableChart->setRanges({ aSingleRangeAddress } );
+
+ bool bsetRowHeaders = false;
+ bool bsetColumnHeaders = false;
+
+ ScVbaRange* pRange = static_cast< ScVbaRange* >( _xCalcRange.get() );
+ if ( pRange )
+ {
+ ScDocument& rDoc = pRange->getScDocument();
+ bsetRowHeaders = rDoc.HasRowHeader( static_cast< SCCOL >( aSingleRangeAddress.StartColumn ), static_cast< SCROW >( aSingleRangeAddress.StartRow ), static_cast< SCCOL >( aSingleRangeAddress.EndColumn ), static_cast< SCROW >( aSingleRangeAddress.EndRow ), static_cast< SCTAB >( aSingleRangeAddress.Sheet ) );
+ bsetColumnHeaders = rDoc.HasColHeader( static_cast< SCCOL >( aSingleRangeAddress.StartColumn ), static_cast< SCROW >( aSingleRangeAddress.StartRow ), static_cast< SCCOL >( aSingleRangeAddress.EndColumn ), static_cast< SCROW >( aSingleRangeAddress.EndRow ), static_cast< SCTAB >( aSingleRangeAddress.Sheet ));
+ }
+ mxTableChart->setHasRowHeaders(bsetRowHeaders);
+ mxTableChart->setHasColumnHeaders(bsetColumnHeaders);
+
+ if ((!bsetColumnHeaders) || (!bsetRowHeaders))
+ {
+ uno::Reference< chart::XChartDataArray > xChartDataArray( mxChartDocument->getData(), uno::UNO_QUERY_THROW );
+ if (!bsetColumnHeaders)
+ {
+ xChartDataArray->setColumnDescriptions( getDefaultSeriesDescriptions(xChartDataArray->getColumnDescriptions().getLength() ));
+ }
+ if (!bsetRowHeaders)
+ {
+ xChartDataArray->setRowDescriptions(getDefaultSeriesDescriptions(xChartDataArray->getRowDescriptions().getLength() ));
+ }
+ }
+
+ if ( _aPlotBy.hasValue() )
+ {
+ sal_Int32 nVal = 0;
+ _aPlotBy >>= nVal;
+ setPlotBy( nVal );
+ }
+ else
+ {
+ sal_Int32 nRows = aSingleRangeAddress.EndRow - aSingleRangeAddress.StartRow;
+ sal_Int32 nCols = aSingleRangeAddress.EndColumn - aSingleRangeAddress.StartColumn;
+ // AutoDetect emulation
+ if ( nRows > nCols )
+ setPlotBy( xlColumns );
+ else if ( nRows <= nCols )
+ setPlotBy( xlRows );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+uno::Sequence< OUString >
+ScVbaChart::getDefaultSeriesDescriptions( sal_Int32 _nCount )
+{
+ uno::Sequence< OUString > sDescriptions ( _nCount );
+ std::generate_n(sDescriptions.getArray(), _nCount,
+ [i = 1]() mutable -> OUString { return DEFAULTSERIESPREFIX + OUString::number(i++); });
+ return sDescriptions;
+}
+
+void
+ScVbaChart::setDefaultChartType()
+{
+ setChartType( xlColumnClustered );
+}
+
+void
+ScVbaChart::setPlotBy( ::sal_Int32 _nPlotBy )
+{
+ try
+ {
+ if ( !mxDiagramPropertySet.is() )
+ setDefaultChartType();
+ switch (_nPlotBy)
+ {
+ case xlRows:
+ mxDiagramPropertySet->setPropertyValue( DATAROWSOURCE, uno::Any( chart::ChartDataRowSource_ROWS ) );
+ break;
+ case xlColumns:
+ mxDiagramPropertySet->setPropertyValue( DATAROWSOURCE, uno::Any( chart::ChartDataRowSource_COLUMNS) );
+ break;
+ default:
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+::sal_Int32 SAL_CALL
+ScVbaChart::getPlotBy( )
+{
+ try
+ {
+ chart::ChartDataRowSource aChartDataRowSource;
+ mxDiagramPropertySet->getPropertyValue(DATAROWSOURCE) >>= aChartDataRowSource;
+ if (aChartDataRowSource == chart::ChartDataRowSource_COLUMNS)
+ {
+ return xlColumns;
+ }
+ else
+ {
+ return xlRows;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+void
+ScVbaChart::setDiagram( const OUString& _sDiagramType )
+{
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xMSF( mxChartDocument, uno::UNO_QUERY_THROW );
+ uno::Reference< chart::XDiagram > xDiagram( xMSF->createInstance( _sDiagramType ), uno::UNO_QUERY_THROW );
+ mxChartDocument->setDiagram( xDiagram );
+ mxDiagramPropertySet.set( xDiagram, uno::UNO_QUERY_THROW );
+ }
+ catch ( const uno::Exception& )
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+// #TODO find out why we have Location/getLocation? there is afaik no
+// Location property, just a Location function for the Chart object
+sal_Int32 SAL_CALL
+ScVbaChart::Location()
+{
+ return getLocation();
+}
+
+sal_Int32 SAL_CALL
+ScVbaChart::getLocation()
+{
+ return -1;
+}
+
+void SAL_CALL
+ScVbaChart::setLocation( ::sal_Int32 /*where*/, const css::uno::Any& /*Name*/ )
+{
+ // Helper api just stubs out the code <shrug>
+ // #TODO come back and make sense out of this
+// String sheetName = null;
+//
+// if ((name != null) && name instanceof String) {
+// sheetName = (String) name;
+// }
+// XSpreadsheetDocument xShDoc = (XSpreadsheetDocument) UnoRuntime.queryInterface( XSpreadsheetDocument.class,getXModel() );
+// com.sun.star.sheet.XSpreadsheets xSheets = xShDoc.Sheets();
+//
+// switch (where) {
+// case ClLocationType.clLocationAsObject_value: //{
+//
+// if (sheetName == null) {
+// DebugHelper.writeInfo("Can't embed in Chart without knowing SheetName");
+// return;
+// }
+//
+// try {
+// Any any = (Any) xSheets.getByName(sheetName);
+// chartSheet = (XSpreadsheet) any.getObject();
+//
+// // chartSheet = (XSpreadsheet) xSheets.getByName( sheetName );
+// } catch (NoSuchElementException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+//
+// return;
+// } catch (WrappedTargetException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+//
+// return;
+// } catch (java.lang.Exception e) {
+// e.printStackTrace();
+// }
+//
+// XTableChartsSupplier xTCS = (XTableChartsSupplier) UnoRuntime.queryInterface( XTableChartsSupplier.class, chartSheet);
+// XTableCharts xTableCharts = xTCS.getCharts();
+// XIndexAccess xIA = (XIndexAccess) UnoRuntime.queryInterface( XIndexAccess.class, xTableCharts);
+// int numCharts = xIA.getCount();
+// chartName = "Chart " + (numCharts + 1);
+//
+// //}
+// break;
+//
+// case ClLocationType.clLocationAsNewSheet_value:
+// case ClLocationType.clLocationAutomatic_value:default: //{
+// chartName = "Chart 1"; // Since it's a new sheet, it's the first on it...
+//
+// XIndexAccess xSheetIA = (XIndexAccess) UnoRuntime.queryInterface( XIndexAccess.class, xSheets);
+//
+// short newSheetNum = (short) (xSheetIA.getCount() + 1);
+//
+// if (sheetName == null){
+// sheetName = "ChartSheet " + newSheetNum; // Why not?
+// }
+// // DPK TODO : Probably should use Sheets to create this!
+// xSheets.insertNewByName(sheetName, newSheetNum);
+//
+// try {
+// chartSheet =
+// (XSpreadsheet) xSheets.getByName(sheetName);
+// } catch (NoSuchElementException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+//
+// return;
+// } catch (WrappedTargetException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+//
+// return;
+// }
+//
+// //}
+// break;
+// }
+//
+// // Last thing should be a call to createChartForReal(), one of them
+// // should succeed.
+// createChartForReal();
+
+}
+
+sal_Bool SAL_CALL
+ScVbaChart::getHasTitle( )
+{
+ bool bHasTitle = false;
+ try
+ {
+ mxChartPropertySet->getPropertyValue(HASMAINTITLE) >>= bHasTitle;
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return bHasTitle;
+}
+
+void SAL_CALL
+ScVbaChart::setHasTitle( sal_Bool bTitle )
+{
+ try
+ {
+ mxChartPropertySet->setPropertyValue(HASMAINTITLE, uno::Any( bTitle ));
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+
+}
+
+sal_Bool SAL_CALL
+ScVbaChart::getHasLegend( )
+{
+ bool bHasLegend = false;
+ try
+ {
+ mxChartPropertySet->getPropertyValue(HASLEGEND) >>= bHasLegend;
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return bHasLegend;
+}
+
+void SAL_CALL
+ScVbaChart::setHasLegend( sal_Bool bLegend )
+{
+ try
+ {
+ mxChartPropertySet->setPropertyValue(HASLEGEND, uno::Any(bLegend));
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+uno::Reference< excel::XChartTitle > SAL_CALL
+ScVbaChart::getChartTitle( )
+{
+ uno::Reference< drawing::XShape > xTitleShape = mxChartDocument->getTitle();
+ // #TODO check parent
+ return new ScVbaChartTitle(this, mxContext, xTitleShape);
+}
+
+uno::Any SAL_CALL
+ScVbaChart::Axes( const uno::Any& Type, const uno::Any& AxisGroup )
+{
+ // mmm chart probably is the parent, #TODO check parent
+ uno::Reference< excel::XAxes > xAxes = new ScVbaAxes( this, mxContext, this );
+ if ( !Type.hasValue() )
+ return uno::Any( xAxes );
+ return xAxes->Item( Type, AxisGroup );
+}
+bool
+ScVbaChart::is3D()
+{
+ // #TODO perhaps provide limited Debughelper functionality
+ bool is3d = false;
+ mxDiagramPropertySet->getPropertyValue(DIM3D) >>= is3d;
+ return is3d;
+}
+
+sal_Int32
+ScVbaChart::getStackedType( sal_Int32 _nStacked, sal_Int32 _n100PercentStacked, sal_Int32 _nUnStacked )
+{
+ // #TODO perhaps provide limited Debughelper functionality
+ if (isStacked())
+ {
+ if (is100PercentStacked())
+ return _n100PercentStacked;
+ else
+ return _nStacked;
+ }
+ else
+ return _nUnStacked;
+}
+
+bool
+ScVbaChart::isStacked()
+{
+ // #TODO perhaps provide limited Debughelper functionality
+ bool bStacked = false;
+ mxDiagramPropertySet->getPropertyValue(STACKED) >>= bStacked;
+ return bStacked;
+}
+
+bool
+ScVbaChart::is100PercentStacked()
+{
+ // #TODO perhaps provide limited Debughelper functionality
+ bool b100Percent = false;
+ mxDiagramPropertySet->getPropertyValue(PERCENT) >>= b100Percent;
+ return b100Percent;
+}
+
+sal_Int32
+ScVbaChart::getSolidType(sal_Int32 _nDeep, sal_Int32 _nVertiStacked, sal_Int32 _nVerti100PercentStacked, sal_Int32 _nVertiUnStacked, sal_Int32 _nHoriStacked, sal_Int32 _nHori100PercentStacked, sal_Int32 _nHoriUnStacked)
+{
+ try
+ {
+ bool bIsVertical = true;
+ mxDiagramPropertySet->getPropertyValue(VERTICAL) >>= bIsVertical;
+ bool bIsDeep = false;
+ mxDiagramPropertySet->getPropertyValue(DEEP) >>= bIsDeep;
+
+ if (bIsDeep)
+ {
+ return _nDeep;
+ }
+ else
+ {
+ if (bIsVertical)
+ {
+ return getStackedType(_nVertiStacked, _nVerti100PercentStacked, _nVertiUnStacked);
+ }
+ else
+ {
+ return getStackedType(_nHoriStacked, _nHori100PercentStacked, _nHoriUnStacked);
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+sal_Int32
+ScVbaChart::getStockUpDownValue(sal_Int32 _nUpDown, sal_Int32 _nNotUpDown)
+{
+ try
+ {
+ bool bUpDown = false;
+ mxDiagramPropertySet->getPropertyValue(UPDOWN) >>= bUpDown;
+ if (bUpDown)
+ {
+ return _nUpDown;
+ }
+ else
+ {
+ return _nNotUpDown;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+}
+
+bool
+ScVbaChart::hasMarkers()
+{
+ bool bHasMarkers = false;
+ try
+ {
+ sal_Int32 nSymbol=0;
+ mxDiagramPropertySet->getPropertyValue(SYMBOLTYPE) >>= nSymbol;
+ bHasMarkers = nSymbol != chart::ChartSymbolType::NONE;
+ }
+ catch (const uno::Exception&)
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return bHasMarkers;
+}
+
+sal_Int32
+ScVbaChart::getMarkerType(sal_Int32 _nWithMarkers, sal_Int32 _nWithoutMarkers)
+{
+ if (hasMarkers())
+ return _nWithMarkers;
+ return _nWithoutMarkers;
+}
+
+void
+ScVbaChart::assignDiagramAttributes()
+{
+ xAxisXSupplier.set( mxDiagramPropertySet, uno::UNO_QUERY_THROW );
+ xAxisYSupplier.set( mxDiagramPropertySet, uno::UNO_QUERY_THROW );
+ xAxisZSupplier.set( mxDiagramPropertySet, uno::UNO_QUERY_THROW );
+ xTwoAxisXSupplier.set( mxDiagramPropertySet, uno::UNO_QUERY_THROW );
+ xTwoAxisYSupplier.set( mxDiagramPropertySet, uno::UNO_QUERY_THROW );
+}
+
+uno::Reference< beans::XPropertySet >
+ScVbaChart::getAxisPropertySet(sal_Int32 _nAxisType, sal_Int32 _nAxisGroup)
+{
+ assignDiagramAttributes();
+ uno::Reference< beans::XPropertySet > xAxisProps;
+ switch(_nAxisType)
+ {
+ case xlCategory:
+ if (_nAxisGroup == xlPrimary)
+ {
+ xAxisProps = xAxisXSupplier->getXAxis();
+ }
+ else if (_nAxisGroup == xlSecondary)
+ {
+ xAxisProps = xTwoAxisXSupplier->getSecondaryXAxis();
+ }
+ break;
+ case xlSeriesAxis:
+ xAxisProps = xAxisZSupplier->getZAxis();
+ break;
+ case xlValue:
+ if (_nAxisGroup == xlPrimary)
+ xAxisProps = xAxisYSupplier->getYAxis();
+ else if (_nAxisGroup == xlSecondary)
+ xAxisProps = xTwoAxisYSupplier->getSecondaryYAxis();
+ break;
+ default:
+ return xAxisProps;
+ }
+ return xAxisProps;
+}
+
+OUString
+ScVbaChart::getServiceImplName()
+{
+ return "ScVbaChart";
+}
+
+uno::Sequence< OUString >
+ScVbaChart::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Chart"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbachart.hxx b/sc/source/ui/vba/vbachart.hxx
new file mode 100644
index 0000000000..fa32513b7e
--- /dev/null
+++ b/sc/source/ui/vba/vbachart.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/table/XTableChart.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XAxisXSupplier.hpp>
+#include <com/sun/star/chart/XAxisYSupplier.hpp>
+#include <com/sun/star/chart/XAxisZSupplier.hpp>
+#include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
+#include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
+#include <ooo/vba/excel/XChart.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl<ov::excel::XChart > ChartImpl_BASE;
+
+class ScVbaChart : public ChartImpl_BASE
+{
+friend class ScVbaAxis;
+
+ css::uno::Reference< css::chart::XChartDocument > mxChartDocument;
+ css::uno::Reference< css::table::XTableChart > mxTableChart;
+ css::uno::Reference< css::beans::XPropertySet > mxDiagramPropertySet;
+ css::uno::Reference< css::beans::XPropertySet > mxChartPropertySet;
+ css::uno::Reference< css::chart::XAxisXSupplier > xAxisXSupplier;
+ css::uno::Reference< css::chart::XAxisYSupplier> xAxisYSupplier;
+ css::uno::Reference< css::chart::XAxisZSupplier > xAxisZSupplier;
+ css::uno::Reference< css::chart::XTwoAxisXSupplier > xTwoAxisXSupplier;
+ css::uno::Reference< css::chart::XTwoAxisYSupplier > xTwoAxisYSupplier;
+
+ static css::uno::Sequence< OUString > getDefaultSeriesDescriptions( sal_Int32 nCount );
+ /// @throws css::script::BasicErrorException
+ void setDefaultChartType() ;
+ /// @throws css::script::BasicErrorException
+ void setDiagram( const OUString& _sDiagramType);
+ /// @throws css::uno::RuntimeException
+ bool isStacked();
+ /// @throws css::uno::RuntimeException
+ bool is100PercentStacked();
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getStackedType( sal_Int32 _nStacked, sal_Int32 _n100PercentStacked, sal_Int32 _nUnStacked );
+ /// @throws css::script::BasicErrorException
+ sal_Int32 getSolidType(sal_Int32 _nDeep, sal_Int32 _nVertiStacked, sal_Int32 _nVerti100PercentStacked, sal_Int32 _nVertiUnStacked, sal_Int32 _nHoriStacked, sal_Int32 _nHori100PercentStacked, sal_Int32 _nHoriUnStacked);
+ /// @throws css::script::BasicErrorException
+ sal_Int32 getStockUpDownValue(sal_Int32 _nUpDown, sal_Int32 _nNotUpDown);
+ /// @throws css::script::BasicErrorException
+ bool hasMarkers();
+ /// @throws css::script::BasicErrorException
+ sal_Int32 getMarkerType(sal_Int32 _nWithMarkers, sal_Int32 _nWithoutMarkers);
+ void assignDiagramAttributes();
+public:
+ ScVbaChart( const css::uno::Reference< ov::XHelperInterface >& _xParent, const css::uno::Reference< css::uno::XComponentContext >& _xContext, const css::uno::Reference< css::lang::XComponent >& _xChartComponent, css::uno::Reference< css::table::XTableChart > _xTableChart );
+
+ // Non-interface
+ const css::uno::Reference< css::beans::XPropertySet >& xDiagramPropertySet() const { return mxDiagramPropertySet; }
+ /// @throws css::uno::RuntimeException
+ bool is3D();
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::beans::XPropertySet > getAxisPropertySet(sal_Int32 _nAxisType, sal_Int32 _nAxisGroup);
+ // Methods
+ virtual OUString SAL_CALL getName() override;
+ virtual css::uno::Any SAL_CALL SeriesCollection(const css::uno::Any&) override;
+ virtual ::sal_Int32 SAL_CALL getChartType() override;
+ virtual void SAL_CALL setChartType( ::sal_Int32 _charttype ) override;
+ virtual void SAL_CALL Activate( ) override;
+ virtual void SAL_CALL setSourceData( const css::uno::Reference< ::ooo::vba::excel::XRange >& range, const css::uno::Any& PlotBy ) override;
+ virtual ::sal_Int32 SAL_CALL Location( ) override;
+ virtual ::sal_Int32 SAL_CALL getLocation( ) override;
+ virtual void SAL_CALL setLocation( ::sal_Int32 where, const css::uno::Any& Name ) override;
+ virtual sal_Bool SAL_CALL getHasTitle( ) override;
+ virtual void SAL_CALL setHasTitle( sal_Bool bTitle ) override;
+ virtual sal_Bool SAL_CALL getHasLegend( ) override;
+ virtual void SAL_CALL setHasLegend( sal_Bool bLegend ) override;
+ virtual void SAL_CALL setPlotBy( ::sal_Int32 xlRowCol ) override;
+ virtual ::sal_Int32 SAL_CALL getPlotBy( ) override;
+ virtual css::uno::Reference< ov::excel::XChartTitle > SAL_CALL getChartTitle( ) override;
+ virtual css::uno::Any SAL_CALL Axes( const css::uno::Any& Type, const css::uno::Any& AxisGroup ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbachartobject.cxx b/sc/source/ui/vba/vbachartobject.cxx
new file mode 100644
index 0000000000..0395da35e0
--- /dev/null
+++ b/sc/source/ui/vba/vbachartobject.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 "vbachart.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XEmbeddedObjectSupplier.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/script/BasicErrorException.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <basic/sberrors.hxx>
+#include <utility>
+#include "vbachartobject.hxx"
+#include "vbachartobjects.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+constexpr OUStringLiteral PERSIST_NAME(u"PersistName");
+
+ScVbaChartObject::ScVbaChartObject( const css::uno::Reference< ov::XHelperInterface >& _xParent, const css::uno::Reference< css::uno::XComponentContext >& _xContext, css::uno::Reference< css::table::XTableChart > _xTableChart, css::uno::Reference< css::drawing::XDrawPageSupplier > _xDrawPageSupplier ) : ChartObjectImpl_BASE( _xParent, _xContext ), xTableChart(std::move( _xTableChart )), xDrawPageSupplier(std::move( _xDrawPageSupplier ))
+{
+ xDrawPage = xDrawPageSupplier->getDrawPage();
+ xEmbeddedObjectSupplier.set( xTableChart, uno::UNO_QUERY_THROW );
+ xNamed.set( xTableChart, uno::UNO_QUERY_THROW );
+ sPersistName = getPersistName();
+ xShape = setShape();
+ setName(sPersistName);
+ oShapeHelper.emplace(xShape);
+}
+
+OUString const & ScVbaChartObject::getPersistName()
+{
+ if ( sPersistName.isEmpty() )
+ sPersistName = xNamed->getName();
+ return sPersistName;
+}
+
+uno::Reference< drawing::XShape >
+ScVbaChartObject::setShape()
+{
+ try
+ {
+ sal_Int32 nItems = xDrawPage->getCount();
+ for (int i = 0; i < nItems; i++)
+ {
+ xShape.set( xDrawPage->getByIndex(i), uno::UNO_QUERY_THROW );
+ if (xShape->getShapeType() == "com.sun.star.drawing.OLE2Shape")
+ {
+ uno::Reference< beans::XPropertySet > xShapePropertySet(xShape, uno::UNO_QUERY_THROW );
+ OUString sName;
+ xShapePropertySet->getPropertyValue(PERSIST_NAME ) >>=sName;
+ if ( sName == sPersistName )
+ {
+ xNamedShape.set( xShape, uno::UNO_QUERY_THROW );
+ return xShape;
+ }
+ }
+ }
+ }
+ catch (uno::Exception& )
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return nullptr;
+}
+
+void SAL_CALL
+ScVbaChartObject::setName( const OUString& sName )
+{
+ xNamedShape->setName(sName);
+}
+
+OUString SAL_CALL
+ScVbaChartObject::getName()
+{
+ return xNamedShape->getName();
+}
+
+void SAL_CALL
+ScVbaChartObject::Delete()
+{
+ // parent of this object is sheet
+ uno::Reference< excel::XWorksheet > xParent( getParent(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XChartObjects > xColl( xParent->ChartObjects( uno::Any() ), uno::UNO_QUERY_THROW );
+ ScVbaChartObjects* pChartObjectsImpl = static_cast< ScVbaChartObjects* >( xColl.get() );
+ if (!pChartObjectsImpl)
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), "Parent is not ChartObjects" );
+
+ pChartObjectsImpl->removeByName( getPersistName() );
+
+}
+
+void
+ScVbaChartObject::Activate()
+{
+ try
+ {
+ // #TODO #FIXME should be ThisWorkbook or equivalent, or in
+ // fact probably the chart object should be created with
+ // the XModel owner
+ //uno::Reference< view::XSelectionSupplier > xSelectionSupplier( getXModel().getCurrentController());
+ uno::Reference< view::XSelectionSupplier > xSelectionSupplier( getCurrentExcelDoc(mxContext)->getCurrentController(), uno::UNO_QUERY_THROW );
+ xSelectionSupplier->select(uno::Any(xShape));
+ }
+ catch (uno::Exception& )
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), "ChartObject Activate internal error" );
+ }
+}
+
+uno::Reference< excel::XChart > SAL_CALL
+ScVbaChartObject::getChart()
+{
+ return new ScVbaChart( this, mxContext, xEmbeddedObjectSupplier->getEmbeddedObject(), xTableChart );
+}
+
+OUString
+ScVbaChartObject::getServiceImplName()
+{
+ return "ScVbaChartObject";
+}
+
+uno::Sequence< OUString >
+ScVbaChartObject::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.ChartObject"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbachartobject.hxx b/sc/source/ui/vba/vbachartobject.hxx
new file mode 100644
index 0000000000..34a20d48db
--- /dev/null
+++ b/sc/source/ui/vba/vbachartobject.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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/table/XTableChart.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/document/XEmbeddedObjectSupplier.hpp>
+#include <ooo/vba/excel/XChartObject.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+#include <optional>
+
+typedef InheritedHelperInterfaceWeakImpl<ov::excel::XChartObject > ChartObjectImpl_BASE;
+
+class ScVbaChartObject : public ChartObjectImpl_BASE
+{
+
+ css::uno::Reference< css::table::XTableChart > xTableChart;
+ css::uno::Reference< css::document::XEmbeddedObjectSupplier > xEmbeddedObjectSupplier;
+ css::uno::Reference< css::drawing::XDrawPageSupplier > xDrawPageSupplier;
+ css::uno::Reference< css::drawing::XDrawPage > xDrawPage;
+ css::uno::Reference< css::drawing::XShape > xShape;
+ css::uno::Reference< css::container::XNamed > xNamed;
+ OUString sPersistName;
+ std::optional<ov::ShapeHelper> oShapeHelper;
+ css::uno::Reference< css::container::XNamed > xNamedShape;
+ OUString const & getPersistName();
+ /// @throws css::script::BasicErrorException
+ css::uno::Reference< css::drawing::XShape > setShape();
+public:
+ ScVbaChartObject( const css::uno::Reference< ov::XHelperInterface >& _xParent, const css::uno::Reference< css::uno::XComponentContext >& _xContext, css::uno::Reference< css::table::XTableChart > _xTableChart, css::uno::Reference< css::drawing::XDrawPageSupplier > _xDrawPageSupplier );
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& sName ) override;
+ virtual css::uno::Reference< ov::excel::XChart > SAL_CALL getChart() override;
+ virtual void SAL_CALL Delete() override;
+ /// @throws css::script::BasicErrorException
+ void Activate();
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbachartobjects.cxx b/sc/source/ui/vba/vbachartobjects.cxx
new file mode 100644
index 0000000000..3cc7788bb8
--- /dev/null
+++ b/sc/source/ui/vba/vbachartobjects.cxx
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/table/XTableChartsSupplier.hpp>
+#include <com/sun/star/table/XTableChart.hpp>
+#include <com/sun/star/script/BasicErrorException.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <ooo/vba/excel/XlChartType.hpp>
+
+#include "vbachartobjects.hxx"
+#include "vbachartobject.hxx"
+#include <docsh.hxx>
+#include <cellsuno.hxx>
+
+#include <string_view>
+#include <utility>
+#include <vector>
+#include <basic/sberrors.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+namespace {
+
+class ChartObjectEnumerationImpl : public EnumerationHelperImpl
+{
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier;
+
+public:
+ /// @throws uno::RuntimeException
+ ChartObjectEnumerationImpl( const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, uno::Reference< drawing::XDrawPageSupplier > _xDrawPageSupplier, const uno::Reference< XHelperInterface >& _xParent ) : EnumerationHelperImpl( _xParent, xContext, xEnumeration ), xDrawPageSupplier(std::move( _xDrawPageSupplier )) {}
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ uno::Any ret;
+
+ try
+ {
+ uno::Reference< table::XTableChart > xTableChart( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ // parent Object is sheet
+ ret <<= uno::Reference< excel::XChartObject > ( new ScVbaChartObject( m_xParent, m_xContext, xTableChart, xDrawPageSupplier ) );
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ throw;
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception&)
+ {
+ css::uno::Any anyEx(cppu::getCaughtException());
+ throw lang::WrappedTargetException(
+ "Error creating ScVbaChartObject!",
+ getXWeak(),
+ anyEx );
+ }
+ return ret;
+ }
+};
+
+}
+
+ScVbaChartObjects::ScVbaChartObjects( const css::uno::Reference< ov::XHelperInterface >& _xParent, const css::uno::Reference< css::uno::XComponentContext >& _xContext, const css::uno::Reference< css::table::XTableCharts >& _xTableCharts, uno::Reference< drawing::XDrawPageSupplier > _xDrawPageSupplier ) : ChartObjects_BASE(_xParent, _xContext, css::uno::Reference< css::container::XIndexAccess >( _xTableCharts, css::uno::UNO_QUERY ) ), xTableCharts( _xTableCharts ) , xDrawPageSupplier(std::move( _xDrawPageSupplier ))
+{
+
+}
+
+void
+ScVbaChartObjects::removeByName(const OUString& _sChartName)
+{
+ xTableCharts->removeByName( _sChartName );
+}
+
+uno::Sequence< OUString >
+ScVbaChartObjects::getChartObjectNames() const
+{
+ uno::Sequence< OUString > sChartNames;
+ try
+ {
+ // c++ hackery
+ uno::Reference< uno::XInterface > xIf( xDrawPageSupplier, uno::UNO_QUERY_THROW );
+ ScCellRangesBase* pUno = dynamic_cast< ScCellRangesBase* >( xIf.get() );
+ ScDocShell* pDocShell = nullptr;
+ if ( !pUno )
+ throw uno::RuntimeException("Failed to obtain the impl class from the drawpage" );
+ pDocShell = pUno->GetDocShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException("Failed to obtain the docshell implclass" );
+
+ uno::Reference< sheet::XSpreadsheets > xSpreadsheets = pDocShell->GetModel()->getSheets();
+ std::vector< OUString > aChartNamesVector;
+
+ const uno::Sequence< OUString > sSheetNames = xSpreadsheets->getElementNames();
+ for (const auto& rSheetName : sSheetNames)
+ {
+ uno::Reference< table::XTableChartsSupplier > xLocTableChartsSupplier( xSpreadsheets->getByName(rSheetName), uno::UNO_QUERY_THROW );
+ const uno::Sequence< OUString > scurchartnames = xLocTableChartsSupplier->getCharts()->getElementNames();
+ aChartNamesVector.insert( aChartNamesVector.end(), scurchartnames.begin(), scurchartnames.end() );
+ }
+ sChartNames = comphelper::containerToSequence( aChartNamesVector );
+ }
+ catch (uno::Exception& )
+ {
+ throw script::BasicErrorException( OUString(), uno::Reference< uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return sChartNames;
+}
+
+// XChartObjects
+uno::Any SAL_CALL
+ScVbaChartObjects::Add( double _nX, double _nY, double _nWidth, double _nHeight )
+{
+ try
+ {
+ uno::Sequence< table::CellRangeAddress > aCellRangeAddress( 1 );
+ awt::Rectangle aRectangle;
+ aRectangle.X = Millimeter::getInHundredthsOfOneMillimeter(_nX);
+ aRectangle.Y = Millimeter::getInHundredthsOfOneMillimeter(_nY);
+ aRectangle.Width = Millimeter::getInHundredthsOfOneMillimeter(_nWidth);
+ aRectangle.Height = Millimeter::getInHundredthsOfOneMillimeter(_nHeight);
+ // Note the space at the end of the stem ("Chart "). In ChartSheets only "Chart" is the stem
+ OUString sPersistChartName = ContainerUtilities::getUniqueName( getChartObjectNames(), "Chart " , std::u16string_view(), 1);
+ xTableCharts->addNewByName(sPersistChartName, aRectangle, aCellRangeAddress, true, false );
+ uno::Reference< excel::XChartObject > xChartObject( getItemByStringIndex( sPersistChartName ), uno::UNO_QUERY_THROW );
+ xChartObject->getChart()->setChartType(excel::XlChartType::xlColumnClustered);
+ return uno::Any( xChartObject );
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("sc");
+ }
+ return aNULL();
+}
+void SAL_CALL ScVbaChartObjects::Delete( )
+{
+ const uno::Sequence< OUString > sChartNames = xTableCharts->getElementNames();
+ for (const auto& rChartName : sChartNames)
+ removeByName(rChartName);
+}
+
+// XEnumerationAccess
+
+uno::Reference< container::XEnumeration >
+ScVbaChartObjects::createEnumeration()
+{
+ css::uno::Reference< container::XEnumerationAccess > xEnumAccess( xTableCharts, uno::UNO_QUERY_THROW );
+ return new ChartObjectEnumerationImpl( mxContext, xEnumAccess->createEnumeration(), xDrawPageSupplier, getParent() /* sheet */);
+}
+
+// XElementAccess
+
+uno::Type
+ScVbaChartObjects::getElementType()
+{
+ return cppu::UnoType<excel::XChartObject>::get();
+}
+
+// ScVbaCollectionBaseImpl
+uno::Any
+ScVbaChartObjects::createCollectionObject( const css::uno::Any& aSource )
+{
+ uno::Reference< table::XTableChart > xTableChart( aSource, uno::UNO_QUERY_THROW );
+ // correct parent object is sheet
+ return uno::Any( uno::Reference< excel::XChartObject > ( new ScVbaChartObject( getParent(), mxContext, xTableChart, xDrawPageSupplier ) ) );
+}
+
+OUString
+ScVbaChartObjects::getServiceImplName()
+{
+ return "ScVbaChartObjects";
+}
+
+css::uno::Sequence<OUString>
+ScVbaChartObjects::getServiceNames()
+{
+ static uno::Sequence< OUString > const sNames
+ {
+ "ooo.vba.excel.ChartObjects"
+ };
+ return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbachartobjects.hxx b/sc/source/ui/vba/vbachartobjects.hxx
new file mode 100644
index 0000000000..0efb4134ff
--- /dev/null
+++ b/sc/source/ui/vba/vbachartobjects.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 .
+ */
+
+#pragma once
+
+#include <ooo/vba/excel/XChartObjects.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::container { class XEnumeration; }
+namespace com::sun::star::drawing { class XDrawPageSupplier; }
+namespace com::sun::star::table { class XTableCharts; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+typedef CollTestImplHelper< ov::excel::XChartObjects > ChartObjects_BASE;
+
+class ScVbaChartObjects : public ChartObjects_BASE
+{
+
+ css::uno::Reference< css::table::XTableCharts > xTableCharts;
+ css::uno::Reference< css::drawing::XDrawPageSupplier > xDrawPageSupplier;
+ // method associated with populating the hashmap ( I'm not convinced this is necessary )
+ //css::uno::Reference< ov::excel::XChartObject > putByPersistName( const rtl:::OUString& _sPersistChartName );
+public:
+ ScVbaChartObjects( const css::uno::Reference< ov::XHelperInterface >& _xParent, const css::uno::Reference< css::uno::XComponentContext >& _xContext, const css::uno::Reference< css::table::XTableCharts >& _xTableCharts, css::uno::Reference< css::drawing::XDrawPageSupplier > _xDrawPageSupplier );
+
+ /// @throws css::script::BasicErrorException
+ css::uno::Sequence< OUString > getChartObjectNames() const;
+ void removeByName(const OUString& _sChartName);
+
+ // XChartObjects
+ virtual css::uno::Any SAL_CALL Add( double Left, double Top, double Width, double Height ) override;
+ virtual void SAL_CALL Delete( ) override;
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ // ScVbaCollectionBaseImpl
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+ // ChartObjects_BASE
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacharttitle.cxx b/sc/source/ui/vba/vbacharttitle.cxx
new file mode 100644
index 0000000000..2e881fd619
--- /dev/null
+++ b/sc/source/ui/vba/vbacharttitle.cxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbacharttitle.hxx"
+#include <comphelper/sequence.hxx>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaChartTitle::ScVbaChartTitle( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< drawing::XShape >& _xTitleShape ) : ChartTitleBase( xParent, xContext, _xTitleShape )
+{
+}
+
+OUString
+ScVbaChartTitle::getServiceImplName()
+{
+ return "ScVbaChartTitle";
+}
+
+uno::Sequence< OUString >
+ScVbaChartTitle::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames = comphelper::concatSequences(
+ ChartTitleBase::getServiceNames(),
+ uno::Sequence< OUString > { "ooo.vba.excel.Chart" } );
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacharttitle.hxx b/sc/source/ui/vba/vbacharttitle.hxx
new file mode 100644
index 0000000000..2b816357e2
--- /dev/null
+++ b/sc/source/ui/vba/vbacharttitle.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include "vbatitle.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XChartTitle.hpp>
+
+typedef TitleImpl< cppu::WeakImplHelper< ov::excel::XChartTitle > > ChartTitleBase;
+
+class ScVbaChartTitle : public ChartTitleBase
+{
+public:
+ ScVbaChartTitle( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::drawing::XShape >& _xTitleShape );
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacomment.cxx b/sc/source/ui/vba/vbacomment.cxx
new file mode 100644
index 0000000000..a9b1cf7c47
--- /dev/null
+++ b/sc/source/ui/vba/vbacomment.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbacomment.hxx"
+
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSheetAnnotationAnchor.hpp>
+#include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
+#include <com/sun/star/sheet/XSheetAnnotationShapeSupplier.hpp>
+#include <com/sun/star/sheet/XSheetCellRange.hpp>
+#include <com/sun/star/sheet/XCellAddressable.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <com/sun/star/table/XCell.hpp>
+#include <com/sun/star/text/XSimpleText.hpp>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <ooo/vba/office/MsoShapeType.hpp>
+
+#include <vbahelper/vbashape.hxx>
+#include <sal/log.hxx>
+#include "vbacomments.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaComment::ScVbaComment(
+ const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Reference< table::XCellRange >& xRange ) :
+ ScVbaComment_BASE( xParent, xContext ),
+ mxModel( xModel, uno::UNO_SET_THROW ),
+ mxRange( xRange )
+{
+ if ( !xRange.is() )
+ throw lang::IllegalArgumentException("range is not set ", uno::Reference< uno::XInterface >() , 1 );
+ getAnnotation();
+}
+
+// private helper functions
+
+uno::Reference< sheet::XSheetAnnotation >
+ScVbaComment::getAnnotation()
+{
+ uno::Reference< table::XCell > xCell( mxRange->getCellByPosition(0, 0), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XSheetAnnotationAnchor > xAnnoAnchor( xCell, uno::UNO_QUERY_THROW );
+ return uno::Reference< sheet::XSheetAnnotation > ( xAnnoAnchor->getAnnotation(), uno::UNO_SET_THROW );
+}
+
+uno::Reference< sheet::XSheetAnnotations >
+ScVbaComment::getAnnotations() const
+{
+ uno::Reference< sheet::XSheetCellRange > xSheetCellRange(mxRange, ::uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
+ uno::Reference< sheet::XSheetAnnotationsSupplier > xAnnosSupp( xSheet, uno::UNO_QUERY_THROW );
+
+ return uno::Reference< sheet::XSheetAnnotations > ( xAnnosSupp->getAnnotations(), uno::UNO_SET_THROW );
+}
+
+sal_Int32
+ScVbaComment::getAnnotationIndex()
+{
+ uno::Reference< sheet::XSheetAnnotations > xAnnos = getAnnotations();
+ table::CellAddress aAddress = getAnnotation()->getPosition();
+
+ sal_Int32 aIndex = 0;
+ sal_Int32 aCount = xAnnos->getCount();
+
+ for ( ; aIndex < aCount ; aIndex++ )
+ {
+ uno::Reference< sheet::XSheetAnnotation > xAnno( xAnnos->getByIndex( aIndex ), uno::UNO_QUERY_THROW );
+ table::CellAddress aAnnoAddress = xAnno->getPosition();
+
+ if ( aAnnoAddress.Column == aAddress.Column && aAnnoAddress.Row == aAddress.Row && aAnnoAddress.Sheet == aAddress.Sheet )
+ {
+ SAL_INFO("sc.ui", "terminating search, index is " << aIndex);
+ break;
+ }
+ }
+ SAL_INFO("sc.ui", "returning index is " << aIndex);
+
+ return aIndex;
+}
+
+uno::Reference< excel::XComment >
+ScVbaComment::getCommentByIndex( sal_Int32 Index )
+{
+ uno::Reference< container::XIndexAccess > xIndexAccess( getAnnotations(), uno::UNO_QUERY_THROW );
+ // parent is sheet ( parent of the range which is the parent of the comment )
+ uno::Reference< XCollection > xColl( new ScVbaComments( getParent()->getParent(), mxContext, mxModel, xIndexAccess ) );
+
+ return uno::Reference< excel::XComment > ( xColl->Item( uno::Any( Index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ }
+
+// public vba functions
+
+OUString SAL_CALL
+ScVbaComment::getAuthor()
+{
+ return getAnnotation()->getAuthor();
+}
+
+void SAL_CALL
+ScVbaComment::setAuthor( const OUString& /*_author*/ )
+{
+ // #TODO #FIXME implementation needed
+}
+
+uno::Reference< msforms::XShape > SAL_CALL
+ScVbaComment::getShape()
+{
+ uno::Reference< sheet::XSheetAnnotationShapeSupplier > xAnnoShapeSupp( getAnnotation(), uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XShape > xAnnoShape( xAnnoShapeSupp->getAnnotationShape(), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XSheetCellRange > xCellRange( mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupp( xCellRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XShapes > xShapes( xDrawPageSupp->getDrawPage(), uno::UNO_QUERY_THROW );
+ return new ScVbaShape( this, mxContext, xAnnoShape, xShapes, mxModel, office::MsoShapeType::msoComment );
+}
+
+sal_Bool SAL_CALL
+ScVbaComment::getVisible()
+{
+ return getAnnotation()->getIsVisible();
+}
+
+void SAL_CALL
+ScVbaComment::setVisible( sal_Bool _visible )
+{
+ getAnnotation()->setIsVisible( _visible );
+}
+
+void SAL_CALL
+ScVbaComment::Delete()
+{
+ getAnnotations()->removeByIndex( getAnnotationIndex() );
+}
+
+uno::Reference< excel::XComment > SAL_CALL
+ScVbaComment::Next()
+{
+ // index: uno = 0, vba = 1
+ return getCommentByIndex( getAnnotationIndex() + 2 );
+}
+
+uno::Reference< excel::XComment > SAL_CALL
+ScVbaComment::Previous()
+{
+ // index: uno = 0, vba = 1
+ return getCommentByIndex( getAnnotationIndex() );
+}
+
+OUString SAL_CALL
+ScVbaComment::Text( const uno::Any& aText, const uno::Any& aStart, const uno::Any& Overwrite )
+{
+ OUString sText;
+ aText >>= sText;
+
+ uno::Reference< text::XSimpleText > xAnnoText( getAnnotation(), uno::UNO_QUERY_THROW );
+ OUString sAnnoText = xAnnoText->getString();
+
+ if ( aStart.hasValue() )
+ {
+ sal_Int16 nStart = 0;
+ bool bOverwrite = true;
+ Overwrite >>= bOverwrite;
+
+ if ( aStart >>= nStart )
+ {
+ uno::Reference< text::XTextCursor > xTextCursor( xAnnoText->createTextCursor(), uno::UNO_SET_THROW );
+
+ if ( bOverwrite )
+ {
+ xTextCursor->collapseToStart();
+ xTextCursor->gotoStart( false );
+ xTextCursor->goRight( nStart - 1, false );
+ xTextCursor->gotoEnd( true );
+ }
+ else
+ {
+ xTextCursor->collapseToStart();
+ xTextCursor->gotoStart( false );
+ xTextCursor->goRight( nStart - 1 , true );
+ }
+
+ uno::Reference< text::XTextRange > xRange( xTextCursor, uno::UNO_QUERY_THROW );
+ xAnnoText->insertString( xRange, sText, bOverwrite );
+ return xAnnoText->getString();
+ }
+ throw uno::RuntimeException("ScVbaComment::Text - bad Start value " );
+ }
+ else if ( aText.hasValue() )
+ {
+ uno::Reference< sheet::XCellAddressable > xCellAddr(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
+ table::CellAddress aAddress = xCellAddr->getCellAddress();
+ getAnnotations()->insertNew( aAddress, sText );
+ }
+
+ return sAnnoText;
+}
+
+OUString
+ScVbaComment::getServiceImplName()
+{
+ return "ScVbaComment";
+}
+
+uno::Sequence< OUString >
+ScVbaComment::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.ScVbaComment"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacomment.hxx b/sc/source/ui/vba/vbacomment.hxx
new file mode 100644
index 0000000000..fe801899ca
--- /dev/null
+++ b/sc/source/ui/vba/vbacomment.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XComment.hpp>
+#include <ooo/vba/msforms/XShape.hpp>
+#include <com/sun/star/sheet/XSheetAnnotations.hpp>
+#include <com/sun/star/sheet/XSheetAnnotation.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XComment > ScVbaComment_BASE;
+
+class ScVbaComment : public ScVbaComment_BASE
+{
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::table::XCellRange > mxRange;
+
+private:
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::sheet::XSheetAnnotation > getAnnotation();
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::sheet::XSheetAnnotations > getAnnotations() const;
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getAnnotationIndex();
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XComment > getCommentByIndex( sal_Int32 Index );
+public:
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ ScVbaComment(
+ const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::table::XCellRange >& xRange );
+
+ // Attributes
+ virtual OUString SAL_CALL getAuthor() override;
+ virtual void SAL_CALL setAuthor( const OUString& _author ) override;
+ virtual css::uno::Reference< ov::msforms::XShape > SAL_CALL getShape() override;
+ virtual sal_Bool SAL_CALL getVisible() override;
+ virtual void SAL_CALL setVisible( sal_Bool _visible ) override;
+
+ // Methods
+ virtual void SAL_CALL Delete() override;
+ virtual css::uno::Reference< ov::excel::XComment > SAL_CALL Next() override;
+ virtual css::uno::Reference< ov::excel::XComment > SAL_CALL Previous() override;
+ virtual OUString SAL_CALL Text( const css::uno::Any& Text, const css::uno::Any& Start, const css::uno::Any& Overwrite ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacomments.cxx b/sc/source/ui/vba/vbacomments.cxx
new file mode 100644
index 0000000000..470e468b71
--- /dev/null
+++ b/sc/source/ui/vba/vbacomments.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 "vbacomments.hxx"
+#include "vbacomment.hxx"
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/sheet/XSheetAnnotation.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+static uno::Any AnnotationToComment( const uno::Any& aSource, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< frame::XModel >& xModel )
+{
+ uno::Reference< sheet::XSheetAnnotation > xAnno( aSource, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XChild > xChild( xAnno, uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xCellRange( xChild->getParent(), uno::UNO_QUERY_THROW );
+
+ // #FIXME needs to find the correct Parent
+ return uno::Any( uno::Reference< excel::XComment > (
+ new ScVbaComment( uno::Reference< XHelperInterface >(), xContext, xModel, xCellRange ) ) );
+}
+
+namespace {
+
+class CommentEnumeration : public EnumerationHelperImpl
+{
+ css::uno::Reference< css::frame::XModel > mxModel;
+public:
+ /// @throws uno::RuntimeException
+ CommentEnumeration(
+ const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< container::XEnumeration >& xEnumeration,
+ const uno::Reference< frame::XModel >& xModel ) :
+ EnumerationHelperImpl( xParent, xContext, xEnumeration ),
+ mxModel( xModel, uno::UNO_SET_THROW )
+ {}
+
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ return AnnotationToComment( m_xEnumeration->nextElement(), m_xContext, mxModel );
+ }
+
+};
+
+}
+
+ScVbaComments::ScVbaComments(
+ const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Reference< container::XIndexAccess >& xIndexAccess ) :
+ ScVbaComments_BASE( xParent, xContext, xIndexAccess ),
+ mxModel( xModel, uno::UNO_SET_THROW )
+{
+}
+
+// public helper functions
+
+uno::Reference< container::XEnumeration >
+ScVbaComments::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
+ return new CommentEnumeration( mxParent, mxContext, xEnumAccess->createEnumeration(), mxModel );
+}
+
+uno::Any
+ScVbaComments::createCollectionObject( const css::uno::Any& aSource )
+{
+ return AnnotationToComment( aSource, mxContext, mxModel );
+}
+
+uno::Type
+ScVbaComments::getElementType()
+{
+ return cppu::UnoType<excel::XComment>::get();
+}
+
+OUString
+ScVbaComments::getServiceImplName()
+{
+ return "ScVbaComments";
+}
+
+css::uno::Sequence<OUString>
+ScVbaComments::getServiceNames()
+{
+ static uno::Sequence< OUString > const sNames
+ {
+ "ooo.vba.excel.Comments"
+ };
+ return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacomments.hxx b/sc/source/ui/vba/vbacomments.hxx
new file mode 100644
index 0000000000..83a62b6e86
--- /dev/null
+++ b/sc/source/ui/vba/vbacomments.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XComments.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+typedef CollTestImplHelper< ov::excel::XComments > ScVbaComments_BASE;
+
+class ScVbaComments : public ScVbaComments_BASE
+{
+public:
+ ScVbaComments(
+ const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::container::XIndexAccess >& xIndexAccess );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // ScVbaComments_BASE
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+private:
+ css::uno::Reference< css::frame::XModel > mxModel;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacondition.cxx b/sc/source/ui/vba/vbacondition.cxx
new file mode 100644
index 0000000000..c22eff9bf5
--- /dev/null
+++ b/sc/source/ui/vba/vbacondition.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 "vbacondition.hxx"
+#include <ooo/vba/excel/XlFormatConditionOperator.hpp>
+#include <ooo/vba/excel/XFormatCondition.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <basic/sberrors.hxx>
+#include <utility>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+const sal_Int32 ISFORMULA = 98765432;
+
+template <typename... Ifc>
+ScVbaCondition<Ifc...>::ScVbaCondition(const uno::Reference<XHelperInterface>& xParent,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ uno::Reference<sheet::XSheetCondition> _xSheetCondition)
+ : ScVbaCondition_BASE(xParent, xContext)
+ , mxSheetCondition(std::move(_xSheetCondition))
+{
+ mxAddressable.set(xParent, uno::UNO_QUERY_THROW);
+}
+
+template <typename... Ifc>
+sheet::ConditionOperator ScVbaCondition<Ifc...>::retrieveAPIOperator(const uno::Any& _aOperator)
+{
+ sheet::ConditionOperator aRetAPIOperator = sheet::ConditionOperator_NONE;
+ sal_Int32 nOperator = 0;
+ if (_aOperator >>= nOperator)
+ {
+ switch (nOperator)
+ {
+ case excel::XlFormatConditionOperator::xlBetween:
+ aRetAPIOperator = sheet::ConditionOperator_BETWEEN;
+ break;
+ case excel::XlFormatConditionOperator::xlNotBetween:
+ aRetAPIOperator = sheet::ConditionOperator_NOT_BETWEEN;
+ break;
+ case excel::XlFormatConditionOperator::xlEqual:
+ aRetAPIOperator = sheet::ConditionOperator_EQUAL;
+ break;
+ case excel::XlFormatConditionOperator::xlNotEqual:
+ aRetAPIOperator = sheet::ConditionOperator_NOT_EQUAL;
+ break;
+ case excel::XlFormatConditionOperator::xlGreater:
+ aRetAPIOperator = sheet::ConditionOperator_GREATER;
+ break;
+ case excel::XlFormatConditionOperator::xlLess:
+ aRetAPIOperator = sheet::ConditionOperator_LESS;
+ break;
+ case excel::XlFormatConditionOperator::xlGreaterEqual:
+ aRetAPIOperator = sheet::ConditionOperator_GREATER_EQUAL;
+ break;
+ case excel::XlFormatConditionOperator::xlLessEqual:
+ aRetAPIOperator = sheet::ConditionOperator_LESS_EQUAL;
+ break;
+ default:
+ aRetAPIOperator = sheet::ConditionOperator_NONE;
+ break;
+ }
+ }
+ return aRetAPIOperator;
+}
+
+template <typename... Ifc> OUString ScVbaCondition<Ifc...>::Formula1()
+{
+ return mxSheetCondition->getFormula1();
+}
+
+template <typename... Ifc> OUString ScVbaCondition<Ifc...>::Formula2()
+{
+ return mxSheetCondition->getFormula2();
+}
+
+template <typename... Ifc> sal_Int32 ScVbaCondition<Ifc...>::Operator(bool _bIncludeFormulaValue)
+{
+ sal_Int32 retvalue = -1;
+ sheet::ConditionOperator aConditionalOperator = mxSheetCondition->getOperator();
+ switch (aConditionalOperator)
+ {
+ case sheet::ConditionOperator_EQUAL:
+ retvalue = excel::XlFormatConditionOperator::xlEqual;
+ break;
+ case sheet::ConditionOperator_NOT_EQUAL:
+ retvalue = excel::XlFormatConditionOperator::xlNotEqual;
+ break;
+ case sheet::ConditionOperator_GREATER:
+ retvalue = excel::XlFormatConditionOperator::xlGreater;
+ break;
+ case sheet::ConditionOperator_GREATER_EQUAL:
+ retvalue = excel::XlFormatConditionOperator::xlGreaterEqual;
+ break;
+ case sheet::ConditionOperator_LESS:
+ retvalue = excel::XlFormatConditionOperator::xlLess;
+ break;
+ case sheet::ConditionOperator_LESS_EQUAL:
+ retvalue = excel::XlFormatConditionOperator::xlLessEqual;
+ break;
+ case sheet::ConditionOperator_BETWEEN:
+ retvalue = excel::XlFormatConditionOperator::xlBetween;
+ break;
+ case sheet::ConditionOperator_NOT_BETWEEN:
+ retvalue = excel::XlFormatConditionOperator::xlNotBetween;
+ break;
+ case sheet::ConditionOperator_FORMULA:
+ if (_bIncludeFormulaValue)
+ {
+ //#FIXME huh what's this all about
+ // from helperapi/impl/.../calc/ConditionImpl
+ retvalue = ISFORMULA;
+ break;
+ }
+ [[fallthrough]]; //TODO ???
+ case sheet::ConditionOperator_NONE:
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, u"Operator not supported");
+ break;
+ }
+ return retvalue;
+}
+
+template class ScVbaCondition<excel::XFormatCondition>;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbacondition.hxx b/sc/source/ui/vba/vbacondition.hxx
new file mode 100644
index 0000000000..4a30036ba8
--- /dev/null
+++ b/sc/source/ui/vba/vbacondition.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 .
+ */
+#pragma once
+
+#include <vbahelper/vbahelperinterface.hxx>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+
+namespace com::sun::star::sheet { class XCellRangeAddressable; }
+namespace com::sun::star::sheet { class XSheetCondition; }
+
+template< typename... Ifc >
+class ScVbaCondition : public InheritedHelperInterfaceWeakImpl< Ifc... >
+{
+typedef InheritedHelperInterfaceWeakImpl< Ifc... > ScVbaCondition_BASE;
+protected:
+ css::uno::Reference< css::sheet::XCellRangeAddressable > mxAddressable;
+ css::uno::Reference< css::sheet::XSheetCondition > mxSheetCondition;
+public:
+ ScVbaCondition( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, css::uno::Reference< css::sheet::XSheetCondition > _xSheetCondition );
+
+ /// @throws css::script::BasicErrorException
+ static css::sheet::ConditionOperator retrieveAPIOperator( const css::uno::Any& _aOperator);
+
+ virtual OUString SAL_CALL Formula1( ) override;
+ virtual OUString SAL_CALL Formula2( ) override;
+ /// @throws css::script::BasicErrorException
+ virtual sal_Int32 Operator(bool _bIncludeFormulaValue);
+ virtual sal_Int32 SAL_CALL Operator() override = 0;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbadialog.cxx b/sc/source/ui/vba/vbadialog.cxx
new file mode 100644
index 0000000000..5537c65a3c
--- /dev/null
+++ b/sc/source/ui/vba/vbadialog.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 <sal/config.h>
+
+#include <string_view>
+
+#include "vbadialog.hxx"
+
+#include <sal/macros.h>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+const std::u16string_view aStringList[]=
+{
+ u".uno:Open",
+ u".uno:FormatCellDialog",
+ u".uno:InsertCell",
+ u".uno:Print",
+ u".uno:PasteSpecial",
+ u".uno:ToolProtectionDocument",
+ u".uno:ColumnWidth",
+ u".uno:DefineName",
+ u".uno:ConfigureDialog",
+ u".uno:HyperlinkDialog",
+ u".uno:InsertGraphic",
+ u".uno:InsertObject",
+ u".uno:PageFormatDialog",
+ u".uno:DataSort",
+ u".uno:RowHeight",
+ u".uno:AutoCorrectDlg",
+ u".uno:ConditionalFormatDialog",
+ u".uno:DataConsolidate",
+ u".uno:CreateNames",
+ u".uno:FillSeries",
+ u".uno:Validation",
+ u".uno:DefineLabelRange",
+ u".uno:DataFilterAutoFilter",
+ u".uno:DataFilterSpecialFilter",
+ u".uno:AutoFormat"
+};
+
+const sal_Int32 nDialogSize = SAL_N_ELEMENTS(aStringList);
+
+OUString
+ScVbaDialog::mapIndexToName( sal_Int32 nIndex )
+{
+ if( nIndex < nDialogSize )
+ return OUString(aStringList[ nIndex ]);
+ return OUString();
+}
+
+OUString
+ScVbaDialog::getServiceImplName()
+{
+ return "ScVbaDialog";
+}
+
+uno::Sequence< OUString >
+ScVbaDialog::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Dialog"
+ };
+ return aServiceNames;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbadialog.hxx b/sc/source/ui/vba/vbadialog.hxx
new file mode 100644
index 0000000000..9317cbfaf2
--- /dev/null
+++ b/sc/source/ui/vba/vbadialog.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 <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XDialog.hpp>
+#include <vbahelper/vbadialogbase.hxx>
+
+typedef cppu::ImplInheritanceHelper< VbaDialogBase, ov::excel::XDialog > ScVbaDialog_BASE;
+
+class ScVbaDialog : public ScVbaDialog_BASE
+{
+public:
+ ScVbaDialog( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel, sal_Int32 nIndex ):ScVbaDialog_BASE( xParent, xContext, xModel, nIndex ) {}
+
+ // Methods
+ virtual OUString mapIndexToName( sal_Int32 nIndex ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbadialogs.cxx b/sc/source/ui/vba/vbadialogs.cxx
new file mode 100644
index 0000000000..9264728a28
--- /dev/null
+++ b/sc/source/ui/vba/vbadialogs.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <ooo/vba/excel/XDialog.hpp>
+#include "vbadialogs.hxx"
+#include "vbadialog.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+uno::Any
+ScVbaDialogs::Item( const uno::Any &aItem )
+{
+ sal_Int32 nIndex = 0;
+ aItem >>= nIndex;
+ uno::Reference< excel::XDialog > aDialog( new ScVbaDialog( uno::Reference< XHelperInterface >( Application(),uno::UNO_QUERY_THROW ), mxContext, m_xModel, nIndex ) );
+ return uno::Any( aDialog );
+}
+
+OUString
+ScVbaDialogs::getServiceImplName()
+{
+ return "ScVbaDialogs";
+}
+
+uno::Sequence< OUString >
+ScVbaDialogs::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Dialogs"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbadialogs.hxx b/sc/source/ui/vba/vbadialogs.hxx
new file mode 100644
index 0000000000..97cbbda044
--- /dev/null
+++ b/sc/source/ui/vba/vbadialogs.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XDialogs.hpp>
+#include <vbahelper/vbadialogsbase.hxx>
+#include <cppuhelper/implbase.hxx>
+
+namespace ooo::vba { class XHelperInterface; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+typedef cppu::ImplInheritanceHelper< VbaDialogsBase, ov::excel::XDialogs > ScVbaDialogs_BASE;
+
+class ScVbaDialogs : public ScVbaDialogs_BASE
+{
+public:
+ ScVbaDialogs( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > &xContext, const css::uno::Reference< css::frame::XModel >& xModel ): ScVbaDialogs_BASE( xParent, xContext, xModel ) {}
+
+ // XCollection
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaeventshelper.cxx b/sc/source/ui/vba/vbaeventshelper.cxx
new file mode 100644
index 0000000000..9cfb3ab31b
--- /dev/null
+++ b/sc/source/ui/vba/vbaeventshelper.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 "vbaeventshelper.hxx"
+#include "excelvbahelper.hxx"
+
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/XTopWindowListener.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/frame/XBorderResizeListener.hpp>
+#include <com/sun/star/frame/XControllerBorder.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <unotools/eventcfg.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vbahelper/vbaaccesshelper.hxx>
+
+#include <docsh.hxx>
+#include <document.hxx>
+#include <cellsuno.hxx>
+#include <convuno.hxx>
+#include "vbaapplication.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::script::vba::VBAEventId;
+using namespace ::ooo::vba;
+
+namespace {
+
+/** Extracts a sheet index from the specified element of the passed sequence.
+ The element may be an integer, a Calc range or ranges object, or a VBA Range object.
+
+ @throws lang::IllegalArgumentException
+ @throws uno::RuntimeException
+*/
+SCTAB lclGetTabFromArgs( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
+{
+ VbaEventsHelperBase::checkArgument( rArgs, nIndex );
+
+ // first try to extract a sheet index
+ sal_Int32 nTab = -1;
+ if( rArgs[ nIndex ] >>= nTab )
+ {
+ if( (nTab < 0) || (nTab > MAXTAB) )
+ throw lang::IllegalArgumentException();
+ return static_cast< SCTAB >( nTab );
+ }
+
+ // try VBA Range object
+ uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
+ if( xVbaRange.is() )
+ {
+ uno::Reference< XHelperInterface > xVbaHelper( xVbaRange, uno::UNO_QUERY_THROW );
+ // TODO: in the future, the parent may be an excel::XChart (chart sheet) -> will there be a common base interface?
+ uno::Reference< excel::XWorksheet > xVbaSheet( xVbaHelper->getParent(), uno::UNO_QUERY_THROW );
+ // VBA sheet index is 1-based
+ return static_cast< SCTAB >( xVbaSheet->getIndex() - 1 );
+ }
+
+ // try single UNO range object
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable = getXSomethingFromArgs< sheet::XCellRangeAddressable >( rArgs, nIndex );
+ if( xCellRangeAddressable.is() )
+ return xCellRangeAddressable->getRangeAddress().Sheet;
+
+ // at last, try UNO range list
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
+ if( xRanges.is() )
+ {
+ uno::Sequence< table::CellRangeAddress > aRangeAddresses = xRanges->getRangeAddresses();
+ if( aRangeAddresses.hasElements() )
+ return aRangeAddresses[ 0 ].Sheet;
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+/** Returns the AWT container window of the passed controller. */
+uno::Reference< awt::XWindow > lclGetWindowForController( const uno::Reference< frame::XController >& rxController )
+{
+ if( rxController.is() ) try
+ {
+ uno::Reference< frame::XFrame > xFrame( rxController->getFrame(), uno::UNO_SET_THROW );
+ return xFrame->getContainerWindow();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return nullptr;
+}
+
+} // namespace
+
+// This class is to process Workbook window related event
+class ScVbaEventListener : public ::cppu::WeakImplHelper< awt::XTopWindowListener,
+ awt::XWindowListener,
+ frame::XBorderResizeListener,
+ util::XChangesListener >
+{
+public:
+ ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell );
+
+ /** Starts listening to the passed document controller. */
+ void startControllerListening( const uno::Reference< frame::XController >& rxController );
+ /** Stops listening to the passed document controller. */
+ void stopControllerListening( const uno::Reference< frame::XController >& rxController );
+
+ // XTopWindowListener
+ virtual void SAL_CALL windowOpened( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowClosing( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowClosed( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowMinimized( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowNormalized( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowActivated( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowDeactivated( const lang::EventObject& rEvent ) override;
+
+ // XWindowListener
+ virtual void SAL_CALL windowResized( const awt::WindowEvent& rEvent ) override;
+ virtual void SAL_CALL windowMoved( const awt::WindowEvent& rEvent ) override;
+ virtual void SAL_CALL windowShown( const lang::EventObject& rEvent ) override;
+ virtual void SAL_CALL windowHidden( const lang::EventObject& rEvent ) override;
+
+ // XBorderResizeListener
+ virtual void SAL_CALL borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& aNewSize ) override;
+
+ // XChangesListener
+ virtual void SAL_CALL changesOccurred( const util::ChangesEvent& rEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const lang::EventObject& rEvent ) override;
+
+private:
+ /** Starts listening to the document model. */
+ void startModelListening();
+ /** Stops listening to the document model. */
+ void stopModelListening();
+
+ /** Returns the controller for the passed VCL window. */
+ uno::Reference< frame::XController > getControllerForWindow( vcl::Window* pWindow ) const;
+
+ /** Calls the Workbook_Window[Activate|Deactivate] event handler. */
+ void processWindowActivateEvent( vcl::Window* pWindow, bool bActivate );
+ /** Posts a Workbook_WindowResize user event. */
+ void postWindowResizeEvent( vcl::Window* pWindow );
+ /** Callback link for Application::PostUserEvent(). */
+ DECL_LINK( processWindowResizeEvent, void*, void );
+
+private:
+ typedef ::std::map< VclPtr<vcl::Window>, uno::Reference< frame::XController > > WindowControllerMap;
+
+ ::osl::Mutex maMutex;
+ ScVbaEventsHelper& mrVbaEvents;
+ uno::Reference< frame::XModel > mxModel;
+ ScDocShell* mpDocShell;
+ WindowControllerMap maControllers; /// Maps VCL top windows to their controllers.
+ std::multiset< VclPtr<vcl::Window> > m_PostedWindows; /// Keeps processWindowResizeEvent windows from being deleted between postWindowResizeEvent and processWindowResizeEvent
+ VclPtr<vcl::Window> mpActiveWindow; /// Currently activated window, to prevent multiple (de)activation.
+ bool mbWindowResized; /// True = window resize system event processed.
+ bool mbBorderChanged; /// True = borders changed system event processed.
+ bool mbDisposed;
+};
+
+ScVbaEventListener::ScVbaEventListener( ScVbaEventsHelper& rVbaEvents, const uno::Reference< frame::XModel >& rxModel, ScDocShell* pDocShell ) :
+ mrVbaEvents( rVbaEvents ),
+ mxModel( rxModel ),
+ mpDocShell( pDocShell ),
+ mpActiveWindow( nullptr ),
+ mbWindowResized( false ),
+ mbBorderChanged( false ),
+ mbDisposed( !rxModel.is() )
+{
+ if( !mxModel.is() )
+ return;
+
+ startModelListening();
+ try
+ {
+ uno::Reference< frame::XController > xController( mxModel->getCurrentController(), uno::UNO_SET_THROW );
+ startControllerListening( xController );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ScVbaEventListener::startControllerListening( const uno::Reference< frame::XController >& rxController )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
+ if( xWindow.is() )
+ try { xWindow->addWindowListener( this ); } catch( uno::Exception& ) {}
+
+ uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
+ if( xTopWindow.is() )
+ try { xTopWindow->addTopWindowListener( this ); } catch( uno::Exception& ) {}
+
+ uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
+ if( xControllerBorder.is() )
+ try { xControllerBorder->addBorderResizeListener( this ); } catch( uno::Exception& ) {}
+
+ if( VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ) )
+ {
+ maControllers[ pWindow ] = rxController;
+ }
+}
+
+void ScVbaEventListener::stopControllerListening( const uno::Reference< frame::XController >& rxController )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( rxController );
+ if( xWindow.is() )
+ try { xWindow->removeWindowListener( this ); } catch( uno::Exception& ) {}
+
+ uno::Reference< awt::XTopWindow > xTopWindow( xWindow, uno::UNO_QUERY );
+ if( xTopWindow.is() )
+ try { xTopWindow->removeTopWindowListener( this ); } catch( uno::Exception& ) {}
+
+ uno::Reference< frame::XControllerBorder > xControllerBorder( rxController, uno::UNO_QUERY );
+ if( xControllerBorder.is() )
+ try { xControllerBorder->removeBorderResizeListener( this ); } catch( uno::Exception& ) {}
+
+ if( VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ) )
+ {
+ maControllers.erase( pWindow );
+ if( pWindow == mpActiveWindow )
+ mpActiveWindow = nullptr;
+ }
+}
+
+void SAL_CALL ScVbaEventListener::windowOpened( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowClosing( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowClosed( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowMinimized( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowNormalized( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowActivated( const lang::EventObject& rEvent )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if( mbDisposed )
+ return;
+
+ uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ // do not fire activation event multiple time for the same window
+ if( pWindow && (pWindow != mpActiveWindow) )
+ {
+ // if another window is active, fire deactivation event first
+ if( mpActiveWindow )
+ processWindowActivateEvent( mpActiveWindow, false );
+ // fire activation event for the new window
+ processWindowActivateEvent( pWindow, true );
+ mpActiveWindow = pWindow;
+ }
+}
+
+void SAL_CALL ScVbaEventListener::windowDeactivated( const lang::EventObject& rEvent )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if( !mbDisposed )
+ {
+ uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ // do not fire the deactivation event, if the window is not active (prevent multiple deactivation)
+ if( pWindow && (pWindow == mpActiveWindow) )
+ processWindowActivateEvent( pWindow, false );
+ // forget pointer to the active window
+ mpActiveWindow = nullptr;
+ }
+}
+
+void SAL_CALL ScVbaEventListener::windowResized( const awt::WindowEvent& rEvent )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ mbWindowResized = true;
+ if( !mbDisposed && mbBorderChanged )
+ {
+ uno::Reference< awt::XWindow > xWindow( rEvent.Source, uno::UNO_QUERY );
+ postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
+ }
+}
+
+void SAL_CALL ScVbaEventListener::windowMoved( const awt::WindowEvent& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowShown( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::windowHidden( const lang::EventObject& /*rEvent*/ )
+{
+}
+
+void SAL_CALL ScVbaEventListener::borderWidthsChanged( const uno::Reference< uno::XInterface >& rSource, const frame::BorderWidths& /*aNewSize*/ )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ mbBorderChanged = true;
+ if( !mbDisposed && mbWindowResized )
+ {
+ uno::Reference< frame::XController > xController( rSource, uno::UNO_QUERY );
+ uno::Reference< awt::XWindow > xWindow = lclGetWindowForController( xController );
+ postWindowResizeEvent( VCLUnoHelper::GetWindow( xWindow ) );
+ }
+}
+
+void SAL_CALL ScVbaEventListener::changesOccurred( const util::ChangesEvent& rEvent )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ sal_Int32 nCount = rEvent.Changes.getLength();
+ if( mbDisposed || !mpDocShell || (nCount == 0) )
+ return;
+
+ util::ElementChange aChange = rEvent.Changes[ 0 ];
+ OUString sOperation;
+ aChange.Accessor >>= sOperation;
+ if( !sOperation.equalsIgnoreAsciiCase("cell-change") )
+ return;
+
+ if( nCount == 1 )
+ {
+ uno::Reference< table::XCellRange > xRangeObj;
+ aChange.ReplacedElement >>= xRangeObj;
+ if( xRangeObj.is() )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xRangeObj) };
+ mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
+ }
+ return;
+ }
+
+ ScRangeList aRangeList;
+ for( const util::ElementChange& rChange : rEvent.Changes )
+ {
+ rChange.Accessor >>= sOperation;
+ uno::Reference< table::XCellRange > xRangeObj;
+ rChange.ReplacedElement >>= xRangeObj;
+ if( xRangeObj.is() && sOperation.equalsIgnoreAsciiCase("cell-change") )
+ {
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable( xRangeObj, uno::UNO_QUERY );
+ if( xCellRangeAddressable.is() )
+ {
+ ScRange aRange;
+ ScUnoConversion::FillScRange( aRange, xCellRangeAddressable->getRangeAddress() );
+ aRangeList.push_back( aRange );
+ }
+ }
+ }
+
+ if (!aRangeList.empty())
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( mpDocShell, aRangeList ) );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xRanges) };
+ mrVbaEvents.processVbaEventNoThrow( WORKSHEET_CHANGE, aArgs );
+ }
+}
+
+void SAL_CALL ScVbaEventListener::disposing( const lang::EventObject& rEvent )
+{
+ ::osl::MutexGuard aGuard( maMutex );
+
+ uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
+ if( xModel.is() )
+ {
+ OSL_ENSURE( xModel.get() == mxModel.get(), "ScVbaEventListener::disposing - disposing from unknown model" );
+ stopModelListening();
+ mbDisposed = true;
+ return;
+ }
+
+ uno::Reference< frame::XController > xController( rEvent.Source, uno::UNO_QUERY );
+ if( xController.is() )
+ {
+ stopControllerListening( xController );
+ return;
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void ScVbaEventListener::startModelListening()
+{
+ try
+ {
+ uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
+ xChangesNotifier->addChangesListener( this );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ScVbaEventListener::stopModelListening()
+{
+ try
+ {
+ uno::Reference< util::XChangesNotifier > xChangesNotifier( mxModel, uno::UNO_QUERY_THROW );
+ xChangesNotifier->removeChangesListener( this );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+uno::Reference< frame::XController > ScVbaEventListener::getControllerForWindow( vcl::Window* pWindow ) const
+{
+ WindowControllerMap::const_iterator aIt = maControllers.find( pWindow );
+ return (aIt == maControllers.end()) ? uno::Reference< frame::XController >() : aIt->second;
+}
+
+void ScVbaEventListener::processWindowActivateEvent( vcl::Window* pWindow, bool bActivate )
+{
+ uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
+ if( xController.is() )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
+ mrVbaEvents.processVbaEventNoThrow( bActivate ? WORKBOOK_WINDOWACTIVATE : WORKBOOK_WINDOWDEACTIVATE, aArgs );
+ }
+}
+
+void ScVbaEventListener::postWindowResizeEvent( vcl::Window* pWindow )
+{
+ // check that the passed window is still alive (it must be registered in maControllers)
+ if( pWindow && (maControllers.count( pWindow ) > 0) )
+ {
+ mbWindowResized = mbBorderChanged = false;
+ acquire(); // ensure we don't get deleted before the timer fires
+ m_PostedWindows.insert(pWindow);
+ Application::PostUserEvent( LINK( this, ScVbaEventListener, processWindowResizeEvent ), pWindow );
+ }
+}
+
+IMPL_LINK( ScVbaEventListener, processWindowResizeEvent, void*, p, void )
+{
+ vcl::Window* pWindow = static_cast<vcl::Window*>(p);
+ ::osl::MutexGuard aGuard( maMutex );
+
+ /* Check that the passed window is still alive (it must be registered in
+ maControllers). While closing a document, postWindowResizeEvent() may
+ be called on the last window which posts a user event via
+ Application::PostUserEvent to call this event handler. VCL will trigger
+ the handler some time later. Sometimes, the window gets deleted before.
+ This is handled via the disposing() function which removes the window
+ pointer from the member maControllers. Thus, checking whether
+ maControllers contains pWindow ensures that the window is still alive. */
+ if( !mbDisposed && pWindow && !pWindow->isDisposed() && (maControllers.count(pWindow) > 0) )
+ {
+ // do not fire event unless all mouse buttons have been released
+ vcl::Window::PointerState aPointerState = pWindow->GetPointerState();
+ if( (aPointerState.mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0 )
+ {
+ uno::Reference< frame::XController > xController = getControllerForWindow( pWindow );
+ if( xController.is() )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
+ // #163419# do not throw exceptions into application core
+ mrVbaEvents.processVbaEventNoThrow( WORKBOOK_WINDOWRESIZE, aArgs );
+ }
+ }
+ }
+ {
+ // note: there may be multiple processWindowResizeEvent outstanding
+ // for pWindow, so it may have been added to m_PostedWindows multiple
+ // times - so this must delete exactly one of these elements!
+ auto const iter(m_PostedWindows.find(pWindow));
+ assert(iter != m_PostedWindows.end());
+ m_PostedWindows.erase(iter);
+ }
+ release();
+}
+
+ScVbaEventsHelper::ScVbaEventsHelper( const uno::Sequence< uno::Any >& rArgs ) :
+ VbaEventsHelperBase( rArgs ),
+ mbOpened( false )
+{
+ mpDocShell = dynamic_cast< ScDocShell* >( mpShell ); // mpShell from base class
+ mpDoc = mpDocShell ? &mpDocShell->GetDocument() : nullptr;
+
+ if( !mxModel.is() || !mpDocShell || !mpDoc )
+ return;
+
+ // global
+ auto registerAutoEvent = [this](sal_Int32 nID, const char* sName)
+ { registerEventHandler(nID, script::ModuleType::NORMAL, OString(OString::Concat("Auto_") + sName).getStr(), -1, uno::Any(false)); };
+ registerAutoEvent(AUTO_OPEN, "Open");
+ registerAutoEvent(AUTO_CLOSE, "Close");
+
+ // Workbook
+ auto registerWorkbookEvent = [this](sal_Int32 nID, const char* sName, sal_Int32 nCancelIndex)
+ { registerEventHandler(nID, script::ModuleType::DOCUMENT, OString(OString::Concat("Workbook_") + sName).getStr(), nCancelIndex, uno::Any(false)); };
+ registerWorkbookEvent( WORKBOOK_ACTIVATE, "Activate", -1 );
+ registerWorkbookEvent( WORKBOOK_DEACTIVATE, "Deactivate", -1 );
+ registerWorkbookEvent( WORKBOOK_OPEN, "Open", -1 );
+ registerWorkbookEvent( WORKBOOK_BEFORECLOSE, "BeforeClose", 0 );
+ registerWorkbookEvent( WORKBOOK_BEFOREPRINT, "BeforePrint", 0 );
+ registerWorkbookEvent( WORKBOOK_BEFORESAVE, "BeforeSave", 1 );
+ registerWorkbookEvent( WORKBOOK_AFTERSAVE, "AfterSave", -1 );
+ registerWorkbookEvent( WORKBOOK_NEWSHEET, "NewSheet", -1 );
+ registerWorkbookEvent( WORKBOOK_WINDOWACTIVATE, "WindowActivate", -1 );
+ registerWorkbookEvent( WORKBOOK_WINDOWDEACTIVATE, "WindowDeactivate", -1 );
+ registerWorkbookEvent( WORKBOOK_WINDOWRESIZE, "WindowResize", -1 );
+
+ // Worksheet events. All events have a corresponding workbook event.
+ auto registerWorksheetEvent = [this](sal_Int32 nID, const char* sName, sal_Int32 nCancelIndex)
+ {
+ registerEventHandler(nID, script::ModuleType::DOCUMENT, OString(OString::Concat("Worksheet_") + sName).getStr(),
+ nCancelIndex, uno::Any(true));
+ registerEventHandler(USERDEFINED_START + nID, script::ModuleType::DOCUMENT,
+ OString(OString::Concat("Workbook_Worksheet") + sName).getStr(),
+ ((nCancelIndex >= 0) ? (nCancelIndex + 1) : -1), uno::Any(false));
+ };
+ registerWorksheetEvent( WORKSHEET_ACTIVATE, "Activate", -1 );
+ registerWorksheetEvent( WORKSHEET_DEACTIVATE, "Deactivate", -1 );
+ registerWorksheetEvent( WORKSHEET_BEFOREDOUBLECLICK, "BeforeDoubleClick", 1 );
+ registerWorksheetEvent( WORKSHEET_BEFORERIGHTCLICK, "BeforeRightClick", 1 );
+ registerWorksheetEvent( WORKSHEET_CALCULATE, "Calculate", -1 );
+ registerWorksheetEvent( WORKSHEET_CHANGE, "Change", -1 );
+ registerWorksheetEvent( WORKSHEET_SELECTIONCHANGE, "SelectionChange", -1 );
+ registerWorksheetEvent( WORKSHEET_FOLLOWHYPERLINK, "FollowHyperlink", -1 );
+}
+
+ScVbaEventsHelper::~ScVbaEventsHelper()
+{
+}
+
+void SAL_CALL ScVbaEventsHelper::notifyEvent( const css::document::EventObject& rEvent )
+{
+ static const uno::Sequence< uno::Any > saEmptyArgs;
+ if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::OPENDOC )) ||
+ (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CREATEDOC )) ) // CREATEDOC triggered e.g. during VBA Workbooks.Add
+ {
+ processVbaEventNoThrow( WORKBOOK_OPEN, saEmptyArgs );
+ }
+ else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::ACTIVATEDOC ) )
+ {
+ processVbaEventNoThrow( WORKBOOK_ACTIVATE, saEmptyArgs );
+ }
+ else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::DEACTIVATEDOC ) )
+ {
+ processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
+ }
+ else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCDONE )) ||
+ (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCDONE )) ||
+ (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCDONE )) )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(true) };
+ processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
+ }
+ else if( (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEDOCFAILED )) ||
+ (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVEASDOCFAILED )) ||
+ (rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::SAVETODOCFAILED )) )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(false) };
+ processVbaEventNoThrow( WORKBOOK_AFTERSAVE, aArgs );
+ }
+ else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) )
+ {
+ /* Trigger the WORKBOOK_WINDOWDEACTIVATE and WORKBOOK_DEACTIVATE
+ events and stop listening to the model (done in base class). */
+ uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
+ if( xController.is() )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xController) };
+ processVbaEventNoThrow( WORKBOOK_WINDOWDEACTIVATE, aArgs );
+ }
+ processVbaEventNoThrow( WORKBOOK_DEACTIVATE, saEmptyArgs );
+ }
+ else if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::VIEWCREATED ) )
+ {
+ uno::Reference< frame::XController > xController( mxModel->getCurrentController() );
+ if( mxListener && xController.is() )
+ mxListener->startControllerListening( xController );
+ }
+ VbaEventsHelperBase::notifyEvent( rEvent );
+}
+
+OUString ScVbaEventsHelper::getImplementationName()
+{
+ return "ScVbaEventsHelper";
+}
+
+css::uno::Sequence<OUString> ScVbaEventsHelper::getSupportedServiceNames()
+{
+ return {"com.sun.star.script.vba.VBASpreadsheetEventProcessor"};
+}
+
+// protected ------------------------------------------------------------------
+
+bool ScVbaEventsHelper::implPrepareEvent( EventQueue& rEventQueue,
+ const EventHandlerInfo& rInfo, const uno::Sequence< uno::Any >& rArgs )
+{
+ // document and document shell are needed during event processing
+ if( !mpShell || !mpDoc )
+ throw uno::RuntimeException();
+
+ /* For document events: check if events are enabled via the
+ Application.EnableEvents symbol (this is an Excel-only attribute).
+ Check this again for every event, as the event handler may change the
+ state of the EnableEvents symbol. Global events such as AUTO_OPEN and
+ AUTO_CLOSE are always enabled. */
+ bool bExecuteEvent = (rInfo.mnModuleType != script::ModuleType::DOCUMENT) || ScVbaApplication::getDocumentEventsEnabled();
+
+ // framework and Calc fire a few events before 'OnLoad', ignore them
+ if( bExecuteEvent )
+ bExecuteEvent = (rInfo.mnEventId == WORKBOOK_OPEN) ? !mbOpened : mbOpened;
+
+ // special handling for some events
+ if( bExecuteEvent ) switch( rInfo.mnEventId )
+ {
+ case WORKBOOK_OPEN:
+ {
+ // execute delayed Activate event too (see above)
+ rEventQueue.emplace_back(WORKBOOK_ACTIVATE );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(mxModel->getCurrentController()) };
+ rEventQueue.emplace_back( WORKBOOK_WINDOWACTIVATE, aArgs );
+ if (!hasModule("Auto_Open"))
+ rEventQueue.emplace_back(AUTO_OPEN );
+ // remember initial selection
+ maOldSelection <<= mxModel->getCurrentSelection();
+ }
+ break;
+ case WORKSHEET_SELECTIONCHANGE:
+ // if selection is not changed, then do not fire the event
+ bExecuteEvent = isSelectionChanged( rArgs, 0 );
+ break;
+ }
+
+ if( bExecuteEvent )
+ {
+ // add workbook event associated to a sheet event
+ bool bSheetEvent = false;
+ if( (rInfo.maUserData >>= bSheetEvent) && bSheetEvent )
+ rEventQueue.emplace_back( rInfo.mnEventId + USERDEFINED_START, rArgs );
+ }
+
+ return bExecuteEvent;
+}
+
+uno::Sequence< uno::Any > ScVbaEventsHelper::implBuildArgumentList( const EventHandlerInfo& rInfo,
+ const uno::Sequence< uno::Any >& rArgs )
+{
+ // fill arguments for workbook events associated to sheet events according to sheet events, sheet will be added below
+ bool bSheetEventAsBookEvent = rInfo.mnEventId > USERDEFINED_START;
+ sal_Int32 nEventId = bSheetEventAsBookEvent ? (rInfo.mnEventId - USERDEFINED_START) : rInfo.mnEventId;
+
+ uno::Sequence< uno::Any > aVbaArgs;
+ switch( nEventId )
+ {
+ // *** Workbook ***
+
+ // no arguments
+ case WORKBOOK_ACTIVATE:
+ case WORKBOOK_DEACTIVATE:
+ case WORKBOOK_OPEN:
+ break;
+ // 1 arg: cancel
+ case WORKBOOK_BEFORECLOSE:
+ case WORKBOOK_BEFOREPRINT:
+ aVbaArgs.realloc( 1 );
+ // current cancel state will be inserted by caller
+ break;
+ // 2 args: saveAs, cancel
+ case WORKBOOK_BEFORESAVE:
+ checkArgumentType< bool >( rArgs, 0 );
+ aVbaArgs = { rArgs[ 0 ], {} };
+ // current cancel state will be inserted by caller
+ break;
+ // 1 arg: success
+ case WORKBOOK_AFTERSAVE:
+ checkArgumentType< bool >( rArgs, 0 );
+ aVbaArgs = { rArgs[ 0 ] };
+ break;
+ // 1 arg: window
+ case WORKBOOK_WINDOWACTIVATE:
+ case WORKBOOK_WINDOWDEACTIVATE:
+ case WORKBOOK_WINDOWRESIZE:
+ aVbaArgs = { createWindow( rArgs, 0 ) };
+ break;
+ // 1 arg: worksheet
+ case WORKBOOK_NEWSHEET:
+ aVbaArgs = { createWorksheet( rArgs, 0 ) };
+ break;
+
+ // *** Worksheet ***
+
+ // no arguments
+ case WORKSHEET_ACTIVATE:
+ case WORKSHEET_CALCULATE:
+ case WORKSHEET_DEACTIVATE:
+ break;
+ // 1 arg: range
+ case WORKSHEET_CHANGE:
+ case WORKSHEET_SELECTIONCHANGE:
+ aVbaArgs = { createRange( rArgs, 0 ) };
+ break;
+ // 2 args: range, cancel
+ case WORKSHEET_BEFOREDOUBLECLICK:
+ case WORKSHEET_BEFORERIGHTCLICK:
+ aVbaArgs = { createRange( rArgs, 0 ), {} };
+ // current cancel state will be inserted by caller
+ break;
+ // 1 arg: hyperlink
+ case WORKSHEET_FOLLOWHYPERLINK:
+ aVbaArgs = { createHyperlink( rArgs, 0 ) };
+ break;
+ }
+
+ /* For workbook events associated to sheet events, the workbook event gets
+ the same arguments but with a Worksheet object in front of them. */
+ if( bSheetEventAsBookEvent )
+ {
+ sal_Int32 nLength = aVbaArgs.getLength();
+ uno::Sequence< uno::Any > aVbaArgs2( nLength + 1 );
+ auto pVbaArgs2 = aVbaArgs2.getArray();
+ *pVbaArgs2 = createWorksheet( rArgs, 0 );
+ std::copy_n(std::cbegin(aVbaArgs), nLength, std::next(pVbaArgs2));
+ aVbaArgs = aVbaArgs2;
+ }
+
+ return aVbaArgs;
+}
+
+void ScVbaEventsHelper::implPostProcessEvent( EventQueue& rEventQueue,
+ const EventHandlerInfo& rInfo, bool bCancel )
+{
+ switch( rInfo.mnEventId )
+ {
+ case WORKBOOK_OPEN:
+ mbOpened = true;
+ // register the listeners
+ if( !mxListener.is() )
+ mxListener = new ScVbaEventListener( *this, mxModel, mpDocShell );
+ break;
+ case WORKBOOK_BEFORECLOSE:
+ /* Execute Auto_Close only if not cancelled by event handler, but
+ before UI asks user whether to cancel closing the document. */
+ if (!bCancel && !hasModule("Auto_Close"))
+ rEventQueue.emplace_back(AUTO_CLOSE );
+ break;
+ }
+}
+
+OUString ScVbaEventsHelper::implGetDocumentModuleName( const EventHandlerInfo& rInfo,
+ const uno::Sequence< uno::Any >& rArgs ) const
+{
+ bool bSheetEvent = false;
+ rInfo.maUserData >>= bSheetEvent;
+ SCTAB nTab = bSheetEvent ? lclGetTabFromArgs( rArgs, 0 ) : -1;
+ if( bSheetEvent && (nTab < 0) )
+ throw lang::IllegalArgumentException();
+
+ OUString aCodeName;
+ if( bSheetEvent )
+ mpDoc->GetCodeName( nTab, aCodeName );
+ else
+ aCodeName = mpDoc->GetCodeName();
+ return aCodeName;
+}
+
+// private --------------------------------------------------------------------
+
+namespace {
+
+/** Compares the passed range lists representing sheet selections. Ignores
+ selections that refer to different sheets (returns false in this case). */
+bool lclSelectionChanged( const ScRangeList& rLeft, const ScRangeList& rRight )
+{
+ // one of the range lists empty? -> return false, if both lists empty
+ bool bLeftEmpty = rLeft.empty();
+ bool bRightEmpty = rRight.empty();
+ if( bLeftEmpty || bRightEmpty )
+ return !(bLeftEmpty && bRightEmpty);
+
+ // check sheet indexes of the range lists (assuming that all ranges in a list are on the same sheet)
+ if (rLeft[0].aStart.Tab() != rRight[0].aStart.Tab())
+ return false;
+
+ // compare all ranges
+ return rLeft != rRight;
+}
+
+} // namespace
+
+bool ScVbaEventsHelper::isSelectionChanged( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex )
+{
+ uno::Reference< uno::XInterface > xOldSelection( maOldSelection, uno::UNO_QUERY );
+ uno::Reference< uno::XInterface > xNewSelection = getXSomethingFromArgs< uno::XInterface >( rArgs, nIndex, false );
+ ScCellRangesBase* pOldCellRanges = dynamic_cast<ScCellRangesBase*>( xOldSelection.get() );
+ ScCellRangesBase* pNewCellRanges = dynamic_cast<ScCellRangesBase*>( xNewSelection.get() );
+ bool bChanged = !pOldCellRanges || !pNewCellRanges || lclSelectionChanged( pOldCellRanges->GetRangeList(), pNewCellRanges->GetRangeList() );
+ maOldSelection <<= xNewSelection;
+ return bChanged;
+}
+
+uno::Any ScVbaEventsHelper::createWorksheet( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
+{
+ // extract sheet index, will throw, if parameter is invalid
+ SCTAB nTab = lclGetTabFromArgs( rArgs, nIndex );
+ return uno::Any( excel::getUnoSheetModuleObj( mxModel, nTab ) );
+}
+
+uno::Any ScVbaEventsHelper::createRange( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
+{
+ // it is possible to pass an existing VBA Range object
+ uno::Reference< excel::XRange > xVbaRange = getXSomethingFromArgs< excel::XRange >( rArgs, nIndex );
+ if( !xVbaRange.is() )
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges = getXSomethingFromArgs< sheet::XSheetCellRangeContainer >( rArgs, nIndex );
+ uno::Reference< table::XCellRange > xRange = getXSomethingFromArgs< table::XCellRange >( rArgs, nIndex );
+ if ( !xRanges.is() && !xRange.is() )
+ throw lang::IllegalArgumentException();
+
+ uno::Sequence< uno::Any > aArgs;
+ if ( xRanges.is() )
+ {
+ aArgs = { uno::Any(excel::getUnoSheetModuleObj( xRanges )), uno::Any(xRanges) };
+ }
+ else
+ {
+ aArgs = { uno::Any(excel::getUnoSheetModuleObj( xRange )), uno::Any(xRange) };
+ }
+ xVbaRange.set( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Range", aArgs ), uno::UNO_QUERY_THROW );
+ }
+ return uno::Any( xVbaRange );
+}
+
+uno::Any ScVbaEventsHelper::createHyperlink( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
+{
+ uno::Reference< table::XCell > xCell = getXSomethingFromArgs< table::XCell >( rArgs, nIndex, false );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(excel::getUnoSheetModuleObj( xCell )),
+ uno::Any(xCell) };
+ uno::Reference< uno::XInterface > xHyperlink( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Hyperlink", aArgs ), uno::UNO_SET_THROW );
+ return uno::Any( xHyperlink );
+}
+
+uno::Any ScVbaEventsHelper::createWindow( const uno::Sequence< uno::Any >& rArgs, sal_Int32 nIndex ) const
+{
+ uno::Sequence< uno::Any > aArgs{ uno::Any(getVBADocument( mxModel )),
+ uno::Any(mxModel),
+ uno::Any(getXSomethingFromArgs< frame::XController >( rArgs, nIndex, false )) };
+ uno::Reference< uno::XInterface > xWindow( createVBAUnoAPIServiceWithArgs( mpShell, "ooo.vba.excel.Window", aArgs ), uno::UNO_SET_THROW );
+ return uno::Any( xWindow );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+ScVbaEventsHelper_get_implementation(
+ css::uno::XComponentContext * /*context*/,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new ScVbaEventsHelper(arguments));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaeventshelper.hxx b/sc/source/ui/vba/vbaeventshelper.hxx
new file mode 100644
index 0000000000..e5bf11ecb3
--- /dev/null
+++ b/sc/source/ui/vba/vbaeventshelper.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 .
+ */
+
+#pragma once
+
+#include <rtl/ref.hxx>
+#include <vbahelper/vbaeventshelperbase.hxx>
+
+class ScDocShell;
+class ScDocument;
+class ScVbaEventListener;
+
+class ScVbaEventsHelper : public VbaEventsHelperBase
+{
+public:
+ ScVbaEventsHelper( const css::uno::Sequence< css::uno::Any >& rArgs );
+ virtual ~ScVbaEventsHelper() override;
+
+ virtual void SAL_CALL notifyEvent( const css::document::EventObject& rEvent ) override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+protected:
+ virtual bool implPrepareEvent( EventQueue& rEventQueue, const EventHandlerInfo& rInfo, const css::uno::Sequence< css::uno::Any >& rArgs ) override;
+ virtual css::uno::Sequence< css::uno::Any > implBuildArgumentList( const EventHandlerInfo& rInfo, const css::uno::Sequence< css::uno::Any >& rArgs ) override;
+ virtual void implPostProcessEvent( EventQueue& rEventQueue, const EventHandlerInfo& rInfo, bool bCancel ) override;
+ virtual OUString implGetDocumentModuleName( const EventHandlerInfo& rInfo, const css::uno::Sequence< css::uno::Any >& rArgs ) const override;
+
+private:
+ /** Checks if selection has been changed compared to selection of last call.
+ @return true, if the selection has been changed.
+ @throws css::lang::IllegalArgumentException
+ @throws css::uno::RuntimeException
+ */
+ bool isSelectionChanged( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex );
+
+ /** Creates a VBA Worksheet object (the argument must contain a sheet index).
+ @throws css::lang::IllegalArgumentException
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Any createWorksheet( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex ) const;
+ /** Creates a VBA Range object (the argument must contain a UNO range or UNO range list).
+ @throws css::lang::IllegalArgumentException
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Any createRange( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex ) const;
+ /** Creates a VBA Hyperlink object (the argument must contain a UNO cell).
+ @throws css::lang::IllegalArgumentException
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Any createHyperlink( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex ) const;
+ /** Creates a VBA Window object (the argument must contain a model controller).
+ @throws css::lang::IllegalArgumentException
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Any createWindow( const css::uno::Sequence< css::uno::Any >& rArgs, sal_Int32 nIndex ) const;
+
+private:
+ ::rtl::Reference< ScVbaEventListener > mxListener;
+ css::uno::Any maOldSelection;
+ ScDocShell* mpDocShell;
+ ScDocument* mpDoc;
+ bool mbOpened;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbafiledialog.cxx b/sc/source/ui/vba/vbafiledialog.cxx
new file mode 100644
index 0000000000..f7f31db2a5
--- /dev/null
+++ b/sc/source/ui/vba/vbafiledialog.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 "vbafiledialog.hxx"
+#include "vbafiledialogitems.hxx"
+
+#include <osl/file.hxx>
+
+#include <ooo/vba/office/MsoFileDialogType.hpp>
+
+#include <com/sun/star/ui/dialogs/FilePicker.hpp>
+#include <com/sun/star/ui/dialogs/FolderPicker.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+ScVbaFileDialog::ScVbaFileDialog( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const sal_Int32 nType )
+ : ScVbaFileDialog_BASE( xParent, xContext)
+ , m_nType(nType)
+ , m_sTitle("FileDialog")
+ , m_bMultiSelectMode(false)
+{}
+
+uno::Any
+ScVbaFileDialog::getInitialFileName() { return uno::Any( m_sInitialFileName ); }
+
+void ScVbaFileDialog::setInitialFileName( const css::uno::Any& rName )
+{
+ OUString sDefaultPath;
+
+ if( rName >>= sDefaultPath )
+ {
+ OUString sDefaultURL;
+ sal_Int32 eSuccess = osl::FileBase::getFileURLFromSystemPath(
+ sDefaultPath, sDefaultURL ) ;
+ if( eSuccess == osl::FileBase::RC::E_INVAL )
+ m_sInitialFileName = sDefaultPath; // the user may gave it in URL form
+ else
+ m_sInitialFileName = sDefaultURL;
+ }
+}
+
+css::uno::Any ScVbaFileDialog::getTitle() { return uno::Any( m_sTitle ); }
+
+void ScVbaFileDialog::setTitle( const css::uno::Any& rTitle )
+{
+ rTitle >>= m_sTitle;
+}
+
+uno::Any ScVbaFileDialog::getAllowMultiSelect()
+{
+ return uno::Any(m_bMultiSelectMode);
+}
+
+void ScVbaFileDialog::setAllowMultiSelect(const uno::Any& rAllowMultiSelect)
+{
+ rAllowMultiSelect >>= m_bMultiSelectMode;
+}
+
+uno::Reference< excel::XFileDialogSelectedItems > SAL_CALL ScVbaFileDialog::getSelectedItems()
+{
+ // TODO use InitialFileName when m_xItems is empty
+ return m_xItems;
+}
+
+sal_Int32 ScVbaFileDialog::Show()
+{
+ std::vector<OUString> sSelectedPaths;
+ sal_Int32 nRet = -1;
+
+ switch (m_nType)
+ {
+ case office::MsoFileDialogType::msoFileDialogOpen:
+ // TODO implement
+ break;
+ case office::MsoFileDialogType::msoFileDialogSaveAs:
+ // TODO implement
+ break;
+ case office::MsoFileDialogType::msoFileDialogFilePicker:
+ {
+ uno::Reference<ui::dialogs::XFilePicker3> xFilePicker =
+ ui::dialogs::FilePicker::createWithMode(
+ mxContext, ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE );
+
+ if( !m_sInitialFileName.isEmpty() )
+ xFilePicker->setDisplayDirectory( m_sInitialFileName );
+
+ if( xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK )
+ {
+ nRet = 0; // cancel pressed
+ break;
+ }
+
+ const uno::Sequence<OUString> aSelectedFiles = xFilePicker->getSelectedFiles();
+ for( const auto& sURL : aSelectedFiles )
+ {
+ OUString sPath;
+ osl::FileBase::getSystemPathFromFileURL(sURL, sPath);
+
+ sSelectedPaths.push_back(sPath);
+ }
+ }
+ break;
+ case office::MsoFileDialogType::msoFileDialogFolderPicker:
+ {
+ uno::Reference< ui::dialogs::XFolderPicker2 > xFolderPicker =
+ ui::dialogs::FolderPicker::create(mxContext);
+
+ if( !m_sInitialFileName.isEmpty() )
+ xFolderPicker->setDisplayDirectory( m_sInitialFileName );
+
+ if( xFolderPicker->execute() != ui::dialogs::ExecutableDialogResults::OK )
+ {
+ nRet = 0; // cancel pressed
+ break;
+ }
+
+ OUString sURL = xFolderPicker->getDirectory();
+
+ if(!sURL.isEmpty())
+ {
+ OUString sPath;
+ osl::FileBase::getSystemPathFromFileURL(sURL, sPath);
+
+ sSelectedPaths.push_back(sPath);
+ }
+
+ }
+ break;
+ default:
+ throw uno::RuntimeException();
+ }
+
+ m_xItems = css::uno::Reference< ov::excel::XFileDialogSelectedItems >(
+ new ScVbaFileDialogSelectedItems(this, mxContext, std::move(sSelectedPaths)) );
+ return nRet;
+}
+
+// XHelperInterface
+OUString
+ScVbaFileDialog::getServiceImplName()
+{
+ return "ScVbaFileDialog";
+}
+
+uno::Sequence<OUString>
+ScVbaFileDialog::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.FileDialog"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbafiledialog.hxx b/sc/source/ui/vba/vbafiledialog.hxx
new file mode 100644
index 0000000000..f339e4a977
--- /dev/null
+++ b/sc/source/ui/vba/vbafiledialog.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XFileDialog.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+namespace ooo::vba { class XHelperInterface; }
+namespace ooo::vba::excel { class XFileDialogSelectedItems; }
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XFileDialog > ScVbaFileDialog_BASE;
+
+class ScVbaFileDialog : public ScVbaFileDialog_BASE
+{
+private:
+ sal_Int32 m_nType;
+ OUString m_sTitle;
+ OUString m_sInitialFileName;
+ bool m_bMultiSelectMode;
+ css::uno::Reference< ov::excel::XFileDialogSelectedItems> m_xItems;
+public:
+ ScVbaFileDialog( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const sal_Int32 nType);
+
+ virtual css::uno::Any SAL_CALL getInitialFileName() override;
+ virtual void SAL_CALL setInitialFileName( const css::uno::Any& rName ) override;
+ virtual css::uno::Any SAL_CALL getTitle() override;
+ virtual void SAL_CALL setTitle( const css::uno::Any& rTitle ) override;
+ virtual css::uno::Any SAL_CALL getAllowMultiSelect() override;
+ virtual void SAL_CALL setAllowMultiSelect(const css::uno::Any& rAllowMultiSelect) override;
+
+ virtual css::uno::Reference< ov::excel::XFileDialogSelectedItems > SAL_CALL getSelectedItems() override;
+
+ virtual sal_Int32 SAL_CALL Show() override;
+
+ //XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbafiledialogitems.cxx b/sc/source/ui/vba/vbafiledialogitems.cxx
new file mode 100644
index 0000000000..d34ace3c55
--- /dev/null
+++ b/sc/source/ui/vba/vbafiledialogitems.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 <o3tl/safeint.hxx>
+
+#include "vbafiledialogitems.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+namespace {
+
+class FileDialogItemEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration >
+{
+ std::vector< OUString > m_sItems;
+ std::vector< OUString >::iterator mIt;
+public:
+ explicit FileDialogItemEnumeration( std::vector< OUString >&& rVector ) : m_sItems( std::move(rVector) ), mIt( m_sItems.begin() ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements() override
+ {
+ return ( mIt != m_sItems.end() );
+ }
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ if( !hasMoreElements() )
+ throw container::NoSuchElementException();
+ OUString sPath = *mIt++;
+ return uno::Any( sPath );
+ }
+};
+
+}
+
+ScVbaFileDialogSelectedItems::ScVbaFileDialogSelectedItems(
+ const css::uno::Reference< ov::XHelperInterface >& xParent
+ ,const css::uno::Reference< css::uno::XComponentContext >& xContext
+ ,std::vector< OUString >&& rItems)
+ : FileDialogSelectedItems_BASE( xParent, xContext, uno::Reference< container::XIndexAccess>() )
+ , m_sItems(std::move(rItems)) {}
+
+
+// XEnumerationAccess
+uno::Type SAL_CALL
+ScVbaFileDialogSelectedItems::getElementType()
+{
+ return cppu::UnoType<OUString>::get();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaFileDialogSelectedItems::createEnumeration()
+{
+ return uno::Reference< container::XEnumeration >( new FileDialogItemEnumeration( std::vector(m_sItems) ) );
+}
+
+uno::Any
+ScVbaFileDialogSelectedItems::createCollectionObject( const uno::Any& aSource )
+{
+ sal_Int32 nPosition = -1;
+ if (!(aSource >>= nPosition))
+ throw uno::RuntimeException("not an sal_Int32");
+ if (nPosition < 0 || o3tl::make_unsigned(nPosition) >= m_sItems.size())
+ throw uno::RuntimeException("out of range");
+
+ OUString sPath = m_sItems[nPosition];
+ return uno::Any( sPath );
+}
+
+// Methods
+uno::Any SAL_CALL
+ScVbaFileDialogSelectedItems::Item( const uno::Any& aIndex, const uno::Any& /*aIndex*/ )
+{
+ sal_Int32 nPosition = -1;
+ aIndex >>= nPosition;
+
+ --nPosition; // vba indexing starts with 1
+
+ if( nPosition < 0 || nPosition >= getCount() )
+ {
+ throw uno::RuntimeException();
+ }
+
+ return createCollectionObject( uno::Any( nPosition ) );
+}
+
+sal_Int32 ScVbaFileDialogSelectedItems::getCount()
+{
+ return m_sItems.size();
+}
+
+// XHelperInterface
+OUString
+ScVbaFileDialogSelectedItems::getServiceImplName()
+{
+ return "ScVbaFileDialogSelectedItems";
+}
+
+uno::Sequence<OUString>
+ScVbaFileDialogSelectedItems::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.FileDialogSelectedItems"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbafiledialogitems.hxx b/sc/source/ui/vba/vbafiledialogitems.hxx
new file mode 100644
index 0000000000..5a01df0102
--- /dev/null
+++ b/sc/source/ui/vba/vbafiledialogitems.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/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XFileDialogSelectedItems.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+typedef CollTestImplHelper< ov::excel::XFileDialogSelectedItems > FileDialogSelectedItems_BASE;
+
+class ScVbaFileDialogSelectedItems final : public FileDialogSelectedItems_BASE
+{
+ const std::vector<OUString> m_sItems;
+public:
+ std::vector<OUString> const& getItems()
+ {
+ return m_sItems;
+ }
+
+ ScVbaFileDialogSelectedItems( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ std::vector<OUString>&& sItems);
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ // Methods
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index, const css::uno::Any& /*Index2*/ ) override;
+ virtual sal_Int32 SAL_CALL getCount() override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbafont.cxx b/sc/source/ui/vba/vbafont.cxx
new file mode 100644
index 0000000000..db49f01349
--- /dev/null
+++ b/sc/source/ui/vba/vbafont.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 .
+ */
+
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <ooo/vba/excel/XlColorIndex.hpp>
+#include <ooo/vba/excel/XlUnderlineStyle.hpp>
+#include <svl/itemset.hxx>
+#include <o3tl/string_view.hxx>
+#include "excelvbahelper.hxx"
+#include "vbafont.hxx"
+#include "vbapalette.hxx"
+#include <scitems.hxx>
+#include <cellsuno.hxx>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaFont::ScVbaFont(
+ const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const ScVbaPalette& dPalette,
+ const uno::Reference< beans::XPropertySet >& xPropertySet,
+ ScCellRangeObj* pRangeObj, bool bFormControl ) :
+ ScVbaFont_BASE( xParent, xContext, dPalette.getPalette(), xPropertySet, Component::EXCEL, bFormControl ),
+ mpRangeObj( pRangeObj )
+{
+}
+
+SfxItemSet*
+ScVbaFont::GetDataSet()
+{
+ return mpRangeObj ? excel::ScVbaCellRangeAccess::GetDataSet( mpRangeObj ) : nullptr;
+}
+
+ScVbaFont::~ScVbaFont()
+{
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getSize()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_HEIGHT) == SfxItemState::DONTCARE )
+ return aNULL();
+ return ScVbaFont_BASE::getSize();
+}
+
+void SAL_CALL
+ScVbaFont::setColorIndex( const uno::Any& _colorindex )
+{
+ if(mbFormControl)
+ return;
+
+ sal_Int32 nIndex = 0;
+ _colorindex >>= nIndex;
+ // #FIXME xlColorIndexAutomatic & xlColorIndexNone are not really
+ // handled properly here
+
+ if ( !nIndex || ( nIndex == excel::XlColorIndex::xlColorIndexAutomatic ) )
+ {
+ nIndex = 1; // check default ( assume black )
+ ScVbaFont_BASE::setColorIndex( uno::Any( nIndex ) );
+ }
+ else
+ ScVbaFont_BASE::setColorIndex( _colorindex );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getColorIndex()
+{
+ if(mbFormControl)
+ return uno::Any( sal_Int32(0) );
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_COLOR) == SfxItemState::DONTCARE )
+ return aNULL();
+ return ScVbaFont_BASE::getColorIndex();
+}
+
+void SAL_CALL
+ScVbaFont::setStandardFontSize( const uno::Any& /*aValue*/ )
+{
+//XXX #TODO# #FIXME#
+ //mxFont->setPropertyValue("CharSize", ( uno::Any )fValue );
+ throw uno::RuntimeException(
+ "setStandardFontSize not supported" );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getStandardFontSize()
+{
+//XXX #TODO# #FIXME#
+ throw uno::RuntimeException( "getStandardFontSize not supported" );
+ // return uno::Any();
+}
+
+void SAL_CALL
+ScVbaFont::setStandardFont( const uno::Any& /*aValue*/ )
+{
+//XXX #TODO# #FIXME#
+ throw uno::RuntimeException("setStandardFont not supported" );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getStandardFont()
+{
+//XXX #TODO# #FIXME#
+ throw uno::RuntimeException("getStandardFont not supported");
+ // return uno::Any();
+}
+
+void SAL_CALL
+ScVbaFont::setFontStyle( const uno::Any& aValue )
+{
+ bool bBold = false;
+ bool bItalic = false;
+
+ OUString aStyles;
+ aValue >>= aStyles;
+
+ for (sal_Int32 nIdx{ 0 }; nIdx>=0; )
+ {
+ const std::u16string_view aToken{ o3tl::getToken(aStyles, 0, ' ', nIdx ) };
+ if (o3tl::equalsIgnoreAsciiCase(aToken, u"Bold"))
+ {
+ bBold = true;
+ if (bItalic)
+ break;
+ }
+ else if (o3tl::equalsIgnoreAsciiCase(aToken, u"Italic"))
+ {
+ bItalic = true;
+ if (bBold)
+ break;
+ }
+ }
+
+ setBold( uno::Any( bBold ) );
+ setItalic( uno::Any( bItalic ) );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getFontStyle()
+{
+ OUStringBuffer aStyles;
+ bool bValue = false;
+ getBold() >>= bValue;
+ if( bValue )
+ aStyles.append("Bold");
+
+ getItalic() >>= bValue;
+ if( bValue )
+ {
+ if( !aStyles.isEmpty() )
+ aStyles.append(" ");
+ aStyles.append("Italic");
+ }
+ return uno::Any( aStyles.makeStringAndClear() );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getBold()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_WEIGHT) == SfxItemState::DONTCARE )
+ return aNULL();
+ return ScVbaFont_BASE::getBold();
+}
+
+void SAL_CALL
+ScVbaFont::setUnderline( const uno::Any& aValue )
+{
+ if(mbFormControl)
+ return;
+
+ // default
+ sal_Int32 nValue = excel::XlUnderlineStyle::xlUnderlineStyleNone;
+ aValue >>= nValue;
+ switch ( nValue )
+ {
+// NOTE:: #TODO #FIMXE
+// xlUnderlineStyleDoubleAccounting & xlUnderlineStyleSingleAccounting
+// don't seem to be supported in Openoffice.
+// The import filter converts them to single or double underlines as appropriate
+// So, here at the moment we are similarly silently converting
+// xlUnderlineStyleSingleAccounting to xlUnderlineStyleSingle.
+
+ case excel::XlUnderlineStyle::xlUnderlineStyleNone:
+ nValue = awt::FontUnderline::NONE;
+ break;
+ case excel::XlUnderlineStyle::xlUnderlineStyleSingle:
+ case excel::XlUnderlineStyle::xlUnderlineStyleSingleAccounting:
+ nValue = awt::FontUnderline::SINGLE;
+ break;
+ case excel::XlUnderlineStyle::xlUnderlineStyleDouble:
+ case excel::XlUnderlineStyle::xlUnderlineStyleDoubleAccounting:
+ nValue = awt::FontUnderline::DOUBLE;
+ break;
+ default:
+ throw uno::RuntimeException("Unknown value for Underline" );
+ }
+
+ mxFont->setPropertyValue("CharUnderline", uno::Any(nValue) );
+
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getUnderline()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_UNDERLINE) == SfxItemState::DONTCARE )
+ return aNULL();
+
+ sal_Int32 nValue = awt::FontUnderline::NONE;
+
+ if(mbFormControl)
+ return uno::Any( nValue );
+
+ mxFont->getPropertyValue("CharUnderline") >>= nValue;
+ switch ( nValue )
+ {
+ case awt::FontUnderline::DOUBLE:
+ nValue = excel::XlUnderlineStyle::xlUnderlineStyleDouble;
+ break;
+ case awt::FontUnderline::SINGLE:
+ nValue = excel::XlUnderlineStyle::xlUnderlineStyleSingle;
+ break;
+ case awt::FontUnderline::NONE:
+ nValue = excel::XlUnderlineStyle::xlUnderlineStyleNone;
+ break;
+ default:
+ throw uno::RuntimeException("Unknown value retrieved for Underline" );
+
+ }
+ return uno::Any( nValue );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getStrikethrough()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_CROSSEDOUT) == SfxItemState::DONTCARE )
+ return aNULL();
+ return ScVbaFont_BASE::getStrikethrough();
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getShadow()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_SHADOWED) == SfxItemState::DONTCARE )
+ return aNULL();
+ return ScVbaFont_BASE::getShadow();
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getItalic()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_POSTURE) == SfxItemState::DONTCARE )
+ return aNULL();
+
+ return ScVbaFont_BASE::getItalic();
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getName()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT) == SfxItemState::DONTCARE )
+ return aNULL();
+ return ScVbaFont_BASE::getName();
+}
+uno::Any
+ScVbaFont::getColor()
+{
+ // #TODO #FIXME - behave like getXXX above ( wrt. GetDataSet )
+ uno::Any aAny = OORGBToXLRGB( mxFont->getPropertyValue("CharColor") );
+ return aAny;
+}
+
+void SAL_CALL
+ScVbaFont::setOutlineFont( const uno::Any& aValue )
+{
+ if(!mbFormControl)
+ mxFont->setPropertyValue("CharContoured", aValue );
+}
+
+uno::Any SAL_CALL
+ScVbaFont::getOutlineFont()
+{
+ if ( GetDataSet() )
+ if ( GetDataSet()->GetItemState( ATTR_FONT_CONTOUR) == SfxItemState::DONTCARE )
+ return aNULL();
+ return mbFormControl ? uno::Any( false ) : mxFont->getPropertyValue("CharContoured");
+}
+
+OUString
+ScVbaFont::getServiceImplName()
+{
+ return "ScVbaFont";
+}
+
+uno::Sequence< OUString >
+ScVbaFont::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Font"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbafont.hxx b/sc/source/ui/vba/vbafont.hxx
new file mode 100644
index 0000000000..d6a01b9a05
--- /dev/null
+++ b/sc/source/ui/vba/vbafont.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 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+
+#include <ooo/vba/excel/XFont.hpp>
+#include <vbahelper/vbafontbase.hxx>
+
+namespace com::sun::star::beans
+{
+class XPropertySet;
+}
+
+class ScCellRangeObj;
+class SfxItemSet;
+class ScVbaPalette;
+
+typedef cppu::ImplInheritanceHelper<VbaFontBase, ov::excel::XFont> ScVbaFont_BASE;
+
+class ScVbaFont : public ScVbaFont_BASE
+{
+ ScCellRangeObj* mpRangeObj;
+ SfxItemSet* GetDataSet();
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaFont(const css::uno::Reference<ov::XHelperInterface>& xParent,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const ScVbaPalette& dPalette,
+ const css::uno::Reference<css::beans::XPropertySet>& xPropertySet,
+ ScCellRangeObj* pRangeObj = nullptr, bool bFormControl = false);
+ virtual ~ScVbaFont() override; // {}
+
+ // Attributes
+ virtual css::uno::Any SAL_CALL getSize() override;
+ virtual css::uno::Any SAL_CALL getStandardFontSize() override;
+ virtual void SAL_CALL setStandardFontSize(const css::uno::Any& _standardfontsize) override;
+ virtual css::uno::Any SAL_CALL getStandardFont() override;
+ virtual void SAL_CALL setStandardFont(const css::uno::Any& _standardfont) override;
+ virtual css::uno::Any SAL_CALL getFontStyle() override;
+ virtual void SAL_CALL setFontStyle(const css::uno::Any& _fontstyle) override;
+ virtual css::uno::Any SAL_CALL getColorIndex() override;
+ virtual void SAL_CALL setColorIndex(const css::uno::Any& _colorindex) override;
+ virtual css::uno::Any SAL_CALL getBold() override;
+ virtual css::uno::Any SAL_CALL getUnderline() override;
+ virtual void SAL_CALL setUnderline(const css::uno::Any& _underline) override;
+ virtual css::uno::Any SAL_CALL getStrikethrough() override;
+ virtual css::uno::Any SAL_CALL getShadow() override;
+ virtual css::uno::Any SAL_CALL getItalic() override;
+ virtual css::uno::Any SAL_CALL getName() override;
+ virtual css::uno::Any SAL_CALL getColor() override;
+ virtual css::uno::Any SAL_CALL getOutlineFont() override;
+ virtual void SAL_CALL setOutlineFont(const css::uno::Any& _outlinefont) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaformat.cxx b/sc/source/ui/vba/vbaformat.cxx
new file mode 100644
index 0000000000..0ae7b298ab
--- /dev/null
+++ b/sc/source/ui/vba/vbaformat.cxx
@@ -0,0 +1,815 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbaformat.hxx"
+#include <ooo/vba/excel/XFont.hpp>
+#include <ooo/vba/excel/XStyle.hpp>
+#include <ooo/vba/excel/XlVAlign.hpp>
+#include <ooo/vba/excel/XlHAlign.hpp>
+#include <ooo/vba/excel/XlOrientation.hpp>
+#include <ooo/vba/excel/Constants.hpp>
+#include <ooo/vba/excel/XRange.hpp>
+#include <com/sun/star/table/CellVertJustify2.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <basic/sberrors.hxx>
+#include <rtl/math.hxx>
+
+#include "excelvbahelper.hxx"
+#include "vbaborders.hxx"
+#include "vbapalette.hxx"
+#include "vbafont.hxx"
+#include "vbainterior.hxx"
+
+#include <docsh.hxx>
+#include <unonames.hxx>
+#include <cellsuno.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+#include <utility>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+constexpr OUString FORMATSTRING = u"FormatString"_ustr;
+constexpr OUString LOCALE = u"Locale"_ustr;
+
+template< typename... Ifc >
+ScVbaFormat< Ifc... >::ScVbaFormat( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ uno::Reference< beans::XPropertySet > _xPropertySet,
+ uno::Reference< frame::XModel > xModel,
+ bool bCheckAmbiguoity )
+ : ScVbaFormat_BASE( xParent, xContext ),
+ m_aDefaultLocale( "en", "US", OUString() ),
+ mxPropertySet(std::move( _xPropertySet )),
+ mxModel(std::move( xModel )),
+ mbCheckAmbiguoity( bCheckAmbiguoity ),
+ mbAddIndent( false )
+{
+ try
+ {
+ if ( !mxModel.is() )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, u"XModel Interface could not be retrieved" );
+ // mxServiceInfo is unused,
+ // mxNumberFormatsSupplier is initialized when needed in initializeNumberFormats.
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setVerticalAlignment( const uno::Any& _oAlignment)
+{
+ try
+ {
+ uno::Any aVal;
+ sal_Int32 nAlignment = 0;
+ if ( !(_oAlignment >>= nAlignment ))
+ throw uno::RuntimeException();
+ switch (nAlignment)
+ {
+ case excel::XlVAlign::xlVAlignBottom :
+ aVal <<= table::CellVertJustify2::BOTTOM;
+ break;
+ case excel::XlVAlign::xlVAlignCenter :
+ aVal <<= table::CellVertJustify2::CENTER;
+ break;
+ case excel::XlVAlign::xlVAlignDistributed:
+ case excel::XlVAlign::xlVAlignJustify:
+ aVal <<= table::CellVertJustify2::STANDARD;
+ break;
+
+ case excel::XlVAlign::xlVAlignTop:
+ aVal <<= table::CellVertJustify2::TOP;
+ break;
+ default:
+ aVal <<= table::CellVertJustify2::STANDARD;
+ break;
+ }
+ mxPropertySet->setPropertyValue( SC_UNONAME_CELLVJUS, aVal );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getVerticalAlignment( )
+{
+ uno::Any aResult = aNULL();
+ try
+ {
+ if (!isAmbiguous( SC_UNONAME_CELLVJUS ) )
+ {
+ sal_Int32 aAPIAlignment = table::CellVertJustify2::STANDARD;
+ mxPropertySet->getPropertyValue( SC_UNONAME_CELLVJUS ) >>= aAPIAlignment;
+ switch( aAPIAlignment )
+ {
+ case table::CellVertJustify2::BOTTOM:
+ aResult <<= excel::XlVAlign::xlVAlignBottom;
+ break;
+ case table::CellVertJustify2::CENTER:
+ aResult <<= excel::XlVAlign::xlVAlignCenter;
+ break;
+ case table::CellVertJustify2::STANDARD:
+ aResult <<= excel::XlVAlign::xlVAlignBottom;
+ break;
+ case table::CellVertJustify2::TOP:
+ aResult <<= excel::XlVAlign::xlVAlignTop;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return aResult;
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setHorizontalAlignment( const uno::Any& HorizontalAlignment )
+{
+ try
+ {
+ uno::Any aVal;
+ sal_Int32 nAlignment = 0;
+ if ( !( HorizontalAlignment >>= nAlignment ) )
+ throw uno::RuntimeException();
+ switch ( nAlignment )
+ {
+ case excel::XlHAlign::xlHAlignJustify:
+ aVal <<= table::CellHoriJustify_BLOCK;
+ break;
+ case excel::XlHAlign::xlHAlignCenter:
+ aVal <<= table::CellHoriJustify_CENTER;
+ break;
+ case excel::XlHAlign::xlHAlignDistributed:
+ aVal <<= table::CellHoriJustify_BLOCK;
+ break;
+ case excel::XlHAlign::xlHAlignLeft:
+ aVal <<= table::CellHoriJustify_LEFT;
+ break;
+ case excel::XlHAlign::xlHAlignRight:
+ aVal <<= table::CellHoriJustify_RIGHT;
+ break;
+ }
+ // #FIXME what about the default case above?
+ // shouldn't need the test below
+ if ( aVal.hasValue() )
+ mxPropertySet->setPropertyValue( SC_UNONAME_CELLHJUS, aVal );
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getHorizontalAlignment( )
+{
+ uno::Any NRetAlignment = aNULL();
+ try
+ {
+ OUString sHoriJust( SC_UNONAME_CELLHJUS );
+ if (!isAmbiguous(sHoriJust))
+ {
+ table::CellHoriJustify aAPIAlignment = table::CellHoriJustify_BLOCK;
+
+ if ( mxPropertySet->getPropertyValue(sHoriJust) >>= aAPIAlignment )
+ {
+ switch( aAPIAlignment )
+ {
+ case table::CellHoriJustify_BLOCK:
+ NRetAlignment <<= excel::XlHAlign::xlHAlignJustify;
+ break;
+ case table::CellHoriJustify_CENTER:
+ NRetAlignment <<= excel::XlHAlign::xlHAlignCenter;
+ break;
+ case table::CellHoriJustify_LEFT:
+ NRetAlignment <<= excel::XlHAlign::xlHAlignLeft;
+ break;
+ case table::CellHoriJustify_RIGHT:
+ NRetAlignment <<= excel::XlHAlign::xlHAlignRight;
+ break;
+ default: // handle those other cases with a NULL return
+ break;
+ }
+ }
+ }
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return NRetAlignment;
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setOrientation( const uno::Any& _aOrientation )
+{
+ try
+ {
+ sal_Int32 nOrientation = 0;
+ if ( !( _aOrientation >>= nOrientation ) )
+ throw uno::RuntimeException();
+ uno::Any aVal;
+ switch( nOrientation )
+ {
+ case excel::XlOrientation::xlDownward:
+ aVal <<= table::CellOrientation_TOPBOTTOM;
+ break;
+ case excel::XlOrientation::xlHorizontal:
+ aVal <<= table::CellOrientation_STANDARD;
+ mxPropertySet->setPropertyValue( SC_UNONAME_ROTANG, uno::Any( sal_Int32(0) ) );
+ break;
+ case excel::XlOrientation::xlUpward:
+ aVal <<= table::CellOrientation_BOTTOMTOP;
+ break;
+ case excel::XlOrientation::xlVertical:
+ aVal <<= table::CellOrientation_STACKED;
+ break;
+ }
+ // #FIXME what about the default case above?
+ // shouldn't need the test below
+ if ( aVal.hasValue() )
+ mxPropertySet->setPropertyValue( SC_UNONAME_CELLORI, aVal );
+
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getOrientation( )
+{
+ uno::Any NRetOrientation = aNULL();
+ try
+ {
+ if (!isAmbiguous(SC_UNONAME_CELLORI))
+ {
+ table::CellOrientation aOrientation = table::CellOrientation_STANDARD;
+ if ( !( mxPropertySet->getPropertyValue( SC_UNONAME_CELLORI ) >>= aOrientation ) )
+ throw uno::RuntimeException();
+
+ switch(aOrientation)
+ {
+ case table::CellOrientation_STANDARD:
+ NRetOrientation <<= excel::XlOrientation::xlHorizontal;
+ break;
+ case table::CellOrientation_BOTTOMTOP:
+ NRetOrientation <<= excel::XlOrientation::xlUpward;
+ break;
+ case table::CellOrientation_TOPBOTTOM:
+ NRetOrientation <<= excel::XlOrientation::xlDownward;
+ break;
+ case table::CellOrientation_STACKED:
+ NRetOrientation <<= excel::XlOrientation::xlVertical;
+ break;
+ default:
+ NRetOrientation <<= excel::XlOrientation::xlHorizontal;
+ }
+ }
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return NRetOrientation;
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setWrapText( const uno::Any& _aWrapText )
+{
+ try
+ {
+ mxPropertySet->setPropertyValue( SC_UNONAME_WRAP, _aWrapText);
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getWrapText( )
+{
+ uno::Any aWrap = aNULL();
+ try
+ {
+ OUString aPropName( SC_UNONAME_WRAP );
+ if (!isAmbiguous( aPropName ))
+ {
+ aWrap = mxPropertySet->getPropertyValue(aPropName);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return aWrap;
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::Borders( const uno::Any& Index )
+{
+ ScVbaPalette aPalette( excel::getDocShell( mxModel ) );
+ uno::Reference< XCollection > xColl = new ScVbaBorders( thisHelperIface(), ScVbaFormat_BASE::mxContext, uno::Reference< table::XCellRange >( mxPropertySet, uno::UNO_QUERY_THROW ), aPalette );
+
+ if ( Index.hasValue() )
+ {
+ return xColl->Item( Index, uno::Any() );
+ }
+ return uno::Any( xColl );
+}
+
+template< typename... Ifc >
+uno::Reference< excel::XFont > SAL_CALL
+ScVbaFormat< Ifc... >::Font( )
+{
+ ScVbaPalette aPalette( excel::getDocShell( mxModel ) );
+ return new ScVbaFont( thisHelperIface(), ScVbaFormat_BASE::mxContext, aPalette, mxPropertySet );
+}
+
+template< typename... Ifc >
+uno::Reference< excel::XInterior > SAL_CALL
+ScVbaFormat< Ifc... >::Interior( )
+{
+ return new ScVbaInterior( thisHelperIface(), ScVbaFormat_BASE::mxContext, mxPropertySet );
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getNumberFormatLocal( )
+{
+ uno::Any aRet{ OUString() };
+ try
+ {
+ OUString sPropName( SC_UNO_DP_NUMBERFO );
+ if (!isAmbiguous( sPropName ))
+ {
+
+ initializeNumberFormats();
+
+ sal_Int32 nFormat = 0;
+ if ( ! (mxPropertySet->getPropertyValue( sPropName ) >>= nFormat ) )
+ throw uno::RuntimeException();
+
+ OUString sFormat;
+ xNumberFormats->getByKey(nFormat)->getPropertyValue( FORMATSTRING ) >>= sFormat;
+ aRet <<= sFormat.toAsciiLowerCase();
+
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return aRet;
+
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setNumberFormatLocal( const uno::Any& _oLocalFormatString )
+{
+ try
+ {
+ OUString sLocalFormatString;
+ sal_Int32 nFormat = -1;
+ OUString sNumFormat( SC_UNO_DP_NUMBERFO );
+ if ( !(_oLocalFormatString >>= sLocalFormatString )
+ || !( mxPropertySet->getPropertyValue(sNumFormat) >>= nFormat ) )
+ throw uno::RuntimeException();
+
+ sLocalFormatString = sLocalFormatString.toAsciiUpperCase();
+ initializeNumberFormats();
+ lang::Locale aRangeLocale;
+ xNumberFormats->getByKey(nFormat)->getPropertyValue( LOCALE ) >>= aRangeLocale;
+ sal_Int32 nNewFormat = xNumberFormats->queryKey(sLocalFormatString, aRangeLocale, true);
+
+ if (nNewFormat == -1)
+ nNewFormat = xNumberFormats->addNew(sLocalFormatString, aRangeLocale);
+ mxPropertySet->setPropertyValue(sNumFormat, uno::Any( nNewFormat ));
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setNumberFormat( const uno::Any& _oFormatString )
+{
+ try
+ {
+ OUString sFormatString;
+ if ( !( _oFormatString >>= sFormatString ) )
+ throw uno::RuntimeException();
+
+ sFormatString = sFormatString.toAsciiUpperCase();
+
+ lang::Locale aDefaultLocale = m_aDefaultLocale;
+ initializeNumberFormats();
+ sal_Int32 nFormat = xNumberFormats->queryKey(sFormatString, aDefaultLocale, true);
+
+ if (nFormat == -1)
+ nFormat = xNumberFormats->addNew(sFormatString, aDefaultLocale);
+
+ lang::Locale aRangeLocale;
+ xNumberFormats->getByKey(nFormat)->getPropertyValue( LOCALE ) >>= aRangeLocale;
+ sal_Int32 nNewFormat = xNumberFormatTypes->getFormatForLocale(nFormat, aRangeLocale);
+ mxPropertySet->setPropertyValue( SC_UNO_DP_NUMBERFO, uno::Any( nNewFormat));
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setIndentLevel( const uno::Any& _aLevel )
+{
+ try
+ {
+ sal_Int32 nLevel = 0;
+ if ( !(_aLevel >>= nLevel ) )
+ throw uno::RuntimeException();
+ table::CellHoriJustify aAPIAlignment = table::CellHoriJustify_STANDARD;
+
+ OUString sHoriJust( SC_UNONAME_CELLHJUS );
+ if ( !( mxPropertySet->getPropertyValue(sHoriJust) >>= aAPIAlignment ) )
+ throw uno::RuntimeException();
+ if (aAPIAlignment == table::CellHoriJustify_STANDARD)
+ mxPropertySet->setPropertyValue( sHoriJust, uno::Any( table::CellHoriJustify_LEFT) ) ;
+ mxPropertySet->setPropertyValue( SC_UNONAME_PINDENT, uno::Any( sal_Int16(nLevel * 352.8) ) );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getIndentLevel( )
+{
+ uno::Any NRetIndentLevel = aNULL();
+ try
+ {
+ OUString sParaIndent( SC_UNONAME_PINDENT );
+ if (!isAmbiguous(sParaIndent))
+ {
+ sal_Int16 IndentLevel = 0;
+ if ( mxPropertySet->getPropertyValue(sParaIndent) >>= IndentLevel )
+ NRetIndentLevel <<= sal_Int32( rtl::math::round(static_cast<double>( IndentLevel ) / 352.8));
+ else
+ NRetIndentLevel <<= sal_Int32(0);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return NRetIndentLevel;
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setLocked( const uno::Any& _aLocked )
+{
+ try
+ {
+ bool bIsLocked = false;
+ if ( !( _aLocked >>= bIsLocked ) )
+ throw uno::RuntimeException();
+ util::CellProtection aCellProtection;
+ OUString sCellProt( SC_UNONAME_CELLPRO );
+ mxPropertySet->getPropertyValue(sCellProt) >>= aCellProtection;
+ aCellProtection.IsLocked = bIsLocked;
+ mxPropertySet->setPropertyValue(sCellProt, uno::Any( aCellProtection ) );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setFormulaHidden( const uno::Any& FormulaHidden )
+{
+ try
+ {
+ bool bIsFormulaHidden = false;
+ FormulaHidden >>= bIsFormulaHidden;
+ util::CellProtection aCellProtection;
+ OUString sCellProt( SC_UNONAME_CELLPRO );
+ mxPropertySet->getPropertyValue(sCellProt) >>= aCellProtection;
+ aCellProtection.IsFormulaHidden = bIsFormulaHidden;
+ mxPropertySet->setPropertyValue(sCellProt,uno::Any(aCellProtection));
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception( ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getLocked( )
+{
+ uno::Any aCellProtection = aNULL();
+ try
+ {
+ OUString sCellProt( SC_UNONAME_CELLPRO );
+
+ if (!isAmbiguous(sCellProt))
+ {
+ SfxItemSet* pDataSet = getCurrentDataSet();
+ if ( pDataSet )
+ {
+ const ScProtectionAttr& rProtAttr = pDataSet->Get(ATTR_PROTECTION);
+ SfxItemState eState = pDataSet->GetItemState(ATTR_PROTECTION);
+ if(eState != SfxItemState::DONTCARE)
+ aCellProtection <<= rProtAttr.GetProtection();
+ }
+ else // fallback to propertyset
+ {
+ util::CellProtection cellProtection;
+ mxPropertySet->getPropertyValue(sCellProt) >>= cellProtection;
+ aCellProtection <<= cellProtection.IsLocked;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return aCellProtection;
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getFormulaHidden( )
+{
+ uno::Any aBoolRet = aNULL();
+ try
+ {
+ OUString sCellProt( SC_UNONAME_CELLPRO );
+ if (!isAmbiguous(sCellProt))
+ {
+ SfxItemSet* pDataSet = getCurrentDataSet();
+ if ( pDataSet )
+ {
+ const ScProtectionAttr& rProtAttr = pDataSet->Get(ATTR_PROTECTION);
+ SfxItemState eState = pDataSet->GetItemState(ATTR_PROTECTION);
+ if(eState != SfxItemState::DONTCARE)
+ aBoolRet <<= rProtAttr.GetHideFormula();
+ }
+ else
+ {
+ util::CellProtection aCellProtection;
+ mxPropertySet->getPropertyValue(sCellProt) >>= aCellProtection;
+ aBoolRet <<= aCellProtection.IsFormulaHidden;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return aBoolRet;
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setShrinkToFit( const uno::Any& ShrinkToFit )
+{
+ try
+ {
+ mxPropertySet->setPropertyValue( SC_UNONAME_SHRINK_TO_FIT, ShrinkToFit);
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {} );
+ }
+
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getShrinkToFit( )
+{
+ uno::Any aRet = aNULL();
+ try
+ {
+ OUString sShrinkToFit( SC_UNONAME_SHRINK_TO_FIT );
+ if (!isAmbiguous(sShrinkToFit))
+ aRet = mxPropertySet->getPropertyValue(sShrinkToFit);
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ }
+ return aRet;
+}
+
+template< typename... Ifc >
+void SAL_CALL
+ScVbaFormat< Ifc... >::setReadingOrder( const uno::Any& ReadingOrder )
+{
+ try
+ {
+ sal_Int32 nReadingOrder = 0;
+ if ( !(ReadingOrder >>= nReadingOrder ))
+ throw uno::RuntimeException();
+ uno::Any aVal = aNULL();
+ switch(nReadingOrder)
+ {
+ case excel::Constants::xlLTR:
+ aVal <<= sal_Int16(text::WritingMode_LR_TB);
+ break;
+ case excel::Constants::xlRTL:
+ aVal <<= sal_Int16(text::WritingMode_RL_TB);
+ break;
+ case excel::Constants::xlContext:
+ // TODO implement xlContext
+ // Reading order has to depend on the language of the first letter
+ // written.
+ aVal <<= sal_Int16(text::WritingMode_LR_TB);
+ break;
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ mxPropertySet->setPropertyValue( SC_UNONAME_WRITING, aVal );
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getReadingOrder( )
+{
+ uno::Any NRetReadingOrder = aNULL();
+ try
+ {
+ OUString sWritingMode( SC_UNONAME_WRITING );
+ if (!isAmbiguous(sWritingMode))
+ {
+ text::WritingMode aWritingMode = text::WritingMode_LR_TB;
+ if ( ( mxPropertySet->getPropertyValue(sWritingMode) ) >>= aWritingMode )
+ switch (aWritingMode)
+ {
+ case text::WritingMode_LR_TB:
+ NRetReadingOrder <<= excel::Constants::xlLTR;
+ break;
+ case text::WritingMode_RL_TB:
+ NRetReadingOrder <<= excel::Constants::xlRTL;
+ break;
+ default:
+ NRetReadingOrder <<= excel::Constants::xlRTL;
+ }
+ }
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ }
+ return NRetReadingOrder;
+
+}
+
+template< typename... Ifc >
+uno::Any SAL_CALL
+ScVbaFormat< Ifc... >::getNumberFormat( )
+{
+ uno::Any aFormat = aNULL();
+ try
+ {
+ sal_Int32 nFormat = -1;
+ OUString sNumFormat( SC_UNO_DP_NUMBERFO );
+ if (!isAmbiguous(sNumFormat) &&
+ ( mxPropertySet->getPropertyValue(sNumFormat) >>= nFormat) )
+ {
+ initializeNumberFormats();
+
+ sal_Int32 nNewFormat = xNumberFormatTypes->getFormatForLocale(nFormat, m_aDefaultLocale );
+ OUString sFormat;
+ xNumberFormats->getByKey(nNewFormat)->getPropertyValue( FORMATSTRING ) >>= sFormat;
+ aFormat <<= sFormat;
+ }
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return aFormat;
+}
+
+template< typename... Ifc >
+bool
+ScVbaFormat< Ifc... >::isAmbiguous(const OUString& _sPropertyName)
+{
+ bool bResult = false;
+ try
+ {
+ if (mbCheckAmbiguoity)
+ bResult = ( getXPropertyState()->getPropertyState(_sPropertyName) == beans::PropertyState_AMBIGUOUS_VALUE );
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return bResult;
+}
+
+template< typename... Ifc >
+void
+ScVbaFormat< Ifc... >::initializeNumberFormats()
+{
+ if ( !xNumberFormats.is() )
+ {
+ mxNumberFormatsSupplier.set( mxModel, uno::UNO_QUERY_THROW );
+ xNumberFormats = mxNumberFormatsSupplier->getNumberFormats();
+ xNumberFormatTypes.set( xNumberFormats, uno::UNO_QUERY ); // _THROW?
+ }
+}
+
+template< typename... Ifc >
+uno::Reference< beans::XPropertyState > const &
+ScVbaFormat< Ifc... >::getXPropertyState()
+{
+ if ( !xPropertyState.is() )
+ xPropertyState.set( mxPropertySet, uno::UNO_QUERY_THROW );
+ return xPropertyState;
+}
+
+template< typename... Ifc >
+ScCellRangesBase*
+ScVbaFormat< Ifc... >::getCellRangesBase()
+{
+ return dynamic_cast<ScCellRangesBase*>( mxPropertySet.get() );
+}
+
+template< typename... Ifc >
+SfxItemSet*
+ScVbaFormat< Ifc... >::getCurrentDataSet()
+{
+ SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( getCellRangesBase() );
+ if ( !pDataSet )
+ throw uno::RuntimeException("Can't access Itemset for XPropertySet" );
+ return pDataSet;
+}
+
+template class ScVbaFormat< excel::XStyle >;
+template class ScVbaFormat< excel::XRange >;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaformat.hxx b/sc/source/ui/vba/vbaformat.hxx
new file mode 100644
index 0000000000..a85818ddd2
--- /dev/null
+++ b/sc/source/ui/vba/vbaformat.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 .
+ */
+#pragma once
+
+#include <com/sun/star/lang/Locale.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::beans { class XPropertyState; }
+namespace com::sun::star::frame { class XModel; }
+namespace com::sun::star::util { class XNumberFormats; }
+namespace com::sun::star::util { class XNumberFormatsSupplier; }
+namespace com::sun::star::util { class XNumberFormatTypes; }
+namespace ooo::vba::excel { class XFont; }
+namespace ooo::vba::excel { class XInterior; }
+
+class ScCellRangesBase;
+class SfxItemSet;
+
+template< typename... Ifc >
+class ScVbaFormat : public InheritedHelperInterfaceWeakImpl< Ifc... >
+{
+typedef InheritedHelperInterfaceWeakImpl< Ifc... > ScVbaFormat_BASE;
+ css::lang::Locale m_aDefaultLocale;
+protected:
+ css::uno::Reference< css::beans::XPropertySet > mxPropertySet;
+ css::uno::Reference< css::util::XNumberFormatsSupplier > mxNumberFormatsSupplier;
+ css::uno::Reference< css::util::XNumberFormats > xNumberFormats;
+ css::uno::Reference< css::util::XNumberFormatTypes > xNumberFormatTypes;
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::beans::XPropertyState > xPropertyState;
+ bool mbCheckAmbiguoity;
+ bool mbAddIndent;
+ /// @throws css::script::BasicErrorException
+ bool isAmbiguous(const OUString& _sPropertyName);
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::beans::XPropertyState > const & getXPropertyState();
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ void initializeNumberFormats();
+ /// @throws css::uno::RuntimeException
+ SfxItemSet* getCurrentDataSet( );
+protected:
+ /// @throws css::uno::RuntimeException
+ virtual ScCellRangesBase* getCellRangesBase();
+public:
+ /// @throws css::script::BasicErrorException
+ ScVbaFormat( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, css::uno::Reference< css::beans::XPropertySet > _xPropertySet, css::uno::Reference< css::frame::XModel > xModel, bool bCheckAmbiguoity );
+ virtual css::uno::Reference< ov::XHelperInterface > thisHelperIface() = 0;
+ /// @throws css::uno::RuntimeException
+ void SAL_CALL setAddIndent( const css::uno::Any& BAddIndent) { BAddIndent >>= mbAddIndent; }
+ /// @throws css::uno::RuntimeException
+ css::uno::Any SAL_CALL getAddIndent() { return css::uno::Any( mbAddIndent ); }
+ // Interface Methods
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL Borders( const css::uno::Any& Index );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Reference< ::ooo::vba::excel::XFont > SAL_CALL Font( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Reference< ::ooo::vba::excel::XInterior > SAL_CALL Interior( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setNumberFormat( const css::uno::Any& NumberFormat );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getNumberFormat( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setNumberFormatLocal( const css::uno::Any& NumberFormatLocal );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getNumberFormatLocal( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setIndentLevel( const css::uno::Any& IndentLevel );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getIndentLevel( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setHorizontalAlignment( const css::uno::Any& HorizontalAlignment );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getHorizontalAlignment( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setVerticalAlignment( const css::uno::Any& VerticalAlignment );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getVerticalAlignment( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setOrientation( const css::uno::Any& Orientation );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getOrientation( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setShrinkToFit( const css::uno::Any& ShrinkToFit );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getShrinkToFit( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setWrapText( const css::uno::Any& WrapText );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getWrapText( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setLocked( const css::uno::Any& Locked );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getLocked( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setFormulaHidden( const css::uno::Any& FormulaHidden );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getFormulaHidden( );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setMergeCells( const css::uno::Any& MergeCells ) = 0;
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getMergeCells( ) = 0;
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual void SAL_CALL setReadingOrder( const css::uno::Any& ReadingOrder );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Any SAL_CALL getReadingOrder( );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaformatcondition.cxx b/sc/source/ui/vba/vbaformatcondition.cxx
new file mode 100644
index 0000000000..8c2b80238a
--- /dev/null
+++ b/sc/source/ui/vba/vbaformatcondition.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 "vbaformatcondition.hxx"
+#include "vbaformatconditions.hxx"
+#include <unonames.hxx>
+#include <ooo/vba/excel/XlFormatConditionType.hpp>
+#include <basic/sberrors.hxx>
+#include <utility>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+/// @throws css::script::BasicErrorException
+static ScVbaFormatConditions*
+lcl_getScVbaFormatConditionsPtr( const uno::Reference< excel::XFormatConditions >& xFormatConditions )
+{
+ ScVbaFormatConditions* pFormatConditions = static_cast< ScVbaFormatConditions* >( xFormatConditions.get() );
+ if ( !pFormatConditions )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ return pFormatConditions;
+}
+
+ScVbaFormatCondition::ScVbaFormatCondition( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< sheet::XSheetConditionalEntry >& _xSheetConditionalEntry,
+ uno::Reference< excel::XStyle > _xStyle,
+ uno::Reference< excel::XFormatConditions > _xFormatConditions,
+ uno::Reference< css::beans::XPropertySet > _xPropertySet )
+ : ScVbaFormatCondition_BASE( xParent, xContext,
+ uno::Reference< sheet::XSheetCondition >( _xSheetConditionalEntry, css::uno::UNO_QUERY_THROW ) ),
+ moFormatConditions(std::move( _xFormatConditions )), mxStyle(std::move( _xStyle )), mxParentRangePropertySet(std::move( _xPropertySet ))
+{
+ mxSheetConditionalEntries = lcl_getScVbaFormatConditionsPtr( moFormatConditions )->getSheetConditionalEntries();
+
+ msStyleName = mxStyle->getName();
+}
+
+void SAL_CALL
+ScVbaFormatCondition::Delete( )
+{
+ ScVbaFormatConditions* pFormatConditions = lcl_getScVbaFormatConditionsPtr( moFormatConditions );
+ pFormatConditions->removeFormatCondition(msStyleName, true);
+ notifyRange();
+}
+
+void SAL_CALL
+ScVbaFormatCondition::Modify( ::sal_Int32 _nType, const uno::Any& _aOperator, const uno::Any& _aFormula1, const uno::Any& _aFormula2 )
+{
+ try
+ {
+ ScVbaFormatConditions* pFormatConditions = lcl_getScVbaFormatConditionsPtr( moFormatConditions );
+ pFormatConditions->removeFormatCondition(msStyleName, false);
+ pFormatConditions->Add(_nType, _aOperator, _aFormula1, _aFormula2, mxStyle);
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+uno::Reference< excel::XInterior > SAL_CALL
+ScVbaFormatCondition::Interior( )
+{
+ return mxStyle->Interior();
+}
+
+uno::Reference< excel::XFont > SAL_CALL
+ScVbaFormatCondition::Font( )
+{
+ return mxStyle->Font();
+}
+uno::Any SAL_CALL
+ScVbaFormatCondition::Borders( const uno::Any& Index )
+{ return mxStyle->Borders( Index );
+}
+
+sheet::ConditionOperator
+ScVbaFormatCondition::retrieveAPIType(sal_Int32 _nVBAType, const uno::Reference< sheet::XSheetCondition >& _xSheetCondition )
+{
+ sheet::ConditionOperator aAPIType = sheet::ConditionOperator_NONE;
+ switch (_nVBAType)
+ {
+ case excel::XlFormatConditionType::xlExpression:
+ aAPIType = sheet::ConditionOperator_FORMULA;
+ break;
+ case excel::XlFormatConditionType::xlCellValue:
+ if ( _xSheetCondition.is() && (_xSheetCondition->getOperator() == sheet::ConditionOperator_FORMULA ) )
+ aAPIType = sheet::ConditionOperator_NONE;
+ break;
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return aAPIType;
+}
+
+::sal_Int32 SAL_CALL
+ScVbaFormatCondition::Type( )
+{
+ sal_Int32 nReturnType = 0;
+ if ( mxSheetCondition->getOperator() == sheet::ConditionOperator_FORMULA)
+ nReturnType = excel::XlFormatConditionType::xlExpression;
+ else
+ nReturnType = excel::XlFormatConditionType::xlCellValue;
+ return nReturnType;
+}
+
+::sal_Int32 SAL_CALL
+ScVbaFormatCondition::Operator( )
+{
+ return ScVbaFormatCondition_BASE::Operator( true );
+}
+
+void
+ScVbaFormatCondition::notifyRange()
+{
+ try
+ {
+ mxParentRangePropertySet->setPropertyValue(SC_UNONAME_CONDFMT, uno::Any( mxSheetConditionalEntries));
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+}
+
+OUString
+ScVbaFormatCondition::getServiceImplName()
+{
+ return "ScVbaFormatCondition";
+}
+
+uno::Sequence< OUString >
+ScVbaFormatCondition::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.FormatCondition"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaformatcondition.hxx b/sc/source/ui/vba/vbaformatcondition.hxx
new file mode 100644
index 0000000000..dfa02dcb58
--- /dev/null
+++ b/sc/source/ui/vba/vbaformatcondition.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include <ooo/vba/excel/XFormatCondition.hpp>
+#include <ooo/vba/excel/XFormatConditions.hpp>
+#include <ooo/vba/excel/XStyle.hpp>
+#include <com/sun/star/sheet/XSheetConditionalEntries.hpp>
+#include <com/sun/star/sheet/XSheetConditionalEntry.hpp>
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include "vbacondition.hxx"
+
+typedef ScVbaCondition< ov::excel::XFormatCondition > ScVbaFormatCondition_BASE;
+class ScVbaFormatCondition final : public ScVbaFormatCondition_BASE
+{
+ OUString msStyleName;
+ css::uno::Reference< css::sheet::XSheetConditionalEntries > mxSheetConditionalEntries;
+ css::uno::Reference< ov::excel::XFormatConditions> moFormatConditions;
+ css::uno::Reference< ov::excel::XStyle > mxStyle;
+ css::uno::Reference< css::beans::XPropertySet > mxParentRangePropertySet;
+
+public:
+ /// @throws css::uno::RuntimeException
+ /// @throws css::script::BasicErrorException
+ ScVbaFormatCondition( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const css::uno::Reference< css::sheet::XSheetConditionalEntry >& _xSheetConditionalEntry,
+ css::uno::Reference< ov::excel::XStyle > ,
+ css::uno::Reference< ov::excel::XFormatConditions > _xFormatConditions,
+ css::uno::Reference< css::beans::XPropertySet > _xPropertySet );
+
+ /// @throws css::script::BasicErrorException
+ void notifyRange();
+ /// @throws css::script::BasicErrorException
+ static css::sheet::ConditionOperator retrieveAPIType(sal_Int32 _nVBAType, const css::uno::Reference< css::sheet::XSheetCondition >& _xSheetCondition );
+
+ //Methods
+ virtual void SAL_CALL Delete( ) override;
+ virtual void SAL_CALL Modify( ::sal_Int32 Type, const css::uno::Any& Operator, const css::uno::Any& Formula1, const css::uno::Any& Formula2 ) override;
+ virtual ::sal_Int32 SAL_CALL Type( ) override;
+ using ScVbaFormatCondition_BASE::Operator;
+ virtual ::sal_Int32 SAL_CALL Operator( ) override;
+ virtual css::uno::Reference< ::ooo::vba::excel::XInterior > SAL_CALL Interior( ) override;
+ virtual css::uno::Any SAL_CALL Borders( const css::uno::Any& Index ) override;
+ virtual css::uno::Reference< ::ooo::vba::excel::XFont > SAL_CALL Font( ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaformatconditions.cxx b/sc/source/ui/vba/vbaformatconditions.cxx
new file mode 100644
index 0000000000..a6c3ae4125
--- /dev/null
+++ b/sc/source/ui/vba/vbaformatconditions.cxx
@@ -0,0 +1,290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ooo/vba/excel/XRange.hpp>
+#include <com/sun/star/sheet/XSheetConditionalEntry.hpp>
+#include <basic/sberrors.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <utility>
+#include <vector>
+#include <unonames.hxx>
+#include "vbaformatconditions.hxx"
+#include "vbaformatcondition.hxx"
+#include "vbastyles.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+void SAL_CALL
+ScVbaFormatConditions::Delete( )
+{
+ try
+ {
+ ScVbaStyles* pStyles = mxStyles.get();
+ if ( !pStyles )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ sal_Int32 nCount = mxSheetConditionalEntries->getCount();
+ for (sal_Int32 i = nCount - 1; i >= 0; i--)
+ {
+ uno::Reference< sheet::XSheetConditionalEntry > xSheetConditionalEntry( mxSheetConditionalEntries->getByIndex(i), uno::UNO_QUERY_THROW );
+ pStyles->Delete(xSheetConditionalEntry->getStyleName());
+ mxSheetConditionalEntries->removeByIndex(i);
+ }
+ notifyRange();
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+uno::Type SAL_CALL
+ScVbaFormatConditions::getElementType()
+{
+ return cppu::UnoType<excel::XFormatCondition>::get();
+}
+
+static uno::Any xSheetConditionToFormatCondition( const uno::Reference< XHelperInterface >& xRangeParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< excel::XStyles >& xStyles, const uno::Reference< excel::XFormatConditions >& xFormatConditions, const uno::Reference< beans::XPropertySet >& xRangeProps, const uno::Any& aObject )
+{
+ uno::Reference< sheet::XSheetConditionalEntry > xSheetConditionalEntry;
+ aObject >>= xSheetConditionalEntry;
+
+ uno::Reference< excel::XStyle > xStyle( xStyles->Item( uno::Any( xSheetConditionalEntry->getStyleName() ), uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XFormatCondition > xCondition = new ScVbaFormatCondition( xRangeParent, xContext, xSheetConditionalEntry, xStyle, xFormatConditions, xRangeProps );
+ return uno::Any( xCondition );
+}
+
+uno::Any
+ScVbaFormatConditions::createCollectionObject(const uno::Any& aObject )
+{
+ return xSheetConditionToFormatCondition( uno::Reference< XHelperInterface >( mxRangeParent, uno::UNO_QUERY_THROW ), mxContext, mxStyles, this, mxParentRangePropertySet, aObject );
+}
+
+namespace {
+
+class EnumWrapper : public EnumerationHelper_BASE
+{
+ uno::Reference<container::XIndexAccess > m_xIndexAccess;
+ uno::Reference<excel::XRange > m_xParentRange;
+ uno::Reference<uno::XComponentContext > m_xContext;
+ uno::Reference<excel::XStyles > m_xStyles;
+ uno::Reference<excel::XFormatConditions > m_xParentCollection;
+ uno::Reference<beans::XPropertySet > m_xProps;
+
+ sal_Int32 nIndex;
+public:
+ EnumWrapper( uno::Reference< container::XIndexAccess > xIndexAccess, uno::Reference<excel::XRange > xRange, uno::Reference<uno::XComponentContext > xContext, uno::Reference<excel::XStyles > xStyles, uno::Reference< excel::XFormatConditions > xCollection, uno::Reference<beans::XPropertySet > xProps ) : m_xIndexAccess(std::move( xIndexAccess )), m_xParentRange(std::move( xRange )), m_xContext(std::move( xContext )), m_xStyles(std::move( xStyles )), m_xParentCollection(std::move( xCollection )), m_xProps(std::move( xProps )), nIndex( 0 ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( nIndex < m_xIndexAccess->getCount() );
+ }
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ try
+ {
+ if ( nIndex < m_xIndexAccess->getCount() )
+ return xSheetConditionToFormatCondition( uno::Reference< XHelperInterface >( m_xParentRange, uno::UNO_QUERY_THROW ), m_xContext, m_xStyles, m_xParentCollection, m_xProps, m_xIndexAccess->getByIndex( nIndex++ ) );
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ throw;
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception& e)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::lang::WrappedTargetException(
+ "wrapped Exception " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), a);
+ }
+ throw container::NoSuchElementException();
+ }
+};
+
+}
+
+uno::Reference< excel::XFormatCondition > SAL_CALL
+ScVbaFormatConditions::Add( ::sal_Int32 _nType, const uno::Any& _aOperator, const uno::Any& _aFormula1, const uno::Any& _aFormula2 )
+{
+ return Add( _nType, _aOperator, _aFormula1, _aFormula2, uno::Reference< excel::XStyle >() );
+}
+
+uno::Reference< excel::XFormatCondition >
+ScVbaFormatConditions::Add( ::sal_Int32 _nType, const uno::Any& _aOperator, const uno::Any& _aFormula1, const uno::Any& _aFormula2, const css::uno::Reference< excel::XStyle >& _xStyle )
+{
+ // #TODO
+ // #FIXME
+ // This method will NOT handle r1c1 formulas [*]and only assumes that
+ // the formulas are _xlA1 based ( need to hook into calc work this should
+ // address this )
+ // [*] reason: getA1Formula method below is just a hook and just
+ // returns what it gets ( e.g. doesn't convert anything )
+ uno::Reference< excel::XStyle > xStyle( _xStyle );
+ uno::Reference< excel::XFormatCondition > xFormatCondition;
+ try
+ {
+ OUString sStyleName;
+ if ( !xStyle.is() )
+ {
+ sStyleName = getStyleName();
+ xStyle = mxStyles->Add(sStyleName, uno::Any() );
+ }
+ else
+ {
+ sStyleName = xStyle->getName();
+ }
+
+ std::vector< beans::PropertyValue > aPropertyValueVector;
+ sheet::ConditionOperator aType = ScVbaFormatCondition::retrieveAPIType(_nType, uno::Reference< sheet::XSheetCondition >() );
+ uno::Any aValue;
+
+ if ( aType == sheet::ConditionOperator_FORMULA)
+ aValue <<= sheet::ConditionOperator_FORMULA;
+ else
+ aValue <<= ScVbaFormatCondition::retrieveAPIOperator(_aOperator);
+
+ beans::PropertyValue aProperty( "Operator", 0, aValue, beans::PropertyState_DIRECT_VALUE );
+ aPropertyValueVector.push_back( aProperty );
+
+ if ( _aFormula1.hasValue() )
+ {
+ beans::PropertyValue aProp( "Formula1", 0, uno::Any( getA1Formula( _aFormula1 ) ), beans::PropertyState_DIRECT_VALUE );
+ aPropertyValueVector.push_back( aProp );
+ }
+ if ( _aFormula2.hasValue() )
+ {
+ beans::PropertyValue aProp( "Formula2", 0, uno::Any( getA1Formula( _aFormula2 ) ), beans::PropertyState_DIRECT_VALUE );
+ aPropertyValueVector.push_back( aProp );
+ }
+ aProperty.Name = "StyleName";
+ aProperty.Value <<= sStyleName;
+
+ mxSheetConditionalEntries->addNew(comphelper::containerToSequence(aPropertyValueVector));
+ for (sal_Int32 i = mxSheetConditionalEntries->getCount()-1; i >= 0; i--)
+ {
+ uno::Reference< sheet::XSheetConditionalEntry > xSheetConditionalEntry( mxSheetConditionalEntries->getByIndex(i), uno::UNO_QUERY_THROW );
+ if (xSheetConditionalEntry->getStyleName() == sStyleName)
+ {
+ xFormatCondition = new ScVbaFormatCondition(uno::Reference< XHelperInterface >( mxRangeParent, uno::UNO_QUERY_THROW ), mxContext, xSheetConditionalEntry, xStyle, this, mxParentRangePropertySet);
+ notifyRange();
+ return xFormatCondition;
+ }
+ }
+ }
+ catch (uno::Exception& )
+ {
+ }
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ return xFormatCondition;
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+ScVbaFormatConditions::createEnumeration()
+{
+ return new EnumWrapper( m_xIndexAccess, mxRangeParent, mxContext, mxStyles, this, mxParentRangePropertySet );
+}
+
+void
+ScVbaFormatConditions::notifyRange()
+{
+ try
+ {
+ mxParentRangePropertySet->setPropertyValue(SC_UNONAME_CONDFMT, uno::Any( mxSheetConditionalEntries ));
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+OUString
+ScVbaFormatConditions::getA1Formula(const css::uno::Any& _aFormula)
+{
+ // #TODO, #FIXME hook-in proper formula conversion detection & logic
+ OUString sFormula;
+ if ( !( _aFormula >>= sFormula ) )
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
+ return sFormula;
+}
+
+OUString
+ScVbaFormatConditions::getStyleName()
+{
+ ScVbaStyles* pStyles = mxStyles.get();
+ if ( !pStyles )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ uno::Sequence< OUString > sCellStyleNames = pStyles->getStyleNames();
+ return ContainerUtilities::getUniqueName(sCellStyleNames, "Excel_CondFormat", u"_");
+}
+
+void
+ScVbaFormatConditions::removeFormatCondition( const OUString& _sStyleName, bool _bRemoveStyle)
+{
+ try
+ {
+ sal_Int32 nElems = mxSheetConditionalEntries->getCount();
+ for (sal_Int32 i = 0; i < nElems; i++)
+ {
+ uno::Reference< sheet::XSheetConditionalEntry > xSheetConditionalEntry( mxSheetConditionalEntries->getByIndex(i), uno::UNO_QUERY_THROW );
+ if (_sStyleName == xSheetConditionalEntry->getStyleName())
+ {
+ mxSheetConditionalEntries->removeByIndex(i);
+ if (_bRemoveStyle)
+ {
+ ScVbaStyles* pStyles = mxStyles.get();
+ if ( !pStyles )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ pStyles->Delete( _sStyleName );
+ }
+ return;
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+OUString
+ScVbaFormatConditions::getServiceImplName()
+{
+ return "ScVbaFormatConditions";
+}
+
+uno::Sequence< OUString >
+ScVbaFormatConditions::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.FormatConditions"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaformatconditions.hxx b/sc/source/ui/vba/vbaformatconditions.hxx
new file mode 100644
index 0000000000..8fe7cfabb8
--- /dev/null
+++ b/sc/source/ui/vba/vbaformatconditions.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ooo/vba/excel/XFormatConditions.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::sheet { class XSheetConditionalEntries; }
+namespace ooo::vba::excel { class XRange; }
+namespace ooo::vba::excel { class XStyle; }
+namespace ooo::vba::excel { class XStyles; }
+class ScVbaStyles;
+
+// This class is used only as a target for casting, it seems,
+// and no objects of this type are created as such, I think.
+
+class ScVbaFormatConditions: public CollTestImplHelper< ov::excel::XFormatConditions >
+{
+ css::uno::Reference< css::sheet::XSheetConditionalEntries > mxSheetConditionalEntries;
+ rtl::Reference< ScVbaStyles > mxStyles;
+ css::uno::Reference< ov::excel::XRange > mxRangeParent;
+ css::uno::Reference< css::beans::XPropertySet > mxParentRangePropertySet;
+public:
+ /// @throws css::script::BasicErrorException
+ void notifyRange();
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XFormatCondition > Add( ::sal_Int32 Type, const css::uno::Any& Operator, const css::uno::Any& Formula1, const css::uno::Any& Formula2, const css::uno::Reference< ov::excel::XStyle >& _xCalcStyle );
+ /// @throws css::script::BasicErrorException
+ static OUString getA1Formula(const css::uno::Any& _aFormula);
+ OUString getStyleName();
+ /// @throws css::script::BasicErrorException
+ void removeFormatCondition( const OUString& _sStyleName, bool _bRemoveStyle);
+ const css::uno::Reference< css::sheet::XSheetConditionalEntries >& getSheetConditionalEntries() const { return mxSheetConditionalEntries; }
+ // XFormatConditions
+ virtual void SAL_CALL Delete( ) override;
+ virtual css::uno::Reference< ov::excel::XFormatCondition > SAL_CALL Add( ::sal_Int32 Type, const css::uno::Any& Operator, const css::uno::Any& Formula1, const css::uno::Any& Formula2 ) override;
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject(const css::uno::Any&) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+ ScVbaFormatConditions() = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaglobals.cxx b/sc/source/ui/vba/vbaglobals.cxx
new file mode 100644
index 0000000000..734553d50d
--- /dev/null
+++ b/sc/source/ui/vba/vbaglobals.cxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbaglobals.hxx"
+
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include "vbaapplication.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::ooo::vba;
+
+// ScVbaGlobals
+
+//ScVbaGlobals::ScVbaGlobals( css::uno::Reference< css::uno::XComponentContext >const& rxContext, ) : ScVbaGlobals_BASE( uno::Reference< XHelperInterface >(), rxContext )
+
+ScVbaGlobals::ScVbaGlobals( uno::Sequence< uno::Any > const& aArgs, uno::Reference< uno::XComponentContext >const& rxContext ) : ScVbaGlobals_BASE( uno::Reference< XHelperInterface >(), rxContext, "ExcelDocumentContext" )
+{
+ uno::Sequence< beans::PropertyValue > aInitArgs( aArgs.hasElements() ? 2 : 1 );
+ auto pInitArgs = aInitArgs.getArray();
+ pInitArgs[ 0 ].Name = "Application";
+ pInitArgs[ 0 ].Value <<= getApplication();
+ if ( aArgs.hasElements() )
+ {
+ pInitArgs[ 1 ].Name = "ExcelDocumentContext";
+ pInitArgs[ 1 ].Value <<= getXSomethingFromArgs< frame::XModel >( aArgs, 0 );
+ }
+ init( aInitArgs );
+}
+
+ScVbaGlobals::~ScVbaGlobals()
+{
+}
+
+// XGlobals
+
+uno::Reference<excel::XApplication > const &
+ScVbaGlobals::getApplication()
+{
+ if ( !mxApplication.is() )
+ mxApplication.set( new ScVbaApplication( mxContext) );
+ return mxApplication;
+}
+
+uno::Reference<excel::XApplication > SAL_CALL
+ScVbaGlobals::getExcel()
+{
+ return getApplication();
+}
+
+uno::Reference< excel::XWorkbook > SAL_CALL
+ScVbaGlobals::getActiveWorkbook()
+{
+ uno::Reference< excel::XWorkbook > xWorkbook( getApplication()->getActiveWorkbook(), uno::UNO_SET_THROW);
+ return xWorkbook;
+}
+
+uno::Reference< excel::XWindow > SAL_CALL
+ScVbaGlobals::getActiveWindow()
+{
+ return getApplication()->getActiveWindow();
+}
+
+uno::Reference< excel::XWorksheet > SAL_CALL
+ScVbaGlobals::getActiveSheet()
+{
+ return getApplication()->getActiveSheet();
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::WorkBooks( const uno::Any& aIndex )
+{
+ return getApplication()->Workbooks(aIndex);
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::WorkSheets(const uno::Any& aIndex)
+{
+ return getApplication()->Worksheets( aIndex );
+}
+uno::Any SAL_CALL
+ScVbaGlobals::Sheets( const uno::Any& aIndex )
+{
+ return WorkSheets( aIndex );
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::Range( const uno::Any& Cell1, const uno::Any& Cell2 )
+{
+ return getApplication()->Range( Cell1, Cell2 );
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::Names( const css::uno::Any& aIndex )
+{
+ return getApplication()->Names( aIndex );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaGlobals::getActiveCell()
+{
+ return getApplication()->getActiveCell();
+}
+
+uno::Reference< XAssistant > SAL_CALL
+ScVbaGlobals::getAssistant()
+{
+ return getApplication()->getAssistant();
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::getSelection()
+{
+ return getApplication()->getSelection();
+}
+
+uno::Reference< excel::XWorkbook > SAL_CALL
+ScVbaGlobals::getThisWorkbook()
+{
+ return getApplication()->getThisWorkbook();
+}
+void SAL_CALL
+ScVbaGlobals::Calculate()
+{
+ return getApplication()->Calculate();
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaGlobals::Cells( const uno::Any& RowIndex, const uno::Any& ColumnIndex )
+{
+ return getApplication()->getActiveSheet()->Cells( RowIndex, ColumnIndex );
+}
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaGlobals::Columns( const uno::Any& aIndex )
+{
+ return getApplication()->getActiveSheet()->Columns( aIndex );
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::CommandBars( const uno::Any& aIndex )
+{
+ uno::Reference< XApplicationBase > xBase( getApplication(), uno::UNO_QUERY_THROW );
+ return xBase->CommandBars( aIndex );
+}
+
+css::uno::Reference< ov::excel::XRange > SAL_CALL
+ScVbaGlobals::Union( const css::uno::Reference< ov::excel::XRange >& Arg1, const css::uno::Reference< ov::excel::XRange >& Arg2, const css::uno::Any& Arg3, const css::uno::Any& Arg4, const css::uno::Any& Arg5, const css::uno::Any& Arg6, const css::uno::Any& Arg7, const css::uno::Any& Arg8, const css::uno::Any& Arg9, const css::uno::Any& Arg10, const css::uno::Any& Arg11, const css::uno::Any& Arg12, const css::uno::Any& Arg13, const css::uno::Any& Arg14, const css::uno::Any& Arg15, const css::uno::Any& Arg16, const css::uno::Any& Arg17, const css::uno::Any& Arg18, const css::uno::Any& Arg19, const css::uno::Any& Arg20, const css::uno::Any& Arg21, const css::uno::Any& Arg22, const css::uno::Any& Arg23, const css::uno::Any& Arg24, const css::uno::Any& Arg25, const css::uno::Any& Arg26, const css::uno::Any& Arg27, const css::uno::Any& Arg28, const css::uno::Any& Arg29, const css::uno::Any& Arg30 )
+{
+ return getApplication()->Union( Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11, Arg12, Arg13, Arg14, Arg15, Arg16, Arg17, Arg18, Arg19, Arg20, Arg21, Arg22, Arg23, Arg24, Arg25, Arg26, Arg27, Arg28, Arg29, Arg30 );
+}
+css::uno::Reference< ov::excel::XRange > SAL_CALL
+ScVbaGlobals::Intersect( const css::uno::Reference< ov::excel::XRange >& Arg1, const css::uno::Reference< ov::excel::XRange >& Arg2, const css::uno::Any& Arg3, const css::uno::Any& Arg4, const css::uno::Any& Arg5, const css::uno::Any& Arg6, const css::uno::Any& Arg7, const css::uno::Any& Arg8, const css::uno::Any& Arg9, const css::uno::Any& Arg10, const css::uno::Any& Arg11, const css::uno::Any& Arg12, const css::uno::Any& Arg13, const css::uno::Any& Arg14, const css::uno::Any& Arg15, const css::uno::Any& Arg16, const css::uno::Any& Arg17, const css::uno::Any& Arg18, const css::uno::Any& Arg19, const css::uno::Any& Arg20, const css::uno::Any& Arg21, const css::uno::Any& Arg22, const css::uno::Any& Arg23, const css::uno::Any& Arg24, const css::uno::Any& Arg25, const css::uno::Any& Arg26, const css::uno::Any& Arg27, const css::uno::Any& Arg28, const css::uno::Any& Arg29, const css::uno::Any& Arg30 )
+{
+ return getApplication()->Intersect( Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11, Arg12, Arg13, Arg14, Arg15, Arg16, Arg17, Arg18, Arg19, Arg20, Arg21, Arg22, Arg23, Arg24, Arg25, Arg26, Arg27, Arg28, Arg29, Arg30 );
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::Evaluate( const OUString& Name )
+{
+ return getApplication()->Evaluate( Name );
+}
+
+css::uno::Any SAL_CALL
+ScVbaGlobals::WorksheetFunction( )
+{
+ return getApplication()->WorksheetFunction();
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::Windows( const uno::Any& aIndex )
+{
+ return getApplication()->Windows( aIndex );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaGlobals::Rows( const uno::Any& aIndex )
+{
+ return getApplication()->getActiveSheet()->Rows( aIndex );
+
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::getDebug()
+{
+ try // return empty object on error
+ {
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager( mxContext->getServiceManager(), uno::UNO_SET_THROW );
+ uno::Reference< uno::XInterface > xVBADebug = xServiceManager->createInstanceWithContext(
+ "ooo.vba.Debug", mxContext );
+ return uno::Any( xVBADebug );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return uno::Any();
+}
+
+uno::Any SAL_CALL
+ScVbaGlobals::MenuBars( const uno::Any& aIndex )
+{
+ return getApplication()->MenuBars(aIndex);
+}
+
+uno::Sequence< OUString > SAL_CALL
+ScVbaGlobals::getAvailableServiceNames( )
+{
+ static const uno::Sequence< OUString > serviceNames = comphelper::concatSequences(
+ ScVbaGlobals_BASE::getAvailableServiceNames(),
+ uno::Sequence< OUString >
+ {
+ "ooo.vba.excel.Range",
+ "ooo.vba.excel.Workbook",
+ "ooo.vba.excel.Window",
+ "ooo.vba.excel.Worksheet",
+ "ooo.vba.excel.Application",
+ "ooo.vba.excel.Hyperlink",
+ "com.sun.star.script.vba.VBASpreadsheetEventProcessor"
+ } );
+ return serviceNames;
+}
+
+OUString
+ScVbaGlobals::getServiceImplName()
+{
+ return "ScVbaGlobals";
+}
+
+uno::Sequence< OUString >
+ScVbaGlobals::getServiceNames()
+{
+ static uno::Sequence< OUString > aServiceNames
+ {
+ "ooo.vba.excel.Globals"
+ };
+ return aServiceNames;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+ScVbaGlobals_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new ScVbaGlobals(arguments, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaglobals.hxx b/sc/source/ui/vba/vbaglobals.hxx
new file mode 100644
index 0000000000..6f978c591d
--- /dev/null
+++ b/sc/source/ui/vba/vbaglobals.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 SC_VBA_GLOBALS
+#define SC_VBA_GLOBALS
+
+#include <ooo/vba/excel/XGlobals.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <vbahelper/vbaglobalbase.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+namespace ooo::vba::excel { class XApplication; }
+
+
+typedef ::cppu::ImplInheritanceHelper< VbaGlobalsBase, ov::excel::XGlobals > ScVbaGlobals_BASE;
+
+class ScVbaGlobals : public ScVbaGlobals_BASE
+{
+ css::uno::Reference< ov::excel::XApplication > mxApplication;
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XApplication > const & getApplication();
+public:
+
+ ScVbaGlobals( css::uno::Sequence< css::uno::Any > const& aArgs,
+ css::uno::Reference< css::uno::XComponentContext >const& rxContext );
+ virtual ~ScVbaGlobals() override;
+
+ // XGlobals
+ virtual css::uno::Reference< ov::excel::XWorkbook > SAL_CALL getActiveWorkbook() override;
+ virtual css::uno::Reference< ov::excel::XWindow > SAL_CALL getActiveWindow() override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL getActiveSheet() override;
+ virtual css::uno::Reference< ov::XAssistant > SAL_CALL getAssistant() override;
+ virtual void SAL_CALL Calculate( ) override;
+
+ virtual css::uno::Any SAL_CALL getSelection() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getActiveCell() override;
+ virtual css::uno::Reference< ov::excel::XWorkbook > SAL_CALL getThisWorkbook() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Cells( const css::uno::Any& RowIndex, const css::uno::Any& ColumnIndex ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Columns( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL CommandBars( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Evaluate( const OUString& Name ) override;
+
+ virtual css::uno::Any SAL_CALL WorkSheets(const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL WorkBooks(const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL WorksheetFunction( ) override;
+ virtual css::uno::Any SAL_CALL Windows( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Sheets( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Range( const css::uno::Any& Cell1, const css::uno::Any& Cell2 ) override;
+ virtual css::uno::Reference< ::ooo::vba::excel::XRange > SAL_CALL Rows( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Names( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Intersect( const css::uno::Reference< ov::excel::XRange >& Arg1, const css::uno::Reference< ov::excel::XRange >& Arg2, const css::uno::Any& Arg3, const css::uno::Any& Arg4, const css::uno::Any& Arg5, const css::uno::Any& Arg6, const css::uno::Any& Arg7, const css::uno::Any& Arg8, const css::uno::Any& Arg9, const css::uno::Any& Arg10, const css::uno::Any& Arg11, const css::uno::Any& Arg12, const css::uno::Any& Arg13, const css::uno::Any& Arg14, const css::uno::Any& Arg15, const css::uno::Any& Arg16, const css::uno::Any& Arg17, const css::uno::Any& Arg18, const css::uno::Any& Arg19, const css::uno::Any& Arg20, const css::uno::Any& Arg21, const css::uno::Any& Arg22, const css::uno::Any& Arg23, const css::uno::Any& Arg24, const css::uno::Any& Arg25, const css::uno::Any& Arg26, const css::uno::Any& Arg27, const css::uno::Any& Arg28, const css::uno::Any& Arg29, const css::uno::Any& Arg30 ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Union( const css::uno::Reference< ov::excel::XRange >& Arg1, const css::uno::Reference< ov::excel::XRange >& Arg2, const css::uno::Any& Arg3, const css::uno::Any& Arg4, const css::uno::Any& Arg5, const css::uno::Any& Arg6, const css::uno::Any& Arg7, const css::uno::Any& Arg8, const css::uno::Any& Arg9, const css::uno::Any& Arg10, const css::uno::Any& Arg11, const css::uno::Any& Arg12, const css::uno::Any& Arg13, const css::uno::Any& Arg14, const css::uno::Any& Arg15, const css::uno::Any& Arg16, const css::uno::Any& Arg17, const css::uno::Any& Arg18, const css::uno::Any& Arg19, const css::uno::Any& Arg20, const css::uno::Any& Arg21, const css::uno::Any& Arg22, const css::uno::Any& Arg23, const css::uno::Any& Arg24, const css::uno::Any& Arg25, const css::uno::Any& Arg26, const css::uno::Any& Arg27, const css::uno::Any& Arg28, const css::uno::Any& Arg29, const css::uno::Any& Arg30 ) override;
+ virtual css::uno::Reference< ov::excel::XApplication > SAL_CALL getExcel() override;
+ virtual css::uno::Any SAL_CALL getDebug() override;
+ virtual css::uno::Any SAL_CALL MenuBars( const css::uno::Any& aIndex ) override;
+
+ // XMultiServiceFactory
+ virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbahyperlink.cxx b/sc/source/ui/vba/vbahyperlink.cxx
new file mode 100644
index 0000000000..17190b9cf6
--- /dev/null
+++ b/sc/source/ui/vba/vbahyperlink.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "vbahyperlink.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <ooo/vba/office/MsoHyperlinkType.hpp>
+#include <ooo/vba/msforms/XShape.hpp>
+#include "vbarange.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaHyperlink::ScVbaHyperlink( const uno::Sequence< uno::Any >& rArgs,
+ const uno::Reference< uno::XComponentContext >& rxContext ) :
+ HyperlinkImpl_BASE( getXSomethingFromArgs< XHelperInterface >( rArgs, 0 ), rxContext ),
+ mxCell( getXSomethingFromArgs< table::XCell >( rArgs, 1, false ) ),
+ mnType( office::MsoHyperlinkType::msoHyperlinkRange )
+{
+ uno::Reference< text::XTextFieldsSupplier > xTextFields( mxCell, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndex( xTextFields->getTextFields(), uno::UNO_QUERY_THROW );
+ mxTextField.set( xIndex->getByIndex(0), uno::UNO_QUERY_THROW );
+}
+
+ScVbaHyperlink::ScVbaHyperlink( const uno::Reference< XHelperInterface >& rxAnchor,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Any& rAddress, const uno::Any& rSubAddress,
+ const uno::Any& rScreenTip, const uno::Any& rTextToDisplay ) :
+ HyperlinkImpl_BASE( rxAnchor, rxContext ) // parent of Hyperlink is the anchor object
+{
+ // extract parameters, Address must not be empty
+ UrlComponents aUrlComp;
+ OUString aTextToDisplay;
+ if( !(rAddress >>= aUrlComp.first) || aUrlComp.first.isEmpty() )
+ throw uno::RuntimeException("Cannot get address" );
+ rSubAddress >>= aUrlComp.second;
+ rScreenTip >>= maScreenTip;
+ rTextToDisplay >>= aTextToDisplay;
+
+ // get anchor range or anchor shape
+ uno::Reference< excel::XRange > xAnchorRange( rxAnchor, uno::UNO_QUERY );
+ if( xAnchorRange.is() )
+ {
+ mnType = office::MsoHyperlinkType::msoHyperlinkRange;
+ // only single ranges are allowed
+ uno::Reference< table::XCellRange > xUnoRange( ScVbaRange::getCellRange( xAnchorRange ), uno::UNO_QUERY_THROW );
+ // insert the hyperlink into the top-left cell only
+ mxCell.set( xUnoRange->getCellByPosition( 0, 0 ), uno::UNO_SET_THROW );
+ uno::Reference< text::XText > xText( mxCell, uno::UNO_QUERY_THROW );
+ // use cell text or URL if no TextToDisplay has been passed
+ if( aTextToDisplay.isEmpty() )
+ {
+ aTextToDisplay = xText->getString();
+ if( aTextToDisplay.isEmpty() )
+ {
+ OUStringBuffer aBuffer( aUrlComp.first );
+ if( !aUrlComp.second.isEmpty() )
+ aBuffer.append( " - " + aUrlComp.second );
+ aTextToDisplay = aBuffer.makeStringAndClear();
+ }
+ }
+ // create and initialize a new URL text field
+ uno::Reference< lang::XMultiServiceFactory > xFactory( ScVbaRange::getUnoModel( xAnchorRange ), uno::UNO_QUERY_THROW );
+ uno::Reference< text::XTextContent > xUrlField( xFactory->createInstance("com.sun.star.text.TextField.URL"), uno::UNO_QUERY_THROW );
+ mxTextField.set( xUrlField, uno::UNO_QUERY_THROW );
+ setUrlComponents( aUrlComp );
+ setTextToDisplay( aTextToDisplay );
+ // insert the text field into the document
+ xText->setString( OUString() );
+ uno::Reference< text::XTextRange > xRange( xText->createTextCursor(), uno::UNO_QUERY_THROW );
+ xText->insertTextContent( xRange, xUrlField, false );
+ }
+ else
+ {
+ uno::Reference< msforms::XShape > xAnchorShape( rxAnchor, uno::UNO_QUERY_THROW );
+ mnType = office::MsoHyperlinkType::msoHyperlinkShape;
+ // FIXME: insert hyperlink into shape
+ throw uno::RuntimeException();
+ }
+}
+
+ScVbaHyperlink::~ScVbaHyperlink()
+{
+}
+
+OUString ScVbaHyperlink::getName()
+{
+ // it seems this attribute is same as TextToDisplay
+ return getTextToDisplay();
+}
+
+void ScVbaHyperlink::setName( const OUString& rName )
+{
+ setTextToDisplay( rName );
+}
+
+OUString ScVbaHyperlink::getAddress()
+{
+ return getUrlComponents().first;
+}
+
+void ScVbaHyperlink::setAddress( const OUString& rAddress )
+{
+ UrlComponents aUrlComp = getUrlComponents();
+ aUrlComp.first = rAddress;
+ setUrlComponents( aUrlComp );
+}
+
+OUString ScVbaHyperlink::getSubAddress()
+{
+ return getUrlComponents().second;
+}
+
+void ScVbaHyperlink::setSubAddress( const OUString& rSubAddress )
+{
+ UrlComponents aUrlComp = getUrlComponents();
+ aUrlComp.second = rSubAddress;
+ setUrlComponents( aUrlComp );
+}
+
+OUString SAL_CALL ScVbaHyperlink::getScreenTip()
+{
+ return maScreenTip;
+}
+
+void SAL_CALL ScVbaHyperlink::setScreenTip( const OUString& rScreenTip )
+{
+ maScreenTip = rScreenTip;
+}
+
+OUString ScVbaHyperlink::getTextToDisplay()
+{
+ ensureTextField();
+ OUString aTextToDisplay;
+ mxTextField->getPropertyValue("Representation") >>= aTextToDisplay;
+ return aTextToDisplay;
+}
+
+void ScVbaHyperlink::setTextToDisplay( const OUString& rTextToDisplay )
+{
+ ensureTextField();
+ mxTextField->setPropertyValue("Representation", uno::Any( rTextToDisplay ) );
+}
+
+sal_Int32 SAL_CALL ScVbaHyperlink::getType()
+{
+ return mnType;
+}
+
+uno::Reference< excel::XRange > SAL_CALL ScVbaHyperlink::getRange()
+{
+ if( mnType == office::MsoHyperlinkType::msoHyperlinkRange )
+ {
+ // if constructed from Hyperlinks object, range has been passed as parent
+ uno::Reference< excel::XRange > xAnchorRange( getParent(), uno::UNO_QUERY );
+ if( !xAnchorRange.is() )
+ {
+ // if constructed via service c'tor, create new range based on cell
+ uno::Reference< table::XCellRange > xRange( mxCell, uno::UNO_QUERY_THROW );
+ // FIXME: need to pass current worksheet as the parent of XRange.
+ xAnchorRange.set( new ScVbaRange( uno::Reference< XHelperInterface >(), mxContext, xRange ) );
+ }
+ return xAnchorRange;
+ }
+ // error if called at a shape Hyperlink object
+ throw uno::RuntimeException();
+}
+
+uno::Reference< msforms::XShape > SAL_CALL ScVbaHyperlink::getShape()
+{
+ // error if called at a range Hyperlink object
+ return uno::Reference< msforms::XShape >( getParent(), uno::UNO_QUERY_THROW );
+}
+
+VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaHyperlink, "ooo.vba.excel.Hyperlink" )
+
+// private --------------------------------------------------------------------
+
+void ScVbaHyperlink::ensureTextField()
+{
+ if( !mxTextField.is() )
+ throw uno::RuntimeException();
+}
+
+ScVbaHyperlink::UrlComponents ScVbaHyperlink::getUrlComponents()
+{
+ ensureTextField();
+ OUString aUrl;
+ mxTextField->getPropertyValue("URL") >>= aUrl;
+ sal_Int32 nHashPos = aUrl.indexOf( '#' );
+ if( nHashPos < 0 )
+ return UrlComponents( aUrl, OUString() );
+ return UrlComponents( aUrl.copy( 0, nHashPos ), aUrl.copy( nHashPos + 1 ) );
+}
+
+void ScVbaHyperlink::setUrlComponents( const UrlComponents& rUrlComp )
+{
+ ensureTextField();
+ OUStringBuffer aUrl( rUrlComp.first );
+ if( !rUrlComp.second.isEmpty() )
+ aUrl.append( "#" + rUrlComp.second );
+ mxTextField->setPropertyValue("URL", uno::Any( aUrl.makeStringAndClear() ) );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScVbaHyperlink_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new ScVbaHyperlink(args, context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbahyperlink.hxx b/sc/source/ui/vba/vbahyperlink.hxx
new file mode 100644
index 0000000000..391853be74
--- /dev/null
+++ b/sc/source/ui/vba/vbahyperlink.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ooo/vba/excel/XHyperlink.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+#include <tools/long.hxx>
+
+namespace ooo::vba::excel { class XRange; }
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::table { class XCell; }
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XHyperlink > HyperlinkImpl_BASE;
+
+class ScVbaHyperlink : public HyperlinkImpl_BASE
+{
+public:
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ ScVbaHyperlink(
+ const css::uno::Sequence< css::uno::Any >& rArgs,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ /// @throws css::uno::RuntimeException
+ ScVbaHyperlink(
+ const css::uno::Reference< ov::XHelperInterface >& rxAnchor,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Any& rAddress, const css::uno::Any& rSubAddress,
+ const css::uno::Any& rScreenTip, const css::uno::Any& rTextToDisplay );
+
+ virtual ~ScVbaHyperlink() override;
+
+ // Attributes
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& rName ) override;
+ virtual OUString SAL_CALL getAddress() override;
+ virtual void SAL_CALL setAddress( const OUString& rAddress ) override;
+ virtual OUString SAL_CALL getSubAddress() override;
+ virtual void SAL_CALL setSubAddress( const OUString& rSubAddress ) override;
+ virtual OUString SAL_CALL getScreenTip() override;
+ virtual void SAL_CALL setScreenTip( const OUString& rScreenTip ) override;
+ virtual OUString SAL_CALL getTextToDisplay() override;
+ virtual void SAL_CALL setTextToDisplay( const OUString& rTextToDisplay ) override;
+ virtual sal_Int32 SAL_CALL getType() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getRange() override;
+ virtual css::uno::Reference< ov::msforms::XShape > SAL_CALL getShape() override;
+
+ // XHelperInterface
+ VBAHELPER_DECL_XHELPERINTERFACE
+
+private:
+ typedef ::std::pair< OUString, OUString > UrlComponents;
+
+ /// @throws css::uno::RuntimeException
+ void ensureTextField();
+ /// @throws css::uno::RuntimeException
+ UrlComponents getUrlComponents();
+ /// @throws css::uno::RuntimeException
+ void setUrlComponents( const UrlComponents& rUrlComp );
+
+private:
+ css::uno::Reference< css::table::XCell > mxCell;
+ css::uno::Reference< css::beans::XPropertySet > mxTextField;
+ OUString maScreenTip;
+ tools::Long mnType;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbahyperlinks.cxx b/sc/source/ui/vba/vbahyperlinks.cxx
new file mode 100644
index 0000000000..d309f5f44e
--- /dev/null
+++ b/sc/source/ui/vba/vbahyperlinks.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 "vbahyperlinks.hxx"
+#include <algorithm>
+#include <vector>
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/office/MsoHyperlinkType.hpp>
+#include <rangelst.hxx>
+#include "vbahyperlink.hxx"
+#include "vbarange.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+namespace {
+
+/** Returns true, if every range of rxInner is contained in any range of rScOuter.
+
+ @throws css::uno::RuntimeException
+*/
+bool lclContains( const ScRangeList& rScOuter, const uno::Reference< excel::XRange >& rxInner )
+{
+ const ScRangeList& rScInner = ScVbaRange::getScRangeList( rxInner );
+ if( rScInner.empty() || rScOuter.empty() )
+ throw uno::RuntimeException("Empty range objects" );
+
+ for( size_t nIndex = 0, nCount = rScInner.size(); nIndex < nCount; ++nIndex )
+ if( !rScOuter.Contains( rScInner[ nIndex ] ) )
+ return false;
+ return true;
+}
+
+/** Functor to decide whether the anchors of two Hyperlink objects are equal. */
+struct EqualAnchorFunctor
+{
+ uno::Reference< excel::XRange > mxAnchorRange;
+ uno::Reference< msforms::XShape > mxAnchorShape;
+ sal_Int32 mnType;
+ /// @throws uno::RuntimeException
+ explicit EqualAnchorFunctor( const uno::Reference< excel::XHyperlink >& rxHlink );
+ /// @throws uno::RuntimeException
+ bool operator()( const uno::Reference< excel::XHyperlink >& rxHlink ) const;
+};
+
+EqualAnchorFunctor::EqualAnchorFunctor( const uno::Reference< excel::XHyperlink >& rxHlink ) :
+ mnType( rxHlink->getType() )
+{
+ switch( mnType )
+ {
+ case office::MsoHyperlinkType::msoHyperlinkRange:
+ mxAnchorRange.set( rxHlink->getRange(), uno::UNO_SET_THROW );
+ break;
+ case office::MsoHyperlinkType::msoHyperlinkShape:
+ case office::MsoHyperlinkType::msoHyperlinkInlineShape:
+ mxAnchorShape.set( rxHlink->getShape(), uno::UNO_SET_THROW );
+ break;
+ default:
+ throw uno::RuntimeException();
+ }
+}
+
+bool EqualAnchorFunctor::operator()( const uno::Reference< excel::XHyperlink >& rxHlink ) const
+{
+ sal_Int32 nType = rxHlink->getType();
+ if( nType != mnType )
+ return false;
+
+ switch( nType )
+ {
+ case office::MsoHyperlinkType::msoHyperlinkRange:
+ {
+ uno::Reference< excel::XRange > xAnchorRange( rxHlink->getRange(), uno::UNO_SET_THROW );
+ const ScRangeList& rScRanges1 = ScVbaRange::getScRangeList( xAnchorRange );
+ const ScRangeList& rScRanges2 = ScVbaRange::getScRangeList( mxAnchorRange );
+ return (rScRanges1.size() == 1) && (rScRanges2.size() == 1) && (rScRanges1[ 0 ] == rScRanges2[ 0 ]);
+ }
+ case office::MsoHyperlinkType::msoHyperlinkShape:
+ case office::MsoHyperlinkType::msoHyperlinkInlineShape:
+ {
+ uno::Reference< msforms::XShape > xAnchorShape( rxHlink->getShape(), uno::UNO_SET_THROW );
+ return xAnchorShape.get() == mxAnchorShape.get();
+ }
+ default:
+ throw uno::RuntimeException();
+ }
+}
+
+} // namespace
+
+namespace detail {
+
+class ScVbaHlinkContainer : public ::cppu::WeakImplHelper< container::XIndexAccess >
+{
+public:
+ /// @throws uno::RuntimeException
+ explicit ScVbaHlinkContainer();
+ /// @throws uno::RuntimeException
+ explicit ScVbaHlinkContainer( const ScVbaHlinkContainerRef& rxSheetContainer, const ScRangeList& rScRanges );
+
+ /** Inserts the passed hyperlink into the collection. Will remove a
+ Hyperlink object with the same anchor as the passed Hyperlink object.
+
+ @throws uno::RuntimeException
+ */
+ void insertHyperlink( const uno::Reference< excel::XHyperlink >& rxHlink );
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+ typedef ::std::vector< uno::Reference< excel::XHyperlink > > HyperlinkVector;
+ HyperlinkVector maHlinks;
+};
+
+ScVbaHlinkContainer::ScVbaHlinkContainer()
+{
+ // TODO FIXME: fill with existing hyperlinks
+}
+
+ScVbaHlinkContainer::ScVbaHlinkContainer( const ScVbaHlinkContainerRef& rxSheetContainer,
+ const ScRangeList& rScRanges )
+{
+ for( sal_Int32 nIndex = 0, nCount = rxSheetContainer->getCount(); nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< excel::XHyperlink > xHlink( rxSheetContainer->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XRange > xHlinkRange( xHlink->getRange(), uno::UNO_SET_THROW );
+ if( lclContains( rScRanges, xHlinkRange ) )
+ maHlinks.push_back( xHlink );
+ }
+}
+
+void ScVbaHlinkContainer::insertHyperlink( const uno::Reference< excel::XHyperlink >& rxHlink )
+{
+ HyperlinkVector::iterator aIt = ::std::find_if( maHlinks.begin(), maHlinks.end(), EqualAnchorFunctor( rxHlink ) );
+ if( aIt == maHlinks.end() )
+ maHlinks.push_back( rxHlink );
+ else
+ *aIt = rxHlink;
+}
+
+sal_Int32 SAL_CALL ScVbaHlinkContainer::getCount()
+{
+ return static_cast< sal_Int32 >( maHlinks.size() );
+}
+
+uno::Any SAL_CALL ScVbaHlinkContainer::getByIndex( sal_Int32 nIndex )
+{
+ if( (0 <= nIndex) && (nIndex < getCount()) )
+ return uno::Any( maHlinks[ static_cast< size_t >( nIndex ) ] );
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Type SAL_CALL ScVbaHlinkContainer::getElementType()
+{
+ return cppu::UnoType<excel::XHyperlink>::get();
+}
+
+sal_Bool SAL_CALL ScVbaHlinkContainer::hasElements()
+{
+ return !maHlinks.empty();
+}
+
+ScVbaHlinkContainerMember::ScVbaHlinkContainerMember( ScVbaHlinkContainer* pContainer ) :
+ mxContainer( pContainer )
+{
+}
+
+ScVbaHlinkContainerMember::~ScVbaHlinkContainerMember()
+{
+}
+
+} // namespace detail
+
+ScVbaHyperlinks::ScVbaHyperlinks( const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext ) :
+ detail::ScVbaHlinkContainerMember( new detail::ScVbaHlinkContainer ),
+ ScVbaHyperlinks_BASE( rxParent, rxContext, uno::Reference< container::XIndexAccess >( mxContainer ) )
+{
+}
+
+ScVbaHyperlinks::ScVbaHyperlinks( const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const ScVbaHyperlinksRef& rxSheetHlinks, const ScRangeList& rScRanges ) :
+ detail::ScVbaHlinkContainerMember( new detail::ScVbaHlinkContainer( rxSheetHlinks->mxContainer, rScRanges ) ),
+ ScVbaHyperlinks_BASE( rxParent, rxContext, uno::Reference< container::XIndexAccess >( mxContainer ) ),
+ mxSheetHlinks( rxSheetHlinks )
+{
+}
+
+ScVbaHyperlinks::~ScVbaHyperlinks()
+{
+}
+
+// XHyperlinks ----------------------------------------------------------------
+
+uno::Reference< excel::XHyperlink > SAL_CALL ScVbaHyperlinks::Add(
+ const uno::Any& rAnchor, const uno::Any& rAddress, const uno::Any& rSubAddress,
+ const uno::Any& rScreenTip, const uno::Any& rTextToDisplay )
+{
+ /* If this Hyperlinks object has been created from a Range object, the
+ call to Add() is passed to the Hyperlinks object of the parent
+ worksheet. This container will not be modified (it will not contain the
+ inserted hyperlink).
+ For details, see documentation in hyperlinks.hxx.
+ */
+ if( mxSheetHlinks.is() )
+ return mxSheetHlinks->Add( rAnchor, rAddress, rSubAddress, rScreenTip, rTextToDisplay );
+
+ // get anchor object (can be a Range or a Shape object)
+ uno::Reference< XHelperInterface > xAnchor( rAnchor, uno::UNO_QUERY_THROW );
+
+ /* Create the Hyperlink object, this tries to insert the hyperlink into
+ the spreadsheet document. Parent of the Hyperlink is the anchor object. */
+ uno::Reference< excel::XHyperlink > xHlink( new ScVbaHyperlink(
+ xAnchor, mxContext, rAddress, rSubAddress, rScreenTip, rTextToDisplay ) );
+
+ /* If creation of the hyperlink did not throw, insert it into the
+ collection. */
+ mxContainer->insertHyperlink( xHlink );
+ return xHlink;
+}
+
+void SAL_CALL ScVbaHyperlinks::Delete()
+{
+ // FIXME not implemented
+ throw uno::RuntimeException();
+}
+
+// XEnumerationAccess ---------------------------------------------------------
+
+uno::Reference< container::XEnumeration > SAL_CALL ScVbaHyperlinks::createEnumeration()
+{
+ return new SimpleIndexAccessToEnumeration( m_xIndexAccess );
+}
+
+// XElementAccess -------------------------------------------------------------
+
+uno::Type SAL_CALL ScVbaHyperlinks::getElementType()
+{
+ return cppu::UnoType<excel::XHyperlink>::get();
+}
+
+// ScVbaCollectionBase --------------------------------------------------------
+
+uno::Any ScVbaHyperlinks::createCollectionObject( const uno::Any& rSource )
+{
+ // container stores XHyperlink objects, just return the passed object
+ return rSource;
+}
+
+// XHelperInterface -----------------------------------------------------------
+
+VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaHyperlinks, "ooo.vba.excel.Hyperlinks" )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbahyperlinks.hxx b/sc/source/ui/vba/vbahyperlinks.hxx
new file mode 100644
index 0000000000..7538fe50b0
--- /dev/null
+++ b/sc/source/ui/vba/vbahyperlinks.hxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ooo/vba/excel/XHyperlinks.hpp>
+#include <rtl/ref.hxx>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+class ScRangeList;
+
+namespace detail {
+
+class ScVbaHlinkContainer;
+typedef ::rtl::Reference< ScVbaHlinkContainer > ScVbaHlinkContainerRef;
+
+/** Base class for ScVbaHyperlinks to get an initialized ScVbaHlinkContainer
+ class member before the ScVbaHyperlinks_BASE base class will be constructed.
+ */
+struct ScVbaHlinkContainerMember
+{
+ ScVbaHlinkContainerRef mxContainer;
+
+ explicit ScVbaHlinkContainerMember( ScVbaHlinkContainer* pContainer );
+ ~ScVbaHlinkContainerMember();
+};
+
+} // namespace detail
+
+class ScVbaHyperlinks;
+typedef ::rtl::Reference< ScVbaHyperlinks > ScVbaHyperlinksRef;
+
+typedef CollTestImplHelper< ov::excel::XHyperlinks > ScVbaHyperlinks_BASE;
+
+/** Represents a collection of hyperlinks of a worksheet or of a range.
+
+ When a Hyperlinks collection object has been constructed from a VBA
+ Worksheet object, it will always represent the current set of all
+ hyperlinks existing in the sheet. Insertion and deletion of hyperlinks will
+ be reflected by the instance.
+
+ When a Hyperlinks collection object has been constructed from a VBA Range
+ object, it will represent the set of hyperlinks that have existed at its
+ construction time, and that are located completely inside the range(s)
+ represented by the Range object. Insertion and deletion of hyperlinks will
+ *not* be reflected by that instance. The instance will always offer all
+ hyperlinks it has been constructed with, even if they no longer exist.
+ Furthermore, the instance will not offer hyperlinks inserted later, even if
+ the instance itself has been used to insert the new hyperlinks.
+
+ VBA code example:
+
+ With ThisWorkbook.Worksheets(1)
+
+ Set hlinks = .Hyperlinks ' global Hyperlinks object
+ Set myrange = .Range("A1:C3")
+ Set rangelinks1 = myrange.Hyperlinks ' hyperlinks of range A1:C3
+
+ MsgBox hlinks.Count ' 0
+ MsgBox rangelinks1.Count ' 0
+
+ hlinks.Add .Range("A1"), "http://example.com"
+ ' a new hyperlink has been added in cell A1
+
+ MsgBox hlinks.Count ' 1
+ MsgBox rangelinks1.Count ' still 0!
+ Set rangelinks2 = myrange.Hyperlinks ' hyperlinks of range A1:C3
+ MsgBox rangelinks2.Count ' 1 (constructed after Add)
+
+ rangelinks1.Add .Range("A2"), "http://example.com"
+ ' a new hyperlink has been constructed via the rangelinks1 object
+ ' but this addition has been done by the worksheet Hyperlinks object
+
+ MsgBox hlinks.Count ' 2
+ MsgBox rangelinks1.Count ' still 0!!!
+ MsgBox rangelinks2.Count ' still 1!!!
+ MsgBox myrange.Hyperlinks.Count ' 2 (constructed after Add)
+
+ End With
+ */
+class ScVbaHyperlinks : private detail::ScVbaHlinkContainerMember, public ScVbaHyperlinks_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaHyperlinks(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaHyperlinks(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const ScVbaHyperlinksRef& rxSheetHlinks, const ScRangeList& rScRanges );
+
+ virtual ~ScVbaHyperlinks() override;
+
+ // XHyperlinks
+ virtual css::uno::Reference< ov::excel::XHyperlink > SAL_CALL Add(
+ const css::uno::Any& rAnchor, const css::uno::Any& rAddress, const css::uno::Any& rSubAddress,
+ const css::uno::Any& rScreenTip, const css::uno::Any& rTextToDisplay ) override;
+
+ virtual void SAL_CALL Delete() override;
+
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ // ScVbaCollectionBase
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& rSource ) override;
+
+ // XHelperInterface
+ VBAHELPER_DECL_XHELPERINTERFACE
+
+private:
+ ScVbaHyperlinksRef mxSheetHlinks;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbainterior.cxx b/sc/source/ui/vba/vbainterior.cxx
new file mode 100644
index 0000000000..ce94c893ad
--- /dev/null
+++ b/sc/source/ui/vba/vbainterior.cxx
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/xml/AttributeData.hpp>
+
+#include <ooo/vba/excel/XlColorIndex.hpp>
+#include <ooo/vba/excel/XlPattern.hpp>
+
+#include <map>
+
+#include <sal/macros.h>
+
+#include "vbainterior.hxx"
+#include "vbapalette.hxx"
+#include <document.hxx>
+#include <docsh.hxx>
+#include <utility>
+#include <frozen/bits/defines.h>
+#include <frozen/bits/elsa_std.h>
+#include <frozen/map.h>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+using namespace ::ooo::vba::excel::XlPattern;
+
+constexpr OUString BACKCOLOR = u"CellBackColor"_ustr;
+constexpr OUString PATTERN = u"Pattern"_ustr;
+constexpr OUString PATTERNCOLOR = u"PatternColor"_ustr;
+
+constexpr auto aPatternMap = frozen::make_map<sal_Int32, sal_Int32>({
+ { xlPatternAutomatic, 0 },
+ { xlPatternChecker, 9 },
+ { xlPatternCrissCross, 16 },
+ { xlPatternDown, 7 },
+ { xlPatternGray16, 17 },
+ { xlPatternGray25, 4 },
+ { xlPatternGray50, 2 },
+ { xlPatternGray75, 3 },
+ { xlPatternGray8, 18 },
+ { xlPatternGrid, 15 },
+ { xlPatternHorizontal, 5 },
+ { xlPatternLightDown, 13 },
+ { xlPatternLightHorizontal, 11 },
+ { xlPatternLightUp, 14 },
+ { xlPatternLightVertical, 12 },
+ { xlPatternNone, 0 },
+ { xlPatternSemiGray75, 10 },
+ { xlPatternSolid, 0 },
+ { xlPatternUp, 8 },
+ { xlPatternVertical, 6 }
+});
+
+ScVbaInterior::ScVbaInterior( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< beans::XPropertySet > xProps, ScDocument* pScDoc ) : ScVbaInterior_BASE( xParent, xContext ), m_xProps(std::move(xProps)), m_pScDoc( pScDoc )
+{
+ // auto color
+ m_aPattColor = Color(0);
+ m_nPattern = 0;
+ if ( !m_xProps.is() )
+ throw lang::IllegalArgumentException("properties", uno::Reference< uno::XInterface >(), 2 );
+}
+
+uno::Any
+ScVbaInterior::getColor()
+{
+ return uno::Any( OORGBToXLRGB( GetBackColor() ) );
+}
+
+void
+ScVbaInterior::setColor( const uno::Any& _color )
+{
+ sal_Int32 nColor = 0;
+ if( _color >>= nColor )
+ {
+ SetUserDefinedAttributes( BACKCOLOR, SetAttributeData( XLRGBToOORGB( nColor ) ) );
+ SetMixedColor();
+ }
+}
+
+void
+ScVbaInterior::SetMixedColor()
+{
+ // pattern
+ uno::Any aPattern = GetUserDefinedAttributes( PATTERN );
+ if( aPattern.hasValue() )
+ {
+ m_nPattern = GetAttributeData( aPattern );
+ }
+ sal_Int32 nPattern = 0;
+ auto it = aPatternMap.find( m_nPattern );
+ if (it != aPatternMap.end())
+ nPattern = it->second;
+ // pattern color
+ uno::Any aPatternColor = GetUserDefinedAttributes( PATTERNCOLOR );
+ if( aPatternColor.hasValue() )
+ {
+ sal_uInt32 nPatternColor = GetAttributeData( aPatternColor );
+ m_aPattColor = Color(ColorTransparency, nPatternColor);
+ }
+ Color nPatternColor = m_aPattColor;
+ // back color
+ Color aBackColor( GetBackColor() );
+ // set mixed color
+ Color aMixedColor;
+ if( nPattern > 0 )
+ aMixedColor = GetPatternColor( nPatternColor, aBackColor, static_cast<sal_uInt32>(nPattern) );
+ else
+ aMixedColor = GetPatternColor( aBackColor, aBackColor, static_cast<sal_uInt32>(nPattern) );
+ Color nMixedColor = aMixedColor.GetRGBColor();
+ m_xProps->setPropertyValue( BACKCOLOR , uno::Any( nMixedColor ) );
+}
+
+uno::Reference< container::XIndexAccess >
+ScVbaInterior::getPalette() const
+{
+ if ( !m_pScDoc )
+ throw uno::RuntimeException();
+ ScDocShell* pShell = m_pScDoc->GetDocumentShell();
+ ScVbaPalette aPalette( pShell );
+ return aPalette.getPalette();
+}
+
+void SAL_CALL
+ScVbaInterior::setColorIndex( const css::uno::Any& _colorindex )
+{
+ sal_Int32 nIndex = 0;
+ _colorindex >>= nIndex;
+
+ // hackly for excel::XlColorIndex::xlColorIndexNone
+ if( nIndex == excel::XlColorIndex::xlColorIndexNone )
+ {
+ m_xProps->setPropertyValue( BACKCOLOR, uno::Any( sal_Int32( -1 ) ) );
+ }
+ else
+ {
+ // setColor expects colors in XL RGB values
+ // #FIXME this is daft we convert OO RGB val to XL RGB val and
+ // then back again to OO RGB value
+ setColor( OORGBToXLRGB( GetIndexColor( nIndex ) ) );
+ }
+}
+uno::Any
+ScVbaInterior::GetIndexColor( sal_Int32 nColorIndex )
+{
+ sal_Int32 nIndex = nColorIndex;
+ // #FIXME xlColorIndexAutomatic & xlColorIndexNone are not really
+ // handled properly here
+ if ( !nIndex || ( nIndex == excel::XlColorIndex::xlColorIndexAutomatic ) || ( nIndex == excel::XlColorIndex::xlColorIndexNone ) )
+ nIndex = 2; // default is white ( this maybe will probably break, e.g. we may at some stage need to know what this interior is, a cell or something else and then pick a default colour based on that )
+ --nIndex; // OOo indices are zero bases
+ uno::Reference< container::XIndexAccess > xIndex = getPalette();
+ return xIndex->getByIndex( nIndex );
+}
+
+sal_Int32
+ScVbaInterior::GetColorIndex( const sal_Int32 nColor )
+{
+ uno::Reference< container::XIndexAccess > xIndex = getPalette();
+ sal_Int32 nElems = xIndex->getCount();
+ sal_Int32 nIndex = -1;
+ for ( sal_Int32 count=0; count<nElems; ++count )
+ {
+ sal_Int32 nPaletteColor = 0;
+ xIndex->getByIndex( count ) >>= nPaletteColor;
+ if ( nPaletteColor == nColor )
+ {
+ nIndex = count + 1; // 1 based
+ break;
+ }
+ }
+ return nIndex;
+}
+
+uno::Any SAL_CALL
+ScVbaInterior::getColorIndex()
+{
+ sal_Int32 nColor = 0;
+ // hackly for excel::XlColorIndex::xlColorIndexNone
+ uno::Any aColor = m_xProps->getPropertyValue( BACKCOLOR );
+ if( ( aColor >>= nColor ) && ( nColor == -1 ) )
+ {
+ nColor = excel::XlColorIndex::xlColorIndexNone;
+ return uno::Any( nColor );
+ }
+
+ // getColor returns Xl ColorValue, need to convert it to OO val
+ // as the palette deals with OO RGB values
+ // #FIXME this is daft in getColor we convert OO RGB val to XL RGB val
+ // and then back again to OO RGB value
+ XLRGBToOORGB( getColor() ) >>= nColor;
+
+ return uno::Any( GetColorIndex( nColor ) );
+}
+Color
+ScVbaInterior::GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt32 nXclPattern )
+{
+ // 0x00 == 0% transparence (full rPattColor)
+ // 0x80 == 100% transparence (full rBackColor)
+ static const sal_uInt8 pnRatioTable[] =
+ {
+ 0x80, 0x00, 0x40, 0x20, 0x60, 0x40, 0x40, 0x40, // 00 - 07
+ 0x40, 0x40, 0x20, 0x60, 0x60, 0x60, 0x60, 0x48, // 08 - 15
+ 0x50, 0x70, 0x78 // 16 - 18
+ };
+ return ( nXclPattern < SAL_N_ELEMENTS( pnRatioTable ) ) ?
+ GetMixedColor( rPattColor, rBackColor, pnRatioTable[ nXclPattern ] ) : rPattColor;
+}
+Color
+ScVbaInterior::GetMixedColor( const Color& rFore, const Color& rBack, sal_uInt8 nTrans )
+{
+ return Color(
+ ColorTransparency, nTrans,
+ GetMixedColorComp( rFore.GetRed(), rBack.GetRed(), nTrans ),
+ GetMixedColorComp( rFore.GetGreen(), rBack.GetGreen(), nTrans ),
+ GetMixedColorComp( rFore.GetBlue(), rBack.GetBlue(), nTrans ));
+}
+sal_uInt8
+ScVbaInterior::GetMixedColorComp( sal_uInt8 nFore, sal_uInt8 nBack, sal_uInt8 nTrans )
+{
+ sal_uInt32 nTemp = ((static_cast< sal_Int32 >( nBack ) - nFore) * nTrans) / 0x80 + nFore;
+ return static_cast< sal_uInt8 >( nTemp );
+}
+uno::Reference< container::XNameContainer >
+ScVbaInterior::GetAttributeContainer()
+{
+ return uno::Reference < container::XNameContainer > ( m_xProps->getPropertyValue("UserDefinedAttributes"), uno::UNO_QUERY_THROW );
+}
+sal_Int32
+ScVbaInterior::GetAttributeData( uno::Any const & aValue )
+{
+ xml::AttributeData aDataValue;
+ if( aValue >>= aDataValue )
+ {
+ return aDataValue.Value.toInt32();
+ }
+ return 0;
+}
+uno::Any
+ScVbaInterior::SetAttributeData( sal_Int32 nValue )
+{
+ xml::AttributeData aAttributeData;
+ aAttributeData.Type = "sal_Int32";
+ aAttributeData.Value = OUString::number( nValue );
+ return uno::Any( aAttributeData );
+}
+uno::Any
+ScVbaInterior::GetUserDefinedAttributes( const OUString& sName )
+{
+ uno::Reference< container::XNameContainer > xNameContainer( GetAttributeContainer(), uno::UNO_SET_THROW );
+ if( xNameContainer->hasByName( sName ) )
+ {
+ return xNameContainer->getByName( sName );
+ }
+ return uno::Any();
+}
+void
+ScVbaInterior::SetUserDefinedAttributes( const OUString& sName, const uno::Any& aValue )
+{
+ if( aValue.hasValue() )
+ {
+ uno::Reference< container::XNameContainer > xNameContainer( GetAttributeContainer(), uno::UNO_SET_THROW );
+ if( xNameContainer->hasByName( sName ) )
+ xNameContainer->removeByName( sName );
+ xNameContainer->insertByName( sName, aValue );
+ m_xProps->setPropertyValue("UserDefinedAttributes", uno::Any( xNameContainer ) );
+ }
+}
+// OOo do not support below API
+uno::Any SAL_CALL
+ScVbaInterior::getPattern()
+{
+ // XlPattern
+ uno::Any aPattern = GetUserDefinedAttributes( PATTERN );
+ if( aPattern.hasValue() )
+ return uno::Any( GetAttributeData( aPattern ) );
+ return uno::Any( excel::XlPattern::xlPatternNone );
+}
+void SAL_CALL
+ScVbaInterior::setPattern( const uno::Any& _pattern )
+{
+ if( !(_pattern >>= m_nPattern) )
+ throw uno::RuntimeException("Invalid Pattern index" );
+
+ SetUserDefinedAttributes( PATTERN, SetAttributeData( m_nPattern ) );
+ SetMixedColor();
+
+}
+Color
+ScVbaInterior::GetBackColor()
+{
+ sal_Int32 nColor = 0;
+ Color aBackColor;
+ uno::Any aColor = GetUserDefinedAttributes( BACKCOLOR );
+ if( aColor.hasValue() )
+ {
+ nColor = GetAttributeData( aColor );
+ aBackColor = Color(ColorTransparency, nColor);
+ }
+ else
+ {
+ uno::Any aAny = OORGBToXLRGB( m_xProps->getPropertyValue( BACKCOLOR ) );
+ if( aAny >>= nColor )
+ {
+ nColor = XLRGBToOORGB( nColor );
+ aBackColor = Color(ColorTransparency, nColor);
+ SetUserDefinedAttributes( BACKCOLOR, SetAttributeData( nColor ) );
+ }
+ }
+ return aBackColor;
+}
+uno::Any SAL_CALL
+ScVbaInterior::getPatternColor()
+{
+ // 0 is the default color. no filled.
+ uno::Any aPatternColor = GetUserDefinedAttributes( PATTERNCOLOR );
+ if( aPatternColor.hasValue() )
+ {
+ sal_uInt32 nPatternColor = GetAttributeData( aPatternColor );
+ return uno::Any( OORGBToXLRGB( Color(ColorTransparency, nPatternColor) ) );
+ }
+ return uno::Any( sal_Int32( 0 ) );
+}
+void SAL_CALL
+ScVbaInterior::setPatternColor( const uno::Any& _patterncolor )
+{
+ sal_Int32 nPattColor = 0;
+ if( !(_patterncolor >>= nPattColor) )
+ throw uno::RuntimeException("Invalid Pattern Color" );
+
+ SetUserDefinedAttributes( PATTERNCOLOR, SetAttributeData( XLRGBToOORGB( nPattColor ) ) );
+ SetMixedColor();
+
+}
+uno::Any SAL_CALL
+ScVbaInterior::getPatternColorIndex()
+{
+ sal_Int32 nColor = 0;
+ XLRGBToOORGB( getPatternColor() ) >>= nColor;
+
+ return uno::Any( GetColorIndex( nColor ) );
+}
+void SAL_CALL
+ScVbaInterior::setPatternColorIndex( const uno::Any& _patterncolorindex )
+{
+ sal_Int32 nColorIndex = 0;
+ if( !(_patterncolorindex >>= nColorIndex) )
+ throw uno::RuntimeException("Invalid Pattern Color" );
+
+ if( nColorIndex == 0 )
+ return;
+ Color nPattColor;
+ GetIndexColor( nColorIndex ) >>= nPattColor;
+ setPatternColor( uno::Any( OORGBToXLRGB( nPattColor ) ) );
+
+}
+
+uno::Any SAL_CALL ScVbaInterior::getThemeColor()
+{
+ // Just a stub for now.
+ return uno::Any(static_cast<sal_Int32>(0));
+}
+
+void SAL_CALL ScVbaInterior::setThemeColor(const uno::Any& /*rAny*/)
+{
+ // Just a stub for now.
+}
+
+uno::Any SAL_CALL ScVbaInterior::getTintAndShade()
+{
+ // Just a stub for now.
+ return uno::Any(static_cast<double>(0));
+}
+
+void SAL_CALL ScVbaInterior::setTintAndShade(const uno::Any& /*rAny*/)
+{
+ // Just a stub for now.
+}
+
+uno::Any SAL_CALL ScVbaInterior::getPatternTintAndShade()
+{
+ // Just a stub for now.
+ return uno::Any(static_cast<double>(0));
+}
+
+void SAL_CALL ScVbaInterior::setPatternTintAndShade(const uno::Any& /*rAny*/)
+{
+ // Just a stub for now.
+}
+
+OUString
+ScVbaInterior::getServiceImplName()
+{
+ return "ScVbaInterior";
+}
+
+uno::Sequence< OUString >
+ScVbaInterior::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Interior"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbainterior.hxx b/sc/source/ui/vba/vbainterior.hxx
new file mode 100644
index 0000000000..eb645fcf50
--- /dev/null
+++ b/sc/source/ui/vba/vbainterior.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XInterior.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+#include <tools/color.hxx>
+
+class ScDocument;
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XInterior > ScVbaInterior_BASE;
+
+class ScVbaInterior final : public ScVbaInterior_BASE
+{
+ css::uno::Reference< css::beans::XPropertySet > m_xProps;
+ ScDocument* m_pScDoc;
+ Color m_aPattColor;
+ sal_Int32 m_nPattern;
+
+ css::uno::Reference< css::container::XIndexAccess > getPalette() const;
+ css::uno::Reference< css::container::XNameContainer > GetAttributeContainer();
+ static css::uno::Any SetAttributeData( sal_Int32 nValue );
+ static sal_Int32 GetAttributeData( css::uno::Any const & aValue );
+ Color GetBackColor();
+ static Color GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt32 nXclPattern );
+ static Color GetMixedColor( const Color& rFore, const Color& rBack, sal_uInt8 nTrans );
+ static sal_uInt8 GetMixedColorComp( sal_uInt8 nFore, sal_uInt8 nBack, sal_uInt8 nTrans );
+ css::uno::Any GetIndexColor( sal_Int32 nColorIndex );
+ sal_Int32 GetColorIndex( const sal_Int32 nColor );
+ css::uno::Any GetUserDefinedAttributes( const OUString& sName );
+ void SetUserDefinedAttributes( const OUString& sName, const css::uno::Any& aValue );
+ void SetMixedColor();
+
+public:
+ /// @throws css::lang::IllegalArgumentException
+ ScVbaInterior( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::beans::XPropertySet > xProps, ScDocument* pScDoc = nullptr);
+
+ virtual css::uno::Any SAL_CALL getColor() override ;
+ virtual void SAL_CALL setColor( const css::uno::Any& _color ) override ;
+
+ virtual css::uno::Any SAL_CALL getColorIndex() override;
+ virtual void SAL_CALL setColorIndex( const css::uno::Any& _colorindex ) override;
+ virtual css::uno::Any SAL_CALL getPattern() override;
+ virtual void SAL_CALL setPattern( const css::uno::Any& _pattern ) override;
+ virtual css::uno::Any SAL_CALL getPatternColor() override;
+ virtual void SAL_CALL setPatternColor( const css::uno::Any& _patterncolor ) override;
+ virtual css::uno::Any SAL_CALL getPatternColorIndex() override;
+ virtual void SAL_CALL setPatternColorIndex( const css::uno::Any& _patterncolorindex ) override;
+ css::uno::Any SAL_CALL getThemeColor() override;
+ void SAL_CALL setThemeColor(const css::uno::Any& rAny) override;
+ css::uno::Any SAL_CALL getTintAndShade() override;
+ void SAL_CALL setTintAndShade(const css::uno::Any& rAny) override;
+ css::uno::Any SAL_CALL getPatternTintAndShade() override;
+ void SAL_CALL setPatternTintAndShade(const css::uno::Any& rAny) override;
+ //XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbalineshape.cxx b/sc/source/ui/vba/vbalineshape.cxx
new file mode 100644
index 0000000000..fac0f17155
--- /dev/null
+++ b/sc/source/ui/vba/vbalineshape.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 "vbalineshape.hxx"
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+/*
+ * This is implemented as a new class in order to provide XTypeProvider
+ * interface. This is needed by TypeOf ... Is ... basic operator.
+ */
+
+ScVbaLineShape::ScVbaLineShape( const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< drawing::XShape >& xShape, const uno::Reference< drawing::XShapes >& xShapes, const uno::Reference< frame::XModel >& xModel ) : LineShapeImpl_BASE( uno::Reference< XHelperInterface >(), xContext, xShape, xShapes, xModel, ScVbaShape::getType( xShape ) )
+{}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbalineshape.hxx b/sc/source/ui/vba/vbalineshape.hxx
new file mode 100644
index 0000000000..953bc87a3e
--- /dev/null
+++ b/sc/source/ui/vba/vbalineshape.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <ooo/vba/msforms/XLine.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <vbahelper/vbashape.hxx>
+
+typedef cppu::ImplInheritanceHelper< ScVbaShape, ov::msforms::XLine > LineShapeImpl_BASE;
+
+class ScVbaLineShape : public LineShapeImpl_BASE
+{
+public:
+ ScVbaLineShape( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::drawing::XShapes >& xShapes, const css::uno::Reference< css::frame::XModel >& xModel );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenu.cxx b/sc/source/ui/vba/vbamenu.cxx
new file mode 100644
index 0000000000..d2bac36933
--- /dev/null
+++ b/sc/source/ui/vba/vbamenu.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "vbamenu.hxx"
+#include "vbamenuitems.hxx"
+#include <ooo/vba/XCommandBarControls.hpp>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+ScVbaMenu::ScVbaMenu( const uno::Reference< ov::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, uno::Reference< XCommandBarControl > xCommandBarControl ) : Menu_BASE( rParent, rContext ), m_xCommandBarControl(std::move( xCommandBarControl ))
+{
+}
+
+OUString SAL_CALL
+ScVbaMenu::getCaption()
+{
+ return m_xCommandBarControl->getCaption();
+}
+
+void SAL_CALL
+ScVbaMenu::setCaption( const OUString& _caption )
+{
+ m_xCommandBarControl->setCaption( _caption );
+}
+
+void SAL_CALL
+ScVbaMenu::Delete( )
+{
+ m_xCommandBarControl->Delete();
+}
+
+uno::Any SAL_CALL
+ScVbaMenu::MenuItems( const uno::Any& aIndex )
+{
+ uno::Reference< XCommandBarControls > xCommandBarControls( m_xCommandBarControl->Controls( uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XMenuItems > xMenuItems( new ScVbaMenuItems( this, mxContext, xCommandBarControls ) );
+ if( aIndex.hasValue() )
+ {
+ return xMenuItems->Item( aIndex, uno::Any() );
+ }
+ return uno::Any( xMenuItems );
+}
+
+OUString
+ScVbaMenu::getServiceImplName()
+{
+ return "ScVbaMenu";
+}
+
+uno::Sequence<OUString>
+ScVbaMenu::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Menu"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenu.hxx b/sc/source/ui/vba/vbamenu.hxx
new file mode 100644
index 0000000000..2e4b897428
--- /dev/null
+++ b/sc/source/ui/vba/vbamenu.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/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XMenu.hpp>
+#include <ooo/vba/XCommandBarControl.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XMenu > Menu_BASE;
+
+class ScVbaMenu : public Menu_BASE
+{
+private:
+ css::uno::Reference< ov::XCommandBarControl > m_xCommandBarControl;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaMenu( const css::uno::Reference< ov::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, css::uno::Reference< ov::XCommandBarControl > xCommandBarControl );
+
+ virtual OUString SAL_CALL getCaption() override;
+ virtual void SAL_CALL setCaption( const OUString& _caption ) override;
+
+ virtual void SAL_CALL Delete( ) override;
+ virtual css::uno::Any SAL_CALL MenuItems( const css::uno::Any& aIndex ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenubar.cxx b/sc/source/ui/vba/vbamenubar.cxx
new file mode 100644
index 0000000000..50f69fdbf6
--- /dev/null
+++ b/sc/source/ui/vba/vbamenubar.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/.
+ */
+#include "vbamenubar.hxx"
+#include "vbamenus.hxx"
+#include <ooo/vba/XCommandBarControls.hpp>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+ScVbaMenuBar::ScVbaMenuBar( const uno::Reference< ov::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, uno::Reference< XCommandBar > xCommandBar ) : MenuBar_BASE(rParent, rContext), m_xCommandBar(std::move(xCommandBar))
+{
+}
+
+uno::Any SAL_CALL
+ScVbaMenuBar::Menus( const uno::Any& aIndex )
+{
+ uno::Reference< XCommandBarControls > xCommandBarControls( m_xCommandBar->Controls( uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XMenus > xMenus( new ScVbaMenus( this, mxContext, xCommandBarControls ) );
+ if( aIndex.hasValue() )
+ {
+ return xMenus->Item( aIndex, uno::Any() );
+ }
+ return uno::Any( xMenus );
+}
+
+OUString
+ScVbaMenuBar::getServiceImplName()
+{
+ return "ScVbaMenuBar";
+}
+
+uno::Sequence<OUString>
+ScVbaMenuBar::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.MenuBar"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenubar.hxx b/sc/source/ui/vba/vbamenubar.hxx
new file mode 100644
index 0000000000..d373f0252d
--- /dev/null
+++ b/sc/source/ui/vba/vbamenubar.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/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XMenuBar.hpp>
+#include <ooo/vba/XCommandBar.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XMenuBar > MenuBar_BASE;
+
+class ScVbaMenuBar : public MenuBar_BASE
+{
+private:
+ css::uno::Reference< ov::XCommandBar > m_xCommandBar;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaMenuBar( const css::uno::Reference< ov::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, css::uno::Reference< ov::XCommandBar > xCommandBar );
+
+ virtual css::uno::Any SAL_CALL Menus( const css::uno::Any& aIndex ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenubars.cxx b/sc/source/ui/vba/vbamenubars.cxx
new file mode 100644
index 0000000000..57f66644fc
--- /dev/null
+++ b/sc/source/ui/vba/vbamenubars.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/.
+ */
+#include "vbamenubars.hxx"
+#include "vbamenubar.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XlSheetType.hpp>
+#include <ooo/vba/XCommandBars.hpp>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+namespace {
+
+class MenuBarEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration >
+{
+ uno::Reference< XHelperInterface > m_xParent;
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< container::XEnumeration > m_xEnumeration;
+public:
+ /// @throws uno::RuntimeException
+ MenuBarEnumeration( uno::Reference< XHelperInterface > xParent, uno::Reference< uno::XComponentContext > xContext, uno::Reference< container::XEnumeration > xEnumeration) : m_xParent(std::move( xParent )), m_xContext(std::move( xContext )), m_xEnumeration(std::move( xEnumeration ))
+ {
+ }
+ virtual sal_Bool SAL_CALL hasMoreElements() override
+ {
+ return m_xEnumeration->hasMoreElements();
+ }
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ // FIXME: should be add menubar
+ if( !hasMoreElements() )
+ throw container::NoSuchElementException();
+
+ uno::Reference< XCommandBar > xCommandBar( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XMenuBar > xMenuBar( new ScVbaMenuBar( m_xParent, m_xContext, xCommandBar ) );
+ return uno::Any( xMenuBar );
+ }
+};
+
+}
+
+ScVbaMenuBars::ScVbaMenuBars( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< XCommandBars > xCommandBars ) : MenuBars_BASE( xParent, xContext, uno::Reference< container::XIndexAccess>() ), m_xCommandBars(std::move( xCommandBars ))
+{
+}
+
+ScVbaMenuBars::~ScVbaMenuBars()
+{
+}
+
+// XEnumerationAccess
+uno::Type SAL_CALL
+ScVbaMenuBars::getElementType()
+{
+ return cppu::UnoType<excel::XMenuBar>::get();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaMenuBars::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xCommandBars, uno::UNO_QUERY_THROW );
+ return uno::Reference< container::XEnumeration >( new MenuBarEnumeration( this, mxContext, xEnumAccess->createEnumeration() ) );
+}
+
+uno::Any
+ScVbaMenuBars::createCollectionObject( const uno::Any& aSource )
+{
+ // make no sense
+ return aSource;
+}
+
+sal_Int32 SAL_CALL
+ScVbaMenuBars::getCount()
+{
+ return m_xCommandBars->getCount();
+}
+
+// ScVbaCollectionBaseImpl
+uno::Any SAL_CALL
+ScVbaMenuBars::Item( const uno::Any& aIndex, const uno::Any& /*aIndex2*/ )
+{
+ sal_Int16 nIndex = 0;
+ aIndex >>= nIndex;
+ if( nIndex == excel::XlSheetType::xlWorksheet )
+ {
+ uno::Any aSource;
+ aSource <<= OUString( "Worksheet Menu Bar" );
+ uno::Reference< XCommandBar > xCommandBar( m_xCommandBars->Item( aSource, uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XMenuBar > xMenuBar( new ScVbaMenuBar( this, mxContext, xCommandBar ) );
+ return uno::Any( xMenuBar );
+ }
+
+ throw uno::RuntimeException("Not implemented" );
+}
+
+// XHelperInterface
+OUString
+ScVbaMenuBars::getServiceImplName()
+{
+ return "ScVbaMenuBars";
+}
+
+uno::Sequence<OUString>
+ScVbaMenuBars::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.MenuBars"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenubars.hxx b/sc/source/ui/vba/vbamenubars.hxx
new file mode 100644
index 0000000000..2b6e7e7db7
--- /dev/null
+++ b/sc/source/ui/vba/vbamenubars.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/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XMenuBars.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace ooo::vba { class XCommandBars; }
+
+typedef CollTestImplHelper< ov::excel::XMenuBars > MenuBars_BASE;
+
+class ScVbaMenuBars : public MenuBars_BASE
+{
+private:
+ css::uno::Reference< ov::XCommandBars > m_xCommandBars;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaMenuBars( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< ov::XCommandBars > xCommandBars );
+ virtual ~ScVbaMenuBars() override;
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& aIndex, const css::uno::Any& /*aIndex2*/ ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenuitem.cxx b/sc/source/ui/vba/vbamenuitem.cxx
new file mode 100644
index 0000000000..522db66b09
--- /dev/null
+++ b/sc/source/ui/vba/vbamenuitem.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/.
+ */
+#include <utility>
+
+#include "vbamenuitem.hxx"
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+ScVbaMenuItem::ScVbaMenuItem( const uno::Reference< ov::XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, uno::Reference< XCommandBarControl > xCommandBarControl ) : MenuItem_BASE( rParent, rContext ), m_xCommandBarControl(std::move( xCommandBarControl ))
+{
+}
+
+OUString SAL_CALL
+ScVbaMenuItem::getCaption()
+{
+ return m_xCommandBarControl->getCaption();
+}
+
+void SAL_CALL
+ScVbaMenuItem::setCaption( const OUString& _caption )
+{
+ m_xCommandBarControl->setCaption( _caption );
+}
+
+OUString SAL_CALL
+ScVbaMenuItem::getOnAction()
+{
+ return m_xCommandBarControl->getOnAction();
+}
+
+void SAL_CALL
+ScVbaMenuItem::setOnAction( const OUString& _onaction )
+{
+ m_xCommandBarControl->setOnAction( _onaction );
+}
+
+void SAL_CALL
+ScVbaMenuItem::Delete( )
+{
+ m_xCommandBarControl->Delete();
+}
+
+OUString
+ScVbaMenuItem::getServiceImplName()
+{
+ return "ScVbaMenuItem";
+}
+
+uno::Sequence<OUString>
+ScVbaMenuItem::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.MenuItem"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenuitem.hxx b/sc/source/ui/vba/vbamenuitem.hxx
new file mode 100644
index 0000000000..0c85614211
--- /dev/null
+++ b/sc/source/ui/vba/vbamenuitem.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XMenuItem.hpp>
+#include <ooo/vba/XCommandBarControl.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XMenuItem > MenuItem_BASE;
+
+class ScVbaMenuItem : public MenuItem_BASE
+{
+private:
+ css::uno::Reference< ov::XCommandBarControl > m_xCommandBarControl;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaMenuItem( const css::uno::Reference< ov::XHelperInterface >& rParent, const css::uno::Reference< css::uno::XComponentContext >& rContext, css::uno::Reference< ov::XCommandBarControl > xCommandBarControl );
+
+ virtual OUString SAL_CALL getCaption() override;
+ virtual void SAL_CALL setCaption( const OUString& _caption ) override;
+ virtual OUString SAL_CALL getOnAction() override;
+ virtual void SAL_CALL setOnAction( const OUString& _onaction ) override;
+
+ virtual void SAL_CALL Delete( ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenuitems.cxx b/sc/source/ui/vba/vbamenuitems.cxx
new file mode 100644
index 0000000000..10d6fc33e0
--- /dev/null
+++ b/sc/source/ui/vba/vbamenuitems.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include "vbamenuitems.hxx"
+#include "vbamenuitem.hxx"
+#include "vbamenu.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/office/MsoControlType.hpp>
+#include <ooo/vba/XCommandBarControls.hpp>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+typedef ::cppu::WeakImplHelper< container::XEnumeration > MenuEnumeration_BASE;
+
+namespace {
+
+class MenuEnumeration : public MenuEnumeration_BASE
+{
+ uno::Reference< XHelperInterface > m_xParent;
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< container::XEnumeration > m_xEnumeration;
+public:
+ /// @throws uno::RuntimeException
+ MenuEnumeration( uno::Reference< XHelperInterface > xParent, uno::Reference< uno::XComponentContext > xContext, uno::Reference< container::XEnumeration > xEnumeration) : m_xParent(std::move( xParent )), m_xContext(std::move( xContext )), m_xEnumeration(std::move( xEnumeration ))
+ {
+ }
+ virtual sal_Bool SAL_CALL hasMoreElements() override
+ {
+ return m_xEnumeration->hasMoreElements();
+ }
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ // FIXME: should be add menu
+ if( !hasMoreElements() )
+ throw container::NoSuchElementException();
+
+ uno::Reference< XCommandBarControl > xCommandBarControl( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ if( xCommandBarControl->getType() == office::MsoControlType::msoControlPopup )
+ {
+ uno::Reference< excel::XMenu > xMenu( new ScVbaMenu( m_xParent, m_xContext, xCommandBarControl ) );
+ return uno::Any( xMenu );
+ }
+ else if( xCommandBarControl->getType() == office::MsoControlType::msoControlButton )
+ {
+ uno::Reference< excel::XMenuItem > xMenuItem( new ScVbaMenuItem( m_xParent, m_xContext, xCommandBarControl ) );
+ return uno::Any( xMenuItem );
+ }
+ nextElement();
+
+ return uno::Any();
+ }
+};
+
+}
+
+ScVbaMenuItems::ScVbaMenuItems( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< XCommandBarControls > xCommandBarControls ) : MenuItems_BASE( xParent, xContext, uno::Reference< container::XIndexAccess>() ), m_xCommandBarControls(std::move( xCommandBarControls ))
+{
+}
+
+// XEnumerationAccess
+uno::Type SAL_CALL
+ScVbaMenuItems::getElementType()
+{
+ return cppu::UnoType<excel::XMenuItem>::get();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaMenuItems::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xCommandBarControls, uno::UNO_QUERY_THROW );
+ return uno::Reference< container::XEnumeration >( new MenuEnumeration( this, mxContext, xEnumAccess->createEnumeration() ) );
+}
+
+uno::Any
+ScVbaMenuItems::createCollectionObject( const uno::Any& aSource )
+{
+ // make no sense
+ return aSource;
+}
+
+sal_Int32 SAL_CALL
+ScVbaMenuItems::getCount()
+{
+ // FIXME: should check if it is a popup menu
+ return m_xCommandBarControls->getCount();
+}
+
+// ScVbaCollectionBaseImpl
+uno::Any SAL_CALL
+ScVbaMenuItems::Item( const uno::Any& aIndex, const uno::Any& /*aIndex2*/ )
+{
+ uno::Reference< XCommandBarControl > xCommandBarControl( m_xCommandBarControls->Item( aIndex, uno::Any() ), uno::UNO_QUERY_THROW );
+ if( xCommandBarControl->getType() == office::MsoControlType::msoControlPopup )
+ return uno::Any( uno::Reference< excel::XMenu > ( new ScVbaMenu( this, mxContext, xCommandBarControl ) ) );
+ else if( xCommandBarControl->getType() == office::MsoControlType::msoControlButton )
+ return uno::Any( uno::Reference< excel::XMenuItem > ( new ScVbaMenuItem( this, mxContext, xCommandBarControl ) ) );
+ throw uno::RuntimeException();
+}
+
+uno::Reference< excel::XMenuItem > SAL_CALL ScVbaMenuItems::Add( const OUString& Caption, const css::uno::Any& OnAction, const css::uno::Any& /*ShortcutKey*/, const css::uno::Any& Before, const css::uno::Any& Restore, const css::uno::Any& /*StatusBar*/, const css::uno::Any& /*HelpFile*/, const css::uno::Any& /*HelpContextID*/ )
+{
+ uno::Reference< XCommandBarControl > xCommandBarControl = m_xCommandBarControls->Add(
+ uno::Any( office::MsoControlType::msoControlButton ),
+ uno::Any(), uno::Any(), Before, Restore );
+ xCommandBarControl->setCaption( Caption );
+ if( OnAction.hasValue() )
+ {
+ OUString sAction;
+ OnAction >>= sAction;
+ xCommandBarControl->setOnAction( sAction );
+ }
+ return uno::Reference< excel::XMenuItem >( new ScVbaMenuItem( this, mxContext, xCommandBarControl ) );
+}
+
+// XHelperInterface
+OUString
+ScVbaMenuItems::getServiceImplName()
+{
+ return "ScVbaMenuItems";
+}
+
+uno::Sequence<OUString>
+ScVbaMenuItems::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.MenuItems"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenuitems.hxx b/sc/source/ui/vba/vbamenuitems.hxx
new file mode 100644
index 0000000000..55e6fe7822
--- /dev/null
+++ b/sc/source/ui/vba/vbamenuitems.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/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XMenuItems.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace ooo::vba { class XCommandBarControls; }
+namespace ooo::vba::excel { class XMenuItem; }
+
+typedef CollTestImplHelper< ov::excel::XMenuItems > MenuItems_BASE;
+
+class ScVbaMenuItems : public MenuItems_BASE
+{
+private:
+ css::uno::Reference< ov::XCommandBarControls > m_xCommandBarControls;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaMenuItems( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< ov::XCommandBarControls > xCommandBarControls );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ // Methods
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index, const css::uno::Any& /*Index2*/ ) override;
+ virtual css::uno::Reference< ov::excel::XMenuItem > SAL_CALL Add( const OUString& Caption, const css::uno::Any& OnAction, const css::uno::Any& ShortcutKey, const css::uno::Any& Before, const css::uno::Any& Restore, const css::uno::Any& StatusBar, const css::uno::Any& HelpFile, const css::uno::Any& HelpContextID ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenus.cxx b/sc/source/ui/vba/vbamenus.cxx
new file mode 100644
index 0000000000..b9b2c08fa0
--- /dev/null
+++ b/sc/source/ui/vba/vbamenus.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 "vbamenus.hxx"
+#include "vbamenu.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/office/MsoControlType.hpp>
+#include <ooo/vba/XCommandBarControls.hpp>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+typedef ::cppu::WeakImplHelper< container::XEnumeration > MenuEnumeration_BASE;
+
+namespace {
+
+class MenuEnumeration : public MenuEnumeration_BASE
+{
+ uno::Reference< XHelperInterface > m_xParent;
+ uno::Reference< uno::XComponentContext > m_xContext;
+ uno::Reference< container::XEnumeration > m_xEnumeration;
+public:
+ /// @throws uno::RuntimeException
+ MenuEnumeration( uno::Reference< XHelperInterface > xParent, uno::Reference< uno::XComponentContext > xContext, uno::Reference< container::XEnumeration > xEnumeration) : m_xParent(std::move( xParent )), m_xContext(std::move( xContext )), m_xEnumeration(std::move( xEnumeration ))
+ {
+ }
+ virtual sal_Bool SAL_CALL hasMoreElements() override
+ {
+ return m_xEnumeration->hasMoreElements();
+ }
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ // FIXME: should be add menu
+ if( !hasMoreElements() )
+ throw container::NoSuchElementException();
+
+ uno::Reference< XCommandBarControl > xCommandBarControl( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ if( xCommandBarControl->getType() == office::MsoControlType::msoControlPopup )
+ {
+ uno::Reference< excel::XMenu > xMenu( new ScVbaMenu( m_xParent, m_xContext, xCommandBarControl ) );
+ return uno::Any( xMenu );
+ }
+ nextElement();
+
+ return uno::Any();
+ }
+};
+
+}
+
+ScVbaMenus::ScVbaMenus( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< XCommandBarControls > xCommandBarControls ) : Menus_BASE( xParent, xContext, uno::Reference< container::XIndexAccess>() ), m_xCommandBarControls(std::move( xCommandBarControls ))
+{
+}
+
+// XEnumerationAccess
+uno::Type SAL_CALL
+ScVbaMenus::getElementType()
+{
+ return cppu::UnoType<excel::XMenu>::get();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaMenus::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xCommandBarControls, uno::UNO_QUERY_THROW );
+ return uno::Reference< container::XEnumeration >( new MenuEnumeration( this, mxContext, xEnumAccess->createEnumeration() ) );
+}
+
+uno::Any
+ScVbaMenus::createCollectionObject( const uno::Any& aSource )
+{
+ // make no sense
+ return aSource;
+}
+
+sal_Int32 SAL_CALL
+ScVbaMenus::getCount()
+{
+ // FIXME: should check if it is a popup menu
+ return m_xCommandBarControls->getCount();
+}
+
+// ScVbaCollectionBaseImpl
+uno::Any SAL_CALL
+ScVbaMenus::Item( const uno::Any& aIndex, const uno::Any& /*aIndex2*/ )
+{
+ uno::Reference< XCommandBarControl > xCommandBarControl( m_xCommandBarControls->Item( aIndex, uno::Any() ), uno::UNO_QUERY_THROW );
+ if( xCommandBarControl->getType() != office::MsoControlType::msoControlPopup )
+ throw uno::RuntimeException();
+ return uno::Any( uno::Reference< excel::XMenu > ( new ScVbaMenu( this, mxContext, xCommandBarControl ) ) );
+}
+
+uno::Reference< excel::XMenu > SAL_CALL ScVbaMenus::Add( const OUString& Caption, const css::uno::Any& Before, const css::uno::Any& Restore )
+{
+ uno::Reference< XCommandBarControl > xCommandBarControl = m_xCommandBarControls->Add(
+ uno::Any( office::MsoControlType::msoControlPopup ),
+ uno::Any(), uno::Any(), Before, Restore );
+ xCommandBarControl->setCaption( Caption );
+ return uno::Reference< excel::XMenu >( new ScVbaMenu( this, mxContext, xCommandBarControl ) );
+}
+
+// XHelperInterface
+OUString
+ScVbaMenus::getServiceImplName()
+{
+ return "ScVbaMenus";
+}
+
+uno::Sequence<OUString>
+ScVbaMenus::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Menus"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbamenus.hxx b/sc/source/ui/vba/vbamenus.hxx
new file mode 100644
index 0000000000..4d8f646254
--- /dev/null
+++ b/sc/source/ui/vba/vbamenus.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/.
+ */
+#pragma once
+
+#include <ooo/vba/excel/XMenus.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace ooo::vba { class XCommandBarControls; }
+namespace ooo::vba::excel { class XMenu; }
+
+typedef CollTestImplHelper< ov::excel::XMenus > Menus_BASE;
+
+class ScVbaMenus : public Menus_BASE
+{
+private:
+ css::uno::Reference< ov::XCommandBarControls > m_xCommandBarControls;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaMenus( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< ov::XCommandBarControls > xCommandBarControls );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ // Methods
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index, const css::uno::Any& /*Index2*/ ) override;
+ virtual css::uno::Reference< ov::excel::XMenu > SAL_CALL Add( const OUString& Caption, const css::uno::Any& Before, const css::uno::Any& Restore ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaname.cxx b/sc/source/ui/vba/vbaname.cxx
new file mode 100644
index 0000000000..98a242401e
--- /dev/null
+++ b/sc/source/ui/vba/vbaname.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 "excelvbahelper.hxx"
+#include "vbaname.hxx"
+#include "vbarange.hxx"
+#include <docsh.hxx>
+#include <rangenam.hxx>
+#include <nameuno.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+
+#include <comphelper/servicehelper.hxx>
+
+#include <memory>
+#include <utility>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaName::ScVbaName(const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::sheet::XNamedRange > xName,
+ css::uno::Reference< css::sheet::XNamedRanges > xNames,
+ css::uno::Reference< css::frame::XModel > xModel ):
+ NameImpl_BASE( xParent , xContext ),
+ mxModel(std::move( xModel )),
+ mxNamedRange(std::move( xName )),
+ mxNames(std::move( xNames ))
+{
+}
+
+ScVbaName::~ScVbaName()
+{
+}
+
+OUString
+ScVbaName::getName()
+{
+ return mxNamedRange->getName();
+}
+
+void
+ScVbaName::setName( const OUString & rName )
+{
+ mxNamedRange->setName( rName );
+}
+
+OUString
+ScVbaName::getNameLocal()
+{
+ return getName();
+}
+
+void
+ScVbaName::setNameLocal( const OUString & rName )
+{
+ setName( rName );
+}
+
+sal_Bool
+ScVbaName::getVisible()
+{
+ return true;
+}
+
+void
+ScVbaName::setVisible( sal_Bool /*bVisible*/ )
+{
+}
+
+OUString ScVbaName::getContent( const formula::FormulaGrammar::Grammar eGrammar )
+{
+ ScNamedRangeObj* pNamedRange = dynamic_cast< ScNamedRangeObj* >( mxNamedRange.get() );
+ OUString aContent;
+ if ( pNamedRange )
+ {
+ ScRangeData* pData = pNamedRange->GetRangeData_Impl();
+ if (pData)
+ aContent = pData->GetSymbol( eGrammar );
+ }
+ if (aContent.indexOf('=') != 0)
+ aContent = "=" + aContent;
+ return aContent;
+}
+
+void ScVbaName::setContent( const OUString& rContent, const formula::FormulaGrammar::Grammar eGrammar )
+{
+ OUString sContent( rContent );
+ if (sContent.startsWith("="))
+ sContent = sContent.copy(1);
+ ScNamedRangeObj* pNamedRange = dynamic_cast< ScNamedRangeObj* >( mxNamedRange.get() );
+
+ // We should be able to do the below by just setting calling SetCode on pNamedRange
+ // right?
+ if ( !(pNamedRange && pNamedRange->pDocShell) )
+ return;
+
+ ScDocument& rDoc = pNamedRange->pDocShell->GetDocument();
+ ScRangeData* pOldData = pNamedRange->GetRangeData_Impl();
+ if (pOldData)
+ {
+ // Shorter way of doing this ?
+ ScCompiler aComp( rDoc, pOldData->GetPos(), eGrammar );
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(sContent));
+ pOldData->SetCode(*pArray);
+ }
+}
+
+OUString
+ScVbaName::getValue()
+{
+ OUString sResult = getContent( formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
+
+ return sResult;
+}
+
+void
+ScVbaName::setValue( const OUString & rValue )
+{
+ setContent( rValue, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
+}
+
+OUString
+ScVbaName::getRefersTo()
+{
+ return getValue();
+}
+
+void
+ScVbaName::setRefersTo( const OUString & rRefersTo )
+{
+ setValue( rRefersTo );
+}
+
+OUString
+ScVbaName::getRefersToLocal()
+{
+ return getRefersTo();
+}
+
+void
+ScVbaName::setRefersToLocal( const OUString & rRefersTo )
+{
+ setRefersTo( rRefersTo );
+}
+
+OUString
+ScVbaName::getRefersToR1C1()
+{
+ OUString sResult = getContent( formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
+ return sResult;
+}
+
+void
+ScVbaName::setRefersToR1C1( const OUString & rRefersTo )
+{
+ setContent( rRefersTo, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
+}
+
+OUString
+ScVbaName::getRefersToR1C1Local()
+{
+ return getRefersToR1C1();
+}
+
+void
+ScVbaName::setRefersToR1C1Local( const OUString & rRefersTo )
+{
+ setRefersTo( rRefersTo );
+}
+
+css::uno::Reference< ov::excel::XRange >
+ScVbaName::getRefersToRange()
+{
+ uno::Reference< ov::excel::XRange > xRange = ScVbaRange::getRangeObjectForName(
+ mxContext, mxNamedRange->getName(), excel::getDocShell( mxModel ), formula::FormulaGrammar::CONV_XL_R1C1 );
+ return xRange;
+}
+
+void
+ScVbaName::Delete()
+{
+ mxNames->removeByName( mxNamedRange->getName() );
+}
+
+OUString
+ScVbaName::getServiceImplName()
+{
+ return "ScVbaName";
+}
+
+uno::Sequence< OUString >
+ScVbaName::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Name"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaname.hxx b/sc/source/ui/vba/vbaname.hxx
new file mode 100644
index 0000000000..9d12f2ac6b
--- /dev/null
+++ b/sc/source/ui/vba/vbaname.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XName.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+#include <formula/grammar.hxx>
+
+namespace com::sun::star::sheet { class XNamedRange; }
+namespace com::sun::star::sheet { class XNamedRanges; }
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XName > NameImpl_BASE;
+
+class ScVbaName : public NameImpl_BASE
+{
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::sheet::XNamedRange > mxNamedRange;
+ css::uno::Reference< css::sheet::XNamedRanges > mxNames;
+ OUString getContent( const formula::FormulaGrammar::Grammar eGrammar );
+ void setContent( const OUString& sContent, const formula::FormulaGrammar::Grammar eGrammar );
+public:
+ ScVbaName( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< css::sheet::XNamedRange > xName , css::uno::Reference< css::sheet::XNamedRanges > xNames , css::uno::Reference< css::frame::XModel > xModel );
+ virtual ~ScVbaName() override;
+
+ // Attributes
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString &rName ) override;
+ virtual OUString SAL_CALL getNameLocal() override;
+ virtual void SAL_CALL setNameLocal( const OUString &rName ) override;
+ virtual sal_Bool SAL_CALL getVisible() override;
+ virtual void SAL_CALL setVisible( sal_Bool bVisible ) override;
+ virtual OUString SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const OUString &rValue ) override;
+ virtual OUString SAL_CALL getRefersTo() override;
+ virtual void SAL_CALL setRefersTo( const OUString &rRefersTo ) override;
+ virtual OUString SAL_CALL getRefersToLocal() override;
+ virtual void SAL_CALL setRefersToLocal( const OUString &rRefersTo ) override;
+ virtual OUString SAL_CALL getRefersToR1C1() override;
+ virtual void SAL_CALL setRefersToR1C1( const OUString &rRefersTo ) override;
+ virtual OUString SAL_CALL getRefersToR1C1Local() override;
+ virtual void SAL_CALL setRefersToR1C1Local( const OUString &rRefersTo ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getRefersToRange() override;
+
+ // Methods
+ virtual void SAL_CALL Delete() override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbanames.cxx b/sc/source/ui/vba/vbanames.cxx
new file mode 100644
index 0000000000..074cfe60d6
--- /dev/null
+++ b/sc/source/ui/vba/vbanames.cxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XNamedRange.hpp>
+#include <com/sun/star/sheet/XNamedRanges.hpp>
+
+#include "excelvbahelper.hxx"
+#include "vbanames.hxx"
+#include "vbaname.hxx"
+#include "vbarange.hxx"
+#include <tabvwsh.hxx>
+#include <utility>
+#include <viewdata.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <cellsuno.hxx>
+
+#include <memory>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+namespace {
+
+class NamesEnumeration : public EnumerationHelperImpl
+{
+ uno::Reference< frame::XModel > m_xModel;
+ uno::Reference< sheet::XNamedRanges > m_xNames;
+public:
+ /// @throws uno::RuntimeException
+ NamesEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, uno::Reference< frame::XModel > xModel , uno::Reference< sheet::XNamedRanges > xNames ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), m_xModel(std::move( xModel )), m_xNames(std::move( xNames )) {}
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ uno::Reference< sheet::XNamedRange > xNamed( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ return uno::Any( uno::Reference< excel::XName > ( new ScVbaName( m_xParent, m_xContext, xNamed ,m_xNames , m_xModel ) ) );
+ }
+
+};
+
+}
+
+ScVbaNames::ScVbaNames(const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::sheet::XNamedRanges >& xNames,
+ css::uno::Reference< css::frame::XModel > xModel ):
+ ScVbaNames_BASE( xParent , xContext , uno::Reference< container::XIndexAccess >( xNames, uno::UNO_QUERY ) ),
+ mxModel(std::move( xModel )),
+ mxNames( xNames )
+{
+ m_xNameAccess.set( xNames, uno::UNO_QUERY_THROW );
+}
+
+ScVbaNames::~ScVbaNames()
+{
+}
+
+ScDocument&
+ScVbaNames::getScDocument()
+{
+ uno::Reference< frame::XModel > xModel( getModel() , uno::UNO_SET_THROW );
+ ScTabViewShell * pTabViewShell = excel::getBestViewShell( xModel );
+ if ( !pTabViewShell )
+ throw uno::RuntimeException( "No ViewShell available" );
+ ScViewData& rViewData = pTabViewShell->GetViewData();
+ return rViewData.GetDocument();
+}
+
+css::uno::Any
+ScVbaNames::Add( const css::uno::Any& Name ,
+ const css::uno::Any& RefersTo,
+ const css::uno::Any& /*Visible*/,
+ const css::uno::Any& /*MacroType*/,
+ const css::uno::Any& /*ShoutcutKey*/,
+ const css::uno::Any& /*Category*/,
+ const css::uno::Any& NameLocal,
+ const css::uno::Any& /*RefersToLocal*/,
+ const css::uno::Any& /*CategoryLocal*/,
+ const css::uno::Any& RefersToR1C1,
+ const css::uno::Any& RefersToR1C1Local )
+{
+ OUString sName;
+ uno::Reference< excel::XRange > xRange;
+ if ( Name.hasValue() )
+ Name >>= sName;
+ else if ( NameLocal.hasValue() )
+ NameLocal >>= sName;
+ if ( !sName.isEmpty() )
+ {
+ if (ScRangeData::IsNameValid(sName, getScDocument())
+ != ScRangeData::IsNameValidType::NAME_VALID)
+ {
+ const sal_Int32 nIndex{ sName.indexOf('!') };
+ if (nIndex>=0)
+ sName = sName.copy(nIndex+1);
+ if (ScRangeData::IsNameValid(sName, getScDocument())
+ != ScRangeData::IsNameValidType::NAME_VALID)
+ throw uno::RuntimeException( "This Name is not valid ." );
+ }
+ }
+ uno::Reference< table::XCellRange > xUnoRange;
+ if ( RefersTo.hasValue() || RefersToR1C1.hasValue() || RefersToR1C1Local.hasValue() )
+ {
+ OUString sFormula;
+
+ formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_NATIVE_XL_A1;
+ if ( RefersTo.hasValue() )
+ {
+ if ( RefersTo.getValueTypeClass() == uno::TypeClass_STRING )
+ RefersTo >>= sFormula;
+ else
+ RefersTo >>= xRange;
+ }
+ if ( RefersToR1C1.hasValue() )
+ {
+ if ( RefersToR1C1.getValueTypeClass() == uno::TypeClass_STRING )
+ {
+ RefersToR1C1 >>= sFormula;
+ eGram = formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1;
+ }
+ else
+ RefersToR1C1 >>= xRange;
+ }
+ if ( RefersToR1C1Local.hasValue() )
+ {
+ if ( RefersToR1C1Local.getValueTypeClass() == uno::TypeClass_STRING )
+ {
+ RefersToR1C1Local >>= sFormula;
+ eGram = formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1;
+ }
+ else
+ RefersToR1C1Local >>= xRange;
+ }
+ if ( !xRange.is() && !sFormula.isEmpty() )
+ {
+ ScAddress aBlank;
+ ScCompiler aComp( getScDocument(), aBlank, eGram );
+ std::unique_ptr<ScTokenArray> pTokens(aComp.CompileString(sFormula));
+ if ( pTokens )
+ {
+ ScRange aRange;
+ ScDocShell* pDocSh = excel::getDocShell(getModel());
+ if (pTokens->IsValidReference(aRange, aBlank))
+ xUnoRange = new ScCellRangeObj( pDocSh, aRange );
+ else
+ {
+ // assume it's an address try strip the '=' if it's there
+ // and try and create a range ( must be a better way )
+ if ( sFormula.startsWith("=") )
+ sFormula = sFormula.copy(1);
+ ScRangeList aCellRanges;
+ ScRefFlags nFlags = ScRefFlags::ZERO;
+ formula::FormulaGrammar::AddressConvention eConv = ( eGram == formula::FormulaGrammar::GRAM_NATIVE_XL_A1 ) ? formula::FormulaGrammar::CONV_XL_A1 : formula::FormulaGrammar::CONV_XL_R1C1;
+ if ( ScVbaRange::getCellRangesForAddress( nFlags, sFormula, pDocSh, aCellRanges, eConv , ',' ) )
+ {
+ if ( aCellRanges.size() == 1 )
+ xUnoRange = new ScCellRangeObj( pDocSh, aCellRanges.front() );
+ else
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocSh, aCellRanges ) );
+ xRange = new ScVbaRange( mxParent, mxContext, xRanges );
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ if ( xRange.is() || xUnoRange.is() )
+ {
+ if ( !xRange.is() )
+ xRange = new ScVbaRange( mxParent, mxContext, xUnoRange );
+
+ uno::Reference< excel::XRange > xArea( xRange->Areas( uno::Any( sal_Int32(1) ) ), uno::UNO_QUERY );
+
+ uno::Any aAny = xArea->getCellRange() ;
+
+ uno::Reference< sheet::XCellRangeAddressable > thisRangeAdd( aAny, ::uno::UNO_QUERY_THROW);
+
+ table::CellRangeAddress aAddr = thisRangeAdd->getRangeAddress();
+ uno::Any aAny2;
+ if ( mxNames.is() )
+ {
+ table::CellAddress aCellAddr( aAddr.Sheet , aAddr.StartColumn , aAddr.StartRow );
+ if ( mxNames->hasByName( sName ) )
+ mxNames->removeByName(sName);
+ OUStringBuffer sTmp = "$";
+ uno::Reference< ov::XCollection > xCol( xRange->Areas( uno::Any() ), uno::UNO_QUERY );
+ for ( sal_Int32 nArea = 1; nArea <= xCol->getCount(); ++nArea )
+ {
+ xArea.set( xRange->Areas( uno::Any( nArea ) ), uno::UNO_QUERY_THROW );
+
+ OUString sRangeAdd = xArea->Address( aAny2, aAny2 , aAny2 , aAny2, aAny2 );
+ if ( nArea > 1 )
+ sTmp.append(",");
+ sTmp.append("'" + xRange->getWorksheet()->getName() + "'." + sRangeAdd);
+ }
+ mxNames->addNewByName( sName, sTmp.makeStringAndClear(), aCellAddr, 0/*nUnoType*/);
+ return Item( uno::Any( sName ), uno::Any() );
+ }
+ }
+ return css::uno::Any();
+}
+
+// XEnumerationAccess
+css::uno::Type
+ScVbaNames::getElementType()
+{
+ return cppu::UnoType<ov::excel::XName>::get();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaNames::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( mxNames, uno::UNO_QUERY_THROW );
+ return new NamesEnumeration( getParent(), mxContext, xEnumAccess->createEnumeration(), mxModel , mxNames );
+}
+
+uno::Any
+ScVbaNames::createCollectionObject( const uno::Any& aSource )
+{
+ uno::Reference< sheet::XNamedRange > xName( aSource, uno::UNO_QUERY );
+ return uno::Any( uno::Reference< excel::XName > ( new ScVbaName( getParent(), mxContext, xName, mxNames , mxModel ) ) );
+}
+
+OUString
+ScVbaNames::getServiceImplName()
+{
+ return "ScVbaNames";
+}
+
+css::uno::Sequence<OUString>
+ScVbaNames::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.NamedRanges"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbanames.hxx b/sc/source/ui/vba/vbanames.hxx
new file mode 100644
index 0000000000..db20803a6d
--- /dev/null
+++ b/sc/source/ui/vba/vbanames.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XNames.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::sheet { class XNamedRanges; }
+
+class ScDocument;
+
+typedef CollTestImplHelper< ov::excel::XNames > ScVbaNames_BASE;
+
+class ScVbaNames final : public ScVbaNames_BASE
+{
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::sheet::XNamedRanges > mxNames;
+
+ const css::uno::Reference< css::frame::XModel >& getModel() const { return mxModel; }
+
+public:
+ ScVbaNames( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::sheet::XNamedRanges >& xNames , css::uno::Reference< css::frame::XModel > xModel );
+
+ ScDocument& getScDocument();
+
+ virtual ~ScVbaNames() override;
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // Methods
+ virtual css::uno::Any SAL_CALL Add( const css::uno::Any& aName ,
+ const css::uno::Any& aRefersTo,
+ const css::uno::Any& aVisible,
+ const css::uno::Any& aMacroType,
+ const css::uno::Any& aShoutcutKey,
+ const css::uno::Any& aCategory,
+ const css::uno::Any& aNameLocal,
+ const css::uno::Any& aRefersToLocal,
+ const css::uno::Any& aCategoryLocal,
+ const css::uno::Any& aRefersToR1C1,
+ const css::uno::Any& aRefersToR1C1Local ) override;
+
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ // ScVbaNames_BASE
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaoleobject.cxx b/sc/source/ui/vba/vbaoleobject.cxx
new file mode 100644
index 0000000000..f2cd5de40f
--- /dev/null
+++ b/sc/source/ui/vba/vbaoleobject.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <ooo/vba/XControlProvider.hpp>
+
+#include "vbaoleobject.hxx"
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+ScVbaOLEObject::ScVbaOLEObject( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext,
+ css::uno::Reference< css::drawing::XControlShape > const & xControlShape )
+: OLEObjectImpl_BASE( xParent, xContext )
+{
+ //init m_xWindowPeer
+ uno::Reference< awt::XControlModel > xControlModel( xControlShape->getControl(), css::uno::UNO_SET_THROW );
+ uno::Reference< container::XChild > xChild( xControlModel, uno::UNO_QUERY_THROW );
+ xChild.set( xChild->getParent(), uno::UNO_QUERY_THROW );
+ xChild.set( xChild->getParent(), uno::UNO_QUERY_THROW );
+ uno::Reference<frame::XModel> xModel( xChild->getParent(), uno::UNO_QUERY_THROW );
+ uno::Reference<lang::XMultiComponentFactory > xServiceManager( mxContext->getServiceManager(), uno::UNO_SET_THROW );
+ uno::Reference< XControlProvider > xControlProvider( xServiceManager->createInstanceWithContext("ooo.vba.ControlProvider", mxContext ), uno::UNO_QUERY_THROW );
+ m_xControl.set( xControlProvider->createControl( xControlShape, xModel ) );
+}
+
+uno::Reference< uno::XInterface > SAL_CALL
+ScVbaOLEObject::getObject()
+{
+ return uno::Reference< uno::XInterface >( m_xControl, uno::UNO_QUERY_THROW );
+}
+
+sal_Bool SAL_CALL
+ScVbaOLEObject::getEnabled()
+{
+ return m_xControl->getEnabled();
+}
+
+void SAL_CALL
+ScVbaOLEObject::setEnabled( sal_Bool _enabled )
+{
+ m_xControl->setEnabled( _enabled );
+}
+
+sal_Bool SAL_CALL
+ScVbaOLEObject::getVisible()
+{
+ return m_xControl->getVisible();
+}
+
+void SAL_CALL
+ScVbaOLEObject::setVisible( sal_Bool _visible )
+{
+ m_xControl->setVisible( _visible );
+}
+
+double SAL_CALL
+ScVbaOLEObject::getLeft()
+{
+ return m_xControl->getLeft();
+}
+
+void SAL_CALL
+ScVbaOLEObject::setLeft( double _left )
+{
+ m_xControl->setLeft( _left );
+
+}
+
+double SAL_CALL
+ScVbaOLEObject::getTop()
+{
+ return m_xControl->getTop();
+}
+
+void SAL_CALL
+ScVbaOLEObject::setTop( double _top )
+{
+ m_xControl->setTop( _top );
+}
+
+double SAL_CALL
+ScVbaOLEObject::getHeight()
+{
+ return m_xControl->getHeight();
+}
+
+void SAL_CALL
+ScVbaOLEObject::setHeight( double _height )
+{
+ m_xControl->setHeight( _height );
+}
+
+double SAL_CALL
+ScVbaOLEObject::getWidth()
+{
+ return m_xControl->getWidth();
+}
+
+void SAL_CALL
+ScVbaOLEObject::setWidth( double _width )
+{
+ m_xControl->setWidth( _width );
+}
+
+OUString SAL_CALL ScVbaOLEObject::getLinkedCell()
+{
+ return m_xControl->getControlSource();
+}
+
+void SAL_CALL ScVbaOLEObject::setLinkedCell( const OUString& _linkedcell )
+{
+ m_xControl->setControlSource( _linkedcell );
+}
+
+OUString
+ScVbaOLEObject::getServiceImplName()
+{
+ return "ScVbaOLEObject";
+}
+
+uno::Sequence< OUString >
+ScVbaOLEObject::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.OLEObject"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaoleobject.hxx b/sc/source/ui/vba/vbaoleobject.hxx
new file mode 100644
index 0000000000..9eecf5fdc6
--- /dev/null
+++ b/sc/source/ui/vba/vbaoleobject.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 .
+ */
+#pragma once
+
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <ooo/vba/excel/XOLEObject.hpp>
+#include <ooo/vba/msforms/XControl.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XOLEObject > OLEObjectImpl_BASE;
+
+class ScVbaOLEObject final : public OLEObjectImpl_BASE
+{
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+ css::uno::Reference< ov::msforms::XControl> m_xControl;
+public:
+ ScVbaOLEObject( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::drawing::XControlShape > const & xControlShape );
+
+ // XOLEObject Attributes
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getObject() override;
+ virtual sal_Bool SAL_CALL getEnabled() override;
+ virtual void SAL_CALL setEnabled( sal_Bool _enabled ) override;
+ virtual sal_Bool SAL_CALL getVisible() override;
+ virtual void SAL_CALL setVisible( sal_Bool _visible ) override;
+
+ virtual double SAL_CALL getLeft() override;
+ virtual void SAL_CALL setLeft( double _left ) override;
+ virtual double SAL_CALL getTop() override;
+ virtual void SAL_CALL setTop( double _top ) override;
+ virtual double SAL_CALL getHeight() override;
+ virtual void SAL_CALL setHeight( double _height ) override;
+ virtual double SAL_CALL getWidth() override;
+ virtual void SAL_CALL setWidth( double _width ) override;
+ virtual OUString SAL_CALL getLinkedCell() override;
+ virtual void SAL_CALL setLinkedCell( const OUString& _linkedcell ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaoleobjects.cxx b/sc/source/ui/vba/vbaoleobjects.cxx
new file mode 100644
index 0000000000..06b576f26f
--- /dev/null
+++ b/sc/source/ui/vba/vbaoleobjects.cxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <ooo/vba/excel/XOLEObject.hpp>
+
+#include "vbaoleobject.hxx"
+#include "vbaoleobjects.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE;
+
+namespace {
+
+class IndexAccessWrapper : public XIndexAccess_BASE
+{
+typedef std::vector< uno::Reference< drawing::XControlShape > > OLEObjects;
+ OLEObjects vObjects;
+public:
+ explicit IndexAccessWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess )
+ {
+ sal_Int32 nLen = xIndexAccess->getCount();
+ for ( sal_Int32 index = 0; index < nLen; ++index )
+ {
+ uno::Reference< drawing::XControlShape > xControlShape( xIndexAccess->getByIndex( index), uno::UNO_QUERY);
+ if ( xControlShape.is() )
+ vObjects.push_back( xControlShape );
+ }
+ }
+
+ virtual ::sal_Int32 SAL_CALL getCount() override
+ {
+ return vObjects.size();
+ }
+
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ if ( Index < 0 || Index >= getCount() )
+ throw lang::IndexOutOfBoundsException();
+ return uno::Any( vObjects[ Index ] );
+ }
+
+ // Methods XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<drawing::XControlShape>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return ( getCount() > 0 );
+ }
+
+};
+
+class EnumWrapper : public EnumerationHelper_BASE
+{
+
+ uno::Reference<XHelperInterface > m_xParent;
+ uno::Reference<uno::XComponentContext > m_xContext;
+ uno::Reference<container::XIndexAccess > m_xIndexAccess;
+ sal_Int32 nIndex;
+public:
+ EnumWrapper( uno::Reference< XHelperInterface > xParent,
+ uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< container::XIndexAccess > xIndexAccess )
+ : m_xParent(std::move( xParent )), m_xContext(std::move( xContext)), m_xIndexAccess(std::move( xIndexAccess )), nIndex( 0 ) {}
+
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( nIndex < m_xIndexAccess->getCount() );
+ }
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( nIndex < m_xIndexAccess->getCount() )
+ {
+ uno::Reference< drawing::XControlShape > xControlShape ( m_xIndexAccess->getByIndex( nIndex++ ), uno::UNO_QUERY_THROW );
+ return uno::Any( uno::Reference< ov::excel::XOLEObject >( new ScVbaOLEObject( m_xParent, m_xContext, xControlShape ) ) );
+ }
+ throw container::NoSuchElementException();
+ }
+};
+
+uno::Reference< container::XIndexAccess > oleObjectIndexWrapper( const uno::Reference< container::XIndexAccess >& xIndexAccess )
+{
+ return new IndexAccessWrapper( xIndexAccess );
+}
+
+}
+
+ScVbaOLEObjects::ScVbaOLEObjects( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::container::XIndexAccess >& xIndexAccess )
+ : OLEObjectsImpl_BASE( xParent, xContext, oleObjectIndexWrapper( xIndexAccess ) )
+{
+}
+uno::Reference< container::XEnumeration >
+ScVbaOLEObjects::createEnumeration()
+{
+ return new EnumWrapper( getParent(), mxContext, m_xIndexAccess );
+}
+
+uno::Any
+ScVbaOLEObjects::createCollectionObject( const css::uno::Any& aSource )
+{
+ if( aSource.hasValue() )
+ {
+ uno::Reference< drawing::XControlShape > xControlShape( aSource, uno::UNO_QUERY_THROW );
+ // parent of OLEObject is the same parent as the collection ( e.g. the sheet )
+ return uno::Any( uno::Reference< ov::excel::XOLEObject >( new ScVbaOLEObject( getParent(), mxContext, xControlShape ) ) );
+ }
+ return uno::Any();
+}
+
+uno::Any
+ScVbaOLEObjects::getItemByStringIndex( const OUString& sIndex )
+{
+ try
+ {
+ return OLEObjectsImpl_BASE::getItemByStringIndex( sIndex );
+ }
+ catch (const uno::RuntimeException&)
+ {
+ uno::Reference< container::XIndexAccess > xIndexAccess( m_xIndexAccess, uno::UNO_SET_THROW );
+ sal_Int32 nCount = xIndexAccess->getCount();
+ for( int index = 0; index < nCount; index++ )
+ {
+ uno::Any aUnoObj = xIndexAccess->getByIndex( index );
+ uno::Reference< drawing::XControlShape > xControlShape( aUnoObj, uno::UNO_QUERY_THROW );
+ uno::Reference< awt::XControlModel > xControlModel( xControlShape->getControl() );
+ uno::Reference< container::XNamed > xNamed( xControlModel, uno::UNO_QUERY_THROW );
+ if( sIndex == xNamed->getName() )
+ {
+ return createCollectionObject( aUnoObj );
+ }
+
+ }
+ return uno::Any();
+ }
+}
+
+uno::Type
+ScVbaOLEObjects::getElementType()
+{
+ return cppu::UnoType<ooo::vba::excel::XOLEObject>::get();
+}
+
+OUString
+ScVbaOLEObjects::getServiceImplName()
+{
+ return "ScVbaOLEObjects";
+}
+
+uno::Sequence< OUString >
+ScVbaOLEObjects::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.OLEObjects"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaoleobjects.hxx b/sc/source/ui/vba/vbaoleobjects.hxx
new file mode 100644
index 0000000000..9d4eab048c
--- /dev/null
+++ b/sc/source/ui/vba/vbaoleobjects.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XOLEObjects.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+typedef CollTestImplHelper<ov::excel::XOLEObjects> OLEObjectsImpl_BASE;
+
+class ScVbaOLEObjects : public OLEObjectsImpl_BASE
+{
+protected:
+ virtual css::uno::Any getItemByStringIndex(const OUString& sIndex) override;
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+public:
+ ScVbaOLEObjects(const css::uno::Reference<ov::XHelperInterface>& xParent,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const css::uno::Reference<css::container::XIndexAccess>& xIndexAccess);
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override;
+
+ // ScVbaCollectionBaseImpl
+ virtual css::uno::Any createCollectionObject(const css::uno::Any& aSource) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaoutline.cxx b/sc/source/ui/vba/vbaoutline.cxx
new file mode 100644
index 0000000000..60bc12921b
--- /dev/null
+++ b/sc/source/ui/vba/vbaoutline.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbaoutline.hxx"
+#include <com/sun/star/sheet/XSheetOutline.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+void
+ScVbaOutline::ShowLevels( const uno::Any& RowLevels, const uno::Any& ColumnLevels )
+{
+ if (mxOutline.is())
+ {
+ sal_Int16 nLevel = 0;
+ if (RowLevels >>= nLevel)
+ {
+ mxOutline->showLevel(nLevel, table::TableOrientation_ROWS);
+ }
+ if (ColumnLevels >>= nLevel)
+ {
+ mxOutline->showLevel(nLevel,table::TableOrientation_COLUMNS);
+ }
+ }
+}
+
+OUString
+ScVbaOutline::getServiceImplName()
+{
+ return "ScVbaOutline";
+}
+
+uno::Sequence< OUString >
+ScVbaOutline::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Outline"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaoutline.hxx b/sc/source/ui/vba/vbaoutline.hxx
new file mode 100644
index 0000000000..7fd46d3685
--- /dev/null
+++ b/sc/source/ui/vba/vbaoutline.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XOutline.hpp>
+#include <utility>
+#include <vbahelper/vbahelperinterface.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+namespace com::sun::star::sheet { class XSheetOutline; }
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XOutline > ScVbaOutline_BASE;
+
+class ScVbaOutline : public ScVbaOutline_BASE
+{
+ css::uno::Reference< css::sheet::XSheetOutline > mxOutline;
+public:
+ ScVbaOutline( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference<css::sheet::XSheetOutline> outline): ScVbaOutline_BASE( xParent, xContext) , mxOutline(std::move(outline))
+ {}
+
+ virtual void SAL_CALL ShowLevels( const css::uno::Any& RowLevels, const css::uno::Any& ColumnLevels ) override ;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaovalshape.cxx b/sc/source/ui/vba/vbaovalshape.cxx
new file mode 100644
index 0000000000..8efc877ee1
--- /dev/null
+++ b/sc/source/ui/vba/vbaovalshape.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 "vbaovalshape.hxx"
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+/*
+ * This is implemented as a new class in order to provide XTypeProvider
+ * interface. This is needed by TypeOf ... Is ... basic operator.
+ */
+
+ScVbaOvalShape::ScVbaOvalShape( const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< drawing::XShape >& xShape, const uno::Reference< drawing::XShapes >& xShapes, const uno::Reference< frame::XModel >& xModel ) : OvalShapeImpl_BASE( uno::Reference< XHelperInterface >(), xContext, xShape, xShapes, xModel, ScVbaShape::getType( xShape ) )
+{}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaovalshape.hxx b/sc/source/ui/vba/vbaovalshape.hxx
new file mode 100644
index 0000000000..1155cd1fc9
--- /dev/null
+++ b/sc/source/ui/vba/vbaovalshape.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <ooo/vba/msforms/XOval.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <vbahelper/vbashape.hxx>
+
+typedef cppu::ImplInheritanceHelper< ScVbaShape, ov::msforms::XOval > OvalShapeImpl_BASE;
+
+class ScVbaOvalShape : public OvalShapeImpl_BASE
+{
+public:
+ ScVbaOvalShape( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::drawing::XShapes >& xShapes, const css::uno::Reference< css::frame::XModel >& xModel );
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapagebreak.cxx b/sc/source/ui/vba/vbapagebreak.cxx
new file mode 100644
index 0000000000..2b7d3ff4a6
--- /dev/null
+++ b/sc/source/ui/vba/vbapagebreak.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 "vbapagebreak.hxx"
+#include "vbarange.hxx"
+#include <basic/sberrors.hxx>
+#include <ooo/vba/excel/XlPageBreak.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+template< typename... Ifc >
+ScVbaPageBreak< Ifc... >::ScVbaPageBreak( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ uno::Reference< beans::XPropertySet > xProps,
+ sheet::TablePageBreakData aTablePageBreakData):
+ ScVbaPageBreak_BASE( xParent, xContext ),
+ mxRowColPropertySet(std::move( xProps )),
+ maTablePageBreakData( aTablePageBreakData )
+{
+}
+
+template< typename... Ifc >
+sal_Int32 ScVbaPageBreak< Ifc... >::getType()
+{
+ uno::Any aValue = mxRowColPropertySet->getPropertyValue("IsStartOfNewPage");
+ bool hasPageBreak = false;
+ aValue >>= hasPageBreak;
+
+ if( !hasPageBreak )
+ return excel::XlPageBreak::xlPageBreakNone;
+
+ if( maTablePageBreakData.ManualBreak )
+ return excel::XlPageBreak::xlPageBreakManual;
+
+ return excel::XlPageBreak::xlPageBreakAutomatic;
+}
+
+template< typename... Ifc >
+void ScVbaPageBreak< Ifc... >::setType(sal_Int32 type)
+{
+ if( (type != excel::XlPageBreak::xlPageBreakNone) &&
+ (type != excel::XlPageBreak::xlPageBreakManual) &&
+ (type != excel::XlPageBreak::xlPageBreakAutomatic) )
+ {
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+
+ if( type == excel::XlPageBreak::xlPageBreakNone )
+ {
+ mxRowColPropertySet->setPropertyValue("IsStartOfNewPage", uno::Any(false));
+ return;
+ }
+
+ mxRowColPropertySet->setPropertyValue("IsStartOfNewPage", uno::Any(true));
+ if( type == excel::XlPageBreak::xlPageBreakManual )
+ maTablePageBreakData.ManualBreak = true;
+ else
+ maTablePageBreakData.ManualBreak = false;
+}
+
+template< typename... Ifc >
+void ScVbaPageBreak< Ifc... >::Delete()
+{
+ mxRowColPropertySet->setPropertyValue("IsStartOfNewPage", uno::Any(false));
+}
+
+template< typename... Ifc >
+uno::Reference< excel::XRange> ScVbaPageBreak< Ifc... >::Location()
+{
+ uno::Reference< table::XCellRange > xRange( mxRowColPropertySet, uno::UNO_QUERY_THROW );
+ return new ScVbaRange( ScVbaPageBreak_BASE::getParent(), ScVbaPageBreak_BASE::mxContext, xRange);
+}
+
+template class ScVbaPageBreak< excel::XHPageBreak >;
+
+/* class ScVbaHPageBreak */
+OUString
+ScVbaHPageBreak::getServiceImplName()
+{
+ return "ScVbaHPageBreak";
+}
+
+uno::Sequence< OUString >
+ScVbaHPageBreak::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.HPageBreak"
+ };
+ return aServiceNames;
+}
+
+template class ScVbaPageBreak< excel::XVPageBreak >;
+
+/* class ScVbaVPageBreak */
+ScVbaVPageBreak::ScVbaVPageBreak( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::beans::XPropertySet >& xProps,
+ css::sheet::TablePageBreakData aTablePageBreakData )
+: ScVbaVPageBreak_BASE( xParent, xContext, xProps, aTablePageBreakData )
+{
+}
+
+ScVbaVPageBreak::~ScVbaVPageBreak()
+{
+}
+
+OUString
+ScVbaVPageBreak::getServiceImplName()
+{
+ return "ScVbaVPageBreak";
+}
+
+uno::Sequence< OUString >
+ScVbaVPageBreak::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.VPageBreak"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapagebreak.hxx b/sc/source/ui/vba/vbapagebreak.hxx
new file mode 100644
index 0000000000..48d6cad078
--- /dev/null
+++ b/sc/source/ui/vba/vbapagebreak.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XHPageBreak.hpp>
+#include <ooo/vba/excel/XVPageBreak.hpp>
+#include <com/sun/star/sheet/TablePageBreakData.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::uno { class XComponentContext; }
+namespace ooo::vba::excel { class XRange; }
+
+template< typename... Ifc >
+class ScVbaPageBreak : public InheritedHelperInterfaceWeakImpl< Ifc... >
+{
+typedef InheritedHelperInterfaceWeakImpl< Ifc... > ScVbaPageBreak_BASE;
+protected:
+ css::uno::Reference< css::beans::XPropertySet > mxRowColPropertySet;
+ css::sheet::TablePageBreakData maTablePageBreakData;
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaPageBreak( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::beans::XPropertySet > xProps,
+ css::sheet::TablePageBreakData aTablePageBreakData);
+
+ virtual sal_Int32 SAL_CALL getType( ) override;
+ virtual void SAL_CALL setType(sal_Int32 type) override;
+
+ virtual void SAL_CALL Delete() override;
+ virtual css::uno::Reference< ov::excel::XRange> SAL_CALL Location() override;
+};
+
+typedef ScVbaPageBreak < ov::excel::XHPageBreak > ScVbaHPageBreak_BASE;
+
+class ScVbaHPageBreak : public ScVbaHPageBreak_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaHPageBreak( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::beans::XPropertySet >& xProps,
+ css::sheet::TablePageBreakData aTablePageBreakData):
+ ScVbaHPageBreak_BASE( xParent,xContext,xProps,aTablePageBreakData ){}
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+//VPageBreak
+typedef ScVbaPageBreak < ov::excel::XVPageBreak > ScVbaVPageBreak_BASE;
+
+class ScVbaVPageBreak : public ScVbaVPageBreak_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaVPageBreak( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::beans::XPropertySet >& xProps,
+ css::sheet::TablePageBreakData aTablePageBreakData);
+
+ virtual ~ScVbaVPageBreak() override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapagebreaks.cxx b/sc/source/ui/vba/vbapagebreaks.cxx
new file mode 100644
index 0000000000..b9a0235c34
--- /dev/null
+++ b/sc/source/ui/vba/vbapagebreaks.cxx
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbapagebreaks.hxx"
+#include "vbapagebreak.hxx"
+#include <basic/sberrors.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XWorksheet.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSheetPageBreak.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+namespace {
+
+class RangePageBreaks : public ::cppu::WeakImplHelper<container::XIndexAccess >
+{
+private:
+ uno::Reference< XHelperInterface > mxParent;
+ uno::Reference< uno::XComponentContext > mxContext;
+ uno::Reference< sheet::XSheetPageBreak > mxSheetPageBreak;
+ bool m_bColumn;
+
+public:
+ RangePageBreaks( uno::Reference< XHelperInterface > xParent,
+ uno::Reference< uno::XComponentContext > xContext,
+ uno::Reference< sheet::XSheetPageBreak > xSheetPageBreak,
+ bool bColumn ) : mxParent(std::move( xParent )), mxContext(std::move( xContext )), mxSheetPageBreak(std::move( xSheetPageBreak )), m_bColumn( bColumn )
+ {
+ }
+
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getAPIStartofRange( const uno::Reference< excel::XRange >& xRange )
+ {
+ if( m_bColumn )
+ return xRange->getColumn() - 1;
+ return xRange->getRow() - 1;
+ }
+
+ /// @throws uno::RuntimeException
+ sal_Int32 getAPIEndIndexofRange( const uno::Reference< excel::XRange >& xRange, sal_Int32 nUsedStart )
+ {
+ if( m_bColumn )
+ return nUsedStart + xRange->Columns( uno::Any() )->getCount() - 1;
+ return nUsedStart + xRange->Rows( uno::Any() )->getCount();
+ }
+
+ /// @throws uno::RuntimeException
+ uno::Sequence<sheet::TablePageBreakData> getAllPageBreaks()
+ {
+ if( m_bColumn )
+ return mxSheetPageBreak->getColumnPageBreaks();
+ return mxSheetPageBreak->getRowPageBreaks();
+ }
+
+ /// @throws uno::RuntimeException
+ uno::Reference<container::XIndexAccess> getRowColContainer() const
+ {
+ uno::Reference< table::XColumnRowRange > xColumnRowRange( mxSheetPageBreak, uno::UNO_QUERY_THROW );
+ uno::Reference<container::XIndexAccess> xIndexAccess;
+ if( m_bColumn )
+ xIndexAccess.set( xColumnRowRange->getColumns(), uno::UNO_QUERY_THROW );
+ else
+ xIndexAccess.set( xColumnRowRange->getRows(), uno::UNO_QUERY_THROW );
+ return xIndexAccess;
+ }
+
+ /// @throws uno::RuntimeException
+ sheet::TablePageBreakData getTablePageBreakData( sal_Int32 nAPIItemIndex );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ uno::Any Add( const css::uno::Any& Before );
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount( ) override;
+ virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+ virtual uno::Type SAL_CALL getElementType( ) override
+ {
+ if( m_bColumn )
+ return cppu::UnoType<excel::XVPageBreak>::get();
+ return cppu::UnoType<excel::XHPageBreak>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ return true;
+ }
+};
+
+}
+
+/** @TODO Unlike MS Excel this method only considers the pagebreaks that intersect the used range
+* To become completely compatible the print area has to be considered. As far as I found out this printarea
+* also considers the position and sizes of shapes and manually inserted page breaks
+* Note: In MS there is a limit of 1026 horizontal page breaks per sheet.
+*/
+sal_Int32 SAL_CALL RangePageBreaks::getCount( )
+{
+ uno::Reference< excel::XWorksheet > xWorksheet( mxParent, uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XRange > xRange = xWorksheet->getUsedRange();
+ sal_Int32 nUsedStart = getAPIStartofRange( xRange );
+ sal_Int32 nUsedEnd = getAPIEndIndexofRange( xRange, nUsedStart );
+ const uno::Sequence<sheet::TablePageBreakData> aTablePageBreakData = getAllPageBreaks();
+
+ auto pPageBreak = std::find_if(aTablePageBreakData.begin(), aTablePageBreakData.end(),
+ [nUsedEnd](const sheet::TablePageBreakData& rPageBreak) { return rPageBreak.Position > nUsedEnd + 1; });
+
+ return static_cast<sal_Int32>(std::distance(aTablePageBreakData.begin(), pPageBreak));
+}
+
+uno::Any SAL_CALL RangePageBreaks::getByIndex( sal_Int32 Index )
+{
+ if( (Index < getCount()) && ( Index >= 0 ))
+ {
+ sheet::TablePageBreakData aTablePageBreakData = getTablePageBreakData( Index );
+ uno::Reference< container::XIndexAccess > xIndexAccess = getRowColContainer();
+ sal_Int32 nPos = aTablePageBreakData.Position;
+ if( (nPos < xIndexAccess->getCount()) && (nPos > -1) )
+ {
+ uno::Reference< beans::XPropertySet > xRowColPropertySet( xIndexAccess->getByIndex(nPos), uno::UNO_QUERY_THROW );
+ if( m_bColumn )
+ return uno::Any( uno::Reference< excel::XVPageBreak >( new ScVbaVPageBreak( mxParent, mxContext, xRowColPropertySet, aTablePageBreakData) ));
+ return uno::Any( uno::Reference< excel::XHPageBreak >( new ScVbaHPageBreak( mxParent, mxContext, xRowColPropertySet, aTablePageBreakData) ));
+ }
+ }
+ throw lang::IndexOutOfBoundsException();
+}
+
+sheet::TablePageBreakData RangePageBreaks::getTablePageBreakData( sal_Int32 nAPIItemIndex )
+{
+ sal_Int32 index = -1;
+ sheet::TablePageBreakData aTablePageBreakData;
+ uno::Reference< excel::XWorksheet > xWorksheet( mxParent, uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XRange > xRange = xWorksheet->getUsedRange();
+ sal_Int32 nUsedStart = getAPIStartofRange( xRange );
+ sal_Int32 nUsedEnd = getAPIEndIndexofRange( xRange, nUsedStart );
+ const uno::Sequence<sheet::TablePageBreakData> aTablePageBreakDataList = getAllPageBreaks();
+
+ for( const auto& rTablePageBreakData : aTablePageBreakDataList )
+ {
+ aTablePageBreakData = rTablePageBreakData;
+ sal_Int32 nPos = aTablePageBreakData.Position;
+ if( nPos > nUsedEnd + 1 )
+ DebugHelper::runtimeexception(ERRCODE_BASIC_METHOD_FAILED);
+ index++;
+ if( index == nAPIItemIndex )
+ return aTablePageBreakData;
+ }
+
+ return aTablePageBreakData;
+}
+
+uno::Any RangePageBreaks::Add( const css::uno::Any& Before )
+{
+ uno::Reference< excel::XRange > xRange;
+ Before >>= xRange;
+ if( !xRange.is() )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, {});
+ }
+
+ sal_Int32 nAPIRowColIndex = getAPIStartofRange( xRange );
+ uno::Reference< container::XIndexAccess > xIndexAccess = getRowColContainer();
+ uno::Reference< beans::XPropertySet > xRowColPropertySet( xIndexAccess->getByIndex(nAPIRowColIndex), uno::UNO_QUERY_THROW );
+ xRowColPropertySet->setPropertyValue("IsStartOfNewPage", uno::Any(true));
+ sheet::TablePageBreakData aTablePageBreakData;
+ aTablePageBreakData.ManualBreak = true;
+ aTablePageBreakData.Position = nAPIRowColIndex;
+ if( m_bColumn )
+ return uno::Any( uno::Reference< excel::XVPageBreak >( new ScVbaVPageBreak( mxParent, mxContext, xRowColPropertySet, aTablePageBreakData) ));
+ return uno::Any( uno::Reference< excel::XHPageBreak >( new ScVbaHPageBreak( mxParent, mxContext, xRowColPropertySet, aTablePageBreakData) ));
+}
+
+namespace {
+
+class RangePageBreaksEnumWrapper : public EnumerationHelper_BASE
+{
+ uno::Reference<container::XIndexAccess > m_xIndexAccess;
+ sal_Int32 nIndex;
+public:
+ explicit RangePageBreaksEnumWrapper( uno::Reference< container::XIndexAccess > xIndexAccess ) : m_xIndexAccess(std::move( xIndexAccess )), nIndex( 0 ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( nIndex < m_xIndexAccess->getCount() );
+ }
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( nIndex < m_xIndexAccess->getCount() )
+ return m_xIndexAccess->getByIndex( nIndex++ );
+ throw container::NoSuchElementException();
+ }
+};
+
+}
+
+ScVbaHPageBreaks::ScVbaHPageBreaks( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< sheet::XSheetPageBreak >& xSheetPageBreak):
+ ScVbaHPageBreaks_BASE( xParent,xContext, new RangePageBreaks( xParent, xContext, xSheetPageBreak, false ))
+{
+}
+
+uno::Any SAL_CALL ScVbaHPageBreaks::Add( const uno::Any& Before)
+{
+ RangePageBreaks* pPageBreaks = dynamic_cast< RangePageBreaks* >( m_xIndexAccess.get() );
+ if( pPageBreaks )
+ {
+ return pPageBreaks->Add( Before );
+ }
+ return uno::Any();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaHPageBreaks::createEnumeration()
+{
+ return new RangePageBreaksEnumWrapper( m_xIndexAccess );
+}
+
+uno::Any
+ScVbaHPageBreaks::createCollectionObject( const css::uno::Any& aSource )
+{
+ return aSource; // it's already a pagebreak object
+}
+
+uno::Type
+ScVbaHPageBreaks::getElementType()
+{
+ return cppu::UnoType<excel::XHPageBreak>::get();
+}
+
+OUString
+ScVbaHPageBreaks::getServiceImplName()
+{
+ return "ScVbaHPageBreaks";
+}
+
+uno::Sequence< OUString >
+ScVbaHPageBreaks::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.HPageBreaks"
+ };
+ return aServiceNames;
+}
+
+//VPageBreak
+ScVbaVPageBreaks::ScVbaVPageBreaks( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< sheet::XSheetPageBreak >& xSheetPageBreak )
+: ScVbaVPageBreaks_BASE( xParent, xContext, new RangePageBreaks( xParent, xContext, xSheetPageBreak, true ) )
+{
+}
+
+ScVbaVPageBreaks::~ScVbaVPageBreaks()
+{
+}
+
+uno::Any SAL_CALL
+ScVbaVPageBreaks::Add( const uno::Any& Before )
+{
+ RangePageBreaks* pPageBreaks = dynamic_cast< RangePageBreaks* >( m_xIndexAccess.get() );
+ if( pPageBreaks )
+ {
+ return pPageBreaks->Add( Before );
+ }
+ return uno::Any();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaVPageBreaks::createEnumeration()
+{
+ return new RangePageBreaksEnumWrapper( m_xIndexAccess );
+}
+
+uno::Any
+ScVbaVPageBreaks::createCollectionObject( const css::uno::Any& aSource )
+{
+ return aSource; // it's already a pagebreak object
+}
+
+uno::Type
+ScVbaVPageBreaks::getElementType()
+{
+ return cppu::UnoType<excel::XVPageBreak>::get();
+}
+
+OUString
+ScVbaVPageBreaks::getServiceImplName()
+{
+ return "ScVbaVPageBreaks";
+}
+
+uno::Sequence< OUString >
+ScVbaVPageBreaks::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.VPageBreaks"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapagebreaks.hxx b/sc/source/ui/vba/vbapagebreaks.hxx
new file mode 100644
index 0000000000..a04f94145d
--- /dev/null
+++ b/sc/source/ui/vba/vbapagebreaks.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XHPageBreaks.hpp>
+#include <ooo/vba/excel/XVPageBreaks.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::sheet
+{
+class XSheetPageBreak;
+}
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+typedef CollTestImplHelper<ov::excel::XHPageBreaks> ScVbaHPageBreaks_BASE;
+
+class ScVbaHPageBreaks : public ScVbaHPageBreaks_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaHPageBreaks(const css::uno::Reference<ov::XHelperInterface>& xParent,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const css::uno::Reference<css::sheet::XSheetPageBreak>& xSheetPageBreak);
+
+ // XHPageBreaks
+ virtual css::uno::Any SAL_CALL Add(const css::uno::Any& Before) override;
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject(const css::uno::Any&) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+//VPageBreaks
+typedef CollTestImplHelper<ov::excel::XVPageBreaks> ScVbaVPageBreaks_BASE;
+
+class ScVbaVPageBreaks : public ScVbaVPageBreaks_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaVPageBreaks(const css::uno::Reference<ov::XHelperInterface>& xParent,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ const css::uno::Reference<css::sheet::XSheetPageBreak>& xSheetPageBreak);
+
+ virtual ~ScVbaVPageBreaks() override;
+
+ // XVPageBreaks
+ virtual css::uno::Any SAL_CALL Add(const css::uno::Any& Before) override;
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject(const css::uno::Any&) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapagesetup.cxx b/sc/source/ui/vba/vbapagesetup.cxx
new file mode 100644
index 0000000000..f945802e10
--- /dev/null
+++ b/sc/source/ui/vba/vbapagesetup.cxx
@@ -0,0 +1,635 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbapagesetup.hxx"
+#include <convuno.hxx>
+#include <rangelst.hxx>
+#include <docsh.hxx>
+#include "excelvbahelper.hxx"
+#include "vbarange.hxx"
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <ooo/vba/excel/XlPageOrientation.hpp>
+#include <ooo/vba/excel/XlOrder.hpp>
+#include <ooo/vba/excel/Constants.hpp>
+#include <ooo/vba/excel/XlPaperSize.hpp>
+#include <basic/sberrors.hxx>
+#include <filter/msfilter/util.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+#define ZOOM_IN 10
+#define ZOOM_MAX 400
+
+ScVbaPageSetup::ScVbaPageSetup(const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ uno::Reference< sheet::XSpreadsheet > xSheet,
+ const uno::Reference< frame::XModel >& xModel):
+ ScVbaPageSetup_BASE( xParent, xContext ), mxSheet(std::move( xSheet )), mbIsLandscape( false )
+{
+ // query for current page style
+ mxModel.set( xModel, uno::UNO_SET_THROW );
+ uno::Reference< beans::XPropertySet > xSheetProps( mxSheet, uno::UNO_QUERY_THROW );
+ uno::Any aValue = xSheetProps->getPropertyValue("PageStyle");
+ OUString aStyleName;
+ aValue >>= aStyleName;
+
+ uno::Reference< style::XStyleFamiliesSupplier > xStyleFamiliesSup( mxModel, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xStyleFamilies = xStyleFamiliesSup->getStyleFamilies();
+ uno::Reference< container::XNameAccess > xPageStyle( xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY_THROW );
+ mxPageProps.set( xPageStyle->getByName(aStyleName), uno::UNO_QUERY_THROW );
+ mnOrientLandscape = excel::XlPageOrientation::xlLandscape;
+ mnOrientPortrait = excel::XlPageOrientation::xlPortrait;
+ mxPageProps->getPropertyValue("IsLandscape") >>= mbIsLandscape;
+}
+
+OUString SAL_CALL ScVbaPageSetup::getPrintArea()
+{
+ OUString aPrintArea;
+ uno::Reference< sheet::XPrintAreas > xPrintAreas( mxSheet, uno::UNO_QUERY_THROW );
+ const uno::Sequence< table::CellRangeAddress > aSeq = xPrintAreas->getPrintAreas();
+ if( aSeq.hasElements() )
+ {
+ ScRangeList aRangeList;
+ for( const auto& rRange : aSeq )
+ {
+ ScRange aRange;
+ ScUnoConversion::FillScRange( aRange, rRange );
+ aRangeList.push_back( aRange );
+ }
+ if ( ScDocShell* pShell = excel::getDocShell( mxModel ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ aRangeList.Format( aPrintArea, ScRefFlags::RANGE_ABS, rDoc, formula::FormulaGrammar::CONV_XL_A1, ',' );
+ }
+ }
+
+ return aPrintArea;
+}
+
+void SAL_CALL ScVbaPageSetup::setPrintArea( const OUString& rAreas )
+{
+ uno::Reference< sheet::XPrintAreas > xPrintAreas( mxSheet, uno::UNO_QUERY_THROW );
+ if( rAreas.isEmpty() ||
+ rAreas.equalsIgnoreAsciiCase( "FALSE" ) )
+ {
+ // print the whole sheet
+ uno::Sequence< table::CellRangeAddress > aSeq;
+ xPrintAreas->setPrintAreas( aSeq );
+ }
+ else
+ {
+ ScRangeList aCellRanges;
+ ScRange aRange;
+ if( getScRangeListForAddress( rAreas, excel::getDocShell( mxModel ) , aRange, aCellRanges ) )
+ {
+ uno::Sequence< table::CellRangeAddress > aSeq( aCellRanges.size() );
+ auto aSeqRange = asNonConstRange(aSeq);
+ for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rRange = aCellRanges[ i ];
+ table::CellRangeAddress aRangeAddress;
+ ScUnoConversion::FillApiRange( aRangeAddress, rRange );
+ aSeqRange[ i++ ] = aRangeAddress;
+ }
+ xPrintAreas->setPrintAreas( aSeq );
+ }
+ }
+}
+
+double SAL_CALL ScVbaPageSetup::getHeaderMargin()
+{
+ return VbaPageSetupBase::getHeaderMargin();
+}
+
+void SAL_CALL ScVbaPageSetup::setHeaderMargin( double margin )
+{
+ VbaPageSetupBase::setHeaderMargin( margin );
+}
+
+double SAL_CALL ScVbaPageSetup::getFooterMargin()
+{
+ return VbaPageSetupBase::getFooterMargin();
+}
+
+void SAL_CALL ScVbaPageSetup::setFooterMargin( double margin )
+{
+ VbaPageSetupBase::setFooterMargin( margin );
+}
+
+uno::Any SAL_CALL ScVbaPageSetup::getFitToPagesTall()
+{
+ return mxPageProps->getPropertyValue("ScaleToPagesY");
+}
+
+void SAL_CALL ScVbaPageSetup::setFitToPagesTall( const uno::Any& fitToPagesTall)
+{
+ try
+ {
+ sal_uInt16 scaleToPageY = 0;
+ bool aValue;
+ if( fitToPagesTall.getValueTypeClass() != uno::TypeClass_BOOLEAN || (fitToPagesTall >>= aValue))
+ {
+ fitToPagesTall >>= scaleToPageY;
+ }
+
+ mxPageProps->setPropertyValue("ScaleToPagesY", uno::Any( scaleToPageY ));
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+uno::Any SAL_CALL ScVbaPageSetup::getFitToPagesWide()
+{
+ return mxPageProps->getPropertyValue("ScaleToPagesX");
+}
+
+void SAL_CALL ScVbaPageSetup::setFitToPagesWide( const uno::Any& fitToPagesWide)
+{
+ try
+ {
+ sal_uInt16 scaleToPageX = 0;
+ bool aValue = false;
+ if( fitToPagesWide.getValueTypeClass() != uno::TypeClass_BOOLEAN || (fitToPagesWide >>= aValue))
+ {
+ fitToPagesWide >>= scaleToPageX;
+ }
+
+ mxPageProps->setPropertyValue("ScaleToPagesX", uno::Any( scaleToPageX ));
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+uno::Any SAL_CALL ScVbaPageSetup::getZoom()
+{
+ return mxPageProps->getPropertyValue("PageScale");
+}
+
+void SAL_CALL ScVbaPageSetup::setZoom( const uno::Any& zoom)
+{
+ sal_uInt16 pageScale = 0;
+ try
+ {
+ if( zoom.getValueTypeClass() == uno::TypeClass_BOOLEAN )
+ {
+ bool aValue = false;
+ zoom >>= aValue;
+ if( aValue )
+ {
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+ }
+ else
+ {
+ zoom >>= pageScale;
+ if(( pageScale < ZOOM_IN )||( pageScale > ZOOM_MAX ))
+ {
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+ }
+
+ // these only exist in S08
+ sal_uInt16 nScale = 0;
+ mxPageProps->setPropertyValue("ScaleToPages", uno::Any( nScale ));
+ mxPageProps->setPropertyValue("ScaleToPagesX", uno::Any( nScale ));
+ mxPageProps->setPropertyValue("ScaleToPagesY", uno::Any( nScale ));
+ }
+ catch (const beans::UnknownPropertyException&)
+ {
+ if( pageScale == 0 )
+ {
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ mxPageProps->setPropertyValue("PageScale", uno::Any( pageScale ));
+}
+
+OUString SAL_CALL ScVbaPageSetup::getLeftHeader()
+{
+ OUString leftHeader;
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xHeaderContent( mxPageProps->getPropertyValue("RightPageHeaderContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xHeaderContent->getLeftText();
+ leftHeader = xText->getString();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return leftHeader;
+}
+
+void SAL_CALL ScVbaPageSetup::setLeftHeader( const OUString& leftHeader)
+{
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xHeaderContent( mxPageProps->getPropertyValue("RightPageHeaderContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xHeaderContent->getLeftText();
+ xText->setString( leftHeader );
+ mxPageProps->setPropertyValue("RightPageHeaderContent", uno::Any(xHeaderContent) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+OUString SAL_CALL ScVbaPageSetup::getCenterHeader()
+{
+ OUString centerHeader;
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xHeaderContent( mxPageProps->getPropertyValue("RightPageHeaderContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xHeaderContent->getCenterText();
+ centerHeader = xText->getString();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return centerHeader;
+}
+
+void SAL_CALL ScVbaPageSetup::setCenterHeader( const OUString& centerHeader)
+{
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xHeaderContent( mxPageProps->getPropertyValue("RightPageHeaderContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xHeaderContent->getCenterText();
+ xText->setString( centerHeader );
+ mxPageProps->setPropertyValue("RightPageHeaderContent", uno::Any(xHeaderContent) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+OUString SAL_CALL ScVbaPageSetup::getRightHeader()
+{
+ OUString rightHeader;
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xHeaderContent( mxPageProps->getPropertyValue("RightPageHeaderContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xHeaderContent->getRightText();
+ rightHeader = xText->getString();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return rightHeader;
+}
+
+void SAL_CALL ScVbaPageSetup::setRightHeader( const OUString& rightHeader)
+{
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xHeaderContent( mxPageProps->getPropertyValue("RightPageHeaderContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xHeaderContent->getRightText();
+ xText->setString( rightHeader );
+ mxPageProps->setPropertyValue("RightPageHeaderContent", uno::Any(xHeaderContent) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+OUString SAL_CALL ScVbaPageSetup::getLeftFooter()
+{
+ OUString leftFooter;
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xFooterContent( mxPageProps->getPropertyValue("RightPageFooterContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xFooterContent->getLeftText();
+ leftFooter = xText->getString();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return leftFooter;
+}
+
+void SAL_CALL ScVbaPageSetup::setLeftFooter( const OUString& leftFooter)
+{
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xFooterContent( mxPageProps->getPropertyValue("RightPageFooterContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xFooterContent->getLeftText();
+ xText->setString( leftFooter );
+ mxPageProps->setPropertyValue("RightPageFooterContent", uno::Any(xFooterContent) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+OUString SAL_CALL ScVbaPageSetup::getCenterFooter()
+{
+ OUString centerFooter;
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xFooterContent( mxPageProps->getPropertyValue("RightPageFooterContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xFooterContent->getCenterText();
+ centerFooter = xText->getString();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return centerFooter;
+}
+
+void SAL_CALL ScVbaPageSetup::setCenterFooter( const OUString& centerFooter)
+{
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xFooterContent( mxPageProps->getPropertyValue("RightPageFooterContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xFooterContent->getCenterText();
+ xText->setString( centerFooter );
+ mxPageProps->setPropertyValue("RightPageFooterContent", uno::Any(xFooterContent) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+}
+
+OUString SAL_CALL ScVbaPageSetup::getRightFooter()
+{
+ OUString rightFooter;
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xFooterContent( mxPageProps->getPropertyValue("RightPageFooterContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xFooterContent->getRightText();
+ rightFooter = xText->getString();
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return rightFooter;
+}
+
+void SAL_CALL ScVbaPageSetup::setRightFooter( const OUString& rightFooter)
+{
+ try
+ {
+ uno::Reference<sheet::XHeaderFooterContent> xFooterContent( mxPageProps->getPropertyValue("RightPageFooterContent"), uno::UNO_QUERY_THROW);
+ uno::Reference< text::XText > xText = xFooterContent->getRightText();
+ xText->setString( rightFooter );
+ mxPageProps->setPropertyValue("RightPageFooterContent", uno::Any(xFooterContent) );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+sal_Int32 SAL_CALL ScVbaPageSetup::getOrder()
+{
+ sal_Int32 order = excel::XlOrder::xlDownThenOver;
+ try
+ {
+ uno::Any aValue = mxPageProps->getPropertyValue("PrintDownFirst");
+ bool bPrintDownFirst = false;
+ aValue >>= bPrintDownFirst;
+ if( !bPrintDownFirst )
+ order = excel::XlOrder::xlOverThenDown;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return order;
+}
+
+void SAL_CALL ScVbaPageSetup::setOrder(sal_Int32 order)
+{
+ bool bOrder = true;
+ switch( order )
+ {
+ case excel::XlOrder::xlDownThenOver:
+ break;
+ case excel::XlOrder::xlOverThenDown:
+ bOrder = false;
+ break;
+ default:
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+
+ try
+ {
+ mxPageProps->setPropertyValue("PrintDownFirst", uno::Any( bOrder ));
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+sal_Int32 SAL_CALL ScVbaPageSetup::getFirstPageNumber()
+{
+ sal_Int16 number = 0;
+ try
+ {
+ uno::Any aValue = mxPageProps->getPropertyValue("FirstPageNumber");
+ aValue >>= number;
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ if( number ==0 )
+ {
+ number = excel::Constants::xlAutomatic;
+ }
+
+ return number;
+}
+
+void SAL_CALL ScVbaPageSetup::setFirstPageNumber( sal_Int32 firstPageNumber)
+{
+ if( firstPageNumber == excel::Constants::xlAutomatic )
+ firstPageNumber = 0;
+
+ try
+ {
+ uno::Any aValue;
+ aValue <<= static_cast<sal_Int16>(firstPageNumber);
+ mxPageProps->setPropertyValue("FirstPageNumber", aValue );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+sal_Bool SAL_CALL ScVbaPageSetup::getCenterVertically()
+{
+ bool centerVertically = false;
+ try
+ {
+ uno::Any aValue = mxPageProps->getPropertyValue("CenterVertically");
+ aValue >>= centerVertically;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return centerVertically;
+}
+
+void SAL_CALL ScVbaPageSetup::setCenterVertically( sal_Bool centerVertically)
+{
+ try
+ {
+ mxPageProps->setPropertyValue("CenterVertically", uno::Any( centerVertically ));
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+sal_Bool SAL_CALL ScVbaPageSetup::getCenterHorizontally()
+{
+ bool centerHorizontally = false;
+ try
+ {
+ uno::Any aValue = mxPageProps->getPropertyValue("CenterHorizontally");
+ aValue >>= centerHorizontally;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return centerHorizontally;
+}
+
+void SAL_CALL ScVbaPageSetup::setCenterHorizontally( sal_Bool centerHorizontally)
+{
+ try
+ {
+ mxPageProps->setPropertyValue("CenterHorizontally", uno::Any( centerHorizontally ));
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+sal_Bool SAL_CALL ScVbaPageSetup::getPrintHeadings()
+{
+ bool printHeadings = false;
+ try
+ {
+ uno::Any aValue = mxPageProps->getPropertyValue("PrintHeaders");
+ aValue >>= printHeadings;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return printHeadings;
+}
+
+void SAL_CALL ScVbaPageSetup::setPrintHeadings( sal_Bool printHeadings)
+{
+ try
+ {
+ mxPageProps->setPropertyValue("PrintHeaders", uno::Any( printHeadings ));
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+sal_Bool SAL_CALL ScVbaPageSetup::getPrintGridlines()
+{
+ return false;
+}
+
+void SAL_CALL ScVbaPageSetup::setPrintGridlines( sal_Bool /*_printgridlines*/ )
+{
+}
+
+OUString SAL_CALL ScVbaPageSetup::getPrintTitleRows()
+{
+ return OUString();
+}
+void SAL_CALL ScVbaPageSetup::setPrintTitleRows( const OUString& /*_printtitlerows*/ )
+{
+}
+OUString SAL_CALL ScVbaPageSetup::getPrintTitleColumns()
+{
+ return OUString();
+}
+
+void SAL_CALL ScVbaPageSetup::setPrintTitleColumns( const OUString& /*_printtitlecolumns*/ )
+{
+}
+
+sal_Int32 SAL_CALL ScVbaPageSetup::getPaperSize()
+{
+ awt::Size aSize; // current papersize
+ mxPageProps->getPropertyValue( "Size" ) >>= aSize;
+ if ( mbIsLandscape )
+ ::std::swap( aSize.Width, aSize.Height );
+
+ sal_Int32 nPaperSizeIndex = msfilter::util::PaperSizeConv::getMSPaperSizeIndex( aSize );
+ if ( nPaperSizeIndex == 0 )
+ nPaperSizeIndex = excel::XlPaperSize::xlPaperUser;
+ return nPaperSizeIndex;
+}
+
+void SAL_CALL ScVbaPageSetup::setPaperSize( sal_Int32 papersize )
+{
+ if ( papersize != excel::XlPaperSize::xlPaperUser )
+ {
+ awt::Size aPaperSize;
+ const msfilter::util::ApiPaperSize& rConvertedSize = msfilter::util::PaperSizeConv::getApiSizeForMSPaperSizeIndex( papersize );
+ aPaperSize.Height = rConvertedSize.mnHeight;
+ aPaperSize.Width = rConvertedSize.mnWidth;
+ if ( mbIsLandscape )
+ ::std::swap( aPaperSize.Width, aPaperSize.Height );
+ mxPageProps->setPropertyValue( "Size", uno::Any( aPaperSize ) );
+ }
+}
+
+OUString
+ScVbaPageSetup::getServiceImplName()
+{
+ return "ScVbaPageSetup";
+}
+
+uno::Sequence< OUString >
+ScVbaPageSetup::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.PageSetup"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapagesetup.hxx b/sc/source/ui/vba/vbapagesetup.hxx
new file mode 100644
index 0000000000..6f2f8b45c5
--- /dev/null
+++ b/sc/source/ui/vba/vbapagesetup.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XPageSetup.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <vbahelper/vbapagesetupbase.hxx>
+
+typedef cppu::ImplInheritanceHelper<VbaPageSetupBase, ov::excel::XPageSetup> ScVbaPageSetup_BASE;
+
+class ScVbaPageSetup : public ScVbaPageSetup_BASE
+{
+ css::uno::Reference<css::sheet::XSpreadsheet> mxSheet;
+ bool mbIsLandscape;
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaPageSetup(const css::uno::Reference<ov::XHelperInterface>& xParent,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ css::uno::Reference<css::sheet::XSpreadsheet> xSheet,
+ const css::uno::Reference<css::frame::XModel>& xModel);
+
+ // Attribute
+ virtual OUString SAL_CALL getPrintArea() override;
+ virtual void SAL_CALL setPrintArea(const OUString& rAreas) override;
+ virtual double SAL_CALL getHeaderMargin() override;
+ void SAL_CALL setHeaderMargin(double margin) override;
+ double SAL_CALL getFooterMargin() override;
+ void SAL_CALL setFooterMargin(double margin) override;
+ virtual css::uno::Any SAL_CALL getFitToPagesTall() override;
+ virtual void SAL_CALL setFitToPagesTall(const css::uno::Any& fitToPagesTall) override;
+ virtual css::uno::Any SAL_CALL getFitToPagesWide() override;
+ virtual void SAL_CALL setFitToPagesWide(const css::uno::Any& fitToPagesWide) override;
+ virtual css::uno::Any SAL_CALL getZoom() override;
+ virtual void SAL_CALL setZoom(const css::uno::Any& zoom) override;
+ virtual OUString SAL_CALL getLeftHeader() override;
+ virtual void SAL_CALL setLeftHeader(const OUString& leftHeader) override;
+ virtual OUString SAL_CALL getCenterHeader() override;
+ virtual void SAL_CALL setCenterHeader(const OUString& centerHeader) override;
+ virtual OUString SAL_CALL getRightHeader() override;
+ virtual void SAL_CALL setRightHeader(const OUString& rightHeader) override;
+ virtual OUString SAL_CALL getLeftFooter() override;
+ virtual void SAL_CALL setLeftFooter(const OUString& leftFooter) override;
+ virtual OUString SAL_CALL getCenterFooter() override;
+ virtual void SAL_CALL setCenterFooter(const OUString& centerFooter) override;
+ virtual OUString SAL_CALL getRightFooter() override;
+ virtual void SAL_CALL setRightFooter(const OUString& rightFooter) override;
+ virtual sal_Int32 SAL_CALL getOrder() override;
+ virtual void SAL_CALL setOrder(sal_Int32 order) override;
+ virtual sal_Int32 SAL_CALL getFirstPageNumber() override;
+ virtual void SAL_CALL setFirstPageNumber(sal_Int32 firstPageNumber) override;
+ virtual sal_Bool SAL_CALL getCenterVertically() override;
+ virtual void SAL_CALL setCenterVertically(sal_Bool centerVertically) override;
+ virtual sal_Bool SAL_CALL getCenterHorizontally() override;
+ virtual void SAL_CALL setCenterHorizontally(sal_Bool centerHorizontally) override;
+ virtual sal_Bool SAL_CALL getPrintHeadings() override;
+ virtual void SAL_CALL setPrintHeadings(sal_Bool printHeadings) override;
+
+ virtual sal_Bool SAL_CALL getPrintGridlines() override;
+ virtual void SAL_CALL setPrintGridlines(sal_Bool _printgridlines) override;
+ virtual OUString SAL_CALL getPrintTitleRows() override;
+ virtual void SAL_CALL setPrintTitleRows(const OUString& _printtitlerows) override;
+ virtual OUString SAL_CALL getPrintTitleColumns() override;
+ virtual void SAL_CALL setPrintTitleColumns(const OUString& _printtitlecolumns) override;
+ virtual sal_Int32 SAL_CALL getPaperSize() override;
+ virtual void SAL_CALL setPaperSize(sal_Int32 papersize) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapalette.cxx b/sc/source/ui/vba/vbapalette.cxx
new file mode 100644
index 0000000000..81caebab60
--- /dev/null
+++ b/sc/source/ui/vba/vbapalette.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 "vbapalette.hxx"
+
+#include <sal/macros.h>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/objsh.hxx>
+#include <docsh.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include "excelvbahelper.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+/** Standard EGA colors, bright. */
+#define EXC_PALETTE_EGA_COLORS_LIGHT \
+ Color(0x000000), Color(0xFFFFFF), Color(0xFF0000), Color(0x00FF00), Color(0x0000FF), Color(0xFFFF00), Color(0xFF00FF), Color(0x00FFFF)
+/** Standard EGA colors), dark. */
+#define EXC_PALETTE_EGA_COLORS_DARK \
+ Color(0x800000), Color(0x008000), Color(0x000080), Color(0x808000), Color(0x800080), Color(0x008080), Color(0xC0C0C0), Color(0x808080)
+
+const Color spnDefColorTable8[] =
+{
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x9999FF), Color(0x993366), Color(0xFFFFCC), Color(0xCCFFFF), Color(0x660066), Color(0xFF8080), Color(0x0066CC), Color(0xCCCCFF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CCFF), Color(0xCCFFFF), Color(0xCCFFCC), Color(0xFFFF99), Color(0x99CCFF), Color(0xFF99CC), Color(0xCC99FF), Color(0xFFCC99),
+/* 48 */ Color(0x3366FF), Color(0x33CCCC), Color(0x99CC00), Color(0xFFCC00), Color(0xFF9900), Color(0xFF6600), Color(0x666699), Color(0x969696),
+/* 56 */ Color(0x003366), Color(0x339966), Color(0x003300), Color(0x333300), Color(0x993300), Color(0x993366), Color(0x333399), Color(0x333333)
+};
+
+typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE;
+
+namespace {
+
+class DefaultPalette : public XIndexAccess_BASE
+{
+public:
+ DefaultPalette(){}
+
+ // Methods XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount() override
+ {
+ return SAL_N_ELEMENTS(spnDefColorTable8);
+ }
+
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ if ( Index < 0 || Index >= getCount() )
+ throw lang::IndexOutOfBoundsException();
+ return uno::Any( sal_Int32( spnDefColorTable8[ Index ] ) );
+ }
+
+ // Methods XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return ::cppu::UnoType<sal_Int32>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return true;
+ }
+
+};
+
+}
+
+ScVbaPalette::ScVbaPalette( const uno::Reference< frame::XModel >& rxModel ) :
+ m_pShell( excel::getDocShell( rxModel ) )
+{
+}
+
+uno::Reference< container::XIndexAccess >
+ScVbaPalette::getDefaultPalette()
+{
+ return new DefaultPalette();
+}
+
+uno::Reference< container::XIndexAccess >
+ScVbaPalette::getPalette() const
+{
+ uno::Reference< container::XIndexAccess > xIndex;
+ uno::Reference< beans::XPropertySet > xProps;
+ if ( !m_pShell )
+ throw uno::RuntimeException("Can't extract palette, no doc shell" );
+
+ xProps.set( m_pShell->GetModel(), uno::UNO_QUERY_THROW );
+
+ xIndex.set( xProps->getPropertyValue("ColorPalette"), uno::UNO_QUERY );
+ if ( !xIndex.is() )
+ return new DefaultPalette();
+ return xIndex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapalette.hxx b/sc/source/ui/vba/vbapalette.hxx
new file mode 100644
index 0000000000..cc0d66f3c2
--- /dev/null
+++ b/sc/source/ui/vba/vbapalette.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace container { class XIndexAccess; }
+ namespace frame { class XModel; }
+}
+
+class SfxObjectShell;
+
+class ScVbaPalette
+{
+private:
+ SfxObjectShell* m_pShell;
+public:
+ explicit ScVbaPalette( SfxObjectShell* pShell ) : m_pShell( pShell ) {}
+ explicit ScVbaPalette( const css::uno::Reference< css::frame::XModel >& rxModel );
+ // if no palette available e.g. because the document doesn't have a
+ // palette defined then a default palette will be returned.
+ css::uno::Reference< css::container::XIndexAccess > getPalette() const;
+ static css::uno::Reference< css::container::XIndexAccess > getDefaultPalette();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapane.cxx b/sc/source/ui/vba/vbapane.cxx
new file mode 100644
index 0000000000..9e9dd2e33f
--- /dev/null
+++ b/sc/source/ui/vba/vbapane.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 "vbapane.hxx"
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <utility>
+#include "vbarange.hxx"
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+ScVbaPane::ScVbaPane(
+ const css::uno::Reference< ov::XHelperInterface >& xParent,
+ uno::Reference< uno::XComponentContext > xContext,
+ const uno::Reference< frame::XModel >& rModel,
+ const uno::Reference< sheet::XViewPane >& rViewPane ) :
+ m_xModel(rModel, uno::UNO_SET_THROW),
+ m_xViewPane(rViewPane, uno::UNO_SET_THROW),
+ m_xParent(xParent),
+ m_xContext(std::move(xContext))
+{
+}
+
+sal_Int32 SAL_CALL
+ScVbaPane::getScrollColumn()
+{
+ return ( m_xViewPane->getFirstVisibleColumn() + 1 );
+}
+
+void SAL_CALL
+ScVbaPane::setScrollColumn( sal_Int32 _scrollcolumn )
+{
+ if( _scrollcolumn < 1 )
+ {
+ throw uno::RuntimeException("Column number should not be less than 1" );
+ }
+ m_xViewPane->setFirstVisibleColumn( _scrollcolumn - 1 );
+}
+
+sal_Int32 SAL_CALL
+ScVbaPane::getScrollRow()
+{
+ return ( m_xViewPane->getFirstVisibleRow() + 1 );
+}
+
+void SAL_CALL
+ScVbaPane::setScrollRow( sal_Int32 _scrollrow )
+{
+ if( _scrollrow < 1 )
+ {
+ throw uno::RuntimeException("Row number should not be less than 1" );
+ }
+ m_xViewPane->setFirstVisibleRow( _scrollrow - 1 );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaPane::getVisibleRange()
+{
+ // TODO: Excel includes partly visible rows/columns, Calc does not
+ table::CellRangeAddress aRangeAddr = m_xViewPane->getVisibleRange();
+ uno::Reference< sheet::XSpreadsheetDocument > xDoc( m_xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xSheetsIA( xDoc->getSheets(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xSheetsIA->getByIndex( aRangeAddr.Sheet ), uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xRange( xSheet->getCellRangeByPosition( aRangeAddr.StartColumn, aRangeAddr.StartRow, aRangeAddr.EndColumn, aRangeAddr.EndRow ), uno::UNO_SET_THROW );
+ // TODO: m_xParent is the window, Range needs the worksheet
+ return new ScVbaRange( m_xParent, m_xContext, xRange );
+}
+
+//Method
+void SAL_CALL
+ScVbaPane::SmallScroll( const uno::Any& Down, const uno::Any& Up, const uno::Any& ToRight, const uno::Any& ToLeft )
+{
+ OUString messageBuffer;
+ sal_Int32 downRows = 0;
+ sal_Int32 rightCols = 0;
+ table::CellRangeAddress visibleRange = m_xViewPane->getVisibleRange();
+
+ if( Down.hasValue() )
+ {
+ sal_Int32 down = 0;
+ if( Down >>= down )
+ downRows += down;
+ else
+ messageBuffer += "Error getting parameter: Down\n";
+ }
+ if( Up.hasValue() )
+ {
+ sal_Int32 up = 0;
+ if( Up >>= up )
+ downRows -= up;
+ else
+ messageBuffer += "Error getting parameter: Up\n";
+ }
+ if( ToRight.hasValue() )
+ {
+ sal_Int32 right = 0;
+ if( ToRight >>= right )
+ rightCols += right;
+ else
+ messageBuffer += "Error getting parameter: ToRight\n";
+ }
+ if( ToLeft.hasValue() )
+ {
+ sal_Int32 left = 0;
+ if( ToLeft >>= left )
+ rightCols -= left;
+ else
+ messageBuffer += "Error getting parameter: ToLeft\n";
+ }
+ if( !messageBuffer.isEmpty() )
+ throw uno::RuntimeException( messageBuffer );
+
+ sal_Int32 newStartRow = visibleRange.StartRow + downRows;
+ if( newStartRow < 0 )
+ newStartRow = 0;
+ sal_Int32 newStartCol = visibleRange.StartColumn + rightCols;
+ if( newStartCol < 0 )
+ newStartCol = 0;
+ m_xViewPane->setFirstVisibleRow( newStartRow );
+ m_xViewPane->setFirstVisibleColumn( newStartCol );
+}
+
+void SAL_CALL
+ScVbaPane::LargeScroll( const uno::Any& Down, const uno::Any& Up, const uno::Any& ToRight, const uno::Any& ToLeft )
+{
+ OUString messageBuffer;
+ table::CellRangeAddress visibleRange = m_xViewPane->getVisibleRange();
+
+ sal_Int32 vertPageSize = 1 + visibleRange.EndRow - visibleRange.StartRow;
+ sal_Int32 horizPageSize = 1 + visibleRange.EndColumn - visibleRange.StartColumn;
+ sal_Int32 downPages = 0;
+ sal_Int32 acrossPages = 0;
+ if( Down.hasValue() )
+ {
+ sal_Int32 down = 0;
+ if( Down >>= down )
+ downPages += down;
+ else
+ messageBuffer += "Error getting parameter: Down\n";
+ }
+ if( Up.hasValue() )
+ {
+ sal_Int32 up = 0;
+ if( Up >>= up )
+ downPages -= up;
+ else
+ messageBuffer += "Error getting parameter: Up\n";
+ }
+ if( ToRight.hasValue() )
+ {
+ sal_Int32 right = 0;
+ if( ToRight >>= right )
+ acrossPages += right;
+ else
+ messageBuffer += "Error getting parameter: ToRight\n";
+ }
+ if( ToLeft.hasValue() )
+ {
+ sal_Int32 left = 0;
+ if( ToLeft >>= left )
+ acrossPages -= left;
+ else
+ messageBuffer += "Error getting parameter: ToLeft\n";
+ }
+ if( !messageBuffer.isEmpty() )
+ throw uno::RuntimeException( messageBuffer );
+
+ sal_Int32 newStartRow = visibleRange.StartRow + (downPages * vertPageSize );
+ if( newStartRow < 0 )
+ newStartRow = 0;
+ sal_Int32 newStartCol = visibleRange.StartColumn + (acrossPages * horizPageSize );
+ if( newStartCol < 0 )
+ newStartCol = 0;
+ m_xViewPane->setFirstVisibleRow( newStartRow );
+ m_xViewPane->setFirstVisibleColumn( newStartCol );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapane.hxx b/sc/source/ui/vba/vbapane.hxx
new file mode 100644
index 0000000000..962e683910
--- /dev/null
+++ b/sc/source/ui/vba/vbapane.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 .
+ */
+#pragma once
+
+#include <com/sun/star/sheet/XViewPane.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <ooo/vba/excel/XPane.hpp>
+#include <vbahelper/vbahelper.hxx>
+
+class ScVbaPane final : public cppu::WeakImplHelper< ov::excel::XPane >
+{
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaPane(
+ const css::uno::Reference< ov::XHelperInterface >& rParent,
+ css::uno::Reference< css::uno::XComponentContext > xContext,
+ const css::uno::Reference< css::frame::XModel >& rModel,
+ const css::uno::Reference< css::sheet::XViewPane >& rViewPane );
+
+ // XPane attributes
+ virtual sal_Int32 SAL_CALL getScrollColumn() override;
+ virtual void SAL_CALL setScrollColumn( sal_Int32 _scrollcolumn ) override;
+ virtual sal_Int32 SAL_CALL getScrollRow() override;
+ virtual void SAL_CALL setScrollRow( sal_Int32 _scrollrow ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getVisibleRange() override;
+
+ // XPane methods
+ virtual void SAL_CALL SmallScroll( const css::uno::Any& Down, const css::uno::Any& Up, const css::uno::Any& ToRight, const css::uno::Any& ToLeft ) override;
+ virtual void SAL_CALL LargeScroll( const css::uno::Any& Down, const css::uno::Any& Up, const css::uno::Any& ToRight, const css::uno::Any& ToLeft ) override;
+
+private:
+ css::uno::Reference< css::frame::XModel > m_xModel;
+ css::uno::Reference< css::sheet::XViewPane > m_xViewPane;
+ css::uno::WeakReference< ov::XHelperInterface > m_xParent;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapivotcache.cxx b/sc/source/ui/vba/vbapivotcache.cxx
new file mode 100644
index 0000000000..15f81c0eb3
--- /dev/null
+++ b/sc/source/ui/vba/vbapivotcache.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 <utility>
+
+#include "vbapivotcache.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+ScVbaPivotCache::ScVbaPivotCache( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< sheet::XDataPilotTable > xTable ) : PivotCacheImpl_BASE( xParent, xContext ), m_xTable(std::move( xTable ))
+{
+}
+
+void SAL_CALL
+ScVbaPivotCache::Refresh()
+{
+ m_xTable->refresh();
+}
+
+OUString
+ScVbaPivotCache::getServiceImplName()
+{
+ return "ScVbaPivotCache";
+}
+
+uno::Sequence< OUString >
+ScVbaPivotCache::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.PivotCache"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapivotcache.hxx b/sc/source/ui/vba/vbapivotcache.hxx
new file mode 100644
index 0000000000..49770f917b
--- /dev/null
+++ b/sc/source/ui/vba/vbapivotcache.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 <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/sheet/XDataPilotTable.hpp>
+
+#include <ooo/vba/excel/XPivotCache.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl<ov::excel::XPivotCache > PivotCacheImpl_BASE;
+
+class ScVbaPivotCache : public PivotCacheImpl_BASE
+{
+ css::uno::Reference< css::sheet::XDataPilotTable > m_xTable;
+public:
+ ScVbaPivotCache( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< css::sheet::XDataPilotTable > xTable );
+
+ virtual void SAL_CALL Refresh() override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapivottable.cxx b/sc/source/ui/vba/vbapivottable.cxx
new file mode 100644
index 0000000000..263a695f79
--- /dev/null
+++ b/sc/source/ui/vba/vbapivottable.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 <utility>
+
+#include "vbapivottable.hxx"
+#include "vbapivotcache.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+ScVbaPivotTable::ScVbaPivotTable( const uno::Reference< uno::XComponentContext >& xContext, uno::Reference< sheet::XDataPilotTable > xTable ) : PivotTableImpl_BASE( uno::Reference< XHelperInterface >(), xContext), m_xTable(std::move( xTable ))
+{
+}
+
+uno::Reference< excel::XPivotCache >
+ScVbaPivotTable::PivotCache()
+{
+ // #FIXME with a quick example failed to determine what the parent
+ // should be, leaving as null at the moment
+ return new ScVbaPivotCache( uno::Reference< XHelperInterface >(), mxContext, m_xTable );
+}
+
+OUString
+ScVbaPivotTable::getServiceImplName()
+{
+ return "ScVbaPivotTable";
+}
+
+uno::Sequence< OUString >
+ScVbaPivotTable::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.PivotTable"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapivottable.hxx b/sc/source/ui/vba/vbapivottable.hxx
new file mode 100644
index 0000000000..c584ebecf1
--- /dev/null
+++ b/sc/source/ui/vba/vbapivottable.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 <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/sheet/XDataPilotTable.hpp>
+#include <ooo/vba/excel/XPivotTable.hpp>
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl<ov::excel::XPivotTable> PivotTableImpl_BASE;
+
+class ScVbaPivotTable : public PivotTableImpl_BASE
+{
+ css::uno::Reference<css::sheet::XDataPilotTable> m_xTable;
+
+public:
+ ScVbaPivotTable(const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ css::uno::Reference<css::sheet::XDataPilotTable> xTable);
+ virtual css::uno::Reference<ov::excel::XPivotCache> SAL_CALL PivotCache() override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapivottables.cxx b/sc/source/ui/vba/vbapivottables.cxx
new file mode 100644
index 0000000000..f49fbeaeb9
--- /dev/null
+++ b/sc/source/ui/vba/vbapivottables.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 "vbapivottables.hxx"
+#include "vbapivottable.hxx"
+#include <com/sun/star/sheet/XDataPilotTable.hpp>
+#include <ooo/vba/excel/XPivotTable.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+static uno::Any DataPilotToPivotTable( const uno::Any& aSource, const uno::Reference< uno::XComponentContext > & xContext )
+{
+ uno::Reference< sheet::XDataPilotTable > xTable( aSource, uno::UNO_QUERY_THROW );
+ return uno::Any( uno::Reference< excel::XPivotTable > ( new ScVbaPivotTable( xContext, xTable ) ) );
+}
+
+namespace {
+
+class PivotTableEnumeration : public EnumerationHelperImpl
+{
+public:
+ /// @throws uno::RuntimeException
+ PivotTableEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ) {}
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ return DataPilotToPivotTable( m_xEnumeration->nextElement(), m_xContext );
+ }
+
+};
+
+}
+
+ScVbaPivotTables::ScVbaPivotTables( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext > & xContext, const uno::Reference< container::XIndexAccess >& xIndexAccess ): ScVbaPivotTables_BASE( xParent, xContext, xIndexAccess )
+{
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaPivotTables::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
+ return new PivotTableEnumeration( mxParent, mxContext, xEnumAccess->createEnumeration() );
+}
+
+uno::Any
+ScVbaPivotTables::createCollectionObject( const css::uno::Any& aSource )
+{
+ return DataPilotToPivotTable( aSource, mxContext );
+}
+
+uno::Type
+ScVbaPivotTables::getElementType()
+{
+ return cppu::UnoType<excel::XPivotTable>::get();
+}
+
+OUString
+ScVbaPivotTables::getServiceImplName()
+{
+ return "ScVbaPivotTables";
+}
+
+css::uno::Sequence<OUString>
+ScVbaPivotTables::getServiceNames()
+{
+ static uno::Sequence< OUString > const sNames
+ {
+ "ooo.vba.excel.PivotTables"
+ };
+ return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbapivottables.hxx b/sc/source/ui/vba/vbapivottables.hxx
new file mode 100644
index 0000000000..69b16b96f7
--- /dev/null
+++ b/sc/source/ui/vba/vbapivottables.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XPivotTables.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+typedef CollTestImplHelper< ov::excel::XPivotTables > ScVbaPivotTables_BASE;
+
+class ScVbaPivotTables : public ScVbaPivotTables_BASE
+{
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+public:
+ ScVbaPivotTables( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::container::XIndexAccess >& xIndexAccess );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XPivotTables
+
+ // ScVbaPivotTables_BASE
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ virtual OUString getServiceImplName() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbarange.cxx b/sc/source/ui/vba/vbarange.cxx
new file mode 100644
index 0000000000..f1ce525daa
--- /dev/null
+++ b/sc/source/ui/vba/vbarange.cxx
@@ -0,0 +1,5778 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbarange.hxx"
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <rtl/math.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/script/ArrayWrapper.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XGoalSeek.hpp>
+#include <com/sun/star/sheet/XSheetOperation.hpp>
+#include <com/sun/star/sheet/CellFlags.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/sheet/XCellAddressable.hpp>
+#include <com/sun/star/table/CellContentType.hpp>
+#include <com/sun/star/sheet/XCellSeries.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+#include <com/sun/star/sheet/XCellRangeReferrer.hpp>
+#include <com/sun/star/sheet/XSheetCellRange.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSheetCellCursor.hpp>
+#include <com/sun/star/sheet/XArrayFormulaRange.hpp>
+#include <com/sun/star/sheet/XNamedRange.hpp>
+#include <com/sun/star/sheet/XNamedRanges.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <com/sun/star/sheet/XCellRangesQuery.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/table/XTableRows.hpp>
+#include <com/sun/star/table/XTableColumns.hpp>
+#include <com/sun/star/table/TableSortField.hpp>
+#include <com/sun/star/util/XMergeable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XReplaceable.hpp>
+#include <com/sun/star/util/XSortable.hpp>
+#include <com/sun/star/sheet/XCellRangeMovement.hpp>
+#include <com/sun/star/sheet/FormulaResult.hpp>
+#include <com/sun/star/sheet/FilterOperator2.hpp>
+#include <com/sun/star/sheet/TableFilterField2.hpp>
+#include <com/sun/star/sheet/XSheetFilterDescriptor2.hpp>
+#include <com/sun/star/sheet/FilterConnection.hpp>
+#include <com/sun/star/util/TriState.hpp>
+
+#include <com/sun/star/sheet/XSubTotalCalculatable.hpp>
+#include <com/sun/star/sheet/XSubTotalDescriptor.hpp>
+#include <com/sun/star/sheet/GeneralFunction.hpp>
+
+#include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
+#include <com/sun/star/sheet/XSheetAnnotations.hpp>
+
+#include <ooo/vba/excel/XlPasteSpecialOperation.hpp>
+#include <ooo/vba/excel/XlPasteType.hpp>
+#include <ooo/vba/excel/XlFindLookIn.hpp>
+#include <ooo/vba/excel/XlLookAt.hpp>
+#include <ooo/vba/excel/XlSearchOrder.hpp>
+#include <ooo/vba/excel/XlSortOrder.hpp>
+#include <ooo/vba/excel/XlYesNoGuess.hpp>
+#include <ooo/vba/excel/XlSortOrientation.hpp>
+#include <ooo/vba/excel/XlSortMethod.hpp>
+#include <ooo/vba/excel/XlDirection.hpp>
+#include <ooo/vba/excel/XlSortDataOption.hpp>
+#include <ooo/vba/excel/XlDeleteShiftDirection.hpp>
+#include <ooo/vba/excel/XlInsertShiftDirection.hpp>
+#include <ooo/vba/excel/XlReferenceStyle.hpp>
+#include <ooo/vba/excel/XlBordersIndex.hpp>
+#include <ooo/vba/excel/XlPageBreak.hpp>
+#include <ooo/vba/excel/XlAutoFilterOperator.hpp>
+#include <ooo/vba/excel/XlAutoFillType.hpp>
+#include <ooo/vba/excel/XlCellType.hpp>
+#include <ooo/vba/excel/XlSpecialCellsValue.hpp>
+#include <ooo/vba/excel/XlConsolidationFunction.hpp>
+#include <ooo/vba/excel/XlSearchDirection.hpp>
+
+#include <scitems.hxx>
+#include <svl/srchitem.hxx>
+#include <cellsuno.hxx>
+#include <dbdata.hxx>
+#include <docfunc.hxx>
+#include <columnspanset.hxx>
+#include <queryparam.hxx>
+#include <sortparam.hxx>
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sc.hrc>
+#include <unonames.hxx>
+
+#include "excelvbahelper.hxx"
+#include "vbaapplication.hxx"
+#include "vbafont.hxx"
+#include "vbacomment.hxx"
+#include "vbainterior.hxx"
+#include "vbacharacters.hxx"
+#include "vbaborders.hxx"
+#include "vbaworksheet.hxx"
+#include "vbavalidation.hxx"
+#include "vbahyperlinks.hxx"
+
+#include <tabvwsh.hxx>
+#include <rangelst.hxx>
+#include <convuno.hxx>
+#include <compiler.hxx>
+#include <patattr.hxx>
+#include <olinetab.hxx>
+#include <transobj.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <basic/sberrors.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <global.hxx>
+
+#include "vbastyle.hxx"
+#include "vbaname.hxx"
+#include <utility>
+#include <vector>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+#include <com/sun/star/bridge/oleautomation/Date.hpp>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+
+#include <memory>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+using ::std::vector;
+
+// difference between VBA and file format width, in character units
+const double fExtraWidth = 182.0 / 256.0;
+
+const sal_Int16 supportedIndexTable[] = { excel::XlBordersIndex::xlEdgeLeft, excel::XlBordersIndex::xlEdgeTop, excel::XlBordersIndex::xlEdgeBottom, excel::XlBordersIndex::xlEdgeRight, excel::XlBordersIndex::xlDiagonalDown, excel::XlBordersIndex::xlDiagonalUp, excel::XlBordersIndex::xlInsideVertical, excel::XlBordersIndex::xlInsideHorizontal };
+
+static sal_uInt16 lcl_pointsToTwips( double nVal )
+{
+ nVal = nVal * static_cast<double>(20);
+ short nTwips = static_cast<short>(nVal);
+ return nTwips;
+}
+static double lcl_TwipsToPoints( sal_uInt16 nVal )
+{
+ double nPoints = nVal;
+ return nPoints / 20;
+}
+
+static double lcl_Round2DecPlaces( double nVal )
+{
+ nVal = (nVal * double(100));
+ tools::Long tmp = static_cast<tools::Long>(nVal);
+ if ( ( nVal - tmp ) >= 0.5 )
+ ++tmp;
+ nVal = double(tmp)/100;
+ return nVal;
+}
+
+static uno::Any lcl_makeRange( const uno::Reference< XHelperInterface >& rParent, const uno::Reference< uno::XComponentContext >& rContext, const uno::Any& rAny, bool bIsRows, bool bIsColumns )
+{
+ uno::Reference< table::XCellRange > xCellRange(rAny, uno::UNO_QUERY_THROW);
+ return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( rParent, rContext, xCellRange, bIsRows, bIsColumns ) ) );
+}
+
+static uno::Reference< excel::XRange > lcl_makeXRangeFromSheetCellRanges( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRanges >& xLocSheetCellRanges, ScDocShell* pDoc )
+{
+ uno::Reference< excel::XRange > xRange;
+ const uno::Sequence< table::CellRangeAddress > sAddresses = xLocSheetCellRanges->getRangeAddresses();
+ ScRangeList aCellRanges;
+ if ( sAddresses.hasElements() )
+ {
+ for ( const auto& rAddress : sAddresses )
+ {
+ ScRange refRange;
+ ScUnoConversion::FillScRange( refRange, rAddress );
+ aCellRanges.push_back( refRange );
+ }
+ // Single range
+ if ( aCellRanges.size() == 1 )
+ {
+ uno::Reference< table::XCellRange > xTmpRange( new ScCellRangeObj( pDoc, aCellRanges.front() ) );
+ xRange = new ScVbaRange( xParent, xContext, xTmpRange );
+ }
+ else
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDoc, aCellRanges ) );
+ xRange = new ScVbaRange( xParent, xContext, xRanges );
+ }
+ }
+ return xRange;
+}
+
+ScCellRangesBase* ScVbaRange::getCellRangesBase()
+{
+ if( mxRanges.is() )
+ return dynamic_cast<ScCellRangesBase*>( mxRanges.get() );
+ if( mxRange.is() )
+ return dynamic_cast<ScCellRangesBase*>( mxRange.get() );
+ throw uno::RuntimeException("General Error creating range - Unknown" );
+}
+
+ScCellRangeObj* ScVbaRange::getCellRangeObj()
+{
+ return dynamic_cast< ScCellRangeObj* >( getCellRangesBase() );
+}
+
+SfxItemSet* ScVbaRange::getCurrentDataSet( )
+{
+ SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( getCellRangesBase() );
+ if ( !pDataSet )
+ throw uno::RuntimeException("Can't access Itemset for range" );
+ return pDataSet;
+}
+
+void ScVbaRange::fireChangeEvent()
+{
+ if( !ScVbaApplication::getDocumentEventsEnabled() )
+ return;
+
+ ScDocument& rDoc = getScDocument();
+ const uno::Reference< script::vba::XVBAEventProcessor >& xVBAEvents = rDoc.GetVbaEventProcessor();
+ if( xVBAEvents.is() ) try
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference< excel::XRange >( this )) };
+ xVBAEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_CHANGE, aArgs );
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+namespace {
+
+class SingleRangeEnumeration : public EnumerationHelper_BASE
+{
+ uno::Reference< table::XCellRange > m_xRange;
+ bool bHasMore;
+public:
+ /// @throws uno::RuntimeException
+ explicit SingleRangeEnumeration( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )), bHasMore( true ) { }
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override { return bHasMore; }
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( !bHasMore )
+ throw container::NoSuchElementException();
+ bHasMore = false;
+ return uno::Any( m_xRange );
+ }
+};
+
+// very simple class to pass to ScVbaCollectionBaseImpl containing
+// just one item
+
+class SingleRangeIndexAccess : public ::cppu::WeakImplHelper< container::XIndexAccess,
+ container::XEnumerationAccess >
+{
+private:
+ uno::Reference< table::XCellRange > m_xRange;
+
+public:
+ explicit SingleRangeIndexAccess( uno::Reference< table::XCellRange > xRange ) : m_xRange(std::move( xRange )) {}
+ // XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount() override { return 1; }
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ if ( Index != 0 )
+ throw lang::IndexOutOfBoundsException();
+ return uno::Any( m_xRange );
+ }
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<table::XCellRange>::get(); }
+ virtual sal_Bool SAL_CALL hasElements() override { return true; }
+ // XEnumerationAccess
+ virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override { return new SingleRangeEnumeration( m_xRange ); }
+
+};
+
+class RangesEnumerationImpl : public EnumerationHelperImpl
+{
+ bool mbIsRows;
+ bool mbIsColumns;
+public:
+ /// @throws uno::RuntimeException
+ RangesEnumerationImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, bool bIsRows, bool bIsColumns ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), mbIsRows( bIsRows ), mbIsColumns( bIsColumns ) {}
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ return lcl_makeRange( m_xParent, m_xContext, m_xEnumeration->nextElement(), mbIsRows, mbIsColumns );
+ }
+};
+
+class ScVbaRangeAreas : public ScVbaCollectionBaseImpl
+{
+ bool mbIsRows;
+ bool mbIsColumns;
+public:
+ ScVbaRangeAreas( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XIndexAccess >& xIndexAccess, bool bIsRows, bool bIsColumns ) : ScVbaCollectionBaseImpl( xParent, xContext, xIndexAccess ), mbIsRows( bIsRows ), mbIsColumns( bIsColumns ) {}
+
+ // XEnumerationAccess
+ virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override { return cppu::UnoType<excel::XRange>::get(); }
+
+ virtual uno::Any createCollectionObject( const uno::Any& aSource ) override;
+
+ virtual OUString getServiceImplName() override { return OUString(); }
+
+ virtual uno::Sequence< OUString > getServiceNames() override { return uno::Sequence< OUString >(); }
+
+};
+
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+ScVbaRangeAreas::createEnumeration()
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
+ return new RangesEnumerationImpl( mxParent, mxContext, xEnumAccess->createEnumeration(), mbIsRows, mbIsColumns );
+}
+
+uno::Any
+ScVbaRangeAreas::createCollectionObject( const uno::Any& aSource )
+{
+ return lcl_makeRange( mxParent, mxContext, aSource, mbIsRows, mbIsColumns );
+}
+
+// assume that xIf is in fact a ScCellRangesBase
+/// @throws uno::RuntimeException
+static ScDocShell*
+getDocShellFromIf( const uno::Reference< uno::XInterface >& xIf )
+{
+ ScCellRangesBase* pUno = dynamic_cast<ScCellRangesBase*>( xIf.get() );
+ if ( !pUno )
+ throw uno::RuntimeException("Failed to access underlying uno range object" );
+ return pUno->GetDocShell();
+}
+
+/// @throws uno::RuntimeException
+static ScDocShell*
+getDocShellFromRange( const uno::Reference< table::XCellRange >& xRange )
+{
+ // need the ScCellRangesBase to get docshell
+ uno::Reference< uno::XInterface > xIf( xRange );
+ return getDocShellFromIf(xIf );
+}
+
+/// @throws uno::RuntimeException
+static ScDocShell*
+getDocShellFromRanges( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges )
+{
+ // need the ScCellRangesBase to get docshell
+ uno::Reference< uno::XInterface > xIf( xRanges );
+ return getDocShellFromIf(xIf );
+}
+
+/// @throws uno::RuntimeException
+static uno::Reference< frame::XModel > getModelFromXIf( const uno::Reference< uno::XInterface >& xIf )
+{
+ ScDocShell* pDocShell = getDocShellFromIf(xIf );
+ return pDocShell->GetModel();
+}
+
+/// @throws uno::RuntimeException
+static uno::Reference< frame::XModel > getModelFromRange( const uno::Reference< table::XCellRange >& xRange )
+{
+ // the XInterface for getImplementation can be any derived interface, no need for queryInterface
+ uno::Reference< uno::XInterface > xIf( xRange );
+ return getModelFromXIf( xIf );
+}
+
+static ScDocument&
+getDocumentFromRange( const uno::Reference< table::XCellRange >& xRange )
+{
+ ScDocShell* pDocShell = getDocShellFromRange( xRange );
+ if ( !pDocShell )
+ throw uno::RuntimeException("Failed to access underlying docshell from uno range object" );
+ ScDocument& rDoc = pDocShell->GetDocument();
+ return rDoc;
+}
+
+ScDocument&
+ScVbaRange::getScDocument()
+{
+ if ( mxRanges.is() )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ return getDocumentFromRange( xRange );
+ }
+ return getDocumentFromRange( mxRange );
+}
+
+ScDocShell*
+ScVbaRange::getScDocShell()
+{
+ if ( mxRanges.is() )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xRange( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ return getDocShellFromRange( xRange );
+ }
+ return getDocShellFromRange( mxRange );
+}
+
+ScVbaRange* ScVbaRange::getImplementation( const uno::Reference< excel::XRange >& rxRange )
+{
+ // FIXME: always save to use dynamic_cast? Or better to (implement and) use XTunnel?
+ return dynamic_cast< ScVbaRange* >( rxRange.get() );
+}
+
+uno::Reference< frame::XModel > ScVbaRange::getUnoModel()
+{
+ if( ScDocShell* pDocShell = getScDocShell() )
+ return pDocShell->GetModel();
+ throw uno::RuntimeException();
+}
+
+uno::Reference< frame::XModel > ScVbaRange::getUnoModel( const uno::Reference< excel::XRange >& rxRange )
+{
+ if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
+ return pScVbaRange->getUnoModel();
+ throw uno::RuntimeException();
+}
+
+const ScRangeList& ScVbaRange::getScRangeList()
+{
+ if( ScCellRangesBase* pScRangesBase = getCellRangesBase() )
+ return pScRangesBase->GetRangeList();
+ throw uno::RuntimeException("Cannot obtain UNO range implementation object" );
+}
+
+const ScRangeList& ScVbaRange::getScRangeList( const uno::Reference< excel::XRange >& rxRange )
+{
+ if( ScVbaRange* pScVbaRange = getImplementation( rxRange ) )
+ return pScVbaRange->getScRangeList();
+ throw uno::RuntimeException("Cannot obtain VBA range implementation object" );
+}
+
+namespace {
+
+class NumFormatHelper
+{
+ uno::Reference< util::XNumberFormatsSupplier > mxSupplier;
+ uno::Reference< beans::XPropertySet > mxRangeProps;
+ uno::Reference< util::XNumberFormats > mxFormats;
+public:
+ explicit NumFormatHelper( const uno::Reference< table::XCellRange >& xRange )
+ {
+ mxSupplier.set( getModelFromRange( xRange ), uno::UNO_QUERY_THROW );
+ mxRangeProps.set( xRange, uno::UNO_QUERY_THROW);
+ mxFormats = mxSupplier->getNumberFormats();
+ }
+ uno::Reference< beans::XPropertySet > getNumberProps()
+ {
+ tools::Long nIndexKey = 0;
+ uno::Any aValue = mxRangeProps->getPropertyValue( "NumberFormat" );
+ aValue >>= nIndexKey;
+
+ if ( mxFormats.is() )
+ return mxFormats->getByKey( nIndexKey );
+ return uno::Reference< beans::XPropertySet > ();
+ }
+
+ bool isBooleanType()
+ {
+
+ return (getNumberFormat() & util::NumberFormat::LOGICAL) != 0;
+ }
+
+ bool isDateType()
+ {
+ sal_Int16 nType = getNumberFormat();
+ return ( nType & util::NumberFormat::DATETIME ) != 0;
+ }
+
+ OUString getNumberFormatString()
+ {
+ uno::Reference< uno::XInterface > xIf( mxRangeProps, uno::UNO_QUERY_THROW );
+ ScCellRangesBase* pUnoCellRange = dynamic_cast<ScCellRangesBase*>( xIf.get() );
+ if ( pUnoCellRange )
+ {
+
+ SfxItemSet* pDataSet = excel::ScVbaCellRangeAccess::GetDataSet( pUnoCellRange );
+ SfxItemState eState = pDataSet->GetItemState( ATTR_VALUE_FORMAT);
+ // one of the cells in the range is not like the other ;-)
+ // so return a zero length format to indicate that
+ if ( eState == SfxItemState::DONTCARE )
+ return OUString();
+ }
+
+ uno::Reference< beans::XPropertySet > xNumberProps( getNumberProps(), uno::UNO_SET_THROW );
+ OUString aFormatString;
+ uno::Any aString = xNumberProps->getPropertyValue( "FormatString" );
+ aString >>= aFormatString;
+ return aFormatString;
+ }
+
+ sal_Int16 getNumberFormat()
+ {
+ uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
+ sal_Int16 nType = ::comphelper::getINT16(
+ xNumberProps->getPropertyValue( "Type" ) );
+ return nType;
+ }
+
+ void setNumberFormat( const OUString& rFormat )
+ {
+ // #163288# treat "General" as "Standard" format
+ sal_Int32 nNewIndex = 0;
+ if( !rFormat.equalsIgnoreAsciiCase( "General" ) )
+ {
+ lang::Locale aLocale;
+ uno::Reference< beans::XPropertySet > xNumProps = getNumberProps();
+ xNumProps->getPropertyValue( "Locale" ) >>= aLocale;
+ nNewIndex = mxFormats->queryKey( rFormat, aLocale, false );
+ if ( nNewIndex == -1 ) // format not defined
+ nNewIndex = mxFormats->addNew( rFormat, aLocale );
+ }
+ mxRangeProps->setPropertyValue( "NumberFormat", uno::Any( nNewIndex ) );
+ }
+
+ void setNumberFormat( sal_Int16 nType )
+ {
+ uno::Reference< beans::XPropertySet > xNumberProps = getNumberProps();
+ lang::Locale aLocale;
+ xNumberProps->getPropertyValue( "Locale" ) >>= aLocale;
+ uno::Reference<util::XNumberFormatTypes> xTypes( mxFormats, uno::UNO_QUERY );
+ if ( xTypes.is() )
+ {
+ sal_Int32 nNewIndex = xTypes->getStandardFormat( nType, aLocale );
+ mxRangeProps->setPropertyValue( "NumberFormat", uno::Any( nNewIndex ) );
+ }
+ }
+
+};
+
+struct CellPos
+{
+ CellPos( sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nArea ):m_nRow(nRow), m_nCol(nCol), m_nArea( nArea ) {};
+sal_Int32 m_nRow;
+sal_Int32 m_nCol;
+sal_Int32 m_nArea;
+};
+
+}
+
+typedef ::cppu::WeakImplHelper< container::XEnumeration > CellsEnumeration_BASE;
+typedef ::std::vector< CellPos > vCellPos;
+
+namespace {
+
+// #FIXME - QUICK
+// we could probably could and should modify CellsEnumeration below
+// to handle rows and columns (but I do this separately for now
+// and... this class only handles single areas (does it have to handle
+// multi area ranges??)
+class ColumnsRowEnumeration: public CellsEnumeration_BASE
+{
+ uno::Reference< excel::XRange > mxRange;
+ sal_Int32 mMaxElems;
+ sal_Int32 mCurElem;
+
+public:
+ ColumnsRowEnumeration( uno::Reference< excel::XRange > xRange, sal_Int32 nElems ) : mxRange(std::move( xRange )), mMaxElems( nElems ), mCurElem( 0 )
+ {
+ }
+
+ virtual sal_Bool SAL_CALL hasMoreElements() override { return mCurElem < mMaxElems; }
+
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ if ( !hasMoreElements() )
+ throw container::NoSuchElementException();
+ sal_Int32 vbaIndex = 1 + mCurElem++;
+ return uno::Any( mxRange->Item( uno::Any( vbaIndex ), uno::Any() ) );
+ }
+};
+
+class CellsEnumeration : public CellsEnumeration_BASE
+{
+ uno::WeakReference< XHelperInterface > mxParent;
+ uno::Reference< uno::XComponentContext > mxContext;
+ uno::Reference< XCollection > m_xAreas;
+ vCellPos m_CellPositions;
+ vCellPos::const_iterator m_it;
+
+ /// @throws uno::RuntimeException
+ uno::Reference< table::XCellRange > getArea( sal_Int32 nVBAIndex )
+ {
+ if ( nVBAIndex < 1 || nVBAIndex > m_xAreas->getCount() )
+ throw uno::RuntimeException();
+ uno::Reference< excel::XRange > xRange( m_xAreas->Item( uno::Any(nVBAIndex), uno::Any() ), uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xCellRange( ScVbaRange::getCellRange( xRange ), uno::UNO_QUERY_THROW );
+ return xCellRange;
+ }
+
+ void populateArea( sal_Int32 nVBAIndex )
+ {
+ uno::Reference< table::XCellRange > xRange = getArea( nVBAIndex );
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, uno::UNO_QUERY_THROW );
+ sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
+ sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
+ for ( sal_Int32 i=0; i<nRowCount; ++i )
+ {
+ for ( sal_Int32 j=0; j<nColCount; ++j )
+ m_CellPositions.emplace_back( i,j,nVBAIndex );
+ }
+ }
+public:
+ CellsEnumeration( const uno::Reference< XHelperInterface >& xParent, uno::Reference< uno::XComponentContext > xContext, uno::Reference< XCollection > xAreas ): mxParent( xParent ), mxContext(std::move( xContext )), m_xAreas(std::move( xAreas ))
+ {
+ sal_Int32 nItems = m_xAreas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ populateArea( index );
+ }
+ m_it = m_CellPositions.begin();
+ }
+ virtual sal_Bool SAL_CALL hasMoreElements() override { return m_it != m_CellPositions.end(); }
+
+ virtual uno::Any SAL_CALL nextElement() override
+ {
+ if ( !hasMoreElements() )
+ throw container::NoSuchElementException();
+ CellPos aPos = *m_it++;
+
+ uno::Reference< table::XCellRange > xRangeArea = getArea( aPos.m_nArea );
+ uno::Reference< table::XCellRange > xCellRange( xRangeArea->getCellByPosition( aPos.m_nCol, aPos.m_nRow ), uno::UNO_QUERY_THROW );
+ return uno::Any( uno::Reference< excel::XRange >( new ScVbaRange( mxParent, mxContext, xCellRange ) ) );
+
+ }
+};
+
+}
+
+constexpr OUString ISVISIBLE = u"IsVisible"_ustr;
+const char EQUALS[] = "=";
+const char NOTEQUALS[] = "<>";
+const char GREATERTHAN[] = ">";
+const char GREATERTHANEQUALS[] = ">=";
+const char LESSTHAN[] = "<";
+const char LESSTHANEQUALS[] = "<=";
+constexpr OUString STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY(u"The command you chose cannot be performed with multiple selections.\nSelect a single range and click the command again"_ustr);
+constexpr OUString CELLSTYLE = u"CellStyle"_ustr;
+
+namespace {
+
+class CellValueSetter : public ValueSetter
+{
+protected:
+ uno::Any maValue;
+public:
+ explicit CellValueSetter( uno::Any aValue );
+ virtual bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override;
+ virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
+
+};
+
+}
+
+CellValueSetter::CellValueSetter( uno::Any aValue ): maValue(std::move( aValue )) {}
+
+void
+CellValueSetter::visitNode( sal_Int32 /*i*/, sal_Int32 /*j*/, const uno::Reference< table::XCell >& xCell )
+{
+ processValue( maValue, xCell );
+}
+
+bool
+CellValueSetter::processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell )
+{
+
+ bool isExtracted = false;
+ switch ( aValue.getValueTypeClass() )
+ {
+ case uno::TypeClass_BOOLEAN:
+ {
+ bool bState = false;
+ if ( aValue >>= bState )
+ {
+ uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
+ if ( bState )
+ xCell->setValue( double(1) );
+ else
+ xCell->setValue( double(0) );
+ NumFormatHelper cellNumFormat( xRange );
+ cellNumFormat.setNumberFormat( util::NumberFormat::LOGICAL );
+ }
+ break;
+ }
+ case uno::TypeClass_STRING:
+ {
+ OUString aString;
+ if ( aValue >>= aString )
+ {
+ // The required behavior for a string value is:
+ // 1. If the first character is a single quote, use the rest as a string cell, regardless of the cell's number format.
+ // 2. Otherwise, if the cell's number format is "text", use the string value as a string cell.
+ // 3. Otherwise, parse the string value in English locale, and apply a corresponding number format with the cell's locale
+ // if the cell's number format was "General".
+ // Case 1 is handled here, the rest in ScCellObj::InputEnglishString
+
+ if ( aString.toChar() == '\'' ) // case 1 - handle with XTextRange
+ {
+ OUString aRemainder( aString.copy(1) ); // strip the quote
+ uno::Reference< text::XTextRange > xTextRange( xCell, uno::UNO_QUERY_THROW );
+ xTextRange->setString( aRemainder );
+ }
+ else
+ {
+ // call implementation method InputEnglishString
+ ScCellObj* pCellObj = dynamic_cast< ScCellObj* >( xCell.get() );
+ if ( pCellObj )
+ pCellObj->InputEnglishString( aString );
+ }
+ }
+ else
+ isExtracted = false;
+ break;
+ }
+ default:
+ {
+ double nDouble = 0.0;
+ if ( aValue >>= nDouble )
+ {
+ uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
+ NumFormatHelper cellFormat( xRange );
+ // If we are setting a number and the cell types was logical
+ // then we need to reset the logical format. ( see case uno::TypeClass_BOOLEAN:
+ // handling above )
+ if ( cellFormat.isBooleanType() )
+ cellFormat.setNumberFormat("General");
+ xCell->setValue( nDouble );
+ }
+ else
+ isExtracted = false;
+ break;
+ }
+ }
+ return isExtracted;
+
+}
+
+namespace {
+
+class CellValueGetter : public ValueGetter
+{
+protected:
+ RangeValueType meValueType;
+ uno::Any maValue;
+public:
+ CellValueGetter(RangeValueType eValueType) { meValueType = eValueType; }
+ virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override;
+ virtual void processValue( const uno::Any& aValue ) override;
+ const uno::Any& getValue() const override { return maValue; }
+};
+
+}
+
+void
+CellValueGetter::processValue( const uno::Any& aValue )
+{
+ maValue = aValue;
+}
+void CellValueGetter::visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell )
+{
+ uno::Any aValue;
+ table::CellContentType eCellContentType = xCell->getType();
+ if( eCellContentType == table::CellContentType_VALUE || eCellContentType == table::CellContentType_FORMULA )
+ {
+ if ( eCellContentType == table::CellContentType_FORMULA )
+ {
+
+ OUString sFormula = xCell->getFormula();
+ if ( sFormula == "=TRUE()" )
+ aValue <<= true;
+ else if ( sFormula == "=FALSE()" )
+ aValue <<= false;
+ else
+ {
+ uno::Reference< beans::XPropertySet > xProp( xCell, uno::UNO_QUERY_THROW );
+
+ sal_Int32 nResultType = sheet::FormulaResult::VALUE;
+ // some formulas give textual results
+ xProp->getPropertyValue( "FormulaResultType2" ) >>= nResultType;
+
+ if ( nResultType == sheet::FormulaResult::STRING )
+ {
+ uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
+ aValue <<= xTextRange->getString();
+ }
+ else
+ aValue <<= xCell->getValue();
+ }
+ }
+ else
+ {
+ uno::Reference< table::XCellRange > xRange( xCell, uno::UNO_QUERY_THROW );
+ NumFormatHelper cellFormat( xRange );
+ if ( cellFormat.isBooleanType() )
+ aValue <<= ( xCell->getValue() != 0.0 );
+ else if ( cellFormat.isDateType() && meValueType == RangeValueType::value)
+ aValue <<= bridge::oleautomation::Date( xCell->getValue() );
+ else
+ aValue <<= xCell->getValue();
+ }
+ }
+ if( eCellContentType == table::CellContentType_TEXT )
+ {
+ uno::Reference< text::XTextRange > xTextRange(xCell, ::uno::UNO_QUERY_THROW);
+ aValue <<= xTextRange->getString();
+ }
+ processValue( aValue );
+}
+
+namespace {
+
+class CellFormulaValueSetter : public CellValueSetter
+{
+private:
+ ScDocument& m_rDoc;
+ formula::FormulaGrammar::Grammar m_eGrammar;
+public:
+ CellFormulaValueSetter( const uno::Any& aValue, ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ):CellValueSetter( aValue ), m_rDoc( rDoc ), m_eGrammar( eGram ){}
+protected:
+ bool processValue( const uno::Any& aValue, const uno::Reference< table::XCell >& xCell ) override
+ {
+ OUString sFormula;
+ double aDblValue = 0.0;
+ if ( aValue >>= sFormula )
+ {
+ // convert to GRAM_API style grammar because XCell::setFormula
+ // always compile it in that grammar. Perhaps
+ // css.sheet.FormulaParser should be used in future to directly
+ // pass formula tokens when that API stabilizes.
+ if ( m_eGrammar != formula::FormulaGrammar::GRAM_API && ( o3tl::starts_with(o3tl::trim(sFormula), u"=") ) )
+ {
+ uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
+ ScCellRangesBase* pUnoRangesBase
+ = dynamic_cast< ScCellRangesBase* >( xIf.get() );
+ if ( pUnoRangesBase )
+ {
+ const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
+ if (!rCellRanges.empty())
+ {
+ ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, m_eGrammar );
+ // compile the string in the format passed in
+ std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sFormula));
+ // convert to API grammar
+ aCompiler.SetGrammar( formula::FormulaGrammar::GRAM_API );
+ OUString sConverted;
+ aCompiler.CreateStringFromTokenArray(sConverted);
+ sFormula = EQUALS + sConverted;
+ }
+ }
+ }
+
+ xCell->setFormula( sFormula );
+ return true;
+ }
+ else if ( aValue >>= aDblValue )
+ {
+ xCell->setValue( aDblValue );
+ return true;
+ }
+ return false;
+ }
+
+};
+
+class CellFormulaValueGetter : public CellValueGetter
+{
+private:
+ ScDocument& m_rDoc;
+ formula::FormulaGrammar::Grammar m_eGrammar;
+public:
+ CellFormulaValueGetter(ScDocument& rDoc, formula::FormulaGrammar::Grammar eGram ) :
+ CellValueGetter( RangeValueType::value ), m_rDoc( rDoc ), m_eGrammar( eGram ) {}
+ virtual void visitNode( sal_Int32 /*x*/, sal_Int32 /*y*/, const uno::Reference< table::XCell >& xCell ) override
+ {
+ uno::Any aValue;
+ aValue <<= xCell->getFormula();
+ // XCell::getFormula() returns the formula in API grammar, convert.
+ if ((xCell->getType() == table::CellContentType_FORMULA)
+ && m_eGrammar != formula::FormulaGrammar::GRAM_API)
+ {
+ uno::Reference< uno::XInterface > xIf( xCell, uno::UNO_QUERY_THROW );
+ ScCellRangesBase* pUnoRangesBase
+ = dynamic_cast< ScCellRangesBase* >( xIf.get() );
+ if (pUnoRangesBase)
+ {
+ OUString sVal;
+ aValue >>= sVal;
+ const ScRangeList& rCellRanges = pUnoRangesBase->GetRangeList();
+ if (!rCellRanges.empty())
+ {
+ // Compile string from API grammar.
+ ScCompiler aCompiler( m_rDoc, rCellRanges.front().aStart, formula::FormulaGrammar::GRAM_API );
+ std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(sVal));
+ // Convert to desired grammar.
+ aCompiler.SetGrammar( m_eGrammar );
+ OUString sConverted;
+ aCompiler.CreateStringFromTokenArray(sConverted);
+ sVal = EQUALS + sConverted;
+ aValue <<= sVal;
+ }
+ }
+ }
+
+ processValue( aValue );
+ }
+
+};
+
+class Dim2ArrayValueGetter : public ArrayVisitor
+{
+protected:
+ uno::Any maValue;
+ ValueGetter& mValueGetter;
+ void processValue( sal_Int32 x, sal_Int32 y, const uno::Any& aValue )
+ {
+ uno::Sequence< uno::Sequence< uno::Any > >& aMatrix = const_cast<css::uno::Sequence<css::uno::Sequence<css::uno::Any>> &>(*o3tl::doAccess<uno::Sequence<uno::Sequence<uno::Any>>>(maValue));
+ aMatrix.getArray()[x].getArray()[y] = aValue;
+ }
+
+public:
+ Dim2ArrayValueGetter(sal_Int32 nRowCount, sal_Int32 nColCount, ValueGetter& rValueGetter ): mValueGetter(rValueGetter)
+ {
+ uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
+ aMatrix.realloc( nRowCount );
+ auto pMatrix = aMatrix.getArray();
+ for ( sal_Int32 index = 0; index < nRowCount; ++index )
+ pMatrix[index].realloc( nColCount );
+ maValue <<= aMatrix;
+ }
+ void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
+
+ {
+ mValueGetter.visitNode( x, y, xCell );
+ processValue( x, y, mValueGetter.getValue() );
+ }
+ const uno::Any& getValue() const { return maValue; }
+
+};
+
+}
+
+constexpr OUString sNA = u"#N/A"_ustr;
+
+namespace {
+
+class Dim1ArrayValueSetter : public ArrayVisitor
+{
+ uno::Sequence< uno::Any > aMatrix;
+ sal_Int32 nColCount;
+ ValueSetter& mCellValueSetter;
+public:
+ Dim1ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ):mCellValueSetter( rCellValueSetter )
+ {
+ aValue >>= aMatrix;
+ nColCount = aMatrix.getLength();
+ }
+ virtual void visitNode( sal_Int32 /*x*/, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
+ {
+ if ( y < nColCount )
+ mCellValueSetter.processValue( aMatrix[ y ], xCell );
+ else
+ mCellValueSetter.processValue( uno::Any( sNA ), xCell );
+ }
+};
+
+class Dim2ArrayValueSetter : public ArrayVisitor
+{
+ uno::Sequence< uno::Sequence< uno::Any > > aMatrix;
+ ValueSetter& mCellValueSetter;
+ sal_Int32 nRowCount;
+ sal_Int32 nColCount;
+public:
+ Dim2ArrayValueSetter( const uno::Any& aValue, ValueSetter& rCellValueSetter ) : mCellValueSetter( rCellValueSetter )
+ {
+ aValue >>= aMatrix;
+ nRowCount = aMatrix.getLength();
+ nColCount = aMatrix[0].getLength();
+ }
+
+ virtual void visitNode( sal_Int32 x, sal_Int32 y, const uno::Reference< table::XCell >& xCell ) override
+ {
+ if ( x < nRowCount && y < nColCount )
+ mCellValueSetter.processValue( aMatrix[ x ][ y ], xCell );
+ else
+ mCellValueSetter.processValue( uno::Any( sNA ), xCell );
+
+ }
+};
+
+class RangeProcessor
+{
+public:
+ virtual void process( const uno::Reference< excel::XRange >& xRange ) = 0;
+
+protected:
+ ~RangeProcessor() {}
+};
+
+class RangeValueProcessor : public RangeProcessor
+{
+ const uno::Any& m_aVal;
+public:
+ explicit RangeValueProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
+ virtual ~RangeValueProcessor() {}
+ virtual void process( const uno::Reference< excel::XRange >& xRange ) override
+ {
+ xRange->setValue( m_aVal );
+ }
+};
+
+class RangeFormulaProcessor : public RangeProcessor
+{
+ const uno::Any& m_aVal;
+public:
+ explicit RangeFormulaProcessor( const uno::Any& rVal ):m_aVal( rVal ) {}
+ virtual ~RangeFormulaProcessor() {}
+ virtual void process( const uno::Reference< excel::XRange >& xRange ) override
+ {
+ xRange->setFormula( m_aVal );
+ }
+};
+
+class RangeCountProcessor : public RangeProcessor
+{
+ sal_Int32 nCount;
+public:
+ RangeCountProcessor():nCount(0){}
+ virtual ~RangeCountProcessor() {}
+ virtual void process( const uno::Reference< excel::XRange >& xRange ) override
+ {
+ nCount = nCount + xRange->getCount();
+ }
+ sal_Int32 value() { return nCount; }
+};
+class AreasVisitor
+{
+private:
+ uno::Reference< XCollection > m_Areas;
+public:
+ explicit AreasVisitor( uno::Reference< XCollection > xAreas ):m_Areas(std::move( xAreas )){}
+
+ void visit( RangeProcessor& processor )
+ {
+ if ( m_Areas.is() )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ processor.process( xRange );
+ }
+ }
+ }
+};
+
+class RangeHelper
+{
+ uno::Reference< table::XCellRange > m_xCellRange;
+
+public:
+ /// @throws uno::RuntimeException
+ explicit RangeHelper( uno::Reference< table::XCellRange > xCellRange ) : m_xCellRange(std::move( xCellRange ))
+ {
+ if ( !m_xCellRange.is() )
+ throw uno::RuntimeException();
+ }
+ /// @throws uno::RuntimeException
+ explicit RangeHelper( const uno::Any& rCellRange )
+ {
+ m_xCellRange.set(rCellRange, uno::UNO_QUERY_THROW);
+ }
+ /// @throws uno::RuntimeException
+ uno::Reference< sheet::XSheetCellRange > getSheetCellRange() const
+ {
+ return uno::Reference< sheet::XSheetCellRange >(m_xCellRange, uno::UNO_QUERY_THROW);
+ }
+ /// @throws uno::RuntimeException
+ uno::Reference< sheet::XSpreadsheet > getSpreadSheet() const
+ {
+ return getSheetCellRange()->getSpreadsheet();
+ }
+
+ /// @throws uno::RuntimeException
+ uno::Reference< table::XCellRange > getCellRangeFromSheet() const
+ {
+ return uno::Reference< table::XCellRange >(getSpreadSheet(), uno::UNO_QUERY_THROW );
+ }
+
+ /// @throws uno::RuntimeException
+ uno::Reference< sheet::XCellRangeAddressable > getCellRangeAddressable() const
+ {
+ return uno::Reference< sheet::XCellRangeAddressable >(m_xCellRange, ::uno::UNO_QUERY_THROW);
+
+ }
+
+ /// @throws uno::RuntimeException
+ uno::Reference< sheet::XSheetCellCursor > getSheetCellCursor() const
+ {
+ return uno::Reference< sheet::XSheetCellCursor >( getSpreadSheet()->createCursorByRange( getSheetCellRange() ), uno::UNO_SET_THROW );
+ }
+
+ static uno::Reference< excel::XRange > createRangeFromRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference<uno::XComponentContext >& xContext,
+ const uno::Reference< table::XCellRange >& xRange, const uno::Reference< sheet::XCellRangeAddressable >& xCellRangeAddressable )
+ {
+ const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
+ return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext,
+ xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow)));
+ }
+
+};
+
+}
+
+bool
+ScVbaRange::getCellRangesForAddress( ScRefFlags& rResFlags, std::u16string_view sAddress, ScDocShell* pDocSh, ScRangeList& rCellRanges, formula::FormulaGrammar::AddressConvention eConv, char cDelimiter )
+{
+
+ if ( pDocSh )
+ {
+ ScDocument& rDoc = pDocSh->GetDocument();
+ rResFlags = rCellRanges.Parse( sAddress, rDoc, eConv, 0, cDelimiter );
+ if ( rResFlags & ScRefFlags::VALID )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool getScRangeListForAddress( const OUString& sName, ScDocShell* pDocSh, const ScRange& refRange, ScRangeList& aCellRanges, formula::FormulaGrammar::AddressConvention aConv )
+{
+ // see if there is a match with a named range
+ uno::Reference< container::XNameAccess > xNameAccess( pDocSh->GetModel()->getPropertyValue( "NamedRanges" ), uno::UNO_QUERY_THROW );
+ // Strange enough you can have Range( "namedRange1, namedRange2, etc," )
+ // loop around each ',' separated name
+ std::vector< OUString > vNames;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = sName.getToken( 0, ',', nIndex );
+ vNames.push_back( aToken );
+ } while ( nIndex >= 0 );
+
+ if ( vNames.empty() )
+ vNames.push_back( sName );
+
+ for ( const auto& rName : vNames )
+ {
+ formula::FormulaGrammar::AddressConvention eConv = aConv;
+ // spaces are illegal ( but the user of course can enter them )
+ OUString sAddress = rName.trim();
+ // if a local name ( on the active sheet ) exists this will
+ // take precedence over a global with the same name
+ if ( !xNameAccess->hasByName( sAddress ) )
+ {
+ // try a local name
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nCurTab = ScDocShell::GetCurTab();
+ ScRangeName* pRangeName = rDoc.GetRangeName(nCurTab);
+ if (pRangeName)
+ {
+ // TODO: Handle local names correctly:
+ // bool bLocalName = pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sAddress)) != nullptr;
+ }
+ }
+ char aChar = 0;
+ if ( xNameAccess->hasByName( sAddress ) )
+ {
+ uno::Reference< sheet::XNamedRange > xNamed( xNameAccess->getByName( sAddress ), uno::UNO_QUERY_THROW );
+ sAddress = xNamed->getContent();
+ // As the address comes from OOO, the addressing
+ // style is may not be XL_A1
+ eConv = pDocSh->GetDocument().GetAddressConvention();
+ aChar = ';';
+ }
+
+ ScRefFlags nFlags = ScRefFlags::ZERO;
+ if ( !ScVbaRange::getCellRangesForAddress( nFlags, sAddress, pDocSh, aCellRanges, eConv, aChar ) )
+ return false;
+
+ bool bTabFromReferrer = !( nFlags & ScRefFlags::TAB_3D );
+
+ for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rRange = aCellRanges[ i ];
+ rRange.aStart.SetCol( refRange.aStart.Col() + rRange.aStart.Col() );
+ rRange.aStart.SetRow( refRange.aStart.Row() + rRange.aStart.Row() );
+ rRange.aStart.SetTab( bTabFromReferrer ? refRange.aStart.Tab() : rRange.aStart.Tab() );
+ rRange.aEnd.SetCol( refRange.aStart.Col() + rRange.aEnd.Col() );
+ rRange.aEnd.SetRow( refRange.aStart.Row() + rRange.aEnd.Row() );
+ rRange.aEnd.SetTab( bTabFromReferrer ? refRange.aEnd.Tab() : rRange.aEnd.Tab() );
+ }
+ }
+ return true;
+}
+
+/// @throws uno::RuntimeException
+static rtl::Reference<ScVbaRange>
+getRangeForName( const uno::Reference< uno::XComponentContext >& xContext, const OUString& sName, ScDocShell* pDocSh, const table::CellRangeAddress& pAddr, formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_XL_A1 )
+{
+ ScRangeList aCellRanges;
+ ScRange refRange;
+ ScUnoConversion::FillScRange( refRange, pAddr );
+ if ( !getScRangeListForAddress ( sName, pDocSh, refRange, aCellRanges, eConv ) )
+ throw uno::RuntimeException();
+ // Single range
+ if ( aCellRanges.size() == 1 )
+ {
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pDocSh, aCellRanges.front() ) );
+ uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRange );
+ return new ScVbaRange( xFixThisParent, xContext, xRange );
+ }
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pDocSh, aCellRanges ) );
+
+ uno::Reference< XHelperInterface > xFixThisParent = excel::getUnoSheetModuleObj( xRanges );
+ return new ScVbaRange( xFixThisParent, xContext, xRanges );
+}
+
+namespace {
+
+/// @throws uno::RuntimeException
+template< typename RangeType >
+table::CellRangeAddress lclGetRangeAddress( const uno::Reference< RangeType >& rxCellRange )
+{
+ return uno::Reference< sheet::XCellRangeAddressable >( rxCellRange, uno::UNO_QUERY_THROW )->getRangeAddress();
+}
+
+/// @throws uno::RuntimeException
+void lclClearRange( const uno::Reference< table::XCellRange >& rxCellRange )
+{
+ using namespace ::com::sun::star::sheet::CellFlags;
+ sal_Int32 const nFlags = VALUE | DATETIME | STRING | ANNOTATION | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED;
+ uno::Reference< sheet::XSheetOperation > xSheetOperation( rxCellRange, uno::UNO_QUERY_THROW );
+ xSheetOperation->clearContents( nFlags );
+}
+
+/// @throws uno::RuntimeException
+uno::Reference< sheet::XSheetCellRange > lclExpandToMerged( const uno::Reference< table::XCellRange >& rxCellRange, bool bRecursive )
+{
+ uno::Reference< sheet::XSheetCellRange > xNewCellRange( rxCellRange, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xNewCellRange->getSpreadsheet(), uno::UNO_SET_THROW );
+ table::CellRangeAddress aNewAddress = lclGetRangeAddress( xNewCellRange );
+ table::CellRangeAddress aOldAddress;
+ // expand as long as there are new merged ranges included
+ do
+ {
+ aOldAddress = aNewAddress;
+ uno::Reference< sheet::XSheetCellCursor > xCursor( xSheet->createCursorByRange( xNewCellRange ), uno::UNO_SET_THROW );
+ if (xCursor.is())
+ {
+ xCursor->collapseToMergedArea();
+ xNewCellRange.set( xCursor, uno::UNO_QUERY_THROW );
+ aNewAddress = lclGetRangeAddress( xNewCellRange );
+ }
+ }
+ while( bRecursive && (aOldAddress != aNewAddress) );
+ return xNewCellRange;
+}
+
+/// @throws uno::RuntimeException
+uno::Reference< sheet::XSheetCellRangeContainer > lclExpandToMerged( const uno::Reference< sheet::XSheetCellRangeContainer >& rxCellRanges )
+{
+ if( !rxCellRanges.is() )
+ throw uno::RuntimeException("Missing cell ranges object" );
+ sal_Int32 nCount = rxCellRanges->getCount();
+ if( nCount < 1 )
+ throw uno::RuntimeException("Missing cell ranges object" );
+
+ ScRangeList aScRanges;
+ for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< table::XCellRange > xRange( rxCellRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ table::CellRangeAddress aRangeAddr = lclGetRangeAddress( lclExpandToMerged( xRange, /*bRecursive*/true ) );
+ ScRange aScRange;
+ ScUnoConversion::FillScRange( aScRange, aRangeAddr );
+ aScRanges.push_back( aScRange );
+ }
+ return new ScCellRangesObj( getDocShellFromRanges( rxCellRanges ), aScRanges );
+}
+
+/// @throws uno::RuntimeException
+void lclExpandAndMerge( const uno::Reference< table::XCellRange >& rxCellRange, bool bMerge )
+{
+ uno::Reference< util::XMergeable > xMerge( lclExpandToMerged( rxCellRange, true ), uno::UNO_QUERY_THROW );
+ // Calc cannot merge over merged ranges, always unmerge first
+ xMerge->merge( false );
+ if( !bMerge )
+ return;
+
+ // clear all contents of the covered cells (not the top-left cell)
+ table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
+ sal_Int32 nLastColIdx = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
+ sal_Int32 nLastRowIdx = aRangeAddr.EndRow - aRangeAddr.StartRow;
+ // clear cells of top row, right of top-left cell
+ if( nLastColIdx > 0 )
+ lclClearRange( rxCellRange->getCellRangeByPosition( 1, 0, nLastColIdx, 0 ) );
+ // clear all rows below top row
+ if( nLastRowIdx > 0 )
+ lclClearRange( rxCellRange->getCellRangeByPosition( 0, 1, nLastColIdx, nLastRowIdx ) );
+ // merge the range
+ xMerge->merge( true );
+}
+
+/// @throws uno::RuntimeException
+util::TriState lclGetMergedState( const uno::Reference< table::XCellRange >& rxCellRange )
+{
+ /* 1) Check if range is completely inside one single merged range. To do
+ this, try to extend from top-left cell only (not from entire range).
+ This will exclude cases where this range consists of several merged
+ ranges (or parts of them). */
+ table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxCellRange );
+ uno::Reference< table::XCellRange > xTopLeft( rxCellRange->getCellRangeByPosition( 0, 0, 0, 0 ), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XSheetCellRange > xExpanded( lclExpandToMerged( xTopLeft, false ), uno::UNO_SET_THROW );
+ table::CellRangeAddress aExpAddr = lclGetRangeAddress( xExpanded );
+ // check that expanded range has more than one cell (really merged)
+ if( ((aExpAddr.StartColumn < aExpAddr.EndColumn) || (aExpAddr.StartRow < aExpAddr.EndRow)) && ScUnoConversion::Contains( aExpAddr, aRangeAddr ) )
+ return util::TriState_YES;
+
+ /* 2) Check if this range contains any merged cells (completely or
+ partly). This seems to be hardly possible via API, as
+ XMergeable::getIsMerged() returns only true, if the top-left cell of a
+ merged range is part of this range, so cases where just the lower part
+ of a merged range is part of this range are not covered. */
+ ScRange aScRange;
+ ScUnoConversion::FillScRange( aScRange, aRangeAddr );
+ bool bHasMerged = getDocumentFromRange( rxCellRange ).HasAttrib( aScRange, HasAttrFlags::Merged | HasAttrFlags::Overlapped );
+ return bHasMerged ? util::TriState_INDETERMINATE : util::TriState_NO;
+}
+
+} // namespace
+
+css::uno::Reference< excel::XRange >
+ScVbaRange::getRangeObjectForName(
+ const uno::Reference< uno::XComponentContext >& xContext, const OUString& sRangeName,
+ ScDocShell* pDocSh, formula::FormulaGrammar::AddressConvention eConv )
+{
+ table::CellRangeAddress refAddr;
+ return getRangeForName( xContext, sRangeName, pDocSh, refAddr, eConv );
+}
+
+/// @throws uno::RuntimeException
+static table::CellRangeAddress getCellRangeAddressForVBARange( const uno::Any& aParam, ScDocShell* pDocSh )
+{
+ uno::Reference< table::XCellRange > xRangeParam;
+ switch ( aParam.getValueTypeClass() )
+ {
+ case uno::TypeClass_STRING:
+ {
+ OUString rString;
+ aParam >>= rString;
+ ScRangeList aCellRanges;
+ ScRange refRange;
+ if ( getScRangeListForAddress ( rString, pDocSh, refRange, aCellRanges ) )
+ {
+ if ( aCellRanges.size() == 1 )
+ {
+ table::CellRangeAddress aRangeAddress;
+ ScUnoConversion::FillApiRange( aRangeAddress, aCellRanges.front() );
+ return aRangeAddress;
+ }
+ }
+ }
+ break;
+
+ case uno::TypeClass_INTERFACE:
+ {
+ uno::Reference< excel::XRange > xRange;
+ aParam >>= xRange;
+ if ( xRange.is() )
+ xRange->getCellRange() >>= xRangeParam;
+ }
+ break;
+
+ default:
+ throw uno::RuntimeException("Can't extract CellRangeAddress from type" );
+ }
+ return lclGetRangeAddress( xRangeParam );
+}
+
+/// @throws uno::RuntimeException
+static uno::Reference< XCollection >
+lcl_setupBorders( const uno::Reference< excel::XRange >& xParentRange, const uno::Reference<uno::XComponentContext>& xContext, const uno::Reference< table::XCellRange >& xRange )
+{
+ uno::Reference< XHelperInterface > xParent( xParentRange, uno::UNO_QUERY_THROW );
+ ScDocument& rDoc = getDocumentFromRange(xRange);
+ ScVbaPalette aPalette( rDoc.GetDocumentShell() );
+ uno::Reference< XCollection > borders( new ScVbaBorders( xParent, xContext, xRange, aPalette ) );
+ return borders;
+}
+
+ScVbaRange::ScVbaRange( uno::Sequence< uno::Any> const & args,
+ uno::Reference< uno::XComponentContext> const & xContext ) : ScVbaRange_BASE( getXSomethingFromArgs< XHelperInterface >( args, 0 ), xContext, getXSomethingFromArgs< beans::XPropertySet >( args, 1, false ), getModelFromXIf( getXSomethingFromArgs< uno::XInterface >( args, 1 ) ), true ), mbIsRows( false ), mbIsColumns( false )
+{
+ mxRange.set( mxPropertySet, uno::UNO_QUERY );
+ mxRanges.set( mxPropertySet, uno::UNO_QUERY );
+ uno::Reference< container::XIndexAccess > xIndex;
+ if ( mxRange.is() )
+ {
+ xIndex = new SingleRangeIndexAccess( mxRange );
+ }
+ else if ( mxRanges.is() )
+ {
+ xIndex.set( mxRanges, uno::UNO_QUERY_THROW );
+ }
+ m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
+}
+
+ScVbaRange::ScVbaRange( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< table::XCellRange >& xRange, bool bIsRows, bool bIsColumns )
+: ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRange, uno::UNO_QUERY_THROW ), getModelFromRange( xRange), true ), mxRange( xRange ),
+ mbIsRows( bIsRows ),
+ mbIsColumns( bIsColumns )
+{
+ if ( !xContext.is() )
+ throw lang::IllegalArgumentException("context is not set ", uno::Reference< uno::XInterface >() , 1 );
+ if ( !xRange.is() )
+ throw lang::IllegalArgumentException("range is not set ", uno::Reference< uno::XInterface >() , 1 );
+
+ uno::Reference< container::XIndexAccess > xIndex( new SingleRangeIndexAccess( xRange ) );
+ m_Areas = new ScVbaRangeAreas( mxParent, mxContext, xIndex, mbIsRows, mbIsColumns );
+
+}
+
+ScVbaRange::ScVbaRange(const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges, bool bIsRows, bool bIsColumns)
+: ScVbaRange_BASE( xParent, xContext, uno::Reference< beans::XPropertySet >( xRanges, uno::UNO_QUERY_THROW ), getModelFromXIf( uno::Reference< uno::XInterface >( xRanges, uno::UNO_QUERY_THROW ) ), true ), mxRanges( xRanges ),mbIsRows( bIsRows ), mbIsColumns( bIsColumns )
+
+{
+ uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
+ m_Areas = new ScVbaRangeAreas( xParent, mxContext, xIndex, mbIsRows, mbIsColumns );
+
+}
+
+ScVbaRange::~ScVbaRange()
+{
+}
+
+uno::Reference< XCollection >& ScVbaRange::getBorders()
+{
+ if ( !m_Borders.is() )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ m_Borders = lcl_setupBorders( this, mxContext, uno::Reference< table::XCellRange >( xRange->getCellRange(), uno::UNO_QUERY_THROW ) );
+ }
+ return m_Borders;
+}
+
+void
+ScVbaRange::visitArray( ArrayVisitor& visitor )
+{
+ ScDocShell* pDocSh = nullptr;
+ if(ScCellRangeObj* range = dynamic_cast<ScCellRangeObj*>(mxRange.get()))
+ pDocSh = range->GetDocShell();
+ if ( pDocSh )
+ pDocSh->LockPaint();
+ table::CellRangeAddress aRangeAddr = lclGetRangeAddress( mxRange );
+ sal_Int32 nRowCount = aRangeAddr.EndRow - aRangeAddr.StartRow + 1;
+ sal_Int32 nColCount = aRangeAddr.EndColumn - aRangeAddr.StartColumn + 1;
+ for ( sal_Int32 i=0; i<nRowCount; ++i )
+ {
+ for ( sal_Int32 j=0; j<nColCount; ++j )
+ {
+ uno::Reference< table::XCell > xCell( mxRange->getCellByPosition( j, i ), uno::UNO_SET_THROW );
+
+ visitor.visitNode( i, j, xCell );
+ }
+ }
+ if ( pDocSh )
+ pDocSh->UnlockPaint();
+}
+
+uno::Any
+ScVbaRange::getValue( ValueGetter& valueGetter)
+{
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
+ // single cell range
+ if ( isSingleCellRange() )
+ {
+ visitArray( valueGetter );
+ return valueGetter.getValue();
+ }
+ sal_Int32 nRowCount = xColumnRowRange->getRows()->getCount();
+ sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
+ // multi cell range ( return array )
+ Dim2ArrayValueGetter arrayGetter( nRowCount, nColCount, valueGetter );
+ visitArray( arrayGetter );
+ return uno::Any( script::ArrayWrapper( false, arrayGetter.getValue() ) );
+}
+
+css::uno::Any ScVbaRange::DoGetValue( RangeValueType eValueType )
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->getValue();
+ }
+
+ CellValueGetter valueGetter( eValueType );
+ return getValue( valueGetter );
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getValue()
+{
+ return DoGetValue( RangeValueType::value );
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getValue2()
+{
+ return DoGetValue( RangeValueType::value2 );
+}
+
+
+void
+ScVbaRange::setValue( const uno::Any& aValue, ValueSetter& valueSetter )
+{
+ uno::TypeClass aClass = aValue.getValueTypeClass();
+ if ( aClass == uno::TypeClass_SEQUENCE )
+ {
+ const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
+ uno::Any aConverted;
+ try
+ {
+ // test for single dimension, could do
+ // with a better test than this
+ if ( aValue.getValueTypeName().indexOf('[') == aValue.getValueTypeName().lastIndexOf('[') )
+ {
+ aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Any >>::get() );
+ Dim1ArrayValueSetter setter( aConverted, valueSetter );
+ visitArray( setter );
+ }
+ else
+ {
+ aConverted = xConverter->convertTo( aValue, cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() );
+ Dim2ArrayValueSetter setter( aConverted, valueSetter );
+ visitArray( setter );
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "Bahhh, caught" );
+ }
+ }
+ else
+ {
+ visitArray( valueSetter );
+ }
+ fireChangeEvent();
+}
+
+void SAL_CALL
+ScVbaRange::setValue( const uno::Any &aValue )
+{
+ // If this is a multiple selection apply setValue over all areas
+ if ( m_Areas->getCount() > 1 )
+ {
+ AreasVisitor aVisitor( m_Areas );
+ RangeValueProcessor valueProcessor( aValue );
+ aVisitor.visit( valueProcessor );
+ return;
+ }
+ CellValueSetter valueSetter( aValue );
+ setValue( aValue, valueSetter );
+}
+
+void SAL_CALL
+ScVbaRange::setValue2( const uno::Any &aValue )
+{
+ return setValue( aValue );
+}
+
+
+void SAL_CALL
+ScVbaRange::Clear()
+{
+ using namespace ::com::sun::star::sheet::CellFlags;
+ sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA | HARDATTR | EDITATTR | FORMATTED;
+ ClearContents( nFlags, true );
+}
+
+//helper ClearContent
+void
+ScVbaRange::ClearContents( sal_Int32 nFlags, bool bFireEvent )
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ ScVbaRange* pRange = getImplementation( xRange );
+ if ( pRange )
+ pRange->ClearContents( nFlags, false ); // do not fire for single ranges
+ }
+ // fire change event for the entire range list
+ if( bFireEvent ) fireChangeEvent();
+ return;
+ }
+
+ uno::Reference< sheet::XSheetOperation > xSheetOperation(mxRange, uno::UNO_QUERY_THROW);
+ xSheetOperation->clearContents( nFlags );
+ if( bFireEvent ) fireChangeEvent();
+}
+
+void SAL_CALL
+ScVbaRange::ClearComments()
+{
+ ClearContents( sheet::CellFlags::ANNOTATION, false );
+}
+
+void SAL_CALL
+ScVbaRange::ClearContents()
+{
+ using namespace ::com::sun::star::sheet::CellFlags;
+ sal_Int32 const nFlags = VALUE | DATETIME | STRING | FORMULA;
+ ClearContents( nFlags, true );
+}
+
+void SAL_CALL
+ScVbaRange::ClearFormats()
+{
+ // FIXME: need to check if we need to combine FORMATTED
+ using namespace ::com::sun::star::sheet::CellFlags;
+ sal_Int32 const nFlags = HARDATTR | FORMATTED | EDITATTR;
+ ClearContents( nFlags, false );
+}
+
+void
+ScVbaRange::setFormulaValue( const uno::Any& rFormula, formula::FormulaGrammar::Grammar eGram )
+{
+ // If this is a multiple selection apply setFormula over all areas
+ if ( m_Areas->getCount() > 1 )
+ {
+ AreasVisitor aVisitor( m_Areas );
+ RangeFormulaProcessor valueProcessor( rFormula );
+ aVisitor.visit( valueProcessor );
+ return;
+ }
+ CellFormulaValueSetter formulaValueSetter( rFormula, getScDocument(), eGram );
+ setValue( rFormula, formulaValueSetter );
+}
+
+uno::Any
+ScVbaRange::getFormulaValue( formula::FormulaGrammar::Grammar eGram )
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->getFormula();
+ }
+ CellFormulaValueGetter valueGetter( getScDocument(), eGram );
+ return getValue( valueGetter );
+
+}
+
+uno::Any
+ScVbaRange::getFormula()
+{
+ return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
+}
+
+void
+ScVbaRange::setFormula(const uno::Any &rFormula )
+{
+ setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 );
+}
+
+uno::Any
+ScVbaRange::getFormulaR1C1()
+{
+ return getFormulaValue( formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
+}
+
+void
+ScVbaRange::setFormulaR1C1(const uno::Any& rFormula )
+{
+ setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1 );
+}
+
+uno::Any
+ScVbaRange::getFormulaLocal()
+{
+ return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
+}
+
+void
+ScVbaRange::setFormulaLocal(const uno::Any &rFormula )
+{
+ setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_A1 );
+}
+
+uno::Any
+ScVbaRange::getFormulaR1C1Local()
+{
+ return getFormulaValue( formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
+}
+
+void
+ScVbaRange::setFormulaR1C1Local(const uno::Any& rFormula )
+{
+ setFormulaValue( rFormula, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1 );
+}
+
+sal_Int32
+ScVbaRange::getCount()
+{
+ // If this is a multiple selection apply setValue over all areas
+ if ( m_Areas->getCount() > 1 )
+ {
+ AreasVisitor aVisitor( m_Areas );
+ RangeCountProcessor valueProcessor;
+ aVisitor.visit( valueProcessor );
+ return valueProcessor.value();
+ }
+ sal_Int32 rowCount = 0;
+ sal_Int32 colCount = 0;
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY_THROW );
+ rowCount = xColumnRowRange->getRows()->getCount();
+ colCount = xColumnRowRange->getColumns()->getCount();
+
+ if( mbIsRows )
+ return rowCount;
+ if( mbIsColumns )
+ return colCount;
+ return rowCount * colCount;
+}
+
+sal_Int32
+ScVbaRange::getRow()
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->getRow();
+ }
+ uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
+ return xCellAddressable->getCellAddress().Row + 1; // Zero value indexing
+}
+
+sal_Int32
+ScVbaRange::getColumn()
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->getColumn();
+ }
+ uno::Reference< sheet::XCellAddressable > xCellAddressable(mxRange->getCellByPosition(0, 0), uno::UNO_QUERY_THROW );
+ return xCellAddressable->getCellAddress().Column + 1; // Zero value indexing
+}
+
+uno::Any
+ScVbaRange::HasFormula()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ uno::Any aResult = aNULL();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ // if the HasFormula for any area is different to another
+ // return null
+ if ( index > 1 )
+ if ( aResult != xRange->HasFormula() )
+ return aNULL();
+ aResult = xRange->HasFormula();
+ if ( aNULL() == aResult )
+ return aNULL();
+ }
+ return aResult;
+ }
+ uno::Reference< uno::XInterface > xIf( mxRange, uno::UNO_QUERY_THROW );
+ ScCellRangesBase* pThisRanges = dynamic_cast< ScCellRangesBase* > ( xIf.get() );
+ if ( pThisRanges )
+ {
+ uno::Reference<uno::XInterface> xRanges( pThisRanges->queryFormulaCells( sheet::FormulaResult::ERROR | sheet::FormulaResult::VALUE | sheet::FormulaResult::STRING ), uno::UNO_QUERY_THROW );
+ ScCellRangesBase* pFormulaRanges
+ = dynamic_cast< ScCellRangesBase* > ( xRanges.get() );
+ assert(pFormulaRanges);
+ // check if there are no formula cell, return false
+ if ( pFormulaRanges->GetRangeList().empty() )
+ return uno::Any(false);
+
+ // check if there are holes (where some cells are not formulas)
+ // or returned range is not equal to this range
+ if ( ( pFormulaRanges->GetRangeList().size() > 1 )
+ || ( pFormulaRanges->GetRangeList().front().aStart != pThisRanges->GetRangeList().front().aStart )
+ || ( pFormulaRanges->GetRangeList().front().aEnd != pThisRanges->GetRangeList().front().aEnd )
+ )
+ return aNULL(); // should return aNULL;
+ }
+ return uno::Any( true );
+}
+void
+ScVbaRange::fillSeries( sheet::FillDirection nFillDirection, sheet::FillMode nFillMode, sheet::FillDateMode nFillDateMode, double fStep, double fEndValue )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ // Multi-Area Range
+ uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
+ for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
+ {
+ uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ ScVbaRange* pThisRange = getImplementation( xRange );
+ pThisRange->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
+
+ }
+ return;
+ }
+
+ uno::Reference< sheet::XCellSeries > xCellSeries(mxRange, uno::UNO_QUERY_THROW );
+ xCellSeries->fillSeries( nFillDirection, nFillMode, nFillDateMode, fStep, fEndValue );
+ fireChangeEvent();
+}
+
+void
+ScVbaRange::FillLeft()
+{
+ fillSeries(sheet::FillDirection_TO_LEFT,
+ sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
+}
+
+void
+ScVbaRange::FillRight()
+{
+ fillSeries(sheet::FillDirection_TO_RIGHT,
+ sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
+}
+
+void
+ScVbaRange::FillUp()
+{
+ fillSeries(sheet::FillDirection_TO_TOP,
+ sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
+}
+
+void
+ScVbaRange::FillDown()
+{
+ fillSeries(sheet::FillDirection_TO_BOTTOM,
+ sheet::FillMode_SIMPLE, sheet::FillDateMode_FILL_DATE_DAY, 0, 0x7FFFFFFF);
+}
+
+OUString
+ScVbaRange::getText()
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->getText();
+ }
+ uno::Reference< text::XTextRange > xTextRange(mxRange->getCellByPosition(0,0), uno::UNO_QUERY_THROW );
+ return xTextRange->getString();
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::Offset( const ::uno::Any &nRowOff, const uno::Any &nColOff )
+{
+ SCROW nRowOffset = 0;
+ SCCOL nColOffset = 0;
+ bool bIsRowOffset = ( nRowOff >>= nRowOffset );
+ bool bIsColumnOffset = ( nColOff >>= nColOffset );
+ ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
+
+ ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
+
+ for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rRange = aCellRanges[ i ];
+ if ( bIsColumnOffset )
+ {
+ rRange.aStart.SetCol( rRange.aStart.Col() + nColOffset );
+ rRange.aEnd.SetCol( rRange.aEnd.Col() + nColOffset );
+ }
+ if ( bIsRowOffset )
+ {
+ rRange.aStart.SetRow( rRange.aStart.Row() + nRowOffset );
+ rRange.aEnd.SetRow( rRange.aEnd.Row() + nRowOffset );
+ }
+ }
+
+ if ( aCellRanges.size() > 1 ) // Multi-Area
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
+ return new ScVbaRange( mxParent, mxContext, xRanges );
+ }
+ // normal range
+ const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
+ return new ScVbaRange( mxParent, mxContext, xRange );
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::CurrentRegion()
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->CurrentRegion();
+ }
+
+ RangeHelper helper( mxRange );
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
+ helper.getSheetCellCursor();
+ xSheetCellCursor->collapseToCurrentRegion();
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
+ return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::CurrentArray()
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->CurrentArray();
+ }
+ RangeHelper helper( mxRange );
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor =
+ helper.getSheetCellCursor();
+ xSheetCellCursor->collapseToCurrentArray();
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
+ return RangeHelper::createRangeFromRange( mxParent, mxContext, helper.getCellRangeFromSheet(), xCellRangeAddressable );
+}
+
+uno::Any
+ScVbaRange::getFormulaArray()
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->getFormulaArray();
+ }
+
+ // return a formula if there is one or else an array
+ // still not sure when the return as array code should run
+ // ( I think it is if there is more than one formula ) at least
+ // that is what the doc says ( but I am not even sure how to detect that )
+ // for the moment any tests we have pass
+ uno::Reference< sheet::XArrayFormulaRange> xFormulaArray( mxRange, uno::UNO_QUERY_THROW );
+ if ( !xFormulaArray->getArrayFormula().isEmpty() )
+ return uno::Any( xFormulaArray->getArrayFormula() );
+
+ uno::Reference< sheet::XCellRangeFormula> xCellRangeFormula( mxRange, uno::UNO_QUERY_THROW );
+ const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
+ uno::Any aSingleValueOrMatrix;
+ // When dealing with a single element ( embedded in the sequence of sequence ) unwrap and return
+ // that value
+ uno::Sequence< uno::Sequence<OUString> > aTmpSeq = xCellRangeFormula->getFormulaArray();
+ if ( aTmpSeq.getLength() == 1 )
+ {
+ if ( aTmpSeq[ 0 ].getLength() == 1 )
+ aSingleValueOrMatrix <<= aTmpSeq[ 0 ][ 0 ];
+ }
+ else
+ aSingleValueOrMatrix = xConverter->convertTo( uno::Any( aTmpSeq ) , cppu::UnoType<uno::Sequence< uno::Sequence< uno::Any > >>::get() ) ;
+ return aSingleValueOrMatrix;
+}
+
+void
+ScVbaRange::setFormulaArray(const uno::Any& rFormula)
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->setFormulaArray( rFormula );
+ }
+ // #TODO need to distinguish between getFormula and getFormulaArray e.g. (R1C1)
+ // but for the moment it's just easier to treat them the same for setting
+ // seems
+ uno::Reference< lang::XMultiServiceFactory > xModelFactory( getUnoModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XFormulaParser > xParser( xModelFactory->createInstance( "com.sun.star.sheet.FormulaParser" ), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY_THROW);
+
+ table::CellRangeAddress aRangeAddress = xSource->getRangeAddress();
+ // #TODO check if api orders the address
+ // e.g. do we need to order the RangeAddress to get the topleft ( or can we assume it
+ // is in the correct order )
+ table::CellAddress aAddress;
+ aAddress.Sheet = aRangeAddress.Sheet;
+ aAddress.Column = aRangeAddress.StartColumn;
+ aAddress.Row = aRangeAddress.StartRow;
+ OUString sFormula;
+ rFormula >>= sFormula;
+ uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( sFormula, aAddress );
+ ScTokenArray aTokenArray(getScDocument());
+ (void)ScTokenConversion::ConvertToTokenArray( getScDocument(), aTokenArray, aTokens );
+
+ getScDocShell()->GetDocFunc().EnterMatrix( getScRangeList()[0], nullptr, &aTokenArray, OUString(), true, true, OUString(), formula::FormulaGrammar::GRAM_API );
+}
+
+OUString
+ScVbaRange::Characters(const uno::Any& Start, const uno::Any& Length)
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->Characters( Start, Length );
+ }
+
+ tools::Long nIndex = 0, nCount = 0;
+ OUString rString;
+ uno::Reference< text::XTextRange > xTextRange(mxRange, ::uno::UNO_QUERY_THROW );
+ rString = xTextRange->getString();
+ if( !( Start >>= nIndex ) && !( Length >>= nCount ) )
+ return rString;
+ if(!( Start >>= nIndex ) )
+ nIndex = 1;
+ if(!( Length >>= nCount ) )
+ nIndex = rString.getLength();
+ return rString.copy( --nIndex, nCount ); // Zero value indexing
+}
+
+OUString
+ScVbaRange::Address( const uno::Any& RowAbsolute, const uno::Any& ColumnAbsolute, const uno::Any& ReferenceStyle, const uno::Any& External, const uno::Any& RelativeTo )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ // Multi-Area Range
+ OUStringBuffer sAddress;
+ uno::Reference< XCollection > xCollection( m_Areas, uno::UNO_SET_THROW );
+ uno::Any aExternalCopy = External;
+ for ( sal_Int32 index = 1; index <= xCollection->getCount(); ++index )
+ {
+ uno::Reference< excel::XRange > xRange( xCollection->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ if ( index > 1 )
+ {
+ sAddress.append(",");
+ // force external to be false
+ // only first address should have the
+ // document and sheet specifications
+ aExternalCopy <<= false;
+ }
+ sAddress.append(xRange->Address( RowAbsolute, ColumnAbsolute, ReferenceStyle, aExternalCopy, RelativeTo ));
+ }
+ return sAddress.makeStringAndClear();
+
+ }
+ ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
+ if ( ReferenceStyle.hasValue() )
+ {
+ sal_Int32 refStyle = excel::XlReferenceStyle::xlA1;
+ ReferenceStyle >>= refStyle;
+ if ( refStyle == excel::XlReferenceStyle::xlR1C1 )
+ dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, 0, 0 );
+ }
+ // default
+ ScRefFlags nFlags = ScRefFlags::RANGE_ABS;
+ ScDocShell* pDocShell = getScDocShell();
+ ScDocument& rDoc = pDocShell->GetDocument();
+
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
+ constexpr ScRefFlags ROW_ABS = ScRefFlags::ROW_ABS | ScRefFlags::ROW2_ABS;
+ constexpr ScRefFlags COL_ABS = ScRefFlags::COL_ABS | ScRefFlags::COL2_ABS;
+
+ if ( RowAbsolute.hasValue() )
+ {
+ bool bVal = true;
+ RowAbsolute >>= bVal;
+ if ( !bVal )
+ nFlags &= ~ROW_ABS;
+ }
+ if ( ColumnAbsolute.hasValue() )
+ {
+ bool bVal = true;
+ ColumnAbsolute >>= bVal;
+ if ( !bVal )
+ nFlags &= ~COL_ABS;
+ }
+ if ( External.hasValue() )
+ {
+ bool bLocal = false;
+ External >>= bLocal;
+ if ( bLocal )
+ nFlags |= ScRefFlags::TAB_3D | ScRefFlags::FORCE_DOC;
+ }
+ if ( RelativeTo.hasValue() )
+ {
+ // #TODO should I throw an error if R1C1 is not set?
+
+ table::CellRangeAddress refAddress = getCellRangeAddressForVBARange( RelativeTo, pDocShell );
+ dDetails = ScAddress::Details( formula::FormulaGrammar::CONV_XL_R1C1, static_cast< SCROW >( refAddress.StartRow ), static_cast< SCCOL >( refAddress.StartColumn ) );
+ }
+ return aRange.Format(rDoc, nFlags, dDetails);
+}
+
+uno::Reference < excel::XFont >
+ScVbaRange::Font()
+{
+ uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY );
+ ScDocument& rDoc = getScDocument();
+ if ( mxRange.is() )
+ xProps.set(mxRange, ::uno::UNO_QUERY );
+ else if ( mxRanges.is() )
+ xProps.set(mxRanges, ::uno::UNO_QUERY );
+
+ ScVbaPalette aPalette( rDoc.GetDocumentShell() );
+ ScCellRangeObj* pRangeObj = nullptr;
+ try
+ {
+ pRangeObj = getCellRangeObj();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return new ScVbaFont( this, mxContext, aPalette, xProps, pRangeObj );
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::Cells( const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
+{
+ // #TODO code within the test below "if ( m_Areas... " can be removed
+ // Test is performed only because m_xRange is NOT set to be
+ // the first range in m_Areas ( to force failure while
+ // the implementations for each method are being updated )
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->Cells( nRowIndex, nColumnIndex );
+ }
+
+ // Performance: Use a common helper method for ScVbaRange::Cells and ScVbaWorksheet::Cells,
+ // instead of creating a new ScVbaRange object in often-called ScVbaWorksheet::Cells
+ return CellsHelper( getScDocument(), mxParent, mxContext, mxRange, nRowIndex, nColumnIndex );
+}
+
+// static
+uno::Reference< excel::XRange >
+ScVbaRange::CellsHelper( const ScDocument& rDoc,
+ const uno::Reference< ov::XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< css::table::XCellRange >& xRange,
+ const uno::Any &nRowIndex, const uno::Any &nColumnIndex )
+{
+ sal_Int32 nRow = 0, nColumn = 0;
+
+ bool bIsIndex = nRowIndex.hasValue();
+ bool bIsColumnIndex = nColumnIndex.hasValue();
+
+ // Sometimes we might get a float or a double or whatever
+ // set in the Any, we should convert as appropriate
+ // #FIXME - perhaps worth turning this into some sort of
+ // conversion routine e.g. bSuccess = getValueFromAny( nRow, nRowIndex, cppu::UnoType<sal_Int32>::get() )
+ if ( nRowIndex.hasValue() && !( nRowIndex >>= nRow ) )
+ {
+ const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
+ uno::Any aConverted;
+ try
+ {
+ aConverted = xConverter->convertTo( nRowIndex, cppu::UnoType<sal_Int32>::get() );
+ bIsIndex = ( aConverted >>= nRow );
+ }
+ catch( uno::Exception& ) {} // silence any errors
+ }
+
+ if ( bIsColumnIndex )
+ {
+ // Column index can be a col address e.g Cells( 1, "B" ) etc.
+ OUString sCol;
+ if ( nColumnIndex >>= sCol )
+ {
+ ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
+ ScRange tmpRange;
+ ScRefFlags flags = tmpRange.ParseCols( rDoc, sCol, dDetails );
+ if ( (flags & ScRefFlags::COL_VALID) == ScRefFlags::ZERO )
+ throw uno::RuntimeException();
+ nColumn = tmpRange.aStart.Col() + 1;
+ }
+ else
+ {
+ if ( !( nColumnIndex >>= nColumn ) )
+ {
+ const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( xContext );
+ uno::Any aConverted;
+ try
+ {
+ aConverted = xConverter->convertTo( nColumnIndex, cppu::UnoType<sal_Int32>::get() );
+ bIsColumnIndex = ( aConverted >>= nColumn );
+ }
+ catch( uno::Exception& ) {} // silence any errors
+ }
+ }
+ }
+ RangeHelper thisRange( xRange );
+ table::CellRangeAddress thisRangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ uno::Reference< table::XCellRange > xSheetRange = thisRange.getCellRangeFromSheet();
+ if( !bIsIndex && !bIsColumnIndex ) // .Cells
+ // #FIXME needs proper parent ( Worksheet )
+ return uno::Reference< excel::XRange >( new ScVbaRange( xParent, xContext, xRange ) );
+
+ sal_Int32 nIndex = --nRow;
+ if( bIsIndex && !bIsColumnIndex ) // .Cells(n)
+ {
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(xRange, ::uno::UNO_QUERY_THROW);
+ sal_Int32 nColCount = xColumnRowRange->getColumns()->getCount();
+
+ if ( !nIndex || nIndex < 0 )
+ nRow = 0;
+ else
+ nRow = nIndex / nColCount;
+ nColumn = nIndex % nColCount;
+ }
+ else
+ --nColumn;
+ nRow = nRow + thisRangeAddress.StartRow;
+ nColumn = nColumn + thisRangeAddress.StartColumn;
+ return new ScVbaRange( xParent, xContext, xSheetRange->getCellRangeByPosition( nColumn, nRow, nColumn, nRow ) );
+}
+
+void
+ScVbaRange::Select()
+{
+ ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
+ if ( !pUnoRangesBase )
+ throw uno::RuntimeException("Failed to access underlying uno range object" );
+ ScDocShell* pShell = pUnoRangesBase->GetDocShell();
+ if ( !pShell )
+ return;
+
+ uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_SET_THROW );
+ uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
+ if ( mxRanges.is() )
+ xSelection->select( uno::Any( lclExpandToMerged( mxRanges ) ) );
+ else
+ xSelection->select( uno::Any( lclExpandToMerged( mxRange, true ) ) );
+ // set focus on document e.g.
+ // ThisComponent.CurrentController.Frame.getContainerWindow.SetFocus
+ try
+ {
+ uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
+ uno::Reference< frame::XFrame > xFrame( xController->getFrame(), uno::UNO_SET_THROW );
+ uno::Reference< awt::XWindow > xWin( xFrame->getContainerWindow(), uno::UNO_SET_THROW );
+ xWin->setFocus();
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+static bool cellInRange( const table::CellRangeAddress& rAddr, sal_Int32 nCol, sal_Int32 nRow )
+{
+ return nCol >= rAddr.StartColumn && nCol <= rAddr.EndColumn &&
+ nRow >= rAddr.StartRow && nRow <= rAddr.EndRow;
+}
+
+static void setCursor( SCCOL nCol, SCROW nRow, const uno::Reference< frame::XModel >& xModel, bool bInSel = true )
+{
+ ScTabViewShell* pShell = excel::getBestViewShell( xModel );
+ if ( pShell )
+ {
+ if ( bInSel )
+ pShell->SetCursor( nCol, nRow );
+ else
+ pShell->MoveCursorAbs( nCol, nRow, SC_FOLLOW_NONE, false, false, true );
+ }
+}
+
+void
+ScVbaRange::Activate()
+{
+ // get first cell of current range
+ uno::Reference< table::XCellRange > xCellRange;
+ if ( mxRanges.is() )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
+ xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ }
+ else
+ xCellRange.set( mxRange, uno::UNO_SET_THROW );
+
+ RangeHelper thisRange( xCellRange );
+ uno::Reference< sheet::XCellRangeAddressable > xThisRangeAddress = thisRange.getCellRangeAddressable();
+ table::CellRangeAddress thisRangeAddress = xThisRangeAddress->getRangeAddress();
+ uno::Reference< frame::XModel > xModel;
+ ScDocShell* pShell = getScDocShell();
+
+ if ( pShell )
+ xModel = pShell->GetModel();
+
+ if ( !xModel.is() )
+ throw uno::RuntimeException();
+
+ // get current selection
+ uno::Reference< sheet::XCellRangeAddressable > xRange( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
+
+ uno::Reference< sheet::XSheetCellRanges > xRanges( xModel->getCurrentSelection(), ::uno::UNO_QUERY);
+
+ if ( xRanges.is() )
+ {
+ const uno::Sequence< table::CellRangeAddress > nAddrs = xRanges->getRangeAddresses();
+ for ( const auto& rAddr : nAddrs )
+ {
+ if ( cellInRange( rAddr, thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
+ {
+ setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
+ return;
+ }
+
+ }
+ }
+
+ if ( xRange.is() && cellInRange( xRange->getRangeAddress(), thisRangeAddress.StartColumn, thisRangeAddress.StartRow ) )
+ setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel );
+ else
+ {
+ // if this range is multi cell select the range other
+ // wise just position the cell at this single range position
+ if ( isSingleCellRange() )
+ // This top-leftmost cell of this Range is not in the current
+ // selection so just select this range
+ setCursor( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), xModel, false );
+ else
+ Select();
+ }
+
+}
+
+ScRange ScVbaRange::obtainRangeEvenIfRangeListIsEmpty( const ScRangeList& rCellRanges ) const
+{
+ // XXX It may be that using the current range list was never correct, but
+ // always the initial sheet range would be instead, history is unclear.
+
+ if (!rCellRanges.empty())
+ return rCellRanges.front();
+
+ table::CellRangeAddress aRA( lclGetRangeAddress( mxRange ));
+ return ScRange( aRA.StartColumn, aRA.StartRow, aRA.Sheet, aRA.EndColumn, aRA.EndRow, aRA.Sheet);
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::Rows(const uno::Any& aIndex )
+{
+ if ( aIndex.hasValue() )
+ {
+ ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
+ ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
+
+ sal_Int32 nValue = 0;
+ OUString sAddress;
+ if( aIndex >>= nValue )
+ {
+ aRange.aStart.SetRow( aRange.aStart.Row() + --nValue );
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ }
+ else if ( aIndex >>= sAddress )
+ {
+ ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
+ ScRange tmpRange;
+ tmpRange.ParseRows( getScDocument(), sAddress, dDetails );
+ SCROW nStartRow = tmpRange.aStart.Row();
+ SCROW nEndRow = tmpRange.aEnd.Row();
+
+ aRange.aStart.SetRow( aRange.aStart.Row() + nStartRow );
+ aRange.aEnd.SetRow( aRange.aStart.Row() + ( nEndRow - nStartRow ));
+ }
+ else
+ throw uno::RuntimeException("Illegal param" );
+
+ if ( aRange.aStart.Row() < 0 || aRange.aEnd.Row() < 0 )
+ throw uno::RuntimeException("Internal failure, illegal param" );
+ // return a normal range ( even for multi-selection
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
+ return new ScVbaRange( mxParent, mxContext, xRange, true );
+ }
+ // Rows() - no params
+ if ( m_Areas->getCount() > 1 )
+ return new ScVbaRange( mxParent, mxContext, mxRanges, true );
+ return new ScVbaRange( mxParent, mxContext, mxRange, true );
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::Columns(const uno::Any& aIndex )
+{
+ ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
+ ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( pUnoRangesBase->GetRangeList()));
+
+ if ( aIndex.hasValue() )
+ {
+ OUString sAddress;
+ sal_Int32 nValue = 0;
+ if ( aIndex >>= nValue )
+ {
+ aRange.aStart.SetCol( aRange.aStart.Col() + static_cast< SCCOL > ( --nValue ) );
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+ }
+
+ else if ( aIndex >>= sAddress )
+ {
+ ScAddress::Details dDetails( formula::FormulaGrammar::CONV_XL_A1, 0, 0 );
+ ScRange tmpRange;
+ tmpRange.ParseCols( getScDocument(), sAddress, dDetails );
+ SCCOL nStartCol = tmpRange.aStart.Col();
+ SCCOL nEndCol = tmpRange.aEnd.Col();
+
+ aRange.aStart.SetCol( aRange.aStart.Col() + nStartCol );
+ aRange.aEnd.SetCol( aRange.aStart.Col() + ( nEndCol - nStartCol ));
+ }
+ else
+ throw uno::RuntimeException("Illegal param" );
+
+ if ( aRange.aStart.Col() < 0 || aRange.aEnd.Col() < 0 )
+ throw uno::RuntimeException("Internal failure, illegal param" );
+ }
+ // Columns() - no params
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange ) );
+ return new ScVbaRange( mxParent, mxContext, xRange, false, true );
+}
+
+void
+ScVbaRange::setMergeCells( const uno::Any& aIsMerged )
+{
+ bool bMerge = extractBoolFromAny( aIsMerged );
+
+ if( mxRanges.is() )
+ {
+ sal_Int32 nCount = mxRanges->getCount();
+
+ // VBA does nothing (no error) if the own ranges overlap somehow
+ ::std::vector< table::CellRangeAddress > aList;
+ for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< sheet::XCellRangeAddressable > xRangeAddr( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ table::CellRangeAddress aAddress = xRangeAddr->getRangeAddress();
+ if (std::any_of(aList.begin(), aList.end(),
+ [&aAddress](const table::CellRangeAddress& rAddress)
+ { return ScUnoConversion::Intersects( rAddress, aAddress ); }))
+ return;
+ aList.push_back( aAddress );
+ }
+
+ // (un)merge every range after it has been extended to intersecting merged ranges from sheet
+ for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ lclExpandAndMerge( xRange, bMerge );
+ }
+ return;
+ }
+
+ // otherwise, merge single range
+ lclExpandAndMerge( mxRange, bMerge );
+}
+
+uno::Any
+ScVbaRange::getMergeCells()
+{
+ if( mxRanges.is() )
+ {
+ sal_Int32 nCount = mxRanges->getCount();
+ for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< table::XCellRange > xRange( mxRanges->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ util::TriState eMerged = lclGetMergedState( xRange );
+ /* Excel always returns NULL, if one range of the range list is
+ partly or completely merged. Even if all ranges are completely
+ merged, the return value is still NULL. */
+ if( eMerged != util::TriState_NO )
+ return aNULL();
+ }
+ // no range is merged anyhow, return false
+ return uno::Any( false );
+ }
+
+ // otherwise, check single range
+ switch( lclGetMergedState( mxRange ) )
+ {
+ case util::TriState_YES: return uno::Any( true );
+ case util::TriState_NO: return uno::Any( false );
+ default: return aNULL();
+ }
+}
+
+void
+ScVbaRange::Copy(const ::uno::Any& Destination)
+{
+ if ( Destination.hasValue() )
+ {
+ // TODO copy with multiple selections should work here too
+ if ( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException("That command cannot be used on multiple selections" );
+ uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
+ uno::Any aRange = xRange->getCellRange();
+ uno::Reference< table::XCellRange > xCellRange;
+ aRange >>= xCellRange;
+ uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
+ uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
+ xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
+ xMover->copyRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
+ if ( ScVbaRange* pRange = getImplementation( xRange ) )
+ pRange->fireChangeEvent();
+ }
+ else
+ {
+ Select();
+ excel::implnCopy(getUnoModel());
+ }
+}
+
+void
+ScVbaRange::Cut(const ::uno::Any& Destination)
+{
+ if ( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException("That command cannot be used on multiple selections" );
+ if (Destination.hasValue())
+ {
+ uno::Reference< excel::XRange > xRange( Destination, uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xCellRange( xRange->getCellRange(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSheetCellRange > xSheetCellRange(xCellRange, ::uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet = xSheetCellRange->getSpreadsheet();
+ uno::Reference< table::XCellRange > xDest( xSheet, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeMovement > xMover( xSheet, uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XCellAddressable > xDestination( xDest->getCellByPosition(
+ xRange->getColumn()-1,xRange->getRow()-1), uno::UNO_QUERY);
+ uno::Reference< sheet::XCellRangeAddressable > xSource( mxRange, uno::UNO_QUERY);
+ xMover->moveRange( xDestination->getCellAddress(), xSource->getRangeAddress() );
+ }
+ else
+ {
+ uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
+ Select();
+ excel::implnCut( xModel );
+ }
+}
+
+void
+ScVbaRange::setNumberFormat( const uno::Any& aFormat )
+{
+ OUString sFormat;
+ aFormat >>= sFormat;
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->setNumberFormat( aFormat );
+ }
+ return;
+ }
+ NumFormatHelper numFormat( mxRange );
+ numFormat.setNumberFormat( sFormat );
+}
+
+uno::Any
+ScVbaRange::getNumberFormat()
+{
+
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ uno::Any aResult = aNULL();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ // if the numberformat of one area is different to another
+ // return null
+ if ( index > 1 )
+ if ( aResult != xRange->getNumberFormat() )
+ return aNULL();
+ aResult = xRange->getNumberFormat();
+ if ( aNULL() == aResult )
+ return aNULL();
+ }
+ return aResult;
+ }
+ NumFormatHelper numFormat( mxRange );
+ OUString sFormat = numFormat.getNumberFormatString();
+ if ( !sFormat.isEmpty() )
+ return uno::Any( sFormat );
+ return aNULL();
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::Resize( const uno::Any &RowSize, const uno::Any &ColumnSize )
+{
+ tools::Long nRowSize = 0, nColumnSize = 0;
+ bool bIsRowChanged = ( RowSize >>= nRowSize ), bIsColumnChanged = ( ColumnSize >>= nColumnSize );
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, ::uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XSheetCellRange > xSheetRange(mxRange, ::uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XSheetCellCursor > xCursor( xSheetRange->getSpreadsheet()->createCursorByRange(xSheetRange), ::uno::UNO_SET_THROW );
+
+ if( !bIsRowChanged )
+ nRowSize = xColumnRowRange->getRows()->getCount();
+ if( !bIsColumnChanged )
+ nColumnSize = xColumnRowRange->getColumns()->getCount();
+
+ xCursor->collapseToSize( nColumnSize, nRowSize );
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xCursor, ::uno::UNO_QUERY_THROW );
+ uno::Reference< table::XCellRange > xRange( xSheetRange->getSpreadsheet(), ::uno::UNO_QUERY_THROW );
+ const table::CellRangeAddress aRA( xCellRangeAddressable->getRangeAddress());
+ return new ScVbaRange( mxParent, mxContext, xRange->getCellRangeByPosition( aRA.StartColumn, aRA.StartRow, aRA.EndColumn, aRA.EndRow));
+}
+
+void
+ScVbaRange::setWrapText( const uno::Any& aIsWrapped )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->setWrapText( aIsWrapped );
+ }
+ return;
+ }
+
+ uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
+ bool bIsWrapped = extractBoolFromAny( aIsWrapped );
+ xProps->setPropertyValue( "IsTextWrapped", uno::Any( bIsWrapped ) );
+}
+
+uno::Any
+ScVbaRange::getWrapText()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ uno::Any aResult;
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ if ( index > 1 )
+ if ( aResult != xRange->getWrapText() )
+ return aNULL();
+ aResult = xRange->getWrapText();
+ }
+ return aResult;
+ }
+
+ SfxItemSet* pDataSet = getCurrentDataSet();
+
+ SfxItemState eState = pDataSet->GetItemState( ATTR_LINEBREAK);
+ if ( eState == SfxItemState::DONTCARE )
+ return aNULL();
+
+ uno::Reference< beans::XPropertySet > xProps(mxRange, ::uno::UNO_QUERY_THROW );
+ uno::Any aValue = xProps->getPropertyValue( "IsTextWrapped" );
+ return aValue;
+}
+
+uno::Reference< excel::XInterior > ScVbaRange::Interior( )
+{
+ uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
+ return new ScVbaInterior ( this, mxContext, xProps, &getScDocument() );
+}
+uno::Reference< excel::XRange >
+ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2 )
+{
+ return Range( Cell1, Cell2, false );
+}
+uno::Reference< excel::XRange >
+ScVbaRange::Range( const uno::Any &Cell1, const uno::Any &Cell2, bool bForceUseInpuRangeTab )
+
+{
+ uno::Reference< table::XCellRange > xCellRange = mxRange;
+
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
+ xCellRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ }
+ else
+ xCellRange.set( mxRange );
+
+ RangeHelper thisRange( xCellRange );
+ uno::Reference< table::XCellRange > xRanges = thisRange.getCellRangeFromSheet();
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( xRanges, uno::UNO_QUERY_THROW );
+
+ uno::Reference< table::XCellRange > xReferrer =
+ xRanges->getCellRangeByPosition( getColumn()-1, getRow()-1,
+ xAddressable->getRangeAddress().EndColumn,
+ xAddressable->getRangeAddress().EndRow );
+ // xAddressable now for this range
+ xAddressable.set( xReferrer, uno::UNO_QUERY_THROW );
+
+ if( !Cell1.hasValue() )
+ throw uno::RuntimeException( "Invalid Argument" );
+
+ table::CellRangeAddress parentRangeAddress = xAddressable->getRangeAddress();
+
+ ScRange aRange;
+ // Cell1 defined only
+ if ( !Cell2.hasValue() )
+ {
+ OUString sName;
+ Cell1 >>= sName;
+ RangeHelper referRange( xReferrer );
+ table::CellRangeAddress referAddress = referRange.getCellRangeAddressable()->getRangeAddress();
+ return getRangeForName( mxContext, sName, getScDocShell(), referAddress );
+
+ }
+ else
+ {
+ table::CellRangeAddress cell1, cell2;
+ cell1 = getCellRangeAddressForVBARange( Cell1, getScDocShell() );
+ // Cell1 & Cell2 defined
+ // Excel seems to combine the range as the range defined by
+ // the combination of Cell1 & Cell2
+
+ cell2 = getCellRangeAddressForVBARange( Cell2, getScDocShell() );
+
+ table::CellRangeAddress resultAddress;
+ resultAddress.StartColumn = ( cell1.StartColumn < cell2.StartColumn ) ? cell1.StartColumn : cell2.StartColumn;
+ resultAddress.StartRow = ( cell1.StartRow < cell2.StartRow ) ? cell1.StartRow : cell2.StartRow;
+ resultAddress.EndColumn = std::max( cell1.EndColumn, cell2.EndColumn );
+ resultAddress.EndRow = std::max( cell1.EndRow, cell2.EndRow );
+ if ( bForceUseInpuRangeTab )
+ {
+ // this is a call from Application.Range( x,y )
+ // it's possible for x or y to specify a different sheet from
+ // the current or active on ( but they must be the same )
+ if ( cell1.Sheet != cell2.Sheet )
+ throw uno::RuntimeException();
+ parentRangeAddress.Sheet = cell1.Sheet;
+ }
+ else
+ {
+ // this is not a call from Application.Range( x,y )
+ // if a different sheet from this range is specified it's
+ // an error
+ if ( parentRangeAddress.Sheet != cell1.Sheet
+ || parentRangeAddress.Sheet != cell2.Sheet
+ )
+ throw uno::RuntimeException();
+
+ }
+ ScUnoConversion::FillScRange( aRange, resultAddress );
+ }
+ ScRange parentAddress;
+ ScUnoConversion::FillScRange( parentAddress, parentRangeAddress);
+ if ( aRange.aStart.Col() >= 0 && aRange.aStart.Row() >= 0 && aRange.aEnd.Col() >= 0 && aRange.aEnd.Row() >= 0 )
+ {
+ sal_Int32 nStartX = parentAddress.aStart.Col() + aRange.aStart.Col();
+ sal_Int32 nStartY = parentAddress.aStart.Row() + aRange.aStart.Row();
+ sal_Int32 nEndX = parentAddress.aStart.Col() + aRange.aEnd.Col();
+ sal_Int32 nEndY = parentAddress.aStart.Row() + aRange.aEnd.Row();
+
+ if ( nStartX <= nEndX && nEndX <= parentAddress.aEnd.Col() &&
+ nStartY <= nEndY && nEndY <= parentAddress.aEnd.Row() )
+ {
+ ScRange aNew( static_cast<SCCOL>(nStartX), static_cast<SCROW>(nStartY), parentAddress.aStart.Tab(),
+ static_cast<SCCOL>(nEndX), static_cast<SCROW>(nEndY), parentAddress.aEnd.Tab() );
+ xCellRange = new ScCellRangeObj( getScDocShell(), aNew );
+ }
+ }
+
+ return new ScVbaRange( mxParent, mxContext, xCellRange );
+
+}
+
+// Allow access to underlying openoffice uno api ( useful for debugging
+// with openoffice basic )
+uno::Any SAL_CALL ScVbaRange::getCellRange( )
+{
+ uno::Any aAny;
+ if ( mxRanges.is() )
+ aAny <<= mxRanges;
+ else if ( mxRange.is() )
+ aAny <<= mxRange;
+ return aAny;
+}
+
+uno::Any ScVbaRange::getCellRange( const uno::Reference< excel::XRange >& rxRange )
+{
+ if( ScVbaRange* pVbaRange = getImplementation( rxRange ) )
+ return pVbaRange->getCellRange();
+ throw uno::RuntimeException();
+}
+
+static InsertDeleteFlags getPasteFlags (sal_Int32 Paste)
+{
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+ switch (Paste) {
+ case excel::XlPasteType::xlPasteComments:
+ nFlags = InsertDeleteFlags::NOTE;break;
+ case excel::XlPasteType::xlPasteFormats:
+ nFlags = InsertDeleteFlags::ATTRIB;break;
+ case excel::XlPasteType::xlPasteFormulas:
+ nFlags = InsertDeleteFlags::FORMULA;break;
+ case excel::XlPasteType::xlPasteFormulasAndNumberFormats :
+ case excel::XlPasteType::xlPasteValues:
+ nFlags = ( InsertDeleteFlags::VALUE | InsertDeleteFlags::DATETIME | InsertDeleteFlags::STRING | InsertDeleteFlags::SPECIAL_BOOLEAN ); break;
+ case excel::XlPasteType::xlPasteValuesAndNumberFormats:
+ nFlags = InsertDeleteFlags::VALUE | InsertDeleteFlags::ATTRIB; break;
+ case excel::XlPasteType::xlPasteColumnWidths:
+ case excel::XlPasteType::xlPasteValidation:
+ nFlags = InsertDeleteFlags::NONE;break;
+ case excel::XlPasteType::xlPasteAll:
+ case excel::XlPasteType::xlPasteAllExceptBorders:
+ default:
+ nFlags = InsertDeleteFlags::ALL;break;
+ }
+ return nFlags;
+}
+
+static ScPasteFunc
+getPasteFormulaBits( sal_Int32 Operation)
+{
+ ScPasteFunc nFormulaBits = ScPasteFunc::NONE;
+ switch (Operation)
+ {
+ case excel::XlPasteSpecialOperation::xlPasteSpecialOperationAdd:
+ nFormulaBits = ScPasteFunc::ADD; break;
+ case excel::XlPasteSpecialOperation::xlPasteSpecialOperationSubtract:
+ nFormulaBits = ScPasteFunc::SUB;break;
+ case excel::XlPasteSpecialOperation::xlPasteSpecialOperationMultiply:
+ nFormulaBits = ScPasteFunc::MUL;break;
+ case excel::XlPasteSpecialOperation::xlPasteSpecialOperationDivide:
+ nFormulaBits = ScPasteFunc::DIV;break;
+
+ case excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone:
+ default:
+ nFormulaBits = ScPasteFunc::NONE; break;
+ }
+
+ return nFormulaBits;
+}
+void SAL_CALL
+ScVbaRange::PasteSpecial( const uno::Any& Paste, const uno::Any& Operation, const uno::Any& SkipBlanks, const uno::Any& Transpose )
+{
+ if ( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException("That command cannot be used on multiple selections" );
+ ScDocShell* pShell = getScDocShell();
+
+ if (!pShell)
+ throw uno::RuntimeException("That command cannot be used with no ScDocShell" );
+
+ uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
+ uno::Reference< view::XSelectionSupplier > xSelection( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
+ // select this range
+ xSelection->select( uno::Any( mxRange ) );
+ // set up defaults
+ sal_Int32 nPaste = excel::XlPasteType::xlPasteAll;
+ sal_Int32 nOperation = excel::XlPasteSpecialOperation::xlPasteSpecialOperationNone;
+ bool bTranspose = false;
+ bool bSkipBlanks = false;
+
+ if ( Paste.hasValue() )
+ Paste >>= nPaste;
+ if ( Operation.hasValue() )
+ Operation >>= nOperation;
+ if ( SkipBlanks.hasValue() )
+ SkipBlanks >>= bSkipBlanks;
+ if ( Transpose.hasValue() )
+ Transpose >>= bTranspose;
+
+ InsertDeleteFlags nFlags = getPasteFlags(nPaste);
+ ScPasteFunc nFormulaBits = getPasteFormulaBits(nOperation);
+
+ excel::implnPasteSpecial(xModel, nFlags, nFormulaBits, bSkipBlanks, bTranspose);
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::getEntireColumnOrRow( bool bColumn )
+{
+ ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
+ // copy the range list
+ ScRangeList aCellRanges = pUnoRangesBase->GetRangeList();
+ ScDocument& rDoc = getScDocument();
+
+ for ( size_t i = 0, nRanges = aCellRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rRange = aCellRanges[ i ];
+ if ( bColumn )
+ {
+ rRange.aStart.SetRow( 0 );
+ rRange.aEnd.SetRow( rDoc.MaxRow() );
+ }
+ else
+ {
+ rRange.aStart.SetCol( 0 );
+ rRange.aEnd.SetCol( rDoc.MaxCol() );
+ }
+ }
+ if ( aCellRanges.size() > 1 ) // Multi-Area
+ {
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( pUnoRangesBase->GetDocShell(), aCellRanges ) );
+
+ return new ScVbaRange( mxParent, mxContext, xRanges, !bColumn, bColumn );
+ }
+ const ScRange aRange( obtainRangeEvenIfRangeListIsEmpty( aCellRanges));
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( pUnoRangesBase->GetDocShell(), aRange));
+ return new ScVbaRange( mxParent, mxContext, xRange, !bColumn, bColumn );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::getEntireRow()
+{
+ return getEntireColumnOrRow(false);
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::getEntireColumn()
+{
+ return getEntireColumnOrRow(true);
+}
+
+uno::Reference< excel::XComment > SAL_CALL
+ScVbaRange::AddComment( const uno::Any& Text )
+{
+ // if there is already a comment in the top-left cell then throw
+ if( getComment().is() )
+ throw uno::RuntimeException();
+
+ // workaround: Excel allows to create empty comment, Calc does not
+ OUString aNoteText;
+ if( Text.hasValue() && !(Text >>= aNoteText) )
+ throw uno::RuntimeException();
+ if( aNoteText.isEmpty() )
+ aNoteText = " ";
+
+ // try to create a new annotation
+ table::CellRangeAddress aRangePos = lclGetRangeAddress( mxRange );
+ table::CellAddress aNotePos( aRangePos.Sheet, aRangePos.StartColumn, aRangePos.StartRow );
+ uno::Reference< sheet::XSheetCellRange > xCellRange( mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSheetAnnotationsSupplier > xAnnosSupp( xCellRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSheetAnnotations > xAnnos( xAnnosSupp->getAnnotations(), uno::UNO_SET_THROW );
+ xAnnos->insertNew( aNotePos, aNoteText );
+ return new ScVbaComment( this, mxContext, getUnoModel(), mxRange );
+}
+
+uno::Reference< excel::XComment > SAL_CALL
+ScVbaRange::getComment()
+{
+ // intentional behavior to return a null object if no
+ // comment defined
+ uno::Reference< excel::XComment > xComment( new ScVbaComment( this, mxContext, getUnoModel(), mxRange ) );
+ if ( xComment->Text( uno::Any(), uno::Any(), uno::Any() ).isEmpty() )
+ return nullptr;
+ return xComment;
+
+}
+
+/// @throws uno::RuntimeException
+static uno::Reference< beans::XPropertySet >
+getRowOrColumnProps( const uno::Reference< table::XCellRange >& xCellRange, bool bRows )
+{
+ uno::Reference< table::XColumnRowRange > xColRow( xCellRange, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps;
+ if ( bRows )
+ xProps.set( xColRow->getRows(), uno::UNO_QUERY_THROW );
+ else
+ xProps.set( xColRow->getColumns(), uno::UNO_QUERY_THROW );
+ return xProps;
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getHidden()
+{
+ // if multi-area result is the result of the
+ // first area
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(sal_Int32(1)), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->getHidden();
+ }
+ bool bIsVisible = false;
+ try
+ {
+ uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
+ if ( !( xProps->getPropertyValue( ISVISIBLE ) >>= bIsVisible ) )
+ throw uno::RuntimeException("Failed to get IsVisible property" );
+ }
+ catch( const uno::Exception& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ nullptr, anyEx );
+ }
+ return uno::Any( !bIsVisible );
+}
+
+void SAL_CALL
+ScVbaRange::setHidden( const uno::Any& _hidden )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->setHidden( _hidden );
+ }
+ return;
+ }
+
+ bool bHidden = extractBoolFromAny( _hidden );
+ try
+ {
+ uno::Reference< beans::XPropertySet > xProps = getRowOrColumnProps( mxRange, mbIsRows );
+ xProps->setPropertyValue( ISVISIBLE, uno::Any( !bHidden ) );
+ }
+ catch( const uno::Exception& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ nullptr, anyEx );
+ }
+}
+
+sal_Bool SAL_CALL
+ScVbaRange::Replace( const OUString& What, const OUString& Replacement, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& MatchCase, const uno::Any& MatchByte, const uno::Any& SearchFormat, const uno::Any& ReplaceFormat )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ for ( sal_Int32 index = 1; index <= m_Areas->getCount(); ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->Replace( What, Replacement, LookAt, SearchOrder, MatchCase, MatchByte, SearchFormat, ReplaceFormat );
+ }
+ return true; // seems to return true always ( or at least I haven't found the trick of
+ }
+
+ // sanity check required params
+ if ( What.isEmpty() )
+ throw uno::RuntimeException("Range::Replace, missing params" );
+ // #TODO #FIXME SearchFormat & ReplacesFormat are not processed
+ // What do we do about MatchByte... we don't seem to support that
+ const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
+ SvxSearchItem newOptions( globalSearchOptions );
+
+ uno::Reference< util::XReplaceable > xReplace( mxRange, uno::UNO_QUERY );
+ if ( xReplace.is() )
+ {
+ uno::Reference< util::XReplaceDescriptor > xDescriptor =
+ xReplace->createReplaceDescriptor();
+
+ xDescriptor->setSearchString(What);
+ xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
+ xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
+ xDescriptor->setReplaceString( Replacement);
+ if ( LookAt.hasValue() )
+ {
+ // sets SearchWords ( true is Cell match )
+ sal_Int16 nLook = ::comphelper::getINT16( LookAt );
+ bool bSearchWords = false;
+ if ( nLook == excel::XlLookAt::xlPart )
+ bSearchWords = false;
+ else if ( nLook == excel::XlLookAt::xlWhole )
+ bSearchWords = true;
+ else
+ throw uno::RuntimeException("Range::Replace, illegal value for LookAt" );
+ // set global search props ( affects the find dialog
+ // and of course the defaults for this method
+ newOptions.SetWordOnly( bSearchWords );
+ xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
+ }
+ // sets SearchByRow ( true for Rows )
+ if ( SearchOrder.hasValue() )
+ {
+ sal_Int16 nSearchOrder = ::comphelper::getINT16( SearchOrder );
+ bool bSearchByRow = false;
+ if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
+ bSearchByRow = false;
+ else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
+ bSearchByRow = true;
+ else
+ throw uno::RuntimeException("Range::Replace, illegal value for SearchOrder" );
+
+ newOptions.SetRowDirection( bSearchByRow );
+ xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
+ }
+ if ( MatchCase.hasValue() )
+ {
+ bool bMatchCase = false;
+
+ // SearchCaseSensitive
+ MatchCase >>= bMatchCase;
+ xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
+ }
+
+ ScGlobal::SetSearchItem( newOptions );
+ // ignore MatchByte for the moment, it's not supported in
+ // OOo.org afaik
+
+ uno::Reference< container::XIndexAccess > xIndexAccess = xReplace->findAll( xDescriptor );
+ xReplace->replaceAll( xDescriptor );
+ if ( xIndexAccess.is() && xIndexAccess->getCount() > 0 )
+ {
+ for ( sal_Int32 i = 0; i < xIndexAccess->getCount(); ++i )
+ {
+ uno::Reference< table::XCellRange > xCellRange( xIndexAccess->getByIndex( i ), uno::UNO_QUERY );
+ if ( xCellRange.is() )
+ {
+ uno::Reference< excel::XRange > xRange( new ScVbaRange( mxParent, mxContext, xCellRange ) );
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( xRange, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Reference< excel::XRange > xNextRange( xEnum->nextElement(), uno::UNO_QUERY_THROW );
+ ScVbaRange* pRange = dynamic_cast< ScVbaRange * > ( xNextRange.get() );
+ if ( pRange )
+ pRange->fireChangeEvent();
+ }
+ }
+ }
+ }
+ }
+ return true; // always
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::Find( const uno::Any& What, const uno::Any& After, const uno::Any& LookIn, const uno::Any& LookAt, const uno::Any& SearchOrder, const uno::Any& SearchDirection, const uno::Any& MatchCase, const uno::Any& /*MatchByte*/, const uno::Any& /*SearchFormat*/ )
+{
+ // return a Range object that represents the first cell where that information is found.
+ OUString sWhat;
+ sal_Int32 nWhat = 0;
+ double fWhat = 0.0;
+
+ // string.
+ if( What >>= sWhat )
+ {}
+ else if( What >>= nWhat )
+ {
+ sWhat = OUString::number( nWhat );
+ }
+ else if( What >>= fWhat )
+ {
+ sWhat = OUString::number( fWhat );
+ }
+ else
+ throw uno::RuntimeException("Range::Find, missing search-for-what param" );
+
+ const SvxSearchItem& globalSearchOptions = ScGlobal::GetSearchItem();
+ SvxSearchItem newOptions( globalSearchOptions );
+
+ uno::Reference< util::XSearchable > xSearch( mxRange, uno::UNO_QUERY );
+ if( xSearch.is() )
+ {
+ uno::Reference< util::XSearchDescriptor > xDescriptor = xSearch->createSearchDescriptor();
+ xDescriptor->setSearchString(sWhat);
+ xDescriptor->setPropertyValue(SC_UNO_SRCHWILDCARD, uno::Any(true));
+ xDescriptor->setPropertyValue(SC_UNO_SRCHWCESCCHAR, uno::Any(sal_Int32('~')));
+
+ uno::Reference< excel::XRange > xAfterRange;
+ uno::Reference< table::XCellRange > xStartCell;
+ if( After >>= xAfterRange )
+ {
+ // After must be a single cell in the range
+ if( xAfterRange->getCount() > 1 )
+ throw uno::RuntimeException("After must be a single cell." );
+ uno::Reference< excel::XRange > xCell( Cells( uno::Any( xAfterRange->getRow() ), uno::Any( xAfterRange->getColumn() ) ), uno::UNO_SET_THROW );
+ xStartCell.set( xAfterRange->getCellRange(), uno::UNO_QUERY_THROW );
+ }
+
+ // LookIn
+ if( LookIn.hasValue() )
+ {
+ sal_Int32 nLookIn = 0;
+ if( LookIn >>= nLookIn )
+ {
+ SvxSearchCellType nSearchType;
+ switch( nLookIn )
+ {
+ case excel::XlFindLookIn::xlComments :
+ nSearchType = SvxSearchCellType::NOTE; // Notes
+ break;
+ case excel::XlFindLookIn::xlFormulas :
+ nSearchType = SvxSearchCellType::FORMULA;
+ break;
+ case excel::XlFindLookIn::xlValues :
+ nSearchType = SvxSearchCellType::VALUE;
+ break;
+ default:
+ throw uno::RuntimeException("Range::Find, illegal value for LookIn." );
+ }
+ newOptions.SetCellType( nSearchType );
+ xDescriptor->setPropertyValue( "SearchType", uno::Any( static_cast<sal_uInt16>(nSearchType) ) );
+ }
+ }
+
+ // LookAt
+ if ( LookAt.hasValue() )
+ {
+ sal_Int16 nLookAt = ::comphelper::getINT16( LookAt );
+ bool bSearchWords = false;
+ if ( nLookAt == excel::XlLookAt::xlPart )
+ bSearchWords = false;
+ else if ( nLookAt == excel::XlLookAt::xlWhole )
+ bSearchWords = true;
+ else
+ throw uno::RuntimeException("Range::Find, illegal value for LookAt" );
+ newOptions.SetWordOnly( bSearchWords );
+ xDescriptor->setPropertyValue( SC_UNO_SRCHWORDS, uno::Any( bSearchWords ) );
+ }
+
+ // SearchOrder
+ if ( SearchOrder.hasValue() )
+ {
+ sal_Int16 nSearchOrder = ::comphelper::getINT16( SearchOrder );
+ bool bSearchByRow = false;
+ if ( nSearchOrder == excel::XlSearchOrder::xlByColumns )
+ bSearchByRow = false;
+ else if ( nSearchOrder == excel::XlSearchOrder::xlByRows )
+ bSearchByRow = true;
+ else
+ throw uno::RuntimeException("Range::Find, illegal value for SearchOrder" );
+
+ newOptions.SetRowDirection( bSearchByRow );
+ xDescriptor->setPropertyValue( SC_UNO_SRCHBYROW, uno::Any( bSearchByRow ) );
+ }
+
+ // SearchDirection
+ if ( SearchDirection.hasValue() )
+ {
+ sal_Int32 nSearchDirection = 0;
+ if( SearchDirection >>= nSearchDirection )
+ {
+ bool bSearchBackwards = false;
+ if ( nSearchDirection == excel::XlSearchDirection::xlNext )
+ bSearchBackwards = false;
+ else if( nSearchDirection == excel::XlSearchDirection::xlPrevious )
+ bSearchBackwards = true;
+ else
+ throw uno::RuntimeException("Range::Find, illegal value for SearchDirection" );
+ newOptions.SetBackward( bSearchBackwards );
+ xDescriptor->setPropertyValue( "SearchBackwards", uno::Any( bSearchBackwards ) );
+ }
+ }
+
+ // MatchCase
+ bool bMatchCase = false;
+ if ( MatchCase.hasValue() )
+ {
+ // SearchCaseSensitive
+ if( !( MatchCase >>= bMatchCase ) )
+ throw uno::RuntimeException("Range::Find illegal value for MatchCase" );
+ }
+ xDescriptor->setPropertyValue( SC_UNO_SRCHCASE, uno::Any( bMatchCase ) );
+
+ // MatchByte
+ // SearchFormat
+ // ignore
+
+ ScGlobal::SetSearchItem( newOptions );
+
+ uno::Reference< uno::XInterface > xInterface = xStartCell.is() ? xSearch->findNext( xStartCell, xDescriptor) : xSearch->findFirst( xDescriptor );
+ uno::Reference< table::XCellRange > xCellRange( xInterface, uno::UNO_QUERY );
+ // if we are searching from a starting cell and failed to find a match
+ // then try from the beginning
+ if ( !xCellRange.is() && xStartCell.is() )
+ {
+ xInterface = xSearch->findFirst( xDescriptor );
+ xCellRange.set( xInterface, uno::UNO_QUERY );
+ }
+ if ( xCellRange.is() )
+ {
+ uno::Reference< excel::XRange > xResultRange = new ScVbaRange( mxParent, mxContext, xCellRange );
+ if( xResultRange.is() )
+ {
+ return xResultRange;
+ }
+ }
+
+ }
+
+ return uno::Reference< excel::XRange >();
+}
+
+static uno::Reference< table::XCellRange > processKey( const uno::Any& Key, const uno::Reference< uno::XComponentContext >& xContext, ScDocShell* pDocSh )
+{
+ uno::Reference< excel::XRange > xKeyRange;
+ if ( Key.getValueType() == cppu::UnoType<excel::XRange>::get() )
+ {
+ xKeyRange.set( Key, uno::UNO_QUERY_THROW );
+ }
+ else if ( Key.getValueType() == ::cppu::UnoType<OUString>::get() )
+
+ {
+ OUString sRangeName = ::comphelper::getString( Key );
+ table::CellRangeAddress aRefAddr;
+ if ( !pDocSh )
+ throw uno::RuntimeException("Range::Sort no docshell to calculate key param" );
+ xKeyRange = getRangeForName( xContext, sRangeName, pDocSh, aRefAddr );
+ }
+ else
+ throw uno::RuntimeException("Range::Sort illegal type value for key param" );
+ uno::Reference< table::XCellRange > xKey;
+ xKey.set( xKeyRange->getCellRange(), uno::UNO_QUERY_THROW );
+ return xKey;
+}
+
+// helper method for Sort
+/// @throws uno::RuntimeException
+static sal_Int32 findSortPropertyIndex( const uno::Sequence< beans::PropertyValue >& props,
+const OUString& sPropName )
+{
+ const beans::PropertyValue* pProp = std::find_if(props.begin(), props.end(),
+ [&sPropName](const beans::PropertyValue& rProp) { return rProp.Name == sPropName; });
+
+ if ( pProp == props.end() )
+ throw uno::RuntimeException("Range::Sort unknown sort property" );
+ return static_cast<sal_Int32>(std::distance(props.begin(), pProp));
+}
+
+// helper method for Sort
+/// @throws uno::RuntimeException
+static void updateTableSortField( const uno::Reference< table::XCellRange >& xParentRange,
+ const uno::Reference< table::XCellRange >& xColRowKey, sal_Int16 nOrder,
+ table::TableSortField& aTableField, bool bIsSortColumn, bool bMatchCase )
+{
+ RangeHelper parentRange( xParentRange );
+ RangeHelper colRowRange( xColRowKey );
+
+ table::CellRangeAddress parentRangeAddress = parentRange.getCellRangeAddressable()->getRangeAddress();
+
+ table::CellRangeAddress colRowKeyAddress = colRowRange.getCellRangeAddressable()->getRangeAddress();
+
+ // make sure that upper left point of key range is within the
+ // parent range
+ if (
+ ( bIsSortColumn || colRowKeyAddress.StartColumn < parentRangeAddress.StartColumn ||
+ colRowKeyAddress.StartColumn > parentRangeAddress.EndColumn )
+ &&
+ ( !bIsSortColumn || colRowKeyAddress.StartRow < parentRangeAddress.StartRow ||
+ colRowKeyAddress.StartRow > parentRangeAddress.EndRow )
+ )
+ throw uno::RuntimeException("Illegal Key param" );
+
+ //determine col/row index
+ if ( bIsSortColumn )
+ aTableField.Field = colRowKeyAddress.StartRow - parentRangeAddress.StartRow;
+ else
+ aTableField.Field = colRowKeyAddress.StartColumn - parentRangeAddress.StartColumn;
+ aTableField.IsCaseSensitive = bMatchCase;
+
+ if ( nOrder == excel::XlSortOrder::xlAscending )
+ aTableField.IsAscending = true;
+ else
+ aTableField.IsAscending = false;
+
+
+}
+
+void SAL_CALL
+ScVbaRange::Sort( const uno::Any& Key1, const uno::Any& Order1, const uno::Any& Key2, const uno::Any& /*Type*/, const uno::Any& Order2, const uno::Any& Key3, const uno::Any& Order3, const uno::Any& Header, const uno::Any& OrderCustom, const uno::Any& MatchCase, const uno::Any& Orientation, const uno::Any& SortMethod, const uno::Any& DataOption1, const uno::Any& DataOption2, const uno::Any& DataOption3 )
+{
+ // #TODO# #FIXME# can we do something with Type
+ if ( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException("That command cannot be used on multiple selections" );
+
+ sal_Int16 nDataOption1 = excel::XlSortDataOption::xlSortNormal;
+ sal_Int16 nDataOption2 = excel::XlSortDataOption::xlSortNormal;
+ sal_Int16 nDataOption3 = excel::XlSortDataOption::xlSortNormal;
+
+ ScDocument& rDoc = getScDocument();
+
+ uno::Reference< table::XCellRange > xRangeCurrent;
+ if (isSingleCellRange())
+ {
+ // Expand to CurrentRegion
+ uno::Reference< excel::XRange > xCurrent( CurrentRegion());
+ if (xCurrent.is())
+ {
+ const ScVbaRange* pRange = getImplementation( xCurrent );
+ if (pRange)
+ xRangeCurrent = pRange->mxRange;
+ }
+ }
+ if (!xRangeCurrent.is())
+ xRangeCurrent = mxRange;
+ RangeHelper thisRange( xRangeCurrent );
+ table::CellRangeAddress thisRangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+
+ ScSortParam aSortParam;
+ SCTAB nTab = thisRangeAddress.Sheet;
+ rDoc.GetSortParam( aSortParam, nTab );
+
+ if ( DataOption1.hasValue() )
+ DataOption1 >>= nDataOption1;
+ if ( DataOption2.hasValue() )
+ DataOption2 >>= nDataOption2;
+ if ( DataOption3.hasValue() )
+ DataOption3 >>= nDataOption3;
+
+ // 1) #TODO #FIXME need to process DataOption[1..3] not used currently
+ // 2) #TODO #FIXME need to refactor this ( below ) into an IsSingleCell() method
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(xRangeCurrent, uno::UNO_QUERY_THROW );
+
+ // set up defaults
+
+ sal_Int16 nOrder1 = aSortParam.maKeyState[0].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
+ sal_Int16 nOrder2 = aSortParam.maKeyState[1].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
+ sal_Int16 nOrder3 = aSortParam.maKeyState[2].bAscending ? excel::XlSortOrder::xlAscending : excel::XlSortOrder::xlDescending;
+
+ sal_Int16 nCustom = aSortParam.nUserIndex;
+ sal_Int16 nSortMethod = excel::XlSortMethod::xlPinYin;
+ bool bMatchCase = aSortParam.bCaseSens;
+
+ // seems to work opposite to expected, see below
+ sal_Int16 nOrientation = aSortParam.bByRow ? excel::XlSortOrientation::xlSortColumns : excel::XlSortOrientation::xlSortRows;
+
+ if ( Orientation.hasValue() )
+ {
+ // Documentation says xlSortRows is default but that doesn't appear to be
+ // the case. Also it appears that xlSortColumns is the default which
+ // strangely enough sorts by Row
+ nOrientation = ::comphelper::getINT16( Orientation );
+ // persist new option to be next calls default
+ if ( nOrientation == excel::XlSortOrientation::xlSortRows )
+ aSortParam.bByRow = false;
+ else
+ aSortParam.bByRow = true;
+
+ }
+
+ bool bIsSortColumns=false; // sort by row
+
+ if ( nOrientation == excel::XlSortOrientation::xlSortRows )
+ bIsSortColumns = true;
+ sal_Int16 nHeader = aSortParam.nCompatHeader;
+ bool bContainsHeader = false;
+
+ if ( Header.hasValue() )
+ {
+ nHeader = ::comphelper::getINT16( Header );
+ aSortParam.nCompatHeader = nHeader;
+ }
+
+ if ( nHeader == excel::XlYesNoGuess::xlGuess )
+ {
+ bool bHasColHeader = rDoc.HasColHeader( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), static_cast< SCCOL >( thisRangeAddress.EndColumn ), static_cast< SCROW >( thisRangeAddress.EndRow ), static_cast< SCTAB >( thisRangeAddress.Sheet ));
+ bool bHasRowHeader = rDoc.HasRowHeader( static_cast< SCCOL >( thisRangeAddress.StartColumn ), static_cast< SCROW >( thisRangeAddress.StartRow ), static_cast< SCCOL >( thisRangeAddress.EndColumn ), static_cast< SCROW >( thisRangeAddress.EndRow ), static_cast< SCTAB >( thisRangeAddress.Sheet ) );
+ if ( bHasColHeader || bHasRowHeader )
+ nHeader = excel::XlYesNoGuess::xlYes;
+ else
+ nHeader = excel::XlYesNoGuess::xlNo;
+ aSortParam.nCompatHeader = nHeader;
+ }
+
+ if ( nHeader == excel::XlYesNoGuess::xlYes )
+ bContainsHeader = true;
+
+ if ( SortMethod.hasValue() )
+ {
+ nSortMethod = ::comphelper::getINT16( SortMethod );
+ }
+
+ if ( OrderCustom.hasValue() )
+ {
+ OrderCustom >>= nCustom;
+ --nCustom; // 0-based in OOo
+ aSortParam.nUserIndex = nCustom;
+ }
+
+ if ( MatchCase.hasValue() )
+ {
+ MatchCase >>= bMatchCase;
+ aSortParam.bCaseSens = bMatchCase;
+ }
+
+ if ( Order1.hasValue() )
+ {
+ nOrder1 = ::comphelper::getINT16(Order1);
+ if ( nOrder1 == excel::XlSortOrder::xlAscending )
+ aSortParam.maKeyState[0].bAscending = true;
+ else
+ aSortParam.maKeyState[0].bAscending = false;
+
+ }
+ if ( Order2.hasValue() )
+ {
+ nOrder2 = ::comphelper::getINT16(Order2);
+ if ( nOrder2 == excel::XlSortOrder::xlAscending )
+ aSortParam.maKeyState[1].bAscending = true;
+ else
+ aSortParam.maKeyState[1].bAscending = false;
+ }
+ if ( Order3.hasValue() )
+ {
+ nOrder3 = ::comphelper::getINT16(Order3);
+ if ( nOrder3 == excel::XlSortOrder::xlAscending )
+ aSortParam.maKeyState[2].bAscending = true;
+ else
+ aSortParam.maKeyState[2].bAscending = false;
+ }
+
+ uno::Reference< table::XCellRange > xKey1;
+ uno::Reference< table::XCellRange > xKey2;
+ uno::Reference< table::XCellRange > xKey3;
+ ScDocShell* pDocShell = getScDocShell();
+ xKey1 = processKey( Key1, mxContext, pDocShell );
+ if ( !xKey1.is() )
+ throw uno::RuntimeException("Range::Sort needs a key1 param" );
+
+ if ( Key2.hasValue() )
+ xKey2 = processKey( Key2, mxContext, pDocShell );
+ if ( Key3.hasValue() )
+ xKey3 = processKey( Key3, mxContext, pDocShell );
+
+ uno::Reference< util::XSortable > xSort( xRangeCurrent, uno::UNO_QUERY_THROW );
+ uno::Sequence< beans::PropertyValue > sortDescriptor = xSort->createSortDescriptor();
+ auto psortDescriptor = sortDescriptor.getArray();
+ sal_Int32 nTableSortFieldIndex = findSortPropertyIndex( sortDescriptor, "SortFields" );
+
+ uno::Sequence< table::TableSortField > sTableFields(1);
+ sal_Int32 nTableIndex = 0;
+ updateTableSortField( xRangeCurrent, xKey1, nOrder1, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
+
+ if ( xKey2.is() )
+ {
+ sTableFields.realloc( sTableFields.getLength() + 1 );
+ updateTableSortField( xRangeCurrent, xKey2, nOrder2, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
+ }
+ if ( xKey3.is() )
+ {
+ sTableFields.realloc( sTableFields.getLength() + 1 );
+ updateTableSortField( xRangeCurrent, xKey3, nOrder3, sTableFields.getArray()[ nTableIndex++ ], bIsSortColumns, bMatchCase );
+ }
+ psortDescriptor[ nTableSortFieldIndex ].Value <<= sTableFields;
+
+ sal_Int32 nIndex = findSortPropertyIndex( sortDescriptor, "IsSortColumns" );
+ psortDescriptor[ nIndex ].Value <<= bIsSortColumns;
+
+ nIndex = findSortPropertyIndex( sortDescriptor, "ContainsHeader" );
+ psortDescriptor[ nIndex ].Value <<= bContainsHeader;
+
+ rDoc.SetSortParam( aSortParam, nTab );
+ xSort->sort( sortDescriptor );
+
+ // #FIXME #TODO
+ // The SortMethod param is not processed ( not sure what its all about, need to
+ (void)nSortMethod;
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::End( ::sal_Int32 Direction )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( getArea( 0 ), uno::UNO_SET_THROW );
+ return xRange->End( Direction );
+ }
+
+ // #FIXME #TODO
+ // euch! found my orig implementation sucked, so
+ // trying this even sucker one (really need to use/expose code in
+ // around ScTabView::MoveCursorArea(), that's the bit that calculates
+ // where the cursor should go)
+ // Main problem with this method is the ultra hacky attempt to preserve
+ // the ActiveCell, there should be no need to go to these extremes
+
+ // Save ActiveSheet/ActiveCell pos (to restore later)
+ uno::Any aDft;
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XWorksheet > sActiveSheet = xApplication->getActiveSheet();
+ OUString sActiveCell = xApplication->getActiveCell()->Address(aDft, aDft, aDft, aDft, aDft );
+
+ // position current cell upper left of this range
+ Cells( uno::Any( sal_Int32(1) ), uno::Any( sal_Int32(1) ) )->Select();
+
+ uno::Reference< frame::XModel > xModel = getModelFromRange( mxRange );
+
+ SfxViewFrame* pViewFrame = excel::getViewFrame( xModel );
+ if ( pViewFrame )
+ {
+ SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
+ // Hoping this will make sure this slot is called
+ // synchronously
+ SfxBoolItem sfxAsync( SID_ASYNCHRON, false );
+ aArgs.Put( sfxAsync, sfxAsync.Which() );
+ SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher();
+
+ sal_uInt16 nSID = 0;
+
+ switch( Direction )
+ {
+ case excel::XlDirection::xlDown:
+ nSID = SID_CURSORBLKDOWN;
+ break;
+ case excel::XlDirection::xlUp:
+ nSID = SID_CURSORBLKUP;
+ break;
+ case excel::XlDirection::xlToLeft:
+ nSID = SID_CURSORBLKLEFT;
+ break;
+ case excel::XlDirection::xlToRight:
+ nSID = SID_CURSORBLKRIGHT;
+ break;
+ default:
+ throw uno::RuntimeException(": Invalid ColumnIndex" );
+ }
+ if ( pDispatcher )
+ {
+ pDispatcher->Execute( nSID, SfxCallMode::SYNCHRON, aArgs );
+ }
+ }
+
+ // result is the ActiveCell
+ OUString sMoved = xApplication->getActiveCell()->Address(aDft, aDft, aDft, aDft, aDft );
+
+ uno::Any aVoid;
+ uno::Reference< excel::XRange > resultCell;
+ resultCell.set( xApplication->getActiveSheet()->Range( uno::Any( sMoved ), aVoid ), uno::UNO_SET_THROW );
+
+ // restore old ActiveCell
+ uno::Reference< excel::XRange > xOldActiveCell( sActiveSheet->Range( uno::Any( sActiveCell ), aVoid ), uno::UNO_SET_THROW );
+ xOldActiveCell->Select();
+
+
+ // return result
+ return resultCell;
+}
+
+bool
+ScVbaRange::isSingleCellRange() const
+{
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( mxRange, uno::UNO_QUERY );
+ if ( xAddressable.is() )
+ {
+ table::CellRangeAddress aRangeAddr = xAddressable->getRangeAddress();
+ return ( aRangeAddr.EndColumn == aRangeAddr.StartColumn && aRangeAddr.EndRow == aRangeAddr.StartRow );
+ }
+ return false;
+}
+
+uno::Reference< excel::XCharacters > SAL_CALL
+ScVbaRange::characters( const uno::Any& Start, const uno::Any& Length )
+{
+ if ( !isSingleCellRange() )
+ throw uno::RuntimeException("Can't create Characters property for multicell range " );
+ uno::Reference< text::XSimpleText > xSimple(mxRange->getCellByPosition(0,0) , uno::UNO_QUERY_THROW );
+ ScDocument& rDoc = getDocumentFromRange(mxRange);
+
+ ScVbaPalette aPalette( rDoc.GetDocumentShell() );
+ return new ScVbaCharacters( this, mxContext, aPalette, xSimple, Start, Length );
+}
+
+ void SAL_CALL
+ScVbaRange::Delete( const uno::Any& Shift )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->Delete( Shift );
+ }
+ return;
+ }
+ sheet::CellDeleteMode mode = sheet::CellDeleteMode_NONE ;
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ if ( Shift.hasValue() )
+ {
+ sal_Int32 nShift = 0;
+ Shift >>= nShift;
+ switch ( nShift )
+ {
+ case excel::XlDeleteShiftDirection::xlShiftUp:
+ mode = sheet::CellDeleteMode_UP;
+ break;
+ case excel::XlDeleteShiftDirection::xlShiftToLeft:
+ mode = sheet::CellDeleteMode_LEFT;
+ break;
+ default:
+ throw uno::RuntimeException("Illegal parameter " );
+ }
+ }
+ else
+ {
+ ScDocument& rDoc = getScDocument();
+ bool bFullRow = ( thisAddress.StartColumn == 0 && thisAddress.EndColumn == rDoc.MaxCol() );
+ sal_Int32 nCols = thisAddress.EndColumn - thisAddress.StartColumn;
+ sal_Int32 nRows = thisAddress.EndRow - thisAddress.StartRow;
+ if ( mbIsRows || bFullRow || ( nCols >= nRows ) )
+ mode = sheet::CellDeleteMode_UP;
+ else
+ mode = sheet::CellDeleteMode_LEFT;
+ }
+ uno::Reference< sheet::XCellRangeMovement > xCellRangeMove( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ xCellRangeMove->removeRange( thisAddress, mode );
+
+}
+
+//XElementAccess
+sal_Bool SAL_CALL
+ScVbaRange::hasElements()
+{
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY );
+ if ( xColumnRowRange.is() )
+ if ( xColumnRowRange->getRows()->getCount() ||
+ xColumnRowRange->getColumns()->getCount() )
+ return true;
+ return false;
+}
+
+// XEnumerationAccess
+uno::Reference< container::XEnumeration > SAL_CALL
+ScVbaRange::createEnumeration()
+{
+ if ( mbIsColumns || mbIsRows )
+ {
+ uno::Reference< table::XColumnRowRange > xColumnRowRange(mxRange, uno::UNO_QUERY );
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ sal_Int32 nElems = 0;
+ if ( mbIsColumns )
+ nElems = xColumnRowRange->getColumns()->getCount();
+ else
+ nElems = xColumnRowRange->getRows()->getCount();
+ return new ColumnsRowEnumeration( xRange, nElems );
+
+ }
+ return new CellsEnumeration( mxParent, mxContext, m_Areas );
+}
+
+OUString SAL_CALL
+ScVbaRange::getDefaultMethodName( )
+{
+ return "Item";
+}
+
+// returns calc internal col. width ( in points )
+double
+ScVbaRange::getCalcColWidth(const table::CellRangeAddress& rAddress)
+{
+ ScDocument& rDoc = getScDocument();
+ sal_uInt16 nWidth = rDoc.GetOriginalWidth( static_cast< SCCOL >( rAddress.StartColumn ), static_cast< SCTAB >( rAddress.Sheet ) );
+ double nPoints = lcl_TwipsToPoints( nWidth );
+ nPoints = lcl_Round2DecPlaces( nPoints );
+ return nPoints;
+}
+
+double
+ScVbaRange::getCalcRowHeight(const table::CellRangeAddress& rAddress)
+{
+ ScDocument& rDoc = getDocumentFromRange( mxRange );
+ sal_uInt16 nWidth = rDoc.GetOriginalHeight( rAddress.StartRow, rAddress.Sheet );
+ double nPoints = lcl_TwipsToPoints( nWidth );
+ nPoints = lcl_Round2DecPlaces( nPoints );
+ return nPoints;
+}
+
+// return Char Width in points
+static double getDefaultCharWidth( ScDocShell* pDocShell )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OutputDevice* pRefDevice = rDoc.GetRefDevice();
+ ScPatternAttr* pAttr = rDoc.GetDefPattern();
+ vcl::Font aDefFont;
+ pAttr->fillFontOnly(aDefFont, pRefDevice);
+ pRefDevice->SetFont(aDefFont);
+ tools::Long nCharWidth = pRefDevice->GetTextWidth( OUString( '0' ) ); // 1/100th mm
+ return o3tl::convert<double>(nCharWidth, o3tl::Length::mm100, o3tl::Length::pt);
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getColumnWidth()
+{
+ sal_Int32 nLen = m_Areas->getCount();
+ if ( nLen > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->getColumnWidth();
+ }
+
+ double nColWidth = 0;
+ ScDocShell* pShell = getScDocShell();
+ if ( pShell )
+ {
+ double defaultCharWidth = getDefaultCharWidth( pShell );
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ sal_Int32 nStartCol = thisAddress.StartColumn;
+ sal_Int32 nEndCol = thisAddress.EndColumn;
+ sal_uInt16 nColTwips = 0;
+ for( sal_Int32 nCol = nStartCol ; nCol <= nEndCol; ++nCol )
+ {
+ thisAddress.StartColumn = nCol;
+ sal_uInt16 nCurTwips = pShell->GetDocument().GetOriginalWidth( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCTAB >( thisAddress.Sheet ) );
+ if ( nCol == nStartCol )
+ nColTwips = nCurTwips;
+ if ( nColTwips != nCurTwips )
+ return aNULL();
+ }
+ nColWidth = lcl_TwipsToPoints( nColTwips );
+ if ( nColWidth != 0.0 )
+ nColWidth = ( nColWidth / defaultCharWidth ) - fExtraWidth;
+ }
+ nColWidth = lcl_Round2DecPlaces( nColWidth );
+ return uno::Any( nColWidth );
+}
+
+void SAL_CALL
+ScVbaRange::setColumnWidth( const uno::Any& _columnwidth )
+{
+ sal_Int32 nLen = m_Areas->getCount();
+ if ( nLen > 1 )
+ {
+ for ( sal_Int32 index = 1; index != nLen; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->setColumnWidth( _columnwidth );
+ }
+ return;
+ }
+ double nColWidth = 0;
+ _columnwidth >>= nColWidth;
+ nColWidth = lcl_Round2DecPlaces( nColWidth );
+ ScDocShell* pDocShell = getScDocShell();
+ if ( !pDocShell )
+ return;
+
+ if ( nColWidth != 0.0 )
+ nColWidth = ( nColWidth + fExtraWidth ) * getDefaultCharWidth( pDocShell );
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ sal_uInt16 nTwips = lcl_pointsToTwips( nColWidth );
+
+ std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(thisAddress.StartColumn, thisAddress.EndColumn));
+ // #163561# use mode SC_SIZE_DIRECT: hide for width 0, show for other values
+ pDocShell->GetDocFunc().SetWidthOrHeight(
+ true, aColArr, thisAddress.Sheet, SC_SIZE_DIRECT, nTwips, true, true);
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getWidth()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->getWidth();
+ }
+ uno::Reference< table::XColumnRowRange > xColRowRange( mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xColRowRange->getColumns(), uno::UNO_QUERY_THROW );
+ sal_Int32 nElems = xIndexAccess->getCount();
+ double nWidth = 0;
+ for ( sal_Int32 index=0; index<nElems; ++index )
+ {
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW );
+ double nTmpWidth = getCalcColWidth( xAddressable->getRangeAddress() );
+ nWidth += nTmpWidth;
+ }
+ return uno::Any( nWidth );
+}
+
+uno::Any SAL_CALL
+ScVbaRange::Areas( const uno::Any& item)
+{
+ if ( !item.hasValue() )
+ return uno::Any( m_Areas );
+ return m_Areas->Item( item, uno::Any() );
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::getArea( sal_Int32 nIndex )
+{
+ if ( !m_Areas.is() )
+ throw uno::RuntimeException("No areas available" );
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( ++nIndex ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange;
+}
+
+uno::Any
+ScVbaRange::Borders( const uno::Any& item )
+{
+ if ( !item.hasValue() )
+ return uno::Any( getBorders() );
+ return getBorders()->Item( item, uno::Any() );
+}
+
+uno::Any SAL_CALL
+ScVbaRange::BorderAround( const css::uno::Any& LineStyle, const css::uno::Any& Weight,
+ const css::uno::Any& ColorIndex, const css::uno::Any& Color )
+{
+ sal_Int32 nCount = getBorders()->getCount();
+
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ const sal_Int32 nLineType = supportedIndexTable[i];
+ switch( nLineType )
+ {
+ case excel::XlBordersIndex::xlEdgeLeft:
+ case excel::XlBordersIndex::xlEdgeTop:
+ case excel::XlBordersIndex::xlEdgeBottom:
+ case excel::XlBordersIndex::xlEdgeRight:
+ {
+ uno::Reference< excel::XBorder > xBorder( m_Borders->Item( uno::Any( nLineType ), uno::Any() ), uno::UNO_QUERY_THROW );
+ if( LineStyle.hasValue() )
+ {
+ xBorder->setLineStyle( LineStyle );
+ }
+ if( Weight.hasValue() )
+ {
+ xBorder->setWeight( Weight );
+ }
+ if( ColorIndex.hasValue() )
+ {
+ xBorder->setColorIndex( ColorIndex );
+ }
+ if( Color.hasValue() )
+ {
+ xBorder->setColor( Color );
+ }
+ break;
+ }
+ case excel::XlBordersIndex::xlInsideVertical:
+ case excel::XlBordersIndex::xlInsideHorizontal:
+ case excel::XlBordersIndex::xlDiagonalDown:
+ case excel::XlBordersIndex::xlDiagonalUp:
+ break;
+ default:
+ return uno::Any( false );
+ }
+ }
+ return uno::Any( true );
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getRowHeight()
+{
+ sal_Int32 nLen = m_Areas->getCount();
+ if ( nLen > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->getRowHeight();
+ }
+
+ // if any row's RowHeight in the
+ // range is different from any other, then return NULL
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+
+ sal_Int32 nStartRow = thisAddress.StartRow;
+ sal_Int32 nEndRow = thisAddress.EndRow;
+ sal_uInt16 nRowTwips = 0;
+ // #TODO probably possible to use the SfxItemSet (and see if
+ // SfxItemState::DONTCARE is set) to improve performance
+// #CHECKME looks like this is general behaviour not just row Range specific
+// if ( mbIsRows )
+ ScDocShell* pShell = getScDocShell();
+ if ( pShell )
+ {
+ for ( sal_Int32 nRow = nStartRow ; nRow <= nEndRow; ++nRow )
+ {
+ thisAddress.StartRow = nRow;
+ sal_uInt16 nCurTwips = pShell->GetDocument().GetOriginalHeight( thisAddress.StartRow, thisAddress.Sheet );
+ if ( nRow == nStartRow )
+ nRowTwips = nCurTwips;
+ if ( nRowTwips != nCurTwips )
+ return aNULL();
+ }
+ }
+ double nHeight = lcl_Round2DecPlaces( lcl_TwipsToPoints( nRowTwips ) );
+ return uno::Any( nHeight );
+}
+
+void SAL_CALL
+ScVbaRange::setRowHeight( const uno::Any& _rowheight)
+{
+ sal_Int32 nLen = m_Areas->getCount();
+ if ( nLen > 1 )
+ {
+ for ( sal_Int32 index = 1; index != nLen; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->setRowHeight( _rowheight );
+ }
+ return;
+ }
+ double nHeight = 0; // Incoming height is in points
+ _rowheight >>= nHeight;
+ nHeight = lcl_Round2DecPlaces( nHeight );
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ sal_uInt16 nTwips = lcl_pointsToTwips( nHeight );
+
+ ScDocShell* pDocShell = getDocShellFromRange( mxRange );
+ std::vector<sc::ColRowSpan> aRowArr(1, sc::ColRowSpan(thisAddress.StartRow, thisAddress.EndRow));
+ pDocShell->GetDocFunc().SetWidthOrHeight(
+ false, aRowArr, thisAddress.Sheet, SC_SIZE_ORIGINAL, nTwips, true, true);
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getPageBreak()
+{
+ sal_Int32 nPageBreak = excel::XlPageBreak::xlPageBreakNone;
+ ScDocShell* pShell = getDocShellFromRange( mxRange );
+ if ( pShell )
+ {
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ bool bColumn = false;
+
+ if (thisAddress.StartRow==0)
+ bColumn = true;
+
+ uno::Reference< frame::XModel > xModel = pShell->GetModel();
+ if ( xModel.is() )
+ {
+ ScDocument& rDoc = getDocumentFromRange( mxRange );
+
+ ScBreakType nBreak = ScBreakType::NONE;
+ if ( !bColumn )
+ nBreak = rDoc.HasRowBreak(thisAddress.StartRow, thisAddress.Sheet);
+ else
+ nBreak = rDoc.HasColBreak(thisAddress.StartColumn, thisAddress.Sheet);
+
+ if (nBreak & ScBreakType::Page)
+ nPageBreak = excel::XlPageBreak::xlPageBreakAutomatic;
+
+ if (nBreak & ScBreakType::Manual)
+ nPageBreak = excel::XlPageBreak::xlPageBreakManual;
+ }
+ }
+
+ return uno::Any( nPageBreak );
+}
+
+void SAL_CALL
+ScVbaRange::setPageBreak( const uno::Any& _pagebreak)
+{
+ sal_Int32 nPageBreak = 0;
+ _pagebreak >>= nPageBreak;
+
+ ScDocShell* pShell = getDocShellFromRange( mxRange );
+ if ( !pShell )
+ return;
+
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ if ((thisAddress.StartColumn==0) && (thisAddress.StartRow==0))
+ return;
+ bool bColumn = false;
+
+ if (thisAddress.StartRow==0)
+ bColumn = true;
+
+ ScAddress aAddr( static_cast<SCCOL>(thisAddress.StartColumn), thisAddress.StartRow, thisAddress.Sheet );
+ uno::Reference< frame::XModel > xModel = pShell->GetModel();
+ if ( xModel.is() )
+ {
+ ScTabViewShell* pViewShell = excel::getBestViewShell( xModel );
+ if ( nPageBreak == excel::XlPageBreak::xlPageBreakManual )
+ pViewShell->InsertPageBreak( bColumn, true, &aAddr);
+ else if ( nPageBreak == excel::XlPageBreak::xlPageBreakNone )
+ pViewShell->DeletePageBreak( bColumn, true, &aAddr);
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getHeight()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32(1) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->getHeight();
+ }
+
+ uno::Reference< table::XColumnRowRange > xColRowRange( mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xColRowRange->getRows(), uno::UNO_QUERY_THROW );
+ sal_Int32 nElems = xIndexAccess->getCount();
+ double nHeight = 0;
+ for ( sal_Int32 index=0; index<nElems; ++index )
+ {
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( xIndexAccess->getByIndex( index ), uno::UNO_QUERY_THROW );
+ nHeight += getCalcRowHeight(xAddressable->getRangeAddress() );
+ }
+ return uno::Any( nHeight );
+}
+
+awt::Point
+ScVbaRange::getPosition() const
+{
+ awt::Point aPoint;
+ uno::Reference< beans::XPropertySet > xProps;
+ if ( mxRange.is() )
+ xProps.set( mxRange, uno::UNO_QUERY_THROW );
+ else
+ xProps.set( mxRanges, uno::UNO_QUERY_THROW );
+ xProps->getPropertyValue( "Position" ) >>= aPoint;
+ return aPoint;
+}
+uno::Any SAL_CALL
+ScVbaRange::getLeft()
+{
+ // helperapi returns the first ranges left ( and top below )
+ if ( m_Areas->getCount() > 1 )
+ return getArea( 0 )->getLeft();
+ awt::Point aPoint = getPosition();
+ return uno::Any(o3tl::convert<double>(aPoint.X, o3tl::Length::mm100, o3tl::Length::pt));
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getTop()
+{
+ // helperapi returns the first ranges top
+ if ( m_Areas->getCount() > 1 )
+ return getArea( 0 )->getTop();
+ awt::Point aPoint= getPosition();
+ return uno::Any(o3tl::convert<double>(aPoint.Y, o3tl::Length::mm100, o3tl::Length::pt));
+}
+
+static uno::Reference< sheet::XCellRangeReferrer > getNamedRange( const uno::Reference< uno::XInterface >& xIf, const uno::Reference< table::XCellRange >& thisRange )
+{
+ uno::Reference< beans::XPropertySet > xProps( xIf, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xNameAccess( xProps->getPropertyValue( "NamedRanges" ), uno::UNO_QUERY_THROW );
+
+ const uno::Sequence< OUString > sNames = xNameAccess->getElementNames();
+// uno::Reference< table::XCellRange > thisRange( getCellRange(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeReferrer > xNamedRange;
+ for ( const auto& rName : sNames )
+ {
+ uno::Reference< sheet::XCellRangeReferrer > xName( xNameAccess->getByName( rName ), uno::UNO_QUERY );
+ if ( xName.is() )
+ {
+ if ( thisRange == xName->getReferredCells() )
+ {
+ xNamedRange = xName;
+ break;
+ }
+ }
+ }
+ return xNamedRange;
+}
+
+uno::Reference< excel::XName >
+ScVbaRange::getName()
+{
+ uno::Reference< beans::XPropertySet > xProps( getUnoModel(), uno::UNO_QUERY );
+ uno::Reference< table::XCellRange > thisRange( getCellRange(), uno::UNO_QUERY_THROW );
+ // Application range
+ uno::Reference< sheet::XCellRangeReferrer > xNamedRange = getNamedRange( xProps, thisRange );
+
+ if ( !xNamedRange.is() )
+ {
+ // not in application range then assume it might be in
+ // sheet namedranges
+ RangeHelper aRange( thisRange );
+ uno::Reference< sheet::XSpreadsheet > xSheet = aRange.getSpreadSheet();
+ xProps.set( xSheet, uno::UNO_QUERY );
+ // impl here
+ xNamedRange = getNamedRange( xProps, thisRange );
+ }
+ if ( xProps.is() && xNamedRange.is() )
+ {
+ uno::Reference< sheet::XNamedRanges > xNamedRanges( xProps, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XNamedRange > xName( xNamedRange, uno::UNO_QUERY_THROW );
+ return new ScVbaName( mxParent, mxContext, xName, xNamedRanges, getUnoModel() );
+ }
+ return uno::Reference< excel::XName >();
+}
+
+uno::Reference< excel::XWorksheet >
+ScVbaRange::getWorksheet()
+{
+ // #TODO #FIXME parent should always be set up ( currently that's not
+ // the case )
+ uno::Reference< excel::XWorksheet > xSheet( getParent(), uno::UNO_QUERY );
+ if ( !xSheet.is() )
+ {
+ uno::Reference< table::XCellRange > xRange = mxRange;
+
+ if ( mxRanges.is() ) // assign xRange to first range
+ {
+ uno::Reference< container::XIndexAccess > xIndex( mxRanges, uno::UNO_QUERY_THROW );
+ xRange.set( xIndex->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ }
+ ScDocShell* pDocShell = getDocShellFromRange(xRange);
+ RangeHelper rHelper(xRange);
+ // parent should be Thisworkbook
+ xSheet.set( new ScVbaWorksheet( uno::Reference< XHelperInterface >(), mxContext,rHelper.getSpreadSheet(),pDocShell->GetModel()) );
+ }
+ return xSheet;
+}
+
+// #TODO remove this ugly application processing
+// Process an application Range request e.g. 'Range("a1,b2,a4:b6")
+uno::Reference< excel::XRange >
+ScVbaRange::ApplicationRange( const uno::Reference< uno::XComponentContext >& xContext, const css::uno::Any &Cell1, const css::uno::Any &Cell2 )
+{
+ // Although the documentation seems clear that Range without a
+ // qualifier then it's a shortcut for ActiveSheet.Range
+ // however, similarly Application.Range is apparently also a
+ // shortcut for ActiveSheet.Range
+ // The is however a subtle behavioural difference I've come across
+ // wrt to named ranges.
+ // If a named range "test" exists { Sheet1!$A1 } and the active sheet
+ // is Sheet2 then the following will fail
+ // msgbox ActiveSheet.Range("test").Address ' fails
+ // msgbox WorkSheets("Sheet2").Range("test").Address
+ // but!!!
+ // msgbox Range("test").Address ' works
+ // msgbox Application.Range("test").Address ' works
+
+ // Single param Range
+ OUString sRangeName;
+ Cell1 >>= sRangeName;
+ if ( Cell1.hasValue() && !Cell2.hasValue() && !sRangeName.isEmpty() )
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( getCurrentExcelDoc(xContext), uno::UNO_QUERY_THROW );
+
+ uno::Reference< container::XNameAccess > xNamed( xPropSet->getPropertyValue( "NamedRanges" ), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XCellRangeReferrer > xReferrer;
+ try
+ {
+ xReferrer.set ( xNamed->getByName( sRangeName ), uno::UNO_QUERY );
+ }
+ catch( uno::Exception& /*e*/ )
+ {
+ // do nothing
+ }
+ if ( xReferrer.is() )
+ {
+ uno::Reference< table::XCellRange > xRange = xReferrer->getReferredCells();
+ if ( xRange.is() )
+ {
+ uno::Reference< excel::XRange > xVbRange = new ScVbaRange( excel::getUnoSheetModuleObj( xRange ), xContext, xRange );
+ return xVbRange;
+ }
+ }
+ }
+
+ uno::Reference<table::XCellRange> xSheetRange;
+
+ try
+ {
+ uno::Reference<sheet::XSpreadsheetView> xView(
+ getCurrentExcelDoc(xContext)->getCurrentController(), uno::UNO_QUERY_THROW);
+
+ xSheetRange.set(xView->getActiveSheet(), uno::UNO_QUERY_THROW);
+ }
+ catch (const uno::Exception&)
+ {
+ return uno::Reference<excel::XRange>();
+ }
+
+ rtl::Reference<ScVbaRange> pRange = new ScVbaRange( excel::getUnoSheetModuleObj( xSheetRange ), xContext, xSheetRange );
+ return pRange->Range( Cell1, Cell2, true );
+}
+
+// Helper functions for AutoFilter
+static ScDBData* lcl_GetDBData_Impl( ScDocShell* pDocShell, sal_Int16 nSheet )
+{
+ ScDBData* pRet = nullptr;
+ if (pDocShell)
+ {
+ pRet = pDocShell->GetDocument().GetAnonymousDBData(nSheet);
+ }
+ return pRet;
+}
+
+static void lcl_SelectAll( ScDocShell* pDocShell, const ScQueryParam& aParam )
+{
+ if ( !pDocShell )
+ return;
+
+ ScViewData* pViewData = ScDocShell::GetViewData();
+ if ( !pViewData )
+ {
+ ScTabViewShell* pViewSh = pDocShell->GetBestViewShell( true );
+ pViewData = pViewSh ? &pViewSh->GetViewData() : nullptr;
+ }
+
+ if ( pViewData )
+ {
+ pViewData->GetView()->Query( aParam, nullptr, true );
+ }
+}
+
+static ScQueryParam lcl_GetQueryParam( ScDocShell* pDocShell, sal_Int16 nSheet )
+{
+ ScDBData* pDBData = lcl_GetDBData_Impl( pDocShell, nSheet );
+ ScQueryParam aParam;
+ if (pDBData)
+ {
+ pDBData->GetQueryParam( aParam );
+ }
+ return aParam;
+}
+
+static void lcl_SetAllQueryForField( ScDocShell* pDocShell, SCCOLROW nField, sal_Int16 nSheet )
+{
+ ScQueryParam aParam = lcl_GetQueryParam( pDocShell, nSheet );
+ aParam.RemoveEntryByField(nField);
+ lcl_SelectAll( pDocShell, aParam );
+}
+
+// Modifies sCriteria, and nOp depending on the value of sCriteria
+static void lcl_setTableFieldsFromCriteria( OUString& sCriteria1, const uno::Reference< beans::XPropertySet >& xDescProps, sheet::TableFilterField2& rFilterField )
+{
+ // #TODO make this more efficient and cycle through
+ // sCriteria1 character by character to pick up <,<>,=, * etc.
+ // right now I am more concerned with just getting it to work right
+
+ sCriteria1 = sCriteria1.trim();
+ // table of translation of criteria text to FilterOperators
+ // <>searchtext - NOT_EQUAL
+ // =searchtext - EQUAL
+ // *searchtext - startwith
+ // <>*searchtext - doesn't startwith
+ // *searchtext* - contains
+ // <>*searchtext* - doesn't contain
+ // [>|>=|<=|...]searchtext for GREATER_value, GREATER_EQUAL_value etc.
+ if ( sCriteria1.startsWith( EQUALS ) )
+ {
+ if ( o3tl::make_unsigned(sCriteria1.getLength()) == strlen(EQUALS) )
+ rFilterField.Operator = sheet::FilterOperator2::EMPTY;
+ else
+ {
+ rFilterField.Operator = sheet::FilterOperator2::EQUAL;
+ sCriteria1 = sCriteria1.copy( strlen(EQUALS) );
+ sCriteria1 = VBAToRegexp( sCriteria1 );
+ // UseRegularExpressions
+ if ( xDescProps.is() )
+ xDescProps->setPropertyValue( "UseRegularExpressions", uno::Any( true ) );
+ }
+
+ }
+ else if ( sCriteria1.startsWith( NOTEQUALS ) )
+ {
+ if ( o3tl::make_unsigned(sCriteria1.getLength()) == strlen(NOTEQUALS) )
+ rFilterField.Operator = sheet::FilterOperator2::NOT_EMPTY;
+ else
+ {
+ rFilterField.Operator = sheet::FilterOperator2::NOT_EQUAL;
+ sCriteria1 = sCriteria1.copy( strlen(NOTEQUALS) );
+ sCriteria1 = VBAToRegexp( sCriteria1 );
+ // UseRegularExpressions
+ if ( xDescProps.is() )
+ xDescProps->setPropertyValue( "UseRegularExpressions", uno::Any( true ) );
+ }
+ }
+ else if ( sCriteria1.startsWith( GREATERTHAN ) )
+ {
+ if ( sCriteria1.startsWith( GREATERTHANEQUALS ) )
+ {
+ sCriteria1 = sCriteria1.copy( strlen(GREATERTHANEQUALS) );
+ rFilterField.Operator = sheet::FilterOperator2::GREATER_EQUAL;
+ }
+ else
+ {
+ sCriteria1 = sCriteria1.copy( strlen(GREATERTHAN) );
+ rFilterField.Operator = sheet::FilterOperator2::GREATER;
+ }
+
+ }
+ else if ( sCriteria1.startsWith( LESSTHAN ) )
+ {
+ if ( sCriteria1.startsWith( LESSTHANEQUALS ) )
+ {
+ sCriteria1 = sCriteria1.copy( strlen(LESSTHANEQUALS) );
+ rFilterField.Operator = sheet::FilterOperator2::LESS_EQUAL;
+ }
+ else
+ {
+ sCriteria1 = sCriteria1.copy( strlen(LESSTHAN) );
+ rFilterField.Operator = sheet::FilterOperator2::LESS;
+ }
+
+ }
+ else
+ rFilterField.Operator = sheet::FilterOperator2::EQUAL;
+
+ // tdf#107885 - check if criteria is numeric using locale dependent settings without group separator
+ // or, if the decimal separator is different from the English locale, without any locale.
+ sal_Int32 nParseEnd = 0;
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ double fValue = ScGlobal::getLocaleData().stringToDouble( sCriteria1, false, &eStatus, &nParseEnd );
+ if ( nParseEnd == sCriteria1.getLength() && eStatus == rtl_math_ConversionStatus_Ok )
+ {
+ rFilterField.IsNumeric = true;
+ rFilterField.NumericValue = fValue;
+ }
+ else if ( ScGlobal::getLocaleData().getNumDecimalSep().toChar() != '.' )
+ {
+ eStatus = rtl_math_ConversionStatus_Ok;
+ fValue = ::rtl::math::stringToDouble( sCriteria1, '.', 0, &eStatus, &nParseEnd );
+ if ( nParseEnd == sCriteria1.getLength() && eStatus == rtl_math_ConversionStatus_Ok )
+ {
+ rFilterField.IsNumeric = true;
+ rFilterField.NumericValue = fValue;
+ }
+ }
+
+ rFilterField.StringValue = sCriteria1;
+}
+
+void SAL_CALL
+ScVbaRange::AutoFilter( const uno::Any& aField, const uno::Any& Criteria1, const uno::Any& Operator, const uno::Any& Criteria2, const uno::Any& /*VisibleDropDown*/ )
+{
+ // Is there an existing autofilter
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ sal_Int16 nSheet = thisAddress.Sheet;
+ ScDocShell* pShell = getScDocShell();
+ bool bHasAuto = false;
+ uno::Reference< sheet::XDatabaseRange > xDataBaseRange = excel::GetAutoFiltRange( pShell, nSheet );
+ if ( xDataBaseRange.is() )
+ bHasAuto = true;
+
+ if ( !bHasAuto )
+ {
+ if ( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException( STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY );
+
+ table::CellRangeAddress autoFiltAddress;
+ //CurrentRegion()
+ if ( isSingleCellRange() )
+ {
+ uno::Reference< excel::XRange > xCurrent( CurrentRegion() );
+ if ( xCurrent.is() )
+ {
+ ScVbaRange* pRange = getImplementation( xCurrent );
+ if ( pRange )
+ {
+ if ( pRange->isSingleCellRange() )
+ throw uno::RuntimeException("Can't create AutoFilter" );
+ RangeHelper currentRegion( pRange->mxRange );
+ autoFiltAddress = currentRegion.getCellRangeAddressable()->getRangeAddress();
+ }
+ }
+ }
+ else // multi-cell range
+ {
+ RangeHelper multiCellRange( mxRange );
+ autoFiltAddress = multiCellRange.getCellRangeAddressable()->getRangeAddress();
+ // #163530# Filter box shows only entry of first row
+ ScDocument* pDocument = ( pShell ? &pShell->GetDocument() : nullptr );
+ if ( pDocument )
+ {
+ SCCOL nStartCol = autoFiltAddress.StartColumn;
+ SCROW nStartRow = autoFiltAddress.StartRow;
+ SCCOL nEndCol = autoFiltAddress.EndColumn;
+ SCROW nEndRow = autoFiltAddress.EndRow;
+ pDocument->GetDataArea( autoFiltAddress.Sheet, nStartCol, nStartRow, nEndCol, nEndRow, true, true );
+ autoFiltAddress.StartColumn = nStartCol;
+ autoFiltAddress.StartRow = nStartRow;
+ autoFiltAddress.EndColumn = nEndCol;
+ autoFiltAddress.EndRow = nEndRow;
+ }
+ }
+
+ uno::Reference< sheet::XUnnamedDatabaseRanges > xDBRanges = excel::GetUnnamedDataBaseRanges( pShell );
+ if ( xDBRanges.is() )
+ {
+ if ( !xDBRanges->hasByTable( nSheet ) )
+ xDBRanges->setByTable( autoFiltAddress );
+ xDataBaseRange.set( xDBRanges->getByTable(nSheet ), uno::UNO_QUERY_THROW );
+ }
+ if ( !xDataBaseRange.is() )
+ throw uno::RuntimeException("Failed to find the autofilter placeholder range" );
+
+ uno::Reference< beans::XPropertySet > xDBRangeProps( xDataBaseRange, uno::UNO_QUERY_THROW );
+ // set autofilter
+ xDBRangeProps->setPropertyValue( "AutoFilter", uno::Any(true) );
+ // set header (autofilter always need column headers)
+ uno::Reference< beans::XPropertySet > xFiltProps( xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY_THROW );
+ xFiltProps->setPropertyValue( "ContainsHeader", uno::Any( true ) );
+ }
+
+ sal_Int32 nField = 0; // *IS* 1 based
+ sal_Int32 nOperator = excel::XlAutoFilterOperator::xlAnd;
+
+ sheet::FilterConnection nConn = sheet::FilterConnection_AND;
+ double nCriteria1 = 0;
+
+ bool bHasCritValue = Criteria1.hasValue();
+ bool bCritHasNumericValue = false; // not sure if a numeric criteria is possible
+ if ( bHasCritValue )
+ bCritHasNumericValue = ( Criteria1 >>= nCriteria1 );
+
+ if ( !aField.hasValue() && ( Criteria1.hasValue() || Operator.hasValue() || Criteria2.hasValue() ) )
+ throw uno::RuntimeException();
+ uno::Any Field( aField );
+ if ( !( Field >>= nField ) )
+ {
+ const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter( mxContext );
+ try
+ {
+ Field = xConverter->convertTo( aField, cppu::UnoType<sal_Int32>::get() );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ // Use the normal uno api, sometimes e.g. when you want to use ALL as the filter
+ // we can't use refresh as the uno interface doesn't have a concept of ALL
+ // in this case we just call the core calc functionality -
+ if ( Field >>= nField )
+ {
+ uno::Reference< sheet::XSheetFilterDescriptor2 > xDesc(
+ xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY );
+ if ( xDesc.is() )
+ {
+ OUString sCriteria1;
+ bool bAcceptCriteria2 = true;
+ bool bAll = false;
+ uno::Sequence< sheet::TableFilterField2 > sTabFilts;
+ sheet::TableFilterField2* pTabFilts = nullptr;
+ uno::Reference< beans::XPropertySet > xDescProps( xDesc, uno::UNO_QUERY_THROW );
+ if ( Criteria1.hasValue() )
+ {
+ sTabFilts.realloc( 1 );
+ pTabFilts = sTabFilts.getArray();
+ pTabFilts[0].Operator = sheet::FilterOperator2::EQUAL;// sensible default
+ if ( !bCritHasNumericValue )
+ {
+ Criteria1 >>= sCriteria1;
+ if ( sCriteria1.isEmpty() )
+ {
+ uno::Sequence< OUString > aCriteria1;
+ Criteria1 >>= aCriteria1;
+ sal_uInt16 nLength = aCriteria1.getLength();
+ if ( nLength )
+ {
+ // When sequence is provided for Criteria1 don't care about Criteria2
+ bAcceptCriteria2 = false;
+
+ auto pCriteria1 = aCriteria1.getArray();
+ sTabFilts.realloc( nLength );
+ pTabFilts = sTabFilts.getArray();
+ for ( sal_uInt16 i = 0; i < nLength; ++i )
+ {
+ lcl_setTableFieldsFromCriteria( pCriteria1[i], xDescProps, pTabFilts[i] );
+ pTabFilts[i].Connection = sheet::FilterConnection_OR;
+ pTabFilts[i].Field = (nField - 1);
+ }
+ }
+ else
+ bAll = true;
+ }
+ else
+ {
+ pTabFilts[0].IsNumeric = bCritHasNumericValue;
+ if ( bHasCritValue && !sCriteria1.isEmpty() )
+ lcl_setTableFieldsFromCriteria( sCriteria1, xDescProps, pTabFilts[0] );
+ else
+ bAll = true;
+ }
+ }
+ else // numeric
+ {
+ pTabFilts[0].IsNumeric = true;
+ pTabFilts[0].NumericValue = nCriteria1;
+ }
+ }
+ else // no value specified
+ bAll = true;
+ // not sure what the relationship between Criteria1 and Operator is,
+ // e.g. can you have an Operator without a Criteria? In LibreOffice it
+ if ( Operator.hasValue() && ( Operator >>= nOperator ) )
+ {
+ // if it's a bottom/top Ten(Percent/Value) and there
+ // is no value specified for criteria1 set it to 10
+ if ( !bCritHasNumericValue && sCriteria1.isEmpty() && ( nOperator != excel::XlAutoFilterOperator::xlOr ) && ( nOperator != excel::XlAutoFilterOperator::xlAnd ) )
+ {
+ pTabFilts[0].IsNumeric = true;
+ pTabFilts[0].NumericValue = 10;
+ bAll = false;
+ }
+ switch ( nOperator )
+ {
+ case excel::XlAutoFilterOperator::xlBottom10Items:
+ pTabFilts[0].Operator = sheet::FilterOperator2::BOTTOM_VALUES;
+ break;
+ case excel::XlAutoFilterOperator::xlBottom10Percent:
+ pTabFilts[0].Operator = sheet::FilterOperator2::BOTTOM_PERCENT;
+ break;
+ case excel::XlAutoFilterOperator::xlTop10Items:
+ pTabFilts[0].Operator = sheet::FilterOperator2::TOP_VALUES;
+ break;
+ case excel::XlAutoFilterOperator::xlTop10Percent:
+ pTabFilts[0].Operator = sheet::FilterOperator2::TOP_PERCENT;
+ break;
+ case excel::XlAutoFilterOperator::xlOr:
+ nConn = sheet::FilterConnection_OR;
+ break;
+ case excel::XlAutoFilterOperator::xlAnd:
+ nConn = sheet::FilterConnection_AND;
+ break;
+ default:
+ throw uno::RuntimeException("UnknownOption" );
+
+ }
+
+ }
+ if ( !bAll && bAcceptCriteria2 )
+ {
+ pTabFilts[0].Connection = sheet::FilterConnection_AND;
+ pTabFilts[0].Field = (nField - 1);
+
+ uno::Sequence< OUString > aCriteria2;
+ if ( Criteria2.hasValue() ) // there is a Criteria2
+ {
+ sTabFilts.realloc(2);
+ pTabFilts = sTabFilts.getArray();
+ pTabFilts[1].Field = sTabFilts[0].Field;
+ pTabFilts[1].Connection = nConn;
+
+ OUString sCriteria2;
+ if ( Criteria2 >>= sCriteria2 )
+ {
+ if ( !sCriteria2.isEmpty() )
+ {
+ uno::Reference< beans::XPropertySet > xProps;
+ lcl_setTableFieldsFromCriteria( sCriteria2, xProps, pTabFilts[1] );
+ pTabFilts[1].IsNumeric = false;
+ }
+ }
+ else if ( Criteria2 >>= aCriteria2 )
+ {
+ sal_uInt16 nLength = aCriteria2.getLength();
+ if ( nLength )
+ {
+ // For compatibility use only the last value from the sequence
+ lcl_setTableFieldsFromCriteria( aCriteria2.getArray()[nLength - 1], xDescProps, pTabFilts[1] );
+ }
+ }
+ else // numeric
+ {
+ Criteria2 >>= pTabFilts[1].NumericValue;
+ pTabFilts[1].IsNumeric = true;
+ pTabFilts[1].Operator = sheet::FilterOperator2::EQUAL;
+ }
+ }
+ }
+
+ xDesc->setFilterFields2( sTabFilts );
+ if ( !bAll )
+ {
+ xDataBaseRange->refresh();
+ }
+ else
+ // was 0 based now seems to be 1
+ lcl_SetAllQueryForField( pShell, nField, nSheet );
+ }
+ }
+ else
+ {
+ // this is just to toggle autofilter on and off ( not to be confused with
+ // a VisibleDropDown option combined with a field, in that case just the
+ // button should be disabled ) - currently we don't support that
+ uno::Reference< beans::XPropertySet > xDBRangeProps( xDataBaseRange, uno::UNO_QUERY_THROW );
+ if ( bHasAuto )
+ {
+ // find the any field with the query and select all
+ ScQueryParam aParam = lcl_GetQueryParam( pShell, nSheet );
+ for (SCSIZE i = 0; i< aParam.GetEntryCount(); ++i)
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if ( rEntry.bDoQuery )
+ lcl_SetAllQueryForField( pShell, rEntry.nField, nSheet );
+ }
+ // remove existing filters
+ uno::Reference< sheet::XSheetFilterDescriptor2 > xSheetFilterDescriptor(
+ xDataBaseRange->getFilterDescriptor(), uno::UNO_QUERY );
+ if( xSheetFilterDescriptor.is() )
+ xSheetFilterDescriptor->setFilterFields2( uno::Sequence< sheet::TableFilterField2 >() );
+ }
+ xDBRangeProps->setPropertyValue( "AutoFilter", uno::Any(!bHasAuto) );
+
+ }
+}
+
+void SAL_CALL
+ScVbaRange::Insert( const uno::Any& Shift, const uno::Any& /*CopyOrigin*/ )
+{
+ // It appears (from the web) that the undocumented CopyOrigin
+ // param should contain member of enum XlInsertFormatOrigin
+ // which can have values xlFormatFromLeftOrAbove or xlFormatFromRightOrBelow
+ // #TODO investigate resultant behaviour using these constants
+ // currently just processing Shift
+
+ sheet::CellInsertMode mode = sheet::CellInsertMode_NONE;
+ if ( Shift.hasValue() )
+ {
+ sal_Int32 nShift = 0;
+ Shift >>= nShift;
+ switch ( nShift )
+ {
+ case excel::XlInsertShiftDirection::xlShiftToRight:
+ mode = sheet::CellInsertMode_RIGHT;
+ break;
+ case excel::XlInsertShiftDirection::xlShiftDown:
+ mode = sheet::CellInsertMode_DOWN;
+ break;
+ default:
+ throw uno::RuntimeException("Illegal parameter " );
+ }
+ }
+ else
+ {
+ if ( getRow() >= getColumn() )
+ mode = sheet::CellInsertMode_DOWN;
+ else
+ mode = sheet::CellInsertMode_RIGHT;
+ }
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ uno::Reference< sheet::XCellRangeMovement > xCellRangeMove( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ xCellRangeMove->insertCells( thisAddress, mode );
+
+ // Paste from clipboard only if the clipboard content was copied via VBA, and not already pasted via VBA again.
+ // "Insert" behavior should not depend on random clipboard content previously copied by the user.
+ ScDocShell* pDocShell = getDocShellFromRange( mxRange );
+ const ScTransferObj* pClipObj = pDocShell ? ScTransferObj::GetOwnClipboard(pDocShell->GetClipData()) : nullptr;
+ if ( pClipObj && pClipObj->GetUseInApi() )
+ {
+ // After the insert ( this range ) actually has moved
+ ScRange aRange( static_cast< SCCOL >( thisAddress.StartColumn ), static_cast< SCROW >( thisAddress.StartRow ), static_cast< SCTAB >( thisAddress.Sheet ), static_cast< SCCOL >( thisAddress.EndColumn ), static_cast< SCROW >( thisAddress.EndRow ), static_cast< SCTAB >( thisAddress.Sheet ) );
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getDocShellFromRange( mxRange ) , aRange ) );
+ uno::Reference< excel::XRange > xVbaRange( new ScVbaRange( mxParent, mxContext, xRange, mbIsRows, mbIsColumns ) );
+ xVbaRange->PasteSpecial( uno::Any(), uno::Any(), uno::Any(), uno::Any() );
+ }
+}
+
+void SAL_CALL
+ScVbaRange::Autofit()
+{
+ sal_Int32 nLen = m_Areas->getCount();
+ if ( nLen > 1 )
+ {
+ for ( sal_Int32 index = 1; index != nLen; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( index ), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->Autofit();
+ }
+ return;
+ }
+
+ // if the range is a not a row or column range autofit will
+ // throw an error
+ if ( !( mbIsColumns || mbIsRows ) )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ ScDocShell* pDocShell = getDocShellFromRange( mxRange );
+ if ( !pDocShell )
+ return;
+
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+
+ std::vector<sc::ColRowSpan> aColArr(1, sc::ColRowSpan(thisAddress.StartColumn,thisAddress.EndColumn));
+ bool bDirection = true;
+ if ( mbIsRows )
+ {
+ bDirection = false;
+ aColArr[0].mnStart = thisAddress.StartRow;
+ aColArr[0].mnEnd = thisAddress.EndRow;
+ }
+ pDocShell->GetDocFunc().SetWidthOrHeight(
+ bDirection, aColArr, thisAddress.Sheet, SC_SIZE_OPTIMAL, 0, true, true);
+}
+
+uno::Any SAL_CALL
+ScVbaRange::Hyperlinks( const uno::Any& aIndex )
+{
+ /* The range object always returns a new Hyperlinks object containing a
+ fixed list of existing hyperlinks in the range.
+ See vbahyperlinks.hxx for more details. */
+
+ // get the global hyperlink object of the sheet (sheet should always be the parent of a Range object)
+ uno::Reference< excel::XWorksheet > xWorksheet( getParent(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XHyperlinks > xSheetHlinks( xWorksheet->Hyperlinks( uno::Any() ), uno::UNO_QUERY_THROW );
+ ScVbaHyperlinksRef xScSheetHlinks( dynamic_cast< ScVbaHyperlinks* >( xSheetHlinks.get() ) );
+ if( !xScSheetHlinks.is() )
+ throw uno::RuntimeException("Cannot obtain hyperlinks implementation object" );
+
+ // create a new local hyperlinks object based on the sheet hyperlinks
+ ScVbaHyperlinksRef xHlinks( new ScVbaHyperlinks( getParent(), mxContext, xScSheetHlinks, getScRangeList() ) );
+ if( aIndex.hasValue() )
+ return xHlinks->Item( aIndex, uno::Any() );
+ return uno::Any( uno::Reference< excel::XHyperlinks >( xHlinks ) );
+}
+
+css::uno::Reference< excel::XValidation > SAL_CALL
+ScVbaRange::getValidation()
+{
+ if ( !m_xValidation.is() )
+ m_xValidation = new ScVbaValidation( this, mxContext, mxRange );
+ return m_xValidation;
+}
+
+namespace {
+
+/// @throws uno::RuntimeException
+sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCell >& rxCell )
+{
+ /* TODO/FIXME: We need an apostroph-prefix property at the cell to
+ implement this correctly. For now, return an apostroph for every text
+ cell.
+
+ TODO/FIXME: When Application.TransitionNavigKeys is supported and true,
+ this function needs to inspect the cell formatting and return different
+ prefixes according to the horizontal cell alignment.
+ */
+ return (rxCell->getType() == table::CellContentType_TEXT) ? '\'' : 0;
+}
+
+/// @throws uno::RuntimeException
+sal_Unicode lclGetPrefixChar( const uno::Reference< table::XCellRange >& rxRange )
+{
+ /* This implementation is able to handle different prefixes (needed if
+ Application.TransitionNavigKeys is true). The function lclGetPrefixChar
+ for single cells called from here may return any prefix. If that
+ function returns an empty prefix (NUL character) or different non-empty
+ prefixes for two cells, this function returns 0.
+ */
+ sal_Unicode cCurrPrefix = 0;
+ table::CellRangeAddress aRangeAddr = lclGetRangeAddress( rxRange );
+ sal_Int32 nEndCol = aRangeAddr.EndColumn - aRangeAddr.StartColumn;
+ sal_Int32 nEndRow = aRangeAddr.EndRow - aRangeAddr.StartRow;
+ for( sal_Int32 nRow = 0; nRow <= nEndRow; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol <= nEndCol; ++nCol )
+ {
+ uno::Reference< table::XCell > xCell( rxRange->getCellByPosition( nCol, nRow ), uno::UNO_SET_THROW );
+ sal_Unicode cNewPrefix = lclGetPrefixChar( xCell );
+ if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) )
+ return 0;
+ cCurrPrefix = cNewPrefix;
+ }
+ }
+ // all cells contain the same prefix - return it
+ return cCurrPrefix;
+}
+
+/// @throws uno::RuntimeException
+sal_Unicode lclGetPrefixChar( const uno::Reference< sheet::XSheetCellRangeContainer >& rxRanges )
+{
+ sal_Unicode cCurrPrefix = 0;
+ uno::Reference< container::XEnumerationAccess > xRangesEA( rxRanges, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XEnumeration > xRangesEnum( xRangesEA->createEnumeration(), uno::UNO_SET_THROW );
+ while( xRangesEnum->hasMoreElements() )
+ {
+ uno::Reference< table::XCellRange > xRange( xRangesEnum->nextElement(), uno::UNO_QUERY_THROW );
+ sal_Unicode cNewPrefix = lclGetPrefixChar( xRange );
+ if( (cNewPrefix == 0) || ((cCurrPrefix != 0) && (cNewPrefix != cCurrPrefix)) )
+ return 0;
+ cCurrPrefix = cNewPrefix;
+ }
+ // all ranges contain the same prefix - return it
+ return cCurrPrefix;
+}
+
+uno::Any lclGetPrefixVariant( sal_Unicode cPrefixChar )
+{
+ return uno::Any( (cPrefixChar == 0) ? OUString() : OUString( cPrefixChar ) );
+}
+
+} // namespace
+
+uno::Any SAL_CALL ScVbaRange::getPrefixCharacter()
+{
+ /* (1) If Application.TransitionNavigKeys is false, this function returns
+ an apostroph character if the text cell begins with an apostroph
+ character (formula return values are not taken into account); otherwise
+ an empty string.
+
+ (2) If Application.TransitionNavigKeys is true, this function returns
+ an apostroph character, if the cell is left-aligned; a double-quote
+ character, if the cell is right-aligned; a circumflex character, if the
+ cell is centered; a backslash character, if the cell is set to filled;
+ or an empty string, if nothing of the above.
+
+ If a range or a list of ranges contains texts with leading apostroph
+ character as well as other cells, this function returns an empty
+ string.
+ */
+
+ if( mxRange.is() )
+ return lclGetPrefixVariant( lclGetPrefixChar( mxRange ) );
+ if( mxRanges.is() )
+ return lclGetPrefixVariant( lclGetPrefixChar( mxRanges ) );
+ throw uno::RuntimeException("Unexpected empty Range object" );
+}
+
+uno::Any ScVbaRange::getShowDetail()
+{
+ // #FIXME, If the specified range is in a PivotTable report
+
+ // In MSO VBA, the specified range must be a single summary column or row in an outline. otherwise throw exception
+ if( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException("Can not get Range.ShowDetail attribute " );
+
+ RangeHelper helper( mxRange );
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = helper.getSheetCellCursor();
+ xSheetCellCursor->collapseToCurrentRegion();
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
+ table::CellRangeAddress aOutlineAddress = xCellRangeAddressable->getRangeAddress();
+
+ // check if the specified range is a single summary column or row.
+ table::CellRangeAddress thisAddress = helper.getCellRangeAddressable()->getRangeAddress();
+ if( (thisAddress.StartRow != thisAddress.EndRow || thisAddress.EndRow != aOutlineAddress.EndRow ) &&
+ (thisAddress.StartColumn != thisAddress.EndColumn || thisAddress.EndColumn != aOutlineAddress.EndColumn ))
+ {
+ throw uno::RuntimeException("Can not set Range.ShowDetail attribute" );
+ }
+
+ bool bColumn = thisAddress.StartRow != thisAddress.EndRow;
+ ScDocument& rDoc = getDocumentFromRange( mxRange );
+ ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable(static_cast<SCTAB>(thisAddress.Sheet), true);
+ const ScOutlineArray& rOutlineArray = bColumn ? pOutlineTable->GetColArray(): pOutlineTable->GetRowArray();
+ SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(thisAddress.EndColumn-1):static_cast<SCCOLROW>(thisAddress.EndRow-1);
+ const ScOutlineEntry* pEntry = rOutlineArray.GetEntryByPos( 0, nPos );
+ if( pEntry )
+ {
+ const bool bShowDetail = !pEntry->IsHidden();
+ return uno::Any( bShowDetail );
+ }
+
+ return aNULL();
+}
+
+void ScVbaRange::setShowDetail(const uno::Any& aShowDetail)
+{
+ // #FIXME, If the specified range is in a PivotTable report
+
+ // In MSO VBA, the specified range must be a single summary column or row in an outline. otherwise throw exception
+ if( m_Areas->getCount() > 1 )
+ throw uno::RuntimeException("Can not set Range.ShowDetail attribute" );
+
+ bool bShowDetail = extractBoolFromAny( aShowDetail );
+
+ RangeHelper helper( mxRange );
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = helper.getSheetCellCursor();
+ xSheetCellCursor->collapseToCurrentRegion();
+ uno::Reference< sheet::XCellRangeAddressable > xCellRangeAddressable(xSheetCellCursor, uno::UNO_QUERY_THROW);
+ table::CellRangeAddress aOutlineAddress = xCellRangeAddressable->getRangeAddress();
+
+ // check if the specified range is a single summary column or row.
+ table::CellRangeAddress thisAddress = helper.getCellRangeAddressable()->getRangeAddress();
+ if( (thisAddress.StartRow != thisAddress.EndRow || thisAddress.EndRow != aOutlineAddress.EndRow ) &&
+ (thisAddress.StartColumn != thisAddress.EndColumn || thisAddress.EndColumn != aOutlineAddress.EndColumn ))
+ {
+ throw uno::RuntimeException("Can not set Range.ShowDetail attribute" );
+ }
+
+ // #FIXME, seems there is a different behavior between MSO and OOo.
+ // In OOo, the showDetail will show all the level entries, while only show the first level entry in MSO
+ uno::Reference< sheet::XSheetOutline > xSheetOutline( helper.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ if( bShowDetail )
+ xSheetOutline->showDetail( aOutlineAddress );
+ else
+ xSheetOutline->hideDetail( aOutlineAddress );
+
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::MergeArea()
+{
+ uno::Reference< sheet::XSheetCellRange > xMergeShellCellRange(mxRange->getCellRangeByPosition(0,0,0,0), uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XSheetCellCursor > xMergeSheetCursor(xMergeShellCellRange->getSpreadsheet()->createCursorByRange( xMergeShellCellRange ), uno::UNO_SET_THROW);
+ if( xMergeSheetCursor.is() )
+ {
+ xMergeSheetCursor->collapseToMergedArea();
+ uno::Reference<sheet::XCellRangeAddressable> xMergeCellAddress(xMergeSheetCursor, uno::UNO_QUERY_THROW);
+ table::CellRangeAddress aCellAddress = xMergeCellAddress->getRangeAddress();
+ if( aCellAddress.StartColumn ==0 && aCellAddress.EndColumn==0 &&
+ aCellAddress.StartRow==0 && aCellAddress.EndRow==0)
+ {
+ return new ScVbaRange( mxParent,mxContext,mxRange );
+ }
+ else
+ {
+ ScRange refRange( static_cast< SCCOL >( aCellAddress.StartColumn ), static_cast< SCROW >( aCellAddress.StartRow ), static_cast< SCTAB >( aCellAddress.Sheet ),
+ static_cast< SCCOL >( aCellAddress.EndColumn ), static_cast< SCROW >( aCellAddress.EndRow ), static_cast< SCTAB >( aCellAddress.Sheet ) );
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell() , refRange ) );
+ return new ScVbaRange( mxParent, mxContext,xRange );
+ }
+ }
+ return new ScVbaRange( mxParent, mxContext, mxRange );
+}
+
+void SAL_CALL
+ScVbaRange::PrintOut( const uno::Any& From, const uno::Any& To, const uno::Any& Copies, const uno::Any& Preview, const uno::Any& ActivePrinter, const uno::Any& PrintToFile, const uno::Any& Collate, const uno::Any& PrToFileName )
+{
+ ScDocShell* pShell = nullptr;
+
+ sal_Int32 nItems = m_Areas->getCount();
+ uno::Sequence< table::CellRangeAddress > printAreas( nItems );
+ auto printAreasRange = asNonConstRange(printAreas);
+ uno::Reference< sheet::XPrintAreas > xPrintAreas;
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+
+ RangeHelper thisRange( xRange->getCellRange() );
+ table::CellRangeAddress rangeAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ if ( index == 1 )
+ {
+ ScVbaRange* pRange = getImplementation( xRange );
+ // initialise the doc shell and the printareas
+ pShell = getDocShellFromRange( pRange->mxRange );
+ xPrintAreas.set( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ }
+ printAreasRange[ index - 1 ] = rangeAddress;
+ }
+ if ( pShell && xPrintAreas.is() )
+ {
+ xPrintAreas->setPrintAreas( printAreas );
+ uno::Reference< frame::XModel > xModel = pShell->GetModel();
+ PrintOutHelper( excel::getBestViewShell( xModel ), From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName, true );
+ }
+}
+
+void SAL_CALL
+ScVbaRange::AutoFill( const uno::Reference< excel::XRange >& Destination, const uno::Any& Type )
+{
+ uno::Reference< excel::XRange > xDest( Destination, uno::UNO_SET_THROW );
+ ScVbaRange* pRange = getImplementation( xDest );
+ RangeHelper destRangeHelper( pRange->mxRange );
+ table::CellRangeAddress destAddress = destRangeHelper.getCellRangeAddressable()->getRangeAddress();
+
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ ScRange sourceRange;
+ ScRange destRange;
+
+ ScUnoConversion::FillScRange( destRange, destAddress );
+ ScUnoConversion::FillScRange( sourceRange, thisAddress );
+
+ FillDir eDir = FILL_TO_BOTTOM;
+ double fStep = 1.0;
+
+ ScRange aRange( destRange );
+ ScRange aSourceRange( destRange );
+
+ // default to include the number of Rows in the source range;
+ SCCOLROW nSourceCount = ( sourceRange.aEnd.Row() - sourceRange.aStart.Row() ) + 1;
+ SCCOLROW nCount = 0;
+
+ if ( sourceRange != destRange )
+ {
+ // Find direction of fill, vertical or horizontal
+ if ( sourceRange.aStart == destRange.aStart )
+ {
+ if ( sourceRange.aEnd.Row() == destRange.aEnd.Row() )
+ {
+ nSourceCount = ( sourceRange.aEnd.Col() - sourceRange.aStart.Col() + 1 );
+ aSourceRange.aEnd.SetCol( static_cast<SCCOL>( aSourceRange.aStart.Col() + nSourceCount - 1 ) );
+ eDir = FILL_TO_RIGHT;
+ nCount = aRange.aEnd.Col() - aSourceRange.aEnd.Col();
+ }
+ else if ( sourceRange.aEnd.Col() == destRange.aEnd.Col() )
+ {
+ aSourceRange.aEnd.SetRow( static_cast<SCROW>( aSourceRange.aStart.Row() + nSourceCount ) - 1 );
+ nCount = aRange.aEnd.Row() - aSourceRange.aEnd.Row();
+ eDir = FILL_TO_BOTTOM;
+ }
+ }
+
+ else if ( aSourceRange.aEnd == destRange.aEnd )
+ {
+ if ( sourceRange.aStart.Col() == destRange.aStart.Col() )
+ {
+ aSourceRange.aStart.SetRow( static_cast<SCROW>( aSourceRange.aEnd.Row() - nSourceCount + 1 ) );
+ nCount = aSourceRange.aStart.Row() - aRange.aStart.Row();
+ eDir = FILL_TO_TOP;
+ fStep = -fStep;
+ }
+ else if ( sourceRange.aStart.Row() == destRange.aStart.Row() )
+ {
+ nSourceCount = ( sourceRange.aEnd.Col() - sourceRange.aStart.Col() ) + 1;
+ aSourceRange.aStart.SetCol( static_cast<SCCOL>( aSourceRange.aEnd.Col() - nSourceCount + 1 ) );
+ nCount = aSourceRange.aStart.Col() - aRange.aStart.Col();
+ eDir = FILL_TO_LEFT;
+ fStep = -fStep;
+ }
+ }
+ }
+
+ FillCmd eCmd = FILL_AUTO;
+ FillDateCmd eDateCmd = FILL_DAY;
+
+ if ( Type.hasValue() )
+ {
+ sal_Int16 nFillType = excel::XlAutoFillType::xlFillDefault;
+ Type >>= nFillType;
+ switch ( nFillType )
+ {
+ case excel::XlAutoFillType::xlFillCopy:
+ eCmd = FILL_SIMPLE;
+ fStep = 0.0;
+ break;
+ case excel::XlAutoFillType::xlFillDays:
+ eCmd = FILL_DATE;
+ break;
+ case excel::XlAutoFillType::xlFillMonths:
+ eCmd = FILL_DATE;
+ eDateCmd = FILL_MONTH;
+ break;
+ case excel::XlAutoFillType::xlFillWeekdays:
+ eCmd = FILL_DATE;
+ eDateCmd = FILL_WEEKDAY;
+ break;
+ case excel::XlAutoFillType::xlFillYears:
+ eCmd = FILL_DATE;
+ eDateCmd = FILL_YEAR;
+ break;
+ case excel::XlAutoFillType::xlGrowthTrend:
+ eCmd = FILL_GROWTH;
+ break;
+ case excel::XlAutoFillType::xlFillFormats:
+ throw uno::RuntimeException("xlFillFormat not supported for AutoFill" );
+ case excel::XlAutoFillType::xlFillValues:
+ case excel::XlAutoFillType::xlFillSeries:
+ case excel::XlAutoFillType::xlLinearTrend:
+ eCmd = FILL_LINEAR;
+ break;
+ case excel::XlAutoFillType::xlFillDefault:
+ default:
+ eCmd = FILL_AUTO;
+ break;
+ }
+ }
+ ScDocShell* pDocSh = getDocShellFromRange( mxRange );
+ pDocSh->GetDocFunc().FillAuto( aSourceRange, nullptr, eDir, eCmd, eDateCmd,
+ nCount, fStep, MAXDOUBLE/*fEndValue*/, true, true );
+}
+sal_Bool SAL_CALL
+ScVbaRange::GoalSeek( const uno::Any& Goal, const uno::Reference< excel::XRange >& ChangingCell )
+{
+ ScDocShell* pDocShell = getScDocShell();
+ bool bRes = true;
+ ScVbaRange* pRange = static_cast< ScVbaRange* >( ChangingCell.get() );
+ if ( pDocShell && pRange )
+ {
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ RangeHelper changingCellRange( pRange->mxRange );
+ table::CellRangeAddress changingCellAddr = changingCellRange.getCellRangeAddressable()->getRangeAddress();
+ OUString sGoal = getAnyAsString( Goal );
+ table::CellAddress thisCell( thisAddress.Sheet, thisAddress.StartColumn, thisAddress.StartRow );
+ table::CellAddress changingCell( changingCellAddr.Sheet, changingCellAddr.StartColumn, changingCellAddr.StartRow );
+ sheet::GoalResult res = pDocShell->GetModel()->seekGoal( thisCell, changingCell, sGoal );
+ ChangingCell->setValue( uno::Any( res.Result ) );
+
+ // openoffice behaves differently, result is 0 if the divergence is too great
+ // but... if it detects 0 is the value it requires then it will use that
+ // e.g. divergence & result both = 0.0 does NOT mean there is an error
+ if ( ( res.Divergence != 0.0 ) && ( res.Result == 0.0 ) )
+ bRes = false;
+ }
+ else
+ bRes = false;
+ return bRes;
+}
+
+void
+ScVbaRange::Calculate( )
+{
+ getWorksheet()->Calculate();
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::Item( const uno::Any& row, const uno::Any& column )
+{
+ if ( mbIsRows || mbIsColumns )
+ {
+ if ( column.hasValue() )
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
+ uno::Reference< excel::XRange > xRange;
+ if ( mbIsColumns )
+ xRange = Columns( row );
+ else
+ xRange = Rows( row );
+ return xRange;
+ }
+ return Cells( row, column );
+}
+
+void
+ScVbaRange::AutoOutline( )
+{
+ // #TODO #FIXME needs to check for summary row/col ( whatever they are )
+ // not valid for multi Area Addresses
+ if ( m_Areas->getCount() > 1 )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY);
+ // So needs to either span an entire Row or a just be a single cell
+ // ( that contains a summary RowColumn )
+ // also the Single cell cause doesn't seem to be handled specially in
+ // this code ( ported from the helperapi RangeImpl.java,
+ // RangeRowsImpl.java, RangesImpl.java, RangeSingleCellImpl.java
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+
+ if ( isSingleCellRange() || mbIsRows )
+ {
+ uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ xSheetOutline->autoOutline( thisAddress );
+ }
+ else
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+}
+
+void SAL_CALL
+ScVbaRange:: ClearOutline( )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->ClearOutline();
+ }
+ return;
+ }
+ RangeHelper thisRange( mxRange );
+ uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ xSheetOutline->clearOutline();
+}
+
+void
+ScVbaRange::groupUnGroup( bool bUnGroup )
+{
+ if ( m_Areas->getCount() > 1 )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, STR_ERRORMESSAGE_APPLIESTOSINGLERANGEONLY);
+ table::TableOrientation nOrient = table::TableOrientation_ROWS;
+ if ( mbIsColumns )
+ nOrient = table::TableOrientation_COLUMNS;
+ RangeHelper thisRange( mxRange );
+ table::CellRangeAddress thisAddress = thisRange.getCellRangeAddressable()->getRangeAddress();
+ uno::Reference< sheet::XSheetOutline > xSheetOutline( thisRange.getSpreadSheet(), uno::UNO_QUERY_THROW );
+ if ( bUnGroup )
+ xSheetOutline->ungroup( thisAddress, nOrient );
+ else
+ xSheetOutline->group( thisAddress, nOrient );
+}
+
+void SAL_CALL
+ScVbaRange::Group( )
+{
+ groupUnGroup(false);
+}
+void SAL_CALL
+ScVbaRange::Ungroup( )
+{
+ groupUnGroup(true);
+}
+
+/// @throws uno::RuntimeException
+static void lcl_mergeCellsOfRange( const uno::Reference< table::XCellRange >& xCellRange, bool _bMerge )
+{
+ uno::Reference< util::XMergeable > xMergeable( xCellRange, uno::UNO_QUERY_THROW );
+ xMergeable->merge(_bMerge);
+}
+void SAL_CALL
+ScVbaRange::Merge( const uno::Any& Across )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->Merge(Across);
+ }
+ return;
+ }
+ bool bAcross = false;
+ Across >>= bAcross;
+ if ( !bAcross )
+ lcl_mergeCellsOfRange( mxRange, true );
+ else
+ {
+ uno::Reference< excel::XRange > oRangeRowsImpl = Rows( uno::Any() );
+ // #TODO #FIXME this seems incredibly lame, this can't be right
+ for (sal_Int32 i=1; i <= oRangeRowsImpl->getCount();i++)
+ {
+ oRangeRowsImpl->Cells( uno::Any( i ), uno::Any() )->Merge( uno::Any( false ) );
+ }
+ }
+}
+
+void SAL_CALL
+ScVbaRange::UnMerge( )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ sal_Int32 nItems = m_Areas->getCount();
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->UnMerge();
+ }
+ return;
+ }
+ lcl_mergeCellsOfRange( mxRange, false);
+}
+
+uno::Any SAL_CALL
+ScVbaRange::getStyle()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->getStyle();
+ }
+ uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
+ OUString sStyleName;
+ xProps->getPropertyValue( CELLSTYLE ) >>= sStyleName;
+ ScDocShell* pShell = getScDocShell();
+ uno::Reference< frame::XModel > xModel( pShell->GetModel() );
+ uno::Reference< excel::XStyle > xStyle = new ScVbaStyle( this, mxContext, sStyleName, xModel );
+ return uno::Any( xStyle );
+}
+void SAL_CALL
+ScVbaRange::setStyle( const uno::Any& _style )
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange->setStyle( _style );
+ return;
+ }
+ uno::Reference< beans::XPropertySet > xProps( mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XStyle > xStyle;
+ _style >>= xStyle;
+ if ( xStyle.is() )
+ xProps->setPropertyValue( CELLSTYLE, uno::Any( xStyle->getName() ) );
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::PreviousNext( bool bIsPrevious )
+{
+ ScMarkData markedRange(getScDocument().GetSheetLimits());
+ ScRange refRange;
+ RangeHelper thisRange( mxRange );
+
+ ScUnoConversion::FillScRange( refRange, thisRange.getCellRangeAddressable()->getRangeAddress());
+ markedRange. SetMarkArea( refRange );
+ short nMove = bIsPrevious ? -1 : 1;
+
+ SCCOL nNewX = refRange.aStart.Col();
+ SCROW nNewY = refRange.aStart.Row();
+ SCTAB nTab = refRange.aStart.Tab();
+
+ ScDocument& rDoc = getScDocument();
+ rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, true,true, markedRange );
+ refRange.aStart.SetCol( nNewX );
+ refRange.aStart.SetRow( nNewY );
+ refRange.aStart.SetTab( nTab );
+ refRange.aEnd.SetCol( nNewX );
+ refRange.aEnd.SetRow( nNewY );
+ refRange.aEnd.SetTab( nTab );
+
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell() , refRange ) );
+
+ return new ScVbaRange( mxParent, mxContext, xRange );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::Next()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ) , uno::UNO_QUERY_THROW );
+ return xRange->Next();
+ }
+ return PreviousNext( false );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::Previous()
+{
+ if ( m_Areas->getCount() > 1 )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any( sal_Int32( 1 ) ), uno::Any() ), uno::UNO_QUERY_THROW );
+ return xRange->Previous();
+ }
+ return PreviousNext( true );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaRange::SpecialCells( const uno::Any& _oType, const uno::Any& _oValue)
+{
+ bool bIsSingleCell = isSingleCellRange();
+ bool bIsMultiArea = ( m_Areas->getCount() > 1 );
+ ScVbaRange* pRangeToUse = this;
+ uno::Reference< excel::XRange > xUsedRange( getWorksheet()->getUsedRange() );
+ sal_Int32 nType = 0;
+ if ( !( _oType >>= nType ) )
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
+ switch(nType)
+ {
+ case excel::XlCellType::xlCellTypeSameFormatConditions:
+ case excel::XlCellType::xlCellTypeAllValidation:
+ case excel::XlCellType::xlCellTypeSameValidation:
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ break;
+ case excel::XlCellType::xlCellTypeBlanks:
+ case excel::XlCellType::xlCellTypeComments:
+ case excel::XlCellType::xlCellTypeConstants:
+ case excel::XlCellType::xlCellTypeFormulas:
+ case excel::XlCellType::xlCellTypeVisible:
+ case excel::XlCellType::xlCellTypeLastCell:
+ {
+ if ( bIsMultiArea )
+ {
+ // need to process each area, gather the results and
+ // create a new range from those
+ std::vector< table::CellRangeAddress > rangeResults;
+ sal_Int32 nItems = m_Areas->getCount() + 1;
+ for ( sal_Int32 index=1; index <= nItems; ++index )
+ {
+ uno::Reference< excel::XRange > xRange( m_Areas->Item( uno::Any(index), uno::Any() ), uno::UNO_QUERY_THROW );
+ xRange = xRange->SpecialCells( _oType, _oValue);
+ ScVbaRange* pRange = getImplementation( xRange );
+ if ( xRange.is() && pRange )
+ {
+ sal_Int32 nElems = pRange->m_Areas->getCount() + 1;
+ for ( sal_Int32 nArea = 1; nArea < nElems; ++nArea )
+ {
+ uno::Reference< excel::XRange > xTmpRange( m_Areas->Item( uno::Any( nArea ), uno::Any() ), uno::UNO_QUERY_THROW );
+ RangeHelper rHelper( xTmpRange->getCellRange() );
+ rangeResults.push_back( rHelper.getCellRangeAddressable()->getRangeAddress() );
+ }
+ }
+ }
+ ScRangeList aCellRanges;
+ for ( const auto& rRangeResult : rangeResults )
+ {
+ ScRange refRange;
+ ScUnoConversion::FillScRange( refRange, rRangeResult );
+ aCellRanges.push_back( refRange );
+ }
+ // Single range
+ if ( aCellRanges.size() == 1 )
+ {
+ uno::Reference< table::XCellRange > xRange( new ScCellRangeObj( getScDocShell(), aCellRanges.front() ) );
+ return new ScVbaRange( mxParent, mxContext, xRange );
+ }
+ uno::Reference< sheet::XSheetCellRangeContainer > xRanges( new ScCellRangesObj( getScDocShell(), aCellRanges ) );
+
+ return new ScVbaRange( mxParent, mxContext, xRanges );
+ }
+ else if ( bIsSingleCell )
+ {
+ pRangeToUse = static_cast< ScVbaRange* >( xUsedRange.get() );
+ }
+
+ break;
+ }
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
+ break;
+ }
+ if ( !pRangeToUse )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ return pRangeToUse->SpecialCellsImpl( nType, _oValue );
+}
+
+static sal_Int32 getContentResultFlags(const uno::Any& aValue)
+{
+ if (sal_Int32 aType; aValue >>= aType)
+ {
+ switch (aType)
+ {
+ case excel::XlSpecialCellsValue::xlNumbers:
+ return sheet::CellFlags::VALUE | sheet::CellFlags::DATETIME;
+ case excel::XlSpecialCellsValue::xlTextValues:
+ return sheet::CellFlags::STRING;
+ case excel::XlSpecialCellsValue::xlLogical:
+ return sheet::CellFlags::VALUE | sheet::CellFlags::DATETIME;
+ case excel::XlSpecialCellsValue::xlErrors:
+ return 0;
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {});
+ }
+ }
+ return sheet::CellFlags::VALUE | sheet::CellFlags::STRING | sheet::CellFlags::DATETIME;
+}
+
+/// @throws script::BasicErrorException
+static sal_Int32 lcl_getFormulaResultFlags(const uno::Any& aType)
+{
+ sal_Int32 nType = excel::XlSpecialCellsValue::xlNumbers;
+ aType >>= nType;
+ sal_Int32 nRes = sheet::FormulaResult::VALUE;
+
+ switch(nType)
+ {
+ case excel::XlSpecialCellsValue::xlErrors:
+ nRes= sheet::FormulaResult::ERROR;
+ break;
+ case excel::XlSpecialCellsValue::xlLogical:
+ //TODO bc93774: ask NN if this is really an appropriate substitute
+ nRes = sheet::FormulaResult::VALUE;
+ break;
+ case excel::XlSpecialCellsValue::xlNumbers:
+ nRes = sheet::FormulaResult::VALUE;
+ break;
+ case excel::XlSpecialCellsValue::xlTextValues:
+ nRes = sheet::FormulaResult::STRING;
+ break;
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
+ }
+ return nRes;
+}
+
+uno::Reference< excel::XRange >
+ScVbaRange::SpecialCellsImpl( sal_Int32 nType, const uno::Any& _oValue)
+{
+ uno::Reference< excel::XRange > xRange;
+ try
+ {
+ uno::Reference< sheet::XCellRangesQuery > xQuery( mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSheetCellRanges > xLocSheetCellRanges;
+ switch(nType)
+ {
+ case excel::XlCellType::xlCellTypeAllFormatConditions:
+ case excel::XlCellType::xlCellTypeSameFormatConditions:
+ case excel::XlCellType::xlCellTypeAllValidation:
+ case excel::XlCellType::xlCellTypeSameValidation:
+ // Shouldn't get here ( should be filtered out by
+ // ScVbaRange::SpecialCells()
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ break;
+ case excel::XlCellType::xlCellTypeBlanks:
+ xLocSheetCellRanges = xQuery->queryEmptyCells();
+ break;
+ case excel::XlCellType::xlCellTypeComments:
+ xLocSheetCellRanges = xQuery->queryContentCells(sheet::CellFlags::ANNOTATION);
+ break;
+ case excel::XlCellType::xlCellTypeConstants:
+ xLocSheetCellRanges = xQuery->queryContentCells(getContentResultFlags(_oValue));
+ break;
+ case excel::XlCellType::xlCellTypeFormulas:
+ {
+ sal_Int32 nFormulaResult = lcl_getFormulaResultFlags(_oValue);
+ xLocSheetCellRanges = xQuery->queryFormulaCells(nFormulaResult);
+ break;
+ }
+ case excel::XlCellType::xlCellTypeLastCell:
+ xRange = Cells( uno::Any( getCount() ), uno::Any() );
+ [[fallthrough]]; //TODO ???
+ case excel::XlCellType::xlCellTypeVisible:
+ xLocSheetCellRanges = xQuery->queryVisibleCells();
+ break;
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {} );
+ break;
+ }
+ if (xLocSheetCellRanges.is())
+ {
+ xRange = lcl_makeXRangeFromSheetCellRanges( getParent(), mxContext, xLocSheetCellRanges, getScDocShell() );
+ }
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, u"No cells were found");
+ }
+ return xRange;
+}
+
+void SAL_CALL
+ScVbaRange::RemoveSubtotal( )
+{
+ uno::Reference< sheet::XSubTotalCalculatable > xSub( mxRange, uno::UNO_QUERY_THROW );
+ xSub->removeSubTotals();
+}
+
+void SAL_CALL
+ScVbaRange::Subtotal( ::sal_Int32 _nGroupBy, ::sal_Int32 _nFunction, const uno::Sequence< ::sal_Int32 >& _nTotalList, const uno::Any& aReplace, const uno::Any& PageBreaks, const uno::Any& /*SummaryBelowData*/ )
+{
+ try
+ {
+ bool bDoReplace = false;
+ aReplace >>= bDoReplace;
+ bool bAddPageBreaks = false;
+ PageBreaks >>= bAddPageBreaks;
+
+ uno::Reference< sheet::XSubTotalCalculatable> xSub(mxRange, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSubTotalDescriptor > xSubDesc = xSub->createSubTotalDescriptor(true);
+ uno::Reference< beans::XPropertySet > xSubDescPropertySet( xSubDesc, uno::UNO_QUERY_THROW );
+ xSubDescPropertySet->setPropertyValue("InsertPageBreaks", uno::Any( bAddPageBreaks));
+ sal_Int32 nLen = _nTotalList.getLength();
+ uno::Sequence< sheet::SubTotalColumn > aColumns( nLen );
+ auto aColumnsRange = asNonConstRange(aColumns);
+ for (int i = 0; i < nLen; i++)
+ {
+ aColumnsRange[i].Column = _nTotalList[i] - 1;
+ switch (_nFunction)
+ {
+ case excel::XlConsolidationFunction::xlAverage:
+ aColumnsRange[i].Function = sheet::GeneralFunction_AVERAGE;
+ break;
+ case excel::XlConsolidationFunction::xlCount:
+ aColumnsRange[i].Function = sheet::GeneralFunction_COUNT;
+ break;
+ case excel::XlConsolidationFunction::xlCountNums:
+ aColumnsRange[i].Function = sheet::GeneralFunction_COUNTNUMS;
+ break;
+ case excel::XlConsolidationFunction::xlMax:
+ aColumnsRange[i].Function = sheet::GeneralFunction_MAX;
+ break;
+ case excel::XlConsolidationFunction::xlMin:
+ aColumnsRange[i].Function = sheet::GeneralFunction_MIN;
+ break;
+ case excel::XlConsolidationFunction::xlProduct:
+ aColumnsRange[i].Function = sheet::GeneralFunction_PRODUCT;
+ break;
+ case excel::XlConsolidationFunction::xlStDev:
+ aColumnsRange[i].Function = sheet::GeneralFunction_STDEV;
+ break;
+ case excel::XlConsolidationFunction::xlStDevP:
+ aColumnsRange[i].Function = sheet::GeneralFunction_STDEVP;
+ break;
+ case excel::XlConsolidationFunction::xlSum:
+ aColumnsRange[i].Function = sheet::GeneralFunction_SUM;
+ break;
+ case excel::XlConsolidationFunction::xlUnknown:
+ aColumnsRange[i].Function = sheet::GeneralFunction_NONE;
+ break;
+ case excel::XlConsolidationFunction::xlVar:
+ aColumnsRange[i].Function = sheet::GeneralFunction_VAR;
+ break;
+ case excel::XlConsolidationFunction::xlVarP:
+ aColumnsRange[i].Function = sheet::GeneralFunction_VARP;
+ break;
+ default:
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_PARAMETER, {}) ;
+ return;
+ }
+ }
+ xSubDesc->addNew(aColumns, _nGroupBy - 1);
+ xSub->applySubTotals(xSubDesc, bDoReplace);
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+void SAL_CALL
+ScVbaRange::ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& /*IgnorePrintAreas*/, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& /*FixedFormatExtClassPtr*/)
+{
+ ScCellRangesBase* pUnoRangesBase = getCellRangesBase();
+ if (!pUnoRangesBase)
+ throw uno::RuntimeException("Failed to access underlying uno range object");
+ ScDocShell* pShell = pUnoRangesBase->GetDocShell();
+ if (!pShell)
+ return;
+
+ uno::Reference< frame::XModel > xModel(pShell->GetModel(), uno::UNO_SET_THROW);
+ uno::Reference< excel::XApplication > xApplication(Application(), uno::UNO_QUERY_THROW);
+
+ excel::ExportAsFixedFormatHelper(xModel, xApplication, Type, FileName, Quality,
+ IncludeDocProperties, From, To, OpenAfterPublish);
+}
+
+OUString
+ScVbaRange::getServiceImplName()
+{
+ return "ScVbaRange";
+}
+
+uno::Sequence< OUString >
+ScVbaRange::getServiceNames()
+{
+ return { "ooo.vba.excel.Range" };
+}
+
+sal_Bool SAL_CALL
+ScVbaRange::hasError()
+{
+ double dResult = 0.0;
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ uno::Reference< script::XInvocation > xInvoc( xApplication->WorksheetFunction(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< excel::XRange > aRange( this );
+ uno::Sequence< uno::Any > Params{ uno::Any(aRange) };
+ uno::Sequence< sal_Int16 > OutParamIndex;
+ uno::Sequence< uno::Any > OutParam;
+ xInvoc->invoke( "IsError", Params, OutParamIndex, OutParam ) >>= dResult;
+ return dResult > 0.0;
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScVbaRange_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new ScVbaRange(args, context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbarange.hxx b/sc/source/ui/vba/vbarange.hxx
new file mode 100644
index 0000000000..a9d82201a6
--- /dev/null
+++ b/sc/source/ui/vba/vbarange.hxx
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this 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 <ooo/vba/excel/XRange.hpp>
+
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/sheet/FillDateMode.hpp>
+#include <com/sun/star/sheet/FillMode.hpp>
+#include <com/sun/star/sheet/FillDirection.hpp>
+
+#include "vbaformat.hxx"
+#include <address.hxx>
+#include <formula/grammar.hxx>
+
+namespace com::sun::star::sheet { class XSheetCellRangeContainer; }
+namespace com::sun::star::table { class XCell; }
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::table { struct CellRangeAddress; }
+namespace com::sun::star::lang { class XServiceInfo; }
+namespace ooo::vba { class XCollection; }
+namespace ooo::vba::excel { class XComment; }
+namespace ooo::vba::excel { class XFont; }
+
+class SfxItemSet;
+class ScCellRangesBase;
+class ScCellRangeObj;
+class ScDocShell;
+class ScDocument;
+class ScRangeList;
+
+class ArrayVisitor
+{
+public:
+ virtual void visitNode( sal_Int32 x, sal_Int32 y, const css::uno::Reference< css::table::XCell >& xCell ) = 0;
+ virtual ~ArrayVisitor(){}
+};
+
+class ValueSetter : public ArrayVisitor
+{
+public:
+ virtual bool processValue( const css::uno::Any& aValue, const css::uno::Reference< css::table::XCell >& xCell ) = 0;
+
+};
+
+class ValueGetter : public ArrayVisitor
+{
+
+public:
+ virtual void processValue( const css::uno::Any& aValue ) = 0;
+ virtual const css::uno::Any& getValue() const = 0;
+};
+
+typedef ScVbaFormat< ov::excel::XRange > ScVbaRange_BASE;
+
+enum class RangeValueType { value, value2 };
+
+class ScVbaRange : public ScVbaRange_BASE
+{
+ css::uno::Reference< ov::XCollection > m_Areas;
+ css::uno::Reference< ov::XCollection > m_Borders;
+ css::uno::Reference< css::table::XCellRange > mxRange;
+ css::uno::Reference< css::sheet::XSheetCellRangeContainer > mxRanges;
+ bool mbIsRows;
+ bool mbIsColumns;
+ css::uno::Reference< ov::excel::XValidation > m_xValidation;
+ /// @throws css::uno::RuntimeException
+ double getCalcColWidth(const css::table::CellRangeAddress&);
+ /// @throws css::uno::RuntimeException
+ double getCalcRowHeight(const css::table::CellRangeAddress&);
+ void visitArray( ArrayVisitor& visitor );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XRange > getEntireColumnOrRow( bool bColumn );
+
+ /// @throws css::uno::RuntimeException
+ void fillSeries( css::sheet::FillDirection nFillDirection, css::sheet::FillMode nFillMode, css::sheet::FillDateMode nFillDateMode, double fStep, double fEndValue );
+
+ /// @throws css::uno::RuntimeException
+ void ClearContents( sal_Int32 nFlags, bool bFireEvent );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Any getValue( ValueGetter& rValueGetter );
+ css::uno::Any DoGetValue( RangeValueType eValueType );
+ /// @throws css::uno::RuntimeException
+ void setValue( const css::uno::Any& aValue, ValueSetter& setter );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Any getFormulaValue( formula::FormulaGrammar::Grammar );
+ /// @throws css::uno::RuntimeException
+ void setFormulaValue( const css::uno::Any& aValue, formula::FormulaGrammar::Grammar );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XRange > getArea( sal_Int32 nIndex );
+ /// @throws css::uno::RuntimeException
+ ScCellRangeObj* getCellRangeObj( );
+ css::uno::Reference< ov::XCollection >& getBorders();
+ /// @throws css::uno::RuntimeException
+ void groupUnGroup( bool bUnGroup );
+ css::uno::Reference< ov::excel::XRange > PreviousNext( bool bIsPrevious );
+ /// @throws css::script::BasicErrorException
+ css::uno::Reference< ov::excel::XRange > SpecialCellsImpl( sal_Int32 nType, const css::uno::Any& _oValue);
+ /// @throws css::uno::RuntimeException
+ css::awt::Point getPosition() const;
+
+ /** Fires a Worksheet_Change event for this range or range list. */
+ void fireChangeEvent();
+
+ /// @throws css::uno::RuntimeException
+ ScRange obtainRangeEvenIfRangeListIsEmpty( const ScRangeList& rCellRanges ) const;
+
+protected:
+ virtual ScCellRangesBase* getCellRangesBase() override;
+ /// @throws css::uno::RuntimeException
+ SfxItemSet* getCurrentDataSet();
+public:
+ /// @throws css::lang::IllegalArgumentException
+ ScVbaRange( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::table::XCellRange >& xRange, bool bIsRows = false, bool bIsColumns = false );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ ScVbaRange( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::sheet::XSheetCellRangeContainer >& xRanges, bool bIsRows = false, bool bIsColumns = false );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ ScVbaRange( css::uno::Sequence< css::uno::Any > const& aArgs, css::uno::Reference< css::uno::XComponentContext >const& xContext );
+
+ /// @throws css::uno::RuntimeException
+ ScDocument& getScDocument();
+ /// @throws css::uno::RuntimeException
+ ScDocShell* getScDocShell();
+
+ /** Returns the ScVbaRange implementation object for the passed VBA Range object. */
+ static ScVbaRange* getImplementation( const css::uno::Reference< ov::excel::XRange >& rxRange );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::frame::XModel > getUnoModel();
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< css::frame::XModel > getUnoModel( const css::uno::Reference< ov::excel::XRange >& rxRange );
+
+ /// @throws css::uno::RuntimeException
+ const ScRangeList& getScRangeList();
+ /// @throws css::uno::RuntimeException
+ static const ScRangeList& getScRangeList( const css::uno::Reference< ov::excel::XRange >& rxRange );
+
+ virtual ~ScVbaRange() override;
+ virtual css::uno::Reference< ov::XHelperInterface > thisHelperIface() override { return this; }
+ bool isSingleCellRange() const;
+
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< ov::excel::XRange > getRangeObjectForName(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const OUString& sRangeName, ScDocShell* pDocSh,
+ formula::FormulaGrammar::AddressConvention eConv );
+
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< ov::excel::XRange > CellsHelper(
+ const ScDocument& rDoc,
+ const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::table::XCellRange >& xRange,
+ const css::uno::Any &nRowIndex, const css::uno::Any &nColumnIndex );
+
+ // Attributes
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual css::uno::Any SAL_CALL getValue2() override;
+ virtual void SAL_CALL setValue( const css::uno::Any& aValue ) override;
+ virtual void SAL_CALL setValue2( const css::uno::Any& aValue2 ) override;
+ virtual css::uno::Any SAL_CALL getFormula() override;
+ virtual void SAL_CALL setFormula( const css::uno::Any& rFormula ) override;
+ virtual css::uno::Any SAL_CALL getFormulaArray() override;
+ virtual void SAL_CALL setFormulaArray(const css::uno::Any& rFormula) override;
+ virtual css::uno::Any SAL_CALL getFormulaR1C1() override;
+ virtual void SAL_CALL setFormulaR1C1( const css::uno::Any &rFormula ) override;
+ virtual css::uno::Any SAL_CALL getFormulaLocal() override;
+ virtual void SAL_CALL setFormulaLocal( const css::uno::Any &rFormula ) override;
+ virtual css::uno::Any SAL_CALL getFormulaR1C1Local() override;
+ virtual void SAL_CALL setFormulaR1C1Local( const css::uno::Any &rFormula ) override;
+ virtual ::sal_Int32 SAL_CALL getCount() override;
+ virtual ::sal_Int32 SAL_CALL getRow() override;
+ virtual ::sal_Int32 SAL_CALL getColumn() override;
+ virtual OUString SAL_CALL getText() override;
+ using ScVbaRange_BASE::setNumberFormat;
+ virtual void SAL_CALL setNumberFormat( const css::uno::Any& rNumberFormat ) override;
+ virtual css::uno::Any SAL_CALL getNumberFormat() override;
+ virtual void SAL_CALL setMergeCells( const css::uno::Any& bMerge ) override;
+ virtual css::uno::Any SAL_CALL getMergeCells() override;
+ virtual void SAL_CALL setWrapText( const css::uno::Any& bIsWrapped ) override;
+ virtual css::uno::Any SAL_CALL getWrapText() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getEntireRow() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getEntireColumn() override;
+ virtual css::uno::Reference< ov::excel::XComment > SAL_CALL getComment() override;
+ virtual css::uno::Any SAL_CALL getHidden() override;
+ virtual void SAL_CALL setHidden( const css::uno::Any& _hidden ) override;
+ virtual css::uno::Any SAL_CALL getColumnWidth() override;
+ virtual void SAL_CALL setColumnWidth( const css::uno::Any& _columnwidth ) override;
+ virtual css::uno::Any SAL_CALL getRowHeight() override;
+ virtual void SAL_CALL setRowHeight( const css::uno::Any& _rowheight ) override;
+ virtual css::uno::Any SAL_CALL getWidth() override;
+ virtual css::uno::Any SAL_CALL getHeight() override;
+ virtual css::uno::Any SAL_CALL getTop() override;
+ virtual css::uno::Any SAL_CALL getLeft() override;
+
+ virtual css::uno::Reference< ov::excel::XName > SAL_CALL getName() override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL getWorksheet() override;
+ virtual css::uno::Any SAL_CALL getPageBreak() override;
+ virtual void SAL_CALL setPageBreak( const css::uno::Any& _pagebreak ) override;
+ virtual css::uno::Reference< ov::excel::XValidation > SAL_CALL getValidation() override;
+ virtual css::uno::Any SAL_CALL getPrefixCharacter() override;
+ virtual css::uno::Any SAL_CALL getShowDetail() override;
+ virtual void SAL_CALL setShowDetail(const css::uno::Any& aShowDetail) override;
+ // Methods
+ virtual css::uno::Reference< ov::excel::XComment > SAL_CALL AddComment( const css::uno::Any& Text ) override;
+ virtual void SAL_CALL Clear() override;
+ virtual void SAL_CALL ClearComments() override;
+ virtual void SAL_CALL ClearContents() override;
+ virtual void SAL_CALL ClearFormats() override;
+ virtual css::uno::Any SAL_CALL HasFormula() override;
+ virtual void SAL_CALL FillLeft() override;
+ virtual void SAL_CALL FillRight() override;
+ virtual void SAL_CALL FillUp() override;
+ virtual void SAL_CALL FillDown() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Offset( const css::uno::Any &nRowOffset, const css::uno::Any &nColOffset ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL CurrentRegion() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL CurrentArray() override;
+ virtual OUString SAL_CALL Characters( const css::uno::Any& nIndex, const css::uno::Any& nCount ) override;
+
+ virtual OUString SAL_CALL Address( const css::uno::Any& RowAbsolute, const css::uno::Any& ColumnAbsolute, const css::uno::Any& ReferenceStyle, const css::uno::Any& External, const css::uno::Any& RelativeTo ) override;
+
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Cells( const css::uno::Any &nRow, const css::uno::Any &nCol ) override;
+ virtual void SAL_CALL Select() override;
+ virtual void SAL_CALL Activate() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Rows( const css::uno::Any& nIndex ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Columns( const css::uno::Any &nIndex ) override;
+ virtual void SAL_CALL Copy( const css::uno::Any& Destination ) override;
+ virtual void SAL_CALL Cut( const css::uno::Any& Destination ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Resize( const css::uno::Any& RowSize, const css::uno::Any& ColumnSize ) override;
+ virtual css::uno::Reference< ov::excel::XFont > SAL_CALL Font() override;
+ virtual css::uno::Reference< ov::excel::XInterior > SAL_CALL Interior( ) override ;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Range( const css::uno::Any &Cell1, const css::uno::Any &Cell2 ) override;
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XRange > Range( const css::uno::Any &Cell1, const css::uno::Any &Cell2, bool bForceUseInpuRangeTab );
+ virtual css::uno::Any SAL_CALL getCellRange( ) override;
+ /// @throws css::uno::RuntimeException
+ static css::uno::Any getCellRange( const css::uno::Reference< ov::excel::XRange >& rxRange );
+ virtual void SAL_CALL PasteSpecial( const css::uno::Any& Paste, const css::uno::Any& Operation, const css::uno::Any& SkipBlanks, const css::uno::Any& Transpose ) override;
+ virtual sal_Bool SAL_CALL Replace( const OUString& What, const OUString& Replacement, const css::uno::Any& LookAt, const css::uno::Any& SearchOrder, const css::uno::Any& MatchCase, const css::uno::Any& MatchByte, const css::uno::Any& SearchFormat, const css::uno::Any& ReplaceFormat ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Find( const css::uno::Any& What, const css::uno::Any& After, const css::uno::Any& LookIn, const css::uno::Any& LookAt, const css::uno::Any& SearchOrder, const css::uno::Any& SearchDirection, const css::uno::Any& MatchCase, const css::uno::Any& MatchByte, const css::uno::Any& SearchFormat ) override;
+ virtual void SAL_CALL Sort( const css::uno::Any& Key1, const css::uno::Any& Order1, const css::uno::Any& Key2, const css::uno::Any& Type, const css::uno::Any& Order2, const css::uno::Any& Key3, const css::uno::Any& Order3, const css::uno::Any& Header, const css::uno::Any& OrderCustom, const css::uno::Any& MatchCase, const css::uno::Any& Orientation, const css::uno::Any& SortMethod, const css::uno::Any& DataOption1, const css::uno::Any& DataOption2, const css::uno::Any& DataOption3 ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL End( ::sal_Int32 Direction ) override;
+ virtual css::uno::Reference< ov::excel::XCharacters > SAL_CALL characters( const css::uno::Any& Start, const css::uno::Any& Length ) override;
+ virtual void SAL_CALL Delete( const css::uno::Any& Shift ) override;
+ virtual css::uno::Any SAL_CALL Areas( const css::uno::Any& ) override;
+ virtual css::uno::Any SAL_CALL Borders( const css::uno::Any& ) override;
+ virtual css::uno::Any SAL_CALL BorderAround( const css::uno::Any& LineStyle,
+ const css::uno::Any& Weight, const css::uno::Any& ColorIndex, const css::uno::Any& Color ) override;
+ virtual css::uno::Any SAL_CALL Hyperlinks( const css::uno::Any& aIndex ) override;
+
+ virtual void SAL_CALL AutoFilter( const css::uno::Any& Field, const css::uno::Any& Criteria1, const css::uno::Any& Operator, const css::uno::Any& Criteria2, const css::uno::Any& VisibleDropDown ) override;
+ virtual void SAL_CALL Insert( const css::uno::Any& Shift, const css::uno::Any& CopyOrigin ) override;
+ virtual void SAL_CALL Autofit() override;
+ virtual void SAL_CALL PrintOut( const css::uno::Any& From, const css::uno::Any& To, const css::uno::Any& Copies, const css::uno::Any& Preview, const css::uno::Any& ActivePrinter, const css::uno::Any& PrintToFile, const css::uno::Any& Collate, const css::uno::Any& PrToFileName ) override;
+ virtual void SAL_CALL AutoFill( const css::uno::Reference< ov::excel::XRange >& Destination, const css::uno::Any& Type ) override ;
+ void SAL_CALL Calculate( ) override;
+ virtual void SAL_CALL AutoOutline( ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Item( const css::uno::Any& row, const css::uno::Any& column ) override;
+ virtual void SAL_CALL ClearOutline( ) override;
+ virtual void SAL_CALL Ungroup( ) override;
+ virtual void SAL_CALL Group( ) override;
+ virtual void SAL_CALL Merge( const css::uno::Any& Across ) override;
+ virtual void SAL_CALL UnMerge( ) override;
+ virtual css::uno::Any SAL_CALL getStyle() override;
+ virtual void SAL_CALL setStyle( const css::uno::Any& _style ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Next() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Previous() override;
+ virtual void SAL_CALL RemoveSubtotal( ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL MergeArea() override;
+ virtual void SAL_CALL Subtotal( ::sal_Int32 GroupBy, ::sal_Int32 Function, const css::uno::Sequence< ::sal_Int32 >& TotalList, const css::uno::Any& Replace, const css::uno::Any& PageBreaks, const css::uno::Any& SummaryBelowData ) override;
+ virtual void SAL_CALL ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& IgnorePrintAreas, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& FixedFormatExtClassPtr) override;
+
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override
+ {
+ return cppu::UnoType<ov::excel::XRange>::get();
+
+ }
+ virtual sal_Bool SAL_CALL hasElements() override;
+ // XDefaultMethod
+ OUString SAL_CALL getDefaultMethodName( ) override;
+ // XDefaultProperty
+ OUString SAL_CALL getDefaultPropertyName( ) override { return "Value"; }
+
+// #TODO completely rewrite ScVbaRange, it's become a hackfest
+// it needs to be closer to ScCellRangeBase in that the underlying
+// object model should probably be a ScRangelst.
+// * would be nice to be able to construct a range from an address only
+// * or a list of address ( multi-area )
+// * object should be a lightweight as possible
+// * we shouldn't need hacks like this below
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< ov::excel::XRange > ApplicationRange( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Any &Cell1, const css::uno::Any &Cell2 );
+ static bool getCellRangesForAddress(ScRefFlags &rResFlags, std::u16string_view sAddress, ScDocShell* pDocSh, ScRangeList& rCellRanges, formula::FormulaGrammar::AddressConvention eConv, char cDelimiter );
+ virtual sal_Bool SAL_CALL GoalSeek( const css::uno::Any& Goal, const css::uno::Reference< ov::excel::XRange >& ChangingCell ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL SpecialCells( const css::uno::Any& _oType, const css::uno::Any& _oValue) override;
+ // XErrorQuery
+ virtual sal_Bool SAL_CALL hasError( ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/// @throws css::uno::RuntimeException
+bool getScRangeListForAddress( const OUString& sName, ScDocShell* pDocSh, const ScRange& refRange,
+ ScRangeList& aCellRanges,
+ formula::FormulaGrammar::AddressConvention aConv = formula::FormulaGrammar::CONV_XL_A1 );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbasheetobject.cxx b/sc/source/ui/vba/vbasheetobject.cxx
new file mode 100644
index 0000000000..c0321a5a3b
--- /dev/null
+++ b/sc/source/ui/vba/vbasheetobject.cxx
@@ -0,0 +1,550 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbasheetobject.hxx"
+#include <com/sun/star/awt/TextAlign.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <comphelper/documentinfo.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <ooo/vba/excel/Constants.hpp>
+#include <ooo/vba/excel/XlOrientation.hpp>
+#include <ooo/vba/excel/XlPlacement.hpp>
+#include <filter/msfilter/msvbahelper.hxx>
+#include "vbafont.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+constexpr OUString gaListenerType = u"XActionListener"_ustr;
+constexpr OUString gaEventMethod = u"actionPerformed"_ustr;
+
+static double HmmToPoints(double nHmm)
+{
+ return o3tl::convert(nHmm, o3tl::Length::mm100, o3tl::Length::pt);
+}
+
+static sal_Int32 PointsToHmm(double fPoints)
+{
+ return std::round(o3tl::convert(fPoints, o3tl::Length::pt, o3tl::Length::mm100));
+}
+
+ScVbaButtonCharacters::ScVbaButtonCharacters(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< beans::XPropertySet >& rxPropSet,
+ const ScVbaPalette& rPalette,
+ const uno::Any& rStart,
+ const uno::Any& rLength ) :
+ ScVbaButtonCharacters_BASE( rxParent, rxContext ),
+ maPalette( rPalette ),
+ mxPropSet( rxPropSet, uno::UNO_SET_THROW )
+{
+ // extract optional start parameter (missing or invalid -> from beginning)
+ if( !(rStart >>= mnStart) || (mnStart < 1) )
+ mnStart = 1;
+ --mnStart; // VBA is 1-based, rtl string is 0-based
+
+ // extract optional length parameter (missing or invalid -> to end)
+ if( !(rLength >>= mnLength) || (mnLength < 1) )
+ mnLength = SAL_MAX_INT32;
+}
+
+ScVbaButtonCharacters::~ScVbaButtonCharacters()
+{
+}
+
+// XCharacters attributes
+
+OUString SAL_CALL ScVbaButtonCharacters::getCaption()
+{
+ // ignore invalid mnStart and/or mnLength members
+ OUString aString = getFullString();
+ sal_Int32 nStart = ::std::min( mnStart, aString.getLength() );
+ sal_Int32 nLength = ::std::min( mnLength, aString.getLength() - nStart );
+ return aString.copy( nStart, nLength );
+}
+
+void SAL_CALL ScVbaButtonCharacters::setCaption( const OUString& rCaption )
+{
+ /* Replace the covered text with the passed text, ignore invalid mnStart
+ and/or mnLength members. This operation does not affect the mnLength
+ parameter. If the inserted text is longer than mnLength, the additional
+ characters are not covered by this object. If the inserted text is
+ shorter than mnLength, other uncovered characters from the original
+ string will be covered now, thus may be changed with subsequent
+ operations. */
+ OUString aString = getFullString();
+ sal_Int32 nStart = ::std::min( mnStart, aString.getLength() );
+ sal_Int32 nLength = ::std::min( mnLength, aString.getLength() - nStart );
+ setFullString( aString.replaceAt( nStart, nLength, rCaption ) );
+}
+
+sal_Int32 SAL_CALL ScVbaButtonCharacters::getCount()
+{
+ // always return the total length of the caption
+ return getFullString().getLength();
+}
+
+OUString SAL_CALL ScVbaButtonCharacters::getText()
+{
+ // Text attribute same as Caption attribute?
+ return getCaption();
+}
+
+void SAL_CALL ScVbaButtonCharacters::setText( const OUString& rText )
+{
+ // Text attribute same as Caption attribute?
+ setCaption( rText );
+}
+
+uno::Reference< excel::XFont > SAL_CALL ScVbaButtonCharacters::getFont()
+{
+ return new ScVbaFont( this, mxContext, maPalette, mxPropSet, nullptr, true );
+}
+
+void SAL_CALL ScVbaButtonCharacters::setFont( const uno::Reference< excel::XFont >& /*rxFont*/ )
+{
+ // TODO
+}
+
+// XCharacters methods
+
+void SAL_CALL ScVbaButtonCharacters::Insert( const OUString& rString )
+{
+ /* The Insert() operation is in fact "replace covered characters", at
+ least for buttons... It seems there is no easy way to really insert a
+ substring. This operation does not affect the mnLength parameter. */
+ setCaption( rString );
+}
+
+void SAL_CALL ScVbaButtonCharacters::Delete()
+{
+ /* The Delete() operation is nothing else than "replace with empty string".
+ This does not affect the mnLength parameter, multiple calls of Delete()
+ will remove characters as long as there are some more covered by this
+ object. */
+ setCaption( OUString() );
+}
+
+// XHelperInterface
+
+VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaButtonCharacters, "ooo.vba.excel.Characters" )
+
+// private
+
+OUString ScVbaButtonCharacters::getFullString() const
+{
+ return mxPropSet->getPropertyValue( "Label" ).get< OUString >();
+}
+
+void ScVbaButtonCharacters::setFullString( const OUString& rString )
+{
+ mxPropSet->setPropertyValue( "Label", uno::Any( rString ) );
+}
+
+ScVbaSheetObjectBase::ScVbaSheetObjectBase(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< drawing::XShape >& rxShape ) :
+ ScVbaSheetObject_BASE( rxParent, rxContext ),
+ maPalette( rxModel ),
+ mxModel( rxModel, uno::UNO_SET_THROW ),
+ mxShape( rxShape, uno::UNO_SET_THROW ),
+ mxShapeProps( rxShape, uno::UNO_QUERY_THROW )
+{
+}
+
+// XSheetObject attributes
+
+double SAL_CALL ScVbaSheetObjectBase::getLeft()
+{
+ return HmmToPoints( mxShape->getPosition().X );
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setLeft( double fLeft )
+{
+ if( fLeft < 0.0 )
+ throw uno::RuntimeException();
+ mxShape->setPosition( awt::Point( PointsToHmm( fLeft ), mxShape->getPosition().Y ) );
+}
+
+double SAL_CALL ScVbaSheetObjectBase::getTop()
+{
+ return HmmToPoints( mxShape->getPosition().Y );
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setTop( double fTop )
+{
+ if( fTop < 0.0 )
+ throw uno::RuntimeException();
+ mxShape->setPosition( awt::Point( mxShape->getPosition().X, PointsToHmm( fTop ) ) );
+}
+
+double SAL_CALL ScVbaSheetObjectBase::getWidth()
+{
+ return HmmToPoints( mxShape->getSize().Width );
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setWidth( double fWidth )
+{
+ if( fWidth <= 0.0 )
+ throw uno::RuntimeException();
+ mxShape->setSize( awt::Size( PointsToHmm( fWidth ), mxShape->getSize().Height ) );
+}
+
+double SAL_CALL ScVbaSheetObjectBase::getHeight()
+{
+ return HmmToPoints( mxShape->getSize().Height );
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setHeight( double fHeight )
+{
+ if( fHeight <= 0.0 )
+ throw uno::RuntimeException();
+ mxShape->setSize( awt::Size( mxShape->getSize().Width, PointsToHmm( fHeight ) ) );
+}
+
+OUString SAL_CALL ScVbaSheetObjectBase::getName()
+{
+ return mxShapeProps->getPropertyValue( "Name" ).get< OUString >();
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setName( const OUString& rName )
+{
+ mxShapeProps->setPropertyValue( "Name", uno::Any( rName ) );
+}
+
+sal_Int32 SAL_CALL ScVbaSheetObjectBase::getPlacement()
+{
+ sal_Int32 const nRet = excel::XlPlacement::xlMoveAndSize;
+#if 0 // TODO: not working at the moment.
+ SvxShape* pShape = SdrObject::getSdrObjectFromXShape( mxShape );
+ if(pShape)
+ {
+ SdrObject* pObj = pShape->GetSdrObject();
+ if (pObj)
+ {
+ ScAnchorType eType = ScDrawLayer::GetAnchor(pObj);
+ if (eType == SCA_PAGE)
+ nRet = excel::XlPlacement::xlFreeFloating;
+ }
+ }
+#endif
+ return nRet;
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setPlacement( sal_Int32 /*nPlacement*/ )
+{
+#if 0 // TODO: not working at the moment.
+ SvxShape* pShape = SdrObject::getSdrObjectFromXShape( mxShape );
+ if(pShape)
+ {
+ SdrObject* pObj = pShape->GetSdrObject();
+ if (pObj)
+ {
+ ScAnchorType eType = SCA_CELL;
+ if ( nPlacement == excel::XlPlacement::xlFreeFloating )
+ eType = SCA_PAGE;
+
+ // xlMove is not supported, treated as SCA_CELL (xlMoveAndSize)
+
+ ScDrawLayer::SetAnchor(pObj, eType);
+ }
+ }
+#endif
+}
+
+sal_Bool SAL_CALL ScVbaSheetObjectBase::getPrintObject()
+{
+ // not supported
+ return true;
+}
+
+void SAL_CALL ScVbaSheetObjectBase::setPrintObject( sal_Bool /*bPrintObject*/ )
+{
+ // not supported
+}
+
+// private
+
+void ScVbaSheetObjectBase::setDefaultProperties( sal_Int32 nIndex )
+{
+ OUString aName = implGetBaseName() + OUStringChar(' ') + OUString::number( nIndex + 1 );
+ setName( aName );
+ implSetDefaultProperties();
+}
+
+void ScVbaSheetObjectBase::implSetDefaultProperties()
+{
+}
+
+ScVbaControlObjectBase::ScVbaControlObjectBase(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< container::XIndexContainer >& rxFormIC,
+ const uno::Reference< drawing::XControlShape >& rxControlShape ) :
+ ScVbaControlObject_BASE( rxParent, rxContext, rxModel, uno::Reference< drawing::XShape >( rxControlShape, uno::UNO_QUERY_THROW ) ),
+ mxFormIC( rxFormIC, uno::UNO_SET_THROW ),
+ mxControlProps( rxControlShape->getControl(), uno::UNO_QUERY_THROW ),
+ mbNotifyMacroEventRead(false)
+{
+}
+
+// XSheetObject attributes
+
+OUString SAL_CALL ScVbaControlObjectBase::getName()
+{
+ return mxControlProps->getPropertyValue( "Name" ).get< OUString >();
+}
+
+void SAL_CALL ScVbaControlObjectBase::setName( const OUString& rName )
+{
+ mxControlProps->setPropertyValue( "Name", uno::Any( rName ) );
+}
+
+OUString SAL_CALL ScVbaControlObjectBase::getOnAction()
+{
+ uno::Reference< script::XEventAttacherManager > xEventMgr( mxFormIC, uno::UNO_QUERY_THROW );
+ sal_Int32 nIndex = getModelIndexInForm();
+ const uno::Sequence< script::ScriptEventDescriptor > aEvents = xEventMgr->getScriptEvents( nIndex );
+ if( aEvents.hasElements() )
+ {
+ const script::ScriptEventDescriptor* pEvent = std::find_if(aEvents.begin(), aEvents.end(),
+ [](const script::ScriptEventDescriptor& rEvent) {
+ return (rEvent.ListenerType == gaListenerType)
+ && (rEvent.EventMethod == gaEventMethod)
+ && (rEvent.ScriptType == "Script");
+ });
+ if (pEvent != aEvents.end())
+ return extractMacroName( pEvent->ScriptCode );
+ }
+ return OUString();
+}
+
+void ScVbaControlObjectBase::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(mxModel);
+ mbNotifyMacroEventRead = true;
+}
+
+void SAL_CALL ScVbaControlObjectBase::setOnAction( const OUString& rMacroName )
+{
+ uno::Reference< script::XEventAttacherManager > xEventMgr( mxFormIC, uno::UNO_QUERY_THROW );
+ sal_Int32 nIndex = getModelIndexInForm();
+
+ // first, remove a registered event (try/catch just in case implementation throws)
+ try { xEventMgr->revokeScriptEvent( nIndex, gaListenerType, gaEventMethod, OUString() ); } catch( uno::Exception& ) {}
+
+ // if a macro name has been passed, try to attach it to the event
+ if( rMacroName.isEmpty() )
+ return;
+
+ MacroResolvedInfo aResolvedMacro = resolveVBAMacro( getSfxObjShell( mxModel ), rMacroName );
+ if( !aResolvedMacro.mbFound )
+ throw uno::RuntimeException();
+ script::ScriptEventDescriptor aDescriptor;
+ aDescriptor.ListenerType = gaListenerType;
+ aDescriptor.EventMethod = gaEventMethod;
+ aDescriptor.ScriptType = "Script";
+ aDescriptor.ScriptCode = makeMacroURL( aResolvedMacro.msResolvedMacro );
+ NotifyMacroEventRead();
+ xEventMgr->registerScriptEvent( nIndex, aDescriptor );
+}
+
+sal_Bool SAL_CALL ScVbaControlObjectBase::getPrintObject()
+{
+ return mxControlProps->getPropertyValue( "Printable" ).get<bool>();
+}
+
+void SAL_CALL ScVbaControlObjectBase::setPrintObject( sal_Bool bPrintObject )
+{
+ mxControlProps->setPropertyValue( "Printable", uno::Any( bPrintObject ) );
+}
+
+// XControlObject attributes
+
+sal_Bool SAL_CALL ScVbaControlObjectBase::getAutoSize()
+{
+ // not supported
+ return false;
+}
+
+void SAL_CALL ScVbaControlObjectBase::setAutoSize( sal_Bool /*bAutoSize*/ )
+{
+ // not supported
+}
+
+// private
+
+sal_Int32 ScVbaControlObjectBase::getModelIndexInForm() const
+{
+ for( sal_Int32 nIndex = 0, nCount = mxFormIC->getCount(); nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< beans::XPropertySet > xProps( mxFormIC->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ if( mxControlProps.get() == xProps.get() )
+ return nIndex;
+ }
+ throw uno::RuntimeException();
+}
+
+ScVbaButton::ScVbaButton(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< container::XIndexContainer >& rxFormIC,
+ const uno::Reference< drawing::XControlShape >& rxControlShape ) :
+ ScVbaButton_BASE( rxParent, rxContext, rxModel, rxFormIC, rxControlShape )
+{
+}
+
+// XButton attributes
+
+OUString SAL_CALL ScVbaButton::getCaption()
+{
+ return mxControlProps->getPropertyValue( "Label" ).get< OUString >();
+}
+
+void SAL_CALL ScVbaButton::setCaption( const OUString& rCaption )
+{
+ mxControlProps->setPropertyValue( "Label", uno::Any( rCaption ) );
+}
+
+uno::Reference< excel::XFont > SAL_CALL ScVbaButton::getFont()
+{
+ return new ScVbaFont( this, mxContext, maPalette, mxControlProps, nullptr, true );
+}
+
+void SAL_CALL ScVbaButton::setFont( const uno::Reference< excel::XFont >& /*rxFont*/ )
+{
+ // TODO
+}
+
+sal_Int32 SAL_CALL ScVbaButton::getHorizontalAlignment()
+{
+ switch( mxControlProps->getPropertyValue( "Align" ).get< sal_Int16 >() )
+ {
+ case awt::TextAlign::LEFT: return excel::Constants::xlLeft;
+ case awt::TextAlign::RIGHT: return excel::Constants::xlRight;
+ case awt::TextAlign::CENTER: return excel::Constants::xlCenter;
+ }
+ return excel::Constants::xlCenter;
+}
+
+void SAL_CALL ScVbaButton::setHorizontalAlignment( sal_Int32 nAlign )
+{
+ sal_Int32 nAwtAlign = awt::TextAlign::CENTER;
+ switch( nAlign )
+ {
+ case excel::Constants::xlLeft: nAwtAlign = awt::TextAlign::LEFT; break;
+ case excel::Constants::xlRight: nAwtAlign = awt::TextAlign::RIGHT; break;
+ case excel::Constants::xlCenter: nAwtAlign = awt::TextAlign::CENTER; break;
+ }
+ // form controls expect short value
+ mxControlProps->setPropertyValue( "Align", uno::Any( static_cast< sal_Int16 >( nAwtAlign ) ) );
+}
+
+sal_Int32 SAL_CALL ScVbaButton::getVerticalAlignment()
+{
+ switch( mxControlProps->getPropertyValue( "VerticalAlign" ).get< style::VerticalAlignment >() )
+ {
+ case style::VerticalAlignment_TOP: return excel::Constants::xlTop;
+ case style::VerticalAlignment_BOTTOM: return excel::Constants::xlBottom;
+ case style::VerticalAlignment_MIDDLE: return excel::Constants::xlCenter;
+ default:;
+ }
+ return excel::Constants::xlCenter;
+}
+
+void SAL_CALL ScVbaButton::setVerticalAlignment( sal_Int32 nAlign )
+{
+ style::VerticalAlignment eAwtAlign = style::VerticalAlignment_MIDDLE;
+ switch( nAlign )
+ {
+ case excel::Constants::xlTop: eAwtAlign = style::VerticalAlignment_TOP; break;
+ case excel::Constants::xlBottom: eAwtAlign = style::VerticalAlignment_BOTTOM; break;
+ case excel::Constants::xlCenter: eAwtAlign = style::VerticalAlignment_MIDDLE; break;
+ }
+ mxControlProps->setPropertyValue( "VerticalAlign", uno::Any( eAwtAlign ) );
+}
+
+sal_Int32 SAL_CALL ScVbaButton::getOrientation()
+{
+ // not supported
+ return excel::XlOrientation::xlHorizontal;
+}
+
+void SAL_CALL ScVbaButton::setOrientation( sal_Int32 /*nOrientation*/ )
+{
+ // not supported
+}
+
+uno::Any SAL_CALL ScVbaButton::getValue()
+{
+ return mxControlProps->getPropertyValue( "State" );
+}
+
+void SAL_CALL ScVbaButton::setValue( const uno::Any &nValue )
+{
+ return mxControlProps->setPropertyValue( "State", nValue );
+}
+
+OUString SAL_CALL ScVbaButton::getText()
+{
+ return mxControlProps->getPropertyValue( "Label" ).get< OUString >();
+}
+
+void SAL_CALL ScVbaButton::setText( const OUString &aText )
+{
+ return mxControlProps->setPropertyValue( "Label", uno::Any( aText ) );
+}
+
+// XButton methods
+
+uno::Reference< excel::XCharacters > SAL_CALL ScVbaButton::Characters( const uno::Any& rStart, const uno::Any& rLength )
+{
+ return new ScVbaButtonCharacters( this, mxContext, mxControlProps, maPalette, rStart, rLength );
+}
+
+// XHelperInterface
+
+VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaButton, "ooo.vba.excel.Button" )
+
+// private
+
+OUString ScVbaButton::implGetBaseName() const
+{
+ return "Button";
+}
+
+void ScVbaButton::implSetDefaultProperties()
+{
+ setCaption( getName() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbasheetobject.hxx b/sc/source/ui/vba/vbasheetobject.hxx
new file mode 100644
index 0000000000..d88b15034f
--- /dev/null
+++ b/sc/source/ui/vba/vbasheetobject.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 .
+ */
+
+#pragma once
+
+#include <ooo/vba/excel/XButton.hpp>
+#include <ooo/vba/excel/XControlObject.hpp>
+#include <ooo/vba/excel/XSheetObject.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <vbahelper/vbahelperinterface.hxx>
+#include "vbapalette.hxx"
+
+namespace com::sun::star {
+ namespace container { class XIndexContainer; }
+ namespace drawing { class XControlShape; }
+}
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XCharacters > ScVbaButtonCharacters_BASE;
+
+/** Simple implementation of the Characters symbol for drawing button objects. */
+class ScVbaButtonCharacters : public ScVbaButtonCharacters_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaButtonCharacters(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::beans::XPropertySet >& rxPropSet,
+ const ScVbaPalette& rPalette,
+ const css::uno::Any& rStart,
+ const css::uno::Any& rLength );
+ virtual ~ScVbaButtonCharacters() override;
+
+ // XCharacters attributes
+ virtual OUString SAL_CALL getCaption() override;
+ virtual void SAL_CALL setCaption( const OUString& rCaption ) override;
+ virtual OUString SAL_CALL getText() override;
+ virtual void SAL_CALL setText( const OUString& rText ) override;
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Reference< ov::excel::XFont > SAL_CALL getFont() override;
+ virtual void SAL_CALL setFont( const css::uno::Reference< ov::excel::XFont >& rxFont ) override;
+
+ // XCharacters methods
+ virtual void SAL_CALL Insert( const OUString& rString ) override;
+ virtual void SAL_CALL Delete() override;
+
+ // XHelperInterface
+ VBAHELPER_DECL_XHELPERINTERFACE
+
+private:
+ /// @throws css::uno::RuntimeException
+ OUString getFullString() const;
+ /// @throws css::uno::RuntimeException
+ void setFullString( const OUString& rString );
+
+private:
+ ScVbaPalette maPalette;
+ css::uno::Reference< css::beans::XPropertySet > mxPropSet;
+ sal_Int32 mnStart;
+ sal_Int32 mnLength;
+};
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XSheetObject > ScVbaSheetObject_BASE;
+
+/** Base class for drawing objects embedded in sheets. */
+class ScVbaSheetObjectBase : public ScVbaSheetObject_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaSheetObjectBase(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const css::uno::Reference< css::drawing::XShape >& rxShape );
+
+ // XSheetObject attributes
+ virtual double SAL_CALL getLeft() override;
+ virtual void SAL_CALL setLeft( double fLeft ) override;
+ virtual double SAL_CALL getTop() override;
+ virtual void SAL_CALL setTop( double fTop ) override;
+ virtual double SAL_CALL getWidth() override;
+ virtual void SAL_CALL setWidth( double fWidth ) override;
+ virtual double SAL_CALL getHeight() override;
+ virtual void SAL_CALL setHeight( double fHeight ) override;
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& rName ) override;
+ virtual sal_Int32 SAL_CALL getPlacement() override;
+ virtual void SAL_CALL setPlacement( sal_Int32 nPlacement ) override;
+ virtual sal_Bool SAL_CALL getPrintObject() override;
+ virtual void SAL_CALL setPrintObject( sal_Bool bPrintObject ) override;
+
+ /** Sets default properties after a new object has been created.
+
+ @throws css::uno::RuntimeException
+ */
+ void setDefaultProperties( sal_Int32 nIndex );
+
+protected:
+ /** Derived classes return the base name used for new objects. */
+ virtual OUString implGetBaseName() const = 0;
+ /** Derived classes set default properties for new drawing objects.
+
+ @throws css::uno::RuntimeException
+ */
+ virtual void implSetDefaultProperties();
+
+protected:
+ ScVbaPalette maPalette;
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::drawing::XShape > mxShape;
+ css::uno::Reference< css::beans::XPropertySet > mxShapeProps;
+};
+
+typedef ::cppu::ImplInheritanceHelper< ScVbaSheetObjectBase, ov::excel::XControlObject > ScVbaControlObject_BASE;
+
+class ScVbaControlObjectBase : public ScVbaControlObject_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaControlObjectBase(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const css::uno::Reference< css::container::XIndexContainer >& rxFormIC,
+ const css::uno::Reference< css::drawing::XControlShape >& rxControlShape );
+
+ // XSheetObject attributes
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& rName ) override;
+ virtual OUString SAL_CALL getOnAction() override;
+ virtual void SAL_CALL setOnAction( const OUString& rMacroName ) override;
+ virtual sal_Bool SAL_CALL getPrintObject() override;
+ virtual void SAL_CALL setPrintObject( sal_Bool bPrintObject ) override;
+
+ // XControlObject attributes
+ virtual sal_Bool SAL_CALL getAutoSize() override;
+ virtual void SAL_CALL setAutoSize( sal_Bool bAutoSize ) override;
+
+ /// Notify that the document contains a macro event handler
+ void NotifyMacroEventRead();
+
+protected:
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getModelIndexInForm() const;
+
+protected:
+ css::uno::Reference< css::container::XIndexContainer > mxFormIC;
+ css::uno::Reference< css::beans::XPropertySet > mxControlProps;
+ bool mbNotifyMacroEventRead;
+};
+
+typedef ::cppu::ImplInheritanceHelper< ScVbaControlObjectBase, ov::excel::XButton > ScVbaButton_BASE;
+
+class ScVbaButton : public ScVbaButton_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaButton(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const css::uno::Reference< css::container::XIndexContainer >& rxFormIC,
+ const css::uno::Reference< css::drawing::XControlShape >& rxControlShape );
+
+ // XButton attributes
+ virtual OUString SAL_CALL getCaption() override;
+ virtual void SAL_CALL setCaption( const OUString& rCaption ) override;
+ virtual css::uno::Reference< ov::excel::XFont > SAL_CALL getFont() override;
+ virtual void SAL_CALL setFont( const css::uno::Reference< ov::excel::XFont >& rxFont ) override;
+ virtual sal_Int32 SAL_CALL getHorizontalAlignment() override;
+ virtual void SAL_CALL setHorizontalAlignment( sal_Int32 nAlign ) override;
+ virtual sal_Int32 SAL_CALL getVerticalAlignment() override;
+ virtual void SAL_CALL setVerticalAlignment( sal_Int32 nAlign ) override;
+ virtual sal_Int32 SAL_CALL getOrientation() override;
+ virtual void SAL_CALL setOrientation( sal_Int32 nOrientation ) override;
+ virtual css::uno::Any SAL_CALL getValue() override;
+ virtual void SAL_CALL setValue( const css::uno::Any &nValue ) override;
+ virtual OUString SAL_CALL getText() override;
+ virtual void SAL_CALL setText( const OUString &aText ) override;
+
+ // XButton methods
+ css::uno::Reference< ov::excel::XCharacters > SAL_CALL Characters(
+ const css::uno::Any& rStart, const css::uno::Any& rLength ) override;
+
+ // XHelperInterface
+ VBAHELPER_DECL_XHELPERINTERFACE
+
+protected:
+ virtual OUString implGetBaseName() const override;
+ virtual void implSetDefaultProperties() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbasheetobjects.cxx b/sc/source/ui/vba/vbasheetobjects.cxx
new file mode 100644
index 0000000000..8ba097f9e7
--- /dev/null
+++ b/sc/source/ui/vba/vbasheetobjects.cxx
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbasheetobjects.hxx"
+#include <utility>
+#include <vector>
+#include <o3tl/unit_conversion.hxx>
+#include <rtl/math.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include "vbasheetobject.hxx"
+#include <cppuhelper/implbase.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+namespace {
+
+template< typename Type >
+bool lclGetProperty( Type& orValue, const uno::Reference< beans::XPropertySet >& rxPropSet, const OUString& rPropName )
+{
+ try
+ {
+ return rxPropSet->getPropertyValue( rPropName ) >>= orValue;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return false;
+}
+
+/** Rounds the passed value to a multiple of 0.75 and converts it to 1/100 mm.
+
+ @throws uno::RuntimeException
+*/
+sal_Int32 lclPointsToHmm( const uno::Any& rPoints )
+{
+ return std::round(o3tl::convert(::rtl::math::approxFloor(rPoints.get<double>() / 0.75) * 0.75,
+ o3tl::Length::pt, o3tl::Length::mm100));
+}
+
+} // namespace
+
+// Base implementations
+
+/** Container for a specific type of drawing object in a spreadsheet.
+
+ Derived classes provide all required functionality specific to the type of
+ shapes covered by the container.
+ */
+class ScVbaObjectContainer : public ::cppu::WeakImplHelper< container::XIndexAccess >
+{
+public:
+ /// @throws uno::RuntimeException
+ explicit ScVbaObjectContainer(
+ uno::Reference< XHelperInterface > xParent,
+ uno::Reference< uno::XComponentContext > xContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ const uno::Type& rVbaType );
+
+ /** Returns the VBA helper interface of the VBA collection object. */
+ const uno::Reference< XHelperInterface >& getParent() const { return mxParent; }
+ /** Returns the component context of the VBA collection object. */
+ const uno::Reference< uno::XComponentContext >& getContext() const { return mxContext; }
+ /** Returns the VBA type information of the objects in this container. */
+ const uno::Type& getVbaType() const { return maVbaType; }
+
+ /** Collects all shapes supported by this instance and inserts them into
+ the internal shape vector.
+
+ @throws uno::RuntimeException
+ */
+ void collectShapes();
+ /** Creates and returns a new UNO shape.
+
+ @throws uno::RuntimeException
+ */
+ uno::Reference< drawing::XShape > createShape( const awt::Point& rPos, const awt::Size& rSize );
+ /** Inserts the passed shape into the draw page and into this container, and returns its index in the draw page.
+
+ @throws uno::RuntimeException
+ */
+ sal_Int32 insertShape( const uno::Reference< drawing::XShape >& rxShape );
+ /** Creates and returns a new VBA implementation object for the passed shape.
+
+ @throws uno::RuntimeException
+ */
+ ::rtl::Reference< ScVbaSheetObjectBase > createVbaObject( const uno::Reference< drawing::XShape >& rxShape );
+ /** Creates and returns a new VBA implementation object for the passed shape in an Any.
+
+ @throws uno::RuntimeException
+ */
+ uno::Any createCollectionObject( const uno::Any& rSource );
+ /** Returns the VBA implementation object with the specified name.
+
+ @throws uno::RuntimeException
+ */
+ uno::Any getItemByStringIndex( const OUString& rIndex );
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+protected:
+ /** Derived classes return true, if the passed shape is supported by the instance. */
+ virtual bool implPickShape( const uno::Reference< drawing::XShape >& rxShape ) const = 0;
+ /** Derived classes create and return a new VBA implementation object for the passed shape.
+
+ @throws uno::RuntimeException
+ */
+ virtual rtl::Reference<ScVbaSheetObjectBase> implCreateVbaObject( const uno::Reference< drawing::XShape >& rxShape ) = 0;
+ /** Derived classes return the service name of the UNO shape. */
+ virtual OUString implGetShapeServiceName() const = 0;
+
+ /** Returns the shape name via 'Name' property of the UNO shape. May be overwritten.
+
+ @throws uno::RuntimeException
+ */
+ virtual OUString implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const;
+ /** Is called when a new UNO shape has been created but not yet inserted into the drawing page.
+
+ @throws uno::RuntimeException
+ */
+ virtual void implOnShapeCreated( const uno::Reference< drawing::XShape >& rxShape );
+
+protected:
+ uno::Reference< XHelperInterface > mxParent;
+ uno::Reference< uno::XComponentContext > mxContext;
+ uno::Reference< frame::XModel > mxModel;
+ uno::Reference< lang::XMultiServiceFactory > mxFactory;
+ uno::Reference< drawing::XShapes > mxShapes;
+
+private:
+ typedef ::std::vector< uno::Reference< drawing::XShape > > ShapeVector;
+ const uno::Type maVbaType;
+ ShapeVector maShapes;
+};
+
+ScVbaObjectContainer::ScVbaObjectContainer(
+ uno::Reference< XHelperInterface > xParent,
+ uno::Reference< uno::XComponentContext > xContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ const uno::Type& rVbaType ) :
+ mxParent(std::move( xParent )),
+ mxContext(std::move( xContext )),
+ mxModel( rxModel, uno::UNO_SET_THROW ),
+ mxFactory( rxModel, uno::UNO_QUERY_THROW ),
+ maVbaType( rVbaType )
+{
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupp( rxSheet, uno::UNO_QUERY_THROW );
+ mxShapes.set( xDrawPageSupp->getDrawPage(), uno::UNO_QUERY_THROW );
+}
+
+void ScVbaObjectContainer::collectShapes()
+{
+ maShapes.clear();
+ for( sal_Int32 nIndex = 0, nCount = mxShapes->getCount(); nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< drawing::XShape > xShape( mxShapes->getByIndex( nIndex ), uno::UNO_QUERY_THROW );
+ if( implPickShape( xShape ) )
+ maShapes.push_back( xShape );
+ }
+}
+
+uno::Reference< drawing::XShape > ScVbaObjectContainer::createShape( const awt::Point& rPos, const awt::Size& rSize )
+{
+ uno::Reference< drawing::XShape > xShape( mxFactory->createInstance( implGetShapeServiceName() ), uno::UNO_QUERY_THROW );
+ xShape->setPosition( rPos );
+ xShape->setSize( rSize );
+ implOnShapeCreated( xShape );
+ return xShape;
+}
+
+sal_Int32 ScVbaObjectContainer::insertShape( const uno::Reference< drawing::XShape >& rxShape )
+{
+ mxShapes->add( rxShape );
+ maShapes.push_back( rxShape );
+ return mxShapes->getCount() - 1;
+}
+
+::rtl::Reference< ScVbaSheetObjectBase > ScVbaObjectContainer::createVbaObject(
+ const uno::Reference< drawing::XShape >& rxShape )
+{
+ return implCreateVbaObject( rxShape );
+}
+
+uno::Any ScVbaObjectContainer::createCollectionObject( const uno::Any& rSource )
+{
+ uno::Reference< drawing::XShape > xShape( rSource, uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XSheetObject > xSheetObject( implCreateVbaObject( xShape ) );
+ return uno::Any( xSheetObject );
+}
+
+uno::Any ScVbaObjectContainer::getItemByStringIndex( const OUString& rIndex )
+{
+ auto aIt = std::find_if(maShapes.begin(), maShapes.end(),
+ [&rIndex, this](const ShapeVector::value_type& rxShape) { return rIndex == implGetShapeName( rxShape ); });
+ if (aIt != maShapes.end())
+ return createCollectionObject( uno::Any( *aIt ) );
+ throw uno::RuntimeException();
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL ScVbaObjectContainer::getCount()
+{
+ return static_cast< sal_Int32 >( maShapes.size() );
+}
+
+uno::Any SAL_CALL ScVbaObjectContainer::getByIndex( sal_Int32 nIndex )
+{
+ if( (0 <= nIndex) && (nIndex < getCount()) )
+ return uno::Any( maShapes[ static_cast< size_t >( nIndex ) ] );
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScVbaObjectContainer::getElementType()
+{
+ return cppu::UnoType<drawing::XShape>::get();
+}
+
+sal_Bool SAL_CALL ScVbaObjectContainer::hasElements()
+{
+ return !maShapes.empty();
+}
+
+// private
+
+OUString ScVbaObjectContainer::implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const
+{
+ uno::Reference< beans::XPropertySet > xPropSet( rxShape, uno::UNO_QUERY_THROW );
+ return xPropSet->getPropertyValue( "Name" ).get< OUString >();
+}
+
+void ScVbaObjectContainer::implOnShapeCreated( const uno::Reference< drawing::XShape >& /*rxShape*/ )
+{
+}
+
+namespace {
+
+class ScVbaObjectEnumeration : public SimpleEnumerationBase
+{
+public:
+ explicit ScVbaObjectEnumeration( const ScVbaObjectContainerRef& rxContainer );
+ virtual uno::Any createCollectionObject( const uno::Any& rSource ) override;
+
+private:
+ ScVbaObjectContainerRef mxContainer;
+};
+
+}
+
+ScVbaObjectEnumeration::ScVbaObjectEnumeration( const ScVbaObjectContainerRef& rxContainer ) :
+ SimpleEnumerationBase( rxContainer ),
+ mxContainer( rxContainer )
+{
+}
+
+uno::Any ScVbaObjectEnumeration::createCollectionObject( const uno::Any& rSource )
+{
+ return mxContainer->createCollectionObject( rSource );
+}
+
+ScVbaSheetObjectsBase::ScVbaSheetObjectsBase( const ScVbaObjectContainerRef& rxContainer ) :
+ ScVbaSheetObjects_BASE( rxContainer->getParent(), rxContainer->getContext(), rxContainer ),
+ mxContainer( rxContainer )
+{
+ mxContainer->collectShapes();
+}
+
+ScVbaSheetObjectsBase::~ScVbaSheetObjectsBase()
+{
+}
+
+void ScVbaSheetObjectsBase::collectShapes()
+{
+ mxContainer->collectShapes();
+}
+
+// XEnumerationAccess
+
+uno::Reference< container::XEnumeration > SAL_CALL ScVbaSheetObjectsBase::createEnumeration()
+{
+ return new ScVbaObjectEnumeration( mxContainer );
+}
+
+// XElementAccess
+
+uno::Type SAL_CALL ScVbaSheetObjectsBase::getElementType()
+{
+ return mxContainer->getVbaType();
+}
+
+// ScVbaCollectionBase
+
+uno::Any ScVbaSheetObjectsBase::createCollectionObject( const uno::Any& rSource )
+{
+ return mxContainer->createCollectionObject( rSource );
+}
+
+uno::Any ScVbaSheetObjectsBase::getItemByStringIndex( const OUString& rIndex )
+{
+ return mxContainer->getItemByStringIndex( rIndex );
+}
+
+// Graphic object containers supporting ooo.vba.excel.XGraphicObject
+
+ScVbaGraphicObjectsBase::ScVbaGraphicObjectsBase( const ScVbaObjectContainerRef& rxContainer ) :
+ ScVbaGraphicObjects_BASE( rxContainer )
+{
+}
+
+// XGraphicObjects
+
+uno::Any SAL_CALL ScVbaGraphicObjectsBase::Add( const uno::Any& rLeft, const uno::Any& rTop, const uno::Any& rWidth, const uno::Any& rHeight )
+{
+ /* Extract double values from passed Anys (the lclPointsToHmm() helper
+ function will throw a RuntimeException on any error), and convert from
+ points to 1/100 mm. */
+ awt::Point aPos( lclPointsToHmm( rLeft ), lclPointsToHmm( rTop ) );
+ awt::Size aSize( lclPointsToHmm( rWidth ), lclPointsToHmm( rHeight ) );
+ // TODO: translate coordinates for RTL sheets
+ if( (aPos.X < 0) || (aPos.Y < 0) || (aSize.Width <= 0) || (aSize.Height <= 0) )
+ throw uno::RuntimeException();
+
+ // create the UNO shape
+ uno::Reference< drawing::XShape > xShape( mxContainer->createShape( aPos, aSize ), uno::UNO_SET_THROW );
+ sal_Int32 nIndex = mxContainer->insertShape( xShape );
+
+ // create and return the VBA object
+ ::rtl::Reference< ScVbaSheetObjectBase > xVbaObject = mxContainer->createVbaObject( xShape );
+ xVbaObject->setDefaultProperties( nIndex );
+ return uno::Any( uno::Reference< excel::XSheetObject >( xVbaObject ) );
+}
+
+// Drawing controls
+
+namespace {
+
+class ScVbaControlContainer : public ScVbaObjectContainer
+{
+public:
+ /// @throws uno::RuntimeException
+ explicit ScVbaControlContainer(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ const uno::Type& rVbaType,
+ OUString aModelServiceName,
+ sal_Int16 /* css::form::FormComponentType */ eType );
+
+protected:
+ /// @throws uno::RuntimeException
+ uno::Reference< container::XIndexContainer > const & createForm();
+
+ virtual bool implPickShape( const uno::Reference< drawing::XShape >& rxShape ) const override;
+ virtual OUString implGetShapeServiceName() const override;
+ virtual bool implCheckProperties( const uno::Reference< beans::XPropertySet >& rxModelProps ) const;
+ virtual OUString implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const override;
+ virtual void implOnShapeCreated( const uno::Reference< drawing::XShape >& rxShape ) override;
+
+protected:
+ uno::Reference< container::XIndexContainer > mxFormIC;
+ OUString maModelServiceName;
+ sal_Int16 /* css::form::FormComponentType */ meType;
+};
+
+}
+
+ScVbaControlContainer::ScVbaControlContainer(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ const uno::Type& rVbaType,
+ OUString aModelServiceName,
+ sal_Int16 /* css::form::FormComponentType */ eType ) :
+ ScVbaObjectContainer( rxParent, rxContext, rxModel, rxSheet, rVbaType ),
+ maModelServiceName(std::move( aModelServiceName )),
+ meType( eType )
+{
+}
+
+uno::Reference< container::XIndexContainer > const & ScVbaControlContainer::createForm()
+{
+ if( !mxFormIC.is() )
+ {
+ uno::Reference< form::XFormsSupplier > xFormsSupp( mxShapes, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameContainer > xFormsNC( xFormsSupp->getForms(), uno::UNO_SET_THROW );
+ OUString aFormName = "Standard";
+ if( xFormsNC->hasByName( aFormName ) )
+ {
+ mxFormIC.set( xFormsNC->getByName( aFormName ), uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ uno::Reference< form::XForm > xForm( mxFactory->createInstance( "com.sun.star.form.component.Form" ), uno::UNO_QUERY_THROW );
+ xFormsNC->insertByName( aFormName, uno::Any( xForm ) );
+ mxFormIC.set( xForm, uno::UNO_QUERY_THROW );
+ }
+ }
+ return mxFormIC;
+}
+
+bool ScVbaControlContainer::implPickShape( const uno::Reference< drawing::XShape >& rxShape ) const
+{
+ try
+ {
+ uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xModelProps( xControlShape->getControl(), uno::UNO_QUERY_THROW );
+ sal_Int16 nClassId = -1;
+ return lclGetProperty( nClassId, xModelProps, "ClassId" ) &&
+ (nClassId == meType) && implCheckProperties( xModelProps );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return false;
+}
+
+OUString ScVbaControlContainer::implGetShapeServiceName() const
+{
+ return "com.sun.star.drawing.ControlShape";
+}
+
+bool ScVbaControlContainer::implCheckProperties( const uno::Reference< beans::XPropertySet >& /*rxModelProps*/ ) const
+{
+ return true;
+}
+
+OUString ScVbaControlContainer::implGetShapeName( const uno::Reference< drawing::XShape >& rxShape ) const
+{
+ uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW );
+ return uno::Reference< container::XNamed >( xControlShape->getControl(), uno::UNO_QUERY_THROW )->getName();
+}
+
+void ScVbaControlContainer::implOnShapeCreated( const uno::Reference< drawing::XShape >& rxShape )
+{
+ // passed shape must be a control shape
+ uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW );
+
+ // create the UNO control model
+ uno::Reference< form::XFormComponent > xFormComponent( mxFactory->createInstance( maModelServiceName ), uno::UNO_QUERY_THROW );
+ uno::Reference< awt::XControlModel > xControlModel( xFormComponent, uno::UNO_QUERY_THROW );
+
+ // insert the control model into the form and the shape
+ createForm();
+ mxFormIC->insertByIndex( mxFormIC->getCount(), uno::Any( xFormComponent ) );
+ xControlShape->setControl( xControlModel );
+}
+
+// Push button
+
+namespace {
+
+class ScVbaButtonContainer : public ScVbaControlContainer
+{
+ bool mbOptionButtons;
+public:
+ /// @throws uno::RuntimeException
+ explicit ScVbaButtonContainer(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ bool bOptionButtons);
+
+protected:
+ virtual rtl::Reference<ScVbaSheetObjectBase> implCreateVbaObject( const uno::Reference< drawing::XShape >& rxShape ) override;
+ virtual bool implCheckProperties( const uno::Reference< beans::XPropertySet >& rxModelProps ) const override;
+};
+
+}
+
+ScVbaButtonContainer::ScVbaButtonContainer(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ bool bOptionButtons ) :
+ ScVbaControlContainer(
+ rxParent, rxContext, rxModel, rxSheet,
+ cppu::UnoType<excel::XButton>::get(),
+ ( bOptionButtons ?
+ OUString( "com.sun.star.form.component.RadioButton" ) :
+ OUString( "com.sun.star.form.component.CommandButton" ) ),
+ ( bOptionButtons ?
+ form::FormComponentType::RADIOBUTTON :
+ form::FormComponentType::COMMANDBUTTON) ),
+ mbOptionButtons(bOptionButtons)
+{
+}
+
+rtl::Reference<ScVbaSheetObjectBase> ScVbaButtonContainer::implCreateVbaObject( const uno::Reference< drawing::XShape >& rxShape )
+{
+ uno::Reference< drawing::XControlShape > xControlShape( rxShape, uno::UNO_QUERY_THROW );
+ return new ScVbaButton( mxParent, mxContext, mxModel, createForm(), xControlShape );
+}
+
+bool ScVbaButtonContainer::implCheckProperties( const uno::Reference< beans::XPropertySet >& rxModelProps ) const
+{
+ if (mbOptionButtons)
+ return true;
+
+ // do not insert toggle buttons into the 'Buttons' collection
+ bool bToggle = false;
+ return lclGetProperty( bToggle, rxModelProps, "Toggle" ) && !bToggle;
+}
+
+ScVbaButtons::ScVbaButtons(
+ const uno::Reference< XHelperInterface >& rxParent,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< frame::XModel >& rxModel,
+ const uno::Reference< sheet::XSpreadsheet >& rxSheet,
+ bool bOptionButtons) :
+ ScVbaGraphicObjectsBase( new ScVbaButtonContainer( rxParent, rxContext, rxModel, rxSheet, bOptionButtons ) )
+{
+}
+
+VBAHELPER_IMPL_XHELPERINTERFACE( ScVbaButtons, "ooo.vba.excel.Buttons" )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbasheetobjects.hxx b/sc/source/ui/vba/vbasheetobjects.hxx
new file mode 100644
index 0000000000..38d2d1c8d6
--- /dev/null
+++ b/sc/source/ui/vba/vbasheetobjects.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XGraphicObjects.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star {
+ namespace container { class XEnumeration; }
+ namespace frame { class XModel; }
+ namespace sheet { class XSpreadsheet; }
+}
+
+class ScVbaObjectContainer;
+typedef ::rtl::Reference< ScVbaObjectContainer > ScVbaObjectContainerRef;
+
+typedef CollTestImplHelper< ov::XCollection > ScVbaSheetObjects_BASE;
+
+/** Base class for collections containing a specific type of drawing object
+ embedded in a sheet (worksheet, chart sheet, or dialog sheet).
+ */
+class ScVbaSheetObjectsBase : public ScVbaSheetObjects_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaSheetObjectsBase( const ScVbaObjectContainerRef& rxContainer );
+ virtual ~ScVbaSheetObjectsBase() override;
+
+ /** Updates the collection by fetching all shapes from the draw page.
+
+ @throws css::uno::RuntimeException
+ */
+ void collectShapes();
+
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+
+ // ScVbaCollectionBase
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& rSource ) override;
+ virtual css::uno::Any getItemByStringIndex( const OUString& rIndex ) override;
+
+protected:
+ ScVbaObjectContainerRef mxContainer;
+};
+
+typedef ::cppu::ImplInheritanceHelper< ScVbaSheetObjectsBase, ov::excel::XGraphicObjects > ScVbaGraphicObjects_BASE;
+
+/** Base class for collections containing a specific type of graphic object
+ from a sheet.
+ */
+class ScVbaGraphicObjectsBase : public ScVbaGraphicObjects_BASE
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaGraphicObjectsBase( const ScVbaObjectContainerRef& rxContainer );
+
+ // XGraphicObjects
+ virtual css::uno::Any SAL_CALL Add(
+ const css::uno::Any& rLeft,
+ const css::uno::Any& rTop,
+ const css::uno::Any& rWidth,
+ const css::uno::Any& rHeight ) override;
+};
+
+/** Collection containing all button controls from a sheet (not ActiveX controls). */
+class ScVbaButtons : public ScVbaGraphicObjectsBase
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScVbaButtons(
+ const css::uno::Reference< ov::XHelperInterface >& rxParent,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const css::uno::Reference< css::sheet::XSpreadsheet >& rxSheet,
+ bool bOptionButtons);
+
+ VBAHELPER_DECL_XHELPERINTERFACE
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbastyle.cxx b/sc/source/ui/vba/vbastyle.cxx
new file mode 100644
index 0000000000..4f839ade00
--- /dev/null
+++ b/sc/source/ui/vba/vbastyle.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbastyle.hxx"
+#include <basic/sberrors.hxx>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+constexpr OUString DISPLAYNAME = u"DisplayName"_ustr;
+
+uno::Reference< container::XNameAccess >
+ScVbaStyle::getStylesNameContainer( const uno::Reference< frame::XModel >& xModel )
+{
+ uno::Reference< style::XStyleFamiliesSupplier > xStyleSupplier( xModel, uno::UNO_QUERY_THROW);
+ uno::Reference< container::XNameAccess > xStylesAccess( xStyleSupplier->getStyleFamilies()->getByName("CellStyles"), uno::UNO_QUERY_THROW );
+ return xStylesAccess;
+}
+
+/// @throws script::BasicErrorException
+/// @throws uno::RuntimeException
+static uno::Reference< beans::XPropertySet >
+lcl_getStyleProps( const OUString& sStyleName, const uno::Reference< frame::XModel >& xModel )
+{
+
+ uno::Reference< beans::XPropertySet > xStyleProps( ScVbaStyle::getStylesNameContainer( xModel )->getByName( sStyleName ), uno::UNO_QUERY_THROW );
+ return xStyleProps;
+}
+
+void ScVbaStyle::initialise()
+{
+ if (!mxModel.is() )
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, u"XModel Interface could not be retrieved" );
+ uno::Reference< lang::XServiceInfo > xServiceInfo( mxPropertySet, uno::UNO_QUERY_THROW );
+ if ( !xServiceInfo->supportsService("com.sun.star.style.CellStyle") )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ mxStyle.set( mxPropertySet, uno::UNO_QUERY_THROW );
+
+ uno::Reference< style::XStyleFamiliesSupplier > xStyleSupplier( mxModel, uno::UNO_QUERY_THROW );
+ mxStyleFamilyNameContainer.set( ScVbaStyle::getStylesNameContainer( mxModel ), uno::UNO_QUERY_THROW );
+
+}
+
+ScVbaStyle::ScVbaStyle( const uno::Reference< ov::XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ const OUString& sStyleName, const uno::Reference< frame::XModel >& _xModel )
+ : ScVbaStyle_BASE( xParent, xContext, lcl_getStyleProps( sStyleName, _xModel ), _xModel, false )
+{
+ try
+ {
+ initialise();
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+ScVbaStyle::ScVbaStyle( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< beans::XPropertySet >& _xPropertySet,
+ const uno::Reference< frame::XModel >& _xModel )
+ : ScVbaStyle_BASE( xParent, xContext, _xPropertySet, _xModel, false )
+{
+ try
+ {
+ initialise();
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+sal_Bool SAL_CALL
+ScVbaStyle::BuiltIn()
+{
+ return !mxStyle->isUserDefined();
+
+}
+void SAL_CALL
+ScVbaStyle::setName( const OUString& Name )
+{
+ mxStyle->setName(Name);
+}
+
+OUString SAL_CALL
+ScVbaStyle::getName()
+{
+ return mxStyle->getName();
+}
+
+void SAL_CALL
+ScVbaStyle::setNameLocal( const OUString& NameLocal )
+{
+ try
+ {
+ mxPropertySet->setPropertyValue(DISPLAYNAME, uno::Any( NameLocal ) );
+ }
+ catch (const uno::Exception& e)
+ {
+ DebugHelper::basicexception(e);
+ }
+}
+
+OUString SAL_CALL
+ScVbaStyle::getNameLocal()
+{
+ OUString sName;
+ try
+ {
+ mxPropertySet->getPropertyValue(DISPLAYNAME) >>= sName;
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {} );
+ }
+ return sName;
+}
+
+void SAL_CALL
+ScVbaStyle::Delete()
+{
+ try
+ {
+ mxStyleFamilyNameContainer->removeByName(mxStyle->getName());
+ }
+ catch (const uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+void SAL_CALL
+ScVbaStyle::setMergeCells( const uno::Any& /*MergeCells*/ )
+{
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+}
+
+uno::Any SAL_CALL
+ScVbaStyle::getMergeCells( )
+{
+ DebugHelper::basicexception(ERRCODE_BASIC_NOT_IMPLEMENTED, {});
+ return uno::Any();
+}
+
+OUString
+ScVbaStyle::getServiceImplName()
+{
+ return "ScVbaStyle";
+}
+
+uno::Sequence< OUString >
+ScVbaStyle::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.XStyle"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbastyle.hxx b/sc/source/ui/vba/vbastyle.hxx
new file mode 100644
index 0000000000..cc002ce6ff
--- /dev/null
+++ b/sc/source/ui/vba/vbastyle.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ooo/vba/excel/XStyle.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include "vbaformat.hxx"
+
+typedef ScVbaFormat< ov::excel::XStyle > ScVbaStyle_BASE;
+
+class ScVbaStyle final : public ScVbaStyle_BASE
+{
+ css::uno::Reference< css::style::XStyle > mxStyle;
+ css::uno::Reference< css::container::XNameContainer > mxStyleFamilyNameContainer;
+ /// @throws css::uno::RuntimeException
+ /// @throws css::script::BasicErrorException
+ void initialise();
+public:
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ ScVbaStyle( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const OUString& sStyleName, const css::uno::Reference< css::frame::XModel >& _xModel );
+ /// @throws css::script::BasicErrorException
+ /// @throws css::uno::RuntimeException
+ ScVbaStyle( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::beans::XPropertySet >& _xPropertySet, const css::uno::Reference< css::frame::XModel >& _xModel );
+ /// @throws css::uno::RuntimeException
+ static css::uno::Reference< css::container::XNameAccess > getStylesNameContainer( const css::uno::Reference< css::frame::XModel >& xModel );
+ virtual css::uno::Reference< ov::XHelperInterface > thisHelperIface() override { return this; };
+ // XStyle Methods
+ virtual sal_Bool SAL_CALL BuiltIn() override;
+ virtual void SAL_CALL setName( const OUString& Name ) override;
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setNameLocal( const OUString& NameLocal ) override;
+ virtual OUString SAL_CALL getNameLocal() override;
+ virtual void SAL_CALL Delete() override;
+ // XFormat
+ virtual void SAL_CALL setMergeCells( const css::uno::Any& MergeCells ) override;
+ virtual css::uno::Any SAL_CALL getMergeCells( ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbastyles.cxx b/sc/source/ui/vba/vbastyles.cxx
new file mode 100644
index 0000000000..6481e8911f
--- /dev/null
+++ b/sc/source/ui/vba/vbastyles.cxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbastyles.hxx"
+#include "vbastyle.hxx"
+#include <basic/sberrors.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <ooo/vba/excel/XRange.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <utility>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+static css::uno::Any
+lcl_createAPIStyleToVBAObject( const css::uno::Any& aObject, const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< frame::XModel >& xModel )
+{
+ uno::Reference< beans::XPropertySet > xStyleProps( aObject, uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XStyle > xStyle( new ScVbaStyle( xParent, xContext, xStyleProps, xModel ) );
+ return uno::Any( xStyle );
+}
+
+ScVbaStyles::ScVbaStyles( const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< css::uno::XComponentContext > & xContext,
+ const uno::Reference< frame::XModel >& xModel )
+: ScVbaStyles_BASE( xParent,
+ xContext,
+ uno::Reference< container::XIndexAccess >( ScVbaStyle::getStylesNameContainer( xModel ), uno::UNO_QUERY_THROW ) ),
+ mxModel( xModel )
+{
+ try
+ {
+ mxMSF.set( mxModel, uno::UNO_QUERY_THROW );
+ mxNameContainerCellStyles.set( m_xNameAccess, uno::UNO_QUERY_THROW );
+ }
+ catch (uno::Exception& )
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+uno::Sequence< OUString >
+ScVbaStyles::getStyleNames()
+{
+ return mxNameContainerCellStyles->getElementNames();
+}
+
+uno::Any
+ScVbaStyles::createCollectionObject(const uno::Any& aObject)
+{
+ return lcl_createAPIStyleToVBAObject( aObject, mxParent, mxContext, mxModel );
+}
+
+uno::Type SAL_CALL
+ScVbaStyles::getElementType()
+{
+ return cppu::UnoType<excel::XStyle>::get();
+}
+
+namespace {
+
+class EnumWrapper : public EnumerationHelper_BASE
+{
+ uno::Reference<container::XIndexAccess > m_xIndexAccess;
+ uno::Reference<XHelperInterface > m_xParent;
+ uno::Reference<uno::XComponentContext > m_xContext;
+ uno::Reference<frame::XModel > m_xModel;
+
+ sal_Int32 nIndex;
+public:
+ EnumWrapper( uno::Reference< container::XIndexAccess > xIndexAccess, uno::Reference<XHelperInterface > xParent, uno::Reference<uno::XComponentContext > xContext, uno::Reference<frame::XModel > xModel ) : m_xIndexAccess(std::move( xIndexAccess )), m_xParent(std::move( xParent )), m_xContext(std::move( xContext )), m_xModel(std::move( xModel )), nIndex( 0 ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( nIndex < m_xIndexAccess->getCount() );
+ }
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ try
+ {
+ if ( nIndex < m_xIndexAccess->getCount() )
+ return lcl_createAPIStyleToVBAObject( m_xIndexAccess->getByIndex( nIndex++ ), m_xParent, m_xContext, m_xModel );
+ }
+ catch (const container::NoSuchElementException&)
+ {
+ throw;
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ throw;
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception& e)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::lang::WrappedTargetException(
+ "wrapped Exception " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), a);
+ }
+ throw container::NoSuchElementException();
+ }
+};
+
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+ScVbaStyles::createEnumeration()
+{
+ return new EnumWrapper( m_xIndexAccess, mxParent, mxContext, mxModel );
+}
+
+uno::Reference< excel::XStyle > SAL_CALL
+ScVbaStyles::Add( const OUString& _sName, const uno::Any& _aBasedOn )
+{
+ uno::Reference< excel::XStyle > aRet;
+ try
+ {
+ OUString sParentCellStyleName("Default");
+ if ( _aBasedOn.hasValue() )
+ {
+ uno::Reference< excel::XRange > oRange;
+ if ( _aBasedOn >>= oRange)
+ {
+ uno::Reference< excel::XStyle > oStyle( oRange->getStyle(), uno::UNO_QUERY_THROW );
+ sParentCellStyleName = oStyle->getName();
+ }
+ else
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_BAD_ARGUMENT, {});
+ }
+ }
+
+ uno::Reference< style::XStyle > xStyle( mxMSF->createInstance("com.sun.star.style.CellStyle"), uno::UNO_QUERY_THROW );
+
+ if (!mxNameContainerCellStyles->hasByName(_sName))
+ {
+ mxNameContainerCellStyles->insertByName(_sName, uno::Any( xStyle) );
+ }
+ if (sParentCellStyleName != "Default")
+ {
+ xStyle->setParentStyle( sParentCellStyleName );
+ }
+ aRet.set( Item( uno::Any( _sName ), uno::Any() ), uno::UNO_QUERY_THROW );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+ return aRet;
+}
+
+void
+ScVbaStyles::Delete(const OUString& _sStyleName)
+{
+ try
+ {
+ if (mxNameContainerCellStyles->hasByName( _sStyleName ) )
+ mxNameContainerCellStyles->removeByName( _sStyleName );
+ }
+ catch (const uno::Exception&)
+ {
+ DebugHelper::basicexception(ERRCODE_BASIC_METHOD_FAILED, {});
+ }
+}
+
+OUString
+ScVbaStyles::getServiceImplName()
+{
+ return "ScVbaStyles";
+}
+
+uno::Sequence< OUString >
+ScVbaStyles::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.XStyles"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbastyles.hxx b/sc/source/ui/vba/vbastyles.hxx
new file mode 100644
index 0000000000..78990bca96
--- /dev/null
+++ b/sc/source/ui/vba/vbastyles.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 .
+ */
+#pragma once
+#include <ooo/vba/excel/XStyles.hpp>
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::lang { class XMultiServiceFactory; }
+namespace com::sun::star::container { class XNameContainer; }
+
+typedef CollTestImplHelper< ov::excel::XStyles > ScVbaStyles_BASE;
+class ScVbaStyles: public ScVbaStyles_BASE
+{
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::lang::XMultiServiceFactory > mxMSF;
+ css::uno::Reference< css::container::XNameContainer > mxNameContainerCellStyles;
+public:
+ /// @throws css::script::BasicErrorException
+ ScVbaStyles( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::frame::XModel >& xModel );
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< OUString > getStyleNames();
+ /// @throws css::script::BasicErrorException
+ void Delete(const OUString& _sStyleName);
+ // XStyles
+ virtual css::uno::Reference< ov::excel::XStyle > SAL_CALL Add( const OUString& Name, const css::uno::Any& BasedOn ) override;
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+ virtual css::uno::Any createCollectionObject(const css::uno::Any&) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbatextboxshape.cxx b/sc/source/ui/vba/vbatextboxshape.cxx
new file mode 100644
index 0000000000..66a85e49e2
--- /dev/null
+++ b/sc/source/ui/vba/vbatextboxshape.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 "excelvbahelper.hxx"
+#include "vbatextboxshape.hxx"
+#include "vbacharacters.hxx"
+#include <com/sun/star/text/XSimpleText.hpp>
+#include <docsh.hxx>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+ScVbaTextBoxShape::ScVbaTextBoxShape( const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< drawing::XShape >& xShape, const uno::Reference< drawing::XShapes >& xShapes, const uno::Reference< frame::XModel >& xModel ) : TextBoxShapeImpl_BASE( uno::Reference< XHelperInterface >(), xContext, xShape, xShapes, xModel, ScVbaShape::getType( xShape ) )
+{
+ m_xTextRange.set( xShape , uno::UNO_QUERY_THROW );
+}
+
+OUString SAL_CALL
+ScVbaTextBoxShape::getText()
+{
+ return m_xTextRange->getString();
+}
+
+void SAL_CALL
+ScVbaTextBoxShape::setText( const OUString& _text )
+{
+ m_xTextRange->setString( _text );
+}
+
+uno::Reference< excel::XCharacters > SAL_CALL
+ScVbaTextBoxShape::characters( const uno::Any& Start, const uno::Any& Length )
+{
+ ScDocShell* pDocShell = excel::getDocShell( m_xModel );
+ ScDocument* pDoc = pDocShell ? &pDocShell->GetDocument() : nullptr;
+
+ if ( !pDoc )
+ throw uno::RuntimeException("Failed to access document from shell" );
+ uno::Reference< text::XSimpleText > xSimple( m_xTextRange, uno::UNO_QUERY_THROW );
+
+ ScVbaPalette aPalette( pDoc->GetDocumentShell() );
+ return new ScVbaCharacters( this, mxContext, aPalette, xSimple, Start, Length, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbatextboxshape.hxx b/sc/source/ui/vba/vbatextboxshape.hxx
new file mode 100644
index 0000000000..c97fca8d8a
--- /dev/null
+++ b/sc/source/ui/vba/vbatextboxshape.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 <cppuhelper/implbase.hxx>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <ooo/vba/msforms/XTextBoxShape.hpp>
+#include <vbahelper/vbashape.hxx>
+
+typedef cppu::ImplInheritanceHelper< ScVbaShape, ov::msforms::XTextBoxShape > TextBoxShapeImpl_BASE;
+
+class ScVbaTextBoxShape : public TextBoxShapeImpl_BASE
+{
+ css::uno::Reference< css::text::XTextRange > m_xTextRange;
+public:
+ ScVbaTextBoxShape( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::drawing::XShapes >& xShapes, const css::uno::Reference< css::frame::XModel >& xModel );
+
+ // Attributes
+ virtual OUString SAL_CALL getText() override;
+ virtual void SAL_CALL setText( const OUString& _text ) override;
+ virtual css::uno::Reference< ov::excel::XCharacters > SAL_CALL characters( const css::uno::Any& Start, const css::uno::Any& Length ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbatextframe.cxx b/sc/source/ui/vba/vbatextframe.cxx
new file mode 100644
index 0000000000..9f921a8fa1
--- /dev/null
+++ b/sc/source/ui/vba/vbatextframe.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 <com/sun/star/drawing/XShape.hpp>
+#include <sfx2/objsh.hxx>
+#include "vbatextframe.hxx"
+#include "vbacharacters.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+ScVbaTextFrame::ScVbaTextFrame( uno::Sequence< uno::Any> const & args, uno::Reference< uno::XComponentContext> const & xContext ) : ScVbaTextFrame_BASE( getXSomethingFromArgs< XHelperInterface >( args, 0 ), xContext, getXSomethingFromArgs< drawing::XShape >( args, 1, false ) )
+{
+}
+
+// Methods
+uno::Any SAL_CALL
+ScVbaTextFrame::Characters()
+{
+ uno::Reference< text::XSimpleText > xSimpleText( m_xShape, uno::UNO_QUERY_THROW );
+ ScVbaPalette aPalette( SfxObjectShell::Current() );
+ uno::Any aStart( sal_Int32( 1 ) );
+ uno::Any aLength(sal_Int32( -1 ) );
+ return uno::Any( uno::Reference< ov::excel::XCharacters >( new ScVbaCharacters( this, mxContext, aPalette, xSimpleText, aStart, aLength, true ) ) );
+}
+
+OUString
+ScVbaTextFrame::getServiceImplName()
+{
+ return "ScVbaTextFrame";
+}
+
+uno::Sequence< OUString >
+ScVbaTextFrame::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.TextFrame"
+ };
+ return aServiceNames;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+ScVbaTextFrame_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &arguments)
+{
+ return cppu::acquire(new ScVbaTextFrame(arguments, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbatextframe.hxx b/sc/source/ui/vba/vbatextframe.hxx
new file mode 100644
index 0000000000..eb7a59a931
--- /dev/null
+++ b/sc/source/ui/vba/vbatextframe.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 <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XTextFrame.hpp>
+#include <vbahelper/vbatextframe.hxx>
+
+//typedef InheritedHelperInterfaceWeakImpl< ov::excel::XTextFrame > ScVbaTextFrame_BASE;
+typedef cppu::ImplInheritanceHelper<VbaTextFrame, ov::excel::XTextFrame> ScVbaTextFrame_BASE;
+
+class ScVbaTextFrame : public ScVbaTextFrame_BASE
+{
+public:
+ /// @throws css::lang::IllegalArgumentException
+ ScVbaTextFrame(css::uno::Sequence<css::uno::Any> const& aArgs,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext);
+ // Methods
+ virtual css::uno::Any SAL_CALL Characters() override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbatitle.hxx b/sc/source/ui/vba/vbatitle.hxx
new file mode 100644
index 0000000000..bb017f0bfb
--- /dev/null
+++ b/sc/source/ui/vba/vbatitle.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 .
+ */
+#pragma once
+
+#include <utility>
+#include <vbahelper/vbahelperinterface.hxx>
+#include "vbainterior.hxx"
+#include "vbafont.hxx"
+#include "vbapalette.hxx"
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/script/BasicErrorException.hpp>
+#include <basic/sberrors.hxx>
+#include <memory>
+
+template< typename... Ifc >
+class TitleImpl : public InheritedHelperInterfaceImpl< Ifc... >
+{
+typedef InheritedHelperInterfaceImpl< Ifc... > BaseClass;
+
+ css::uno::Reference< css::drawing::XShape > xTitleShape;
+ css::uno::Reference< css::beans::XPropertySet > xShapePropertySet;
+ ov::ShapeHelper maShapeHelper;
+ ScVbaPalette m_Palette;
+public:
+ TitleImpl( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::drawing::XShape > _xTitleShape )
+ : BaseClass( xParent, xContext ),
+ xTitleShape(std::move( _xTitleShape )),
+ xShapePropertySet( xTitleShape, css::uno::UNO_QUERY_THROW ),
+ maShapeHelper( xTitleShape ),
+ m_Palette(nullptr)
+ {
+ }
+ css::uno::Reference< ov::excel::XInterior > SAL_CALL Interior( ) override
+ {
+ // #TODO find out what the proper parent should be
+ // leaving as set by the helperapi for the moment
+ // #TODO we really need the ScDocument to pass to ScVbaInterior
+ // otherwise attempts to access the palette will fail
+ return new ScVbaInterior( BaseClass::mxParent, BaseClass::mxContext, xShapePropertySet );
+ }
+ css::uno::Reference< ov::excel::XFont > SAL_CALL Font( ) override
+ {
+ // #TODO find out what the proper parent should be
+ // leaving as set by the helperapi for the moment
+ return new ScVbaFont( BaseClass::mxParent, BaseClass::mxContext, m_Palette, xShapePropertySet );
+
+ }
+ void SAL_CALL setText( const OUString& Text ) override
+ {
+ try
+ {
+ xShapePropertySet->setPropertyValue("String", css::uno::Any( Text ));
+ }
+ catch ( css::uno::Exception& )
+ {
+ throw css::script::BasicErrorException( OUString(), css::uno::Reference< css::uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ }
+ OUString SAL_CALL getText( ) override
+ {
+ OUString sText;
+ try
+ {
+ xShapePropertySet->getPropertyValue("String") >>= sText;
+ }
+ catch ( css::uno::Exception& )
+ {
+ throw css::script::BasicErrorException( OUString(), css::uno::Reference< css::uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return sText;
+ }
+
+ void SAL_CALL setTop( double Top ) override
+ {
+ maShapeHelper.setTop( Top );
+ }
+ double SAL_CALL getTop( ) override
+ {
+ return maShapeHelper.getTop();
+ }
+ void SAL_CALL setLeft( double Left ) override
+ {
+ maShapeHelper.setLeft( Left );
+ }
+ double SAL_CALL getLeft( ) override
+ {
+ return maShapeHelper.getLeft();
+ }
+ void SAL_CALL setOrientation( ::sal_Int32 _nOrientation ) override
+ {
+ try
+ {
+ xShapePropertySet->setPropertyValue("TextRotation", css::uno::Any(_nOrientation*100));
+ }
+ catch (css::uno::Exception& )
+ {
+ throw css::script::BasicErrorException( OUString(), css::uno::Reference< css::uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ }
+ ::sal_Int32 SAL_CALL getOrientation( ) override
+ {
+ sal_Int32 nSOOrientation = 0;
+ try
+ {
+ xShapePropertySet->getPropertyValue("TextRotation") >>= nSOOrientation;
+ }
+ catch (css::uno::Exception& )
+ {
+ throw css::script::BasicErrorException( OUString(), css::uno::Reference< css::uno::XInterface >(), sal_uInt32(ERRCODE_BASIC_METHOD_FAILED), OUString() );
+ }
+ return static_cast< sal_Int32 >(nSOOrientation / 100) ;
+ }
+// XHelperInterface
+ OUString getServiceImplName() override
+ {
+ return "TitleImpl";
+ }
+ css::uno::Sequence< OUString > getServiceNames() override
+ {
+ static const css::uno::Sequence< OUString > aServiceNames{ "ooo.vba.excel.XTitle" };
+ return aServiceNames;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbavalidation.cxx b/sc/source/ui/vba/vbavalidation.cxx
new file mode 100644
index 0000000000..a30b43fe73
--- /dev/null
+++ b/sc/source/ui/vba/vbavalidation.cxx
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "vbavalidation.hxx"
+#include "vbaformatcondition.hxx"
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <ooo/vba/excel/XlDVType.hpp>
+#include <ooo/vba/excel/XlDVAlertStyle.hpp>
+
+#include <unonames.hxx>
+#include <rangelst.hxx>
+#include "excelvbahelper.hxx"
+#include "vbarange.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+static void
+lcl_setValidationProps( const uno::Reference< table::XCellRange >& xRange, const uno::Reference< beans::XPropertySet >& xProps )
+{
+ uno::Reference< beans::XPropertySet > xRangeProps( xRange, uno::UNO_QUERY_THROW );
+ xRangeProps->setPropertyValue( SC_UNONAME_VALIDAT , uno::Any( xProps ) );
+}
+
+static uno::Reference< beans::XPropertySet >
+lcl_getValidationProps( const uno::Reference< table::XCellRange >& xRange )
+{
+ uno::Reference< beans::XPropertySet > xProps( xRange, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xValProps;
+ xValProps.set( xProps->getPropertyValue( SC_UNONAME_VALIDAT ), uno::UNO_QUERY_THROW );
+ return xValProps;
+}
+
+sal_Bool SAL_CALL
+ScVbaValidation::getIgnoreBlank()
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ bool bBlank = false;
+ xProps->getPropertyValue( SC_UNONAME_IGNOREBL ) >>= bBlank;
+ return bBlank;
+}
+
+void SAL_CALL
+ScVbaValidation::setIgnoreBlank( sal_Bool _ignoreblank )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ xProps->setPropertyValue( SC_UNONAME_IGNOREBL, uno::Any( _ignoreblank ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+sal_Bool SAL_CALL
+ScVbaValidation::getInCellDropdown()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ sal_Int32 nShowList = 0;
+ xProps->getPropertyValue( SC_UNONAME_SHOWLIST ) >>= nShowList;
+ return nShowList != 0;
+}
+
+void SAL_CALL
+ScVbaValidation::setInCellDropdown( sal_Bool _incelldropdown )
+{
+ sal_Int32 nDropDown = 0;
+ if ( _incelldropdown )
+ nDropDown = 1;
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps(m_xRange) );
+ xProps->setPropertyValue( SC_UNONAME_SHOWLIST, uno::Any( nDropDown ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+sal_Bool SAL_CALL
+ScVbaValidation::getShowInput()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ bool bShowInput = false;
+ xProps->getPropertyValue( SC_UNONAME_SHOWINP ) >>= bShowInput;
+ return bShowInput;
+}
+
+void SAL_CALL
+ScVbaValidation:: setShowInput( sal_Bool _showinput )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps(m_xRange) );
+ xProps->setPropertyValue( SC_UNONAME_IGNOREBL, uno::Any( _showinput ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+sal_Bool SAL_CALL
+ScVbaValidation::getShowError()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ bool bShowError = false;
+ xProps->getPropertyValue( SC_UNONAME_SHOWERR ) >>= bShowError;
+ return bShowError;
+}
+
+void SAL_CALL
+ScVbaValidation::setShowError( sal_Bool _showerror )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ xProps->setPropertyValue( SC_UNONAME_SHOWERR, uno::Any( _showerror ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+OUString SAL_CALL
+ScVbaValidation::getErrorTitle()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ OUString sErrorTitle;
+ xProps->getPropertyValue( SC_UNONAME_ERRTITLE ) >>= sErrorTitle;
+ return sErrorTitle;
+}
+
+void
+ScVbaValidation::setErrorTitle( const OUString& _errormessage )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ xProps->setPropertyValue( SC_UNONAME_ERRTITLE, uno::Any( _errormessage ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+OUString SAL_CALL
+ScVbaValidation::getInputMessage()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ OUString sMsg;
+ xProps->getPropertyValue( SC_UNONAME_INPMESS ) >>= sMsg;
+ return sMsg;
+}
+
+void SAL_CALL
+ScVbaValidation::setInputMessage( const OUString& _inputmessage )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ xProps->setPropertyValue( SC_UNONAME_INPMESS, uno::Any( _inputmessage ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+OUString SAL_CALL
+ScVbaValidation::getInputTitle()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ OUString sString;
+ xProps->getPropertyValue( SC_UNONAME_INPTITLE ) >>= sString;
+ return sString;
+}
+
+void SAL_CALL
+ScVbaValidation::setInputTitle( const OUString& _inputtitle )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ xProps->setPropertyValue( SC_UNONAME_INPTITLE, uno::Any( _inputtitle ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+OUString SAL_CALL
+ScVbaValidation::getErrorMessage()
+{
+ uno::Reference< beans::XPropertySet > xProps = lcl_getValidationProps( m_xRange );
+ OUString sString;
+ xProps->getPropertyValue( SC_UNONAME_ERRMESS ) >>= sString;
+ return sString;
+}
+
+void SAL_CALL
+ScVbaValidation::setErrorMessage( const OUString& _errormessage )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ xProps->setPropertyValue( SC_UNONAME_ERRMESS, uno::Any( _errormessage ) );
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+void SAL_CALL
+ScVbaValidation::Delete( )
+{
+ OUString sBlank;
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ uno::Reference< sheet::XSheetCondition > xCond( xProps, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue( SC_UNONAME_IGNOREBL, uno::Any( true ) );
+ xProps->setPropertyValue( SC_UNONAME_SHOWINP, uno::Any( true ) );
+ xProps->setPropertyValue( SC_UNONAME_SHOWERR, uno::Any( true ) );
+ xProps->setPropertyValue( SC_UNONAME_ERRTITLE, uno::Any( sBlank ) );
+ xProps->setPropertyValue( SC_UNONAME_INPMESS, uno::Any( sBlank) );
+ xProps->setPropertyValue( SC_UNONAME_ERRALSTY, uno::Any( sheet::ValidationAlertStyle_STOP) );
+ xProps->setPropertyValue( SC_UNONAME_TYPE, uno::Any( sheet::ValidationType_ANY ) );
+ xCond->setFormula1( sBlank );
+ xCond->setFormula2( sBlank );
+ xCond->setOperator( sheet::ConditionOperator_NONE );
+
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+// Fix the defect that validation cannot work when the input should be limited between a lower bound and an upper bound
+void SAL_CALL
+ScVbaValidation::Add( const uno::Any& Type, const uno::Any& AlertStyle, const uno::Any& Operator, const uno::Any& Formula1, const uno::Any& Formula2 )
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ uno::Reference< sheet::XSheetCondition > xCond( xProps, uno::UNO_QUERY_THROW );
+
+ sheet::ValidationType nValType = sheet::ValidationType_ANY;
+ xProps->getPropertyValue( SC_UNONAME_TYPE ) >>= nValType;
+ if ( nValType != sheet::ValidationType_ANY )
+ throw uno::RuntimeException("validation object already exists" );
+ sal_Int32 nType = -1;
+ if ( !Type.hasValue() || !( Type >>= nType ) )
+ throw uno::RuntimeException("missing required param" );
+
+ Delete(); // set up defaults
+ OUString sFormula1;
+ Formula1 >>= sFormula1;
+ OUString sFormula2;
+ Formula2 >>= sFormula2;
+ switch ( nType )
+ {
+ case excel::XlDVType::xlValidateList:
+ {
+ // for validate list
+ // at least formula1 is required
+ if ( !Formula1.hasValue() )
+ throw uno::RuntimeException("missing param" );
+ nValType = sheet::ValidationType_LIST;
+ xProps->setPropertyValue( SC_UNONAME_TYPE, uno::Any(nValType ));
+ // #TODO validate required params
+ // #TODO need to correct the ';' delimited formula on get/set
+ break;
+ }
+ case excel::XlDVType::xlValidateWholeNumber:
+ nValType = sheet::ValidationType_WHOLE;
+ xProps->setPropertyValue( SC_UNONAME_TYPE, uno::Any(nValType ));
+ break;
+ default:
+ throw uno::RuntimeException("unsupported operation..." );
+ }
+
+ sheet::ValidationAlertStyle eStyle = sheet::ValidationAlertStyle_STOP;
+ sal_Int32 nVbaAlertStyle = excel::XlDVAlertStyle::xlValidAlertStop;
+ if ( AlertStyle.hasValue() && ( AlertStyle >>= nVbaAlertStyle ) )
+ {
+ switch( nVbaAlertStyle )
+ {
+ case excel::XlDVAlertStyle::xlValidAlertStop:
+ // yes I know it's already defaulted but safer to assume
+ // someone probably could change the code above
+ eStyle = sheet::ValidationAlertStyle_STOP;
+ break;
+ case excel::XlDVAlertStyle::xlValidAlertWarning:
+ eStyle = sheet::ValidationAlertStyle_WARNING;
+ break;
+ case excel::XlDVAlertStyle::xlValidAlertInformation:
+ eStyle = sheet::ValidationAlertStyle_INFO;
+ break;
+ default:
+ throw uno::RuntimeException("bad param..." );
+
+ }
+ }
+
+ xProps->setPropertyValue( SC_UNONAME_ERRALSTY, uno::Any( eStyle ) );
+
+ // i#108860: fix the defect that validation cannot work when the input
+ // should be limited between a lower bound and an upper bound
+ if ( Operator.hasValue() )
+ {
+ css::sheet::ConditionOperator conOperator = ScVbaFormatCondition::retrieveAPIOperator( Operator );
+ xCond->setOperator( conOperator );
+ }
+
+ if ( !sFormula1.isEmpty() )
+ xCond->setFormula1( sFormula1 );
+ if ( !sFormula2.isEmpty() )
+ xCond->setFormula2( sFormula2 );
+
+ lcl_setValidationProps( m_xRange, xProps );
+}
+
+OUString SAL_CALL
+ScVbaValidation::getFormula1()
+{
+ uno::Reference< sheet::XSheetCondition > xCond( lcl_getValidationProps( m_xRange ), uno::UNO_QUERY_THROW );
+ OUString sString = xCond->getFormula1();
+
+ ScRefFlags nFlags = ScRefFlags::ZERO;
+ ScRangeList aCellRanges;
+
+ ScDocShell* pDocSh = excel::GetDocShellFromRange( m_xRange );
+ // in calc validation formula is either a range or formula
+ // that results in range.
+ // In VBA both formula and address can have a leading '='
+ // in result of getFormula1, however it *seems* that a named range or
+ // real formula has to (or is expected to) have the '='
+ if ( pDocSh && !ScVbaRange::getCellRangesForAddress( nFlags, sString, pDocSh, aCellRanges, formula::FormulaGrammar::CONV_XL_A1, 0 ) )
+ sString = "=" + sString;
+ return sString;
+}
+
+OUString SAL_CALL
+ScVbaValidation::getFormula2()
+{
+ uno::Reference< sheet::XSheetCondition > xCond( lcl_getValidationProps( m_xRange ), uno::UNO_QUERY_THROW );
+ return xCond->getFormula2();
+}
+
+sal_Int32 SAL_CALL
+ScVbaValidation::getType()
+{
+ uno::Reference< beans::XPropertySet > xProps( lcl_getValidationProps( m_xRange ) );
+ sheet::ValidationType nValType = sheet::ValidationType_ANY;
+ xProps->getPropertyValue( SC_UNONAME_TYPE ) >>= nValType;
+ sal_Int32 nExcelType = excel::XlDVType::xlValidateList; // pick a default
+ if ( xProps.is() )
+ {
+ switch ( nValType )
+ {
+ case sheet::ValidationType_LIST:
+ nExcelType = excel::XlDVType::xlValidateList;
+ break;
+ case sheet::ValidationType_ANY: // not ANY not really a great match for anything I fear:-(
+ nExcelType = excel::XlDVType::xlValidateInputOnly;
+ break;
+ case sheet::ValidationType_CUSTOM:
+ nExcelType = excel::XlDVType::xlValidateCustom;
+ break;
+ case sheet::ValidationType_WHOLE:
+ nExcelType = excel::XlDVType::xlValidateWholeNumber;
+ break;
+ case sheet::ValidationType_DECIMAL:
+ nExcelType = excel::XlDVType::xlValidateDecimal;
+ break;
+ case sheet::ValidationType_DATE:
+ nExcelType = excel::XlDVType::xlValidateDate;
+ break;
+ case sheet::ValidationType_TIME:
+ nExcelType = excel::XlDVType::xlValidateTime;
+ break;
+ case sheet::ValidationType_TEXT_LEN:
+ nExcelType = excel::XlDVType::xlValidateTextLength;
+ break;
+ case sheet::ValidationType::ValidationType_MAKE_FIXED_SIZE:
+ default:
+ break;
+ }
+ }
+ return nExcelType;
+}
+
+OUString
+ScVbaValidation::getServiceImplName()
+{
+ return "ScVbaValidation";
+}
+
+uno::Sequence< OUString >
+ScVbaValidation::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Validation"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbavalidation.hxx b/sc/source/ui/vba/vbavalidation.hxx
new file mode 100644
index 0000000000..6395d6728a
--- /dev/null
+++ b/sc/source/ui/vba/vbavalidation.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XValidation.hpp>
+#include <utility>
+#include <vbahelper/vbahelperinterface.hxx>
+
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+typedef InheritedHelperInterfaceWeakImpl<ov::excel::XValidation > ValidationImpl_BASE;
+
+class ScVbaValidation : public ValidationImpl_BASE
+{
+ css::uno::Reference< css::table::XCellRange > m_xRange;
+
+public:
+ ScVbaValidation( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< css::table::XCellRange > xRange ) : ValidationImpl_BASE( xParent, xContext ), m_xRange(std::move( xRange)) {}
+ // Attributes
+ virtual sal_Bool SAL_CALL getIgnoreBlank() override;
+ virtual void SAL_CALL setIgnoreBlank( sal_Bool _ignoreblank ) override;
+ virtual sal_Bool SAL_CALL getInCellDropdown() override;
+ virtual void SAL_CALL setInCellDropdown( sal_Bool _incelldropdown ) override;
+ virtual sal_Bool SAL_CALL getShowInput() override;
+ virtual void SAL_CALL setShowInput( sal_Bool _showinput ) override;
+ virtual sal_Bool SAL_CALL getShowError() override;
+ virtual void SAL_CALL setShowError( sal_Bool _showerror ) override;
+ virtual OUString SAL_CALL getInputTitle() override;
+ virtual void SAL_CALL setInputTitle( const OUString& _inputtitle ) override;
+ virtual OUString SAL_CALL getErrorTitle() override;
+ virtual void SAL_CALL setErrorTitle( const OUString& _errortitle ) override;
+ virtual OUString SAL_CALL getInputMessage() override;
+ virtual void SAL_CALL setInputMessage( const OUString& _inputmessage ) override;
+ virtual OUString SAL_CALL getErrorMessage() override;
+ virtual void SAL_CALL setErrorMessage( const OUString& _errormessage ) override;
+ virtual OUString SAL_CALL getFormula1() override ;
+ virtual OUString SAL_CALL getFormula2() override;
+ virtual sal_Int32 SAL_CALL getType() override;
+ // Methods
+ virtual void SAL_CALL Delete( ) override;
+ virtual void SAL_CALL Add( const css::uno::Any& Type, const css::uno::Any& AlertStyle, const css::uno::Any& Operator, const css::uno::Any& Formula1, const css::uno::Any& Formula2 ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbawindow.cxx b/sc/source/ui/vba/vbawindow.cxx
new file mode 100644
index 0000000000..bd0ebd4635
--- /dev/null
+++ b/sc/source/ui/vba/vbawindow.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "excelvbahelper.hxx"
+#include "vbawindow.hxx"
+#include "vbaworksheets.hxx"
+#include "vbaworksheet.hxx"
+#include "vbaworkbook.hxx"
+#include "vbapane.hxx"
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XViewSplitable.hpp>
+#include <com/sun/star/sheet/XViewFreezable.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/view/DocumentZoomType.hpp>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <o3tl/safeint.hxx>
+#include <ooo/vba/excel/XApplication.hpp>
+#include <ooo/vba/excel/XlWindowState.hpp>
+#include <ooo/vba/excel/XlWindowView.hpp>
+#include <basic/sberrors.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <docuno.hxx>
+#include <sc.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <utility>
+#include <vcl/wrkwin.hxx>
+#include <unonames.hxx>
+#include <markdata.hxx>
+#include <unordered_map>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+using namespace ::ooo::vba::excel::XlWindowState;
+
+typedef std::unordered_map< OUString,
+SCTAB > NameIndexHash;
+
+typedef std::vector< uno::Reference< sheet::XSpreadsheet > > Sheets;
+
+typedef ::cppu::WeakImplHelper< container::XEnumerationAccess
+ , css::container::XIndexAccess
+ , css::container::XNameAccess
+ > SelectedSheets_BASE;
+
+namespace {
+
+class SelectedSheetsEnum : public ::cppu::WeakImplHelper< container::XEnumeration >
+{
+public:
+ uno::Reference< uno::XComponentContext > m_xContext;
+ Sheets m_sheets;
+ uno::Reference< frame::XModel > m_xModel;
+ Sheets::const_iterator m_it;
+
+ /// @throws uno::RuntimeException
+ SelectedSheetsEnum( uno::Reference< uno::XComponentContext > xContext, Sheets&& sheets, uno::Reference< frame::XModel > xModel )
+ : m_xContext(std::move( xContext )), m_sheets( std::move(sheets) ), m_xModel(std::move( xModel ))
+ {
+ m_it = m_sheets.begin();
+ }
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return m_it != m_sheets.end();
+ }
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( !hasMoreElements() )
+ {
+ throw container::NoSuchElementException();
+ }
+ // #FIXME needs ThisWorkbook as parent
+ return uno::Any( uno::Reference< excel::XWorksheet > ( new ScVbaWorksheet( uno::Reference< XHelperInterface >(), m_xContext, *(m_it++), m_xModel ) ) );
+ }
+
+};
+
+class SelectedSheetsEnumAccess : public SelectedSheets_BASE
+{
+ uno::Reference< uno::XComponentContext > m_xContext;
+ NameIndexHash namesToIndices;
+ Sheets sheets;
+ rtl::Reference< ScModelObj > m_xModel;
+public:
+ SelectedSheetsEnumAccess( uno::Reference< uno::XComponentContext > xContext, const uno::Reference< frame::XModel > & xModel ):m_xContext(std::move( xContext ))
+ {
+ ScModelObj* pModel = static_cast< ScModelObj* >( xModel.get() );
+ if ( !pModel )
+ throw uno::RuntimeException("Cannot obtain current document" );
+ m_xModel = pModel;
+ ScDocShell* pDocShell = static_cast<ScDocShell*>(pModel->GetEmbeddedObject());
+ if ( !pDocShell )
+ throw uno::RuntimeException("Cannot obtain docshell" );
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( !pViewShell )
+ throw uno::RuntimeException("Cannot obtain view shell" );
+
+ SCTAB nTabCount = pDocShell->GetDocument().GetTableCount();
+ SCTAB nIndex = 0;
+ const ScMarkData& rMarkData = pViewShell->GetViewData().GetMarkData();
+ sheets.reserve( nTabCount );
+ uno::Reference <container::XIndexAccess> xIndex( m_xModel->getSheets(), uno::UNO_QUERY_THROW );
+ for (const auto& rTab : rMarkData)
+ {
+ if (rTab >= nTabCount)
+ break;
+ uno::Reference< sheet::XSpreadsheet > xSheet( xIndex->getByIndex( rTab ), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNamed > xNamed( xSheet, uno::UNO_QUERY_THROW );
+ sheets.push_back( xSheet );
+ namesToIndices[ xNamed->getName() ] = nIndex++;
+ }
+
+ }
+
+ //XEnumerationAccess
+ virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override
+ {
+ return new SelectedSheetsEnum( m_xContext, std::vector(sheets), m_xModel );
+ }
+ // XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount( ) override
+ {
+ return sheets.size();
+ }
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ if ( Index < 0
+ || o3tl::make_unsigned( Index ) >= sheets.size() )
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any( sheets[ Index ] );
+ }
+
+ //XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override
+ {
+ return cppu::UnoType<excel::XWorksheet>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ return ( !sheets.empty() );
+ }
+
+ //XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ NameIndexHash::const_iterator it = namesToIndices.find( aName );
+ if ( it == namesToIndices.end() )
+ throw container::NoSuchElementException();
+ return uno::Any( sheets[ it->second ] );
+
+ }
+
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ return comphelper::mapKeysToSequence( namesToIndices );
+ }
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ NameIndexHash::const_iterator it = namesToIndices.find( aName );
+ return (it != namesToIndices.end());
+ }
+
+};
+
+}
+
+ScVbaWindow::ScVbaWindow(
+ const uno::Reference< XHelperInterface >& xParent,
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< frame::XModel >& xModel,
+ const uno::Reference< frame::XController >& xController ) :
+ WindowImpl_BASE( xParent, xContext, xModel, xController )
+{
+ init();
+}
+
+ScVbaWindow::ScVbaWindow(
+ const uno::Sequence< uno::Any >& args,
+ const uno::Reference< uno::XComponentContext >& xContext ) :
+ WindowImpl_BASE( args, xContext )
+{
+ init();
+}
+
+void
+ScVbaWindow::init()
+{
+ /* This method is called from the constructor, thus the own refcount is
+ still zero. The implementation of ActivePane() uses a UNO reference of
+ this (to set this window as parent of the pane object). This requires
+ the own refcount to be non-zero, otherwise this instance will be
+ destructed immediately! Guard the call to ActivePane() in try/catch to
+ not miss the decrementation of the reference count on exception. */
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ m_xPane = ActivePane();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+uno::Reference< beans::XPropertySet >
+ScVbaWindow::getControllerProps() const
+{
+ return uno::Reference< beans::XPropertySet >( getController(), uno::UNO_QUERY_THROW );
+}
+
+uno::Reference< beans::XPropertySet >
+ScVbaWindow::getFrameProps() const
+{
+ return uno::Reference< beans::XPropertySet >( getController()->getFrame(), uno::UNO_QUERY_THROW );
+}
+
+uno::Reference< awt::XDevice >
+ScVbaWindow::getDevice() const
+{
+ return uno::Reference< awt::XDevice >( getWindow(), uno::UNO_QUERY_THROW );
+}
+
+void
+ScVbaWindow::Scroll( const uno::Any& Down, const uno::Any& Up, const uno::Any& ToRight, const uno::Any& ToLeft, bool bLargeScroll )
+{
+ if( !m_xPane.is() )
+ throw uno::RuntimeException();
+ if( bLargeScroll )
+ m_xPane->LargeScroll( Down, Up, ToRight, ToLeft );
+ else
+ m_xPane->SmallScroll( Down, Up, ToRight, ToLeft );
+}
+
+void SAL_CALL
+ScVbaWindow::SmallScroll( const uno::Any& Down, const uno::Any& Up, const uno::Any& ToRight, const uno::Any& ToLeft )
+{
+ Scroll( Down, Up, ToRight, ToLeft, false );
+}
+
+void SAL_CALL
+ScVbaWindow::LargeScroll( const uno::Any& Down, const uno::Any& Up, const uno::Any& ToRight, const uno::Any& ToLeft )
+{
+ Scroll( Down, Up, ToRight, ToLeft, true );
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::SelectedSheets( const uno::Any& aIndex )
+{
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( new SelectedSheetsEnumAccess( mxContext, m_xModel ) );
+ // #FIXME needs a workbook as a parent
+ uno::Reference< excel::XWorksheets > xSheets( new ScVbaWorksheets( uno::Reference< XHelperInterface >(), mxContext, xEnumAccess, m_xModel ) );
+ if ( aIndex.hasValue() )
+ {
+ uno::Reference< XCollection > xColl( xSheets, uno::UNO_QUERY_THROW );
+ return xColl->Item( aIndex, uno::Any() );
+ }
+ return uno::Any( xSheets );
+}
+
+void SAL_CALL
+ScVbaWindow::ScrollWorkbookTabs( const uno::Any& /*Sheets*/, const uno::Any& /*Position*/ )
+{
+// #TODO #FIXME need some implementation to scroll through the tabs
+// but where is this done?
+/*
+ sal_Int32 nSheets = 0;
+ sal_Int32 nPosition = 0;
+ throw uno::RuntimeException("No Implemented" );
+ sal_Bool bSheets = ( Sheets >>= nSheets );
+ sal_Bool bPosition = ( Position >>= nPosition );
+ if ( bSheets || bPosition ) // at least one param specified
+ if ( bSheets )
+ ;// use sheets
+ else if ( bPosition )
+ ; //use position
+*/
+
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::getCaption()
+{
+ // tdf#118129 - return only the caption property of the frame
+ OUString sTitle;
+ getFrameProps()->getPropertyValue(SC_UNONAME_TITLE) >>= sTitle;
+ return uno::Any( sTitle );
+}
+
+void SAL_CALL
+ScVbaWindow::setCaption( const uno::Any& _caption )
+{
+ getFrameProps()->setPropertyValue( SC_UNONAME_TITLE, _caption );
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::getScrollRow()
+{
+ sal_Int32 nValue = 0;
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell )
+ {
+ ScSplitPos eWhich = pViewShell->GetViewData().GetActivePart();
+ nValue = pViewShell->GetViewData().GetPosY(WhichV(eWhich));
+ }
+
+ return uno::Any( nValue + 1);
+}
+
+void SAL_CALL
+ScVbaWindow::setScrollRow( const uno::Any& _scrollrow )
+{
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell )
+ {
+ sal_Int32 scrollRow = 0;
+ _scrollrow >>= scrollRow;
+ ScSplitPos eWhich = pViewShell->GetViewData().GetActivePart();
+ sal_Int32 nOldValue = pViewShell->GetViewData().GetPosY(WhichV(eWhich)) + 1;
+ pViewShell->ScrollLines(0, scrollRow - nOldValue);
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::getScrollColumn()
+{
+ sal_Int32 nValue = 0;
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell )
+ {
+ ScSplitPos eWhich = pViewShell->GetViewData().GetActivePart();
+ nValue = pViewShell->GetViewData().GetPosX(WhichH(eWhich));
+ }
+
+ return uno::Any( nValue + 1);
+}
+
+void SAL_CALL
+ScVbaWindow::setScrollColumn( const uno::Any& _scrollcolumn )
+{
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell )
+ {
+ sal_Int32 scrollColumn = 0;
+ _scrollcolumn >>= scrollColumn;
+ ScSplitPos eWhich = pViewShell->GetViewData().GetActivePart();
+ sal_Int32 nOldValue = pViewShell->GetViewData().GetPosX(WhichH(eWhich)) + 1;
+ pViewShell->ScrollLines(scrollColumn - nOldValue, 0);
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::getWindowState()
+{
+ sal_Int32 nwindowState = xlNormal;
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+ WorkWindow* pWork = static_cast<WorkWindow*>( rViewFrame.GetFrame().GetSystemWindow() );
+ if ( pWork )
+ {
+ if ( pWork -> IsMaximized())
+ nwindowState = xlMaximized;
+ else if (pWork -> IsMinimized())
+ nwindowState = xlMinimized;
+ }
+ return uno::Any( nwindowState );
+}
+
+void SAL_CALL
+ScVbaWindow::setWindowState( const uno::Any& _windowstate )
+{
+ sal_Int32 nwindowState = xlMaximized;
+ _windowstate >>= nwindowState;
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+ WorkWindow* pWork = static_cast<WorkWindow*>( rViewFrame.GetFrame().GetSystemWindow() );
+ if ( pWork )
+ {
+ if ( nwindowState == xlMaximized)
+ pWork -> Maximize();
+ else if (nwindowState == xlMinimized)
+ pWork -> Minimize();
+ else if (nwindowState == xlNormal)
+ pWork -> Restore();
+ else
+ throw uno::RuntimeException("Invalid Parameter" );
+ }
+}
+
+void
+ScVbaWindow::Activate()
+{
+ rtl::Reference<ScVbaWorkbook> workbook( new ScVbaWorkbook( uno::Reference< XHelperInterface >( Application(), uno::UNO_QUERY_THROW ), mxContext, m_xModel ) );
+
+ workbook->Activate();
+}
+
+void
+ScVbaWindow::Close( const uno::Any& SaveChanges, const uno::Any& FileName, const uno::Any& RouteWorkBook )
+{
+ rtl::Reference< ScVbaWorkbook > workbook( new ScVbaWorkbook( uno::Reference< XHelperInterface >( Application(), uno::UNO_QUERY_THROW ), mxContext, m_xModel ) );
+ workbook->Close(SaveChanges, FileName, RouteWorkBook );
+}
+
+uno::Reference< excel::XPane > SAL_CALL
+ScVbaWindow::ActivePane()
+{
+ uno::Reference< sheet::XViewPane > xViewPane( getController(), uno::UNO_QUERY_THROW );
+ return new ScVbaPane( this, mxContext, m_xModel, xViewPane );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaWindow::ActiveCell( )
+{
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ return xApplication->getActiveCell();
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::Selection( )
+{
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ return xApplication->getSelection();
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaWindow::RangeSelection()
+{
+ /* TODO / FIXME: According to documentation, this method returns the range
+ selection even if shapes are selected. */
+ return uno::Reference< excel::XRange >( Selection(), uno::UNO_QUERY_THROW );
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getDisplayGridlines()
+{
+ bool bGrid = true;
+ getControllerProps()->getPropertyValue( SC_UNO_SHOWGRID ) >>= bGrid;
+ return bGrid;
+}
+
+void SAL_CALL
+ScVbaWindow::setDisplayGridlines( sal_Bool _displaygridlines )
+{
+ getControllerProps()->setPropertyValue( SC_UNO_SHOWGRID, uno::Any( _displaygridlines ));
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getDisplayHeadings()
+{
+ bool bHeading = true;
+ getControllerProps()->getPropertyValue( SC_UNO_COLROWHDR ) >>= bHeading;
+ return bHeading;
+}
+
+void SAL_CALL
+ScVbaWindow::setDisplayHeadings( sal_Bool _bDisplayHeadings )
+{
+ getControllerProps()->setPropertyValue( SC_UNO_COLROWHDR, uno::Any( _bDisplayHeadings ));
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getDisplayHorizontalScrollBar()
+{
+ bool bHorizontalScrollBar = true;
+ getControllerProps()->getPropertyValue( SC_UNO_HORSCROLL ) >>= bHorizontalScrollBar;
+ return bHorizontalScrollBar;
+}
+
+void SAL_CALL
+ScVbaWindow::setDisplayHorizontalScrollBar( sal_Bool _bDisplayHorizontalScrollBar )
+{
+ getControllerProps()->setPropertyValue( SC_UNO_HORSCROLL, uno::Any( _bDisplayHorizontalScrollBar ));
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getDisplayOutline()
+{
+ bool bOutline = true;
+ getControllerProps()->getPropertyValue( SC_UNO_OUTLSYMB ) >>= bOutline;
+ return bOutline;
+}
+
+void SAL_CALL
+ScVbaWindow::setDisplayOutline( sal_Bool _bDisplayOutline )
+{
+ getControllerProps()->setPropertyValue( SC_UNO_OUTLSYMB, uno::Any( _bDisplayOutline ));
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getDisplayVerticalScrollBar()
+{
+ bool bVerticalScrollBar = true;
+ getControllerProps()->getPropertyValue( SC_UNO_VERTSCROLL ) >>= bVerticalScrollBar;
+ return bVerticalScrollBar;
+}
+
+void SAL_CALL
+ScVbaWindow::setDisplayVerticalScrollBar( sal_Bool _bDisplayVerticalScrollBar )
+{
+ getControllerProps()->setPropertyValue( SC_UNO_VERTSCROLL, uno::Any( _bDisplayVerticalScrollBar ));
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getDisplayWorkbookTabs()
+{
+ bool bWorkbookTabs = true;
+ getControllerProps()->getPropertyValue( SC_UNO_SHEETTABS ) >>= bWorkbookTabs;
+ return bWorkbookTabs;
+}
+
+void SAL_CALL
+ScVbaWindow::setDisplayWorkbookTabs( sal_Bool _bDisplayWorkbookTabs )
+{
+ getControllerProps()->setPropertyValue( SC_UNO_SHEETTABS, uno::Any( _bDisplayWorkbookTabs ));
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getFreezePanes()
+{
+ uno::Reference< sheet::XViewFreezable > xViewFreezable( getController(), uno::UNO_QUERY_THROW );
+ return xViewFreezable->hasFrozenPanes();
+}
+
+void SAL_CALL
+ScVbaWindow::setFreezePanes( sal_Bool _bFreezePanes )
+{
+ uno::Reference< sheet::XViewPane > xViewPane( getController(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( xViewPane, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XViewFreezable > xViewFreezable( xViewPane, uno::UNO_QUERY_THROW );
+ if( _bFreezePanes )
+ {
+ if( xViewSplitable->getIsWindowSplit() )
+ {
+ // if there is a split we freeze at the split
+ sal_Int32 nColumn = getSplitColumn();
+ sal_Int32 nRow = getSplitRow();
+ xViewFreezable->freezeAtPosition( nColumn, nRow );
+ }
+ else
+ {
+ // otherwise we freeze in the center of the visible sheet
+ table::CellRangeAddress aCellRangeAddress = xViewPane->getVisibleRange();
+ sal_Int32 nColumn = aCellRangeAddress.StartColumn + (( aCellRangeAddress.EndColumn - aCellRangeAddress.StartColumn )/2 );
+ sal_Int32 nRow = aCellRangeAddress.StartRow + (( aCellRangeAddress.EndRow - aCellRangeAddress.StartRow )/2 );
+ xViewFreezable->freezeAtPosition( nColumn, nRow );
+ }
+ }
+ else
+ {
+ //remove the freeze panes
+ xViewSplitable->splitAtPosition(0,0);
+ }
+}
+
+sal_Bool SAL_CALL
+ScVbaWindow::getSplit()
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ return xViewSplitable->getIsWindowSplit();
+}
+
+void SAL_CALL
+ScVbaWindow::setSplit( sal_Bool _bSplit )
+{
+ if( !_bSplit )
+ {
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ xViewSplitable->splitAtPosition(0,0);
+ }
+ else
+ {
+ uno::Reference< sheet::XViewFreezable > xViewFreezable( getController(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XRange > xRange = ActiveCell();
+ sal_Int32 nRow = xRange->getRow();
+ sal_Int32 nColumn = xRange->getColumn();
+ SplitAtDefinedPosition( nColumn-1, nRow-1 );
+ }
+}
+
+sal_Int32 SAL_CALL
+ScVbaWindow::getSplitColumn()
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ return xViewSplitable->getSplitColumn();
+}
+
+void SAL_CALL
+ScVbaWindow::setSplitColumn( sal_Int32 _splitcolumn )
+{
+ if( getSplitColumn() != _splitcolumn )
+ {
+ uno::Reference< sheet::XViewFreezable > xViewFreezable( getController(), uno::UNO_QUERY_THROW );
+ sal_Int32 nRow = getSplitRow();
+ SplitAtDefinedPosition( _splitcolumn, nRow );
+ }
+}
+
+double SAL_CALL
+ScVbaWindow::getSplitHorizontal()
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ return PixelsToPoints( getDevice(), xViewSplitable->getSplitHorizontal(), true );
+}
+
+void SAL_CALL
+ScVbaWindow::setSplitHorizontal( double _splithorizontal )
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ double fHoriPixels = PointsToPixels( getDevice(), _splithorizontal, true );
+ xViewSplitable->splitAtPosition( static_cast< sal_Int32 >( fHoriPixels ), 0 );
+}
+
+sal_Int32 SAL_CALL
+ScVbaWindow::getSplitRow()
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ return xViewSplitable->getSplitRow();
+}
+
+void SAL_CALL
+ScVbaWindow::setSplitRow( sal_Int32 _splitrow )
+{
+ if( getSplitRow() != _splitrow )
+ {
+ uno::Reference< sheet::XViewFreezable > xViewFreezable( getController(), uno::UNO_QUERY_THROW );
+ sal_Int32 nColumn = getSplitColumn();
+ SplitAtDefinedPosition( nColumn, _splitrow );
+ }
+}
+
+double SAL_CALL
+ScVbaWindow::getSplitVertical()
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ return PixelsToPoints( getDevice(), xViewSplitable->getSplitVertical(), false );
+}
+
+void SAL_CALL
+ScVbaWindow::setSplitVertical(double _splitvertical )
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ double fVertiPixels = PointsToPixels( getDevice(), _splitvertical, false );
+ xViewSplitable->splitAtPosition( 0, static_cast<sal_Int32>( fVertiPixels ) );
+}
+
+void ScVbaWindow::SplitAtDefinedPosition( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ uno::Reference< sheet::XViewSplitable > xViewSplitable( getController(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XViewFreezable > xViewFreezable( xViewSplitable, uno::UNO_QUERY_THROW );
+ // nColumns and nRows means split columns/rows
+ if( nColumns == 0 && nRows == 0 )
+ return;
+
+ sal_Int32 cellColumn = nColumns + 1;
+ sal_Int32 cellRow = nRows + 1;
+
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell )
+ {
+ //firstly remove the old splitter
+ xViewSplitable->splitAtPosition(0,0);
+
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XWorksheet > xSheet( xApplication->getActiveSheet(), uno::UNO_SET_THROW );
+ xSheet->Cells(uno::Any(cellRow), uno::Any(cellColumn))->Select();
+
+ //pViewShell->FreezeSplitters( FALSE );
+ dispatchExecute( pViewShell, SID_WINDOW_SPLIT );
+ }
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::getZoom()
+{
+ uno::Reference< beans::XPropertySet > xProps = getControllerProps();
+ OUString sName( SC_UNO_ZOOMTYPE );
+ sal_Int16 nZoomType = view::DocumentZoomType::PAGE_WIDTH;
+ xProps->getPropertyValue( sName ) >>= nZoomType;
+ if( nZoomType == view::DocumentZoomType::PAGE_WIDTH )
+ {
+ return uno::Any( true );
+ }
+ else if( nZoomType == view::DocumentZoomType::BY_VALUE )
+ {
+ sName = SC_UNO_ZOOMVALUE;
+ sal_Int16 nZoom = 100;
+ xProps->getPropertyValue( sName ) >>= nZoom;
+ return uno::Any( nZoom );
+ }
+ return uno::Any();
+}
+
+void SAL_CALL ScVbaWindow::setZoom(const uno::Any& _zoom)
+{
+ sal_Int16 nZoom = 100;
+ _zoom >>= nZoom;
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( m_xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XWorksheet > xActiveSheet = ActiveSheet();
+ SCTAB nTab = 0;
+ if ( !ScVbaWorksheets::nameExists (xSpreadDoc, xActiveSheet->getName(), nTab) )
+ throw uno::RuntimeException();
+ std::vector< SCTAB > vTabs { nTab };
+ excel::implSetZoom( m_xModel, nZoom, vTabs );
+}
+
+uno::Reference< excel::XWorksheet > SAL_CALL
+ScVbaWindow::ActiveSheet( )
+{
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ return xApplication->getActiveSheet();
+}
+
+uno::Any SAL_CALL
+ScVbaWindow::getView()
+{
+ bool bPageBreak = false;
+ sal_Int32 nWindowView = excel::XlWindowView::xlNormalView;
+
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if (pViewShell)
+ bPageBreak = pViewShell->GetViewData().IsPagebreakMode();
+
+ if( bPageBreak )
+ nWindowView = excel::XlWindowView::xlPageBreakPreview;
+ else
+ nWindowView = excel::XlWindowView::xlNormalView;
+
+ return uno::Any( nWindowView );
+}
+
+void SAL_CALL
+ScVbaWindow::setView( const uno::Any& _view)
+{
+ sal_Int32 nWindowView = excel::XlWindowView::xlNormalView;
+ _view >>= nWindowView;
+ sal_uInt16 nSlot = FID_NORMALVIEWMODE;
+ switch ( nWindowView )
+ {
+ case excel::XlWindowView::xlNormalView:
+ nSlot = FID_NORMALVIEWMODE;
+ break;
+ case excel::XlWindowView::xlPageBreakPreview:
+ nSlot = FID_PAGEBREAKMODE;
+ break;
+ default:
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell )
+ dispatchExecute( pViewShell, nSlot );
+}
+
+uno::Reference< excel::XRange > SAL_CALL
+ScVbaWindow::getVisibleRange()
+{
+ uno::Reference< container::XIndexAccess > xPanesIA( getController(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XViewPane > xTopLeftPane( xPanesIA->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XPane > xPane( new ScVbaPane( this, mxContext, m_xModel, xTopLeftPane ) );
+ return xPane->getVisibleRange();
+}
+
+sal_Int32 SAL_CALL
+ScVbaWindow::PointsToScreenPixelsX(sal_Int32 _points)
+{
+ sal_Int32 nHundredthsofOneMillimeters = Millimeter::getInHundredthsOfOneMillimeter( _points );
+ double fConvertFactor = getDevice()->getInfo().PixelPerMeterX/100000;
+ return static_cast<sal_Int32>(fConvertFactor * nHundredthsofOneMillimeters );
+}
+
+sal_Int32 SAL_CALL
+ScVbaWindow::PointsToScreenPixelsY(sal_Int32 _points)
+{
+ sal_Int32 nHundredthsofOneMillimeters = Millimeter::getInHundredthsOfOneMillimeter( _points );
+ double fConvertFactor = getDevice()->getInfo().PixelPerMeterY/100000;
+ return static_cast<sal_Int32>(fConvertFactor * nHundredthsofOneMillimeters );
+}
+
+void SAL_CALL
+ScVbaWindow::PrintOut( const css::uno::Any& From, const css::uno::Any&To, const css::uno::Any& Copies, const css::uno::Any& Preview, const css::uno::Any& ActivePrinter, const css::uno::Any& PrintToFile, const css::uno::Any& Collate, const css::uno::Any& PrToFileName )
+{
+ // need test, print current active sheet
+ // !! TODO !! get view shell from controller
+ PrintOutHelper( excel::getBestViewShell( m_xModel ), From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName, true );
+}
+
+void SAL_CALL
+ScVbaWindow::PrintPreview( const css::uno::Any& EnableChanges )
+{
+ // need test, print preview current active sheet
+ // !! TODO !! get view shell from controller
+ PrintPreviewHelper( EnableChanges, excel::getBestViewShell( m_xModel ) );
+}
+
+double SAL_CALL ScVbaWindow::getTabRatio()
+{
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell && pViewShell->GetViewData().GetView() )
+ {
+ double fRatio = ScTabView::GetRelTabBarWidth();
+ if ( fRatio >= 0.0 && fRatio <= 1.0 )
+ return fRatio;
+ }
+ return 0.0;
+}
+
+void SAL_CALL ScVbaWindow::setTabRatio( double fRatio )
+{
+ ScTabViewShell* pViewShell = excel::getBestViewShell( m_xModel );
+ if ( pViewShell && pViewShell->GetViewData().GetView() )
+ {
+ if ( fRatio >= 0.0 && fRatio <= 1.0 )
+ pViewShell->GetViewData().GetView()->SetRelTabBarWidth( fRatio );
+ }
+}
+
+OUString
+ScVbaWindow::getServiceImplName()
+{
+ return "ScVbaWindow";
+}
+
+uno::Sequence< OUString >
+ScVbaWindow::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Window"
+ };
+ return aServiceNames;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScVbaWindow_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new ScVbaWindow(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbawindow.hxx b/sc/source/ui/vba/vbawindow.hxx
new file mode 100644
index 0000000000..c4c0a0becb
--- /dev/null
+++ b/sc/source/ui/vba/vbawindow.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XWindow.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <ooo/vba/excel/XPane.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <vbahelper/vbawindowbase.hxx>
+
+typedef cppu::ImplInheritanceHelper< VbaWindowBase, ov::excel::XWindow > WindowImpl_BASE;
+
+class ScVbaWindow : public WindowImpl_BASE
+{
+private:
+ css::uno::Reference< ov::excel::XPane > m_xPane;
+
+ void init();
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::beans::XPropertySet > getControllerProps() const;
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::beans::XPropertySet > getFrameProps() const;
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::awt::XDevice > getDevice() const;
+
+protected:
+ void SplitAtDefinedPosition( sal_Int32 nColumns, sal_Int32 nRows );
+
+public:
+ /// @throws css::uno::RuntimeException
+ void Scroll( const css::uno::Any& Down, const css::uno::Any& Up, const css::uno::Any& ToRight, const css::uno::Any& ToLeft, bool bLargeScroll );
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaWindow(
+ const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::frame::XController >& xController );
+ /// @throws css::uno::RuntimeException
+ ScVbaWindow(
+ const css::uno::Sequence< css::uno::Any >& aArgs,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ // XWindow
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL ActiveCell( ) override;
+ virtual css::uno::Reference< ov::excel::XPane > SAL_CALL ActivePane() override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL ActiveSheet( ) override;
+ virtual void SAL_CALL setCaption( const css::uno::Any& _caption ) override;
+ virtual css::uno::Any SAL_CALL getCaption() override;
+ virtual sal_Bool SAL_CALL getDisplayGridlines() override;
+ virtual void SAL_CALL setDisplayGridlines( sal_Bool _displaygridlines ) override;
+ virtual sal_Bool SAL_CALL getDisplayHeadings() override;
+ virtual void SAL_CALL setDisplayHeadings( sal_Bool _bDisplayHeadings ) override;
+ virtual sal_Bool SAL_CALL getDisplayHorizontalScrollBar() override;
+ virtual void SAL_CALL setDisplayHorizontalScrollBar( sal_Bool _bDisplayHorizontalScrollBar ) override;
+ virtual sal_Bool SAL_CALL getDisplayOutline() override;
+ virtual void SAL_CALL setDisplayOutline( sal_Bool _bDisplayOutline ) override;
+ virtual sal_Bool SAL_CALL getDisplayVerticalScrollBar() override;
+ virtual void SAL_CALL setDisplayVerticalScrollBar( sal_Bool _bDisplayVerticalScrollBar ) override;
+ virtual sal_Bool SAL_CALL getDisplayWorkbookTabs() override;
+ virtual void SAL_CALL setDisplayWorkbookTabs( sal_Bool _bDisplayWorkbookTabs ) override;
+ virtual sal_Bool SAL_CALL getFreezePanes() override;
+ virtual void SAL_CALL setFreezePanes( sal_Bool _bFreezePanes ) override;
+ virtual sal_Bool SAL_CALL getSplit() override;
+ virtual void SAL_CALL setSplit( sal_Bool _bSplit ) override;
+ virtual sal_Int32 SAL_CALL getSplitColumn() override ;
+ virtual void SAL_CALL setSplitColumn( sal_Int32 _splitcolumn ) override ;
+ virtual double SAL_CALL getSplitHorizontal() override ;
+ virtual void SAL_CALL setSplitHorizontal( double _splithorizontal ) override ;
+ virtual sal_Int32 SAL_CALL getSplitRow() override ;
+ virtual void SAL_CALL setSplitRow( sal_Int32 _splitrow ) override ;
+ virtual double SAL_CALL getSplitVertical() override ;
+ virtual void SAL_CALL setSplitVertical( double _splitvertical ) override ;
+ virtual css::uno::Any SAL_CALL getScrollRow() override ;
+ virtual void SAL_CALL setScrollRow( const css::uno::Any& _scrollrow ) override ;
+ virtual css::uno::Any SAL_CALL getScrollColumn() override ;
+ virtual void SAL_CALL setScrollColumn( const css::uno::Any& _scrollcolumn ) override ;
+ virtual css::uno::Any SAL_CALL getView() override;
+ virtual void SAL_CALL setView( const css::uno::Any& _view ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getVisibleRange() override;
+ virtual css::uno::Any SAL_CALL getWindowState() override;
+ virtual void SAL_CALL setWindowState( const css::uno::Any& _windowstate ) override;
+ virtual css::uno::Any SAL_CALL getZoom() override;
+ virtual void SAL_CALL setZoom(const css::uno::Any& _zoom) override;
+ virtual double SAL_CALL getTabRatio() override ;
+ virtual void SAL_CALL setTabRatio( double _tabratio ) override ;
+
+ // Methods
+ virtual void SAL_CALL SmallScroll( const css::uno::Any& Down, const css::uno::Any& Up, const css::uno::Any& ToRight, const css::uno::Any& ToLeft ) override;
+ virtual void SAL_CALL LargeScroll( const css::uno::Any& Down, const css::uno::Any& Up, const css::uno::Any& ToRight, const css::uno::Any& ToLeft ) override;
+ virtual css::uno::Any SAL_CALL SelectedSheets( const css::uno::Any& aIndex ) override;
+ virtual void SAL_CALL ScrollWorkbookTabs( const css::uno::Any& Sheets, const css::uno::Any& Position ) override;
+ virtual void SAL_CALL Activate( ) override;
+ virtual void SAL_CALL Close( const css::uno::Any& SaveChanges, const css::uno::Any& FileName, const css::uno::Any& RouteWorkBook ) override;
+ virtual css::uno::Any SAL_CALL Selection( ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL RangeSelection() override;
+ virtual sal_Int32 SAL_CALL PointsToScreenPixelsX(sal_Int32 _points) override;
+ virtual sal_Int32 SAL_CALL PointsToScreenPixelsY(sal_Int32 _points) override;
+ virtual void SAL_CALL PrintOut( const css::uno::Any& From, const css::uno::Any&To, const css::uno::Any& Copies, const css::uno::Any& Preview, const css::uno::Any& ActivePrinter, const css::uno::Any& PrintToFile, const css::uno::Any& Collate, const css::uno::Any& PrToFileName ) override;
+ virtual void SAL_CALL PrintPreview( const css::uno::Any& EnableChanges ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbawindows.cxx b/sc/source/ui/vba/vbawindows.cxx
new file mode 100644
index 0000000000..8c5e7e5653
--- /dev/null
+++ b/sc/source/ui/vba/vbawindows.cxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbawindows.hxx"
+
+
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ref.hxx>
+
+#include "vbawindow.hxx"
+#include "vbaworkbook.hxx"
+
+#include <unordered_map>
+#include <utility>
+
+#include <osl/file.hxx>
+#include <ooo/vba/excel/XApplication.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::ooo::vba;
+
+typedef std::unordered_map< OUString,
+sal_Int32 > NameIndexHash;
+
+static uno::Reference< XHelperInterface > lcl_createWorkbookHIParent( const uno::Reference< frame::XModel >& xModel, const uno::Reference< uno::XComponentContext >& xContext, const uno::Any& aApplication )
+{
+ return new ScVbaWorkbook( uno::Reference< XHelperInterface >( aApplication, uno::UNO_QUERY_THROW ), xContext, xModel );
+}
+
+static uno::Any ComponentToWindow( const uno::Any& aSource, const uno::Reference< uno::XComponentContext > & xContext, const uno::Any& aApplication )
+{
+ uno::Reference< frame::XModel > xModel( aSource, uno::UNO_QUERY_THROW );
+ // !! TODO !! iterate over all controllers
+ uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
+ uno::Reference< excel::XWindow > xWin( new ScVbaWindow( lcl_createWorkbookHIParent( xModel, xContext, aApplication ), xContext, xModel, xController ) );
+ return uno::Any( xWin );
+}
+
+typedef std::vector < uno::Reference< sheet::XSpreadsheetDocument > > Components;
+
+namespace {
+
+// #TODO more or less the same as class in workwindows ( code sharing needed )
+class WindowComponentEnumImpl : public EnumerationHelper_BASE
+{
+protected:
+ uno::Reference< uno::XComponentContext > m_xContext;
+ Components m_components;
+ Components::const_iterator m_it;
+
+public:
+ /// @throws uno::RuntimeException
+ WindowComponentEnumImpl( uno::Reference< uno::XComponentContext > xContext, Components&& components )
+ : m_xContext(std::move( xContext )), m_components( std::move(components) )
+ {
+ m_it = m_components.begin();
+ }
+
+ /// @throws uno::RuntimeException
+ explicit WindowComponentEnumImpl( uno::Reference< uno::XComponentContext > xContext ) : m_xContext(std::move( xContext ))
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(m_xContext);
+ uno::Reference< container::XEnumeration > xComponents = xDesktop->getComponents()->createEnumeration();
+ while( xComponents->hasMoreElements() )
+ {
+ uno::Reference< sheet::XSpreadsheetDocument > xNext( xComponents->nextElement(), uno::UNO_QUERY );
+ if ( xNext.is() )
+ m_components.push_back( xNext );
+ }
+ m_it = m_components.begin();
+ }
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return m_it != m_components.end();
+ }
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( !hasMoreElements() )
+ {
+ throw container::NoSuchElementException();
+ }
+ return css::uno::Any( *(m_it++) );
+ }
+};
+
+class WindowEnumImpl : public WindowComponentEnumImpl
+{
+ uno::Any m_aApplication;
+public:
+ WindowEnumImpl( const uno::Reference< uno::XComponentContext >& xContext, uno::Any aApplication ): WindowComponentEnumImpl( xContext ), m_aApplication(std::move( aApplication )) {}
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ return ComponentToWindow( WindowComponentEnumImpl::nextElement(), m_xContext, m_aApplication );
+ }
+};
+
+}
+
+typedef ::cppu::WeakImplHelper< container::XEnumerationAccess
+ , css::container::XIndexAccess
+ , css::container::XNameAccess
+ > WindowsAccessImpl_BASE;
+
+namespace {
+
+class WindowsAccessImpl : public WindowsAccessImpl_BASE
+{
+ uno::Reference< uno::XComponentContext > m_xContext;
+ Components m_windows;
+ NameIndexHash namesToIndices;
+public:
+ explicit WindowsAccessImpl( uno::Reference< uno::XComponentContext > xContext ):m_xContext(std::move( xContext ))
+ {
+ css::uno::Reference<css::container::XNameAccess> xNameAccess(m_xContext,
+ css::uno::UNO_QUERY_THROW);
+ const auto aAppplication = xNameAccess->getByName("Application");
+
+ uno::Reference< container::XEnumeration > xEnum = new WindowComponentEnumImpl( m_xContext );
+ sal_Int32 nIndex=0;
+ while( xEnum->hasMoreElements() )
+ {
+ uno::Reference< sheet::XSpreadsheetDocument > xNext( xEnum->nextElement(), uno::UNO_QUERY );
+ if ( xNext.is() )
+ {
+ m_windows.push_back( xNext );
+ uno::Reference< frame::XModel > xModel( xNext, uno::UNO_QUERY_THROW ); // that the spreadsheetdocument is a xmodel is a given
+
+ // tdf#126457 - add workbook name to window titles
+ rtl::Reference<ScVbaWorkbook> workbook(new ScVbaWorkbook(
+ uno::Reference<XHelperInterface>(aAppplication, uno::UNO_QUERY_THROW),
+ m_xContext, xModel));
+ const OUString aWorkBookName(workbook->getName());
+ if (!hasByName(aWorkBookName))
+ namesToIndices[aWorkBookName] = nIndex;
+
+ // tdf#126457 - add file url to window titles
+ OUString sName;
+ ::osl::File::getSystemPathFromFileURL(xModel->getURL(), sName);
+ if (!hasByName(sName))
+ namesToIndices[sName] = nIndex;
+
+ // !! TODO !! iterate over all controllers
+ uno::Reference< frame::XController > xController( xModel->getCurrentController(), uno::UNO_SET_THROW );
+ uno::Reference< XHelperInterface > xTemp; // temporary needed for g++ 3.3.5
+ rtl::Reference< ScVbaWindow > window( new ScVbaWindow( xTemp, m_xContext, xModel, xController ) );
+ OUString sCaption;
+ window->getCaption() >>= sCaption;
+ namesToIndices[ sCaption ] = nIndex++;
+ }
+ }
+
+ }
+
+ //XEnumerationAccess
+ virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override
+ {
+ return new WindowComponentEnumImpl( m_xContext, std::vector(m_windows) );
+ }
+ // XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount( ) override
+ {
+ return m_windows.size();
+ }
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ if ( Index < 0
+ || o3tl::make_unsigned( Index ) >= m_windows.size() )
+ throw lang::IndexOutOfBoundsException();
+ return css::uno::Any( m_windows[ Index ] ); // returns xspreadsheetdoc
+ }
+
+ //XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override
+ {
+ return cppu::UnoType<sheet::XSpreadsheetDocument>::get();
+ }
+
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ return ( !m_windows.empty() );
+ }
+
+ //XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ NameIndexHash::const_iterator it = namesToIndices.find( aName );
+ if ( it == namesToIndices.end() )
+ throw container::NoSuchElementException();
+ return css::uno::Any( m_windows[ it->second ] );
+
+ }
+
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ return comphelper::mapKeysToSequence( namesToIndices );
+ }
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ NameIndexHash::const_iterator it = namesToIndices.find( aName );
+ return (it != namesToIndices.end());
+ }
+
+};
+
+}
+
+ScVbaWindows::ScVbaWindows( const uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext ) : ScVbaWindows_BASE( xParent, xContext, uno::Reference< container::XIndexAccess > ( new WindowsAccessImpl( xContext ) ) )
+{
+}
+uno::Reference< container::XEnumeration >
+ScVbaWindows::createEnumeration()
+{
+ return new WindowEnumImpl( mxContext, Application() );
+}
+
+uno::Any
+ScVbaWindows::createCollectionObject( const css::uno::Any& aSource )
+{
+ return ComponentToWindow( aSource, mxContext, Application() );
+}
+
+uno::Type
+ScVbaWindows::getElementType()
+{
+ return cppu::UnoType<excel::XWindows>::get();
+}
+
+void SAL_CALL
+ScVbaWindows::Arrange( ::sal_Int32 /*ArrangeStyle*/, const uno::Any& /*ActiveWorkbook*/, const uno::Any& /*SyncHorizontal*/, const uno::Any& /*SyncVertical*/ )
+{
+ //#TODO #FIXME see what can be done for an implementation here
+}
+
+OUString
+ScVbaWindows::getServiceImplName()
+{
+ return "ScVbaWindows";
+}
+
+css::uno::Sequence<OUString>
+ScVbaWindows::getServiceNames()
+{
+ static uno::Sequence< OUString > const sNames
+ {
+ "ooo.vba.excel.Windows"
+ };
+ return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbawindows.hxx b/sc/source/ui/vba/vbawindows.hxx
new file mode 100644
index 0000000000..b827bf7e44
--- /dev/null
+++ b/sc/source/ui/vba/vbawindows.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 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XWindows.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+typedef CollTestImplHelper< ov::excel::XWindows > ScVbaWindows_BASE;
+
+class ScVbaWindows : public ScVbaWindows_BASE
+{
+public:
+ ScVbaWindows( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XWindows
+ virtual void SAL_CALL Arrange( ::sal_Int32 ArrangeStyle, const css::uno::Any& ActiveWorkbook, const css::uno::Any& SyncHorizontal, const css::uno::Any& SyncVertical ) override;
+ // ScVbaCollectionBaseImpl
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworkbook.cxx b/sc/source/ui/vba/vbaworkbook.cxx
new file mode 100644
index 0000000000..eee7c8529c
--- /dev/null
+++ b/sc/source/ui/vba/vbaworkbook.cxx
@@ -0,0 +1,436 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <tools/urlobj.hxx>
+
+#include <com/sun/star/util/XProtectable.hpp>
+#include <com/sun/star/sheet/XNamedRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <ooo/vba/excel/XlFileFormat.hpp>
+#include <ooo/vba/excel/XApplication.hpp>
+
+#include "vbaworksheet.hxx"
+#include "vbaworksheets.hxx"
+#include "vbaworkbook.hxx"
+#include "vbawindows.hxx"
+#include "vbastyles.hxx"
+#include "excelvbahelper.hxx"
+#include "vbapalette.hxx"
+#include <osl/file.hxx>
+#include "vbanames.hxx"
+#include <docoptio.hxx>
+#include <docsh.hxx>
+
+// Much of the impl. for the equivalent UNO module is
+// sc/source/ui/unoobj/docuno.cxx, viewuno.cxx
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+uno::Sequence< sal_Int32 > ScVbaWorkbook::ColorData;
+
+void SAL_CALL
+ScVbaWorkbook::ResetColors( )
+{
+ uno::Reference< container::XIndexAccess > xIndexAccess( ScVbaPalette::getDefaultPalette(), uno::UNO_SET_THROW );
+ sal_Int32 nLen = xIndexAccess->getCount();
+ ColorData.realloc( nLen );
+
+ sal_Int32* pDest = ColorData.getArray();
+ for ( sal_Int32 index=0; index < nLen; ++pDest, ++index )
+ xIndexAccess->getByIndex( index ) >>= *pDest;
+}
+
+::uno::Any SAL_CALL
+ScVbaWorkbook::Colors( const ::uno::Any& Index )
+{
+ uno::Any aRet;
+ if ( Index.hasValue() )
+ {
+ sal_Int32 nIndex = 0;
+ Index >>= nIndex;
+ aRet <<= XLRGBToOORGB( ColorData[ --nIndex ] );
+ }
+ else
+ aRet <<= ColorData;
+ return aRet;
+}
+
+bool ScVbaWorkbook::setFilterPropsFromFormat( sal_Int32 nFormat, uno::Sequence< beans::PropertyValue >& rProps )
+{
+ auto [begin, end] = asNonConstRange(rProps);
+ auto pProp = std::find_if(begin, end,
+ [](const beans::PropertyValue& rProp) { return rProp.Name == "FilterName"; });
+ bool bRes = pProp != end;
+ if (bRes)
+ {
+ switch( nFormat )
+ {
+ case excel::XlFileFormat::xlCSV:
+ pProp->Value <<= SC_TEXT_CSV_FILTER_NAME;
+ break;
+ case excel::XlFileFormat::xlDBF4:
+ pProp->Value <<= OUString("DBF");
+ break;
+ case excel::XlFileFormat::xlDIF:
+ pProp->Value <<= OUString("DIF");
+ break;
+ case excel::XlFileFormat::xlWK3:
+ pProp->Value <<= OUString("Lotus");
+ break;
+ case excel::XlFileFormat::xlExcel4Workbook:
+ pProp->Value <<= OUString("MS Excel 4.0");
+ break;
+ case excel::XlFileFormat::xlExcel5:
+ pProp->Value <<= OUString("MS Excel 5.0/95");
+ break;
+ case excel::XlFileFormat::xlHtml:
+ pProp->Value <<= OUString("HTML (StarCalc)");
+ break;
+ case excel::XlFileFormat::xlExcel9795:
+ default:
+ pProp->Value <<= OUString("MS Excel 97");
+ break;
+ }
+ }
+ return bRes;
+}
+
+::sal_Int32 SAL_CALL
+ScVbaWorkbook::getFileFormat( )
+{
+ sal_Int32 aFileFormat = 0;
+ OUString aFilterName;
+ uno::Sequence< beans::PropertyValue > aArgs = getModel()->getArgs();
+
+ // #FIXME - seems suspect should we not walk through the properties
+ // to find the FilterName
+ if ( aArgs[0].Name == "FilterName" ) {
+ aArgs[0].Value >>= aFilterName;
+ } else {
+ aArgs[1].Value >>= aFilterName;
+ }
+
+ if (aFilterName == SC_TEXT_CSV_FILTER_NAME) {
+ aFileFormat = excel::XlFileFormat::xlCSV; //xlFileFormat.
+ }
+
+ if ( aFilterName == "DBF" ) {
+ aFileFormat = excel::XlFileFormat::xlDBF4;
+ }
+
+ if ( aFilterName == "DIF" ) {
+ aFileFormat = excel::XlFileFormat::xlDIF;
+ }
+
+ if ( aFilterName == "Lotus" ) {
+ aFileFormat = excel::XlFileFormat::xlWK3;
+ }
+
+ if ( aFilterName == "MS Excel 4.0" ) {
+ aFileFormat = excel::XlFileFormat::xlExcel4Workbook;
+ }
+
+ if ( aFilterName == "MS Excel 5.0/95" ) {
+ aFileFormat = excel::XlFileFormat::xlExcel5;
+ }
+
+ if ( aFilterName == "MS Excel 97" ) {
+ aFileFormat = excel::XlFileFormat::xlExcel9795;
+ }
+
+ if (aFilterName == "HTML (StarCalc)") {
+ aFileFormat = excel::XlFileFormat::xlHtml;
+ }
+
+ if ( aFilterName == "calc_StarOffice_XML_Calc_Template" ) {
+ aFileFormat = excel::XlFileFormat::xlTemplate;
+ }
+
+ if (aFilterName == "StarOffice XML (Calc)") {
+ aFileFormat = excel::XlFileFormat::xlWorkbookNormal;
+ }
+ if ( aFilterName == "calc8" ) {
+ aFileFormat = excel::XlFileFormat::xlWorkbookNormal;
+ }
+
+ return aFileFormat;
+}
+
+void
+ScVbaWorkbook::init()
+{
+ if ( !ColorData.hasElements() )
+ ResetColors();
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ pShell->RegisterAutomationWorkbookObject( this );
+}
+
+ScVbaWorkbook::ScVbaWorkbook( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext, css::uno::Reference< css::frame::XModel > const & xModel ) : ScVbaWorkbook_BASE( xParent, xContext, xModel )
+{
+ init();
+}
+
+ScVbaWorkbook::ScVbaWorkbook( uno::Sequence< uno::Any> const & args,
+ uno::Reference< uno::XComponentContext> const & xContext ) : ScVbaWorkbook_BASE( args, xContext )
+{
+ init();
+}
+
+uno::Reference< excel::XWorksheet >
+ScVbaWorkbook::getActiveSheet()
+{
+ uno::Reference< frame::XModel > xModel( getCurrentExcelDoc( mxContext ), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XSpreadsheetView > xView( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet( xView->getActiveSheet(), uno::UNO_SET_THROW );
+ // #162503# return the original sheet module wrapper object, instead of a new instance
+ uno::Reference< excel::XWorksheet > xWorksheet( excel::getUnoSheetModuleObj( xSheet ), uno::UNO_QUERY );
+ if( xWorksheet.is() ) return xWorksheet;
+ // #i116936# excel::getUnoSheetModuleObj() may return null in documents without global VBA mode enabled
+ return new ScVbaWorksheet( this, mxContext, xSheet, xModel );
+}
+
+uno::Any SAL_CALL
+ScVbaWorkbook::Sheets( const uno::Any& aIndex )
+{
+ return Worksheets( aIndex );
+}
+
+uno::Any SAL_CALL
+ScVbaWorkbook::Worksheets( const uno::Any& aIndex )
+{
+ uno::Reference< frame::XModel > xModel( getModel() );
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference<container::XIndexAccess > xSheets( xSpreadDoc->getSheets(), uno::UNO_QUERY_THROW );
+ uno::Reference< XCollection > xWorkSheets( new ScVbaWorksheets( this, mxContext, xSheets, xModel ) );
+ if ( aIndex.getValueTypeClass() == uno::TypeClass_VOID )
+ {
+ return uno::Any( xWorkSheets );
+ }
+ // pass on to collection
+ return xWorkSheets->Item( aIndex, uno::Any() );
+}
+uno::Any SAL_CALL
+ScVbaWorkbook::Windows( const uno::Any& aIndex )
+{
+
+ uno::Reference< excel::XWindows > xWindows( new ScVbaWindows( getParent(), mxContext ) );
+ if ( aIndex.getValueTypeClass() == uno::TypeClass_VOID )
+ return uno::Any( xWindows );
+ return xWindows->Item( aIndex, uno::Any() );
+}
+
+void SAL_CALL
+ScVbaWorkbook::Activate()
+{
+ VbaDocumentBase::Activate();
+}
+
+void
+ScVbaWorkbook::Protect( const uno::Any &aPassword )
+{
+ VbaDocumentBase::Protect( aPassword );
+}
+
+sal_Bool
+ScVbaWorkbook::getProtectStructure()
+{
+ uno::Reference< util::XProtectable > xProt( getModel(), uno::UNO_QUERY_THROW );
+ return xProt->isProtected();
+}
+
+sal_Bool SAL_CALL ScVbaWorkbook::getPrecisionAsDisplayed()
+{
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ return rDoc.GetDocOptions().IsCalcAsShown();
+ }
+ return false;
+}
+
+void SAL_CALL ScVbaWorkbook::setPrecisionAsDisplayed( sal_Bool _precisionAsDisplayed )
+{
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ ScDocOptions aOpt = rDoc.GetDocOptions();
+ aOpt.SetCalcAsShown( _precisionAsDisplayed );
+ rDoc.SetDocOptions( aOpt );
+ }
+}
+
+OUString SAL_CALL ScVbaWorkbook::getAuthor()
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS( getModel(), uno::UNO_QUERY );
+ if (!xDPS.is())
+ return "?";
+ uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
+ return xDocProps->getAuthor();
+}
+
+void SAL_CALL ScVbaWorkbook::setAuthor( const OUString& _author )
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS( getModel(), uno::UNO_QUERY );
+ if (!xDPS.is())
+ return;
+ uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
+ xDocProps->setAuthor( _author );
+}
+
+void
+ScVbaWorkbook::SaveCopyAs( const OUString& sFileName )
+{
+ OUString aURL;
+ osl::FileBase::getFileURLFromSystemPath( sFileName, aURL );
+ uno::Reference< frame::XStorable > xStor( getModel(), uno::UNO_QUERY_THROW );
+ uno::Sequence< beans::PropertyValue > storeProps{ comphelper::makePropertyValue(
+ "FilterName", OUString( "MS Excel 97" )) };
+ xStor->storeToURL( aURL, storeProps );
+}
+
+void SAL_CALL
+ScVbaWorkbook::SaveAs( const uno::Any& FileName, const uno::Any& FileFormat, const uno::Any& /*Password*/, const uno::Any& /*WriteResPassword*/, const uno::Any& /*ReadOnlyRecommended*/, const uno::Any& /*CreateBackup*/, const uno::Any& /*AccessMode*/, const uno::Any& /*ConflictResolution*/, const uno::Any& /*AddToMru*/, const uno::Any& /*TextCodepage*/, const uno::Any& /*TextVisualLayout*/, const uno::Any& /*Local*/ )
+{
+ OUString sFileName;
+ FileName >>= sFileName;
+ OUString sURL;
+ osl::FileBase::getFileURLFromSystemPath( sFileName, sURL );
+ // detect if there is no path then we need
+ // to use the current folder
+ INetURLObject aURL( sURL );
+ sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ if( sURL.isEmpty() )
+ {
+ // need to add cur dir ( of this workbook ) or else the 'Work' dir
+ sURL = getModel()->getURL();
+
+ if ( sURL.isEmpty() )
+ {
+ // not path available from 'this' document
+ // need to add the 'document'/work directory then
+ uno::Reference< excel::XApplication > xApplication ( Application(),uno::UNO_QUERY_THROW );
+ OUString sWorkPath = xApplication->getDefaultFilePath();
+ OUString sWorkURL;
+ osl::FileBase::getFileURLFromSystemPath( sWorkPath, sWorkURL );
+ aURL.SetURL( sWorkURL );
+ }
+ else
+ {
+ aURL.SetURL( sURL );
+ aURL.Append( sFileName );
+ }
+ sURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+
+ }
+
+ sal_Int32 nFileFormat = excel::XlFileFormat::xlExcel9795;
+ FileFormat >>= nFileFormat;
+
+ uno::Sequence storeProps{ comphelper::makePropertyValue("FilterName", uno::Any()) };
+ setFilterPropsFromFormat( nFileFormat, storeProps );
+
+ uno::Reference< frame::XStorable > xStor( getModel(), uno::UNO_QUERY_THROW );
+ xStor->storeAsURL( sURL, storeProps );
+}
+
+void SAL_CALL
+ScVbaWorkbook::ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& /*IgnorePrintAreas*/, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& /*FixedFormatExtClassPtr*/)
+{
+ uno::Reference< frame::XModel > xModel(getModel(), uno::UNO_SET_THROW);
+ uno::Reference< excel::XApplication > xApplication(Application(), uno::UNO_QUERY_THROW);
+
+ excel::ExportAsFixedFormatHelper(xModel, xApplication, Type, FileName, Quality,
+ IncludeDocProperties, From, To, OpenAfterPublish);
+}
+
+css::uno::Any SAL_CALL
+ScVbaWorkbook::Styles( const uno::Any& Item )
+{
+ // quick look and Styles object doesn't seem to have a valid parent
+ // or a least the object browser just shows an object that has no
+ // variables ( therefore... leave as NULL for now )
+ uno::Reference< XCollection > dStyles = new ScVbaStyles( uno::Reference< XHelperInterface >(), mxContext, getModel() );
+ if ( Item.hasValue() )
+ return dStyles->Item( Item, uno::Any() );
+ return uno::Any( dStyles );
+}
+
+uno::Any SAL_CALL
+ScVbaWorkbook::Names( const uno::Any& aIndex )
+{
+ uno::Reference< frame::XModel > xModel( getModel(), uno::UNO_SET_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xModel, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XNamedRanges > xNamedRanges( xProps->getPropertyValue("NamedRanges"), uno::UNO_QUERY_THROW );
+ uno::Reference< XCollection > xNames( new ScVbaNames( this, mxContext, xNamedRanges, xModel ) );
+ if ( aIndex.hasValue() )
+ return xNames->Item( aIndex, uno::Any() );
+ return uno::Any( xNames );
+}
+
+OUString
+ScVbaWorkbook::getServiceImplName()
+{
+ return "ScVbaWorkbook";
+}
+
+uno::Sequence< OUString >
+ScVbaWorkbook::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Workbook"
+ };
+ return aServiceNames;
+}
+
+OUString SAL_CALL
+ScVbaWorkbook::getCodeName()
+{
+ uno::Reference< beans::XPropertySet > xModelProp( getModel(), uno::UNO_QUERY_THROW );
+ return xModelProp->getPropertyValue("CodeName").get< OUString >();
+}
+
+sal_Int64
+ScVbaWorkbook::getSomething(const uno::Sequence<sal_Int8 >& rId )
+{
+ if (comphelper::isUnoTunnelId<ScVbaWorksheet>(rId)) // ???
+ {
+ return comphelper::getSomething_cast(this);
+ }
+ return 0;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScVbaWorkbook_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new ScVbaWorkbook(args, context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworkbook.hxx b/sc/source/ui/vba/vbaworkbook.hxx
new file mode 100644
index 0000000000..e6a838b1ab
--- /dev/null
+++ b/sc/source/ui/vba/vbaworkbook.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 .
+ */
+#pragma once
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <ooo/vba/excel/XWorkbook.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <vbahelper/vbadocumentbase.hxx>
+
+typedef cppu::ImplInheritanceHelper< VbaDocumentBase, ov::excel::XWorkbook > ScVbaWorkbook_BASE;
+
+class ScVbaWorkbook : public ScVbaWorkbook_BASE
+{
+ static css::uno::Sequence< sal_Int32 > ColorData;
+ static bool setFilterPropsFromFormat( sal_Int32 nFormat, css::uno::Sequence< css::beans::PropertyValue >& rProps );
+ void init();
+
+public:
+ ScVbaWorkbook( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::frame::XModel > const & xModel );
+ ScVbaWorkbook( css::uno::Sequence< css::uno::Any > const& aArgs, css::uno::Reference< css::uno::XComponentContext >const& xContext );
+
+ // Attributes
+ virtual sal_Bool SAL_CALL getProtectStructure() override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL getActiveSheet() override;
+ virtual sal_Bool SAL_CALL getPrecisionAsDisplayed() override;
+ virtual void SAL_CALL setPrecisionAsDisplayed( sal_Bool _precisionAsDisplayed ) override;
+ virtual OUString SAL_CALL getAuthor() override;
+ virtual void SAL_CALL setAuthor( const OUString& _author ) override;
+
+ // Methods
+ virtual css::uno::Any SAL_CALL Worksheets( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Sheets( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Windows( const css::uno::Any& aIndex ) override;
+ virtual void SAL_CALL Activate() override;
+ virtual void SAL_CALL Protect( const css::uno::Any & aPassword ) override;
+ virtual void SAL_CALL SaveAs( const css::uno::Any& FileName, const css::uno::Any& FileFormat, const css::uno::Any& Password, const css::uno::Any& WriteResPassword, const css::uno::Any& ReadOnlyRecommended, const css::uno::Any& CreateBackup, const css::uno::Any& AccessMode, const css::uno::Any& ConflictResolution, const css::uno::Any& AddToMru, const css::uno::Any& TextCodepage, const css::uno::Any& TextVisualLayout, const css::uno::Any& Local ) override;
+ virtual css::uno::Any SAL_CALL Names( const css::uno::Any& aIndex ) override;
+
+ virtual css::uno::Any SAL_CALL Styles( const css::uno::Any& Item ) override;
+ virtual void SAL_CALL ResetColors( ) override;
+ virtual css::uno::Any SAL_CALL Colors( const css::uno::Any& Index ) override;
+ virtual ::sal_Int32 SAL_CALL getFileFormat( ) override;
+ virtual void SAL_CALL SaveCopyAs( const OUString& Filename ) override;
+ virtual void SAL_CALL ExportAsFixedFormat( const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& IgnorePrintAreas, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& FixedFormatExtClassPtr) override;
+
+ // code name
+ virtual OUString SAL_CALL getCodeName() override;
+
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+ // XUnoTunnel
+ virtual ::sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8 >& rId ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworkbooks.cxx b/sc/source/ui/vba/vbaworkbooks.cxx
new file mode 100644
index 0000000000..3405806b8c
--- /dev/null
+++ b/sc/source/ui/vba/vbaworkbooks.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+
+#include <tools/urlobj.hxx>
+
+#include "excelvbahelper.hxx"
+#include "vbaworkbook.hxx"
+#include "vbaworkbooks.hxx"
+#include <vbahelper/vbahelper.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/file.hxx>
+#include <rtl/ref.hxx>
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+const sal_Int16 CUSTOM_CHAR = 5;
+
+static uno::Any
+getWorkbook( const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< sheet::XSpreadsheetDocument > &xDoc,
+ const uno::Reference< XHelperInterface >& xParent )
+{
+ // FIXME: fine as long as ScVbaWorkbook is stateless ...
+ uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
+ if( !xModel.is() )
+ return uno::Any();
+
+ uno::Reference< excel::XWorkbook > xWb( getVBADocument( xModel ), uno::UNO_QUERY );
+ if ( xWb.is() )
+ {
+ return uno::Any( xWb );
+ }
+
+ rtl::Reference<ScVbaWorkbook> pWb = new ScVbaWorkbook( xParent, xContext, xModel );
+ return uno::Any( uno::Reference< excel::XWorkbook > (pWb) );
+}
+
+namespace {
+
+class WorkBookEnumImpl : public EnumerationHelperImpl
+{
+public:
+ /// @throws uno::RuntimeException
+ WorkBookEnumImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ) {}
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ uno::Reference< sheet::XSpreadsheetDocument > xDoc( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ return getWorkbook( m_xContext, xDoc, m_xParent );
+ }
+
+};
+
+}
+
+ScVbaWorkbooks::ScVbaWorkbooks( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext >& xContext ) : ScVbaWorkbooks_BASE( xParent, xContext, VbaDocumentsBase::EXCEL_DOCUMENT )
+{
+}
+// XEnumerationAccess
+uno::Type
+ScVbaWorkbooks::getElementType()
+{
+ return cppu::UnoType<excel::XWorkbook>::get();
+}
+uno::Reference< container::XEnumeration >
+ScVbaWorkbooks::createEnumeration()
+{
+ // #FIXME it's possible the WorkBookEnumImpl here doesn't reflect
+ // the state of this object ( although it should ) would be
+ // safer to create an enumeration based on this objects state
+ // rather than one effectively based of the desktop component
+ uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
+ return new WorkBookEnumImpl( mxParent, mxContext, xEnumerationAccess->createEnumeration() );
+}
+
+uno::Any
+ScVbaWorkbooks::createCollectionObject( const css::uno::Any& aSource )
+{
+ uno::Reference< sheet::XSpreadsheetDocument > xDoc( aSource, uno::UNO_QUERY_THROW );
+ return getWorkbook( mxContext, xDoc, mxParent );
+}
+
+uno::Any SAL_CALL
+ScVbaWorkbooks::Add( const uno::Any& Template )
+{
+ uno::Reference< sheet::XSpreadsheetDocument > xSpreadDoc;
+ sal_Int32 nWorkbookType = 0;
+ OUString aTemplateFileName;
+ if( Template >>= nWorkbookType )
+ {
+ // nWorkbookType is a constant from XlWBATemplate (added in Excel 2007)
+ // TODO: create chart-sheet if supported by Calc
+
+ xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW );
+ // create a document with one sheet only
+ uno::Reference< sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xSheetsIA( xSheets, uno::UNO_QUERY_THROW );
+ while( xSheetsIA->getCount() > 1 )
+ {
+ uno::Reference< container::XNamed > xSheetName( xSheetsIA->getByIndex( xSheetsIA->getCount() - 1 ), uno::UNO_QUERY_THROW );
+ xSheets->removeByName( xSheetName->getName() );
+ }
+ }
+ else if( Template >>= aTemplateFileName )
+ {
+ // TODO: create document from template
+ xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW );
+ }
+ else if( !Template.hasValue() )
+ {
+ // regular spreadsheet document with configured number of sheets
+ xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW );
+ }
+ else
+ {
+ // illegal argument
+ throw uno::RuntimeException();
+ }
+
+ // need to set up the document modules ( and vba mode ) here
+ excel::setUpDocumentModules( xSpreadDoc );
+ if (!xSpreadDoc.is())
+ return uno::Any();
+
+ uno::Any aRet = getWorkbook( mxContext, xSpreadDoc, mxParent );
+ uno::Reference< excel::XWorkbook > xWBook( aRet, uno::UNO_QUERY );
+ if (xWBook.is())
+ xWBook->Activate();
+ return aRet;
+}
+
+void SAL_CALL
+ScVbaWorkbooks::Close()
+{
+}
+
+bool
+ScVbaWorkbooks::isTextFile( std::u16string_view sType )
+{
+ // will return true if the file is
+ // a) a variant of a text file
+ // b) a csv file
+ // c) unknown
+ // returning true basically means treat this like a csv file
+ return sType == u"generic_Text" || sType.empty();
+}
+
+bool
+ScVbaWorkbooks::isSpreadSheetFile( std::u16string_view sType )
+{
+ // include calc_QPro etc. ? ( not for the moment anyway )
+ return o3tl::starts_with( sType, u"calc_MS" )
+ || o3tl::starts_with( sType, u"MS Excel" )
+ || o3tl::starts_with( sType, u"calc8" )
+ || o3tl::starts_with( sType, u"calc_StarOffice" );
+}
+
+OUString
+ScVbaWorkbooks::getFileFilterType( const OUString& rFileName )
+{
+ uno::Reference< document::XTypeDetection > xTypeDetect( mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext), uno::UNO_QUERY_THROW );
+ uno::Sequence aMediaDesc{ comphelper::makePropertyValue("URL", rFileName) };
+ OUString sType = xTypeDetect->queryTypeByDescriptor( aMediaDesc, true );
+ return sType;
+}
+
+// #TODO# #FIXME# can any of the unused params below be used?
+uno::Any SAL_CALL
+ScVbaWorkbooks::Open( const OUString& rFileName, const uno::Any& /*UpdateLinks*/, const uno::Any& ReadOnly, const uno::Any& Format, const uno::Any& /*Password*/, const uno::Any& /*WriteResPassword*/, const uno::Any& /*IgnoreReadOnlyRecommended*/, const uno::Any& /*Origin*/, const uno::Any& Delimiter, const uno::Any& /*Editable*/, const uno::Any& /*Notify*/, const uno::Any& /*Converter*/, const uno::Any& /*AddToMru*/ )
+{
+ // we need to detect if this is a URL, if not then assume it's a file path
+ OUString aURL;
+ INetURLObject aObj;
+ aObj.SetURL( rFileName );
+ bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
+ if ( bIsURL )
+ aURL = rFileName;
+ else
+ osl::FileBase::getFileURLFromSystemPath( rFileName, aURL );
+
+ uno::Sequence< beans::PropertyValue > sProps;
+
+ OUString sType = getFileFilterType( aURL );
+ // A text file means it needs to be processed as a csv file
+ if ( isTextFile( sType ) )
+ {
+ // Values for format
+ // 1 Tabs
+ // 2 Commas
+ // 3 Spaces
+ // 4 Semicolons
+ // 5 Nothing
+ // 6 Custom character (see the Delimiter argument
+ // no format means use the current delimiter
+ sal_Int16 const delims[] { 0 /*default not used*/, 9/*tab*/, 44/*comma*/, 32/*space*/, 59/*semicolon*/ };
+
+ OUString sFormat;
+ sal_Int16 nFormat = 0; // default indicator
+
+ if ( Format.hasValue() )
+ {
+ Format >>= nFormat; // val of nFormat overwritten if extracted
+ // validate param
+ if ( nFormat < 1 || nFormat > 6 )
+ throw uno::RuntimeException("Illegal value for Format" );
+ }
+
+ sal_Int16 nDelim = getCurrentDelim();
+
+ if ( nFormat > 0 && nFormat < CUSTOM_CHAR )
+ {
+ nDelim = delims[ nFormat ];
+ }
+ else if ( nFormat > CUSTOM_CHAR )
+ {
+ // Need to check Delimiter param
+ if ( !Delimiter.hasValue() )
+ throw uno::RuntimeException("Expected value for Delimiter" );
+ OUString sStr;
+ Delimiter >>= sStr;
+ if ( sStr.isEmpty() )
+ throw uno::RuntimeException("Incorrect value for Delimiter" );
+
+ nDelim = sStr[0];
+
+ }
+
+ getCurrentDelim() = nDelim; //set new current
+
+ sFormat = OUString::number( nDelim ) + ",34,0,1";
+
+ sProps = { comphelper::makePropertyValue("FilterOptions", sFormat),
+ comphelper::makePropertyValue("FilterName", SC_TEXT_CSV_FILTER_NAME),
+ // Ensure WORKAROUND_CSV_TXT_BUG_i60158 gets called in typedetection.cxx so
+ // csv is forced for deep detected 'writerxxx' types
+ comphelper::makePropertyValue(
+ "DocumentService", OUString("com.sun.star.sheet.SpreadsheetDocument")) };
+ }
+ else if ( !isSpreadSheetFile( sType ) )
+ throw uno::RuntimeException("Bad Format" );
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( openDocument( rFileName, ReadOnly, sProps ), uno::UNO_QUERY_THROW );
+ uno::Any aRet = getWorkbook( mxContext, xSpreadDoc, mxParent );
+ uno::Reference< excel::XWorkbook > xWBook( aRet, uno::UNO_QUERY );
+ if ( xWBook.is() )
+ xWBook->Activate();
+ return aRet;
+}
+
+OUString
+ScVbaWorkbooks::getServiceImplName()
+{
+ return "ScVbaWorkbooks";
+}
+
+css::uno::Sequence<OUString>
+ScVbaWorkbooks::getServiceNames()
+{
+ static uno::Sequence< OUString > const sNames
+ {
+ "ooo.vba.excel.Workbooks"
+ };
+ return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworkbooks.hxx b/sc/source/ui/vba/vbaworkbooks.hxx
new file mode 100644
index 0000000000..45d41757a4
--- /dev/null
+++ b/sc/source/ui/vba/vbaworkbooks.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 <sal/config.h>
+
+#include <string_view>
+
+#include <cppuhelper/implbase.hxx>
+#include <ooo/vba/excel/XWorkbooks.hpp>
+#include <vbahelper/vbadocumentsbase.hxx>
+
+typedef cppu::ImplInheritanceHelper< VbaDocumentsBase, ov::excel::XWorkbooks > ScVbaWorkbooks_BASE;
+
+class ScVbaWorkbooks : public ScVbaWorkbooks_BASE
+{
+private:
+ OUString getFileFilterType( const OUString& rString );
+ static bool isTextFile( std::u16string_view rString );
+ static bool isSpreadSheetFile( std::u16string_view rString );
+ static sal_Int16& getCurrentDelim(){ static sal_Int16 nDelim = 44; return nDelim; }
+public:
+ ScVbaWorkbooks( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // ScVbaWorkbooks_BASE
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+ // XWorkbooks
+ virtual css::uno::Any SAL_CALL Add( const css::uno::Any& Template ) override;
+ virtual void SAL_CALL Close( ) override;
+ virtual css::uno::Any SAL_CALL Open( const OUString& Filename, const css::uno::Any& UpdateLinks, const css::uno::Any& ReadOnly, const css::uno::Any& Format, const css::uno::Any& Password, const css::uno::Any& WriteResPassword, const css::uno::Any& IgnoreReadOnlyRecommended, const css::uno::Any& Origin, const css::uno::Any& Delimiter, const css::uno::Any& Editable, const css::uno::Any& Notify, const css::uno::Any& Converter, const css::uno::Any& AddToMru ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworksheet.cxx b/sc/source/ui/vba/vbaworksheet.cxx
new file mode 100644
index 0000000000..466e904d6f
--- /dev/null
+++ b/sc/source/ui/vba/vbaworksheet.cxx
@@ -0,0 +1,1074 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 "vbaworksheet.hxx"
+#include "vbanames.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/util/XProtectable.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/sheet/XSpreadsheetView.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XCalculatable.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XSheetCellRange.hpp>
+#include <com/sun/star/sheet/XSheetCellCursor.hpp>
+#include <com/sun/star/sheet/XSheetAnnotationsSupplier.hpp>
+#include <com/sun/star/sheet/XUsedAreaCursor.hpp>
+#include <com/sun/star/sheet/XSpreadsheets.hpp>
+#include <com/sun/star/sheet/XSheetOutline.hpp>
+#include <com/sun/star/sheet/XSheetPageBreak.hpp>
+#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp>
+#include <com/sun/star/sheet/XNamedRanges.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/table/XTableChartsSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <ooo/vba/excel/XApplication.hpp>
+#include <ooo/vba/excel/XlEnableSelection.hpp>
+#include <ooo/vba/excel/XlSheetVisibility.hpp>
+#include <ooo/vba/XControlProvider.hpp>
+
+#include <basic/sberrors.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <utility>
+#include <vbahelper/vbashapes.hxx>
+
+//zhangyun showdataform
+#include <scabstdlg.hxx>
+#include <tabvwsh.hxx>
+
+#include <tabprotection.hxx>
+#include "excelvbahelper.hxx"
+#include "vbaoutline.hxx"
+#include "vbarange.hxx"
+#include "vbacomments.hxx"
+#include "vbachartobjects.hxx"
+#include "vbapivottables.hxx"
+#include "vbaoleobjects.hxx"
+#include "vbapagesetup.hxx"
+#include "vbapagebreaks.hxx"
+#include "vbaworksheets.hxx"
+#include "vbahyperlinks.hxx"
+#include "vbasheetobjects.hxx"
+#include <dbdata.hxx>
+
+#include <attrib.hxx>
+
+#define STANDARDWIDTH 2267
+#define STANDARDHEIGHT 427
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+static void getNewSpreadsheetName (OUString &aNewName, std::u16string_view aOldName, const uno::Reference <sheet::XSpreadsheetDocument>& xSpreadDoc )
+{
+ if (!xSpreadDoc.is())
+ throw lang::IllegalArgumentException( "getNewSpreadsheetName() xSpreadDoc is null", uno::Reference< uno::XInterface >(), 1 );
+ static const char aUnderScore[] = "_";
+ int currentNum =2;
+ aNewName = OUString::Concat(aOldName) + aUnderScore + OUString::number(currentNum) ;
+ SCTAB nTab = 0;
+ while ( ScVbaWorksheets::nameExists(xSpreadDoc,aNewName, nTab ) )
+ {
+ aNewName = OUString::Concat(aOldName) + aUnderScore + OUString::number(++currentNum);
+ }
+}
+
+static void removeAllSheets( const uno::Reference <sheet::XSpreadsheetDocument>& xSpreadDoc, const OUString& aSheetName)
+{
+ if (!xSpreadDoc.is())
+ throw lang::IllegalArgumentException( "removeAllSheets() xSpreadDoc is null", uno::Reference< uno::XInterface >(), 1 );
+ uno::Reference<sheet::XSpreadsheets> xSheets = xSpreadDoc->getSheets();
+ uno::Reference <container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+
+ if ( !xIndex.is() )
+ return;
+
+ uno::Reference<container::XNameContainer> xNameContainer(xSheets,uno::UNO_QUERY_THROW);
+ for (sal_Int32 i = xIndex->getCount() -1; i>= 1; i--)
+ {
+ uno::Reference< sheet::XSpreadsheet > xSheet(xIndex->getByIndex(i), uno::UNO_QUERY);
+ uno::Reference< container::XNamed > xNamed( xSheet, uno::UNO_QUERY_THROW );
+ xNameContainer->removeByName(xNamed->getName());
+ }
+
+ uno::Reference< sheet::XSpreadsheet > xSheet(xIndex->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference< container::XNamed > xNamed( xSheet, uno::UNO_QUERY_THROW );
+ xNamed->setName(aSheetName);
+}
+
+static uno::Reference<frame::XModel>
+openNewDoc(const OUString& aSheetName )
+{
+ uno::Reference<frame::XModel> xModel;
+ try
+ {
+ uno::Reference< uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+
+ uno::Reference <frame::XDesktop2 > xComponentLoader = frame::Desktop::create(xContext);
+
+ uno::Reference<lang::XComponent > xComponent( xComponentLoader->loadComponentFromURL(
+ "private:factory/scalc",
+ "_blank", 0,
+ uno::Sequence < css::beans::PropertyValue >() ) );
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( xComponent, uno::UNO_QUERY_THROW );
+ removeAllSheets(xSpreadDoc,aSheetName);
+ xModel.set(xSpreadDoc,uno::UNO_QUERY_THROW);
+ }
+ catch ( uno::Exception & /*e*/ )
+ {
+ }
+ return xModel;
+}
+
+ScVbaWorksheet::ScVbaWorksheet(const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext,
+ uno::Reference< sheet::XSpreadsheet > xSheet,
+ uno::Reference< frame::XModel > xModel ) : WorksheetImpl_BASE( xParent, xContext ), mxSheet(std::move( xSheet )), mxModel(std::move(xModel)), mbVeryHidden( false )
+{
+}
+
+ScVbaWorksheet::ScVbaWorksheet( uno::Sequence< uno::Any> const & args,
+ uno::Reference< uno::XComponentContext> const & xContext ) : WorksheetImpl_BASE( getXSomethingFromArgs< XHelperInterface >( args, 0 ), xContext ), mxModel( getXSomethingFromArgs< frame::XModel >( args, 1 ) ), mbVeryHidden( false )
+{
+ if ( args.getLength() < 3 )
+ throw lang::IllegalArgumentException();
+
+ OUString sSheetName;
+ args[2] >>= sSheetName;
+
+ uno::Reference< sheet::XSpreadsheetDocument > xSpreadDoc( mxModel, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xNameAccess( xSpreadDoc->getSheets(), uno::UNO_QUERY_THROW );
+ mxSheet.set( xNameAccess->getByName( sSheetName ), uno::UNO_QUERY_THROW );
+}
+
+ScVbaWorksheet::~ScVbaWorksheet()
+{
+}
+
+const uno::Sequence<sal_Int8>& ScVbaWorksheet::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theScVbaWorksheetUnoTunnelId;
+ return theScVbaWorksheetUnoTunnelId.getSeq();
+}
+
+uno::Reference< ov::excel::XWorksheet >
+ScVbaWorksheet::createSheetCopyInNewDoc(const OUString& aCurrSheetName)
+{
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = getSheet()->createCursor( );
+ uno::Reference<sheet::XUsedAreaCursor> xUsedCursor(xSheetCellCursor,uno::UNO_QUERY_THROW);
+ uno::Reference<excel::XRange> xRange = new ScVbaRange( this, mxContext, xSheetCellCursor);
+ if (xRange.is())
+ xRange->Select();
+ excel::implnCopy(mxModel);
+ uno::Reference<frame::XModel> xModel = openNewDoc(aCurrSheetName);
+ if (xModel.is())
+ {
+ excel::implnPaste(xModel);
+ }
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( xModel, uno::UNO_QUERY_THROW );
+ excel::setUpDocumentModules(xSpreadDoc);
+ uno::Reference <sheet::XSpreadsheets> xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW );
+ uno::Reference <container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSpreadsheet > xSheet(xIndex->getByIndex(0), uno::UNO_QUERY_THROW);
+
+ ScDocShell* pShell = excel::getDocShell( xModel );
+ OUString aCodeName;
+ if (pShell)
+ pShell->GetDocument().GetCodeName( 0, aCodeName );
+ return uno::Reference< excel::XWorksheet >( getUnoDocModule( aCodeName, pShell ), uno::UNO_QUERY_THROW );
+}
+
+css::uno::Reference< ov::excel::XWorksheet >
+ScVbaWorksheet::createSheetCopy(uno::Reference<excel::XWorksheet> const & xSheet, bool bAfter)
+{
+ OUString aCurrSheetName = getName();
+ ScVbaWorksheet* pDestSheet = excel::getImplFromDocModuleWrapper<ScVbaWorksheet>( xSheet );
+
+ uno::Reference <sheet::XSpreadsheetDocument> xDestDoc( pDestSheet->getModel(), uno::UNO_QUERY );
+ uno::Reference <sheet::XSpreadsheetDocument> xSrcDoc( getModel(), uno::UNO_QUERY );
+
+ SCTAB nDest = 0;
+ SCTAB nSrc = 0;
+ OUString aSheetName = xSheet->getName();
+ bool bSameDoc = ( pDestSheet->getModel() == getModel() );
+ bool bDestSheetExists = ScVbaWorksheets::nameExists (xDestDoc, aSheetName, nDest );
+ bool bSheetExists = ScVbaWorksheets::nameExists (xSrcDoc, aCurrSheetName, nSrc );
+
+ // set sheet name to be newSheet name
+ aSheetName = aCurrSheetName;
+ if ( bSheetExists && bDestSheetExists )
+ {
+ SCTAB nDummy=0;
+ if(bAfter)
+ nDest++;
+ uno::Reference<sheet::XSpreadsheets> xSheets = xDestDoc->getSheets();
+ if ( bSameDoc || ScVbaWorksheets::nameExists( xDestDoc, aCurrSheetName, nDummy ) )
+ getNewSpreadsheetName(aSheetName,aCurrSheetName,xDestDoc);
+ if ( bSameDoc )
+ xSheets->copyByName(aCurrSheetName,aSheetName,nDest);
+ else
+ {
+ ScDocShell* pDestDocShell = excel::getDocShell( pDestSheet->getModel() );
+ ScDocShell* pSrcDocShell = excel::getDocShell( getModel() );
+ if ( pDestDocShell && pSrcDocShell )
+ pDestDocShell->TransferTab( *pSrcDocShell, nSrc, nDest, true, true );
+ }
+ }
+ // return new sheet
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XWorksheet > xNewSheet( xApplication->Worksheets( uno::Any( aSheetName ) ), uno::UNO_QUERY_THROW );
+ return xNewSheet;
+}
+
+OUString
+ScVbaWorksheet::getName()
+{
+ uno::Reference< container::XNamed > xNamed( getSheet(), uno::UNO_QUERY_THROW );
+ return xNamed->getName();
+}
+
+void
+ScVbaWorksheet::setName(const OUString &rName )
+{
+ uno::Reference< container::XNamed > xNamed( getSheet(), uno::UNO_QUERY_THROW );
+ xNamed->setName( rName );
+}
+
+sal_Int32
+ScVbaWorksheet::getVisible()
+{
+ uno::Reference< beans::XPropertySet > xProps( getSheet(), uno::UNO_QUERY_THROW );
+ bool bVisible = false;
+ xProps->getPropertyValue( "IsVisible" ) >>= bVisible;
+ using namespace ::ooo::vba::excel::XlSheetVisibility;
+ return bVisible ? xlSheetVisible : (mbVeryHidden ? xlSheetVeryHidden : xlSheetHidden);
+}
+
+void
+ScVbaWorksheet::setVisible( sal_Int32 nVisible )
+{
+ using namespace ::ooo::vba::excel::XlSheetVisibility;
+ bool bVisible = true;
+ switch( nVisible )
+ {
+ case xlSheetVisible: case 1: // Excel accepts -1 and 1 for visible sheets
+ bVisible = true;
+ mbVeryHidden = false;
+ break;
+ case xlSheetHidden:
+ bVisible = false;
+ mbVeryHidden = false;
+ break;
+ case xlSheetVeryHidden:
+ bVisible = false;
+ mbVeryHidden = true;
+ break;
+ default:
+ throw uno::RuntimeException();
+ }
+ uno::Reference< beans::XPropertySet > xProps( getSheet(), uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue( "IsVisible", uno::Any( bVisible ) );
+}
+
+sal_Int16
+ScVbaWorksheet::getIndex()
+{
+ return getSheetID() + 1;
+}
+
+sal_Int32
+ScVbaWorksheet::getEnableSelection()
+{
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( getModel(), uno::UNO_QUERY_THROW );
+ SCTAB nTab = 0;
+ if ( !ScVbaWorksheets::nameExists(xSpreadDoc, getName(), nTab) )
+ throw uno::RuntimeException("Sheet Name does not exist." );
+
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ bool bLockedCells = false;
+ bool bUnlockedCells = false;
+ if( pProtect )
+ {
+ bLockedCells = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bUnlockedCells = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+ if( bLockedCells )
+ return excel::XlEnableSelection::xlNoRestrictions;
+ if( bUnlockedCells )
+ return excel::XlEnableSelection::xlUnlockedCells;
+ }
+ return excel::XlEnableSelection::xlNoSelection;
+
+}
+
+void
+ScVbaWorksheet::setEnableSelection( sal_Int32 nSelection )
+{
+ if( (nSelection != excel::XlEnableSelection::xlNoRestrictions) &&
+ (nSelection != excel::XlEnableSelection::xlUnlockedCells) &&
+ (nSelection != excel::XlEnableSelection::xlNoSelection) )
+ {
+ DebugHelper::runtimeexception(ERRCODE_BASIC_BAD_PARAMETER);
+ }
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( getModel(), uno::UNO_QUERY_THROW );
+ SCTAB nTab = 0;
+ if ( !ScVbaWorksheets::nameExists(xSpreadDoc, getName(), nTab) )
+ throw uno::RuntimeException("Sheet Name does not exist." );
+
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ // default is xlNoSelection
+ bool bLockedCells = false;
+ bool bUnlockedCells = false;
+ if( nSelection == excel::XlEnableSelection::xlNoRestrictions )
+ {
+ bLockedCells = true;
+ bUnlockedCells = true;
+ }
+ else if( nSelection == excel::XlEnableSelection::xlUnlockedCells )
+ {
+ bUnlockedCells = true;
+ }
+ if( pProtect )
+ {
+ ScTableProtection aNewProtect(*pProtect);
+ aNewProtect.setOption(ScTableProtection::SELECT_LOCKED_CELLS, bLockedCells);
+ aNewProtect.setOption(ScTableProtection::SELECT_UNLOCKED_CELLS, bUnlockedCells);
+ rDoc.SetTabProtection(nTab, &aNewProtect);
+ }
+ }
+}
+
+sal_Bool SAL_CALL ScVbaWorksheet::getAutoFilterMode()
+{
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ ScDBData* pDBData = rDoc.GetAnonymousDBData(getSheetID());
+ if (pDBData)
+ return pDBData->HasAutoFilter();
+ }
+ return false;
+}
+
+void SAL_CALL ScVbaWorksheet::setAutoFilterMode( sal_Bool bAutoFilterMode )
+{
+ ScDocShell* pDocShell = excel::getDocShell( getModel() );
+ if (!pDocShell)
+ return;
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDBData* pDBData = rDoc.GetAnonymousDBData(getSheetID());
+ if (!pDBData)
+ return;
+
+ pDBData->SetAutoFilter(bAutoFilterMode);
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ if (bAutoFilterMode)
+ rDoc.ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), ScMF::Auto );
+ else if (!bAutoFilterMode)
+ rDoc.RemoveFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), ScMF::Auto );
+ ScRange aPaintRange(aRange.aStart, aRange.aEnd);
+ aPaintRange.aEnd.SetRow(aPaintRange.aStart.Row());
+ pDocShell->PostPaint(aPaintRange, PaintPartFlags::Grid);
+}
+
+uno::Reference< excel::XRange >
+ScVbaWorksheet::getUsedRange()
+{
+ uno::Reference< sheet::XSheetCellRange > xSheetCellRange(getSheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor( getSheet()->createCursorByRange( xSheetCellRange ), uno::UNO_SET_THROW );
+ uno::Reference<sheet::XUsedAreaCursor> xUsedCursor(xSheetCellCursor,uno::UNO_QUERY_THROW);
+ xUsedCursor->gotoStartOfUsedArea( false );
+ xUsedCursor->gotoEndOfUsedArea( true );
+ return new ScVbaRange(this, mxContext, xSheetCellCursor);
+}
+
+uno::Reference< excel::XOutline >
+ScVbaWorksheet::Outline( )
+{
+ uno::Reference<sheet::XSheetOutline> xOutline(getSheet(),uno::UNO_QUERY_THROW);
+ return new ScVbaOutline( this, mxContext, xOutline);
+}
+
+uno::Reference< excel::XPageSetup >
+ScVbaWorksheet::PageSetup( )
+{
+ return new ScVbaPageSetup( this, mxContext, getSheet(), getModel() );
+}
+
+uno::Any
+ScVbaWorksheet::HPageBreaks( const uno::Any& aIndex )
+{
+ uno::Reference< sheet::XSheetPageBreak > xSheetPageBreak(getSheet(),uno::UNO_QUERY_THROW);
+ uno::Reference< excel::XHPageBreaks > xHPageBreaks( new ScVbaHPageBreaks( this, mxContext, xSheetPageBreak));
+ if ( aIndex.hasValue() )
+ return xHPageBreaks->Item( aIndex, uno::Any());
+ return uno::Any( xHPageBreaks );
+}
+
+uno::Any
+ScVbaWorksheet::VPageBreaks( const uno::Any& aIndex )
+{
+ uno::Reference< sheet::XSheetPageBreak > xSheetPageBreak( getSheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< excel::XVPageBreaks > xVPageBreaks( new ScVbaVPageBreaks( this, mxContext, xSheetPageBreak ) );
+ if( aIndex.hasValue() )
+ return xVPageBreaks->Item( aIndex, uno::Any());
+ return uno::Any( xVPageBreaks );
+}
+
+sal_Int32
+ScVbaWorksheet::getStandardWidth()
+{
+ return STANDARDWIDTH ;
+}
+
+sal_Int32
+ScVbaWorksheet::getStandardHeight()
+{
+ return STANDARDHEIGHT;
+}
+
+sal_Bool
+ScVbaWorksheet::getProtectionMode()
+{
+ return false;
+}
+
+sal_Bool
+ScVbaWorksheet::getProtectContents()
+{
+ uno::Reference<util::XProtectable > xProtectable(getSheet(), uno::UNO_QUERY_THROW);
+ return xProtectable->isProtected();
+}
+
+sal_Bool
+ScVbaWorksheet::getProtectDrawingObjects()
+{
+ SCTAB nTab = 0;
+ OUString aSheetName = getName();
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( getModel(), uno::UNO_QUERY_THROW );
+ bool bSheetExists = ScVbaWorksheets::nameExists (xSpreadDoc, aSheetName, nTab);
+ if ( bSheetExists )
+ {
+ if ( ScDocShell* pShell = excel::getDocShell( getModel() ))
+ {
+ ScDocument& rDoc = pShell->GetDocument();
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if ( pProtect )
+ return pProtect->isOptionEnabled( ScTableProtection::OBJECTS );
+ }
+ }
+ return false;
+}
+
+sal_Bool
+ScVbaWorksheet::getProtectScenarios()
+{
+ return false;
+}
+
+void
+ScVbaWorksheet::Activate()
+{
+ uno::Reference< sheet::XSpreadsheetView > xSpreadsheet(
+ getModel()->getCurrentController(), uno::UNO_QUERY_THROW );
+ xSpreadsheet->setActiveSheet(getSheet());
+}
+
+void
+ScVbaWorksheet::Select()
+{
+ Activate();
+}
+
+void
+ScVbaWorksheet::Move( const uno::Any& Before, const uno::Any& After )
+{
+ uno::Reference<excel::XWorksheet> xSheet;
+ OUString aCurrSheetName = getName();
+
+ if (!(Before >>= xSheet) && !(After >>=xSheet)&& !(Before.hasValue()) && !(After.hasValue()))
+ {
+ uno::Reference< sheet::XSheetCellCursor > xSheetCellCursor = getSheet()->createCursor( );
+ uno::Reference<sheet::XUsedAreaCursor> xUsedCursor(xSheetCellCursor,uno::UNO_QUERY_THROW);
+ // #FIXME needs worksheet as parent
+ uno::Reference<excel::XRange> xRange = new ScVbaRange( this, mxContext, xSheetCellCursor);
+ if (xRange.is())
+ xRange->Select();
+ excel::implnCopy(mxModel);
+ uno::Reference<frame::XModel> xModel = openNewDoc(aCurrSheetName);
+ if (xModel.is())
+ {
+ excel::implnPaste(xModel);
+ Delete();
+ }
+ return ;
+ }
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( getModel(), uno::UNO_QUERY_THROW );
+ SCTAB nDest = 0;
+ if ( ScVbaWorksheets::nameExists (xSpreadDoc, xSheet->getName(), nDest) )
+ {
+ bool bAfter = After.hasValue();
+ if (bAfter)
+ nDest++;
+ uno::Reference<sheet::XSpreadsheets> xSheets = xSpreadDoc->getSheets();
+ xSheets->moveByName(aCurrSheetName,nDest);
+ }
+}
+
+void
+ScVbaWorksheet::Copy( const uno::Any& Before, const uno::Any& After )
+{
+ uno::Reference<excel::XWorksheet> xSheet;
+ if (!(Before >>= xSheet) && !(After >>=xSheet)&& !(Before.hasValue()) && !(After.hasValue()))
+ {
+ createSheetCopyInNewDoc(getName());
+ return;
+ }
+
+ uno::Reference<excel::XWorksheet> xNewSheet = createSheetCopy(xSheet, After.hasValue());
+ xNewSheet->Activate();
+}
+
+void
+ScVbaWorksheet::Paste( const uno::Any& Destination, const uno::Any& /*Link*/ )
+{
+ // #TODO# #FIXME# Link is not used
+ uno::Reference<excel::XRange> xRange( Destination, uno::UNO_QUERY );
+ if ( xRange.is() )
+ xRange->Select();
+ excel::implnPaste( mxModel );
+}
+
+void
+ScVbaWorksheet::Delete()
+{
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( getModel(), uno::UNO_QUERY_THROW );
+ OUString aSheetName = getName();
+ SCTAB nTab = 0;
+ if (!ScVbaWorksheets::nameExists(xSpreadDoc, aSheetName, nTab ))
+ {
+ return;
+ }
+ uno::Reference<sheet::XSpreadsheets> xSheets = xSpreadDoc->getSheets();
+ uno::Reference<container::XNameContainer> xNameContainer(xSheets,uno::UNO_QUERY_THROW);
+ xNameContainer->removeByName(aSheetName);
+ mxSheet.clear();
+}
+
+uno::Reference< excel::XWorksheet >
+ScVbaWorksheet::getSheetAtOffset(SCTAB offset)
+{
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( getModel(), uno::UNO_QUERY_THROW );
+ uno::Reference <sheet::XSpreadsheets> xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW );
+ uno::Reference <container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY_THROW );
+
+ SCTAB nIdx = 0;
+ if ( !ScVbaWorksheets::nameExists (xSpreadDoc, getName(), nIdx ) )
+ return uno::Reference< excel::XWorksheet >();
+ nIdx = nIdx + offset;
+ uno::Reference< sheet::XSpreadsheet > xSheet(xIndex->getByIndex(nIdx), uno::UNO_QUERY_THROW);
+ // parent will be the parent of 'this' worksheet
+ return new ScVbaWorksheet (getParent(), mxContext, xSheet, getModel());
+}
+
+uno::Reference< excel::XWorksheet >
+ScVbaWorksheet::getNext()
+{
+ return getSheetAtOffset(static_cast<SCTAB>(1));
+}
+
+uno::Reference< excel::XWorksheet >
+ScVbaWorksheet::getPrevious()
+{
+ return getSheetAtOffset(-1);
+}
+
+void
+ScVbaWorksheet::Protect( const uno::Any& Password, const uno::Any& /*DrawingObjects*/, const uno::Any& /*Contents*/, const uno::Any& /*Scenarios*/, const uno::Any& /*UserInterfaceOnly*/ )
+{
+ // #TODO# #FIXME# is there anything we can do with the unused param
+ // can the implementation use anything else here
+ uno::Reference<util::XProtectable > xProtectable(getSheet(), uno::UNO_QUERY_THROW);
+ OUString aPasswd;
+ Password >>= aPasswd;
+ xProtectable->protect( aPasswd );
+}
+
+void
+ScVbaWorksheet::Unprotect( const uno::Any& Password )
+{
+ uno::Reference<util::XProtectable > xProtectable(getSheet(), uno::UNO_QUERY_THROW);
+ OUString aPasswd;
+ Password >>= aPasswd;
+ xProtectable->unprotect( aPasswd );
+}
+
+void
+ScVbaWorksheet::Calculate()
+{
+ uno::Reference <sheet::XCalculatable> xReCalculate(getModel(), uno::UNO_QUERY_THROW);
+ xReCalculate->calculate();
+}
+
+uno::Reference< excel::XRange >
+ScVbaWorksheet::Range( const ::uno::Any& Cell1, const ::uno::Any& Cell2 )
+{
+ uno::Reference< excel::XRange > xSheetRange( new ScVbaRange( this, mxContext
+, uno::Reference< table::XCellRange >( getSheet(), uno::UNO_QUERY_THROW ) ) );
+ return xSheetRange->Range( Cell1, Cell2 );
+}
+
+void
+ScVbaWorksheet::CheckSpelling( const uno::Any& /*CustomDictionary*/,const uno::Any& /*IgnoreUppercase*/,const uno::Any& /*AlwaysSuggest*/, const uno::Any& /*SpellingLang*/ )
+{
+ // #TODO# #FIXME# unused params above, can we do anything with those
+ uno::Reference< frame::XModel > xModel( getModel() );
+ dispatchRequests(xModel,".uno:SpellDialog");
+}
+
+uno::Reference< excel::XRange >
+ScVbaWorksheet::getSheetRange()
+{
+ uno::Reference< table::XCellRange > xRange( getSheet(),uno::UNO_QUERY_THROW );
+ return uno::Reference< excel::XRange >( new ScVbaRange( this, mxContext, xRange ) );
+}
+
+// These are hacks - we prolly (somehow) need to inherit
+// the vbarange functionality here ...
+uno::Reference< excel::XRange >
+ScVbaWorksheet::Cells( const ::uno::Any &nRow, const ::uno::Any &nCol )
+{
+ // Performance optimization for often-called Cells method:
+ // Use a common helper method instead of creating a new ScVbaRange object
+ uno::Reference< table::XCellRange > xRange( getSheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< frame::XModel > xModel( getModel(), uno::UNO_SET_THROW );
+ if(ScDocShell* pShell = excel::getDocShell( xModel ))
+ return ScVbaRange::CellsHelper(pShell->GetDocument(), this, mxContext, xRange, nRow, nCol );
+ throw uno::RuntimeException();
+}
+
+uno::Reference< excel::XRange >
+ScVbaWorksheet::Rows(const uno::Any& aIndex )
+{
+ return getSheetRange()->Rows( aIndex );
+}
+
+uno::Reference< excel::XRange >
+ScVbaWorksheet::Columns( const uno::Any& aIndex )
+{
+ return getSheetRange()->Columns( aIndex );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::ChartObjects( const uno::Any& Index )
+{
+ if ( !mxCharts.is() )
+ {
+ uno::Reference< table::XTableChartsSupplier > xChartSupplier( getSheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< table::XTableCharts > xTableCharts = xChartSupplier->getCharts();
+
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( mxSheet, uno::UNO_QUERY_THROW );
+ mxCharts = new ScVbaChartObjects( this, mxContext, xTableCharts, xDrawPageSupplier );
+ }
+ if ( Index.hasValue() )
+ {
+ uno::Reference< XCollection > xColl( mxCharts, uno::UNO_QUERY_THROW );
+ return xColl->Item( Index, uno::Any() );
+ }
+ else
+ return uno::Any( mxCharts );
+
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::PivotTables( const uno::Any& Index )
+{
+ uno::Reference< css::sheet::XSpreadsheet > xSheet = getSheet();
+ uno::Reference< sheet::XDataPilotTablesSupplier > xTables(xSheet, uno::UNO_QUERY_THROW ) ;
+ uno::Reference< container::XIndexAccess > xIndexAccess( xTables->getDataPilotTables(), uno::UNO_QUERY_THROW );
+
+ uno::Reference< XCollection > xColl( new ScVbaPivotTables( this, mxContext, xIndexAccess ) );
+ if ( Index.hasValue() )
+ return xColl->Item( Index, uno::Any() );
+ return uno::Any( xColl );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Comments( const uno::Any& Index )
+{
+ uno::Reference< css::sheet::XSpreadsheet > xSheet = getSheet();
+ uno::Reference< sheet::XSheetAnnotationsSupplier > xAnnosSupp( xSheet, uno::UNO_QUERY_THROW );
+ uno::Reference< sheet::XSheetAnnotations > xAnnos( xAnnosSupp->getAnnotations(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xAnnos, uno::UNO_QUERY_THROW );
+ uno::Reference< XCollection > xColl( new ScVbaComments( this, mxContext, mxModel, xIndexAccess ) );
+ if ( Index.hasValue() )
+ return xColl->Item( Index, uno::Any() );
+ return uno::Any( xColl );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Hyperlinks( const uno::Any& aIndex )
+{
+ /* The worksheet always returns the same Hyperlinks object.
+ See vbahyperlinks.hxx for more details. */
+ if( !mxHlinks.is() )
+ mxHlinks.set( new ScVbaHyperlinks( this, mxContext ) );
+ if( aIndex.hasValue() )
+ return uno::Reference< XCollection >( mxHlinks, uno::UNO_QUERY_THROW )->Item( aIndex, uno::Any() );
+ return uno::Any( mxHlinks );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Names( const css::uno::Any& aIndex )
+{
+ css::uno::Reference<css::beans::XPropertySet> xProps(getSheet(), css::uno::UNO_QUERY_THROW);
+ uno::Reference< sheet::XNamedRanges > xNamedRanges( xProps->getPropertyValue("NamedRanges"), uno::UNO_QUERY_THROW );
+ uno::Reference< XCollection > xNames( new ScVbaNames( this, mxContext, xNamedRanges, mxModel ) );
+ if ( aIndex.hasValue() )
+ return xNames->Item( aIndex, uno::Any() );
+ return uno::Any( xNames );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::OLEObjects( const uno::Any& Index )
+{
+ uno::Reference< sheet::XSpreadsheet > xSpreadsheet( getSheet(), uno::UNO_SET_THROW );
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( xSpreadsheet, uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XDrawPage > xDrawPage( xDrawPageSupplier->getDrawPage(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xDrawPage, uno::UNO_QUERY_THROW );
+
+ uno::Reference< excel::XOLEObjects >xOleObjects( new ScVbaOLEObjects( this, mxContext, xIndexAccess ) );
+ if( Index.hasValue() )
+ return xOleObjects->Item( Index, uno::Any() );
+ return uno::Any( xOleObjects );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Shapes( const uno::Any& aIndex )
+{
+ uno::Reference< sheet::XSpreadsheet > xSpreadsheet( getSheet(), uno::UNO_SET_THROW );
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( xSpreadsheet, uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XShapes > xShapes( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xShapes, uno::UNO_QUERY_THROW );
+
+ uno::Reference< msforms::XShapes> xVbaShapes( new ScVbaShapes( this, mxContext, xIndexAccess, getModel() ) );
+ if ( aIndex.hasValue() )
+ return xVbaShapes->Item( aIndex, uno::Any() );
+ return uno::Any( xVbaShapes );
+}
+
+uno::Any
+ScVbaWorksheet::getButtons( const uno::Any &rIndex, bool bOptionButtons )
+{
+ ::rtl::Reference< ScVbaSheetObjectsBase > &rxButtons = bOptionButtons ? mxButtons[0] : mxButtons[1];
+
+ if( !rxButtons.is() )
+ rxButtons.set( new ScVbaButtons( this, mxContext, mxModel, mxSheet, bOptionButtons ) );
+ else
+ rxButtons->collectShapes();
+ if( rIndex.hasValue() )
+ return rxButtons->Item( rIndex, uno::Any() );
+ return uno::Any( uno::Reference< XCollection >( rxButtons ) );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Buttons( const uno::Any& rIndex )
+{
+ return getButtons( rIndex, false );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::CheckBoxes( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::DropDowns( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::GroupBoxes( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Labels( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::ListBoxes( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::OptionButtons( const uno::Any& rIndex )
+{
+ return getButtons( rIndex, true );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::ScrollBars( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Spinners( const uno::Any& /*rIndex*/ )
+{
+ throw uno::RuntimeException();
+}
+
+void SAL_CALL
+ScVbaWorksheet::ShowDataForm( )
+{
+ uno::Reference< frame::XModel > xModel( getModel(), uno::UNO_SET_THROW );
+ ScTabViewShell* pTabViewShell = excel::getBestViewShell( xModel );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScDataFormDlg> pDlg(pFact->CreateScDataFormDlg(pTabViewShell->GetFrameWeld(),
+ pTabViewShell));
+
+ pDlg->Execute();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::Evaluate( const OUString& Name )
+{
+ // #TODO Evaluate allows other things to be evaluated, e.g. functions
+ // I think ( like SIN(3) etc. ) need to investigate that
+ // named Ranges also? e.g. [MyRange] if so need a list of named ranges
+ uno::Any aVoid;
+ return uno::Any( Range( uno::Any( Name ), aVoid ) );
+}
+
+uno::Reference< beans::XIntrospectionAccess > SAL_CALL
+ScVbaWorksheet::getIntrospection( )
+{
+ return uno::Reference< beans::XIntrospectionAccess >();
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheet::invoke( const OUString& /*aFunctionName*/, const uno::Sequence< uno::Any >& /*aParams*/, uno::Sequence< ::sal_Int16 >& /*aOutParamIndex*/, uno::Sequence< uno::Any >& /*aOutParam*/ )
+{
+ throw uno::RuntimeException("Unsupported"); // unsupported operation
+}
+
+void SAL_CALL
+ScVbaWorksheet::setValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ setDefaultPropByIntrospection( getValue( aPropertyName ), aValue );
+}
+uno::Any SAL_CALL
+ScVbaWorksheet::getValue( const OUString& aPropertyName )
+{
+ uno::Reference< drawing::XControlShape > xControlShape( getControlShape( aPropertyName ), uno::UNO_QUERY_THROW );
+
+ uno::Reference<lang::XMultiComponentFactory > xServiceManager( mxContext->getServiceManager(), uno::UNO_SET_THROW );
+ uno::Reference< XControlProvider > xControlProvider( xServiceManager->createInstanceWithContext("ooo.vba.ControlProvider", mxContext ), uno::UNO_QUERY_THROW );
+ uno::Reference< msforms::XControl > xControl( xControlProvider->createControl( xControlShape, getModel() ) );
+ return uno::Any( xControl );
+}
+
+sal_Bool SAL_CALL
+ScVbaWorksheet::hasMethod( const OUString& /*aName*/ )
+{
+ return false;
+}
+
+uno::Reference< container::XNameAccess >
+ScVbaWorksheet::getFormControls() const
+{
+ uno::Reference< container::XNameAccess > xFormControls;
+ try
+ {
+ uno::Reference< sheet::XSpreadsheet > xSpreadsheet( getSheet(), uno::UNO_SET_THROW );
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( xSpreadsheet, uno::UNO_QUERY_THROW );
+ uno::Reference< form::XFormsSupplier > xFormSupplier( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xFormSupplier->getForms(), uno::UNO_QUERY_THROW );
+ // get the www-standard container ( maybe we should access the
+ // 'www-standard' by name rather than index, this seems an
+ // implementation detail
+ if( xIndexAccess->hasElements() )
+ xFormControls.set( xIndexAccess->getByIndex(0), uno::UNO_QUERY );
+
+ }
+ catch( uno::Exception& )
+ {
+ }
+ return xFormControls;
+
+ }
+sal_Bool SAL_CALL
+ScVbaWorksheet::hasProperty( const OUString& aName )
+{
+ uno::Reference< container::XNameAccess > xFormControls( getFormControls() );
+ if ( xFormControls.is() )
+ return xFormControls->hasByName( aName );
+ return false;
+}
+
+uno::Any
+ScVbaWorksheet::getControlShape( std::u16string_view sName )
+{
+ // ideally we would get an XControl object but it appears an XControl
+ // implementation only exists for a Control implementation obtained from the
+ // view ( e.g. in basic you would get this from
+ // thiscomponent.currentcontroller.getControl( controlModel ) )
+ // and the thing to realise is that it is only possible to get an XControl
+ // for a currently displayed control :-( often we would want to modify
+ // a control not on the active sheet. But... you can always access the
+ // XControlShape from the DrawPage whether that is the active drawpage or not
+
+ uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier( getSheet(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW );
+
+ sal_Int32 nCount = xIndexAccess->getCount();
+ for( int index = 0; index < nCount; index++ )
+ {
+ uno::Any aUnoObj = xIndexAccess->getByIndex( index );
+ // It seems there are some drawing objects that can not query into Control shapes?
+ uno::Reference< drawing::XControlShape > xControlShape( aUnoObj, uno::UNO_QUERY );
+ if( xControlShape.is() )
+ {
+ uno::Reference< container::XNamed > xNamed( xControlShape->getControl(), uno::UNO_QUERY_THROW );
+ if( sName == xNamed->getName() )
+ {
+ return aUnoObj;
+ }
+ }
+ }
+ return uno::Any();
+}
+
+OUString
+ScVbaWorksheet::getServiceImplName()
+{
+ return "ScVbaWorksheet";
+}
+
+void SAL_CALL
+ScVbaWorksheet::setEnableCalculation( sal_Bool bEnableCalculation )
+{
+ uno::Reference <sheet::XCalculatable> xCalculatable(getModel(), uno::UNO_QUERY_THROW);
+ xCalculatable->enableAutomaticCalculation( bEnableCalculation);
+}
+sal_Bool SAL_CALL
+ScVbaWorksheet::getEnableCalculation( )
+{
+ uno::Reference <sheet::XCalculatable> xCalculatable(getModel(), uno::UNO_QUERY_THROW);
+ return xCalculatable->isAutomaticCalculationEnabled();
+}
+
+uno::Sequence< OUString >
+ScVbaWorksheet::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.Worksheet"
+ };
+ return aServiceNames;
+}
+
+OUString SAL_CALL
+ScVbaWorksheet::getCodeName()
+{
+ uno::Reference< beans::XPropertySet > xSheetProp( mxSheet, uno::UNO_QUERY_THROW );
+ return xSheetProp->getPropertyValue("CodeName").get< OUString >();
+}
+
+sal_Int16
+ScVbaWorksheet::getSheetID() const
+{
+ uno::Reference< sheet::XCellRangeAddressable > xAddressable( mxSheet, uno::UNO_QUERY_THROW ); // if ActiveSheet, mxSheet is null.
+ return xAddressable->getRangeAddress().Sheet;
+}
+
+void SAL_CALL
+ScVbaWorksheet::PrintOut( const uno::Any& From, const uno::Any& To, const uno::Any& Copies, const uno::Any& Preview, const uno::Any& ActivePrinter, const uno::Any& PrintToFile, const uno::Any& Collate, const uno::Any& PrToFileName, const uno::Any& )
+{
+ sal_Int32 nTo = 0;
+ sal_Int32 nFrom = 0;
+ bool bSelection = false;
+ From >>= nFrom;
+ To >>= nTo;
+
+ if ( !( nFrom || nTo ) )
+ bSelection = true;
+
+ uno::Reference< frame::XModel > xModel( getModel(), uno::UNO_SET_THROW );
+ PrintOutHelper( excel::getBestViewShell( xModel ), From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName, bSelection );
+}
+
+void SAL_CALL
+ScVbaWorksheet::ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& /*IgnorePrintAreas*/, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& /*FixedFormatExtClassPtr*/)
+{
+ uno::Reference< frame::XModel > xModel(getModel(), uno::UNO_SET_THROW);
+ uno::Reference< excel::XApplication > xApplication(Application(), uno::UNO_QUERY_THROW);
+
+ excel::ExportAsFixedFormatHelper(xModel, xApplication, Type, FileName, Quality,
+ IncludeDocProperties, From, To, OpenAfterPublish);
+}
+
+sal_Int64 SAL_CALL
+ScVbaWorksheet::getSomething(const uno::Sequence<sal_Int8 > & rId)
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_ScVbaWorksheet_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new ScVbaWorksheet(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworksheet.hxx b/sc/source/ui/vba/vbaworksheet.hxx
new file mode 100644
index 0000000000..7ac05220a2
--- /dev/null
+++ b/sc/source/ui/vba/vbaworksheet.hxx
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XWorksheet.hpp>
+#include <rtl/ref.hxx>
+
+#include <vbahelper/vbahelperinterface.hxx>
+#include <types.hxx>
+
+namespace com::sun::star::frame { class XModel; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::uno { class XComponentContext; }
+namespace ooo::vba::excel { class XOutline; }
+namespace ooo::vba::excel { class XPageSetup; }
+namespace ooo::vba::excel { class XRange; }
+
+namespace ooo::vba::excel {
+ class XChartObjects;
+ class XHyperlinks;
+}
+
+class ScVbaSheetObjectsBase;
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XWorksheet > WorksheetImpl_BASE;
+
+class ScVbaWorksheet : public WorksheetImpl_BASE
+{
+ css::uno::Reference< css::sheet::XSpreadsheet > mxSheet;
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< ov::excel::XChartObjects > mxCharts;
+ css::uno::Reference< ov::excel::XHyperlinks > mxHlinks;
+ ::rtl::Reference< ScVbaSheetObjectsBase > mxButtons[2];
+ bool mbVeryHidden;
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XWorksheet > getSheetAtOffset(SCTAB offset);
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< ov::excel::XRange > getSheetRange();
+
+ css::uno::Reference< css::container::XNameAccess > getFormControls() const;
+ css::uno::Any getControlShape( std::u16string_view sName );
+
+ css::uno::Any getButtons( const css::uno::Any &rIndex, bool bOptionButtons );
+
+public:
+ /// @throws css::uno::RuntimeException
+ ScVbaWorksheet( const css::uno::Reference< ov::XHelperInterface >& xParent,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ css::uno::Reference< css::sheet::XSpreadsheet > xSheet,
+ css::uno::Reference< css::frame::XModel > xModel ) ;
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ ScVbaWorksheet( css::uno::Sequence< css::uno::Any > const& aArgs, css::uno::Reference< css::uno::XComponentContext >const& xContext );
+
+ virtual ~ScVbaWorksheet() override;
+
+ const css::uno::Reference< css::frame::XModel >& getModel() const
+ { return mxModel; }
+ const css::uno::Reference< css::sheet::XSpreadsheet >& getSheet() const
+ { return mxSheet; }
+ static const css::uno::Sequence<sal_Int8>& getUnoTunnelId();
+ css::uno::Reference< ov::excel::XWorksheet > createSheetCopyInNewDoc( const OUString& );
+ css::uno::Reference< ov::excel::XWorksheet > createSheetCopy(css::uno::Reference< ov::excel::XWorksheet> const & xSheet, bool bAfter);
+
+ // Attributes
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString &rName ) override;
+ virtual sal_Int32 SAL_CALL getVisible() override;
+ virtual void SAL_CALL setVisible( sal_Int32 nVisible ) override;
+ virtual ::sal_Int32 SAL_CALL getStandardWidth() override;
+ virtual ::sal_Int32 SAL_CALL getStandardHeight() override;
+ virtual sal_Bool SAL_CALL getProtectionMode() override;
+ virtual sal_Bool SAL_CALL getProtectContents() override;
+ virtual sal_Bool SAL_CALL getProtectDrawingObjects() override;
+ virtual sal_Bool SAL_CALL getProtectScenarios() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL getUsedRange() override ;
+ virtual css::uno::Any SAL_CALL ChartObjects( const css::uno::Any& Index ) override;
+ virtual css::uno::Reference< ov::excel::XOutline > SAL_CALL Outline( ) override;
+ virtual css::uno::Reference< ov::excel::XPageSetup > SAL_CALL PageSetup( ) override;
+ virtual css::uno::Any SAL_CALL HPageBreaks( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL VPageBreaks( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL getNext() override;
+ virtual css::uno::Reference< ov::excel::XWorksheet > SAL_CALL getPrevious() override;
+ virtual sal_Int16 SAL_CALL getIndex() override;
+ virtual sal_Int32 SAL_CALL getEnableSelection() override;
+ virtual void SAL_CALL setEnableSelection( sal_Int32 nSelection ) override;
+ virtual sal_Bool SAL_CALL getAutoFilterMode() override;
+ virtual void SAL_CALL setAutoFilterMode( sal_Bool bAutoFilterMode ) override;
+
+ // Methods
+ virtual void SAL_CALL Activate() override;
+ virtual void SAL_CALL Select() override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Range( const css::uno::Any& Cell1, const css::uno::Any& Cell2 ) override;
+ virtual void SAL_CALL Move( const css::uno::Any& Before, const css::uno::Any& After ) override ;
+ virtual void SAL_CALL Copy( const css::uno::Any& Before, const css::uno::Any& After ) override;
+ virtual void SAL_CALL Paste( const css::uno::Any& Destination, const css::uno::Any& Link ) override;
+ virtual void SAL_CALL Delete( ) override;
+ virtual void SAL_CALL Protect( const css::uno::Any& Password, const css::uno::Any& DrawingObjects, const css::uno::Any& Contents, const css::uno::Any& Scenarios, const css::uno::Any& UserInterfaceOnly ) override;
+ virtual void SAL_CALL Unprotect( const css::uno::Any& Password ) override;
+
+ virtual void SAL_CALL Calculate( ) override;
+ virtual void SAL_CALL CheckSpelling( const css::uno::Any& CustomDictionary,const css::uno::Any& IgnoreUppercase,const css::uno::Any& AlwaysSuggest, const css::uno::Any& SpellingLang ) override;
+ // Hacks (?)
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Cells( const css::uno::Any &nRow, const css::uno::Any &nCol ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Rows(const css::uno::Any& aIndex ) override;
+ virtual css::uno::Reference< ov::excel::XRange > SAL_CALL Columns(const css::uno::Any& aIndex ) override;
+
+ virtual css::uno::Any SAL_CALL Evaluate( const OUString& Name ) override;
+ virtual css::uno::Any SAL_CALL PivotTables( const css::uno::Any& Index ) override;
+ virtual css::uno::Any SAL_CALL Comments( const css::uno::Any& Index ) override;
+ virtual css::uno::Any SAL_CALL Hyperlinks( const css::uno::Any& aIndex ) override;
+ virtual css::uno::Any SAL_CALL Names( const css::uno::Any& aIndex ) override;
+
+ virtual css::uno::Any SAL_CALL OLEObjects( const css::uno::Any& Index ) override;
+ virtual css::uno::Any SAL_CALL Shapes( const css::uno::Any& aIndex ) override;
+
+ virtual css::uno::Any SAL_CALL Buttons( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL CheckBoxes( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL DropDowns( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL GroupBoxes( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL Labels( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL ListBoxes( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL OptionButtons( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL ScrollBars( const css::uno::Any& rIndex ) override;
+ virtual css::uno::Any SAL_CALL Spinners( const css::uno::Any& rIndex ) override;
+
+ virtual void SAL_CALL setEnableCalculation( sal_Bool EnableCalculation ) override;
+ virtual sal_Bool SAL_CALL getEnableCalculation( ) override;
+ virtual void SAL_CALL ShowDataForm( ) override;
+ // XInvocation
+ virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection( ) override;
+ virtual css::uno::Any SAL_CALL invoke( const OUString& aFunctionName, const css::uno::Sequence< css::uno::Any >& aParams, css::uno::Sequence< ::sal_Int16 >& aOutParamIndex, css::uno::Sequence< css::uno::Any >& aOutParam ) override;
+ virtual void SAL_CALL setValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getValue( const OUString& aPropertyName ) override;
+ virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override;
+ // CodeName
+ virtual OUString SAL_CALL getCodeName() override;
+ /// @throws css::uno::RuntimeException
+ sal_Int16 getSheetID() const;
+
+ virtual void SAL_CALL PrintOut( const css::uno::Any& From, const css::uno::Any& To, const css::uno::Any& Copies, const css::uno::Any& Preview, const css::uno::Any& ActivePrinter, const css::uno::Any& PrintToFile, const css::uno::Any& Collate, const css::uno::Any& PrToFileName, const css::uno::Any& IgnorePrintAreas ) override;
+ virtual void SAL_CALL ExportAsFixedFormat(const css::uno::Any& Type, const css::uno::Any& FileName, const css::uno::Any& Quality,
+ const css::uno::Any& IncludeDocProperties, const css::uno::Any& IgnorePrintAreas, const css::uno::Any& From,
+ const css::uno::Any& To, const css::uno::Any& OpenAfterPublish, const css::uno::Any& FixedFormatExtClassPtr) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+ // XUnoTunnel
+ virtual ::sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8 >& rId ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworksheets.cxx b/sc/source/ui/vba/vbaworksheets.cxx
new file mode 100644
index 0000000000..bd552fbb03
--- /dev/null
+++ b/sc/source/ui/vba/vbaworksheets.cxx
@@ -0,0 +1,536 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "vbaworksheets.hxx"
+
+#include <sfx2/viewfrm.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+
+#include <ooo/vba/excel/XApplication.hpp>
+#include <tabvwsh.hxx>
+
+#include "excelvbahelper.hxx"
+#include "vbaworksheet.hxx"
+#include <markdata.hxx>
+
+#include <utility>
+#include <vector>
+#include <prevwsh.hxx>
+#include <preview.hxx>
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+// a map ( or hashmap ) won't do as we need also to preserve the order
+// (as added ) of the items
+typedef std::vector< uno::Reference< sheet::XSpreadsheet > > SheetMap;
+
+// #FIXME #TODO the implementation of the Sheets collections sucks,
+// e.g. there is no support for tracking sheets added/removed from the collection
+
+namespace {
+
+class WorkSheetsEnumeration : public ::cppu::WeakImplHelper< container::XEnumeration >
+{
+ SheetMap mSheetMap;
+ SheetMap::iterator mIt;
+public:
+ explicit WorkSheetsEnumeration( SheetMap&& sMap ) : mSheetMap( std::move(sMap) ), mIt( mSheetMap.begin() ) {}
+ virtual sal_Bool SAL_CALL hasMoreElements( ) override
+ {
+ return ( mIt != mSheetMap.end() );
+ }
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ if ( !hasMoreElements() )
+ throw container::NoSuchElementException();
+ uno::Reference< sheet::XSpreadsheet > xSheet( *mIt++ );
+ return uno::Any( xSheet ) ;
+ }
+};
+
+class SheetCollectionHelper : public ::cppu::WeakImplHelper< container::XNameAccess,
+ container::XIndexAccess,
+ container::XEnumerationAccess >
+{
+ SheetMap mSheetMap;
+ SheetMap::iterator cachePos;
+public:
+ explicit SheetCollectionHelper( SheetMap&& sMap ) : mSheetMap( std::move(sMap) ), cachePos(mSheetMap.begin()) {}
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<sheet::XSpreadsheet>::get(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override { return ( !mSheetMap.empty() ); }
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ if ( !hasByName(aName) )
+ throw container::NoSuchElementException();
+ return uno::Any( *cachePos );
+ }
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ uno::Sequence< OUString > sNames( mSheetMap.size() );
+ OUString* pString = sNames.getArray();
+
+ for ( const auto& rItem : mSheetMap )
+ {
+ uno::Reference< container::XNamed > xName( rItem, uno::UNO_QUERY_THROW );
+ *pString = xName->getName();
+ ++pString;
+ }
+ return sNames;
+ }
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ cachePos = mSheetMap.begin();
+ SheetMap::iterator it_end = mSheetMap.end();
+ for ( ; cachePos != it_end; ++cachePos )
+ {
+ uno::Reference< container::XNamed > xName( *cachePos, uno::UNO_QUERY_THROW );
+ if ( aName == xName->getName() )
+ break;
+ }
+ return ( cachePos != it_end );
+ }
+
+ // XElementAccess
+ virtual ::sal_Int32 SAL_CALL getCount( ) override { return mSheetMap.size(); }
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ if ( Index < 0 || Index >= getCount() )
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any( mSheetMap[ Index ] );
+
+ }
+ // XEnumerationAccess
+ virtual uno::Reference< container::XEnumeration > SAL_CALL createEnumeration( ) override
+ {
+ return new WorkSheetsEnumeration( std::vector(mSheetMap) );
+ }
+};
+
+class SheetsEnumeration : public EnumerationHelperImpl
+{
+ uno::Reference< frame::XModel > m_xModel;
+public:
+ /// @throws uno::RuntimeException
+ SheetsEnumeration( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, uno::Reference< frame::XModel > xModel ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), m_xModel(std::move( xModel )) {}
+
+ virtual uno::Any SAL_CALL nextElement( ) override
+ {
+ uno::Reference< sheet::XSpreadsheet > xSheet( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
+ uno::Reference< XHelperInterface > xIf = excel::getUnoSheetModuleObj( xSheet );
+ uno::Any aRet;
+ if ( !xIf.is() )
+ {
+ // if the Sheet is in a document created by the api unfortunately ( at the
+ // moment, it actually won't have the special Document modules
+ uno::Reference< excel::XWorksheet > xNewSheet( new ScVbaWorksheet( m_xParent, m_xContext, xSheet, m_xModel ) );
+ aRet <<= xNewSheet;
+ }
+ else
+ aRet <<= xIf;
+ return aRet;
+ }
+
+};
+
+}
+
+ScVbaWorksheets::ScVbaWorksheets( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< container::XIndexAccess >& xSheets, uno::Reference< frame::XModel > xModel ): ScVbaWorksheets_BASE( xParent, xContext, xSheets ), mxModel(std::move( xModel )), m_xSheets( uno::Reference< sheet::XSpreadsheets >( xSheets, uno::UNO_QUERY ) )
+{
+}
+
+ScVbaWorksheets::ScVbaWorksheets( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext > & xContext, const uno::Reference< container::XEnumerationAccess >& xEnumAccess, uno::Reference< frame::XModel > xModel ): ScVbaWorksheets_BASE( xParent, xContext, uno::Reference< container::XIndexAccess >( xEnumAccess, uno::UNO_QUERY ) ), mxModel(std::move(xModel))
+{
+}
+
+// XEnumerationAccess
+uno::Type
+ScVbaWorksheets::getElementType()
+{
+ return cppu::UnoType<excel::XWorksheet>::get();
+}
+
+uno::Reference< container::XEnumeration >
+ScVbaWorksheets::createEnumeration()
+{
+ if ( !m_xSheets.is() )
+ {
+ uno::Reference< container::XEnumerationAccess > xAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
+ return xAccess->createEnumeration();
+ }
+ uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xSheets, uno::UNO_QUERY_THROW );
+ return new SheetsEnumeration( this, mxContext, xEnumAccess->createEnumeration(), mxModel );
+}
+
+uno::Any
+ScVbaWorksheets::createCollectionObject( const uno::Any& aSource )
+{
+ uno::Reference< sheet::XSpreadsheet > xSheet( aSource, uno::UNO_QUERY );
+ uno::Reference< XHelperInterface > xIf = excel::getUnoSheetModuleObj( xSheet );
+ uno::Any aRet;
+ if ( !xIf.is() )
+ {
+ // if the Sheet is in a document created by the api unfortunately ( at the
+ // moment, it actually won't have the special Document modules
+ uno::Reference< excel::XWorksheet > xNewSheet( new ScVbaWorksheet( getParent(), mxContext, xSheet, mxModel ) );
+ aRet <<= xNewSheet;
+ }
+ else
+ aRet <<= xIf;
+ return aRet;
+}
+
+// XWorksheets
+uno::Any
+ScVbaWorksheets::Add( const uno::Any& Before, const uno::Any& After,
+ const uno::Any& Count, const uno::Any& Type )
+{
+ if ( isSelectedSheets() )
+ return uno::Any(); // or should we throw?
+
+ OUString aStringSheet;
+ bool bBefore(true);
+ SCTAB nSheetIndex = 0;
+ SCTAB nNewSheets = 1, nType = 0;
+ Count >>= nNewSheets;
+ Type >>= nType;
+ SCTAB nCount = 0;
+
+ uno::Reference< excel::XWorksheet > xBeforeAfterSheet;
+
+ if ( Before.hasValue() )
+ {
+ if ( Before >>= xBeforeAfterSheet )
+ aStringSheet = xBeforeAfterSheet->getName();
+ else
+ Before >>= aStringSheet;
+ }
+
+ if (aStringSheet.isEmpty() && After.hasValue() )
+ {
+ if ( After >>= xBeforeAfterSheet )
+ aStringSheet = xBeforeAfterSheet->getName();
+ else
+ After >>= aStringSheet;
+ bBefore = false;
+ }
+ if (aStringSheet.isEmpty())
+ {
+ uno::Reference< excel::XApplication > xApplication( Application(), uno::UNO_QUERY_THROW );
+ aStringSheet = xApplication->getActiveWorkbook()->getActiveSheet()->getName();
+ bBefore = true;
+ }
+ nCount = static_cast< SCTAB >( m_xIndexAccess->getCount() );
+ for (SCTAB i=0; i < nCount; i++)
+ {
+ uno::Reference< sheet::XSpreadsheet > xSheet(m_xIndexAccess->getByIndex(i), uno::UNO_QUERY);
+ uno::Reference< container::XNamed > xNamed( xSheet, uno::UNO_QUERY_THROW );
+ if (xNamed->getName() == aStringSheet)
+ {
+ nSheetIndex = i;
+ break;
+ }
+ }
+
+ if(!bBefore)
+ nSheetIndex++;
+
+ SCTAB nSheetName = nCount + 1;
+ OUString aStringBase( "Sheet" );
+ uno::Any result;
+ for (SCTAB i=0; i < nNewSheets; i++, nSheetName++)
+ {
+ OUString aStringName = aStringBase + OUString::number(nSheetName);
+ while (m_xNameAccess->hasByName(aStringName))
+ {
+ nSheetName++;
+ aStringName = aStringBase + OUString::number(nSheetName);
+ }
+ m_xSheets->insertNewByName(aStringName, nSheetIndex + i);
+ result = getItemByStringIndex( aStringName );
+ }
+ uno::Reference< excel::XWorksheet > xNewSheet( result, uno::UNO_QUERY );
+ if ( xNewSheet.is() )
+ xNewSheet->Activate();
+ return result;
+}
+
+void
+ScVbaWorksheets::Delete()
+{
+ // #TODO #INVESTIGATE
+ // mmm this method could be trouble if the underlying
+ // uno objects ( the m_xIndexAccess etc ) aren't aware of the
+ // contents that are deleted
+ sal_Int32 nElems = getCount();
+ for ( sal_Int32 nItem = 1; nItem <= nElems; ++nItem )
+ {
+ uno::Reference< excel::XWorksheet > xSheet( Item( uno::Any( nItem ), uno::Any() ), uno::UNO_QUERY_THROW );
+ xSheet->Delete();
+ }
+}
+
+bool
+ScVbaWorksheets::isSelectedSheets() const
+{
+ return !m_xSheets.is();
+}
+
+void SAL_CALL
+ScVbaWorksheets::PrintOut( const uno::Any& From, const uno::Any& To, const uno::Any& Copies, const uno::Any& Preview, const uno::Any& ActivePrinter, const uno::Any& PrintToFile, const uno::Any& Collate, const uno::Any& PrToFileName )
+{
+ sal_Int32 nTo = 0;
+ sal_Int32 nFrom = 0;
+ bool bSelection = false;
+ From >>= nFrom;
+ To >>= nTo;
+
+ if ( !( nFrom || nTo ) )
+ if ( isSelectedSheets() )
+ bSelection = true;
+
+ PrintOutHelper( excel::getBestViewShell( mxModel ), From, To, Copies, Preview, ActivePrinter, PrintToFile, Collate, PrToFileName, bSelection );
+}
+
+uno::Any SAL_CALL
+ScVbaWorksheets::getVisible()
+{
+ bool bVisible = true;
+ uno::Reference< container::XEnumeration > xEnum( createEnumeration(), uno::UNO_SET_THROW );
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Reference< excel::XWorksheet > xSheet( xEnum->nextElement(), uno::UNO_QUERY_THROW );
+ if ( xSheet->getVisible() == 0 )
+ {
+ bVisible = false;
+ break;
+ }
+ }
+ return uno::Any( bVisible );
+}
+
+void SAL_CALL
+ScVbaWorksheets::setVisible( const uno::Any& _visible )
+{
+ bool bState = false;
+ if ( !(_visible >>= bState) )
+ throw uno::RuntimeException("Visible property doesn't support non boolean #FIXME" );
+
+ uno::Reference< container::XEnumeration > xEnum( createEnumeration(), uno::UNO_SET_THROW );
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Reference< excel::XWorksheet > xSheet( xEnum->nextElement(), uno::UNO_QUERY_THROW );
+ xSheet->setVisible( bState ? 1 : 0 );
+ }
+
+}
+
+void SAL_CALL
+ScVbaWorksheets::Select( const uno::Any& Replace )
+{
+ ScTabViewShell* pViewShell = excel::getBestViewShell( mxModel );
+ if ( !pViewShell )
+ throw uno::RuntimeException("Cannot obtain view shell" );
+
+ ScMarkData& rMarkData = pViewShell->GetViewData().GetMarkData();
+ bool bReplace = true;
+ Replace >>= bReplace;
+ // Replace is defaulted to True, meaning this current collection
+ // becomes the Selection, if it were false then the current selection would
+ // be extended
+ bool bSelectSingle = bReplace;
+ sal_Int32 nElems = getCount();
+ for ( sal_Int32 nItem = 1; nItem <= nElems; ++nItem )
+ {
+ uno::Reference< excel::XWorksheet > xSheet( Item( uno::Any( nItem ), uno::Any() ), uno::UNO_QUERY_THROW );
+ ScVbaWorksheet* pSheet = excel::getImplFromDocModuleWrapper<ScVbaWorksheet>( xSheet );
+ if ( bSelectSingle )
+ {
+ rMarkData.SelectOneTable( static_cast< SCTAB >( pSheet->getSheetID() ) );
+ bSelectSingle = false;
+ }
+ else
+ rMarkData.SelectTable( static_cast< SCTAB >( pSheet->getSheetID() ), true );
+ }
+
+}
+
+void SAL_CALL
+ScVbaWorksheets::Copy ( const uno::Any& Before, const uno::Any& After)
+{
+ uno::Reference<excel::XWorksheet> xSheet;
+ sal_Int32 nElems = getCount();
+ bool bAfter = After.hasValue();
+ std::vector< uno::Reference< excel::XWorksheet > > Sheets;
+ sal_Int32 nItem = 0;
+
+ for ( nItem = 1; nItem <= nElems; ++nItem)
+ {
+ uno::Reference<excel::XWorksheet> xWorksheet(Item( uno::Any( nItem ), uno::Any() ), uno::UNO_QUERY_THROW );
+ Sheets.push_back(xWorksheet);
+ }
+ bool bNewDoc = (!(Before >>= xSheet) && !(After >>=xSheet)&& !(Before.hasValue()) && !(After.hasValue()));
+
+ uno::Reference< excel::XWorksheet > xSrcSheet;
+ if ( bNewDoc )
+ {
+ bAfter = true;
+ xSrcSheet = Sheets.at(0);
+ ScVbaWorksheet* pSrcSheet = excel::getImplFromDocModuleWrapper<ScVbaWorksheet>( xSrcSheet );
+ xSheet = pSrcSheet->createSheetCopyInNewDoc(xSrcSheet->getName());
+ nItem = 1;
+ }
+ else
+ {
+ nItem=0;
+ }
+
+ for (; nItem < nElems; ++nItem )
+ {
+ xSrcSheet = Sheets[nItem];
+ ScVbaWorksheet* pSrcSheet = excel::getImplFromDocModuleWrapper<ScVbaWorksheet>( xSrcSheet );
+ if ( bAfter )
+ xSheet = pSrcSheet->createSheetCopy(xSheet, bAfter);
+ else
+ pSrcSheet->createSheetCopy(xSheet, bAfter);
+ }
+}
+
+//ScVbaCollectionBaseImpl
+uno::Any SAL_CALL
+ScVbaWorksheets::Item(const uno::Any& Index, const uno::Any& Index2)
+{
+ if ( Index.getValueTypeClass() == uno::TypeClass_SEQUENCE )
+ {
+ const uno::Reference< script::XTypeConverter >& xConverter = getTypeConverter(mxContext);
+ uno::Any aConverted = xConverter->convertTo( Index, cppu::UnoType<uno::Sequence< uno::Any >>::get() );
+ SheetMap aSheets;
+ uno::Sequence< uno::Any > sIndices;
+ aConverted >>= sIndices;
+ for( const auto& rIndex : std::as_const(sIndices) )
+ {
+ uno::Reference< excel::XWorksheet > xWorkSheet( ScVbaWorksheets_BASE::Item( rIndex, Index2 ), uno::UNO_QUERY_THROW );
+ ScVbaWorksheet* pWorkSheet = excel::getImplFromDocModuleWrapper<ScVbaWorksheet>( xWorkSheet );
+ uno::Reference< sheet::XSpreadsheet > xSheet( pWorkSheet->getSheet() , uno::UNO_SET_THROW );
+ uno::Reference< container::XNamed > xName( xSheet, uno::UNO_QUERY_THROW );
+ aSheets.push_back( xSheet );
+ }
+ uno::Reference< container::XIndexAccess > xIndexAccess = new SheetCollectionHelper( std::move(aSheets) );
+ uno::Reference< XCollection > xSelectedSheets( new ScVbaWorksheets( getParent(), mxContext, xIndexAccess, mxModel ) );
+ return uno::Any( xSelectedSheets );
+ }
+ return ScVbaWorksheets_BASE::Item( Index, Index2 );
+}
+
+OUString
+ScVbaWorksheets::getServiceImplName()
+{
+ return "ScVbaWorksheets";
+}
+
+css::uno::Sequence<OUString>
+ScVbaWorksheets::getServiceNames()
+{
+ static uno::Sequence< OUString > const sNames
+ {
+ "ooo.vba.excel.Worksheets"
+ };
+ return sNames;
+}
+
+bool ScVbaWorksheets::nameExists( const uno::Reference <sheet::XSpreadsheetDocument>& xSpreadDoc, std::u16string_view name, SCTAB& nTab )
+{
+ if (!xSpreadDoc.is())
+ throw lang::IllegalArgumentException( "nameExists() xSpreadDoc is null", uno::Reference< uno::XInterface >(), 1 );
+ uno::Reference <container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ SCTAB nCount = static_cast< SCTAB >( xIndex->getCount() );
+ for (SCTAB i=0; i < nCount; i++)
+ {
+ uno::Reference< container::XNamed > xNamed( xIndex->getByIndex(i), uno::UNO_QUERY_THROW );
+ if (xNamed->getName() == name)
+ {
+ nTab = i;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void ScVbaWorksheets::PrintPreview( const css::uno::Any& /*EnableChanges*/ )
+{
+ // need test, print preview current active sheet
+ // !! TODO !! get view shell from controller
+ ScTabViewShell* pViewShell = excel::getBestViewShell( mxModel );
+ SfxViewFrame* pViewFrame = nullptr;
+ if ( pViewShell )
+ pViewFrame = &pViewShell->GetViewFrame();
+ if ( !pViewFrame )
+ return;
+
+ if ( pViewFrame->GetFrame().IsInPlace() )
+ return;
+
+ dispatchExecute( pViewShell, SID_VIEWSHELL1 );
+ SfxViewShell* pShell = SfxViewShell::Get( pViewFrame->GetFrame().GetFrameInterface()->getController() );
+
+ ScPreviewShell* pPrvShell = dynamic_cast< ScPreviewShell* >( pShell );
+ if ( !pPrvShell )
+ return;
+
+ ScPreview* pPrvView = pPrvShell->GetPreview();
+ const ScDocument& rDoc = pViewShell->GetViewData().GetDocument();
+ ScMarkData aMarkData(rDoc.GetSheetLimits());
+ sal_Int32 nElems = getCount();
+ for ( sal_Int32 nItem = 1; nItem <= nElems; ++nItem )
+ {
+ uno::Reference< excel::XWorksheet > xSheet( Item( uno::Any( nItem ), uno::Any() ), uno::UNO_QUERY_THROW );
+ ScVbaWorksheet* pSheet = excel::getImplFromDocModuleWrapper<ScVbaWorksheet>( xSheet );
+ if ( pSheet )
+ aMarkData.SelectTable(static_cast< SCTAB >( pSheet->getSheetID() ), true );
+ }
+ // save old selection, setting the selectedtabs in the preview
+ // can affect the current selection when preview has been
+ // closed
+ ScMarkData::MarkedTabsType aOldTabs = pPrvView->GetSelectedTabs();
+ pPrvView->SetSelectedTabs( aMarkData );
+ // force update
+ pPrvView->DataChanged(false);
+ // set sensible first page
+ tools::Long nPage = pPrvView->GetFirstPage( 1 );
+ pPrvView->SetPageNo( nPage );
+ WaitUntilPreviewIsClosed( pViewFrame );
+ // restore old tab selection
+ pViewShell = excel::getBestViewShell( mxModel );
+ pViewShell->GetViewData().GetMarkData().SetSelectedTabs(aOldTabs);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbaworksheets.hxx b/sc/source/ui/vba/vbaworksheets.hxx
new file mode 100644
index 0000000000..611365edfc
--- /dev/null
+++ b/sc/source/ui/vba/vbaworksheets.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XWorksheets.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+#include <types.hxx>
+
+namespace com::sun::star::container { class XEnumerationAccess; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+namespace com::sun::star::sheet { class XSpreadsheets; }
+namespace com::sun::star::uno { class XComponentContext; }
+
+typedef CollTestImplHelper< ov::excel::XWorksheets > ScVbaWorksheets_BASE;
+
+class ScVbaWorksheets : public ScVbaWorksheets_BASE
+{
+ css::uno::Reference< css::frame::XModel > mxModel;
+ css::uno::Reference< css::sheet::XSpreadsheets > m_xSheets;
+public:
+ ScVbaWorksheets( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::container::XIndexAccess >& xSheets, css::uno::Reference< css::frame::XModel > xModel );
+ ScVbaWorksheets( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext > & xContext, const css::uno::Reference< css::container::XEnumerationAccess >& xEnum, css::uno::Reference< css::frame::XModel > xModel );
+
+ bool isSelectedSheets() const;
+
+ // XEnumerationAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override;
+
+ // XWorksheets
+ virtual css::uno::Any SAL_CALL getVisible() override;
+ virtual void SAL_CALL setVisible( const css::uno::Any& _visible ) override;
+ virtual css::uno::Any SAL_CALL Add( const css::uno::Any& Before, const css::uno::Any& After, const css::uno::Any& Count, const css::uno::Any& Type ) override;
+ virtual void SAL_CALL Delete( ) override;
+ virtual void SAL_CALL PrintOut( const css::uno::Any& From, const css::uno::Any& To, const css::uno::Any& Copies, const css::uno::Any& Preview, const css::uno::Any& ActivePrinter, const css::uno::Any& PrintToFile, const css::uno::Any& Collate, const css::uno::Any& PrToFileName ) override;
+ virtual css::uno::Any createCollectionObject( const css::uno::Any& aSource ) override;
+ virtual void SAL_CALL Select( const css::uno::Any& Replace ) override;
+ virtual void SAL_CALL Copy ( const css::uno::Any& Before, const css::uno::Any& After) override;
+ virtual void SAL_CALL PrintPreview( const css::uno::Any& EnableChanges ) override;
+ // ScVbaWorksheets_BASE
+ virtual css::uno::Any SAL_CALL Item( const css::uno::Any& Index1, const css::uno::Any& Index2 ) override;
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ static bool nameExists( const css::uno::Reference <css::sheet::XSpreadsheetDocument>& xSpreadDoc, std::u16string_view name, SCTAB& nTab );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbawsfunction.cxx b/sc/source/ui/vba/vbawsfunction.cxx
new file mode 100644
index 0000000000..6af1732232
--- /dev/null
+++ b/sc/source/ui/vba/vbawsfunction.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/sheet/XFunctionAccess.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <ooo/vba/excel/XRange.hpp>
+
+#include "vbawsfunction.hxx"
+#include <compiler.hxx>
+
+using namespace com::sun::star;
+using namespace ooo::vba;
+
+namespace {
+
+void lclConvertDoubleToBoolean( uno::Any& rAny )
+{
+ if( rAny.has< double >() )
+ {
+ double fValue = rAny.get< double >();
+ if( fValue == 0.0 )
+ rAny <<= false;
+ else if( fValue == 1.0 )
+ rAny <<= true;
+ // do nothing for other values or types
+ }
+}
+
+void lclConvertBooleanToDouble( uno::Any& rAny )
+{
+ bool bValue( false );
+ if ( rAny >>= bValue )
+ {
+ if ( bValue )
+ rAny <<= 1.0;
+ else
+ rAny <<= 0.0;
+ }
+}
+
+} // namespace
+
+ScVbaWSFunction::ScVbaWSFunction( const uno::Reference< XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext ) :
+ ScVbaWSFunction_BASE( xParent, xContext )
+{
+}
+
+uno::Reference< beans::XIntrospectionAccess >
+ScVbaWSFunction::getIntrospection()
+{
+ return uno::Reference<beans::XIntrospectionAccess>();
+}
+
+uno::Any SAL_CALL
+ScVbaWSFunction::invoke(const OUString& FunctionName, const uno::Sequence< uno::Any >& Params, uno::Sequence< sal_Int16 >& /*OutParamIndex*/, uno::Sequence< uno::Any >& /*OutParam*/)
+{
+ // create copy of parameters, replace Excel range objects with UNO range objects
+ uno::Sequence< uno::Any > aParamTemp( Params );
+ if( aParamTemp.hasElements() )
+ {
+ for( uno::Any & rArray : asNonConstRange(aParamTemp) )
+ {
+ switch( rArray.getValueType().getTypeClass() )
+ {
+ case uno::TypeClass_BOOLEAN:
+ lclConvertBooleanToDouble( rArray );
+ break;
+ case uno::TypeClass_INTERFACE:
+ {
+ uno::Reference< excel::XRange > myRange( rArray, uno::UNO_QUERY );
+ if( myRange.is() )
+ rArray = myRange->getCellRange();
+ }
+ break;
+ case uno::TypeClass_SEQUENCE:
+ {
+ // the sheet.FunctionAccess service doesn't deal with Sequences, only Sequences of Sequence
+ uno::Type aType = rArray.getValueType();
+ if ( aType.equals( cppu::UnoType<uno::Sequence<sal_Int16>>::get() ) )
+ {
+ uno::Sequence< uno::Sequence< sal_Int16 > > aTmp(1);
+ rArray >>= aTmp.getArray()[ 0 ];
+ rArray <<= aTmp;
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence<sal_Int32>>::get() ) )
+ {
+ uno::Sequence< uno::Sequence< sal_Int32 > > aTmp(1);
+ rArray >>= aTmp.getArray()[ 0 ];
+ rArray <<= aTmp;
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence<double>>::get() ) )
+ {
+ uno::Sequence< uno::Sequence< double > > aTmp(1);
+ rArray >>= aTmp.getArray()[ 0 ];
+ rArray <<= aTmp;
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence<OUString>>::get() ) )
+ {
+ uno::Sequence< uno::Sequence< OUString > > aTmp(1);
+ rArray >>= aTmp.getArray()[ 0 ];
+ rArray <<= aTmp;
+ }
+ else if ( aType.equals( cppu::UnoType<uno::Sequence<uno::Any>>::get() ) )
+ {
+ uno::Sequence< uno::Sequence<uno::Any > > aTmp(1);
+ rArray >>= aTmp.getArray()[ 0 ];
+ rArray <<= aTmp;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ uno::Any aRet;
+ bool bAsArray = true;
+
+ // special handing for some functions that don't work correctly in FunctionAccess
+ formula::FormulaCompiler aCompiler;
+ OpCode eOpCode = aCompiler.GetEnglishOpCode( FunctionName.toAsciiUpperCase() );
+ switch( eOpCode )
+ {
+ // ISLOGICAL does not work in array formula mode
+ case ocIsLogical:
+ {
+ if( aParamTemp.getLength() != 1 )
+ throw lang::IllegalArgumentException();
+ const uno::Any& rParam = aParamTemp[ 0 ];
+ if( rParam.has< bool >() )
+ {
+ aRet <<= true;
+ }
+ else if( rParam.has< uno::Reference< table::XCellRange > >() ) try
+ {
+ uno::Reference< sheet::XCellRangeAddressable > xRangeAddr( rParam, uno::UNO_QUERY_THROW );
+ table::CellRangeAddress aRangeAddr = xRangeAddr->getRangeAddress();
+ bAsArray = (aRangeAddr.StartColumn != aRangeAddr.EndColumn) || (aRangeAddr.StartRow != aRangeAddr.EndRow);
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ break;
+ default:;
+ }
+
+ if( !aRet.hasValue() )
+ {
+ uno::Reference< lang::XMultiComponentFactory > xSMgr( mxContext->getServiceManager(), uno::UNO_SET_THROW );
+ uno::Reference< sheet::XFunctionAccess > xFunctionAccess( xSMgr->createInstanceWithContext(
+ "com.sun.star.sheet.FunctionAccess", mxContext ),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xPropSet( xFunctionAccess, uno::UNO_QUERY_THROW );
+ xPropSet->setPropertyValue("IsArrayFunction", uno::Any( bAsArray ) );
+ aRet = xFunctionAccess->callFunction( FunctionName, aParamTemp );
+ }
+
+ /* Convert return value from double to Boolean for some functions that
+ return Booleans. */
+ typedef uno::Sequence< uno::Sequence< uno::Any > > AnySeqSeq;
+ if( (eOpCode == ocIsEmpty) || (eOpCode == ocIsString) || (eOpCode == ocIsNonString) || (eOpCode == ocIsLogical) ||
+ (eOpCode == ocIsRef) || (eOpCode == ocIsValue) || (eOpCode == ocIsFormula) || (eOpCode == ocIsNA) ||
+ (eOpCode == ocIsErr) || (eOpCode == ocIsError) || (eOpCode == ocIsEven) || (eOpCode == ocIsOdd) ||
+ (eOpCode == ocAnd) || (eOpCode == ocOr) || (eOpCode == ocXor) || (eOpCode == ocNot) || (eOpCode == ocTrue) || (eOpCode == ocFalse) )
+ {
+ if( aRet.has< AnySeqSeq >() )
+ {
+ AnySeqSeq aAnySeqSeq = aRet.get< AnySeqSeq >();
+ for( auto& rAnySeq : asNonConstRange(aAnySeqSeq) )
+ for( auto& rAny : asNonConstRange(rAnySeq) )
+ lclConvertDoubleToBoolean( rAny );
+ aRet <<= aAnySeqSeq;
+ }
+ else
+ {
+ lclConvertDoubleToBoolean( aRet );
+ }
+ }
+
+ /* Hack/workaround (?): shorten single-row matrix to simple array, shorten
+ 1x1 matrix to single value. */
+ if( aRet.has< AnySeqSeq >() )
+ {
+ AnySeqSeq aAnySeqSeq = aRet.get< AnySeqSeq >();
+ if( aAnySeqSeq.getLength() == 1 )
+ {
+ if( aAnySeqSeq[ 0 ].getLength() == 1 )
+ aRet = aAnySeqSeq[ 0 ][ 0 ];
+ else
+ aRet <<= aAnySeqSeq[ 0 ];
+ }
+ }
+
+#if 0
+ // MATCH function should alwayse return a double value, but currently if the first argument is XCellRange, MATCH function returns an array instead of a double value. Don't know why?
+ // To fix this issue in safe, current solution is to convert this array to a double value just for MATCH function.
+ OUString aUpper( FunctionName.toAsciiUpperCase() );
+ ScCompiler aCompiler( NULL, ScAddress() );
+ OpCode eOp = aCompiler.GetEnglishOpCode( aUpper );
+ if( eOp == ocMatch )
+ {
+ double fVal = 0.0;
+ if( aRet >>= fVal )
+ return aRet;
+ uno::Sequence< uno::Sequence< uno::Any > > aSequence;
+ if( !( ( aRet >>= aSequence ) && ( aSequence.getLength() > 0 ) &&
+ ( aSequence[0].getLength() > 0 ) && ( aSequence[0][0] >>= fVal ) ) )
+ throw uno::RuntimeException();
+ aRet <<= fVal;
+ }
+#endif
+
+ return aRet;
+}
+
+void SAL_CALL
+ScVbaWSFunction::setValue(const OUString& /*PropertyName*/, const uno::Any& /*Value*/)
+{
+ throw beans::UnknownPropertyException();
+}
+
+uno::Any SAL_CALL
+ScVbaWSFunction::getValue(const OUString& /*PropertyName*/)
+{
+ throw beans::UnknownPropertyException();
+}
+
+sal_Bool SAL_CALL
+ScVbaWSFunction::hasMethod(const OUString& Name)
+{
+ bool bIsFound = false;
+ try
+ {
+ // the function name contained in the com.sun.star.sheet.FunctionDescription service is alwayse localized.
+ // but the function name used in WorksheetFunction is a programmatic name (seems English).
+ // So m_xNameAccess->hasByName( Name ) may fail to find name when a function name has a localized name.
+ if( ScCompiler::IsEnglishSymbol( Name ) )
+ bIsFound = true;
+ }
+ catch( uno::Exception& /*e*/ )
+ {
+ // failed to find name
+ }
+ return bIsFound;
+}
+
+sal_Bool SAL_CALL
+ScVbaWSFunction::hasProperty(const OUString& /*Name*/)
+{
+ return false;
+}
+
+OUString SAL_CALL
+ScVbaWSFunction::getExactName( const OUString& aApproximateName )
+{
+ OUString sName = aApproximateName.toAsciiUpperCase();
+ if ( !hasMethod( sName ) )
+ return OUString();
+ return sName;
+}
+
+OUString
+ScVbaWSFunction::getServiceImplName()
+{
+ return "ScVbaWSFunction";
+}
+
+uno::Sequence< OUString >
+ScVbaWSFunction::getServiceNames()
+{
+ static uno::Sequence< OUString > const aServiceNames
+ {
+ "ooo.vba.excel.WorksheetFunction"
+ };
+ return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/vba/vbawsfunction.hxx b/sc/source/ui/vba/vbawsfunction.hxx
new file mode 100644
index 0000000000..9a5dd821fc
--- /dev/null
+++ b/sc/source/ui/vba/vbawsfunction.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <ooo/vba/excel/XWorksheetFunction.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl< ov::excel::XWorksheetFunction > ScVbaWSFunction_BASE;
+
+class ScVbaWSFunction : public ScVbaWSFunction_BASE
+{
+public:
+ ScVbaWSFunction( const css::uno::Reference< ov::XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext);
+
+ virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection() override;
+ virtual css::uno::Any SAL_CALL invoke(const OUString& FunctionName, const css::uno::Sequence< css::uno::Any >& Params, css::uno::Sequence< sal_Int16 >& OutParamIndex, css::uno::Sequence< css::uno::Any >& OutParam) override;
+ virtual void SAL_CALL setValue(const OUString& PropertyName, const css::uno::Any& Value) override;
+ virtual css::uno::Any SAL_CALL getValue(const OUString& PropertyName) override;
+ virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override;
+ virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override;
+ virtual OUString SAL_CALL getExactName( const OUString& aApproximateName ) override;
+ // XHelperInterface
+ virtual OUString getServiceImplName() override;
+ virtual css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/SparklineShell.cxx b/sc/source/ui/view/SparklineShell.cxx
new file mode 100644
index 0000000000..3b3c4f8399
--- /dev/null
+++ b/sc/source/ui/view/SparklineShell.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <scitems.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <sc.hrc>
+#include <SparklineShell.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+
+#define ShellClass_SparklineShell
+#include <scslots.hxx>
+
+namespace sc
+{
+SFX_IMPL_INTERFACE(SparklineShell, SfxShell)
+
+void SparklineShell::InitInterface_Impl() { GetStaticInterface()->RegisterPopupMenu("sparkline"); }
+
+SparklineShell::SparklineShell(ScTabViewShell* pViewShell)
+ : SfxShell(pViewShell)
+ , m_pViewShell(pViewShell)
+{
+ SetPool(&m_pViewShell->GetPool());
+ ScViewData& rViewData = m_pViewShell->GetViewData();
+ SfxUndoManager* pUndoManager = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager(pUndoManager);
+ if (!rViewData.GetDocument().IsUndoEnabled())
+ {
+ pUndoManager->SetMaxUndoActionCount(0);
+ }
+ SetName("Sparkline");
+ SfxShell::SetContextName(
+ vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Sparkline));
+}
+
+SparklineShell::~SparklineShell() = default;
+
+} // end sc namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/auditsh.cxx b/sc/source/ui/view/auditsh.cxx
new file mode 100644
index 0000000000..2ea903f59e
--- /dev/null
+++ b/sc/source/ui/view/auditsh.cxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <auditsh.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <document.hxx>
+
+#define ShellClass_ScAuditingShell
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScAuditingShell, SfxShell)
+
+void ScAuditingShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("audit");
+}
+
+ScAuditingShell::ScAuditingShell(ScViewData& rData) :
+ SfxShell(rData.GetViewShell()),
+ rViewData( rData ),
+ nFunction( SID_FILL_ADD_PRED )
+{
+ SetPool( &rViewData.GetViewShell()->GetPool() );
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if ( !rViewData.GetDocument().IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Auditing");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Auditing));
+}
+
+ScAuditingShell::~ScAuditingShell()
+{
+}
+
+void ScAuditingShell::Execute( const SfxRequest& rReq )
+{
+ SfxBindings& rBindings = rViewData.GetBindings();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_FILL_ADD_PRED:
+ case SID_FILL_DEL_PRED:
+ case SID_FILL_ADD_SUCC:
+ case SID_FILL_DEL_SUCC:
+ nFunction = nSlot;
+ rBindings.Invalidate( SID_FILL_ADD_PRED );
+ rBindings.Invalidate( SID_FILL_DEL_PRED );
+ rBindings.Invalidate( SID_FILL_ADD_SUCC );
+ rBindings.Invalidate( SID_FILL_DEL_SUCC );
+ break;
+ case SID_CANCEL: // Escape
+ case SID_FILL_NONE:
+ rViewData.GetViewShell()->SetAuditShell( false );
+ break;
+
+ case SID_FILL_SELECT:
+ {
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ if ( pReqArgs )
+ {
+ const SfxInt16Item* pXItem = pReqArgs->GetItemIfSet( SID_RANGE_COL );
+ const SfxInt32Item* pYItem = pReqArgs->GetItemIfSet( SID_RANGE_ROW );
+ if ( pXItem && pYItem )
+ {
+ SCCOL nCol = static_cast<SCCOL>(pXItem->GetValue());
+ SCROW nRow = static_cast<SCROW>(pYItem->GetValue());
+ ScViewFunc* pView = rViewData.GetView();
+ pView->MoveCursorAbs( nCol, nRow, SC_FOLLOW_LINE, false, false );
+ switch ( nFunction )
+ {
+ case SID_FILL_ADD_PRED:
+ pView->DetectiveAddPred();
+ break;
+ case SID_FILL_DEL_PRED:
+ pView->DetectiveDelPred();
+ break;
+ case SID_FILL_ADD_SUCC:
+ pView->DetectiveAddSucc();
+ break;
+ case SID_FILL_DEL_SUCC:
+ pView->DetectiveDelSucc();
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScAuditingShell::GetState( SfxItemSet& rSet )
+{
+ rSet.Put( SfxBoolItem( nFunction, true ) ); // mark active functions
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellmergeoption.cxx b/sc/source/ui/view/cellmergeoption.cxx
new file mode 100644
index 0000000000..524117080f
--- /dev/null
+++ b/sc/source/ui/view/cellmergeoption.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/.
+ */
+
+#include <cellmergeoption.hxx>
+#include <address.hxx>
+
+ScCellMergeOption::ScCellMergeOption(const ScRange& rRange) :
+ mnStartCol(rRange.aStart.Col()),
+ mnStartRow(rRange.aStart.Row()),
+ mnEndCol(rRange.aEnd.Col()),
+ mnEndRow(rRange.aEnd.Row()),
+ mbCenter(false)
+{
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i = nTab1; i <= nTab2; ++i)
+ maTabs.insert(i);
+}
+
+ScCellMergeOption::ScCellMergeOption(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, bool bCenter) :
+ mnStartCol(nStartCol),
+ mnStartRow(nStartRow),
+ mnEndCol(nEndCol),
+ mnEndRow(nEndRow),
+ mbCenter(bCenter)
+{
+}
+
+ScRange ScCellMergeOption::getSingleRange(SCTAB nTab) const
+{
+ return ScRange(mnStartCol, mnStartRow, nTab, mnEndCol, mnEndRow, nTab);
+}
+
+ScRange ScCellMergeOption::getFirstSingleRange() const
+{
+ SCTAB nTab = 0;
+ if (!maTabs.empty())
+ nTab = *maTabs.begin();
+
+ return getSingleRange(nTab);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh.cxx b/sc/source/ui/view/cellsh.cxx
new file mode 100644
index 0000000000..1a866df3c8
--- /dev/null
+++ b/sc/source/ui/view/cellsh.cxx
@@ -0,0 +1,1325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/cliplistener.hxx>
+#include <svtools/insdlg.hxx>
+#include <sot/formats.hxx>
+#include <svx/hlnkitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <svx/statusitem.hxx>
+
+#include <cellsh.hxx>
+#include <sc.hrc>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <tabvwsh.hxx>
+#include <formulacell.hxx>
+#include <scmod.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <scabstdlg.hxx>
+#include <postit.hxx>
+#include <cliputil.hxx>
+#include <clipparam.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+
+#define ShellClass_ScCellShell
+#define ShellClass_CellMovement
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScCellShell, ScFormatShell)
+
+void ScCellShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Format);
+
+ GetStaticInterface()->RegisterPopupMenu("cell");
+}
+
+ScCellShell::ScCellShell(ScViewData& rData, const VclPtr<vcl::Window>& frameWin) :
+ ScFormatShell(rData),
+ pImpl( new CellShell_Impl() ),
+ bPastePossible(false),
+ pFrameWin(frameWin)
+{
+ SetName("Cell");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Cell));
+}
+
+ScCellShell::~ScCellShell()
+{
+ if ( pImpl->m_xClipEvtLstnr.is() )
+ {
+ pImpl->m_xClipEvtLstnr->RemoveListener( GetViewData().GetActiveWin() );
+
+ // The listener may just now be waiting for the SolarMutex and call the link
+ // afterwards, in spite of RemoveListener. So the link has to be reset, too.
+ pImpl->m_xClipEvtLstnr->ClearCallbackLink();
+
+ pImpl->m_xClipEvtLstnr.clear();
+ }
+
+ pImpl->m_pLinkedDlg.disposeAndClear();
+ delete pImpl->m_pRequest;
+}
+
+void ScCellShell::GetBlockState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScRange aMarkRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange );
+ bool bSimpleArea = (eMarkType == SC_MARK_SIMPLE);
+ bool bOnlyNotBecauseOfMatrix;
+ bool bEditable = pTabViewShell->SelectionEditable( &bOnlyNotBecauseOfMatrix );
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ nCol1 = aMarkRange.aStart.Col();
+ nRow1 = aMarkRange.aStart.Row();
+ nCol2 = aMarkRange.aEnd.Col();
+ nRow2 = aMarkRange.aEnd.Row();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ bool bDisable = false;
+ bool bNeedEdit = true; // need selection be editable?
+ switch ( nWhich )
+ {
+ case FID_FILL_TO_BOTTOM: // fill to top / bottom
+ {
+ bDisable = !bSimpleArea || (nRow1 == 0 && nRow2 == 0);
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol2, nRow1, rMark ); // first row
+ }
+ }
+ break;
+ case FID_FILL_TO_TOP:
+ {
+ bDisable = (!bSimpleArea) || (nRow1 == rDoc.MaxRow() && nRow2 == rDoc.MaxRow());
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow2, nCol2, nRow2, rMark ); // last row
+ }
+ }
+ break;
+ case FID_FILL_TO_RIGHT: // fill to left / right
+ {
+ bDisable = !bSimpleArea || (nCol1 == 0 && nCol2 == 0);
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol1, nRow2, rMark ); // first column
+ }
+ }
+ break;
+ case FID_FILL_TO_LEFT:
+ {
+ bDisable = (!bSimpleArea) || (nCol1 == rDoc.MaxCol() && nCol2 == rDoc.MaxCol());
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+ if ( !bDisable && bEditable )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol2, nRow1, nCol2, nRow2, rMark ); // last column
+ }
+ }
+ break;
+
+ case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
+ bDisable = !bSimpleArea || GetViewData().SelectionForbidsCellFill();
+ break;
+ case SID_SAMPLING_DIALOG:
+ case SID_DESCRIPTIVE_STATISTICS_DIALOG:
+ case SID_ANALYSIS_OF_VARIANCE_DIALOG:
+ case SID_CORRELATION_DIALOG:
+ case SID_COVARIANCE_DIALOG:
+ case SID_INSERT_SPARKLINE:
+ {
+ bDisable = !bSimpleArea;
+ }
+ break;
+ case SID_GROUP_SPARKLINES:
+ case SID_UNGROUP_SPARKLINES:
+ {
+ bDisable = !bSimpleArea;
+ }
+ break;
+
+ case SID_EDIT_SPARKLINE:
+ {
+ bDisable = !rDoc.HasSparkline(GetViewData().GetCurPos());
+ }
+ break;
+
+ case SID_DELETE_SPARKLINE:
+ case SID_EDIT_SPARKLINE_GROUP:
+ case SID_DELETE_SPARKLINE_GROUP:
+ {
+ bDisable = !rDoc.HasOneSparklineGroup(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
+ }
+ break;
+
+ case FID_FILL_SERIES: // fill block
+ case SID_OPENDLG_TABOP: // multiple-cell operations, are at least 2 cells marked?
+ if (rDoc.GetChangeTrack()!=nullptr &&nWhich ==SID_OPENDLG_TABOP)
+ bDisable = true;
+ else
+ bDisable = (!bSimpleArea) || (nCol1 == nCol2 && nRow1 == nRow2);
+
+ if (!bDisable && GetViewData().SelectionForbidsCellFill())
+ bDisable = true;
+
+ if ( !bDisable && bEditable && nWhich == FID_FILL_SERIES )
+ { // do not damage matrix
+ bDisable = rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol2, nRow1, rMark ) // first row
+ || rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow2, nCol2, nRow2, rMark ) // last row
+ || rDoc.HasSelectedBlockMatrixFragment(
+ nCol1, nRow1, nCol1, nRow2, rMark ) // first column
+ || rDoc.HasSelectedBlockMatrixFragment(
+ nCol2, nRow1, nCol2, nRow2, rMark ); // last column
+ }
+ break;
+ case FID_FILL_SINGLE_EDIT:
+ bDisable = false;
+ break;
+ case SID_CUT: // cut
+ bDisable = !bSimpleArea || GetObjectShell()->isContentExtractionLocked();
+ break;
+ case FID_INS_CELL: // insert cells, just simple selection
+ bDisable = (!bSimpleArea);
+ break;
+
+ case SID_PASTE:
+ case SID_PASTE_SPECIAL:
+ case SID_PASTE_UNFORMATTED:
+ case SID_PASTE_ONLY_VALUE:
+ case SID_PASTE_ONLY_TEXT:
+ case SID_PASTE_ONLY_FORMULA:
+ case SID_PASTE_TRANSPOSED:
+ case SID_PASTE_AS_LINK:
+ case SID_PASTE_TEXTIMPORT_DIALOG:
+ bDisable = GetViewData().SelectionForbidsPaste();
+ break;
+
+ case FID_INS_ROW:
+ case FID_INS_ROWS_BEFORE: // insert rows
+ case FID_INS_ROWS_AFTER:
+ {
+ sc::ColRowEditAction eAction = sc::ColRowEditAction::InsertRowsBefore;
+ if (nWhich == FID_INS_ROWS_AFTER)
+ eAction = sc::ColRowEditAction::InsertRowsAfter;
+
+ bDisable = (!bSimpleArea) || GetViewData().SimpleColMarked();
+ if (!bEditable && nCol1 == 0 && nCol2 == rDoc.MaxCol())
+ {
+ // See if row insertions are allowed.
+ bEditable = rDoc.IsEditActionAllowed(eAction, rMark, nRow1, nRow2);
+ }
+ break;
+ }
+ case FID_INS_CELLSDOWN:
+ bDisable = (!bSimpleArea) || GetViewData().SimpleColMarked();
+ break;
+
+ case FID_INS_COLUMN:
+ case FID_INS_COLUMNS_BEFORE: // insert columns
+ case FID_INS_COLUMNS_AFTER:
+ {
+ sc::ColRowEditAction eAction = sc::ColRowEditAction::InsertColumnsBefore;
+ if (nWhich == FID_INS_COLUMNS_AFTER)
+ eAction = sc::ColRowEditAction::InsertColumnsAfter;
+
+ bDisable = (!bSimpleArea && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ || GetViewData().SimpleRowMarked();
+ if (!bEditable && nRow1 == 0 && nRow2 == rDoc.MaxRow())
+ {
+ // See if row insertions are allowed.
+ bEditable = rDoc.IsEditActionAllowed(eAction, rMark, nCol1, nCol2);
+ }
+ break;
+ }
+ case FID_INS_CELLSRIGHT:
+ bDisable = (!bSimpleArea) || GetViewData().SimpleRowMarked();
+ break;
+
+ case SID_COPY: // copy
+ // not editable because of matrix only? Do not damage matrix
+ //! is not called, when protected AND matrix, we will have
+ //! to live with this... is caught in Copy-Routine, otherwise
+ //! work is to be done once more
+ if ( bEditable || !bOnlyNotBecauseOfMatrix )
+ bNeedEdit = false; // allowed when protected/ReadOnly
+ bDisable = GetObjectShell()->isContentExtractionLocked();
+ break;
+
+ case SID_AUTOFORMAT: // Autoformat, at least 3x3 selected
+ bDisable = (!bSimpleArea)
+ || ((nCol2 - nCol1) < 2) || ((nRow2 - nRow1) < 2);
+ break;
+
+ case SID_CELL_FORMAT_RESET :
+ case FID_CELL_FORMAT :
+ case SID_ENABLE_HYPHENATION :
+ // not editable because of matrix only? Attribute ok nonetheless
+ if ( !bEditable && bOnlyNotBecauseOfMatrix )
+ bNeedEdit = false;
+ break;
+
+ case FID_VALIDATION:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ bDisable = true;
+ }
+ }
+ break;
+ case SID_TRANSLITERATE_HALFWIDTH:
+ case SID_TRANSLITERATE_FULLWIDTH:
+ case SID_TRANSLITERATE_HIRAGANA:
+ case SID_TRANSLITERATE_KATAKANA:
+ ScViewUtil::HideDisabledSlot( rSet, GetViewData().GetBindings(), nWhich );
+ break;
+ case SID_CONVERT_FORMULA_TO_VALUE:
+ {
+ // Check and see if the marked range has at least one formula cell.
+ bDisable = !rDoc.HasFormulaCell(aMarkRange);
+ }
+ break;
+ }
+ if (!bDisable && bNeedEdit && !bEditable)
+ bDisable = true;
+
+ if (bDisable)
+ rSet.DisableItem(nWhich);
+ else if (nWhich == SID_ENABLE_HYPHENATION)
+ {
+ // toggle slots need a bool item
+ rSet.Put( SfxBoolItem( nWhich, false ) );
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+// functions, disabled depending on cursor position
+// Default:
+// SID_INSERT_POSTIT, SID_CHARMAP, SID_OPENDLG_FUNCTION
+
+void ScCellShell::GetCellState( SfxItemSet& rSet )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument();
+ ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ bool bDisable = false;
+ bool bNeedEdit = true; // need cursor position be editable?
+ switch ( nWhich )
+ {
+ case SID_THESAURUS:
+ {
+ CellType eType = rDoc.GetCellType( aCursor );
+ bDisable = ( eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT);
+ if (!bDisable)
+ {
+ // test for available languages
+ LanguageType nLang = ScViewUtil::GetEffLanguage( rDoc, aCursor );
+ bDisable = !ScModule::HasThesaurusLanguage( nLang );
+ }
+ }
+ break;
+ case SID_OPENDLG_FUNCTION:
+ {
+ ScMarkData aMarkData = GetViewData().GetMarkData();
+ aMarkData.MarkToSimple();
+ const ScRange& aRange = aMarkData.GetMarkArea();
+ if(aMarkData.IsMarked())
+ {
+ if (!rDoc.IsBlockEditable( aCursor.Tab(), aRange.aStart.Col(),aRange.aStart.Row(),
+ aRange.aEnd.Col(),aRange.aEnd.Row() ))
+ {
+ bDisable = true;
+ }
+ bNeedEdit=false;
+ }
+
+ }
+ break;
+ case SID_INSERT_POSTIT:
+ {
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if( rDoc.GetNote(aPos) )
+ {
+ bDisable = true;
+ }
+ else
+ {
+ bDisable = false;
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ bDisable = true;
+ }
+ }
+ }
+ break;
+ case SID_EDIT_POSTIT:
+ {
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ bDisable = rDoc.GetNote(aPos) == nullptr;
+ }
+ break;
+ }
+ if (!bDisable && bNeedEdit)
+ if (!rDoc.IsBlockEditable( aCursor.Tab(), aCursor.Col(),aCursor.Row(),
+ aCursor.Col(),aCursor.Row() ))
+ bDisable = true;
+ if (bDisable)
+ rSet.DisableItem(nWhich);
+ nWhich = aIter.NextWhich();
+ }
+}
+
+static bool lcl_TestFormat( SvxClipboardFormatItem& rFormats, const TransferableDataHelper& rDataHelper,
+ SotClipboardFormatId nFormatId )
+{
+ if ( rDataHelper.HasFormat( nFormatId ) )
+ {
+ // translated format name strings are no longer inserted here,
+ // handled by "paste special" dialog / toolbox controller instead.
+ // Only the object type name has to be set here:
+ OUString aStrVal;
+ if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE )
+ {
+ TransferableObjectDescriptor aDesc;
+ if ( rDataHelper.GetTransferableObjectDescriptor(
+ SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc ) )
+ aStrVal = aDesc.maTypeName;
+ }
+ else if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE_OLE
+ || nFormatId == SotClipboardFormatId::EMBEDDED_OBJ_OLE )
+ {
+ OUString aSource;
+ SvPasteObjectHelper::GetEmbeddedName( rDataHelper, aStrVal, aSource, nFormatId );
+ }
+
+ if ( !aStrVal.isEmpty() )
+ rFormats.AddClipbrdFormat( nFormatId, aStrVal );
+ else
+ rFormats.AddClipbrdFormat( nFormatId );
+
+ return true;
+ }
+
+ return false;
+}
+
+void ScCellShell::GetPossibleClipboardFormats( SvxClipboardFormatItem& rFormats )
+{
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ bool bDraw = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin)) != nullptr;
+
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::DRAWING );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::SVXB );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::GDIMETAFILE );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::PNG );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BITMAP );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBED_SOURCE );
+
+ if ( !bDraw )
+ {
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::LINK );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::STRING );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::STRING_TSVC );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::DIF );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::RTF );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::RICHTEXT );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::HTML );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::HTML_SIMPLE );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BIFF_8 );
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BIFF_5 );
+ }
+
+ if ( !lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBED_SOURCE_OLE ) )
+ lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBEDDED_OBJ_OLE );
+}
+
+// insert, insert contents
+
+static bool lcl_IsCellPastePossible( const TransferableDataHelper& rData )
+{
+ bool bPossible = false;
+ css::uno::Reference< css::datatransfer::XTransferable2 > xTransferable(rData.GetXTransferable(), css::uno::UNO_QUERY);
+ if ( ScTransferObj::GetOwnClipboard(xTransferable) || ScDrawTransferObj::GetOwnClipboard(xTransferable) )
+ bPossible = true;
+ else
+ {
+ if ( rData.HasFormat( SotClipboardFormatId::PNG ) ||
+ rData.HasFormat( SotClipboardFormatId::BITMAP ) ||
+ rData.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ||
+ rData.HasFormat( SotClipboardFormatId::SVXB ) ||
+ rData.HasFormat( SotClipboardFormatId::PRIVATE ) ||
+ rData.HasFormat( SotClipboardFormatId::RTF ) ||
+ rData.HasFormat( SotClipboardFormatId::RICHTEXT ) ||
+ rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) ||
+ rData.HasFormat( SotClipboardFormatId::LINK_SOURCE ) ||
+ rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
+ rData.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
+ rData.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
+ rData.HasFormat( SotClipboardFormatId::STRING ) ||
+ rData.HasFormat( SotClipboardFormatId::STRING_TSVC ) ||
+ rData.HasFormat( SotClipboardFormatId::SYLK ) ||
+ rData.HasFormat( SotClipboardFormatId::LINK ) ||
+ rData.HasFormat( SotClipboardFormatId::HTML ) ||
+ rData.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) ||
+ rData.HasFormat( SotClipboardFormatId::DIF ) )
+ {
+ bPossible = true;
+ }
+ }
+ return bPossible;
+}
+
+bool ScCellShell::HasClipboardFormat( SotClipboardFormatId nFormatId )
+{
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ));
+ return aDataHelper.HasFormat( nFormatId );
+}
+
+IMPL_LINK( ScCellShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void )
+{
+ bPastePossible = lcl_IsCellPastePossible( *pDataHelper );
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+ rBindings.Invalidate( SID_PASTE_ONLY_VALUE );
+ rBindings.Invalidate( SID_PASTE_ONLY_TEXT );
+ rBindings.Invalidate( SID_PASTE_ONLY_FORMULA );
+ rBindings.Invalidate( SID_PASTE_TRANSPOSED );
+ rBindings.Invalidate( SID_PASTE_AS_LINK );
+ rBindings.Invalidate( SID_PASTE_TEXTIMPORT_DIALOG );
+ rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS );
+}
+
+namespace {
+
+bool checkDestRanges(ScViewData& rViewData)
+{
+ ScRange aDummy;
+ ScMarkType eMarkType = rViewData.GetSimpleArea( aDummy);
+ if (eMarkType != SC_MARK_MULTI)
+ {
+ // Single destination range.
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ return false;
+ }
+
+ // Multiple destination ranges.
+
+ // Same as ScViewData::SelectionForbidsPaste() in
+ // sc/source/ui/view/viewdata.cxx but different return details.
+
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ if (!pWin)
+ return false;
+
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if (!pOwnClip)
+ // If it's not a Calc document, we won't be picky.
+ return true;
+
+ ScDocument* pClipDoc = pOwnClip->GetDocument();
+ if (!pClipDoc)
+ return false;
+
+ ScRange aSrcRange = pClipDoc->GetClipParam().getWholeRange();
+ SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
+ SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ if (rViewData.SelectionForbidsPaste( nColSize, nRowSize))
+ return false;
+
+ ScMarkData aMark = rViewData.GetMarkData();
+ ScRangeList aRanges;
+ aMark.MarkToSimple();
+ aMark.FillRangeListWithMarks(&aRanges, false);
+
+ return ScClipUtil::CheckDestRanges(rViewData.GetDocument(), nColSize, nRowSize, aMark, aRanges);
+}
+
+}
+
+void ScCellShell::GetClipState( SfxItemSet& rSet )
+{
+// SID_PASTE
+// SID_PASTE_SPECIAL
+// SID_PASTE_UNFORMATTED
+// SID_CLIPBOARD_FORMAT_ITEMS
+
+ if ( !pImpl->m_xClipEvtLstnr.is() )
+ {
+ // create listener
+ pImpl->m_xClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScCellShell, ClipboardChanged ) );
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ pImpl->m_xClipEvtLstnr->AddListener( pWin );
+
+ // get initial state
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+ bPastePossible = lcl_IsCellPastePossible( aDataHelper );
+ }
+
+ bool bDisable = !bPastePossible;
+
+ // cell protection / multiple selection
+
+ if (!bDisable)
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument();
+ if (!rDoc.IsBlockEditable( nTab, nCol,nRow, nCol,nRow ))
+ bDisable = true;
+
+ if (!bDisable && !checkDestRanges(GetViewData()))
+ bDisable = true;
+ }
+
+ if (bDisable)
+ {
+ rSet.DisableItem( SID_PASTE );
+ rSet.DisableItem( SID_PASTE_SPECIAL );
+ rSet.DisableItem( SID_PASTE_UNFORMATTED );
+ rSet.DisableItem( SID_PASTE_ONLY_VALUE );
+ rSet.DisableItem( SID_PASTE_ONLY_TEXT );
+ rSet.DisableItem( SID_PASTE_ONLY_FORMULA );
+ rSet.DisableItem( SID_PASTE_TRANSPOSED );
+ rSet.DisableItem( SID_PASTE_AS_LINK );
+ rSet.DisableItem( SID_PASTE_TEXTIMPORT_DIALOG );
+ rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS );
+ }
+ else if ( rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) != SfxItemState::UNKNOWN )
+ {
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ GetPossibleClipboardFormats( aFormats );
+ rSet.Put( aFormats );
+ }
+}
+
+// only SID_HYPERLINK_GETLINK:
+
+void ScCellShell::GetHLinkState( SfxItemSet& rSet )
+{
+ // always return an item (or inserting will be disabled)
+ // if the cell at the cursor contains only a link, return that link
+
+ SvxHyperlinkItem aHLinkItem;
+ if ( !GetViewData().GetView()->HasBookmarkAtCursor( &aHLinkItem ) )
+ {
+ // tdf#80043 - put selected text into item
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+ SCCOL nPosX = rData.GetCurX();
+ SCROW nPosY = rData.GetCurY();
+ SCTAB nTab = rData.GetTabNo();
+ aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab));
+ }
+
+ rSet.Put(aHLinkItem);
+}
+
+void ScCellShell::GetState(SfxItemSet &rSet)
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData& rMark = rData.GetMarkData();
+ SCCOL nPosX = rData.GetCurX();
+ SCROW nPosY = rData.GetCurY();
+ SCTAB nTab = rData.GetTabNo();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_DETECTIVE_REFRESH:
+ if (!rDoc.HasDetectiveOperations())
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_RANGE_ADDRESS:
+ {
+ ScRange aRange;
+ if ( rData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ OUString aStr(aRange.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
+ rSet.Put( SfxStringItem( nWhich, aStr ) );
+ }
+ }
+ break;
+
+ case SID_RANGE_NOTETEXT:
+ {
+ // always take cursor position, do not use top-left cell of selection
+ OUString aNoteText;
+ if ( const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab) )
+ aNoteText = pNote->GetText();
+ rSet.Put( SfxStringItem( nWhich, aNoteText ) );
+ }
+ break;
+
+ case SID_RANGE_ROW:
+ rSet.Put( SfxInt32Item( SID_RANGE_ROW, nPosY+1 ) );
+ break;
+
+ case SID_RANGE_COL:
+ rSet.Put( SfxInt16Item( SID_RANGE_COL, nPosX+1 ) );
+ break;
+
+ case SID_RANGE_TABLE:
+ rSet.Put( SfxInt16Item( SID_RANGE_TABLE, nTab+1 ) );
+ break;
+
+ case SID_RANGE_FORMULA:
+ {
+ OUString aString = rDoc.GetFormula( nPosX, nPosY, nTab );
+ if( aString.isEmpty() )
+ {
+ aString = rDoc.GetInputString( nPosX, nPosY, nTab );
+ }
+ rSet.Put( SfxStringItem( nWhich, aString ) );
+ }
+ break;
+
+ case SID_RANGE_TEXTVALUE:
+ {
+ OUString aString = rDoc.GetString(nPosX, nPosY, nTab);
+ rSet.Put( SfxStringItem( nWhich, aString ) );
+ }
+ break;
+
+ case SID_STATUS_SELMODE:
+ {
+ /* 0: STD Click cancels Sel
+ * 1: ER Click extends selection
+ * 2: ERG Click defines further selection
+ */
+ sal_uInt16 nMode = pTabViewShell->GetLockedModifiers();
+
+ switch ( nMode )
+ {
+ case KEY_SHIFT: nMode = 1; break;
+ case KEY_MOD1: nMode = 2; break; // Control-key
+ case 0:
+ default:
+ nMode = 0;
+ }
+
+ rSet.Put( SfxUInt16Item( nWhich, nMode ) );
+ }
+ break;
+
+ case SID_STATUS_DOCPOS:
+ {
+ OUString aStr = ScResId( STR_TABLE_COUNT );
+
+ aStr = aStr.replaceFirst("%1", OUString::number( nTab + 1 ) );
+ aStr = aStr.replaceFirst("%2", OUString::number( nTabCount ) );
+
+ rSet.Put( SfxStringItem( nWhich, aStr ) ); }
+ break;
+
+ case SID_ROWCOL_SELCOUNT:
+ {
+ ScRangeListRef aMarkRanges;
+ GetViewData().GetMultiArea(aMarkRanges);
+ const SCCOL nCol1 = aMarkRanges->front().aStart.Col();
+ const SCROW nRow1 = aMarkRanges->front().aStart.Row();
+ const SCCOL nCol2 = aMarkRanges->front().aEnd.Col();
+ const SCROW nRow2 = aMarkRanges->front().aEnd.Row();
+ const size_t nRanges = aMarkRanges->size();
+
+ if ((nRanges == 1 && (nCol2 != nCol1 || nRow1 != nRow2)) || nRanges > 1)
+ {
+ bool bSameRows = true;
+ bool bSameCols = true;
+ SCROW nRowsSum = 0;
+ SCCOL nColsSum = 0;
+ for (size_t i = 0; i < nRanges; ++i)
+ {
+ const ScRange& rRange = (*aMarkRanges)[i];
+ const SCCOL nRangeCol1 = rRange.aStart.Col();
+ const SCROW nRangeRow1 = rRange.aStart.Row();
+ const SCCOL nRangeCol2 = rRange.aEnd.Col();
+ const SCROW nRangeRow2 = rRange.aEnd.Row();
+ bSameRows &= (nRow1 == nRangeRow1 && nRow2 == nRangeRow2);
+ bSameCols &= (nCol1 == nRangeCol1 && nCol2 == nRangeCol2);
+ // Sum rows if the number of cols is the same or
+ // sum columns if the number of rows is the same,
+ // otherwise do not show any count of selected cells.
+ if (bSameRows || bSameCols)
+ {
+ const auto nCols = nRangeCol2 - nRangeCol1 + 1;
+ const auto nRows = (bSameCols || nRowsSum == 0) ?
+ rDoc.CountNonFilteredRows( nRangeRow1, nRangeRow2, rRange.aStart.Tab()) :
+ nRowsSum;
+ if (bSameRows)
+ {
+ nRowsSum = nRows;
+ nColsSum += nCols;
+ }
+ else if (bSameCols)
+ {
+ nRowsSum += nRows;
+ nColsSum = nCols;
+ }
+ }
+ else
+ break;
+ }
+ // Either the rows or columns are the same among selections
+ if (bSameRows || bSameCols)
+ {
+ const LocaleDataWrapper& rLocaleData
+ = Application::GetSettings().GetUILocaleDataWrapper();
+ OUString aRowArg
+ = ScResId(STR_SELCOUNT_ROWARG, nRowsSum)
+ .replaceAll("%d", rLocaleData.getNum(nRowsSum, 0));
+ OUString aColArg
+ = ScResId(STR_SELCOUNT_COLARG, nColsSum)
+ .replaceAll("%d", rLocaleData.getNum(nColsSum, 0));
+ OUString aStr = ScResId(STR_SELCOUNT);
+ aStr = aStr.replaceAll("%1", aRowArg);
+ aStr = aStr.replaceAll("%2", aColArg);
+ rSet.Put(SfxStringItem(nWhich, aStr));
+ }
+ }
+ else
+ {
+ SCSIZE nSelected, nTotal;
+ rDoc.GetFilterSelCount( nPosX, nPosY, nTab, nSelected, nTotal );
+ if( nTotal && nSelected != SCSIZE_MAX )
+ {
+ OUString aStr = ScResId( STR_FILTER_SELCOUNT );
+ aStr = aStr.replaceAll( "%1", OUString::number( nSelected ) );
+ aStr = aStr.replaceAll( "%2", OUString::number( nTotal ) );
+ rSet.Put( SfxStringItem( nWhich, aStr ) );
+ }
+ }
+ }
+ break;
+
+ // calculations etc. with date/time/Fail/position&size together
+
+ // #i34458# The SvxStatusItem belongs only into SID_TABLE_CELL. It no longer has to be
+ // duplicated in SID_ATTR_POSITION or SID_ATTR_SIZE for SvxPosSizeStatusBarControl.
+ case SID_TABLE_CELL:
+ {
+ // Test, if error under cursor
+ // (not rDoc.GetErrCode, to avoid erasing circular references)
+
+ // In interpreter may happen via rescheduled Basic
+ if ( rDoc.IsInInterpreter() )
+ rSet.Put( SvxStatusItem( SID_TABLE_CELL, "...", StatusCategory::Formula ) );
+ else
+ {
+ FormulaError nErrCode = FormulaError::NONE;
+ ScFormulaCell* pCell = rDoc.GetFormulaCell(ScAddress(nPosX, nPosY, nTab));
+ if (pCell && !pCell->IsRunning())
+ nErrCode = pCell->GetErrCode();
+
+ OUString aFuncStr;
+ if ( pTabViewShell->GetFunction( aFuncStr, nErrCode ) )
+ {
+ rSet.Put( SvxStatusItem( SID_TABLE_CELL, aFuncStr, StatusCategory::Formula ) );
+ }
+ }
+ }
+ break;
+
+ case SID_DATA_SELECT:
+ // HasSelectionData includes column content and validity,
+ // page fields have to be checked separately.
+ if ( !rDoc.HasSelectionData( nPosX, nPosY, nTab ) &&
+ !pTabViewShell->HasPageFieldDataAtCursor() )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_CURRENTVALIDATION:
+ if ( !rDoc.HasValidationData( nPosX, nPosY, nTab ))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_STATUS_SUM:
+ {
+ OUString aFuncStr;
+ if ( pTabViewShell->GetFunction( aFuncStr, FormulaError::NONE ) )
+ rSet.Put( SfxStringItem( nWhich, aFuncStr ) );
+ }
+ break;
+
+ case FID_MERGE_ON:
+ if ( rDoc.GetChangeTrack() || !pTabViewShell->TestMergeCells() )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_MERGE_OFF:
+ if ( rDoc.GetChangeTrack() || !pTabViewShell->TestRemoveMerge() )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_MERGE_TOGGLE:
+ if ( rDoc.GetChangeTrack() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool bCanMerge = pTabViewShell->TestMergeCells();
+ bool bCanSplit = pTabViewShell->TestRemoveMerge();
+ if( !bCanMerge && !bCanSplit )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, bCanSplit ) );
+ }
+ break;
+
+ case FID_INS_ROWBRK:
+ if ( nPosY==0 || (rDoc.HasRowBreak(nPosY, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_INS_COLBRK:
+ if ( nPosX==0 || (rDoc.HasColBreak(nPosX, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_DEL_ROWBRK:
+ if ( nPosY==0 || !(rDoc.HasRowBreak(nPosY, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_DEL_COLBRK:
+ if ( nPosX==0 || !(rDoc.HasColBreak(nPosX, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_FILL_TAB:
+ if ( nTabSelCount < 2 )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_CURRENT_DATE:
+ case SID_INSERT_CURRENT_TIME:
+ {
+ if ( rDoc.IsTabProtected(nTab) &&
+ rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected))
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_SELECT_SCENARIO:
+ {
+ std::vector<OUString> aList;
+ Color aDummyCol;
+
+ if ( !rDoc.IsScenario(nTab) )
+ {
+ OUString aStr;
+ ScScenarioFlags nFlags;
+ SCTAB nScTab = nTab + 1;
+ bool bSheetProtected = rDoc.IsTabProtected(nTab);
+
+ while ( rDoc.IsScenario(nScTab) )
+ {
+ rDoc.GetName( nScTab, aStr );
+ aList.push_back(aStr);
+ rDoc.GetScenarioData( nScTab, aStr, aDummyCol, nFlags );
+ aList.push_back(aStr);
+ // Protection is sal_True if both Sheet and Scenario are protected
+ aList.push_back((bSheetProtected && (nFlags & ScScenarioFlags::Protected)) ? OUString("1") : OUString("0"));
+ ++nScTab;
+ }
+ }
+ else
+ {
+ OUString aComment;
+ ScScenarioFlags nDummyFlags;
+ rDoc.GetScenarioData( nTab, aComment, aDummyCol, nDummyFlags );
+ OSL_ENSURE( aList.empty(), "List not empty!" );
+ aList.push_back(aComment);
+ }
+
+ rSet.Put( SfxStringListItem( nWhich, &aList ) );
+ }
+ break;
+
+ case FID_ROW_HIDE:
+ case FID_ROW_SHOW:
+ case FID_COL_HIDE:
+ case FID_COL_SHOW:
+ case FID_COL_OPT_WIDTH:
+ case FID_ROW_OPT_HEIGHT:
+ case FID_DELETE_CELL:
+ if ( rDoc.IsTabProtected(nTab) || pDocSh->IsReadOnly())
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_OUTLINE_MAKE:
+ {
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else if (rDoc.GetChangeTrack()!=nullptr || GetViewData().IsMultiMarked())
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_OUTLINE_SHOW:
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else if (!pTabViewShell->OutlinePossible(false))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_OUTLINE_HIDE:
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else if (!pTabViewShell->OutlinePossible(true))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_OUTLINE_REMOVE:
+ {
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ //! test for data pilot operation
+ }
+ else
+ {
+ bool bCol, bRow;
+ pTabViewShell->TestRemoveOutline( bCol, bRow );
+ if ( !bCol && !bRow )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_COL_WIDTH:
+ {
+ SfxUInt16Item aWidthItem( FID_COL_WIDTH, rDoc.GetColWidth( nPosX , nTab) );
+ rSet.Put( aWidthItem );
+ if ( pDocSh->IsReadOnly())
+ rSet.DisableItem( nWhich );
+
+ //XXX disable if not conclusive
+ }
+ break;
+
+ case FID_ROW_HEIGHT:
+ {
+ SfxUInt16Item aHeightItem( FID_ROW_HEIGHT, rDoc.GetRowHeight( nPosY , nTab) );
+ rSet.Put( aHeightItem );
+ //XXX disable if not conclusive
+ if ( pDocSh->IsReadOnly())
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DETECTIVE_FILLMODE:
+ rSet.Put(SfxBoolItem( nWhich, pTabViewShell->IsAuditShell() ));
+ break;
+
+ case FID_INPUTLINE_STATUS:
+ OSL_FAIL( "Old update method. Use ScTabViewShell::UpdateInputHandler()." );
+ break;
+
+ case SID_SCENARIOS: // scenarios:
+ if (!(rMark.IsMarked() || rMark.IsMultiMarked())) // only, if something selected
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_NOTE_VISIBLE:
+ {
+ const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab);
+ if ( pNote && rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) )
+ rSet.Put( SfxBoolItem( nWhich, pNote->IsCaptionShown() ) );
+ else
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_HIDE_NOTE:
+ case FID_SHOW_NOTE:
+ {
+ bool bEnable = false;
+ bool bSearchForHidden = nWhich == FID_SHOW_NOTE;
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ {
+ // Check current cell
+ const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab);
+ if ( pNote && rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) )
+ if ( pNote->IsCaptionShown() != bSearchForHidden)
+ bEnable = true;
+ }
+ else
+ {
+ // Check selection range
+ ScRangeListRef aRangesRef;
+ rData.GetMultiArea(aRangesRef);
+ ScRangeList aRanges = *aRangesRef;
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetNotesInRange(aRanges, aNotes);
+ for(const auto& rNote : aNotes)
+ {
+ const ScAddress& rAdr = rNote.maPos;
+ if( rDoc.IsBlockEditable( rAdr.Tab(), rAdr.Col(), rAdr.Row(), rAdr.Col(), rAdr.Row() ))
+ {
+ if (rNote.mpNote->IsCaptionShown() != bSearchForHidden)
+ {
+ bEnable = true;
+ break;
+ }
+ }
+ }
+
+ }
+ if ( !bEnable )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_SHOW_ALL_NOTES:
+ case FID_HIDE_ALL_NOTES:
+ case FID_DELETE_ALL_NOTES:
+ {
+ bool bHasNotes = false;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ if (rDoc.HasTabNotes( rTab ))
+ {
+ bHasNotes = true;
+ break;
+ }
+ }
+
+ if ( !bHasNotes )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_TOGGLE_NOTES:
+ {
+ bool bHasNotes = false;
+ ScRangeList aRanges;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ if (rDoc.HasTabNotes( rTab ))
+ {
+ bHasNotes = true;
+ aRanges.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab));
+ }
+ }
+
+ if ( !bHasNotes )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ CommentCaptionState eState = rDoc.GetAllNoteCaptionsState( aRanges );
+ bool bAllNotesInShown = (eState != ALLHIDDEN && eState != MIXED);
+ rSet.Put( SfxBoolItem( SID_TOGGLE_NOTES, bAllNotesInShown) );
+ }
+ }
+ break;
+
+ case SID_DELETE_NOTE:
+ {
+ bool bEnable = false;
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ if ( rDoc.IsSelectionEditable( rMark ) )
+ {
+ // look for at least one note in selection
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ bEnable = rDoc.ContainsNotesInRange( aRanges );
+ }
+ }
+ else
+ {
+ bEnable = rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) &&
+ rDoc.GetNote(nPosX, nPosY, nTab);
+ }
+ if ( !bEnable )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_OPENDLG_CONSOLIDATE:
+ case SCITEM_CONSOLIDATEDATA:
+ {
+ if (rDoc.GetChangeTrack()!=nullptr)
+ rSet.DisableItem( nWhich);
+ }
+ break;
+
+ case SID_CHINESE_CONVERSION:
+ case SID_HANGUL_HANJA_CONVERSION:
+ ScViewUtil::HideDisabledSlot( rSet, rData.GetBindings(), nWhich );
+ break;
+
+ case FID_USE_NAME:
+ {
+ if ( pDocSh && pDocSh->IsDocShared() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ ScRange aRange;
+ if ( rData.GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_DEFINE_NAME:
+ case FID_INSERT_NAME:
+ case FID_ADD_NAME:
+ case SID_DEFINE_COLROWNAMERANGES:
+ {
+ if ( pDocSh && pDocSh->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_DEFINE_CURRENT_NAME:
+ {
+ ScAddress aCurrentAddress( nPosX, nPosY, nTab );
+
+ if ( !rDoc.IsAddressInRangeName( RangeNameScope::GLOBAL, aCurrentAddress ) &&
+ !rDoc.IsAddressInRangeName( RangeNameScope::SHEET, aCurrentAddress ))
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_SPELL_DIALOG:
+ {
+ if (rDoc.IsTabProtected(rData.GetTabNo()))
+ {
+ bool bVisible = false;
+ SfxViewFrame* pViewFrame = ( pTabViewShell ? &pTabViewShell->GetViewFrame() : nullptr );
+ if ( pViewFrame && pViewFrame->HasChildWindow( nWhich ) )
+ {
+ SfxChildWindow* pChild = pViewFrame->GetChildWindow( nWhich );
+ std::shared_ptr<SfxDialogController> xController = pChild ? pChild->GetController() : nullptr;
+ if (xController && xController->getDialog()->get_visible())
+ {
+ bVisible = true;
+ }
+ }
+ if ( !bVisible )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ }
+ break;
+
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT_MANAGER:
+ {
+ const SfxPoolItem* pItem = rDoc.GetAttr( nPosX, nPosY, nTab, ATTR_CONDITIONAL );
+ const ScCondFormatItem* pCondFormatItem = static_cast<const ScCondFormatItem*>(pItem);
+
+ if ( pCondFormatItem->GetCondFormatData().empty() )
+ rSet.DisableItem( nWhich );
+ else if ( pCondFormatItem->GetCondFormatData().size() == 1 )
+ rSet.DisableItem( SID_OPENDLG_CURRENTCONDFRMT_MANAGER );
+ else if ( pCondFormatItem->GetCondFormatData().size() > 1 )
+ rSet.DisableItem( SID_OPENDLG_CURRENTCONDFRMT );
+ }
+ break;
+
+ } // switch ( nWitch )
+ nWhich = aIter.NextWhich();
+ } // while ( nWitch )
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh1.cxx b/sc/source/ui/view/cellsh1.cxx
new file mode 100644
index 0000000000..558d5a8166
--- /dev/null
+++ b/sc/source/ui/view/cellsh1.cxx
@@ -0,0 +1,3600 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/i18n/TextConversionOption.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+
+#include <scitems.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <basic/sberrors.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <svl/stritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/svxdlg.hxx>
+#include <sot/formats.hxx>
+#include <svx/postattr.hxx>
+#include <editeng/fontitem.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <svx/hlnkitem.hxx>
+#include <basic/sbxcore.hxx>
+#include <editeng/editview.hxx>
+#include <svtools/cliplistener.hxx>
+
+#include <cellsh.hxx>
+#include <ftools.hxx>
+#include <sc.hrc>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <reffact.hxx>
+#include <inputhdl.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <docfunc.hxx>
+#include <editable.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <spellparam.hxx>
+#include <postit.hxx>
+#include <dpsdbtab.hxx>
+#include <dpshttab.hxx>
+#include <dbdata.hxx>
+#include <docsh.hxx>
+#include <cliputil.hxx>
+#include <markdata.hxx>
+#include <colorscale.hxx>
+#include <condformatdlg.hxx>
+#include <attrib.hxx>
+#include <condformatdlgitem.hxx>
+#include <impex.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scui_def.hxx>
+#include <scabstdlg.hxx>
+#include <tokenstringcontext.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <formulacell.hxx>
+#include <gridwin.hxx>
+#include <searchresults.hxx>
+#include <Sparkline.hxx>
+
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/bootstrap.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+namespace{
+InsertDeleteFlags FlagsFromString(const OUString& rFlagsStr,
+ InsertDeleteFlags nFlagsMask = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB)
+{
+ OUString aFlagsStr = rFlagsStr.toAsciiUpperCase();
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+
+ for (sal_Int32 i=0 ; i < aFlagsStr.getLength(); ++i)
+ {
+ switch (aFlagsStr[i])
+ {
+ case 'A': return InsertDeleteFlags::ALL;
+ case 'S': nFlags |= InsertDeleteFlags::STRING & nFlagsMask; break;
+ case 'V': nFlags |= InsertDeleteFlags::VALUE & nFlagsMask; break;
+ case 'D': nFlags |= InsertDeleteFlags::DATETIME & nFlagsMask; break;
+ case 'F': nFlags |= InsertDeleteFlags::FORMULA & nFlagsMask; break;
+ case 'N': nFlags |= InsertDeleteFlags::NOTE & nFlagsMask; break;
+ case 'T': nFlags |= InsertDeleteFlags::ATTRIB & nFlagsMask; break;
+ case 'O': nFlags |= InsertDeleteFlags::OBJECTS & nFlagsMask; break;
+ }
+ }
+ return nFlags;
+}
+
+OUString FlagsToString( InsertDeleteFlags nFlags,
+ InsertDeleteFlags nFlagsMask = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB )
+{
+ OUString aFlagsStr;
+
+ if( nFlags == InsertDeleteFlags::ALL )
+ {
+ aFlagsStr = "A";
+ }
+ else
+ {
+ nFlags &= nFlagsMask;
+
+ if( nFlags & InsertDeleteFlags::STRING ) aFlagsStr += "S";
+ if( nFlags & InsertDeleteFlags::VALUE ) aFlagsStr += "V";
+ if( nFlags & InsertDeleteFlags::DATETIME ) aFlagsStr += "D";
+ if( nFlags & InsertDeleteFlags::FORMULA ) aFlagsStr += "F";
+ if( nFlags & InsertDeleteFlags::NOTE ) aFlagsStr += "N";
+ if( nFlags & InsertDeleteFlags::ATTRIB ) aFlagsStr += "T";
+ if( nFlags & InsertDeleteFlags::OBJECTS ) aFlagsStr += "O";
+ }
+ return aFlagsStr;
+}
+
+void SetTabNoAndCursor( const ScViewData& rViewData, std::u16string_view rCellId )
+{
+ ScTabViewShell* pTabViewShell = rViewData.GetViewShell();
+ assert(pTabViewShell);
+ const ScDocument& rDoc = rViewData.GetDocShell()->GetDocument();
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+
+ sal_uInt32 nId = o3tl::toUInt32(rCellId);
+ auto lComp = [nId](const sc::NoteEntry& rNote) { return rNote.mpNote->GetId() == nId; };
+
+ const auto& aFoundNoteIt = std::find_if(aNotes.begin(), aNotes.end(), lComp);
+ if (aFoundNoteIt != aNotes.end())
+ {
+ ScAddress aFoundPos = aFoundNoteIt->maPos;
+ pTabViewShell->SetTabNo(aFoundPos.Tab());
+ pTabViewShell->SetCursor(aFoundPos.Col(), aFoundPos.Row());
+ }
+}
+
+void InsertCells(ScTabViewShell* pTabViewShell, SfxRequest &rReq, InsCellCmd eCmd)
+{
+ if (eCmd!=INS_NONE)
+ {
+ pTabViewShell->InsertCells( eCmd );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aParam;
+
+ switch( eCmd )
+ {
+ case INS_CELLSDOWN: aParam = "V"; break;
+ case INS_CELLSRIGHT: aParam = ">"; break;
+ case INS_INSROWS_BEFORE: aParam = "R"; break;
+ case INS_INSCOLS_BEFORE: aParam = "C"; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ rReq.AppendItem( SfxStringItem( FID_INS_CELL, aParam ) );
+ rReq.Done();
+ }
+ }
+}
+
+void DeleteCells(ScTabViewShell* pTabViewShell, SfxRequest &rReq, DelCellCmd eCmd)
+{
+ if (eCmd != DelCellCmd::NONE )
+ {
+ pTabViewShell->DeleteCells( eCmd );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aParam;
+
+ switch( eCmd )
+ {
+ case DelCellCmd::CellsUp: aParam = "U"; break;
+ case DelCellCmd::CellsLeft: aParam = "L"; break;
+ case DelCellCmd::Rows: aParam = "R"; break;
+ case DelCellCmd::Cols: aParam = "C"; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ rReq.AppendItem( SfxStringItem( FID_DELETE_CELL, aParam ) );
+ rReq.Done();
+ }
+ }
+}
+}
+
+void ScCellShell::ExecuteEdit( SfxRequest& rReq )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = pTabViewShell->GetViewFrame().GetBindings();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ // finish input
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ switch ( nSlot )
+ {
+ case FID_DEFINE_NAME:
+ case FID_ADD_NAME:
+ case FID_USE_NAME:
+ case FID_INSERT_NAME:
+ case SID_SPELL_DIALOG:
+ case SID_HANGUL_HANJA_CONVERSION:
+ case SID_OPENDLG_CONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ case SID_OPENDLG_COLORSCALE:
+ case SID_OPENDLG_DATABAR:
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch ( nSlot )
+ {
+
+ // insert / delete cells / rows / columns
+
+ case FID_INS_ROW:
+ case FID_INS_ROWS_BEFORE:
+ pTabViewShell->InsertCells(INS_INSROWS_BEFORE);
+ rReq.Done();
+ break;
+
+ case FID_INS_COLUMN:
+ case FID_INS_COLUMNS_BEFORE:
+ pTabViewShell->InsertCells(INS_INSCOLS_BEFORE);
+ rReq.Done();
+ break;
+
+ case FID_INS_ROWS_AFTER:
+ pTabViewShell->InsertCells(INS_INSROWS_AFTER);
+ rReq.Done();
+ break;
+
+ case FID_INS_COLUMNS_AFTER:
+ pTabViewShell->InsertCells(INS_INSCOLS_AFTER);
+ rReq.Done();
+ break;
+
+ case FID_INS_CELLSDOWN:
+ pTabViewShell->InsertCells(INS_CELLSDOWN);
+ rReq.Done();
+ break;
+
+ case FID_INS_CELLSRIGHT:
+ pTabViewShell->InsertCells(INS_CELLSRIGHT);
+ rReq.Done();
+ break;
+
+ case SID_DEL_ROWS:
+ pTabViewShell->DeleteCells( DelCellCmd::Rows );
+ rReq.Done();
+ break;
+
+ case SID_DEL_COLS:
+ pTabViewShell->DeleteCells( DelCellCmd::Cols );
+ rReq.Done();
+ break;
+
+ case FID_INS_CELL:
+ {
+ InsCellCmd eCmd=INS_NONE;
+
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags;
+
+ if( pReqArgs->HasItem( FID_INS_CELL, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( !aFlags.isEmpty() )
+ {
+ switch( aFlags[0] )
+ {
+ case 'V': eCmd = INS_CELLSDOWN ;break;
+ case '>': eCmd = INS_CELLSRIGHT ;break;
+ case 'R': eCmd = INS_INSROWS_BEFORE ;break;
+ case 'C': eCmd = INS_INSCOLS_BEFORE ;break;
+ }
+ }
+ }
+ else
+ {
+ if ( GetViewData().SimpleColMarked() )
+ eCmd = INS_INSCOLS_BEFORE;
+ else if ( GetViewData().SimpleRowMarked() )
+ eCmd = INS_INSROWS_BEFORE;
+ else
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bTheFlag=(rDoc.GetChangeTrack()!=nullptr);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScInsertCellDlg> pDlg(pFact->CreateScInsertCellDlg(pTabViewShell->GetFrameWeld(), bTheFlag));
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ SfxRequest aRequest(pTabViewShell->GetViewFrame(), FID_INS_CELL);
+ InsertCells(pTabViewShell, aRequest, pDlg->GetInsCellCmd());
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+
+ InsertCells(pTabViewShell, rReq, eCmd);
+ }
+ break;
+
+ case FID_DELETE_CELL:
+ {
+ DelCellCmd eCmd = DelCellCmd::NONE;
+
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags;
+
+ if( pReqArgs->HasItem( FID_DELETE_CELL, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( !aFlags.isEmpty() )
+ {
+ switch( aFlags[0] )
+ {
+ case 'U': eCmd = DelCellCmd::CellsUp ;break;
+ case 'L': eCmd = DelCellCmd::CellsLeft ;break;
+ case 'R': eCmd = DelCellCmd::Rows ;break;
+ case 'C': eCmd = DelCellCmd::Cols ;break;
+ }
+ }
+ }
+ else
+ {
+ if ( GetViewData().SimpleColMarked() )
+ eCmd = DelCellCmd::Cols;
+ else if ( GetViewData().SimpleRowMarked() )
+ eCmd = DelCellCmd::Rows;
+ else
+ {
+ ScRange aRange;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bTheFlag=GetViewData().IsMultiMarked() ||
+ (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE_FILTERED) ||
+ (rDoc.GetChangeTrack() != nullptr);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScDeleteCellDlg> pDlg(pFact->CreateScDeleteCellDlg( pTabViewShell->GetFrameWeld(), bTheFlag ));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ SfxRequest aRequest(pTabViewShell->GetViewFrame(), FID_INS_CELL);
+ DeleteCells(pTabViewShell, aRequest, pDlg->GetDelCellCmd());
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ DeleteCells(pTabViewShell, rReq, eCmd);
+ }
+ break;
+
+ // delete contents from cells
+
+ case SID_DELETE_CONTENTS:
+ pTabViewShell->DeleteContents( InsertDeleteFlags::CONTENTS );
+ rReq.Done();
+ break;
+
+ case SID_DELETE:
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+
+ if ( pReqArgs!=nullptr && pTabViewShell->SelectionEditable() )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags('A');
+
+ if( pReqArgs->HasItem( SID_DELETE, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ nFlags |= FlagsFromString(aFlags, InsertDeleteFlags::ALL);
+ }
+ else
+ {
+ ScEditableTester aTester( pTabViewShell );
+ if (aTester.IsEditable())
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScDeleteContentsDlg> pDlg(pFact->CreateScDeleteContentsDlg(pTabViewShell->GetFrameWeld()));
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ if ( rDoc.IsTabProtected(nTab) )
+ pDlg->DisableObjects();
+ if (pDlg->Execute() == RET_OK)
+ {
+ nFlags = pDlg->GetDelContentsCmdBits();
+ }
+ }
+ else
+ pTabViewShell->ErrorMessage(aTester.GetMessageId());
+ }
+
+ if( nFlags != InsertDeleteFlags::NONE )
+ {
+ pTabViewShell->DeleteContents( nFlags );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aFlags = FlagsToString( nFlags, InsertDeleteFlags::ALL );
+
+ rReq.AppendItem( SfxStringItem( SID_DELETE, aFlags ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ // fill...
+
+ case FID_FILL_TO_BOTTOM:
+ pTabViewShell->FillSimple( FILL_TO_BOTTOM );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TO_RIGHT:
+ pTabViewShell->FillSimple( FILL_TO_RIGHT );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TO_TOP:
+ pTabViewShell->FillSimple( FILL_TO_TOP );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TO_LEFT:
+ pTabViewShell->FillSimple( FILL_TO_LEFT );
+ rReq.Done();
+ break;
+
+ case FID_FILL_TAB:
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+ ScPasteFunc nFunction = ScPasteFunc::NONE;
+ bool bSkipEmpty = false;
+ bool bAsLink = false;
+
+ if ( pReqArgs!=nullptr && pTabViewShell->SelectionEditable() )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags('A');
+
+ if( pReqArgs->HasItem( FID_FILL_TAB, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ nFlags |= FlagsFromString(aFlags);
+ }
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScInsertContentsDlg> pDlg(pFact->CreateScInsertContentsDlg(pTabViewShell->GetFrameWeld(),
+ new OUString(ScResId(STR_FILL_TAB))));
+ pDlg->SetFillMode(true);
+
+ if (pDlg->Execute() == RET_OK)
+ {
+ nFlags = pDlg->GetInsContentsCmdBits();
+ nFunction = pDlg->GetFormulaCmdBits();
+ bSkipEmpty = pDlg->IsSkipEmptyCells();
+ bAsLink = pDlg->IsLink();
+ // there is no MoveMode with fill tabs
+ }
+ }
+
+ if( nFlags != InsertDeleteFlags::NONE )
+ {
+ pTabViewShell->FillTab( nFlags, nFunction, bSkipEmpty, bAsLink );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aFlags = FlagsToString( nFlags );
+
+ rReq.AppendItem( SfxStringItem( FID_FILL_TAB, aFlags ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case FID_FILL_SERIES:
+ {
+ if (GetViewData().SelectionForbidsCellFill())
+ // Slot should be already disabled, but in case it wasn't
+ // don't even attempt to do the evaluation and popup a
+ // dialog.
+ break;
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ sal_uInt16 nPossDir = FDS_OPT_NONE;
+ FillDir eFillDir = FILL_TO_BOTTOM;
+ FillCmd eFillCmd = FILL_LINEAR;
+ FillDateCmd eFillDateCmd = FILL_DAY;
+ double fStartVal = MAXDOUBLE;
+ double fIncVal = 1;
+ double fMaxVal = MAXDOUBLE;
+ bool bDoIt = false;
+
+ GetViewData().GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ if( nStartCol!=nEndCol )
+ {
+ nPossDir |= FDS_OPT_HORZ;
+ eFillDir=FILL_TO_RIGHT;
+ }
+
+ if( nStartRow!=nEndRow )
+ {
+ nPossDir |= FDS_OPT_VERT;
+ eFillDir=FILL_TO_BOTTOM;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFillDir, aFillCmd, aFillDateCmd;
+ OUString aFillStep, aFillStart, aFillMax;
+ sal_uInt32 nKey;
+ double fTmpVal;
+
+ if( pReqArgs->HasItem( FID_FILL_SERIES, &pItem ) )
+ aFillDir = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ aFillCmd = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ aFillDateCmd = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_3, &pItem ) )
+ aFillStep = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_4, &pItem ) )
+ aFillStart = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if( pReqArgs->HasItem( FN_PARAM_5, &pItem ) )
+ aFillMax = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( !aFillDir.isEmpty() )
+ switch( aFillDir[0] )
+ {
+ case 'B': case 'b': eFillDir=FILL_TO_BOTTOM; break;
+ case 'R': case 'r': eFillDir=FILL_TO_RIGHT; break;
+ case 'T': case 't': eFillDir=FILL_TO_TOP; break;
+ case 'L': case 'l': eFillDir=FILL_TO_LEFT; break;
+ }
+
+ if( !aFillCmd.isEmpty() )
+ switch( aFillCmd[0] )
+ {
+ case 'S': case 's': eFillCmd=FILL_SIMPLE; break;
+ case 'L': case 'l': eFillCmd=FILL_LINEAR; break;
+ case 'G': case 'g': eFillCmd=FILL_GROWTH; break;
+ case 'D': case 'd': eFillCmd=FILL_DATE; break;
+ case 'A': case 'a': eFillCmd=FILL_AUTO; break;
+ }
+
+ if( !aFillDateCmd.isEmpty() )
+ switch( aFillDateCmd[0] )
+ {
+ case 'D': case 'd': eFillDateCmd=FILL_DAY; break;
+ case 'W': case 'w': eFillDateCmd=FILL_WEEKDAY; break;
+ case 'M': case 'm': eFillDateCmd=FILL_MONTH; break;
+ case 'Y': case 'y': eFillDateCmd=FILL_YEAR; break;
+ }
+
+ nKey = 0;
+ if( pFormatter->IsNumberFormat( aFillStart, nKey, fTmpVal ))
+ fStartVal = fTmpVal;
+
+ nKey = 0;
+ if( pFormatter->IsNumberFormat( aFillStep, nKey, fTmpVal ))
+ fIncVal = fTmpVal;
+
+ nKey = 0;
+ if( pFormatter->IsNumberFormat( aFillMax, nKey, fTmpVal ))
+ fMaxVal = fTmpVal;
+
+ bDoIt = true;
+
+ }
+ else // (pReqArgs == nullptr) => raise Dialog
+ {
+ sal_uInt32 nPrivFormat = rDoc.GetNumberFormat( nStartCol, nStartRow, nStartTab );
+ CellType eCellType = rDoc.GetCellType( nStartCol, nStartRow, nStartTab );
+ const SvNumberformat* pPrivEntry = pFormatter->GetEntry( nPrivFormat );
+ const SCSIZE nSelectHeight = nEndRow - nStartRow + 1;
+ const SCSIZE nSelectWidth = nEndCol - nStartCol + 1;
+
+ if (!pPrivEntry)
+ {
+ OSL_FAIL("Numberformat not found !!!");
+ }
+ else
+ {
+ SvNumFormatType nPrivType = pPrivEntry->GetType();
+ if (nPrivType & SvNumFormatType::DATE)
+ {
+ eFillCmd=FILL_DATE;
+ }
+ else if(eCellType==CELLTYPE_STRING)
+ {
+ eFillCmd=FILL_AUTO;
+ }
+ }
+
+ OUString aStartStr;
+
+ // suggest default Startvalue only, when just 1 row or column
+ if ( nStartCol == nEndCol || nStartRow == nEndRow )
+ {
+ double fInputEndVal = 0.0;
+ OUString aEndStr;
+
+ const bool forceSystemLocale = true;
+ aStartStr = rDoc.GetInputString( nStartCol, nStartRow, nStartTab, forceSystemLocale );
+ fStartVal = rDoc.GetValue( nStartCol, nStartRow, nStartTab );
+
+ if(eFillDir==FILL_TO_BOTTOM && nStartRow < nEndRow )
+ {
+ aEndStr = rDoc.GetInputString( nStartCol, nStartRow+1, nStartTab, forceSystemLocale );
+ if(!aEndStr.isEmpty())
+ {
+ fInputEndVal = rDoc.GetValue( nStartCol, nStartRow+1, nStartTab );
+ fIncVal=fInputEndVal-fStartVal;
+ }
+ }
+ else
+ {
+ if(nStartCol < nEndCol)
+ {
+ aEndStr = rDoc.GetInputString( nStartCol+1, nStartRow, nStartTab, forceSystemLocale );
+ if(!aEndStr.isEmpty())
+ {
+ fInputEndVal = rDoc.GetValue( nStartCol+1, nStartRow, nStartTab );
+ fIncVal=fInputEndVal-fStartVal;
+ }
+ }
+ }
+ if(eFillCmd==FILL_DATE)
+ {
+ const Date& rNullDate = rDoc.GetFormatTable()->GetNullDate();
+ Date aStartDate = rNullDate;
+ aStartDate.AddDays(fStartVal);
+ Date aEndDate = rNullDate;
+ aEndDate.AddDays(fInputEndVal);
+ double fTempDate=0;
+
+ if(aStartDate.GetYear()!=aEndDate.GetYear())
+ {
+ eFillDateCmd = FILL_YEAR;
+ fTempDate=aEndDate.GetYear()-aStartDate.GetYear();
+ }
+ if(aStartDate.GetMonth()!=aEndDate.GetMonth())
+ {
+ eFillDateCmd = FILL_MONTH;
+ fTempDate=fTempDate*12+aEndDate.GetMonth()-aStartDate.GetMonth();
+ }
+ if(aStartDate.GetDay()==aEndDate.GetDay())
+ {
+ fIncVal=fTempDate;
+ }
+ }
+ }
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScFillSeriesDlg> pDlg(pFact->CreateScFillSeriesDlg( pTabViewShell->GetFrameWeld(),
+ rDoc,
+ eFillDir, eFillCmd, eFillDateCmd,
+ aStartStr, fIncVal, fMaxVal,
+ nSelectHeight, nSelectWidth, nPossDir));
+
+ if ( nStartCol != nEndCol && nStartRow != nEndRow )
+ {
+ pDlg->SetEdStartValEnabled(false);
+ }
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ eFillDir = pDlg->GetFillDir();
+ eFillCmd = pDlg->GetFillCmd();
+ eFillDateCmd = pDlg->GetFillDateCmd();
+
+ if(eFillCmd==FILL_AUTO)
+ {
+ OUString aStr = pDlg->GetStartStr();
+ if(!aStr.isEmpty())
+ pTabViewShell->EnterData( nStartCol, nStartRow, nStartTab, aStr );
+ }
+ fStartVal = pDlg->GetStart();
+ fIncVal = pDlg->GetStep();
+ fMaxVal = pDlg->GetMax();
+ bDoIt = true;
+ }
+ }
+
+ if( bDoIt )
+ {
+ //nScFillModeMouseModifier = 0; // no Ctrl/Copy
+ pTabViewShell->FillSeries( eFillDir, eFillCmd, eFillDateCmd, fStartVal, fIncVal, fMaxVal );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aPara;
+ const Color* pColor = nullptr;
+
+ switch( eFillDir )
+ {
+ case FILL_TO_BOTTOM: aPara = "B"; break;
+ case FILL_TO_RIGHT: aPara = "R"; break;
+ case FILL_TO_TOP: aPara = "T"; break;
+ case FILL_TO_LEFT: aPara = "L"; break;
+ default: break;
+ }
+ rReq.AppendItem( SfxStringItem( FID_FILL_SERIES, aPara ) );
+
+ switch( eFillCmd )
+ {
+ case FILL_SIMPLE: aPara = "S"; break;
+ case FILL_LINEAR: aPara = "L"; break;
+ case FILL_GROWTH: aPara = "G"; break;
+ case FILL_DATE: aPara = "D"; break;
+ case FILL_AUTO: aPara = "A"; break;
+ default: break;
+ }
+ rReq.AppendItem( SfxStringItem( FN_PARAM_1, aPara ) );
+
+ switch( eFillDateCmd )
+ {
+ case FILL_DAY: aPara = "D"; break;
+ case FILL_WEEKDAY: aPara = "W"; break;
+ case FILL_MONTH: aPara = "M"; break;
+ case FILL_YEAR: aPara = "Y"; break;
+ default: break;
+ }
+ rReq.AppendItem( SfxStringItem( FN_PARAM_2, aPara ) );
+
+ sal_uInt32 nFormatKey = pFormatter->GetStandardFormat(SvNumFormatType::NUMBER,
+ ScGlobal::eLnge );
+
+ pFormatter->GetOutputString( fIncVal, nFormatKey, aPara, &pColor );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_3, aPara ) );
+
+ pFormatter->GetOutputString( fStartVal, nFormatKey, aPara, &pColor );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_4, aPara ) );
+
+ pFormatter->GetOutputString( fMaxVal, nFormatKey, aPara, &pColor );
+ rReq.AppendItem( SfxStringItem( FN_PARAM_5, aPara ) );
+
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case FID_FILL_AUTO:
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+
+ GetViewData().GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
+ SCCOL nFillCol = GetViewData().GetRefEndX();
+ SCROW nFillRow = GetViewData().GetRefEndY();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ if( pReqArgs != nullptr )
+ {
+ if( const SfxStringItem* pItem = pReqArgs->GetItemIfSet( FID_FILL_AUTO ) )
+ {
+ ScAddress aScAddress;
+ OUString aArg = pItem->GetValue();
+
+ if( aScAddress.Parse( aArg, rDoc, rDoc.GetAddressConvention() ) & ScRefFlags::VALID )
+ {
+ nFillRow = aScAddress.Row();
+ nFillCol = aScAddress.Col();
+ }
+ }
+
+ SCTAB nStartTab, nEndTab;
+ GetViewData().GetSimpleArea( nStartCol,nStartRow,nStartTab,
+ nEndCol,nEndRow,nEndTab );
+ }
+ else // call via mouse
+ {
+ // not in a merged cell
+
+ if ( nStartCol == nEndCol && nStartRow == nEndRow )
+ {
+ SCCOL nMergeCol = nStartCol;
+ SCROW nMergeRow = nStartRow;
+ if ( GetViewData().GetDocument().ExtendMerge(
+ nStartCol, nStartRow, nMergeCol, nMergeRow,
+ GetViewData().GetTabNo() ) )
+ {
+ if ( nFillCol >= nStartCol && nFillCol <= nMergeCol && nFillRow == nStartRow )
+ nFillCol = nStartCol;
+ if ( nFillRow >= nStartRow && nFillRow <= nMergeRow && nFillCol == nStartCol )
+ nFillRow = nStartRow;
+ }
+ }
+ }
+
+ if ( nFillCol != nEndCol || nFillRow != nEndRow )
+ {
+ if ( nFillCol==nEndCol || nFillRow==nEndRow )
+ {
+ FillDir eDir = FILL_TO_BOTTOM;
+ SCCOLROW nCount = 0;
+
+ if ( nFillCol==nEndCol )
+ {
+ if ( nFillRow > nEndRow )
+ {
+ eDir = FILL_TO_BOTTOM;
+ nCount = nFillRow - nEndRow;
+ }
+ else if ( nFillRow < nStartRow )
+ {
+ eDir = FILL_TO_TOP;
+ nCount = nStartRow - nFillRow;
+ }
+ }
+ else
+ {
+ if ( nFillCol > nEndCol )
+ {
+ eDir = FILL_TO_RIGHT;
+ nCount = nFillCol - nEndCol;
+ }
+ else if ( nFillCol < nStartCol )
+ {
+ eDir = FILL_TO_LEFT;
+ nCount = nStartCol - nFillCol;
+ }
+ }
+
+ if ( nCount != 0)
+ {
+ pTabViewShell->FillAuto( eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount );
+
+ if( ! rReq.IsAPI() )
+ {
+ ScAddress aAdr( nFillCol, nFillRow, 0 );
+ OUString aAdrStr(aAdr.Format(ScRefFlags::RANGE_ABS, &rDoc, rDoc.GetAddressConvention()));
+
+ rReq.AppendItem( SfxStringItem( FID_FILL_AUTO, aAdrStr ) );
+ rReq.Done();
+ }
+ }
+
+ }
+ else
+ {
+ OSL_FAIL( "Direction not unique for autofill" );
+ }
+ }
+ }
+ break;
+ case FID_FILL_SINGLE_EDIT:
+ ExecuteFillSingleEdit();
+ break;
+ case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
+ {
+ sal_uInt16 nId = ScRandomNumberGeneratorDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_SAMPLING_DIALOG:
+ {
+ sal_uInt16 nId = ScSamplingDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_DESCRIPTIVE_STATISTICS_DIALOG:
+ {
+ sal_uInt16 nId = ScDescriptiveStatisticsDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_ANALYSIS_OF_VARIANCE_DIALOG:
+ {
+ sal_uInt16 nId = ScAnalysisOfVarianceDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_CORRELATION_DIALOG:
+ {
+ sal_uInt16 nId = ScCorrelationDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_COVARIANCE_DIALOG:
+ {
+ sal_uInt16 nId = ScCovarianceDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_EXPONENTIAL_SMOOTHING_DIALOG:
+ {
+ sal_uInt16 nId = ScExponentialSmoothingDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_MOVING_AVERAGE_DIALOG:
+ {
+ sal_uInt16 nId = ScMovingAverageDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_REGRESSION_DIALOG:
+ {
+ sal_uInt16 nId = ScRegressionDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case SID_TTEST_DIALOG:
+ {
+ sal_uInt16 nId = ScTTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_FTEST_DIALOG:
+ {
+ sal_uInt16 nId = ScFTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_ZTEST_DIALOG:
+ {
+ sal_uInt16 nId = ScZTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_CHI_SQUARE_TEST_DIALOG:
+ {
+ sal_uInt16 nId = ScChiSquareTestDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_FOURIER_ANALYSIS_DIALOG:
+ {
+ sal_uInt16 nId = ScFourierAnalysisDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+ case SID_SEARCH_RESULTS_DIALOG:
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if (pReqArgs && pReqArgs->HasItem(SID_SEARCH_RESULTS_DIALOG, &pItem))
+ {
+ bool bVisible = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ // The window ID should equal the slot ID, but not a biggie if it wasn't.
+ sal_uInt16 nId = sc::SearchResultsDlgWrapper::GetChildWindowId();
+ rViewFrm.SetChildWindow(nId, bVisible, false);
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_INSERT_SPARKLINE:
+ case SID_EDIT_SPARKLINE_GROUP:
+ {
+ sal_uInt16 nId = sc::SparklineDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWindow = rViewFrame.GetChildWindow(nId);
+ pScMod->SetRefDialog(nId, pWindow == nullptr);
+ rReq.Done();
+ }
+ break;
+
+ case SID_EDIT_SPARKLINE:
+ {
+ sal_uInt16 nId = sc::SparklineDataRangeDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWindow = rViewFrame.GetChildWindow(nId);
+ pScMod->SetRefDialog(nId, pWindow == nullptr);
+ rReq.Done();
+ }
+ break;
+
+ case SID_DELETE_SPARKLINE:
+ {
+ pTabViewShell->DeleteContents(InsertDeleteFlags::SPARKLINES);
+
+ rReq.Done();
+ }
+ break;
+
+ case SID_DELETE_SPARKLINE_GROUP:
+ {
+ ScRange aMarkRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aMarkRange);
+ if (eMarkType == SC_MARK_SIMPLE)
+ {
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup;
+ if (GetViewData().GetDocument().GetSparklineGroupInRange(aMarkRange, pSparklineGroup) && pSparklineGroup)
+ {
+ GetViewData().GetDocShell()->GetDocFunc().DeleteSparklineGroup(pSparklineGroup, GetViewData().GetTabNo());
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_GROUP_SPARKLINES:
+ {
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScAddress aCursorAddress(GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo());
+ auto pSparkline = GetViewData().GetDocument().GetSparkline(aCursorAddress);
+ if (pSparkline)
+ {
+ auto const& rpSparklineGroup = pSparkline->getSparklineGroup();
+ GetViewData().GetDocShell()->GetDocFunc().GroupSparklines(aRange, rpSparklineGroup);
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_UNGROUP_SPARKLINES:
+ {
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ GetViewData().GetDocShell()->GetDocFunc().UngroupSparklines(aRange);
+ }
+ rReq.Done();
+ }
+ break;
+
+ // disposal (Outlines)
+ // SID_AUTO_OUTLINE, SID_OUTLINE_DELETEALL in Execute (in docsh.idl)
+
+ case SID_OUTLINE_HIDE:
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ pTabViewShell->SetDataPilotDetails( false );
+ else
+ pTabViewShell->HideMarkedOutlines();
+ rReq.Done();
+ break;
+
+ case SID_OUTLINE_SHOW:
+ {
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( pDPObj )
+ {
+ Sequence<sheet::DataPilotFieldFilter> aFilters;
+ css::sheet::DataPilotFieldOrientation nOrientation;
+ if ( pTabViewShell->HasSelectionForDrillDown( nOrientation ) )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScDPShowDetailDlg> pDlg( pFact->CreateScDPShowDetailDlg(
+ pTabViewShell->GetFrameWeld(), *pDPObj, nOrientation ) );
+ if ( pDlg->Execute() == RET_OK )
+ {
+ OUString aNewDimName( pDlg->GetDimensionName() );
+ pTabViewShell->SetDataPilotDetails( true, &aNewDimName );
+ }
+ }
+ else if ( !pDPObj->IsServiceData() &&
+ pDPObj->GetDataFieldPositionData(
+ ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ),
+ aFilters ) )
+ pTabViewShell->ShowDataPilotSourceData( *pDPObj, aFilters );
+ else
+ pTabViewShell->SetDataPilotDetails(true);
+ }
+ else
+ pTabViewShell->ShowMarkedOutlines();
+ rReq.Done();
+ }
+ break;
+
+ case SID_OUTLINE_MAKE:
+ {
+ bool bColumns = false;
+ bool bOk = true;
+
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ ScDPNumGroupInfo aNumInfo;
+ aNumInfo.mbEnable = true;
+ aNumInfo.mbAutoStart = true;
+ aNumInfo.mbAutoEnd = true;
+ sal_Int32 nParts = 0;
+ if ( pTabViewShell->HasSelectionForDateGroup( aNumInfo, nParts ) )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ const Date& rNullDate( GetViewData().GetDocument().GetFormatTable()->GetNullDate() );
+ ScopedVclPtr<AbstractScDPDateGroupDlg> pDlg( pFact->CreateScDPDateGroupDlg(
+ pTabViewShell->GetFrameWeld(),
+ aNumInfo, nParts, rNullDate ) );
+ if( pDlg->Execute() == RET_OK )
+ {
+ aNumInfo = pDlg->GetGroupInfo();
+ pTabViewShell->DateGroupDataPilot( aNumInfo, pDlg->GetDatePart() );
+ }
+ }
+ else if ( pTabViewShell->HasSelectionForNumGroup( aNumInfo ) )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScDPNumGroupDlg> pDlg( pFact->CreateScDPNumGroupDlg(
+ pTabViewShell->GetFrameWeld(), aNumInfo ) );
+ if( pDlg->Execute() == RET_OK )
+ pTabViewShell->NumGroupDataPilot( pDlg->GetGroupInfo() );
+ }
+ else
+ pTabViewShell->GroupDataPilot();
+
+ bOk = false;
+ }
+ else if( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ bOk = false;
+
+ if( pReqArgs->HasItem( SID_OUTLINE_MAKE, &pItem ) )
+ {
+ OUString aCol = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ aCol = aCol.toAsciiUpperCase();
+
+ switch( aCol[0] )
+ {
+ case 'R': bColumns=false; bOk = true;break;
+ case 'C': bColumns=true; bOk = true;break;
+ }
+ }
+ }
+ else // Dialog, when not whole rows/columns are marked
+ {
+ if ( GetViewData().SimpleColMarked() && !GetViewData().SimpleRowMarked() )
+ bColumns = true;
+ else if ( !GetViewData().SimpleColMarked() && GetViewData().SimpleRowMarked() )
+ bColumns = false;
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScGroupDlg> pDlg(pFact->CreateAbstractScGroupDlg(pTabViewShell->GetFrameWeld()));
+
+ pDlg->StartExecuteAsync(
+ [pDlg, pTabViewShell] (sal_Int32 nResult) {
+ if( RET_OK == nResult )
+ {
+ bool bColumn = pDlg->GetColsChecked();
+ pTabViewShell->MakeOutline( bColumn );
+ }
+ pDlg->disposeOnce();
+ }
+ );
+
+ bOk = false;
+ }
+ }
+ if (bOk)
+ {
+ pTabViewShell->MakeOutline( bColumns );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aCol = bColumns ? OUString('C') : OUString('R');
+ rReq.AppendItem( SfxStringItem( SID_OUTLINE_MAKE, aCol ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case SID_OUTLINE_REMOVE:
+ {
+ bool bColumns = false;
+ bool bOk = true;
+
+ if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() ) )
+ {
+ pTabViewShell->UngroupDataPilot();
+ bOk = false;
+ }
+ else if( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ bOk = false;
+
+ if( pReqArgs->HasItem( SID_OUTLINE_REMOVE, &pItem ) )
+ {
+ OUString aCol = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ aCol = aCol.toAsciiUpperCase();
+
+ switch (aCol[0])
+ {
+ case 'R': bColumns=false; bOk = true;break;
+ case 'C': bColumns=true; bOk = true;break;
+ }
+ }
+ }
+ else // Dialog only when removal for rows and columns is possible
+ {
+ bool bColPoss, bRowPoss;
+ pTabViewShell->TestRemoveOutline( bColPoss, bRowPoss );
+ // TODO: handle this case in LOK too
+ if ( bColPoss && bRowPoss && !comphelper::LibreOfficeKit::isActive() )
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScGroupDlg> pDlg(pFact->CreateAbstractScGroupDlg(pTabViewShell->GetFrameWeld(), true));
+
+ pDlg->StartExecuteAsync(
+ [pDlg, pTabViewShell] (sal_Int32 nResult) {
+ if( RET_OK == nResult )
+ {
+ bool bColumn = pDlg->GetColsChecked();
+ pTabViewShell->RemoveOutline( bColumn );
+ }
+ pDlg->disposeOnce();
+ }
+ );
+
+ bOk = false;
+ }
+ else if ( bColPoss )
+ bColumns = true;
+ else if ( bRowPoss )
+ bColumns = false;
+ else
+ bOk = false;
+ }
+ if (bOk)
+ {
+ pTabViewShell->RemoveOutline( bColumns );
+
+ if( ! rReq.IsAPI() )
+ {
+ OUString aCol = bColumns ? OUString('C') : OUString('R');
+ rReq.AppendItem( SfxStringItem( SID_OUTLINE_REMOVE, aCol ) );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ // Clipboard
+
+ case SID_COPY: // for graphs in DrawShell
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ pTabViewShell->CopyToClip( nullptr, false, false, true );
+ rReq.Done();
+ GetViewData().SetPasteMode( ScPasteFlags::Mode | ScPasteFlags::Border );
+ pTabViewShell->ShowCursor();
+ pTabViewShell->UpdateCopySourceOverlay();
+ }
+ break;
+
+ case SID_CUT: // for graphs in DrawShell
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ pTabViewShell->CutToClip();
+ rReq.Done();
+ GetViewData().SetPasteMode( ScPasteFlags::Mode | ScPasteFlags::Border );
+ pTabViewShell->ShowCursor();
+ pTabViewShell->UpdateCopySourceOverlay();
+ }
+ break;
+
+ case SID_PASTE:
+ {
+ ScClipUtil::PasteFromClipboard( GetViewData(), pTabViewShell, true );
+ rReq.Done();
+ }
+ break;
+
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ if (auto pIntItem = dynamic_cast<const SfxUInt32Item*>(pItem) )
+ nFormat = static_cast<SotClipboardFormatId>(pIntItem->GetValue());
+
+ if ( nFormat != SotClipboardFormatId::NONE )
+ {
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(GetViewData().GetActiveWin()));
+ bool bCells = ( ScTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+ bool bDraw = ( ScDrawTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+ bool bOle = ( nFormat == SotClipboardFormatId::EMBED_SOURCE );
+
+ if ( bCells && bOle )
+ pTabViewShell->PasteFromSystem();
+ else if ( bDraw && bOle )
+ pTabViewShell->PasteDraw();
+ else
+ pTabViewShell->PasteFromSystem(nFormat);
+ }
+ //?else
+ //? pTabViewShell->PasteFromSystem();
+
+ rReq.Done();
+ }
+ pTabViewShell->CellContentChanged();
+ break;
+
+ case FID_INS_CELL_CONTENTS:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bOtherDoc = !rDoc.IsClipboardSource();
+ // keep a reference in case the clipboard is changed during dialog or PasteFromClip
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(GetViewData().GetActiveWin()));
+ if ( pOwnClip )
+ {
+ InsertDeleteFlags nFlags = InsertDeleteFlags::NONE;
+ ScPasteFunc nFunction = ScPasteFunc::NONE;
+ InsCellCmd eMoveMode = INS_NONE;
+ bool bSkipEmpty = false;
+ bool bTranspose = false;
+ bool bAsLink = false;
+
+ if ( pReqArgs!=nullptr && pTabViewShell->SelectionEditable() )
+ {
+ const SfxPoolItem* pItem;
+ OUString aFlags('A');
+
+ if( pReqArgs->HasItem( FID_INS_CELL_CONTENTS, &pItem ) )
+ aFlags = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ nFlags |= FlagsFromString(aFlags);
+
+ const SfxUInt16Item* pFuncItem = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1);
+ const SfxBoolItem* pSkipItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_2);
+ const SfxBoolItem* pTransposeItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_3);
+ const SfxBoolItem* pLinkItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_4);
+ const SfxInt16Item* pMoveItem = rReq.GetArg<SfxInt16Item>(FN_PARAM_5);
+ if ( pFuncItem )
+ nFunction = static_cast<ScPasteFunc>(pFuncItem->GetValue());
+ if ( pSkipItem )
+ bSkipEmpty = pSkipItem->GetValue();
+ if ( pTransposeItem )
+ bTranspose = pTransposeItem->GetValue();
+ if ( pLinkItem )
+ bAsLink = pLinkItem->GetValue();
+ if ( pMoveItem )
+ eMoveMode = static_cast<InsCellCmd>(pMoveItem->GetValue());
+ }
+ else
+ {
+ ScEditableTester aTester( pTabViewShell );
+ if (aTester.IsEditable())
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScInsertContentsDlg> pDlg(pFact->CreateScInsertContentsDlg(pTabViewShell->GetFrameWeld()));
+ pDlg->SetOtherDoc( bOtherDoc );
+ // if ChangeTrack MoveMode disable
+ pDlg->SetChangeTrack( rDoc.GetChangeTrack() != nullptr );
+ // fdo#56098 disable shift if necessary
+ if (!bOtherDoc)
+ {
+ ScViewData& rData = GetViewData();
+ if ( rData.GetMarkData().GetTableSelect( rData.GetTabNo() ) )
+ {
+ SCCOL nStartX, nEndX, nClipStartX, nClipSizeX, nRangeSizeX;
+ SCROW nStartY, nEndY, nClipStartY, nClipSizeY, nRangeSizeY;
+ SCTAB nStartTab, nEndTab;
+ pOwnClip->GetDocument()->GetClipStart( nClipStartX, nClipStartY );
+ pOwnClip->GetDocument()->GetClipArea( nClipSizeX, nClipSizeY, true );
+
+ if ( rData.GetSimpleArea( nStartX, nStartY, nStartTab,
+ nEndX, nEndY, nEndTab ) != SC_MARK_SIMPLE ||
+ nStartTab != nEndTab )
+ {
+ // the destination is not a simple range,
+ // assume the destination as the current cell
+ nStartX = nEndX = rData.GetCurX();
+ nStartY = nEndY = rData.GetCurY();
+ nStartTab = rData.GetTabNo();
+ }
+ // we now have clip- and range dimensions
+ // the size of the destination area is the larger of the two
+ nRangeSizeX = nClipSizeX >= nEndX - nStartX ? nClipSizeX : nEndX - nStartX;
+ nRangeSizeY = nClipSizeY >= nEndY - nStartY ? nClipSizeY : nEndY - nStartY;
+ // When the source and destination areas intersect things may go wrong,
+ // especially if the area contains references. This may produce data loss
+ // (e.g. formulas that get wrong references), this scenario _must_ be avoided.
+ ScRange aSource( nClipStartX, nClipStartY, nStartTab,
+ nClipStartX + nClipSizeX, nClipStartY + nClipSizeY, nStartTab );
+ ScRange aDest( nStartX, nStartY, nStartTab,
+ nStartX + nRangeSizeX, nStartY + nRangeSizeY, nStartTab );
+ if ( pOwnClip->GetDocument()->IsCutMode() && aSource.Intersects( aDest ) )
+ pDlg->SetCellShiftDisabled( CellShiftDisabledFlags::Down | CellShiftDisabledFlags::Right );
+ else
+ {
+ //no conflict with intersecting ranges,
+ //check if paste plus shift will fit on sheet
+ //and disable shift-option if no fit
+ CellShiftDisabledFlags nDisableShiftX = CellShiftDisabledFlags::NONE;
+ CellShiftDisabledFlags nDisableShiftY = CellShiftDisabledFlags::NONE;
+
+ //check if horizontal shift will fit
+ if ( !rData.GetDocument().IsBlockEmpty(
+ rDoc.MaxCol() - nRangeSizeX, nStartY,
+ rDoc.MaxCol(), nStartY + nRangeSizeY,
+ nStartTab ) )
+ nDisableShiftX = CellShiftDisabledFlags::Right;
+
+ //check if vertical shift will fit
+ if ( !rData.GetDocument().IsBlockEmpty(
+ nStartX, rDoc.MaxRow() - nRangeSizeY,
+ nStartX + nRangeSizeX, rDoc.MaxRow(),
+ nStartTab ) )
+ nDisableShiftY = CellShiftDisabledFlags::Down;
+
+ if ( nDisableShiftX != CellShiftDisabledFlags::NONE || nDisableShiftY != CellShiftDisabledFlags::NONE)
+ pDlg->SetCellShiftDisabled( nDisableShiftX | nDisableShiftY );
+ }
+ }
+ }
+ if (pDlg->Execute() == RET_OK)
+ {
+ nFlags = pDlg->GetInsContentsCmdBits();
+ nFunction = pDlg->GetFormulaCmdBits();
+ bSkipEmpty = pDlg->IsSkipEmptyCells();
+ bTranspose = pDlg->IsTranspose();
+ bAsLink = pDlg->IsLink();
+ eMoveMode = pDlg->GetMoveMode();
+ }
+ }
+ else
+ pTabViewShell->ErrorMessage(aTester.GetMessageId());
+ }
+
+ if( nFlags != InsertDeleteFlags::NONE )
+ {
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ if ( bAsLink && bOtherDoc )
+ pTabViewShell->PasteFromSystem(SotClipboardFormatId::LINK); // DDE insert
+ else
+ {
+ pTabViewShell->PasteFromClip( nFlags, pOwnClip->GetDocument(),
+ nFunction, bSkipEmpty, bTranspose, bAsLink,
+ eMoveMode, InsertDeleteFlags::NONE, true ); // allow warning dialog
+ }
+ }
+
+ if( !pReqArgs )
+ {
+ OUString aFlags = FlagsToString( nFlags );
+
+ rReq.AppendItem( SfxStringItem( FID_INS_CELL_CONTENTS, aFlags ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_2, bSkipEmpty ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_3, bTranspose ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_4, bAsLink ) );
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nFunction) ) );
+ rReq.AppendItem( SfxInt16Item( FN_PARAM_5, static_cast<sal_Int16>(eMoveMode) ) );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ pTabViewShell->CellContentChanged(); // => PasteFromXXX ???
+ break;
+ case SID_PASTE_ONLY_VALUE:
+ case SID_PASTE_ONLY_TEXT:
+ case SID_PASTE_ONLY_FORMULA:
+ {
+ if ( ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(GetViewData().GetActiveWin())) ) // own cell data
+ {
+ rReq.SetSlot( FID_INS_CELL_CONTENTS );
+ OUString aFlags;
+ if ( nSlot == SID_PASTE_ONLY_VALUE )
+ aFlags = "V";
+ else if ( nSlot == SID_PASTE_ONLY_TEXT )
+ aFlags = "S";
+ else
+ aFlags = "F";
+ rReq.AppendItem( SfxStringItem( FID_INS_CELL_CONTENTS, aFlags ) );
+ ExecuteSlot( rReq, GetInterface() );
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ pTabViewShell->CellContentChanged();
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ break;
+ }
+ case SID_PASTE_TRANSPOSED:
+ {
+ if (ScTransferObj::GetOwnClipboard(
+ ScTabViewShell::GetClipData(GetViewData().GetActiveWin()))) // own cell data
+ {
+ rReq.SetSlot(FID_INS_CELL_CONTENTS);
+ // By default content (values/numbers, strings, formulas and dates),
+ // attributes and notes are pasted
+ rReq.AppendItem(SfxBoolItem(FN_PARAM_3, true)); // transpose
+ ExecuteSlot(rReq, GetInterface());
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ pTabViewShell->CellContentChanged();
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ break;
+ }
+ case SID_PASTE_AS_LINK:
+ {
+ if (ScTransferObj::GetOwnClipboard(
+ ScTabViewShell::GetClipData(GetViewData().GetActiveWin()))) // own cell data
+ {
+ rReq.SetSlot(FID_INS_CELL_CONTENTS);
+ // paste links to values/numbers, strings, formulas and dates
+ // do not paste attributes, notes and objects
+ rReq.AppendItem(SfxStringItem(FID_INS_CELL_CONTENTS, "VSFD"));
+ rReq.AppendItem(SfxBoolItem(FN_PARAM_4, true)); // as link
+ ExecuteSlot(rReq, GetInterface());
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ pTabViewShell->CellContentChanged();
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ break;
+ }
+ case SID_PASTE_TEXTIMPORT_DIALOG:
+ {
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard(pWin));
+ const uno::Reference<datatransfer::XTransferable>& xTransferable
+ = aDataHelper.GetTransferable();
+ SotClipboardFormatId format = SotClipboardFormatId::STRING;
+ bool bSuccess = false;
+ if (xTransferable.is() && HasClipboardFormat(format))
+ {
+ OUString sStrBuffer;
+ bSuccess = aDataHelper.GetString(format, sStrBuffer);
+ if (bSuccess)
+ {
+ auto pStrm = std::make_shared<ScImportStringStream>(sStrBuffer);
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScImportAsciiDlg> pDlg(pFact->CreateScImportAsciiDlg(
+ pWin ? pWin->GetFrameWeld() : nullptr, OUString(), pStrm.get(), SC_PASTETEXT));
+ ScRange aRange;
+ SCCOL nPosX = 0;
+ SCROW nPosY = 0;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ nPosX = aRange.aStart.Col();
+ nPosY = aRange.aStart.Row();
+ }
+ else
+ {
+ nPosX = GetViewData().GetCurX();
+ nPosY = GetViewData().GetCurY();
+ }
+ ScAddress aCellPos(nPosX, nPosY, GetViewData().GetTabNo());
+ auto pObj = std::make_shared<ScImportExport>(GetViewData().GetDocument(), aCellPos);
+ pObj->SetOverwriting(true);
+ if (pDlg->Execute()) {
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions(aOptions);
+ pDlg->SaveParameters();
+ pObj->SetExtOptions(aOptions);
+ pObj->ImportString(sStrBuffer, format);
+ }
+ pDlg->disposeOnce();
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success, 0 = fail
+ rReq.Done();
+ }
+ }
+ if (!bSuccess)
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ rReq.Ignore();
+ }
+ }
+ break;
+ case SID_PASTE_SPECIAL:
+ // differentiate between own cell data and draw objects/external data
+ // this makes FID_INS_CELL_CONTENTS superfluous
+ {
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(pWin));
+
+ // Clipboard-ID given as parameter? Basic "PasteSpecial(Format)"
+ const SfxPoolItem* pItem=nullptr;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET &&
+ dynamic_cast<const SfxUInt32Item*>( pItem) != nullptr )
+ {
+ SotClipboardFormatId nFormat = static_cast<SotClipboardFormatId>(static_cast<const SfxUInt32Item*>(pItem)->GetValue());
+ bool bRet=true;
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ bool bDraw = ( ScDrawTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+ if ( bDraw && nFormat == SotClipboardFormatId::EMBED_SOURCE )
+ pTabViewShell->PasteDraw();
+ else
+ bRet = pTabViewShell->PasteFromSystem(nFormat, true); // TRUE: no error messages
+ }
+
+ if ( bRet )
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success, 0 = fail
+ rReq.Done();
+ }
+ else
+ // if format is not available -> fallback to request without parameters
+ pItem = nullptr;
+ }
+
+ if ( !pItem )
+ {
+ if ( ScTransferObj::GetOwnClipboard(xTransferable) ) // own cell data
+ {
+ rReq.SetSlot( FID_INS_CELL_CONTENTS );
+ ExecuteSlot( rReq, GetInterface() );
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ }
+ else // draw objects or external data
+ {
+ bool bDraw = ( ScDrawTransferObj::GetOwnClipboard(xTransferable) != nullptr );
+
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ GetPossibleClipboardFormats( aFormats );
+
+ sal_uInt16 nFormatCount = aFormats.Count();
+ if ( nFormatCount )
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(pTabViewShell->GetFrameWeld()));
+ for (sal_uInt16 i=0; i<nFormatCount; i++)
+ {
+ SotClipboardFormatId nFormatId = aFormats.GetClipbrdFormatId( i );
+ OUString aName = aFormats.GetClipbrdFormatName( i );
+ // special case for paste dialog: '*' is replaced by object type
+ if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE )
+ aName = "*";
+ pDlg->Insert( nFormatId, aName );
+ }
+
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ auto xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ const OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:PasteTextImportDialog", aModuleName);
+ OUString sLabel(vcl::CommandInfoProvider::GetTooltipLabelForCommand(aProperties));
+ pDlg->InsertUno(".uno:PasteTextImportDialog", sLabel);
+
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+ SotClipboardFormatId nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() );
+ if (nFormat != SotClipboardFormatId::NONE)
+ {
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+ if ( bDraw && nFormat == SotClipboardFormatId::EMBED_SOURCE )
+ pTabViewShell->PasteDraw();
+ else
+ pTabViewShell->PasteFromSystem(nFormat);
+ }
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ rReq.AppendItem( SfxUInt32Item( nSlot, static_cast<sal_uInt32>(nFormat) ) );
+ rReq.Done();
+ }
+ else
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ rReq.Ignore();
+ }
+ }
+ else
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ }
+ }
+ }
+ pTabViewShell->CellContentChanged(); // => PasteFromSystem() ???
+ break;
+
+ case SID_PASTE_UNFORMATTED:
+ // differentiate between own cell data and draw objects/external data
+ // this makes FID_INS_CELL_CONTENTS superfluous
+ {
+ weld::WaitObject aWait( GetViewData().GetDialogParent() );
+
+ // we should differentiate between SotClipboardFormatId::STRING and SotClipboardFormatId::STRING_TSVC,
+ // and paste the SotClipboardFormatId::STRING_TSVC if it is available.
+ // Which makes a difference if the clipboard contains cells with embedded line breaks.
+
+ SotClipboardFormatId nFormat = HasClipboardFormat( SotClipboardFormatId::STRING_TSVC) ?
+ SotClipboardFormatId::STRING_TSVC : SotClipboardFormatId::STRING;
+
+ const bool bRet = pTabViewShell->PasteFromSystem(nFormat, true); // TRUE: no error messages
+ if ( bRet )
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 1)); // 1 = success
+ rReq.Done();
+ }
+ else
+ {
+ rReq.SetReturnValue(SfxInt16Item(nSlot, 0)); // 0 = fail
+ }
+
+ pTabViewShell->CellContentChanged(); // => PasteFromSystem() ???
+ }
+ break;
+
+ // other
+
+ case FID_INS_ROWBRK:
+ pTabViewShell->InsertPageBreak( false );
+ rReq.Done();
+ break;
+
+ case FID_INS_COLBRK:
+ pTabViewShell->InsertPageBreak( true );
+ rReq.Done();
+ break;
+
+ case FID_DEL_ROWBRK:
+ pTabViewShell->DeletePageBreak( false );
+ rReq.Done();
+ break;
+
+ case FID_DEL_COLBRK:
+ pTabViewShell->DeletePageBreak( true );
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_ADD_PRED:
+ pTabViewShell->DetectiveAddPred();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_DEL_PRED:
+ pTabViewShell->DetectiveDelPred();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_ADD_SUCC:
+ pTabViewShell->DetectiveAddSucc();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_DEL_SUCC:
+ pTabViewShell->DetectiveDelSucc();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_ADD_ERR:
+ pTabViewShell->DetectiveAddError();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_INVALID:
+ pTabViewShell->DetectiveMarkInvalid();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_REFRESH:
+ pTabViewShell->DetectiveRefresh();
+ rReq.Done();
+ break;
+
+ case SID_DETECTIVE_MARK_PRED:
+ pTabViewShell->DetectiveMarkPred();
+ break;
+ case SID_DETECTIVE_MARK_SUCC:
+ pTabViewShell->DetectiveMarkSucc();
+ break;
+ case SID_INSERT_CURRENT_DATE:
+ pTabViewShell->InsertCurrentTime(
+ SvNumFormatType::DATE, ScResId(STR_UNDO_INSERT_CURRENT_DATE));
+ break;
+ case SID_INSERT_CURRENT_TIME:
+ pTabViewShell->InsertCurrentTime(
+ SvNumFormatType::TIME, ScResId(STR_UNDO_INSERT_CURRENT_TIME));
+ break;
+
+ case SID_SPELL_DIALOG:
+ {
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ if( rReq.GetArgs() )
+ rViewFrame.SetChildWindow( SID_SPELL_DIALOG,
+ static_cast< const SfxBoolItem& >( rReq.GetArgs()->
+ Get( SID_SPELL_DIALOG ) ).GetValue() );
+ else
+ rViewFrame.ToggleChildWindow( SID_SPELL_DIALOG );
+
+ rViewFrame.GetBindings().Invalidate( SID_SPELL_DIALOG );
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_HANGUL_HANJA_CONVERSION:
+ pTabViewShell->DoHangulHanjaConversion();
+ break;
+
+ case SID_CHINESE_CONVERSION:
+ {
+ //open ChineseTranslationDialog
+ Reference< XComponentContext > xContext(
+ ::cppu::defaultBootstrap_InitialComponentContext() ); //@todo get context from calc if that has one
+ if(xContext.is())
+ {
+ Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() );
+ if(xMCF.is())
+ {
+ Reference< ui::dialogs::XExecutableDialog > xDialog(
+ xMCF->createInstanceWithContext(
+ "com.sun.star.linguistic2.ChineseTranslationDialog"
+ , xContext),
+ UNO_QUERY);
+ Reference< lang::XInitialization > xInit( xDialog, UNO_QUERY );
+ if( xInit.is() )
+ {
+ // initialize dialog
+ uno::Sequence<uno::Any> aSeq(comphelper::InitAnyPropertySequence(
+ {
+ {"ParentWindow", uno::Any(Reference< awt::XWindow >())}
+ }));
+ xInit->initialize( aSeq );
+
+ //execute dialog
+ sal_Int16 nDialogRet = xDialog->execute();
+ if( RET_OK == nDialogRet )
+ {
+ //get some parameters from the dialog
+ bool bToSimplified = true;
+ bool bUseVariants = true;
+ bool bCommonTerms = true;
+ Reference< beans::XPropertySet > xProp( xDialog, UNO_QUERY );
+ if( xProp.is() )
+ {
+ try
+ {
+ xProp->getPropertyValue("IsDirectionToSimplified") >>= bToSimplified;
+ xProp->getPropertyValue("IsUseCharacterVariants") >>= bUseVariants;
+ xProp->getPropertyValue("IsTranslateCommonTerms") >>= bCommonTerms;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ //execute translation
+ LanguageType eSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED;
+ LanguageType eTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL;
+ sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0;
+ if( !bCommonTerms )
+ nOptions |= i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+
+ vcl::Font aTargetFont = OutputDevice::GetDefaultFont(
+ DefaultFontType::CJK_SPREADSHEET,
+ eTargetLang, GetDefaultFontFlags::OnlyOne );
+ ScConversionParam aConvParam( SC_CONVERSION_CHINESE_TRANSL,
+ eSourceLang, eTargetLang, std::move(aTargetFont), nOptions, false );
+ pTabViewShell->DoSheetConversion( aConvParam );
+ }
+ }
+ Reference< lang::XComponent > xComponent( xDialog, UNO_QUERY );
+ if( xComponent.is() )
+ xComponent->dispose();
+ }
+ }
+ }
+ break;
+
+ case SID_CONVERT_FORMULA_TO_VALUE:
+ {
+ pTabViewShell->ConvertFormulaToValue();
+ }
+ break;
+ case SID_THESAURUS:
+ pTabViewShell->DoThesaurus();
+ break;
+
+ case SID_TOGGLE_REL:
+ pTabViewShell->DoRefConversion();
+ break;
+
+ case SID_DEC_INDENT:
+ pTabViewShell->ChangeIndent( false );
+ break;
+ case SID_INC_INDENT:
+ pTabViewShell->ChangeIndent( true );
+ break;
+
+ case FID_USE_NAME:
+ {
+ CreateNameFlags nFlags = pTabViewShell->GetCreateNameFlags();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNameCreateDlg> pDlg(pFact->CreateScNameCreateDlg(pTabViewShell->GetFrameWeld(), nFlags));
+
+ if( pDlg->Execute() )
+ {
+ pTabViewShell->CreateNames(pDlg->GetFlags());
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_CONSOLIDATE:
+ {
+ const ScConsolidateItem* pItem;
+ if ( pReqArgs && (pItem =
+ pReqArgs->GetItemIfSet( SCITEM_CONSOLIDATEDATA )) )
+ {
+ const ScConsolidateParam& rParam = pItem->GetData();
+
+ pTabViewShell->Consolidate( rParam );
+ GetViewData().GetDocument().SetConsolidateDlgData( std::unique_ptr<ScConsolidateParam>(new ScConsolidateParam(rParam)) );
+
+ rReq.Done();
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if (rReq.IsAPI())
+ SbxBase::SetError(ERRCODE_BASIC_BAD_PARAMETER);
+#endif
+ }
+ break;
+
+ case SID_INS_FUNCTION:
+ {
+ const SfxBoolItem* pOkItem = static_cast<const SfxBoolItem*>(&pReqArgs->Get( SID_DLG_RETOK ));
+
+ if ( pOkItem->GetValue() ) // OK
+ {
+ OUString aFormula;
+ const SfxStringItem* pSItem = &pReqArgs->Get( SCITEM_STRING );
+ const SfxBoolItem* pMatrixItem = static_cast<const SfxBoolItem*>(&pReqArgs->Get( SID_DLG_MATRIX ));
+
+ aFormula += pSItem->GetValue();
+ pScMod->ActivateInputWindow( &aFormula, pMatrixItem->GetValue() );
+ }
+ else // CANCEL
+ {
+ pScMod->ActivateInputWindow();
+ }
+ rReq.Ignore(); // only SID_ENTER_STRING is recorded
+ }
+ break;
+
+ case FID_DEFINE_NAME:
+ case FID_DEFINE_CURRENT_NAME:
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ OUString aName, aSymbol, aAttrib;
+
+ if( pReqArgs->HasItem( FID_DEFINE_NAME, &pItem ) )
+ aName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ aSymbol = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ aAttrib = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if ( !aName.isEmpty() && !aSymbol.isEmpty() )
+ {
+ if (pTabViewShell->InsertName( aName, aSymbol, aAttrib ))
+ rReq.Done();
+#if HAVE_FEATURE_SCRIPTING
+ else
+ SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); // Basic-error
+#endif
+ }
+ }
+ else
+ {
+ sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+ case FID_ADD_NAME:
+ {
+ sal_uInt16 nId = ScNameDefDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_OPENDLG_CONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ case SID_OPENDLG_COLORSCALE:
+ case SID_OPENDLG_DATABAR:
+ case SID_OPENDLG_ICONSET:
+ case SID_OPENDLG_CONDDATE:
+ {
+ sal_uInt32 nIndex = sal_uInt32(-1);
+ bool bManaged = false;
+
+ // Get the pool item stored by Conditional Format Manager Dialog.
+ auto itemsRange = pTabViewShell->GetPool().GetItemSurrogates(SCITEM_CONDFORMATDLGDATA);
+ if (itemsRange.begin() != itemsRange.end())
+ {
+ const ScCondFormatDlgItem* pDlgItem = static_cast<const ScCondFormatDlgItem*>(*itemsRange.begin());
+ nIndex = pDlgItem->GetIndex();
+ bManaged = true;
+ }
+
+ // Check if the Conditional Manager Dialog is editing or adding
+ // conditional format item.
+ if ( bManaged )
+ {
+ sal_uInt16 nId = ScCondFormatDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ break;
+ }
+
+ ScRangeList aRangeList;
+ ScViewData& rData = GetViewData();
+ rData.GetMarkData().FillRangeListWithMarks(&aRangeList, false);
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if(rDoc.IsTabProtected(rData.GetTabNo()))
+ {
+ pTabViewShell->ErrorMessage( STR_ERR_CONDFORMAT_PROTECTED );
+ break;
+ }
+
+ ScAddress aPos(rData.GetCurX(), rData.GetCurY(), rData.GetTabNo());
+ if(aRangeList.empty())
+ {
+ aRangeList.push_back(ScRange(aPos));
+ }
+
+ // try to find an existing conditional format
+ const ScConditionalFormat* pCondFormat = nullptr;
+ const ScPatternAttr* pPattern = rDoc.GetPattern(aPos.Col(), aPos.Row(), aPos.Tab());
+ ScConditionalFormatList* pList = rDoc.GetCondFormList(aPos.Tab());
+ const ScCondFormatIndexes& rCondFormats = pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData();
+ bool bContainsCondFormat = !rCondFormats.empty();
+ bool bCondFormatDlg = false;
+ bool bContainsExistingCondFormat = false;
+ if(bContainsCondFormat)
+ {
+ for (const auto& rCondFormat : rCondFormats)
+ {
+ // check if at least one existing conditional format has the same range
+ pCondFormat = pList->GetFormat(rCondFormat);
+ if(!pCondFormat)
+ continue;
+
+ bContainsExistingCondFormat = true;
+ const ScRangeList& rCondFormatRange = pCondFormat->GetRange();
+ if(rCondFormatRange == aRangeList)
+ {
+ // found a matching range, edit this conditional format
+ bCondFormatDlg = true;
+ nIndex = pCondFormat->GetKey();
+ break;
+ }
+ }
+ }
+
+ // do we have a parameter with the conditional formatting type?
+ const SfxInt16Item* pParam = rReq.GetArg<SfxInt16Item>(FN_PARAM_1);
+ if (pParam)
+ {
+ auto pFormat = std::make_unique<ScConditionalFormat>(0, &rDoc);
+ pFormat->SetRange(aRangeList);
+
+ if (nSlot == SID_OPENDLG_ICONSET)
+ {
+ ScIconSetType eIconSetType = limit_cast<ScIconSetType>(pParam->GetValue(), IconSet_3Arrows, IconSet_5Boxes);
+ const int nSteps = ScIconSetFormat::getIconSetElements(eIconSetType);
+
+ ScIconSetFormat* pEntry = new ScIconSetFormat(&rDoc);
+ ScIconSetFormatData* pIconSetFormatData = new ScIconSetFormatData(eIconSetType);
+
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(0, COL_RED, COLORSCALE_PERCENT));
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(100. / nSteps), COL_BROWN, COLORSCALE_PERCENT));
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(200. / nSteps), COL_YELLOW, COLORSCALE_PERCENT));
+ if (nSteps > 3)
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(300. / nSteps), COL_WHITE, COLORSCALE_PERCENT));
+ if (nSteps > 4)
+ pIconSetFormatData->m_Entries.emplace_back(new ScColorScaleEntry(round(400. / nSteps), COL_GREEN, COLORSCALE_PERCENT));
+
+ pEntry->SetIconSetData(pIconSetFormatData);
+ pFormat->AddEntry(pEntry);
+ }
+ else if (nSlot == SID_OPENDLG_COLORSCALE)
+ {
+ typedef std::tuple<double, Color, ScColorScaleEntryType> ScaleEntry;
+ static std::vector<std::vector<ScaleEntry>> aScaleThemes =
+ {
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX },
+ { 50, Color(0xFFEB84), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX },
+ { 50, Color(0xFFEB84), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0x5A8AC6), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0x5A8AC6), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX },
+ { 50, Color(0xFCFCFF), COLORSCALE_PERCENTILE }
+ },
+ {
+ { 0, Color(0xF8696B), COLORSCALE_MIN },
+ { 0, Color(0xFCFCFF), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0xFCFCFF), COLORSCALE_MIN },
+ { 0, Color(0xF8696B), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xFCFCFF), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0xFCFCFF), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0x63BE7B), COLORSCALE_MIN },
+ { 0, Color(0xFFEF9C), COLORSCALE_MAX }
+ },
+ {
+ { 0, Color(0xFFEF9C), COLORSCALE_MIN },
+ { 0, Color(0x63BE7B), COLORSCALE_MAX }
+ }
+ };
+
+ sal_uInt16 nTheme = pParam->GetValue();
+ if (nTheme < aScaleThemes.size())
+ {
+ ScColorScaleFormat* pFormatEntry = new ScColorScaleFormat(&rDoc);
+
+ auto& aTheme = aScaleThemes[nTheme];
+
+ ScColorScaleEntry* pMin = new ScColorScaleEntry(std::get<0>(aTheme[0]), std::get<1>(aTheme[0]), std::get<2>(aTheme[0]));
+ ScColorScaleEntry* pMax = new ScColorScaleEntry(std::get<0>(aTheme[1]), std::get<1>(aTheme[1]), std::get<2>(aTheme[1]));
+
+ pFormatEntry->AddEntry(pMin);
+
+ // COLORSCALE_PERCENTILE has to be in the middle
+ if (aTheme.size() > 2)
+ {
+ ScColorScaleEntry* pPer = new ScColorScaleEntry(std::get<0>(aTheme[2]), std::get<1>(aTheme[2]), std::get<2>(aTheme[2]));
+ pFormatEntry->AddEntry(pPer);
+ }
+
+ pFormatEntry->AddEntry(pMax);
+
+ pFormat->AddEntry(pFormatEntry);
+ }
+
+ }
+ else if (nSlot == SID_OPENDLG_DATABAR)
+ {
+ typedef std::tuple<Color, bool> DatabarEntry;
+ static std::vector<DatabarEntry> aDatabarThemes =
+ {
+ { Color(0x638EC6), true },
+ { Color(0x63C384), true },
+ { Color(0xFF555A), true },
+ { Color(0xFFB628), true },
+ { Color(0x008AEF), true },
+ { Color(0xD6007B), true },
+ { Color(0x638EC6), false },
+ { Color(0x63C384), false },
+ { Color(0xFF555A), false },
+ { Color(0xFFB628), false },
+ { Color(0x008AEF), false },
+ { Color(0xD6007B), false }
+ };
+
+ sal_uInt16 nTheme = pParam->GetValue();
+ if (nTheme < aDatabarThemes.size())
+ {
+ ScDataBarFormat* pFormatEntry = new ScDataBarFormat(&rDoc);
+
+ auto& aTheme = aDatabarThemes[nTheme];
+
+ ScDataBarFormatData* pData = new ScDataBarFormatData();
+ pData->maPositiveColor = std::get<0>(aTheme);
+ pData->mbGradient = std::get<1>(aTheme);
+ pData->mxNegativeColor = Color(0xFF0000);
+ pData->mpLowerLimit.reset(new ScColorScaleEntry(0, 0, COLORSCALE_AUTO));
+ pData->mpUpperLimit.reset(new ScColorScaleEntry(0, 0, COLORSCALE_AUTO));
+
+ pFormatEntry->SetDataBarData(pData);
+
+ pFormat->AddEntry(pFormatEntry);
+ }
+ }
+
+ // use the new conditional formatting
+ GetViewData().GetDocShell()->GetDocFunc().ReplaceConditionalFormat(nIndex, std::move(pFormat), aPos.Tab(), aRangeList);
+
+ break;
+ }
+
+ // if not found a conditional format ask whether we should edit one of the existing
+ // or should create a new overlapping conditional format
+ if(bContainsCondFormat && !bCondFormatDlg && bContainsExistingCondFormat)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_EDIT_EXISTING_COND_FORMATS)));
+ xQueryBox->set_default_response(RET_YES);
+ bool bEditExisting = xQueryBox->run() == RET_YES;
+ if (bEditExisting)
+ {
+ // differentiate between ranges where one conditional format is defined
+ // and several formats are defined
+ // if we have only one => open the cond format dlg to edit it
+ // otherwise open the manage cond format dlg
+ if (rCondFormats.size() == 1)
+ {
+ pCondFormat = pList->GetFormat(rCondFormats[0]);
+ assert(pCondFormat);
+ nIndex = pCondFormat->GetKey();
+ bCondFormatDlg = true;
+ }
+ else
+ {
+ // Queue message to open Conditional Format Manager Dialog.
+ GetViewData().GetDispatcher().Execute( SID_OPENDLG_CONDFRMT_MANAGER, SfxCallMode::ASYNCHRON );
+ break;
+ }
+ }
+ else
+ {
+ // define an overlapping conditional format
+ pCondFormat = pList->GetFormat(rCondFormats[0]);
+ assert(pCondFormat);
+ bCondFormatDlg = true;
+ }
+ }
+
+ condformat::dialog::ScCondFormatDialogType eType = condformat::dialog::NONE;
+ switch(nSlot)
+ {
+ case SID_OPENDLG_CONDFRMT:
+ case SID_OPENDLG_CURRENTCONDFRMT:
+ eType = condformat::dialog::CONDITION;
+ break;
+ case SID_OPENDLG_COLORSCALE:
+ eType = condformat::dialog::COLORSCALE;
+ break;
+ case SID_OPENDLG_DATABAR:
+ eType = condformat::dialog::DATABAR;
+ break;
+ case SID_OPENDLG_ICONSET:
+ eType = condformat::dialog::ICONSET;
+ break;
+ case SID_OPENDLG_CONDDATE:
+ eType = condformat::dialog::DATE;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+
+ if(bCondFormatDlg || !bContainsCondFormat)
+ {
+ // Put the xml string parameter to initialize the
+ // Conditional Format Dialog.
+ ScCondFormatDlgItem aDlgItem(nullptr, nIndex, false);
+ aDlgItem.SetDialogType(eType);
+ pTabViewShell->GetPool().DirectPutItemInPool(aDlgItem);
+
+ sal_uInt16 nId = ScCondFormatDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ }
+ break;
+
+ case SID_DEFINE_COLROWNAMERANGES:
+ {
+
+ sal_uInt16 nId = ScColRowNameRangesDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+
+ case SID_UPDATECHART:
+ {
+ bool bAll = false;
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+
+ if( pReqArgs->HasItem( SID_UPDATECHART, &pItem ) )
+ bAll = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+
+ pTabViewShell->UpdateCharts( bAll );
+
+ if( ! rReq.IsAPI() )
+ {
+ rReq.AppendItem( SfxBoolItem( SID_UPDATECHART, bAll ) );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_TABOP:
+ if (pReqArgs)
+ {
+ const ScTabOpItem& rItem =
+ static_cast<const ScTabOpItem&>(
+ pReqArgs->Get( SID_TABOP ));
+
+ pTabViewShell->TabOp( rItem.GetData() );
+
+ rReq.Done( *pReqArgs );
+ }
+ break;
+
+ case SID_SOLVE:
+ if (pReqArgs)
+ {
+ const ScSolveItem& rItem =
+ pReqArgs->Get( SCITEM_SOLVEDATA );
+
+ pTabViewShell->Solve( rItem.GetData() );
+
+ rReq.Done( *pReqArgs );
+ }
+ break;
+
+ case FID_INSERT_NAME:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNamePasteDlg> pDlg(pFact->CreateScNamePasteDlg(pTabViewShell->GetFrameWeld(), GetViewData().GetDocShell()));
+ switch( pDlg->Execute() )
+ {
+ case BTN_PASTE_LIST:
+ pTabViewShell->InsertNameList();
+ break;
+ case BTN_PASTE_NAME:
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pTabViewShell );
+ if (pHdl)
+ {
+ // "=" in KeyEvent, switches to input-mode
+ (void)pScMod->InputKeyEvent( KeyEvent('=', vcl::KeyCode()) );
+
+ std::vector<OUString> aNames = pDlg->GetSelectedNames();
+ if (!aNames.empty())
+ {
+ OUStringBuffer aBuffer;
+ for (const auto& rName : aNames)
+ {
+ aBuffer.append(rName + " ");
+ }
+ pHdl->InsertFunction( aBuffer.makeStringAndClear(), false ); // without "()"
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case SID_RANGE_NOTETEXT:
+ if (pReqArgs)
+ {
+ const SfxStringItem& rTextItem = pReqArgs->Get( SID_RANGE_NOTETEXT );
+
+ // always cursor position
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ pTabViewShell->SetNoteText( aPos, rTextItem.GetValue() );
+ rReq.Done();
+ }
+ break;
+
+ case SID_INSERT_POSTIT:
+ case SID_EDIT_POSTIT:
+ {
+ const SvxPostItTextItem* pTextItem;
+ if ( pReqArgs && (pTextItem = pReqArgs->GetItemIfSet( SID_ATTR_POSTIT_TEXT )) )
+ {
+ OUString aCellId;
+ // SID_ATTR_POSTIT_ID only argument for SID_EDIT_POSTIT
+ if (const SvxPostItIdItem* pCellId = pReqArgs->GetItemIfSet( SID_ATTR_POSTIT_ID ))
+ aCellId = pCellId->GetValue();
+
+ const SvxPostItAuthorItem* pAuthorItem = pReqArgs->GetItem( SID_ATTR_POSTIT_AUTHOR );
+ const SvxPostItDateItem* pDateItem = pReqArgs->GetItem( SID_ATTR_POSTIT_DATE );
+
+ if (!aCellId.isEmpty())
+ {
+ SetTabNoAndCursor( GetViewData(), aCellId );
+ }
+
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ pTabViewShell->ReplaceNote( aPos, pTextItem->GetValue(),
+ pAuthorItem ? &pAuthorItem->GetValue() : nullptr,
+ pDateItem ? &pDateItem->GetValue() : nullptr );
+ }
+ else if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations())
+ {
+ pTabViewShell->EditNote(); // note object to edit
+ }
+ rReq.Done();
+ }
+ break;
+
+ case FID_NOTE_VISIBLE:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if( ScPostIt* pNote = rDoc.GetNote(aPos) )
+ {
+ bool bShow;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && (pReqArgs->GetItemState( FID_NOTE_VISIBLE, true, &pItem ) == SfxItemState::SET) )
+ bShow = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ else
+ bShow = !pNote->IsCaptionShown();
+
+ pTabViewShell->ShowNote( bShow );
+
+ if (!pReqArgs)
+ rReq.AppendItem( SfxBoolItem( FID_NOTE_VISIBLE, bShow ) );
+
+ rReq.Done();
+ rBindings.Invalidate( FID_NOTE_VISIBLE );
+ }
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case FID_HIDE_NOTE:
+ case FID_SHOW_NOTE:
+ {
+ bool bShowNote = nSlot == FID_SHOW_NOTE;
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData& rMark = rData.GetMarkData();
+
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ {
+ // Check current cell
+ ScAddress aPos( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+ if( rDoc.GetNote(aPos) )
+ {
+ rData.GetDocShell()->GetDocFunc().ShowNote( aPos, bShowNote );
+ }
+ }
+ else
+ {
+ // Check selection range
+ bool bDone = false;
+ ScRangeListRef aRangesRef;
+ rData.GetMultiArea(aRangesRef);
+ const ScRangeList aRanges = *aRangesRef;
+
+ OUString aUndo = ScResId( bShowNote ? STR_UNDO_SHOWNOTE : STR_UNDO_HIDENOTE );
+ rData.GetDocShell()->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ // get notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(rTab, aNotes);
+
+ for (const sc::NoteEntry& rNote : aNotes)
+ {
+ // check if note is in our selection range
+ const ScAddress& rAdr = rNote.maPos;
+ const ScRange* rRange = aRanges.Find(rAdr);
+ if (! rRange)
+ continue;
+
+ // check if cell is editable
+ const SCTAB nRangeTab = rRange->aStart.Tab();
+ if (rDoc.IsBlockEditable( nRangeTab, rAdr.Col(), rAdr.Row(), rAdr.Col(), rAdr.Row() ))
+ {
+ rData.GetDocShell()->GetDocFunc().ShowNote( rAdr, bShowNote );
+ bDone = true;
+ }
+ }
+ }
+
+ rData.GetDocShell()->GetUndoManager()->LeaveListAction();
+
+ if ( bDone )
+ {
+ rReq.Done();
+ rBindings.Invalidate( nSlot );
+ }
+ else
+ rReq.Ignore();
+ }
+
+ }
+ break;
+
+ case FID_SHOW_ALL_NOTES:
+ case FID_HIDE_ALL_NOTES:
+ {
+ bool bShowNote = nSlot == FID_SHOW_ALL_NOTES;
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ std::vector<sc::NoteEntry> aNotes;
+
+ OUString aUndo = ScResId( bShowNote ? STR_UNDO_SHOWALLNOTES : STR_UNDO_HIDEALLNOTES );
+ rData.GetDocShell()->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ rDoc.GetAllNoteEntries(rTab, aNotes);
+ }
+
+ for (const sc::NoteEntry& rNote : aNotes)
+ {
+ const ScAddress& rAdr = rNote.maPos;
+ rData.GetDocShell()->GetDocFunc().ShowNote( rAdr, bShowNote );
+ }
+
+ rData.GetDocShell()->GetUndoManager()->LeaveListAction();
+ }
+ break;
+
+ case SID_TOGGLE_NOTES:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScRangeList aRanges;
+ std::vector<sc::NoteEntry> aNotes;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ aRanges.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab));
+
+ CommentCaptionState eState = rDoc.GetAllNoteCaptionsState( aRanges );
+ rDoc.GetNotesInRange(aRanges, aNotes);
+ bool bShowNote = (eState == ALLHIDDEN || eState == MIXED);
+
+ OUString aUndo = ScResId( bShowNote ? STR_UNDO_SHOWALLNOTES : STR_UNDO_HIDEALLNOTES );
+ rData.GetDocShell()->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ for(const auto& rNote : aNotes)
+ {
+ const ScAddress& rAdr = rNote.maPos;
+ rData.GetDocShell()->GetDocFunc().ShowNote( rAdr, bShowNote );
+ }
+
+ rData.GetDocShell()->GetUndoManager()->LeaveListAction();
+
+ if (!pReqArgs)
+ rReq.AppendItem( SfxBoolItem( SID_TOGGLE_NOTES, bShowNote ) );
+
+ rReq.Done();
+ rBindings.Invalidate( SID_TOGGLE_NOTES );
+ }
+ break;
+
+ case SID_DELETE_NOTE:
+ {
+ const SvxPostItIdItem* pIdItem;
+ // If Id is mentioned, select the appropriate cell first
+ if ( pReqArgs && (pIdItem = pReqArgs->GetItemIfSet( SID_ATTR_POSTIT_ID )) )
+ {
+ const OUString& aCellId = pIdItem->GetValue();
+ if (!aCellId.isEmpty())
+ {
+ SetTabNoAndCursor( GetViewData(), aCellId );
+ }
+ }
+
+ pTabViewShell->DeleteContents( InsertDeleteFlags::NOTE ); // delete all notes in selection
+ rReq.Done();
+ }
+ break;
+
+ case FID_DELETE_ALL_NOTES:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData aNewMark(rDoc.GetSheetLimits());
+ ScRangeList aRangeList;
+
+ for (auto const& rTab : rMark.GetSelectedTabs())
+ {
+ aRangeList.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab));
+ }
+
+ aNewMark.MarkFromRangeList( aRangeList, true );
+ rData.GetDocShell()->GetDocFunc().DeleteContents(aNewMark, InsertDeleteFlags::NOTE, true, false );
+ }
+ break;
+
+ case SID_CHARMAP:
+ if( pReqArgs != nullptr )
+ {
+ OUString aChars, aFontName;
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem = nullptr;
+ if ( pArgs )
+ pArgs->GetItemState(SID_CHARMAP, false, &pItem);
+ if ( pItem )
+ {
+ const SfxStringItem* pStringItem = dynamic_cast<const SfxStringItem*>( pItem );
+ if ( pStringItem )
+ aChars = pStringItem->GetValue();
+ const SfxStringItem* pFontItem =
+ pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false);
+ if ( pFontItem )
+ aFontName = pFontItem->GetValue();
+ }
+
+ if ( !aChars.isEmpty() )
+ {
+ vcl::Font aFont;
+ pTabViewShell->GetSelectionPattern()->fillFontOnly(aFont, nullptr, nullptr, nullptr,
+ pTabViewShell->GetSelectionScriptType() );
+ if ( !aFontName.isEmpty() )
+ aFont = vcl::Font( aFontName, Size(1,1) );
+ pTabViewShell->InsertSpecialChar( aChars, aFont );
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ }
+ else
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+
+ // font color doesn't matter here
+ vcl::Font aCurFont;
+ pTabViewShell->GetSelectionPattern()->fillFontOnly(aCurFont, nullptr, nullptr, nullptr,
+ pTabViewShell->GetSelectionScriptType());
+
+ SfxAllItemSet aSet( GetPool() );
+ aSet.Put( SfxBoolItem( FN_PARAM_1, false ) );
+ aSet.Put( SvxFontItem( aCurFont.GetFamilyType(), aCurFont.GetFamilyName(), aCurFont.GetStyleName(), aCurFont.GetPitch(), aCurFont.GetCharSet(), GetPool().GetWhich(SID_ATTR_CHAR_FONT) ) );
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ auto xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(pTabViewShell->GetFrameWeld(), aSet, xFrame));
+ pDlg->Execute();
+ }
+ break;
+
+ case SID_SELECT_SCENARIO:
+ {
+ // Testing
+
+ if ( pReqArgs )
+ {
+ const SfxStringItem& rItem = pReqArgs->Get(SID_SELECT_SCENARIO);
+ pTabViewShell->UseScenario(rItem.GetValue());
+ //! why should the return value be valid?!?!
+ rReq.SetReturnValue(SfxStringItem(SID_SELECT_SCENARIO, rItem.GetValue()));
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_HYPERLINK_SETLINK:
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( SID_HYPERLINK_SETLINK, &pItem ) )
+ {
+ const SvxHyperlinkItem* pHyper = static_cast<const SvxHyperlinkItem*>(pItem);
+ const OUString& rName = pHyper->GetName();
+ const OUString& rURL = pHyper->GetURL();
+ const OUString& rTarget = pHyper->GetTargetFrame();
+ sal_uInt16 nType = static_cast<sal_uInt16>(pHyper->GetInsertMode());
+
+ pTabViewShell->InsertURL( rName, rURL, rTarget, nType );
+ rReq.Done();
+ }
+ else
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_OPENDLG_CONDFRMT_MANAGER:
+ case SID_OPENDLG_CURRENTCONDFRMT_MANAGER:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ if (rDoc.IsTabProtected(rData.GetTabNo()))
+ {
+ pTabViewShell->ErrorMessage( STR_ERR_CONDFORMAT_PROTECTED );
+ break;
+ }
+
+ ScAddress aPos(rData.GetCurX(), rData.GetCurY(), rData.GetTabNo());
+
+ ScConditionalFormatList* pList = nullptr;
+
+ const ScCondFormatDlgItem* pDlgItem = nullptr;
+ auto itemsRange = pTabViewShell->GetPool().GetItemSurrogates(SCITEM_CONDFORMATDLGDATA);
+ if (itemsRange.begin() != itemsRange.end())
+ {
+ pDlgItem= static_cast<const ScCondFormatDlgItem*>(*itemsRange.begin());
+ pList = const_cast<ScCondFormatDlgItem*>(pDlgItem)->GetConditionalFormatList();
+ }
+
+ if (!pList)
+ pList = rDoc.GetCondFormList( aPos.Tab() );
+
+ VclPtr<AbstractScCondFormatManagerDlg> pDlg(pFact->CreateScCondFormatMgrDlg(
+ pTabViewShell->GetFrameWeld(), rDoc, pList));
+
+ if (pDlgItem)
+ pDlg->SetModified();
+
+ pDlg->StartExecuteAsync([this, pDlg, &rData, pTabViewShell, pDlgItem, aPos](sal_Int32 nRet){
+ std::unique_ptr<ScConditionalFormatList> pCondFormatList = pDlg->GetConditionalFormatList();
+ if(nRet == RET_OK && pDlg->CondFormatsChanged())
+ {
+ rData.GetDocShell()->GetDocFunc().SetConditionalFormatList(pCondFormatList.release(), aPos.Tab());
+ }
+ else if(nRet == DLG_RET_ADD)
+ {
+ // Put the xml string parameter to initialize the
+ // Conditional Format Dialog. ( add new )
+ pTabViewShell->GetPool().DirectPutItemInPool(ScCondFormatDlgItem(
+ std::shared_ptr<ScConditionalFormatList>(pCondFormatList.release()), -1, true));
+ // Queue message to open Conditional Format Dialog
+ GetViewData().GetDispatcher().Execute( SID_OPENDLG_CONDFRMT, SfxCallMode::ASYNCHRON );
+ }
+ else if (nRet == DLG_RET_EDIT)
+ {
+ ScConditionalFormat* pFormat = pDlg->GetCondFormatSelected();
+ sal_Int32 nIndex = pFormat ? pFormat->GetKey() : -1;
+ // Put the xml string parameter to initialize the
+ // Conditional Format Dialog. ( edit selected conditional format )
+ pTabViewShell->GetPool().DirectPutItemInPool(ScCondFormatDlgItem(
+ std::shared_ptr<ScConditionalFormatList>(pCondFormatList.release()), nIndex, true));
+
+ // Queue message to open Conditional Format Dialog
+ GetViewData().GetDispatcher().Execute( SID_OPENDLG_CONDFRMT, SfxCallMode::ASYNCHRON );
+ }
+ else
+ pCondFormatList.reset();
+
+ if (pDlgItem)
+ pTabViewShell->GetPool().DirectRemoveItemFromPool(*pDlgItem);
+
+ pDlg->disposeOnce();
+ });
+ }
+ break;
+
+ case SID_EXTERNAL_SOURCE:
+ {
+ const SfxStringItem* pFile = rReq.GetArg<SfxStringItem>(SID_FILE_NAME);
+ const SfxStringItem* pSource = rReq.GetArg<SfxStringItem>(FN_PARAM_1);
+ if ( pFile && pSource )
+ {
+ OUString aFile;
+ OUString aFilter;
+ OUString aOptions;
+ OUString aSource;
+ sal_Int32 nRefreshDelaySeconds=0;
+
+ aFile = pFile->GetValue();
+ aSource = pSource->GetValue();
+ const SfxStringItem* pFilter = rReq.GetArg<SfxStringItem>(SID_FILTER_NAME);
+ if ( pFilter )
+ aFilter = pFilter->GetValue();
+ const SfxStringItem* pOptions = rReq.GetArg<SfxStringItem>(SID_FILE_FILTEROPTIONS);
+ if ( pOptions )
+ aOptions = pOptions->GetValue();
+ const SfxUInt32Item* pRefresh = rReq.GetArg<SfxUInt32Item>(FN_PARAM_2);
+ if ( pRefresh )
+ nRefreshDelaySeconds = pRefresh->GetValue();
+
+ ExecuteExternalSource( aFile, aFilter, aOptions, aSource, nRefreshDelaySeconds, rReq );
+ }
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ pImpl->m_pLinkedDlg.disposeAndClear();
+ pImpl->m_pLinkedDlg =
+ pFact->CreateScLinkedAreaDlg(pTabViewShell->GetFrameWeld());
+ delete pImpl->m_pRequest;
+ pImpl->m_pRequest = new SfxRequest( rReq );
+ OUString sFile, sFilter, sOptions, sSource;
+ sal_Int32 nRefreshDelaySeconds = 0;
+ if (pImpl->m_pLinkedDlg->Execute() == RET_OK)
+ {
+ sFile = pImpl->m_pLinkedDlg->GetURL();
+ sFilter = pImpl->m_pLinkedDlg->GetFilter();
+ sOptions = pImpl->m_pLinkedDlg->GetOptions();
+ sSource = pImpl->m_pLinkedDlg->GetSource();
+ nRefreshDelaySeconds = pImpl->m_pLinkedDlg->GetRefreshDelaySeconds();
+ if ( !sFile.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( SID_FILE_NAME, sFile ) );
+ if ( !sFilter.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( SID_FILTER_NAME, sFilter ) );
+ if ( !sOptions.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( SID_FILE_FILTEROPTIONS, sOptions ) );
+ if ( !sSource.isEmpty() )
+ pImpl->m_pRequest->AppendItem( SfxStringItem( FN_PARAM_1, sSource ) );
+ if ( nRefreshDelaySeconds )
+ pImpl->m_pRequest->AppendItem( SfxUInt32Item( FN_PARAM_2, nRefreshDelaySeconds ) );
+ }
+
+ ExecuteExternalSource( sFile, sFilter, sOptions, sSource, nRefreshDelaySeconds, *(pImpl->m_pRequest) );
+ }
+ }
+ break;
+
+ case SID_AUTO_SUM:
+ {
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const OUString sFunction = pArgs ?
+ static_cast<const SfxStringItem&>( pArgs->Get( SID_AUTO_SUM ) ).GetValue()
+ : "";
+
+ OpCode eFunction = ocSum;
+ if (sFunction == "average")
+ eFunction = ocAverage;
+ else if (sFunction == "count")
+ eFunction = ocCount;
+ else if (sFunction == "min")
+ eFunction = ocMin;
+ if (sFunction == "max")
+ eFunction = ocMax;
+
+ bool bSubTotal = false;
+ bool bRangeFinder = false;
+ const OUString aFormula = pTabViewShell->DoAutoSum( bRangeFinder, bSubTotal , eFunction );
+ if ( !aFormula.isEmpty() )
+ {
+ const sal_Int32 nPar = aFormula.indexOf( '(' );
+ const sal_Int32 nLen = aFormula.getLength();
+ ScInputHandler* pHdl = pScMod->GetInputHdl( pTabViewShell );
+
+ if ( pHdl && nPar != -1 )
+ {
+ if ( !pScMod->IsEditMode() )
+ {
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ }
+
+ EditView *pEditView=pHdl->GetActiveView();
+ if ( pEditView )
+ {
+ ESelection aTextSel = pEditView->GetSelection();
+ aTextSel.nStartPos = 0;
+ aTextSel.nEndPos = EE_TEXTPOS_ALL;
+ pHdl->DataChanging();
+ pEditView->SetSelection(aTextSel);
+ pEditView->InsertText(aFormula);
+ pEditView->SetSelection( bRangeFinder ? ESelection( 0, nPar + ( bSubTotal ? 3 : 1 ), 0, nLen - 1 ) : ESelection( 0, nLen - 1, 0, nLen - 1 ) );
+ pHdl->DataChanged();
+
+ if ( bRangeFinder )
+ {
+ pHdl->InitRangeFinder( aFormula );
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_SELECT_UNPROTECTED_CELLS:
+ {
+ ScViewData& rData = GetViewData();
+ SCTAB aTab = rData.GetTabNo();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+ ScRangeList rRangeList;
+
+ rDoc.GetUnprotectedCells(rRangeList, aTab);
+ rMark.MarkFromRangeList(rRangeList, true);
+ pTabViewShell->SetMarkData(rMark);
+ }
+ break;
+
+ case SID_SELECT_VISIBLE_ROWS:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ rMark.MarkToMulti();
+
+ const ScRange& aMultiArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiArea.aStart.Col();
+ SCROW nStartRow = aMultiArea.aStart.Row();
+ SCCOL nEndCol = aMultiArea.aEnd.Col();
+ SCROW nEndRow = aMultiArea.aEnd.Row();
+
+ bool bChanged = false;
+ for (const SCTAB& nTab : rMark)
+ {
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ {
+ rMark.SetMultiMarkArea(
+ ScRange(nStartCol, nRow, nTab, nEndCol, nLastRow, nTab), false);
+ bChanged = true;
+ nRow = nLastRow;
+ }
+ }
+ }
+
+ if (bChanged && !rMark.HasAnyMultiMarks())
+ rMark.ResetMark();
+
+ rMark.MarkToSimple();
+
+ pTabViewShell->SelectionChanged();
+ }
+ break;
+
+ case SID_SELECT_VISIBLE_COLUMNS:
+ {
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ rMark.MarkToMulti();
+
+ const ScRange& aMultiArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiArea.aStart.Col();
+ SCROW nStartRow = aMultiArea.aStart.Row();
+ SCCOL nEndCol = aMultiArea.aEnd.Col();
+ SCROW nEndRow = aMultiArea.aEnd.Row();
+
+ bool bChanged = false;
+ for (const SCTAB& nTab : rMark)
+ {
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
+ {
+ SCCOL nLastCol = nCol;
+ if (rDoc.ColHidden(nCol, nTab, nullptr, &nLastCol))
+ {
+ rMark.SetMultiMarkArea(
+ ScRange(nCol, nStartRow, nTab, nLastCol, nEndRow, nTab), false);
+ bChanged = true;
+ nCol = nLastCol;
+ }
+ }
+ }
+
+ if (bChanged && !rMark.HasAnyMultiMarks())
+ rMark.ResetMark();
+
+ rMark.MarkToSimple();
+
+ pTabViewShell->SelectionChanged();
+ }
+ break;
+
+ case SID_CURRENT_FORMULA_RANGE:
+ {
+ const SfxInt32Item* param1 = rReq.GetArg<SfxInt32Item>(FN_PARAM_1);
+ SCCOL colStart = param1 ? param1->GetValue() : 0;
+
+ const SfxInt32Item* param2 = rReq.GetArg<SfxInt32Item>(FN_PARAM_2);
+ SCROW rowStart = param2 ? param2->GetValue() : 0;
+
+ const SfxInt32Item* param3 = rReq.GetArg<SfxInt32Item>(FN_PARAM_3);
+ SCCOL colEnd = param3 ? param3->GetValue() : 0;
+
+ const SfxInt32Item* param4 = rReq.GetArg<SfxInt32Item>(FN_PARAM_4);
+ SCROW rowEnd = param4 ? param4->GetValue() : 0;
+
+ const SfxInt32Item* param5 = rReq.GetArg<SfxInt32Item>(FN_PARAM_5);
+ SCROW table = param5 ? param5->GetValue() : 0;
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+
+ if (param3 && param4 && pInputHdl)
+ {
+ ScViewData& rData = pTabViewShell->GetViewData();
+ ScTabView* pTabView = rData.GetView();
+
+ if (param1 && param2)
+ rData.SetRefStart(colStart, rowStart, table);
+
+ pTabView->UpdateRef( colEnd, rowEnd, table ); // setup the end & refresh formula
+
+ ScRange aRef(
+ colStart, rowStart, rData.GetRefStartZ(),
+ colEnd, rowEnd, rData.GetRefEndZ() );
+ SC_MOD()->SetReference( aRef, rData.GetDocument(), &rData.GetMarkData() );
+
+ pInputHdl->UpdateLokReferenceMarks();
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("incorrect slot in ExecuteEdit");
+ break;
+ }
+}
+
+void ScCellShell::ExecuteTrans( SfxRequest& rReq )
+{
+ TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() );
+ if ( nType != TransliterationFlags::NONE )
+ {
+ GetViewData().GetView()->TransliterateText( nType );
+ rReq.Done();
+ }
+}
+
+void ScCellShell::ExecuteRotateTrans( const SfxRequest& rReq )
+{
+ if( rReq.GetSlot() == SID_TRANSLITERATE_ROTATE_CASE )
+ GetViewData().GetView()->TransliterateText( m_aRotateCase.getNextMode() );
+}
+
+void ScCellShell::ExecuteExternalSource(
+ const OUString& _rFile, const OUString& _rFilter, const OUString& _rOptions,
+ const OUString& _rSource, sal_Int32 _nRefreshDelaySeconds, SfxRequest& _rRequest )
+{
+ if ( !_rFile.isEmpty() && !_rSource.isEmpty() ) // filter may be empty
+ {
+ ScRange aLinkRange;
+ bool bMove = false;
+
+ ScViewData& rData = GetViewData();
+ ScMarkData& rMark = rData.GetMarkData();
+ rMark.MarkToSimple();
+ if ( rMark.IsMarked() )
+ {
+ aLinkRange = rMark.GetMarkArea();
+ bMove = true; // insert/delete cells to fit range
+ }
+ else
+ aLinkRange = ScRange( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+
+ rData.GetDocFunc().InsertAreaLink( _rFile, _rFilter, _rOptions, _rSource,
+ aLinkRange, _nRefreshDelaySeconds, bMove, false );
+ _rRequest.Done();
+ }
+ else
+ _rRequest.Ignore();
+}
+
+namespace {
+
+bool isDPSourceValid(const ScDPObject& rDPObj)
+{
+ if (rDPObj.IsImportData())
+ {
+ // If the data type is database, check if the database is still valid.
+ const ScImportSourceDesc* pDesc = rDPObj.GetImportSourceDesc();
+ if (!pDesc)
+ return false;
+
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ const ScDPDimensionSaveData* pDimData = nullptr;
+ if (pSaveData)
+ pDimData = pSaveData->GetExistingDimensionData();
+
+ const ScDPCache* pCache = pDesc->CreateCache(pDimData);
+ if (!pCache)
+ // cache creation failed, probably due to invalid connection.
+ return false;
+ }
+ return true;
+}
+
+void RunPivotLayoutDialog(ScModule* pScMod,
+ ScTabViewShell* pTabViewShell,
+ std::unique_ptr<ScDPObject>& pNewDPObject)
+{
+ bool bHadNewDPObject = pNewDPObject != nullptr;
+ pTabViewShell->SetDialogDPObject( std::move(pNewDPObject) );
+ if ( bHadNewDPObject )
+ {
+ // start layout dialog
+
+ sal_uInt16 nId = ScPivotLayoutWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+}
+
+void SetupRangeForPivotTableDialog(const ScRange& rRange,
+ ScAddress& rDestPos,
+ ScDocument* pDoc,
+ TranslateId pSrcErrorId,
+ std::unique_ptr<ScDPObject>& pNewDPObject)
+{
+ ScSheetSourceDesc aShtDesc(pDoc);
+ aShtDesc.SetSourceRange(rRange);
+ pSrcErrorId = aShtDesc.CheckSourceRange();
+ if (!pSrcErrorId)
+ {
+ pNewDPObject.reset(new ScDPObject(pDoc));
+ pNewDPObject->SetSheetDesc( aShtDesc );
+ }
+
+ // output below source data
+ if ( rRange.aEnd.Row()+2 <= pDoc->MaxRow() - 4 )
+ rDestPos = ScAddress( rRange.aStart.Col(),
+ rRange.aEnd.Row()+2,
+ rRange.aStart.Tab() );
+}
+
+void ErrorOrRunPivotLayoutDialog(TranslateId pSrcErrorId,
+ const ScAddress& rDestPos,
+ ScModule* pScMod,
+ ScTabViewShell* pTabViewShell,
+ std::unique_ptr<ScDPObject>& pNewDPObject)
+{
+ if (pSrcErrorId)
+ {
+ // Error occurred during data creation. Launch an error and bail out.
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(pSrcErrorId)));
+ xInfoBox->run();
+ return;
+ }
+
+ if ( pNewDPObject )
+ pNewDPObject->SetOutRange( rDestPos );
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+}
+
+}
+
+void ScCellShell::ExecuteDataPilotDialog()
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScViewData& rData = GetViewData();
+ ScDocument& rDoc = rData.GetDocument();
+
+ // ScPivot is no longer used...
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(
+ rData.GetCurX(), rData.GetCurY(),
+ rData.GetTabNo() );
+ if ( pDPObj ) // on an existing table?
+ {
+ std::unique_ptr<ScDPObject> pNewDPObject;
+
+ if (isDPSourceValid(*pDPObj))
+ pNewDPObject.reset(new ScDPObject(*pDPObj));
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+ }
+ else // create new table
+ {
+ // select database range or data
+ pTabViewShell->GetDBData( true, SC_DB_OLD );
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ pTabViewShell->MarkDataArea( false );
+
+ // output to cursor position for non-sheet data
+ ScAddress aDestPos( rData.GetCurX(), rData.GetCurY(),
+ rData.GetTabNo() );
+
+ // first select type of source data
+
+ bool bEnableExt = ScDPObject::HasRegisteredSources();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScDataPilotSourceTypeDlg> pTypeDlg(
+ pFact->CreateScDataPilotSourceTypeDlg(
+ pTabViewShell->GetFrameWeld(), bEnableExt));
+
+ // Populate named ranges (if any).
+ ScRangeName* pRangeName = rDoc.GetRangeName();
+ if (pRangeName)
+ {
+ ScRangeName::const_iterator itr = pRangeName->begin(), itrEnd = pRangeName->end();
+ for (; itr != itrEnd; ++itr)
+ pTypeDlg->AppendNamedRange(itr->second->GetName());
+ }
+
+ pTypeDlg->StartExecuteAsync([this, pTypeDlg, pTabViewShell,
+ pScMod, pFact, &rDoc, &rMark, aDestPos](int nResult) mutable {
+
+ if (nResult == RET_OK )
+ {
+ if ( pTypeDlg->IsExternal() )
+ {
+ std::vector<OUString> aSources = ScDPObject::GetRegisteredSources();
+ VclPtr<AbstractScDataPilotServiceDlg> pServDlg(
+ pFact->CreateScDataPilotServiceDlg(
+ pTabViewShell->GetFrameWeld(), aSources));
+
+ pServDlg->StartExecuteAsync([pServDlg, pScMod, pTabViewShell,
+ aDestPos, &rDoc](int nResult2) mutable {
+ if ( nResult2 == RET_OK )
+ {
+ ScDPServiceDesc aServDesc(
+ pServDlg->GetServiceName(),
+ pServDlg->GetParSource(),
+ pServDlg->GetParName(),
+ pServDlg->GetParUser(),
+ pServDlg->GetParPass() );
+ std::unique_ptr<ScDPObject> pNewDPObject(new ScDPObject(&rDoc));
+ pNewDPObject->SetServiceData( aServDesc );
+ pNewDPObject->SetOutRange(aDestPos);
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+ }
+
+ pServDlg->disposeOnce();
+ });
+ }
+ else if ( pTypeDlg->IsDatabase() )
+ {
+ assert(pFact && "ScAbstractFactory create fail!");
+ VclPtr<AbstractScDataPilotDatabaseDlg> pDataDlg(
+ pFact->CreateScDataPilotDatabaseDlg(pTabViewShell->GetFrameWeld()));
+ assert(pDataDlg && "Dialog create fail!");
+
+ pDataDlg->StartExecuteAsync([pDataDlg, pScMod, pTabViewShell,
+ aDestPos, &rDoc](int nResult2) mutable {
+ if ( nResult2 == RET_OK )
+ {
+ ScImportSourceDesc aImpDesc(&rDoc);
+ pDataDlg->GetValues( aImpDesc );
+ std::unique_ptr<ScDPObject> pNewDPObject(new ScDPObject(&rDoc));
+ pNewDPObject->SetImportDesc( aImpDesc );
+ pNewDPObject->SetOutRange(aDestPos);
+
+ RunPivotLayoutDialog(pScMod, pTabViewShell, pNewDPObject);
+ }
+
+ pDataDlg->disposeOnce();
+ });
+ }
+ else
+ {
+ TranslateId pSrcErrorId;
+
+ if (pTypeDlg->IsNamedRange())
+ {
+ std::unique_ptr<ScDPObject> pNewDPObject;
+ OUString aName = pTypeDlg->GetSelectedNamedRange();
+ ScSheetSourceDesc aShtDesc(&rDoc);
+ aShtDesc.SetRangeName(aName);
+ pSrcErrorId = aShtDesc.CheckSourceRange();
+ if (!pSrcErrorId)
+ {
+ pNewDPObject.reset(new ScDPObject(&rDoc));
+ pNewDPObject->SetSheetDesc(aShtDesc);
+ }
+
+ ErrorOrRunPivotLayoutDialog(pSrcErrorId, aDestPos, pScMod, pTabViewShell, pNewDPObject);
+ }
+ else // selection
+ {
+ //! use database ranges (select before type dialog?)
+ ScRange aRange;
+ ScMarkType eType = GetViewData().GetSimpleArea(aRange);
+ if ( (eType & SC_MARK_SIMPLE) == SC_MARK_SIMPLE )
+ {
+ ScDocument* pDoc = &rDoc;
+
+ // Shrink the range to the data area.
+ SCCOL nStartCol = aRange.aStart.Col(), nEndCol = aRange.aEnd.Col();
+ SCROW nStartRow = aRange.aStart.Row(), nEndRow = aRange.aEnd.Row();
+ if (rDoc.ShrinkToDataArea(aRange.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow))
+ {
+ aRange.aStart.SetCol(nStartCol);
+ aRange.aStart.SetRow(nStartRow);
+ aRange.aEnd.SetCol(nEndCol);
+ aRange.aEnd.SetRow(nEndRow);
+ rMark.SetMarkArea(aRange);
+ pTabViewShell->MarkRange(aRange);
+ }
+
+ if ( rDoc.HasSubTotalCells( aRange ) )
+ {
+ // confirm selection if it contains SubTotal cells
+ std::shared_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_DATAPILOT_SUBTOTAL)));
+ xQueryBox->set_default_response(RET_YES);
+ xQueryBox->runAsync(xQueryBox, [aRange, pDoc, pTypeDlg, aDestPos,
+ pScMod, pTabViewShell, pSrcErrorId] (int nResult2) mutable {
+ if (nResult2 == RET_NO)
+ return;
+
+ std::unique_ptr<ScDPObject> pNewDPObject;
+ SetupRangeForPivotTableDialog(aRange, aDestPos, pDoc, pSrcErrorId, pNewDPObject);
+ ErrorOrRunPivotLayoutDialog(pSrcErrorId, aDestPos, pScMod, pTabViewShell, pNewDPObject);
+ });
+
+ pTypeDlg->disposeOnce();
+ return;
+ }
+
+ std::unique_ptr<ScDPObject> pNewDPObject;
+ SetupRangeForPivotTableDialog(aRange, aDestPos, pDoc, pSrcErrorId, pNewDPObject);
+ ErrorOrRunPivotLayoutDialog(pSrcErrorId, aDestPos, pScMod, pTabViewShell, pNewDPObject);
+ }
+ }
+ }
+ }
+
+ pTypeDlg->disposeOnce();
+ });
+ }
+}
+
+void ScCellShell::ExecuteXMLSourceDialog()
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ if (!pTabViewShell)
+ return;
+
+ ScModule* pScMod = SC_MOD();
+
+ sal_uInt16 nId = ScXMLSourceDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrame.GetChildWindow(nId);
+ pScMod->SetRefDialog(nId, pWnd == nullptr);
+}
+
+void ScCellShell::ExecuteSubtotals(SfxRequest& rReq)
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ pTabViewShell->DoSubTotals( pArgs->Get( SCITEM_SUBTDATA ).
+ GetSubTotalData() );
+ rReq.Done();
+ return;
+ }
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg;
+ ScSubTotalParam aSubTotalParam;
+ SfxItemSetFixed<SCITEM_SUBTDATA, SCITEM_SUBTDATA> aArgSet( GetPool() );
+
+ bool bAnonymous;
+
+ // Only get existing named database range.
+ ScDBData* pDBData = pTabViewShell->GetDBData(true, SC_DB_OLD);
+ if (pDBData)
+ bAnonymous = false;
+ else
+ {
+ // No existing DB data at this position. Create an
+ // anonymous DB.
+ bAnonymous = true;
+ pDBData = pTabViewShell->GetAnonymousDBData();
+ ScRange aDataRange;
+ pDBData->GetArea(aDataRange);
+ pTabViewShell->MarkRange(aDataRange, false);
+ }
+
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ aSubTotalParam.bRemoveOnly = false;
+ if (bAnonymous)
+ {
+ // Preset sort formatting along with values and also create formula
+ // cells with "needs formatting". Subtotals on data of different types
+ // doesn't make much sense anyway.
+ aSubTotalParam.bIncludePattern = true;
+ }
+
+ aArgSet.Put( ScSubTotalItem( SCITEM_SUBTDATA, &GetViewData(), &aSubTotalParam ) );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ pDlg.disposeAndReset(pFact->CreateScSubTotalDlg(pTabViewShell->GetFrameWeld(), aArgSet));
+ pDlg->SetCurPageId("1stgroup");
+
+ short bResult = pDlg->Execute();
+
+ if ( (bResult == RET_OK) || (bResult == SCRET_REMOVE) )
+ {
+ const SfxItemSet* pOutSet = nullptr;
+
+ if ( bResult == RET_OK )
+ {
+ pOutSet = pDlg->GetOutputItemSet();
+ aSubTotalParam =
+ pOutSet->Get( SCITEM_SUBTDATA ).GetSubTotalData();
+ }
+ else // if (bResult == SCRET_REMOVE)
+ {
+ pOutSet = &aArgSet;
+ aSubTotalParam.bRemoveOnly = true;
+ aSubTotalParam.bReplace = true;
+ aArgSet.Put( ScSubTotalItem( SCITEM_SUBTDATA,
+ &GetViewData(),
+ &aSubTotalParam ) );
+ }
+
+ pTabViewShell->DoSubTotals( aSubTotalParam );
+ rReq.Done( *pOutSet );
+ }
+ else
+ GetViewData().GetDocShell()->CancelAutoDBRange();
+}
+
+void ScCellShell::ExecuteFillSingleEdit()
+{
+ ScAddress aCurPos = GetViewData().GetCurPos();
+
+ OUString aInit;
+
+ if (aCurPos.Row() > 0)
+ {
+ // Get the initial text value from the above cell.
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScAddress aPrevPos = aCurPos;
+ aPrevPos.IncRow(-1);
+ ScRefCellValue aCell(rDoc, aPrevPos);
+
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ aInit = "=";
+ const ScTokenArray* pCode = aCell.getFormula()->GetCode();
+ sc::TokenStringContext aCxt(rDoc, rDoc.GetGrammar());
+ aInit += pCode->CreateString(aCxt, aCurPos);
+ }
+ else
+ aInit = aCell.getString(&rDoc);
+ }
+
+ SC_MOD()->SetInputMode(SC_INPUT_TABLE, &aInit);
+}
+
+CellShell_Impl::CellShell_Impl() :
+ m_pRequest( nullptr ) {}
+
+CellShell_Impl::~CellShell_Impl()
+{
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh2.cxx b/sc/source/ui/view/cellsh2.cxx
new file mode 100644
index 0000000000..71bcd6cac3
--- /dev/null
+++ b/sc/source/ui/view/cellsh2.cxx
@@ -0,0 +1,1256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <basic/sberrors.hxx>
+#include <scitems.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/request.hxx>
+#include <basic/sbxcore.hxx>
+#include <svl/whiter.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/stritem.hxx>
+#include <svl/visitem.hxx>
+#include <unotools/moduleoptions.hxx>
+
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+
+#include <cellsh.hxx>
+#include <dbdata.hxx>
+#include <queryparam.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <uiitems.hxx>
+#include <dbdocfun.hxx>
+#include <reffact.hxx>
+#include <utility>
+#include <validat.hxx>
+#include <validate.hxx>
+#include <datamapper.hxx>
+
+#include <scui_def.hxx>
+#include <scabstdlg.hxx>
+#include <impex.hxx>
+#include <asciiopt.hxx>
+#include <datastream.hxx>
+#include <datastreamdlg.hxx>
+#include <dataproviderdlg.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <documentlinkmgr.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <o3tl/make_shared.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+static bool lcl_GetTextToColumnsRange( const ScViewData& rData, ScRange& rRange, bool bDoEmptyCheckOnly )
+{
+ bool bRet = false;
+ const ScMarkData& rMark = rData.GetMarkData();
+
+ if ( rMark.IsMarked() )
+ {
+ if ( !rMark.IsMultiMarked() )
+ {
+ rRange = rMark.GetMarkArea();
+ if ( rRange.aStart.Col() == rRange.aEnd.Col() )
+ {
+ bRet = true;
+ }
+ }
+ }
+ else
+ {
+ const SCCOL nCol = rData.GetCurX();
+ const SCROW nRow = rData.GetCurY();
+ const SCTAB nTab = rData.GetTabNo();
+ rRange = ScRange( nCol, nRow, nTab, nCol, nRow, nTab );
+ bRet = true;
+ }
+
+ const ScDocument& rDoc = rData.GetDocument();
+
+ if ( bDoEmptyCheckOnly )
+ {
+ if ( bRet && rDoc.IsBlockEmpty( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(),
+ rRange.aStart.Tab() ) )
+ {
+ bRet = false;
+ }
+ }
+ else if ( bRet )
+ {
+ rRange.PutInOrder();
+ SCCOL nStartCol = rRange.aStart.Col(), nEndCol = rRange.aEnd.Col();
+ SCROW nStartRow = rRange.aStart.Row(), nEndRow = rRange.aEnd.Row();
+ bool bShrunk = false;
+ rDoc.ShrinkToUsedDataArea( bShrunk, rRange.aStart.Tab(), nStartCol, nStartRow,
+ nEndCol, nEndRow, false, false, true );
+ if ( bShrunk )
+ {
+ rRange.aStart.SetRow( nStartRow );
+ rRange.aEnd.SetRow( nEndRow );
+ }
+ }
+
+ return bRet;
+}
+
+static bool lcl_GetSortParam( const ScViewData& rData, const ScSortParam& rSortParam )
+{
+ ScTabViewShell* pTabViewShell = rData.GetViewShell();
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScDocument& rDoc = rData.GetDocument();
+ SCTAB nTab = rData.GetTabNo();
+ ScDirection eFillDir = DIR_TOP;
+ bool bSort = true;
+ ScRange aExternalRange;
+
+ if( rSortParam.nCol1 != rSortParam.nCol2 )
+ eFillDir = DIR_LEFT;
+ if( rSortParam.nRow1 != rSortParam.nRow2 )
+ eFillDir = DIR_TOP;
+
+ if( rSortParam.nRow2 == rDoc.MaxRow() )
+ {
+ // Assume that user selected entire column(s), but cater for the
+ // possibility that the start row is not the first row.
+ SCSIZE nCount = rDoc.GetEmptyLinesInBlock( rSortParam.nCol1, rSortParam.nRow1, nTab,
+ rSortParam.nCol2, rSortParam.nRow2, nTab, eFillDir );
+ aExternalRange = ScRange( rSortParam.nCol1,
+ ::std::min( rSortParam.nRow1 + sal::static_int_cast<SCROW>( nCount ), rDoc.MaxRow()), nTab,
+ rSortParam.nCol2, rSortParam.nRow2, nTab);
+ aExternalRange.PutInOrder();
+ }
+ else if (rSortParam.nCol1 != rSortParam.nCol2 || rSortParam.nRow1 != rSortParam.nRow2)
+ {
+ // Preserve a preselected area.
+ aExternalRange = ScRange( rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab);
+ aExternalRange.PutInOrder();
+ }
+ else
+ aExternalRange = ScRange( rData.GetCurX(), rData.GetCurY(), nTab );
+
+ SCROW nStartRow = aExternalRange.aStart.Row();
+ SCCOL nStartCol = aExternalRange.aStart.Col();
+ SCROW nEndRow = aExternalRange.aEnd.Row();
+ SCCOL nEndCol = aExternalRange.aEnd.Col();
+ rDoc.GetDataArea( aExternalRange.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow, false, false );
+ aExternalRange.aStart.SetRow( nStartRow );
+ aExternalRange.aStart.SetCol( nStartCol );
+ aExternalRange.aEnd.SetRow( nEndRow );
+ aExternalRange.aEnd.SetCol( nEndCol );
+
+ // with LibreOfficeKit, don't try to interact with the user
+ if (!comphelper::LibreOfficeKit::isActive() &&
+ ((rSortParam.nCol1 == rSortParam.nCol2 && aExternalRange.aStart.Col() != aExternalRange.aEnd.Col()) ||
+ (rSortParam.nRow1 == rSortParam.nRow2 && aExternalRange.aStart.Row() != aExternalRange.aEnd.Row())))
+ {
+ pTabViewShell->AddHighlightRange( aExternalRange,COL_LIGHTBLUE );
+ OUString aExtendStr( aExternalRange.Format(rDoc, ScRefFlags::VALID));
+
+ ScRange aCurrentRange( rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab );
+ OUString aCurrentStr(aCurrentRange.Format(rDoc, ScRefFlags::VALID));
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScSortWarningDlg> pWarningDlg(pFact->CreateScSortWarningDlg(pTabViewShell->GetFrameWeld(), aExtendStr, aCurrentStr));
+ short bResult = pWarningDlg->Execute();
+ if( bResult == BTN_EXTEND_RANGE || bResult == BTN_CURRENT_SELECTION )
+ {
+ if( bResult == BTN_EXTEND_RANGE )
+ {
+ pTabViewShell->MarkRange( aExternalRange, false );
+ pDBData->SetArea( nTab, aExternalRange.aStart.Col(), aExternalRange.aStart.Row(), aExternalRange.aEnd.Col(), aExternalRange.aEnd.Row() );
+ }
+ }
+ else
+ {
+ bSort = false;
+ rData.GetDocShell()->CancelAutoDBRange();
+ }
+
+ pTabViewShell->ClearHighlightRanges();
+ }
+ return bSort;
+}
+
+namespace
+{
+ // this registers the dialog which Find1RefWindow search for
+ class ScValidationRegisteredDlg
+ {
+ std::shared_ptr<SfxDialogController> m_xDlg;
+ public:
+ ScValidationRegisteredDlg(weld::Window* pParent, std::shared_ptr<SfxDialogController> xDlg)
+ : m_xDlg(std::move(xDlg))
+ {
+ SC_MOD()->RegisterRefController(static_cast<sal_uInt16>(ScValidationDlg::SLOTID), m_xDlg, pParent);
+ }
+ ~ScValidationRegisteredDlg()
+ {
+ m_xDlg->Close();
+ SC_MOD()->UnregisterRefController(static_cast<sal_uInt16>(ScValidationDlg::SLOTID), m_xDlg);
+ }
+ };
+}
+
+void ScCellShell::ExecuteDB( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ ScModule* pScMod = SC_MOD();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ }
+
+ switch ( nSlotId )
+ {
+ case SID_VIEW_DATA_SOURCE_BROWSER:
+ {
+ // check if database beamer is open
+
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ bool bWasOpen = false;
+ {
+ uno::Reference<frame::XFrame> xFrame = rViewFrame.GetFrame().GetFrameInterface();
+ uno::Reference<frame::XFrame> xBeamerFrame = xFrame->findFrame(
+ "_beamer",
+ frame::FrameSearchFlag::CHILDREN);
+ if ( xBeamerFrame.is() )
+ bWasOpen = true;
+ }
+
+ if ( bWasOpen )
+ {
+ // close database beamer: just forward to SfxViewFrame
+
+ rViewFrame.ExecuteSlot( rReq );
+ }
+ else
+ {
+ // show database beamer: SfxViewFrame call must be synchronous
+
+ rViewFrame.ExecuteSlot( rReq, false ); // false = synchronous
+
+ // select current database in database beamer
+
+ ScImportParam aImportParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData(true,SC_DB_OLD); // don't create if none found
+ if (pDBData)
+ pDBData->GetImportParam( aImportParam );
+
+ ScDBDocFunc::ShowInBeamer( aImportParam, &pTabViewShell->GetViewFrame() );
+ }
+ rReq.Done(); // needed because it's a toggle slot
+ }
+ break;
+
+ case SID_REIMPORT_DATA:
+ {
+ bool bOk = false;
+ ScDBData* pDBData = pTabViewShell->GetDBData(true,SC_DB_OLD);
+ if (pDBData)
+ {
+ ScImportParam aImportParam;
+ pDBData->GetImportParam( aImportParam );
+ if (aImportParam.bImport && !pDBData->HasImportSelection())
+ {
+ pTabViewShell->ImportData( aImportParam );
+ pDBData->SetImportParam( aImportParam ); //! Undo ??
+ bOk = true;
+ }
+ }
+
+ if (!bOk && ! rReq.IsAPI() )
+ pTabViewShell->ErrorMessage(STR_REIMPORT_EMPTY);
+
+ if( bOk )
+ rReq.Done();
+ }
+ break;
+
+ case SID_REFRESH_DBAREA:
+ {
+ ScDBData* pDBData = pTabViewShell->GetDBData(true,SC_DB_OLD);
+ if (pDBData)
+ {
+ // repeat import like SID_REIMPORT_DATA
+
+ bool bContinue = true;
+ ScImportParam aImportParam;
+ pDBData->GetImportParam( aImportParam );
+ if (aImportParam.bImport && !pDBData->HasImportSelection())
+ {
+ bContinue = pTabViewShell->ImportData( aImportParam );
+ pDBData->SetImportParam( aImportParam ); //! Undo ??
+
+ // mark (size may have been changed)
+ ScRange aNewRange;
+ pDBData->GetArea(aNewRange);
+ pTabViewShell->MarkRange(aNewRange);
+ }
+
+ if ( bContinue ) // fail at import -> break
+ {
+ // internal operations, when any stored
+
+ if ( pDBData->HasQueryParam() || pDBData->HasSortParam() ||
+ pDBData->HasSubTotalParam() )
+ pTabViewShell->RepeatDB();
+
+ // pivot tables that have the range as data source
+
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ GetViewData().GetDocShell()->RefreshPivotTables(aRange);
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_SBA_BRW_INSERT:
+ {
+ OSL_FAIL( "Deprecated Slot" );
+ }
+ break;
+
+ case SID_DATA_FORM:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScDataFormDlg> pDlg(pFact->CreateScDataFormDlg(
+ pTabViewShell->GetFrameWeld(), pTabViewShell));
+
+ pDlg->Execute();
+
+ rReq.Done();
+ }
+ break;
+
+ case SID_SUBTOTALS:
+ ExecuteSubtotals(rReq);
+ break;
+
+ case SID_SORT_DESCENDING:
+ case SID_SORT_ASCENDING:
+ {
+ //#i60401 ux-ctest: Calc does not support all users' strategies regarding sorting data
+ //the patch comes from maoyg
+ ScSortParam aSortParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScViewData& rData = GetViewData();
+
+ pDBData->GetSortParam( aSortParam );
+
+ if( lcl_GetSortParam( rData, aSortParam ) )
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCCOL nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ pDBData->GetSortParam( aSortParam );
+ bool bHasHeader = rDoc.HasColHeader( aSortParam.nCol1, aSortParam.nRow1, aSortParam.nCol2, aSortParam.nRow2, nTab );
+
+ if( nCol < aSortParam.nCol1 )
+ nCol = aSortParam.nCol1;
+ else if( nCol > aSortParam.nCol2 )
+ nCol = aSortParam.nCol2;
+
+ aSortParam.bHasHeader = bHasHeader;
+ aSortParam.bByRow = true;
+ aSortParam.bCaseSens = false;
+ aSortParam.bNaturalSort = false;
+ aSortParam.aDataAreaExtras.mbCellNotes = false;
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
+ aSortParam.aDataAreaExtras.mbCellFormats = true;
+ aSortParam.bInplace = true;
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = nCol;
+ aSortParam.maKeyState[0].bAscending = ( nSlotId == SID_SORT_ASCENDING );
+
+ for ( sal_uInt16 i=1; i<aSortParam.GetSortKeyCount(); i++ )
+ aSortParam.maKeyState[i].bDoSort = false;
+
+ pTabViewShell->UISort( aSortParam ); // subtotal when needed new
+
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_SORT:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ //#i60401 ux-ctest: Calc does not support all users' strategies regarding sorting data
+ //the patch comes from maoyg
+
+ if ( pArgs ) // Basic
+ {
+ ScSortParam aSortParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScViewData& rData = GetViewData();
+
+ pDBData->GetSortParam( aSortParam );
+
+ if( lcl_GetSortParam( rData, aSortParam ) )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ pDBData->GetSortParam( aSortParam );
+ bool bHasHeader = rDoc.HasColHeader( aSortParam.nCol1, aSortParam.nRow1, aSortParam.nCol2, aSortParam.nRow2, rData.GetTabNo() );
+ if( bHasHeader )
+ aSortParam.bHasHeader = bHasHeader;
+
+ aSortParam.bInplace = true; // from Basic always
+
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_BYROW ) )
+ aSortParam.bByRow = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_HASHEADER ) )
+ aSortParam.bHasHeader = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_CASESENS ) )
+ aSortParam.bCaseSens = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_NATURALSORT ) )
+ aSortParam.bNaturalSort = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_INCCOMMENTS ) )
+ aSortParam.aDataAreaExtras.mbCellNotes = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_INCIMAGES ) )
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = pItem->GetValue();
+ if ( const SfxBoolItem* pItem = pArgs->GetItemIfSet( SID_SORT_ATTRIBS ) )
+ aSortParam.aDataAreaExtras.mbCellFormats = pItem->GetValue();
+ if ( const SfxUInt16Item* pItem = pArgs->GetItemIfSet( SID_SORT_USERDEF ) )
+ {
+ sal_uInt16 nUserIndex = pItem->GetValue();
+ aSortParam.bUserDef = ( nUserIndex != 0 );
+ if ( nUserIndex )
+ aSortParam.nUserIndex = nUserIndex - 1; // Basic: 1-based
+ }
+
+ SCCOLROW nField0 = 0;
+ const SfxPoolItem* pItem = nullptr;
+ if ( pArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET )
+ nField0 = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ aSortParam.maKeyState[0].bDoSort = ( nField0 != 0 );
+ aSortParam.maKeyState[0].nField = nField0 > 0 ? (nField0-1) : 0;
+ if ( pArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET )
+ aSortParam.maKeyState[0].bAscending = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ SCCOLROW nField1 = 0;
+ if ( pArgs->GetItemState( FN_PARAM_3, true, &pItem ) == SfxItemState::SET )
+ nField1 = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ aSortParam.maKeyState[1].bDoSort = ( nField1 != 0 );
+ aSortParam.maKeyState[1].nField = nField1 > 0 ? (nField1-1) : 0;
+ if ( pArgs->GetItemState( FN_PARAM_4, true, &pItem ) == SfxItemState::SET )
+ aSortParam.maKeyState[1].bAscending = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ SCCOLROW nField2 = 0;
+ if ( pArgs->GetItemState( FN_PARAM_5, true, &pItem ) == SfxItemState::SET )
+ nField2 = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ aSortParam.maKeyState[2].bDoSort = ( nField2 != 0 );
+ aSortParam.maKeyState[2].nField = nField2 > 0 ? (nField2-1) : 0;
+ if ( pArgs->GetItemState( FN_PARAM_6, true, &pItem ) == SfxItemState::SET )
+ aSortParam.maKeyState[2].bAscending = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // subtotal when needed new
+ pTabViewShell->UISort( aSortParam );
+ rReq.Done();
+ }
+ }
+ else
+ {
+ ScSortParam aSortParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+ ScViewData& rData = GetViewData();
+
+ pDBData->GetSortParam( aSortParam );
+
+ if( lcl_GetSortParam( rData, aSortParam ) )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SfxItemSetFixed<SCITEM_SORTDATA, SCITEM_SORTDATA> aArgSet( GetPool() );
+
+ pDBData->GetSortParam( aSortParam );
+ bool bHasHeader = rDoc.HasColHeader( aSortParam.nCol1, aSortParam.nRow1, aSortParam.nCol2, aSortParam.nRow2, rData.GetTabNo() );
+ if( bHasHeader )
+ aSortParam.bHasHeader = bHasHeader;
+
+ aArgSet.Put( ScSortItem( SCITEM_SORTDATA, &GetViewData(), &aSortParam ) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ std::shared_ptr<ScAsyncTabController> pDlg(pFact->CreateScSortDlg(pTabViewShell->GetFrameWeld(), &aArgSet));
+ pDlg->SetCurPageId("criteria"); // 1=sort field tab 2=sort options tab
+
+ VclAbstractDialog::AsyncContext aContext;
+ aContext.maEndDialogFn = [pDlg, &rData, pTabViewShell](sal_Int32 nResult)
+ {
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+ const ScSortParam& rOutParam =
+ pOutSet->Get( SCITEM_SORTDATA ).GetSortData();
+
+ // subtotal when needed new
+
+ pTabViewShell->UISort( rOutParam );
+
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxRequest aRequest(rViewFrm, SID_SORT);
+
+ if ( rOutParam.bInplace )
+ {
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_BYROW,
+ rOutParam.bByRow ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_HASHEADER,
+ rOutParam.bHasHeader ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_CASESENS,
+ rOutParam.bCaseSens ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_NATURALSORT,
+ rOutParam.bNaturalSort ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_INCCOMMENTS,
+ rOutParam.aDataAreaExtras.mbCellNotes ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_INCIMAGES,
+ rOutParam.aDataAreaExtras.mbCellDrawObjects ) );
+ aRequest.AppendItem( SfxBoolItem( SID_SORT_ATTRIBS,
+ rOutParam.aDataAreaExtras.mbCellFormats ) );
+ sal_uInt16 nUser = rOutParam.bUserDef ? ( rOutParam.nUserIndex + 1 ) : 0;
+ aRequest.AppendItem( SfxUInt16Item( SID_SORT_USERDEF, nUser ) );
+ if ( rOutParam.maKeyState[0].bDoSort )
+ {
+ aRequest.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_1),
+ rOutParam.maKeyState[0].nField + 1 ) );
+ aRequest.AppendItem( SfxBoolItem( FN_PARAM_2,
+ rOutParam.maKeyState[0].bAscending ) );
+ }
+ if ( rOutParam.maKeyState[1].bDoSort )
+ {
+ aRequest.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_3),
+ rOutParam.maKeyState[1].nField + 1 ) );
+ aRequest.AppendItem( SfxBoolItem( FN_PARAM_4,
+ rOutParam.maKeyState[1].bAscending ) );
+ }
+ if ( rOutParam.maKeyState[2].bDoSort )
+ {
+ aRequest.AppendItem( SfxInt32Item( TypedWhichId<SfxInt32Item>(FN_PARAM_5),
+ rOutParam.maKeyState[2].nField + 1 ) );
+ aRequest.AppendItem( SfxBoolItem( FN_PARAM_6,
+ rOutParam.maKeyState[2].bAscending ) );
+ }
+ }
+
+ aRequest.Done();
+ }
+ else
+ {
+ rData.GetDocShell()->CancelAutoDBRange();
+ }
+ };
+
+ pDlg->StartExecuteAsync(aContext);
+ }
+ }
+ }
+ break;
+
+ case SID_FILTER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ OSL_FAIL("SID_FILTER with arguments?");
+ pTabViewShell->Query(
+ pArgs->Get( SCITEM_QUERYDATA ).GetQueryData(), nullptr, true );
+ rReq.Done();
+ }
+ else
+ {
+ sal_uInt16 nId = ScFilterDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ }
+ break;
+
+ case SID_SPECIAL_FILTER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ OSL_FAIL("SID_SPECIAL_FILTER with arguments?");
+ pTabViewShell->Query(
+ pArgs->Get( SCITEM_QUERYDATA ).GetQueryData(), nullptr, true );
+ rReq.Done();
+ }
+ else
+ {
+ sal_uInt16 nId = ScSpecialFilterDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ }
+ break;
+
+ case FID_FILTER_OK:
+ {
+ const ScQueryItem* pQueryItem;
+ if ( pReqArgs && (pQueryItem =
+ pReqArgs->GetItemIfSet( SCITEM_QUERYDATA )) )
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ SCTAB nRefTab = GetViewData().GetRefTabNo();
+
+ // If RefInput switched to a different sheet from the data sheet,
+ // switch back:
+
+ if ( nCurTab != nRefTab )
+ {
+ pTabViewShell->SetTabNo( nRefTab );
+ pTabViewShell->PaintExtras();
+ }
+
+ ScRange aAdvSource;
+ if (pQueryItem->GetAdvancedQuerySource(aAdvSource))
+ pTabViewShell->Query( pQueryItem->GetQueryData(), &aAdvSource, true );
+ else
+ pTabViewShell->Query( pQueryItem->GetQueryData(), nullptr, true );
+ rReq.Done( *pReqArgs );
+ }
+ }
+ break;
+
+ case SID_UNFILTER:
+ {
+ ScQueryParam aParam;
+ ScDBData* pDBData = pTabViewShell->GetDBData();
+
+ pDBData->GetQueryParam( aParam );
+ SCSIZE nEC = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nEC; i++)
+ aParam.GetEntry(i).bDoQuery = false;
+ aParam.bDuplicate = true;
+ pTabViewShell->Query( aParam, nullptr, true );
+ rReq.Done();
+ }
+ break;
+
+ case SID_AUTO_FILTER:
+ pTabViewShell->ToggleAutoFilter();
+ rReq.Done();
+ break;
+
+ case SID_AUTOFILTER_HIDE:
+ pTabViewShell->HideAutoFilter();
+ rReq.Done();
+ break;
+
+ case SID_PIVOT_TABLE:
+ {
+ const ScPivotItem* pPItem;
+ if ( pReqArgs && (pPItem =
+ pReqArgs->GetItemIfSet( SCITEM_PIVOTDATA )) )
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ SCTAB nRefTab = GetViewData().GetRefTabNo();
+
+ // If RefInput switched to a different sheet from the data sheet,
+ // switch back:
+
+ if ( nCurTab != nRefTab )
+ {
+ pTabViewShell->SetTabNo( nRefTab );
+ pTabViewShell->PaintExtras();
+ }
+
+ const ScDPObject* pDPObject = pTabViewShell->GetDialogDPObject();
+ if ( pDPObject )
+ {
+ bool bSuccess = pTabViewShell->MakePivotTable(
+ pPItem->GetData(), pPItem->GetDestRange(), pPItem->IsNewSheet(), *pDPObject );
+ SfxBoolItem aRet(0, bSuccess);
+ rReq.SetReturnValue(aRet);
+ }
+ rReq.Done();
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if (rReq.IsAPI())
+ SbxBase::SetError(ERRCODE_BASIC_BAD_PARAMETER);
+#endif
+ }
+ break;
+
+ case SID_OPENDLG_PIVOTTABLE:
+ ExecuteDataPilotDialog();
+ break;
+ case SID_DEFINE_DBNAME:
+ {
+
+ sal_uInt16 nId = ScDbNameDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+
+ }
+ break;
+
+ case SID_SELECT_DB:
+ {
+ if ( pReqArgs )
+ {
+ const SfxStringItem& rItem = pReqArgs->Get(SID_SELECT_DB);
+ pTabViewShell->GotoDBArea(rItem.GetValue());
+ rReq.Done();
+ }
+ else
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDBCollection* pDBCol = rDoc.GetDBCollection();
+
+ if ( pDBCol )
+ {
+ std::vector<OUString> aList;
+ const ScDBCollection::NamedDBs& rDBs = pDBCol->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ aList.push_back(rxDB->GetName());
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScSelEntryDlg> pDlg(pFact->CreateScSelEntryDlg(pTabViewShell->GetFrameWeld(), aList));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ OUString aName = pDlg->GetSelectedEntry();
+ pTabViewShell->GotoDBArea( aName );
+ rReq.AppendItem( SfxStringItem( SID_SELECT_DB, aName ) );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ break;
+ case SID_DATA_STREAMS:
+ {
+ sc::DataStreamDlg aDialog(GetViewData().GetDocShell(), pTabViewShell->GetFrameWeld());
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ sc::DataStream* pStrm = rMgr.getDataStream();
+ if (pStrm)
+ aDialog.Init(*pStrm);
+
+ if (aDialog.run() == RET_OK)
+ aDialog.StartStream();
+ }
+ break;
+ case SID_DATA_STREAMS_PLAY:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ sc::DataStream* pStrm = rMgr.getDataStream();
+ if (pStrm)
+ pStrm->StartImport();
+ }
+ break;
+ case SID_DATA_STREAMS_STOP:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ sc::DataStream* pStrm = rMgr.getDataStream();
+ if (pStrm)
+ pStrm->StopImport();
+ }
+ break;
+ case SID_DATA_PROVIDER:
+ {
+ auto xDoc = o3tl::make_shared<ScDocument>();
+ xDoc->InsertTab(0, "test");
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDataProviderDlg aDialog(pTabViewShell->GetDialogParent(), xDoc, &rDoc);
+ if (aDialog.run() == RET_OK)
+ {
+ aDialog.import(rDoc);
+ }
+ }
+ break;
+ case SID_DATA_PROVIDER_REFRESH:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ auto& rDataMapper = rDoc.GetExternalDataMapper();
+ for (auto& rDataSource : rDataMapper.getDataSources())
+ {
+ rDataSource.refresh(&rDoc, false);
+ }
+ }
+ break;
+ case SID_MANAGE_XML_SOURCE:
+ ExecuteXMLSourceDialog();
+ break;
+ case FID_VALIDATION:
+ case FID_CURRENTVALIDATION:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ OSL_FAIL("later...");
+ }
+ else
+ {
+ SfxItemSet aArgSet( GetPool(), ScTPValidationValue::GetRanges() );
+ ScValidationMode eMode = SC_VALID_ANY;
+ ScConditionMode eOper = ScConditionMode::Equal;
+ OUString aExpr1, aExpr2;
+ bool bBlank = true;
+ sal_Int16 nListType = css::sheet::TableValidationVisibility::UNSORTED;
+ bool bShowHelp = false;
+ OUString aHelpTitle, aHelpText;
+ bool bShowError = false;
+ ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
+ OUString aErrTitle, aErrText;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCursorPos( nCurX, nCurY, nTab );
+ sal_uInt32 nIndex = rDoc.GetAttr(
+ nCurX, nCurY, nTab, ATTR_VALIDDATA )->GetValue();
+ if ( nIndex )
+ {
+ const ScValidationData* pOldData = rDoc.GetValidationEntry( nIndex );
+ if ( pOldData )
+ {
+ eMode = pOldData->GetDataMode();
+ eOper = pOldData->GetOperation();
+ sal_uInt32 nNumFmt = 0;
+ if ( eMode == SC_VALID_DATE || eMode == SC_VALID_TIME )
+ {
+ SvNumFormatType nType = ( eMode == SC_VALID_DATE ) ? SvNumFormatType::DATE
+ : SvNumFormatType::TIME;
+ nNumFmt = rDoc.GetFormatTable()->GetStandardFormat(
+ nType, ScGlobal::eLnge );
+ }
+ aExpr1 = pOldData->GetExpression( aCursorPos, 0, nNumFmt );
+ aExpr2 = pOldData->GetExpression( aCursorPos, 1, nNumFmt );
+ bBlank = pOldData->IsIgnoreBlank();
+ nListType = pOldData->GetListType();
+
+ bShowHelp = pOldData->GetInput( aHelpTitle, aHelpText );
+ bShowError = pOldData->GetErrMsg( aErrTitle, aErrText, eErrStyle );
+
+ aArgSet.Put( SfxUInt16Item( FID_VALID_MODE, sal::static_int_cast<sal_uInt16>(eMode) ) );
+ aArgSet.Put( SfxUInt16Item( FID_VALID_CONDMODE, sal::static_int_cast<sal_uInt16>(eOper) ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_VALUE1, aExpr1 ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_VALUE2, aExpr2 ) );
+ aArgSet.Put( SfxBoolItem( FID_VALID_BLANK, bBlank ) );
+ aArgSet.Put( SfxInt16Item( FID_VALID_LISTTYPE, nListType ) );
+ aArgSet.Put( SfxBoolItem( FID_VALID_SHOWHELP, bShowHelp ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_HELPTITLE, aHelpTitle ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_HELPTEXT, aHelpText ) );
+ aArgSet.Put( SfxBoolItem( FID_VALID_SHOWERR, bShowError ) );
+ aArgSet.Put( SfxUInt16Item( FID_VALID_ERRSTYLE, sal::static_int_cast<sal_uInt16>(eErrStyle) ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_ERRTITLE, aErrTitle ) );
+ aArgSet.Put( SfxStringItem( FID_VALID_ERRTEXT, aErrText ) );
+ }
+ }
+
+ // cell range picker
+ vcl::Window* pWin = GetViewData().GetActiveWin();
+ weld::Window* pParentWin = pWin ? pWin->GetFrameWeld() : nullptr;
+ auto xDlg = std::make_shared<ScValidationDlg>(pParentWin, &aArgSet, pTabViewShell);
+ ScValidationRegisteredDlg aRegisterThatDlgExists(pParentWin, xDlg);
+
+ short nResult = xDlg->run();
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = xDlg->GetOutputItemSet();
+
+ if ( const SfxUInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_MODE ) )
+ eMode = static_cast<ScValidationMode>(pItem->GetValue());
+ if ( const SfxUInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_CONDMODE ) )
+ eOper = static_cast<ScConditionMode>(pItem->GetValue());
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_VALUE1 ) )
+ {
+ OUString aTemp1 = pItem->GetValue();
+ if (eMode == SC_VALID_DATE || eMode == SC_VALID_TIME)
+ {
+ sal_uInt32 nNumIndex = 0;
+ double nVal;
+ if (rDoc.GetFormatTable()->IsNumberFormat(aTemp1, nNumIndex, nVal))
+ aExpr1 = ::rtl::math::doubleToUString( nVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+ else
+ aExpr1 = aTemp1;
+ }
+ else
+ aExpr1 = aTemp1;
+ }
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_VALUE2 ) )
+ {
+ OUString aTemp2 = pItem->GetValue();
+ if (eMode == SC_VALID_DATE || eMode == SC_VALID_TIME)
+ {
+ sal_uInt32 nNumIndex = 0;
+ double nVal;
+ if (rDoc.GetFormatTable()->IsNumberFormat(aTemp2, nNumIndex, nVal))
+ aExpr2 = ::rtl::math::doubleToUString( nVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+ else
+ aExpr2 = aTemp2;
+ if ( eMode == SC_VALID_TIME ) {
+ sal_Int32 wraparound = aExpr1.compareTo(aExpr2);
+ if (wraparound > 0) {
+ if (eOper == ScConditionMode::Between) {
+ eOper = ScConditionMode::NotBetween;
+ std::swap( aExpr1, aExpr2 );
+ }
+ else if (eOper == ScConditionMode::NotBetween) {
+ eOper = ScConditionMode::Between;
+ std::swap( aExpr1, aExpr2 );
+ }
+ }
+ }
+ }
+ else
+ aExpr2 = aTemp2;
+ }
+ if ( const SfxBoolItem* pItem = pOutSet->GetItemIfSet( FID_VALID_BLANK ) )
+ bBlank = pItem->GetValue();
+ if ( const SfxInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_LISTTYPE ) )
+ nListType = pItem->GetValue();
+
+ if ( const SfxBoolItem* pItem = pOutSet->GetItemIfSet( FID_VALID_SHOWHELP ) )
+ bShowHelp = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_HELPTITLE ) )
+ aHelpTitle = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_HELPTEXT ) )
+ aHelpText = pItem->GetValue();
+
+ if ( const SfxBoolItem* pItem = pOutSet->GetItemIfSet( FID_VALID_SHOWERR ) )
+ bShowError = pItem->GetValue();
+ if ( const SfxUInt16Item* pItem = pOutSet->GetItemIfSet( FID_VALID_ERRSTYLE ) )
+ eErrStyle = static_cast<ScValidErrorStyle>(pItem->GetValue());
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_ERRTITLE ) )
+ aErrTitle = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pOutSet->GetItemIfSet( FID_VALID_ERRTEXT ) )
+ aErrText = pItem->GetValue();
+
+ ScValidationData aData( eMode, eOper, aExpr1, aExpr2, rDoc, aCursorPos );
+ aData.SetIgnoreBlank( bBlank );
+ aData.SetListType( nListType );
+
+ aData.SetInput(aHelpTitle, aHelpText); // sets bShowInput to TRUE
+ if (!bShowHelp)
+ aData.ResetInput(); // reset only bShowInput
+
+ aData.SetError(aErrTitle, aErrText, eErrStyle); // sets bShowError to TRUE
+ if (!bShowError)
+ aData.ResetError(); // reset only bShowError
+
+ pTabViewShell->SetValidation( aData );
+ pTabViewShell->TestHintWindow();
+ rReq.Done( *pOutSet );
+ }
+ }
+ }
+ break;
+
+ case SID_TEXT_TO_COLUMNS:
+ {
+ ScViewData& rData = GetViewData();
+ ScRange aRange;
+
+ if ( lcl_GetTextToColumnsRange( rData, aRange, false ) )
+ {
+ ScDocument& rDoc = rData.GetDocument();
+
+ ScImportExport aExport( rDoc, aRange );
+ aExport.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::None, 0, false ) );
+
+ // #i87703# text to columns fails with tab separator
+ aExport.SetDelimiter( u'\0' );
+
+ SvMemoryStream aStream;
+ aStream.SetStreamCharSet( RTL_TEXTENCODING_UNICODE );
+ ScImportExport::SetNoEndianSwap( aStream );
+ aExport.ExportStream( aStream, OUString(), SotClipboardFormatId::STRING );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScImportAsciiDlg> pDlg(pFact->CreateScImportAsciiDlg(
+ pTabViewShell->GetFrameWeld(), OUString(), &aStream, SC_TEXTTOCOLUMNS));
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScDocShell* pDocSh = rData.GetDocShell();
+ OSL_ENSURE( pDocSh, "ScCellShell::ExecuteDB: SID_TEXT_TO_COLUMNS - pDocSh is null!" );
+
+ OUString aUndo = ScResId( STR_UNDO_TEXTTOCOLUMNS );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, rData.GetViewShell()->GetViewShellId() );
+
+ ScImportExport aImport( rDoc, aRange.aStart );
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions( aOptions );
+ pDlg->SaveParameters();
+ aImport.SetExtOptions( aOptions );
+ aImport.SetApi( false );
+ aImport.SetImportBroadcast( true );
+ aImport.SetOverwriting( true );
+ aStream.Seek( 0 );
+ aImport.ImportStream( aStream, OUString(), SotClipboardFormatId::STRING );
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScCellShell::GetDBState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScViewData& rData = GetViewData();
+ ScDocShell* pDocSh = rData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nPosX = rData.GetCurX();
+ SCROW nPosY = rData.GetCurY();
+ SCTAB nTab = rData.GetTabNo();
+
+ bool bAutoFilter = false;
+ bool bAutoFilterTested = false;
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_REFRESH_DBAREA:
+ {
+ // imported data without selection
+ // or filter,sort,subtotal (also without import)
+ bool bOk = false;
+ ScDBData* pDBData = pTabViewShell->GetDBData(false,SC_DB_OLD);
+ if (pDBData && rDoc.GetChangeTrack() == nullptr)
+ {
+ if ( pDBData->HasImportParam() )
+ bOk = !pDBData->HasImportSelection();
+ else
+ {
+ bOk = pDBData->HasQueryParam() ||
+ pDBData->HasSortParam() ||
+ pDBData->HasSubTotalParam();
+ }
+ }
+ if (!bOk)
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_FILTER:
+ case SID_SPECIAL_FILTER:
+ {
+ ScRange aDummy;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy);
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ //in case of Redlining and multiselection disable
+ case SID_SORT_ASCENDING:
+ case SID_SORT_DESCENDING:
+ case SCITEM_SORTDATA:
+ case SCITEM_SUBTDATA:
+ case SID_OPENDLG_PIVOTTABLE:
+ {
+ //! move ReadOnly check to idl flags
+
+ if ( pDocSh->IsReadOnly() || rDoc.GetChangeTrack()!=nullptr ||
+ GetViewData().IsMultiMarked() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_REIMPORT_DATA:
+ {
+ // only imported data without selection
+ ScDBData* pDBData = pTabViewShell->GetDBData(false,SC_DB_OLD);
+ if (!pDBData || !pDBData->HasImportParam() || pDBData->HasImportSelection() ||
+ rDoc.GetChangeTrack()!=nullptr)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_VIEW_DATA_SOURCE_BROWSER:
+ {
+ if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::DATABASE))
+ rSet.Put(SfxVisibilityItem(nWhich, false));
+ else
+ // get state (BoolItem) from SfxViewFrame
+ pTabViewShell->GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ }
+ break;
+ case SID_SBA_BRW_INSERT:
+ {
+ // SBA wants a sal_Bool-item, enabled
+
+ rSet.Put(SfxBoolItem(nWhich, true));
+ }
+ break;
+
+ case SID_AUTO_FILTER:
+ case SID_AUTOFILTER_HIDE:
+ {
+ if (!bAutoFilterTested)
+ {
+ bAutoFilter = rDoc.HasAutoFilter( nPosX, nPosY, nTab );
+ bAutoFilterTested = true;
+ }
+ if ( nWhich == SID_AUTO_FILTER )
+ {
+ ScRange aDummy;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy);
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else if (rDoc.GetDPAtBlock(aDummy))
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ rSet.Put( SfxBoolItem( nWhich, bAutoFilter ) );
+ }
+ else
+ if (!bAutoFilter)
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_UNFILTER:
+ {
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+ bool bAnyQuery = false;
+
+ bool bSelected = (GetViewData().GetSimpleArea(
+ nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab )
+ == SC_MARK_SIMPLE);
+
+ if ( bSelected )
+ {
+ if (nStartCol==nEndCol && nStartRow==nEndRow)
+ bSelected = false;
+ }
+ else
+ {
+ nStartCol = GetViewData().GetCurX();
+ nStartRow = GetViewData().GetCurY();
+ nStartTab = GetViewData().GetTabNo();
+ }
+
+ ScDBData* pDBData = bSelected
+ ? rDoc.GetDBAtArea( nStartTab, nStartCol, nStartRow, nEndCol, nEndRow )
+ : rDoc.GetDBAtCursor( nStartCol, nStartRow, nStartTab, ScDBDataPortion::AREA );
+
+ if ( pDBData )
+ {
+ ScQueryParam aParam;
+ pDBData->GetQueryParam( aParam );
+ if ( aParam.GetEntry(0).bDoQuery )
+ bAnyQuery = true;
+ }
+
+ if ( !bAnyQuery )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DEFINE_DBNAME:
+ {
+ if ( pDocSh->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_DATA_PROVIDER:
+ break;
+ case SID_DATA_PROVIDER_REFRESH:
+ {
+ ScDocument& rViewDoc = GetViewData().GetDocument();
+ auto& rDataMapper = rViewDoc.GetExternalDataMapper();
+ if (rDataMapper.getDataSources().empty())
+ rSet.DisableItem(nWhich);
+ }
+ break;
+ case SID_DATA_STREAMS:
+ case SID_DATA_STREAMS_PLAY:
+ case SID_DATA_STREAMS_STOP:
+ {
+ if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+ case SID_TEXT_TO_COLUMNS:
+ {
+ ScRange aRange;
+ if ( !lcl_GetTextToColumnsRange( rData, aRange, true ) )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_MANAGE_XML_SOURCE:
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh3.cxx b/sc/source/ui/view/cellsh3.cxx
new file mode 100644
index 0000000000..e6c89b6a2b
--- /dev/null
+++ b/sc/source/ui/view/cellsh3.cxx
@@ -0,0 +1,1096 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <formula/formulahelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <reffact.hxx>
+#include <uiitems.hxx>
+#include <autoform.hxx>
+#include <cellsh.hxx>
+#include <inputhdl.hxx>
+#include <editable.hxx>
+#include <funcdesc.hxx>
+#include <markdata.hxx>
+#include <scabstdlg.hxx>
+#include <condformateasydlg.hxx>
+#include <columnspanset.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <inputwin.hxx>
+
+#include <memory>
+
+using sc::TwipsToEvenHMM;
+
+namespace
+{
+/// Rid ourselves of unwanted " quoted json characters.
+OString escapeJSON(const OUString &aStr)
+{
+ OUString aEscaped = aStr;
+ aEscaped = aEscaped.replaceAll("\n", " ");
+ aEscaped = aEscaped.replaceAll("\"", "'");
+ return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8);
+}
+
+void lcl_lokGetWholeFunctionList()
+{
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (!(comphelper::LibreOfficeKit::isActive()
+ && pViewShell && pViewShell->isLOKMobilePhone()))
+ return;
+
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ sal_uInt32 nListCount = pFuncList->GetCount();
+ std::set<OUString> aFuncNameOrderedSet;
+ for(sal_uInt32 i = 0; i < nListCount; ++i)
+ {
+ const ScFuncDesc* pDesc = pFuncList->GetFunction( i );
+ if ( pDesc->mxFuncName )
+ {
+ aFuncNameOrderedSet.insert(*pDesc->mxFuncName);
+ }
+ }
+ ScFunctionMgr* pFuncManager = ScGlobal::GetStarCalcFunctionMgr();
+ if (!(pFuncManager && aFuncNameOrderedSet.size()))
+ return;
+
+ OStringBuffer aPayload(
+ "{ \"wholeList\": true, "
+ "\"categories\": [ ");
+
+ formula::FormulaHelper aHelper(pFuncManager);
+ sal_uInt32 nCategoryCount = pFuncManager->getCount();
+ for (sal_uInt32 i = 0; i < nCategoryCount; ++i)
+ {
+ OUString sCategoryName = ScFunctionMgr::GetCategoryName(i);
+ aPayload.append("{"
+ "\"name\": \""
+ + escapeJSON(sCategoryName)
+ + "\"}, ");
+ }
+ sal_Int32 nLen = aPayload.getLength();
+ aPayload[nLen - 2] = ' ';
+ aPayload[nLen - 1] = ']';
+ aPayload.append(", ");
+
+ OUString aDescFuncNameStr;
+ aPayload.append("\"functions\": [ ");
+ sal_uInt32 nCurIndex = 0;
+ for (const OUString& aFuncNameStr : aFuncNameOrderedSet)
+ {
+ aDescFuncNameStr = aFuncNameStr + "()";
+ sal_Int32 nNextFStart = 0;
+ const formula::IFunctionDescription* ppFDesc;
+ ::std::vector< OUString > aArgs;
+ OUString eqPlusFuncName = "=" + aDescFuncNameStr;
+ if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
+ {
+ if ( ppFDesc && !ppFDesc->getFunctionName().isEmpty() )
+ {
+ if (ppFDesc->getCategory())
+ {
+ aPayload.append("{"
+ "\"index\": "
+ + OString::number(static_cast<sal_Int64>(nCurIndex))
+ + ", "
+ "\"category\": "
+ + OString::number(static_cast<sal_Int64>(ppFDesc->getCategory()->getNumber()))
+ + ", "
+ "\"signature\": \""
+ + escapeJSON(ppFDesc->getSignature())
+ + "\", "
+ "\"description\": \""
+ + escapeJSON(ppFDesc->getDescription())
+ + "\"}, ");
+ }
+ }
+ }
+ ++nCurIndex;
+ }
+ nLen = aPayload.getLength();
+ aPayload[nLen - 2] = ' ';
+ aPayload[nLen - 1] = ']';
+ aPayload.append(" }");
+
+ OString s = aPayload.makeStringAndClear();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s);
+}
+
+} // end namespace
+
+void ScCellShell::Execute( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = pTabViewShell->GetViewFrame().GetBindings();
+ ScModule* pScMod = SC_MOD();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ if (nSlot != SID_CURRENTCELL) // this comes with MouseButtonUp
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ switch ( nSlot )
+ {
+ // when opening a reference-dialog the subshell may not be switched
+ // (on closing the dialog StopEditShell is called)
+ case SID_OPENDLG_FUNCTION:
+ // inplace leads to trouble with EditShell ...
+ //! cannot always be switched ????
+ if (!pTabViewShell->GetViewFrame().GetFrame().IsInPlace())
+ pTabViewShell->SetDontSwitch(true); // do not switch off EditShell
+ [[fallthrough]];
+
+ case FID_CELL_FORMAT:
+ case SID_ENABLE_HYPHENATION:
+ case SID_DATA_SELECT:
+ case SID_OPENDLG_CONSOLIDATE:
+ case SID_OPENDLG_SOLVE:
+ case SID_OPENDLG_OPTSOLVER:
+
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+
+ pTabViewShell->SetDontSwitch(false);
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch ( nSlot )
+ {
+ case SID_STATUS_SELMODE:
+ if ( pReqArgs )
+ {
+ /* 0: STD Click cancels selection
+ * 1: ER Click extends selection
+ * 2: ERG Click defines further selection
+ */
+ sal_uInt16 nMode = static_cast<const SfxUInt16Item&>(pReqArgs->Get( nSlot )).GetValue();
+
+ switch ( nMode )
+ {
+ case 1: nMode = KEY_SHIFT; break;
+ case 2: nMode = KEY_MOD1; break; // control-key
+ case 0:
+ default:
+ nMode = 0;
+ }
+
+ pTabViewShell->LockModifiers( nMode );
+ }
+ else
+ {
+ // no arguments (also executed by double click on the status bar controller):
+ // advance to next selection mode
+
+ sal_uInt16 nModifiers = pTabViewShell->GetLockedModifiers();
+ switch ( nModifiers )
+ {
+ case KEY_SHIFT: nModifiers = KEY_MOD1; break; // EXT -> ADD
+ case KEY_MOD1: nModifiers = 0; break; // ADD -> STD
+ default: nModifiers = KEY_SHIFT; break; // STD -> EXT
+ }
+ pTabViewShell->LockModifiers( nModifiers );
+ }
+
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ rReq.Done();
+ break;
+
+ // SID_STATUS_SELMODE_NORM is not used ???
+
+ case SID_STATUS_SELMODE_NORM:
+ pTabViewShell->LockModifiers( 0 );
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ break;
+
+ // SID_STATUS_SELMODE_ERG / SID_STATUS_SELMODE_ERW as toggles:
+
+ case SID_STATUS_SELMODE_ERG:
+ if ( pTabViewShell->GetLockedModifiers() & KEY_MOD1 )
+ pTabViewShell->LockModifiers( 0 );
+ else
+ pTabViewShell->LockModifiers( KEY_MOD1 );
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ break;
+
+ case SID_STATUS_SELMODE_ERW:
+ if ( pTabViewShell->GetLockedModifiers() & KEY_SHIFT )
+ pTabViewShell->LockModifiers( 0 );
+ else
+ pTabViewShell->LockModifiers( KEY_SHIFT );
+ rBindings.Invalidate( SID_STATUS_SELMODE );
+ break;
+
+ case SID_ENTER_STRING:
+ {
+ if ( pReqArgs )
+ {
+ // In the LOK case, we want to set the document modified state
+ // right away at the start of the edit, so that the content is
+ // saved even when the user leaves the document before hitting
+ // Enter
+ // NOTE: This also means we want to set the modified state
+ // regardless of the DontCommit parameter's value.
+ if (comphelper::LibreOfficeKit::isActive() && !GetViewData().GetDocShell()->IsModified())
+ {
+ GetViewData().GetDocShell()->SetModified();
+ rBindings.Invalidate(SID_SAVEDOC);
+ rBindings.Invalidate(SID_DOC_MODIFIED);
+ }
+
+ OUString aStr( pReqArgs->Get( SID_ENTER_STRING ).GetValue() );
+ const SfxPoolItem* pDontCommitItem;
+ bool bCommit = true;
+ if (pReqArgs->HasItem(FN_PARAM_1, &pDontCommitItem))
+ bCommit = !(static_cast<const SfxBoolItem*>(pDontCommitItem)->GetValue());
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( pTabViewShell );
+ if (bCommit)
+ {
+ pTabViewShell->EnterData( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo(),
+ aStr, nullptr,
+ true /*bMatrixExpand*/);
+ }
+ else if (pHdl)
+ {
+ SC_MOD()->SetInputMode(SC_INPUT_TABLE);
+
+ EditView* pTableView = pHdl->GetActiveView();
+ pHdl->DataChanging();
+ if (pTableView)
+ pTableView->GetEditEngine()->SetText(aStr);
+ pHdl->DataChanged();
+
+ SC_MOD()->SetInputMode(SC_INPUT_NONE);
+ }
+
+ if ( !pHdl || !pHdl->IsInEnterHandler() )
+ {
+ // UpdateInputHandler is needed after the cell content
+ // has changed, but if called from EnterHandler, UpdateInputHandler
+ // will be called later when moving the cursor.
+ pTabViewShell->UpdateInputHandler();
+ }
+
+ rReq.Done();
+
+ // no GrabFocus here, as otherwise on a Mac the tab jumps before the
+ // sideview, when the input was not finished
+ // (GrabFocus is called in KillEditView)
+ }
+ }
+ break;
+
+ case SID_INSERT_MATRIX:
+ {
+ if ( pReqArgs )
+ {
+ OUString aStr = static_cast<const SfxStringItem&>(pReqArgs->
+ Get( SID_INSERT_MATRIX )).GetValue();
+ ScDocument& rDoc = GetViewData().GetDocument();
+ pTabViewShell->EnterMatrix( aStr, rDoc.GetGrammar() );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case FID_INPUTLINE_ENTER:
+ case FID_INPUTLINE_BLOCK:
+ case FID_INPUTLINE_MATRIX:
+ {
+ if( pReqArgs == nullptr ) //XXX temporary HACK to avoid GPF
+ break;
+
+ const ScInputStatusItem* pStatusItem
+ = static_cast<const ScInputStatusItem*>(&pReqArgs->
+ Get( FID_INPUTLINE_STATUS ));
+
+ const ScAddress& aCursorPos = pStatusItem->GetPos();
+ const OUString& aString = pStatusItem->GetString();
+ const EditTextObject* pData = pStatusItem->GetEditData();
+
+ if (pData)
+ {
+ if (nSlot == FID_INPUTLINE_BLOCK)
+ {
+ pTabViewShell->EnterBlock( aString, pData );
+ }
+ else if ( !aString.isEmpty() && ( aString[0] == '=' || aString[0] == '+' || aString[0] == '-' ) )
+ {
+ pTabViewShell->EnterData( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(),
+ aString, pData, true /*bMatrixExpand*/);
+ }
+ else
+ {
+ pTabViewShell->EnterData(aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), *pData);
+ }
+ }
+ else
+ {
+ if (nSlot == FID_INPUTLINE_ENTER)
+ {
+ if (
+ aCursorPos.Col() == GetViewData().GetCurX() &&
+ aCursorPos.Row() == GetViewData().GetCurY() &&
+ aCursorPos.Tab() == GetViewData().GetTabNo()
+ )
+ {
+ SfxStringItem aItem( SID_ENTER_STRING, aString );
+
+ const SfxPoolItem* aArgs[2];
+ aArgs[0] = &aItem;
+ aArgs[1] = nullptr;
+ rBindings.Execute( SID_ENTER_STRING, aArgs );
+ }
+ else
+ {
+ pTabViewShell->EnterData( aCursorPos.Col(),
+ aCursorPos.Row(),
+ aCursorPos.Tab(),
+ aString, nullptr,
+ true /*bMatrixExpand*/);
+ rReq.Done();
+ }
+ }
+ else if (nSlot == FID_INPUTLINE_BLOCK)
+ {
+ pTabViewShell->EnterBlock( aString, nullptr );
+ rReq.Done();
+ }
+ else
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ pTabViewShell->EnterMatrix( aString, rDoc.GetGrammar() );
+ rReq.Done();
+ }
+ }
+
+ pTabViewShell->SetAutoSpellData(
+ aCursorPos.Col(), aCursorPos.Row(), pStatusItem->GetMisspellRanges());
+
+ // no GrabFocus here, as otherwise on a Mac the tab jumps before the
+ // sideview, when the input was not finished
+ // (GrabFocus is called in KillEditView)
+ }
+ break;
+
+ case SID_OPENDLG_FUNCTION:
+ {
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (comphelper::LibreOfficeKit::isActive()
+ && pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ // not set the dialog id in the mobile case or we would
+ // not be able to get cell address pasted in the edit view
+ // by just tapping on them
+ lcl_lokGetWholeFunctionList();
+ }
+ else
+ {
+ sal_uInt16 nId = SID_OPENDLG_FUNCTION;
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+ bool bVis = comphelper::LibreOfficeKit::isActive() || pWnd == nullptr;
+ pScMod->SetRefDialog( nId, bVis );
+ }
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_OPENDLG_CONSOLIDATE:
+ {
+ sal_uInt16 nId = ScConsolidateDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_EASY_CONDITIONAL_FORMAT_DIALOG:
+ {
+ if (pReqArgs != nullptr)
+ {
+ const SfxPoolItem* pFormat;
+ if (pReqArgs->HasItem( FN_PARAM_1, &pFormat))
+ {
+ sal_Int16 nFormat = static_cast<const SfxInt16Item*>(pFormat)->GetValue();
+ sal_uInt16 nId = sc::ConditionalFormatEasyDialogWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrame = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWindow = rViewFrame.GetChildWindow( nId );
+ GetViewData().GetDocument().SetEasyConditionalFormatDialogData(std::make_unique<ScConditionMode>(static_cast<ScConditionMode>(nFormat)));
+
+ pScMod->SetRefDialog( nId, pWindow == nullptr );
+ }
+ }
+ }
+ break;
+
+ case FID_CELL_FORMAT:
+ {
+ if ( pReqArgs != nullptr )
+ {
+
+ // set cell attribute without dialog:
+
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aEmptySet( *pReqArgs->GetPool() );
+
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *pReqArgs->GetPool() );
+
+ const SfxPoolItem* pAttr = nullptr;
+ sal_uInt16 nWhich = 0;
+
+ for ( nWhich=ATTR_PATTERN_START; nWhich<=ATTR_PATTERN_END; nWhich++ )
+ if ( pReqArgs->GetItemState( nWhich, true, &pAttr ) == SfxItemState::SET )
+ aNewSet.Put( *pAttr );
+
+ pTabViewShell->ApplyAttributes( aNewSet, aEmptySet );
+
+ rReq.Done();
+ }
+ else
+ {
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "" );
+ }
+ }
+ break;
+
+ case SID_ENABLE_HYPHENATION:
+ pTabViewShell->ExecuteCellFormatDlg(rReq, "alignment");
+ break;
+
+ case SID_PROPERTY_PANEL_CELLTEXT_DLG:
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "font" );
+ break;
+
+ case SID_CELL_FORMAT_BORDER:
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "borders" );
+ break;
+
+ case SID_CHAR_DLG_EFFECT:
+ pTabViewShell->ExecuteCellFormatDlg( rReq, "fonteffects" );
+ break;
+
+ case SID_OPENDLG_SOLVE:
+ {
+ sal_uInt16 nId = ScSolverDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_OPENDLG_OPTSOLVER:
+ {
+ sal_uInt16 nId = ScOptSolverDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_OPENDLG_TABOP:
+ {
+ sal_uInt16 nId = ScTabOpDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = pTabViewShell->GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_SCENARIOS:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ if ( rDoc.IsScenario(nTab) )
+ {
+ rMark.MarkToMulti();
+ if ( rMark.IsMultiMarked() )
+ {
+
+ bool bExtend = rReq.IsAPI();
+ if (!bExtend)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_UPDATE_SCENARIO)));
+ xQueryBox->set_default_response(RET_YES);
+ bExtend = xQueryBox->run() == RET_YES;
+ }
+
+ if (bExtend)
+ {
+ pTabViewShell->ExtendScenario();
+ rReq.Done();
+ }
+ }
+ else if( ! rReq.IsAPI() )
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pTabViewShell->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_NOAREASELECTED)));
+ xErrorBox->run();
+ }
+ }
+ else
+ {
+ rMark.MarkToMulti();
+ if ( rMark.IsMultiMarked() )
+ {
+ SCTAB i=1;
+ OUString aBaseName;
+ OUString aName;
+ Color aColor;
+ ScScenarioFlags nFlags;
+
+ OUString aTmp;
+ rDoc.GetName(nTab, aTmp);
+ aBaseName = aTmp + "_" + ScResId(STR_SCENARIO) + "_";
+
+ // first test, if the prefix is recognised as valid,
+ // else avoid only doubles
+ bool bPrefix = ScDocument::ValidTabName( aBaseName );
+ OSL_ENSURE(bPrefix, "invalid sheet name");
+
+ while ( rDoc.IsScenario(nTab+i) )
+ i++;
+
+ bool bValid;
+ SCTAB nDummy;
+ do
+ {
+ aName = aBaseName + OUString::number( i );
+ if (bPrefix)
+ bValid = rDoc.ValidNewTabName( aName );
+ else
+ bValid = !rDoc.GetTable( aName, nDummy );
+ ++i;
+ }
+ while ( !bValid && i <= MAXTAB + 2 );
+
+ if ( pReqArgs != nullptr )
+ {
+ OUString aArgName;
+ OUString aArgComment;
+ if ( const SfxStringItem* pItem = pReqArgs->GetItemIfSet( SID_SCENARIOS ) )
+ aArgName = pItem->GetValue();
+ if ( const SfxStringItem* pItem = pReqArgs->GetItemIfSet( SID_NEW_TABLENAME ) )
+ aArgComment = pItem->GetValue();
+
+ aColor = COL_LIGHTGRAY; // Default
+ nFlags = ScScenarioFlags::NONE; // not TwoWay
+
+ pTabViewShell->MakeScenario( aArgName, aArgComment, aColor, nFlags );
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ bool bSheetProtected = rDoc.IsTabProtected(nTab);
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNewScenarioDlg> pNewDlg(pFact->CreateScNewScenarioDlg(pTabViewShell->GetFrameWeld(), aName, false, bSheetProtected));
+ if ( pNewDlg->Execute() == RET_OK )
+ {
+ OUString aComment;
+ pNewDlg->GetScenarioData( aName, aComment, aColor, nFlags );
+ pTabViewShell->MakeScenario( aName, aComment, aColor, nFlags );
+
+ rReq.AppendItem( SfxStringItem( SID_SCENARIOS, aName ) );
+ rReq.AppendItem( SfxStringItem( SID_NEW_TABLENAME, aComment ) );
+ rReq.Done();
+ }
+ }
+ }
+ else if( ! rReq.IsAPI() )
+ {
+ pTabViewShell->ErrorMessage(STR_ERR_NEWSCENARIO);
+ }
+ }
+ }
+ break;
+
+ case SID_SELECTALL:
+ {
+ pTabViewShell->SelectAll();
+ rReq.Done();
+ }
+ break;
+
+ case FID_ROW_HEIGHT:
+ {
+ const SfxPoolItem* pRow;
+ const SfxUInt16Item* pHeight;
+ sal_uInt16 nHeight;
+
+ if ( pReqArgs && (pHeight = pReqArgs->GetItemIfSet( FID_ROW_HEIGHT )) &&
+ pReqArgs->HasItem( FN_PARAM_1, &pRow ) )
+ {
+ std::vector<sc::ColRowSpan> aRanges;
+ SCCOLROW nRow = static_cast<const SfxInt32Item*>(pRow)->GetValue() - 1;
+ nHeight = pHeight->GetValue();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if ( rMark.IsRowMarked( static_cast<SCROW>(nRow) ) )
+ {
+ aRanges = rMark.GetMarkedRowSpans();
+ }
+ else
+ {
+ aRanges.emplace_back(nRow, nRow);
+ }
+
+ pTabViewShell->SetWidthOrHeight(false, aRanges, SC_SIZE_DIRECT, o3tl::toTwips(nHeight, o3tl::Length::mm100));
+ }
+ else if ( pReqArgs && (pHeight = pReqArgs->GetItemIfSet( FID_ROW_HEIGHT )) )
+ {
+ nHeight = pHeight->GetValue();
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_DIRECT,
+ o3tl::toTwips(nHeight, o3tl::Length::mm100));
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ ScViewData& rData = GetViewData();
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+ sal_uInt16 nCurHeight = rData.GetDocument().
+ GetRowHeight( rData.GetCurY(),
+ rData.GetTabNo() );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "RowHeightDialog", nCurHeight,
+ rData.GetDocument().GetSheetOptimalMinRowHeight(rData.GetTabNo()),
+ eMetric, 2, MAX_ROW_HEIGHT));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if (nResult == RET_OK)
+ {
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_ROW_HEIGHT);
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_DIRECT, static_cast<sal_uInt16>(nVal) );
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_ROW_HEIGHT, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal)) ) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_ROW_OPT_HEIGHT:
+ {
+ if ( pReqArgs )
+ {
+ const SfxUInt16Item& rUInt16Item = pReqArgs->Get( FID_ROW_OPT_HEIGHT );
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_OPTIMAL,
+ o3tl::toTwips(rUInt16Item.GetValue(), o3tl::Length::mm100) );
+ ScGlobal::nLastRowHeightExtra = rUInt16Item.GetValue();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "OptimalRowHeightDialog",
+ ScGlobal::nLastRowHeightExtra, 0, eMetric, 2, MAX_EXTRA_HEIGHT));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_ROW_OPT_HEIGHT);
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_OPTIMAL, static_cast<sal_uInt16>(nVal) );
+ ScGlobal::nLastRowHeightExtra = nVal;
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_ROW_OPT_HEIGHT, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal)) ) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_COL_WIDTH:
+ {
+ const SfxPoolItem* pColumn;
+ const SfxUInt16Item* pWidth;
+ sal_uInt16 nWidth;
+
+ if ( pReqArgs && (pWidth = pReqArgs->GetItemIfSet( FID_COL_WIDTH )) &&
+ pReqArgs->HasItem( FN_PARAM_1, &pColumn ) )
+ {
+ std::vector<sc::ColRowSpan> aRanges;
+ SCCOLROW nColumn = static_cast<const SfxUInt16Item*>(pColumn)->GetValue() - 1;
+ nWidth = pWidth->GetValue();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if ( rMark.IsColumnMarked( static_cast<SCCOL>(nColumn) ) )
+ {
+ aRanges = rMark.GetMarkedColSpans();
+ }
+ else
+ {
+ aRanges.emplace_back(nColumn, nColumn);
+ }
+
+ pTabViewShell->SetWidthOrHeight(true, aRanges, SC_SIZE_DIRECT, o3tl::toTwips(nWidth, o3tl::Length::mm100));
+ }
+ else if ( pReqArgs && (pWidth = pReqArgs->GetItemIfSet( FID_COL_WIDTH )) )
+ {
+ nWidth = pWidth->GetValue();
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_DIRECT,
+ o3tl::toTwips(nWidth, o3tl::Length::mm100));
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+ ScViewData& rData = GetViewData();
+ sal_uInt16 nCurHeight = rData.GetDocument().
+ GetColWidth( rData.GetCurX(),
+ rData.GetTabNo() );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "ColWidthDialog", nCurHeight,
+ STD_COL_WIDTH, eMetric, 2, MAX_COL_WIDTH));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ if ( nResult == RET_OK )
+ {
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_COL_WIDTH);
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_DIRECT, static_cast<sal_uInt16>(nVal) );
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_COL_WIDTH, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal))) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_COL_OPT_WIDTH:
+ {
+ if ( pReqArgs )
+ {
+ const SfxUInt16Item& rUInt16Item = pReqArgs->Get( FID_COL_OPT_WIDTH );
+
+ // #101390#; the value of the macro is in HMM so use convertMm100ToTwip to convert
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_OPTIMAL,
+ o3tl::toTwips(rUInt16Item.GetValue(), o3tl::Length::mm100) );
+ ScGlobal::nLastColWidthExtra = rUInt16Item.GetValue();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ FieldUnit eMetric = SC_MOD()->GetAppOptions().GetAppMetric();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScMetricInputDlg> pDlg(pFact->CreateScMetricInputDlg(
+ pTabViewShell->GetFrameWeld(), "OptimalColWidthDialog",
+ ScGlobal::nLastColWidthExtra, STD_EXTRA_WIDTH, eMetric, 2, MAX_EXTRA_WIDTH));
+
+ pDlg->StartExecuteAsync([pDlg, pTabViewShell](sal_Int32 nResult){
+ SfxRequest pRequest(pTabViewShell->GetViewFrame(), FID_COL_OPT_WIDTH);
+ if ( nResult == RET_OK )
+ {
+ tools::Long nVal = pDlg->GetInputValue();
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_OPTIMAL, static_cast<sal_uInt16>(nVal) );
+ ScGlobal::nLastColWidthExtra = nVal;
+
+ // #101390#; the value of the macro should be in HMM so use TwipsToEvenHMM to convert
+ pRequest.AppendItem( SfxUInt16Item( FID_COL_OPT_WIDTH, static_cast<sal_uInt16>(TwipsToEvenHMM(nVal)) ) );
+ pRequest.Done();
+ }
+ pDlg->disposeOnce();
+ });
+ }
+ }
+ break;
+
+ case FID_COL_OPT_DIRECT:
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_OPTIMAL, STD_EXTRA_WIDTH );
+ rReq.Done();
+ break;
+
+ case FID_ROW_HIDE:
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_DIRECT, 0 );
+ rReq.Done();
+ break;
+ case FID_ROW_SHOW:
+ pTabViewShell->SetMarkedWidthOrHeight( false, SC_SIZE_SHOW, 0 );
+ rReq.Done();
+ break;
+ case FID_COL_HIDE:
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_DIRECT, 0 );
+ rReq.Done();
+ break;
+ case FID_COL_SHOW:
+ pTabViewShell->SetMarkedWidthOrHeight( true, SC_SIZE_SHOW, 0 );
+ rReq.Done();
+ break;
+
+ case SID_CELL_FORMAT_RESET:
+ {
+ pTabViewShell->DeleteContents( InsertDeleteFlags::HARDATTR | InsertDeleteFlags::EDITATTR );
+ rReq.Done();
+ }
+ break;
+
+ case FID_MERGE_ON:
+ case FID_MERGE_OFF:
+ case FID_MERGE_TOGGLE:
+ {
+ if ( !GetViewData().GetDocument().GetChangeTrack() )
+ {
+ // test whether to merge or to split
+ bool bMerge = false;
+ bool bCenter = false;
+ switch( nSlot )
+ {
+ case FID_MERGE_ON:
+ bMerge = true;
+ break;
+ case FID_MERGE_OFF:
+ bMerge = false;
+ break;
+ case FID_MERGE_TOGGLE:
+ {
+ bCenter = true;
+ std::unique_ptr<SfxPoolItem> pItem;
+ if( rBindings.QueryState( nSlot, pItem ) >= SfxItemState::DEFAULT )
+ bMerge = !static_cast< SfxBoolItem* >( pItem.get() )->GetValue();
+ }
+ break;
+ }
+
+ if( bMerge )
+ {
+ // merge - check if to move contents of covered cells
+ bool bMoveContents = false;
+ bool bApi = rReq.IsAPI();
+ const SfxPoolItem* pItem;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ {
+ assert(dynamic_cast<const SfxBoolItem*>( pItem) && "wrong item");
+ bMoveContents = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+
+ pTabViewShell->MergeCells( bApi, bMoveContents, bCenter, nSlot );
+ }
+ else
+ {
+ // split cells
+ if (pTabViewShell->RemoveMerge())
+ {
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case SID_AUTOFORMAT:
+ {
+ weld::Window* pDlgParent = pTabViewShell->GetFrameWeld();
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ pTabViewShell->MarkDataArea();
+
+ GetViewData().GetSimpleArea( nStartCol,nStartRow,nStartTab,
+ nEndCol,nEndRow,nEndTab );
+
+ if ( ( std::abs(nEndCol-nStartCol) > 1 )
+ && ( std::abs(nEndRow-nStartRow) > 1 ) )
+ {
+ if ( pReqArgs )
+ {
+ const SfxStringItem& rNameItem = pReqArgs->Get( SID_AUTOFORMAT );
+ ScAutoFormat* pFormat = ScGlobal::GetOrCreateAutoFormat();
+ ScAutoFormat::const_iterator it = pFormat->find(rNameItem.GetValue());
+ ScAutoFormat::const_iterator itBeg = pFormat->begin();
+ size_t nIndex = std::distance(itBeg, it);
+
+ pTabViewShell->AutoFormat( nIndex );
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ else
+ {
+ ScGlobal::ClearAutoFormat();
+ std::unique_ptr<ScAutoFormatData> pNewEntry(pTabViewShell->CreateAutoFormatData());
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScAutoFormatDlg> pDlg(pFact->CreateScAutoFormatDlg(pDlgParent, ScGlobal::GetOrCreateAutoFormat(), pNewEntry.get(), GetViewData()));
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScEditableTester aTester( pTabViewShell );
+ if ( !aTester.IsEditable() )
+ {
+ pTabViewShell->ErrorMessage(aTester.GetMessageId());
+ }
+ else
+ {
+ pTabViewShell->AutoFormat( pDlg->GetIndex() );
+
+ rReq.AppendItem( SfxStringItem( SID_AUTOFORMAT, pDlg->GetCurrFormatName() ) );
+ rReq.Done();
+ }
+ }
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pDlgParent,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_INVALID_AFAREA)));
+ xErrorBox->run();
+ }
+ }
+ break;
+
+ case SID_CANCEL:
+ {
+ if (GetViewData().HasEditView(GetViewData().GetActivePart()))
+ pScMod->InputCancelHandler();
+ else if (pTabViewShell->HasPaintBrush())
+ pTabViewShell->ResetBrushDocument(); // abort format paint brush
+ else if (pTabViewShell->HasHintWindow())
+ pTabViewShell->RemoveHintWindow();
+ else if( ScViewUtil::IsFullScreen( *pTabViewShell ) )
+ ScViewUtil::SetFullScreen( *pTabViewShell, false );
+ else
+ {
+ // TODO/LATER: when is this code executed?
+ pTabViewShell->Escape();
+ }
+ }
+ break;
+
+ case SID_ACCEPT_FORMULA:
+ {
+ if (GetViewData().HasEditView(GetViewData().GetActivePart()))
+ pScMod->InputEnterHandler();
+ }
+ break;
+
+ case SID_START_FORMULA:
+ {
+ ScInputHandler* pInputHandler = pScMod->GetInputHdl();
+ if (pInputHandler && pInputHandler->GetInputWindow())
+ pInputHandler->GetInputWindow()->StartFormula();
+ }
+ break;
+
+ case SID_DATA_SELECT:
+ pTabViewShell->StartDataSelect();
+ break;
+
+ case SID_DETECTIVE_FILLMODE:
+ {
+ bool bOldMode = pTabViewShell->IsAuditShell();
+ pTabViewShell->SetAuditShell( !bOldMode );
+ pTabViewShell->Invalidate( nSlot );
+ }
+ break;
+
+ case FID_INPUTLINE_STATUS:
+ OSL_FAIL("Execute from InputLine status");
+ break;
+
+ case SID_STATUS_DOCPOS:
+ // Launch navigator.
+ GetViewData().GetDispatcher().Execute(
+ SID_NAVIGATOR, SfxCallMode::SYNCHRON|SfxCallMode::RECORD );
+ break;
+
+ case SID_MARKAREA:
+ // called from Basic at the hidden view to select a range in the visible view
+ OSL_FAIL("old slot SID_MARKAREA");
+ break;
+
+ default:
+ OSL_FAIL("ScCellShell::Execute: unknown slot");
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cellsh4.cxx b/sc/source/ui/view/cellsh4.cxx
new file mode 100644
index 0000000000..bacbf2b98f
--- /dev/null
+++ b/sc/source/ui/view/cellsh4.cxx
@@ -0,0 +1,522 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/request.hxx>
+#include <osl/diagnose.h>
+
+#include <cellsh.hxx>
+#include <tabvwsh.hxx>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <document.hxx>
+#include <officecfg/Office/Calc.hxx>
+#include <sc.hrc>
+
+void ScCellShell::ExecuteCursor( SfxRequest& rReq )
+{
+ ScViewData& rData = GetViewData();
+ ScTabViewShell* pTabViewShell = rData.GetViewShell();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ SCCOLROW nRepeat = 1;
+ bool bSel = false;
+ bool bKeep = false;
+
+ if ( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FN_PARAM_1, &pItem))
+ nRepeat = static_cast<SCCOLROW>(static_cast<const SfxInt16Item*>(pItem)->GetValue());
+ if (pReqArgs->HasItem(FN_PARAM_2, &pItem))
+ bSel = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+ else
+ {
+ // evaluate locked selection mode
+
+ sal_uInt16 nLocked = pTabViewShell->GetLockedModifiers();
+ if ( nLocked & KEY_SHIFT )
+ bSel = true; // EXT
+ else if ( nLocked & KEY_MOD1 )
+ {
+ // ADD mode: keep the selection, start a new block when marking with shift again
+ bKeep = true;
+ }
+ }
+
+ if (bSel)
+ {
+ switch (nSlotId)
+ {
+ case SID_CURSORDOWN:
+ rReq.SetSlot(SID_CURSORDOWN_SEL);
+ break;
+ case SID_CURSORUP:
+ rReq.SetSlot(SID_CURSORUP_SEL);
+ break;
+ case SID_CURSORRIGHT:
+ rReq.SetSlot(SID_CURSORRIGHT_SEL);
+ break;
+ case SID_CURSORLEFT:
+ rReq.SetSlot(SID_CURSORLEFT_SEL);
+ break;
+ case SID_CURSORPAGEDOWN:
+ rReq.SetSlot(SID_CURSORPAGEDOWN_SEL);
+ break;
+ case SID_CURSORPAGEUP:
+ rReq.SetSlot(SID_CURSORPAGEUP_SEL);
+ break;
+ case SID_CURSORBLKDOWN:
+ rReq.SetSlot(SID_CURSORBLKDOWN_SEL);
+ break;
+ case SID_CURSORBLKUP:
+ rReq.SetSlot(SID_CURSORBLKUP_SEL);
+ break;
+ case SID_CURSORBLKRIGHT:
+ rReq.SetSlot(SID_CURSORBLKRIGHT_SEL);
+ break;
+ case SID_CURSORBLKLEFT:
+ rReq.SetSlot(SID_CURSORBLKLEFT_SEL);
+ break;
+ default:
+ ;
+ }
+ ExecuteCursorSel(rReq);
+ return;
+ }
+
+ SCCOLROW nRTLSign = 1;
+ if ( rData.GetDocument().IsLayoutRTL( rData.GetTabNo() ) )
+ {
+ //! evaluate cursor movement option?
+ nRTLSign = -1;
+ }
+
+ // once extra, so that the cursor will not be painted too often with ExecuteInputDirect:
+ pTabViewShell->HideAllCursors();
+
+ // #i123629#
+ if( pTabViewShell->GetCurObjectSelectionType() == OST_Editing )
+ pTabViewShell->SetForceFocusOnCurCell(true);
+ else
+ pTabViewShell->SetForceFocusOnCurCell(false);
+
+ // If ScrollLock key is active, cell cursor stays on the current cell while
+ // scrolling the grid.
+ bool bScrollLock = false;
+ // tdf#112876 - allow to disable for special keyboards
+ if (officecfg::Office::Calc::Input::UseScrollLock::get())
+ {
+ KeyIndicatorState eState = pFrameWin->GetIndicatorState();
+ if (eState & KeyIndicatorState::SCROLLLOCK)
+ bScrollLock = true;
+ }
+ //OS: once for all should do, however!
+ pTabViewShell->ExecuteInputDirect();
+ switch ( nSlotId )
+ {
+ case SID_CURSORDOWN:
+ if (bScrollLock)
+ pTabViewShell->ScrollY( nRepeat, SC_SPLIT_BOTTOM );
+ else
+ pTabViewShell->MoveCursorRel( 0, nRepeat, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKDOWN:
+ pTabViewShell->MoveCursorArea( 0, nRepeat, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORUP:
+ if (bScrollLock)
+ pTabViewShell->ScrollY( -nRepeat, SC_SPLIT_BOTTOM);
+ else
+ pTabViewShell->MoveCursorRel( 0, -nRepeat, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKUP:
+ pTabViewShell->MoveCursorArea( 0, -nRepeat, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORLEFT:
+ if (bScrollLock)
+ pTabViewShell->ScrollX( static_cast<SCCOL>(-nRepeat * nRTLSign), SC_SPLIT_LEFT);
+ else
+ pTabViewShell->MoveCursorRel( static_cast<SCCOL>(-nRepeat * nRTLSign), 0, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKLEFT:
+ pTabViewShell->MoveCursorArea( static_cast<SCCOL>(-nRepeat * nRTLSign), 0, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORRIGHT:
+ if (bScrollLock)
+ pTabViewShell->ScrollX( static_cast<SCCOL>(nRepeat * nRTLSign), SC_SPLIT_LEFT);
+ else
+ pTabViewShell->MoveCursorRel( static_cast<SCCOL>(nRepeat * nRTLSign), 0, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORBLKRIGHT:
+ pTabViewShell->MoveCursorArea( static_cast<SCCOL>(nRepeat * nRTLSign), 0, SC_FOLLOW_JUMP, bSel, bKeep, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORPAGEDOWN:
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( 0, nRepeat, nPageX, nPageY);
+ pTabViewShell->ScrollY( nPageY, SC_SPLIT_BOTTOM);
+ }
+ else
+ pTabViewShell->MoveCursorPage( 0, nRepeat, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ case SID_CURSORPAGEUP:
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( 0, nRepeat, nPageX, nPageY);
+ pTabViewShell->ScrollY( -nPageY, SC_SPLIT_BOTTOM);
+ }
+ else
+ pTabViewShell->MoveCursorPage( 0, -nRepeat, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ case SID_CURSORPAGERIGHT_: //XXX !!!
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( static_cast<SCCOL>(nRepeat), 0, nPageX, nPageY);
+ pTabViewShell->ScrollX( nPageX, SC_SPLIT_LEFT);
+ }
+ else
+ pTabViewShell->MoveCursorPage( static_cast<SCCOL>(nRepeat), 0, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ case SID_CURSORPAGELEFT_: //XXX !!!
+ if (bScrollLock)
+ {
+ SCCOL nPageX;
+ SCROW nPageY;
+ pTabViewShell->GetPageMoveEndPosition( static_cast<SCCOL>(nRepeat), 0, nPageX, nPageY);
+ pTabViewShell->ScrollX( -nPageX, SC_SPLIT_LEFT);
+ }
+ else
+ pTabViewShell->MoveCursorPage( static_cast<SCCOL>(-nRepeat), 0, SC_FOLLOW_FIX, bSel, bKeep );
+ break;
+
+ default:
+ OSL_FAIL("Unknown message in ViewShell (Cursor)");
+ return;
+ }
+
+ pTabViewShell->ShowAllCursors();
+
+ rReq.AppendItem( SfxInt16Item(FN_PARAM_1, static_cast<sal_Int16>(nRepeat)) );
+ rReq.AppendItem( SfxBoolItem(FN_PARAM_2, bSel) );
+ rReq.Done();
+}
+
+void ScCellShell::GetStateCursor( SAL_UNUSED_PARAMETER SfxItemSet& /* rSet */ )
+{
+}
+
+void ScCellShell::ExecuteCursorSel( SfxRequest& rReq )
+{
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ ScTabViewShell* pViewShell = GetViewData().GetViewShell();
+ ScInputHandler* pInputHdl = pViewShell->GetInputHandler();
+ pViewShell->HideAllCursors();
+ if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ // the current cell is in edit mode. Commit the text before moving on.
+ pViewShell->ExecuteInputDirect();
+ }
+
+ SCCOLROW nRepeat = 1;
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ // get repetition
+ if ( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FN_PARAM_1, &pItem))
+ nRepeat = static_cast<SCCOLROW>(static_cast<const SfxInt16Item*>(pItem)->GetValue());
+ }
+
+ SCROW nMovY = nRepeat;
+ // Horizontal direction depends on whether or not the UI language is RTL.
+ SCCOL nMovX = nRepeat;
+ if (GetViewData().GetDocument().IsLayoutRTL(GetViewData().GetTabNo()))
+ {
+ // mirror horizontal movement for right-to-left mode.
+ nMovX = -nRepeat;
+ }
+
+ switch (nSlotId)
+ {
+ case SID_CURSORDOWN_SEL:
+ pViewShell->ExpandBlock(0, nMovY, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORUP_SEL:
+ pViewShell->ExpandBlock(0, -nMovY, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORRIGHT_SEL:
+ pViewShell->ExpandBlock(nMovX, 0, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORLEFT_SEL:
+ pViewShell->ExpandBlock(-nMovX, 0, SC_FOLLOW_LINE);
+ break;
+ case SID_CURSORPAGEUP_SEL:
+ pViewShell->ExpandBlockPage(0, -nMovY);
+ break;
+ case SID_CURSORPAGEDOWN_SEL:
+ pViewShell->ExpandBlockPage(0, nMovY);
+ break;
+ case SID_CURSORPAGERIGHT_SEL:
+ pViewShell->ExpandBlockPage(nMovX, 0);
+ break;
+ case SID_CURSORPAGELEFT_SEL:
+ pViewShell->ExpandBlockPage(-nMovX, 0);
+ break;
+ case SID_CURSORBLKDOWN_SEL:
+ pViewShell->ExpandBlockArea(0, nMovY);
+ break;
+ case SID_CURSORBLKUP_SEL:
+ pViewShell->ExpandBlockArea(0, -nMovY);
+ break;
+ case SID_CURSORBLKRIGHT_SEL:
+ pViewShell->ExpandBlockArea(nMovX , 0);
+ break;
+ case SID_CURSORBLKLEFT_SEL:
+ pViewShell->ExpandBlockArea(-nMovX, 0);
+ break;
+ default:
+ ;
+ }
+ pViewShell->ShowAllCursors();
+
+ rReq.AppendItem( SfxInt16Item(FN_PARAM_1,static_cast<sal_Int16>(nRepeat)) );
+ rReq.Done();
+}
+
+void ScCellShell::ExecuteMove( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ if(nSlotId != SID_CURSORTOPOFSCREEN && nSlotId != SID_CURSORENDOFSCREEN)
+ pTabViewShell->ExecuteInputDirect();
+ switch ( nSlotId )
+ {
+ case SID_NEXT_TABLE:
+ case SID_NEXT_TABLE_SEL:
+ pTabViewShell->SelectNextTab( 1, (nSlotId == SID_NEXT_TABLE_SEL) );
+ break;
+
+ case SID_PREV_TABLE:
+ case SID_PREV_TABLE_SEL:
+ pTabViewShell->SelectNextTab( -1, (nSlotId == SID_PREV_TABLE_SEL) );
+ break;
+
+ // cursor movements in range do not originate from Basic,
+ // because the ScSbxRange-object changes the marking at input
+
+ case SID_NEXT_UNPROTECT:
+ pTabViewShell->FindNextUnprot( false, !rReq.IsAPI() );
+ break;
+
+ case SID_PREV_UNPROTECT:
+ pTabViewShell->FindNextUnprot( true, !rReq.IsAPI() );
+ break;
+
+ case SID_CURSORENTERUP:
+ if (rReq.IsAPI())
+ pTabViewShell->MoveCursorRel( 0, -1, SC_FOLLOW_LINE, false );
+ else
+ pTabViewShell->MoveCursorEnter( true );
+ break;
+
+ case SID_CURSORENTERDOWN:
+ if (rReq.IsAPI())
+ pTabViewShell->MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
+ else
+ pTabViewShell->MoveCursorEnter( false );
+ break;
+
+ case SID_SELECT_COL:
+ {
+ const SfxPoolItem* pColItem;
+ const SfxPoolItem* pModifierItem;
+ if ( pReqArgs && pReqArgs->HasItem( FN_PARAM_1, &pColItem ) &&
+ pReqArgs->HasItem( FN_PARAM_2, &pModifierItem ) )
+ {
+ SCCOL nCol = static_cast<SCCOL>(static_cast<const SfxInt32Item*>(pColItem)->GetValue());
+ sal_Int16 nModifier = static_cast<const SfxInt16Item*>(pModifierItem)->GetValue();
+
+ pTabViewShell->MarkColumns( nCol, nModifier );
+ }
+ else
+ pTabViewShell->MarkColumns();
+ }
+ break;
+
+ case SID_SELECT_ROW:
+ {
+ const SfxPoolItem* pRowItem;
+ const SfxPoolItem* pModifierItem;
+ if ( pReqArgs && pReqArgs->HasItem( FN_PARAM_1, &pRowItem ) &&
+ pReqArgs->HasItem( FN_PARAM_2, &pModifierItem ) )
+ {
+ SCROW nRow = static_cast<SCROW>(static_cast<const SfxInt32Item*>(pRowItem)->GetValue());
+ sal_Int16 nModifier = static_cast<const SfxInt16Item*>(pModifierItem)->GetValue();
+
+ pTabViewShell->MarkRows( nRow, nModifier );
+ }
+ else
+ pTabViewShell->MarkRows();
+ }
+ break;
+
+ case SID_SELECT_NONE:
+ pTabViewShell->Unmark();
+ break;
+
+ case SID_ALIGNCURSOR:
+ pTabViewShell->AlignToCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), SC_FOLLOW_JUMP );
+ break;
+
+ case SID_MARKDATAAREA:
+ pTabViewShell->MarkDataArea();
+ break;
+
+ case SID_MARKARRAYFORMULA:
+ pTabViewShell->MarkMatrixFormula();
+ break;
+
+ case SID_SETINPUTMODE:
+ SC_MOD()->SetInputMode( SC_INPUT_TABLE );
+ break;
+
+ case SID_FOCUS_INPUTLINE:
+ {
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( pTabViewShell );
+ if (pHdl)
+ {
+ ScInputWindow* pWin = pHdl->GetInputWindow();
+ if (pWin)
+ pWin->SwitchToTextWin();
+ }
+ }
+ break;
+
+ case SID_CURSORTOPOFSCREEN:
+ pTabViewShell->MoveCursorScreen( 0, -1, SC_FOLLOW_LINE, false );
+ break;
+
+ case SID_CURSORENDOFSCREEN:
+ pTabViewShell->MoveCursorScreen( 0, 1, SC_FOLLOW_LINE, false );
+ break;
+
+ default:
+ OSL_FAIL("Unknown message in ViewShell (Cursor)");
+ return;
+ }
+
+ rReq.Done();
+}
+
+void ScCellShell::ExecutePageSel( SfxRequest& rReq )
+{
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ switch ( nSlotId )
+ {
+ case SID_CURSORHOME_SEL: rReq.SetSlot( SID_CURSORHOME ); break;
+ case SID_CURSOREND_SEL: rReq.SetSlot( SID_CURSOREND ); break;
+ case SID_CURSORTOPOFFILE_SEL: rReq.SetSlot( SID_CURSORTOPOFFILE ); break;
+ case SID_CURSORENDOFFILE_SEL: rReq.SetSlot( SID_CURSORENDOFFILE ); break;
+ default:
+ OSL_FAIL("Unknown message in ViewShell (ExecutePageSel)");
+ return;
+ }
+ rReq.AppendItem( SfxBoolItem(FN_PARAM_2, true) );
+ ExecuteSlot( rReq, GetInterface() );
+}
+
+void ScCellShell::ExecutePage( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ bool bSel = false;
+ bool bKeep = false;
+
+ if ( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FN_PARAM_2, &pItem))
+ bSel = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+ else
+ {
+ // evaluate locked selection mode
+
+ sal_uInt16 nLocked = pTabViewShell->GetLockedModifiers();
+ if ( nLocked & KEY_SHIFT )
+ bSel = true; // EXT
+ else if ( nLocked & KEY_MOD1 )
+ {
+ // ADD mode: keep the selection, start a new block when marking with shift again
+ bKeep = true;
+ }
+ }
+
+ pTabViewShell->ExecuteInputDirect();
+ switch ( nSlotId )
+ {
+ case SID_CURSORHOME:
+ pTabViewShell->MoveCursorEnd( -1, 0, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSOREND:
+ pTabViewShell->MoveCursorEnd( 1, 0, SC_FOLLOW_JUMP, bSel, bKeep );
+ break;
+
+ case SID_CURSORTOPOFFILE:
+ pTabViewShell->MoveCursorEnd( -1, -1, SC_FOLLOW_LINE, bSel, bKeep );
+ break;
+
+ case SID_CURSORENDOFFILE:
+ pTabViewShell->MoveCursorEnd( 1, 1, SC_FOLLOW_JUMP_END, bSel, bKeep );
+ break;
+
+ default:
+ OSL_FAIL("Unknown message in ViewShell (ExecutePage)");
+ return;
+ }
+
+ rReq.AppendItem( SfxBoolItem(FN_PARAM_2, bSel) );
+ rReq.Done();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/cliputil.cxx b/sc/source/ui/view/cliputil.cxx
new file mode 100644
index 0000000000..9c7d25db10
--- /dev/null
+++ b/sc/source/ui/view/cliputil.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/.
+ */
+
+#include <cliputil.hxx>
+#include <attrib.hxx>
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <transobj.hxx>
+#include <document.hxx>
+#include <dpobject.hxx>
+#include <globstr.hrc>
+#include <clipparam.hxx>
+#include <clipoptions.hxx>
+#include <rangelst.hxx>
+#include <viewutil.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <scitems.hxx>
+
+#include <sfx2/classificationhelper.hxx>
+#include <comphelper/lok.hxx>
+
+namespace
+{
+
+/// Paste only if SfxClassificationHelper recommends so.
+bool lcl_checkClassification(ScDocument* pSourceDoc, const ScDocument& rDestinationDoc)
+{
+ if (!pSourceDoc)
+ return true;
+
+ ScClipOptions* pSourceOptions = pSourceDoc->GetClipOptions();
+ ScDocShell* pDestinationShell = rDestinationDoc.GetDocumentShell();
+ if (!pSourceOptions || !pDestinationShell)
+ return true;
+
+ SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSourceOptions->m_xDocumentProperties, pDestinationShell->getDocProperties());
+ return SfxClassificationHelper::ShowPasteInfo(eResult);
+}
+
+}
+
+void ScClipUtil::PasteFromClipboard( ScViewData& rViewData, ScTabViewShell* pTabViewShell, bool bShowDialog )
+{
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin()));
+ ScDocument& rThisDoc = rViewData.GetDocument();
+ SCCOL nThisCol = rViewData.GetCurX();
+ SCROW nThisRow = rViewData.GetCurY();
+ SCTAB nThisTab = rViewData.GetTabNo();
+ ScDPObject* pDPObj = rThisDoc.GetDPAtCursor( nThisCol, nThisRow, nThisTab );
+
+ if ( pOwnClip && pDPObj )
+ {
+ // paste from Calc into DataPilot table: sort (similar to drag & drop)
+
+ ScDocument* pClipDoc = pOwnClip->GetDocument();
+ SCTAB nSourceTab = pOwnClip->GetVisibleTab();
+
+ SCCOL nClipStartX;
+ SCROW nClipStartY;
+ SCCOL nClipEndX;
+ SCROW nClipEndY;
+ pClipDoc->GetClipStart( nClipStartX, nClipStartY );
+ pClipDoc->GetClipArea( nClipEndX, nClipEndY, true );
+ nClipEndX = nClipEndX + nClipStartX;
+ nClipEndY = nClipEndY + nClipStartY; // GetClipArea returns the difference
+
+ ScRange aSource( nClipStartX, nClipStartY, nSourceTab, nClipEndX, nClipEndY, nSourceTab );
+ bool bDone = pTabViewShell->DataPilotMove( aSource, rViewData.GetCurPos() );
+ if ( !bDone )
+ pTabViewShell->ErrorMessage( STR_ERR_DATAPILOT_INPUT );
+ }
+ else
+ {
+ // normal paste
+ weld::WaitObject aWait( rViewData.GetDialogParent() );
+ if (!pOwnClip)
+ {
+ pTabViewShell->PasteFromSystem();
+ // Anchor To Cell rather than To Page
+ ScDrawView* pDrawView = pTabViewShell->GetScDrawView();
+ if(pDrawView && 1 == pDrawView->GetMarkedObjectCount())
+ {
+ SdrObject* pPickObj = pDrawView->GetMarkedObjectByIndex(0);
+ if(pPickObj)
+ {
+ ScDrawLayer::SetCellAnchoredFromPosition( *pPickObj, rThisDoc, nThisTab, false );
+ }
+ }
+ }
+ else
+ {
+ ScDocument* pClipDoc = pOwnClip->GetDocument();
+ InsertDeleteFlags nFlags = InsertDeleteFlags::ALL;
+ if (pClipDoc->GetClipParam().isMultiRange())
+ // For multi-range paste, we paste values by default.
+ nFlags &= ~InsertDeleteFlags::FORMULA;
+
+ if (lcl_checkClassification(pClipDoc, rThisDoc))
+ pTabViewShell->PasteFromClip( nFlags, pClipDoc,
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ bShowDialog ); // allow warning dialog
+ }
+ }
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool entireColumnOrRowSelected = false;
+ if (pOwnClip)
+ {
+ ScClipParam clipParam = pOwnClip->GetDocument()->GetClipParam();
+ if (clipParam.maRanges.size() > 0)
+ {
+ if (clipParam.maRanges[0].aEnd.Col() == pOwnClip->GetDocument()->MaxCol()
+ || clipParam.maRanges[0].aEnd.Row() == pOwnClip->GetDocument()->MaxRow())
+ {
+ entireColumnOrRowSelected = true;
+ }
+ }
+ }
+ const SfxBoolItem* pItem = rThisDoc.GetAttr(nThisCol, nThisRow, nThisTab, ATTR_LINEBREAK);
+ if (pItem->GetValue() || entireColumnOrRowSelected)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ pTabViewShell, true /* bColumns */, true /* bRows */, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */, true /* bGroups */, nThisTab);
+ }
+ }
+ pTabViewShell->CellContentChanged(); // => PasteFromSystem() ???
+}
+
+bool ScClipUtil::CheckDestRanges(
+ const ScDocument& rDoc, SCCOL nSrcCols, SCROW nSrcRows, const ScMarkData& rMark, const ScRangeList& rDest)
+{
+ for (size_t i = 0, n = rDest.size(); i < n; ++i)
+ {
+ ScRange aTest = rDest[i];
+ // Check for filtered rows in all selected sheets.
+ for (const auto& rTab : rMark)
+ {
+ aTest.aStart.SetTab(rTab);
+ aTest.aEnd.SetTab(rTab);
+ if (ScViewUtil::HasFiltered(aTest, rDoc))
+ {
+ // I don't know how to handle pasting into filtered rows yet.
+ return false;
+ }
+ }
+
+ // Destination range must be an exact multiple of the source range.
+ SCROW nRows = aTest.aEnd.Row() - aTest.aStart.Row() + 1;
+ SCCOL nCols = aTest.aEnd.Col() - aTest.aStart.Col() + 1;
+ SCROW nRowTest = (nRows / nSrcRows) * nSrcRows;
+ SCCOL nColTest = (nCols / nSrcCols) * nSrcCols;
+ if ( rDest.size() > 1 && ( nRows != nRowTest || nCols != nColTest ) )
+ {
+ // Destination range is not a multiple of the source range. Bail out.
+ return false;
+ }
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/colrowba.cxx b/sc/source/ui/view/colrowba.cxx
new file mode 100644
index 0000000000..ab9e82282e
--- /dev/null
+++ b/sc/source/ui/view/colrowba.cxx
@@ -0,0 +1,383 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/fieldvalues.hxx>
+
+#include <colrowba.hxx>
+#include <document.hxx>
+#include <scmod.hxx>
+#include <tabvwsh.hxx>
+#include <appoptio.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <markdata.hxx>
+#include <tabview.hxx>
+#include <columnspanset.hxx>
+
+static OUString lcl_MetricString( tools::Long nTwips, std::u16string_view rText )
+{
+ if ( nTwips <= 0 )
+ return ScResId(STR_TIP_HIDE);
+ else
+ {
+ FieldUnit eUserMet = SC_MOD()->GetAppOptions().GetAppMetric();
+
+ sal_Int64 nUserVal = vcl::ConvertValue( nTwips*100, 1, 2, FieldUnit::TWIP, eUserMet );
+
+ OUString aStr = OUString::Concat(rText) + " "
+ + ScGlobal::getLocaleData().getNum( nUserVal, 2 )
+ + " " + SdrFormatter::GetUnitStr(eUserMet);
+ return aStr;
+ }
+}
+
+ScColBar::ScColBar( vcl::Window* pParent, ScHSplitPos eWhich,
+ ScHeaderFunctionSet* pFuncSet, ScHeaderSelectionEngine* pEng,
+ ScTabView* pTab ) :
+ ScHeaderControl( pParent, pEng, pTab->GetViewData().GetDocument().MaxCol()+1, false, pTab ),
+ meWhich( eWhich ),
+ mpFuncSet( pFuncSet )
+{
+ Show();
+}
+
+ScColBar::~ScColBar()
+{
+}
+
+SCCOLROW ScColBar::GetPos() const
+{
+ return pTabView->GetViewData().GetPosX(meWhich);
+}
+
+sal_uInt16 ScColBar::GetEntrySize( SCCOLROW nEntryNo ) const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ if (rDoc.ColHidden(static_cast<SCCOL>(nEntryNo), nTab))
+ return 0;
+ else
+ return static_cast<sal_uInt16>(ScViewData::ToPixel( rDoc.GetColWidth( static_cast<SCCOL>(nEntryNo), nTab ), rViewData.GetPPTX() ));
+}
+
+OUString ScColBar::GetEntryText( SCCOLROW nEntryNo ) const
+{
+ return pTabView->GetViewData().GetDocument().GetAddressConvention() == formula::FormulaGrammar::CONV_XL_R1C1
+ ? OUString::number(nEntryNo + 1)
+ : ScColToAlpha( static_cast<SCCOL>(nEntryNo) );
+}
+
+void ScColBar::SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize )
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ sal_uInt16 nSizeTwips;
+ ScSizeMode eMode = SC_SIZE_DIRECT;
+ if (nNewSize < 10) nNewSize = 10; // pixels
+
+ if ( nNewSize == HDR_SIZE_OPTIMUM )
+ {
+ nSizeTwips = STD_EXTRA_WIDTH;
+ eMode = SC_SIZE_OPTIMAL;
+ }
+ else
+ nSizeTwips = static_cast<sal_uInt16>( nNewSize / rViewData.GetPPTX() );
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+
+ std::vector<sc::ColRowSpan> aRanges;
+ if ( rMark.IsColumnMarked( static_cast<SCCOL>(nPos) ) )
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nStart = 0;
+ while (nStart<=rDoc.MaxCol())
+ {
+ while (nStart<rDoc.MaxCol() && !rMark.IsColumnMarked(nStart))
+ ++nStart;
+ if (rMark.IsColumnMarked(nStart))
+ {
+ SCCOL nEnd = nStart;
+ while (nEnd<rDoc.MaxCol() && rMark.IsColumnMarked(nEnd))
+ ++nEnd;
+ if (!rMark.IsColumnMarked(nEnd))
+ --nEnd;
+ aRanges.emplace_back(nStart,nEnd);
+ nStart = nEnd+1;
+ }
+ else
+ nStart = rDoc.MaxCol()+1;
+ }
+ }
+ else
+ {
+ aRanges.emplace_back(nPos,nPos);
+ }
+
+ rViewData.GetView()->SetWidthOrHeight(true, aRanges, eMode, nSizeTwips);
+}
+
+void ScColBar::HideEntries( SCCOLROW nStart, SCCOLROW nEnd )
+{
+ std::vector<sc::ColRowSpan> aRanges(1, sc::ColRowSpan(nStart,nEnd));
+ pTabView->GetViewData().GetView()->SetWidthOrHeight(true, aRanges, SC_SIZE_DIRECT, 0);
+}
+
+void ScColBar::SetMarking( bool bSet )
+{
+ pTabView->GetViewData().GetMarkData().SetMarking( bSet );
+ if (!bSet)
+ {
+ pTabView->GetViewData().GetView()->UpdateAutoFillMark();
+ }
+}
+
+void ScColBar::SelectWindow()
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScTabViewShell* pViewSh = rViewData.GetViewShell();
+
+ pViewSh->SetActive(); // Appear and SetViewFrame
+ pViewSh->DrawDeselectAll();
+
+ ScSplitPos eActive = rViewData.GetActivePart();
+ if (meWhich==SC_SPLIT_LEFT)
+ {
+ if (eActive==SC_SPLIT_TOPRIGHT) eActive=SC_SPLIT_TOPLEFT;
+ if (eActive==SC_SPLIT_BOTTOMRIGHT) eActive=SC_SPLIT_BOTTOMLEFT;
+ }
+ else
+ {
+ if (eActive==SC_SPLIT_TOPLEFT) eActive=SC_SPLIT_TOPRIGHT;
+ if (eActive==SC_SPLIT_BOTTOMLEFT) eActive=SC_SPLIT_BOTTOMRIGHT;
+ }
+ pViewSh->ActivatePart( eActive );
+
+ mpFuncSet->SetColumn( true );
+ mpFuncSet->SetWhich( eActive );
+
+ pViewSh->ActiveGrabFocus();
+}
+
+bool ScColBar::IsDisabled() const
+{
+ ScModule* pScMod = SC_MOD();
+ return pScMod->IsModalMode();
+}
+
+bool ScColBar::ResizeAllowed() const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return !rViewData.HasEditView( rViewData.GetActivePart() );
+}
+
+void ScColBar::DrawInvert( tools::Long nDragPosP )
+{
+ tools::Rectangle aRect( nDragPosP,0, nDragPosP+HDR_SLIDERSIZE-1,GetOutputSizePixel().Width()-1 );
+ PaintImmediately();
+ GetOutDev()->Invert(aRect);
+
+ pTabView->GetViewData().GetView()->InvertVertical(meWhich,nDragPosP);
+}
+
+OUString ScColBar::GetDragHelp( tools::Long nVal )
+{
+ tools::Long nTwips = static_cast<tools::Long>( nVal / pTabView->GetViewData().GetPPTX() );
+ return lcl_MetricString( nTwips, ScResId(STR_TIP_WIDTH) );
+}
+
+bool ScColBar::IsLayoutRTL() const // override only for columns
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() );
+}
+
+ScRowBar::ScRowBar( vcl::Window* pParent, ScVSplitPos eWhich,
+ ScHeaderFunctionSet* pFuncSet, ScHeaderSelectionEngine* pEng,
+ ScTabView* pTab ) :
+ ScHeaderControl( pParent, pEng, pTab->GetViewData().GetDocument().MaxRow()+1, true, pTab ),
+ meWhich( eWhich ),
+ mpFuncSet( pFuncSet )
+{
+ Show();
+}
+
+ScRowBar::~ScRowBar()
+{
+}
+
+SCCOLROW ScRowBar::GetPos() const
+{
+ return pTabView->GetViewData().GetPosY(meWhich);
+}
+
+sal_uInt16 ScRowBar::GetEntrySize( SCCOLROW nEntryNo ) const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ SCROW nLastRow = -1;
+ if (rDoc.RowHidden(nEntryNo, nTab, nullptr, &nLastRow))
+ return 0;
+ else
+ return static_cast<sal_uInt16>(ScViewData::ToPixel( rDoc.GetOriginalHeight( nEntryNo,
+ nTab ), rViewData.GetPPTY() ));
+}
+
+OUString ScRowBar::GetEntryText( SCCOLROW nEntryNo ) const
+{
+ return OUString::number( nEntryNo + 1 );
+}
+
+void ScRowBar::SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize )
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ sal_uInt16 nSizeTwips;
+ ScSizeMode eMode = SC_SIZE_DIRECT;
+ if (nNewSize < 10) nNewSize = 10; // pixels
+
+ if ( nNewSize == HDR_SIZE_OPTIMUM )
+ {
+ nSizeTwips = 0;
+ eMode = SC_SIZE_OPTIMAL;
+ }
+ else
+ nSizeTwips = static_cast<sal_uInt16>( nNewSize / rViewData.GetPPTY() );
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+
+ std::vector<sc::ColRowSpan> aRanges;
+ if ( rMark.IsRowMarked( nPos ) )
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCROW nStart = 0;
+ while (nStart<=rDoc.MaxRow())
+ {
+ while (nStart<rDoc.MaxRow() && !rMark.IsRowMarked(nStart))
+ ++nStart;
+ if (rMark.IsRowMarked(nStart))
+ {
+ SCROW nEnd = nStart;
+ while (nEnd<rDoc.MaxRow() && rMark.IsRowMarked(nEnd))
+ ++nEnd;
+ if (!rMark.IsRowMarked(nEnd))
+ --nEnd;
+ aRanges.emplace_back(nStart,nEnd);
+ nStart = nEnd+1;
+ }
+ else
+ nStart = rDoc.MaxRow()+1;
+ }
+ }
+ else
+ {
+ aRanges.emplace_back(nPos,nPos);
+ }
+
+ rViewData.GetView()->SetWidthOrHeight(false, aRanges, eMode, nSizeTwips);
+}
+
+void ScRowBar::HideEntries( SCCOLROW nStart, SCCOLROW nEnd )
+{
+ std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(nStart,nEnd));
+ pTabView->GetViewData().GetView()->SetWidthOrHeight(false, aRange, SC_SIZE_DIRECT, 0);
+}
+
+void ScRowBar::SetMarking( bool bSet )
+{
+ pTabView->GetViewData().GetMarkData().SetMarking( bSet );
+ if (!bSet)
+ {
+ pTabView->GetViewData().GetView()->UpdateAutoFillMark();
+ }
+}
+
+void ScRowBar::SelectWindow()
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScTabViewShell* pViewSh = rViewData.GetViewShell();
+
+ pViewSh->SetActive(); // Appear and SetViewFrame
+ pViewSh->DrawDeselectAll();
+
+ ScSplitPos eActive = rViewData.GetActivePart();
+ if (meWhich==SC_SPLIT_TOP)
+ {
+ if (eActive==SC_SPLIT_BOTTOMLEFT) eActive=SC_SPLIT_TOPLEFT;
+ if (eActive==SC_SPLIT_BOTTOMRIGHT) eActive=SC_SPLIT_TOPRIGHT;
+ }
+ else
+ {
+ if (eActive==SC_SPLIT_TOPLEFT) eActive=SC_SPLIT_BOTTOMLEFT;
+ if (eActive==SC_SPLIT_TOPRIGHT) eActive=SC_SPLIT_BOTTOMRIGHT;
+ }
+ pViewSh->ActivatePart( eActive );
+
+ mpFuncSet->SetColumn( false );
+ mpFuncSet->SetWhich( eActive );
+
+ pViewSh->ActiveGrabFocus();
+}
+
+bool ScRowBar::IsDisabled() const
+{
+ ScModule* pScMod = SC_MOD();
+ return pScMod->IsModalMode();
+}
+
+bool ScRowBar::ResizeAllowed() const
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return !rViewData.HasEditView( rViewData.GetActivePart() );
+}
+
+void ScRowBar::DrawInvert( tools::Long nDragPosP )
+{
+ tools::Rectangle aRect( 0,nDragPosP, GetOutputSizePixel().Width()-1,nDragPosP+HDR_SLIDERSIZE-1 );
+ PaintImmediately();
+ GetOutDev()->Invert(aRect);
+
+ pTabView->GetViewData().GetView()->InvertHorizontal(meWhich,nDragPosP);
+}
+
+OUString ScRowBar::GetDragHelp( tools::Long nVal )
+{
+ tools::Long nTwips = static_cast<tools::Long>( nVal / pTabView->GetViewData().GetPPTY() );
+ return lcl_MetricString( nTwips, ScResId(STR_TIP_HEIGHT) );
+}
+
+SCCOLROW ScRowBar::GetHiddenCount( SCCOLROW nEntryNo ) const // override only for rows
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ return rDoc.GetHiddenRowCount( nEntryNo, nTab );
+}
+
+bool ScRowBar::IsMirrored() const // override only for rows
+{
+ const ScViewData& rViewData = pTabView->GetViewData();
+ return rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc.cxx b/sc/source/ui/view/dbfunc.cxx
new file mode 100644
index 0000000000..8f1b9e8fc5
--- /dev/null
+++ b/sc/source/ui/view/dbfunc.cxx
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/bindings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+
+#include <dbfunc.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <sc.hrc>
+#include <undodat.hxx>
+#include <dbdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <dbdocfun.hxx>
+#include <editable.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <tabvwsh.hxx>
+#include <sortparam.hxx>
+
+ScDBFunc::ScDBFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
+ ScViewFunc( pParent, rDocSh, pViewShell )
+{
+}
+
+ScDBFunc::~ScDBFunc()
+{
+}
+
+// auxiliary functions
+
+void ScDBFunc::GotoDBArea( const OUString& rDBName )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDBCollection* pDBCol = rDoc.GetDBCollection();
+ ScDBData* pData = pDBCol->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
+ if (!pData)
+ return;
+
+ SCTAB nTab = 0;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+
+ pData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ SetTabNo( nTab );
+
+ MoveCursorAbs( nStartCol, nStartRow, SC_FOLLOW_JUMP,
+ false, false ); // bShift,bControl
+ DoneBlockMode();
+ InitBlockMode( nStartCol, nStartRow, nTab );
+ MarkCursor( nEndCol, nEndRow, nTab );
+ SelectionChanged();
+}
+
+// search current datarange for sort / filter
+
+ScDBData* ScDBFunc::GetDBData( bool bMark, ScGetDBMode eMode, ScGetDBSelection eSel )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDBData* pData = nullptr;
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
+ if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
+ {
+ bool bShrinkColumnsOnly = false;
+ if (eSel == ScGetDBSelection::RowDown)
+ {
+ // Don't alter row range, additional rows may have been selected on
+ // purpose to append data, or to have a fake header row.
+ bShrinkColumnsOnly = true;
+ // Select further rows only if only one row or a portion thereof is
+ // selected.
+ if (aRange.aStart.Row() != aRange.aEnd.Row())
+ {
+ // If an area is selected shrink that to the actual used
+ // columns, don't draw filter buttons for empty columns.
+ eSel = ScGetDBSelection::ShrinkToUsedData;
+ }
+ else if (aRange.aStart.Col() == aRange.aEnd.Col())
+ {
+ // One cell only, if it is not marked obtain entire used data
+ // area.
+ const ScMarkData& rMarkData = GetViewData().GetMarkData();
+ if (!(rMarkData.IsMarked() || rMarkData.IsMultiMarked()))
+ eSel = ScGetDBSelection::Keep;
+ }
+ }
+ switch (eSel)
+ {
+ case ScGetDBSelection::ShrinkToUsedData:
+ case ScGetDBSelection::RowDown:
+ {
+ // Shrink the selection to actual used area.
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nCol1 = aRange.aStart.Col(), nCol2 = aRange.aEnd.Col();
+ SCROW nRow1 = aRange.aStart.Row(), nRow2 = aRange.aEnd.Row();
+ bool bShrunk;
+ rDoc.ShrinkToUsedDataArea( bShrunk, aRange.aStart.Tab(),
+ nCol1, nRow1, nCol2, nRow2, bShrinkColumnsOnly);
+ if (bShrunk)
+ {
+ aRange.aStart.SetCol(nCol1);
+ aRange.aEnd.SetCol(nCol2);
+ aRange.aStart.SetRow(nRow1);
+ aRange.aEnd.SetRow(nRow2);
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ pData = pDocSh->GetDBData( aRange, eMode, eSel );
+ }
+ else if ( eMode != SC_DB_OLD )
+ pData = pDocSh->GetDBData(
+ ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(),
+ GetViewData().GetTabNo() ),
+ eMode, ScGetDBSelection::Keep );
+
+ if (!pData)
+ return nullptr;
+
+ if (bMark)
+ {
+ ScRange aFound;
+ pData->GetArea(aFound);
+ MarkRange( aFound, false );
+ }
+ return pData;
+}
+
+ScDBData* ScDBFunc::GetAnonymousDBData()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ return nullptr;
+
+ // Expand to used data area if not explicitly marked.
+ const ScMarkData& rMarkData = GetViewData().GetMarkData();
+ if (!rMarkData.IsMarked() && !rMarkData.IsMultiMarked())
+ {
+ SCCOL nCol1 = aRange.aStart.Col();
+ SCCOL nCol2 = aRange.aEnd.Col();
+ SCROW nRow1 = aRange.aStart.Row();
+ SCROW nRow2 = aRange.aEnd.Row();
+ pDocSh->GetDocument().GetDataArea(aRange.aStart.Tab(), nCol1, nRow1, nCol2, nRow2, false, false);
+ aRange.aStart.SetCol(nCol1);
+ aRange.aStart.SetRow(nRow1);
+ aRange.aEnd.SetCol(nCol2);
+ aRange.aEnd.SetRow(nRow2);
+ }
+
+ return pDocSh->GetAnonymousDBData(aRange);
+}
+
+// main functions
+
+// Sort
+
+void ScDBFunc::UISort( const ScSortParam& rSortParam )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
+ rSortParam.nCol2, rSortParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "Sort: no DBData" );
+ return;
+ }
+
+ ScSubTotalParam aSubTotalParam;
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ if (aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly)
+ {
+ // repeat subtotals, with new sortorder
+
+ DoSubTotals( aSubTotalParam, true/*bRecord*/, &rSortParam );
+ }
+ else
+ {
+ Sort( rSortParam ); // just sort
+ }
+}
+
+void ScDBFunc::Sort( const ScSortParam& rSortParam, bool bRecord, bool bPaint )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDBDocFunc aDBDocFunc( *pDocSh );
+ bool bSuccess = aDBDocFunc.Sort( nTab, rSortParam, bRecord, bPaint, false );
+ if ( bSuccess && !rSortParam.bInplace )
+ {
+ // mark target
+ ScRange aDestRange( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab,
+ rSortParam.nDestCol + rSortParam.nCol2 - rSortParam.nCol1,
+ rSortParam.nDestRow + rSortParam.nRow2 - rSortParam.nRow1,
+ rSortParam.nDestTab );
+ MarkRange( aDestRange );
+ }
+
+ ResetAutoSpellForContentChange();
+}
+
+// filters
+
+void ScDBFunc::Query( const ScQueryParam& rQueryParam, const ScRange* pAdvSource, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDBDocFunc aDBDocFunc( *pDocSh );
+ bool bSuccess = aDBDocFunc.Query( nTab, rQueryParam, pAdvSource, bRecord, false );
+
+ if (!bSuccess)
+ return;
+
+ bool bCopy = !rQueryParam.bInplace;
+ if (bCopy)
+ {
+ // mark target range (data base range has been set up if applicable)
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDBData* pDestData = rDoc.GetDBAtCursor(
+ rQueryParam.nDestCol, rQueryParam.nDestRow,
+ rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDestData)
+ {
+ ScRange aDestRange;
+ pDestData->GetArea(aDestRange);
+ MarkRange( aDestRange );
+ }
+ }
+
+ if (!bCopy)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(),
+ false /* bColumns */, true /* bRows */,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ false /* bGroups */, nTab);
+ UpdateScrollBars(ROW_HEADER);
+ SelectionChanged(); // for attribute states (filtered rows are ignored)
+ }
+
+ GetViewData().GetBindings().Invalidate( SID_UNFILTER );
+}
+
+// autofilter-buttons show / hide
+
+void ScDBFunc::ToggleAutoFilter()
+{
+ ScViewData* pViewData = &GetViewData();
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ ScQueryParam aParam;
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScDBData* pDBData = GetDBData(false, SC_DB_AUTOFILTER, ScGetDBSelection::RowDown);
+
+ pDBData->SetByRow( true ); //! undo, retrieve beforehand ??
+ pDBData->GetQueryParam( aParam );
+
+ SCCOL nCol;
+ SCROW nRow = aParam.nRow1;
+ SCTAB nTab = pViewData->GetTabNo();
+ ScMF nFlag;
+ bool bHasAuto = true;
+ bool bHeader = pDBData->HasHeader();
+
+ //! instead retrieve from DB-range?
+
+ for (nCol=aParam.nCol1; nCol<=aParam.nCol2 && bHasAuto; nCol++)
+ {
+ nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
+
+ if ( !(nFlag & ScMF::Auto) )
+ bHasAuto = false;
+ }
+
+ if (bHasAuto) // remove
+ {
+ // hide filter buttons
+
+ for (nCol=aParam.nCol1; nCol<=aParam.nCol2; nCol++)
+ {
+ nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr( nFlag & ~ScMF::Auto ) );
+ }
+
+ // use a list action for the AutoFilter buttons (ScUndoAutoFilter) and the filter operation
+
+ OUString aUndo = ScResId( STR_UNDO_QUERY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, pViewData->GetViewShell()->GetViewShellId() );
+
+ ScRange aRange;
+ pDBData->GetArea( aRange );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFilter>( pDocSh, aRange, pDBData->GetName(), false ) );
+
+ pDBData->SetAutoFilter(false);
+
+ // remove filter (incl. Paint / Undo)
+
+ SCSIZE nEC = aParam.GetEntryCount();
+ for (SCSIZE i=0; i<nEC; i++)
+ aParam.GetEntry(i).bDoQuery = false;
+ aParam.bDuplicate = true;
+ Query( aParam, nullptr, true );
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ ScDBFunc::ModifiedAutoFilter(pDocSh);
+ }
+ else // show filter buttons
+ {
+ if ( !rDoc.IsBlockEmpty( aParam.nCol1, aParam.nRow1,
+ aParam.nCol2, aParam.nRow2, nTab ) )
+ {
+ if (!bHeader)
+ {
+ std::shared_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pViewData->GetDialogParent(),
+ VclMessageType::Question,
+ VclButtonsType::YesNo, ScResId(STR_MSSG_MAKEAUTOFILTER_0))); // header from first row?
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
+ xBox->set_default_response(RET_YES);
+ xBox->SetInstallLOKNotifierHdl(LINK(this, ScDBFunc, InstallLOKNotifierHdl));
+ xBox->runAsync(xBox, [pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam] (sal_Int32 nResult) {
+ if (nResult == RET_YES)
+ {
+ pDBData->SetHeader( true ); //! Undo ??
+ }
+
+ ApplyAutoFilter(pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam);
+ });
+ }
+ else
+ ApplyAutoFilter(pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam);
+ }
+ else
+ {
+ std::shared_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pViewData->GetDialogParent(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ ScResId(STR_ERR_AUTOFILTER)));
+ xErrorBox->SetInstallLOKNotifierHdl(LINK(this, ScDBFunc, InstallLOKNotifierHdl));
+ xErrorBox->runAsync(xErrorBox, [] (sal_Int32) {});
+ }
+ }
+}
+
+IMPL_STATIC_LINK_NOARG(ScDBFunc, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+void ScDBFunc::ApplyAutoFilter(ScDocShell* pDocSh, ScViewData* pViewData, ScDBData* pDBData,
+ SCCOL nCol, SCROW nRow, SCTAB nTab, ScQueryParam aParam)
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFilter>(pDocSh, aRange, pDBData->GetName(), true));
+
+ pDBData->SetAutoFilter(true);
+
+ for (nCol=aParam.nCol1; nCol<=aParam.nCol2; nCol++)
+ {
+ ScMF nFlag = rDoc.GetAttr(nCol, nRow, nTab, ATTR_MERGE_FLAG)->GetValue();
+ rDoc.ApplyAttr(nCol, nRow, nTab, ScMergeFlagAttr(nFlag | ScMF::Auto));
+ }
+ pDocSh->PostPaint(ScRange(aParam.nCol1, nRow, nTab, aParam.nCol2, nRow, nTab),
+ PaintPartFlags::Grid);
+
+ ScDBFunc::ModifiedAutoFilter(pDocSh);
+}
+
+void ScDBFunc::ModifiedAutoFilter(ScDocShell* pDocSh)
+{
+ ScDocShellModificator aModificator(*pDocSh);
+ aModificator.SetDocumentModified();
+
+ SfxBindings* pBindings = pDocSh->GetViewBindings();
+ pBindings->Invalidate(SID_AUTO_FILTER);
+ pBindings->Invalidate(SID_AUTOFILTER_HIDE);
+}
+
+// just hide, no data change
+
+void ScDBFunc::HideAutoFilter()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ ScDBData* pDBData = GetDBData( false );
+
+ SCTAB nTab;
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ pDBData->GetArea(nTab, nCol1, nRow1, nCol2, nRow2);
+
+ for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++)
+ {
+ ScMF nFlag = rDoc.GetAttr( nCol, nRow1, nTab, ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, nRow1, nTab, ScMergeFlagAttr( nFlag & ~ScMF::Auto ) );
+ }
+
+ ScRange aRange;
+ pDBData->GetArea( aRange );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoAutoFilter>( pDocSh, aRange, pDBData->GetName(), false ) );
+
+ pDBData->SetAutoFilter(false);
+
+ pDocSh->PostPaint(ScRange(nCol1, nRow1, nTab, nCol2, nRow1, nTab), PaintPartFlags::Grid );
+ aModificator.SetDocumentModified();
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_AUTO_FILTER );
+ rBindings.Invalidate( SID_AUTOFILTER_HIDE );
+}
+
+// Re-Import
+
+bool ScDBFunc::ImportData( const ScImportParam& rParam )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScEditableTester aTester( rDoc, GetViewData().GetTabNo(), rParam.nCol1,rParam.nRow1,
+ rParam.nCol2,rParam.nRow2 );
+ if ( !aTester.IsEditable() )
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ ScDBDocFunc aDBDocFunc( *GetViewData().GetDocShell() );
+ return aDBDocFunc.DoImport( GetViewData().GetTabNo(), rParam, nullptr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc2.cxx b/sc/source/ui/view/dbfunc2.cxx
new file mode 100644
index 0000000000..fffa16909f
--- /dev/null
+++ b/sc/source/ui/view/dbfunc2.cxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dbfunc.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+
+void ScDBFunc::UpdateCharts( bool bAllCharts )
+{
+ sal_uInt16 nFound = 0;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ if ( rDoc.GetDrawLayer() )
+ nFound = DoUpdateCharts( ScAddress( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo()),
+ rDoc,
+ bAllCharts );
+
+ if ( !nFound && !bAllCharts )
+ ErrorMessage(STR_NOCHARTATCURSOR);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc3.cxx b/sc/source/ui/view/dbfunc3.cxx
new file mode 100644
index 0000000000..9720bf7f4f
--- /dev/null
+++ b/sc/source/ui/view/dbfunc3.cxx
@@ -0,0 +1,2312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dbfunc.hxx>
+#include <scitems.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sfx2/app.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
+
+#include <global.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <undotab.hxx>
+#include <undodat.hxx>
+#include <dbdata.hxx>
+#include <rangenam.hxx>
+#include <docsh.hxx>
+#include <olinetab.hxx>
+#include <olinefun.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dbdocfun.hxx>
+#include <dpoutput.hxx>
+#include <editable.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <unonames.hxx>
+#include <userlist.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <tabvwsh.hxx>
+#include <generalfunction.hxx>
+#include <sortparam.hxx>
+
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+#include <string_view>
+#include <unordered_set>
+#include <unordered_map>
+#include <vector>
+#include <algorithm>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::container::XNameAccess;
+using ::com::sun::star::sheet::XDimensionsSupplier;
+using ::std::vector;
+
+// outliner
+
+// create outline grouping
+
+void ScDBFunc::MakeOutline( bool bColumns, bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.MakeOutline( aRange, bColumns, bRecord, false );
+
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ false /* bHidden */, false /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// delete outline grouping
+
+void ScDBFunc::RemoveOutline( bool bColumns, bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.RemoveOutline( aRange, bColumns, bRecord, false );
+
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// menu status: delete outlines
+
+void ScDBFunc::TestRemoveOutline( bool& rCol, bool& rRow )
+{
+ bool bColFound = false;
+ bool bRowFound = false;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ {
+ SCTAB nTab = nStartTab;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ ScOutlineEntry* pEntry;
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+ bool bColMarked = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
+ bool bRowMarked = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
+
+ // columns
+
+ if ( !bRowMarked || bColMarked ) // not when entire rows are marked
+ {
+ ScOutlineArray& rArray = pTable->GetColArray();
+ ScSubOutlineIterator aColIter( &rArray );
+ while (!bColFound)
+ {
+ pEntry=aColIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
+ bColFound = true;
+ }
+ }
+
+ // rows
+
+ if ( !bColMarked || bRowMarked ) // not when entire columns are marked
+ {
+ ScOutlineArray& rArray = pTable->GetRowArray();
+ ScSubOutlineIterator aRowIter( &rArray );
+ while (!bRowFound)
+ {
+ pEntry=aRowIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( nStartRow<=nEnd && nEndRow>=nStart )
+ bRowFound = true;
+ }
+ }
+ }
+ }
+
+ rCol = bColFound;
+ rRow = bRowFound;
+}
+
+void ScDBFunc::RemoveAllOutlines( bool bRecord )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord );
+
+ if (bOk)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ true /* bColumns */, true /* bRows */, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(BOTH_HEADERS);
+ }
+}
+
+// auto outlines
+
+void ScDBFunc::AutoOutline( )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScRange aRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ); // the complete sheet, if nothing is marked
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ rMark.MarkToMulti();
+ aRange = rMark.GetMultiMarkArea();
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ aFunc.AutoOutline( aRange, true );
+}
+
+// select outline level
+
+void ScDBFunc::SelectLevel( bool bColumns, sal_uInt16 nLevel, bool bRecord )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, true/*bPaint*/ );
+
+ if (bOk)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
+ }
+}
+
+// show individual outline groups
+
+void ScDBFunc::SetOutlineState( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bHidden)
+{
+ const sal_uInt16 nHeadEntry = static_cast< sal_uInt16 >( -1 );
+ if ( nEntry == nHeadEntry)
+ SelectLevel( bColumns, sal::static_int_cast<sal_uInt16>(nLevel) );
+ else
+ {
+ if ( !bHidden )
+ ShowOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ else
+ HideOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ }
+}
+
+void ScDBFunc::ShowOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
+
+ if ( bPaint )
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
+ }
+}
+
+// hide individual outline groups
+
+void ScDBFunc::HideOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+
+ bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
+
+ if ( bOk && bPaint )
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColumns, !bColumns, false /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, nTab);
+ UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
+ }
+}
+
+// menu status: show/hide marked range
+
+bool ScDBFunc::OutlinePossible(bool bHide)
+{
+ bool bEnable = false;
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ SCCOLROW nStart;
+ SCCOLROW nEnd;
+
+ // columns
+
+ ScOutlineArray& rColArray = pTable->GetColArray();
+ ScSubOutlineIterator aColIter( &rColArray );
+ while (!bEnable)
+ {
+ ScOutlineEntry* pEntry = aColIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( bHide )
+ {
+ if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
+ if (!pEntry->IsHidden())
+ bEnable = true;
+ }
+ else
+ {
+ if ( nStart>=nStartCol && nEnd<=nEndCol )
+ if (pEntry->IsHidden())
+ bEnable = true;
+ }
+ }
+
+ // rows
+
+ ScOutlineArray& rRowArray = pTable->GetRowArray();
+ ScSubOutlineIterator aRowIter( &rRowArray );
+ for (;;)
+ {
+ ScOutlineEntry* pEntry = aRowIter.GetNext();
+ if (!pEntry)
+ break;
+ nStart = pEntry->GetStart();
+ nEnd = pEntry->GetEnd();
+ if ( bHide )
+ {
+ if ( nStartRow<=nEnd && nEndRow>=nStart )
+ if (!pEntry->IsHidden())
+ bEnable = true;
+ }
+ else
+ {
+ if ( nStart>=nStartRow && nEnd<=nEndRow )
+ if (pEntry->IsHidden())
+ bEnable = true;
+ }
+ }
+ }
+ }
+
+ return bEnable;
+}
+
+// show marked range
+
+void ScDBFunc::ShowMarkedOutlines( bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord );
+ if (bDone)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(), true, true,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ UpdateScrollBars();
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// hide marked range
+
+void ScDBFunc::HideMarkedOutlines( bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScOutlineDocFunc aFunc(*pDocSh);
+ bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord );
+ if (bDone)
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(), true, true,
+ false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ UpdateScrollBars();
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// sub totals
+
+void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, bool bRecord,
+ const ScSortParam* pForceNewSort )
+{
+ bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
+ rParam.nCol2, rParam.nRow2 );
+ if (!pDBData)
+ {
+ OSL_FAIL( "SubTotals: no DBData" );
+ return;
+ }
+
+ ScEditableTester aTester( rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
+ rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ ErrorMessage(STR_MSSG_INSERTCELLS_0); // do not insert into merged
+ return;
+ }
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+ bool bOk = true;
+ if (rParam.bReplace)
+ {
+ if (rDoc.TestRemoveSubTotals( nTab, rParam ))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_MSSG_DOSUBTOTALS_1))); // "delete data?"
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
+ xBox->set_default_response(RET_YES);
+ bOk = xBox->run() == RET_YES;
+ }
+ }
+
+ if (!bOk)
+ return;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScSubTotalParam aNewParam( rParam ); // change end of range
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::unique_ptr<ScRangeName> pUndoRange;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+
+ if (bRecord) // record old data
+ {
+ bool bOldFilter = bDo && rParam.bDoSort;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ SCCOLROW nOutStartCol; // row/column status
+ SCCOLROW nOutStartRow;
+ SCCOLROW nOutEndCol;
+ SCCOLROW nOutEndRow;
+ pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
+ pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
+
+ // record data range - including filter results
+ rDoc.CopyToDocument( 0,rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
+ InsertDeleteFlags::ALL, false, *pUndoDoc );
+
+ // all formulas for reference
+ rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc );
+
+ // database and other ranges
+ ScRangeName* pDocRange = rDoc.GetRangeName();
+ if (!pDocRange->empty())
+ pUndoRange.reset(new ScRangeName( *pDocRange ));
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+ }
+
+ ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
+ if (pOut)
+ {
+ // Remove all existing outlines in the specified range.
+ ScOutlineArray& rRowArray = pOut->GetRowArray();
+ sal_uInt16 nDepth = rRowArray.GetDepth();
+ for (sal_uInt16 i = 0; i < nDepth; ++i)
+ {
+ bool bSize;
+ rRowArray.Remove(aNewParam.nRow1, aNewParam.nRow2, bSize);
+ }
+ }
+
+ if (rParam.bReplace)
+ rDoc.RemoveSubTotals( nTab, aNewParam );
+ bool bSuccess = true;
+ if (bDo)
+ {
+ // Sort
+ if ( rParam.bDoSort || pForceNewSort )
+ {
+ pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+
+ // set subtotal fields before sorting
+ // (duplicate values are dropped, so that they can be called again)
+
+ ScSortParam aOldSort;
+ pDBData->GetSortParam( aOldSort );
+ ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
+ Sort( aSortParam, false, false );
+ }
+
+ bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
+ }
+ ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
+ aNewParam.nCol2, aNewParam.nRow2, nTab );
+ rDoc.SetDirty( aDirtyRange, true );
+
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSubTotals>( pDocSh, nTab,
+ rParam, aNewParam.nRow2,
+ std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
+ std::move(pUndoRange), std::move(pUndoDB) ) );
+ }
+
+ if (!bSuccess)
+ {
+ // "Can not insert any rows"
+ ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
+ }
+
+ // store
+ pDBData->SetSubTotalParam( aNewParam );
+ pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
+ rDoc.CompileDBFormula();
+
+ const ScRange aMarkRange( aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, nTab);
+ DoneBlockMode();
+ InitOwnBlockMode( aMarkRange );
+ rMark.SetMarkArea( aMarkRange );
+ MarkDataChanged();
+
+ pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
+
+ aModificator.SetDocumentModified();
+
+ SelectionChanged();
+}
+
+// consolidate
+
+void ScDBFunc::Consolidate( const ScConsolidateParam& rParam )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ pDocShell->DoConsolidate( rParam );
+ SetTabNo( rParam.nTab, true );
+}
+
+// pivot
+
+static OUString lcl_MakePivotTabName( std::u16string_view rPrefix, SCTAB nNumber )
+{
+ OUString aName = rPrefix + OUString::number( nNumber );
+ return aName;
+}
+
+bool ScDBFunc::MakePivotTable(
+ const ScDPSaveData& rData, const ScRange& rDest, bool bNewTable,
+ const ScDPObject& rSource )
+{
+ // error message if no fields are set
+ // this must be removed when drag&drop of fields from a toolbox is available
+
+ if ( rData.IsEmpty() )
+ {
+ ErrorMessage(STR_PIVOT_NODATA);
+ return false;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = GetViewData().GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ ScRange aDestRange = rDest;
+ if ( bNewTable )
+ {
+ SCTAB nSrcTab = GetViewData().GetTabNo();
+
+ OUString aName( ScResId(STR_PIVOT_TABLE) );
+ OUString aStr;
+
+ rDoc.GetName( nSrcTab, aStr );
+ aName += "_" + aStr + "_";
+
+ SCTAB nNewTab = nSrcTab+1;
+
+ SCTAB i=1;
+ while ( !rDoc.InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
+ i++;
+
+ bool bAppend = ( nNewTab+1 == rDoc.GetTableCount() );
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
+ }
+
+ GetViewData().InsertTab( nNewTab );
+ SetTabNo(nNewTab, true);
+
+ aDestRange = ScRange( 0, 0, nNewTab );
+ }
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(
+ aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
+
+ ScDPObject aObj( rSource );
+ aObj.SetOutRange( aDestRange );
+ if ( pDPObj && !rData.GetExistingDimensionData() )
+ {
+ // copy dimension data from old object - lost in the dialog
+ //! change the dialog to keep the dimension data
+
+ ScDPSaveData aNewData( rData );
+ const ScDPSaveData* pOldData = pDPObj->GetSaveData();
+ if ( pOldData )
+ {
+ const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
+ aNewData.SetDimensionData( pDimSave );
+ }
+ aObj.SetSaveData( aNewData );
+ }
+ else
+ aObj.SetSaveData( rData );
+
+ bool bAllowMove = (pDPObj != nullptr); // allow re-positioning when editing existing table
+
+ ScDBDocFunc aFunc( *pDocSh );
+ bool bSuccess = aFunc.DataPilotUpdate(pDPObj, &aObj, true, false, bAllowMove);
+
+ CursorPosChanged(); // shells may be switched
+
+ if ( bNewTable )
+ {
+ pDocSh->PostPaintExtras();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ }
+
+ return bSuccess;
+}
+
+void ScDBFunc::DeletePivotTable()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+ if ( pDPObj )
+ {
+ ScDBDocFunc aFunc( *pDocSh );
+ aFunc.RemovePivotTable(*pDPObj, true, false);
+ CursorPosChanged(); // shells may be switched
+ }
+ else
+ ErrorMessage(STR_PIVOT_NOTFOUND);
+}
+
+void ScDBFunc::RecalcPivotTable()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+ if (pDPObj)
+ {
+ // Remove existing data cache for the data that this datapilot uses,
+ // to force re-build data cache.
+ ScDBDocFunc aFunc(*pDocSh);
+ aFunc.RefreshPivotTables(pDPObj, false);
+
+ CursorPosChanged(); // shells may be switched
+ }
+ else
+ ErrorMessage(STR_PIVOT_NOTFOUND);
+}
+
+void ScDBFunc::GetSelectedMemberList(ScDPUniqueStringSet& rEntries, tools::Long& rDimension)
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( !pDPObj )
+ return;
+
+ tools::Long nStartDimension = -1;
+ tools::Long nStartHierarchy = -1;
+ tools::Long nStartLevel = -1;
+
+ ScRangeListRef xRanges;
+ GetViewData().GetMultiArea( xRanges ); // incl. cursor if nothing is selected
+ size_t nRangeCount = xRanges->size();
+ bool bContinue = true;
+
+ for (size_t nRangePos=0; nRangePos < nRangeCount && bContinue; nRangePos++)
+ {
+ ScRange const & rRange = (*xRanges)[nRangePos];
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
+ for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
+ {
+ sheet::DataPilotTableHeaderData aData;
+ pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
+ if ( aData.Dimension < 0 )
+ bContinue = false; // not part of any dimension
+ else
+ {
+ if ( nStartDimension < 0 ) // first member?
+ {
+ nStartDimension = aData.Dimension;
+ nStartHierarchy = aData.Hierarchy;
+ nStartLevel = aData.Level;
+ }
+ if ( aData.Dimension != nStartDimension ||
+ aData.Hierarchy != nStartHierarchy ||
+ aData.Level != nStartLevel )
+ {
+ bContinue = false; // cannot mix dimensions
+ }
+ }
+ if ( bContinue )
+ {
+ // accept any part of a member description, also subtotals,
+ // but don't stop if empty parts are contained
+ if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
+ rEntries.insert(aData.MemberName);
+ }
+ }
+ }
+
+ rDimension = nStartDimension; // dimension from which the found members came
+ if (!bContinue)
+ rEntries.clear(); // remove all if not valid
+}
+
+bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
+{
+ // determine if the date group dialog has to be shown for the current selection
+
+ bool bFound = false;
+
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
+ if ( pDPObj )
+ {
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (!aEntries.empty())
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+ OUString aBaseDimName( aDimName );
+
+ bool bInGroupDim = false;
+ bool bFoundParts = false;
+
+ ScDPDimensionSaveData* pDimData =
+ const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
+ if ( pDimData )
+ {
+ const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
+ const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
+ if ( pNumGroupDim )
+ {
+ // existing num group dimension
+
+ if ( pNumGroupDim->GetDatePart() != 0 )
+ {
+ // dimension has date info -> edit settings of this dimension
+ // (parts are collected below)
+
+ rOldInfo = pNumGroupDim->GetDateInfo();
+ bFound = true;
+ }
+ else if ( pNumGroupDim->GetInfo().mbDateValues )
+ {
+ // Numerical grouping with DateValues flag is used for grouping
+ // of days with a "Number of days" value.
+
+ rOldInfo = pNumGroupDim->GetInfo();
+ rParts = css::sheet::DataPilotFieldGroupBy::DAYS; // not found in CollectDateParts
+ bFoundParts = true;
+ bFound = true;
+ }
+ bInGroupDim = true;
+ }
+ else if ( pGroupDim )
+ {
+ // existing additional group dimension
+
+ if ( pGroupDim->GetDatePart() != 0 )
+ {
+ // dimension has date info -> edit settings of this dimension
+ // (parts are collected below)
+
+ rOldInfo = pGroupDim->GetDateInfo();
+ aBaseDimName = pGroupDim->GetSourceDimName();
+ bFound = true;
+ }
+ bInGroupDim = true;
+ }
+ }
+ if ( bFound && !bFoundParts )
+ {
+ // collect date parts from all group dimensions
+ rParts = pDimData->CollectDateParts( aBaseDimName );
+ }
+ if ( !bFound && !bInGroupDim )
+ {
+ // create new date group dimensions if the selection is a single cell
+ // in a normal dimension with date content
+
+ ScRange aSelRange;
+ if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
+ aSelRange.aStart == aSelRange.aEnd )
+ {
+ SCCOL nSelCol = aSelRange.aStart.Col();
+ SCROW nSelRow = aSelRange.aStart.Row();
+ SCTAB nSelTab = aSelRange.aStart.Tab();
+ if ( rDoc.HasValueData( nSelCol, nSelRow, nSelTab ) )
+ {
+ sal_uLong nIndex = rDoc.GetAttr(
+ nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT)->GetValue();
+ SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
+ if ( nType == SvNumFormatType::DATE || nType == SvNumFormatType::TIME || nType == SvNumFormatType::DATETIME )
+ {
+ bFound = true;
+ // use currently selected value for automatic limits
+ if( rOldInfo.mbAutoStart )
+ rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
+ if( rOldInfo.mbAutoEnd )
+ rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bFound;
+}
+
+bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
+{
+ // determine if the numeric group dialog has to be shown for the current selection
+
+ bool bFound = false;
+
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
+ if ( pDPObj )
+ {
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (!aEntries.empty())
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ bool bInGroupDim = false;
+
+ ScDPDimensionSaveData* pDimData =
+ const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
+ if ( pDimData )
+ {
+ const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
+ if ( pNumGroupDim )
+ {
+ // existing num group dimension
+ // -> edit settings of this dimension
+
+ rOldInfo = pNumGroupDim->GetInfo();
+ bFound = true;
+ }
+ else if ( pDimData->GetNamedGroupDim( aDimName ) )
+ bInGroupDim = true; // in a group dimension
+ }
+ if ( !bFound && !bInGroupDim )
+ {
+ // create a new num group dimension if the selection is a single cell
+ // in a normal dimension with numeric content
+
+ ScRange aSelRange;
+ if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
+ aSelRange.aStart == aSelRange.aEnd )
+ {
+ if ( rDoc.HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
+ aSelRange.aStart.Tab() ) )
+ {
+ bFound = true;
+ // use currently selected value for automatic limits
+ if( rOldInfo.mbAutoStart )
+ rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
+ if( rOldInfo.mbAutoEnd )
+ rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
+ }
+ }
+ }
+ }
+ }
+
+ return bFound;
+}
+
+void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ std::vector<OUString> aDeletedNames;
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
+
+ // find the source dimension name.
+ OUString aBaseDimName = aDimName;
+ if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
+ aBaseDimName = pBaseGroupDim->GetSourceDimName();
+
+ // Remove all group dimensions associated with this source dimension. For
+ // date grouping, we need to remove all existing groups for the affected
+ // source dimension and build new one(s) from scratch. Keep the deleted
+ // names so that they can be reused during re-construction.
+ aData.RemoveAllGroupDimensions(aBaseDimName, &aDeletedNames);
+
+ if ( nParts )
+ {
+ // create date group dimensions
+
+ bool bFirst = true;
+ sal_Int32 nMask = 1;
+ for (sal_uInt16 nBit=0; nBit<32; nBit++)
+ {
+ if ( nParts & nMask )
+ {
+ if ( bFirst )
+ {
+ // innermost part: create NumGroupDimension (replacing original values)
+ // Dimension name is left unchanged
+
+ if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.mfStep >= 1.0) )
+ {
+ // only days, and a step value specified: use numerical grouping
+ // with DateValues flag, not date grouping
+
+ ScDPNumGroupInfo aNumInfo( rInfo );
+ aNumInfo.mbDateValues = true;
+
+ ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+ else
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+
+ bFirst = false;
+ }
+ else
+ {
+ // additional parts: create GroupDimension (shown as additional dimensions)
+ OUString aGroupDimName =
+ pDimData->CreateDateGroupDimName(nMask, *pDPObj, true, &aDeletedNames);
+ ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
+ aGroupDim.SetDateInfo( rInfo, nMask );
+ pDimData->AddGroupDimension( aGroupDim );
+
+ // set orientation
+ ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
+ if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
+ pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
+ aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
+ }
+ }
+ }
+ nMask *= 2;
+ }
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
+
+ ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
+ if ( pExisting )
+ {
+ // modify existing group dimension
+ pExisting->SetGroupInfo( rInfo );
+ }
+ else
+ {
+ // create new group dimension
+ ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
+ pDimData->AddNumGroupDimension( aNumGroupDim );
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::GroupDataPilot()
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
+
+ // find original base
+ OUString aBaseDimName = aDimName;
+ const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
+ if ( pBaseGroupDim )
+ {
+ // any entry's SourceDimName is the original base
+ aBaseDimName = pBaseGroupDim->GetSourceDimName();
+ }
+
+ // find existing group dimension
+ // (using the selected dim, can be intermediate group dim)
+ ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
+
+ // remove the selected items from their groups
+ // (empty groups are removed, too)
+ if ( pGroupDimension )
+ {
+ for (const OUString& aEntryName : aEntries)
+ {
+ if ( pBaseGroupDim )
+ {
+ // for each selected (intermediate) group, remove all its items
+ // (same logic as for adding, below)
+ const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
+ if ( pBaseGroup )
+ pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements
+ else
+ pGroupDimension->RemoveFromGroups( aEntryName );
+ }
+ else
+ pGroupDimension->RemoveFromGroups( aEntryName );
+ }
+ }
+
+ std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim;
+ if ( !pGroupDimension )
+ {
+ // create a new group dimension
+ OUString aGroupDimName =
+ pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, nullptr);
+ pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName ));
+
+ pGroupDimension = pNewGroupDim.get(); // make changes to the new dim if none existed
+
+ if ( pBaseGroupDim )
+ {
+ // If it's a higher-order group dimension, pre-allocate groups for all
+ // non-selected original groups, so the individual base members aren't
+ // used for automatic groups (this would make the original groups hard
+ // to find).
+ //! Also do this when removing groups?
+ //! Handle this case dynamically with automatic groups?
+
+ tools::Long nGroupCount = pBaseGroupDim->GetGroupCount();
+ for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ )
+ {
+ const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
+
+ if (!aEntries.count(rBaseGroup.GetGroupName()))
+ {
+ // add an additional group for each item that is not in the selection
+ ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() );
+ aGroup.AddElementsFromGroup( rBaseGroup );
+ pGroupDimension->AddGroupItem( aGroup );
+ }
+ }
+ }
+ }
+ OUString aGroupDimName = pGroupDimension->GetGroupDimName();
+
+ OUString aGroupName = pGroupDimension->CreateGroupName(ScResId(STR_PIVOT_GROUP));
+ ScDPSaveGroupItem aGroup( aGroupName );
+ for (const OUString& aEntryName : aEntries)
+ {
+ if ( pBaseGroupDim )
+ {
+ // for each selected (intermediate) group, add all its items
+ const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
+ if ( pBaseGroup )
+ aGroup.AddElementsFromGroup( *pBaseGroup );
+ else
+ aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself
+ }
+ else
+ aGroup.AddElement( aEntryName ); // no group dimension, add all items directly
+ }
+
+ pGroupDimension->AddGroupItem( aGroup );
+
+ if ( pNewGroupDim )
+ {
+ pDimData->AddGroupDimension( *pNewGroupDim );
+ pNewGroupDim.reset(); // AddGroupDimension copies the object
+ // don't access pGroupDimension after here
+ }
+ pGroupDimension = nullptr;
+
+ // set orientation
+ ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
+ if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
+ {
+ ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
+ pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
+ aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::UngroupDataPilot()
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if (!pDPObj)
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ if (!aData.GetExistingDimensionData())
+ // There is nothing to ungroup.
+ return;
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+
+ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
+ const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
+ if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
+ ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
+ {
+ // Date grouping: need to remove all affected group dimensions.
+ // This is done using DateGroupDataPilot with nParts=0.
+
+ DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
+ return;
+ }
+
+ if ( pGroupDim )
+ {
+ for (const auto& rEntry : aEntries)
+ pGroupDim->RemoveGroup(rEntry);
+
+ // remove group dimension if empty
+ bool bEmptyDim = pGroupDim->IsEmpty();
+ if ( !bEmptyDim )
+ {
+ // If all remaining groups in the dimension aren't shown, remove
+ // the dimension too, as if it was completely empty.
+ ScDPUniqueStringSet aVisibleEntries;
+ pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
+ bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
+ }
+ if ( bEmptyDim )
+ {
+ pDimData->RemoveGroupDimension( aDimName ); // pGroupDim is deleted
+
+ // also remove SaveData settings for the dimension that no longer exists
+ aData.RemoveDimensionByName( aDimName );
+ }
+ }
+ else if ( pNumGroupDim )
+ {
+ // remove the numerical grouping
+ pDimData->RemoveNumGroupDimension( aDimName );
+ // SaveData settings can remain unchanged - the same dimension still exists
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ aFunc.RefreshPivotTableGroups(pDPObj);
+
+ // unmark cell selection
+ Unmark();
+}
+
+static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, std::u16string_view rMemberName)
+{
+ sal_Int32 n = rSubtotal.getLength();
+ const sal_Unicode* p = rSubtotal.getStr();
+ OUStringBuffer aBuf, aWordBuf;
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ sal_Unicode c = p[i];
+ if (c == ' ')
+ {
+ OUString aWord = aWordBuf.makeStringAndClear();
+ if (aWord == rMemberName)
+ aBuf.append('?');
+ else
+ aBuf.append(aWord);
+ aBuf.append(c);
+ }
+ else if (c == '\\')
+ {
+ // Escape a backslash character.
+ aWordBuf.append(OUStringChar(c) + OUStringChar(c));
+ }
+ else if (c == '?')
+ {
+ // A literal '?' must be escaped with a backslash ('\');
+ aWordBuf.append("\\" + OUStringChar(c));
+ }
+ else
+ aWordBuf.append(c);
+ }
+
+ if (!aWordBuf.isEmpty())
+ {
+ OUString aWord = aWordBuf.makeStringAndClear();
+ if (aWord == rMemberName)
+ aBuf.append('?');
+ else
+ aBuf.append(aWord);
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+void ScDBFunc::DataPilotInput( const ScAddress& rPos, const OUString& rString )
+{
+ using namespace ::com::sun::star::sheet;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
+ if (!pDPObj)
+ return;
+
+ OUString aOldText = rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab());
+
+ if ( aOldText == rString )
+ {
+ // nothing to do: silently exit
+ return;
+ }
+
+ TranslateId pErrorId;
+
+ pDPObj->BuildAllDimensionMembers();
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ bool bChange = false;
+ bool bNeedReloadGroups = false;
+
+ DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
+ tools::Long nField = pDPObj->GetHeaderDim( rPos, nOrient );
+ if ( nField >= 0 )
+ {
+ // changing a field title
+ if ( aData.GetExistingDimensionData() )
+ {
+ // only group dimensions can be renamed
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
+ if ( pGroupDim )
+ {
+ // valid name: not empty, no existing dimension (group or other)
+ if (!rString.isEmpty() && !pDPObj->IsDimNameInUse(rString))
+ {
+ pGroupDim->Rename( rString );
+
+ // also rename in SaveData to preserve the field settings
+ ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
+ pSaveDim->SetName( rString );
+
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
+ {
+ bool bDataLayout = false;
+ OUString aDimName = pDPObj->GetDimName(nField, bDataLayout);
+ ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
+ if (pDim)
+ {
+ if (!rString.isEmpty())
+ {
+ if (rString.equalsIgnoreAsciiCase(aDimName))
+ {
+ pDim->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDPObj->IsDimNameInUse(rString))
+ {
+ pDim->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ }
+ else if (pDPObj->IsDataDescriptionCell(rPos))
+ {
+ // There is only one data dimension.
+ ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
+ if (pDim)
+ {
+ if (!rString.isEmpty())
+ {
+ if (pDim->GetName().equalsIgnoreAsciiCase(rString))
+ {
+ pDim->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDPObj->IsDimNameInUse(rString))
+ {
+ pDim->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ else
+ {
+ // This is not a field header.
+ sheet::DataPilotTableHeaderData aPosData;
+ pDPObj->GetHeaderPositionData(rPos, aPosData);
+
+ if ((aPosData.Flags & MemberResultFlags::HASMEMBER) && !aOldText.isEmpty())
+ {
+ if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
+ if ( pGroupDim )
+ {
+ // valid name: not empty, no existing group in this dimension
+ //! ignore case?
+ if (!rString.isEmpty() && !pGroupDim->GetNamedGroup(rString))
+ {
+ ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
+ if ( pGroup )
+ pGroup->Rename( rString ); // rename the existing group
+ else
+ {
+ // create a new group to replace the automatic group
+ ScDPSaveGroupItem aGroup( rString );
+ aGroup.AddElement( aOldText );
+ pGroupDim->AddGroupItem( aGroup );
+ }
+
+ // in both cases also adjust savedata, to preserve member settings (show details)
+ ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
+ ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
+ if ( pSaveMember )
+ pSaveMember->SetName( rString );
+
+ bChange = true;
+ bNeedReloadGroups = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ else if (aPosData.Flags & MemberResultFlags::GRANDTOTAL)
+ {
+ aData.SetGrandTotalName(rString);
+ bChange = true;
+ }
+ else if (aPosData.Dimension >= 0 && !aPosData.MemberName.isEmpty())
+ {
+ bool bDataLayout = false;
+ OUString aDimName = pDPObj->GetDimName(static_cast<tools::Long>(aPosData.Dimension), bDataLayout);
+ if (bDataLayout)
+ {
+ // data dimension
+ do
+ {
+ if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
+ break;
+
+ ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
+ if (!pDim)
+ break;
+
+ if (rString.isEmpty())
+ {
+ pErrorId = STR_INVALIDNAME;
+ break;
+ }
+
+ if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
+ {
+ pDim->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDPObj->IsDimNameInUse(rString))
+ {
+ pDim->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ while (false);
+ }
+ else
+ {
+ // field member
+ do
+ {
+ ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
+ if (!pDim)
+ break;
+
+ ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
+ if (!pMem)
+ break;
+
+ if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
+ {
+ // Change subtotal only when the table has one data dimension.
+ if (aData.GetDataDimensionCount() > 1)
+ break;
+
+ // display name for subtotal is allowed only if the subtotal type is 'Automatic'.
+ if (pDim->GetSubTotalsCount() != 1)
+ break;
+
+ if (pDim->GetSubTotalFunc(0) != ScGeneralFunction::AUTO)
+ break;
+
+ const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
+ OUString aMemberName;
+ if (pLayoutName)
+ aMemberName = *pLayoutName;
+ else
+ aMemberName = aPosData.MemberName;
+
+ OUString aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
+ pDim->SetSubtotalName(aNew);
+ bChange = true;
+ }
+ else
+ {
+ // Check to make sure the member name isn't
+ // already used.
+ if (!rString.isEmpty())
+ {
+ if (rString.equalsIgnoreAsciiCase(pMem->GetName()))
+ {
+ pMem->RemoveLayoutName();
+ bChange = true;
+ }
+ else if (!pDim->IsMemberNameInUse(rString))
+ {
+ pMem->SetLayoutName(rString);
+ bChange = true;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ else
+ pErrorId = STR_INVALIDNAME;
+ }
+ }
+ while (false);
+ }
+ }
+ }
+ }
+
+ if ( bChange )
+ {
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ pDPObj->SetSaveData( aData );
+ if (bNeedReloadGroups)
+ {
+ ScDPCollection* pDPs = rDoc.GetDPCollection();
+ if (pDPs)
+ {
+ o3tl::sorted_vector<ScDPObject*> aRefs;
+ // tdf#111305: Reload groups in cache after modifications.
+ pDPs->ReloadGroupsInCache(pDPObj, aRefs);
+ } // pDPs
+ } // bNeedReloadGroups
+ aFunc.UpdatePivotTable(*pDPObj, true, false);
+ }
+ else
+ {
+ if (!pErrorId)
+ pErrorId = STR_ERR_DATAPILOT_INPUT;
+ ErrorMessage(pErrorId);
+ }
+}
+
+static void lcl_MoveToEnd( ScDPSaveDimension& rDim, const OUString& rItemName )
+{
+ std::unique_ptr<ScDPSaveMember> pNewMember;
+ const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
+ if ( pOldMember )
+ pNewMember.reset(new ScDPSaveMember( *pOldMember ));
+ else
+ pNewMember.reset(new ScDPSaveMember( rItemName ));
+ rDim.AddMember( std::move(pNewMember) );
+ // AddMember takes ownership of the new pointer,
+ // puts it to the end of the list even if it was in the list before.
+}
+
+namespace {
+
+struct ScOUStringCollate
+{
+ CollatorWrapper* mpCollator;
+
+ explicit ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
+
+ bool operator()(const OUString& rStr1, const OUString& rStr2) const
+ {
+ return ( mpCollator->compareString(rStr1, rStr2) < 0 );
+ }
+};
+
+}
+
+void ScDBFunc::DataPilotSort(ScDPObject* pDPObj, tools::Long nDimIndex, bool bAscending, const sal_uInt16* pUserListId)
+{
+ if (!pDPObj)
+ return;
+
+ // We need to run this to get all members later.
+ if ( pUserListId )
+ pDPObj->BuildAllDimensionMembers();
+
+ if (nDimIndex < 0)
+ // Invalid dimension index. Bail out.
+ return;
+
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if (!pSaveData)
+ return;
+
+ ScDPSaveData aNewSaveData(*pSaveData);
+ bool bDataLayout;
+ OUString aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
+ ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
+ if (!pSaveDim)
+ return;
+
+ // manual evaluation of sort order is only needed if a user list id is given
+ if ( pUserListId )
+ {
+ typedef ScDPSaveDimension::MemberList MemList;
+ const MemList& rDimMembers = pSaveDim->GetMembers();
+ vector<OUString> aMembers;
+ std::unordered_set<OUString> aMemberSet;
+ size_t nMemberCount = 0;
+ for (ScDPSaveMember* pMem : rDimMembers)
+ {
+ aMembers.push_back(pMem->GetName());
+ aMemberSet.insert(pMem->GetName());
+ ++nMemberCount;
+ }
+
+ // Sort the member list in ascending order.
+ ScOUStringCollate aCollate( &ScGlobal::GetCollator() );
+ std::stable_sort(aMembers.begin(), aMembers.end(), aCollate);
+
+ // Collect and rank those custom sort strings that also exist in the member name list.
+
+ typedef std::unordered_map<OUString, sal_uInt16> UserSortMap;
+ UserSortMap aSubStrs;
+ sal_uInt16 nSubCount = 0;
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (!pUserList)
+ return;
+
+ {
+ size_t n = pUserList->size();
+ if (!n || *pUserListId >= static_cast<sal_uInt16>(n))
+ return;
+ }
+
+ const ScUserListData& rData = (*pUserList)[*pUserListId];
+ sal_uInt16 n = rData.GetSubCount();
+ for (sal_uInt16 i = 0; i < n; ++i)
+ {
+ OUString aSub = rData.GetSubStr(i);
+ if (!aMemberSet.count(aSub))
+ // This string doesn't exist in the member name set. Don't add this.
+ continue;
+
+ aSubStrs.emplace(aSub, nSubCount++);
+ }
+
+ // Rank all members.
+
+ vector<OUString> aRankedNames(nMemberCount);
+ sal_uInt16 nCurStrId = 0;
+ for (auto const& aMemberName : aMembers)
+ {
+ sal_uInt16 nRank = 0;
+ UserSortMap::const_iterator itrSub = aSubStrs.find(aMemberName);
+ if (itrSub == aSubStrs.end())
+ nRank = nSubCount + nCurStrId++;
+ else
+ nRank = itrSub->second;
+
+ if (!bAscending)
+ nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
+
+ aRankedNames[nRank] = aMemberName;
+ }
+
+ // Re-order ScDPSaveMember instances with the new ranks.
+ for (auto const& aRankedName : aRankedNames)
+ {
+ const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(aRankedName);
+ if (!pOldMem)
+ // All members are supposed to be present.
+ continue;
+
+ pSaveDim->AddMember(std::unique_ptr<ScDPSaveMember>(new ScDPSaveMember(*pOldMem)));
+ }
+
+ // Set the sorting mode to manual for now. We may introduce a new sorting
+ // mode later on.
+
+ sheet::DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
+ pSaveDim->SetSortInfo(&aSortInfo);
+ }
+ else
+ {
+ // without user list id, just apply sorting mode
+
+ sheet::DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
+ aSortInfo.IsAscending = bAscending;
+ pSaveDim->SetSortInfo(&aSortInfo);
+ }
+
+ // Update the datapilot with the newly sorted field members.
+
+ std::unique_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
+ pNewObj->SetSaveData(aNewSaveData);
+ ScDBDocFunc aFunc(*GetViewData().GetDocShell());
+
+ aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
+}
+
+bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
+{
+ bool bRet = false;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
+ if ( pDPObj && pDPObj == rDoc.GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
+ {
+ sheet::DataPilotTableHeaderData aDestData;
+ pDPObj->GetHeaderPositionData( rDest, aDestData );
+ bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
+
+ // look through the source range
+ std::unordered_set< OUString > aMembersSet; // for lookup
+ std::vector< OUString > aMembersVector; // members in original order, for inserting
+ aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
+ static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
+ for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
+ for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
+ {
+ sheet::DataPilotTableHeaderData aSourceData;
+ pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
+ if ( aSourceData.Dimension == aDestData.Dimension && !aSourceData.MemberName.isEmpty() )
+ {
+ if ( aMembersSet.insert( aSourceData.MemberName ).second )
+ {
+ aMembersVector.push_back( aSourceData.MemberName );
+ }
+ // duplicates are ignored
+ }
+ else
+ bValid = false; // empty (subtotal) or different field
+ }
+
+ if ( bValid )
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
+ if ( !bIsDataLayout )
+ {
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
+
+ // get all member names in source order
+ uno::Sequence<OUString> aMemberNames;
+ pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
+
+ bool bInserted = false;
+
+ for (const OUString& aMemberStr : std::as_const(aMemberNames))
+ {
+ if ( !bInserted && aMemberStr == aDestData.MemberName )
+ {
+ // insert dragged items before this item
+ for ( const auto& rMember : aMembersVector )
+ lcl_MoveToEnd( *pDim, rMember );
+ bInserted = true;
+ }
+
+ if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() ) // skip dragged items
+ lcl_MoveToEnd( *pDim, aMemberStr );
+ }
+ // insert dragged item at end if dest wasn't found (for example, empty)
+ if ( !bInserted )
+ for ( const auto& rMember : aMembersVector )
+ lcl_MoveToEnd( *pDim, rMember );
+
+ // Items that were in SaveData, but not in the source, end up at the start of the list.
+
+ // set flag for manual sorting
+ sheet::DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
+ pDim->SetSortInfo( &aSortInfo );
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
+ pNewObj->SetSaveData( aData );
+ aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false ); //! bApi for drag&drop?
+ pNewObj.reset();
+
+ Unmark(); // entry was moved - no use in leaving the old cell selected
+
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool ScDBFunc::HasSelectionForDrillDown( css::sheet::DataPilotFieldOrientation& rOrientation )
+{
+ bool bRet = false;
+
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( pDPObj )
+ {
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (!aEntries.empty())
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+ if ( !bIsDataLayout )
+ {
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
+ if ( pDim )
+ {
+ css::sheet::DataPilotFieldOrientation nDimOrient = pDim->GetOrientation();
+ ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
+ if ( pDim == pInner )
+ {
+ rOrientation = nDimOrient;
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void ScDBFunc::SetDataPilotDetails(bool bShow, const OUString* pNewDimensionName)
+{
+ ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ if ( !pDPObj )
+ return;
+
+ ScDPUniqueStringSet aEntries;
+ tools::Long nSelectDimension = -1;
+ GetSelectedMemberList( aEntries, nSelectDimension );
+
+ if (aEntries.empty())
+ return;
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
+ if ( bIsDataLayout )
+ return;
+
+ ScDPSaveData aData( *pDPObj->GetSaveData() );
+ ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
+
+ if ( bShow && pNewDimensionName )
+ {
+ // add the new dimension with the same orientation, at the end
+
+ ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
+ ScDPSaveDimension* pDuplicated = nullptr;
+ if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
+ {
+ // Need to duplicate the dimension, create column/row in addition to data:
+ // The duplicated dimension inherits the existing settings, pNewDim is modified below.
+ pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
+ }
+
+ css::sheet::DataPilotFieldOrientation nOrientation = pDim->GetOrientation();
+ pNewDim->SetOrientation( nOrientation );
+
+ tools::Long nPosition = LONG_MAX;
+ aData.SetPosition( pNewDim, nPosition );
+
+ ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
+ if ( pDataLayout->GetOrientation() == nOrientation &&
+ aData.GetDataDimensionCount() <= 1 )
+ {
+ // If there is only one data dimension, the data layout dimension
+ // must still be the last one in its orientation.
+ aData.SetPosition( pDataLayout, nPosition );
+ }
+
+ if ( pDuplicated )
+ {
+ // The duplicated (data) dimension needs to be behind the original dimension
+ aData.SetPosition( pDuplicated, nPosition );
+ }
+
+ // Hide details for all visible members (selected are changed below).
+ //! Use all members from source level instead (including non-visible)?
+
+ ScDPUniqueStringSet aVisibleEntries;
+ pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
+
+ for (const OUString& aVisName : aVisibleEntries)
+ {
+ ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
+ pMember->SetShowDetails( false );
+ }
+ }
+
+ for (const auto& rEntry : aEntries)
+ {
+ ScDPSaveMember* pMember = pDim->GetMemberByName(rEntry);
+ pMember->SetShowDetails( bShow );
+ }
+
+ // apply changes
+ ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
+ std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
+ pNewObj->SetSaveData( aData );
+ aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false );
+ pNewObj.reset();
+
+ // unmark cell selection
+ Unmark();
+}
+
+void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if (rDoc.GetDocumentShell()->IsReadOnly())
+ {
+ ErrorMessage(STR_READONLYERR);
+ return;
+ }
+
+ Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
+ Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
+ Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
+ if (!xDDSupplier.is())
+ return;
+
+ Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
+ sal_Int32 nRowSize = aTabData.getLength();
+ if (nRowSize <= 1)
+ // There is no data to show. Bail out.
+ return;
+
+ SCCOL nColSize = aTabData[0].getLength();
+
+ SCTAB nNewTab = GetViewData().GetTabNo();
+
+ ScDocumentUniquePtr pInsDoc(new ScDocument(SCDOCMODE_CLIP));
+ pInsDoc->ResetClip( &rDoc, nNewTab );
+ for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
+ {
+ for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
+ {
+ const Any& rAny = aTabData[nRow][nCol];
+ OUString aStr;
+ double fVal;
+ if (rAny >>= aStr)
+ {
+ pInsDoc->SetString(ScAddress(nCol,nRow,nNewTab), aStr);
+ }
+ else if (rAny >>= fVal)
+ pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
+ }
+ }
+
+ // set number format (important for dates)
+ for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
+ {
+ OUString aStr;
+ if (!(aTabData[0][nCol] >>= aStr))
+ continue;
+
+ Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
+ if (!xPropSet.is())
+ continue;
+
+ Any any = xPropSet->getPropertyValue( SC_UNO_DP_NUMBERFO );
+ sal_Int32 nNumFmt = 0;
+ if (!(any >>= nNumFmt))
+ continue;
+
+ ScPatternAttr aPattern( pInsDoc->GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) );
+ pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
+ }
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
+ pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
+
+ SfxUndoManager* pMgr = GetViewData().GetDocShell()->GetUndoManager();
+ OUString aUndo = ScResId( STR_UNDO_DOOUTLINE );
+ pMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+
+ OUString aNewTabName;
+ rDoc.CreateValidTabName(aNewTabName);
+ if ( InsertTable(aNewTabName, nNewTab) )
+ PasteFromClip( InsertDeleteFlags::ALL, pInsDoc.get() );
+
+ pMgr->LeaveListAction();
+}
+
+// repeat data base operations (sorting, filtering, subtotals)
+
+void ScDBFunc::RepeatDB( bool bRecord )
+{
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDBData* pDBData = GetDBData();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScQueryParam aQueryParam;
+ pDBData->GetQueryParam( aQueryParam );
+ bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
+
+ ScSortParam aSortParam;
+ pDBData->GetSortParam( aSortParam );
+ bool bSort = aSortParam.maKeyState[0].bDoSort;
+
+ ScSubTotalParam aSubTotalParam;
+ pDBData->GetSubTotalParam( aSubTotalParam );
+ bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
+
+ if ( bQuery || bSort || bSubTotal )
+ {
+ bool bQuerySize = false;
+ ScRange aOldQuery;
+ ScRange aNewQuery;
+ if (bQuery && !aQueryParam.bInplace)
+ {
+ ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDest && pDest->IsDoSize())
+ {
+ pDest->GetArea( aOldQuery );
+ bQuerySize = true;
+ }
+ }
+
+ SCTAB nDummy;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
+
+ //! undo only needed data ?
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::unique_ptr<ScRangeName> pUndoRange;
+ std::unique_ptr<ScDBCollection> pUndoDB;
+
+ if (bRecord)
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
+ if (pTable)
+ {
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+
+ SCCOLROW nOutStartCol; // row/column status
+ SCCOLROW nOutStartRow;
+ SCCOLROW nOutEndCol;
+ SCCOLROW nOutEndRow;
+ pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
+ pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
+
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ else
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+
+ // Record data range - including filter results
+ rDoc.CopyToDocument( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
+
+ // all formulas for reference
+ rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc );
+
+ // data base and other ranges
+ ScRangeName* pDocRange = rDoc.GetRangeName();
+ if (!pDocRange->empty())
+ pUndoRange.reset(new ScRangeName( *pDocRange ));
+ ScDBCollection* pDocDB = rDoc.GetDBCollection();
+ if (!pDocDB->empty())
+ pUndoDB.reset(new ScDBCollection( *pDocDB ));
+ }
+
+ if (bSort && bSubTotal)
+ {
+ // sort without subtotals
+
+ aSubTotalParam.bRemoveOnly = true; // is reset below
+ DoSubTotals( aSubTotalParam, false );
+ }
+
+ if (bSort)
+ {
+ pDBData->GetSortParam( aSortParam ); // range may have changed
+ Sort( aSortParam, false, false);
+ }
+ if (bQuery)
+ {
+ pDBData->GetQueryParam( aQueryParam ); // range may have changed
+ ScRange aAdvSource;
+ if (pDBData->GetAdvancedQuerySource(aAdvSource))
+ {
+ rDoc.CreateQueryParam(aAdvSource, aQueryParam);
+ Query( aQueryParam, &aAdvSource, false );
+ }
+ else
+ Query( aQueryParam, nullptr, false );
+
+ // if not inplace the sheet may have changed
+ if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
+ SetTabNo( nTab );
+ }
+ if (bSubTotal)
+ {
+ pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed
+ aSubTotalParam.bRemoveOnly = false;
+ DoSubTotals( aSubTotalParam, false );
+ }
+
+ if (bRecord)
+ {
+ SCTAB nDummyTab;
+ SCCOL nDummyCol;
+ SCROW nDummyRow, nNewEndRow;
+ pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
+
+ const ScRange* pOld = nullptr;
+ const ScRange* pNew = nullptr;
+ if (bQuerySize)
+ {
+ ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
+ aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
+ if (pDest)
+ {
+ pDest->GetArea( aNewQuery );
+ pOld = &aOldQuery;
+ pNew = &aNewQuery;
+ }
+ }
+
+ GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRepeatDB>( GetViewData().GetDocShell(), nTab,
+ nStartCol, nStartRow, nEndCol, nEndRow,
+ nNewEndRow,
+ nCurX, nCurY,
+ std::move(pUndoDoc), std::move(pUndoTab),
+ std::move(pUndoRange), std::move(pUndoDB),
+ pOld, pNew ) );
+ }
+
+ GetViewData().GetDocShell()->PostPaint(
+ ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
+ PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
+ }
+ else // "no not execute any operations"
+ ErrorMessage(STR_MSSG_REPEATDB_0);
+}
+
+void ScDBFunc::OnLOKShowHideColRow(bool bColumns, SCCOLROW nStart)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
+ SfxViewShell* pThisViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pThisViewShell->GetDocId())
+ {
+ if (bColumns)
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+ else
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+
+ if (pTabViewShell->getPart() == nCurrentTabIndex)
+ {
+ pTabViewShell->ShowCursor();
+ pTabViewShell->MarkDataChanged();
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/dbfunc4.cxx b/sc/source/ui/view/dbfunc4.cxx
new file mode 100644
index 0000000000..f13035b291
--- /dev/null
+++ b/sc/source/ui/view/dbfunc4.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 <svx/svditer.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+
+#include <dbfunc.hxx>
+#include <drwlayer.hxx>
+#include <document.hxx>
+
+using namespace com::sun::star;
+
+sal_uInt16 ScDBFunc::DoUpdateCharts(const ScAddress& rPos, ScDocument& rDoc, bool bAllCharts)
+{
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (!pModel)
+ return 0;
+
+ sal_uInt16 nFound = 0;
+
+ sal_uInt16 nPageCount = pModel->GetPageCount();
+ for (sal_uInt16 nPageNo = 0; nPageNo < nPageCount; nPageNo++)
+ {
+ SdrPage* pPage = pModel->GetPage(nPageNo);
+ OSL_ENSURE(pPage, "Page ?");
+
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups);
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if (pObject->GetObjIdentifier() == SdrObjKind::OLE2 && ScDocument::IsChart(pObject))
+ {
+ OUString aName = static_cast<SdrOle2Obj*>(pObject)->GetPersistName();
+ bool bHit = true;
+ if (!bAllCharts)
+ {
+ ScRangeList aRanges;
+ bool bColHeaders = false;
+ bool bRowHeaders = false;
+ rDoc.GetOldChartParameters(aName, aRanges, bColHeaders, bRowHeaders);
+ bHit = aRanges.Contains(rPos);
+ }
+ if (bHit)
+ {
+ rDoc.UpdateChart(aName);
+ ++nFound;
+ }
+ }
+ pObject = aIter.Next();
+ }
+ }
+ return nFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawutil.cxx b/sc/source/ui/view/drawutil.cxx
new file mode 100644
index 0000000000..9658fa7ff2
--- /dev/null
+++ b/sc/source/ui/view/drawutil.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 <o3tl/unit_conversion.hxx>
+#include <vcl/outdev.hxx>
+
+#include <drawutil.hxx>
+#include <document.hxx>
+#include <viewdata.hxx>
+
+void ScDrawUtil::CalcScale( const ScDocument& rDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ const OutputDevice* pDev,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ double nPPTX, double nPPTY,
+ Fraction& rScaleX, Fraction& rScaleY )
+{
+ tools::Long nPixelX = 0;
+ tools::Long nTwipsX = 0;
+ tools::Long nPixelY = 0;
+ tools::Long nTwipsY = 0;
+ for (SCCOL i=nStartCol; i<nEndCol; i++)
+ {
+ sal_uInt16 nWidth = rDoc.GetColWidth(i,nTab);
+ nTwipsX += static_cast<tools::Long>(nWidth);
+ nPixelX += ScViewData::ToPixel( nWidth, nPPTX );
+ }
+
+ for (SCROW nRow = nStartRow; nRow <= nEndRow-1; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ sal_uInt16 nHeight = rDoc.GetRowHeight(nRow, nTab);
+ nTwipsY += static_cast<tools::Long>(nHeight);
+ nPixelY += ScViewData::ToPixel(nHeight, nPPTY);
+ }
+
+ MapMode aHMMMode( MapUnit::Map100thMM, Point(), rZoomX, rZoomY );
+ Point aPixelLog = pDev->PixelToLogic( Point( nPixelX,nPixelY ), aHMMMode );
+
+ // Fraction(double) ctor can be used here (and avoid overflows of PixelLog * Zoom)
+ // because ReduceInaccurate is called later anyway.
+
+ if ( aPixelLog.X() && nTwipsX )
+ rScaleX = Fraction( static_cast<double>(aPixelLog.X()) *
+ static_cast<double>(rZoomX.GetNumerator()) /
+ o3tl::convert<double>(nTwipsX, o3tl::Length::twip, o3tl::Length::mm100) /
+ static_cast<double>(rZoomX.GetDenominator()) );
+ else
+ rScaleX = Fraction( 1, 1 );
+
+ if ( aPixelLog.Y() && nTwipsY )
+ rScaleY = Fraction( static_cast<double>(aPixelLog.Y()) *
+ static_cast<double>(rZoomY.GetNumerator()) /
+ o3tl::convert<double>(nTwipsY, o3tl::Length::twip, o3tl::Length::mm100) /
+ static_cast<double>(rZoomY.GetDenominator()) );
+ else
+ rScaleY = Fraction( 1, 1 );
+
+ // 25 bits of accuracy are needed to always hit the right part of
+ // cells in the last rows (was 17 before 1M rows).
+ rScaleX.ReduceInaccurate( 25 );
+ rScaleY.ReduceInaccurate( 25 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawvie3.cxx b/sc/source/ui/view/drawvie3.cxx
new file mode 100644
index 0000000000..6561423ab2
--- /dev/null
+++ b/sc/source/ui/view/drawvie3.cxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/lok.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+#include <strings.hrc>
+#include <scresid.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include "imapwrap.hxx"
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <userdat.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+
+ScDrawView::ScDrawView(
+ OutputDevice* pOut,
+ ScViewData* pData )
+: FmFormView(*pData->GetDocument().GetDrawLayer(), pOut),
+ pViewData( pData ),
+ pDev( pOut ),
+ rDoc( pData->GetDocument() ),
+ nTab( pData->GetTabNo() ),
+ pDropMarkObj( nullptr ),
+ bInConstruct( true )
+{
+ SetNegativeX(comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(nTab));
+ // #i73602# Use default from the configuration
+ SetBufferedOverlayAllowed(SvtOptionsDrawinglayer::IsOverlayBuffer_Calc());
+
+ // #i74769#, #i75172# Use default from the configuration
+ SetBufferedOutputAllowed(SvtOptionsDrawinglayer::IsPaintBuffer_Calc());
+
+ Construct();
+}
+
+// set anchor
+
+void ScDrawView::SetPageAnchored()
+{
+ if( !AreObjectsMarked() )
+ return;
+
+ const SdrMarkList* pMark = &GetMarkedObjectList();
+ const size_t nCount = pMark->GetMarkCount();
+
+ BegUndo(ScResId(SCSTR_UNDO_PAGE_ANCHOR));
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SdrObject* pObj = pMark->GetMark(i)->GetMarkedSdrObj();
+ AddUndo (std::make_unique<ScUndoAnchorData>( pObj, &rDoc, nTab ));
+ ScDrawLayer::SetPageAnchored( *pObj );
+ }
+ EndUndo();
+
+ if ( pViewData )
+ pViewData->GetDocShell()->SetDrawModified();
+
+ // Remove the anchor object.
+ maHdlList.RemoveAllByKind(SdrHdlKind::Anchor);
+ maHdlList.RemoveAllByKind(SdrHdlKind::Anchor_TR);
+}
+
+void ScDrawView::SetCellAnchored(bool bResizeWithCell)
+{
+ if( !AreObjectsMarked() )
+ return;
+
+ const SdrMarkList* pMark = &GetMarkedObjectList();
+ const size_t nCount = pMark->GetMarkCount();
+
+ BegUndo(ScResId(SCSTR_UNDO_CELL_ANCHOR));
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SdrObject* pObj = pMark->GetMark(i)->GetMarkedSdrObj();
+ AddUndo (std::make_unique<ScUndoAnchorData>( pObj, &rDoc, nTab ));
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, nTab, bResizeWithCell);
+ }
+ EndUndo();
+
+ if ( pViewData )
+ {
+ pViewData->GetDocShell()->SetDrawModified();
+
+ // Set the anchor object.
+ AddCustomHdl();
+ }
+}
+
+ScAnchorType ScDrawView::GetAnchorType() const
+{
+ bool bPage = false;
+ bool bCell = false;
+ bool bCellResize = false;
+ if( AreObjectsMarked() )
+ {
+ const SdrMarkList* pMark = &GetMarkedObjectList();
+ const size_t nCount = pMark->GetMarkCount();
+ for( size_t i=0; i<nCount; ++i )
+ {
+ const SdrObject* pObj = pMark->GetMark(i)->GetMarkedSdrObj();
+ const ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType( *pObj );
+ if( aAnchorType == SCA_CELL )
+ bCell =true;
+ else if (aAnchorType == SCA_CELL_RESIZE)
+ bCellResize = true;
+ else
+ bPage = true;
+ }
+ }
+ if( bPage && !bCell && !bCellResize )
+ return SCA_PAGE;
+ if( !bPage && bCell && !bCellResize )
+ return SCA_CELL;
+ if( !bPage && !bCell && bCellResize )
+ return SCA_CELL_RESIZE;
+ return SCA_DONTKNOW;
+}
+
+namespace {
+
+bool lcl_AreRectanglesApproxEqual(const tools::Rectangle& rRectA, const tools::Rectangle& rRectB)
+{
+ // Twips <-> Hmm conversions introduce +-1 differences although the rectangles should actually
+ // be equal. Therefore test with == is not appropriate in some cases.
+ if (std::abs(rRectA.Left() - rRectB.Left()) > 1)
+ return false;
+ if (std::abs(rRectA.Top() - rRectB.Top()) > 1)
+ return false;
+ if (std::abs(rRectA.Right() - rRectB.Right()) > 1)
+ return false;
+ if (std::abs(rRectA.Bottom() - rRectB.Bottom()) > 1)
+ return false;
+ return true;
+}
+
+/**
+ * Updated the anchors of any non-note object that is cell anchored which
+ * has been moved since the last anchors for its position was calculated.
+ */
+void adjustAnchoredPosition(const SdrHint& rHint, const ScDocument& rDoc, SCTAB nTab)
+{
+ if (rHint.GetKind() != SdrHintKind::ObjectChange && rHint.GetKind() != SdrHintKind::ObjectInserted)
+ return;
+
+ SdrObject* pObj = const_cast<SdrObject*>(rHint.GetObject());
+ if (!pObj)
+ return;
+
+ ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pObj);
+ if (!pAnchor)
+ return;
+
+ if (pAnchor->meType == ScDrawObjData::CellNote)
+ return;
+
+ // SetCellAnchoredFromPosition has to be called only if shape geometry has been changed, and not
+ // if only shape visibility has been changed. It is not enough to test shape rect, because e.g. a
+ // 180deg rotation changes only the logic rect (tdf#139583).
+ ScDrawObjData& rNoRotatedAnchor = *ScDrawLayer::GetNonRotatedObjData(pObj, true /*bCreate*/);
+ if (lcl_AreRectanglesApproxEqual(pAnchor->getShapeRect(), pObj->GetSnapRect())
+ && lcl_AreRectanglesApproxEqual(rNoRotatedAnchor.getShapeRect(), pObj->GetLogicRect()))
+ return;
+
+ if (pAnchor->maStart.Tab() != nTab)
+ // The object is not anchored on the current sheet. Skip it.
+ // TODO: In the future, we may want to adjust objects that are
+ // anchored on all selected sheets.
+ return;
+
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, pAnchor->maStart.Tab(), pAnchor->mbResizeWithCell);
+}
+
+}
+
+void ScDrawView::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
+ adjustAnchoredPosition(*pSdrHint, rDoc, nTab);
+ FmFormView::Notify( rBC,rHint );
+ }
+ else if (auto pDeletedHint = dynamic_cast<const ScTabDeletedHint*>(&rHint)) // Sheet has been deleted
+ {
+ SCTAB nDelTab = pDeletedHint->GetTab();
+ if (ValidTab(nDelTab))
+ {
+ // used to be: HidePagePgNum(nDelTab) - hide only if the deleted sheet is shown here
+ if ( nDelTab == nTab )
+ HideSdrPage();
+ }
+ }
+ else if (auto pChangedHint = dynamic_cast<const ScTabSizeChangedHint*>(&rHint)) // Size has been changed
+ {
+ if ( nTab == pChangedHint->GetTab() )
+ UpdateWorkArea();
+ }
+ else
+ FmFormView::Notify( rBC,rHint );
+}
+
+void ScDrawView::UpdateIMap( SdrObject* pObj )
+{
+ if ( !(pViewData &&
+ pViewData->GetViewShell()->GetViewFrame().HasChildWindow( ScIMapChildWindowId() ) &&
+ pObj && ( dynamic_cast<const SdrGrafObj*>( pObj) != nullptr || dynamic_cast<const SdrOle2Obj*>( pObj) != nullptr )) )
+ return;
+
+ Graphic aGraphic;
+ TargetList aTargetList;
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pObj );
+ const ImageMap* pImageMap = nullptr;
+ if ( pIMapInfo )
+ pImageMap = &pIMapInfo->GetImageMap();
+
+ // handle target list
+ SfxViewFrame::GetTargetList( aTargetList );
+
+ // handle graphics from object
+ if ( auto pGrafObj = dynamic_cast<SdrGrafObj*>( pObj) )
+ aGraphic = pGrafObj->GetGraphic();
+ else
+ {
+ const Graphic* pGraphic = static_cast<const SdrOle2Obj*>(pObj)->GetGraphic();
+ if ( pGraphic )
+ aGraphic = *pGraphic;
+ }
+
+ ScIMapDlgSet( aGraphic, pImageMap, &aTargetList, pObj ); // from imapwrap
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawvie4.cxx b/sc/source/ui/view/drawvie4.cxx
new file mode 100644
index 0000000000..2bd3290982
--- /dev/null
+++ b/sc/source/ui/view/drawvie4.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdundo.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <drawview.hxx>
+#include <global.hxx>
+#include <drwlayer.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <drwtrans.hxx>
+#include <transobj.hxx>
+#include <drawutil.hxx>
+#include <scmod.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <gridwin.hxx>
+#include <userdat.hxx>
+
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace com::sun::star;
+
+Point aDragStartDiff;
+
+void ScDrawView::BeginDrag( vcl::Window* pWindow, const Point& rStartPos )
+{
+ if ( !AreObjectsMarked() )
+ return;
+
+ BrkAction();
+
+ tools::Rectangle aMarkedRect = GetAllMarkedRect();
+
+ aDragStartDiff = rStartPos - aMarkedRect.TopLeft();
+
+ bool bAnyOle, bOneOle;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ CheckOle( rMarkList, bAnyOle, bOneOle );
+
+ ScDocShellRef aDragShellRef;
+ if (bAnyOle)
+ {
+ aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aDragShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
+ std::unique_ptr<SdrModel> pModel(CreateMarkedObjModel());
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ // Charts now always copy their data in addition to the source reference, so
+ // there's no need to call SchDLL::Update for the charts in the clipboard doc.
+ // Update with the data (including NumberFormatter) from the live document would
+ // also store the NumberFormatter in the clipboard chart (#88749#)
+
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) );
+
+ pTransferObj->SetDrawPersist( aDragShellRef.get() ); // keep persist for ole objects alive
+ pTransferObj->SetDragSource( this ); // copies selection
+
+ SC_MOD()->SetDragObject( nullptr, pTransferObj.get() ); // for internal D&D
+ pTransferObj->StartDrag( pWindow, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
+}
+
+namespace {
+
+void getRangeFromDataSource( uno::Reference< chart2::data::XDataSource > const & xDataSource, std::vector<OUString>& rRangeRep)
+{
+ const uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence> > xSeqs = xDataSource->getDataSequences();
+ for (const uno::Reference<chart2::data::XLabeledDataSequence>& xLS : xSeqs)
+ {
+ uno::Reference<chart2::data::XDataSequence> xSeq = xLS->getValues();
+ if (xSeq.is())
+ {
+ OUString aRep = xSeq->getSourceRangeRepresentation();
+ rRangeRep.push_back(aRep);
+ }
+ xSeq = xLS->getLabel();
+ if (xSeq.is())
+ {
+ OUString aRep = xSeq->getSourceRangeRepresentation();
+ rRangeRep.push_back(aRep);
+ }
+ }
+}
+
+void getRangeFromErrorBar(const uno::Reference< chart2::XChartDocument >& rChartDoc, std::vector<OUString>& rRangeRep)
+{
+ uno::Reference <chart2::XDiagram > xDiagram = rChartDoc->getFirstDiagram();
+ if(!xDiagram.is())
+ return;
+
+ uno::Reference< chart2::XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY);
+ if(!xCooSysContainer.is())
+ return;
+
+ const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > xCooSysSequence( xCooSysContainer->getCoordinateSystems());
+ for(const auto& rCooSys : xCooSysSequence)
+ {
+ uno::Reference< chart2::XChartTypeContainer > xChartTypeContainer( rCooSys, uno::UNO_QUERY);
+ if(!xChartTypeContainer.is())
+ continue;
+
+ const uno::Sequence< uno::Reference< chart2::XChartType > > xChartTypeSequence( xChartTypeContainer->getChartTypes() );
+ for(const auto& rChartType : xChartTypeSequence)
+ {
+ uno::Reference< chart2::XDataSeriesContainer > xDataSequenceContainer( rChartType, uno::UNO_QUERY);
+ if(!xDataSequenceContainer.is())
+ continue;
+
+ const uno::Sequence< uno::Reference< chart2::XDataSeries > > xSeriesSequence( xDataSequenceContainer->getDataSeries() );
+ for(const uno::Reference<chart2::XDataSeries>& xSeries : xSeriesSequence)
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xSeries, uno::UNO_QUERY);
+ uno::Reference< chart2::data::XDataSource > xErrorBarY;
+ xPropSet->getPropertyValue("ErrorBarY") >>= xErrorBarY;
+ if(xErrorBarY.is())
+ getRangeFromDataSource(xErrorBarY, rRangeRep);
+ uno::Reference< chart2::data::XDataSource > xErrorBarX;
+ xPropSet->getPropertyValue("ErrorBarX") >>= xErrorBarX;
+ if(xErrorBarX.is())
+ getRangeFromDataSource(xErrorBarX, rRangeRep);
+ }
+ }
+ }
+}
+
+void getRangeFromOle2Object(const SdrOle2Obj& rObj, std::vector<OUString>& rRangeRep)
+{
+ if (!rObj.IsChart())
+ // not a chart object.
+ return;
+
+ const uno::Reference<embed::XEmbeddedObject>& xObj = rObj.GetObjRef();
+ if (!xObj.is())
+ return;
+
+ uno::Reference<chart2::XChartDocument> xChartDoc(xObj->getComponent(), uno::UNO_QUERY);
+ if (!xChartDoc.is())
+ return;
+
+ if(xChartDoc->hasInternalDataProvider())
+ return;
+
+ getRangeFromErrorBar(xChartDoc, rRangeRep);
+
+ uno::Reference<chart2::data::XDataSource> xDataSource(xChartDoc, uno::UNO_QUERY);
+ if (!xDataSource.is())
+ return;
+
+ // Get all data sources used in this chart.
+ getRangeFromDataSource(xDataSource, rRangeRep);
+
+ return;
+}
+
+// Get all cell ranges that are referenced by the selected chart objects.
+void getOleSourceRanges(const SdrMarkList& rMarkList, bool& rAnyOle, bool& rOneOle, std::vector<ScRange>* pRanges = nullptr, const ScDocument* pDoc = nullptr )
+{
+ bool bCalcSourceRanges = pRanges && pDoc;
+ std::vector<OUString> aRangeReps;
+ rAnyOle = rOneOle = false;
+ const size_t nCount = rMarkList.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrMark* pMark = rMarkList.GetMark(i);
+ if ( !pMark )
+ continue;
+
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if ( !pObj )
+ continue;
+
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ rAnyOle = true;
+ rOneOle = (nCount == 1);
+ if ( bCalcSourceRanges )
+ getRangeFromOle2Object( static_cast<const SdrOle2Obj&>( *pObj ), aRangeReps );
+ else
+ break;
+ }
+ else if ( dynamic_cast<const SdrObjGroup*>( pObj) != nullptr )
+ {
+ SdrObjListIter aIter( *pObj, SdrIterMode::DeepNoGroups );
+ SdrObject* pSubObj = aIter.Next();
+ while (pSubObj)
+ {
+ if ( pSubObj->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ rAnyOle = true;
+ // rOneOle remains false - a group isn't treated like a single OLE object
+ if ( !bCalcSourceRanges )
+ return;
+
+ getRangeFromOle2Object( static_cast<const SdrOle2Obj&>( *pSubObj ), aRangeReps );
+ }
+ pSubObj = aIter.Next();
+ }
+ }
+ }
+
+ if (!bCalcSourceRanges)
+ return;
+
+ // Compile all range representation strings into ranges.
+ for (const auto& rRangeRep : aRangeReps)
+ {
+ ScRangeList aRange;
+ ScAddress aAddr;
+ if (aRange.Parse(rRangeRep, *pDoc, pDoc->GetAddressConvention()) & ScRefFlags::VALID)
+ {
+ pRanges->insert(pRanges->end(), aRange.begin(), aRange.end());
+ }
+ else if (aAddr.Parse(rRangeRep, *pDoc, pDoc->GetAddressConvention()) & ScRefFlags::VALID)
+ pRanges->push_back(aAddr);
+ }
+
+ return;
+}
+
+class InsertTabIndex
+{
+ std::vector<SCTAB>& mrTabs;
+public:
+ explicit InsertTabIndex(std::vector<SCTAB>& rTabs) : mrTabs(rTabs) {}
+ void operator() (const ScRange& rRange)
+ {
+ mrTabs.push_back(rRange.aStart.Tab());
+ }
+};
+
+class CopyRangeData
+{
+ ScDocument& mrSrc;
+ ScDocument& mrDest;
+public:
+ CopyRangeData(ScDocument& rSrc, ScDocument& rDest) : mrSrc(rSrc), mrDest(rDest) {}
+
+ void operator() (const ScRange& rRange)
+ {
+ OUString aTabName;
+ mrSrc.GetName(rRange.aStart.Tab(), aTabName);
+
+ SCTAB nTab;
+ if (!mrDest.GetTable(aTabName, nTab))
+ // Sheet by this name doesn't exist.
+ return;
+
+ mrSrc.CopyStaticToDocument(rRange, nTab, mrDest);
+ }
+};
+
+void copyChartRefDataToClipDoc(ScDocument& rSrcDoc, ScDocument& rClipDoc, const std::vector<ScRange>& rRanges)
+{
+ // Get a list of referenced table indices.
+ std::vector<SCTAB> aTabs;
+ std::for_each(rRanges.begin(), rRanges.end(), InsertTabIndex(aTabs));
+ std::sort(aTabs.begin(), aTabs.end());
+ aTabs.erase(std::unique(aTabs.begin(), aTabs.end()), aTabs.end());
+
+ // Get table names.
+ if (aTabs.empty())
+ return;
+
+ // Create sheets only for referenced source sheets.
+ OUString aName;
+ std::vector<SCTAB>::const_iterator it = aTabs.begin(), itEnd = aTabs.end();
+ if (!rSrcDoc.GetName(*it, aName))
+ return;
+
+ rClipDoc.SetTabNameOnLoad(0, aName); // document initially has one sheet.
+
+ for (++it; it != itEnd; ++it)
+ {
+ if (!rSrcDoc.GetName(*it, aName))
+ return;
+
+ rClipDoc.AppendTabOnLoad(aName);
+ }
+
+ std::for_each(rRanges.begin(), rRanges.end(), CopyRangeData(rSrcDoc, rClipDoc));
+}
+
+}
+
+void ScDrawView::CheckOle( const SdrMarkList& rMarkList, bool& rAnyOle, bool& rOneOle )
+{
+ getOleSourceRanges( rMarkList, rAnyOle, rOneOle );
+}
+
+void ScDrawView::DoCopy()
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ std::vector<ScRange> aRanges;
+ bool bAnyOle = false, bOneOle = false;
+ getOleSourceRanges( rMarkList, bAnyOle, bOneOle, &aRanges, &rDoc );
+
+ // update ScGlobal::xDrawClipDocShellRef
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
+ if (ScGlobal::xDrawClipDocShellRef.is() && !aRanges.empty())
+ {
+ // Copy data referenced by the chart objects to the draw clip
+ // document. We need to do this before CreateMarkedObjModel() below.
+ ScDocShellRef xDocSh = ScGlobal::xDrawClipDocShellRef;
+ ScDocument& rClipDoc = xDocSh->GetDocument();
+ copyChartRefDataToClipDoc(rDoc, rClipDoc, aRanges);
+ }
+ std::unique_ptr<SdrModel> pModel(CreateMarkedObjModel());
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ // Charts now always copy their data in addition to the source reference, so
+ // there's no need to call SchDLL::Update for the charts in the clipboard doc.
+ // Update with the data (including NumberFormatter) from the live document would
+ // also store the NumberFormatter in the clipboard chart (#88749#)
+
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj(new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) ));
+
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ pTransferObj->SetDrawPersist( ScGlobal::xDrawClipDocShellRef.get() ); // keep persist for ole objects alive
+ }
+
+ pTransferObj->CopyToClipboard( pViewData->GetActiveWin() ); // system clipboard
+}
+
+uno::Reference<datatransfer::XTransferable> ScDrawView::CopyToTransferable()
+{
+ bool bAnyOle, bOneOle;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ CheckOle( rMarkList, bAnyOle, bOneOle );
+
+ // update ScGlobal::xDrawClipDocShellRef
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
+ std::unique_ptr<SdrModel> pModel( CreateMarkedObjModel() );
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+
+ // Charts now always copy their data in addition to the source reference, so
+ // there's no need to call SchDLL::Update for the charts in the clipboard doc.
+ // Update with the data (including NumberFormatter) from the live document would
+ // also store the NumberFormatter in the clipboard chart (#88749#)
+ // lcl_RefreshChartData( pModel, pViewData->GetDocument() );
+
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScDrawTransferObj ctor
+
+ rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, std::move(aObjDesc) );
+
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ pTransferObj->SetDrawPersist( ScGlobal::xDrawClipDocShellRef.get() ); // keep persist for ole objects alive
+ }
+
+ return pTransferObj;
+}
+
+// Calculate correction for 100%, regardless of current settings
+
+void ScDrawView::CalcNormScale( Fraction& rFractX, Fraction& rFractY ) const
+{
+ double nPPTX = ScGlobal::nScreenPPTX;
+ double nPPTY = ScGlobal::nScreenPPTY;
+
+ if (pViewData)
+ nPPTX /= pViewData->GetDocShell()->GetOutputFactor();
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ rDoc.GetTableArea( nTab, nEndCol, nEndRow );
+ if (nEndCol<20)
+ nEndCol = 20;
+ if (nEndRow<20)
+ nEndRow = 1000;
+
+ Fraction aZoom(1,1);
+ ScDrawUtil::CalcScale( rDoc, nTab, 0,0, nEndCol,nEndRow, pDev, aZoom,aZoom,
+ nPPTX, nPPTY, rFractX,rFractY );
+}
+
+void ScDrawView::SetMarkedOriginalSize()
+{
+ std::unique_ptr<SdrUndoGroup> pUndoGroup(new SdrUndoGroup(GetModel()));
+
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ tools::Long nDone = 0;
+ const size_t nCount = rMarkList.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ SdrObjKind nIdent = pObj->GetObjIdentifier();
+ bool bDo = false;
+ Size aOriginalSize;
+ if (nIdent == SdrObjKind::OLE2)
+ {
+ // TODO/LEAN: working with visual area can switch object to running state
+ uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObj)->GetObjRef();
+ if ( xObj.is() ) // NULL for an invalid object that couldn't be loaded
+ {
+ sal_Int64 nAspect = static_cast<SdrOle2Obj*>(pObj)->GetAspect();
+
+ if ( nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ MapMode aMapMode( MapUnit::Map100thMM );
+ aOriginalSize = static_cast<SdrOle2Obj*>(pObj)->GetOrigObjSize( &aMapMode );
+ bDo = true;
+ }
+ else
+ {
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( static_cast<SdrOle2Obj*>(pObj)->GetAspect() ) );
+ try
+ {
+ awt::Size aSz = xObj->getVisualAreaSize( static_cast<SdrOle2Obj*>(pObj)->GetAspect() );
+ aOriginalSize = OutputDevice::LogicToLogic(
+ Size( aSz.Width, aSz.Height ),
+ MapMode(aUnit),
+ MapMode(MapUnit::Map100thMM));
+ bDo = true;
+ } catch( embed::NoVisualAreaSizeException& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.ui", "Can't get the original size of the object!" );
+ }
+ }
+ }
+ }
+ else if (nIdent == SdrObjKind::Graphic)
+ {
+ const SdrGrafObj* pSdrGrafObj = static_cast<const SdrGrafObj*>(pObj);
+
+ MapMode aSourceMap = pSdrGrafObj->GetGraphic().GetPrefMapMode();
+ MapMode aDestMap( MapUnit::Map100thMM );
+ if (aSourceMap.GetMapUnit() == MapUnit::MapPixel)
+ {
+ // consider pixel correction, so that the bitmap is correct on the screen
+ Fraction aNormScaleX, aNormScaleY;
+ CalcNormScale( aNormScaleX, aNormScaleY );
+ aDestMap.SetScaleX(aNormScaleX);
+ aDestMap.SetScaleY(aNormScaleY);
+ }
+ aOriginalSize = pSdrGrafObj->getOriginalSize();
+ bDo = true;
+ }
+
+ if ( bDo )
+ {
+ tools::Rectangle aDrawRect = pObj->GetLogicRect();
+
+ pUndoGroup->AddAction( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ pObj->Resize( aDrawRect.TopLeft(), Fraction( aOriginalSize.Width(), aDrawRect.GetWidth() ),
+ Fraction( aOriginalSize.Height(), aDrawRect.GetHeight() ) );
+ ++nDone;
+ }
+ }
+
+ if (nDone && pViewData)
+ {
+ pUndoGroup->SetComment(ScResId( STR_UNDO_ORIGINALSIZE ));
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndoGroup));
+ pDocSh->SetDrawModified();
+ }
+}
+
+void ScDrawView::FitToCellSize()
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+
+ if (rMarkList.GetMarkCount() != 1)
+ {
+ SAL_WARN("sc.ui", "Fit to cell only works with one graphic!");
+ return;
+ }
+
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj);
+ if (aAnchorType != SCA_CELL && aAnchorType != SCA_CELL_RESIZE)
+ {
+ SAL_WARN("sc.ui", "Fit to cell only works with cell anchored graphics!");
+ return;
+ }
+
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
+ if (!pObjData)
+ {
+ SAL_WARN("sc.ui", "Missing ScDrawObjData!");
+ return;
+ }
+
+ std::unique_ptr<SdrUndoGroup> pUndoGroup(new SdrUndoGroup(GetModel()));
+ tools::Rectangle aGraphicRect = pObj->GetSnapRect();
+ tools::Rectangle aCellRect = ScDrawLayer::GetCellRect( rDoc, pObjData->maStart, true);
+
+ // For graphic objects, we want to keep the aspect ratio
+ if (pObj->shouldKeepAspectRatio())
+ {
+ tools::Long nWidth = aGraphicRect.GetWidth();
+ assert(nWidth && "div-by-zero");
+ double fScaleX = static_cast<double>(aCellRect.GetWidth()) / static_cast<double>(nWidth);
+ tools::Long nHeight = aGraphicRect.GetHeight();
+ assert(nHeight && "div-by-zero");
+ double fScaleY = static_cast<double>(aCellRect.GetHeight()) / static_cast<double>(nHeight);
+ double fScaleMin = std::min(fScaleX, fScaleY);
+
+ aCellRect.setWidth(static_cast<double>(aGraphicRect.GetWidth()) * fScaleMin);
+ aCellRect.setHeight(static_cast<double>(aGraphicRect.GetHeight()) * fScaleMin);
+ }
+
+ pUndoGroup->AddAction( std::make_unique<SdrUndoGeoObj>( *pObj ) );
+ if (pObj->GetObjIdentifier() == SdrObjKind::CustomShape)
+ pObj->AdjustToMaxRect(aCellRect);
+ else
+ pObj->SetSnapRect(aCellRect);
+
+ pUndoGroup->SetComment(ScResId( STR_UNDO_FITCELLSIZE ));
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndoGroup));
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/drawview.cxx b/sc/source/ui/view/drawview.cxx
new file mode 100644
index 0000000000..c1a48dc6df
--- /dev/null
+++ b/sc/source/ui/view/drawview.cxx
@@ -0,0 +1,1251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xbtmpit.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+
+#include <drawview.hxx>
+#include <global.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <drawutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <tabvwsh.hxx>
+#include <client.hxx>
+#include <scmod.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <viewuno.hxx>
+#include <userdat.hxx>
+#include <postit.hxx>
+#include <undocell.hxx>
+#include <gridwin.hxx>
+
+#include <sc.hrc>
+
+using namespace com::sun::star;
+
+#define SC_HANDLESIZE_BIG 9
+
+void ScDrawView::Construct()
+{
+ EnableExtendedKeyInputDispatcher(false);
+ EnableExtendedMouseEventDispatcher(false);
+
+ SetFrameDragSingles();
+
+ SetMinMoveDistancePixel( 2 );
+ SetHitTolerancePixel( 2 );
+
+ if (pViewData)
+ {
+ SCTAB nViewTab = pViewData->GetTabNo();
+ ShowSdrPage(GetModel().GetPage(nViewTab));
+
+ bool bEx = pViewData->GetViewShell()->IsDrawSelMode();
+ bool bProt = rDoc.IsTabProtected( nViewTab ) ||
+ pViewData->GetSfxDocShell()->IsReadOnly();
+
+ SdrLayer* pLayer;
+ SdrLayerAdmin& rAdmin = GetModel().GetLayerAdmin();
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_BACK);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName(), bProt || !bEx );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_INTERN);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName() );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_FRONT);
+ if (pLayer)
+ {
+ SetLayerLocked( pLayer->GetName(), bProt );
+ SetActiveLayer( pLayer->GetName() ); // set active layer to FRONT
+ }
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_CONTROLS);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName(), bProt );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_HIDDEN);
+ if (pLayer)
+ {
+ SetLayerLocked( pLayer->GetName(), bProt );
+ SetLayerVisible( pLayer->GetName(), false);
+ }
+
+ SetSwapAsynchron();
+ }
+ else
+ {
+ ShowSdrPage(GetModel().GetPage(nTab));
+ }
+
+ UpdateUserViewOptions();
+ RecalcScale();
+ UpdateWorkArea();
+
+ bInConstruct = false;
+}
+
+void ScDrawView::ImplClearCalcDropMarker()
+{
+ pDropMarker.reset();
+}
+
+ScDrawView::~ScDrawView()
+{
+ ImplClearCalcDropMarker();
+}
+
+void ScDrawView::AddCustomHdl()
+{
+ const SdrMarkList &rMrkList = GetMarkedObjectList();
+ const size_t nCount = rMrkList.GetMarkCount();
+ for(size_t nPos=0; nPos<nCount; ++nPos )
+ {
+ SdrObject* pObj = rMrkList.GetMark(nPos)->GetMarkedSdrObj();
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjDataTab(pObj, nTab))
+ {
+ if (ScTabView* pView = pViewData->GetView())
+ pView->CreateAnchorHandles(maHdlList, pAnchor->maStart);
+ }
+ }
+}
+
+void ScDrawView::InvalidateAttribs()
+{
+ if (!pViewData) return;
+ SfxBindings& rBindings = pViewData->GetBindings();
+
+ // true status values:
+ rBindings.InvalidateAll( true );
+}
+
+void ScDrawView::InvalidateDrawTextAttrs()
+{
+ if (!pViewData) return;
+ SfxBindings& rBindings = pViewData->GetBindings();
+
+ // cjk/ctl font items have no configured slots,
+ // need no invalidate
+
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+ rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+ rBindings.Invalidate( SID_ATTR_CHAR_BACK_COLOR );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_10 );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_15 );
+ rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_20 );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+ rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
+ rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+ rBindings.Invalidate( SID_TABLE_VERT_NONE );
+ rBindings.Invalidate( SID_TABLE_VERT_CENTER );
+ rBindings.Invalidate( SID_TABLE_VERT_BOTTOM );
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+}
+
+void ScDrawView::SetMarkedToLayer( SdrLayerID nLayerNo )
+{
+ if (!AreObjectsMarked())
+ return;
+
+ // #i11702# use SdrUndoObjectLayerChange for undo
+ // STR_UNDO_SELATTR is "Attributes" - should use a different text later
+ BegUndo( ScResId( STR_UNDO_SELATTR ) );
+
+ const SdrMarkList& rMark = GetMarkedObjectList();
+ const size_t nCount = rMark.GetMarkCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMark.GetMark(i)->GetMarkedSdrObj();
+ if ( dynamic_cast<const SdrUnoObj*>( pObj) == nullptr && (pObj->GetLayer() != SC_LAYER_INTERN) )
+ {
+ AddUndo( std::make_unique<SdrUndoObjectLayerChange>( *pObj, pObj->GetLayer(), nLayerNo) );
+ pObj->SetLayer( nLayerNo );
+ }
+ }
+
+ EndUndo();
+
+ // repaint is done in SetLayer
+
+ pViewData->GetDocShell()->SetDrawModified();
+
+ // check mark list now instead of later in a timer
+ CheckMarked();
+ MarkListHasChanged();
+}
+
+bool ScDrawView::HasMarkedControl() const
+{
+ SdrObjListIter aIter( GetMarkedObjectList() );
+ for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
+ if( dynamic_cast<const SdrUnoObj*>( pObj) != nullptr )
+ return true;
+ return false;
+}
+
+bool ScDrawView::HasMarkedInternal() const
+{
+ // internal objects should not be inside a group, but who knows...
+ SdrObjListIter aIter( GetMarkedObjectList() );
+ for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
+ if( pObj->GetLayer() == SC_LAYER_INTERN )
+ return true;
+ return false;
+}
+
+void ScDrawView::UpdateWorkArea()
+{
+ SdrPage* pPage = GetModel().GetPage(static_cast<sal_uInt16>(nTab));
+ if (pPage)
+ {
+ Size aPageSize( pPage->GetSize() );
+ tools::Rectangle aNewArea( Point(), aPageSize );
+ if ( aPageSize.Width() < 0 )
+ {
+ // RTL: from max.negative (left) to zero (right)
+ aNewArea.SetRight( 0 );
+ aNewArea.SetLeft( aPageSize.Width() + 1 );
+ }
+ SetWorkArea( aNewArea );
+ }
+ else
+ {
+ OSL_FAIL("Page not found");
+ }
+}
+
+void ScDrawView::DoCut()
+{
+ DoCopy();
+ BegUndo( ScResId( STR_UNDO_CUT ) );
+ DeleteMarked(); // In this View - not affected by 505f change
+ EndUndo();
+}
+
+void ScDrawView::GetScale( Fraction& rFractX, Fraction& rFractY ) const
+{
+ rFractX = aScaleX;
+ rFractY = aScaleY;
+}
+
+void ScDrawView::RecalcScale()
+{
+ double nPPTX;
+ double nPPTY;
+ Fraction aZoomX(1,1);
+ Fraction aZoomY(1,1);
+
+ if (pViewData)
+ {
+ nTab = pViewData->GetTabNo();
+ nPPTX = pViewData->GetPPTX();
+ nPPTY = pViewData->GetPPTY();
+ aZoomX = pViewData->GetZoomX();
+ aZoomY = pViewData->GetZoomY();
+ }
+ else
+ {
+ Point aLogic = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
+ nPPTX = aLogic.X() / 1000.0;
+ nPPTY = aLogic.Y() / 1000.0;
+ //! Zoom, handed over ???
+ }
+
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ rDoc.GetTableArea( nTab, nEndCol, nEndRow );
+ if (nEndCol<20)
+ nEndCol = 20;
+ if (nEndRow<20)
+ nEndRow = 20;
+
+ ScDrawUtil::CalcScale(
+ rDoc, nTab, 0, 0, nEndCol, nEndRow, pDev, aZoomX, aZoomY, nPPTX, nPPTY,
+ aScaleX, aScaleY);
+
+ // clear all evtl existing GridOffset vectors
+ resetGridOffsetsForAllSdrPageViews();
+
+ SdrPageView* pPV = GetSdrPageView();
+ if ( !(pViewData && pPV) )
+ return;
+
+ if ( SdrPage* pPage = pPV->GetPage() )
+ {
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ // Align objects to nearest grid position
+ SyncForGrid( pObj.get() );
+ }
+}
+
+void ScDrawView::DoConnect(SdrOle2Obj* pOleObj)
+{
+ if ( pViewData )
+ pViewData->GetViewShell()->ConnectObject( pOleObj );
+}
+
+void ScDrawView::MarkListHasChanged()
+{
+ FmFormView::MarkListHasChanged();
+
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+
+ // #i110829# remove the cell selection only if drawing objects are selected
+ if ( !bInConstruct && GetMarkedObjectList().GetMarkCount() )
+ {
+ pViewSh->Unmark(); // remove cell selection
+
+ // end cell edit mode if drawing objects are selected
+ SC_MOD()->InputEnterHandler();
+ }
+
+ // deactivate IP
+
+ ScModule* pScMod = SC_MOD();
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ ScClient* pClient = static_cast<ScClient*>( pViewSh->GetIPClient() );
+ if ( pClient && pClient->IsObjectInPlaceActive() && !bUnoRefDialog )
+ {
+ // do not display the handles for ViewShell::Activate from the Reset2Open
+ pClient->DeactivateObject();
+ // replacing image ole graphics is now done in ScClient::UIActivate
+ }
+
+ // Select Ole object?
+
+ SdrOle2Obj* pOle2Obj = nullptr;
+ SdrGrafObj* pGrafObj = nullptr;
+
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+
+ if ( nMarkCount == 0 && !pViewData->GetViewShell()->IsDrawSelMode() && !bInConstruct )
+ {
+ // relock layers that may have been unlocked before
+ LockBackgroundLayer(true);
+ LockInternalLayer();
+ }
+
+ bool bSubShellSet = false;
+ if (nMarkCount == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
+ if (!ScDocument::IsChart(pObj) )
+ pViewSh->SetOleObjectShell(true);
+ else
+ pViewSh->SetChartShell(true);
+ bSubShellSet = true;
+ }
+ else if (pObj->GetObjIdentifier() == SdrObjKind::Graphic)
+ {
+ pGrafObj = static_cast<SdrGrafObj*>(pObj);
+ pViewSh->SetGraphicShell(true);
+ bSubShellSet = true;
+ }
+ else if (pObj->GetObjIdentifier() == SdrObjKind::Media)
+ {
+ pViewSh->SetMediaShell(true);
+ bSubShellSet = true;
+ }
+ else if (pObj->GetObjIdentifier() != SdrObjKind::Text // prevent switching to the drawing shell
+ || !pViewSh->IsDrawTextShell()) // when creating a text object @#70206#
+ {
+ pViewSh->SetDrawShell(true);
+ }
+ }
+
+ if ( nMarkCount && !bSubShellSet )
+ {
+ bool bOnlyControls = true;
+ bool bOnlyGraf = true;
+ for (size_t i=0; i<nMarkCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
+ {
+ const SdrObjList *pLst = pObjGroup->GetSubList();
+ const size_t nListCount = pLst->GetObjCount();
+ if ( nListCount == 0 )
+ {
+ // An empty group (may occur during Undo) is no control or graphics object.
+ // Creating the form shell during undo would lead to problems with the undo manager.
+ bOnlyControls = false;
+ bOnlyGraf = false;
+ }
+ for ( size_t j = 0; j < nListCount; ++j )
+ {
+ SdrObject *pSubObj = pLst->GetObj( j );
+
+ if (dynamic_cast<const SdrUnoObj*>( pSubObj) == nullptr)
+ bOnlyControls = false;
+ if (pSubObj->GetObjIdentifier() != SdrObjKind::Graphic)
+ bOnlyGraf = false;
+
+ if ( !bOnlyControls && !bOnlyGraf ) break;
+ }
+ }
+ else
+ {
+ if (dynamic_cast<const SdrUnoObj*>( pObj) == nullptr)
+ bOnlyControls = false;
+ if (pObj->GetObjIdentifier() != SdrObjKind::Graphic)
+ bOnlyGraf = false;
+ }
+
+ if ( !bOnlyControls && !bOnlyGraf ) break;
+ }
+
+ if(bOnlyControls)
+ {
+ pViewSh->SetDrawFormShell(true); // now UNO controls
+ }
+ else if(bOnlyGraf)
+ {
+ pViewSh->SetGraphicShell(true);
+ }
+ else if(nMarkCount>1)
+ {
+ pViewSh->SetDrawShell(true);
+ }
+ }
+
+ // adjust verbs
+
+ SfxViewFrame& rViewFrame = pViewSh->GetViewFrame();
+ bool bOle = pViewSh->GetViewFrame().GetFrame().IsInPlace();
+ uno::Sequence< embed::VerbDescriptor > aVerbs;
+ if ( pOle2Obj && !bOle )
+ {
+ const uno::Reference < embed::XEmbeddedObject >& xObj = pOle2Obj->GetObjRef();
+ OSL_ENSURE( xObj.is(), "SdrOle2Obj without ObjRef" );
+ if (xObj.is())
+ aVerbs = xObj->getSupportedVerbs();
+ }
+ pViewSh->SetVerbs( aVerbs );
+
+ // image map editor
+
+ if ( pOle2Obj )
+ UpdateIMap( pOle2Obj );
+ else if ( pGrafObj )
+ UpdateIMap( pGrafObj );
+
+ InvalidateAttribs(); // after the image map editor update
+ InvalidateDrawTextAttrs();
+
+ for(sal_uInt32 a(0); a < PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if(OUTDEV_WINDOW == rOutDev.GetOutDevType())
+ {
+ rOutDev.GetOwnerWindow()->PaintImmediately();
+ }
+ }
+
+ // uno object for view returns drawing objects as selection,
+ // so it must notify its SelectionChangeListeners
+
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+ uno::Reference<frame::XController> xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ // update selection transfer object
+
+ pViewSh->CheckSelectionTransfer();
+
+}
+
+bool ScDrawView::SdrBeginTextEdit(
+ SdrObject* pObj,
+ SdrPageView* pPV,
+ vcl::Window* pWinL,
+ bool bIsNewObj,
+ SdrOutliner* pGivenOutliner,
+ OutlinerView* pGivenOutlinerView,
+ bool bDontDeleteOutliner,
+ bool bOnlyOneView,
+ bool bGrabFocus )
+{
+ const bool bRet = FmFormView::SdrBeginTextEdit(
+ pObj, pPV, pWinL, bIsNewObj,
+ pGivenOutliner, pGivenOutlinerView, bDontDeleteOutliner,
+ bOnlyOneView, bGrabFocus );
+
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (OutlinerView* pView = GetTextEditOutlinerView())
+ {
+ tools::Rectangle aRectangle = pView->GetOutputArea();
+ if (pWinL && pWinL->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ OString sRectangle = aRectangle.toString();
+ SfxLokHelper::notifyOtherViews(pViewSh, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle);
+ }
+ }
+
+ SfxFrame& rFrame = pViewSh->GetViewFrame().GetFrame();
+ uno::Reference< frame::XController > xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ return bRet;
+}
+
+SdrEndTextEditKind ScDrawView::SdrEndTextEdit( bool bDontDeleteReally )
+{
+ const SdrEndTextEditKind eRet = FmFormView::SdrEndTextEdit( bDontDeleteReally );
+
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ SfxLokHelper::notifyOtherViews(pViewSh, LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY"_ostr);
+
+ SfxFrame& rFrame = pViewSh->GetViewFrame().GetFrame();
+ uno::Reference< frame::XController > xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ return eRet;
+}
+
+void ScDrawView::ModelHasChanged()
+{
+ SdrObject* pEditObj = GetTextEditObject();
+ if ( pEditObj && !pEditObj->IsInserted() && pViewData )
+ {
+ // SdrObjEditView::ModelHasChanged will end text edit in this case,
+ // so make sure the EditEngine's undo manager is no longer used.
+ pViewData->GetViewShell()->SetDrawTextUndo(nullptr);
+ SetCreateMode(); // don't leave FuText in a funny state
+ }
+
+ FmFormView::ModelHasChanged();
+}
+
+void ScDrawView::UpdateUserViewOptions()
+{
+ if (!pViewData)
+ return;
+
+ const ScViewOptions& rOpt = pViewData->GetOptions();
+ const ScGridOptions& rGrid = rOpt.GetGridOptions();
+
+ SetDragStripes( rOpt.GetOption( VOPT_HELPLINES ) );
+ SetMarkHdlSizePixel( SC_HANDLESIZE_BIG );
+
+ SetGridVisible( rGrid.GetGridVisible() );
+ SetSnapEnabled( rGrid.GetUseGridSnap() );
+ SetGridSnap( rGrid.GetUseGridSnap() );
+
+ Fraction aFractX( rGrid.GetFieldDrawX(), rGrid.GetFieldDivisionX() + 1 );
+ Fraction aFractY( rGrid.GetFieldDrawY(), rGrid.GetFieldDivisionY() + 1 );
+ SetSnapGridWidth( aFractX, aFractY );
+
+ SetGridCoarse( Size( rGrid.GetFieldDrawX(), rGrid.GetFieldDrawY() ) );
+ SetGridFine( Size( rGrid.GetFieldDrawX() / (rGrid.GetFieldDivisionX() + 1),
+ rGrid.GetFieldDrawY() / (rGrid.GetFieldDivisionY() + 1) ) );
+}
+
+SdrObject* ScDrawView::GetObjectByName(std::u16string_view rName)
+{
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SdrModel& rDrawLayer = GetModel();
+ sal_uInt16 nTabCount = rDoc.GetTableCount();
+ for (sal_uInt16 i=0; i<nTabCount; i++)
+ {
+ SdrPage* pPage = rDrawLayer.GetPage(i);
+ DBG_ASSERT(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
+ {
+ return pObject;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+//realize multi-selection of objects
+
+void ScDrawView::SelectCurrentViewObject( std::u16string_view rName )
+{
+ sal_uInt16 nObjectTab = 0;
+ SdrObject* pFound = nullptr;
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SdrModel& rDrawLayer = GetModel();
+ sal_uInt16 nTabCount = rDoc.GetTableCount();
+ for (sal_uInt16 i=0; i<nTabCount && !pFound; i++)
+ {
+ SdrPage* pPage = rDrawLayer.GetPage(i);
+ DBG_ASSERT(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !pFound)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
+ {
+ pFound = pObject;
+ nObjectTab = i;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+ if ( !pFound )
+ return;
+
+ ScTabView* pView = pViewData->GetView();
+ if ( nObjectTab != nTab ) // switch sheet
+ pView->SetTabNo( nObjectTab );
+ DBG_ASSERT( nTab == nObjectTab, "Switching sheets did not work" );
+ pView->ScrollToObject( pFound );
+ if ( pFound->GetLayer() == SC_LAYER_BACK &&
+ !pViewData->GetViewShell()->IsDrawSelMode() &&
+ !rDoc.IsTabProtected( nTab ) &&
+ !pViewData->GetSfxDocShell()->IsReadOnly() )
+ {
+ SdrLayer* pLayer = GetModel().GetLayerAdmin().GetLayerPerID(SC_LAYER_BACK);
+ if (pLayer)
+ SetLayerLocked( pLayer->GetName(), false );
+ }
+ SdrPageView* pPV = GetSdrPageView();
+ const bool bUnMark = IsObjMarked(pFound);
+ MarkObj( pFound, pPV, bUnMark);
+}
+
+bool ScDrawView::SelectObject( std::u16string_view rName )
+{
+ UnmarkAll();
+
+ SCTAB nObjectTab = 0;
+ SdrObject* pFound = nullptr;
+
+ ScDocShell* pShell = rDoc.GetDocumentShell();
+ if (pShell)
+ {
+ SdrModel& rDrawLayer = GetModel();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !pFound; i++)
+ {
+ SdrPage* pPage = rDrawLayer.GetPage(static_cast<sal_uInt16>(i));
+ OSL_ENSURE(pPage,"Page ?");
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject && !pFound)
+ {
+ if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
+ {
+ pFound = pObject;
+ nObjectTab = i;
+ }
+ pObject = aIter.Next();
+ }
+ }
+ }
+ }
+
+ if ( pFound )
+ {
+ ScTabView* pView = pViewData->GetView();
+ if ( nObjectTab != nTab ) // switch sheet
+ pView->SetTabNo( nObjectTab );
+
+ OSL_ENSURE( nTab == nObjectTab, "Switching sheets did not work" );
+
+ pView->ScrollToObject( pFound );
+
+ /* To select an object on the background layer, the layer has to
+ be unlocked even if exclusive drawing selection mode is not active
+ (this is reversed in MarkListHasChanged when nothing is selected) */
+ if ( pFound->GetLayer() == SC_LAYER_BACK &&
+ !pViewData->GetViewShell()->IsDrawSelMode() &&
+ !rDoc.IsTabProtected( nTab ) &&
+ !pViewData->GetSfxDocShell()->IsReadOnly() )
+ {
+ LockBackgroundLayer(false);
+ }
+
+ SdrPageView* pPV = GetSdrPageView();
+ MarkObj( pFound, pPV );
+ }
+
+ return ( pFound != nullptr );
+}
+
+//If object is marked , return true , else return false .
+bool ScDrawView::GetObjectIsMarked( const SdrObject* pObject )
+{
+ bool bisMarked = false;
+ if (pObject )
+ {
+ bisMarked = IsObjMarked(pObject);
+ }
+ return bisMarked;
+}
+
+bool ScDrawView::InsertObjectSafe(SdrObject* pObj, SdrPageView& rPV)
+{
+ SdrInsertFlags nOptions=SdrInsertFlags::NONE;
+ // Do not change marks when the ole object is active
+ // (for Drop from ole object would otherwise be deactivated in the middle of ExecuteDrag!)
+
+ if (pViewData)
+ {
+ SfxInPlaceClient* pClient = pViewData->GetViewShell()->GetIPClient();
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ nOptions |= SdrInsertFlags::DONTMARK;
+ }
+
+ return InsertObjectAtView(pObj, rPV, nOptions);
+}
+
+SdrObject* ScDrawView::GetMarkedNoteCaption( ScDrawObjData** ppCaptData )
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if( pViewData && (rMarkList.GetMarkCount() == 1) )
+ {
+ SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObj, pViewData->GetTabNo() ) )
+ {
+ if( ppCaptData ) *ppCaptData = pCaptData;
+ return pObj;
+ }
+ }
+ return nullptr;
+}
+
+void ScDrawView::LockCalcLayer( SdrLayerID nLayer, bool bLock )
+{
+ SdrLayer* pLockLayer = GetModel().GetLayerAdmin().GetLayerPerID( nLayer );
+ if( pLockLayer && (IsLayerLocked( pLockLayer->GetName() ) != bLock) )
+ SetLayerLocked( pLockLayer->GetName(), bLock );
+}
+
+void ScDrawView::MakeVisible( const tools::Rectangle& rRect, vcl::Window& rWin )
+{
+ //! Evaluate rWin properly
+ //! change zoom if necessary
+
+ if ( pViewData && pViewData->GetActiveWin() == &rWin )
+ pViewData->GetView()->MakeVisible( rRect );
+}
+
+SfxViewShell* ScDrawView::GetSfxViewShell() const
+{
+ return pViewData->GetViewShell();
+}
+
+void ScDrawView::DeleteMarked()
+{
+ // try to delete a note caption object with its cell note in the Calc document
+ ScDrawObjData* pCaptData = nullptr;
+ if( SdrObject* pCaptObj = GetMarkedNoteCaption( &pCaptData ) )
+ {
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ ScDocShell* pDocShell = pViewData ? pViewData->GetDocShell() : nullptr;
+ SfxUndoManager* pUndoMgr = pDocShell ? pDocShell->GetUndoManager() : nullptr;
+ bool bUndo = pDrawLayer && pDocShell && pUndoMgr && rDoc.IsUndoEnabled();
+
+ // remove the cell note from document, we are its owner now
+ std::unique_ptr<ScPostIt> pNote = rDoc.ReleaseNote( pCaptData->maStart );
+ OSL_ENSURE( pNote, "ScDrawView::DeleteMarked - cell note missing in document" );
+ if( pNote )
+ {
+ // rescue note data for undo (with pointer to caption object)
+ ScNoteData aNoteData = pNote->GetNoteData();
+ OSL_ENSURE( aNoteData.mxCaption.get() == pCaptObj, "ScDrawView::DeleteMarked - caption object does not match" );
+ // collect the drawing undo action created while deleting the note
+ if( bUndo )
+ pDrawLayer->BeginCalcUndo(false);
+ // delete the note (already removed from document above)
+ pNote.reset();
+ // add the undo action for the note
+ if( bUndo )
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( *pDocShell, pCaptData->maStart, aNoteData, false, pDrawLayer->GetCalcUndo() ) );
+ // repaint the cell to get rid of the note marker
+ if( pDocShell )
+ pDocShell->PostPaintCell( pCaptData->maStart );
+ // done, return now to skip call of FmFormView::DeleteMarked()
+ return;
+ }
+ }
+
+ FmFormView::DeleteMarked();
+}
+
+SdrEndTextEditKind ScDrawView::ScEndTextEdit()
+{
+ bool bIsTextEdit = IsTextEdit();
+ SdrEndTextEditKind eKind = SdrEndTextEdit();
+
+ if (bIsTextEdit)
+ pViewData->GetViewShell()->SetDrawTextUndo(nullptr); // the "normal" undo manager
+
+ return eKind;
+}
+
+void ScDrawView::MarkDropObj( SdrObject* pObj )
+{
+ if ( pDropMarkObj != pObj )
+ {
+ pDropMarkObj = pObj;
+ ImplClearCalcDropMarker();
+
+ if(pDropMarkObj)
+ {
+ pDropMarker.reset( new SdrDropMarkerOverlay(*this, *pDropMarkObj) );
+ }
+ }
+}
+
+// In order to counteract the effects of rounding due to the nature of how the
+// grid positions are calculated and drawn we calculate the offset needed at the
+// current zoom to be applied to an SrdObject when it is drawn in order to make
+// sure that it's position relative to the nearest cell anchor doesn't change.
+// Of course not all shape(s)/control(s) are cell anchored, if the
+// object doesn't have a cell anchor we synthesise a temporary anchor.
+void ScDrawView::SyncForGrid( SdrObject* pObj )
+{
+ // process members of a group shape separately
+ if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
+ {
+ SdrObjList *pLst = pObjGroup->GetSubList();
+ for (const rtl::Reference<SdrObject>& pChild : *pLst)
+ SyncForGrid( pChild.get() );
+ }
+
+ ScSplitPos eWhich = pViewData->GetActivePart();
+ ScGridWindow* pGridWin = pViewData->GetActiveWin();
+ ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
+ if ( !pGridWin )
+ return;
+
+ ScAddress aOldStt;
+ if( pData && pData->maStart.IsValid())
+ {
+ aOldStt = pData->maStart;
+ }
+ else
+ {
+ // Page anchored object so...
+ // synthesise an anchor ( but don't attach it to
+ // the object as we want to maintain page anchoring )
+ ScDrawObjData aAnchor;
+ const tools::Rectangle aObjRect(pObj->GetLogicRect());
+ ScDrawLayer::GetCellAnchorFromPosition(
+ aObjRect,
+ aAnchor,
+ rDoc,
+ GetTab());
+ aOldStt = aAnchor.maStart;
+ }
+ MapMode aDrawMode = pGridWin->GetDrawMapMode();
+ // find pos anchor position
+ Point aOldPos( rDoc.GetColOffset( aOldStt.Col(), aOldStt.Tab() ), rDoc.GetRowOffset( aOldStt.Row(), aOldStt.Tab() ) );
+ aOldPos.setX(convertTwipToMm100(aOldPos.X()));
+ aOldPos.setY(convertTwipToMm100(aOldPos.Y()));
+ // find position of same point on the screen ( e.g. grid )
+ Point aCurPos = pViewData->GetScrPos( aOldStt.Col(), aOldStt.Row(), eWhich, true );
+ Point aCurPosHmm = pGridWin->PixelToLogic(aCurPos, aDrawMode );
+ Point aGridOff = aCurPosHmm - aOldPos;
+ // fdo#63878 Fix the X position for RTL Sheet
+ if( rDoc.IsNegativePage( GetTab() ) && !comphelper::LibreOfficeKit::isActive() )
+ aGridOff.setX( aCurPosHmm.getX() + aOldPos.getX() );
+}
+
+void ScDrawView::resetGridOffsetsForAllSdrPageViews()
+{
+ SdrPageView* pPageView(GetSdrPageView());
+
+ if(nullptr == pPageView)
+ return;
+
+ for(sal_uInt32 a(0); a < pPageView->PageWindowCount(); a++)
+ {
+ SdrPageWindow* pPageWindow(pPageView->GetPageWindow(a));
+ assert(pPageWindow && "SdrView::SetMasterPagePaintCaching: Corrupt SdrPageWindow list (!)");
+
+ if(nullptr != pPageWindow)
+ {
+ sdr::contact::ObjectContact& rObjectContact(pPageWindow->GetObjectContact());
+
+ if(rObjectContact.supportsGridOffsets())
+ {
+ rObjectContact.resetAllGridOffsets();
+ }
+ }
+ }
+}
+
+bool ScDrawView::calculateGridOffsetForSdrObject(
+ SdrObject& rSdrObject,
+ basegfx::B2DVector& rTarget) const
+{
+ if (comphelper::LibreOfficeKit::isActive() &&
+ !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return false;
+
+ ScGridWindow* pGridWin(pViewData->GetActiveWin());
+
+ if(nullptr == pGridWin)
+ {
+ return false;
+ }
+
+ ScDrawObjData* pData(ScDrawLayer::GetObjData(&rSdrObject));
+ ScAddress aOldStt;
+
+ if(nullptr != pData && pData->maStart.IsValid())
+ {
+ aOldStt = pData->maStart;
+ }
+ else
+ {
+ // Page anchored object so...
+ // synthesise an anchor ( but don't attach it to
+ // the object as we want to maintain page anchoring )
+ ScDrawObjData aAnchor;
+ const tools::Rectangle aObjRect(rSdrObject.GetLogicRect());
+ ScDrawLayer::GetCellAnchorFromPosition(
+ aObjRect,
+ aAnchor,
+ rDoc,
+ GetTab());
+ aOldStt = aAnchor.maStart;
+ }
+
+ MapMode aDrawMode = pGridWin->GetDrawMapMode();
+
+ // find pos anchor position
+ Point aOldPos(rDoc.GetColOffset(aOldStt.Col(), aOldStt.Tab()), rDoc.GetRowOffset(aOldStt.Row(), aOldStt.Tab()));
+ aOldPos.setX(convertTwipToMm100(aOldPos.X()));
+ aOldPos.setY(convertTwipToMm100(aOldPos.Y()));
+
+ // find position of same point on the screen ( e.g. grid )
+ ScSplitPos eWhich(pViewData->GetActivePart());
+ Point aCurPos(pViewData->GetScrPos(aOldStt.Col(), aOldStt.Row(), eWhich, true));
+ Point aCurPosHmm(pGridWin->PixelToLogic(aCurPos, aDrawMode));
+ Point aGridOff(aCurPosHmm - aOldPos);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bNegativePage = rDoc.IsNegativePage(GetTab());
+
+ // fdo#63878 Fix the X position for RTL Sheet
+ if(bNegativePage && !bLOKActive)
+ {
+ aGridOff.setX(aCurPosHmm.getX() + aOldPos.getX());
+ }
+
+ rTarget.setX(bNegativePage && bLOKActive ? -aGridOff.X() : aGridOff.X());
+ rTarget.setY(aGridOff.Y());
+ return true;
+}
+
+bool ScDrawView::calculateGridOffsetForB2DRange(
+ const basegfx::B2DRange& rB2DRange,
+ basegfx::B2DVector& rTarget) const
+{
+ ScGridWindow* pGridWin(pViewData->GetActiveWin());
+
+ if(nullptr == pGridWin || rB2DRange.isEmpty())
+ {
+ return false;
+ }
+
+ // No SdrObject, so synthesise an anchor ( but don't attach it to
+ // the object as we want to maintain page anchoring )
+ ScDrawObjData aAnchor;
+ const tools::Rectangle aRectangle(
+ basegfx::fround(rB2DRange.getMinX()), basegfx::fround(rB2DRange.getMinY()),
+ basegfx::fround(rB2DRange.getMaxX()), basegfx::fround(rB2DRange.getMaxY()));
+ ScDrawLayer::GetCellAnchorFromPosition(
+ aRectangle,
+ aAnchor,
+ rDoc,
+ GetTab());
+ ScAddress aOldStt(aAnchor.maStart);
+
+ MapMode aDrawMode = pGridWin->GetDrawMapMode();
+
+ // find pos anchor position
+ Point aOldPos(rDoc.GetColOffset(aOldStt.Col(), aOldStt.Tab()), rDoc.GetRowOffset(aOldStt.Row(), aOldStt.Tab()));
+ aOldPos.setX(convertTwipToMm100(aOldPos.X()));
+ aOldPos.setY(convertTwipToMm100(aOldPos.Y()));
+
+ // find position of same point on the screen ( e.g. grid )
+ ScSplitPos eWhich(pViewData->GetActivePart());
+ Point aCurPos(pViewData->GetScrPos(aOldStt.Col(), aOldStt.Row(), eWhich, true));
+ Point aCurPosHmm(pGridWin->PixelToLogic(aCurPos, aDrawMode));
+ Point aGridOff(aCurPosHmm - aOldPos);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bNegativePage = rDoc.IsNegativePage(GetTab());
+
+ // fdo#63878 Fix the X position for RTL Sheet
+ if(bNegativePage && !bLOKActive)
+ {
+ aGridOff.setX(aCurPosHmm.getX() + aOldPos.getX());
+ }
+
+ rTarget.setX(bLOKActive && bNegativePage ? -aGridOff.X() : aGridOff.X());
+ rTarget.setY(aGridOff.Y());
+ return true;
+}
+
+// Create a new view-local UndoManager manager for Calc
+std::unique_ptr<SdrUndoManager> ScDrawView::createLocalTextUndoManager()
+{
+ std::unique_ptr<SdrUndoManager> pUndoManager(new SdrUndoManager);
+ ScDocShell* pDocShell = pViewData ? pViewData->GetDocShell() : nullptr;
+ pUndoManager->SetDocShell(pDocShell);
+ return pUndoManager;
+}
+
+// #i123922# helper to apply a Graphic to an existing SdrObject
+SdrObject* ScDrawView::ApplyGraphicToObject(
+ SdrObject& rHitObject,
+ const Graphic& rGraphic,
+ const OUString& rBeginUndoText,
+ const OUString& rFile)
+{
+ if(auto pGrafHitObj = dynamic_cast< SdrGrafObj* >(&rHitObject))
+ {
+ rtl::Reference<SdrGrafObj> pNewGrafObj = SdrObject::Clone(*pGrafHitObj, rHitObject.getSdrModelFromSdrObject());
+
+ pNewGrafObj->SetGraphic(rGraphic);
+ BegUndo(rBeginUndoText);
+ ReplaceObjectAtView(&rHitObject, *GetSdrPageView(), pNewGrafObj.get());
+
+ // set in all cases - the Clone() will have copied an existing link (!)
+ pNewGrafObj->SetGraphicLink( rFile );
+
+ EndUndo();
+ return pNewGrafObj.get();
+ }
+ else if(rHitObject.IsClosedObj() && !dynamic_cast< SdrOle2Obj* >(&rHitObject))
+ {
+ AddUndo(std::make_unique<SdrUndoAttrObj>(rHitObject));
+
+ SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(GetModel().GetItemPool());
+
+ aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+ aSet.Put(XFillBitmapItem(OUString(), rGraphic));
+ rHitObject.SetMergedItemSetAndBroadcast(aSet);
+ return &rHitObject;
+ }
+
+ return nullptr;
+}
+
+// Own derivation of ObjectContact to allow on-demand calculation of
+// GridOffset for non-linear ViewToDevice transformation (calc)
+namespace sdr::contact
+{
+ namespace {
+
+ class ObjectContactOfScDrawView final : public ObjectContactOfPageView
+ {
+ private:
+ // The ScDrawView to work on
+ const ScDrawView& mrScDrawView;
+
+ public:
+ explicit ObjectContactOfScDrawView(
+ const ScDrawView& rScDrawView,
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName);
+
+ virtual bool supportsGridOffsets() const override;
+ virtual void calculateGridOffsetForViewObjectContact(
+ basegfx::B2DVector& rTarget,
+ const ViewObjectContact& rClient) const override;
+ virtual void calculateGridOffsetForB2DRange(
+ basegfx::B2DVector& rTarget,
+ const basegfx::B2DRange& rB2DRange) const override;
+ };
+
+ }
+
+ ObjectContactOfScDrawView::ObjectContactOfScDrawView(
+ const ScDrawView& rScDrawView,
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName)
+ : ObjectContactOfPageView(rPageWindow, pDebugName),
+ mrScDrawView(rScDrawView)
+ {
+ }
+
+ bool ObjectContactOfScDrawView::supportsGridOffsets() const
+ {
+ // Except when scPrintTwipsMsgs flag is active,
+ // Calc in LOK mode directly sets pixel-aligned logical coordinates for draw-objects.
+ if (comphelper::LibreOfficeKit::isActive() &&
+ !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return false;
+
+ // no GridOffset support for printer
+ if(isOutputToPrinter())
+ {
+ return false;
+ }
+
+ // no GridOffset support for PDF export
+ if(isOutputToPDFFile())
+ {
+ return false;
+ }
+
+ // yes - we support it
+ return true;
+ }
+
+ void ObjectContactOfScDrawView::calculateGridOffsetForViewObjectContact(
+ basegfx::B2DVector& rTarget,
+ const ViewObjectContact& rClient) const
+ {
+ // Here the on-demand calculation happens. Try to access the SdrObject involved
+ SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
+
+ if(nullptr != pTargetSdrObject)
+ {
+ mrScDrawView.calculateGridOffsetForSdrObject(
+ *pTargetSdrObject,
+ rTarget);
+ }
+ }
+
+ void ObjectContactOfScDrawView::calculateGridOffsetForB2DRange(
+ basegfx::B2DVector& rTarget,
+ const basegfx::B2DRange& rB2DRange) const
+ {
+ // Here the on-demand calculation happens. Try to access the SdrObject involved
+ if(!rB2DRange.isEmpty())
+ {
+ mrScDrawView.calculateGridOffsetForB2DRange(
+ rB2DRange,
+ rTarget);
+ }
+ }
+}
+
+// Create own derivation of ObjectContact for calc
+sdr::contact::ObjectContact* ScDrawView::createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) const
+{
+ return new sdr::contact::ObjectContactOfScDrawView(
+ *this,
+ rPageWindow,
+ pDebugName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/editsh.cxx b/sc/source/ui/view/editsh.cxx
new file mode 100644
index 0000000000..c392f111e2
--- /dev/null
+++ b/sc/source/ui/view/editsh.cxx
@@ -0,0 +1,1427 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <comphelper/lok.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <i18nutil/unicode.hxx>
+#include <i18nutil/transliteration.hxx>
+
+#include <svx/clipfmtitem.hxx>
+#include <svx/svxdlg.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/urlfieldhelper.hxx>
+#include <editeng/editund2.hxx>
+#include <svx/hlnkitem.hxx>
+#include <vcl/EnumContext.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/cliplistener.hxx>
+#include <svl/whiter.hxx>
+#include <sot/formats.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/unohelp2.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/colritem.hxx>
+
+#include <editsh.hxx>
+#include <global.hxx>
+#include <appoptio.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <inputhdl.hxx>
+#include <viewutil.hxx>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <reffind.hxx>
+#include <tabvwsh.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <gridwin.hxx>
+
+#define ShellClass_ScEditShell
+#include <scslots.hxx>
+
+#include <scui_def.hxx>
+#include <scabstdlg.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+
+SFX_IMPL_INTERFACE(ScEditShell, SfxShell)
+
+void ScEditShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("celledit");
+}
+
+ScEditShell::ScEditShell(EditView* pView, ScViewData& rData) :
+ pEditView (pView),
+ rViewData (rData),
+ bPastePossible (false),
+ bIsInsertMode (true)
+{
+ SetPool( pEditView->GetEditEngine()->GetEmptyItemSet().GetPool() );
+ SetUndoManager( &pEditView->GetEditEngine()->GetUndoManager() );
+ SetName("EditCell");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::EditCell));
+}
+
+ScEditShell::~ScEditShell()
+{
+ if ( mxClipEvtLstnr.is() )
+ {
+ mxClipEvtLstnr->RemoveListener( rViewData.GetActiveWin() );
+
+ // The listener may just now be waiting for the SolarMutex and call the link
+ // afterwards, in spite of RemoveListener. So the link has to be reset, too.
+ mxClipEvtLstnr->ClearCallbackLink();
+ }
+}
+
+ScInputHandler* ScEditShell::GetMyInputHdl()
+{
+ return SC_MOD()->GetInputHdl( rViewData.GetViewShell() );
+}
+
+void ScEditShell::SetEditView(EditView* pView)
+{
+ pEditView = pView;
+ pEditView->SetInsertMode( bIsInsertMode );
+ SetPool( pEditView->GetEditEngine()->GetEmptyItemSet().GetPool() );
+ SetUndoManager( &pEditView->GetEditEngine()->GetUndoManager() );
+}
+
+static void lcl_RemoveAttribs( EditView& rEditView )
+{
+ ScEditEngineDefaulter* pEngine = static_cast<ScEditEngineDefaulter*>(rEditView.GetEditEngine());
+
+ bool bOld = pEngine->SetUpdateLayout(false);
+
+ OUString aName = ScResId( STR_UNDO_DELETECONTENTS );
+ ViewShellId nViewShellId(-1);
+ if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
+ nViewShellId = pViewSh->GetViewShellId();
+ pEngine->GetUndoManager().EnterListAction( aName, aName, 0, nViewShellId );
+
+ rEditView.RemoveAttribs(true);
+ pEngine->RepeatDefaults(); // paragraph attributes from cell formats must be preserved
+
+ pEngine->GetUndoManager().LeaveListAction();
+
+ pEngine->SetUpdateLayout(bOld);
+}
+
+static void lclInsertCharacter( EditView* pTableView, EditView* pTopView, sal_Unicode cChar )
+{
+ OUString aString( cChar );
+ if( pTableView )
+ pTableView->InsertText( aString );
+ if( pTopView )
+ pTopView->InsertText( aString );
+}
+
+void ScEditShell::Execute( SfxRequest& rReq )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ SfxBindings& rBindings = rViewData.GetBindings();
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ OSL_ENSURE(pHdl,"no ScInputHandler");
+
+ EditView* pTopView = pHdl->GetTopView(); // Has thee input cell the focus?
+ EditView* pTableView = pHdl->GetTableView();
+
+ OSL_ENSURE(pTableView,"no EditView :-(");
+ /* #i91683# No EditView if spell-check dialog is active and positioned on
+ * an error and user immediately (without double click or F2) selected a
+ * text portion of that cell with the mouse and wanted to modify it. */
+ /* FIXME: Bailing out only cures the symptom and prevents a crash, no edit
+ * action is possible. A real fix somehow would need to create a valid
+ * EditView from the spell-check view. */
+ if (!pTableView)
+ return;
+
+ EditEngine* pEngine = pTableView->GetEditEngine();
+
+ pHdl->DataChanging();
+ bool bSetSelIsRef = false;
+ bool bSetModified = true;
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_INSERT:
+ case FID_INS_CELL_CONTENTS: // Insert taste, while defined as Acc
+ bIsInsertMode = !pTableView->IsInsertMode();
+ pTableView->SetInsertMode( bIsInsertMode );
+ if (pTopView)
+ pTopView->SetInsertMode( bIsInsertMode );
+ rBindings.Invalidate( SID_ATTR_INSERT );
+ break;
+
+ case SID_THES:
+ {
+ OUString aReplaceText;
+ const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE);
+ if (pItem2)
+ aReplaceText = pItem2->GetValue();
+ if (!aReplaceText.isEmpty())
+ ReplaceTextWithSynonym( *pEditView, aReplaceText );
+ }
+ break;
+
+ case SID_COPY:
+ pTableView->Copy();
+ bSetModified = false;
+ break;
+
+ case SID_CUT:
+ pTableView->Cut();
+ if (pTopView)
+ pTopView->DeleteSelected();
+ break;
+
+ case SID_PASTE:
+ {
+ EVControlBits nControl = pTableView->GetControlWord();
+ if (pTopView)
+ {
+ pTopView->Paste();
+ pTableView->SetControlWord(nControl | EVControlBits::SINGLELINEPASTE);
+ }
+
+ pTableView->PasteSpecial();
+ pTableView->SetControlWord(nControl);
+ }
+ break;
+
+ case SID_DELETE:
+ pTableView->DeleteSelected();
+ if (pTopView)
+ pTopView->DeleteSelected();
+ break;
+
+ case SID_CELL_FORMAT_RESET: // "Standard"
+ lcl_RemoveAttribs( *pTableView );
+ if ( pTopView )
+ lcl_RemoveAttribs( *pTopView );
+ break;
+
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ {
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ if (auto pIntItem = dynamic_cast<const SfxUInt32Item*>( pItem))
+ nFormat = static_cast<SotClipboardFormatId>(pIntItem->GetValue());
+
+ if ( nFormat != SotClipboardFormatId::NONE )
+ {
+ if (SotClipboardFormatId::STRING == nFormat)
+ pTableView->Paste();
+ else
+ pTableView->PasteSpecial();
+
+ if (pTopView)
+ pTopView->Paste();
+ }
+ }
+ break;
+
+ case SID_PASTE_SPECIAL:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(rViewData.GetDialogParent()));
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+ pDlg->Insert( SotClipboardFormatId::STRING, OUString() );
+ pDlg->Insert( SotClipboardFormatId::RTF, OUString() );
+ pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() );
+ // Do not offer SotClipboardFormatId::STRING_TSVC for
+ // in-cell paste.
+
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
+
+ nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() );
+ pDlg.disposeAndClear();
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+
+ if (nFormat != SotClipboardFormatId::NONE)
+ {
+ if (SotClipboardFormatId::STRING == nFormat)
+ pTableView->Paste();
+ else
+ pTableView->PasteSpecial();
+
+ if (pTopView)
+ pTopView->Paste();
+ }
+
+ if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
+ pViewWindow->GrabFocus();
+ }
+ break;
+
+ case SID_PASTE_UNFORMATTED:
+ {
+ pTableView->Paste();
+
+ if (pTopView)
+ {
+ pTopView->Paste();
+ if (vcl::Window* pViewWindow = pTopView->GetWindow())
+ pViewWindow->GrabFocus();
+ }
+ }
+ break;
+
+ case SID_SELECTALL:
+ {
+ sal_Int32 nPar = pEngine->GetParagraphCount();
+ if (nPar)
+ {
+ sal_Int32 nLen = pEngine->GetTextLen(nPar-1);
+ pTableView->SetSelection(ESelection(0,0,nPar-1,nLen));
+ if (pTopView)
+ pTopView->SetSelection(ESelection(0,0,nPar-1,nLen));
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ }
+ }
+ return;
+ case SID_UNICODE_NOTATION_TOGGLE:
+ {
+ EditView* pActiveView = pHdl->GetActiveView();
+ if( pActiveView )
+ {
+ OUString sInput = pEngine->GetText();
+ ESelection aSel( pActiveView->GetSelection() );
+ if( aSel.HasRange() )
+ sInput = pActiveView->GetSelected();
+
+ if( aSel.nStartPos > aSel.nEndPos )
+ aSel.nEndPos = aSel.nStartPos;
+
+ //calculate a valid end-position by reading logical characters
+ sal_Int32 nUtf16Pos=0;
+ while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
+ {
+ sInput.iterateCodePoints(&nUtf16Pos);
+ if( nUtf16Pos > aSel.nEndPos )
+ aSel.nEndPos = nUtf16Pos;
+ }
+
+ ToggleUnicodeCodepoint aToggle;
+ while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
+ --nUtf16Pos;
+ OUString sReplacement = aToggle.ReplacementString();
+ if( !sReplacement.isEmpty() )
+ {
+ aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
+ pTableView->SetSelection( aSel );
+ pTableView->InsertText(sReplacement, true);
+ if( pTopView )
+ {
+ pTopView->SetSelection( aSel );
+ pTopView->InsertText(sReplacement, true);
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_CHARMAP:
+ {
+ SvtScriptType nScript = pTableView->GetSelectedScriptType();
+ sal_uInt16 nFontWhich = ( nScript == SvtScriptType::ASIAN ) ? EE_CHAR_FONTINFO_CJK :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? EE_CHAR_FONTINFO_CTL :
+ EE_CHAR_FONTINFO );
+ auto const attribs = pTableView->GetAttribs();
+ const SvxFontItem& rItem = static_cast<const SvxFontItem&>(
+ attribs.Get(nFontWhich));
+
+ OUString aString;
+ std::shared_ptr<SvxFontItem> aNewItem(std::make_shared<SvxFontItem>(EE_CHAR_FONTINFO));
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem = nullptr;
+ if( pArgs )
+ pArgs->GetItemState(SID_CHARMAP, false, &pItem);
+
+ if ( pItem )
+ {
+ aString = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false);
+ if ( pFontItem )
+ {
+ const OUString& aFontName(pFontItem->GetValue());
+ vcl::Font aFont(aFontName, Size(1,1)); // Size just because CTOR
+ // tdf#125054 see comment in drtxob.cxx, same ID
+ aNewItem = std::make_shared<SvxFontItem>(
+ aFont.GetFamilyType(), aFont.GetFamilyName(),
+ aFont.GetStyleName(), aFont.GetPitch(),
+ aFont.GetCharSet(), ATTR_FONT);
+ }
+ else
+ {
+ aNewItem.reset(rItem.Clone());
+ }
+
+ // tdf#125054 force Item to correct intended ID
+ aNewItem->SetWhich(EE_CHAR_FONTINFO);
+ }
+ else
+ {
+ ScViewUtil::ExecuteCharMap(rItem, *rViewData.GetViewShell());
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+ }
+
+ if ( !aString.isEmpty() )
+ {
+ // if string contains WEAK characters, set all fonts
+ SvtScriptType nSetScript;
+ ScDocument& rDoc = rViewData.GetDocument();
+ if ( rDoc.HasStringWeakCharacters( aString ) )
+ nSetScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ else
+ nSetScript = rDoc.GetStringScriptType( aString );
+
+ SfxItemSet aSet( pTableView->GetEmptyItemSet() );
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, GetPool() );
+ aSetItem.PutItemForScriptType( nSetScript, *aNewItem );
+ aSet.Put( aSetItem.GetItemSet(), false );
+
+ // SetAttribs on the View selects a word, when nothing is selected
+ pTableView->GetEditEngine()->QuickSetAttribs( aSet, pTableView->GetSelection() );
+ pTableView->InsertText(aString);
+ if (pTopView)
+ pTopView->InsertText(aString);
+
+ SfxStringItem aStringItem( SID_CHARMAP, aString );
+ SfxStringItem aFontItem( SID_ATTR_SPECIALCHAR, aNewItem->GetFamilyName() );
+ rReq.AppendItem( aFontItem );
+ rReq.AppendItem( aStringItem );
+ rReq.Done();
+
+ }
+
+ if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
+ pViewWindow->GrabFocus();
+ }
+ break;
+
+ case FID_INSERT_NAME:
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScNamePasteDlg> pDlg(pFact->CreateScNamePasteDlg(rViewData.GetDialogParent(), rViewData.GetDocShell()));
+ short nRet = pDlg->Execute();
+ // pDlg is needed below
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+
+ if ( nRet == BTN_PASTE_NAME )
+ {
+ std::vector<OUString> aNames = pDlg->GetSelectedNames();
+ if (!aNames.empty())
+ {
+ OUStringBuffer aBuffer;
+ for (const auto& rName : aNames)
+ {
+ aBuffer.append(rName + " ");
+ }
+ const OUString s = aBuffer.makeStringAndClear();
+ pTableView->InsertText(s);
+ if (pTopView)
+ pTopView->InsertText(s);
+ }
+ }
+ pDlg.disposeAndClear();
+
+ if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr)
+ pViewWindow->GrabFocus();
+ }
+ break;
+
+ case SID_CHAR_DLG_EFFECT:
+ case SID_CHAR_DLG:
+ {
+ SfxItemSet aAttrs( pTableView->GetAttribs() );
+
+ SfxObjectShell* pObjSh = rViewData.GetSfxDocShell();
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScCharDlg(
+ rViewData.GetDialogParent(), &aAttrs, pObjSh, false));
+ if (nSlot == SID_CHAR_DLG_EFFECT)
+ {
+ pDlg->SetCurPageId("fonteffects");
+ }
+ short nRet = pDlg->Execute();
+ // pDlg is needed below
+
+ // while the dialog was open, edit mode may have been stopped
+ if (!SC_MOD()->IsInputMode())
+ return;
+
+ if ( nRet == RET_OK )
+ {
+ const SfxItemSet* pOut = pDlg->GetOutputItemSet();
+ pTableView->SetAttribs( *pOut );
+ }
+ }
+ break;
+
+ case SID_TOGGLE_REL:
+ {
+ /* TODO: MLFORMULA: this should work also with multi-line formulas. */
+ if (pEngine->GetParagraphCount() == 1)
+ {
+ OUString aText = pEngine->GetText();
+ ESelection aSel = pEditView->GetSelection(); // current View
+
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScRefFinder aFinder(aText, rViewData.GetCurPos(), rDoc, rDoc.GetAddressConvention());
+ aFinder.ToggleRel( aSel.nStartPos, aSel.nEndPos );
+ if (aFinder.GetFound())
+ {
+ const OUString& aNew = aFinder.GetText();
+ ESelection aNewSel( 0,aFinder.GetSelStart(), 0,aFinder.GetSelEnd() );
+ pEngine->SetText( aNew );
+ pTableView->SetSelection( aNewSel );
+ if ( pTopView )
+ {
+ pTopView->GetEditEngine()->SetText( aNew );
+ pTopView->SetSelection( aNewSel );
+ }
+
+ // reference is being selected -> do not overwrite when typing
+ bSetSelIsRef = true;
+ }
+ }
+ }
+ break;
+
+ case SID_HYPERLINK_SETLINK:
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_HYPERLINK_SETLINK, true, &pItem ) == SfxItemState::SET )
+ {
+ const SvxHyperlinkItem* pHyper = static_cast<const SvxHyperlinkItem*>(pItem);
+ const OUString& rName = pHyper->GetName();
+ const OUString& rURL = pHyper->GetURL();
+ const OUString& rTarget = pHyper->GetTargetFrame();
+ SvxLinkInsertMode eMode = pHyper->GetInsertMode();
+
+ bool bCellLinksOnly
+ = (SC_MOD()->GetAppOptions().GetLinksInsertedLikeMSExcel()
+ && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat())
+ || comphelper::LibreOfficeKit::isActive();
+
+ bool bDone = false;
+ if ( (eMode == HLINK_DEFAULT || eMode == HLINK_FIELD) && !bCellLinksOnly )
+ {
+ std::unique_ptr<const SvxFieldData> aSvxFieldDataPtr(GetURLField());
+ const SvxURLField* pURLField(static_cast<const SvxURLField*>(aSvxFieldDataPtr.get()));
+ if ( pURLField )
+ {
+ // select old field
+
+ ESelection aSel = pTableView->GetSelection();
+ aSel.Adjust();
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nEndPos = aSel.nStartPos + 1;
+ pTableView->SetSelection( aSel );
+
+ // insert new field
+
+ SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr );
+ aURLField.SetTargetFrame( rTarget );
+ SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD );
+ pTableView->InsertField( aURLItem );
+ pTableView->SetSelection( aSel ); // select inserted field
+
+ // now also fields in the Top-View
+
+ if ( pTopView )
+ {
+ aSel = pTopView->GetSelection();
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nEndPos = aSel.nStartPos + 1;
+ pTopView->SetSelection( aSel );
+ pTopView->InsertField( aURLItem );
+ pTopView->SetSelection( aSel ); // select inserted field
+ }
+
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ if (bCellLinksOnly)
+ {
+ sal_Int32 nPar = pEngine->GetParagraphCount();
+ if (nPar)
+ {
+ sal_Int32 nLen = pEngine->GetTextLen(nPar - 1);
+ pTableView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
+ if (pTopView)
+ pTopView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
+ }
+ }
+ rViewData.GetViewShell()->
+ InsertURL( rName, rURL, rTarget, static_cast<sal_uInt16>(eMode) );
+
+ // when "Button", the InsertURL in ViewShell turns the EditShell off
+ // thus the immediate return statement
+ return;
+ }
+ }
+ }
+ break;
+ case SID_OPEN_HYPERLINK:
+ {
+ const SvxFieldItem* pFieldItem
+ = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ ScGlobal::OpenURL( pURLField->GetURL(), pURLField->GetTargetFrame(), true );
+ return;
+ }
+ case SID_EDIT_HYPERLINK:
+ {
+ // Ensure the field is selected first
+ pEditView->SelectFieldAtCursor();
+ rViewData.GetViewShell()->GetViewFrame().GetDispatcher()->Execute(
+ SID_HYPERLINK_DIALOG);
+ }
+ break;
+ case SID_COPY_HYPERLINK_LOCATION:
+ {
+ const SvxFieldItem* pFieldItem
+ = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true);
+ const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr;
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ uno::Reference<datatransfer::clipboard::XClipboard> xClipboard
+ = pEditView->GetClipboard();
+ vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard, SfxViewShell::Current());
+ }
+ }
+ break;
+ case SID_REMOVE_HYPERLINK:
+ {
+ URLFieldHelper::RemoveURLField(*pEditView);
+ }
+ break;
+
+ case FN_INSERT_SOFT_HYPHEN:
+ lclInsertCharacter( pTableView, pTopView, CHAR_SHY );
+ break;
+ case FN_INSERT_HARDHYPHEN:
+ lclInsertCharacter( pTableView, pTopView, CHAR_NBHY );
+ break;
+ case FN_INSERT_HARD_SPACE:
+ lclInsertCharacter( pTableView, pTopView, CHAR_NBSP );
+ break;
+ case FN_INSERT_NNBSP:
+ lclInsertCharacter( pTableView, pTopView, CHAR_NNBSP );
+ break;
+ case SID_INSERT_RLM:
+ lclInsertCharacter( pTableView, pTopView, CHAR_RLM );
+ break;
+ case SID_INSERT_LRM:
+ lclInsertCharacter( pTableView, pTopView, CHAR_LRM );
+ break;
+ case SID_INSERT_ZWSP:
+ lclInsertCharacter( pTableView, pTopView, CHAR_ZWSP );
+ break;
+ case SID_INSERT_WJ:
+ lclInsertCharacter( pTableView, pTopView, CHAR_WJ );
+ break;
+ case SID_INSERT_FIELD_SHEET:
+ {
+ SvxTableField aField(rViewData.GetTabNo());
+ SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
+ pTableView->InsertField(aItem);
+ }
+ break;
+ case SID_INSERT_FIELD_TITLE:
+ {
+ SvxFileField aField;
+ SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
+ pTableView->InsertField(aItem);
+ }
+ break;
+ case SID_INSERT_FIELD_DATE_VAR:
+ {
+ SvxDateField aField;
+ SvxFieldItem aItem(aField, EE_FEATURE_FIELD);
+ pTableView->InsertField(aItem);
+ }
+ break;
+ }
+
+ pHdl->DataChanged(false, bSetModified);
+ if (bSetSelIsRef)
+ pHdl->SetSelIsRef(true);
+}
+
+static void lcl_DisableAll( SfxItemSet& rSet ) // disable all slots
+{
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ rSet.DisableItem( nWhich );
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScEditShell::GetState( SfxItemSet& rSet )
+{
+ // When deactivating the view, edit mode is stopped, but the EditShell is left active
+ // (a shell can't be removed from within Deactivate). In that state, the EditView isn't inserted
+ // into the EditEngine, so it can have an invalid selection and must not be used.
+ ScInputHandler* pHdl = GetMyInputHdl();
+ if ( !rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ lcl_DisableAll( rSet );
+
+ // Some items are actually useful and still applicable when in formula building mode: enable
+ if (pHdl && pHdl->IsFormulaMode())
+ {
+ rSet.ClearItem(SID_TOGGLE_REL); // F4 Cycle Cell Reference Types
+ rSet.ClearItem(SID_CHARMAP); // Insert Special Characters
+ }
+ return;
+ }
+
+ EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView;
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_ATTR_INSERT: // Status row
+ {
+ if ( pActiveView )
+ rSet.Put( SfxBoolItem( nWhich, pActiveView->IsInsertMode() ) );
+ else
+ {
+ // Here the code used to pass the value 42 and it used
+ // to "work" without warnings because the SfxBoolItem
+ // was based on 'sal_Bool', which is actually 'unsigned
+ // char'. But now it uses actual 'bool', and passing 42
+ // for a 'bool' parameter causes a warning at least with
+ // MSVC. So use 'true'. I really really hope there is
+ // not code somewhere that retrieves this "boolean" item
+ // and checks it value for the magic value 42...
+ rSet.Put( SfxBoolItem( nWhich, true) );
+ }
+ }
+ break;
+
+ case SID_HYPERLINK_GETLINK:
+ {
+ SvxHyperlinkItem aHLinkItem;
+ bool bCellLinksOnly
+ = (SC_MOD()->GetAppOptions().GetLinksInsertedLikeMSExcel()
+ && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat())
+ || comphelper::LibreOfficeKit::isActive();
+ std::unique_ptr<const SvxFieldData> aSvxFieldDataPtr(GetURLField());
+ const SvxURLField* pURLField(static_cast<const SvxURLField*>(aSvxFieldDataPtr.get()));
+ if (!bCellLinksOnly)
+ {
+ if (pURLField)
+ {
+ aHLinkItem.SetName(pURLField->GetRepresentation());
+ aHLinkItem.SetURL(pURLField->GetURL());
+ aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame());
+ }
+ else if (pActiveView)
+ {
+ // use selected text as name for urls
+ OUString sReturn = pActiveView->GetSelected();
+ sReturn = sReturn.copy(
+ 0, std::min(sReturn.getLength(), static_cast<sal_Int32>(255)));
+ aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' '));
+ }
+ }
+ else
+ {
+ if (!pURLField)
+ {
+ aSvxFieldDataPtr = GetFirstURLFieldFromCell();
+ pURLField = static_cast<const SvxURLField*>(aSvxFieldDataPtr.get());
+ }
+ if (pURLField)
+ {
+ aHLinkItem.SetURL(pURLField->GetURL());
+ aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame());
+ }
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab));
+ }
+ rSet.Put(aHLinkItem);
+ }
+ break;
+
+ case SID_OPEN_HYPERLINK:
+ case SID_EDIT_HYPERLINK:
+ case SID_COPY_HYPERLINK_LOCATION:
+ case SID_REMOVE_HYPERLINK:
+ {
+ if (!URLFieldHelper::IsCursorAtURLField(*pEditView,
+ /*AlsoCheckBeforeCursor=*/true))
+ rSet.DisableItem (nWhich);
+ }
+ break;
+
+ case SID_TRANSLITERATE_HALFWIDTH:
+ case SID_TRANSLITERATE_FULLWIDTH:
+ case SID_TRANSLITERATE_HIRAGANA:
+ case SID_TRANSLITERATE_KATAKANA:
+ case SID_INSERT_RLM:
+ case SID_INSERT_LRM:
+ ScViewUtil::HideDisabledSlot( rSet, rViewData.GetBindings(), nWhich );
+ break;
+
+ case SID_THES:
+ {
+ OUString aStatusVal;
+ LanguageType nLang = LANGUAGE_NONE;
+ bool bIsLookUpWord = pActiveView &&
+ GetStatusValueForThesaurusFromContext(aStatusVal, nLang, *pActiveView);
+ rSet.Put( SfxStringItem( SID_THES, aStatusVal ) );
+
+ // disable thesaurus context menu entry if there is nothing to look up
+ bool bCanDoThesaurus = ScModule::HasThesaurusLanguage( nLang );
+ if (!bIsLookUpWord || !bCanDoThesaurus)
+ rSet.DisableItem( SID_THES );
+ }
+ break;
+ case SID_INSERT_FIELD_SHEET:
+ case SID_INSERT_FIELD_TITLE:
+ case SID_INSERT_FIELD_DATE_VAR:
+ break;
+ case SID_COPY:
+ case SID_CUT:
+ if (GetObjectShell() && GetObjectShell()->isContentExtractionLocked())
+ {
+ rSet.DisableItem(SID_COPY);
+ rSet.DisableItem(SID_CUT);
+ }
+ break;
+
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+std::unique_ptr<const SvxFieldData> ScEditShell::GetURLField()
+{
+ ScInputHandler* pHdl = GetMyInputHdl();
+ EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView;
+ if (!pActiveView)
+ return std::unique_ptr<const SvxFieldData>();
+
+ const SvxFieldData* pField = pActiveView->GetFieldUnderMouseOrInSelectionOrAtCursor();
+ if (auto pURLField = dynamic_cast<const SvxURLField*>(pField))
+ return pURLField->Clone();
+
+ return std::unique_ptr<const SvxFieldData>();
+}
+
+std::unique_ptr<const SvxFieldData> ScEditShell::GetFirstURLFieldFromCell()
+{
+ EditEngine* pEE = GetEditView()->GetEditEngine();
+ sal_Int32 nParaCount = pEE->GetParagraphCount();
+ for (sal_Int32 nPara = 0; nPara < nParaCount; ++nPara)
+ {
+ ESelection aSel(nPara, 0);
+ std::vector<sal_Int32> aPosList;
+ pEE->GetPortions(nPara, aPosList);
+ for (const auto& rPos : aPosList)
+ {
+ aSel.nEndPos = rPos;
+
+ SfxItemSet aEditSet(pEE->GetAttribs(aSel));
+ if (aSel.nStartPos + 1 == aSel.nEndPos)
+ {
+ // test if the character is a text field
+ if (const SvxFieldItem* pItem = aEditSet.GetItemIfSet(EE_FEATURE_FIELD, false))
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ if (const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ return pUrlField->Clone();
+ }
+ }
+ }
+ aSel.nStartPos = aSel.nEndPos;
+ }
+ }
+
+ return std::unique_ptr<const SvxFieldData>();
+}
+
+IMPL_LINK( ScEditShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void )
+{
+ bPastePossible = ( pDataHelper->HasFormat( SotClipboardFormatId::STRING )
+ || pDataHelper->HasFormat( SotClipboardFormatId::RTF )
+ || pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ));
+
+ SfxBindings& rBindings = rViewData.GetBindings();
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+ rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS );
+}
+
+void ScEditShell::GetClipState( SfxItemSet& rSet )
+{
+ // Do not offer SotClipboardFormatId::STRING_TSVC for in-cell paste.
+
+ if ( !mxClipEvtLstnr.is() )
+ {
+ // create listener
+ mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScEditShell, ClipboardChanged ) );
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ mxClipEvtLstnr->AddListener( pWin );
+
+ // get initial state
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
+ bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING )
+ || aDataHelper.HasFormat( SotClipboardFormatId::RTF )
+ || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) );
+ }
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_PASTE:
+ case SID_PASTE_SPECIAL:
+ case SID_PASTE_UNFORMATTED:
+ if( !bPastePossible )
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_CLIPBOARD_FORMAT_ITEMS:
+ if( bPastePossible )
+ {
+ SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS );
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) );
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING );
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF );
+
+ rSet.Put( aFormats );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+static void lcl_InvalidateUnder( SfxBindings& rBindings )
+{
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+}
+
+void ScEditShell::ExecuteAttr(SfxRequest& rReq)
+{
+ SfxItemSet aSet( pEditView->GetEmptyItemSet() );
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ case SID_ATTR_CHAR_FONT:
+ {
+ if (pArgs)
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ if (nSlot == SID_ATTR_CHAR_FONT)
+ {
+ nScript = pEditView->GetSelectedScriptType();
+ if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
+ }
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ aSetItem.PutItemForScriptType( nScript, pArgs->Get( nWhich ) );
+
+ aSet.Put( aSetItem.GetItemSet(), false );
+ }
+ }
+ break;
+
+ case SID_ATTR_CHAR_COLOR:
+ {
+ if (pArgs)
+ {
+ aSet.Put( pArgs->Get( pArgs->GetPool()->GetWhich( nSlot ) ) );
+ rBindings.Invalidate( nSlot );
+ }
+ }
+ break;
+
+ // Toggles
+
+ case SID_ATTR_CHAR_WEIGHT:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+
+ bool bOld = false;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxWeightItem*>(pCore)->GetWeight() > WEIGHT_NORMAL )
+ bOld = true;
+
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ aSetItem.PutItemForScriptType( nScript,
+ SvxWeightItem( bOld ? WEIGHT_NORMAL : WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
+ aSet.Put( aSetItem.GetItemSet(), false );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_POSTURE:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+
+ bool bOld = false;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxPostureItem*>(pCore)->GetValue() != ITALIC_NONE )
+ bOld = true;
+
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ aSetItem.PutItemForScriptType( nScript,
+ SvxPostureItem( bOld ? ITALIC_NONE : ITALIC_NORMAL, EE_CHAR_ITALIC ) );
+ aSet.Put( aSetItem.GetItemSet(), false );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ULINE_VAL_NONE:
+ aSet.Put( SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE ) );
+ lcl_InvalidateUnder( rBindings );
+ break;
+
+ case SID_ATTR_CHAR_UNDERLINE:
+ case SID_ULINE_VAL_SINGLE:
+ case SID_ULINE_VAL_DOUBLE:
+ case SID_ULINE_VAL_DOTTED:
+ {
+ FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_UNDERLINE).GetLineStyle();
+ FontLineStyle eNew = eOld;
+ switch (nSlot)
+ {
+ case SID_ATTR_CHAR_UNDERLINE:
+ if ( pArgs )
+ {
+ const SvxTextLineItem& rTextLineItem = static_cast< const SvxTextLineItem& >( pArgs->Get( pArgs->GetPool()->GetWhich(nSlot) ) );
+ eNew = rTextLineItem.GetLineStyle();
+ }
+ else
+ {
+ eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ }
+ break;
+ case SID_ULINE_VAL_SINGLE:
+ eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ break;
+ case SID_ULINE_VAL_DOUBLE:
+ eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE;
+ break;
+ case SID_ULINE_VAL_DOTTED:
+ eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED;
+ break;
+ }
+ aSet.Put( SvxUnderlineItem( eNew, EE_CHAR_UNDERLINE ) );
+ lcl_InvalidateUnder( rBindings );
+ }
+ break;
+
+ case SID_ATTR_CHAR_OVERLINE:
+ {
+ FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_OVERLINE).GetLineStyle();
+ FontLineStyle eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ aSet.Put( SvxOverlineItem( eNew, EE_CHAR_OVERLINE ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_STRIKEOUT:
+ {
+ bool bOld = pEditView->GetAttribs().Get(EE_CHAR_STRIKEOUT).GetValue() != STRIKEOUT_NONE;
+ aSet.Put( SvxCrossedOutItem( bOld ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_SHADOWED:
+ {
+ bool bOld = pEditView->GetAttribs().Get(EE_CHAR_SHADOW).GetValue();
+ aSet.Put( SvxShadowedItem( !bOld, EE_CHAR_SHADOW ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_ATTR_CHAR_CONTOUR:
+ {
+ bool bOld = pEditView->GetAttribs().Get(EE_CHAR_OUTLINE).GetValue();
+ aSet.Put( SvxContourItem( !bOld, EE_CHAR_OUTLINE ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_SET_SUPER_SCRIPT:
+ {
+ SvxEscapement eOld = static_cast<SvxEscapement>(pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEnumValue());
+ SvxEscapement eNew = (eOld == SvxEscapement::Superscript) ?
+ SvxEscapement::Off : SvxEscapement::Superscript;
+ aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+ case SID_SET_SUB_SCRIPT:
+ {
+ SvxEscapement eOld = static_cast<SvxEscapement>(pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEnumValue());
+ SvxEscapement eNew = (eOld == SvxEscapement::Subscript) ?
+ SvxEscapement::Off : SvxEscapement::Subscript;
+ aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) );
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+ case SID_ATTR_CHAR_KERNING:
+ {
+ if(pArgs)
+ {
+ aSet.Put ( pArgs->Get(pArgs->GetPool()->GetWhich(nSlot)));
+ rBindings.Invalidate( nSlot );
+ }
+ }
+ break;
+
+ case SID_GROW_FONT_SIZE:
+ case SID_SHRINK_FONT_SIZE:
+ {
+ SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ const SvxFontListItem* pFontListItem = static_cast<const SvxFontListItem*>
+ (pObjSh ? pObjSh->GetItem(SID_ATTR_CHAR_FONTLIST) : nullptr);
+ const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr;
+ pEditView->ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pFontList );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ break;
+ }
+
+ // apply
+
+ EditEngine* pEngine = pEditView->GetEditEngine();
+ bool bOld = pEngine->SetUpdateLayout(false);
+
+ pEditView->SetAttribs( aSet );
+
+ pEngine->SetUpdateLayout(bOld);
+ pEditView->Invalidate();
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ pHdl->SetModified();
+
+ rReq.Done();
+}
+
+void ScEditShell::GetAttrState(SfxItemSet &rSet)
+{
+ if ( !rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ lcl_DisableAll( rSet );
+ return;
+ }
+
+ SfxItemSet aAttribs = pEditView->GetAttribs();
+ rSet.Put( aAttribs );
+
+ // choose font info according to selection script type
+
+ SvtScriptType nScript = pEditView->GetSelectedScriptType();
+ if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType();
+
+ // #i55929# input-language-dependent script type (depends on input language if nothing selected)
+ SvtScriptType nInputScript = nScript;
+ if ( !pEditView->GetSelection().HasRange() )
+ {
+ LanguageType nInputLang = rViewData.GetActiveWin()->GetInputLanguage();
+ if (nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM)
+ nInputScript = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang );
+ }
+
+ // #i55929# according to spec, nInputScript is used for font and font height only
+ if ( rSet.GetItemState( EE_CHAR_FONTINFO ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTINFO, nInputScript );
+ if ( rSet.GetItemState( EE_CHAR_FONTHEIGHT ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTHEIGHT, nInputScript );
+ if ( rSet.GetItemState( EE_CHAR_WEIGHT ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_WEIGHT, nScript );
+ if ( rSet.GetItemState( EE_CHAR_ITALIC ) != SfxItemState::UNKNOWN )
+ ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_ITALIC, nScript );
+
+ // underline
+ SfxItemState eState = aAttribs.GetItemState( EE_CHAR_UNDERLINE );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rSet.InvalidateItem( SID_ULINE_VAL_NONE );
+ rSet.InvalidateItem( SID_ULINE_VAL_SINGLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOUBLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOTTED );
+ }
+ else
+ {
+ FontLineStyle eUnderline = aAttribs.Get(EE_CHAR_UNDERLINE).GetLineStyle();
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE));
+ }
+
+ //! Testing whether brace highlighting is active !!!!
+ ScInputHandler* pHdl = GetMyInputHdl();
+ if ( pHdl && pHdl->IsFormulaMode() )
+ rSet.ClearItem( EE_CHAR_WEIGHT ); // Highlighted brace not here
+
+ SvxEscapement eEsc = static_cast<SvxEscapement>(aAttribs.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue());
+ rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript));
+ rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript));
+ rViewData.GetBindings().Invalidate( SID_SET_SUPER_SCRIPT );
+ rViewData.GetBindings().Invalidate( SID_SET_SUB_SCRIPT );
+
+ eState = aAttribs.GetItemState( EE_CHAR_KERNING );
+ rViewData.GetBindings().Invalidate( SID_ATTR_CHAR_KERNING );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rSet.InvalidateItem(EE_CHAR_KERNING);
+ }
+}
+
+OUString ScEditShell::GetSelectionText( bool bWholeWord )
+{
+ OUString aStrSelection;
+
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
+ {
+ if ( bWholeWord )
+ {
+ EditEngine* pEngine = pEditView->GetEditEngine();
+ ESelection aSel = pEditView->GetSelection();
+ OUString aStrCurrentDelimiters = pEngine->GetWordDelimiters();
+
+ pEngine->SetWordDelimiters(" .,;\"'");
+ aStrSelection = pEngine->GetWord( aSel.nEndPara, aSel.nEndPos );
+ pEngine->SetWordDelimiters( aStrCurrentDelimiters );
+ }
+ else
+ {
+ aStrSelection = pEditView->GetSelected();
+ }
+ }
+
+ return aStrSelection;
+}
+
+void ScEditShell::ExecuteUndo(const SfxRequest& rReq)
+{
+ // Undo must be handled here because it's called for both EditViews
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ OSL_ENSURE(pHdl,"no ScInputHandler");
+ EditView* pTopView = pHdl->GetTopView();
+ EditView* pTableView = pHdl->GetTableView();
+ OSL_ENSURE(pTableView,"no EditView");
+
+ pHdl->DataChanging();
+
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_UNDO:
+ case SID_REDO:
+ {
+ bool bIsUndo = ( nSlot == SID_UNDO );
+
+ sal_uInt16 nCount = 1;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ nCount = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ if ( bIsUndo )
+ {
+ pTableView->Undo();
+ if (pTopView)
+ pTopView->Undo();
+ }
+ else
+ {
+ pTableView->Redo();
+ if (pTopView)
+ pTopView->Redo();
+ }
+ }
+ }
+ break;
+ }
+ rViewData.GetBindings().InvalidateAll(false);
+
+ pHdl->DataChanged();
+}
+
+void ScEditShell::GetUndoState(SfxItemSet &rSet)
+{
+ // Undo state is taken from normal ViewFrame state function
+
+ SfxViewFrame& rViewFrm = rViewData.GetViewShell()->GetViewFrame();
+ if ( GetUndoManager() )
+ {
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ rViewFrm.GetSlotState( nWhich, nullptr, &rSet );
+ nWhich = aIter.NextWhich();
+ }
+ }
+
+ // disable if no action in input line EditView
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ OSL_ENSURE(pHdl,"no ScInputHandler");
+ EditView* pTopView = pHdl->GetTopView();
+ if (pTopView)
+ {
+ SfxUndoManager& rTopMgr = pTopView->GetEditEngine()->GetUndoManager();
+ if ( rTopMgr.GetUndoActionCount() == 0 )
+ rSet.DisableItem( SID_UNDO );
+ if ( rTopMgr.GetRedoActionCount() == 0 )
+ rSet.DisableItem( SID_REDO );
+ }
+}
+
+void ScEditShell::ExecuteTrans( const SfxRequest& rReq )
+{
+ TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() );
+ if ( nType == TransliterationFlags::NONE )
+ return;
+
+ ScInputHandler* pHdl = GetMyInputHdl();
+ assert(pHdl && "no ScInputHandler");
+
+ EditView* pTopView = pHdl->GetTopView();
+ EditView* pTableView = pHdl->GetTableView();
+ assert(pTableView && "no EditView");
+
+ pHdl->DataChanging();
+
+ pTableView->TransliterateText( nType );
+ if (pTopView)
+ pTopView->TransliterateText( nType );
+
+ pHdl->DataChanged();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/formatsh.cxx b/sc/source/ui/view/formatsh.cxx
new file mode 100644
index 0000000000..93a456e46b
--- /dev/null
+++ b/sc/source/ui/view/formatsh.cxx
@@ -0,0 +1,2108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <svl/whiter.hxx>
+
+#include <svl/stritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/languageoptions.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svx/numinf.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <formatsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <patattr.hxx>
+#include <scmod.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <printfun.hxx>
+#include <docpool.hxx>
+#include <tabvwsh.hxx>
+#include <attrib.hxx>
+
+#define ShellClass_ScFormatShell
+#define ShellClass_TableFont
+#define ShellClass_FormatForSelection
+#include <scslots.hxx>
+
+#include <editeng/fontitem.hxx>
+#include <sfx2/classificationhelper.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+SvxCellHorJustify lclConvertSlotToHAlign( sal_uInt16 nSlot )
+{
+ SvxCellHorJustify eHJustify = SvxCellHorJustify::Standard;
+ switch( nSlot )
+ {
+ case SID_ALIGN_ANY_HDEFAULT: eHJustify = SvxCellHorJustify::Standard; break;
+ case SID_ALIGN_ANY_LEFT: eHJustify = SvxCellHorJustify::Left; break;
+ case SID_ALIGN_ANY_HCENTER: eHJustify = SvxCellHorJustify::Center; break;
+ case SID_ALIGN_ANY_RIGHT: eHJustify = SvxCellHorJustify::Right; break;
+ case SID_ALIGN_ANY_JUSTIFIED: eHJustify = SvxCellHorJustify::Block; break;
+ default: OSL_FAIL( "lclConvertSlotToHAlign - invalid slot" );
+ }
+ return eHJustify;
+}
+
+SvxCellVerJustify lclConvertSlotToVAlign( sal_uInt16 nSlot )
+{
+ SvxCellVerJustify eVJustify = SvxCellVerJustify::Standard;
+ switch( nSlot )
+ {
+ case SID_ALIGN_ANY_VDEFAULT: eVJustify = SvxCellVerJustify::Standard; break;
+ case SID_ALIGN_ANY_TOP: eVJustify = SvxCellVerJustify::Top; break;
+ case SID_ALIGN_ANY_VCENTER: eVJustify = SvxCellVerJustify::Center; break;
+ case SID_ALIGN_ANY_BOTTOM: eVJustify = SvxCellVerJustify::Bottom; break;
+ default: OSL_FAIL( "lclConvertSlotToVAlign - invalid slot" );
+ }
+ return eVJustify;
+}
+
+} // namespace
+
+
+SFX_IMPL_INTERFACE(ScFormatShell, SfxShell)
+
+void ScFormatShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Format);
+}
+
+ScFormatShell::ScFormatShell(ScViewData& rData) :
+ SfxShell(rData.GetViewShell()),
+ rViewData(rData)
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+
+ SetPool( &pTabViewShell->GetPool() );
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if (pMgr && !rViewData.GetDocument().IsUndoEnabled())
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Format");
+}
+
+ScFormatShell::~ScFormatShell()
+{
+}
+
+void ScFormatShell::ExecuteStyle( SfxRequest& rReq )
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const sal_uInt16 nSlotId = rReq.GetSlot();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScTabViewShell* pTabViewShell= GetViewData().GetViewShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
+
+ if ( (nSlotId == SID_STYLE_PREVIEW)
+ || (nSlotId == SID_STYLE_END_PREVIEW) )
+ {
+ if (nSlotId == SID_STYLE_PREVIEW)
+ {
+ SfxStyleFamily eFamily = SfxStyleFamily::Para;
+ const SfxUInt16Item* pFamItem;
+ if ( pArgs && (pFamItem = pArgs->GetItemIfSet( SID_STYLE_FAMILY )) )
+ eFamily = static_cast<SfxStyleFamily>(pFamItem->GetValue());
+ const SfxPoolItem* pNameItem;
+ OUString aStyleName;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
+ aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+ if ( eFamily == SfxStyleFamily::Para ) // CellStyles
+ {
+ ScMarkData aFuncMark( rViewData.GetMarkData() );
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ aFuncMark.MarkToMulti();
+
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ ScRange aRange( nCol, nRow, nTab );
+ aFuncMark.SetMarkArea( aRange );
+ }
+ rDoc.SetPreviewSelection( aFuncMark );
+ ScStyleSheet* pPreviewStyle = static_cast<ScStyleSheet*>( pStylePool->Find( aStyleName, eFamily ) );
+ rDoc.SetPreviewCellStyle( pPreviewStyle );
+ ScPatternAttr aAttr( *rDoc.GetSelectionPattern( aFuncMark ) );
+ aAttr.SetStyleSheet( pPreviewStyle );
+
+ SfxItemSet aItemSet( GetPool() );
+
+ ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ rNewSet.Put( aItemSet, false );
+
+ rDoc.ApplySelectionPattern( aNewAttrs, rDoc.GetPreviewSelection() );
+ pTabViewShell->UpdateSelectionArea( aFuncMark, &aAttr );
+ }
+ }
+ else
+ {
+ // No mark at all happens when creating a new document, in which
+ // case the selection pattern obtained would be empty (created of
+ // GetPool()) anyway and nothing needs to be applied.
+ ScMarkData aPreviewMark( rDoc.GetPreviewSelection());
+ if (aPreviewMark.IsMarked() || aPreviewMark.IsMultiMarked())
+ {
+ ScPatternAttr aAttr( *rDoc.GetSelectionPattern( aPreviewMark ) );
+ if ( ScStyleSheet* pPreviewStyle = rDoc.GetPreviewCellStyle() )
+ aAttr.SetStyleSheet( pPreviewStyle );
+ rDoc.SetPreviewCellStyle(nullptr);
+
+ SfxItemSet aItemSet( GetPool() );
+
+ ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ rNewSet.Put( aItemSet, false );
+ rDoc.ApplySelectionPattern( aNewAttrs, aPreviewMark );
+ pTabViewShell->UpdateSelectionArea( aPreviewMark, &aAttr );
+ }
+ }
+ }
+ else if (nSlotId == SID_CLASSIFICATION_APPLY)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if (pArgs && pArgs->GetItemState(nSlotId, false, &pItem) == SfxItemState::SET)
+ {
+ const OUString& rName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ SfxClassificationHelper aHelper(pDocSh->getDocProperties());
+ auto eType = SfxClassificationPolicyType::IntellectualProperty;
+ if (const SfxStringItem* pNameItem = pArgs->GetItemIfSet(SID_TYPE_NAME, false))
+ {
+ const OUString& rType = pNameItem->GetValue();
+ eType = SfxClassificationHelper::stringToPolicyType(rType);
+ }
+ aHelper.SetBACName(rName, eType);
+ }
+ else
+ SAL_WARN("sc.ui", "missing parameter for SID_CLASSIFICATION_APPLY");
+ }
+ else
+ {
+ OSL_FAIL( "Unknown slot (ScViewShell::ExecuteStyle)" );
+ }
+}
+
+void ScFormatShell::ExecuteNumFormat( SfxRequest& rReq )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ SfxBindings& rBindings = pTabViewShell->GetViewFrame().GetBindings();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ // End input
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ switch ( nSlot )
+ {
+ case SID_NUMBER_TYPE_FORMAT:
+ case SID_NUMBER_TWODEC:
+ case SID_NUMBER_SCIENTIFIC:
+ case SID_NUMBER_DATE:
+ case SID_NUMBER_CURRENCY:
+ case SID_NUMBER_PERCENT:
+ case SID_NUMBER_STANDARD:
+ case SID_NUMBER_FORMAT:
+ case SID_NUMBER_INCDEC:
+ case SID_NUMBER_DECDEC:
+ case SID_NUMBER_THOUSANDS:
+ case FID_DEFINE_NAME:
+ case FID_ADD_NAME:
+ case FID_USE_NAME:
+ case FID_INSERT_NAME:
+ case SID_SPELL_DIALOG:
+ case SID_HANGUL_HANJA_CONVERSION:
+
+ pScMod->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ SvNumFormatType nType = GetCurrentNumberFormatType();
+ switch ( nSlot )
+ {
+ case SID_NUMBER_TWODEC:
+ {
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ sal_uInt32 nNumberFormat = rAttrSet.Get(ATTR_VALUE_FORMAT).GetValue();
+
+ if ((nType & SvNumFormatType::NUMBER) && nNumberFormat == 4)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER, 4 );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ }
+ break;
+ case SID_NUMBER_SCIENTIFIC:
+ if (nType & SvNumFormatType::SCIENTIFIC)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::SCIENTIFIC );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_DATE:
+ if (nType & SvNumFormatType::DATE)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::DATE );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_TIME:
+ if (nType & SvNumFormatType::TIME)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::TIME );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_CURRENCY:
+ if(pReqArgs)
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->HasItem( SID_NUMBER_CURRENCY, &pItem ) )
+ {
+ sal_uInt32 nNewFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SfxItemSet& rOldSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+
+ LanguageType eOldLang = rOldSet.Get( ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ sal_uInt32 nOldFormat = rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+
+ if ( nOldFormat != nNewFormat )
+ {
+ const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ LanguageType eNewLang = pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ if ( eNewLang != eOldLang && eNewLang != LANGUAGE_DONTKNOW )
+ rSet.Put( SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ pTabViewShell->ApplySelectionPattern( aNewAttrs );
+ }
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ }
+ }
+ else
+ {
+ if ( nType & SvNumFormatType::CURRENCY )
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::CURRENCY );
+ }
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_PERCENT:
+ if (nType & SvNumFormatType::PERCENT)
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ else
+ pTabViewShell->SetNumberFormat( SvNumFormatType::PERCENT );
+ rBindings.Invalidate( nSlot );
+ rReq.Done();
+ break;
+ case SID_NUMBER_STANDARD:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER );
+ rReq.Done();
+ break;
+ case SID_NUMBER_INCDEC:
+ pTabViewShell->ChangeNumFmtDecimals( true );
+ rReq.Done();
+ break;
+ case SID_NUMBER_DECDEC:
+ pTabViewShell->ChangeNumFmtDecimals( false );
+ rReq.Done();
+ break;
+ case SID_NUMBER_THOUSANDS:
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ bool bThousand(false);
+ bool bNegRed(false);
+ sal_uInt16 nPrecision(0);
+ sal_uInt16 nLeadZeroes(0);
+ LanguageType eLanguage = ScGlobal::eLnge;
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat(
+ rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nCurrentNumberFormat);
+
+ if (pEntry)
+ eLanguage = pEntry->GetLanguage();
+
+ pFormatter->GetFormatSpecialInfo(nCurrentNumberFormat, bThousand, bNegRed, nPrecision, nLeadZeroes);
+ bThousand = !bThousand;
+ OUString aCode = pFormatter->GenerateFormat(
+ nCurrentNumberFormat,
+ eLanguage,
+ bThousand,
+ bNegRed,
+ nPrecision,
+ nLeadZeroes);
+ pTabViewShell->SetNumFmtByStr(aCode);
+
+ rBindings.Invalidate(nSlot);
+ rReq.Done();
+ }
+ break;
+ case SID_NUMBER_FORMAT:
+ // symphony version with format interpretation
+ if(pReqArgs)
+ {
+ const SfxPoolItem* pItem;
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat(
+ rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nCurrentNumberFormat);
+
+ if(!pEntry)
+ break;
+
+ LanguageType eLanguage = pEntry->GetLanguage();
+ SvNumFormatType eType = pEntry->GetMaskedType();
+
+ //Just use eType to judge whether the command is fired for NUMBER/PERCENT/CURRENCY/SCIENTIFIC/FRACTION/TIME
+ //In sidebar, users can fire SID_NUMBER_FORMAT command by operating the related UI controls before they are disable
+ if(!(eType == SvNumFormatType::ALL
+ || eType == SvNumFormatType::NUMBER
+ || eType == SvNumFormatType::PERCENT
+ || eType == SvNumFormatType::CURRENCY
+ || eType == SvNumFormatType::SCIENTIFIC
+ || eType == SvNumFormatType::TIME
+ || eType == SvNumFormatType::FRACTION))
+ pEntry = nullptr;
+
+ if(SfxItemState::SET == pReqArgs->GetItemState(nSlot, true, &pItem) && pEntry)
+ {
+ OUString aCode = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ sal_uInt16 aLen = aCode.getLength();
+ std::unique_ptr<OUString[]> sFormat( new OUString[4] );
+ OUStringBuffer sTmpStr;
+ sal_uInt16 nCount(0);
+ sal_uInt16 nStrCount(0);
+
+ while(nCount < aLen)
+ {
+ sal_Unicode cChar = aCode[nCount];
+
+ if(cChar == ',')
+ {
+ sFormat[nStrCount] = sTmpStr.makeStringAndClear();
+ nStrCount++;
+ }
+ else
+ {
+ sTmpStr.append(cChar);
+ }
+
+ nCount++;
+
+ if(nStrCount > 3)
+ break;
+ }
+
+ const bool bThousand = static_cast<bool>(sFormat[0].toInt32());
+ const bool bNegRed = static_cast<bool>(sFormat[1].toInt32());
+ const sal_uInt16 nPrecision = static_cast<sal_uInt16>(sFormat[2].toInt32());
+ const sal_uInt16 nLeadZeroes = static_cast<sal_uInt16>(sFormat[3].toInt32());
+
+ aCode = pFormatter->GenerateFormat(
+ nCurrentNumberFormat,//modify
+ eLanguage,
+ bThousand,
+ bNegRed,
+ nPrecision,
+ nLeadZeroes);
+ pTabViewShell->SetNumFmtByStr(aCode);
+ }
+ }
+ break;
+
+ case SID_ATTR_NUMBERFORMAT_VALUE:
+ if ( pReqArgs )
+ {
+ if ( const SfxUInt32Item* pItem = pReqArgs->GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ // We have to accomplish this using ApplyAttributes()
+ // because we also need the language information to be
+ // considered.
+ const SfxItemSet& rOldSet =
+ pTabViewShell->GetSelectionPattern()->GetItemSet();
+ SfxItemPool* pDocPool = GetViewData().GetDocument().GetPool();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *pDocPool );
+ aNewSet.Put( *pItem );
+ pTabViewShell->ApplyAttributes( aNewSet, rOldSet );
+ }
+ }
+ break;
+
+ case SID_NUMBER_TYPE_FORMAT:
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ {
+ sal_uInt16 nFormat = static_cast<const SfxUInt16Item *>(pItem)->GetValue();
+ switch(nFormat)
+ {
+ case 0:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER); //Modify
+ break;
+ case 1:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::NUMBER, 2 ); //Modify
+ break;
+ case 2:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::PERCENT );
+ break;
+ case 3:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::CURRENCY );
+ break;
+ case 4:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::DATE );
+ break;
+ case 5:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::TIME );
+ break;
+ case 6:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::SCIENTIFIC );
+ break;
+ case 7:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::FRACTION );
+ break;
+ case 8:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::LOGICAL );
+ break;
+ case 9:
+ pTabViewShell->SetNumberFormat( SvNumFormatType::TEXT );
+ break;
+ default:
+ ;
+ }
+ rReq.Done();
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("ExecuteEdit: invalid slot");
+ break;
+ }
+}
+
+void ScFormatShell::ExecuteAlignment( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const SfxItemSet* pSet = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ switch( nSlot )
+ {
+ // pseudo slots for Format menu
+ case SID_ALIGN_ANY_HDEFAULT:
+ case SID_ALIGN_ANY_LEFT:
+ case SID_ALIGN_ANY_HCENTER:
+ case SID_ALIGN_ANY_RIGHT:
+ case SID_ALIGN_ANY_JUSTIFIED:
+ pTabViewShell->ApplyAttr( SvxHorJustifyItem( lclConvertSlotToHAlign( nSlot ), ATTR_HOR_JUSTIFY ) );
+ break;
+ case SID_ALIGN_ANY_VDEFAULT:
+ case SID_ALIGN_ANY_TOP:
+ case SID_ALIGN_ANY_VCENTER:
+ case SID_ALIGN_ANY_BOTTOM:
+ pTabViewShell->ApplyAttr( SvxVerJustifyItem( lclConvertSlotToVAlign( nSlot ), ATTR_VER_JUSTIFY ) );
+ break;
+
+ default:
+ if( pSet )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( pSet->GetItemState(GetPool().GetWhich(nSlot), true, &pItem ) == SfxItemState::SET )
+ {
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_ALIGN_HOR_JUSTIFY:
+ case SID_ATTR_ALIGN_VER_JUSTIFY:
+ case SID_ATTR_ALIGN_INDENT:
+ case SID_ATTR_ALIGN_HYPHENATION:
+ case SID_ATTR_ALIGN_DEGREES:
+ case SID_ATTR_ALIGN_LOCKPOS:
+ case SID_ATTR_ALIGN_MARGIN:
+ case SID_ATTR_ALIGN_STACKED:
+ pTabViewShell->ApplyAttr( *pItem );
+ break;
+
+ case SID_H_ALIGNCELL:
+ {
+ SvxCellHorJustify eJust = static_cast<const SvxHorJustifyItem*>(pItem)->GetValue();
+ // #i78476# update alignment of text in cell edit mode
+ pTabViewShell->UpdateInputHandlerCellAdjust( eJust );
+ pTabViewShell->ApplyAttr( SvxHorJustifyItem( eJust, ATTR_HOR_JUSTIFY ) );
+ }
+ break;
+ case SID_V_ALIGNCELL:
+ pTabViewShell->ApplyAttr( SvxVerJustifyItem( static_cast<const SvxVerJustifyItem*>(pItem)->GetValue(), ATTR_VER_JUSTIFY ) );
+ break;
+ default:
+ OSL_FAIL( "ExecuteAlignment: invalid slot" );
+ return;
+ }
+ }
+ }
+ }
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ALIGNTOP );
+ rBindings.Invalidate( SID_ALIGNBOTTOM );
+ rBindings.Invalidate( SID_ALIGNCENTERVER );
+ rBindings.Invalidate( SID_V_ALIGNCELL );
+ rBindings.Invalidate( SID_H_ALIGNCELL );
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_HDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ rBindings.Invalidate( SID_ALIGN_ANY_VDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_TOP );
+ rBindings.Invalidate( SID_ALIGN_ANY_VCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_BOTTOM );
+ rBindings.Update();
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+}
+
+void ScFormatShell::ExecuteTextAttr( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const ScPatternAttr* pAttrs = pTabViewShell->GetSelectionPattern();
+ const SfxItemSet* pSet = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ std::optional<SfxAllItemSet> pNewSet;
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+
+ if ( (nSlot == SID_ATTR_CHAR_WEIGHT)
+ ||(nSlot == SID_ATTR_CHAR_POSTURE)
+ ||(nSlot == SID_ATTR_CHAR_UNDERLINE)
+ ||(nSlot == SID_ULINE_VAL_NONE)
+ ||(nSlot == SID_ULINE_VAL_SINGLE)
+ ||(nSlot == SID_ULINE_VAL_DOUBLE)
+ ||(nSlot == SID_ULINE_VAL_DOTTED) )
+ {
+ pNewSet.emplace( GetPool() );
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_WEIGHT:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ if ( pSet )
+ aSetItem.PutItemForScriptType( nScript, pSet->Get( ATTR_FONT_WEIGHT ) );
+ else
+ {
+ // toggle manually
+
+ FontWeight eWeight = WEIGHT_BOLD;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pAttrs->GetItemSet(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxWeightItem*>(pCore)->GetWeight() == WEIGHT_BOLD )
+ eWeight = WEIGHT_NORMAL;
+
+ aSetItem.PutItemForScriptType( nScript, SvxWeightItem( eWeight, ATTR_FONT_WEIGHT ) );
+ }
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+ pNewSet->Put( aSetItem.GetItemSet(), false );
+ }
+ break;
+
+ case SID_ATTR_CHAR_POSTURE:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ if ( pSet )
+ aSetItem.PutItemForScriptType( nScript, pSet->Get( ATTR_FONT_POSTURE ) );
+ else
+ {
+ // toggle manually
+
+ FontItalic eItalic = ITALIC_NORMAL;
+ SvxScriptSetItem aOldSetItem( nSlot, rPool );
+ aOldSetItem.GetItemSet().Put( pAttrs->GetItemSet(), false );
+ const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript );
+ if ( pCore && static_cast<const SvxPostureItem*>(pCore)->GetPosture() == ITALIC_NORMAL )
+ eItalic = ITALIC_NONE;
+
+ aSetItem.PutItemForScriptType( nScript, SvxPostureItem( eItalic, ATTR_FONT_POSTURE ) );
+ }
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+ pNewSet->Put( aSetItem.GetItemSet(), false );
+ }
+ break;
+
+ case SID_ATTR_CHAR_UNDERLINE:
+ {
+ if( pSet )
+ {
+ const SfxPoolItem& rUnderline = pSet->Get( ATTR_FONT_UNDERLINE );
+
+ if( dynamic_cast<const SvxUnderlineItem*>( &rUnderline) != nullptr )
+ {
+ pTabViewShell->ApplyAttr( rUnderline );
+ pNewSet->Put( rUnderline,rUnderline.Which() );
+ }
+ else if ( auto pTextLineItem = dynamic_cast<const SvxTextLineItem*>( &rUnderline) )
+ {
+ // #i106580# also allow SvxTextLineItem (base class of SvxUnderlineItem)
+ SvxUnderlineItem aNewItem( pTextLineItem->GetLineStyle(), pTextLineItem->Which() );
+ aNewItem.SetColor( pTextLineItem->GetColor() );
+ pTabViewShell->ApplyAttr( aNewItem );
+ pNewSet->Put( aNewItem, aNewItem.Which() );
+ }
+ }
+ else
+ {
+ SvxUnderlineItem aUnderline( pAttrs->GetItem( ATTR_FONT_UNDERLINE ) );
+ FontLineStyle eUnderline = (LINESTYLE_NONE != aUnderline.GetLineStyle())
+ ? LINESTYLE_NONE
+ : LINESTYLE_SINGLE;
+ aUnderline.SetLineStyle( eUnderline );
+ pTabViewShell->ApplyAttr( aUnderline );
+ pNewSet->Put( aUnderline,aUnderline.Which() );
+ }
+ }
+ break;
+
+ case SID_ULINE_VAL_NONE:
+ pTabViewShell->ApplyAttr( SvxUnderlineItem( LINESTYLE_NONE, ATTR_FONT_UNDERLINE ) );
+ break;
+ case SID_ULINE_VAL_SINGLE: // Toggles
+ case SID_ULINE_VAL_DOUBLE:
+ case SID_ULINE_VAL_DOTTED:
+ {
+ FontLineStyle eOld = pAttrs->GetItem(ATTR_FONT_UNDERLINE).GetLineStyle();
+ FontLineStyle eNew = eOld;
+ switch (nSlot)
+ {
+ case SID_ULINE_VAL_SINGLE:
+ eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE;
+ break;
+ case SID_ULINE_VAL_DOUBLE:
+ eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE;
+ break;
+ case SID_ULINE_VAL_DOTTED:
+ eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED;
+ break;
+ }
+ pTabViewShell->ApplyAttr( SvxUnderlineItem( eNew, ATTR_FONT_UNDERLINE ) );
+ }
+ break;
+
+ default:
+ break;
+ }
+ rBindings.Invalidate( nSlot );
+ }
+ else
+ {
+ /*
+ * "Self-made" functionality of radio buttons
+ * At the toggle the default state is used, this means
+ * no button was clicked.
+ */
+
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ const SvxHorJustifyItem* pHorJustify = rAttrSet.GetItemIfSet(ATTR_HOR_JUSTIFY);
+ const SvxVerJustifyItem* pVerJustify = rAttrSet.GetItemIfSet(ATTR_VER_JUSTIFY );
+ SvxCellHorJustify eHorJustify = SvxCellHorJustify::Standard;
+ SvxCellVerJustify eVerJustify = SvxCellVerJustify::Standard;
+
+ if (pHorJustify)
+ {
+ eHorJustify = pHorJustify->GetValue();
+ }
+ if (pVerJustify)
+ {
+ eVerJustify = pVerJustify->GetValue();
+ }
+
+ switch ( nSlot )
+ {
+ case SID_ALIGNLEFT:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Left) ?
+ SvxCellHorJustify::Left : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNRIGHT:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Right) ?
+ SvxCellHorJustify::Right : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNCENTERHOR:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Center) ?
+ SvxCellHorJustify::Center : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNBLOCK:
+ rReq.SetSlot( SID_H_ALIGNCELL );
+ rReq.AppendItem( SvxHorJustifyItem(
+ !pHorJustify || (eHorJustify != SvxCellHorJustify::Block) ?
+ SvxCellHorJustify::Block : SvxCellHorJustify::Standard, SID_H_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNTOP:
+ rReq.SetSlot( SID_V_ALIGNCELL );
+ rReq.AppendItem( SvxVerJustifyItem(
+ !pVerJustify || (eVerJustify != SvxCellVerJustify::Top) ?
+ SvxCellVerJustify::Top : SvxCellVerJustify::Standard, SID_V_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNBOTTOM:
+ rReq.SetSlot( SID_V_ALIGNCELL );
+ rReq.AppendItem( SvxVerJustifyItem(
+ !pVerJustify || (eVerJustify != SvxCellVerJustify::Bottom) ?
+ SvxCellVerJustify::Bottom : SvxCellVerJustify::Standard, SID_V_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ case SID_ALIGNCENTERVER:
+ rReq.SetSlot( SID_V_ALIGNCELL );
+ rReq.AppendItem( SvxVerJustifyItem(
+ !pVerJustify || (eVerJustify != SvxCellVerJustify::Center) ?
+ SvxCellVerJustify::Center : SvxCellVerJustify::Standard, SID_V_ALIGNCELL ) );
+ ExecuteSlot( rReq, GetInterface() );
+ return;
+
+ default:
+ break;
+ }
+
+ }
+
+ rBindings.Update();
+
+ if( pNewSet )
+ {
+ rReq.Done( *pNewSet );
+ pNewSet.reset();
+ }
+ else
+ {
+ rReq.Done();
+ }
+
+}
+
+void ScFormatShell::ExecuteAttr( SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ SfxBindings& rBindings = rViewData.GetBindings();
+ const SfxItemSet* pNewAttrs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( !pNewAttrs )
+ {
+ switch ( nSlot )
+ {
+ case SID_GROW_FONT_SIZE:
+ case SID_SHRINK_FONT_SIZE:
+ {
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONTHEIGHT, rPool );
+ aSetItem.GetItemSet().Put( pTabViewShell->GetSelectionPattern()->GetItemSet(), false );
+
+ SvtScriptType nScriptTypes = pTabViewShell->GetSelectionScriptType();
+ const SvxFontHeightItem* pSize( static_cast<const SvxFontHeightItem*>( aSetItem.GetItemOfScript( nScriptTypes ) ) );
+
+ if ( pSize )
+ {
+ SvxFontHeightItem aSize( *pSize );
+ sal_uInt32 nSize = aSize.GetHeight();
+
+ const sal_uInt32 nFontInc = 40; // 2pt
+ const sal_uInt32 nFontMaxSz = 19998; // 999.9pt
+ if ( nSlot == SID_GROW_FONT_SIZE )
+ nSize = std::min< sal_uInt32 >( nSize + nFontInc, nFontMaxSz );
+ else
+ nSize = std::max< sal_Int32 >( nSize - nFontInc, nFontInc );
+
+ aSize.SetHeight( nSize );
+ aSetItem.PutItemForScriptType( nScriptTypes, aSize );
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+ }
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ break;
+
+ case SID_ATTR_CHAR_ENDPREVIEW_FONT:
+ {
+ rDoc.SetPreviewFont(nullptr);
+ pTabViewShell->UpdateSelectionArea( rDoc.GetPreviewSelection() );
+ break;
+ }
+ case SID_ATTR_CHAR_COLOR:
+ case SID_ATTR_CHAR_FONT:
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ pTabViewShell->ExecuteCellFormatDlg(rReq, "font"); // when ToolBar is vertical
+ break;
+
+ case SID_BACKGROUND_COLOR:
+ {
+ SvxBrushItem aBrushItem(
+ pTabViewShell->GetSelectionPattern()->GetItem( ATTR_BACKGROUND ) );
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ pTabViewShell->ApplyAttr( aBrushItem, false );
+ }
+ break;
+
+ case SID_ATTR_ALIGN_LINEBREAK: // without parameter as toggle
+ {
+ const ScPatternAttr* pAttrs = pTabViewShell->GetSelectionPattern();
+ bool bOld = pAttrs->GetItem(ATTR_LINEBREAK).GetValue();
+ ScLineBreakCell aBreakItem(!bOld);
+ pTabViewShell->ApplyAttr( aBreakItem );
+
+ SfxAllItemSet aNewSet( GetPool() );
+ aNewSet.Put( aBreakItem,aBreakItem.Which() );
+ rReq.Done( aNewSet );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+
+ case SID_SCATTR_CELLPROTECTION: // without parameter as toggle
+ {
+ const ScPatternAttr* pAttrs = pTabViewShell->GetSelectionPattern();
+ bool bProtect = pAttrs->GetItem(ATTR_PROTECTION).GetProtection();
+ bool bHideFormula = pAttrs->GetItem(ATTR_PROTECTION).GetHideFormula();
+ bool bHideCell = pAttrs->GetItem(ATTR_PROTECTION).GetHideCell();
+ bool bHidePrint = pAttrs->GetItem(ATTR_PROTECTION).GetHidePrint();
+
+ ScProtectionAttr aProtectionItem( !bProtect, bHideFormula, bHideCell, bHidePrint );
+ pTabViewShell->ApplyAttr( aProtectionItem );
+
+ SfxAllItemSet aNewSet( GetPool() );
+ aNewSet.Put( aProtectionItem, aProtectionItem.Which());
+ aNewSet.Put( SfxBoolItem( SID_SCATTR_CELLPROTECTION, !bProtect ) );
+ rReq.Done( aNewSet );
+
+ rBindings.Invalidate( nSlot );
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_PREVIEW_FONT:
+ {
+ SfxItemPool& rPool = GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ const SvxFontItem& rFont = static_cast<const SvxFontItem&>(pNewAttrs->Get( nWhich ));
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, rPool );
+ SvtScriptType nScript = pTabViewShell->GetSelectionScriptType();
+ aSetItem.PutItemForScriptType( nScript, rFont );
+
+ ScMarkData aFuncMark( rViewData.GetMarkData() );
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ rDoc.SetPreviewFont( aSetItem.GetItemSet().Clone() );
+ aFuncMark.MarkToMulti();
+
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ ScRange aRange( nCol, nRow, nTab );
+ aFuncMark.SetMarkArea( aRange );
+ }
+ rDoc.SetPreviewSelection( aFuncMark );
+ pTabViewShell->UpdateSelectionArea( aFuncMark );
+ break;
+ }
+ case SID_ATTR_CHAR_OVERLINE:
+ case SID_ATTR_CHAR_STRIKEOUT:
+ case SID_ATTR_ALIGN_LINEBREAK:
+ case SID_ATTR_CHAR_CONTOUR:
+ case SID_ATTR_CHAR_SHADOWED:
+ case SID_ATTR_CHAR_RELIEF:
+ pTabViewShell->ApplyAttr( pNewAttrs->Get( pNewAttrs->GetPool()->GetWhich( nSlot ) ) );
+ rBindings.Invalidate( nSlot );
+ rBindings.Update( nSlot );
+ break;
+ case SID_ATTR_CHAR_COLOR:
+ case SID_SCATTR_PROTECTION :
+ {
+ pTabViewShell->ApplyAttr( pNewAttrs->Get( pNewAttrs->GetPool()->GetWhich( nSlot) ), false);
+
+ rBindings.Invalidate( nSlot );
+ rBindings.Update( nSlot );
+ }
+
+ break;
+
+ case SID_ATTR_CHAR_FONT:
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ {
+ // #i78017 establish the same behaviour as in Writer
+ SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ if (nSlot == SID_ATTR_CHAR_FONT)
+ nScript = pTabViewShell->GetSelectionScriptType();
+
+ SfxItemPool& rPool = GetPool();
+ SvxScriptSetItem aSetItem( nSlot, rPool );
+ sal_uInt16 nWhich = rPool.GetWhich( nSlot );
+ aSetItem.PutItemForScriptType( nScript, pNewAttrs->Get( nWhich ) );
+
+ pTabViewShell->ApplyUserItemSet( aSetItem.GetItemSet() );
+
+ rBindings.Invalidate( nSlot );
+ rBindings.Update( nSlot );
+ }
+ break;
+
+ case SID_FRAME_LINESTYLE:
+ {
+ // Update default line
+ const ::editeng::SvxBorderLine* pLine =
+ pNewAttrs->Get( SID_FRAME_LINESTYLE ).
+ GetLine();
+
+ if ( pLine )
+ {
+ ::editeng::SvxBorderLine* pDefLine = pTabViewShell->GetDefaultFrameLine();
+
+ if ( pDefLine )
+ {
+ pDefLine->SetBorderLineStyle(
+ pLine->GetBorderLineStyle());
+ pDefLine->SetWidth( pLine->GetWidth( ) );
+ pTabViewShell->SetSelectionFrameLines( pDefLine, false );
+ }
+ else
+ {
+ pTabViewShell->SetDefaultFrameLine( pLine );
+ pTabViewShell->GetDefaultFrameLine()->SetColor( COL_BLACK );
+ pTabViewShell->SetSelectionFrameLines( pLine, false );
+ }
+ }
+ else
+ {
+ Color aColorBlack( COL_BLACK );
+ ::editeng::SvxBorderLine aDefLine( &aColorBlack, 20,
+ SvxBorderLineStyle::SOLID );
+ pTabViewShell->SetDefaultFrameLine( &aDefLine );
+ pTabViewShell->SetSelectionFrameLines( nullptr, false );
+ }
+ }
+ break;
+
+ case SID_FRAME_LINECOLOR:
+ {
+ ::editeng::SvxBorderLine* pDefLine = pTabViewShell->GetDefaultFrameLine();
+
+ Color aColor = pNewAttrs->Get( SID_FRAME_LINECOLOR ).GetValue();
+
+ // Update default line
+ if ( pDefLine )
+ {
+ pDefLine->SetColor( aColor );
+ pTabViewShell->SetSelectionFrameLines( pDefLine, true );
+ }
+ else
+ {
+ ::editeng::SvxBorderLine aDefLine( &aColor, 20, SvxBorderLineStyle::SOLID );
+ pTabViewShell->SetDefaultFrameLine( &aDefLine );
+ pTabViewShell->SetSelectionFrameLines( &aDefLine, false );
+ }
+ }
+ break;
+
+ case SID_ATTR_BORDER_OUTER:
+ case SID_ATTR_BORDER:
+ {
+ ::editeng::SvxBorderLine* pDefLine = pTabViewShell->GetDefaultFrameLine();
+ const ScPatternAttr* pOldAttrs = pTabViewShell->GetSelectionPattern();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aOldSet( *rDoc.GetPool() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *rDoc.GetPool() );
+ const SfxPoolItem& rBorderAttr =
+ pOldAttrs->GetItemSet().
+ Get( ATTR_BORDER );
+
+ // Evaluate border items from controller:
+
+ if ( const SvxBoxItem* pBoxItem = pNewAttrs->GetItemIfSet( ATTR_BORDER ) )
+ {
+ // The SvxFrameToolBoxControl toolbox controller uses a default
+ // SvxBorderLine (all widths 0) to mark the lines that should be set.
+ // Macro recording uses a SvxBoxItem with the real values (OutWidth > 0)
+ // or NULL pointers for no lines.
+ // -> Substitute existing lines with pDefLine only if widths are 0.
+ SvxBoxItem aBoxItem ( *pBoxItem );
+ if ( aBoxItem.GetTop() && aBoxItem.GetTop()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::TOP );
+ if ( aBoxItem.GetBottom() && aBoxItem.GetBottom()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::BOTTOM );
+ if ( aBoxItem.GetLeft() && aBoxItem.GetLeft()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::LEFT );
+ if ( aBoxItem.GetRight() && aBoxItem.GetRight()->GetOutWidth() == 0 )
+ aBoxItem.SetLine( pDefLine, SvxBoxItemLine::RIGHT );
+ aNewSet.Put( aBoxItem );
+ rReq.AppendItem( aBoxItem );
+ }
+
+ if ( const SvxBoxInfoItem* pBoxInfoItem = pNewAttrs->GetItemIfSet( ATTR_BORDER_INNER ) )
+ {
+ SvxBoxInfoItem aBoxInfoItem( *pBoxInfoItem );
+ if ( aBoxInfoItem.GetHori() && aBoxInfoItem.GetHori()->GetOutWidth() == 0 )
+ aBoxInfoItem.SetLine( pDefLine, SvxBoxInfoItemLine::HORI );
+ if ( aBoxInfoItem.GetVert() && aBoxInfoItem.GetVert()->GetOutWidth() == 0 )
+ aBoxInfoItem.SetLine( pDefLine, SvxBoxInfoItemLine::VERT );
+ aNewSet.Put( aBoxInfoItem );
+ rReq.AppendItem( aBoxInfoItem );
+ }
+ else
+ {
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+ aBoxInfoItem.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ aBoxInfoItem.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ aNewSet.Put( aBoxInfoItem );
+ }
+
+ aOldSet.Put( rBorderAttr );
+ pTabViewShell->ApplyAttributes( aNewSet, aOldSet );
+ }
+ break;
+
+ case SID_ATTR_BORDER_DIAG_TLBR:
+ case SID_ATTR_BORDER_DIAG_BLTR:
+ {
+ const ScPatternAttr* pOldAttrs = pTabViewShell->GetSelectionPattern();
+ SfxItemSet aOldSet(pOldAttrs->GetItemSet());
+ SfxItemSet aNewSet(pOldAttrs->GetItemSet());
+
+ if(SID_ATTR_BORDER_DIAG_TLBR == nSlot)
+ {
+ if(SfxItemState::SET == pNewAttrs->GetItemState(ATTR_BORDER_TLBR))
+ {
+ SvxLineItem aItem(ATTR_BORDER_TLBR);
+ aItem.SetLine(pNewAttrs->Get(ATTR_BORDER_TLBR).GetLine());
+ aNewSet.Put(aItem);
+ rReq.AppendItem(aItem);
+ pTabViewShell->ApplyAttributes(aNewSet, aOldSet);
+ }
+ }
+ else // if( nSlot == SID_ATTR_BORDER_DIAG_BLTR )
+ {
+ if(SfxItemState::SET == pNewAttrs->GetItemState(ATTR_BORDER_BLTR ))
+ {
+ SvxLineItem aItem(ATTR_BORDER_BLTR);
+ aItem.SetLine(pNewAttrs->Get(ATTR_BORDER_BLTR).GetLine());
+ aNewSet.Put(aItem);
+ rReq.AppendItem(aItem);
+ pTabViewShell->ApplyAttributes(aNewSet, aOldSet);
+ }
+ }
+
+ rBindings.Invalidate(nSlot);
+ }
+ break;
+
+ // ATTR_BACKGROUND (=SID_ATTR_BRUSH) has to be set to two IDs:
+ case SID_BACKGROUND_COLOR:
+ {
+ const SvxColorItem& rNewColorItem = pNewAttrs->Get( SID_BACKGROUND_COLOR );
+ Color aColor = rNewColorItem.GetValue();
+
+ SvxBrushItem aBrushItem(
+ pTabViewShell->GetSelectionPattern()->GetItem( ATTR_BACKGROUND ) );
+ aBrushItem.SetColor(aColor);
+ aBrushItem.setComplexColor(rNewColorItem.getComplexColor());
+
+ pTabViewShell->ApplyAttr( aBrushItem, false );
+ }
+ break;
+
+ case SID_ATTR_BRUSH:
+ {
+ SvxBrushItem aBrushItem( pTabViewShell->GetSelectionPattern()->
+ GetItem( ATTR_BACKGROUND ) );
+ const SvxBrushItem& rNewBrushItem = static_cast<const SvxBrushItem&>(
+ pNewAttrs->Get( GetPool().GetWhich(nSlot) ) );
+ aBrushItem.SetColor(rNewBrushItem.GetColor());
+ aBrushItem.setComplexColor(rNewBrushItem.getComplexColor());
+ pTabViewShell->ApplyAttr( aBrushItem );
+ }
+ break;
+
+ case SID_ATTR_BORDER_SHADOW:
+ {
+ const SvxShadowItem& rNewShadowItem =
+ pNewAttrs->Get( ATTR_SHADOW );
+ pTabViewShell->ApplyAttr( rNewShadowItem );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if( ! rReq.IsAPI() && ! rReq.IsDone() )
+ rReq.Done();
+ }
+}
+
+void ScFormatShell::GetAttrState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ const SvxBrushItem& rBrushItem = rAttrSet.Get( ATTR_BACKGROUND );
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ rSet.Put( rAttrSet, false );
+
+ // choose font info according to selection script type
+ SvtScriptType nScript = SvtScriptType::NONE; // GetSelectionScriptType never returns 0
+ if ( rSet.GetItemState( ATTR_FONT ) != SfxItemState::UNKNOWN )
+ {
+ nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT, nScript );
+ }
+ if ( rSet.GetItemState( ATTR_FONT_HEIGHT ) != SfxItemState::UNKNOWN )
+ {
+ if (nScript == SvtScriptType::NONE) nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT_HEIGHT, nScript );
+ }
+
+ while ( nWhich )
+ {
+ switch(nWhich)
+ {
+ case SID_BACKGROUND_COLOR:
+ {
+ rSet.Put( SvxColorItem( rBrushItem.GetColor(), SID_BACKGROUND_COLOR ) );
+ if(SfxItemState::DONTCARE == rAttrSet.GetItemState(ATTR_BACKGROUND))
+ {
+ rSet.InvalidateItem(SID_BACKGROUND_COLOR);
+ }
+ }
+ break;
+ case SID_FRAME_LINESTYLE:
+ case SID_FRAME_LINECOLOR:
+ {
+ // handled together because both need the cell border information for decisions
+ Color aCol;
+ editeng::SvxBorderLine aLine(nullptr,0,SvxBorderLineStyle::SOLID);
+ bool bCol = false;
+ bool bColDisable = false, bStyleDisable = false;
+ std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(ATTR_BORDER));
+ std::shared_ptr<SvxBoxInfoItem> aInfoItem(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));
+
+ pTabViewShell->GetSelectionFrame(aBoxItem, aInfoItem);
+
+ if( aBoxItem->GetTop() )
+ {
+ bCol = true;
+ aCol = aBoxItem->GetTop()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetTop()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetTop()->GetBorderLineStyle());
+ }
+
+ if( aBoxItem->GetBottom() )
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aBoxItem->GetBottom()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetBottom()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetBottom()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aBoxItem->GetBottom()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aBoxItem->GetBottom() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aBoxItem->GetLeft() )
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aBoxItem->GetLeft()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetLeft()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetLeft()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aBoxItem->GetLeft()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aBoxItem->GetLeft() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aBoxItem->GetRight() )
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aBoxItem->GetRight()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aBoxItem->GetRight()->GetWidth());
+ aLine.SetBorderLineStyle( aBoxItem->GetRight()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aBoxItem->GetRight()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aBoxItem->GetRight() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aInfoItem->GetVert())
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aInfoItem->GetVert()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aInfoItem->GetVert()->GetWidth());
+ aLine.SetBorderLineStyle( aInfoItem->GetVert()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aInfoItem->GetVert()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aInfoItem->GetVert() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( aInfoItem->GetHori())
+ {
+ if(!bCol)
+ {
+ bCol = true;
+ aCol = aInfoItem->GetHori()->GetColor() ;
+ aLine.SetColor(aCol);
+ aLine.SetWidth( aInfoItem->GetHori()->GetWidth());
+ aLine.SetBorderLineStyle( aInfoItem->GetHori()->GetBorderLineStyle());
+ }
+ else
+ {
+ if(aCol != aInfoItem->GetHori()->GetColor() )
+ bColDisable = true;
+ if( aLine != *aInfoItem->GetHori() )
+ bStyleDisable = true;
+ }
+ }
+
+ if( !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::VERT )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::HORI )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::LEFT )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::RIGHT )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::TOP )
+ || !aInfoItem->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) )
+ {
+ bColDisable = true;
+ bStyleDisable = true;
+ }
+
+ if(SID_FRAME_LINECOLOR == nWhich)
+ {
+ if(bColDisable) // if different lines have different colors
+ {
+ aCol = COL_TRANSPARENT;
+ rSet.Put( SvxColorItem(aCol, SID_FRAME_LINECOLOR ) );
+ rSet.InvalidateItem(SID_FRAME_LINECOLOR);
+ }
+ else if (!bCol) // if no line available
+ {
+ aCol = COL_AUTO;
+ rSet.Put( SvxColorItem(aCol, SID_FRAME_LINECOLOR ) );
+ }
+ else
+ rSet.Put( SvxColorItem(aCol, SID_FRAME_LINECOLOR ) );
+ }
+ else // if( nWhich == SID_FRAME_LINESTYLE)
+ {
+ if(bStyleDisable) // if have several lines but don't have same style
+ {
+ aLine.SetWidth( 1 );
+ SvxLineItem aItem(SID_FRAME_LINESTYLE);
+ aItem.SetLine(&aLine);
+ rSet.Put( aItem );
+ rSet.InvalidateItem(SID_FRAME_LINESTYLE);
+ }
+ else // all the lines have same style or no line available, use initial value (0,0,0,0)
+ {
+ SvxLineItem aItem(SID_FRAME_LINESTYLE);
+ aItem.SetLine(&aLine);
+ rSet.Put( aItem );
+ }
+ }
+ }
+ break;
+ case SID_ATTR_BRUSH:
+ {
+ rSet.Put( rBrushItem.CloneSetWhich(GetPool().GetWhich(nWhich)) );
+ }
+ break;
+ case SID_SCATTR_CELLPROTECTION:
+ {
+ bool bProtect = rAttrSet.Get( ATTR_PROTECTION ).GetProtection();
+ rSet.Put( SfxBoolItem(SID_SCATTR_CELLPROTECTION, bProtect) );
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+
+ // stuff for sidebar panels
+ Invalidate(SID_ATTR_ALIGN_DEGREES);
+ Invalidate(SID_ATTR_ALIGN_LOCKPOS);
+ Invalidate(SID_ATTR_ALIGN_STACKED);
+}
+
+void ScFormatShell::GetTextAttrState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ rSet.Put( rAttrSet, false ); // Include ItemStates in copy
+
+ // choose font info according to selection script type
+ SvtScriptType nScript = SvtScriptType::NONE; // GetSelectionScriptType never returns 0
+ if ( rSet.GetItemState( ATTR_FONT_WEIGHT ) != SfxItemState::UNKNOWN )
+ {
+ nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT_WEIGHT, nScript );
+ }
+ if ( rSet.GetItemState( ATTR_FONT_POSTURE ) != SfxItemState::UNKNOWN )
+ {
+ if (nScript == SvtScriptType::NONE) nScript = pTabViewShell->GetSelectionScriptType();
+ ScViewUtil::PutItemScript( rSet, rAttrSet, ATTR_FONT_POSTURE, nScript );
+ }
+
+ SfxItemState eState;
+
+ // own control on radio button functionality:
+
+ // underline
+
+ eState = rAttrSet.GetItemState( ATTR_FONT_UNDERLINE );
+ if ( eState == SfxItemState::DONTCARE )
+ {
+ rSet.InvalidateItem( SID_ULINE_VAL_NONE );
+ rSet.InvalidateItem( SID_ULINE_VAL_SINGLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOUBLE );
+ rSet.InvalidateItem( SID_ULINE_VAL_DOTTED );
+ }
+ else
+ {
+ FontLineStyle eUnderline =
+ rAttrSet.Get(ATTR_FONT_UNDERLINE).GetLineStyle();
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED));
+ rSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE));
+ }
+
+ // horizontal alignment
+
+ const SvxHorJustifyItem* pHorJustify = nullptr;
+ const SvxVerJustifyItem* pVerJustify = nullptr;
+ SvxCellVerJustify eVerJustify = SvxCellVerJustify::Standard;
+ sal_uInt16 nWhich = 0;
+ bool bJustifyStd = false;
+ SfxBoolItem aBoolItem ( 0, true );
+
+ eState = rAttrSet.GetItemState( ATTR_HOR_JUSTIFY, true,
+ reinterpret_cast<const SfxPoolItem**>(&pHorJustify) );
+ switch ( eState )
+ {
+ case SfxItemState::SET:
+ {
+ switch ( pHorJustify->GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ break;
+
+ case SvxCellHorJustify::Left:
+ nWhich = SID_ALIGNLEFT;
+ break;
+
+ case SvxCellHorJustify::Right:
+ nWhich = SID_ALIGNRIGHT;
+ break;
+
+ case SvxCellHorJustify::Center:
+ nWhich = SID_ALIGNCENTERHOR;
+ break;
+
+ case SvxCellHorJustify::Block:
+ nWhich = SID_ALIGNBLOCK;
+ break;
+
+ case SvxCellHorJustify::Repeat:
+ default:
+ bJustifyStd = true;
+ break;
+ }
+ }
+ break;
+
+ case SfxItemState::DONTCARE:
+ rSet.InvalidateItem( SID_ALIGNLEFT );
+ rSet.InvalidateItem( SID_ALIGNRIGHT );
+ rSet.InvalidateItem( SID_ALIGNCENTERHOR );
+ rSet.InvalidateItem( SID_ALIGNBLOCK );
+ break;
+
+ default:
+ bJustifyStd = true;
+ break;
+ }
+
+ if ( nWhich )
+ {
+ aBoolItem.SetWhich( nWhich );
+ rSet.Put( aBoolItem );
+ }
+ else if ( bJustifyStd )
+ {
+ aBoolItem.SetValue( false );
+ aBoolItem.SetWhich( SID_ALIGNLEFT ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNRIGHT ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNCENTERHOR ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNBLOCK ); rSet.Put( aBoolItem );
+ bJustifyStd = false;
+ }
+
+ // vertical alignment
+
+ nWhich = 0;
+ aBoolItem.SetValue( true );
+
+ eState = rAttrSet.GetItemState( ATTR_VER_JUSTIFY, true,
+ reinterpret_cast<const SfxPoolItem**>(&pVerJustify) );
+
+ switch ( eState )
+ {
+ case SfxItemState::SET:
+ {
+ eVerJustify = pVerJustify->GetValue();
+
+ switch ( eVerJustify )
+ {
+ case SvxCellVerJustify::Top:
+ nWhich = SID_ALIGNTOP;
+ break;
+
+ case SvxCellVerJustify::Bottom:
+ nWhich = SID_ALIGNBOTTOM;
+ break;
+
+ case SvxCellVerJustify::Center:
+ nWhich = SID_ALIGNCENTERVER;
+ break;
+
+ case SvxCellVerJustify::Standard:
+ default:
+ bJustifyStd = true;
+ break;
+ }
+ }
+ break;
+
+ case SfxItemState::DONTCARE:
+ rSet.InvalidateItem( SID_ALIGNTOP );
+ rSet.InvalidateItem( SID_ALIGNBOTTOM );
+ rSet.InvalidateItem( SID_ALIGNCENTERVER );
+ break;
+
+ default:
+ bJustifyStd = true;
+ break;
+ }
+
+ if ( nWhich )
+ {
+ aBoolItem.SetWhich( nWhich );
+ rSet.Put( aBoolItem );
+ }
+ else if ( bJustifyStd )
+ {
+ aBoolItem.SetValue( false );
+ aBoolItem.SetWhich( SID_ALIGNTOP ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNBOTTOM ); rSet.Put( aBoolItem );
+ aBoolItem.SetWhich( SID_ALIGNCENTERVER ); rSet.Put( aBoolItem );
+ }
+}
+
+void ScFormatShell::GetBorderState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(ATTR_BORDER));
+ std::shared_ptr<SvxBoxInfoItem> aInfoItem(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));
+
+ pTabViewShell->GetSelectionFrame( aBoxItem, aInfoItem );
+
+ if ( rSet.GetItemState( ATTR_BORDER ) != SfxItemState::UNKNOWN )
+ rSet.Put( *aBoxItem );
+ if ( rSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::UNKNOWN )
+ rSet.Put( *aInfoItem );
+}
+
+void ScFormatShell::GetAlignState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ SvxCellHorJustify eHAlign = SvxCellHorJustify::Standard;
+ bool bHasHAlign = rAttrSet.GetItemState( ATTR_HOR_JUSTIFY ) != SfxItemState::DONTCARE;
+ if( bHasHAlign )
+ eHAlign = rAttrSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
+
+ SvxCellVerJustify eVAlign = SvxCellVerJustify::Standard;
+ bool bHasVAlign = rAttrSet.GetItemState( ATTR_VER_JUSTIFY ) != SfxItemState::DONTCARE;
+ if( bHasVAlign )
+ eVAlign = rAttrSet.Get( ATTR_VER_JUSTIFY ).GetValue();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_H_ALIGNCELL:
+ if ( bHasHAlign )
+ rSet.Put( SvxHorJustifyItem( eHAlign, nWhich ));
+ break;
+ case SID_V_ALIGNCELL:
+ if ( bHasVAlign )
+ rSet.Put( SvxVerJustifyItem( eVAlign, nWhich ));
+ break;
+
+ // pseudo slots for Format menu
+ case SID_ALIGN_ANY_HDEFAULT:
+ case SID_ALIGN_ANY_LEFT:
+ case SID_ALIGN_ANY_HCENTER:
+ case SID_ALIGN_ANY_RIGHT:
+ case SID_ALIGN_ANY_JUSTIFIED:
+ rSet.Put( SfxBoolItem( nWhich, bHasHAlign && (eHAlign == lclConvertSlotToHAlign( nWhich )) ) );
+ break;
+ case SID_ALIGN_ANY_VDEFAULT:
+ case SID_ALIGN_ANY_TOP:
+ case SID_ALIGN_ANY_VCENTER:
+ case SID_ALIGN_ANY_BOTTOM:
+ rSet.Put( SfxBoolItem( nWhich, bHasVAlign && (eVAlign == lclConvertSlotToVAlign( nWhich )) ) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScFormatShell::GetNumFormatState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+ const SfxItemState eItemState = rAttrSet.GetItemState( ATTR_VALUE_FORMAT );
+ sal_uInt32 nNumberFormat = rAttrSet.Get(ATTR_VALUE_FORMAT).GetValue();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ // If item state is default or set it
+ // indicates one number format so we
+ // don't have to iterate over all
+ // selected cells' attribute ranges to
+ // determine selected types.
+ // Does *NOT* include the
+ // SvNumFormatType::DEFINED bit.
+ const SvNumFormatType nType = (eItemState >= SfxItemState::DEFAULT ? pFormatter->GetType( nNumberFormat) :
+ GetCurrentNumberFormatType());
+ NfIndexTableOffset nOffset = pFormatter->GetIndexTableOffset(nNumberFormat);
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_NUMBER_THOUSANDS:
+ {
+ bool bEnable = (SfxItemState::DONTCARE != eItemState);
+ if (bEnable)
+ {
+ bEnable = ((nType != SvNumFormatType::ALL) && (nType &
+ (SvNumFormatType::NUMBER |
+ SvNumFormatType::PERCENT |
+ SvNumFormatType::CURRENCY |
+ SvNumFormatType::FRACTION)));
+ if (bEnable)
+ {
+ bool bThousand( false );
+ bool bNegRed( false );
+ sal_uInt16 nPrecision( 0 );
+ sal_uInt16 nLeadZeroes( 0 );
+ pFormatter->GetFormatSpecialInfo( nNumberFormat, bThousand, bNegRed, nPrecision, nLeadZeroes);
+ rSet.Put( SfxBoolItem( nWhich, bThousand));
+ }
+ }
+ if (!bEnable)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_NUMBER_FORMAT:
+ // symphony version with format interpretation
+ {
+ if(SfxItemState::DONTCARE != eItemState)
+ {
+ bool bThousand(false);
+ bool bNegRed(false);
+ sal_uInt16 nPrecision(0);
+ sal_uInt16 nLeadZeroes(0);
+
+ pFormatter->GetFormatSpecialInfo(nNumberFormat,bThousand, bNegRed, nPrecision, nLeadZeroes);
+
+ const SvNumberformat* pFormatEntry = pFormatter->GetEntry( nNumberFormat );
+ if (pFormatEntry && (pFormatEntry->GetType() & SvNumFormatType::SCIENTIFIC))
+ {
+ // if scientific, bThousand is used for engineering notation
+ const sal_uInt16 nIntegerDigits = pFormatEntry->GetFormatIntegerDigits();
+ bThousand = nIntegerDigits > 0 && ((nIntegerDigits % 3) == 0);
+ }
+ OUString aFormat;
+ static constexpr OUString sBreak = u","_ustr;
+ const OUString sThousand = OUString::number(static_cast<sal_Int32>(bThousand));
+ const OUString sNegRed = OUString::number(static_cast<sal_Int32>(bNegRed));
+ const OUString sPrecision = OUString::number(nPrecision);
+ const OUString sLeadZeroes = OUString::number(nLeadZeroes);
+ const OUString sNatNum12 = OUString::number( static_cast< sal_Int32 >( pFormatter->IsNatNum12( nNumberFormat ) ) );
+
+ aFormat += sThousand +
+ sBreak +
+ sNegRed +
+ sBreak +
+ sPrecision +
+ sBreak +
+ sLeadZeroes +
+ sBreak +
+ sNatNum12 +
+ sBreak;
+
+ rSet.Put(SfxStringItem(nWhich, aFormat));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString sPayload = ".uno:NumberFormat=" + aFormat;
+ GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ OUStringToOString(sPayload, RTL_TEXTENCODING_ASCII_US));
+ }
+ }
+ else
+ {
+ rSet.InvalidateItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_NUMBER_TYPE_FORMAT:
+ {
+ sal_Int16 nFormatCategory = -1;
+ if ( eItemState >= SfxItemState::DEFAULT ) //Modify for more robust
+ {
+ switch(nType)
+ {
+ case SvNumFormatType::NUMBER:
+ // Determine if General format.
+ if ((nNumberFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
+ nFormatCategory = 0;
+ else
+ nFormatCategory = 1;
+ break;
+ case SvNumFormatType::PERCENT:
+ nFormatCategory = 2;
+ break;
+ case SvNumFormatType::CURRENCY:
+ nFormatCategory = 3;
+ break;
+ case SvNumFormatType::DATE:
+ //Add
+ case SvNumFormatType::DATETIME:
+ nFormatCategory = 4;
+ break;
+ case SvNumFormatType::TIME:
+ nFormatCategory = 5;
+ break;
+ case SvNumFormatType::SCIENTIFIC:
+ nFormatCategory = 6;
+ break;
+ case SvNumFormatType::FRACTION:
+ nFormatCategory = 7;
+ break;
+ case SvNumFormatType::LOGICAL:
+ nFormatCategory = 8;
+ break;
+ case SvNumFormatType::TEXT:
+ nFormatCategory = 9;
+ break;
+ default:
+ nFormatCategory = -1; //for more robust
+ }
+ if( nFormatCategory == -1 )
+ rSet.InvalidateItem( nWhich );
+ else
+ rSet.Put( SfxUInt16Item( nWhich, nFormatCategory ) );
+ }
+ else
+ {
+ rSet.InvalidateItem( nWhich );
+ }
+
+ }
+ break;
+ case SID_NUMBER_CURRENCY:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::CURRENCY)) );
+ break;
+ case SID_NUMBER_SCIENTIFIC:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::SCIENTIFIC)) );
+ break;
+ case SID_NUMBER_DATE:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::DATE)) );
+ break;
+ case SID_NUMBER_PERCENT:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::PERCENT)) );
+ break;
+ case SID_NUMBER_TIME:
+ rSet.Put( SfxBoolItem(nWhich, bool(nType & SvNumFormatType::TIME)) );
+ break;
+ case SID_NUMBER_TWODEC:
+ rSet.Put( SfxBoolItem(nWhich, (nType & SvNumFormatType::NUMBER) && nOffset == NF_NUMBER_1000DEC2 ) );
+ break;
+ case SID_NUMBER_STANDARD:
+ rSet.Put( SfxBoolItem(nWhich, (nType & SvNumFormatType::NUMBER) && (nNumberFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScFormatShell::ExecuteTextDirection( const SfxRequest& rReq )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ pTabViewShell->HideListBox(); // Autofilter-DropDown-Listbox
+ bool bEditMode = false;
+ if ( GetViewData().HasEditView( GetViewData().GetActivePart() ) )
+ {
+ bEditMode=true;
+ SC_MOD()->InputEnterHandler();
+ pTabViewShell->UpdateInputHandler();
+ }
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch( nSlot )
+ {
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ {
+ bool bVert = (nSlot == SID_TEXTDIRECTION_TOP_TO_BOTTOM);
+ ScPatternAttr aAttr( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rItemSet = aAttr.GetItemSet();
+ rItemSet.Put( ScVerticalStackCell( bVert ) );
+ rItemSet.Put( SfxBoolItem( ATTR_VERTICAL_ASIAN, bVert ) );
+ pTabViewShell->ApplySelectionPattern( aAttr );
+ pTabViewShell->AdjustBlockHeight();
+ }
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ {
+ SvxFrameDirection eDirection = ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT ) ?
+ SvxFrameDirection::Horizontal_LR_TB : SvxFrameDirection::Horizontal_RL_TB;
+ pTabViewShell->ApplyAttr( SvxFrameDirectionItem( eDirection, ATTR_WRITINGDIR ) );
+ }
+ break;
+ }
+ if (bEditMode)
+ SC_MOD()->SetInputMode( SC_INPUT_TABLE );
+}
+
+void ScFormatShell::GetTextDirectionState( SfxItemSet& rSet )
+{
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ const SfxItemSet& rAttrSet = pTabViewShell->GetSelectionPattern()->GetItemSet();
+
+ bool bVertDontCare =
+ (rAttrSet.GetItemState( ATTR_VERTICAL_ASIAN ) == SfxItemState::DONTCARE) ||
+ (rAttrSet.GetItemState( ATTR_STACKED ) == SfxItemState::DONTCARE);
+ bool bLeftRight = !bVertDontCare &&
+ !rAttrSet.Get( ATTR_STACKED ).GetValue();
+ bool bTopBottom = !bVertDontCare && !bLeftRight &&
+ rAttrSet.Get( ATTR_VERTICAL_ASIAN ).GetValue();
+
+ bool bBidiDontCare = (rAttrSet.GetItemState( ATTR_WRITINGDIR ) == SfxItemState::DONTCARE);
+ EEHorizontalTextDirection eBidiDir = EEHorizontalTextDirection::Default;
+ if ( !bBidiDontCare )
+ {
+ SvxFrameDirection eCellDir = rAttrSet.Get( ATTR_WRITINGDIR ).GetValue();
+ if ( eCellDir == SvxFrameDirection::Environment )
+ eBidiDir = GetViewData().GetDocument().
+ GetEditTextDirection( GetViewData().GetTabNo() );
+ else if ( eCellDir == SvxFrameDirection::Horizontal_RL_TB )
+ eBidiDir = EEHorizontalTextDirection::R2L;
+ else
+ eBidiDir = EEHorizontalTextDirection::L2R;
+ }
+
+ bool bDisableCTLFont = !SvtCTLOptions::IsCTLFontEnabled();
+ bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled();
+
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ switch( nWhich )
+ {
+ case SID_TEXTDIRECTION_LEFT_TO_RIGHT:
+ case SID_TEXTDIRECTION_TOP_TO_BOTTOM:
+ if ( bDisableVerticalText )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if( bVertDontCare )
+ rSet.InvalidateItem( nWhich );
+ else if ( nWhich == SID_TEXTDIRECTION_LEFT_TO_RIGHT )
+ rSet.Put( SfxBoolItem( nWhich, bLeftRight ) );
+ else
+ rSet.Put( SfxBoolItem( nWhich, bTopBottom ) );
+ }
+ break;
+
+ case SID_ATTR_PARA_LEFT_TO_RIGHT:
+ case SID_ATTR_PARA_RIGHT_TO_LEFT:
+ if ( bDisableCTLFont )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if ( bTopBottom )
+ rSet.DisableItem( nWhich );
+ else if ( bBidiDontCare )
+ rSet.InvalidateItem( nWhich );
+ else if ( nWhich == SID_ATTR_PARA_LEFT_TO_RIGHT )
+ rSet.Put( SfxBoolItem( nWhich, eBidiDir == EEHorizontalTextDirection::L2R ) );
+ else
+ rSet.Put( SfxBoolItem( nWhich, eBidiDir == EEHorizontalTextDirection::R2L ) );
+ }
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScFormatShell::ExecFormatPaintbrush( const SfxRequest& rReq )
+{
+ ScViewFunc* pView = rViewData.GetView();
+ if ( pView->HasPaintBrush() )
+ {
+ // cancel paintbrush mode
+ pView->ResetBrushDocument();
+ }
+ else
+ {
+ bool bLock = false;
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ if( pArgs && pArgs->Count() >= 1 )
+ bLock = pArgs->Get(SID_FORMATPAINTBRUSH).GetValue();
+
+ // in case of multi selection, deselect all and use the cursor position
+ ScRange aDummy;
+ if ( rViewData.GetSimpleArea(aDummy) != SC_MARK_SIMPLE )
+ pView->Unmark();
+
+ ScDocumentUniquePtr pBrushDoc(new ScDocument( SCDOCMODE_CLIP ));
+ pView->CopyToClip( pBrushDoc.get(), false, true );
+ pView->SetBrushDocument( std::move(pBrushDoc), bLock );
+ }
+}
+
+void ScFormatShell::StateFormatPaintbrush( SfxItemSet& rSet )
+{
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
+ rSet.DisableItem( SID_FORMATPAINTBRUSH );
+ else
+ rSet.Put( SfxBoolItem( SID_FORMATPAINTBRUSH, rViewData.GetView()->HasPaintBrush() ) );
+}
+
+SvNumFormatType ScFormatShell::GetCurrentNumberFormatType()
+{
+ SvNumFormatType nType = SvNumFormatType::ALL;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData aMark(GetViewData().GetMarkData());
+ const SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ if (!pFormatter)
+ return nType;
+
+ // TODO: Find out how to get a selected table range in case multiple tables
+ // are selected. Currently we only check for the current active table.
+
+ if ( aMark.IsMarked() || aMark.IsMultiMarked() )
+ {
+ aMark.MarkToMulti();
+ const ScRange& aRange = aMark.GetMultiMarkArea();
+ const ScMultiSel& rMultiSel = aMark.GetMultiSelData();
+
+ SvNumFormatType nComboType = SvNumFormatType::ALL;
+ bool bFirstItem = true;
+ for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
+ {
+ if (!rMultiSel.HasMarks(nCol))
+ continue;
+
+ SCROW nRow1, nRow2;
+ ScMultiSelIter aMultiIter(rMultiSel, nCol);
+ while (aMultiIter.Next(nRow1, nRow2))
+ {
+ ScRange aColRange(nCol, nRow1, aRange.aStart.Tab());
+ aColRange.aEnd.SetRow(nRow2);
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat(aColRange);
+ SvNumFormatType nThisType = pFormatter->GetType(nNumFmt);
+ if (bFirstItem)
+ {
+ bFirstItem = false;
+ nComboType = nThisType;
+ }
+ else if (nComboType != nThisType)
+ // mixed number format type.
+ return SvNumFormatType::ALL;
+ }
+ }
+ nType = nComboType;
+ }
+ else
+ {
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ nType = pFormatter->GetType( nNumFmt );
+ }
+ return nType;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridmerg.cxx b/sc/source/ui/view/gridmerg.cxx
new file mode 100644
index 0000000000..117b3e1ad7
--- /dev/null
+++ b/sc/source/ui/view/gridmerg.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <vcl/lineinfo.hxx>
+#include <vcl/outdev.hxx>
+
+#include <gridmerg.hxx>
+
+#define PAGEBREAK_LINE_DISTANCE_PIXEL 5
+#define PAGEBREAK_LINE_DASH_LEN_PIXEL 5
+#define PAGEBREAK_LINE_DASH_COUNT 1
+
+ScGridMerger::ScGridMerger( OutputDevice* pOutDev, tools::Long nOnePixelX, tools::Long nOnePixelY )
+ : pDev(pOutDev)
+ , nOneX(nOnePixelX)
+ , nOneY(nOnePixelY)
+ , nFixStart(0)
+ , nFixEnd(0)
+ , nVarStart(0)
+ , nVarDiff(0)
+ , nCount(0)
+ , bVertical(false)
+{
+ // optimize (DrawGrid) only for pixel MapMode,
+ // to avoid rounding errors
+
+ bOptimize = ( pDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel );
+}
+
+ScGridMerger::~ScGridMerger()
+{
+ Flush();
+}
+
+void ScGridMerger::AddLine( tools::Long nStart, tools::Long nEnd, tools::Long nPos )
+{
+ if ( nCount )
+ {
+ // not first line - test fix position
+ // more than one previous line - test distance
+
+ if ( nStart != nFixStart || nEnd != nFixEnd )
+ {
+ if ( nCount == 1 && nPos == nVarStart &&
+ ( nStart == nFixEnd ||
+ nStart == nFixEnd + ( bVertical ? nOneY : nOneX ) ) )
+ {
+ // additional optimization: extend connected lines
+ // keep nCount at 1
+ nFixEnd = nEnd;
+ }
+ else
+ Flush();
+ }
+ else if ( nCount == 1 )
+ {
+ nVarDiff = nPos - nVarStart;
+ ++nCount;
+ }
+ else if ( nPos != nVarStart + nCount * nVarDiff ) //! keep VarEnd?
+ Flush();
+ else
+ ++nCount;
+ }
+
+ if ( !nCount )
+ {
+ // first line (or flushed above) - just store
+
+ nFixStart = nStart;
+ nFixEnd = nEnd;
+ nVarStart = nPos;
+ nVarDiff = 0;
+ nCount = 1;
+ }
+}
+
+void ScGridMerger::AddHorLine(bool bWorksInPixels, tools::Long nX1, tools::Long nX2, tools::Long nY, bool bDashed)
+{
+ if ( bWorksInPixels )
+ {
+ Point aPoint(pDev->PixelToLogic(Point(nX1, nY)));
+ nX1 = aPoint.X();
+ nY = aPoint.Y();
+ nX2 = pDev->PixelToLogic(Point(nX2, 0)).X();
+ }
+
+ if ( bDashed )
+ {
+ // If there are some unflushed lines they must be flushed since
+ // new line is of different style
+ if (bOptimize) {
+ Flush();
+ bVertical = false;
+ }
+
+ LineInfo aLineInfo(LineStyle::Dash, 1);
+ aLineInfo.SetDashCount( PAGEBREAK_LINE_DASH_COUNT );
+
+ // Calculating logic values of DashLen and Distance from fixed pixel values
+ Size aDashDistanceLen( pDev->PixelToLogic( Size( PAGEBREAK_LINE_DISTANCE_PIXEL,
+ PAGEBREAK_LINE_DASH_LEN_PIXEL )));
+
+ aLineInfo.SetDistance( aDashDistanceLen.Width() );
+ aLineInfo.SetDashLen( aDashDistanceLen.Height() );
+
+ pDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ), aLineInfo );
+ }
+ else if ( bOptimize )
+ {
+ if ( bVertical )
+ {
+ Flush();
+ bVertical = false;
+ }
+ AddLine( nX1, nX2, nY );
+ }
+ else
+ pDev->DrawLine( Point( nX1, nY ), Point( nX2, nY ) );
+}
+
+void ScGridMerger::AddVerLine(bool bWorksInPixels, tools::Long nX, tools::Long nY1, tools::Long nY2, bool bDashed)
+{
+ if (bWorksInPixels)
+ {
+ Point aPoint(pDev->PixelToLogic(Point(nX, nY1)));
+ nX = aPoint.X();
+ nY1 = aPoint.Y();
+ nY2 = pDev->PixelToLogic(Point(0, nY2)).Y();
+ }
+
+ if ( bDashed )
+ {
+ // If there are some unflushed lines they must be flushed since
+ // new line is of different style
+ if (bOptimize) {
+ Flush();
+ bVertical = false;
+ }
+
+ LineInfo aLineInfo(LineStyle::Dash, 1);
+ aLineInfo.SetDashCount( PAGEBREAK_LINE_DASH_COUNT );
+
+ // Calculating logic values of DashLen and Distance from fixed pixel values
+ Size aDashDistanceLen( pDev->PixelToLogic( Size( PAGEBREAK_LINE_DISTANCE_PIXEL,
+ PAGEBREAK_LINE_DASH_LEN_PIXEL )));
+
+ aLineInfo.SetDistance( aDashDistanceLen.Width() );
+ aLineInfo.SetDashLen( aDashDistanceLen.Height() );
+
+ pDev->DrawLine( Point( nX, nY1 ), Point( nX, nY2 ), aLineInfo);
+ }
+ else if ( bOptimize )
+ {
+ if ( !bVertical )
+ {
+ Flush();
+ bVertical = true;
+ }
+ AddLine( nY1, nY2, nX );
+ }
+ else
+ pDev->DrawLine( Point( nX, nY1 ), Point( nX, nY2 ) );
+}
+
+void ScGridMerger::Flush()
+{
+ if (!nCount)
+ return;
+
+ if (bVertical)
+ {
+ if ( nCount == 1 )
+ pDev->DrawLine( Point( nVarStart, nFixStart ), Point( nVarStart, nFixEnd ) );
+ else
+ {
+ tools::Long nVarEnd = nVarStart + ( nCount - 1 ) * nVarDiff;
+ if ( nVarDiff < 0 )
+ {
+ // nVarDiff is negative in RTL layout mode
+ // Change the positions so DrawGrid is called with a positive distance
+ // (nVarStart / nVarDiff can be modified, aren't used after Flush)
+
+ nVarDiff = -nVarDiff;
+ std::swap( nVarStart, nVarEnd );
+ }
+ pDev->DrawGrid( tools::Rectangle( nVarStart, nFixStart, nVarEnd, nFixEnd ),
+ Size( nVarDiff, nFixEnd - nFixStart ),
+ DrawGridFlags::VertLines );
+ }
+ }
+ else
+ {
+ if ( nCount == 1 )
+ pDev->DrawLine( Point( nFixStart, nVarStart ), Point( nFixEnd, nVarStart ) );
+ else
+ {
+ tools::Long nVarEnd = nVarStart + ( nCount - 1 ) * nVarDiff;
+ pDev->DrawGrid( tools::Rectangle( nFixStart, nVarStart, nFixEnd, nVarEnd ),
+ Size( nFixEnd - nFixStart, nVarDiff ),
+ DrawGridFlags::HorzLines );
+ }
+ }
+ nCount = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
new file mode 100644
index 0000000000..3f4f6b219c
--- /dev/null
+++ b/sc/source/ui/view/gridwin.cxx
@@ -0,0 +1,7218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <cstdlib>
+#include <memory>
+#include <editeng/adjustitem.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <sot/storage.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/misspellrange.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/ipclient.hxx>
+#include <svl/stritem.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/dialoghelper.hxx>
+#include <vcl/inputctx.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+#include <sot/formats.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdpagv.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+#include <com/sun/star/sheet/MemberResultFlags.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/script/vba/VBAEventId.hpp>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/text/textfield/Type.hpp>
+
+#include <gridwin.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <tabview.hxx>
+#include <select.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <dbdata.hxx>
+#include <stlpool.hxx>
+#include <printfun.hxx>
+#include <cbutton.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <editutil.hxx>
+#include <scresid.hxx>
+#include <inputhdl.hxx>
+#include <uiitems.hxx>
+#include <formulacell.hxx>
+#include <patattr.hxx>
+#include <notemark.hxx>
+#include <rfindlst.hxx>
+#include <output.hxx>
+#include <docfunc.hxx>
+#include <dbdocfun.hxx>
+#include <dpobject.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <seltrans.hxx>
+#include <sizedev.hxx>
+#include <AccessibilityHints.hxx>
+#include <dpsave.hxx>
+#include <viewuno.hxx>
+#include <compiler.hxx>
+#include <editable.hxx>
+#include <fillinfo.hxx>
+#include <filterentries.hxx>
+#include <drwlayer.hxx>
+#include <validat.hxx>
+#include <tabprotection.hxx>
+#include <postit.hxx>
+#include <dpcontrol.hxx>
+#include <checklistmenu.hxx>
+#include <clipparam.hxx>
+#include <overlayobject.hxx>
+#include <cellsuno.hxx>
+#include <drawview.hxx>
+#include <dragdata.hxx>
+#include <cliputil.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <externalrefmgr.hxx>
+#include <spellcheckcontext.hxx>
+#include <uiobject.hxx>
+#include <undoblk.hxx>
+#include <datamapper.hxx>
+#include <inputopt.hxx>
+#include <queryparam.hxx>
+#include <SparklineList.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <svx/PaletteManager.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <vector>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <FilterListBox.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+struct ScGridWindow::MouseEventState
+{
+ bool mbActivatePart;
+
+ MouseEventState() :
+ mbActivatePart(false)
+ {}
+};
+
+#define SC_FILTERLISTBOX_LINES 12
+
+ScGridWindow::VisibleRange::VisibleRange(const ScDocument& rDoc)
+ : mnCol1(0)
+ , mnCol2(rDoc.MaxCol())
+ , mnRow1(0)
+ , mnRow2(rDoc.MaxRow())
+{
+}
+
+bool ScGridWindow::VisibleRange::isInside(SCCOL nCol, SCROW nRow) const
+{
+ return mnCol1 <= nCol && nCol <= mnCol2 && mnRow1 <= nRow && nRow <= mnRow2;
+}
+
+bool ScGridWindow::VisibleRange::set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ bool bChanged = mnCol1 != nCol1 || mnRow1 != nRow1 || mnCol2 != nCol2 || mnRow2 != nRow2;
+
+ mnCol1 = nCol1;
+ mnRow1 = nRow1;
+ mnCol2 = nCol2;
+ mnRow2 = nRow2;
+
+ return bChanged;
+}
+
+// ListBox in a FloatingWindow (pParent)
+ScFilterListBox::ScFilterListBox(weld::Window* pParent, ScGridWindow* pGrid,
+ SCCOL nNewCol, SCROW nNewRow, ScFilterBoxMode eNewMode)
+ : xBuilder(Application::CreateBuilder(pParent, "modules/scalc/ui/filterlist.ui"))
+ , xPopover(xBuilder->weld_popover("FilterList"))
+ , xTreeView(xBuilder->weld_tree_view("list"))
+ , pGridWin(pGrid)
+ , nCol(nNewCol)
+ , nRow(nNewRow)
+ , bInit(true)
+ , bCancelled(false)
+ , bGridHadMouseCaptured(pGrid->IsMouseCaptured())
+ , nSel(0)
+ , eMode(eNewMode)
+ , nAsyncSelectHdl(nullptr)
+{
+ xTreeView->connect_row_activated(LINK(this, ScFilterListBox, SelectHdl));
+ xTreeView->connect_key_press(LINK(this, ScFilterListBox, KeyInputHdl));
+}
+
+ScFilterListBox::~ScFilterListBox()
+{
+ if (nAsyncSelectHdl)
+ {
+ Application::RemoveUserEvent(nAsyncSelectHdl);
+ nAsyncSelectHdl = nullptr;
+ }
+}
+
+void ScFilterListBox::EndInit()
+{
+ sal_Int32 nPos = xTreeView->get_selected_index();
+ if (nPos == -1)
+ nSel = 0;
+ else
+ nSel = nPos;
+
+ bInit = false;
+}
+
+IMPL_LINK(ScFilterListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ bool bDone = false;
+
+ vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
+ // esc with no modifiers
+ if (!aCode.GetModifier() && aCode.GetCode() == KEY_ESCAPE)
+ {
+ pGridWin->ClickExtern(); // clears the listbox
+ bDone = true;
+ }
+
+ // nowhere to tab to
+ if (aCode.GetCode() == KEY_TAB)
+ bDone = true;
+
+ return bDone;
+}
+
+IMPL_LINK_NOARG(ScFilterListBox, SelectHdl, weld::TreeView&, bool)
+{
+ if (!bInit && !bCancelled && !nAsyncSelectHdl)
+ {
+ int nPos = xTreeView->get_selected_index();
+ if (nPos != -1)
+ {
+ nSel = nPos;
+ // #i81298# launch async so the box isn't deleted from modifications within FilterSelect
+ nAsyncSelectHdl = Application::PostUserEvent(LINK(this, ScFilterListBox, AsyncSelectHdl));
+ }
+ }
+ return true;
+}
+
+IMPL_LINK_NOARG(ScFilterListBox, AsyncSelectHdl, void*, void)
+{
+ nAsyncSelectHdl = nullptr;
+
+ //tdf#133971 hold self-ref until we return
+ auto xThis(shared_from_this());
+ pGridWin->FilterSelect(nSel);
+ if (xThis.use_count() == 1)
+ {
+ // tdf#133855 we got disposed by FilterSelect
+ return;
+ }
+ pGridWin->ClickExtern();
+}
+
+static bool lcl_IsEditableMatrix( ScDocument& rDoc, const ScRange& rRange )
+{
+ // If it is an editable range and if there is a Matrix cell at the bottom right with an
+ // origin top left then the range will be set to contain the exact matrix.
+ //! Extract the MatrixEdges functions directly from the column ???
+ if ( !rDoc.IsBlockEditable( rRange.aStart.Tab(), rRange.aStart.Col(),rRange.aStart.Row(),
+ rRange.aEnd.Col(),rRange.aEnd.Row() ) )
+ return false;
+
+ ScRefCellValue aCell(rDoc, rRange.aEnd);
+ ScAddress aPos;
+ return (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetMatrixOrigin(rDoc, aPos) && aPos == rRange.aStart);
+}
+
+static void lcl_UnLockComment( ScDrawView* pView, const Point& rPos, const ScViewData& rViewData )
+{
+ if (!pView)
+ return;
+
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScAddress aCellPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+ ScPostIt* pNote = rDoc.GetNote( aCellPos );
+ SdrObject* pObj = pNote ? pNote->GetCaption() : nullptr;
+ if( pObj && pObj->GetLogicRect().Contains( rPos ) && ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ const ScProtectionAttr* pProtAttr = rDoc.GetAttr( aCellPos, ATTR_PROTECTION );
+ bool bProtectAttr = pProtAttr->GetProtection() || pProtAttr->GetHideCell() ;
+ bool bProtectDoc = rDoc.IsTabProtected( aCellPos.Tab() ) || rViewData.GetSfxDocShell()->IsReadOnly() ;
+ // unlock internal layer (if not protected), will be relocked in ScDrawView::MarkListHasChanged()
+ pView->LockInternalLayer( bProtectDoc && bProtectAttr );
+ }
+}
+
+static bool lcl_GetHyperlinkCell(
+ ScDocument& rDoc, SCCOL& rPosX, SCROW nPosY, SCTAB nTab, ScRefCellValue& rCell, OUString& rURL )
+{
+ bool bFound = false;
+ do
+ {
+ ScAddress aPos(rPosX, nPosY, nTab);
+ rCell.assign(rDoc, aPos);
+ if (rCell.isEmpty())
+ {
+ if ( rPosX <= 0 )
+ return false; // everything empty to the links
+ else
+ --rPosX; // continue search
+ }
+ else
+ {
+ const ScPatternAttr* pPattern = rDoc.GetPattern(aPos);
+ if ( !pPattern->GetItem(ATTR_HYPERLINK).GetValue().isEmpty() )
+ {
+ rURL = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
+ bFound = true;
+ }
+ else if (rCell.getType() == CELLTYPE_EDIT)
+ bFound = true;
+ else if (rCell.getType() == CELLTYPE_FORMULA && rCell.getFormula()->IsHyperLinkCell())
+ bFound = true;
+ else
+ return false; // other cell
+ }
+ }
+ while ( !bFound );
+
+ return bFound;
+}
+
+static void lcl_GetMirror(Point& rPoint, tools::Rectangle& rRect, const tools::Long nWidth)
+{
+ tools::Long nLeft = rRect.Left();
+ tools::Long nRight = rRect.Right();
+ tools::Long nMirrorPX = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px);
+ tools::Long nMirrorMM = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);
+
+ rPoint.setX(nMirrorPX - rPoint.X());
+ rRect.SetLeft(nMirrorMM - nRight);
+ rRect.SetRight(nMirrorMM - nLeft);
+}
+
+// WB_DIALOGCONTROL needed for UNO-Controls
+ScGridWindow::ScGridWindow( vcl::Window* pParent, ScViewData& rData, ScSplitPos eWhichPos )
+: DocWindow( pParent, WB_CLIPCHILDREN | WB_DIALOGCONTROL ),
+ DropTargetHelper( this ),
+ DragSourceHelper( this ),
+ maVisibleRange(rData.GetDocument()),
+ mrViewData( rData ),
+ eWhich( eWhichPos ),
+ nCursorHideCount( 0 ),
+ nButtonDown( 0 ),
+ nMouseStatus( SC_GM_NONE ),
+ nNestedButtonState( ScNestedButtonState::NONE ),
+ nDPField( 0 ),
+ pDragDPObj( nullptr ),
+ nRFIndex( 0 ),
+ nRFAddX( 0 ),
+ nRFAddY( 0 ),
+ nPagebreakMouse( SC_PD_NONE ),
+ nPagebreakBreak( 0 ),
+ nPagebreakPrev( 0 ),
+ nPageScript( SvtScriptType::NONE ),
+ nDragStartX( -1 ),
+ nDragStartY( -1 ),
+ nDragEndX( -1 ),
+ nDragEndY( -1 ),
+ meDragInsertMode( INS_NONE ),
+ aComboButton( GetOutDev() ),
+ aCurMousePos( 0,0 ),
+ nPaintCount( 0 ),
+ aRFSelectedCorned( NONE ),
+ maShowPageBreaksTimer("ScGridWindow maShowPageBreaksTimer"),
+ bEEMouse( false ),
+ bDPMouse( false ),
+ bRFMouse( false ),
+ bRFSize( false ),
+ bPagebreakDrawn( false ),
+ bDragRect( false ),
+ bIsInPaint( false ),
+ bNeedsRepaint( false ),
+ bAutoMarkVisible( false ),
+ bListValButton( false ),
+ m_nDownPosX( -1 ),
+ m_nDownPosY( -1 )
+{
+ set_id("grid_window");
+ switch(eWhich)
+ {
+ case SC_SPLIT_TOPLEFT:
+ eHWhich = SC_SPLIT_LEFT;
+ eVWhich = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_TOPRIGHT:
+ eHWhich = SC_SPLIT_RIGHT;
+ eVWhich = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_BOTTOMLEFT:
+ eHWhich = SC_SPLIT_LEFT;
+ eVWhich = SC_SPLIT_BOTTOM;
+ break;
+ case SC_SPLIT_BOTTOMRIGHT:
+ eHWhich = SC_SPLIT_RIGHT;
+ eVWhich = SC_SPLIT_BOTTOM;
+ break;
+ default:
+ OSL_FAIL("GridWindow: wrong position");
+ }
+
+ SetUseFrameData(comphelper::LibreOfficeKit::isActive());
+ SetBackground();
+
+ SetMapMode(mrViewData.GetLogicMode(eWhich));
+ EnableChildTransparentMode();
+ SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );
+
+ SetHelpId( HID_SC_WIN_GRIDWIN );
+
+ GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+ EnableRTL( false );
+
+ bInitialPageBreaks = true;
+ maShowPageBreaksTimer.SetInvokeHandler(LINK(this, ScGridWindow, InitiatePageBreaksTimer));
+ maShowPageBreaksTimer.SetTimeout(1);
+}
+
+ScGridWindow::~ScGridWindow()
+{
+ disposeOnce();
+}
+
+void ScGridWindow::dispose()
+{
+ maShowPageBreaksTimer.Stop();
+
+ ImpDestroyOverlayObjects();
+
+ mpFilterBox.reset();
+ mpNoteMarker.reset();
+ mpAutoFilterPopup.reset();
+ mpDPFieldPopup.reset();
+ aComboButton.SetOutputDevice(nullptr);
+
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->reset();
+ mpSpellCheckCxt.reset();
+
+ vcl::Window::dispose();
+}
+
+void ScGridWindow::ClickExtern()
+{
+ do
+ {
+ // #i84277# when initializing the filter box, a Basic error can deactivate the view
+ if (mpFilterBox && mpFilterBox->IsInInit())
+ break;
+ mpFilterBox.reset();
+ }
+ while (false);
+
+ if (mpDPFieldPopup)
+ {
+ mpDPFieldPopup->close(false);
+ mpDPFieldPopup.reset();
+ }
+}
+
+IMPL_LINK_NOARG(ScGridWindow, PopupModeEndHdl, weld::Popover&, void)
+{
+ if (mpFilterBox)
+ {
+ bool bMouseWasCaptured = mpFilterBox->MouseWasCaptured();
+ mpFilterBox->SetCancelled(); // cancel select
+ // restore the mouse capture state of the GridWindow to
+ // what it was at initial popup time
+ SAL_WARN_IF(bMouseWasCaptured, "sc.ui", "Is there a scenario where the mouse was captured before mouse down?");
+ if (bMouseWasCaptured)
+ CaptureMouse();
+ }
+ GrabFocus();
+}
+
+IMPL_LINK( ScGridWindow, PopupSpellingHdl, SpellCallbackInfo&, rInfo, void )
+{
+ if( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
+ mrViewData.GetDispatcher().Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON );
+ else if (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS)
+ mrViewData.GetDispatcher().Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON );
+ else //IGNOREWORD, ADDTODICTIONARY, WORDLANGUAGE, PARALANGUAGE
+ {
+ // The spelling status of the word has changed. Close the cell to reset the caches
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ pHdl->EnterHandler();
+ }
+}
+
+namespace {
+
+struct AutoFilterData : public ScCheckListMenuControl::ExtendedData
+{
+ ScAddress maPos;
+ ScDBData* mpData;
+};
+
+class AutoFilterAction : public ScCheckListMenuControl::Action
+{
+protected:
+ VclPtr<ScGridWindow> mpWindow;
+ ScGridWindow::AutoFilterMode meMode;
+public:
+ AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
+ mpWindow(p), meMode(eMode) {}
+ virtual bool execute() override
+ {
+ mpWindow->UpdateAutoFilterFromMenu(meMode);
+ // UpdateAutoFilterFromMenu manually closes the popup so return
+ // false to not attempt a second close
+ return false;
+ }
+};
+
+class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
+{
+ VclPtr<ScGridWindow> mpWindow;
+ ScAddress maPos;
+public:
+ AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
+ mpWindow(p), maPos(rPos) {}
+ virtual bool execute() override
+ {
+ mpWindow->RefreshAutoFilterButton(maPos);
+ mpWindow->GrabFocus();
+ return false; // this is called after the popup has been closed
+ }
+};
+
+class AutoFilterSubMenuAction : public AutoFilterAction
+{
+protected:
+ ScListSubMenuControl* m_pSubMenu;
+
+public:
+ AutoFilterSubMenuAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode)
+ : AutoFilterAction(p, eMode)
+ , m_pSubMenu(pSubMenu)
+ {
+ }
+};
+
+class AutoFilterColorAction : public AutoFilterSubMenuAction
+{
+private:
+ Color m_aColor;
+
+public:
+ AutoFilterColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor)
+ : AutoFilterSubMenuAction(p, pSubMenu, eMode)
+ , m_aColor(rColor)
+ {
+ }
+
+ virtual bool execute() override
+ {
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
+
+ if (!pData)
+ return false;
+
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return false;
+
+ const ScAddress& rPos = pData->maPos;
+
+ ScViewData& rViewData = m_pSubMenu->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+
+ // Try to use the existing entry for the column (if one exists).
+ ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
+
+ if (!pEntry)
+ {
+ // Something went terribly wrong!
+ return false;
+ }
+
+ if (ScTabViewShell::isAnyEditViewInRange(rViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
+ return false;
+
+ pEntry->bDoQuery = true;
+ pEntry->nField = rPos.Col();
+ pEntry->eConnect = SC_AND;
+
+ ScFilterEntries aFilterEntries;
+ rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
+
+ bool bActive = false;
+ auto aItem = pEntry->GetQueryItem();
+ if (aItem.maColor == m_aColor
+ && ((meMode == ScGridWindow::AutoFilterMode::TextColor
+ && aItem.meType == ScQueryEntry::ByTextColor)
+ || (meMode == ScGridWindow::AutoFilterMode::BackgroundColor
+ && aItem.meType == ScQueryEntry::ByBackgroundColor)))
+ {
+ bActive = true;
+ }
+
+ // Disable color filter when active color was selected
+ if (bActive)
+ {
+ aParam.RemoveAllEntriesByField(rPos.Col());
+ pEntry = nullptr; // invalidated by RemoveAllEntriesByField call
+
+ // tdf#46184 reset filter options to default values
+ aParam.eSearchType = utl::SearchParam::SearchType::Normal;
+ aParam.bCaseSens = false;
+ aParam.bDuplicate = true;
+ aParam.bInplace = true;
+ }
+ else
+ {
+ if (meMode == ScGridWindow::AutoFilterMode::TextColor)
+ pEntry->SetQueryByTextColor(m_aColor);
+ else
+ pEntry->SetQueryByBackgroundColor(m_aColor);
+ }
+
+ rViewData.GetView()->Query(aParam, nullptr, true);
+ pDBData->SetQueryParam(aParam);
+
+ return true;
+ }
+};
+
+class AutoFilterSortColorAction : public AutoFilterSubMenuAction
+{
+private:
+ Color m_aColor;
+ ScViewData& m_rViewData;
+
+public:
+ AutoFilterSortColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor, ScViewData& rViewData)
+ : AutoFilterSubMenuAction(p, pSubMenu, eMode)
+ , m_aColor(rColor)
+ , m_rViewData(rViewData)
+ {
+ }
+
+ virtual bool execute() override
+ {
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
+
+ if (!pData)
+ return false;
+
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return false;
+
+ const ScAddress& rPos = pData->maPos;
+ SCCOL nCol = rPos.Col();
+ ScSortParam aSortParam;
+ pDBData->GetSortParam(aSortParam);
+ if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
+ // out of bound
+ return false;
+
+ bool bHasHeader = pDBData->HasHeader();
+
+ aSortParam.bHasHeader = bHasHeader;
+ aSortParam.bByRow = true;
+ aSortParam.bCaseSens = false;
+ aSortParam.bNaturalSort = false;
+ aSortParam.aDataAreaExtras.mbCellNotes = false;
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
+ aSortParam.aDataAreaExtras.mbCellFormats = true;
+ aSortParam.bInplace = true;
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = nCol;
+ aSortParam.maKeyState[0].bAscending = true;
+ aSortParam.maKeyState[0].aColorSortMode = meMode == ScGridWindow::AutoFilterMode::TextColor
+ ? ScColorSortMode::TextColor
+ : ScColorSortMode::BackgroundColor;
+ aSortParam.maKeyState[0].aColorSortColor = m_aColor;
+
+ for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
+ aSortParam.maKeyState[i].bDoSort = false;
+
+ m_rViewData.GetViewShell()->UISort(aSortParam);
+
+ return true;
+ }
+};
+
+class AutoFilterColorPopupStartAction : public AutoFilterSubMenuAction
+{
+private:
+ bool mbIsFilter;
+public:
+ AutoFilterColorPopupStartAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, bool bIsFilter)
+ : AutoFilterSubMenuAction(p, pSubMenu, ScGridWindow::AutoFilterMode::Normal),
+ mbIsFilter(bIsFilter)
+ {
+ }
+
+ virtual bool execute() override
+ {
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());
+
+ if (!pData)
+ return false;
+
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return false;
+
+ ScViewData& rViewData = m_pSubMenu->GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const ScAddress& rPos = pData->maPos;
+
+ ScFilterEntries aFilterEntries;
+ rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);
+
+ m_pSubMenu->clearMenuItems();
+
+ XColorListRef xUserColorList;
+
+ OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
+ PaletteManager aPaletteManager;
+ std::vector<OUString> aPaletteNames = aPaletteManager.GetPaletteList();
+ for (size_t i = 0, nLen = aPaletteNames.size(); i < nLen; ++i)
+ {
+ if (aPaletteName == aPaletteNames[i])
+ {
+ aPaletteManager.SetPalette(i);
+ xUserColorList = XPropertyList::AsColorList(
+ XPropertyList::CreatePropertyListFromURL(
+ XPropertyListType::Color, aPaletteManager.GetSelectedPalettePath()));
+ if (!xUserColorList->Load())
+ xUserColorList = nullptr;
+ break;
+ }
+ }
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+ ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
+
+ int nMenu = 0;
+ for (auto eMode : {ScGridWindow::AutoFilterMode::BackgroundColor, ScGridWindow::AutoFilterMode::TextColor})
+ {
+ std::set<Color> aColors = eMode == ScGridWindow::AutoFilterMode::TextColor
+ ? aFilterEntries.getTextColors()
+ : aFilterEntries.getBackgroundColors();
+
+ for (auto& rColor : aColors)
+ {
+ bool bActive = false;
+
+ if (pEntry)
+ {
+ auto aItem = pEntry->GetQueryItem();
+ if (aItem.maColor == rColor
+ && ((eMode == ScGridWindow::AutoFilterMode::TextColor
+ && aItem.meType == ScQueryEntry::ByTextColor)
+ || (eMode == ScGridWindow::AutoFilterMode::BackgroundColor
+ && aItem.meType == ScQueryEntry::ByBackgroundColor)))
+ {
+ bActive = true;
+ }
+ }
+
+ const bool bAutoColor = rColor == COL_AUTO;
+
+ // ColorListBox::ShowPreview is similar
+ ScopedVclPtr<VirtualDevice> xDev(m_pSubMenu->create_virtual_device());
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+ xDev->SetOutputSize(aImageSize);
+ const tools::Rectangle aRect(Point(0, 0), aImageSize);
+
+ if (bAutoColor)
+ {
+ const Color aW(COL_WHITE);
+ const Color aG(0xef, 0xef, 0xef);
+ int nMinDim = std::min(aImageSize.Width(), aImageSize.Height()) + 1;
+ int nCheckSize = nMinDim / 3;
+ xDev->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), std::min(nCheckSize, 8), aW, aG);
+ xDev->SetFillColor();
+ }
+ else
+ xDev->SetFillColor(rColor);
+
+ xDev->SetLineColor(rStyleSettings.GetDisableColor());
+ xDev->DrawRect(aRect);
+
+ if (bAutoColor)
+ {
+ OUString sText = eMode == ScGridWindow::AutoFilterMode::TextColor
+ ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
+ : ScResId(SCSTR_FILTER_NO_FILL);
+ if (mbIsFilter)
+ {
+ m_pSubMenu->addMenuColorItem(
+ sText, bActive, *xDev, nMenu,
+ new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
+ }
+ else
+ {
+ m_pSubMenu->addMenuColorItem(
+ sText, bActive, *xDev, nMenu,
+ new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor, rViewData));
+ }
+ }
+ else
+ {
+ OUString sName;
+
+ bool bFoundColorName = false;
+ if (xUserColorList)
+ {
+ sal_Int32 nPos = xUserColorList->GetIndexOfColor(rColor);
+ if (nPos != -1)
+ {
+ XColorEntry* pColorEntry = xUserColorList->GetColor(nPos);
+ sName = pColorEntry->GetName();
+ bFoundColorName = true;
+ }
+ }
+ if (!bFoundColorName)
+ sName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
+
+ if (mbIsFilter)
+ {
+ m_pSubMenu->addMenuColorItem(
+ sName, bActive, *xDev, nMenu,
+ new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
+ }
+ else
+ {
+ m_pSubMenu->addMenuColorItem(
+ sName, bActive, *xDev, nMenu,
+ new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor,
+ rViewData));
+ }
+ }
+ }
+
+ ++nMenu;
+ }
+
+ m_pSubMenu->resizeToFitMenuItems();
+
+ return false;
+ }
+};
+
+class AddItemToEntry
+{
+ ScQueryEntry::QueryItemsType& mrItems;
+ svl::SharedStringPool& mrPool;
+public:
+ AddItemToEntry(ScQueryEntry::QueryItemsType& rItems, svl::SharedStringPool& rPool) :
+ mrItems(rItems), mrPool(rPool) {}
+ void operator() (const ScCheckListMenuControl::ResultEntry& rEntry)
+ {
+ if (rEntry.bValid)
+ {
+ ScQueryEntry::Item aNew;
+ aNew.maString = mrPool.intern(rEntry.aName);
+ // set the filter type to ByValue, if the filter condition is value
+ aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : rEntry.bValue ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
+ aNew.mfVal = rEntry.nValue;
+ mrItems.push_back(aNew);
+ }
+ }
+};
+
+class AddSelectedItemString
+{
+ std::unordered_set<OUString>& mrSetString;
+ std::unordered_set<double>& mrSetValue;
+public:
+ explicit AddSelectedItemString(std::unordered_set<OUString>& rString, std::unordered_set<double>& rValue) :
+ mrSetString(rString), mrSetValue(rValue) {}
+
+ void operator() (const ScQueryEntry::Item& rItem)
+ {
+ if( rItem.meType == ScQueryEntry::QueryType::ByValue )
+ mrSetValue.insert(rItem.mfVal);
+ else
+ mrSetString.insert(rItem.maString.getString());
+ }
+};
+
+void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUString& aevent)
+{
+ EventDescription aDescription;
+ aDescription.aAction = "LAUNCH";
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{aevent, ""},
+ {"ROW", aRow}, {"COL", aCol}};
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow)
+{
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ mpAutoFilterPopup.reset();
+
+ // Estimate the width (in pixels) of the longest text in the list
+ ScFilterEntries aFilterEntries;
+ rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ int nColWidth = ScViewData::ToPixel(rDoc.GetColWidth(nCol, nTab), mrViewData.GetPPTX());
+ mpAutoFilterPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ aFilterEntries.mbHasDates, nColWidth));
+
+ int nMaxTextWidth = 0;
+ if (aFilterEntries.size() <= 10)
+ {
+ // do pixel calculation for all elements of short lists
+ for (const auto& rEntry : aFilterEntries)
+ {
+ const OUString& aText = rEntry.GetString();
+ nMaxTextWidth = std::max<int>(nMaxTextWidth, mpAutoFilterPopup->GetTextWidth(aText) + aText.getLength() * 2);
+ }
+ }
+ else
+ {
+ // find the longest string, probably it will be the longest rendered text, too
+ // (performance optimization for long lists)
+ auto itMax = aFilterEntries.begin();
+ for (auto it = itMax; it != aFilterEntries.end(); ++it)
+ {
+ int nTextWidth = it->GetString().getLength();
+ if (nMaxTextWidth < nTextWidth)
+ {
+ nMaxTextWidth = nTextWidth;
+ itMax = it;
+ }
+ }
+ nMaxTextWidth = mpAutoFilterPopup->GetTextWidth(itMax->GetString()) + nMaxTextWidth * 2;
+ }
+
+ // window should be at least as wide as the column, or the longest text + checkbox, scrollbar ... (it is estimated with 70 pixel now)
+ // window should be maximum 1024 pixel wide.
+ int nWindowWidth = std::min<int>(1024, nMaxTextWidth + 70);
+ nWindowWidth = mpAutoFilterPopup->IncreaseWindowWidthToFitText(nWindowWidth);
+ nMaxTextWidth = std::max<int>(nMaxTextWidth, nWindowWidth - 70);
+
+ mpAutoFilterPopup->setOKAction(new AutoFilterAction(this, AutoFilterMode::Normal));
+ mpAutoFilterPopup->setPopupEndAction(
+ new AutoFilterPopupEndAction(this, ScAddress(nCol, nRow, nTab)));
+ std::unique_ptr<AutoFilterData> pData(new AutoFilterData);
+ pData->maPos = ScAddress(nCol, nRow, nTab);
+
+ Point aPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
+ if (bLOKActive)
+ {
+ // Reverse the zoom factor from aPos and nSize[X|Y]
+ // before letting the autofilter window convert the to twips
+ // with no zoom information.
+ double fZoomX(mrViewData.GetZoomX());
+ double fZoomY(mrViewData.GetZoomY());
+ aPos.setX(aPos.getX() / fZoomX);
+ aPos.setY(aPos.getY() / fZoomY);
+ nSizeX = nSizeX / fZoomX;
+ nSizeY = nSizeY / fZoomY;
+ }
+ tools::Rectangle aCellRect(bLOKActive ? aPos : OutputToScreenPixel(aPos), Size(nSizeX, nSizeY));
+
+ ScDBData* pDBData = rDoc.GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA);
+ if (!pDBData)
+ return;
+
+ pDBData->ExtendBackColorArea(rDoc);
+ pData->mpData = pDBData;
+ mpAutoFilterPopup->setExtendedData(std::move(pData));
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+ std::vector<ScQueryEntry*> aEntries = aParam.FindAllEntriesByField(nCol);
+ std::unordered_set<OUString> aSelectedString;
+ std::unordered_set<double> aSelectedValue;
+ bool bQueryByNonEmpty = aEntries.size() == 1 && aEntries[0]->IsQueryByNonEmpty();
+
+ if (!bQueryByNonEmpty)
+ {
+ for (ScQueryEntry* pEntry : aEntries)
+ {
+ if (pEntry && pEntry->eOp == SC_EQUAL)
+ {
+ ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
+ std::for_each(rItems.begin(), rItems.end(), AddSelectedItemString(aSelectedString, aSelectedValue));
+ }
+ }
+ }
+
+ // Populate the check box list.
+ mpAutoFilterPopup->setMemberSize(aFilterEntries.size());
+ for (auto it = aFilterEntries.begin(); it != aFilterEntries.end(); ++it)
+ {
+ // tdf#140745 show (empty) entry on top of the checkbox list
+ if (it->GetString().isEmpty())
+ {
+ const OUString& aStringVal = it->GetString();
+ const double aDoubleVal = it->GetValue();
+ bool bSelected = true;
+ if (!aSelectedValue.empty() || !aSelectedString.empty())
+ bSelected = aSelectedString.count(aStringVal) > 0;
+ else if (bQueryByNonEmpty)
+ bSelected = false;
+ mpAutoFilterPopup->addMember(aStringVal, aDoubleVal, bSelected, it->IsHiddenByFilter());
+ aFilterEntries.maStrData.erase(it);
+ break;
+ }
+ }
+ for (const auto& rEntry : aFilterEntries)
+ {
+ const OUString& aStringVal = rEntry.GetString();
+ const double aDoubleVal = rEntry.GetValue();
+ const double aRDoubleVal = rEntry.GetRoundedValue();
+ bool bSelected = !rEntry.IsHiddenByFilter();
+
+ if (!aSelectedValue.empty() || !aSelectedString.empty())
+ {
+ if (rEntry.GetStringType() == ScTypedStrData::Value)
+ {
+ if (aDoubleVal == aRDoubleVal)
+ bSelected = aSelectedValue.count(aDoubleVal) > 0
+ || aSelectedString.count(aStringVal) > 0;
+ else
+ bSelected = aSelectedValue.count(aDoubleVal) > 0
+ || aSelectedValue.count(aRDoubleVal) > 0
+ || aSelectedString.count(aStringVal) > 0;
+ }
+ else
+ bSelected = aSelectedString.count(aStringVal) > 0;
+ }
+
+ if ( rEntry.IsDate() )
+ mpAutoFilterPopup->addDateMember( aStringVal, rEntry.GetValue(), bSelected, rEntry.IsHiddenByFilter());
+ else
+ mpAutoFilterPopup->addMember( aStringVal, aRDoubleVal, bSelected, rEntry.IsHiddenByFilter(),
+ rEntry.GetStringType() == ScTypedStrData::Value );
+ }
+
+ // Populate the menu.
+ mpAutoFilterPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_ASC),
+ new AutoFilterAction(this, AutoFilterMode::SortAscending));
+ mpAutoFilterPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_DESC),
+ new AutoFilterAction(this, AutoFilterMode::SortDescending));
+ if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_SORT_COLOR), true, true))
+ pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, false));
+ mpAutoFilterPopup->addSeparator();
+ if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_COLOR), true, true))
+ pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, true));
+ if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_CONDITION), true, false))
+ {
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_FILTER_EMPTY), new AutoFilterAction(this, AutoFilterMode::Empty));
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_FILTER_NOTEMPTY), new AutoFilterAction(this, AutoFilterMode::NonEmpty));
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_TOP10FILTER), new AutoFilterAction(this, AutoFilterMode::Top10));
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_BOTTOM10FILTER), new AutoFilterAction(this, AutoFilterMode::Bottom10));
+ pSubMenu->addSeparator();
+ pSubMenu->addMenuItem(
+ ScResId(SCSTR_STDFILTER), new AutoFilterAction(this, AutoFilterMode::Custom));
+ pSubMenu->resizeToFitMenuItems();
+ }
+ if (aEntries.size())
+ mpAutoFilterPopup->addMenuItem(
+ ScResId(SCSTR_CLEAR_FILTER), new AutoFilterAction(this, AutoFilterMode::Clear));
+
+ mpAutoFilterPopup->initMembers(nMaxTextWidth + 20); // 20 pixel estimated for the checkbox
+
+ ScCheckListMenuControl::Config aConfig;
+ aConfig.mbAllowEmptySet = false;
+ aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
+ mpAutoFilterPopup->setConfig(aConfig);
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ mpAutoFilterPopup->launch(pPopupParent, aCellRect);
+
+ // remember filter rules before modification
+ mpAutoFilterPopup->getResult(aSaveAutoFilterResult);
+
+ collectUIInformation(OUString::number(nRow), OUString::number(nCol),"AUTOFILTER");
+}
+
+void ScGridWindow::RefreshAutoFilterButton(const ScAddress& rPos)
+{
+ if (mpFilterButton)
+ {
+ bool bFilterActive = IsAutoFilterActive(rPos.Col(), rPos.Row(), rPos.Tab());
+ mpFilterButton->setHasHiddenMember(bFilterActive);
+ mpFilterButton->setPopupPressed(false);
+ mpFilterButton->draw();
+ }
+}
+
+void ScGridWindow::UpdateAutoFilterFromMenu(AutoFilterMode eMode)
+{
+ // Terminate autofilter popup now when there is no further user input needed
+ bool bColorMode = eMode == AutoFilterMode::TextColor || eMode == AutoFilterMode::BackgroundColor;
+ if (!bColorMode)
+ mpAutoFilterPopup->terminateAllPopupMenus();
+
+ const AutoFilterData* pData =
+ static_cast<const AutoFilterData*>(mpAutoFilterPopup->getExtendedData());
+
+ if (!pData)
+ return;
+
+ const ScAddress& rPos = pData->maPos;
+ ScDBData* pDBData = pData->mpData;
+ if (!pDBData)
+ return;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ switch (eMode)
+ {
+ case AutoFilterMode::SortAscending:
+ case AutoFilterMode::SortDescending:
+ {
+ SCCOL nCol = rPos.Col();
+ ScSortParam aSortParam;
+ pDBData->GetSortParam(aSortParam);
+ if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
+ // out of bound
+ return;
+
+ bool bHasHeader = pDBData->HasHeader();
+
+ aSortParam.bHasHeader = bHasHeader;
+ aSortParam.bByRow = true;
+ aSortParam.bCaseSens = false;
+ aSortParam.bNaturalSort = false;
+ aSortParam.aDataAreaExtras.mbCellNotes = false;
+ aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
+ aSortParam.aDataAreaExtras.mbCellFormats = true;
+ aSortParam.bInplace = true;
+ aSortParam.maKeyState[0].bDoSort = true;
+ aSortParam.maKeyState[0].nField = nCol;
+ aSortParam.maKeyState[0].bAscending = (eMode == AutoFilterMode::SortAscending);
+ aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;
+
+ for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
+ aSortParam.maKeyState[i].bDoSort = false;
+
+ mrViewData.GetViewShell()->UISort(aSortParam);
+ return;
+ }
+ case AutoFilterMode::Custom:
+ {
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ mrViewData.GetView()->MarkRange(aRange);
+ mrViewData.GetView()->SetCursor(rPos.Col(), rPos.Row());
+ mrViewData.GetDispatcher().Execute(SID_FILTER, SfxCallMode::SLOT | SfxCallMode::RECORD);
+ return;
+ }
+ default:
+ ;
+ }
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam(aParam);
+
+ if (eMode == AutoFilterMode::Normal)
+ {
+ // Do not recreate autofilter rules if there are no changes from the user
+ ScCheckListMenuControl::ResultType aResult;
+ mpAutoFilterPopup->getResult(aResult);
+
+ if (aResult == aSaveAutoFilterResult)
+ {
+ SAL_INFO("sc.ui", "Apply autofilter to data when entries are the same");
+
+ if (!mpAutoFilterPopup->isAllSelected())
+ {
+ // Apply autofilter to data
+ ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
+ pEntry->bDoQuery = true;
+ pEntry->nField = rPos.Col();
+ pEntry->eConnect = SC_AND;
+ pEntry->eOp = SC_EQUAL;
+ mrViewData.GetView()->Query(aParam, nullptr, true);
+ }
+
+ return;
+ }
+ }
+
+ // Remove old entries in auto-filter rules
+ if (!bColorMode)
+ {
+ aParam.RemoveAllEntriesByField(rPos.Col());
+
+ // tdf#46184 reset filter options to default values
+ aParam.eSearchType = utl::SearchParam::SearchType::Normal;
+ aParam.bCaseSens = false;
+ aParam.bDuplicate = true;
+ aParam.bInplace = true;
+ }
+
+ if (eMode != AutoFilterMode::Clear
+ && !(eMode == AutoFilterMode::Normal && mpAutoFilterPopup->isAllSelected()))
+ {
+ // Try to use the existing entry for the column (if one exists).
+ ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
+
+ if (!pEntry)
+ // Something went terribly wrong!
+ return;
+
+ if (ScTabViewShell::isAnyEditViewInRange(mrViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
+ return;
+
+ pEntry->bDoQuery = true;
+ pEntry->nField = rPos.Col();
+ pEntry->eConnect = SC_AND;
+
+ switch (eMode)
+ {
+ case AutoFilterMode::Normal:
+ {
+ pEntry->eOp = SC_EQUAL;
+
+ ScCheckListMenuControl::ResultType aResult;
+ mpAutoFilterPopup->getResult(aResult);
+
+ ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
+ rItems.clear();
+ std::for_each(aResult.begin(), aResult.end(), AddItemToEntry(rItems, rPool));
+ }
+ break;
+ case AutoFilterMode::Top10:
+ pEntry->eOp = SC_TOPVAL;
+ pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
+ pEntry->GetQueryItem().maString = rPool.intern("10");
+ break;
+ case AutoFilterMode::Bottom10:
+ pEntry->eOp = SC_BOTVAL;
+ pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
+ pEntry->GetQueryItem().maString = rPool.intern("10");
+ break;
+ case AutoFilterMode::Empty:
+ pEntry->SetQueryByEmpty();
+ break;
+ case AutoFilterMode::NonEmpty:
+ pEntry->SetQueryByNonEmpty();
+ break;
+ case AutoFilterMode::TextColor:
+ case AutoFilterMode::BackgroundColor:
+ assert(false && "should be handled by AutoFilterColorAction::execute");
+ break;
+ break;
+ default:
+ // We don't know how to handle this!
+ return;
+ }
+ }
+
+ mrViewData.GetView()->Query(aParam, nullptr, true);
+ pDBData->SetQueryParam(aParam);
+}
+
+namespace {
+
+void getCellGeometry(Point& rScrPos, Size& rScrSize, const ScViewData& rViewData, SCCOL nCol, SCROW nRow, ScSplitPos eWhich)
+{
+ // Get the screen position of the cell.
+ rScrPos = rViewData.GetScrPos(nCol, nRow, eWhich);
+
+ // Get the screen size of the cell.
+ tools::Long nSizeX, nSizeY;
+ rViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
+ rScrSize = Size(nSizeX-1, nSizeY-1);
+}
+
+}
+
+void ScGridWindow::LaunchPageFieldMenu( SCCOL nCol, SCROW nRow )
+{
+ if (nCol == 0)
+ // We assume that the page field button is located in cell to the immediate left.
+ return;
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ Point aScrPos;
+ Size aScrSize;
+ getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+ DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol-1, nRow, nTab), pDPObj);
+}
+
+void ScGridWindow::LaunchDPFieldMenu( SCCOL nCol, SCROW nRow )
+{
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ Point aScrPos;
+ Size aScrSize;
+ getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+ DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol, nRow, nTab), pDPObj);
+}
+
+void ScGridWindow::ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL)
+{
+ auto nSizeX = rCellRect.GetWidth();
+
+ // minimum width in pixel
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ const tools::Long nMinLOKWinWidth = o3tl::convert(STD_COL_WIDTH * 13 / 10, o3tl::Length::twip, o3tl::Length::px);
+ if (nSizeX < nMinLOKWinWidth)
+ nSizeX = nMinLOKWinWidth;
+ }
+
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ int nEntryCount = rFilterBox.n_children();
+ if (nEntryCount > SC_FILTERLISTBOX_LINES)
+ nEntryCount = SC_FILTERLISTBOX_LINES;
+ auto nHeight = rFilterBox.get_height_rows(nEntryCount);
+ rFilterBox.set_size_request(-1, nHeight);
+ Size aSize(rFilterBox.get_preferred_size());
+ auto nMaxToExpandTo = std::min(nSizeX, static_cast<decltype(nSizeX)>(300)); // do not over do it (Pixel)
+ if (aSize.Width() < nMaxToExpandTo)
+ aSize.setWidth(nMaxToExpandTo);
+
+ aSize.AdjustWidth(4); // add a little margin
+ nSizeX += 4;
+ aSize.AdjustHeight(4);
+
+ tools::Rectangle aCellRect(rCellRect);
+ aCellRect.AdjustLeft(-2); // offset the little margin above
+
+ if (!bLayoutRTL && aSize.Width() > nSizeX)
+ {
+ // move popup position
+ tools::Long nDiff = aSize.Width() - nSizeX;
+ tools::Long nNewX = aCellRect.Left() - nDiff;
+ if ( nNewX < 0 )
+ nNewX = 0;
+ aCellRect.SetLeft( nNewX );
+ }
+
+ rFilterBox.set_size_request(aSize.Width(), aSize.Height());
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ mpFilterBox->popup_at_rect(pParent, aCellRect);
+}
+
+void ScGridWindow::DoScenarioMenu( const ScRange& rScenRange )
+{
+ bool bMenuAtTop = true;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ mpFilterBox.reset();
+
+ SCCOL nCol = rScenRange.aEnd.Col(); // Cell is below the Buttons
+ SCROW nRow = rScenRange.aStart.Row();
+ if (nRow == 0)
+ {
+ nRow = rScenRange.aEnd.Row() + 1; // Range at very the top -> Button below
+ if (nRow>rDoc.MaxRow()) nRow = rDoc.MaxRow();
+ bMenuAtTop = false;
+ }
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ // The button height should not use the merged cell height, should still use single row height
+ nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
+ Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ if ( bLayoutRTL )
+ aPos.AdjustX( -nSizeX );
+ tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
+ aCellRect.AdjustTop( -nSizeY );
+ aCellRect.AdjustBottom( -(nSizeY - 1) );
+ if (!bMenuAtTop)
+ {
+ Size aButSize = mrViewData.GetScenButSize();
+ aCellRect.AdjustBottom(aButSize.Height());
+ }
+
+ // Place the ListBox directly below the black line of the cell grid
+ // (It looks odd if the line gets hidden...)
+
+ weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
+ mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::Scenario);
+ mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
+
+ // Listbox fill
+ rFilterBox.freeze();
+ OUString aCurrent;
+ OUString aTabName;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
+ {
+ if (rDoc.HasScenarioRange( i, rScenRange ))
+ if (rDoc.GetName( i, aTabName ))
+ {
+ rFilterBox.append_text(aTabName);
+ if (rDoc.IsActiveScenario(i))
+ aCurrent = aTabName;
+ }
+ }
+ rFilterBox.thaw();
+
+ ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
+
+ rFilterBox.grab_focus();
+
+ sal_Int32 nPos = -1;
+ if (!aCurrent.isEmpty())
+ {
+ nPos = rFilterBox.find_text(aCurrent);
+ }
+ if (nPos == -1 && rFilterBox.n_children() > 0 )
+ {
+ nPos = 0;
+ }
+ if (nPos != -1)
+ {
+ rFilterBox.set_cursor(nPos);
+ rFilterBox.select(nPos);
+ }
+ mpFilterBox->EndInit();
+}
+
+void ScGridWindow::LaunchDataSelectMenu(const SCCOL nCol, const SCROW nRow)
+{
+ mpFilterBox.reset();
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ if (bLOKActive)
+ {
+ // aPos is now view-zoom adjusted and in pixels an more importantly this is pixel aligned to the view-zoom,
+ // but once we use this to set the position of the floating window, it has no information of view-zoom level
+ // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, MapMode(MapUnit::MapTwip)) employed in
+ // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position which will again get zoom scaled in the
+ // client (effective double scaling) causing wrong positioning/size.
+ double fZoomX(mrViewData.GetZoomX());
+ double fZoomY(mrViewData.GetZoomY());
+ aPos.setX(aPos.getX() / fZoomX);
+ aPos.setY(aPos.getY() / fZoomY);
+ nSizeX = nSizeX / fZoomX;
+ nSizeY = nSizeY / fZoomY;
+ }
+
+ if ( bLayoutRTL )
+ aPos.AdjustX( -nSizeX );
+ tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
+
+ weld::Window* pParent = comphelper::LibreOfficeKit::isActive() ? GetFrameWeld() : weld::GetPopupParent(*this, aCellRect);
+ mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::DataSelect);
+ mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR
+
+ // SetSize later
+
+ const sal_uInt32 nIndex = rDoc.GetAttr(nCol, nRow, nTab, ATTR_VALIDDATA)->GetValue();
+ const ScValidationData* pData = nIndex ? rDoc.GetValidationEntry(nIndex) : nullptr;
+
+ bool bEmpty = false;
+ std::vector<ScTypedStrData> aStrings; // case sensitive
+ // Fill List
+ rDoc.GetDataEntries(nCol, nRow, nTab, aStrings, true /* bValidation */);
+
+ // IsIgnoreBlank allows blank values. Don't add empty string unless "Allow Empty Cells"
+ if (pData && !pData->IsIgnoreBlank())
+ {
+ auto lambda = [](const ScTypedStrData& rStr) { return rStr.GetString().isEmpty(); };
+ std::erase_if(aStrings, lambda);
+ }
+
+ if (aStrings.empty())
+ bEmpty = true;
+
+ if (!bEmpty)
+ {
+ rFilterBox.freeze();
+
+ // Fill Listbox
+ bool bWait = aStrings.size() > 100;
+
+ if (bWait)
+ EnterWait();
+
+ for (const auto& rString : aStrings)
+ {
+ const OUString& rFilterString = rString.GetString();
+ rFilterBox.append_text(rFilterString);
+ }
+
+ if (bWait)
+ LeaveWait();
+
+ rFilterBox.thaw();
+
+ ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
+ }
+
+ sal_Int32 nSelPos = -1;
+
+ if ( nIndex )
+ {
+ if (pData)
+ {
+ std::unique_ptr<ScTypedStrData> pNew;
+ OUString aDocStr = rDoc.GetString(nCol, nRow, nTab);
+ if ( rDoc.HasValueData( nCol, nRow, nTab ) )
+ {
+ double fVal = rDoc.GetValue(ScAddress(nCol, nRow, nTab));
+ pNew.reset(new ScTypedStrData(aDocStr, fVal, fVal, ScTypedStrData::Value));
+ }
+ else
+ pNew.reset(new ScTypedStrData(aDocStr, 0.0, 0.0, ScTypedStrData::Standard));
+
+ if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
+ {
+ auto it = std::lower_bound(aStrings.begin(), aStrings.end(), *pNew, ScTypedStrData::LessCaseSensitive());
+ if (it != aStrings.end() && ScTypedStrData::EqualCaseSensitive()(*it, *pNew))
+ nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
+ }
+ else
+ {
+ auto it = std::find_if(aStrings.begin(), aStrings.end(), FindTypedStrData(*pNew, true));
+ if (it != aStrings.end())
+ nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
+ }
+ }
+ }
+
+ // Do not show an empty selection List:
+
+ if ( bEmpty )
+ {
+ mpFilterBox.reset();
+ }
+ else
+ {
+ rFilterBox.grab_focus();
+
+ if (rFilterBox.n_children())
+ {
+ if (nSelPos != -1)
+ rFilterBox.set_cursor(nSelPos);
+ else
+ rFilterBox.set_cursor(0);
+ }
+ // Select only after GrabFocus, so that the focus rectangle gets correct
+ if (nSelPos != -1)
+ rFilterBox.select(nSelPos);
+ else
+ rFilterBox.unselect_all();
+
+ mpFilterBox->EndInit();
+ }
+ collectUIInformation(OUString::number(nRow), OUString::number(nCol),"SELECTMENU");
+}
+
+void ScGridWindow::FilterSelect( sal_uLong nSel )
+{
+ weld::TreeView& rFilterBox = mpFilterBox->get_widget();
+ OUString aString = rFilterBox.get_text(static_cast<sal_Int32>(nSel));
+
+ SCCOL nCol = mpFilterBox->GetCol();
+ SCROW nRow = mpFilterBox->GetRow();
+ switch (mpFilterBox->GetMode())
+ {
+ case ScFilterBoxMode::DataSelect:
+ ExecDataSelect(nCol, nRow, aString);
+ break;
+ case ScFilterBoxMode::Scenario:
+ mrViewData.GetView()->UseScenario(aString);
+ break;
+ }
+
+ // coverity[check_after_deref] - could be destroyed by ExecDataSelect
+ if (mpFilterBox)
+ mpFilterBox->popdown();
+
+ GrabFocus(); // Otherwise the focus would be wrong on OS/2
+}
+
+void ScGridWindow::ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr )
+{
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pViewHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pViewHdl && mrViewData.HasEditView(mrViewData.GetActivePart()))
+ pViewHdl->CancelHandler();
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScViewFunc* pView = mrViewData.GetView();
+ pView->EnterData( nCol, nRow, nTab, rStr );
+
+ // #i52307# CellContentChanged is not in EnterData so it isn't called twice
+ // if the cursor is moved afterwards.
+ pView->CellContentChanged();
+}
+
+void ScGridWindow::MoveMouseStatus( ScGridWindow& rDestWin )
+{
+ if (nButtonDown)
+ {
+ rDestWin.nButtonDown = nButtonDown;
+ rDestWin.nMouseStatus = nMouseStatus;
+ }
+
+ if (bRFMouse)
+ {
+ rDestWin.bRFMouse = bRFMouse;
+ rDestWin.bRFSize = bRFSize;
+ rDestWin.nRFIndex = nRFIndex;
+ rDestWin.nRFAddX = nRFAddX;
+ rDestWin.nRFAddY = nRFAddY;
+ bRFMouse = false;
+ }
+
+ if (nPagebreakMouse)
+ {
+ rDestWin.nPagebreakMouse = nPagebreakMouse;
+ rDestWin.nPagebreakBreak = nPagebreakBreak;
+ rDestWin.nPagebreakPrev = nPagebreakPrev;
+ rDestWin.aPagebreakSource = aPagebreakSource;
+ rDestWin.aPagebreakDrag = aPagebreakDrag;
+ nPagebreakMouse = SC_PD_NONE;
+ }
+}
+
+bool ScGridWindow::TestMouse( const MouseEvent& rMEvt, bool bAction )
+{
+ // MouseEvent buttons must only be checked if bAction==TRUE
+ // to allow changing the mouse pointer in MouseMove,
+ // but not start AutoFill with right button (#74229#).
+ // with bAction==sal_True, SetFillMode / SetDragMode is called
+
+ if ( bAction && !rMEvt.IsLeft() )
+ return false;
+
+ bool bNewPointer = false;
+
+ SfxInPlaceClient* pClient = mrViewData.GetViewShell()->GetIPClient();
+ bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
+
+ if ( mrViewData.IsActive() && !bOleActive )
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // Auto-Fill
+
+ ScRange aMarkRange;
+ if (mrViewData.GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE)
+ {
+ if (aMarkRange.aStart.Tab() == mrViewData.GetTabNo() && mpAutoFillRect)
+ {
+ Point aMousePos = rMEvt.GetPosPixel();
+ if (mpAutoFillRect->Contains(aMousePos))
+ {
+ SetPointer( PointerStyle::Cross ); //! bold cross ?
+ if (bAction)
+ {
+ SCCOL nX = aMarkRange.aEnd.Col();
+ SCROW nY = aMarkRange.aEnd.Row();
+
+ if ( lcl_IsEditableMatrix( mrViewData.GetDocument(), aMarkRange ) )
+ mrViewData.SetDragMode(
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY, ScFillMode::MATRIX );
+ else
+ mrViewData.SetFillMode(
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY );
+
+ // The simple selection must also be recognized when dragging,
+ // where the Marking flag is set and MarkToSimple won't work anymore.
+ mrViewData.GetMarkData().MarkToSimple();
+ }
+ bNewPointer = true;
+ }
+ }
+ }
+
+ // Embedded rectangle
+
+ if (rDoc.IsEmbedded())
+ {
+ ScRange aRange;
+ rDoc.GetEmbedded( aRange );
+ if ( mrViewData.GetTabNo() == aRange.aStart.Tab() )
+ {
+ Point aStartPos = mrViewData.GetScrPos( aRange.aStart.Col(), aRange.aStart.Row(), eWhich );
+ Point aEndPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich );
+ Point aMousePos = rMEvt.GetPosPixel();
+ if ( bLayoutRTL )
+ {
+ aStartPos.AdjustX(2 );
+ aEndPos.AdjustX(2 );
+ }
+ bool bTop = ( aMousePos.X() >= aStartPos.X()-3 && aMousePos.X() <= aStartPos.X()+1 &&
+ aMousePos.Y() >= aStartPos.Y()-3 && aMousePos.Y() <= aStartPos.Y()+1 );
+ bool bBottom = ( aMousePos.X() >= aEndPos.X()-3 && aMousePos.X() <= aEndPos.X()+1 &&
+ aMousePos.Y() >= aEndPos.Y()-3 && aMousePos.Y() <= aEndPos.Y()+1 );
+ if ( bTop || bBottom )
+ {
+ SetPointer( PointerStyle::Cross );
+ if (bAction)
+ {
+ ScFillMode nMode = bTop ? ScFillMode::EMBED_LT : ScFillMode::EMBED_RB;
+ mrViewData.SetDragMode(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), nMode );
+ }
+ bNewPointer = true;
+ }
+ }
+ }
+ }
+
+ if (!bNewPointer && bAction)
+ {
+ mrViewData.ResetFillMode();
+ }
+
+ return bNewPointer;
+}
+
+void ScGridWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (SfxLokHelper::getDeviceFormFactor() == LOKDeviceFormFactor::MOBILE)
+ {
+ ScViewFunc* pView = mrViewData.GetView();
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+
+ Point aPos(rMEvt.GetPosPixel());
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel(aPos.X(), aPos.Y(), eWhich, nPosX, nPosY);
+
+ if (bRefMode && pView->GetFunctionSet().CheckRefBounds(nPosX, nPosY))
+ return;
+ }
+
+ nNestedButtonState = ScNestedButtonState::Down;
+
+ MouseEventState aState;
+ HandleMouseButtonDown(rMEvt, aState);
+ if (aState.mbActivatePart)
+ mrViewData.GetView()->ActivatePart(eWhich);
+
+ if ( nNestedButtonState == ScNestedButtonState::Up )
+ {
+ // #i41690# If an object is deactivated from MouseButtonDown, it might reschedule,
+ // so MouseButtonUp comes before the MouseButtonDown call is finished. In this case,
+ // simulate another MouseButtonUp call, so the selection state is consistent.
+
+ nButtonDown = rMEvt.GetButtons();
+ FakeButtonUp();
+
+ if ( IsTracking() )
+ EndTracking(); // normally done in VCL as part of MouseButtonUp handling
+ }
+ nNestedButtonState = ScNestedButtonState::NONE;
+}
+
+void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventState& rState )
+{
+ // We have to check if a context menu is shown and we have an UI
+ // active inplace client. In that case we have to ignore the event.
+ // Otherwise we would crash (context menu has been
+ // opened by inplace client and we would deactivate the inplace client,
+ // the context menu is closed by VCL asynchronously which in the end
+ // would work on deleted objects or the context menu has no parent anymore)
+ SfxViewShell* pViewSh = mrViewData.GetViewShell();
+ SfxInPlaceClient* pClient = pViewSh->GetIPClient();
+ if ( pClient &&
+ pClient->IsObjectInPlaceActive() &&
+ vcl::IsInPopupMenuExecute() )
+ return;
+
+ aCurMousePos = rMEvt.GetPosPixel();
+
+ // Filter popup is ended with its own mouse click, not when clicking into the Grid Window,
+ // so the following query is no longer necessary:
+ ClickExtern(); // deletes FilterBox when available
+
+ HideNoteMarker();
+
+ bEEMouse = false;
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
+ return;
+
+ const bool bWasMouseCaptured = IsMouseCaptured();
+ SAL_WARN_IF(bWasMouseCaptured, "sc.ui", "Is there a scenario where the mouse is captured before mouse down?");
+
+ pScActiveViewShell = mrViewData.GetViewShell(); // if left is clicked
+ nScClickMouseModifier = rMEvt.GetModifier(); // to always catch a control click
+
+ bool bDetective = mrViewData.GetViewShell()->IsAuditShell();
+ bool bRefMode = mrViewData.IsRefMode(); // Start reference
+ bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
+ bool bEditMode = mrViewData.HasEditView(eWhich); // also in Mode==SC_INPUT_TYPE
+ bool bDouble = (rMEvt.GetClicks() == 2);
+ ScDocument& rDoc = mrViewData.GetDocument();
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ // DeactivateIP does only happen when MarkListHasChanged
+
+ // An error message can show up during GrabFocus call
+ // (for instance when renaming tables per sheet title)
+
+ if ( !nButtonDown || !bDouble ) // single (first) click is always valid
+ nButtonDown = rMEvt.GetButtons(); // set nButtonDown first, so StopMarking works
+
+ if ( ( bEditMode && mrViewData.GetActivePart() == eWhich ) || !bFormulaMode )
+ GrabFocus();
+
+ // #i31846# need to cancel a double click if the first click has set the "ignore" state,
+ // but a single (first) click is always valid
+ if ( nMouseStatus == SC_GM_IGNORE && bDouble )
+ {
+ nButtonDown = 0;
+ nMouseStatus = SC_GM_NONE;
+ return;
+ }
+
+ if ( bDetective ) // Detectiv fill mode
+ {
+ if ( rMEvt.IsLeft() && !rMEvt.GetModifier() )
+ {
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ SfxInt16Item aPosXItem( SID_RANGE_COL, nPosX );
+ SfxInt32Item aPosYItem( SID_RANGE_ROW, nPosY );
+ mrViewData.GetDispatcher().ExecuteList(SID_FILL_SELECT,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aPosXItem, &aPosYItem });
+
+ }
+ nButtonDown = 0;
+ nMouseStatus = SC_GM_NONE;
+ return;
+ }
+
+ if (!bDouble)
+ nMouseStatus = SC_GM_NONE;
+
+ rState.mbActivatePart = !bFormulaMode; // Don't activate when in formula mode.
+
+ if (bFormulaMode)
+ {
+ ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
+ pSelEng->SetWindow(this);
+ pSelEng->SetWhich(eWhich);
+ pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
+ }
+
+ if (bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()))
+ {
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+ SCCOL nEndCol = mrViewData.GetEditEndCol();
+ SCROW nEndRow = mrViewData.GetEditEndRow();
+ SCCOL nStartCol = mrViewData.GetEditStartCol();
+
+ if ( nPosX >= nStartCol && nPosX <= nEndCol &&
+ nPosY >= nEditRow && nPosY <= nEndRow )
+ {
+ // when clicking in the table EditView, always reset the focus
+ if (bFormulaMode) // otherwise this has already happen above
+ GrabFocus();
+
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ bEEMouse = true;
+
+ if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ Point aMouse = rMEvt.GetPosPixel();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ comphelper::ScopeGuard aOutputGuard(
+ [pEditView, aOutputArea] {
+ pEditView->SetOutputArea(aOutputArea);
+ });
+
+ lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
+ pEditView->SetOutputArea(aOutputArea);
+
+ MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
+ rMEvt.GetButtons(), rMEvt.GetModifier());
+ pEditView->MouseButtonDown( aEvent );
+ }
+ else
+ pEditView->MouseButtonDown( rMEvt );
+ return;
+ }
+ }
+
+ if (pScMod->GetIsWaterCan())
+ {
+ //! what's up with the Mac ???
+ if ( rMEvt.GetModifier() + rMEvt.GetButtons() == MOUSE_RIGHT )
+ {
+ nMouseStatus = SC_GM_WATERUNDO;
+ return;
+ }
+ }
+
+ // Order that matches the displayed Cursor:
+ // RangeFinder, AutoFill, PageBreak, Drawing
+
+ RfCorner rCorner = NONE;
+ bool bFound = HitRangeFinder(rMEvt.GetPosPixel(), rCorner, &nRFIndex, &nRFAddX, &nRFAddY);
+ bRFSize = (rCorner != NONE);
+ aRFSelectedCorned = rCorner;
+
+ if (bFound)
+ {
+ bRFMouse = true; // the other variables are initialized above
+
+ rState.mbActivatePart = true; // always activate ?
+ StartTracking();
+ return;
+ }
+
+ bool bCrossPointer = TestMouse( rMEvt, true );
+ if ( bCrossPointer )
+ {
+ if ( bDouble )
+ mrViewData.GetView()->FillCrossDblClick();
+ else
+ pScMod->InputEnterHandler(); // Autofill etc.
+ }
+
+ if ( !bCrossPointer )
+ {
+ nPagebreakMouse = HitPageBreak( rMEvt.GetPosPixel(), &aPagebreakSource,
+ &nPagebreakBreak, &nPagebreakPrev );
+ if (nPagebreakMouse)
+ {
+ bPagebreakDrawn = false;
+ StartTracking();
+ PagebreakMove( rMEvt, false );
+ return;
+ }
+ }
+
+ // in the tiled rendering case, single clicks into drawing objects take
+ // precedence over bEditMode
+ if (((!bFormulaMode && !bEditMode) || bIsTiledRendering) && rMEvt.IsLeft())
+ {
+ if ( !bCrossPointer && DrawMouseButtonDown(rMEvt) )
+ {
+ return;
+ }
+
+ mrViewData.GetViewShell()->SetDrawShell( false ); // no Draw-object selected
+
+ // TestMouse has already happened above
+ }
+
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ SCTAB nTab = mrViewData.GetTabNo();
+ m_nDownPosX = nPosX;
+ m_nDownPosY = nPosY;
+
+ // FIXME: this is to limit the number of rows handled in the Online
+ // to 1000; this will be removed again when the performance
+ // bottlenecks are sorted out
+ if ( comphelper::LibreOfficeKit::isActive() && nPosY > MAXTILEDROW - 1 )
+ {
+ nButtonDown = 0;
+ nMouseStatus = SC_GM_NONE;
+ return;
+ }
+
+ // Auto filter / pivot table / data select popup. This shouldn't activate the part.
+
+ if ( !bDouble && !bFormulaMode && rMEvt.IsLeft() )
+ {
+ SCCOL nRealPosX;
+ SCROW nRealPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nRealPosX, nRealPosY, false );//the real row/col
+
+ // show in the merged cells the filter of the first cell (nPosX instead of nRealPosX)
+ const ScMergeFlagAttr* pRealPosAttr = rDoc.GetAttr(nPosX, nRealPosY, nTab, ATTR_MERGE_FLAG);
+ if( pRealPosAttr->HasAutoFilter() )
+ {
+ SC_MOD()->InputEnterHandler();
+ if (DoAutoFilterButton(nPosX, nRealPosY, rMEvt))
+ return;
+ }
+
+ const ScMergeFlagAttr* pAttr = rDoc.GetAttr(nPosX, nPosY, nTab, ATTR_MERGE_FLAG);
+ if (pAttr->HasAutoFilter())
+ {
+ if (DoAutoFilterButton(nPosX, nPosY, rMEvt))
+ {
+ rState.mbActivatePart = false;
+ return;
+ }
+ }
+
+ if (pAttr->HasPivotButton() || pAttr->HasPivotPopupButton() || pAttr->HasPivotMultiFieldPopupButton())
+ {
+ DoPushPivotButton(nPosX, nPosY, rMEvt, pAttr->HasPivotButton(),
+ pAttr->HasPivotPopupButton(), pAttr->HasPivotMultiFieldPopupButton());
+ rState.mbActivatePart = false;
+ return;
+ }
+
+ if (pAttr->HasPivotToggle())
+ {
+ DoPushPivotToggle(nPosX, nPosY, rMEvt);
+ rState.mbActivatePart = false;
+ }
+
+ // List Validity drop-down button
+
+ if ( bListValButton )
+ {
+ tools::Rectangle aButtonRect = GetListValButtonRect( aListValPos );
+ if ( aButtonRect.Contains( aPos ) )
+ {
+ // tdf#149609 if we captured the mouse in the course of this function
+ // release it before showing the data select menu to undo any unhelpful
+ // seleng capture
+ if (!bWasMouseCaptured && IsMouseCaptured())
+ ReleaseMouse();
+
+ LaunchDataSelectMenu( aListValPos.Col(), aListValPos.Row() );
+
+ nMouseStatus = SC_GM_FILTER; // not set in DoAutoFilterMenue for bDataSelect
+ rState.mbActivatePart = false;
+ return;
+ }
+ }
+ }
+
+ // scenario selection
+
+ ScRange aScenRange;
+ if ( rMEvt.IsLeft() && HasScenarioButton( aPos, aScenRange ) )
+ {
+ // tdf#149609 if we captured the mouse in the course of this function
+ // release it before showing the data scenario menu to undo any unhelpful
+ // seleng capture
+ if (!bWasMouseCaptured && IsMouseCaptured())
+ ReleaseMouse();
+
+ DoScenarioMenu( aScenRange );
+
+ // Scenario selection comes from MouseButtonDown:
+ // The next MouseMove on the FilterBox is like a ButtonDown
+ nMouseStatus = SC_GM_FILTER;
+ return;
+ }
+
+ // double click started ?
+
+ // StopMarking can be called from DrawMouseButtonDown
+
+ if ( nMouseStatus != SC_GM_IGNORE && !bRefMode )
+ {
+ if ( bDouble && !bCrossPointer )
+ {
+ if (nMouseStatus == SC_GM_TABDOWN)
+ nMouseStatus = SC_GM_DBLDOWN;
+ }
+ else
+ nMouseStatus = SC_GM_TABDOWN;
+ }
+
+ // links in the edit cell
+
+ bool bAlt = rMEvt.IsMod2();
+ if ( !bAlt && rMEvt.IsLeft() && ScGlobal::ShouldOpenURL() &&
+ GetEditUrl(rMEvt.GetPosPixel()) ) // click on link: do not move cursor
+ {
+ SetPointer( PointerStyle::RefHand );
+ nMouseStatus = SC_GM_URLDOWN; // also only execute when ButtonUp
+ return;
+ }
+
+ // Gridwin - Selection Engine
+
+ if ( !rMEvt.IsLeft() )
+ return;
+
+ ScViewSelectionEngine* pSelEng = mrViewData.GetView()->GetSelEngine();
+ pSelEng->SetWindow(this);
+ pSelEng->SetWhich(eWhich);
+ pSelEng->SetVisibleArea( tools::Rectangle(Point(), GetOutputSizePixel()) );
+
+ // SelMouseButtonDown on the View is still setting the bMoveIsShift flag
+ if ( mrViewData.GetView()->SelMouseButtonDown( rMEvt ) )
+ {
+ if (IsMouseCaptured())
+ {
+ // Tracking instead of CaptureMouse, so it can be canceled cleanly
+ //! Someday SelectionEngine should call StartTracking on its own!?!
+ ReleaseMouse();
+ StartTracking();
+ }
+ mrViewData.GetMarkData().SetMarking(true);
+ return;
+ }
+}
+
+void ScGridWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ aCurMousePos = rMEvt.GetPosPixel();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScMarkData& rMark = mrViewData.GetMarkData();
+ // #i41690# detect a MouseButtonUp call from within MouseButtonDown
+ // (possible through Reschedule from storing an OLE object that is deselected)
+
+ if ( nNestedButtonState == ScNestedButtonState::Down )
+ nNestedButtonState = ScNestedButtonState::Up;
+
+ if (nButtonDown != rMEvt.GetButtons())
+ nMouseStatus = SC_GM_IGNORE; // reset and return
+
+ nButtonDown = 0;
+
+ if (nMouseStatus == SC_GM_IGNORE)
+ {
+ nMouseStatus = SC_GM_NONE;
+ // Selection engine: cancel selection
+ mrViewData.GetView()->GetSelEngine()->Reset();
+ rMark.SetMarking(false);
+ if (mrViewData.IsAnyFillMode())
+ {
+ mrViewData.GetView()->StopRefMode();
+ mrViewData.ResetFillMode();
+ }
+ StopMarking();
+ DrawEndAction(); // cancel selection/moving in drawing layer
+ ReleaseMouse();
+ return;
+ }
+
+ if (nMouseStatus == SC_GM_FILTER)
+ {
+ nMouseStatus = SC_GM_NONE;
+ ReleaseMouse();
+ return; // nothing more should happen here
+ }
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
+ return;
+
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ if (bEEMouse && mrViewData.HasEditView( eWhich ))
+ {
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+
+ if (comphelper::LibreOfficeKit::isActive() && rDoc.IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ Point aMouse = rMEvt.GetPosPixel();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ comphelper::ScopeGuard aOutputGuard(
+ [pEditView, aOutputArea] {
+ pEditView->SetOutputArea(aOutputArea);
+ });
+
+ lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
+ pEditView->SetOutputArea(aOutputArea);
+
+ MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
+ rMEvt.GetButtons(), rMEvt.GetModifier());
+ pEditView->MouseButtonUp( aEvent );
+ }
+ else
+ pEditView->MouseButtonUp( rMEvt );
+
+ if ( rMEvt.IsMiddle() &&
+ GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
+ {
+ // EditView may have pasted from selection
+ pScMod->InputChanged( pEditView );
+ }
+ else
+ pScMod->InputSelection( pEditView ); // parentheses etc.
+
+ mrViewData.GetView()->InvalidateAttribs();
+ rBindings.Invalidate( SID_HYPERLINK_GETLINK );
+ bEEMouse = false;
+ return;
+ }
+
+ if (bDPMouse)
+ {
+ DPMouseButtonUp( rMEvt ); // resets bDPMouse
+ return;
+ }
+
+ if (bRFMouse)
+ {
+ RFMouseMove( rMEvt, true ); // Again the proper range
+ bRFMouse = false;
+ SetPointer( PointerStyle::Arrow );
+ ReleaseMouse();
+ return;
+ }
+
+ if (nPagebreakMouse)
+ {
+ PagebreakMove( rMEvt, true );
+ nPagebreakMouse = SC_PD_NONE;
+ SetPointer( PointerStyle::Arrow );
+ ReleaseMouse();
+ return;
+ }
+
+ if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode
+ {
+ SfxUndoManager* pMgr = mrViewData.GetDocShell()->GetUndoManager();
+ if ( pMgr->GetUndoActionCount() && dynamic_cast<ScUndoSelectionStyle*>(pMgr->GetUndoAction()) )
+ pMgr->Undo();
+ return;
+ }
+
+ if (DrawMouseButtonUp(rMEvt)) // includes format paint brush handling for drawing objects
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ SfxBindings& rFrmBindings=pViewShell->GetViewFrame().GetBindings();
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_WIDTH);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_HEIGHT);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ANGLE);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_X);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_ROT_Y);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOWIDTH);
+ rFrmBindings.Invalidate(SID_ATTR_TRANSFORM_AUTOHEIGHT);
+ return;
+ }
+
+ rMark.SetMarking(false);
+
+ SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
+
+ if (mrViewData.IsFillMode() ||
+ ( mrViewData.GetFillMode() == ScFillMode::MATRIX && rMEvt.IsMod1() ))
+ {
+ nScFillModeMouseModifier = rMEvt.GetModifier();
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
+ ScRange aDelRange;
+ bool bIsDel = mrViewData.GetDelMark( aDelRange );
+
+ ScViewFunc* pView = mrViewData.GetView();
+ pView->StopRefMode();
+ mrViewData.ResetFillMode();
+ pView->GetFunctionSet().SetAnchorFlag( false ); // #i5819# don't use AutoFill anchor flag for selection
+
+ if ( bIsDel )
+ {
+ pView->MarkRange( aDelRange, false );
+ pView->DeleteContents( InsertDeleteFlags::CONTENTS );
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
+ if ( aBlockRange != aDelRange )
+ {
+ if ( aDelRange.aStart.Row() == nStartRow )
+ aBlockRange.aEnd.SetCol( aDelRange.aStart.Col() - 1 );
+ else
+ aBlockRange.aEnd.SetRow( aDelRange.aStart.Row() - 1 );
+ pView->MarkRange( aBlockRange, false );
+ }
+ }
+ else
+ mrViewData.GetDispatcher().Execute( FID_FILL_AUTO, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else if (mrViewData.GetFillMode() == ScFillMode::MATRIX)
+ {
+ SCTAB nTab = mrViewData.GetTabNo();
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ mrViewData.GetFillData( nStartCol, nStartRow, nEndCol, nEndRow );
+ ScRange aBlockRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab );
+ SCCOL nFillCol = mrViewData.GetRefEndX();
+ SCROW nFillRow = mrViewData.GetRefEndY();
+ ScAddress aEndPos( nFillCol, nFillRow, nTab );
+
+ ScTabView* pView = mrViewData.GetView();
+ pView->StopRefMode();
+ mrViewData.ResetFillMode();
+ pView->GetFunctionSet().SetAnchorFlag( false );
+
+ if ( aEndPos != aBlockRange.aEnd )
+ {
+ mrViewData.GetDocShell()->GetDocFunc().ResizeMatrix( aBlockRange, aEndPos );
+ mrViewData.GetView()->MarkRange( ScRange( aBlockRange.aStart, aEndPos ) );
+ }
+ }
+ else if (mrViewData.IsAnyFillMode())
+ {
+ // Embedded area has been changed
+ ScTabView* pView = mrViewData.GetView();
+ pView->StopRefMode();
+ mrViewData.ResetFillMode();
+ pView->GetFunctionSet().SetAnchorFlag( false );
+ mrViewData.GetDocShell()->UpdateOle(mrViewData);
+ }
+
+ bool bRefMode = mrViewData.IsRefMode();
+ if (bRefMode)
+ pScMod->EndReference();
+
+ // Format paintbrush mode (Switch)
+
+ if (pScMod->GetIsWaterCan())
+ {
+ // Check on undo already done above
+
+ ScStyleSheetPool* pStylePool = mrViewData.GetDocument().
+ GetStyleSheetPool();
+ if ( pStylePool )
+ {
+ SfxStyleSheet* pStyleSheet = static_cast<SfxStyleSheet*>(
+ pStylePool->GetActualStyleSheet());
+
+ if ( pStyleSheet )
+ {
+ SfxStyleFamily eFamily = pStyleSheet->GetFamily();
+
+ switch ( eFamily )
+ {
+ case SfxStyleFamily::Para:
+ mrViewData.GetView()->SetStyleSheetToMarked( pStyleSheet );
+ mrViewData.GetView()->DoneBlockMode();
+ break;
+
+ case SfxStyleFamily::Page:
+ mrViewData.GetDocument().SetPageStyle( mrViewData.GetTabNo(),
+ pStyleSheet->GetName() );
+
+ ScPrintFunc( mrViewData.GetDocShell(),
+ mrViewData.GetViewShell()->GetPrinter(true),
+ mrViewData.GetTabNo() ).UpdatePages();
+
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ ScDBFunc* pView = mrViewData.GetView();
+ ScDocument* pBrushDoc = pView->GetBrushDocument();
+ if ( pBrushDoc )
+ {
+ pView->PasteFromClip( InsertDeleteFlags::ATTRIB, pBrushDoc );
+ if ( !pView->IsPaintBrushLocked() )
+ pView->ResetBrushDocument(); // invalidates pBrushDoc pointer
+ }
+
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ SCTAB nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor( nPosX, nPosY, nTab );
+
+ // double click (only left button)
+
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ bool bDouble = ( rMEvt.GetClicks() == 2 && rMEvt.IsLeft() );
+ if ( bDouble
+ && !bRefMode
+ && (nMouseStatus == SC_GM_DBLDOWN || (bIsTiledRendering && nMouseStatus != SC_GM_URLDOWN))
+ && !pScMod->IsRefDialogOpen())
+ {
+ // data pilot table
+ if ( pDPObj && pDPObj->GetSaveData()->GetDrillDown() )
+ {
+ ScAddress aCellPos( nPosX, nPosY, mrViewData.GetTabNo() );
+
+ // Check for header drill-down first.
+ sheet::DataPilotTableHeaderData aData;
+ pDPObj->GetHeaderPositionData(aCellPos, aData);
+
+ if ( ( aData.Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+ ! ( aData.Flags & sheet::MemberResultFlags::SUBTOTAL ) )
+ {
+ css::sheet::DataPilotFieldOrientation nDummy;
+ if ( pView->HasSelectionForDrillDown( nDummy ) )
+ {
+ // execute slot to show dialog
+ mrViewData.GetDispatcher().Execute( SID_OUTLINE_SHOW, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else
+ {
+ // toggle single entry
+ ScDPObject aNewObj( *pDPObj );
+ pDPObj->ToggleDetails( aData, &aNewObj );
+ ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
+ aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
+ mrViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+ else
+ {
+ // Check if the data area is double-clicked.
+
+ Sequence<sheet::DataPilotFieldFilter> aFilters;
+ if ( pDPObj->GetDataFieldPositionData(aCellPos, aFilters) )
+ mrViewData.GetView()->ShowDataPilotSourceData( *pDPObj, aFilters );
+ }
+
+ return;
+ }
+
+ // Check for cell protection attribute.
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ bool bEditAllowed = true;
+ if ( pProtect && pProtect->isProtected() )
+ {
+ bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
+ bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if ( bSkipProtected && bSkipUnprotected )
+ bEditAllowed = false;
+ else if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
+ bEditAllowed = false;
+ }
+
+ if ( bEditAllowed )
+ {
+ // edit cell contents
+ mrViewData.GetViewShell()->UpdateInputHandler();
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ if (mrViewData.HasEditView(eWhich))
+ {
+ // Set text cursor where clicked
+ EditView* pEditView = mrViewData.GetEditView( eWhich );
+ MouseEvent aEditEvt( rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0 );
+ pEditView->MouseButtonDown( aEditEvt );
+ pEditView->MouseButtonUp( aEditEvt );
+ }
+ }
+
+ if ( bIsTiledRendering && rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt ) )
+ {
+ mrViewData.GetView()->SelectionChanged();
+ }
+
+ if ( bDouble )
+ return;
+ }
+
+ // Links in edit cells
+
+ bool bAlt = rMEvt.IsMod2();
+ if ( !bAlt && !bRefMode && !bDouble && nMouseStatus == SC_GM_URLDOWN )
+ {
+ // Only execute on ButtonUp, if ButtonDown also was done on a URL
+
+ OUString aName, aUrl, aTarget;
+ if ( GetEditUrl( rMEvt.GetPosPixel(), &aName, &aUrl, &aTarget ) )
+ {
+ nMouseStatus = SC_GM_NONE; // Ignore double-click
+ bool isTiledRendering = comphelper::LibreOfficeKit::isActive();
+ // ScGlobal::OpenURL() only understands Calc A1 style syntax.
+ // Convert it to Calc A1 before calling OpenURL().
+ if (rDoc.GetAddressConvention() == formula::FormulaGrammar::CONV_OOO)
+ {
+ if (aUrl.startsWith("#")) {
+ ScGlobal::OpenURL(aUrl, aTarget, isTiledRendering);
+ return;
+ }
+ // On a mobile device view there is no ctrl+click and for hyperlink popup
+ // the cell coordinates must be sent along with click position for elegance
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ if (isTiledRendering && pViewShell &&
+ (pViewShell->isLOKMobilePhone() || pViewShell->isLOKTablet()))
+ {
+ aPos = rMEvt.GetPosPixel();
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ OString aCursor = pViewShell->GetViewData().describeCellCursorAt(nPosX, nPosY);
+ double fPPTX = pViewShell->GetViewData().GetPPTX();
+ int mouseX = aPos.X() / fPPTX;
+ OString aMsg(aUrl.toUtf8() + " coordinates: " + aCursor + ", " + OString::number(mouseX));
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
+ } else
+ ScGlobal::OpenURL(aUrl, aTarget);
+ }
+ else
+ {
+ ScAddress aTempAddr;
+ ScAddress::ExternalInfo aExtInfo;
+ ScRefFlags nRes = aTempAddr.Parse(aUrl, rDoc, rDoc.GetAddressConvention(), &aExtInfo);
+ if (!(nRes & ScRefFlags::VALID))
+ {
+ // Not a reference string. Pass it through unmodified.
+ ScGlobal::OpenURL(aUrl, aTarget);
+ return;
+ }
+
+ OUStringBuffer aBuf;
+ if (aExtInfo.mbExternal)
+ {
+ // External reference.
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pStr = pRefMgr->getExternalFileName(aExtInfo.mnFileId);
+ if (pStr)
+ aBuf.append(*pStr);
+
+ OUString aRefCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS, nullptr, formula::FormulaGrammar::CONV_OOO));
+ aBuf.append("#" + aExtInfo.maTabName + "." + aRefCalcA1);
+ ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget);
+ }
+ else
+ {
+ // Internal reference.
+ OUString aUrlCalcA1(aTempAddr.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, formula::FormulaGrammar::CONV_OOO));
+ aBuf.append("#" + aUrlCalcA1);
+ ScGlobal::OpenURL(aBuf.makeStringAndClear(), aTarget, isTiledRendering);
+ }
+ }
+
+ // fire worksheet_followhyperlink event
+ uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents = rDoc.GetVbaEventProcessor();
+ if( xVbaEvents.is() ) try
+ {
+ aPos = rMEvt.GetPosPixel();
+ nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ OUString sURL;
+ ScRefCellValue aCell;
+ if (lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL))
+ {
+ ScAddress aCellPos( nPosX, nPosY, nTab );
+ uno::Reference< table::XCell > xCell( new ScCellObj( mrViewData.GetDocShell(), aCellPos ) );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xCell) };
+ xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKSHEET_FOLLOWHYPERLINK, aArgs );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ return;
+ }
+ }
+
+ // Gridwin - SelectionEngine
+
+ // SelMouseButtonDown is called only for left button, but SelMouseButtonUp would return
+ // sal_True for any call, so IsLeft must be checked here, too.
+
+ if ( !(rMEvt.IsLeft() && mrViewData.GetView()->GetSelEngine()->SelMouseButtonUp( rMEvt )) )
+ return;
+
+ mrViewData.GetView()->SelectionChanged();
+
+ SfxDispatcher* pDisp = mrViewData.GetViewShell()->GetDispatcher();
+ bool bFormulaMode = pScMod->IsFormulaMode();
+ OSL_ENSURE( pDisp || bFormulaMode, "Cursor moved on inactive View ?" );
+
+ // #i14927# execute SID_CURRENTCELL (for macro recording) only if there is no
+ // multiple selection, so the argument string completely describes the selection,
+ // and executing the slot won't change the existing selection (executing the slot
+ // here and from a recorded macro is treated equally)
+ if ( pDisp && !bFormulaMode && !rMark.IsMultiMarked() )
+ {
+ OUString aAddr; // CurrentCell
+ if( rMark.IsMarked() )
+ {
+ const ScRange& aScRange = rMark.GetMarkArea();
+ aAddr = aScRange.Format(rDoc, ScRefFlags::RANGE_ABS);
+ if ( aScRange.aStart == aScRange.aEnd )
+ {
+ // make sure there is a range selection string even for a single cell
+ aAddr += ":" + aAddr;
+ }
+
+ //! SID_MARKAREA does not exist anymore ???
+ //! What happens when selecting with the cursor ???
+ }
+ else // only move cursor
+ {
+ ScAddress aScAddress( mrViewData.GetCurX(), mrViewData.GetCurY(), 0 );
+ aAddr = aScAddress.Format(ScRefFlags::ADDR_ABS);
+ }
+
+ SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
+ // We don't want to align to the cursor position because if the
+ // cell cursor isn't visible after making selection, it would jump
+ // back to the origin of the selection where the cell cursor is.
+ SfxBoolItem aAlignCursorItem( FN_PARAM_2, false );
+ pDisp->ExecuteList(SID_CURRENTCELL,
+ SfxCallMode::SLOT | SfxCallMode::RECORD,
+ { &aPosItem, &aAlignCursorItem });
+
+ mrViewData.GetView()->InvalidateAttribs();
+
+ }
+ mrViewData.GetViewShell()->SelectionChanged();
+
+ if (bIsTiledRendering && !bRefMode && !bDouble)
+ {
+ OUString aName, aUrl, aTarget;
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ if (pViewShell && nPosX == m_nDownPosX && nPosY == m_nDownPosY
+ && GetEditUrl(aPos, &aName, &aUrl, &aTarget))
+ {
+ OString aMsg(aUrl.toUtf8() + " coordinates: " +
+ pViewShell->GetViewData().describeCellCursorAt(nPosX, nPosY) + ", " +
+ OString::number(aPos.X() / pViewShell->GetViewData().GetPPTX()));
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aMsg);
+ }
+ }
+
+ m_nDownPosX = m_nDownPosY = -1;
+
+ return;
+}
+
+void ScGridWindow::FakeButtonUp()
+{
+ if ( nButtonDown )
+ {
+ MouseEvent aEvent( aCurMousePos ); // nButtons = 0 -> ignore
+ MouseButtonUp( aEvent );
+ }
+}
+
+void ScGridWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ aCurMousePos = rMEvt.GetPosPixel();
+
+ if (rMEvt.IsLeaveWindow() && mpNoteMarker && !mpNoteMarker->IsByKeyboard())
+ HideNoteMarker();
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsModalMode(mrViewData.GetSfxDocShell()))
+ return;
+
+ // If the Drag&Drop is started in the edit mode then sadly nothing else is kept
+ if (bEEMouse && nButtonDown && !rMEvt.GetButtons())
+ {
+ bEEMouse = false;
+ nButtonDown = 0;
+ nMouseStatus = SC_GM_NONE;
+ return;
+ }
+
+ if (nMouseStatus == SC_GM_IGNORE)
+ return;
+
+ if (nMouseStatus == SC_GM_WATERUNDO) // Undo in format paintbrush mode -> only what for Up
+ return;
+
+ if ( mrViewData.GetViewShell()->IsAuditShell() ) // Detective Fill Mode
+ {
+ SetPointer( PointerStyle::Fill );
+ return;
+ }
+
+ bool bFormulaMode = pScMod->IsFormulaMode(); // next click -> reference
+
+ if (bEEMouse && mrViewData.HasEditView( eWhich ))
+ {
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+
+ if (comphelper::LibreOfficeKit::isActive() && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ Point aMouse = rMEvt.GetPosPixel();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ comphelper::ScopeGuard aOutputGuard(
+ [pEditView, aOutputArea] {
+ pEditView->SetOutputArea(aOutputArea);
+ });
+
+ lcl_GetMirror(aMouse, aOutputArea, mrViewData.getLOKVisibleArea().GetWidth());
+ pEditView->SetOutputArea(aOutputArea);
+
+ MouseEvent aEvent(aMouse, rMEvt.GetClicks(), rMEvt.GetMode(),
+ rMEvt.GetButtons(), rMEvt.GetModifier());
+
+ pEditView->MouseMove( aEvent );
+ }
+ else
+ pEditView->MouseMove( rMEvt );
+ return;
+ }
+
+ if (bDPMouse)
+ {
+ DPMouseMove( rMEvt );
+ return;
+ }
+
+ if (bRFMouse)
+ {
+ RFMouseMove( rMEvt, false );
+ return;
+ }
+
+ if (nPagebreakMouse)
+ {
+ PagebreakMove( rMEvt, false );
+ return;
+ }
+
+ // Show other mouse pointer?
+
+ bool bEditMode = mrViewData.HasEditView(eWhich);
+
+ //! Test if refMode dragging !!!
+ if ( bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo()) )
+ {
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+ SCCOL nEndCol = mrViewData.GetEditEndCol();
+ SCROW nEndRow = mrViewData.GetEditEndRow();
+
+ if ( nPosX >= nEditCol && nPosX <= nEndCol &&
+ nPosY >= nEditRow && nPosY <= nEndRow )
+ {
+ if ( !pEditView )
+ {
+ SetPointer( PointerStyle::Text );
+ return;
+ }
+
+ const SvxFieldItem* pFld;
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ Point aLogicClick = pEditView->GetOutputDevice().PixelToLogic(aPos);
+ pFld = pEditView->GetField( aLogicClick );
+ }
+ else
+ {
+ pFld = pEditView->GetFieldUnderMousePointer();
+ }
+ // Field can only be URL field
+ bool bAlt = rMEvt.IsMod2();
+ if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() && pFld )
+ SetPointer( PointerStyle::RefHand );
+ else if ( pEditView->GetEditEngine()->IsEffectivelyVertical() )
+ SetPointer( PointerStyle::TextVertical );
+ else
+ SetPointer( PointerStyle::Text );
+ return;
+ }
+ }
+
+ bool bWater = SC_MOD()->GetIsWaterCan() || mrViewData.GetView()->HasPaintBrush();
+ if (bWater)
+ SetPointer( PointerStyle::Fill );
+
+ if (!bWater)
+ {
+ bool bCross = false;
+
+ // range finder
+
+ RfCorner rCorner = NONE;
+ if ( HitRangeFinder( rMEvt.GetPosPixel(), rCorner, nullptr, nullptr, nullptr ) )
+ {
+ if (rCorner != NONE)
+ SetPointer( PointerStyle::Cross );
+ else
+ SetPointer( PointerStyle::Hand );
+ bCross = true;
+ }
+
+ // Page-Break-Mode
+
+ if ( !nButtonDown && mrViewData.IsPagebreakMode() )
+ {
+ sal_uInt16 nBreakType = HitPageBreak( rMEvt.GetPosPixel(), nullptr, nullptr, nullptr );
+ if (nBreakType != 0 )
+ {
+ PointerStyle eNew = PointerStyle::Arrow;
+ switch ( nBreakType )
+ {
+ case SC_PD_RANGE_L:
+ case SC_PD_RANGE_R:
+ case SC_PD_BREAK_H:
+ eNew = PointerStyle::ESize;
+ break;
+ case SC_PD_RANGE_T:
+ case SC_PD_RANGE_B:
+ case SC_PD_BREAK_V:
+ eNew = PointerStyle::SSize;
+ break;
+ case SC_PD_RANGE_TL:
+ case SC_PD_RANGE_BR:
+ eNew = PointerStyle::SESize;
+ break;
+ case SC_PD_RANGE_TR:
+ case SC_PD_RANGE_BL:
+ eNew = PointerStyle::NESize;
+ break;
+ }
+ SetPointer( eNew );
+ bCross = true;
+ }
+ }
+
+ // Show fill cursor?
+
+ if ( !bFormulaMode && !nButtonDown )
+ if (TestMouse( rMEvt, false ))
+ bCross = true;
+
+ if ( nButtonDown && mrViewData.IsAnyFillMode() )
+ {
+ SetPointer( PointerStyle::Cross );
+ bCross = true;
+ nScFillModeMouseModifier = rMEvt.GetModifier(); // evaluated for AutoFill and Matrix
+ }
+
+ if (!bCross)
+ {
+ bool bAlt = rMEvt.IsMod2();
+
+ if (bEditMode) // First has to be in edit mode!
+ SetPointer( mrViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow );
+ else if ( !bAlt && !nButtonDown && ScGlobal::ShouldOpenURL() &&
+ GetEditUrl(rMEvt.GetPosPixel()) )
+ SetPointer( PointerStyle::RefHand );
+ else if ( DrawMouseMove(rMEvt) ) // Reset pointer
+ return;
+ }
+ }
+
+ // In LOK case, avoid spurious "leavingwindow" mouse move events which has negative coordinates.
+ // Such events occur for some reason when a user is selecting a range, (even when not leaving the view area)
+ // with one or more other viewers in that sheet.
+ bool bSkipSelectionUpdate = comphelper::LibreOfficeKit::isActive() &&
+ rMEvt.IsLeaveWindow() && (aCurMousePos.X() < 0 || aCurMousePos.Y() < 0);
+
+ if (!bSkipSelectionUpdate)
+ mrViewData.GetView()->GetSelEngine()->SelMouseMove( rMEvt );
+}
+
+static void lcl_InitMouseEvent(css::awt::MouseEvent& rEvent, const MouseEvent& rEvt)
+{
+ rEvent.Modifiers = 0;
+ if ( rEvt.IsShift() )
+ rEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
+ if ( rEvt.IsMod1() )
+ rEvent.Modifiers |= css::awt::KeyModifier::MOD1;
+ if ( rEvt.IsMod2() )
+ rEvent.Modifiers |= css::awt::KeyModifier::MOD2;
+ if ( rEvt.IsMod3() )
+ rEvent.Modifiers |= css::awt::KeyModifier::MOD3;
+
+ rEvent.Buttons = 0;
+ if ( rEvt.IsLeft() )
+ rEvent.Buttons |= css::awt::MouseButton::LEFT;
+ if ( rEvt.IsRight() )
+ rEvent.Buttons |= css::awt::MouseButton::RIGHT;
+ if ( rEvt.IsMiddle() )
+ rEvent.Buttons |= css::awt::MouseButton::MIDDLE;
+
+ rEvent.X = rEvt.GetPosPixel().X();
+ rEvent.Y = rEvt.GetPosPixel().Y();
+ rEvent.ClickCount = rEvt.GetClicks();
+ rEvent.PopupTrigger = false;
+}
+
+bool ScGridWindow::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bDone = false;
+ NotifyEventType nType = rNEvt.GetType();
+ if ( nType == NotifyEventType::MOUSEBUTTONUP || nType == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ if (pWindow == this)
+ {
+ SfxViewFrame& rViewFrame = mrViewData.GetViewShell()->GetViewFrame();
+ css::uno::Reference<css::frame::XController> xController = rViewFrame.GetFrame().GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp && pImp->IsMouseListening())
+ {
+ css::awt::MouseEvent aEvent;
+ lcl_InitMouseEvent( aEvent, *rNEvt.GetMouseEvent() );
+ if ( rNEvt.GetWindow() )
+ aEvent.Source = rNEvt.GetWindow()->GetComponentInterface();
+ if ( nType == NotifyEventType::MOUSEBUTTONDOWN)
+ bDone = pImp->MousePressed( aEvent );
+ else
+ bDone = pImp->MouseReleased( aEvent );
+ }
+ }
+ }
+ }
+ if (bDone) // event consumed by a listener
+ {
+ if ( nType == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ const MouseEvent* pMouseEvent = rNEvt.GetMouseEvent();
+ if ( pMouseEvent->IsRight() && pMouseEvent->GetClicks() == 1 )
+ {
+ // If a listener returned true for a right-click call, also prevent opening the context menu
+ // (this works only if the context menu is opened on mouse-down)
+ nMouseStatus = SC_GM_IGNORE;
+ }
+ }
+
+ return true;
+ }
+ else
+ return Window::PreNotify( rNEvt );
+}
+
+void ScGridWindow::Tracking( const TrackingEvent& rTEvt )
+{
+ // Since the SelectionEngine does not track, the events have to be
+ // handed to the different MouseHandler...
+
+ const MouseEvent& rMEvt = rTEvt.GetMouseEvent();
+
+ if ( rTEvt.IsTrackingCanceled() ) // Cancel everything...
+ {
+ if (!mrViewData.GetView()->IsInActivatePart() && !SC_MOD()->IsRefDialogOpen())
+ {
+ if (bDPMouse)
+ bDPMouse = false; // Paint for each bDragRect
+ if (bDragRect)
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ if (bRFMouse)
+ {
+ RFMouseMove( rMEvt, true ); // Not possible to cancel properly...
+ bRFMouse = false;
+ }
+ if (nPagebreakMouse)
+ {
+ bPagebreakDrawn = false;
+ UpdateDragRectOverlay();
+ nPagebreakMouse = SC_PD_NONE;
+ }
+
+ SetPointer( PointerStyle::Arrow );
+ StopMarking();
+ MouseButtonUp( rMEvt ); // With status SC_GM_IGNORE from StopMarking
+
+ bool bRefMode = mrViewData.IsRefMode();
+ if (bRefMode)
+ SC_MOD()->EndReference(); // Do not let the Dialog remain minimized
+ }
+ }
+ else if ( rTEvt.IsTrackingEnded() )
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // MouseButtonUp always with matching buttons (eg for test tool, # 63148 #)
+ // The tracking event will indicate if it was completed and not canceled.
+ MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
+ rMEvt.GetMode(), nButtonDown, rMEvt.GetModifier() );
+ MouseButtonUp( aUpEvt );
+ }
+ }
+ else
+ MouseMove( rMEvt );
+}
+
+void ScGridWindow::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
+{
+ if (mpFilterBox || nPagebreakMouse)
+ return;
+
+ HideNoteMarker();
+
+ CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
+
+ if (bEEMouse && mrViewData.HasEditView( eWhich ))
+ {
+ EditView* pEditView;
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+
+ // don't remove the edit view while switching views
+ ScModule* pScMod = SC_MOD();
+ pScMod->SetInEditCommand( true );
+
+ pEditView->Command( aDragEvent );
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl();
+ if (pHdl)
+ pHdl->DataChanged();
+
+ pScMod->SetInEditCommand( false );
+ if (!mrViewData.IsActive()) // dropped to different view?
+ {
+ ScInputHandler* pViewHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
+ if ( pViewHdl && mrViewData.HasEditView( eWhich ) )
+ {
+ pViewHdl->CancelHandler();
+ ShowCursor(); // missing from KillEditView
+ }
+ }
+ }
+ else
+ if ( !DrawCommand(aDragEvent) )
+ mrViewData.GetView()->GetSelEngine()->Command( aDragEvent );
+}
+
+static void lcl_SetTextCursorPos( ScViewData& rViewData, ScSplitPos eWhich, vcl::Window* pWin )
+{
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ tools::Rectangle aEditArea = rViewData.GetEditArea( eWhich, nCol, nRow, pWin, nullptr, true );
+ aEditArea.SetRight( aEditArea.Left() );
+ aEditArea = pWin->PixelToLogic( aEditArea );
+ pWin->SetCursorRect( &aEditArea );
+}
+
+void ScGridWindow::Command( const CommandEvent& rCEvt )
+{
+ // The command event is send to the window after a possible context
+ // menu from an inplace client is closed. Now we have the chance to
+ // deactivate the inplace client without any problem regarding parent
+ // windows and code on the stack.
+ CommandEventId nCmd = rCEvt.GetCommand();
+ ScTabViewShell* pTabViewSh = mrViewData.GetViewShell();
+ SfxInPlaceClient* pClient = pTabViewSh->GetIPClient();
+ if ( pClient &&
+ pClient->IsObjectInPlaceActive() &&
+ nCmd == CommandEventId::ContextMenu )
+ {
+ pTabViewSh->DeactivateOle();
+ return;
+ }
+
+ ScModule* pScMod = SC_MOD();
+ OSL_ENSURE( nCmd != CommandEventId::StartDrag, "ScGridWindow::Command called with CommandEventId::StartDrag" );
+
+ if (nCmd == CommandEventId::ModKeyChange)
+ {
+ Window::Command(rCEvt);
+ return;
+ }
+
+ if ( nCmd == CommandEventId::StartExtTextInput ||
+ nCmd == CommandEventId::EndExtTextInput ||
+ nCmd == CommandEventId::ExtTextInput ||
+ nCmd == CommandEventId::CursorPos ||
+ nCmd == CommandEventId::QueryCharPosition )
+ {
+ bool bEditView = mrViewData.HasEditView( eWhich );
+ if (!bEditView)
+ {
+ // only if no cell editview is active, look at drawview
+ SdrView* pSdrView = mrViewData.GetView()->GetScDrawView();
+ if ( pSdrView )
+ {
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if ( pOlView && pOlView->GetWindow() == this )
+ {
+ pOlView->Command( rCEvt );
+ return; // done
+ }
+ }
+ }
+
+ if ( nCmd == CommandEventId::CursorPos && !bEditView )
+ {
+ // CURSORPOS may be called without following text input,
+ // to set the input method window position
+ // -> input mode must not be started,
+ // manually calculate text insert position if not in input mode
+
+ lcl_SetTextCursorPos( mrViewData, eWhich, this );
+ return;
+ }
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
+ if ( pHdl )
+ {
+ pHdl->InputCommand( rCEvt );
+ return; // done
+ }
+
+ Window::Command( rCEvt );
+ return;
+ }
+
+ if ( nCmd == CommandEventId::PasteSelection )
+ {
+ if ( bEEMouse )
+ {
+ // EditEngine handles selection in MouseButtonUp - no action
+ // needed in command handler
+ }
+ else
+ {
+ PasteSelection( rCEvt.GetMousePosPixel() );
+ }
+ return;
+ }
+
+ if ( nCmd == CommandEventId::InputLanguageChange )
+ {
+ // #i55929# Font and font size state depends on input language if nothing is selected,
+ // so the slots have to be invalidated when the input language is changed.
+
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ return;
+ }
+
+ if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
+ {
+ bool bDone = mrViewData.GetView()->ScrollCommand( rCEvt, eWhich );
+ if (!bDone)
+ Window::Command(rCEvt);
+ return;
+ }
+
+ if (nCmd == CommandEventId::GestureZoom)
+ {
+ bool bDone = mrViewData.GetView()->GestureZoomCommand(rCEvt);
+ if (!bDone)
+ Window::Command(rCEvt);
+ return;
+ }
+
+ // #i7560# FormulaMode check is below scrolling - scrolling is allowed during formula input
+ bool bDisable = pScMod->IsFormulaMode() ||
+ pScMod->IsModalMode(mrViewData.GetSfxDocShell());
+ if (bDisable)
+ return;
+
+ if (nCmd != CommandEventId::ContextMenu || SC_MOD()->GetIsWaterCan())
+ return;
+
+ bool bMouse = rCEvt.IsMouseEvent();
+ if ( bMouse && nMouseStatus == SC_GM_IGNORE )
+ return;
+
+ if (mrViewData.IsAnyFillMode())
+ {
+ mrViewData.GetView()->StopRefMode();
+ mrViewData.ResetFillMode();
+ }
+ ReleaseMouse();
+ StopMarking();
+
+ Point aPosPixel = rCEvt.GetMousePosPixel();
+ Point aMenuPos = aPosPixel;
+
+ bool bPosIsInEditView = mrViewData.HasEditView(eWhich);
+ SCCOL nCellX = -1;
+ SCROW nCellY = -1;
+ mrViewData.GetPosFromPixel(aPosPixel.X(), aPosPixel.Y(), eWhich, nCellX, nCellY);
+ // GetPosFromPixel ignores the fact that when editing a cell, the cell might grow to cover
+ // other rows/columns. In addition, the mouse might now be outside the edited cell.
+ if (bPosIsInEditView)
+ {
+ if (nCellX >= mrViewData.GetEditViewCol() && nCellX <= mrViewData.GetEditEndCol())
+ nCellX = mrViewData.GetEditViewCol();
+ else
+ bPosIsInEditView = false;
+
+ if (nCellY >= mrViewData.GetEditViewRow() && nCellY <= mrViewData.GetEditEndRow())
+ nCellY = mrViewData.GetEditViewRow();
+ else
+ bPosIsInEditView = false;
+
+ if (!bPosIsInEditView)
+ {
+ // Close the edit view when moving outside of the edited cell
+ // to avoid showing the edit popup, or providing the wrong EditView to spellcheck.
+ ScInputHandler* pHdl = pScMod->GetInputHdl();
+ if (pHdl)
+ pHdl->EnterHandler();
+ }
+ }
+
+ bool bSpellError = false;
+ SCCOL nColSpellError = nCellX;
+
+ if ( bMouse )
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ bool bSelectAllowed = true;
+ if ( pProtect && pProtect->isProtected() )
+ {
+ // This sheet is protected. Check if a context menu is allowed on this cell.
+ bool bCellProtected = rDoc.HasAttrib(nCellX, nCellY, nTab, nCellX, nCellY, nTab, HasAttrFlags::Protected);
+ bool bSelProtected = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if (bCellProtected)
+ bSelectAllowed = bSelProtected;
+ else
+ bSelectAllowed = bSelUnprotected;
+ }
+ if (!bSelectAllowed)
+ // Selecting this cell is not allowed, neither is context menu.
+ return;
+
+ if (mpSpellCheckCxt)
+ {
+ // Find the first string to the left for spell checking in case the current cell is empty.
+ ScAddress aPos(nCellX, nCellY, nTab);
+ ScRefCellValue aSpellCheckCell(rDoc, aPos);
+ while (!bPosIsInEditView && aSpellCheckCell.getType() == CELLTYPE_NONE)
+ {
+ // Loop until we get the first non-empty cell in the row.
+ aPos.IncCol(-1);
+ if (aPos.Col() < 0)
+ break;
+
+ aSpellCheckCell.assign(rDoc, aPos);
+ }
+
+ if (aPos.Col() >= 0 && (aSpellCheckCell.getType() == CELLTYPE_STRING || aSpellCheckCell.getType() == CELLTYPE_EDIT))
+ nColSpellError = aPos.Col();
+
+ // Is there possibly a misspelled word somewhere in the cell?
+ // A "yes" does not mean that the word under the mouse pointer is wrong though.
+ bSpellError = (mpSpellCheckCxt->isMisspelled(nColSpellError, nCellY));
+ // Avoid situations where selecting the cell-with-wrong-spelling would be bad
+ if (bSpellError)
+ {
+ // When the mouse is over an empty cell, text with spelling errors
+ // potentially could have overflowed underneath the mouse pointer
+ if (nColSpellError != nCellX)
+ {
+ // If the mouse is over a selected cell, only consider spell-checking
+ // if the cell with the misspelling is also selected. tdf#157038
+ if (mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY))
+ bSpellError = mrViewData.GetMarkData().IsCellMarked(nColSpellError, nCellY);
+ }
+ }
+ }
+
+ // #i18735# First select the item under the mouse pointer.
+ // This can change the selection, and the view state (edit mode, etc).
+ SelectForContextMenu(aPosPixel, bSpellError ? nColSpellError : nCellX, nCellY);
+ }
+
+ bool bDone = false;
+ bool bEdit = mrViewData.HasEditView(eWhich);
+
+ if ( !bEdit )
+ {
+ // Edit cell with spelling errors?
+ // tdf#127341 the formerly used GetEditUrl(aPosPixel) additionally
+ // to bSpellError activated EditMode here for right-click on URL
+ // which prevents the regular context-menu from appearing. Since this
+ // is more expected than the context-menu for editing an URL, I removed
+ // this. If this was wanted and can be argued it might be re-activated.
+ // For now, reduce to spelling errors - as the original comment above
+ // suggests.
+ if (bMouse && bSpellError)
+ {
+ // GetEditUrlOrError has already moved the Cursor
+
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ bEdit = mrViewData.HasEditView(eWhich); // Did it work?
+
+ OSL_ENSURE( bEdit, "Can not be switched in edit mode" );
+ }
+ }
+ if ( bEdit )
+ {
+ EditView* pEditView = mrViewData.GetEditView( eWhich ); // is then not 0
+
+ if ( !bMouse )
+ {
+ vcl::Cursor* pCur = pEditView->GetCursor();
+ if ( pCur )
+ {
+ Point aLogicPos = pCur->GetPos();
+ // use the position right of the cursor (spell popup is opened if
+ // the cursor is before the word, but not if behind it)
+ aLogicPos.AdjustX(pCur->GetWidth() );
+ aLogicPos.AdjustY(pCur->GetHeight() / 2 ); // center vertically
+ aMenuPos = LogicToPixel( aLogicPos );
+ }
+ }
+
+ // if edit mode was just started above, online spelling may be incomplete
+ pEditView->GetEditEngine()->CompleteOnlineSpelling();
+
+ // IsCursorAtWrongSpelledWord could be used for !bMouse
+ // if there was a corresponding ExecuteSpellPopup call
+
+ if (bSpellError)
+ {
+ // On OS/2 when clicking next to the Popup menu, the MouseButtonDown
+ // comes before the end of menu execute, thus the SetModified has to
+ // be done prior to this (Bug #40968#)
+ ScInputHandler* pHdl = pScMod->GetInputHdl();
+ if (pHdl)
+ pHdl->SetModified();
+
+ const OUString sOldText = pHdl ? pHdl->GetEditString() : "";
+
+ // Only done/shown if a misspelled word is actually under the mouse pointer.
+ Link<SpellCallbackInfo&,void> aLink = LINK( this, ScGridWindow, PopupSpellingHdl );
+ bDone = pEditView->ExecuteSpellPopup(aMenuPos, aLink);
+
+ // If the spelling is corrected, stop editing to flush any cached spelling info.
+ // Or, if no misspelled word at this position, and it wasn't initially in edit mode,
+ // then exit the edit mode in order to get the full context popup (not edit popup).
+ if (pHdl && (pHdl->GetEditString() != sOldText
+ || (!bDone && !bPosIsInEditView)))
+ {
+ pHdl->EnterHandler();
+ }
+
+ if (!bDone && nColSpellError != nCellX)
+ {
+ // NOTE: This call can change the selection, and the view state (edit mode, etc).
+ SelectForContextMenu(aPosPixel, nCellX, nCellY);
+ }
+ }
+ }
+ else if ( !bMouse )
+ {
+ // non-edit menu by keyboard -> use lower right of cell cursor position
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTabNo = mrViewData.GetTabNo();
+ bool bLayoutIsRTL = rDoc.IsLayoutRTL(nTabNo);
+
+ SCCOL nCurX = mrViewData.GetCurX();
+ SCROW nCurY = mrViewData.GetCurY();
+ aMenuPos = mrViewData.GetScrPos( nCurX, nCurY, eWhich, true );
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nCurX, nCurY, nSizeXPix, nSizeYPix );
+ // fdo#55432 take the correct position for RTL sheet
+ aMenuPos.AdjustX(bLayoutIsRTL ? -nSizeXPix : nSizeXPix );
+ aMenuPos.AdjustY(nSizeYPix );
+
+ ScTabViewShell* pViewSh = mrViewData.GetViewShell();
+ if (pViewSh)
+ {
+ // Is a draw object selected?
+
+ SdrView* pDrawView = pViewSh->GetScDrawView();
+ if (pDrawView && pDrawView->AreObjectsMarked())
+ {
+ // #100442#; the context menu should open in the middle of the selected objects
+ tools::Rectangle aSelectRect(LogicToPixel(pDrawView->GetAllMarkedBoundRect()));
+ aMenuPos = aSelectRect.Center();
+ }
+ }
+ }
+
+ if (bDone)
+ return;
+
+ SfxDispatcher::ExecutePopup( this, &aMenuPos );
+}
+
+void ScGridWindow::SelectForContextMenu( const Point& rPosPixel, SCCOL nCellX, SCROW nCellY )
+{
+ // #i18735# if the click was outside of the current selection,
+ // the cursor is moved or an object at the click position selected.
+ // (see SwEditWin::SelectMenuPosition in Writer)
+
+ ScTabView* pView = mrViewData.GetView();
+ ScDrawView* pDrawView = pView->GetScDrawView();
+
+ // check cell edit mode
+
+ if ( mrViewData.HasEditView(eWhich) )
+ {
+ ScModule* pScMod = SC_MOD();
+ SCCOL nEditStartCol = mrViewData.GetEditViewCol(); //! change to GetEditStartCol after calcrtl is integrated
+ SCROW nEditStartRow = mrViewData.GetEditViewRow();
+ SCCOL nEditEndCol = mrViewData.GetEditEndCol();
+ SCROW nEditEndRow = mrViewData.GetEditEndRow();
+
+ if ( nCellX >= nEditStartCol && nCellX <= nEditEndCol &&
+ nCellY >= nEditStartRow && nCellY <= nEditEndRow )
+ {
+ // handle selection within the EditView
+
+ EditView* pEditView = mrViewData.GetEditView( eWhich ); // not NULL (HasEditView)
+ EditEngine* pEditEngine = pEditView->GetEditEngine();
+ tools::Rectangle aOutputArea = pEditView->GetOutputArea();
+ tools::Rectangle aVisArea = pEditView->GetVisArea();
+
+ Point aTextPos = PixelToLogic( rPosPixel );
+ if ( pEditEngine->IsEffectivelyVertical() ) // have to manually transform position
+ {
+ aTextPos -= aOutputArea.TopRight();
+ tools::Long nTemp = -aTextPos.X();
+ aTextPos.setX( aTextPos.Y() );
+ aTextPos.setY( nTemp );
+ }
+ else
+ aTextPos -= aOutputArea.TopLeft();
+ aTextPos += aVisArea.TopLeft(); // position in the edit document
+
+ EPosition aDocPosition = pEditEngine->FindDocPosition(aTextPos);
+ ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
+ ESelection aSelection = pEditView->GetSelection();
+ aSelection.Adjust(); // needed for IsLess/IsGreater
+ if ( aCompare < aSelection || aCompare > aSelection )
+ {
+ // clicked outside the selected text - deselect and move text cursor
+ MouseEvent aEvent( rPosPixel );
+ pEditView->MouseButtonDown( aEvent );
+ pEditView->MouseButtonUp( aEvent );
+ pScMod->InputSelection( pEditView );
+ }
+
+ return; // clicked within the edit view - keep edit mode
+ }
+ else
+ {
+ // outside of the edit view - end edit mode, regardless of cell selection, then continue
+ pScMod->InputEnterHandler();
+ }
+ }
+
+ // check draw text edit mode
+
+ Point aLogicPos = PixelToLogic( rPosPixel ); // after cell edit mode is ended
+ if ( pDrawView && pDrawView->GetTextEditObject() && pDrawView->GetTextEditOutlinerView() )
+ {
+ OutlinerView* pOlView = pDrawView->GetTextEditOutlinerView();
+ tools::Rectangle aOutputArea = pOlView->GetOutputArea();
+ if ( aOutputArea.Contains( aLogicPos ) )
+ {
+ // handle selection within the OutlinerView
+
+ Outliner* pOutliner = pOlView->GetOutliner();
+ const EditEngine& rEditEngine = pOutliner->GetEditEngine();
+ tools::Rectangle aVisArea = pOlView->GetVisArea();
+
+ Point aTextPos = aLogicPos;
+ if ( pOutliner->IsVertical() ) // have to manually transform position
+ {
+ aTextPos -= aOutputArea.TopRight();
+ tools::Long nTemp = -aTextPos.X();
+ aTextPos.setX( aTextPos.Y() );
+ aTextPos.setY( nTemp );
+ }
+ else
+ aTextPos -= aOutputArea.TopLeft();
+ aTextPos += aVisArea.TopLeft(); // position in the edit document
+
+ EPosition aDocPosition = rEditEngine.FindDocPosition(aTextPos);
+ ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
+ ESelection aSelection = pOlView->GetSelection();
+ aSelection.Adjust(); // needed for IsLess/IsGreater
+ if ( aCompare < aSelection || aCompare > aSelection )
+ {
+ // clicked outside the selected text - deselect and move text cursor
+ // use DrawView to allow extra handling there (none currently)
+ MouseEvent aEvent( rPosPixel );
+ pDrawView->MouseButtonDown( aEvent, GetOutDev() );
+ pDrawView->MouseButtonUp( aEvent, GetOutDev() );
+ }
+
+ return; // clicked within the edit area - keep edit mode
+ }
+ else
+ {
+ // Outside of the edit area - end text edit mode, then continue.
+ // DrawDeselectAll also ends text edit mode and updates the shells.
+ // If the click was on the edited object, it will be selected again below.
+ pView->DrawDeselectAll();
+ }
+ }
+
+ // look for existing selection
+
+ bool bHitSelected = false;
+ if ( pDrawView && pDrawView->IsMarkedObjHit( aLogicPos ) )
+ {
+ // clicked on selected object -> don't change anything
+ bHitSelected = true;
+ }
+ else if ( mrViewData.GetMarkData().IsCellMarked(nCellX, nCellY) )
+ {
+ // clicked on selected cell -> don't change anything
+ bHitSelected = true;
+ }
+
+ // select drawing object or move cell cursor
+
+ if ( bHitSelected )
+ return;
+
+ bool bWasDraw = ( pDrawView && pDrawView->AreObjectsMarked() );
+ bool bHitDraw = false;
+ if ( pDrawView )
+ {
+ pDrawView->UnmarkAllObj();
+ // Unlock the Internal Layer in order to activate the context menu.
+ // re-lock in ScDrawView::MarkListHasChanged()
+ lcl_UnLockComment(pDrawView, aLogicPos, mrViewData);
+ bHitDraw = pDrawView->MarkObj( aLogicPos );
+ // draw shell is activated in MarkListHasChanged
+ }
+ if ( !bHitDraw )
+ {
+ pView->Unmark();
+ pView->SetCursor(nCellX, nCellY);
+ if ( bWasDraw )
+ mrViewData.GetViewShell()->SetDrawShell( false ); // switch shells
+ }
+}
+
+void ScGridWindow::KeyInput(const KeyEvent& rKEvt)
+{
+ // Cursor control for ref input dialog
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+#ifdef DBG_UTIL
+
+ if (rKeyCode.IsMod1() && rKeyCode.IsShift())
+ {
+ if (rKeyCode.GetCode() == KEY_F12)
+ {
+ dumpColumnInformationPixel();
+ }
+ else if (rKeyCode.GetCode() == KEY_F11)
+ {
+ dumpGraphicInformation();
+ }
+ else if (rKeyCode.GetCode() == KEY_F10)
+ {
+ dumpColumnInformationHmm();
+ }
+ else if (rKeyCode.GetCode() == KEY_F6)
+ {
+ dumpCellProperties();
+ }
+ else if (rKeyCode.GetCode() == KEY_F8)
+ {
+ dumpColumnCellStorage();
+ }
+ else if (rKeyCode.GetCode() == KEY_F7)
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ auto& rMapper = rDoc.GetExternalDataMapper();
+ for (auto& itr : rMapper.getDataSources())
+ {
+ itr.refresh(&rDoc);
+ }
+ return;
+ }
+ }
+
+#endif
+
+ if( SC_MOD()->IsRefDialogOpen() )
+ {
+ if( !rKeyCode.GetModifier() && (rKeyCode.GetCode() == KEY_F2) )
+ {
+ SC_MOD()->EndReference();
+ }
+ else if( mrViewData.GetViewShell()->MoveCursorKeyInput( rKEvt ) )
+ {
+ ScRange aRef(
+ mrViewData.GetRefStartX(), mrViewData.GetRefStartY(), mrViewData.GetRefStartZ(),
+ mrViewData.GetRefEndX(), mrViewData.GetRefEndY(), mrViewData.GetRefEndZ() );
+ SC_MOD()->SetReference( aRef, mrViewData.GetDocument() );
+ }
+ mrViewData.GetViewShell()->SelectionChanged();
+ return ;
+ }
+ else if( rKeyCode.GetCode() == KEY_RETURN && mrViewData.IsPasteMode()
+ && SC_MOD()->GetInputOptions().GetEnterPasteMode() )
+ {
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+ ScClipUtil::PasteFromClipboard( mrViewData, pTabViewShell, true );
+
+ // Clear clipboard content.
+ uno::Reference<datatransfer::clipboard::XClipboard> xSystemClipboard =
+ GetClipboard();
+ if (xSystemClipboard.is())
+ {
+ xSystemClipboard->setContents(
+ uno::Reference<datatransfer::XTransferable>(),
+ uno::Reference<datatransfer::clipboard::XClipboardOwner>());
+ }
+
+ // hide the border around the copy source
+ mrViewData.SetPasteMode( ScPasteFlags::NONE );
+ // Clear CopySourceOverlay in each window of a split/frozen tabview
+ mrViewData.GetView()->UpdateCopySourceOverlay();
+ return;
+ }
+ // if semi-modeless SfxChildWindow dialog above, then no KeyInputs:
+ else if( !mrViewData.IsAnyFillMode() )
+ {
+ if (rKeyCode.GetCode() == KEY_ESCAPE)
+ {
+ mrViewData.SetPasteMode( ScPasteFlags::NONE );
+ // Clear CopySourceOverlay in each window of a split/frozen tabview
+ mrViewData.GetView()->UpdateCopySourceOverlay();
+ }
+ // query for existing note marker before calling ViewShell's keyboard handling
+ // which may remove the marker
+ bool bHadKeyMarker = mpNoteMarker && mpNoteMarker->IsByKeyboard();
+ ScTabViewShell* pViewSh = mrViewData.GetViewShell();
+
+ if (mrViewData.GetDocShell()->GetProgress())
+ return;
+
+ if (DrawKeyInput(rKEvt, this))
+ {
+ const vcl::KeyCode& rLclKeyCode = rKEvt.GetKeyCode();
+ if (rLclKeyCode.GetCode() == KEY_DOWN
+ || rLclKeyCode.GetCode() == KEY_UP
+ || rLclKeyCode.GetCode() == KEY_LEFT
+ || rLclKeyCode.GetCode() == KEY_RIGHT)
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_X);
+ rBindings.Invalidate(SID_ATTR_TRANSFORM_POS_Y);
+ }
+ return;
+ }
+
+ if (!mrViewData.GetView()->IsDrawSelMode() && !DrawHasMarkedObj()) // No entries in draw mode
+ { //! check DrawShell !!!
+ if (pViewSh->TabKeyInput(rKEvt))
+ return;
+ }
+ else
+ if (pViewSh->SfxViewShell::KeyInput(rKEvt)) // from SfxViewShell
+ return;
+
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ if ( aCode.GetCode() == KEY_ESCAPE && aCode.GetModifier() == 0 )
+ {
+ if ( bHadKeyMarker )
+ HideNoteMarker();
+ else
+ pViewSh->Escape();
+ return;
+ }
+ if ( aCode.GetCode() == KEY_F1 && aCode.GetModifier() == KEY_MOD1 )
+ {
+ // ctrl-F1 shows or hides the note or redlining info for the cursor position
+ // (hard-coded because F1 can't be configured)
+
+ if ( bHadKeyMarker )
+ HideNoteMarker(); // hide when previously visible
+ else
+ ShowNoteMarker( mrViewData.GetCurX(), mrViewData.GetCurY(), true );
+ return;
+ }
+ if (aCode.GetCode() == KEY_BRACKETLEFT && aCode.GetModifier() == KEY_MOD1)
+ {
+ pViewSh->DetectiveMarkPred();
+ return;
+ }
+ if (aCode.GetCode() == KEY_BRACKETRIGHT && aCode.GetModifier() == KEY_MOD1)
+ {
+ pViewSh->DetectiveMarkSucc();
+ return;
+ }
+
+ }
+
+ Window::KeyInput(rKEvt);
+}
+
+OUString ScGridWindow::GetSurroundingText() const
+{
+ bool bEditView = mrViewData.HasEditView(eWhich);
+ if (bEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ return pHdl->GetSurroundingText();
+ }
+ else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
+ {
+ // if no cell editview is active, look at drawview
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ return pOlView->GetSurroundingText();
+ }
+
+ return Window::GetSurroundingText();
+}
+
+Selection ScGridWindow::GetSurroundingTextSelection() const
+{
+ bool bEditView = mrViewData.HasEditView(eWhich);
+ if (bEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ return pHdl->GetSurroundingTextSelection();
+ }
+ else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
+ {
+ // if no cell editview is active, look at drawview
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ return pOlView->GetSurroundingTextSelection();
+ }
+
+ return Window::GetSurroundingTextSelection();
+}
+
+bool ScGridWindow::DeleteSurroundingText(const Selection& rSelection)
+{
+ bool bEditView = mrViewData.HasEditView(eWhich);
+ if (bEditView)
+ {
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl(mrViewData.GetViewShell());
+ if (pHdl)
+ return pHdl->DeleteSurroundingText(rSelection);
+ }
+ else if (SdrView* pSdrView = mrViewData.GetView()->GetScDrawView())
+ {
+ // if no cell editview is active, look at drawview
+ OutlinerView* pOlView = pSdrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ return pOlView->DeleteSurroundingText(rSelection);
+ }
+
+ return Window::DeleteSurroundingText(rSelection);
+}
+
+void ScGridWindow::StopMarking()
+{
+ DrawEndAction(); // Cancel Select/move on Drawing-Layer
+
+ if (nButtonDown)
+ {
+ mrViewData.GetMarkData().SetMarking(false);
+ nMouseStatus = SC_GM_IGNORE;
+ }
+}
+
+void ScGridWindow::UpdateInputContext()
+{
+ bool bReadOnly = mrViewData.GetDocShell()->IsReadOnly();
+ InputContextFlags nOptions = bReadOnly ? InputContextFlags::NONE : ( InputContextFlags::Text | InputContextFlags::ExtText );
+
+ // when font from InputContext is used,
+ // it must be taken from the cursor position's cell attributes
+
+ InputContext aContext;
+ aContext.SetOptions( nOptions );
+ SetInputContext( aContext );
+}
+
+ // sensitive range (Pixel)
+#define SCROLL_SENSITIVE 20
+
+void ScGridWindow::DropScroll( const Point& rMousePos )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ Size aSize = GetOutputSizePixel();
+
+ if (aSize.Width() > SCROLL_SENSITIVE * 3)
+ {
+ if ( rMousePos.X() < SCROLL_SENSITIVE && mrViewData.GetPosX(WhichH(eWhich)) > 0 )
+ nDx = -1;
+ if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE
+ && mrViewData.GetPosX(WhichH(eWhich)) < rDoc.MaxCol() )
+ nDx = 1;
+ }
+ if (aSize.Height() > SCROLL_SENSITIVE * 3)
+ {
+ if ( rMousePos.Y() < SCROLL_SENSITIVE && mrViewData.GetPosY(WhichV(eWhich)) > 0 )
+ nDy = -1;
+ if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE
+ && mrViewData.GetPosY(WhichV(eWhich)) < rDoc.MaxRow() )
+ nDy = 1;
+ }
+
+ if ( nDx != 0 || nDy != 0 )
+ {
+ if ( nDx != 0 )
+ mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 )
+ mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+ }
+}
+
+static bool lcl_TestScenarioRedliningDrop( const ScDocument* pDoc, const ScRange& aDragRange)
+{
+ // Test, if a scenario is affected by a drop when turing on RedLining,
+ bool bReturn = false;
+ SCTAB nTab = aDragRange.aStart.Tab();
+ SCTAB nTabCount = pDoc->GetTableCount();
+
+ if(pDoc->GetChangeTrack()!=nullptr)
+ {
+ if( pDoc->IsScenario(nTab) && pDoc->HasScenarioRange(nTab, aDragRange))
+ {
+ bReturn = true;
+ }
+ else
+ {
+ for(SCTAB i=nTab+1; i<nTabCount && pDoc->IsScenario(i); i++)
+ {
+ if(pDoc->HasScenarioRange(i, aDragRange))
+ {
+ bReturn = true;
+ break;
+ }
+ }
+ }
+ }
+ return bReturn;
+}
+
+static ScRange lcl_MakeDropRange( const ScDocument& rDoc, SCCOL nPosX, SCROW nPosY, SCTAB nTab, const ScRange& rSource )
+{
+ SCCOL nCol1 = nPosX;
+ SCCOL nCol2 = nCol1 + ( rSource.aEnd.Col() - rSource.aStart.Col() );
+ if ( nCol2 > rDoc.MaxCol() )
+ {
+ nCol1 -= nCol2 - rDoc.MaxCol();
+ nCol2 = rDoc.MaxCol();
+ }
+ SCROW nRow1 = nPosY;
+ SCROW nRow2 = nRow1 + ( rSource.aEnd.Row() - rSource.aStart.Row() );
+ if ( nRow2 > rDoc.MaxRow() )
+ {
+ nRow1 -= nRow2 - rDoc.MaxRow();
+ nRow2 = rDoc.MaxRow();
+ }
+
+ return ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
+}
+
+sal_Int8 ScGridWindow::AcceptPrivateDrop( const AcceptDropEvent& rEvt, const ScDragData& rData )
+{
+ if ( rEvt.mbLeaving )
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ return rEvt.mnAction;
+ }
+
+ if ( rData.pCellTransfer )
+ {
+ // Don't move source that would include filtered rows.
+ if ((rEvt.mnAction & DND_ACTION_MOVE) && rData.pCellTransfer->HasFilteredRows())
+ {
+ if (bDragRect)
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+
+ Point aPos = rEvt.maPosPixel;
+
+ ScDocument* pSourceDoc = rData.pCellTransfer->GetSourceDocument();
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ if (pSourceDoc == &rThisDoc)
+ {
+ OUString aName;
+ if ( rThisDoc.HasChartAtPoint(mrViewData.GetTabNo(), PixelToLogic(aPos), aName ))
+ {
+ if (bDragRect) // Remove rectangle
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+
+ //! highlight chart? (selection border?)
+
+ sal_Int8 nRet = rEvt.mnAction;
+ return nRet;
+ }
+ }
+
+ if (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) // whole sheet?
+ {
+ bool bOk = rThisDoc.IsDocEditable();
+ return bOk ? rEvt.mnAction : 0; // don't draw selection frame
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ ScRange aSourceRange = rData.pCellTransfer->GetRange();
+ SCCOL nSourceStartX = aSourceRange.aStart.Col();
+ SCROW nSourceStartY = aSourceRange.aStart.Row();
+ SCCOL nSourceEndX = aSourceRange.aEnd.Col();
+ SCROW nSourceEndY = aSourceRange.aEnd.Row();
+ SCCOL nSizeX = nSourceEndX - nSourceStartX + 1;
+ SCROW nSizeY = nSourceEndY - nSourceStartY + 1;
+
+ if ( rEvt.mnAction != DND_ACTION_MOVE )
+ nSizeY = rData.pCellTransfer->GetNonFilteredRows(); // copy/link: no filtered rows
+
+ SCCOL nNewDragX = nPosX - rData.pCellTransfer->GetDragHandleX();
+ if (nNewDragX<0) nNewDragX=0;
+ if (nNewDragX+(nSizeX-1) > rThisDoc.MaxCol())
+ nNewDragX = rThisDoc.MaxCol()-(nSizeX-1);
+ SCROW nNewDragY = nPosY - rData.pCellTransfer->GetDragHandleY();
+ if (nNewDragY<0) nNewDragY=0;
+ if (nNewDragY+(nSizeY-1) > rThisDoc.MaxRow())
+ nNewDragY = rThisDoc.MaxRow()-(nSizeY-1);
+
+ // don't break scenario ranges, don't drop on filtered
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScRange aDropRange = lcl_MakeDropRange( rThisDoc, nNewDragX, nNewDragY, nTab, aSourceRange );
+ if ( lcl_TestScenarioRedliningDrop( &rThisDoc, aDropRange ) ||
+ lcl_TestScenarioRedliningDrop( pSourceDoc, aSourceRange ) ||
+ ScViewUtil::HasFiltered( aDropRange, rThisDoc) )
+ {
+ if (bDragRect)
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+
+ InsCellCmd eDragInsertMode = INS_NONE;
+ Window::PointerState aState = GetPointerState();
+
+ // check for datapilot item sorting
+ ScDPObject* pDPObj = nullptr;
+ if ( &rThisDoc == pSourceDoc && ( pDPObj = rThisDoc.GetDPAtCursor( nNewDragX, nNewDragY, nTab ) ) != nullptr )
+ {
+ // drop on DataPilot table: sort or nothing
+
+ bool bDPSort = false;
+ if ( rThisDoc.GetDPAtCursor( nSourceStartX, nSourceStartY, aSourceRange.aStart.Tab() ) == pDPObj )
+ {
+ sheet::DataPilotTableHeaderData aDestData;
+ pDPObj->GetHeaderPositionData( ScAddress(nNewDragX, nNewDragY, nTab), aDestData );
+ bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
+
+ // look through the source range
+ for (SCROW nRow = aSourceRange.aStart.Row(); bValid && nRow <= aSourceRange.aEnd.Row(); ++nRow )
+ for (SCCOL nCol = aSourceRange.aStart.Col(); bValid && nCol <= aSourceRange.aEnd.Col(); ++nCol )
+ {
+ sheet::DataPilotTableHeaderData aSourceData;
+ pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, aSourceRange.aStart.Tab() ), aSourceData );
+ if ( aSourceData.Dimension != aDestData.Dimension || aSourceData.MemberName.isEmpty() )
+ bValid = false; // empty (subtotal) or different field
+ }
+
+ if ( bValid )
+ {
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
+ const ScDPSaveDimension* pDim = pDPObj->GetSaveData()->GetExistingDimensionByName( aDimName );
+ if ( pDim )
+ {
+ ScRange aOutRange = pDPObj->GetOutRange();
+
+ sheet::DataPilotFieldOrientation nOrient = pDim->GetOrientation();
+ if ( nOrient == sheet::DataPilotFieldOrientation_COLUMN )
+ {
+ eDragInsertMode = INS_CELLSRIGHT;
+ nSizeY = aOutRange.aEnd.Row() - nNewDragY + 1;
+ bDPSort = true;
+ }
+ else if ( nOrient == sheet::DataPilotFieldOrientation_ROW )
+ {
+ eDragInsertMode = INS_CELLSDOWN;
+ nSizeX = aOutRange.aEnd.Col() - nNewDragX + 1;
+ bDPSort = true;
+ }
+ }
+ }
+ }
+
+ if ( !bDPSort )
+ {
+ // no valid sorting in a DataPilot table -> disallow
+ if ( bDragRect )
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+ }
+ else if ( aState.mnState & KEY_MOD2 )
+ {
+ if ( &rThisDoc == pSourceDoc && nTab == aSourceRange.aStart.Tab() )
+ {
+ tools::Long nDeltaX = std::abs( static_cast< tools::Long >( nNewDragX - nSourceStartX ) );
+ tools::Long nDeltaY = std::abs( static_cast< tools::Long >( nNewDragY - nSourceStartY ) );
+ if ( nDeltaX <= nDeltaY )
+ {
+ eDragInsertMode = INS_CELLSDOWN;
+ }
+ else
+ {
+ eDragInsertMode = INS_CELLSRIGHT;
+ }
+
+ if ( ( eDragInsertMode == INS_CELLSDOWN && nNewDragY <= nSourceEndY &&
+ ( nNewDragX + nSizeX - 1 ) >= nSourceStartX && nNewDragX <= nSourceEndX &&
+ ( nNewDragX != nSourceStartX || nNewDragY >= nSourceStartY ) ) ||
+ ( eDragInsertMode == INS_CELLSRIGHT && nNewDragX <= nSourceEndX &&
+ ( nNewDragY + nSizeY - 1 ) >= nSourceStartY && nNewDragY <= nSourceEndY &&
+ ( nNewDragY != nSourceStartY || nNewDragX >= nSourceStartX ) ) )
+ {
+ if ( bDragRect )
+ {
+ bDragRect = false;
+ UpdateDragRectOverlay();
+ }
+ return DND_ACTION_NONE;
+ }
+ }
+ else
+ {
+ if ( static_cast< tools::Long >( nSizeX ) >= static_cast< tools::Long >( nSizeY ) )
+ {
+ eDragInsertMode = INS_CELLSDOWN;
+
+ }
+ else
+ {
+ eDragInsertMode = INS_CELLSRIGHT;
+ }
+ }
+ }
+
+ if ( nNewDragX != nDragStartX || nNewDragY != nDragStartY ||
+ nDragStartX+nSizeX-1 != nDragEndX || nDragStartY+nSizeY-1 != nDragEndY ||
+ !bDragRect || eDragInsertMode != meDragInsertMode )
+ {
+ nDragStartX = nNewDragX;
+ nDragStartY = nNewDragY;
+ nDragEndX = nDragStartX+nSizeX-1;
+ nDragEndY = nDragStartY+nSizeY-1;
+ bDragRect = true;
+ meDragInsertMode = eDragInsertMode;
+
+ UpdateDragRectOverlay();
+ }
+ }
+
+ return rEvt.mnAction;
+}
+
+sal_Int8 ScGridWindow::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ const ScDragData& rData = SC_MOD()->GetDragData();
+ if ( rEvt.mbLeaving )
+ {
+ DrawMarkDropObj( nullptr );
+ if ( rData.pCellTransfer )
+ return AcceptPrivateDrop( rEvt, rData ); // hide drop marker for internal D&D
+ else
+ return rEvt.mnAction;
+ }
+
+ if ( mrViewData.GetDocShell()->IsReadOnly() )
+ return DND_ACTION_NONE;
+
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if (rData.pCellTransfer)
+ {
+ ScRange aSource = rData.pCellTransfer->GetRange();
+ if ( aSource.aStart.Col() != 0 || aSource.aEnd.Col() != rThisDoc.MaxCol() ||
+ aSource.aStart.Row() != 0 || aSource.aEnd.Row() != rThisDoc.MaxRow() )
+ DropScroll( rEvt.maPosPixel );
+
+ nRet = AcceptPrivateDrop( rEvt, rData );
+ }
+ else
+ {
+ if ( !rData.aLinkDoc.isEmpty() )
+ {
+ OUString aThisName;
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ if (pDocSh && pDocSh->HasName())
+ aThisName = pDocSh->GetMedium()->GetName();
+
+ if ( rData.aLinkDoc != aThisName )
+ nRet = rEvt.mnAction;
+ }
+ else if (!rData.aJumpTarget.isEmpty())
+ {
+ // internal bookmarks (from Navigator)
+ // local jumps from an unnamed document are possible only within a document
+
+ if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
+ nRet = rEvt.mnAction;
+ }
+ else
+ {
+ sal_Int8 nMyAction = rEvt.mnAction;
+
+ // clear DND_ACTION_LINK when other actions are set. The usage below cannot handle
+ // multiple set values
+ if((nMyAction & DND_ACTION_LINK) && (nMyAction & DND_ACTION_COPYMOVE))
+ {
+ nMyAction &= ~DND_ACTION_LINK;
+ }
+
+ if ( !rData.pDrawTransfer ||
+ !IsMyModel(rData.pDrawTransfer->GetDragSourceView()) ) // drawing within the document
+ if ( rEvt.mbDefault && nMyAction == DND_ACTION_MOVE )
+ nMyAction = DND_ACTION_COPY;
+
+ SdrObject* pHitObj = rThisDoc.GetObjectAtPoint(
+ mrViewData.GetTabNo(), PixelToLogic(rEvt.maPosPixel) );
+ if ( pHitObj && nMyAction == DND_ACTION_LINK )
+ {
+ if ( IsDropFormatSupported(SotClipboardFormatId::SVXB)
+ || IsDropFormatSupported(SotClipboardFormatId::GDIMETAFILE)
+ || IsDropFormatSupported(SotClipboardFormatId::PNG)
+ || IsDropFormatSupported(SotClipboardFormatId::BITMAP) )
+ {
+ // graphic dragged onto drawing object
+ DrawMarkDropObj( pHitObj );
+ nRet = nMyAction;
+ }
+ }
+ if (!nRet)
+ {
+ DrawMarkDropObj(nullptr);
+
+ switch ( nMyAction )
+ {
+ case DND_ACTION_COPY:
+ case DND_ACTION_MOVE:
+ case DND_ACTION_COPYMOVE:
+ {
+ bool bMove = ( nMyAction == DND_ACTION_MOVE );
+ if ( IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::EMBED_SOURCE_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::STRING ) ||
+ IsDropFormatSupported( SotClipboardFormatId::STRING_TSVC ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SYLK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::HTML ) ||
+ IsDropFormatSupported( SotClipboardFormatId::HTML_SIMPLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::DIF ) ||
+ IsDropFormatSupported( SotClipboardFormatId::DRAWING ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SVXB ) ||
+ IsDropFormatSupported( SotClipboardFormatId::RTF ) ||
+ IsDropFormatSupported( SotClipboardFormatId::RICHTEXT ) ||
+ IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::PNG ) ||
+ IsDropFormatSupported( SotClipboardFormatId::BITMAP ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SBA_DATAEXCHANGE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) ||
+ ( !bMove && (
+ IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
+ IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) ) ) )
+ {
+ nRet = nMyAction;
+ }
+ }
+ break;
+ case DND_ACTION_LINK:
+ if ( IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK_SOURCE_OLE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::LINK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
+ IsDropFormatSupported( SotClipboardFormatId::SOLK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) ||
+ IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ) ||
+ IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
+ {
+ nRet = nMyAction;
+ }
+ break;
+ }
+
+ if ( nRet )
+ {
+ // Simple check for protection: It's not known here if the drop will result
+ // in cells or drawing objects (some formats can be both) and how many cells
+ // the result will be. But if IsFormatEditable for the drop cell position
+ // is sal_False (ignores matrix formulas), nothing can be pasted, so the drop
+ // can already be rejected here.
+
+ Point aPos = rEvt.maPosPixel;
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ ScEditableTester aTester( rDoc, nTab, nPosX,nPosY, nPosX,nPosY );
+ if ( !aTester.IsFormatEditable() )
+ nRet = DND_ACTION_NONE; // forbidden
+ }
+ }
+ }
+
+ // scroll only for accepted formats
+ if (nRet)
+ DropScroll( rEvt.maPosPixel );
+ }
+
+ return nRet;
+}
+
+static SotClipboardFormatId lcl_GetDropFormatId( const uno::Reference<datatransfer::XTransferable>& xTransfer, bool bPreferText )
+{
+ TransferableDataHelper aDataHelper( xTransfer );
+
+ if ( !aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
+ {
+ // use bookmark formats if no sba is present
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
+ return SotClipboardFormatId::SOLK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
+ return SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
+ return SotClipboardFormatId::NETSCAPE_BOOKMARK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
+ return SotClipboardFormatId::FILEGRPDESCRIPTOR;
+ }
+
+ SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) )
+ nFormatId = SotClipboardFormatId::DRAWING;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
+ nFormatId = SotClipboardFormatId::SVXB;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) )
+ {
+ // If it's a Writer object, insert RTF instead of OLE
+
+ bool bDoRtf = false;
+ tools::SvRef<SotTempStream> xStm;
+ TransferableObjectDescriptor aObjDesc;
+ if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) &&
+ aDataHelper.GetSotStorageStream( SotClipboardFormatId::EMBED_SOURCE, xStm ) )
+ {
+ bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
+ aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
+ && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
+ }
+ if ( bDoRtf )
+ nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
+ else
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE;
+ }
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_DATAEXCHANGE ) )
+ nFormatId = SotClipboardFormatId::SBA_DATAEXCHANGE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE ) )
+ nFormatId = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_8 ) )
+ nFormatId = SotClipboardFormatId::BIFF_8;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BIFF_5 ) )
+ nFormatId = SotClipboardFormatId::BIFF_5;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) )
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) )
+ nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ nFormatId = SotClipboardFormatId::RTF;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
+ nFormatId = SotClipboardFormatId::RICHTEXT;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML ) )
+ nFormatId = SotClipboardFormatId::HTML;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) )
+ nFormatId = SotClipboardFormatId::HTML_SIMPLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SYLK ) )
+ nFormatId = SotClipboardFormatId::SYLK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
+ nFormatId = SotClipboardFormatId::LINK;
+ else if ( bPreferText && aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) // #i86734# the behaviour introduced in #i62773# is wrong when pasting
+ nFormatId = SotClipboardFormatId::STRING;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
+ nFormatId = SotClipboardFormatId::FILE_LIST;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) ) // #i62773# FILE_LIST/FILE before STRING (Unix file managers)
+ nFormatId = SotClipboardFormatId::SIMPLE_FILE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING_TSVC ) )
+ nFormatId = SotClipboardFormatId::STRING_TSVC;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ nFormatId = SotClipboardFormatId::STRING;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
+ nFormatId = SotClipboardFormatId::GDIMETAFILE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
+ nFormatId = SotClipboardFormatId::PNG;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) )
+ nFormatId = SotClipboardFormatId::BITMAP;
+
+ return nFormatId;
+}
+
+static SotClipboardFormatId lcl_GetDropLinkId( const uno::Reference<datatransfer::XTransferable>& xTransfer )
+{
+ TransferableDataHelper aDataHelper( xTransfer );
+
+ SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) )
+ nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::LINK ) )
+ nFormatId = SotClipboardFormatId::LINK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
+ nFormatId = SotClipboardFormatId::FILE_LIST;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )
+ nFormatId = SotClipboardFormatId::SIMPLE_FILE;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) )
+ nFormatId = SotClipboardFormatId::SOLK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) )
+ nFormatId = SotClipboardFormatId::UNIFORMRESOURCELOCATOR;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) )
+ nFormatId = SotClipboardFormatId::NETSCAPE_BOOKMARK;
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) )
+ nFormatId = SotClipboardFormatId::FILEGRPDESCRIPTOR;
+
+ return nFormatId;
+}
+
+sal_Int8 ScGridWindow::ExecutePrivateDrop( const ExecuteDropEvent& rEvt, const ScDragData& rData )
+{
+ // hide drop marker
+ bDragRect = false;
+ UpdateDragRectOverlay();
+
+ return DropTransferObj( rData.pCellTransfer, nDragStartX, nDragStartY,
+ PixelToLogic(rEvt.maPosPixel), rEvt.mnAction );
+}
+
+sal_Int8 ScGridWindow::DropTransferObj( ScTransferObj* pTransObj, SCCOL nDestPosX, SCROW nDestPosY,
+ const Point& rLogicPos, sal_Int8 nDndAction )
+{
+ if ( !pTransObj )
+ return 0;
+
+ ScDocument* pSourceDoc = pTransObj->GetSourceDocument();
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ ScViewFunc* pView = mrViewData.GetView();
+ SCTAB nThisTab = mrViewData.GetTabNo();
+ ScDragSrc nFlags = pTransObj->GetDragSourceFlags();
+
+ bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
+ bool bIsMove = ( nDndAction == DND_ACTION_MOVE && !bIsNavi );
+
+ // workaround for wrong nDndAction on Windows when pressing solely
+ // the Alt key during drag and drop;
+ // can be removed after #i79215# has been fixed
+ if ( meDragInsertMode != INS_NONE )
+ {
+ bIsMove = ( nDndAction & DND_ACTION_MOVE && !bIsNavi );
+ }
+
+ bool bIsLink = ( nDndAction == DND_ACTION_LINK );
+
+ ScRange aSource = pTransObj->GetRange();
+
+ // only use visible tab from source range - when dragging within one table,
+ // all selected tables at the time of dropping are used (handled in MoveBlockTo)
+ SCTAB nSourceTab = pTransObj->GetVisibleTab();
+ aSource.aStart.SetTab( nSourceTab );
+ aSource.aEnd.SetTab( nSourceTab );
+
+ SCCOL nSizeX = aSource.aEnd.Col() - aSource.aStart.Col() + 1;
+ SCROW nSizeY = (bIsMove ? (aSource.aEnd.Row() - aSource.aStart.Row() + 1) :
+ pTransObj->GetNonFilteredRows()); // copy/link: no filtered rows
+ ScRange aDest( nDestPosX, nDestPosY, nThisTab,
+ nDestPosX + nSizeX - 1, nDestPosY + nSizeY - 1, nThisTab );
+
+ /* NOTE: AcceptPrivateDrop() already checked for filtered conditions during
+ * dragging and adapted drawing of the selection frame. We check here
+ * (again) because this may actually also be called from PasteSelection(),
+ * we would have to duplicate determination of flags and destination range
+ * and would lose the context of the "filtered destination is OK" cases
+ * below, which is already awkward enough as is. */
+
+ // Don't move filtered source.
+ bool bFiltered = (bIsMove && pTransObj->HasFilteredRows());
+ if (!bFiltered)
+ {
+ if (pSourceDoc != &rThisDoc && ((nFlags & ScDragSrc::Table) ||
+ (!bIsLink && meDragInsertMode == INS_NONE)))
+ {
+ // Nothing. Either entire sheet to be dropped, or the one case
+ // where PasteFromClip() is to be called that handles a filtered
+ // destination itself. Drag-copy from another document without
+ // inserting cells.
+ }
+ else
+ // Don't copy or move to filtered destination.
+ bFiltered = ScViewUtil::HasFiltered(aDest, rThisDoc);
+ }
+
+ bool bDone = false;
+
+ if (!bFiltered && pSourceDoc == &rThisDoc)
+ {
+ if (nFlags & ScDragSrc::Table) // whole sheet?
+ {
+ if ( rThisDoc.IsDocEditable() )
+ {
+ SCTAB nSrcTab = aSource.aStart.Tab();
+ mrViewData.GetDocShell()->MoveTable( nSrcTab, nThisTab, !bIsMove, true ); // with Undo
+ pView->SetTabNo( nThisTab, true );
+ bDone = true;
+ }
+ }
+ else // move/copy block
+ {
+ OUString aChartName;
+ if (rThisDoc.HasChartAtPoint( nThisTab, rLogicPos, aChartName ))
+ {
+ OUString aRangeName(aSource.Format(rThisDoc, ScRefFlags::RANGE_ABS_3D,
+ rThisDoc.GetAddressConvention()));
+ SfxStringItem aNameItem( SID_CHART_NAME, aChartName );
+ SfxStringItem aRangeItem( SID_CHART_SOURCE, aRangeName );
+ sal_uInt16 nId = bIsMove ? SID_CHART_SOURCE : SID_CHART_ADDSOURCE;
+ mrViewData.GetDispatcher().ExecuteList(nId,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
+ { &aRangeItem, &aNameItem });
+ bDone = true;
+ }
+ else if ( rThisDoc.GetDPAtCursor( nDestPosX, nDestPosY, nThisTab ) )
+ {
+ // drop on DataPilot table: try to sort, fail if that isn't possible
+
+ ScAddress aDestPos( nDestPosX, nDestPosY, nThisTab );
+ if ( aDestPos != aSource.aStart )
+ bDone = mrViewData.GetView()->DataPilotMove( aSource, aDestPos );
+ else
+ bDone = true; // same position: nothing
+ }
+ else if ( nDestPosX != aSource.aStart.Col() || nDestPosY != aSource.aStart.Row() ||
+ nSourceTab != nThisTab )
+ {
+ OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ SCCOL nCorrectCursorPosCol = 0;
+ SCROW nCorrectCursorPosRow = 0;
+
+ bDone = true;
+ if ( meDragInsertMode != INS_NONE )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
+ if ( bDone )
+ {
+ if ( nThisTab == nSourceTab )
+ {
+ if ( meDragInsertMode == INS_CELLSDOWN &&
+ nDestPosX == aSource.aStart.Col() && nDestPosY < aSource.aStart.Row() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aSource.Move( 0, nSizeY, 0, aErrorRange, *pSourceDoc );
+ nCorrectCursorPosRow = nSizeY;
+ }
+ else if ( meDragInsertMode == INS_CELLSRIGHT &&
+ nDestPosY == aSource.aStart.Row() && nDestPosX < aSource.aStart.Col() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aSource.Move( nSizeX, 0, 0, aErrorRange, *pSourceDoc );
+ nCorrectCursorPosCol = nSizeX;
+ }
+ }
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+
+ if ( bDone )
+ {
+ if ( bIsLink )
+ {
+ bDone = pView->LinkBlock( aSource, aDest.aStart );
+ }
+ else
+ {
+ bDone = pView->MoveBlockTo( aSource, aDest.aStart, bIsMove );
+ }
+ }
+
+ if ( bDone && meDragInsertMode != INS_NONE && bIsMove && nThisTab == nSourceTab )
+ {
+ DelCellCmd eCmd = DelCellCmd::NONE;
+ if ( meDragInsertMode == INS_CELLSDOWN )
+ {
+ eCmd = DelCellCmd::CellsUp;
+ }
+ else if ( meDragInsertMode == INS_CELLSRIGHT )
+ {
+ eCmd = DelCellCmd::CellsLeft;
+ }
+
+ if ( ( eCmd == DelCellCmd::CellsUp && nDestPosX == aSource.aStart.Col() ) ||
+ ( eCmd == DelCellCmd::CellsLeft && nDestPosY == aSource.aStart.Row() ) )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().DeleteCells( aSource, nullptr, eCmd, true /*bApi*/ );
+ if ( bDone )
+ {
+ if ( eCmd == DelCellCmd::CellsUp && nDestPosY > aSource.aEnd.Row() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aDest.Move( 0, -nSizeY, 0, aErrorRange, rThisDoc );
+ }
+ else if ( eCmd == DelCellCmd::CellsLeft && nDestPosX > aSource.aEnd.Col() )
+ {
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ bDone = aDest.Move( -nSizeX, 0, 0, aErrorRange, rThisDoc );
+ }
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+ }
+
+ if ( bDone )
+ {
+ pView->MarkRange( aDest, false );
+
+ SCCOL nDCol;
+ SCROW nDRow;
+ if (pTransObj->WasSourceCursorInSelection())
+ {
+ nDCol = pTransObj->GetSourceCursorX() - aSource.aStart.Col() + nCorrectCursorPosCol;
+ nDRow = pTransObj->GetSourceCursorY() - aSource.aStart.Row() + nCorrectCursorPosRow;
+ }
+ else
+ {
+ nDCol = 0;
+ nDRow = 0;
+ }
+ pView->SetCursor( aDest.aStart.Col() + nDCol, aDest.aStart.Row() + nDRow );
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ }
+ else
+ bDone = true; // nothing to do
+ }
+
+ if (bDone)
+ pTransObj->SetDragWasInternal(); // don't delete source in DragFinished
+ }
+ else if ( !bFiltered && pSourceDoc ) // between documents
+ {
+ if (nFlags & ScDragSrc::Table) // copy/link sheets between documents
+ {
+ if ( rThisDoc.IsDocEditable() )
+ {
+ ScDocShell* pSrcShell = pTransObj->GetSourceDocShell();
+
+ std::vector<SCTAB> nTabs;
+
+ ScMarkData aMark = pTransObj->GetSourceMarkData();
+ SCTAB nTabCount = pSourceDoc->GetTableCount();
+
+ for(SCTAB i=0; i<nTabCount; i++)
+ {
+ if(aMark.GetTableSelect(i))
+ {
+ nTabs.push_back(i);
+ for(SCTAB j=i+1;j<nTabCount;j++)
+ {
+ if((!pSourceDoc->IsVisible(j))&&(pSourceDoc->IsScenario(j)))
+ {
+ nTabs.push_back( j );
+ i=j;
+ }
+ else break;
+ }
+ }
+ }
+
+ pView->ImportTables( pSrcShell,static_cast<SCTAB>(nTabs.size()), nTabs.data(), bIsLink, nThisTab );
+ bDone = true;
+ }
+ }
+ else if ( bIsLink )
+ {
+ // as in PasteDDE
+ // (external references might be used instead?)
+
+ ScDocShell* pSourceSh = pSourceDoc->GetDocumentShell();
+ OSL_ENSURE(pSourceSh, "drag document has no shell");
+ if (pSourceSh)
+ {
+ OUString aUndo = ScResId( STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ bDone = true;
+ if ( meDragInsertMode != INS_NONE )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
+ if ( bDone )
+ {
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+
+ if ( bDone )
+ {
+ OUString aApp = Application::GetAppName();
+ OUString aTopic = pSourceSh->GetTitle( SFX_TITLE_FULLNAME );
+ OUString aItem(aSource.Format(*pSourceDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D));
+
+ // TODO: we could define ocQuote for "
+ const OUString aQuote('"');
+ const OUString& sSep = ScCompiler::GetNativeSymbol( ocSep);
+ OUString aFormula =
+ "=" +
+ ScCompiler::GetNativeSymbol(ocDde) +
+ ScCompiler::GetNativeSymbol(ocOpen) +
+ aQuote +
+ aApp +
+ aQuote +
+ sSep +
+ aQuote +
+ aTopic +
+ aQuote +
+ sSep +
+ aQuote +
+ aItem +
+ aQuote +
+ ScCompiler::GetNativeSymbol(ocClose);
+
+ pView->DoneBlockMode();
+ pView->InitBlockMode( nDestPosX, nDestPosY, nThisTab );
+ pView->MarkCursor( nDestPosX + nSizeX - 1,
+ nDestPosY + nSizeY - 1, nThisTab );
+
+ pView->EnterMatrix( aFormula, ::formula::FormulaGrammar::GRAM_NATIVE );
+
+ pView->MarkRange( aDest, false );
+ pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+ }
+ else
+ {
+ //! HasSelectedBlockMatrixFragment without selected sheet?
+ //! or don't start dragging on a part of a matrix
+
+ OUString aUndo = ScResId( bIsMove ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+
+ bDone = true;
+ if ( meDragInsertMode != INS_NONE )
+ {
+ // call with bApi = sal_True to avoid error messages in drop handler
+ bDone = pDocSh->GetDocFunc().InsertCells( aDest, nullptr, meDragInsertMode, true /*bRecord*/, true /*bApi*/, true /*bPartOfPaste*/ );
+ if ( bDone )
+ {
+ pDocSh->UpdateOle(mrViewData);
+ pView->CellContentChanged();
+ }
+ }
+
+ if ( bDone )
+ {
+ pView->Unmark(); // before SetCursor, so CheckSelectionTransfer isn't called with a selection
+ pView->SetCursor( nDestPosX, nDestPosY );
+ bDone = pView->PasteFromClip( InsertDeleteFlags::ALL, pTransObj->GetDocument() ); // clip-doc
+ if ( bDone )
+ {
+ pView->MarkRange( aDest, false );
+ pView->SetCursor( aDest.aStart.Col(), aDest.aStart.Row() );
+ }
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ // no longer call ResetMark here - the inserted block has been selected
+ // and may have been copied to primary selection
+ }
+ }
+
+ sal_Int8 nRet = bDone ? nDndAction : DND_ACTION_NONE;
+ return nRet;
+}
+
+sal_Int8 ScGridWindow::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ DrawMarkDropObj( nullptr ); // drawing layer
+
+ ScModule* pScMod = SC_MOD();
+ const ScDragData& rData = pScMod->GetDragData();
+ if (rData.pCellTransfer)
+ return ExecutePrivateDrop( rEvt, rData );
+
+ Point aPos = rEvt.maPosPixel;
+
+ if ( !rData.aLinkDoc.isEmpty() )
+ {
+ // try to insert a link
+
+ bool bOk = true;
+ OUString aThisName;
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ if (pDocSh && pDocSh->HasName())
+ aThisName = pDocSh->GetMedium()->GetName();
+
+ if ( rData.aLinkDoc == aThisName ) // error - no link within a document
+ bOk = false;
+ else
+ {
+ ScViewFunc* pView = mrViewData.GetView();
+ if ( !rData.aLinkTable.isEmpty() )
+ pView->InsertTableLink( rData.aLinkDoc, OUString(), OUString(),
+ rData.aLinkTable );
+ else if ( !rData.aLinkArea.isEmpty() )
+ {
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ pView->MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
+
+ pView->InsertAreaLink( rData.aLinkDoc, OUString(), OUString(),
+ rData.aLinkArea );
+ }
+ else
+ {
+ OSL_FAIL("drop with link: no sheet nor area");
+ bOk = false;
+ }
+ }
+
+ return bOk ? rEvt.mnAction : DND_ACTION_NONE; // don't try anything else
+ }
+
+ Point aLogicPos = PixelToLogic(aPos);
+ bool bIsLink = ( rEvt.mnAction == DND_ACTION_LINK );
+
+ if (!bIsLink && rData.pDrawTransfer)
+ {
+ ScDragSrc nFlags = rData.pDrawTransfer->GetDragSourceFlags();
+
+ bool bIsNavi = (nFlags & ScDragSrc::Navigator) == ScDragSrc::Navigator;
+ bool bIsMove = ( rEvt.mnAction == DND_ACTION_MOVE && !bIsNavi );
+
+ bPasteIsMove = bIsMove;
+
+ mrViewData.GetView()->PasteDraw(
+ aLogicPos, rData.pDrawTransfer->GetModel(), false, u"A", u"B");
+
+ if (bPasteIsMove)
+ rData.pDrawTransfer->SetDragWasInternal();
+ bPasteIsMove = false;
+
+ return rEvt.mnAction;
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ if (!rData.aJumpTarget.isEmpty())
+ {
+ // internal bookmark (from Navigator)
+ // bookmark clipboard formats are in PasteScDataObject
+
+ if ( !rData.pJumpLocalDoc || rData.pJumpLocalDoc == &mrViewData.GetDocument() )
+ {
+ mrViewData.GetViewShell()->InsertBookmark( rData.aJumpText, rData.aJumpTarget,
+ nPosX, nPosY );
+ return rEvt.mnAction;
+ }
+ }
+
+ ScDocument& rThisDoc = mrViewData.GetDocument();
+ SdrObject* pHitObj = rThisDoc.GetObjectAtPoint( mrViewData.GetTabNo(), PixelToLogic(aPos) );
+ if ( pHitObj && bIsLink )
+ {
+ // dropped on drawing object
+ // PasteOnDrawObjectLinked checks for valid formats
+ if ( mrViewData.GetView()->PasteOnDrawObjectLinked( rEvt.maDropEvent.Transferable, *pHitObj ) )
+ return rEvt.mnAction;
+ }
+
+ bool bDone = false;
+
+ SotClipboardFormatId nFormatId = bIsLink ?
+ lcl_GetDropLinkId( rEvt.maDropEvent.Transferable ) :
+ lcl_GetDropFormatId( rEvt.maDropEvent.Transferable, false );
+ if ( nFormatId != SotClipboardFormatId::NONE )
+ {
+ pScMod->SetInExecuteDrop( true ); // #i28468# prevent error messages from PasteDataFormat
+ bDone = mrViewData.GetView()->PasteDataFormat(
+ nFormatId, rEvt.maDropEvent.Transferable, nPosX, nPosY, &aLogicPos, bIsLink );
+ pScMod->SetInExecuteDrop( false );
+ }
+
+ sal_Int8 nRet = bDone ? rEvt.mnAction : DND_ACTION_NONE;
+ return nRet;
+}
+
+void ScGridWindow::PasteSelection( const Point& rPosPixel )
+{
+ Point aLogicPos = PixelToLogic( rPosPixel );
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( rPosPixel.X(), rPosPixel.Y(), eWhich, nPosX, nPosY );
+
+ // If the mouse down was inside a visible note window, ignore it and
+ // leave it up to the ScPostIt to handle it
+ SdrView* pDrawView = mrViewData.GetViewShell()->GetScDrawView();
+ if (pDrawView)
+ {
+ const size_t nCount = pDrawView->GetMarkedObjectCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrObject* pObj = pDrawView->GetMarkedObjectByIndex(i);
+ if (pObj && pObj->GetLogicRect().Contains(aLogicPos))
+ {
+ // Inside an active drawing object. Bail out.
+ return;
+ }
+ }
+ }
+
+ ScSelectionTransferObj* pOwnSelection = SC_MOD()->GetSelectionTransfer();
+ if ( pOwnSelection )
+ {
+ // within Calc
+
+ // keep a reference to the data in case the selection is changed during paste
+ rtl::Reference<ScTransferObj> pCellTransfer = pOwnSelection->GetCellData();
+ if ( pCellTransfer )
+ {
+ DropTransferObj( pCellTransfer.get(), nPosX, nPosY, aLogicPos, DND_ACTION_COPY );
+ }
+ else
+ {
+ // keep a reference to the data in case the selection is changed during paste
+ rtl::Reference<ScDrawTransferObj> pDrawTransfer = pOwnSelection->GetDrawData();
+ if ( pDrawTransfer )
+ {
+ // bSameDocClipboard argument for PasteDraw is needed
+ // because only DragData is checked directly inside PasteDraw
+ mrViewData.GetView()->PasteDraw(
+ aLogicPos, pDrawTransfer->GetModel(), false,
+ pDrawTransfer->GetShellID(), SfxObjectShell::CreateShellID(mrViewData.GetDocShell()));
+ }
+ }
+ }
+ else
+ {
+ // get selection from system
+ TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromPrimarySelection());
+ const uno::Reference<datatransfer::XTransferable>& xTransferable = aDataHelper.GetTransferable();
+ if ( xTransferable.is() )
+ {
+ SotClipboardFormatId nFormatId = lcl_GetDropFormatId( xTransferable, true );
+ if ( nFormatId != SotClipboardFormatId::NONE )
+ mrViewData.GetView()->PasteDataFormat( nFormatId, xTransferable, nPosX, nPosY, &aLogicPos );
+ }
+ }
+}
+
+void ScGridWindow::UpdateEditViewPos()
+{
+ if (!mrViewData.HasEditView(eWhich))
+ return;
+
+ EditView* pView;
+ SCCOL nCol;
+ SCROW nRow;
+ mrViewData.GetEditView( eWhich, pView, nCol, nRow );
+ SCCOL nEndCol = mrViewData.GetEditEndCol();
+ SCROW nEndRow = mrViewData.GetEditEndRow();
+
+ // hide EditView?
+
+ bool bHide = ( nEndCol<mrViewData.GetPosX(eHWhich) || nEndRow<mrViewData.GetPosY(eVWhich) );
+ if ( SC_MOD()->IsFormulaMode() )
+ if ( mrViewData.GetTabNo() != mrViewData.GetRefTabNo() )
+ bHide = true;
+
+ if (bHide)
+ {
+ tools::Rectangle aRect = pView->GetOutputArea();
+ tools::Long nHeight = aRect.Bottom() - aRect.Top();
+ aRect.SetTop( PixelToLogic(GetOutputSizePixel(), mrViewData.GetLogicMode()).
+ Height() * 2 );
+ aRect.SetBottom( aRect.Top() + nHeight );
+ pView->SetOutputArea( aRect );
+ pView->HideCursor();
+ }
+ else
+ {
+ // bForceToTop = sal_True for editing
+ tools::Rectangle aPixRect = mrViewData.GetEditArea( eWhich, nCol, nRow, this, nullptr, true );
+
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ tools::Rectangle aPTwipsRect = mrViewData.GetEditArea(eWhich, nCol, nRow, this, nullptr,
+ true, true /* bInPrintTwips */);
+ tools::Rectangle aOutputAreaPTwips = pView->GetLOKSpecialOutputArea();
+ aOutputAreaPTwips.SetPos(aPTwipsRect.TopLeft());
+ pView->SetLOKSpecialOutputArea(aOutputAreaPTwips);
+ }
+
+ Point aScrPos = PixelToLogic( aPixRect.TopLeft(), mrViewData.GetLogicMode() );
+
+ tools::Rectangle aRect = pView->GetOutputArea();
+ aRect.SetPos( aScrPos );
+ pView->SetOutputArea( aRect );
+ pView->ShowCursor();
+ }
+}
+
+void ScGridWindow::ScrollPixel( tools::Long nDifX, tools::Long nDifY )
+{
+ ClickExtern();
+ HideNoteMarker();
+
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ Scroll( nDifX, nDifY, ScrollFlags::Children );
+ SetMapMode( GetDrawMapMode() ); // generated shifted MapMode
+
+ UpdateEditViewPos();
+
+ DrawAfterScroll();
+}
+
+// Update Formulas ------------------------------------------------------
+
+void ScGridWindow::UpdateFormulas(SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2)
+{
+ if (mrViewData.GetView()->IsMinimized())
+ return;
+
+ if ( nPaintCount )
+ {
+ // Do not start, switched to paint
+ // (then at least the MapMode would no longer be right)
+
+ bNeedsRepaint = true; // -> at end of paint run Invalidate on all
+ aRepaintPixel = tools::Rectangle(); // All
+ return;
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ if (nX1 < 0)
+ nX1 = pViewShell->GetLOKStartHeaderCol() + 1;
+ if (nY1 < 0)
+ nY1 = pViewShell->GetLOKStartHeaderRow() + 1;
+ if (nX2 < 0)
+ nX2 = pViewShell->GetLOKEndHeaderCol();
+ if (nY2 < 0)
+ nY2 = pViewShell->GetLOKEndHeaderRow();
+
+ if (nX1 < 0 || nY1 < 0) return;
+ }
+ else
+ {
+ nX1 = mrViewData.GetPosX( eHWhich );
+ nY1 = mrViewData.GetPosY( eVWhich );
+ nX2 = nX1 + mrViewData.VisibleCellsX( eHWhich );
+ nY2 = nY1 + mrViewData.VisibleCellsY( eVWhich );
+ }
+
+ if (nX2 < nX1) nX2 = nX1;
+ if (nY2 < nY1) nY2 = nY1;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
+ if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();
+
+ // Draw( nX1, nY1, nX2, nY2, SC_UPDATE_CHANGED );
+
+ // don't draw directly - instead use OutputData to find changed area and invalidate
+
+ SCROW nPosY = nY1;
+
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ if ( !comphelper::LibreOfficeKit::isActive() )
+ {
+ rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
+ }
+
+ Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
+ tools::Long nMirrorWidth = GetSizePixel().Width();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( bLayoutRTL )
+ {
+ tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, nPosY, eWhich ).X();
+ nMirrorWidth = aScrPos.X() - nEndPixel;
+ aScrPos.setX( nEndPixel + 1 );
+ }
+
+ tools::Long nScrX = aScrPos.X();
+ tools::Long nScrY = aScrPos.Y();
+
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab, nPPTX, nPPTY, false, false );
+
+ Fraction aZoomX = mrViewData.GetZoomX();
+ Fraction aZoomY = mrViewData.GetZoomY();
+ ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
+ &aZoomX, &aZoomY );
+ aOutputData.SetMirrorWidth( nMirrorWidth );
+
+ aOutputData.FindChanged();
+
+ // #i122149# do not use old GetChangedArea() which used polygon-based Regions, but use
+ // the region-band based new version; anyways, only rectangles are added
+ vcl::Region aChangedRegion( aOutputData.GetChangedAreaRegion() ); // logic (PixelToLogic)
+ if(!aChangedRegion.IsEmpty())
+ {
+ Invalidate(aChangedRegion);
+ }
+
+ CheckNeedsRepaint(); // #i90362# used to be called via Draw() - still needed here
+}
+
+void ScGridWindow::UpdateAutoFillMark(bool bMarked, const ScRange& rMarkRange)
+{
+ if ( bMarked != bAutoMarkVisible || ( bMarked && rMarkRange.aEnd != aAutoMarkPos ) )
+ {
+ bAutoMarkVisible = bMarked;
+ if ( bMarked )
+ aAutoMarkPos = rMarkRange.aEnd;
+
+ UpdateAutoFillOverlay();
+ }
+}
+
+void ScGridWindow::updateLOKInputHelp(const OUString& title, const OUString& content) const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ boost::property_tree::ptree aTree;
+ aTree.put("title", title);
+ aTree.put("content", content);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_INPUT_HELP, OString(aStream.str()));
+}
+
+void ScGridWindow::updateLOKValListButton( bool bVisible, const ScAddress& rPos ) const
+{
+ SCCOL nX = rPos.Col();
+ SCROW nY = rPos.Row();
+ std::stringstream ss;
+ ss << nX << ", " << nY << ", " << static_cast<unsigned int>(bVisible);
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_VALIDITY_LIST_BUTTON, OString(ss.str()));
+}
+
+void ScGridWindow::notifyKitCellFollowJump( ) const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SC_FOLLOW_JUMP, getCellCursor());
+}
+
+void ScGridWindow::UpdateListValPos( bool bVisible, const ScAddress& rPos )
+{
+ bool bOldButton = bListValButton;
+ ScAddress aOldPos = aListValPos;
+
+ bListValButton = bVisible;
+ aListValPos = rPos;
+
+ if ( bListValButton )
+ {
+ if ( !bOldButton || aListValPos != aOldPos )
+ {
+ // paint area of new button
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ updateLOKValListButton( true, aListValPos );
+ }
+ else
+ {
+ Invalidate( PixelToLogic( GetListValButtonRect( aListValPos ) ) );
+ }
+ }
+ }
+ if ( !bOldButton )
+ return;
+
+ if ( !bListValButton || aListValPos != aOldPos )
+ {
+ // paint area of old button
+ if ( comphelper::LibreOfficeKit::isActive() )
+ {
+ updateLOKValListButton( false, aOldPos );
+ }
+ else
+ {
+ Invalidate( PixelToLogic( GetListValButtonRect( aOldPos ) ) );
+ }
+ }
+}
+
+void ScGridWindow::HideCursor()
+{
+ ++nCursorHideCount;
+}
+
+void ScGridWindow::ShowCursor()
+{
+ --nCursorHideCount;
+}
+
+void ScGridWindow::GetFocus()
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->SetFormShellAtTop( false ); // focus in GridWindow -> FormShell no longer on top
+
+ if (pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility(ScAccGridWinFocusGotHint(eWhich));
+
+ if ( !SC_MOD()->IsFormulaMode() )
+ {
+ pViewShell->UpdateInputHandler();
+// StopMarking(); // If Dialog (error), because then no ButtonUp
+ // MO: only when not in RefInput mode
+ // -> GetFocus/MouseButtonDown order on Mac
+ }
+
+ mrViewData.GetDocShell()->CheckConfigOptions();
+ Window::GetFocus();
+}
+
+void ScGridWindow::LoseFocus()
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ if (pViewShell && pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility(ScAccGridWinFocusLostHint(eWhich));
+
+ Window::LoseFocus();
+}
+
+bool ScGridWindow::HitRangeFinder( const Point& rMouse, RfCorner& rCorner,
+ sal_uInt16* pIndex, SCCOL* pAddX, SCROW* pAddY)
+{
+ bool bFound = false;
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( mrViewData.GetViewShell() );
+ if (pHdl)
+ {
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if ( pRangeFinder && !pRangeFinder->IsHidden() &&
+ pRangeFinder->GetDocName() == mrViewData.GetDocShell()->GetTitle() )
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( rMouse.X(), rMouse.Y(), eWhich, nPosX, nPosY );
+ // merged (single/Range) ???
+ ScAddress aAddr( nPosX, nPosY, nTab );
+
+ Point aCellStart = mrViewData.GetScrPos( nPosX, nPosY, eWhich, true );
+ Point aCellEnd = aCellStart;
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeXPix, nSizeYPix );
+
+ aCellEnd.AdjustX(nSizeXPix * nLayoutSign );
+ aCellEnd.AdjustY(nSizeYPix );
+
+ bool bCornerHorizontalRight;
+ bool bCornerHorizontalLeft;
+ if ( bLayoutRTL )
+ {
+ bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() && rMouse.X() <= aCellEnd.X() + 8 );
+ bCornerHorizontalLeft = ( rMouse.X() >= aCellStart.X() - 8 && rMouse.X() <= aCellStart.X() );
+ }
+ else
+ {
+ bCornerHorizontalRight = ( rMouse.X() >= aCellEnd.X() - 8 && rMouse.X() <= aCellEnd.X() );
+ bCornerHorizontalLeft = ( rMouse.X() >= aCellStart.X() && rMouse.X() <= aCellStart.X() + 8 );
+ }
+
+ bool bCornerVerticalDown = rMouse.Y() >= aCellEnd.Y() - 8 && rMouse.Y() <= aCellEnd.Y();
+ bool bCornerVerticalUp = rMouse.Y() >= aCellStart.Y() && rMouse.Y() <= aCellStart.Y() + 8;
+
+ // corner is hit only if the mouse is within the cell
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
+ for (sal_uInt16 i=nCount; i;)
+ {
+ // search backwards so that the last repainted frame is found
+ --i;
+ ScRangeFindData& rData = pRangeFinder->GetObject(i);
+ if ( rData.aRef.Contains(aAddr) )
+ {
+ if (pIndex)
+ *pIndex = i;
+ if (pAddX)
+ *pAddX = nPosX - rData.aRef.aStart.Col();
+ if (pAddY)
+ *pAddY = nPosY - rData.aRef.aStart.Row();
+
+ bFound = true;
+
+ rCorner = NONE;
+
+ ScAddress aEnd = rData.aRef.aEnd;
+ ScAddress aStart = rData.aRef.aStart;
+
+ if ( bCornerHorizontalLeft && bCornerVerticalUp &&
+ aAddr == aStart)
+ {
+ rCorner = LEFT_UP;
+ }
+ else if (bCornerHorizontalRight && bCornerVerticalDown &&
+ aAddr == aEnd)
+ {
+ rCorner = RIGHT_DOWN;
+ }
+ else if (bCornerHorizontalRight && bCornerVerticalUp &&
+ aAddr == ScAddress(aEnd.Col(), aStart.Row(), aStart.Tab()))
+ {
+ rCorner = RIGHT_UP;
+ }
+ else if (bCornerHorizontalLeft && bCornerVerticalDown &&
+ aAddr == ScAddress(aStart.Col(), aEnd.Row(), aStart.Tab()))
+ {
+ rCorner = LEFT_DOWN;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return bFound;
+}
+
+#define SCE_TOP 1
+#define SCE_BOTTOM 2
+#define SCE_LEFT 4
+#define SCE_RIGHT 8
+#define SCE_ALL 15
+
+static void lcl_PaintOneRange( ScDocShell* pDocSh, const ScRange& rRange, sal_uInt16 nEdges )
+{
+ // the range is always properly oriented
+
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ SCROW nRow2 = rRange.aEnd.Row();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ bool bHiddenEdge = false;
+ SCROW nTmp;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab1) )
+ {
+ --nCol1;
+ bHiddenEdge = true;
+ }
+ while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab1) )
+ {
+ ++nCol2;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.FirstVisibleRow(0, nRow1, nTab1);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = 0;
+ if (nTmp < nRow1)
+ {
+ nRow1 = nTmp;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab1);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = rDoc.MaxRow();
+ if (nTmp > nRow2)
+ {
+ nRow2 = nTmp;
+ bHiddenEdge = true;
+ }
+
+ if ( nCol2 > nCol1 + 1 && nRow2 > nRow1 + 1 && !bHiddenEdge )
+ {
+ // Only along the edges (The corners are hit twice)
+ if ( nEdges & SCE_TOP )
+ pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow1, nTab2, PaintPartFlags::Marks );
+ if ( nEdges & SCE_LEFT )
+ pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol1, nRow2, nTab2, PaintPartFlags::Marks );
+ if ( nEdges & SCE_RIGHT )
+ pDocSh->PostPaint( nCol2, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
+ if ( nEdges & SCE_BOTTOM )
+ pDocSh->PostPaint( nCol1, nRow2, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
+ }
+ else // everything in one call
+ pDocSh->PostPaint( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, PaintPartFlags::Marks );
+}
+
+static void lcl_PaintRefChanged( ScDocShell* pDocSh, const ScRange& rOldUn, const ScRange& rNewUn )
+{
+ // Repaint for the parts of the frame in old, which in are no more in New
+
+ ScRange aOld = rOldUn;
+ ScRange aNew = rNewUn;
+ aOld.PutInOrder();
+ aNew.PutInOrder();
+
+ if ( aOld.aStart == aOld.aEnd ) //! Ignore sheet ?
+ pDocSh->GetDocument().ExtendMerge(aOld);
+ if ( aNew.aStart == aNew.aEnd ) //! Ignore sheet ?
+ pDocSh->GetDocument().ExtendMerge(aNew);
+
+ SCCOL nOldCol1 = aOld.aStart.Col();
+ SCROW nOldRow1 = aOld.aStart.Row();
+ SCCOL nOldCol2 = aOld.aEnd.Col();
+ SCROW nOldRow2 = aOld.aEnd.Row();
+ SCCOL nNewCol1 = aNew.aStart.Col();
+ SCROW nNewRow1 = aNew.aStart.Row();
+ SCCOL nNewCol2 = aNew.aEnd.Col();
+ SCROW nNewRow2 = aNew.aEnd.Row();
+ SCTAB nTab1 = aOld.aStart.Tab(); // sheet is not changed
+ SCTAB nTab2 = aOld.aEnd.Tab();
+
+ if ( nNewRow2 < nOldRow1 || nNewRow1 > nOldRow2 ||
+ nNewCol2 < nOldCol1 || nNewCol1 > nOldCol2 ||
+ ( nNewCol1 != nOldCol1 && nNewRow1 != nOldRow1 &&
+ nNewCol2 != nOldCol2 && nNewRow2 != nOldRow2 ) )
+ {
+ // Completely removed or changed all sides
+ // (check <= instead of < goes wrong for single rows/columns)
+
+ lcl_PaintOneRange( pDocSh, aOld, SCE_ALL );
+ }
+ else // Test all four corners separately
+ {
+ // upper part
+ if ( nNewRow1 < nOldRow1 ) // only delete upper line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nOldCol2, nOldRow1, nTab2 ), SCE_ALL );
+ else if ( nNewRow1 > nOldRow1 ) // the upper part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nOldCol2, nNewRow1-1, nTab2 ),
+ SCE_ALL &~ SCE_BOTTOM );
+
+ // bottom part
+ if ( nNewRow2 > nOldRow2 ) // only delete bottom line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow2, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
+ else if ( nNewRow2 < nOldRow2 ) // the bottom part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nNewRow2+1, nTab1, nOldCol2, nOldRow2, nTab2 ),
+ SCE_ALL &~ SCE_TOP );
+
+ // left part
+ if ( nNewCol1 < nOldCol1 ) // only delete left line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nOldCol1, nOldRow2, nTab2 ), SCE_ALL );
+ else if ( nNewCol1 > nOldCol1 ) // the left part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol1, nOldRow1, nTab1, nNewCol1-1, nOldRow2, nTab2 ),
+ SCE_ALL &~ SCE_RIGHT );
+
+ // right part
+ if ( nNewCol2 > nOldCol2 ) // only delete right line
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nOldCol2, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ), SCE_ALL );
+ else if ( nNewCol2 < nOldCol2 ) // the right part which is will be removed
+ lcl_PaintOneRange( pDocSh, ScRange(
+ nNewCol2+1, nOldRow1, nTab1, nOldCol2, nOldRow2, nTab2 ),
+ SCE_ALL &~ SCE_LEFT );
+ }
+}
+
+void ScGridWindow::RFMouseMove( const MouseEvent& rMEvt, bool bUp )
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( mrViewData.GetViewShell() );
+ if (!pHdl)
+ return;
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if (!pRangeFinder || nRFIndex >= pRangeFinder->Count())
+ return;
+ ScRangeFindData& rData = pRangeFinder->GetObject( nRFIndex );
+
+ // Mouse pointer
+
+ if (bRFSize)
+ SetPointer( PointerStyle::Cross );
+ else
+ SetPointer( PointerStyle::Hand );
+
+ // Scrolling
+
+ bool bTimer = false;
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ if ( aPos.X() < 0 ) nDx = -1;
+ if ( aPos.Y() < 0 ) nDy = -1;
+ Size aSize = GetOutputSizePixel();
+ if ( aPos.X() >= aSize.Width() )
+ nDx = 1;
+ if ( aPos.Y() >= aSize.Height() )
+ nDy = 1;
+ if ( nDx != 0 || nDy != 0 )
+ {
+ if ( nDx != 0) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+ bTimer = true;
+ }
+
+ // Switching when fixating (so Scrolling works)
+
+ if ( eWhich == mrViewData.GetActivePart() ) //??
+ {
+ if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if ( nDx > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+
+ if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if ( nDy > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+ }
+
+ // Move
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+
+ ScRange aOld = rData.aRef;
+ ScRange aNew = aOld;
+ if ( bRFSize )
+ {
+ switch (aRFSelectedCorned)
+ {
+ case LEFT_UP:
+ aNew.aStart.SetCol(nPosX);
+ aNew.aStart.SetRow(nPosY);
+ break;
+ case LEFT_DOWN:
+ aNew.aStart.SetCol(nPosX);
+ aNew.aEnd.SetRow(nPosY);
+ break;
+ case RIGHT_UP:
+ aNew.aEnd.SetCol(nPosX);
+ aNew.aStart.SetRow(nPosY);
+ break;
+ case RIGHT_DOWN:
+ aNew.aEnd.SetCol(nPosX);
+ aNew.aEnd.SetRow(nPosY);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ ScDocument& rDoc = mrViewData.GetDocument();
+ tools::Long nStartX = nPosX - nRFAddX;
+ if ( nStartX < 0 ) nStartX = 0;
+ tools::Long nStartY = nPosY - nRFAddY;
+ if ( nStartY < 0 ) nStartY = 0;
+ tools::Long nEndX = nStartX + aOld.aEnd.Col() - aOld.aStart.Col();
+ if ( nEndX > rDoc.MaxCol() )
+ {
+ nStartX -= ( nEndX - rDoc.MaxRow() );
+ nEndX = rDoc.MaxCol();
+ }
+ tools::Long nEndY = nStartY + aOld.aEnd.Row() - aOld.aStart.Row();
+ if ( nEndY > rDoc.MaxRow() )
+ {
+ nStartY -= ( nEndY - rDoc.MaxRow() );
+ nEndY = rDoc.MaxRow();
+ }
+
+ aNew.aStart.SetCol(static_cast<SCCOL>(nStartX));
+ aNew.aStart.SetRow(static_cast<SCROW>(nStartY));
+ aNew.aEnd.SetCol(static_cast<SCCOL>(nEndX));
+ aNew.aEnd.SetRow(static_cast<SCROW>(nEndY));
+ }
+
+ if ( bUp )
+ aNew.PutInOrder(); // For ButtonUp again in the proper order
+
+ if ( aNew != aOld )
+ {
+ pHdl->UpdateRange( nRFIndex, aNew );
+
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+
+ pHdl->UpdateLokReferenceMarks();
+
+ // only redrawing what has been changed...
+ lcl_PaintRefChanged( pDocSh, aOld, aNew );
+
+ // only redraw new frame (synchronously)
+ pDocSh->Broadcast( ScIndexHint( SfxHintId::ScShowRangeFinder, nRFIndex ) );
+
+ PaintImmediately(); // what you move, will be seen immediately
+ }
+
+ // Timer for Scrolling
+
+ if (bTimer)
+ mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
+ else
+ mrViewData.GetView()->ResetTimer();
+}
+
+namespace {
+
+SvxAdjust toSvxAdjust( const ScPatternAttr& rPat )
+{
+ SvxCellHorJustify eHorJust =
+ rPat.GetItem(ATTR_HOR_JUSTIFY).GetValue();
+
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ switch (eHorJust)
+ {
+ case SvxCellHorJustify::Left:
+ case SvxCellHorJustify::Repeat: // not implemented
+ case SvxCellHorJustify::Standard: // always Text if an EditCell type
+ eSvxAdjust = SvxAdjust::Left;
+ break;
+ case SvxCellHorJustify::Right:
+ eSvxAdjust = SvxAdjust::Right;
+ break;
+ case SvxCellHorJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellHorJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+
+ return eSvxAdjust;
+}
+
+std::shared_ptr<ScFieldEditEngine> createEditEngine( ScDocShell* pDocSh, const ScPatternAttr& rPat )
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ auto pEngine = std::make_shared<ScFieldEditEngine>(&rDoc, rDoc.GetEditPool());
+ ScSizeDeviceProvider aProv(pDocSh);
+ pEngine->SetRefDevice(aProv.GetDevice());
+ pEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ SfxItemSet aDefault = pEngine->GetEmptyItemSet();
+ rPat.FillEditItemSet(&aDefault);
+ aDefault.Put( SvxAdjustItem(toSvxAdjust(rPat), EE_PARA_JUST) );
+ pEngine->SetDefaults(aDefault);
+
+ return pEngine;
+}
+
+bool extractURLInfo( const SvxFieldItem* pFieldItem, OUString* pName, OUString* pUrl, OUString* pTarget )
+{
+ if (!pFieldItem)
+ return false;
+
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if (pField->GetClassId() != text::textfield::Type::URL)
+ return false;
+
+ const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
+
+ if (pName)
+ *pName = pURLField->GetRepresentation();
+ if (pUrl)
+ *pUrl = pURLField->GetURL();
+ if (pTarget)
+ *pTarget = pURLField->GetTargetFrame();
+
+ return true;
+}
+
+}
+
+bool ScGridWindow::GetEditUrl( const Point& rPos,
+ OUString* pName, OUString* pUrl, OUString* pTarget )
+{
+ ScTabViewShell* pViewSh = mrViewData.GetViewShell();
+ ScInputHandler* pInputHdl = nullptr;
+ if (pViewSh)
+ pInputHdl = pViewSh->GetInputHandler();
+ EditView* pView = (pInputHdl && pInputHdl->IsInputMode()) ? pInputHdl->GetTableView() : nullptr;
+ if (pView)
+ return extractURLInfo(pView->GetFieldUnderMousePointer(), pName, pUrl, pTarget);
+
+ //! Pass on nPosX/Y?
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( rPos.X(), rPos.Y(), eWhich, nPosX, nPosY );
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ OUString sURL;
+ ScRefCellValue aCell;
+ bool bFound = lcl_GetHyperlinkCell(rDoc, nPosX, nPosY, nTab, aCell, sURL);
+ if( !bFound )
+ return false;
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
+ // bForceToTop = sal_False, use the cell's real position
+ tools::Rectangle aEditRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, false );
+ if (rPos.Y() < aEditRect.Top())
+ return false;
+
+ // vertical can not (yet) be clicked:
+
+ if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
+ return false;
+
+ bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
+ (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block);
+ SvxCellHorJustify eHorJust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
+
+ // EditEngine
+
+ std::shared_ptr<ScFieldEditEngine> pEngine = createEditEngine(pDocSh, *pPattern);
+
+ MapMode aEditMode = mrViewData.GetLogicMode(eWhich); // without draw scaling
+ tools::Rectangle aLogicEdit = PixelToLogic( aEditRect, aEditMode );
+ tools::Long nThisColLogic = aLogicEdit.Right() - aLogicEdit.Left() + 1;
+ Size aPaperSize( 1000000, 1000000 );
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ tools::Long nSizeX = 0;
+ tools::Long nSizeY = 0;
+ mrViewData.GetMergeSizePixel( nPosX, nPosY, nSizeX, nSizeY );
+ aPaperSize = Size(nSizeX, nSizeY );
+ aPaperSize = PixelToLogic(aPaperSize);
+ }
+
+ if (bBreak)
+ aPaperSize.setWidth( nThisColLogic );
+ pEngine->SetPaperSize( aPaperSize );
+
+ std::unique_ptr<EditTextObject> pTextObj;
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ if (aCell.getEditText())
+ pEngine->SetTextCurrentDefaults(*aCell.getEditText());
+ }
+ else // Not an Edit cell and is a formula cell with 'Hyperlink'
+ // function if we have no URL, otherwise it could be a formula
+ // cell ( or other type ? ) with a hyperlink associated with it.
+ {
+ if (sURL.isEmpty())
+ pTextObj = aCell.getFormula()->CreateURLObject();
+ else
+ {
+ OUString aRepres = sURL;
+
+ // TODO: text content of formatted numbers can be different
+ if (aCell.hasNumeric())
+ aRepres = OUString::number(aCell.getValue());
+ else if (aCell.getType() == CELLTYPE_FORMULA)
+ aRepres = aCell.getFormula()->GetString().getString();
+
+ pTextObj = ScEditUtil::CreateURLObjectFromURL(rDoc, sURL, aRepres);
+ }
+
+ if (pTextObj)
+ pEngine->SetTextCurrentDefaults(*pTextObj);
+ }
+
+ tools::Long nStartX = aLogicEdit.Left();
+
+ tools::Long nTextWidth = pEngine->CalcTextWidth();
+ tools::Long nTextHeight = pEngine->GetTextHeight();
+ if ( nTextWidth < nThisColLogic )
+ {
+ if (eHorJust == SvxCellHorJustify::Right)
+ nStartX += nThisColLogic - nTextWidth;
+ else if (eHorJust == SvxCellHorJustify::Center)
+ nStartX += (nThisColLogic - nTextWidth) / 2;
+ }
+
+ aLogicEdit.SetLeft( nStartX );
+ if (!bBreak)
+ aLogicEdit.SetRight( nStartX + nTextWidth );
+
+ // There is one glitch when dealing with a hyperlink cell and
+ // the cell content is NUMERIC. This defaults to right aligned and
+ // we need to adjust accordingly.
+ if (aCell.hasNumeric() && eHorJust == SvxCellHorJustify::Standard)
+ {
+ aLogicEdit.SetRight( aLogicEdit.Left() + nThisColLogic - 1 );
+ aLogicEdit.SetLeft( aLogicEdit.Right() - nTextWidth );
+ }
+ aLogicEdit.SetBottom( aLogicEdit.Top() + nTextHeight );
+
+ Point aLogicClick = PixelToLogic(rPos,aEditMode);
+ if ( aLogicEdit.Contains(aLogicClick) )
+ {
+ EditView aTempView(pEngine.get(), this);
+ aTempView.SetOutputArea( aLogicEdit );
+
+ bool bRet;
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bRet = extractURLInfo(aTempView.GetField(aLogicClick), pName, pUrl, pTarget);
+ }
+ else
+ {
+ MapMode aOld = GetMapMode();
+ SetMapMode(aEditMode); // no return anymore
+ bRet = extractURLInfo(aTempView.GetFieldUnderMousePointer(), pName, pUrl, pTarget);
+ SetMapMode(aOld);
+ }
+ return bRet;
+ }
+ return false;
+}
+
+bool ScGridWindow::HasScenarioButton( const Point& rPosPixel, ScRange& rScenRange )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) )
+ {
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ Size aButSize = mrViewData.GetScenButSize();
+ tools::Long nBWidth = aButSize.Width();
+ if (!nBWidth)
+ return false; // No Button drawn yet -> there is none
+ tools::Long nBHeight = aButSize.Height();
+ tools::Long nHSpace = static_cast<tools::Long>( SC_SCENARIO_HSPACE * mrViewData.GetPPTX() );
+
+ //! cache the Ranges in Table!!!!
+
+ ScMarkData aMarks(rDoc.GetSheetLimits());
+ for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
+ rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
+ ScRangeList aRanges;
+ aMarks.FillRangeListWithMarks( &aRanges, false );
+
+ size_t nRangeCount = aRanges.size();
+ for (size_t j=0; j< nRangeCount; ++j)
+ {
+ ScRange aRange = aRanges[j];
+ // Always extend scenario frame to merged cells where no new non-covered cells
+ // are framed
+ rDoc.ExtendTotalMerge( aRange );
+
+ bool bTextBelow = ( aRange.aStart.Row() == 0 );
+
+ Point aButtonPos;
+ if ( bTextBelow )
+ {
+ aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1,
+ eWhich, true );
+ }
+ else
+ {
+ aButtonPos = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aStart.Row(),
+ eWhich, true );
+ aButtonPos.AdjustY( -nBHeight );
+ }
+ if ( bLayoutRTL )
+ aButtonPos.AdjustX( -(nHSpace - 1) );
+ else
+ aButtonPos.AdjustX( -(nBWidth - nHSpace) ); // same for top or bottom
+
+ tools::Rectangle aButRect( aButtonPos, Size(nBWidth,nBHeight) );
+ if ( aButRect.Contains( rPosPixel ) )
+ {
+ rScenRange = aRange;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ScGridWindow::DrawLayerCreated()
+{
+ SetMapMode( GetDrawMapMode() );
+
+ // initially create overlay objects
+ ImpCreateOverlayObjects();
+}
+
+void ScGridWindow::SetAutoSpellContext( const std::shared_ptr<sc::SpellCheckContext> &ctx )
+{
+ mpSpellCheckCxt = ctx;
+}
+
+void ScGridWindow::ResetAutoSpell()
+{
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->reset();
+}
+
+void ScGridWindow::ResetAutoSpellForContentChange()
+{
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->resetForContentChange();
+}
+
+void ScGridWindow::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ if (!mpSpellCheckCxt)
+ return;
+
+ mpSpellCheckCxt->setMisspellRanges(nPosX, nPosY, pRanges);
+}
+
+const std::vector<editeng::MisspellRanges>* ScGridWindow::GetAutoSpellData( SCCOL nPosX, SCROW nPosY )
+{
+ if (!mpSpellCheckCxt)
+ return nullptr;
+
+ if (!maVisibleRange.isInside(nPosX, nPosY))
+ return nullptr;
+
+ return mpSpellCheckCxt->getMisspellRanges(nPosX, nPosY);
+}
+
+bool ScGridWindow::InsideVisibleRange( SCCOL nPosX, SCROW nPosY )
+{
+ return maVisibleRange.isInside(nPosX, nPosY);
+}
+
+OString ScGridWindow::getCellCursor() const
+{
+ // GridWindow stores a shown cell cursor in mpOOCursors, hence
+ // we can use that to determine whether we would want to be showing
+ // one (client-side) for tiled rendering too.
+ if (!mpOOCursors)
+ return "EMPTY"_ostr;
+
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return mrViewData.describeCellCursorInPrintTwips();
+
+ return mrViewData.describeCellCursor();
+}
+
+void ScGridWindow::notifyKitCellCursor() const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, getCellCursor());
+ if (bListValButton && aListValPos == mrViewData.GetCurPos())
+ updateLOKValListButton(true, aListValPos);
+ std::vector<tools::Rectangle> aRects;
+ GetSelectionRects(aRects);
+ if (aRects.empty() || !mrViewData.IsActive())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, ""_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
+ }
+}
+
+void ScGridWindow::notifyKitCellViewCursor(const SfxViewShell* pForShell) const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+
+ if (pViewShell->GetDocId() != pForShell->GetDocId())
+ return;
+
+ OString aCursor("EMPTY"_ostr);
+ if (mpOOCursors) // cf. getCellCursor above
+ {
+ auto pForTabView = dynamic_cast<const ScTabViewShell *>(pForShell);
+ if (!pForTabView)
+ return;
+
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ aCursor = mrViewData.describeCellCursorInPrintTwips();
+ else
+ aCursor = pForTabView->GetViewData().describeCellCursorAt(
+ mrViewData.GetCurX(), mrViewData.GetCurY()); // our position.
+ }
+ SfxLokHelper::notifyOtherView(pViewShell, pForShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
+}
+
+// Send our cursor details to a view described by @pForShell, or all views
+// if @pForShell is null. In each case send the current view a cell-cursor
+// event, and others a cell_view_cursor event.
+//
+// NB. we need to re-construct the cursor details for each other view in their
+// own zoomed co-ordinate system (but not in scPrintTwipsMsgs mode).
+void ScGridWindow::updateKitCellCursor(const SfxViewShell* pForShell) const
+{
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ // Generate the cursor info string just once and directly send to all.
+ // Calling notifyKitCellViewCursor() would regenerate the
+ // cursor-string unnecessarily.
+ OString aCursor = getCellCursor();
+
+ if (pForShell)
+ {
+ SfxLokHelper::notifyOtherView(pViewShell, pForShell,
+ LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
+ }
+ else
+ {
+ notifyKitCellCursor();
+ SfxLokHelper::notifyOtherViews(pViewShell,
+ LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", aCursor);
+ }
+
+ return;
+ }
+
+ if (!pForShell)
+ {
+ for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
+ it = SfxViewShell::GetNext(*it))
+ updateKitCellCursor(it);
+ return;
+ }
+
+ if (pForShell == mrViewData.GetViewShell())
+ notifyKitCellCursor();
+ else
+ notifyKitCellViewCursor(pForShell);
+}
+
+void ScGridWindow::updateKitOtherCursors() const
+{
+ for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
+ it = SfxViewShell::GetNext(*it))
+ {
+ auto pOther = dynamic_cast<const ScTabViewShell *>(it);
+ if (!pOther)
+ continue;
+ const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
+ assert(pGrid);
+ if (pGrid == this)
+ notifyKitCellCursor();
+ else
+ pGrid->notifyKitCellViewCursor(mrViewData.GetViewShell());
+ }
+}
+
+void ScGridWindow::CursorChanged()
+{
+ // here the created OverlayObjects may be transformed in later versions. For
+ // now, just re-create them
+
+ UpdateCursorOverlay();
+ UpdateSparklineGroupOverlay();
+}
+
+void ScGridWindow::ImpCreateOverlayObjects()
+{
+ UpdateCursorOverlay();
+ UpdateCopySourceOverlay();
+ UpdateSelectionOverlay();
+ UpdateHighlightOverlay();
+ UpdateAutoFillOverlay();
+ UpdateDragRectOverlay();
+ UpdateHeaderOverlay();
+ UpdateShrinkOverlay();
+ UpdateSparklineGroupOverlay();
+}
+
+void ScGridWindow::ImpDestroyOverlayObjects()
+{
+ DeleteCursorOverlay();
+ DeleteCopySourceOverlay();
+ DeleteSelectionOverlay();
+ mpOOHighlight.reset(); // DeleteHighlightOverlay
+ DeleteAutoFillOverlay();
+ DeleteDragRectOverlay();
+ DeleteHeaderOverlay();
+ DeleteShrinkOverlay();
+ DeleteSparklineGroupOverlay();
+}
+
+void ScGridWindow::UpdateAllOverlays()
+{
+ // delete and re-allocate all overlay objects
+
+ ImpDestroyOverlayObjects();
+ ImpCreateOverlayObjects();
+}
+
+void ScGridWindow::DeleteCursorOverlay()
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_CURSOR, "EMPTY"_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
+ mpOOCursors.reset();
+}
+
+void ScGridWindow::DeleteCopySourceOverlay()
+{
+ mpOOSelectionBorder.reset();
+}
+
+void ScGridWindow::UpdateCopySourceOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteCopySourceOverlay();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ if (!mrViewData.ShowPasteSource())
+ return;
+ if (!SC_MOD()->GetInputOptions().GetEnterPasteMode())
+ return;
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (!xOverlayManager.is())
+ return;
+ const ScTransferObj* pTransObj = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(mrViewData.GetActiveWin()));
+ if (!pTransObj)
+ return;
+ ScDocument* pClipDoc = pTransObj->GetDocument();
+ if (!pClipDoc)
+ return;
+
+ SCTAB nCurTab = mrViewData.GetCurPos().Tab();
+
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ mpOOSelectionBorder.reset(new sdr::overlay::OverlayObjectList);
+ for ( size_t i = 0; i < rClipParam.maRanges.size(); ++i )
+ {
+ ScRange const & r = rClipParam.maRanges[i];
+ if (r.aStart.Tab() != nCurTab)
+ continue;
+
+ SCCOL nClipStartX = r.aStart.Col();
+ SCROW nClipStartY = r.aStart.Row();
+ SCCOL nClipEndX = r.aEnd.Col();
+ SCROW nClipEndY = r.aEnd.Row();
+
+ Point aClipStartScrPos = mrViewData.GetScrPos( nClipStartX, nClipStartY, eWhich );
+ Point aClipEndScrPos = mrViewData.GetScrPos( nClipEndX + 1, nClipEndY + 1, eWhich );
+ aClipStartScrPos -= Point(1, 1);
+ tools::Long nSizeXPix = aClipEndScrPos.X() - aClipStartScrPos.X();
+ tools::Long nSizeYPix = aClipEndScrPos.Y() - aClipStartScrPos.Y();
+
+ tools::Rectangle aRect( aClipStartScrPos, Size(nSizeXPix, nSizeYPix) );
+
+ Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
+
+ tools::Rectangle aLogic = PixelToLogic(aRect, aDrawMode);
+ ::basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aLogic);
+ std::unique_ptr<ScOverlayDashedBorder> pDashedBorder(new ScOverlayDashedBorder(aRange, aHighlight));
+ xOverlayManager->add(*pDashedBorder);
+ mpOOSelectionBorder->append(std::move(pDashedBorder));
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+static std::vector<tools::Rectangle> convertPixelToLogical(
+ const ScViewData& rViewData,
+ const std::vector<tools::Rectangle>& rRectangles,
+ tools::Rectangle &rBoundingBox)
+{
+ std::vector<tools::Rectangle> aLogicRects;
+
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+
+ for (const auto& rRectangle : rRectangles)
+ {
+ // We explicitly create a copy, since we need to expand
+ // the rectangle before coordinate conversion
+ tools::Rectangle aRectangle(rRectangle);
+ aRectangle.AdjustRight(1 );
+ aRectangle.AdjustBottom(1 );
+
+ tools::Rectangle aRect(aRectangle.Left() / nPPTX, aRectangle.Top() / nPPTY,
+ aRectangle.Right() / nPPTX, aRectangle.Bottom() / nPPTY);
+
+ rBoundingBox.Union(aRect);
+ aLogicRects.push_back(aRect);
+ }
+ return aLogicRects;
+}
+
+static OString rectanglesToString(const std::vector<tools::Rectangle> &rLogicRects)
+{
+ bool bFirst = true;
+ OStringBuffer aRects;
+ for (const auto &rRect : rLogicRects)
+ {
+ if (!bFirst)
+ aRects.append("; ");
+ bFirst = false;
+ aRects.append(rRect.toString());
+ }
+ return aRects.makeStringAndClear();
+}
+
+/**
+ * Turn the selection ranges rRectangles into the LibreOfficeKit selection, and send to other views.
+ *
+ * @param pLogicRects - if set then don't invoke the callback, just collect the rectangles in the pointed vector.
+ */
+void ScGridWindow::UpdateKitSelection(const std::vector<tools::Rectangle>& rRectangles, std::vector<tools::Rectangle>* pLogicRects)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // If this is true, rRectangles should already in print twips.
+ // If false, rRectangles are in pixels.
+ bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ tools::Rectangle aBoundingBox;
+ std::vector<tools::Rectangle> aConvertedRects;
+
+ if (bInPrintTwips)
+ std::for_each(rRectangles.begin(), rRectangles.end(),
+ [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
+ else
+ aConvertedRects = convertPixelToLogical(mrViewData, rRectangles, aBoundingBox);
+
+ const std::vector<tools::Rectangle>& rLogicRects = bInPrintTwips ? rRectangles : aConvertedRects;
+ if (pLogicRects)
+ {
+ *pLogicRects = rLogicRects;
+ return;
+ }
+
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->UpdateInputHandler();
+ OString sBoundingBoxString = "EMPTY"_ostr;
+ if (!aBoundingBox.IsEmpty())
+ sBoundingBoxString = aBoundingBox.toString();
+ OString aRectListString = rectanglesToString(rLogicRects);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectListString);
+
+ if (bInPrintTwips)
+ {
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
+ "selection", aRectListString);
+ return;
+ }
+
+ for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
+ it = SfxViewShell::GetNext(*it))
+ {
+ if (it == pViewShell)
+ continue;
+ auto pOther = dynamic_cast<const ScTabViewShell *>(it);
+ if (!pOther)
+ return;
+
+ const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
+ assert(pGrid);
+
+ // Fetch pixels & convert for each view separately.
+ tools::Rectangle aDummyBBox;
+ std::vector<tools::Rectangle> aPixelRects;
+ pGrid->GetPixelRectsFor(mrViewData.GetMarkData() /* ours */, aPixelRects);
+ auto aOtherLogicRects = convertPixelToLogical(pOther->GetViewData(), aPixelRects, aDummyBBox);
+ SfxLokHelper::notifyOtherView(pViewShell, pOther, LOK_CALLBACK_TEXT_VIEW_SELECTION,
+ "selection", rectanglesToString(aOtherLogicRects));
+ }
+}
+
+/**
+ * Fetch the selection ranges for other views into the LibreOfficeKit selection,
+ * map them into our view co-ordinates and send to our view.
+ */
+void ScGridWindow::updateOtherKitSelections() const
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ for (SfxViewShell* it = SfxViewShell::GetFirst(); it;
+ it = SfxViewShell::GetNext(*it))
+ {
+ auto pOther = dynamic_cast<const ScTabViewShell *>(it);
+ if (!pOther)
+ return;
+
+ // Fetch pixels & convert for each view separately.
+ tools::Rectangle aBoundingBox;
+ std::vector<tools::Rectangle> aRects;
+ OString aRectsString;
+ GetRectsAnyFor(pOther->GetViewData().GetMarkData() /* theirs */, aRects, bInPrintTwips);
+ if (bInPrintTwips)
+ {
+ std::for_each(aRects.begin(), aRects.end(),
+ [&aBoundingBox](const tools::Rectangle& rRect) { aBoundingBox.Union(rRect); });
+ aRectsString = rectanglesToString(aRects);
+ }
+ else
+ aRectsString = rectanglesToString(
+ convertPixelToLogical(pViewShell->GetViewData(), aRects, aBoundingBox));
+
+ if (it == pViewShell)
+ {
+ OString sBoundingBoxString = "EMPTY"_ostr;
+ if (!aBoundingBox.IsEmpty())
+ sBoundingBoxString = aBoundingBox.toString();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
+ }
+ else
+ SfxLokHelper::notifyOtherView(it, pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION,
+ "selection", aRectsString);
+ }
+}
+
+namespace
+{
+
+void updateLibreOfficeKitAutoFill(const ScViewData& rViewData, tools::Rectangle const & rRectangle)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+
+ OString sRectangleString = "EMPTY"_ostr;
+ if (!rRectangle.IsEmpty())
+ {
+ // selection start handle
+ tools::Rectangle aLogicRectangle(
+ rRectangle.Left() / nPPTX, rRectangle.Top() / nPPTY,
+ rRectangle.Right() / nPPTX, rRectangle.Bottom() / nPPTY);
+ sRectangleString = aLogicRectangle.toString();
+ }
+
+ ScTabViewShell* pViewShell = rViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_AUTO_FILL_AREA, sRectangleString);
+}
+
+} //end anonymous namespace
+
+void ScGridWindow::UpdateCursorOverlay()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ // Existing OverlayObjects may be transformed in later versions.
+ // For now, just re-create them.
+
+ DeleteCursorOverlay();
+
+ std::vector<tools::Rectangle> aPixelRects;
+
+ // determine the cursor rectangles in pixels (moved from ScGridWindow::DrawCursor)
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ SCCOL nX = mrViewData.GetCurX();
+ SCROW nY = mrViewData.GetCurY();
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern(nX,nY,nTab);
+
+ if (!comphelper::LibreOfficeKit::isActive() && !maVisibleRange.isInside(nX, nY))
+ {
+ if (maVisibleRange.mnCol2 < nX || maVisibleRange.mnRow2 < nY)
+ return; // no further check needed, nothing visible
+
+ // fdo#87382 Also display the cell cursor for the visible part of
+ // merged cells if the view position is part of merged cells.
+ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
+ if (rMerge.GetColMerge() <= 1 && rMerge.GetRowMerge() <= 1)
+ return; // not merged and invisible
+
+ SCCOL nX2 = nX + rMerge.GetColMerge() - 1;
+ SCROW nY2 = nY + rMerge.GetRowMerge() - 1;
+ // Check if the middle or tail of the merged range is visible.
+ if (maVisibleRange.mnCol1 > nX2 || maVisibleRange.mnRow1 > nY2)
+ return; // no visible part
+ }
+
+ // don't show the cursor in overlapped cells
+ const ScMergeFlagAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE_FLAG);
+ bool bOverlapped = rMergeFlag.IsOverlapped();
+
+ // left or above of the screen?
+ bool bVis = comphelper::LibreOfficeKit::isActive() || ( nX>=mrViewData.GetPosX(eHWhich) && nY>=mrViewData.GetPosY(eVWhich) );
+ if (!bVis)
+ {
+ SCCOL nEndX = nX;
+ SCROW nEndY = nY;
+ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
+ if (rMerge.GetColMerge() > 1)
+ nEndX += rMerge.GetColMerge()-1;
+ if (rMerge.GetRowMerge() > 1)
+ nEndY += rMerge.GetRowMerge()-1;
+ bVis = ( nEndX>=mrViewData.GetPosX(eHWhich) && nEndY>=mrViewData.GetPosY(eVWhich) );
+ }
+
+ if ( bVis && !bOverlapped && !mrViewData.HasEditView(eWhich) && mrViewData.IsActive() )
+ {
+ Point aScrPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // completely right of/below the screen?
+ // (test with logical start position in aScrPos)
+ bool bMaybeVisible;
+ if ( bLayoutRTL )
+ bMaybeVisible = ( aScrPos.X() >= -2 && aScrPos.Y() >= -2 );
+ else
+ {
+ Size aOutSize = GetOutputSizePixel();
+ bMaybeVisible = ( aScrPos.X() <= aOutSize.Width() + 2 && aScrPos.Y() <= aOutSize.Height() + 2 );
+ }
+
+ // in the tiled rendering case, don't limit to the screen size
+ if (bMaybeVisible || comphelper::LibreOfficeKit::isActive())
+ {
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
+
+ if (bLayoutRTL)
+ aScrPos.AdjustX( -(nSizeXPix - 2) ); // move instead of mirroring
+
+ // show the cursor as 4 (thin) rectangles
+ tools::Rectangle aRect(aScrPos, Size(nSizeXPix - 1, nSizeYPix - 1));
+
+ float fScaleFactor = GetDPIScaleFactor();
+
+ tools::Long aCursorWidth = 1 * fScaleFactor;
+
+ tools::Rectangle aLeft = aRect;
+ aLeft.AdjustTop( -aCursorWidth );
+ aLeft.AdjustBottom(aCursorWidth );
+ aLeft.SetRight( aLeft.Left() );
+ aLeft.AdjustLeft( -aCursorWidth );
+
+ tools::Rectangle aRight = aRect;
+ aRight.AdjustTop( -aCursorWidth );
+ aRight.AdjustBottom(aCursorWidth );
+ aRight.SetLeft( aRight.Right() );
+ aRight.AdjustRight(aCursorWidth );
+
+ tools::Rectangle aTop = aRect;
+ aTop.SetBottom( aTop.Top() );
+ aTop.AdjustTop( -aCursorWidth );
+
+ tools::Rectangle aBottom = aRect;
+ aBottom.SetTop( aBottom.Bottom() );
+ aBottom.AdjustBottom(aCursorWidth );
+
+ aPixelRects.push_back(aLeft);
+ aPixelRects.push_back(aRight);
+ aPixelRects.push_back(aTop);
+ aPixelRects.push_back(aBottom);
+ }
+ }
+
+ if ( !aPixelRects.empty() )
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
+ updateKitCellCursor(nullptr);
+ }
+ else
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is())
+ {
+ Color aCursorColor = GetSettings().GetStyleSettings().GetAccentColor();
+ if (mrViewData.GetActivePart() != eWhich)
+ // non-active pane uses a different color.
+ aCursorColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+
+ // tdf#143733, tdf#145080 - improve border visibility
+ // constants picked for maximum consistency at 100% and adequate response on zoom
+ // line width = 1.5 at 100% (0.75 left +/- 0.75 right), 50% = 1, 200% = 1.25, 400% = 2.25
+ const double MinSize = 0.25 * GetDPIScaleFactor();
+ double fZoom(mrViewData.GetZoomX() * 0.5);
+ for(const tools::Rectangle & rRA : aPixelRects)
+ {
+ basegfx::B2DRange aRB(rRA.Left() - MinSize - fZoom, rRA.Top() - MinSize - fZoom,
+ rRA.Right() + MinSize + fZoom, rRA.Bottom() + MinSize + fZoom);
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Solid,
+ aCursorColor,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOCursors.reset(new sdr::overlay::OverlayObjectList);
+ mpOOCursors->append(std::move(pOverlay));
+ }
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::GetCellSelection(std::vector<tools::Rectangle>& rLogicRects)
+{
+ std::vector<tools::Rectangle> aRects;
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ GetSelectionRectsPrintTwips(aRects);
+ else
+ GetSelectionRects(aRects);
+ UpdateKitSelection(aRects, &rLogicRects);
+}
+
+void ScGridWindow::DeleteSelectionOverlay()
+{
+ mpOOSelection.reset();
+}
+
+void ScGridWindow::UpdateSelectionOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteSelectionOverlay();
+ std::vector<tools::Rectangle> aRects;
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ GetSelectionRectsPrintTwips(aRects);
+ else
+ GetSelectionRects(aRects);
+
+ if (!aRects.empty() && mrViewData.IsActive())
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // notify the LibreOfficeKit too
+ UpdateKitSelection(aRects);
+ }
+ else if (xOverlayManager.is())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ for(const tools::Rectangle & rRA : aRects)
+ {
+ if (bLayoutRTL)
+ {
+ basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ else
+ {
+ basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ }
+
+ // get the system's highlight color
+ const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlight,
+ std::move(aRanges),
+ true));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOSelection.reset(new sdr::overlay::OverlayObjectList);
+ mpOOSelection->append(std::move(pOverlay));
+ }
+ }
+ else
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY"_ostr);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY"_ostr);
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "EMPTY"_ostr);
+
+ ScInputHandler* pViewHdl = SC_MOD()->GetInputHdl(pViewShell);
+ if (!pViewHdl || !pViewHdl->IsEditMode())
+ {
+ std::vector<ReferenceMark> aReferenceMarks;
+ ScInputHandler::SendReferenceMarks(pViewShell, aReferenceMarks);
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::UpdateHighlightOverlay()
+{
+ mpOOHighlight.reset(); // DeleteHighlightOverlay
+ std::vector<tools::Rectangle> aRects;
+ if (comphelper::LibreOfficeKit::isActive() &&
+ comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ GetRectsAnyFor(mrViewData.GetHighlightData(), aRects, true);
+ else
+ GetPixelRectsFor(mrViewData.GetHighlightData(), aRects);
+
+ if (!aRects.empty() && mrViewData.IsActive())
+ {
+ // #i70788# get the OverlayManager safely
+ if (rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ for(const tools::Rectangle & rRA : aRects)
+ {
+ if (bLayoutRTL)
+ {
+ basegfx::B2DRange aRB(rRA.Left(), rRA.Top() - 1, rRA.Right() + 1, rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ else
+ {
+ basegfx::B2DRange aRB(rRA.Left() - 1, rRA.Top() - 1, rRA.Right(), rRA.Bottom());
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+ }
+
+ const Color aBackgroundColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ Color aHighlightColor = Application::GetSettings().GetStyleSettings().GetAccentColor();
+ aHighlightColor.Merge(aBackgroundColor, 100);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlightColor,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOHighlight.reset(new sdr::overlay::OverlayObjectList);
+ mpOOHighlight->append(std::move(pOverlay));
+ }
+ }
+}
+
+void ScGridWindow::DeleteAutoFillOverlay()
+{
+ mpOOAutoFill.reset();
+ mpAutoFillRect.reset();
+}
+
+void ScGridWindow::UpdateAutoFillOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteAutoFillOverlay();
+
+ // get the AutoFill handle rectangle in pixels
+
+ if ( !(bAutoMarkVisible && aAutoMarkPos.Tab() == mrViewData.GetTabNo() &&
+ !mrViewData.HasEditView(eWhich) && mrViewData.IsActive()) )
+ return;
+
+ SCCOL nX = aAutoMarkPos.Col();
+ SCROW nY = aAutoMarkPos.Row();
+
+ if (!maVisibleRange.isInside(nX, nY) && !comphelper::LibreOfficeKit::isActive())
+ {
+ // Autofill mark is not visible. Bail out.
+ return;
+ }
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // tdf#143733 tdf#145080 - improve border visibility
+ // constants picked for maximum consistency at 100%
+ // size = 6 at 100% (as before), 50% = 4.5, 200% = 9, 400% = 15
+ const float fScaleFactor = 3 * GetDPIScaleFactor();
+ const double fZoom(3 * mrViewData.GetZoomX());
+ // Size should be even
+ Size aFillHandleSize(fZoom + fScaleFactor, fZoom + fScaleFactor);
+
+ Point aFillPos = mrViewData.GetScrPos( nX, nY, eWhich, true );
+ tools::Long nSizeXPix;
+ tools::Long nSizeYPix;
+ mrViewData.GetMergeSizePixel( nX, nY, nSizeXPix, nSizeYPix );
+
+ if (bLayoutRTL && !comphelper::LibreOfficeKit::isActive())
+ aFillPos.AdjustX( -(nSizeXPix - 2 + (aFillHandleSize.Width() / 2)) );
+ else
+ aFillPos.AdjustX(nSizeXPix - (aFillHandleSize.Width() / 2) );
+
+ aFillPos.AdjustY(nSizeYPix );
+ aFillPos.AdjustY( -(aFillHandleSize.Height() / 2) );
+
+ tools::Rectangle aFillRect(aFillPos, aFillHandleSize);
+
+ // expand rect to increase hit area
+ mpAutoFillRect = aFillRect;
+ mpAutoFillRect->expand(fScaleFactor);
+
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (comphelper::LibreOfficeKit::isActive()) // notify the LibreOfficeKit
+ {
+ updateLibreOfficeKitAutoFill(mrViewData, aFillRect);
+ }
+ else if (xOverlayManager.is())
+ {
+ Color aHandleColor = GetSettings().GetStyleSettings().GetHighlightColor();
+ if (mrViewData.GetActivePart() != eWhich)
+ // non-active pane uses a different color.
+ aHandleColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ basegfx::B2DRange aRB = vcl::unotools::b2DRectangleFromRectangle(aFillRect);
+
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Solid,
+ aHandleColor,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOAutoFill.reset(new sdr::overlay::OverlayObjectList);
+ mpOOAutoFill->append(std::move(pOverlay));
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteDragRectOverlay()
+{
+ mpOODragRect.reset();
+}
+
+void ScGridWindow::UpdateDragRectOverlay()
+{
+ bool bInPrintTwips = comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteDragRectOverlay();
+
+ // get the rectangles in pixels (moved from DrawDragRect)
+
+ if ( bDragRect || bPagebreakDrawn )
+ {
+ std::vector<tools::Rectangle> aPixelRects;
+
+ SCCOL nX1 = bDragRect ? nDragStartX : aPagebreakDrag.aStart.Col();
+ SCROW nY1 = bDragRect ? nDragStartY : aPagebreakDrag.aStart.Row();
+ SCCOL nX2 = bDragRect ? nDragEndX : aPagebreakDrag.aEnd.Col();
+ SCROW nY2 = bDragRect ? nDragEndY : aPagebreakDrag.aEnd.Row();
+
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ SCCOL nPosX = mrViewData.GetPosX(WhichH(eWhich));
+ SCROW nPosY = mrViewData.GetPosY(WhichV(eWhich));
+ if (nX1 < nPosX) nX1 = nPosX;
+ if (nX2 < nPosX) nX2 = nPosX;
+ if (nY1 < nPosY) nY1 = nPosY;
+ if (nY2 < nPosY) nY2 = nPosY;
+
+ Point aScrPos(bInPrintTwips ? mrViewData.GetPrintTwipsPos( nX1, nY1 )
+ : mrViewData.GetScrPos( nX1, nY1, eWhich ) );
+
+ tools::Long nSizeXPix=0;
+ tools::Long nSizeYPix=0;
+ ScDocument& rDoc = mrViewData.GetDocument();
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+ SCCOLROW i;
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ tools::Long nAdjust = comphelper::LibreOfficeKit::isActive() ? 0 : 2;
+
+ if (rDoc.ValidCol(nX2) && nX2>=nX1)
+ for (i=nX1; i<=nX2; i++)
+ {
+ tools::Long nWidth = rDoc.GetColWidth( static_cast<SCCOL>(i), nTab );
+ nSizeXPix += bInPrintTwips ? nWidth : ScViewData::ToPixel( nWidth, nPPTX );
+ }
+ else
+ {
+ aScrPos.AdjustX( -nLayoutSign );
+ nSizeXPix += nAdjust;
+ }
+
+ if (rDoc.ValidRow(nY2) && nY2>=nY1)
+ for (i=nY1; i<=nY2; i++)
+ {
+ tools::Long nHeight = rDoc.GetRowHeight( i, nTab );
+ nSizeYPix += bInPrintTwips ? nHeight : ScViewData::ToPixel( nHeight, nPPTY );
+ }
+ else
+ {
+ aScrPos.AdjustY( -1 );
+ nSizeYPix += nAdjust;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ nSizeXPix -= 2;
+ nSizeYPix -= 2;
+ }
+
+ aScrPos.AdjustX( -(nAdjust * nLayoutSign) );
+ aScrPos.AdjustY( -1 * nAdjust );
+ tools::Rectangle aRect( aScrPos.X(), aScrPos.Y(),
+ aScrPos.X() + ( nSizeXPix + nAdjust ) * nLayoutSign, aScrPos.Y() + nSizeYPix + nAdjust );
+ if ( bLayoutRTL )
+ {
+ aRect.SetLeft( aRect.Right() ); // end position is left
+ aRect.SetRight( aScrPos.X() );
+ }
+
+ if ( meDragInsertMode == INS_CELLSDOWN )
+ {
+ aPixelRects.emplace_back( aRect.Left()+1, aRect.Top()+3, aRect.Left()+1, aRect.Bottom()-2 );
+ aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+3, aRect.Right()-1, aRect.Bottom()-2 );
+ aPixelRects.emplace_back( aRect.Left()+1, aRect.Top(), aRect.Right()-1, aRect.Top()+2 );
+ aPixelRects.emplace_back( aRect.Left()+1, aRect.Bottom()-1, aRect.Right()-1, aRect.Bottom()-1 );
+ }
+ else if ( meDragInsertMode == INS_CELLSRIGHT )
+ {
+ aPixelRects.emplace_back( aRect.Left(), aRect.Top()+1, aRect.Left()+2, aRect.Bottom()-1 );
+ aPixelRects.emplace_back( aRect.Right()-1, aRect.Top()+1, aRect.Right()-1, aRect.Bottom()-1 );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Top()+1, aRect.Right()-2, aRect.Top()+1 );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-1, aRect.Right()-2, aRect.Bottom()-1 );
+ }
+ else
+ {
+ aPixelRects.emplace_back( aRect.Left(), aRect.Top(), aRect.Left()+2, aRect.Bottom() );
+ aPixelRects.emplace_back( aRect.Right()-2, aRect.Top(), aRect.Right(), aRect.Bottom() );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Top(), aRect.Right()-3, aRect.Top()+2 );
+ aPixelRects.emplace_back( aRect.Left()+3, aRect.Bottom()-2, aRect.Right()-3, aRect.Bottom() );
+ }
+
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+
+ for(const tools::Rectangle & rRA : aPixelRects)
+ {
+ basegfx::B2DRange aRB(rRA.Left(), rRA.Top(), rRA.Right() + 1, rRA.Bottom() + 1);
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+ }
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Invert,
+ COL_BLACK,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOODragRect.reset(new sdr::overlay::OverlayObjectList);
+ mpOODragRect->append(std::move(pOverlay));
+ }
+
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (comphelper::LibreOfficeKit::isActive() && pViewShell)
+ {
+ OString aRectsString;
+ tools::Rectangle aBoundingBox;
+
+ std::vector<tools::Rectangle> aRectangles;
+ aRectangles.push_back(aRect);
+
+ if (bInPrintTwips)
+ {
+ aBoundingBox = aRect;
+ aRectsString = rectanglesToString(aRectangles);
+ }
+ else
+ {
+ aRectsString = rectanglesToString(convertPixelToLogical(pViewShell->GetViewData(), aRectangles, aBoundingBox));
+ }
+
+ OString sBoundingBoxString = "EMPTY"_ostr;
+ if (!aBoundingBox.IsEmpty())
+ sBoundingBoxString = aBoundingBox.toString();
+
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, sBoundingBoxString);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aRectsString);
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteHeaderOverlay()
+{
+ mpOOHeader.reset();
+}
+
+void ScGridWindow::UpdateHeaderOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteHeaderOverlay();
+
+ // Pixel rectangle is in aInvertRect
+ if ( !aInvertRect.IsEmpty() )
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
+ {
+ // Color aHighlight = GetSettings().GetStyleSettings().GetHighlightColor();
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ basegfx::B2DRange aRB(aInvertRect.Left(), aInvertRect.Top(), aInvertRect.Right() + 1, aInvertRect.Bottom() + 1);
+
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Invert,
+ COL_BLACK,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOHeader.reset(new sdr::overlay::OverlayObjectList);
+ mpOOHeader->append(std::move(pOverlay));
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteShrinkOverlay()
+{
+ mpOOShrink.reset();
+}
+
+void ScGridWindow::UpdateShrinkOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ DeleteShrinkOverlay();
+
+ // get the rectangle in pixels
+
+ tools::Rectangle aPixRect;
+ ScRange aRange;
+ SCTAB nTab = mrViewData.GetTabNo();
+ if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() &&
+ mrViewData.GetDelMark( aRange ) )
+ {
+ //! limit to visible area
+ if ( aRange.aStart.Col() <= aRange.aEnd.Col() &&
+ aRange.aStart.Row() <= aRange.aEnd.Row() )
+ {
+ Point aStart = mrViewData.GetScrPos( aRange.aStart.Col(),
+ aRange.aStart.Row(), eWhich );
+ Point aEnd = mrViewData.GetScrPos( aRange.aEnd.Col()+1,
+ aRange.aEnd.Row()+1, eWhich );
+ aEnd.AdjustX( -1 );
+ aEnd.AdjustY( -1 );
+
+ aPixRect = tools::Rectangle( aStart,aEnd );
+ }
+ }
+
+ if ( !aPixRect.IsEmpty() )
+ {
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is() && !comphelper::LibreOfficeKit::isActive())
+ {
+ std::vector< basegfx::B2DRange > aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+ basegfx::B2DRange aRB(aPixRect.Left(), aPixRect.Top(), aPixRect.Right() + 1, aPixRect.Bottom() + 1);
+
+ aRB.transform(aTransform);
+ aRanges.push_back(aRB);
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Invert,
+ COL_BLACK,
+ std::move(aRanges),
+ false));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOShrink.reset(new sdr::overlay::OverlayObjectList);
+ mpOOShrink->append(std::move(pOverlay));
+ }
+ }
+
+ if ( aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+}
+
+void ScGridWindow::DeleteSparklineGroupOverlay()
+{
+ mpOOSparklineGroup.reset();
+}
+
+void ScGridWindow::UpdateSparklineGroupOverlay()
+{
+ MapMode aDrawMode = GetDrawMapMode();
+
+ MapMode aOldMode = GetMapMode();
+ if (aOldMode != aDrawMode)
+ SetMapMode(aDrawMode);
+
+ DeleteSparklineGroupOverlay();
+
+ ScAddress aCurrentAddress = mrViewData.GetCurPos();
+
+ ScDocument& rDocument = mrViewData.GetDocument();
+ if (auto pSparkline = rDocument.GetSparkline(aCurrentAddress))
+ {
+ mpOOSparklineGroup.reset(new sdr::overlay::OverlayObjectList);
+
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+ if (xOverlayManager.is())
+ {
+ auto* pList = rDocument.GetSparklineList(aCurrentAddress.Tab());
+ if (pList)
+ {
+ auto const& pSparklines = pList->getSparklinesFor(pSparkline->getSparklineGroup());
+
+ Color aColor = SvtOptionsDrawinglayer::getHilightColor();
+
+ std::vector<basegfx::B2DRange> aRanges;
+ const basegfx::B2DHomMatrix aTransform(GetOutDev()->GetInverseViewTransformation());
+
+ for (auto const& pCurrentSparkline : pSparklines)
+ {
+ SCCOL nColumn = pCurrentSparkline->getColumn();
+ SCROW nRow = pCurrentSparkline->getRow();
+
+ Point aStart = mrViewData.GetScrPos(nColumn, nRow, eWhich);
+ Point aEnd = mrViewData.GetScrPos(nColumn + 1, nRow + 1, eWhich);
+
+ basegfx::B2DRange aRange(aStart.X(), aStart.Y(), aEnd.X(), aEnd.Y());
+
+ aRange.transform(aTransform);
+ aRanges.push_back(aRange);
+ }
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlay(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aColor, std::move(aRanges), true));
+
+ xOverlayManager->add(*pOverlay);
+ mpOOSparklineGroup->append(std::move(pOverlay));
+ }
+ }
+ }
+
+ if (aOldMode != aDrawMode)
+ SetMapMode(aOldMode);
+}
+
+// #i70788# central method to get the OverlayManager safely
+rtl::Reference<sdr::overlay::OverlayManager> ScGridWindow::getOverlayManager() const
+{
+ SdrPageView* pPV = mrViewData.GetView()->GetScDrawView()->GetSdrPageView();
+
+ if(pPV)
+ {
+ SdrPageWindow* pPageWin = pPV->FindPageWindow( *GetOutDev() );
+
+ if ( pPageWin )
+ {
+ return pPageWin->GetOverlayManager();
+ }
+ }
+
+ return rtl::Reference<sdr::overlay::OverlayManager>();
+}
+
+void ScGridWindow::flushOverlayManager()
+{
+ // #i70788# get the OverlayManager safely
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = getOverlayManager();
+
+ if (xOverlayManager.is())
+ xOverlayManager->flush();
+}
+
+ScViewData& ScGridWindow::getViewData()
+{
+ return mrViewData;
+}
+
+FactoryFunction ScGridWindow::GetUITestFactory() const
+{
+ return ScGridWinUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx
new file mode 100644
index 0000000000..01f5f39dd7
--- /dev/null
+++ b/sc/source/ui/view/gridwin2.cxx
@@ -0,0 +1,1312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/lok.hxx>
+
+#include <attrib.hxx>
+#include <gridwin.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <pivot.hxx>
+#include <uiitems.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <pagedata.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpshttab.hxx>
+#include <dbdocfun.hxx>
+#include <checklistmenu.hxx>
+#include <dpcontrol.hxx>
+#include <userlist.hxx>
+#include <scabstdlg.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
+
+#include <unordered_map>
+#include <memory>
+#include <vector>
+
+using namespace css;
+using namespace css::sheet;
+using css::sheet::DataPilotFieldOrientation;
+using std::vector;
+
+DataPilotFieldOrientation ScGridWindow::GetDPFieldOrientation( SCCOL nCol, SCROW nRow ) const
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return DataPilotFieldOrientation_HIDDEN;
+
+ DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
+
+ // Check for page field first.
+ if (nCol > 0)
+ {
+ // look for the dimension header left of the drop-down arrow
+ tools::Long nField = pDPObj->GetHeaderDim( ScAddress( nCol-1, nRow, nTab ), nOrient );
+ if ( nField >= 0 && nOrient == DataPilotFieldOrientation_PAGE )
+ {
+ bool bIsDataLayout = false;
+ OUString aFieldName = pDPObj->GetDimName( nField, bIsDataLayout );
+ if ( !aFieldName.isEmpty() && !bIsDataLayout )
+ return DataPilotFieldOrientation_PAGE;
+ }
+ }
+
+ nOrient = DataPilotFieldOrientation_HIDDEN;
+
+ // Now, check for row/column field.
+ tools::Long nField = pDPObj->GetHeaderDim(ScAddress(nCol, nRow, nTab), nOrient);
+ if (nField >= 0 && (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW) )
+ {
+ bool bIsDataLayout = false;
+ OUString aFieldName = pDPObj->GetDimName(nField, bIsDataLayout);
+ if (!aFieldName.isEmpty() && !bIsDataLayout)
+ return nOrient;
+ }
+
+ return DataPilotFieldOrientation_HIDDEN;
+}
+
+// private method for mouse button handling
+bool ScGridWindow::DoPageFieldSelection( SCCOL nCol, SCROW nRow )
+{
+ if (GetDPFieldOrientation( nCol, nRow ) == DataPilotFieldOrientation_PAGE)
+ {
+ LaunchPageFieldMenu( nCol, nRow );
+ return true;
+ }
+ return false;
+}
+
+bool ScGridWindow::DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ Point aDiffPix = rMEvt.GetPosPixel();
+
+ aDiffPix -= aScrPos;
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( bLayoutRTL && !bLOKActive )
+ aDiffPix.setX( -aDiffPix.X() );
+
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ // The button height should not use the merged cell height, should still use single row height
+ nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
+ Size aScrSize(nSizeX-1, nSizeY-1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ mpFilterButton.reset(new ScDPFieldButton(GetOutDev(), &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc));
+ mpFilterButton->setBoundingBox(aScrPos, aScrSize, bLayoutRTL && !bLOKActive);
+ mpFilterButton->setPopupLeft(bLayoutRTL && bLOKActive ? false : bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL
+ Point aPopupPos;
+ Size aPopupSize;
+ mpFilterButton->getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ if ( DoPageFieldSelection( nCol, nRow ) )
+ return true;
+
+ bool bFilterActive = IsAutoFilterActive(nCol, nRow, nTab);
+ mpFilterButton->setHasHiddenMember(bFilterActive);
+ mpFilterButton->setDrawBaseButton(false);
+ mpFilterButton->setDrawPopupButton(true);
+ mpFilterButton->setPopupPressed(true);
+ mpFilterButton->draw();
+ LaunchAutoFilterMenu(nCol, nRow);
+ return true;
+ }
+
+ return false;
+}
+
+void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+
+ if (pDPObj)
+ {
+ DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
+ ScAddress aPos( nCol, nRow, nTab );
+ ScAddress aDimPos = aPos;
+ if (!bButton && bPopup && aDimPos.Col() > 0)
+ // For page field selection cell, the real field position is to the left.
+ aDimPos.IncCol(-1);
+
+ if (bMultiField && DPTestMultiFieldPopupArrow(rMEvt, aPos, pDPObj))
+ {
+ // Multi-field pop up menu has been launched. Don't activate
+ // field move or regular popup.
+ return;
+ }
+
+ tools::Long nField = pDPObj->GetHeaderDim(aDimPos, nOrient);
+ if ( nField >= 0 )
+ {
+ bDPMouse = false;
+ nDPField = nField;
+ pDragDPObj = pDPObj;
+
+ if (bPopup && DPTestFieldPopupArrow(rMEvt, aPos, aDimPos, pDPObj))
+ {
+ // field name pop up menu has been launched. Don't activate
+ // field move.
+ return;
+ }
+
+ if (bButton)
+ {
+ bDPMouse = true;
+ DPTestMouse( rMEvt, true );
+ StartTracking();
+ }
+ }
+ else if ( pDPObj->IsFilterButton(aPos) )
+ {
+ ReleaseMouse(); // may have been captured in ButtonDown
+
+ ScQueryParam aQueryParam;
+ SCTAB nSrcTab = 0;
+ const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
+ OSL_ENSURE(pDesc, "no sheet source for filter button");
+ if (pDesc)
+ {
+ aQueryParam = pDesc->GetQueryParam();
+ nSrcTab = pDesc->GetSourceRange().aStart.Tab();
+ }
+
+ SfxItemSetFixed<SCITEM_QUERYDATA, SCITEM_QUERYDATA> aArgSet( mrViewData.GetViewShell()->GetPool() );
+ aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA, &mrViewData, &aQueryParam ) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScPivotFilterDlg> pDlg(
+ pFact->CreateScPivotFilterDlg(
+ mrViewData.GetViewShell()->GetFrameWeld(), aArgSet, nSrcTab));
+ if ( pDlg->Execute() == RET_OK )
+ {
+ ScSheetSourceDesc aNewDesc(&rDoc);
+ if (pDesc)
+ aNewDesc = *pDesc;
+
+ const ScQueryItem& rQueryItem = pDlg->GetOutputItem();
+ aNewDesc.SetQueryParam(rQueryItem.GetQueryData());
+
+ ScDPObject aNewObj( *pDPObj );
+ aNewObj.SetSheetDesc( aNewDesc );
+ ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
+ aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
+ mrViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("Nothing here");
+ }
+}
+
+void ScGridWindow::DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt )
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab);
+ if (!pDPObj)
+ return;
+
+ if (!pDPObj->GetSaveData()->GetDrillDown())
+ return;
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
+ Size aScrSize(nSizeX - 1, nSizeY - 1);
+
+ sal_uInt16 nIndent = 0;
+ if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
+ nIndent = pIndentItem->GetValue();
+
+ // Check if the mouse cursor is clicking on the toggle +/- box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setDrawToggleButton(true, true, nIndent);
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getToggleBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ // Mouse cursor inside the toggle +/- box.
+ sheet::DataPilotTableHeaderData aData;
+ ScAddress aCellPos(nCol, nRow, nTab);
+ pDPObj->GetHeaderPositionData(aCellPos, aData);
+ ScDPObject aNewObj(*pDPObj);
+ pDPObj->ToggleDetails(aData, &aNewObj);
+ ScDBDocFunc aFunc(*mrViewData.GetDocShell());
+ aFunc.DataPilotUpdate(pDPObj, &aNewObj, true, false);
+ }
+}
+
+// Data Pilot interaction
+
+void ScGridWindow::DPTestMouse( const MouseEvent& rMEvt, bool bMove )
+{
+ OSL_ENSURE(pDragDPObj, "pDragDPObj missing");
+
+ // scroll window if at edges
+ //! move this to separate method
+
+ bool bTimer = false;
+ Point aPixel = rMEvt.GetPosPixel();
+
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ if ( aPixel.X() < 0 )
+ nDx = -1;
+ if ( aPixel.Y() < 0 )
+ nDy = -1;
+ Size aSize = GetOutputSizePixel();
+ if ( aPixel.X() >= aSize.Width() )
+ nDx = 1;
+ if ( aPixel.Y() >= aSize.Height() )
+ nDy = 1;
+ if ( nDx != 0 || nDy != 0 )
+ {
+ UpdateDragRect( false, tools::Rectangle() );
+
+ if ( nDx != 0)
+ mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 )
+ mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+
+ bTimer = true;
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPixel.X(), aPixel.Y(), eWhich, nPosX, nPosY );
+ bool bMouseLeft;
+ bool bMouseTop;
+ mrViewData.GetMouseQuadrant( aPixel, eWhich, nPosX, nPosY, bMouseLeft, bMouseTop );
+
+ ScAddress aPos( nPosX, nPosY, mrViewData.GetTabNo() );
+
+ tools::Rectangle aPosRect;
+ DataPilotFieldOrientation nOrient;
+ tools::Long nDimPos;
+ bool bHasRange = pDragDPObj->GetHeaderDrag( aPos, bMouseLeft, bMouseTop, nDPField,
+ aPosRect, nOrient, nDimPos );
+ UpdateDragRect( bHasRange && bMove, aPosRect );
+
+ bool bIsDataLayout;
+ sal_Int32 nDimFlags = 0;
+ OUString aDimName = pDragDPObj->GetDimName( nDPField, bIsDataLayout, &nDimFlags );
+ bool bAllowed = !bHasRange || ScDPObject::IsOrientationAllowed( nOrient, nDimFlags );
+
+ if (bMove) // set mouse pointer
+ {
+ PointerStyle ePointer = PointerStyle::PivotDelete;
+ if ( !bAllowed )
+ ePointer = PointerStyle::NotAllowed;
+ else if ( bHasRange )
+ switch (nOrient)
+ {
+ case DataPilotFieldOrientation_COLUMN: ePointer = PointerStyle::PivotCol; break;
+ case DataPilotFieldOrientation_ROW: ePointer = PointerStyle::PivotRow; break;
+ case DataPilotFieldOrientation_PAGE:
+ case DataPilotFieldOrientation_DATA: ePointer = PointerStyle::PivotField; break;
+ default: break;
+ }
+ SetPointer( ePointer );
+ }
+ else // execute change
+ {
+ if (!bHasRange)
+ nOrient = DataPilotFieldOrientation_HIDDEN;
+
+ if ( bIsDataLayout && ( nOrient != DataPilotFieldOrientation_COLUMN &&
+ nOrient != DataPilotFieldOrientation_ROW ) )
+ {
+ // removing data layout is not allowed
+ mrViewData.GetView()->ErrorMessage(STR_PIVOT_MOVENOTALLOWED);
+ }
+ else if ( bAllowed )
+ {
+ ScDPSaveData aSaveData( *pDragDPObj->GetSaveData() );
+
+ ScDPSaveDimension* pDim;
+ if ( bIsDataLayout )
+ pDim = aSaveData.GetDataLayoutDimension();
+ else
+ pDim = aSaveData.GetDimensionByName(aDimName);
+ pDim->SetOrientation( nOrient );
+ aSaveData.SetPosition( pDim, nDimPos );
+
+ //! docfunc method with ScDPSaveData as argument?
+
+ ScDPObject aNewObj( *pDragDPObj );
+ aNewObj.SetSaveData( aSaveData );
+ ScDBDocFunc aFunc( *mrViewData.GetDocShell() );
+ // when dragging fields, allow re-positioning (bAllowMove)
+ aFunc.DataPilotUpdate( pDragDPObj, &aNewObj, true, false, true );
+ mrViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+
+ if (bTimer && bMove)
+ mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
+ else
+ mrViewData.GetView()->ResetTimer();
+}
+
+bool ScGridWindow::DPTestFieldPopupArrow(
+ const MouseEvent& rMEvt, const ScAddress& rPos, const ScAddress& rDimPos, ScDPObject* pDPObj)
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY);
+ Size aScrSize(nSizeX-1, nSizeY-1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ // Mouse cursor inside the popup arrow box. Launch the field menu.
+ DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, rDimPos, pDPObj);
+ return true;
+ }
+
+ return false;
+}
+
+bool ScGridWindow::DPTestMultiFieldPopupArrow(
+ const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj)
+{
+ bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() );
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+
+ // Get the geometry of the cell.
+ Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich);
+ tools::Long nSizeX, nSizeY;
+ mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY);
+ Size aScrSize(nSizeX - 1, nSizeY - 1);
+
+ // Check if the mouse cursor is clicking on the popup arrow box.
+ ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY());
+ aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL);
+ aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ aBtn.setDrawPopupButtonMulti(true);
+ Point aPopupPos;
+ Size aPopupSize;
+ aBtn.getPopupBoundingBox(aPopupPos, aPopupSize);
+ tools::Rectangle aRect(aPopupPos, aPopupSize);
+ if (aRect.Contains(rMEvt.GetPosPixel()))
+ {
+ DataPilotFieldOrientation nOrient;
+ pDPObj->GetHeaderDim(rPos, nOrient);
+ // Mouse cursor inside the popup arrow box. Launch the multi-field menu.
+ DPLaunchMultiFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, pDPObj, nOrient);
+ return true;
+ }
+
+ return false;
+}
+
+namespace {
+
+struct DPFieldPopupData : public ScCheckListMenuControl::ExtendedData
+{
+ ScDPLabelData maLabels;
+ ScDPObject* mpDPObj;
+ tools::Long mnDim;
+};
+
+struct DPMultiFieldPopupData : public DPFieldPopupData
+{
+ std::vector<tools::Long> maFieldIndices;
+ std::vector<OUString> maFieldNames;
+};
+
+class DPFieldPopupOKAction : public ScCheckListMenuControl::Action
+{
+public:
+ explicit DPFieldPopupOKAction(ScGridWindow* p) :
+ mpGridWindow(p) {}
+
+ virtual bool execute() override
+ {
+ mpGridWindow->UpdateDPFromFieldPopupMenu();
+ return true;
+ }
+private:
+ VclPtr<ScGridWindow> mpGridWindow;
+};
+
+class DPFieldChangedAction : public ScCheckListMenuControl::Action
+{
+public:
+ explicit DPFieldChangedAction(ScGridWindow* p) :
+ mpGridWindow(p) {}
+
+ virtual bool execute() override
+ {
+ mpGridWindow->UpdateDPPopupMenuForFieldChange();
+ return true;
+ }
+private:
+ VclPtr<ScGridWindow> mpGridWindow;
+};
+
+class PopupSortAction : public ScCheckListMenuControl::Action
+{
+public:
+ enum SortType { ASCENDING, DESCENDING, CUSTOM };
+
+ explicit PopupSortAction(ScDPObject* pDPObject, tools::Long nDimIndex, SortType eType,
+ sal_uInt16 nUserListIndex, ScTabViewShell* pViewShell)
+ : mpDPObject(pDPObject)
+ , mnDimIndex(nDimIndex)
+ , meType(eType)
+ , mnUserListIndex(nUserListIndex)
+ , mpViewShell(pViewShell)
+ {}
+
+ virtual bool execute() override
+ {
+ switch (meType)
+ {
+ case ASCENDING:
+ mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, true);
+ break;
+ case DESCENDING:
+ mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, false);
+ break;
+ case CUSTOM:
+ mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, true, &mnUserListIndex);
+ break;
+ default:
+ ;
+ }
+ return true;
+ }
+
+private:
+ ScDPObject* mpDPObject;
+ tools::Long mnDimIndex;
+ SortType meType;
+ sal_uInt16 mnUserListIndex;
+ ScTabViewShell* mpViewShell;
+};
+
+}
+
+void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize,
+ const ScAddress& rAddress, ScDPObject* pDPObject)
+{
+ DataPilotFieldOrientation nOrient;
+ tools::Long nDimIndex = pDPObject->GetHeaderDim(rAddress, nOrient);
+
+ DPLaunchFieldPopupMenu(rScreenPosition, rScreenSize, nDimIndex, pDPObject);
+}
+
+bool lcl_FillDPFieldPopupData(tools::Long nDimIndex, ScDPObject* pDPObj,
+ DPFieldPopupData& rDPData, bool& bDimOrientNotPage)
+{
+ if (!pDPObj)
+ return false;
+
+ rDPData.mnDim = nDimIndex;
+ pDPObj->GetSource();
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName(rDPData.mnDim, bIsDataLayout);
+ pDPObj->BuildAllDimensionMembers();
+ const ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ const ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName(aDimName);
+ if (!pDim)
+ // This should never happen.
+ return false;
+
+ bDimOrientNotPage = pDim->GetOrientation() != DataPilotFieldOrientation_PAGE;
+
+ // We need to get the list of field members.
+ pDPObj->FillLabelData(rDPData.mnDim, rDPData.maLabels);
+ rDPData.mpDPObj = pDPObj;
+
+ return true;
+}
+
+void ScGridWindow::DPLaunchMultiFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize,
+ ScDPObject* pDPObj, DataPilotFieldOrientation nOrient)
+{
+ if (!pDPObj)
+ return;
+
+ pDPObj->GetSource();
+
+ std::unique_ptr<DPMultiFieldPopupData> pDPData(new DPMultiFieldPopupData);
+ pDPObj->GetFieldIdsNames(nOrient, pDPData->maFieldIndices, pDPData->maFieldNames);
+
+ if (pDPData->maFieldIndices.empty())
+ return;
+
+ tools::Long nDimIndex = pDPData->maFieldIndices[0];
+
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup.reset();
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ false, -1, true));
+
+ mpDPFieldPopup->addFields(pDPData->maFieldNames);
+ DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj, true);
+
+ DPConfigFieldPopup();
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+
+ tools::Rectangle aCellRect(rScreenPosition, rScreenSize);
+ mpDPFieldPopup->launch(pPopupParent, aCellRect);
+}
+
+void ScGridWindow::DPPopulateFieldMembers(const ScDPLabelData& rLabelData)
+{
+ // Populate field members.
+ size_t n = rLabelData.maMembers.size();
+ mpDPFieldPopup->setMemberSize(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ const ScDPLabelData::Member& rMem = rLabelData.maMembers[i];
+ OUString aName = rMem.getDisplayName();
+ if (aName.isEmpty())
+ // Use special string for an empty name.
+ mpDPFieldPopup->addMember(ScResId(STR_EMPTYDATA), 0.0, rMem.mbVisible, false);
+ else
+ mpDPFieldPopup->addMember(rMem.getDisplayName(), 0.0, rMem.mbVisible, false);
+ }
+}
+
+void ScGridWindow::DPSetupFieldPopup(std::unique_ptr<ScCheckListMenuControl::ExtendedData> pDPData,
+ bool bDimOrientNotPage, ScDPObject* pDPObj,
+ bool bMultiField)
+{
+ if (!mpDPFieldPopup || !pDPObj)
+ return;
+
+ const ScDPLabelData& rLabelData = static_cast<DPFieldPopupData*>(pDPData.get())->maLabels;
+ const tools::Long nDimIndex = static_cast<DPFieldPopupData*>(pDPData.get())->mnDim;
+ mpDPFieldPopup->setExtendedData(std::move(pDPData));
+
+ if (bMultiField)
+ mpDPFieldPopup->setFieldChangedAction(new DPFieldChangedAction(this));
+
+ mpDPFieldPopup->setOKAction(new DPFieldPopupOKAction(this));
+ DPPopulateFieldMembers(rLabelData);
+
+ if (bDimOrientNotPage)
+ {
+ vector<OUString> aUserSortNames;
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (pUserList)
+ {
+ size_t n = pUserList->size();
+ aUserSortNames.reserve(n);
+ for (size_t i = 0; i < n; ++i)
+ {
+ const ScUserListData& rData = (*pUserList)[i];
+ aUserSortNames.push_back(rData.GetString());
+ }
+ }
+
+ // Populate the menus.
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ mpDPFieldPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_ASC),
+ new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::ASCENDING, 0, pViewShell));
+ mpDPFieldPopup->addMenuItem(
+ ScResId(STR_MENU_SORT_DESC),
+ new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::DESCENDING, 0, pViewShell));
+
+ ScListSubMenuControl* pSubMenu = mpDPFieldPopup->addSubMenuItem(ScResId(STR_MENU_SORT_CUSTOM), !aUserSortNames.empty(), false);
+ if (pSubMenu)
+ {
+ size_t n = aUserSortNames.size();
+ for (size_t i = 0; i < n; ++i)
+ {
+ pSubMenu->addMenuItem(aUserSortNames[i],
+ new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::CUSTOM, sal_uInt16(i), pViewShell));
+ }
+ pSubMenu->resizeToFitMenuItems();
+ }
+ }
+
+ mpDPFieldPopup->initMembers();
+}
+
+void ScGridWindow::DPConfigFieldPopup()
+{
+ if (!mpDPFieldPopup)
+ return;
+
+ ScCheckListMenuControl::Config aConfig;
+ aConfig.mbAllowEmptySet = false;
+ aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
+ mpDPFieldPopup->setConfig(aConfig);
+}
+
+void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize,
+ tools::Long nDimIndex, ScDPObject* pDPObj)
+{
+ std::unique_ptr<DPFieldPopupData> pDPData(new DPFieldPopupData);
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup.reset();
+
+ vcl::ILibreOfficeKitNotifier* pNotifier = nullptr;
+ if (comphelper::LibreOfficeKit::isActive())
+ pNotifier = SfxViewShell::Current();
+
+ weld::Window* pPopupParent = GetFrameWeld();
+ mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
+ false, -1, pNotifier));
+
+ DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj);
+
+ DPConfigFieldPopup();
+
+ if (IsMouseCaptured())
+ ReleaseMouse();
+
+ tools::Rectangle aCellRect(rScrPos, rScrSize);
+ mpDPFieldPopup->launch(pPopupParent, aCellRect);
+}
+
+void ScGridWindow::UpdateDPPopupMenuForFieldChange()
+{
+ if (!mpDPFieldPopup)
+ return;
+
+ DPMultiFieldPopupData* pDPData = static_cast<DPMultiFieldPopupData*>(mpDPFieldPopup->getExtendedData());
+ if (!pDPData)
+ return;
+
+ if (pDPData->maFieldIndices.empty())
+ return;
+
+ tools::Long nIndex = mpDPFieldPopup->getField();
+ if (nIndex < 0)
+ return;
+
+ tools::Long nDimIndex = pDPData->maFieldIndices[nIndex];
+ if (nDimIndex == pDPData->mnDim)
+ return;
+
+ bool bDimOrientNotPage = true;
+ if (!lcl_FillDPFieldPopupData(nDimIndex, pDPData->mpDPObj, *pDPData, bDimOrientNotPage))
+ return;
+
+ mpDPFieldPopup->clearMembers();
+
+ DPPopulateFieldMembers(pDPData->maLabels);
+
+ mpDPFieldPopup->initMembers();
+}
+
+void ScGridWindow::UpdateDPFromFieldPopupMenu()
+{
+ typedef std::unordered_map<OUString, OUString> MemNameMapType;
+
+ if (!mpDPFieldPopup)
+ return;
+
+ DPFieldPopupData* pDPData = static_cast<DPFieldPopupData*>(mpDPFieldPopup->getExtendedData());
+ if (!pDPData)
+ return;
+
+ ScDPObject* pDPObj = pDPData->mpDPObj;
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+
+ bool bIsDataLayout;
+ OUString aDimName = pDPObj->GetDimName(pDPData->mnDim, bIsDataLayout);
+ ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aDimName);
+ if (!pDim)
+ return;
+
+ // Build a map of layout names to original names.
+ const ScDPLabelData& rLabelData = pDPData->maLabels;
+ MemNameMapType aMemNameMap;
+ for (const auto& rMember : rLabelData.maMembers)
+ aMemNameMap.emplace(rMember.maLayoutName, rMember.maName);
+
+ // The raw result may contain a mixture of layout names and original names.
+ ScCheckListMenuControl::ResultType aRawResult;
+ mpDPFieldPopup->getResult(aRawResult);
+
+ std::unordered_map<OUString, bool> aResult;
+ for (const auto& rItem : aRawResult)
+ {
+ MemNameMapType::const_iterator itrNameMap = aMemNameMap.find(rItem.aName);
+ if (itrNameMap == aMemNameMap.end())
+ {
+ // This is an original member name. Use it as-is.
+ OUString aName = rItem.aName;
+ if (aName == ScResId(STR_EMPTYDATA))
+ // Translate the special empty name into an empty string.
+ aName.clear();
+
+ aResult.emplace(aName, rItem.bValid);
+ }
+ else
+ {
+ // This is a layout name. Get the original member name and use it.
+ aResult.emplace(itrNameMap->second, rItem.bValid);
+ }
+ }
+ pDim->UpdateMemberVisibility(aResult);
+
+ ScDBDocFunc aFunc(*mrViewData.GetDocShell());
+ aFunc.UpdatePivotTable(*pDPObj, true, false);
+}
+
+namespace {
+
+template <typename T>
+inline
+T lcl_getValidValue(T value, T defvalue)
+{
+ return (value <0) ? defvalue : value;
+}
+
+} // anonymous namespace
+
+bool ScGridWindow::UpdateVisibleRange()
+{
+ ScDocument const& rDoc = mrViewData.GetDocument();
+ SCCOL nPosX = 0;
+ SCROW nPosY = 0;
+ SCCOL nXRight = rDoc.MaxCol();
+ SCROW nYBottom = rDoc.MaxRow();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ nPosX = lcl_getValidValue(pViewShell->GetLOKStartHeaderCol(), nPosX);
+ nPosY = lcl_getValidValue(pViewShell->GetLOKStartHeaderRow(), nPosY);
+ nXRight = lcl_getValidValue(pViewShell->GetLOKEndHeaderCol(), nXRight);
+ nYBottom = lcl_getValidValue(pViewShell->GetLOKEndHeaderRow(), nYBottom);
+ }
+ else
+ {
+ nPosX = mrViewData.GetPosX(eHWhich);
+ nPosY = mrViewData.GetPosY(eVWhich);
+ nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich);
+ if (nXRight > rDoc.MaxCol())
+ nXRight = rDoc.MaxCol();
+ nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich);
+ if (nYBottom > rDoc.MaxRow())
+ nYBottom = rDoc.MaxRow();
+ }
+
+ // Store the current visible range.
+ return maVisibleRange.set(nPosX, nPosY, nXRight, nYBottom);
+}
+
+void ScGridWindow::DPMouseMove( const MouseEvent& rMEvt )
+{
+ DPTestMouse( rMEvt, true );
+}
+
+void ScGridWindow::DPMouseButtonUp( const MouseEvent& rMEvt )
+{
+ bDPMouse = false;
+ ReleaseMouse();
+
+ DPTestMouse( rMEvt, false );
+ SetPointer( PointerStyle::Arrow );
+}
+
+void ScGridWindow::UpdateDragRect( bool bShowRange, const tools::Rectangle& rPosRect )
+{
+ SCCOL nStartX = ( rPosRect.Left() >= 0 ) ? static_cast<SCCOL>(rPosRect.Left()) : SCCOL_MAX;
+ SCROW nStartY = ( rPosRect.Top() >= 0 ) ? static_cast<SCROW>(rPosRect.Top()) : SCROW_MAX;
+ SCCOL nEndX = ( rPosRect.Right() >= 0 ) ? static_cast<SCCOL>(rPosRect.Right()) : SCCOL_MAX;
+ SCROW nEndY = ( rPosRect.Bottom() >= 0 ) ? static_cast<SCROW>(rPosRect.Bottom()) : SCROW_MAX;
+
+ if ( bShowRange == bDragRect && nDragStartX == nStartX && nDragEndX == nEndX &&
+ nDragStartY == nStartY && nDragEndY == nEndY )
+ {
+ return; // everything unchanged
+ }
+
+ if ( bShowRange )
+ {
+ nDragStartX = nStartX;
+ nDragStartY = nStartY;
+ nDragEndX = nEndX;
+ nDragEndY = nEndY;
+ bDragRect = true;
+ }
+ else
+ bDragRect = false;
+
+ UpdateDragRectOverlay();
+}
+
+// Page-Break Mode
+
+sal_uInt16 ScGridWindow::HitPageBreak( const Point& rMouse, ScRange* pSource,
+ SCCOLROW* pBreak, SCCOLROW* pPrev )
+{
+ sal_uInt16 nFound = SC_PD_NONE; // 0
+ ScRange aSource;
+ SCCOLROW nBreak = 0;
+ SCCOLROW nPrev = 0;
+
+ ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData();
+ if ( pPageData )
+ {
+ bool bHori = false;
+ bool bVert = false;
+ SCCOL nHitX = 0;
+ SCROW nHitY = 0;
+
+ tools::Long nMouseX = rMouse.X();
+ tools::Long nMouseY = rMouse.Y();
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( nMouseX, nMouseY, eWhich, nPosX, nPosY );
+ Point aTL = mrViewData.GetScrPos( nPosX, nPosY, eWhich );
+ Point aBR = mrViewData.GetScrPos( nPosX+1, nPosY+1, eWhich );
+
+ // Horizontal more tolerances as for vertical, because there is more space
+ if ( nMouseX <= aTL.X() + 4 )
+ {
+ bHori = true;
+ nHitX = nPosX;
+ }
+ else if ( nMouseX >= aBR.X() - 6 )
+ {
+ bHori = true;
+ nHitX = nPosX+1; // left edge of the next cell
+ }
+ if ( nMouseY <= aTL.Y() + 2 )
+ {
+ bVert = true;
+ nHitY = nPosY;
+ }
+ else if ( nMouseY >= aBR.Y() - 4 )
+ {
+ bVert = true;
+ nHitY = nPosY+1; // upper edge of the next cell
+ }
+
+ if ( bHori || bVert )
+ {
+ sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
+ for (sal_uInt16 nPos=0; nPos<nCount && !nFound; nPos++)
+ {
+ ScPrintRangeData& rData = pPageData->GetData(nPos);
+ ScRange aRange = rData.GetPrintRange();
+ bool bLHit = ( bHori && nHitX == aRange.aStart.Col() );
+ bool bRHit = ( bHori && nHitX == aRange.aEnd.Col() + 1 );
+ bool bTHit = ( bVert && nHitY == aRange.aStart.Row() );
+ bool bBHit = ( bVert && nHitY == aRange.aEnd.Row() + 1 );
+ bool bInsideH = ( nPosX >= aRange.aStart.Col() && nPosX <= aRange.aEnd.Col() );
+ bool bInsideV = ( nPosY >= aRange.aStart.Row() && nPosY <= aRange.aEnd.Row() );
+
+ if ( bLHit )
+ {
+ if ( bTHit )
+ nFound = SC_PD_RANGE_TL;
+ else if ( bBHit )
+ nFound = SC_PD_RANGE_BL;
+ else if ( bInsideV )
+ nFound = SC_PD_RANGE_L;
+ }
+ else if ( bRHit )
+ {
+ if ( bTHit )
+ nFound = SC_PD_RANGE_TR;
+ else if ( bBHit )
+ nFound = SC_PD_RANGE_BR;
+ else if ( bInsideV )
+ nFound = SC_PD_RANGE_R;
+ }
+ else if ( bTHit && bInsideH )
+ nFound = SC_PD_RANGE_T;
+ else if ( bBHit && bInsideH )
+ nFound = SC_PD_RANGE_B;
+ if (nFound)
+ aSource = aRange;
+
+ // breaks
+
+ if ( bVert && bInsideH && !nFound )
+ {
+ size_t nRowCount = rData.GetPagesY();
+ const SCROW* pRowEnd = rData.GetPageEndY();
+ for (size_t nRowPos=0; nRowPos+1<nRowCount; nRowPos++)
+ if ( pRowEnd[nRowPos]+1 == nHitY )
+ {
+ nFound = SC_PD_BREAK_V;
+ aSource = aRange;
+ nBreak = nHitY;
+ if ( nRowPos )
+ nPrev = pRowEnd[nRowPos-1]+1;
+ else
+ nPrev = aRange.aStart.Row();
+ }
+ }
+ if ( bHori && bInsideV && !nFound )
+ {
+ size_t nColCount = rData.GetPagesX();
+ const SCCOL* pColEnd = rData.GetPageEndX();
+ for (size_t nColPos=0; nColPos+1<nColCount; nColPos++)
+ if ( pColEnd[nColPos]+1 == nHitX )
+ {
+ nFound = SC_PD_BREAK_H;
+ aSource = aRange;
+ nBreak = nHitX;
+ if ( nColPos )
+ nPrev = pColEnd[nColPos-1]+1;
+ else
+ nPrev = aRange.aStart.Col();
+ }
+ }
+ }
+ }
+ }
+
+ if (pSource)
+ *pSource = aSource; // print break
+ if (pBreak)
+ *pBreak = nBreak; // X/Y position of the moved page break
+ if (pPrev)
+ *pPrev = nPrev; // X/Y beginning of the page, which is above the break
+ return nFound;
+}
+
+void ScGridWindow::PagebreakMove( const MouseEvent& rMEvt, bool bUp )
+{
+ //! Combine scrolling and switching with RFMouseMove !
+ //! (Inverting before scrolling is different)
+
+ // Scrolling
+
+ bool bTimer = false;
+ Point aPos = rMEvt.GetPosPixel();
+ SCCOL nDx = 0;
+ SCROW nDy = 0;
+ if ( aPos.X() < 0 ) nDx = -1;
+ if ( aPos.Y() < 0 ) nDy = -1;
+ Size aSize = GetOutputSizePixel();
+ if ( aPos.X() >= aSize.Width() )
+ nDx = 1;
+ if ( aPos.Y() >= aSize.Height() )
+ nDy = 1;
+ if ( nDx != 0 || nDy != 0 )
+ {
+ if ( bPagebreakDrawn ) // invert
+ {
+ bPagebreakDrawn = false;
+ UpdateDragRectOverlay();
+ }
+
+ if ( nDx != 0 ) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) );
+ if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) );
+ bTimer = true;
+ }
+
+ // Switching when fixating (so Scrolling works)
+
+ if ( eWhich == mrViewData.GetActivePart() ) //??
+ {
+ if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if ( nDx > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+
+ if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if ( nDy > 0 )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+ }
+
+ // from here new
+
+ // Searching for a position between the cells (before nPosX / nPosY)
+ SCCOL nPosX;
+ SCROW nPosY;
+ mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY );
+ bool bLeft, bTop;
+ mrViewData.GetMouseQuadrant( aPos, eWhich, nPosX, nPosY, bLeft, bTop );
+ if ( !bLeft ) ++nPosX;
+ if ( !bTop ) ++nPosY;
+
+ bool bBreak = ( nPagebreakMouse == SC_PD_BREAK_H || nPagebreakMouse == SC_PD_BREAK_V );
+ bool bHide = false;
+ bool bToEnd = false;
+ ScRange aDrawRange = aPagebreakSource;
+ if ( bBreak )
+ {
+ if ( nPagebreakMouse == SC_PD_BREAK_H )
+ {
+ if ( nPosX > aPagebreakSource.aStart.Col() &&
+ nPosX <= aPagebreakSource.aEnd.Col() + 1 ) // to the end is also allowed
+ {
+ bToEnd = ( nPosX == aPagebreakSource.aEnd.Col() + 1 );
+ aDrawRange.aStart.SetCol( nPosX );
+ aDrawRange.aEnd.SetCol( nPosX - 1 );
+ }
+ else
+ bHide = true;
+ }
+ else
+ {
+ if ( nPosY > aPagebreakSource.aStart.Row() &&
+ nPosY <= aPagebreakSource.aEnd.Row() + 1 ) // to the end is also allowed
+ {
+ bToEnd = ( nPosY == aPagebreakSource.aEnd.Row() + 1 );
+ aDrawRange.aStart.SetRow( nPosY );
+ aDrawRange.aEnd.SetRow( nPosY - 1 );
+ }
+ else
+ bHide = true;
+ }
+ }
+ else
+ {
+ if ( nPagebreakMouse & SC_PD_RANGE_L )
+ aDrawRange.aStart.SetCol( nPosX );
+ if ( nPagebreakMouse & SC_PD_RANGE_T )
+ aDrawRange.aStart.SetRow( nPosY );
+ if ( nPagebreakMouse & SC_PD_RANGE_R )
+ {
+ if ( nPosX > 0 )
+ aDrawRange.aEnd.SetCol( nPosX-1 );
+ else
+ bHide = true;
+ }
+ if ( nPagebreakMouse & SC_PD_RANGE_B )
+ {
+ if ( nPosY > 0 )
+ aDrawRange.aEnd.SetRow( nPosY-1 );
+ else
+ bHide = true;
+ }
+ if ( aDrawRange.aStart.Col() > aDrawRange.aEnd.Col() ||
+ aDrawRange.aStart.Row() > aDrawRange.aEnd.Row() )
+ bHide = true;
+ }
+
+ if ( !bPagebreakDrawn || bUp || aDrawRange != aPagebreakDrag )
+ {
+ // draw...
+
+ if ( bPagebreakDrawn )
+ {
+ // invert
+ bPagebreakDrawn = false;
+ }
+ aPagebreakDrag = aDrawRange;
+ if ( !bUp && !bHide )
+ {
+ // revert
+ bPagebreakDrawn = true;
+ }
+ UpdateDragRectOverlay();
+ }
+
+ // when ButtonUp execute the changes
+
+ if ( bUp )
+ {
+ ScViewFunc* pViewFunc = mrViewData.GetView();
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ if ( bBreak )
+ {
+ bool bColumn = ( nPagebreakMouse == SC_PD_BREAK_H );
+ SCCOLROW nNew = bColumn ? static_cast<SCCOLROW>(nPosX) : static_cast<SCCOLROW>(nPosY);
+ if ( nNew != nPagebreakBreak )
+ {
+ if (bUndo)
+ {
+ OUString aUndo = ScResId( STR_UNDO_DRAG_BREAK );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() );
+ }
+
+ bool bGrow = !bHide && nNew > nPagebreakBreak;
+ if ( bColumn )
+ {
+ if (rDoc.HasColBreak(static_cast<SCCOL>(nPagebreakBreak), nTab) & ScBreakType::Manual)
+ {
+ ScAddress aOldAddr( static_cast<SCCOL>(nPagebreakBreak), nPosY, nTab );
+ pViewFunc->DeletePageBreak( true, true, &aOldAddr, false );
+ }
+ if ( !bHide && !bToEnd ) // not at the end
+ {
+ ScAddress aNewAddr( static_cast<SCCOL>(nNew), nPosY, nTab );
+ pViewFunc->InsertPageBreak( true, true, &aNewAddr, false );
+ }
+ if ( bGrow )
+ {
+ // change last break to hard, and change scaling
+ bool bManualBreak(rDoc.HasColBreak(static_cast<SCCOL>(nPagebreakPrev), nTab) & ScBreakType::Manual);
+ if ( static_cast<SCCOL>(nPagebreakPrev) > aPagebreakSource.aStart.Col() && !bManualBreak )
+ {
+ ScAddress aPrev( static_cast<SCCOL>(nPagebreakPrev), nPosY, nTab );
+ pViewFunc->InsertPageBreak( true, true, &aPrev, false );
+ }
+
+ if (!pDocSh->AdjustPrintZoom( ScRange(
+ static_cast<SCCOL>(nPagebreakPrev),0,nTab, static_cast<SCCOL>(nNew-1),0,nTab ) ))
+ bGrow = false;
+ }
+ }
+ else
+ {
+ if (rDoc.HasRowBreak(nPagebreakBreak, nTab) & ScBreakType::Manual)
+ {
+ ScAddress aOldAddr( nPosX, nPagebreakBreak, nTab );
+ pViewFunc->DeletePageBreak( false, true, &aOldAddr, false );
+ }
+ if ( !bHide && !bToEnd ) // not at the end
+ {
+ ScAddress aNewAddr( nPosX, nNew, nTab );
+ pViewFunc->InsertPageBreak( false, true, &aNewAddr, false );
+ }
+ if ( bGrow )
+ {
+ // change last break to hard, and change scaling
+ bool bManualBreak(rDoc.HasRowBreak(nPagebreakPrev, nTab) & ScBreakType::Manual);
+ if ( nPagebreakPrev > aPagebreakSource.aStart.Row() && !bManualBreak )
+ {
+ ScAddress aPrev( nPosX, nPagebreakPrev, nTab );
+ pViewFunc->InsertPageBreak( false, true, &aPrev, false );
+ }
+
+ if (!pDocSh->AdjustPrintZoom( ScRange(
+ 0,nPagebreakPrev,nTab, 0,nNew-1,nTab ) ))
+ bGrow = false;
+ }
+ }
+
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+
+ if (!bGrow) // otherwise has already happened in AdjustPrintZoom
+ {
+ pViewFunc->UpdatePageBreakData( true );
+ pDocSh->SetDocumentModified();
+ }
+ }
+ }
+ else if ( bHide || aPagebreakDrag != aPagebreakSource )
+ {
+ // set print range
+
+ OUString aNewRanges;
+ sal_uInt16 nOldCount = rDoc.GetPrintRangeCount( nTab );
+ if ( nOldCount )
+ {
+ for (sal_uInt16 nPos=0; nPos<nOldCount; nPos++)
+ {
+ const ScRange* pOld = rDoc.GetPrintRange( nTab, nPos );
+ if ( pOld )
+ {
+ OUString aTemp;
+ if ( *pOld != aPagebreakSource )
+ aTemp = pOld->Format(rDoc, ScRefFlags::VALID);
+ else if ( !bHide )
+ aTemp = aPagebreakDrag.Format(rDoc, ScRefFlags::VALID);
+ if (!aTemp.isEmpty())
+ {
+ if ( !aNewRanges.isEmpty() )
+ aNewRanges += ";";
+ aNewRanges += aTemp;
+ }
+ }
+ }
+ }
+ else if (!bHide)
+ aNewRanges = aPagebreakDrag.Format(rDoc, ScRefFlags::VALID);
+
+ pViewFunc->SetPrintRanges( rDoc.IsPrintEntireSheet( nTab ), &aNewRanges, nullptr, nullptr, false );
+ }
+ }
+
+ // Timer for Scrolling
+
+ if (bTimer && !bUp)
+ mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event
+ else
+ mrViewData.GetView()->ResetTimer();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin3.cxx b/sc/source/ui/view/gridwin3.cxx
new file mode 100644
index 0000000000..e550447da8
--- /dev/null
+++ b/sc/source/ui/view/gridwin3.cxx
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdpagv.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/sizeitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/ptitem.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <gridwin.hxx>
+#include <dbfunc.hxx>
+#include <viewdata.hxx>
+#include <output.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+
+#include <drawutil.hxx>
+#include <document.hxx>
+#include <comphelper/lok.hxx>
+
+bool ScGridWindow::DrawMouseButtonDown(const MouseEvent& rMEvt)
+{
+ bool bRet = false;
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDraw && !mrViewData.IsRefMode())
+ {
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ pDraw->SetWindow( this );
+ Point aLogicPos = PixelToLogic(rMEvt.GetPosPixel());
+ if ( pDraw->IsDetectiveHit( aLogicPos ) )
+ {
+ // nothing on detective arrows (double click is evaluated on ButtonUp)
+ bRet = true;
+ }
+ else
+ {
+ bRet = pDraw->MouseButtonDown( rMEvt );
+ if ( bRet )
+ UpdateStatusPosSize();
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+ }
+
+ // cancel draw with right key
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ if ( pDrView && !rMEvt.IsLeft() && !bRet )
+ {
+ pDrView->BrkAction();
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool ScGridWindow::DrawMouseButtonUp(const MouseEvent& rMEvt)
+{
+ ScViewFunc* pView = mrViewData.GetView();
+ bool bRet = false;
+ FuPoor* pDraw = pView->GetDrawFuncPtr();
+ if (pDraw && !mrViewData.IsRefMode())
+ {
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ pDraw->SetWindow( this );
+ bRet = pDraw->MouseButtonUp( rMEvt );
+
+ // execute "format paint brush" for drawing objects
+ SfxItemSet* pDrawBrush = pView->GetDrawBrushSet();
+ if ( pDrawBrush )
+ {
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ if ( pDrView )
+ {
+ pDrView->SetAttrToMarked(*pDrawBrush, true/*bReplaceAll*/);
+ }
+
+ if ( !pView->IsPaintBrushLocked() )
+ pView->ResetBrushDocument(); // end paint brush mode if not locked
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+ }
+
+ return bRet;
+}
+
+bool ScGridWindow::DrawMouseMove(const MouseEvent& rMEvt)
+{
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDraw && !mrViewData.IsRefMode())
+ {
+ MapMode aDrawMode = GetDrawMapMode();
+ MapMode aOldMode = GetMapMode();
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aDrawMode );
+
+ pDraw->SetWindow( this );
+ bool bRet = pDraw->MouseMove( rMEvt );
+ if ( bRet )
+ UpdateStatusPosSize();
+
+ if ( comphelper::LibreOfficeKit::isActive() && aOldMode != aDrawMode )
+ SetMapMode( aOldMode );
+
+ return bRet;
+ }
+ else
+ {
+ SetPointer( PointerStyle::Arrow );
+ return false;
+ }
+}
+
+void ScGridWindow::DrawEndAction()
+{
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ if ( pDrView && pDrView->IsAction() )
+ pDrView->BrkAction();
+
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDraw)
+ pDraw->StopDragTimer();
+
+ // ReleaseMouse on call
+}
+
+bool ScGridWindow::DrawCommand(const CommandEvent& rCEvt)
+{
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+ if (pDrView && pDraw && !mrViewData.IsRefMode())
+ {
+ pDraw->SetWindow( this );
+ sal_uInt8 nUsed = pDraw->Command( rCEvt );
+ if( nUsed == SC_CMD_USED )
+ nButtonDown = 0; // MouseButtonUp is swallowed...
+ if( nUsed || pDrView->IsAction() )
+ return true;
+ }
+
+ return false;
+}
+
+bool ScGridWindow::DrawKeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ ScDrawView* pDrView = mrViewData.GetScDrawView();
+ FuPoor* pDraw = mrViewData.GetView()->GetDrawFuncPtr();
+
+
+ if (pDrView && pDrView->KeyInput(rKEvt, pWin))
+ return true;
+
+ if (pDrView && pDraw && !mrViewData.IsRefMode())
+ {
+ pDraw->SetWindow( this );
+ bool bOldMarked = pDrView->AreObjectsMarked();
+ if (pDraw->KeyInput( rKEvt ))
+ {
+ bool bLeaveDraw = false;
+ bool bUsed = true;
+ bool bNewMarked = pDrView->AreObjectsMarked();
+ if ( !mrViewData.GetView()->IsDrawSelMode() )
+ if ( !bNewMarked )
+ {
+ mrViewData.GetViewShell()->SetDrawShell( false );
+ bLeaveDraw = true;
+ if ( !bOldMarked &&
+ rKEvt.GetKeyCode().GetCode() == KEY_DELETE )
+ bUsed = false; // nothing deleted
+ if(bOldMarked)
+ GetFocus();
+ }
+ if (!bLeaveDraw)
+ UpdateStatusPosSize(); // for moving/resizing etc. by keyboard
+ return bUsed;
+ }
+ }
+
+ return false;
+}
+
+void ScGridWindow::DrawRedraw( ScOutputData& rOutputData, SdrLayerID nLayer )
+{
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+
+ // use new flags at SdrPaintView for hiding objects
+ const bool bDrawOle(VOBJ_MODE_SHOW == rOpts.GetObjMode(VOBJ_TYPE_OLE));
+ const bool bDrawChart(VOBJ_MODE_SHOW == rOpts.GetObjMode(VOBJ_TYPE_CHART));
+ const bool bDrawDraw(VOBJ_MODE_SHOW == rOpts.GetObjMode(VOBJ_TYPE_DRAW));
+
+ if(!(bDrawOle || bDrawChart || bDrawDraw))
+ return;
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+
+ if(pDrView)
+ {
+ pDrView->setHideOle(!bDrawOle);
+ pDrView->setHideChart(!bDrawChart);
+ pDrView->setHideDraw(!bDrawDraw);
+ pDrView->setHideFormControl(!bDrawDraw);
+ }
+
+ rOutputData.DrawSelectiveObjects(nLayer);
+}
+
+void ScGridWindow::DrawSdrGrid( const tools::Rectangle& rDrawingRect, OutputDevice* pContentDev )
+{
+ // Draw grid lines
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if ( pDrView && pDrView->IsGridVisible() )
+ {
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ OSL_ENSURE(pPV, "PageView not available");
+ if (pPV)
+ {
+ pContentDev->SetLineColor(COL_GRAY);
+
+ pPV->DrawPageViewGrid( *pContentDev, rDrawingRect );
+ }
+ }
+}
+
+MapMode ScGridWindow::GetDrawMapMode( bool bForce )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ // FIXME this shouldn't be necessary once we change the entire Calc to
+ // work in the logic coordinates (ideally 100ths of mm - so that it is
+ // the same as editeng and drawinglayer), and get rid of all the
+ // SetMapMode's and other unnecessary fun we have with pixels
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ return mrViewData.GetLogicMode();
+ }
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bNegativePage = rDoc.IsNegativePage( nTab );
+
+ MapMode aDrawMode = mrViewData.GetLogicMode();
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if ( pDrView || bForce )
+ {
+ Fraction aScaleX;
+ Fraction aScaleY;
+ if (pDrView)
+ pDrView->GetScale( aScaleX, aScaleY );
+ else
+ {
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ rDoc.GetTableArea( nTab, nEndCol, nEndRow );
+ if (nEndCol<20) nEndCol = 20;
+ if (nEndRow<20) nEndRow = 1000;
+ ScDrawUtil::CalcScale( rDoc, nTab, 0,0, nEndCol,nEndRow, GetOutDev(),
+ mrViewData.GetZoomX(),mrViewData.GetZoomY(),
+ mrViewData.GetPPTX(),mrViewData.GetPPTY(),
+ aScaleX,aScaleY );
+ }
+ aDrawMode.SetScaleX(aScaleX);
+ aDrawMode.SetScaleY(aScaleY);
+ }
+ aDrawMode.SetOrigin(Point());
+ Point aStartPos = mrViewData.GetPixPos(eWhich);
+ if ( bNegativePage )
+ {
+ // RTL uses negative positions for drawing objects
+ aStartPos.setX( -aStartPos.X() + GetOutputSizePixel().Width() - 1 );
+ }
+ aDrawMode.SetOrigin( PixelToLogic( aStartPos, aDrawMode ) );
+
+ return aDrawMode;
+}
+
+void ScGridWindow::DrawAfterScroll()
+{
+ PaintImmediately(); // always, so the behaviour with and without DrawingLayer is the same
+
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (pDrView)
+ {
+ OutlinerView* pOlView = pDrView->GetTextEditOutlinerView();
+ if (pOlView && pOlView->GetWindow() == this)
+ pOlView->ShowCursor(false); // was removed at scrolling
+ }
+}
+
+void ScGridWindow::CreateAnchorHandle(SdrHdlList& rHdl, const ScAddress& rAddress)
+{
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (pDrView)
+ {
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ if(rOpts.GetOption( VOPT_ANCHOR ))
+ {
+ bool bNegativePage = mrViewData.GetDocument().IsNegativePage( mrViewData.GetTabNo() );
+ Point aPos = mrViewData.GetScrPos( rAddress.Col(), rAddress.Row(), eWhich, true );
+ aPos = PixelToLogic(aPos);
+ rHdl.AddHdl(std::make_unique<SdrHdl>(aPos, bNegativePage ? SdrHdlKind::Anchor_TR : SdrHdlKind::Anchor));
+ }
+ }
+}
+
+void ScGridWindow::UpdateStatusPosSize()
+{
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (!pDrView)
+ return; // shouldn't be called in that case
+
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ if (!pPV)
+ return; // shouldn't be called in that case either
+
+ SfxItemSetFixed<SID_ATTR_POSITION, SID_ATTR_SIZE> aSet(mrViewData.GetViewShell()->GetPool());
+
+ // Fill items for position and size:
+ // show action rectangle during action,
+ // position and size of selected object(s) if something is selected,
+ // mouse position otherwise
+
+ bool bActionItem = false;
+ if ( pDrView->IsAction() ) // action rectangle
+ {
+ tools::Rectangle aRect;
+ pDrView->TakeActionRect( aRect );
+ if ( !aRect.IsEmpty() )
+ {
+ pPV->LogicToPagePos(aRect);
+ aSet.Put( SfxPointItem( SID_ATTR_POSITION, aRect.TopLeft() ) );
+ aSet.Put( SvxSizeItem( SID_ATTR_SIZE,
+ Size( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ) ) );
+ bActionItem = true;
+ }
+ }
+ if ( !bActionItem )
+ {
+ if ( pDrView->AreObjectsMarked() ) // selected objects
+ {
+ tools::Rectangle aRect = pDrView->GetAllMarkedRect();
+ pPV->LogicToPagePos(aRect);
+ aSet.Put( SfxPointItem( SID_ATTR_POSITION, aRect.TopLeft() ) );
+ aSet.Put( SvxSizeItem( SID_ATTR_SIZE,
+ Size( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ) ) );
+ }
+ else // mouse position
+ {
+ Point aPos = PixelToLogic(aCurMousePos);
+ pPV->LogicToPagePos(aPos);
+ aSet.Put( SfxPointItem( SID_ATTR_POSITION, aPos ) );
+ aSet.Put( SvxSizeItem( SID_ATTR_SIZE, Size( 0, 0 ) ) );
+ }
+ }
+
+ mrViewData.GetBindings().SetState(aSet);
+}
+
+bool ScGridWindow::DrawHasMarkedObj()
+{
+ ScDrawView* p = mrViewData.GetScDrawView();
+ return p && p->AreObjectsMarked();
+}
+
+void ScGridWindow::DrawMarkDropObj( SdrObject* pObj )
+{
+ ScDrawView* pDrView = mrViewData.GetView()->GetScDrawView();
+ if (pDrView)
+ pDrView->MarkDropObj(pObj);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
new file mode 100644
index 0000000000..3639e82876
--- /dev/null
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -0,0 +1,2695 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/printer.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <tabvwsh.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <gridwin.hxx>
+#include <viewdata.hxx>
+#include <output.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <dbdata.hxx>
+#include <docoptio.hxx>
+#include <notemark.hxx>
+#include <dbfunc.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <rfindlst.hxx>
+#include <hiranges.hxx>
+#include <pagedata.hxx>
+#include <docpool.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <cbutton.hxx>
+#include <invmerge.hxx>
+#include <editutil.hxx>
+#include <inputopt.hxx>
+#include <fillinfo.hxx>
+#include <dpcontrol.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <sc.hrc>
+#include <vcl/virdev.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <drwlayer.hxx>
+
+static void lcl_LimitRect( tools::Rectangle& rRect, const tools::Rectangle& rVisible )
+{
+ if ( rRect.Top() < rVisible.Top()-1 ) rRect.SetTop( rVisible.Top()-1 );
+ if ( rRect.Bottom() > rVisible.Bottom()+1 ) rRect.SetBottom( rVisible.Bottom()+1 );
+
+ // The header row must be drawn also when the inner rectangle is not visible,
+ // that is why there is no return value anymore.
+ // When it is far away, then lcl_DrawOneFrame is not even called.
+}
+
+static void lcl_DrawOneFrame( vcl::RenderContext* pDev, const tools::Rectangle& rInnerPixel,
+ const OUString& rTitle, const Color& rColor, bool bTextBelow,
+ double nPPTX, double nPPTY, const Fraction& rZoomY,
+ ScDocument& rDoc, ScViewData& rButtonViewData, bool bLayoutRTL )
+{
+ // rButtonViewData is only used to set the button size,
+
+ tools::Rectangle aInner = rInnerPixel;
+ if ( bLayoutRTL )
+ {
+ aInner.SetLeft( rInnerPixel.Right() );
+ aInner.SetRight( rInnerPixel.Left() );
+ }
+
+ tools::Rectangle aVisible( Point(0,0), pDev->GetOutputSizePixel() );
+ lcl_LimitRect( aInner, aVisible );
+
+ tools::Rectangle aOuter = aInner;
+ tools::Long nHor = static_cast<tools::Long>( SC_SCENARIO_HSPACE * nPPTX );
+ tools::Long nVer = static_cast<tools::Long>( SC_SCENARIO_VSPACE * nPPTY );
+ aOuter.AdjustLeft( -nHor );
+ aOuter.AdjustRight(nHor );
+ aOuter.AdjustTop( -nVer );
+ aOuter.AdjustBottom(nVer );
+
+ // use ScPatternAttr::GetFont only for font size
+ vcl::Font aAttrFont;
+ rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN).
+ fillFontOnly(aAttrFont, pDev, &rZoomY);
+
+ // everything else from application font
+ vcl::Font aAppFont = pDev->GetSettings().GetStyleSettings().GetAppFont();
+ aAppFont.SetFontSize( aAttrFont.GetFontSize() );
+
+ aAppFont.SetAlignment( ALIGN_TOP );
+ pDev->SetFont( aAppFont );
+
+ Size aTextSize( pDev->GetTextWidth( rTitle ), pDev->GetTextHeight() );
+
+ if ( bTextBelow )
+ aOuter.AdjustBottom(aTextSize.Height() );
+ else
+ aOuter.AdjustTop( -(aTextSize.Height()) );
+
+ pDev->SetLineColor();
+ pDev->SetFillColor( rColor );
+ // left, top, right, bottom
+ pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aInner.Left(), aOuter.Bottom() ) );
+ pDev->DrawRect( tools::Rectangle( aOuter.Left(), aOuter.Top(), aOuter.Right(), aInner.Top() ) );
+ pDev->DrawRect( tools::Rectangle( aInner.Right(), aOuter.Top(), aOuter.Right(), aOuter.Bottom() ) );
+ pDev->DrawRect( tools::Rectangle( aOuter.Left(), aInner.Bottom(), aOuter.Right(), aOuter.Bottom() ) );
+
+ tools::Long nButtonY = bTextBelow ? aInner.Bottom() : aOuter.Top();
+
+ ScDDComboBoxButton aComboButton(pDev);
+ aComboButton.SetOptSizePixel();
+ tools::Long nBWidth = tools::Long(aComboButton.GetSizePixel().Width() * rZoomY);
+ tools::Long nBHeight = nVer + aTextSize.Height() + 1;
+ Size aButSize( nBWidth, nBHeight );
+ tools::Long nButtonPos = bLayoutRTL ? aOuter.Left() : aOuter.Right()-nBWidth+1;
+ aComboButton.Draw( Point(nButtonPos, nButtonY), aButSize );
+ rButtonViewData.SetScenButSize( aButSize );
+
+ tools::Long nTextStart = bLayoutRTL ? aInner.Right() - aTextSize.Width() + 1 : aInner.Left();
+
+ bool bWasClip = false;
+ vcl::Region aOldClip;
+ bool bClip = ( aTextSize.Width() > aOuter.Right() - nBWidth - aInner.Left() );
+ if ( bClip )
+ {
+ if (pDev->IsClipRegion())
+ {
+ bWasClip = true;
+ aOldClip = pDev->GetActiveClipRegion();
+ }
+ tools::Long nClipStartX = bLayoutRTL ? aOuter.Left() + nBWidth : aInner.Left();
+ tools::Long nClipEndX = bLayoutRTL ? aInner.Right() : aOuter.Right() - nBWidth;
+ pDev->SetClipRegion( vcl::Region(tools::Rectangle( nClipStartX, nButtonY + nVer/2,
+ nClipEndX, nButtonY + nVer/2 + aTextSize.Height())) );
+ }
+
+ pDev->DrawText( Point( nTextStart, nButtonY + nVer/2 ), rTitle );
+
+ if ( bClip )
+ {
+ if ( bWasClip )
+ pDev->SetClipRegion(aOldClip);
+ else
+ pDev->SetClipRegion();
+ }
+
+ pDev->SetFillColor();
+ pDev->SetLineColor( COL_BLACK );
+ pDev->DrawRect( aInner );
+ pDev->DrawRect( aOuter );
+}
+
+static void lcl_DrawScenarioFrames( OutputDevice* pDev, ScViewData& rViewData, ScSplitPos eWhich,
+ SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
+{
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( nTab+1 >= nTabCount || !rDoc.IsScenario(nTab+1) || rDoc.IsScenario(nTab) )
+ return;
+
+ if ( nX1 > 0 ) --nX1;
+ if ( nY1>=2 ) nY1 -= 2; // Hack: Header row affects two cells
+ else if ( nY1 > 0 ) --nY1;
+ if ( nX2 < rDoc.MaxCol() ) ++nX2;
+ if ( nY2 < rDoc.MaxRow()-1 ) nY2 += 2; // Hack: Header row affects two cells
+ else if ( nY2 < rDoc.MaxRow() ) ++nY2;
+ ScRange aViewRange( nX1,nY1,nTab, nX2,nY2,nTab );
+
+ //! cache the ranges in table!!!!
+
+ ScMarkData aMarks(rDoc.GetSheetLimits());
+ for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
+ rDoc.MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
+ ScRangeListRef xRanges = new ScRangeList;
+ aMarks.FillRangeListWithMarks( xRanges.get(), false );
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ for (size_t j = 0, n = xRanges->size(); j < n; ++j)
+ {
+ ScRange aRange = (*xRanges)[j];
+ // Always extend scenario frame to merged cells where no new non-covered cells
+ // are framed
+ rDoc.ExtendTotalMerge( aRange );
+
+ //! -> Extend repaint when merging !!!
+
+ if ( aRange.Intersects( aViewRange ) ) //! Space for Text/Button?
+ {
+ Point aStartPos = rViewData.GetScrPos(
+ aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
+ Point aEndPos = rViewData.GetScrPos(
+ aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich, true );
+ // on the grid:
+ aStartPos.AdjustX( -nLayoutSign );
+ aStartPos.AdjustY( -1 );
+ aEndPos.AdjustX( -nLayoutSign );
+ aEndPos.AdjustY( -1 );
+
+ bool bTextBelow = ( aRange.aStart.Row() == 0 );
+
+ OUString aCurrent;
+ Color aColor( COL_LIGHTGRAY );
+ for (SCTAB nAct=nTab+1; nAct<nTabCount && rDoc.IsScenario(nAct); nAct++)
+ if ( rDoc.IsActiveScenario(nAct) && rDoc.HasScenarioRange(nAct,aRange) )
+ {
+ OUString aDummyComment;
+ ScScenarioFlags nDummyFlags;
+ rDoc.GetName( nAct, aCurrent );
+ rDoc.GetScenarioData( nAct, aDummyComment, aColor, nDummyFlags );
+ }
+
+ if (aCurrent.isEmpty())
+ aCurrent = ScResId( STR_EMPTYDATA );
+
+ //! Own text "(None)" instead of "(Empty)" ???
+
+ lcl_DrawOneFrame( pDev, tools::Rectangle( aStartPos, aEndPos ),
+ aCurrent, aColor, bTextBelow,
+ rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomY(),
+ rDoc, rViewData, bLayoutRTL );
+ }
+ }
+}
+
+static void lcl_DrawHighlight( ScOutputData& rOutputData, const ScViewData& rViewData,
+ const std::vector<ScHighlightEntry>& rHighlightRanges )
+{
+ SCTAB nTab = rViewData.GetTabNo();
+ for ( const auto& rHighlightRange : rHighlightRanges)
+ {
+ ScRange aRange = rHighlightRange.aRef;
+ if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
+ {
+ rOutputData.DrawRefMark(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ rHighlightRange.aColor, false );
+ }
+ }
+}
+
+// Calculates top-left offset to be applied based on margins and indent.
+static void lcl_GetEditAreaTLOffset(tools::Long& nOffsetX, tools::Long& nOffsetY, const ScAddress& rAddr,
+ const ScViewData& rViewData, ScDocument& rDoc)
+{
+ tools::Long nLeftMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nIndent = 0;
+ tools::Long nDummy = 0;
+ ScEditUtil aEUtil(&rDoc, rAddr.Col(), rAddr.Row(), rAddr.Tab(),
+ Point(0, 0), nullptr, rViewData.GetPPTX(),
+ rViewData.GetPPTY(), Fraction(1.0), Fraction(1.0),
+ false /* bPrintTwips */);
+ const ScPatternAttr* pPattern = rDoc.GetPattern(rAddr);
+ if (!rDoc.IsLayoutRTL(rAddr.Tab()))
+ nIndent = aEUtil.GetIndent(pPattern);
+ aEUtil.GetMargins(pPattern, nLeftMargin, nTopMargin, nDummy, nDummy);
+ nOffsetX = nIndent + nLeftMargin;
+ nOffsetY = nTopMargin;
+}
+
+void ScGridWindow::DoInvertRect( const tools::Rectangle& rPixel )
+{
+ if ( rPixel == aInvertRect )
+ aInvertRect = tools::Rectangle(); // Cancel
+ else
+ {
+ OSL_ENSURE( aInvertRect.IsEmpty(), "DoInvertRect no pairs" );
+
+ aInvertRect = rPixel; // Mark new rectangle
+ }
+
+ UpdateHeaderOverlay(); // uses aInvertRect
+}
+
+void ScGridWindow::PrePaint(vcl::RenderContext& /*rRenderContext*/)
+{
+ // forward PrePaint to DrawingLayer
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+
+ if(pTabViewShell)
+ {
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+
+ if (pDrawView)
+ {
+ pDrawView->PrePaint();
+ }
+ }
+}
+
+bool ScGridWindow::NeedLOKCursorInvalidation(const tools::Rectangle& rCursorRect,
+ const Fraction aScaleX, const Fraction aScaleY)
+{
+ // Don't see the need for a map as there will be only a few zoom levels
+ // and as of now X and Y zooms in online are the same.
+ for (auto& rEntry : maLOKLastCursor)
+ {
+ if (aScaleX == rEntry.aScaleX && aScaleY == rEntry.aScaleY)
+ {
+ if (rCursorRect == rEntry.aRect)
+ return false; // No change
+
+ // Update and allow invalidate.
+ rEntry.aRect = rCursorRect;
+ return true;
+ }
+ }
+
+ maLOKLastCursor.push_back(LOKCursorEntry{aScaleX, aScaleY, rCursorRect});
+ return true;
+}
+
+void ScGridWindow::InvalidateLOKViewCursor(const tools::Rectangle& rCursorRect,
+ const Fraction aScaleX, const Fraction aScaleY)
+{
+ if (!NeedLOKCursorInvalidation(rCursorRect, aScaleX, aScaleY))
+ return;
+
+ ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+
+ while (pViewShell)
+ {
+ if (pViewShell != pThisViewShell && pViewShell->GetDocId() == pThisViewShell->GetDocId())
+ {
+ ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ Fraction aZoomX = rOtherViewData.GetZoomX();
+ Fraction aZoomY = rOtherViewData.GetZoomY();
+ if (aZoomX == aScaleX && aZoomY == aScaleY)
+ {
+ SfxLokHelper::notifyOtherView(pThisViewShell, pOtherViewShell,
+ LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", rCursorRect.toString());
+ }
+ }
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void ScGridWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ if ( rDoc.IsInInterpreter() )
+ {
+ // Via Reschedule, interpreted cells do not trigger Invalidate again,
+ // otherwise for instance an error box would never appear (bug 36381).
+ // Later, through bNeedsRepaint everything is painted again.
+ if ( bNeedsRepaint )
+ {
+ //! Merge Rectangle?
+ aRepaintPixel = tools::Rectangle(); // multiple -> paint all
+ }
+ else
+ {
+ bNeedsRepaint = true;
+ aRepaintPixel = LogicToPixel(rRect); // only affected ranges
+ }
+ return;
+ }
+
+ // #i117893# If GetSizePixel needs to call the resize handler, the resulting nested Paint call
+ // (possibly for a larger rectangle) has to be allowed. Call GetSizePixel before setting bIsInPaint.
+ GetSizePixel();
+
+ if (bIsInPaint)
+ return;
+
+ bIsInPaint = true;
+
+ tools::Rectangle aPixRect = LogicToPixel( rRect );
+
+ SCCOL nX1 = mrViewData.GetPosX(eHWhich);
+ SCROW nY1 = mrViewData.GetPosY(eVWhich);
+
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+
+ tools::Rectangle aMirroredPixel = aPixRect;
+ if ( rDoc.IsLayoutRTL( nTab ) )
+ {
+ // mirror and swap
+ tools::Long nWidth = GetSizePixel().Width();
+ aMirroredPixel.SetLeft( nWidth - 1 - aPixRect.Right() );
+ aMirroredPixel.SetRight( nWidth - 1 - aPixRect.Left() );
+ }
+
+ tools::Long nScrX = ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
+ while ( nScrX <= aMirroredPixel.Left() && nX1 < rDoc.MaxCol() )
+ {
+ ++nX1;
+ nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX1, nTab ), nPPTX );
+ }
+ SCCOL nX2 = nX1;
+ while ( nScrX <= aMirroredPixel.Right() && nX2 < rDoc.MaxCol() )
+ {
+ ++nX2;
+ nScrX += ScViewData::ToPixel( rDoc.GetColWidth( nX2, nTab ), nPPTX );
+ }
+
+ tools::Long nScrY = 0;
+ ScViewData::AddPixelsWhile( nScrY, aPixRect.Top(), nY1, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
+ SCROW nY2 = nY1;
+ if (nScrY <= aPixRect.Bottom() && nY2 < rDoc.MaxRow())
+ {
+ ++nY2;
+ ScViewData::AddPixelsWhile( nScrY, aPixRect.Bottom(), nY2, rDoc.MaxRow(), nPPTY, &rDoc, nTab);
+ }
+
+ Draw( nX1,nY1,nX2,nY2, ScUpdateMode::Marks ); // don't continue with painting
+
+ bIsInPaint = false;
+}
+
+void ScGridWindow::Draw( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, ScUpdateMode eMode )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ // let's ignore the normal Draw() attempts when doing the tiled rendering,
+ // all the rendering should go through PaintTile() in that case.
+ // TODO revisit if we can actually turn this into an assert(), and clean
+ // up the callers
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScModule* pScMod = SC_MOD();
+ bool bTextWysiwyg = pScMod->GetInputOptions().GetTextWysiwyg();
+
+ if (mrViewData.IsMinimized())
+ return;
+
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ OSL_ENSURE( rDoc.ValidCol(nX2) && rDoc.ValidRow(nY2), "GridWin Draw area too big" );
+
+ UpdateVisibleRange();
+
+ if (nX2 < maVisibleRange.mnCol1 || nY2 < maVisibleRange.mnRow1)
+ return;
+ // invisible
+ if (nX1 < maVisibleRange.mnCol1)
+ nX1 = maVisibleRange.mnCol1;
+ if (nY1 < maVisibleRange.mnRow1)
+ nY1 = maVisibleRange.mnRow1;
+
+ if (nX1 > maVisibleRange.mnCol2 || nY1 > maVisibleRange.mnRow2)
+ return;
+
+ if (nX2 > maVisibleRange.mnCol2)
+ nX2 = maVisibleRange.mnCol2;
+ if (nY2 > maVisibleRange.mnRow2)
+ nY2 = maVisibleRange.mnRow2;
+
+ if ( eMode != ScUpdateMode::Marks && nX2 < maVisibleRange.mnCol2)
+ nX2 = maVisibleRange.mnCol2; // to continue painting
+
+ // point of no return
+
+ ++nPaintCount; // mark that painting is in progress
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ rDoc.ExtendHidden( nX1, nY1, nX2, nY2, nTab );
+
+ Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
+ tools::Long nMirrorWidth = GetSizePixel().Width();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ if ( bLayoutRTL )
+ {
+ tools::Long nEndPixel = mrViewData.GetScrPos( nX2+1, maVisibleRange.mnRow1, eWhich ).X();
+ nMirrorWidth = aScrPos.X() - nEndPixel;
+ aScrPos.setX( nEndPixel + 1 );
+ }
+
+ tools::Long nScrX = aScrPos.X();
+ tools::Long nScrY = aScrPos.Y();
+
+ SCCOL nCurX = mrViewData.GetCurX();
+ SCROW nCurY = mrViewData.GetCurY();
+ SCCOL nCurEndX = nCurX;
+ SCROW nCurEndY = nCurY;
+ rDoc.ExtendMerge( nCurX, nCurY, nCurEndX, nCurEndY, nTab );
+ bool bCurVis = nCursorHideCount==0 &&
+ ( nCurEndX+1 >= nX1 && nCurX <= nX2+1 && nCurEndY+1 >= nY1 && nCurY <= nY2+1 );
+
+ // AutoFill Handles
+ if ( !bCurVis && nCursorHideCount==0 && bAutoMarkVisible && aAutoMarkPos.Tab() == nTab &&
+ ( aAutoMarkPos.Col() != nCurX || aAutoMarkPos.Row() != nCurY ) )
+ {
+ SCCOL nHdlX = aAutoMarkPos.Col();
+ SCROW nHdlY = aAutoMarkPos.Row();
+ rDoc.ExtendMerge( nHdlX, nHdlY, nHdlX, nHdlY, nTab );
+ // left and top is unaffected
+
+ //! Paint AutoFill handles alone (without Cursor) ???
+ }
+
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+
+ // data block
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
+ nPPTX, nPPTY, false, rOpts.GetOption(VOPT_FORMULAS),
+ &mrViewData.GetMarkData() );
+
+ Fraction aZoomX = mrViewData.GetZoomX();
+ Fraction aZoomY = mrViewData.GetZoomY();
+ ScOutputData aOutputData( GetOutDev(), OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nPPTX, nPPTY,
+ &aZoomX, &aZoomY );
+
+ aOutputData.SetMirrorWidth( nMirrorWidth ); // needed for RTL
+ aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
+
+ ScopedVclPtr< VirtualDevice > xFmtVirtDev;
+ bool bLogicText = bTextWysiwyg; // call DrawStrings in logic MapMode?
+
+ if ( bTextWysiwyg )
+ {
+ // use printer for text formatting
+
+ OutputDevice* pFmtDev = rDoc.GetPrinter();
+ pFmtDev->SetMapMode( mrViewData.GetLogicMode(eWhich) );
+ aOutputData.SetFmtDevice( pFmtDev );
+ }
+ else if ( aZoomX != aZoomY && mrViewData.IsOle() )
+ {
+ // #i45033# For OLE inplace editing with different zoom factors,
+ // use a virtual device with 1/100th mm as text formatting reference
+
+ xFmtVirtDev.disposeAndReset( VclPtr<VirtualDevice>::Create() );
+ xFmtVirtDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ aOutputData.SetFmtDevice( xFmtVirtDev.get() );
+
+ bLogicText = true; // use logic MapMode
+ }
+
+ DrawContent(*GetOutDev(), aTabInfo, aOutputData, bLogicText);
+
+ // If something was inverted during the Paint (selection changed from Basic Macro)
+ // then this is now mixed up and has to be repainted
+ OSL_ENSURE(nPaintCount, "Wrong nPaintCount");
+ --nPaintCount;
+ if (!nPaintCount)
+ CheckNeedsRepaint();
+
+ // Flag drawn formula cells "unchanged".
+ rDoc.ResetChanged(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
+ rDoc.PrepareFormulaCalc();
+}
+
+namespace {
+
+class SuppressEditViewMessagesGuard
+{
+public:
+ SuppressEditViewMessagesGuard(EditView& rEditView) :
+ mrEditView(rEditView),
+ mbOrigSuppressFlag(rEditView.IsSuppressLOKMessages())
+ {
+ if (!mbOrigSuppressFlag)
+ mrEditView.SuppressLOKMessages(true);
+ }
+
+ ~SuppressEditViewMessagesGuard()
+ {
+ if (mrEditView.IsSuppressLOKMessages() != mbOrigSuppressFlag)
+ mrEditView.SuppressLOKMessages(mbOrigSuppressFlag);
+ }
+
+private:
+ EditView& mrEditView;
+ const bool mbOrigSuppressFlag;
+};
+
+}
+
+/**
+ * Used to store the necessary information about the (combined-)tile
+ * area relevant to coordinate transformations in RTL mode.
+ */
+class ScLokRTLContext
+{
+public:
+ ScLokRTLContext(const ScOutputData& rOutputData, const tools::Long nTileDeviceOriginPixelX):
+ mrOutputData(rOutputData),
+ mnTileDevOriginX(nTileDeviceOriginPixelX)
+ {}
+
+ /**
+ * Converts from document x pixel position to the
+ * corresponding pixel position w.r.t the tile device origin.
+ */
+ tools::Long docToTilePos(tools::Long nPosX) const
+ {
+ tools::Long nMirrorX = (-2 * mnTileDevOriginX) + mrOutputData.GetScrW();
+ return nMirrorX - 1 - nPosX;
+ }
+
+
+private:
+ const ScOutputData& mrOutputData;
+ const tools::Long mnTileDevOriginX;
+};
+
+namespace
+{
+int lcl_GetMultiLineHeight(EditEngine* pEditEngine)
+{
+ int nHeight = 0;
+ int nParagraphs = pEditEngine->GetParagraphCount();
+ if (nParagraphs > 1 || (nParagraphs > 0 && pEditEngine->GetLineCount(0) > 1))
+ {
+ for (int nPara = 0; nPara < nParagraphs; nPara++)
+ {
+ nHeight += pEditEngine->GetLineCount(nPara) * pEditEngine->GetLineHeight(nPara);
+ }
+ }
+
+ return nHeight;
+}
+}
+
+void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData,
+ bool bLogicText)
+{
+ ScModule* pScMod = SC_MOD();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ bool bNoBackgroundAndGrid = bIsTiledRendering
+ && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scNoGridBackground);
+
+ SCTAB nTab = aOutputData.nTab;
+ SCCOL nX1 = aOutputData.nX1;
+ SCROW nY1 = aOutputData.nY1;
+ SCCOL nX2 = aOutputData.nX2;
+ SCROW nY2 = aOutputData.nY2;
+ tools::Long nScrX = aOutputData.nScrX;
+ tools::Long nScrY = aOutputData.nScrY;
+
+ const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig();
+ Color aGridColor( rColorCfg.GetColorValue( svtools::CALCGRID ).nColor );
+ if ( aGridColor == COL_TRANSPARENT )
+ {
+ // use view options' grid color only if color config has "automatic" color
+ aGridColor = rOpts.GetGridColor();
+ }
+
+ aOutputData.SetSyntaxMode ( mrViewData.IsSyntaxMode() );
+ aOutputData.SetGridColor ( aGridColor );
+ aOutputData.SetShowNullValues ( rOpts.GetOption( VOPT_NULLVALS ) );
+ aOutputData.SetShowFormulas ( rOpts.GetOption( VOPT_FORMULAS ) );
+ aOutputData.SetShowSpellErrors ( rDoc.GetDocOptions().IsAutoSpell() );
+ aOutputData.SetMarkClipped ( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).bIsVisible );
+
+ aOutputData.SetUseStyleColor( true ); // always set in table view
+
+ aOutputData.SetViewShell( mrViewData.GetViewShell() );
+
+ bool bGrid = rOpts.GetOption( VOPT_GRID ) && mrViewData.GetShowGrid();
+ bool bGridFirst = !rOpts.GetOption( VOPT_GRID_ONTOP );
+
+ bool bPage = rOpts.GetOption( VOPT_PAGEBREAKS ) && !bIsTiledRendering;
+
+ bool bPageMode = mrViewData.IsPagebreakMode();
+ if (bPageMode) // after FindChanged
+ {
+ // SetPagebreakMode also initializes bPrinted Flags
+ aOutputData.SetPagebreakMode( mrViewData.GetView()->GetPageBreakData() );
+ }
+
+ EditView* pEditView = nullptr;
+ bool bEditMode = mrViewData.HasEditView(eWhich);
+ if ( bEditMode && mrViewData.GetRefTabNo() == nTab )
+ {
+ SCCOL nEditCol;
+ SCROW nEditRow;
+ mrViewData.GetEditView( eWhich, pEditView, nEditCol, nEditRow );
+ SCCOL nEditEndCol = mrViewData.GetEditEndCol();
+ SCROW nEditEndRow = mrViewData.GetEditEndRow();
+
+ if ( nEditEndCol >= nX1 && nEditCol <= nX2 && nEditEndRow >= nY1 && nEditRow <= nY2 )
+ aOutputData.SetEditCell( nEditCol, nEditRow );
+ else
+ bEditMode = false;
+ }
+
+ const MapMode aOriginalMode = rDevice.GetMapMode();
+
+ // define drawing layer map mode and paint rectangle
+ MapMode aDrawMode = GetDrawMapMode();
+ if (bIsTiledRendering)
+ {
+ // FIXME this shouldn't be necessary once we change the entire Calc to
+ // work in the logic coordinates (ideally 100ths of mm - so that it is
+ // the same as editeng and drawinglayer), and get rid of all the
+ // SetMapMode's and other unnecessary fun we have with pixels
+ // See also ScGridWindow::GetDrawMapMode() for the rest of this hack
+ aDrawMode.SetOrigin(PixelToLogic(Point(nScrX, nScrY), aDrawMode));
+ }
+ tools::Rectangle aDrawingRectLogic;
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ bool bLokRTL = bLayoutRTL && bIsTiledRendering;
+ std::unique_ptr<ScLokRTLContext> pLokRTLCtxt(
+ bLokRTL ?
+ new ScLokRTLContext(aOutputData, o3tl::convert(aOriginalMode.GetOrigin().X(), o3tl::Length::twip, o3tl::Length::px)) :
+ nullptr);
+
+ {
+ // get drawing pixel rect
+ tools::Rectangle aDrawingRectPixel(
+ bLokRTL ? Point(-(nScrX + aOutputData.GetScrW()), nScrY) : Point(nScrX, nScrY),
+ Size(aOutputData.GetScrW(), aOutputData.GetScrH()));
+
+ // correct for border (left/right)
+ if(rDoc.MaxCol() == nX2 && !bLokRTL)
+ {
+ if(bLayoutRTL)
+ {
+ aDrawingRectPixel.SetLeft( 0 );
+ }
+ else
+ {
+ aDrawingRectPixel.SetRight( GetOutputSizePixel().getWidth() );
+ }
+ }
+
+ // correct for border (bottom)
+ if(rDoc.MaxRow() == nY2)
+ {
+ aDrawingRectPixel.SetBottom( GetOutputSizePixel().getHeight() );
+ }
+
+ // get logic positions
+ aDrawingRectLogic = PixelToLogic(aDrawingRectPixel, aDrawMode);
+ }
+
+ bool bInPlaceEditing = bEditMode && (mrViewData.GetRefTabNo() == mrViewData.GetTabNo());
+ vcl::Cursor* pInPlaceCrsr = nullptr;
+ bool bInPlaceVisCursor = false;
+ if (bInPlaceEditing)
+ {
+ // toggle the cursor off if it's on to ensure the cursor invert
+ // background logic remains valid after the background is cleared on
+ // the next cursor flash
+ pInPlaceCrsr = pEditView->GetCursor();
+ bInPlaceVisCursor = pInPlaceCrsr && pInPlaceCrsr->IsVisible();
+ if (bInPlaceVisCursor)
+ pInPlaceCrsr->Hide();
+ }
+
+ OutputDevice* pContentDev = &rDevice; // device for document content, used by overlay manager
+ SdrPaintWindow* pTargetPaintWindow = nullptr; // #i74769# work with SdrPaintWindow directly
+
+ {
+ // init redraw
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+
+ if(pTabViewShell)
+ {
+ MapMode aCurrentMapMode(pContentDev->GetMapMode());
+ pContentDev->SetMapMode(aDrawMode);
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+
+ if(pDrawView)
+ {
+ // #i74769# Use new BeginDrawLayers() interface
+ vcl::Region aDrawingRegion(aDrawingRectLogic);
+ pTargetPaintWindow = pDrawView->BeginDrawLayers(pContentDev, aDrawingRegion);
+ OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");
+
+ if (!bIsTiledRendering)
+ {
+ // #i74769# get target device from SdrPaintWindow, this may be the prerender
+ // device now, too.
+ pContentDev = &(pTargetPaintWindow->GetTargetOutputDevice());
+ aOutputData.SetContentDevice(pContentDev);
+ }
+ }
+
+ pContentDev->SetMapMode(aCurrentMapMode);
+ }
+ }
+
+ // app-background / document edge (area) (Pixel)
+ if ( !bIsTiledRendering && ( nX2 == rDoc.MaxCol() || nY2 == rDoc.MaxRow() ) )
+ {
+ // save MapMode and set to pixel
+ MapMode aCurrentMapMode(pContentDev->GetMapMode());
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ tools::Rectangle aPixRect( Point(), GetOutputSizePixel() );
+ pContentDev->SetFillColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor );
+ pContentDev->SetLineColor();
+ if ( nX2==rDoc.MaxCol() )
+ {
+ tools::Rectangle aDrawRect( aPixRect );
+ if ( bLayoutRTL )
+ aDrawRect.SetRight( nScrX - 1 );
+ else
+ aDrawRect.SetLeft( nScrX + aOutputData.GetScrW() );
+ if (aDrawRect.Right() >= aDrawRect.Left())
+ pContentDev->DrawRect( aDrawRect );
+ }
+ if ( nY2==rDoc.MaxRow() )
+ {
+ tools::Rectangle aDrawRect( aPixRect );
+ aDrawRect.SetTop( nScrY + aOutputData.GetScrH() );
+ if ( nX2==rDoc.MaxCol() )
+ {
+ // no double painting of the corner
+ if ( bLayoutRTL )
+ aDrawRect.SetLeft( nScrX );
+ else
+ aDrawRect.SetRight( nScrX + aOutputData.GetScrW() - 1 );
+ }
+ if (aDrawRect.Bottom() >= aDrawRect.Top())
+ pContentDev->DrawRect( aDrawRect );
+ }
+
+ // restore MapMode
+ pContentDev->SetMapMode(aCurrentMapMode);
+ }
+
+ if ( rDoc.HasBackgroundDraw( nTab, aDrawingRectLogic ) )
+ {
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ aOutputData.DrawClear();
+
+ // drawing background
+
+ pContentDev->SetMapMode(aDrawMode);
+ DrawRedraw( aOutputData, SC_LAYER_BACK );
+ }
+ else
+ aOutputData.SetSolidBackground(!bNoBackgroundAndGrid);
+
+ aOutputData.DrawDocumentBackground();
+
+ if (bGridFirst && (bGrid || bPage))
+ {
+ // Draw lines in background color cover over lok client grid lines in merged cell areas if bNoBackgroundAndGrid is set.
+ if (bNoBackgroundAndGrid)
+ aOutputData.DrawGrid(*pContentDev, false /* bGrid */, false /* bPage */, true /* bMergeCover */);
+ else
+ aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
+ }
+
+ aOutputData.DrawBackground(*pContentDev);
+
+ if (!bGridFirst && (bGrid || bPage) && !bNoBackgroundAndGrid)
+ aOutputData.DrawGrid(*pContentDev, bGrid, bPage);
+
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ //tdf#128258 - draw a dotted line before hidden columns/rows
+ DrawHiddenIndicator(nX1,nY1,nX2,nY2, *pContentDev);
+
+ if ( bPageMode )
+ {
+ // DrawPagePreview draws complete lines/page numbers, must always be clipped
+ if ( aOutputData.SetChangedClip() )
+ {
+ DrawPagePreview(nX1,nY1,nX2,nY2, *pContentDev);
+ pContentDev->SetClipRegion();
+ }
+ }
+
+ aOutputData.DrawShadow();
+ aOutputData.DrawFrame(*pContentDev);
+
+ aOutputData.DrawSparklines(*pContentDev);
+
+ // Show Note Mark
+ if ( rOpts.GetOption( VOPT_NOTES ) )
+ aOutputData.DrawNoteMarks(*pContentDev);
+
+ if ( rOpts.GetOption( VOPT_FORMULAS_MARKS ) )
+ aOutputData.DrawFormulaMarks(*pContentDev);
+
+ if ( !bLogicText )
+ aOutputData.DrawStrings(); // in pixel MapMode
+
+ // edit cells and printer-metrics text must be before the buttons
+ // (DataPilot buttons contain labels in UI font)
+
+ pContentDev->SetMapMode(mrViewData.GetLogicMode(eWhich));
+ if ( bLogicText )
+ aOutputData.DrawStrings(true); // in logic MapMode if bLogicText is set
+ aOutputData.DrawEdit(true);
+
+ // the buttons are painted in absolute coordinates
+ if (bIsTiledRendering)
+ {
+ // Tiled offset nScrX, nScrY
+ MapMode aMap( MapUnit::MapPixel );
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px) + nScrX);
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px) + nScrY);
+ aMap.SetOrigin(aOrigin);
+ pContentDev->SetMapMode(aMap);
+ }
+ else
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // Autofilter- and Pivot-Buttons
+ DrawButtons(nX1, nX2, rTableInfo, pContentDev, pLokRTLCtxt.get()); // Pixel
+
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ aOutputData.DrawClipMarks();
+
+ // In any case, Scenario / ChangeTracking must happen after DrawGrid, also for !bGridFirst
+
+ //! test if ChangeTrack display is active
+ //! Disable scenario frame via view option?
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ const std::vector<ScHighlightEntry> &rHigh = mrViewData.GetView()->GetHighlightRanges();
+ bool bHasScenario = ( nTab+1<nTabCount && rDoc.IsScenario(nTab+1) && !rDoc.IsScenario(nTab) );
+ bool bHasChange = ( rDoc.GetChangeTrack() != nullptr );
+
+ if ( bHasChange || bHasScenario || !rHigh.empty() )
+ {
+ //! Merge SetChangedClip() with DrawMarks() ?? (different MapMode!)
+
+ if ( bHasChange )
+ aOutputData.DrawChangeTrack();
+
+ if ( bHasScenario )
+ lcl_DrawScenarioFrames( pContentDev, mrViewData, eWhich, nX1,nY1,nX2,nY2 );
+
+ lcl_DrawHighlight( aOutputData, mrViewData, rHigh );
+ }
+
+ // Drawing foreground
+
+ pContentDev->SetMapMode(aDrawMode);
+
+ // Bitmaps and buttons are in absolute pixel coordinates.
+ const MapMode aOrig = pContentDev->GetMapMode();
+ if (bIsTiledRendering)
+ {
+ Point aOrigin = aOriginalMode.GetOrigin();
+ tools::Long nXOffset = bLayoutRTL ?
+ (-o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px) + aOutputData.GetScrW()) :
+ o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px);
+ Size aPixelOffset(nXOffset, o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px));
+ pContentDev->SetPixelOffset(aPixelOffset);
+ comphelper::LibreOfficeKit::setLocalRendering();
+ }
+
+ DrawRedraw( aOutputData, SC_LAYER_FRONT );
+ DrawRedraw( aOutputData, SC_LAYER_INTERN );
+ DrawSdrGrid( aDrawingRectLogic, pContentDev );
+
+ if (bIsTiledRendering)
+ {
+ pContentDev->SetPixelOffset(Size());
+ pContentDev->SetMapMode(aOrig);
+ }
+
+ pContentDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ if ( mrViewData.IsRefMode() && nTab >= mrViewData.GetRefStartZ() && nTab <= mrViewData.GetRefEndZ() )
+ {
+ Color aRefColor( rColorCfg.GetColorValue(svtools::CALCREFERENCE).nColor );
+ aOutputData.DrawRefMark( mrViewData.GetRefStartX(), mrViewData.GetRefStartY(),
+ mrViewData.GetRefEndX(), mrViewData.GetRefEndY(),
+ aRefColor, false );
+ }
+
+ // range finder
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl( mrViewData.GetViewShell() );
+ if (pHdl)
+ {
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if ( pRangeFinder && !pRangeFinder->IsHidden() &&
+ pRangeFinder->GetDocName() == pDocSh->GetTitle() )
+ {
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ScRangeFindData& rData = pRangeFinder->GetObject(i);
+
+ ScRange aRef = rData.aRef;
+ aRef.PutInOrder();
+ if ( aRef.aStart.Tab() >= nTab && aRef.aEnd.Tab() <= nTab )
+ aOutputData.DrawRefMark( aRef.aStart.Col(), aRef.aStart.Row(),
+ aRef.aEnd.Col(), aRef.aEnd.Row(),
+ rData.nColor,
+ true );
+ }
+ }
+ }
+
+ {
+ // end redraw
+ ScTabViewShell* pTabViewShell = mrViewData.GetViewShell();
+
+ if(pTabViewShell)
+ {
+ MapMode aCurrentMapMode(pContentDev->GetMapMode());
+ pContentDev->SetMapMode(aDrawMode);
+
+ if (bIsTiledRendering)
+ {
+ Point aOrigin = aOriginalMode.GetOrigin();
+ if (bLayoutRTL)
+ aOrigin.setX(-o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + aOutputData.nScrX + aOutputData.GetScrW());
+ else
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + aOutputData.nScrX);
+
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + aOutputData.nScrY);
+ const double twipFactor = 15 * 1.76388889; // 26.45833335
+ aOrigin = Point(aOrigin.getX() * twipFactor,
+ aOrigin.getY() * twipFactor);
+ MapMode aNew = rDevice.GetMapMode();
+ aNew.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aNew);
+ }
+
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+
+ if(pDrawView)
+ {
+ // #i74769# work with SdrPaintWindow directly
+ pDrawView->EndDrawLayers(*pTargetPaintWindow, true);
+ }
+
+ pContentDev->SetMapMode(aCurrentMapMode);
+ }
+ }
+
+ // paint in-place editing
+ if (bIsTiledRendering)
+ {
+ ScTabViewShell* pThisViewShell = mrViewData.GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+
+ while (pViewShell)
+ {
+ bool bEnterLoop = bIsTiledRendering || pViewShell != pThisViewShell;
+ if (bEnterLoop && pViewShell->GetDocId() == pThisViewShell->GetDocId())
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell)
+ {
+ ScViewData& rOtherViewData = pTabViewShell->GetViewData();
+ ScSplitPos eOtherWhich = rOtherViewData.GetEditActivePart();
+
+ bool bOtherEditMode = rOtherViewData.HasEditView(eOtherWhich);
+ SCCOL nCol1 = rOtherViewData.GetEditStartCol();
+ SCROW nRow1 = rOtherViewData.GetEditStartRow();
+ SCCOL nCol2 = rOtherViewData.GetEditEndCol();
+ SCROW nRow2 = rOtherViewData.GetEditEndRow();
+ bOtherEditMode = bOtherEditMode
+ && ( nCol2 >= nX1 && nCol1 <= nX2 && nRow2 >= nY1 && nRow1 <= nY2 );
+ if (bOtherEditMode && rOtherViewData.GetRefTabNo() == nTab)
+ {
+ EditView* pOtherEditView = rOtherViewData.GetEditView(eOtherWhich);
+ if (pOtherEditView)
+ {
+ tools::Long nScreenX = aOutputData.nScrX;
+ tools::Long nScreenY = aOutputData.nScrY;
+
+ rDevice.SetLineColor();
+ SfxViewShell* pSfxViewShell = SfxViewShell::Current();
+ ScTabViewShell* pCurrentViewShell = dynamic_cast<ScTabViewShell*>(pSfxViewShell);
+ if (pCurrentViewShell)
+ {
+ const ScViewData& pViewData = pCurrentViewShell->GetViewData();
+ const ScViewOptions& aViewOptions = pViewData.GetOptions();
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nCol1, nRow1, nTab );
+ Color aCellColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
+ if (aCellColor.IsTransparent())
+ {
+ aCellColor = aViewOptions.GetDocColor();
+ }
+ rDevice.SetFillColor(aCellColor);
+ pOtherEditView->SetBackgroundColor(aCellColor);
+ }
+ Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eOtherWhich );
+ Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eOtherWhich );
+
+ if (bIsTiledRendering)
+ {
+ EditEngine* pEditEngine = pOtherEditView->GetEditEngine();
+ if (pEditEngine)
+ aEnd.AdjustY(lcl_GetMultiLineHeight(pEditEngine));
+ }
+
+ if (bLokRTL)
+ {
+ // Transform the cell range X coordinates such that the edit cell area is
+ // horizontally mirrored w.r.t the (combined-)tile.
+ aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
+ aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
+ }
+
+ // don't overwrite grid
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ aEnd.AdjustX( -(2 * nLayoutSign) );
+ aEnd.AdjustY( -2 );
+
+ tools::Rectangle aBackground(aStart, aEnd);
+ if (bLokRTL)
+ aBackground.Normalize();
+
+ // Need to draw the background in absolute coords.
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(
+ o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + nScreenX);
+ aOrigin.setY(
+ o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + nScreenY);
+ aBackground += aOrigin;
+ rDevice.SetMapMode(aDrawMode);
+
+ static const double twipFactor = 15 * 1.76388889; // 26.45833335
+ // keep into account the zoom factor
+ aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()),
+ (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY()));
+
+ MapMode aNew = rDevice.GetMapMode();
+ aNew.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aNew);
+
+ // paint the background
+ rDevice.DrawRect(rDevice.PixelToLogic(aBackground));
+ tools::Rectangle aBGAbs(aBackground);
+
+ tools::Rectangle aEditRect(aBackground);
+ tools::Long nOffsetX = 0, nOffsetY = 0;
+ // Get top-left offset because of margin and indent.
+ lcl_GetEditAreaTLOffset(nOffsetX, nOffsetY, ScAddress(nCol1, nRow1, nTab), mrViewData, rDoc);
+ aEditRect.AdjustLeft(nOffsetX + 1);
+ aEditRect.AdjustRight(1);
+ aEditRect.AdjustTop(nOffsetY + 1);
+ aEditRect.AdjustBottom(1);
+
+ // EditView has an 'output area' which is used to clip the 'paint area' we provide below.
+ // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
+ // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
+ // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
+ OutputDevice& rOtherWin = pOtherEditView->GetOutputDevice();
+ const tools::Rectangle aOrigOutputArea(pOtherEditView->GetOutputArea()); // Not in pixels.
+ const MapMode aOrigMapMode = rOtherWin.GetMapMode();
+ rOtherWin.SetMapMode(rDevice.GetMapMode());
+
+ // Avoid sending wrong cursor/selection messages by the 'other' view, as the output-area is going
+ // to be tweaked temporarily to match the current view's zoom.
+ SuppressEditViewMessagesGuard aGuard(*pOtherEditView);
+ comphelper::ScopeGuard aOutputGuard(
+ [pOtherEditView, aOrigOutputArea, bLokRTL] {
+ if (bLokRTL && aOrigOutputArea != pOtherEditView->GetOutputArea())
+ pOtherEditView->SetOutputArea(aOrigOutputArea);
+ });
+
+ aEditRect = rDevice.PixelToLogic(aEditRect);
+ if (bIsTiledRendering)
+ pOtherEditView->SetOutputArea(aEditRect);
+ else
+ aEditRect.Intersection(pOtherEditView->GetOutputArea());
+ pOtherEditView->Paint(aEditRect, &rDevice);
+
+ // EditView will do the cursor notifications correctly if we're in
+ // print-twips messaging mode.
+ if (bIsTiledRendering && !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ // Now we need to get relative cursor position within the editview.
+ // This is for sending the pixel-aligned twips position of the cursor to the specific views with
+ // the same given zoom level.
+ tools::Rectangle aCursorRect = pOtherEditView->GetEditCursor();
+ Point aCursPos = OutputDevice::LogicToLogic(aCursorRect.TopLeft(),
+ MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+
+ const MapMode& rDevMM = rDevice.GetMapMode();
+ MapMode aMM(MapUnit::MapTwip);
+ aMM.SetScaleX(rDevMM.GetScaleX());
+ aMM.SetScaleY(rDevMM.GetScaleY());
+
+ aBGAbs.AdjustLeft(1);
+ aBGAbs.AdjustTop(1);
+ aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
+ aCursorRect.setWidth(0);
+ aCursorRect.Move(aCursPos.getX(), 0);
+ // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
+ InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
+ }
+
+ // Rollback the mapmode and 'output area'.
+ rOtherWin.SetMapMode(aOrigMapMode);
+ if (!bIsTiledRendering)
+ pOtherEditView->SetOutputArea(aOrigOutputArea);
+ rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
+ }
+ }
+ }
+ }
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+
+ }
+
+ // In-place editing - when the user is typing, we need to paint the text
+ // using the editeng.
+ // It's being done after EndDrawLayers() to get it outside the overlay
+ // buffer and on top of everything.
+ if (bInPlaceEditing)
+ {
+ // get the coordinates of the area we need to clear (overpaint by
+ // the background)
+ SCCOL nCol1 = mrViewData.GetEditStartCol();
+ SCROW nRow1 = mrViewData.GetEditStartRow();
+ SCCOL nCol2 = mrViewData.GetEditEndCol();
+ SCROW nRow2 = mrViewData.GetEditEndRow();
+ rDevice.SetLineColor();
+ rDevice.SetFillColor(pEditView->GetBackgroundColor());
+ Point aStart = mrViewData.GetScrPos( nCol1, nRow1, eWhich );
+ Point aEnd = mrViewData.GetScrPos( nCol2+1, nRow2+1, eWhich );
+
+ if (bLokRTL)
+ {
+ // Transform the cell range X coordinates such that the edit cell area is
+ // horizontally mirrored w.r.t the (combined-)tile.
+ aStart.setX(pLokRTLCtxt->docToTilePos(aStart.X()));
+ aEnd.setX(pLokRTLCtxt->docToTilePos(aEnd.X()));
+ }
+
+ // don't overwrite grid
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ aEnd.AdjustX( -(2 * nLayoutSign) );
+ aEnd.AdjustY( -2 );
+
+ // set the correct mapmode
+ tools::Rectangle aBackground(aStart, aEnd);
+ if (bLokRTL)
+ aBackground.Normalize();
+ tools::Rectangle aBGAbs(aBackground);
+
+ if (bIsTiledRendering)
+ {
+ // Need to draw the background in absolute coords.
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrX);
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrY);
+ aBackground += aOrigin;
+ rDevice.SetMapMode(aDrawMode);
+ }
+ else
+ rDevice.SetMapMode(mrViewData.GetLogicMode());
+
+ if (bIsTiledRendering)
+ {
+ Point aOrigin = aOriginalMode.GetOrigin();
+ aOrigin.setX(o3tl::convert(aOrigin.getX(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrX);
+ aOrigin.setY(o3tl::convert(aOrigin.getY(), o3tl::Length::twip, o3tl::Length::px)
+ + nScrY);
+ static const double twipFactor = 15 * 1.76388889; // 26.45833335
+ // keep into account the zoom factor
+ aOrigin = Point((aOrigin.getX() * twipFactor) / static_cast<double>(aDrawMode.GetScaleX()),
+ (aOrigin.getY() * twipFactor) / static_cast<double>(aDrawMode.GetScaleY()));
+ MapMode aNew = rDevice.GetMapMode();
+ aNew.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aNew);
+ }
+
+ // paint the editeng text
+ if (bIsTiledRendering)
+ {
+ // EditView has an 'output area' which is used to clip the paint area we provide below.
+ // So they need to be in the same coordinates/units. This is tied to the mapmode of the gridwin
+ // attached to the EditView, so we have to change its mapmode too (temporarily). We save the
+ // original mapmode and 'output area' and roll them back when we finish painting to rDevice.
+ const MapMode aOrigMapMode = GetMapMode();
+ SetMapMode(rDevice.GetMapMode());
+
+ // Avoid sending wrong cursor/selection messages by the current view, as the output-area is going
+ // to be tweaked temporarily to match other view's zoom. (This does not affect the manual
+ // cursor-messaging done in the non print-twips mode)
+ SuppressEditViewMessagesGuard aGuard(*pEditView);
+
+ // EditView will do the cursor notifications correctly if we're in
+ // print-twips messaging mode.
+ if (!comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ {
+ // Now we need to get relative cursor position within the editview.
+ // This is for sending the pixel-aligned twips position of the cursor to the specific views with
+ // the same given zoom level.
+ tools::Rectangle aCursorRect = pEditView->GetEditCursor();
+ Point aCursPos = o3tl::toTwips(aCursorRect.TopLeft(), o3tl::Length::mm100);
+
+ const MapMode& rDevMM = rDevice.GetMapMode();
+ MapMode aMM(MapUnit::MapTwip);
+ aMM.SetScaleX(rDevMM.GetScaleX());
+ aMM.SetScaleY(rDevMM.GetScaleY());
+
+ aBGAbs.AdjustLeft(1);
+ aBGAbs.AdjustTop(1);
+ aCursorRect = GetOutDev()->PixelToLogic(aBGAbs, aMM);
+ aCursorRect.setWidth(0);
+ aCursorRect.Move(aCursPos.getX(), 0);
+ // Sends view cursor position to views of all matching zooms if needed (avoids duplicates).
+ InvalidateLOKViewCursor(aCursorRect, aMM.GetScaleX(), aMM.GetScaleY());
+ }
+
+ // Rollback the mapmode and 'output area'.
+ SetMapMode(aOrigMapMode);
+ }
+ else
+ {
+ // paint the background
+ tools::Rectangle aLogicRect(rDevice.PixelToLogic(aBackground));
+ //tdf#100925, rhbz#1283420, Draw some text here, to get
+ //X11CairoTextRender::getCairoContext called, so that the forced read
+ //from the underlying X Drawable gets it to sync.
+ rDevice.DrawText(aLogicRect.BottomLeft(), " ");
+ rDevice.DrawRect(aLogicRect);
+
+ tools::Rectangle aEditRect(Point(nScrX, nScrY), Size(aOutputData.GetScrW(), aOutputData.GetScrH()));
+ pEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice);
+ }
+
+ rDevice.SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // restore the cursor it was originally visible
+ if (bInPlaceVisCursor)
+ pInPlaceCrsr->Show();
+ }
+
+ if (mrViewData.HasEditView(eWhich))
+ {
+ // flush OverlayManager before changing the MapMode
+ flushOverlayManager();
+
+ // set MapMode for text edit
+ rDevice.SetMapMode(mrViewData.GetLogicMode());
+ }
+ else
+ rDevice.SetMapMode(aDrawMode);
+
+ if (mpNoteMarker)
+ mpNoteMarker->Draw(); // Above the cursor, in drawing map mode
+
+ if (bPage && bInitialPageBreaks)
+ SetupInitialPageBreaks(rDoc, nTab);
+}
+
+
+void ScGridWindow::SetupInitialPageBreaks(const ScDocument& rDoc, SCTAB nTab)
+{
+ // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page breaks
+ // is enabled, breaks should be visible. If the document is opened the first
+ // time, the breaks are not calculated yet, so for this initialization
+ // a timer will be triggered here.
+ std::set<SCCOL> aColBreaks;
+ std::set<SCROW> aRowBreaks;
+ rDoc.GetAllColBreaks(aColBreaks, nTab, true, false);
+ rDoc.GetAllRowBreaks(aRowBreaks, nTab, true, false);
+ if (aColBreaks.size() == 0 || aRowBreaks.size() == 0)
+ {
+ maShowPageBreaksTimer.SetPriority(TaskPriority::DEFAULT_IDLE);
+ maShowPageBreaksTimer.Start();
+ }
+ bInitialPageBreaks = false;
+}
+
+namespace
+{
+ template<typename IndexType>
+ void lcl_getBoundingRowColumnforTile(ScViewData& rViewData,
+ tools::Long nTileStartPosPx, tools::Long nTileEndPosPx,
+ sal_Int32& nTopLeftTileOffset, sal_Int32& nTopLeftTileOrigin,
+ sal_Int32& nTopLeftTileIndex, sal_Int32& nBottomRightTileIndex)
+ {
+ const bool bColumnHeader = std::is_same<IndexType, SCCOL>::value;
+
+ SCTAB nTab = rViewData.GetTabNo();
+
+ IndexType nStartIndex = -1;
+ IndexType nEndIndex = -1;
+ tools::Long nStartPosPx = 0;
+ tools::Long nEndPosPx = 0;
+
+ ScPositionHelper& rPositionHelper =
+ bColumnHeader ? rViewData.GetLOKWidthHelper() : rViewData.GetLOKHeightHelper();
+ const auto& rStartNearest = rPositionHelper.getNearestByPosition(nTileStartPosPx);
+ const auto& rEndNearest = rPositionHelper.getNearestByPosition(nTileEndPosPx);
+
+ ScBoundsProvider aBoundsProvider(rViewData, nTab, bColumnHeader);
+ aBoundsProvider.Compute(rStartNearest, rEndNearest, nTileStartPosPx, nTileEndPosPx);
+ aBoundsProvider.GetStartIndexAndPosition(nStartIndex, nStartPosPx); ++nStartIndex;
+ aBoundsProvider.GetEndIndexAndPosition(nEndIndex, nEndPosPx);
+
+ nTopLeftTileOffset = nTileStartPosPx - nStartPosPx;
+ nTopLeftTileOrigin = nStartPosPx;
+ nTopLeftTileIndex = nStartIndex;
+ nBottomRightTileIndex = nEndIndex;
+ }
+
+ void lcl_RTLAdjustTileColOffset(ScViewData& rViewData, sal_Int32& nTileColOffset,
+ tools::Long nTileEndPx, sal_Int32 nEndCol, SCTAB nTab,
+ const ScDocument& rDoc, double fPPTX)
+ {
+ auto GetColWidthPx = [&rDoc, nTab, fPPTX](SCCOL nCol) {
+ const sal_uInt16 nSize = rDoc.GetColWidth(nCol, nTab);
+ const tools::Long nSizePx = ScViewData::ToPixel(nSize, fPPTX);
+ return nSizePx;
+ };
+
+ ScPositionHelper rHelper = rViewData.GetLOKWidthHelper();
+ tools::Long nEndColPos = rHelper.computePosition(nEndCol, GetColWidthPx);
+
+ nTileColOffset += (nEndColPos - nTileEndPx - nTileColOffset);
+ }
+
+ class ScLOKProxyObjectContact final : public sdr::contact::ObjectContactOfPageView
+ {
+ private:
+ ScDrawView* mpScDrawView;
+
+ public:
+ explicit ScLOKProxyObjectContact(
+ ScDrawView* pDrawView,
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) :
+ ObjectContactOfPageView(rPageWindow, pDebugName),
+ mpScDrawView(pDrawView)
+ {
+ }
+
+ virtual bool supportsGridOffsets() const override { return true; }
+
+ virtual void calculateGridOffsetForViewObjectContact(
+ basegfx::B2DVector& rTarget,
+ const sdr::contact::ViewObjectContact& rClient) const override
+ {
+ if (!mpScDrawView)
+ return;
+
+ SdrPageView* pPageView(mpScDrawView->GetSdrPageView());
+ if (!pPageView)
+ return;
+
+ SdrPageWindow* pSdrPageWindow = nullptr;
+ if (pPageView->PageWindowCount() > 0)
+ pSdrPageWindow = pPageView->GetPageWindow(0);
+ if (!pSdrPageWindow)
+ return;
+
+ sdr::contact::ObjectContact& rObjContact(pSdrPageWindow->GetObjectContact());
+
+ SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
+ if (pTargetSdrObject)
+ rTarget = pTargetSdrObject->GetViewContact().GetViewObjectContact(rObjContact).getGridOffset();
+ }
+ };
+
+ class ScLOKDrawView : public FmFormView
+ {
+ public:
+ ScLOKDrawView(OutputDevice* pOut, ScViewData& rData) :
+ FmFormView(*rData.GetDocument().GetDrawLayer(), pOut),
+ mpScDrawView(rData.GetScDrawView())
+ {
+ }
+
+ virtual sdr::contact::ObjectContact* createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow, const char* pDebugName) const override
+ {
+ if (!mpScDrawView)
+ return SdrView::createViewSpecificObjectContact(rPageWindow, pDebugName);
+
+ return new ScLOKProxyObjectContact(mpScDrawView, rPageWindow, pDebugName);
+ }
+
+ private:
+ ScDrawView* mpScDrawView;
+ };
+} // anonymous namespace
+
+void ScGridWindow::PaintTile( VirtualDevice& rDevice,
+ int nOutputWidth, int nOutputHeight,
+ int nTilePosX, int nTilePosY,
+ tools::Long nTileWidth, tools::Long nTileHeight,
+ SCCOL nTiledRenderingAreaEndCol, SCROW nTiledRenderingAreaEndRow )
+{
+ Fraction origZoomX = mrViewData.GetZoomX();
+ Fraction origZoomY = mrViewData.GetZoomY();
+
+ // Output size is in pixels while tile position and size are in logical units (twips).
+
+ // Assumption: always paint the whole sheet i.e. "visible" range is always
+ // from (0,0) to last data position.
+
+ // Tile geometry is independent of the zoom level, but the output size is
+ // dependent of the zoom level. Determine the correct zoom level before
+ // we start.
+
+ // FIXME the painting works using a mixture of drawing with coordinates in
+ // pixels and in logic coordinates; it should be cleaned up to use logic
+ // coords only, and avoid all the SetMapMode()'s.
+ // Similarly to Writer, we should set the mapmode once on the rDevice, and
+ // not care about any zoom settings.
+
+ Fraction aFracX(o3tl::convert(nOutputWidth, o3tl::Length::px, o3tl::Length::twip), nTileWidth);
+ Fraction aFracY(o3tl::convert(nOutputHeight, o3tl::Length::px, o3tl::Length::twip), nTileHeight);
+
+ const bool bChangeZoom = (aFracX != origZoomX || aFracY != origZoomY);
+
+ // page break zoom, and aLogicMode in ScViewData
+ // FIXME: there are issues when SetZoom is called conditionally.
+ mrViewData.SetZoom(aFracX, aFracY, true);
+ if (bChangeZoom)
+ {
+ if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
+ pDrawView->resetGridOffsetsForAllSdrPageViews();
+ }
+
+ const double fTilePosXPixel = static_cast<double>(nTilePosX) * nOutputWidth / nTileWidth;
+ const double fTilePosYPixel = static_cast<double>(nTilePosY) * nOutputHeight / nTileHeight;
+ const double fTileBottomPixel = static_cast<double>(nTilePosY + nTileHeight) * nOutputHeight / nTileHeight;
+ const double fTileRightPixel = static_cast<double>(nTilePosX + nTileWidth) * nOutputWidth / nTileWidth;
+
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+
+ const double fPPTX = mrViewData.GetPPTX();
+ const double fPPTY = mrViewData.GetPPTY();
+
+ // find approximate col/row offsets of nearby.
+ sal_Int32 nTopLeftTileRowOffset = 0;
+ sal_Int32 nTopLeftTileColOffset = 0;
+ sal_Int32 nTopLeftTileRowOrigin = 0;
+ sal_Int32 nTopLeftTileColOrigin = 0;
+
+ sal_Int32 nTopLeftTileRow = 0;
+ sal_Int32 nTopLeftTileCol = 0;
+ sal_Int32 nBottomRightTileRow = 0;
+ sal_Int32 nBottomRightTileCol = 0;
+
+ lcl_getBoundingRowColumnforTile<SCROW>(mrViewData,
+ fTilePosYPixel, fTileBottomPixel,
+ nTopLeftTileRowOffset, nTopLeftTileRowOrigin,
+ nTopLeftTileRow, nBottomRightTileRow);
+
+ lcl_getBoundingRowColumnforTile<SCCOL>(mrViewData,
+ fTilePosXPixel, fTileRightPixel,
+ nTopLeftTileColOffset, nTopLeftTileColOrigin,
+ nTopLeftTileCol, nBottomRightTileCol);
+
+ // Enlarge
+ nBottomRightTileCol++;
+ nBottomRightTileRow++;
+
+ if (nBottomRightTileCol > rDoc.MaxCol())
+ nBottomRightTileCol = rDoc.MaxCol();
+
+ if (nBottomRightTileRow > MAXTILEDROW)
+ nBottomRightTileRow = MAXTILEDROW;
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ if (bLayoutRTL)
+ {
+ lcl_RTLAdjustTileColOffset(mrViewData, nTopLeftTileColOffset,
+ fTileRightPixel, nBottomRightTileCol, nTab,
+ rDoc, fPPTX);
+ }
+
+ // size of the document including drawings, charts, etc.
+ SCCOL nEndCol = nTiledRenderingAreaEndCol;
+ SCROW nEndRow = nTiledRenderingAreaEndRow;
+
+ if (nEndCol < nBottomRightTileCol)
+ nEndCol = nBottomRightTileCol;
+
+ if (nEndRow < nBottomRightTileRow)
+ nEndRow = nBottomRightTileRow;
+
+ nTopLeftTileCol = std::max<sal_Int32>(nTopLeftTileCol, 0);
+ nTopLeftTileRow = std::max<sal_Int32>(nTopLeftTileRow, 0);
+ nTopLeftTileColOrigin = o3tl::convert(nTopLeftTileColOrigin, o3tl::Length::px, o3tl::Length::twip);
+ nTopLeftTileRowOrigin = o3tl::convert(nTopLeftTileRowOrigin, o3tl::Length::px, o3tl::Length::twip);
+
+ // Checkout -> 'rDoc.ExtendMerge' ... if we miss merged cells.
+
+ // Origin must be the offset of the first col and row
+ // containing our top-left pixel.
+ const MapMode aOriginalMode = rDevice.GetMapMode();
+ MapMode aAbsMode = aOriginalMode;
+ const Point aOrigin(-nTopLeftTileColOrigin, -nTopLeftTileRowOrigin);
+ aAbsMode.SetOrigin(aOrigin);
+ rDevice.SetMapMode(aAbsMode);
+
+ ScTableInfo aTabInfo(nEndRow + 3);
+ rDoc.FillInfo(aTabInfo, nTopLeftTileCol, nTopLeftTileRow,
+ nBottomRightTileCol, nBottomRightTileRow,
+ nTab, fPPTX, fPPTY, false, false);
+
+// FIXME: is this called some
+// Point aScrPos = mrViewData.GetScrPos( nX1, nY1, eWhich );
+
+ ScOutputData aOutputData(&rDevice, OUTTYPE_WINDOW, aTabInfo, &rDoc, nTab,
+ -nTopLeftTileColOffset,
+ -nTopLeftTileRowOffset,
+ nTopLeftTileCol, nTopLeftTileRow,
+ nBottomRightTileCol, nBottomRightTileRow,
+ fPPTX, fPPTY, nullptr, nullptr);
+
+ // setup the SdrPage so that drawinglayer works correctly
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ if (pModel)
+ {
+ bool bPrintTwipsMsgs = comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+ if (!mpLOKDrawView)
+ {
+ mpLOKDrawView.reset(bPrintTwipsMsgs ?
+ new ScLOKDrawView(
+ &rDevice,
+ mrViewData) :
+ new FmFormView(
+ *pModel,
+ &rDevice));
+ }
+
+ mpLOKDrawView->SetNegativeX(bLayoutRTL);
+ mpLOKDrawView->ShowSdrPage(mpLOKDrawView->GetModel().GetPage(nTab));
+ aOutputData.SetDrawView(mpLOKDrawView.get());
+ aOutputData.SetSpellCheckContext(mpSpellCheckCxt.get());
+ }
+
+ // draw the content
+ DrawContent(rDevice, aTabInfo, aOutputData, true);
+ rDevice.SetMapMode(aOriginalMode);
+
+ // Paint the chart(s) in edit mode.
+ LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight,
+ nTilePosX, nTilePosY, nTileWidth, nTileHeight, bLayoutRTL);
+
+ rDevice.SetMapMode(aOriginalMode);
+
+ // Flag drawn formula cells "unchanged".
+ rDoc.ResetChanged(ScRange(nTopLeftTileCol, nTopLeftTileRow, nTab, nBottomRightTileCol, nBottomRightTileRow, nTab));
+ rDoc.PrepareFormulaCalc();
+
+ mrViewData.SetZoom(origZoomX, origZoomY, true);
+ if (bChangeZoom)
+ {
+ if (ScDrawView* pDrawView = mrViewData.GetScDrawView())
+ pDrawView->resetGridOffsetsForAllSdrPageViews();
+ }
+
+ if (bLayoutRTL)
+ {
+ Bitmap aCellBMP = rDevice.GetBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight));
+ aCellBMP.Mirror(BmpMirrorFlags::Horizontal);
+ rDevice.DrawBitmap(Point(0, 0), Size(nOutputWidth, nOutputHeight), aCellBMP);
+ }
+}
+
+void ScGridWindow::LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart)
+{
+ tools::Rectangle aRectangle;
+ tools::Rectangle* pResultRectangle;
+ if (!pRectangle)
+ pResultRectangle = nullptr;
+ else
+ {
+ aRectangle = *pRectangle;
+ // When dragging shapes the map mode is disabled.
+ if (IsMapModeEnabled())
+ {
+ if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ }
+ else
+ aRectangle = PixelToLogic(aRectangle, MapMode(MapUnit::MapTwip));
+ pResultRectangle = &aRectangle;
+ }
+
+ // Trim invalidation rectangle overlapping negative X region in RTL mode.
+ if (pResultRectangle && pResultRectangle->Left() < 0
+ && mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()))
+ {
+ pResultRectangle->SetLeft(0);
+ if (pResultRectangle->Right() < 0)
+ pResultRectangle->SetRight(0);
+ }
+
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ SfxLokHelper::notifyInvalidation(pViewShell, nPart, pResultRectangle);
+}
+
+void ScGridWindow::LogicInvalidate(const tools::Rectangle* pRectangle)
+{
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ LogicInvalidatePart(pRectangle, pViewShell->getPart());
+}
+
+void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY)
+{
+ ScTabView* pTabView = mrViewData.GetView();
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl(pViewShell);
+
+ if (pInputHandler && pInputHandler->IsInputMode())
+ {
+ // we need to switch off the editeng
+ ScTabView::UpdateInputLine();
+ pViewShell->UpdateInputHandler();
+ }
+
+ if (nType == LOK_SETTEXTSELECTION_RESET)
+ {
+ pTabView->DoneBlockMode();
+ return;
+ }
+
+ // obtain the current selection
+ ScRangeList aRangeList = mrViewData.GetMarkData().GetMarkedRanges();
+
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+
+ bool bWasEmpty = false;
+ if (aRangeList.empty())
+ {
+ nCol1 = nCol2 = mrViewData.GetCurX();
+ nRow1 = nRow2 = mrViewData.GetCurY();
+ bWasEmpty = true;
+ }
+ else
+ aRangeList.Combine().GetVars(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+
+ // convert the coordinates to column/row
+ SCCOL nNewPosX;
+ SCROW nNewPosY;
+ SCTAB nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel(nPixelX, nPixelY, eWhich, nNewPosX, nNewPosY);
+
+ // change the selection
+ switch (nType)
+ {
+ case LOK_SETTEXTSELECTION_START:
+ if (nNewPosX != nCol1 || nNewPosY != nRow1 || bWasEmpty)
+ {
+ pTabView->SetCursor(nNewPosX, nNewPosY);
+ pTabView->DoneBlockMode();
+ pTabView->InitBlockMode(nNewPosX, nNewPosY, nTab, true);
+ pTabView->MarkCursor(nCol2, nRow2, nTab);
+ }
+ break;
+ case LOK_SETTEXTSELECTION_END:
+ if (nNewPosX != nCol2 || nNewPosY != nRow2 || bWasEmpty)
+ {
+ pTabView->SetCursor(nCol1, nRow1);
+ pTabView->DoneBlockMode();
+ pTabView->InitBlockMode(nCol1, nRow1, nTab, true);
+ pTabView->MarkCursor(nNewPosX, nNewPosY, nTab);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void ScGridWindow::CheckNeedsRepaint()
+{
+ // called at the end of painting, and from timer after background text width calculation
+
+ if (!bNeedsRepaint)
+ return;
+
+ bNeedsRepaint = false;
+ if (aRepaintPixel.IsEmpty())
+ Invalidate();
+ else
+ Invalidate(PixelToLogic(aRepaintPixel));
+ aRepaintPixel = tools::Rectangle();
+
+ // selection function in status bar might also be invalid
+ SfxBindings& rBindings = mrViewData.GetBindings();
+ rBindings.Invalidate( SID_STATUS_SUM );
+ rBindings.Invalidate( SID_ATTR_SIZE );
+ rBindings.Invalidate( SID_TABLE_CELL );
+}
+
+void ScGridWindow::DrawHiddenIndicator( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext)
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ const svtools::ColorConfigValue aColorValue = rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL);
+ if (aColorValue.bIsVisible) {
+ rRenderContext.SetLineColor(aColorValue.nColor);
+ LineInfo aLineInfo(LineStyle::Dash, 2);
+ aLineInfo.SetDashCount(0);
+ aLineInfo.SetDotCount(1);
+ aLineInfo.SetDistance(15);
+ // round caps except when running VCL_PLUGIN=gen due to a performance issue
+ // https://bugs.documentfoundation.org/show_bug.cgi?id=128258#c14
+ if (mrViewData.GetActiveWin()->GetSystemData()->toolkit != SystemEnvData::Toolkit::Gen)
+ aLineInfo.SetLineCap(css::drawing::LineCap_ROUND);
+ aLineInfo.SetDotLen(1);
+ for (int i=nX1; i<nX2; i++) {
+ if (rDoc.ColHidden(i,nTab) && (i<rDoc.MaxCol() ? !rDoc.ColHidden(i+1,nTab) : true)) {
+ Point aStart = mrViewData.GetScrPos(i, nY1, eWhich, true );
+ Point aEnd = mrViewData.GetScrPos(i, nY2, eWhich, true );
+ rRenderContext.DrawLine(aStart,aEnd,aLineInfo);
+ }
+ }
+ for (int i=nY1; i<nY2; i++) {
+ if (rDoc.RowHidden(i,nTab) && (i<rDoc.MaxRow() ? !rDoc.RowHidden(i+1,nTab) : true)) {
+ Point aStart = mrViewData.GetScrPos(nX1, i, eWhich, true );
+ Point aEnd = mrViewData.GetScrPos(nX2, i, eWhich, true );
+ rRenderContext.DrawLine(aStart,aEnd,aLineInfo);
+ }
+ }
+ } //visible
+}
+
+void ScGridWindow::DrawPagePreview( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, vcl::RenderContext& rRenderContext)
+{
+ ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData();
+ if (!pPageData)
+ return;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ Size aWinSize = GetOutputSizePixel();
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aManual( rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor );
+ Color aAutomatic( rColorCfg.GetColorValue(svtools::CALCPAGEBREAK).nColor );
+
+ OUString aPageStr = ScResId( STR_PGNUM );
+ if ( nPageScript == SvtScriptType::NONE )
+ {
+ // get script type of translated "Page" string only once
+ nPageScript = rDoc.GetStringScriptType( aPageStr );
+ if (nPageScript == SvtScriptType::NONE)
+ nPageScript = ScGlobal::GetDefaultScriptType();
+ }
+
+ vcl::Font aFont;
+ std::unique_ptr<ScEditEngineDefaulter> pEditEng;
+ const ScPatternAttr& rDefPattern = rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
+ if ( nPageScript == SvtScriptType::LATIN )
+ {
+ // use single font and call DrawText directly
+ rDefPattern.fillFontOnly(aFont);
+ aFont.SetColor(COL_LIGHTGRAY);
+ // font size is set as needed
+ }
+ else
+ {
+ // use EditEngine to draw mixed-script string
+ pEditEng.reset(new ScEditEngineDefaulter( EditEngine::CreatePool().get(), true ));
+ pEditEng->SetRefMapMode(rRenderContext.GetMapMode());
+ auto pEditDefaults = std::make_unique<SfxItemSet>( pEditEng->GetEmptyItemSet() );
+ rDefPattern.FillEditItemSet( pEditDefaults.get() );
+ pEditDefaults->Put( SvxColorItem( COL_LIGHTGRAY, EE_CHAR_COLOR ) );
+ pEditEng->SetDefaults( std::move(pEditDefaults) );
+ }
+
+ sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
+ for (sal_uInt16 nPos=0; nPos<nCount; nPos++)
+ {
+ ScPrintRangeData& rData = pPageData->GetData(nPos);
+ ScRange aRange = rData.GetPrintRange();
+ if ( aRange.aStart.Col() <= nX2+1 && aRange.aEnd.Col()+1 >= nX1 &&
+ aRange.aStart.Row() <= nY2+1 && aRange.aEnd.Row()+1 >= nY1 )
+ {
+ // 3 pixel frame around the print area
+ // (middle pixel on the grid lines)
+
+ rRenderContext.SetLineColor();
+ if (rData.IsAutomatic())
+ rRenderContext.SetFillColor( aAutomatic );
+ else
+ rRenderContext.SetFillColor( aManual );
+
+ Point aStart = mrViewData.GetScrPos(
+ aRange.aStart.Col(), aRange.aStart.Row(), eWhich, true );
+ Point aEnd = mrViewData.GetScrPos(
+ aRange.aEnd.Col() + 1, aRange.aEnd.Row() + 1, eWhich, true );
+ aStart.AdjustX( -2 );
+ aStart.AdjustY( -2 );
+
+ // Prevent overflows:
+ if ( aStart.X() < -10 ) aStart.setX( -10 );
+ if ( aStart.Y() < -10 ) aStart.setY( -10 );
+ if ( aEnd.X() > aWinSize.Width() + 10 )
+ aEnd.setX( aWinSize.Width() + 10 );
+ if ( aEnd.Y() > aWinSize.Height() + 10 )
+ aEnd.setY( aWinSize.Height() + 10 );
+
+ rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aEnd.X(),aStart.Y()+2) ) );
+ rRenderContext.DrawRect( tools::Rectangle( aStart, Point(aStart.X()+2,aEnd.Y()) ) );
+ rRenderContext.DrawRect( tools::Rectangle( Point(aStart.X(),aEnd.Y()-2), aEnd ) );
+ rRenderContext.DrawRect( tools::Rectangle( Point(aEnd.X()-2,aStart.Y()), aEnd ) );
+
+ // Page breaks
+ //! Display differently (dashed ????)
+
+ size_t nColBreaks = rData.GetPagesX();
+ const SCCOL* pColEnd = rData.GetPageEndX();
+ size_t nColPos;
+ for (nColPos=0; nColPos+1<nColBreaks; nColPos++)
+ {
+ SCCOL nBreak = pColEnd[nColPos]+1;
+ if ( nBreak >= nX1 && nBreak <= nX2+1 )
+ {
+ //! Search for hidden
+ if (rDoc.HasColBreak(nBreak, nTab) & ScBreakType::Manual)
+ rRenderContext.SetFillColor( aManual );
+ else
+ rRenderContext.SetFillColor( aAutomatic );
+ Point aBreak = mrViewData.GetScrPos(
+ nBreak, aRange.aStart.Row(), eWhich, true );
+ rRenderContext.DrawRect( tools::Rectangle( aBreak.X()-1, aStart.Y(), aBreak.X(), aEnd.Y() ) );
+ }
+ }
+
+ size_t nRowBreaks = rData.GetPagesY();
+ const SCROW* pRowEnd = rData.GetPageEndY();
+ size_t nRowPos;
+ for (nRowPos=0; nRowPos+1<nRowBreaks; nRowPos++)
+ {
+ SCROW nBreak = pRowEnd[nRowPos]+1;
+ if ( nBreak >= nY1 && nBreak <= nY2+1 )
+ {
+ //! Search for hidden
+ if (rDoc.HasRowBreak(nBreak, nTab) & ScBreakType::Manual)
+ rRenderContext.SetFillColor( aManual );
+ else
+ rRenderContext.SetFillColor( aAutomatic );
+ Point aBreak = mrViewData.GetScrPos(
+ aRange.aStart.Col(), nBreak, eWhich, true );
+ rRenderContext.DrawRect( tools::Rectangle( aStart.X(), aBreak.Y()-1, aEnd.X(), aBreak.Y() ) );
+ }
+ }
+
+ // Page numbers
+
+ SCROW nPrStartY = aRange.aStart.Row();
+ for (nRowPos=0; nRowPos<nRowBreaks; nRowPos++)
+ {
+ SCROW nPrEndY = pRowEnd[nRowPos];
+ if ( nPrEndY >= nY1 && nPrStartY <= nY2 )
+ {
+ SCCOL nPrStartX = aRange.aStart.Col();
+ for (nColPos=0; nColPos<nColBreaks; nColPos++)
+ {
+ SCCOL nPrEndX = pColEnd[nColPos];
+ if ( nPrEndX >= nX1 && nPrStartX <= nX2 )
+ {
+ Point aPageStart = mrViewData.GetScrPos(
+ nPrStartX, nPrStartY, eWhich, true );
+ Point aPageEnd = mrViewData.GetScrPos(
+ nPrEndX+1,nPrEndY+1, eWhich, true );
+
+ tools::Long nPageNo = rData.GetFirstPage();
+ if ( rData.IsTopDown() )
+ nPageNo += static_cast<tools::Long>(nColPos)*nRowBreaks+nRowPos;
+ else
+ nPageNo += static_cast<tools::Long>(nRowPos)*nColBreaks+nColPos;
+
+ OUString aThisPageStr = aPageStr.replaceFirst("%1", OUString::number(nPageNo));
+
+ if ( pEditEng )
+ {
+ // find right font size with EditEngine
+ tools::Long nHeight = 100;
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+ pEditEng->SetTextCurrentDefaults( aThisPageStr );
+ Size aSize100( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() );
+
+ // 40% of width or 60% of height
+ tools::Long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width();
+ tools::Long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height();
+ nHeight = std::min(nSizeX,nSizeY);
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ pEditEng->SetDefaultItem( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ // centered output with EditEngine
+ Size aTextSize( pEditEng->CalcTextWidth(), pEditEng->GetTextHeight() );
+ Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2,
+ (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 );
+ pEditEng->Draw(rRenderContext, aPos);
+ }
+ else
+ {
+ // find right font size for DrawText
+ aFont.SetFontSize( Size( 0,100 ) );
+ rRenderContext.SetFont( aFont );
+
+ Size aSize100(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() );
+ if (aSize100.Width() && aSize100.Height())
+ {
+ // 40% of width or 60% of height
+ tools::Long nSizeX = 40 * ( aPageEnd.X() - aPageStart.X() ) / aSize100.Width();
+ tools::Long nSizeY = 60 * ( aPageEnd.Y() - aPageStart.Y() ) / aSize100.Height();
+ aFont.SetFontSize( Size( 0,std::min(nSizeX,nSizeY) ) );
+ rRenderContext.SetFont( aFont );
+ }
+
+ // centered output with DrawText
+ Size aTextSize(rRenderContext.GetTextWidth( aThisPageStr ), rRenderContext.GetTextHeight() );
+ Point aPos( (aPageStart.X()+aPageEnd.X()-aTextSize.Width())/2,
+ (aPageStart.Y()+aPageEnd.Y()-aTextSize.Height())/2 );
+ rRenderContext.DrawText( aPos, aThisPageStr );
+ }
+ }
+ nPrStartX = nPrEndX + 1;
+ }
+ }
+ nPrStartY = nPrEndY + 1;
+ }
+ }
+ }
+}
+
+void ScGridWindow::DrawButtons(SCCOL nX1, SCCOL nX2, const ScTableInfo& rTabInfo, OutputDevice* pContentDev, const ScLokRTLContext* pLokRTLContext)
+{
+ aComboButton.SetOutputDevice( pContentDev );
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScDPFieldButton aCellBtn(pContentDev, &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc);
+
+ SCCOL nCol;
+ SCROW nRow;
+ SCSIZE nArrY;
+ SCSIZE nQuery;
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScDBData* pDBData = nullptr;
+ std::unique_ptr<ScQueryParam> pQueryParam;
+
+ RowInfo* pRowInfo = rTabInfo.mpRowInfo.get();
+ sal_uInt16 nArrCount = rTabInfo.mnArrCount;
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ Point aOldPos = aComboButton.GetPosPixel(); // store state for MouseDown/Up
+ Size aOldSize = aComboButton.GetSizePixel();
+
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ if ( pRowInfo[nArrY].bAutoFilter && pRowInfo[nArrY].bChanged )
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ nRow = pThisRowInfo->nRowNo;
+
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
+ //if several columns merged on a row, there should be only one auto button at the end of the columns.
+ //if several rows merged on a column, the button may be in the middle, so "!pInfo->bVOverlapped" should not be used
+ if ( pInfo->bAutoFilter && !pInfo->bHOverlapped )
+ {
+ if (!pQueryParam)
+ pQueryParam.reset(new ScQueryParam);
+
+ bool bNewData = true;
+ if (pDBData)
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nAreaTab;
+ pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ if ( nCol >= nStartCol && nCol <= nEndCol &&
+ nRow >= nStartRow && nRow <= nEndRow )
+ bNewData = false;
+ }
+ if (bNewData)
+ {
+ pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
+ if (pDBData)
+ pDBData->GetQueryParam( *pQueryParam );
+ else
+ {
+ // can also be part of DataPilot table
+ }
+ }
+
+ // pQueryParam can only include MAXQUERY entries
+
+ bool bArrowState = false;
+ if (pQueryParam->bInplace)
+ {
+ SCSIZE nCount = pQueryParam->GetEntryCount();
+ for (nQuery = 0; nQuery < nCount; ++nQuery)
+ {
+ // Do no restrict to EQUAL here
+ // (Column head should become blue also when ">1")
+ const ScQueryEntry& rEntry = pQueryParam->GetEntry(nQuery);
+ if (rEntry.bDoQuery && rEntry.nField == nCol)
+ {
+ bArrowState = true;
+ break;
+ }
+ }
+ }
+
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ SCCOL nStartCol= nCol;
+ SCROW nStartRow = nRow;
+ //if address(nCol,nRow) is not the start pos of the merge area, the value of the nSizeX will be incorrect, it will be the length of the cell.
+ //should first get the start pos of the merge area, then get the nSizeX through the start pos.
+ rDoc.ExtendOverlapped(nStartCol, nStartRow,nCol, nRow, nTab);//get nStartCol,nStartRow
+ mrViewData.GetMergeSizePixel( nStartCol, nStartRow, nSizeX, nSizeY );//get nSizeX
+ nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
+ Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ if (pLokRTLContext)
+ aScrPos.setX(pLokRTLContext->docToTilePos(aScrPos.X()));
+
+ aCellBtn.setBoundingBox(aScrPos, Size(nSizeX-1, nSizeY-1), bLayoutRTL);
+ aCellBtn.setPopupLeft(bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL
+ aCellBtn.setDrawBaseButton(false);
+ aCellBtn.setDrawPopupButton(true);
+ aCellBtn.setHasHiddenMember(bArrowState);
+ aCellBtn.draw();
+ }
+ }
+ }
+
+ if ( (pRowInfo[nArrY].bPivotToggle || pRowInfo[nArrY].bPivotButton) && pRowInfo[nArrY].bChanged )
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ nRow = pThisRowInfo->nRowNo;
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nCol);
+ if (pInfo->bHOverlapped || pInfo->bVOverlapped)
+ continue;
+
+ Point aScrPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
+ tools::Long nPosX = aScrPos.X();
+ tools::Long nPosY = aScrPos.Y();
+ // bLayoutRTL is handled in setBoundingBox
+
+ bool bDrawToggle = pInfo->bPivotCollapseButton || pInfo->bPivotExpandButton;
+ if (!bDrawToggle)
+ {
+ OUString aStr = rDoc.GetString(nCol, nRow, nTab);
+ aCellBtn.setText(aStr);
+ }
+
+ sal_uInt16 nIndent = 0;
+ if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT))
+ nIndent = pIndentItem->GetValue();
+ aCellBtn.setBoundingBox(Point(nPosX, nPosY), Size(nSizeX-1, nSizeY-1), bLayoutRTL);
+ aCellBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now
+ aCellBtn.setDrawBaseButton(pInfo->bPivotButton);
+ aCellBtn.setDrawPopupButton(pInfo->bPivotPopupButton);
+ aCellBtn.setDrawPopupButtonMulti(pInfo->bPivotPopupButtonMulti);
+ aCellBtn.setDrawToggleButton(bDrawToggle, pInfo->bPivotCollapseButton, nIndent);
+ aCellBtn.setHasHiddenMember(pInfo->bFilterActive);
+ aCellBtn.draw();
+ }
+ }
+
+ if ( !comphelper::LibreOfficeKit::isActive() && bListValButton && pRowInfo[nArrY].nRowNo == aListValPos.Row() && pRowInfo[nArrY].bChanged )
+ {
+ tools::Rectangle aRect = GetListValButtonRect( aListValPos );
+ aComboButton.SetPosPixel( aRect.TopLeft() );
+ aComboButton.SetSizePixel( aRect.GetSize() );
+ pContentDev->SetClipRegion(vcl::Region(aRect));
+ aComboButton.Draw();
+ pContentDev->SetClipRegion(); // always called from Draw() without clip region
+ aComboButton.SetPosPixel( aOldPos ); // restore old state
+ aComboButton.SetSizePixel( aOldSize ); // for MouseUp/Down (AutoFilter)
+ }
+ }
+
+ pQueryParam.reset();
+ aComboButton.SetOutputDevice( GetOutDev() );
+}
+
+tools::Rectangle ScGridWindow::GetListValButtonRect( const ScAddress& rButtonPos )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ ScDDComboBoxButton aButton( GetOutDev() ); // for optimal size
+ Size aBtnSize = aButton.GetSizePixel();
+
+ SCCOL nCol = rButtonPos.Col();
+ SCROW nRow = rButtonPos.Row();
+
+ tools::Long nCellSizeX; // width of this cell, including merged
+ tools::Long nDummy;
+ mrViewData.GetMergeSizePixel( nCol, nRow, nCellSizeX, nDummy );
+
+ // for height, only the cell's row is used, excluding merged cells
+ tools::Long nCellSizeY = ScViewData::ToPixel( rDoc.GetRowHeight( nRow, nTab ), mrViewData.GetPPTY() );
+ tools::Long nAvailable = nCellSizeX;
+
+ // left edge of next cell if there is a non-hidden next column
+ SCCOL nNextCol = nCol + 1;
+ const ScMergeAttr* pMerge = rDoc.GetAttr( nCol,nRow,nTab, ATTR_MERGE );
+ if ( pMerge->GetColMerge() > 1 )
+ nNextCol = nCol + pMerge->GetColMerge(); // next cell after the merged area
+ while ( nNextCol <= rDoc.MaxCol() && rDoc.ColHidden(nNextCol, nTab) )
+ ++nNextCol;
+ bool bNextCell = ( nNextCol <= rDoc.MaxCol() );
+ if ( bNextCell )
+ nAvailable = ScViewData::ToPixel( rDoc.GetColWidth( nNextCol, nTab ), mrViewData.GetPPTX() );
+
+ if ( nAvailable < aBtnSize.Width() )
+ aBtnSize.setWidth( nAvailable );
+ if ( nCellSizeY < aBtnSize.Height() )
+ aBtnSize.setHeight( nCellSizeY );
+
+ Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich, true );
+ aPos.AdjustX(nCellSizeX * nLayoutSign ); // start of next cell
+ if (!bNextCell)
+ aPos.AdjustX( -(aBtnSize.Width() * nLayoutSign) ); // right edge of cell if next cell not available
+ aPos.AdjustY(nCellSizeY - aBtnSize.Height() );
+ // X remains at the left edge
+
+ if ( bLayoutRTL )
+ aPos.AdjustX( -(aBtnSize.Width()-1) ); // align right edge of button with cell border
+
+ return tools::Rectangle( aPos, aBtnSize );
+}
+
+bool ScGridWindow::IsAutoFilterActive( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScDBData* pDBData = rDoc.GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
+ ScQueryParam aQueryParam;
+
+ if ( pDBData )
+ pDBData->GetQueryParam( aQueryParam );
+ else
+ {
+ OSL_FAIL("Auto filter button without DBData");
+ }
+
+ bool bSimpleQuery = true;
+ bool bColumnFound = false;
+ SCSIZE nQuery;
+
+ if ( !aQueryParam.bInplace )
+ bSimpleQuery = false;
+
+ // aQueryParam can only include MAXQUERY entries
+
+ SCSIZE nCount = aQueryParam.GetEntryCount();
+ for (nQuery = 0; nQuery < nCount && bSimpleQuery; ++nQuery)
+ if ( aQueryParam.GetEntry(nQuery).bDoQuery )
+ {
+ if (aQueryParam.GetEntry(nQuery).nField == nCol)
+ bColumnFound = true;
+
+ if (nQuery > 0)
+ if (aQueryParam.GetEntry(nQuery).eConnect != SC_AND)
+ bSimpleQuery = false;
+ }
+
+ return ( bSimpleQuery && bColumnFound );
+}
+
+void ScGridWindow::GetSelectionRects( ::std::vector< tools::Rectangle >& rPixelRects ) const
+{
+ GetPixelRectsFor( mrViewData.GetMarkData(), rPixelRects );
+}
+
+void ScGridWindow::GetSelectionRectsPrintTwips(::std::vector< tools::Rectangle >& rRects) const
+{
+ GetRectsAnyFor(mrViewData.GetMarkData(), rRects, true);
+}
+
+/// convert rMarkData into pixel rectangles for this view
+void ScGridWindow::GetPixelRectsFor( const ScMarkData &rMarkData,
+ ::std::vector< tools::Rectangle >& rPixelRects ) const
+{
+ GetRectsAnyFor(rMarkData, rPixelRects, false);
+}
+
+void ScGridWindow::GetRectsAnyFor(const ScMarkData &rMarkData,
+ ::std::vector< tools::Rectangle >& rRects,
+ bool bInPrintTwips) const
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ double nPPTX = mrViewData.GetPPTX();
+ double nPPTY = mrViewData.GetPPTY();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ // LOK clients needs exact document coordinates, so don't horizontally mirror them.
+ tools::Long nLayoutSign = (!comphelper::LibreOfficeKit::isActive() && bLayoutRTL) ? -1 : 1;
+
+ ScMarkData aMultiMark( rMarkData );
+ aMultiMark.SetMarking( false );
+
+ if (!aMultiMark.IsMultiMarked())
+ {
+ // simple range case - simplify calculation
+ const ScRange& aSimpleRange = aMultiMark.GetMarkArea();
+
+ aMultiMark.MarkToMulti();
+ if ( !aMultiMark.IsMultiMarked() )
+ return;
+
+ SCCOL nX1 = aSimpleRange.aStart.Col();
+ SCROW nY1 = aSimpleRange.aStart.Row();
+ SCCOL nX2 = aSimpleRange.aEnd.Col();
+ SCROW nY2 = aSimpleRange.aEnd.Row();
+
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ SCCOL nPosX = mrViewData.GetPosX( eHWhich );
+ SCROW nPosY = mrViewData.GetPosY( eVWhich );
+ // is the selection visible at all?
+ if (nX2 < nPosX || nY2 < nPosY)
+ return;
+
+ Point aScrStartPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) :
+ mrViewData.GetScrPos(nX1, nY1, eWhich);
+
+ tools::Long nStartX = aScrStartPos.X();
+ tools::Long nStartY = aScrStartPos.Y();
+
+ Point aScrEndPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX2, nY2) :
+ mrViewData.GetScrPos(nX2, nY2, eWhich);
+
+ tools::Long nWidthTwips = rDoc.GetColWidth(nX2, nTab);
+ const tools::Long nWidth = bInPrintTwips ?
+ nWidthTwips : ScViewData::ToPixel(nWidthTwips, nPPTX);
+ tools::Long nEndX = aScrEndPos.X() + (nWidth - 1) * nLayoutSign;
+
+ sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY2, nTab );
+ const tools::Long nHeight = bInPrintTwips ?
+ nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY);
+ tools::Long nEndY = aScrEndPos.Y() + nHeight - 1;
+
+ ScInvertMerger aInvert( &rRects );
+ aInvert.AddRect( tools::Rectangle( nStartX, nStartY, nEndX, nEndY ) );
+
+ return;
+ }
+
+ aMultiMark.MarkToMulti();
+ if ( !aMultiMark.IsMultiMarked() )
+ return;
+ const ScRange& aMultiRange = aMultiMark.GetMultiMarkArea();
+ SCCOL nX1 = aMultiRange.aStart.Col();
+ SCROW nY1 = aMultiRange.aStart.Row();
+ SCCOL nX2 = aMultiRange.aEnd.Col();
+ SCROW nY2 = aMultiRange.aEnd.Row();
+
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ SCCOL nTestX2 = nX2;
+ SCROW nTestY2 = nY2;
+
+ rDoc.ExtendMerge( nX1,nY1, nTestX2,nTestY2, nTab );
+
+ SCCOL nPosX = mrViewData.GetPosX( eHWhich );
+ SCROW nPosY = mrViewData.GetPosY( eVWhich );
+ // is the selection visible at all?
+ if (nTestX2 < nPosX || nTestY2 < nPosY)
+ return;
+ SCCOL nRealX1 = nX1;
+ if (nX1 < nPosX)
+ nX1 = nPosX;
+ if (nY1 < nPosY)
+ nY1 = nPosY;
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // limit the selection to only what is visible on the screen
+ SCCOL nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich);
+ if (nXRight > rDoc.MaxCol())
+ nXRight = rDoc.MaxCol();
+
+ SCROW nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich);
+ if (nYBottom > rDoc.MaxRow())
+ nYBottom = rDoc.MaxRow();
+
+ // is the selection visible at all?
+ if (nX1 > nXRight || nY1 > nYBottom)
+ return;
+
+ if (nX2 > nXRight)
+ nX2 = nXRight;
+ if (nY2 > nYBottom)
+ nY2 = nYBottom;
+ }
+ else
+ {
+ SCCOL nMaxTiledCol;
+ SCROW nMaxTiledRow;
+ rDoc.GetTiledRenderingArea(nTab, nMaxTiledCol, nMaxTiledRow);
+
+ if (nX2 > nMaxTiledCol)
+ nX2 = nMaxTiledCol;
+ if (nY2 > nMaxTiledRow)
+ nY2 = nMaxTiledRow;
+ }
+
+ ScInvertMerger aInvert( &rRects );
+
+ Point aScrPos = bInPrintTwips ? mrViewData.GetPrintTwipsPos(nX1, nY1) :
+ mrViewData.GetScrPos(nX1, nY1, eWhich);
+ tools::Long nScrY = aScrPos.Y();
+ bool bWasHidden = false;
+ for (SCROW nY=nY1; nY<=nY2; nY++)
+ {
+ bool bFirstRow = ( nY == nPosY ); // first visible row?
+ bool bDoHidden = false; // repeat hidden ?
+ sal_uInt16 nHeightTwips = rDoc.GetRowHeight( nY,nTab );
+ bool bDoRow = ( nHeightTwips != 0 );
+ if (bDoRow)
+ {
+ if (bWasHidden) // test hidden merge
+ {
+ bDoHidden = true;
+ bDoRow = true;
+ }
+
+ bWasHidden = false;
+ }
+ else
+ {
+ bWasHidden = true;
+ if (nY==nY2)
+ bDoRow = true; // last cell of the block
+ }
+
+ if ( bDoRow )
+ {
+ SCCOL nLoopEndX = nX2;
+ if (nX2 < nX1) // the rest of the merge
+ {
+ SCCOL nStartX = nX1;
+ while ( rDoc.GetAttr(nStartX,nY,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped() )
+ --nStartX;
+ if (nStartX <= nX2)
+ nLoopEndX = nX1;
+ }
+
+ const tools::Long nHeight = bInPrintTwips ?
+ nHeightTwips : ScViewData::ToPixel(nHeightTwips, nPPTY);
+ tools::Long nEndY = nScrY + nHeight - 1;
+ tools::Long nScrX = aScrPos.X();
+ for (SCCOL nX=nX1; nX<=nLoopEndX; nX++)
+ {
+ tools::Long nWidth = rDoc.GetColWidth(nX, nTab);
+ if (!bInPrintTwips)
+ nWidth = ScViewData::ToPixel(nWidth, nPPTX);
+
+ if ( nWidth > 0 )
+ {
+ tools::Long nEndX = nScrX + ( nWidth - 1 ) * nLayoutSign;
+
+ SCROW nThisY = nY;
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nX, nY, nTab );
+ const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ if ( pMergeFlag->IsVerOverlapped() && ( bDoHidden || bFirstRow ) )
+ {
+ while ( pMergeFlag->IsVerOverlapped() && nThisY > 0 &&
+ (rDoc.RowHidden(nThisY-1, nTab) || bFirstRow) )
+ {
+ --nThisY;
+ pPattern = rDoc.GetPattern( nX, nThisY, nTab );
+ pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ }
+ }
+
+ // only the rest of the merged is seen ?
+ SCCOL nThisX = nX;
+ if ( pMergeFlag->IsHorOverlapped() && nX == nPosX && nX > nRealX1 )
+ {
+ while ( pMergeFlag->IsHorOverlapped() )
+ {
+ --nThisX;
+ pPattern = rDoc.GetPattern( nThisX, nThisY, nTab );
+ pMergeFlag = &pPattern->GetItem(ATTR_MERGE_FLAG);
+ }
+ }
+
+ if ( aMultiMark.IsCellMarked( nThisX, nThisY, true ) )
+ {
+ if ( !pMergeFlag->IsOverlapped() )
+ {
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ if (pMerge->GetColMerge() > 0 || pMerge->GetRowMerge() > 0)
+ {
+ const SCCOL nEndColMerge = nThisX + pMerge->GetColMerge();
+ const SCROW nEndRowMerge = nThisY + pMerge->GetRowMerge();
+ Point aEndPos = bInPrintTwips ?
+ mrViewData.GetPrintTwipsPos(nEndColMerge, nEndRowMerge) :
+ mrViewData.GetScrPos(nEndColMerge, nEndRowMerge, eWhich);
+ if ( aEndPos.X() * nLayoutSign > nScrX * nLayoutSign && aEndPos.Y() > nScrY )
+ {
+ aInvert.AddRect( tools::Rectangle( nScrX,nScrY,
+ aEndPos.X()-nLayoutSign,aEndPos.Y()-1 ) );
+ }
+ }
+ else if ( nEndX * nLayoutSign >= nScrX * nLayoutSign && nEndY >= nScrY )
+ {
+ aInvert.AddRect( tools::Rectangle( nScrX,nScrY,nEndX,nEndY ) );
+ }
+ }
+ }
+
+ nScrX = nEndX + nLayoutSign;
+ }
+ }
+ nScrY = nEndY + 1;
+ }
+ }
+}
+
+void ScGridWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged(rDCEvt);
+
+ if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) ||
+ (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
+ return;
+
+ if ( rDCEvt.GetType() == DataChangedEventType::FONTS && eWhich == mrViewData.GetActivePart() )
+ mrViewData.GetDocShell()->UpdateFontList();
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ if ( eWhich == mrViewData.GetActivePart() ) // only once for the view
+ {
+ ScTabView* pView = mrViewData.GetView();
+
+ pView->RecalcPPT();
+
+ // RepeatResize in case scroll bar sizes have changed
+ pView->RepeatResize();
+ pView->UpdateAllOverlays();
+
+ // invalidate cell attribs in input handler, in case the
+ // EditEngine BackgroundColor has to be changed
+ if ( mrViewData.IsActive() )
+ {
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+void ScGridWindow::initiatePageBreaks()
+{
+ bInitialPageBreaks = true;
+}
+
+IMPL_LINK(ScGridWindow, InitiatePageBreaksTimer, Timer*, pTimer, void)
+{
+ if (pTimer != &maShowPageBreaksTimer)
+ return;
+
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ const bool bPage = rOpts.GetOption(VOPT_PAGEBREAKS);
+ // tdf#124983, if option LibreOfficeDev Calc/View/Visual Aids/Page
+ // breaks is enabled, breaks should be visible. If the document is
+ // opened the first time or a tab is activated the first time, the
+ // breaks are not calculated yet, so this initialization is done here.
+ if (bPage)
+ {
+ const SCTAB nCurrentTab = mrViewData.GetTabNo();
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const Size aPageSize = rDoc.GetPageSize(nCurrentTab);
+ // Do not attempt to calculate a page size here if it is empty if
+ // that involves counting pages.
+ // An earlier implementation did
+ // ScPrintFunc(pDocSh, pDocSh->GetPrinter(), nCurrentTab);
+ // rDoc.SetPageSize(nCurrentTab, rDoc.GetPageSize(nCurrentTab));
+ // which resulted in tremendous waiting times after having loaded
+ // larger documents i.e. imported from CSV, in which UI is entirely
+ // blocked. All time is spent under ScPrintFunc::CountPages() in
+ // ScTable::ExtendPrintArea() in the loop that calls
+ // MaybeAddExtraColumn() to do stuff for each text string content
+ // cell (each row in each column). Maybe that can be optimized, or
+ // obtaining page size without that overhead would be possible, but
+ // as is calling that from here is a no-no so this is a quick
+ // disable things.
+ if (!aPageSize.IsEmpty())
+ {
+ ScDocShell* pDocSh = mrViewData.GetDocShell();
+ const bool bModified = pDocSh->IsModified();
+ // Even setting the same size sets page size valid, so
+ // UpdatePageBreaks() actually does something.
+ rDoc.SetPageSize( nCurrentTab, aPageSize);
+ rDoc.UpdatePageBreaks(nCurrentTab);
+ pDocSh->PostPaint(0, 0, nCurrentTab, rDoc.MaxCol(), rDoc.MaxRow(), nCurrentTab, PaintPartFlags::Grid);
+ pDocSh->SetModified(bModified);
+ }
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin5.cxx b/sc/source/ui/view/gridwin5.cxx
new file mode 100644
index 0000000000..206b53843e
--- /dev/null
+++ b/sc/source/ui/view/gridwin5.cxx
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/flditem.hxx>
+
+#include <svx/fmpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/help.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/sfxhelp.hxx>
+
+#include <AccessibleDocument.hxx>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+
+#include <gridwin.hxx>
+#include <viewdata.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include <document.hxx>
+#include <notemark.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <dbfunc.hxx>
+#include <postit.hxx>
+#include <global.hxx>
+
+bool ScGridWindow::ShowNoteMarker( SCCOL nPosX, SCROW nPosY, bool bKeyboard )
+{
+ bool bDone = false;
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ ScAddress aCellPos( nPosX, nPosY, nTab );
+
+ OUString aTrackText;
+ bool bLeftEdge = false;
+
+ // change tracking
+
+ ScChangeTrack* pTrack = rDoc.GetChangeTrack();
+ ScChangeViewSettings* pSettings = rDoc.GetChangeViewSettings();
+ if ( pTrack && pTrack->GetFirst() && pSettings && pSettings->ShowChanges())
+ {
+ const ScChangeAction* pFound = nullptr;
+ const ScChangeAction* pFoundContent = nullptr;
+ const ScChangeAction* pFoundMove = nullptr;
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ if ( pAction->IsVisible() &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, rDoc ) )
+ {
+ ScChangeActionType eType = pAction->GetType();
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == nTab )
+ {
+ ScRange aRange = rBig.MakeRange( rDoc );
+
+ if ( eType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+
+ if ( aRange.Contains( aCellPos ) )
+ {
+ pFound = pAction; // the last one wins
+ switch ( eType )
+ {
+ case SC_CAT_CONTENT :
+ pFoundContent = pAction;
+ break;
+ case SC_CAT_MOVE :
+ pFoundMove = pAction;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+ if ( eType == SC_CAT_MOVE )
+ {
+ ScRange aRange =
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( rDoc );
+ if ( aRange.Contains( aCellPos ) )
+ {
+ pFound = pAction;
+ }
+ }
+ }
+ pAction = pAction->GetNext();
+ }
+
+ if ( pFound )
+ {
+ if ( pFoundContent && pFound->GetType() != SC_CAT_CONTENT )
+ pFound = pFoundContent; // content wins
+ if ( pFoundMove && pFound->GetType() != SC_CAT_MOVE &&
+ pFoundMove->GetActionNumber() >
+ pFound->GetActionNumber() )
+ pFound = pFoundMove; // move wins
+
+ // for deleted columns: Arrow on the left side of the cell
+ if ( pFound->GetType() == SC_CAT_DELETE_COLS )
+ bLeftEdge = true;
+
+ DateTime aDT = pFound->GetDateTime();
+ aTrackText = pFound->GetUser()
+ + ", "
+ + ScGlobal::getLocaleData().getDate(aDT)
+ + " "
+ + ScGlobal::getLocaleData().getTime(aDT)
+ + ":\n";
+ OUString aComStr=pFound->GetComment();
+ if(!aComStr.isEmpty())
+ {
+ aTrackText += aComStr + "\n( ";
+ }
+ OUString aTmp = pFound->GetDescription(rDoc);
+ aTrackText += aTmp;
+ if(!aComStr.isEmpty())
+ {
+ aTrackText += ")";
+ }
+ }
+ }
+
+ // Note, only if it is not already displayed on the Drawing Layer:
+ const ScPostIt* pNote = rDoc.GetNote( aCellPos );
+ if ( (!aTrackText.isEmpty()) || (pNote && !pNote->IsCaptionShown()) )
+ {
+ bool bNew = true;
+ bool bFast = false;
+ if (mpNoteMarker) // A note already shown
+ {
+ if (mpNoteMarker->GetDocPos() == aCellPos)
+ bNew = false; // then stop
+ else
+ bFast = true; // otherwise, at once
+
+ // marker which was shown for ctrl-F1 isn't removed by mouse events
+ if (mpNoteMarker->IsByKeyboard() && !bKeyboard)
+ bNew = false;
+ }
+ if (bNew)
+ {
+ if (bKeyboard)
+ bFast = true; // keyboard also shows the marker immediately
+
+ mpNoteMarker.reset();
+
+ bool bHSplit = mrViewData.GetHSplitMode() != SC_SPLIT_NONE;
+ bool bVSplit = mrViewData.GetVSplitMode() != SC_SPLIT_NONE;
+
+ vcl::Window* pLeft = mrViewData.GetView()->GetWindowByPos( bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT );
+ vcl::Window* pRight = bHSplit ? mrViewData.GetView()->GetWindowByPos( bVSplit ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT ) : nullptr;
+ vcl::Window* pBottom = bVSplit ? mrViewData.GetView()->GetWindowByPos( SC_SPLIT_BOTTOMLEFT ) : nullptr;
+ vcl::Window* pDiagonal = (bHSplit && bVSplit) ? mrViewData.GetView()->GetWindowByPos( SC_SPLIT_BOTTOMRIGHT ) : nullptr;
+ OSL_ENSURE( pLeft, "ScGridWindow::ShowNoteMarker - missing top-left grid window" );
+
+ /* If caption is shown from right or bottom windows, adjust
+ mapmode to include size of top-left window. */
+ MapMode aMapMode = GetDrawMapMode( true );
+ Size aLeftSize = pLeft->PixelToLogic( pLeft->GetOutputSizePixel(), aMapMode );
+ Point aOrigin = aMapMode.GetOrigin();
+ if( (this == pRight) || (this == pDiagonal) )
+ aOrigin.AdjustX(aLeftSize.Width() );
+ if( (this == pBottom) || (this == pDiagonal) )
+ aOrigin.AdjustY(aLeftSize.Height() );
+ aMapMode.SetOrigin( aOrigin );
+
+ mpNoteMarker.reset(new ScNoteMarker(pLeft, pRight, pBottom, pDiagonal,
+ &rDoc, aCellPos, aTrackText,
+ aMapMode, bLeftEdge, bFast, bKeyboard));
+ }
+
+ bDone = true; // something is shown (old or new)
+ }
+
+ return bDone;
+}
+
+void ScGridWindow::RequestHelp(const HelpEvent& rHEvt)
+{
+ bool bDone = false;
+ OUString aFormulaText;
+ tools::Rectangle aFormulaPixRect;
+ bool bHelpEnabled = bool(rHEvt.GetMode() & ( HelpEventMode::BALLOON | HelpEventMode::QUICK ));
+ SdrView* pDrView = mrViewData.GetScDrawView();
+ bool bDrawTextEdit = false;
+ if (pDrView)
+ bDrawTextEdit = pDrView->IsTextEdit();
+ // notes or change tracking
+ if ( bHelpEnabled && !bDrawTextEdit )
+ {
+ Point aPosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
+ SCCOL nPosX;
+ SCROW nPosY;
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ const ScViewOptions& rOpts = mrViewData.GetOptions();
+ mrViewData.GetPosFromPixel( aPosPixel.X(), aPosPixel.Y(), eWhich, nPosX, nPosY );
+
+ if ( ShowNoteMarker( nPosX, nPosY, false ) )
+ {
+ Window::RequestHelp( rHEvt ); // turn off old Tip/Balloon
+ bDone = true;
+ }
+
+ if ( rOpts.GetOption( VOPT_FORMULAS_MARKS ) )
+ {
+ aFormulaText = rDoc.GetFormula( nPosX, nPosY, nTab );
+ if ( !aFormulaText.isEmpty() ) {
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
+ aFormulaPixRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, true );
+ }
+ }
+ }
+
+ if (!bDone && mpNoteMarker)
+ {
+ if (mpNoteMarker->IsByKeyboard())
+ {
+ // marker which was shown for ctrl-F1 isn't removed by mouse events
+ }
+ else
+ {
+ mpNoteMarker.reset();
+ }
+ }
+
+ if ( !aFormulaText.isEmpty() )
+ {
+ tools::Rectangle aScreenRect(OutputToScreenPixel(aFormulaPixRect.TopLeft()),
+ OutputToScreenPixel(aFormulaPixRect.BottomRight()));
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon(this, rHEvt.GetMousePosPixel(), aScreenRect, aFormulaText);
+ else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
+ Help::ShowQuickHelp(this, aScreenRect, aFormulaText);
+ bDone = true;
+ }
+
+ // Image-Map / Text-URL
+
+ if ( bHelpEnabled && !bDone && !nButtonDown ) // only without pressed button
+ {
+ OUString aHelpText;
+ tools::Rectangle aPixRect;
+ Point aPosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
+
+ if ( pDrView ) // URL / Image-Map
+ {
+ SdrViewEvent aVEvt;
+ MouseEvent aMEvt( aPosPixel, 1, MouseEventModifiers::NONE, MOUSE_LEFT );
+ SdrHitKind eHit = pDrView->PickAnything( aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt );
+
+ if ( eHit != SdrHitKind::NONE && aVEvt.mpObj != nullptr )
+ {
+ // URL for IMapObject below Pointer is help text
+ if (SvxIMapInfo::GetIMapInfo(aVEvt.mpObj))
+ {
+ Point aLogicPos = PixelToLogic( aPosPixel );
+ IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(
+ aVEvt.mpObj, aLogicPos, GetOutDev() );
+
+ if ( pIMapObj )
+ {
+ // For image maps show the description, if available
+ aHelpText = pIMapObj->GetAltText();
+ if (aHelpText.isEmpty())
+ aHelpText = SfxHelp::GetURLHelpText(pIMapObj->GetURL());
+ aPixRect = LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ }
+ }
+ // URL in shape text or at shape itself (URL in text overrides object URL)
+ if ( aHelpText.isEmpty() )
+ {
+ if( aVEvt.meEvent == SdrEventKind::ExecuteUrl )
+ {
+ if (aVEvt.mpURLField)
+ {
+ aHelpText = SfxHelp::GetURLHelpText(aVEvt.mpURLField->GetURL());
+ aPixRect = LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ }
+ }
+ else
+ {
+ SdrPageView* pPV = nullptr;
+ Point aMDPos = PixelToLogic( aPosPixel );
+ SdrObject* pObj = pDrView->PickObj(aMDPos, pDrView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER);
+ if (pObj)
+ {
+ if ( pObj->IsGroupObject() )
+ {
+ SdrObject* pHit = pDrView->PickObj(aMDPos, pDrView->getHitTolLog(), pPV, SdrSearchOptions::DEEP);
+ if (pHit)
+ pObj = pHit;
+ }
+ // Fragments pointing into the current document need no tooltip
+ // describing the ctrl-click functionality.
+ if ( !pObj->getHyperlink().isEmpty() && !pObj->getHyperlink().startsWith("#") )
+ {
+ aPixRect = LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ aHelpText = SfxHelp::GetURLHelpText(pObj->getHyperlink());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( aHelpText.isEmpty() ) // Text-URL
+ {
+ OUString aUrl;
+ if ( GetEditUrl( aPosPixel, nullptr, &aUrl ) )
+ {
+ aHelpText = SfxHelp::GetURLHelpText(
+ INetURLObject::decode(aUrl, INetURLObject::DecodeMechanism::Unambiguous));
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCCOL nPosX;
+ SCROW nPosY;
+ SCTAB nTab = mrViewData.GetTabNo();
+ mrViewData.GetPosFromPixel( aPosPixel.X(), aPosPixel.Y(), eWhich, nPosX, nPosY );
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nPosX, nPosY, nTab );
+
+ // bForceToTop = sal_False, use the cell's real position
+ aPixRect = mrViewData.GetEditArea( eWhich, nPosX, nPosY, this, pPattern, false );
+ }
+ }
+
+ if ( !aHelpText.isEmpty() )
+ {
+ tools::Rectangle aScreenRect(OutputToScreenPixel(aPixRect.TopLeft()),
+ OutputToScreenPixel(aPixRect.BottomRight()));
+
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon(this,rHEvt.GetMousePosPixel(), aScreenRect, aHelpText);
+ else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
+ Help::ShowQuickHelp(this,aScreenRect, aHelpText);
+
+ bDone = true;
+ }
+ }
+
+ // basic controls
+
+ if ( pDrView && bHelpEnabled && !bDone )
+ {
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ OSL_ENSURE( pPV, "SdrPageView* is NULL" );
+ if (pPV)
+ bDone = FmFormPage::RequestHelp( this, pDrView, rHEvt );
+ }
+
+ // If QuickHelp for AutoFill is shown, do not allow it to be removed
+
+ if ( nMouseStatus == SC_GM_TABDOWN && mrViewData.GetRefType() == SC_REFTYPE_FILL &&
+ Help::IsQuickHelpEnabled() )
+ bDone = true;
+
+ if (!bDone)
+ Window::RequestHelp( rHEvt );
+}
+
+bool ScGridWindow::IsMyModel(const SdrEditView* pSdrView)
+{
+ return pSdrView &&
+ &pSdrView->GetModel() == mrViewData.GetDocument().GetDrawLayer();
+}
+
+void ScGridWindow::HideNoteMarker()
+{
+ mpNoteMarker.reset();
+}
+
+css::uno::Reference< css::accessibility::XAccessible >
+ ScGridWindow::CreateAccessible()
+{
+ css::uno::Reference< css::accessibility::XAccessible > xAcc= GetAccessible(false);
+ if (xAcc.is())
+ {
+ return xAcc;
+ }
+
+ rtl::Reference<ScAccessibleDocument> pAccessibleDocument =
+ new ScAccessibleDocument(GetAccessibleParentWindow()->GetAccessible(),
+ mrViewData.GetViewShell(), eWhich);
+ pAccessibleDocument->PreInit();
+
+ xAcc = pAccessibleDocument;
+ SetAccessible(xAcc);
+
+ pAccessibleDocument->Init();
+
+ return xAcc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/gridwin_dbgutil.cxx b/sc/source/ui/view/gridwin_dbgutil.cxx
new file mode 100644
index 0000000000..b141bddd76
--- /dev/null
+++ b/sc/source/ui/view/gridwin_dbgutil.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <iostream>
+
+#include <gridwin.hxx>
+#include <svx/svdpage.hxx>
+#include <libxml/xmlwriter.h>
+#include <viewdata.hxx>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <userdat.hxx>
+#include <dpobject.hxx>
+
+namespace {
+
+std::ostream& operator<<(std::ostream& rStrm, const ScAddress& rAddr)
+{
+ rStrm << "Col: " << rAddr.Col() << ", Row: " << rAddr.Row() << ", Tab: " << rAddr.Tab();
+ return rStrm;
+}
+
+void dumpScDrawObjData(const ScGridWindow& rWindow, const ScDrawObjData& rData, MapUnit eMapUnit)
+{
+ const Point& rStartOffset = rData.maStartOffset;
+ Point aStartOffsetPixel = rWindow.LogicToPixel(rStartOffset, MapMode(eMapUnit));
+ std::cout << " Start: " << rData.maStart << ", Offset: " << aStartOffsetPixel << std::endl;
+
+ const Point& rEndOffset = rData.maEndOffset;
+ Point aEndOffsetPixel = rWindow.LogicToPixel(rEndOffset, MapMode(eMapUnit));
+ std::cout << " End: : " << rData.maEnd << ", Offset: " << aEndOffsetPixel << std::endl;
+}
+
+}
+
+void ScGridWindow::dumpColumnInformationPixel()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ for (SCCOL nCol = 0; nCol <= 20; ++nCol)
+ {
+ sal_uInt16 nWidth = rDoc.GetColWidth(nCol, nTab);
+ tools::Long nPixel = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::MapTwip)).getX();
+ std::cout << "Column: " << nCol << ", Width: " << nPixel << "px" << std::endl;
+ }
+}
+
+void ScGridWindow::dumpColumnInformationHmm()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ SCTAB nTab = mrViewData.GetTabNo();
+ for (SCCOL nCol = 0; nCol <= 20; ++nCol)
+ {
+ sal_uInt16 nWidth = rDoc.GetColWidth(nCol, nTab);
+ tools::Long nPixel = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);
+ std::cout << "Column: " << nCol << ", Width: " << nPixel << "hmm" << std::endl;
+ }
+}
+
+void ScGridWindow::dumpCellProperties()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const ScMarkData& rMark = mrViewData.GetMarkData();
+ SCTAB nTab = mrViewData.GetTabNo();
+
+ ScRangeList aList;
+ if (rMark.IsMultiMarked())
+ {
+ aList = rMark.GetMarkedRangesForTab(nTab);
+ }
+ else if (rMark.IsMarked())
+ {
+ aList.Join(rMark.GetMarkArea());
+ }
+ else
+ {
+ SCCOL nCol = mrViewData.GetCurX();
+ SCROW nRow = mrViewData.GetCurY();
+
+ ScRange aRange(nCol, nRow, nTab);
+ aList.Join(aRange, false);
+ }
+
+ xmlTextWriterPtr writer = xmlNewTextWriterFilename( "dump.xml", 0 );
+ xmlTextWriterSetIndent(writer,1);
+ (void)xmlTextWriterSetIndentString(writer, BAD_CAST(" "));
+
+ (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
+
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("selection"));
+
+ for (size_t i = 0, n = aList.size(); i < n; ++i)
+ {
+ ScRange const & rRange = aList[i];
+
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ const ScPatternAttr* pPatternAttr = rDoc.GetPattern(nCol, nRow, nTab);
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("cell"));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("column"), BAD_CAST(OString::number(nCol).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("row"), BAD_CAST(OString::number(nRow).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("tab"), BAD_CAST(OString::number(nTab).getStr()));
+
+ pPatternAttr->GetItemSet().dumpAsXml(writer);
+
+ (void)xmlTextWriterEndElement(writer);
+ }
+ }
+ }
+
+ (void)xmlTextWriterEndElement(writer);
+
+ (void)xmlTextWriterEndDocument( writer );
+ xmlFreeTextWriter (writer);
+}
+
+void ScGridWindow::dumpGraphicInformation()
+{
+ ScDocument& rDoc = mrViewData.GetDocument();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (!pDrawLayer)
+ return;
+
+ sal_uInt16 nPageCount = pDrawLayer->GetPageCount();
+ for (sal_uInt16 nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ SdrPage* pPage = pDrawLayer->GetPage(nPage);
+ size_t nObjCount = pPage->GetObjCount();
+ for (size_t nObj = 0; nObj < nObjCount; ++nObj)
+ {
+ SdrObject* pObj = pPage->GetObj(nObj);
+ std::cout << "Graphic Object" << std::endl;
+ ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
+ if (pObjData)
+ dumpScDrawObjData(*this, *pObjData, pDrawLayer->GetScaleUnit());
+
+ const tools::Rectangle& rRect = pObj->GetSnapRect();
+ tools::Rectangle aRect = LogicToPixel(rRect, MapMode(pDrawLayer->GetScaleUnit()));
+ std::cout << "Snap Rectangle (in pixel): " << aRect << std::endl;
+ }
+ }
+}
+
+void ScGridWindow::dumpColumnCellStorage()
+{
+ // Get the current cursor position.
+ ScAddress aCurPos = mrViewData.GetCurPos();
+
+ ScDocument& rDoc = mrViewData.GetDocument();
+ const ScDPObject* pDP = rDoc.GetDPAtCursor(aCurPos.Col(), aCurPos.Row(), aCurPos.Tab());
+ if (pDP)
+ {
+ // Dump the pivot table info if the cursor is over a pivot table.
+ pDP->Dump();
+ pDP->DumpCache();
+ return;
+ }
+
+ // Dump the column cell storage info.
+ rDoc.DumpColumnStorage(aCurPos.Tab(), aCurPos.Col());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/hdrcont.cxx b/sc/source/ui/view/hdrcont.cxx
new file mode 100644
index 0000000000..c6688ea115
--- /dev/null
+++ b/sc/source/ui/view/hdrcont.cxx
@@ -0,0 +1,1124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <vcl/commandevent.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <svtools/colorcfg.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <hdrcont.hxx>
+#include <dbdata.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <gridmerg.hxx>
+#include <document.hxx>
+#include <markdata.hxx>
+#include <tabview.hxx>
+#include <viewdata.hxx>
+#include <columnspanset.hxx>
+
+#define SC_DRAG_MIN 2
+
+// passes in paint
+// (selection left/right must be first because the continuous lines
+// are partly overwritten later)
+
+#define SC_HDRPAINT_SEL_BOTTOM 4
+#define SC_HDRPAINT_BOTTOM 5
+#define SC_HDRPAINT_TEXT 6
+#define SC_HDRPAINT_COUNT 7
+
+ScHeaderControl::ScHeaderControl( vcl::Window* pParent, SelectionEngine* pSelectionEngine,
+ SCCOLROW nNewSize, bool bNewVertical, ScTabView* pTab ) :
+ Window ( pParent ),
+ pSelEngine ( pSelectionEngine ),
+ aShowHelpTimer("sc HeaderControl Popover Timer"),
+ bVertical ( bNewVertical ),
+ nSize ( nNewSize ),
+ nMarkStart ( 0 ),
+ nMarkEnd ( 0 ),
+ bMarkRange ( false ),
+ bDragging ( false ),
+ nDragNo ( 0 ),
+ nDragStart ( 0 ),
+ nDragPos ( 0 ),
+ nTipVisible ( nullptr ),
+ bDragMoved ( false ),
+ bIgnoreMove ( false ),
+ bInRefMode ( false ),
+ pTabView ( pTab )
+{
+ // RTL: no default mirroring for this window, the spreadsheet itself
+ // is also not mirrored
+ // mirror the vertical window for correct border drawing
+ // table layout depends on sheet format, not UI setting, so the
+ // borders of the vertical window have to be handled manually, too.
+ EnableRTL( false );
+
+ aNormFont = GetFont();
+ aNormFont.SetTransparent( true ); //! hard-set WEIGHT_NORMAL ???
+ aBoldFont = aNormFont;
+ aBoldFont.SetWeight( WEIGHT_BOLD );
+ aAutoFilterFont = aNormFont;
+
+ SetFont(aBoldFont);
+ bBoldSet = true;
+ bAutoFilterSet = false;
+
+ Size aSize = LogicToPixel( Size(
+ GetTextWidth("8888"),
+ GetTextHeight() ) );
+ aSize.AdjustWidth(4 ); // place for highlight border
+ aSize.AdjustHeight(3 );
+ SetSizePixel( aSize );
+
+ nWidth = nSmallWidth = aSize.Width();
+ nBigWidth = LogicToPixel( Size( GetTextWidth("8888888"), 0 ) ).Width() + 5;
+
+ aShowHelpTimer.SetInvokeHandler(LINK(this, ScHeaderControl, ShowDragHelpHdl));
+ aShowHelpTimer.SetTimeout(GetSettings().GetMouseSettings().GetDoubleClickTime());
+
+ SetBackground();
+}
+
+void ScHeaderControl::dispose()
+{
+ aShowHelpTimer.Stop();
+ vcl::Window::dispose();
+}
+
+void ScHeaderControl::SetWidth( tools::Long nNew )
+{
+ OSL_ENSURE( bVertical, "SetWidth works only on row headers" );
+ if ( nNew != nWidth )
+ {
+ Size aSize( nNew, GetSizePixel().Height() );
+ SetSizePixel( aSize );
+
+ nWidth = nNew;
+
+ Invalidate();
+ }
+}
+
+ScHeaderControl::~ScHeaderControl()
+{
+}
+
+void ScHeaderControl::DoPaint( SCCOLROW nStart, SCCOLROW nEnd )
+{
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Rectangle aRect( Point(0,0), GetOutputSizePixel() );
+ if ( bVertical )
+ {
+ aRect.SetTop( GetScrPos( nStart )-nLayoutSign ); // extra pixel for line at top of selection
+ aRect.SetBottom( GetScrPos( nEnd+1 )-nLayoutSign );
+ }
+ else
+ {
+ aRect.SetLeft( GetScrPos( nStart )-nLayoutSign ); // extra pixel for line left of selection
+ aRect.SetRight( GetScrPos( nEnd+1 )-nLayoutSign );
+ }
+ Invalidate(aRect);
+}
+
+void ScHeaderControl::SetMark( bool bNewSet, SCCOLROW nNewStart, SCCOLROW nNewEnd )
+{
+ bool bEnabled = SC_MOD()->GetInputOptions().GetMarkHeader(); //! cache?
+ if (!bEnabled)
+ bNewSet = false;
+
+ bool bOldSet = bMarkRange;
+ SCCOLROW nOldStart = nMarkStart;
+ SCCOLROW nOldEnd = nMarkEnd;
+ PutInOrder( nNewStart, nNewEnd );
+ bMarkRange = bNewSet;
+ nMarkStart = nNewStart;
+ nMarkEnd = nNewEnd;
+
+ // Paint
+
+ if ( bNewSet )
+ {
+ if ( bOldSet )
+ {
+ if ( nNewStart == nOldStart )
+ {
+ if ( nNewEnd != nOldEnd )
+ DoPaint( std::min( nNewEnd, nOldEnd ) + 1, std::max( nNewEnd, nOldEnd ) );
+ }
+ else if ( nNewEnd == nOldEnd )
+ DoPaint( std::min( nNewStart, nOldStart ), std::max( nNewStart, nOldStart ) - 1 );
+ else if ( nNewStart > nOldEnd || nNewEnd < nOldStart )
+ {
+ // two areas
+ DoPaint( nOldStart, nOldEnd );
+ DoPaint( nNewStart, nNewEnd );
+ }
+ else // somehow overlapping... (it is not often)
+ DoPaint( std::min( nNewStart, nOldStart ), std::max( nNewEnd, nOldEnd ) );
+ }
+ else
+ DoPaint( nNewStart, nNewEnd ); // completely new selection
+ }
+ else if ( bOldSet )
+ DoPaint( nOldStart, nOldEnd ); // cancel selection
+}
+
+tools::Long ScHeaderControl::GetScrPos( SCCOLROW nEntryNo ) const
+{
+ tools::Long nScrPos;
+
+ tools::Long nMax = ( bVertical ? GetOutputSizePixel().Height() : GetOutputSizePixel().Width() ) + 1;
+ if (nEntryNo >= nSize)
+ nScrPos = nMax;
+ else
+ {
+ nScrPos = 0;
+ for (SCCOLROW i=GetPos(); i<nEntryNo && nScrPos<nMax; i++)
+ {
+ sal_uInt16 nAdd = GetEntrySize(i);
+ if (nAdd)
+ nScrPos += nAdd;
+ else
+ {
+ SCCOLROW nHidden = GetHiddenCount(i);
+ if (nHidden > 0)
+ i += nHidden - 1;
+ }
+ }
+ }
+
+ if ( IsLayoutRTL() )
+ nScrPos = nMax - nScrPos - 2;
+
+ return nScrPos;
+}
+
+void ScHeaderControl::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect )
+{
+ // It is important for VCL to have few calls, that is why the outer lines are
+ // grouped together
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ bool bHighContrast = rStyleSettings.GetHighContrastMode();
+ bool bDark = rStyleSettings.GetFaceColor().IsDark();
+ // Use the same distinction for bDark as in Window::DrawSelectionBackground
+
+ Color aTextColor = rStyleSettings.GetButtonTextColor();
+ Color aSelTextColor = rStyleSettings.GetHighlightTextColor();
+ Color aAFilterTextColor = rStyleSettings.GetButtonTextColor();
+ aAFilterTextColor.Merge(COL_LIGHTBLUE, bDark ? 150 : 10); // color of filtered row numbers
+ aNormFont.SetColor( aTextColor );
+ aAutoFilterFont.SetColor(aAFilterTextColor);
+ if ( bHighContrast )
+ aBoldFont.SetColor( aTextColor );
+ else
+ aBoldFont.SetColor( aSelTextColor );
+
+ if (bAutoFilterSet)
+ SetTextColor(aAFilterTextColor);
+ else
+ SetTextColor((bBoldSet && !bHighContrast) ? aSelTextColor : aTextColor);
+
+ Color aSelLineColor = rStyleSettings.GetHighlightColor();
+ aSelLineColor.Merge( COL_BLACK, 0xe0 ); // darken just a little bit
+
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ bool bMirrored = IsMirrored();
+
+ OUString aString;
+ sal_uInt16 nBarSize;
+ Point aScrPos;
+ Size aTextSize;
+
+ if (bVertical)
+ nBarSize = static_cast<sal_uInt16>(GetSizePixel().Width());
+ else
+ nBarSize = static_cast<sal_uInt16>(GetSizePixel().Height());
+
+ SCCOLROW nPos = GetPos();
+
+ tools::Long nPStart = bVertical ? rRect.Top() : rRect.Left();
+ tools::Long nPEnd = bVertical ? rRect.Bottom() : rRect.Right();
+
+ tools::Long nTransStart = nPEnd + 1;
+ tools::Long nTransEnd = 0;
+
+ tools::Long nInitScrPos = 0;
+ if ( bLayoutRTL )
+ {
+ std::swap(nPStart, nPEnd);
+ std::swap(nTransStart, nTransEnd);
+ if ( bVertical ) // start loops from the end
+ nInitScrPos = GetSizePixel().Height() - 1;
+ else
+ nInitScrPos = GetSizePixel().Width() - 1;
+ }
+
+ // complete the painting of the outer lines
+ // first find the end of the last cell
+
+ tools::Long nLineEnd = nInitScrPos - nLayoutSign;
+
+ for (SCCOLROW i=nPos; i<nSize; i++)
+ {
+ sal_uInt16 nSizePix = GetEntrySize( i );
+ if (nSizePix)
+ {
+ nLineEnd += nSizePix * nLayoutSign;
+
+ if ( bMarkRange && i >= nMarkStart && i <= nMarkEnd )
+ {
+ tools::Long nLineStart = nLineEnd - ( nSizePix - 1 ) * nLayoutSign;
+ if ( nLineStart * nLayoutSign < nTransStart * nLayoutSign )
+ nTransStart = nLineStart;
+ if ( nLineEnd * nLayoutSign > nTransEnd * nLayoutSign )
+ nTransEnd = nLineEnd;
+ }
+
+ if ( nLineEnd * nLayoutSign > nPEnd * nLayoutSign )
+ {
+ nLineEnd = nPEnd;
+ break;
+ }
+ }
+ else
+ {
+ SCCOLROW nHidden = GetHiddenCount(i);
+ if (nHidden > 0)
+ i += nHidden - 1;
+ }
+ }
+
+ // background is different for entry area and behind the entries
+
+ tools::Rectangle aFillRect;
+ GetOutDev()->SetLineColor();
+
+ if ( nLineEnd * nLayoutSign >= nInitScrPos * nLayoutSign )
+ {
+ GetOutDev()->SetFillColor( rStyleSettings.GetFaceColor() );
+ if ( bVertical )
+ aFillRect = tools::Rectangle( 0, nInitScrPos, nBarSize-1, nLineEnd );
+ else
+ aFillRect = tools::Rectangle( nInitScrPos, 0, nLineEnd, nBarSize-1 );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+
+ if ( nLineEnd * nLayoutSign < nPEnd * nLayoutSign )
+ {
+ GetOutDev()->SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::APPBACKGROUND).nColor );
+ if ( bVertical )
+ aFillRect = tools::Rectangle( 0, nLineEnd+nLayoutSign, nBarSize-1, nPEnd );
+ else
+ aFillRect = tools::Rectangle( nLineEnd+nLayoutSign, 0, nPEnd, nBarSize-1 );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+
+ if ( nLineEnd * nLayoutSign >= nPStart * nLayoutSign )
+ {
+ if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign )
+ {
+ if (bVertical)
+ aFillRect = tools::Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
+ else
+ aFillRect = tools::Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
+
+ if ( bHighContrast )
+ {
+ if ( bDark )
+ {
+ // solid grey background for dark face color is drawn before lines
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( COL_LIGHTGRAY );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+ }
+ else
+ {
+ // background for selection
+ GetOutDev()->SetLineColor();
+ Color aColor( rStyleSettings.GetAccentColor() );
+// merging the highlightcolor (which is used if accent does not exist) with the background
+// fails in many cases such as Breeze Dark (highlight is too close to background) and
+// Breeze Light (font color is white and not readable anymore)
+#ifdef MACOSX
+ aColor.Merge( rStyleSettings.GetFaceColor(), 80 );
+#endif
+ GetOutDev()->SetFillColor( aColor );
+ GetOutDev()->DrawRect( aFillRect );
+ }
+ }
+
+ GetOutDev()->SetLineColor( rStyleSettings.GetDarkShadowColor() );
+ if (bVertical)
+ {
+ tools::Long nDarkPos = bMirrored ? 0 : nBarSize-1;
+ GetOutDev()->DrawLine( Point( nDarkPos, nPStart ), Point( nDarkPos, nLineEnd ) );
+ }
+ else
+ GetOutDev()->DrawLine( Point( nPStart, nBarSize-1 ), Point( nLineEnd, nBarSize-1 ) );
+
+ // line in different color for selection
+ if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign && !bHighContrast )
+ {
+ GetOutDev()->SetLineColor( aSelLineColor );
+ if (bVertical)
+ {
+ tools::Long nDarkPos = bMirrored ? 0 : nBarSize-1;
+ GetOutDev()->DrawLine( Point( nDarkPos, nTransStart ), Point( nDarkPos, nTransEnd ) );
+ }
+ else
+ GetOutDev()->DrawLine( Point( nTransStart, nBarSize-1 ), Point( nTransEnd, nBarSize-1 ) );
+ }
+ }
+
+ // tdf#89841 Use blue row numbers when Autofilter selected
+ std::vector<sc::ColRowSpan> aSpans;
+ if (bVertical && pTabView)
+ {
+ SCTAB nTab = pTabView->GetViewData().GetTabNo();
+ ScDocument& rDoc = pTabView->GetViewData().GetDocument();
+
+ ScDBData* pDBData = rDoc.GetAnonymousDBData(nTab);
+ if (pDBData && pDBData->HasAutoFilter())
+ {
+ SCSIZE nSelected = 0;
+ SCSIZE nTotal = 0;
+ pDBData->GetFilterSelCount(nSelected, nTotal);
+ if (nTotal > nSelected)
+ {
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ if (pDBData->HasHeader())
+ nStartRow++;
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ }
+
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ if (!pDocColl->empty())
+ {
+ ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ if (rxDB->GetTab() == nTab && rxDB->HasAutoFilter())
+ {
+ SCSIZE nSelected = 0;
+ SCSIZE nTotal = 0;
+ rxDB->GetFilterSelCount(nSelected, nTotal);
+ if (nTotal > nSelected)
+ {
+ ScRange aRange;
+ rxDB->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ if (rxDB->HasHeader())
+ nStartRow++;
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ }
+ }
+ }
+ }
+
+ // loop through entries several times to avoid changing the line color too often
+ // and to allow merging of lines
+
+ ScGridMerger aGrid( GetOutDev(), 1, 1 );
+
+ // start at SC_HDRPAINT_BOTTOM instead of 0 - selection doesn't get different
+ // borders, light border at top isn't used anymore
+ // use SC_HDRPAINT_SEL_BOTTOM for different color
+
+ for (sal_uInt16 nPass = SC_HDRPAINT_SEL_BOTTOM; nPass < SC_HDRPAINT_COUNT; nPass++)
+ {
+ // set line color etc. before entry loop
+ switch ( nPass )
+ {
+ case SC_HDRPAINT_SEL_BOTTOM:
+ // same as non-selected for high contrast
+ GetOutDev()->SetLineColor( bHighContrast ? rStyleSettings.GetDarkShadowColor() : aSelLineColor );
+ break;
+ case SC_HDRPAINT_BOTTOM:
+ GetOutDev()->SetLineColor( rStyleSettings.GetDarkShadowColor() );
+ break;
+ case SC_HDRPAINT_TEXT:
+ // DrawSelectionBackground is used only for high contrast on light background
+ if ( nTransEnd * nLayoutSign >= nTransStart * nLayoutSign && bHighContrast && !bDark )
+ {
+ // Transparent selection background is drawn after lines, before text.
+ // Use DrawSelectionBackground to make sure there is a visible
+ // difference. The case of a dark face color, where DrawSelectionBackground
+ // would just paint over the lines, is handled separately (bDark).
+ // Otherwise, GetHighlightColor is used with 80% transparency.
+ // The window's background color (SetBackground) has to be the background
+ // of the cell area, for the contrast comparison in DrawSelectionBackground.
+
+ tools::Rectangle aTransRect;
+ if (bVertical)
+ aTransRect = tools::Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
+ else
+ aTransRect = tools::Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
+ SetBackground( rStyleSettings.GetFaceColor() );
+ DrawSelectionBackground( aTransRect, 0, true, false );
+ SetBackground();
+ }
+ break;
+ }
+
+ SCCOLROW nCount=0;
+ tools::Long nScrPos=nInitScrPos;
+ do
+ {
+ if (bVertical)
+ aScrPos = Point( 0, nScrPos );
+ else
+ aScrPos = Point( nScrPos, 0 );
+
+ SCCOLROW nEntryNo = nCount + nPos;
+ if ( nEntryNo >= nSize ) // rDoc.MaxCol()/rDoc.MaxRow()
+ nScrPos = nPEnd + nLayoutSign; // beyond nPEnd -> stop
+ else
+ {
+ sal_uInt16 nSizePix = GetEntrySize( nEntryNo );
+
+ if (nSizePix == 0)
+ {
+ SCCOLROW nHidden = GetHiddenCount(nEntryNo);
+ if (nHidden > 0)
+ nCount += nHidden - 1;
+ }
+ else if ((nScrPos+nSizePix*nLayoutSign)*nLayoutSign >= nPStart*nLayoutSign)
+ {
+ Point aEndPos(aScrPos);
+ if (bVertical)
+ aEndPos = Point( aScrPos.X()+nBarSize-1, aScrPos.Y()+(nSizePix-1)*nLayoutSign );
+ else
+ aEndPos = Point( aScrPos.X()+(nSizePix-1)*nLayoutSign, aScrPos.Y()+nBarSize-1 );
+
+ bool bMark = bMarkRange && nEntryNo >= nMarkStart && nEntryNo <= nMarkEnd;
+ bool bNextToMark = bMarkRange && nEntryNo + 1 >= nMarkStart && nEntryNo <= nMarkEnd;
+
+ switch ( nPass )
+ {
+ case SC_HDRPAINT_SEL_BOTTOM:
+ case SC_HDRPAINT_BOTTOM:
+ if ( nPass == ( bNextToMark ? SC_HDRPAINT_SEL_BOTTOM : SC_HDRPAINT_BOTTOM ) )
+ {
+ if (bVertical)
+ aGrid.AddHorLine(/* here we work in pixels */ true, aScrPos.X(), aEndPos.X(), aEndPos.Y());
+ else
+ aGrid.AddVerLine(/* here we work in pixels */ true, aEndPos.X(), aScrPos.Y(), aEndPos.Y());
+
+ // thick bottom for hidden rows
+ // (drawn directly, without aGrid)
+ if ( nEntryNo+1 < nSize )
+ if ( GetEntrySize(nEntryNo+1)==0 )
+ {
+ if (bVertical)
+ GetOutDev()->DrawLine( Point(aScrPos.X(),aEndPos.Y()-nLayoutSign),
+ Point(aEndPos.X(),aEndPos.Y()-nLayoutSign) );
+ else
+ GetOutDev()->DrawLine( Point(aEndPos.X()-nLayoutSign,aScrPos.Y()),
+ Point(aEndPos.X()-nLayoutSign,aEndPos.Y()) );
+ }
+ }
+ break;
+
+ case SC_HDRPAINT_TEXT:
+ if ( nSizePix > 1 ) // minimal check for small columns/rows
+ {
+ if (bVertical)
+ {
+ bool bAutoFilterPos = false;
+ for (const auto& rSpan : aSpans)
+ {
+ if (nEntryNo >= rSpan.mnStart && nEntryNo <= rSpan.mnEnd)
+ {
+ bAutoFilterPos = true;
+ break;
+ }
+ }
+
+ if (bMark != bBoldSet || bAutoFilterPos != bAutoFilterSet)
+ {
+ if (bMark)
+ SetFont(aBoldFont);
+ else if (bAutoFilterPos)
+ SetFont(aAutoFilterFont);
+ else
+ SetFont(aNormFont);
+ bBoldSet = bMark;
+ bAutoFilterSet = bAutoFilterPos && !bMark;
+ }
+ }
+ else
+ {
+ if (bMark != bBoldSet)
+ {
+ if (bMark)
+ SetFont(aBoldFont);
+ else
+ SetFont(aNormFont);
+ bBoldSet = bMark;
+ }
+ }
+
+ aString = GetEntryText( nEntryNo );
+ aTextSize.setWidth( GetTextWidth( aString ) );
+ aTextSize.setHeight( GetTextHeight() );
+
+ Point aTxtPos(aScrPos);
+ if (bVertical)
+ {
+ aTxtPos.AdjustX((nBarSize-aTextSize.Width())/2 );
+ aTxtPos.AdjustY((nSizePix*nLayoutSign-aTextSize.Height())/2 );
+ if ( bMirrored )
+ aTxtPos.AdjustX(1 ); // dark border is left instead of right
+ }
+ else
+ {
+ aTxtPos.AdjustX((nSizePix*nLayoutSign-aTextSize.Width()+1)/2 );
+ aTxtPos.AdjustY((nBarSize-aTextSize.Height())/2 );
+ }
+ GetOutDev()->DrawText( aTxtPos, aString );
+ }
+ break;
+ }
+
+ // when selecting the complete row/column:
+ // InvertRect( Rectangle( aScrPos, aEndPos ) );
+ }
+ nScrPos += nSizePix * nLayoutSign; // also if before the visible area
+ }
+ ++nCount;
+ }
+ while ( nScrPos * nLayoutSign <= nPEnd * nLayoutSign );
+
+ aGrid.Flush();
+ }
+}
+
+SCCOLROW ScHeaderControl::GetMousePos(const Point& rPos, bool& rBorder) const
+{
+ bool bFound = false;
+ SCCOLROW nPos = GetPos();
+ SCCOLROW nHitNo = nPos;
+ SCCOLROW nEntryNo = 1 + nPos;
+ tools::Long nScrPos;
+ tools::Long nMousePos = bVertical ? rPos.Y() : rPos.X();
+ tools::Long nDif;
+ Size aSize = GetOutputSizePixel();
+ tools::Long nWinSize = bVertical ? aSize.Height() : aSize.Width();
+
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ tools::Long nEndPos = bLayoutRTL ? -1 : nWinSize;
+
+ nScrPos = GetScrPos( nPos ) - nLayoutSign;
+ do
+ {
+ if (nEntryNo > nSize)
+ nScrPos = nEndPos + nLayoutSign;
+ else
+ nScrPos += GetEntrySize( nEntryNo - 1 ) * nLayoutSign; //! GetHiddenCount() ??
+
+ nDif = nMousePos - nScrPos;
+ if (nDif >= -2 && nDif <= 2)
+ {
+ bFound = true;
+ nHitNo=nEntryNo-1;
+ }
+ else if (nDif * nLayoutSign >= 0 && nEntryNo < nSize)
+ nHitNo = nEntryNo;
+ ++nEntryNo;
+ }
+ while ( nScrPos * nLayoutSign < nEndPos * nLayoutSign && nDif * nLayoutSign > 0 );
+
+ rBorder = bFound;
+ return nHitNo;
+}
+
+bool ScHeaderControl::IsSelectionAllowed(SCCOLROW nPos) const
+{
+ ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+ if (!pViewSh)
+ return false;
+
+ ScViewData& rViewData = pViewSh->GetViewData();
+ sal_uInt16 nTab = rViewData.GetTabNo();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ bool bSelectAllowed = true;
+ if ( pProtect && pProtect->isProtected() )
+ {
+ // This sheet is protected. Check if a context menu is allowed on this cell.
+ bool bCellsProtected = false;
+ if (bVertical)
+ {
+ // row header
+ SCROW nRPos = static_cast<SCROW>(nPos);
+ bCellsProtected = rDoc.HasAttrib(0, nRPos, nTab, rDoc.MaxCol(), nRPos, nTab, HasAttrFlags::Protected);
+ }
+ else
+ {
+ // column header
+ SCCOL nCPos = static_cast<SCCOL>(nPos);
+ bCellsProtected = rDoc.HasAttrib(nCPos, 0, nTab, nCPos, rDoc.MaxRow(), nTab, HasAttrFlags::Protected);
+ }
+
+ bool bSelProtected = pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSelUnprotected = pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if (bCellsProtected)
+ bSelectAllowed = bSelProtected;
+ else
+ bSelectAllowed = bSelUnprotected;
+ }
+ return bSelectAllowed;
+}
+
+void ScHeaderControl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (IsDisabled())
+ return;
+
+ bIgnoreMove = false;
+ SelectWindow();
+
+ bool bIsBorder;
+ SCCOLROW nHitNo = GetMousePos(rMEvt.GetPosPixel(), bIsBorder);
+ if (!IsSelectionAllowed(nHitNo))
+ return;
+ if ( ! rMEvt.IsLeft() )
+ return;
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ if( !pTabView )
+ return;
+ SCTAB nTab = pTabView->GetViewData().GetTabNo();
+ if( !rMEvt.IsShift() )
+ pTabView->DoneRefMode( rMEvt.IsMod1() );
+ ScDocument& rDoc = pTabView->GetViewData().GetDocument();
+ if( !bVertical )
+ {
+ pTabView->InitRefMode( nHitNo, 0, nTab, SC_REFTYPE_REF );
+ pTabView->UpdateRef( nHitNo, rDoc.MaxRow(), nTab );
+ }
+ else
+ {
+ pTabView->InitRefMode( 0, nHitNo, nTab, SC_REFTYPE_REF );
+ pTabView->UpdateRef( rDoc.MaxCol(), nHitNo, nTab );
+ }
+ bInRefMode = true;
+ return;
+ }
+ if ( bIsBorder && ResizeAllowed() )
+ {
+ nDragNo = nHitNo;
+ sal_uInt16 nClicks = rMEvt.GetClicks();
+ if ( nClicks && nClicks%2==0 )
+ {
+ SetEntrySize( nDragNo, HDR_SIZE_OPTIMUM );
+ SetPointer( PointerStyle::Arrow );
+ }
+ else
+ {
+ if (bVertical)
+ nDragStart = rMEvt.GetPosPixel().Y();
+ else
+ nDragStart = rMEvt.GetPosPixel().X();
+ nDragPos = nDragStart;
+ // tdf#140833 launch help tip to show after the double click time has expired
+ // so under gtk the popover isn't active when the double click is processed
+ // by gtk because under load on wayland the double click is getting handled
+ // by something else and getting sent to the window underneath our window
+ aShowHelpTimer.Start();
+ DrawInvert( nDragPos );
+
+ StartTracking();
+ bDragging = true;
+ bDragMoved = false;
+ }
+ }
+ else
+ {
+ pSelEngine->SetWindow( this );
+ tools::Rectangle aVis( Point(), GetOutputSizePixel() );
+ if (bVertical)
+ {
+ aVis.SetLeft( LONG_MIN );
+ aVis.SetRight( LONG_MAX );
+ }
+ else
+ {
+ aVis.SetTop( LONG_MIN );
+ aVis.SetBottom( LONG_MAX );
+ }
+ pSelEngine->SetVisibleArea( aVis );
+
+ SetMarking( true ); // must precede SelMouseButtonDown
+ pSelEngine->SelMouseButtonDown( rMEvt );
+
+ // In column/row headers a simple click already is a selection.
+ // -> Call SelMouseMove to ensure CreateAnchor is called (and DestroyAnchor
+ // if the next click is somewhere else with Control key).
+ pSelEngine->SelMouseMove( rMEvt );
+
+ if (IsMouseCaptured())
+ {
+ // tracking instead of CaptureMouse, so it can be cancelled cleanly
+ //! someday SelectionEngine itself should call StartTracking!?!
+ ReleaseMouse();
+ StartTracking();
+ }
+ }
+}
+
+void ScHeaderControl::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( IsDisabled() )
+ return;
+
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ SC_MOD()->EndReference();
+ bInRefMode = false;
+ return;
+ }
+
+ SetMarking( false );
+ bIgnoreMove = false;
+
+ if ( bDragging )
+ {
+ DrawInvert( nDragPos );
+ ReleaseMouse();
+ HideDragHelp();
+ bDragging = false;
+
+ tools::Long nScrPos = GetScrPos( nDragNo );
+ tools::Long nMousePos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nNewWidth = bLayoutRTL ? ( nScrPos - nMousePos + 1 )
+ : ( nMousePos + 2 - nScrPos );
+
+ if ( nNewWidth < 0 /* && !IsSelected(nDragNo) */ )
+ {
+ SCCOLROW nStart = 0;
+ SCCOLROW nEnd = nDragNo;
+ while (nNewWidth < 0)
+ {
+ nStart = nDragNo;
+ if (nDragNo>0)
+ {
+ --nDragNo;
+ nNewWidth += GetEntrySize( nDragNo ); //! GetHiddenCount() ???
+ }
+ else
+ nNewWidth = 0;
+ }
+ HideEntries( nStart, nEnd );
+ }
+ else
+ {
+ if (bDragMoved)
+ SetEntrySize( nDragNo, static_cast<sal_uInt16>(nNewWidth) );
+ }
+ }
+ else
+ {
+ pSelEngine->SelMouseButtonUp( rMEvt );
+ ReleaseMouse();
+ }
+}
+
+void ScHeaderControl::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( IsDisabled() )
+ {
+ SetPointer( PointerStyle::Arrow );
+ return;
+ }
+
+ if ( bInRefMode && rMEvt.IsLeft() && SC_MOD()->IsFormulaMode() )
+ {
+ if( !pTabView )
+ return;
+ bool bTmp;
+ SCCOLROW nHitNo = GetMousePos(rMEvt.GetPosPixel(), bTmp);
+ SCTAB nTab = pTabView->GetViewData().GetTabNo();
+ ScDocument& rDoc = pTabView->GetViewData().GetDocument();
+ if( !bVertical )
+ pTabView->UpdateRef( nHitNo, rDoc.MaxRow(), nTab );
+ else
+ pTabView->UpdateRef( rDoc.MaxCol(), nHitNo, nTab );
+
+ return;
+ }
+
+ if ( bDragging )
+ {
+ tools::Long nNewPos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
+ if ( nNewPos != nDragPos )
+ {
+ DrawInvert( nDragPos );
+ nDragPos = nNewPos;
+ ShowDragHelp();
+ DrawInvert( nDragPos );
+
+ if (nDragPos <= nDragStart-SC_DRAG_MIN || nDragPos >= nDragStart+SC_DRAG_MIN)
+ bDragMoved = true;
+ }
+ }
+ else
+ {
+ bool bIsBorder;
+ (void)GetMousePos(rMEvt.GetPosPixel(), bIsBorder);
+
+ if ( bIsBorder && rMEvt.GetButtons()==0 && ResizeAllowed() )
+ SetPointer( bVertical ? PointerStyle::VSizeBar : PointerStyle::HSizeBar );
+ else
+ SetPointer( PointerStyle::Arrow );
+
+ if (!bIgnoreMove)
+ pSelEngine->SelMouseMove( rMEvt );
+ }
+}
+
+void ScHeaderControl::Tracking( const TrackingEvent& rTEvt )
+{
+ // Distribute the tracking events to the various MouseEvents, because
+ // SelectionEngine does not know anything about Tracking
+
+ if ( rTEvt.IsTrackingCanceled() )
+ StopMarking();
+ else if ( rTEvt.IsTrackingEnded() )
+ MouseButtonUp( rTEvt.GetMouseEvent() );
+ else
+ MouseMove( rTEvt.GetMouseEvent() );
+}
+
+void ScHeaderControl::Command( const CommandEvent& rCEvt )
+{
+ CommandEventId nCmd = rCEvt.GetCommand();
+ if ( nCmd == CommandEventId::ContextMenu )
+ {
+ StopMarking(); // finish selection / dragging
+
+ // execute popup menu
+
+ ScTabViewShell* pViewSh = dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() );
+ if ( pViewSh )
+ {
+ if ( rCEvt.IsMouseEvent() )
+ {
+ // #i18735# select the column/row under the mouse pointer
+ ScViewData& rViewData = pViewSh->GetViewData();
+
+ SelectWindow(); // also deselects drawing objects, stops draw text edit
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) )
+ SC_MOD()->InputEnterHandler(); // always end edit mode
+
+ bool bBorder;
+ SCCOLROW nPos = GetMousePos(rCEvt.GetMousePosPixel(), bBorder );
+ if (!IsSelectionAllowed(nPos))
+ // Selecting this cell is not allowed, neither is context menu.
+ return;
+
+ SCTAB nTab = rViewData.GetTabNo();
+ ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
+ ScRange aNewRange;
+ if ( bVertical )
+ aNewRange = ScRange( 0, sal::static_int_cast<SCROW>(nPos), nTab,
+ rDoc.MaxCol(), sal::static_int_cast<SCROW>(nPos), nTab );
+ else
+ aNewRange = ScRange( sal::static_int_cast<SCCOL>(nPos), 0, nTab,
+ sal::static_int_cast<SCCOL>(nPos), rDoc.MaxRow(), nTab );
+
+ // see if any part of the range is already selected
+ ScRangeList aRanges;
+ rViewData.GetMarkData().FillRangeListWithMarks( &aRanges, false );
+ bool bSelected = aRanges.Intersects(aNewRange);
+
+ // select the range if no part of it was selected
+ if ( !bSelected )
+ pViewSh->MarkRange( aNewRange );
+ }
+
+ pViewSh->GetDispatcher()->ExecutePopup( bVertical ? OUString( "rowheader" ) : OUString( "colheader" ) );
+ }
+ }
+ else if ( nCmd == CommandEventId::StartDrag )
+ {
+ pSelEngine->Command( rCEvt );
+ }
+}
+
+void ScHeaderControl::StopMarking()
+{
+ if ( bDragging )
+ {
+ DrawInvert( nDragPos );
+ HideDragHelp();
+ bDragging = false;
+ }
+
+ SetMarking( false );
+ bIgnoreMove = true;
+
+ // don't call pSelEngine->Reset, so selection across the parts of
+ // a split/frozen view is possible
+ if (IsMouseCaptured())
+ ReleaseMouse();
+}
+
+IMPL_LINK_NOARG(ScHeaderControl, ShowDragHelpHdl, Timer*, void)
+{
+ ShowDragHelp();
+}
+
+void ScHeaderControl::ShowDragHelp()
+{
+ aShowHelpTimer.Stop();
+ if (!Help::IsQuickHelpEnabled())
+ return;
+
+ tools::Long nScrPos = GetScrPos( nDragNo );
+ bool bLayoutRTL = IsLayoutRTL();
+ tools::Long nVal = bLayoutRTL ? ( nScrPos - nDragPos + 1 )
+ : ( nDragPos + 2 - nScrPos );
+
+ OUString aHelpStr = GetDragHelp( nVal );
+ Point aPos = OutputToScreenPixel( Point(0,0) );
+ Size aSize = GetSizePixel();
+
+ Point aMousePos = OutputToScreenPixel(GetPointerPosPixel());
+
+ tools::Rectangle aRect;
+ QuickHelpFlags nAlign;
+ if (!bVertical)
+ {
+ // above
+ aRect.SetLeft( aMousePos.X() );
+ aRect.SetTop( aPos.Y() - 4 );
+ nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
+ }
+ else
+ {
+ // top right
+ aRect.SetLeft( aPos.X() + aSize.Width() + 8 );
+ aRect.SetTop( aMousePos.Y() - 2 );
+ nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
+ }
+
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+
+ if (nTipVisible)
+ Help::HidePopover(this, nTipVisible);
+ nTipVisible = Help::ShowPopover(this, aRect, aHelpStr, nAlign);
+}
+
+void ScHeaderControl::HideDragHelp()
+{
+ aShowHelpTimer.Stop();
+ if (nTipVisible)
+ {
+ Help::HidePopover(this, nTipVisible);
+ nTipVisible = nullptr;
+ }
+}
+
+void ScHeaderControl::RequestHelp( const HelpEvent& rHEvt )
+{
+ // If the own QuickHelp is displayed, don't let RequestHelp remove it
+
+ bool bOwn = bDragging && Help::IsQuickHelpEnabled();
+ if (!bOwn)
+ Window::RequestHelp(rHEvt);
+}
+
+// dummies for virtual methods
+
+SCCOLROW ScHeaderControl::GetHiddenCount( SCCOLROW nEntryNo ) const
+{
+ SCCOLROW nHidden = 0;
+ while ( nEntryNo < nSize && GetEntrySize( nEntryNo ) == 0 )
+ {
+ ++nEntryNo;
+ ++nHidden;
+ }
+ return nHidden;
+}
+
+bool ScHeaderControl::IsLayoutRTL() const
+{
+ return false;
+}
+
+bool ScHeaderControl::IsMirrored() const
+{
+ return false;
+}
+
+bool ScHeaderControl::IsDisabled() const
+{
+ return false;
+}
+
+bool ScHeaderControl::ResizeAllowed() const
+{
+ return true;
+}
+
+void ScHeaderControl::SelectWindow()
+{
+}
+
+void ScHeaderControl::DrawInvert( tools::Long /* nDragPos */ )
+{
+}
+
+OUString ScHeaderControl::GetDragHelp( tools::Long /* nVal */ )
+{
+ return OUString();
+}
+
+void ScHeaderControl::SetMarking( bool /* bSet */ )
+{
+}
+
+void ScHeaderControl::GetMarkRange(SCCOLROW& rStart, SCCOLROW& rEnd) const
+{
+ rStart = nMarkStart;
+ rEnd = nMarkEnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/hintwin.cxx b/sc/source/ui/view/hintwin.cxx
new file mode 100644
index 0000000000..d57d8e0b78
--- /dev/null
+++ b/sc/source/ui/view/hintwin.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 <overlayobject.hxx>
+
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/lineend.hxx>
+#include <utility>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/metric.hxx>
+#include <sal/log.hxx>
+
+#define HINT_LINESPACE 2
+#define HINT_INDENT 3
+#define HINT_MARGIN 4
+
+ScOverlayHint::ScOverlayHint(OUString aTit, const OUString& rMsg, const Color& rColor, vcl::Font aFont)
+ : OverlayObject(rColor)
+ , m_aTitle(std::move(aTit))
+ , m_aMessage(convertLineEnd(rMsg, LINEEND_CR))
+ , m_aTextFont(std::move(aFont))
+ , m_aMapMode(MapUnit::MapPixel)
+ , m_nLeft(0)
+ , m_nTop(0)
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ScOverlayHint::createOverlaySequence(sal_Int32 nLeft, sal_Int32 nTop,
+ const MapMode &rMapMode,
+ basegfx::B2DRange &rRange) const
+{
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ MapMode aOld = pDefaultDev->GetMapMode();
+ pDefaultDev->SetMapMode(rMapMode);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color& rColor = rStyleSettings.GetLabelTextColor();
+ vcl::Font aTextFont = m_aTextFont;
+ aTextFont.SetFontSize(pDefaultDev->PixelToLogic(aTextFont.GetFontSize(), rMapMode));
+ vcl::Font aHeadFont = aTextFont;
+ aHeadFont.SetWeight(WEIGHT_BOLD);
+
+ // Create the text primitive for the title
+ basegfx::B2DVector aFontSize;
+ drawinglayer::attribute::FontAttribute aFontAttr =
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, aHeadFont, false, false);
+
+ FontMetric aFontMetric = pDefaultDev->GetFontMetric(aHeadFont);
+ Size aHintMargin = pDefaultDev->PixelToLogic(Size(HINT_MARGIN, HINT_MARGIN), rMapMode);
+ Size aIndent = pDefaultDev->PixelToLogic(Size(HINT_INDENT, HINT_LINESPACE), rMapMode);
+ double nTextOffsetY = nTop + aHintMargin.Height() + aFontMetric.GetAscent();
+ Point aTextPos(nLeft + aHintMargin.Width() , nTextOffsetY);
+ rRange = basegfx::B2DRange(nLeft, nTop, nLeft + aHintMargin.Width(), nTop + aHintMargin.Height());
+
+ basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ aTextPos.X(), aTextPos.Y()));
+
+ rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> pTitle =
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix, m_aTitle, 0, m_aTitle.getLength(),
+ std::vector<double>(), {}, std::move(aFontAttr), css::lang::Locale(),
+ rColor.getBColor());
+
+ Point aTextStart(nLeft + aHintMargin.Width() + aIndent.Width(),
+ nTop + aHintMargin.Height() + aFontMetric.GetLineHeight() + aIndent.Height());
+
+ drawinglayer::geometry::ViewInformation2D aDummy;
+ rRange.expand(pTitle->getB2DRange(aDummy));
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq { pTitle };
+
+ aFontMetric = pDefaultDev->GetFontMetric(aTextFont);
+ pDefaultDev->SetMapMode(aOld);
+
+ nTextOffsetY = aFontMetric.GetAscent();
+ sal_Int32 nLineHeight = aFontMetric.GetLineHeight();
+
+ aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, aTextFont, false, false);
+
+ sal_Int32 nIndex = 0;
+ Point aLineStart = aTextStart;
+ sal_Int32 nLineCount = 0;
+ while (nIndex != -1)
+ {
+ OUString aLine = m_aMessage.getToken( 0, '\r', nIndex );
+ if (aLine.getLength() > 255)
+ {
+ // prevent silliness higher up from hanging up the program
+ SAL_WARN("sc", "ridiculously long line, truncating, len=" << aLine.getLength());
+ aLine = aLine.copy(0,255);
+ }
+
+ aTextMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ aLineStart.X(), aLineStart.Y() + nTextOffsetY);
+
+ // Create the text primitive for each line of text
+ rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> pMessage =
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix, aLine, 0, aLine.getLength(),
+ std::vector<double>(), {}, aFontAttr, css::lang::Locale(),
+ rColor.getBColor());
+
+ rRange.expand(pMessage->getB2DRange(aDummy));
+
+ aSeq.push_back(pMessage);
+
+ aLineStart.AdjustY(nLineHeight );
+ nLineCount++;
+ if (nLineCount > 50)
+ {
+ // prevent silliness higher up from hanging up the program
+ SAL_WARN("sc", "ridiculously long message, bailing out");
+ break;
+ }
+ }
+
+ rRange.expand(basegfx::B2DTuple(rRange.getMaxX() + aHintMargin.Width(),
+ rRange.getMaxY() + aHintMargin.Height()));
+
+ basegfx::B2DPolygon aPoly(basegfx::utils::createPolygonFromRect(rRange));
+
+ const drawinglayer::primitive2d::Primitive2DReference aBg(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPoly), getBaseColor().getBColor()));
+
+ basegfx::BColor aBorderColor(0.5, 0.5, 0.5);
+ const drawinglayer::primitive2d::Primitive2DReference aBorder(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ std::move(aPoly), aBorderColor));
+
+ aSeq.insert(aSeq.begin(), aBorder);
+ aSeq.insert(aSeq.begin(), aBg);
+
+ return aSeq;
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ScOverlayHint::createOverlayObjectPrimitive2DSequence()
+{
+ basegfx::B2DRange aRange;
+ return createOverlaySequence(m_nLeft, m_nTop, m_aMapMode, aRange);
+}
+
+Size ScOverlayHint::GetSizePixel() const
+{
+ basegfx::B2DRange aRange;
+ createOverlaySequence(0, 0, MapMode(MapUnit::MapPixel), aRange);
+ return Size(aRange.getWidth(), aRange.getHeight());
+}
+
+void ScOverlayHint::SetPos(const Point& rPos, const MapMode& rMode)
+{
+ m_nLeft = rPos.X();
+ m_nTop = rPos.Y();
+ m_aMapMode = rMode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/imapwrap.cxx b/sc/source/ui/view/imapwrap.cxx
new file mode 100644
index 0000000000..72d1373393
--- /dev/null
+++ b/sc/source/ui/view/imapwrap.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 <svx/imapdlg.hxx>
+
+#include "imapwrap.hxx"
+
+sal_uInt16 ScIMapChildWindowId()
+{
+ return SvxIMapDlgChildWindow::GetChildWindowId();
+}
+
+void ScIMapDlgSet( const Graphic& rGraphic, const ImageMap* pImageMap,
+ const TargetList* pTargetList, void* pEditingObj )
+{
+ SvxIMapDlgChildWindow::UpdateIMapDlg( rGraphic, pImageMap, pTargetList, pEditingObj );
+}
+
+const void* ScIMapDlgGetObj( const SvxIMapDlg* pDlg )
+{
+ if ( pDlg )
+ return pDlg->GetEditingObject();
+ else
+ return nullptr;
+}
+
+const ImageMap& ScIMapDlgGetMap( const SvxIMapDlg* pDlg )
+{
+ return pDlg->GetImageMap();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/imapwrap.hxx b/sc/source/ui/view/imapwrap.hxx
new file mode 100644
index 0000000000..9b46b1bc83
--- /dev/null
+++ b/sc/source/ui/view/imapwrap.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>
+#include <sfx2/frame.hxx>
+
+class Graphic;
+class ImageMap;
+class SvxIMapDlg;
+
+sal_uInt16 ScIMapChildWindowId();
+
+ImageMap const & ScIMapDlgGetMap(const SvxIMapDlg * pDlg);
+
+void const * ScIMapDlgGetObj(const SvxIMapDlg * pDlg);
+
+void ScIMapDlgSet(
+ Graphic const & rGraphic, ImageMap const * pImageMap,
+ TargetList const * pTargetList, void * pEditingObj);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/invmerge.cxx b/sc/source/ui/view/invmerge.cxx
new file mode 100644
index 0000000000..a082221977
--- /dev/null
+++ b/sc/source/ui/view/invmerge.cxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+
+#include <invmerge.hxx>
+
+ScInvertMerger::ScInvertMerger( ::std::vector< tools::Rectangle >* pRectangles ) :
+ pRects( pRectangles )
+{
+ // collect rectangles instead of inverting
+}
+
+ScInvertMerger::~ScInvertMerger()
+{
+ Flush();
+}
+
+void ScInvertMerger::Flush()
+{
+ FlushLine();
+ FlushTotal();
+
+ OSL_ENSURE( aLineRect.IsEmpty() && aTotalRect.IsEmpty(), "Flush: not empty" );
+
+ if ( !pRects )
+ return;
+
+ // also join vertically if there are non-adjacent columns involved
+
+ size_t nComparePos = 0;
+ while ( nComparePos < pRects->size() )
+ {
+ tools::Rectangle aCompRect = (*pRects)[nComparePos];
+ sal_Int32 nBottom = aCompRect.Bottom();
+ size_t nOtherPos = nComparePos + 1;
+
+ while ( nOtherPos < pRects->size() )
+ {
+ tools::Rectangle aOtherRect = (*pRects)[nOtherPos];
+ if ( aOtherRect.Top() > nBottom + 1 )
+ {
+ // rectangles are sorted, so we can stop searching
+ break;
+ }
+ if ( aOtherRect.Top() == nBottom + 1 &&
+ aOtherRect.Left() == aCompRect.Left() &&
+ aOtherRect.Right() == aCompRect.Right() )
+ {
+ // extend first rectangle
+ nBottom = aOtherRect.Bottom();
+ aCompRect.SetBottom( nBottom );
+ (*pRects)[nComparePos].SetBottom( nBottom );
+
+ // remove second rectangle
+ pRects->erase( pRects->begin() + nOtherPos );
+
+ // continue at unmodified nOtherPos
+ }
+ else
+ ++nOtherPos;
+ }
+
+ ++nComparePos;
+ }
+}
+
+void ScInvertMerger::FlushTotal()
+{
+ if( aTotalRect.IsEmpty() )
+ return; // nothing to do
+
+ if ( pRects )
+ pRects->push_back( aTotalRect );
+
+ aTotalRect.SetEmpty();
+}
+
+void ScInvertMerger::FlushLine()
+{
+ if( aLineRect.IsEmpty() )
+ return; // nothing to do
+
+ if ( aTotalRect.IsEmpty() )
+ {
+ aTotalRect = aLineRect; // start new total rect
+ }
+ else
+ {
+ if ( aLineRect.Left() == aTotalRect.Left() &&
+ aLineRect.Right() == aTotalRect.Right() &&
+ aLineRect.Top() == aTotalRect.Bottom() + 1 )
+ {
+ // extend total rect
+ aTotalRect.SetBottom( aLineRect.Bottom() );
+ }
+ else
+ {
+ FlushTotal(); // draw old total rect
+ aTotalRect = aLineRect; // and start new one
+ }
+ }
+
+ aLineRect.SetEmpty();
+}
+
+void ScInvertMerger::AddRect( const tools::Rectangle& rRect )
+{
+ tools::Rectangle aJustified = rRect;
+ if ( rRect.Left() > rRect.Right() ) // switch for RTL layout
+ {
+ aJustified.SetLeft( rRect.Right() );
+ aJustified.SetRight( rRect.Left() );
+ }
+
+ if ( aLineRect.IsEmpty() )
+ {
+ aLineRect = aJustified; // start new line rect
+ }
+ else
+ {
+ bool bDone = false;
+ if ( aJustified.Top() == aLineRect.Top() &&
+ aJustified.Bottom() == aLineRect.Bottom() )
+ {
+ // try to extend line rect
+ if ( aJustified.Left() == aLineRect.Right() + 1 )
+ {
+ aLineRect.SetRight( aJustified.Right() );
+ bDone = true;
+ }
+ else if ( aJustified.Right() + 1 == aLineRect.Left() ) // for RTL layout
+ {
+ aLineRect.SetLeft( aJustified.Left() );
+ bDone = true;
+ }
+ }
+ if (!bDone)
+ {
+ FlushLine(); // use old line rect for total rect
+ aLineRect = aJustified; // and start new one
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/notemark.cxx b/sc/source/ui/view/notemark.cxx
new file mode 100644
index 0000000000..2824a453e7
--- /dev/null
+++ b/sc/source/ui/view/notemark.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 <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdocapt.hxx>
+#include <svl/itempool.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+
+#include <notemark.hxx>
+#include <document.hxx>
+#include <postit.hxx>
+
+#define SC_NOTEMARK_TIME 800
+#define SC_NOTEMARK_SHORT 70
+
+ScNoteMarker::ScNoteMarker( vcl::Window* pWin, vcl::Window* pRight, vcl::Window* pBottom, vcl::Window* pDiagonal,
+ ScDocument* pD, const ScAddress& aPos, OUString aUser,
+ const MapMode& rMap, bool bLeftEdge, bool bForce, bool bKeyboard) :
+ m_pWindow( pWin ),
+ m_pRightWin( pRight ),
+ m_pBottomWin( pBottom ),
+ m_pDiagWin( pDiagonal ),
+ m_pDoc( pD ),
+ m_aDocPos( aPos ),
+ m_aUserText(std::move( aUser )),
+ m_aTimer("ScNoteMarker m_aTimer"),
+ m_aMapMode( rMap ),
+ m_bLeft( bLeftEdge ),
+ m_bByKeyboard( bKeyboard ),
+ m_bVisible( false )
+{
+ Size aSizePixel = m_pWindow->GetOutputSizePixel();
+ if( m_pRightWin )
+ aSizePixel.AdjustWidth(m_pRightWin->GetOutputSizePixel().Width() );
+ if( m_pBottomWin )
+ aSizePixel.AdjustHeight(m_pBottomWin->GetOutputSizePixel().Height() );
+ tools::Rectangle aVisPixel( Point( 0, 0 ), aSizePixel );
+ m_aVisRect = m_pWindow->PixelToLogic( aVisPixel, m_aMapMode );
+
+ m_aTimer.SetInvokeHandler( LINK( this, ScNoteMarker, TimeHdl ) );
+ m_aTimer.SetTimeout( bForce ? SC_NOTEMARK_SHORT : SC_NOTEMARK_TIME );
+ m_aTimer.Start();
+}
+
+ScNoteMarker::~ScNoteMarker()
+{
+ m_xObject.clear();
+
+ InvalidateWin();
+
+ m_pModel.reset();
+}
+
+IMPL_LINK_NOARG(ScNoteMarker, TimeHdl, Timer *, void)
+{
+ if (!m_bVisible)
+ {
+ m_pModel.reset( new SdrModel() );
+ m_pModel->SetScaleUnit(MapUnit::Map100thMM);
+ SfxItemPool& rPool = m_pModel->GetItemPool();
+ rPool.SetDefaultMetric(MapUnit::Map100thMM);
+ rPool.FreezeIdRanges();
+
+ OutputDevice* pPrinter = m_pDoc->GetRefDevice();
+ if (pPrinter)
+ {
+ // On the outliner of the draw model also the printer is set as RefDevice,
+ // and it should look uniform.
+ Outliner& rOutliner = m_pModel->GetDrawOutliner();
+ rOutliner.SetRefDevice(pPrinter);
+ }
+
+ if( rtl::Reference<SdrPage> pPage = m_pModel->AllocPage( false ) )
+
+ {
+ m_xObject = ScNoteUtil::CreateTempCaption( *m_pDoc, m_aDocPos, *pPage, m_aUserText, m_aVisRect, m_bLeft );
+ if( m_xObject )
+ {
+ // Here, SyncForGrid and GetGridOffset was used with the comment:
+ // // Need to include grid offset: GetCurrentBoundRect is removing it
+ // // but we need to know actual rect position
+ // This is no longer true - SdrObject::RecalcBoundRect() uses the
+ // GetViewContact().getViewIndependentPrimitive2DContainer()) call
+ // that now by default adds the eventually needed GridOffset. Thus
+ // I have removed that adaptation stuff.
+ m_aRect = m_xObject->GetCurrentBoundRect();
+ }
+
+ // Insert page so that the model recognise it and also deleted
+ m_pModel->InsertPage( pPage.get() );
+
+ }
+ m_bVisible = true;
+ }
+
+ Draw();
+}
+
+static void lcl_DrawWin( const SdrObject* pObject, vcl::RenderContext* pWindow, const MapMode& rMap )
+{
+ MapMode aOld = pWindow->GetMapMode();
+ pWindow->SetMapMode( rMap );
+
+ DrawModeFlags nOldDrawMode = pWindow->GetDrawMode();
+ if ( Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pWindow->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill |
+ DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+ }
+
+ pObject->SingleObjectPainter( *pWindow ); // #110094#-17
+
+ pWindow->SetDrawMode( nOldDrawMode );
+ pWindow->SetMapMode( aOld );
+}
+
+static MapMode lcl_MoveMapMode( const MapMode& rMap, const Size& rMove )
+{
+ MapMode aNew = rMap;
+ Point aOrigin = aNew.GetOrigin();
+ aOrigin.AdjustX( -(rMove.Width()) );
+ aOrigin.AdjustY( -rMove.Height() );
+ aNew.SetOrigin(aOrigin);
+ return aNew;
+}
+
+void ScNoteMarker::Draw()
+{
+ if ( !(m_xObject && m_bVisible) )
+ return;
+
+ lcl_DrawWin( m_xObject.get(), m_pWindow->GetOutDev(), m_aMapMode );
+
+ if ( m_pRightWin || m_pBottomWin )
+ {
+ Size aWinSize = m_pWindow->PixelToLogic( m_pWindow->GetOutputSizePixel(), m_aMapMode );
+ if ( m_pRightWin )
+ lcl_DrawWin( m_xObject.get(), m_pRightWin->GetOutDev(),
+ lcl_MoveMapMode( m_aMapMode, Size( aWinSize.Width(), 0 ) ) );
+ if ( m_pBottomWin )
+ lcl_DrawWin( m_xObject.get(), m_pBottomWin->GetOutDev(),
+ lcl_MoveMapMode( m_aMapMode, Size( 0, aWinSize.Height() ) ) );
+ if ( m_pDiagWin )
+ lcl_DrawWin( m_xObject.get(), m_pDiagWin->GetOutDev(), lcl_MoveMapMode( m_aMapMode, aWinSize ) );
+ }
+}
+
+void ScNoteMarker::InvalidateWin()
+{
+ if (!m_bVisible)
+ return;
+
+ // Extend the invalidated rectangle by 1 pixel in each direction in case AA would slightly
+ // paint outside the nominal area.
+ tools::Rectangle aRect(m_aRect);
+ const Size aPixelSize = m_pWindow->PixelToLogic(Size(1, 1));
+ aRect.AdjustLeft(-aPixelSize.getWidth());
+ aRect.AdjustTop(-aPixelSize.getHeight());
+ aRect.AdjustRight(aPixelSize.getWidth());
+ aRect.AdjustBottom(aPixelSize.getHeight());
+
+ m_pWindow->Invalidate( OutputDevice::LogicToLogic(aRect, m_aMapMode, m_pWindow->GetMapMode()) );
+
+ if ( !(m_pRightWin || m_pBottomWin) )
+ return;
+
+ Size aWinSize = m_pWindow->PixelToLogic( m_pWindow->GetOutputSizePixel(), m_aMapMode );
+ if ( m_pRightWin )
+ m_pRightWin->Invalidate( OutputDevice::LogicToLogic(aRect,
+ lcl_MoveMapMode( m_aMapMode, Size( aWinSize.Width(), 0 ) ),
+ m_pRightWin->GetMapMode()) );
+ if ( m_pBottomWin )
+ m_pBottomWin->Invalidate( OutputDevice::LogicToLogic(aRect,
+ lcl_MoveMapMode( m_aMapMode, Size( 0, aWinSize.Height() ) ),
+ m_pBottomWin->GetMapMode()) );
+ if ( m_pDiagWin )
+ m_pDiagWin->Invalidate( OutputDevice::LogicToLogic(aRect,
+ lcl_MoveMapMode( m_aMapMode, aWinSize ),
+ m_pDiagWin->GetMapMode()) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/olinewin.cxx b/sc/source/ui/view/olinewin.cxx
new file mode 100644
index 0000000000..1cc55a9f3f
--- /dev/null
+++ b/sc/source/ui/view/olinewin.cxx
@@ -0,0 +1,1040 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <vcl/image.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/syswin.hxx>
+#include <osl/diagnose.h>
+
+#include <olinewin.hxx>
+#include <olinetab.hxx>
+#include <document.hxx>
+#include <dbfunc.hxx>
+#include <bitmaps.hlst>
+
+const tools::Long SC_OL_BITMAPSIZE = 12;
+const tools::Long SC_OL_POSOFFSET = 2;
+
+const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 );
+const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 );
+
+ScOutlineWindow::ScOutlineWindow( vcl::Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
+ Window( pParent ),
+ mrViewData( *pViewData ),
+ meWhich( eWhich ),
+ mbHoriz( eMode == SC_OUTLINE_HOR ),
+ mbMirrorEntries( false ), // updated in SetHeaderSize
+ mbMirrorLevels( false ), // updated in SetHeaderSize
+ maLineColor( COL_BLACK ),
+ mnHeaderSize( 0 ),
+ mnHeaderPos( 0 ),
+ mnMainFirstPos( 0 ),
+ mnMainLastPos( 0 ),
+ mbMTActive( false ),
+ mbMTPressed( false ),
+ mnFocusLevel( 0 ),
+ mnFocusEntry( SC_OL_HEADERENTRY ),
+ mbDontDrawFocus( false )
+{
+ EnableRTL( false ); // mirroring is done manually
+
+ InitSettings();
+ maFocusRect.SetEmpty();
+ SetHeaderSize( 0 );
+
+ // insert the window into task pane list for "F6 cycling"
+ if( SystemWindow* pSysWin = GetSystemWindow() )
+ if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
+ pTaskPaneList->AddWindow( this );
+}
+
+ScOutlineWindow::~ScOutlineWindow()
+{
+ disposeOnce();
+}
+
+void ScOutlineWindow::dispose()
+{
+ // remove the window from task pane list
+ if( SystemWindow* pSysWin = GetSystemWindow() )
+ if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
+ pTaskPaneList->RemoveWindow( this );
+ vcl::Window::dispose();
+}
+
+void ScOutlineWindow::SetHeaderSize( tools::Long nNewSize )
+{
+ bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
+ mbMirrorEntries = bLayoutRTL && mbHoriz;
+ mbMirrorLevels = bLayoutRTL && !mbHoriz;
+
+ bool bNew = (nNewSize != mnHeaderSize);
+ mnHeaderSize = nNewSize;
+ mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
+ mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
+ mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
+ if ( bNew )
+ Invalidate();
+}
+
+tools::Long ScOutlineWindow::GetDepthSize() const
+{
+ tools::Long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
+ if ( nSize > 0 )
+ nSize += 2 * SC_OL_POSOFFSET + 1;
+ return nSize;
+}
+
+void ScOutlineWindow::ScrollPixel( tools::Long nDiff )
+{
+ HideFocus();
+ mbDontDrawFocus = true;
+
+ tools::Long nStart = mnMainFirstPos;
+ tools::Long nEnd = mnMainLastPos;
+
+ tools::Long nInvStart, nInvEnd;
+ if (nDiff < 0)
+ {
+ nStart -= nDiff;
+ nInvStart = nEnd + nDiff;
+ nInvEnd = nEnd;
+ }
+ else
+ {
+ nEnd -= nDiff;
+ nInvStart = nStart;
+ nInvEnd = nStart + nDiff;
+ }
+
+ ScrollRel( nDiff, nStart, nEnd );
+ Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
+
+ // if focus becomes invisible, move it to next visible button
+ ImplMoveFocusToVisible( nDiff < 0 );
+
+ mbDontDrawFocus = false;
+ ShowFocus();
+}
+
+void ScOutlineWindow::ScrollRel( tools::Long nEntryDiff, tools::Long nEntryStart, tools::Long nEntryEnd )
+{
+ tools::Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
+ if ( mbHoriz )
+ Scroll( nEntryDiff, 0, aRect );
+ else
+ Scroll( 0, nEntryDiff, aRect );
+}
+
+// internal -------------------------------------------------------------------
+
+void ScOutlineWindow::InitSettings()
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ SetBackground( rStyleSettings.GetFaceColor() );
+ maLineColor = rStyleSettings.GetButtonTextColor();
+ Invalidate();
+}
+
+const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
+{
+ const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
+ if ( !pTable ) return nullptr;
+ return mbHoriz ? &pTable->GetColArray() : &pTable->GetRowArray();
+}
+
+const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : nullptr;
+}
+
+bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
+{
+ return mbHoriz ?
+ GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
+ GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
+}
+
+bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
+{
+ // columns cannot be filtered
+ return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
+}
+
+bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
+{
+ bool bAllHidden = true;
+ for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
+ bAllHidden = IsHidden( nPos );
+ return bAllHidden;
+}
+
+void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
+{
+ if ( mbHoriz )
+ {
+ rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
+ rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
+ }
+ else
+ {
+ rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
+ rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
+ }
+
+ // include collapsed columns/rows in front of visible range
+ while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
+ --rnColRowStart;
+}
+
+Point ScOutlineWindow::GetPoint( tools::Long nLevelPos, tools::Long nEntryPos ) const
+{
+ return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
+}
+
+tools::Rectangle ScOutlineWindow::GetRectangle(
+ tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd ) const
+{
+ return tools::Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
+}
+
+tools::Long ScOutlineWindow::GetOutputSizeLevel() const
+{
+ Size aSize( GetOutputSizePixel() );
+ return mbHoriz ? aSize.Height() : aSize.Width();
+}
+
+tools::Long ScOutlineWindow::GetOutputSizeEntry() const
+{
+ Size aSize( GetOutputSizePixel() );
+ return mbHoriz ? aSize.Width() : aSize.Height();
+}
+
+size_t ScOutlineWindow::GetLevelCount() const
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
+ return nLevelCount ? (nLevelCount + 1) : 0;
+}
+
+tools::Long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
+{
+ // #i51970# must always return the *left* edge of the area used by a level
+ tools::Long nPos = static_cast< tools::Long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
+ return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
+}
+
+size_t ScOutlineWindow::GetLevelFromPos( tools::Long nLevelPos ) const
+{
+ if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
+ tools::Long nStart = SC_OL_POSOFFSET;
+ if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
+ size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
+ return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
+}
+
+tools::Long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
+{
+ tools::Long nDocPos = mbHoriz ?
+ mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, true ).X() :
+ mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, true ).Y();
+ return mnMainFirstPos + nDocPos;
+}
+
+tools::Long ScOutlineWindow::GetHeaderEntryPos() const
+{
+ return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
+}
+
+bool ScOutlineWindow::GetEntryPos(
+ size_t nLevel, size_t nEntry,
+ tools::Long& rnStartPos, tools::Long& rnEndPos, tools::Long& rnImagePos ) const
+{
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( !pEntry || !pEntry->IsVisible() )
+ return false;
+
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ tools::Long nEntriesSign = mbMirrorEntries ? -1 : 1;
+
+ // --- common calculation ---
+
+ rnStartPos = GetColRowPos( nStart );
+ rnEndPos = GetColRowPos( nEnd + 1 );
+
+ bool bHidden = IsHidden( nStart );
+ rnImagePos = bHidden ?
+ (rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
+ rnStartPos + nEntriesSign;
+ tools::Long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
+ ( mbMirrorEntries ? 1 : 0 )) / 2;
+ rnImagePos = mbMirrorEntries ? std::max( rnImagePos, nCenter ) : std::min( rnImagePos, nCenter );
+
+ // --- refinements ---
+
+ // do not cut leftmost/topmost image
+ if ( bHidden && IsFirstVisible( nStart ) )
+ rnImagePos = rnStartPos;
+
+ // do not cover previous collapsed image
+ bool bDoNoCover = !bHidden && nEntry;
+ const ScOutlineEntry* pPrevEntry = bDoNoCover ? GetOutlineEntry(nLevel, nEntry - 1) : nullptr;
+ if (pPrevEntry)
+ {
+ SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
+ if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
+ {
+ if ( IsFirstVisible( pPrevEntry->GetStart() ) )
+ rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
+ else
+ rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
+ rnImagePos = rnStartPos;
+ }
+ }
+
+ // restrict rnStartPos...rnEndPos to valid area
+ rnStartPos = std::max( rnStartPos, mnMainFirstPos );
+ rnEndPos = std::max( rnEndPos, mnMainFirstPos );
+
+ if ( mbMirrorEntries )
+ rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap
+
+ // --- all rows filtered? ---
+
+ bool bVisible = true;
+ if ( !mbHoriz )
+ {
+ bVisible = false;
+ for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
+ bVisible = !IsFiltered( nRow );
+ }
+ return bVisible;
+}
+
+bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
+{
+ bool bRet = nLevel < GetLevelCount();
+ if ( bRet )
+ {
+ tools::Long nLevelPos = GetLevelPos( nLevel );
+ if ( nEntry == SC_OL_HEADERENTRY )
+ rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
+ else
+ {
+ tools::Long nStartPos, nEndPos, nImagePos;
+ bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
+ rPos = GetPoint( nLevelPos, nImagePos );
+ }
+ }
+ return bRet;
+}
+
+bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
+{
+ bool bRet = false;
+ if ( nEntry == SC_OL_HEADERENTRY )
+ bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
+ else
+ {
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry && pEntry->IsVisible() )
+ {
+ SCCOLROW nStart, nEnd;
+ GetVisibleRange( nStart, nEnd );
+ bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
+ }
+ }
+ return bRet;
+}
+
+bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray ) return false;
+
+ SCCOLROW nStartIndex, nEndIndex;
+ GetVisibleRange( nStartIndex, nEndIndex );
+
+ size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
+ if ( nLevel == SC_OL_NOLEVEL )
+ return false;
+
+ tools::Long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
+
+ // --- level buttons ---
+
+ if ( mnHeaderSize > 0 )
+ {
+ tools::Long nImagePos = GetHeaderEntryPos();
+ if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
+ {
+ rnLevel = nLevel;
+ rnEntry = SC_OL_HEADERENTRY;
+ rbButton = true;
+ return true;
+ }
+ }
+
+ // --- expand/collapse buttons and expanded lines ---
+
+ // search outline entries backwards
+ size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
+ while ( nEntry )
+ {
+ --nEntry;
+
+ const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
+ sal::static_int_cast<sal_uInt16>(nEntry) );
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
+ {
+ tools::Long nStartPos, nEndPos, nImagePos;
+ if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
+ {
+ rnLevel = nLevel;
+ rnEntry = nEntry;
+
+ // button?
+ if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
+ {
+ rbButton = true;
+ return true;
+ }
+
+ // line?
+ if ( mbMirrorEntries )
+ ::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value
+ if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
+ {
+ rbButton = false;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
+{
+ bool bButton;
+ bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
+ return bRet && bButton;
+}
+
+bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
+{
+ bool bButton;
+ bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
+ return bRet && !bButton;
+}
+
+void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
+{
+ ScDBFunc& rFunc = *mrViewData.GetView();
+ if ( nEntry == SC_OL_HEADERENTRY )
+ rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
+ else
+ {
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry )
+ {
+ if ( pEntry->IsHidden() )
+ rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ else
+ rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
+ }
+ }
+}
+
+void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
+{
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry && pEntry->IsHidden() )
+ DoFunction( nLevel, nEntry );
+}
+
+void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
+{
+ const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
+ if ( pEntry && !pEntry->IsHidden() )
+ DoFunction( nLevel, nEntry );
+}
+
+void ScOutlineWindow::Resize()
+{
+ Window::Resize();
+ SetHeaderSize( mnHeaderSize ); // recalculates header/group positions
+ if ( !IsFocusButtonVisible() )
+ {
+ HideFocus();
+ ShowFocus(); // calculates valid position
+ }
+}
+
+void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ InitSettings();
+ Invalidate();
+ }
+ Window::DataChanged( rDCEvt );
+}
+
+// drawing --------------------------------------------------------------------
+
+void ScOutlineWindow::SetEntryAreaClipRegion()
+{
+ GetOutDev()->SetClipRegion( vcl::Region(tools::Rectangle(
+ GetPoint( 0, mnMainFirstPos ),
+ GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ))));
+}
+
+void ScOutlineWindow::DrawLineRel(
+ tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd )
+{
+ GetOutDev()->DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
+}
+
+void ScOutlineWindow::DrawRectRel(
+ tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd )
+{
+ GetOutDev()->DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
+}
+
+namespace
+{
+ Image GetImage(const OUString& rId)
+ {
+ return Image(StockImage::Yes, rId);
+ }
+}
+
+void ScOutlineWindow::DrawImageRel(tools::Long nLevelPos, tools::Long nEntryPos, const OUString& rId)
+{
+ const Image& rImage = GetImage(rId);
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( GetBackground().GetColor() );
+ Point aPos( GetPoint( nLevelPos, nEntryPos ) );
+ GetOutDev()->DrawRect( tools::Rectangle( aPos, rImage.GetSizePixel() ) );
+ GetOutDev()->DrawImage( aPos, rImage );
+}
+
+void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
+{
+ Point aPos;
+ if ( GetImagePos( nLevel, nEntry, aPos ) )
+ {
+ OUString sId = bPressed ? RID_BMP_PRESSED : RID_BMP_NOTPRESSED;
+ bool bClip = (nEntry != SC_OL_HEADERENTRY);
+ if ( bClip )
+ SetEntryAreaClipRegion();
+ GetOutDev()->DrawImage(aPos, GetImage(sId));
+ if ( bClip )
+ GetOutDev()->SetClipRegion();
+ }
+ mbMTPressed = bPressed;
+}
+
+void ScOutlineWindow::ShowFocus()
+{
+ if ( !HasFocus() )
+ return;
+
+ // first move to a visible position
+ ImplMoveFocusToVisible( true );
+
+ if ( !IsFocusButtonVisible() )
+ return;
+
+ Point aPos;
+ if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
+ {
+ aPos += Point( 1, 1 );
+ maFocusRect = tools::Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
+ bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
+ if ( bClip )
+ SetEntryAreaClipRegion();
+ InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
+ if ( bClip )
+ GetOutDev()->SetClipRegion();
+ }
+}
+
+void ScOutlineWindow::HideFocus()
+{
+ if ( !maFocusRect.IsEmpty() )
+ {
+ bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
+ if ( bClip )
+ SetEntryAreaClipRegion();
+ InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
+ if ( bClip )
+ GetOutDev()->SetClipRegion();
+ maFocusRect.SetEmpty();
+ }
+}
+
+constexpr OUString aLevelBmps[]=
+{
+ RID_BMP_LEVEL1,
+ RID_BMP_LEVEL2,
+ RID_BMP_LEVEL3,
+ RID_BMP_LEVEL4,
+ RID_BMP_LEVEL5,
+ RID_BMP_LEVEL6,
+ RID_BMP_LEVEL7,
+ RID_BMP_LEVEL8
+};
+
+void ScOutlineWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /* rRect */ )
+{
+ tools::Long nEntriesSign = mbMirrorEntries ? -1 : 1;
+ tools::Long nLevelsSign = mbMirrorLevels ? -1 : 1;
+
+ Size aSize = GetOutputSizePixel();
+ tools::Long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
+ tools::Long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
+
+ GetOutDev()->SetLineColor( maLineColor );
+ tools::Long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
+ DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
+
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray ) return;
+
+ size_t nLevelCount = GetLevelCount();
+
+ // --- draw header images ---
+
+ if ( mnHeaderSize > 0 )
+ {
+ tools::Long nEntryPos = GetHeaderEntryPos();
+ for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
+ DrawImageRel(GetLevelPos(nLevel), nEntryPos, aLevelBmps[nLevel]);
+
+ GetOutDev()->SetLineColor( maLineColor );
+ tools::Long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
+ DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
+ }
+
+ // --- draw lines & collapse/expand images ---
+
+ SetEntryAreaClipRegion();
+
+ SCCOLROW nStartIndex, nEndIndex;
+ GetVisibleRange( nStartIndex, nEndIndex );
+
+ for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
+ {
+ tools::Long nLevelPos = GetLevelPos( nLevel );
+ tools::Long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
+
+ size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
+ size_t nEntry;
+
+ // first draw all lines in the current level
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( maLineColor );
+ for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
+ {
+ const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
+ sal::static_int_cast<sal_uInt16>(nEntry) );
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+
+ // visible range?
+ bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
+ // find output coordinates
+ if ( bDraw )
+ bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
+ // draw, if not collapsed
+ if ( bDraw && !pEntry->IsHidden() )
+ {
+ if ( nStart >= nStartIndex )
+ nEntryPos1 += nEntriesSign;
+ nEntryPos2 -= 2 * nEntriesSign;
+ tools::Long nLinePos = nLevelPos;
+ if ( mbMirrorLevels )
+ nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap
+ DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
+
+ if ( nEnd <= nEndIndex )
+ DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
+ nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
+ }
+ }
+
+ // draw all images in the level from last to first
+ nEntry = nEntryCount;
+ while ( nEntry )
+ {
+ --nEntry;
+
+ const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
+ sal::static_int_cast<sal_uInt16>(nEntry) );
+ SCCOLROW nStart = pEntry->GetStart();
+
+ // visible range?
+ bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
+ // find output coordinates
+ if ( bDraw )
+ bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
+ // draw, if not hidden by higher levels
+ if ( bDraw )
+ {
+ OUString sImageId = pEntry->IsHidden() ? RID_BMP_PLUS : RID_BMP_MINUS;
+ DrawImageRel(nLevelPos, nImagePos, sImageId);
+ }
+ }
+ }
+
+ GetOutDev()->SetClipRegion();
+
+ if ( !mbDontDrawFocus )
+ ShowFocus();
+}
+
+// focus ----------------------------------------------------------------------
+
+/** Increments or decrements a value and wraps at the specified limits.
+ @return true = value wrapped. */
+static bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
+{
+ OSL_ENSURE( nMin <= nMax, "lcl_RotateValue - invalid range" );
+ OSL_ENSURE( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
+ bool bWrap = false;
+ if ( bForward )
+ {
+ if ( rnValue < nMax )
+ ++rnValue;
+ else
+ {
+ rnValue = nMin;
+ bWrap = true;
+ }
+ }
+ else
+ {
+ if ( rnValue > nMin )
+ --rnValue;
+ else
+ {
+ rnValue = nMax;
+ bWrap = true;
+ }
+ }
+ return bWrap;
+}
+
+bool ScOutlineWindow::IsFocusButtonVisible() const
+{
+ return IsButtonVisible( mnFocusLevel, mnFocusEntry );
+}
+
+bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray )
+ return false;
+
+ bool bWrapped = false;
+ size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) );
+ // #i29530# entry count may be decreased after changing active sheet
+ if( mnFocusEntry >= nEntryCount )
+ mnFocusEntry = SC_OL_HEADERENTRY;
+ size_t nOldEntry = mnFocusEntry;
+
+ do
+ {
+ if ( mnFocusEntry == SC_OL_HEADERENTRY )
+ {
+ // move from header to first or last entry
+ if ( nEntryCount > 0 )
+ mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
+ /* wrapped, if forward from right header to first entry,
+ or if backward from left header to last entry */
+ // Header and entries are now always in consistent order,
+ // so there's no need to check for mirroring here.
+ if ( !nEntryCount || !bForward )
+ bWrapped = true;
+ }
+ else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
+ {
+ // lcl_RotateValue returns true -> wrapped the entry range -> move to header
+ mnFocusEntry = SC_OL_HEADERENTRY;
+ /* wrapped, if forward from last entry to left header,
+ or if backward from first entry to right header */
+ if ( bForward )
+ bWrapped = true;
+ }
+ }
+ while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
+
+ return bWrapped;
+}
+
+bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
+{
+ const ScOutlineArray* pArray = GetOutlineArray();
+ if ( !pArray )
+ return false;
+
+ bool bWrapped = false;
+ size_t nLevelCount = GetLevelCount();
+
+ if ( mnFocusEntry == SC_OL_HEADERENTRY )
+ {
+ if ( nLevelCount > 0 )
+ bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
+ }
+ else
+ {
+ const ScOutlineEntry* pEntry = pArray->GetEntry(
+ mnFocusLevel, mnFocusEntry);
+
+ if ( pEntry )
+ {
+ SCCOLROW nStart = pEntry->GetStart();
+ SCCOLROW nEnd = pEntry->GetEnd();
+ size_t nNewLevel = mnFocusLevel;
+ size_t nNewEntry = 0;
+
+ bool bFound = false;
+ if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
+ {
+ // next level -> find first child entry
+ nNewLevel = mnFocusLevel + 1;
+ bFound = pArray->GetEntryIndexInRange(nNewLevel, nStart, nEnd, nNewEntry);
+ }
+ else if ( !bForward && (mnFocusLevel > 0) )
+ {
+ // previous level -> find parent entry
+ nNewLevel = mnFocusLevel - 1;
+ bFound = pArray->GetEntryIndex(nNewLevel, nStart, nNewEntry);
+ }
+
+ if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
+ {
+ mnFocusLevel = nNewLevel;
+ mnFocusEntry = nNewEntry;
+ }
+ }
+ }
+
+ return bWrapped;
+}
+
+bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward )
+{
+ bool bRet = false;
+ size_t nOldLevel = mnFocusLevel;
+ size_t nOldEntry = mnFocusEntry;
+
+ do
+ {
+ /* one level up, if backward from left header,
+ or one level down, if forward from right header */
+ if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
+ bRet |= ImplMoveFocusByLevel( bForward );
+ // move to next/previous entry
+ bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
+ bRet |= bWrapInLevel;
+ /* one level up, if wrapped backward to right header,
+ or one level down, if wrapped forward to right header */
+ if ( bForward && bWrapInLevel )
+ bRet |= ImplMoveFocusByLevel( bForward );
+ }
+ while ( !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
+
+ return bRet;
+}
+
+void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
+{
+ // first try to find an entry in the same level
+ if ( !IsFocusButtonVisible() )
+ ImplMoveFocusByEntry( bForward, true );
+ // then try to find any other entry
+ if ( !IsFocusButtonVisible() )
+ ImplMoveFocusByTabOrder( bForward );
+}
+
+void ScOutlineWindow::MoveFocusByEntry( bool bForward )
+{
+ HideFocus();
+ ImplMoveFocusByEntry( bForward, true );
+ ShowFocus();
+}
+
+void ScOutlineWindow::MoveFocusByLevel( bool bForward )
+{
+ HideFocus();
+ ImplMoveFocusByLevel( bForward );
+ ShowFocus();
+}
+
+void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
+{
+ HideFocus();
+ ImplMoveFocusByTabOrder( bForward );
+ ShowFocus();
+}
+
+void ScOutlineWindow::GetFocus()
+{
+ Window::GetFocus();
+ ShowFocus();
+}
+
+void ScOutlineWindow::LoseFocus()
+{
+ HideFocus();
+ Window::LoseFocus();
+}
+
+// mouse ----------------------------------------------------------------------
+
+void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
+{
+ mbMTActive = true;
+ mnMTLevel = nLevel;
+ mnMTEntry = nEntry;
+ DrawBorderRel( nLevel, nEntry, true );
+}
+
+void ScOutlineWindow::EndMouseTracking()
+{
+ if ( mbMTPressed )
+ DrawBorderRel( mnMTLevel, mnMTEntry, false );
+ mbMTActive = false;
+}
+
+void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( IsMouseTracking() )
+ {
+ size_t nLevel, nEntry;
+ bool bHit = false;
+
+ if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
+ bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
+
+ if ( bHit != mbMTPressed )
+ DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
+ }
+}
+
+void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( IsMouseTracking() )
+ {
+ EndMouseTracking();
+
+ size_t nLevel, nEntry;
+ if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
+ if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
+ DoFunction( nLevel, nEntry );
+ }
+}
+
+void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ size_t nLevel, nEntry;
+ bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
+ if ( bHit )
+ StartMouseTracking( nLevel, nEntry );
+ else if ( rMEvt.GetClicks() == 2 )
+ {
+ bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
+ if ( bHit )
+ DoFunction( nLevel, nEntry );
+ }
+
+ // if an item has been hit and window is focused, move focus to this item
+ if ( bHit && HasFocus() )
+ {
+ HideFocus();
+ mnFocusLevel = nLevel;
+ mnFocusEntry = nEntry;
+ ShowFocus();
+ }
+}
+
+// keyboard -------------------------------------------------------------------
+
+void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ bool bNoMod = !rKCode.GetModifier();
+ bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
+ bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
+
+ sal_uInt16 nCode = rKCode.GetCode();
+ bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
+ bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
+
+ // TAB key
+ if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
+ // move forward without SHIFT key
+ MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring
+
+ // LEFT/RIGHT/UP/DOWN keys
+ else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
+ {
+ bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
+ if ( mbHoriz == bLeftRightKey )
+ // move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
+ MoveFocusByEntry( bForward != mbMirrorEntries );
+ else
+ // move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
+ MoveFocusByLevel( bForward != mbMirrorLevels );
+ }
+
+ // CTRL + number
+ else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
+ {
+ size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
+ if ( nLevel < GetLevelCount() )
+ DoFunction( nLevel, SC_OL_HEADERENTRY );
+ }
+
+ // other key codes
+ else switch ( rKCode.GetFullCode() )
+ {
+ case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break;
+ case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break;
+ case KEY_SPACE:
+ case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break;
+ default: Window::KeyInput( rKEvt );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/output.cxx b/sc/source/ui/view/output.cxx
new file mode 100644
index 0000000000..9d0fe14305
--- /dev/null
+++ b/sc/source/ui/view/output.cxx
@@ -0,0 +1,2773 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/svxfont.hxx>
+#include <tools/poly.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svx/framelinkarray.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/settings.hxx>
+#include <svx/unoapi.hxx>
+#include <sal/log.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+
+#include <output.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <formulacell.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <progress.hxx>
+#include <pagedata.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <viewutil.hxx>
+#include <gridmerg.hxx>
+#include <fillinfo.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <postit.hxx>
+#include <validat.hxx>
+#include <detfunc.hxx>
+#include <editutil.hxx>
+
+#include <SparklineRenderer.hxx>
+#include <colorscale.hxx>
+
+#include <math.h>
+#include <memory>
+
+using namespace com::sun::star;
+
+// Static Data
+
+// color for ChangeTracking "by author" as in the writer (swmodul1.cxx)
+
+#define SC_AUTHORCOLORCOUNT 9
+
+const Color nAuthorColor[ SC_AUTHORCOLORCOUNT ] = {
+ COL_LIGHTRED, COL_LIGHTBLUE, COL_LIGHTMAGENTA,
+ COL_GREEN, COL_RED, COL_BLUE,
+ COL_BROWN, COL_MAGENTA, COL_CYAN };
+
+// Helper class for color assignment to avoid repeated lookups for the same user
+
+ScActionColorChanger::ScActionColorChanger( const ScChangeTrack& rTrack ) :
+ rOpt( SC_MOD()->GetAppOptions() ),
+ rUsers( rTrack.GetUserCollection() ),
+ nLastUserIndex( 0 ),
+ nColor( COL_BLACK )
+{
+}
+
+void ScActionColorChanger::Update( const ScChangeAction& rAction )
+{
+ Color nSetColor;
+ switch (rAction.GetType())
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ nSetColor = rOpt.GetTrackInsertColor();
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ nSetColor = rOpt.GetTrackDeleteColor();
+ break;
+ case SC_CAT_MOVE:
+ nSetColor = rOpt.GetTrackMoveColor();
+ break;
+ default:
+ nSetColor = rOpt.GetTrackContentColor();
+ break;
+ }
+ if ( nSetColor != COL_TRANSPARENT ) // color assigned
+ nColor = nSetColor;
+ else // by author
+ {
+ if (aLastUserName != rAction.GetUser())
+ {
+ aLastUserName = rAction.GetUser();
+ std::set<OUString>::const_iterator it = rUsers.find(aLastUserName);
+ if (it == rUsers.end())
+ {
+ // empty string is possible if a name wasn't found while saving a 5.0 file
+ SAL_INFO_IF( aLastUserName.isEmpty(), "sc.ui", "Author not found" );
+ nLastUserIndex = 0;
+ }
+ else
+ {
+ size_t nPos = std::distance(rUsers.begin(), it);
+ nLastUserIndex = nPos % SC_AUTHORCOLORCOUNT;
+ }
+ }
+ nColor = nAuthorColor[nLastUserIndex];
+ }
+}
+
+ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
+ ScTableInfo& rTabInfo, ScDocument* pNewDoc,
+ SCTAB nNewTab, tools::Long nNewScrX, tools::Long nNewScrY,
+ SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
+ double nPixelPerTwipsX, double nPixelPerTwipsY,
+ const Fraction* pZoomX, const Fraction* pZoomY ) :
+ mpDev( pNewDev ),
+ mpRefDevice( pNewDev ), // default is output device
+ pFmtDevice( pNewDev ), // default is output device
+ mrTabInfo( rTabInfo ),
+ pRowInfo( rTabInfo.mpRowInfo.get() ),
+ nArrCount( rTabInfo.mnArrCount ),
+ mpDoc( pNewDoc ),
+ nTab( nNewTab ),
+ nScrX( nNewScrX ),
+ nScrY( nNewScrY ),
+ nX1( nNewX1 ),
+ nY1( nNewY1 ),
+ nX2( nNewX2 ),
+ nY2( nNewY2 ),
+ eType( eNewType ),
+ mnPPTX( nPixelPerTwipsX ),
+ mnPPTY( nPixelPerTwipsY ),
+ pViewShell( nullptr ),
+ pDrawView( nullptr ),
+ bEditMode( false ),
+ nEditCol( 0 ),
+ nEditRow( 0 ),
+ bMetaFile( false ),
+ bPagebreakMode( false ),
+ bSolidBackground( false ),
+ mbUseStyleColor( false ),
+ mbForceAutoColor( SvtAccessibilityOptions::GetIsAutomaticFontColor() ),
+ mbSyntaxMode( false ),
+ aGridColor( COL_BLACK ),
+ mbShowNullValues( true ),
+ mbShowFormulas( false ),
+ bShowSpellErrors( false ),
+ bMarkClipped( false ), // sal_False for printer/metafile etc.
+ bSnapPixel( false ),
+ bAnyClipped( false ),
+ bVertical(false),
+ mpTargetPaintWindow(nullptr), // #i74769# use SdrPaintWindow direct
+ mpSpellCheckCxt(nullptr)
+{
+ if (pZoomX)
+ aZoomX = *pZoomX;
+ else
+ aZoomX = Fraction(1,1);
+ if (pZoomY)
+ aZoomY = *pZoomY;
+ else
+ aZoomY = Fraction(1,1);
+
+ nVisX1 = nX1;
+ nVisY1 = nY1;
+ nVisX2 = nX2;
+ nVisY2 = nY2;
+ mpDoc->StripHidden( nVisX1, nVisY1, nVisX2, nVisY2, nTab );
+
+ nScrW = 0;
+ for (SCCOL nX=nVisX1; nX<=nVisX2; nX++)
+ nScrW += pRowInfo[0].basicCellInfo(nX).nWidth;
+
+ nMirrorW = nScrW;
+
+ nScrH = 0;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ nScrH += pRowInfo[nArrY].nHeight;
+
+ bTabProtected = mpDoc->IsTabProtected( nTab );
+ bLayoutRTL = mpDoc->IsLayoutRTL( nTab );
+
+ // always needed, so call at the end of the constructor
+ SetCellRotations();
+ InitOutputEditEngine();
+}
+
+ScOutputData::~ScOutputData()
+{
+}
+
+void ScOutputData::SetSpellCheckContext( const sc::SpellCheckContext* pCxt )
+{
+ mpSpellCheckCxt = pCxt;
+}
+
+void ScOutputData::SetContentDevice( OutputDevice* pContentDev )
+{
+ // use pContentDev instead of pDev where used
+
+ if ( mpRefDevice == mpDev )
+ mpRefDevice = pContentDev;
+ if ( pFmtDevice == mpDev )
+ pFmtDevice = pContentDev;
+ mpDev = pContentDev;
+}
+
+void ScOutputData::SetMirrorWidth( tools::Long nNew )
+{
+ nMirrorW = nNew;
+}
+
+void ScOutputData::SetGridColor( const Color& rColor )
+{
+ aGridColor = rColor;
+}
+
+void ScOutputData::SetMarkClipped( bool bSet )
+{
+ bMarkClipped = bSet;
+}
+
+void ScOutputData::SetShowNullValues( bool bSet )
+{
+ mbShowNullValues = bSet;
+}
+
+void ScOutputData::SetShowFormulas( bool bSet )
+{
+ mbShowFormulas = bSet;
+}
+
+void ScOutputData::SetShowSpellErrors( bool bSet )
+{
+ bShowSpellErrors = bSet;
+ // reset EditEngine because it depends on bShowSpellErrors
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::SetSnapPixel()
+{
+ bSnapPixel = true;
+}
+
+void ScOutputData::SetEditCell( SCCOL nCol, SCROW nRow )
+{
+ nEditCol = nCol;
+ nEditRow = nRow;
+ bEditMode = true;
+}
+
+void ScOutputData::SetMetaFileMode( bool bNewMode )
+{
+ bMetaFile = bNewMode;
+}
+
+void ScOutputData::SetSyntaxMode( bool bNewMode )
+{
+ mbSyntaxMode = bNewMode;
+ if ( bNewMode && !mxValueColor )
+ {
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ mxValueColor = rColorCfg.GetColorValue( svtools::CALCVALUE ).nColor;
+ mxTextColor = rColorCfg.GetColorValue( svtools::CALCTEXT ).nColor;
+ mxFormulaColor = rColorCfg.GetColorValue( svtools::CALCFORMULA ).nColor;
+ }
+}
+
+void ScOutputData::DrawGrid(vcl::RenderContext& rRenderContext, bool bGrid, bool bPage, bool bMergeCover)
+{
+ // bMergeCover : Draw lines in sheet bgcolor to cover lok client grid lines in merged cell areas.
+ // When scNoGridBackground is set in lok mode, bMergeCover is set to true and bGrid to false.
+
+ SCCOL nX;
+ SCROW nY;
+ tools::Long nPosX;
+ tools::Long nPosY;
+ SCSIZE nArrY;
+ ScBreakType nBreak = ScBreakType::NONE;
+ ScBreakType nBreakOld = ScBreakType::NONE;
+
+ bool bSingle;
+ bool bDashed = false;
+ Color aPageColor;
+ Color aManualColor;
+
+ if (bPagebreakMode)
+ bPage = false; // no "normal" breaks over the whole width/height
+
+ // It is a big mess to distinguish when we are using pixels and when logic
+ // units for drawing. Ultimately we want to work only in the logic units,
+ // but until that happens, we need to special-case:
+ //
+ // * metafile
+ // * drawing to the screen - everything is internally counted in pixels there
+ //
+ // 'Internally' in the above means the pCellInfo[...].nWidth and
+ // pRowInfo[...]->nHeight:
+ //
+ // * when bWorksInPixels is true: these are in pixels
+ // * when bWorksInPixels is false: these are in the logic units
+ //
+ // This is where all the confusion comes from, ultimately we want them
+ // always in the logic units (100th of millimeters), but we need to get
+ // there gradually (get rid of setting MapUnit::MapPixel first), otherwise we'd
+ // break all the drawing by one change.
+ // So until that happens, we need to special case.
+ bool bWorksInPixels = bMetaFile;
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aSheetBGColor = rColorCfg.GetColorValue(::svtools::DOCCOLOR).nColor;
+
+ if ( eType == OUTTYPE_WINDOW )
+ {
+ bWorksInPixels = true;
+ aPageColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
+ aManualColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor;
+ }
+ else
+ {
+ aPageColor = aGridColor;
+ aManualColor = aGridColor;
+ }
+
+ tools::Long nOneX = 1;
+ tools::Long nOneY = 1;
+ if (!bWorksInPixels)
+ {
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ nOneX = aOnePixel.Width();
+ nOneY = aOnePixel.Height();
+ }
+
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ tools::Long nSignedOneX = nOneX * nLayoutSign;
+
+ rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
+
+ ScGridMerger aGrid(&rRenderContext, nOneX, nOneY);
+
+ // vertical lines
+
+ nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - nOneX;
+
+ for (nX=nX1; nX<=nX2; nX++)
+ {
+ sal_uInt16 nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ if (nWidth)
+ {
+ nPosX += nWidth * nLayoutSign;
+
+ if ( bPage )
+ {
+ // Search also in hidden part for page breaks
+ SCCOL nCol = nX + 1;
+ while (nCol <= mpDoc->MaxCol())
+ {
+ nBreak = mpDoc->HasColBreak(nCol, nTab);
+ bool bHidden = mpDoc->ColHidden(nCol, nTab);
+
+ if ( nBreak != ScBreakType::NONE || !bHidden )
+ break;
+ ++nCol;
+ }
+
+ if (nBreak != nBreakOld)
+ {
+ aGrid.Flush();
+
+ if (static_cast<int>(nBreak))
+ {
+ rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
+ aPageColor );
+ bDashed = true;
+ }
+ else
+ {
+ rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
+ bDashed = false;
+ }
+
+ nBreakOld = nBreak;
+ }
+ }
+
+ bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set that way
+
+ sal_uInt16 nWidthXplus1 = pRowInfo[0].basicCellInfo(nX+1).nWidth;
+ bSingle = false; //! get into Fillinfo !!!!!
+ if ( nX<mpDoc->MaxCol() && !bSingle )
+ {
+ bSingle = ( nWidthXplus1 == 0 );
+ for (nArrY=1; nArrY+1<nArrCount && !bSingle; nArrY++)
+ {
+ if (pRowInfo[nArrY].cellInfo(nX+1).bHOverlapped)
+ bSingle = true;
+ if (pRowInfo[nArrY].cellInfo(nX).bHideGrid)
+ bSingle = true;
+ }
+ }
+
+ if (bDraw)
+ {
+ if ( nX<mpDoc->MaxCol() && bSingle )
+ {
+ SCCOL nVisX = nX + 1;
+ while ( nVisX < mpDoc->MaxCol() && !mpDoc->GetColWidth(nVisX,nTab) )
+ ++nVisX;
+
+ nPosY = nScrY;
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ const tools::Long nNextY = nPosY + pThisRowInfo->nHeight;
+
+ bool bHOver = pThisRowInfo->cellInfo(nX).bHideGrid;
+ if (!bHOver)
+ {
+ if (nWidthXplus1)
+ bHOver = pThisRowInfo->cellInfo(nX+1).bHOverlapped;
+ else
+ {
+ if (nVisX <= nX2)
+ bHOver = pThisRowInfo->cellInfo(nVisX).bHOverlapped;
+ else
+ bHOver = mpDoc->GetAttr(
+ nVisX,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
+ ->IsHorOverlapped();
+ if (bHOver)
+ bHOver = mpDoc->GetAttr(
+ nX + 1,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
+ ->IsHorOverlapped();
+ }
+ }
+
+ if ((pThisRowInfo->bChanged && !bHOver && !bMergeCover) || (bHOver && bMergeCover))
+ {
+ aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nPosY, nNextY-nOneY, bDashed);
+ }
+ nPosY = nNextY;
+ }
+ }
+ else if (!bMergeCover)
+ {
+ aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nScrY, nScrY+nScrH-nOneY, bDashed);
+ }
+ }
+ }
+ }
+
+ // horizontal lines
+
+ bool bHiddenRow = true;
+ SCROW nHiddenEndRow = -1;
+ nPosY = nScrY;
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ SCSIZE nArrYplus1 = nArrY+1;
+ nY = pRowInfo[nArrY].nRowNo;
+ SCROW nYplus1 = nY+1;
+ nPosY += pRowInfo[nArrY].nHeight;
+
+ if (pRowInfo[nArrY].bChanged)
+ {
+ if ( bPage )
+ {
+ for (SCROW i = nYplus1; i <= mpDoc->MaxRow(); ++i)
+ {
+ if (i > nHiddenEndRow)
+ bHiddenRow = mpDoc->RowHidden(i, nTab, nullptr, &nHiddenEndRow);
+ /* TODO: optimize the row break thing for large hidden
+ * segments where HasRowBreak() has to be called
+ * nevertheless for each row, as a row break is drawn also
+ * for hidden rows, above them. This needed to be done only
+ * once per hidden segment, maybe giving manual breaks
+ * priority. Something like GetNextRowBreak() and
+ * GetNextManualRowBreak(). */
+ nBreak = mpDoc->HasRowBreak(i, nTab);
+ if (!bHiddenRow || nBreak != ScBreakType::NONE)
+ break;
+ }
+
+ if (nBreakOld != nBreak)
+ {
+ aGrid.Flush();
+
+ if (static_cast<int>(nBreak))
+ {
+ rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
+ aPageColor );
+ bDashed = true;
+ }
+ else
+ {
+ rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
+ bDashed = false;
+ }
+
+ nBreakOld = nBreak;
+ }
+ }
+
+ bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set so
+
+ bool bNextYisNextRow = (pRowInfo[nArrYplus1].nRowNo == nYplus1);
+ bSingle = !bNextYisNextRow; // Hidden
+ for (SCCOL i=nX1; i<=nX2 && !bSingle; i++)
+ {
+ if (pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped)
+ bSingle = true;
+ }
+
+ if (bDraw)
+ {
+ if ( bSingle && nY<mpDoc->MaxRow() )
+ {
+ SCROW nVisY = pRowInfo[nArrYplus1].nRowNo;
+
+ nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - nOneX;
+
+ for (SCCOL i=nX1; i<=nX2; i++)
+ {
+ const tools::Long nNextX = nPosX + pRowInfo[0].basicCellInfo(i).nWidth * nLayoutSign;
+ if (nNextX != nPosX) // visible
+ {
+ bool bVOver;
+ if ( bNextYisNextRow )
+ bVOver = pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped;
+ else
+ {
+ bVOver = mpDoc->GetAttr(
+ i,nYplus1,nTab,ATTR_MERGE_FLAG)
+ ->IsVerOverlapped()
+ && mpDoc->GetAttr(
+ i,nVisY,nTab,ATTR_MERGE_FLAG)
+ ->IsVerOverlapped();
+ //! nVisY from Array ??
+ }
+
+ if ((!bVOver && !bMergeCover) || (bVOver && bMergeCover))
+ {
+ aGrid.AddHorLine(bWorksInPixels, nPosX, nNextX-nSignedOneX, nPosY-nOneY, bDashed);
+ }
+ }
+ nPosX = nNextX;
+ }
+ }
+ else if (!bMergeCover)
+ {
+ aGrid.AddHorLine(bWorksInPixels, nScrX, nScrX+nScrW-nOneX, nPosY-nOneY, bDashed);
+ }
+ }
+ }
+ }
+}
+
+void ScOutputData::SetPagebreakMode( ScPageBreakData* pPageData )
+{
+ bPagebreakMode = true;
+ if (!pPageData)
+ return; // not yet initialized -> everything "not printed"
+
+ // mark printed range
+ // (everything in FillInfo is already initialized to sal_False)
+
+ sal_uInt16 nRangeCount = sal::static_int_cast<sal_uInt16>(pPageData->GetCount());
+ for (sal_uInt16 nPos=0; nPos<nRangeCount; nPos++)
+ {
+ ScRange aRange = pPageData->GetData( nPos ).GetPrintRange();
+
+ SCCOL nStartX = std::max( aRange.aStart.Col(), nX1 );
+ SCCOL nEndX = std::min( aRange.aEnd.Col(), nX2 );
+ SCROW nStartY = std::max( aRange.aStart.Row(), nY1 );
+ SCROW nEndY = std::min( aRange.aEnd.Row(), nY2 );
+
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged && pThisRowInfo->nRowNo >= nStartY &&
+ pThisRowInfo->nRowNo <= nEndY )
+ {
+ for (SCCOL nX=nStartX; nX<=nEndX; nX++)
+ pThisRowInfo->cellInfo(nX).bPrinted = true;
+ }
+ }
+ }
+}
+
+void ScOutputData::SetCellRotations()
+{
+ //! save nRotMax
+ SCCOL nRotMax = nX2;
+ for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
+ if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nRotY].nRotMaxCol;
+
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE &&
+ ( pThisRowInfo->bChanged || pRowInfo[nArrY-1].bChanged ||
+ ( nArrY+1<nArrCount && pRowInfo[nArrY+1].bChanged ) ) )
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+
+ for (SCCOL nX=0; nX<=nRotMax; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ const ScPatternAttr* pPattern = pInfo->pPatternAttr;
+ const SfxItemSet* pCondSet = pInfo->pConditionSet;
+
+ if ( !pPattern && !mpDoc->ColHidden(nX, nTab) )
+ {
+ pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
+ }
+
+ if ( pPattern ) // column isn't hidden
+ {
+ ScRotateDir nDir = pPattern->GetRotateDir( pCondSet );
+ if (nDir != ScRotateDir::NONE)
+ {
+ // Needed for ScCellInfo internal decisions (bg fill, ...)
+ pInfo->nRotateDir = nDir;
+
+ // create target coordinates
+ const SCCOL nTargetX(nX - nVisX1 + 1);
+ const SCROW nTargetY(nY - nVisY1 + 1);
+
+ // Check for values - below in SetCellRotation these will
+ // be converted to size_t and thus may not be negative
+ if(nTargetX >= 0 && nTargetY >= 0)
+ {
+ // add rotation info to Array information
+ const Degree100 nAttrRotate(pPattern->GetRotateVal(pCondSet));
+ const SvxRotateMode eRotMode(pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue());
+ const double fOrient((bLayoutRTL ? -1.0 : 1.0) * toRadians(nAttrRotate)); // 1/100th degrees -> [0..2PI]
+ svx::frame::Array& rArray = mrTabInfo.maArray;
+
+ rArray.SetCellRotation(nTargetX, nTargetY, eRotMode, fOrient);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static ScRotateDir lcl_GetRotateDir( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+
+ ScRotateDir nRet = ScRotateDir::NONE;
+
+ Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
+ if ( nAttrRotate )
+ {
+ SvxRotateMode eRotMode =
+ pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nRet = ScRotateDir::Standard;
+ else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
+ nRet = ScRotateDir::Center;
+ else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
+ {
+ tools::Long nRot180 = nAttrRotate.get() % 18000; // 1/100 degree
+ if ( nRot180 == 9000 )
+ nRet = ScRotateDir::Center;
+ else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) ||
+ ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) )
+ nRet = ScRotateDir::Left;
+ else
+ nRet = ScRotateDir::Right;
+ }
+ }
+
+ return nRet;
+}
+
+static const SvxBrushItem* lcl_FindBackground( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ const SvxBrushItem* pBackground =
+ &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
+
+ ScRotateDir nDir = lcl_GetRotateDir( pDoc, nCol, nRow, nTab );
+
+ // treat CENTER like RIGHT
+ if ( nDir == ScRotateDir::Right || nDir == ScRotateDir::Center )
+ {
+ // text goes to the right -> take background from the left
+ while ( nCol > 0 && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
+ pBackground->GetColor().GetAlpha() != 0 )
+ {
+ --nCol;
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
+ }
+ }
+ else if ( nDir == ScRotateDir::Left )
+ {
+ // text goes to the left -> take background from the right
+ while ( nCol < pDoc->MaxCol() && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
+ pBackground->GetColor().GetAlpha() != 0 )
+ {
+ ++nCol;
+ pPattern = pDoc->GetPattern( nCol, nRow, nTab );
+ pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
+ }
+ }
+
+ return pBackground;
+}
+
+static bool lcl_EqualBack( const RowInfo& rFirst, const RowInfo& rOther,
+ SCCOL nX1, SCCOL nX2, bool bShowProt, bool bPagebreakMode )
+{
+ if ( rFirst.bChanged != rOther.bChanged ||
+ rFirst.bEmptyBack != rOther.bEmptyBack )
+ return false;
+
+ SCCOL nX;
+ if ( bShowProt )
+ {
+ for ( nX=nX1; nX<=nX2; nX++ )
+ {
+ const ScPatternAttr* pPat1 = rFirst.cellInfo(nX).pPatternAttr;
+ const ScPatternAttr* pPat2 = rOther.cellInfo(nX).pPatternAttr;
+ if ( !pPat1 || !pPat2 ||
+ !SfxPoolItem::areSame(pPat1->GetItem(ATTR_PROTECTION), pPat2->GetItem(ATTR_PROTECTION) ) )
+ return false;
+ }
+ }
+ else
+ {
+ for ( nX=nX1; nX<=nX2; nX++ )
+ if ( !SfxPoolItem::areSame(rFirst.cellInfo(nX).pBackground, rOther.cellInfo(nX).pBackground ) )
+ return false;
+ }
+
+ if ( rFirst.nRotMaxCol != SC_ROTMAX_NONE || rOther.nRotMaxCol != SC_ROTMAX_NONE )
+ for ( nX=nX1; nX<=nX2; nX++ )
+ if ( rFirst.cellInfo(nX).nRotateDir != rOther.cellInfo(nX).nRotateDir )
+ return false;
+
+ if ( bPagebreakMode )
+ for ( nX=nX1; nX<=nX2; nX++ )
+ if ( rFirst.cellInfo(nX).bPrinted != rOther.cellInfo(nX).bPrinted )
+ return false;
+
+ for ( nX=nX1; nX<=nX2; nX++ )
+ {
+ std::optional<Color> const & pCol1 = rFirst.cellInfo(nX).mxColorScale;
+ std::optional<Color> const & pCol2 = rOther.cellInfo(nX).mxColorScale;
+ if( (pCol1 && !pCol2) || (!pCol1 && pCol2) )
+ return false;
+
+ if (pCol1 && (*pCol1 != *pCol2))
+ return false;
+
+ const ScDataBarInfo* pInfo1 = rFirst.cellInfo(nX).pDataBar;
+ const ScDataBarInfo* pInfo2 = rOther.cellInfo(nX).pDataBar;
+
+ if( (pInfo1 && !pInfo2) || (!pInfo1 && pInfo2) )
+ return false;
+
+ if (pInfo1 && (*pInfo1 != *pInfo2))
+ return false;
+
+ // each cell with an icon set should be painted the same way
+ const ScIconSetInfo* pIconSet1 = rFirst.cellInfo(nX).pIconSet;
+ const ScIconSetInfo* pIconSet2 = rOther.cellInfo(nX).pIconSet;
+
+ if(pIconSet1 || pIconSet2)
+ return false;
+ }
+
+ return true;
+}
+
+void ScOutputData::DrawDocumentBackground()
+{
+ if ( !bSolidBackground )
+ return;
+
+ Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
+ mpDev->SetLineColor(aBgColor);
+ mpDev->SetFillColor(aBgColor);
+
+ Point aScreenPos = mpDev->PixelToLogic(Point(nScrX, nScrY));
+ Size aScreenSize = mpDev->PixelToLogic(Size(nScrW - 1,nScrH - 1));
+
+ mpDev->DrawRect(tools::Rectangle(aScreenPos, aScreenSize));
+}
+
+namespace {
+
+const double lclCornerRectTransparency = 40.0;
+
+void drawDataBars(vcl::RenderContext& rRenderContext, const ScDataBarInfo* pOldDataBarInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY)
+{
+ tools::Long nPosZero = 0;
+ tools::Rectangle aPaintRect = rRect;
+ aPaintRect.AdjustTop(2 * nOneY );
+ aPaintRect.AdjustBottom( -(2 * nOneY) );
+ aPaintRect.AdjustLeft( 2 * nOneX );
+ aPaintRect.AdjustRight( -(2 * nOneX) );
+ if(pOldDataBarInfo->mnZero)
+ {
+ // need to calculate null point in cell
+ tools::Long nLength = aPaintRect.Right() - aPaintRect.Left();
+ nPosZero = static_cast<tools::Long>(aPaintRect.Left() + nLength*pOldDataBarInfo->mnZero/100.0);
+ }
+ else
+ {
+ nPosZero = aPaintRect.Left();
+ }
+
+ if(pOldDataBarInfo->mnLength < 0)
+ {
+ aPaintRect.SetRight( nPosZero );
+ tools::Long nLength = nPosZero - aPaintRect.Left();
+ aPaintRect.SetLeft( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
+ }
+ else if(pOldDataBarInfo->mnLength > 0)
+ {
+ aPaintRect.SetLeft( nPosZero );
+ tools::Long nLength = aPaintRect.Right() - nPosZero;
+ aPaintRect.SetRight( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
+ }
+ else
+ return;
+
+ if(pOldDataBarInfo->mbGradient)
+ {
+ rRenderContext.SetLineColor(pOldDataBarInfo->maColor);
+ Gradient aGradient(css::awt::GradientStyle_LINEAR, pOldDataBarInfo->maColor, COL_TRANSPARENT);
+ aGradient.SetSteps(255);
+
+ if(pOldDataBarInfo->mnLength < 0)
+ aGradient.SetAngle(2700_deg10);
+ else
+ aGradient.SetAngle(900_deg10);
+
+ rRenderContext.DrawGradient(aPaintRect, aGradient);
+
+ rRenderContext.SetLineColor();
+ }
+ else
+ {
+ rRenderContext.SetFillColor(pOldDataBarInfo->maColor);
+ rRenderContext.DrawRect(aPaintRect);
+ }
+
+ //draw axis
+ if(!(pOldDataBarInfo->mnZero && pOldDataBarInfo->mnZero != 100))
+ return;
+
+ Point aPoint1(nPosZero, rRect.Top());
+ Point aPoint2(nPosZero, rRect.Bottom());
+ LineInfo aLineInfo(LineStyle::Dash, 1);
+ aLineInfo.SetDashCount( 4 );
+ aLineInfo.SetDistance( 3 );
+ aLineInfo.SetDashLen( 3 );
+ rRenderContext.SetFillColor(pOldDataBarInfo->maAxisColor);
+ rRenderContext.SetLineColor(pOldDataBarInfo->maAxisColor);
+ rRenderContext.DrawLine(aPoint1, aPoint2, aLineInfo);
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor();
+}
+
+const BitmapEx& getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap, ScIconSetType eType, sal_Int32 nIndex)
+{
+ return ScIconSetFormat::getBitmap(rIconSetBitmapMap, eType, nIndex);
+}
+
+void drawIconSets(vcl::RenderContext& rRenderContext, const ScIconSetInfo* pOldIconSetInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY,
+ sc::IconSetBitmapMap & rIconSetBitmapMap)
+{
+ ScIconSetType eType = pOldIconSetInfo->eIconSetType;
+ sal_Int32 nIndex = pOldIconSetInfo->nIconIndex;
+ const BitmapEx& rIcon = getIcon(rIconSetBitmapMap, eType, nIndex);
+
+ tools::Long aHeight = o3tl::convert(10, o3tl::Length::pt, o3tl::Length::mm100);
+
+ if (pOldIconSetInfo->mnHeight)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ aHeight = rRenderContext.LogicToPixel(Size(0, pOldIconSetInfo->mnHeight), MapMode(MapUnit::MapTwip)).Height();
+ aHeight *= comphelper::LibreOfficeKit::getDPIScale();
+ }
+ else
+ {
+ aHeight = o3tl::convert(pOldIconSetInfo->mnHeight, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ }
+
+ Size aSize = rIcon.GetSizePixel();
+ double fRatio = static_cast<double>(aSize.Width()) / aSize.Height();
+ tools::Long aWidth = fRatio * aHeight;
+
+ rRenderContext.Push();
+ rRenderContext.SetClipRegion(vcl::Region(rRect));
+ rRenderContext.DrawBitmapEx(Point(rRect.Left() + 2 * nOneX, rRect.Bottom() - 2 * nOneY - aHeight), Size(aWidth, aHeight), rIcon);
+ rRenderContext.Pop();
+}
+
+void drawCells(vcl::RenderContext& rRenderContext, std::optional<Color> const & pColor, const SvxBrushItem* pBackground, std::optional<Color>& pOldColor, const SvxBrushItem*& pOldBackground,
+ tools::Rectangle& rRect, tools::Long nPosX, tools::Long nLayoutSign, tools::Long nOneX, tools::Long nOneY, const ScDataBarInfo* pDataBarInfo, const ScDataBarInfo*& pOldDataBarInfo,
+ const ScIconSetInfo* pIconSetInfo, const ScIconSetInfo*& pOldIconSetInfo,
+ sc::IconSetBitmapMap & rIconSetBitmapMap)
+{
+ tools::Long nSignedOneX = nOneX * nLayoutSign;
+ // need to paint if old color scale has been used and now
+ // we have a different color or a style based background
+ // we can here fall back to pointer comparison
+ if (pOldColor && (pBackground || pOldColor != pColor || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo))
+ {
+ rRect.SetRight( nPosX-nSignedOneX );
+ if( !pOldColor->IsTransparent() )
+ {
+ rRenderContext.SetFillColor( *pOldColor );
+ rRenderContext.DrawRect( rRect );
+ }
+ if( pOldDataBarInfo )
+ drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
+ if( pOldIconSetInfo )
+ drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
+
+ rRect.SetLeft( nPosX - nSignedOneX );
+ }
+
+ if ( pOldBackground && (pColor || !SfxPoolItem::areSame(pBackground, pOldBackground) || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo) )
+ {
+ rRect.SetRight( nPosX-nSignedOneX );
+ if (pOldBackground) // ==0 if hidden
+ {
+ Color aBackCol = pOldBackground->GetColor();
+ if ( !aBackCol.IsTransparent() ) //! partial transparency?
+ {
+ rRenderContext.SetFillColor( aBackCol );
+ rRenderContext.DrawRect( rRect );
+ }
+ }
+ if( pOldDataBarInfo )
+ drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
+ if( pOldIconSetInfo )
+ drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
+
+ rRect.SetLeft( nPosX - nSignedOneX );
+ }
+
+ if (!pOldBackground && !pOldColor && (pDataBarInfo || pIconSetInfo))
+ {
+ rRect.SetRight( nPosX -nSignedOneX );
+ rRect.SetLeft( nPosX - nSignedOneX );
+ }
+
+ if(pColor)
+ {
+ // only update pOldColor if the colors changed
+ if (!pOldColor || *pOldColor != *pColor)
+ pOldColor = pColor;
+
+ pOldBackground = nullptr;
+ }
+ else if(pBackground)
+ {
+ pOldBackground = pBackground;
+ pOldColor.reset();
+ }
+
+ if(pDataBarInfo)
+ pOldDataBarInfo = pDataBarInfo;
+ else
+ pOldDataBarInfo = nullptr;
+
+ if(pIconSetInfo)
+ pOldIconSetInfo = pIconSetInfo;
+ else
+ pOldIconSetInfo = nullptr;
+}
+
+}
+
+void ScOutputData::DrawBackground(vcl::RenderContext& rRenderContext)
+{
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneXLogic = aOnePixel.Width();
+ tools::Long nOneYLogic = aOnePixel.Height();
+
+ // See more about bWorksInPixels in ScOutputData::DrawGrid
+ bool bWorksInPixels = false;
+ if (eType == OUTTYPE_WINDOW)
+ bWorksInPixels = true;
+
+ tools::Long nOneX = 1;
+ tools::Long nOneY = 1;
+ if (!bWorksInPixels)
+ {
+ nOneX = nOneXLogic;
+ nOneY = nOneYLogic;
+ }
+
+ tools::Rectangle aRect;
+
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ rRenderContext.SetLineColor();
+
+ bool bShowProt = mbSyntaxMode && mpDoc->IsTabProtected(nTab);
+ bool bDoAll = bShowProt || bPagebreakMode || bSolidBackground;
+
+ bool bCellContrast = mbUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ tools::Long nPosY = nScrY;
+
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aProtectedColor( rColorCfg.GetColorValue( svtools::CALCPROTECTEDBACKGROUND ).nColor );
+ auto pProtectedBackground = std::make_shared<SvxBrushItem>( aProtectedColor, ATTR_BACKGROUND );
+
+ // iterate through the rows to show
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nRowHeight = pThisRowInfo->nHeight;
+
+ if ( pThisRowInfo->bChanged )
+ {
+ if ( ( ( pThisRowInfo->bEmptyBack ) || mbSyntaxMode ) && !bDoAll )
+ {
+ // nothing
+ }
+ else
+ {
+ // scan for rows with the same background:
+ SCSIZE nSkip = 0;
+ while ( nArrY+nSkip+2<nArrCount &&
+ lcl_EqualBack( *pThisRowInfo, pRowInfo[nArrY+nSkip+1],
+ nX1, nX2, bShowProt, bPagebreakMode ) )
+ {
+ ++nSkip;
+ nRowHeight += pRowInfo[nArrY+nSkip].nHeight; // after incrementing
+ }
+
+ tools::Long nPosX = nScrX;
+
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - nOneX;
+
+ aRect = tools::Rectangle(nPosX, nPosY - nOneY, nPosX, nPosY - nOneY + nRowHeight);
+ if (bWorksInPixels)
+ aRect = rRenderContext.PixelToLogic(aRect); // internal data in pixels, but we'll be drawing in logic units
+
+ const SvxBrushItem* pOldBackground = nullptr;
+ const SvxBrushItem* pBackground = nullptr;
+ std::optional<Color> pOldColor;
+ const ScDataBarInfo* pOldDataBarInfo = nullptr;
+ const ScIconSetInfo* pOldIconSetInfo = nullptr;
+ SCCOL nMergedCols = 1;
+ SCCOL nOldMerged = 0;
+
+ for (SCCOL nX=nX1; nX + nMergedCols <= nX2 + 1; nX += nOldMerged)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX-1+nMergedCols);
+
+ nOldMerged = nMergedCols;
+
+ tools::Long nNewPosX = nPosX;
+ // extend for all merged cells
+ nMergedCols = 1;
+ if (pInfo->bMerged && pInfo->pPatternAttr)
+ {
+ const ScMergeAttr* pMerge =
+ &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
+ nMergedCols = std::max<SCCOL>(1, pMerge->GetColMerge());
+ }
+
+ for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
+ {
+ SCCOL nCol = nX+nOldMerged+nMerged;
+ if (nCol > nX2+2)
+ break;
+ nNewPosX += pRowInfo[0].basicCellInfo(nCol-1).nWidth * nLayoutSign;
+ }
+
+ if (nNewPosX == nPosX)
+ continue; // Zero width, no need to draw.
+
+ if (bCellContrast)
+ {
+ // high contrast for cell borders and backgrounds -> empty background
+ pBackground = ScGlobal::GetEmptyBrushItem();
+ }
+ else if (bShowProt) // show cell protection in syntax mode
+ {
+ const ScPatternAttr* pP = pInfo->pPatternAttr;
+ if (pP)
+ {
+ const ScProtectionAttr& rProt = pP->GetItem(ATTR_PROTECTION);
+ if (rProt.GetProtection() || rProt.GetHideCell())
+ pBackground = pProtectedBackground.get();
+ else
+ pBackground = ScGlobal::GetEmptyBrushItem();
+ }
+ else
+ pBackground = nullptr;
+ }
+ else
+ pBackground = pInfo->pBackground;
+
+ if ( bPagebreakMode && !pInfo->bPrinted )
+ pBackground = pProtectedBackground.get();
+
+ if ( pInfo->nRotateDir > ScRotateDir::Standard &&
+ !pBackground->GetColor().IsFullyTransparent() &&
+ !bCellContrast )
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ pBackground = lcl_FindBackground( mpDoc, nX, nY, nTab );
+ }
+
+ std::optional<Color> const & pColor = pInfo->mxColorScale;
+ const ScDataBarInfo* pDataBarInfo = pInfo->pDataBar;
+ const ScIconSetInfo* pIconSetInfo = pInfo->pIconSet;
+
+ tools::Long nPosXLogic = nPosX;
+ if (bWorksInPixels)
+ nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
+
+ drawCells(rRenderContext, pColor, pBackground, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, pDataBarInfo, pOldDataBarInfo, pIconSetInfo, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
+
+ nPosX = nNewPosX;
+ }
+
+ tools::Long nPosXLogic = nPosX;
+ if (bWorksInPixels)
+ nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
+
+ drawCells(rRenderContext, std::optional<Color>(), nullptr, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, nullptr, pOldDataBarInfo, nullptr, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
+
+ nArrY += nSkip;
+ }
+ }
+ nPosY += nRowHeight;
+ }
+}
+
+void ScOutputData::DrawShadow()
+{
+ DrawExtraShadow( false, false, false, false );
+}
+
+void ScOutputData::DrawExtraShadow(bool bLeft, bool bTop, bool bRight, bool bBottom)
+{
+ mpDev->SetLineColor();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
+ Color aAutoTextColor;
+ if ( bCellContrast )
+ aAutoTextColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY - pRowInfo[0].nHeight;
+ for (SCSIZE nArrY=0; nArrY<nArrCount; nArrY++)
+ {
+ bool bCornerY = ( nArrY == 0 ) || ( nArrY+1 == nArrCount );
+ bool bSkipY = ( nArrY==0 && !bTop ) || ( nArrY+1 == nArrCount && !bBottom );
+
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nRowHeight = pThisRowInfo->nHeight;
+
+ if ( pThisRowInfo->bChanged && !bSkipY )
+ {
+ tools::Long nPosX = nInitPosX - pRowInfo[0].basicCellInfo(nX1-1).nWidth * nLayoutSign;
+ for (SCCOL nCol=nX1-1; nCol<=nX2+1; nCol++)
+ {
+ bool bCornerX = ( nCol==nX1-1 || nCol==nX2+1 );
+ bool bSkipX = ( nCol==nX1-1 && !bLeft ) || ( nCol==nX2+1 && !bRight );
+
+ for (sal_uInt16 nPass=0; nPass<2; nPass++) // horizontal / vertical
+ {
+ const SvxShadowItem* pAttr = nPass ?
+ pThisRowInfo->cellInfo(nCol).pVShadowOrigin :
+ pThisRowInfo->cellInfo(nCol).pHShadowOrigin;
+ if ( pAttr && !bSkipX )
+ {
+ ScShadowPart ePart = nPass ?
+ pThisRowInfo->cellInfo(nCol).eVShadowPart :
+ pThisRowInfo->cellInfo(nCol).eHShadowPart;
+
+ bool bDo = true;
+ if ( (nPass==0 && bCornerX) || (nPass==1 && bCornerY) )
+ if ( ePart != SC_SHADOW_CORNER )
+ bDo = false;
+
+ if (bDo)
+ {
+ tools::Long nThisWidth = pRowInfo[0].basicCellInfo(nCol).nWidth;
+ tools::Long nMaxWidth = nThisWidth;
+ if (!nMaxWidth)
+ {
+ //! direction must depend on shadow location
+ SCCOL nWx = nCol+1;
+ while (nWx<nX2 && !pRowInfo[0].basicCellInfo(nWx).nWidth)
+ ++nWx;
+ nMaxWidth = pRowInfo[0].basicCellInfo(nWx).nWidth;
+ }
+
+ // rectangle is in logical orientation
+ tools::Rectangle aRect( nPosX, nPosY,
+ nPosX + ( nThisWidth - 1 ) * nLayoutSign,
+ nPosY + pRowInfo[nArrY].nHeight - 1 );
+
+ tools::Long nSize = pAttr->GetWidth();
+ tools::Long nSizeX = static_cast<tools::Long>(nSize*mnPPTX);
+ if (nSizeX >= nMaxWidth) nSizeX = nMaxWidth-1;
+ tools::Long nSizeY = static_cast<tools::Long>(nSize*mnPPTY);
+ if (nSizeY >= nRowHeight) nSizeY = nRowHeight-1;
+
+ nSizeX *= nLayoutSign; // used only to add to rectangle values
+
+ SvxShadowLocation eLoc = pAttr->GetLocation();
+ if ( bLayoutRTL )
+ {
+ // Shadow location is specified as "visual" (right is always right),
+ // so the attribute's location value is mirrored here and in FillInfo.
+ switch (eLoc)
+ {
+ case SvxShadowLocation::BottomRight: eLoc = SvxShadowLocation::BottomLeft; break;
+ case SvxShadowLocation::BottomLeft: eLoc = SvxShadowLocation::BottomRight; break;
+ case SvxShadowLocation::TopRight: eLoc = SvxShadowLocation::TopLeft; break;
+ case SvxShadowLocation::TopLeft: eLoc = SvxShadowLocation::TopRight; break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if (ePart == SC_SHADOW_HORIZ || ePart == SC_SHADOW_HSTART ||
+ ePart == SC_SHADOW_CORNER)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
+ aRect.SetTop( aRect.Bottom() - nSizeY );
+ else
+ aRect.SetBottom( aRect.Top() + nSizeY );
+ }
+ if (ePart == SC_SHADOW_VERT || ePart == SC_SHADOW_VSTART ||
+ ePart == SC_SHADOW_CORNER)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
+ aRect.SetLeft( aRect.Right() - nSizeX );
+ else
+ aRect.SetRight( aRect.Left() + nSizeX );
+ }
+ if (ePart == SC_SHADOW_HSTART)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
+ aRect.AdjustRight( -nSizeX );
+ else
+ aRect.AdjustLeft(nSizeX );
+ }
+ if (ePart == SC_SHADOW_VSTART)
+ {
+ if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
+ aRect.AdjustBottom( -nSizeY );
+ else
+ aRect.AdjustTop(nSizeY );
+ }
+
+ //! merge rectangles?
+ mpDev->SetFillColor( bCellContrast ? aAutoTextColor : pAttr->GetColor() );
+ mpDev->DrawRect( aRect );
+ }
+ }
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nCol).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += nRowHeight;
+ }
+}
+
+void ScOutputData::DrawClear()
+{
+ tools::Rectangle aRect;
+ Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ // (called only for ScGridWindow)
+ Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
+
+ if (bMetaFile)
+ nOneX = nOneY = 0;
+
+ mpDev->SetLineColor();
+
+ mpDev->SetFillColor( aBgColor );
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nRowHeight = pThisRowInfo->nHeight;
+
+ if ( pThisRowInfo->bChanged )
+ {
+ // scan for more rows which must be painted:
+ SCSIZE nSkip = 0;
+ while ( nArrY+nSkip+2<nArrCount && pRowInfo[nArrY+nSkip+1].bChanged )
+ {
+ ++nSkip;
+ nRowHeight += pRowInfo[nArrY+nSkip].nHeight; // after incrementing
+ }
+
+ aRect = tools::Rectangle( Point( nScrX, nPosY ),
+ Size( nScrW+1-nOneX, nRowHeight+1-nOneY) );
+ mpDev->DrawRect( aRect );
+
+ nArrY += nSkip;
+ }
+ nPosY += nRowHeight;
+ }
+}
+
+// Lines
+
+static tools::Long lclGetSnappedX( const OutputDevice& rDev, tools::Long nPosX, bool bSnapPixel )
+{
+ return (bSnapPixel && nPosX) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( nPosX, 0 ) ) ).Width() : nPosX;
+}
+
+static tools::Long lclGetSnappedY( const OutputDevice& rDev, tools::Long nPosY, bool bSnapPixel )
+{
+ return (bSnapPixel && nPosY) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( 0, nPosY ) ) ).Height() : nPosY;
+}
+
+void ScOutputData::DrawFrame(vcl::RenderContext& rRenderContext)
+{
+ DrawModeFlags nOldDrawMode = rRenderContext.GetDrawMode();
+
+ Color aSingleColor;
+ bool bUseSingleColor = false;
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
+
+ // if a Calc OLE object is embedded in Draw/Impress, the VCL DrawMode is used
+ // for display mode / B&W printing. The VCL DrawMode handling doesn't work for lines
+ // that are drawn with DrawRect, so if the line/background bits are set, the DrawMode
+ // must be reset and the border colors handled here.
+
+ if ( ( nOldDrawMode & DrawModeFlags::WhiteFill ) && ( nOldDrawMode & DrawModeFlags::BlackLine ) )
+ {
+ rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::WhiteFill) );
+ aSingleColor = COL_BLACK;
+ bUseSingleColor = true;
+ }
+ else if ( ( nOldDrawMode & DrawModeFlags::SettingsFill ) && ( nOldDrawMode & DrawModeFlags::SettingsLine ) )
+ {
+ rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::SettingsFill) );
+ aSingleColor = rStyleSettings.GetWindowTextColor(); // same as used in VCL for DrawModeFlags::SettingsLine
+ bUseSingleColor = true;
+ }
+ else if ( bCellContrast )
+ {
+ aSingleColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ bUseSingleColor = true;
+ }
+
+ const Color* pForceColor = bUseSingleColor ? &aSingleColor : nullptr;
+
+ if (mrTabInfo.maArray.HasCellRotation())
+ {
+ DrawRotatedFrame(rRenderContext); // removes the lines that must not be painted here
+ }
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ // *** set column and row sizes of the frame border array ***
+
+ svx::frame::Array& rArray = mrTabInfo.maArray;
+ size_t nColCount = rArray.GetColCount();
+ size_t nRowCount = rArray.GetRowCount();
+
+ // row heights
+
+ // row 0 is not visible (dummy for borders from top) - subtract its height from initial position
+ // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit before
+ tools::Long nOldPosY = nScrY - 1 - pRowInfo[ 0 ].nHeight;
+ tools::Long nOldSnapY = lclGetSnappedY( rRenderContext, nOldPosY, bSnapPixel );
+ rArray.SetYOffset( nOldSnapY );
+ for( size_t nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ tools::Long nNewPosY = nOldPosY + pRowInfo[ nRow ].nHeight;
+ tools::Long nNewSnapY = lclGetSnappedY( rRenderContext, nNewPosY, bSnapPixel );
+ rArray.SetRowHeight( nRow, nNewSnapY - nOldSnapY );
+ nOldPosY = nNewPosY;
+ nOldSnapY = nNewSnapY;
+ }
+
+ // column widths
+
+ // column nX1-1 is not visible (dummy for borders from left) - subtract its width from initial position
+ // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit above
+ tools::Long nOldPosX = nInitPosX - nLayoutSign * (1 + pRowInfo[ 0 ].basicCellInfo( nX1 - 1 ).nWidth);
+ tools::Long nOldSnapX = lclGetSnappedX( rRenderContext, nOldPosX, bSnapPixel );
+ // set X offset for left-to-right sheets; for right-to-left sheets this is done after for() loop
+ if( !bLayoutRTL )
+ rArray.SetXOffset( nOldSnapX );
+ for( SCCOL nCol = nX1 - 1; nCol <= nX2 + 1; ++nCol )
+ {
+ size_t nArrCol = bLayoutRTL ? nX2 + 1 - nCol : nCol - (nX1 - 1);
+ tools::Long nNewPosX = nOldPosX + pRowInfo[ 0 ].basicCellInfo( nCol ).nWidth * nLayoutSign;
+ tools::Long nNewSnapX = lclGetSnappedX( rRenderContext, nNewPosX, bSnapPixel );
+ rArray.SetColWidth( nArrCol, std::abs( nNewSnapX - nOldSnapX ) );
+ nOldPosX = nNewPosX;
+ nOldSnapX = nNewSnapX;
+ }
+ if( bLayoutRTL )
+ rArray.SetXOffset( nOldSnapX );
+
+ // *** draw the array ***
+
+ size_t nFirstCol = 1;
+ size_t nFirstRow = 1;
+ size_t nLastCol = nColCount - 2;
+ size_t nLastRow = nRowCount - 2;
+
+ if( mrTabInfo.mbPageMode )
+ rArray.SetClipRange( nFirstCol, nFirstRow, nLastCol, nLastRow );
+
+ // draw only rows with set RowInfo::bChanged flag
+ size_t nRow1 = nFirstRow;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D());
+ if (!pProcessor)
+ return;
+ while( nRow1 <= nLastRow )
+ {
+ while( (nRow1 <= nLastRow) && !pRowInfo[ nRow1 ].bChanged ) ++nRow1;
+ if( nRow1 <= nLastRow )
+ {
+ size_t nRow2 = nRow1;
+ while( (nRow2 + 1 <= nLastRow) && pRowInfo[ nRow2 + 1 ].bChanged ) ++nRow2;
+ auto xPrimitive = rArray.CreateB2DPrimitiveRange(
+ nFirstCol, nRow1, nLastCol, nRow2, pForceColor );
+ pProcessor->process(xPrimitive);
+ nRow1 = nRow2 + 1;
+ }
+ }
+ pProcessor.reset();
+
+ rRenderContext.SetDrawMode(nOldDrawMode);
+}
+
+void ScOutputData::DrawRotatedFrame(vcl::RenderContext& rRenderContext)
+{
+ //! save nRotMax
+ SCCOL nRotMax = nX2;
+ for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
+ if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nRotY].nRotMaxCol;
+
+ const ScPatternAttr* pPattern;
+ const SfxItemSet* pCondSet;
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Rectangle aClipRect( Point(nScrX, nScrY), Size(nScrW, nScrH) );
+ if (bMetaFile)
+ {
+ rRenderContext.Push();
+ rRenderContext.IntersectClipRegion( aClipRect );
+ }
+ else
+ rRenderContext.SetClipRegion( vcl::Region( aClipRect ) );
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D( ));
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
+ {
+ // Rotated is also drawn one line above/below Changed if parts extend into the cell
+
+ RowInfo& rPrevRowInfo = pRowInfo[nArrY-1];
+ RowInfo& rThisRowInfo = pRowInfo[nArrY];
+ RowInfo& rNextRowInfo = pRowInfo[nArrY+1];
+
+ tools::Long nRowHeight = rThisRowInfo.nHeight;
+ if ( rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE &&
+ ( rThisRowInfo.bChanged || rPrevRowInfo.bChanged ||
+ ( nArrY+1<nArrCount && rNextRowInfo.bChanged ) ) )
+ {
+ SCROW nY = rThisRowInfo.nRowNo;
+ tools::Long nPosX = 0;
+ SCCOL nX;
+ for (nX=0; nX<=nRotMax; nX++)
+ {
+ if (nX==nX1) nPosX = nInitPosX; // calculated individually for preceding positions
+
+ ScCellInfo* pInfo = &rThisRowInfo.cellInfo(nX);
+ tools::Long nColWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ if ( pInfo->nRotateDir > ScRotateDir::Standard &&
+ !pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ pPattern = pInfo->pPatternAttr;
+ pCondSet = pInfo->pConditionSet;
+ if (!pPattern)
+ {
+ pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ pInfo->pPatternAttr = pPattern;
+ pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
+ pInfo->pConditionSet = pCondSet;
+ }
+
+ //! LastPattern etc.
+
+ Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
+ SvxRotateMode eRotMode =
+ pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ if (nAttrRotate)
+ {
+ if (nX < nX1) // compute negative position
+ {
+ nPosX = nInitPosX;
+ SCCOL nCol = nX1;
+ while (nCol > nX)
+ {
+ --nCol;
+ nPosX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
+ }
+ }
+
+ // start position minus 1 so rotated backgrounds suit the border
+ // (border is on the grid)
+
+ tools::Long nTop = nPosY - 1;
+ tools::Long nBottom = nPosY + nRowHeight - 1;
+ tools::Long nTopLeft = nPosX - nLayoutSign;
+ tools::Long nTopRight = nPosX + (nColWidth - 1) * nLayoutSign;
+ tools::Long nBotLeft = nTopLeft;
+ tools::Long nBotRight = nTopRight;
+
+ // inclusion of the sign here hasn't been decided yet
+ // (if not, the extension of the non-rotated background must also be changed)
+ double nRealOrient = nLayoutSign * toRadians(nAttrRotate); // 1/100th degrees
+ double nCos = cos(nRealOrient);
+ double nSin = sin(nRealOrient);
+ //! restrict !!!
+ tools::Long nSkew = static_cast<tools::Long>(nRowHeight * nCos / nSin);
+
+ switch (eRotMode)
+ {
+ case SVX_ROTATE_MODE_BOTTOM:
+ nTopLeft += nSkew;
+ nTopRight += nSkew;
+ break;
+ case SVX_ROTATE_MODE_CENTER:
+ nSkew /= 2;
+ nTopLeft += nSkew;
+ nTopRight += nSkew;
+ nBotLeft -= nSkew;
+ nBotRight -= nSkew;
+ break;
+ case SVX_ROTATE_MODE_TOP:
+ nBotLeft -= nSkew;
+ nBotRight -= nSkew;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ Point aPoints[4];
+ aPoints[0] = Point(nTopLeft, nTop);
+ aPoints[1] = Point(nTopRight, nTop);
+ aPoints[2] = Point(nBotRight, nBottom);
+ aPoints[3] = Point(nBotLeft, nBottom);
+
+ const SvxBrushItem* pBackground = pInfo->pBackground;
+ if (!pBackground)
+ pBackground = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
+ if (bCellContrast)
+ {
+ // high contrast for cell borders and backgrounds -> empty background
+ pBackground = ScGlobal::GetEmptyBrushItem();
+ }
+ if (!pInfo->mxColorScale)
+ {
+ const Color& rColor = pBackground->GetColor();
+ if (rColor.GetAlpha() != 0)
+ {
+ // draw background only for the changed row itself
+ // (background doesn't extend into other cells).
+ // For the borders (rotated and normal), clipping should be
+ // set if the row isn't changed, but at least the borders
+ // don't cover the cell contents.
+ if (rThisRowInfo.bChanged)
+ {
+ tools::Polygon aPoly(4, aPoints);
+
+ // for DrawPolygon, without Pen one pixel is left out
+ // to the right and below...
+ if (!rColor.IsTransparent())
+ rRenderContext.SetLineColor(rColor);
+ else
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(rColor);
+ rRenderContext.DrawPolygon(aPoly);
+ }
+ }
+ }
+ else
+ {
+ tools::Polygon aPoly(4, aPoints);
+ std::optional<Color> const & pColor = pInfo->mxColorScale;
+
+ // for DrawPolygon, without Pen one pixel is left out
+ // to the right and below...
+ if (!pColor->IsTransparent())
+ rRenderContext.SetLineColor(*pColor);
+ else
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(*pColor);
+ rRenderContext.DrawPolygon(aPoly);
+
+ }
+ }
+ }
+ nPosX += nColWidth * nLayoutSign;
+ }
+ }
+ nPosY += nRowHeight;
+ }
+
+ pProcessor.reset();
+
+ if (bMetaFile)
+ rRenderContext.Pop();
+ else
+ rRenderContext.SetClipRegion();
+}
+
+std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> ScOutputData::CreateProcessor2D( )
+{
+ mpDoc->InitDrawLayer(mpDoc->GetDocumentShell());
+ ScDrawLayer* pDrawLayer = mpDoc->GetDrawLayer();
+ if (!pDrawLayer)
+ return nullptr;
+
+ basegfx::B2DRange aViewRange;
+ SdrPage *pDrawPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
+ drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ aNewViewInfos.setViewTransformation(mpDev->GetViewTransformation());
+ aNewViewInfos.setViewport(aViewRange);
+ aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
+
+ return drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ *mpDev, aNewViewInfos );
+}
+
+// Printer
+
+vcl::Region ScOutputData::GetChangedAreaRegion()
+{
+ vcl::Region aRegion;
+ tools::Rectangle aDrawingRect;
+ bool bHad(false);
+ tools::Long nPosY = nScrY;
+ SCSIZE nArrY;
+
+ aDrawingRect.SetLeft( nScrX );
+ aDrawingRect.SetRight( nScrX+nScrW-1 );
+
+ for(nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ if(pThisRowInfo->bChanged)
+ {
+ if(!bHad)
+ {
+ aDrawingRect.SetTop( nPosY );
+ bHad = true;
+ }
+
+ aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
+ }
+ else if(bHad)
+ {
+ aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
+ bHad = false;
+ }
+
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ if(bHad)
+ {
+ aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
+ }
+
+ return aRegion;
+}
+
+bool ScOutputData::SetChangedClip()
+{
+ tools::PolyPolygon aPoly;
+
+ tools::Rectangle aDrawingRect;
+ aDrawingRect.SetLeft( nScrX );
+ aDrawingRect.SetRight( nScrX+nScrW-1 );
+
+ bool bHad = false;
+ tools::Long nPosY = nScrY;
+ SCSIZE nArrY;
+ for (nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ if ( pThisRowInfo->bChanged )
+ {
+ if (!bHad)
+ {
+ aDrawingRect.SetTop( nPosY );
+ bHad = true;
+ }
+ aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
+ }
+ else if (bHad)
+ {
+ aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
+ bHad = false;
+ }
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ if (bHad)
+ aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
+
+ bool bRet = (aPoly.Count() != 0);
+ if (bRet)
+ mpDev->SetClipRegion(vcl::Region(aPoly));
+ return bRet;
+}
+
+void ScOutputData::FindChanged()
+{
+ SCCOL nX;
+ SCSIZE nArrY;
+
+ bool bWasIdleEnabled = mpDoc->IsIdleEnabled();
+ mpDoc->EnableIdle(false);
+ for (nArrY=0; nArrY<nArrCount; nArrY++)
+ pRowInfo[nArrY].bChanged = false;
+
+ SCCOL nCol1 = mpDoc->MaxCol(), nCol2 = 0;
+ SCROW nRow1 = mpDoc->MaxRow(), nRow2 = 0;
+ bool bAnyDirty = false;
+ bool bAnyChanged = false;
+
+ for (nArrY=0; nArrY<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ for (nX=nX1; nX<=nX2; nX++)
+ {
+ const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
+
+ if (rCell.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->IsRunning())
+ // still being interpreted. Skip it.
+ continue;
+
+ bool bDirty = pFCell->GetDirty();
+ bAnyChanged = bAnyChanged || pFCell->IsChanged();
+
+ if (bDirty)
+ {
+ if (!bAnyDirty)
+ {
+ ScProgress::CreateInterpretProgress(mpDoc);
+ bAnyDirty = true;
+ }
+
+ ScAddress& rPos(pFCell->aPos);
+ nCol1 = std::min(rPos.Col(), nCol1);
+ nCol2 = std::max(rPos.Col(), nCol2);
+ nRow1 = std::min(rPos.Row(), nRow1);
+ nRow2 = std::max(rPos.Row(), nRow2);
+
+ const SfxUInt32Item* pItem = mpDoc->GetAttr(rPos, ATTR_VALIDDATA);
+ const ScValidationData* pData = mpDoc->GetValidationEntry(pItem->GetValue());
+ if (pData)
+ {
+ ScRefCellValue aCell(*mpDoc, rPos);
+ if (pData->IsDataValid(aCell, rPos))
+ ScDetectiveFunc(*mpDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
+ }
+ }
+ }
+ }
+
+ if (bAnyDirty || bAnyChanged)
+ {
+ if (bAnyDirty)
+ mpDoc->EnsureFormulaCellResults(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), true);
+
+ for (nArrY=0; nArrY<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ for (nX=nX1; nX<=nX2; nX++)
+ {
+ const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
+
+ if (rCell.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->IsRunning())
+ // still being interpreted. Skip it.
+ continue;
+
+ if (!pFCell->IsChanged())
+ // the result hasn't changed. Skip it.
+ continue;
+
+ pThisRowInfo->bChanged = true;
+ if ( pThisRowInfo->cellInfo(nX).bMerged )
+ {
+ SCSIZE nOverY = nArrY + 1;
+ while ( nOverY<nArrCount &&
+ pRowInfo[nOverY].cellInfo(nX).bVOverlapped )
+ {
+ pRowInfo[nOverY].bChanged = true;
+ ++nOverY;
+ }
+ }
+ }
+ }
+
+ if (bAnyDirty)
+ ScProgress::DeleteInterpretProgress();
+ }
+
+ mpDoc->EnableIdle(bWasIdleEnabled);
+}
+
+ReferenceMark ScOutputData::FillReferenceMark( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY, const Color& rColor)
+{
+ ReferenceMark aResult;
+
+ PutInOrder( nRefStartX, nRefEndX );
+ PutInOrder( nRefStartY, nRefEndY );
+
+ if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
+ mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
+
+ if ( nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
+ nRefStartY <= nVisY2 && nRefEndY >= nVisY1 )
+ {
+ tools::Long nMinX = nScrX;
+ tools::Long nMinY = nScrY;
+ tools::Long nMaxX = nScrX + nScrW - 1;
+ tools::Long nMaxY = nScrY + nScrH - 1;
+ if ( bLayoutRTL )
+ std::swap( nMinX, nMaxX );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ bool bTop = false;
+ bool bBottom = false;
+ bool bLeft = false;
+ bool bRight = false;
+
+ tools::Long nPosY = nScrY;
+ bool bNoStartY = ( nY1 < nRefStartY );
+ bool bNoEndY = false;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+
+ if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
+ {
+ nMinY = nPosY;
+ bTop = true;
+ }
+ if ( nY==nRefEndY )
+ {
+ nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
+ bBottom = true;
+ }
+ if ( nY>nRefEndY && bNoEndY )
+ {
+ nMaxY = nPosY-2;
+ bBottom = true;
+ }
+ bNoStartY = ( nY < nRefStartY );
+ bNoEndY = ( nY < nRefEndY );
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - 1; // always in pixels
+
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ if ( nX==nRefStartX )
+ {
+ nMinX = nPosX;
+ bLeft = true;
+ }
+ if ( nX==nRefEndX )
+ {
+ nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
+ bRight = true;
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+
+ if (bTop && bBottom && bLeft && bRight)
+ {
+ // mnPPT[XY] already has the factor aZoom[XY] in it.
+ aResult = ReferenceMark( nMinX / mnPPTX,
+ nMinY / mnPPTY,
+ ( nMaxX - nMinX ) / mnPPTX,
+ ( nMaxY - nMinY ) / mnPPTY,
+ nTab,
+ rColor );
+ }
+ }
+
+ return aResult;
+}
+
+void ScOutputData::DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor, bool bHandle )
+{
+ PutInOrder( nRefStartX, nRefEndX );
+ PutInOrder( nRefStartY, nRefEndY );
+
+ if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
+ mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
+ else if (mpDoc->HasAttrib(nRefEndX, nRefEndY, nTab, HasAttrFlags::Merged))
+ mpDoc->ExtendMerge(nRefEndX, nRefEndY, nRefEndX, nRefEndY, nTab);
+
+ if ( !(nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
+ nRefStartY <= nVisY2 && nRefEndY >= nVisY1) )
+ return;
+
+ tools::Long nMinX = nScrX;
+ tools::Long nMinY = nScrY;
+ tools::Long nMaxX = nScrX + nScrW - 1;
+ tools::Long nMaxY = nScrY + nScrH - 1;
+ if ( bLayoutRTL )
+ std::swap( nMinX, nMaxX );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ bool bTop = false;
+ bool bBottom = false;
+ bool bLeft = false;
+ bool bRight = false;
+
+ tools::Long nPosY = nScrY;
+ bool bNoStartY = ( nY1 < nRefStartY );
+ bool bNoEndY = false;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+
+ if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
+ {
+ nMinY = nPosY;
+ bTop = true;
+ }
+ if ( nY==nRefEndY )
+ {
+ nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
+ bBottom = true;
+ }
+ if ( nY>nRefEndY && bNoEndY )
+ {
+ nMaxY = nPosY-2;
+ bBottom = true;
+ }
+ bNoStartY = ( nY < nRefStartY );
+ bNoEndY = ( nY < nRefEndY );
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - 1; // always in pixels
+
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ if ( nX==nRefStartX )
+ {
+ nMinX = nPosX;
+ bLeft = true;
+ }
+ if ( nX==nRefEndX )
+ {
+ nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
+ bRight = true;
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+
+ if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
+ return;
+
+ mpDev->SetLineColor( rColor );
+ if (bTop && bBottom && bLeft && bRight && !comphelper::LibreOfficeKit::isActive() )
+ {
+ mpDev->SetFillColor();
+ mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
+ }
+ else if ( !comphelper::LibreOfficeKit::isActive() )
+ {
+ if (bTop)
+ mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMaxX, nMinY ) );
+ if (bBottom)
+ mpDev->DrawLine( Point( nMinX, nMaxY ), Point( nMaxX, nMaxY ) );
+ if (bLeft)
+ mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMinX, nMaxY ) );
+ if (bRight)
+ mpDev->DrawLine( Point( nMaxX, nMinY ), Point( nMaxX, nMaxY ) );
+ }
+ if ( !bHandle || !bRight || !bBottom || comphelper::LibreOfficeKit::isActive() )
+ return;
+
+ mpDev->SetLineColor( rColor );
+ mpDev->SetFillColor( rColor );
+
+ const sal_Int32 aRadius = 4;
+
+ sal_Int32 aRectMaxX1 = nMaxX - nLayoutSign * aRadius;
+ sal_Int32 aRectMaxX2 = nMaxX + nLayoutSign;
+ sal_Int32 aRectMinX1 = nMinX - nLayoutSign;
+ sal_Int32 aRectMinX2 = nMinX + nLayoutSign * aRadius;
+
+ sal_Int32 aRectMaxY1 = nMaxY - aRadius;
+ sal_Int32 aRectMaxY2 = nMaxY + 1;
+ sal_Int32 aRectMinY1 = nMinY - 1;
+ sal_Int32 aRectMinY2 = nMinY + aRadius;
+
+ // Draw corner rectangles
+ tools::Rectangle aLowerRight( aRectMaxX1, aRectMaxY1, aRectMaxX2, aRectMaxY2 );
+ tools::Rectangle aUpperLeft ( aRectMinX1, aRectMinY1, aRectMinX2, aRectMinY2 );
+ tools::Rectangle aLowerLeft ( aRectMinX1, aRectMaxY1, aRectMinX2, aRectMaxY2 );
+ tools::Rectangle aUpperRight( aRectMaxX1, aRectMinY1, aRectMaxX2, aRectMinY2 );
+
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerRight ) ), lclCornerRectTransparency );
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperLeft ) ), lclCornerRectTransparency );
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerLeft ) ), lclCornerRectTransparency );
+ mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperRight ) ), lclCornerRectTransparency );
+}
+
+void ScOutputData::DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
+ SCCOL nRefEndX, SCROW nRefEndY,
+ const Color& rColor, sal_uInt16 nType )
+{
+ PutInOrder( nRefStartX, nRefEndX );
+ PutInOrder( nRefStartY, nRefEndY );
+
+ if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
+ mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
+
+ if ( !(nRefStartX <= nVisX2 + 1 && nRefEndX >= nVisX1 &&
+ nRefStartY <= nVisY2 + 1 && nRefEndY >= nVisY1) ) // +1 because it touches next cells left/top
+ return;
+
+ tools::Long nMinX = nScrX;
+ tools::Long nMinY = nScrY;
+ tools::Long nMaxX = nScrX+nScrW-1;
+ tools::Long nMaxY = nScrY+nScrH-1;
+ if ( bLayoutRTL )
+ std::swap( nMinX, nMaxX );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ bool bTop = false;
+ bool bBottom = false;
+ bool bLeft = false;
+ bool bRight = false;
+
+ tools::Long nPosY = nScrY;
+ bool bNoStartY = ( nY1 < nRefStartY );
+ bool bNoEndY = false;
+ for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++) // loop to end for bNoEndY check
+ {
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+
+ if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
+ {
+ nMinY = nPosY - 1;
+ bTop = true;
+ }
+ if ( nY==nRefEndY )
+ {
+ nMaxY = nPosY + pRowInfo[nArrY].nHeight - 1;
+ bBottom = true;
+ }
+ if ( nY>nRefEndY && bNoEndY )
+ {
+ nMaxY = nPosY - 1;
+ bBottom = true;
+ }
+ bNoStartY = ( nY < nRefStartY );
+ bNoEndY = ( nY < nRefEndY );
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ nPosX += nMirrorW - 1; // always in pixels
+
+ for (SCCOL nX=nX1; nX<=nX2+1; nX++)
+ {
+ if ( nX==nRefStartX )
+ {
+ nMinX = nPosX - nLayoutSign;
+ bLeft = true;
+ }
+ if ( nX==nRefEndX )
+ {
+ nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 1 ) * nLayoutSign;
+ bRight = true;
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+
+ if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
+ return;
+
+ if ( nType == SC_CAT_DELETE_ROWS )
+ bLeft = bRight = bBottom = false; //! thick lines???
+ else if ( nType == SC_CAT_DELETE_COLS )
+ bTop = bBottom = bRight = false; //! thick lines???
+
+ mpDev->SetLineColor( rColor );
+ if (bTop && bBottom && bLeft && bRight)
+ {
+ mpDev->SetFillColor();
+ mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
+ }
+ else
+ {
+ if (bTop)
+ {
+ mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
+ if ( nType == SC_CAT_DELETE_ROWS )
+ mpDev->DrawLine( Point( nMinX,nMinY+1 ), Point( nMaxX,nMinY+1 ) );
+ }
+ if (bBottom)
+ mpDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
+ if (bLeft)
+ {
+ mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
+ if ( nType == SC_CAT_DELETE_COLS )
+ mpDev->DrawLine( Point( nMinX+nLayoutSign,nMinY ), Point( nMinX+nLayoutSign,nMaxY ) );
+ }
+ if (bRight)
+ mpDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
+ }
+ if ( bLeft && bTop )
+ {
+ mpDev->SetLineColor();
+ mpDev->SetFillColor( rColor );
+ mpDev->DrawRect( tools::Rectangle( nMinX+nLayoutSign, nMinY+1, nMinX+3*nLayoutSign, nMinY+3 ) );
+ }
+}
+
+void ScOutputData::DrawChangeTrack()
+{
+ ScChangeTrack* pTrack = mpDoc->GetChangeTrack();
+ ScChangeViewSettings* pSettings = mpDoc->GetChangeViewSettings();
+ if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
+ return; // nothing there or hidden
+
+ ScActionColorChanger aColorChanger(*pTrack);
+
+ // clipping happens from the outside
+ //! without clipping, only paint affected cells ??!??!?
+
+ SCCOL nEndX = nX2;
+ SCROW nEndY = nY2;
+ if ( nEndX < mpDoc->MaxCol() ) ++nEndX; // also from the next cell since the mark
+ if ( nEndY < mpDoc->MaxRow() ) ++nEndY; // protrudes from the preceding cell
+ ScRange aViewRange( nX1, nY1, nTab, nEndX, nEndY, nTab );
+ const ScChangeAction* pAction = pTrack->GetFirst();
+ while (pAction)
+ {
+ if ( pAction->IsVisible() )
+ {
+ ScChangeActionType eActionType = pAction->GetType();
+ const ScBigRange& rBig = pAction->GetBigRange();
+ if ( rBig.aStart.Tab() == nTab )
+ {
+ ScRange aRange = rBig.MakeRange( *mpDoc );
+
+ if ( eActionType == SC_CAT_DELETE_ROWS )
+ aRange.aEnd.SetRow( aRange.aStart.Row() );
+ else if ( eActionType == SC_CAT_DELETE_COLS )
+ aRange.aEnd.SetCol( aRange.aStart.Col() );
+
+ if ( aRange.Intersects( aViewRange ) &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
+ {
+ aColorChanger.Update( *pAction );
+ Color aColor( aColorChanger.GetColor() );
+ DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
+
+ }
+ }
+ if ( eActionType == SC_CAT_MOVE &&
+ static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().aStart.Tab() == nTab )
+ {
+ ScRange aRange = static_cast<const ScChangeActionMove*>(pAction)->
+ GetFromRange().MakeRange( *mpDoc );
+ if ( aRange.Intersects( aViewRange ) &&
+ ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
+ {
+ aColorChanger.Update( *pAction );
+ Color aColor( aColorChanger.GetColor() );
+ DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
+ }
+ }
+ }
+
+ pAction = pAction->GetNext();
+ }
+}
+
+void ScOutputData::DrawSparklines(vcl::RenderContext& rRenderContext)
+{
+ Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
+ tools::Long nOneXLogic = aOnePixel.Width();
+ tools::Long nOneYLogic = aOnePixel.Height();
+
+ // See more about bWorksInPixels in ScOutputData::DrawGrid
+ bool bWorksInPixels = false;
+ if (eType == OUTTYPE_WINDOW)
+ bWorksInPixels = true;
+
+ tools::Long nOneX = 1;
+ tools::Long nOneY = 1;
+ if (!bWorksInPixels)
+ {
+ nOneX = nOneXLogic;
+ nOneY = nOneYLogic;
+ }
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bIsMerged = false;
+
+ if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ // find start of merged cell
+ bIsMerged = true;
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ SCCOL nMergeX = nX;
+ SCROW nMergeY = nY;
+ mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+ }
+
+ std::shared_ptr<sc::Sparkline> pSparkline;
+ ScAddress aCurrentAddress(nX, pRowInfo[nArrY].nRowNo, nTab);
+
+ if (!mpDoc->ColHidden(nX, nTab) && (pSparkline = mpDoc->GetSparkline(aCurrentAddress))
+ && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
+ {
+ const tools::Long nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ const tools::Long nHeight = pThisRowInfo->nHeight;
+
+ Point aPoint(nPosX, nPosY);
+ Size aSize(nWidth, nHeight);
+
+ sc::SparklineRenderer renderer(*mpDoc);
+ renderer.render(pSparkline, rRenderContext, tools::Rectangle(aPoint, aSize), nOneX, nOneY, double(aZoomX), double(aZoomY));
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+
+}
+
+//TODO: moggi Need to check if this can't be written simpler
+void ScOutputData::DrawNoteMarks(vcl::RenderContext& rRenderContext)
+{
+ // cool#6911 draw the note indicator browser-side instead
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bIsMerged = false;
+
+ if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ // find start of merged cell
+ bIsMerged = true;
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ SCCOL nMergeX = nX;
+ SCROW nMergeY = nY;
+ mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+ }
+
+ if (!mpDoc->ColHidden(nX, nTab) && mpDoc->GetNote(nX, pRowInfo[nArrY].nRowNo, nTab)
+ && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
+ {
+
+ const bool bIsDarkBackground = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
+ const Color aColor = pInfo->pBackground->GetColor();
+ if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
+ rRenderContext.SetLineColor(COL_WHITE);
+ else
+ rRenderContext.SetLineColor(COL_BLACK);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
+ rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+ else
+ rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCCOMMENTS).nColor );
+
+ tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
+ if ( bIsMerged || pInfo->bMerged )
+ {
+ // if merged, add widths of all cells
+ SCCOL nNextX = nX + 1;
+ while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
+ {
+ nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
+ ++nNextX;
+ }
+ }
+ // DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
+ // DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
+ const double nSize( rRenderContext.GetDPIScaleFactor() * aZoomX * 6 + 4);
+ Point aPoints[3];
+ aPoints[0] = Point(nMarkX, nPosY);
+ aPoints[0].setX( bLayoutRTL ? aPoints[0].X() + nSize : aPoints[0].X() - nSize );
+ aPoints[1] = Point(nMarkX, nPosY);
+ aPoints[2] = Point(nMarkX, nPosY + nSize);
+ tools::Polygon aPoly(3, aPoints);
+
+ if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
+ rRenderContext.DrawPolygon(aPoly);
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+}
+
+void ScOutputData::DrawFormulaMarks(vcl::RenderContext& rRenderContext)
+{
+ bool bFirst = true;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ if (!mpDoc->ColHidden(nX, nTab) && !mpDoc->GetFormula(nX, pRowInfo[nArrY].nRowNo, nTab).isEmpty()
+ && (!pInfo->bHOverlapped && !pInfo->bVOverlapped))
+ {
+ if (bFirst)
+ {
+ rRenderContext.SetLineColor(COL_WHITE);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
+ rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+ else
+ rRenderContext.SetFillColor(COL_LIGHTBLUE);
+
+ bFirst = false;
+ }
+
+ tools::Long nMarkX = nPosX;
+ tools::Long nMarkY = nPosY + pThisRowInfo->nHeight - 2;
+ if ( pInfo->bMerged )
+ {
+ for (SCSIZE nNextY=nArrY+1; nNextY+1<nArrCount; nNextY++)
+ {
+ bool bVOver;
+ if (pRowInfo[nNextY + 1].nRowNo == (pRowInfo[nNextY].nRowNo + 1)) {
+ bVOver = pRowInfo[nNextY].cellInfo(nX).bVOverlapped;
+ } else {
+ bVOver = mpDoc->GetAttr(nX,nNextY,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped();
+ }
+ if (!bVOver) break;
+ nMarkY += pRowInfo[nNextY].nHeight;
+ }
+ }
+ // DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
+ // DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
+ const double nSize( rRenderContext.GetDPIScaleFactor() * aZoomX * 6 + 4);
+ Point aPoints[3];
+ aPoints[0] = Point(nMarkX, nMarkY);
+ aPoints[0].setX( bLayoutRTL ? aPoints[0].X() - nSize : aPoints[0].X() + nSize );
+ aPoints[1] = Point(nMarkX, nMarkY);
+ aPoints[2] = Point(nMarkX, nMarkY - nSize);
+ tools::Polygon aPoly(3, aPoints);
+
+ if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
+ rRenderContext.DrawPolygon(aPoly);
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+}
+
+void ScOutputData::AddPDFNotes()
+{
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( mpDev->GetExtOutDevData() );
+ if ( !pPDFData || !pPDFData->GetIsExportNotes() )
+ return;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ nInitPosX += nMirrorW - nOneX;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bIsMerged = false;
+ SCROW nY = pRowInfo[nArrY].nRowNo;
+ SCCOL nMergeX = nX;
+ SCROW nMergeY = nY;
+
+ if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
+ {
+ // find start of merged cell
+ bIsMerged = true;
+ mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
+ // use origin's pCell for NotePtr test below
+ }
+
+ if ( mpDoc->GetNote(nMergeX, nMergeY, nTab) && ( bIsMerged ||
+ ( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
+ {
+ tools::Long nNoteWidth = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ tools::Long nNoteHeight = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTY );
+
+ tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - nNoteWidth ) * nLayoutSign;
+ if ( bIsMerged || pInfo->bMerged )
+ {
+ // if merged, add widths of all cells
+ SCCOL nNextX = nX + 1;
+ while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
+ {
+ nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
+ ++nNextX;
+ }
+ }
+ if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
+ {
+ tools::Rectangle aNoteRect( nMarkX, nPosY, nMarkX+nNoteWidth*nLayoutSign, nPosY+nNoteHeight );
+ const ScPostIt* pNote = mpDoc->GetNote(nMergeX, nMergeY, nTab);
+
+ // Note title is the cell address (as on printed note pages)
+ ScAddress aAddress( nMergeX, nMergeY, nTab );
+ OUString aTitle(aAddress.Format(ScRefFlags::VALID, mpDoc, mpDoc->GetAddressConvention()));
+
+ // Content has to be a simple string without line breaks
+ OUString aContent = pNote->GetText();
+ aContent = aContent.replaceAll("\n", " ");
+
+ vcl::PDFNote aNote;
+ aNote.Title = aTitle;
+ aNote.Contents = aContent;
+ pPDFData->CreateNote( aNoteRect, aNote );
+ }
+ }
+
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+}
+
+void ScOutputData::DrawClipMarks()
+{
+ if (!bAnyClipped)
+ return;
+
+ Color aArrowFillCol( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).nColor );
+ const bool bIsDarkBackground = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
+
+ DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // always in pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Rectangle aCellRect;
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ if ( pThisRowInfo->bChanged )
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+ tools::Long nPosX = nInitPosX;
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ if (pInfo->nClipMark != ScClipMark::NONE)
+ {
+ if (pInfo->bHOverlapped || pInfo->bVOverlapped)
+ {
+ // merge origin may be outside of visible area - use document functions
+
+ SCCOL nOverX = nX;
+ SCROW nOverY = nY;
+ tools::Long nStartPosX = nPosX;
+ tools::Long nStartPosY = nPosY;
+
+ while ( nOverX > 0 && ( mpDoc->GetAttr(
+ nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Hor ) )
+ {
+ --nOverX;
+ nStartPosX -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
+ }
+
+ while ( nOverY > 0 && ( mpDoc->GetAttr(
+ nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Ver ) )
+ {
+ --nOverY;
+ nStartPosY -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
+ }
+
+ tools::Long nOutWidth = static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
+ tools::Long nOutHeight = static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
+
+ const ScMergeAttr* pMerge = mpDoc->GetAttr( nOverX, nOverY, nTab, ATTR_MERGE );
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
+ SCROW nCountY = pMerge->GetRowMerge();
+ nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
+
+ if ( bLayoutRTL )
+ nStartPosX -= nOutWidth - 1;
+ aCellRect = tools::Rectangle( Point( nStartPosX, nStartPosY ), Size( nOutWidth, nOutHeight ) );
+ }
+ else
+ {
+ tools::Long nOutWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
+ tools::Long nOutHeight = pThisRowInfo->nHeight;
+
+ if ( pInfo->bMerged && pInfo->pPatternAttr )
+ {
+ SCCOL nOverX = nX;
+ SCROW nOverY = nY;
+ const ScMergeAttr* pMerge =
+ &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
+ SCROW nCountY = pMerge->GetRowMerge();
+ nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
+ }
+
+ tools::Long nStartPosX = nPosX;
+ if ( bLayoutRTL )
+ nStartPosX -= nOutWidth - 1;
+ // #i80447# create aCellRect from two points in case nOutWidth is 0
+ aCellRect = tools::Rectangle( Point( nStartPosX, nPosY ),
+ Point( nStartPosX+nOutWidth-1, nPosY+nOutHeight-1 ) );
+ }
+
+ aCellRect.AdjustBottom( -1 ); // don't paint over the cell grid
+ if ( bLayoutRTL )
+ aCellRect.AdjustLeft(1 );
+ else
+ aCellRect.AdjustRight( -1 );
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );
+
+ const Color aColor = pInfo->pBackground ? pInfo->pBackground->GetColor() : COL_AUTO;
+ if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
+ mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::WhiteLine );
+ else
+ mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::BlackLine );
+
+ if (bVertical)
+ {
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Bottom : ScClipMark::Top))
+ {
+ // visually top
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetBottom(aCellRect.Top() + nMarkPixel - 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true, true);
+ }
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Top : ScClipMark::Bottom))
+ {
+ // visually bottom
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetTop(aCellRect.Bottom() + nMarkPixel + 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
+ true);
+ }
+ }
+ else
+ {
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Right : ScClipMark::Left))
+ {
+ // visually left
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetRight(aCellRect.Left() + nMarkPixel - 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true,
+ false);
+ }
+ if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Left : ScClipMark::Right))
+ {
+ // visually right
+ tools::Rectangle aMarkRect = aCellRect;
+ aMarkRect.SetLeft(aCellRect.Right() - nMarkPixel + 1);
+ SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
+ false);
+ }
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pThisRowInfo->nHeight;
+ }
+
+ mpDev->SetDrawMode(nOldDrawMode);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx
new file mode 100644
index 0000000000..d419981d8e
--- /dev/null
+++ b/sc/source/ui/view/output2.cxx
@@ -0,0 +1,5248 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <formula/errorcodes.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/kernarray.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/glyphitem.hxx>
+#include <vcl/vcllayout.hxx>
+#include <vcl/glyphitemcache.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+#include <tools/stream.hxx>
+
+#include <output.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <cellform.hxx>
+#include <editutil.hxx>
+#include <progress.hxx>
+#include <scmod.hxx>
+#include <fillinfo.hxx>
+#include <stlsheet.hxx>
+#include <spellcheckcontext.hxx>
+#include <scopetools.hxx>
+
+#include <com/sun/star/i18n/DirectionProperty.hpp>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/string.hxx>
+
+#include <memory>
+#include <vector>
+#include <o3tl/lru_map.hxx>
+#include <o3tl/hash_combine.hxx>
+
+#include <math.h>
+
+using namespace com::sun::star;
+
+//! Merge Autofilter width with column.cxx
+#define DROPDOWN_BITMAP_SIZE 18
+
+#define DRAWTEXT_MAX 32767
+
+const sal_uInt16 SC_SHRINKAGAIN_MAX = 7;
+constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+
+class ScDrawStringsVars
+{
+ ScOutputData* pOutput; // connection
+
+ const ScPatternAttr* pPattern; // attribute
+ const SfxItemSet* pCondSet; // from conditional formatting
+
+ vcl::Font aFont; // created from attributes
+ FontMetric aMetric;
+ tools::Long nAscentPixel; // always pixels
+ SvxCellOrientation eAttrOrient;
+ SvxCellHorJustify eAttrHorJust;
+ SvxCellVerJustify eAttrVerJust;
+ SvxCellJustifyMethod eAttrHorJustMethod;
+ const SvxMarginItem* pMargin;
+ sal_uInt16 nIndent;
+ bool bRotated;
+
+ OUString aString; // contents
+ Size aTextSize;
+ tools::Long nOriginalWidth;
+ tools::Long nMaxDigitWidth;
+ tools::Long nSignWidth;
+ tools::Long nDotWidth;
+ tools::Long nExpWidth;
+
+ ScRefCellValue maLastCell;
+ sal_uLong nValueFormat;
+ bool bLineBreak;
+ bool bRepeat;
+ bool bShrink;
+
+ bool bPixelToLogic;
+ bool bCellContrast;
+
+ Color aBackConfigColor; // used for ScPatternAttr::GetFont calls
+ Color aTextConfigColor;
+ sal_Int32 nRepeatPos;
+ sal_Unicode nRepeatChar;
+
+public:
+ ScDrawStringsVars(ScOutputData* pData, bool bPTL);
+
+ // SetPattern = ex-SetVars
+ // SetPatternSimple: without Font
+
+ void SetPattern(
+ const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
+ SvtScriptType nScript );
+
+ void SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet );
+
+ bool SetText( const ScRefCellValue& rCell ); // TRUE -> drop pOldPattern
+ void SetHashText();
+ bool SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth );
+ void SetAutoText( const OUString& rAutoText );
+
+ SvxCellOrientation GetOrient() const { return eAttrOrient; }
+ SvxCellHorJustify GetHorJust() const { return eAttrHorJust; }
+ SvxCellVerJustify GetVerJust() const { return eAttrVerJust; }
+ SvxCellJustifyMethod GetHorJustMethod() const { return eAttrHorJustMethod; }
+ const SvxMarginItem* GetMargin() const { return pMargin; }
+
+ sal_uInt16 GetLeftTotal() const { return pMargin->GetLeftMargin() + nIndent; }
+ sal_uInt16 GetRightTotal() const { return pMargin->GetRightMargin() + nIndent; }
+
+ const OUString& GetString() const { return aString; }
+ const Size& GetTextSize() const { return aTextSize; }
+ tools::Long GetOriginalWidth() const { return nOriginalWidth; }
+ tools::Long GetFmtTextWidth(const OUString& rString);
+
+ // Get the effective number format, including formula result types.
+ // This assumes that a formula cell has already been calculated.
+ sal_uLong GetResultValueFormat() const { return nValueFormat;}
+
+ bool GetLineBreak() const { return bLineBreak; }
+ bool IsRepeat() const { return bRepeat; }
+ bool IsShrink() const { return bShrink; }
+ void RepeatToFill( tools::Long nColWidth );
+
+ tools::Long GetAscent() const { return nAscentPixel; }
+ bool IsRotated() const { return bRotated; }
+
+ void SetShrinkScale( tools::Long nScale, SvtScriptType nScript );
+
+ bool HasCondHeight() const { return pCondSet && SfxItemState::SET ==
+ pCondSet->GetItemState( ATTR_FONT_HEIGHT ); }
+
+ bool HasEditCharacters() const;
+
+ // ScOutputData::LayoutStrings() usually triggers a number of calls that require
+ // to lay out the text, which is relatively slow, so cache that operation.
+ const SalLayoutGlyphs* GetLayoutGlyphs(const OUString& rString) const
+ {
+ return SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOutput->pFmtDevice, rString);
+ }
+
+private:
+ tools::Long GetMaxDigitWidth(); // in logic units
+ tools::Long GetSignWidth();
+ tools::Long GetDotWidth();
+ tools::Long GetExpWidth();
+ void TextChanged();
+};
+
+ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, bool bPTL) :
+ pOutput ( pData ),
+ pPattern ( nullptr ),
+ pCondSet ( nullptr ),
+ nAscentPixel(0),
+ eAttrOrient ( SvxCellOrientation::Standard ),
+ eAttrHorJust( SvxCellHorJustify::Standard ),
+ eAttrVerJust( SvxCellVerJustify::Bottom ),
+ eAttrHorJustMethod( SvxCellJustifyMethod::Auto ),
+ pMargin ( nullptr ),
+ nIndent ( 0 ),
+ bRotated ( false ),
+ nOriginalWidth( 0 ),
+ nMaxDigitWidth( 0 ),
+ nSignWidth( 0 ),
+ nDotWidth( 0 ),
+ nExpWidth( 0 ),
+ nValueFormat( 0 ),
+ bLineBreak ( false ),
+ bRepeat ( false ),
+ bShrink ( false ),
+ bPixelToLogic( bPTL ),
+ nRepeatPos( -1 ),
+ nRepeatChar( 0x0 )
+{
+ ScModule* pScMod = SC_MOD();
+ bCellContrast = pOutput->mbUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ const svtools::ColorConfig& rColorConfig = pScMod->GetColorConfig();
+ aBackConfigColor = rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
+ aTextConfigColor = rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor;
+}
+
+void ScDrawStringsVars::SetShrinkScale( tools::Long nScale, SvtScriptType nScript )
+{
+ // text remains valid, size is updated
+
+ OutputDevice* pDev = pOutput->mpDev;
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+
+ // call GetFont with a modified fraction, use only the height
+
+ Fraction aFraction( nScale, 100 );
+ if ( !bPixelToLogic )
+ aFraction *= pOutput->aZoomY;
+ vcl::Font aTmpFont;
+ pPattern->fillFontOnly(aTmpFont, pFmtDevice, &aFraction, pCondSet, nScript);
+ // only need font height
+ tools::Long nNewHeight = aTmpFont.GetFontHeight();
+ if ( nNewHeight > 0 )
+ aFont.SetFontHeight( nNewHeight );
+
+ // set font and dependent variables as in SetPattern
+
+ pDev->SetFont( aFont );
+ if ( pFmtDevice != pDev )
+ pFmtDevice->SetFont( aFont );
+
+ aMetric = pFmtDevice->GetFontMetric();
+ if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
+ {
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ MapMode aOld = pDefaultDev->GetMapMode();
+ pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
+ aMetric = pDefaultDev->GetFontMetric( aFont );
+ pDefaultDev->SetMapMode( aOld );
+ }
+
+ nAscentPixel = aMetric.GetAscent();
+ if ( bPixelToLogic )
+ nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
+
+ SetAutoText( aString ); // same text again, to get text size
+}
+
+namespace {
+
+template<typename ItemType, typename EnumType>
+EnumType lcl_GetValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
+{
+ const ItemType& rItem = static_cast<const ItemType&>(rPattern.GetItem(nWhich, pCondSet));
+ return static_cast<EnumType>(rItem.GetValue());
+}
+
+bool lcl_GetBoolValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet)
+{
+ return lcl_GetValue<SfxBoolItem, bool>(rPattern, nWhich, pCondSet);
+}
+
+}
+
+static bool lcl_isNumberFormatText(const ScDocument* pDoc, SCCOL nCellX, SCROW nCellY, SCTAB nTab )
+{
+ sal_uInt32 nCurrentNumberFormat = pDoc->GetNumberFormat( nCellX, nCellY, nTab );
+ SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable();
+ return pNumberFormatter->GetType( nCurrentNumberFormat ) == SvNumFormatType::TEXT;
+}
+
+void ScDrawStringsVars::SetPattern(
+ const ScPatternAttr* pNew, const SfxItemSet* pSet, const ScRefCellValue& rCell,
+ SvtScriptType nScript )
+{
+ nMaxDigitWidth = 0;
+ nSignWidth = 0;
+ nDotWidth = 0;
+ nExpWidth = 0;
+
+ pPattern = pNew;
+ pCondSet = pSet;
+
+ // evaluate pPattern
+
+ OutputDevice* pDev = pOutput->mpDev;
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+
+ // font
+
+ ScAutoFontColorMode eColorMode;
+ if ( pOutput->mbUseStyleColor )
+ {
+ if ( pOutput->mbForceAutoColor )
+ eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreAll : ScAutoFontColorMode::IgnoreFont;
+ else
+ eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreBack : ScAutoFontColorMode::Display;
+ }
+ else
+ eColorMode = ScAutoFontColorMode::Print;
+
+ if (bPixelToLogic)
+ pPattern->fillFont(aFont, eColorMode, pFmtDevice, nullptr, pCondSet, nScript, &aBackConfigColor, &aTextConfigColor);
+ else
+ pPattern->fillFont(aFont, eColorMode, pFmtDevice, &pOutput->aZoomY, pCondSet, nScript, &aBackConfigColor, &aTextConfigColor );
+
+ aFont.SetAlignment(ALIGN_BASELINE);
+
+ // orientation
+
+ eAttrOrient = pPattern->GetCellOrientation( pCondSet );
+
+ // alignment
+
+ eAttrHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue();
+
+ eAttrVerJust = pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet ).GetValue();
+ if ( eAttrVerJust == SvxCellVerJustify::Standard )
+ eAttrVerJust = SvxCellVerJustify::Bottom;
+
+ // justification method
+
+ eAttrHorJustMethod = lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet);
+
+ // line break
+
+ bLineBreak = pPattern->GetItem( ATTR_LINEBREAK, pCondSet ).GetValue();
+
+ // handle "repeat" alignment
+
+ bRepeat = ( eAttrHorJust == SvxCellHorJustify::Repeat );
+ if ( bRepeat )
+ {
+ // "repeat" disables rotation (before constructing the font)
+ eAttrOrient = SvxCellOrientation::Standard;
+
+ // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled)
+ if ( bLineBreak )
+ eAttrHorJust = SvxCellHorJustify::Standard;
+ }
+
+ sal_Int16 nRot;
+ switch (eAttrOrient)
+ {
+ case SvxCellOrientation::Standard:
+ nRot = 0;
+ bRotated = pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue() != 0_deg100 &&
+ !bRepeat;
+ break;
+ case SvxCellOrientation::Stacked:
+ nRot = 0;
+ bRotated = false;
+ break;
+ case SvxCellOrientation::TopBottom:
+ nRot = 2700;
+ bRotated = false;
+ break;
+ case SvxCellOrientation::BottomUp:
+ nRot = 900;
+ bRotated = false;
+ break;
+ default:
+ OSL_FAIL("Invalid SvxCellOrientation value");
+ nRot = 0;
+ bRotated = false;
+ break;
+ }
+ aFont.SetOrientation( Degree10(nRot) );
+
+ // syntax mode
+
+ if (pOutput->mbSyntaxMode)
+ pOutput->SetSyntaxColor(&aFont, rCell);
+
+ // There is no cell attribute for kerning, default is kerning OFF, all
+ // kerning is stored at an EditText object that is drawn using EditEngine.
+ // See also matching kerning cases in ScColumn::GetNeededSize and
+ // ScColumn::GetOptimalColWidth.
+ aFont.SetKerning(FontKerning::NONE);
+
+ pDev->SetFont( aFont );
+ if ( pFmtDevice != pDev )
+ pFmtDevice->SetFont( aFont );
+
+ aMetric = pFmtDevice->GetFontMetric();
+
+ // if there is the leading 0 on a printer device, we have problems
+ // -> take metric from the screen (as for EditEngine!)
+ if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 )
+ {
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ MapMode aOld = pDefaultDev->GetMapMode();
+ pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() );
+ aMetric = pDefaultDev->GetFontMetric( aFont );
+ pDefaultDev->SetMapMode( aOld );
+ }
+
+ nAscentPixel = aMetric.GetAscent();
+ if ( bPixelToLogic )
+ nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height();
+
+ Color aULineColor( pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet ).GetColor() );
+ pDev->SetTextLineColor( aULineColor );
+
+ Color aOLineColor( pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet ).GetColor() );
+ pDev->SetOverlineColor( aOLineColor );
+
+ // number format
+
+ nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
+
+ // margins
+ pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
+ if ( eAttrHorJust == SvxCellHorJustify::Left || eAttrHorJust == SvxCellHorJustify::Right )
+ nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
+ else
+ nIndent = 0;
+
+ // "Shrink to fit"
+
+ bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
+
+ // at least the text size needs to be retrieved again
+ //! differentiate and do not get the text again from the number format?
+ maLastCell.clear();
+}
+
+void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet )
+{
+ nMaxDigitWidth = 0;
+ nSignWidth = 0;
+ nDotWidth = 0;
+ nExpWidth = 0;
+
+ // Is called, when the font variables do not change (!StringDiffer)
+
+ pPattern = pNew;
+ pCondSet = pSet; //! is this needed ???
+
+ // number format
+
+ sal_uLong nOld = nValueFormat;
+ nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet );
+
+ if (nValueFormat != nOld)
+ maLastCell.clear(); // always reformat
+
+ // margins
+
+ pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet );
+
+ if ( eAttrHorJust == SvxCellHorJustify::Left )
+ nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue();
+ else
+ nIndent = 0;
+
+ // "Shrink to fit"
+
+ bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
+}
+
+static bool SameValue( const ScRefCellValue& rCell, const ScRefCellValue& rOldCell )
+{
+ return rOldCell.getType() == CELLTYPE_VALUE && rCell.getType() == CELLTYPE_VALUE &&
+ rCell.getDouble() == rOldCell.getDouble();
+}
+
+bool ScDrawStringsVars::SetText( const ScRefCellValue& rCell )
+{
+ bool bChanged = false;
+
+ if (!rCell.isEmpty())
+ {
+ if (!SameValue(rCell, maLastCell))
+ {
+ maLastCell = rCell; // store cell
+
+ const Color* pColor;
+ sal_uLong nFormat = nValueFormat;
+ aString = ScCellFormat::GetString( rCell,
+ nFormat, &pColor,
+ *pOutput->mpDoc->GetFormatTable(),
+ *pOutput->mpDoc,
+ pOutput->mbShowNullValues,
+ pOutput->mbShowFormulas,
+ true );
+ if ( nFormat )
+ {
+ nRepeatPos = aString.indexOf( 0x1B );
+ if ( nRepeatPos != -1 )
+ {
+ if (nRepeatPos + 1 == aString.getLength())
+ nRepeatPos = -1;
+ else
+ {
+ nRepeatChar = aString[ nRepeatPos + 1 ];
+ // delete placeholder and char to repeat
+ aString = aString.replaceAt( nRepeatPos, 2, u"" );
+ // Do not cache/reuse a repeat-filled string, column
+ // widths or fonts or sizes may differ.
+ maLastCell.clear();
+ }
+ }
+ }
+ else
+ {
+ nRepeatPos = -1;
+ nRepeatChar = 0x0;
+ }
+ if (aString.getLength() > DRAWTEXT_MAX)
+ aString = aString.copy(0, DRAWTEXT_MAX);
+
+ if ( pColor && !pOutput->mbSyntaxMode && !( pOutput->mbUseStyleColor && pOutput->mbForceAutoColor ) )
+ {
+ OutputDevice* pDev = pOutput->mpDev;
+ aFont.SetColor(*pColor);
+ pDev->SetFont( aFont ); // only for output
+ bChanged = true;
+ maLastCell.clear(); // next time return here again
+ }
+
+ TextChanged();
+ }
+ // otherwise keep string/size
+ }
+ else
+ {
+ aString.clear();
+ maLastCell.clear();
+ aTextSize = Size(0,0);
+ nOriginalWidth = 0;
+ }
+
+ return bChanged;
+}
+
+void ScDrawStringsVars::SetHashText()
+{
+ SetAutoText("###");
+}
+
+void ScDrawStringsVars::RepeatToFill( tools::Long nColWidth )
+{
+ if ( nRepeatPos == -1 || nRepeatPos > aString.getLength() )
+ return;
+
+ tools::Long nCharWidth = GetFmtTextWidth(OUString(nRepeatChar));
+
+ if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) )
+ return;
+
+ // Are there restrictions on the cell type we should filter out here ?
+ tools::Long nTextWidth = aTextSize.Width();
+ if ( bPixelToLogic )
+ {
+ nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width();
+ nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width();
+ }
+
+ tools::Long nSpaceToFill = nColWidth - nTextWidth;
+ if ( nSpaceToFill <= nCharWidth )
+ return;
+
+ sal_Int32 nCharsToInsert = nSpaceToFill / nCharWidth;
+ OUStringBuffer aFill(nCharsToInsert);
+ comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar);
+ aString = aString.replaceAt( nRepeatPos, 0, aFill );
+ TextChanged();
+}
+
+bool ScDrawStringsVars::SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth )
+{
+ // #i113045# do the single-character width calculations in logic units
+ if (bPixelToLogic)
+ nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width();
+
+ CellType eType = rCell.getType();
+ if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA)
+ // must be a value or formula cell.
+ return false;
+
+ if (eType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas)
+ {
+ SetHashText(); // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#)
+ return true;
+ }
+ // If it's formula, the result must be a value.
+ if (!pFCell->IsValue())
+ return false;
+ }
+
+ sal_uLong nFormat = GetResultValueFormat();
+ if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
+ {
+ // Not 'General' number format. Set hash text and bail out.
+ SetHashText();
+ return true;
+ }
+
+ double fVal = rCell.getValue();
+
+ const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat);
+ if (!pNumFormat)
+ return false;
+
+ tools::Long nMaxDigit = GetMaxDigitWidth();
+ if (!nMaxDigit)
+ return false;
+
+ sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
+ {
+ OUString sTempOut(aString);
+ if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
+ {
+ aString = sTempOut;
+ // Failed to get output string. Bail out.
+ return false;
+ }
+ aString = sTempOut;
+ }
+ sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0;
+ sal_Int32 nLen = aString.getLength();
+ sal_Unicode cDecSep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator[0];
+ for( sal_Int32 i = 0; i < nLen; ++i )
+ {
+ sal_Unicode c = aString[i];
+ if (c == '-')
+ ++nSignCount;
+ else if (c == cDecSep)
+ ++nDecimalCount;
+ else if (c == 'E')
+ ++nExpCount;
+ }
+
+ // #i112250# A small value might be formatted as "0" when only counting the digits,
+ // but fit into the column when considering the smaller width of the decimal separator.
+ if (aString == "0" && fVal != 0.0)
+ nDecimalCount = 1;
+
+ if (nDecimalCount)
+ nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount;
+ if (nSignCount)
+ nWidth += (nMaxDigit - GetSignWidth()) * nSignCount;
+ if (nExpCount)
+ nWidth += (nMaxDigit - GetExpWidth()) * nExpCount;
+
+ if (nDecimalCount || nSignCount || nExpCount)
+ {
+ // Re-calculate.
+ nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit);
+ OUString sTempOut(aString);
+ if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut))
+ {
+ aString = sTempOut;
+ // Failed to get output string. Bail out.
+ return false;
+ }
+ aString = sTempOut;
+ }
+
+ tools::Long nActualTextWidth = GetFmtTextWidth(aString);
+ if (nActualTextWidth > nWidth)
+ {
+ // Even after the decimal adjustment the text doesn't fit. Give up.
+ SetHashText();
+ return true;
+ }
+
+ TextChanged();
+ maLastCell.clear(); // #i113022# equal cell and format in another column may give different string
+ return false;
+}
+
+void ScDrawStringsVars::SetAutoText( const OUString& rAutoText )
+{
+ aString = rAutoText;
+
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+ aTextSize.setWidth( GetFmtTextWidth( aString ) );
+ aTextSize.setHeight( pFmtDevice->GetTextHeight() );
+
+ if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
+ {
+ double fMul = pOutput->GetStretch();
+ aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
+ }
+
+ aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
+ if ( GetOrient() != SvxCellOrientation::Standard )
+ {
+ tools::Long nTemp = aTextSize.Height();
+ aTextSize.setHeight( aTextSize.Width() );
+ aTextSize.setWidth( nTemp );
+ }
+
+ nOriginalWidth = aTextSize.Width();
+ if ( bPixelToLogic )
+ aTextSize = pRefDevice->LogicToPixel( aTextSize );
+
+ maLastCell.clear(); // the same text may fit in the next cell
+}
+
+tools::Long ScDrawStringsVars::GetMaxDigitWidth()
+{
+ if (nMaxDigitWidth > 0)
+ return nMaxDigitWidth;
+
+ for (char i = 0; i < 10; ++i)
+ {
+ char cDigit = '0' + i;
+ // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached.
+ tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit));
+ nMaxDigitWidth = ::std::max(nMaxDigitWidth, n);
+ }
+ return nMaxDigitWidth;
+}
+
+tools::Long ScDrawStringsVars::GetSignWidth()
+{
+ if (nSignWidth > 0)
+ return nSignWidth;
+
+ nSignWidth = pOutput->pFmtDevice->GetTextWidth(OUString('-'));
+ return nSignWidth;
+}
+
+tools::Long ScDrawStringsVars::GetDotWidth()
+{
+ if (nDotWidth > 0)
+ return nDotWidth;
+
+ const OUString& sep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator;
+ nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep);
+ return nDotWidth;
+}
+
+tools::Long ScDrawStringsVars::GetExpWidth()
+{
+ if (nExpWidth > 0)
+ return nExpWidth;
+
+ nExpWidth = pOutput->pFmtDevice->GetTextWidth(OUString('E'));
+ return nExpWidth;
+}
+
+tools::Long ScDrawStringsVars::GetFmtTextWidth( const OUString& rString )
+{
+ return pOutput->pFmtDevice->GetTextWidth( rString, 0, -1, nullptr, GetLayoutGlyphs( rString ));
+}
+
+void ScDrawStringsVars::TextChanged()
+{
+ OutputDevice* pRefDevice = pOutput->mpRefDevice;
+ OutputDevice* pFmtDevice = pOutput->pFmtDevice;
+ aTextSize.setWidth( GetFmtTextWidth( aString ) );
+ aTextSize.setHeight( pFmtDevice->GetTextHeight() );
+
+ if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER )
+ {
+ double fMul = pOutput->GetStretch();
+ aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) );
+ }
+
+ aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() );
+ if ( GetOrient() != SvxCellOrientation::Standard )
+ {
+ tools::Long nTemp = aTextSize.Height();
+ aTextSize.setHeight( aTextSize.Width() );
+ aTextSize.setWidth( nTemp );
+ }
+
+ nOriginalWidth = aTextSize.Width();
+ if ( bPixelToLogic )
+ aTextSize = pRefDevice->LogicToPixel( aTextSize );
+}
+
+bool ScDrawStringsVars::HasEditCharacters() const
+{
+ for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx)
+ {
+ switch(aString[nIdx])
+ {
+ case CHAR_NBSP:
+ // tdf#122676: Ignore CHAR_NBSP (this is thousand separator in any number)
+ // if repeat character is set
+ if (nRepeatPos < 0)
+ return true;
+ break;
+ case CHAR_SHY:
+ case CHAR_ZWSP:
+ case CHAR_LRM:
+ case CHAR_RLM:
+ case CHAR_NBHY:
+ case CHAR_WJ:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+double ScOutputData::GetStretch() const
+{
+ if ( mpRefDevice->IsMapModeEnabled() )
+ {
+ // If a non-trivial MapMode is set, its scale is now already
+ // taken into account in the OutputDevice's font handling
+ // (OutputDevice::ImplNewFont, see #95414#).
+ // The old handling below is only needed for pixel output.
+ return 1.0;
+ }
+
+ // calculation in double is faster than Fraction multiplication
+ // and doesn't overflow
+
+ if ( mpRefDevice == pFmtDevice )
+ {
+ MapMode aOld = mpRefDevice->GetMapMode();
+ return static_cast<double>(aOld.GetScaleY()) / static_cast<double>(aOld.GetScaleX()) * static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
+ }
+ else
+ {
+ // when formatting for printer, device map mode has already been taken care of
+ return static_cast<double>(aZoomY) / static_cast<double>(aZoomX);
+ }
+}
+
+// output strings
+
+static void lcl_DoHyperlinkResult( const OutputDevice* pDev, const tools::Rectangle& rRect, ScRefCellValue& rCell )
+{
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
+
+ OUString aURL;
+ OUString aCellText;
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if ( pFCell->IsHyperLinkCell() )
+ pFCell->GetURLResult( aURL, aCellText );
+ }
+
+ if ( !aURL.isEmpty() && pPDFData )
+ {
+ vcl::PDFExtOutDevBookmarkEntry aBookmark;
+ aBookmark.nLinkId = pPDFData->CreateLink(rRect, aCellText);
+ aBookmark.aBookmark = aURL;
+ std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks();
+ rBookmarks.push_back( aBookmark );
+ }
+}
+
+void ScOutputData::SetSyntaxColor( vcl::Font* pFont, const ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ pFont->SetColor(*mxValueColor);
+ break;
+ case CELLTYPE_STRING:
+ pFont->SetColor(*mxTextColor);
+ break;
+ case CELLTYPE_FORMULA:
+ pFont->SetColor(*mxFormulaColor);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+static void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor )
+{
+ ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 );
+ SfxItemSet aSet( rEngine.GetEmptyItemSet() );
+ aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) );
+ rEngine.QuickSetAttribs( aSet, aSel );
+ // function is called with update mode set to FALSE
+}
+
+void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, const ScRefCellValue& rCell )
+{
+ Color aColor;
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ aColor = *mxValueColor;
+ break;
+ case CELLTYPE_STRING:
+ aColor = *mxTextColor;
+ break;
+ case CELLTYPE_FORMULA:
+ aColor = *mxFormulaColor;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ lcl_SetEditColor( rEngine, aColor );
+}
+
+bool ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY,
+ SCCOL& rOverX, SCROW& rOverY,
+ bool bVisRowChanged )
+{
+ bool bDoMerge = false;
+ bool bIsLeft = ( nX == nVisX1 );
+ bool bIsTop = ( nY == nVisY1 ) || bVisRowChanged;
+
+ bool bHOver;
+ bool bVOver;
+ bool bHidden;
+
+ if (!mpDoc->ColHidden(nX, nTab) && nX >= nX1 && nX <= nX2
+ && !mpDoc->RowHidden(nY, nTab) && nY >= nY1 && nY <= nY2)
+ {
+ ScCellInfo* pInfo = &pRowInfo[nArrY].cellInfo(nX);
+ bHOver = pInfo->bHOverlapped;
+ bVOver = pInfo->bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap2 = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG)->GetValue();
+ bHOver = bool(nOverlap2 & ScMF::Hor);
+ bVOver = bool(nOverlap2 & ScMF::Ver);
+ }
+
+ if ( bHOver && bVOver )
+ bDoMerge = bIsLeft && bIsTop;
+ else if ( bHOver )
+ bDoMerge = bIsLeft;
+ else if ( bVOver )
+ bDoMerge = bIsTop;
+
+ rOverX = nX;
+ rOverY = nY;
+
+ while (bHOver) // nY constant
+ {
+ --rOverX;
+ bHidden = mpDoc->ColHidden(rOverX, nTab);
+ if ( !bDoMerge && !bHidden )
+ return false;
+
+ if (rOverX >= nX1 && !bHidden)
+ {
+ bHOver = pRowInfo[nArrY].cellInfo(rOverX).bHOverlapped;
+ bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap = mpDoc->GetAttr(rOverX, rOverY, nTab, ATTR_MERGE_FLAG)->GetValue();
+ bHOver = bool(nOverlap & ScMF::Hor);
+ bVOver = bool(nOverlap & ScMF::Ver);
+ }
+ }
+
+ while (bVOver)
+ {
+ --rOverY;
+ bHidden = mpDoc->RowHidden(rOverY, nTab);
+ if ( !bDoMerge && !bHidden )
+ return false;
+
+ if (nArrY>0)
+ --nArrY; // local copy !
+
+ if (rOverX >= nX1 && rOverY >= nY1 &&
+ !mpDoc->ColHidden(rOverX, nTab) &&
+ !mpDoc->RowHidden(rOverY, nTab) &&
+ pRowInfo[nArrY].nRowNo == rOverY)
+ {
+ bVOver = pRowInfo[nArrY].cellInfo(rOverX).bVOverlapped;
+ }
+ else
+ {
+ ScMF nOverlap = mpDoc->GetAttr( rOverX, rOverY, nTab, ATTR_MERGE_FLAG )->GetValue();
+ bVOver = bool(nOverlap & ScMF::Ver);
+ }
+ }
+
+ return true;
+}
+
+static bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr* pNewPattern )
+{
+ OSL_ENSURE( pNewPattern, "pNewPattern" );
+
+ if ( SfxPoolItem::areSame( pNewPattern, rpOldPattern ) )
+ return false;
+ else if ( !rpOldPattern )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT ), rpOldPattern->GetItem( ATTR_FONT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT ), rpOldPattern->GetItem( ATTR_CJK_FONT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT ), rpOldPattern->GetItem( ATTR_CTL_FONT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_FONT_POSTURE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_UNDERLINE ), rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_OVERLINE ), rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WORDLINE ), rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ), rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CONTOUR ), rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_SHADOWED ), rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_COLOR ), rpOldPattern->GetItem( ATTR_FONT_COLOR ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_STACKED ), rpOldPattern->GetItem( ATTR_STACKED ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_LINEBREAK ), rpOldPattern->GetItem( ATTR_LINEBREAK ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_MARGIN ), rpOldPattern->GetItem( ATTR_MARGIN ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_ROTATE_VALUE ), rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FORBIDDEN_RULES ), rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ), rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_RELIEF ), rpOldPattern->GetItem( ATTR_FONT_RELIEF ) ) )
+ return true;
+ else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_BACKGROUND ), rpOldPattern->GetItem( ATTR_BACKGROUND ) ) )
+ return true; // needed with automatic text color
+ else
+ {
+ rpOldPattern = pNewPattern;
+ return false;
+ }
+}
+
+static void lcl_CreateInterpretProgress( bool& bProgress, ScDocument* pDoc,
+ const ScFormulaCell* pFCell )
+{
+ if ( !bProgress && pFCell->GetDirty() )
+ {
+ ScProgress::CreateInterpretProgress( pDoc );
+ bProgress = true;
+ }
+}
+
+static bool IsAmbiguousScript( SvtScriptType nScript )
+{
+ return ( nScript != SvtScriptType::LATIN &&
+ nScript != SvtScriptType::ASIAN &&
+ nScript != SvtScriptType::COMPLEX );
+}
+
+bool ScOutputData::IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY )
+{
+ // pThisRowInfo may be NULL
+
+ bool bEmpty;
+ if ( pThisRowInfo && nX <= nX2 )
+ bEmpty = pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
+ else
+ {
+ ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
+ bEmpty = aCell.isEmpty();
+ }
+
+ if ( !bEmpty && ( nX < nX1 || nX > nX2 || !pThisRowInfo ) )
+ {
+ // for the range nX1..nX2 in RowInfo, cell protection attribute is already evaluated
+ // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun)
+
+ bool bIsPrint = ( eType == OUTTYPE_PRINTER );
+
+ if ( bIsPrint || bTabProtected )
+ {
+ const ScProtectionAttr* pAttr =
+ mpDoc->GetEffItem( nX, nY, nTab, ATTR_PROTECTION );
+ if ( bIsPrint && pAttr->GetHidePrint() )
+ bEmpty = true;
+ else if ( bTabProtected )
+ {
+ if ( pAttr->GetHideCell() )
+ bEmpty = true;
+ else if ( mbShowFormulas && pAttr->GetHideFormula() )
+ {
+ if (mpDoc->GetCellType(ScAddress(nX, nY, nTab)) == CELLTYPE_FORMULA)
+ bEmpty = true;
+ }
+ }
+ }
+ }
+ return bEmpty;
+}
+
+void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScRefCellValue& rCell )
+{
+ rCell.assign(*mpDoc, ScAddress(nCol, nRow, nTabP));
+ if (!rCell.isEmpty() && IsEmptyCellText(nullptr, nCol, nRow))
+ rCell.clear();
+}
+
+bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY )
+{
+ // apply the same logic here as in DrawStrings/DrawEdit:
+ // Stop at non-empty or merged or overlapped cell,
+ // where a note is empty as well as a cell that's hidden by protection settings
+
+ ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, nTab));
+ if (!aCell.isEmpty() && !IsEmptyCellText(nullptr, nX, nY))
+ return false;
+
+ const ScPatternAttr* pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ return !(pPattern->GetItem(ATTR_MERGE).IsMerged() ||
+ pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped());
+}
+
+// nX, nArrY: loop variables from DrawStrings / DrawEdit
+// nPosX, nPosY: corresponding positions for nX, nArrY
+// nCellX, nCellY: position of the cell that contains the text
+// nNeeded: Text width, including margin
+// rPattern: cell format at nCellX, nCellY
+// nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings
+// bCellIsValue: if set, don't extend into empty cells
+// bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set)
+// bOverwrite: if set, also extend into non-empty cells (for rotated text)
+// rParam output: various area parameters.
+
+void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::Long nPosY,
+ SCCOL nCellX, SCROW nCellY, tools::Long nNeeded,
+ const ScPatternAttr& rPattern,
+ sal_uInt16 nHorJustify, bool bCellIsValue,
+ bool bBreak, bool bOverwrite,
+ OutputAreaParam& rParam )
+{
+ // rThisRowInfo may be for a different row than nCellY, is still used for clip marks
+ RowInfo& rThisRowInfo = pRowInfo[nArrY];
+
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nCellPosX = nPosX; // find nCellX position, starting at nX/nPosX
+ SCCOL nCompCol = nX;
+ while ( nCellX > nCompCol )
+ {
+ //! extra member function for width?
+ tools::Long nColWidth = ( nCompCol <= nX2 ) ?
+ pRowInfo[0].basicCellInfo(nCompCol).nWidth :
+ static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
+ nCellPosX += nColWidth * nLayoutSign;
+ ++nCompCol;
+ }
+ while ( nCellX < nCompCol )
+ {
+ --nCompCol;
+ tools::Long nColWidth = ( nCompCol <= nX2 ) ?
+ pRowInfo[0].basicCellInfo(nCompCol).nWidth :
+ static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, nTab ) * mnPPTX );
+ nCellPosX -= nColWidth * nLayoutSign;
+ }
+
+ tools::Long nCellPosY = nPosY; // find nCellY position, starting at nArrY/nPosY
+ SCSIZE nCompArr = nArrY;
+ SCROW nCompRow = pRowInfo[nCompArr].nRowNo;
+ while ( nCellY > nCompRow )
+ {
+ if ( nCompArr + 1 < nArrCount )
+ {
+ nCellPosY += pRowInfo[nCompArr].nHeight;
+ ++nCompArr;
+ nCompRow = pRowInfo[nCompArr].nRowNo;
+ }
+ else
+ {
+ sal_uInt16 nDocHeight = mpDoc->GetRowHeight( nCompRow, nTab );
+ if ( nDocHeight )
+ nCellPosY += static_cast<tools::Long>( nDocHeight * mnPPTY );
+ ++nCompRow;
+ }
+ }
+ nCellPosY -= mpDoc->GetScaledRowHeight( nCellY, nCompRow-1, nTab, mnPPTY );
+
+ const ScMergeAttr* pMerge = &rPattern.GetItem( ATTR_MERGE );
+ bool bMerged = pMerge->IsMerged();
+ tools::Long nMergeCols = pMerge->GetColMerge();
+ if ( nMergeCols == 0 )
+ nMergeCols = 1;
+ tools::Long nMergeRows = pMerge->GetRowMerge();
+ if ( nMergeRows == 0 )
+ nMergeRows = 1;
+
+ tools::Long nMergeSizeX = 0;
+ for ( tools::Long i=0; i<nMergeCols; i++ )
+ {
+ tools::Long nColWidth = ( nCellX+i <= nX2 ) ?
+ pRowInfo[0].basicCellInfo(nCellX+i).nWidth :
+ static_cast<tools::Long>( mpDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), nTab ) * mnPPTX );
+ nMergeSizeX += nColWidth;
+ }
+ tools::Long nMergeSizeY = 0;
+ short nDirect = 0;
+ if ( rThisRowInfo.nRowNo == nCellY )
+ {
+ // take first row's height from row info
+ nMergeSizeY += rThisRowInfo.nHeight;
+ nDirect = 1; // skip in loop
+ }
+ // following rows always from document
+ nMergeSizeY += mpDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, nTab, mnPPTY);
+
+ --nMergeSizeX; // leave out the grid horizontally, also for alignment (align between grid lines)
+
+ rParam.mnColWidth = nMergeSizeX; // store the actual column width.
+ rParam.mnLeftClipLength = rParam.mnRightClipLength = 0;
+
+ // construct the rectangles using logical left/right values (justify is called at the end)
+
+ // rAlignRect is the single cell or merged area, used for alignment.
+
+ rParam.maAlignRect.SetLeft( nCellPosX );
+ rParam.maAlignRect.SetRight( nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign );
+ rParam.maAlignRect.SetTop( nCellPosY );
+ rParam.maAlignRect.SetBottom( nCellPosY + nMergeSizeY - 1 );
+
+ // rClipRect is all cells that are used for output.
+ // For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used.
+
+ rParam.maClipRect = rParam.maAlignRect;
+ if ( nNeeded > nMergeSizeX )
+ {
+ SvxCellHorJustify eHorJust = static_cast<SvxCellHorJustify>(nHorJustify);
+
+ tools::Long nMissing = nNeeded - nMergeSizeX;
+ tools::Long nLeftMissing = 0;
+ tools::Long nRightMissing = 0;
+ switch ( eHorJust )
+ {
+ case SvxCellHorJustify::Left:
+ nRightMissing = nMissing;
+ break;
+ case SvxCellHorJustify::Right:
+ nLeftMissing = nMissing;
+ break;
+ case SvxCellHorJustify::Center:
+ nLeftMissing = nMissing / 2;
+ nRightMissing = nMissing - nLeftMissing;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // nLeftMissing, nRightMissing are logical, eHorJust values are visual
+ if ( bLayoutRTL )
+ ::std::swap( nLeftMissing, nRightMissing );
+
+ SCCOL nRightX = nCellX;
+ SCCOL nLeftX = nCellX;
+ if ( !bMerged && !bCellIsValue && !bBreak )
+ {
+ // look for empty cells into which the text can be extended
+
+ while ( nRightMissing > 0 && nRightX < mpDoc->MaxCol() && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) )
+ {
+ ++nRightX;
+ tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nRightX, nTab ) * mnPPTX );
+ nRightMissing -= nAdd;
+ rParam.maClipRect.AdjustRight(nAdd * nLayoutSign );
+
+ if ( rThisRowInfo.nRowNo == nCellY && nRightX >= nX1 && nRightX <= nX2 )
+ rThisRowInfo.cellInfo(nRightX-1).bHideGrid = true;
+ }
+
+ while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) )
+ {
+ if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= nX1 && nLeftX <= nX2 )
+ rThisRowInfo.cellInfo(nLeftX-1).bHideGrid = true;
+
+ --nLeftX;
+ tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nLeftX, nTab ) * mnPPTX );
+ nLeftMissing -= nAdd;
+ rParam.maClipRect.AdjustLeft( -(nAdd * nLayoutSign) );
+ }
+ }
+
+ // Set flag and reserve space for clipping mark triangle,
+ // even if rThisRowInfo isn't for nCellY (merged cells).
+ if ( nRightMissing > 0 && bMarkClipped && nRightX >= nX1 && nRightX <= nX2 && !bBreak && !bCellIsValue )
+ {
+ rThisRowInfo.cellInfo(nRightX).nClipMark |= ScClipMark::Right;
+ bAnyClipped = true;
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ rParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
+ }
+ if ( nLeftMissing > 0 && bMarkClipped && nLeftX >= nX1 && nLeftX <= nX2 && !bBreak && !bCellIsValue )
+ {
+ rThisRowInfo.cellInfo(nLeftX).nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ rParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign );
+ }
+
+ rParam.mbLeftClip = ( nLeftMissing > 0 );
+ rParam.mbRightClip = ( nRightMissing > 0 );
+ rParam.mnLeftClipLength = nLeftMissing;
+ rParam.mnRightClipLength = nRightMissing;
+ }
+ else
+ {
+ rParam.mbLeftClip = rParam.mbRightClip = false;
+
+ // leave space for AutoFilter on screen
+ // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize)
+
+ if ( eType==OUTTYPE_WINDOW &&
+ ( rPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & (ScMF::Auto|ScMF::Button|ScMF::ButtonPopup) ) &&
+ ( !bBreak || mpRefDevice == pFmtDevice ) )
+ {
+ // filter drop-down width depends on row height
+ double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
+ fZoom = fZoom > 1.0 ? fZoom : 1.0;
+ const tools::Long nFilter = fZoom * DROPDOWN_BITMAP_SIZE;
+ bool bFit = ( nNeeded + nFilter <= nMergeSizeX );
+ if ( bFit )
+ {
+ // content fits even in the remaining area without the filter button
+ // -> align within that remaining area
+
+ rParam.maAlignRect.AdjustRight( -(nFilter * nLayoutSign) );
+ rParam.maClipRect.AdjustRight( -(nFilter * nLayoutSign) );
+ }
+ }
+ }
+
+ // justify both rectangles for alignment calculation, use with DrawText etc.
+
+ rParam.maAlignRect.Normalize();
+ rParam.maClipRect.Normalize();
+}
+
+namespace {
+
+bool beginsWithRTLCharacter(const OUString& rStr)
+{
+ if (rStr.isEmpty())
+ return false;
+
+ switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0))
+ {
+ case i18n::DirectionProperty_RIGHT_TO_LEFT:
+ case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC:
+ case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING:
+ case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE:
+ return true;
+ default:
+ ;
+ }
+
+ return false;
+}
+
+}
+
+/** Get left, right or centered alignment from RTL context.
+
+ Does not return standard, block or repeat, for these the contextual left or
+ right alignment is returned.
+ */
+static SvxCellHorJustify getAlignmentFromContext( SvxCellHorJustify eInHorJust,
+ bool bCellIsValue, const OUString& rText,
+ const ScPatternAttr& rPattern, const SfxItemSet* pCondSet,
+ const ScDocument* pDoc, SCTAB nTab, const bool bNumberFormatIsText )
+{
+ SvxCellHorJustify eHorJustContext = eInHorJust;
+ bool bUseWritingDirection = false;
+ if (eInHorJust == SvxCellHorJustify::Standard)
+ {
+ // fdo#32530: Default alignment depends on value vs
+ // string, and the direction of the 1st letter.
+ if (beginsWithRTLCharacter( rText)) //If language is RTL
+ {
+ if (bCellIsValue)
+ eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
+ else
+ eHorJustContext = SvxCellHorJustify::Right;
+ }
+ else if (bCellIsValue) //If language is not RTL
+ eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right;
+ else
+ bUseWritingDirection = true;
+ }
+
+ if (bUseWritingDirection ||
+ eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat)
+ {
+ SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet);
+ if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB)
+ eHorJustContext = SvxCellHorJustify::Left;
+ else if (nDirection == SvxFrameDirection::Environment)
+ {
+ SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL");
+ // fdo#73588: The content of the cell must also
+ // begin with a RTL character to be right
+ // aligned; otherwise, it should be left aligned.
+ eHorJustContext = (pDoc && pDoc->IsLayoutRTL(nTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
+ }
+ else
+ eHorJustContext = SvxCellHorJustify::Right;
+ }
+ return eHorJustContext;
+}
+
+void ScOutputData::DrawStrings( bool bPixelToLogic )
+{
+ LayoutStrings(bPixelToLogic);
+}
+
+void ScOutputData::LayoutStrings(bool bPixelToLogic)
+{
+ bool bOrigIsInLayoutStrings = mpDoc->IsInLayoutStrings();
+ mpDoc->SetLayoutStrings(true);
+ OSL_ENSURE( mpDev == mpRefDevice ||
+ mpDev->GetMapMode().GetMapUnit() == mpRefDevice->GetMapMode().GetMapUnit(),
+ "LayoutStrings: different MapUnits ?!?!" );
+ vcl::text::ComplexTextLayoutFlags eTextLayout = mpDev->GetLayoutMode();
+ mpDev->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+
+ comphelper::ScopeGuard g([this, bOrigIsInLayoutStrings, eTextLayout] {
+ mpDoc->SetLayoutStrings(bOrigIsInLayoutStrings);
+ mpDev->SetLayoutMode(eTextLayout);
+ });
+
+ sc::IdleSwitch aIdleSwitch(*mpDoc, false);
+
+ // Try to limit interpreting to only visible cells. Calling e.g. IsValue()
+ // on a formula cell that needs interpreting would call Interpret()
+ // for the entire formula group, which could be large.
+ mpDoc->InterpretCellsIfNeeded( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ));
+
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(mpDev->GetExtOutDevData() );
+
+ ScDrawStringsVars aVars( this, bPixelToLogic );
+
+ bool bProgress = false;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ nInitPosX += nMirrorW - 1; // pixels
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ SCCOL nLastContentCol = mpDoc->MaxCol();
+ if ( nX2 < mpDoc->MaxCol() )
+ {
+ SCROW nEndRow;
+ mpDoc->GetCellArea(nTab, nLastContentCol, nEndRow);
+ }
+
+ SCCOL nLoopStartX = nX1;
+ if ( nX1 > 0 )
+ --nLoopStartX; // start before nX1 for rest of long text to the left
+
+ // variables for GetOutputArea
+ OutputAreaParam aAreaParam;
+ bool bCellIsValue = false;
+ tools::Long nNeededWidth = 0;
+ const ScPatternAttr* pPattern = nullptr;
+ const SfxItemSet* pCondSet = nullptr;
+ const ScPatternAttr* pOldPattern = nullptr;
+ const SfxItemSet* pOldCondSet = nullptr;
+ SvtScriptType nOldScript = SvtScriptType::NONE;
+
+ // alternative pattern instances in case we need to modify the pattern
+ // before processing the cell value.
+ std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns;
+
+ KernArray aDX;
+ tools::Long nPosY = nScrY;
+ for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ SCROW nY = pThisRowInfo->nRowNo;
+ if (pThisRowInfo->bChanged)
+ {
+ tools::Long nPosX = nInitPosX;
+ if ( nLoopStartX < nX1 )
+ nPosX -= pRowInfo[0].basicCellInfo(nLoopStartX).nWidth * nLayoutSign;
+ for (SCCOL nX=nLoopStartX; nX<=nX2; nX++)
+ {
+ bool bMergeEmpty = false;
+ const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ bool bEmpty = nX < nX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText;
+
+ SCCOL nCellX = nX; // position where the cell really starts
+ SCROW nCellY = nY;
+ bool bDoCell = false;
+ bool bUseEditEngine = false;
+
+ // Part of a merged cell?
+
+ bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped);
+ if ( bOverlapped )
+ {
+ bEmpty = true;
+
+ SCCOL nOverX; // start of the merged cells
+ SCROW nOverY;
+ bool bVisChanged = !pRowInfo[nArrY-1].bChanged;
+ if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged ))
+ {
+ nCellX = nOverX;
+ nCellY = nOverY;
+ bDoCell = true;
+ }
+ else
+ bMergeEmpty = true;
+ }
+
+ // Rest of a long text further to the left?
+
+ if ( bEmpty && !bMergeEmpty && nX < nX1 && !bOverlapped )
+ {
+ SCCOL nTempX=nX1;
+ while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
+ --nTempX;
+
+ if ( nTempX < nX1 &&
+ !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
+ !mpDoc->HasAttrib( nTempX,nY,nTab, nX1,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ nCellX = nTempX;
+ bDoCell = true;
+ }
+ }
+
+ // Rest of a long text further to the right?
+
+ if ( bEmpty && !bMergeEmpty && nX == nX2 && !bOverlapped )
+ {
+ // don't have to look further than nLastContentCol
+
+ SCCOL nTempX=nX;
+ while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
+ ++nTempX;
+
+ if ( nTempX > nX &&
+ !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
+ !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ nCellX = nTempX;
+ bDoCell = true;
+ }
+ }
+
+ // normal visible cell
+
+ if (!bEmpty)
+ bDoCell = true;
+
+ // don't output the cell that's being edited
+
+ if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
+ bDoCell = false;
+
+ // skip text in cell if data bar/icon set is set and only value selected
+ if ( bDoCell )
+ {
+ if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue)
+ bDoCell = false;
+ if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue)
+ bDoCell = false;
+ }
+
+ // output the cell text
+
+ ScRefCellValue aCell;
+ if (bDoCell)
+ {
+ if ( nCellY == nY && nCellX == nX && nCellX >= nX1 && nCellX <= nX2 )
+ aCell = pThisRowInfo->cellInfo(nCellX).maCell;
+ else
+ GetVisibleCell( nCellX, nCellY, nTab, aCell ); // get from document
+ if (aCell.isEmpty())
+ bDoCell = false;
+ else if (aCell.getType() == CELLTYPE_EDIT)
+ bUseEditEngine = true;
+ }
+
+ // Check if this cell is mis-spelled.
+ if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING)
+ {
+ if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY))
+ bUseEditEngine = true;
+ }
+
+ if (bDoCell && !bUseEditEngine)
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ {
+ ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
+ pPattern = rCellInfo.pPatternAttr;
+ pCondSet = rCellInfo.pConditionSet;
+
+ if ( !pPattern )
+ {
+ // #i68085# pattern from cell info for hidden columns is null,
+ // test for null is quicker than using column flags
+ pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
+ pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
+ }
+ }
+ else // get from document
+ {
+ pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
+ pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
+ }
+ if ( mpDoc->GetPreviewFont() || mpDoc->GetPreviewCellStyle() )
+ {
+ aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
+ ScPatternAttr* pAltPattern = aAltPatterns.back().get();
+ if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
+ {
+ pAltPattern->SetStyleSheet(pPreviewStyle);
+ }
+ else if ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab ) )
+ {
+ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) )
+ pAltPattern->GetItemSet().Put( *pItem );
+ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
+ pAltPattern->GetItemSet().Put( *pItem );
+ if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
+ pAltPattern->GetItemSet().Put( *pItem );
+ }
+ pPattern = pAltPattern;
+ }
+
+ if (aCell.hasNumeric() &&
+ pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue())
+ {
+ // Disable line break when the cell content is numeric.
+ aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern));
+ ScPatternAttr* pAltPattern = aAltPatterns.back().get();
+ ScLineBreakCell aLineBreak(false);
+ pAltPattern->GetItemSet().Put(aLineBreak);
+ pPattern = pAltPattern;
+ }
+
+ SvtScriptType nScript = mpDoc->GetCellScriptType(
+ ScAddress(nCellX, nCellY, nTab),
+ pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet));
+
+ if (nScript == SvtScriptType::NONE)
+ nScript = ScGlobal::GetDefaultScriptType();
+
+ if ( !SfxPoolItem::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet ||
+ nScript != nOldScript || mbSyntaxMode )
+ {
+ if ( StringDiffer(pOldPattern,pPattern) ||
+ pCondSet != pOldCondSet || nScript != nOldScript || mbSyntaxMode )
+ {
+ aVars.SetPattern(pPattern, pCondSet, aCell, nScript);
+ }
+ else
+ aVars.SetPatternSimple( pPattern, pCondSet );
+ pOldPattern = pPattern;
+ pOldCondSet = pCondSet;
+ nOldScript = nScript;
+ }
+
+ // use edit engine for rotated, stacked or mixed-script text
+ if ( aVars.GetOrient() == SvxCellOrientation::Stacked ||
+ aVars.IsRotated() || IsAmbiguousScript(nScript) )
+ bUseEditEngine = true;
+ }
+ if (bDoCell && !bUseEditEngine)
+ {
+ bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA);
+ if ( bFormulaCell )
+ lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula());
+ if ( aVars.SetText(aCell) )
+ pOldPattern = nullptr;
+ bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult());
+ }
+ tools::Long nTotalMargin = 0;
+ SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard;
+ if (bDoCell && !bUseEditEngine)
+ {
+ CellType eCellType = aCell.getType();
+ bCellIsValue = ( eCellType == CELLTYPE_VALUE );
+ if ( eCellType == CELLTYPE_FORMULA )
+ {
+ ScFormulaCell* pFCell = aCell.getFormula();
+ bCellIsValue = pFCell->IsRunning() || pFCell->IsValue();
+ }
+
+ const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
+ eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(),
+ *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText );
+
+ bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block );
+ // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats
+ // Must be synchronized with ScColumn::GetNeededSize()
+ SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
+ if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER))
+ bBreak = false;
+
+ bool bRepeat = aVars.IsRepeat() && !bBreak;
+ bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat;
+
+ nTotalMargin =
+ static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) +
+ static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX);
+
+ nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin;
+
+ // GetOutputArea gives justified rectangles
+ GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth,
+ *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ bCellIsValue || bRepeat || bShrink, bBreak, false,
+ aAreaParam );
+
+ aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin );
+ if ( bShrink )
+ {
+ if ( aVars.GetOrient() != SvxCellOrientation::Standard )
+ {
+ // Only horizontal scaling is handled here.
+ // DrawEdit is used to vertically scale 90 deg rotated text.
+ bUseEditEngine = true;
+ }
+ else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal
+ {
+ tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
+ tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin
+
+ if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats)
+ {
+ tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
+
+ aVars.SetShrinkScale( nScale, nOldScript );
+ tools::Long nNewSize = aVars.GetTextSize().Width();
+
+ sal_uInt16 nShrinkAgain = 0;
+ while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
+ {
+ // If the text is still too large, reduce the scale again by 10%, until it fits,
+ // at most 7 times (it's less than 50% of the calculated scale then).
+
+ nScale = ( nScale * 9 ) / 10;
+ aVars.SetShrinkScale( nScale, nOldScript );
+ nNewSize = aVars.GetTextSize().Width();
+ ++nShrinkAgain;
+ }
+ // If even at half the size the font still isn't rendered smaller,
+ // fall back to normal clipping (showing ### for numbers).
+ if ( nNewSize <= nAvailable )
+ {
+ // Reset relevant parameters.
+ aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
+ aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
+ }
+
+ pOldPattern = nullptr;
+ }
+ }
+ }
+
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip )
+ {
+ tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin;
+ tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin
+ // When formatting for the printer, the text sizes don't always add up.
+ // Round down (too few repetitions) rather than exceeding the cell size then:
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUString aCellStr = aVars.GetString();
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+ aVars.SetAutoText( aRepeated.makeStringAndClear() );
+ }
+ }
+ }
+
+ // use edit engine if automatic line breaks are needed
+ if ( bBreak )
+ {
+ if ( aVars.GetOrient() == SvxCellOrientation::Standard )
+ bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip );
+ else
+ {
+ tools::Long nHeight = aVars.GetTextSize().Height() +
+ static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) +
+ static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY);
+ bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() );
+ }
+ }
+ if (!bUseEditEngine)
+ {
+ bUseEditEngine =
+ aVars.GetHorJust() == SvxCellHorJustify::Block &&
+ aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute;
+ }
+ }
+ if (bUseEditEngine)
+ {
+ // mark the cell in ScCellInfo to be drawn in DrawEdit:
+ // Cells to the left are marked directly, cells to the
+ // right are handled by the flag for nX2
+ SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2;
+ RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0];
+ pMarkRowInfo->basicCellInfo(nMarkX).bEditEngine = true;
+ bDoCell = false; // don't draw here
+ }
+ if ( bDoCell )
+ {
+ if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ bool bHasHashText = false;
+ if (mbShowFormulas)
+ {
+ aVars.SetHashText();
+ bHasHashText = true;
+ }
+ else
+ // Adjust the decimals to fit the available column width.
+ bHasHashText = aVars.SetTextToWidthOrHash( aCell, aAreaParam.mnColWidth - nTotalMargin );
+
+ if ( bHasHashText )
+ {
+ tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX;
+
+ if ( eOutHorJust == SvxCellHorJustify::Left )
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
+ }
+ else if ( eOutHorJust == SvxCellHorJustify::Right )
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign);
+ }
+ else
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 )
+ {
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right;
+ pRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left;
+ }
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) );
+ aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign);
+ }
+ }
+
+ nNeededWidth = aVars.GetTextSize().Width() +
+ static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) +
+ static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX );
+ if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() )
+ {
+ // Cell value is no longer clipped. Reset relevant parameters.
+ aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false;
+ aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0;
+ }
+ }
+
+ tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added
+ tools::Long nJustPosY = aAreaParam.maAlignRect.Top();
+ tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight();
+
+ bool bOutside = ( aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW );
+ // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip
+ bool bVClip = AdjustAreaParamClipRect(aAreaParam);
+ bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip;
+
+ // check horizontal space
+
+ if ( !bOutside )
+ {
+ bool bRightAdjusted = false; // to correct text width calculation later
+ switch (eOutHorJust)
+ {
+ case SvxCellHorJustify::Left:
+ nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX );
+ break;
+ case SvxCellHorJustify::Right:
+ nJustPosX += nAvailWidth - aVars.GetTextSize().Width() -
+ static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX );
+ bRightAdjusted = true;
+ break;
+ case SvxCellHorJustify::Center:
+ nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() +
+ static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) -
+ static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2;
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ tools::Long nTestClipHeight = aVars.GetTextSize().Height();
+ switch (aVars.GetVerJust())
+ {
+ case SvxCellVerJustify::Top:
+ case SvxCellVerJustify::Block:
+ {
+ tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
+ nJustPosY += nTop;
+ nTestClipHeight += nTop;
+ }
+ break;
+ case SvxCellVerJustify::Bottom:
+ {
+ tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
+ nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot;
+ nTestClipHeight += nBot;
+ }
+ break;
+ case SvxCellVerJustify::Center:
+ {
+ tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY );
+ tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY );
+ nJustPosY += ( nOutHeight + nTop -
+ aVars.GetTextSize().Height() - nBot ) / 2;
+ nTestClipHeight += std::abs( nTop - nBot );
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if ( nTestClipHeight > nOutHeight )
+ {
+ // no vertical clipping when printing cells with optimal height,
+ // except when font size is from conditional formatting.
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( nCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( aVars.HasCondHeight() ) )
+ bVClip = true;
+ }
+
+ if ( bHClip || bVClip )
+ {
+ // only clip the affected dimension so that not all right-aligned
+ // columns are cut off when performing a non-proportional resize
+ if (!bHClip)
+ {
+ aAreaParam.maClipRect.SetLeft( nScrX );
+ aAreaParam.maClipRect.SetRight( nScrX+nScrW );
+ }
+ if (!bVClip)
+ {
+ aAreaParam.maClipRect.SetTop( nScrY );
+ aAreaParam.maClipRect.SetBottom( nScrY+nScrH );
+ }
+
+ // aClipRect is not used after SetClipRegion/IntersectClipRegion,
+ // so it can be modified here
+ if (bPixelToLogic)
+ aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect );
+
+ if (bMetaFile)
+ {
+ mpDev->Push();
+ mpDev->IntersectClipRegion( aAreaParam.maClipRect );
+ }
+ else
+ mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
+ }
+
+ Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation
+
+ switch (aVars.GetOrient())
+ {
+ case SvxCellOrientation::Standard:
+ nJustPosY += aVars.GetAscent();
+ break;
+ case SvxCellOrientation::TopBottom:
+ nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent();
+ break;
+ case SvxCellOrientation::BottomUp:
+ nJustPosY += aVars.GetTextSize().Height();
+ nJustPosX += aVars.GetAscent();
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ // When clipping, the visible part is now completely defined by the alignment,
+ // there's no more special handling to show the right part of RTL text.
+
+ Point aDrawTextPos( nJustPosX, nJustPosY );
+ if ( bPixelToLogic )
+ {
+ // undo text width adjustment in pixels
+ if (bRightAdjusted)
+ aDrawTextPos.AdjustX(aVars.GetTextSize().Width() );
+
+ aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos );
+
+ // redo text width adjustment in logic units
+ if (bRightAdjusted)
+ aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) );
+ }
+
+ // in Metafiles always use DrawTextArray to ensure that positions are
+ // recorded (for non-proportional resize):
+
+ const OUString& aString = aVars.GetString();
+ if (!aString.isEmpty())
+ {
+ // If the string is clipped, make it shorter for
+ // better performance since drawing by HarfBuzz is
+ // quite expensive especially for long string.
+
+ OUString aShort = aString;
+
+ // But never fiddle with numeric values.
+ // (Which was the cause of tdf#86024).
+ // The General automatic format output takes
+ // care of this, or fixed width numbers either fit
+ // or display as ###.
+ if (!bCellIsValue)
+ {
+ double fVisibleRatio = 1.0;
+ double fTextWidth = aVars.GetTextSize().Width();
+ sal_Int32 nTextLen = aString.getLength();
+ if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0)
+ {
+ fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth;
+ if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
+ {
+ // Only show the left-end segment.
+ sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
+ aShort = aShort.copy(0, nShortLen);
+ }
+ }
+ else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0)
+ {
+ fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth;
+ if (0.0 < fVisibleRatio && fVisibleRatio < 1.0)
+ {
+ // Only show the right-end segment.
+ sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1;
+ aShort = aShort.copy(nTextLen-nShortLen);
+
+ // Adjust the text position after shortening of the string.
+ double fShortWidth = aVars.GetFmtTextWidth(aShort);
+ double fOffset = fTextWidth - fShortWidth;
+ aDrawTextPos.Move(fOffset, 0);
+ }
+ }
+ }
+
+ if (bMetaFile || pFmtDevice != mpDev || aZoomX != aZoomY)
+ {
+ size_t nLen = aShort.getLength();
+ if (aDX.size() < nLen)
+ aDX.resize(nLen, 0);
+
+ pFmtDevice->GetTextArray(aShort, &aDX);
+
+ if ( !mpRefDevice->GetConnectMetaFile() ||
+ mpRefDevice->GetOutDevType() == OUTDEV_PRINTER )
+ {
+ double fMul = GetStretch();
+ for (size_t i = 0; i < nLen; ++i)
+ aDX.set(i, static_cast<sal_Int32>(aDX[i] / fMul + 0.5));
+ }
+
+ mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen);
+ }
+ else
+ {
+ mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr,
+ aVars.GetLayoutGlyphs(aShort));
+ }
+ }
+
+ if ( bHClip || bVClip )
+ {
+ if (bMetaFile)
+ mpDev->Pop();
+ else
+ mpDev->SetClipRegion();
+ }
+
+ // PDF: whole-cell hyperlink from formula?
+ bool bHasURL = pPDFData && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell();
+ if (bHasURL)
+ {
+ tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() );
+ lcl_DoHyperlinkResult(mpDev, aURLRect, aCell);
+ }
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nPosY += pRowInfo[nArrY].nHeight;
+ }
+ if ( bProgress )
+ ScProgress::DeleteInterpretProgress();
+}
+
+void ScOutputData::SetRefDevice( OutputDevice* pRDev )
+{
+ mpRefDevice = pFmtDevice = pRDev;
+ // reset EditEngine because it depends on pFmtDevice and mpRefDevice
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::SetFmtDevice( OutputDevice* pRDev )
+{
+ pFmtDevice = pRDev;
+ // reset EditEngine because it depends on pFmtDevice
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::SetUseStyleColor( bool bSet )
+{
+ mbUseStyleColor = bSet;
+ // reset EditEngine because it depends on mbUseStyleColor
+ mxOutputEditEngine.reset();
+}
+
+void ScOutputData::InitOutputEditEngine()
+{
+ if (!mxOutputEditEngine)
+ {
+ mxOutputEditEngine = std::make_unique<ScFieldEditEngine>(mpDoc, mpDoc->GetEnginePool());
+ mxOutputEditEngine->SetUpdateLayout( false );
+ mxOutputEditEngine->EnableUndo( false ); // don't need undo for painting purposes
+ // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice
+ mxOutputEditEngine->SetRefDevice( pFmtDevice );
+ EEControlBits nCtrl = mxOutputEditEngine->GetControlWord();
+ if ( bShowSpellErrors )
+ nCtrl |= EEControlBits::ONLINESPELLING;
+ if ( eType == OUTTYPE_PRINTER )
+ nCtrl &= ~EEControlBits::MARKFIELDS;
+ else
+ nCtrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output
+ if ( eType == OUTTYPE_WINDOW && mpRefDevice == pFmtDevice )
+ nCtrl &= ~EEControlBits::FORMAT100; // use the actual MapMode
+ mxOutputEditEngine->SetControlWord( nCtrl );
+ mxOutputEditEngine->EnableAutoColor( mbUseStyleColor );
+ }
+ else
+ {
+ // just in case someone turned it on during the last paint cycle
+ mxOutputEditEngine->SetUpdateLayout( false );
+ }
+ // we don't track changes to these settings, so we have to apply them every time
+ mpDoc->ApplyAsianEditSettings( *mxOutputEditEngine );
+ mxOutputEditEngine->SetDefaultHorizontalTextDirection( mpDoc->GetEditTextDirection( nTab ) );
+}
+
+static void lcl_ClearEdit( EditEngine& rEngine ) // text and attributes
+{
+ rEngine.SetUpdateLayout( false );
+
+ rEngine.SetText(OUString());
+ // do not keep any para-attributes
+ const SfxItemSet& rPara = rEngine.GetParaAttribs(0);
+ if (rPara.Count())
+ rEngine.SetParaAttribs( 0,
+ SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) );
+ rEngine.EnableSkipOutsideFormat(false);
+}
+
+static bool lcl_SafeIsValue( ScRefCellValue& rCell )
+{
+ switch (rCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ return true;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = rCell.getFormula();
+ if (pFCell->IsRunning() || pFCell->IsValue())
+ return true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return false;
+}
+
+static void lcl_ScaleFonts( EditEngine& rEngine, tools::Long nPercent )
+{
+ bool bUpdateMode = rEngine.SetUpdateLayout( false );
+
+ sal_Int32 nParCount = rEngine.GetParagraphCount();
+ for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
+ {
+ std::vector<sal_Int32> aPortions;
+ rEngine.GetPortions( nPar, aPortions );
+
+ sal_Int32 nStart = 0;
+ for ( const sal_Int32 nEnd : aPortions )
+ {
+ ESelection aSel( nPar, nStart, nPar, nEnd );
+ SfxItemSet aAttribs = rEngine.GetAttribs( aSel );
+
+ tools::Long nWestern = aAttribs.Get(EE_CHAR_FONTHEIGHT).GetHeight();
+ tools::Long nCJK = aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK).GetHeight();
+ tools::Long nCTL = aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL).GetHeight();
+
+ nWestern = ( nWestern * nPercent ) / 100;
+ nCJK = ( nCJK * nPercent ) / 100;
+ nCTL = ( nCTL * nPercent ) / 100;
+
+ aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) );
+ aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ rEngine.QuickSetAttribs( aAttribs, aSel ); //! remove paragraph attributes from aAttribs?
+
+ nStart = nEnd;
+ }
+ }
+
+ if ( bUpdateMode )
+ rEngine.SetUpdateLayout( true );
+}
+
+static tools::Long lcl_GetEditSize( EditEngine& rEngine, bool bWidth, bool bSwap, Degree100 nAttrRotate )
+{
+ if ( bSwap )
+ bWidth = !bWidth;
+
+ if ( nAttrRotate )
+ {
+ tools::Long nRealWidth = static_cast<tools::Long>(rEngine.CalcTextWidth());
+ tools::Long nRealHeight = rEngine.GetTextHeight();
+
+ // assuming standard mode, otherwise width isn't used
+
+ double nRealOrient = toRadians(nAttrRotate); // 1/100th degrees
+ double nAbsCos = fabs( cos( nRealOrient ) );
+ double nAbsSin = fabs( sin( nRealOrient ) );
+ if ( bWidth )
+ return static_cast<tools::Long>( nRealWidth * nAbsCos + nRealHeight * nAbsSin );
+ else
+ return static_cast<tools::Long>( nRealHeight * nAbsCos + nRealWidth * nAbsSin );
+ }
+ else if ( bWidth )
+ return static_cast<tools::Long>(rEngine.CalcTextWidth());
+ else
+ return rEngine.GetTextHeight();
+}
+
+void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const tools::Rectangle& rAlignRect,
+ tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM,
+ bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic,
+ tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel, bool& rLeftClip, bool& rRightClip )
+{
+ if ( !bWidth )
+ {
+ // vertical
+
+ tools::Long nScaleSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
+
+ // Don't scale if it fits already.
+ // Allowing to extend into the margin, to avoid scaling at optimal height.
+ if ( nScaleSize <= rAlignRect.GetHeight() )
+ return;
+
+ bool bSwap = ( nOrient == SvxCellOrientation::TopBottom || nOrient == SvxCellOrientation::BottomUp );
+ tools::Long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM;
+ tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
+
+ lcl_ScaleFonts( rEngine, nScale );
+ rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
+ tools::Long nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
+
+ sal_uInt16 nShrinkAgain = 0;
+ while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
+ {
+ // further reduce, like in DrawStrings
+ lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
+ rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate );
+ nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight;
+ ++nShrinkAgain;
+ }
+
+ // sizes for further processing (alignment etc):
+ rEngineWidth = lcl_GetEditSize( rEngine, true, bSwap, nAttrRotate );
+ tools::Long nPixelWidth = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
+ rNeededPixel = nPixelWidth + nLeftM + nRightM;
+ }
+ else if ( rLeftClip || rRightClip )
+ {
+ // horizontal
+
+ tools::Long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM;
+ tools::Long nScaleSize = rNeededPixel - nLeftM - nRightM; // without margin
+
+ if ( nScaleSize <= nAvailable )
+ return;
+
+ tools::Long nScale = ( nAvailable * 100 ) / nScaleSize;
+
+ lcl_ScaleFonts( rEngine, nScale );
+ rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
+ tools::Long nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
+
+ sal_uInt16 nShrinkAgain = 0;
+ while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX )
+ {
+ // further reduce, like in DrawStrings
+ lcl_ScaleFonts( rEngine, 90 ); // reduce by 10%
+ rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate );
+ nNewSize = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth;
+ ++nShrinkAgain;
+ }
+ if ( nNewSize <= nAvailable )
+ rLeftClip = rRightClip = false;
+
+ // sizes for further processing (alignment etc):
+ rNeededPixel = nNewSize + nLeftM + nRightM;
+ rEngineHeight = lcl_GetEditSize( rEngine, false, false, nAttrRotate );
+ }
+}
+
+ScOutputData::DrawEditParam::DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, bool bCellIsValue) :
+ meHorJustAttr( lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(*pPattern, ATTR_HOR_JUSTIFY, pCondSet) ),
+ meHorJustContext( meHorJustAttr ),
+ meHorJustResult( meHorJustAttr ),
+ meVerJust( lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(*pPattern, ATTR_VER_JUSTIFY, pCondSet) ),
+ meHorJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet) ),
+ meVerJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_VER_JUSTIFY_METHOD, pCondSet) ),
+ meOrient( pPattern->GetCellOrientation(pCondSet) ),
+ mnArrY(0),
+ mnX(0), mnCellX(0), mnCellY(0),
+ mnPosX(0), mnPosY(0), mnInitPosX(0),
+ mbBreak( (meHorJustAttr == SvxCellHorJustify::Block) || lcl_GetBoolValue(*pPattern, ATTR_LINEBREAK, pCondSet) ),
+ mbCellIsValue(bCellIsValue),
+ mbAsianVertical(false),
+ mbPixelToLogic(false),
+ mbHyphenatorSet(false),
+ mpEngine(nullptr),
+ mpPattern(pPattern),
+ mpCondSet(pCondSet),
+ mpPreviewFontSet(nullptr),
+ mpOldPattern(nullptr),
+ mpOldCondSet(nullptr),
+ mpOldPreviewFontSet(nullptr),
+ mpThisRowInfo(nullptr),
+ mpMisspellRanges(nullptr)
+{}
+
+bool ScOutputData::DrawEditParam::readCellContent(
+ const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields)
+{
+ if (maCell.getType() == CELLTYPE_EDIT)
+ {
+ const EditTextObject* pData = maCell.getEditText();
+ if (pData)
+ {
+ mpEngine->SetTextCurrentDefaults(*pData);
+
+ if ( mbBreak && !mbAsianVertical && pData->HasField() )
+ {
+ // Fields aren't wrapped, so clipping is enabled to prevent
+ // a field from being drawn beyond the cell size
+
+ rWrapFields = true;
+ }
+ }
+ else
+ {
+ OSL_FAIL("pData == 0");
+ return false;
+ }
+ }
+ else
+ {
+ sal_uInt32 nFormat = mpPattern->GetNumberFormat(
+ pDoc->GetFormatTable(), mpCondSet );
+ const Color* pColor;
+ OUString aString = ScCellFormat::GetString( maCell,
+ nFormat, &pColor,
+ *pDoc->GetFormatTable(),
+ *pDoc,
+ bShowNullValues,
+ bShowFormulas);
+
+ mpEngine->SetTextCurrentDefaults(aString);
+ if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) )
+ lcl_SetEditColor( *mpEngine, *pColor );
+ }
+
+ if (mpMisspellRanges)
+ mpEngine->SetAllMisspellRanges(*mpMisspellRanges);
+
+ return true;
+}
+
+void ScOutputData::DrawEditParam::setPatternToEngine(bool bUseStyleColor)
+{
+ // syntax highlighting mode is ignored here
+ // StringDiffer doesn't look at hyphenate, language items
+
+ if (SfxPoolItem::areSame(mpPattern, mpOldPattern) && mpCondSet == mpOldCondSet && mpPreviewFontSet == mpOldPreviewFontSet )
+ return;
+
+ Color nConfBackColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ bool bCellContrast = bUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ auto pSet = std::make_unique<SfxItemSet>( mpEngine->GetEmptyItemSet() );
+ mpPattern->FillEditItemSet( pSet.get(), mpCondSet );
+ if ( mpPreviewFontSet )
+ {
+ if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_FONT ) )
+ {
+ // tdf#125054 adapt WhichID
+ pSet->Put(*pItem, EE_CHAR_FONTINFO);
+ }
+ if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CJK_FONT ) )
+ {
+ // tdf#125054 adapt WhichID
+ pSet->Put(*pItem, EE_CHAR_FONTINFO_CJK);
+ }
+ if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CTL_FONT ) )
+ {
+ // tdf#125054 adapt WhichID
+ pSet->Put(*pItem, EE_CHAR_FONTINFO_CTL);
+ }
+ }
+ bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
+ mpEngine->SetDefaults( std::move(pSet) );
+ mpOldPattern = mpPattern;
+ mpOldCondSet = mpCondSet;
+ mpOldPreviewFontSet = mpPreviewFontSet;
+
+ EEControlBits nControl = mpEngine->GetControlWord();
+ if (meOrient == SvxCellOrientation::Stacked)
+ nControl |= EEControlBits::ONECHARPERLINE;
+ else
+ nControl &= ~EEControlBits::ONECHARPERLINE;
+ mpEngine->SetControlWord( nControl );
+
+ if ( !mbHyphenatorSet && bParaHyphenate )
+ {
+ // set hyphenator the first time it is needed
+ css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
+ mpEngine->SetHyphenator( xXHyphenator );
+ mbHyphenatorSet = true;
+ }
+
+ Color aBackCol = mpPattern->GetItem( ATTR_BACKGROUND, mpCondSet ).GetColor();
+ if ( bUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
+ aBackCol = nConfBackColor;
+ mpEngine->SetBackgroundColor( aBackCol );
+}
+
+void ScOutputData::DrawEditParam::calcMargins(tools::Long& rTopM, tools::Long& rLeftM, tools::Long& rBottomM, tools::Long& rRightM, double nPPTX, double nPPTY) const
+{
+ const SvxMarginItem& rMargin = mpPattern->GetItem(ATTR_MARGIN, mpCondSet);
+
+ sal_uInt16 nIndent = 0;
+ if (meHorJustAttr == SvxCellHorJustify::Left || meHorJustAttr == SvxCellHorJustify::Right)
+ nIndent = lcl_GetValue<ScIndentItem, sal_uInt16>(*mpPattern, ATTR_INDENT, mpCondSet);
+
+ rLeftM = static_cast<tools::Long>(((rMargin.GetLeftMargin() + nIndent) * nPPTX));
+ rTopM = static_cast<tools::Long>((rMargin.GetTopMargin() * nPPTY));
+ rRightM = static_cast<tools::Long>((rMargin.GetRightMargin() * nPPTX));
+ rBottomM = static_cast<tools::Long>((rMargin.GetBottomMargin() * nPPTY));
+ if(meHorJustAttr == SvxCellHorJustify::Right)
+ {
+ rLeftM = static_cast<tools::Long>((rMargin.GetLeftMargin() * nPPTX));
+ rRightM = static_cast<tools::Long>(((rMargin.GetRightMargin() + nIndent) * nPPTX));
+ }
+}
+
+void ScOutputData::DrawEditParam::calcPaperSize(
+ Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const
+{
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ calcMargins(nTopM, nLeftM, nBottomM, nRightM, nPPTX, nPPTY);
+
+ if (isVerticallyOriented())
+ {
+ rPaperSize.setWidth( rAlignRect.GetHeight() - nTopM - nBottomM );
+ rPaperSize.setHeight( rAlignRect.GetWidth() - nLeftM - nRightM );
+ }
+ else
+ {
+ rPaperSize.setWidth( rAlignRect.GetWidth() - nLeftM - nRightM );
+ rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
+ }
+
+ if (mbAsianVertical)
+ {
+ rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM );
+ // Subtract some extra value from the height or else the text would go
+ // outside the cell area. The value of 5 is arbitrary, and is based
+ // entirely on heuristics.
+ rPaperSize.AdjustHeight( -5 );
+ }
+}
+
+void ScOutputData::DrawEditParam::getEngineSize(ScFieldEditEngine* pEngine, tools::Long& rWidth, tools::Long& rHeight) const
+{
+ tools::Long nEngineWidth = 0;
+ if (!mbBreak || meOrient == SvxCellOrientation::Stacked || mbAsianVertical)
+ nEngineWidth = static_cast<tools::Long>(pEngine->CalcTextWidth());
+
+ tools::Long nEngineHeight = pEngine->GetTextHeight();
+
+ if (isVerticallyOriented())
+ std::swap( nEngineWidth, nEngineHeight );
+
+ if (meOrient == SvxCellOrientation::Stacked)
+ nEngineWidth = nEngineWidth * 11 / 10;
+
+ rWidth = nEngineWidth;
+ rHeight = nEngineHeight;
+}
+
+bool ScOutputData::DrawEditParam::hasLineBreak() const
+{
+ return (mbBreak || (meOrient == SvxCellOrientation::Stacked) || mbAsianVertical);
+}
+
+bool ScOutputData::DrawEditParam::isHyperlinkCell() const
+{
+ if (maCell.getType() != CELLTYPE_FORMULA)
+ return false;
+
+ return maCell.getFormula()->IsHyperLinkCell();
+}
+
+bool ScOutputData::DrawEditParam::isVerticallyOriented() const
+{
+ return (meOrient == SvxCellOrientation::TopBottom || meOrient == SvxCellOrientation::BottomUp);
+}
+
+void ScOutputData::DrawEditParam::calcStartPosForVertical(
+ Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice)
+{
+ OSL_ENSURE(isVerticallyOriented(), "Use this only for vertically oriented cell!");
+
+ if (mbPixelToLogic)
+ rLogicStart = pRefDevice->PixelToLogic(rLogicStart);
+
+ if (!mbBreak)
+ return;
+
+ // vertical adjustment is within the EditEngine
+ if (mbPixelToLogic)
+ rLogicStart.AdjustY(pRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ rLogicStart.AdjustY(nTopM );
+
+ switch (meHorJustResult)
+ {
+ case SvxCellHorJustify::Center:
+ rLogicStart.AdjustX((nCellWidth - nEngineWidth) / 2 );
+ break;
+ case SvxCellHorJustify::Right:
+ rLogicStart.AdjustX(nCellWidth - nEngineWidth );
+ break;
+ default:
+ ; // do nothing
+ }
+}
+
+void ScOutputData::DrawEditParam::setAlignmentToEngine()
+{
+ if (isVerticallyOriented() || mbAsianVertical)
+ {
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ switch (meVerJust)
+ {
+ case SvxCellVerJustify::Top:
+ eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
+ SvxAdjust::Left : SvxAdjust::Right;
+ break;
+ case SvxCellVerJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellVerJustify::Bottom:
+ case SvxCellVerJustify::Standard:
+ eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ?
+ SvxAdjust::Right : SvxAdjust::Left;
+ break;
+ case SvxCellVerJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+
+ mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
+ mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
+
+ if (meHorJustResult == SvxCellHorJustify::Block)
+ mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
+ }
+ else
+ {
+ // horizontal alignment now may depend on cell content
+ // (for values with number formats with mixed script types)
+ // -> always set adjustment
+
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ if (meOrient == SvxCellOrientation::Stacked)
+ eSvxAdjust = SvxAdjust::Center;
+ else if (mbBreak)
+ {
+ if (meOrient == SvxCellOrientation::Standard)
+ switch (meHorJustResult)
+ {
+ case SvxCellHorJustify::Repeat: // repeat is not yet implemented
+ case SvxCellHorJustify::Standard:
+ SAL_WARN("sc.ui","meHorJustResult does not match getAlignmentFromContext()");
+ [[fallthrough]];
+ case SvxCellHorJustify::Left:
+ eSvxAdjust = SvxAdjust::Left;
+ break;
+ case SvxCellHorJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellHorJustify::Right:
+ eSvxAdjust = SvxAdjust::Right;
+ break;
+ case SvxCellHorJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+ else
+ switch (meVerJust)
+ {
+ case SvxCellVerJustify::Top:
+ eSvxAdjust = SvxAdjust::Right;
+ break;
+ case SvxCellVerJustify::Center:
+ eSvxAdjust = SvxAdjust::Center;
+ break;
+ case SvxCellVerJustify::Bottom:
+ case SvxCellVerJustify::Standard:
+ eSvxAdjust = SvxAdjust::Left;
+ break;
+ case SvxCellVerJustify::Block:
+ eSvxAdjust = SvxAdjust::Block;
+ break;
+ }
+ }
+
+ mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) );
+
+ if (mbAsianVertical)
+ {
+ mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) );
+ if (meHorJustResult == SvxCellHorJustify::Block)
+ mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
+ }
+ else
+ {
+ mpEngine->SetDefaultItem( SvxJustifyMethodItem(meHorJustMethod, EE_PARA_JUST_METHOD) );
+ if (meVerJust == SvxCellVerJustify::Block)
+ mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) );
+ }
+ }
+
+ mpEngine->SetVertical(mbAsianVertical);
+ if (maCell.getType() == CELLTYPE_EDIT)
+ {
+ // We need to synchronize the vertical mode in the EditTextObject
+ // instance too. No idea why we keep this state in two separate
+ // instances.
+ const EditTextObject* pData = maCell.getEditText();
+ if (pData)
+ const_cast<EditTextObject*>(pData)->SetVertical(mbAsianVertical);
+ }
+}
+
+bool ScOutputData::DrawEditParam::adjustHorAlignment(ScFieldEditEngine* pEngine)
+{
+ if (meHorJustResult == SvxCellHorJustify::Right || meHorJustResult == SvxCellHorJustify::Center)
+ {
+ SvxAdjust eEditAdjust = (meHorJustResult == SvxCellHorJustify::Center) ?
+ SvxAdjust::Center : SvxAdjust::Right;
+
+ const bool bPrevUpdateLayout = pEngine->SetUpdateLayout(false);
+ pEngine->SetDefaultItem( SvxAdjustItem(eEditAdjust, EE_PARA_JUST) );
+ pEngine->SetUpdateLayout(bPrevUpdateLayout);
+ return true;
+ }
+ return false;
+}
+
+void ScOutputData::DrawEditParam::adjustForHyperlinkInPDF(Point aURLStart, const OutputDevice* pDev)
+{
+ // PDF: whole-cell hyperlink from formula?
+ vcl::PDFExtOutDevData* pPDFData = dynamic_cast<vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() );
+ bool bHasURL = pPDFData && isHyperlinkCell();
+ if (!bHasURL)
+ return;
+
+ tools::Long nURLWidth = static_cast<tools::Long>(mpEngine->CalcTextWidth());
+ tools::Long nURLHeight = mpEngine->GetTextHeight();
+ if (mbBreak)
+ {
+ Size aPaper = mpEngine->GetPaperSize();
+ if ( mbAsianVertical )
+ nURLHeight = aPaper.Height();
+ else
+ nURLWidth = aPaper.Width();
+ }
+ if (isVerticallyOriented())
+ std::swap( nURLWidth, nURLHeight );
+ else if (mbAsianVertical)
+ aURLStart.AdjustX( -nURLWidth );
+
+ tools::Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) );
+ lcl_DoHyperlinkResult(pDev, aURLRect, maCell);
+}
+
+// Returns true if the rect is clipped vertically
+bool ScOutputData::AdjustAreaParamClipRect(OutputAreaParam& rAreaParam)
+{
+ if( rAreaParam.maClipRect.Left() < nScrX )
+ {
+ rAreaParam.maClipRect.SetLeft( nScrX );
+ rAreaParam.mbLeftClip = true;
+ }
+ if( rAreaParam.maClipRect.Right() > nScrX + nScrW )
+ {
+ rAreaParam.maClipRect.SetRight( nScrX + nScrW ); //! minus one?
+ rAreaParam.mbRightClip = true;
+ }
+
+ bool bVClip = false;
+
+ if( rAreaParam.maClipRect.Top() < nScrY )
+ {
+ rAreaParam.maClipRect.SetTop( nScrY );
+ bVClip = true;
+ }
+ if( rAreaParam.maClipRect.Bottom() > nScrY + nScrH )
+ {
+ rAreaParam.maClipRect.SetBottom( nScrY + nScrH ); //! minus one?
+ bVClip = true;
+ }
+ return bVClip;
+}
+
+// Doesn't handle clip marks - should be handled in advance using GetOutputArea
+class ClearableClipRegion
+{
+public:
+ ClearableClipRegion( const tools::Rectangle& rRect, bool bClip, bool bSimClip,
+ const VclPtr<OutputDevice>& pDev, bool bMetaFile )
+ :mbMetaFile(bMetaFile)
+ {
+ if (!(bClip || bSimClip))
+ return;
+
+ maRect = rRect;
+ if (bClip) // for bSimClip only initialize aClipRect
+ {
+ mpDev.reset(pDev);
+ if (mbMetaFile)
+ {
+ mpDev->Push();
+ mpDev->IntersectClipRegion(maRect);
+ }
+ else
+ mpDev->SetClipRegion(vcl::Region(maRect));
+ }
+ }
+
+ ~ClearableClipRegion() COVERITY_NOEXCEPT_FALSE
+ {
+ // Pop() or SetClipRegion() must only be called in case bClip was true
+ // in the ctor, and only then mpDev is set.
+ if (mpDev)
+ {
+ if (mbMetaFile)
+ mpDev->Pop();
+ else
+ mpDev->SetClipRegion();
+ }
+ }
+
+ const tools::Rectangle& getRect() const { return maRect; }
+
+private:
+ tools::Rectangle maRect;
+ VclPtr<OutputDevice> mpDev;
+ bool mbMetaFile;
+};
+
+// Returns needed width in current units; sets rNeededPixel to needed width in pixels
+tools::Long ScOutputData::SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString,
+ tools::Long& rNeededPixel, tools::Long nAddWidthPixels )
+{
+ rParam.mpEngine->SetTextCurrentDefaults( rSetString );
+ tools::Long nEngineWidth = static_cast<tools::Long>( rParam.mpEngine->CalcTextWidth() );
+ if ( rParam.mbPixelToLogic )
+ rNeededPixel = mpRefDevice->LogicToPixel( Size( nEngineWidth, 0 ) ).Width();
+ else
+ rNeededPixel = nEngineWidth;
+
+ rNeededPixel += nAddWidthPixels;
+
+ return nEngineWidth;
+}
+
+void ScOutputData::DrawEditStandard(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
+ OSL_ASSERT(!rParam.mbAsianVertical);
+
+ Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+
+ bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+ Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
+
+ if ( rParam.meHorJustAttr == SvxCellHorJustify::Repeat )
+ {
+ // ignore orientation/rotation if "repeat" is active
+ rParam.meOrient = SvxCellOrientation::Standard;
+ nAttrRotate = 0_deg100;
+
+ // #i31843# "repeat" with "line breaks" is treated as default alignment
+ // (but rotation is still disabled).
+ // Default again leads to context dependent alignment instead of
+ // SvxCellHorJustify::Standard.
+ if ( rParam.mbBreak )
+ rParam.meHorJustResult = rParam.meHorJustContext;
+ }
+
+ if (nAttrRotate)
+ {
+ //! set flag to find the cell in DrawRotated again ?
+ //! (or flag already set during DrawBackground, then no query here)
+ return; // rotated is outputted separately
+ }
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (rParam.mbBreak)
+ {
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+ }
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
+ {
+ // #i85342# screen display and formatting for printer,
+ // use same GetEditArea call as in ScViewData::SetEditEngine
+
+ Fraction aFract(1,1);
+ tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
+ aLogicSize.setWidth( aUtilRect.GetWidth() );
+ }
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ // default alignment for asian vertical mode is top-right
+ if ( rParam.mbAsianVertical && rParam.meVerJust == SvxCellVerJustify::Standard )
+ rParam.meVerJust = SvxCellVerJustify::Top;
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+ // Don't format unnecessary parts if the text will be drawn from top (Standard will
+ // act that way if text doesn't fit, see below).
+ rParam.mpEngine->EnableSkipOutsideFormat(rParam.meVerJust==SvxCellVerJustify::Top
+ || rParam.meVerJust==SvxCellVerJustify::Standard);
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor(*rParam.mpEngine, rParam.maCell);
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (!rParam.mbBreak || bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, true,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
+ {
+ // First check if twice the space for the formatted text is available
+ // (otherwise just keep it unchanged).
+
+ tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
+ tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
+ if ( nAvailable >= 2 * nFormatted )
+ {
+ // "repeat" is handled with unformatted text (for performance reasons)
+ OUString aCellStr = rParam.mpEngine->GetText();
+
+ tools::Long nRepeatSize = 0;
+ SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+
+ SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
+ nNeededPixel, (nLeftM + nRightM ) );
+
+ nEngineHeight = rParam.mpEngine->GetTextHeight();
+ }
+ }
+ }
+ }
+
+
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+ SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
+ }
+
+ if (eOutHorJust != SvxCellHorJustify::Left)
+ {
+ aPaperSize.setWidth( nNeededPixel + 1 );
+ if (rParam.mbPixelToLogic)
+ rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ Size aCellSize; // output area, excluding margins, in logical units
+ if (rParam.mbPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight );
+
+ if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ // Show clip marks if height is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if ( nEngineHeight - aCellSize.Height() > 100 &&
+ rParam.mbBreak && bMarkClipped &&
+ ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
+ {
+ ScCellInfo* pClipMarkCell = nullptr;
+ if ( bMerged )
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
+ bAnyClipped = true;
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
+ aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+
+ // Standard is normally treated as Bottom, but if text height is clipped, then
+ // Top looks better and also allows using EditEngine::EnableSkipOutsideFormat().
+ if (rParam.meVerJust==SvxCellVerJustify::Standard)
+ rParam.meVerJust=SvxCellVerJustify::Top;
+ }
+ }
+
+ Point aURLStart;
+
+ { // Clip marks are already handled in GetOutputArea
+ ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
+
+ Point aLogicStart;
+ if (rParam.mbPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+
+ if (!rParam.mbBreak)
+ {
+ // horizontal alignment
+ if (rParam.adjustHorAlignment(rParam.mpEngine))
+ // reset adjustment for the next cell
+ rParam.mpOldPattern = nullptr;
+ }
+
+ if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
+ rParam.meVerJust==SvxCellVerJustify::Standard)
+ {
+ //! if pRefDevice != pFmtDevice, keep heights in logic units,
+ //! only converting margin?
+
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
+ )).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
+ }
+ else if (rParam.meVerJust==SvxCellVerJustify::Center)
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
+ / 2)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
+ }
+ else // top
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM );
+ }
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ if (bSimClip)
+ {
+ // no hard clip, only draw the affected rows
+ Point aDocStart = aClip.getRect().TopLeft();
+ aDocStart -= aLogicStart;
+ rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
+ }
+ else
+ {
+ rParam.mpEngine->Draw(*mpDev, aLogicStart);
+ }
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::SetClipMarks( OutputAreaParam &aAreaParam, ScCellInfo* pClipMarkCell,
+ SvxCellHorJustify eOutHorJust,
+ tools::Long nLayoutSign )
+{
+ tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX;
+
+ if ( eOutHorJust == SvxCellHorJustify::Left )
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Right;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) );
+ }
+ else if ( eOutHorJust == SvxCellHorJustify::Right )
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign );
+ }
+ else
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Right;
+ pClipMarkCell->nClipMark |= ScClipMark::Left;
+ bAnyClipped = true;
+ aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) );
+ aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign );
+ }
+
+}
+
+void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize,
+ bool bMerged, OutputAreaParam& aAreaParam, bool bTop)
+{
+ // Show clip marks if width is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if (nEngineWidth - aCellSize.Width() <= 100 || !rParam.mbBreak || !bMarkClipped
+ || (rParam.mpEngine->GetParagraphCount() <= 1 && rParam.mpEngine->GetLineCount(0) <= 1))
+ return;
+
+ ScCellInfo* pClipMarkCell = nullptr;
+ if (bMerged)
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = (rParam.mnX < nX1) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ bAnyClipped = true;
+ bVertical = true;
+ const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX);
+ if (bTop)
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Top;
+ if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
+ aAreaParam.maClipRect.AdjustTop(+nMarkPixel);
+ }
+ else
+ {
+ pClipMarkCell->nClipMark |= ScClipMark::Bottom;
+ if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom())
+ aAreaParam.maClipRect.AdjustBottom(-nMarkPixel);
+ }
+}
+
+ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aCellSize,
+ OutputAreaParam& aAreaParam, tools::Long nEngineWidth,
+ bool bWrapFields, bool bTop)
+{
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ const Size& aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+ if ( nEngineWidth >= aCellSize.Width() + aRefOne.Width() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ const bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ ShowClipMarks( rParam, nEngineWidth, aCellSize, bMerged, aAreaParam, bTop);
+ }
+
+ // Clip marks are already handled in GetOutputArea
+ return ClearableClipRegionPtr(new ClearableClipRegion(rParam.mbPixelToLogic ?
+ mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile));
+}
+
+void ScOutputData::DrawEditBottomTop(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+
+ const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (rParam.mbBreak)
+ {
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+ }
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (!rParam.mbBreak || bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, false,
+ (rParam.meOrient), 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
+ {
+ // First check if twice the space for the formatted text is available
+ // (otherwise just keep it unchanged).
+
+ const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
+ const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
+ if ( nAvailable >= 2 * nFormatted )
+ {
+ // "repeat" is handled with unformatted text (for performance reasons)
+ OUString aCellStr = rParam.mpEngine->GetText();
+
+ tools::Long nRepeatSize = 0;
+ SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ const tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
+ nNeededPixel, (nLeftM + nRightM ) );
+
+ nEngineHeight = rParam.mpEngine->GetTextHeight();
+ }
+ }
+ }
+ }
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+
+ // No clip marks if "###" doesn't fit (same as in DrawStrings)
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ const tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // output area, excluding margins, in logical units
+ const Size& aCellSize = rParam.mbPixelToLogic
+ ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
+ : Size( nOutWidth, nOutHeight );
+
+ Point aURLStart;
+
+ {
+ const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, true );
+
+ Point aLogicStart(nStartX, nStartY);
+ rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ if (rParam.meHorJustResult == SvxCellHorJustify::Block || rParam.mbBreak)
+ {
+ Size aPSize = rParam.mpEngine->GetPaperSize();
+ aPSize.setWidth( aCellSize.Height() );
+ rParam.mpEngine->SetPaperSize(aPSize);
+ aLogicStart.AdjustY(
+ rParam.mbBreak ? aPSize.Width() : nEngineHeight );
+ }
+ else
+ {
+ // Note that the "paper" is rotated 90 degrees to the left, so
+ // paper's width is in vertical direction. Also, the whole text
+ // is on a single line, as text wrap is not in effect.
+
+ // Set the paper width to be the width of the text.
+ Size aPSize = rParam.mpEngine->GetPaperSize();
+ aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
+ rParam.mpEngine->SetPaperSize(aPSize);
+
+ tools::Long nGap = 0;
+ tools::Long nTopOffset = 0;
+ if (rParam.mbPixelToLogic)
+ {
+ nGap = mpRefDevice->LogicToPixel(aCellSize).Height() - mpRefDevice->LogicToPixel(aPSize).Width();
+ nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
+ nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
+ }
+ else
+ {
+ nGap = aCellSize.Height() - aPSize.Width();
+ nTopOffset = nTopM;
+ }
+
+ // First, align text to bottom.
+ aLogicStart.AdjustY(aCellSize.Height() );
+ aLogicStart.AdjustY(nTopOffset );
+
+ switch (rParam.meVerJust)
+ {
+ case SvxCellVerJustify::Standard:
+ case SvxCellVerJustify::Bottom:
+ // align to bottom (do nothing).
+ break;
+ case SvxCellVerJustify::Center:
+ // center it.
+ aLogicStart.AdjustY( -(nGap / 2) );
+ break;
+ case SvxCellVerJustify::Block:
+ case SvxCellVerJustify::Top:
+ // align to top
+ aLogicStart.AdjustY( -nGap );
+ break;
+ default:
+ ;
+ }
+ }
+
+ rParam.mpEngine->Draw(*mpDev, aLogicStart, 900_deg10);
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEditTopBottom(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+
+ const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (rParam.hasLineBreak())
+ {
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+ }
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (!rParam.mbBreak || bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, false,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 )
+ {
+ // First check if twice the space for the formatted text is available
+ // (otherwise just keep it unchanged).
+
+ const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin
+ const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM;
+ if ( nAvailable >= 2 * nFormatted )
+ {
+ // "repeat" is handled with unformatted text (for performance reasons)
+ OUString aCellStr = rParam.mpEngine->GetText();
+
+ tools::Long nRepeatSize = 0;
+ SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 );
+
+ if ( pFmtDevice != mpRefDevice )
+ ++nRepeatSize;
+ if ( nRepeatSize > 0 )
+ {
+ const tools::Long nRepeatCount = nAvailable / nRepeatSize;
+ if ( nRepeatCount > 1 )
+ {
+ OUStringBuffer aRepeated(aCellStr);
+ for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ )
+ aRepeated.append(aCellStr);
+
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(),
+ nNeededPixel, (nLeftM + nRightM ) );
+
+ nEngineHeight = rParam.mpEngine->GetTextHeight();
+ }
+ }
+ }
+ }
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+
+ // No clip marks if "###" doesn't fit (same as in DrawStrings)
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ const tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ if (rParam.meHorJustResult == SvxCellHorJustify::Block)
+ nStartX += aPaperSize.Height();
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ const bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // output area, excluding margins, in logical units
+ const Size& aCellSize = rParam.mbPixelToLogic
+ ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) )
+ : Size( nOutWidth, nOutHeight );
+
+ Point aURLStart;
+
+ {
+ const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, false );
+
+ Point aLogicStart(nStartX, nStartY);
+ rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice);
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ if (rParam.meHorJustResult != SvxCellHorJustify::Block)
+ {
+ aLogicStart.AdjustX(nEngineWidth );
+ if (!rParam.mbBreak)
+ {
+ // Set the paper width to text size.
+ Size aPSize = rParam.mpEngine->GetPaperSize();
+ aPSize.setWidth( rParam.mpEngine->CalcTextWidth() );
+ rParam.mpEngine->SetPaperSize(aPSize);
+
+ tools::Long nGap = 0;
+ tools::Long nTopOffset = 0; // offset by top margin
+ if (rParam.mbPixelToLogic)
+ {
+ nGap = mpRefDevice->LogicToPixel(aPSize).Width() - mpRefDevice->LogicToPixel(aCellSize).Height();
+ nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height();
+ nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height();
+ }
+ else
+ {
+ nGap = aPSize.Width() - aCellSize.Height();
+ nTopOffset = nTopM;
+ }
+ aLogicStart.AdjustY(nTopOffset );
+
+ switch (rParam.meVerJust)
+ {
+ case SvxCellVerJustify::Standard:
+ case SvxCellVerJustify::Bottom:
+ // align to bottom
+ aLogicStart.AdjustY( -nGap );
+ break;
+ case SvxCellVerJustify::Center:
+ // center it.
+ aLogicStart.AdjustY( -(nGap / 2) );
+ break;
+ case SvxCellVerJustify::Block:
+ case SvxCellVerJustify::Top:
+ // align to top (do nothing)
+ default:
+ ;
+ }
+ }
+ }
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ rParam.mpEngine->Draw(*mpDev, aLogicStart, 2700_deg10);
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEditStacked(DrawEditParam& rParam)
+{
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+ Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+
+ bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak);
+ bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+
+ rParam.mbAsianVertical =
+ lcl_GetBoolValue(*rParam.mpPattern, ATTR_VERTICAL_ASIAN, rParam.mpCondSet);
+
+ if ( rParam.mbAsianVertical )
+ {
+ // in asian mode, use EditEngine::SetVertical instead of EEControlBits::ONECHARPERLINE
+ rParam.meOrient = SvxCellOrientation::Standard;
+ DrawEditAsianVertical(rParam);
+ return;
+ }
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ if ( rParam.mbBreak && mpRefDevice != pFmtDevice )
+ {
+ // #i85342# screen display and formatting for printer,
+ // use same GetEditArea call as in ScViewData::SetEditEngine
+
+ Fraction aFract(1,1);
+ tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
+ aLogicSize.setWidth( aUtilRect.GetWidth() );
+ }
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ if (bShrink)
+ {
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ true, false, false, aAreaParam );
+
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, true,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+ SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
+ }
+
+ if ( eOutHorJust != SvxCellHorJustify::Left )
+ {
+ aPaperSize.setWidth( nNeededPixel + 1 );
+ if (rParam.mbPixelToLogic)
+ rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+ }
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ if (rParam.mbBreak)
+ {
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+ }
+ else
+ {
+ if ( eOutHorJust == SvxCellHorJustify::Right )
+ nStartX -= nNeededPixel - nCellWidth + nRightM + 1;
+ else if ( eOutHorJust == SvxCellHorJustify::Center )
+ nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2;
+ else
+ nStartX += nLeftM;
+ }
+
+ bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ Size aCellSize; // output area, excluding margins, in logical units
+ if (rParam.mbPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight );
+
+ if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ // Show clip marks if height is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if ( nEngineHeight - aCellSize.Height() > 100 &&
+ rParam.mbBreak && bMarkClipped &&
+ ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
+ {
+ ScCellInfo* pClipMarkCell = nullptr;
+ if ( bMerged )
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
+ bAnyClipped = true;
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
+ aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+ }
+ }
+
+ Point aURLStart;
+
+ { // Clip marks are already handled in GetOutputArea
+ ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
+
+ Point aLogicStart;
+ if (rParam.mbPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+
+ if (rParam.meVerJust==SvxCellVerJustify::Bottom ||
+ rParam.meVerJust==SvxCellVerJustify::Standard)
+ {
+ //! if pRefDevice != pFmtDevice, keep heights in logic units,
+ //! only converting margin?
+
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM +
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
+ )).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight );
+ }
+ else if (rParam.meVerJust==SvxCellVerJustify::Center)
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + (
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() )
+ / 2)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 );
+ }
+ else // top
+ {
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM );
+ }
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ Size aPaperLogic = rParam.mpEngine->GetPaperSize();
+ aPaperLogic.setWidth( nEngineWidth );
+ rParam.mpEngine->SetPaperSize(aPaperLogic);
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ if (bSimClip)
+ {
+ // no hard clip, only draw the affected rows
+ Point aDocStart = aClip.getRect().TopLeft();
+ aDocStart -= aLogicStart;
+ rParam.mpEngine->Draw(*mpDev, aClip.getRect(), aDocStart, false);
+ }
+ else
+ {
+ rParam.mpEngine->Draw(*mpDev, aLogicStart);
+ }
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEditAsianVertical(DrawEditParam& rParam)
+{
+ // When in asian vertical orientation, the orientation value is STANDARD,
+ // and the asian vertical boolean is true.
+ OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard);
+ OSL_ASSERT(rParam.mbAsianVertical);
+ OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat);
+
+ Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1));
+
+ bool bHidden = false;
+ bool bShrink = !rParam.mbBreak && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet);
+ Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet);
+
+ if (nAttrRotate)
+ {
+ //! set flag to find the cell in DrawRotated again ?
+ //! (or flag already set during DrawBackground, then no query here)
+ bHidden = true; // rotated is outputted separately
+ }
+
+ // default alignment for asian vertical mode is top-right
+ /* TODO: is setting meHorJustContext and meHorJustResult unconditionally to
+ * SvxCellHorJustify::Right really wanted? Seems this was done all the time,
+ * also before context was introduced and everything was attr only. */
+ if ( rParam.meHorJustAttr == SvxCellHorJustify::Standard )
+ rParam.meHorJustResult = rParam.meHorJustContext = SvxCellHorJustify::Right;
+
+ if (bHidden)
+ return;
+
+ SvxCellHorJustify eOutHorJust = rParam.meHorJustContext;
+
+ //! mirror margin values for RTL?
+ //! move margin down to after final GetOutputArea call
+ tools::Long nTopM, nLeftM, nBottomM, nRightM;
+ rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY);
+
+ SCCOL nXForPos = rParam.mnX;
+ if ( nXForPos < nX1 )
+ {
+ nXForPos = nX1;
+ rParam.mnPosX = rParam.mnInitPosX;
+ }
+ SCSIZE nArrYForPos = rParam.mnArrY;
+ if ( nArrYForPos < 1 )
+ {
+ nArrYForPos = 1;
+ rParam.mnPosY = nScrY;
+ }
+
+ OutputAreaParam aAreaParam;
+
+ // Initial page size - large for normal text, cell size for automatic line breaks
+
+ Size aPaperSize( 1000000, 1000000 );
+ // call GetOutputArea with nNeeded=0, to get only the cell width
+
+ //! handle nArrY == 0
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue, true, false, aAreaParam );
+
+ //! special ScEditUtil handling if formatting for printer
+ rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY);
+
+ if (rParam.mbPixelToLogic)
+ {
+ Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize);
+ if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice )
+ {
+ // #i85342# screen display and formatting for printer,
+ // use same GetEditArea call as in ScViewData::SetEditEngine
+
+ Fraction aFract(1,1);
+ tools::Rectangle aUtilRect = ScEditUtil( mpDoc, rParam.mnCellX, rParam.mnCellY, nTab, Point(0,0), pFmtDevice,
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( rParam.mpPattern, false );
+ aLogicSize.setWidth( aUtilRect.GetWidth() );
+ }
+ rParam.mpEngine->SetPaperSize(aLogicSize);
+ }
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+
+ // Fill the EditEngine (cell attributes and text)
+
+ // default alignment for asian vertical mode is top-right
+ if ( rParam.meVerJust == SvxCellVerJustify::Standard )
+ rParam.meVerJust = SvxCellVerJustify::Top;
+
+ rParam.setPatternToEngine(mbUseStyleColor);
+ rParam.setAlignmentToEngine();
+
+ // Read content from cell
+
+ bool bWrapFields = false;
+ if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields))
+ // Failed to read cell content. Bail out.
+ return;
+
+ if ( mbSyntaxMode )
+ SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell );
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ // Get final output area using the calculated width
+
+ tools::Long nEngineWidth, nEngineHeight;
+ rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight);
+
+ tools::Long nNeededPixel = nEngineWidth;
+ if (rParam.mbPixelToLogic)
+ nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width();
+ nNeededPixel += nLeftM + nRightM;
+
+ // for break, the first GetOutputArea call is sufficient
+ GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel,
+ *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ rParam.mbCellIsValue || bShrink, false, false, aAreaParam );
+
+ if ( bShrink )
+ {
+ ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect,
+ nLeftM, nTopM, nRightM, nBottomM, false,
+ rParam.meOrient, 0_deg100, rParam.mbPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel,
+ aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+ if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) )
+ {
+ nEngineWidth = SetEngineTextAndGetWidth( rParam, "###", nNeededPixel, ( nLeftM + nRightM ) );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+ ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+ SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign );
+ }
+
+ if (eOutHorJust != SvxCellHorJustify::Left)
+ {
+ aPaperSize.setWidth( nNeededPixel + 1 );
+ if (rParam.mbPixelToLogic)
+ rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ rParam.mpEngine->SetPaperSize(aPaperSize);
+ }
+
+ tools::Long nStartX = aAreaParam.maAlignRect.Left();
+ tools::Long nStartY = aAreaParam.maAlignRect.Top();
+ tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth();
+ tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM;
+ tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM;
+
+ // text with automatic breaks is aligned only within the
+ // edit engine's paper size, the output of the whole area
+ // is always left-aligned
+
+ nStartX += nLeftM;
+
+ bool bOutside = (aAreaParam.maClipRect.Right() < nScrX || aAreaParam.maClipRect.Left() >= nScrX + nScrW);
+ if (bOutside)
+ return;
+
+ // Also take fields in a cell with automatic breaks into account: clip to cell width
+ bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields;
+ bool bSimClip = false;
+
+ Size aCellSize; // output area, excluding margins, in logical units
+ if (rParam.mbPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight );
+
+ if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() )
+ {
+ const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ // Don't clip for text height when printing rows with optimal height,
+ // except when font size is from conditional formatting.
+ //! Allow clipping when vertically merged?
+ if ( eType != OUTTYPE_PRINTER ||
+ ( mpDoc->GetRowFlags( rParam.mnCellY, nTab ) & CRFlags::ManualSize ) ||
+ ( rParam.mpCondSet && SfxItemState::SET ==
+ rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) )
+ bClip = true;
+ else
+ bSimClip = true;
+
+ // Show clip marks if height is at least 5pt too small and
+ // there are several lines of text.
+ // Not for asian vertical text, because that would interfere
+ // with the default right position of the text.
+ // Only with automatic line breaks, to avoid having to find
+ // the cells with the horizontal end of the text again.
+ if ( nEngineHeight - aCellSize.Height() > 100 &&
+ ( rParam.mbBreak || rParam.meOrient == SvxCellOrientation::Stacked ) &&
+ !rParam.mbAsianVertical && bMarkClipped &&
+ ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) )
+ {
+ ScCellInfo* pClipMarkCell = nullptr;
+ if ( bMerged )
+ {
+ // anywhere in the merged area...
+ SCCOL nClipX = ( rParam.mnX < nX1 ) ? nX1 : rParam.mnX;
+ pClipMarkCell = &pRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX);
+ }
+ else
+ pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX);
+
+ pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left?
+ bAnyClipped = true;
+
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
+ if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() )
+ aAreaParam.maClipRect.AdjustRight( -nMarkPixel );
+ }
+ }
+
+ Point aURLStart;
+
+ { // Clip marks are already handled in GetOutputArea
+ ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect)
+ : aAreaParam.maClipRect, bClip, bSimClip, mpDev, bMetaFile);
+
+ Point aLogicStart;
+ if (rParam.mbPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+
+ tools::Long nAvailWidth = aCellSize.Width();
+ // space for AutoFilter is already handled in GetOutputArea
+
+ // horizontal alignment
+
+ if (rParam.meHorJustResult==SvxCellHorJustify::Right)
+ aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
+ else if (rParam.meHorJustResult==SvxCellHorJustify::Center)
+ aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
+
+ // paper size is subtracted below
+ aLogicStart.AdjustX(nEngineWidth );
+
+ // vertical adjustment is within the EditEngine
+ if (rParam.mbPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() );
+ else
+ aLogicStart.AdjustY(nTopM );
+
+ aURLStart = aLogicStart; // copy before modifying for orientation
+
+ // bMoveClipped handling has been replaced by complete alignment
+ // handling (also extending to the left).
+
+ // with SetVertical, the start position is top left of
+ // the whole output area, not the text itself
+ aLogicStart.AdjustX( -(rParam.mpEngine->GetPaperSize().Width()) );
+
+ rParam.mpEngine->Draw(*mpDev, aLogicStart);
+ }
+
+ rParam.adjustForHyperlinkInPDF(aURLStart, mpDev);
+}
+
+void ScOutputData::DrawEdit(bool bPixelToLogic)
+{
+ InitOutputEditEngine();
+
+ bool bHyphenatorSet = false;
+ const ScPatternAttr* pOldPattern = nullptr;
+ const SfxItemSet* pOldCondSet = nullptr;
+ const SfxItemSet* pOldPreviewFontSet = nullptr;
+ ScRefCellValue aCell;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ nInitPosX += nMirrorW - 1;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ SCCOL nLastContentCol = mpDoc->MaxCol();
+ if ( nX2 < mpDoc->MaxCol() )
+ {
+ SCROW nEndRow;
+ mpDoc->GetCellArea(nTab, nLastContentCol, nEndRow);
+ }
+
+ tools::Long nRowPosY = nScrY;
+ for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 of the rest of the merged
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+
+ if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
+
+ if ( pThisRowInfo->bChanged || nArrY==0 )
+ {
+ tools::Long nPosX = 0;
+ for (SCCOL nX=0; nX<=nX2; nX++) // due to overflow
+ {
+ std::unique_ptr< ScPatternAttr > pPreviewPattr;
+ if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
+
+ if (pThisRowInfo->basicCellInfo(nX).bEditEngine)
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+
+ SCCOL nCellX = nX; // position where the cell really starts
+ SCROW nCellY = nY;
+ bool bDoCell = false;
+
+ tools::Long nPosY = nRowPosY;
+ if ( nArrY == 0 )
+ {
+ nPosY = nScrY;
+ nY = pRowInfo[1].nRowNo;
+ SCCOL nOverX; // start of the merged cells
+ SCROW nOverY;
+ if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, true ))
+ {
+ nCellX = nOverX;
+ nCellY = nOverY;
+ bDoCell = true;
+ }
+ }
+ else if ( nX == nX2 && pThisRowInfo->cellInfo(nX).maCell.isEmpty() )
+ {
+ // Rest of a long text further to the right?
+
+ SCCOL nTempX=nX;
+ while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY ))
+ ++nTempX;
+
+ if ( nTempX > nX &&
+ !IsEmptyCellText( pThisRowInfo, nTempX, nY ) &&
+ !mpDoc->HasAttrib( nTempX,nY,nTab, nX,nY,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ {
+ nCellX = nTempX;
+ bDoCell = true;
+ }
+ }
+ else
+ {
+ bDoCell = true;
+ }
+
+ if ( bDoCell && bEditMode && nCellX == nEditCol && nCellY == nEditRow )
+ bDoCell = false;
+
+ const ScPatternAttr* pPattern = nullptr;
+ const SfxItemSet* pCondSet = nullptr;
+ if (bDoCell)
+ {
+ if ( nCellY == nY && nCellX >= nX1 && nCellX <= nX2 &&
+ !mpDoc->ColHidden(nCellX, nTab) )
+ {
+ ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX);
+ pPattern = rCellInfo.pPatternAttr;
+ pCondSet = rCellInfo.pConditionSet;
+ aCell = rCellInfo.maCell;
+ }
+ else // get from document
+ {
+ pPattern = mpDoc->GetPattern( nCellX, nCellY, nTab );
+ pCondSet = mpDoc->GetCondResult( nCellX, nCellY, nTab );
+ GetVisibleCell( nCellX, nCellY, nTab, aCell );
+ }
+ if (aCell.isEmpty())
+ bDoCell = false;
+ }
+ if (bDoCell)
+ {
+ if ( mpDoc->GetPreviewCellStyle() )
+ {
+ if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, nTab ) )
+ {
+ pPreviewPattr.reset( new ScPatternAttr(*pPattern) );
+ pPreviewPattr->SetStyleSheet(pPreviewStyle);
+ pPattern = pPreviewPattr.get();
+ }
+ }
+ SfxItemSet* pPreviewFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, nTab );
+ lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False)
+
+ // fdo#32530: Check if the first character is RTL.
+ OUString aStr = mpDoc->GetString(nCellX, nCellY, nTab);
+
+ DrawEditParam aParam(pPattern, pCondSet, lcl_SafeIsValue(aCell));
+ const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, nTab );
+ aParam.meHorJustContext = getAlignmentFromContext( aParam.meHorJustAttr,
+ aParam.mbCellIsValue, aStr, *pPattern, pCondSet, mpDoc, nTab, bNumberFormatIsText);
+ aParam.meHorJustResult = (aParam.meHorJustAttr == SvxCellHorJustify::Block) ?
+ SvxCellHorJustify::Block : aParam.meHorJustContext;
+ aParam.mbPixelToLogic = bPixelToLogic;
+ aParam.mbHyphenatorSet = bHyphenatorSet;
+ aParam.mpEngine = mxOutputEditEngine.get();
+ aParam.maCell = aCell;
+ aParam.mnArrY = nArrY;
+ aParam.mnX = nX;
+ aParam.mnCellX = nCellX;
+ aParam.mnCellY = nCellY;
+ aParam.mnPosX = nPosX;
+ aParam.mnPosY = nPosY;
+ aParam.mnInitPosX = nInitPosX;
+ aParam.mpPreviewFontSet = pPreviewFontSet;
+ aParam.mpOldPattern = pOldPattern;
+ aParam.mpOldCondSet = pOldCondSet;
+ aParam.mpOldPreviewFontSet = pOldPreviewFontSet;
+ aParam.mpThisRowInfo = pThisRowInfo;
+ if (mpSpellCheckCxt)
+ aParam.mpMisspellRanges = mpSpellCheckCxt->getMisspellRanges(nCellX, nCellY);
+
+ if (aParam.meHorJustAttr == SvxCellHorJustify::Repeat)
+ {
+ // ignore orientation/rotation if "repeat" is active
+ aParam.meOrient = SvxCellOrientation::Standard;
+ }
+ switch (aParam.meOrient)
+ {
+ case SvxCellOrientation::BottomUp:
+ DrawEditBottomTop(aParam);
+ break;
+ case SvxCellOrientation::TopBottom:
+ DrawEditTopBottom(aParam);
+ break;
+ case SvxCellOrientation::Stacked:
+ // this can be vertically stacked or asian vertical.
+ DrawEditStacked(aParam);
+ break;
+ default:
+ DrawEditStandard(aParam);
+ }
+
+ // Retrieve parameters for next iteration.
+ pOldPattern = aParam.mpOldPattern;
+ pOldCondSet = aParam.mpOldCondSet;
+ pOldPreviewFontSet = aParam.mpOldPreviewFontSet;
+ bHyphenatorSet = aParam.mbHyphenatorSet;
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nRowPosY += pRowInfo[nArrY].nHeight;
+ }
+
+ if (mrTabInfo.maArray.HasCellRotation())
+ {
+ DrawRotated(bPixelToLogic); //! call from outside ?
+ }
+}
+
+void ScOutputData::DrawRotated(bool bPixelToLogic)
+{
+ InitOutputEditEngine();
+ //! store nRotMax
+ SCCOL nRotMax = nX2;
+ for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
+ if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
+ nRotMax = pRowInfo[nRotY].nRotMaxCol;
+
+ ScModule* pScMod = SC_MOD();
+ Color nConfBackColor = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ bool bCellContrast = mbUseStyleColor &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+
+ bool bHyphenatorSet = false;
+ const ScPatternAttr* pPattern;
+ const SfxItemSet* pCondSet;
+ const ScPatternAttr* pOldPattern = nullptr;
+ const SfxItemSet* pOldCondSet = nullptr;
+ ScRefCellValue aCell;
+
+ tools::Long nInitPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ nInitPosX += nMirrorW - 1;
+ }
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nRowPosY = nScrY;
+ for (SCSIZE nArrY=0; nArrY+1<nArrCount; nArrY++) // 0 for the rest of the merged
+ {
+ RowInfo* pThisRowInfo = &pRowInfo[nArrY];
+ tools::Long nCellHeight = static_cast<tools::Long>(pThisRowInfo->nHeight);
+ if (nArrY==1) nRowPosY = nScrY; // positions before are calculated individually
+
+ if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE )
+ {
+ tools::Long nPosX = 0;
+ for (SCCOL nX=0; nX<=nRotMax; nX++)
+ {
+ if (nX==nX1) nPosX = nInitPosX; // positions before nX1 are calculated individually
+
+ const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
+ if ( pInfo->nRotateDir != ScRotateDir::NONE )
+ {
+ SCROW nY = pThisRowInfo->nRowNo;
+
+ bool bHidden = false;
+ if (bEditMode)
+ if ( nX == nEditCol && nY == nEditRow )
+ bHidden = true;
+
+ if (!bHidden)
+ {
+ lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False)
+
+ tools::Long nPosY = nRowPosY;
+
+ //! rest from merged cells further up do not work!
+
+ bool bFromDoc = false;
+ pPattern = pInfo->pPatternAttr;
+ pCondSet = pInfo->pConditionSet;
+ if (!pPattern)
+ {
+ pPattern = mpDoc->GetPattern( nX, nY, nTab );
+ bFromDoc = true;
+ }
+ aCell = pInfo->maCell;
+ if (bFromDoc)
+ pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
+
+ if (aCell.isEmpty() && nX>nX2)
+ GetVisibleCell( nX, nY, nTab, aCell );
+
+ if (aCell.isEmpty() || IsEmptyCellText(pThisRowInfo, nX, nY))
+ bHidden = true; // nRotateDir is also set without a cell
+
+ tools::Long nCellWidth = static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nX).nWidth);
+
+ SvxCellHorJustify eHorJust =
+ pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet).GetValue();
+ bool bBreak = ( eHorJust == SvxCellHorJustify::Block ) ||
+ pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue();
+ bool bRepeat = ( eHorJust == SvxCellHorJustify::Repeat && !bBreak );
+ bool bShrink = !bBreak && !bRepeat &&
+ pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue();
+ SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
+
+ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE);
+ bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1;
+
+ tools::Long nStartX = nPosX;
+ tools::Long nStartY = nPosY;
+ if (nX<nX1)
+ {
+ if ((bBreak || eOrient!=SvxCellOrientation::Standard) && !bMerged)
+ bHidden = true;
+ else
+ {
+ nStartX = nInitPosX;
+ SCCOL nCol = nX1;
+ while (nCol > nX)
+ {
+ --nCol;
+ nStartX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
+ }
+ }
+ }
+ tools::Long nCellStartX = nStartX;
+
+ // omit substitute representation of small text
+
+ if (!bHidden)
+ {
+ tools::Long nOutWidth = nCellWidth - 1;
+ tools::Long nOutHeight = nCellHeight;
+
+ if ( bMerged )
+ {
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=1; i<nCountX; i++)
+ nOutWidth += mpDoc->GetColWidth(nX+i,nTab) * mnPPTX;
+ SCROW nCountY = pMerge->GetRowMerge();
+ nOutHeight += mpDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, nTab, mnPPTY);
+ }
+
+ SvxCellVerJustify eVerJust =
+ pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet).GetValue();
+
+ // syntax mode is ignored here...
+
+ // StringDiffer doesn't look at hyphenate, language items
+ if ( !SfxPoolItem::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet )
+ {
+ auto pSet = std::make_unique<SfxItemSet>( mxOutputEditEngine->GetEmptyItemSet() );
+ pPattern->FillEditItemSet( pSet.get(), pCondSet );
+
+ // adjustment for EditEngine
+ SvxAdjust eSvxAdjust = SvxAdjust::Left;
+ if (eOrient==SvxCellOrientation::Stacked)
+ eSvxAdjust = SvxAdjust::Center;
+ // adjustment for bBreak is omitted here
+ pSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
+
+ bool bParaHyphenate = pSet->Get(EE_PARA_HYPHENATE).GetValue();
+ mxOutputEditEngine->SetDefaults( std::move(pSet) );
+ pOldPattern = pPattern;
+ pOldCondSet = pCondSet;
+
+ EEControlBits nControl = mxOutputEditEngine->GetControlWord();
+ if (eOrient==SvxCellOrientation::Stacked)
+ nControl |= EEControlBits::ONECHARPERLINE;
+ else
+ nControl &= ~EEControlBits::ONECHARPERLINE;
+ mxOutputEditEngine->SetControlWord( nControl );
+
+ if ( !bHyphenatorSet && bParaHyphenate )
+ {
+ // set hyphenator the first time it is needed
+ css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
+ mxOutputEditEngine->SetHyphenator( xXHyphenator );
+ bHyphenatorSet = true;
+ }
+
+ Color aBackCol =
+ pPattern->GetItem( ATTR_BACKGROUND, pCondSet ).GetColor();
+ if ( mbUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) )
+ aBackCol = nConfBackColor;
+ mxOutputEditEngine->SetBackgroundColor( aBackCol );
+ }
+
+ // margins
+
+ //! change position and paper size to EditUtil !!!
+
+ const SvxMarginItem* pMargin =
+ &pPattern->GetItem(ATTR_MARGIN, pCondSet);
+ sal_uInt16 nIndent = 0;
+ if ( eHorJust == SvxCellHorJustify::Left )
+ nIndent = pPattern->GetItem(ATTR_INDENT, pCondSet).GetValue();
+
+ tools::Long nTotalHeight = nOutHeight; // without subtracting the margin
+ if ( bPixelToLogic )
+ nTotalHeight = mpRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height();
+
+ tools::Long nLeftM = static_cast<tools::Long>( (pMargin->GetLeftMargin() + nIndent) * mnPPTX );
+ tools::Long nTopM = static_cast<tools::Long>( pMargin->GetTopMargin() * mnPPTY );
+ tools::Long nRightM = static_cast<tools::Long>( pMargin->GetRightMargin() * mnPPTX );
+ tools::Long nBottomM = static_cast<tools::Long>( pMargin->GetBottomMargin() * mnPPTY );
+ nStartX += nLeftM;
+ nStartY += nTopM;
+ nOutWidth -= nLeftM + nRightM;
+ nOutHeight -= nTopM + nBottomM;
+
+ // rotate here already, to adjust paper size for page breaks
+ Degree100 nAttrRotate;
+ double nSin = 0.0;
+ double nCos = 1.0;
+ SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
+ if ( eOrient == SvxCellOrientation::Standard )
+ {
+ nAttrRotate = pPattern->
+ GetItem(ATTR_ROTATE_VALUE, pCondSet).GetValue();
+ if ( nAttrRotate )
+ {
+ eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
+
+ // tdf#143377 To use the same limits to avoid too big Skew
+ // with TextOrientation in Calc, use 1/2 degree here, too.
+ // This equals '50' in the notation here (100th degree)
+ static const sal_Int32 nMinRad(50);
+
+ // bring nAttrRotate to the range [0..36000[
+ nAttrRotate = Degree100(((nAttrRotate.get() % 36000) + 36000) % 36000);
+
+ // check for to be avoided extreme values and correct
+ if (nAttrRotate < Degree100(nMinRad))
+ {
+ // range [0..50]
+ nAttrRotate = Degree100(nMinRad);
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+ else if (nAttrRotate > Degree100(36000 - nMinRad))
+ {
+ // range [35950..36000[
+ nAttrRotate = Degree100(36000 - nMinRad);
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+ else if (nAttrRotate > Degree100(18000 - nMinRad) && (nAttrRotate < Degree100(18000 + nMinRad)))
+ {
+ // range 50 around 18000, [17950..18050]
+ nAttrRotate = (nAttrRotate > Degree100(18000))
+ ? Degree100(18000 + nMinRad)
+ : Degree100(18000 - nMinRad);
+ eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
+ }
+
+ if ( bLayoutRTL )
+ {
+ // keep in range [0..36000[
+ nAttrRotate = Degree100(36000 - nAttrRotate.get());
+ }
+
+ double nRealOrient = toRadians(nAttrRotate); // 1/100 degree
+ nCos = cos( nRealOrient );
+
+ // tdf#143377 new strategy: instead of using zero for nSin, which
+ // would be the *correct* value, continue with the corrected maximum
+ // allowed value which is then *not* zero. This is similar to
+ // the behaviour before where (just due to numerical unprecisions)
+ // nSin was also not zero (pure coincidence), but very close to it.
+ // I checked and tried to make safe all places below that use
+ // nSin and divide by it, but there is too much going on and that
+ // would not be safe, so rely on the same values as before, but
+ // now numerically limited to not get the Skew go havoc
+ nSin = sin( nRealOrient );
+ }
+ }
+
+ Size aPaperSize( 1000000, 1000000 );
+ if (eOrient==SvxCellOrientation::Stacked)
+ aPaperSize.setWidth( nOutWidth ); // to center
+ else if (bBreak)
+ {
+ if (nAttrRotate)
+ {
+ //! the correct paper size for break depends on the number
+ //! of rows, as long as the rows can not be outputted individually
+ //! offsetted -> therefore unlimited, so no wrapping.
+ //! With offset rows the following would be correct:
+ aPaperSize.setWidth( static_cast<tools::Long>(nOutHeight / fabs(nSin)) );
+ }
+ else if (eOrient == SvxCellOrientation::Standard)
+ aPaperSize.setWidth( nOutWidth );
+ else
+ aPaperSize.setWidth( nOutHeight - 1 );
+ }
+ if (bPixelToLogic)
+ mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ mxOutputEditEngine->SetPaperSize(aPaperSize); // scale is always 1
+
+ // read data from cell
+
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ if (aCell.getEditText())
+ mxOutputEditEngine->SetTextCurrentDefaults(*aCell.getEditText());
+ else
+ {
+ OSL_FAIL("pData == 0");
+ }
+ }
+ else
+ {
+ sal_uInt32 nFormat = pPattern->GetNumberFormat(
+ mpDoc->GetFormatTable(), pCondSet );
+ const Color* pColor;
+ OUString aString = ScCellFormat::GetString( aCell,
+ nFormat, &pColor,
+ *mpDoc->GetFormatTable(),
+ *mpDoc,
+ mbShowNullValues,
+ mbShowFormulas);
+
+ mxOutputEditEngine->SetTextCurrentDefaults(aString);
+ if ( pColor && !mbSyntaxMode && !( mbUseStyleColor && mbForceAutoColor ) )
+ lcl_SetEditColor( *mxOutputEditEngine, *pColor );
+ }
+
+ if ( mbSyntaxMode )
+ {
+ SetEditSyntaxColor(*mxOutputEditEngine, aCell);
+ }
+ else if ( mbUseStyleColor && mbForceAutoColor )
+ lcl_SetEditColor( *mxOutputEditEngine, COL_AUTO ); //! or have a flag at EditEngine
+
+ mxOutputEditEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight
+
+ tools::Long nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
+ tools::Long nEngineHeight = mxOutputEditEngine->GetTextHeight();
+
+ if (nAttrRotate && bBreak)
+ {
+ double nAbsCos = fabs( nCos );
+ double nAbsSin = fabs( nSin );
+
+ // adjust width of papersize for height of text
+ int nSteps = 5;
+ while (nSteps > 0)
+ {
+ // everything is in pixels
+ tools::Long nEnginePixel = mpRefDevice->LogicToPixel(
+ Size(0,nEngineHeight)).Height();
+ tools::Long nEffHeight = nOutHeight - static_cast<tools::Long>(nEnginePixel * nAbsCos) + 2;
+ tools::Long nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
+ bool bFits = ( nNewWidth >= aPaperSize.Width() );
+ if ( bFits )
+ nSteps = 0;
+ else
+ {
+ if ( nNewWidth < 4 )
+ {
+ // can't fit -> fall back to using half height
+ nEffHeight = nOutHeight / 2;
+ nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2;
+ nSteps = 0;
+ }
+ else
+ --nSteps;
+
+ // set paper width and get new text height
+ aPaperSize.setWidth( nNewWidth );
+ if (bPixelToLogic)
+ mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ mxOutputEditEngine->SetPaperSize(aPaperSize); // Scale is always 1
+ //mxOutputEditEngine->QuickFormatDoc( sal_True );
+
+ nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
+ nEngineHeight = mxOutputEditEngine->GetTextHeight();
+ }
+ }
+ }
+
+ tools::Long nRealWidth = nEngineWidth;
+ tools::Long nRealHeight = nEngineHeight;
+
+ // when rotated, adjust size
+ if (nAttrRotate)
+ {
+ double nAbsCos = fabs( nCos );
+ double nAbsSin = fabs( nSin );
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nEngineWidth = static_cast<tools::Long>( nRealWidth * nAbsCos +
+ nRealHeight * nAbsSin );
+ else
+ nEngineWidth = static_cast<tools::Long>( nRealHeight / nAbsSin );
+ //! limit !!!
+
+ nEngineHeight = static_cast<tools::Long>( nRealHeight * nAbsCos +
+ nRealWidth * nAbsSin );
+ }
+
+ if (!nAttrRotate) // only rotated text here
+ bHidden = true; //! check first !!!
+
+ //! omit which doesn't stick out
+
+ if (!bHidden)
+ {
+ Size aClipSize( nScrX+nScrW-nStartX, nScrY+nScrH-nStartY );
+
+ // go on writing
+
+ Size aCellSize;
+ if (bPixelToLogic)
+ aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) );
+ else
+ aCellSize = Size( nOutWidth, nOutHeight ); // scale is one
+
+ tools::Long nGridWidth = nEngineWidth;
+ bool bNegative = false;
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ {
+ nGridWidth = aCellSize.Width() +
+ std::abs(static_cast<tools::Long>( aCellSize.Height() * nCos / nSin ));
+ bNegative = ( pInfo->nRotateDir == ScRotateDir::Left );
+ if ( bLayoutRTL )
+ bNegative = !bNegative;
+ }
+
+ // use GetOutputArea to hide the grid
+ // (clip region is done manually below)
+ OutputAreaParam aAreaParam;
+
+ SCCOL nCellX = nX;
+ SCROW nCellY = nY;
+ SvxCellHorJustify eOutHorJust = eHorJust;
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ eOutHorJust = bNegative ? SvxCellHorJustify::Right : SvxCellHorJustify::Left;
+ tools::Long nNeededWidth = nGridWidth; // in pixel for GetOutputArea
+ if ( bPixelToLogic )
+ nNeededWidth = mpRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width();
+
+ GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth,
+ *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust),
+ false, false, true, aAreaParam );
+
+ if ( bShrink )
+ {
+ tools::Long nPixelWidth = bPixelToLogic ?
+ mpRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth;
+ tools::Long nNeededPixel = nPixelWidth + nLeftM + nRightM;
+
+ aAreaParam.mbLeftClip = aAreaParam.mbRightClip = true;
+
+ // always do height
+ ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
+ false, eOrient, nAttrRotate, bPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+
+ if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
+ {
+ // do width only if rotating within the cell (standard mode)
+ ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM,
+ true, eOrient, nAttrRotate, bPixelToLogic,
+ nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip );
+ }
+
+ // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine
+ // (but width is only valid for standard mode)
+ nRealWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth());
+ nRealHeight = mxOutputEditEngine->GetTextHeight();
+
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ nEngineWidth = static_cast<tools::Long>( nRealHeight / fabs( nSin ) );
+ }
+
+ tools::Long nClipStartX = nStartX;
+ if (nX<nX1)
+ {
+ //! clipping is not needed when on the left side of the window
+
+ if (nStartX<nScrX)
+ {
+ tools::Long nDif = nScrX - nStartX;
+ nClipStartX = nScrX;
+ aClipSize.AdjustWidth( -nDif );
+ }
+ }
+
+ tools::Long nClipStartY = nStartY;
+ if (nArrY==0 && nClipStartY < nRowPosY )
+ {
+ tools::Long nDif = nRowPosY - nClipStartY;
+ nClipStartY = nRowPosY;
+ aClipSize.AdjustHeight( -nDif );
+ }
+
+ if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ )
+ {
+ // only clip rotated output text at the page border
+ nClipStartX = nScrX;
+ aClipSize.setWidth( nScrW );
+ }
+
+ if (bPixelToLogic)
+ aAreaParam.maClipRect = mpRefDevice->PixelToLogic( tools::Rectangle(
+ Point(nClipStartX,nClipStartY), aClipSize ) );
+ else
+ aAreaParam.maClipRect = tools::Rectangle(Point(nClipStartX, nClipStartY),
+ aClipSize ); // Scale = 1
+
+ if (bMetaFile)
+ {
+ mpDev->Push();
+ mpDev->IntersectClipRegion( aAreaParam.maClipRect );
+ }
+ else
+ mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) );
+
+ Point aLogicStart;
+ if (bPixelToLogic)
+ aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) );
+ else
+ aLogicStart = Point(nStartX, nStartY);
+ if ( eOrient!=SvxCellOrientation::Standard || !bBreak )
+ {
+ tools::Long nAvailWidth = aCellSize.Width();
+ if (eType==OUTTYPE_WINDOW &&
+ eOrient!=SvxCellOrientation::Stacked &&
+ pInfo->bAutoFilter)
+ {
+ // filter drop-down width depends on row height
+ double fZoom = mpRefDevice ? static_cast<double>(mpRefDevice->GetMapMode().GetScaleY()) : 1.0;
+ fZoom = fZoom > 1.0 ? fZoom : 1.0;
+ if (bPixelToLogic)
+ nAvailWidth -= mpRefDevice->PixelToLogic(Size(0,fZoom * DROPDOWN_BITMAP_SIZE)).Height();
+ else
+ nAvailWidth -= fZoom * DROPDOWN_BITMAP_SIZE;
+ tools::Long nComp = nEngineWidth;
+ if (nAvailWidth<nComp) nAvailWidth=nComp;
+ }
+
+ // horizontal orientation
+
+ if (eOrient==SvxCellOrientation::Standard && !nAttrRotate)
+ {
+ if (eHorJust==SvxCellHorJustify::Right ||
+ eHorJust==SvxCellHorJustify::Center)
+ {
+ mxOutputEditEngine->SetUpdateLayout( false );
+
+ SvxAdjust eSvxAdjust =
+ (eHorJust==SvxCellHorJustify::Right) ?
+ SvxAdjust::Right : SvxAdjust::Center;
+ mxOutputEditEngine->SetDefaultItem(
+ SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
+
+ aPaperSize.setWidth( nOutWidth );
+ if (bPixelToLogic)
+ mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize));
+ else
+ mxOutputEditEngine->SetPaperSize(aPaperSize);
+
+ mxOutputEditEngine->SetUpdateLayout( true );
+ }
+ }
+ else
+ {
+ // rotated text is centered by default
+ if (eHorJust==SvxCellHorJustify::Right)
+ aLogicStart.AdjustX(nAvailWidth - nEngineWidth );
+ else if (eHorJust==SvxCellHorJustify::Center ||
+ eHorJust==SvxCellHorJustify::Standard)
+ aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 );
+ }
+ }
+
+ if ( bLayoutRTL )
+ {
+ if (bPixelToLogic)
+ aLogicStart.AdjustX( -(mpRefDevice->PixelToLogic(
+ Size( nCellWidth, 0 ) ).Width()) );
+ else
+ aLogicStart.AdjustX( -nCellWidth );
+ }
+
+ if ( eOrient==SvxCellOrientation::Standard ||
+ eOrient==SvxCellOrientation::Stacked || !bBreak )
+ {
+ if (eVerJust==SvxCellVerJustify::Bottom ||
+ eVerJust==SvxCellVerJustify::Standard)
+ {
+ if (bPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()
+ )).Height() );
+ else
+ aLogicStart.AdjustY(aCellSize.Height() - nEngineHeight );
+ }
+
+ else if (eVerJust==SvxCellVerJustify::Center)
+ {
+ if (bPixelToLogic)
+ aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,(
+ mpRefDevice->LogicToPixel(aCellSize).Height() -
+ mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height())
+ / 2)).Height() );
+ else
+ aLogicStart.AdjustY((aCellSize.Height() - nEngineHeight) / 2 );
+ }
+ }
+
+ // TOPBOTTOM and BOTTOMTOP are handled in DrawStrings/DrawEdit
+ OSL_ENSURE( eOrient == SvxCellOrientation::Standard && nAttrRotate,
+ "DrawRotated: no rotation" );
+
+ Degree10 nOriVal = 0_deg10;
+ if ( nAttrRotate )
+ {
+ // attribute is 1/100, Font 1/10 degrees
+ nOriVal = to<Degree10>(nAttrRotate);
+
+ double nAddX = 0.0;
+ double nAddY = 0.0;
+ if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD )
+ {
+ //! limit !!!
+ double nH = nRealHeight * nCos;
+ nAddX += nH * ( nCos / fabs(nSin) );
+ }
+ if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD )
+ nAddX -= nRealWidth * nCos;
+ if ( nSin < 0.0 )
+ nAddX -= nRealHeight * nSin;
+ if ( nSin > 0.0 )
+ nAddY += nRealWidth * nSin;
+ if ( nCos < 0.0 )
+ nAddY -= nRealHeight * nCos;
+
+ if ( eRotMode != SVX_ROTATE_MODE_STANDARD )
+ {
+ //! limit !!!
+ double nSkew = nTotalHeight * nCos / fabs(nSin);
+ if ( eRotMode == SVX_ROTATE_MODE_CENTER )
+ nAddX -= nSkew * 0.5;
+ if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) ||
+ ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) )
+ nAddX -= nSkew;
+
+ tools::Long nUp = 0;
+ if ( eVerJust == SvxCellVerJustify::Center )
+ nUp = ( aCellSize.Height() - nEngineHeight ) / 2;
+ else if ( eVerJust == SvxCellVerJustify::Top )
+ {
+ if ( nSin > 0.0 )
+ nUp = aCellSize.Height() - nEngineHeight;
+ }
+ else // BOTTOM / STANDARD
+ {
+ if ( nSin < 0.0 )
+ nUp = aCellSize.Height() - nEngineHeight;
+ }
+ if ( nUp )
+ nAddX += ( nUp * nCos / fabs(nSin) );
+ }
+
+ aLogicStart.AdjustX(static_cast<tools::Long>(nAddX) );
+ aLogicStart.AdjustY(static_cast<tools::Long>(nAddY) );
+ }
+
+ // bSimClip is not used here (because nOriVal is set)
+
+ mxOutputEditEngine->Draw(*mpDev, aLogicStart, nOriVal);
+
+ if (bMetaFile)
+ mpDev->Pop();
+ else
+ mpDev->SetClipRegion();
+ }
+ }
+ }
+ }
+ nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
+ }
+ }
+ nRowPosY += pRowInfo[nArrY].nHeight;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/output3.cxx b/sc/source/ui/view/output3.cxx
new file mode 100644
index 0000000000..bc6efec654
--- /dev/null
+++ b/sc/source/ui/view/output3.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 <sal/config.h>
+
+#include <o3tl/unit_conversion.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+
+#include <output.hxx>
+#include <drwlayer.hxx>
+#include <document.hxx>
+#include <tabvwsh.hxx>
+
+#include <svx/fmview.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+
+// #i72502#
+Point ScOutputData::PrePrintDrawingLayer(tools::Long nLogStX, tools::Long nLogStY )
+{
+ tools::Rectangle aRect;
+ SCCOL nCol;
+ Point aOffset;
+ tools::Long nLayoutSign(bLayoutRTL ? -1 : 1);
+
+ for (nCol=0; nCol<nX1; nCol++)
+ aOffset.AdjustX( -(mpDoc->GetColWidth( nCol, nTab ) * nLayoutSign) );
+ aOffset.AdjustY( -sal_Int32(mpDoc->GetRowHeight( 0, nY1-1, nTab )) );
+
+ tools::Long nDataWidth = 0;
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ nDataWidth += mpDoc->GetColWidth( nCol, nTab );
+
+ if ( bLayoutRTL )
+ aOffset.AdjustX(nDataWidth );
+
+ aRect.SetLeft( -aOffset.X() );
+ aRect.SetRight( -aOffset.X() );
+ aRect.SetTop( -aOffset.Y() );
+ aRect.SetBottom( -aOffset.Y() );
+
+ Point aMMOffset( aOffset );
+ aMMOffset.setX(o3tl::convert(aMMOffset.X(), o3tl::Length::twip, o3tl::Length::mm100));
+ aMMOffset.setY(o3tl::convert(aMMOffset.Y(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ if (!bMetaFile)
+ aMMOffset += Point( nLogStX, nLogStY );
+
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ aRect.AdjustRight(mpDoc->GetColWidth( nCol, nTab ) );
+ aRect.AdjustBottom(mpDoc->GetRowHeight( nY1, nY2, nTab ) );
+
+ aRect.SetLeft(o3tl::convert(aRect.Left(), o3tl::Length::twip, o3tl::Length::mm100));
+ aRect.SetTop(o3tl::convert(aRect.Top(), o3tl::Length::twip, o3tl::Length::mm100));
+ aRect.SetRight(o3tl::convert(aRect.Right(), o3tl::Length::twip, o3tl::Length::mm100));
+ aRect.SetBottom(o3tl::convert(aRect.Bottom(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ // #i76114# MapMode has to be set because BeginDrawLayers uses GetPaintRegion
+ MapMode aOldMode = mpDev->GetMapMode();
+ if (!bMetaFile)
+ mpDev->SetMapMode( MapMode( MapUnit::Map100thMM, aMMOffset, aOldMode.GetScaleX(), aOldMode.GetScaleY() ) );
+
+ // #i74769# work with SdrPaintWindow directly
+ // #i76114# pass bDisableIntersect = true, because the intersection of the table area
+ // with the Window's paint region can be empty
+ vcl::Region aRectRegion(aRect);
+ mpTargetPaintWindow = pLocalDrawView->BeginDrawLayers(mpDev, aRectRegion, true);
+ OSL_ENSURE(mpTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");
+
+ if (!bMetaFile)
+ mpDev->SetMapMode( aOldMode );
+ }
+ }
+
+ return aMMOffset;
+}
+
+// #i72502#
+void ScOutputData::PostPrintDrawingLayer(const Point& rMMOffset) // #i74768#
+{
+ // #i74768# just use offset as in PrintDrawingLayer() to also get the form controls
+ // painted with offset
+ MapMode aOldMode = mpDev->GetMapMode();
+
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( MapMode( MapUnit::Map100thMM, rMMOffset, aOldMode.GetScaleX(), aOldMode.GetScaleY() ) );
+ }
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ // #i74769# work with SdrPaintWindow directly
+ pLocalDrawView->EndDrawLayers(*mpTargetPaintWindow, true);
+ mpTargetPaintWindow = nullptr;
+ }
+ }
+
+ // #i74768#
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( aOldMode );
+ }
+}
+
+// #i72502#
+void ScOutputData::PrintDrawingLayer(SdrLayerID nLayer, const Point& rMMOffset)
+{
+ bool bHideAllDrawingLayer(false);
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ bHideAllDrawingLayer = pLocalDrawView->getHideOle() && pLocalDrawView->getHideChart()
+ && pLocalDrawView->getHideDraw() && pLocalDrawView->getHideFormControl();
+ }
+ }
+
+ if(bHideAllDrawingLayer || (!mpDoc->GetDrawLayer()))
+ {
+ return;
+ }
+
+ MapMode aOldMode = mpDev->GetMapMode();
+
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( MapMode( MapUnit::Map100thMM, rMMOffset, aOldMode.GetScaleX(), aOldMode.GetScaleY() ) );
+ }
+
+ DrawSelectiveObjects( nLayer );
+
+ if (!bMetaFile)
+ {
+ mpDev->SetMapMode( aOldMode );
+ }
+}
+
+void ScOutputData::DrawSelectiveObjects(SdrLayerID nLayer)
+{
+ ScDrawLayer* pModel = mpDoc->GetDrawLayer();
+ if (!pModel)
+ return;
+
+ // #i46362# high contrast mode (and default text direction) must be handled
+ // by the application, so it's still needed when using DrawLayer().
+
+ SdrOutliner& rOutl = pModel->GetDrawOutliner();
+ rOutl.EnableAutoColor( mbUseStyleColor );
+ rOutl.SetDefaultHorizontalTextDirection(
+ mpDoc->GetEditTextDirection( nTab ) );
+
+ // #i69767# The hyphenator must be set (used to be before drawing a text shape with hyphenation).
+ // LinguMgr::GetHyphenator (EditEngine) uses a wrapper now that creates the real hyphenator on demand,
+ // so it's not a performance problem to call UseHyphenator even when it's not needed.
+
+ pModel->UseHyphenator();
+
+ DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
+ if ( mbUseStyleColor && Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill |
+ DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+ }
+
+ if(pViewShell || pDrawView)
+ {
+ SdrView* pLocalDrawView = pDrawView ? pDrawView : pViewShell->GetScDrawView();
+
+ if(pLocalDrawView)
+ {
+ SdrPageView* pPageView = pLocalDrawView->GetSdrPageView();
+
+ if(pPageView)
+ {
+ if (nullptr != pPageView->FindPageWindow(*mpDev))
+ {
+ // Target OutputDevice is registered for this view
+ // (as it should be), we can just render
+ pPageView->DrawLayer(sal::static_int_cast<SdrLayerID>(nLayer), mpDev);
+ }
+ else if (0 != pPageView->PageWindowCount())
+ {
+ // We need to temporarily make the target OutputDevice being
+ // 'known/registered' in the paint mechanism so that
+ // SdrPageView::DrawLayer can find it.
+ // This situation can occur when someone interprets the
+ // OutputDevice parameter that gets handed over to DrawLayer
+ // (or other SdrPageView repaint methods) to be there to
+ // define a new render target.
+ // This is *not* the case: This parameter is used to
+ // *identify* an already registered target-OutputDevice.
+ // The default is even to call with a nullptr -> that triggers
+ // the repaint for *all* registered OutputDevices/Windows.
+ // Since this is a common and known misinterpretation it
+ // is good to offer workarounds in the code - there are some
+ // already.
+ // For now - use an already existing 'patch mechanism' and
+ // 'smuggle' the unknown/temporary OutputDevice as a
+ // temporary SdrPaintWindow to the SdrPageWindow, that is
+ // not very expensive.
+ // NOTE: Just using the 1st SdrPageWindow here will be OK
+ // in most cases, the splitting of a view is only used
+ // in calc nowadays and should have identical zoom.
+ // Still, trigger a warning...
+ OSL_ENSURE(1 == pPageView->PageWindowCount(),
+ "ScOutputData::DrawSelectiveObjects: More than one SdrPageView, still using 1st one (!)");
+ SdrPageWindow* patchedPageWindow(pPageView->GetPageWindow(0));
+ assert(nullptr != patchedPageWindow && "SdrPageWindow *must* exist when 0 != PageWindowCount()");
+ SdrPaintWindow temporaryPaintWindow(*pLocalDrawView, *mpDev);
+ SdrPaintWindow* previousPaintWindow(patchedPageWindow->patchPaintWindow(temporaryPaintWindow));
+ pPageView->DrawLayer(sal::static_int_cast<SdrLayerID>(nLayer), mpDev);
+ patchedPageWindow->unpatchPaintWindow(previousPaintWindow);
+ }
+ else
+ {
+ // There does not even exist a SdrPageWindow. Still call the
+ // paint to get the paint done, but be aware that this will create
+ // temporary SdrPaintWindow and SdrPageWindow and - due to the
+ // former - will not be able to use the decomposition buffering
+ // in the VC/VOC/OC mechanism. For that reason this might be
+ // somewhat 'expensive'.
+ // You will also get a warning about it (see OSL_FAIL in
+ // SdrPageView::DrawLayer)
+ pPageView->DrawLayer(sal::static_int_cast<SdrLayerID>(nLayer), mpDev);
+ }
+ }
+ }
+ }
+
+ mpDev->SetDrawMode(nOldDrawMode);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/overlayobject.cxx b/sc/source/ui/view/overlayobject.cxx
new file mode 100644
index 0000000000..a564265a95
--- /dev/null
+++ b/sc/source/ui/view/overlayobject.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 <overlayobject.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <vcl/settings.hxx>
+
+using sdr::overlay::OverlayObject;
+using sdr::overlay::OverlayManager;
+
+#define DASH_UPDATE_INTERVAL 500 // in msec
+
+ScOverlayDashedBorder::ScOverlayDashedBorder(const ::basegfx::B2DRange& rRange, const Color& rColor) :
+ OverlayObject(rColor),
+ mbToggle(true)
+{
+ // tdf#155414 include system "reduce animation" preferences
+ // Allow the system's "reduce animation" preferences to disable the
+ // Calc animated border when copying a selection of cells.
+ mbAllowsAnimation = (officecfg::Office::Common::VCL::AnimationsEnabled::get() && !MiscSettings::GetUseReducedAnimation());
+ maRange = rRange;
+}
+
+ScOverlayDashedBorder::~ScOverlayDashedBorder()
+{
+}
+
+void ScOverlayDashedBorder::Trigger(sal_uInt32 nTime)
+{
+ OverlayManager* pMgr = getOverlayManager();
+ if (pMgr)
+ {
+ SetTime(nTime + DASH_UPDATE_INTERVAL);
+ mbToggle = !mbToggle;
+ pMgr->InsertEvent(*this);
+ objectChange();
+ }
+}
+
+void ScOverlayDashedBorder::stripeDefinitionHasChanged()
+{
+ objectChange();
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ScOverlayDashedBorder::createOverlayObjectPrimitive2DSequence()
+{
+ using ::basegfx::B2DPolygon;
+ using ::basegfx::B2DPolyPolygon;
+
+ OverlayManager* pMgr = getOverlayManager();
+ if (!pMgr)
+ return drawinglayer::primitive2d::Primitive2DContainer();
+
+ basegfx::BColor aColorA = pMgr->getStripeColorA().getBColor();
+ basegfx::BColor aColorB = pMgr->getStripeColorB().getBColor();
+ if (!mbToggle)
+ ::std::swap(aColorA, aColorB);
+
+ const basegfx::B2DPolygon aPoly = basegfx::utils::createPolygonFromRect(maRange);
+ B2DPolyPolygon aPolygon(aPoly);
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
+ std::move(aPolygon), aColorA, aColorB, pMgr->getStripeLengthPixel()));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/pfuncache.cxx b/sc/source/ui/view/pfuncache.cxx
new file mode 100644
index 0000000000..fe563ba961
--- /dev/null
+++ b/sc/source/ui/view/pfuncache.cxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/multisel.hxx>
+#include <osl/diagnose.h>
+
+#include <pfuncache.hxx>
+#include <printfun.hxx>
+#include <docsh.hxx>
+#include <markdata.hxx>
+#include <prevloc.hxx>
+#include <utility>
+
+ScPrintFuncCache::ScPrintFuncCache( ScDocShell* pD, const ScMarkData& rMark,
+ ScPrintSelectionStatus aStatus ) :
+ aSelection(std::move( aStatus )),
+ pDocSh( pD ),
+ nTotalPages( 0 ),
+ bLocInitialized( false )
+{
+ // page count uses the stored cell widths for the printer anyway,
+ // so ScPrintFunc with the document's printer can be used to count
+
+ SfxPrinter* pPrinter = pDocSh->GetPrinter();
+
+ ScRange aRange;
+ const ScRange* pSelRange = nullptr;
+ if ( rMark.IsMarked() )
+ {
+ aRange = rMark.GetMarkArea();
+ pSelRange = &aRange;
+ }
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ // avoid repeated progress bars if row heights for all sheets are needed
+ if ( nTabCount > 1 && rMark.GetSelectCount() == nTabCount )
+ pDocSh->UpdatePendingRowHeights( nTabCount-1, true );
+
+ SCTAB nTab;
+ for ( nTab=0; nTab<nTabCount; nTab++ )
+ {
+ tools::Long nAttrPage = nTab > 0 ? nFirstAttr[nTab-1] : 1;
+
+ tools::Long nThisTab = 0;
+ if ( rMark.GetTableSelect( nTab ) )
+ {
+ ScPrintFunc aFunc( pDocSh, pPrinter, nTab, nAttrPage, 0, pSelRange, &aSelection.GetOptions() );
+ nThisTab = aFunc.GetTotalPages();
+ nFirstAttr.push_back( aFunc.GetFirstPageNo() ); // from page style or previous sheet
+ }
+ else
+ nFirstAttr.push_back( nAttrPage );
+
+ nPages.push_back( nThisTab );
+ nTotalPages += nThisTab;
+ }
+}
+
+ScPrintFuncCache::~ScPrintFuncCache()
+{
+}
+
+void ScPrintFuncCache::InitLocations( const ScMarkData& rMark, OutputDevice* pDev )
+{
+ if ( bLocInitialized )
+ return; // initialize only once
+
+ ScRange aRange;
+ const ScRange* pSelRange = nullptr;
+ if ( rMark.IsMarked() )
+ {
+ aRange = rMark.GetMarkArea();
+ pSelRange = &aRange;
+ }
+
+ tools::Long nRenderer = 0; // 0-based physical page number across sheets
+ tools::Long nTabStart = 0;
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab : rMark)
+ {
+ if (nTab >= nTabCount)
+ break;
+ ScPrintFunc aFunc( pDev, pDocSh, nTab, nFirstAttr[nTab], nTotalPages, pSelRange, &aSelection.GetOptions() );
+ aFunc.SetRenderFlag( true );
+
+ tools::Long nDisplayStart = GetDisplayStart( nTab );
+
+ for ( tools::Long nPage=0; nPage<nPages[nTab]; nPage++ )
+ {
+ Range aPageRange( nRenderer+1, nRenderer+1 );
+ MultiSelection aPage( aPageRange );
+ aPage.SetTotalRange( Range(0,RANGE_MAX) );
+ aPage.Select( aPageRange );
+
+ ScPreviewLocationData aLocData( &rDoc, pDev );
+ aFunc.DoPrint( aPage, nTabStart, nDisplayStart, false, &aLocData );
+
+ ScRange aCellRange;
+ tools::Rectangle aPixRect;
+ if ( aLocData.GetMainCellRange( aCellRange, aPixRect ) )
+ aLocations.emplace_back( nRenderer, aCellRange, aPixRect );
+
+ ++nRenderer;
+ }
+
+ nTabStart += nPages[nTab];
+ }
+
+ bLocInitialized = true;
+}
+
+bool ScPrintFuncCache::FindLocation( const ScAddress& rCell, ScPrintPageLocation& rLocation ) const
+{
+ auto aIter = std::find_if(aLocations.begin(), aLocations.end(),
+ [&rCell](const ScPrintPageLocation& rLoc) { return rLoc.aCellRange.Contains(rCell); });
+ if (aIter != aLocations.end())
+ {
+ rLocation = *aIter;
+ return true;
+ }
+ return false; // not found
+}
+
+bool ScPrintFuncCache::IsSameSelection( const ScPrintSelectionStatus& rStatus ) const
+{
+ return aSelection == rStatus;
+}
+
+SCTAB ScPrintFuncCache::GetTabForPage( tools::Long nPage ) const
+{
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTab = 0;
+ while ( nTab < nTabCount && nPage >= nPages[nTab] )
+ nPage -= nPages[nTab++];
+ if (nTab >= nTabCount)
+ nTab = nTabCount - 1;
+ return nTab;
+}
+
+tools::Long ScPrintFuncCache::GetTabStart( SCTAB nTab ) const
+{
+ tools::Long nRet = 0;
+ const SCTAB maxIndex = std::min(nTab, static_cast<SCTAB>(nPages.size()));
+ for ( SCTAB i=0; i<maxIndex; i++ )
+ nRet += nPages[i];
+ return nRet;
+}
+
+tools::Long ScPrintFuncCache::GetDisplayStart( SCTAB nTab ) const
+{
+ //! merge with lcl_GetDisplayStart in preview?
+
+ tools::Long nDisplayStart = 0;
+ ScDocument& rDoc = pDocSh->GetDocument();
+ for (SCTAB i=0; i<nTab; i++)
+ {
+ if ( rDoc.NeedPageResetAfterTab(i) )
+ nDisplayStart = 0;
+ else
+ {
+ if ( i < static_cast<SCTAB>(nPages.size()) )
+ nDisplayStart += nPages[i];
+ else
+ OSL_FAIL("nPages out of bounds, FIX IT!");
+ }
+ }
+ return nDisplayStart;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/pgbrksh.cxx b/sc/source/ui/view/pgbrksh.cxx
new file mode 100644
index 0000000000..a0cdb21be6
--- /dev/null
+++ b/sc/source/ui/view/pgbrksh.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <pgbrksh.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+
+#define ShellClass_ScPageBreakShell
+#include <scslots.hxx>
+
+SFX_IMPL_INTERFACE(ScPageBreakShell, SfxShell)
+
+void ScPageBreakShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("pagebreak");
+}
+
+ScPageBreakShell::ScPageBreakShell(ScTabViewShell* pViewSh)
+ : SfxShell(pViewSh)
+{
+ SetPool(&pViewSh->GetPool());
+ ScViewData& rViewData = pViewSh->GetViewData();
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager(pMgr);
+ if (!rViewData.GetDocument().IsUndoEnabled())
+ {
+ pMgr->SetMaxUndoActionCount(0);
+ }
+ SetName("PageBreak");
+}
+
+ScPageBreakShell::~ScPageBreakShell() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/pivotsh.cxx b/sc/source/ui/view/pivotsh.cxx
new file mode 100644
index 0000000000..b4ef807ffa
--- /dev/null
+++ b/sc/source/ui/view/pivotsh.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 <scitems.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <sc.hrc>
+#include <pivotsh.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dbdocfun.hxx>
+#include <uiitems.hxx>
+#include <scabstdlg.hxx>
+
+#define ShellClass_ScPivotShell
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScPivotShell, SfxShell)
+
+void ScPivotShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("pivot");
+}
+
+ScPivotShell::ScPivotShell( ScTabViewShell* pViewSh ) :
+ SfxShell(pViewSh),
+ pViewShell( pViewSh )
+{
+ SetPool( &pViewSh->GetPool() );
+ ScViewData& rViewData = pViewSh->GetViewData();
+ SfxUndoManager* pMgr = rViewData.GetSfxDocShell()->GetUndoManager();
+ SetUndoManager( pMgr );
+ if ( !rViewData.GetDocument().IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetName("Pivot");
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Pivot));
+}
+
+ScPivotShell::~ScPivotShell()
+{
+}
+
+void ScPivotShell::Execute( const SfxRequest& rReq )
+{
+ switch ( rReq.GetSlot() )
+ {
+ case SID_PIVOT_RECALC:
+ pViewShell->RecalcPivotTable();
+ break;
+
+ case SID_PIVOT_KILL:
+ pViewShell->DeletePivotTable();
+ break;
+
+ case SID_DP_FILTER:
+ {
+ ScDPObject* pDPObj = GetCurrDPObject();
+ if( pDPObj )
+ {
+ ScQueryParam aQueryParam;
+ SCTAB nSrcTab = 0;
+ const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
+ OSL_ENSURE( pDesc, "no sheet source for DP filter dialog" );
+ if( pDesc )
+ {
+ aQueryParam = pDesc->GetQueryParam();
+ nSrcTab = pDesc->GetSourceRange().aStart.Tab();
+ }
+
+ ScViewData& rViewData = pViewShell->GetViewData();
+ SfxItemSetFixed<SCITEM_QUERYDATA, SCITEM_QUERYDATA> aArgSet( pViewShell->GetPool() );
+ aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA, &rViewData, &aQueryParam ) );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScPivotFilterDlg> pDlg(pFact->CreateScPivotFilterDlg(
+ pViewShell->GetFrameWeld(), aArgSet, nSrcTab));
+
+ if( pDlg->Execute() == RET_OK )
+ {
+ ScSheetSourceDesc aNewDesc(&rViewData.GetDocument());
+ if( pDesc )
+ aNewDesc = *pDesc;
+
+ const ScQueryItem& rQueryItem = pDlg->GetOutputItem();
+ aNewDesc.SetQueryParam(rQueryItem.GetQueryData());
+
+ ScDPObject aNewObj( *pDPObj );
+ aNewObj.SetSheetDesc( aNewDesc );
+ ScDBDocFunc aFunc( *rViewData.GetDocShell() );
+ aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false );
+ rViewData.GetView()->CursorPosChanged(); // shells may be switched
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScPivotShell::GetState( SfxItemSet& rSet )
+{
+ ScDocShell* pDocSh = pViewShell->GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bDisable = pDocSh->IsReadOnly() || rDoc.GetChangeTrack();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_PIVOT_RECALC:
+ case SID_PIVOT_KILL:
+ {
+ //! move ReadOnly check to idl flags
+ if ( bDisable )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+ case SID_DP_FILTER:
+ {
+ ScDPObject* pDPObj = GetCurrDPObject();
+ if( bDisable || !pDPObj || !pDPObj->IsSheetData() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+ScDPObject* ScPivotShell::GetCurrDPObject()
+{
+ const ScViewData& rViewData = pViewShell->GetViewData();
+ return rViewData.GetDocument().GetDPAtCursor(
+ rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/preview.cxx b/sc/source/ui/view/preview.cxx
new file mode 100644
index 0000000000..33430883da
--- /dev/null
+++ b/sc/source/ui/view/preview.cxx
@@ -0,0 +1,1575 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/fmview.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/svdpagv.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/itemset.hxx>
+#include <tools/multisel.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/deleter.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <preview.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <printfun.hxx>
+#include <printopt.hxx>
+#include <stlpool.hxx>
+#include <undostyl.hxx>
+#include <drwlayer.hxx>
+#include <scmod.hxx>
+#include <markdata.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <AccessibleDocumentPagePreview.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <AccessibilityHints.hxx>
+#include <vcl/svapp.hxx>
+#include <viewutil.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <columnspanset.hxx>
+
+#include <memory>
+
+#define SC_PREVIEW_SHADOWSIZE 2
+
+static tools::Long lcl_GetDisplayStart( SCTAB nTab, const ScDocument* pDoc, std::vector<tools::Long>& nPages )
+{
+ tools::Long nDisplayStart = 0;
+ for (SCTAB i=0; i<nTab; i++)
+ {
+ if ( pDoc->NeedPageResetAfterTab(i) )
+ nDisplayStart = 0;
+ else
+ nDisplayStart += nPages[i];
+ }
+ return nDisplayStart;
+}
+
+ScPreview::ScPreview( vcl::Window* pParent, ScDocShell* pDocSh, ScPreviewShell* pViewSh ) :
+ Window( pParent ),
+ nPageNo( 0 ),
+ nZoom( 100 ),
+ nTabCount( 0 ),
+ nTabsTested( 0 ),
+ nTab( 0 ),
+ nTabPage( 0 ),
+ nTabStart( 0 ),
+ nDisplayStart( 0 ),
+ aDateTime( DateTime::SYSTEM ),
+ nTotalPages( 0 ),
+ pDocShell( pDocSh ),
+ pViewShell( pViewSh ),
+ bInGetState( false ),
+ bValid( false ),
+ bStateValid( false ),
+ bLocationValid( false ),
+ bInPaint( false ),
+ bInSetZoom( false ),
+ bLeftRulerMove( false ),
+ bRightRulerMove( false ),
+ bTopRulerMove( false ),
+ bBottomRulerMove( false ),
+ bHeaderRulerMove( false ),
+ bFooterRulerMove( false ),
+ bLeftRulerChange( false ),
+ bRightRulerChange( false ),
+ bTopRulerChange( false ),
+ bBottomRulerChange( false ),
+ bHeaderRulerChange( false ),
+ bFooterRulerChange( false ),
+ bPageMargin ( false ),
+ bColRulerMove( false ),
+ mbHasEmptyRangeTable(false),
+ nLeftPosition( 0 ),
+ mnScale( 0 ),
+ nColNumberButtonDown( 0 ),
+ nHeaderHeight ( 0 ),
+ nFooterHeight ( 0 )
+{
+ GetOutDev()->SetOutDevViewType( OutDevViewType::PrintPreview );
+ SetBackground();
+
+ SetHelpId( HID_SC_WIN_PREVIEW );
+
+ GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
+}
+
+ScPreview::~ScPreview()
+{
+ disposeOnce();
+}
+
+void ScPreview::dispose()
+{
+ pDrawView.reset();
+ pLocationData.reset();
+ vcl::Window::dispose();
+}
+
+void ScPreview::UpdateDrawView() // nTab must be right
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ ScDrawLayer* pModel = rDoc.GetDrawLayer(); // is not 0
+
+ if ( pModel )
+ {
+ SdrPage* pPage = pModel->GetPage(nTab);
+ if ( pDrawView && ( !pDrawView->GetSdrPageView() || pDrawView->GetSdrPageView()->GetPage() != pPage ) )
+ {
+ // convert the displayed Page of drawView (see below) does not work?!?
+ pDrawView.reset();
+ }
+
+ if ( !pDrawView ) // New Drawing?
+ {
+ pDrawView.reset( new FmFormView( *pModel, GetOutDev()) );
+
+ // The DrawView takes over the Design-Mode from the Model
+ // (Settings "In opening Draftmode"), therefore to restore here
+ pDrawView->SetDesignMode();
+ pDrawView->SetPrintPreview();
+ pDrawView->ShowSdrPage(pPage);
+ }
+ }
+ else if ( pDrawView )
+ {
+ pDrawView.reset(); // for this Chart is not needed
+ }
+}
+
+void ScPreview::TestLastPage()
+{
+ if (nPageNo < nTotalPages)
+ return;
+
+ if (nTotalPages)
+ {
+ nPageNo = nTotalPages - 1;
+ nTab = static_cast<SCTAB>(nPages.size()) -1;
+ while (nTab > 0 && !nPages[nTab]) // not the last empty Table
+ --nTab;
+ OSL_ENSURE(0 < static_cast<SCTAB>(nPages.size()),"are all tables empty?");
+ nTabPage = nPages[nTab] - 1;
+ nTabStart = 0;
+ for (sal_uInt16 i=0; i<nTab; i++)
+ nTabStart += nPages[i];
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nDisplayStart = lcl_GetDisplayStart( nTab, &rDoc, nPages );
+ }
+ else // empty Document
+ {
+ nTab = 0;
+ nPageNo = nTabPage = nTabStart = nDisplayStart = 0;
+ aState.nPrintTab = 0;
+ aState.nStartCol = aState.nEndCol = 0;
+ aState.nStartRow = aState.nEndRow = 0;
+ aState.nZoom = 0;
+ aState.nPagesX = aState.nPagesY = 0;
+ aState.nTabPages = aState.nTotalPages =
+ aState.nPageStart = aState.nDocPages = 0;
+ }
+}
+
+void ScPreview::CalcPages()
+{
+ weld::WaitObject aWait(GetFrameWeld());
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nTabCount = rDoc.GetTableCount();
+
+ if (maSelectedTabs.empty())
+ {
+ SCTAB nCurrentTab = ScDocShell::GetCurTab();
+ maSelectedTabs.insert(nCurrentTab);
+ }
+
+ SCTAB nStart = nTabsTested;
+ if (!bValid)
+ {
+ nStart = 0;
+ nTotalPages = 0;
+ nTabsTested = 0;
+ }
+
+ // update all pending row heights with a single progress bar,
+ // instead of a separate progress for each sheet from ScPrintFunc
+ pDocShell->UpdatePendingRowHeights( nTabCount-1, true );
+
+ // PrintOptions is passed to PrintFunc for SkipEmpty flag,
+ // but always all sheets are used (there is no selected sheet)
+ ScPrintOptions aOptions = SC_MOD()->GetPrintOptions();
+
+ while (nStart > static_cast<SCTAB>(nPages.size()))
+ nPages.push_back(0);
+ while (nStart > static_cast<SCTAB>(nFirstAttr.size()))
+ nFirstAttr.push_back(1);
+
+ for (SCTAB i=nStart; i<nTabCount; i++)
+ {
+ if ( i == static_cast<SCTAB>(nPages.size()))
+ nPages.push_back(0);
+ if ( i == static_cast<SCTAB>(nFirstAttr.size()))
+ nFirstAttr.push_back(1);
+ if (!aOptions.GetAllSheets() && maSelectedTabs.count(i) == 0)
+ {
+ nPages[i] = 0;
+ nFirstAttr[i] = 1;
+ continue;
+ }
+
+ tools::Long nAttrPage = i > 0 ? nFirstAttr[i-1] : 1;
+
+ tools::Long nThisStart = nTotalPages;
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, i, nAttrPage, 0, nullptr, &aOptions );
+ tools::Long nThisTab = aPrintFunc.GetTotalPages();
+ if (!aPrintFunc.HasPrintRange())
+ mbHasEmptyRangeTable = true;
+
+ nPages[i] = nThisTab;
+ nTotalPages += nThisTab;
+ nFirstAttr[i] = aPrintFunc.GetFirstPageNo(); // to keep or from template
+
+ if (nPageNo>=nThisStart && nPageNo<nTotalPages)
+ {
+ nTab = i;
+ nTabPage = nPageNo - nThisStart;
+ nTabStart = nThisStart;
+
+ aPrintFunc.GetPrintState( aState );
+ }
+ }
+
+ nDisplayStart = lcl_GetDisplayStart( nTab, &rDoc, nPages );
+
+ if (nTabCount > nTabsTested)
+ nTabsTested = nTabCount;
+
+ TestLastPage();
+
+ aState.nDocPages = nTotalPages;
+
+ bValid = true;
+ bStateValid = true;
+ DoInvalidate();
+}
+
+void ScPreview::RecalcPages() // only nPageNo is changed
+{
+ if (!bValid)
+ return; // then CalcPages is called
+
+ SCTAB nOldTab = nTab;
+
+ bool bDone = false;
+ while (nPageNo >= nTotalPages && nTabsTested < nTabCount)
+ {
+ CalcPages();
+ bDone = true;
+ }
+
+ if (!bDone)
+ {
+ tools::Long nPartPages = 0;
+ for (SCTAB i=0; i<nTabsTested && nTab < static_cast<SCTAB>(nPages.size()); i++)
+ {
+ tools::Long nThisStart = nPartPages;
+ nPartPages += nPages[i];
+
+ if (nPageNo>=nThisStart && nPageNo<nPartPages)
+ {
+ nTab = i;
+ nTabPage = nPageNo - nThisStart;
+ nTabStart = nThisStart;
+ }
+ }
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ nDisplayStart = lcl_GetDisplayStart( nTab, &rDoc, nPages );
+ }
+
+ TestLastPage(); // to test, if after last page
+
+ if ( nTab != nOldTab )
+ bStateValid = false;
+
+ DoInvalidate();
+}
+
+void ScPreview::DoPrint( ScPreviewLocationData* pFillLocation )
+{
+ if (!bValid)
+ {
+ CalcPages();
+ RecalcPages();
+ UpdateDrawView(); // Spreadsheet eventually changes
+ }
+
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+
+ bool bDoPrint = ( pFillLocation == nullptr );
+ bool bValidPage = ( nPageNo < nTotalPages );
+
+ ScModule* pScMod = SC_MOD();
+ const svtools::ColorConfig& rColorCfg = pScMod->GetColorConfig();
+ Color aBackColor( rColorCfg.GetColorValue(svtools::APPBACKGROUND).nColor );
+
+ if ( bDoPrint && ( aOffset.X() < 0 || aOffset.Y() < 0 ) && bValidPage )
+ {
+ SetMapMode( aMMMode );
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor(aBackColor);
+
+ Size aWinSize = GetOutDev()->GetOutputSize();
+ if ( aOffset.X() < 0 )
+ GetOutDev()->DrawRect(tools::Rectangle( 0, 0, -aOffset.X(), aWinSize.Height() ));
+ if ( aOffset.Y() < 0 )
+ GetOutDev()->DrawRect(tools::Rectangle( 0, 0, aWinSize.Width(), -aOffset.Y() ));
+ }
+
+ tools::Long nLeftMargin = 0;
+ tools::Long nRightMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nBottomMargin = 0;
+ bool bHeaderOn = false;
+ bool bFooterOn = false;
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ Size aLocalPageSize;
+ if ( bValidPage )
+ {
+ ScPrintOptions aOptions = pScMod->GetPrintOptions();
+
+ std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc;
+ if (bStateValid)
+ pPrintFunc.reset(new ScPrintFunc(GetOutDev(), pDocShell, aState, &aOptions));
+ else
+ pPrintFunc.reset(new ScPrintFunc(GetOutDev(), pDocShell, nTab, nFirstAttr[nTab], nTotalPages, nullptr, &aOptions));
+
+ pPrintFunc->SetOffset(aOffset);
+ pPrintFunc->SetManualZoom(nZoom);
+ pPrintFunc->SetDateTime(aDateTime);
+ pPrintFunc->SetClearFlag(true);
+ pPrintFunc->SetUseStyleColor( officecfg::Office::Common::Accessibility::IsForPagePreviews::get() );
+
+ pPrintFunc->SetDrawView( pDrawView.get() );
+
+ // MultiSelection for the one Page must produce something inconvenient
+ Range aPageRange( nPageNo+1, nPageNo+1 );
+ MultiSelection aPage( aPageRange );
+ aPage.SetTotalRange( Range(0,RANGE_MAX) );
+ aPage.Select( aPageRange );
+
+ tools::Long nPrinted = pPrintFunc->DoPrint( aPage, nTabStart, nDisplayStart, bDoPrint, pFillLocation );
+ OSL_ENSURE(nPrinted<=1, "What is happening?");
+
+ SetMapMode(aMMMode);
+
+ //init nLeftMargin ... in the ScPrintFunc::InitParam!!!
+ nLeftMargin = pPrintFunc->GetLeftMargin();
+ nRightMargin = pPrintFunc->GetRightMargin();
+ nTopMargin = pPrintFunc->GetTopMargin();
+ nBottomMargin = pPrintFunc->GetBottomMargin();
+ nHeaderHeight = pPrintFunc->GetHeader().nHeight;
+ nFooterHeight = pPrintFunc->GetFooter().nHeight;
+ bHeaderOn = pPrintFunc->GetHeader().bEnable;
+ bFooterOn = pPrintFunc->GetFooter().bEnable;
+ mnScale = pPrintFunc->GetZoom();
+
+ if ( bDoPrint && bPageMargin && pLocationData ) // don't make use of pLocationData while filling it
+ {
+ tools::Rectangle aPixRect;
+ tools::Rectangle aRectCellPosition;
+ tools::Rectangle aRectPosition;
+ pLocationData->GetMainCellRange( aPageArea, aPixRect );
+ mvRight.resize(aPageArea.aEnd.Col()+1);
+ if( !bLayoutRTL )
+ {
+ pLocationData->GetCellPosition( aPageArea.aStart, aRectPosition );
+ nLeftPosition = aRectPosition.Left();
+ for( SCCOL i = aPageArea.aStart.Col(); i <= aPageArea.aEnd.Col(); i++ )
+ {
+ pLocationData->GetCellPosition( ScAddress( i,aPageArea.aStart.Row(),aPageArea.aStart.Tab()),aRectCellPosition );
+ mvRight[i] = aRectCellPosition.Right();
+ }
+ }
+ else
+ {
+ pLocationData->GetCellPosition( aPageArea.aEnd, aRectPosition );
+ nLeftPosition = aRectPosition.Right()+1;
+
+ pLocationData->GetCellPosition( aPageArea.aStart,aRectCellPosition );
+ mvRight[ aPageArea.aEnd.Col() ] = aRectCellPosition.Left();
+ for( SCCOL i = aPageArea.aEnd.Col(); i > aPageArea.aStart.Col(); i-- )
+ {
+ pLocationData->GetCellPosition( ScAddress( i,aPageArea.aEnd.Row(),aPageArea.aEnd.Tab()),aRectCellPosition );
+ mvRight[ i-1 ] = mvRight[ i ] + aRectCellPosition.Right() - aRectCellPosition.Left() + 1;
+ }
+ }
+ }
+
+ if (nPrinted) // if not, draw everything grey
+ {
+ aLocalPageSize = pPrintFunc->GetPageSize();
+ aLocalPageSize.setWidth(
+ o3tl::convert(aLocalPageSize.Width(), o3tl::Length::twip, o3tl::Length::mm100));
+ aLocalPageSize.setHeight(
+ o3tl::convert(aLocalPageSize.Height(), o3tl::Length::twip, o3tl::Length::mm100));
+
+ nLeftMargin = o3tl::convert(nLeftMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ nRightMargin = o3tl::convert(nRightMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ nTopMargin = o3tl::convert(nTopMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ nBottomMargin = o3tl::convert(nBottomMargin, o3tl::Length::twip, o3tl::Length::mm100);
+ constexpr auto md = o3tl::getConversionMulDiv(o3tl::Length::twip, o3tl::Length::mm10);
+ const auto m = md.first * mnScale, d = md.second * 100;
+ nHeaderHeight = o3tl::convert(nHeaderHeight, m, d) + nTopMargin;
+ nFooterHeight = o3tl::convert(nFooterHeight, m, d) + nBottomMargin;
+ }
+
+ if (!bStateValid)
+ {
+ pPrintFunc->GetPrintState( aState );
+ aState.nDocPages = nTotalPages;
+ bStateValid = true;
+ }
+ }
+
+ if ( !bDoPrint )
+ return;
+
+ tools::Long nPageEndX = aLocalPageSize.Width() - aOffset.X();
+ tools::Long nPageEndY = aLocalPageSize.Height() - aOffset.Y();
+ if ( !bValidPage )
+ nPageEndX = nPageEndY = 0;
+
+ Size aWinSize = GetOutDev()->GetOutputSize();
+ Point aWinEnd( aWinSize.Width(), aWinSize.Height() );
+ bool bRight = nPageEndX <= aWinEnd.X();
+ bool bBottom = nPageEndY <= aWinEnd.Y();
+
+ if (!nTotalPages)
+ {
+ // There is no data to print. Print a friendly warning message and
+ // bail out.
+
+ SetMapMode(aMMMode);
+
+ // Draw background first.
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor(aBackColor);
+ GetOutDev()->DrawRect(tools::Rectangle(0, 0, aWinEnd.X(), aWinEnd.Y()));
+
+ const ScPatternAttr& rDefPattern =
+ rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
+
+ std::unique_ptr<ScEditEngineDefaulter> pEditEng(
+ new ScEditEngineDefaulter(EditEngine::CreatePool().get(), true));
+
+ pEditEng->SetRefMapMode(aMMMode);
+ auto pEditDefaults = std::make_unique<SfxItemSet>( pEditEng->GetEmptyItemSet() );
+ rDefPattern.FillEditItemSet(pEditDefaults.get());
+ pEditDefaults->Put(SvxColorItem(COL_LIGHTGRAY, EE_CHAR_COLOR));
+ pEditEng->SetDefaults(std::move(pEditDefaults));
+
+ OUString aEmptyMsg;
+ if (mbHasEmptyRangeTable)
+ aEmptyMsg = ScResId(STR_PRINT_PREVIEW_EMPTY_RANGE);
+ else
+ aEmptyMsg = ScResId(STR_PRINT_PREVIEW_NODATA);
+
+ tools::Long nHeight = 3000;
+ pEditEng->SetDefaultItem(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT));
+ pEditEng->SetDefaultItem(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CJK));
+ pEditEng->SetDefaultItem(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CTL));
+
+ pEditEng->SetTextCurrentDefaults(aEmptyMsg);
+
+ Point aCenter(
+ (aWinEnd.X() - pEditEng->CalcTextWidth())/2,
+ (aWinEnd.Y() - pEditEng->GetTextHeight())/2);
+
+ pEditEng->Draw(*GetOutDev(), aCenter);
+
+ return;
+ }
+
+ if( bPageMargin && bValidPage )
+ {
+ SetMapMode(aMMMode);
+ GetOutDev()->SetLineColor( COL_BLACK );
+ DrawInvert( static_cast<tools::Long>( nTopMargin - aOffset.Y() ), PointerStyle::VSizeBar );
+ DrawInvert( static_cast<tools::Long>(nPageEndY - nBottomMargin ), PointerStyle::VSizeBar );
+ DrawInvert( static_cast<tools::Long>( nLeftMargin - aOffset.X() ), PointerStyle::HSizeBar );
+ DrawInvert( static_cast<tools::Long>( nPageEndX - nRightMargin ) , PointerStyle::HSizeBar );
+ if( bHeaderOn )
+ {
+ DrawInvert( nHeaderHeight - aOffset.Y(), PointerStyle::VSizeBar );
+ }
+ if( bFooterOn )
+ {
+ DrawInvert( nPageEndY - nFooterHeight, PointerStyle::VSizeBar );
+ }
+
+ SetMapMode( MapMode( MapUnit::MapPixel ) );
+ for( int i= aPageArea.aStart.Col(); i<= aPageArea.aEnd.Col(); i++ )
+ {
+ Point aColumnTop = LogicToPixel( Point( 0, -aOffset.Y() ) ,aMMMode );
+ GetOutDev()->SetLineColor( COL_BLACK );
+ GetOutDev()->SetFillColor( COL_BLACK );
+ GetOutDev()->DrawRect( tools::Rectangle( Point( mvRight[i] - 2, aColumnTop.Y() ),Point( mvRight[i] + 2 , 4 + aColumnTop.Y()) ));
+ GetOutDev()->DrawLine( Point( mvRight[i], aColumnTop.Y() ), Point( mvRight[i], 10 + aColumnTop.Y()) );
+ }
+ SetMapMode( aMMMode );
+ }
+
+ if (bRight || bBottom)
+ {
+ SetMapMode(aMMMode);
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor(aBackColor);
+ if (bRight)
+ GetOutDev()->DrawRect(tools::Rectangle(nPageEndX,0, aWinEnd.X(),aWinEnd.Y()));
+ if (bBottom)
+ {
+ if (bRight)
+ GetOutDev()->DrawRect(tools::Rectangle(0,nPageEndY, nPageEndX,aWinEnd.Y())); // Corner not duplicated
+ else
+ GetOutDev()->DrawRect(tools::Rectangle(0,nPageEndY, aWinEnd.X(),aWinEnd.Y()));
+ }
+ }
+
+ if ( !bValidPage )
+ return;
+
+ Color aBorderColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+
+ // draw border
+
+ if ( aOffset.X() <= 0 || aOffset.Y() <= 0 || bRight || bBottom )
+ {
+ GetOutDev()->SetLineColor( aBorderColor );
+ GetOutDev()->SetFillColor();
+
+ tools::Rectangle aPixel( LogicToPixel( tools::Rectangle( -aOffset.X(), -aOffset.Y(), nPageEndX, nPageEndY ) ) );
+ aPixel.AdjustRight( -1 );
+ aPixel.AdjustBottom( -1 );
+ GetOutDev()->DrawRect( PixelToLogic( aPixel ) );
+ }
+
+ // draw shadow
+
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor( aBorderColor );
+
+ tools::Rectangle aPixel;
+
+ aPixel = LogicToPixel( tools::Rectangle( nPageEndX, -aOffset.Y(), nPageEndX, nPageEndY ) );
+ aPixel.AdjustTop(SC_PREVIEW_SHADOWSIZE );
+ aPixel.AdjustRight(SC_PREVIEW_SHADOWSIZE - 1 );
+ aPixel.AdjustBottom(SC_PREVIEW_SHADOWSIZE - 1 );
+ GetOutDev()->DrawRect( PixelToLogic( aPixel ) );
+
+ aPixel = LogicToPixel( tools::Rectangle( -aOffset.X(), nPageEndY, nPageEndX, nPageEndY ) );
+ aPixel.AdjustLeft(SC_PREVIEW_SHADOWSIZE );
+ aPixel.AdjustRight(SC_PREVIEW_SHADOWSIZE - 1 );
+ aPixel.AdjustBottom(SC_PREVIEW_SHADOWSIZE - 1 );
+ GetOutDev()->DrawRect( PixelToLogic( aPixel ) );
+}
+
+void ScPreview::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /* rRect */ )
+{
+ bool bWasInPaint = bInPaint; // nested calls shouldn't be necessary, but allow for now
+ bInPaint = true;
+
+ if (bPageMargin)
+ GetLocationData(); // fill location data for column positions
+ DoPrint( nullptr );
+ pViewShell->UpdateScrollBars();
+
+ bInPaint = bWasInPaint;
+}
+
+void ScPreview::Command( const CommandEvent& rCEvt )
+{
+ CommandEventId nCmd = rCEvt.GetCommand();
+ if ( nCmd == CommandEventId::Wheel || nCmd == CommandEventId::StartAutoScroll || nCmd == CommandEventId::AutoScroll )
+ {
+ bool bDone = pViewShell->ScrollCommand( rCEvt );
+ if (!bDone)
+ Window::Command(rCEvt);
+ }
+ else if ( nCmd == CommandEventId::ContextMenu )
+ SfxDispatcher::ExecutePopup();
+ else
+ Window::Command( rCEvt );
+}
+
+void ScPreview::KeyInput( const KeyEvent& rKEvt )
+{
+ // The + and - keys can't be configured as accelerator entries, so they must be handled directly
+ // (in ScPreview, not ScPreviewShell -> only if the preview window has the focus)
+
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKey = rKeyCode.GetCode();
+ bool bHandled = false;
+ if(!rKeyCode.GetModifier())
+ {
+ sal_uInt16 nSlot = 0;
+ switch(nKey)
+ {
+ case KEY_ADD: nSlot = SID_ZOOM_IN; break;
+ case KEY_ESCAPE: nSlot = ScViewUtil::IsFullScreen( *pViewShell ) ? SID_CANCEL : SID_PREVIEW_CLOSE; break;
+ case KEY_SUBTRACT: nSlot = SID_ZOOM_OUT; break;
+ }
+ if(nSlot)
+ {
+ bHandled = true;
+ pViewShell->GetViewFrame().GetDispatcher()->Execute( nSlot, SfxCallMode::ASYNCHRON );
+ }
+ }
+
+ if ( !bHandled && !pViewShell->KeyInput(rKEvt) )
+ Window::KeyInput(rKEvt);
+}
+
+const ScPreviewLocationData& ScPreview::GetLocationData()
+{
+ if ( !pLocationData )
+ {
+ pLocationData.reset( new ScPreviewLocationData( &pDocShell->GetDocument(), GetOutDev() ) );
+ bLocationValid = false;
+ }
+ if ( !bLocationValid )
+ {
+ pLocationData->Clear();
+ DoPrint( pLocationData.get() );
+ bLocationValid = true;
+ }
+ return *pLocationData;
+}
+
+void ScPreview::DataChanged(bool bNewTime)
+{
+ if (bNewTime)
+ aDateTime = DateTime( DateTime::SYSTEM );
+
+ bValid = false;
+ InvalidateLocationData( SfxHintId::ScDataChanged );
+ Invalidate();
+}
+
+OUString ScPreview::GetPosString()
+{
+ if (!bValid)
+ {
+ CalcPages();
+ UpdateDrawView(); // The table eventually changes
+ }
+
+ OUString aString = ScResId( STR_PAGE ) +
+ " " + OUString::number(nPageNo+1);
+
+ if (nTabsTested >= nTabCount)
+ aString += " / " + OUString::number(nTotalPages);
+
+ return aString;
+}
+
+void ScPreview::SetZoom(sal_uInt16 nNewZoom)
+{
+ if (nNewZoom < 20)
+ nNewZoom = 20;
+ if (nNewZoom > 400)
+ nNewZoom = 400;
+ if (nNewZoom == nZoom)
+ return;
+
+ nZoom = nNewZoom;
+
+ // apply new MapMode and call UpdateScrollBars to update aOffset
+
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+ SetMapMode( aMMMode );
+
+ bInSetZoom = true; // don't scroll during SetYOffset in UpdateScrollBars
+ pViewShell->UpdateNeededScrollBars(true);
+ bInSetZoom = false;
+
+ bStateValid = false;
+ InvalidateLocationData( SfxHintId::ScAccVisAreaChanged );
+ DoInvalidate();
+ Invalidate();
+}
+
+void ScPreview::SetPageNo( tools::Long nPage )
+{
+ nPageNo = nPage;
+ RecalcPages();
+ UpdateDrawView(); // The table eventually changes
+ InvalidateLocationData( SfxHintId::ScDataChanged );
+ Invalidate();
+}
+
+tools::Long ScPreview::GetFirstPage(SCTAB nTabP)
+{
+ SCTAB nDocTabCount = pDocShell->GetDocument().GetTableCount();
+ if (nTabP >= nDocTabCount)
+ nTabP = nDocTabCount-1;
+
+ tools::Long nPage = 0;
+ if (nTabP>0)
+ {
+ CalcPages();
+ if (nTabP >= static_cast<SCTAB>(nPages.size()) )
+ OSL_FAIL("nPages out of bounds, FIX IT");
+ UpdateDrawView(); // The table eventually changes
+
+ for (SCTAB i=0; i<nTabP; i++)
+ nPage += nPages[i];
+
+ // An empty Table on the previous Page
+
+ if ( nPages[nTabP]==0 && nPage > 0 )
+ --nPage;
+ }
+
+ return nPage;
+}
+
+static Size lcl_GetDocPageSize( const ScDocument* pDoc, SCTAB nTab )
+{
+ OUString aName = pDoc->GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = pDoc->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aName, SfxStyleFamily::Page );
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ return rStyleSet.Get(ATTR_PAGE_SIZE).GetSize();
+ }
+ else
+ {
+ OSL_FAIL( "PageStyle not found" );
+ return Size();
+ }
+}
+
+sal_uInt16 ScPreview::GetOptimalZoom(bool bWidthOnly)
+{
+ double nWinScaleX = ScGlobal::nScreenPPTX / pDocShell->GetOutputFactor();
+ double nWinScaleY = ScGlobal::nScreenPPTY;
+ Size aWinSize = GetOutputSizePixel();
+
+ // desired margin is 0.25cm in default MapMode (like Writer),
+ // but some additional margin is introduced by integer scale values
+ // -> add only 0.10cm, so there is some margin in all cases.
+ Size aMarginSize( LogicToPixel(Size(100, 100), MapMode(MapUnit::Map100thMM)) );
+ aWinSize.AdjustWidth( -(2 * aMarginSize.Width()) );
+ aWinSize.AdjustHeight( -(2 * aMarginSize.Height()) );
+
+ Size aLocalPageSize = lcl_GetDocPageSize( &pDocShell->GetDocument(), nTab );
+ if ( aLocalPageSize.Width() && aLocalPageSize.Height() )
+ {
+ tools::Long nZoomX = static_cast<tools::Long>( aWinSize.Width() * 100 / ( aLocalPageSize.Width() * nWinScaleX ));
+ tools::Long nZoomY = static_cast<tools::Long>( aWinSize.Height() * 100 / ( aLocalPageSize.Height() * nWinScaleY ));
+
+ tools::Long nOptimal = nZoomX;
+ if (!bWidthOnly && nZoomY<nOptimal)
+ nOptimal = nZoomY;
+
+ if (nOptimal<20)
+ nOptimal = 20;
+ if (nOptimal>400)
+ nOptimal = 400;
+
+ return static_cast<sal_uInt16>(nOptimal);
+ }
+ else
+ return nZoom;
+}
+
+void ScPreview::SetXOffset( tools::Long nX )
+{
+ if ( aOffset.X() == nX )
+ return;
+
+ if (bValid)
+ {
+ tools::Long nDif = LogicToPixel(aOffset).X() - LogicToPixel(Point(nX,0)).X();
+ aOffset.setX( nX );
+ if (nDif && !bInSetZoom)
+ {
+ MapMode aOldMode = GetMapMode();
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ Scroll( nDif, 0 );
+ SetMapMode(aOldMode);
+ }
+ }
+ else
+ {
+ aOffset.setX( nX );
+ if (!bInSetZoom)
+ Invalidate();
+ }
+ InvalidateLocationData( SfxHintId::ScAccVisAreaChanged );
+ Invalidate();
+}
+
+void ScPreview::SetYOffset( tools::Long nY )
+{
+ if ( aOffset.Y() == nY )
+ return;
+
+ if (bValid)
+ {
+ tools::Long nDif = LogicToPixel(aOffset).Y() - LogicToPixel(Point(0,nY)).Y();
+ aOffset.setY( nY );
+ if (nDif && !bInSetZoom)
+ {
+ MapMode aOldMode = GetMapMode();
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ Scroll( 0, nDif );
+ SetMapMode(aOldMode);
+ }
+ }
+ else
+ {
+ aOffset.setY( nY );
+ if (!bInSetZoom)
+ Invalidate();
+ }
+ InvalidateLocationData( SfxHintId::ScAccVisAreaChanged );
+ Invalidate();
+}
+
+void ScPreview::DoInvalidate()
+{
+ // If the whole GetState of the shell is called
+ // The Invalidate must come behind asynchronously
+
+ if (bInGetState)
+ Application::PostUserEvent( LINK( this, ScPreview, InvalidateHdl ), nullptr, true );
+ else
+ StaticInvalidate(); // Immediately
+}
+
+void ScPreview::StaticInvalidate()
+{
+ // static method, because it's called asynchronously
+ // -> must use current viewframe
+
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ if (!pViewFrm)
+ return;
+
+ SfxBindings& rBindings = pViewFrm->GetBindings();
+ rBindings.Invalidate(SID_STATUS_DOCPOS);
+ rBindings.Invalidate(SID_ROWCOL_SELCOUNT);
+ rBindings.Invalidate(SID_STATUS_PAGESTYLE);
+ rBindings.Invalidate(SID_PREVIEW_PREVIOUS);
+ rBindings.Invalidate(SID_PREVIEW_NEXT);
+ rBindings.Invalidate(SID_PREVIEW_FIRST);
+ rBindings.Invalidate(SID_PREVIEW_LAST);
+ rBindings.Invalidate(SID_ATTR_ZOOM);
+ rBindings.Invalidate(SID_ZOOM_IN);
+ rBindings.Invalidate(SID_ZOOM_OUT);
+ rBindings.Invalidate(SID_PREVIEW_SCALINGFACTOR);
+ rBindings.Invalidate(SID_ATTR_ZOOMSLIDER);
+}
+
+IMPL_STATIC_LINK_NOARG( ScPreview, InvalidateHdl, void*, void )
+{
+ StaticInvalidate();
+}
+
+void ScPreview::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged(rDCEvt);
+
+ if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) ||
+ (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
+ return;
+
+ if ( rDCEvt.GetType() == DataChangedEventType::FONTS )
+ pDocShell->UpdateFontList();
+
+ // #i114518# Paint of form controls may modify the window's settings.
+ // Ignore the event if it is called from within Paint.
+ if ( !bInPaint )
+ {
+ if ( rDCEvt.GetType() == DataChangedEventType::SETTINGS &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ // scroll bar size may have changed
+ pViewShell->InvalidateBorder(); // calls OuterResizePixel
+ }
+ Invalidate();
+ InvalidateLocationData( SfxHintId::ScDataChanged );
+ }
+}
+
+void ScPreview::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+
+ aButtonDownChangePoint = PixelToLogic( rMEvt.GetPosPixel(),aMMMode );
+ aButtonDownPt = PixelToLogic( rMEvt.GetPosPixel(),aMMMode );
+
+ CaptureMouse();
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::HSizeBar )
+ {
+ SetMapMode( aMMMode );
+ if( bLeftRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.X(), PointerStyle::HSizeBar );
+ bLeftRulerMove = true;
+ bRightRulerMove = false;
+ }
+ else if( bRightRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.X(), PointerStyle::HSizeBar );
+ bLeftRulerMove = false;
+ bRightRulerMove = true;
+ }
+ }
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::VSizeBar )
+ {
+ SetMapMode( aMMMode );
+ if( bTopRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bTopRulerMove = true;
+ bBottomRulerMove = false;
+ }
+ else if( bBottomRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bTopRulerMove = false;
+ bBottomRulerMove = true;
+ }
+ else if( bHeaderRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bHeaderRulerMove = true;
+ bFooterRulerMove = false;
+ }
+ else if( bFooterRulerChange )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), PointerStyle::VSizeBar );
+ bHeaderRulerMove = false;
+ bFooterRulerMove = true;
+ }
+ }
+
+ if( !(rMEvt.IsLeft() && GetPointer() == PointerStyle::HSplit) )
+ return;
+
+ Point aNowPt = rMEvt.GetPosPixel();
+ SCCOL i = 0;
+ for( i = aPageArea.aStart.Col(); i<= aPageArea.aEnd.Col(); i++ )
+ {
+ if( aNowPt.X() < mvRight[i] + 2 && aNowPt.X() > mvRight[i] - 2 )
+ {
+ nColNumberButtonDown = i;
+ break;
+ }
+ }
+ if( i == aPageArea.aEnd.Col()+1 )
+ return;
+
+ SetMapMode( aMMMode );
+ if( nColNumberButtonDown == aPageArea.aStart.Col() )
+ DrawInvert( PixelToLogic( Point( nLeftPosition, 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+ else
+ DrawInvert( PixelToLogic( Point( mvRight[ nColNumberButtonDown-1 ], 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+
+ DrawInvert( aButtonDownChangePoint.X(), PointerStyle::HSplit );
+ bColRulerMove = true;
+}
+
+void ScPreview::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+
+ aButtonUpPt = PixelToLogic( rMEvt.GetPosPixel(),aMMMode );
+
+ tools::Long nWidth = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Width();
+ tools::Long nHeight = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Height();
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::HSizeBar )
+ {
+ SetPointer( PointerStyle::Arrow );
+
+ ScDocument& rDoc = pDocShell->GetDocument();
+ OUString aOldName = rDoc.GetPageStyle( nTab );
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+
+ if ( pStyleSheet )
+ {
+ bool bMoveRulerAction= true;
+ ScStyleSaveData aOldData;
+ if( bUndo )
+ aOldData.InitFromStyle( pStyleSheet );
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+
+ SvxLRSpaceItem aLRItem = rStyleSet.Get( ATTR_LRSPACE );
+
+ if(( bLeftRulerChange || bRightRulerChange ) && ( aButtonUpPt.X() <= ( 0 - aOffset.X() ) || aButtonUpPt.X() > o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( bLeftRulerChange && ( o3tl::convert(aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) > nWidth - aLRItem.GetRight() - o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip) ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( bRightRulerChange && ( o3tl::convert(aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) < aLRItem.GetLeft() - o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip) ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( aButtonDownPt.X() == aButtonUpPt.X() )
+ {
+ bMoveRulerAction = false;
+ DrawInvert( aButtonUpPt.X(), PointerStyle::HSizeBar );
+ }
+ if( bMoveRulerAction )
+ {
+ ScDocShellModificator aModificator( *pDocShell );
+ if( bLeftRulerChange && bLeftRulerMove )
+ {
+ aLRItem.SetLeft(o3tl::convert( aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) + o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aLRItem );
+ pDocShell->SetModified();
+ }
+ else if( bRightRulerChange && bRightRulerMove )
+ {
+ aLRItem.SetRight(nWidth - o3tl::convert(aButtonUpPt.X(), o3tl::Length::mm100, o3tl::Length::twip) - o3tl::convert(aOffset.X(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aLRItem );
+ pDocShell->SetModified();
+ }
+
+ ScStyleSaveData aNewData;
+ aNewData.InitFromStyle( pStyleSheet );
+ if( bUndo )
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( pDocShell, SfxStyleFamily::Page,
+ aOldData, aNewData ) );
+ }
+
+ if ( ValidTab( nTab ) )
+ {
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, nTab );
+ aPrintFunc.UpdatePages();
+ }
+
+ tools::Rectangle aRect(0,0,10000,10000);
+ Invalidate(aRect);
+ aModificator.SetDocumentModified();
+ bLeftRulerChange = false;
+ bRightRulerChange = false;
+ }
+ }
+ bLeftRulerMove = false;
+ bRightRulerMove = false;
+ }
+
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::VSizeBar )
+ {
+ SetPointer( PointerStyle::Arrow );
+
+ bool bMoveRulerAction = true;
+ if( ( bTopRulerChange || bBottomRulerChange || bHeaderRulerChange || bFooterRulerChange ) && ( aButtonUpPt.Y() <= ( 0 - aOffset.Y() ) || aButtonUpPt.Y() > o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) -aOffset.Y() ) )
+ {
+ bMoveRulerAction = false;
+ Invalidate(tools::Rectangle(0, 0, 10000, 10000));
+ }
+ else if( aButtonDownPt.Y() == aButtonUpPt.Y() )
+ {
+ bMoveRulerAction = false;
+ DrawInvert( aButtonUpPt.Y(), PointerStyle::VSizeBar );
+ }
+ if( bMoveRulerAction )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bUndo = rDoc.IsUndoEnabled();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( pStyleSheet )
+ {
+ ScDocShellModificator aModificator( *pDocShell );
+ ScStyleSaveData aOldData;
+ if( bUndo )
+ aOldData.InitFromStyle( pStyleSheet );
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+
+ SvxULSpaceItem aULItem = rStyleSet.Get( ATTR_ULSPACE );
+
+ if( bTopRulerMove && bTopRulerChange )
+ {
+ aULItem.SetUpperValue(o3tl::convert(aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) + o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aULItem );
+ pDocShell->SetModified();
+ }
+ else if( bBottomRulerMove && bBottomRulerChange )
+ {
+ aULItem.SetLowerValue(nHeight - o3tl::convert(aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) - o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip));
+ rStyleSet.Put( aULItem );
+ pDocShell->SetModified();
+ }
+ else if( bHeaderRulerMove && bHeaderRulerChange )
+ {
+ if ( const SvxSetItem* pSetItem = rStyleSet.GetItemIfSet( ATTR_PAGE_HEADERSET, false ) )
+ {
+ const SfxItemSet& rHeaderSet = pSetItem->GetItemSet();
+ Size aHeaderSize = rHeaderSet.Get(ATTR_PAGE_SIZE).GetSize();
+ aHeaderSize.setHeight(o3tl::convert( aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) + o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip) - aULItem.GetUpper());
+ aHeaderSize.setHeight( aHeaderSize.Height() * 100 / mnScale );
+ SvxSetItem aNewHeader( rStyleSet.Get(ATTR_PAGE_HEADERSET) );
+ aNewHeader.GetItemSet().Put( SvxSizeItem( ATTR_PAGE_SIZE, aHeaderSize ) );
+ rStyleSet.Put( aNewHeader );
+ pDocShell->SetModified();
+ }
+ }
+ else if( bFooterRulerMove && bFooterRulerChange )
+ {
+ if( const SvxSetItem* pSetItem = rStyleSet.GetItemIfSet( ATTR_PAGE_FOOTERSET, false ) )
+ {
+ const SfxItemSet& rFooterSet = pSetItem->GetItemSet();
+ Size aFooterSize = rFooterSet.Get(ATTR_PAGE_SIZE).GetSize();
+ aFooterSize.setHeight(nHeight - o3tl::convert(aButtonUpPt.Y(), o3tl::Length::mm100, o3tl::Length::twip) - o3tl::convert(aOffset.Y(), o3tl::Length::mm100, o3tl::Length::twip) - aULItem.GetLower());
+ aFooterSize.setHeight( aFooterSize.Height() * 100 / mnScale );
+ SvxSetItem aNewFooter( rStyleSet.Get(ATTR_PAGE_FOOTERSET) );
+ aNewFooter.GetItemSet().Put( SvxSizeItem( ATTR_PAGE_SIZE, aFooterSize ) );
+ rStyleSet.Put( aNewFooter );
+ pDocShell->SetModified();
+ }
+ }
+
+ ScStyleSaveData aNewData;
+ aNewData.InitFromStyle( pStyleSheet );
+ if( bUndo )
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( pDocShell, SfxStyleFamily::Page,
+ aOldData, aNewData ) );
+ }
+
+ if ( ValidTab( nTab ) )
+ {
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, nTab );
+ aPrintFunc.UpdatePages();
+ }
+
+ tools::Rectangle aRect(0, 0, 10000, 10000);
+ Invalidate(aRect);
+ aModificator.SetDocumentModified();
+ bTopRulerChange = false;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = false;
+ }
+ }
+ bTopRulerMove = false;
+ bBottomRulerMove = false;
+ bHeaderRulerMove = false;
+ bFooterRulerMove = false;
+ }
+ if( rMEvt.IsLeft() && GetPointer() == PointerStyle::HSplit )
+ {
+ SetPointer(PointerStyle::Arrow);
+ ScDocument& rDoc = pDocShell->GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ bool bMoveRulerAction = true;
+ if( aButtonDownPt.X() == aButtonUpPt.X() )
+ {
+ bMoveRulerAction = false;
+ if( nColNumberButtonDown == aPageArea.aStart.Col() )
+ DrawInvert( PixelToLogic( Point( nLeftPosition, 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+ else
+ DrawInvert( PixelToLogic( Point( mvRight[ nColNumberButtonDown-1 ], 0 ),aMMMode ).X() ,PointerStyle::HSplit );
+ DrawInvert( aButtonUpPt.X(), PointerStyle::HSplit );
+ }
+ if( bMoveRulerAction )
+ {
+ tools::Long nNewColWidth = 0;
+ std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nColNumberButtonDown,nColNumberButtonDown));
+
+ constexpr auto md = o3tl::getConversionMulDiv(o3tl::Length::mm100, o3tl::Length::twip);
+ const auto m = md.first * 100, d = md.second * mnScale;
+ if( !bLayoutRTL )
+ {
+ nNewColWidth = o3tl::convert(PixelToLogic( Point( rMEvt.GetPosPixel().X() - mvRight[ nColNumberButtonDown ], 0), aMMMode ).X(), m, d);
+ nNewColWidth += pDocShell->GetDocument().GetColWidth( nColNumberButtonDown, nTab );
+ }
+ else
+ {
+
+ nNewColWidth = o3tl::convert(PixelToLogic( Point( mvRight[ nColNumberButtonDown ] - rMEvt.GetPosPixel().X(), 0), aMMMode ).X(), m, d);
+ nNewColWidth += pDocShell->GetDocument().GetColWidth( nColNumberButtonDown, nTab );
+ }
+
+ if( nNewColWidth >= 0 )
+ {
+ pDocShell->GetDocFunc().SetWidthOrHeight(
+ true, aCols, nTab, SC_SIZE_DIRECT, static_cast<sal_uInt16>(nNewColWidth), true, true);
+ pDocShell->SetModified();
+ }
+ if ( ValidTab( nTab ) )
+ {
+ ScPrintFunc aPrintFunc( GetOutDev(), pDocShell, nTab );
+ aPrintFunc.UpdatePages();
+ }
+ tools::Rectangle aRect(0, 0, 10000, 10000);
+ Invalidate(aRect);
+ }
+ bColRulerMove = false;
+ }
+ ReleaseMouse();
+}
+
+void ScPreview::MouseMove( const MouseEvent& rMEvt )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+ Point aMouseMovePoint = PixelToLogic( rMEvt.GetPosPixel(), aMMMode );
+
+ tools::Long nLeftMargin = 0;
+ tools::Long nRightMargin = 0;
+ tools::Long nTopMargin = 0;
+ tools::Long nBottomMargin = 0;
+
+ tools::Long nWidth = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Width();
+ tools::Long nHeight = lcl_GetDocPageSize(&pDocShell->GetDocument(), nTab).Height();
+
+ if ( nPageNo < nTotalPages )
+ {
+ ScPrintOptions aOptions = SC_MOD()->GetPrintOptions();
+
+ std::unique_ptr<ScPrintFunc, o3tl::default_delete<ScPrintFunc>> pPrintFunc;
+ if (bStateValid)
+ pPrintFunc.reset(new ScPrintFunc( GetOutDev(), pDocShell, aState, &aOptions ));
+ else
+ pPrintFunc.reset(new ScPrintFunc( GetOutDev(), pDocShell, nTab, nFirstAttr[nTab], nTotalPages, nullptr, &aOptions ));
+
+ nLeftMargin = o3tl::convert(pPrintFunc->GetLeftMargin(), o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X();
+ nRightMargin = o3tl::convert(pPrintFunc->GetRightMargin(), o3tl::Length::twip, o3tl::Length::mm100);
+ nRightMargin = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - nRightMargin - aOffset.X();
+ nTopMargin = o3tl::convert(pPrintFunc->GetTopMargin(), o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y();
+ nBottomMargin = o3tl::convert(pPrintFunc->GetBottomMargin(), o3tl::Length::twip, o3tl::Length::mm100);
+ nBottomMargin = o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - nBottomMargin - aOffset.Y();
+ if( mnScale > 0 )
+ {
+ constexpr auto md = o3tl::getConversionMulDiv(o3tl::Length::twip, o3tl::Length::mm100);
+ const auto m = md.first * mnScale, d = md.second * 100;
+ nHeaderHeight = nTopMargin + o3tl::convert(pPrintFunc->GetHeader().nHeight, m, d);
+ nFooterHeight = nBottomMargin - o3tl::convert(pPrintFunc->GetFooter().nHeight, m, d);
+ }
+ else
+ {
+ nHeaderHeight = nTopMargin + o3tl::convert(pPrintFunc->GetHeader().nHeight, o3tl::Length::twip, o3tl::Length::mm100);
+ nFooterHeight = nBottomMargin - o3tl::convert(pPrintFunc->GetFooter().nHeight, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ }
+
+ Point aPixPt( rMEvt.GetPosPixel() );
+ Point aLeftTop = LogicToPixel( Point( nLeftMargin, -aOffset.Y() ) , aMMMode );
+ Point aLeftBottom = LogicToPixel( Point( nLeftMargin, o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y()), aMMMode );
+ Point aRightTop = LogicToPixel( Point( nRightMargin, -aOffset.Y() ), aMMMode );
+ Point aTopLeft = LogicToPixel( Point( -aOffset.X(), nTopMargin ), aMMMode );
+ Point aTopRight = LogicToPixel( Point( o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X(), nTopMargin ), aMMMode );
+ Point aBottomLeft = LogicToPixel( Point( -aOffset.X(), nBottomMargin ), aMMMode );
+ Point aHeaderLeft = LogicToPixel( Point( -aOffset.X(), nHeaderHeight ), aMMMode );
+ Point aFooderLeft = LogicToPixel( Point( -aOffset.X(), nFooterHeight ), aMMMode );
+
+ bool bOnColRulerChange = false;
+
+ for( SCCOL i=aPageArea.aStart.Col(); i<= aPageArea.aEnd.Col(); i++ )
+ {
+ Point aColumnTop = LogicToPixel( Point( 0, -aOffset.Y() ) ,aMMMode );
+ Point aColumnBottom = LogicToPixel( Point( 0, o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y()), aMMMode );
+ tools::Long nRight = i < static_cast<SCCOL>(mvRight.size()) ? mvRight[i] : 0;
+ if( aPixPt.X() < ( nRight + 2 ) && ( aPixPt.X() > ( nRight - 2 ) ) && ( aPixPt.X() < aRightTop.X() ) && ( aPixPt.X() > aLeftTop.X() )
+ && ( aPixPt.Y() > aColumnTop.Y() ) && ( aPixPt.Y() < aColumnBottom.Y() ) && !bLeftRulerMove && !bRightRulerMove
+ && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ bOnColRulerChange = true;
+ if( !rMEvt.GetButtons() && GetPointer() == PointerStyle::HSplit )
+ nColNumberButtonDown = i;
+ break;
+ }
+ }
+
+ if( aPixPt.X() < ( aLeftTop.X() + 2 ) && aPixPt.X() > ( aLeftTop.X() - 2 ) && !bRightRulerMove )
+ {
+ bLeftRulerChange = true;
+ bRightRulerChange = false;
+ }
+ else if( aPixPt.X() < ( aRightTop.X() + 2 ) && aPixPt.X() > ( aRightTop.X() - 2 ) && !bLeftRulerMove )
+ {
+ bLeftRulerChange = false;
+ bRightRulerChange = true;
+ }
+ else if( aPixPt.Y() < ( aTopLeft.Y() + 2 ) && aPixPt.Y() > ( aTopLeft.Y() - 2 ) && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ bTopRulerChange = true;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = false;
+ }
+ else if( aPixPt.Y() < ( aBottomLeft.Y() + 2 ) && aPixPt.Y() > ( aBottomLeft.Y() - 2 ) && !bTopRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ bTopRulerChange = false;
+ bBottomRulerChange = true;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = false;
+ }
+ else if( aPixPt.Y() < ( aHeaderLeft.Y() + 2 ) && aPixPt.Y() > ( aHeaderLeft.Y() - 2 ) && !bTopRulerMove && !bBottomRulerMove && !bFooterRulerMove )
+ {
+ bTopRulerChange = false;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = true;
+ bFooterRulerChange = false;
+ }
+ else if( aPixPt.Y() < ( aFooderLeft.Y() + 2 ) && aPixPt.Y() > ( aFooderLeft.Y() - 2 ) && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove )
+ {
+ bTopRulerChange = false;
+ bBottomRulerChange = false;
+ bHeaderRulerChange = false;
+ bFooterRulerChange = true;
+ }
+
+ if( !bPageMargin )
+ return;
+
+ if(( (aPixPt.X() < ( aLeftTop.X() + 2 ) && aPixPt.X() > ( aLeftTop.X() - 2 )) || bLeftRulerMove ||
+ ( aPixPt.X() < ( aRightTop.X() + 2 ) && aPixPt.X() > ( aRightTop.X() - 2 ) ) || bRightRulerMove || bOnColRulerChange || bColRulerMove )
+ && aPixPt.Y() > aLeftTop.Y() && aPixPt.Y() < aLeftBottom.Y() )
+ {
+ if( bOnColRulerChange || bColRulerMove )
+ {
+ SetPointer( PointerStyle::HSplit );
+ if( bColRulerMove )
+ {
+ if( aMouseMovePoint.X() > -aOffset.X() && aMouseMovePoint.X() < o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() )
+ DragMove( aMouseMovePoint.X(), PointerStyle::HSplit );
+ }
+ }
+ else
+ {
+ if( bLeftRulerChange && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ SetPointer( PointerStyle::HSizeBar );
+ if( bLeftRulerMove )
+ {
+ if( aMouseMovePoint.X() > -aOffset.X() && aMouseMovePoint.X() < o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() )
+ DragMove( aMouseMovePoint.X(), PointerStyle::HSizeBar );
+ }
+ }
+ else if( bRightRulerChange && !bTopRulerMove && !bBottomRulerMove && !bHeaderRulerMove && !bFooterRulerMove )
+ {
+ SetPointer( PointerStyle::HSizeBar );
+ if( bRightRulerMove )
+ {
+ if( aMouseMovePoint.X() > -aOffset.X() && aMouseMovePoint.X() < o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X() )
+ DragMove( aMouseMovePoint.X(), PointerStyle::HSizeBar );
+ }
+ }
+ }
+ }
+ else
+ {
+ if( ( ( aPixPt.Y() < ( aTopLeft.Y() + 2 ) && aPixPt.Y() > ( aTopLeft.Y() - 2 ) ) || bTopRulerMove ||
+ ( aPixPt.Y() < ( aBottomLeft.Y() + 2 ) && aPixPt.Y() > ( aBottomLeft.Y() - 2 ) ) || bBottomRulerMove ||
+ ( aPixPt.Y() < ( aHeaderLeft.Y() + 2 ) && aPixPt.Y() > ( aHeaderLeft.Y() - 2 ) ) || bHeaderRulerMove ||
+ ( aPixPt.Y() < ( aFooderLeft.Y() + 2 ) && aPixPt.Y() > ( aFooderLeft.Y() - 2 ) ) || bFooterRulerMove )
+ && aPixPt.X() > aTopLeft.X() && aPixPt.X() < aTopRight.X() )
+ {
+ if( bTopRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bTopRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ else if( bBottomRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bBottomRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ else if( bHeaderRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bHeaderRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ else if( bFooterRulerChange )
+ {
+ SetPointer( PointerStyle::VSizeBar );
+ if( bFooterRulerMove )
+ {
+ if( aMouseMovePoint.Y() > -aOffset.Y() && aMouseMovePoint.Y() < o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y() )
+ DragMove( aMouseMovePoint.Y(), PointerStyle::VSizeBar );
+ }
+ }
+ }
+ else
+ SetPointer( PointerStyle::Arrow );
+ }
+}
+
+void ScPreview::InvalidateLocationData(SfxHintId nId)
+{
+ bLocationValid = false;
+ if (pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility( SfxHint( nId ) );
+}
+
+void ScPreview::GetFocus()
+{
+ Window::GetFocus();
+ if (pViewShell && pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility( ScAccWinFocusGotHint() );
+}
+
+void ScPreview::LoseFocus()
+{
+ if (pViewShell && pViewShell->HasAccessibilityObjects())
+ pViewShell->BroadcastAccessibility( ScAccWinFocusLostHint() );
+ Window::LoseFocus();
+}
+
+css::uno::Reference<css::accessibility::XAccessible> ScPreview::CreateAccessible()
+{
+ css::uno::Reference<css::accessibility::XAccessible> xAcc= GetAccessible(false);
+ if (xAcc.is())
+ {
+ return xAcc;
+ }
+
+ rtl::Reference<ScAccessibleDocumentPagePreview> pAccessible =
+ new ScAccessibleDocumentPagePreview( GetAccessibleParentWindow()->GetAccessible(), pViewShell );
+
+ xAcc = pAccessible;
+ SetAccessible(xAcc);
+ pAccessible->Init();
+ return xAcc;
+}
+
+void ScPreview::DragMove( tools::Long nDragMovePos, PointerStyle nFlags )
+{
+ Fraction aPreviewZoom( nZoom, 100 );
+ Fraction aHorPrevZoom( static_cast<tools::Long>( 100 * nZoom / pDocShell->GetOutputFactor() ), 10000 );
+ MapMode aMMMode( MapUnit::Map100thMM, Point(), aHorPrevZoom, aPreviewZoom );
+ SetMapMode( aMMMode );
+ tools::Long nPos = nDragMovePos;
+ if( nFlags == PointerStyle::HSizeBar || nFlags == PointerStyle::HSplit )
+ {
+ if( nDragMovePos != aButtonDownChangePoint.X() )
+ {
+ DrawInvert( aButtonDownChangePoint.X(), nFlags );
+ aButtonDownChangePoint.setX( nPos );
+ DrawInvert( aButtonDownChangePoint.X(), nFlags );
+ }
+ }
+ else if( nFlags == PointerStyle::VSizeBar )
+ {
+ if( nDragMovePos != aButtonDownChangePoint.Y() )
+ {
+ DrawInvert( aButtonDownChangePoint.Y(), nFlags );
+ aButtonDownChangePoint.setY( nPos );
+ DrawInvert( aButtonDownChangePoint.Y(), nFlags );
+ }
+ }
+}
+
+void ScPreview::DrawInvert( tools::Long nDragPos, PointerStyle nFlags )
+{
+ tools::Long nHeight = lcl_GetDocPageSize( &pDocShell->GetDocument(), nTab ).Height();
+ tools::Long nWidth = lcl_GetDocPageSize( &pDocShell->GetDocument(), nTab ).Width();
+ if( nFlags == PointerStyle::HSizeBar || nFlags == PointerStyle::HSplit )
+ {
+ tools::Rectangle aRect( nDragPos, -aOffset.Y(), nDragPos + 1, o3tl::convert(nHeight, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.Y());
+ GetOutDev()->Invert( aRect, InvertFlags::N50 );
+ }
+ else if( nFlags == PointerStyle::VSizeBar )
+ {
+ tools::Rectangle aRect( -aOffset.X(), nDragPos, o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100) - aOffset.X(), nDragPos + 1 );
+ GetOutDev()->Invert( aRect, InvertFlags::N50 );
+ }
+}
+
+void ScPreview::SetSelectedTabs(const ScMarkData& rMark)
+{
+ maSelectedTabs = rMark.GetSelectedTabs();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/prevloc.cxx b/sc/source/ui/view/prevloc.cxx
new file mode 100644
index 0000000000..1e2375ab5c
--- /dev/null
+++ b/sc/source/ui/view/prevloc.cxx
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <prevloc.hxx>
+#include <document.hxx>
+
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <vcl/outdev.hxx>
+
+namespace {
+
+enum ScPreviewLocationType : sal_uInt8
+{
+ SC_PLOC_CELLRANGE,
+ SC_PLOC_COLHEADER,
+ SC_PLOC_ROWHEADER,
+ SC_PLOC_LEFTHEADER,
+ SC_PLOC_RIGHTHEADER,
+ SC_PLOC_LEFTFOOTER,
+ SC_PLOC_RIGHTFOOTER,
+ SC_PLOC_NOTEMARK,
+ SC_PLOC_NOTETEXT
+};
+
+}
+
+struct ScPreviewLocationEntry
+{
+ tools::Rectangle aPixelRect;
+ ScRange aCellRange;
+ ScPreviewLocationType eType;
+ bool bRepeatCol;
+ bool bRepeatRow;
+
+ ScPreviewLocationEntry( ScPreviewLocationType eNewType, const tools::Rectangle& rPixel, const ScRange& rRange,
+ bool bRepCol, bool bRepRow ) :
+ aPixelRect( rPixel ),
+ aCellRange( rRange ),
+ eType( eNewType ),
+ bRepeatCol( bRepCol ),
+ bRepeatRow( bRepRow )
+ {
+ }
+};
+
+ScPreviewTableInfo::ScPreviewTableInfo() :
+ nTab(0),
+ nCols(0),
+ nRows(0)
+{
+}
+
+ScPreviewTableInfo::~ScPreviewTableInfo()
+{
+}
+
+void ScPreviewTableInfo::SetTab( SCTAB nNewTab )
+{
+ nTab = nNewTab;
+}
+
+void ScPreviewTableInfo::SetColInfo( SCCOL nCount, ScPreviewColRowInfo* pNewInfo )
+{
+ pColInfo.reset(pNewInfo);
+ nCols = nCount;
+}
+
+void ScPreviewTableInfo::SetRowInfo( SCROW nCount, ScPreviewColRowInfo* pNewInfo )
+{
+ pRowInfo.reset(pNewInfo);
+ nRows = nCount;
+}
+
+void ScPreviewTableInfo::LimitToArea( const tools::Rectangle& rPixelArea )
+{
+ if ( pColInfo )
+ {
+ // cells completely left of the visible area
+ SCCOL nStart = 0;
+ while ( nStart < nCols && pColInfo[nStart].nPixelEnd < rPixelArea.Left() )
+ ++nStart;
+
+ // cells completely right of the visible area
+ SCCOL nEnd = nCols;
+ while ( nEnd > 0 && pColInfo[nEnd-1].nPixelStart > rPixelArea.Right() )
+ --nEnd;
+
+ if ( nStart > 0 || nEnd < nCols )
+ {
+ if ( nEnd > nStart )
+ {
+ SCCOL nNewCount = nEnd - nStart;
+ ScPreviewColRowInfo* pNewInfo = new ScPreviewColRowInfo[nNewCount];
+ for (SCCOL i=0; i<nNewCount; i++)
+ pNewInfo[i] = pColInfo[nStart + i];
+ SetColInfo( nNewCount, pNewInfo );
+ }
+ else
+ SetColInfo( 0, nullptr ); // all invisible
+ }
+ }
+
+ if ( !pRowInfo )
+ return;
+
+ // cells completely above the visible area
+ SCROW nStart = 0;
+ while ( nStart < nRows && pRowInfo[nStart].nPixelEnd < rPixelArea.Top() )
+ ++nStart;
+
+ // cells completely below the visible area
+ SCROW nEnd = nRows;
+ while ( nEnd > 0 && pRowInfo[nEnd-1].nPixelStart > rPixelArea.Bottom() )
+ --nEnd;
+
+ if ( nStart <= 0 && nEnd >= nRows )
+ return;
+
+ if ( nEnd > nStart )
+ {
+ SCROW nNewCount = nEnd - nStart;
+ ScPreviewColRowInfo* pNewInfo = new ScPreviewColRowInfo[nNewCount];
+ for (SCROW i=0; i<nNewCount; i++)
+ pNewInfo[i] = pRowInfo[nStart + i];
+ SetRowInfo( nNewCount, pNewInfo );
+ }
+ else
+ SetRowInfo( 0, nullptr ); // all invisible
+}
+
+ScPreviewLocationData::ScPreviewLocationData( ScDocument* pDocument, OutputDevice* pWin ) :
+ pWindow( pWin ),
+ pDoc( pDocument ),
+ nDrawRanges( 0 ),
+ nPrintTab( 0 )
+{
+}
+
+ScPreviewLocationData::~ScPreviewLocationData()
+{
+ Clear();
+}
+
+void ScPreviewLocationData::SetCellMapMode( const MapMode& rMapMode )
+{
+ aCellMapMode = rMapMode;
+}
+
+void ScPreviewLocationData::SetPrintTab( SCTAB nNew )
+{
+ nPrintTab = nNew;
+}
+
+void ScPreviewLocationData::Clear()
+{
+ m_Entries.clear();
+
+ nDrawRanges = 0;
+}
+
+void ScPreviewLocationData::AddCellRange( const tools::Rectangle& rRect, const ScRange& rRange, bool bRepCol, bool bRepRow,
+ const MapMode& rDrawMap )
+{
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_CELLRANGE, aPixelRect, rRange, bRepCol, bRepRow) );
+
+ OSL_ENSURE( nDrawRanges < SC_PREVIEW_MAXRANGES, "too many ranges" );
+
+ if ( nDrawRanges >= SC_PREVIEW_MAXRANGES )
+ return;
+
+ aDrawRectangle[nDrawRanges] = aPixelRect;
+ aDrawMapMode[nDrawRanges] = rDrawMap;
+
+ if (bRepCol)
+ {
+ if (bRepRow)
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_EDGE;
+ else
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_REPCOL;
+ }
+ else
+ {
+ if (bRepRow)
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_REPROW;
+ else
+ aDrawRangeId[nDrawRanges] = SC_PREVIEW_RANGE_TAB;
+ }
+
+ ++nDrawRanges;
+}
+
+void ScPreviewLocationData::AddColHeaders( const tools::Rectangle& rRect, SCCOL nStartCol, SCCOL nEndCol, bool bRepCol )
+{
+ SCTAB nTab = 0; //! ?
+ ScRange aRange( nStartCol, 0, nTab, nEndCol, 0, nTab );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_COLHEADER, aPixelRect, aRange, bRepCol, false) );
+}
+
+void ScPreviewLocationData::AddRowHeaders( const tools::Rectangle& rRect, SCROW nStartRow, SCROW nEndRow, bool bRepRow )
+{
+ SCTAB nTab = 0; //! ?
+ ScRange aRange( 0, nStartRow, nTab, 0, nEndRow, nTab );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_ROWHEADER, aPixelRect, aRange, false, bRepRow) );
+}
+
+void ScPreviewLocationData::AddHeaderFooter( const tools::Rectangle& rRect, bool bHeader, bool bLeft )
+{
+ ScRange aRange; //! ?
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ ScPreviewLocationType eType = bHeader ?
+ ( bLeft ? SC_PLOC_LEFTHEADER : SC_PLOC_RIGHTHEADER ) :
+ ( bLeft ? SC_PLOC_LEFTFOOTER : SC_PLOC_RIGHTFOOTER );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(eType, aPixelRect, aRange, false, false) );
+}
+
+void ScPreviewLocationData::AddNoteMark( const tools::Rectangle& rRect, const ScAddress& rPos )
+{
+ ScRange aRange( rPos );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_NOTEMARK, aPixelRect, aRange, false, false) );
+}
+
+void ScPreviewLocationData::AddNoteText( const tools::Rectangle& rRect, const ScAddress& rPos )
+{
+ ScRange aRange( rPos );
+ tools::Rectangle aPixelRect( pWindow->LogicToPixel( rRect ) );
+
+ m_Entries.push_front( std::make_unique<ScPreviewLocationEntry>(SC_PLOC_NOTETEXT, aPixelRect, aRange, false, false) );
+}
+
+void ScPreviewLocationData::GetDrawRange( sal_uInt16 nPos, tools::Rectangle& rPixelRect, MapMode& rMapMode, sal_uInt8& rRangeId ) const
+{
+ OSL_ENSURE( nPos < nDrawRanges, "wrong position" );
+ if ( nPos < nDrawRanges )
+ {
+ rPixelRect = aDrawRectangle[nPos];
+ rMapMode = aDrawMapMode[nPos];
+ rRangeId = aDrawRangeId[nPos];
+ }
+}
+
+static ScPreviewLocationEntry* lcl_GetEntryByAddress(
+ ScPreviewLocationData::Entries_t const& rEntries,
+ const ScAddress& rPos, ScPreviewLocationType const eType)
+{
+ for (auto const& it : rEntries)
+ {
+ if ( it->eType == eType && it->aCellRange.Contains( rPos ) )
+ return it.get();
+ }
+
+ return nullptr;
+}
+
+tools::Rectangle ScPreviewLocationData::GetOffsetPixel( const ScAddress& rCellPos, const ScRange& rRange ) const
+{
+ SCTAB nTab = rRange.aStart.Tab();
+
+ tools::Long nPosX = 0;
+ SCCOL nEndCol = rCellPos.Col();
+ for (SCCOL nCol = rRange.aStart.Col(); nCol < nEndCol; nCol++)
+ {
+ sal_uInt16 nDocW = pDoc->GetColWidth( nCol, nTab );
+ if (nDocW)
+ nPosX += o3tl::convert(nDocW, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ const tools::Long nSizeX
+ = o3tl::convert(pDoc->GetColWidth(nEndCol, nTab), o3tl::Length::twip, o3tl::Length::mm100);
+
+ SCROW nEndRow = rCellPos.Row();
+ tools::Long nPosY = o3tl::convert(pDoc->GetRowHeight(rRange.aStart.Row(), nEndRow, nTab),
+ o3tl::Length::twip, o3tl::Length::mm100);
+ tools::Long nSizeY
+ = o3tl::convert(pDoc->GetRowHeight(nEndRow, nTab), o3tl::Length::twip, o3tl::Length::mm100);
+
+ Size aOffsetLogic( nPosX, nPosY );
+ Size aSizeLogic( nSizeX, nSizeY );
+ Size aOffsetPixel = pWindow->LogicToPixel( aOffsetLogic, aCellMapMode );
+ Size aSizePixel = pWindow->LogicToPixel( aSizeLogic, aCellMapMode );
+
+ return tools::Rectangle( Point( aOffsetPixel.Width(), aOffsetPixel.Height() ), aSizePixel );
+}
+
+void ScPreviewLocationData::GetCellPosition( const ScAddress& rCellPos, tools::Rectangle& rCellRect ) const
+{
+ ScPreviewLocationEntry* pEntry = lcl_GetEntryByAddress( m_Entries, rCellPos, SC_PLOC_CELLRANGE );
+ if ( pEntry )
+ {
+ tools::Rectangle aOffsetRect = GetOffsetPixel( rCellPos, pEntry->aCellRange );
+ rCellRect = tools::Rectangle( aOffsetRect.Left() + pEntry->aPixelRect.Left(),
+ aOffsetRect.Top() + pEntry->aPixelRect.Top(),
+ aOffsetRect.Right() + pEntry->aPixelRect.Left(),
+ aOffsetRect.Bottom() + pEntry->aPixelRect.Top() );
+ }
+}
+
+bool ScPreviewLocationData::HasCellsInRange( const tools::Rectangle& rVisiblePixel ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_CELLRANGE || it->eType == SC_PLOC_COLHEADER || it->eType == SC_PLOC_ROWHEADER )
+ if ( it->aPixelRect.Overlaps( rVisiblePixel ) )
+ return true;
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::GetHeaderPosition( tools::Rectangle& rRect ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTHEADER || it->eType == SC_PLOC_RIGHTHEADER )
+ {
+ rRect = it->aPixelRect;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::GetFooterPosition( tools::Rectangle& rRect ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTFOOTER || it->eType == SC_PLOC_RIGHTFOOTER )
+ {
+ rRect = it->aPixelRect;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::IsHeaderLeft() const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTHEADER )
+ return true;
+
+ if ( it->eType == SC_PLOC_RIGHTHEADER )
+ return false;
+ }
+
+ return false;
+}
+
+bool ScPreviewLocationData::IsFooterLeft() const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_LEFTFOOTER )
+ return true;
+
+ if ( it->eType == SC_PLOC_RIGHTFOOTER )
+ return false;
+ }
+
+ return false;
+}
+
+tools::Long ScPreviewLocationData::GetNoteCountInRange( const tools::Rectangle& rVisiblePixel, bool bNoteMarks ) const
+{
+ ScPreviewLocationType eType = bNoteMarks ? SC_PLOC_NOTEMARK : SC_PLOC_NOTETEXT;
+
+ sal_uLong nRet = 0;
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == eType && it->aPixelRect.Overlaps( rVisiblePixel ) )
+ ++nRet;
+ }
+
+ return nRet;
+}
+
+bool ScPreviewLocationData::GetNoteInRange( const tools::Rectangle& rVisiblePixel, tools::Long nIndex, bool bNoteMarks,
+ ScAddress& rCellPos, tools::Rectangle& rNoteRect ) const
+{
+ ScPreviewLocationType eType = bNoteMarks ? SC_PLOC_NOTEMARK : SC_PLOC_NOTETEXT;
+
+ sal_uLong nPos = 0;
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == eType && it->aPixelRect.Overlaps( rVisiblePixel ) )
+ {
+ if ( nPos == sal::static_int_cast<sal_uLong>(nIndex) )
+ {
+ rCellPos = it->aCellRange.aStart;
+ rNoteRect = it->aPixelRect;
+ return true;
+ }
+ ++nPos;
+ }
+ }
+
+ return false;
+}
+
+tools::Rectangle ScPreviewLocationData::GetNoteInRangeOutputRect(const tools::Rectangle& rVisiblePixel, bool bNoteMarks, const ScAddress& aCellPos) const
+{
+ ScPreviewLocationType eType = bNoteMarks ? SC_PLOC_NOTEMARK : SC_PLOC_NOTETEXT;
+
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == eType && it->aPixelRect.Overlaps( rVisiblePixel ) )
+ {
+ if ( aCellPos == it->aCellRange.aStart )
+ return it->aPixelRect;
+ }
+ }
+
+ return tools::Rectangle();
+}
+
+void ScPreviewLocationData::GetTableInfo( const tools::Rectangle& rVisiblePixel, ScPreviewTableInfo& rInfo ) const
+{
+ // from left to right:
+ bool bHasHeaderCol = false;
+ bool bHasRepCols = false;
+ bool bHasMainCols = false;
+ SCCOL nRepeatColStart = 0;
+ SCCOL nRepeatColEnd = 0;
+ SCCOL nMainColStart = 0;
+ SCCOL nMainColEnd = 0;
+
+ // from top to bottom:
+ bool bHasHeaderRow = false;
+ bool bHasRepRows = false;
+ bool bHasMainRows = false;
+ SCROW nRepeatRowStart = 0;
+ SCROW nRepeatRowEnd = 0;
+ SCROW nMainRowStart = 0;
+ SCROW nMainRowEnd = 0;
+
+ tools::Rectangle aHeaderRect, aRepeatRect, aMainRect;
+ SCTAB nTab = 0;
+
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_CELLRANGE )
+ {
+ if ( it->bRepeatCol )
+ {
+ bHasRepCols = true;
+ nRepeatColStart = it->aCellRange.aStart.Col();
+ nRepeatColEnd = it->aCellRange.aEnd.Col();
+ aRepeatRect.SetLeft( it->aPixelRect.Left() );
+ aRepeatRect.SetRight( it->aPixelRect.Right() );
+ }
+ else
+ {
+ bHasMainCols = true;
+ nMainColStart = it->aCellRange.aStart.Col();
+ nMainColEnd = it->aCellRange.aEnd.Col();
+ aMainRect.SetLeft( it->aPixelRect.Left() );
+ aMainRect.SetRight( it->aPixelRect.Right() );
+ }
+ if ( it->bRepeatRow )
+ {
+ bHasRepRows = true;
+ nRepeatRowStart = it->aCellRange.aStart.Row();
+ nRepeatRowEnd = it->aCellRange.aEnd.Row();
+ aRepeatRect.SetTop( it->aPixelRect.Top() );
+ aRepeatRect.SetBottom( it->aPixelRect.Bottom() );
+ }
+ else
+ {
+ bHasMainRows = true;
+ nMainRowStart = it->aCellRange.aStart.Row();
+ nMainRowEnd = it->aCellRange.aEnd.Row();
+ aMainRect.SetTop( it->aPixelRect.Top() );
+ aMainRect.SetBottom( it->aPixelRect.Bottom() );
+ }
+ nTab = it->aCellRange.aStart.Tab(); //! store separately?
+ }
+ else if ( it->eType == SC_PLOC_ROWHEADER )
+ {
+ // row headers result in an additional column
+ bHasHeaderCol = true;
+ aHeaderRect.SetLeft( it->aPixelRect.Left() );
+ aHeaderRect.SetRight( it->aPixelRect.Right() );
+ }
+ else if ( it->eType == SC_PLOC_COLHEADER )
+ {
+ // column headers result in an additional row
+ bHasHeaderRow = true;
+ aHeaderRect.SetTop( it->aPixelRect.Top() );
+ aHeaderRect.SetBottom( it->aPixelRect.Bottom() );
+ }
+ }
+
+ // get column info
+
+ SCCOL nColCount = 0;
+ SCCOL nCol;
+ if ( bHasHeaderCol )
+ ++nColCount;
+ if ( bHasRepCols )
+ for ( nCol=nRepeatColStart; nCol<=nRepeatColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ ++nColCount;
+ if ( bHasMainCols )
+ for ( nCol=nMainColStart; nCol<=nMainColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ ++nColCount;
+
+ if ( nColCount > 0 )
+ {
+ ScPreviewColRowInfo* pColInfo = new ScPreviewColRowInfo[ nColCount ];
+ SCCOL nColPos = 0;
+
+ if ( bHasHeaderCol )
+ {
+ pColInfo[nColPos].Set( true, 0, aHeaderRect.Left(), aHeaderRect.Right() );
+ ++nColPos;
+ }
+ if ( bHasRepCols )
+ {
+ tools::Long nPosX = 0;
+ for ( nCol=nRepeatColStart; nCol<=nRepeatColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ {
+ sal_uInt16 nDocW = pDoc->GetColWidth( nCol, nTab );
+ tools::Long nNextX
+ = nPosX + o3tl::convert(nDocW, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( nPosX, 0 ), aCellMapMode ).Width();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( nNextX, 0 ), aCellMapMode ).Width() - 1;
+ pColInfo[nColPos].Set( false, nCol,
+ aRepeatRect.Left() + nPixelStart,
+ aRepeatRect.Left() + nPixelEnd );
+
+ nPosX = nNextX;
+ ++nColPos;
+ }
+ }
+ if ( bHasMainCols )
+ {
+ tools::Long nPosX = 0;
+ for ( nCol=nMainColStart; nCol<=nMainColEnd; nCol++ )
+ if (!pDoc->ColHidden(nCol, nTab))
+ {
+ sal_uInt16 nDocW = pDoc->GetColWidth( nCol, nTab );
+ tools::Long nNextX
+ = nPosX + o3tl::convert(nDocW, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( nPosX, 0 ), aCellMapMode ).Width();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( nNextX, 0 ), aCellMapMode ).Width() - 1;
+ pColInfo[nColPos].Set( false, nCol,
+ aMainRect.Left() + nPixelStart,
+ aMainRect.Left() + nPixelEnd );
+
+ nPosX = nNextX;
+ ++nColPos;
+ }
+ }
+ rInfo.SetColInfo( nColCount, pColInfo );
+ }
+ else
+ rInfo.SetColInfo( 0, nullptr );
+
+ // get row info
+
+ SCROW nRowCount = 0;
+ if ( bHasHeaderRow )
+ ++nRowCount;
+ if ( bHasRepRows )
+ nRowCount += pDoc->CountVisibleRows(nRepeatRowStart, nRepeatRowEnd, nTab);
+ if ( bHasMainRows )
+ nRowCount += pDoc->CountVisibleRows(nMainRowStart, nMainRowEnd, nTab);
+
+ if ( nRowCount > 0 )
+ {
+ ScPreviewColRowInfo* pRowInfo = new ScPreviewColRowInfo[ nRowCount ];
+ SCROW nRowPos = 0;
+
+ if ( bHasHeaderRow )
+ {
+ pRowInfo[nRowPos].Set( true, 0, aHeaderRect.Top(), aHeaderRect.Bottom() );
+ ++nRowPos;
+ }
+ if ( bHasRepRows )
+ {
+ tools::Long nPosY = 0;
+ for (SCROW nRow = nRepeatRowStart; nRow <= nRepeatRowEnd; ++nRow)
+ {
+ if (pDoc->RowHidden(nRow, nTab))
+ continue;
+
+ sal_uInt16 nDocH = pDoc->GetOriginalHeight( nRow, nTab );
+ tools::Long nNextY
+ = nPosY + o3tl::convert(nDocH, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( 0, nPosY ), aCellMapMode ).Height();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( 0, nNextY ), aCellMapMode ).Height() - 1;
+ pRowInfo[nRowPos].Set( false, nRow,
+ aRepeatRect.Top() + nPixelStart,
+ aRepeatRect.Top() + nPixelEnd );
+
+ nPosY = nNextY;
+ ++nRowPos;
+ }
+ }
+ if ( bHasMainRows )
+ {
+ tools::Long nPosY = 0;
+ for (SCROW nRow = nMainRowStart; nRow <= nMainRowEnd; ++nRow)
+ {
+ if (pDoc->RowHidden(nRow, nTab))
+ continue;
+
+ sal_uInt16 nDocH = pDoc->GetOriginalHeight( nRow, nTab );
+ tools::Long nNextY
+ = nPosY + o3tl::convert(nDocH, o3tl::Length::twip, o3tl::Length::mm100);
+
+ tools::Long nPixelStart = pWindow->LogicToPixel( Size( 0, nPosY ), aCellMapMode ).Height();
+ tools::Long nPixelEnd = pWindow->LogicToPixel( Size( 0, nNextY ), aCellMapMode ).Height() - 1;
+ pRowInfo[nRowPos].Set( false, nRow,
+ aMainRect.Top() + nPixelStart,
+ aMainRect.Top() + nPixelEnd );
+
+ nPosY = nNextY;
+ ++nRowPos;
+ }
+ }
+ rInfo.SetRowInfo( nRowCount, pRowInfo );
+ }
+ else
+ rInfo.SetRowInfo( 0, nullptr );
+
+ // limit to visible area
+
+ rInfo.SetTab( nTab );
+ rInfo.LimitToArea( rVisiblePixel );
+}
+
+tools::Rectangle ScPreviewLocationData::GetHeaderCellOutputRect(const tools::Rectangle& rVisRect, const ScAddress& rCellPos, bool bColHeader) const
+{
+ // first a stupid implementation
+ // NN says here should be done more
+ tools::Rectangle aClipRect;
+ ScPreviewTableInfo aTableInfo;
+ GetTableInfo( rVisRect, aTableInfo );
+
+ if ( (rCellPos.Col() >= 0) &&
+ (rCellPos.Row() >= 0) && (rCellPos.Col() < aTableInfo.GetCols()) &&
+ (rCellPos.Row() < aTableInfo.GetRows()) )
+ {
+ SCCOL nCol(0);
+ SCROW nRow(0);
+ if (bColHeader)
+ nCol = rCellPos.Col();
+ else
+ nRow = rCellPos.Row();
+ const ScPreviewColRowInfo& rColInfo = aTableInfo.GetColInfo()[nCol];
+ const ScPreviewColRowInfo& rRowInfo = aTableInfo.GetRowInfo()[nRow];
+
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ aClipRect = tools::Rectangle( rColInfo.nPixelStart, rRowInfo.nPixelStart, rColInfo.nPixelEnd, rRowInfo.nPixelEnd );
+ }
+ return aClipRect;
+}
+
+tools::Rectangle ScPreviewLocationData::GetCellOutputRect(const ScAddress& rCellPos) const
+{
+ // first a stupid implementation
+ // NN says here should be done more
+ tools::Rectangle aRect;
+ GetCellPosition(rCellPos, aRect);
+ return aRect;
+}
+
+// GetMainCellRange is used for links in PDF export
+
+bool ScPreviewLocationData::GetMainCellRange( ScRange& rRange, tools::Rectangle& rPixRect ) const
+{
+ for (auto const& it : m_Entries)
+ {
+ if ( it->eType == SC_PLOC_CELLRANGE && !it->bRepeatCol && !it->bRepeatRow )
+ {
+ rRange = it->aCellRange;
+ rPixRect = it->aPixelRect;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/prevwsh.cxx b/sc/source/ui/view/prevwsh.cxx
new file mode 100644
index 0000000000..c526331d58
--- /dev/null
+++ b/sc/source/ui/view/prevwsh.cxx
@@ -0,0 +1,1181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <scitems.hxx>
+
+#include <comphelper/SetFlagContextHelper.hxx>
+#include <sfx2/app.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewfac.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <drwlayer.hxx>
+#include <prevwsh.hxx>
+#include <preview.hxx>
+#include <printfun.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <docsh.hxx>
+#include <tabvwsh.hxx>
+#include <stlpool.hxx>
+#include <editutil.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <sc.hrc>
+#include <ViewSettingsSequenceDefines.hxx>
+#include <viewuno.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <svx/dialogs.hrc>
+
+#include <basegfx/utils/zoomtools.hxx>
+#include <svx/zoom_def.hxx>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#include <scabstdlg.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/notebookbar/notebookbar.hxx>
+
+// for mouse wheel
+#define MINZOOM_SLIDER 10
+#define MAXZOOM_SLIDER 400
+
+#define SC_USERDATA_SEP ';'
+
+using namespace com::sun::star;
+
+#define ShellClass_ScPreviewShell
+#include <scslots.hxx>
+
+#include <memory>
+
+
+SFX_IMPL_INTERFACE(ScPreviewShell, SfxViewShell)
+
+void ScPreviewShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT,
+ SfxVisibilityFlags::Standard|SfxVisibilityFlags::Server|SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::Objectbar_Preview);
+
+ GetStaticInterface()->RegisterPopupMenu("preview");
+}
+
+SFX_IMPL_NAMED_VIEWFACTORY( ScPreviewShell, "PrintPreview" )
+{
+ SFX_VIEW_REGISTRATION(ScDocShell);
+}
+
+void ScPreviewShell::Construct( vcl::Window* pParent )
+{
+ // Find the top-most window, and set the close window handler to intercept
+ // the window close event.
+ vcl::Window* pWin = pParent;
+ while (!pWin->IsSystemWindow())
+ {
+ if (pWin->GetParent())
+ pWin = pWin->GetParent();
+ else
+ break;
+ }
+
+ mpFrameWindow = dynamic_cast<SystemWindow*>(pWin);
+ if (mpFrameWindow)
+ mpFrameWindow->SetCloseHdl(LINK(this, ScPreviewShell, CloseHdl));
+
+ eZoom = SvxZoomType::WHOLEPAGE;
+
+ pHorScroll = VclPtr<ScrollAdaptor>::Create(pParent, true);
+ pVerScroll = VclPtr<ScrollAdaptor>::Create(pParent, false);
+
+ // RTL: no mirroring for horizontal scrollbars
+ pHorScroll->EnableRTL( false );
+
+ pHorScroll->SetScrollHdl(LINK(this, ScPreviewShell, HorzScrollHandler));
+ pVerScroll->SetScrollHdl(LINK(this, ScPreviewShell, VertScrollHandler));
+
+ pPreview = VclPtr<ScPreview>::Create( pParent, pDocShell, this );
+
+ SetPool( &SC_MOD()->GetPool() );
+ SetWindow( pPreview );
+ StartListening(*pDocShell, DuplicateHandling::Prevent);
+ StartListening(*SfxGetpApp(), DuplicateHandling::Prevent); // #i62045# #i62046# application is needed for Calc's own hints
+ SfxBroadcaster* pDrawBC = pDocShell->GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ StartListening(*pDrawBC);
+
+ pHorScroll->Show( false );
+ pVerScroll->Show( false );
+ SetName("Preview");
+}
+
+ScPreviewShell::ScPreviewShell(SfxViewFrame& rViewFrame,
+ SfxViewShell* pOldSh) :
+ SfxViewShell(rViewFrame, SfxViewShellFlags::HAS_PRINTOPTIONS),
+ pDocShell( static_cast<ScDocShell*>(rViewFrame.GetObjectShell()) ),
+ mpFrameWindow(nullptr),
+ nSourceDesignMode( TRISTATE_INDET ),
+ nMaxVertPos(0),
+ nPrevHThumbPos(0),
+ nPrevVThumbPos(0)
+{
+ Construct(&rViewFrame.GetWindow());
+ SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Printpreview));
+
+ if ( auto pTabViewShell = dynamic_cast<ScTabViewShell*>( pOldSh) )
+ {
+ // store view settings, show table from TabView
+ //! store live ScViewData instead, and update on ScTablesHint?
+ //! or completely forget aSourceData on ScTablesHint?
+
+ const ScViewData& rData = pTabViewShell->GetViewData();
+ pPreview->SetSelectedTabs(rData.GetMarkData());
+ InitStartTable( rData.GetTabNo() );
+
+ // also have to store the TabView's DesignMode state
+ // (only if draw view exists)
+ SdrView* pDrawView = pTabViewShell->GetScDrawView();
+ if ( pDrawView )
+ nSourceDesignMode
+ = pDrawView->IsDesignMode() ? TRISTATE_TRUE : TRISTATE_FALSE;
+ }
+
+ new ScPreviewObj(this);
+}
+
+ScPreviewShell::~ScPreviewShell()
+{
+ if (mpFrameWindow)
+ mpFrameWindow->SetCloseHdl(Link<SystemWindow&,void>()); // Remove close handler.
+
+ // #108333#; notify Accessibility that Shell is dying and before destroy all
+ BroadcastAccessibility( SfxHint( SfxHintId::Dying ) );
+ pAccessibilityBroadcaster.reset();
+
+ SfxBroadcaster* pDrawBC = pDocShell->GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ EndListening(*pDrawBC);
+ EndListening(*SfxGetpApp());
+ EndListening(*pDocShell);
+
+ SetWindow(nullptr);
+ pPreview.disposeAndClear();
+ pHorScroll.disposeAndClear();
+ pVerScroll.disposeAndClear();
+
+ // normal mode of operation is switching back to default view in the same frame,
+ // so there's no need to activate any other window here anymore
+}
+
+void ScPreviewShell::InitStartTable(SCTAB nTab)
+{
+ pPreview->SetPageNo( pPreview->GetFirstPage(nTab) );
+}
+
+void ScPreviewShell::AdjustPosSizePixel( const Point &rPos, const Size &rSize )
+{
+ Size aOutSize( rSize );
+ pPreview->SetPosSizePixel( rPos, aOutSize );
+
+ if ( SvxZoomType::WHOLEPAGE == eZoom )
+ pPreview->SetZoom( pPreview->GetOptimalZoom(false) );
+ else if ( SvxZoomType::PAGEWIDTH == eZoom )
+ pPreview->SetZoom( pPreview->GetOptimalZoom(true) );
+
+ UpdateNeededScrollBars(false);
+}
+
+void ScPreviewShell::InnerResizePixel( const Point &rOfs, const Size &rSize, bool )
+{
+ AdjustPosSizePixel( rOfs,rSize );
+}
+
+void ScPreviewShell::OuterResizePixel( const Point &rOfs, const Size &rSize )
+{
+ AdjustPosSizePixel( rOfs,rSize );
+}
+
+bool ScPreviewShell::GetPageSize( Size& aPageSize )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = pPreview->GetTab();
+
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ),
+ SfxStyleFamily::Page );
+ OSL_ENSURE(pStyleSheet,"No style sheet");
+ if (!pStyleSheet) return false;
+ const SfxItemSet* pParamSet = &pStyleSheet->GetItemSet();
+
+ aPageSize = pParamSet->Get(ATTR_PAGE_SIZE).GetSize();
+ aPageSize.setWidth(o3tl::convert(aPageSize.Width(), o3tl::Length::twip, o3tl::Length::mm100));
+ aPageSize.setHeight(o3tl::convert(aPageSize.Height(), o3tl::Length::twip, o3tl::Length::mm100));
+ return true;
+}
+
+void ScPreviewShell::UpdateNeededScrollBars( bool bFromZoom )
+{
+ Size aPageSize;
+ OutputDevice* pDevice = Application::GetDefaultDevice();
+
+ tools::Long nBarW = GetViewFrame().GetWindow().GetSettings().GetStyleSettings().GetScrollBarSize();
+ tools::Long nBarH = nBarW;
+
+ tools::Long aHeightOffSet = pDevice ? pDevice->PixelToLogic( Size( nBarW, nBarH ), pPreview->GetMapMode() ).Height() : 0;
+ tools::Long aWidthOffSet = aHeightOffSet;
+
+ if (!GetPageSize( aPageSize ))
+ return;
+
+ // for centering, page size without the shadow is used
+ bool bVert = pVerScroll->IsVisible();
+ bool bHori = pHorScroll->IsVisible();
+ Size aWindowSize = pPreview->GetOutDev()->GetOutputSize();
+ Point aPos = pPreview->GetPosPixel();
+ Size aWindowPixelSize = pPreview->GetOutputSizePixel();
+
+ // if we are called from Zoom then we need to compensate for whatever
+ // scrollbars were displayed before the zoom was called
+ if ( bFromZoom )
+ {
+ if ( bVert )
+ {
+ aWindowPixelSize.AdjustWidth(nBarH );
+ aWindowSize.AdjustWidth(aHeightOffSet );
+ }
+ if ( bHori )
+ {
+ aWindowPixelSize.AdjustHeight(nBarW );
+ aWindowSize.AdjustHeight(aWidthOffSet );
+ }
+ }
+
+ // recalculate any needed scrollbars
+ tools::Long nMaxWidthPos = aPageSize.Width() - aWindowSize.Width();
+ bHori = nMaxWidthPos >= 0;
+ tools::Long nMaxHeightPos = aPageSize.Height() - aWindowSize.Height();
+ bVert = nMaxHeightPos >= 0;
+
+ // see if having a scroll bar requires the other
+ if ( bVert != bHori && ( bVert || bHori ) )
+ {
+ if ( bVert && ( (nMaxWidthPos + aWidthOffSet ) > 0 ) )
+ bHori = true;
+ else if ( (nMaxHeightPos + aHeightOffSet ) > 0 )
+ bVert = true;
+ }
+ pHorScroll->Show( bHori );
+ pVerScroll->Show( bVert );
+
+ // make room for needed scrollbars ( and reduce the size
+ // of the preview appropriately )
+ if ( bHori )
+ aWindowPixelSize.AdjustHeight( -nBarW );
+ if ( bVert )
+ aWindowPixelSize.AdjustWidth( -nBarH );
+
+ pPreview->SetSizePixel( aWindowPixelSize );
+ pHorScroll->SetPosSizePixel( Point( aPos.X(), aPos.Y() + aWindowPixelSize.Height() ),
+ Size( aWindowPixelSize.Width(), nBarH ) );
+ pVerScroll->SetPosSizePixel( Point( aPos.X() + aWindowPixelSize.Width(), aPos.Y() ),
+ Size( nBarW, aWindowPixelSize.Height() ) );
+ UpdateScrollBars();
+}
+
+void ScPreviewShell::UpdateScrollBars()
+{
+ Size aPageSize;
+ if ( !GetPageSize( aPageSize ) )
+ return;
+
+ // for centering, page size without the shadow is used
+
+ Size aWindowSize = pPreview->GetOutDev()->GetOutputSize();
+
+ Point aOfs = pPreview->GetOffset();
+
+ if( pHorScroll )
+ {
+ pHorScroll->SetRange( Range( 0, aPageSize.Width() ) );
+ pHorScroll->SetLineSize( aWindowSize.Width() / 16 );
+ pHorScroll->SetPageSize( aWindowSize.Width() );
+ pHorScroll->SetVisibleSize( aWindowSize.Width() );
+ tools::Long nMaxPos = aPageSize.Width() - aWindowSize.Width();
+ if ( nMaxPos<0 )
+ {
+ // page smaller than window -> center (but put scrollbar to 0)
+ aOfs.setX( 0 );
+ pPreview->SetXOffset( nMaxPos / 2 );
+ }
+ else if (aOfs.X() < 0)
+ {
+ // page larger than window -> never use negative offset
+ aOfs.setX( 0 );
+ pPreview->SetXOffset( 0 );
+ }
+ else if (aOfs.X() > nMaxPos)
+ {
+ // limit offset to align with right edge of window
+ aOfs.setX( nMaxPos );
+ pPreview->SetXOffset(nMaxPos);
+ }
+ pHorScroll->SetThumbPos( aOfs.X() );
+ nPrevHThumbPos = pHorScroll->GetThumbPos();
+ }
+
+ if( !pVerScroll )
+ return;
+
+ tools::Long nPageNo = pPreview->GetPageNo();
+ tools::Long nTotalPages = pPreview->GetTotalPages();
+
+ nMaxVertPos = aPageSize.Height() - aWindowSize.Height();
+ pVerScroll->SetLineSize( aWindowSize.Height() / 16 );
+ pVerScroll->SetPageSize( aWindowSize.Height() );
+ pVerScroll->SetVisibleSize( aWindowSize.Height() );
+ if ( nMaxVertPos < 0 )
+ {
+ // page smaller than window -> center (but put scrollbar to 0)
+ aOfs.setY( 0 );
+ pPreview->SetYOffset( nMaxVertPos / 2 );
+ pVerScroll->SetThumbPos( nPageNo * aWindowSize.Height() );
+ pVerScroll->SetRange( Range( 0, aWindowSize.Height() * nTotalPages ));
+ }
+ else if (aOfs.Y() < 0)
+ {
+ // page larger than window -> never use negative offset
+ pVerScroll->SetRange( Range( 0, aPageSize.Height() ) );
+ aOfs.setY( 0 );
+ pPreview->SetYOffset( 0 );
+ pVerScroll->SetThumbPos( aOfs.Y() );
+ }
+ else if (aOfs.Y() > nMaxVertPos )
+ {
+ // limit offset to align with window bottom
+ pVerScroll->SetRange( Range( 0, aPageSize.Height() ) );
+ aOfs.setY( nMaxVertPos );
+ pPreview->SetYOffset( nMaxVertPos );
+ pVerScroll->SetThumbPos( aOfs.Y() );
+ }
+ nPrevVThumbPos = pVerScroll->GetThumbPos();
+}
+
+IMPL_LINK_NOARG(ScPreviewShell, HorzScrollHandler, weld::Scrollbar&, void)
+{
+ ScrollHandler(pHorScroll);
+}
+
+IMPL_LINK_NOARG(ScPreviewShell, VertScrollHandler, weld::Scrollbar&, void)
+{
+ ScrollHandler(pVerScroll);
+}
+
+void ScPreviewShell::ScrollHandler(ScrollAdaptor* pScroll)
+{
+ tools::Long nPos = pScroll->GetThumbPos();
+ tools::Long nMaxRange = pScroll->GetRangeMax();
+ tools::Long nTotalPages = pPreview->GetTotalPages();
+ tools::Long nPageNo = 0;
+ tools::Long nPerPageLength = 0;
+ bool bIsDivide = true;
+
+ if( nTotalPages )
+ nPerPageLength = nMaxRange / nTotalPages;
+
+ if( nPerPageLength )
+ {
+ nPageNo = nPos / nPerPageLength;
+ if( nPos % nPerPageLength )
+ {
+ bIsDivide = false;
+ nPageNo ++;
+ }
+ }
+
+ bool bHoriz = ( pScroll == pHorScroll );
+
+ tools::Long nDelta = bHoriz ? (pHorScroll->GetThumbPos() - nPrevHThumbPos)
+ : (pVerScroll->GetThumbPos() - nPrevVThumbPos);
+
+ if( bHoriz )
+ pPreview->SetXOffset( nPos );
+ else
+ {
+ if( nMaxVertPos > 0 )
+ pPreview->SetYOffset( nPos );
+ else
+ {
+ Point aMousePos = pScroll->OutputToNormalizedScreenPixel( pScroll->GetPointerPosPixel() );
+ Point aPos = pScroll->GetParent()->OutputToNormalizedScreenPixel( pScroll->GetPosPixel() );
+ OUString aHelpStr;
+ tools::Rectangle aRect;
+ QuickHelpFlags nAlign;
+
+ if( nDelta < 0 )
+ {
+ if ( nTotalPages && nPageNo > 0 && !bIsDivide )
+ pPreview->SetPageNo( nPageNo-1 );
+ if( bIsDivide )
+ pPreview->SetPageNo( nPageNo );
+
+ aHelpStr = ScResId( STR_PAGE ) +
+ " " + OUString::number( nPageNo ) +
+ " / " + OUString::number( nTotalPages );
+ }
+ else if( nDelta > 0 )
+ {
+ bool bAllTested = pPreview->AllTested();
+ if ( nTotalPages && ( nPageNo < nTotalPages || !bAllTested ) )
+ pPreview->SetPageNo( nPageNo );
+
+ aHelpStr = ScResId( STR_PAGE ) +
+ " " + OUString::number( nPageNo+1 ) +
+ " / " + OUString::number( nTotalPages );
+ }
+
+ aRect.SetLeft( aPos.X() - 8 );
+ aRect.SetTop( aMousePos.Y() );
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+ nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
+ Help::ShowQuickHelp( pScroll->GetParent(), aRect, aHelpStr, nAlign );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ScPreviewShell, CloseHdl, SystemWindow&, void)
+{
+ ExitPreview();
+}
+
+bool ScPreviewShell::ScrollCommand( const CommandEvent& rCEvt )
+{
+ bool bDone = false;
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if ( pData && pData->GetMode() == CommandWheelMode::ZOOM )
+ {
+ sal_uInt16 nOld = pPreview->GetZoom();
+ sal_uInt16 nNew;
+ if ( pData->GetDelta() < 0 )
+ nNew = std::max( MINZOOM, basegfx::zoomtools::zoomOut( nOld ));
+ else
+ nNew = std::min( MAXZOOM, basegfx::zoomtools::zoomIn( nOld ));
+
+ if ( nNew != nOld )
+ {
+ eZoom = SvxZoomType::PERCENT;
+ pPreview->SetZoom( nNew );
+ }
+
+ bDone = true;
+ }
+ else
+ {
+ bDone = pPreview->HandleScrollCommand( rCEvt, pHorScroll, pVerScroll );
+ }
+
+ return bDone;
+}
+
+SfxPrinter* ScPreviewShell::GetPrinter( bool bCreate )
+{
+ return pDocShell->GetPrinter(bCreate);
+}
+
+sal_uInt16 ScPreviewShell::SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ return pDocShell->SetPrinter( pNewPrinter, nDiffFlags );
+}
+
+bool ScPreviewShell::HasPrintOptionsPage() const
+{
+ return true;
+}
+
+std::unique_ptr<SfxTabPage> ScPreviewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions)
+{
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
+ if ( ScTpPrintOptionsCreate )
+ return ScTpPrintOptionsCreate(pPage, pController, &rOptions);
+ return nullptr;
+}
+
+void ScPreviewShell::Activate(bool bMDI)
+{
+ SfxViewShell::Activate(bMDI);
+
+ //! Basic etc. -> outsource to its own file (see tabvwsh4)
+
+ if (bMDI)
+ {
+ // InputHdl is now mostly Null, no more assertion!
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl )
+ pInputHdl->NotifyChange( nullptr );
+ }
+
+ SfxShell::Activate(bMDI);
+}
+
+void ScPreviewShell::Execute( SfxRequest& rReq )
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ switch ( nSlot )
+ {
+ case SID_FORMATPAGE:
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ pDocShell->ExecutePageStyle( *this, rReq, pPreview->GetTab() );
+ break;
+ case SID_REPAINT:
+ pPreview->Invalidate();
+ rReq.Done();
+ break;
+ case SID_PREV_TABLE: // Accelerator
+ case SID_PREVIEW_PREVIOUS:
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && nPage > 0)
+ pPreview->SetPageNo( nPage-1 );
+ }
+ break;
+ case SID_NEXT_TABLE: // Accelerator
+ case SID_PREVIEW_NEXT:
+ {
+ bool bAllTested = pPreview->AllTested();
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && (nPage+1 < nTotal || !bAllTested))
+ pPreview->SetPageNo( nPage+1 );
+ }
+ break;
+ case SID_CURSORTOPOFFILE: // Accelerator
+ case SID_PREVIEW_FIRST:
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && nPage != 0)
+ pPreview->SetPageNo( 0 );
+ }
+ break;
+ case SID_CURSORENDOFFILE: // Accelerator
+ case SID_PREVIEW_LAST:
+ {
+ if (!pPreview->AllTested())
+ pPreview->CalcAll();
+
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if (nTotal && nPage+1 != nTotal)
+ pPreview->SetPageNo( nTotal-1 );
+ }
+ break;
+ case SID_ATTR_ZOOM:
+ case FID_SCALE:
+ {
+ sal_uInt16 nZoom = 100;
+ bool bCancel = false;
+
+ eZoom = SvxZoomType::PERCENT;
+
+ if ( pReqArgs )
+ {
+
+ const SvxZoomItem& rZoomItem = pReqArgs->Get(SID_ATTR_ZOOM);
+
+ eZoom = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ else
+ {
+ SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( GetPool() );
+ SvxZoomItem aZoomItem( SvxZoomType::PERCENT, pPreview->GetZoom(), SID_ATTR_ZOOM );
+
+ aSet.Put( aZoomItem );
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxZoomDialog> pDlg(pFact->CreateSvxZoomDialog(nullptr, aSet));
+ pDlg->SetLimits( 20, 400 );
+ pDlg->HideButton( ZoomButtonId::OPTIMAL );
+ bCancel = ( RET_CANCEL == pDlg->Execute() );
+
+ if ( !bCancel )
+ {
+ const SvxZoomItem& rZoomItem = pDlg->GetOutputItemSet()->
+ Get( SID_ATTR_ZOOM );
+
+ eZoom = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ }
+
+ if ( !bCancel )
+ {
+ switch ( eZoom )
+ {
+ case SvxZoomType::OPTIMAL:
+ case SvxZoomType::WHOLEPAGE:
+ nZoom = pPreview->GetOptimalZoom(false);
+ break;
+ case SvxZoomType::PAGEWIDTH:
+ nZoom = pPreview->GetOptimalZoom(true);
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ pPreview->SetZoom( nZoom );
+ rReq.Done();
+ }
+ }
+ break;
+ case SID_ZOOM_IN:
+ {
+ sal_uInt16 nNew = pPreview->GetZoom() + 20 ;
+ nNew -= nNew % 20;
+ pPreview->SetZoom( nNew );
+ eZoom = SvxZoomType::PERCENT;
+ rReq.Done();
+ }
+ break;
+ case SID_ZOOM_OUT:
+ {
+ sal_uInt16 nNew = pPreview->GetZoom() - 1;
+ nNew -= nNew % 20;
+ pPreview->SetZoom( nNew );
+ eZoom = SvxZoomType::PERCENT;
+ rReq.Done();
+ }
+ break;
+ case SID_PREVIEW_MARGIN:
+ {
+ bool bMargin = pPreview->GetPageMargins();
+ pPreview->SetPageMargins( !bMargin );
+ pPreview->Invalidate();
+ rReq.Done();
+ }
+ break;
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ const SvxZoomSliderItem* pItem;
+ eZoom = SvxZoomType::PERCENT;
+ if( pReqArgs && (pItem = pReqArgs->GetItemIfSet( SID_ATTR_ZOOMSLIDER )) )
+ {
+ const sal_uInt16 nCurrentZoom = pItem->GetValue();
+ if( nCurrentZoom )
+ {
+ pPreview->SetZoom( nCurrentZoom );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+ case SID_PREVIEW_SCALINGFACTOR:
+ {
+ const SvxZoomSliderItem* pItem;
+ SCTAB nTab = pPreview->GetTab();
+ OUString aOldName = pDocShell->GetDocument().GetPageStyle( pPreview->GetTab() );
+ ScStyleSheetPool* pStylePool = pDocShell->GetDocument().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pReqArgs && pStyleSheet && (pItem = pReqArgs->GetItemIfSet( SID_PREVIEW_SCALINGFACTOR )) )
+ {
+ const sal_uInt16 nCurrentZoom = pItem->GetValue();
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ rSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, nCurrentZoom ) );
+ ScPrintFunc aPrintFunc( pDocShell, pDocShell->GetPrinter(), nTab );
+ aPrintFunc.UpdatePages();
+ rReq.Done();
+ }
+ GetViewFrame().GetBindings().Invalidate( nSlot );
+ }
+ break;
+ case SID_PRINTPREVIEW:
+ case SID_PREVIEW_CLOSE:
+ // print preview is now always in the same frame as the tab view
+ // -> always switch this frame back to normal view
+ // (ScTabViewShell ctor reads stored view data)
+
+ ExitPreview();
+ break;
+ case SID_CURSORPAGEUP:
+ case SID_CURSORPAGEDOWN:
+ case SID_CURSORHOME:
+ case SID_CURSOREND:
+ case SID_CURSORUP:
+ case SID_CURSORDOWN:
+ case SID_CURSORLEFT:
+ case SID_CURSORRIGHT:
+ DoScroll( nSlot );
+ break;
+ case SID_CANCEL:
+ if( ScViewUtil::IsFullScreen( *this ) )
+ ScViewUtil::SetFullScreen( *this, false );
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ScPreviewShell::GetState( SfxItemSet& rSet )
+{
+ pPreview->SetInGetState(true);
+
+ SCTAB nTab = pPreview->GetTab();
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ sal_uInt16 nZoom = pPreview->GetZoom();
+ bool bAllTested = pPreview->AllTested();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ pDocShell->GetStatePageStyle( rSet, nTab );
+ break;
+ case SID_UNDO:
+ case SID_REDO:
+ case SID_REPEAT:
+ case SID_SAVEDOC:
+ case SID_SAVEASDOC:
+ case SID_MAIL_SENDDOC:
+ case SID_VIEW_DATA_SOURCE_BROWSER:
+ case SID_QUITAPP:
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_PREVIEW_PREVIOUS:
+ case SID_PREVIEW_FIRST:
+ if (!nTotal || nPage==0)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_PREVIEW_NEXT:
+ case SID_PREVIEW_LAST:
+ if (bAllTested)
+ if (!nTotal || nPage==nTotal-1)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_ZOOM_IN:
+ if (nZoom >= 400)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_ZOOM_OUT:
+ if (nZoom <= 20)
+ rSet.DisableItem(nWhich);
+ break;
+ case SID_ATTR_ZOOM:
+ {
+ SvxZoomItem aZoom( eZoom, nZoom, TypedWhichId<SvxZoomItem>(nWhich) );
+ aZoom.SetValueSet( SvxZoomEnableFlags::ALL & ~SvxZoomEnableFlags::OPTIMAL );
+ rSet.Put( aZoom );
+ }
+ break;
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ SvxZoomSliderItem aZoomSliderItem( nZoom, MINZOOM, MAXZOOM, SID_ATTR_ZOOMSLIDER );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ break;
+ case SID_PREVIEW_SCALINGFACTOR:
+ {
+ if( pDocShell->IsReadOnly() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ OUString aOldName = pDocShell->GetDocument().GetPageStyle( pPreview->GetTab() );
+ ScStyleSheetPool* pStylePool = pDocShell->GetDocument().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aOldName, SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-/" );
+
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ sal_uInt16 nCurrentZoom = rStyleSet.Get(ATTR_PAGE_SCALE).GetValue();
+ if( nCurrentZoom )
+ {
+ SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM_SLIDER, MAXZOOM_SLIDER, SID_PREVIEW_SCALINGFACTOR );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ }
+ }
+ }
+ break;
+ case SID_STATUS_DOCPOS:
+ rSet.Put( SfxStringItem( nWhich, pPreview->GetPosString() ) );
+ break;
+ case SID_PRINTPREVIEW:
+ rSet.Put( SfxBoolItem( nWhich, true ) );
+ break;
+ case SID_FORMATPAGE:
+ case SID_PREVIEW_MARGIN:
+ if( pDocShell->IsReadOnly() )
+ rSet.DisableItem( nWhich );
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+
+ pPreview->SetInGetState(false);
+}
+
+void ScPreviewShell::FillFieldData( ScHeaderFieldData& rData )
+{
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = pPreview->GetTab();
+ OUString aTmp;
+ rDoc.GetName(nTab, aTmp);
+ rData.aTabName = aTmp;
+
+ if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
+ rData.aTitle = pDocShell->getDocProperties()->getTitle();
+ else
+ rData.aTitle = pDocShell->GetTitle();
+
+ const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
+ rData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if ( !rData.aLongDocName.isEmpty() )
+ rData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ rData.aShortDocName = rData.aLongDocName = rData.aTitle;
+ rData.nPageNo = pPreview->GetPageNo() + 1;
+
+ bool bAllTested = pPreview->AllTested();
+ if (bAllTested)
+ rData.nTotalPages = pPreview->GetTotalPages();
+ else
+ rData.nTotalPages = 99;
+
+ // the dialog knows eNumType
+}
+
+void ScPreviewShell::WriteUserData(OUString& rData, bool /* bBrowse */)
+{
+ // nZoom
+ // nPageNo
+
+ rData = OUString::number(pPreview->GetZoom())
+ + OUStringChar(SC_USERDATA_SEP)
+ + OUString::number(pPreview->GetPageNo());
+}
+
+void ScPreviewShell::ReadUserData(const OUString& rData, bool /* bBrowse */)
+{
+ if (!rData.isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ pPreview->SetZoom(static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rData, 0, SC_USERDATA_SEP, nIndex))));
+ pPreview->SetPageNo(o3tl::toInt32(o3tl::getToken(rData, 0, SC_USERDATA_SEP, nIndex)));
+ eZoom = SvxZoomType::PERCENT;
+ }
+}
+
+void ScPreviewShell::WriteUserDataSequence(uno::Sequence < beans::PropertyValue >& rSeq)
+{
+ // tdf#130559: don't export preview view data if active
+ if (comphelper::IsContextFlagActive("NoPreviewData"))
+ return;
+
+ rSeq.realloc(3);
+ beans::PropertyValue* pSeq = rSeq.getArray();
+ sal_uInt16 nViewID(GetViewFrame().GetCurViewId());
+ pSeq[0].Name = SC_VIEWID;
+ pSeq[0].Value <<= SC_VIEW + OUString::number(nViewID);
+ pSeq[1].Name = SC_ZOOMVALUE;
+ pSeq[1].Value <<= sal_Int32 (pPreview->GetZoom());
+ pSeq[2].Name = "PageNumber";
+ pSeq[2].Value <<= pPreview->GetPageNo();
+
+ // Common SdrModel processing
+ if (ScDrawLayer* pDrawLayer = GetDocument().GetDrawLayer())
+ pDrawLayer->WriteUserDataSequence(rSeq);
+}
+
+void ScPreviewShell::ReadUserDataSequence(const uno::Sequence < beans::PropertyValue >& rSeq)
+{
+ for (const auto& propval : rSeq)
+ {
+ if (propval.Name == SC_ZOOMVALUE)
+ {
+ sal_Int32 nTemp = 0;
+ if (propval.Value >>= nTemp)
+ pPreview->SetZoom(sal_uInt16(nTemp));
+ }
+ else if (propval.Name == "PageNumber")
+ {
+ sal_Int32 nTemp = 0;
+ if (propval.Value >>= nTemp)
+ pPreview->SetPageNo(nTemp);
+ }
+ // Fallback to common SdrModel processing
+ else
+ pDocShell->MakeDrawLayer()->ReadUserDataSequenceValue(&propval);
+ }
+}
+
+void ScPreviewShell::DoScroll( sal_uInt16 nMode )
+{
+ Point aCurPos, aPrevPos;
+
+ tools::Long nHRange = pHorScroll->GetRange().Max();
+ tools::Long nHLine = pHorScroll->GetLineSize();
+ tools::Long nHPage = pHorScroll->GetPageSize();
+ tools::Long nVRange = pVerScroll->GetRange().Max();
+ tools::Long nVLine = pVerScroll->GetLineSize();
+ tools::Long nVPage = pVerScroll->GetPageSize();
+
+ aCurPos.setX( pHorScroll->GetThumbPos() );
+ aCurPos.setY( pVerScroll->GetThumbPos() );
+ aPrevPos = aCurPos;
+
+ tools::Long nThumbPos = pVerScroll->GetThumbPos();
+ tools::Long nRangeMax = pVerScroll->GetRangeMax();
+
+ switch( nMode )
+ {
+ case SID_CURSORUP:
+ if( nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+
+ if( nPage>0 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_PREVIOUS);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ aCurPos.AdjustY( -nVLine );
+ break;
+ case SID_CURSORDOWN:
+ if( nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+
+ // before testing for last page, make sure all page counts are calculated
+ if ( nPage+1 == nTotal && !pPreview->AllTested() )
+ {
+ pPreview->CalcAll();
+ nTotal = pPreview->GetTotalPages();
+ }
+
+ if( nPage<nTotal-1 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_NEXT);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ aCurPos.AdjustY(nVLine );
+ break;
+ case SID_CURSORLEFT:
+ aCurPos.AdjustX( -nHLine );
+ break;
+ case SID_CURSORRIGHT:
+ aCurPos.AdjustX(nHLine );
+ break;
+ case SID_CURSORPAGEUP:
+ if( nThumbPos==0 || nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+
+ if( nPage>0 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_PREVIOUS);
+ Execute( aSfxRequest );
+ aCurPos.setY( nVRange );
+ }
+ }
+ else
+ aCurPos.AdjustY( -nVPage );
+ break;
+ case SID_CURSORPAGEDOWN:
+ if( (std::abs(nVPage+nThumbPos-nRangeMax)<10) || nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+
+ // before testing for last page, make sure all page counts are calculated
+ if ( nPage+1 == nTotal && !pPreview->AllTested() )
+ {
+ pPreview->CalcAll();
+ nTotal = pPreview->GetTotalPages();
+ }
+ if( nPage<nTotal-1 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_NEXT);
+ Execute( aSfxRequest );
+ aCurPos.setY( 0 );
+ }
+ }
+ else
+ aCurPos.AdjustY(nVPage );
+ break;
+ case SID_CURSORHOME:
+ if( nMaxVertPos<0 )
+ {
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if( nTotal && nPage != 0 )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_FIRST);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ {
+ aCurPos.setY( 0 );
+ aCurPos.setX( 0 );
+ }
+ break;
+ case SID_CURSOREND:
+ if( nMaxVertPos<0 )
+ {
+ if( !pPreview->AllTested() )
+ pPreview->CalcAll();
+ tools::Long nPage = pPreview->GetPageNo();
+ tools::Long nTotal = pPreview->GetTotalPages();
+ if( nTotal && nPage+1 != nTotal )
+ {
+ SfxViewFrame& rSfxViewFrame = GetViewFrame();
+ SfxRequest aSfxRequest(rSfxViewFrame, SID_PREVIEW_LAST);
+ Execute( aSfxRequest );
+ }
+ }
+ else
+ {
+ aCurPos.setY( nVRange );
+ aCurPos.setX( nHRange );
+ }
+ break;
+ }
+
+ // nHRange-nHPage might be negative, that's why we check for < 0 afterwards
+
+ if( aCurPos.Y() > (nVRange-nVPage) )
+ aCurPos.setY( nVRange-nVPage );
+ if( aCurPos.Y() < 0 )
+ aCurPos.setY( 0 );
+ if( aCurPos.X() > (nHRange-nHPage) )
+ aCurPos.setX( nHRange-nHPage );
+ if( aCurPos.X() < 0 )
+ aCurPos.setX( 0 );
+
+ if( nMaxVertPos>=0 )
+ {
+ if( aCurPos.Y() != aPrevPos.Y() )
+ {
+ pVerScroll->SetThumbPos( aCurPos.Y() );
+ nPrevVThumbPos = pVerScroll->GetThumbPos();
+ pPreview->SetYOffset( aCurPos.Y() );
+ }
+ }
+
+ if( aCurPos.X() != aPrevPos.X() )
+ {
+ pHorScroll->SetThumbPos( aCurPos.X() );
+ nPrevHThumbPos = pHorScroll->GetThumbPos();
+ pPreview->SetXOffset( aCurPos.X() );
+ }
+
+}
+
+void ScPreviewShell::ExitPreview()
+{
+ GetViewFrame().GetDispatcher()->Execute(SID_VIEWSHELL0, SfxCallMode::ASYNCHRON);
+}
+
+void ScPreviewShell::AddAccessibilityObject( SfxListener& rObject )
+{
+ if (!pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster.reset( new SfxBroadcaster );
+
+ rObject.StartListening( *pAccessibilityBroadcaster );
+}
+
+void ScPreviewShell::RemoveAccessibilityObject( SfxListener& rObject )
+{
+ if (pAccessibilityBroadcaster)
+ rObject.EndListening( *pAccessibilityBroadcaster );
+ else
+ {
+ OSL_FAIL("no accessibility broadcaster?");
+ }
+}
+
+void ScPreviewShell::BroadcastAccessibility( const SfxHint &rHint )
+{
+ if (pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster->Broadcast( rHint );
+}
+
+bool ScPreviewShell::HasAccessibilityObjects() const
+{
+ return pAccessibilityBroadcaster && pAccessibilityBroadcaster->HasListeners();
+}
+
+const ScPreviewLocationData& ScPreviewShell::GetLocationData()
+{
+ return pPreview->GetLocationData();
+}
+
+ScDocument& ScPreviewShell::GetDocument()
+{
+ return pDocShell->GetDocument();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/prevwsh2.cxx b/sc/source/ui/view/prevwsh2.cxx
new file mode 100644
index 0000000000..bb15ac24ad
--- /dev/null
+++ b/sc/source/ui/view/prevwsh2.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdmodel.hxx>
+#include <svl/hint.hxx>
+
+#include <prevwsh.hxx>
+#include <docsh.hxx>
+#include <preview.hxx>
+#include <hints.hxx>
+
+void ScPreviewShell::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ bool bDataChanged = false;
+
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ // SdrHints are no longer used for invalidating, thus react on objectchange instead
+ if(SdrHintKind::ObjectChange == pSdrHint->GetKind())
+ bDataChanged = true;
+ }
+ else if (const ScPaintHint* pPaintHint = dynamic_cast<const ScPaintHint*>(&rHint))
+ {
+ PaintPartFlags nParts = pPaintHint->GetParts();
+ if (nParts & ( PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size ))
+ bDataChanged = true;
+ }
+ else
+ {
+ switch ( rHint.GetId() )
+ {
+ case SfxHintId::ScDataChanged:
+ case SfxHintId::ScPrintOptions:
+ bDataChanged = true;
+ break;
+ case SfxHintId::ScDrawLayerNew:
+ {
+ SfxBroadcaster* pDrawBC = pDocShell->GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ StartListening(*pDrawBC);
+ }
+ break;
+ default: break;
+ }
+ }
+
+ if (bDataChanged)
+ pPreview->DataChanged(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/printfun.cxx b/sc/source/ui/view/printfun.cxx
new file mode 100644
index 0000000000..84e21da022
--- /dev/null
+++ b/sc/source/ui/view/printfun.cxx
@@ -0,0 +1,3204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <printfun.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/editstat.hxx>
+#include <svx/fmview.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/pbinitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <sfx2/printer.hxx>
+#include <tools/multisel.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+
+#include <editutil.hxx>
+#include <docsh.hxx>
+#include <output.hxx>
+#include <viewdata.hxx>
+#include <viewopti.hxx>
+#include <stlpool.hxx>
+#include <pagepar.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <dociter.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <pagedata.hxx>
+#include <printopt.hxx>
+#include <prevloc.hxx>
+#include <scmod.hxx>
+#include <drwlayer.hxx>
+#include <fillinfo.hxx>
+#include <postit.hxx>
+
+#include <memory>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+
+#define ZOOM_MIN 10
+
+namespace{
+
+bool lcl_GetBool(const SfxItemSet* pSet, sal_uInt16 nWhich)
+{
+ return static_cast<const SfxBoolItem&>(pSet->Get(nWhich)).GetValue();
+}
+
+sal_uInt16 lcl_GetUShort(const SfxItemSet* pSet, sal_uInt16 nWhich)
+{
+ return static_cast<const SfxUInt16Item&>(pSet->Get(nWhich)).GetValue();
+}
+
+bool lcl_GetShow(const SfxItemSet* pSet, sal_uInt16 nWhich)
+{
+ return ScVObjMode::VOBJ_MODE_SHOW == static_cast<const ScViewObjectModeItem&>(pSet->Get(nWhich)).GetValue();
+}
+
+
+} // namespace
+
+ScPageRowEntry::ScPageRowEntry(const ScPageRowEntry& r)
+{
+ nStartRow = r.nStartRow;
+ nEndRow = r.nEndRow;
+ nPagesX = r.nPagesX;
+ aHidden = r.aHidden;
+ aHidden.resize(nPagesX, false);
+}
+
+ScPageRowEntry& ScPageRowEntry::operator=(const ScPageRowEntry& r)
+{
+ nStartRow = r.nStartRow;
+ nEndRow = r.nEndRow;
+ nPagesX = r.nPagesX;
+ aHidden = r.aHidden;
+ aHidden.resize(nPagesX, false);
+ return *this;
+}
+
+void ScPageRowEntry::SetPagesX(size_t nNew)
+{
+ nPagesX = nNew;
+ aHidden.resize(nPagesX, false);
+}
+
+void ScPageRowEntry::SetHidden(size_t nX)
+{
+ if ( nX < nPagesX )
+ {
+ if ( nX+1 == nPagesX ) // last page?
+ --nPagesX;
+ else
+ {
+ aHidden.resize(nPagesX, false);
+ aHidden[nX] = true;
+ }
+ }
+}
+
+bool ScPageRowEntry::IsHidden(size_t nX) const
+{
+ return nX >= nPagesX || aHidden[nX]; //! inline?
+}
+
+size_t ScPageRowEntry::CountVisible() const
+{
+ if (!aHidden.empty())
+ {
+ size_t nVis = 0;
+ for (size_t i=0; i<nPagesX; i++)
+ if (!aHidden[i])
+ ++nVis;
+ return nVis;
+ }
+ else
+ return nPagesX;
+}
+
+static tools::Long lcl_LineTotal(const ::editeng::SvxBorderLine* pLine)
+{
+ return pLine ? ( pLine->GetScaledWidth() ) : 0;
+}
+
+void ScPrintFunc::Construct( const ScPrintOptions* pOptions )
+{
+ pDocShell->UpdatePendingRowHeights( nPrintTab );
+
+ SfxPrinter* pDocPrinter = rDoc.GetPrinter(); // use the printer, even for preview
+ if (pDocPrinter)
+ aOldPrinterMode = pDocPrinter->GetMapMode();
+
+ // unified MapMode for all calls (e.g. Repaint!!!)
+ // else, EditEngine outputs different text heights
+ pDev->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ pBorderItem = nullptr;
+ pBackgroundItem = nullptr;
+ pShadowItem = nullptr;
+
+ pEditEngine = nullptr;
+ pEditDefaults = nullptr;
+
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find(
+ rDoc.GetPageStyle( nPrintTab ),
+ SfxStyleFamily::Page );
+ if (pStyleSheet)
+ pParamSet = &pStyleSheet->GetItemSet();
+ else
+ {
+ OSL_FAIL("Template not found" );
+ pParamSet = nullptr;
+ }
+
+ if (!bFromPrintState)
+ nZoom = 100;
+ nManualZoom = 100;
+ bClearWin = false;
+ bUseStyleColor = false;
+ bIsRender = false;
+
+ InitParam(pOptions);
+
+ pPageData = nullptr; // is only needed for initialisation
+}
+
+ScPrintFunc::ScPrintFunc( ScDocShell* pShell, SfxPrinter* pNewPrinter, SCTAB nTab,
+ tools::Long nPage, tools::Long nDocP, const ScRange* pArea,
+ const ScPrintOptions* pOptions,
+ ScPageBreakData* pData )
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( pNewPrinter ),
+ pDrawView ( nullptr ),
+ nPrintTab ( nTab ),
+ nPageStart ( nPage ),
+ nDocPages ( nDocP ),
+ pUserArea ( pArea ),
+ bFromPrintState ( false ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ nTabPages ( 0 ),
+ nTotalPages ( 0 ),
+ bPrintAreaValid ( false ),
+ pPageData ( pData )
+{
+ pDev = pPrinter.get();
+ aSrcOffset = pPrinter->PixelToLogic(pPrinter->GetPageOffsetPixel(), MapMode(MapUnit::Map100thMM));
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ Construct( pOptions );
+}
+
+ScPrintFunc::ScPrintFunc(ScDocShell* pShell, SfxPrinter* pNewPrinter,
+ const ScPrintState& rState, const ScPrintOptions* pOptions)
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( pNewPrinter ),
+ pDrawView ( nullptr ),
+ pUserArea ( nullptr ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ pPageData ( nullptr )
+{
+ pDev = pPrinter.get();
+
+ nPrintTab = rState.nPrintTab;
+ nStartCol = rState.nStartCol;
+ nStartRow = rState.nStartRow;
+ nEndCol = rState.nEndCol;
+ nEndRow = rState.nEndRow;
+ bPrintAreaValid = rState.bPrintAreaValid;
+ nZoom = rState.nZoom;
+ m_aRanges.m_nPagesX = rState.nPagesX;
+ m_aRanges.m_nPagesY = rState.nPagesY;
+ nTabPages = rState.nTabPages;
+ nTotalPages = rState.nTotalPages;
+ nPageStart = rState.nPageStart;
+ nDocPages = rState.nDocPages;
+ bFromPrintState = true;
+
+ if (rState.bSavedStateRanges)
+ {
+ m_aRanges.m_nTotalY = rState.nTotalY;
+ m_aRanges.m_xPageEndX = rState.xPageEndX;
+ m_aRanges.m_xPageEndY = rState.xPageEndY;
+ m_aRanges.m_xPageRows = rState.xPageRows;
+ m_aRanges.m_aInput = rState.aPrintPageRangesInput;
+ }
+ else
+ {
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ }
+
+ aSrcOffset = pPrinter->PixelToLogic(pPrinter->GetPageOffsetPixel(), MapMode(MapUnit::Map100thMM));
+ Construct( pOptions );
+}
+
+ScPrintFunc::ScPrintFunc( OutputDevice* pOutDev, ScDocShell* pShell, SCTAB nTab,
+ tools::Long nPage, tools::Long nDocP, const ScRange* pArea,
+ const ScPrintOptions* pOptions )
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( nullptr ),
+ pDrawView ( nullptr ),
+ nPrintTab ( nTab ),
+ nPageStart ( nPage ),
+ nDocPages ( nDocP ),
+ pUserArea ( pArea ),
+ bFromPrintState ( false ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ nTabPages ( 0 ),
+ nTotalPages ( 0 ),
+ bPrintAreaValid ( false ),
+ pPageData ( nullptr )
+{
+ pDev = pOutDev;
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ Construct( pOptions );
+}
+
+ScPrintFunc::ScPrintFunc( OutputDevice* pOutDev, ScDocShell* pShell,
+ const ScPrintState& rState, const ScPrintOptions* pOptions )
+ : pDocShell ( pShell ),
+ rDoc(pDocShell->GetDocument()),
+ pPrinter ( nullptr ),
+ pDrawView ( nullptr ),
+ pUserArea ( nullptr ),
+ bSourceRangeValid ( false ),
+ bPrintCurrentTable ( false ),
+ bMultiArea ( false ),
+ mbHasPrintRange(true),
+ pPageData ( nullptr )
+{
+ pDev = pOutDev;
+
+ nPrintTab = rState.nPrintTab;
+ nStartCol = rState.nStartCol;
+ nStartRow = rState.nStartRow;
+ nEndCol = rState.nEndCol;
+ nEndRow = rState.nEndRow;
+ bPrintAreaValid = rState.bPrintAreaValid;
+ nZoom = rState.nZoom;
+ m_aRanges.m_nPagesX = rState.nPagesX;
+ m_aRanges.m_nPagesY = rState.nPagesY;
+ nTabPages = rState.nTabPages;
+ nTotalPages = rState.nTotalPages;
+ nPageStart = rState.nPageStart;
+ nDocPages = rState.nDocPages;
+ bFromPrintState = true;
+
+ if (rState.bSavedStateRanges)
+ {
+ m_aRanges.m_nTotalY = rState.nTotalY;
+ m_aRanges.m_xPageEndX = rState.xPageEndX;
+ m_aRanges.m_xPageEndY = rState.xPageEndY;
+ m_aRanges.m_xPageRows = rState.xPageRows;
+ m_aRanges.m_aInput = rState.aPrintPageRangesInput;
+ }
+ else
+ {
+ m_aRanges.m_xPageEndX = std::make_shared<std::vector<SCCOL>>();
+ m_aRanges.m_xPageEndY = std::make_shared<std::vector<SCROW>>();
+ m_aRanges.m_xPageRows = std::make_shared<std::map<size_t, ScPageRowEntry>>();
+ }
+
+ Construct( pOptions );
+}
+
+void ScPrintFunc::GetPrintState(ScPrintState& rState, bool bSavePageRanges)
+{
+ rState.nPrintTab = nPrintTab;
+ rState.nStartCol = nStartCol;
+ rState.nStartRow = nStartRow;
+ rState.nEndCol = nEndCol;
+ rState.nEndRow = nEndRow;
+ rState.bPrintAreaValid = bPrintAreaValid;
+ rState.nZoom = nZoom;
+ rState.nPagesX = m_aRanges.m_nPagesX;
+ rState.nPagesY = m_aRanges.m_nPagesY;
+ rState.nTabPages = nTabPages;
+ rState.nTotalPages = nTotalPages;
+ rState.nPageStart = nPageStart;
+ rState.nDocPages = nDocPages;
+ if (bSavePageRanges)
+ {
+ rState.bSavedStateRanges = true;
+ rState.nTotalY = m_aRanges.m_nTotalY;
+ rState.xPageEndX = m_aRanges.m_xPageEndX;
+ rState.xPageEndY = m_aRanges.m_xPageEndY;
+ rState.xPageRows = m_aRanges.m_xPageRows;
+ rState.aPrintPageRangesInput = m_aRanges.m_aInput;
+ }
+}
+
+bool ScPrintFunc::GetLastSourceRange( ScRange& rRange ) const
+{
+ rRange = aLastSourceRange;
+ return bSourceRangeValid;
+}
+
+void ScPrintFunc::FillPageData()
+{
+ if (!pPageData)
+ return;
+
+ sal_uInt16 nCount = sal::static_int_cast<sal_uInt16>( pPageData->GetCount() );
+ ScPrintRangeData& rData = pPageData->GetData(nCount); // count up
+
+ assert( bPrintAreaValid );
+ rData.SetPrintRange( ScRange( nStartCol, nStartRow, nPrintTab,
+ nEndCol, nEndRow, nPrintTab ) );
+ // #i123672#
+ if(m_aRanges.m_xPageEndX->empty())
+ {
+ OSL_ENSURE(false, "vector access error for maPageEndX (!)");
+ }
+ else
+ {
+ rData.SetPagesX( m_aRanges.m_nPagesX, m_aRanges.m_xPageEndX->data());
+ }
+
+ // #i123672#
+ if(m_aRanges.m_xPageEndY->empty())
+ {
+ OSL_ENSURE(false, "vector access error for maPageEndY (!)");
+ }
+ else
+ {
+ rData.SetPagesY( m_aRanges.m_nTotalY, m_aRanges.m_xPageEndY->data());
+ }
+
+ // Settings
+ rData.SetTopDown( aTableParam.bTopDown );
+ rData.SetAutomatic( !aAreaParam.bPrintArea );
+}
+
+ScPrintFunc::~ScPrintFunc()
+{
+ pEditDefaults.reset();
+ pEditEngine.reset();
+
+ // Printer settings are now restored from outside
+
+ // For DrawingLayer/Charts, the MapMode of the printer (RefDevice) must always be correct
+ SfxPrinter* pDocPrinter = rDoc.GetPrinter(); // use Preview also for the printer
+ if (pDocPrinter)
+ pDocPrinter->SetMapMode(aOldPrinterMode);
+}
+
+void ScPrintFunc::SetDrawView( FmFormView* pNew )
+{
+ pDrawView = pNew;
+}
+
+static void lcl_HidePrint( const ScTableInfo& rTabInfo, SCCOL nX1, SCCOL nX2 )
+{
+ for (SCSIZE nArrY=1; nArrY+1<rTabInfo.mnArrCount; nArrY++)
+ {
+ RowInfo* pThisRowInfo = &rTabInfo.mpRowInfo[nArrY];
+ for (SCCOL nX=nX1; nX<=nX2; nX++)
+ {
+ ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nX);
+ ScBasicCellInfo& rBasicCellInfo = pThisRowInfo->basicCellInfo(nX);
+ if (!rBasicCellInfo.bEmptyCellText)
+ if (rCellInfo.pPatternAttr->
+ GetItem(ATTR_PROTECTION, rCellInfo.pConditionSet).GetHidePrint())
+ {
+ rCellInfo.maCell.clear();
+ rBasicCellInfo.bEmptyCellText = true;
+ }
+ }
+ }
+}
+
+// output to Device (static)
+//
+// us used for:
+// - Clipboard/Bitmap
+// - Ole-Object (DocShell::Draw)
+// - Preview of templates
+
+void ScPrintFunc::DrawToDev(ScDocument& rDoc, OutputDevice* pDev, double /* nPrintFactor */,
+ const tools::Rectangle& rBound, ScViewData* pViewData, bool bMetaFile)
+{
+ if (rDoc.GetMaxTableNumber() < 0)
+ return;
+
+ //! evaluate nPrintFactor !!!
+
+ SCTAB nTab = 0;
+ if (pViewData)
+ nTab = pViewData->GetTabNo();
+
+ bool bDoGrid, bNullVal, bFormula;
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( rDoc.GetPageStyle( nTab ), SfxStyleFamily::Page );
+ if (pStyleSheet)
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ bDoGrid = rSet.Get(ATTR_PAGE_GRID).GetValue();
+ bNullVal = rSet.Get(ATTR_PAGE_NULLVALS).GetValue();
+ bFormula = rSet.Get(ATTR_PAGE_FORMULAS).GetValue();
+ }
+ else
+ {
+ const ScViewOptions& rOpt = rDoc.GetViewOptions();
+ bDoGrid = rOpt.GetOption(VOPT_GRID);
+ bNullVal = rOpt.GetOption(VOPT_NULLVALS);
+ bFormula = rOpt.GetOption(VOPT_FORMULAS);
+ }
+
+ MapMode aMode = pDev->GetMapMode();
+
+ tools::Rectangle aRect = rBound;
+
+ if (aRect.Right() < aRect.Left() || aRect.Bottom() < aRect.Top())
+ aRect = tools::Rectangle( Point(), pDev->GetOutputSize() );
+
+ SCCOL nX1 = 0;
+ SCROW nY1 = 0;
+ SCCOL nX2 = OLE_STD_CELLS_X - 1;
+ SCROW nY2 = OLE_STD_CELLS_Y - 1;
+ if (bMetaFile)
+ {
+ ScRange aRange = rDoc.GetRange( nTab, rBound );
+ nX1 = aRange.aStart.Col();
+ nY1 = aRange.aStart.Row();
+ nX2 = aRange.aEnd.Col();
+ nY2 = aRange.aEnd.Row();
+ }
+ else if (pViewData)
+ {
+ ScSplitPos eWhich = pViewData->GetActivePart();
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ ScVSplitPos eVWhich = WhichV(eWhich);
+ nX1 = pViewData->GetPosX(eHWhich);
+ nY1 = pViewData->GetPosY(eVWhich);
+ nX2 = nX1 + pViewData->VisibleCellsX(eHWhich);
+ if (nX2>nX1) --nX2;
+ nY2 = nY1 + pViewData->VisibleCellsY(eVWhich);
+ if (nY2>nY1) --nY2;
+ }
+
+ if (nX1 > rDoc.MaxCol()) nX1 = rDoc.MaxCol();
+ if (nX2 > rDoc.MaxCol()) nX2 = rDoc.MaxCol();
+ if (nY1 > rDoc.MaxRow()) nY1 = rDoc.MaxRow();
+ if (nY2 > rDoc.MaxRow()) nY2 = rDoc.MaxRow();
+
+ tools::Long nDevSizeX = aRect.Right()-aRect.Left()+1;
+ tools::Long nDevSizeY = aRect.Bottom()-aRect.Top()+1;
+
+ tools::Long nTwipsSizeX = 0;
+ for (SCCOL i=nX1; i<=nX2; i++)
+ nTwipsSizeX += rDoc.GetColWidth( i, nTab );
+ tools::Long nTwipsSizeY = rDoc.GetRowHeight( nY1, nY2, nTab );
+
+ // if no lines, still space for the outline frame (20 Twips = 1pt)
+ // (HasLines initializes aLines to 0,0,0,0)
+ nTwipsSizeX += 20;
+ nTwipsSizeY += 20;
+
+ double nScaleX = static_cast<double>(nDevSizeX) / nTwipsSizeX;
+ double nScaleY = static_cast<double>(nDevSizeY) / nTwipsSizeY;
+
+ //! hand over Flag at FillInfo !!!!!
+ ScRange aERange;
+ bool bEmbed = rDoc.IsEmbedded();
+ if (bEmbed)
+ {
+ rDoc.GetEmbedded(aERange);
+ rDoc.ResetEmbedded();
+ }
+
+ // Assemble data
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nTab,
+ nScaleX, nScaleY, false, bFormula );
+ lcl_HidePrint( aTabInfo, nX1, nX2 );
+
+ if (bEmbed)
+ rDoc.SetEmbedded(aERange);
+
+ tools::Long nScrX = aRect.Left();
+ tools::Long nScrY = aRect.Top();
+
+ // If no lines, still leave space for grid lines
+ // (would be elseways cut away)
+ nScrX += 1;
+ nScrY += 1;
+
+ ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, &rDoc, nTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nScaleX, nScaleY );
+ aOutputData.SetMetaFileMode(bMetaFile);
+ aOutputData.SetShowNullValues(bNullVal);
+ aOutputData.SetShowFormulas(bFormula);
+
+ ScDrawLayer* pModel = rDoc.GetDrawLayer();
+ std::unique_ptr<FmFormView> pDrawView;
+
+ if( pModel )
+ {
+ pDrawView.reset(
+ new FmFormView(
+ *pModel,
+ pDev));
+ pDrawView->ShowSdrPage(pDrawView->GetModel().GetPage(nTab));
+ pDrawView->SetPrintPreview();
+ aOutputData.SetDrawView( pDrawView.get() );
+ }
+
+ //! SetUseStyleColor ??
+
+ if ( bMetaFile && pDev->IsVirtual() )
+ aOutputData.SetSnapPixel();
+
+ Point aLogStart = pDev->PixelToLogic(Point(nScrX, nScrY), MapMode(MapUnit::Map100thMM));
+ tools::Long nLogStX = aLogStart.X();
+ tools::Long nLogStY = aLogStart.Y();
+
+ //! nZoom for GetFont in OutputData ???
+
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(pViewData->GetLogicMode(pViewData->GetActivePart()));
+
+ // #i72502#
+ const Point aMMOffset(aOutputData.PrePrintDrawingLayer(nLogStX, nLogStY));
+ aOutputData.PrintDrawingLayer(SC_LAYER_BACK, aMMOffset);
+
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(aMode);
+
+ aOutputData.DrawBackground(*pDev);
+
+ aOutputData.DrawShadow();
+ aOutputData.DrawFrame(*pDev);
+ aOutputData.DrawSparklines(*pDev);
+ aOutputData.DrawStrings();
+
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(pViewData->GetLogicMode(pViewData->GetActivePart()));
+
+ aOutputData.DrawEdit(!bMetaFile);
+
+ if (bDoGrid)
+ {
+ if (!bMetaFile && pViewData)
+ pDev->SetMapMode(aMode);
+
+ aOutputData.DrawGrid(*pDev, true, false); // no page breaks
+
+ pDev->SetLineColor( COL_BLACK );
+
+ Size aOne = pDev->PixelToLogic( Size(1,1) );
+ if (bMetaFile)
+ aOne = Size(1,1); // compatible with DrawGrid
+ tools::Long nRight = nScrX + aOutputData.GetScrW() - aOne.Width();
+ tools::Long nBottom = nScrY + aOutputData.GetScrH() - aOne.Height();
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ // extra line at the left edge for left-to-right, right for right-to-left
+ if ( bLayoutRTL )
+ pDev->DrawLine( Point(nRight,nScrY), Point(nRight,nBottom) );
+ else
+ pDev->DrawLine( Point(nScrX,nScrY), Point(nScrX,nBottom) );
+ // extra line at the top in both cases
+ pDev->DrawLine( Point(nScrX,nScrY), Point(nRight,nScrY) );
+ }
+
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_FRONT, aMMOffset);
+ aOutputData.PrintDrawingLayer(SC_LAYER_INTERN, aMMOffset);
+ aOutputData.PostPrintDrawingLayer(aMMOffset); // #i74768#
+}
+
+// Printing
+
+static void lcl_FillHFParam( ScPrintHFParam& rParam, const SfxItemSet* pHFSet )
+{
+ // nDistance must be initialized differently before
+
+ if ( pHFSet == nullptr )
+ {
+ rParam.bEnable = false;
+ rParam.pBorder = nullptr;
+ rParam.pBack = nullptr;
+ rParam.pShadow = nullptr;
+ }
+ else
+ {
+ rParam.bEnable = pHFSet->Get(ATTR_PAGE_ON).GetValue();
+ rParam.bDynamic = pHFSet->Get(ATTR_PAGE_DYNAMIC).GetValue();
+ rParam.bShared = pHFSet->Get(ATTR_PAGE_SHARED).GetValue();
+ rParam.bSharedFirst = pHFSet->Get(ATTR_PAGE_SHARED_FIRST).GetValue();
+ rParam.nHeight = pHFSet->Get(ATTR_PAGE_SIZE).GetSize().Height();
+ const SvxLRSpaceItem* pHFLR = &pHFSet->Get(ATTR_LRSPACE);
+ tools::Long nTmp;
+ nTmp = pHFLR->GetLeft();
+ rParam.nLeft = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ nTmp = pHFLR->GetRight();
+ rParam.nRight = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ rParam.pBorder = &pHFSet->Get(ATTR_BORDER);
+ rParam.pBack = &pHFSet->Get(ATTR_BACKGROUND);
+ rParam.pShadow = &pHFSet->Get(ATTR_SHADOW);
+
+// now back in the dialog:
+// rParam.nHeight += rParam.nDistance; // not in the dialog any more ???
+
+ rParam.nHeight += lcl_LineTotal( rParam.pBorder->GetTop() ) +
+ lcl_LineTotal( rParam.pBorder->GetBottom() );
+
+ rParam.nManHeight = rParam.nHeight;
+ }
+
+ if (!rParam.bEnable)
+ rParam.nHeight = 0;
+}
+
+// bNew = TRUE: search for used part of the document
+// bNew = FALSE: only limit whole lines/columns
+
+bool ScPrintFunc::AdjustPrintArea( bool bNew )
+{
+ SCCOL nOldEndCol = nEndCol; // only important for !bNew
+ SCROW nOldEndRow = nEndRow;
+ bool bChangeCol = true; // at bNew both are being adjusted
+ bool bChangeRow = true;
+
+ bool bNotes = aTableParam.bNotes;
+ if ( bNew )
+ {
+ nStartCol = 0;
+ nStartRow = 0;
+ if (!rDoc.GetPrintArea( nPrintTab, nEndCol, nEndRow, bNotes ))
+ return false; // nothing
+ bPrintAreaValid = true;
+ }
+ else
+ {
+ bool bFound = true;
+ bChangeCol = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
+ bChangeRow = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
+ bool bForcedChangeRow = false;
+
+ // #i53558# Crop entire column of old row limit to real print area with
+ // some fuzzyness.
+ if (!bChangeRow && nStartRow == 0)
+ {
+ SCROW nPAEndRow;
+ bFound = rDoc.GetPrintAreaVer( nPrintTab, nStartCol, nEndCol, nPAEndRow, bNotes );
+ // Say we don't want to print more than ~1000 empty rows, which are
+ // about 14 pages intentionally left blank...
+ const SCROW nFuzzy = 23*42;
+ if (nPAEndRow + nFuzzy < nEndRow)
+ {
+ bForcedChangeRow = true;
+ nEndRow = nPAEndRow;
+ }
+ else
+ bFound = true; // user seems to _want_ to print some empty rows
+ }
+ // TODO: in case we extend the number of columns we may have to do the
+ // same for horizontal cropping.
+
+ if ( bChangeCol && bChangeRow )
+ bFound = rDoc.GetPrintArea( nPrintTab, nEndCol, nEndRow, bNotes );
+ else if ( bChangeCol )
+ bFound = rDoc.GetPrintAreaHor( nPrintTab, nStartRow, nEndRow, nEndCol );
+ else if ( bChangeRow )
+ bFound = rDoc.GetPrintAreaVer( nPrintTab, nStartCol, nEndCol, nEndRow, bNotes );
+
+ if (!bFound)
+ return false; // empty
+
+ bPrintAreaValid = true;
+ if (bForcedChangeRow)
+ bChangeRow = true;
+ }
+
+ assert( bPrintAreaValid );
+ rDoc.ExtendMerge( nStartCol,nStartRow, nEndCol,nEndRow, nPrintTab ); // no Refresh, incl. Attrs
+
+ if ( bChangeCol )
+ {
+ OutputDevice* pRefDev = rDoc.GetPrinter(); // use the printer also for Preview
+ pRefDev->SetMapMode(MapMode(MapUnit::MapPixel)); // important for GetNeededSize
+
+ rDoc.ExtendPrintArea( pRefDev,
+ nPrintTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ // changing nEndCol
+ }
+
+ if ( nEndCol < rDoc.MaxCol() && rDoc.HasAttrib(
+ nEndCol,nStartRow,nPrintTab, nEndCol,nEndRow,nPrintTab, HasAttrFlags::ShadowRight ) )
+ ++nEndCol;
+ if ( nEndRow < rDoc.MaxRow() && rDoc.HasAttrib(
+ nStartCol,nEndRow,nPrintTab, nEndCol,nEndRow,nPrintTab, HasAttrFlags::ShadowDown ) )
+ ++nEndRow;
+
+ if (!bChangeCol) nEndCol = nOldEndCol;
+ if (!bChangeRow) nEndRow = nOldEndRow;
+
+ return true;
+}
+
+tools::Long ScPrintFunc::TextHeight( const EditTextObject* pObject )
+{
+ if (!pObject)
+ return 0;
+
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+
+ return static_cast<tools::Long>(pEditEngine->GetTextHeight());
+}
+
+// nZoom must be set !!!
+// and the respective Twip-MapMode configured
+
+void ScPrintFunc::UpdateHFHeight( ScPrintHFParam& rParam )
+{
+ OSL_ENSURE( aPageSize.Width(), "UpdateHFHeight without aPageSize");
+
+ if (!(rParam.bEnable && rParam.bDynamic))
+ return;
+
+ // calculate nHeight from content
+
+ MakeEditEngine();
+ tools::Long nPaperWidth = ( aPageSize.Width() - nLeftMargin - nRightMargin -
+ rParam.nLeft - rParam.nRight ) * 100 / nZoom;
+ if (rParam.pBorder)
+ nPaperWidth -= ( rParam.pBorder->GetDistance(SvxBoxItemLine::LEFT) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::RIGHT) +
+ lcl_LineTotal(rParam.pBorder->GetLeft()) +
+ lcl_LineTotal(rParam.pBorder->GetRight()) ) * 100 / nZoom;
+
+ if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
+ nPaperWidth -= ( rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT) +
+ rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT) ) * 100 / nZoom;
+
+ pEditEngine->SetPaperSize( Size( nPaperWidth, 10000 ) );
+
+ tools::Long nMaxHeight = 0;
+ if ( rParam.pLeft )
+ {
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pLeft->GetRightArea() ) );
+ }
+ if ( rParam.pRight )
+ {
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pRight->GetRightArea() ) );
+ }
+ if ( rParam.pFirst )
+ {
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( rParam.pFirst->GetRightArea() ) );
+ }
+
+ rParam.nHeight = nMaxHeight + rParam.nDistance;
+ if (rParam.pBorder)
+ rParam.nHeight += rParam.pBorder->GetDistance(SvxBoxItemLine::TOP) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM) +
+ lcl_LineTotal( rParam.pBorder->GetTop() ) +
+ lcl_LineTotal( rParam.pBorder->GetBottom() );
+ if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
+ rParam.nHeight += rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+
+ if (rParam.nHeight < rParam.nManHeight)
+ rParam.nHeight = rParam.nManHeight; // configured minimum
+}
+
+void ScPrintFunc::InitParam( const ScPrintOptions* pOptions )
+{
+ if (!pParamSet)
+ return;
+
+ // TabPage "Page"
+ const SvxLRSpaceItem* pLRItem = &pParamSet->Get( ATTR_LRSPACE );
+ tools::Long nTmp;
+ nTmp = pLRItem->GetLeft();
+ nLeftMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ nTmp = pLRItem->GetRight();
+ nRightMargin = nTmp < 0 ? 0 : sal_uInt16(nTmp);
+ const SvxULSpaceItem* pULItem = &pParamSet->Get( ATTR_ULSPACE );
+ nTopMargin = pULItem->GetUpper();
+ nBottomMargin = pULItem->GetLower();
+
+ const SvxPageItem* pPageItem = &pParamSet->Get( ATTR_PAGE );
+ nPageUsage = pPageItem->GetPageUsage();
+ bLandscape = pPageItem->IsLandscape();
+ aFieldData.eNumType = pPageItem->GetNumType();
+
+ bCenterHor = pParamSet->Get(ATTR_PAGE_HORCENTER).GetValue();
+ bCenterVer = pParamSet->Get(ATTR_PAGE_VERCENTER).GetValue();
+
+ aPageSize = pParamSet->Get(ATTR_PAGE_SIZE).GetSize();
+ if ( !aPageSize.Width() || !aPageSize.Height() )
+ {
+ OSL_FAIL("PageSize Null ?!?!?");
+ aPageSize = SvxPaperInfo::GetPaperSize( PAPER_A4 );
+ }
+
+ pBorderItem = &pParamSet->Get(ATTR_BORDER);
+ pBackgroundItem = &pParamSet->Get(ATTR_BACKGROUND);
+ pShadowItem = &pParamSet->Get(ATTR_SHADOW);
+
+ // TabPage "Headline"
+
+ aHdr.pLeft = &pParamSet->Get(ATTR_PAGE_HEADERLEFT); // Content
+ aHdr.pRight = &pParamSet->Get(ATTR_PAGE_HEADERRIGHT);
+ aHdr.pFirst = &pParamSet->Get(ATTR_PAGE_HEADERFIRST);
+
+ const SfxItemSet* pHeaderSet = nullptr;
+ if ( const SvxSetItem* pHeaderSetItem = pParamSet->GetItemIfSet( ATTR_PAGE_HEADERSET, false ) )
+ {
+ pHeaderSet = &pHeaderSetItem->GetItemSet();
+ // Headline has space below
+ aHdr.nDistance = pHeaderSet->Get(ATTR_ULSPACE).GetLower();
+ }
+ lcl_FillHFParam( aHdr, pHeaderSet );
+
+ // TabPage "Footline"
+
+ aFtr.pLeft = &pParamSet->Get(ATTR_PAGE_FOOTERLEFT); // Content
+ aFtr.pRight = &pParamSet->Get(ATTR_PAGE_FOOTERRIGHT);
+ aFtr.pFirst = &pParamSet->Get(ATTR_PAGE_FOOTERFIRST);
+
+ const SfxItemSet* pFooterSet = nullptr;
+ if ( const SvxSetItem* pFooterSetItem = pParamSet->GetItemIfSet( ATTR_PAGE_FOOTERSET, false ) )
+ {
+ pFooterSet = &pFooterSetItem->GetItemSet();
+ // Footline has space above
+ aFtr.nDistance = pFooterSet->Get(ATTR_ULSPACE).GetUpper();
+ }
+ lcl_FillHFParam( aFtr, pFooterSet );
+
+ // Compile Table-/Area-Params from single Items
+
+ // TabPage "Table"
+
+ const SfxUInt16Item* pScaleItem = nullptr;
+ const ScPageScaleToItem* pScaleToItem = nullptr;
+ const SfxUInt16Item* pScaleToPagesItem = nullptr;
+ SfxItemState eState;
+
+ eState = pParamSet->GetItemState( ATTR_PAGE_SCALE, false,
+ reinterpret_cast<const SfxPoolItem**>(&pScaleItem) );
+ if ( SfxItemState::DEFAULT == eState )
+ pScaleItem = &pParamSet->GetPool()->GetDefaultItem( ATTR_PAGE_SCALE );
+
+ eState = pParamSet->GetItemState( ATTR_PAGE_SCALETO, false,
+ reinterpret_cast<const SfxPoolItem**>(&pScaleToItem) );
+ if ( SfxItemState::DEFAULT == eState )
+ pScaleToItem = &pParamSet->GetPool()->GetDefaultItem( ATTR_PAGE_SCALETO );
+
+ eState = pParamSet->GetItemState( ATTR_PAGE_SCALETOPAGES, false,
+ reinterpret_cast<const SfxPoolItem**>(&pScaleToPagesItem) );
+ if ( SfxItemState::DEFAULT == eState )
+ pScaleToPagesItem = &pParamSet->GetPool()->GetDefaultItem( ATTR_PAGE_SCALETOPAGES );
+
+ OSL_ENSURE( pScaleItem && pScaleToItem && pScaleToPagesItem, "Missing ScaleItem! :-/" );
+
+ aTableParam.bCellContent = true;
+ aTableParam.bNotes = lcl_GetBool(pParamSet,ATTR_PAGE_NOTES);
+ aTableParam.bGrid = lcl_GetBool(pParamSet,ATTR_PAGE_GRID);
+ aTableParam.bHeaders = lcl_GetBool(pParamSet,ATTR_PAGE_HEADERS);
+ aTableParam.bFormulas = lcl_GetBool(pParamSet,ATTR_PAGE_FORMULAS);
+ aTableParam.bNullVals = lcl_GetBool(pParamSet,ATTR_PAGE_NULLVALS);
+ aTableParam.bCharts = lcl_GetShow(pParamSet,ATTR_PAGE_CHARTS);
+ aTableParam.bObjects = lcl_GetShow(pParamSet,ATTR_PAGE_OBJECTS);
+ aTableParam.bDrawings = lcl_GetShow(pParamSet,ATTR_PAGE_DRAWINGS);
+ aTableParam.bTopDown = lcl_GetBool(pParamSet,ATTR_PAGE_TOPDOWN);
+ aTableParam.bLeftRight = !aTableParam.bLeftRight;
+ aTableParam.nFirstPageNo = lcl_GetUShort(pParamSet,ATTR_PAGE_FIRSTPAGENO);
+ if (!aTableParam.nFirstPageNo)
+ aTableParam.nFirstPageNo = static_cast<sal_uInt16>(nPageStart); // from previous table
+
+ if ( pScaleItem && pScaleToItem && pScaleToPagesItem )
+ {
+ sal_uInt16 nScaleAll = pScaleItem->GetValue();
+ sal_uInt16 nScaleToPages = pScaleToPagesItem->GetValue();
+
+ aTableParam.bScaleNone = (nScaleAll == 100);
+ aTableParam.bScaleAll = (nScaleAll > 0 );
+ aTableParam.bScaleTo = pScaleToItem->IsValid();
+ aTableParam.bScalePageNum = (nScaleToPages > 0 );
+ aTableParam.nScaleAll = nScaleAll;
+ aTableParam.nScaleWidth = pScaleToItem->GetWidth();
+ aTableParam.nScaleHeight = pScaleToItem->GetHeight();
+ aTableParam.nScalePageNum = nScaleToPages;
+ }
+ else
+ {
+ aTableParam.bScaleNone = true;
+ aTableParam.bScaleAll = false;
+ aTableParam.bScaleTo = false;
+ aTableParam.bScalePageNum = false;
+ aTableParam.nScaleAll = 0;
+ aTableParam.nScaleWidth = 0;
+ aTableParam.nScaleHeight = 0;
+ aTableParam.nScalePageNum = 0;
+ }
+
+ // skip empty pages only if options with that flag are passed
+ aTableParam.bSkipEmpty = pOptions && pOptions->GetSkipEmpty();
+ if ( pPageData )
+ aTableParam.bSkipEmpty = false;
+ // If pPageData is set, only the breaks are interesting for the
+ // pagebreak preview, empty pages are not addressed separately.
+
+ aTableParam.bForceBreaks = pOptions && pOptions->GetForceBreaks();
+
+ // TabPage "Parts":
+
+ //! walk through all PrintAreas of the table !!!
+ const ScRange* pPrintArea = rDoc.GetPrintRange( nPrintTab, 0 );
+ std::optional<ScRange> oRepeatCol = rDoc.GetRepeatColRange( nPrintTab );
+ std::optional<ScRange> oRepeatRow = rDoc.GetRepeatRowRange( nPrintTab );
+
+ // ignoring ATTR_PAGE_PRINTTABLES
+
+ bool bHasPrintRange = rDoc.HasPrintRange();
+ sal_uInt16 nPrintRangeCount = rDoc.GetPrintRangeCount(nPrintTab);
+ bool bPrintEntireSheet = rDoc.IsPrintEntireSheet(nPrintTab);
+
+ if (!bPrintEntireSheet && !nPrintRangeCount)
+ mbHasPrintRange = false;
+
+ if ( pUserArea ) // UserArea (selection) has priority
+ {
+ bPrintCurrentTable =
+ aAreaParam.bPrintArea = true; // Selection
+ aAreaParam.aPrintArea = *pUserArea;
+
+ // The table-query is already in DocShell::Print, here always
+ aAreaParam.aPrintArea.aStart.SetTab(nPrintTab);
+ aAreaParam.aPrintArea.aEnd.SetTab(nPrintTab);
+ }
+ else if (bHasPrintRange)
+ {
+ if ( pPrintArea ) // at least one set?
+ {
+ bPrintCurrentTable =
+ aAreaParam.bPrintArea = true;
+ aAreaParam.aPrintArea = *pPrintArea;
+
+ bMultiArea = nPrintRangeCount > 1;
+ }
+ else
+ {
+ // do not print hidden sheets with "Print entire sheet" flag
+ bPrintCurrentTable = rDoc.IsPrintEntireSheet( nPrintTab ) && rDoc.IsVisible( nPrintTab );
+ aAreaParam.bPrintArea = !bPrintCurrentTable; // otherwise the table is always counted
+ }
+ }
+ else
+ {
+ // don't print hidden tables if there's no print range defined there
+ if ( rDoc.IsVisible( nPrintTab ) )
+ {
+ aAreaParam.bPrintArea = false;
+ bPrintCurrentTable = true;
+ }
+ else
+ {
+ aAreaParam.bPrintArea = true; // otherwise the table is always counted
+ bPrintCurrentTable = false;
+ }
+ }
+
+ if ( oRepeatCol )
+ {
+ aAreaParam.bRepeatCol = true;
+ nRepeatStartCol = oRepeatCol->aStart.Col();
+ nRepeatEndCol = oRepeatCol->aEnd .Col();
+ }
+ else
+ {
+ aAreaParam.bRepeatCol = false;
+ nRepeatStartCol = nRepeatEndCol = SCCOL_REPEAT_NONE;
+ }
+
+ if ( oRepeatRow )
+ {
+ aAreaParam.bRepeatRow = true;
+ nRepeatStartRow = oRepeatRow->aStart.Row();
+ nRepeatEndRow = oRepeatRow->aEnd .Row();
+ }
+ else
+ {
+ aAreaParam.bRepeatRow = false;
+ nRepeatStartRow = nRepeatEndRow = SCROW_REPEAT_NONE;
+ }
+
+ // Split pages
+
+ if (!bPrintAreaValid)
+ {
+ nTabPages = CountPages(); // also calculates zoom
+ nTotalPages = nTabPages;
+ nTotalPages += CountNotePages();
+ }
+ else
+ {
+ CalcPages(); // search breaks only
+ CountNotePages(); // Count notes, even if number of pages is already known
+ }
+
+ if (nDocPages)
+ aFieldData.nTotalPages = nDocPages;
+ else
+ aFieldData.nTotalPages = nTotalPages;
+
+ SetDateTime( DateTime( DateTime::SYSTEM ) );
+
+ if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
+ aFieldData.aTitle = pDocShell->getDocProperties()->getTitle();
+ else
+ aFieldData.aTitle = pDocShell->GetTitle();
+
+ const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
+ aFieldData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if ( !aFieldData.aLongDocName.isEmpty() )
+ aFieldData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ aFieldData.aShortDocName = aFieldData.aLongDocName = aFieldData.aTitle;
+
+ // Printer settings (Orientation, Paper) at DoPrint
+}
+
+Size ScPrintFunc::GetDataSize() const
+{
+ Size aSize = aPageSize;
+ aSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
+ aSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
+ aSize.AdjustHeight( -(aHdr.nHeight + aFtr.nHeight) );
+ return aSize;
+}
+
+void ScPrintFunc::GetScaleData( Size& rPhysSize, tools::Long& rDocHdr, tools::Long& rDocFtr )
+{
+ rPhysSize = aPageSize;
+ rPhysSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
+ rPhysSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
+
+ rDocHdr = aHdr.nHeight;
+ rDocFtr = aFtr.nHeight;
+}
+
+void ScPrintFunc::SetDateTime( const DateTime& rDateTime )
+{
+ aFieldData.aDateTime = rDateTime;
+}
+
+static void lcl_DrawGraphic( const Graphic &rGraphic, vcl::RenderContext& rOutDev,
+ const tools::Rectangle &rGrf, const tools::Rectangle &rOut )
+{
+ const bool bNotInside = !rOut.Contains( rGrf );
+ if ( bNotInside )
+ {
+ rOutDev.Push();
+ rOutDev.IntersectClipRegion( rOut );
+ }
+
+ rGraphic.Draw(rOutDev, rGrf.TopLeft(), rGrf.GetSize());
+
+ if ( bNotInside )
+ rOutDev.Pop();
+}
+
+static void lcl_DrawGraphic( const SvxBrushItem &rBrush, vcl::RenderContext& rOutDev, const OutputDevice* pRefDev,
+ const tools::Rectangle &rOrg, const tools::Rectangle &rOut,
+ OUString const & referer )
+{
+ Size aGrfSize(0,0);
+ const Graphic *pGraphic = rBrush.GetGraphic(referer);
+ SvxGraphicPosition ePos;
+ if ( pGraphic && pGraphic->IsSupportedGraphic() )
+ {
+ const MapMode aMapMM( MapUnit::Map100thMM );
+ if ( pGraphic->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
+ aGrfSize = pRefDev->PixelToLogic( pGraphic->GetPrefSize(), aMapMM );
+ else
+ aGrfSize = OutputDevice::LogicToLogic( pGraphic->GetPrefSize(),
+ pGraphic->GetPrefMapMode(), aMapMM );
+ ePos = rBrush.GetGraphicPos();
+ }
+ else
+ ePos = GPOS_NONE;
+
+ Point aPos;
+ Size aDrawSize = aGrfSize;
+
+ bool bDraw = true;
+ switch ( ePos )
+ {
+ case GPOS_LT: aPos = rOrg.TopLeft();
+ break;
+ case GPOS_MT: aPos.setY( rOrg.Top() );
+ aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
+ break;
+ case GPOS_RT: aPos.setY( rOrg.Top() );
+ aPos.setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
+ aPos.setX( rOrg.Left() );
+ break;
+ case GPOS_MM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
+ aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
+ break;
+ case GPOS_RM: aPos.setY( rOrg.Top() + rOrg.GetSize().Height()/2 - aGrfSize.Height()/2 );
+ aPos.setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
+ aPos.setX( rOrg.Left() );
+ break;
+ case GPOS_MB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
+ aPos.setX( rOrg.Left() + rOrg.GetSize().Width()/2 - aGrfSize.Width()/2 );
+ break;
+ case GPOS_RB: aPos.setY( rOrg.Bottom() - aGrfSize.Height() );
+ aPos.setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_AREA:
+ aPos = rOrg.TopLeft();
+ aDrawSize = rOrg.GetSize();
+ break;
+ case GPOS_TILED:
+ {
+ // use GraphicObject::DrawTiled instead of an own loop
+ // (pixel rounding is handled correctly, and a very small bitmap
+ // is duplicated into a bigger one for better performance)
+
+ GraphicObject aObject( *pGraphic );
+
+ if( rOutDev.GetOutDevType() == OUTDEV_PDF &&
+ (aObject.GetType() == GraphicType::Bitmap || aObject.GetType() == GraphicType::Default) )
+ {
+ // For PDF export, every draw
+ // operation for bitmaps takes a noticeable
+ // amount of place (~50 characters). Thus,
+ // optimize between tile bitmap size and
+ // number of drawing operations here.
+ //
+ // A_out
+ // n_chars = k1 * ---------- + k2 * A_bitmap
+ // A_bitmap
+ //
+ // minimum n_chars is obtained for (derive for
+ // A_bitmap, set to 0, take positive
+ // solution):
+ // k1
+ // A_bitmap = Sqrt( ---- A_out )
+ // k2
+ //
+ // where k1 is the number of chars per draw
+ // operation, and k2 is the number of chars
+ // per bitmap pixel. This is approximately 50
+ // and 7 for current PDF writer, respectively.
+
+ const double k1( 50 );
+ const double k2( 7 );
+ const Size aSize( rOrg.GetSize() );
+ const double Abitmap( k1/k2 * aSize.Width()*aSize.Height() );
+
+ aObject.DrawTiled( rOutDev, rOrg, aGrfSize, Size(0,0),
+ ::std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
+ }
+ else
+ {
+ aObject.DrawTiled( rOutDev, rOrg, aGrfSize, Size(0,0) );
+ }
+
+ bDraw = false;
+ }
+ break;
+
+ case GPOS_NONE:
+ bDraw = false;
+ break;
+
+ default: OSL_ENSURE( false, "new Graphic position?" );
+ }
+ tools::Rectangle aGrf( aPos,aDrawSize );
+ if ( bDraw && aGrf.Overlaps( rOut ) )
+ {
+ lcl_DrawGraphic( *pGraphic, rOutDev, aGrf, rOut );
+ }
+}
+
+// The frame is drawn inwards
+
+void ScPrintFunc::DrawBorder( tools::Long nScrX, tools::Long nScrY, tools::Long nScrW, tools::Long nScrH,
+ const SvxBoxItem* pBorderData, const SvxBrushItem* pBackground,
+ const SvxShadowItem* pShadow )
+{
+ //! direct output from SvxBoxItem !!!
+
+ if (pBorderData)
+ if ( !pBorderData->GetTop() && !pBorderData->GetBottom() && !pBorderData->GetLeft() &&
+ !pBorderData->GetRight() )
+ pBorderData = nullptr;
+
+ if (!pBorderData && !pBackground && !pShadow)
+ return; // nothing to do
+
+ tools::Long nLeft = 0;
+ tools::Long nRight = 0;
+ tools::Long nTop = 0;
+ tools::Long nBottom = 0;
+
+ // aFrameRect - outside around frame, without shadow
+ if ( pShadow && pShadow->GetLocation() != SvxShadowLocation::NONE )
+ {
+ nLeft += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT) * nScaleX );
+ nRight += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT) * nScaleX );
+ nTop += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) * nScaleY );
+ nBottom += static_cast<tools::Long>( pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM) * nScaleY );
+ }
+ tools::Rectangle aFrameRect( Point(nScrX+nLeft, nScrY+nTop),
+ Size(nScrW-nLeft-nRight, nScrH-nTop-nBottom) );
+
+ // center of frame, to paint lines through OutputData
+ if (pBorderData)
+ {
+ nLeft += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetLeft()) * nScaleX / 2 );
+ nRight += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetRight()) * nScaleX / 2 );
+ nTop += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetTop()) * nScaleY / 2 );
+ nBottom += static_cast<tools::Long>( lcl_LineTotal(pBorderData->GetBottom()) * nScaleY / 2 );
+ }
+ tools::Long nEffHeight = nScrH - nTop - nBottom;
+ tools::Long nEffWidth = nScrW - nLeft - nRight;
+ if (nEffHeight<=0 || nEffWidth<=0)
+ return; // empty
+
+ if ( pBackground )
+ {
+ if (pBackground->GetGraphicPos() != GPOS_NONE)
+ {
+ OutputDevice* pRefDev;
+ if ( bIsRender )
+ pRefDev = pDev; // don't use printer for PDF
+ else
+ pRefDev = rDoc.GetPrinter(); // use printer also for preview
+ OUString referer;
+ if (pDocShell->HasName()) {
+ referer = pDocShell->GetMedium()->GetName();
+ }
+ lcl_DrawGraphic(*pBackground, *pDev, pRefDev, aFrameRect, aFrameRect, referer);
+ }
+ else
+ {
+ pDev->SetFillColor(pBackground->GetColor());
+ pDev->SetLineColor();
+ pDev->DrawRect(aFrameRect);
+ }
+ }
+
+ if ( pShadow && pShadow->GetLocation() != SvxShadowLocation::NONE )
+ {
+ pDev->SetFillColor(pShadow->GetColor());
+ pDev->SetLineColor();
+ tools::Long nShadowX = static_cast<tools::Long>( pShadow->GetWidth() * nScaleX );
+ tools::Long nShadowY = static_cast<tools::Long>( pShadow->GetWidth() * nScaleY );
+ switch (pShadow->GetLocation())
+ {
+ case SvxShadowLocation::TopLeft:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Top()-nShadowY,
+ aFrameRect.Right()-nShadowX, aFrameRect.Top() ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Top()-nShadowY,
+ aFrameRect.Left(), aFrameRect.Bottom()-nShadowY ) );
+ break;
+ case SvxShadowLocation::TopRight:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()+nShadowX, aFrameRect.Top()-nShadowY,
+ aFrameRect.Right()+nShadowX, aFrameRect.Top() ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Right(), aFrameRect.Top()-nShadowY,
+ aFrameRect.Right()+nShadowX, aFrameRect.Bottom()-nShadowY ) );
+ break;
+ case SvxShadowLocation::BottomLeft:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Bottom(),
+ aFrameRect.Right()-nShadowX, aFrameRect.Bottom()+nShadowY ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()-nShadowX, aFrameRect.Top()+nShadowY,
+ aFrameRect.Left(), aFrameRect.Bottom()+nShadowY ) );
+ break;
+ case SvxShadowLocation::BottomRight:
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Left()+nShadowX, aFrameRect.Bottom(),
+ aFrameRect.Right()+nShadowX, aFrameRect.Bottom()+nShadowY ) );
+ pDev->DrawRect( tools::Rectangle(
+ aFrameRect.Right(), aFrameRect.Top()+nShadowY,
+ aFrameRect.Right()+nShadowX, aFrameRect.Bottom()+nShadowY ) );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if (!pBorderData)
+ return;
+
+ ScDocumentUniquePtr pBorderDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pBorderDoc->InitUndo( rDoc, 0,0, true,true );
+ pBorderDoc->ApplyAttr( 0,0,0, *pBorderData );
+
+ ScTableInfo aTabInfo;
+ pBorderDoc->FillInfo( aTabInfo, 0,0, 0,0, 0,
+ nScaleX, nScaleY, false, false );
+ OSL_ENSURE(aTabInfo.mnArrCount,"nArrCount == 0");
+
+ aTabInfo.mpRowInfo[1].nHeight = static_cast<sal_uInt16>(nEffHeight);
+ aTabInfo.mpRowInfo[0].basicCellInfo(0).nWidth =
+ aTabInfo.mpRowInfo[1].basicCellInfo(0).nWidth = static_cast<sal_uInt16>(nEffWidth);
+
+ ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, pBorderDoc.get(), 0,
+ nScrX+nLeft, nScrY+nTop, 0,0, 0,0, nScaleX, nScaleY );
+ aOutputData.SetUseStyleColor( bUseStyleColor );
+
+ aOutputData.DrawFrame(*pDev);
+}
+
+void ScPrintFunc::PrintColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY )
+{
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+ SCCOL nCol;
+
+ tools::Long nHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
+ tools::Long nEndY = nScrY + nHeight - nOneY;
+
+ tools::Long nPosX = nScrX;
+ if ( bLayoutRTL )
+ {
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ nPosX += static_cast<tools::Long>( rDoc.GetColWidth( nCol, nPrintTab ) * nScaleX );
+ }
+ else
+ nPosX -= nOneX;
+ tools::Long nPosY = nScrY - nOneY;
+ OUString aText;
+
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
+ if (nDocW)
+ {
+ tools::Long nWidth = static_cast<tools::Long>(nDocW * nScaleX);
+ tools::Long nEndX = nPosX + nWidth * nLayoutSign;
+
+ pDev->DrawRect( tools::Rectangle( nPosX,nPosY,nEndX,nEndY ) );
+
+ aText = ::ScColToAlpha( nCol);
+ tools::Long nTextWidth = pDev->GetTextWidth(aText);
+ tools::Long nTextHeight = pDev->GetTextHeight();
+ tools::Long nAddX = ( nWidth - nTextWidth ) / 2;
+ tools::Long nAddY = ( nHeight - nTextHeight ) / 2;
+ tools::Long nTextPosX = nPosX+nAddX;
+ if ( bLayoutRTL )
+ nTextPosX -= nWidth;
+ pDev->DrawText( Point( nTextPosX,nPosY+nAddY ), aText );
+
+ nPosX = nEndX;
+ }
+ }
+}
+
+void ScPrintFunc::PrintRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY )
+{
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+
+ tools::Long nWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
+ tools::Long nEndX = nScrX + nWidth;
+ tools::Long nPosX = nScrX;
+ if ( !bLayoutRTL )
+ {
+ nEndX -= nOneX;
+ nPosX -= nOneX;
+ }
+ tools::Long nPosY = nScrY - nOneY;
+ OUString aText;
+
+ for (SCROW nRow=nY1; nRow<=nY2; nRow++)
+ {
+ sal_uInt16 nDocH = rDoc.GetRowHeight( nRow, nPrintTab );
+ if (nDocH)
+ {
+ tools::Long nHeight = static_cast<tools::Long>(nDocH * nScaleY);
+ tools::Long nEndY = nPosY + nHeight;
+
+ pDev->DrawRect( tools::Rectangle( nPosX,nPosY,nEndX,nEndY ) );
+
+ aText = OUString::number( nRow+1 );
+ tools::Long nTextWidth = pDev->GetTextWidth(aText);
+ tools::Long nTextHeight = pDev->GetTextHeight();
+ tools::Long nAddX = ( nWidth - nTextWidth ) / 2;
+ tools::Long nAddY = ( nHeight - nTextHeight ) / 2;
+ pDev->DrawText( Point( nPosX+nAddX,nPosY+nAddY ), aText );
+
+ nPosY = nEndY;
+ }
+ }
+}
+
+void ScPrintFunc::LocateColHdr( SCCOL nX1, SCCOL nX2, tools::Long nScrX, tools::Long nScrY,
+ bool bRepCol, ScPreviewLocationData& rLocationData )
+{
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ tools::Long nHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
+ tools::Long nEndY = nScrY + nHeight - nOneY;
+
+ tools::Long nPosX = nScrX - nOneX;
+ for (SCCOL nCol=nX1; nCol<=nX2; nCol++)
+ {
+ sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
+ if (nDocW)
+ nPosX += static_cast<tools::Long>(nDocW * nScaleX);
+ }
+ tools::Rectangle aCellRect( nScrX, nScrY, nPosX, nEndY );
+ rLocationData.AddColHeaders( aCellRect, nX1, nX2, bRepCol );
+}
+
+void ScPrintFunc::LocateRowHdr( SCROW nY1, SCROW nY2, tools::Long nScrX, tools::Long nScrY,
+ bool bRepRow, ScPreviewLocationData& rLocationData )
+{
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+
+ tools::Long nWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
+ tools::Long nEndX = nScrX + nWidth;
+ if ( !bLayoutRTL )
+ nEndX -= nOneX;
+
+ tools::Long nPosY = nScrY - nOneY;
+ nPosY += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab, nScaleY);
+ tools::Rectangle aCellRect( nScrX, nScrY, nEndX, nPosY );
+ rLocationData.AddRowHeaders( aCellRect, nY1, nY2, bRepRow );
+}
+
+void ScPrintFunc::LocateArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ tools::Long nScrX, tools::Long nScrY, bool bRepCol, bool bRepRow,
+ ScPreviewLocationData& rLocationData )
+{
+ // get MapMode for drawing objects (same MapMode as in ScOutputData::PrintDrawingLayer)
+
+ Point aLogPos = OutputDevice::LogicToLogic(Point(nScrX,nScrY), aOffsetMode, aLogicMode);
+ tools::Long nLogStX = aLogPos.X();
+ tools::Long nLogStY = aLogPos.Y();
+
+ SCCOL nCol;
+ Point aTwipOffset;
+ for (nCol=0; nCol<nX1; nCol++)
+ aTwipOffset.AdjustX( -(rDoc.GetColWidth( nCol, nPrintTab )) );
+ aTwipOffset.AdjustY( -sal_Int32(rDoc.GetRowHeight( 0, nY1-1, nPrintTab )) );
+
+ Point aMMOffset(o3tl::convert(aTwipOffset, o3tl::Length::twip, o3tl::Length::mm100));
+ aMMOffset += Point( nLogStX, nLogStY );
+ MapMode aDrawMapMode( MapUnit::Map100thMM, aMMOffset, aLogicMode.GetScaleX(), aLogicMode.GetScaleY() );
+
+ // get pixel rectangle
+
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ tools::Long nPosX = nScrX - nOneX;
+ for (nCol=nX1; nCol<=nX2; nCol++)
+ {
+ sal_uInt16 nDocW = rDoc.GetColWidth( nCol, nPrintTab );
+ if (nDocW)
+ nPosX += static_cast<tools::Long>(nDocW * nScaleX);
+ }
+
+ tools::Long nPosY = nScrY - nOneY;
+ nPosY += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab, nScaleY);
+ tools::Rectangle aCellRect( nScrX, nScrY, nPosX, nPosY );
+ rLocationData.AddCellRange( aCellRect, ScRange( nX1,nY1,nPrintTab, nX2,nY2,nPrintTab ),
+ bRepCol, bRepRow, aDrawMapMode );
+}
+
+void ScPrintFunc::PrintArea( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ tools::Long nScrX, tools::Long nScrY,
+ bool bShLeft, bool bShTop, bool bShRight, bool bShBottom )
+{
+ // #i47547# nothing to do if the end of the print area is before the end of
+ // the repeat columns/rows (don't use negative size for ScOutputData)
+ if ( nX2 < nX1 || nY2 < nY1 )
+ return;
+
+ //! hand over Flag at FillInfo !!!!!
+ ScRange aERange;
+ bool bEmbed = rDoc.IsEmbedded();
+ if (bEmbed)
+ {
+ rDoc.GetEmbedded(aERange);
+ rDoc.ResetEmbedded();
+ }
+
+ Point aPos = OutputDevice::LogicToLogic(Point(nScrX,nScrY), aOffsetMode, aLogicMode);
+ tools::Long nLogStX = aPos.X();
+ tools::Long nLogStY = aPos.Y();
+
+ // Assemble data
+
+ ScTableInfo aTabInfo;
+ rDoc.FillInfo( aTabInfo, nX1, nY1, nX2, nY2, nPrintTab,
+ nScaleX, nScaleY, true, aTableParam.bFormulas );
+ lcl_HidePrint( aTabInfo, nX1, nX2 );
+
+ if (bEmbed)
+ rDoc.SetEmbedded(aERange);
+
+ ScOutputData aOutputData( pDev, OUTTYPE_PRINTER, aTabInfo, &rDoc, nPrintTab,
+ nScrX, nScrY, nX1, nY1, nX2, nY2, nScaleX, nScaleY );
+
+ aOutputData.SetDrawView( pDrawView );
+
+ // test if all paint parts are hidden, then a paint is not necessary at all
+ const Point aMMOffset(aOutputData.PrePrintDrawingLayer(nLogStX, nLogStY));
+ const bool bHideAllDrawingLayer( pDrawView && pDrawView->getHideOle() && pDrawView->getHideChart()
+ && pDrawView->getHideDraw() && pDrawView->getHideFormControl() );
+
+ if(!bHideAllDrawingLayer)
+ {
+ pDev->SetMapMode(aLogicMode);
+ // don's set Clipping here (Mapmode is being moved)
+
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_BACK, aMMOffset);
+ }
+
+ pDev->SetMapMode(aOffsetMode);
+
+ aOutputData.SetShowFormulas( aTableParam.bFormulas );
+ aOutputData.SetShowNullValues( aTableParam.bNullVals );
+ aOutputData.SetUseStyleColor( bUseStyleColor );
+
+ Color aGridColor( COL_BLACK );
+ if ( bUseStyleColor )
+ aGridColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ aOutputData.SetGridColor( aGridColor );
+
+ if ( !pPrinter )
+ {
+ OutputDevice* pRefDev = rDoc.GetPrinter(); // use the printer also for Preview
+ Fraction aPrintFrac( nZoom, 100 ); // without nManualZoom
+ // MapMode, as it would arrive at the printer:
+ pRefDev->SetMapMode( MapMode( MapUnit::Map100thMM, Point(), aPrintFrac, aPrintFrac ) );
+
+ // when rendering (PDF), don't use printer as ref device, but printer's MapMode
+ // has to be set anyway, as charts still use it (#106409#)
+ if ( !bIsRender )
+ aOutputData.SetRefDevice( pRefDev );
+ }
+
+ if( aTableParam.bCellContent )
+ aOutputData.DrawBackground(*pDev);
+
+ pDev->SetClipRegion(vcl::Region(tools::Rectangle(
+ aPos, Size(aOutputData.GetScrW(), aOutputData.GetScrH()))));
+ pDev->SetClipRegion();
+
+ if( aTableParam.bCellContent )
+ {
+ aOutputData.DrawExtraShadow( bShLeft, bShTop, bShRight, bShBottom );
+ aOutputData.DrawFrame(*pDev);
+ aOutputData.DrawSparklines(*pDev);
+ aOutputData.DrawStrings();
+ aOutputData.DrawEdit(false);
+ }
+
+ if (aTableParam.bGrid)
+ aOutputData.DrawGrid(*pDev, true, false); // no page breaks
+
+ aOutputData.AddPDFNotes(); // has no effect if not rendering PDF with notes enabled
+
+ // test if all paint parts are hidden, then a paint is not necessary at all
+ if(!bHideAllDrawingLayer)
+ {
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_FRONT, aMMOffset);
+ }
+
+ // #i72502#
+ aOutputData.PrintDrawingLayer(SC_LAYER_INTERN, aMMOffset);
+ aOutputData.PostPrintDrawingLayer(aMMOffset); // #i74768#
+}
+
+bool ScPrintFunc::IsMirror( tools::Long nPageNo ) // Mirror margins?
+{
+ return nPageUsage == SvxPageUsage::Mirror && (nPageNo & 1);
+}
+
+bool ScPrintFunc::IsLeft( tools::Long nPageNo ) // left foot notes?
+{
+ bool bLeft;
+ if (nPageUsage == SvxPageUsage::Left)
+ bLeft = true;
+ else if (nPageUsage == SvxPageUsage::Right)
+ bLeft = false;
+ else
+ bLeft = (nPageNo & 1) != 0;
+ return bLeft;
+}
+
+void ScPrintFunc::MakeTableString()
+{
+ OUString aTmp;
+ rDoc.GetName(nPrintTab, aTmp);
+ aFieldData.aTabName = aTmp;
+}
+
+void ScPrintFunc::MakeEditEngine()
+{
+ if (!pEditEngine)
+ {
+ // can't use document's edit engine pool here,
+ // because pool must have twips as default metric
+ pEditEngine.reset( new ScHeaderEditEngine( EditEngine::CreatePool().get() ) );
+
+ pEditEngine->EnableUndo(false);
+ //fdo#45869 we want text to be positioned as it would be for the
+ //high dpi printed output, not as would be ideal for the 96dpi preview
+ //window itself
+ pEditEngine->SetRefDevice(pPrinter ? pPrinter : rDoc.GetRefDevice());
+ pEditEngine->SetWordDelimiters(
+ ScEditUtil::ModifyDelimiters( pEditEngine->GetWordDelimiters() ) );
+ pEditEngine->SetControlWord( pEditEngine->GetControlWord() & ~EEControlBits::RTFSTYLESHEETS );
+ rDoc.ApplyAsianEditSettings( *pEditEngine );
+ pEditEngine->EnableAutoColor( bUseStyleColor );
+
+ // Default-Set for alignment
+ pEditDefaults.reset( new SfxItemSet( pEditEngine->GetEmptyItemSet() ) );
+
+ const ScPatternAttr& rPattern = rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN);
+ rPattern.FillEditItemSet( pEditDefaults.get() );
+ // FillEditItemSet adjusts font height to 1/100th mm,
+ // but for header/footer twips is needed, as in the PatternAttr:
+ pEditDefaults->Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ pEditDefaults->Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ pEditDefaults->Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ // don't use font color, because background color is not used
+ //! there's no way to set the background for note pages
+ pEditDefaults->ClearItem( EE_CHAR_COLOR );
+ if (ScGlobal::IsSystemRTL())
+ pEditDefaults->Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) );
+ }
+
+ pEditEngine->SetData( aFieldData ); // Set page count etc.
+}
+
+// nStartY = logic
+void ScPrintFunc::PrintHF( tools::Long nPageNo, bool bHeader, tools::Long nStartY,
+ bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ const ScPrintHFParam& rParam = bHeader ? aHdr : aFtr;
+
+ pDev->SetMapMode( aTwipMode ); // Head-/Footlines in Twips
+
+ bool bFirst = 0 == nPageNo && !rParam.bSharedFirst;
+ bool bLeft = IsLeft(nPageNo) && !rParam.bShared;
+ const ScPageHFItem* pHFItem = bFirst ? rParam.pFirst : (bLeft ? rParam.pLeft : rParam.pRight);
+
+ tools::Long nLineStartX = aPageRect.Left() + rParam.nLeft;
+ tools::Long nLineEndX = aPageRect.Right() - rParam.nRight;
+ tools::Long nLineWidth = nLineEndX - nLineStartX + 1;
+
+ // Edit-Engine
+
+ Point aStart( nLineStartX, nStartY );
+ Size aPaperSize( nLineWidth, rParam.nHeight-rParam.nDistance );
+ if ( rParam.pBorder )
+ {
+ tools::Long nLeft = lcl_LineTotal( rParam.pBorder->GetLeft() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::LEFT);
+ tools::Long nTop = lcl_LineTotal( rParam.pBorder->GetTop() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::TOP);
+ aStart.AdjustX(nLeft );
+ aStart.AdjustY(nTop );
+ aPaperSize.AdjustWidth( -(nLeft + lcl_LineTotal( rParam.pBorder->GetRight() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::RIGHT)) );
+ aPaperSize.AdjustHeight( -(nTop + lcl_LineTotal( rParam.pBorder->GetBottom() ) + rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM)) );
+ }
+
+ if ( rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE )
+ {
+ tools::Long nLeft = rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::LEFT);
+ tools::Long nTop = rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP);
+ aStart.AdjustX(nLeft );
+ aStart.AdjustY(nTop );
+ aPaperSize.AdjustWidth( -(nLeft + rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::RIGHT)) );
+ aPaperSize.AdjustHeight( -(nTop + rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM)) );
+ }
+
+ aFieldData.nPageNo = nPageNo+aTableParam.nFirstPageNo;
+ MakeEditEngine();
+
+ pEditEngine->SetPaperSize(aPaperSize);
+
+ // Frame / Background
+
+ Point aBorderStart( nLineStartX, nStartY );
+ Size aBorderSize( nLineWidth, rParam.nHeight-rParam.nDistance );
+ if ( rParam.bDynamic )
+ {
+ // adjust here again, for even/odd head-/footlines
+ // and probably other breaks by variable (page number etc.)
+
+ tools::Long nMaxHeight = 0;
+ nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetLeftArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetCenterArea() ) );
+ nMaxHeight = std::max( nMaxHeight, TextHeight( pHFItem->GetRightArea() ) );
+ if (rParam.pBorder)
+ nMaxHeight += lcl_LineTotal( rParam.pBorder->GetTop() ) +
+ lcl_LineTotal( rParam.pBorder->GetBottom() ) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::TOP) +
+ rParam.pBorder->GetDistance(SvxBoxItemLine::BOTTOM);
+ if (rParam.pShadow && rParam.pShadow->GetLocation() != SvxShadowLocation::NONE)
+ nMaxHeight += rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ rParam.pShadow->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+
+ if (nMaxHeight < rParam.nManHeight-rParam.nDistance)
+ nMaxHeight = rParam.nManHeight-rParam.nDistance; // configured Minimum
+
+ aBorderSize.setHeight( nMaxHeight );
+ }
+
+ if ( bDoPrint )
+ {
+ double nOldScaleX = nScaleX;
+ double nOldScaleY = nScaleY;
+ nScaleX = nScaleY = 1.0; // output directly in Twips
+ DrawBorder( aBorderStart.X(), aBorderStart.Y(), aBorderSize.Width(), aBorderSize.Height(),
+ rParam.pBorder, rParam.pBack, rParam.pShadow );
+ nScaleX = nOldScaleX;
+ nScaleY = nOldScaleY;
+
+ // Clipping for Text
+
+ pDev->SetClipRegion(vcl::Region(tools::Rectangle(aStart, aPaperSize)));
+
+ // left
+
+ const EditTextObject* pObject = pHFItem->GetLeftArea();
+ if (pObject)
+ {
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+ Point aDraw = aStart;
+ tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
+ if (nDif > 0)
+ aDraw.AdjustY(nDif / 2 );
+ pEditEngine->Draw(*pDev, aDraw);
+ }
+
+ // center
+
+ pObject = pHFItem->GetCenterArea();
+ if (pObject)
+ {
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+ Point aDraw = aStart;
+ tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
+ if (nDif > 0)
+ aDraw.AdjustY(nDif / 2 );
+ pEditEngine->Draw(*pDev, aDraw);
+ }
+
+ // right
+
+ pObject = pHFItem->GetRightArea();
+ if (pObject)
+ {
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+ pEditEngine->SetTextNewDefaults( *pObject, *pEditDefaults, false );
+ Point aDraw = aStart;
+ tools::Long nDif = aPaperSize.Height() - static_cast<tools::Long>(pEditEngine->GetTextHeight());
+ if (nDif > 0)
+ aDraw.AdjustY(nDif / 2 );
+ pEditEngine->Draw(*pDev, aDraw);
+ }
+
+ pDev->SetClipRegion();
+ }
+
+ if ( pLocationData )
+ {
+ tools::Rectangle aHeaderRect( aBorderStart, aBorderSize );
+ pLocationData->AddHeaderFooter( aHeaderRect, bHeader, bLeft );
+ }
+}
+
+tools::Long ScPrintFunc::DoNotes( tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ if (bDoPrint)
+ pDev->SetMapMode(aTwipMode);
+
+ MakeEditEngine();
+ pEditDefaults->Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+ pEditEngine->SetDefaults( *pEditDefaults );
+
+ vcl::Font aMarkFont;
+ ScAutoFontColorMode eColorMode = bUseStyleColor ? ScAutoFontColorMode::Display : ScAutoFontColorMode::Print;
+ rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN).fillFont(aMarkFont, eColorMode);
+ pDev->SetFont(aMarkFont);
+ tools::Long nMarkLen = pDev->GetTextWidth("GW99999:");
+ // without Space-Char, because it rarely arrives there
+
+ Size aDataSize = aPageRect.GetSize();
+ if ( nMarkLen > aDataSize.Width() / 2 ) // everything much too small?
+ nMarkLen = aDataSize.Width() / 2; // split the page appropriately
+ aDataSize.AdjustWidth( -nMarkLen );
+
+ pEditEngine->SetPaperSize( aDataSize );
+ tools::Long nPosX = aPageRect.Left() + nMarkLen;
+ tools::Long nPosY = aPageRect.Top();
+
+ tools::Long nCount = 0;
+ tools::Long nSize = aNotePosList.size();
+ bool bOk;
+ do
+ {
+ bOk = false;
+ if ( nNoteStart + nCount < nSize)
+ {
+ ScAddress &rPos = aNotePosList[ nNoteStart + nCount ];
+
+ if( const ScPostIt* pNote = rDoc.GetNote( rPos ) )
+ {
+ if(const EditTextObject *pEditText = pNote->GetEditTextObject())
+ pEditEngine->SetTextCurrentDefaults(*pEditText);
+ tools::Long nTextHeight = pEditEngine->GetTextHeight();
+ if ( nPosY + nTextHeight < aPageRect.Bottom() )
+ {
+ if (bDoPrint)
+ {
+ pEditEngine->Draw(*pDev, Point(nPosX, nPosY));
+
+ OUString aMarkStr(rPos.Format(ScRefFlags::VALID, &rDoc, rDoc.GetAddressConvention()) + ":");
+
+ // cell position also via EditEngine, for correct positioning
+ pEditEngine->SetTextCurrentDefaults(aMarkStr);
+ pEditEngine->Draw(*pDev, Point(aPageRect.Left(), nPosY));
+ }
+
+ if ( pLocationData )
+ {
+ tools::Rectangle aTextRect( Point( nPosX, nPosY ), Size( aDataSize.Width(), nTextHeight ) );
+ pLocationData->AddNoteText( aTextRect, rPos );
+ tools::Rectangle aMarkRect( Point( aPageRect.Left(), nPosY ), Size( nMarkLen, nTextHeight ) );
+ pLocationData->AddNoteMark( aMarkRect, rPos );
+ }
+
+ nPosY += nTextHeight;
+ nPosY += 200; // Distance
+ ++nCount;
+ bOk = true;
+ }
+ }
+ }
+ }
+ while (bOk);
+
+ return nCount;
+}
+
+tools::Long ScPrintFunc::PrintNotes( tools::Long nPageNo, tools::Long nNoteStart, bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ if ( nNoteStart >= static_cast<tools::Long>(aNotePosList.size()) || !aTableParam.bNotes )
+ return 0;
+
+ if ( bDoPrint && bClearWin )
+ {
+ //! aggregate PrintPage !!!
+
+ Color aBackgroundColor( COL_WHITE );
+ if ( bUseStyleColor )
+ aBackgroundColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+
+ pDev->SetMapMode(aOffsetMode);
+ pDev->SetLineColor();
+ pDev->SetFillColor(aBackgroundColor);
+ pDev->DrawRect(tools::Rectangle(Point(),
+ Size(static_cast<tools::Long>(aPageSize.Width() * nScaleX * 100 / nZoom),
+ static_cast<tools::Long>(aPageSize.Height() * nScaleY * 100 / nZoom))));
+ }
+
+ // adjust aPageRect for left/right page
+
+ tools::Rectangle aTempRect( Point(), aPageSize );
+ if (IsMirror(nPageNo))
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nRightMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nLeftMargin ) * 100 / nZoom );
+ }
+ else
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nLeftMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nRightMargin ) * 100 / nZoom );
+ }
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "StartPage does not exist anymore" );
+ }
+
+ if ( bDoPrint || pLocationData )
+ {
+ // Head and foot lines
+
+ if (aHdr.bEnable)
+ {
+ tools::Long nHeaderY = aPageRect.Top()-aHdr.nHeight;
+ PrintHF( nPageNo, true, nHeaderY, bDoPrint, pLocationData );
+ }
+ if (aFtr.bEnable)
+ {
+ tools::Long nFooterY = aPageRect.Bottom()+aFtr.nDistance;
+ PrintHF( nPageNo, false, nFooterY, bDoPrint, pLocationData );
+ }
+ }
+
+ tools::Long nCount = DoNotes( nNoteStart, bDoPrint, pLocationData );
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "EndPage does not exist anymore" );
+ }
+
+ return nCount;
+}
+
+void ScPrintFunc::PrintPage( tools::Long nPageNo, SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
+ bool bDoPrint, ScPreviewLocationData* pLocationData )
+{
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nPrintTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ // nPageNo is the page number within all sheets of one "start page" setting
+
+ if ( bClearWin && bDoPrint )
+ {
+ // must exactly fit to painting the frame in preview.cxx !!!
+
+ Color aBackgroundColor( COL_WHITE );
+ if ( bUseStyleColor )
+ aBackgroundColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+
+ pDev->SetMapMode(aOffsetMode);
+ pDev->SetLineColor();
+ pDev->SetFillColor(aBackgroundColor);
+ pDev->DrawRect(tools::Rectangle(Point(),
+ Size(static_cast<tools::Long>(aPageSize.Width() * nScaleX * 100 / nZoom),
+ static_cast<tools::Long>(aPageSize.Height() * nScaleY * 100 / nZoom))));
+ }
+
+ // adjust aPageRect for left/right page
+
+ tools::Rectangle aTempRect( Point(), aPageSize );
+ if (IsMirror(nPageNo))
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nRightMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nLeftMargin ) * 100 / nZoom );
+ }
+ else
+ {
+ aPageRect.SetLeft( ( aTempRect.Left() + nLeftMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aTempRect.Right() - nRightMargin ) * 100 / nZoom );
+ }
+
+ if ( aAreaParam.bRepeatCol )
+ if ( nX1 > nRepeatStartCol && nX1 <= nRepeatEndCol )
+ nX1 = nRepeatEndCol + 1;
+ bool bDoRepCol = (aAreaParam.bRepeatCol && nX1 > nRepeatEndCol);
+ if ( aAreaParam.bRepeatRow )
+ if ( nY1 > nRepeatStartRow && nY1 <= nRepeatEndRow )
+ nY1 = nRepeatEndRow + 1;
+ bool bDoRepRow = (aAreaParam.bRepeatRow && nY1 > nRepeatEndRow);
+
+ // use new object hide flags in SdrPaintView
+ if(pDrawView)
+ {
+ pDrawView->setHideOle(!aTableParam.bObjects);
+ pDrawView->setHideChart(!aTableParam.bCharts);
+ pDrawView->setHideDraw(!aTableParam.bDrawings);
+ pDrawView->setHideFormControl(!aTableParam.bDrawings);
+ }
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "StartPage does not exist anymore" );
+ }
+
+ // head and foot lines (without centering)
+
+ if (aHdr.bEnable)
+ {
+ tools::Long nHeaderY = aPageRect.Top()-aHdr.nHeight;
+ PrintHF( nPageNo, true, nHeaderY, bDoPrint, pLocationData );
+ }
+ if (aFtr.bEnable)
+ {
+ tools::Long nFooterY = aPageRect.Bottom()+aFtr.nDistance;
+ PrintHF( nPageNo, false, nFooterY, bDoPrint, pLocationData );
+ }
+
+ // Position ( margins / centering )
+
+ tools::Long nLeftSpace = aPageRect.Left(); // Document-Twips
+ tools::Long nTopSpace = aPageRect.Top();
+ if ( bCenterHor || bLayoutRTL )
+ {
+ tools::Long nDataWidth = 0;
+ SCCOL i;
+ for (i=nX1; i<=nX2; i++)
+ nDataWidth += rDoc.GetColWidth( i,nPrintTab );
+ if (bDoRepCol)
+ for (i=nRepeatStartCol; i<=nRepeatEndCol; i++)
+ nDataWidth += rDoc.GetColWidth( i,nPrintTab );
+ if (aTableParam.bHeaders)
+ nDataWidth += tools::Long(PRINT_HEADER_WIDTH);
+ if (pBorderItem)
+ nDataWidth += pBorderItem->GetDistance(SvxBoxItemLine::LEFT) +
+ pBorderItem->GetDistance(SvxBoxItemLine::RIGHT); //! Line width?
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ nDataWidth += pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT);
+ if ( bCenterHor )
+ {
+ nLeftSpace += ( aPageRect.GetWidth() - nDataWidth ) / 2; // LTR or RTL
+ if (pBorderItem)
+ nLeftSpace -= lcl_LineTotal(pBorderItem->GetLeft());
+ }
+ else if ( bLayoutRTL )
+ nLeftSpace += aPageRect.GetWidth() - nDataWidth; // align to the right edge of the page
+ }
+ if ( bCenterVer )
+ {
+ tools::Long nDataHeight = rDoc.GetRowHeight( nY1, nY2, nPrintTab);
+ if (bDoRepRow)
+ nDataHeight += rDoc.GetRowHeight( nRepeatStartRow,
+ nRepeatEndRow, nPrintTab);
+ if (aTableParam.bHeaders)
+ nDataHeight += tools::Long(PRINT_HEADER_HEIGHT);
+ if (pBorderItem)
+ nDataHeight += pBorderItem->GetDistance(SvxBoxItemLine::TOP) +
+ pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM); //! Line width?
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ nDataHeight += pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+ nTopSpace += ( aPageRect.GetHeight() - nDataHeight ) / 2;
+ if (pBorderItem)
+ nTopSpace -= lcl_LineTotal(pBorderItem->GetTop());
+ }
+
+ // calculate sizes of the elements for partitioning
+ // (header, repeat, data)
+
+ tools::Long nHeaderWidth = 0;
+ tools::Long nHeaderHeight = 0;
+ tools::Long nRepeatWidth = 0;
+ tools::Long nRepeatHeight = 0;
+ tools::Long nContentWidth = 0; // scaled - not the same as nDataWidth above
+ tools::Long nContentHeight = 0;
+ if (aTableParam.bHeaders)
+ {
+ nHeaderWidth = static_cast<tools::Long>(PRINT_HEADER_WIDTH * nScaleX);
+ nHeaderHeight = static_cast<tools::Long>(PRINT_HEADER_HEIGHT * nScaleY);
+ }
+ if (bDoRepCol)
+ for (SCCOL i=nRepeatStartCol; i<=nRepeatEndCol; i++)
+ nRepeatWidth += static_cast<tools::Long>(rDoc.GetColWidth(i,nPrintTab) * nScaleX);
+ if (bDoRepRow)
+ nRepeatHeight += rDoc.GetScaledRowHeight( nRepeatStartRow,
+ nRepeatEndRow, nPrintTab, nScaleY);
+ for (SCCOL i=nX1; i<=nX2; i++)
+ nContentWidth += static_cast<tools::Long>(rDoc.GetColWidth(i,nPrintTab) * nScaleX);
+ nContentHeight += rDoc.GetScaledRowHeight( nY1, nY2, nPrintTab,
+ nScaleY);
+
+ // partition the page
+
+ tools::Long nStartX = static_cast<tools::Long>( nLeftSpace * nScaleX );
+ tools::Long nStartY = static_cast<tools::Long>( nTopSpace * nScaleY );
+ tools::Long nInnerStartX = nStartX;
+ tools::Long nInnerStartY = nStartY;
+ if (pBorderItem)
+ {
+ nInnerStartX += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetLeft()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::LEFT) ) * nScaleX );
+ nInnerStartY += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetTop()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::TOP) ) * nScaleY );
+ }
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ {
+ nInnerStartX += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) * nScaleX );
+ nInnerStartY += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) * nScaleY );
+ }
+
+ if ( bLayoutRTL )
+ {
+ // arrange elements starting from the right edge
+ nInnerStartX += nHeaderWidth + nRepeatWidth + nContentWidth;
+
+ // make rounding easier so the elements are really next to each other in preview
+ Size aOffsetOnePixel = pDev->PixelToLogic( Size(1,1), aOffsetMode );
+ tools::Long nOffsetOneX = aOffsetOnePixel.Width();
+ nInnerStartX += nOffsetOneX / 2;
+ }
+
+ tools::Long nFrameStartX = nInnerStartX;
+ tools::Long nFrameStartY = nInnerStartY;
+
+ tools::Long nRepStartX = nInnerStartX + nHeaderWidth * nLayoutSign; // widths/heights are 0 if not used
+ tools::Long nRepStartY = nInnerStartY + nHeaderHeight;
+ tools::Long nDataX = nRepStartX + nRepeatWidth * nLayoutSign;
+ tools::Long nDataY = nRepStartY + nRepeatHeight;
+ tools::Long nEndX = nDataX + nContentWidth * nLayoutSign;
+ tools::Long nEndY = nDataY + nContentHeight;
+ tools::Long nFrameEndX = nEndX;
+ tools::Long nFrameEndY = nEndY;
+
+ if ( bLayoutRTL )
+ {
+ // each element's start position is its left edge
+ //! subtract one pixel less?
+ nInnerStartX -= nHeaderWidth; // used for header
+ nRepStartX -= nRepeatWidth;
+ nDataX -= nContentWidth;
+
+ // continue right of the main elements again
+ nEndX += nHeaderWidth + nRepeatWidth + nContentWidth;
+ }
+
+ // Page frame / background
+
+ //! adjust nEndX/Y
+
+ tools::Long nBorderEndX = nEndX;
+ tools::Long nBorderEndY = nEndY;
+ if (pBorderItem)
+ {
+ nBorderEndX += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetRight()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::RIGHT) ) * nScaleX );
+ nBorderEndY += static_cast<tools::Long>( ( lcl_LineTotal(pBorderItem->GetBottom()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM) ) * nScaleY );
+ }
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ {
+ nBorderEndX += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT) * nScaleX );
+ nBorderEndY += static_cast<tools::Long>( pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM) * nScaleY );
+ }
+
+ if ( bDoPrint )
+ {
+ pDev->SetMapMode( aOffsetMode );
+ DrawBorder( nStartX, nStartY, nBorderEndX-nStartX, nBorderEndY-nStartY,
+ pBorderItem, pBackgroundItem, pShadowItem );
+
+ pDev->SetMapMode( aTwipMode );
+ }
+
+ pDev->SetMapMode( aOffsetMode );
+
+ // Output repeating rows/columns
+
+ if (bDoRepCol && bDoRepRow)
+ {
+ if ( bDoPrint )
+ PrintArea( nRepeatStartCol,nRepeatStartRow, nRepeatEndCol,nRepeatEndRow,
+ nRepStartX,nRepStartY, true, true, false, false );
+ if ( pLocationData )
+ LocateArea( nRepeatStartCol,nRepeatStartRow, nRepeatEndCol,nRepeatEndRow,
+ nRepStartX,nRepStartY, true, true, *pLocationData );
+ }
+ if (bDoRepCol)
+ {
+ if ( bDoPrint )
+ PrintArea( nRepeatStartCol,nY1, nRepeatEndCol,nY2, nRepStartX,nDataY,
+ true, !bDoRepRow, false, true );
+ if ( pLocationData )
+ LocateArea( nRepeatStartCol,nY1, nRepeatEndCol,nY2, nRepStartX,nDataY, true, false, *pLocationData );
+ }
+ if (bDoRepRow)
+ {
+ if ( bDoPrint )
+ PrintArea( nX1,nRepeatStartRow, nX2,nRepeatEndRow, nDataX,nRepStartY,
+ !bDoRepCol, true, true, false );
+ if ( pLocationData )
+ LocateArea( nX1,nRepeatStartRow, nX2,nRepeatEndRow, nDataX,nRepStartY, false, true, *pLocationData );
+ }
+
+ // output data
+
+ if ( bDoPrint )
+ PrintArea( nX1,nY1, nX2,nY2, nDataX,nDataY, !bDoRepCol,!bDoRepRow, true, true );
+ if ( pLocationData )
+ LocateArea( nX1,nY1, nX2,nY2, nDataX,nDataY, false,false, *pLocationData );
+
+ // output column/row headers
+ // after data (through probably shadow)
+
+ Color aGridColor( COL_BLACK );
+ if ( bUseStyleColor )
+ aGridColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+
+ if (aTableParam.bHeaders)
+ {
+ if ( bDoPrint )
+ {
+ pDev->SetLineColor( aGridColor );
+ pDev->SetFillColor();
+ pDev->SetMapMode(aOffsetMode);
+ }
+
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ vcl::Font aFont;
+ ScAutoFontColorMode eColorMode = bUseStyleColor ? ScAutoFontColorMode::Display : ScAutoFontColorMode::Print;
+ aPattern.fillFont(aFont, eColorMode, pDev);
+ pDev->SetFont(aFont);
+
+ if (bDoRepCol)
+ {
+ if ( bDoPrint )
+ PrintColHdr( nRepeatStartCol,nRepeatEndCol, nRepStartX,nInnerStartY );
+ if ( pLocationData )
+ LocateColHdr( nRepeatStartCol,nRepeatEndCol, nRepStartX,nInnerStartY, true, *pLocationData );
+ }
+ if ( bDoPrint )
+ PrintColHdr( nX1,nX2, nDataX,nInnerStartY );
+ if ( pLocationData )
+ LocateColHdr( nX1,nX2, nDataX,nInnerStartY, false, *pLocationData );
+ if (bDoRepRow)
+ {
+ if ( bDoPrint )
+ PrintRowHdr( nRepeatStartRow,nRepeatEndRow, nInnerStartX,nRepStartY );
+ if ( pLocationData )
+ LocateRowHdr( nRepeatStartRow,nRepeatEndRow, nInnerStartX,nRepStartY, true, *pLocationData );
+ }
+ if ( bDoPrint )
+ PrintRowHdr( nY1,nY2, nInnerStartX,nDataY );
+ if ( pLocationData )
+ LocateRowHdr( nY1,nY2, nInnerStartX,nDataY, false, *pLocationData );
+ }
+
+ // simple frame
+
+ if ( bDoPrint && ( aTableParam.bGrid || aTableParam.bHeaders ) )
+ {
+ Size aOnePixel = pDev->PixelToLogic(Size(1,1));
+ tools::Long nOneX = aOnePixel.Width();
+ tools::Long nOneY = aOnePixel.Height();
+
+ tools::Long nLeftX = nFrameStartX;
+ tools::Long nTopY = nFrameStartY - nOneY;
+ tools::Long nRightX = nFrameEndX;
+ tools::Long nBottomY = nFrameEndY - nOneY;
+ if ( !bLayoutRTL )
+ {
+ nLeftX -= nOneX;
+ nRightX -= nOneX;
+ }
+ pDev->SetMapMode(aOffsetMode);
+ pDev->SetLineColor( aGridColor );
+ pDev->SetFillColor();
+ pDev->DrawRect( tools::Rectangle( nLeftX, nTopY, nRightX, nBottomY ) );
+ // nEndX/Y without frame-adaptation
+ }
+
+ if ( pPrinter && bDoPrint )
+ {
+ OSL_FAIL( "EndPage does not exist anymore" );
+ }
+
+ aLastSourceRange = ScRange( nX1, nY1, nPrintTab, nX2, nY2, nPrintTab );
+ bSourceRangeValid = true;
+}
+
+void ScPrintFunc::SetOffset( const Point& rOfs )
+{
+ aSrcOffset = rOfs;
+}
+
+void ScPrintFunc::SetManualZoom( sal_uInt16 nNewZoom )
+{
+ nManualZoom = nNewZoom;
+}
+
+void ScPrintFunc::SetClearFlag( bool bFlag )
+{
+ bClearWin = bFlag;
+}
+
+void ScPrintFunc::SetUseStyleColor( bool bFlag )
+{
+ bUseStyleColor = bFlag;
+ if (pEditEngine)
+ pEditEngine->EnableAutoColor( bUseStyleColor );
+}
+
+void ScPrintFunc::SetRenderFlag( bool bFlag )
+{
+ bIsRender = bFlag; // set when using XRenderable (PDF)
+}
+
+void ScPrintFunc::SetExclusivelyDrawOleAndDrawObjects()
+{
+ aTableParam.bCellContent = false;
+ aTableParam.bNotes = false;
+ aTableParam.bGrid = false;
+ aTableParam.bHeaders = false;
+ aTableParam.bFormulas = false;
+ aTableParam.bNullVals = false;
+}
+
+// UpdatePages is only called from outside to set the breaks correctly for viewing
+// - always without UserArea
+
+bool ScPrintFunc::UpdatePages()
+{
+ if (!pParamSet)
+ return false;
+
+ // Zoom
+
+ nZoom = 100;
+ if (aTableParam.bScalePageNum || aTableParam.bScaleTo)
+ nZoom = ZOOM_MIN; // correct for breaks
+ else if (aTableParam.bScaleAll)
+ {
+ nZoom = aTableParam.nScaleAll;
+ if ( nZoom <= ZOOM_MIN )
+ nZoom = ZOOM_MIN;
+ }
+
+ OUString aName = rDoc.GetPageStyle( nPrintTab );
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ if ( nTab==nPrintTab || rDoc.GetPageStyle(nTab)==aName )
+ {
+ // Repeating rows/columns
+ rDoc.SetRepeatArea( nTab, nRepeatStartCol,nRepeatEndCol, nRepeatStartRow,nRepeatEndRow );
+
+ // set breaks
+ ResetBreaks(nTab);
+ pDocShell->PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid);
+ }
+
+ return true;
+}
+
+tools::Long ScPrintFunc::CountPages() // sets also nPagesX, nPagesY
+{
+ bool bAreaOk = false;
+
+ if (rDoc.HasTable( nPrintTab ))
+ {
+ if (aAreaParam.bPrintArea) // Specify print area?
+ {
+ if ( bPrintCurrentTable )
+ {
+ ScRange& rRange = aAreaParam.aPrintArea;
+
+ // Here, no comparison of the tables any more. Area is always valid for this table
+ // If comparison should be done here, the table of print ranges must be adjusted
+ // when inserting tables etc.!
+
+ nStartCol = rRange.aStart.Col();
+ nStartRow = rRange.aStart.Row();
+ nEndCol = rRange.aEnd .Col();
+ nEndRow = rRange.aEnd .Row();
+ bAreaOk = AdjustPrintArea(false); // limit
+ }
+ else
+ bAreaOk = false;
+ }
+ else // search from document
+ bAreaOk = AdjustPrintArea(true);
+ }
+
+ if (bAreaOk)
+ {
+ tools::Long nPages = 0;
+ size_t nY;
+ if (bMultiArea)
+ {
+ sal_uInt16 nRCount = rDoc.GetPrintRangeCount( nPrintTab );
+ for (sal_uInt16 i=0; i<nRCount; i++)
+ {
+ CalcZoom(i);
+ if ( aTableParam.bSkipEmpty )
+ for (nY=0; nY< m_aRanges.m_nPagesY; nY++)
+ nPages += (*m_aRanges.m_xPageRows)[nY].CountVisible();
+ else
+ nPages += static_cast<tools::Long>(m_aRanges.m_nPagesX) * m_aRanges.m_nPagesY;
+ if ( pPageData )
+ FillPageData();
+ }
+ }
+ else
+ {
+ CalcZoom(RANGENO_NORANGE); // calculate Zoom
+ if ( aTableParam.bSkipEmpty )
+ for (nY=0; nY<m_aRanges.m_nPagesY; nY++)
+ nPages += (*m_aRanges.m_xPageRows)[nY].CountVisible();
+ else
+ nPages += static_cast<tools::Long>(m_aRanges.m_nPagesX) * m_aRanges.m_nPagesY;
+ if ( pPageData )
+ FillPageData();
+ }
+ return nPages;
+ }
+ else
+ {
+ m_aRanges.m_nPagesX = m_aRanges.m_nPagesY = m_aRanges.m_nTotalY = 0;
+ return 0;
+ }
+}
+
+tools::Long ScPrintFunc::CountNotePages()
+{
+ if ( !aTableParam.bNotes || !bPrintCurrentTable )
+ return 0;
+
+ bool bError = false;
+ if (!aAreaParam.bPrintArea)
+ bError = !AdjustPrintArea(true); // completely search in Doc
+
+ sal_uInt16 nRepeats = 1; // how often go through it ?
+ if (bMultiArea)
+ nRepeats = rDoc.GetPrintRangeCount(nPrintTab);
+ if (bError)
+ nRepeats = 0;
+
+ for (sal_uInt16 nStep=0; nStep<nRepeats; nStep++)
+ {
+ bool bDoThis = true;
+ if (bMultiArea) // go through all Areas
+ {
+ const ScRange* pThisRange = rDoc.GetPrintRange( nPrintTab, nStep );
+ if ( pThisRange )
+ {
+ nStartCol = pThisRange->aStart.Col();
+ nStartRow = pThisRange->aStart.Row();
+ nEndCol = pThisRange->aEnd .Col();
+ nEndRow = pThisRange->aEnd .Row();
+ bDoThis = AdjustPrintArea(false);
+ }
+ }
+
+ if (bDoThis)
+ {
+ assert( bPrintAreaValid );
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ if (rDoc.HasColNotes(nCol, nPrintTab))
+ {
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ if ( rDoc.HasNote(nCol, nRow, nPrintTab) )
+ aNotePosList.emplace_back( nCol, nRow, nPrintTab );
+ }
+ }
+ }
+ }
+ }
+
+ tools::Long nPages = 0;
+ tools::Long nNoteNr = 0;
+ tools::Long nNoteAdd;
+ do
+ {
+ nNoteAdd = PrintNotes( nPages, nNoteNr, false, nullptr );
+ if (nNoteAdd)
+ {
+ nNoteNr += nNoteAdd;
+ ++nPages;
+ }
+ }
+ while (nNoteAdd);
+
+ return nPages;
+}
+
+void ScPrintFunc::InitModes() // set MapModes from nZoom etc.
+{
+ aOffset = Point( aSrcOffset.X()*100/nZoom, aSrcOffset.Y()*100/nZoom );
+
+ tools::Long nEffZoom = nZoom * static_cast<tools::Long>(nManualZoom);
+ constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+ nScaleX = nScaleY = HMM_PER_TWIPS; // output in 1/100 mm
+
+ Fraction aZoomFract( nEffZoom,10000 );
+ Fraction aHorFract = aZoomFract;
+
+ if ( !pPrinter && !bIsRender ) // adjust scale for preview
+ {
+ double nFact = pDocShell->GetOutputFactor();
+ aHorFract = Fraction( static_cast<tools::Long>( nEffZoom / nFact ), 10000 );
+ }
+
+ aLogicMode = MapMode( MapUnit::Map100thMM, Point(), aHorFract, aZoomFract );
+
+ Point aLogicOfs( -aOffset.X(), -aOffset.Y() );
+ aOffsetMode = MapMode( MapUnit::Map100thMM, aLogicOfs, aHorFract, aZoomFract );
+
+ Point aTwipsOfs( static_cast<tools::Long>( -aOffset.X() / nScaleX + 0.5 ), static_cast<tools::Long>( -aOffset.Y() / nScaleY + 0.5 ) );
+ aTwipMode = MapMode( MapUnit::MapTwip, aTwipsOfs, aHorFract, aZoomFract );
+}
+
+void ScPrintFunc::ApplyPrintSettings()
+{
+ if ( !pPrinter )
+ return;
+
+ // Configure Printer to Printing
+
+ Size aEnumSize = aPageSize;
+
+ pPrinter->SetOrientation( bLandscape ? Orientation::Landscape : Orientation::Portrait );
+ if ( bLandscape )
+ {
+ // landscape is always interpreted as a rotation by 90 degrees !
+ // this leads to non WYSIWIG but at least it prints!
+ // #i21775#
+ tools::Long nTemp = aEnumSize.Width();
+ aEnumSize.setWidth( aEnumSize.Height() );
+ aEnumSize.setHeight( nTemp );
+ }
+ Paper ePaper = SvxPaperInfo::GetSvxPaper( aEnumSize, MapUnit::MapTwip );
+ sal_uInt16 nPaperBin = pParamSet->Get(ATTR_PAGE_PAPERBIN).GetValue();
+
+ pPrinter->SetPaper( ePaper );
+ if ( PAPER_USER == ePaper )
+ {
+ MapMode aPrinterMode = pPrinter->GetMapMode();
+ MapMode aLocalMode( MapUnit::MapTwip );
+ pPrinter->SetMapMode( aLocalMode );
+ pPrinter->SetPaperSizeUser( aEnumSize );
+ pPrinter->SetMapMode( aPrinterMode );
+ }
+
+ pPrinter->SetPaperBin( nPaperBin );
+}
+
+// rPageRanges = range for all tables
+// nStartPage = rPageRanges starts at nStartPage
+// nDisplayStart = continuous number for displaying the page number
+
+tools::Long ScPrintFunc::DoPrint( const MultiSelection& rPageRanges,
+ tools::Long nStartPage, tools::Long nDisplayStart, bool bDoPrint,
+ ScPreviewLocationData* pLocationData )
+{
+ OSL_ENSURE(pDev,"Device == NULL");
+ if (!pParamSet)
+ return 0;
+
+ if ( pPrinter && bDoPrint )
+ ApplyPrintSettings();
+
+ InitModes();
+ if ( pLocationData )
+ {
+ pLocationData->SetCellMapMode( aOffsetMode );
+ pLocationData->SetPrintTab( nPrintTab );
+ }
+
+ MakeTableString();
+
+ tools::Long nPageNo = 0;
+ tools::Long nPrinted = 0;
+ tools::Long nEndPage = rPageRanges.GetTotalRange().Max();
+
+ sal_uInt16 nRepeats = 1;
+ if (bMultiArea)
+ nRepeats = rDoc.GetPrintRangeCount(nPrintTab);
+ for (sal_uInt16 nStep=0; nStep<nRepeats; nStep++)
+ {
+ if (bMultiArea) // replace area
+ {
+ CalcZoom(nStep); // also sets nStartCol etc. new
+ InitModes();
+ }
+
+ SCCOL nX1;
+ SCROW nY1;
+ SCCOL nX2;
+ SCROW nY2;
+ size_t nCountX;
+ size_t nCountY;
+
+ if (aTableParam.bTopDown) // top-bottom
+ {
+ nX1 = nStartCol;
+ for (nCountX=0; nCountX<m_aRanges.m_nPagesX; nCountX++)
+ {
+ OSL_ENSURE(nCountX < m_aRanges.m_xPageEndX->size(), "vector access error for aPageEndX (!)");
+ nX2 = (*m_aRanges.m_xPageEndX)[nCountX];
+ for (nCountY=0; nCountY<m_aRanges.m_nPagesY; nCountY++)
+ {
+ auto& rPageRow = (*m_aRanges.m_xPageRows)[nCountY];
+ nY1 = rPageRow.GetStartRow();
+ nY2 = rPageRow.GetEndRow();
+ if ( !aTableParam.bSkipEmpty || !rPageRow.IsHidden(nCountX) )
+ {
+ if ( rPageRanges.IsSelected( nPageNo+nStartPage+1 ) )
+ {
+ PrintPage( nPageNo+nDisplayStart, nX1, nY1, nX2, nY2,
+ bDoPrint, pLocationData );
+ ++nPrinted;
+ }
+ ++nPageNo;
+ }
+ }
+ nX1 = nX2 + 1;
+ }
+ }
+ else // left to right
+ {
+ for (nCountY=0; nCountY<m_aRanges.m_nPagesY; nCountY++)
+ {
+ auto& rPageRow = (*m_aRanges.m_xPageRows)[nCountY];
+ nY1 = rPageRow.GetStartRow();
+ nY2 = rPageRow.GetEndRow();
+ nX1 = nStartCol;
+ for (nCountX=0; nCountX<m_aRanges.m_nPagesX; nCountX++)
+ {
+ OSL_ENSURE(nCountX < m_aRanges.m_xPageEndX->size(), "vector access error for aPageEndX");
+ nX2 = (*m_aRanges.m_xPageEndX)[nCountX];
+ if ( !aTableParam.bSkipEmpty || !rPageRow.IsHidden(nCountX) )
+ {
+ if ( rPageRanges.IsSelected( nPageNo+nStartPage+1 ) )
+ {
+ PrintPage( nPageNo+nDisplayStart, nX1, nY1, nX2, nY2,
+ bDoPrint, pLocationData );
+ ++nPrinted;
+ }
+ ++nPageNo;
+ }
+ nX1 = nX2 + 1;
+ }
+ }
+ }
+ }
+
+ aFieldData.aTabName = ScResId( STR_NOTES );
+
+ tools::Long nNoteNr = 0;
+ tools::Long nNoteAdd;
+ do
+ {
+ if ( nPageNo+nStartPage <= nEndPage )
+ {
+ bool bPageSelected = rPageRanges.IsSelected( nPageNo+nStartPage+1 );
+ nNoteAdd = PrintNotes( nPageNo+nStartPage, nNoteNr, bDoPrint && bPageSelected,
+ ( bPageSelected ? pLocationData : nullptr ) );
+ if ( nNoteAdd )
+ {
+ nNoteNr += nNoteAdd;
+ if (bPageSelected)
+ {
+ ++nPrinted;
+ bSourceRangeValid = false; // last page was no cell range
+ }
+ ++nPageNo;
+ }
+ }
+ else
+ nNoteAdd = 0;
+ }
+ while (nNoteAdd);
+
+ if ( bMultiArea )
+ ResetBreaks(nPrintTab); //breaks correct for displaying
+
+ return nPrinted;
+}
+
+void ScPrintFunc::CalcZoom( sal_uInt16 nRangeNo ) // calculate zoom
+{
+ sal_uInt16 nRCount = rDoc.GetPrintRangeCount( nPrintTab );
+ const ScRange* pThisRange = nullptr;
+ if (nRangeNo != RANGENO_NORANGE && nRangeNo < nRCount)
+ pThisRange = rDoc.GetPrintRange( nPrintTab, nRangeNo );
+ if ( pThisRange )
+ {
+ nStartCol = pThisRange->aStart.Col();
+ nStartRow = pThisRange->aStart.Row();
+ nEndCol = pThisRange->aEnd .Col();
+ nEndRow = pThisRange->aEnd .Row();
+ }
+
+ if (!AdjustPrintArea(false)) // empty
+ {
+ nZoom = 100;
+ m_aRanges.m_nPagesX = m_aRanges.m_nPagesY = m_aRanges.m_nTotalY = 0;
+ return;
+ }
+
+ rDoc.SetRepeatArea( nPrintTab, nRepeatStartCol,nRepeatEndCol, nRepeatStartRow,nRepeatEndRow );
+
+ if (aTableParam.bScalePageNum)
+ {
+ nZoom = 100;
+ sal_uInt16 nPagesToFit = aTableParam.nScalePageNum;
+
+ // If manual breaks are forced, calculate minimum # pages required
+ if (aTableParam.bForceBreaks)
+ {
+ sal_uInt16 nMinPages = 0;
+ std::set<SCROW> aRowBreaks;
+ std::set<SCCOL> aColBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nPrintTab, false, true);
+ rDoc.GetAllColBreaks(aColBreaks, nPrintTab, false, true);
+ nMinPages = (aRowBreaks.size() + 1) * (aColBreaks.size() + 1);
+
+ // #i54993# use min forced by breaks if it's > # pages in
+ // scale parameter to avoid bottoming out at <= ZOOM_MIN
+ nPagesToFit = std::max(nMinPages, nPagesToFit);
+ }
+
+ sal_uInt16 nLastFitZoom = 0, nLastNonFitZoom = 0;
+ while (true)
+ {
+ if (nZoom <= ZOOM_MIN)
+ break;
+
+ CalcPages();
+ bool bFitsPage = (m_aRanges.m_nPagesX * m_aRanges.m_nPagesY <= nPagesToFit);
+
+ if (bFitsPage)
+ {
+ if (nZoom == 100)
+ // If it fits at 100%, it's good enough for me.
+ break;
+
+ nLastFitZoom = nZoom;
+ nZoom = (nLastNonFitZoom + nZoom) / 2;
+
+ if (nLastFitZoom == nZoom)
+ // It converged. Use this zoom level.
+ break;
+ }
+ else
+ {
+ if (nZoom - nLastFitZoom <= 1)
+ {
+ nZoom = nLastFitZoom;
+ CalcPages();
+ break;
+ }
+
+ nLastNonFitZoom = nZoom;
+ nZoom = (nLastFitZoom + nZoom) / 2;
+ }
+ }
+ }
+ else if (aTableParam.bScaleTo)
+ {
+ nZoom = 100;
+ sal_uInt16 nW = aTableParam.nScaleWidth;
+ sal_uInt16 nH = aTableParam.nScaleHeight;
+
+ // If manual breaks are forced, calculate minimum # pages required
+ if (aTableParam.bForceBreaks)
+ {
+ sal_uInt16 nMinPagesW = 0, nMinPagesH = 0;
+ std::set<SCROW> aRowBreaks;
+ std::set<SCCOL> aColBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nPrintTab, false, true);
+ rDoc.GetAllColBreaks(aColBreaks, nPrintTab, false, true);
+ nMinPagesW = aColBreaks.size() + 1;
+ nMinPagesH = aRowBreaks.size() + 1;
+
+ // #i54993# use min forced by breaks if it's > # pages in
+ // scale parameters to avoid bottoming out at <= ZOOM_MIN
+ nW = std::max(nMinPagesW, nW);
+ nH = std::max(nMinPagesH, nH);
+ }
+
+ sal_uInt16 nLastFitZoom = 0, nLastNonFitZoom = 0;
+ while (true)
+ {
+ if (nZoom <= ZOOM_MIN)
+ break;
+
+ CalcPages();
+ bool bFitsPage = ((!nW || (m_aRanges.m_nPagesX <= nW)) && (!nH || (m_aRanges.m_nPagesY <= nH)));
+
+ if (bFitsPage)
+ {
+ if (nZoom == 100)
+ // If it fits at 100%, it's good enough for me.
+ break;
+
+ nLastFitZoom = nZoom;
+ nZoom = (nLastNonFitZoom + nZoom) / 2;
+
+ if (nLastFitZoom == nZoom)
+ // It converged. Use this zoom level.
+ break;
+ }
+ else
+ {
+ if (nZoom - nLastFitZoom <= 1)
+ {
+ nZoom = nLastFitZoom;
+ CalcPages();
+ break;
+ }
+
+ nLastNonFitZoom = nZoom;
+ nZoom = (nLastFitZoom + nZoom) / 2;
+ }
+ }
+ // tdf#103516 remove the almost blank page(s) for better
+ // interoperability by using slightly smaller zoom
+ if (nW > 0 && nH == 0 && m_aRanges.m_nPagesY > 1)
+ {
+ sal_uInt32 nLastPagesY = m_aRanges.m_nPagesY;
+ nLastFitZoom = nZoom;
+ nZoom *= 0.98;
+ if (nZoom < nLastFitZoom)
+ {
+ CalcPages();
+ // same page count with smaller zoom: use the original zoom
+ if (m_aRanges.m_nPagesY == nLastPagesY)
+ {
+ nZoom = nLastFitZoom;
+ CalcPages();
+ }
+ }
+ }
+ }
+ else if (aTableParam.bScaleAll)
+ {
+ nZoom = aTableParam.nScaleAll;
+ if ( nZoom <= ZOOM_MIN )
+ nZoom = ZOOM_MIN;
+ CalcPages();
+ }
+ else
+ {
+ OSL_ENSURE( aTableParam.bScaleNone, "no scale flag is set" );
+ nZoom = 100;
+ CalcPages();
+ }
+}
+
+Size ScPrintFunc::GetDocPageSize()
+{
+ // Adjust height of head/foot line
+
+ InitModes(); // initialize aTwipMode from nZoom
+ pDev->SetMapMode( aTwipMode ); // head/foot line in Twips
+ UpdateHFHeight( aHdr );
+ UpdateHFHeight( aFtr );
+
+ // Page size in Document-Twips
+ // Calculating Left / Right also in PrintPage
+
+ aPageRect = tools::Rectangle( Point(), aPageSize );
+ aPageRect.SetLeft( ( aPageRect.Left() + nLeftMargin ) * 100 / nZoom );
+ aPageRect.SetRight( ( aPageRect.Right() - nRightMargin ) * 100 / nZoom );
+ aPageRect.SetTop( ( aPageRect.Top() + nTopMargin ) * 100 / nZoom + aHdr.nHeight );
+ aPageRect.SetBottom( ( aPageRect.Bottom() - nBottomMargin ) * 100 / nZoom - aFtr.nHeight );
+
+ Size aDocPageSize = aPageRect.GetSize();
+ if (aTableParam.bHeaders)
+ {
+ aDocPageSize.AdjustWidth( -(tools::Long(PRINT_HEADER_WIDTH)) );
+ aDocPageSize.AdjustHeight( -(tools::Long(PRINT_HEADER_HEIGHT)) );
+ }
+ if (pBorderItem)
+ {
+ aDocPageSize.AdjustWidth( -(lcl_LineTotal(pBorderItem->GetLeft()) +
+ lcl_LineTotal(pBorderItem->GetRight()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::LEFT) +
+ pBorderItem->GetDistance(SvxBoxItemLine::RIGHT)) );
+ aDocPageSize.AdjustHeight( -(lcl_LineTotal(pBorderItem->GetTop()) +
+ lcl_LineTotal(pBorderItem->GetBottom()) +
+ pBorderItem->GetDistance(SvxBoxItemLine::TOP) +
+ pBorderItem->GetDistance(SvxBoxItemLine::BOTTOM)) );
+ }
+ if (pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE)
+ {
+ aDocPageSize.AdjustWidth( -(pShadowItem->CalcShadowSpace(SvxShadowItemSide::LEFT) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::RIGHT)) );
+ aDocPageSize.AdjustHeight( -(pShadowItem->CalcShadowSpace(SvxShadowItemSide::TOP) +
+ pShadowItem->CalcShadowSpace(SvxShadowItemSide::BOTTOM)) );
+ }
+ return aDocPageSize;
+}
+
+void ScPrintFunc::ResetBreaks( SCTAB nTab ) // Set Breaks correctly for view
+{
+ rDoc.SetPageSize( nTab, GetDocPageSize() );
+ rDoc.UpdatePageBreaks( nTab );
+}
+
+static void lcl_SetHidden( const ScDocument& rDoc, SCTAB nPrintTab, ScPageRowEntry& rPageRowEntry,
+ SCCOL nStartCol, const std::vector< SCCOL >& rPageEndX )
+{
+ size_t nPagesX = rPageRowEntry.GetPagesX();
+ SCROW nStartRow = rPageRowEntry.GetStartRow();
+ SCROW nEndRow = rPageRowEntry.GetEndRow();
+
+ bool bLeftIsEmpty = false;
+ ScRange aTempRange;
+ tools::Rectangle aTempRect = rDoc.GetMMRect( 0,0, 0,0, 0 );
+
+ for (size_t i=0; i<nPagesX; i++)
+ {
+ OSL_ENSURE(i < rPageEndX.size(), "vector access error for aPageEndX");
+ SCCOL nEndCol = rPageEndX[i];
+ if ( rDoc.IsPrintEmpty( nStartCol, nStartRow, nEndCol, nEndRow, nPrintTab,
+ bLeftIsEmpty, &aTempRange, &aTempRect ) )
+ {
+ rPageRowEntry.SetHidden(i);
+ bLeftIsEmpty = true;
+ }
+ else
+ bLeftIsEmpty = false;
+
+ nStartCol = nEndCol+1;
+ }
+}
+
+void ScPrintFunc::CalcPages() // calculates aPageRect and pages from nZoom
+{
+ assert( bPrintAreaValid );
+
+ sc::PrintPageRangesInput aInput(aTableParam.bSkipEmpty, aAreaParam.bPrintArea,
+ ScRange(nStartCol, nStartRow, nPrintTab, nEndCol, nEndRow, nPrintTab),
+ GetDocPageSize());
+ m_aRanges.calculate(rDoc, aInput);
+}
+
+namespace sc
+{
+
+PrintPageRanges::PrintPageRanges()
+ : m_nPagesX(0)
+ , m_nPagesY(0)
+ , m_nTotalY(0)
+{}
+
+void PrintPageRanges::calculate(ScDocument& rDoc, PrintPageRangesInput const& rInput)
+{
+ // Already calculated?
+ if (m_aInput == rInput)
+ return;
+
+ m_aInput = rInput;
+
+ rDoc.SetPageSize(m_aInput.getPrintTab(), m_aInput.m_aDocSize);
+
+ // Clear the map to prevent any outdated values to "survive" when
+ // we have to recalculate the new values anyway
+ m_xPageRows->clear();
+
+ // #i123672# use dynamic mem to react on size changes
+ if (m_xPageEndX->size() < static_cast<size_t>(rDoc.MaxCol()) + 1)
+ {
+ m_xPageEndX->resize(rDoc.MaxCol()+1, SCCOL());
+ }
+
+ if (m_aInput.m_bPrintArea)
+ {
+ ScRange aRange(m_aInput.getStartColumn(), m_aInput.getStartRow(), m_aInput.getPrintTab(), m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab());
+ rDoc.UpdatePageBreaks(m_aInput.getPrintTab(), &aRange);
+ }
+ else
+ {
+ rDoc.UpdatePageBreaks(m_aInput.getPrintTab()); // else, end is marked
+ }
+
+ const size_t nRealCnt = m_aInput.getEndRow() - m_aInput.getStartRow() + 1;
+
+ // #i123672# use dynamic mem to react on size changes
+ if (m_xPageEndY->size() < nRealCnt+1)
+ {
+ m_xPageEndY->resize(nRealCnt + 1, SCROW());
+ }
+
+ // Page alignment/splitting after breaks in Col/RowFlags
+ // Of several breaks in a hidden area, only one counts.
+
+ m_nPagesX = 0;
+ m_nPagesY = 0;
+ m_nTotalY = 0;
+
+ bool bVisCol = false;
+ for (SCCOL i = m_aInput.getStartColumn(); i <= m_aInput.getEndColumn(); i++)
+ {
+ bool bHidden = rDoc.ColHidden(i, m_aInput.getPrintTab());
+ bool bPageBreak(rDoc.HasColBreak(i, m_aInput.getPrintTab()) & ScBreakType::Page);
+ if (i > m_aInput.getStartColumn() && bVisCol && bPageBreak)
+ {
+ OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX");
+ (*m_xPageEndX)[m_nPagesX] = i-1;
+ ++m_nPagesX;
+ bVisCol = false;
+ }
+ if (!bHidden)
+ bVisCol = true;
+ }
+ if (bVisCol) // also at the end, no empty pages
+ {
+ OSL_ENSURE(m_nPagesX < m_xPageEndX->size(), "vector access error for aPageEndX");
+ (*m_xPageEndX)[m_nPagesX] = m_aInput.getEndColumn();
+ ++m_nPagesX;
+ }
+
+ bool bVisRow = false;
+ SCROW nPageStartRow = m_aInput.getStartRow();
+ SCROW nLastVisibleRow = -1;
+
+ std::unique_ptr<ScRowBreakIterator> pRowBreakIter(rDoc.GetRowBreakIterator(m_aInput.getPrintTab()));
+ SCROW nNextPageBreak = pRowBreakIter->first();
+ while (nNextPageBreak != ScRowBreakIterator::NOT_FOUND && nNextPageBreak < m_aInput.getStartRow())
+ // Skip until the page break position is at the start row or greater.
+ nNextPageBreak = pRowBreakIter->next();
+
+ for (SCROW nRow = m_aInput.getStartRow(); nRow <= m_aInput.getEndRow(); ++nRow)
+ {
+ bool bPageBreak = (nNextPageBreak == nRow);
+ if (bPageBreak)
+ nNextPageBreak = pRowBreakIter->next();
+
+ if (nRow > m_aInput.getStartRow() && bVisRow && bPageBreak)
+ {
+ OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for rPageEndY");
+ (*m_xPageEndY)[m_nTotalY] = nRow - 1;
+ ++m_nTotalY;
+
+ if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), nRow-1, m_aInput.getPrintTab()))
+ {
+ auto& rPageRow = (*m_xPageRows)[m_nPagesY];
+ rPageRow.SetStartRow(nPageStartRow);
+ rPageRow.SetEndRow(nRow - 1);
+ rPageRow.SetPagesX(m_nPagesX);
+ if (m_aInput.m_bSkipEmpty)
+ lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX);
+ ++m_nPagesY;
+ }
+
+ nPageStartRow = nRow;
+ bVisRow = false;
+ }
+
+ if (nRow <= nLastVisibleRow)
+ {
+ // This row is still visible. Don't bother calling RowHidden() to
+ // find out, for speed optimization.
+ bVisRow = true;
+ continue;
+ }
+
+ SCROW nLastRow = -1;
+ if (!rDoc.RowHidden(nRow, m_aInput.getPrintTab(), nullptr, &nLastRow))
+ {
+ bVisRow = true;
+ nLastVisibleRow = nLastRow;
+ }
+ else
+ {
+ // Skip all hidden rows until next pagebreak.
+ nRow = ((nNextPageBreak == ScRowBreakIterator::NOT_FOUND) ? nLastRow :
+ std::min(nLastRow, nNextPageBreak - 1));
+ }
+ }
+
+ if (!bVisRow)
+ return;
+
+ OSL_ENSURE(m_nTotalY < m_xPageEndY->size(), "vector access error for maPageEndY");
+ (*m_xPageEndY)[m_nTotalY] = m_aInput.getEndRow();
+ ++m_nTotalY;
+
+ if (!m_aInput.m_bSkipEmpty || !rDoc.IsPrintEmpty(m_aInput.getStartColumn(), nPageStartRow, m_aInput.getEndColumn(), m_aInput.getEndRow(), m_aInput.getPrintTab()))
+ {
+ auto& rPageRow = (*m_xPageRows)[m_nPagesY];
+ rPageRow.SetStartRow(nPageStartRow);
+ rPageRow.SetEndRow(m_aInput.getEndRow());
+ rPageRow.SetPagesX(m_nPagesX);
+ if (m_aInput.m_bSkipEmpty)
+ lcl_SetHidden(rDoc, m_aInput.getPrintTab(), rPageRow, m_aInput.getStartColumn(), *m_xPageEndX);
+ ++m_nPagesY;
+ }
+}
+
+} // end namespace sc
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/reffact.cxx b/sc/source/ui/view/reffact.cxx
new file mode 100644
index 0000000000..3834e3b8c3
--- /dev/null
+++ b/sc/source/ui/view/reffact.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 <sfx2/basedlgs.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <reffact.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <acredlin.hxx>
+#include <simpref.hxx>
+#include <scmod.hxx>
+#include <scres.hrc>
+#include <validate.hxx>
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScNameDlgWrapper, FID_DEFINE_NAME)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScNameDefDlgWrapper, FID_ADD_NAME)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScSolverDlgWrapper, SID_OPENDLG_SOLVE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScOptSolverDlgWrapper, SID_OPENDLG_OPTSOLVER)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScXMLSourceDlgWrapper, SID_MANAGE_XML_SOURCE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScPivotLayoutWrapper, SID_OPENDLG_PIVOTTABLE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScTabOpDlgWrapper, SID_OPENDLG_TABOP)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScFilterDlgWrapper, SID_FILTER)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScSpecialFilterDlgWrapper, SID_SPECIAL_FILTER)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScDbNameDlgWrapper, SID_DEFINE_DBNAME)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScConsolidateDlgWrapper, SID_OPENDLG_CONSOLIDATE)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScPrintAreasDlgWrapper, SID_OPENDLG_EDIT_PRINTAREA)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScColRowNameRangesDlgWrapper, SID_DEFINE_COLROWNAMERANGES)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScFormulaDlgWrapper, SID_OPENDLG_FUNCTION)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScAcceptChgDlgWrapper, FID_CHG_ACCEPT)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScHighlightChgDlgWrapper, FID_CHG_SHOW)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScSimpleRefDlgWrapper, WID_SIMPLE_REF)
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(ScCondFormatDlgWrapper, WID_CONDFRMT_REF)
+
+SFX_IMPL_CHILDWINDOW_WITHID(ScValidityRefChildWin, SID_VALIDITY_REFERENCE)
+
+SfxChildWinInfo ScValidityRefChildWin::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ return aInfo;
+}
+
+namespace
+{
+ ScTabViewShell* lcl_GetTabViewShell( const SfxBindings* pBindings );
+}
+
+#define IMPL_CONTROLLER_CHILD_CTOR(Class,sid) \
+ Class::Class( vcl::Window* pParentP, \
+ sal_uInt16 nId, \
+ SfxBindings* p, \
+ const SfxChildWinInfo* pInfo ) \
+ : SfxChildWindow(pParentP, nId) \
+ { \
+ /************************************************************************************/\
+ /* When a new document is creating, the SfxViewFrame may be ready, */\
+ /* But the ScTabViewShell may have not been activated yet. In this */\
+ /* situation, SfxViewShell::Current() does not get the correct shell, */\
+ /* and we should lcl_GetTabViewShell( p ) instead of SfxViewShell::Current() */\
+ /************************************************************************************/\
+ ScTabViewShell* pViewShell = lcl_GetTabViewShell( p ); \
+ if (!pViewShell) \
+ pViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); \
+ OSL_ENSURE( pViewShell, "missing view shell :-(" ); \
+ SetController( pViewShell ? \
+ pViewShell->CreateRefDialogController( p, this, pInfo, pParentP->GetFrameWeld(), sid ) : nullptr ); \
+ if (pViewShell && !GetController()) \
+ pViewShell->GetViewFrame().SetChildWindow( nId, false ); \
+ }
+
+
+IMPL_CONTROLLER_CHILD_CTOR( ScNameDlgWrapper, FID_DEFINE_NAME )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScNameDefDlgWrapper, FID_ADD_NAME )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScSolverDlgWrapper, SID_OPENDLG_SOLVE )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScOptSolverDlgWrapper, SID_OPENDLG_OPTSOLVER )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScXMLSourceDlgWrapper, SID_MANAGE_XML_SOURCE)
+
+IMPL_CONTROLLER_CHILD_CTOR( ScPivotLayoutWrapper, SID_OPENDLG_PIVOTTABLE )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScTabOpDlgWrapper, SID_OPENDLG_TABOP )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScFilterDlgWrapper, SID_FILTER )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScSpecialFilterDlgWrapper, SID_SPECIAL_FILTER )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScDbNameDlgWrapper, SID_DEFINE_DBNAME )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScColRowNameRangesDlgWrapper, SID_DEFINE_COLROWNAMERANGES )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScConsolidateDlgWrapper, SID_OPENDLG_CONSOLIDATE )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScPrintAreasDlgWrapper, SID_OPENDLG_EDIT_PRINTAREA )
+
+IMPL_CONTROLLER_CHILD_CTOR( ScFormulaDlgWrapper, SID_OPENDLG_FUNCTION )
+
+
+// ScSimpleRefDlgWrapper
+
+static bool bScSimpleRefFlag;
+static tools::Long nScSimpleRefHeight;
+static tools::Long nScSimpleRefWidth;
+static tools::Long nScSimpleRefX;
+static tools::Long nScSimpleRefY;
+static bool bAutoReOpen = true;
+
+ScSimpleRefDlgWrapper::ScSimpleRefDlgWrapper( vcl::Window* pParentP,
+ sal_uInt16 nId,
+ SfxBindings* p,
+ SfxChildWinInfo* pInfo )
+ : SfxChildWindow(pParentP, nId)
+{
+
+ ScTabViewShell* pViewShell = nullptr;
+ SfxDispatcher* pDisp = p->GetDispatcher();
+ if ( pDisp )
+ {
+ SfxViewFrame* pViewFrm = pDisp->GetFrame();
+ if ( pViewFrm )
+ pViewShell = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() );
+ }
+
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+
+ if(pInfo!=nullptr && bScSimpleRefFlag)
+ {
+ pInfo->aPos.setX(nScSimpleRefX );
+ pInfo->aPos.setY(nScSimpleRefY );
+ pInfo->aSize.setHeight(nScSimpleRefHeight );
+ pInfo->aSize.setWidth(nScSimpleRefWidth );
+ }
+ SetController(nullptr);
+
+ if (bAutoReOpen && pViewShell)
+ SetController(pViewShell->CreateRefDialogController(p, this, pInfo, pParentP->GetFrameWeld(), WID_SIMPLE_REF));
+
+ if (!GetController())
+ {
+ SC_MOD()->SetRefDialog( nId, false );
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetAutoReOpen(bool bFlag)
+{
+ bAutoReOpen=bFlag;
+}
+
+void ScSimpleRefDlgWrapper::SetRefString(const OUString& rStr)
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetRefString(rStr);
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetCloseHdl( const Link<const OUString*,void>& rLink )
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetCloseHdl(rLink);
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetUnoLinks( const Link<const OUString&,void>& rDone,
+ const Link<const OUString&,void>& rAbort, const Link<const OUString&,void>& rChange )
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetUnoLinks( rDone, rAbort, rChange );
+ }
+}
+
+void ScSimpleRefDlgWrapper::SetFlags( bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection )
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->SetFlags( bCloseOnButtonUp, bSingleCell, bMultiSelection );
+ }
+}
+
+void ScSimpleRefDlgWrapper::StartRefInput()
+{
+ auto xDlgController = GetController();
+ if (xDlgController)
+ {
+ static_cast<ScSimpleRefDlg*>(xDlgController.get())->StartRefInput();
+ }
+}
+
+// ScAcceptChgDlgWrapper //FIXME: should be moved into ViewShell
+
+ScAcceptChgDlgWrapper::ScAcceptChgDlgWrapper(vcl::Window* pParentP,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo ) :
+ SfxChildWindow( pParentP, nId )
+{
+ ScTabViewShell* pViewShell =
+ dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+ if (pViewShell)
+ {
+ auto xDlg = std::make_shared<ScAcceptChgDlg>(pBindings, this, pParentP->GetFrameWeld(), &pViewShell->GetViewData());
+ SetController(xDlg);
+ xDlg->Initialize( pInfo );
+ }
+ else
+ SetController( nullptr );
+ if (pViewShell && !GetController())
+ pViewShell->GetViewFrame().SetChildWindow( nId, false );
+}
+
+void ScAcceptChgDlgWrapper::ReInitDlg()
+{
+ ScTabViewShell* pViewShell =
+ dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+
+ if (GetController() && pViewShell)
+ {
+ static_cast<ScAcceptChgDlg*>(GetController().get())->ReInit(&pViewShell->GetViewData());
+ }
+}
+
+// ScHighlightChgDlgWrapper
+
+IMPL_CONTROLLER_CHILD_CTOR(ScHighlightChgDlgWrapper, FID_CHG_SHOW)
+
+namespace
+{
+ ScTabViewShell * lcl_GetTabViewShell( const SfxBindings *pBindings )
+ {
+ if( pBindings )
+ if( SfxDispatcher* pDisp = pBindings ->GetDispatcher() )
+ if( SfxViewFrame *pFrm = pDisp->GetFrame() )
+ if( SfxViewShell* pViewSh = pFrm->GetViewShell() )
+ return dynamic_cast<ScTabViewShell*>( pViewSh );
+
+ return nullptr;
+ }
+}
+
+ScValidityRefChildWin::ScValidityRefChildWin(vcl::Window* pParentP,
+ sal_uInt16 nId,
+ const SfxBindings* p,
+ SAL_UNUSED_PARAMETER SfxChildWinInfo* /*pInfo*/ )
+ : SfxChildWindow(pParentP, nId)
+ , m_bVisibleLock(false)
+ , m_bFreeWindowLock(false)
+{
+ SetWantsFocus( false );
+ std::shared_ptr<SfxDialogController> xDlg(ScValidationDlg::Find1AliveObject(pParentP->GetFrameWeld()));
+ SetController(xDlg);
+ ScTabViewShell* pViewShell;
+ if (xDlg)
+ pViewShell = static_cast<ScValidationDlg*>(xDlg.get())->GetTabViewShell();
+ else
+ pViewShell = lcl_GetTabViewShell( p );
+ if (!pViewShell)
+ pViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
+ OSL_ENSURE( pViewShell, "missing view shell :-(" );
+ if (pViewShell && !xDlg)
+ pViewShell->GetViewFrame().SetChildWindow( nId, false );
+}
+
+ScValidityRefChildWin::~ScValidityRefChildWin()
+{
+ if (m_bFreeWindowLock)
+ SetController(nullptr);
+}
+
+IMPL_CONTROLLER_CHILD_CTOR( ScCondFormatDlgWrapper, WID_CONDFRMT_REF )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/scextopt.cxx b/sc/source/ui/view/scextopt.cxx
new file mode 100644
index 0000000000..206b7cdc8d
--- /dev/null
+++ b/sc/source/ui/view/scextopt.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 <scextopt.hxx>
+
+#include <osl/diagnose.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+ScExtDocSettings::ScExtDocSettings() :
+ mfTabBarWidth( -1.0 ),
+ mnLinkCnt( 0 ),
+ mnDisplTab( -1 )
+{
+}
+
+ScExtTabSettings::ScExtTabSettings() :
+ maUsedArea( ScAddress::INITIALIZE_INVALID ),
+ maCursor( ScAddress::INITIALIZE_INVALID ),
+ maFirstVis( ScAddress::INITIALIZE_INVALID ),
+ maSecondVis( ScAddress::INITIALIZE_INVALID ),
+ maFreezePos( 0, 0, 0 ),
+ maSplitPos( 0, 0 ),
+ meActivePane( SCEXT_PANE_TOPLEFT ),
+ maGridColor( COL_AUTO ),
+ mnNormalZoom( 0 ),
+ mnPageZoom( 0 ),
+ mbSelected( false ),
+ mbFrozenPanes( false ),
+ mbPageMode( false ),
+ mbShowGrid( true )
+{
+}
+
+namespace {
+
+/** A container for ScExtTabSettings objects.
+ @descr Internally, a std::map with shared pointers to ScExtTabSettings is
+ used. The copy constructor and assignment operator make deep copies of the
+ objects. */
+class ScExtTabSettingsCont
+{
+public:
+ explicit ScExtTabSettingsCont();
+ ScExtTabSettingsCont( const ScExtTabSettingsCont& rSrc );
+ ScExtTabSettingsCont& operator=( const ScExtTabSettingsCont& rSrc );
+
+ const ScExtTabSettings* GetTabSettings( SCTAB nTab ) const;
+ ScExtTabSettings& GetOrCreateTabSettings( SCTAB nTab );
+
+ SCTAB GetLastTab() const;
+
+private:
+ typedef std::shared_ptr< ScExtTabSettings > ScExtTabSettingsRef;
+ typedef ::std::map< SCTAB, ScExtTabSettingsRef > ScExtTabSettingsMap;
+
+ /** Makes a deep copy of all objects in the passed map. */
+ void CopyFromMap( const ScExtTabSettingsMap& rMap );
+
+ ScExtTabSettingsMap maMap;
+};
+
+}
+
+ScExtTabSettingsCont::ScExtTabSettingsCont()
+{
+}
+
+ScExtTabSettingsCont::ScExtTabSettingsCont( const ScExtTabSettingsCont& rSrc )
+{
+ CopyFromMap( rSrc.maMap );
+}
+
+ScExtTabSettingsCont& ScExtTabSettingsCont::operator=( const ScExtTabSettingsCont& rSrc )
+{
+ CopyFromMap( rSrc.maMap );
+ return *this;
+}
+
+const ScExtTabSettings* ScExtTabSettingsCont::GetTabSettings( SCTAB nTab ) const
+{
+ ScExtTabSettingsMap::const_iterator aIt = maMap.find( nTab );
+ return (aIt == maMap.end()) ? nullptr : aIt->second.get();
+}
+
+ScExtTabSettings& ScExtTabSettingsCont::GetOrCreateTabSettings( SCTAB nTab )
+{
+ ScExtTabSettingsRef& rxTabSett = maMap[ nTab ];
+ if( !rxTabSett )
+ rxTabSett = std::make_shared<ScExtTabSettings>();
+ return *rxTabSett;
+}
+
+SCTAB ScExtTabSettingsCont::GetLastTab() const
+{
+ return maMap.empty() ? -1 : maMap.rbegin()->first;
+}
+
+void ScExtTabSettingsCont::CopyFromMap( const ScExtTabSettingsMap& rMap )
+{
+ maMap.clear();
+ for( const auto& [rTab, rxSettings] : rMap )
+ maMap[ rTab ] = std::make_shared<ScExtTabSettings>( *rxSettings );
+}
+
+/** Implementation struct for ScExtDocOptions containing all members. */
+struct ScExtDocOptionsImpl
+{
+ ScExtDocSettings maDocSett; /// Global document settings.
+ ScExtTabSettingsCont maTabSett; /// Settings for all sheets.
+ std::vector< OUString > maCodeNames; /// Codenames for all sheets (VBA module names).
+ bool mbChanged; /// Use only if something has been changed.
+
+ explicit ScExtDocOptionsImpl();
+};
+
+ScExtDocOptionsImpl::ScExtDocOptionsImpl() :
+ mbChanged( false )
+{
+}
+
+ScExtDocOptions::ScExtDocOptions() :
+ mxImpl( new ScExtDocOptionsImpl )
+{
+}
+
+ScExtDocOptions::ScExtDocOptions( const ScExtDocOptions& rSrc ) :
+ mxImpl( new ScExtDocOptionsImpl( *rSrc.mxImpl ) )
+{
+}
+
+ScExtDocOptions::~ScExtDocOptions()
+{
+}
+
+ScExtDocOptions& ScExtDocOptions::operator=( const ScExtDocOptions& rSrc )
+{
+ *mxImpl = *rSrc.mxImpl;
+ return *this;
+}
+
+bool ScExtDocOptions::IsChanged() const
+{
+ return mxImpl->mbChanged;
+}
+
+void ScExtDocOptions::SetChanged( bool bChanged )
+{
+ mxImpl->mbChanged = bChanged;
+}
+
+const ScExtDocSettings& ScExtDocOptions::GetDocSettings() const
+{
+ return mxImpl->maDocSett;
+}
+
+ScExtDocSettings& ScExtDocOptions::GetDocSettings()
+{
+ return mxImpl->maDocSett;
+}
+
+const ScExtTabSettings* ScExtDocOptions::GetTabSettings( SCTAB nTab ) const
+{
+ return mxImpl->maTabSett.GetTabSettings( nTab );
+}
+
+SCTAB ScExtDocOptions::GetLastTab() const
+{
+ return mxImpl->maTabSett.GetLastTab();
+}
+
+ScExtTabSettings& ScExtDocOptions::GetOrCreateTabSettings( SCTAB nTab )
+{
+ return mxImpl->maTabSett.GetOrCreateTabSettings( nTab );
+}
+
+SCTAB ScExtDocOptions::GetCodeNameCount() const
+{
+ return static_cast< SCTAB >( mxImpl->maCodeNames.size() );
+}
+
+OUString ScExtDocOptions::GetCodeName( SCTAB nTab ) const
+{
+ OSL_ENSURE( (0 <= nTab) && (nTab < GetCodeNameCount()), "ScExtDocOptions::GetCodeName - invalid sheet index" );
+ return ((0 <= nTab) && (nTab < GetCodeNameCount())) ? mxImpl->maCodeNames[ static_cast< size_t >( nTab ) ] : OUString();
+}
+
+void ScExtDocOptions::SetCodeName( SCTAB nTab, const OUString& rCodeName )
+{
+ OSL_ENSURE( nTab >= 0, "ScExtDocOptions::SetCodeName - invalid sheet index" );
+ if( nTab >= 0 )
+ {
+ size_t nIndex = static_cast< size_t >( nTab );
+ if( nIndex >= mxImpl->maCodeNames.size() )
+ mxImpl->maCodeNames.resize( nIndex + 1 );
+ mxImpl->maCodeNames[ nIndex ] = rCodeName;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/select.cxx b/sc/source/ui/view/select.cxx
new file mode 100644
index 0000000000..d972c9b4eb
--- /dev/null
+++ b/sc/source/ui/view/select.cxx
@@ -0,0 +1,986 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sfx2/docfile.hxx>
+#include <osl/diagnose.h>
+
+#include <select.hxx>
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <transobj.hxx>
+#include <docsh.hxx>
+#include <tabprotection.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <comphelper/lok.hxx>
+
+#if defined(_WIN32)
+#define SC_SELENG_REFMODE_UPDATE_INTERVAL_MIN 65
+#endif
+
+using namespace com::sun::star;
+
+static Point aSwitchPos; //! Member
+static bool bDidSwitch = false;
+
+// View (Gridwin / keyboard)
+ScViewFunctionSet::ScViewFunctionSet( ScViewData* pNewViewData ) :
+ m_pViewData( pNewViewData ),
+ m_pEngine( nullptr ),
+ m_bAnchor( false ),
+ m_bStarted( false )
+{
+ OSL_ENSURE(m_pViewData, "ViewData==0 at FunctionSet");
+}
+
+ScSplitPos ScViewFunctionSet::GetWhich() const
+{
+ if (m_pEngine)
+ return m_pEngine->GetWhich();
+ else
+ return m_pViewData->GetActivePart();
+}
+
+sal_uInt64 ScViewFunctionSet::CalcUpdateInterval( const Size& rWinSize, const Point& rEffPos,
+ bool bLeftScroll, bool bTopScroll, bool bRightScroll, bool bBottomScroll )
+{
+ sal_uInt64 nUpdateInterval = SELENG_AUTOREPEAT_INTERVAL_MAX;
+ vcl::Window* pWin = m_pEngine->GetWindow();
+ AbsoluteScreenPixelRectangle aScrRect = pWin->GetDesktopRectPixel();
+ AbsoluteScreenPixelPoint aRootPos = pWin->OutputToAbsoluteScreenPixel(Point(0,0));
+ if (bRightScroll)
+ {
+ double nWinRight = rWinSize.getWidth() + aRootPos.getX();
+ double nMarginRight = aScrRect.GetWidth() - nWinRight;
+ double nHOffset = rEffPos.X() - rWinSize.Width();
+ double nHAccelRate = nHOffset / nMarginRight;
+
+ if (nHAccelRate > 1.0)
+ nHAccelRate = 1.0;
+
+ nUpdateInterval = SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nHAccelRate);
+ }
+
+ if (bLeftScroll)
+ {
+ double nMarginLeft = aRootPos.getX();
+ double nHOffset = -rEffPos.X();
+ double nHAccelRate = nHOffset / nMarginLeft;
+
+ if (nHAccelRate > 1.0)
+ nHAccelRate = 1.0;
+
+ sal_uLong nTmp = static_cast<sal_uLong>(SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nHAccelRate));
+ if (nUpdateInterval > nTmp)
+ nUpdateInterval = nTmp;
+ }
+
+ if (bBottomScroll)
+ {
+ double nWinBottom = rWinSize.getHeight() + aRootPos.getY();
+ double nMarginBottom = aScrRect.GetHeight() - nWinBottom;
+ double nVOffset = rEffPos.Y() - rWinSize.Height();
+ double nVAccelRate = nVOffset / nMarginBottom;
+
+ if (nVAccelRate > 1.0)
+ nVAccelRate = 1.0;
+
+ sal_uInt64 nTmp = SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nVAccelRate);
+ if (nUpdateInterval > nTmp)
+ nUpdateInterval = nTmp;
+ }
+
+ if (bTopScroll)
+ {
+ double nMarginTop = aRootPos.getY();
+ double nVOffset = -rEffPos.Y();
+ double nVAccelRate = nVOffset / nMarginTop;
+
+ if (nVAccelRate > 1.0)
+ nVAccelRate = 1.0;
+
+ sal_uInt64 nTmp = SELENG_AUTOREPEAT_INTERVAL_MAX*(1.0 - nVAccelRate);
+ if (nUpdateInterval > nTmp)
+ nUpdateInterval = nTmp;
+ }
+
+#ifdef _WIN32
+ ScTabViewShell* pViewShell = m_pViewData->GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+ if (bRefMode && nUpdateInterval < SC_SELENG_REFMODE_UPDATE_INTERVAL_MIN)
+ // Lower the update interval during ref mode, because re-draw can be
+ // expensive on Windows. Making this interval too small would queue up
+ // the scroll/paint requests which would cause semi-infinite
+ // scrolls even after the mouse cursor is released. We don't have
+ // this problem on Linux.
+ nUpdateInterval = SC_SELENG_REFMODE_UPDATE_INTERVAL_MIN;
+#endif
+ return nUpdateInterval;
+}
+
+void ScViewFunctionSet::SetSelectionEngine( ScViewSelectionEngine* pSelEngine )
+{
+ m_pEngine = pSelEngine;
+}
+
+// Drag & Drop
+void ScViewFunctionSet::BeginDrag()
+{
+ SCTAB nTab = m_pViewData->GetTabNo();
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ if (m_pEngine)
+ {
+ Point aMPos = m_pEngine->GetMousePosPixel();
+ m_pViewData->GetPosFromPixel( aMPos.X(), aMPos.Y(), GetWhich(), nPosX, nPosY );
+ }
+ else
+ {
+ nPosX = m_pViewData->GetCurX();
+ nPosY = m_pViewData->GetCurY();
+ }
+
+ ScModule* pScMod = SC_MOD();
+ bool bRefMode = pScMod->IsFormulaMode();
+ if (bRefMode)
+ return;
+
+ m_pViewData->GetView()->FakeButtonUp( GetWhich() ); // ButtonUp is swallowed
+
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ rMark.MarkToSimple();
+ if ( !rMark.IsMarked() || rMark.IsMultiMarked() )
+ return;
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ // bApi = TRUE -> no error messages
+ bool bCopied = m_pViewData->GetView()->CopyToClip( pClipDoc.get(), false, true );
+ if ( !bCopied )
+ return;
+
+ sal_Int8 nDragActions = m_pViewData->GetView()->SelectionEditable() ?
+ ( DND_ACTION_COPYMOVE | DND_ACTION_LINK ) :
+ ( DND_ACTION_COPY | DND_ACTION_LINK );
+
+ ScDocShell* pDocSh = m_pViewData->GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+
+ // set position of dragged cell within range
+ ScRange aMarkRange = pTransferObj->GetRange();
+ SCCOL nStartX = aMarkRange.aStart.Col();
+ SCROW nStartY = aMarkRange.aStart.Row();
+ SCCOL nHandleX = (nPosX >= nStartX) ? nPosX - nStartX : 0;
+ SCROW nHandleY = (nPosY >= nStartY) ? nPosY - nStartY : 0;
+ pTransferObj->SetDragHandlePos( nHandleX, nHandleY );
+ pTransferObj->SetSourceCursorPos( m_pViewData->GetCurX(), m_pViewData->GetCurY() );
+ pTransferObj->SetVisibleTab( nTab );
+
+ pTransferObj->SetDragSource( pDocSh, rMark );
+
+ vcl::Window* pWindow = m_pViewData->GetActiveWin();
+ if ( pWindow->IsTracking() )
+ pWindow->EndTracking( TrackingEventFlags::Cancel ); // abort selecting
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pWindow->LocalStartDrag();
+
+ SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D
+ pTransferObj->StartDrag( pWindow, nDragActions );
+
+ return; // dragging started
+
+}
+
+// Selection
+void ScViewFunctionSet::CreateAnchor()
+{
+ if (m_bAnchor) return;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ SetAnchor( m_pViewData->GetRefStartX(), m_pViewData->GetRefStartY() );
+ else
+ SetAnchor( m_pViewData->GetCurX(), m_pViewData->GetCurY() );
+}
+
+void ScViewFunctionSet::SetAnchor( SCCOL nPosX, SCROW nPosY )
+{
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ ScTabView* pView = m_pViewData->GetView();
+ SCTAB nTab = m_pViewData->GetTabNo();
+
+ if (bRefMode)
+ {
+ pView->DoneRefMode();
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ pView->InitRefMode( m_aAnchorPos.Col(), m_aAnchorPos.Row(), m_aAnchorPos.Tab(),
+ SC_REFTYPE_REF );
+ m_bStarted = true;
+ }
+ else if (m_pViewData->IsAnyFillMode())
+ {
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ m_bStarted = true;
+ }
+ else
+ {
+ // don't go there and back again
+ if ( m_bStarted && pView->IsMarking( nPosX, nPosY, nTab ) )
+ {
+ // don't do anything
+ }
+ else
+ {
+ pView->DoneBlockMode( true );
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ pView->InitBlockMode( m_aAnchorPos.Col(), m_aAnchorPos.Row(),
+ m_aAnchorPos.Tab(), true );
+ m_bStarted = true;
+ }
+ else
+ m_bStarted = false;
+ }
+ }
+ m_bAnchor = true;
+}
+
+void ScViewFunctionSet::DestroyAnchor()
+{
+ if (m_pViewData->IsAnyFillMode())
+ return;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ m_pViewData->GetView()->DoneRefMode( true );
+ else
+ m_pViewData->GetView()->DoneBlockMode( true );
+
+ m_bAnchor = false;
+}
+
+void ScViewFunctionSet::SetAnchorFlag( bool bSet )
+{
+ m_bAnchor = bSet;
+}
+
+void ScViewFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool /* bDontSelectAtCursor */ )
+{
+ if ( bDidSwitch )
+ {
+ if ( rPointPixel == aSwitchPos )
+ return; // don't scroll in wrong window
+ else
+ bDidSwitch = false;
+ }
+ aSwitchPos = rPointPixel; // only important, if bDidSwitch
+
+ // treat position 0 as -1, so scrolling is always possible
+ // (with full screen and hidden headers, the top left border may be at 0)
+ // (moved from ScViewData::GetPosFromPixel)
+
+ Point aEffPos = rPointPixel;
+ if ( aEffPos.X() == 0 )
+ aEffPos.setX( -1 );
+ if ( aEffPos.Y() == 0 )
+ aEffPos.setY( -1 );
+
+ // Scrolling
+ Size aWinSize = m_pEngine->GetWindow()->GetOutputSizePixel();
+ bool bLeftScroll = ( aEffPos.X() < 0 );
+ bool bTopScroll = ( aEffPos.Y() < 0 );
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ m_pViewData->GetPosFromPixel( aEffPos.X(), aEffPos.Y(), GetWhich(),
+ nPosX, nPosY, true, true ); // with Repair
+
+ tools::Rectangle aEditArea = m_pViewData->GetEditArea(GetWhich(), nPosX, nPosY,
+ m_pEngine->GetWindow(),
+ nullptr, false);
+
+ bool bFillingSelection = m_pViewData->IsFillMode() || m_pViewData->GetFillMode() == ScFillMode::MATRIX;
+ bool bBottomScroll;
+ bool bRightScroll;
+ // for Autofill don't yet assume we want to auto-scroll to the cell under the mouse
+ // because the autofill handle extends into a cells neighbours so initial click is usually
+ // above a neighbour cell
+ if (bFillingSelection)
+ {
+ bBottomScroll = aEffPos.Y() >= aWinSize.Height();
+ bRightScroll = aEffPos.X() >= aWinSize.Width();
+ }
+ else
+ {
+ //in the normal case make the full selected cell visible
+ bBottomScroll = aEditArea.Bottom() >= aWinSize.Height();
+ bRightScroll = aEditArea.Right() >= aWinSize.Width();
+ }
+
+ bool bScroll = bRightScroll || bBottomScroll || bLeftScroll || bTopScroll;
+
+ // for Autofill switch in the center of cell thereby don't prevent scrolling to bottom/right
+ if (bFillingSelection)
+ {
+ bool bLeft, bTop;
+ m_pViewData->GetMouseQuadrant( aEffPos, GetWhich(), nPosX, nPosY, bLeft, bTop );
+ ScDocument& rDoc = m_pViewData->GetDocument();
+ SCTAB nTab = m_pViewData->GetTabNo();
+ if ( bLeft && !bRightScroll )
+ do --nPosX; while ( nPosX>=0 && rDoc.ColHidden( nPosX, nTab ) );
+ if ( bTop && !bBottomScroll )
+ {
+ if (--nPosY >= 0)
+ {
+ nPosY = rDoc.LastVisibleRow(0, nPosY, nTab);
+ if (!rDoc.ValidRow(nPosY))
+ nPosY = -1;
+ }
+ }
+ // negative value is allowed
+ }
+
+ // moved out of fix limit?
+ ScSplitPos eWhich = GetWhich();
+ if ( eWhich == m_pViewData->GetActivePart() )
+ {
+ if ( m_pViewData->GetHSplitMode() == SC_SPLIT_FIX )
+ if ( aEffPos.X() >= aWinSize.Width() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ }
+
+ if ( m_pViewData->GetVSplitMode() == SC_SPLIT_FIX )
+ if ( aEffPos.Y() >= aWinSize.Height() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ {
+ m_pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bScroll = false;
+ bDidSwitch = true;
+ }
+ }
+ }
+
+ if (bScroll)
+ {
+ // Adjust update interval based on how far the mouse pointer is from the edge.
+ sal_uInt64 nUpdateInterval = CalcUpdateInterval(
+ aWinSize, aEffPos, bLeftScroll, bTopScroll, bRightScroll, bBottomScroll);
+ m_pEngine->SetUpdateInterval(nUpdateInterval);
+ }
+ else
+ {
+ // Don't forget to reset the interval when not scrolling!
+ m_pEngine->SetUpdateInterval(SELENG_AUTOREPEAT_INTERVAL);
+ }
+
+ m_pViewData->ResetOldCursor();
+ SetCursorAtCell( nPosX, nPosY, bScroll );
+}
+
+bool ScViewFunctionSet::CheckRefBounds(SCCOL nPosX, SCROW nPosY)
+{
+ SCCOL startX = m_pViewData->GetRefStartX();
+ SCROW startY = m_pViewData->GetRefStartY();
+
+ SCCOL endX = m_pViewData->GetRefEndX();
+ SCROW endY = m_pViewData->GetRefEndY();
+
+ return nPosX >= startX && nPosX <= endX && nPosY >= startY && nPosY <= endY;
+}
+
+bool ScViewFunctionSet::SetCursorAtCell( SCCOL nPosX, SCROW nPosY, bool bScroll )
+{
+ ScTabView* pView = m_pViewData->GetView();
+ SCTAB nTab = m_pViewData->GetTabNo();
+ ScDocument& rDoc = m_pViewData->GetDocument();
+
+ if ( rDoc.IsTabProtected(nTab) )
+ {
+ if (nPosX < 0 || nPosY < 0)
+ return false;
+
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (!pProtect)
+ return false;
+
+ bool bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bool bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+
+ if ( bSkipProtected && bSkipUnprotected )
+ return false;
+
+ bool bCellProtected = rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected);
+ if ( (bCellProtected && bSkipProtected) || (!bCellProtected && bSkipUnprotected) )
+ // Don't select this cell!
+ return false;
+ }
+
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pViewShell = m_pViewData->GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+
+ bool bHide = !bRefMode && !m_pViewData->IsAnyFillMode() &&
+ ( nPosX != m_pViewData->GetCurX() || nPosY != m_pViewData->GetCurY() );
+
+ if (bHide)
+ pView->HideAllCursors();
+
+ if (bScroll)
+ {
+ if (bRefMode)
+ {
+ ScSplitPos eWhich = GetWhich();
+ pView->AlignToCursor( nPosX, nPosY, SC_FOLLOW_LINE, &eWhich );
+ }
+ else
+ pView->AlignToCursor( nPosX, nPosY, SC_FOLLOW_LINE );
+ }
+
+ if (bRefMode)
+ {
+ // if no input is possible from this doc, don't move the reference cursor around
+ if ( !pScMod->IsModalMode(m_pViewData->GetSfxDocShell()) && (!CheckRefBounds(nPosX, nPosY) || SfxLokHelper::getDeviceFormFactor() != LOKDeviceFormFactor::MOBILE))
+ {
+ if (!m_bAnchor)
+ {
+ pView->DoneRefMode( true );
+ pView->InitRefMode( nPosX, nPosY, m_pViewData->GetTabNo(), SC_REFTYPE_REF );
+ }
+
+ if(SfxLokHelper::getDeviceFormFactor() != LOKDeviceFormFactor::MOBILE)
+ pView->UpdateRef( nPosX, nPosY, m_pViewData->GetTabNo() );
+
+ pView->SelectionChanged();
+ }
+ }
+ else if (m_pViewData->IsFillMode() ||
+ (m_pViewData->GetFillMode() == ScFillMode::MATRIX && (nScFillModeMouseModifier & KEY_MOD1) ))
+ {
+ // If a matrix got touched, switch back to Autofill is possible with Ctrl
+
+ SCCOL nStartX, nEndX;
+ SCROW nStartY, nEndY; // Block
+ SCTAB nDummy;
+ m_pViewData->GetSimpleArea( nStartX, nStartY, nDummy, nEndX, nEndY, nDummy );
+
+ if (m_pViewData->GetRefType() != SC_REFTYPE_FILL)
+ {
+ pView->InitRefMode( nStartX, nStartY, nTab, SC_REFTYPE_FILL );
+ CreateAnchor();
+ }
+
+ ScRange aDelRange;
+ bool bOldDelMark = m_pViewData->GetDelMark( aDelRange );
+
+ if ( nPosX+1 >= nStartX && nPosX <= nEndX &&
+ nPosY+1 >= nStartY && nPosY <= nEndY &&
+ ( nPosX != nEndX || nPosY != nEndY ) ) // minimize?
+ {
+ // direction (left or top)
+
+ tools::Long nSizeX = 0;
+ for (SCCOL i=nPosX+1; i<=nEndX; i++)
+ nSizeX += rDoc.GetColWidth( i, nTab );
+ tools::Long nSizeY = rDoc.GetRowHeight( nPosY+1, nEndY, nTab );
+
+ SCCOL nDelStartX = nStartX;
+ SCROW nDelStartY = nStartY;
+ if ( nSizeX > nSizeY )
+ nDelStartX = nPosX + 1;
+ else
+ nDelStartY = nPosY + 1;
+ // there is no need to check for zero, because nPosX/Y is also negative
+
+ if ( nDelStartX < nStartX )
+ nDelStartX = nStartX;
+ if ( nDelStartY < nStartY )
+ nDelStartY = nStartY;
+
+ // set range
+
+ m_pViewData->SetDelMark( ScRange( nDelStartX,nDelStartY,nTab,
+ nEndX,nEndY,nTab ) );
+ m_pViewData->GetView()->UpdateShrinkOverlay();
+
+ m_pViewData->GetView()->
+ PaintArea( nStartX,nDelStartY, nEndX,nEndY, ScUpdateMode::Marks );
+
+ nPosX = nEndX; // keep red border around range
+ nPosY = nEndY;
+
+ // reference the right way up, if it's upside down below
+ if ( nStartX != m_pViewData->GetRefStartX() || nStartY != m_pViewData->GetRefStartY() )
+ {
+ m_pViewData->GetView()->DoneRefMode();
+ m_pViewData->GetView()->InitRefMode( nStartX, nStartY, nTab, SC_REFTYPE_FILL );
+ }
+ }
+ else
+ {
+ if ( bOldDelMark )
+ {
+ m_pViewData->ResetDelMark();
+ m_pViewData->GetView()->UpdateShrinkOverlay();
+ }
+
+ bool bNegX = ( nPosX < nStartX );
+ bool bNegY = ( nPosY < nStartY );
+
+ tools::Long nSizeX = 0;
+ if ( bNegX )
+ {
+ // in SetCursorAtPoint hidden columns are skipped.
+ // They must be skipped here too, or the result will always be the first hidden column.
+ do ++nPosX; while ( nPosX<nStartX && rDoc.ColHidden(nPosX, nTab) );
+ for (SCCOL i=nPosX; i<nStartX; i++)
+ nSizeX += rDoc.GetColWidth( i, nTab );
+ }
+ else
+ for (SCCOL i=nEndX+1; i<=nPosX; i++)
+ nSizeX += rDoc.GetColWidth( i, nTab );
+
+ tools::Long nSizeY = 0;
+ if ( bNegY )
+ {
+ // in SetCursorAtPoint hidden rows are skipped.
+ // They must be skipped here too, or the result will always be the first hidden row.
+ if (++nPosY < nStartY)
+ {
+ nPosY = rDoc.FirstVisibleRow(nPosY, nStartY-1, nTab);
+ if (!rDoc.ValidRow(nPosY))
+ nPosY = nStartY;
+ }
+ nSizeY += rDoc.GetRowHeight( nPosY, nStartY-1, nTab );
+ }
+ else
+ nSizeY += rDoc.GetRowHeight( nEndY+1, nPosY, nTab );
+
+ if ( nSizeX > nSizeY ) // Fill only ever in one direction
+ {
+ nPosY = nEndY;
+ bNegY = false;
+ }
+ else
+ {
+ nPosX = nEndX;
+ bNegX = false;
+ }
+
+ SCCOL nRefStX = bNegX ? nEndX : nStartX;
+ SCROW nRefStY = bNegY ? nEndY : nStartY;
+ if ( nRefStX != m_pViewData->GetRefStartX() || nRefStY != m_pViewData->GetRefStartY() )
+ {
+ m_pViewData->GetView()->DoneRefMode();
+ m_pViewData->GetView()->InitRefMode( nRefStX, nRefStY, nTab, SC_REFTYPE_FILL );
+ }
+ }
+
+ pView->UpdateRef( nPosX, nPosY, nTab );
+ }
+ else if (m_pViewData->IsAnyFillMode())
+ {
+ ScFillMode nMode = m_pViewData->GetFillMode();
+ if ( nMode == ScFillMode::EMBED_LT || nMode == ScFillMode::EMBED_RB )
+ {
+ OSL_ENSURE( rDoc.IsEmbedded(), "!rDoc.IsEmbedded()" );
+ ScRange aRange;
+ rDoc.GetEmbedded( aRange);
+ ScRefType eRefMode = (nMode == ScFillMode::EMBED_LT) ? SC_REFTYPE_EMBED_LT : SC_REFTYPE_EMBED_RB;
+ if (m_pViewData->GetRefType() != eRefMode)
+ {
+ if ( nMode == ScFillMode::EMBED_LT )
+ pView->InitRefMode( aRange.aEnd.Col(), aRange.aEnd.Row(), nTab, eRefMode );
+ else
+ pView->InitRefMode( aRange.aStart.Col(), aRange.aStart.Row(), nTab, eRefMode );
+ CreateAnchor();
+ }
+
+ pView->UpdateRef( nPosX, nPosY, nTab );
+ }
+ else if ( nMode == ScFillMode::MATRIX )
+ {
+ SCCOL nStartX, nEndX;
+ SCROW nStartY, nEndY; // Block
+ SCTAB nDummy;
+ m_pViewData->GetSimpleArea( nStartX, nStartY, nDummy, nEndX, nEndY, nDummy );
+
+ if (m_pViewData->GetRefType() != SC_REFTYPE_FILL)
+ {
+ pView->InitRefMode( nStartX, nStartY, nTab, SC_REFTYPE_FILL );
+ CreateAnchor();
+ }
+
+ if ( nPosX < nStartX ) nPosX = nStartX;
+ if ( nPosY < nStartY ) nPosY = nStartY;
+
+ pView->UpdateRef( nPosX, nPosY, nTab );
+ }
+ // else new modes
+ }
+ else // regular selection
+ {
+ bool bHideCur = m_bAnchor && ( nPosX != m_pViewData->GetCurX() ||
+ nPosY != m_pViewData->GetCurY() );
+ if (bHideCur)
+ pView->HideAllCursors(); // otherwise twice: Block and SetCursor
+
+ if (m_bAnchor)
+ {
+ if (!m_bStarted)
+ {
+ bool bMove = ( nPosX != m_aAnchorPos.Col() ||
+ nPosY != m_aAnchorPos.Row() );
+ if ( bMove || ( m_pEngine && m_pEngine->GetMouseEvent().IsShift() ) )
+ {
+ pView->InitBlockMode( m_aAnchorPos.Col(), m_aAnchorPos.Row(),
+ m_aAnchorPos.Tab(), true );
+ m_bStarted = true;
+ }
+ }
+ if (m_bStarted)
+ // If the selection is already started, don't set the cursor.
+ pView->MarkCursor( nPosX, nPosY, nTab, false, false, true );
+ else
+ pView->SetCursor( nPosX, nPosY );
+ }
+ else
+ {
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ if (rMark.IsMarked() || rMark.IsMultiMarked())
+ {
+ pView->DoneBlockMode(true);
+ pView->InitBlockMode( nPosX, nPosY, nTab, true );
+ pView->MarkCursor( nPosX, nPosY, nTab );
+
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ m_bStarted = true;
+ }
+ // #i3875# *Hack* When a new cell is Ctrl-clicked with no pre-selected cells,
+ // it highlights that new cell as well as the old cell where the cursor is
+ // positioned prior to the click. A selection mode via Shift-F8 should also
+ // follow the same behavior.
+ else if ( m_pViewData->IsSelCtrlMouseClick() )
+ {
+ SCCOL nOldX = m_pViewData->GetCurX();
+ SCROW nOldY = m_pViewData->GetCurY();
+
+ pView->InitBlockMode( nOldX, nOldY, nTab, true );
+ pView->MarkCursor( nOldX, nOldY, nTab );
+
+ if ( nOldX != nPosX || nOldY != nPosY )
+ {
+ pView->DoneBlockMode( true );
+ pView->InitBlockMode( nPosX, nPosY, nTab, true );
+ pView->MarkCursor( nPosX, nPosY, nTab );
+ m_aAnchorPos.Set( nPosX, nPosY, nTab );
+ }
+
+ m_bStarted = true;
+ }
+ pView->SetCursor( nPosX, nPosY );
+ }
+
+ m_pViewData->SetRefStart( nPosX, nPosY, nTab );
+ if (bHideCur)
+ pView->ShowAllCursors();
+ }
+
+ if (bHide)
+ pView->ShowAllCursors();
+
+ return true;
+}
+
+bool ScViewFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
+{
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ return false;
+
+ if (m_pViewData->IsAnyFillMode())
+ return false;
+
+ ScMarkData& rMark = m_pViewData->GetMarkData();
+ if (m_bAnchor || !rMark.IsMultiMarked())
+ {
+ SCCOL nPosX;
+ SCROW nPosY;
+ m_pViewData->GetPosFromPixel( rPointPixel.X(), rPointPixel.Y(), GetWhich(), nPosX, nPosY );
+ return m_pViewData->GetMarkData().IsCellMarked( nPosX, nPosY );
+ }
+
+ return false;
+}
+
+void ScViewFunctionSet::DeselectAtPoint( const Point& /* rPointPixel */ )
+{
+ // doesn't exist
+}
+
+void ScViewFunctionSet::DeselectAll()
+{
+ if (m_pViewData->IsAnyFillMode())
+ return;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (bRefMode)
+ {
+ m_pViewData->GetView()->DoneRefMode();
+ }
+ else
+ {
+ m_pViewData->GetView()->DoneBlockMode();
+ m_pViewData->GetViewShell()->UpdateInputHandler();
+ }
+
+ m_bAnchor = false;
+}
+
+ScViewSelectionEngine::ScViewSelectionEngine( vcl::Window* pWindow, ScTabView* pView,
+ ScSplitPos eSplitPos ) :
+ SelectionEngine( pWindow, &pView->GetFunctionSet() ),
+ eWhich( eSplitPos )
+{
+ SetSelectionMode( SelectionMode::Multiple );
+ EnableDrag( true );
+}
+
+// column and row headers
+ScHeaderFunctionSet::ScHeaderFunctionSet( ScViewData* pNewViewData ) :
+ pViewData( pNewViewData ),
+ bColumn( false ),
+ eWhich( SC_SPLIT_TOPLEFT ),
+ bAnchor( false ),
+ nCursorPos( 0 )
+{
+ OSL_ENSURE(pViewData, "ViewData==0 at FunctionSet");
+}
+
+void ScHeaderFunctionSet::SetColumn( bool bSet )
+{
+ bColumn = bSet;
+}
+
+void ScHeaderFunctionSet::SetWhich( ScSplitPos eNew )
+{
+ eWhich = eNew;
+}
+
+void ScHeaderFunctionSet::BeginDrag()
+{
+ // doesn't exist
+}
+
+void ScHeaderFunctionSet::CreateAnchor()
+{
+ if (bAnchor)
+ return;
+
+ ScTabView* pView = pViewData->GetView();
+ pView->DoneBlockMode( true );
+ if (bColumn)
+ {
+ pView->InitBlockMode( static_cast<SCCOL>(nCursorPos), 0, pViewData->GetTabNo(), true, true );
+ pView->MarkCursor( static_cast<SCCOL>(nCursorPos), pViewData->MaxRow(), pViewData->GetTabNo() );
+ }
+ else
+ {
+ pView->InitBlockMode( 0, nCursorPos, pViewData->GetTabNo(), true, false, true );
+ pView->MarkCursor( pViewData->MaxCol(), nCursorPos, pViewData->GetTabNo() );
+ }
+ bAnchor = true;
+}
+
+void ScHeaderFunctionSet::DestroyAnchor()
+{
+ pViewData->GetView()->DoneBlockMode( true );
+ bAnchor = false;
+}
+
+void ScHeaderFunctionSet::SetCursorAtPoint( const Point& rPointPixel, bool /* bDontSelectAtCursor */ )
+{
+ if ( bDidSwitch )
+ {
+ // next valid position has to be originated from another window
+ if ( rPointPixel == aSwitchPos )
+ return; // don't scroll in the wrong window
+ else
+ bDidSwitch = false;
+ }
+
+ // Scrolling
+ Size aWinSize = pViewData->GetActiveWin()->GetOutputSizePixel();
+ bool bScroll;
+ if (bColumn)
+ bScroll = ( rPointPixel.X() < 0 || rPointPixel.X() >= aWinSize.Width() );
+ else
+ bScroll = ( rPointPixel.Y() < 0 || rPointPixel.Y() >= aWinSize.Height() );
+
+ // moved out of fix limit?
+ bool bSwitched = false;
+ if ( bColumn )
+ {
+ if ( pViewData->GetHSplitMode() == SC_SPLIT_FIX )
+ {
+ if ( rPointPixel.X() > aWinSize.Width() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_TOPRIGHT );
+ bSwitched = true;
+ }
+ else if ( eWhich == SC_SPLIT_BOTTOMLEFT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bSwitched = true;
+ }
+ }
+ }
+ }
+ else // column headers
+ {
+ if ( pViewData->GetVSplitMode() == SC_SPLIT_FIX )
+ {
+ if ( rPointPixel.Y() > aWinSize.Height() )
+ {
+ if ( eWhich == SC_SPLIT_TOPLEFT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ bSwitched = true;
+ }
+ else if ( eWhich == SC_SPLIT_TOPRIGHT )
+ {
+ pViewData->GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ bSwitched = true;
+ }
+ }
+ }
+ }
+ if (bSwitched)
+ {
+ aSwitchPos = rPointPixel;
+ bDidSwitch = true;
+ return; // do not crunch with wrong positions
+ }
+
+ SCCOL nPosX;
+ SCROW nPosY;
+ pViewData->GetPosFromPixel( rPointPixel.X(), rPointPixel.Y(), pViewData->GetActivePart(),
+ nPosX, nPosY, false );
+ if (bColumn)
+ {
+ nCursorPos = static_cast<SCCOLROW>(nPosX);
+ nPosY = pViewData->GetPosY(WhichV(pViewData->GetActivePart()));
+ }
+ else
+ {
+ nCursorPos = static_cast<SCCOLROW>(nPosY);
+ nPosX = pViewData->GetPosX(WhichH(pViewData->GetActivePart()));
+ }
+
+ ScTabView* pView = pViewData->GetView();
+ bool bHide = pViewData->GetCurX() != nPosX ||
+ pViewData->GetCurY() != nPosY;
+ if (bHide)
+ pView->HideAllCursors();
+
+ if (bScroll)
+ pView->AlignToCursor( nPosX, nPosY, SC_FOLLOW_LINE );
+ pView->SetCursor( nPosX, nPosY );
+
+ if ( !bAnchor || !pView->IsBlockMode() )
+ {
+ pView->DoneBlockMode( true );
+ pViewData->GetMarkData().MarkToMulti(); //! who changes this?
+ pView->InitBlockMode( nPosX, nPosY, pViewData->GetTabNo(), true, bColumn, !bColumn );
+
+ bAnchor = true;
+ }
+
+ pView->MarkCursor( nPosX, nPosY, pViewData->GetTabNo(), bColumn, !bColumn );
+
+ // SelectionChanged inside of HideCursor because of UpdateAutoFillMark
+ pView->SelectionChanged();
+
+ if (bHide)
+ pView->ShowAllCursors();
+}
+
+bool ScHeaderFunctionSet::IsSelectionAtPoint( const Point& rPointPixel )
+{
+ SCCOL nPosX;
+ SCROW nPosY;
+ pViewData->GetPosFromPixel( rPointPixel.X(), rPointPixel.Y(), pViewData->GetActivePart(),
+ nPosX, nPosY, false );
+
+ ScMarkData& rMark = pViewData->GetMarkData();
+ if (bColumn)
+ return rMark.IsColumnMarked( nPosX );
+ else
+ return rMark.IsRowMarked( nPosY );
+}
+
+void ScHeaderFunctionSet::DeselectAtPoint( const Point& /* rPointPixel */ )
+{
+}
+
+void ScHeaderFunctionSet::DeselectAll()
+{
+ pViewData->GetView()->DoneBlockMode();
+ bAnchor = false;
+}
+
+ScHeaderSelectionEngine::ScHeaderSelectionEngine( vcl::Window* pWindow, ScHeaderFunctionSet* pFuncSet ) :
+ SelectionEngine( pWindow, pFuncSet )
+{
+ SetSelectionMode( SelectionMode::Multiple );
+ EnableDrag( false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/selectionstate.cxx b/sc/source/ui/view/selectionstate.cxx
new file mode 100644
index 0000000000..91c6a278cb
--- /dev/null
+++ b/sc/source/ui/view/selectionstate.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <selectionstate.hxx>
+
+#include <editeng/editview.hxx>
+#include <viewdata.hxx>
+#include <markdata.hxx>
+
+ScSelectionState::ScSelectionState( ScViewData& rViewData ) :
+ meType( SC_SELECTTYPE_NONE )
+{
+ maCursor.SetTab( rViewData.GetTabNo() );
+ ScSplitPos eWhich = rViewData.GetActivePart();
+
+ if( rViewData.HasEditView( eWhich ) )
+ {
+ meType = SC_SELECTTYPE_EDITCELL;
+ maCursor.SetCol( rViewData.GetEditViewCol() );
+ maCursor.SetRow( rViewData.GetEditViewRow() );
+ maEditSel = rViewData.GetEditView( eWhich )->GetSelection();
+ }
+ else
+ {
+ maCursor.SetCol( rViewData.GetCurX() );
+ maCursor.SetRow( rViewData.GetCurY() );
+
+ ScMarkData& rMarkData = rViewData.GetMarkData();
+ rMarkData.MarkToMulti();
+ if( rMarkData.IsMultiMarked() )
+ {
+ meType = SC_SELECTTYPE_SHEET;
+ }
+ // else type is SC_SELECTTYPE_NONE - already initialized
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/spellcheckcontext.cxx b/sc/source/ui/view/spellcheckcontext.cxx
new file mode 100644
index 0000000000..b18483aa88
--- /dev/null
+++ b/sc/source/ui/view/spellcheckcontext.cxx
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <spellcheckcontext.hxx>
+#include <svl/sharedstring.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/unolingu.hxx>
+
+#include <scitems.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+#include <dpobject.hxx>
+
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+
+#include <o3tl/hash_combine.hxx>
+
+#include <unordered_map>
+
+using namespace css;
+
+using sc::SpellCheckContext;
+
+class SpellCheckContext::SpellCheckCache
+{
+ struct CellPos
+ {
+ struct Hash
+ {
+ size_t operator() (const CellPos& rPos) const
+ {
+ std::size_t seed = 0;
+ o3tl::hash_combine(seed, rPos.mnCol);
+ o3tl::hash_combine(seed, rPos.mnRow);
+ return seed;
+ }
+ };
+
+ SCCOL mnCol;
+ SCROW mnRow;
+
+ CellPos(SCCOL nCol, SCROW nRow) : mnCol(nCol), mnRow(nRow) {}
+
+ bool operator== (const CellPos& r) const
+ {
+ return mnCol == r.mnCol && mnRow == r.mnRow;
+ }
+
+ };
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+ typedef std::unordered_map<CellPos, std::unique_ptr<MisspellType>, CellPos::Hash> CellMapType;
+ typedef std::unordered_map<const rtl_uString*, std::unique_ptr<MisspellType>> SharedStringMapType;
+ typedef std::unordered_map<CellPos, LanguageType, CellPos::Hash> CellLangMapType;
+
+ SharedStringMapType maStringMisspells;
+ CellMapType maEditTextMisspells;
+ CellLangMapType maCellLanguages;
+ LanguageType meDefCellLanguage;
+
+public:
+
+ SpellCheckCache(LanguageType eDefaultCellLanguage) : meDefCellLanguage(eDefaultCellLanguage)
+ {
+ }
+
+ bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, MisspellType*& rpRanges) const
+ {
+ CellType eType = rCell.getType();
+ if (eType == CELLTYPE_STRING)
+ {
+ SharedStringMapType::const_iterator it = maStringMisspells.find(rCell.getSharedString()->getData());
+ if (it == maStringMisspells.end())
+ return false; // Not available
+
+ rpRanges = it->second.get();
+ return true;
+ }
+
+ if (eType == CELLTYPE_EDIT)
+ {
+ CellMapType::const_iterator it = maEditTextMisspells.find(CellPos(nCol, nRow));
+ if (it == maEditTextMisspells.end())
+ return false; // Not available
+
+ rpRanges = it->second.get();
+ return true;
+ }
+
+ rpRanges = nullptr;
+ return true;
+ }
+
+ void set(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, std::unique_ptr<MisspellType> pRanges)
+ {
+ CellType eType = rCell.getType();
+ if (eType == CELLTYPE_STRING)
+ maStringMisspells.insert_or_assign(rCell.getSharedString()->getData(), std::move(pRanges));
+ else if (eType == CELLTYPE_EDIT)
+ maEditTextMisspells.insert_or_assign(CellPos(nCol, nRow), std::move(pRanges));
+ }
+
+ LanguageType getLanguage(SCCOL nCol, SCROW nRow) const
+ {
+ CellLangMapType::const_iterator it = maCellLanguages.find(CellPos(nCol, nRow));
+ if (it == maCellLanguages.end())
+ return meDefCellLanguage;
+
+ return it->second;
+ }
+
+ void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
+ {
+ if (eCellLang == meDefCellLanguage)
+ maCellLanguages.erase(CellPos(nCol, nRow));
+ else
+ maCellLanguages.insert_or_assign(CellPos(nCol, nRow), eCellLang);
+ }
+
+ void clear(LanguageType eDefaultCellLanguage)
+ {
+ maStringMisspells.clear();
+ maEditTextMisspells.clear();
+ maCellLanguages.clear();
+ meDefCellLanguage = eDefaultCellLanguage;
+ }
+
+ void clearEditTextMap()
+ {
+ maEditTextMisspells.clear();
+ }
+};
+
+struct SpellCheckContext::SpellCheckStatus
+{
+ bool mbModified;
+
+ SpellCheckStatus() : mbModified(false) {};
+
+ DECL_LINK( EventHdl, EditStatus&, void );
+};
+
+IMPL_LINK(SpellCheckContext::SpellCheckStatus, EventHdl, EditStatus&, rStatus, void)
+{
+ EditStatusFlags nStatus = rStatus.GetStatusWord();
+ if (nStatus & EditStatusFlags::WRONGWORDCHANGED)
+ mbModified = true;
+}
+
+struct SpellCheckContext::SpellCheckResult
+{
+ SCCOL mnCol;
+ SCROW mnRow;
+ const std::vector<editeng::MisspellRanges>* pRanges;
+
+ SpellCheckResult() : mnCol(-1), mnRow(-1), pRanges(nullptr) {}
+
+ void set(SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pMisspells)
+ {
+ mnCol = nCol;
+ mnRow = nRow;
+ pRanges = pMisspells;
+ }
+
+ const std::vector<editeng::MisspellRanges>* query(SCCOL nCol, SCROW nRow) const
+ {
+ assert(mnCol == nCol);
+ assert(mnRow == nRow);
+ (void)nCol;
+ (void)nRow;
+ return pRanges;
+ }
+
+ void clear()
+ {
+ mnCol = -1;
+ mnRow = -1;
+ pRanges = nullptr;
+ }
+};
+
+SpellCheckContext::SpellCheckContext(ScDocument* pDocument, SCTAB nTab) :
+ pDoc(pDocument),
+ mnTab(nTab),
+ meLanguage(ScGlobal::GetEditDefaultLanguage())
+{
+ // defer init of engine and cache till the first query/set
+}
+
+SpellCheckContext::~SpellCheckContext()
+{
+}
+
+void SpellCheckContext::dispose()
+{
+ mpEngine.reset();
+ mpCache.reset();
+ pDoc = nullptr;
+}
+
+void SpellCheckContext::setTabNo(SCTAB nTab)
+{
+ if (mnTab == nTab)
+ return;
+ mnTab = nTab;
+ reset();
+}
+
+bool SpellCheckContext::isMisspelled(SCCOL nCol, SCROW nRow) const
+{
+ const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
+ return mpResult->query(nCol, nRow);
+}
+
+const std::vector<editeng::MisspellRanges>* SpellCheckContext::getMisspellRanges(
+ SCCOL nCol, SCROW nRow ) const
+{
+ const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
+ return mpResult->query(nCol, nRow);
+}
+
+void SpellCheckContext::setMisspellRanges(
+ SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ if (!mpEngine || !mpCache)
+ reset();
+
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
+ CellType eType = aCell.getType();
+
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return;
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+ std::unique_ptr<MisspellType> pMisspells(pRanges ? new MisspellType(*pRanges) : nullptr);
+ mpCache->set(nCol, nRow, aCell, std::move(pMisspells));
+}
+
+void SpellCheckContext::reset()
+{
+ meLanguage = ScGlobal::GetEditDefaultLanguage();
+ resetCache();
+ mpEngine.reset();
+ mpStatus.reset();
+}
+
+void SpellCheckContext::resetForContentChange()
+{
+ resetCache(true /* bContentChangeOnly */);
+}
+
+void SpellCheckContext::ensureResults(SCCOL nCol, SCROW nRow)
+{
+ if (!mpEngine || !mpCache ||
+ ScGlobal::GetEditDefaultLanguage() != meLanguage)
+ {
+ reset();
+ setup();
+ }
+
+ // perhaps compute the pivot rangelist once in some pivot-table change handler ?
+ if (pDoc->HasPivotTable())
+ {
+ if (ScDPCollection* pDPs = pDoc->GetDPCollection())
+ {
+ ScRangeList aPivotRanges = pDPs->GetAllTableRanges(mnTab);
+ if (aPivotRanges.Contains(ScAddress(nCol, nRow, mnTab))) // Don't spell check within pivot tables
+ {
+ mpResult->set(nCol, nRow, nullptr);
+ return;
+ }
+ }
+ }
+
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
+ CellType eType = aCell.getType();
+
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ {
+ // No spell-check required.
+ mpResult->set(nCol, nRow, nullptr);
+ return;
+ }
+
+
+ // Cell content is either shared-string or EditTextObject
+
+ // For spell-checking, we currently only use the primary
+ // language; not CJK nor CTL.
+ const ScPatternAttr* pPattern = pDoc->GetPattern(nCol, nRow, mnTab);
+ LanguageType eCellLang = pPattern->GetItem(ATTR_FONT_LANGUAGE).GetValue();
+
+ if (eCellLang == LANGUAGE_SYSTEM)
+ eCellLang = meLanguage; // never use SYSTEM for spelling
+
+ if (eCellLang == LANGUAGE_NONE)
+ {
+ mpResult->set(nCol, nRow, nullptr); // No need to spell check this cell.
+ return;
+ }
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+
+ LanguageType eCachedCellLang = mpCache->getLanguage(nCol, nRow);
+
+ if (eCellLang != eCachedCellLang)
+ mpCache->setLanguage(eCellLang, nCol, nRow);
+
+ else
+ {
+ MisspellType* pRanges = nullptr;
+ bool bFound = mpCache->query(nCol, nRow, aCell, pRanges);
+ if (bFound)
+ {
+ // Cache hit.
+ mpResult->set(nCol, nRow, pRanges);
+ return;
+ }
+ }
+
+ // Cache miss, the cell needs spell-check..
+ if (eType == CELLTYPE_STRING)
+ mpEngine->SetText(aCell.getSharedString()->getString());
+ else
+ mpEngine->SetText(*aCell.getEditText());
+
+ // it has to happen after we set text
+ mpEngine->SetDefaultItem(SvxLanguageItem(eCellLang, EE_CHAR_LANGUAGE));
+
+ mpStatus->mbModified = false;
+ mpEngine->CompleteOnlineSpelling();
+ std::unique_ptr<MisspellType> pRanges;
+ if (mpStatus->mbModified)
+ {
+ pRanges.reset(new MisspellType);
+ mpEngine->GetAllMisspellRanges(*pRanges);
+
+ if (pRanges->empty())
+ pRanges.reset(nullptr);
+ }
+ // else : No change in status for EditStatusFlags::WRONGWORDCHANGED => no spell errors (which is the default status).
+
+ mpResult->set(nCol, nRow, pRanges.get());
+ mpCache->set(nCol, nRow, aCell, std::move(pRanges));
+}
+
+void SpellCheckContext::resetCache(bool bContentChangeOnly)
+{
+ if (!mpResult)
+ mpResult.reset(new SpellCheckResult());
+ else
+ mpResult->clear();
+
+ if (!mpCache)
+ mpCache.reset(new SpellCheckCache(meLanguage));
+ else if (bContentChangeOnly)
+ mpCache->clearEditTextMap();
+ else
+ mpCache->clear(meLanguage);
+}
+
+void SpellCheckContext::setup()
+{
+ mpEngine.reset(new ScTabEditEngine(pDoc));
+ mpStatus.reset(new SpellCheckStatus());
+
+ mpEngine->SetControlWord(
+ mpEngine->GetControlWord() | (EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS));
+ mpEngine->SetStatusEventHdl(LINK(mpStatus.get(), SpellCheckStatus, EventHdl));
+ // Delimiters here like in inputhdl.cxx !!!
+ mpEngine->SetWordDelimiters(
+ ScEditUtil::ModifyDelimiters(mpEngine->GetWordDelimiters()));
+
+ uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
+ mpEngine->SetSpeller(xXSpellChecker1);
+ mpEngine->SetDefaultLanguage(meLanguage);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/spelldialog.cxx b/sc/source/ui/view/spelldialog.cxx
new file mode 100644
index 0000000000..da1e90698b
--- /dev/null
+++ b/sc/source/ui/view/spelldialog.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 <spelldialog.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/unolingu.hxx>
+#include <selectionstate.hxx>
+#include <osl/diagnose.h>
+
+#include <spelleng.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <editable.hxx>
+#include <undoblk.hxx>
+#include <gridwin.hxx>
+#include <refupdatecontext.hxx>
+#include <vcl/svapp.hxx>
+
+SFX_IMPL_CHILDWINDOW_WITHID( ScSpellDialogChildWindow, SID_SPELL_DIALOG )
+
+ScSpellDialogChildWindow::ScSpellDialogChildWindow( vcl::Window* pParentP, sal_uInt16 nId,
+ SfxBindings* pBindings, SAL_UNUSED_PARAMETER SfxChildWinInfo* /*pInfo*/ ) :
+ svx::SpellDialogChildWindow( pParentP, nId, pBindings ),
+ mpViewShell( nullptr ),
+ mpViewData( nullptr ),
+ mpDocShell( nullptr ),
+ mpDoc( nullptr ),
+ mbNeedNextObj( false ),
+ mbOldIdleEnabled(true)
+{
+ Init();
+}
+
+ScSpellDialogChildWindow::~ScSpellDialogChildWindow()
+{
+ Reset();
+}
+
+SfxChildWinInfo ScSpellDialogChildWindow::GetInfo() const
+{
+ return svx::SpellDialogChildWindow::GetInfo();
+}
+
+void ScSpellDialogChildWindow::InvalidateSpellDialog()
+{
+ svx::SpellDialogChildWindow::InvalidateSpellDialog();
+}
+
+// protected ------------------------------------------------------------------
+
+svx::SpellPortions ScSpellDialogChildWindow::GetNextWrongSentence( bool /*bRecheck*/ )
+{
+ svx::SpellPortions aPortions;
+ if( mxEngine && mpViewData )
+ {
+ if( EditView* pEditView = mpViewData->GetSpellingView() )
+ {
+ // edit engine handles cell iteration internally
+ do
+ {
+ if( mbNeedNextObj )
+ mxEngine->SpellNextDocument();
+ mbNeedNextObj = !mxEngine->IsFinished() && !mxEngine->SpellSentence( *pEditView, aPortions );
+ }
+ while( mbNeedNextObj );
+ }
+ }
+ return aPortions;
+}
+
+void ScSpellDialogChildWindow::ApplyChangedSentence( const svx::SpellPortions& rChanged, bool bRecheck )
+{
+ if( mxEngine && mpViewData )
+ if( EditView* pEditView = mpViewData->GetSpellingView() )
+ {
+ mxEngine->ApplyChangedSentence( *pEditView, rChanged, bRecheck );
+
+ // Reset the spell checking results to clear the markers.
+ mpViewData->GetActiveWin()->ResetAutoSpell();
+ }
+}
+
+void ScSpellDialogChildWindow::GetFocus()
+{
+ SolarMutexGuard aGuard;
+
+ if( IsSelectionChanged() )
+ {
+ Reset();
+ InvalidateSpellDialog();
+ Init();
+ }
+}
+
+void ScSpellDialogChildWindow::LoseFocus()
+{
+}
+
+// private --------------------------------------------------------------------
+
+void ScSpellDialogChildWindow::Reset()
+{
+ if( mpViewShell && (mpViewShell == dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() )) )
+ {
+ if( mxEngine && mxEngine->IsAnyModified() )
+ {
+ const ScAddress& rCursor = mxOldSel->GetCellCursor();
+ SCTAB nTab = rCursor.Tab();
+ SCCOL nOldCol = rCursor.Col();
+ SCROW nOldRow = rCursor.Row();
+ SCCOL nNewCol = mpViewData->GetCurX();
+ SCROW nNewRow = mpViewData->GetCurY();
+ mpDocShell->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoConversion>(
+ mpDocShell, mpViewData->GetMarkData(),
+ nOldCol, nOldRow, nTab, std::move(mxUndoDoc),
+ nNewCol, nNewRow, nTab, std::move(mxRedoDoc),
+ ScConversionParam( SC_CONVERSION_SPELLCHECK ) ) );
+
+ sc::SetFormulaDirtyContext aCxt;
+ mpDoc->SetAllFormulasDirty(aCxt);
+
+ mpDocShell->SetDocumentModified();
+ }
+
+ mpViewData->SetSpellingView( nullptr );
+ mpViewShell->KillEditView( true );
+ mpDocShell->PostPaintGridAll();
+ mpViewShell->UpdateInputHandler();
+ mpDoc->EnableIdle(mbOldIdleEnabled);
+ }
+ mxEngine.reset();
+ mxUndoDoc.reset();
+ mxRedoDoc.reset();
+ mxOldSel.reset();
+ mxOldRangeList.clear();
+ mpViewShell = nullptr;
+ mpViewData = nullptr;
+ mpDocShell = nullptr;
+ mpDoc = nullptr;
+ mbNeedNextObj = false;
+ mbOldIdleEnabled = true;
+}
+
+void ScSpellDialogChildWindow::Init()
+{
+ if( mpViewShell )
+ return;
+ if( (mpViewShell = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() )) == nullptr )
+ return;
+
+ mpViewData = &mpViewShell->GetViewData();
+
+ // exit edit mode - TODO support spelling in edit mode
+ if( mpViewData->HasEditView( mpViewData->GetActivePart() ) )
+ SC_MOD()->InputEnterHandler();
+
+ mxOldSel.reset( new ScSelectionState( *mpViewData ) );
+
+ mpDocShell = mpViewData->GetDocShell();
+ mpDoc = &mpDocShell->GetDocument();
+
+ const ScAddress& rCursor = mxOldSel->GetCellCursor();
+ SCCOL nCol = rCursor.Col();
+ SCROW nRow = rCursor.Row();
+ SCTAB nTab = rCursor.Tab();
+
+ ScMarkData& rMarkData = mpViewData->GetMarkData();
+
+ mxOldRangeList = new ScRangeList;
+ rMarkData.FillRangeListWithMarks(mxOldRangeList.get(), true);
+
+ rMarkData.MarkToMulti();
+
+ switch( mxOldSel->GetSelectionType() )
+ {
+ case SC_SELECTTYPE_NONE:
+ case SC_SELECTTYPE_SHEET:
+ {
+ // test if there is something editable
+ ScEditableTester aTester( *mpDoc, rMarkData );
+ if( !aTester.IsEditable() )
+ {
+ // #i85751# Don't show an ErrorMessage here, because the vcl
+ // parent of the InfoBox is not fully initialized yet.
+ // This leads to problems in the modality behaviour of the
+ // ScSpellDialogChildWindow.
+
+ //mpViewShell->ErrorMessage( aTester.GetMessageId() );
+ return;
+ }
+ }
+ break;
+
+ // edit mode exited, see TODO above
+// case SC_SELECTTYPE_EDITCELL:
+// break;
+
+ default:
+ OSL_FAIL( "ScSpellDialogChildWindow::Init - unknown selection type" );
+ }
+
+ mbOldIdleEnabled = mpDoc->IsIdleEnabled();
+ mpDoc->EnableIdle(false); // stop online spelling
+
+ // *** create Undo/Redo documents *** -------------------------------------
+
+ mxUndoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ mxUndoDoc->InitUndo( *mpDoc, nTab, nTab );
+ mxRedoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ mxRedoDoc->InitUndo( *mpDoc, nTab, nTab );
+
+ if ( rMarkData.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMarkData)
+ {
+ if( rTab != nTab )
+ {
+ mxUndoDoc->AddUndoTab( rTab, rTab );
+ mxRedoDoc->AddUndoTab( rTab, rTab );
+ }
+ }
+ }
+
+ // *** create and init the edit engine *** --------------------------------
+
+ mxEngine.reset( new ScSpellingEngine(
+ mpDoc->GetEnginePool(), *mpViewData, mxUndoDoc.get(), mxRedoDoc.get(), LinguMgr::GetSpellChecker() ) );
+ mxEngine->SetRefDevice( mpViewData->GetActiveWin()->GetOutDev() );
+
+ mpViewShell->MakeEditView( mxEngine.get(), nCol, nRow );
+ EditView* pEditView = mpViewData->GetEditView( mpViewData->GetActivePart() );
+ mpViewData->SetSpellingView( pEditView );
+ tools::Rectangle aRect( Point( 0, 0 ), Point( 0, 0 ) );
+ pEditView->SetOutputArea( aRect );
+ mxEngine->SetControlWord( EEControlBits::USECHARATTRIBS );
+ mxEngine->EnableUndo( false );
+ mxEngine->SetPaperSize( aRect.GetSize() );
+ mxEngine->SetTextCurrentDefaults( OUString() );
+ mxEngine->ClearModifyFlag();
+
+ mbNeedNextObj = true;
+}
+
+bool ScSpellDialogChildWindow::IsSelectionChanged()
+{
+ if (!mxOldRangeList || !mpViewShell
+ || (mpViewShell != dynamic_cast<ScTabViewShell*>(SfxViewShell::Current())))
+ return true;
+
+ if( EditView* pEditView = mpViewData->GetSpellingView() )
+ if( pEditView->GetEditEngine() != mxEngine.get() )
+ return true;
+
+ ScRangeList aCurrentRangeList;
+ mpViewData->GetMarkData().FillRangeListWithMarks(&aCurrentRangeList, true);
+
+ return (*mxOldRangeList != aCurrentRangeList);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/spelleng.cxx b/sc/source/ui/view/spelleng.cxx
new file mode 100644
index 0000000000..ae50d82930
--- /dev/null
+++ b/sc/source/ui/view/spelleng.cxx
@@ -0,0 +1,448 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <spelleng.hxx>
+#include <com/sun/star/i18n/TextConversionOption.hpp>
+
+#include <scitems.hxx>
+
+#include <editeng/langitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/eeitem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <utility>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+
+#include <spelldialog.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <cellvalue.hxx>
+#include <cellform.hxx>
+#include <patattr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <markdata.hxx>
+#include <docpool.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+ScConversionEngineBase::ScConversionEngineBase(
+ SfxItemPool* pEnginePoolP, ScViewData& rViewData,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc ) :
+ ScEditEngineDefaulter( pEnginePoolP ),
+ mrViewData( rViewData ),
+ mrDocShell( *rViewData.GetDocShell() ),
+ mrDoc( rViewData.GetDocShell()->GetDocument() ),
+ maSelState( rViewData ),
+ mpUndoDoc( pUndoDoc ),
+ mpRedoDoc( pRedoDoc ),
+ meCurrLang( LANGUAGE_ENGLISH_US ),
+ mbIsAnyModified( false ),
+ mbInitialState( true ),
+ mbWrappedInTable( false ),
+ mbFinished( false )
+{
+ maSelState.GetCellCursor().GetVars( mnStartCol, mnStartRow, mnStartTab );
+ // start with cell A1 in cell/range/multi-selection, will seek to first selected
+ if( maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET )
+ {
+ mnStartCol = 0;
+ mnStartRow = 0;
+ }
+ mnCurrCol = mnStartCol;
+ mnCurrRow = mnStartRow;
+}
+
+ScConversionEngineBase::~ScConversionEngineBase()
+{
+}
+
+bool ScConversionEngineBase::FindNextConversionCell()
+{
+ ScMarkData& rMark = mrViewData.GetMarkData();
+ ScTabViewShell* pViewShell = mrViewData.GetViewShell();
+ const ScPatternAttr* pPattern = nullptr;
+ const ScPatternAttr* pLastPattern = nullptr;
+
+ SfxItemSet aEditDefaults(GetEmptyItemSet());
+
+ if( IsModified() )
+ {
+ mbIsAnyModified = true;
+
+ OUString aNewStr = GetText();
+
+ // Check if the user has changed the language. If the new language is
+ // applied to the entire string length, we will set the language as cell
+ // attribute. Otherwise we will commit this as an edit-engine string.
+ editeng::LanguageSpan aLang = GetLanguage(0, 0);
+
+ bool bSimpleString = GetParagraphCount() == 1 &&
+ aLang.nLang != LANGUAGE_DONTKNOW &&
+ aLang.nStart == 0 &&
+ aLang.nEnd == aNewStr.getLength();
+
+ bool bMultiTab = (rMark.GetSelectCount() > 1);
+
+ OUString aVisibleStr;
+ if( bMultiTab )
+ aVisibleStr = mrDoc.GetString(mnCurrCol, mnCurrRow, mnStartTab);
+
+ for( SCTAB nTab = 0, nTabCount = mrDoc.GetTableCount(); nTab < nTabCount; ++nTab )
+ {
+ // always change the cell on the visible tab,
+ // on the other selected tabs only if they contain the same text
+
+ if ((nTab == mnStartTab) ||
+ (bMultiTab && rMark.GetTableSelect(nTab) && mrDoc.GetString(mnCurrCol, mnCurrRow, nTab) == aVisibleStr))
+ {
+ ScAddress aPos( mnCurrCol, mnCurrRow, nTab );
+ CellType eCellType = mrDoc.GetCellType( aPos );
+ bool bEmptyCell = eCellType == CELLTYPE_NONE;
+
+ if (mpUndoDoc && !bEmptyCell)
+ mrDoc.CopyCellToDocument(aPos, aPos, *mpUndoDoc);
+
+ if (!bSimpleString || eCellType == CELLTYPE_EDIT)
+ {
+ std::unique_ptr<EditTextObject> pEditObj(CreateTextObject());
+ mrDoc.SetEditText(aPos, *pEditObj, GetEditTextObjectPool());
+ }
+ else
+ {
+ // Set the new string and update the language with the cell.
+ mrDoc.SetString(aPos, aNewStr);
+
+ const ScPatternAttr* pAttr = mrDoc.GetPattern(aPos);
+ std::unique_ptr<ScPatternAttr> pNewAttr;
+
+ if (pAttr)
+ pNewAttr = std::make_unique<ScPatternAttr>(*pAttr);
+ else
+ pNewAttr = std::make_unique<ScPatternAttr>(mrDoc.GetPool());
+
+ pNewAttr->GetItemSet().Put(SvxLanguageItem(aLang.nLang, EE_CHAR_LANGUAGE), ATTR_FONT_LANGUAGE);
+ mrDoc.SetPattern(aPos, std::move(pNewAttr));
+ }
+
+ if (mpRedoDoc && !bEmptyCell)
+ mrDoc.CopyCellToDocument(aPos, aPos, *mpRedoDoc);
+
+ mrDocShell.PostPaintCell(aPos);
+ }
+ }
+ }
+
+ SCCOL nNewCol = mnCurrCol;
+ SCROW nNewRow = mnCurrRow;
+
+ if( mbInitialState )
+ {
+ /* On very first call, decrement row to let GetNextSpellingCell() find
+ the first cell of current range. */
+ mbInitialState = false;
+ --nNewRow;
+ }
+
+ bool bSheetSel = maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET;
+ bool bLoop = true;
+ bool bFound = false;
+ while( bLoop && !bFound )
+ {
+ bLoop = mrDoc.GetNextSpellingCell( nNewCol, nNewRow, mnStartTab, bSheetSel, rMark );
+ if( bLoop )
+ {
+ FillFromCell( mnCurrCol, mnCurrRow, mnStartTab );
+
+ if( mbWrappedInTable && ((nNewCol > mnStartCol) || ((nNewCol == mnStartCol) && (nNewRow >= mnStartRow))) )
+ {
+ ShowFinishDialog();
+ bLoop = false;
+ mbFinished = true;
+ }
+ else if( nNewCol >= mrDoc.GetAllocatedColumnsCount(mnStartTab) )
+ {
+ // no more cells in the sheet - try to restart at top of sheet
+
+ if( bSheetSel || ((mnStartCol == 0) && (mnStartRow == 0)) )
+ {
+ // conversion started at cell A1 or in selection, do not query to restart at top
+ ShowFinishDialog();
+ bLoop = false;
+ mbFinished = true;
+ }
+ else if( ShowTableWrapDialog() )
+ {
+ // conversion started anywhere but in cell A1, user wants to restart
+ nNewRow = mrDoc.MaxRow() + 2;
+ mbWrappedInTable = true;
+ }
+ else
+ {
+ bLoop = false;
+ mbFinished = true;
+ }
+ }
+ else
+ {
+ // GetPattern may implicitly allocates the column if not exists,
+ pPattern = mrDoc.GetPattern( nNewCol, nNewRow, mnStartTab );
+ if( pPattern && !SfxPoolItem::areSame(pPattern, pLastPattern) )
+ {
+ pPattern->FillEditItemSet( &aEditDefaults );
+ SetDefaults( aEditDefaults );
+ pLastPattern = pPattern;
+ }
+
+ // language changed?
+ const SfxPoolItem* pItem = mrDoc.GetAttr( nNewCol, nNewRow, mnStartTab, ATTR_FONT_LANGUAGE );
+ if( const SvxLanguageItem* pLangItem = dynamic_cast<const SvxLanguageItem*>( pItem ) )
+ {
+ LanguageType eLang = pLangItem->GetValue();
+ if( eLang == LANGUAGE_SYSTEM )
+ eLang = Application::GetSettings().GetLanguageTag().getLanguageType(); // never use SYSTEM for spelling
+ if( eLang != meCurrLang )
+ {
+ meCurrLang = eLang;
+ SetDefaultLanguage( eLang );
+ }
+ }
+
+ FillFromCell( nNewCol, nNewRow, mnStartTab );
+
+ bFound = bLoop && NeedsConversion();
+ }
+ }
+ }
+
+ if( bFound )
+ {
+ pViewShell->AlignToCursor( nNewCol, nNewRow, SC_FOLLOW_JUMP );
+ pViewShell->SetCursor( nNewCol, nNewRow, true );
+ mrViewData.GetView()->MakeEditView( this, nNewCol, nNewRow );
+ EditView* pEditView = mrViewData.GetSpellingView();
+ // maSelState.GetEditSelection() returns (0,0) if not in edit mode -> ok
+ pEditView->SetSelection( maSelState.GetEditSelection() );
+
+ ClearModifyFlag();
+ mnCurrCol = nNewCol;
+ mnCurrRow = nNewRow;
+ }
+
+ return bFound;
+}
+
+void ScConversionEngineBase::RestoreCursorPos()
+{
+ const ScAddress& rPos = maSelState.GetCellCursor();
+ mrViewData.GetViewShell()->SetCursor( rPos.Col(), rPos.Row() );
+}
+
+bool ScConversionEngineBase::ShowTableWrapDialog()
+{
+ // default: no dialog, always restart at top
+ return true;
+}
+
+void ScConversionEngineBase::ShowFinishDialog()
+{
+ // default: no dialog
+}
+
+// private --------------------------------------------------------------------
+
+void ScConversionEngineBase::FillFromCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScAddress aPos(nCol, nRow, nTab);
+
+ ScRefCellValue aCell(mrDoc, aPos);
+ switch (aCell.getType())
+ {
+ case CELLTYPE_STRING:
+ {
+ SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
+ sal_uInt32 nNumFmt = mrDoc.GetNumberFormat(aPos);
+ const Color* pColor;
+ OUString aText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, mrDoc);
+
+ SetTextCurrentDefaults(aText);
+ }
+ break;
+ case CELLTYPE_EDIT:
+ {
+ const EditTextObject* pNewEditObj = aCell.getEditText();
+ SetTextCurrentDefaults(*pNewEditObj);
+ }
+ break;
+ default:
+ SetTextCurrentDefaults(OUString());
+ }
+}
+
+ScSpellingEngine::ScSpellingEngine(
+ SfxItemPool* pEnginePoolP, ScViewData& rViewData,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc,
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > const & xSpeller ) :
+ ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc )
+{
+ SetSpeller( xSpeller );
+}
+
+void ScSpellingEngine::ConvertAll(weld::Widget* pDialogParent, EditView& rEditView)
+{
+ EESpellState eState = EESpellState::Ok;
+ if( FindNextConversionCell() )
+ eState = rEditView.StartSpeller(pDialogParent, true);
+
+ OSL_ENSURE( eState != EESpellState::NoSpeller, "ScSpellingEngine::Convert - no spell checker" );
+}
+
+bool ScSpellingEngine::SpellNextDocument()
+{
+ return FindNextConversionCell();
+}
+
+bool ScSpellingEngine::NeedsConversion()
+{
+ return HasSpellErrors() != EESpellState::Ok;
+}
+
+bool ScSpellingEngine::ShowTableWrapDialog()
+{
+ weld::Widget* pParent = GetDialogParent();
+ weld::WaitObject aWaitOff(pParent);
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Question, VclButtonsType::YesNo,
+ ScResId(STR_SPELLING_BEGIN_TAB))); // "delete data?"
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0));
+ xBox->set_default_response(RET_YES);
+ return xBox->run() == RET_YES;
+}
+
+void ScSpellingEngine::ShowFinishDialog()
+{
+ weld::Widget* pParent = GetDialogParent();
+ weld::WaitObject aWaitOff(pParent);
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(STR_SPELLING_STOP_OK)));
+ xInfoBox->run();
+}
+
+weld::Widget* ScSpellingEngine::GetDialogParent()
+{
+ sal_uInt16 nWinId = ScSpellDialogChildWindow::GetChildWindowId();
+ SfxViewFrame& rViewFrm = mrViewData.GetViewShell()->GetViewFrame();
+ if( rViewFrm.HasChildWindow( nWinId ) )
+ {
+ if( SfxChildWindow* pChild = rViewFrm.GetChildWindow( nWinId ) )
+ {
+ auto xController = pChild->GetController();
+ if (xController)
+ {
+ if (weld::Window *pRet = xController->getDialog())
+ {
+ if (pRet->get_visible())
+ return pRet;
+ }
+ }
+ }
+ }
+
+ // fall back to standard dialog parent
+ return ScDocShell::GetActiveDialogParent();
+}
+
+ScConversionParam::ScConversionParam( ScConversionType eConvType ) :
+ meConvType( eConvType ),
+ meSourceLang( LANGUAGE_NONE ),
+ meTargetLang( LANGUAGE_NONE ),
+ mnOptions( 0 ),
+ mbUseTargetFont( false ),
+ mbIsInteractive( false )
+{
+}
+
+ScConversionParam::ScConversionParam( ScConversionType eConvType,
+ LanguageType eLang, sal_Int32 nOptions, bool bIsInteractive ) :
+ meConvType( eConvType ),
+ meSourceLang( eLang ),
+ meTargetLang( eLang ),
+ mnOptions( nOptions ),
+ mbUseTargetFont( false ),
+ mbIsInteractive( bIsInteractive )
+{
+ if (LANGUAGE_KOREAN == eLang)
+ mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+}
+
+ScConversionParam::ScConversionParam( ScConversionType eConvType,
+ LanguageType eSourceLang, LanguageType eTargetLang, vcl::Font aTargetFont,
+ sal_Int32 nOptions, bool bIsInteractive ) :
+ meConvType( eConvType ),
+ meSourceLang( eSourceLang ),
+ meTargetLang( eTargetLang ),
+ maTargetFont(std::move( aTargetFont )),
+ mnOptions( nOptions ),
+ mbUseTargetFont( true ),
+ mbIsInteractive( bIsInteractive )
+{
+ if (LANGUAGE_KOREAN == meSourceLang && LANGUAGE_KOREAN == meTargetLang)
+ mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+}
+
+ScTextConversionEngine::ScTextConversionEngine(
+ SfxItemPool* pEnginePoolP, ScViewData& rViewData,
+ ScConversionParam aConvParam,
+ ScDocument* pUndoDoc, ScDocument* pRedoDoc ) :
+ ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc ),
+ maConvParam(std::move( aConvParam ))
+{
+}
+
+void ScTextConversionEngine::ConvertAll(weld::Widget* pDialogParent, EditView& rEditView)
+{
+ if( FindNextConversionCell() )
+ {
+ rEditView.StartTextConversion(pDialogParent,
+ maConvParam.GetSourceLang(), maConvParam.GetTargetLang(), maConvParam.GetTargetFont(),
+ maConvParam.GetOptions(), maConvParam.IsInteractive(), true );
+ // #i34769# restore initial cursor position
+ RestoreCursorPos();
+ }
+}
+
+bool ScTextConversionEngine::ConvertNextDocument()
+{
+ return FindNextConversionCell();
+}
+
+bool ScTextConversionEngine::NeedsConversion()
+{
+ return HasConvertibleTextPortion( maConvParam.GetSourceLang() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabcont.cxx b/sc/source/ui/view/tabcont.cxx
new file mode 100644
index 0000000000..12fc1a7a6c
--- /dev/null
+++ b/sc/source/ui/view/tabcont.cxx
@@ -0,0 +1,666 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <tabcont.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <transobj.hxx>
+#include <clipparam.hxx>
+#include <dragdata.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+
+ScTabControl::ScTabControl( vcl::Window* pParent, ScViewData* pData )
+ : TabBar(pParent, WB_3DLOOK | WB_MINSCROLL | WB_SCROLL | WB_RANGESELECT | WB_MULTISELECT | WB_DRAG, true)
+ , DropTargetHelper(this)
+ , DragSourceHelper(this)
+ , pViewData(pData)
+ , nMouseClickPageId(TabBar::PAGE_NOT_FOUND)
+ , nSelPageIdByMouse(TabBar::PAGE_NOT_FOUND)
+ , bErrorShown(false)
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+
+ OUString aString;
+ Color aTabBgColor;
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ if (rDoc.GetName(i,aString))
+ {
+ if ( rDoc.IsScenario(i) )
+ InsertPage( static_cast<sal_uInt16>(i)+1, aString, TabBarPageBits::Blue);
+ else
+ InsertPage( static_cast<sal_uInt16>(i)+1, aString );
+
+ if ( rDoc.IsTabProtected(i) )
+ SetProtectionSymbol(static_cast<sal_uInt16>(i)+1, true);
+
+ if ( !rDoc.IsDefaultTabBgColor(i) )
+ {
+ aTabBgColor = rDoc.GetTabBgColor(i);
+ SetTabBgColor( static_cast<sal_uInt16>(i)+1, aTabBgColor );
+ }
+ }
+ }
+ }
+
+ SetCurPageId( static_cast<sal_uInt16>(pViewData->GetTabNo()) + 1 );
+
+ SetSizePixel( Size(SC_TABBAR_DEFWIDTH, 0) );
+
+ SetSplitHdl( LINK( pViewData->GetView(), ScTabView, TabBarResize ) );
+
+ EnableEditMode();
+ UpdateInputContext();
+
+ SetScrollAlwaysEnabled(false);
+
+ SetScrollAreaContextHdl( LINK( this, ScTabControl, ShowPageList ) );
+}
+
+IMPL_LINK(ScTabControl, ShowPageList, const CommandEvent &, rEvent, void)
+{
+ tools::Rectangle aRect(rEvent.GetMousePosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/scalc/ui/pagelistmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+
+ sal_uInt16 nCurPageId = GetCurPageId();
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ SCTAB nCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount; ++i)
+ {
+ if (!rDoc.IsVisible(i))
+ continue;
+ OUString aString;
+ if (!rDoc.GetName(i, aString))
+ continue;
+ sal_uInt16 nId = static_cast<sal_uInt16>(i)+1;
+ OUString sId = OUString::number(nId);
+ xPopup->append_radio(sId, aString);
+ if (nId == nCurPageId)
+ xPopup->set_active(sId, true);
+ }
+
+ OUString sIdent(xPopup->popup_at_rect(pPopupParent, aRect));
+ if (!sIdent.isEmpty())
+ SwitchToPageId(sIdent.toUInt32());
+}
+
+ScTabControl::~ScTabControl()
+{
+ disposeOnce();
+}
+
+void ScTabControl::dispose()
+{
+ DragSourceHelper::dispose();
+ DropTargetHelper::dispose();
+ TabBar::dispose();
+}
+
+sal_uInt16 ScTabControl::GetMaxId() const
+{
+ sal_uInt16 nVisCnt = GetPageCount();
+ if (nVisCnt)
+ return GetPageId(nVisCnt-1);
+
+ return 0;
+}
+
+SCTAB ScTabControl::GetPrivatDropPos(const Point& rPos )
+{
+ sal_uInt16 nPos = ShowDropPos(rPos);
+
+ SCTAB nRealPos = static_cast<SCTAB>(nPos);
+
+ if(nPos !=0 )
+ {
+ ScDocument& rDoc = pViewData->GetDocument();
+
+ SCTAB nCount = rDoc.GetTableCount();
+
+ sal_uInt16 nViewPos=0;
+ nRealPos = nCount;
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ nViewPos++;
+ if(nViewPos==nPos)
+ {
+ SCTAB j;
+ for (j=i+1; j<nCount; j++)
+ {
+ if (rDoc.IsVisible(j))
+ {
+ break;
+ }
+ }
+ nRealPos =j;
+ break;
+ }
+ }
+ }
+ }
+ return nRealPos ;
+}
+
+void ScTabControl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ ScModule* pScMod = SC_MOD();
+ if ( !pScMod->IsModalMode() && !pScMod->IsFormulaMode() && !IsInEditMode() )
+ {
+ // activate View
+ pViewData->GetViewShell()->SetActive(); // Appear and SetViewFrame
+ pViewData->GetView()->ActiveGrabFocus();
+ }
+
+ if (rMEvt.IsLeft() && rMEvt.GetModifier() == 0)
+ nMouseClickPageId = GetPageId(rMEvt.GetPosPixel());
+
+ TabBar::MouseButtonDown( rMEvt );
+}
+
+void ScTabControl::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ Point aPos = PixelToLogic( rMEvt.GetPosPixel() );
+
+ // mouse button down and up on same page?
+ if( nMouseClickPageId != GetPageId(aPos))
+ nMouseClickPageId = TabBar::PAGE_NOT_FOUND;
+
+ if ( rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && nMouseClickPageId != 0 && nMouseClickPageId != TabBar::PAGE_NOT_FOUND )
+ {
+ SfxDispatcher* pDispatcher = pViewData->GetViewShell()->GetViewFrame().GetDispatcher();
+ pDispatcher->Execute( FID_TAB_MENU_RENAME, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ return;
+ }
+
+ if( nMouseClickPageId == 0 )
+ {
+ // Click in the area next to the existing tabs:
+ SfxDispatcher* pDispatcher = pViewData->GetViewShell()->GetViewFrame().GetDispatcher();
+ pDispatcher->Execute( FID_TAB_DESELECTALL, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ // forget page ID, to be really sure that the dialog is not called twice
+ nMouseClickPageId = TabBar::PAGE_NOT_FOUND;
+ }
+
+ TabBar::MouseButtonUp( rMEvt );
+}
+
+void ScTabControl::AddTabClick()
+{
+ TabBar::AddTabClick();
+
+ // Insert a new sheet at the right end, with default name.
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScModule* pScMod = SC_MOD();
+ if (!rDoc.IsDocEditable() || pScMod->IsTableLocked())
+ return;
+
+ // auto-accept any in-process input - which would otherwise end up on the new sheet
+ if (!pScMod->IsFormulaMode())
+ pScMod->InputEnterHandler();
+
+ OUString aName;
+ rDoc.CreateValidTabName(aName);
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pViewData->GetViewShell()->InsertTable(aName, nTabCount);
+}
+
+void ScTabControl::Select()
+{
+ /* Remember last clicked page ID. */
+ nSelPageIdByMouse = nMouseClickPageId;
+ /* Reset nMouseClickPageId, so that next Select() call may invalidate
+ nSelPageIdByMouse (i.e. if called from keyboard). */
+ nMouseClickPageId = TabBar::PAGE_NOT_FOUND;
+
+ ScModule* pScMod = SC_MOD();
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScMarkData& rMark = pViewData->GetMarkData();
+ SCTAB nCount = rDoc.GetTableCount();
+ SCTAB i;
+
+ if ( pScMod->IsTableLocked() ) // may not be switched now ?
+ {
+ // restore the old state of TabControls
+
+ for (i=0; i<nCount; i++)
+ SelectPage( static_cast<sal_uInt16>(i)+1, rMark.GetTableSelect(i) );
+ SetCurPageId( static_cast<sal_uInt16>(pViewData->GetTabNo()) + 1 );
+
+ return;
+ }
+
+ sal_uInt16 nCurId = GetCurPageId();
+ if (!nCurId) return; // for Excel import it can happen that everything is hidden
+ sal_uInt16 nPage = nCurId - 1;
+
+ // OLE-inplace deactivate
+ if ( nPage != static_cast<sal_uInt16>(pViewData->GetTabNo()) )
+ pViewData->GetView()->DrawMarkListHasChanged();
+
+ // InputEnterHandler onlw when not reference input
+
+ bool bRefMode = pScMod->IsFormulaMode();
+ if (!bRefMode)
+ pScMod->InputEnterHandler();
+
+ for (i=0; i<nCount; i++)
+ rMark.SelectTable( i, IsPageSelected(static_cast<sal_uInt16>(i)+1) );
+
+ SfxDispatcher& rDisp = pViewData->GetDispatcher();
+ if (rDisp.IsLocked())
+ pViewData->GetView()->SetTabNo( static_cast<SCTAB>(nPage) );
+ else
+ {
+ // sheet for basic is 1-based
+ SfxUInt16Item aItem( SID_CURRENTTAB, nPage + 1 );
+ rDisp.ExecuteList(SID_CURRENTTAB,
+ SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem });
+ }
+
+ SfxBindings& rBind = pViewData->GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+
+ rBind.Invalidate( FID_INS_TABLE );
+ rBind.Invalidate( FID_TAB_APPEND );
+ rBind.Invalidate( FID_TAB_MOVE );
+ rBind.Invalidate( FID_TAB_DUPLICATE );
+ rBind.Invalidate( FID_TAB_RENAME );
+ rBind.Invalidate( FID_DELETE_TABLE );
+ rBind.Invalidate( FID_TABLE_SHOW );
+ rBind.Invalidate( FID_TABLE_HIDE );
+ rBind.Invalidate( FID_TAB_SET_TAB_BG_COLOR );
+
+ // Recalculate status bar functions.
+ rBind.Invalidate( SID_TABLE_CELL );
+
+ // SetReference onlw when the consolidate dialog is open
+ // (for references over multiple sheets)
+ // for others this is only needed fidgeting
+
+ if ( bRefMode && pViewData->GetRefType() == SC_REFTYPE_REF )
+ if ( pViewData->GetViewShell()->GetViewFrame().HasChildWindow(SID_OPENDLG_CONSOLIDATE) )
+ {
+ ScRange aRange(
+ pViewData->GetRefStartX(), pViewData->GetRefStartY(), pViewData->GetRefStartZ(),
+ pViewData->GetRefEndX(), pViewData->GetRefEndY(), pViewData->GetRefEndZ() );
+ pScMod->SetReference( aRange, rDoc, &rMark );
+ pScMod->EndReference(); // due to Auto-Hide
+ }
+}
+
+void ScTabControl::UpdateInputContext()
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+ WinBits nStyle = GetStyle();
+ if (rDoc.GetDocumentShell()->IsReadOnly())
+ // no insert sheet tab for readonly doc.
+ SetStyle(nStyle & ~WB_INSERTTAB);
+ else
+ SetStyle(nStyle | WB_INSERTTAB);
+}
+
+void ScTabControl::UpdateStatus()
+{
+ ScDocument& rDoc = pViewData->GetDocument();
+ ScMarkData& rMark = pViewData->GetMarkData();
+ bool bActive = pViewData->IsActive();
+
+ SCTAB nCount = rDoc.GetTableCount();
+ SCTAB i;
+ OUString aString;
+ SCTAB nMaxCnt = std::max( nCount, static_cast<SCTAB>(GetMaxId()) );
+ Color aTabBgColor;
+
+ bool bModified = false; // sheet name
+ for (i=0; i<nMaxCnt && !bModified; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ rDoc.GetName(i,aString);
+ aTabBgColor = rDoc.GetTabBgColor(i);
+ }
+ else
+ {
+ aString.clear();
+ }
+
+ if ( aString != GetPageText(static_cast<sal_uInt16>(i)+1) || (GetTabBgColor(static_cast<sal_uInt16>(i)+1) != aTabBgColor) )
+ bModified = true;
+ }
+
+ if (bModified)
+ {
+ Clear();
+ for (i=0; i<nCount; i++)
+ {
+ if (rDoc.IsVisible(i))
+ {
+ if (rDoc.GetName(i,aString))
+ {
+ if ( rDoc.IsScenario(i) )
+ InsertPage(static_cast<sal_uInt16>(i)+1, aString, TabBarPageBits::Blue);
+ else
+ InsertPage( static_cast<sal_uInt16>(i)+1, aString );
+
+ if ( rDoc.IsTabProtected(i) )
+ SetProtectionSymbol(static_cast<sal_uInt16>(i)+1, true);
+
+ if ( !rDoc.IsDefaultTabBgColor(i) )
+ {
+ aTabBgColor = rDoc.GetTabBgColor(i);
+ SetTabBgColor(static_cast<sal_uInt16>(i)+1, aTabBgColor );
+ }
+ }
+ }
+ }
+ }
+ SetCurPageId( static_cast<sal_uInt16>(pViewData->GetTabNo()) + 1 );
+
+ if (bActive)
+ {
+ bModified = false; // selection
+ for (i=0; i<nMaxCnt && !bModified; i++)
+ if ( rMark.GetTableSelect(i) != IsPageSelected(static_cast<sal_uInt16>(i)+1) )
+ bModified = true;
+
+ if ( bModified )
+ for (i=0; i<nCount; i++)
+ SelectPage( static_cast<sal_uInt16>(i)+1, rMark.GetTableSelect(i) );
+ }
+}
+
+void ScTabControl::SetSheetLayoutRTL( bool bSheetRTL )
+{
+ SetEffectiveRTL( bSheetRTL );
+ nSelPageIdByMouse = TabBar::PAGE_NOT_FOUND;
+}
+
+void ScTabControl::SwitchToPageId(sal_uInt16 nId)
+{
+ if (!nId)
+ return;
+
+ bool bAlreadySelected = IsPageSelected( nId );
+ //make the clicked page the current one
+ SetCurPageId( nId );
+ //change the selection when the current one is not already
+ //selected or part of a multi selection
+ if(bAlreadySelected)
+ return;
+
+ sal_uInt16 nCount = GetMaxId();
+
+ for (sal_uInt16 i=1; i<=nCount; i++)
+ SelectPage( i, i==nId );
+ Select();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // notify LibreOfficeKit about changed page
+ OString aPayload = OString::number(nId - 1);
+ pViewData->GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload);
+ }
+}
+
+void ScTabControl::Command( const CommandEvent& rCEvt )
+{
+ ScModule* pScMod = SC_MOD();
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+ bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
+
+ // first activate ViewFrame (Bug 19493):
+ pViewSh->SetActive();
+
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu || bDisable)
+ return;
+
+ // #i18735# select the page that is under the mouse cursor
+ // if multiple tables are selected and the one under the cursor
+ // is not part of them then unselect them
+ sal_uInt16 nId = GetPageId( rCEvt.GetMousePosPixel() );
+ SwitchToPageId(nId);
+
+ // #i52073# OLE inplace editing has to be stopped before showing the sheet tab context menu
+ pViewSh->DeactivateOle();
+
+ // Popup-Menu:
+ // get Dispatcher from ViewData (ViewFrame) instead of Shell (Frame), so it can't be null
+ pViewData->GetDispatcher().ExecutePopup( "sheettab" );
+}
+
+void ScTabControl::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
+{
+ ScModule* pScMod = SC_MOD();
+ bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
+
+ if (!bDisable)
+ {
+ vcl::Region aRegion( tools::Rectangle(0,0,0,0) );
+ CommandEvent aCEvt( rPosPixel, CommandEventId::StartDrag, true ); // needed for StartDrag
+ if (TabBar::StartDrag( aCEvt, aRegion ))
+ DoDrag();
+ }
+}
+
+void ScTabControl::DoDrag()
+{
+ ScDocShell* pDocSh = pViewData->GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ SCTAB nTab = pViewData->GetTabNo();
+ ScRange aTabRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
+ ScMarkData aTabMark = pViewData->GetMarkData();
+ aTabMark.ResetMark(); // doesn't change marked table information
+ aTabMark.SetMarkArea( aTabRange );
+
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ ScClipParam aClipParam(aTabRange, false);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aTabMark, false, false);
+
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+
+ pTransferObj->SetDragSourceFlags(ScDragSrc::Table);
+
+ pTransferObj->SetDragSource( pDocSh, aTabMark );
+
+ pTransferObj->SetSourceCursorPos( pViewData->GetCurX(), pViewData->GetCurY() );
+
+ vcl::Window* pWindow = pViewData->GetActiveWin();
+ SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D
+ pTransferObj->StartDrag( pWindow, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
+}
+
+static sal_uInt16 lcl_DocShellNr( const ScDocument& rDoc )
+{
+ sal_uInt16 nShellCnt = 0;
+ SfxObjectShell* pShell = SfxObjectShell::GetFirst();
+ while ( pShell )
+ {
+ if ( auto pDocShell = dynamic_cast<const ScDocShell *>(pShell) )
+ {
+ if ( &pDocShell->GetDocument() == &rDoc )
+ return nShellCnt;
+
+ ++nShellCnt;
+ }
+ pShell = SfxObjectShell::GetNext( *pShell );
+ }
+
+ OSL_FAIL("Document not found");
+ return 0;
+}
+
+sal_Int8 ScTabControl::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ EndSwitchPage();
+
+ ScDocument& rDoc = pViewData->GetDocument();
+ const ScDragData& rData = SC_MOD()->GetDragData();
+ if ( rData.pCellTransfer && (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) &&
+ rData.pCellTransfer->GetSourceDocument() == &rDoc )
+ {
+ // moving of tables within the document
+ SCTAB nPos = GetPrivatDropPos( rEvt.maPosPixel );
+ HideDropPos();
+
+ if ( nPos == rData.pCellTransfer->GetVisibleTab() && rEvt.mnAction == DND_ACTION_MOVE )
+ {
+ // #i83005# do nothing - don't move to the same position
+ // (too easily triggered unintentionally, and might take a long time in large documents)
+ }
+ else
+ {
+ if ( !rDoc.GetChangeTrack() && rDoc.IsDocEditable() )
+ {
+ //! use table selection from the tab control where dragging was started?
+ pViewData->GetView()->MoveTable( lcl_DocShellNr(rDoc), nPos, rEvt.mnAction != DND_ACTION_MOVE );
+
+ rData.pCellTransfer->SetDragWasInternal(); // don't delete
+ return DND_ACTION_COPY;
+ }
+ }
+ }
+
+ return DND_ACTION_NONE;
+}
+
+sal_Int8 ScTabControl::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ if ( rEvt.mbLeaving )
+ {
+ EndSwitchPage();
+ HideDropPos();
+ return rEvt.mnAction;
+ }
+
+ const ScDocument& rDoc = pViewData->GetDocument();
+ const ScDragData& rData = SC_MOD()->GetDragData();
+ if ( rData.pCellTransfer && (rData.pCellTransfer->GetDragSourceFlags() & ScDragSrc::Table) &&
+ rData.pCellTransfer->GetSourceDocument() == &rDoc )
+ {
+ // moving of tables within the document
+ if ( !rDoc.GetChangeTrack() && rDoc.IsDocEditable() )
+ {
+ ShowDropPos( rEvt.maPosPixel );
+ return rEvt.mnAction;
+ }
+ }
+ else // switch sheets for all formats
+ {
+ SwitchPage( rEvt.maPosPixel ); // switch sheet after timeout
+ return 0; // nothing can be dropped here
+ }
+
+ return 0;
+}
+
+bool ScTabControl::StartRenaming()
+{
+ return pViewData->GetDocument().IsDocEditable();
+}
+
+TabBarAllowRenamingReturnCode ScTabControl::AllowRenaming()
+{
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+ OSL_ENSURE( pViewSh, "pViewData->GetViewShell()" );
+
+ TabBarAllowRenamingReturnCode nRet = TABBAR_RENAMING_CANCEL;
+ sal_uInt16 nId = GetEditPageId();
+ if ( nId )
+ {
+ SCTAB nTab = nId - 1;
+ OUString aNewName = GetEditText();
+ bool bDone = pViewSh->RenameTable( aNewName, nTab );
+ if ( bDone )
+ nRet = TABBAR_RENAMING_YES;
+ else if ( bErrorShown )
+ {
+ // if the error message from this TabControl is currently visible,
+ // don't end edit mode now, to avoid problems when returning to
+ // the other call (showing the error) - this should not happen
+ OSL_FAIL("ScTabControl::AllowRenaming: nested calls");
+ nRet = TABBAR_RENAMING_NO;
+ }
+ else if (pViewData->GetDocShell()->IsInModalMode())
+ {
+ // don't show error message above any modal dialog
+ // instead cancel renaming without error message
+ // e.g. start with default Sheet1, add another sheet
+ // alt+left click on Sheet2 tab, edit to say Sheet1
+ // ctrl+S to trigger modal file save dialog
+ nRet = TABBAR_RENAMING_CANCEL;
+ }
+ else
+ {
+ bErrorShown = true;
+ pViewSh->ErrorMessage( STR_INVALIDTABNAME );
+ bErrorShown = false;
+ nRet = TABBAR_RENAMING_NO;
+ }
+ }
+ return nRet;
+}
+
+void ScTabControl::EndRenaming()
+{
+ if ( HasFocus() )
+ pViewData->GetView()->ActiveGrabFocus();
+}
+
+void ScTabControl::Mirror()
+{
+ TabBar::Mirror();
+ if( nSelPageIdByMouse != TabBar::PAGE_NOT_FOUND )
+ {
+ tools::Rectangle aRect( GetPageRect( GetCurPageId() ) );
+ if( !aRect.IsEmpty() )
+ SetPointerPosPixel( aRect.Center() );
+ nSelPageIdByMouse = TabBar::PAGE_NOT_FOUND; // only once after a Select()
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabsplit.cxx b/sc/source/ui/view/tabsplit.cxx
new file mode 100644
index 0000000000..fb8435b271
--- /dev/null
+++ b/sc/source/ui/view/tabsplit.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 <tabsplit.hxx>
+#include <viewdata.hxx>
+
+#include <vcl/settings.hxx>
+
+ScTabSplitter::ScTabSplitter( vcl::Window* pParent, WinBits nWinStyle, const ScViewData* pData ) :
+ Splitter(pParent, nWinStyle),
+ pViewData(pData)
+{
+ SetFixed(false);
+ EnableRTL(false);
+}
+
+ScTabSplitter::~ScTabSplitter()
+{
+}
+
+void ScTabSplitter::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (bFixed)
+ Window::MouseButtonDown( rMEvt );
+ else
+ Splitter::MouseButtonDown( rMEvt );
+}
+
+void ScTabSplitter::SetFixed(bool bSet)
+{
+ bFixed = bSet;
+ if (bSet)
+ SetPointer(PointerStyle::Arrow);
+ else if (IsHorizontal())
+ SetPointer(PointerStyle::HSplit);
+ else
+ SetPointer(PointerStyle::VSplit);
+}
+
+void ScTabSplitter::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
+{
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (IsHorizontal())
+ {
+ switch (pViewData->GetHSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+
+ // Draw handle
+ rRenderContext.SetLineColor(COL_BLACK);
+ rRenderContext.SetFillColor(COL_BLACK);
+ const tools::Long xc = rRect.Right() + rRect.Left();
+ const tools::Long h4 = rRect.GetHeight() / 4;
+ // First xc fraction is truncated, second one is rounded. This will draw a centered line
+ // in handlers with odd width and a centered rectangle in those with even width.
+ rRenderContext.DrawRect(tools::Rectangle(Point(xc / 2, rRect.Top() + h4),
+ Point((xc + 1) / 2, rRect.Bottom() - h4)));
+ break;
+ }
+ case SC_SPLIT_NORMAL:
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+ break;
+ case SC_SPLIT_FIX:
+ // Nothing to draw
+ break;
+ }
+ }
+ else
+ {
+ switch (pViewData->GetVSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+
+ // Draw handle
+ rRenderContext.SetLineColor(COL_BLACK);
+ rRenderContext.SetFillColor(COL_BLACK);
+ const tools::Long yc = rRect.Top() + rRect.Bottom();
+ const tools::Long w4 = rRect.GetWidth() / 4;
+ // First yc fraction is truncated, second one is rounded. This will draw a centered line
+ // in handlers with odd height and a centered rectangle in those with even height.
+ GetOutDev()->DrawRect(tools::Rectangle(Point(rRect.Left() + w4, yc / 2),
+ Point(rRect.Right() - w4, (yc + 1) / 2)));
+ break;
+ }
+ case SC_SPLIT_NORMAL:
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawRect(tools::Rectangle(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()));
+ break;
+ case SC_SPLIT_FIX:
+ // Nothing to draw
+ break;
+ }
+ }
+
+ rRenderContext.Pop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx
new file mode 100644
index 0000000000..9eff50195e
--- /dev/null
+++ b/sc/source/ui/view/tabview.cxx
@@ -0,0 +1,3162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <tools/svborder.hxx>
+#include <tools/json_writer.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <pagedata.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <gridwin.hxx>
+#include <olinewin.hxx>
+#include <olinetab.hxx>
+#include <tabsplit.hxx>
+#include <colrowba.hxx>
+#include <tabcont.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <drawview.hxx>
+#include <docsh.hxx>
+#include <viewuno.hxx>
+#include <appoptio.hxx>
+#include <attrib.hxx>
+#include <spellcheckcontext.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <osl/diagnose.h>
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+
+#include <algorithm>
+
+#include <basegfx/utils/zoomtools.hxx>
+
+#define SPLIT_MARGIN 30
+#define SPLIT_HANDLE_SIZE 5
+constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10;
+
+#define SC_ICONSIZE 36
+
+#define SC_SCROLLBAR_MIN 30
+#define SC_TABBAR_MIN 6
+
+using namespace ::com::sun::star;
+
+// Corner-Button
+
+ScCornerButton::ScCornerButton( vcl::Window* pParent, ScViewData* pData ) :
+ Window( pParent, WinBits( 0 ) ),
+ pViewData( pData )
+{
+ EnableRTL( false );
+}
+
+ScCornerButton::~ScCornerButton()
+{
+}
+
+void ScCornerButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ SetBackground(rStyleSettings.GetFaceColor());
+
+ Size aSize(GetOutputSizePixel());
+ tools::Long nPosX = aSize.Width() - 1;
+ tools::Long nPosY = aSize.Height() - 1;
+
+ Window::Paint(rRenderContext, rRect);
+
+ bool bLayoutRTL = pViewData->GetDocument().IsLayoutRTL( pViewData->GetTabNo() );
+ tools::Long nDarkX = bLayoutRTL ? 0 : nPosX;
+
+ // both buttons have the same look now - only dark right/bottom lines
+ rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
+ rRenderContext.DrawLine(Point(0, nPosY), Point(nPosX, nPosY));
+ rRenderContext.DrawLine(Point(nDarkX, 0), Point(nDarkX, nPosY));
+}
+
+void ScCornerButton::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ SetBackground( rStyleSettings.GetFaceColor() );
+ Invalidate();
+}
+
+void ScCornerButton::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ SetBackground( rStyleSettings.GetFaceColor() );
+ Invalidate();
+}
+
+void ScCornerButton::Resize()
+{
+ Invalidate();
+}
+
+void ScCornerButton::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ ScModule* pScMod = SC_MOD();
+ bool bDisable = pScMod->IsFormulaMode() || pScMod->IsModalMode();
+ if (!bDisable)
+ {
+ ScTabViewShell* pViewSh = pViewData->GetViewShell();
+ pViewSh->SetActive(); // Appear and SetViewFrame
+ pViewSh->ActiveGrabFocus();
+
+ bool bControl = rMEvt.IsMod1();
+ pViewSh->SelectAll( bControl );
+ }
+}
+namespace
+{
+
+bool lcl_HasColOutline( const ScViewData& rViewData )
+{
+ const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.GetTabNo());
+ if (pTable)
+ {
+ const ScOutlineArray& rArray = pTable->GetColArray();
+ if ( rArray.GetDepth() > 0 )
+ return true;
+ }
+ return false;
+}
+
+bool lcl_HasRowOutline( const ScViewData& rViewData )
+{
+ const ScOutlineTable* pTable = rViewData.GetDocument().GetOutlineTable(rViewData.GetTabNo());
+ if (pTable)
+ {
+ const ScOutlineArray& rArray = pTable->GetRowArray();
+ if ( rArray.GetDepth() > 0 )
+ return true;
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+ScTabView::ScTabView( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
+ pFrameWin( pParent ),
+ aViewData( rDocSh, pViewShell ),
+ aFunctionSet( &aViewData ),
+ aHdrFunc( &aViewData ),
+ aVScrollTop( VclPtr<ScrollAdaptor>::Create( pFrameWin, false ) ),
+ aVScrollBottom( VclPtr<ScrollAdaptor>::Create( pFrameWin, false ) ),
+ aHScrollLeft( VclPtr<ScrollAdaptor>::Create( pFrameWin, true ) ),
+ aHScrollRight( VclPtr<ScrollAdaptor>::Create( pFrameWin, true ) ),
+ aCornerButton( VclPtr<ScCornerButton>::Create( pFrameWin, &aViewData ) ),
+ aTopButton( VclPtr<ScCornerButton>::Create( pFrameWin, &aViewData ) ),
+ aScrollTimer("ScTabView aScrollTimer"),
+ pTimerWindow( nullptr ),
+ aExtraEditViewManager( pViewShell, pGridWin ),
+ nTipVisible( nullptr ),
+ nTipAlign( QuickHelpFlags::NONE ),
+ nPrevDragPos( 0 ),
+ meBlockMode(None),
+ meHighlightBlockMode(None),
+ nBlockStartX( 0 ),
+ nBlockStartXOrig( 0 ),
+ nBlockEndX( 0 ),
+ nBlockStartY( 0 ),
+ nBlockStartYOrig( 0 ),
+ nBlockEndY( 0 ),
+ nBlockStartZ( 0 ),
+ nBlockEndZ( 0 ),
+ nOldCurX( 0 ),
+ nOldCurY( 0 ),
+ mfPendingTabBarWidth( -1.0 ),
+ mnLOKStartHeaderRow( -2 ),
+ mnLOKEndHeaderRow( -1 ),
+ mnLOKStartHeaderCol( -2 ),
+ mnLOKEndHeaderCol( -1 ),
+ bMinimized( false ),
+ bInUpdateHeader( false ),
+ bInActivatePart( false ),
+ bInZoomUpdate( false ),
+ bMoveIsShift( false ),
+ bDrawSelMode( false ),
+ bLockPaintBrush( false ),
+ bDragging( false ),
+ bBlockNeg( false ),
+ bBlockCols( false ),
+ bBlockRows( false ),
+ mbInlineWithScrollbar( false )
+{
+ Init();
+}
+
+void ScTabView::InitScrollBar(ScrollAdaptor& rScrollBar, tools::Long nMaxVal, const Link<weld::Scrollbar&, void>& rLink)
+{
+ rScrollBar.SetRange( Range( 0, nMaxVal ) );
+ rScrollBar.SetLineSize( 1 );
+ rScrollBar.SetPageSize( 1 ); // is queried separately
+ rScrollBar.SetVisibleSize( 10 ); // is reset by Resize
+
+ rScrollBar.SetScrollHdl(rLink);
+ rScrollBar.SetMouseReleaseHdl(LINK(this, ScTabView, EndScrollHdl));
+
+ rScrollBar.EnableRTL( aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() ) );
+
+ // Related: tdf#155266 Eliminate delayed scrollbar redrawing when swiping
+ // By default, the layout idle timer in the InterimWindowItem class
+ // is set to TaskPriority::RESIZE. That is too high of a priority as
+ // it appears that other timers are drawing after the scrollbar has been
+ // redrawn.
+ // As a result, when swiping, the content moves fluidly but the scrollbar
+ // thumb does not move until after swiping stops or pauses. Then, after a
+ // short lag, the scrollbar thumb finally "jumps" to the expected
+ // position.
+ // So, to fix this scrollbar "stickiness" when swiping, setting the
+ // priority to TaskPriority::POST_PAINT causes the scrollbar to be
+ // redrawn after any competing timers.
+ rScrollBar.SetPriority(TaskPriority::POST_PAINT);
+}
+
+// Scroll-Timer
+void ScTabView::SetTimer( ScGridWindow* pWin, const MouseEvent& rMEvt )
+{
+ pTimerWindow = pWin;
+ aTimerMEvt = rMEvt;
+ aScrollTimer.Start();
+}
+
+void ScTabView::ResetTimer()
+{
+ aScrollTimer.Stop();
+ pTimerWindow = nullptr;
+}
+
+IMPL_LINK_NOARG(ScTabView, TimerHdl, Timer *, void)
+{
+ if (pTimerWindow)
+ pTimerWindow->MouseMove( aTimerMEvt );
+}
+
+// --- Resize ---------------------------------------------------------------------
+
+static void lcl_SetPosSize( vcl::Window& rWindow, const Point& rPos, const Size& rSize,
+ tools::Long nTotalWidth, bool bLayoutRTL )
+{
+ Point aNewPos = rPos;
+ if ( bLayoutRTL )
+ {
+ aNewPos.setX( nTotalWidth - rPos.X() - rSize.Width() );
+ if ( aNewPos == rWindow.GetPosPixel() && rSize.Width() != rWindow.GetSizePixel().Width() )
+ {
+ // Document windows are manually painted right-to-left, so they need to
+ // be repainted if the size changes.
+ rWindow.Invalidate();
+ }
+ }
+ rWindow.SetPosSizePixel( aNewPos, rSize );
+}
+
+void ScTabView::DoResize( const Point& rOffset, const Size& rSize, bool bInner )
+{
+ HideListBox();
+
+ bool bHasHint = HasHintWindow();
+ if (bHasHint)
+ RemoveHintWindow();
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ tools::Long nTotalWidth = rSize.Width();
+ if ( bLayoutRTL )
+ nTotalWidth += 2*rOffset.X();
+
+ bool bVScroll = aViewData.IsVScrollMode();
+ bool bHScroll = aViewData.IsHScrollMode();
+ bool bTabControl = aViewData.IsTabMode();
+ bool bHeaders = aViewData.IsHeaderMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+
+ if ( aViewData.GetDocShell()->IsPreview() )
+ bHScroll = bVScroll = bTabControl = bHeaders = bHOutline = bVOutline = false;
+
+ tools::Long nBarX = 0;
+ tools::Long nBarY = 0;
+ tools::Long nOutlineX = 0;
+ tools::Long nOutlineY = 0;
+ tools::Long nOutPosX;
+ tools::Long nOutPosY;
+
+ tools::Long nPosX = rOffset.X();
+ tools::Long nPosY = rOffset.Y();
+ tools::Long nSizeX = rSize.Width();
+ tools::Long nSizeY = rSize.Height();
+
+ bMinimized = ( nSizeX<=SC_ICONSIZE || nSizeY<=SC_ICONSIZE );
+ if ( bMinimized )
+ return;
+
+ float fScaleFactor = pFrameWin->GetDPIScaleFactor();
+
+ tools::Long nSplitSizeX = SPLIT_HANDLE_SIZE * fScaleFactor;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ nSplitSizeX = 1;
+ tools::Long nSplitSizeY = SPLIT_HANDLE_SIZE * fScaleFactor;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ nSplitSizeY = 1;
+
+ aBorderPos = rOffset;
+ aFrameSize = rSize;
+
+ const StyleSettings& rStyleSettings = pFrameWin->GetSettings().GetStyleSettings();
+
+
+ Size aFontSize = rStyleSettings.GetTabFont().GetFontSize();
+ MapMode aPtMapMode(MapUnit::MapPoint);
+ aFontSize = pFrameWin->LogicToPixel(aFontSize, aPtMapMode);
+ sal_Int32 nTabHeight = aFontSize.Height() + TAB_HEIGHT_MARGIN;
+
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ {
+ if ( aViewData.GetHSplitPos() > nSizeX - SPLIT_MARGIN )
+ {
+ aViewData.SetHSplitMode( SC_SPLIT_NONE );
+ if ( WhichH( aViewData.GetActivePart() ) == SC_SPLIT_RIGHT )
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ InvalidateSplit();
+ }
+ }
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ if ( aViewData.GetVSplitPos() > nSizeY - SPLIT_MARGIN )
+ {
+ aViewData.SetVSplitMode( SC_SPLIT_NONE );
+ if ( WhichV( aViewData.GetActivePart() ) == SC_SPLIT_TOP )
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ InvalidateSplit();
+ }
+ }
+
+ UpdateShow();
+
+ if (bHScroll || bVScroll) // Scrollbars horizontal or vertical
+ {
+ tools::Long nScrollBarSize = rStyleSettings.GetScrollBarSize();
+ if (bVScroll)
+ {
+ nBarX = nScrollBarSize;
+ nSizeX -= nBarX;
+ }
+ if (bHScroll)
+ {
+ nBarY = nTabHeight;
+
+ if (!mbInlineWithScrollbar)
+ nBarY += nScrollBarSize;
+
+ nSizeY -= nBarY;
+ }
+
+ if (bHScroll) // Scrollbars horizontal
+ {
+ tools::Long nSizeLt = 0; // left scroll bar
+ tools::Long nSizeRt = 0; // right scroll bar
+ tools::Long nSizeSp = 0; // splitter
+
+ switch (aViewData.GetHSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ nSizeSp = nSplitSizeX;
+ nSizeLt = nSizeX - nSizeSp; // Convert the corner
+ break;
+ case SC_SPLIT_NORMAL:
+ nSizeSp = nSplitSizeX;
+ nSizeLt = aViewData.GetHSplitPos();
+ break;
+ case SC_SPLIT_FIX:
+ nSizeSp = 0;
+ nSizeLt = 0;
+ break;
+ }
+ nSizeRt = nSizeX - nSizeLt - nSizeSp;
+
+ tools::Long nTabSize = 0;
+
+ if (bTabControl)
+ {
+ // pending relative tab bar width from extended document options
+ if( mfPendingTabBarWidth >= 0.0 )
+ {
+ SetRelTabBarWidth( mfPendingTabBarWidth );
+ mfPendingTabBarWidth = -1.0;
+ }
+
+ if (mbInlineWithScrollbar)
+ {
+ nTabSize = pTabControl->GetSizePixel().Width();
+
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_FIX ) // left Scrollbar
+ {
+ if (nTabSize > nSizeLt-SC_SCROLLBAR_MIN)
+ nTabSize = nSizeLt-SC_SCROLLBAR_MIN;
+ if (nTabSize < SC_TABBAR_MIN)
+ nTabSize = SC_TABBAR_MIN;
+ nSizeLt -= nTabSize;
+ }
+ else // right Scrollbar
+ {
+ if (nTabSize > nSizeRt-SC_SCROLLBAR_MIN)
+ nTabSize = nSizeRt-SC_SCROLLBAR_MIN;
+ if (nTabSize < SC_TABBAR_MIN)
+ nTabSize = SC_TABBAR_MIN;
+ nSizeRt -= nTabSize;
+ }
+ }
+ }
+
+ if (mbInlineWithScrollbar)
+ {
+ Point aTabPoint(nPosX, nPosY + nSizeY);
+ Size aTabSize(nTabSize, nBarY);
+ lcl_SetPosSize(*pTabControl, aTabPoint, aTabSize, nTotalWidth, bLayoutRTL);
+ pTabControl->SetSheetLayoutRTL(bLayoutRTL);
+
+ Point aHScrollLeftPoint(nPosX + nTabSize, nPosY + nSizeY);
+ Size aHScrollLeftSize(nSizeLt, nBarY);
+ lcl_SetPosSize(*aHScrollLeft, aHScrollLeftPoint, aHScrollLeftSize, nTotalWidth, bLayoutRTL);
+
+ Point aHSplitterPoint(nPosX + nTabSize + nSizeLt, nPosY + nSizeY);
+ Size aHSplitterSize(nSizeSp, nBarY);
+ lcl_SetPosSize(*pHSplitter, aHSplitterPoint, aHSplitterSize, nTotalWidth, bLayoutRTL);
+
+ Point aHScrollRightPoint(nPosX + nTabSize + nSizeLt + nSizeSp, nPosY + nSizeY);
+ Size aHScrollRightSize(nSizeRt, nBarY);
+ lcl_SetPosSize(*aHScrollRight, aHScrollRightPoint, aHScrollRightSize, nTotalWidth, bLayoutRTL);
+ }
+ else
+ {
+ Point aTabPoint(nPosX, nPosY + nSizeY + nScrollBarSize);
+ Size aTabSize(nSizeX, nTabHeight);
+ lcl_SetPosSize(*pTabControl, aTabPoint, aTabSize, nTotalWidth, bLayoutRTL);
+ pTabControl->SetSheetLayoutRTL(bLayoutRTL);
+
+ Point aHScrollLeftPoint(nPosX, nPosY + nSizeY);
+ Size aHScrollLeftSize(nSizeLt, nScrollBarSize);
+ lcl_SetPosSize(*aHScrollLeft, aHScrollLeftPoint, aHScrollLeftSize, nTotalWidth, bLayoutRTL);
+
+ Point aHSplitterPoint(nPosX + nSizeLt, nPosY + nSizeY);
+ Size aHSplitterSize(nSizeSp, nScrollBarSize);
+ lcl_SetPosSize(*pHSplitter, aHSplitterPoint, aHSplitterSize, nTotalWidth, bLayoutRTL);
+
+ Point aHScrollRightPoint(nPosX + nSizeLt + nSizeSp, nPosY + nSizeY);
+ Size aHScrollRightSize(nSizeRt, nScrollBarSize);
+ lcl_SetPosSize(*aHScrollRight, aHScrollRightPoint, aHScrollRightSize, nTotalWidth, bLayoutRTL);
+ }
+ // SetDragRectPixel is done below
+ }
+
+ if (bVScroll)
+ {
+ tools::Long nSizeUp = 0; // upper scroll bar
+ tools::Long nSizeSp = 0; // splitter
+ tools::Long nSizeDn; // lower scroll bar
+
+ switch (aViewData.GetVSplitMode())
+ {
+ case SC_SPLIT_NONE:
+ nSizeUp = 0;
+ nSizeSp = nSplitSizeY;
+ break;
+ case SC_SPLIT_NORMAL:
+ nSizeUp = aViewData.GetVSplitPos();
+ nSizeSp = nSplitSizeY;
+ break;
+ case SC_SPLIT_FIX:
+ nSizeUp = 0;
+ nSizeSp = 0;
+ break;
+ }
+ nSizeDn = nSizeY - nSizeUp - nSizeSp;
+
+ lcl_SetPosSize( *aVScrollTop, Point(nPosX + nSizeX, nPosY),
+ Size(nBarX, nSizeUp), nTotalWidth, bLayoutRTL );
+ lcl_SetPosSize( *pVSplitter, Point( nPosX + nSizeX, nPosY+nSizeUp ),
+ Size( nBarX, nSizeSp ), nTotalWidth, bLayoutRTL );
+ lcl_SetPosSize( *aVScrollBottom, Point(nPosX + nSizeX,
+ nPosY + nSizeUp + nSizeSp),
+ Size(nBarX, nSizeDn), nTotalWidth, bLayoutRTL );
+
+ // SetDragRectPixel is done below
+ }
+ }
+
+ // SetDragRectPixel also without Scrollbars etc., when already split
+ if ( bHScroll || aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ pHSplitter->SetDragRectPixel(
+ tools::Rectangle( nPosX, nPosY, nPosX+nSizeX, nPosY+nSizeY ), pFrameWin );
+ if ( bVScroll || aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ pVSplitter->SetDragRectPixel(
+ tools::Rectangle( nPosX, nPosY, nPosX+nSizeX, nPosY+nSizeY ), pFrameWin );
+
+ if (bTabControl && ! bHScroll )
+ {
+ nBarY = aHScrollLeft->GetSizePixel().Height();
+
+ tools::Long nSize1 = nSizeX;
+
+ tools::Long nTabSize = nSize1;
+ if (nTabSize < 0) nTabSize = 0;
+
+ lcl_SetPosSize( *pTabControl, Point(nPosX, nPosY+nSizeY-nBarY),
+ Size(nTabSize, nBarY), nTotalWidth, bLayoutRTL );
+ nSizeY -= nBarY;
+
+ if( bVScroll )
+ {
+ Size aVScrSize = aVScrollBottom->GetSizePixel();
+ aVScrSize.AdjustHeight( -nBarY );
+ aVScrollBottom->SetSizePixel( aVScrSize );
+ }
+ }
+
+ nOutPosX = nPosX;
+ nOutPosY = nPosY;
+
+ // Outline-Controls
+ if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
+ {
+ nOutlineX = pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize();
+ nSizeX -= nOutlineX;
+ nPosX += nOutlineX;
+ }
+ if (bHOutline && pColOutline[SC_SPLIT_LEFT])
+ {
+ nOutlineY = pColOutline[SC_SPLIT_LEFT]->GetDepthSize();
+ nSizeY -= nOutlineY;
+ nPosY += nOutlineY;
+ }
+
+ if (bHeaders) // column/row header
+ {
+ nBarX = pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
+ nBarY = pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
+ nSizeX -= nBarX;
+ nSizeY -= nBarY;
+ nPosX += nBarX;
+ nPosY += nBarY;
+ }
+ else
+ nBarX = nBarY = 0;
+
+ // evaluate splitter
+
+ tools::Long nLeftSize = nSizeX;
+ tools::Long nRightSize = 0;
+ tools::Long nTopSize = 0;
+ tools::Long nBottomSize = nSizeY;
+ tools::Long nSplitPosX = nPosX;
+ tools::Long nSplitPosY = nPosY;
+
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ {
+ tools::Long nSplitHeight = rSize.Height();
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ {
+ // Do not allow freeze splitter to overlap scroll bar/tab bar
+ if ( bHScroll )
+ nSplitHeight -= aHScrollLeft->GetSizePixel().Height();
+ else if ( bTabControl && pTabControl )
+ nSplitHeight -= pTabControl->GetSizePixel().Height();
+ }
+ nSplitPosX = aViewData.GetHSplitPos();
+ lcl_SetPosSize( *pHSplitter,
+ Point(nSplitPosX, nOutPosY),
+ Size(nSplitSizeX, nSplitHeight - nTabHeight), nTotalWidth, bLayoutRTL);
+ nLeftSize = nSplitPosX - nPosX;
+ nSplitPosX += nSplitSizeX;
+ nRightSize = nSizeX - nLeftSize - nSplitSizeX;
+ }
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ tools::Long nSplitWidth = rSize.Width();
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX && bVScroll )
+ nSplitWidth -= aVScrollBottom->GetSizePixel().Width();
+ nSplitPosY = aViewData.GetVSplitPos();
+ lcl_SetPosSize( *pVSplitter,
+ Point( nOutPosX, nSplitPosY ), Size( nSplitWidth, nSplitSizeY ), nTotalWidth, bLayoutRTL );
+ nTopSize = nSplitPosY - nPosY;
+ nSplitPosY += nSplitSizeY;
+ nBottomSize = nSizeY - nTopSize - nSplitSizeY;
+ }
+
+ // ShowHide for pColOutline / pRowOutline happens in UpdateShow
+
+ if (bHOutline) // Outline-Controls
+ {
+ if (pColOutline[SC_SPLIT_LEFT])
+ {
+ pColOutline[SC_SPLIT_LEFT]->SetHeaderSize( nBarX );
+ lcl_SetPosSize( *pColOutline[SC_SPLIT_LEFT],
+ Point(nPosX-nBarX,nOutPosY), Size(nLeftSize+nBarX,nOutlineY), nTotalWidth, bLayoutRTL );
+ }
+ if (pColOutline[SC_SPLIT_RIGHT])
+ {
+ pColOutline[SC_SPLIT_RIGHT]->SetHeaderSize( 0 ); // always call to update RTL flag
+ lcl_SetPosSize( *pColOutline[SC_SPLIT_RIGHT],
+ Point(nSplitPosX,nOutPosY), Size(nRightSize,nOutlineY), nTotalWidth, bLayoutRTL );
+ }
+ }
+ if (bVOutline)
+ {
+ if (nTopSize)
+ {
+ if (pRowOutline[SC_SPLIT_TOP] && pRowOutline[SC_SPLIT_BOTTOM])
+ {
+ pRowOutline[SC_SPLIT_TOP]->SetHeaderSize( nBarY );
+ lcl_SetPosSize( *pRowOutline[SC_SPLIT_TOP],
+ Point(nOutPosX,nPosY-nBarY), Size(nOutlineX,nTopSize+nBarY), nTotalWidth, bLayoutRTL );
+ pRowOutline[SC_SPLIT_BOTTOM]->SetHeaderSize( 0 );
+ lcl_SetPosSize( *pRowOutline[SC_SPLIT_BOTTOM],
+ Point(nOutPosX,nSplitPosY), Size(nOutlineX,nBottomSize), nTotalWidth, bLayoutRTL );
+ }
+ }
+ else if (pRowOutline[SC_SPLIT_BOTTOM])
+ {
+ pRowOutline[SC_SPLIT_BOTTOM]->SetHeaderSize( nBarY );
+ lcl_SetPosSize( *pRowOutline[SC_SPLIT_BOTTOM],
+ Point(nOutPosX,nSplitPosY-nBarY), Size(nOutlineX,nBottomSize+nBarY), nTotalWidth, bLayoutRTL );
+ }
+ }
+ if (bHOutline && bVOutline)
+ {
+ lcl_SetPosSize( *aTopButton, Point(nOutPosX,nOutPosY), Size(nOutlineX,nOutlineY), nTotalWidth, bLayoutRTL );
+ aTopButton->Show();
+ }
+ else
+ aTopButton->Hide();
+
+ if (bHeaders) // column/row header
+ {
+ lcl_SetPosSize( *pColBar[SC_SPLIT_LEFT],
+ Point(nPosX,nPosY-nBarY), Size(nLeftSize,nBarY), nTotalWidth, bLayoutRTL );
+ if (pColBar[SC_SPLIT_RIGHT])
+ lcl_SetPosSize( *pColBar[SC_SPLIT_RIGHT],
+ Point(nSplitPosX,nPosY-nBarY), Size(nRightSize,nBarY), nTotalWidth, bLayoutRTL );
+
+ if (pRowBar[SC_SPLIT_TOP])
+ lcl_SetPosSize( *pRowBar[SC_SPLIT_TOP],
+ Point(nPosX-nBarX,nPosY), Size(nBarX,nTopSize), nTotalWidth, bLayoutRTL );
+ lcl_SetPosSize( *pRowBar[SC_SPLIT_BOTTOM],
+ Point(nPosX-nBarX,nSplitPosY), Size(nBarX,nBottomSize), nTotalWidth, bLayoutRTL );
+
+ lcl_SetPosSize( *aCornerButton, Point(nPosX-nBarX,nPosY-nBarY), Size(nBarX,nBarY), nTotalWidth, bLayoutRTL );
+ aCornerButton->Show();
+ pColBar[SC_SPLIT_LEFT]->Show();
+ pRowBar[SC_SPLIT_BOTTOM]->Show();
+ }
+ else
+ {
+ aCornerButton->Hide();
+ pColBar[SC_SPLIT_LEFT]->Hide(); // always here
+ pRowBar[SC_SPLIT_BOTTOM]->Hide();
+ }
+
+ // Grid-Windows
+
+ if (bInner)
+ {
+ tools::Long nInnerPosX = bLayoutRTL ? ( nTotalWidth - nPosX - nLeftSize ) : nPosX;
+ pGridWin[SC_SPLIT_BOTTOMLEFT]->SetPosPixel( Point(nInnerPosX,nSplitPosY) );
+ }
+ else
+ {
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_BOTTOMLEFT],
+ Point(nPosX,nSplitPosY), Size(nLeftSize,nBottomSize), nTotalWidth, bLayoutRTL );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_BOTTOMRIGHT],
+ Point(nSplitPosX,nSplitPosY), Size(nRightSize,nBottomSize), nTotalWidth, bLayoutRTL );
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_TOPLEFT],
+ Point(nPosX,nPosY), Size(nLeftSize,nTopSize), nTotalWidth, bLayoutRTL );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE && aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ lcl_SetPosSize( *pGridWin[SC_SPLIT_TOPRIGHT],
+ Point(nSplitPosX,nPosY), Size(nRightSize,nTopSize), nTotalWidth, bLayoutRTL );
+ }
+
+ // update scroll bars
+
+ if (!bInUpdateHeader)
+ {
+ UpdateScrollBars(); // don't reset scroll bars when scrolling
+ UpdateHeaderWidth();
+
+ InterpretVisible(); // have everything calculated before painting
+ }
+
+ if (bHasHint)
+ TestHintWindow(); // reposition
+
+ UpdateVarZoom(); // update variable zoom types (after resizing GridWindows)
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccWindowResized));
+}
+
+void ScTabView::UpdateVarZoom()
+{
+ // update variable zoom types
+
+ SvxZoomType eZoomType = GetZoomType();
+ if (eZoomType == SvxZoomType::PERCENT || bInZoomUpdate)
+ return;
+
+ bInZoomUpdate = true;
+ const Fraction& rOldX = GetViewData().GetZoomX();
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ tools::Long nOldPercent = tools::Long(rOldY * 100);
+ sal_uInt16 nNewZoom = CalcZoom( eZoomType, static_cast<sal_uInt16>(nOldPercent) );
+ Fraction aNew( nNewZoom, 100 );
+
+ if ( aNew != rOldX || aNew != rOldY )
+ {
+ SetZoom( aNew, aNew, false ); // always separately per sheet
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ aViewData.GetViewShell()->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOM );
+ aViewData.GetViewShell()->GetViewFrame().GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER );
+ aViewData.GetBindings().Invalidate(SID_ZOOM_IN);
+ aViewData.GetBindings().Invalidate(SID_ZOOM_OUT);
+ }
+ bInZoomUpdate = false;
+}
+
+void ScTabView::UpdateFixPos()
+{
+ bool bResize = false;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixX())
+ bResize = true;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixY())
+ bResize = true;
+ if (bResize)
+ RepeatResize(false);
+}
+
+void ScTabView::RepeatResize( bool bUpdateFix )
+{
+ if ( bUpdateFix )
+ {
+ ScSplitMode eHSplit = aViewData.GetHSplitMode();
+ ScSplitMode eVSplit = aViewData.GetVSplitMode();
+
+ // #i46796# UpdateFixX / UpdateFixY uses GetGridOffset, which requires the
+ // outline windows to be available. So UpdateShow has to be called before
+ // (also called from DoResize).
+ if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX )
+ UpdateShow();
+
+ if ( eHSplit == SC_SPLIT_FIX )
+ aViewData.UpdateFixX();
+ if ( eVSplit == SC_SPLIT_FIX )
+ aViewData.UpdateFixY();
+ }
+
+ DoResize( aBorderPos, aFrameSize );
+
+ //! border must be reset ???
+}
+
+void ScTabView::GetBorderSize( SvBorder& rBorder, const Size& /* rSize */ )
+{
+ bool bScrollBars = aViewData.IsVScrollMode();
+ bool bHeaders = aViewData.IsHeaderMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+
+ rBorder = SvBorder();
+
+ if (bScrollBars) // Scrollbars horizontal or vertical
+ {
+ rBorder.Right() += aVScrollBottom->GetSizePixel().Width();
+ rBorder.Bottom() += aHScrollLeft->GetSizePixel().Height();
+ }
+
+ // Outline-Controls
+ if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
+ rBorder.Left() += pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize();
+ if (bHOutline && pColOutline[SC_SPLIT_LEFT])
+ rBorder.Top() += pColOutline[SC_SPLIT_LEFT]->GetDepthSize();
+
+ if (bHeaders) // column/row headers
+ {
+ rBorder.Left() += pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
+ rBorder.Top() += pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
+ }
+
+ if ( bLayoutRTL )
+ ::std::swap( rBorder.Left(), rBorder.Right() );
+}
+
+IMPL_LINK_NOARG(ScTabView, TabBarResize, TabBar*, void)
+{
+ if (!aViewData.IsHScrollMode())
+ return;
+
+ tools::Long nSize = pTabControl->GetSplitSize();
+
+ if (aViewData.GetHSplitMode() != SC_SPLIT_FIX)
+ {
+ tools::Long nMax = pHSplitter->GetPosPixel().X();
+ if( pTabControl->IsEffectiveRTL() )
+ nMax = pFrameWin->GetSizePixel().Width() - nMax;
+ --nMax;
+ if (nSize>nMax) nSize = nMax;
+ }
+
+ if ( nSize != pTabControl->GetSizePixel().Width() )
+ {
+ pTabControl->SetSizePixel( Size( nSize,
+ pTabControl->GetSizePixel().Height() ) );
+ RepeatResize();
+ }
+}
+
+void ScTabView::SetTabBarWidth( tools::Long nNewWidth )
+{
+ Size aSize = pTabControl->GetSizePixel();
+
+ if ( aSize.Width() != nNewWidth )
+ {
+ aSize.setWidth( nNewWidth );
+ pTabControl->SetSizePixel( aSize );
+ }
+}
+
+void ScTabView::SetRelTabBarWidth( double fRelTabBarWidth )
+{
+ if( (0.0 <= fRelTabBarWidth) && (fRelTabBarWidth <= 1.0) )
+ if( tools::Long nFrameWidth = pFrameWin->GetSizePixel().Width() )
+ SetTabBarWidth( static_cast< tools::Long >( fRelTabBarWidth * nFrameWidth + 0.5 ) );
+}
+
+void ScTabView::SetPendingRelTabBarWidth( double fRelTabBarWidth )
+{
+ mfPendingTabBarWidth = fRelTabBarWidth;
+ SetRelTabBarWidth( fRelTabBarWidth );
+}
+
+tools::Long ScTabView::GetTabBarWidth() const
+{
+ return pTabControl->GetSizePixel().Width();
+}
+
+double ScTabView::GetRelTabBarWidth()
+{
+ return 0.5;
+}
+
+ScGridWindow* ScTabView::GetActiveWin()
+{
+ ScSplitPos ePos = aViewData.GetActivePart();
+ OSL_ENSURE(pGridWin[ePos],"no active window");
+ return pGridWin[ePos];
+}
+
+void ScTabView::SetActivePointer( PointerStyle nPointer )
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin)
+ pWin->SetPointer( nPointer );
+}
+
+void ScTabView::ActiveGrabFocus()
+{
+ ScSplitPos ePos = aViewData.GetActivePart();
+ if (pGridWin[ePos])
+ pGridWin[ePos]->GrabFocus();
+}
+
+ScSplitPos ScTabView::FindWindow( const vcl::Window* pWindow ) const
+{
+ ScSplitPos eVal = SC_SPLIT_BOTTOMLEFT; // Default
+ for (sal_uInt16 i=0; i<4; i++)
+ if ( pGridWin[i] == pWindow )
+ eVal = static_cast<ScSplitPos>(i);
+
+ return eVal;
+}
+
+Point ScTabView::GetGridOffset() const
+{
+ Point aPos;
+
+ // size as in DoResize
+
+ bool bHeaders = aViewData.IsHeaderMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+
+ // Outline-Controls
+ if (bVOutline && pRowOutline[SC_SPLIT_BOTTOM])
+ aPos.AdjustX(pRowOutline[SC_SPLIT_BOTTOM]->GetDepthSize() );
+ if (bHOutline && pColOutline[SC_SPLIT_LEFT])
+ aPos.AdjustY(pColOutline[SC_SPLIT_LEFT]->GetDepthSize() );
+
+ if (bHeaders) // column/row headers
+ {
+ if (pRowBar[SC_SPLIT_BOTTOM])
+ aPos.AdjustX(pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() );
+ if (pColBar[SC_SPLIT_LEFT])
+ aPos.AdjustY(pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() );
+ }
+
+ return aPos;
+}
+
+// --- Scroll-Bars --------------------------------------------------------
+
+void ScTabView::SetZoomPercentFromCommand(sal_uInt16 nZoomPercent)
+{
+ // scroll wheel doesn't set the AppOptions default
+
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ SetZoomType(SvxZoomType::PERCENT, bSyncZoom);
+ Fraction aFract(nZoomPercent, 100);
+ SetZoom(aFract, aFract, bSyncZoom);
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ aViewData.GetBindings().Invalidate( SID_ATTR_ZOOM);
+ aViewData.GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER);
+ aViewData.GetBindings().Invalidate( SID_ZOOM_IN);
+ aViewData.GetBindings().Invalidate( SID_ZOOM_OUT);
+}
+
+bool ScTabView::ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos )
+{
+ HideNoteMarker();
+
+ bool bDone = false;
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if (pData && pData->GetMode() == CommandWheelMode::ZOOM)
+ {
+ if ( !aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace() )
+ {
+ // for ole inplace editing, the scale is defined by the visarea and client size
+ // and can't be changed directly
+
+ const Fraction& rOldY = aViewData.GetZoomY();
+ sal_uInt16 nOld = static_cast<tools::Long>( rOldY * 100 );
+ sal_uInt16 nNew;
+ if ( pData->GetDelta() < 0 )
+ nNew = std::max( MINZOOM, basegfx::zoomtools::zoomOut( nOld ));
+ else
+ nNew = std::min( MAXZOOM, basegfx::zoomtools::zoomIn( nOld ));
+ if ( nNew != nOld )
+ {
+ SetZoomPercentFromCommand(nNew);
+ }
+
+ bDone = true;
+ }
+ }
+ else
+ {
+ ScHSplitPos eHPos = WhichH(ePos);
+ ScVSplitPos eVPos = WhichV(ePos);
+ ScrollAdaptor* pHScroll = ( eHPos == SC_SPLIT_LEFT ) ? aHScrollLeft.get() : aHScrollRight.get();
+ ScrollAdaptor* pVScroll = ( eVPos == SC_SPLIT_TOP ) ? aVScrollTop.get() : aVScrollBottom.get();
+ if ( pGridWin[ePos] )
+ bDone = pGridWin[ePos]->HandleScrollCommand( rCEvt, pHScroll, pVScroll );
+ }
+ return bDone;
+}
+
+bool ScTabView::GestureZoomCommand(const CommandEvent& rCEvt)
+{
+ HideNoteMarker();
+
+ const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData();
+ if (!pData)
+ return false;
+
+ if (aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace())
+ return false;
+
+ if (pData->meEventType == GestureEventZoomType::Begin)
+ {
+ mfLastZoomScale = pData->mfScaleDelta;
+ return true;
+ }
+
+ if (pData->meEventType == GestureEventZoomType::Update)
+ {
+ double deltaBetweenEvents = (pData->mfScaleDelta - mfLastZoomScale) / mfLastZoomScale;
+ mfLastZoomScale = pData->mfScaleDelta;
+
+ // Accumulate fractional zoom to avoid small zoom changes from being ignored
+ mfAccumulatedZoom += deltaBetweenEvents;
+ int nZoomChangePercent = mfAccumulatedZoom * 100;
+ mfAccumulatedZoom -= nZoomChangePercent / 100.0;
+
+ const Fraction& rOldY = aViewData.GetZoomY();
+ sal_uInt16 nOld = static_cast<tools::Long>(rOldY * 100);
+ sal_uInt16 nNew = nOld + nZoomChangePercent;
+ nNew = std::clamp<sal_uInt16>(nNew, MINZOOM, MAXZOOM);
+
+ if (nNew != nOld)
+ {
+ SetZoomPercentFromCommand(nNew);
+ }
+
+ return true;
+ }
+ return true;
+}
+
+IMPL_LINK_NOARG(ScTabView, HScrollLeftHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aHScrollLeft.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, HScrollRightHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aHScrollRight.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, VScrollTopHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aVScrollTop.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, VScrollBottomHdl, weld::Scrollbar&, void)
+{
+ ScrollHdl(aVScrollBottom.get());
+}
+
+IMPL_LINK_NOARG(ScTabView, EndScrollHdl, const MouseEvent&, bool)
+{
+ if (bDragging)
+ {
+ UpdateScrollBars();
+ bDragging = false;
+ }
+ return false;
+}
+
+void ScTabView::ScrollHdl(ScrollAdaptor* pScroll)
+{
+ bool bHoriz = ( pScroll == aHScrollLeft.get() || pScroll == aHScrollRight.get() );
+ tools::Long nViewPos;
+ if ( bHoriz )
+ nViewPos = aViewData.GetPosX( (pScroll == aHScrollLeft.get()) ?
+ SC_SPLIT_LEFT : SC_SPLIT_RIGHT );
+ else
+ nViewPos = aViewData.GetPosY( (pScroll == aVScrollTop.get()) ?
+ SC_SPLIT_TOP : SC_SPLIT_BOTTOM );
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+
+ ScrollType eType = pScroll->GetScrollType();
+ if ( eType == ScrollType::Drag )
+ {
+ if (!bDragging)
+ {
+ bDragging = true;
+ nPrevDragPos = nViewPos;
+ }
+
+ // show scroll position
+ // (only QuickHelp, there is no entry for it in the status bar)
+
+ if (Help::IsQuickHelpEnabled())
+ {
+ Size aSize = pScroll->GetSizePixel();
+
+ /* Convert scrollbar mouse position to screen position. If RTL
+ mode of scrollbar differs from RTL mode of its parent, then the
+ direct call to Window::OutputToNormalizedScreenPixel() will
+ give unusable results, because calculation of screen position
+ is based on parent orientation and expects equal orientation of
+ the child position. Need to mirror mouse position before. */
+ Point aMousePos = pScroll->GetPointerPosPixel();
+ if( pScroll->IsRTLEnabled() != pScroll->GetParent()->IsRTLEnabled() )
+ aMousePos.setX( aSize.Width() - aMousePos.X() - 1 );
+ aMousePos = pScroll->OutputToNormalizedScreenPixel( aMousePos );
+
+ // convert top-left position of scrollbar to screen position
+ Point aPos = pScroll->OutputToNormalizedScreenPixel( Point() );
+
+ // get scrollbar scroll position for help text (row number/column name)
+ tools::Long nScrollMin = 0; // simulate RangeMin
+ if ( aViewData.GetHSplitMode()==SC_SPLIT_FIX && pScroll == aHScrollRight.get() )
+ nScrollMin = aViewData.GetFixPosX();
+ if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() )
+ nScrollMin = aViewData.GetFixPosY();
+ tools::Long nScrollPos = GetScrollBarPos( *pScroll ) + nScrollMin;
+
+ OUString aHelpStr;
+ tools::Rectangle aRect;
+ QuickHelpFlags nAlign;
+ if (bHoriz)
+ {
+ aHelpStr = ScResId(STR_COLUMN) +
+ " " + ScColToAlpha(static_cast<SCCOL>(nScrollPos));
+
+ aRect.SetLeft( aMousePos.X() );
+ aRect.SetTop( aPos.Y() - 4 );
+ nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
+ }
+ else
+ {
+ aHelpStr = ScResId(STR_ROW) +
+ " " + OUString::number(nScrollPos + 1);
+
+ // show quicktext always inside sheet area
+ aRect.SetLeft( bLayoutRTL ? (aPos.X() + aSize.Width() + 8) : (aPos.X() - 8) );
+ aRect.SetTop( aMousePos.Y() );
+ nAlign = (bLayoutRTL ? QuickHelpFlags::Left : QuickHelpFlags::Right) | QuickHelpFlags::VCenter;
+ }
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+
+ Help::ShowQuickHelp(pScroll->GetParent(), aRect, aHelpStr, nAlign);
+ }
+ }
+ else
+ bDragging = false;
+
+ tools::Long nDelta(0);
+ switch ( eType )
+ {
+ case ScrollType::LineUp:
+ nDelta = -1;
+ break;
+ case ScrollType::LineDown:
+ nDelta = 1;
+ break;
+ case ScrollType::PageUp:
+ if ( pScroll == aHScrollLeft.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsX( SC_SPLIT_LEFT ));
+ if ( pScroll == aHScrollRight.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsX( SC_SPLIT_RIGHT ));
+ if ( pScroll == aVScrollTop.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsY( SC_SPLIT_TOP ));
+ if ( pScroll == aVScrollBottom.get() ) nDelta = -static_cast<tools::Long>(aViewData.PrevCellsY( SC_SPLIT_BOTTOM ));
+ if (nDelta==0) nDelta=-1;
+ break;
+ case ScrollType::PageDown:
+ if ( pScroll == aHScrollLeft.get() ) nDelta = aViewData.VisibleCellsX( SC_SPLIT_LEFT );
+ if ( pScroll == aHScrollRight.get() ) nDelta = aViewData.VisibleCellsX( SC_SPLIT_RIGHT );
+ if ( pScroll == aVScrollTop.get() ) nDelta = aViewData.VisibleCellsY( SC_SPLIT_TOP );
+ if ( pScroll == aVScrollBottom.get() ) nDelta = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM );
+ if (nDelta==0) nDelta=1;
+ break;
+ default:
+ {
+ // only scroll in the correct direction, do not jitter around hidden ranges
+ tools::Long nScrollMin = 0; // simulate RangeMin
+ if ( aViewData.GetHSplitMode()==SC_SPLIT_FIX && pScroll == aHScrollRight.get() )
+ nScrollMin = aViewData.GetFixPosX();
+ if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() )
+ nScrollMin = aViewData.GetFixPosY();
+
+ tools::Long nScrollPos = GetScrollBarPos( *pScroll ) + nScrollMin;
+ nDelta = nScrollPos - nViewPos;
+
+ // tdf#152406 Disable anti-jitter code for scroll wheel events
+ // After moving thousands of columns to the right via
+ // horizontal scroll wheel or trackpad swipe events, most
+ // vertical scroll wheel or trackpad swipe events will trigger
+ // the anti-jitter code because nScrollPos and nPrevDragPos
+ // will be equal and nDelta will be overridden and set to zero.
+ // So, only use the anti-jitter code for mouse drag events.
+ if ( eType == ScrollType::Drag )
+ {
+ if ( nScrollPos > nPrevDragPos )
+ {
+ if (nDelta<0) nDelta=0;
+ }
+ else if ( nScrollPos < nPrevDragPos )
+ {
+ if (nDelta>0) nDelta=0;
+ }
+ else
+ nDelta = 0;
+ }
+ else if ( bHoriz )
+ {
+ // tdf#135478 Reduce sensitivity of horizontal scrollwheel
+ // Problem: at least on macOS, swipe events are very
+ // precise. So, when swiping at a slight angle off of
+ // vertical, swipe events will include a small amount
+ // of horizontal movement. Since horizontal swipe units
+ // are measured in cell widths, these small amounts of
+ // horizontal movement results in shifting many columns
+ // to the right or left while swiping almost vertically.
+ // So my hacky fix is to reduce the amount of horizontal
+ // swipe events to roughly match the "visual distance"
+ // of vertical swipe events.
+ // The reduction factor is arbitrary but is set to
+ // roughly the ratio of default cell width divided by
+ // default cell height. This hacky fix isn't a perfect
+ // fix, but hopefully it reduces the amount of
+ // unexpected horizontal shifting while swiping
+ // vertically to a tolerable amount for most users.
+ // Note: the potential downside of doing this is that
+ // some users might find horizontal swiping to be
+ // slower than they are used to. If that becomes an
+ // issue for enough users, the reduction factor may
+ // need to be lowered to find a good balance point.
+ static const sal_uInt16 nHScrollReductionFactor = 8;
+ if ( pScroll == aHScrollLeft.get() )
+ {
+ mnPendingaHScrollLeftDelta += nDelta;
+ nDelta = 0;
+ if ( abs(mnPendingaHScrollLeftDelta) > nHScrollReductionFactor )
+ {
+ nDelta = mnPendingaHScrollLeftDelta / nHScrollReductionFactor;
+ mnPendingaHScrollLeftDelta = mnPendingaHScrollLeftDelta % nHScrollReductionFactor;
+ }
+ }
+ else if ( pScroll == aHScrollRight.get() )
+ {
+ mnPendingaHScrollRightDelta += nDelta;
+ nDelta = 0;
+ if ( abs(mnPendingaHScrollRightDelta) > nHScrollReductionFactor )
+ {
+ nDelta = mnPendingaHScrollRightDelta / nHScrollReductionFactor;
+ mnPendingaHScrollRightDelta = mnPendingaHScrollRightDelta % nHScrollReductionFactor;
+ }
+ }
+ }
+
+ nPrevDragPos = nScrollPos;
+ }
+ break;
+ }
+
+ if (nDelta)
+ {
+ bool bUpdate = ( eType != ScrollType::Drag ); // don't alter the ranges while dragging
+ if ( bHoriz )
+ ScrollX( nDelta, (pScroll == aHScrollLeft.get()) ? SC_SPLIT_LEFT : SC_SPLIT_RIGHT, bUpdate );
+ else
+ ScrollY( nDelta, (pScroll == aVScrollTop.get()) ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM, bUpdate );
+ }
+}
+
+void ScTabView::ScrollX( tools::Long nDeltaX, ScHSplitPos eWhich, bool bUpdBars )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nOldX = aViewData.GetPosX(eWhich);
+ SCCOL nNewX = nOldX + static_cast<SCCOL>(nDeltaX);
+ if ( nNewX < 0 )
+ {
+ nDeltaX -= nNewX;
+ nNewX = 0;
+ }
+ if ( nNewX > rDoc.MaxCol() )
+ {
+ nDeltaX -= nNewX - rDoc.MaxCol();
+ nNewX = rDoc.MaxCol();
+ }
+
+ SCCOL nDir = ( nDeltaX > 0 ) ? 1 : -1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( rDoc.ColHidden(nNewX, nTab) &&
+ nNewX+nDir >= 0 && nNewX+nDir <= rDoc.MaxCol() )
+ nNewX = sal::static_int_cast<SCCOL>( nNewX + nDir );
+
+ // freeze
+
+ if (aViewData.GetHSplitMode() == SC_SPLIT_FIX)
+ {
+ if (eWhich == SC_SPLIT_LEFT)
+ nNewX = nOldX; // always keep the left part
+ else
+ {
+ SCCOL nFixX = aViewData.GetFixPosX();
+ if (nNewX < nFixX)
+ nNewX = nFixX;
+ }
+ }
+ if (nNewX == nOldX)
+ return;
+
+ HideAllCursors();
+
+ if ( nNewX >= 0 && nNewX <= rDoc.MaxCol() && nDeltaX )
+ {
+ SCCOL nTrackX = std::max( nOldX, nNewX );
+
+ // with VCL Update() affects all windows at the moment, that is why
+ // calling Update after scrolling of the GridWindow would possibly
+ // already have painted the column/row bar with updated position. -
+ // Therefore call Update once before on column/row bar
+ if (pColBar[eWhich])
+ pColBar[eWhich]->PaintImmediately();
+
+ tools::Long nOldPos = aViewData.GetScrPos( nTrackX, 0, eWhich ).X();
+ aViewData.SetPosX( eWhich, nNewX );
+ tools::Long nDiff = aViewData.GetScrPos( nTrackX, 0, eWhich ).X() - nOldPos;
+
+ if ( eWhich==SC_SPLIT_LEFT )
+ {
+ pGridWin[SC_SPLIT_BOTTOMLEFT]->ScrollPixel( nDiff, 0 );
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_TOPLEFT]->ScrollPixel( nDiff, 0 );
+ }
+ else
+ {
+ pGridWin[SC_SPLIT_BOTTOMRIGHT]->ScrollPixel( nDiff, 0 );
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_TOPRIGHT]->ScrollPixel( nDiff, 0 );
+ }
+ if (pColBar[eWhich]) { pColBar[eWhich]->Scroll( nDiff,0 ); pColBar[eWhich]->PaintImmediately(); }
+ if (pColOutline[eWhich]) pColOutline[eWhich]->ScrollPixel( nDiff );
+ if (bUpdBars)
+ UpdateScrollBars();
+ }
+
+ if (nDeltaX==1 || nDeltaX==-1)
+ pGridWin[aViewData.GetActivePart()]->PaintImmediately();
+
+ ShowAllCursors();
+
+ SetNewVisArea(); // MapMode must already be set
+
+ TestHintWindow();
+}
+
+void ScTabView::ScrollY( tools::Long nDeltaY, ScVSplitPos eWhich, bool bUpdBars )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCROW nOldY = aViewData.GetPosY(eWhich);
+ SCROW nNewY = nOldY + static_cast<SCROW>(nDeltaY);
+ if ( nNewY < 0 )
+ {
+ nDeltaY -= nNewY;
+ nNewY = 0;
+ }
+ if ( nNewY > rDoc.MaxRow() )
+ {
+ nDeltaY -= nNewY - rDoc.MaxRow();
+ nNewY = rDoc.MaxRow();
+ }
+
+ SCROW nDir = ( nDeltaY > 0 ) ? 1 : -1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( rDoc.RowHidden(nNewY, nTab) &&
+ nNewY+nDir >= 0 && nNewY+nDir <= rDoc.MaxRow() )
+ nNewY += nDir;
+
+ // freeze
+
+ if (aViewData.GetVSplitMode() == SC_SPLIT_FIX)
+ {
+ if (eWhich == SC_SPLIT_TOP)
+ nNewY = nOldY; // always keep the upper part
+ else
+ {
+ SCROW nFixY = aViewData.GetFixPosY();
+ if (nNewY < nFixY)
+ nNewY = nFixY;
+ }
+ }
+ if (nNewY == nOldY)
+ return;
+
+ HideAllCursors();
+
+ if ( nNewY >= 0 && nNewY <= rDoc.MaxRow() && nDeltaY )
+ {
+ SCROW nTrackY = std::max( nOldY, nNewY );
+
+ // adjust row headers before the actual scrolling, so it does not get painted twice
+ // PosY may then also not be set yet, pass on new value
+ SCROW nUNew = nNewY;
+ UpdateHeaderWidth( &eWhich, &nUNew ); // adjust row headers
+
+ if (pRowBar[eWhich])
+ pRowBar[eWhich]->PaintImmediately();
+
+ tools::Long nOldPos = aViewData.GetScrPos( 0, nTrackY, eWhich ).Y();
+ aViewData.SetPosY( eWhich, nNewY );
+ tools::Long nDiff = aViewData.GetScrPos( 0, nTrackY, eWhich ).Y() - nOldPos;
+
+ if ( eWhich==SC_SPLIT_TOP )
+ {
+ pGridWin[SC_SPLIT_TOPLEFT]->ScrollPixel( 0, nDiff );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_TOPRIGHT]->ScrollPixel( 0, nDiff );
+ }
+ else
+ {
+ pGridWin[SC_SPLIT_BOTTOMLEFT]->ScrollPixel( 0, nDiff );
+ if ( aViewData.GetHSplitMode() != SC_SPLIT_NONE )
+ pGridWin[SC_SPLIT_BOTTOMRIGHT]->ScrollPixel( 0, nDiff );
+ }
+ if (pRowBar[eWhich]) { pRowBar[eWhich]->Scroll( 0,nDiff ); pRowBar[eWhich]->PaintImmediately(); }
+ if (pRowOutline[eWhich]) pRowOutline[eWhich]->ScrollPixel( nDiff );
+ if (bUpdBars)
+ UpdateScrollBars();
+ }
+
+ if (nDeltaY==1 || nDeltaY==-1)
+ pGridWin[aViewData.GetActivePart()]->PaintImmediately();
+
+ ShowAllCursors();
+
+ SetNewVisArea(); // MapMode must already be set
+
+ TestHintWindow();
+}
+
+void ScTabView::ScrollLines( tools::Long nDeltaX, tools::Long nDeltaY )
+{
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ if (nDeltaX)
+ ScrollX(nDeltaX,WhichH(eWhich));
+ if (nDeltaY)
+ ScrollY(nDeltaY,WhichV(eWhich));
+}
+
+namespace
+{
+
+SCROW lcl_LastVisible( const ScViewData& rViewData )
+{
+ // If many rows are hidden at end of the document,
+ // then there should not be a switch to wide row headers because of this
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ SCROW nVis = rDoc.MaxRow();
+ SCROW startRow;
+ while ( nVis > 0 && rDoc.GetRowHeight( nVis, nTab, &startRow, nullptr ) == 0 )
+ nVis = std::max<SCROW>( startRow - 1, 0 );
+ return nVis;
+}
+
+} // anonymous namespace
+
+void ScTabView::UpdateHeaderWidth( const ScVSplitPos* pWhich, const SCROW* pPosY )
+{
+ if (!pRowBar[SC_SPLIT_BOTTOM])
+ return;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCROW nEndPos = rDoc.MaxRow();
+ if ( !aViewData.GetViewShell()->GetViewFrame().GetFrame().IsInPlace() )
+ {
+ // for OLE Inplace always MAXROW
+
+ if ( pWhich && *pWhich == SC_SPLIT_BOTTOM && pPosY )
+ nEndPos = *pPosY;
+ else
+ nEndPos = aViewData.GetPosY( SC_SPLIT_BOTTOM );
+ nEndPos += aViewData.CellsAtY( nEndPos, 1, SC_SPLIT_BOTTOM ); // VisibleCellsY
+ if (nEndPos > rDoc.MaxRow())
+ nEndPos = lcl_LastVisible( aViewData );
+
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ {
+ SCROW nTopEnd;
+ if ( pWhich && *pWhich == SC_SPLIT_TOP && pPosY )
+ nTopEnd = *pPosY;
+ else
+ nTopEnd = aViewData.GetPosY( SC_SPLIT_TOP );
+ nTopEnd += aViewData.CellsAtY( nTopEnd, 1, SC_SPLIT_TOP );// VisibleCellsY
+ if (nTopEnd > rDoc.MaxRow())
+ nTopEnd = lcl_LastVisible( aViewData );
+
+ if ( nTopEnd > nEndPos )
+ nEndPos = nTopEnd;
+ }
+ }
+
+ tools::Long nSmall = pRowBar[SC_SPLIT_BOTTOM]->GetSmallWidth();
+ tools::Long nBig = pRowBar[SC_SPLIT_BOTTOM]->GetBigWidth();
+ tools::Long nDiff = nBig - nSmall;
+
+ if (nEndPos>10000)
+ nEndPos = 10000;
+ else if (nEndPos<1) // avoid extra step at 0 (when only one row is visible)
+ nEndPos = 1;
+ tools::Long nWidth = nBig - ( 10000 - nEndPos ) * nDiff / 10000;
+
+ if (nWidth == pRowBar[SC_SPLIT_BOTTOM]->GetWidth() || bInUpdateHeader)
+ return;
+
+ bInUpdateHeader = true;
+
+ pRowBar[SC_SPLIT_BOTTOM]->SetWidth( nWidth );
+ if (pRowBar[SC_SPLIT_TOP])
+ pRowBar[SC_SPLIT_TOP]->SetWidth( nWidth );
+
+ RepeatResize();
+
+ // on VCL there are endless updates (each Update is valid for all windows)
+ //aCornerButton->Update(); // otherwise this never gets an Update
+
+ bInUpdateHeader = false;
+}
+
+static void ShowHide( vcl::Window* pWin, bool bShow )
+{
+ OSL_ENSURE(pWin || !bShow, "window is not present");
+ if (pWin)
+ pWin->Show(bShow);
+}
+
+void ScTabView::UpdateShow()
+{
+ bool bHScrollMode = aViewData.IsHScrollMode();
+ bool bVScrollMode = aViewData.IsVScrollMode();
+ bool bTabMode = aViewData.IsTabMode();
+ bool bOutlMode = aViewData.IsOutlineMode();
+ bool bHOutline = bOutlMode && lcl_HasColOutline(aViewData);
+ bool bVOutline = bOutlMode && lcl_HasRowOutline(aViewData);
+ bool bHeader = aViewData.IsHeaderMode();
+
+ bool bShowH = ( aViewData.GetHSplitMode() != SC_SPLIT_NONE );
+ bool bShowV = ( aViewData.GetVSplitMode() != SC_SPLIT_NONE );
+
+ if ( aViewData.GetDocShell()->IsPreview() )
+ bHScrollMode = bVScrollMode = bTabMode = bHeader = bHOutline = bVOutline = false;
+
+ // create Windows
+
+ if (bShowH && !pGridWin[SC_SPLIT_BOTTOMRIGHT])
+ {
+ pGridWin[SC_SPLIT_BOTTOMRIGHT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_BOTTOMRIGHT );
+ DoAddWin( pGridWin[SC_SPLIT_BOTTOMRIGHT] );
+ }
+ if (bShowV && !pGridWin[SC_SPLIT_TOPLEFT])
+ {
+ pGridWin[SC_SPLIT_TOPLEFT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_TOPLEFT );
+ DoAddWin( pGridWin[SC_SPLIT_TOPLEFT] );
+ }
+ if (bShowH && bShowV && !pGridWin[SC_SPLIT_TOPRIGHT])
+ {
+ pGridWin[SC_SPLIT_TOPRIGHT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_TOPRIGHT );
+ DoAddWin( pGridWin[SC_SPLIT_TOPRIGHT] );
+ }
+
+ if (bHOutline && !pColOutline[SC_SPLIT_LEFT])
+ pColOutline[SC_SPLIT_LEFT] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_HOR, &aViewData, SC_SPLIT_BOTTOMLEFT );
+ if (bShowH && bHOutline && !pColOutline[SC_SPLIT_RIGHT])
+ pColOutline[SC_SPLIT_RIGHT] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_HOR, &aViewData, SC_SPLIT_BOTTOMRIGHT );
+
+ if (bVOutline && !pRowOutline[SC_SPLIT_BOTTOM])
+ pRowOutline[SC_SPLIT_BOTTOM] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_VER, &aViewData, SC_SPLIT_BOTTOMLEFT );
+ if (bShowV && bVOutline && !pRowOutline[SC_SPLIT_TOP])
+ pRowOutline[SC_SPLIT_TOP] = VclPtr<ScOutlineWindow>::Create( pFrameWin, SC_OUTLINE_VER, &aViewData, SC_SPLIT_TOPLEFT );
+
+ if (bShowH && bHeader && !pColBar[SC_SPLIT_RIGHT])
+ pColBar[SC_SPLIT_RIGHT] = VclPtr<ScColBar>::Create( pFrameWin, SC_SPLIT_RIGHT,
+ &aHdrFunc, pHdrSelEng.get(), this );
+ if (bShowV && bHeader && !pRowBar[SC_SPLIT_TOP])
+ pRowBar[SC_SPLIT_TOP] = VclPtr<ScRowBar>::Create( pFrameWin, SC_SPLIT_TOP,
+ &aHdrFunc, pHdrSelEng.get(), this );
+
+ // show Windows
+
+ ShowHide( aHScrollLeft.get(), bHScrollMode );
+ ShowHide( aHScrollRight.get(), bShowH && bHScrollMode );
+ ShowHide( aVScrollBottom.get(), bVScrollMode );
+ ShowHide( aVScrollTop.get(), bShowV && bVScrollMode );
+
+ ShowHide( pHSplitter, bHScrollMode || bShowH ); // always generated
+ ShowHide( pVSplitter, bVScrollMode || bShowV );
+ ShowHide( pTabControl, bTabMode );
+
+ // from here dynamically generated
+
+ ShowHide( pGridWin[SC_SPLIT_BOTTOMRIGHT], bShowH );
+ ShowHide( pGridWin[SC_SPLIT_TOPLEFT], bShowV );
+ ShowHide( pGridWin[SC_SPLIT_TOPRIGHT], bShowH && bShowV );
+
+ ShowHide( pColOutline[SC_SPLIT_LEFT], bHOutline );
+ ShowHide( pColOutline[SC_SPLIT_RIGHT], bShowH && bHOutline );
+
+ ShowHide( pRowOutline[SC_SPLIT_BOTTOM], bVOutline );
+ ShowHide( pRowOutline[SC_SPLIT_TOP], bShowV && bVOutline );
+
+ ShowHide( pColBar[SC_SPLIT_RIGHT], bShowH && bHeader );
+ ShowHide( pRowBar[SC_SPLIT_TOP], bShowV && bHeader );
+
+ //! register new Gridwindows
+}
+
+bool ScTabView::UpdateVisibleRange()
+{
+ bool bChanged = false;
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin || !pWin->IsVisible())
+ continue;
+
+ if (pWin->UpdateVisibleRange())
+ bChanged = true;
+ }
+
+ return bChanged;
+}
+
+// --- Splitter --------------------------------------------------------
+
+IMPL_LINK( ScTabView, SplitHdl, Splitter*, pSplitter, void )
+{
+ if ( pSplitter == pHSplitter )
+ DoHSplit( pHSplitter->GetSplitPosPixel() );
+ else
+ DoVSplit( pVSplitter->GetSplitPosPixel() );
+
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX || aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ FreezeSplitters( true );
+
+ DoResize( aBorderPos, aFrameSize );
+}
+
+void ScTabView::DoHSplit(tools::Long nSplitPos)
+{
+ // nSplitPos is the real pixel position on the frame window,
+ // mirroring for RTL has to be done here.
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ if ( bLayoutRTL )
+ nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
+
+ tools::Long nMinPos;
+ tools::Long nMaxPos;
+
+ nMinPos = SPLIT_MARGIN;
+ if ( pRowBar[SC_SPLIT_BOTTOM] && pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() >= nMinPos )
+ nMinPos = pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width() + 1;
+ nMaxPos = aFrameSize.Width() - SPLIT_MARGIN;
+
+ ScSplitMode aOldMode = aViewData.GetHSplitMode();
+ ScSplitMode aNewMode = SC_SPLIT_NORMAL;
+
+ aViewData.SetHSplitPos( nSplitPos );
+ if ( nSplitPos < nMinPos || nSplitPos > nMaxPos )
+ aNewMode = SC_SPLIT_NONE;
+
+ aViewData.SetHSplitMode( aNewMode );
+
+ if ( aNewMode == aOldMode )
+ return;
+
+ UpdateShow(); // before ActivatePart !!
+
+ if ( aNewMode == SC_SPLIT_NONE )
+ {
+ if (aViewData.GetActivePart() == SC_SPLIT_TOPRIGHT)
+ ActivatePart( SC_SPLIT_TOPLEFT );
+ if (aViewData.GetActivePart() == SC_SPLIT_BOTTOMRIGHT)
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ }
+ else
+ {
+ SCCOL nOldDelta = aViewData.GetPosX( SC_SPLIT_LEFT );
+ tools::Long nLeftWidth = nSplitPos - pRowBar[SC_SPLIT_BOTTOM]->GetSizePixel().Width();
+ if ( nLeftWidth < 0 ) nLeftWidth = 0;
+ SCCOL nNewDelta = nOldDelta + aViewData.CellsAtX( nOldDelta, 1, SC_SPLIT_LEFT,
+ static_cast<sal_uInt16>(nLeftWidth) );
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( nNewDelta > rDoc.MaxCol() )
+ nNewDelta = rDoc.MaxCol();
+ aViewData.SetPosX( SC_SPLIT_RIGHT, nNewDelta );
+ if ( nNewDelta > aViewData.GetCurX() )
+ ActivatePart( (WhichV(aViewData.GetActivePart()) == SC_SPLIT_BOTTOM) ?
+ SC_SPLIT_BOTTOMLEFT : SC_SPLIT_TOPLEFT );
+ else
+ ActivatePart( (WhichV(aViewData.GetActivePart()) == SC_SPLIT_BOTTOM) ?
+ SC_SPLIT_BOTTOMRIGHT : SC_SPLIT_TOPRIGHT );
+ }
+
+ // Form Layer needs to know the visible part of all windows
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin)
+ pWin->SetMapMode( pWin->GetDrawMapMode() );
+ SetNewVisArea();
+
+ PaintGrid();
+ PaintTop();
+
+ InvalidateSplit();
+}
+
+void ScTabView::DoVSplit(tools::Long nSplitPos)
+{
+ tools::Long nMinPos;
+ tools::Long nMaxPos;
+ SCROW nOldDelta;
+
+ nMinPos = SPLIT_MARGIN;
+ if ( pColBar[SC_SPLIT_LEFT] && pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() >= nMinPos )
+ nMinPos = pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height() + 1;
+ nMaxPos = aFrameSize.Height() - SPLIT_MARGIN;
+
+ ScSplitMode aOldMode = aViewData.GetVSplitMode();
+ ScSplitMode aNewMode = SC_SPLIT_NORMAL;
+
+ aViewData.SetVSplitPos( nSplitPos );
+ if ( nSplitPos < nMinPos || nSplitPos > nMaxPos )
+ aNewMode = SC_SPLIT_NONE;
+
+ aViewData.SetVSplitMode( aNewMode );
+
+ if ( aNewMode == aOldMode )
+ return;
+
+ UpdateShow(); // before ActivatePart !!
+
+ if ( aNewMode == SC_SPLIT_NONE )
+ {
+ nOldDelta = aViewData.GetPosY( SC_SPLIT_TOP );
+ aViewData.SetPosY( SC_SPLIT_BOTTOM, nOldDelta );
+
+ if (aViewData.GetActivePart() == SC_SPLIT_TOPLEFT)
+ ActivatePart( SC_SPLIT_BOTTOMLEFT );
+ if (aViewData.GetActivePart() == SC_SPLIT_TOPRIGHT)
+ ActivatePart( SC_SPLIT_BOTTOMRIGHT );
+ }
+ else
+ {
+ if ( aOldMode == SC_SPLIT_NONE )
+ nOldDelta = aViewData.GetPosY( SC_SPLIT_BOTTOM );
+ else
+ nOldDelta = aViewData.GetPosY( SC_SPLIT_TOP );
+
+ aViewData.SetPosY( SC_SPLIT_TOP, nOldDelta );
+ tools::Long nTopHeight = nSplitPos - pColBar[SC_SPLIT_LEFT]->GetSizePixel().Height();
+ if ( nTopHeight < 0 ) nTopHeight = 0;
+ SCROW nNewDelta = nOldDelta + aViewData.CellsAtY( nOldDelta, 1, SC_SPLIT_TOP,
+ static_cast<sal_uInt16>(nTopHeight) );
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( nNewDelta > rDoc.MaxRow() )
+ nNewDelta = rDoc.MaxRow();
+ aViewData.SetPosY( SC_SPLIT_BOTTOM, nNewDelta );
+ if ( nNewDelta > aViewData.GetCurY() )
+ ActivatePart( (WhichH(aViewData.GetActivePart()) == SC_SPLIT_LEFT) ?
+ SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
+ else
+ ActivatePart( (WhichH(aViewData.GetActivePart()) == SC_SPLIT_LEFT) ?
+ SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
+ }
+
+ // Form Layer needs to know the visible part of all windows
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin)
+ pWin->SetMapMode( pWin->GetDrawMapMode() );
+ SetNewVisArea();
+
+ PaintGrid();
+ PaintLeft();
+
+ InvalidateSplit();
+}
+
+Point ScTabView::GetInsertPos() const
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+ SCTAB nTab = aViewData.GetTabNo();
+ tools::Long nPosX = 0;
+ for (SCCOL i=0; i<nCol; i++)
+ nPosX += rDoc.GetColWidth(i,nTab);
+ nPosX = o3tl::convert(nPosX, o3tl::Length::twip, o3tl::Length::mm100);
+ if ( rDoc.IsNegativePage( nTab ) )
+ nPosX = -nPosX;
+ tools::Long nPosY = rDoc.GetRowHeight( 0, nRow-1, nTab);
+ nPosY = o3tl::convert(nPosY, o3tl::Length::twip, o3tl::Length::mm100);
+ return Point(nPosX,nPosY);
+}
+
+Point ScTabView::GetChartInsertPos( const Size& rSize, const ScRange& rCellRange )
+{
+ Point aInsertPos;
+ const tools::Long nBorder = 100; // leave 1mm for border
+ tools::Long nNeededWidth = rSize.Width() + 2 * nBorder;
+ tools::Long nNeededHeight = rSize.Height() + 2 * nBorder;
+
+ // use the active window, or lower/right if frozen (as in CalcZoom)
+ ScSplitPos eUsedPart = aViewData.GetActivePart();
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+
+ ScGridWindow* pWin = pGridWin[eUsedPart].get();
+ OSL_ENSURE( pWin, "Window not found" );
+ if (pWin)
+ {
+ ActivatePart( eUsedPart );
+
+ // get the visible rectangle in logic units
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ MapMode aDrawMode = pWin->GetDrawMapMode();
+ tools::Rectangle aVisible(
+ bLOKActive ?
+ OutputDevice::LogicToLogic( aViewData.getLOKVisibleArea(), MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM) )
+ : pWin->PixelToLogic( tools::Rectangle( Point(0,0), pWin->GetOutputSizePixel() ), aDrawMode ) );
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ tools::Long nDocX = o3tl::convert(rDoc.GetColOffset(rDoc.MaxCol() + 1, nTab), o3tl::Length::twip, o3tl::Length::mm100) * nLayoutSign;
+ tools::Long nDocY = o3tl::convert(rDoc.GetRowOffset( rDoc.MaxRow() + 1, nTab ), o3tl::Length::twip, o3tl::Length::mm100);
+
+ if ( aVisible.Left() * nLayoutSign > nDocX * nLayoutSign )
+ aVisible.SetLeft( nDocX );
+ if ( aVisible.Right() * nLayoutSign > nDocX * nLayoutSign )
+ aVisible.SetRight( nDocX );
+ if ( aVisible.Top() > nDocY )
+ aVisible.SetTop( nDocY );
+ if ( aVisible.Bottom() > nDocY )
+ aVisible.SetBottom( nDocY );
+
+ // get the logic position of the selection
+
+ tools::Rectangle aSelection = rDoc.GetMMRect( rCellRange.aStart.Col(), rCellRange.aStart.Row(),
+ rCellRange.aEnd.Col(), rCellRange.aEnd.Row(), nTab );
+
+ if (bLOKActive && bLayoutRTL)
+ {
+ // In this case we operate in negative X coordinates. The rectangle aSelection already
+ // has negative X coordinates. So the x coordinates in the rectangle aVisible(from getLOKVisibleArea)
+ // need be negated to match.
+ aVisible = tools::Rectangle(-aVisible.Right(), aVisible.Top(), -aVisible.Left(), aVisible.Bottom());
+ }
+
+ tools::Long nLeftSpace = aSelection.Left() - aVisible.Left();
+ tools::Long nRightSpace = aVisible.Right() - aSelection.Right();
+ tools::Long nTopSpace = aSelection.Top() - aVisible.Top();
+ tools::Long nBottomSpace = aVisible.Bottom() - aSelection.Bottom();
+
+ bool bFitLeft = ( nLeftSpace >= nNeededWidth );
+ bool bFitRight = ( nRightSpace >= nNeededWidth );
+
+ if ( bFitLeft || bFitRight )
+ {
+ // first preference: completely left or right of the selection
+
+ // if both fit, prefer left in RTL mode, right otherwise
+ bool bPutLeft = bFitLeft && ( bLayoutRTL || !bFitRight );
+
+ if ( bPutLeft )
+ aInsertPos.setX( aSelection.Left() - nNeededWidth );
+ else
+ aInsertPos.setX( aSelection.Right() + 1 );
+
+ // align with top of selection (is moved again if it doesn't fit)
+ aInsertPos.setY( std::max( aSelection.Top(), aVisible.Top() ) );
+ }
+ else if ( nTopSpace >= nNeededHeight || nBottomSpace >= nNeededHeight )
+ {
+ // second preference: completely above or below the selection
+ if ( nBottomSpace > nNeededHeight ) // bottom is preferred
+ aInsertPos.setY( aSelection.Bottom() + 1 );
+ else
+ aInsertPos.setY( aSelection.Top() - nNeededHeight );
+
+ // align with (logic) left edge of selection (moved again if it doesn't fit)
+ if ( bLayoutRTL )
+ aInsertPos.setX( std::min( aSelection.Right(), aVisible.Right() ) - nNeededWidth + 1 );
+ else
+ aInsertPos.setX( std::max( aSelection.Left(), aVisible.Left() ) );
+ }
+ else
+ {
+ // place to the (logic) right of the selection and move so it fits
+
+ if ( bLayoutRTL )
+ aInsertPos.setX( aSelection.Left() - nNeededWidth );
+ else
+ aInsertPos.setX( aSelection.Right() + 1 );
+ aInsertPos.setY( std::max( aSelection.Top(), aVisible.Top() ) );
+ }
+
+ // move the position if the object doesn't fit in the screen
+
+ tools::Rectangle aCompareRect( aInsertPos, Size( nNeededWidth, nNeededHeight ) );
+ if ( aCompareRect.Right() > aVisible.Right() )
+ aInsertPos.AdjustX( -(aCompareRect.Right() - aVisible.Right()) );
+ if ( aCompareRect.Bottom() > aVisible.Bottom() )
+ aInsertPos.AdjustY( -(aCompareRect.Bottom() - aVisible.Bottom()) );
+
+ if ( aInsertPos.X() < aVisible.Left() )
+ aInsertPos.setX( aVisible.Left() );
+ if ( aInsertPos.Y() < aVisible.Top() )
+ aInsertPos.setY( aVisible.Top() );
+
+ // nNeededWidth / nNeededHeight includes all borders - move aInsertPos to the
+ // object position, inside the border
+
+ aInsertPos.AdjustX(nBorder );
+ aInsertPos.AdjustY(nBorder );
+ }
+ return aInsertPos;
+}
+
+Point ScTabView::GetChartDialogPos( const Size& rDialogSize, const tools::Rectangle& rLogicChart )
+{
+ // rDialogSize must be in pixels, rLogicChart in 1/100 mm. Return value is in pixels.
+
+ Point aRet;
+
+ // use the active window, or lower/right if frozen (as in CalcZoom)
+ ScSplitPos eUsedPart = aViewData.GetActivePart();
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+
+ ScGridWindow* pWin = pGridWin[eUsedPart].get();
+ OSL_ENSURE( pWin, "Window not found" );
+ if (pWin)
+ {
+ MapMode aDrawMode = pWin->GetDrawMapMode();
+ tools::Rectangle aObjPixel = pWin->LogicToPixel( rLogicChart, aDrawMode );
+ AbsoluteScreenPixelRectangle aObjAbs( pWin->OutputToAbsoluteScreenPixel( aObjPixel.TopLeft() ),
+ pWin->OutputToAbsoluteScreenPixel( aObjPixel.BottomRight() ) );
+
+ AbsoluteScreenPixelRectangle aDesktop = pWin->GetDesktopRectPixel();
+ Size aSpace = pWin->LogicToPixel( Size(8, 12), MapMode(MapUnit::MapAppFont));
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+
+ bool bCenterHor = false;
+
+ if ( aDesktop.Bottom() - aObjAbs.Bottom() >= rDialogSize.Height() + aSpace.Height() )
+ {
+ // first preference: below the chart
+
+ aRet.setY( aObjAbs.Bottom() + aSpace.Height() );
+ bCenterHor = true;
+ }
+ else if ( aObjAbs.Top() - aDesktop.Top() >= rDialogSize.Height() + aSpace.Height() )
+ {
+ // second preference: above the chart
+
+ aRet.setY( aObjAbs.Top() - rDialogSize.Height() - aSpace.Height() );
+ bCenterHor = true;
+ }
+ else
+ {
+ bool bFitLeft = ( aObjAbs.Left() - aDesktop.Left() >= rDialogSize.Width() + aSpace.Width() );
+ bool bFitRight = ( aDesktop.Right() - aObjAbs.Right() >= rDialogSize.Width() + aSpace.Width() );
+
+ if ( bFitLeft || bFitRight )
+ {
+ // if both fit, prefer right in RTL mode, left otherwise
+ bool bPutRight = bFitRight && ( bLayoutRTL || !bFitLeft );
+ if ( bPutRight )
+ aRet.setX( aObjAbs.Right() + aSpace.Width() );
+ else
+ aRet.setX( aObjAbs.Left() - rDialogSize.Width() - aSpace.Width() );
+
+ // center vertically
+ aRet.setY( aObjAbs.Top() + ( aObjAbs.GetHeight() - rDialogSize.Height() ) / 2 );
+ }
+ else
+ {
+ // doesn't fit on any edge - put at the bottom of the screen
+ aRet.setY( aDesktop.Bottom() - rDialogSize.Height() );
+ bCenterHor = true;
+ }
+ }
+ if ( bCenterHor )
+ aRet.setX( aObjAbs.Left() + ( aObjAbs.GetWidth() - rDialogSize.Width() ) / 2 );
+
+ // limit to screen (centering might lead to invalid positions)
+ if ( aRet.X() + rDialogSize.Width() - 1 > aDesktop.Right() )
+ aRet.setX( aDesktop.Right() - rDialogSize.Width() + 1 );
+ if ( aRet.X() < aDesktop.Left() )
+ aRet.setX( aDesktop.Left() );
+ if ( aRet.Y() + rDialogSize.Height() - 1 > aDesktop.Bottom() )
+ aRet.setY( aDesktop.Bottom() - rDialogSize.Height() + 1 );
+ if ( aRet.Y() < aDesktop.Top() )
+ aRet.setY( aDesktop.Top() );
+ }
+
+ return aRet;
+}
+
+void ScTabView::LockModifiers( sal_uInt16 nModifiers )
+{
+ pSelEngine->LockModifiers( nModifiers );
+ pHdrSelEng->LockModifiers( nModifiers );
+}
+
+sal_uInt16 ScTabView::GetLockedModifiers() const
+{
+ return pSelEngine->GetLockedModifiers();
+}
+
+Point ScTabView::GetMousePosPixel()
+{
+ Point aPos;
+ ScGridWindow* pWin = GetActiveWin();
+
+ if ( pWin )
+ aPos = pWin->GetMousePosPixel();
+
+ return aPos;
+}
+
+void ScTabView::FreezeSplitters( bool bFreeze, SplitMethod eSplitMethod, SCCOLROW nFreezeIndex)
+{
+ if ((eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_ROW) && nFreezeIndex < 0)
+ nFreezeIndex = 0;
+
+ ScSplitMode eOldH = aViewData.GetHSplitMode();
+ ScSplitMode eOldV = aViewData.GetVSplitMode();
+
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ if ( eOldV != SC_SPLIT_NONE )
+ ePos = SC_SPLIT_TOPLEFT;
+ vcl::Window* pWin = pGridWin[ePos];
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ bool bUpdateFix = false;
+
+ if ( bFreeze )
+ {
+ Point aWinStart = pWin->GetPosPixel();
+ aViewData.GetDocShell()->SetDocumentModified();
+
+ Point aSplit;
+ SCCOL nPosX = 1;
+ SCROW nPosY = 1;
+ if (eOldV != SC_SPLIT_NONE || eOldH != SC_SPLIT_NONE)
+ {
+ if ( eOldV != SC_SPLIT_NONE && (eSplitMethod == SC_SPLIT_METHOD_ROW || eSplitMethod == SC_SPLIT_METHOD_CURSOR))
+ aSplit.setY( aViewData.GetVSplitPos() - aWinStart.Y() );
+
+ if ( eOldH != SC_SPLIT_NONE && (eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_CURSOR))
+ {
+ tools::Long nSplitPos = aViewData.GetHSplitPos();
+ if ( bLayoutRTL )
+ nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
+ aSplit.setX( nSplitPos - aWinStart.X() );
+ }
+
+ aViewData.GetPosFromPixel( aSplit.X(), aSplit.Y(), ePos, nPosX, nPosY );
+ bool bLeft;
+ bool bTop;
+ aViewData.GetMouseQuadrant( aSplit, ePos, nPosX, nPosY, bLeft, bTop );
+ if (eSplitMethod == SC_SPLIT_METHOD_COL)
+ nPosX = static_cast<SCCOL>(nFreezeIndex);
+ else if (!bLeft)
+ ++nPosX;
+ if (eSplitMethod == SC_SPLIT_METHOD_ROW)
+ nPosY = static_cast<SCROW>(nFreezeIndex);
+ else if (!bTop)
+ ++nPosY;
+ }
+ else
+ {
+ switch(eSplitMethod)
+ {
+ case SC_SPLIT_METHOD_ROW:
+ {
+ nPosX = 0;
+ nPosY = static_cast<SCROW>(nFreezeIndex);
+ }
+ break;
+ case SC_SPLIT_METHOD_COL:
+ {
+ nPosX = static_cast<SCCOL>(nFreezeIndex);
+ nPosY = 0;
+ }
+ break;
+ case SC_SPLIT_METHOD_CURSOR:
+ {
+ nPosX = aViewData.GetCurX();
+ nPosY = aViewData.GetCurY();
+ }
+ break;
+ }
+ }
+
+ SCROW nTopPos = aViewData.GetPosY(SC_SPLIT_BOTTOM);
+ SCROW nBottomPos = nPosY;
+ SCCOL nLeftPos = aViewData.GetPosX(SC_SPLIT_LEFT);
+ SCCOL nRightPos = nPosX;
+
+ if (eSplitMethod == SC_SPLIT_METHOD_ROW || eSplitMethod == SC_SPLIT_METHOD_CURSOR)
+ {
+ if (eOldV != SC_SPLIT_NONE)
+ {
+ nTopPos = aViewData.GetPosY(SC_SPLIT_TOP);
+ if (aViewData.GetPosY(SC_SPLIT_BOTTOM) > nBottomPos)
+ nBottomPos = aViewData.GetPosY(SC_SPLIT_BOTTOM);
+ }
+ aSplit = aViewData.GetScrPos(nPosX, nPosY, ePos, true);
+ if (aSplit.Y() > 0)
+ {
+ aViewData.SetVSplitMode(SC_SPLIT_FIX);
+ aViewData.SetVSplitPos(aSplit.Y() + aWinStart.Y());
+ aViewData.SetFixPosY(nPosY);
+
+ aViewData.SetPosY(SC_SPLIT_TOP, nTopPos);
+ aViewData.SetPosY(SC_SPLIT_BOTTOM, nBottomPos);
+ }
+ else if (nPosY == 1 && eSplitMethod == SC_SPLIT_METHOD_ROW)
+ {
+ // Freeze first row, but row 1 is not visible on screen now == special handling
+ aViewData.SetVSplitMode(SC_SPLIT_FIX);
+ aViewData.SetFixPosY(nPosY);
+
+ aViewData.SetPosY(SC_SPLIT_TOP, 0);
+ bUpdateFix = true;
+ }
+ else
+ aViewData.SetVSplitMode(SC_SPLIT_NONE);
+ }
+
+ if (eSplitMethod == SC_SPLIT_METHOD_COL || eSplitMethod == SC_SPLIT_METHOD_CURSOR)
+ {
+ if (eOldH != SC_SPLIT_NONE)
+ {
+ if (aViewData.GetPosX(SC_SPLIT_RIGHT) > nRightPos)
+ nRightPos = aViewData.GetPosX(SC_SPLIT_RIGHT);
+ }
+ aSplit = aViewData.GetScrPos( nPosX, nPosY, ePos, true );
+ if (nPosX > aViewData.GetPosX(SC_SPLIT_LEFT)) // (aSplit.X() > 0) doesn't work for RTL
+ {
+ tools::Long nSplitPos = aSplit.X() + aWinStart.X();
+ if ( bLayoutRTL )
+ nSplitPos = pFrameWin->GetOutputSizePixel().Width() - nSplitPos - 1;
+
+ aViewData.SetHSplitMode( SC_SPLIT_FIX );
+ aViewData.SetHSplitPos( nSplitPos );
+ aViewData.SetFixPosX( nPosX );
+
+ aViewData.SetPosX(SC_SPLIT_LEFT, nLeftPos);
+ aViewData.SetPosX(SC_SPLIT_RIGHT, nRightPos);
+ }
+ else if (nPosX == 1 && eSplitMethod == SC_SPLIT_METHOD_COL)
+ {
+ // Freeze first column, but col A is not visible on screen now == special handling
+ aViewData.SetHSplitMode(SC_SPLIT_FIX);
+ aViewData.SetFixPosX(nPosX);
+
+ aViewData.SetPosX(SC_SPLIT_RIGHT, aViewData.GetPosX(SC_SPLIT_LEFT));
+ aViewData.SetPosX(SC_SPLIT_LEFT, 0);
+ bUpdateFix = true;
+ }
+ else
+ aViewData.SetHSplitMode( SC_SPLIT_NONE );
+ }
+ }
+ else // unfreeze
+ {
+ if ( eOldH == SC_SPLIT_FIX )
+ aViewData.SetHSplitMode( SC_SPLIT_NORMAL );
+ if ( eOldV == SC_SPLIT_FIX )
+ aViewData.SetVSplitMode( SC_SPLIT_NORMAL );
+ }
+
+ // Form Layer needs to know the visible part of all windows
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & p : pGridWin)
+ if (p)
+ p->SetMapMode( p->GetDrawMapMode() );
+ SetNewVisArea();
+
+ RepeatResize(bUpdateFix);
+
+ UpdateShow();
+ PaintLeft();
+ PaintTop();
+ PaintGrid();
+
+ // SC_FOLLOW_NONE: only update active part
+ AlignToCursor( aViewData.GetCurX(), aViewData.GetCurY(), SC_FOLLOW_NONE );
+ UpdateAutoFillMark();
+
+ InvalidateSplit();
+}
+
+void ScTabView::RemoveSplit()
+{
+ if (aViewData.GetHSplitMode() == SC_SPLIT_FIX || aViewData.GetVSplitMode() == SC_SPLIT_FIX)
+ aViewData.GetDocShell()->SetDocumentModified();
+ DoHSplit( 0 );
+ DoVSplit( 0 );
+ RepeatResize();
+}
+
+void ScTabView::SplitAtCursor()
+{
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ if ( aViewData.GetVSplitMode() != SC_SPLIT_NONE )
+ ePos = SC_SPLIT_TOPLEFT;
+ vcl::Window* pWin = pGridWin[ePos];
+ Point aWinStart = pWin->GetPosPixel();
+
+ SCCOL nPosX = aViewData.GetCurX();
+ SCROW nPosY = aViewData.GetCurY();
+ Point aSplit = aViewData.GetScrPos( nPosX, nPosY, ePos, true );
+ if ( nPosX > 0 )
+ DoHSplit( aSplit.X() + aWinStart.X() );
+ else
+ DoHSplit( 0 );
+ if ( nPosY > 0 )
+ DoVSplit( aSplit.Y() + aWinStart.Y() );
+ else
+ DoVSplit( 0 );
+ RepeatResize();
+}
+
+void ScTabView::SplitAtPixel( const Point& rPixel )
+{
+ // pixel is relative to the entire View, not to the first GridWin
+
+ if ( rPixel.X() > 0 )
+ DoHSplit( rPixel.X() );
+ else
+ DoHSplit( 0 );
+ if ( rPixel.Y() > 0 )
+ DoVSplit( rPixel.Y() );
+ else
+ DoVSplit( 0 );
+ RepeatResize();
+}
+
+void ScTabView::InvalidateSplit()
+{
+ SfxBindings& rBindings = aViewData.GetBindings();
+ rBindings.Invalidate( SID_WINDOW_SPLIT );
+ rBindings.Invalidate( SID_WINDOW_FIX );
+ rBindings.Invalidate( SID_WINDOW_FIX_COL );
+ rBindings.Invalidate( SID_WINDOW_FIX_ROW );
+
+ pHSplitter->SetFixed( aViewData.GetHSplitMode() == SC_SPLIT_FIX );
+ pVSplitter->SetFixed( aViewData.GetVSplitMode() == SC_SPLIT_FIX );
+}
+
+void ScTabView::SetNewVisArea()
+{
+ // Draw-MapMode must be set for Controls when VisAreaChanged
+ // (also when Edit-MapMode is set instead)
+ MapMode aOldMode[4];
+ MapMode aDrawMode[4];
+ sal_uInt16 i;
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ aOldMode[i] = pGridWin[i]->GetMapMode();
+ aDrawMode[i] = pGridWin[i]->GetDrawMapMode();
+ if (aDrawMode[i] != aOldMode[i])
+ pGridWin[i]->SetMapMode(aDrawMode[i]);
+ }
+
+ vcl::Window* pActive = pGridWin[aViewData.GetActivePart()];
+ if (pActive)
+ aViewData.GetViewShell()->VisAreaChanged();
+ if (pDrawView)
+ pDrawView->VisAreaChanged(nullptr); // no window passed on -> for all windows
+
+ UpdateAllOverlays(); // #i79909# with drawing MapMode set
+
+ for (i=0; i<4; i++)
+ if (pGridWin[i] && aDrawMode[i] != aOldMode[i])
+ {
+ pGridWin[i]->flushOverlayManager(); // #i79909# flush overlays before switching to edit MapMode
+ pGridWin[i]->SetMapMode(aOldMode[i]);
+ }
+
+ SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+ css::uno::Reference<css::frame::XController> xController = rFrame.GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->VisAreaChanged();
+ }
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccVisAreaChanged));
+}
+
+bool ScTabView::HasPageFieldDataAtCursor() const
+{
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+ if (pWin)
+ return pWin->GetDPFieldOrientation( nCol, nRow ) == sheet::DataPilotFieldOrientation_PAGE;
+
+ return false;
+}
+
+void ScTabView::StartDataSelect()
+{
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+
+ if (!pWin)
+ return;
+
+ switch (pWin->GetDPFieldOrientation(nCol, nRow))
+ {
+ case sheet::DataPilotFieldOrientation_PAGE:
+ // #i36598# If the cursor is on a page field's data cell,
+ // no meaningful input is possible anyway, so this function
+ // can be used to select a page field entry.
+ pWin->LaunchPageFieldMenu( nCol, nRow );
+ return;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ case sheet::DataPilotFieldOrientation_ROW:
+ pWin->LaunchDPFieldMenu( nCol, nRow );
+ return;
+ default:
+ ;
+ }
+
+ // Do autofilter if the current cell has autofilter button. Otherwise do
+ // a normal data select popup.
+ const ScMergeFlagAttr* pAttr =
+ aViewData.GetDocument().GetAttr(
+ nCol, nRow, aViewData.GetTabNo(), ATTR_MERGE_FLAG);
+
+ if (pAttr->HasAutoFilter())
+ pWin->LaunchAutoFilterMenu(nCol, nRow);
+ else
+ pWin->LaunchDataSelectMenu(nCol, nRow);
+}
+
+void ScTabView::EnableRefInput(bool bFlag)
+{
+ aHScrollLeft->EnableInput(bFlag);
+ aHScrollRight->EnableInput(bFlag);
+ aVScrollBottom->EnableInput(bFlag);
+ aVScrollTop->EnableInput(bFlag);
+
+ // from here on dynamically created ones
+
+ if(pTabControl!=nullptr) pTabControl->EnableInput(bFlag);
+
+ for (auto& p : pGridWin)
+ if (p)
+ p->EnableInput(bFlag, false);
+ for (auto& p : pColBar)
+ if (p)
+ p->EnableInput(bFlag, false);
+ for (auto& p : pRowBar)
+ if (p)
+ p->EnableInput(bFlag, false);
+}
+
+void ScTabView::EnableAutoSpell( bool bEnable )
+{
+ if (bEnable)
+ mpSpellCheckCxt =
+ std::make_shared<sc::SpellCheckContext>(&aViewData.GetDocument(),
+ aViewData.GetTabNo());
+ else
+ mpSpellCheckCxt.reset();
+
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->SetAutoSpellContext(mpSpellCheckCxt);
+ }
+}
+
+void ScTabView::ResetAutoSpell()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->ResetAutoSpell();
+ }
+}
+
+void ScTabView::ResetAutoSpellForContentChange()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->ResetAutoSpellForContentChange();
+ }
+}
+
+void ScTabView::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges )
+{
+ for (VclPtr<ScGridWindow> & pWin: pGridWin)
+ {
+ if (!pWin)
+ continue;
+
+ pWin->SetAutoSpellData(nPosX, nPosY, pRanges);
+ }
+}
+
+namespace
+{
+
+tools::Long lcl_GetRowHeightPx(const ScViewData &rViewData, SCROW nRow, SCTAB nTab)
+{
+ const sal_uInt16 nSize = rViewData.GetDocument().GetRowHeight(nRow, nTab);
+ return ScViewData::ToPixel(nSize, rViewData.GetPPTY());
+}
+
+tools::Long lcl_GetColWidthPx(const ScViewData &rViewData, SCCOL nCol, SCTAB nTab)
+{
+ const sal_uInt16 nSize = rViewData.GetDocument().GetColWidth(nCol, nTab);
+ return ScViewData::ToPixel(nSize, rViewData.GetPPTX());
+}
+
+void lcl_getGroupIndexes(const ScOutlineArray& rArray, SCCOLROW nStart, SCCOLROW nEnd, std::vector<size_t>& rGroupIndexes)
+{
+ rGroupIndexes.clear();
+ const size_t nGroupDepth = rArray.GetDepth();
+ rGroupIndexes.resize(nGroupDepth);
+
+ // Get first group per each level
+ for (size_t nLevel = 0; nLevel < nGroupDepth; ++nLevel)
+ {
+ if (rArray.GetCount(nLevel))
+ {
+ // look for a group inside the [nStartRow+1, nEndRow] range
+ size_t nIndex;
+ bool bFound = rArray.GetEntryIndexInRange(nLevel, nStart + 1, nEnd, nIndex);
+ if (bFound)
+ {
+ if (nIndex > 0)
+ {
+ // is there a previous group not inside the range
+ // but anyway intersecting it ?
+ const ScOutlineEntry* pPrevEntry = rArray.GetEntry(nLevel, nIndex - 1);
+ if (pPrevEntry && nStart < pPrevEntry->GetEnd())
+ {
+ --nIndex;
+ }
+ }
+ }
+ else
+ {
+ // look for a group which contains nStartRow+1
+ bFound = rArray.GetEntryIndex(nLevel, nStart + 1, nIndex);
+ if (!bFound)
+ {
+ // look for a group which contains nEndRow
+ bFound = rArray.GetEntryIndex(nLevel, nEnd, nIndex);
+ }
+ }
+
+ if (bFound)
+ {
+ // skip groups with no visible control
+ bFound = false;
+ while (nIndex < rArray.GetCount(nLevel))
+ {
+ const ScOutlineEntry* pEntry = rArray.GetEntry(nLevel, nIndex);
+ if (pEntry && pEntry->IsVisible())
+ {
+ bFound = true;
+ break;
+ }
+ if (pEntry && pEntry->GetStart() > nEnd)
+ {
+ break;
+ }
+ ++nIndex;
+ }
+ }
+
+ rGroupIndexes[nLevel] = bFound ? nIndex : -1;
+ }
+ }
+}
+
+void lcl_createGroupsData(
+ SCCOLROW nHeaderIndex, SCCOLROW nEnd, tools::Long nSizePx, tools::Long nTotalPx,
+ const ScOutlineArray& rArray, std::vector<size_t>& rGroupIndexes,
+ std::vector<tools::Long>& rGroupStartPositions, OStringBuffer& rGroupsBuffer)
+{
+ const size_t nGroupDepth = rArray.GetDepth();
+ // create string data for group controls
+ for (size_t nLevel = nGroupDepth - 1; nLevel != size_t(-1); --nLevel)
+ {
+ size_t nIndex = rGroupIndexes[nLevel];
+ if (nIndex == size_t(-1))
+ continue;
+ const ScOutlineEntry* pEntry = rArray.GetEntry(nLevel, nIndex);
+ if (pEntry)
+ {
+ if (nHeaderIndex < pEntry->GetStart())
+ {
+ continue;
+ }
+ else if (nHeaderIndex == pEntry->GetStart())
+ {
+ rGroupStartPositions[nLevel] = nTotalPx - nSizePx;
+ }
+ else if (nHeaderIndex > pEntry->GetStart() && (nHeaderIndex < nEnd && nHeaderIndex < pEntry->GetEnd()))
+ {
+ // for handling group started before the current view range
+ if (rGroupStartPositions[nLevel] < 0)
+ rGroupStartPositions[nLevel] *= -1;
+ break;
+ }
+ if (nHeaderIndex == pEntry->GetEnd() || (nHeaderIndex == nEnd && rGroupStartPositions[nLevel] != -1))
+ {
+ // nHeaderIndex is the end col/row of a group or is the last col/row and a group started and not yet ended
+
+ // append a new group control data
+ auto len = rGroupsBuffer.getLength();
+ if (len && rGroupsBuffer[len-1] == '}')
+ {
+ rGroupsBuffer.append(", ");
+ }
+
+ bool bGroupHidden = pEntry->IsHidden();
+
+ rGroupsBuffer.append(
+ "{ \"level\": " + OString::number(nLevel + 1) + ", "
+ "\"index\": " + OString::number(nIndex) + ", "
+ "\"startPos\": " + OString::number(rGroupStartPositions[nLevel]) + ", "
+ "\"endPos\": " + OString::number(nTotalPx) + ", "
+ "\"hidden\": " + OString::number(bGroupHidden ? 1 : 0) + " }");
+
+ // look for the next visible group control at level nLevel
+ bool bFound = false;
+ ++nIndex;
+ while (nIndex < rArray.GetCount(nLevel))
+ {
+ pEntry = rArray.GetEntry(nLevel, nIndex);
+ if (pEntry && pEntry->IsVisible())
+ {
+ bFound = true;
+ break;
+ }
+ if (pEntry && pEntry->GetStart() > nEnd)
+ {
+ break;
+ }
+ ++nIndex;
+ }
+ rGroupIndexes[nLevel] = bFound ? nIndex : -1;
+ rGroupStartPositions[nLevel] = -1;
+ }
+ }
+ }
+}
+
+class ScRangeProvider
+{
+public:
+ ScRangeProvider(const tools::Rectangle& rArea, bool bInPixels,
+ ScViewData& rViewData):
+ mrViewData(rViewData)
+ {
+ tools::Rectangle aAreaPx = bInPixels ? rArea :
+ tools::Rectangle(rArea.Left() * mrViewData.GetPPTX(),
+ rArea.Top() * mrViewData.GetPPTY(),
+ rArea.Right() * mrViewData.GetPPTX(),
+ rArea.Bottom() * mrViewData.GetPPTY());
+ calculateBounds(aAreaPx);
+ }
+
+ const ScRange& getCellRange() const
+ {
+ return maRange;
+ }
+
+ void getColPositions(tools::Long& rStartColPos, tools::Long& rEndColPos) const
+ {
+ rStartColPos = maBoundPositions.Left();
+ rEndColPos = maBoundPositions.Right();
+ }
+
+ void getRowPositions(tools::Long& rStartRowPos, tools::Long& rEndRowPos) const
+ {
+ rStartRowPos = maBoundPositions.Top();
+ rEndRowPos = maBoundPositions.Bottom();
+ }
+
+private:
+ void calculateBounds(const tools::Rectangle& rAreaPx)
+ {
+ tools::Long nLeftPx = 0, nRightPx = 0;
+ SCCOLROW nStartCol = -1, nEndCol = -1;
+ calculateDimensionBounds(rAreaPx.Left(), rAreaPx.Right(), true,
+ nStartCol, nEndCol, nLeftPx, nRightPx,
+ mnEnlargeX, mrViewData);
+ tools::Long nTopPx = 0, nBottomPx = 0;
+ SCCOLROW nStartRow = -1, nEndRow = -1;
+ calculateDimensionBounds(rAreaPx.Top(), rAreaPx.Bottom(), false,
+ nStartRow, nEndRow, nTopPx, nBottomPx,
+ mnEnlargeY, mrViewData);
+
+ maRange.aStart.Set(nStartCol, nStartRow, mrViewData.GetTabNo());
+ maRange.aEnd.Set(nEndCol, nEndRow, mrViewData.GetTabNo());
+
+ maBoundPositions.SetLeft(nLeftPx);
+ maBoundPositions.SetRight(nRightPx);
+ maBoundPositions.SetTop(nTopPx);
+ maBoundPositions.SetBottom(nBottomPx);
+ }
+
+ // All positions are in pixels.
+ static void calculateDimensionBounds(const tools::Long nStartPos, const tools::Long nEndPos,
+ bool bColumns, SCCOLROW& rStartIndex,
+ SCCOLROW& rEndIndex, tools::Long& rBoundStart,
+ tools::Long& rBoundEnd, SCCOLROW nEnlarge,
+ ScViewData& rViewData)
+ {
+ ScPositionHelper& rPosHelper = bColumns ? rViewData.GetLOKWidthHelper() :
+ rViewData.GetLOKHeightHelper();
+ const auto& rStartNearest = rPosHelper.getNearestByPosition(nStartPos);
+ const auto& rEndNearest = rPosHelper.getNearestByPosition(nEndPos);
+
+ ScBoundsProvider aBoundsProvider(rViewData, rViewData.GetTabNo(), bColumns);
+ aBoundsProvider.Compute(rStartNearest, rEndNearest, nStartPos, nEndPos);
+ aBoundsProvider.EnlargeBy(nEnlarge);
+ if (bColumns)
+ {
+ SCCOL nStartCol = -1, nEndCol = -1;
+ aBoundsProvider.GetStartIndexAndPosition(nStartCol, rBoundStart);
+ aBoundsProvider.GetEndIndexAndPosition(nEndCol, rBoundEnd);
+ rStartIndex = nStartCol;
+ rEndIndex = nEndCol;
+ }
+ else
+ {
+ SCROW nStartRow = -1, nEndRow = -1;
+ aBoundsProvider.GetStartIndexAndPosition(nStartRow, rBoundStart);
+ aBoundsProvider.GetEndIndexAndPosition(nEndRow, rBoundEnd);
+ rStartIndex = nStartRow;
+ rEndIndex = nEndRow;
+ }
+ }
+
+private:
+
+ ScRange maRange;
+ tools::Rectangle maBoundPositions;
+ ScViewData& mrViewData;
+ static const SCCOLROW mnEnlargeX = 2;
+ static const SCCOLROW mnEnlargeY = 2;
+};
+
+void lcl_ExtendTiledDimension(bool bColumn, const SCCOLROW nEnd, const SCCOLROW nExtra,
+ ScTabView& rTabView, ScViewData& rViewData)
+{
+ ScDocument& rDoc = rViewData.GetDocument();
+ // If we are approaching current max tiled row/col, signal a size changed event
+ // and invalidate the involved area
+ SCCOLROW nMaxTiledIndex = bColumn ? rViewData.GetMaxTiledCol() : rViewData.GetMaxTiledRow();
+ SCCOLROW nHardLimit = !bColumn ? MAXTILEDROW : rDoc.MaxCol();
+
+ if (nMaxTiledIndex >= nHardLimit)
+ return;
+
+ if (nEnd <= nMaxTiledIndex - nExtra) // No need to extend.
+ return;
+
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
+ Size aOldSize(0, 0);
+ if (pModelObj)
+ aOldSize = pModelObj->getDocumentSize();
+
+ SCCOLROW nNewMaxTiledIndex = std::min(std::max(nEnd, nMaxTiledIndex) + nExtra, nHardLimit);
+
+ if (bColumn)
+ rViewData.SetMaxTiledCol(nNewMaxTiledIndex);
+ else
+ rViewData.SetMaxTiledRow(nNewMaxTiledIndex);
+
+ Size aNewSize(0, 0);
+ if (pModelObj)
+ aNewSize = pModelObj->getDocumentSize();
+
+ if (aOldSize == aNewSize)
+ return;
+
+ if (!pDocSh)
+ return;
+
+ // New area extended to the right/bottom of the sheet after last col/row
+ // excluding overlapping area with aNewArea
+ tools::Rectangle aNewArea = bColumn ?
+ tools::Rectangle(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight()):
+ tools::Rectangle(0, aOldSize.getHeight(), aNewSize.getWidth(), aNewSize.getHeight());
+
+ // Only invalidate if spreadsheet has extended to the right or bottom
+ if ((bColumn && aNewArea.getOpenWidth()) || (!bColumn && aNewArea.getOpenHeight()))
+ {
+ rTabView.UpdateSelectionOverlay();
+ SfxLokHelper::notifyInvalidation(rViewData.GetViewShell(), &aNewArea);
+ }
+
+ // Provide size in the payload, so clients don't have to query for that.
+ std::stringstream ss;
+ ss << aNewSize.Width() << ", " << aNewSize.Height();
+ OString sSize( ss.str() );
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(
+ rViewData.GetViewShell()->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(rViewData.GetViewShell(), sSize, pModel, false);
+}
+
+} // anonymous namespace
+
+void ScTabView::getRowColumnHeaders(const tools::Rectangle& rRectangle, tools::JsonWriter& rJsonWriter)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (rRectangle.IsEmpty())
+ return;
+
+ bool bRangeHeaderSupport = comphelper::LibreOfficeKit::isRangeHeaders();
+
+ rJsonWriter.put("commandName", ".uno:ViewRowColumnHeaders");
+
+ SCTAB nTab = aViewData.GetTabNo();
+ SCROW nStartRow = -1;
+ SCROW nEndRow = -1;
+ tools::Long nStartHeightPx = 0;
+ SCCOL nStartCol = -1;
+ SCCOL nEndCol = -1;
+ tools::Long nStartWidthPx = 0;
+
+ tools::Rectangle aOldVisArea(
+ mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow);
+
+ ScRangeProvider aRangeProvider(rRectangle, /* bInPixels */ false, aViewData);
+ const ScRange& rCellRange = aRangeProvider.getCellRange();
+
+ /// *** start collecting ROWS ***
+
+ /// 1) compute start and end rows
+
+ if (rRectangle.Top() < rRectangle.Bottom())
+ {
+ SAL_INFO("sc.lok.header", "Row Header: compute start/end rows.");
+ tools::Long nEndHeightPx = 0;
+ nStartRow = rCellRange.aStart.Row();
+ nEndRow = rCellRange.aEnd.Row();
+ aRangeProvider.getRowPositions(nStartHeightPx, nEndHeightPx);
+
+ aViewData.GetLOKHeightHelper().removeByIndex(mnLOKStartHeaderRow);
+ aViewData.GetLOKHeightHelper().removeByIndex(mnLOKEndHeaderRow);
+ aViewData.GetLOKHeightHelper().insert(nStartRow, nStartHeightPx);
+ aViewData.GetLOKHeightHelper().insert(nEndRow, nEndHeightPx);
+
+ mnLOKStartHeaderRow = nStartRow;
+ mnLOKEndHeaderRow = nEndRow;
+ }
+
+ sal_Int32 nVisibleRows = nEndRow - nStartRow;
+ if (nVisibleRows < 25)
+ nVisibleRows = 25;
+
+ SAL_INFO("sc.lok.header", "Row Header: visible rows: " << nVisibleRows);
+
+
+ // Get row groups
+ // per each level store the index of the first group intersecting
+ // [nStartRow, nEndRow] range
+
+ const ScOutlineTable* pTable = rDoc.GetOutlineTable(nTab);
+ const ScOutlineArray* pRowArray = pTable ? &(pTable->GetRowArray()) : nullptr;
+ size_t nRowGroupDepth = 0;
+ std::vector<size_t> aRowGroupIndexes;
+ if (bRangeHeaderSupport && pTable)
+ {
+ nRowGroupDepth = pRowArray->GetDepth();
+ lcl_getGroupIndexes(*pRowArray, nStartRow, nEndRow, aRowGroupIndexes);
+ }
+
+ /// 2) if we are approaching current max tiled row, signal a size changed event
+ /// and invalidate the involved area
+ lcl_ExtendTiledDimension(/* bColumn */ false, nEndRow, nVisibleRows, *this, aViewData);
+
+ /// 3) create string data for rows
+
+ tools::Long nTotalPixels = nStartHeightPx;
+ tools::Long nPrevSizePx = -1;
+ OStringBuffer aRowGroupsBuffer = "\"rowGroups\": [\n";
+ {
+ auto rowsNode = rJsonWriter.startArray("rows");
+
+ SAL_INFO("sc.lok.header", "Row Header: [create string data for rows]: start row: "
+ << nStartRow << " start height: " << nTotalPixels);
+
+ if (nStartRow != nEndRow)
+ {
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", nStartRow + 1);
+ rJsonWriter.put("size", nTotalPixels);
+ rJsonWriter.put("groupLevels", static_cast<sal_Int64>(nRowGroupDepth));
+ }
+
+ std::vector<tools::Long> aRowGroupStartPositions(nRowGroupDepth, -nTotalPixels);
+ for (SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow)
+ {
+ // nSize will be 0 for hidden rows.
+ const tools::Long nSizePx = lcl_GetRowHeightPx(aViewData, nRow, nTab);
+ nTotalPixels += nSizePx;
+
+ if (bRangeHeaderSupport && nRowGroupDepth > 0)
+ {
+ lcl_createGroupsData(nRow, nEndRow, nSizePx, nTotalPixels,
+ *pRowArray, aRowGroupIndexes, aRowGroupStartPositions,
+ aRowGroupsBuffer);
+ }
+
+ if (bRangeHeaderSupport && nRow < nEndRow && nSizePx == nPrevSizePx)
+ continue;
+ nPrevSizePx = nSizePx;
+
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", pRowBar[SC_SPLIT_BOTTOM]->GetEntryText(nRow));
+ rJsonWriter.put("size", nTotalPixels);
+ }
+
+ aRowGroupsBuffer.append("]");
+ }
+ if (nRowGroupDepth > 0)
+ {
+ aRowGroupsBuffer.append(",\n");
+ rJsonWriter.putRaw(aRowGroupsBuffer);
+ }
+ /// end collecting ROWS
+
+
+ /// *** start collecting COLS ***
+
+ /// 1) compute start and end columns
+
+ if (rRectangle.Left() < rRectangle.Right())
+ {
+ SAL_INFO("sc.lok.header", "Column Header: compute start/end columns.");
+ tools::Long nEndWidthPx = 0;
+ nStartCol = rCellRange.aStart.Col();
+ nEndCol = rCellRange.aEnd.Col();
+ aRangeProvider.getColPositions(nStartWidthPx, nEndWidthPx);
+
+ aViewData.GetLOKWidthHelper().removeByIndex(mnLOKStartHeaderCol);
+ aViewData.GetLOKWidthHelper().removeByIndex(mnLOKEndHeaderCol);
+ aViewData.GetLOKWidthHelper().insert(nStartCol, nStartWidthPx);
+ aViewData.GetLOKWidthHelper().insert(nEndCol, nEndWidthPx);
+
+ mnLOKStartHeaderCol = nStartCol;
+ mnLOKEndHeaderCol = nEndCol;
+ }
+
+ sal_Int32 nVisibleCols = nEndCol - nStartCol;
+ if (nVisibleCols < 10)
+ nVisibleCols = 10;
+
+
+ // Get column groups
+ // per each level store the index of the first group intersecting
+ // [nStartCol, nEndCol] range
+
+ const ScOutlineArray* pColArray = pTable ? &(pTable->GetColArray()) : nullptr;
+ size_t nColGroupDepth = 0;
+ std::vector<size_t> aColGroupIndexes;
+ if (bRangeHeaderSupport && pTable)
+ {
+ nColGroupDepth = pColArray->GetDepth();
+ lcl_getGroupIndexes(*pColArray, nStartCol, nEndCol, aColGroupIndexes);
+ }
+
+ /// 2) if we are approaching current max tiled column, signal a size changed event
+ /// and invalidate the involved area
+ lcl_ExtendTiledDimension(/* bColumn */ true, nEndCol, nVisibleCols, *this, aViewData);
+
+ /// 3) create string data for columns
+ OStringBuffer aColGroupsBuffer = "\"columnGroups\": [\n";
+ {
+ auto columnsNode = rJsonWriter.startArray("columns");
+
+ nTotalPixels = nStartWidthPx;
+ SAL_INFO("sc.lok.header", "Col Header: [create string data for cols]: start col: "
+ << nStartRow << " start width: " << nTotalPixels);
+
+ if (nStartCol != nEndCol)
+ {
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", static_cast<sal_Int64>(nStartCol + 1));
+ rJsonWriter.put("size", nTotalPixels);
+ rJsonWriter.put("groupLevels", static_cast<sal_Int64>(nColGroupDepth));
+ }
+
+ std::vector<tools::Long> aColGroupStartPositions(nColGroupDepth, -nTotalPixels);
+ nPrevSizePx = -1;
+ for (SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
+ {
+ // nSize will be 0 for hidden columns.
+ const tools::Long nSizePx = lcl_GetColWidthPx(aViewData, nCol, nTab);
+ nTotalPixels += nSizePx;
+
+ if (bRangeHeaderSupport && nColGroupDepth > 0)
+ lcl_createGroupsData(nCol, nEndCol, nSizePx, nTotalPixels,
+ *pColArray, aColGroupIndexes,
+ aColGroupStartPositions, aColGroupsBuffer);
+
+ if (bRangeHeaderSupport && nCol < nEndCol && nSizePx == nPrevSizePx)
+ continue;
+ nPrevSizePx = nSizePx;
+
+ OUString aText = bRangeHeaderSupport ?
+ OUString::number(nCol + 1) : pColBar[SC_SPLIT_LEFT]->GetEntryText(nCol);
+
+ auto node = rJsonWriter.startStruct();
+ rJsonWriter.put("text", aText);
+ rJsonWriter.put("size", nTotalPixels);
+ }
+
+ aColGroupsBuffer.append("]");
+ }
+ if (nColGroupDepth > 0)
+ {
+ aColGroupsBuffer.append(",\n");
+ rJsonWriter.putRaw(aColGroupsBuffer);
+ }
+ /// end collecting COLs
+
+ vcl::Region aNewVisArea(
+ tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow));
+ aNewVisArea.Exclude(aOldVisArea);
+ tools::Rectangle aChangedArea = aNewVisArea.GetBoundRect();
+ if (!aChangedArea.IsEmpty())
+ {
+ UpdateVisibleRange();
+ UpdateFormulas(aChangedArea.Left(), aChangedArea.Top(), aChangedArea.Right(), aChangedArea.Bottom());
+ }
+}
+
+OString ScTabView::getSheetGeometryData(bool bColumns, bool bRows, bool bSizes, bool bHidden,
+ bool bFiltered, bool bGroups)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ boost::property_tree::ptree aTree;
+ aTree.put("commandName", ".uno:SheetGeometryData");
+ aTree.put("maxtiledcolumn", rDoc.MaxCol());
+ aTree.put("maxtiledrow", MAXTILEDROW);
+
+ auto getJSONString = [](const boost::property_tree::ptree& rTree) {
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, rTree);
+ return aStream.str();
+ };
+
+ if ((!bSizes && !bHidden && !bFiltered && !bGroups) ||
+ (!bColumns && !bRows))
+ {
+ return OString(getJSONString(aTree));
+ }
+
+ struct GeomEntry
+ {
+ SheetGeomType eType;
+ const char* pKey;
+ bool bEnabled;
+ };
+
+ const GeomEntry aGeomEntries[] = {
+ { SheetGeomType::SIZES, "sizes", bSizes },
+ { SheetGeomType::HIDDEN, "hidden", bHidden },
+ { SheetGeomType::FILTERED, "filtered", bFiltered },
+ { SheetGeomType::GROUPS, "groups", bGroups }
+ };
+
+ struct DimensionEntry
+ {
+ const char* pKey;
+ bool bDimIsCol;
+ bool bEnabled;
+ };
+
+ const DimensionEntry aDimEntries[] = {
+ { "columns", true, bColumns },
+ { "rows", false, bRows }
+ };
+
+ SCTAB nTab = aViewData.GetTabNo();
+
+ for (const auto& rDimEntry : aDimEntries)
+ {
+ if (!rDimEntry.bEnabled)
+ continue;
+
+ bool bDimIsCol = rDimEntry.bDimIsCol;
+
+ boost::property_tree::ptree aDimTree;
+ for (const auto& rGeomEntry : aGeomEntries)
+ {
+ if (!rGeomEntry.bEnabled)
+ continue;
+
+ OString aGeomDataEncoding = rDoc.dumpSheetGeomData(nTab, bDimIsCol, rGeomEntry.eType);
+ // TODO: Investigate if we can avoid the copy of the 'value' string in put().
+ aDimTree.put(rGeomEntry.pKey, aGeomDataEncoding.getStr());
+ }
+
+ aTree.add_child(rDimEntry.pKey, aDimTree);
+ }
+
+ return OString(getJSONString(aTree));
+}
+
+void ScTabView::extendTiledAreaIfNeeded()
+{
+ SAL_INFO("sc.lok.header",
+ "extendTiledAreaIfNeeded: START: ClientView: ColRange["
+ << mnLOKStartHeaderCol << "," << mnLOKEndHeaderCol
+ << "] RowRange[" << mnLOKStartHeaderRow << "," << mnLOKEndHeaderRow
+ << "] MaxTiledCol = " << aViewData.GetMaxTiledCol()
+ << " MaxTiledRow = " << aViewData.GetMaxTiledRow());
+
+ const tools::Rectangle rVisArea = aViewData.getLOKVisibleArea();
+ if (rVisArea.Top() >= rVisArea.Bottom() ||
+ rVisArea.Left() >= rVisArea.Right())
+ return;
+
+ // Needed for conditional updating of visible-range/formula.
+ tools::Rectangle aOldVisCellRange(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow);
+
+ ScRangeProvider aRangeProvider(rVisArea, /* bInPixels */ false, aViewData);
+ // Index bounds.
+ const ScRange& rCellRange = aRangeProvider.getCellRange();
+ const SCCOL nStartCol = rCellRange.aStart.Col();
+ const SCCOL nEndCol = rCellRange.aEnd.Col();
+ const SCROW nStartRow = rCellRange.aStart.Row();
+ const SCROW nEndRow = rCellRange.aEnd.Row();
+
+ // Column/Row positions.
+ tools::Long nStartColPos, nEndColPos, nStartRowPos, nEndRowPos;
+ aRangeProvider.getColPositions(nStartColPos, nEndColPos);
+ aRangeProvider.getRowPositions(nStartRowPos, nEndRowPos);
+
+ ScPositionHelper& rWidthHelper = aViewData.GetLOKWidthHelper();
+ ScPositionHelper& rHeightHelper = aViewData.GetLOKHeightHelper();
+
+ // Update mnLOKStartHeaderCol and mnLOKEndHeaderCol members.
+ // These are consulted in some ScGridWindow methods.
+ if (mnLOKStartHeaderCol != nStartCol)
+ {
+ rWidthHelper.removeByIndex(mnLOKStartHeaderCol);
+ rWidthHelper.insert(nStartCol, nStartColPos);
+ mnLOKStartHeaderCol = nStartCol;
+ }
+
+ if (mnLOKEndHeaderCol != nEndCol)
+ {
+ rWidthHelper.removeByIndex(mnLOKEndHeaderCol);
+ rWidthHelper.insert(nEndCol, nEndColPos);
+ mnLOKEndHeaderCol = nEndCol;
+ }
+
+ // Update mnLOKStartHeaderRow and mnLOKEndHeaderRow members.
+ // These are consulted in some ScGridWindow methods.
+ if (mnLOKStartHeaderRow != nStartRow)
+ {
+ rHeightHelper.removeByIndex(mnLOKStartHeaderRow);
+ rHeightHelper.insert(nStartRow, nStartRowPos);
+ mnLOKStartHeaderRow = nStartRow;
+ }
+
+ if (mnLOKEndHeaderRow != nEndRow)
+ {
+ rHeightHelper.removeByIndex(mnLOKEndHeaderRow);
+ rHeightHelper.insert(nEndRow, nEndRowPos);
+ mnLOKEndHeaderRow = nEndRow;
+ }
+
+ constexpr SCCOL nMinExtraCols = 10;
+ SCCOL nExtraCols = std::max<SCCOL>(nMinExtraCols, nEndCol - nStartCol);
+ // If we are approaching current max tiled column, signal a size changed event
+ // and invalidate the involved area.
+ lcl_ExtendTiledDimension(/* bColumn */ true, nEndCol, nExtraCols, *this, aViewData);
+
+ constexpr SCROW nMinExtraRows = 25;
+ SCROW nExtraRows = std::max(nMinExtraRows, nEndRow - nStartRow);
+ // If we are approaching current max tiled row, signal a size changed event
+ // and invalidate the involved area.
+ lcl_ExtendTiledDimension(/* bColumn */ false, nEndRow, nExtraRows, *this, aViewData);
+
+ vcl::Region aNewVisCellRange(
+ tools::Rectangle(mnLOKStartHeaderCol + 1, mnLOKStartHeaderRow + 1,
+ mnLOKEndHeaderCol, mnLOKEndHeaderRow));
+ aNewVisCellRange.Exclude(aOldVisCellRange);
+ tools::Rectangle aChangedCellRange = aNewVisCellRange.GetBoundRect();
+ if (!aChangedCellRange.IsEmpty())
+ {
+ UpdateVisibleRange();
+ UpdateFormulas(aChangedCellRange.Left(), aChangedCellRange.Top(),
+ aChangedCellRange.Right(), aChangedCellRange.Bottom());
+ }
+
+ SAL_INFO("sc.lok.header",
+ "extendTiledAreaIfNeeded: END: ClientView: ColRange["
+ << mnLOKStartHeaderCol << "," << mnLOKEndHeaderCol
+ << "] RowRange[" << mnLOKStartHeaderRow << "," << mnLOKEndHeaderRow
+ << "] MaxTiledCol = " << aViewData.GetMaxTiledCol()
+ << " MaxTiledRow = " << aViewData.GetMaxTiledRow());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview2.cxx b/sc/source/ui/view/tabview2.cxx
new file mode 100644
index 0000000000..d5be3d5b59
--- /dev/null
+++ b/sc/source/ui/view/tabview2.cxx
@@ -0,0 +1,1709 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/bindings.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <pagedata.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <printfun.hxx>
+#include <stlpool.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+#include <sc.hrc>
+#include <viewutil.hxx>
+#include <colrowba.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <table.hxx>
+#include <tabprotection.hxx>
+#include <markdata.hxx>
+#include <inputopt.hxx>
+#include <comphelper/lok.hxx>
+
+namespace {
+
+bool isCellQualified(const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, bool bSelectLocked, bool bSelectUnlocked)
+{
+ bool bCellProtected = pDoc->HasAttrib(
+ nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::Protected);
+
+ if (bCellProtected && !bSelectLocked)
+ return false;
+
+ if (!bCellProtected && !bSelectUnlocked)
+ return false;
+
+ return true;
+}
+
+bool areCellsQualified(const ScDocument* pDoc, SCCOL nColStart, SCROW nRowStart, SCCOL nColEnd,
+ SCROW nRowEnd, SCTAB nTab, bool bSelectLocked, bool bSelectUnlocked)
+{
+ PutInOrder(nColStart, nColEnd);
+ PutInOrder(nRowStart, nRowEnd);
+ for (SCCOL col = nColStart; col <= nColEnd; ++col)
+ for (SCROW row = nRowStart; row <= nRowEnd; ++row)
+ if (!isCellQualified(pDoc, col, row, nTab, bSelectLocked, bSelectUnlocked))
+ return false;
+
+ return true;
+}
+
+void moveCursorByProtRule(
+ SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY, SCTAB nTab, const ScDocument* pDoc)
+{
+ bool bSelectLocked = true;
+ bool bSelectUnlocked = true;
+ const ScTableProtection* pTabProtection = pDoc->GetTabProtection(nTab);
+ if (pTabProtection && pTabProtection->isProtected())
+ {
+ bSelectLocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSelectUnlocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ if (nMovX > 0)
+ {
+ for (SCCOL i = 0; i < nMovX && rCol < pDoc->MaxCol(); ++i)
+ {
+ SCCOL nNewUnhiddenCol = rCol + 1;
+ SCCOL nEndCol = 0;
+ while(pDoc->ColHidden(nNewUnhiddenCol, nTab, nullptr, &nEndCol))
+ {
+ if(nNewUnhiddenCol >= pDoc->MaxCol())
+ return;
+
+ i += nEndCol - nNewUnhiddenCol + 1;
+ nNewUnhiddenCol = nEndCol +1;
+ }
+
+ if (!isCellQualified(pDoc, nNewUnhiddenCol, rRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = nNewUnhiddenCol;
+ }
+ }
+ else if (nMovX < 0)
+ {
+ for (SCCOL i = 0; i > nMovX && rCol > 0; --i)
+ {
+ SCCOL nNewUnhiddenCol = rCol - 1;
+ SCCOL nStartCol = 0;
+ while(pDoc->ColHidden(nNewUnhiddenCol, nTab, &nStartCol))
+ {
+ if(nNewUnhiddenCol <= 0)
+ return;
+
+ i -= nNewUnhiddenCol - nStartCol + 1;
+ nNewUnhiddenCol = nStartCol - 1;
+ }
+
+ if (!isCellQualified(pDoc, nNewUnhiddenCol, rRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = nNewUnhiddenCol;
+ }
+ }
+
+ if (nMovY > 0)
+ {
+ for (SCROW i = 0; i < nMovY && rRow < pDoc->MaxRow(); ++i)
+ {
+ SCROW nNewUnhiddenRow = rRow + 1;
+ SCROW nEndRow = 0;
+ while(pDoc->RowHidden(nNewUnhiddenRow, nTab, nullptr, &nEndRow))
+ {
+ if(nNewUnhiddenRow >= pDoc->MaxRow())
+ return;
+
+ i += nEndRow - nNewUnhiddenRow + 1;
+ nNewUnhiddenRow = nEndRow + 1;
+ }
+
+ if (!isCellQualified(pDoc, rCol, nNewUnhiddenRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = nNewUnhiddenRow;
+ }
+ }
+ else if (nMovY < 0)
+ {
+ for (SCROW i = 0; i > nMovY && rRow > 0; --i)
+ {
+ SCROW nNewUnhiddenRow = rRow - 1;
+ SCROW nStartRow = 0;
+ while(pDoc->RowHidden(nNewUnhiddenRow, nTab, &nStartRow))
+ {
+ if(nNewUnhiddenRow <= 0)
+ return;
+
+ i -= nNewUnhiddenRow - nStartRow + 1;
+ nNewUnhiddenRow = nStartRow - 1;
+ }
+
+ if (!isCellQualified(pDoc, rCol, nNewUnhiddenRow, nTab, bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = nNewUnhiddenRow;
+ }
+ }
+}
+
+bool checkBoundary(const ScDocument* pDoc, SCCOL& rCol, SCROW& rRow)
+{
+ bool bGood = true;
+ if (rCol < 0)
+ {
+ rCol = 0;
+ bGood = false;
+ }
+ else if (rCol > pDoc->MaxCol())
+ {
+ rCol = pDoc->MaxCol();
+ bGood = false;
+ }
+
+ if (rRow < 0)
+ {
+ rRow = 0;
+ bGood = false;
+ }
+ else if (rRow > pDoc->MaxRow())
+ {
+ rRow = pDoc->MaxRow();
+ bGood = false;
+ }
+ return bGood;
+}
+
+void moveRefByCell(SCCOL& rNewX, SCROW& rNewY,
+ SCCOL nMovX, SCROW nMovY, SCTAB nRefTab,
+ const ScDocument& rDoc)
+{
+ SCCOL nOldX = rNewX;
+ SCROW nOldY = rNewY;
+ bool bSelectLocked = true;
+ bool bSelectUnlocked = true;
+ const ScTableProtection* pTabProtection = rDoc.GetTabProtection(nRefTab);
+ if (pTabProtection && pTabProtection->isProtected())
+ {
+ bSelectLocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSelectUnlocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ moveCursorByProtRule(rNewX, rNewY, nMovX, nMovY, nRefTab, &rDoc);
+ checkBoundary(&rDoc, rNewX, rNewY);
+
+ if (nMovX)
+ {
+ SCCOL nTempX = rNewX;
+ while (rDoc.IsHorOverlapped(nTempX, rNewY, nRefTab))
+ {
+ nTempX = (nMovX > 0) ? nTempX + 1 : nTempX - 1;
+ if (!checkBoundary(&rDoc, nTempX, rNewY))
+ break;
+ }
+ if (isCellQualified(&rDoc, nTempX, rNewY, nRefTab, bSelectLocked, bSelectUnlocked))
+ rNewX = nTempX;
+
+ if (nMovX < 0 && rNewX > 0)
+ {
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(rNewX, rNewY, nRefTab, ATTR_MERGE);
+ if (pMergeAttr && pMergeAttr->IsMerged() &&
+ nOldX >= rNewX &&
+ nOldX <= rNewX + pMergeAttr->GetRowMerge() - 1)
+ rNewX = rNewX - 1;
+ }
+ }
+
+ if (nMovY)
+ {
+ SCROW nTempY = rNewY;
+ while (rDoc.IsVerOverlapped(rNewX, nTempY, nRefTab))
+ {
+ nTempY = (nMovY > 0) ? nTempY + 1 : nTempY - 1;
+ if (!checkBoundary(&rDoc, rNewX, nTempY))
+ break;
+ }
+ if (isCellQualified(&rDoc, rNewX, nTempY, nRefTab, bSelectLocked, bSelectUnlocked))
+ rNewY = nTempY;
+
+ if (nMovY < 0 && rNewY > 0)
+ {
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(rNewX, rNewY, nRefTab, ATTR_MERGE);
+ if (pMergeAttr && pMergeAttr->IsMerged() &&
+ nOldY >= rNewY &&
+ nOldY <= rNewY + pMergeAttr->GetRowMerge() - 1)
+ rNewY = rNewY - 1;
+ }
+ }
+
+ rDoc.SkipOverlapped(rNewX, rNewY, nRefTab);
+}
+
+void moveCursorByMergedCell(SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY, SCCOL nStartX,
+ SCROW nStartY, SCTAB nTab, const ScDocument* pDoc)
+{
+ const ScTableProtection* pTabProtection = pDoc->GetTabProtection(nTab);
+ bool bSelectLocked = true;
+ bool bSelectUnlocked = true;
+ if (pTabProtection && pTabProtection->isProtected())
+ {
+ bSelectLocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSelectUnlocked = pTabProtection->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ if (nMovX > 0)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+
+ for (SCROW i = rowStart; i <= rowEnd && rCol < nStartX;)
+ {
+ SCCOL tmpCol = rCol;
+ while (tmpCol < pDoc->MaxCol() && pDoc->IsHorOverlapped(tmpCol, i, nTab))
+ ++tmpCol;
+ if (tmpCol != rCol)
+ {
+ i = rowStart;
+ if (tmpCol > nStartX)
+ --tmpCol;
+ if (!areCellsQualified(pDoc, rCol + 1, rowStart, tmpCol, rowEnd, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = tmpCol;
+ }
+ else
+ ++i;
+ }
+ }
+ else if (nMovX < 0)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+
+ for (SCROW i = rowStart; i <= rowEnd && rCol > nStartX;)
+ {
+ SCCOL tmpCol = rCol;
+ while (tmpCol >= 0 && pDoc->IsHorOverlapped(tmpCol + 1, i, nTab))
+ --tmpCol;
+ if (tmpCol != rCol)
+ {
+ i = rowStart;
+ if (tmpCol < nStartX)
+ ++tmpCol;
+ if (!areCellsQualified(pDoc, rCol - 1, rowStart, tmpCol, rowEnd, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rCol = tmpCol;
+ }
+ else
+ ++i;
+ }
+ }
+
+ if (nMovY > 0)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+
+ for (SCCOL i = colStart; i <= colEnd && rRow < nStartY;)
+ {
+ SCROW tmpRow = rRow;
+ while (tmpRow < pDoc->MaxRow() && pDoc->IsVerOverlapped(i, tmpRow, nTab))
+ ++tmpRow;
+ if (tmpRow != rRow)
+ {
+ i = colStart;
+ if (tmpRow > nStartY)
+ --tmpRow;
+ if (!areCellsQualified(pDoc, colStart, rRow + 1, colEnd, tmpRow, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = tmpRow;
+ }
+ else
+ ++i;
+ }
+ }
+ else if (nMovY < 0)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+
+ for (SCCOL i = colStart; i <= colEnd && rRow > nStartY;)
+ {
+ SCROW tmpRow = rRow;
+ while (tmpRow >= 0 && pDoc->IsVerOverlapped(i, tmpRow + 1, nTab))
+ --tmpRow;
+ if (tmpRow != rRow)
+ {
+ i = colStart;
+ if (tmpRow < nStartY)
+ ++tmpRow;
+ if (!areCellsQualified(pDoc, colStart, rRow - 1, colEnd, tmpRow, nTab,
+ bSelectLocked, bSelectUnlocked))
+ break;
+ rRow = tmpRow;
+ }
+ else
+ ++i;
+ }
+ }
+}
+
+void moveCursorToProperSide(SCCOL& rCol, SCROW& rRow, SCCOL nMovX, SCROW nMovY, SCCOL nStartX,
+ SCROW nStartY, SCTAB nTab, const ScDocument* pDoc)
+{
+ SCCOL tmpCol = rCol;
+ SCROW tmpRow = rRow;
+
+ if (nMovX > 0 && nStartX < pDoc->MaxCol() && rCol < nStartX)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+ for (SCROW i = rowStart; i <= rowEnd && tmpCol < nStartX;)
+ {
+ if (pDoc->IsHorOverlapped(tmpCol + 1, i, nTab))
+ {
+ do
+ {
+ ++tmpCol;
+ } while (pDoc->IsHorOverlapped(tmpCol + 1, i, nTab));
+ i = rowStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpCol < nStartX)
+ tmpCol = rCol;
+ }
+ else if (nMovX < 0 && nStartX > 0 && rCol > nStartX)
+ {
+ SCROW rowStart = std::min(rRow, nStartY);
+ SCROW rowEnd = std::max(rRow, nStartY);
+ for (SCROW i = rowStart; i <= rowEnd && tmpCol > nStartX;)
+ {
+ if (pDoc->IsHorOverlapped(tmpCol, i, nTab))
+ {
+ do
+ {
+ --tmpCol;
+ } while (pDoc->IsHorOverlapped(tmpCol, i, nTab));
+ i = rowStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpCol > nStartX)
+ tmpCol = rCol;
+ }
+
+ if (nMovY > 0 && nStartY < pDoc->MaxRow() && rRow < nStartY)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+ for (SCCOL i = colStart; i <= colEnd && tmpRow < nStartY;)
+ {
+ if (pDoc->IsVerOverlapped(i, tmpRow + 1, nTab))
+ {
+ do
+ {
+ ++tmpRow;
+ } while (pDoc->IsVerOverlapped(i, tmpRow + 1, nTab));
+ i = colStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpRow < nStartY)
+ tmpRow = rRow;
+ }
+ else if (nMovY < 0 && nStartY > 0 && rRow > nStartY)
+ {
+ SCCOL colStart = std::min(rCol, nStartX);
+ SCCOL colEnd = std::max(rCol, nStartX);
+ for (SCCOL i = colStart; i <= colEnd && tmpRow > nStartY;)
+ {
+ if (pDoc->IsVerOverlapped(i, tmpRow, nTab))
+ {
+ do
+ {
+ --tmpRow;
+ } while (pDoc->IsVerOverlapped(i, tmpRow, nTab));
+ i = colStart;
+ }
+ else
+ ++i;
+ }
+ if (tmpRow > nStartY)
+ tmpRow = rRow;
+ }
+
+ if (tmpCol != rCol)
+ rCol = tmpCol;
+ if (tmpRow != rRow)
+ rRow = tmpRow;
+}
+}
+
+void ScTabView::PaintMarks(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ auto& rDoc = aViewData.GetDocument();
+ if (!rDoc.ValidCol(nStartCol)) nStartCol = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nStartRow)) nStartRow = rDoc.MaxRow();
+ if (!rDoc.ValidCol(nEndCol)) nEndCol = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nEndRow)) nEndRow = rDoc.MaxRow();
+
+ bool bLeft = (nStartCol==0 && nEndCol==rDoc.MaxCol());
+ bool bTop = (nStartRow==0 && nEndRow==rDoc.MaxRow());
+
+ if (bLeft)
+ PaintLeftArea( nStartRow, nEndRow );
+ if (bTop)
+ PaintTopArea( nStartCol, nEndCol );
+
+ aViewData.GetDocument().ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow,
+ aViewData.GetTabNo() );
+ PaintArea( nStartCol, nStartRow, nEndCol, nEndRow, ScUpdateMode::Marks );
+}
+
+bool ScTabView::IsMarking( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ return IsBlockMode()
+ && nBlockStartX == nCol
+ && nBlockStartY == nRow
+ && nBlockStartZ == nTab;
+}
+
+void ScTabView::InitOwnBlockMode( const ScRange& rMarkRange )
+{
+ if (IsBlockMode())
+ return;
+
+ // when there is no (old) selection anymore, delete anchor in SelectionEngine:
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ GetSelEngine()->CursorPosChanging( false, false );
+
+ meBlockMode = Own;
+ nBlockStartX = rMarkRange.aStart.Col();
+ nBlockStartY = rMarkRange.aStart.Row();
+ nBlockStartZ = rMarkRange.aStart.Tab();
+ nBlockEndX = rMarkRange.aEnd.Col();
+ nBlockEndY = rMarkRange.aEnd.Row();
+ nBlockEndZ = rMarkRange.aEnd.Tab();
+
+ SelectionChanged(); // status is checked with mark set
+}
+
+void ScTabView::InitBlockModeHighlight( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bCols, bool bRows )
+{
+ if (meHighlightBlockMode != None)
+ return;
+
+ auto& rDoc = aViewData.GetDocument();
+ if (!rDoc.ValidCol(nCurX)) nCurX = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nCurY)) nCurY = rDoc.MaxRow();
+
+ ScMarkData& rMark = aViewData.GetHighlightData();
+ meHighlightBlockMode = Normal;
+
+ SCROW nStartY = nCurY;
+ SCCOL nStartX = nCurX;
+ SCROW nEndY = nCurY;
+ SCCOL nEndX = nCurX;
+
+ if (bCols)
+ {
+ nStartY = 0;
+ nEndY = rDoc.MaxRow();
+ }
+
+ if (bRows)
+ {
+ nStartX = 0;
+ nEndX = rDoc.MaxCol();
+ }
+
+ rMark.SetMarkArea( ScRange( nStartX, nStartY, nCurZ, nEndX, nEndY, nCurZ ) );
+ UpdateHighlightOverlay();
+}
+
+void ScTabView::InitBlockMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bTestNeg, bool bCols, bool bRows, bool bForceNeg )
+{
+ if (IsBlockMode())
+ return;
+
+ auto& rDoc = aViewData.GetDocument();
+ if (!rDoc.ValidCol(nCurX)) nCurX = rDoc.MaxCol();
+ if (!rDoc.ValidRow(nCurY)) nCurY = rDoc.MaxRow();
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ // unmark part?
+ if (bForceNeg)
+ bBlockNeg = true;
+ else if (bTestNeg)
+ {
+ if ( bCols )
+ bBlockNeg = rMark.IsColumnMarked( nCurX );
+ else if ( bRows )
+ bBlockNeg = rMark.IsRowMarked( nCurY );
+ else
+ bBlockNeg = rMark.IsCellMarked( nCurX, nCurY );
+ }
+ else
+ bBlockNeg = false;
+ rMark.SetMarkNegative(bBlockNeg);
+
+ meBlockMode = Normal;
+ bBlockCols = bCols;
+ bBlockRows = bRows;
+ nBlockStartX = nBlockStartXOrig = nCurX;
+ nBlockStartY = nBlockStartYOrig = nCurY;
+ nBlockStartZ = nCurZ;
+ nBlockEndX = nOldCurX = nBlockStartX;
+ nBlockEndY = nOldCurY = nBlockStartY;
+ nBlockEndZ = nBlockStartZ;
+
+ if (bBlockCols)
+ {
+ nBlockStartY = nBlockStartYOrig = 0;
+ nBlockEndY = rDoc.MaxRow();
+ }
+
+ if (bBlockRows)
+ {
+ nBlockStartX = nBlockStartXOrig = 0;
+ nBlockEndX = rDoc.MaxCol();
+ }
+
+ rMark.SetMarkArea( ScRange( nBlockStartX,nBlockStartY, nTab, nBlockEndX,nBlockEndY, nTab ) );
+
+ UpdateSelectionOverlay();
+}
+
+void ScTabView::DoneBlockModeHighlight( bool bContinue )
+{
+ if (meHighlightBlockMode == None)
+ return;
+
+ ScMarkData& rMark = aViewData.GetHighlightData();
+ bool bFlag = rMark.GetMarkingFlag();
+ rMark.SetMarking(false);
+
+ if (bContinue)
+ rMark.MarkToMulti();
+ else
+ {
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( rDoc.HasTable(nTab) )
+ rMark.ResetMark();
+ }
+ meHighlightBlockMode = None;
+
+ rMark.SetMarking(bFlag);
+ if (bContinue)
+ rMark.SetMarking(false);
+}
+
+void ScTabView::DoneBlockMode( bool bContinue )
+{
+ // When switching between sheet and header SelectionEngine DeselectAll may be called,
+ // because the other engine does not have any anchor.
+ // bMoveIsShift prevents the selection to be canceled.
+
+ if (!IsBlockMode() || bMoveIsShift)
+ return;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ bool bFlag = rMark.GetMarkingFlag();
+ rMark.SetMarking(false);
+
+ if (bBlockNeg && !bContinue)
+ rMark.MarkToMulti();
+
+ if (bContinue)
+ rMark.MarkToMulti();
+ else
+ {
+ // the sheet may be invalid at this point because DoneBlockMode from SetTabNo is
+ // called (for example, when the current sheet is closed from another View)
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( rDoc.HasTable(nTab) )
+ PaintBlock( true ); // true -> delete block
+ else
+ rMark.ResetMark();
+ }
+ meBlockMode = None;
+
+ rMark.SetMarking(bFlag);
+ rMark.SetMarkNegative(false);
+}
+
+bool ScTabView::IsBlockMode() const
+{
+ return meBlockMode != None;
+}
+
+void ScTabView::MarkCursor( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ,
+ bool bCols, bool bRows, bool bCellSelection )
+{
+ ScDocument& rDocument = aViewData.GetDocument();
+ if (!rDocument.ValidCol(nCurX)) nCurX = rDocument.MaxCol();
+ if (!rDocument.ValidRow(nCurY)) nCurY = rDocument.MaxRow();
+
+ if (!IsBlockMode())
+ {
+ OSL_FAIL( "MarkCursor not in BlockMode" );
+ InitBlockMode( nCurX, nCurY, nCurZ, false, bCols, bRows );
+ }
+
+ if (bCols)
+ nCurY = rDocument.MaxRow();
+ if (bRows)
+ nCurX = rDocument.MaxCol();
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ OSL_ENSURE(rMark.IsMarked() || rMark.IsMultiMarked(), "MarkCursor, !IsMarked()");
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ if (( aMarkRange.aStart.Col() != nBlockStartX && aMarkRange.aEnd.Col() != nBlockStartX ) ||
+ ( aMarkRange.aStart.Row() != nBlockStartY && aMarkRange.aEnd.Row() != nBlockStartY ) ||
+ ( meBlockMode == Own ))
+ {
+ // Mark has been changed
+ // (Eg MarkToSimple if by negative everything was erased, except for a rectangle)
+ // or after InitOwnBlockMode is further marked with shift-
+ bool bOldShift = bMoveIsShift;
+ bMoveIsShift = false; // really move
+ DoneBlockMode(); //! Set variables directly? (-> no flicker)
+ bMoveIsShift = bOldShift;
+
+ InitBlockMode( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ nBlockStartZ, rMark.IsMarkNegative(), bCols, bRows );
+ }
+
+ if ( nCurX != nOldCurX || nCurY != nOldCurY )
+ {
+ // Current cursor has moved
+
+ SCTAB nTab = nCurZ;
+
+ if ( bCellSelection )
+ {
+ // Expand selection area accordingly when the current selection cuts
+ // through a merged cell.
+ ScRange cellSel(nBlockStartXOrig, nBlockStartYOrig, nTab, nCurX, nCurY, nTab);
+ cellSel.PutInOrder();
+ ScRange oldSel;
+ do
+ {
+ oldSel = cellSel;
+ rDocument.ExtendOverlapped(cellSel);
+ rDocument.ExtendMerge(cellSel);
+ } while (oldSel != cellSel);
+
+ // Preserve the directionality of the selection
+ if (nCurX >= nBlockStartXOrig)
+ {
+ nBlockStartX = cellSel.aStart.Col();
+ nBlockEndX = cellSel.aEnd.Col();
+ }
+ else
+ {
+ nBlockStartX = cellSel.aEnd.Col();
+ nBlockEndX = cellSel.aStart.Col();
+ }
+ if (nCurY >= nBlockStartYOrig)
+ {
+ nBlockStartY = cellSel.aStart.Row();
+ nBlockEndY = cellSel.aEnd.Row();
+ }
+ else
+ {
+ nBlockStartY = cellSel.aEnd.Row();
+ nBlockEndY = cellSel.aStart.Row();
+ }
+ }
+ else
+ {
+ nBlockEndX = nCurX;
+ nBlockEndY = nCurY;
+ }
+
+ // Set new selection area
+ rMark.SetMarkArea( ScRange( nBlockStartX, nBlockStartY, nTab, nBlockEndX, nBlockEndY, nTab ) );
+
+ UpdateSelectionOverlay();
+ SelectionChanged();
+
+ nOldCurX = nBlockEndX;
+ nOldCurY = nBlockEndY;
+
+ aViewData.GetViewShell()->UpdateInputHandler();
+ }
+
+ if ( !bCols && !bRows )
+ aHdrFunc.SetAnchorFlag( false );
+}
+
+void ScTabView::GetPageMoveEndPosition(SCCOL nMovX, SCROW nMovY, SCCOL& rPageX, SCROW& rPageY)
+{
+ SCCOL nCurX;
+ SCROW nCurY;
+ if (aViewData.IsRefMode())
+ {
+ nCurX = aViewData.GetRefEndX();
+ nCurY = aViewData.GetRefEndY();
+ }
+ else if (IsBlockMode())
+ {
+ // block end position.
+ nCurX = nBlockEndX;
+ nCurY = nBlockEndY;
+ }
+ else
+ {
+ // cursor position
+ nCurX = aViewData.GetCurX();
+ nCurY = aViewData.GetCurY();
+ }
+
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ ScHSplitPos eWhichX = WhichH( eWhich );
+ ScVSplitPos eWhichY = WhichV( eWhich );
+
+ sal_uInt16 nScrSizeY = SC_SIZE_NONE;
+ if (comphelper::LibreOfficeKit::isActive() && aViewData.GetPageUpDownOffset() > 0) {
+ nScrSizeY = ScViewData::ToPixel( aViewData.GetPageUpDownOffset(), aViewData.GetPPTX() );
+ }
+
+ SCCOL nPageX;
+ SCROW nPageY;
+ if (nMovX >= 0)
+ nPageX = aViewData.CellsAtX( nCurX, 1, eWhichX ) * nMovX;
+ else
+ nPageX = aViewData.CellsAtX( nCurX, -1, eWhichX ) * nMovX;
+
+ if (nMovY >= 0)
+ nPageY = aViewData.CellsAtY( nCurY, 1, eWhichY, nScrSizeY ) * nMovY;
+ else
+ nPageY = aViewData.CellsAtY( nCurY, -1, eWhichY, nScrSizeY ) * nMovY;
+
+ if (nMovX != 0 && nPageX == 0) nPageX = (nMovX>0) ? 1 : -1;
+ if (nMovY != 0 && nPageY == 0) nPageY = (nMovY>0) ? 1 : -1;
+
+ rPageX = nPageX;
+ rPageY = nPageY;
+}
+
+void ScTabView::GetAreaMoveEndPosition(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ SCCOL& rAreaX, SCROW& rAreaY, ScFollowMode& rMode,
+ bool bInteractiveByUser)
+{
+ SCCOL nNewX = -1;
+ SCROW nNewY = -1;
+
+ // current cursor position.
+ SCCOL nCurX = aViewData.GetCurX();
+ SCROW nCurY = aViewData.GetCurY();
+
+ ScModule* pScModule = SC_MOD();
+ bool bLegacyCellSelection = pScModule->GetInputOptions().GetLegacyCellSelection();
+ bool bIncrementallyExpandToDocLimits(false);
+
+ if (aViewData.IsRefMode())
+ {
+ nNewX = aViewData.GetRefEndX();
+ nNewY = aViewData.GetRefEndY();
+ nCurX = aViewData.GetRefStartX();
+ nCurY = aViewData.GetRefStartY();
+ }
+ else if (IsBlockMode())
+ {
+ // block end position.
+ nNewX = nBlockEndX;
+ nNewY = nBlockEndY;
+ }
+ else
+ {
+ nNewX = nCurX;
+ nNewY = nCurY;
+ // cool#6931 on ctrl+[right/down] don't immediately leap to the far limits of the document when no more data,
+ // instead jump a generous block of emptiness. Limit to direct interaction by user and the simple
+ // case.
+ bIncrementallyExpandToDocLimits = bInteractiveByUser && (nMovX == 1 || nMovY == 1) &&
+ !bLegacyCellSelection && comphelper::LibreOfficeKit::isActive();
+ }
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ // FindAreaPos knows only -1 or 1 as direction
+ SCCOL nVirtualX = bLegacyCellSelection ? nNewX : nCurX;
+ SCROW nVirtualY = bLegacyCellSelection ? nNewY : nCurY;
+
+ SCCOLROW i;
+ if ( nMovX > 0 )
+ for ( i=0; i<nMovX; i++ )
+ rDoc.FindAreaPos( nNewX, nVirtualY, nTab, SC_MOVE_RIGHT );
+ if ( nMovX < 0 )
+ for ( i=0; i<-nMovX; i++ )
+ rDoc.FindAreaPos( nNewX, nVirtualY, nTab, SC_MOVE_LEFT );
+ if ( nMovY > 0 )
+ for ( i=0; i<nMovY; i++ )
+ rDoc.FindAreaPos( nVirtualX, nNewY, nTab, SC_MOVE_DOWN );
+ if ( nMovY < 0 )
+ for ( i=0; i<-nMovY; i++ )
+ rDoc.FindAreaPos( nVirtualX, nNewY, nTab, SC_MOVE_UP );
+
+ if (eMode==SC_FOLLOW_JUMP) // bottom right do not show too much grey
+ {
+ if (nMovX != 0 && nNewX == rDoc.MaxCol())
+ {
+ eMode = SC_FOLLOW_LINE;
+ if (bIncrementallyExpandToDocLimits)
+ {
+ if (const ScTable* pTab = rDoc.FetchTable(nTab))
+ {
+ if (!pTab->HasData(nNewX, nCurY))
+ {
+ SCCOL nLastUsedCol(0);
+ SCROW nLastUsedRow(0);
+ rDoc.GetPrintArea(nTab, nLastUsedCol, nLastUsedRow);
+ SCCOL nJumpFrom = std::max(nCurX, nLastUsedCol);
+ nNewX = ((nJumpFrom / 13) + 2) * 13 - 1;
+ }
+ }
+ }
+ }
+ if (nMovY != 0 && nNewY == rDoc.MaxRow())
+ {
+ eMode = SC_FOLLOW_LINE;
+ if (bIncrementallyExpandToDocLimits)
+ {
+ if (const ScTable* pTab = rDoc.FetchTable(nTab))
+ {
+ if (!pTab->HasData(nCurX, nNewY))
+ {
+ SCCOL nLastUsedCol(0);
+ SCROW nLastUsedRow(0);
+ rDoc.GetPrintArea(nTab, nLastUsedCol, nLastUsedRow);
+ SCROW nJumpFrom = std::max(nCurY, nLastUsedRow);
+ nNewY = ((nJumpFrom / 500) + 2) * 500 - 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (aViewData.IsRefMode())
+ {
+ rAreaX = nNewX - aViewData.GetRefEndX();
+ rAreaY = nNewY - aViewData.GetRefEndY();
+ }
+ else if (IsBlockMode())
+ {
+ rAreaX = nNewX - nBlockEndX;
+ rAreaY = nNewY - nBlockEndY;
+ }
+ else
+ {
+ rAreaX = nNewX - nCurX;
+ rAreaY = nNewY - nCurY;
+ }
+ rMode = eMode;
+}
+
+void ScTabView::SkipCursorHorizontal(SCCOL& rCurX, SCROW& rCurY, SCCOL nOldX, SCCOL nMovX)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ bool bSkipProtected = false, bSkipUnprotected = false;
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect && pProtect->isProtected())
+ {
+ bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ bool bSkipCell = false;
+ bool bHFlip = false;
+ // If a number of last columns are hidden, search up to and including the first of them,
+ // because after it nothing changes.
+ SCCOL nMaxCol;
+ if(rDoc.ColHidden(rDoc.MaxCol(), nTab, &nMaxCol))
+ ++nMaxCol;
+ else
+ nMaxCol = rDoc.MaxCol();
+ // Search also at least up to and including the first unallocated column (all unallocated columns
+ // share a set of attrs).
+ nMaxCol = std::max( nMaxCol, std::min<SCCOL>( rDoc.GetAllocatedColumnsCount(nTab) + 1, rDoc.MaxCol()));
+ do
+ {
+ bSkipCell = rDoc.ColHidden(rCurX, nTab) || rDoc.IsHorOverlapped(rCurX, rCurY, nTab);
+ if (bSkipProtected && !bSkipCell)
+ bSkipCell = rDoc.HasAttrib(rCurX, rCurY, nTab, rCurX, rCurY, nTab, HasAttrFlags::Protected);
+ if (bSkipUnprotected && !bSkipCell)
+ bSkipCell = !rDoc.HasAttrib(rCurX, rCurY, nTab, rCurX, rCurY, nTab, HasAttrFlags::Protected);
+
+ if (bSkipCell)
+ {
+ if (rCurX <= 0 || rCurX >= nMaxCol)
+ {
+ if (bHFlip)
+ {
+ rCurX = nOldX;
+ bSkipCell = false;
+ }
+ else
+ {
+ nMovX = -nMovX;
+ if (nMovX > 0)
+ ++rCurX;
+ else
+ --rCurX;
+ bHFlip = true;
+ }
+ }
+ else
+ if (nMovX > 0)
+ ++rCurX;
+ else
+ --rCurX;
+ }
+ }
+ while (bSkipCell);
+
+ if (rDoc.IsVerOverlapped(rCurX, rCurY, nTab))
+ {
+ aViewData.SetOldCursor(rCurX, rCurY);
+ while (rDoc.IsVerOverlapped(rCurX, rCurY, nTab))
+ --rCurY;
+ }
+}
+
+void ScTabView::SkipCursorVertical(SCCOL& rCurX, SCROW& rCurY, SCROW nOldY, SCROW nMovY)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ bool bSkipProtected = false, bSkipUnprotected = false;
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect && pProtect->isProtected())
+ {
+ bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ bool bSkipCell = false;
+ bool bVFlip = false;
+ // Avoid repeated calls to RowHidden(), IsVerOverlapped() and HasAttrib().
+ SCROW nFirstSameHiddenRow = -1;
+ SCROW nLastSameHiddenRow = -1;
+ bool bRowHidden = false;
+ SCROW nFirstSameIsVerOverlapped = -1;
+ SCROW nLastSameIsVerOverlapped = -1;
+ bool bIsVerOverlapped = false;
+ SCROW nFirstSameHasAttribRow = -1;
+ SCROW nLastSameHasAttribRow = -1;
+ bool bHasAttribProtected = false;
+ do
+ {
+ if( rCurY < nFirstSameHiddenRow || rCurY > nLastSameHiddenRow )
+ bRowHidden = rDoc.RowHidden(rCurY, nTab, &nFirstSameHiddenRow, &nLastSameHiddenRow);
+ bSkipCell = bRowHidden;
+ if( !bSkipCell )
+ {
+ if( rCurY < nFirstSameIsVerOverlapped || rCurY > nLastSameIsVerOverlapped )
+ bIsVerOverlapped = rDoc.IsVerOverlapped(rCurX, rCurY, nTab, &nFirstSameIsVerOverlapped, &nLastSameIsVerOverlapped);
+ bSkipCell = bIsVerOverlapped;
+ }
+ if (bSkipProtected && !bSkipCell)
+ {
+ if( rCurY < nFirstSameHasAttribRow || rCurY > nLastSameHasAttribRow )
+ bHasAttribProtected = rDoc.HasAttrib(rCurX, rCurY, nTab, HasAttrFlags::Protected,
+ &nFirstSameHasAttribRow, &nLastSameHasAttribRow);
+ bSkipCell = bHasAttribProtected;
+ }
+ if (bSkipUnprotected && !bSkipCell)
+ {
+ if( rCurY < nFirstSameHasAttribRow || rCurY > nLastSameHasAttribRow )
+ bHasAttribProtected = rDoc.HasAttrib(rCurX, rCurY, nTab, HasAttrFlags::Protected,
+ &nFirstSameHasAttribRow, &nLastSameHasAttribRow);
+ bSkipCell = !bHasAttribProtected;
+ }
+
+ if (bSkipCell)
+ {
+ if (rCurY <= 0 || rCurY >= rDoc.MaxRow())
+ {
+ if (bVFlip)
+ {
+ rCurY = nOldY;
+ bSkipCell = false;
+ }
+ else
+ {
+ nMovY = -nMovY;
+ if (nMovY > 0)
+ ++rCurY;
+ else
+ --rCurY;
+ bVFlip = true;
+ }
+ }
+ else
+ if (nMovY > 0)
+ ++rCurY;
+ else
+ --rCurY;
+ }
+ }
+ while (bSkipCell);
+
+ if (rDoc.IsHorOverlapped(rCurX, rCurY, nTab))
+ {
+ aViewData.SetOldCursor(rCurX, rCurY);
+ while (rDoc.IsHorOverlapped(rCurX, rCurY, nTab))
+ --rCurX;
+ }
+}
+
+void ScTabView::ExpandBlock(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode)
+{
+ if (!nMovX && !nMovY)
+ // Nothing to do. Bail out.
+ return;
+
+ ScTabViewShell* pViewShell = aViewData.GetViewShell();
+ bool bRefInputMode = pViewShell && pViewShell->IsRefInputMode();
+ if (bRefInputMode && !aViewData.IsRefMode())
+ // initialize formula reference mode if it hasn't already.
+ InitRefMode(aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo(), SC_REFTYPE_REF);
+
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (aViewData.IsRefMode())
+ {
+ // formula reference mode
+
+ SCCOL nNewX = aViewData.GetRefEndX();
+ SCROW nNewY = aViewData.GetRefEndY();
+ SCTAB nRefTab = aViewData.GetRefEndZ();
+
+ moveRefByCell(nNewX, nNewY, nMovX, nMovY, nRefTab, rDoc);
+
+ UpdateRef(nNewX, nNewY, nRefTab);
+ SCCOL nTargetCol = nNewX;
+ SCROW nTargetRow = nNewY;
+ if (((aViewData.GetRefStartX() == 0) || (aViewData.GetRefStartY() == 0)) &&
+ ((nNewX != rDoc.MaxCol()) || (nNewY != rDoc.MaxRow())))
+ {
+ // Row selection
+ if ((aViewData.GetRefStartX() == 0) && (nNewX == rDoc.MaxCol()))
+ nTargetCol = aViewData.GetCurX();
+ // Column selection
+ if ((aViewData.GetRefStartY() == 0) && (nNewY == rDoc.MaxRow()))
+ nTargetRow = aViewData.GetCurY();
+ }
+ AlignToCursor(nTargetCol, nTargetRow, eMode);
+ }
+ else
+ {
+ // normal selection mode
+
+ SCTAB nTab = aViewData.GetTabNo();
+ SCCOL nOrigX = aViewData.GetCurX();
+ SCROW nOrigY = aViewData.GetCurY();
+
+ // Note that the origin position *never* moves during selection.
+
+ if (!IsBlockMode())
+ {
+ InitBlockMode(nOrigX, nOrigY, nTab, true);
+ const ScMergeAttr* pMergeAttr = rDoc.GetAttr(nOrigX, nOrigY, nTab, ATTR_MERGE);
+ if (pMergeAttr && pMergeAttr->IsMerged())
+ {
+ nBlockEndX = nOrigX + pMergeAttr->GetColMerge() - 1;
+ nBlockEndY = nOrigY + pMergeAttr->GetRowMerge() - 1;
+ }
+ }
+
+ moveCursorToProperSide(nBlockEndX, nBlockEndY, nMovX, nMovY, nBlockStartX, nBlockStartY,
+ nTab, &rDoc);
+ moveCursorByProtRule(nBlockEndX, nBlockEndY, nMovX, nMovY, nTab, &rDoc);
+ checkBoundary(&rDoc, nBlockEndX, nBlockEndY);
+ moveCursorByMergedCell(nBlockEndX, nBlockEndY, nMovX, nMovY, nBlockStartX, nBlockStartY,
+ nTab, &rDoc);
+ checkBoundary(&rDoc, nBlockEndX, nBlockEndY);
+
+ MarkCursor(nBlockEndX, nBlockEndY, nTab, false, false, true);
+
+ // Check if the entire row(s) or column(s) are selected.
+ ScSplitPos eActive = aViewData.GetActivePart();
+ bool bRowSelected = (nBlockStartX == 0 && nBlockEndX == rDoc.MaxCol());
+ bool bColSelected = (nBlockStartY == 0 && nBlockEndY == rDoc.MaxRow());
+ SCCOL nAlignX = bRowSelected ? aViewData.GetPosX(WhichH(eActive)) : nBlockEndX;
+ SCROW nAlignY = bColSelected ? aViewData.GetPosY(WhichV(eActive)) : nBlockEndY;
+ AlignToCursor(nAlignX, nAlignY, eMode);
+
+ SelectionChanged();
+ }
+}
+
+void ScTabView::ExpandBlockPage(SCCOL nMovX, SCROW nMovY)
+{
+ SCCOL nPageX;
+ SCROW nPageY;
+ GetPageMoveEndPosition(nMovX, nMovY, nPageX, nPageY);
+ ExpandBlock(nPageX, nPageY, SC_FOLLOW_FIX);
+}
+
+void ScTabView::ExpandBlockArea(SCCOL nMovX, SCROW nMovY)
+{
+ SCCOL nAreaX;
+ SCROW nAreaY;
+ ScFollowMode eMode;
+ GetAreaMoveEndPosition(nMovX, nMovY, SC_FOLLOW_JUMP, nAreaX, nAreaY, eMode);
+ ExpandBlock(nAreaX, nAreaY, eMode);
+}
+
+void ScTabView::UpdateCopySourceOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin && pWin->IsVisible())
+ pWin->UpdateCopySourceOverlay();
+}
+
+void ScTabView::UpdateSelectionOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateSelectionOverlay();
+}
+
+void ScTabView::UpdateHighlightOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateHighlightOverlay();
+}
+
+void ScTabView::UpdateShrinkOverlay()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateShrinkOverlay();
+}
+
+void ScTabView::UpdateAllOverlays()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin && pWin->IsVisible() )
+ pWin->UpdateAllOverlays();
+}
+
+//!
+//! divide PaintBlock into two methods: RepaintBlock and RemoveBlock or similar
+//!
+
+void ScTabView::PaintBlock( bool bReset )
+{
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bMulti = rMark.IsMultiMarked();
+ if (!(rMark.IsMarked() || bMulti))
+ return;
+
+ ScRange aMarkRange;
+ HideAllCursors();
+ if (bMulti)
+ {
+ bool bFlag = rMark.GetMarkingFlag();
+ rMark.SetMarking(false);
+ rMark.MarkToMulti();
+ aMarkRange = rMark.GetMultiMarkArea();
+ rMark.MarkToSimple();
+ rMark.SetMarking(bFlag);
+ }
+ else
+ aMarkRange = rMark.GetMarkArea();
+
+ nBlockStartX = aMarkRange.aStart.Col();
+ nBlockStartY = aMarkRange.aStart.Row();
+ nBlockStartZ = aMarkRange.aStart.Tab();
+ nBlockEndX = aMarkRange.aEnd.Col();
+ nBlockEndY = aMarkRange.aEnd.Row();
+ nBlockEndZ = aMarkRange.aEnd.Tab();
+
+ bool bDidReset = false;
+
+ if ( nTab>=nBlockStartZ && nTab<=nBlockEndZ )
+ {
+ if ( bReset )
+ {
+ // Inverting when deleting only on active View
+ if ( aViewData.IsActive() )
+ {
+ rMark.ResetMark();
+ UpdateSelectionOverlay();
+ bDidReset = true;
+ }
+ }
+ else
+ PaintMarks( nBlockStartX, nBlockStartY, nBlockEndX, nBlockEndY );
+ }
+
+ if ( bReset && !bDidReset )
+ rMark.ResetMark();
+
+ ShowAllCursors();
+}
+
+void ScTabView::SelectAll( bool bContinue )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ if (rMark.IsMarked())
+ {
+ if ( rMark.GetMarkArea() == ScRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ) )
+ return;
+ }
+
+ DoneBlockMode( bContinue );
+ InitBlockMode( 0,0,nTab );
+ MarkCursor( rDoc.MaxCol(),rDoc.MaxRow(),nTab );
+
+ SelectionChanged();
+}
+
+void ScTabView::SelectAllTables()
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nCount = rDoc.GetTableCount();
+
+ if (nCount>1)
+ {
+ for (SCTAB i=0; i<nCount; i++)
+ rMark.SelectTable( i, true );
+
+ aViewData.GetDocShell()->PostPaintExtras();
+ SfxBindings& rBind = aViewData.GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+ }
+}
+
+void ScTabView::DeselectAllTables()
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+ SCTAB nCount = rDoc.GetTableCount();
+
+ for (SCTAB i=0; i<nCount; i++)
+ rMark.SelectTable( i, ( i == nTab ) );
+
+ aViewData.GetDocShell()->PostPaintExtras();
+ SfxBindings& rBind = aViewData.GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+}
+
+static bool lcl_FitsInWindow( double fScaleX, double fScaleY, sal_uInt16 nZoom,
+ tools::Long nWindowX, tools::Long nWindowY, const ScDocument* pDoc, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCCOL nFixPosX, SCROW nFixPosY )
+{
+ double fZoomFactor = static_cast<double>(Fraction(nZoom,100));
+ fScaleX *= fZoomFactor;
+ fScaleY *= fZoomFactor;
+
+ tools::Long nBlockX = 0;
+ SCCOL nCol;
+ for (nCol=0; nCol<nFixPosX; nCol++)
+ {
+ // for frozen panes, add both parts
+ sal_uInt16 nColTwips = pDoc->GetColWidth( nCol, nTab );
+ if (nColTwips)
+ {
+ nBlockX += static_cast<tools::Long>(nColTwips * fScaleX);
+ if (nBlockX > nWindowX)
+ return false;
+ }
+ }
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ sal_uInt16 nColTwips = pDoc->GetColWidth( nCol, nTab );
+ if (nColTwips)
+ {
+ nBlockX += static_cast<tools::Long>(nColTwips * fScaleX);
+ if (nBlockX > nWindowX)
+ return false;
+ }
+ }
+
+ tools::Long nBlockY = 0;
+ for (SCROW nRow = 0; nRow <= nFixPosY-1; ++nRow)
+ {
+ if (pDoc->RowHidden(nRow, nTab))
+ continue;
+
+ // for frozen panes, add both parts
+ sal_uInt16 nRowTwips = pDoc->GetRowHeight(nRow, nTab);
+ if (nRowTwips)
+ {
+ nBlockY += static_cast<tools::Long>(nRowTwips * fScaleY);
+ if (nBlockY > nWindowY)
+ return false;
+ }
+ }
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ sal_uInt16 nRowTwips = pDoc->GetRowHeight(nRow, nTab);
+ if (nRowTwips)
+ {
+ nBlockY += static_cast<tools::Long>(nRowTwips * fScaleY);
+ if (nBlockY > nWindowY)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+sal_uInt16 ScTabView::CalcZoom( SvxZoomType eType, sal_uInt16 nOldZoom )
+{
+ sal_uInt16 nZoom = 100;
+
+ switch ( eType )
+ {
+ case SvxZoomType::PERCENT: // rZoom is no particular percent value
+ nZoom = nOldZoom;
+ break;
+
+ case SvxZoomType::OPTIMAL: // nZoom corresponds to the optimal size
+ {
+ ScMarkData& rMark = aViewData.GetMarkData();
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ nZoom = 100; // nothing selected
+ else
+ {
+ SCTAB nTab = aViewData.GetTabNo();
+ ScRange aMarkRange;
+ if ( aViewData.GetSimpleArea( aMarkRange ) != SC_MARK_SIMPLE )
+ aMarkRange = rMark.GetMultiMarkArea();
+
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+
+ if ( nTab < nStartTab && nTab > nEndTab )
+ nTab = nStartTab;
+
+ ScSplitPos eUsedPart = aViewData.GetActivePart();
+
+ SCCOL nFixPosX = 0;
+ SCROW nFixPosY = 0;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ {
+ // use right part
+ eUsedPart = (WhichV(eUsedPart)==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT;
+ nFixPosX = aViewData.GetFixPosX();
+ if ( nStartCol < nFixPosX )
+ nStartCol = nFixPosX;
+ }
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ {
+ // use bottom part
+ eUsedPart = (WhichH(eUsedPart)==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+ nFixPosY = aViewData.GetFixPosY();
+ if ( nStartRow < nFixPosY )
+ nStartRow = nFixPosY;
+ }
+
+ if (pGridWin[eUsedPart])
+ {
+ // Because scale is rounded to pixels, the only reliable way to find
+ // the right scale is to check if a zoom fits
+
+ Size aWinSize = pGridWin[eUsedPart]->GetOutputSizePixel();
+
+ // for frozen panes, use sum of both parts for calculation
+
+ if ( nFixPosX != 0 )
+ aWinSize.AdjustWidth(GetGridWidth( SC_SPLIT_LEFT ) );
+ if ( nFixPosY != 0 )
+ aWinSize.AdjustHeight(GetGridHeight( SC_SPLIT_TOP ) );
+
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ double nPPTX = ScGlobal::nScreenPPTX / pDocSh->GetOutputFactor();
+ double nPPTY = ScGlobal::nScreenPPTY;
+
+ sal_uInt16 nMin = MINZOOM;
+ sal_uInt16 nMax = MAXZOOM;
+ while ( nMax > nMin )
+ {
+ sal_uInt16 nTest = (nMin+nMax+1)/2;
+ if ( lcl_FitsInWindow(
+ nPPTX, nPPTY, nTest, aWinSize.Width(), aWinSize.Height(),
+ &rDoc, nTab, nStartCol, nStartRow, nEndCol, nEndRow,
+ nFixPosX, nFixPosY ) )
+ nMin = nTest;
+ else
+ nMax = nTest-1;
+ }
+ OSL_ENSURE( nMin == nMax, "Nesting is wrong" );
+ nZoom = nMin;
+
+ if ( nZoom != nOldZoom )
+ {
+ // scroll to block only in active split part
+ // (the part for which the size was calculated)
+
+ if ( nStartCol <= nEndCol )
+ aViewData.SetPosX( WhichH(eUsedPart), nStartCol );
+ if ( nStartRow <= nEndRow )
+ aViewData.SetPosY( WhichV(eUsedPart), nStartRow );
+ }
+ }
+ }
+ }
+ break;
+
+ case SvxZoomType::WHOLEPAGE: // nZoom corresponds to the whole page or
+ case SvxZoomType::PAGEWIDTH: // nZoom corresponds to the page width
+ {
+ SCTAB nCurTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet =
+ pStylePool->Find( rDoc.GetPageStyle( nCurTab ),
+ SfxStyleFamily::Page );
+
+ OSL_ENSURE( pStyleSheet, "PageStyle not found :-/" );
+
+ if ( pStyleSheet )
+ {
+ ScPrintFunc aPrintFunc( aViewData.GetDocShell(),
+ aViewData.GetViewShell()->GetPrinter(true),
+ nCurTab );
+
+ Size aPageSize = aPrintFunc.GetDataSize();
+
+ // use the size of the largest GridWin for normal split,
+ // or both combined for frozen panes, with the (document) size
+ // of the frozen part added to the page size
+ // (with frozen panes, the size of the individual parts
+ // depends on the scale that is to be calculated)
+
+ if (!pGridWin[SC_SPLIT_BOTTOMLEFT])
+ return nZoom;
+
+ Size aWinSize = pGridWin[SC_SPLIT_BOTTOMLEFT]->GetOutputSizePixel();
+ ScSplitMode eHMode = aViewData.GetHSplitMode();
+ if ( eHMode != SC_SPLIT_NONE && pGridWin[SC_SPLIT_BOTTOMRIGHT] )
+ {
+ tools::Long nOtherWidth = pGridWin[SC_SPLIT_BOTTOMRIGHT]->
+ GetOutputSizePixel().Width();
+ if ( eHMode == SC_SPLIT_FIX )
+ {
+ aWinSize.AdjustWidth(nOtherWidth );
+ for ( SCCOL nCol = aViewData.GetPosX(SC_SPLIT_LEFT);
+ nCol < aViewData.GetFixPosX(); nCol++ )
+ aPageSize.AdjustWidth(rDoc.GetColWidth( nCol, nCurTab ) );
+ }
+ else if ( nOtherWidth > aWinSize.Width() )
+ aWinSize.setWidth( nOtherWidth );
+ }
+ ScSplitMode eVMode = aViewData.GetVSplitMode();
+ if ( eVMode != SC_SPLIT_NONE && pGridWin[SC_SPLIT_TOPLEFT] )
+ {
+ tools::Long nOtherHeight = pGridWin[SC_SPLIT_TOPLEFT]->
+ GetOutputSizePixel().Height();
+ if ( eVMode == SC_SPLIT_FIX )
+ {
+ aWinSize.AdjustHeight(nOtherHeight );
+ aPageSize.AdjustHeight(rDoc.GetRowHeight(
+ aViewData.GetPosY(SC_SPLIT_TOP),
+ aViewData.GetFixPosY()-1, nCurTab) );
+ }
+ else if ( nOtherHeight > aWinSize.Height() )
+ aWinSize.setHeight( nOtherHeight );
+ }
+
+ double nPPTX = ScGlobal::nScreenPPTX / aViewData.GetDocShell()->GetOutputFactor();
+ double nPPTY = ScGlobal::nScreenPPTY;
+
+ tools::Long nZoomX = static_cast<tools::Long>( aWinSize.Width() * 100 /
+ ( aPageSize.Width() * nPPTX ) );
+ tools::Long nZoomY = static_cast<tools::Long>( aWinSize.Height() * 100 /
+ ( aPageSize.Height() * nPPTY ) );
+
+ if (nZoomX > 0)
+ nZoom = static_cast<sal_uInt16>(nZoomX);
+
+ if (eType == SvxZoomType::WHOLEPAGE && nZoomY > 0 && nZoomY < nZoom)
+ nZoom = static_cast<sal_uInt16>(nZoomY);
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("Unknown Zoom-Revision");
+ }
+
+ return nZoom;
+}
+
+// is called for instance when the view window is shifted:
+
+void ScTabView::StopMarking()
+{
+ ScSplitPos eActive = aViewData.GetActivePart();
+ if (pGridWin[eActive])
+ pGridWin[eActive]->StopMarking();
+
+ ScHSplitPos eH = WhichH(eActive);
+ if (pColBar[eH])
+ pColBar[eH]->StopMarking();
+
+ ScVSplitPos eV = WhichV(eActive);
+ if (pRowBar[eV])
+ pRowBar[eV]->StopMarking();
+}
+
+void ScTabView::HideNoteMarker()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if (pWin && pWin->IsVisible())
+ pWin->HideNoteMarker();
+}
+
+void ScTabView::MakeDrawLayer()
+{
+ if (pDrawView)
+ return;
+
+ aViewData.GetDocShell()->MakeDrawLayer();
+
+ // pDrawView is set per Notify
+ OSL_ENSURE(pDrawView,"ScTabView::MakeDrawLayer does not work");
+
+ for(VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if(pWin)
+ {
+ pWin->DrawLayerCreated();
+ }
+ }
+}
+
+IMPL_STATIC_LINK_NOARG(ScTabView, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+void ScTabView::ErrorMessage(TranslateId pGlobStrId)
+{
+ if ( SC_MOD()->IsInExecuteDrop() )
+ {
+ // #i28468# don't show error message when called from Drag&Drop, silently abort instead
+ return;
+ }
+
+ StopMarking(); // if called by Focus from MouseButtonDown
+
+ weld::Window* pParent = aViewData.GetDialogParent();
+ weld::WaitObject aWaitOff( pParent );
+ bool bFocus = pParent && pParent->has_focus();
+
+ if (pGlobStrId && pGlobStrId == STR_PROTECTIONERR)
+ {
+ if (aViewData.GetDocShell()->IsReadOnly())
+ {
+ pGlobStrId = STR_READONLYERR;
+ }
+ }
+
+ m_xMessageBox.reset(Application::CreateMessageDialog(pParent,
+ VclMessageType::Info, VclButtonsType::Ok,
+ ScResId(pGlobStrId)));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ m_xMessageBox->SetInstallLOKNotifierHdl(LINK(this, ScTabView, InstallLOKNotifierHdl));
+
+ weld::Window* pGrabOnClose = bFocus ? pParent : nullptr;
+ m_xMessageBox->runAsync(m_xMessageBox, [this, pGrabOnClose](sal_Int32 /*nResult*/) {
+ m_xMessageBox.reset();
+ if (pGrabOnClose)
+ pGrabOnClose->grab_focus();
+ });
+}
+
+void ScTabView::UpdatePageBreakData( bool bForcePaint )
+{
+ std::unique_ptr<ScPageBreakData> pNewData;
+
+ if (aViewData.IsPagebreakMode())
+ {
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ sal_uInt16 nCount = rDoc.GetPrintRangeCount(nTab);
+ if (!nCount)
+ nCount = 1;
+ pNewData.reset( new ScPageBreakData(nCount) );
+
+ ScPrintFunc aPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab, 0,0,nullptr, nullptr, pNewData.get() );
+ // ScPrintFunc fills the PageBreakData in ctor
+ if ( nCount > 1 )
+ {
+ aPrintFunc.ResetBreaks(nTab);
+ pNewData->AddPages();
+ }
+
+ // print area changed?
+ if ( bForcePaint || ( pPageBreakData && !( *pPageBreakData == *pNewData ) ) )
+ PaintGrid();
+ }
+
+ pPageBreakData = std::move(pNewData);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx
new file mode 100644
index 0000000000..4a78aa38e7
--- /dev/null
+++ b/sc/source/ui/view/tabview3.cxx
@@ -0,0 +1,3171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <officecfg/Office/Calc.hxx>
+#include <rangelst.hxx>
+#include <scitems.hxx>
+
+#include <editeng/editview.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/svdoole2.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <IAnyRefDialog.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+#include <olinewin.hxx>
+#include <overlayobject.hxx>
+#include <colrowba.hxx>
+#include <tabcont.hxx>
+#include <scmod.hxx>
+#include <sc.hrc>
+#include <viewutil.hxx>
+#include <editutil.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <validat.hxx>
+#include <inputopt.hxx>
+#include <rfindlst.hxx>
+#include <hiranges.hxx>
+#include <viewuno.hxx>
+#include <dpobject.hxx>
+#include <seltrans.hxx>
+#include <fillinfo.hxx>
+#include <rangeutl.hxx>
+#include <client.hxx>
+#include <tabprotection.hxx>
+#include <spellcheckcontext.hxx>
+#include <markdata.hxx>
+#include <formula/FormulaCompiler.hxx>
+#include <comphelper/lok.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <output.hxx>
+
+#include <utility>
+
+#include <com/sun/star/chart2/data/HighlightedRange.hpp>
+
+namespace
+{
+
+ScRange lcl_getSubRangeByIndex( const ScRange& rRange, sal_Int32 nIndex )
+{
+ ScAddress aResult( rRange.aStart );
+
+ SCCOL nWidth = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ SCROW nHeight = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ SCTAB nDepth = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+ if( (nWidth > 0) && (nHeight > 0) && (nDepth > 0) )
+ {
+ // row by row from first to last sheet
+ sal_Int32 nArea = nWidth * nHeight;
+ aResult.IncCol( static_cast< SCCOL >( nIndex % nWidth ) );
+ aResult.IncRow( static_cast< SCROW >( (nIndex % nArea) / nWidth ) );
+ aResult.IncTab( static_cast< SCTAB >( nIndex / nArea ) );
+ if( !rRange.Contains( aResult ) )
+ aResult = rRange.aStart;
+ }
+
+ return ScRange( aResult );
+}
+
+} // anonymous namespace
+
+using namespace com::sun::star;
+
+ScExtraEditViewManager::~ScExtraEditViewManager()
+{
+ DBG_ASSERT(nTotalWindows == 0, "ScExtraEditViewManager dtor: some out window has not yet been removed!");
+}
+
+inline void ScExtraEditViewManager::Add(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ Apply<Adder>(pViewShell, eWhich);
+}
+
+inline void ScExtraEditViewManager::Remove(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ Apply<Remover>(pViewShell, eWhich);
+}
+
+
+template<ScExtraEditViewManager::ModifierTagType ModifierTag>
+void ScExtraEditViewManager::Apply(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pOtherViewShell == nullptr || pOtherViewShell == mpThisViewShell)
+ return;
+
+ mpOtherEditView = pOtherViewShell->GetViewData().GetEditView(eWhich);
+ if (mpOtherEditView != nullptr)
+ {
+ DBG_ASSERT(mpOtherEditView->GetEditEngine(), "Edit view has no valid engine.");
+ for (int i = 0; i < 4; ++i)
+ {
+ ScGridWindow* pWin = mpGridWin[i].get();
+ if (pWin != nullptr)
+ {
+ Modifier<ModifierTag>(pWin);
+ }
+ }
+ }
+}
+
+template<ScExtraEditViewManager::ModifierTagType ModifierTag>
+void ScExtraEditViewManager::Modifier(ScGridWindow* /*pWin*/)
+{
+ (void)this;
+ SAL_WARN("sc", "ScExtraEditViewManager::Modifier<ModifierTag>: non-specialized version should not be invoked.");
+}
+
+template<>
+void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Adder>(ScGridWindow* pWin)
+{
+ if (mpOtherEditView->AddOtherViewWindow(pWin))
+ ++nTotalWindows;
+}
+
+template<>
+void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Remover>(ScGridWindow* pWin)
+{
+ if (mpOtherEditView->RemoveOtherViewWindow(pWin))
+ --nTotalWindows;
+}
+
+// --- public functions
+
+void ScTabView::ClickCursor( SCCOL nPosX, SCROW nPosY, bool bControl )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ rDoc.SkipOverlapped(nPosX, nPosY, nTab);
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+
+ if ( bRefMode )
+ {
+ DoneRefMode();
+
+ if (bControl)
+ SC_MOD()->AddRefEntry();
+
+ InitRefMode( nPosX, nPosY, nTab, SC_REFTYPE_REF );
+ }
+ else
+ {
+ DoneBlockMode( bControl );
+ aViewData.ResetOldCursor();
+ SetCursor( nPosX, nPosY );
+ }
+}
+
+void ScTabView::UpdateAutoFillMark(bool bFromPaste)
+{
+ // single selection or cursor
+ ScRange aMarkRange;
+ ScMarkType eMarkType = aViewData.GetSimpleArea(aMarkRange);
+ bool bMarked = eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED;
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->UpdateAutoFillMark( bMarked, aMarkRange );
+ }
+
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ if (pColBar[i] && pColBar[i]->IsVisible())
+ pColBar[i]->SetMark( bMarked, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col() );
+ if (pRowBar[i] && pRowBar[i]->IsVisible())
+ pRowBar[i]->SetMark( bMarked, aMarkRange.aStart.Row(), aMarkRange.aEnd.Row() );
+ }
+
+ // selection transfer object is checked together with AutoFill marks,
+ // because it has the same requirement of a single continuous block.
+ if (!bFromPaste)
+ CheckSelectionTransfer(); // update selection transfer object
+}
+
+void ScTabView::FakeButtonUp( ScSplitPos eWhich )
+{
+ if (pGridWin[eWhich])
+ pGridWin[eWhich]->FakeButtonUp();
+}
+
+void ScTabView::HideAllCursors()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ {
+ vcl::Cursor* pCur = pWin->GetCursor();
+ if (pCur && pCur->IsVisible())
+ pCur->Hide();
+ pWin->HideCursor();
+ }
+ }
+}
+
+void ScTabView::ShowAllCursors()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ {
+ pWin->ShowCursor();
+ pWin->CursorChanged();
+ }
+ }
+}
+
+void ScTabView::ShowCursor()
+{
+ pGridWin[aViewData.GetActivePart()]->ShowCursor();
+ pGridWin[aViewData.GetActivePart()]->CursorChanged();
+}
+
+void ScTabView::InvalidateAttribs()
+{
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ rBindings.Invalidate( SID_STYLE_APPLY );
+ rBindings.Invalidate( SID_STYLE_FAMILY2 );
+ rBindings.Invalidate( SID_STYLE_FAMILY3 );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_FONT );
+ rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
+ rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
+ rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
+ rBindings.Invalidate( SID_ULINE_VAL_NONE );
+ rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
+ rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
+
+ rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
+ rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
+ rBindings.Invalidate( SID_SET_SUB_SCRIPT );
+ rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
+ rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
+
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
+ rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
+ rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT);
+
+ rBindings.Invalidate( SID_ALIGNLEFT );
+ rBindings.Invalidate( SID_ALIGNRIGHT );
+ rBindings.Invalidate( SID_ALIGNBLOCK );
+ rBindings.Invalidate( SID_ALIGNCENTERHOR );
+
+ rBindings.Invalidate( SID_ALIGNTOP );
+ rBindings.Invalidate( SID_ALIGNBOTTOM );
+ rBindings.Invalidate( SID_ALIGNCENTERVER );
+
+ rBindings.Invalidate( SID_SCATTR_CELLPROTECTION );
+
+ // stuff for sidebar panels
+ {
+ rBindings.Invalidate( SID_H_ALIGNCELL );
+ rBindings.Invalidate( SID_V_ALIGNCELL );
+ rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
+ rBindings.Invalidate( SID_FRAME_LINECOLOR );
+ rBindings.Invalidate( SID_FRAME_LINESTYLE );
+ rBindings.Invalidate( SID_ATTR_BORDER_OUTER );
+ rBindings.Invalidate( SID_ATTR_BORDER_INNER );
+ rBindings.Invalidate( SID_ATTR_BORDER_DIAG_TLBR );
+ rBindings.Invalidate( SID_ATTR_BORDER_DIAG_BLTR );
+ rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT );
+ }
+
+ rBindings.Invalidate( SID_BACKGROUND_COLOR );
+
+ rBindings.Invalidate( SID_ATTR_ALIGN_LINEBREAK );
+ rBindings.Invalidate( SID_NUMBER_FORMAT );
+
+ rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
+ rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
+ rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
+
+ // pseudo slots for Format menu
+ rBindings.Invalidate( SID_ALIGN_ANY_HDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
+ rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
+ rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
+ rBindings.Invalidate( SID_ALIGN_ANY_VDEFAULT );
+ rBindings.Invalidate( SID_ALIGN_ANY_TOP );
+ rBindings.Invalidate( SID_ALIGN_ANY_VCENTER );
+ rBindings.Invalidate( SID_ALIGN_ANY_BOTTOM );
+
+ rBindings.Invalidate( SID_NUMBER_CURRENCY );
+ rBindings.Invalidate( SID_NUMBER_SCIENTIFIC );
+ rBindings.Invalidate( SID_NUMBER_DATE );
+ rBindings.Invalidate( SID_NUMBER_CURRENCY );
+ rBindings.Invalidate( SID_NUMBER_PERCENT );
+ rBindings.Invalidate( SID_NUMBER_TWODEC );
+ rBindings.Invalidate( SID_NUMBER_TIME );
+ rBindings.Invalidate( SID_NUMBER_STANDARD );
+ rBindings.Invalidate( SID_NUMBER_THOUSANDS );
+}
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = "SELECT";
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+// SetCursor - Cursor, set, draw, update InputWin
+// or send reference
+// Optimising breaks the functionality
+
+void ScTabView::SetCursor( SCCOL nPosX, SCROW nPosY, bool bNew )
+{
+ SCCOL nOldX = aViewData.GetCurX();
+ SCROW nOldY = aViewData.GetCurY();
+
+ // DeactivateIP only for MarkListHasChanged
+
+ // FIXME: this is to limit the number of rows handled in the Online
+ // to 1000; this will be removed again when the performance
+ // bottlenecks are sorted out
+ if (comphelper::LibreOfficeKit::isActive())
+ nPosY = std::min(nPosY, MAXTILEDROW);
+
+ if ( !(nPosX != nOldX || nPosY != nOldY || bNew) )
+ {
+ HighlightOverlay();
+ return;
+ }
+
+ ScTabViewShell* pViewShell = aViewData.GetViewShell();
+ bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
+ if ( aViewData.HasEditView( aViewData.GetActivePart() ) && !bRefMode ) // 23259 or so
+ {
+ UpdateInputLine();
+ }
+
+ HideAllCursors();
+
+ aViewData.SetCurX( nPosX );
+ aViewData.SetCurY( nPosY );
+
+ ShowAllCursors();
+
+ HighlightOverlay();
+
+ CursorPosChanged();
+
+ OUString aCurrAddress = ScAddress(nPosX,nPosY,0).GetColRowString();
+ collectUIInformation({{"CELL", aCurrAddress}});
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (nPosX <= aViewData.GetMaxTiledCol() - 10 && nPosY <= aViewData.GetMaxTiledRow() - 25)
+ return;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
+ Size aOldSize(0, 0);
+ if (pModelObj)
+ aOldSize = pModelObj->getDocumentSize();
+
+ if (nPosX > aViewData.GetMaxTiledCol() - 10)
+ aViewData.SetMaxTiledCol(std::min<SCCOL>(std::max(nPosX, aViewData.GetMaxTiledCol()) + 10, rDoc.MaxCol()));
+
+ if (nPosY > aViewData.GetMaxTiledRow() - 25)
+ aViewData.SetMaxTiledRow(std::min<SCROW>(std::max(nPosY, aViewData.GetMaxTiledRow()) + 25, MAXTILEDROW));
+
+ Size aNewSize(0, 0);
+ if (pModelObj)
+ aNewSize = pModelObj->getDocumentSize();
+
+ if (!pDocSh)
+ return;
+
+ // New area extended to the right of the sheet after last column
+ // including overlapping area with aNewRowArea
+ tools::Rectangle aNewColArea(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight());
+ // New area extended to the bottom of the sheet after last row
+ // excluding overlapping area with aNewColArea
+ tools::Rectangle aNewRowArea(0, aOldSize.getHeight(), aOldSize.getWidth(), aNewSize.getHeight());
+
+ // Only invalidate if spreadsheet extended to the right
+ if (aNewColArea.getOpenWidth())
+ {
+ SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewColArea);
+ }
+
+ // Only invalidate if spreadsheet extended to the bottom
+ if (aNewRowArea.getOpenHeight())
+ {
+ SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewRowArea);
+ }
+
+ // Provide size in the payload, so clients don't have to
+ // call lok::Document::getDocumentSize().
+ std::stringstream ss;
+ ss << aNewSize.Width() << ", " << aNewSize.Height();
+ OString sSize( ss.str() );
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(aViewData.GetViewShell()->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(aViewData.GetViewShell(), sSize, pModel, false);
+}
+
+static bool lcl_IsRefDlgActive(SfxViewFrame& rViewFrm)
+{
+ ScModule* pScMod = SC_MOD();
+ if (!pScMod->IsRefDialogOpen())
+ return false;
+
+ auto nDlgId = pScMod->GetCurRefDlgId();
+ if (!rViewFrm.HasChildWindow(nDlgId))
+ return false;
+
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(nDlgId);
+ if (!pChild)
+ return false;
+
+ auto xDlgController = pChild->GetController();
+ if (!xDlgController || !xDlgController->getDialog()->get_visible())
+ return false;
+
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(xDlgController.get());
+ return pRefDlg && pRefDlg->IsRefInputMode();
+}
+
+void ScTabView::CheckSelectionTransfer()
+{
+ if ( !aViewData.IsActive() ) // only for active view
+ return;
+
+ ScModule* pScMod = SC_MOD();
+ ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
+ rtl::Reference<ScSelectionTransferObj> pNew = ScSelectionTransferObj::CreateFromView( this );
+ if ( !pNew )
+ return;
+
+ // create new selection
+
+ if (pOld)
+ pOld->ForgetView();
+
+ pScMod->SetSelectionTransfer( pNew.get() );
+
+ // tdf#124975/tdf#136242 changing the calc selection can trigger removal of the
+ // selection of an open RefDlg dialog, so don't inform the
+ // desktop clipboard of the changed selection if that dialog is open
+ if (!lcl_IsRefDlgActive(aViewData.GetViewShell()->GetViewFrame()))
+ pNew->CopyToPrimarySelection(); // may delete pOld
+
+ // Log the selection change
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (rMark.IsMarked())
+ {
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}});
+ }
+}
+
+// update input row / menus
+// CursorPosChanged calls SelectionChanged
+// SelectionChanged calls CellContentChanged
+
+void ScTabView::CellContentChanged()
+{
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ rBindings.Invalidate( SID_ATTR_SIZE ); // -> show error message
+ rBindings.Invalidate( SID_THESAURUS );
+ rBindings.Invalidate( SID_HYPERLINK_GETLINK );
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT );
+
+ InvalidateAttribs(); // attributes updates
+
+ aViewData.GetViewShell()->UpdateInputHandler();
+}
+
+void ScTabView::SetTabProtectionSymbol( SCTAB nTab, const bool bProtect )
+{
+ pTabControl->SetProtectionSymbol( static_cast<sal_uInt16>(nTab)+1, bProtect);
+}
+
+void ScTabView::SelectionChanged(bool bFromPaste)
+{
+ SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
+ uno::Reference<frame::XController> xController = rViewFrame.GetFrame().GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SelectionChanged();
+ }
+
+ UpdateAutoFillMark(bFromPaste); // also calls CheckSelectionTransfer
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ rBindings.Invalidate( SID_CURRENTCELL ); // -> Navigator
+ rBindings.Invalidate( SID_AUTO_FILTER ); // -> Menu
+ rBindings.Invalidate( FID_NOTE_VISIBLE );
+ rBindings.Invalidate( FID_SHOW_NOTE );
+ rBindings.Invalidate( FID_HIDE_NOTE );
+ rBindings.Invalidate( FID_SHOW_ALL_NOTES );
+ rBindings.Invalidate( FID_HIDE_ALL_NOTES );
+ rBindings.Invalidate( SID_TOGGLE_NOTES );
+ rBindings.Invalidate( SID_DELETE_NOTE );
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT );
+
+ // functions than may need to be disabled
+
+ rBindings.Invalidate( FID_INS_ROWBRK );
+ rBindings.Invalidate( FID_INS_COLBRK );
+ rBindings.Invalidate( FID_DEL_ROWBRK );
+ rBindings.Invalidate( FID_DEL_COLBRK );
+ rBindings.Invalidate( FID_MERGE_ON );
+ rBindings.Invalidate( FID_MERGE_OFF );
+ rBindings.Invalidate( FID_MERGE_TOGGLE );
+ rBindings.Invalidate( SID_AUTOFILTER_HIDE );
+ rBindings.Invalidate( SID_UNFILTER );
+ rBindings.Invalidate( SID_REIMPORT_DATA );
+ rBindings.Invalidate( SID_REFRESH_DBAREA );
+ rBindings.Invalidate( SID_OUTLINE_SHOW );
+ rBindings.Invalidate( SID_OUTLINE_HIDE );
+ rBindings.Invalidate( SID_OUTLINE_REMOVE );
+ rBindings.Invalidate( FID_FILL_TO_BOTTOM );
+ rBindings.Invalidate( FID_FILL_TO_RIGHT );
+ rBindings.Invalidate( FID_FILL_TO_TOP );
+ rBindings.Invalidate( FID_FILL_TO_LEFT );
+ rBindings.Invalidate( FID_FILL_SERIES );
+ rBindings.Invalidate( SID_SCENARIOS );
+ rBindings.Invalidate( SID_AUTOFORMAT );
+ rBindings.Invalidate( SID_OPENDLG_TABOP );
+ rBindings.Invalidate( SID_DATA_SELECT );
+
+ rBindings.Invalidate( SID_CUT );
+ rBindings.Invalidate( SID_COPY );
+ rBindings.Invalidate( SID_PASTE );
+ rBindings.Invalidate( SID_PASTE_SPECIAL );
+ rBindings.Invalidate( SID_PASTE_UNFORMATTED );
+
+ rBindings.Invalidate( FID_INS_ROW );
+ rBindings.Invalidate( FID_INS_COLUMN );
+ rBindings.Invalidate( FID_INS_ROWS_BEFORE );
+ rBindings.Invalidate( FID_INS_COLUMNS_BEFORE );
+ rBindings.Invalidate( FID_INS_ROWS_AFTER );
+ rBindings.Invalidate( FID_INS_COLUMNS_AFTER );
+ rBindings.Invalidate( FID_INS_CELL );
+ rBindings.Invalidate( FID_INS_CELLSDOWN );
+ rBindings.Invalidate( FID_INS_CELLSRIGHT );
+
+ rBindings.Invalidate( FID_CHG_COMMENT );
+
+ // only due to protect cell:
+
+ rBindings.Invalidate( SID_CELL_FORMAT_RESET );
+ rBindings.Invalidate( SID_DELETE );
+ rBindings.Invalidate( SID_DELETE_CONTENTS );
+ rBindings.Invalidate( FID_DELETE_CELL );
+ rBindings.Invalidate( FID_CELL_FORMAT );
+ rBindings.Invalidate( SID_ENABLE_HYPHENATION );
+ rBindings.Invalidate( SID_INSERT_POSTIT );
+ rBindings.Invalidate( SID_CHARMAP );
+ rBindings.Invalidate( SID_OPENDLG_FUNCTION );
+ rBindings.Invalidate( FID_VALIDATION );
+ rBindings.Invalidate( SID_EXTERNAL_SOURCE );
+ rBindings.Invalidate( SID_TEXT_TO_COLUMNS );
+ rBindings.Invalidate( SID_SORT_ASCENDING );
+ rBindings.Invalidate( SID_SORT_DESCENDING );
+ rBindings.Invalidate( SID_SELECT_UNPROTECTED_CELLS );
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccCursorChanged));
+
+ CellContentChanged();
+}
+
+void ScTabView::CursorPosChanged()
+{
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if ( !bRefMode ) // check that RefMode works when switching sheets
+ aViewData.GetDocShell()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
+
+ // Broadcast, so that other Views of the document also switch
+
+ ScDocument& rDocument = aViewData.GetDocument();
+ bool bDataPilot = rDocument.HasDataPilotAtPosition(aViewData.GetCurPos());
+ aViewData.GetViewShell()->SetPivotShell(bDataPilot);
+
+ if (!bDataPilot)
+ {
+ bool bSparkline = rDocument.HasSparkline(aViewData.GetCurPos());
+ aViewData.GetViewShell()->SetSparklineShell(bSparkline);
+ }
+
+ // UpdateInputHandler now in CellContentChanged
+
+ SelectionChanged();
+
+ aViewData.SetTabStartCol( SC_TABSTART_NONE );
+}
+
+namespace {
+
+Point calcHintWindowPosition(
+ const Point& rCellPos, const Size& rCellSize, const Size& rFrameWndSize, const Size& rHintWndSize)
+{
+ const tools::Long nMargin = 20;
+
+ tools::Long nMLeft = rCellPos.X();
+ tools::Long nMRight = rFrameWndSize.Width() - rCellPos.X() - rCellSize.Width();
+ tools::Long nMTop = rCellPos.Y();
+ tools::Long nMBottom = rFrameWndSize.Height() - rCellPos.Y() - rCellSize.Height();
+
+ // First, see if we can fit the entire hint window in the visible region.
+
+ if (nMRight - nMargin >= rHintWndSize.Width())
+ {
+ // Right margin is wide enough.
+ if (rFrameWndSize.Height() >= rHintWndSize.Height())
+ {
+ // The frame has enough height. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustX(rCellSize.Width() + nMargin );
+ if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
+ {
+ // Push the hint window up a bit to make it fit.
+ aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
+ }
+ return aPos;
+ }
+ }
+
+ if (nMBottom - nMargin >= rHintWndSize.Height())
+ {
+ // Bottom margin is high enough.
+ if (rFrameWndSize.Width() >= rHintWndSize.Width())
+ {
+ // The frame has enough width. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustY(rCellSize.Height() + nMargin );
+ if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
+ {
+ // Move the hint window to the left to make it fit.
+ aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
+ }
+ return aPos;
+ }
+ }
+
+ if (nMLeft - nMargin >= rHintWndSize.Width())
+ {
+ // Left margin is wide enough.
+ if (rFrameWndSize.Height() >= rHintWndSize.Height())
+ {
+ // The frame is high enough. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
+ if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
+ {
+ // Push the hint window up a bit to make it fit.
+ aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
+ }
+ return aPos;
+ }
+ }
+
+ if (nMTop - nMargin >= rHintWndSize.Height())
+ {
+ // Top margin is high enough.
+ if (rFrameWndSize.Width() >= rHintWndSize.Width())
+ {
+ // The frame is wide enough. Take it.
+ Point aPos = rCellPos;
+ aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
+ if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
+ {
+ // Move the hint window to the left to make it fit.
+ aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
+ }
+ return aPos;
+ }
+ }
+
+ // The popup doesn't fit in any direction in its entirety. Do our best.
+
+ if (nMRight - nMargin >= rHintWndSize.Width())
+ {
+ // Right margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustX(nMargin + rCellSize.Width() );
+ aPos.setY( 0 );
+ return aPos;
+ }
+
+ if (nMBottom - nMargin >= rHintWndSize.Height())
+ {
+ // Bottom margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustY(nMargin + rCellSize.Height() );
+ aPos.setX( 0 );
+ return aPos;
+ }
+
+ if (nMLeft - nMargin >= rHintWndSize.Width())
+ {
+ // Left margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
+ aPos.setY( 0 );
+ return aPos;
+ }
+
+ if (nMTop - nMargin >= rHintWndSize.Height())
+ {
+ // Top margin is good enough.
+ Point aPos = rCellPos;
+ aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
+ aPos.setX( 0 );
+ return aPos;
+ }
+
+ // None of the above. Hopeless. At least try not to cover the current
+ // cell.
+ Point aPos = rCellPos;
+ aPos.AdjustX(rCellSize.Width() );
+ return aPos;
+}
+
+}
+
+void ScTabView::TestHintWindow()
+{
+ // show input help window and list drop-down button for validity
+
+ mxInputHintOO.reset();
+
+ bool bListValButton = false;
+ ScAddress aListValPos;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ const SfxUInt32Item* pItem = rDoc.GetAttr( aViewData.GetCurX(),
+ aViewData.GetCurY(),
+ aViewData.GetTabNo(),
+ ATTR_VALIDDATA );
+ if ( pItem->GetValue() )
+ {
+ const ScValidationData* pData = rDoc.GetValidationEntry( pItem->GetValue() );
+ OSL_ENSURE(pData,"ValidationData not found");
+ OUString aTitle, aMessage;
+
+ if ( pData && pData->GetInput( aTitle, aMessage ) && !aMessage.isEmpty() )
+ {
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ ScGridWindow* pWin = pGridWin[eWhich].get();
+ SCCOL nCol = aViewData.GetCurX();
+ SCROW nRow = aViewData.GetCurY();
+ Point aPos = aViewData.GetScrPos( nCol, nRow, eWhich );
+ Size aWinSize = pWin->GetOutputSizePixel();
+ // cursor visible?
+ if ( nCol >= aViewData.GetPosX(WhichH(eWhich)) &&
+ nRow >= aViewData.GetPosY(WhichV(eWhich)) &&
+ aPos.X() < aWinSize.Width() && aPos.Y() < aWinSize.Height() )
+ {
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ Color aCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
+ // create HintWindow, determines its size by itself
+ ScOverlayHint* pOverlay = new ScOverlayHint(aTitle, aMessage, aCommentColor, pFrameWin->GetFont());
+
+ mxInputHintOO.reset(new sdr::overlay::OverlayObjectList);
+ mxInputHintOO->append(std::unique_ptr<sdr::overlay::OverlayObject>(pOverlay));
+
+ Size aHintWndSize = pOverlay->GetSizePixel();
+ tools::Long nCellSizeX = 0;
+ tools::Long nCellSizeY = 0;
+ aViewData.GetMergeSizePixel(nCol, nRow, nCellSizeX, nCellSizeY);
+
+ Point aHintPos = calcHintWindowPosition(
+ aPos, Size(nCellSizeX,nCellSizeY), aWinSize, aHintWndSize);
+
+ pOverlay->SetPos(pWin->PixelToLogic(aHintPos, pWin->GetDrawMapMode()), pWin->GetDrawMapMode());
+ for (VclPtr<ScGridWindow> & pWindow : pGridWin)
+ {
+ if (!pWindow)
+ continue;
+ if (!pWindow->IsVisible())
+ continue;
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = pWindow->getOverlayManager();
+ if (!xOverlayManager.is())
+ continue;
+ if (pWindow == pWin)
+ {
+ xOverlayManager->add(*pOverlay);
+ pWindow->updateLOKInputHelp(aTitle, aMessage);
+ }
+ else
+ {
+ //tdf#92530 if the help tip doesn't fit into its allocated area in a split window
+ //scenario, then because here we place it into the other split windows as well the
+ //missing portions will be displayed in the other split windows to form an apparent
+ //single tip, albeit "under" the split lines
+ Point aOtherPos(pWindow->ScreenToOutputPixel(pWin->OutputToScreenPixel(aHintPos)));
+ std::unique_ptr<ScOverlayHint> pOtherOverlay(new ScOverlayHint(aTitle, aMessage, aCommentColor, pFrameWin->GetFont()));
+ Point aFooPos(pWindow->PixelToLogic(aOtherPos, pWindow->GetDrawMapMode()));
+ pOtherOverlay->SetPos(aFooPos, pWindow->GetDrawMapMode());
+ xOverlayManager->add(*pOtherOverlay);
+ mxInputHintOO->append(std::move(pOtherOverlay));
+ }
+ }
+ }
+ }
+
+ // list drop-down button
+ if ( pData && pData->HasSelectionList() )
+ {
+ aListValPos.Set( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
+ bListValButton = true;
+ }
+ }
+
+ for (VclPtr<ScGridWindow> const & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ pWin->UpdateListValPos(bListValButton, aListValPos);
+ }
+}
+
+bool ScTabView::HasHintWindow() const { return mxInputHintOO != nullptr; }
+
+void ScTabView::RemoveHintWindow()
+{
+ mxInputHintOO.reset();
+}
+
+// find window that should not be over the cursor
+static weld::Window* lcl_GetCareWin(SfxViewFrame& rViewFrm)
+{
+ //! also spelling ??? (then set the member variables when calling)
+
+ // search & replace
+ if (rViewFrm.HasChildWindow(SID_SEARCH_DLG))
+ {
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(SID_SEARCH_DLG);
+ if (pChild)
+ {
+ auto xDlgController = pChild->GetController();
+ if (xDlgController && xDlgController->getDialog()->get_visible())
+ return xDlgController->getDialog();
+ }
+ }
+
+ // apply changes
+ if ( rViewFrm.HasChildWindow(FID_CHG_ACCEPT) )
+ {
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(FID_CHG_ACCEPT);
+ if (pChild)
+ {
+ auto xDlgController = pChild->GetController();
+ if (xDlgController && xDlgController->getDialog()->get_visible())
+ return xDlgController->getDialog();
+ }
+ }
+
+ return nullptr;
+}
+
+ // adjust screen with respect to cursor position
+
+void ScTabView::AlignToCursor( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
+ const ScSplitPos* pWhich )
+{
+ // now switch active part here
+
+ ScSplitPos eActive = aViewData.GetActivePart();
+ ScHSplitPos eActiveX = WhichH(eActive);
+ ScVSplitPos eActiveY = WhichV(eActive);
+ bool bHFix = (aViewData.GetHSplitMode() == SC_SPLIT_FIX);
+ bool bVFix = (aViewData.GetVSplitMode() == SC_SPLIT_FIX);
+ if (bHFix && eActiveX == SC_SPLIT_LEFT && nCurX >= aViewData.GetFixPosX())
+ {
+ ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT );
+ eActiveX = SC_SPLIT_RIGHT;
+ }
+ if (bVFix && eActiveY == SC_SPLIT_TOP && nCurY >= aViewData.GetFixPosY())
+ {
+ ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
+ eActiveY = SC_SPLIT_BOTTOM;
+ }
+
+ // actual align
+
+ if ( eMode != SC_FOLLOW_NONE )
+ {
+ ScSplitPos eAlign;
+ if (pWhich)
+ eAlign = *pWhich;
+ else
+ eAlign = aViewData.GetActivePart();
+ ScHSplitPos eAlignX = WhichH(eAlign);
+ ScVSplitPos eAlignY = WhichV(eAlign);
+
+ SCCOL nDeltaX = aViewData.GetPosX(eAlignX);
+ SCROW nDeltaY = aViewData.GetPosY(eAlignY);
+ SCCOL nSizeX = aViewData.VisibleCellsX(eAlignX);
+ SCROW nSizeY = aViewData.VisibleCellsY(eAlignY);
+
+ tools::Long nCellSizeX;
+ tools::Long nCellSizeY;
+ if ( nCurX >= 0 && nCurY >= 0 )
+ aViewData.GetMergeSizePixel( nCurX, nCurY, nCellSizeX, nCellSizeY );
+ else
+ nCellSizeX = nCellSizeY = 0;
+ Size aScrSize = aViewData.GetScrSize();
+
+ tools::Long nDenom;
+ if ( eMode == SC_FOLLOW_JUMP_END && nCurX > aViewData.GetRefStartX()
+ && nCurY > aViewData.GetRefStartY() )
+ nDenom = 1; // tdf#154271 Selected cell will be at the bottom corner
+ // to maximize the visible/usable area
+ else
+ nDenom = 2; // Selected cell will be at the center of the screen, so that
+ // it will be visible. This is useful for search results, etc.
+ tools::Long nSpaceX = ( aScrSize.Width() - nCellSizeX ) / nDenom;
+ tools::Long nSpaceY = ( aScrSize.Height() - nCellSizeY ) / nDenom;
+ // nSpaceY: desired start position of cell for FOLLOW_JUMP, modified if dialog interferes
+
+ bool bForceNew = false; // force new calculation of JUMP position (vertical only)
+
+ // VisibleCellsY == CellsAtY( GetPosY( eWhichY ), 1, eWhichY )
+
+ // when for instance a search dialog is open, don't put the cursor behind the dialog
+ // if possible, put the row with the cursor above or below the dialog
+ //! not if already completely visible
+
+ if ( eMode == SC_FOLLOW_JUMP || eMode == SC_FOLLOW_JUMP_END )
+ {
+ weld::Window* pCare = lcl_GetCareWin( aViewData.GetViewShell()->GetViewFrame() );
+ if (pCare)
+ {
+ bool bLimit = false;
+ tools::Rectangle aDlgPixel;
+ Size aWinSize;
+ vcl::Window* pWin = GetActiveWin();
+ weld::Window* pFrame = pWin ? pWin->GetFrameWeld() : nullptr;
+ int x, y, width, height;
+ if (pFrame && pCare->get_extents_relative_to(*pFrame, x, y, width, height))
+ {
+ aDlgPixel = tools::Rectangle(Point(x, y), Size(width, height));
+ aWinSize = pWin->GetOutputSizePixel();
+ // dos the dialog cover the GridWin?
+ if ( aDlgPixel.Right() >= 0 && aDlgPixel.Left() < aWinSize.Width() )
+ {
+ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX ||
+ nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
+ bLimit = true; // scroll anyway
+ else
+ {
+ // cursor is on the screen
+ Point aStart = aViewData.GetScrPos( nCurX, nCurY, eAlign );
+ tools::Long nCSX, nCSY;
+ aViewData.GetMergeSizePixel( nCurX, nCurY, nCSX, nCSY );
+ tools::Rectangle aCursor( aStart, Size( nCSX, nCSY ) );
+ if ( aCursor.Overlaps( aDlgPixel ) )
+ bLimit = true; // cell is covered by the dialog
+ }
+ }
+ }
+
+ if (bLimit)
+ {
+ bool bBottom = false;
+ tools::Long nTopSpace = aDlgPixel.Top();
+ tools::Long nBotSpace = aWinSize.Height() - aDlgPixel.Bottom();
+ if ( nBotSpace > 0 && nBotSpace > nTopSpace )
+ {
+ tools::Long nDlgBot = aDlgPixel.Bottom();
+ SCCOL nWPosX;
+ SCROW nWPosY;
+ aViewData.GetPosFromPixel( 0,nDlgBot, eAlign, nWPosX, nWPosY );
+ ++nWPosY; // below the last affected cell
+
+ SCROW nDiff = nWPosY - nDeltaY;
+ if ( nCurY >= nDiff ) // position can not be negative
+ {
+ nSpaceY = nDlgBot + ( nBotSpace - nCellSizeY ) / 2;
+ bBottom = true;
+ bForceNew = true;
+ }
+ }
+ if ( !bBottom && nTopSpace > 0 )
+ {
+ nSpaceY = ( nTopSpace - nCellSizeY ) / 2;
+ bForceNew = true;
+ }
+ }
+ }
+ }
+
+ SCCOL nNewDeltaX = nDeltaX;
+ SCROW nNewDeltaY = nDeltaY;
+ bool bDoLine = false;
+
+ switch (eMode)
+ {
+ case SC_FOLLOW_JUMP:
+ case SC_FOLLOW_JUMP_END:
+ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
+ {
+ nNewDeltaX = nCurX - aViewData.CellsAtX( nCurX, -1, eAlignX, static_cast<sal_uInt16>(nSpaceX) );
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY || bForceNew )
+ {
+ nNewDeltaY = nCurY - aViewData.CellsAtY( nCurY, -1, eAlignY, static_cast<sal_uInt16>(nSpaceY) );
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+ bDoLine = true;
+ break;
+
+ case SC_FOLLOW_LINE:
+ bDoLine = true;
+ break;
+
+ case SC_FOLLOW_FIX:
+ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
+ {
+ nNewDeltaX = nDeltaX + nCurX - aViewData.GetCurX();
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
+ {
+ nNewDeltaY = nDeltaY + nCurY - aViewData.GetCurY();
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+
+ // like old version of SC_FOLLOW_JUMP:
+
+ if ( nCurX < nNewDeltaX || nCurX >= nNewDeltaX+nSizeX )
+ {
+ nNewDeltaX = nCurX - (nSizeX / 2);
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ if ( nCurY < nNewDeltaY || nCurY >= nNewDeltaY+nSizeY )
+ {
+ nNewDeltaY = nCurY - (nSizeY / 2);
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+
+ bDoLine = true;
+ break;
+
+ case SC_FOLLOW_NONE:
+ break;
+ default:
+ OSL_FAIL("Wrong cursor mode");
+ break;
+ }
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ if (bDoLine)
+ {
+ while ( nCurX >= nNewDeltaX+nSizeX )
+ {
+ nNewDeltaX = nCurX-nSizeX+1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( nNewDeltaX < rDoc.MaxCol() && !rDoc.GetColWidth( nNewDeltaX, nTab ) )
+ ++nNewDeltaX;
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ }
+ while ( nCurY >= nNewDeltaY+nSizeY )
+ {
+ nNewDeltaY = nCurY-nSizeY+1;
+ SCTAB nTab = aViewData.GetTabNo();
+ while ( nNewDeltaY < rDoc.MaxRow() && !rDoc.GetRowHeight( nNewDeltaY, nTab ) )
+ ++nNewDeltaY;
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ }
+ if ( nCurX < nNewDeltaX )
+ nNewDeltaX = nCurX;
+ if ( nCurY < nNewDeltaY )
+ nNewDeltaY = nCurY;
+ }
+
+ if ( nNewDeltaX != nDeltaX )
+ nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
+ if (nNewDeltaX+nSizeX-1 > rDoc.MaxCol())
+ nNewDeltaX = rDoc.MaxCol()-nSizeX+1;
+ if (nNewDeltaX < 0)
+ nNewDeltaX = 0;
+
+ if ( nNewDeltaY != nDeltaY )
+ nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
+ if (nNewDeltaY+nSizeY-1 > rDoc.MaxRow())
+ nNewDeltaY = rDoc.MaxRow()-nSizeY+1;
+ if (nNewDeltaY < 0)
+ nNewDeltaY = 0;
+
+ if ( nNewDeltaX != nDeltaX )
+ ScrollX( nNewDeltaX - nDeltaX, eAlignX );
+ if ( nNewDeltaY != nDeltaY )
+ ScrollY( nNewDeltaY - nDeltaY, eAlignY );
+ }
+
+ // switch active part again
+
+ if (bHFix)
+ if (eActiveX == SC_SPLIT_RIGHT && nCurX < aViewData.GetFixPosX())
+ {
+ ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT );
+ eActiveX = SC_SPLIT_LEFT;
+ }
+ if (bVFix)
+ if (eActiveY == SC_SPLIT_BOTTOM && nCurY < aViewData.GetFixPosY())
+ {
+ ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
+ }
+}
+
+bool ScTabView::SelMouseButtonDown( const MouseEvent& rMEvt )
+{
+ bool bRet = false;
+
+ // #i3875# *Hack*
+ bool bMod1Locked = (aViewData.GetViewShell()->GetLockedModifiers() & KEY_MOD1) != 0;
+ aViewData.SetSelCtrlMouseClick( rMEvt.IsMod1() || bMod1Locked );
+
+ if ( pSelEngine )
+ {
+ bMoveIsShift = rMEvt.IsShift();
+ bRet = pSelEngine->SelMouseButtonDown( rMEvt );
+ bMoveIsShift = false;
+ }
+
+ aViewData.SetSelCtrlMouseClick( false ); // #i3875# *Hack*
+
+ return bRet;
+}
+
+ // MoveCursor - with adjustment of the view section
+
+void ScTabView::MoveCursorAbs( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
+ bool bShift, bool bControl, bool bKeepOld, bool bKeepSel )
+{
+ if (!bKeepOld)
+ aViewData.ResetOldCursor();
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ // #i123629#
+ if( aViewData.GetViewShell()->GetForceFocusOnCurCell() )
+ aViewData.GetViewShell()->SetForceFocusOnCurCell( !rDoc.ValidColRow(nCurX, nCurY) );
+
+ if (nCurX < 0) nCurX = 0;
+ if (nCurY < 0) nCurY = 0;
+ if (nCurX > rDoc.MaxCol()) nCurX = rDoc.MaxCol();
+ if (nCurY > rDoc.MaxRow()) nCurY = rDoc.MaxRow();
+
+ // FIXME: this is to limit the number of rows handled in the Online
+ // to 1000; this will be removed again when the performance
+ // bottlenecks are sorted out
+ if (comphelper::LibreOfficeKit::isActive())
+ nCurY = std::min(nCurY, MAXTILEDROW);
+
+ HideAllCursors();
+
+ // switch of active now in AlignToCursor
+
+ AlignToCursor( nCurX, nCurY, eMode );
+
+ if (bKeepSel)
+ {
+ SetCursor( nCurX, nCurY ); // keep selection
+
+ // If the cursor is in existing selection, it's a cursor movement by
+ // ENTER or TAB. If not, then it's a new selection during ADD
+ // selection mode.
+
+ const ScMarkData& rMark = aViewData.GetMarkData();
+ ScRangeList aSelList;
+ rMark.FillRangeListWithMarks(&aSelList, false);
+ if (!aSelList.Contains(ScRange(nCurX, nCurY, aViewData.GetTabNo())))
+ // Cursor not in existing selection. Start a new selection.
+ DoneBlockMode(true);
+ }
+ else
+ {
+ if (!bShift)
+ {
+ // Remove all marked data on cursor movement unless the Shift is
+ // locked or while editing a formula. It is cheaper to check for
+ // marks first and then formula mode.
+ ScMarkData& rMark = aViewData.GetMarkData();
+ bool bMarked = rMark.IsMarked() || rMark.IsMultiMarked();
+ if (bMarked && !SC_MOD()->IsFormulaMode())
+ {
+ rMark.ResetMark();
+ DoneBlockMode();
+ InitOwnBlockMode( ScRange( nCurX, nCurY, aViewData.GetTabNo()));
+ MarkDataChanged();
+ }
+ }
+
+ bool bSame = ( nCurX == aViewData.GetCurX() && nCurY == aViewData.GetCurY() );
+ bMoveIsShift = bShift;
+ pSelEngine->CursorPosChanging( bShift, bControl );
+ bMoveIsShift = false;
+ aFunctionSet.SetCursorAtCell( nCurX, nCurY, false );
+
+ // If the cursor has not been moved, the SelectionChanged for canceling the
+ // selection has to happen here individually:
+ if (bSame)
+ SelectionChanged();
+ }
+
+ ShowAllCursors();
+ TestHintWindow();
+}
+
+void ScTabView::MoveCursorRel( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
+ bool bShift, bool bKeepSel )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ bool bSkipProtected = false, bSkipUnprotected = false;
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if ( pProtect && pProtect->isProtected() )
+ {
+ bSkipProtected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
+ bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
+ }
+
+ if ( bSkipProtected && bSkipUnprotected )
+ return;
+
+ SCCOL nOldX;
+ SCROW nOldY;
+ SCCOL nCurX;
+ SCROW nCurY;
+ if ( aViewData.IsRefMode() )
+ {
+ nOldX = aViewData.GetRefEndX();
+ nOldY = aViewData.GetRefEndY();
+ nCurX = nOldX + nMovX;
+ nCurY = nOldY + nMovY;
+ }
+ else
+ {
+ nOldX = aViewData.GetCurX();
+ nOldY = aViewData.GetCurY();
+ nCurX = (nMovX != 0) ? nOldX+nMovX : aViewData.GetOldCurX();
+ nCurY = (nMovY != 0) ? nOldY+nMovY : aViewData.GetOldCurY();
+ }
+
+ if (nMovX < 0 && nOldX == 0)
+ { // trying to go left from 1st column
+ if (nMovY == 0) // done, because no vertical move is requested
+ return;
+ }
+ if (nMovY < 0 && nOldY == 0)
+ { // trying to go up from 1st row
+ if (nMovX == 0) // done, because no horizontal move is requested
+ return;
+ }
+
+ aViewData.ResetOldCursor();
+
+ if (nMovX != 0 && rDoc.ValidColRow(nCurX,nCurY))
+ SkipCursorHorizontal(nCurX, nCurY, nOldX, nMovX);
+
+ if (nMovY != 0 && rDoc.ValidColRow(nCurX,nCurY))
+ SkipCursorVertical(nCurX, nCurY, nOldY, nMovY);
+
+ MoveCursorAbs( nCurX, nCurY, eMode, bShift, false, true, bKeepSel );
+}
+
+void ScTabView::MoveCursorPage( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
+{
+ SCCOL nPageX;
+ SCROW nPageY;
+ GetPageMoveEndPosition(nMovX, nMovY, nPageX, nPageY);
+ MoveCursorRel( nPageX, nPageY, eMode, bShift, bKeepSel );
+}
+
+void ScTabView::MoveCursorArea( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel, bool bInteractiveByUser )
+{
+ SCCOL nNewX;
+ SCROW nNewY;
+ GetAreaMoveEndPosition(nMovX, nMovY, eMode, nNewX, nNewY, eMode, bInteractiveByUser);
+ MoveCursorRel(nNewX, nNewY, eMode, bShift, bKeepSel);
+}
+
+void ScTabView::MoveCursorEnd( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+
+ SCCOL nUsedX = 0;
+ SCROW nUsedY = 0;
+ if ( nMovX > 0 || nMovY > 0 )
+ rDoc.GetPrintArea( nTab, nUsedX, nUsedY ); // get end
+
+ if (nMovX<0)
+ nNewX=0;
+ else if (nMovX>0)
+ nNewX=nUsedX; // last used range
+
+ if (nMovY<0)
+ nNewY=0;
+ else if (nMovY>0)
+ nNewY=nUsedY;
+
+ aViewData.ResetOldCursor();
+ MoveCursorRel( nNewX-nCurX, nNewY-nCurY, eMode, bShift, bKeepSel );
+}
+
+void ScTabView::MoveCursorScreen( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ SCCOL nPosX = aViewData.GetPosX( WhichH(eWhich) );
+ SCROW nPosY = aViewData.GetPosY( WhichV(eWhich) );
+
+ SCCOL nAddX = aViewData.VisibleCellsX( WhichH(eWhich) );
+ if (nAddX != 0)
+ --nAddX;
+ SCROW nAddY = aViewData.VisibleCellsY( WhichV(eWhich) );
+ if (nAddY != 0)
+ --nAddY;
+
+ if (nMovX<0)
+ nNewX=nPosX;
+ else if (nMovX>0)
+ nNewX=nPosX+nAddX;
+
+ if (nMovY<0)
+ nNewY=nPosY;
+ else if (nMovY>0)
+ nNewY=nPosY+nAddY;
+
+ aViewData.SetOldCursor( nNewX,nNewY );
+ rDoc.SkipOverlapped(nNewX, nNewY, nTab);
+ MoveCursorAbs( nNewX, nNewY, eMode, bShift, false, true );
+}
+
+void ScTabView::MoveCursorEnter( bool bShift ) // bShift -> up/down
+{
+ const ScInputOptions& rOpt = SC_MOD()->GetInputOptions();
+ if (!rOpt.GetMoveSelection())
+ {
+ aViewData.UpdateInputHandler(true);
+ return;
+ }
+
+ SCCOL nMoveX = 0;
+ SCROW nMoveY = 0;
+ switch (static_cast<ScDirection>(rOpt.GetMoveDir()))
+ {
+ case DIR_BOTTOM:
+ nMoveY = bShift ? -1 : 1;
+ break;
+ case DIR_RIGHT:
+ nMoveX = bShift ? -1 : 1;
+ break;
+ case DIR_TOP:
+ nMoveY = bShift ? 1 : -1;
+ break;
+ case DIR_LEFT:
+ nMoveX = bShift ? 1 : -1;
+ break;
+ }
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (rMark.IsMarked() || rMark.IsMultiMarked())
+ {
+ rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, true, false, rMark );
+
+ MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false, true );
+
+ // update input line even if cursor was not moved
+ if ( nNewX == nCurX && nNewY == nCurY )
+ aViewData.UpdateInputHandler(true);
+ }
+ else
+ {
+ // After Tab and Enter back to the starting column again.
+ const SCCOL nTabStartCol = ((nMoveY != 0 && !nMoveX) ? aViewData.GetTabStartCol() : SC_TABSTART_NONE);
+ rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, false, true, rMark, nTabStartCol );
+
+ MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false);
+ }
+}
+
+bool ScTabView::MoveCursorKeyInput( const KeyEvent& rKeyEvent )
+{
+ const vcl::KeyCode& rKCode = rKeyEvent.GetKeyCode();
+
+ enum { MOD_NONE, MOD_CTRL, MOD_ALT, MOD_BOTH } eModifier =
+ rKCode.IsMod1() ?
+ (rKCode.IsMod2() ? MOD_BOTH : MOD_CTRL) :
+ (rKCode.IsMod2() ? MOD_ALT : MOD_NONE);
+
+ bool bSel = rKCode.IsShift();
+ sal_uInt16 nCode = rKCode.GetCode();
+
+ // CURSOR keys
+ SCCOL nDX = 0;
+ SCROW nDY = 0;
+ switch( nCode )
+ {
+ case KEY_LEFT: nDX = -1; break;
+ case KEY_RIGHT: nDX = 1; break;
+ case KEY_UP: nDY = -1; break;
+ case KEY_DOWN: nDY = 1; break;
+ }
+ if( nDX != 0 || nDY != 0 )
+ {
+ switch( eModifier )
+ {
+ case MOD_NONE: MoveCursorRel( nDX, nDY, SC_FOLLOW_LINE, bSel ); break;
+ case MOD_CTRL: MoveCursorArea( nDX, nDY, SC_FOLLOW_JUMP, bSel ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ // always true to suppress changes of col/row size (ALT+CURSOR)
+ return true;
+ }
+
+ // PAGEUP/PAGEDOWN
+ if( (nCode == KEY_PAGEUP) || (nCode == KEY_PAGEDOWN) )
+ {
+ nDX = (nCode == KEY_PAGEUP) ? -1 : 1;
+ switch( eModifier )
+ {
+ case MOD_NONE: MoveCursorPage( 0, static_cast<SCCOLROW>(nDX), SC_FOLLOW_FIX, bSel ); break;
+ case MOD_ALT: MoveCursorPage( nDX, 0, SC_FOLLOW_FIX, bSel ); break;
+ case MOD_CTRL: SelectNextTab( nDX, false ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return true;
+ }
+
+ // HOME/END
+ if( (nCode == KEY_HOME) || (nCode == KEY_END) )
+ {
+ nDX = (nCode == KEY_HOME) ? -1 : 1;
+ ScFollowMode eMode = (nCode == KEY_HOME) ? SC_FOLLOW_LINE : SC_FOLLOW_JUMP_END;
+ switch( eModifier )
+ {
+ case MOD_NONE: MoveCursorEnd( nDX, 0, eMode, bSel ); break;
+ case MOD_CTRL: MoveCursorEnd( nDX, static_cast<SCCOLROW>(nDX), eMode, bSel ); break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+ // next/previous unprotected cell
+void ScTabView::FindNextUnprot( bool bShift, bool bInSelection )
+{
+ short nMove = bShift ? -1 : 1;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ bool bMarked = bInSelection && (rMark.IsMarked() || rMark.IsMultiMarked());
+
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ SCCOL nNewX = nCurX;
+ SCROW nNewY = nCurY;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, bMarked, true, rMark );
+
+ SCCOL nTabCol = aViewData.GetTabStartCol();
+ if ( nTabCol == SC_TABSTART_NONE )
+ nTabCol = nCurX; // back to this column after Enter
+
+ MoveCursorRel( nNewX-nCurX, nNewY-nCurY, SC_FOLLOW_LINE, false, true );
+
+ // TabCol is reset in MoveCursorRel...
+ aViewData.SetTabStartCol( nTabCol );
+}
+
+void ScTabView::MarkColumns()
+{
+ SCCOL nStartCol;
+ SCCOL nEndCol;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (rMark.IsMarked())
+ {
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ nStartCol = aMarkRange.aStart.Col();
+ nEndCol = aMarkRange.aEnd.Col();
+ }
+ else
+ {
+ SCROW nDummy;
+ aViewData.GetMoveCursor( nStartCol, nDummy );
+ nEndCol=nStartCol;
+ }
+
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ DoneBlockMode();
+ InitBlockMode( nStartCol,0, nTab );
+ MarkCursor( nEndCol, rDoc.MaxRow(), nTab );
+ SelectionChanged();
+}
+
+void ScTabView::MarkRows()
+{
+ SCROW nStartRow;
+ SCROW nEndRow;
+
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (rMark.IsMarked())
+ {
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ nStartRow = aMarkRange.aStart.Row();
+ nEndRow = aMarkRange.aEnd.Row();
+ }
+ else
+ {
+ SCCOL nDummy;
+ aViewData.GetMoveCursor( nDummy, nStartRow );
+ nEndRow=nStartRow;
+ }
+
+ SCTAB nTab = aViewData.GetTabNo();
+ ScDocument& rDoc = aViewData.GetDocument();
+ DoneBlockMode();
+ InitBlockMode( 0,nStartRow, nTab );
+ MarkCursor( rDoc.MaxCol(), nEndRow, nTab );
+ SelectionChanged();
+}
+
+
+void ScTabView::MarkColumns(SCCOL nCol, sal_Int16 nModifier)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nStartCol = nCol;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
+ bMoveIsShift = true;
+
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ DoneRefMode( nModifier != 0 );
+ InitRefMode( nCol, 0, nTab, SC_REFTYPE_REF );
+ UpdateRef( nCol, rDoc.MaxRow(), nTab );
+ bMoveIsShift = false;
+ }
+ else
+ {
+ DoneBlockMode( nModifier != 0 );
+ InitBlockMode( nStartCol, 0, nTab, true, true);
+ MarkCursor( nCol, rDoc.MaxRow(), nTab );
+ bMoveIsShift = false;
+ SetCursor( nCol, 0 );
+ SelectionChanged();
+ }
+}
+
+void ScTabView::MarkRows(SCROW nRow, sal_Int16 nModifier)
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCROW nStartRow = nRow;
+ SCTAB nTab = aViewData.GetTabNo();
+
+ if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
+ bMoveIsShift = true;
+
+ if ( SC_MOD()->IsFormulaMode() )
+ {
+ DoneRefMode( nModifier != 0 );
+ InitRefMode( 0, nRow, nTab, SC_REFTYPE_REF );
+ UpdateRef( rDoc.MaxCol(), nRow, nTab );
+ bMoveIsShift = false;
+ }
+ else
+ {
+ DoneBlockMode( nModifier != 0 );
+ InitBlockMode( 0, nStartRow, nTab, true, false, true );
+ MarkCursor( rDoc.MaxCol(), nRow, nTab );
+ bMoveIsShift = false;
+ SetCursor( 0, nRow );
+ SelectionChanged();
+ }
+}
+
+void ScTabView::HighlightOverlay()
+{
+ if (!officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get())
+ {
+ aViewData.GetHighlightData().ResetMark();
+ UpdateHighlightOverlay();
+ return;
+ }
+
+ ScAddress aCell = GetViewData().GetCurPos();
+ SCROW nRow = aCell.Row();
+ SCCOL nCol = aCell.Col();
+
+ bool nModifier = false; // modifier key pressed?
+ DoneBlockModeHighlight( nModifier );
+ InitBlockModeHighlight( nCol, 0, aCell.Tab(), true, false);
+ nModifier = true;
+ DoneBlockModeHighlight( nModifier );
+ InitBlockModeHighlight( 0, nRow, aCell.Tab(), false, true );
+}
+
+void ScTabView::MarkDataArea( bool bIncludeCursor )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ SCCOL nStartCol = aViewData.GetCurX();
+ SCROW nStartRow = aViewData.GetCurY();
+ SCCOL nEndCol = nStartCol;
+ SCROW nEndRow = nStartRow;
+
+ rDoc.GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, bIncludeCursor, false );
+
+ HideAllCursors();
+ DoneBlockMode();
+ InitBlockMode( nStartCol, nStartRow, nTab );
+ MarkCursor( nEndCol, nEndRow, nTab );
+ ShowAllCursors();
+
+ SelectionChanged();
+}
+
+void ScTabView::MarkMatrixFormula()
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScAddress aCursor( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
+ ScRange aMatrix;
+ if ( rDoc.GetMatrixFormulaRange( aCursor, aMatrix ) )
+ {
+ MarkRange( aMatrix, false ); // cursor is already within the range
+ }
+}
+
+void ScTabView::MarkRange( const ScRange& rRange, bool bSetCursor, bool bContinue )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = rRange.aStart.Tab();
+ SetTabNo( nTab );
+
+ HideAllCursors();
+ DoneBlockMode( bContinue ); // bContinue==true -> clear old mark
+ if (bSetCursor) // if Cursor is set, also always align
+ {
+ SCCOL nAlignX = rRange.aStart.Col();
+ SCROW nAlignY = rRange.aStart.Row();
+ bool bCol = ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() ) && !aViewData.GetDocument().IsInVBAMode();
+ bool bRow = ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() );
+ if ( bCol )
+ nAlignX = aViewData.GetPosX(WhichH(aViewData.GetActivePart()));
+ if ( bRow )
+ nAlignY = aViewData.GetPosY(WhichV(aViewData.GetActivePart()));
+ AlignToCursor( nAlignX, nAlignY, SC_FOLLOW_JUMP );
+ }
+ InitBlockMode( rRange.aStart.Col(), rRange.aStart.Row(), nTab );
+ MarkCursor( rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
+ if (bSetCursor)
+ {
+ SCCOL nPosX = rRange.aStart.Col();
+ SCROW nPosY = rRange.aStart.Row();
+ rDoc.SkipOverlapped(nPosX, nPosY, nTab);
+
+ aViewData.ResetOldCursor();
+ SetCursor( nPosX, nPosY );
+ }
+ ShowAllCursors();
+
+ SelectionChanged();
+}
+
+void ScTabView::Unmark()
+{
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ SCCOL nCurX;
+ SCROW nCurY;
+ aViewData.GetMoveCursor( nCurX,nCurY );
+ MoveCursorAbs( nCurX, nCurY, SC_FOLLOW_NONE, false, false );
+
+ SelectionChanged();
+ }
+}
+
+void ScTabView::SetMarkData( const ScMarkData& rNew )
+{
+ DoneBlockMode();
+ InitOwnBlockMode( rNew.GetMarkArea());
+ aViewData.GetMarkData() = rNew;
+
+ MarkDataChanged();
+}
+
+void ScTabView::MarkDataChanged()
+{
+ // has to be called after making direct changes to mark data (not via MarkCursor etc)
+
+ UpdateSelectionOverlay();
+}
+
+void ScTabView::SelectNextTab( short nDir, bool bExtendSelection )
+{
+ if (!nDir)
+ return;
+ OSL_ENSURE( nDir==-1 || nDir==1, "SelectNextTab: invalid value");
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ SCTAB nNextTab = nTab;
+ if (nDir < 0)
+ {
+ do
+ {
+ --nNextTab;
+ if (nNextTab < 0)
+ nNextTab = rDoc.GetTableCount();
+ if (rDoc.IsVisible(nNextTab))
+ break;
+ } while (nNextTab != nTab);
+ }
+ if (nDir > 0)
+ {
+ SCTAB nCount = rDoc.GetTableCount();
+ do
+ {
+ ++nNextTab;
+ if (nNextTab >= nCount)
+ nNextTab = 0;
+ if (rDoc.IsVisible(nNextTab))
+ break;
+ } while (nNextTab != nTab);
+ }
+ if (nNextTab == nTab)
+ return;
+
+ SetTabNo(nNextTab, false, bExtendSelection);
+ PaintExtras();
+}
+
+void ScTabView::SelectTabPage( const sal_uInt16 nTab )
+{
+ pTabControl->SwitchToPageId( nTab );
+}
+
+// SetTabNo - set the displayed sheet
+
+void ScTabView::SetTabNo( SCTAB nTab, bool bNew, bool bExtendSelection, bool bSameTabButMoved )
+{
+ if ( !ValidTab(nTab) )
+ {
+ OSL_FAIL("SetTabNo: invalid sheet");
+ return;
+ }
+
+ if (!bNew && nTab == aViewData.GetTabNo())
+ return;
+
+ // FormShell would like to be informed before the switch
+ FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell();
+ if (pFormSh)
+ {
+ bool bAllowed = pFormSh->PrepareClose();
+ if (!bAllowed)
+ {
+ //! error message? or does FormShell do it?
+ //! return error flag and cancel actions
+
+ return; // FormShell says that it can not be switched
+ }
+ }
+
+ // not InputEnterHandler due to reference input
+
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ rDoc.MakeTable( nTab );
+
+ // Update pending row heights before switching the sheet, so Reschedule from the progress bar
+ // doesn't paint the new sheet with old heights
+ aViewData.GetDocShell()->UpdatePendingRowHeights( nTab );
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nOldPos = nTab;
+ while (!rDoc.IsVisible(nTab)) // search for next visible
+ {
+ bool bUp = (nTab>=nOldPos);
+ if (bUp)
+ {
+ ++nTab;
+ if (nTab>=nTabCount)
+ {
+ nTab = nOldPos;
+ bUp = false;
+ }
+ }
+
+ if (!bUp)
+ {
+ if (nTab != 0)
+ --nTab;
+ else
+ {
+ OSL_FAIL("no visible sheets");
+ rDoc.SetVisible( 0, true );
+ }
+ }
+ }
+
+ // #i71490# Deselect drawing objects before changing the sheet number in view data,
+ // so the handling of notes still has the sheet selected on which the notes are.
+ DrawDeselectAll();
+
+ ScModule* pScMod = SC_MOD();
+ bool bRefMode = pScMod->IsFormulaMode();
+ if ( !bRefMode ) // query, so that RefMode works when switching sheet
+ {
+ DoneBlockMode();
+ pSelEngine->Reset(); // reset all flags, including locked modifiers
+ aViewData.SetRefTabNo( nTab );
+ }
+
+ ScSplitPos eOldActive = aViewData.GetActivePart(); // before switching
+ bool bFocus = pGridWin[eOldActive] && pGridWin[eOldActive]->HasFocus();
+
+ aViewData.SetTabNo( nTab );
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->setTabNo( nTab );
+ // UpdateShow before SetCursor, so that UpdateAutoFillMark finds the correct
+ // window (is called from SetCursor)
+ UpdateShow();
+ aViewData.GetView()->TestHintWindow();
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+ ScMarkData& rMark = aViewData.GetMarkData();
+
+ bool bAllSelected = true;
+ for (SCTAB nSelTab = 0; nSelTab < nTabCount; ++nSelTab)
+ {
+ if (!rDoc.IsVisible(nSelTab) || rMark.GetTableSelect(nSelTab))
+ {
+ if (nTab == nSelTab)
+ // This tab is already in selection. Keep the current
+ // selection.
+ bExtendSelection = true;
+ }
+ else
+ {
+ bAllSelected = false;
+ if (bExtendSelection)
+ // We got what we need. No need to stay in the loop.
+ break;
+ }
+ }
+ if (bAllSelected && !bNew)
+ // #i6327# if all tables are selected, a selection event (#i6330#) will deselect all
+ // (not if called with bNew to update settings)
+ bExtendSelection = false;
+
+ if (bExtendSelection)
+ rMark.SelectTable( nTab, true );
+ else
+ {
+ rMark.SelectOneTable( nTab );
+ rBindings.Invalidate( FID_FILL_TAB );
+ rBindings.Invalidate( FID_TAB_DESELECTALL );
+ }
+
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ // recalc zoom-dependent values (before TabChanged, before UpdateEditViewPos)
+ RefreshZoom();
+ UpdateVarZoom();
+
+ if ( bRefMode ) // hide EditView if necessary (after aViewData.SetTabNo !)
+ {
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ pWin->UpdateEditViewPos();
+ }
+ }
+
+ TabChanged(bSameTabButMoved); // DrawView
+ collectUIInformation({{"TABLE", OUString::number(nTab)}});
+ UpdateVisibleRange();
+
+ aViewData.GetViewShell()->WindowChanged(); // if the active window has changed
+ aViewData.ResetOldCursor();
+ SetCursor( aViewData.GetCurX(), aViewData.GetCurY(), true );
+
+ if ( !bUnoRefDialog )
+ aViewData.GetViewShell()->DisconnectAllClients(); // important for floating frames
+ else
+ {
+ // hide / show inplace client
+ ScClient* pClient = static_cast<ScClient*>(aViewData.GetViewShell()->GetIPClient());
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ {
+ tools::Rectangle aObjArea = pClient->GetObjArea();
+ if ( nTab == aViewData.GetRefTabNo() )
+ {
+ // move to its original position
+
+ SdrOle2Obj* pDrawObj = pClient->GetDrawObj();
+ if ( pDrawObj )
+ {
+ tools::Rectangle aRect = pDrawObj->GetLogicRect();
+ MapMode aMapMode( MapUnit::Map100thMM );
+ Size aOleSize = pDrawObj->GetOrigObjSize( &aMapMode );
+ aRect.SetSize( aOleSize );
+ aObjArea = aRect;
+ }
+ }
+ else
+ {
+ // move to an invisible position
+
+ aObjArea.SetPos( Point( 0, -2*aObjArea.GetHeight() ) );
+ }
+ pClient->SetObjArea( aObjArea );
+ }
+ }
+
+ if ( bFocus && aViewData.GetActivePart() != eOldActive && !bRefMode )
+ ActiveGrabFocus(); // grab focus to the pane that's active now
+
+ // freeze
+
+ bool bResize = false;
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixX())
+ bResize = true;
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
+ if (aViewData.UpdateFixY())
+ bResize = true;
+ if (bResize)
+ RepeatResize();
+ InvalidateSplit();
+
+ if ( aViewData.IsPagebreakMode() )
+ UpdatePageBreakData(); //! asynchronously ??
+
+ // Form Layer must know the visible area of the new sheet
+ // that is why MapMode must already be correct here
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin)
+ pWin->SetMapMode(pWin->GetDrawMapMode());
+ }
+ SetNewVisArea();
+
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ PaintExtras();
+
+ DoResize( aBorderPos, aFrameSize );
+ rBindings.Invalidate( SID_DELETE_PRINTAREA ); // Menu
+ rBindings.Invalidate( FID_DEL_MANUALBREAKS );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ rBindings.Invalidate( SID_STATUS_DOCPOS ); // Status bar
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT ); // Status bar
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE ); // Status bar
+ rBindings.Invalidate( SID_CURRENTTAB ); // Navigator
+ rBindings.Invalidate( SID_STYLE_FAMILY2 ); // Designer
+ rBindings.Invalidate( SID_STYLE_FAMILY4 ); // Designer
+ rBindings.Invalidate( SID_TABLES_COUNT );
+
+ if (pScMod->IsRefDialogOpen())
+ {
+ sal_uInt16 nCurRefDlgId=pScMod->GetCurRefDlgId();
+ SfxViewFrame& rViewFrm = aViewData.GetViewShell()->GetViewFrame();
+ SfxChildWindow* pChildWnd = rViewFrm.GetChildWindow( nCurRefDlgId );
+ if (pChildWnd)
+ {
+ if (pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
+ if (pRefDlg)
+ pRefDlg->ViewShellChanged();
+ }
+ }
+ }
+
+ OnLibreOfficeKitTabChanged();
+}
+
+void ScTabView::AddWindowToForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ aExtraEditViewManager.Add(pViewShell, eWhich);
+}
+
+void ScTabView::RemoveWindowFromForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich)
+{
+ aExtraEditViewManager.Remove(pViewShell, eWhich);
+}
+
+void ScTabView::OnLibreOfficeKitTabChanged()
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScTabViewShell* pThisViewShell = aViewData.GetViewShell();
+ SCTAB nThisTabNo = pThisViewShell->GetViewData().GetTabNo();
+ auto lTabSwitch = [pThisViewShell, nThisTabNo] (ScTabViewShell* pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ SCTAB nOtherTabNo = rOtherViewData.GetTabNo();
+ if (nThisTabNo == nOtherTabNo)
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ if (rOtherViewData.HasEditView(ScSplitPos(i)))
+ {
+ pThisViewShell->AddWindowToForeignEditView(pOtherViewShell, ScSplitPos(i));
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ if (rOtherViewData.HasEditView(ScSplitPos(i)))
+ {
+ pThisViewShell->RemoveWindowFromForeignEditView(pOtherViewShell, ScSplitPos(i));
+ }
+ }
+ }
+ };
+
+ SfxLokHelper::forEachOtherView(pThisViewShell, lTabSwitch);
+
+ pThisViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_HEADER, "all"_ostr);
+
+ if (pThisViewShell->GetInputHandler())
+ pThisViewShell->GetInputHandler()->UpdateLokReferenceMarks();
+}
+
+// paint functions - only for this View
+
+void ScTabView::MakeEditView( ScEditEngineDefaulter* pEngine, SCCOL nCol, SCROW nRow )
+{
+ DrawDeselectAll();
+
+ if (pDrawView)
+ DrawEnableAnim( false );
+
+ EditView* pSpellingView = aViewData.GetSpellingView();
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible() && !aViewData.HasEditView(ScSplitPos(i)))
+ {
+ ScHSplitPos eHWhich = WhichH( static_cast<ScSplitPos>(i) );
+ ScVSplitPos eVWhich = WhichV( static_cast<ScSplitPos>(i) );
+ SCCOL nScrX = aViewData.GetPosX( eHWhich );
+ SCROW nScrY = aViewData.GetPosY( eVWhich );
+
+ bool bPosVisible =
+ ( nCol >= nScrX && nCol <= nScrX + aViewData.VisibleCellsX(eHWhich) - 1 &&
+ nRow >= nScrY && nRow <= nScrY + aViewData.VisibleCellsY(eVWhich) - 1 );
+
+ // for the active part, create edit view even if outside the visible area,
+ // so input isn't lost (and the edit view may be scrolled into the visible area)
+
+ // #i26433# during spelling, the spelling view must be active
+ if ( bPosVisible || aViewData.GetActivePart() == static_cast<ScSplitPos>(i) ||
+ ( pSpellingView && aViewData.GetEditView(static_cast<ScSplitPos>(i)) == pSpellingView ) )
+ {
+ pGridWin[i]->HideCursor();
+
+ pGridWin[i]->DeleteCursorOverlay();
+ pGridWin[i]->DeleteAutoFillOverlay();
+ pGridWin[i]->DeleteCopySourceOverlay();
+
+ // flush OverlayManager before changing MapMode to text edit
+ pGridWin[i]->flushOverlayManager();
+
+ // MapMode must be set after HideCursor
+ pGridWin[i]->SetMapMode(aViewData.GetLogicMode());
+
+ aViewData.SetEditEngine( static_cast<ScSplitPos>(i), pEngine, pGridWin[i], nCol, nRow );
+
+ if ( !bPosVisible )
+ {
+ // move the edit view area to the real (possibly negative) position,
+ // or hide if completely above or left of the window
+ pGridWin[i]->UpdateEditViewPos();
+ }
+ }
+ }
+ }
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccEnterEditMode));
+}
+
+void ScTabView::UpdateEditView()
+{
+ if (aViewData.GetTabNo() != aViewData.GetRefTabNo() && SC_MOD()->IsFormulaMode())
+ return;
+
+ ScSplitPos eActive = aViewData.GetActivePart();
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ ScSplitPos eCurrent = ScSplitPos(i);
+ if (aViewData.HasEditView(eCurrent))
+ {
+ EditView* pEditView = aViewData.GetEditView(eCurrent);
+
+ tools::Long nRefTabNo = GetViewData().GetRefTabNo();
+ tools::Long nX = GetViewData().GetCurXForTab(nRefTabNo);
+ tools::Long nY = GetViewData().GetCurYForTab(nRefTabNo);
+
+ aViewData.SetEditEngine(eCurrent,
+ static_cast<ScEditEngineDefaulter*>(pEditView->GetEditEngine()),
+ pGridWin[i], nX, nY );
+ if (eCurrent == eActive)
+ pEditView->ShowCursor( false );
+ }
+ }
+}
+
+void ScTabView::KillEditView( bool bNoPaint )
+{
+ SCCOL nCol1 = aViewData.GetEditStartCol();
+ SCROW nRow1 = aViewData.GetEditStartRow();
+ SCCOL nCol2 = aViewData.GetEditEndCol();
+ SCROW nRow2 = aViewData.GetEditEndRow();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bPaint[4];
+ bool bNotifyAcc = false;
+ tools::Rectangle aRectangle[4];
+
+ bool bExtended = nRow1 != nRow2; // column is painted to the end anyway
+
+ bool bAtCursor = nCol1 <= aViewData.GetCurX() &&
+ nCol2 >= aViewData.GetCurX() &&
+ nRow1 == aViewData.GetCurY();
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ bPaint[i] = aViewData.HasEditView( static_cast<ScSplitPos>(i) );
+ if (bPaint[i])
+ {
+ bNotifyAcc = true;
+
+ EditView* pView = aViewData.GetEditView( static_cast<ScSplitPos>(i) );
+ aRectangle[i] = pView->GetInvalidateRect();
+ }
+ }
+
+ // notify accessibility before all things happen
+ if (bNotifyAcc && aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccLeaveEditMode));
+
+ aViewData.ResetEditView();
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && bPaint[i] && pGridWin[i]->IsVisible())
+ {
+ pGridWin[i]->ShowCursor();
+
+ pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ const tools::Rectangle& rInvRect = aRectangle[i];
+ pGridWin[i]->LogicInvalidatePart(&rInvRect, nTab);
+
+ // invalidate other views
+ auto lInvalidateWindows =
+ [nTab, &rInvRect] (ScTabView* pTabView)
+ {
+ for (VclPtr<ScGridWindow> const & pWin: pTabView->pGridWin)
+ {
+ if (pWin)
+ pWin->LogicInvalidatePart(&rInvRect, nTab);
+ }
+ };
+
+ SfxLokHelper::forEachOtherView(GetViewData().GetViewShell(), lInvalidateWindows);
+ }
+ // #i73567# the cell still has to be repainted
+ else if (bExtended || ( bAtCursor && !bNoPaint ))
+ {
+ pGridWin[i]->Draw( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::All );
+ pGridWin[i]->UpdateSelectionOverlay();
+ }
+ }
+ }
+
+ if (pDrawView)
+ DrawEnableAnim( true );
+
+ // GrabFocus always when this View is active and
+ // when the input row has the focus
+
+ bool bGrabFocus = false;
+ if (aViewData.IsActive())
+ {
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl )
+ {
+ ScInputWindow* pInputWin = pInputHdl->GetInputWindow();
+ if (pInputWin && pInputWin->IsInputActive())
+ bGrabFocus = true;
+ }
+ }
+
+ if (bGrabFocus)
+ {
+// should be done like this, so that Sfx notice it, but it does not work:
+//! aViewData.GetViewShell()->GetViewFrame().GetWindow().GrabFocus();
+// therefore first like this:
+ GetActiveWin()->GrabFocus();
+ }
+
+ // cursor query only after GrabFocus
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ {
+ vcl::Cursor* pCur = pGridWin[i]->GetCursor();
+ if (pCur && pCur->IsVisible())
+ pCur->Hide();
+
+ if (bPaint[i])
+ {
+ pGridWin[i]->UpdateCursorOverlay();
+ pGridWin[i]->UpdateAutoFillOverlay();
+ }
+ }
+ }
+}
+
+void ScTabView::UpdateFormulas(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow)
+{
+ if ( aViewData.GetDocument().IsAutoCalcShellDisabled() )
+ return;
+
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->UpdateFormulas(nStartCol, nStartRow, nEndCol, nEndRow);
+ }
+
+ if ( aViewData.IsPagebreakMode() )
+ UpdatePageBreakData(); //! asynchronous
+
+ UpdateHeaderWidth();
+
+ // if in edit mode, adjust edit view area because widths/heights may have changed
+ if ( aViewData.HasEditView( aViewData.GetActivePart() ) )
+ UpdateEditView();
+}
+
+// PaintArea - repaint block
+
+void ScTabView::PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScUpdateMode eMode )
+{
+ SCCOL nCol1;
+ SCROW nRow1;
+ SCCOL nCol2;
+ SCROW nRow2;
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+
+ for (size_t i = 0; i < 4; ++i)
+ {
+ if (!pGridWin[i] || !pGridWin[i]->IsVisible())
+ continue;
+
+ ScHSplitPos eHWhich = WhichH( static_cast<ScSplitPos>(i) );
+ ScVSplitPos eVWhich = WhichV( static_cast<ScSplitPos>(i) );
+ bool bOut = false;
+
+ nCol1 = nStartCol;
+ nRow1 = nStartRow;
+ nCol2 = nEndCol;
+ nRow2 = nEndRow;
+
+ SCCOL nLastX = 0;
+ SCROW nLastY = 0;
+
+ if (bIsTiledRendering)
+ {
+ nLastX = aViewData.GetMaxTiledCol();
+ nLastY = aViewData.GetMaxTiledRow();
+ }
+ else
+ {
+
+ SCCOL nScrX = aViewData.GetPosX( eHWhich );
+ SCROW nScrY = aViewData.GetPosY( eVWhich );
+
+ if (nCol1 < nScrX)
+ nCol1 = nScrX;
+ if (nCol2 < nScrX)
+ {
+ if ( eMode == ScUpdateMode::All ) // for UPDATE_ALL, paint anyway
+ nCol2 = nScrX; // (because of extending strings to the right)
+ else
+ bOut = true; // completely outside the window
+ }
+ if (nRow1 < nScrY)
+ nRow1 = nScrY;
+ if (nRow2 < nScrY)
+ bOut = true;
+
+ nLastX = nScrX + aViewData.VisibleCellsX( eHWhich ) + 1;
+ nLastY = nScrY + aViewData.VisibleCellsY( eVWhich ) + 1;
+ }
+
+ if (nCol1 > nLastX)
+ bOut = true;
+ if (nCol2 > nLastX)
+ nCol2 = nLastX;
+ if (nRow1 > nLastY)
+ bOut = true;
+ if (nRow2 > nLastY)
+ nRow2 = nLastY;
+
+ if (bOut)
+ continue;
+
+ bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
+ tools::Long nLayoutSign = (!bIsTiledRendering && bLayoutRTL) ? -1 : 1;
+
+ Point aStart = aViewData.GetScrPos( nCol1, nRow1, static_cast<ScSplitPos>(i) );
+ Point aEnd = aViewData.GetScrPos( nCol2+1, nRow2+1, static_cast<ScSplitPos>(i) );
+
+ if ( eMode == ScUpdateMode::All )
+ {
+ if (bIsTiledRendering)
+ {
+ // When a cell content is deleted we have no clue about
+ // the width of the embedded text.
+ // Anyway, clients will ask only for tiles that overlaps
+ // the visible area.
+ // Remember that wsd expects int and that aEnd.X() is
+ // in pixels and will be converted in twips, before performing
+ // the lok callback, so we need to avoid that an overflow occurs.
+ aEnd.setX( std::numeric_limits<int>::max() / 1000 );
+ }
+ else
+ {
+ aEnd.setX( bLayoutRTL ? 0 : pGridWin[i]->GetOutputSizePixel().Width() );
+ }
+ }
+ aEnd.AdjustX( -nLayoutSign );
+ aEnd.AdjustY( -1 );
+
+ // #i85232# include area below cells (could be done in GetScrPos?)
+ if ( eMode == ScUpdateMode::All && nRow2 >= rDoc.MaxRow() && !bIsTiledRendering )
+ aEnd.setY( pGridWin[i]->GetOutputSizePixel().Height() );
+
+ aStart.AdjustX( -nLayoutSign ); // include change marks
+ aStart.AdjustY( -1 );
+
+ bool bMarkClipped = SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).bIsVisible;
+ if (bMarkClipped)
+ {
+ // ScColumn::IsEmptyData has to be optimized for this
+ // (switch to Search() )
+ //!if ( nCol1 > 0 && !aViewData.GetDocument()->IsBlockEmpty(
+ //! 0, nRow1, nCol1-1, nRow2.
+ //! aViewData.GetTabNo() ) )
+ tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * aViewData.GetPPTX() );
+ aStart.AdjustX( -(nMarkPixel * nLayoutSign) );
+ }
+
+ pGridWin[i]->Invalidate( pGridWin[i]->PixelToLogic( tools::Rectangle( aStart,aEnd ) ) );
+ }
+
+ // #i79909# Calling UpdateAllOverlays here isn't necessary and would lead to overlay calls from a timer,
+ // with a wrong MapMode if editing in a cell (reference input).
+ // #i80499# Overlays need updates in a lot of cases, e.g. changing row/column size,
+ // or showing/hiding outlines. TODO: selections in inactive windows are vanishing.
+ // #i84689# With relative conditional formats, PaintArea may be called often (for each changed cell),
+ // so UpdateAllOverlays was moved to ScTabViewShell::Notify and is called only if PaintPartFlags::Left/PaintPartFlags::Top
+ // is set (width or height changed).
+}
+
+void ScTabView::PaintRangeFinderEntry (const ScRangeFindData* pData, const SCTAB nTab)
+{
+ ScRange aRef = pData->aRef;
+ aRef.PutInOrder(); // PutInOrder for the queries below
+
+ if ( aRef.aStart == aRef.aEnd ) //! ignore sheet?
+ aViewData.GetDocument().ExtendMerge(aRef);
+
+ if (aRef.aStart.Tab() < nTab || aRef.aEnd.Tab() > nTab)
+ return;
+
+ SCCOL nCol1 = aRef.aStart.Col();
+ SCROW nRow1 = aRef.aStart.Row();
+ SCCOL nCol2 = aRef.aEnd.Col();
+ SCROW nRow2 = aRef.aEnd.Row();
+
+ // remove -> repaint
+ // ScUpdateMode::Marks: Invalidate, nothing until end of row
+
+ bool bHiddenEdge = false;
+ SCROW nTmp;
+ ScDocument& rDoc = aViewData.GetDocument();
+ while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab) )
+ {
+ --nCol1;
+ bHiddenEdge = true;
+ }
+ while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab) )
+ {
+ ++nCol2;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.LastVisibleRow(0, nRow1, nTab);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = 0;
+ if (nTmp < nRow1)
+ {
+ nRow1 = nTmp;
+ bHiddenEdge = true;
+ }
+ nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab);
+ if (!rDoc.ValidRow(nTmp))
+ nTmp = rDoc.MaxRow();
+ if (nTmp > nRow2)
+ {
+ nRow2 = nTmp;
+ bHiddenEdge = true;
+ }
+
+ if ( nCol2 - nCol1 > 1 && nRow2 - nRow1 > 1 && !bHiddenEdge )
+ {
+ // only along the edges
+ PaintArea( nCol1, nRow1, nCol2, nRow1, ScUpdateMode::Marks );
+ PaintArea( nCol1, nRow1+1, nCol1, nRow2-1, ScUpdateMode::Marks );
+ PaintArea( nCol2, nRow1+1, nCol2, nRow2-1, ScUpdateMode::Marks );
+ PaintArea( nCol1, nRow2, nCol2, nRow2, ScUpdateMode::Marks );
+ }
+ else // all in one
+ PaintArea( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::Marks );
+}
+
+void ScTabView::PaintRangeFinder( tools::Long nNumber )
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( aViewData.GetViewShell() );
+ if (!pHdl)
+ return;
+
+ ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
+ if ( !(pRangeFinder && pRangeFinder->GetDocName() == aViewData.GetDocShell()->GetTitle()) )
+ return;
+
+ SCTAB nTab = aViewData.GetTabNo();
+ sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
+
+ if (nNumber < 0)
+ {
+ for (sal_uInt16 i=0; i<nCount; i++)
+ PaintRangeFinderEntry(&pRangeFinder->GetObject(i),nTab);
+ }
+ else
+ {
+ sal_uInt16 idx = nNumber;
+ if (idx < nCount)
+ PaintRangeFinderEntry(&pRangeFinder->GetObject(idx),nTab);
+ }
+}
+
+// for chart data selection
+
+void ScTabView::AddHighlightRange( const ScRange& rRange, const Color& rColor )
+{
+ maHighlightRanges.emplace_back( rRange, rColor );
+
+ SCTAB nTab = aViewData.GetTabNo();
+ if ( nTab >= rRange.aStart.Tab() && nTab <= rRange.aEnd.Tab() )
+ PaintArea( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), ScUpdateMode::Marks );
+}
+
+void ScTabView::ClearHighlightRanges()
+{
+ SCTAB nTab = aViewData.GetTabNo();
+ for (ScHighlightEntry const & rEntry : maHighlightRanges)
+ {
+ ScRange aRange = rEntry.aRef;
+ if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
+ PaintArea( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), ScUpdateMode::Marks );
+ }
+
+ maHighlightRanges.clear();
+}
+
+void ScTabView::DoChartSelection(
+ const uno::Sequence< chart2::data::HighlightedRange > & rHilightRanges )
+{
+ ClearHighlightRanges();
+ const sal_Unicode sep = ::formula::FormulaCompiler::GetNativeSymbolChar(ocSep);
+ size_t nSize = 0;
+ size_t nIndex = 0;
+ std::vector<ReferenceMark> aReferenceMarks( nSize );
+
+ for (chart2::data::HighlightedRange const & rHighlightedRange : rHilightRanges)
+ {
+ Color aSelColor(ColorTransparency, rHighlightedRange.PreferredColor);
+ ScRangeList aRangeList;
+ ScDocument& rDoc = aViewData.GetDocShell()->GetDocument();
+ if( ScRangeStringConverter::GetRangeListFromString(
+ aRangeList, rHighlightedRange.RangeRepresentation, rDoc, rDoc.GetAddressConvention(), sep ))
+ {
+ size_t nListSize = aRangeList.size();
+ nSize += nListSize;
+ aReferenceMarks.resize(nSize);
+
+ for ( size_t j = 0; j < nListSize; ++j )
+ {
+ ScRange& p = aRangeList[j];
+ ScRange aTargetRange;
+ if( rHighlightedRange.Index == - 1 )
+ {
+ aTargetRange = p;
+ AddHighlightRange( aTargetRange, aSelColor );
+ }
+ else
+ {
+ aTargetRange = lcl_getSubRangeByIndex( p, rHighlightedRange.Index );
+ AddHighlightRange( aTargetRange, aSelColor );
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aViewData.GetViewShell() )
+ {
+ aTargetRange.PutInOrder();
+
+ tools::Long nX1 = aTargetRange.aStart.Col();
+ tools::Long nX2 = aTargetRange.aEnd.Col();
+ tools::Long nY1 = aTargetRange.aStart.Row();
+ tools::Long nY2 = aTargetRange.aEnd.Row();
+ tools::Long nTab = aTargetRange.aStart.Tab();
+
+ aReferenceMarks[nIndex++] = ScInputHandler::GetReferenceMark( aViewData, aViewData.GetDocShell(),
+ nX1, nX2, nY1, nY2,
+ nTab, aSelColor );
+ }
+ }
+ }
+ }
+
+ if ( comphelper::LibreOfficeKit::isActive() && aViewData.GetViewShell() )
+ ScInputHandler::SendReferenceMarks( aViewData.GetViewShell(), aReferenceMarks );
+}
+
+void ScTabView::DoDPFieldPopup(std::u16string_view rPivotTableName, sal_Int32 nDimensionIndex, Point aPoint, Size aSize)
+{
+ ScDocument& rDocument = aViewData.GetDocShell()->GetDocument();
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+
+ if (!pWin)
+ return;
+
+ ScDPCollection* pDPCollection = rDocument.GetDPCollection();
+ ScDPObject* pDPObject = pDPCollection->GetByName(rPivotTableName);
+ if (!pDPObject)
+ return;
+
+ pDPObject->BuildAllDimensionMembers();
+
+ Point aPos = pWin->LogicToPixel(aPoint);
+ bool bLOK = comphelper::LibreOfficeKit::isActive();
+ Point aScreenPoint = bLOK ? aPos : pWin->OutputToScreenPixel(aPos);
+ Size aScreenSize = pWin->LogicToPixel(aSize);
+
+ pWin->DPLaunchFieldPopupMenu(aScreenPoint, aScreenSize, nDimensionIndex, pDPObject);
+}
+
+// PaintGrid - repaint data range
+
+void ScTabView::PaintGrid()
+{
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->Invalidate();
+ }
+}
+
+// PaintTop - repaint top control elements
+
+void ScTabView::PaintTop()
+{
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ if (pColBar[i])
+ pColBar[i]->Invalidate();
+ if (pColOutline[i])
+ pColOutline[i]->Invalidate();
+ }
+}
+
+void ScTabView::CreateAnchorHandles(SdrHdlList& rHdl, const ScAddress& rAddress)
+{
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if(pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->CreateAnchorHandle(rHdl, rAddress);
+ }
+}
+
+void ScTabView::PaintTopArea( SCCOL nStartCol, SCCOL nEndCol )
+{
+ // pixel position of the left edge
+
+ if ( nStartCol < aViewData.GetPosX(SC_SPLIT_LEFT) ||
+ nStartCol < aViewData.GetPosX(SC_SPLIT_RIGHT) )
+ aViewData.RecalcPixPos();
+
+ // adjust freeze (UpdateFixX resets HSplitPos)
+
+ if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX && nStartCol < aViewData.GetFixPosX() )
+ if (aViewData.UpdateFixX())
+ RepeatResize();
+
+ // paint
+
+ if (nStartCol>0)
+ --nStartCol; //! general ?
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( aViewData.GetTabNo() );
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ ScHSplitPos eWhich = ScHSplitPos(i);
+ if (pColBar[eWhich])
+ {
+ Size aWinSize = pColBar[eWhich]->GetSizePixel();
+ tools::Long nStartX = aViewData.GetScrPos( nStartCol, 0, eWhich ).X();
+ tools::Long nEndX;
+ if (nEndCol >= rDoc.MaxCol())
+ nEndX = bLayoutRTL ? 0 : ( aWinSize.Width()-1 );
+ else
+ nEndX = aViewData.GetScrPos( nEndCol+1, 0, eWhich ).X() - nLayoutSign;
+ if (nStartX > nEndX)
+ std::swap(nStartX, nEndX);
+ pColBar[eWhich]->Invalidate(
+ tools::Rectangle( nStartX, 0, nEndX, aWinSize.Height()-1 ) );
+ }
+ if (pColOutline[eWhich])
+ pColOutline[eWhich]->Invalidate();
+ }
+}
+
+// PaintLeft - repaint left control elements
+
+void ScTabView::PaintLeft()
+{
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ if (pRowBar[i])
+ pRowBar[i]->Invalidate();
+ if (pRowOutline[i])
+ pRowOutline[i]->Invalidate();
+ }
+}
+
+void ScTabView::PaintLeftArea( SCROW nStartRow, SCROW nEndRow )
+{
+ // pixel position of the upper edge
+
+ if ( nStartRow < aViewData.GetPosY(SC_SPLIT_TOP) ||
+ nStartRow < aViewData.GetPosY(SC_SPLIT_BOTTOM) )
+ aViewData.RecalcPixPos();
+
+ // adjust freeze (UpdateFixY reset VSplitPos)
+
+ if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX && nStartRow < aViewData.GetFixPosY() )
+ if (aViewData.UpdateFixY())
+ RepeatResize();
+
+ // paint
+
+ if (nStartRow>0)
+ --nStartRow;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ for (sal_uInt16 i = 0; i < 2; i++)
+ {
+ ScVSplitPos eWhich = ScVSplitPos(i);
+ if (pRowBar[eWhich])
+ {
+ Size aWinSize = pRowBar[eWhich]->GetSizePixel();
+ tools::Long nStartY = aViewData.GetScrPos( 0, nStartRow, eWhich ).Y();
+ tools::Long nEndY;
+ if (nEndRow >= rDoc.MaxRow())
+ nEndY = aWinSize.Height() - 1;
+ else
+ nEndY = aViewData.GetScrPos( 0, nEndRow+1, eWhich ).Y() - 1;
+ if (nStartY > nEndY)
+ std::swap(nStartY, nEndY);
+ pRowBar[eWhich]->Invalidate(
+ tools::Rectangle( 0, nStartY, aWinSize.Width()-1, nEndY ) );
+ }
+ if (pRowOutline[eWhich])
+ pRowOutline[eWhich]->Invalidate();
+ }
+}
+
+bool ScTabView::PaintExtras()
+{
+ bool bRet = false;
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ if (!rDoc.HasTable(nTab)) // sheet is deleted?
+ {
+ SCTAB nCount = rDoc.GetTableCount();
+ aViewData.SetTabNo(nCount-1);
+ bRet = true;
+ }
+ pTabControl->UpdateStatus(); // true = active
+ return bRet;
+}
+
+void ScTabView::RecalcPPT()
+{
+ // called after changes that require the PPT values to be recalculated
+ // (currently from detective operations)
+
+ double nOldX = aViewData.GetPPTX();
+ double nOldY = aViewData.GetPPTY();
+
+ aViewData.RefreshZoom(); // pre-calculate new PPT values
+
+ bool bChangedX = ( aViewData.GetPPTX() != nOldX );
+ bool bChangedY = ( aViewData.GetPPTY() != nOldY );
+ if ( !(bChangedX || bChangedY) )
+ return;
+
+ // call view SetZoom (including draw scale, split update etc)
+ // and paint only if values changed
+
+ Fraction aZoomX = aViewData.GetZoomX();
+ Fraction aZoomY = aViewData.GetZoomY();
+ SetZoom( aZoomX, aZoomY, false );
+
+ PaintGrid();
+ if (bChangedX)
+ PaintTop();
+ if (bChangedY)
+ PaintLeft();
+}
+
+void ScTabView::ActivateView( bool bActivate, bool bFirst )
+{
+ if ( bActivate == aViewData.IsActive() && !bFirst )
+ {
+ // no assertion anymore - occurs when previously in Drag&Drop switching over
+ // to another document
+ return;
+ }
+
+ // is only called for MDI-(De)Activate
+ // aViewData.Activate behind due to cursor show for KillEditView
+ // don't delete selection - if Activate(false) is set in ViewData,
+ // then the selection is not displayed
+
+ if (!bActivate)
+ {
+ ScModule* pScMod = SC_MOD();
+ bool bRefMode = pScMod->IsFormulaMode();
+
+ // don't cancel reference input, to allow reference
+ // to other document
+
+ if (!bRefMode)
+ {
+ // pass view to GetInputHdl, this view may not be current anymore
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
+ if (pHdl)
+ pHdl->EnterHandler();
+ }
+ }
+
+ PaintExtras();
+
+ aViewData.Activate(bActivate);
+
+ PaintBlock(false); // repaint, selection after active status
+
+ if (!bActivate)
+ HideAllCursors(); // Cursor
+ else if (!bFirst)
+ ShowAllCursors();
+
+ if (bActivate)
+ {
+ if ( bFirst )
+ {
+ ScSplitPos eWin = aViewData.GetActivePart();
+ OSL_ENSURE( pGridWin[eWin], "Corrupted document, not all SplitPos in GridWin" );
+ if ( !pGridWin[eWin] )
+ {
+ eWin = SC_SPLIT_BOTTOMLEFT;
+ if ( !pGridWin[eWin] )
+ {
+ short i;
+ for ( i=0; i<4; i++ )
+ {
+ if ( pGridWin[i] )
+ {
+ eWin = static_cast<ScSplitPos>(i);
+ break; // for
+ }
+ }
+ OSL_ENSURE( i<4, "and BOOM" );
+ }
+ aViewData.SetActivePart( eWin );
+ }
+ }
+ // do not call GrabFocus from here!
+ // if the document is processed, then Sfx calls GrabFocus in the window of the shell.
+ // if it is a mail body for instance, then it can't get the focus
+ UpdateInputContext();
+ }
+ else
+ pGridWin[aViewData.GetActivePart()]->ClickExtern();
+}
+
+void ScTabView::ActivatePart( ScSplitPos eWhich )
+{
+ ScSplitPos eOld = aViewData.GetActivePart();
+ if ( eOld == eWhich )
+ return;
+
+ bInActivatePart = true;
+
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+
+ // the HasEditView call during SetCursor would fail otherwise
+ if ( aViewData.HasEditView(eOld) && !bRefMode )
+ UpdateInputLine();
+
+ ScHSplitPos eOldH = WhichH(eOld);
+ ScVSplitPos eOldV = WhichV(eOld);
+ ScHSplitPos eNewH = WhichH(eWhich);
+ ScVSplitPos eNewV = WhichV(eWhich);
+ bool bTopCap = pColBar[eOldH] && pColBar[eOldH]->IsMouseCaptured();
+ bool bLeftCap = pRowBar[eOldV] && pRowBar[eOldV]->IsMouseCaptured();
+
+ bool bFocus = pGridWin[eOld]->HasFocus();
+ bool bCapture = pGridWin[eOld]->IsMouseCaptured();
+ if (bCapture)
+ pGridWin[eOld]->ReleaseMouse();
+ pGridWin[eOld]->ClickExtern();
+ pGridWin[eOld]->HideCursor();
+ pGridWin[eWhich]->HideCursor();
+ aViewData.SetActivePart( eWhich );
+
+ ScTabViewShell* pShell = aViewData.GetViewShell();
+ pShell->WindowChanged();
+
+ pSelEngine->SetWindow(pGridWin[eWhich]);
+ pSelEngine->SetWhich(eWhich);
+ pSelEngine->SetVisibleArea( tools::Rectangle(Point(), pGridWin[eWhich]->GetOutputSizePixel()) );
+
+ pGridWin[eOld]->MoveMouseStatus(*pGridWin[eWhich]);
+
+ if ( bCapture || pGridWin[eWhich]->IsMouseCaptured() )
+ {
+ // tracking instead of CaptureMouse, so it can be cancelled cleanly
+ // (SelectionEngine calls CaptureMouse for SetWindow)
+ //! someday SelectionEngine itself should call StartTracking!?!
+ pGridWin[eWhich]->ReleaseMouse();
+ pGridWin[eWhich]->StartTracking();
+ }
+
+ if ( bTopCap && pColBar[eNewH] )
+ {
+ pColBar[eOldH]->SetIgnoreMove(true);
+ pColBar[eNewH]->SetIgnoreMove(false);
+ pHdrSelEng->SetWindow( pColBar[eNewH] );
+ tools::Long nWidth = pColBar[eNewH]->GetOutputSizePixel().Width();
+ pHdrSelEng->SetVisibleArea( tools::Rectangle( 0, LONG_MIN, nWidth-1, LONG_MAX ) );
+ pColBar[eNewH]->CaptureMouse();
+ }
+ if ( bLeftCap && pRowBar[eNewV] )
+ {
+ pRowBar[eOldV]->SetIgnoreMove(true);
+ pRowBar[eNewV]->SetIgnoreMove(false);
+ pHdrSelEng->SetWindow( pRowBar[eNewV] );
+ tools::Long nHeight = pRowBar[eNewV]->GetOutputSizePixel().Height();
+ pHdrSelEng->SetVisibleArea( tools::Rectangle( LONG_MIN, 0, LONG_MAX, nHeight-1 ) );
+ pRowBar[eNewV]->CaptureMouse();
+ }
+ aHdrFunc.SetWhich(eWhich);
+
+ pGridWin[eOld]->ShowCursor();
+ pGridWin[eWhich]->ShowCursor();
+
+ SfxInPlaceClient* pClient = aViewData.GetViewShell()->GetIPClient();
+ bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
+
+ // don't switch ViewShell's active window during RefInput, because the focus
+ // might change, and subsequent SetReference calls wouldn't find the right EditView
+ if ( !bRefMode && !bOleActive )
+ aViewData.GetViewShell()->SetWindow( pGridWin[eWhich] );
+
+ if ( bFocus && !aViewData.IsAnyFillMode() && !bRefMode )
+ {
+ // GrabFocus only if previously the other GridWindow had the focus
+ // (for instance due to search and replace)
+ pGridWin[eWhich]->GrabFocus();
+ }
+
+ bInActivatePart = false;
+}
+
+void ScTabView::HideListBox()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin)
+ pWin->ClickExtern();
+ }
+}
+
+void ScTabView::UpdateInputContext()
+{
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+ if (pWin)
+ pWin->UpdateInputContext();
+
+ if (pTabControl)
+ pTabControl->UpdateInputContext();
+}
+
+// GetGridWidth - width of an output range (for ViewData)
+
+tools::Long ScTabView::GetGridWidth( ScHSplitPos eWhich )
+{
+ // at present only the size of the current pane is synchronized with
+ // the size of the visible area in Online;
+ // as a workaround we use the same width for all panes independently
+ // from the eWhich value
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScGridWindow* pGridWindow = aViewData.GetActiveWin();
+ if (pGridWindow)
+ return pGridWindow->GetSizePixel().Width();
+ }
+
+ ScSplitPos eGridWhich = ( eWhich == SC_SPLIT_LEFT ) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+ if (pGridWin[eGridWhich])
+ return pGridWin[eGridWhich]->GetSizePixel().Width();
+ else
+ return 0;
+}
+
+// GetGridHeight - height of an output range (for ViewData)
+
+tools::Long ScTabView::GetGridHeight( ScVSplitPos eWhich )
+{
+ // at present only the size of the current pane is synchronized with
+ // the size of the visible area in Online;
+ // as a workaround we use the same height for all panes independently
+ // from the eWhich value
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScGridWindow* pGridWindow = aViewData.GetActiveWin();
+ if (pGridWindow)
+ return pGridWindow->GetSizePixel().Height();
+ }
+
+ ScSplitPos eGridWhich = ( eWhich == SC_SPLIT_TOP ) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
+ if (pGridWin[eGridWhich])
+ return pGridWin[eGridWhich]->GetSizePixel().Height();
+ else
+ return 0;
+}
+
+void ScTabView::UpdateInputLine()
+{
+ SC_MOD()->InputEnterHandler();
+}
+
+void ScTabView::ZoomChanged()
+{
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
+ if (pHdl)
+ pHdl->SetRefScale( aViewData.GetZoomX(), aViewData.GetZoomY() );
+
+ UpdateFixPos();
+
+ UpdateScrollBars();
+
+ // VisArea...
+ // AW: Discussed with NN if there is a reason that new map mode was only set for one window,
+ // but is not. Setting only on one window causes the first repaint to have the old mapMode
+ // in three of four views, so the overlay will save the wrong content e.g. when zooming out.
+ // Changing to setting map mode at all windows.
+
+ for (sal_uInt32 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i])
+ pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
+ }
+
+ SetNewVisArea();
+
+ InterpretVisible(); // have everything calculated before painting
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+ rBindings.Invalidate( SID_ATTR_ZOOM );
+ rBindings.Invalidate( SID_ATTR_ZOOMSLIDER );
+ rBindings.Invalidate(SID_ZOOM_IN);
+ rBindings.Invalidate(SID_ZOOM_OUT);
+
+ HideNoteMarker();
+
+ // To not change too much, use pWin here
+ ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
+
+ if ( pWin && aViewData.HasEditView( aViewData.GetActivePart() ) )
+ {
+ // flush OverlayManager before changing the MapMode
+ pWin->flushOverlayManager();
+
+ // make sure the EditView's position and size are updated
+ // with the right (logic, not drawing) MapMode
+ pWin->SetMapMode( aViewData.GetLogicMode() );
+ UpdateEditView();
+ }
+}
+
+void ScTabView::CheckNeedsRepaint()
+{
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ if (pGridWin[i] && pGridWin[i]->IsVisible())
+ pGridWin[i]->CheckNeedsRepaint();
+ }
+}
+
+bool ScTabView::NeedsRepaint()
+{
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ {
+ if (pWin && pWin->IsVisible() && pWin->NeedsRepaint())
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview4.cxx b/sc/source/ui/view/tabview4.cxx
new file mode 100644
index 0000000000..a7de6bdf67
--- /dev/null
+++ b/sc/source/ui/view/tabview4.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 <vcl/help.hxx>
+
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <gridwin.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <inputhdl.hxx>
+
+// --- Referenz-Eingabe / Fill-Cursor
+
+void ScTabView::HideTip()
+{
+ if ( nTipVisible )
+ {
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ vcl::Window* pWin = pGridWin[eWhich];
+ Help::HidePopover(pWin, nTipVisible);
+ nTipVisible = nullptr;
+ aTipRectangle = tools::Rectangle();
+ nTipAlign = QuickHelpFlags::NONE;
+ sTipString.clear();
+ sTopParent.clear();
+ }
+}
+
+void ScTabView::ShowRefTip()
+{
+ bool bDone = false;
+ if ( aViewData.GetRefType() == SC_REFTYPE_REF && Help::IsQuickHelpEnabled() )
+ {
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nEndX != nStartX || nEndY != nStartY ) // not for a single cell
+ {
+ bool bLeft = ( nEndX < nStartX );
+ bool bTop = ( nEndY < nStartY );
+ PutInOrder( nStartX, nEndX );
+ PutInOrder( nStartY, nEndY );
+ SCCOL nCols = nEndX+1-nStartX;
+ SCROW nRows = nEndY+1-nStartY;
+
+ OUString aHelp = ScResId( STR_QUICKHELP_REF );
+ aHelp = aHelp.replaceFirst("%1", OUString::number(nRows) );
+ aHelp = aHelp.replaceFirst("%2", OUString::number(nCols) );
+
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ vcl::Window* pWin = pGridWin[eWhich];
+ if ( pWin )
+ {
+ Point aStart = aViewData.GetScrPos( nStartX, nStartY, eWhich );
+ Point aEnd = aViewData.GetScrPos( nEndX+1, nEndY+1, eWhich );
+
+ Point aPos( bLeft ? aStart.X() : ( aEnd.X() + 3 ),
+ bTop ? aStart.Y() : ( aEnd.Y() + 3 ) );
+ QuickHelpFlags nFlags = ( bLeft ? QuickHelpFlags::Right : QuickHelpFlags::Left ) |
+ ( bTop ? QuickHelpFlags::Bottom : QuickHelpFlags::Top );
+
+ // not over the edited formula
+ if ( !bTop && aViewData.HasEditView( eWhich ) &&
+ nEndY+1 == aViewData.GetEditViewRow() )
+ {
+ // then align at the upper border of edited cell
+ aPos.AdjustY( -2 ); // the three from above
+ nFlags = ( nFlags & ~QuickHelpFlags::Top ) | QuickHelpFlags::Bottom;
+ }
+
+ tools::Rectangle aRect( pWin->OutputToScreenPixel( aPos ), Size(1,1) );
+
+ // Test if changed.
+ if (!nTipVisible || nFlags != nTipAlign || aRect != aTipRectangle || sTipString != aHelp || sTopParent != pWin)
+ {
+ HideTip();
+ nTipVisible = Help::ShowPopover(pWin, aRect, aHelp, nFlags);
+ aTipRectangle = aRect;
+ nTipAlign = nFlags;
+ sTipString = aHelp;
+ sTopParent = pWin;
+ }
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ HideTip();
+}
+
+void ScTabView::StopRefMode()
+{
+ if (aViewData.IsRefMode())
+ {
+ aViewData.SetRefMode( false, SC_REFTYPE_NONE );
+
+ HideTip();
+ UpdateShrinkOverlay();
+
+ if ( aViewData.GetTabNo() >= aViewData.GetRefStartZ() &&
+ aViewData.GetTabNo() <= aViewData.GetRefEndZ() )
+ {
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );
+
+ PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
+ }
+
+ pSelEngine->Reset();
+ pSelEngine->SetAddMode( false ); //! shouldn't that happen during reset?
+
+ ScSplitPos eOld = pSelEngine->GetWhich();
+ ScSplitPos eNew = aViewData.GetActivePart();
+ if ( eNew != eOld )
+ {
+ pSelEngine->SetWindow( pGridWin[ eNew ] );
+ pSelEngine->SetWhich( eNew );
+ pSelEngine->SetVisibleArea( tools::Rectangle(Point(),
+ pGridWin[eNew]->GetOutputSizePixel()) );
+ pGridWin[eOld]->MoveMouseStatus(*pGridWin[eNew]);
+ }
+ }
+
+ // AlignToCursor(SC_FOLLOW_NONE): Only switch active part.
+ // This must also be done if no RefMode was active (for RangeFinder dragging),
+ // but if RefMode was set, AlignToCursor must be after SelectionEngine reset,
+ // so the SelectionEngine SetWindow call from AlignToCursor doesn't capture
+ // the mouse again when called from Tracking/MouseButtonUp (#94562#).
+ AlignToCursor( aViewData.GetCurX(), aViewData.GetCurY(), SC_FOLLOW_NONE );
+}
+
+void ScTabView::DoneRefMode( bool bContinue )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( aViewData.GetRefType() == SC_REFTYPE_REF && bContinue )
+ SC_MOD()->AddRefEntry();
+
+ bool bWasRef = aViewData.IsRefMode();
+ aViewData.SetRefMode( false, SC_REFTYPE_NONE );
+
+ HideTip();
+ UpdateShrinkOverlay();
+
+ // Paint:
+ if ( bWasRef && aViewData.GetTabNo() >= aViewData.GetRefStartZ() &&
+ aViewData.GetTabNo() <= aViewData.GetRefEndZ() )
+ {
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );
+
+ PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
+ }
+}
+
+void ScTabView::UpdateRef( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+
+ if (!aViewData.IsRefMode())
+ {
+ // This will happen, when first at a reference dialog with Control in
+ // the table is clicked. Then append the new reference to the old content:
+
+ ScModule* pScMod = SC_MOD();
+ if (pScMod->IsFormulaMode())
+ pScMod->AddRefEntry();
+
+ InitRefMode( nCurX, nCurY, nCurZ, SC_REFTYPE_REF );
+ }
+
+ if ( nCurX != aViewData.GetRefEndX() || nCurY != aViewData.GetRefEndY() ||
+ nCurZ != aViewData.GetRefEndZ() )
+ {
+ ScMarkData& rMark = aViewData.GetMarkData();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ SCCOL nStartX = aViewData.GetRefStartX();
+ SCROW nStartY = aViewData.GetRefStartY();
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
+ ScUpdateRect aRect( nStartX, nStartY, nEndX, nEndY );
+
+ if (rDoc.HasAttrib(nCurX, nCurY, nCurZ, HasAttrFlags::Merged))
+ rDoc.ExtendMerge(nStartX, nStartY, nCurX, nCurY, nCurZ);
+
+ aViewData.SetRefEnd( nCurX, nCurY, nCurZ );
+
+ nStartX = aViewData.GetRefStartX();
+ nStartY = aViewData.GetRefStartY();
+ nEndX = aViewData.GetRefEndX();
+ nEndY = aViewData.GetRefEndY();
+ if ( nStartX == nEndX && nStartY == nEndY )
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
+ aRect.SetNew( nStartX, nStartY, nEndX, nEndY );
+
+ ScRefType eType = aViewData.GetRefType();
+ if ( eType == SC_REFTYPE_REF )
+ {
+ if ((nStartX > nEndX || nStartY > nEndY) &&
+ rDoc.HasAttrib(nStartX, nStartY, nTab, HasAttrFlags::Merged))
+ rDoc.ExtendMerge( nStartX, nStartY, nStartX, nStartY, nTab );
+
+ ScRange aRef(
+ nStartX, nStartY, aViewData.GetRefStartZ(),
+ nEndX, nEndY, aViewData.GetRefEndZ() );
+ SC_MOD()->SetReference( aRef, rDoc, &rMark );
+ ShowRefTip();
+ }
+ else if ( eType == SC_REFTYPE_EMBED_LT || eType == SC_REFTYPE_EMBED_RB )
+ {
+ PutInOrder(nStartX,nEndX);
+ PutInOrder(nStartY,nEndY);
+ rDoc.SetEmbedded( ScRange(nStartX,nStartY,nTab, nEndX,nEndY,nTab) );
+ ScDocShell* pDocSh = aViewData.GetDocShell();
+ pDocSh->UpdateOle( aViewData, true );
+ pDocSh->SetDocumentModified();
+ }
+
+ SCCOL nPaintStartX;
+ SCROW nPaintStartY;
+ SCCOL nPaintEndX;
+ SCROW nPaintEndY;
+ if (aRect.GetDiff( nPaintStartX, nPaintStartY, nPaintEndX, nPaintEndY ))
+ PaintArea( nPaintStartX, nPaintStartY, nPaintEndX, nPaintEndY, ScUpdateMode::Marks );
+
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl();
+ if (pInputHandler)
+ {
+ pInputHandler->UpdateLokReferenceMarks();
+ }
+ }
+
+ // autocomplete for Auto-Fill
+ if ( !(aViewData.GetRefType() == SC_REFTYPE_FILL && Help::IsQuickHelpEnabled()) )
+ return;
+
+ vcl::Window* pWin = GetActiveWin();
+ if ( !pWin )
+ return;
+
+ OUString aHelpStr;
+ ScRange aMarkRange;
+ aViewData.GetSimpleArea( aMarkRange );
+ SCCOL nEndX = aViewData.GetRefEndX();
+ SCROW nEndY = aViewData.GetRefEndY();
+ ScRange aDelRange;
+ if ( aViewData.GetFillMode() == ScFillMode::MATRIX && !(nScFillModeMouseModifier & KEY_MOD1) )
+ {
+ aHelpStr = ScResId( STR_TIP_RESIZEMATRIX );
+ SCCOL nCols = nEndX + 1 - aViewData.GetRefStartX(); // order is right
+ SCROW nRows = nEndY + 1 - aViewData.GetRefStartY();
+ aHelpStr = aHelpStr.replaceFirst("%1", OUString::number(nRows) );
+ aHelpStr = aHelpStr.replaceFirst("%2", OUString::number(nCols) );
+ }
+ else if ( aViewData.GetDelMark( aDelRange ) )
+ aHelpStr = ScResId( STR_QUICKHELP_DELETE );
+ else if ( nEndX != aMarkRange.aEnd.Col() || nEndY != aMarkRange.aEnd.Row() )
+ aHelpStr = rDoc.GetAutoFillPreview( aMarkRange, nEndX, nEndY );
+
+ if (!aHelpStr.getLength())
+ return;
+
+ // depending on direction the upper or lower corner
+ SCCOL nAddX = ( nEndX >= aMarkRange.aEnd.Col() ) ? 1 : 0;
+ SCROW nAddY = ( nEndY >= aMarkRange.aEnd.Row() ) ? 1 : 0;
+ Point aPos = aViewData.GetScrPos( nEndX+nAddX, nEndY+nAddY, aViewData.GetActivePart() );
+ aPos.AdjustX(8 );
+ aPos.AdjustY(4 );
+ aPos = pWin->OutputToScreenPixel( aPos );
+ tools::Rectangle aRect( aPos, aPos );
+ QuickHelpFlags nAlign = QuickHelpFlags::Left|QuickHelpFlags::Top;
+ if (!nTipVisible || nAlign != nTipAlign || aRect != aTipRectangle || sTipString != aHelpStr || sTopParent != pWin)
+ {
+ HideTip();
+ nTipVisible = Help::ShowPopover(pWin, aRect, aHelpStr, nAlign);
+ aTipRectangle = aRect;
+ nTipAlign = nAlign;
+ sTipString = aHelpStr;
+ sTopParent = pWin;
+ }
+}
+
+void ScTabView::InitRefMode( SCCOL nCurX, SCROW nCurY, SCTAB nCurZ, ScRefType eType )
+{
+ ScDocument& rDoc = aViewData.GetDocument();
+ ScMarkData& rMark = aViewData.GetMarkData();
+ if (aViewData.IsRefMode())
+ return;
+
+ aViewData.SetRefMode( true, eType );
+ aViewData.SetRefStart( nCurX, nCurY, nCurZ );
+ aViewData.SetRefEnd( nCurX, nCurY, nCurZ );
+
+ if (nCurZ == aViewData.GetTabNo())
+ {
+ SCCOL nStartX = nCurX;
+ SCROW nStartY = nCurY;
+ SCCOL nEndX = nCurX;
+ SCROW nEndY = nCurY;
+ rDoc.ExtendMerge( nStartX, nStartY, nEndX, nEndY, aViewData.GetTabNo() );
+
+ //! draw only markings over content!
+ PaintArea( nStartX,nStartY,nEndX,nEndY, ScUpdateMode::Marks );
+
+ // SetReference without Merge-Adjustment
+ ScRange aRef( nCurX,nCurY,nCurZ, nCurX,nCurY,nCurZ );
+ SC_MOD()->SetReference( aRef, rDoc, &rMark );
+ }
+
+ ScInputHandler* pInputHandler = SC_MOD()->GetInputHdl();
+ if (pInputHandler)
+ {
+ pInputHandler->UpdateLokReferenceMarks();
+ }
+}
+
+void ScTabView::SetScrollBar( ScrollAdaptor& rScroll, tools::Long nRangeMax, tools::Long nVisible, tools::Long nPos, bool bLayoutRTL )
+{
+ if ( nVisible == 0 )
+ nVisible = 1; // #i59893# don't use visible size 0
+
+ rScroll.SetRange( Range( 0, nRangeMax ) );
+ rScroll.SetVisibleSize( nVisible );
+ rScroll.SetThumbPos( nPos );
+
+ rScroll.EnableRTL( bLayoutRTL );
+}
+
+tools::Long ScTabView::GetScrollBarPos( const ScrollAdaptor& rScroll )
+{
+ return rScroll.GetThumbPos();
+}
+
+// UpdateScrollBars - set visible area and scroll width of scroll bars
+static tools::Long lcl_UpdateBar( ScrollAdaptor& rScroll, SCCOLROW nSize ) // Size = (complete) cells
+{
+ tools::Long nOldPos;
+ tools::Long nNewPos;
+
+ nOldPos = rScroll.GetThumbPos();
+ rScroll.SetPageSize( static_cast<tools::Long>(nSize) );
+ nNewPos = rScroll.GetThumbPos();
+#ifndef UNX
+ rScroll.SetPageSize( 1 ); // always possible !
+#endif
+
+ return nNewPos - nOldPos;
+}
+
+static tools::Long lcl_GetScrollRange( SCCOLROW nDocEnd, SCCOLROW nPos, SCCOLROW nVis, SCCOLROW nMax, SCCOLROW nStart )
+{
+ // get the end (positive) of a scroll bar range that always starts at 0
+
+ ++nVis;
+ ++nMax; // for partially visible cells
+ SCCOLROW nEnd = std::max(nDocEnd, static_cast<SCCOLROW>(nPos+nVis)) + nVis;
+ if (nEnd > nMax)
+ nEnd = nMax;
+
+ return ( nEnd - nStart ); // for range starting at 0
+}
+
+void ScTabView::UpdateScrollBars( HeaderType eHeaderType )
+{
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), eHeaderType, GetViewData().GetTabNo());
+
+ tools::Long nDiff;
+ bool bTop = ( aViewData.GetVSplitMode() != SC_SPLIT_NONE );
+ bool bRight = ( aViewData.GetHSplitMode() != SC_SPLIT_NONE );
+ ScDocument& rDoc = aViewData.GetDocument();
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );
+ SCCOL nUsedX;
+ SCROW nUsedY;
+ rDoc.GetTableArea( nTab, nUsedX, nUsedY ); //! cached !!!!!!!!!!!!!!!
+
+ SCCOL nVisXL = 0;
+ SCCOL nVisXR = 0;
+ SCROW nVisYB = 0;
+ SCROW nVisYT = 0;
+
+ SCCOL nStartX = 0;
+ SCROW nStartY = 0;
+ if (aViewData.GetHSplitMode()==SC_SPLIT_FIX)
+ nStartX = aViewData.GetFixPosX();
+ if (aViewData.GetVSplitMode()==SC_SPLIT_FIX)
+ nStartY = aViewData.GetFixPosY();
+
+ nVisXL = aViewData.VisibleCellsX( SC_SPLIT_LEFT );
+ tools::Long nMaxXL = lcl_GetScrollRange( nUsedX, aViewData.GetPosX(SC_SPLIT_LEFT), nVisXL, rDoc.MaxCol(), 0 );
+ SetScrollBar( *aHScrollLeft, nMaxXL, nVisXL, aViewData.GetPosX( SC_SPLIT_LEFT ), bLayoutRTL );
+
+ nVisYB = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM );
+ tools::Long nMaxYB = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_BOTTOM), nVisYB, rDoc.MaxRow(), nStartY );
+ SetScrollBar( *aVScrollBottom, nMaxYB, nVisYB, aViewData.GetPosY( SC_SPLIT_BOTTOM ) - nStartY, bLayoutRTL );
+
+ if (bRight)
+ {
+ nVisXR = aViewData.VisibleCellsX( SC_SPLIT_RIGHT );
+ tools::Long nMaxXR = lcl_GetScrollRange( nUsedX, aViewData.GetPosX(SC_SPLIT_RIGHT), nVisXR, rDoc.MaxCol(), nStartX );
+ SetScrollBar( *aHScrollRight, nMaxXR, nVisXR, aViewData.GetPosX( SC_SPLIT_RIGHT ) - nStartX, bLayoutRTL );
+ }
+
+ if (bTop)
+ {
+ nVisYT = aViewData.VisibleCellsY( SC_SPLIT_TOP );
+ tools::Long nMaxYT = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_TOP), nVisYT, rDoc.MaxRow(), 0 );
+ SetScrollBar( *aVScrollTop, nMaxYT, nVisYT, aViewData.GetPosY( SC_SPLIT_TOP ), bLayoutRTL );
+ }
+
+ // test the range
+
+ nDiff = lcl_UpdateBar( *aHScrollLeft, nVisXL );
+ if (nDiff) ScrollX( nDiff, SC_SPLIT_LEFT );
+ if (bRight)
+ {
+ nDiff = lcl_UpdateBar( *aHScrollRight, nVisXR );
+ if (nDiff) ScrollX( nDiff, SC_SPLIT_RIGHT );
+ }
+
+ nDiff = lcl_UpdateBar( *aVScrollBottom, nVisYB );
+ if (nDiff) ScrollY( nDiff, SC_SPLIT_BOTTOM );
+ if (bTop)
+ {
+ nDiff = lcl_UpdateBar( *aVScrollTop, nVisYT );
+ if (nDiff) ScrollY( nDiff, SC_SPLIT_TOP );
+ }
+
+ // set visible area for online spelling
+
+ if ( aViewData.IsActive() )
+ {
+ if (UpdateVisibleRange())
+ SC_MOD()->AnythingChanged(); // if visible area has changed
+ }
+}
+
+#ifndef HDR_SLIDERSIZE
+#define HDR_SLIDERSIZE 2
+#endif
+
+void ScTabView::InvertHorizontal( ScVSplitPos eWhich, tools::Long nDragPos )
+{
+ for (sal_uInt16 i=0; i<4; i++)
+ if (WhichV(static_cast<ScSplitPos>(i))==eWhich)
+ {
+ ScGridWindow* pWin = pGridWin[i].get();
+ if (pWin)
+ {
+ tools::Rectangle aRect( 0,nDragPos, pWin->GetOutputSizePixel().Width()-1,nDragPos+HDR_SLIDERSIZE-1 );
+ pWin->PaintImmediately();
+ pWin->DoInvertRect( aRect ); // Pixel
+ }
+ }
+}
+
+void ScTabView::InvertVertical( ScHSplitPos eWhich, tools::Long nDragPos )
+{
+ for (sal_uInt16 i=0; i<4; i++)
+ if (WhichH(static_cast<ScSplitPos>(i))==eWhich)
+ {
+ ScGridWindow* pWin = pGridWin[i].get();
+ if (pWin)
+ {
+ tools::Rectangle aRect( nDragPos,0, nDragPos+HDR_SLIDERSIZE-1,pWin->GetOutputSizePixel().Height()-1 );
+ pWin->PaintImmediately();
+ pWin->DoInvertRect( aRect ); // Pixel
+ }
+ }
+}
+
+void ScTabView::InterpretVisible()
+{
+ // make sure all visible cells are interpreted,
+ // so the next paint will not execute a macro function
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( !rDoc.GetAutoCalc() )
+ return;
+
+ SCTAB nTab = aViewData.GetTabNo();
+ for (sal_uInt16 i=0; i<4; i++)
+ {
+ // rely on gridwin pointers to find used panes
+ // no IsVisible test in case the whole view is not yet shown
+
+ if (pGridWin[i])
+ {
+ ScHSplitPos eHWhich = WhichH( ScSplitPos(i) );
+ ScVSplitPos eVWhich = WhichV( ScSplitPos(i) );
+
+ SCCOL nX1 = rDoc.SanitizeCol( aViewData.GetPosX( eHWhich ));
+ SCROW nY1 = rDoc.SanitizeRow( aViewData.GetPosY( eVWhich ));
+ SCCOL nX2 = rDoc.SanitizeCol( nX1 + aViewData.VisibleCellsX( eHWhich ));
+ SCROW nY2 = rDoc.SanitizeRow( nY1 + aViewData.VisibleCellsY( eVWhich ));
+
+ rDoc.InterpretDirtyCells(ScRange(nX1, nY1, nTab, nX2, nY2, nTab));
+ }
+ }
+
+ // #i65047# repaint during the above loop may have set the bNeedsRepaint flag
+ CheckNeedsRepaint();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview5.cxx b/sc/source/ui/view/tabview5.cxx
new file mode 100644
index 0000000000..c9b65bb58c
--- /dev/null
+++ b/sc/source/ui/view/tabview5.cxx
@@ -0,0 +1,704 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/fmshell.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdoutl.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <osl/diagnose.h>
+
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <gridwin.hxx>
+#include <olinewin.hxx>
+#include <tabsplit.hxx>
+#include <colrowba.hxx>
+#include <tabcont.hxx>
+#include <sc.hrc>
+#include <pagedata.hxx>
+#include <hiranges.hxx>
+#include <drawview.hxx>
+#include <drwlayer.hxx>
+#include <fusel.hxx>
+#include <seltrans.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <viewuno.hxx>
+#include <postit.hxx>
+#include <spellcheckcontext.hxx>
+
+#include <vcl/settings.hxx>
+
+#include <comphelper/lok.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+using namespace com::sun::star;
+
+void ScTabView::Init()
+{
+ /* RTL layout of the view windows is done manually, because it depends on
+ the sheet orientation, not the UI setting. Note: controls that are
+ already constructed (e.g. scroll bars) have the RTL setting of the GUI.
+ Eventually this has to be disabled manually (see below). */
+ pFrameWin->EnableRTL( false );
+
+ sal_uInt16 i;
+
+ mbInlineWithScrollbar = officecfg::Office::Calc::Layout::Other::TabbarInlineWithScrollbar::get();
+
+ aScrollTimer.SetTimeout(10);
+ aScrollTimer.SetInvokeHandler( LINK( this, ScTabView, TimerHdl ) );
+
+ for (i=0; i<4; i++)
+ pGridWin[i] = nullptr;
+ pGridWin[SC_SPLIT_BOTTOMLEFT] = VclPtr<ScGridWindow>::Create( pFrameWin, aViewData, SC_SPLIT_BOTTOMLEFT );
+
+ pSelEngine.reset( new ScViewSelectionEngine( pGridWin[SC_SPLIT_BOTTOMLEFT], this,
+ SC_SPLIT_BOTTOMLEFT ) );
+ aFunctionSet.SetSelectionEngine( pSelEngine.get() );
+
+ pHdrSelEng.reset( new ScHeaderSelectionEngine( pFrameWin, &aHdrFunc ) );
+
+ pColBar[SC_SPLIT_LEFT] = VclPtr<ScColBar>::Create( pFrameWin, SC_SPLIT_LEFT,
+ &aHdrFunc, pHdrSelEng.get(), this );
+ pColBar[SC_SPLIT_RIGHT] = nullptr;
+ pRowBar[SC_SPLIT_BOTTOM] = VclPtr<ScRowBar>::Create( pFrameWin, SC_SPLIT_BOTTOM,
+ &aHdrFunc, pHdrSelEng.get(), this );
+ pRowBar[SC_SPLIT_TOP] = nullptr;
+ for (i=0; i<2; i++)
+ pColOutline[i] = pRowOutline[i] = nullptr;
+
+ pHSplitter = VclPtr<ScTabSplitter>::Create( pFrameWin, WinBits( WB_HSCROLL ), &aViewData );
+ pVSplitter = VclPtr<ScTabSplitter>::Create( pFrameWin, WinBits( WB_VSCROLL ), &aViewData );
+
+ // SSA: override default keyboard step size to allow snap to row/column
+ pHSplitter->SetKeyboardStepSize( 1 );
+ pVSplitter->SetKeyboardStepSize( 1 );
+
+ pTabControl = VclPtr<ScTabControl>::Create(pFrameWin, &aViewData);
+ if (mbInlineWithScrollbar)
+ pTabControl->SetStyle(pTabControl->GetStyle() | WB_SIZEABLE);
+
+ /* #i97900# The tab control has to remain in RTL mode if GUI is RTL, this
+ is needed to draw the 3D effect correctly. The base TabBar implements
+ mirroring independent from the GUI direction. Have to set RTL mode
+ explicitly because the parent frame window is already RTL disabled. */
+ pTabControl->EnableRTL( AllSettings::GetLayoutRTL() );
+
+ InitScrollBar( *aHScrollLeft, aViewData.GetDocument().MaxCol()+1, LINK(this, ScTabView, HScrollLeftHdl) );
+ InitScrollBar( *aHScrollRight, aViewData.GetDocument().MaxCol()+1, LINK(this, ScTabView, HScrollRightHdl) );
+ InitScrollBar( *aVScrollTop, aViewData.GetDocument().MaxRow()+1, LINK(this, ScTabView, VScrollTopHdl) );
+ InitScrollBar( *aVScrollBottom, aViewData.GetDocument().MaxRow()+1, LINK(this, ScTabView, VScrollBottomHdl) );
+ /* #i97900# scrollbars remain in correct RTL mode, needed mirroring etc.
+ is now handled correctly at the respective places. */
+
+ // Don't show anything here, because still in wrong order
+ // Show is received from UpdateShow during first resize
+ // pTabControl, pGridWin, aHScrollLeft, aVScrollBottom,
+ // aCornerButton, pHSplitter, pVSplitter
+
+ // fragment
+
+ pHSplitter->SetSplitHdl( LINK( this, ScTabView, SplitHdl ) );
+ pVSplitter->SetSplitHdl( LINK( this, ScTabView, SplitHdl ) );
+
+ // UpdateShow is done during resize or a copy of an existing view from ctor
+
+ pDrawActual = nullptr;
+ pDrawOld = nullptr;
+
+ // DrawView cannot be create in the TabView - ctor
+ // when the ViewShell isn't constructed yet...
+ // The also applies to ViewOptionsHasChanged()
+
+ TestHintWindow();
+}
+
+ScTabView::~ScTabView()
+{
+ sal_uInt16 i;
+
+ // remove selection object
+ ScModule* pScMod = SC_MOD();
+ ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
+ if ( pOld && pOld->GetView() == this )
+ {
+ pOld->ForgetView();
+ pScMod->SetSelectionTransfer( nullptr );
+ TransferableHelper::ClearPrimarySelection(); // may delete pOld
+ }
+
+ pBrushDocument.reset();
+ pDrawBrushSet.reset();
+
+ pPageBreakData.reset();
+
+ delete pDrawActual;
+ pDrawActual = nullptr;
+ delete pDrawOld;
+ pDrawOld = nullptr;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell* pThisViewShell = GetViewData().GetViewShell();
+
+ auto lRemoveWindows =
+ [pThisViewShell] (ScTabViewShell* pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ for (int k = 0; k < 4; ++k)
+ {
+ if (rOtherViewData.HasEditView(static_cast<ScSplitPos>(k)))
+ pThisViewShell->RemoveWindowFromForeignEditView(pOtherViewShell, static_cast<ScSplitPos>(k));
+ }
+ };
+
+ SfxLokHelper::forEachOtherView(pThisViewShell, lRemoveWindows);
+ }
+
+ aViewData.KillEditView(); // as long as GridWins still exist
+
+ if (pDrawView)
+ {
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ pDrawView->DeleteDeviceFromPaintView(*pGridWin[i]->GetOutDev());
+ }
+
+ pDrawView->HideSdrPage();
+ pDrawView.reset();
+ }
+
+ pSelEngine.reset();
+
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->dispose();
+ mpSpellCheckCxt.reset();
+
+ mxInputHintOO.reset();
+ for (i=0; i<4; i++)
+ pGridWin[i].disposeAndClear();
+
+ pHdrSelEng.reset();
+
+ for (i=0; i<2; i++)
+ {
+ pColBar[i].disposeAndClear();
+ pRowBar[i].disposeAndClear();
+ pColOutline[i].disposeAndClear();
+ pRowOutline[i].disposeAndClear();
+ }
+
+ aCornerButton.disposeAndClear();
+ aTopButton.disposeAndClear();
+ aHScrollLeft.disposeAndClear();
+ aHScrollRight.disposeAndClear();
+ aVScrollTop.disposeAndClear();
+ aVScrollBottom.disposeAndClear();
+
+ pHSplitter.disposeAndClear();
+ pVSplitter.disposeAndClear();
+ pTabControl.disposeAndClear();
+}
+
+void ScTabView::MakeDrawView( TriState nForceDesignMode )
+{
+ if (pDrawView)
+ return;
+
+ ScDrawLayer* pLayer = aViewData.GetDocument().GetDrawLayer();
+ OSL_ENSURE(pLayer, "Where is the Draw Layer ??");
+
+ sal_uInt16 i;
+ pDrawView.reset( new ScDrawView( pGridWin[SC_SPLIT_BOTTOMLEFT]->GetOutDev(), &aViewData ) );
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ if ( SC_SPLIT_BOTTOMLEFT != static_cast<ScSplitPos>(i) )
+ pDrawView->AddDeviceToPaintView(*pGridWin[i]->GetOutDev(), nullptr);
+ }
+ pDrawView->RecalcScale();
+ for (i=0; i<4; i++)
+ if (pGridWin[i])
+ {
+ pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
+
+ pGridWin[i]->PaintImmediately(); // because of Invalidate in DrawView ctor (ShowPage),
+ // so that immediately can be drawn
+ }
+ SfxRequest aSfxRequest(SID_OBJECT_SELECT, SfxCallMode::SLOT, aViewData.GetViewShell()->GetPool());
+ SetDrawFuncPtr(new FuSelection(*aViewData.GetViewShell(), GetActiveWin(), pDrawView.get(),
+ pLayer,aSfxRequest));
+
+ // used when switching back from page preview: restore saved design mode state
+ // (otherwise, keep the default from the draw view ctor)
+ if ( nForceDesignMode != TRISTATE_INDET )
+ pDrawView->SetDesignMode( nForceDesignMode != TRISTATE_FALSE );
+
+ // register at FormShell
+ FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell();
+ if (pFormSh)
+ pFormSh->SetView(pDrawView.get());
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccMakeDrawLayer));
+}
+
+void ScTabView::DoAddWin( ScGridWindow* pWin )
+{
+ if (pDrawView)
+ {
+ pDrawView->AddDeviceToPaintView(*pWin->GetOutDev(), nullptr);
+ pWin->DrawLayerCreated();
+ }
+ pWin->SetAutoSpellContext(mpSpellCheckCxt);
+}
+
+void ScTabView::TabChanged( bool bSameTabButMoved )
+{
+ if (pDrawView)
+ {
+ DrawDeselectAll(); // end also text edit mode
+
+ SCTAB nTab = aViewData.GetTabNo();
+ pDrawView->HideSdrPage();
+ pDrawView->ShowSdrPage(pDrawView->GetModel().GetPage(nTab));
+
+ UpdateLayerLocks();
+
+ pDrawView->RecalcScale();
+ pDrawView->UpdateWorkArea(); // PageSize is different per page
+ }
+
+ SfxBindings& rBindings = aViewData.GetBindings();
+
+ // There is no easy way to invalidate all slots of the FormShell
+ // (for disabled slots on protected tables), therefore simply everything...
+ rBindings.InvalidateAll(false);
+
+ if (aViewData.GetViewShell()->HasAccessibilityObjects())
+ {
+ SfxHint aAccHint(SfxHintId::ScAccTableChanged);
+ aViewData.GetViewShell()->BroadcastAccessibility(aAccHint);
+ }
+
+ // notification for XActivationBroadcaster
+ SfxViewFrame& rViewFrame = aViewData.GetViewShell()->GetViewFrame();
+ uno::Reference<frame::XController> xController = rViewFrame.GetFrame().GetController();
+ if (xController.is())
+ {
+ ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
+ if (pImp)
+ pImp->SheetChanged( bSameTabButMoved );
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (pGridWin[i])
+ {
+ pGridWin[i]->initiatePageBreaks();
+ // Trigger calculating page breaks only once.
+ break;
+ }
+ }
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScModelObj* pModelObj = pDocSh ? pDocSh->GetModel() : nullptr;
+
+ if (!pModelObj)
+ return;
+
+ Size aDocSize = pModelObj->getDocumentSize();
+ std::stringstream ss;
+ ss << aDocSize.Width() << ", " << aDocSize.Height();
+ OString sRect(ss.str());
+ ScTabViewShell* pViewShell = aViewData.GetViewShell();
+
+ // Invalidate first
+ tools::Rectangle aRectangle(0, 0, 1000000000, 1000000000);
+ pViewShell->libreOfficeKitViewInvalidateTilesCallback(&aRectangle, aViewData.GetTabNo(), 0);
+
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(pViewShell->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(pViewShell, sRect, pModel, false);
+}
+
+void ScTabView::UpdateLayerLocks()
+{
+ if (!pDrawView)
+ return;
+
+ SCTAB nTab = aViewData.GetTabNo();
+ bool bEx = aViewData.GetViewShell()->IsDrawSelMode();
+ bool bProt = aViewData.GetDocument().IsTabProtected( nTab ) ||
+ aViewData.GetSfxDocShell()->IsReadOnly();
+ bool bShared = aViewData.GetDocShell()->IsDocShared();
+
+ SdrLayer* pLayer;
+ SdrLayerAdmin& rAdmin = pDrawView->GetModel().GetLayerAdmin();
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_BACK);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || !bEx || bShared );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_INTERN);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName() );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_FRONT);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || bShared );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_CONTROLS);
+ if (pLayer)
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || bShared );
+ pLayer = rAdmin.GetLayerPerID(SC_LAYER_HIDDEN);
+ if (pLayer)
+ {
+ pDrawView->SetLayerLocked( pLayer->GetName(), bProt || bShared );
+ pDrawView->SetLayerVisible( pLayer->GetName(), false);
+ }
+ pTabControl->SetAddButtonEnabled(aViewData.GetDocument().IsDocEditable());
+}
+
+void ScTabView::DrawDeselectAll()
+{
+ if (!pDrawView)
+ return;
+
+ ScTabViewShell* pViewSh = aViewData.GetViewShell();
+ if ( pDrawActual &&
+ ( pViewSh->IsDrawTextShell() || pDrawActual->GetSlotID() == SID_DRAW_NOTEEDIT ) )
+ {
+ // end text edit (as if escape pressed, in FuDraw)
+ aViewData.GetDispatcher().Execute( pDrawActual->GetSlotID(),
+ SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+
+ pDrawView->ScEndTextEdit();
+ pDrawView->UnmarkAll();
+
+ if (!pViewSh->IsDrawSelMode())
+ pViewSh->SetDrawShell( false );
+}
+
+bool ScTabView::IsDrawTextEdit() const
+{
+ if (pDrawView)
+ return pDrawView->IsTextEdit();
+ else
+ return false;
+}
+
+SvxZoomType ScTabView::GetZoomType() const
+{
+ return aViewData.GetZoomType();
+}
+
+void ScTabView::SetZoomType( SvxZoomType eNew, bool bAll )
+{
+ aViewData.SetZoomType( eNew, bAll );
+}
+
+void ScTabView::SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll )
+{
+ aViewData.SetZoom( rNewX, rNewY, bAll );
+ if (pDrawView)
+ pDrawView->RecalcScale();
+ ZoomChanged();
+}
+
+void ScTabView::RefreshZoom()
+{
+ aViewData.RefreshZoom();
+ if (pDrawView)
+ pDrawView->RecalcScale();
+ ZoomChanged();
+}
+
+void ScTabView::SetPagebreakMode( bool bSet )
+{
+ aViewData.SetPagebreakMode(bSet);
+ if (pDrawView)
+ pDrawView->RecalcScale();
+ ZoomChanged();
+}
+
+void ScTabView::ResetDrawDragMode()
+{
+ if (pDrawView)
+ pDrawView->SetDragMode( SdrDragMode::Move );
+}
+
+void ScTabView::ViewOptionsHasChanged( bool bHScrollChanged, bool bGraphicsChanged )
+{
+ // create DrawView when grid should be displayed
+ if ( !pDrawView && aViewData.GetOptions().GetGridOptions().GetGridVisible() )
+ MakeDrawLayer();
+
+ if (pDrawView)
+ pDrawView->UpdateUserViewOptions();
+
+ if (bGraphicsChanged)
+ DrawEnableAnim(true); // DrawEnableAnim checks the options state
+
+ // if TabBar is set to visible, make sure its size is not 0
+ bool bGrow = ( aViewData.IsTabMode() && pTabControl->GetSizePixel().Width() <= 0 );
+
+ // if ScrollBar is set to visible, TabBar must make room
+ bool bShrink = ( bHScrollChanged && aViewData.IsTabMode() && aViewData.IsHScrollMode() &&
+ pTabControl->GetSizePixel().Width() > SC_TABBAR_DEFWIDTH );
+
+ if ( bGrow || bShrink )
+ {
+ Size aSize = pTabControl->GetSizePixel();
+ aSize.setWidth( SC_TABBAR_DEFWIDTH ); // initial size
+ pTabControl->SetSizePixel(aSize); // DoResize is called later...
+ }
+}
+
+// helper function against including the drawing layer
+
+void ScTabView::DrawMarkListHasChanged()
+{
+ if ( pDrawView )
+ pDrawView->MarkListHasChanged();
+}
+
+void ScTabView::UpdateAnchorHandles()
+{
+ if ( pDrawView )
+ pDrawView->AdjustMarkHdl();
+}
+
+void ScTabView::UpdateIMap( SdrObject* pObj )
+{
+ if ( pDrawView )
+ pDrawView->UpdateIMap( pObj );
+}
+
+void ScTabView::DrawEnableAnim(bool bSet)
+{
+ sal_uInt16 i;
+ if ( !pDrawView )
+ return;
+
+ // don't start animations if display of graphics is disabled
+ // graphics are controlled by VOBJ_TYPE_OLE
+ if ( bSet && aViewData.GetOptions().GetObjMode(VOBJ_TYPE_OLE) == VOBJ_MODE_SHOW )
+ {
+ if ( !pDrawView->IsAnimationEnabled() )
+ {
+ pDrawView->SetAnimationEnabled();
+
+ // animated GIFs must be restarted:
+ ScDocument& rDoc = aViewData.GetDocument();
+ for (i=0; i<4; i++)
+ if ( pGridWin[i] && pGridWin[i]->IsVisible() )
+ rDoc.StartAnimations( aViewData.GetTabNo() );
+ }
+ }
+ else
+ {
+ pDrawView->SetAnimationEnabled(false);
+ }
+}
+
+void ScTabView::UpdateDrawTextOutliner()
+{
+ if ( pDrawView )
+ {
+ Outliner* pOL = pDrawView->GetTextEditOutliner();
+ if (pOL)
+ aViewData.UpdateOutlinerFlags( *pOL );
+ }
+}
+
+void ScTabView::DigitLanguageChanged()
+{
+ LanguageType eNewLang = ScModule::GetOptDigitLanguage();
+ for (VclPtr<ScGridWindow> & pWin : pGridWin)
+ if ( pWin )
+ pWin->GetOutDev()->SetDigitLanguage( eNewLang );
+}
+
+void ScTabView::ScrollToObject( const SdrObject* pDrawObj )
+{
+ if ( pDrawObj )
+ {
+ // #i118524# use the BoundRect, this defines the visible area
+ MakeVisible(pDrawObj->GetCurrentBoundRect());
+ }
+}
+
+void ScTabView::MakeVisible( const tools::Rectangle& rHMMRect )
+{
+ vcl::Window* pWin = GetActiveWin();
+ Size aWinSize = pWin->GetOutputSizePixel();
+ SCTAB nTab = aViewData.GetTabNo();
+
+ tools::Rectangle aRect = pWin->LogicToPixel( rHMMRect );
+
+ tools::Long nScrollX=0, nScrollY=0; // pixel
+
+ if ( aRect.Right() >= aWinSize.Width() ) // right out
+ {
+ nScrollX = aRect.Right() - aWinSize.Width() + 1; // right border visible
+ if ( aRect.Left() < nScrollX )
+ nScrollX = aRect.Left(); // left visible (if too big)
+ }
+ if ( aRect.Bottom() >= aWinSize.Height() ) // bottom out
+ {
+ nScrollY = aRect.Bottom() - aWinSize.Height() + 1; // bottom border visible
+ if ( aRect.Top() < nScrollY )
+ nScrollY = aRect.Top(); // top visible (if too big)
+ }
+
+ if ( aRect.Left() < 0 ) // left out
+ nScrollX = aRect.Left(); // left border visible
+ if ( aRect.Top() < 0 ) // top out
+ nScrollY = aRect.Top(); // top border visible
+
+ if (!(nScrollX || nScrollY))
+ return;
+
+ ScDocument& rDoc = aViewData.GetDocument();
+ if ( rDoc.IsNegativePage( nTab ) )
+ nScrollX = -nScrollX;
+
+ double nPPTX = aViewData.GetPPTX();
+ double nPPTY = aViewData.GetPPTY();
+ ScSplitPos eWhich = aViewData.GetActivePart();
+ SCCOL nPosX = aViewData.GetPosX(WhichH(eWhich));
+ SCROW nPosY = aViewData.GetPosY(WhichV(eWhich));
+
+ tools::Long nLinesX=0, nLinesY=0; // columns/rows - scroll at least nScrollX/Y
+
+ if (nScrollX > 0)
+ while (nScrollX > 0 && nPosX < rDoc.MaxCol())
+ {
+ nScrollX -= static_cast<tools::Long>( rDoc.GetColWidth(nPosX, nTab) * nPPTX );
+ ++nPosX;
+ ++nLinesX;
+ }
+ else if (nScrollX < 0)
+ while (nScrollX < 0 && nPosX > 0)
+ {
+ --nPosX;
+ nScrollX += static_cast<tools::Long>( rDoc.GetColWidth(nPosX, nTab) * nPPTX );
+ --nLinesX;
+ }
+
+ if (nScrollY > 0)
+ while (nScrollY > 0 && nPosY < rDoc.MaxRow())
+ {
+ nScrollY -= static_cast<tools::Long>( rDoc.GetRowHeight(nPosY, nTab) * nPPTY );
+ ++nPosY;
+ ++nLinesY;
+ }
+ else if (nScrollY < 0)
+ while (nScrollY < 0 && nPosY > 0)
+ {
+ --nPosY;
+ nScrollY += static_cast<tools::Long>( rDoc.GetRowHeight(nPosY, nTab) * nPPTY );
+ --nLinesY;
+ }
+
+ ScrollLines( nLinesX, nLinesY ); // execute
+}
+
+void ScTabView::SetBrushDocument( ScDocumentUniquePtr pNew, bool bLock )
+{
+ pDrawBrushSet.reset();
+ pBrushDocument = std::move(pNew);
+
+ bLockPaintBrush = bLock;
+
+ aViewData.GetBindings().Invalidate(SID_FORMATPAINTBRUSH);
+}
+
+void ScTabView::SetDrawBrushSet( std::unique_ptr<SfxItemSet> pNew, bool bLock )
+{
+ pBrushDocument.reset();
+ pDrawBrushSet = std::move(pNew);
+
+ bLockPaintBrush = bLock;
+
+ aViewData.GetBindings().Invalidate(SID_FORMATPAINTBRUSH);
+}
+
+void ScTabView::ResetBrushDocument()
+{
+ if ( HasPaintBrush() )
+ {
+ SetBrushDocument( nullptr, false );
+ SetActivePointer( aViewData.IsThemedCursor() ? PointerStyle::FatCross : PointerStyle::Arrow ); // switch pointers also when ended with escape key
+ }
+}
+
+void ScTabView::OnLOKNoteStateChanged(const ScPostIt* pNote)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ const SdrCaptionObj* pCaption = pNote->GetCaption();
+ if (!pCaption) return;
+
+ tools::Rectangle aRect = pCaption->GetLogicRect();
+ basegfx::B2DRange aTailRange = pCaption->getTailPolygon().getB2DRange();
+ tools::Rectangle aTailRect(aTailRange.getMinX(), aTailRange.getMinY(),
+ aTailRange.getMaxX(), aTailRange.getMaxY());
+ aRect.Union( aTailRect );
+
+ // This is a temporary workaround: sometime in tiled rendering mode
+ // the tip of the note arrow is misplaced by a fixed offset.
+ // The value used below is enough to get the tile, where the arrow tip is
+ // placed, invalidated.
+ const int nBorderSize = 200;
+ tools::Rectangle aInvalidRect = aRect;
+ aInvalidRect.AdjustLeft( -nBorderSize );
+ aInvalidRect.AdjustRight( nBorderSize );
+ aInvalidRect.AdjustTop( -nBorderSize );
+ aInvalidRect.AdjustBottom( nBorderSize );
+
+ SfxViewShell* pCurrentViewShell = SfxViewShell::Current();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ for (auto& pWin: pTabViewShell->pGridWin)
+ {
+ if (pWin && pWin->IsVisible())
+ {
+ pWin->Invalidate(aInvalidRect);
+ }
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh.cxx b/sc/source/ui/view/tabvwsh.cxx
new file mode 100644
index 0000000000..f64b960485
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh.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/imapdlg.hxx>
+#include <svx/srchdlg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
+#include <sfx2/viewfac.hxx>
+
+#include <cellvalue.hxx>
+
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <reffact.hxx>
+#include <sc.hrc>
+#include <spelldialog.hxx>
+#include <formulacell.hxx>
+#include <searchresults.hxx>
+
+ // needed for -fsanitize=function visibility of typeinfo for functions of
+ // type void(SfxShell*,SfxRequest&) defined in scslots.hxx
+#define ShellClass_ScTabViewShell
+#include <scslots.hxx>
+
+
+SFX_IMPL_INTERFACE(ScTabViewShell, SfxViewShell)
+
+void ScTabViewShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
+ ToolbarId::Objectbar_Tools);
+
+ GetStaticInterface()->RegisterChildWindow(FID_INPUTLINE_STATUS);
+ GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+
+ GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true);
+
+ GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScNameDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScNameDefDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSolverDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScOptSolverDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScXMLSourceDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScPivotLayoutWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScTabOpDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFilterDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSpecialFilterDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScDbNameDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScConsolidateDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScPrintAreasDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScColRowNameRangesDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFormulaDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFormulaDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScAcceptChgDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScHighlightChgDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSimpleRefDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SID_HYPERLINK_DIALOG);
+ GetStaticInterface()->RegisterChildWindow(ScSpellDialogChildWindow::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScValidityRefChildWin::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::ConditionalFormatEasyDialogWrapper::GetChildWindowId());
+
+ GetStaticInterface()->RegisterChildWindow(ScRandomNumberGeneratorDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScSamplingDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScDescriptiveStatisticsDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScAnalysisOfVarianceDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScCorrelationDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScCovarianceDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScExponentialSmoothingDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScMovingAverageDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScRegressionDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScTTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScZTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScChiSquareTestDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScFourierAnalysisDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(ScCondFormatDlgWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::SparklineDialogWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(sc::SparklineDataRangeDialogWrapper::GetChildWindowId());
+}
+
+SFX_IMPL_NAMED_VIEWFACTORY( ScTabViewShell, "Default" )
+{
+ SFX_VIEW_REGISTRATION(ScDocShell);
+}
+
+OUString ScTabViewShell::GetFormula(const ScAddress& rAddress)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScRefCellValue aCell(rDoc, rAddress);
+ if (!aCell.isEmpty() && aCell.getType() == CELLTYPE_FORMULA)
+ {
+ return aCell.getFormula()->GetFormula();
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh2.cxx b/sc/source/ui/view/tabvwsh2.cxx
new file mode 100644
index 0000000000..b2fef44d96
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh2.cxx
@@ -0,0 +1,472 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/lok.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/whiter.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <svl/cjkoptions.hxx>
+#include <sfx2/dispatch.hxx>
+#include <tools/UnitConversion.hxx>
+
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+#include <fuconrec.hxx>
+#include <fuconpol.hxx>
+#include <fuconarc.hxx>
+#include <fuconuno.hxx>
+#include <fusel.hxx>
+#include <futext.hxx>
+#include <fuinsert.hxx>
+#include <sc.hrc>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <gridwin.hxx>
+
+// Create default drawing objects via keyboard
+#include <svx/svdpagv.hxx>
+#include <svl/stritem.hxx>
+#include <fuconcustomshape.hxx>
+
+SdrView* ScTabViewShell::GetDrawView() const
+{
+ return const_cast<ScTabViewShell*>(this)->GetScDrawView(); // GetScDrawView is non-const
+}
+
+void ScTabViewShell::WindowChanged()
+{
+ vcl::Window* pWin = GetActiveWin();
+
+ ScDrawView* pDrView = GetScDrawView();
+ if (pDrView)
+ pDrView->SetActualWin(pWin->GetOutDev());
+
+ FuPoor* pFunc = GetDrawFuncPtr();
+ if (pFunc)
+ pFunc->SetWindow(pWin);
+
+ // when font from InputContext is used,
+ // this must be moved to change of cursor position:
+ UpdateInputContext();
+}
+
+void ScTabViewShell::ExecDraw(SfxRequest& rReq)
+{
+ SC_MOD()->InputEnterHandler();
+ UpdateInputHandler();
+
+ MakeDrawLayer();
+
+ ScTabView* pTabView = GetViewData().GetView();
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+
+ vcl::Window* pWin = pTabView->GetActiveWin();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ SdrModel& rModel = pView->GetModel();
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ sal_uInt16 nNewId = rReq.GetSlot();
+
+ if ( nNewId == SID_DRAW_CHART )
+ {
+ // #i71254# directly insert a chart instead of drawing its output rectangle
+ FuInsertChart(*this, pWin, pView, &rModel, rReq, LINK( this, ScTabViewShell, DialogClosedHdl ));
+ return;
+ }
+
+ if ( nNewId == SID_DRAW_SELECT )
+ nNewId = SID_OBJECT_SELECT;
+
+ SdrObjKind eNewFormObjKind = SdrObjKind::NONE;
+ if (nNewId == SID_FM_CREATE_CONTROL)
+ {
+ const SfxUInt16Item* pIdentifierItem = rReq.GetArg<SfxUInt16Item>(SID_FM_CONTROL_IDENTIFIER);
+ if (pIdentifierItem)
+ eNewFormObjKind = static_cast<SdrObjKind>(pIdentifierItem->GetValue());
+ }
+
+ OUString sStringItemValue;
+ if ( pArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( pArgs->GetItemState( nNewId, true, &pItem ) == SfxItemState::SET )
+ if (auto pStringItem = dynamic_cast<const SfxStringItem*>(pItem) )
+ sStringItemValue = pStringItem->GetValue();
+ }
+ bool bSwitchCustom = ( !sStringItemValue.isEmpty() && !sDrawCustom.isEmpty() && sStringItemValue != sDrawCustom );
+
+ if (nNewId == SID_INSERT_FRAME) // from Tbx button
+ nNewId = SID_DRAW_TEXT;
+
+ // CTRL-SID_OBJECT_SELECT is used to select the first object,
+ // but not if SID_OBJECT_SELECT is the result of clicking a create function again,
+ // so this must be tested before changing nNewId below.
+ bool bSelectFirst = ( nNewId == SID_OBJECT_SELECT && (rReq.GetModifier() & KEY_MOD1) );
+
+ bool bEx = IsDrawSelMode();
+ if ( rReq.GetModifier() & KEY_MOD1 )
+ {
+ // always allow keyboard selection also on background layer
+ // also allow creation of default objects if the same object type
+ // was already active
+ bEx = true;
+ }
+ else if ( nNewId == nDrawSfxId && ( nNewId != SID_FM_CREATE_CONTROL ||
+ eNewFormObjKind == eFormObjKind || eNewFormObjKind == SdrObjKind::NONE ) && !bSwitchCustom )
+ {
+ // #i52871# if a different custom shape is selected, the slot id can be the same,
+ // so the custom shape type string has to be compared, too.
+
+ // SID_FM_CREATE_CONTROL with eNewFormObjKind==OBJ_NONE (without parameter) comes
+ // from FuConstruct::SimpleMouseButtonUp when deactivating
+ // Execute for the form shell, to deselect the controller
+ if ( nNewId == SID_FM_CREATE_CONTROL )
+ {
+ GetViewData().GetDispatcher().Execute(SID_FM_LEAVE_CREATE);
+ GetViewFrame().GetBindings().InvalidateAll(false);
+ //! what kind of slot does the weird controller really need to display this????
+ }
+
+ bEx = !bEx;
+ nNewId = SID_OBJECT_SELECT;
+ }
+ else
+ bEx = true;
+
+ if ( nDrawSfxId == SID_FM_CREATE_CONTROL && nNewId != nDrawSfxId )
+ {
+ // switching from control- to paint function -> deselect in control-controller
+ GetViewData().GetDispatcher().Execute(SID_FM_LEAVE_CREATE);
+ GetViewFrame().GetBindings().InvalidateAll(false);
+ //! what kind of slot does the weird controller really need to display this????
+ }
+
+ SetDrawSelMode(bEx);
+
+ pView->LockBackgroundLayer( !bEx );
+
+ if ( bSelectFirst )
+ {
+ // select first draw object if none is selected yet
+ if(!pView->AreObjectsMarked())
+ {
+ // select first object
+ pView->UnmarkAllObj();
+ pView->MarkNextObj(true);
+
+ // ...and make it visible
+ if(pView->AreObjectsMarked())
+ pView->MakeVisible(pView->GetAllMarkedRect(), *pWin);
+ }
+ }
+
+ nDrawSfxId = nNewId;
+ sDrawCustom.clear(); // value is set below for custom shapes
+
+ if (nNewId == SID_DRAW_TEXT || nNewId == SID_DRAW_TEXT_VERTICAL
+ || nNewId == SID_DRAW_TEXT_MARQUEE || nNewId == SID_DRAW_NOTEEDIT)
+ SetDrawTextShell(true);
+ else
+ {
+ if (bEx || pView->GetMarkedObjectList().GetMarkCount() != 0)
+ SetDrawShellOrSub();
+ else
+ SetDrawShell(false);
+ }
+
+ if (pTabView->GetDrawFuncPtr())
+ {
+ if (pTabView->GetDrawFuncOldPtr() != pTabView->GetDrawFuncPtr())
+ delete pTabView->GetDrawFuncOldPtr();
+
+ pTabView->GetDrawFuncPtr()->Deactivate();
+ pTabView->SetDrawFuncOldPtr(pTabView->GetDrawFuncPtr());
+ pTabView->SetDrawFuncPtr(nullptr);
+ }
+
+ SfxRequest aNewReq(rReq);
+ aNewReq.SetSlot(nDrawSfxId);
+
+ assert(nNewId != SID_DRAW_CHART); //#i71254# handled already above
+
+ // for LibreOfficeKit - choosing a shape should construct it directly
+ bool bCreateDirectly = false;
+
+ switch (nNewId)
+ {
+ case SID_OBJECT_SELECT:
+ // not always switch back
+ if(pView->GetMarkedObjectList().GetMarkCount() == 0) SetDrawShell(bEx);
+ pTabView->SetDrawFuncPtr(new FuSelection(*this, pWin, pView, &rModel, aNewReq));
+ break;
+
+ case SID_DRAW_LINE:
+ case SID_DRAW_XLINE:
+ case SID_LINE_ARROW_END:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ case SID_LINE_ARROW_START:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ case SID_LINE_ARROWS:
+ case SID_DRAW_RECT:
+ case SID_DRAW_ELLIPSE:
+ case SID_DRAW_MEASURELINE:
+ pTabView->SetDrawFuncPtr(new FuConstRectangle(*this, pWin, pView, &rModel, aNewReq));
+ bCreateDirectly = comphelper::LibreOfficeKit::isActive();
+ break;
+
+ case SID_DRAW_CAPTION:
+ case SID_DRAW_CAPTION_VERTICAL:
+ pTabView->SetDrawFuncPtr(new FuConstRectangle(*this, pWin, pView, &rModel, aNewReq));
+ pView->SetFrameDragSingles( false );
+ rBindings.Invalidate( SID_BEZIER_EDIT );
+ break;
+
+ case SID_DRAW_XPOLYGON:
+ case SID_DRAW_XPOLYGON_NOFILL:
+ case SID_DRAW_POLYGON:
+ case SID_DRAW_POLYGON_NOFILL:
+ case SID_DRAW_BEZIER_NOFILL:
+ case SID_DRAW_BEZIER_FILL:
+ case SID_DRAW_FREELINE:
+ case SID_DRAW_FREELINE_NOFILL:
+ pTabView->SetDrawFuncPtr(new FuConstPolygon(*this, pWin, pView, &rModel, aNewReq));
+ break;
+
+ case SID_DRAW_ARC:
+ case SID_DRAW_PIE:
+ case SID_DRAW_CIRCLECUT:
+ pTabView->SetDrawFuncPtr(new FuConstArc(*this, pWin, pView, &rModel, aNewReq));
+ break;
+
+ case SID_DRAW_TEXT:
+ case SID_DRAW_TEXT_VERTICAL:
+ case SID_DRAW_TEXT_MARQUEE:
+ case SID_DRAW_NOTEEDIT:
+ pTabView->SetDrawFuncPtr(new FuText(*this, pWin, pView, &rModel, aNewReq));
+ bCreateDirectly = comphelper::LibreOfficeKit::isActive();
+ break;
+
+ case SID_FM_CREATE_CONTROL:
+ SetDrawFormShell(true);
+ pTabView->SetDrawFuncPtr(new FuConstUnoControl(*this, pWin, pView, &rModel, aNewReq));
+ eFormObjKind = eNewFormObjKind;
+ break;
+
+ case SID_DRAWTBX_CS_BASIC :
+ case SID_DRAWTBX_CS_SYMBOL :
+ case SID_DRAWTBX_CS_ARROW :
+ case SID_DRAWTBX_CS_FLOWCHART :
+ case SID_DRAWTBX_CS_CALLOUT :
+ case SID_DRAWTBX_CS_STAR :
+ case SID_DRAW_CS_ID :
+ {
+ pTabView->SetDrawFuncPtr(new FuConstCustomShape(*this, pWin, pView, &rModel, aNewReq));
+
+ bCreateDirectly = comphelper::LibreOfficeKit::isActive();
+
+ if ( nNewId != SID_DRAW_CS_ID )
+ {
+ const SfxStringItem* pEnumCommand = rReq.GetArg<SfxStringItem>(nNewId);
+ if ( pEnumCommand )
+ {
+ SfxBindings& rBind = GetViewFrame().GetBindings();
+ rBind.Invalidate( nNewId );
+ rBind.Update( nNewId );
+
+ sDrawCustom = pEnumCommand->GetValue(); // to detect when a different shape type is selected
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (pTabView->GetDrawFuncPtr())
+ pTabView->GetDrawFuncPtr()->Activate();
+
+ rReq.Done();
+
+ Invalidate();
+
+ // Create default drawing objects via keyboard
+ // with qualifier construct directly
+ FuPoor* pFuActual = GetDrawFuncPtr();
+
+ if(!(pFuActual && ((rReq.GetModifier() & KEY_MOD1) || bCreateDirectly)))
+ return;
+
+ // Create default drawing objects via keyboard
+ const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions();
+ sal_uInt32 nDefaultObjectSizeWidth = rAppOpt.GetDefaultObjectSizeWidth();
+ sal_uInt32 nDefaultObjectSizeHeight = rAppOpt.GetDefaultObjectSizeHeight();
+
+ // calc position and size
+ bool bLOKIsActive = comphelper::LibreOfficeKit::isActive();
+ Point aInsertPos;
+ if(!bLOKIsActive)
+ {
+ tools::Rectangle aVisArea = pWin->PixelToLogic(tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel()));
+ aInsertPos = aVisArea.Center();
+ aInsertPos.AdjustX( -sal_Int32(nDefaultObjectSizeWidth / 2) );
+ aInsertPos.AdjustY( -sal_Int32(nDefaultObjectSizeHeight / 2) );
+ }
+ else
+ {
+ ScViewData& rViewData = GetViewData();
+ tools::Long nLayoutSign = rViewData.GetDocument().IsLayoutRTL(rViewData.GetTabNo()) ? -1 : 1;
+ aInsertPos = rViewData.getLOKVisibleArea().Center();
+ if (comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ aInsertPos = rViewData.GetPrintTwipsPosFromTileTwips(aInsertPos);
+
+ aInsertPos.setX(nLayoutSign * convertTwipToMm100(aInsertPos.X()));
+ aInsertPos.setY(convertTwipToMm100(aInsertPos.Y()));
+
+ aInsertPos.AdjustX( -sal_Int32(nDefaultObjectSizeWidth / 2) );
+ aInsertPos.AdjustY( -sal_Int32(nDefaultObjectSizeHeight / 2) );
+ }
+
+ tools::Rectangle aNewObjectRectangle(aInsertPos, Size(nDefaultObjectSizeWidth, nDefaultObjectSizeHeight));
+
+ ScDrawView* pDrView = GetScDrawView();
+
+ if(!pDrView)
+ return;
+
+ SdrPageView* pPageView = pDrView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ // create the default object
+ rtl::Reference<SdrObject> pObj = pFuActual->CreateDefaultObject(nNewId, aNewObjectRectangle);
+
+ if(!pObj)
+ return;
+
+ // insert into page
+ pView->InsertObjectAtView(pObj.get(), *pPageView);
+
+ switch ( nNewId )
+ {
+ case SID_DRAW_CAPTION:
+ case SID_DRAW_CAPTION_VERTICAL:
+ case SID_DRAW_TEXT:
+ case SID_DRAW_TEXT_VERTICAL:
+ // use KeyInput to start edit mode (FuText is created).
+ // For FuText objects, edit mode is handled within CreateDefaultObject.
+ // KEY_F2 is handled in FuDraw::KeyInput.
+
+ pFuActual->KeyInput( KeyEvent( 0, vcl::KeyCode( KEY_F2 ) ) );
+ break;
+ default:
+ break;
+ }
+}
+
+void ScTabViewShell::GetDrawState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_DRAW_CHART:
+ {
+ bool bOle = GetViewFrame().GetFrame().IsInPlace();
+ if ( bOle || !SvtModuleOptions().IsChart() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_DRAW_LINE:
+ case SID_DRAW_XLINE:
+ case SID_LINE_ARROW_END:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ case SID_LINE_ARROW_START:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ case SID_LINE_ARROWS:
+ case SID_DRAW_MEASURELINE:
+ case SID_DRAW_RECT:
+ case SID_DRAW_ELLIPSE:
+ case SID_DRAW_POLYGON:
+ case SID_DRAW_POLYGON_NOFILL:
+ case SID_DRAW_XPOLYGON:
+ case SID_DRAW_XPOLYGON_NOFILL:
+ case SID_DRAW_BEZIER_FILL:
+ case SID_DRAW_BEZIER_NOFILL:
+ case SID_DRAW_FREELINE:
+ case SID_DRAW_FREELINE_NOFILL:
+ case SID_DRAW_ARC:
+ case SID_DRAW_PIE:
+ case SID_DRAW_CIRCLECUT:
+ case SID_DRAW_TEXT:
+ case SID_DRAW_TEXT_MARQUEE:
+ case SID_DRAW_CAPTION:
+ rSet.Put( SfxBoolItem( nWhich, nDrawSfxId == nWhich ) );
+ break;
+
+ case SID_DRAW_TEXT_VERTICAL:
+ case SID_DRAW_CAPTION_VERTICAL:
+ if ( !SvtCJKOptions::IsVerticalTextEnabled() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, nDrawSfxId == nWhich ) );
+ break;
+
+ case SID_OBJECT_SELECT: // important for the old control-controller
+ rSet.Put( SfxBoolItem( nWhich, nDrawSfxId == SID_OBJECT_SELECT && IsDrawSelMode() ) );
+ break;
+
+ case SID_DRAWTBX_CS_BASIC:
+ case SID_DRAWTBX_CS_SYMBOL:
+ case SID_DRAWTBX_CS_ARROW:
+ case SID_DRAWTBX_CS_FLOWCHART:
+ case SID_DRAWTBX_CS_CALLOUT:
+ case SID_DRAWTBX_CS_STAR:
+ rSet.Put( SfxStringItem( nWhich, nDrawSfxId == nWhich ? sDrawCustom : OUString() ) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+bool ScTabViewShell::SelectObject( std::u16string_view rName )
+{
+ ScDrawView* pView = GetViewData().GetScDrawView();
+ if (!pView)
+ return false;
+
+ bool bFound = pView->SelectObject( rName );
+ // DrawShell etc. is handled in MarkListHasChanged
+
+ return bFound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh3.cxx b/sc/source/ui/view/tabvwsh3.cxx
new file mode 100644
index 0000000000..208748b711
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh3.cxx
@@ -0,0 +1,1398 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/passwd.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <svl/ptitem.hxx>
+#include <svl/stritem.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/objface.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <inputwin.hxx>
+#include <scresid.hxx>
+#include <docsh.hxx>
+#include <rangeutl.hxx>
+#include <reffact.hxx>
+#include <tabprotection.hxx>
+#include <protectiondlg.hxx>
+#include <markdata.hxx>
+
+#include <svl/ilstitem.hxx>
+#include <vector>
+
+#include <svx/zoomslideritem.hxx>
+#include <svx/svxdlg.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <scabstdlg.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+#include <basegfx/utils/zoomtools.hxx>
+
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/dialog/ThemeDialog.hxx>
+#include <ThemeColorChanger.hxx>
+
+namespace
+{
+ void collectUIInformation(const OUString& aZoom)
+ {
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{"ZOOM", aZoom}};
+ aDescription.aAction = "SET";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+ aDescription.aParent = "MainWindow";
+ UITestLogger::getInstance().logEvent(aDescription);
+ }
+
+ enum class DetectFlags
+ {
+ NONE,
+ RANGE,
+ ADDRESS
+ };
+
+ struct ScRefFlagsAndType
+ {
+ ScRefFlags nResult;
+ DetectFlags eDetected;
+ };
+
+ ScRefFlagsAndType lcl_ParseRangeOrAddress(ScRange& rScRange, ScAddress& rScAddress,
+ const OUString& aAddress, const ScDocument& rDoc,
+ SCCOL nCurCol, SCROW nCurRow)
+ {
+ ScRefFlagsAndType aRet;
+
+ // Relative address parsing needs current position.
+ // row,col parameters, not col,row!
+ ScAddress::Details aDetails( rDoc.GetAddressConvention(), nCurRow, nCurCol);
+
+ // start with the address convention set in the document
+ aRet.nResult = rScRange.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ // try the default Calc (A1) address convention
+ aRet.nResult = rScRange.Parse(aAddress, rDoc);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ // try the Excel A1 address convention
+ aRet.nResult = rScRange.Parse(aAddress, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ // try the Excel A1 address convention
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ // try Excel R1C1 address convention
+ aDetails.eConv = formula::FormulaGrammar::CONV_XL_R1C1;
+ aRet.nResult = rScRange.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::RANGE;
+ return aRet;
+ }
+
+ aRet.nResult = rScAddress.Parse(aAddress, rDoc, aDetails);
+ if (aRet.nResult & ScRefFlags::VALID)
+ {
+ aRet.eDetected = DetectFlags::ADDRESS;
+ return aRet;
+ }
+
+ aRet.nResult = ScRefFlags::ZERO;
+ aRet.eDetected = DetectFlags::NONE;
+
+ return aRet;
+ }
+}
+
+void ScTabViewShell::Execute( SfxRequest& rReq )
+{
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ SfxBindings& rBindings = rThisFrame.GetBindings();
+ ScModule* pScMod = SC_MOD();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+ if (nSlot != SID_CURRENTCELL) // comes with MouseButtonUp
+ HideListBox(); // Autofilter-DropDown-Listbox
+
+ switch ( nSlot )
+ {
+ case FID_INSERT_FILE:
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState(FID_INSERT_FILE,true,&pItem) == SfxItemState::SET )
+ {
+ OUString aFileName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ // insert position
+
+ Point aInsertPos;
+ if ( pReqArgs->GetItemState(FN_PARAM_1,true,&pItem) == SfxItemState::SET )
+ aInsertPos = static_cast<const SfxPointItem*>(pItem)->GetValue();
+ else
+ aInsertPos = GetInsertPos();
+
+ // as Link?
+
+ bool bAsLink = false;
+ if ( pReqArgs->GetItemState(FN_PARAM_2,true,&pItem) == SfxItemState::SET )
+ bAsLink = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // execute
+
+ PasteFile( aInsertPos, aFileName, bAsLink );
+ }
+ }
+ break;
+
+ case SID_OPENDLG_EDIT_PRINTAREA:
+ {
+ sal_uInt16 nId = ScPrintAreasDlgWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case SID_CHANGE_PRINTAREA:
+ {
+ if ( pReqArgs ) // OK from dialog
+ {
+ OUString aPrintStr;
+ OUString aRowStr;
+ OUString aColStr;
+ bool bEntire = false;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_CHANGE_PRINTAREA, true, &pItem ) == SfxItemState::SET )
+ aPrintStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if ( pReqArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET )
+ aRowStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if ( pReqArgs->GetItemState( FN_PARAM_3, true, &pItem ) == SfxItemState::SET )
+ aColStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ if ( pReqArgs->GetItemState( FN_PARAM_4, true, &pItem ) == SfxItemState::SET )
+ bEntire = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ SetPrintRanges( bEntire, &aPrintStr, &aColStr, &aRowStr, false );
+
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_ADD_PRINTAREA:
+ case SID_DEFINE_PRINTAREA: // menu or basic
+ {
+ bool bAdd = ( nSlot == SID_ADD_PRINTAREA );
+ if ( pReqArgs )
+ {
+ OUString aPrintStr;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( SID_DEFINE_PRINTAREA, true, &pItem ) == SfxItemState::SET )
+ aPrintStr = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ SetPrintRanges( false, &aPrintStr, nullptr, nullptr, bAdd );
+ }
+ else
+ {
+ SetPrintRanges( false, nullptr, nullptr, nullptr, bAdd ); // from selection
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_DELETE_PRINTAREA:
+ {
+ // Clear currently defined print range if any, and reset it to
+ // print entire sheet which is the default.
+ OUString aEmpty;
+ SetPrintRanges(true, &aEmpty, nullptr, nullptr, false);
+ rReq.Done();
+ }
+ break;
+
+ case FID_DEL_MANUALBREAKS:
+ RemoveManualBreaks();
+ rReq.Done();
+ break;
+
+ case FID_ADJUST_PRINTZOOM:
+ AdjustPrintZoom();
+ rReq.Done();
+ break;
+
+ case FID_RESET_PRINTZOOM:
+ SetPrintZoom( 100 ); // 100%, not on pages
+ rReq.Done();
+ break;
+
+ case SID_FORMATPAGE:
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ GetViewData().GetDocShell()->
+ ExecutePageStyle( *this, rReq, GetViewData().GetTabNo() );
+ break;
+
+ case SID_JUMPTOMARK:
+ case SID_CURRENTCELL:
+ if ( pReqArgs )
+ {
+ OUString aAddress;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ aAddress = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ else if ( nSlot == SID_JUMPTOMARK && pReqArgs->GetItemState(
+ SID_JUMPTOMARK, true, &pItem ) == SfxItemState::SET )
+ aAddress = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ // #i14927# SID_CURRENTCELL with a single cell must unmark if FN_PARAM_1
+ // isn't set (for recorded macros, because IsAPI is no longer available).
+ // ScGridWindow::MouseButtonUp no longer executes the slot for a single
+ // cell if there is a multi selection.
+ bool bUnmark = ( nSlot == SID_CURRENTCELL );
+ if ( pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET )
+ bUnmark = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ bool bAlignToCursor = true;
+ if (pReqArgs->GetItemState(FN_PARAM_2, true, &pItem) == SfxItemState::SET)
+ bAlignToCursor = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ bool bForceGlobalName = false;
+ if (pReqArgs->GetItemState(FN_PARAM_3, true, &pItem) == SfxItemState::SET)
+ bForceGlobalName = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ if ( nSlot == SID_JUMPTOMARK )
+ {
+ // URL has to be decoded for escaped characters (%20)
+ aAddress = INetURLObject::decode( aAddress,
+ INetURLObject::DecodeMechanism::WithCharset );
+ }
+
+ bool bFound = false;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ ScRange aScRange;
+ ScAddress aScAddress;
+ ScRefFlagsAndType aResult = lcl_ParseRangeOrAddress(aScRange, aScAddress, aAddress, rDoc,
+ rViewData.GetCurX(), rViewData.GetCurY());
+ ScRefFlags nResult = aResult.nResult;
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bMark = true;
+
+ // Is this a range ?
+ if (aResult.eDetected == DetectFlags::RANGE)
+ {
+ if ( nResult & ScRefFlags::TAB_3D )
+ {
+ if( aScRange.aStart.Tab() != nTab )
+ {
+ nTab = aScRange.aStart.Tab();
+ SetTabNo( nTab );
+ }
+ }
+ else
+ {
+ aScRange.aStart.SetTab( nTab );
+ aScRange.aEnd.SetTab( nTab );
+ }
+ }
+ // Is this a cell ?
+ else if (aResult.eDetected == DetectFlags::ADDRESS)
+ {
+ if ( nResult & ScRefFlags::TAB_3D )
+ {
+ if( aScAddress.Tab() != nTab )
+ {
+ nTab = aScAddress.Tab();
+ SetTabNo( nTab );
+ }
+ }
+ else
+ aScAddress.SetTab( nTab );
+
+ aScRange = ScRange( aScAddress, aScAddress );
+ // cells should not be marked
+ bMark = false;
+ }
+ // Is it a named area (first named ranges then database ranges)?
+ else
+ {
+ const RutlNameScope eScope = (bForceGlobalName ? RUTL_NAMES_GLOBAL : RUTL_NAMES);
+ ScAddress::Details aDetails( rDoc.GetAddressConvention(), rViewData.GetCurY(), rViewData.GetCurX());
+ if (ScRangeUtil::MakeRangeFromName( aAddress, rDoc, nTab, aScRange, eScope, aDetails, true) ||
+ ScRangeUtil::MakeRangeFromName( aAddress, rDoc, nTab, aScRange, RUTL_DBASE, aDetails, true))
+ {
+ nResult |= ScRefFlags::VALID;
+ if( aScRange.aStart.Tab() != nTab )
+ {
+ nTab = aScRange.aStart.Tab();
+ SetTabNo( nTab );
+ }
+ }
+ }
+
+ if ( !(nResult & ScRefFlags::VALID) && comphelper::string::isdigitAsciiString(aAddress) )
+ {
+ sal_Int32 nNumeric = aAddress.toInt32();
+ if ( nNumeric > 0 && nNumeric <= rDoc.MaxRow()+1 )
+ {
+ // one-based row numbers
+
+ aScAddress.SetRow( static_cast<SCROW>(nNumeric - 1) );
+ aScAddress.SetCol( rViewData.GetCurX() );
+ aScAddress.SetTab( nTab );
+ aScRange = ScRange( aScAddress, aScAddress );
+ bMark = false;
+ nResult = ScRefFlags::VALID;
+ }
+ }
+
+ if ( !rDoc.ValidRow(aScRange.aStart.Row()) || !rDoc.ValidRow(aScRange.aEnd.Row()) )
+ nResult = ScRefFlags::ZERO;
+
+ // we have found something
+ if( nResult & ScRefFlags::VALID )
+ {
+ bFound = true;
+ SCCOL nCol = aScRange.aStart.Col();
+ SCROW nRow = aScRange.aStart.Row();
+ bool bNothing = ( rViewData.GetCurX()==nCol && rViewData.GetCurY()==nRow );
+
+ // mark
+ if( bMark )
+ {
+ if (rMark.IsMarked()) // is the same range already marked?
+ {
+ ScRange aOldMark = rMark.GetMarkArea();
+ aOldMark.PutInOrder();
+ ScRange aCurrent = aScRange;
+ aCurrent.PutInOrder();
+ bNothing = ( aCurrent == aOldMark );
+ }
+ else
+ bNothing = false;
+
+ if (!bNothing)
+ MarkRange( aScRange, false ); // cursor comes after...
+ }
+ else
+ {
+ // remove old selection, unless bUnmark argument is sal_False (from navigator)
+ if( bUnmark )
+ {
+ MoveCursorAbs( nCol, nRow,
+ SC_FOLLOW_NONE, false, false );
+ }
+ }
+
+ // and set cursor
+
+ // consider merged cells:
+ rDoc.SkipOverlapped(nCol, nRow, nTab);
+
+ // navigator calls are not part of the API!!!
+
+ if( bNothing )
+ {
+ if (rReq.IsAPI())
+ rReq.Ignore(); // if macro, then nothing
+ else
+ rReq.Done(); // then at least paint it
+ }
+ else
+ {
+ rViewData.ResetOldCursor();
+ SetCursor( nCol, nRow );
+ rBindings.Invalidate( SID_CURRENTCELL );
+ rBindings.Update( nSlot );
+
+ if (!rReq.IsAPI())
+ rReq.Done();
+ }
+
+ if (bAlignToCursor)
+ {
+ // align to cursor even if the cursor position hasn't changed,
+ // because the cursor may be set outside the visible area.
+ AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
+ if ( nSlot == SID_JUMPTOMARK && comphelper::LibreOfficeKit::isActive() )
+ rViewData.GetActiveWin()->notifyKitCellFollowJump();
+ }
+
+ rReq.SetReturnValue( SfxStringItem( SID_CURRENTCELL, aAddress ) );
+ }
+
+ if (!bFound) // no valid range
+ {
+ // if it is a sheet name, then switch (for Navigator/URL)
+
+ SCTAB nNameTab;
+ if ( rDoc.GetTable( aAddress, nNameTab ) )
+ {
+ bFound = true;
+ if ( nNameTab != nTab )
+ SetTabNo( nNameTab );
+ }
+ }
+
+ if ( !bFound && nSlot == SID_JUMPTOMARK )
+ {
+ // test graphics objects (only for URL)
+
+ bFound = SelectObject( aAddress );
+ }
+
+ if (!bFound && !rReq.IsAPI())
+ ErrorMessage( STR_ERR_INVALID_AREA );
+ }
+ break;
+
+ case SID_CURRENTOBJECT:
+ if ( pReqArgs )
+ {
+ OUString aName = static_cast<const SfxStringItem&>(pReqArgs->Get(nSlot)).GetValue();
+ SelectObject( aName );
+ }
+ break;
+
+ case SID_CURRENTTAB:
+ {
+ SCTAB nTab;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ if ( pReqArgs ) // command from Navigator with nTab
+ {
+ // sheet for basic is one-based
+ nTab = static_cast<const SfxUInt16Item&>(pReqArgs->Get(nSlot)).GetValue() - 1;
+ }
+ else // command from Menu: ask for nTab
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScGoToTabDlg> pDlg(pFact->CreateScGoToTabDlg(GetFrameWeld()));
+ pDlg->SetDescription(
+ ScResId( STR_DLG_SELECTTABLE_TITLE ),
+ ScResId( STR_DLG_SELECTTABLE_MASK ),
+ ScResId( STR_DLG_SELECTTABLE_LBNAME ),
+ GetStaticInterface()->GetSlot(SID_CURRENTTAB)->GetCommand(), HID_GOTOTABLEMASK, HID_GOTOTABLE );
+
+ // fill all table names and select current tab
+ OUString aTabName;
+ for( nTab = 0; nTab < nTabCount; ++nTab )
+ {
+ if( rDoc.IsVisible( nTab ) )
+ {
+ rDoc.GetName( nTab, aTabName );
+ pDlg->Insert( aTabName, rViewData.GetTabNo() == nTab );
+ }
+ }
+
+ if( pDlg->Execute() == RET_OK )
+ {
+ if( !rDoc.GetTable( pDlg->GetSelectedEntry(), nTab ) )
+ nTab = nTabCount;
+ pDlg.disposeAndClear();
+ }
+ else
+ {
+ rReq.Ignore();
+ }
+ }
+ if ( nTab < nTabCount )
+ {
+ SetTabNo( nTab );
+ rBindings.Update( nSlot );
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ //! otherwise an error ?
+ }
+ break;
+
+ case SID_CURRENTDOC:
+ if ( pReqArgs )
+ {
+ OUString aStrDocName( static_cast<const SfxStringItem&>(pReqArgs->
+ Get(nSlot)).GetValue() );
+
+ SfxViewFrame* pViewFrame = nullptr;
+ ScDocShell* pDocSh = static_cast<ScDocShell*>(SfxObjectShell::GetFirst());
+ bool bFound = false;
+
+ // search for ViewFrame to be activated
+
+ while ( pDocSh && !bFound )
+ {
+ if ( pDocSh->GetTitle() == aStrDocName )
+ {
+ pViewFrame = SfxViewFrame::GetFirst( pDocSh );
+ bFound = ( nullptr != pViewFrame );
+ }
+
+ pDocSh = static_cast<ScDocShell*>(SfxObjectShell::GetNext( *pDocSh ));
+ }
+
+ if ( bFound )
+ pViewFrame->GetFrame().Appear();
+
+ rReq.Ignore();//XXX is handled by SFX
+ }
+ break;
+
+ case SID_PRINTPREVIEW:
+ {
+ if ( !rThisFrame.GetFrame().IsInPlace() ) // not for OLE
+ {
+ // print preview is now always in the same frame as the tab view
+ // -> always switch this frame back to normal view
+ // (ScPreviewShell ctor reads view data)
+
+ // #102785#; finish input
+ pScMod->InputEnterHandler();
+
+ rThisFrame.GetDispatcher()->Execute( SID_VIEWSHELL1, SfxCallMode::ASYNCHRON );
+ }
+ // else error (e.g. Ole)
+ }
+ break;
+
+ case SID_DETECTIVE_DEL_ALL:
+ DetectiveDelAll();
+ rReq.Done();
+ break;
+
+ // SID_TABLE_ACTIVATE and SID_MARKAREA are called by basic for the
+ // hidden View, to mark/switch on the visible View:
+
+ case SID_TABLE_ACTIVATE:
+ OSL_FAIL("old slot SID_TABLE_ACTIVATE");
+ break;
+
+ case SID_REPAINT:
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ PaintExtras();
+ rReq.Done();
+ break;
+
+ case FID_NORMALVIEWMODE:
+ case FID_PAGEBREAKMODE:
+ {
+ bool bWantPageBreak = nSlot == FID_PAGEBREAKMODE;
+
+ // check whether there is an explicit argument, use it
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ {
+ bool bItemValue = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ bWantPageBreak = (nSlot == FID_PAGEBREAKMODE) == bItemValue;
+ }
+
+ if( GetViewData().IsPagebreakMode() != bWantPageBreak )
+ {
+ SetPagebreakMode( bWantPageBreak );
+ UpdatePageBreakData();
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate( nSlot );
+ rReq.AppendItem( SfxBoolItem( nSlot, true ) );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case FID_FUNCTION_BOX:
+ {
+ // First make sure that the sidebar is visible
+ rThisFrame.ShowChildWindow(SID_SIDEBAR);
+
+ ::sfx2::sidebar::Sidebar::ShowPanel(u"ScFunctionsPanel",
+ rThisFrame.GetFrame().GetFrameInterface(),
+ true);
+ rReq.Done ();
+ }
+ break;
+
+ case FID_TOGGLESYNTAX:
+ {
+ bool bSet = !GetViewData().IsSyntaxMode();
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ GetViewData().SetSyntaxMode( bSet );
+ PaintGrid();
+ rBindings.Invalidate( FID_TOGGLESYNTAX );
+ rReq.AppendItem( SfxBoolItem( nSlot, bSet ) );
+ rReq.Done();
+ }
+ break;
+ case FID_TOGGLECOLROWHIGHLIGHTING:
+ {
+ bool bNewVal = !officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get();
+
+ auto pChange(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::set(bNewVal, pChange);
+ pChange->commit();
+
+ rReq.AppendItem(SfxBoolItem(nSlot, bNewVal));
+ rReq.Done();
+ }
+ break;
+ case FID_TOGGLEHEADERS:
+ {
+ bool bSet = !GetViewData().IsHeaderMode();
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ GetViewData().SetHeaderMode( bSet );
+ RepeatResize();
+ rBindings.Invalidate( FID_TOGGLEHEADERS );
+ rReq.AppendItem( SfxBoolItem( nSlot, bSet ) );
+ rReq.Done();
+ }
+ break;
+
+ case FID_TOGGLEFORMULA:
+ {
+ ScViewData& rViewData = GetViewData();
+ const ScViewOptions& rOpts = rViewData.GetOptions();
+ bool bFormulaMode = !rOpts.GetOption( VOPT_FORMULAS );
+ const SfxPoolItem *pItem;
+ if( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bFormulaMode = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+
+ ScViewOptions aSetOpts = rOpts;
+ aSetOpts.SetOption( VOPT_FORMULAS, bFormulaMode );
+ rViewData.SetOptions( aSetOpts );
+ ScDocument& rDoc = rViewData.GetDocument();
+ rDoc.SetViewOptions(aSetOpts);
+
+ rViewData.GetDocShell()->PostPaintGridAll();
+
+ rBindings.Invalidate( FID_TOGGLEFORMULA );
+ rReq.AppendItem( SfxBoolItem( nSlot, bFormulaMode ) );
+ rReq.Done();
+ }
+ break;
+
+ case FID_TOGGLEINPUTLINE:
+ {
+ sal_uInt16 nId = ScInputWindowWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+ bool bSet = ( pWnd == nullptr );
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET )
+ bSet = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ rThisFrame.SetChildWindow( nId, bSet );
+ rBindings.Invalidate( FID_TOGGLEINPUTLINE );
+ rReq.AppendItem( SfxBoolItem( nSlot, bSet ) );
+ rReq.Done();
+ }
+ break;
+
+ // handling for SID_ZOOM_IN and SID_ZOOM_OUT is ScTabView::ScrollCommand
+ // CommandWheelMode::ZOOM inspired
+ case SID_ZOOM_IN:
+ case SID_ZOOM_OUT:
+ {
+ HideNoteMarker();
+
+ if (!GetViewData().GetViewShell()->GetViewFrame().GetFrame().IsInPlace())
+ {
+ // for ole inplace editing, the scale is defined by the visarea and client size
+ // and can't be changed directly
+
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ sal_uInt16 nOld = tools::Long(rOldY * 100);
+ sal_uInt16 nNew;
+ if (SID_ZOOM_OUT == nSlot)
+ nNew = std::max(MINZOOM, basegfx::zoomtools::zoomOut(nOld));
+ else
+ nNew = std::min(MAXZOOM, basegfx::zoomtools::zoomIn(nOld));
+ if ( nNew != nOld)
+ {
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ SetZoomType(SvxZoomType::PERCENT, bSyncZoom);
+ Fraction aFract(nNew, 100);
+ SetZoom(aFract, aFract, bSyncZoom);
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate(SID_ATTR_ZOOM);
+ rBindings.Invalidate(SID_ATTR_ZOOMSLIDER);
+ rBindings.Invalidate(SID_ZOOM_IN);
+ rBindings.Invalidate(SID_ZOOM_OUT);
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_ZOOM: // status row
+ case FID_SCALE:
+ {
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ SvxZoomType eOldZoomType = GetZoomType();
+ SvxZoomType eNewZoomType = eOldZoomType;
+ const Fraction& rOldY = GetViewData().GetZoomY(); // Y is shown
+ sal_uInt16 nOldZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
+ sal_uInt16 nZoom = nOldZoom;
+ bool bCancel = false;
+
+ if ( pReqArgs )
+ {
+ const SvxZoomItem& rZoomItem = pReqArgs->Get(SID_ATTR_ZOOM);
+
+ eNewZoomType = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ else
+ {
+ SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( GetPool() );
+ SvxZoomItem aZoomItem( eOldZoomType, nOldZoom, SID_ATTR_ZOOM );
+ ScopedVclPtr<AbstractSvxZoomDialog> pDlg;
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SvxZoomEnableFlags nBtnFlags = SvxZoomEnableFlags::N50
+ | SvxZoomEnableFlags::N75
+ | SvxZoomEnableFlags::N100
+ | SvxZoomEnableFlags::N150
+ | SvxZoomEnableFlags::N200
+ | SvxZoomEnableFlags::WHOLEPAGE
+ | SvxZoomEnableFlags::PAGEWIDTH;
+
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ nBtnFlags = nBtnFlags | SvxZoomEnableFlags::OPTIMAL;
+
+ aZoomItem.SetValueSet( nBtnFlags );
+ aSet.Put( aZoomItem );
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ pDlg.disposeAndReset(pFact->CreateSvxZoomDialog(GetFrameWeld(), aSet));
+ pDlg->SetLimits( MINZOOM, MAXZOOM );
+
+ bCancel = ( RET_CANCEL == pDlg->Execute() );
+
+ // bCancel is True only if we were in the previous if block,
+ // so no need to check again pDlg
+ if ( !bCancel )
+ {
+ const SvxZoomItem& rZoomItem = pDlg->GetOutputItemSet()->
+ Get( SID_ATTR_ZOOM );
+
+ eNewZoomType = rZoomItem.GetType();
+ nZoom = rZoomItem.GetValue();
+ }
+ }
+
+ if ( !bCancel )
+ {
+ if ( eNewZoomType == SvxZoomType::PERCENT )
+ {
+ if ( nZoom < MINZOOM ) nZoom = MINZOOM;
+ if ( nZoom > MAXZOOM ) nZoom = MAXZOOM;
+ }
+ else
+ {
+ nZoom = CalcZoom( eNewZoomType, nOldZoom );
+ bCancel = nZoom == 0;
+ }
+
+ switch ( eNewZoomType )
+ {
+ case SvxZoomType::WHOLEPAGE:
+ case SvxZoomType::PAGEWIDTH:
+ SetZoomType( eNewZoomType, bSyncZoom );
+ break;
+
+ default:
+ SetZoomType( SvxZoomType::PERCENT, bSyncZoom );
+ }
+ }
+
+ if ( nZoom != nOldZoom && !bCancel )
+ {
+ if (!GetViewData().IsPagebreakMode())
+ {
+ ScAppOptions aNewOpt = pScMod->GetAppOptions();
+ aNewOpt.SetZoom( nZoom );
+ aNewOpt.SetZoomType( GetZoomType() );
+ pScMod->SetAppOptions( aNewOpt );
+ }
+ Fraction aFract( nZoom, 100 );
+ SetZoom( aFract, aFract, bSyncZoom );
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate( SID_ATTR_ZOOM );
+ rReq.AppendItem( SvxZoomItem( GetZoomType(), nZoom, TypedWhichId<SvxZoomItem>(nSlot) ) );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ const SfxPoolItem* pItem = nullptr;
+ bool bSyncZoom = SC_MOD()->GetAppOptions().GetSynchronizeZoom();
+ if ( pReqArgs && pReqArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem) == SfxItemState::SET )
+ {
+ const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
+ if( nCurrentZoom )
+ {
+ SetZoomType( SvxZoomType::PERCENT, bSyncZoom );
+ if (!GetViewData().IsPagebreakMode())
+ {
+ ScAppOptions aNewOpt = pScMod->GetAppOptions();
+ aNewOpt.SetZoom( nCurrentZoom );
+ collectUIInformation(OUString::number(nCurrentZoom));
+ aNewOpt.SetZoomType( GetZoomType() );
+ pScMod->SetAppOptions( aNewOpt );
+ }
+ Fraction aFract( nCurrentZoom,100 );
+ SetZoom( aFract, aFract, bSyncZoom );
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+ rBindings.Invalidate( SID_ATTR_ZOOMSLIDER );
+ rBindings.Invalidate( SID_ZOOM_IN );
+ rBindings.Invalidate( SID_ZOOM_OUT );
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_SELECTALL:
+ SelectAllTables();
+ rReq.Done();
+ break;
+
+ case FID_TAB_DESELECTALL:
+ DeselectAllTables();
+ rReq.Done();
+ break;
+
+ case SID_SELECT_TABLES:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTab;
+
+ ::std::vector < sal_Int32 > aIndexList;
+ const SfxIntegerListItem* pItem = rReq.GetArg<SfxIntegerListItem>(SID_SELECT_TABLES);
+ if ( pItem )
+ aIndexList = pItem->GetList();
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScShowTabDlg> pDlg(pFact->CreateScShowTabDlg(GetFrameWeld()));
+ pDlg->SetDescription(
+ ScResId( STR_DLG_SELECTTABLES_TITLE ),
+ ScResId( STR_DLG_SELECTTABLES_LBNAME ),
+ GetStaticInterface()->GetSlot(SID_SELECT_TABLES)->GetCommand(), HID_SELECTTABLES );
+
+ // fill all table names with selection state
+ OUString aTabName;
+ for( nTab = 0; nTab < nTabCount; ++nTab )
+ {
+ rDoc.GetName( nTab, aTabName );
+ pDlg->Insert( aTabName, rMark.GetTableSelect( nTab ) );
+ }
+
+ if( pDlg->Execute() == RET_OK )
+ {
+ aIndexList = pDlg->GetSelectedRows();
+ pDlg.disposeAndClear();
+ rReq.AppendItem( SfxIntegerListItem( SID_SELECT_TABLES, std::vector(aIndexList) ) );
+ }
+ else
+ rReq.Ignore();
+ }
+
+ if ( !aIndexList.empty() )
+ {
+ sal_uInt16 nSelCount = aIndexList.size();
+ sal_uInt16 nSelIx;
+ SCTAB nFirstVisTab = 0;
+
+ // special case: only hidden tables selected -> do nothing
+ bool bVisSelected = false;
+ for( nSelIx = 0; !bVisSelected && (nSelIx < nSelCount); ++nSelIx )
+ {
+ nFirstVisTab = static_cast<SCTAB>(aIndexList[nSelIx]);
+ bVisSelected = rDoc.IsVisible( nFirstVisTab );
+ }
+ if( !bVisSelected )
+ nSelCount = 0;
+
+ // select the tables
+ if( nSelCount )
+ {
+ for( nTab = 0; nTab < nTabCount; ++nTab )
+ rMark.SelectTable( nTab, false );
+
+ for( nSelIx = 0; nSelIx < nSelCount; ++nSelIx )
+ rMark.SelectTable( static_cast<SCTAB>(aIndexList[nSelIx]), true );
+
+ // activate another table, if current is deselected
+ if( !rMark.GetTableSelect( rViewData.GetTabNo() ) )
+ {
+ rMark.SelectTable( nFirstVisTab, true );
+ SetTabNo( nFirstVisTab );
+ }
+
+ rViewData.GetDocShell()->PostPaintExtras();
+ SfxBindings& rBind = rViewData.GetBindings();
+ rBind.Invalidate( FID_FILL_TAB );
+ rBind.Invalidate( FID_TAB_DESELECTALL );
+ }
+
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_OUTLINE_DELETEALL:
+ RemoveAllOutlines();
+ rReq.Done();
+ break;
+
+ case SID_AUTO_OUTLINE:
+ AutoOutline();
+ rReq.Done();
+ break;
+
+ case SID_WINDOW_SPLIT:
+ {
+ ScSplitMode eHSplit = GetViewData().GetHSplitMode();
+ ScSplitMode eVSplit = GetViewData().GetVSplitMode();
+ if ( eHSplit == SC_SPLIT_NORMAL || eVSplit == SC_SPLIT_NORMAL ) // remove
+ RemoveSplit();
+ else if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX ) // normal
+ FreezeSplitters( false );
+ else // create
+ SplitAtCursor();
+ rReq.Done();
+
+ InvalidateSplit();
+ }
+ break;
+
+ case SID_WINDOW_FIX:
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ ScSplitMode eHSplit = GetViewData().GetHSplitMode();
+ ScSplitMode eVSplit = GetViewData().GetVSplitMode();
+ if ( eHSplit == SC_SPLIT_FIX || eVSplit == SC_SPLIT_FIX ) // remove
+ RemoveSplit();
+ else
+ FreezeSplitters( true, SC_SPLIT_METHOD_CURSOR); // create or fixate
+ rReq.Done();
+ InvalidateSplit();
+ }
+ else
+ {
+ ScViewData& rViewData = GetViewData();
+ SCTAB nThisTab = rViewData.GetTabNo();
+ bool bChangedX = false, bChangedY = false;
+ if (rViewData.GetLOKSheetFreezeIndex(true) > 0 ||
+ rViewData.GetLOKSheetFreezeIndex(false) > 0 ) // remove freeze
+ {
+ bChangedX = rViewData.RemoveLOKFreeze();
+ } // create or fixate
+ else
+ {
+ bChangedX = rViewData.SetLOKSheetFreezeIndex(rViewData.GetCurX(), true); // Freeze column
+ bChangedY = rViewData.SetLOKSheetFreezeIndex(rViewData.GetCurY(), false); // Freeze row
+ }
+
+ rReq.Done();
+ if (bChangedX || bChangedY)
+ {
+ rBindings.Invalidate( SID_WINDOW_FIX );
+ rBindings.Invalidate( SID_WINDOW_FIX_COL );
+ rBindings.Invalidate( SID_WINDOW_FIX_ROW );
+ // Invalidate the slot for all views on the same tab of the document.
+ SfxLokHelper::forEachOtherView(this, [nThisTab](ScTabViewShell* pOther) {
+ ScViewData& rOtherViewData = pOther->GetViewData();
+ if (rOtherViewData.GetTabNo() != nThisTab)
+ return;
+
+ SfxBindings& rOtherBind = rOtherViewData.GetBindings();
+ rOtherBind.Invalidate( SID_WINDOW_FIX );
+ rOtherBind.Invalidate( SID_WINDOW_FIX_COL );
+ rOtherBind.Invalidate( SID_WINDOW_FIX_ROW );
+ });
+ if (!GetViewData().GetDocShell()->IsReadOnly())
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+ }
+ }
+ break;
+
+ case SID_WINDOW_FIX_COL:
+ case SID_WINDOW_FIX_ROW:
+ {
+ bool bIsCol = (nSlot == SID_WINDOW_FIX_COL);
+ sal_Int32 nFreezeIndex = 1;
+ if (const SfxInt32Item* pItem = rReq.GetArg<SfxInt32Item>(nSlot))
+ {
+ nFreezeIndex = pItem->GetValue();
+ if (nFreezeIndex < 0)
+ nFreezeIndex = 0;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScViewData& rViewData = GetViewData();
+ SCTAB nThisTab = rViewData.GetTabNo();
+ bool bChanged = rViewData.SetLOKSheetFreezeIndex(nFreezeIndex, bIsCol);
+ rReq.Done();
+ if (bChanged)
+ {
+ rBindings.Invalidate( SID_WINDOW_FIX );
+ rBindings.Invalidate(nSlot);
+ // Invalidate the slot for all views on the same tab of the document.
+ SfxLokHelper::forEachOtherView(this, [nSlot, nThisTab](ScTabViewShell* pOther) {
+ ScViewData& rOtherViewData = pOther->GetViewData();
+ if (rOtherViewData.GetTabNo() != nThisTab)
+ return;
+
+ SfxBindings& rOtherBind = rOtherViewData.GetBindings();
+ rOtherBind.Invalidate( SID_WINDOW_FIX );
+ rOtherBind.Invalidate(nSlot);
+ });
+ if (!GetViewData().GetDocShell()->IsReadOnly())
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+ }
+ else
+ {
+ FreezeSplitters( true, bIsCol ? SC_SPLIT_METHOD_COL : SC_SPLIT_METHOD_ROW, nFreezeIndex);
+ rReq.Done();
+ InvalidateSplit();
+ }
+ }
+ break;
+
+ case FID_CHG_SHOW:
+ {
+ sal_uInt16 nId = ScHighlightChgDlgWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd == nullptr );
+ }
+ break;
+
+ case FID_CHG_ACCEPT:
+ {
+ rThisFrame.ToggleChildWindow(ScAcceptChgDlgWrapper::GetChildWindowId());
+ GetViewFrame().GetBindings().Invalidate(FID_CHG_ACCEPT);
+ rReq.Done ();
+
+ /*
+ sal_uInt16 nId = ScAcceptChgDlgWrapper::GetChildWindowId();
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+
+ pScMod->SetRefDialog( nId, pWnd ? sal_False : sal_True );
+ */
+ }
+ break;
+
+ case FID_CHG_COMMENT:
+ {
+ ScViewData& rData = GetViewData();
+ ScAddress aCursorPos( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+ ScDocShell* pDocSh = rData.GetDocShell();
+
+ ScChangeAction* pAction = pDocSh->GetChangeAction( aCursorPos );
+ if ( pAction )
+ {
+ const SfxPoolItem* pItem;
+ if ( pReqArgs &&
+ pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET &&
+ dynamic_cast<const SfxStringItem*>( pItem) != nullptr )
+ {
+ OUString aComment = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ pDocSh->SetChangeComment( pAction, aComment );
+ rReq.Done();
+ }
+ else
+ {
+ pDocSh->ExecuteChangeCommentDialog(pAction, GetFrameWeld());
+ rReq.Done();
+ }
+ }
+ }
+ break;
+
+ case SID_CREATE_SW_DRAWVIEW:
+ // is called by Forms, when the DrawView has to be created with all
+ // the extras
+ if (!GetScDrawView())
+ {
+ GetViewData().GetDocShell()->MakeDrawLayer();
+ rBindings.InvalidateAll(false);
+ }
+ break;
+
+ case FID_PROTECT_DOC:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FID_PROTECT_DOC, &pItem ) &&
+ static_cast<const SfxBoolItem*>(pItem)->GetValue() == rDoc.IsDocProtected() )
+ {
+ rReq.Ignore();
+ break;
+ }
+ }
+
+ ScDocProtection* pProtect = rDoc.GetDocProtection();
+ if (pProtect && pProtect->isProtected())
+ {
+ bool bCancel = false;
+ OUString aPassword;
+
+ if (pProtect->isProtectedWithPass())
+ {
+ OUString aText(ScResId(SCSTR_PASSWORD));
+
+ SfxPasswordDialog aDlg(GetFrameWeld(), &aText);
+ aDlg.set_title(ScResId(SCSTR_UNPROTECTDOC));
+ aDlg.SetMinLen(0);
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(FID_PROTECT_DOC)->GetCommand());
+ aDlg.SetEditHelpId(HID_PASSWD_DOC);
+
+ if (aDlg.run() == RET_OK)
+ aPassword = aDlg.GetPassword();
+ else
+ bCancel = true;
+ }
+ if (!bCancel)
+ {
+ Unprotect( TABLEID_DOC, aPassword );
+ rReq.AppendItem( SfxBoolItem( FID_PROTECT_DOC, false ) );
+ rReq.Done();
+ }
+ }
+ else
+ {
+ OUString aText(ScResId(SCSTR_PASSWORDOPT));
+
+ SfxPasswordDialog aDlg(GetFrameWeld(), &aText);
+ aDlg.set_title(ScResId(SCSTR_PROTECTDOC));
+ aDlg.SetMinLen( 0 );
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(FID_PROTECT_DOC)->GetCommand());
+ aDlg.SetEditHelpId(HID_PASSWD_DOC);
+ aDlg.ShowExtras(SfxShowExtras::CONFIRM);
+ aDlg.SetConfirmHelpId(HID_PASSWD_DOC_CONFIRM);
+
+ if (aDlg.run() == RET_OK)
+ {
+ OUString aPassword = aDlg.GetPassword();
+ ProtectDoc( aPassword );
+ rReq.AppendItem( SfxBoolItem( FID_PROTECT_DOC, true ) );
+ rReq.Done();
+ }
+ }
+ rBindings.Invalidate( FID_PROTECT_DOC );
+ }
+ break;
+
+ case FID_PROTECT_TABLE:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bOldProtection = rDoc.IsTabProtected(nTab);
+
+ if( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ bool bNewProtection = !bOldProtection;
+ if( pReqArgs->HasItem( FID_PROTECT_TABLE, &pItem ) )
+ bNewProtection = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ if( bNewProtection == bOldProtection )
+ {
+ rReq.Ignore();
+ break;
+ }
+ }
+
+ if (bOldProtection)
+ {
+ // Unprotect a protected sheet.
+
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect && pProtect->isProtectedWithPass())
+ {
+ OUString aText( ScResId(SCSTR_PASSWORDOPT) );
+ SfxPasswordDialog aDlg(GetFrameWeld(), &aText);
+ aDlg.set_title(ScResId(SCSTR_UNPROTECTTAB));
+ aDlg.SetMinLen(0);
+ aDlg.set_help_id(GetStaticInterface()->GetSlot(FID_PROTECT_TABLE)->GetCommand());
+ aDlg.SetEditHelpId(HID_PASSWD_TABLE);
+
+ if (aDlg.run() == RET_OK)
+ {
+ OUString aPassword = aDlg.GetPassword();
+ Unprotect(nTab, aPassword);
+ }
+ }
+ else
+ // this sheet is not password-protected.
+ Unprotect(nTab, OUString());
+
+ if (!pReqArgs)
+ {
+ rReq.AppendItem( SfxBoolItem(FID_PROTECT_TABLE, false) );
+ rReq.Done();
+ }
+ }
+ else
+ {
+ // Protect a current sheet.
+
+ ScTableProtectionDlg aDlg(GetFrameWeld());
+
+ const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
+ if (pProtect)
+ aDlg.SetDialogData(*pProtect);
+
+ if (aDlg.run() == RET_OK)
+ {
+ pScMod->InputEnterHandler();
+
+ ScTableProtection aNewProtect;
+ aDlg.WriteData(aNewProtect);
+ ProtectSheet(nTab, aNewProtect);
+ if (!pReqArgs)
+ {
+ rReq.AppendItem( SfxBoolItem(FID_PROTECT_TABLE, true) );
+ rReq.Done();
+ }
+ }
+ }
+ TabChanged();
+ UpdateInputHandler(true); // to immediately enable input again
+ SelectionChanged();
+ }
+ break;
+ case SID_THEME_DIALOG:
+ {
+ MakeDrawLayer();
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDocument = rViewData.GetDocument();
+ ScDrawLayer* pModel = rDocument.GetDrawLayer();
+ auto const& pTheme = pModel->getTheme();
+ if (pTheme)
+ {
+ vcl::Window* pWin = rViewData.GetActiveWin();
+ auto pDialog = std::make_shared<svx::ThemeDialog>(pWin ? pWin->GetFrameWeld() : nullptr, pTheme.get());
+ weld::DialogController::runAsync(pDialog, [this, pDialog](sal_uInt32 nResult) {
+ if (RET_OK != nResult)
+ return;
+
+ auto pColorSet = pDialog->getCurrentColorSet();
+ if (pColorSet)
+ {
+ sc::ThemeColorChanger aChanger(*GetViewData().GetDocShell());
+ aChanger.apply(pColorSet);
+ }
+ });
+ }
+ rReq.Done();
+ }
+ break;
+ case SID_OPT_LOCALE_CHANGED :
+ { // locale changed, SYSTEM number formats changed => repaint cell contents
+ PaintGrid();
+ rReq.Done();
+ }
+ break;
+
+ default:
+ OSL_FAIL("Unknown Slot at ScTabViewShell::Execute");
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh4.cxx b/sc/source/ui/view/tabvwsh4.cxx
new file mode 100644
index 0000000000..345a33534d
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh4.cxx
@@ -0,0 +1,1946 @@
+/* -*- 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 <formdata.hxx>
+
+#include <sfx2/app.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/extrusionbar.hxx>
+#include <svx/fontworkbar.hxx>
+#include <editeng/borderline.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/ipclient.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/svborder.hxx>
+
+#include <IAnyRefDialog.hxx>
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <docsh.hxx>
+#include <scmod.hxx>
+#include <appoptio.hxx>
+#include <drawsh.hxx>
+#include <drformsh.hxx>
+#include <editsh.hxx>
+#include <pivotsh.hxx>
+#include <SparklineShell.hxx>
+#include <auditsh.hxx>
+#include <drtxtob.hxx>
+#include <inputhdl.hxx>
+#include <editutil.hxx>
+#include <inputopt.hxx>
+#include <inputwin.hxx>
+#include <dbdata.hxx>
+#include <reffact.hxx>
+#include <viewuno.hxx>
+#include <dispuno.hxx>
+#include <chgtrack.hxx>
+#include <cellsh.hxx>
+#include <oleobjsh.hxx>
+#include <chartsh.hxx>
+#include <graphsh.hxx>
+#include <mediash.hxx>
+#include <pgbrksh.hxx>
+#include <dpobject.hxx>
+#include <prevwsh.hxx>
+#include <scextopt.hxx>
+#include <drawview.hxx>
+#include <fupoor.hxx>
+#include <navsett.hxx>
+#include <scabstdlg.hxx>
+#include <externalrefmgr.hxx>
+#include <defaultsoptions.hxx>
+#include <markdata.hxx>
+#include <preview.hxx>
+#include <docoptio.hxx>
+#include <documentlinkmgr.hxx>
+#include <gridwin.hxx>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <sfx2/lokhelper.hxx>
+#include <comphelper/flagguard.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/sidebar/SidebarController.hxx>
+
+using namespace com::sun::star;
+using namespace sfx2::sidebar;
+
+namespace {
+
+bool inChartOrMathContext(const ScTabViewShell* pViewShell)
+{
+ SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell);
+ if (pSidebar)
+ return pSidebar->hasChartOrMathContextCurrently();
+
+ return false;
+}
+
+} // anonymous namespace
+
+void ScTabViewShell::Activate(bool bMDI)
+{
+ SfxViewShell::Activate(bMDI);
+ bIsActive = true;
+ // here no GrabFocus, otherwise there will be problems when something is edited inplace!
+
+ if ( bMDI )
+ {
+ // for input row (ClearCache)
+ ScModule* pScMod = SC_MOD();
+ pScMod->ViewShellChanged(/*bStopEditing=*/ !comphelper::LibreOfficeKit::isActive());
+
+ ActivateView( true, bFirstActivate );
+
+ // update AutoCorrect, if Writer has newly created this
+ UpdateDrawTextOutliner();
+
+ // RegisterNewTargetNames does not exist anymore
+
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ if ( mpInputHandler && rThisFrame.HasChildWindow(FID_INPUTLINE_STATUS) )
+ {
+ // actually only required for Reload (last version):
+ // The InputWindow remains, but the View along with the InputHandler is newly created,
+ // that is why the InputHandler must be set at the InputWindow.
+ SfxChildWindow* pChild = rThisFrame.GetChildWindow(FID_INPUTLINE_STATUS);
+ if (pChild)
+ {
+ ScInputWindow* pWin = static_cast<ScInputWindow*>(pChild->GetWindow());
+ if (pWin && pWin->IsVisible())
+ {
+ pWin->NumLinesChanged(); // tdf#150664
+ ScInputHandler* pOldHdl=pWin->GetInputHandler();
+
+ SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
+ while ( pSh!=nullptr && pOldHdl!=nullptr)
+ {
+ // Hmm, what if pSh is a shell for a different document? But as this code
+ // does not seem to be LibreOfficeKit-specific, probably that doesn't
+ // happen, because having multiple documents open simultaneously has of
+ // course not been a problem at all in traditional desktop LibreOffice.
+ // (Unlike in a LibreOfficeKit-based process where it has been a problem.)
+ if (static_cast<ScTabViewShell*>(pSh)->GetInputHandler() == pOldHdl)
+ {
+ pOldHdl->ResetDelayTimer();
+ break;
+ }
+ pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
+ }
+
+ pWin->SetInputHandler( mpInputHandler.get() );
+ }
+ }
+ }
+
+ bool isLOK = comphelper::LibreOfficeKit::isActive();
+ UpdateInputHandler( /*bForce=*/ !isLOK, /*bStopEditing=*/ !isLOK );
+
+ if ( bFirstActivate )
+ {
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScNavigatorUpdateAll ) );
+ bFirstActivate = false;
+
+ // ReadExtOptions (view settings from Excel import) must also be done
+ // after the ctor, because of the potential calls to Window::Show.
+ // Even after a bugfix (Window::Show no longer notifies the access
+ // bridge, it's done in ImplSetReallyVisible), there are problems if Window::Show
+ // is called during the ViewShell ctor and reschedules asynchronous calls
+ // (for example from the FmFormShell ctor).
+ ScExtDocOptions* pExtOpt = GetViewData().GetDocument().GetExtDocOptions();
+ if ( pExtOpt && pExtOpt->IsChanged() )
+ {
+ GetViewData().ReadExtOptions(*pExtOpt); // Excel view settings
+ SetTabNo( GetViewData().GetTabNo(), true );
+ pExtOpt->SetChanged( false );
+ }
+ }
+
+ pScActiveViewShell = this;
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ if (pHdl)
+ {
+ pHdl->SetRefScale( GetViewData().GetZoomX(), GetViewData().GetZoomY() );
+ }
+
+ // update change dialog
+
+ if ( rThisFrame.HasChildWindow(FID_CHG_ACCEPT) )
+ {
+ SfxChildWindow* pChild = rThisFrame.GetChildWindow(FID_CHG_ACCEPT);
+ if (pChild)
+ {
+ static_cast<ScAcceptChgDlgWrapper*>(pChild)->ReInitDlg();
+ }
+ }
+
+ if(pScMod->IsRefDialogOpen())
+ {
+ sal_uInt16 nModRefDlgId=pScMod->GetCurRefDlgId();
+ SfxChildWindow* pChildWnd = rThisFrame.GetChildWindow( nModRefDlgId );
+ if ( pChildWnd )
+ {
+ if (auto pController = pChildWnd->GetController())
+ {
+ IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pController.get());
+ if (pRefDlg)
+ pRefDlg->ViewShellChanged();
+ }
+ }
+ }
+ }
+
+ // don't call CheckSelectionTransfer here - activating a view should not change the
+ // primary selection (may be happening just because the mouse was moved over the window)
+
+ if (!inChartOrMathContext(this))
+ {
+ ContextChangeEventMultiplexer::NotifyContextChange(
+ GetController(),
+ vcl::EnumContext::Context::Default);
+ }
+}
+
+void ScTabViewShell::Deactivate(bool bMDI)
+{
+ HideTip();
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScChangeTrack* pChanges = rDoc.GetChangeTrack();
+
+ if(pChanges!=nullptr)
+ {
+ Link<ScChangeTrack&,void> aLink;
+ pChanges->SetModifiedLink(aLink);
+ }
+
+ SfxViewShell::Deactivate(bMDI);
+ bIsActive = false;
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl(this);
+
+ if( bMDI && !comphelper::LibreOfficeKit::isActive())
+ {
+ // during shell deactivation, shells must not be switched, or the loop
+ // through the shell stack (in SfxDispatcher::DoDeactivate_Impl) will not work
+ bool bOldDontSwitch = bDontSwitch;
+ bDontSwitch = true;
+
+ ActivateView( false, false );
+
+ if ( GetViewFrame().GetFrame().IsInPlace() ) // inplace
+ GetViewData().GetDocShell()->UpdateOle(GetViewData(), true);
+
+ if ( pHdl )
+ pHdl->NotifyChange( nullptr, true ); // timer-delayed due to document switching
+
+ if (pScActiveViewShell == this)
+ pScActiveViewShell = nullptr;
+
+ bDontSwitch = bOldDontSwitch;
+ }
+ else
+ {
+ HideNoteMarker(); // note marker
+
+ if ( pHdl )
+ pHdl->HideTip(); // Hide formula auto input tip
+ }
+}
+
+void ScTabViewShell::SetActive()
+{
+ // SFX-View would like to activate itself, since then magical things would happen
+ // (eg else the designer may crash)
+ ActiveGrabFocus();
+}
+
+bool ScTabViewShell::PrepareClose(bool bUI)
+{
+ comphelper::FlagRestorationGuard aFlagGuard(bInPrepareClose, true);
+
+ // Call EnterHandler even in formula mode here,
+ // so a formula change in an embedded object isn't lost
+ // (ScDocShell::PrepareClose isn't called then).
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl( this );
+ if ( pHdl && pHdl->IsInputMode() )
+ {
+ pHdl->EnterHandler();
+ }
+
+ // draw text edit mode must be closed
+ FuPoor* pPoor = GetDrawFuncPtr();
+ if (pPoor && IsDrawTextShell())
+ {
+ // "clean" end of text edit, including note handling, subshells and draw func switching,
+ // as in FuDraw and ScTabView::DrawDeselectAll
+ GetViewData().GetDispatcher().Execute( pPoor->GetSlotID(), SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ ScDrawView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ // force end of text edit, to be safe
+ // ScEndTextEdit must always be used, to ensure correct UndoManager
+ pDrView->ScEndTextEdit();
+ }
+
+ if ( pFormShell )
+ {
+ bool bRet = pFormShell->PrepareClose(bUI);
+ if (!bRet)
+ return bRet;
+ }
+ return SfxViewShell::PrepareClose(bUI);
+}
+
+// calculate zoom for in-place
+// from the ratio of VisArea and window size of GridWin
+
+void ScTabViewShell::UpdateOleZoom()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ //TODO/LATER: is there a difference between the two GetVisArea methods?
+ Size aObjSize = static_cast<const SfxObjectShell*>(pDocSh)->GetVisArea().GetSize();
+ if ( !aObjSize.IsEmpty() )
+ {
+ vcl::Window* pWin = GetActiveWin();
+ Size aWinHMM = pWin->PixelToLogic(pWin->GetOutputSizePixel(), MapMode(MapUnit::Map100thMM));
+ SetZoomFactor( Fraction( aWinHMM.Width(),aObjSize.Width() ),
+ Fraction( aWinHMM.Height(),aObjSize.Height() ) );
+ }
+ }
+}
+
+void ScTabViewShell::InnerResizePixel( const Point &rOfs, const Size &rSize, bool inplaceEditModeChange )
+{
+ Size aNewSize( rSize );
+ if ( GetViewFrame().GetFrame().IsInPlace() )
+ {
+ SvBorder aBorder;
+ GetBorderSize( aBorder, rSize );
+ SetBorderPixel( aBorder );
+
+ Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
+
+ Size aSize( rSize );
+ aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) );
+ aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) );
+
+ if ( !aObjSize.IsEmpty() )
+ {
+ Size aLogicSize = GetWindow()->PixelToLogic(aSize, MapMode(MapUnit::Map100thMM));
+ SfxViewShell::SetZoomFactor( Fraction( aLogicSize.Width(),aObjSize.Width() ),
+ Fraction( aLogicSize.Height(),aObjSize.Height() ) );
+ }
+
+ Point aPos( rOfs );
+ aPos.AdjustX(aBorder.Left() );
+ aPos.AdjustY(aBorder.Top() );
+ GetWindow()->SetPosSizePixel( aPos, aSize );
+ }
+ else
+ {
+ SvBorder aBorder;
+ GetBorderSize( aBorder, rSize );
+ SetBorderPixel( aBorder );
+ aNewSize.AdjustWidth(aBorder.Left() + aBorder.Right() );
+ aNewSize.AdjustHeight(aBorder.Top() + aBorder.Bottom() );
+ }
+
+ DoResize( rOfs, aNewSize, true ); // rSize = size of gridwin
+
+ UpdateOleZoom(); // calculate zoom for in-place
+
+ if (!inplaceEditModeChange)
+ {
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+}
+
+void ScTabViewShell::OuterResizePixel( const Point &rOfs, const Size &rSize )
+{
+ SvBorder aBorder;
+ GetBorderSize( aBorder, rSize );
+ SetBorderPixel( aBorder );
+
+ DoResize( rOfs, rSize ); // position and size of tabview as passed
+
+ // ForceMove as replacement for Sfx-Move mechanism
+ // (aWinPos must be kept current, so that ForceMove works for Ole deactivation)
+
+ ForceMove();
+}
+
+void ScTabViewShell::SetZoomFactor( const Fraction &rZoomX, const Fraction &rZoomY )
+{
+ // for OLE...
+
+ Fraction aFrac20( 1,5 );
+ Fraction aFrac400( 4,1 );
+
+ Fraction aNewX( rZoomX );
+ if ( aNewX < aFrac20 )
+ aNewX = aFrac20;
+ if ( aNewX > aFrac400 )
+ aNewX = aFrac400;
+ Fraction aNewY( rZoomY );
+ if ( aNewY < aFrac20 )
+ aNewY = aFrac20;
+ if ( aNewY > aFrac400 )
+ aNewY = aFrac400;
+
+ GetViewData().UpdateScreenZoom( aNewX, aNewY );
+ SetZoom( aNewX, aNewY, true );
+
+ PaintGrid();
+ PaintTop();
+ PaintLeft();
+
+ SfxViewShell::SetZoomFactor( rZoomX, rZoomY );
+}
+
+void ScTabViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
+{
+ // adjust to entire cells (in 1/100 mm)
+
+ Size aPixelSize = rRect.GetSize();
+ vcl::Window* pWin = const_cast<ScTabViewShell*>(this)->GetActiveWin();
+ Size aLogicSize = pWin->PixelToLogic( aPixelSize );
+
+ const ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScSplitPos ePos = rViewData.GetActivePart();
+ SCCOL nCol = rViewData.GetPosX(WhichH(ePos));
+ SCROW nRow = rViewData.GetPosY(WhichV(ePos));
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bNegativePage = rDoc.IsNegativePage( nTab );
+
+ tools::Rectangle aLogicRect = rDoc.GetMMRect( nCol, nRow, nCol, nRow, nTab );
+ if ( bNegativePage )
+ {
+ // use right edge of aLogicRect, and aLogicSize
+ aLogicRect.SetLeft( aLogicRect.Right() - aLogicSize.Width() + 1 ); // Right() is set below
+ }
+ aLogicRect.SetSize( aLogicSize );
+
+ rViewData.GetDocShell()->SnapVisArea( aLogicRect );
+
+ rRect.SetSize( pWin->LogicToPixel( aLogicRect.GetSize() ) );
+}
+
+void ScTabViewShell::Move()
+{
+ Point aNewPos = GetViewFrame().GetWindow().OutputToScreenPixel(Point());
+
+ if (aNewPos != aWinPos)
+ {
+ StopMarking();
+ aWinPos = aNewPos;
+ }
+}
+
+void ScTabViewShell::ShowCursor(bool /* bOn */)
+{
+/*!!! ShowCursor is not called as a pair as in gridwin.
+ here the CursorLockCount for Gridwin must be set directly to 0
+
+ if (bOn)
+ ShowAllCursors();
+ else
+ HideAllCursors();
+*/
+}
+
+void ScTabViewShell::WriteUserData(OUString& rData, bool /* bBrowse */)
+{
+ GetViewData().WriteUserData(rData);
+}
+
+void ScTabViewShell::WriteUserDataSequence (uno::Sequence < beans::PropertyValue >& rSettings )
+{
+ GetViewData().WriteUserDataSequence(rSettings);
+}
+
+void ScTabViewShell::ReadUserData(const OUString& rData, bool /* bBrowse */)
+{
+ if ( !GetViewData().GetDocShell()->IsPreview() )
+ DoReadUserData( rData );
+}
+
+void ScTabViewShell::ReadUserDataSequence (const uno::Sequence < beans::PropertyValue >& rSettings )
+{
+ if ( !GetViewData().GetDocShell()->IsPreview() )
+ DoReadUserDataSequence( rSettings );
+}
+
+void ScTabViewShell::DoReadUserDataSequence( const uno::Sequence < beans::PropertyValue >& rSettings )
+{
+ vcl::Window* pOldWin = GetActiveWin();
+ bool bFocus = pOldWin && pOldWin->HasFocus();
+
+ GetViewData().ReadUserDataSequence(rSettings);
+ SetTabNo( GetViewData().GetTabNo(), true );
+
+ if ( GetViewData().IsPagebreakMode() )
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+
+ vcl::Window* pNewWin = GetActiveWin();
+ if (pNewWin && pNewWin != pOldWin)
+ {
+ SetWindow( pNewWin ); //! is this ViewShell always active???
+ if (bFocus)
+ pNewWin->GrabFocus();
+ WindowChanged(); // drawing layer (for instance #56771#)
+ }
+
+ if (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
+ GetViewData().GetVSplitMode() == SC_SPLIT_FIX)
+ {
+ InvalidateSplit();
+ }
+
+ ZoomChanged();
+
+ TestHintWindow();
+
+ //! if ViewData has more tables than document, remove tables in ViewData
+}
+
+// DoReadUserData is also called from ctor when switching from print preview
+
+void ScTabViewShell::DoReadUserData( std::u16string_view rData )
+{
+ vcl::Window* pOldWin = GetActiveWin();
+ bool bFocus = pOldWin && pOldWin->HasFocus();
+
+ GetViewData().ReadUserData(rData);
+ SetTabNo( GetViewData().GetTabNo(), true );
+
+ if ( GetViewData().IsPagebreakMode() )
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+
+ vcl::Window* pNewWin = GetActiveWin();
+ if (pNewWin && pNewWin != pOldWin)
+ {
+ SetWindow( pNewWin ); //! is this ViewShell always active???
+ if (bFocus)
+ pNewWin->GrabFocus();
+ WindowChanged(); // drawing layer (for instance #56771#)
+ }
+
+ if (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
+ GetViewData().GetVSplitMode() == SC_SPLIT_FIX)
+ {
+ InvalidateSplit();
+ }
+
+ ZoomChanged();
+
+ TestHintWindow();
+
+ //! if ViewData has more tables than document, remove tables in ViewData
+}
+
+void ScTabViewShell::UpdateDrawShell()
+{
+ // Called after user interaction that may delete the selected drawing object.
+ // Remove DrawShell if nothing is selected.
+
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView && !pDrView->AreObjectsMarked() && !IsDrawSelMode() )
+ SetDrawShell( false );
+}
+
+void ScTabViewShell::SetDrawShellOrSub()
+{
+ bActiveDrawSh = true;
+
+ if(bActiveDrawFormSh)
+ {
+ SetCurSubShell(OST_DrawForm);
+ }
+ else if(bActiveGraphicSh)
+ {
+ SetCurSubShell(OST_Graphic);
+ }
+ else if(bActiveMediaSh)
+ {
+ SetCurSubShell(OST_Media);
+ }
+ else if(bActiveChartSh)
+ {
+ SetCurSubShell(OST_Chart);
+ }
+ else if(bActiveOleObjectSh)
+ {
+ SetCurSubShell(OST_OleObject);
+ }
+ else
+ {
+ SetCurSubShell(OST_Drawing, true /* force: different toolbars are
+ visible concerning shape type
+ and shape state */);
+ }
+}
+
+void ScTabViewShell::SetDrawShell( bool bActive )
+{
+ if(bActive)
+ {
+ SetCurSubShell(OST_Drawing, true /* force: different toolbars are
+ visible concerning shape type
+ and shape state */);
+ }
+ else
+ {
+ if(bActiveDrawFormSh || bActiveDrawSh ||
+ bActiveGraphicSh || bActiveMediaSh || bActiveOleObjectSh||
+ bActiveChartSh || bActiveDrawTextSh)
+ {
+ SetCurSubShell(OST_Cell);
+ }
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ }
+
+ bool bWasDraw = bActiveDrawSh || bActiveDrawTextSh;
+
+ bActiveDrawSh = bActive;
+ bActiveDrawTextSh = false;
+
+ if ( !bActive )
+ {
+ ResetDrawDragMode(); // switch off Mirror / Rotate
+
+ if (bWasDraw && (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
+ GetViewData().GetVSplitMode() == SC_SPLIT_FIX))
+ {
+ // adjust active part to cursor, etc.
+ MoveCursorAbs( GetViewData().GetCurX(), GetViewData().GetCurY(),
+ SC_FOLLOW_NONE, false, false, true );
+ }
+ }
+}
+
+void ScTabViewShell::SetDrawTextShell( bool bActive )
+{
+ bActiveDrawTextSh = bActive;
+ if ( bActive )
+ {
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ bActiveDrawSh = false;
+ SetCurSubShell(OST_DrawText);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+
+}
+
+void ScTabViewShell::SetPivotShell( bool bActive )
+{
+ // SetPivotShell is called from CursorPosChanged every time
+ // -> don't change anything except switching between cell and pivot shell
+
+ if (eCurOST != OST_Pivot && eCurOST != OST_Cell)
+ return;
+
+ if ( bActive )
+ {
+ bActiveDrawTextSh = bActiveDrawSh = false;
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ SetCurSubShell(OST_Pivot);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetSparklineShell(bool bActive)
+{
+ if (eCurOST != OST_Sparkline && eCurOST != OST_Cell)
+ return;
+
+ if (bActive)
+ {
+ bActiveDrawTextSh = bActiveDrawSh = false;
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ SetCurSubShell(OST_Sparkline);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetAuditShell( bool bActive )
+{
+ if ( bActive )
+ {
+ bActiveDrawTextSh = bActiveDrawSh = false;
+ bActiveDrawFormSh=false;
+ bActiveGraphicSh=false;
+ bActiveMediaSh=false;
+ bActiveOleObjectSh=false;
+ bActiveChartSh=false;
+ SetCurSubShell(OST_Auditing);
+ }
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetDrawFormShell( bool bActive )
+{
+ bActiveDrawFormSh = bActive;
+
+ if(bActiveDrawFormSh)
+ SetCurSubShell(OST_DrawForm);
+}
+void ScTabViewShell::SetChartShell( bool bActive )
+{
+ bActiveChartSh = bActive;
+
+ if(bActiveChartSh)
+ SetCurSubShell(OST_Chart);
+}
+
+void ScTabViewShell::SetGraphicShell( bool bActive )
+{
+ bActiveGraphicSh = bActive;
+
+ if(bActiveGraphicSh)
+ SetCurSubShell(OST_Graphic);
+}
+
+void ScTabViewShell::SetMediaShell( bool bActive )
+{
+ bActiveMediaSh = bActive;
+
+ if(bActiveMediaSh)
+ SetCurSubShell(OST_Media);
+}
+
+void ScTabViewShell::SetOleObjectShell( bool bActive )
+{
+ bActiveOleObjectSh = bActive;
+
+ if(bActiveOleObjectSh)
+ SetCurSubShell(OST_OleObject);
+ else
+ SetCurSubShell(OST_Cell);
+}
+
+void ScTabViewShell::SetEditShell(EditView* pView, bool bActive )
+{
+ if(bActive)
+ {
+ if (pEditShell)
+ pEditShell->SetEditView( pView );
+ else
+ pEditShell.reset( new ScEditShell(pView, GetViewData()) );
+
+ SetCurSubShell(OST_Editing);
+ }
+ else if(bActiveEditSh)
+ {
+ SetCurSubShell(OST_Cell);
+ }
+ bActiveEditSh = bActive;
+}
+
+void ScTabViewShell::SetCurSubShell(ObjectSelectionType eOST, bool bForce)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ if(bDontSwitch) return;
+
+ if(!pCellShell) // is anyway always used
+ {
+ pCellShell.reset(new ScCellShell(GetViewData(), GetFrameWin()));
+ pCellShell->SetRepeatTarget( &aTarget );
+ }
+
+ bool bPgBrk = rViewData.IsPagebreakMode();
+
+ if(bPgBrk && !pPageBreakShell)
+ {
+ pPageBreakShell.reset( new ScPageBreakShell( this ) );
+ pPageBreakShell->SetRepeatTarget( &aTarget );
+ }
+
+ if ( !(eOST!=eCurOST || bForce) )
+ return;
+
+ bool bCellBrush = false; // "format paint brush" allowed for cells
+ bool bDrawBrush = false; // "format paint brush" allowed for drawing objects
+
+ if(eCurOST!=OST_NONE) RemoveSubShell();
+
+ if (pFormShell && !bFormShellAtTop)
+ AddSubShell(*pFormShell); // add below own subshells
+
+ switch(eOST)
+ {
+ case OST_Cell:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+ bCellBrush = true;
+ }
+ break;
+ case OST_Editing:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if(pEditShell)
+ {
+ AddSubShell(*pEditShell);
+ }
+ }
+ break;
+ case OST_DrawText:
+ {
+ if ( !pDrawTextShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pDrawTextShell.reset( new ScDrawTextObjectBar(GetViewData()) );
+ }
+ AddSubShell(*pDrawTextShell);
+ }
+ break;
+ case OST_Drawing:
+ {
+ if (svx::checkForSelectedCustomShapes(
+ GetScDrawView(), true /* bOnlyExtruded */ )) {
+ if (pExtrusionBarShell == nullptr)
+ pExtrusionBarShell.reset( new svx::ExtrusionBar(this) );
+ AddSubShell( *pExtrusionBarShell );
+ }
+
+ if (svx::checkForSelectedFontWork(
+ GetScDrawView() )) {
+ if (pFontworkBarShell == nullptr)
+ pFontworkBarShell.reset( new svx::FontworkBar(this) );
+ AddSubShell( *pFontworkBarShell );
+ }
+
+ if ( !pDrawShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pDrawShell.reset(new ScDrawShell(GetViewData()));
+ pDrawShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pDrawShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_DrawForm:
+ {
+ if ( !pDrawFormShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pDrawFormShell.reset( new ScDrawFormShell(GetViewData()) );
+ pDrawFormShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pDrawFormShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_Chart:
+ {
+ if ( !pChartShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pChartShell.reset( new ScChartShell(GetViewData()) );
+ pChartShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pChartShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_OleObject:
+ {
+ if ( !pOleObjectShell )
+ {
+ pDocSh->MakeDrawLayer();
+ pOleObjectShell.reset( new ScOleObjectShell(GetViewData()) );
+ pOleObjectShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pOleObjectShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_Graphic:
+ {
+ if ( !pGraphicShell)
+ {
+ pDocSh->MakeDrawLayer();
+ pGraphicShell.reset( new ScGraphicShell(GetViewData()) );
+ pGraphicShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pGraphicShell);
+ bDrawBrush = true;
+ }
+ break;
+
+ case OST_Media:
+ {
+ if ( !pMediaShell)
+ {
+ pDocSh->MakeDrawLayer();
+ pMediaShell.reset( new ScMediaShell(GetViewData()) );
+ pMediaShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pMediaShell);
+ }
+ break;
+
+ case OST_Pivot:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if ( !pPivotShell )
+ {
+ pPivotShell.reset( new ScPivotShell( this ) );
+ pPivotShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pPivotShell);
+ bCellBrush = true;
+ }
+ break;
+ case OST_Auditing:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if ( !pAuditingShell )
+ {
+ pDocSh->MakeDrawLayer(); // the waiting time rather now as on the click
+
+ pAuditingShell.reset( new ScAuditingShell(GetViewData()) );
+ pAuditingShell->SetRepeatTarget( &aTarget );
+ }
+ AddSubShell(*pAuditingShell);
+ bCellBrush = true;
+ }
+ break;
+ case OST_Sparkline:
+ {
+ AddSubShell(*pCellShell);
+ if(bPgBrk) AddSubShell(*pPageBreakShell);
+
+ if (!m_pSparklineShell)
+ {
+ m_pSparklineShell.reset(new sc::SparklineShell(this));
+ m_pSparklineShell->SetRepeatTarget(&aTarget);
+ }
+ AddSubShell(*m_pSparklineShell);
+ bCellBrush = true;
+ }
+ break;
+ default:
+ OSL_FAIL("wrong shell requested");
+ break;
+ }
+
+ if (pFormShell && bFormShellAtTop)
+ AddSubShell(*pFormShell); // add on top of own subshells
+
+ eCurOST=eOST;
+
+ // abort "format paint brush" when switching to an incompatible shell
+ if ( ( GetBrushDocument() && !bCellBrush ) || ( GetDrawBrushSet() && !bDrawBrush ) )
+ ResetBrushDocument();
+}
+
+void ScTabViewShell::SetFormShellAtTop( bool bSet )
+{
+ if ( pFormShell && !bSet )
+ pFormShell->ForgetActiveControl(); // let the FormShell know it no longer has the focus
+
+ if ( bFormShellAtTop != bSet )
+ {
+ bFormShellAtTop = bSet;
+ SetCurSubShell( GetCurObjectSelectionType(), true );
+ }
+}
+
+IMPL_LINK_NOARG(ScTabViewShell, FormControlActivated, LinkParamNone*, void)
+{
+ // a form control got the focus, so the form shell has to be on top
+ SetFormShellAtTop( true );
+}
+
+// GetMySubShell / SetMySubShell: simulate old behavior,
+// so that there is only one SubShell (only within the 5 own SubShells)
+
+SfxShell* ScTabViewShell::GetMySubShell() const
+{
+ // GetSubShell() was const before, and GetSubShell(sal_uInt16) should also be const...
+
+ sal_uInt16 nPos = 0;
+ SfxShell* pSub = const_cast<ScTabViewShell*>(this)->GetSubShell(nPos);
+ while (pSub)
+ {
+ if (pSub == pDrawShell.get() || pSub == pDrawTextShell.get() || pSub == pEditShell.get() ||
+ pSub == pPivotShell.get() || pSub == pAuditingShell.get() || pSub == pDrawFormShell.get() ||
+ pSub == pCellShell.get() || pSub == pOleObjectShell.get() || pSub == pChartShell.get() ||
+ pSub == pGraphicShell.get() || pSub == pMediaShell.get() || pSub == pPageBreakShell.get() ||
+ pSub == m_pSparklineShell.get())
+ {
+ return pSub; // found
+ }
+
+ pSub = const_cast<ScTabViewShell*>(this)->GetSubShell(++nPos);
+ }
+ return nullptr; // none from mine present
+}
+
+bool ScTabViewShell::IsDrawTextShell() const
+{
+ return ( pDrawTextShell && ( GetMySubShell() == pDrawTextShell.get() ) );
+}
+
+bool ScTabViewShell::IsAuditShell() const
+{
+ return ( pAuditingShell && ( GetMySubShell() == pAuditingShell.get() ) );
+}
+
+void ScTabViewShell::SetDrawTextUndo( SfxUndoManager* pNewUndoMgr )
+{
+ // Default: undo manager for DocShell
+ if (!pNewUndoMgr)
+ pNewUndoMgr = GetViewData().GetDocShell()->GetUndoManager();
+
+ if (pDrawTextShell)
+ {
+ pDrawTextShell->SetUndoManager(pNewUndoMgr);
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if ( pNewUndoMgr == pDocSh->GetUndoManager() &&
+ !pDocSh->GetDocument().IsUndoEnabled() )
+ {
+ pNewUndoMgr->SetMaxUndoActionCount( 0 );
+ }
+ }
+ else
+ {
+ OSL_FAIL("SetDrawTextUndo without DrawTextShell");
+ }
+}
+
+ScTabViewShell* ScTabViewShell::GetActiveViewShell()
+{
+ return dynamic_cast< ScTabViewShell *>( Current() );
+}
+
+SfxPrinter* ScTabViewShell::GetPrinter( bool bCreate )
+{
+ // printer is always present (is created for the FontList already on start-up)
+ return GetViewData().GetDocShell()->GetPrinter(bCreate);
+}
+
+sal_uInt16 ScTabViewShell::SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ return GetViewData().GetDocShell()->SetPrinter( pNewPrinter, nDiffFlags );
+}
+
+bool ScTabViewShell::HasPrintOptionsPage() const
+{
+ return true;
+}
+
+std::unique_ptr<SfxTabPage> ScTabViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions )
+{
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
+ if ( ScTpPrintOptionsCreate )
+ return ScTpPrintOptionsCreate(pPage, pController, &rOptions);
+ return nullptr;
+}
+
+void ScTabViewShell::StopEditShell()
+{
+ if ( pEditShell != nullptr && !bDontSwitch )
+ SetEditShell(nullptr, false );
+}
+
+// close handler to ensure function of dialog:
+
+IMPL_LINK_NOARG(ScTabViewShell, SimpleRefClose, const OUString*, void)
+{
+ SfxInPlaceClient* pClient = GetIPClient();
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ {
+ // If range selection was started with an active embedded object,
+ // switch back to original sheet (while the dialog is still open).
+
+ SetTabNo( GetViewData().GetRefTabNo() );
+ }
+
+ ScSimpleRefDlgWrapper::SetAutoReOpen( true );
+}
+
+// handlers to call UNO listeners:
+
+static ScTabViewObj* lcl_GetViewObj( const ScTabViewShell& rShell )
+{
+ ScTabViewObj* pRet = nullptr;
+ SfxViewFrame& rViewFrame = rShell.GetViewFrame();
+ SfxFrame& rFrame = rViewFrame.GetFrame();
+ uno::Reference<frame::XController> xController = rFrame.GetController();
+ if (xController.is())
+ pRet = dynamic_cast<ScTabViewObj*>( xController.get() );
+ return pRet;
+}
+
+IMPL_LINK( ScTabViewShell, SimpleRefDone, const OUString&, aResult, void )
+{
+ ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
+ if ( pImpObj )
+ pImpObj->RangeSelDone( aResult );
+}
+
+IMPL_LINK( ScTabViewShell, SimpleRefAborted, const OUString&, rResult, void )
+{
+ ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
+ if ( pImpObj )
+ pImpObj->RangeSelAborted( rResult );
+}
+
+IMPL_LINK( ScTabViewShell, SimpleRefChange, const OUString&, rResult, void )
+{
+ ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
+ if ( pImpObj )
+ pImpObj->RangeSelChanged( rResult );
+}
+
+void ScTabViewShell::StartSimpleRefDialog(
+ const OUString& rTitle, const OUString& rInitVal,
+ bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection )
+{
+ SfxViewFrame& rViewFrm = GetViewFrame();
+
+ if ( GetActiveViewShell() != this )
+ {
+ // #i18833# / #i34499# The API method can be called for a view that's not active.
+ // Then the view has to be activated first, the same way as in Execute for SID_CURRENTDOC.
+ // Can't use GrabFocus here, because it needs to take effect immediately.
+
+ rViewFrm.GetFrame().Appear();
+ }
+
+ sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
+
+ SC_MOD()->SetRefDialog( nId, true, &rViewFrm );
+
+ ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
+ if (!pWnd)
+ return;
+
+ pWnd->SetCloseHdl( LINK( this, ScTabViewShell, SimpleRefClose ) );
+ pWnd->SetUnoLinks( LINK( this, ScTabViewShell, SimpleRefDone ),
+ LINK( this, ScTabViewShell, SimpleRefAborted ),
+ LINK( this, ScTabViewShell, SimpleRefChange ) );
+ pWnd->SetRefString( rInitVal );
+ pWnd->SetFlags( bCloseOnButtonUp, bSingleCell, bMultiSelection );
+ ScSimpleRefDlgWrapper::SetAutoReOpen( false );
+ if (auto xWin = pWnd->GetController())
+ xWin->set_title(rTitle);
+ pWnd->StartRefInput();
+}
+
+void ScTabViewShell::StopSimpleRefDialog()
+{
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
+
+ ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
+ if (pWnd)
+ {
+ if (auto pWin = pWnd->GetController())
+ pWin->response(RET_CLOSE);
+ }
+}
+
+bool ScTabViewShell::TabKeyInput(const KeyEvent& rKEvt)
+{
+ ScModule* pScMod = SC_MOD();
+
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ if ( rThisFrame.GetChildWindow( SID_OPENDLG_FUNCTION ) )
+ return false;
+
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+ bool bShift = aCode.IsShift();
+ bool bControl = aCode.IsMod1();
+ bool bAlt = aCode.IsMod2();
+ sal_uInt16 nCode = aCode.GetCode();
+ bool bUsed = false;
+ bool bInPlace = pScMod->IsEditMode(); // Editengine gets all
+ bool bAnyEdit = pScMod->IsInputMode(); // only characters & backspace
+ bool bDraw = IsDrawTextEdit();
+
+ HideNoteMarker(); // note marker
+
+ // don't do extra HideCursor/ShowCursor calls if EnterHandler will switch to a different sheet
+ bool bOnRefSheet = ( GetViewData().GetRefTabNo() == GetViewData().GetTabNo() );
+ bool bHideCursor = ( ( nCode == KEY_RETURN && bInPlace ) || nCode == KEY_TAB ) && bOnRefSheet;
+
+ if (bHideCursor)
+ HideAllCursors();
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.KeyInput(); // TimerDelays etc.
+
+ if( bInPlace )
+ {
+ bUsed = pScMod->InputKeyEvent( rKEvt ); // input
+ if( !bUsed )
+ bUsed = SfxViewShell::KeyInput( rKEvt ); // accelerators
+ }
+ else if( bAnyEdit )
+ {
+ bool bIsType = false;
+ sal_uInt16 nModi = aCode.GetModifier();
+ sal_uInt16 nGroup = aCode.GetGroup();
+
+ if ( nGroup == KEYGROUP_NUM || nGroup == KEYGROUP_ALPHA || nGroup == 0 )
+ if ( !bControl && !bAlt )
+ bIsType = true;
+
+ if ( nGroup == KEYGROUP_MISC )
+ switch ( nCode )
+ {
+ case KEY_RETURN:
+ bIsType = bControl && !bAlt; // Control, Shift-Control-Return
+ if ( !bIsType && nModi == 0 )
+ {
+ // Does the Input Handler also want a simple Return?
+
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ bIsType = pHdl && pHdl->TakesReturn();
+ }
+ break;
+ case KEY_SPACE:
+ bIsType = !bControl && !bAlt; // without modifier or Shift-Space
+ break;
+ case KEY_ESCAPE:
+ bIsType = (nModi == 0); // only without modifier
+ break;
+ default:
+ bIsType = true;
+ }
+ else if (nCode == KEY_RIGHT && !bControl && !bShift && !bAlt)
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ bIsType = pHdl && pHdl->HasPartialComplete();
+ }
+
+ if( bIsType )
+ bUsed = pScMod->InputKeyEvent( rKEvt ); // input
+
+ if( !bUsed )
+ bUsed = SfxViewShell::KeyInput( rKEvt ); // accelerators
+
+ if ( !bUsed && !bIsType && nCode != KEY_RETURN ) // input once again afterwards
+ bUsed = pScMod->InputKeyEvent( rKEvt );
+ }
+ else
+ {
+ // special case: copy/cut for multiselect -> error message
+ // (Slot is disabled, so SfxViewShell::KeyInput would be swallowed without a comment)
+ KeyFuncType eFunc = aCode.GetFunction();
+ if ( eFunc == KeyFuncType::CUT )
+ {
+ ScRange aDummy;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy );
+ if (eMarkType != SC_MARK_SIMPLE)
+ {
+ ErrorMessage(STR_NOMULTISELECT);
+ bUsed = true;
+ }
+ }
+ if (!bUsed)
+ bUsed = SfxViewShell::KeyInput( rKEvt ); // accelerators
+
+ // during inplace editing, some slots are handled by the
+ // container app and are executed during Window::KeyInput.
+ // -> don't pass keys to input handler that would be used there
+ // but should call slots instead.
+ bool bParent = ( GetViewFrame().GetFrame().IsInPlace() && eFunc != KeyFuncType::DONTKNOW );
+
+ if( !bUsed && !bDraw && nCode != KEY_RETURN && !bParent )
+ bUsed = pScMod->InputKeyEvent( rKEvt, true ); // input
+ }
+
+ if (!bInPlace && !bUsed && !bDraw)
+ {
+ switch (nCode)
+ {
+ case KEY_RETURN:
+ {
+ bool bNormal = !bControl && !bAlt;
+ if ( !bAnyEdit && bNormal )
+ {
+ // Depending on options, Enter switches to edit mode.
+ const ScInputOptions& rOpt = pScMod->GetInputOptions();
+ if ( rOpt.GetEnterEdit() )
+ {
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ bUsed = true;
+ }
+ }
+
+ bool bEditReturn = bControl && !bShift; // pass on to edit engine
+ if ( !bUsed && !bEditReturn )
+ {
+ if ( bOnRefSheet )
+ HideAllCursors();
+
+ ScEnterMode nMode = ScEnterMode::NORMAL;
+ if ( bShift && bControl )
+ nMode = ScEnterMode::MATRIX;
+ else if ( bAlt )
+ nMode = ScEnterMode::BLOCK;
+ pScMod->InputEnterHandler(nMode);
+
+ if (nMode == ScEnterMode::NORMAL)
+ {
+ if( bShift )
+ GetViewData().GetDispatcher().Execute( SID_CURSORENTERUP,
+ SfxCallMode::SLOT | SfxCallMode::RECORD );
+ else
+ GetViewData().GetDispatcher().Execute( SID_CURSORENTERDOWN,
+ SfxCallMode::SLOT | SfxCallMode::RECORD );
+ }
+ else
+ UpdateInputHandler(true);
+
+ if ( bOnRefSheet )
+ ShowAllCursors();
+
+ // here no UpdateInputHandler, since during reference input on another
+ // document this ViewShell is not the one that is used for input.
+
+ bUsed = true;
+ }
+ }
+ break;
+ }
+ }
+
+ // hard-code Alt-Cursor key, since Alt is not configurable
+
+ if ( !bUsed && bAlt && !bControl )
+ {
+ sal_uInt16 nSlotId = 0;
+ switch (nCode)
+ {
+ case KEY_UP:
+ ModifyCellSize( DIR_TOP, bShift );
+ bUsed = true;
+ break;
+ case KEY_DOWN:
+ ModifyCellSize( DIR_BOTTOM, bShift );
+ bUsed = true;
+ break;
+ case KEY_LEFT:
+ ModifyCellSize( DIR_LEFT, bShift );
+ bUsed = true;
+ break;
+ case KEY_RIGHT:
+ ModifyCellSize( DIR_RIGHT, bShift );
+ bUsed = true;
+ break;
+ case KEY_PAGEUP:
+ nSlotId = bShift ? SID_CURSORPAGELEFT_SEL : SID_CURSORPAGELEFT_;
+ break;
+ case KEY_PAGEDOWN:
+ nSlotId = bShift ? SID_CURSORPAGERIGHT_SEL : SID_CURSORPAGERIGHT_;
+ break;
+ case KEY_EQUAL:
+ {
+ // #tdf39302: Use "Alt + =" for autosum
+ if ( !bAnyEdit ) // Ignore shortcut if currently editing a cell
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl(this);
+ if ( pHdl )
+ {
+ ScInputWindow* pWin = pHdl->GetInputWindow();
+ if ( pWin )
+ {
+ bool bRangeFinder = false;
+ bool bSubTotal = false;
+ pWin->AutoSum( bRangeFinder, bSubTotal, ocSum );
+ }
+ }
+
+ bUsed = true;
+ break;
+ }
+ }
+ }
+ if ( nSlotId )
+ {
+ GetViewData().GetDispatcher().Execute( nSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ bUsed = true;
+ }
+ }
+
+ // use Ctrl+Alt+Shift+arrow keys to move the cursor in cells
+ // while keeping the last selection
+ if ( !bUsed && bAlt && bControl && bShift)
+ {
+ sal_uInt16 nSlotId = 0;
+ switch (nCode)
+ {
+ case KEY_UP:
+ nSlotId = SID_CURSORUP;
+ break;
+ case KEY_DOWN:
+ nSlotId = SID_CURSORDOWN;
+ break;
+ case KEY_LEFT:
+ nSlotId = SID_CURSORLEFT;
+ break;
+ case KEY_RIGHT:
+ nSlotId = SID_CURSORRIGHT;
+ break;
+ case KEY_PAGEUP:
+ nSlotId = SID_CURSORPAGEUP;
+ break;
+ case KEY_PAGEDOWN:
+ nSlotId = SID_CURSORPAGEDOWN;
+ break;
+ case KEY_HOME:
+ nSlotId = SID_CURSORHOME;
+ break;
+ case KEY_END:
+ nSlotId = SID_CURSOREND;
+ break;
+ default:
+ nSlotId = 0;
+ break;
+ }
+ if ( nSlotId )
+ {
+ sal_uInt16 nMode = GetLockedModifiers();
+ LockModifiers(KEY_MOD1);
+ GetViewData().GetDispatcher().Execute( nSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD );
+ LockModifiers(nMode);
+ bUsed = true;
+ }
+ }
+ if (bHideCursor)
+ ShowAllCursors();
+
+ return bUsed;
+}
+
+bool ScTabViewShell::SfxKeyInput(const KeyEvent& rKeyEvent)
+{
+ return SfxViewShell::KeyInput( rKeyEvent );
+}
+
+bool ScTabViewShell::KeyInput( const KeyEvent &rKeyEvent )
+{
+ return TabKeyInput( rKeyEvent );
+}
+
+void ScTabViewShell::Construct( TriState nForceDesignMode )
+{
+ SfxApplication* pSfxApp = SfxGetpApp();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bReadOnly = pDocSh->IsReadOnly();
+ bIsActive = false;
+
+ EnableAutoSpell(rDoc.GetDocOptions().IsAutoSpell());
+
+ SetName("View"); // for SBX
+ Color aColBlack( COL_BLACK );
+ SetPool( &SC_MOD()->GetPool() );
+ SetWindow( GetActiveWin() );
+
+ pCurFrameLine.reset( new ::editeng::SvxBorderLine(&aColBlack, 20, SvxBorderLineStyle::SOLID) );
+ StartListening(*GetViewData().GetDocShell(), DuplicateHandling::Prevent);
+ StartListening(GetViewFrame(), DuplicateHandling::Prevent);
+ StartListening(*pSfxApp, DuplicateHandling::Prevent); // #i62045# #i62046# application is needed for Calc's own hints
+
+ SfxViewFrame* pFirst = SfxViewFrame::GetFirst(pDocSh);
+ bool bFirstView = !pFirst
+ || (pFirst == &GetViewFrame() && !SfxViewFrame::GetNext(*pFirst,pDocSh));
+
+ if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ //TODO/LATER: is there a difference between the two GetVisArea methods?
+ tools::Rectangle aVisArea = static_cast<const SfxObjectShell*>(pDocSh)->GetVisArea();
+
+ SCTAB nVisTab = rDoc.GetVisibleTab();
+ if (!rDoc.HasTable(nVisTab))
+ {
+ nVisTab = 0;
+ rDoc.SetVisibleTab(nVisTab);
+ }
+ SetTabNo( nVisTab );
+ bool bNegativePage = rDoc.IsNegativePage( nVisTab );
+ // show the right cells
+ GetViewData().SetScreenPos( bNegativePage ? aVisArea.TopRight() : aVisArea.TopLeft() );
+
+ if ( GetViewFrame().GetFrame().IsInPlace() ) // inplace
+ {
+ pDocSh->SetInplace( true ); // already initiated like this
+ if (rDoc.IsEmbedded())
+ rDoc.ResetEmbedded(); // no blue mark
+ }
+ else if ( bFirstView )
+ {
+ pDocSh->SetInplace( false );
+ GetViewData().RefreshZoom(); // recalculate PPT
+ if (!rDoc.IsEmbedded())
+ rDoc.SetEmbedded( rDoc.GetVisibleTab(), aVisArea ); // mark VisArea
+ }
+ }
+
+ // ViewInputHandler
+ // Each task now has its own InputWindow,
+ // therefore either should each task get its own InputHandler,
+ // or the InputWindow should create its own InputHandler
+ // (then always search via InputWindow and only if not found
+ // use the InputHandler of the App).
+ // As an intermediate solution each View gets its own InputHandler,
+ // which only yields problems if two Views are in one task window.
+ mpInputHandler.reset(new ScInputHandler);
+
+ // old version:
+ // if ( !GetViewFrame().ISA(SfxTopViewFrame) ) // OLE or Plug-In
+ // pInputHandler = new ScInputHandler;
+
+ // create FormShell before MakeDrawView, so that DrawView can be registered at the
+ // FormShell in every case
+ // the FormShell is pushed in the first activate
+ pFormShell.reset( new FmFormShell(this) );
+ pFormShell->SetControlActivationHandler( LINK( this, ScTabViewShell, FormControlActivated ) );
+
+ // DrawView must not be created in TabView - ctor,
+ // if the ViewShell is not yet constructed...
+ if (rDoc.GetDrawLayer())
+ MakeDrawView( nForceDesignMode );
+ ViewOptionsHasChanged(false, false); // possibly also creates DrawView
+
+ SfxUndoManager* pMgr = pDocSh->GetUndoManager();
+ SetUndoManager( pMgr );
+ pFormShell->SetUndoManager( pMgr );
+ if ( !rDoc.IsUndoEnabled() )
+ {
+ pMgr->SetMaxUndoActionCount( 0 );
+ }
+ SetRepeatTarget( &aTarget );
+ pFormShell->SetRepeatTarget( &aTarget );
+
+ if ( bFirstView ) // first view?
+ {
+ rDoc.SetDocVisible( true ); // used when creating new sheets
+ if ( pDocSh->IsEmpty() )
+ {
+ // set first sheet's RTL flag (following will already be initialized because of SetDocVisible)
+ rDoc.SetLayoutRTL( 0, ScGlobal::IsSystemRTL() );
+
+ // append additional sheets (not for OLE object)
+ if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ // Get the customized initial tab count
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ SCTAB nInitTabCount = rOpt.GetInitTabCount();
+
+ for (SCTAB i=1; i<nInitTabCount; i++)
+ rDoc.MakeTable(i,false);
+ }
+
+ pDocSh->SetEmpty( false ); // #i6232# make sure this is done only once
+ }
+
+ // ReadExtOptions is now in Activate
+
+ // link update no nesting
+ if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::INTERNAL &&
+ pDocSh->IsUpdateEnabled() ) // #105575#; update only in the first creation of the ViewShell
+ {
+ // Check if there are any external data.
+ bool bLink = rDoc.GetExternalRefManager()->hasExternalData();
+ if (!bLink)
+ {
+ // #i100042# sheet links can still exist independently from external formula references
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !bLink; i++)
+ if (rDoc.IsLinked(i))
+ bLink = true;
+ }
+ if (!bLink)
+ {
+ const sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
+ if (rDoc.HasLinkFormulaNeedingCheck() || rDoc.HasAreaLinks() || rMgr.hasDdeOrOleOrWebServiceLinks())
+ bLink = true;
+ }
+ if (bLink)
+ {
+ if ( !pFirst )
+ pFirst = &GetViewFrame();
+
+ if(SC_MOD()->GetCurRefDlgId()==0)
+ {
+ pFirst->GetDispatcher()->Execute( SID_UPDATETABLINKS,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
+ }
+ }
+ else
+ {
+ // No links yet, but loading an existing document may have
+ // disabled link update but there's no "Allow updating" infobar
+ // that could enable it again. So in order to enable the user
+ // to add formulas with external references allow link updates
+ // again.
+ pDocSh->AllowLinkUpdate();
+ }
+
+ bool bReImport = false; // update imported data
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+ if ( pDBColl )
+ {
+ const ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
+ bReImport = std::any_of(rDBs.begin(), rDBs.end(),
+ [](const std::unique_ptr<ScDBData>& rxDB) { return rxDB->IsStripData() && rxDB->HasImportParam() && !rxDB->HasImportSelection(); });
+ }
+ if (bReImport)
+ {
+ if ( !pFirst )
+ pFirst = &GetViewFrame();
+ if(SC_MOD()->GetCurRefDlgId()==0)
+ {
+ pFirst->GetDispatcher()->Execute( SID_REIMPORT_AFTER_LOAD,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
+ }
+ }
+ }
+ }
+
+ UpdateAutoFillMark();
+
+ // ScDispatchProviderInterceptor registers itself in ctor
+ xDisProvInterceptor = new ScDispatchProviderInterceptor( this );
+
+ bFirstActivate = true; // delay NavigatorUpdate until Activate()
+
+ // #105575#; update only in the first creation of the ViewShell
+ pDocSh->SetUpdateEnabled(false);
+
+ if ( GetViewFrame().GetFrame().IsInPlace() )
+ UpdateHeaderWidth(); // The inplace activation requires headers to be calculated
+
+ SvBorder aBorder;
+ GetBorderSize( aBorder, Size() );
+ SetBorderPixel( aBorder );
+}
+
+ScTabViewShell::ScTabViewShell( SfxViewFrame& rViewFrame,
+ SfxViewShell* pOldSh ) :
+ SfxViewShell(rViewFrame, SfxViewShellFlags::HAS_PRINTOPTIONS),
+ ScDBFunc( &rViewFrame.GetWindow(), static_cast<ScDocShell&>(*rViewFrame.GetObjectShell()), this ),
+ eCurOST(OST_NONE),
+ nDrawSfxId(0),
+ aTarget(this),
+ bActiveDrawSh(false),
+ bActiveDrawTextSh(false),
+ bActiveDrawFormSh(false),
+ bActiveOleObjectSh(false),
+ bActiveChartSh(false),
+ bActiveGraphicSh(false),
+ bActiveMediaSh(false),
+ bFormShellAtTop(false),
+ bDontSwitch(false),
+ bInFormatDialog(false),
+ bReadOnly(false),
+ bForceFocusOnCurCell(false),
+ bInPrepareClose(false),
+ bInDispose(false),
+ nCurRefDlgId(0),
+ mbInSwitch(false),
+ m_pDragData(new ScDragData)
+{
+ const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions();
+
+ // if switching back from print preview,
+ // restore the view settings that were active when creating the preview
+ // ReadUserData must not happen from ctor, because the view's edit window
+ // has to be shown by the sfx. ReadUserData is deferred until the first Activate call.
+ // old DesignMode state from form layer must be restored, too
+
+ TriState nForceDesignMode = TRISTATE_INDET;
+ if ( auto pPreviewShell = dynamic_cast<ScPreviewShell*>( pOldSh) )
+ {
+ nForceDesignMode = pPreviewShell->GetSourceDesignMode();
+ ScPreview* p = pPreviewShell->GetPreview();
+ if (p)
+ GetViewData().GetMarkData().SetSelectedTabs(p->GetSelectedTabs());
+ }
+
+ Construct( nForceDesignMode );
+
+ // make Controller known to SFX
+ new ScTabViewObj( this );
+
+ // Resolves: tdf#53899 if there is no controller, register the above
+ // ScTabViewObj as the current controller for the duration of the first
+ // round of calculations triggered here by SetZoom. That way any StarBasic
+ // macros triggered while the document is loading have a CurrentController
+ // available to them.
+ bool bInstalledScTabViewObjAsTempController = false;
+ uno::Reference<frame::XController> xCurrentController(GetViewData().GetDocShell()->GetModel()->getCurrentController());
+ if (!xCurrentController)
+ {
+ //GetController here returns the ScTabViewObj above
+ GetViewData().GetDocShell()->GetModel()->setCurrentController(GetController());
+ bInstalledScTabViewObjAsTempController = true;
+ }
+ xCurrentController.clear();
+
+ if ( GetViewData().GetDocShell()->IsPreview() )
+ {
+ // preview for template dialog: always show whole page
+ SetZoomType( SvxZoomType::WHOLEPAGE, true ); // zoom value is recalculated at next Resize
+ }
+ else
+ {
+ Fraction aFract( rAppOpt.GetZoom(), 100 );
+ SetZoom( aFract, aFract, true );
+ SetZoomType( rAppOpt.GetZoomType(), true );
+ }
+
+ SetCurSubShell(OST_Cell);
+ SvBorder aBorder;
+ GetBorderSize( aBorder, Size() );
+ SetBorderPixel( aBorder );
+
+ MakeDrawLayer();
+
+ //put things back as we found them
+ if (bInstalledScTabViewObjAsTempController)
+ GetViewData().GetDocShell()->GetModel()->setCurrentController(nullptr);
+
+ // formula mode in online is not usable in collaborative mode,
+ // this is a workaround for disabling formula mode in online
+ // when there is more than a single view
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ // have we already one view ?
+ if (!pViewShell)
+ return;
+
+ // this view is not yet visible at this stage, so we look for not visible views, too, for this same document
+ SfxViewShell* pViewShell2 = pViewShell;
+ do
+ {
+ pViewShell2 = SfxViewShell::GetNext(*pViewShell2, /*only visible shells*/ false);
+ } while (pViewShell2 && pViewShell2->GetDocId() != pViewShell->GetDocId());
+ // if the second view is not this one, it means that there is
+ // already more than one active view and so the formula mode
+ // has already been disabled
+ if (pViewShell2 && pViewShell2 == this)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ assert(pTabViewShell);
+ ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
+ if (pInputHdl && pInputHdl->IsFormulaMode())
+ {
+ pInputHdl->SetMode(SC_INPUT_NONE);
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(GetCurrentDocument());
+ SfxLokHelper::notifyViewRenderState(this, pModel);
+ }
+}
+
+ScTabViewShell::~ScTabViewShell()
+{
+ bInDispose = true;
+
+ // Notify other LOK views that we are going away.
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false"_ostr);
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""_ostr);
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY"_ostr);
+ SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
+
+ // all to NULL, in case the TabView-dtor tries to access them
+ //! (should not really! ??!?!)
+ if (mpInputHandler)
+ {
+ mpInputHandler->SetDocumentDisposing(true);
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ EndListening(*pDocSh);
+ EndListening(GetViewFrame());
+ EndListening(*SfxGetpApp()); // #i62045# #i62046# needed now - SfxViewShell no longer does it
+
+ SC_MOD()->ViewShellGone(this);
+
+ RemoveSubShell(); // all
+ SetWindow(nullptr);
+
+ // need kill editview or we will touch the editengine after it has been freed by the ScInputHandler
+ KillEditView(true);
+
+ pFontworkBarShell.reset();
+ pExtrusionBarShell.reset();
+ pCellShell.reset();
+ pPageBreakShell.reset();
+ pDrawShell.reset();
+ pDrawFormShell.reset();
+ pOleObjectShell.reset();
+ pChartShell.reset();
+ pGraphicShell.reset();
+ pMediaShell.reset();
+ pDrawTextShell.reset();
+ pEditShell.reset();
+ pPivotShell.reset();
+ m_pSparklineShell.reset();
+ pAuditingShell.reset();
+ pCurFrameLine.reset();
+ mpFormEditData.reset();
+ mpInputHandler.reset();
+ pDialogDPObject.reset();
+ pNavSettings.reset();
+
+ pFormShell.reset();
+ pAccessibilityBroadcaster.reset();
+}
+
+void ScTabViewShell::SetDialogDPObject( std::unique_ptr<ScDPObject> pObj )
+{
+ pDialogDPObject = std::move(pObj);
+}
+
+void ScTabViewShell::FillFieldData( ScHeaderFieldData& rData )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocShell->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ OUString aTmp;
+ rDoc.GetName(nTab, aTmp);
+ rData.aTabName = aTmp;
+
+ if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
+ rData.aTitle = pDocShell->getDocProperties()->getTitle();
+ else
+ rData.aTitle = pDocShell->GetTitle();
+
+ const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
+ rData.aLongDocName = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ if ( !rData.aLongDocName.isEmpty() )
+ rData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ rData.aShortDocName = rData.aLongDocName = rData.aTitle;
+ rData.nPageNo = 1;
+ rData.nTotalPages = 99;
+
+ // eNumType is known by the dialog
+}
+
+ScNavigatorSettings* ScTabViewShell::GetNavigatorSettings()
+{
+ if( !pNavSettings )
+ pNavSettings.reset(new ScNavigatorSettings);
+ return pNavSettings.get();
+}
+
+tools::Rectangle ScTabViewShell::getLOKVisibleArea() const
+{
+ return GetViewData().getLOKVisibleArea();
+}
+
+void ScTabViewShell::SetDragObject(ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj)
+{
+ ResetDragObject();
+ m_pDragData->pCellTransfer = pCellObj;
+ m_pDragData->pDrawTransfer = pDrawObj;
+}
+
+void ScTabViewShell::ResetDragObject()
+{
+ m_pDragData->pCellTransfer = nullptr;
+ m_pDragData->pDrawTransfer = nullptr;
+ m_pDragData->pJumpLocalDoc = nullptr;
+ m_pDragData->aLinkDoc.clear();
+ m_pDragData->aLinkTable.clear();
+ m_pDragData->aLinkArea.clear();
+ m_pDragData->aJumpTarget.clear();
+ m_pDragData->aJumpText.clear();
+}
+
+void ScTabViewShell::SetDragLink(const OUString& rDoc, const OUString& rTab, const OUString& rArea)
+{
+ ResetDragObject();
+ m_pDragData->aLinkDoc = rDoc;
+ m_pDragData->aLinkTable = rTab;
+ m_pDragData->aLinkArea = rArea;
+}
+
+void ScTabViewShell::SetDragJump(ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText)
+{
+ ResetDragObject();
+ m_pDragData->pJumpLocalDoc = pLocalDoc;
+ m_pDragData->aJumpTarget = rTarget;
+ m_pDragData->aJumpText = rText;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh5.cxx b/sc/source/ui/view/tabvwsh5.cxx
new file mode 100644
index 0000000000..fab96304f3
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh5.cxx
@@ -0,0 +1,390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/hint.hxx>
+#include <comphelper/lok.hxx>
+#include <svx/numfmtsh.hxx>
+#include <svx/numinf.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <global.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <scmod.hxx>
+#include <uiitems.hxx>
+#include <hints.hxx>
+#include <cellvalue.hxx>
+#include <svl/numformat.hxx>
+#include <svl/sharedstring.hxx>
+
+void ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (const ScPaintHint* pPaintHint = dynamic_cast<const ScPaintHint*>(&rHint)) // draw new
+ {
+ PaintPartFlags nParts = pPaintHint->GetParts();
+ SCTAB nTab = GetViewData().GetTabNo();
+ if (pPaintHint->GetStartTab() <= nTab && pPaintHint->GetEndTab() >= nTab)
+ {
+ if (nParts & PaintPartFlags::Extras) // first if table vanished !!!
+ if (PaintExtras())
+ nParts = PaintPartFlags::All;
+
+ // if the current sheet has pending row height updates (sheet links refreshed),
+ // execute them before invalidating the window
+ GetViewData().GetDocShell()->UpdatePendingRowHeights( GetViewData().GetTabNo() );
+
+ if (nParts & PaintPartFlags::Size)
+ RepeatResize(); //! InvalidateBorder ???
+ if (nParts & PaintPartFlags::Grid)
+ PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
+ pPaintHint->GetEndCol(), pPaintHint->GetEndRow() );
+ if (nParts & PaintPartFlags::Marks)
+ PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
+ pPaintHint->GetEndCol(), pPaintHint->GetEndRow(), ScUpdateMode::Marks );
+ if (nParts & PaintPartFlags::Left)
+ PaintLeftArea( pPaintHint->GetStartRow(), pPaintHint->GetEndRow() );
+ if (nParts & PaintPartFlags::Top)
+ PaintTopArea( pPaintHint->GetStartCol(), pPaintHint->GetEndCol() );
+
+ // #i84689# call UpdateAllOverlays here instead of in ScTabView::PaintArea
+ if (nParts & ( PaintPartFlags::Left | PaintPartFlags::Top )) // only if widths or heights changed
+ UpdateAllOverlays();
+
+ HideNoteMarker();
+ }
+ }
+ else if (auto pEditViewHint = dynamic_cast<const ScEditViewHint*>(&rHint)) // create Edit-View
+ {
+ // ScEditViewHint is only received at active view
+
+ SCTAB nTab = GetViewData().GetTabNo();
+ if ( pEditViewHint->GetTab() == nTab )
+ {
+ SCCOL nCol = pEditViewHint->GetCol();
+ SCROW nRow = pEditViewHint->GetRow();
+ {
+ HideNoteMarker();
+
+ MakeEditView( pEditViewHint->GetEngine(), nCol, nRow );
+
+ StopEditShell(); // shouldn't be set
+
+ ScSplitPos eActive = GetViewData().GetActivePart();
+ if ( GetViewData().HasEditView(eActive) )
+ {
+ // MakeEditView will fail, if the cursor is outside the screen.
+ // Then GetEditView will return a none-active view, therefore
+ // calling HasEditView.
+
+ EditView* pView = GetViewData().GetEditView(eActive); // isn't zero
+
+ SetEditShell(pView, true);
+ }
+ }
+ }
+ }
+ else if (auto pTablesHint = dynamic_cast<const ScTablesHint*>(&rHint)) // table insert / deleted
+ {
+ // first fetch current table (can be changed during DeleteTab on ViewData)
+ SCTAB nActiveTab = GetViewData().GetTabNo();
+
+ SCTAB nTab1 = pTablesHint->GetTab1();
+ SCTAB nTab2 = pTablesHint->GetTab2();
+ sal_uInt16 nId = pTablesHint->GetTablesHintId();
+ switch (nId)
+ {
+ case SC_TAB_INSERTED:
+ GetViewData().InsertTab( nTab1 );
+ break;
+ case SC_TAB_DELETED:
+ GetViewData().DeleteTab( nTab1 );
+ break;
+ case SC_TAB_MOVED:
+ GetViewData().MoveTab( nTab1, nTab2 );
+ break;
+ case SC_TAB_COPIED:
+ GetViewData().CopyTab( nTab1, nTab2 );
+ break;
+ case SC_TAB_HIDDEN:
+ break;
+ case SC_TABS_INSERTED:
+ GetViewData().InsertTabs( nTab1, nTab2 );
+ break;
+ case SC_TABS_DELETED:
+ GetViewData().DeleteTabs( nTab1, nTab2 );
+ break;
+ default:
+ OSL_FAIL("unknown ScTablesHint");
+ }
+
+ // No calling of IsActive() here, because the actions can be coming from Basic
+ // and then also the active view has to be switched.
+
+ SCTAB nNewTab = nActiveTab;
+ bool bStayOnActiveTab = true;
+ switch (nId)
+ {
+ case SC_TAB_INSERTED:
+ if ( nTab1 <= nNewTab ) // insert before
+ ++nNewTab;
+ break;
+ case SC_TAB_DELETED:
+ if ( nTab1 < nNewTab ) // deleted before
+ --nNewTab;
+ else if ( nTab1 == nNewTab ) // deleted current
+ bStayOnActiveTab = false;
+ break;
+ case SC_TAB_MOVED:
+ if ( nNewTab == nTab1 ) // moved table
+ nNewTab = nTab2;
+ else if ( nTab1 < nTab2 ) // moved back
+ {
+ if ( nNewTab > nTab1 && nNewTab <= nTab2 ) // succeeding area
+ --nNewTab;
+ }
+ else // move in front
+ {
+ if ( nNewTab >= nTab2 && nNewTab < nTab1 ) // succeeding area
+ ++nNewTab;
+ }
+ break;
+ case SC_TAB_COPIED:
+ if ( nNewTab >= nTab2 ) // insert before
+ ++nNewTab;
+ break;
+ case SC_TAB_HIDDEN:
+ if ( nTab1 == nNewTab ) // current is hidden
+ bStayOnActiveTab = false;
+ break;
+ case SC_TABS_INSERTED:
+ if ( nTab1 <= nNewTab )
+ nNewTab += nTab2;
+ break;
+ case SC_TABS_DELETED:
+ if ( nTab1 < nNewTab )
+ nNewTab -= nTab2;
+ break;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( nNewTab >= rDoc.GetTableCount() )
+ nNewTab = rDoc.GetTableCount() - 1;
+
+ bool bForce = !bStayOnActiveTab;
+ SetTabNo( nNewTab, bForce, false, bStayOnActiveTab );
+ }
+ else if (const ScIndexHint* pIndexHint = dynamic_cast<const ScIndexHint*>(&rHint))
+ {
+ SfxHintId nId = pIndexHint->GetId();
+ sal_uInt16 nIndex = pIndexHint->GetIndex();
+ switch (nId)
+ {
+ case SfxHintId::ScShowRangeFinder:
+ PaintRangeFinder( nIndex );
+ break;
+ default: break;
+ }
+ }
+ else // without parameter
+ {
+ const SfxHintId nSlot = rHint.GetId();
+ switch ( nSlot )
+ {
+ case SfxHintId::ScDataChanged:
+ UpdateFormulas();
+ break;
+
+ case SfxHintId::ScRefModeChanged:
+ {
+ bool bRefMode = SC_MOD()->IsFormulaMode();
+ if (!bRefMode)
+ StopRefMode();
+ else
+ GetSelEngine()->Reset();
+ }
+ break;
+
+ case SfxHintId::ScKillEditView:
+ case SfxHintId::ScKillEditViewNoPaint:
+ if (!comphelper::LibreOfficeKit::isActive()
+ || this == SfxViewShell::Current()
+ || bInPrepareClose
+ || bInDispose)
+ {
+ StopEditShell();
+ KillEditView( nSlot == SfxHintId::ScKillEditViewNoPaint );
+ }
+ break;
+
+ case SfxHintId::DocChanged:
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if (!rDoc.HasTable( GetViewData().GetTabNo() ))
+ {
+ SetTabNo(0);
+ }
+ }
+ break;
+
+ case SfxHintId::ScDrawLayerNew:
+ MakeDrawView(TRISTATE_INDET);
+ break;
+
+ case SfxHintId::ScDocSaved:
+ {
+ // "Save as" can make a write-protected document writable,
+ // therefore the Layer-Locks anew (#39884#)
+ // (Invalidate etc. is happening already from Sfx)
+ // by SID_EDITDOC no SfxHintId::TitleChanged will occur, that
+ // is why the own hint from DoSaveCompleted
+ //! what is with SfxHintId::SAVECOMPLETED ?
+
+ UpdateLayerLocks();
+
+ // Would be too much to change Design-Mode with every save
+ // (when saving under the name, it should remain unchanged)
+ // Therefore only by SfxHintId::ModeChanged (from ViewFrame)
+ }
+ break;
+
+ case SfxHintId::ModeChanged:
+ // Since you can no longer rely on it where this hint was coming
+ // from, always switch the design mode when the ReadOnly state
+ // really was changed:
+
+ if ( GetViewData().GetSfxDocShell()->IsReadOnly() != bReadOnly )
+ {
+ bReadOnly = GetViewData().GetSfxDocShell()->IsReadOnly();
+
+ SfxBoolItem aItem( SID_FM_DESIGN_MODE, !bReadOnly);
+ GetViewData().GetDispatcher().ExecuteList(SID_FM_DESIGN_MODE,
+ SfxCallMode::ASYNCHRON, { &aItem });
+
+ UpdateInputContext();
+ }
+ break;
+
+ case SfxHintId::ScShowRangeFinder:
+ PaintRangeFinder(-1);
+ break;
+
+ case SfxHintId::ScForceSetTab:
+ SetTabNo( GetViewData().GetTabNo(), true );
+ break;
+
+ case SfxHintId::LanguageChanged:
+ {
+ GetViewFrame().GetBindings().Invalidate(SID_LANGUAGE_STATUS);
+ if ( ScGridWindow* pWin = GetViewData().GetActiveWin() )
+ pWin->ResetAutoSpell();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ SfxViewShell::Notify( rBC, rHint );
+}
+
+std::unique_ptr<SvxNumberInfoItem> ScTabViewShell::MakeNumberInfoItem( ScDocument& rDoc, const ScViewData& rViewData )
+{
+
+ // construct NumberInfo item
+
+ SvxNumberValueType eValType = SvxNumberValueType::Undefined;
+ double nCellValue = 0;
+ OUString aCellString;
+
+ ScRefCellValue aCell(rDoc, rViewData.GetCurPos());
+
+ switch (aCell.getType())
+ {
+ case CELLTYPE_VALUE:
+ {
+ nCellValue = aCell.getDouble();
+ eValType = SvxNumberValueType::Number;
+ }
+ break;
+
+ case CELLTYPE_STRING:
+ {
+ aCellString = aCell.getSharedString()->getString();
+ eValType = SvxNumberValueType::String;
+ }
+ break;
+
+ case CELLTYPE_FORMULA:
+ {
+ if (aCell.getFormula()->IsValue())
+ {
+ nCellValue = aCell.getFormula()->GetValue();
+ eValType = SvxNumberValueType::Number;
+ }
+ else
+ {
+ nCellValue = 0;
+ eValType = SvxNumberValueType::Undefined;
+ }
+ }
+ break;
+
+ default:
+ nCellValue = 0;
+ eValType = SvxNumberValueType::Undefined;
+ }
+
+ switch ( eValType )
+ {
+ case SvxNumberValueType::String:
+ return std::make_unique<SvxNumberInfoItem>(
+ rDoc.GetFormatTable(),
+ aCellString,
+ SID_ATTR_NUMBERFORMAT_INFO );
+
+ case SvxNumberValueType::Number:
+ return std::make_unique<SvxNumberInfoItem>(
+ rDoc.GetFormatTable(),
+ nCellValue,
+ SID_ATTR_NUMBERFORMAT_INFO );
+
+ case SvxNumberValueType::Undefined:
+ default:
+ ;
+ }
+
+ return std::make_unique<SvxNumberInfoItem>(
+ rDoc.GetFormatTable(), SID_ATTR_NUMBERFORMAT_INFO);
+}
+
+void ScTabViewShell::UpdateNumberFormatter(
+ const SvxNumberInfoItem& rInfoItem )
+{
+ for ( sal_uInt32 key : rInfoItem.GetDelFormats() )
+ rInfoItem.GetNumberFormatter()->DeleteEntry( key );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh8.cxx b/sc/source/ui/view/tabvwsh8.cxx
new file mode 100644
index 0000000000..9a743c295b
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh8.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 <editeng/borderline.hxx>
+
+#include <tabvwsh.hxx>
+#include <document.hxx>
+
+void ScTabViewShell::SetDefaultFrameLine( const ::editeng::SvxBorderLine* pLine )
+{
+ if ( pLine )
+ {
+ pCurFrameLine.reset( new ::editeng::SvxBorderLine( &pLine->GetColor(),
+ pLine->GetWidth(),
+ pLine->GetBorderLineStyle() ) );
+ }
+ else
+ pCurFrameLine.reset();
+}
+
+bool ScTabViewShell::HasSelection( bool bText ) const
+{
+ bool bHas = false;
+ ScViewData& rData = const_cast<ScViewData&>(GetViewData());
+ if ( bText )
+ {
+ // Content contained: Count2 >= 1
+ ScDocument& rDoc = rData.GetDocument();
+ ScMarkData& rMark = rData.GetMarkData();
+ ScAddress aCursor( rData.GetCurX(), rData.GetCurY(), rData.GetTabNo() );
+ double fVal = 0.0;
+ if ( rDoc.GetSelectionFunction( SUBTOTAL_FUNC_CNT2, aCursor, rMark, fVal ) )
+ bHas = ( fVal > 0.5 );
+ }
+ else
+ {
+ ScRange aRange;
+ ScMarkType eMarkType = rData.GetSimpleArea( aRange );
+ if ( eMarkType == SC_MARK_SIMPLE )
+ bHas = ( aRange.aStart != aRange.aEnd ); // more than 1 cell
+ else
+ bHas = true; // multiple selection or filtered
+ }
+ return bHas;
+}
+
+void ScTabViewShell::UIDeactivated( SfxInPlaceClient* pClient )
+{
+ ClearHighlightRanges();
+
+ // Move in the ViewShell should really be called from Sfx, when the
+ // frame window is moved due to different toolboxes or other things
+ // (to not move painted objects by mistake, #56515#).
+ // this mechanism does however not work at the moment, that is why this
+ // call is here (in Move it is checked if the position has really changed).
+ ForceMove();
+ SfxViewShell::UIDeactivated( pClient );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsh9.cxx b/sc/source/ui/view/tabvwsh9.cxx
new file mode 100644
index 0000000000..9127bb598a
--- /dev/null
+++ b/sc/source/ui/view/tabvwsh9.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 <svx/imapdlg.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdview.hxx>
+#include <svx/ImageMapInfo.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <svl/whiter.hxx>
+#include <svl/stritem.hxx>
+
+#include "imapwrap.hxx"
+#include <tabvwsh.hxx>
+#include <viewdata.hxx>
+#include <docsh.hxx>
+
+#include <svx/galleryitem.hxx>
+#include <com/sun/star/gallery/GalleryItemType.hpp>
+
+class SvxIMapDlg;
+
+void ScTabViewShell::ExecChildWin(const SfxRequest& rReq)
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch(nSlot)
+ {
+ case SID_GALLERY:
+ {
+ // First make sure that the sidebar is visible
+ GetViewFrame().ShowChildWindow(SID_SIDEBAR);
+
+ ::sfx2::sidebar::Sidebar::ShowPanel(
+ u"GalleryPanel",
+ GetViewFrame().GetFrame().GetFrameInterface());
+ }
+ break;
+ }
+}
+
+void ScTabViewShell::ExecGallery( const SfxRequest& rReq )
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ const SvxGalleryItem* pGalleryItem = SfxItemSet::GetItem<SvxGalleryItem>(pArgs, SID_GALLERY_FORMATS, false);
+ if ( !pGalleryItem )
+ return;
+
+ sal_Int8 nType( pGalleryItem->GetType() );
+ if ( nType == css::gallery::GalleryItemType::GRAPHIC )
+ {
+ MakeDrawLayer();
+
+ Graphic aGraphic( pGalleryItem->GetGraphic() );
+ Point aPos = GetInsertPos();
+
+ PasteGraphic( aPos, aGraphic, OUString() );
+ }
+ else if ( nType == css::gallery::GalleryItemType::MEDIA )
+ {
+ // for sounds (linked or not), insert a hyperlink button,
+ // like in Impress and Writer
+ const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, pGalleryItem->GetURL() );
+ GetViewFrame().GetDispatcher()->ExecuteList(SID_INSERT_AVMEDIA,
+ SfxCallMode::SYNCHRON, { &aMediaURLItem });
+ }
+}
+
+void ScTabViewShell::ExecImageMap( SfxRequest& rReq )
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch(nSlot)
+ {
+ case SID_IMAP:
+ {
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ sal_uInt16 nId = ScIMapChildWindowId();
+ rThisFrame.ToggleChildWindow( nId );
+ GetViewFrame().GetBindings().Invalidate( SID_IMAP );
+
+ if ( rThisFrame.HasChildWindow( nId ) )
+ {
+ SvxIMapDlg* pDlg = GetIMapDlg();
+ if ( pDlg )
+ {
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if ( rMarkList.GetMarkCount() == 1 )
+ UpdateIMap( rMarkList.GetMark( 0 )->GetMarkedSdrObj() );
+ }
+ }
+ }
+
+ rReq.Ignore();
+ }
+ break;
+
+ case SID_IMAP_EXEC:
+ {
+ SdrView* pDrView = GetScDrawView();
+ SdrMark* pMark = pDrView ? pDrView->GetMarkedObjectList().GetMark(0) : nullptr;
+
+ if ( pMark )
+ {
+ SdrObject* pSdrObj = pMark->GetMarkedSdrObj();
+ SvxIMapDlg* pDlg = GetIMapDlg();
+
+ if ( ScIMapDlgGetObj(pDlg) == static_cast<void*>(pSdrObj) )
+ {
+ const ImageMap& rImageMap = ScIMapDlgGetMap(pDlg);
+ SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pSdrObj );
+
+ if ( !pIMapInfo )
+ pSdrObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( rImageMap )) );
+ else
+ pIMapInfo->SetImageMap( rImageMap );
+
+ GetViewData().GetDocShell()->SetDrawModified();
+ }
+ }
+ }
+ break;
+ }
+}
+
+void ScTabViewShell::GetImageMapState( SfxItemSet& rSet )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_IMAP:
+ {
+ // We don't disable this anymore
+
+ bool bThere = false;
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ sal_uInt16 nId = ScIMapChildWindowId();
+ if ( rThisFrame.KnowsChildWindow(nId) )
+ if ( rThisFrame.HasChildWindow(nId) )
+ bThere = true;
+
+ ObjectSelectionType eType=GetCurObjectSelectionType();
+ bool bEnable=(eType==OST_OleObject) ||(eType==OST_Graphic);
+ if(!bThere && !bEnable)
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, bThere ) );
+ }
+ }
+ break;
+
+ case SID_IMAP_EXEC:
+ {
+ bool bDisable = true;
+
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if ( rMarkList.GetMarkCount() == 1 )
+ if ( ScIMapDlgGetObj(GetIMapDlg()) ==
+ static_cast<void*>(rMarkList.GetMark(0)->GetMarkedSdrObj()) )
+ bDisable = false;
+ }
+
+ rSet.Put( SfxBoolItem( SID_IMAP_EXEC, bDisable ) );
+ }
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwsha.cxx b/sc/source/ui/view/tabvwsha.cxx
new file mode 100644
index 0000000000..c332c9542a
--- /dev/null
+++ b/sc/source/ui/view/tabvwsha.cxx
@@ -0,0 +1,1817 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/BorderLineStyle.hpp>
+#include <officecfg/Office/Calc.hxx>
+
+#include <comphelper/lok.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/temporary.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/newstyle.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <svl/ilstitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zformat.hxx>
+#include <svl/int64item.hxx>
+#include <svl/ptitem.hxx>
+#include <svl/srchitem.hxx>
+#include <svl/srchdefs.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <svx/numinf.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/zoomslideritem.hxx>
+
+#include <global.hxx>
+#include <appoptio.hxx>
+#include <attrib.hxx>
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+#include <compiler.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <globstr.hrc>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <markdata.hxx>
+#include <patattr.hxx>
+#include <sc.hrc>
+#include <scabstdlg.hxx>
+#include <scitems.hxx>
+#include <scmod.hxx>
+#include <scresid.hxx>
+#include <stlpool.hxx>
+#include <tabvwsh.hxx>
+#include <tokenarray.hxx>
+#include <viewdata.hxx>
+#include <docpool.hxx>
+#include <printfun.hxx>
+#include <undostyl.hxx>
+#include <futext.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+
+bool ScTabViewShell::GetFunction( OUString& rFuncStr, FormulaError nErrCode )
+{
+ sal_uInt32 nFuncs = SC_MOD()->GetAppOptions().GetStatusFunc();
+ ScViewData& rViewData = GetViewData();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ bool bIgnoreError = (rMark.IsMarked() || rMark.IsMultiMarked());
+ bool bFirst = true;
+ for ( sal_uInt16 nFunc = 0; nFunc < 32; nFunc++ )
+ {
+ if ( !(nFuncs & (1U << nFunc)) )
+ continue;
+ ScSubTotalFunc eFunc = static_cast<ScSubTotalFunc>(nFunc);
+
+ if (bIgnoreError && (eFunc == SUBTOTAL_FUNC_CNT || eFunc == SUBTOTAL_FUNC_CNT2))
+ nErrCode = FormulaError::NONE;
+
+ if (nErrCode != FormulaError::NONE)
+ {
+ rFuncStr = ScGlobal::GetLongErrorString(nErrCode);
+ return true;
+ }
+
+ TranslateId pGlobStrId;
+ switch (eFunc)
+ {
+ case SUBTOTAL_FUNC_AVE: pGlobStrId = STR_FUN_TEXT_AVG; break;
+ case SUBTOTAL_FUNC_CNT: pGlobStrId = STR_FUN_TEXT_COUNT; break;
+ case SUBTOTAL_FUNC_CNT2: pGlobStrId = STR_FUN_TEXT_COUNT2; break;
+ case SUBTOTAL_FUNC_MAX: pGlobStrId = STR_FUN_TEXT_MAX; break;
+ case SUBTOTAL_FUNC_MIN: pGlobStrId = STR_FUN_TEXT_MIN; break;
+ case SUBTOTAL_FUNC_SUM: pGlobStrId = STR_FUN_TEXT_SUM; break;
+ case SUBTOTAL_FUNC_SELECTION_COUNT: pGlobStrId = STR_FUN_TEXT_SELECTION_COUNT; break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ if (pGlobStrId)
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ OUString aStr = ScResId(pGlobStrId) + ": ";
+
+ ScAddress aCursor( nPosX, nPosY, nTab );
+ double nVal;
+ if ( rDoc.GetSelectionFunction( eFunc, aCursor, rMark, nVal ) )
+ {
+ if ( nVal == 0.0 )
+ aStr += "0";
+ else
+ {
+ // Number in the standard format, the other on the cursor position
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nNumFmt = 0;
+ if ( eFunc != SUBTOTAL_FUNC_CNT && eFunc != SUBTOTAL_FUNC_CNT2 && eFunc != SUBTOTAL_FUNC_SELECTION_COUNT)
+ {
+ // number format from attributes or formula
+ nNumFmt = rDoc.GetNumberFormat( nPosX, nPosY, nTab );
+ // If the number format is time (without date) and the
+ // result is not within 24 hours, use a duration
+ // format. Summing date+time doesn't make much sense
+ // otherwise but we also don't want to display duration
+ // for a single date+time value.
+ if (nVal < 0.0 || nVal >= 1.0)
+ {
+ const SvNumberformat* pFormat = pFormatter->GetEntry(nNumFmt);
+ if (pFormat && (pFormat->GetType() == SvNumFormatType::TIME))
+ nNumFmt = pFormatter->GetTimeFormat( nVal, pFormat->GetLanguage(), true);
+ }
+ }
+
+ OUString aValStr;
+ const Color* pDummy;
+ pFormatter->GetOutputString( nVal, nNumFmt, aValStr, &pDummy );
+ aStr += aValStr;
+ }
+ }
+ if ( bFirst )
+ {
+ rFuncStr += aStr;
+ bFirst = false;
+ }
+ else
+ rFuncStr += "; " + aStr;
+ }
+ }
+
+ return !rFuncStr.isEmpty();
+}
+
+// Functions that are disabled, depending on the selection
+// Default:
+// SID_DELETE,
+// SID_DELETE_CONTENTS,
+// FID_DELETE_CELL
+// FID_VALIDATION
+
+void ScTabViewShell::GetState( SfxItemSet& rSet )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ SfxViewFrame& rThisFrame = GetViewFrame();
+ bool bOle = GetViewFrame().GetFrame().IsInPlace();
+
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case FID_CHG_COMMENT:
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScAddress aPos( nPosX, nPosY, nTab );
+ if ( pDocSh->IsReadOnly() || !pDocSh->GetChangeAction(aPos) || pDocSh->IsDocShared() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_OPENDLG_EDIT_PRINTAREA:
+ case SID_ADD_PRINTAREA:
+ case SID_DEFINE_PRINTAREA:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_DELETE_PRINTAREA:
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else if (rDoc.IsPrintEntireSheet(nTab))
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_STATUS_PAGESTYLE:
+ case SID_HFEDIT:
+ GetViewData().GetDocShell()->GetStatePageStyle( rSet, nTab );
+ break;
+
+ case SID_SEARCH_ITEM:
+ {
+ SvxSearchItem aItem(ScGlobal::GetSearchItem()); // make a copy.
+ // Search on current selection if a range is marked.
+ aItem.SetSelection(rMark.IsMarked());
+ rSet.Put(aItem);
+ break;
+ }
+
+ case SID_SEARCH_OPTIONS:
+ {
+ // Anything goes
+ SearchOptionFlags nOptions = SearchOptionFlags::ALL;
+
+ // No replacement if ReadOnly
+ if (GetViewData().GetDocShell()->IsReadOnly())
+ nOptions &= ~SearchOptionFlags( SearchOptionFlags::REPLACE | SearchOptionFlags::REPLACE_ALL );
+ rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(nOptions) ) );
+ }
+ break;
+
+ case SID_CURRENTCELL:
+ {
+ ScAddress aScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), 0 );
+ OUString aAddr(aScAddress.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
+ SfxStringItem aPosItem( SID_CURRENTCELL, aAddr );
+
+ rSet.Put( aPosItem );
+ }
+ break;
+
+ case SID_CURRENTTAB:
+ // Table for Basic is 1-based
+ rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(GetViewData().GetTabNo()) + 1 ) );
+ break;
+
+ case SID_CURRENTDOC:
+ rSet.Put( SfxStringItem( nWhich, GetViewData().GetDocShell()->GetTitle() ) );
+ break;
+
+ case FID_TOGGLEINPUTLINE:
+ {
+ sal_uInt16 nId = ScInputWindowWrapper::GetChildWindowId();
+
+ if ( rThisFrame.KnowsChildWindow( nId ) )
+ {
+ SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
+ rSet.Put( SfxBoolItem( nWhich, pWnd != nullptr ) );
+ }
+ else
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_DEL_MANUALBREAKS:
+ if (!rDoc.HasManualBreaks(nTab))
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_RESET_PRINTZOOM:
+ {
+ // disable if already set to default
+
+ OUString aStyleName = rDoc.GetPageStyle( nTab );
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName,
+ SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found" );
+ if ( pStyleSheet )
+ {
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ sal_uInt16 nScale = rStyleSet.Get(ATTR_PAGE_SCALE).GetValue();
+ sal_uInt16 nPages = rStyleSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue();
+ if ( nScale == 100 && nPages == 0 )
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_ZOOM_IN:
+ {
+ const Fraction& rZoomY = GetViewData().GetZoomY();
+ tools::Long nZoom = tools::Long(rZoomY * 100);
+ if (nZoom >= MAXZOOM)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+ case SID_ZOOM_OUT:
+ {
+ const Fraction& rZoomY = GetViewData().GetZoomY();
+ tools::Long nZoom = tools::Long(rZoomY * 100);
+ if (nZoom <= MINZOOM)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+
+ case FID_SCALE:
+ case SID_ATTR_ZOOM:
+ if ( bOle )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ sal_uInt16 nZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
+ rSet.Put( SvxZoomItem( SvxZoomType::PERCENT, nZoom, TypedWhichId<SvxZoomItem>(nWhich) ) );
+ }
+ break;
+
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ if ( bOle )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ const Fraction& rOldY = GetViewData().GetZoomY();
+ sal_uInt16 nCurrentZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
+
+ if( nCurrentZoom )
+ {
+ SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM, SID_ATTR_ZOOMSLIDER );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ }
+ }
+ break;
+
+ case FID_FUNCTION_BOX:
+ {
+ const bool bBoxOpen = ::sfx2::sidebar::Sidebar::IsPanelVisible(u"ScFunctionsPanel",
+ rThisFrame.GetFrame().GetFrameInterface());
+ rSet.Put(SfxBoolItem(nWhich, bBoxOpen));
+ break;
+ }
+
+ case FID_TOGGLESYNTAX:
+ rSet.Put(SfxBoolItem(nWhich, GetViewData().IsSyntaxMode()));
+ break;
+
+ case FID_TOGGLECOLROWHIGHLIGHTING:
+ rSet.Put(SfxBoolItem(
+ nWhich,
+ officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get()));
+ break;
+
+ case FID_TOGGLEHEADERS:
+ rSet.Put(SfxBoolItem(nWhich, GetViewData().IsHeaderMode()));
+ break;
+
+ case FID_TOGGLEFORMULA:
+ {
+ const ScViewOptions& rOpts = rViewData.GetOptions();
+ bool bFormulaMode = rOpts.GetOption( VOPT_FORMULAS );
+ rSet.Put(SfxBoolItem(nWhich, bFormulaMode ));
+ }
+ break;
+
+ case FID_NORMALVIEWMODE:
+ case FID_PAGEBREAKMODE:
+ // always handle both slots - they exclude each other
+ if ( bOle )
+ {
+ rSet.DisableItem( FID_NORMALVIEWMODE );
+ rSet.DisableItem( FID_PAGEBREAKMODE );
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(FID_NORMALVIEWMODE, !GetViewData().IsPagebreakMode()));
+ rSet.Put(SfxBoolItem(FID_PAGEBREAKMODE, GetViewData().IsPagebreakMode()));
+ }
+ break;
+
+ case FID_PROTECT_DOC:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsDocProtected() ) );
+ }
+ }
+ break;
+
+ case FID_PROTECT_TABLE:
+ {
+ if ( pDocShell && pDocShell->IsDocShared() )
+ {
+ rSet.DisableItem( nWhich );
+ }
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsTabProtected( nTab ) ) );
+ }
+ }
+ break;
+
+ case SID_AUTO_OUTLINE:
+ {
+ if (rDoc.GetChangeTrack()!=nullptr || GetViewData().IsMultiMarked())
+ {
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case SID_OUTLINE_DELETEALL:
+ {
+ SCTAB nOlTab = GetViewData().GetTabNo();
+ ScOutlineTable* pOlTable = rDoc.GetOutlineTable( nOlTab );
+ if (pOlTable == nullptr)
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case SID_WINDOW_SPLIT:
+ rSet.Put(SfxBoolItem(nWhich,
+ rViewData.GetHSplitMode() == SC_SPLIT_NORMAL ||
+ rViewData.GetVSplitMode() == SC_SPLIT_NORMAL ));
+ break;
+
+ case SID_WINDOW_FIX:
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ rSet.Put(SfxBoolItem(nWhich,
+ rViewData.GetHSplitMode() == SC_SPLIT_FIX ||
+ rViewData.GetVSplitMode() == SC_SPLIT_FIX ));
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(nWhich,
+ rViewData.GetLOKSheetFreezeIndex(true) > 0 ||
+ rViewData.GetLOKSheetFreezeIndex(false) > 0 ));
+ }
+ break;
+
+ case SID_WINDOW_FIX_COL:
+ case SID_WINDOW_FIX_ROW:
+ {
+ Point aPos;
+ bool bIsCol = (nWhich == SID_WINDOW_FIX_COL);
+ aPos.setX(rViewData.GetLOKSheetFreezeIndex(bIsCol));
+ aPos.setY(rViewData.GetTabNo());
+ rSet.Put(SfxPointItem(nWhich, aPos));
+ }
+ break;
+
+ case FID_CHG_SHOW:
+ {
+ if ( rDoc.GetChangeTrack() == nullptr || ( pDocShell && pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+ case FID_CHG_ACCEPT:
+ {
+ if(
+ ( !rDoc.GetChangeTrack() && !rThisFrame.HasChildWindow(FID_CHG_ACCEPT) )
+ ||
+ ( pDocShell && pDocShell->IsDocShared() )
+ )
+ {
+ rSet.DisableItem( nWhich);
+ }
+ else
+ {
+ rSet.Put(SfxBoolItem(FID_CHG_ACCEPT,
+ rThisFrame.HasChildWindow(FID_CHG_ACCEPT)));
+ }
+ }
+ break;
+
+ case SID_FORMATPAGE:
+ // in protected tables
+ if ( pDocShell && ( pDocShell->IsReadOnly() || pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_PRINTPREVIEW:
+ // Toggle slot needs a State
+ rSet.Put( SfxBoolItem( nWhich, false ) );
+ break;
+
+ case SID_READONLY_MODE:
+ rSet.Put( SfxBoolItem( nWhich, GetViewData().GetDocShell()->IsReadOnly() ) );
+ break;
+
+ case FID_TAB_DESELECTALL:
+ if ( nTabSelCount == 1 )
+ rSet.DisableItem( nWhich ); // enabled only if several sheets are selected
+ break;
+
+ case FID_TOGGLEHIDDENCOLROW:
+ const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
+ rSet.Put( SfxBoolItem( nWhich, rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL).bIsVisible) );
+ break;
+
+ } // switch ( nWitch )
+ nWhich = aIter.NextWhich();
+ } // while ( nWitch )
+}
+
+void ScTabViewShell::ExecuteCellFormatDlg(SfxRequest& rReq, const OUString &rName)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ std::shared_ptr<SvxBoxItem> aLineOuter(std::make_shared<SvxBoxItem>(ATTR_BORDER));
+ std::shared_ptr<SvxBoxInfoItem> aLineInner(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));
+
+ const ScPatternAttr* pOldAttrs = GetSelectionPattern();
+
+ auto pOldSet = std::make_shared<SfxItemSet>(pOldAttrs->GetItemSet());
+
+ pOldSet->MergeRange(XATTR_FILLSTYLE, XATTR_FILLCOLOR);
+
+ pOldSet->MergeRange(SID_ATTR_BORDER_STYLES, SID_ATTR_BORDER_DEFAULT_WIDTH);
+
+ // We only allow these border line types.
+ std::vector<sal_Int32> aBorderStyles{
+ table::BorderLineStyle::SOLID,
+ table::BorderLineStyle::DOTTED,
+ table::BorderLineStyle::DASHED,
+ table::BorderLineStyle::FINE_DASHED,
+ table::BorderLineStyle::DASH_DOT,
+ table::BorderLineStyle::DASH_DOT_DOT,
+ table::BorderLineStyle::DOUBLE_THIN };
+
+ pOldSet->Put(SfxIntegerListItem(SID_ATTR_BORDER_STYLES, std::move(aBorderStyles)));
+
+ // Set the default border width to 0.75 points.
+ SfxInt64Item aBorderWidthItem(SID_ATTR_BORDER_DEFAULT_WIDTH, 75);
+ pOldSet->Put(aBorderWidthItem);
+
+ // Get border items and put them in the set:
+ GetSelectionFrame( aLineOuter, aLineInner );
+
+ //Fix border incorrect for RTL fdo#62399
+ if( rDoc.IsLayoutRTL( GetViewData().GetTabNo() ) )
+ {
+ std::unique_ptr<SvxBoxItem> aNewFrame(aLineOuter->Clone());
+ std::unique_ptr<SvxBoxInfoItem> aTempInfo(aLineInner->Clone());
+
+ if ( aLineInner->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
+ aNewFrame->SetLine( aLineOuter->GetLeft(), SvxBoxItemLine::RIGHT );
+ if ( aLineInner->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
+ aNewFrame->SetLine( aLineOuter->GetRight(), SvxBoxItemLine::LEFT );
+
+ aLineInner->SetValid( SvxBoxInfoItemValidFlags::LEFT, aTempInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT));
+ aLineInner->SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT));
+
+ pOldSet->Put( std::move(aNewFrame) );
+ }
+ else
+ {
+ pOldSet->Put( *aLineOuter );
+ }
+
+ pOldSet->Put( *aLineInner );
+
+ // Generate NumberFormat Value from Value and Language and box it.
+ pOldSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT,
+ pOldAttrs->GetNumberFormat( rDoc.GetFormatTable() ) ) );
+
+ std::unique_ptr<SvxNumberInfoItem> pNumberInfoItem = MakeNumberInfoItem(rDoc, GetViewData());
+ pOldSet->MergeRange( SID_ATTR_NUMBERFORMAT_INFO, SID_ATTR_NUMBERFORMAT_INFO );
+ pOldSet->Put( std::move(pNumberInfoItem) );
+
+ bInFormatDialog = true;
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScAttrDlg(GetFrameWeld(), pOldSet.get()));
+
+ if (!rName.isEmpty())
+ pDlg->SetCurPageId(rName);
+
+ auto pRequest = std::make_shared<SfxRequest>(rReq);
+ rReq.Ignore(); // the 'old' request is not relevant any more
+
+ pDlg->StartExecuteAsync([pDlg, pOldSet, pRequest, this](sal_Int32 nResult){
+ bInFormatDialog = false;
+
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+ if(const SvxNumberInfoItem* pItem = pOutSet->GetItemIfSet(SID_ATTR_NUMBERFORMAT_INFO))
+ {
+ UpdateNumberFormatter(*pItem);
+ }
+
+ ApplyAttributes(*pOutSet, *pOldSet);
+
+ pRequest->Done(*pOutSet);
+ }
+
+ pDlg->disposeOnce();
+ });
+}
+
+const OUString* ScTabViewShell::GetEditString() const
+{
+ if (mpInputHandler)
+ return &mpInputHandler->GetEditString();
+
+ return nullptr;
+}
+
+bool ScTabViewShell::IsRefInputMode() const
+{
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod )
+ {
+ if( pScMod->IsRefDialogOpen() )
+ return pScMod->IsFormulaMode();
+ if( pScMod->IsFormulaMode() )
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl();
+ if ( pHdl )
+ {
+ const ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ const ScAddress aPos( rViewData.GetCurPos() );
+ const sal_uInt32 nIndex = rDoc.GetAttr(aPos, ATTR_VALUE_FORMAT )->GetValue();
+ const SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
+ if (nType == SvNumFormatType::TEXT)
+ {
+ return false;
+ }
+ OUString aString = pHdl->GetEditString();
+ if ( !pHdl->GetSelIsRef() && aString.getLength() > 1 &&
+ ( aString[0] == '+' || aString[0] == '-' ) )
+ {
+ ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar() );
+ aComp.SetCloseBrackets( false );
+ std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aString));
+ if ( pArr && pArr->MayReferenceFollow() )
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ScTabViewShell::ExecuteInputDirect()
+{
+ if ( !IsRefInputMode() )
+ {
+ ScModule* pScMod = SC_MOD();
+ if ( pScMod )
+ {
+ pScMod->InputEnterHandler();
+ }
+ }
+}
+
+void ScTabViewShell::UpdateInputHandler( bool bForce /* = sal_False */, bool bStopEditing /* = sal_True */ )
+{
+ ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : SC_MOD()->GetInputHdl();
+
+ if ( pHdl )
+ {
+ OUString aString;
+ const EditTextObject* pObject = nullptr;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ SCTAB nStartTab = 0;
+ SCTAB nEndTab = 0;
+ SCCOL nStartCol = 0;
+ SCROW nStartRow = 0;
+ SCCOL nEndCol = 0;
+ SCROW nEndRow = 0;
+ ScAddress aPos = rViewData.GetCurPos();
+
+ rViewData.GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+
+ bool bHideFormula = false;
+ bool bHideAll = false;
+
+ if (rDoc.IsTabProtected(nTab))
+ {
+ const ScProtectionAttr* pProt = rDoc.GetAttr( nPosX,nPosY,nTab,
+ ATTR_PROTECTION);
+ bHideFormula = pProt->GetHideFormula();
+ bHideAll = pProt->GetHideCell();
+ }
+
+ if (!bHideAll)
+ {
+ ScRefCellValue rCell(rDoc, aPos);
+ if (rCell.getType() == CELLTYPE_FORMULA)
+ {
+ if (!bHideFormula)
+ aString = rCell.getFormula()->GetFormula();
+ }
+ else if (rCell.getType() == CELLTYPE_EDIT)
+ {
+ pObject = rCell.getEditText();
+ }
+ else
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uInt32 nNumFmt = rDoc.GetNumberFormat( aPos );
+
+ aString = ScCellFormat::GetInputString( rCell, nNumFmt, *pFormatter, rDoc );
+ if (rCell.getType() == CELLTYPE_STRING)
+ {
+ // Put a ' in front if necessary, so that the string is not
+ // unintentionally interpreted as a number, and to show the
+ // user that it is a string (#35060#).
+ // If cell is not formatted as Text, a leading apostrophe
+ // needs another prepended, also '=' or '+' or '-'
+ // otherwise starting a formula.
+ // NOTE: this corresponds with
+ // sc/source/core/data/column3.cxx ScColumn::ParseString()
+ // removing one apostrophe.
+ // For number format Text IsNumberFormat() would never
+ // result in numeric anyway.
+ if (!pFormatter->IsTextFormat(nNumFmt) && (aString.startsWith("'")
+ || aString.startsWith("=") || aString.startsWith("+") || aString.startsWith("-")
+ || pFormatter->IsNumberFormat(aString, nNumFmt, o3tl::temporary(double()))))
+ aString = "'" + aString;
+ }
+ }
+ }
+
+ ScInputHdlState aState( ScAddress( nPosX, nPosY, nTab ),
+ ScAddress( nStartCol, nStartRow, nTab ),
+ ScAddress( nEndCol, nEndRow, nTab ),
+ aString,
+ pObject );
+
+ // if using the view's local input handler, this view can always be set
+ // as current view inside NotifyChange.
+ ScTabViewShell* pSourceSh = mpInputHandler ? this : nullptr;
+
+ pHdl->NotifyChange( &aState, bForce, pSourceSh, bStopEditing );
+ }
+
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ rBindings.Invalidate( SID_STATUS_SUM ); // always together with the input row
+ rBindings.Invalidate( SID_ATTR_SIZE );
+ rBindings.Invalidate( SID_TABLE_CELL );
+}
+
+void ScTabViewShell::UpdateInputHandlerCellAdjust( SvxCellHorJustify eJust )
+{
+ if( ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : SC_MOD()->GetInputHdl() )
+ pHdl->UpdateCellAdjust( eJust );
+}
+
+void ScTabViewShell::ExecuteSave( SfxRequest& rReq )
+{
+ // only SID_SAVEDOC / SID_SAVEASDOC
+ bool bCommitChanges = true;
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+
+ if (pReqArgs && pReqArgs->HasItem(FN_PARAM_1, &pItem))
+ bCommitChanges = !static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ // Finish entering unless 'DontTerminateEdit' is specified, even if a formula is being processed
+ if (bCommitChanges)
+ {
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ // Disable error dialog box when about to save in lok mode as
+ // this ultimately invokes SvpSalInstance::DoYield() when we want
+ // to save immediately without committing any erroneous input in possibly
+ // a cell with validation rules. After save is complete the user
+ // can continue editing.
+ SC_MOD()->InputEnterHandler(ScEnterMode::NORMAL, bLOKActive /* bBeforeSavingInLOK */);
+
+ if (bLOKActive)
+ {
+ // Normally this isn't needed, but in Calc when editing a cell formula
+ // and manually saving (without changing cells or hitting enter), while
+ // InputEnterHandler will mark the doc as modified (when it is), because
+ // we will save the doc immediately afterwards, the modified state event
+ // is clobbered. To avoid that, we need to update SID_DOC_MODIFIED so that
+ // a possible state of "true" after "InputEnterHandler" will be sent
+ // as a notification. It is important that the notification goes through
+ // normal process (cache) rather than directly notifying the views.
+ // Otherwise, because there is a previous state of "false" in cache, the
+ // "false" state after saving will be ignored.
+ // This will work only if .uno:ModifiedStatus message will be removed from
+ // the mechanism that keeps in the message queue only last message of
+ // a particular status even if the values are different.
+ GetViewData().GetDocShell()->GetViewBindings()->Update(SID_DOC_MODIFIED);
+ }
+ }
+
+ if ( GetViewData().GetDocShell()->IsDocShared() )
+ {
+ GetViewData().GetDocShell()->SetDocumentModified();
+ }
+
+ // otherwise as normal
+ GetViewData().GetDocShell()->ExecuteSlot( rReq );
+}
+
+void ScTabViewShell::GetSaveState( SfxItemSet& rSet )
+{
+ SfxShell* pDocSh = GetViewData().GetDocShell();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ if ( nWhich != SID_SAVEDOC || !GetViewData().GetDocShell()->IsDocShared() )
+ {
+ // get state from DocShell
+ pDocSh->GetSlotState( nWhich, nullptr, &rSet );
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScTabViewShell::ExecDrawOpt( const SfxRequest& rReq )
+{
+ ScViewOptions aViewOptions = GetViewData().GetOptions();
+ ScGridOptions aGridOptions = aViewOptions.GetGridOptions();
+
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ switch (nSlotId)
+ {
+ case SID_GRID_VISIBLE:
+ if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
+ {
+ aGridOptions.SetGridVisible( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
+ aViewOptions.SetGridOptions(aGridOptions);
+ rBindings.Invalidate(SID_GRID_VISIBLE);
+ }
+ break;
+
+ case SID_GRID_USE:
+ if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
+ {
+ aGridOptions.SetUseGridSnap( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
+ aViewOptions.SetGridOptions(aGridOptions);
+ rBindings.Invalidate(SID_GRID_USE);
+ }
+ break;
+
+ case SID_HELPLINES_MOVE:
+ if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
+ {
+ aViewOptions.SetOption( VOPT_HELPLINES, static_cast<const SfxBoolItem*>(pItem)->GetValue() );
+ rBindings.Invalidate(SID_HELPLINES_MOVE);
+ }
+ break;
+ }
+
+ GetViewData().SetOptions(aViewOptions);
+}
+
+void ScTabViewShell::GetDrawOptState( SfxItemSet& rSet )
+{
+ SfxBoolItem aBool;
+
+ const ScViewOptions& rViewOptions = GetViewData().GetOptions();
+ const ScGridOptions& rGridOptions = rViewOptions.GetGridOptions();
+
+ aBool.SetValue(rGridOptions.GetGridVisible());
+ aBool.SetWhich( SID_GRID_VISIBLE );
+ rSet.Put( aBool );
+
+ aBool.SetValue(rGridOptions.GetUseGridSnap());
+ aBool.SetWhich( SID_GRID_USE );
+ rSet.Put( aBool );
+
+ aBool.SetValue(rViewOptions.GetOption( VOPT_HELPLINES ));
+ aBool.SetWhich( SID_HELPLINES_MOVE );
+ rSet.Put( aBool );
+}
+
+void ScTabViewShell::ExecStyle( SfxRequest& rReq )
+{
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const sal_uInt16 nSlotId = rReq.GetSlot();
+ if ( !pArgs && nSlotId != SID_STYLE_NEW_BY_EXAMPLE && nSlotId != SID_STYLE_UPDATE_BY_EXAMPLE )
+ {
+ // in case of vertical toolbar
+ GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
+ return;
+ }
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ const SCTAB nCurTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScModule* pScMod = SC_MOD();
+ SdrObject* pEditObject = GetDrawView()->GetTextEditObject();
+ OutlinerView* pOLV = GetDrawView()->GetTextEditOutlinerView();
+ ESelection aSelection = pOLV ? pOLV->GetSelection() : ESelection();
+ OUString aRefName;
+ bool bUndo = rDoc.IsUndoEnabled();
+
+ SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = nullptr;
+
+ bool bStyleToMarked = false;
+ bool bListAction = false;
+ bool bAddUndo = false; // add ScUndoModifyStyle (style modified)
+ ScStyleSaveData aOldData; // for undo/redo
+ ScStyleSaveData aNewData;
+
+ SfxStyleFamily eFamily = SfxStyleFamily::Para;
+ const SfxUInt16Item* pFamItem;
+ const SfxStringItem* pFamilyNameItem;
+ if ( pArgs && (pFamItem = pArgs->GetItemIfSet( SID_STYLE_FAMILY )) )
+ eFamily = static_cast<SfxStyleFamily>(pFamItem->GetValue());
+ else if ( pArgs && (pFamilyNameItem = pArgs->GetItemIfSet( SID_STYLE_FAMILYNAME )) )
+ {
+ OUString sFamily = pFamilyNameItem->GetValue();
+ if (sFamily == "CellStyles")
+ eFamily = SfxStyleFamily::Para;
+ else if (sFamily == "PageStyles")
+ eFamily = SfxStyleFamily::Page;
+ else if (sFamily == "GraphicStyles")
+ eFamily = SfxStyleFamily::Frame;
+ }
+
+ OUString aStyleName;
+ sal_uInt16 nRetMask = 0xffff;
+
+ switch ( nSlotId )
+ {
+ case SID_STYLE_NEW:
+ {
+ const SfxPoolItem* pNameItem;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
+ aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+
+ const SfxStringItem* pRefItem=nullptr;
+ if (pArgs && (pRefItem = pArgs->GetItemIfSet( SID_STYLE_REFERENCE )))
+ {
+ aRefName = pRefItem->GetValue();
+ }
+
+ pStyleSheet = &(pStylePool->Make( aStyleName, eFamily,
+ SfxStyleSearchBits::UserDefined ) );
+
+ if (pStyleSheet->HasParentSupport())
+ pStyleSheet->SetParent(aRefName);
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_APPLY_STYLE);
+ const SfxStringItem* pFamilyItem = rReq.GetArg<SfxStringItem>(SID_STYLE_FAMILYNAME);
+ if ( pFamilyItem && pNameItem )
+ {
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xStyles;
+ css::uno::Reference< css::container::XNameAccess > xCont = pDocSh->GetModel()->getStyleFamilies();
+ xCont->getByName(pFamilyItem->GetValue()) >>= xStyles;
+ css::uno::Reference< css::beans::XPropertySet > xInfo;
+ xStyles->getByName( pNameItem->GetValue() ) >>= xInfo;
+ OUString aUIName;
+ xInfo->getPropertyValue("DisplayName") >>= aUIName;
+ if ( !aUIName.isEmpty() )
+ rReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aUIName ) );
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+ }
+ [[fallthrough]];
+ }
+ case SID_STYLE_EDIT:
+ case SID_STYLE_DELETE:
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ {
+ const SfxPoolItem* pNameItem;
+ if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
+ aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+ else if ( nSlotId == SID_STYLE_NEW_BY_EXAMPLE )
+ {
+ weld::Window* pDialogParent = rReq.GetFrameWeld();
+ if (!pDialogParent)
+ pDialogParent = GetFrameWeld();
+ SfxNewStyleDlg aDlg(pDialogParent, *pStylePool, eFamily);
+ if (aDlg.run() != RET_OK)
+ return;
+ aStyleName = aDlg.GetName();
+ }
+
+ pStyleSheet = pStylePool->Find( aStyleName, eFamily );
+
+ aOldData.InitFromStyle( pStyleSheet );
+ }
+ break;
+
+ case SID_STYLE_WATERCAN:
+ {
+ bool bWaterCan = pScMod->GetIsWaterCan();
+
+ if( !bWaterCan )
+ {
+ const SfxPoolItem* pItem;
+
+ if ( SfxItemState::SET ==
+ pArgs->GetItemState( nSlotId, true, &pItem ) )
+ {
+ const SfxStringItem* pStrItem = dynamic_cast< const SfxStringItem *>( pItem );
+ if ( pStrItem )
+ {
+ aStyleName = pStrItem->GetValue();
+ pStyleSheet = pStylePool->Find( aStyleName, eFamily );
+
+ if ( pStyleSheet )
+ {
+ static_cast<ScStyleSheetPool*>(pStylePool)->
+ SetActualStyleSheet( pStyleSheet );
+ rReq.Done();
+ }
+ }
+ }
+ }
+
+ if ( !bWaterCan && pStyleSheet )
+ {
+ pScMod->SetWaterCan( true );
+ SetActivePointer( PointerStyle::Fill );
+ rReq.Done();
+ }
+ else
+ {
+ pScMod->SetWaterCan( false );
+ SetActivePointer( PointerStyle::Arrow );
+ rReq.Done();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // set new style for paintbrush format mode
+ if ( nSlotId == SID_STYLE_APPLY && pScMod->GetIsWaterCan() && pStyleSheet )
+ static_cast<ScStyleSheetPool*>(pStylePool)->SetActualStyleSheet( pStyleSheet );
+
+ switch ( eFamily )
+ {
+ case SfxStyleFamily::Para:
+ {
+ switch ( nSlotId )
+ {
+ case SID_STYLE_DELETE:
+ {
+ if ( pStyleSheet )
+ {
+ RemoveStyleSheetInUse( pStyleSheet );
+ pStylePool->Remove( pStyleSheet );
+ InvalidateAttribs();
+ nRetMask = sal_uInt16(true);
+ bAddUndo = true;
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ if ( pStyleSheet )
+ {
+ pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ if ( pStyleSheet && !pScMod->GetIsWaterCan() )
+ {
+ // apply style sheet to document
+ SetStyleSheetToMarked( static_cast<SfxStyleSheet*>(pStyleSheet) );
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ // create/replace style sheet by attributes
+ // at cursor position:
+
+ const ScPatternAttr* pAttrItem = nullptr;
+
+ // The query if marked, was always wrong here,
+ // so now no more, and just from the cursor.
+ // If attributes are to be removed from the selection, still need to be
+ // cautious not to adopt items from templates
+ // (GetSelectionPattern also collects items from originals) (# 44748 #)
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ pAttrItem = rDoc.GetPattern( nCol, nRow, nCurTab );
+
+ SfxItemSet aAttrSet = pAttrItem->GetItemSet();
+ aAttrSet.ClearItem( ATTR_MERGE );
+ aAttrSet.ClearItem( ATTR_MERGE_FLAG );
+
+ // Do not adopt conditional formatting and validity,
+ // because they can not be edited in the template
+ aAttrSet.ClearItem( ATTR_VALIDDATA );
+ aAttrSet.ClearItem( ATTR_CONDITIONAL );
+
+ if ( SID_STYLE_NEW_BY_EXAMPLE == nSlotId )
+ {
+ if ( bUndo )
+ {
+ OUString aUndo = ScResId( STR_UNDO_EDITCELLSTYLE );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
+ bListAction = true;
+ }
+
+ bool bConvertBack = false;
+ SfxStyleSheet* pSheetInUse = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());
+
+ // when a new style is present and is used in the selection,
+ // then the parent can not be adopted:
+ if ( pStyleSheet && pSheetInUse && pStyleSheet == pSheetInUse )
+ pSheetInUse = nullptr;
+
+ // if already present, first remove ...
+ if ( pStyleSheet )
+ {
+ // style pointer to names before erase,
+ // otherwise cells will get invalid pointer
+ //!!! As it happens, a method that does it for a particular style
+ rDoc.StylesToNames();
+ bConvertBack = true;
+ pStylePool->Remove(pStyleSheet);
+ }
+
+ // ...and create new
+ pStyleSheet = &pStylePool->Make( aStyleName, eFamily,
+ SfxStyleSearchBits::UserDefined );
+
+ // when a style is present, then this will become
+ // the parent of the new style:
+ if ( pSheetInUse && pStyleSheet->HasParentSupport() )
+ pStyleSheet->SetParent( pSheetInUse->GetName() );
+
+ if ( bConvertBack )
+ // Name to style pointer
+ rDoc.UpdStlShtPtrsFrmNms();
+ else
+ rDoc.GetPool()->CellStyleCreated( aStyleName, rDoc );
+
+ // Adopt attribute and use style
+ pStyleSheet->GetItemSet().Put( aAttrSet );
+ UpdateStyleSheetInUse( pStyleSheet );
+
+ // call SetStyleSheetToMarked after adding the ScUndoModifyStyle
+ // (pStyleSheet pointer is used!)
+ bStyleToMarked = true;
+ }
+ else // ( nSlotId == SID_STYLE_UPDATE_BY_EXAMPLE )
+ {
+ pStyleSheet = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());
+
+ if ( pStyleSheet )
+ {
+ aOldData.InitFromStyle( pStyleSheet );
+
+ if ( bUndo )
+ {
+ OUString aUndo = ScResId( STR_UNDO_EDITCELLSTYLE );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
+ bListAction = true;
+ }
+
+ pStyleSheet->GetItemSet().Put( aAttrSet );
+ UpdateStyleSheetInUse( pStyleSheet );
+
+ // call SetStyleSheetToMarked after adding the ScUndoModifyStyle
+ // (pStyleSheet pointer is used!)
+ bStyleToMarked = true;
+ }
+ }
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+ rReq.Done();
+ }
+ break;
+
+ default:
+ break;
+ }
+ } // case SfxStyleFamily::Para:
+ break;
+
+ case SfxStyleFamily::Page:
+ {
+ switch ( nSlotId )
+ {
+ case SID_STYLE_DELETE:
+ {
+ nRetMask = sal_uInt16( nullptr != pStyleSheet );
+ if ( pStyleSheet )
+ {
+ if ( rDoc.RemovePageStyleInUse( pStyleSheet->GetName() ) )
+ {
+ ScPrintFunc( pDocSh, GetPrinter(true), nCurTab ).UpdatePages();
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+ pStylePool->Remove( pStyleSheet );
+ rBindings.Invalidate( SID_STYLE_FAMILY4 );
+ pDocSh->SetDocumentModified();
+ bAddUndo = true;
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ nRetMask = sal_uInt16( nullptr != pStyleSheet );
+ if ( pStyleSheet )
+ {
+ pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
+ rBindings.Invalidate( SID_STYLE_FAMILY4 );
+ pDocSh->SetDocumentModified();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ nRetMask = sal_uInt16( nullptr != pStyleSheet );
+ if ( pStyleSheet && !pScMod->GetIsWaterCan() )
+ {
+ std::unique_ptr<ScUndoApplyPageStyle> pUndoAction;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nTabCount)
+ break;
+ OUString aOldName = rDoc.GetPageStyle( rTab );
+ if ( aOldName != aStyleName )
+ {
+ rDoc.SetPageStyle( rTab, aStyleName );
+ ScPrintFunc( pDocSh, GetPrinter(true), rTab ).UpdatePages();
+ if( !pUndoAction )
+ pUndoAction.reset(new ScUndoApplyPageStyle( pDocSh, aStyleName ));
+ pUndoAction->AddSheetAction( rTab, aOldName );
+ }
+ }
+ if( pUndoAction )
+ {
+ pDocSh->GetUndoManager()->AddUndoAction( std::move(pUndoAction) );
+ pDocSh->SetDocumentModified();
+ rBindings.Invalidate( SID_STYLE_FAMILY4 );
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ {
+ const OUString& rStrCurStyle = rDoc.GetPageStyle( nCurTab );
+
+ if ( rStrCurStyle != aStyleName )
+ {
+ SfxStyleSheetBase* pCurStyle = pStylePool->Find( rStrCurStyle, eFamily );
+ SfxItemSet aAttrSet = pCurStyle->GetItemSet();
+ SCTAB nInTab;
+ bool bUsed = rDoc.IsPageStyleInUse( aStyleName, &nInTab );
+
+ // if already present, first remove...
+ if ( pStyleSheet )
+ pStylePool->Remove( pStyleSheet );
+
+ // ...and create new
+ pStyleSheet = &pStylePool->Make( aStyleName, eFamily,
+ SfxStyleSearchBits::UserDefined );
+
+ // Adopt attribute
+ pStyleSheet->GetItemSet().Put( aAttrSet );
+ pDocSh->SetDocumentModified();
+
+ // If being used -> Update
+ if ( bUsed )
+ ScPrintFunc( pDocSh, GetPrinter(true), nInTab ).UpdatePages();
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+ rReq.Done();
+ nRetMask = sal_uInt16(true);
+ }
+ }
+ break;
+
+ default:
+ break;
+ } // switch ( nSlotId )
+ } // case SfxStyleFamily::Page:
+ break;
+
+ case SfxStyleFamily::Frame:
+ {
+ switch ( nSlotId )
+ {
+ case SID_STYLE_DELETE:
+ {
+ if ( pStyleSheet )
+ {
+ pStylePool->Remove( pStyleSheet );
+ InvalidateAttribs();
+ pDocSh->SetDocumentModified();
+ nRetMask = sal_uInt16(true);
+ bAddUndo = true;
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ if ( pStyleSheet )
+ {
+ pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ else
+ nRetMask = sal_uInt16(false);
+ }
+ break;
+
+ case SID_STYLE_APPLY:
+ {
+ if ( pStyleSheet && !pScMod->GetIsWaterCan() )
+ {
+ GetScDrawView()->ScEndTextEdit();
+ GetScDrawView()->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), false);
+
+ GetScDrawView()->InvalidateAttribs();
+ InvalidateAttribs();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_STYLE_NEW_BY_EXAMPLE:
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ if (nSlotId == SID_STYLE_NEW_BY_EXAMPLE)
+ {
+ pStyleSheet = &pStylePool->Make( aStyleName, eFamily, SfxStyleSearchBits::UserDefined );
+
+ // when a style is present, then this will become
+ // the parent of the new style:
+ if (SfxStyleSheet* pOldStyle = GetDrawView()->GetStyleSheet())
+ pStyleSheet->SetParent(pOldStyle->GetName());
+ }
+ else
+ {
+ pStyleSheet = GetDrawView()->GetStyleSheet();
+ aOldData.InitFromStyle( pStyleSheet );
+ }
+
+ if ( bUndo )
+ {
+ OUString aUndo = ScResId( STR_UNDO_EDITGRAPHICSTYLE );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
+ bListAction = true;
+ }
+
+ SfxItemSet aCoreSet(GetDrawView()->GetModel().GetItemPool());
+ GetDrawView()->GetAttributes(aCoreSet, true);
+
+ SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet();
+ pStyleSet->Put(aCoreSet);
+ static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+
+ // call SetStyleSheet after adding the ScUndoModifyStyle
+ // (pStyleSheet pointer is used!)
+ bStyleToMarked = true;
+ rReq.Done();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ } // switch ( eFamily )
+
+ // create new or process through Dialog:
+ if ( nSlotId == SID_STYLE_NEW || nSlotId == SID_STYLE_EDIT )
+ {
+ if ( pStyleSheet )
+ {
+ SfxStyleFamily eFam = pStyleSheet->GetFamily();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg;
+ bool bPage = false;
+
+ // Store old Items from the style
+ SfxItemSet aOldSet = pStyleSheet->GetItemSet();
+ OUString aOldName = pStyleSheet->GetName();
+
+ switch ( eFam )
+ {
+ case SfxStyleFamily::Page:
+ bPage = true;
+ break;
+
+ case SfxStyleFamily::Para:
+ {
+ SfxItemSet& rSet = pStyleSheet->GetItemSet();
+
+ if ( const SfxUInt32Item* pItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT,
+ false ) )
+ {
+ // Produce and format NumberFormat Value from Value and Language
+ sal_uLong nFormat = pItem->GetValue();
+ LanguageType eLang =
+ rSet.Get(ATTR_LANGUAGE_FORMAT ).GetLanguage();
+ sal_uLong nLangFormat = rDoc.GetFormatTable()->
+ GetFormatForLanguageIfBuiltIn( nFormat, eLang );
+ if ( nLangFormat != nFormat )
+ {
+ SfxUInt32Item aNewItem( ATTR_VALUE_FORMAT, nLangFormat );
+ rSet.Put( aNewItem );
+ aOldSet.Put( aNewItem );
+ // Also in aOldSet for comparison after the dialog,
+ // Otherwise might miss a language change
+ }
+ }
+
+ std::unique_ptr<SvxNumberInfoItem> pNumberInfoItem(
+ ScTabViewShell::MakeNumberInfoItem(rDoc, GetViewData()));
+
+ pDocSh->PutItem( *pNumberInfoItem );
+ bPage = false;
+
+ // Definitely a SvxBoxInfoItem with Table = sal_False in set:
+ // (If there is no item, the dialogue will also delete the
+ // BORDER_OUTER SvxBoxItem from the Template Set)
+ if ( rSet.GetItemState( ATTR_BORDER_INNER, false ) != SfxItemState::SET )
+ {
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+ aBoxInfoItem.SetTable(false); // no inner lines
+ aBoxInfoItem.SetDist(true);
+ aBoxInfoItem.SetMinDist(false);
+ rSet.Put( aBoxInfoItem );
+ }
+ }
+ break;
+
+ case SfxStyleFamily::Frame:
+ default:
+ break;
+ }
+
+ SetInFormatDialog(true);
+
+ SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
+ rStyleSet.MergeRange( XATTR_FILL_FIRST, XATTR_FILL_LAST );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ weld::Window* pDialogParent = rReq.GetFrameWeld();
+ if (!pDialogParent)
+ pDialogParent = GetFrameWeld();
+
+ if (eFam == SfxStyleFamily::Frame)
+ pDlg.disposeAndReset(pFact->CreateScDrawStyleDlg(pDialogParent, *pStyleSheet, GetDrawView()));
+ else
+ pDlg.disposeAndReset(pFact->CreateScStyleDlg(pDialogParent, *pStyleSheet, bPage));
+
+ short nResult = pDlg->Execute();
+ SetInFormatDialog(false);
+
+ if ( nResult == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
+
+ if ( pOutSet )
+ {
+ nRetMask = sal_uInt16(pStyleSheet->GetMask());
+
+ // Attribute comparisons (earlier in ModifyStyleSheet) now here
+ // with the old values (the style is already changed)
+ if ( SfxStyleFamily::Para == eFam )
+ {
+ SfxItemSet& rNewSet = pStyleSheet->GetItemSet();
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate(
+ bNumFormatChanged, rNewSet, aOldSet ) )
+ rDoc.InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ rDoc.SetStreamValid(nTab, false);
+
+ sal_uLong nOldFormat = aOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ sal_uLong nNewFormat = rNewSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ if ( nNewFormat != nOldFormat )
+ {
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SvNumberformat* pOld = pFormatter->GetEntry( nOldFormat );
+ const SvNumberformat* pNew = pFormatter->GetEntry( nNewFormat );
+ if ( pOld && pNew && pOld->GetLanguage() != pNew->GetLanguage() )
+ rNewSet.Put( SvxLanguageItem(
+ pNew->GetLanguage(), ATTR_LANGUAGE_FORMAT ) );
+ }
+
+ rDoc.GetPool()->CellStyleCreated( pStyleSheet->GetName(), rDoc );
+ }
+ else if ( SfxStyleFamily::Page == eFam )
+ {
+ //! Here also queries for Page Styles
+
+ OUString aNewName = pStyleSheet->GetName();
+ if ( aNewName != aOldName &&
+ rDoc.RenamePageStyleInUse( aOldName, aNewName ) )
+ {
+ rBindings.Invalidate( SID_STATUS_PAGESTYLE );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+
+ rDoc.ModifyStyleSheet( *pStyleSheet, *pOutSet );
+ rBindings.Invalidate( FID_RESET_PRINTZOOM );
+ }
+ else
+ {
+ SfxItemSet& rAttr = pStyleSheet->GetItemSet();
+ sdr::properties::CleanupFillProperties(rAttr);
+
+ // check for unique names of named items for xml
+ auto checkForUniqueItem = [&] (auto nWhichId)
+ {
+ if (auto pOldItem = rAttr.GetItemIfSet(nWhichId, false))
+ {
+ if (auto pNewItem = pOldItem->checkForUniqueItem(&GetDrawView()->GetModel()))
+ rAttr.Put(std::move(pNewItem));
+ }
+ };
+
+ checkForUniqueItem(XATTR_FILLBITMAP);
+ checkForUniqueItem(XATTR_LINEDASH);
+ checkForUniqueItem(XATTR_LINESTART);
+ checkForUniqueItem(XATTR_LINEEND);
+ checkForUniqueItem(XATTR_FILLGRADIENT);
+ checkForUniqueItem(XATTR_FILLFLOATTRANSPARENCE);
+ checkForUniqueItem(XATTR_FILLHATCH);
+
+ static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
+ GetScDrawView()->InvalidateAttribs();
+ }
+
+ pDocSh->SetDocumentModified();
+
+ if ( SfxStyleFamily::Para == eFam )
+ {
+ ScTabViewShell::UpdateNumberFormatter(
+ *( pDocSh->GetItem(SID_ATTR_NUMBERFORMAT_INFO) ));
+
+ UpdateStyleSheetInUse( pStyleSheet );
+ InvalidateAttribs();
+ }
+
+ aNewData.InitFromStyle( pStyleSheet );
+ bAddUndo = true;
+ }
+ }
+ else
+ {
+ if ( nSlotId == SID_STYLE_NEW )
+ pStylePool->Remove( pStyleSheet );
+ else
+ {
+ // If in the meantime something was painted with the
+ // temporary changed item set
+ pDocSh->PostPaintGridAll();
+ }
+ }
+ }
+ }
+
+ rReq.SetReturnValue( SfxUInt16Item( nSlotId, nRetMask ) );
+
+ if ( bAddUndo && bUndo)
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoModifyStyle>( pDocSh, eFamily, aOldData, aNewData ) );
+
+ if ( bStyleToMarked )
+ {
+ // call SetStyleSheetToMarked after adding the ScUndoModifyStyle,
+ // so redo will find the modified style
+ if (eFamily == SfxStyleFamily::Para)
+ {
+ SetStyleSheetToMarked( static_cast<SfxStyleSheet*>(pStyleSheet) );
+ }
+ else if (eFamily == SfxStyleFamily::Frame)
+ {
+ GetScDrawView()->ScEndTextEdit();
+ GetScDrawView()->SetStyleSheet( static_cast<SfxStyleSheet*>(pStyleSheet), false );
+ }
+ InvalidateAttribs();
+ }
+
+ if ( bListAction )
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ // The above call to ScEndTextEdit left us in an inconsistent state:
+ // Text editing isn't active, but the text edit shell still is. And we
+ // couldn't just deactivate it fully, because in case of editing a
+ // comment, that will make the comment disappear. So let's try to
+ // reactivate text editing instead:
+ auto pFuText = dynamic_cast<FuText*>(GetDrawFuncPtr());
+ if (pFuText && pEditObject != GetDrawView()->GetTextEditObject())
+ {
+ pFuText->SetInEditMode(pEditObject);
+ if (GetDrawView()->GetTextEditOutlinerView())
+ GetDrawView()->GetTextEditOutlinerView()->SetSelection(aSelection);
+ }
+}
+
+void ScTabViewShell::GetStyleState( SfxItemSet& rSet )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
+
+ bool bProtected = false;
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount && !bProtected; i++)
+ if (rDoc.IsTabProtected(i)) // look after protected table
+ bProtected = true;
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ sal_uInt16 nSlotId = 0;
+
+ while ( nWhich )
+ {
+ nSlotId = SfxItemPool::IsWhich( nWhich )
+ ? GetPool().GetSlotId( nWhich )
+ : nWhich;
+
+ switch ( nSlotId )
+ {
+ case SID_STYLE_APPLY:
+ if ( !pStylePool )
+ rSet.DisableItem( nSlotId );
+ break;
+
+ case SID_STYLE_FAMILY2: // cell style sheets
+ {
+ SfxStyleSheet* pStyleSheet = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());
+
+ if ( pStyleSheet )
+ rSet.Put( SfxTemplateItem( nSlotId, pStyleSheet->GetName() ) );
+ else
+ rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
+ }
+ break;
+
+ case SID_STYLE_FAMILY3: // drawing style sheets
+ {
+ SfxStyleSheet* pStyleSheet = GetDrawView()->GetStyleSheet();
+
+ if ( pStyleSheet )
+ rSet.Put( SfxTemplateItem( nSlotId, pStyleSheet->GetName() ) );
+ else
+ rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
+ }
+ break;
+
+ case SID_STYLE_FAMILY4: // page style sheets
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ OUString aPageStyle = rDoc.GetPageStyle( nCurTab );
+ SfxStyleSheet* pStyleSheet = pStylePool ? static_cast<SfxStyleSheet*>(pStylePool->
+ Find( aPageStyle, SfxStyleFamily::Page )) : nullptr;
+
+ if ( pStyleSheet )
+ rSet.Put( SfxTemplateItem( nSlotId, aPageStyle ) );
+ else
+ rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
+ }
+ break;
+
+ case SID_STYLE_WATERCAN:
+ {
+ rSet.Put( SfxBoolItem( nSlotId, SC_MOD()->GetIsWaterCan() ) );
+ }
+ break;
+
+ case SID_STYLE_UPDATE_BY_EXAMPLE:
+ {
+ std::unique_ptr<SfxUInt16Item> pFamilyItem;
+ GetViewFrame().GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem);
+
+ bool bPage = pFamilyItem && SfxStyleFamily::Page == static_cast<SfxStyleFamily>(pFamilyItem->GetValue());
+
+ if ( bProtected || bPage )
+ rSet.DisableItem( nSlotId );
+ }
+ break;
+
+ case SID_STYLE_EDIT:
+ case SID_STYLE_DELETE:
+ case SID_STYLE_HIDE:
+ case SID_STYLE_SHOW:
+ {
+ std::unique_ptr<SfxUInt16Item> pFamilyItem;
+ GetViewFrame().GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem);
+ bool bPage = pFamilyItem && SfxStyleFamily::Page == static_cast<SfxStyleFamily>(pFamilyItem->GetValue());
+
+ if ( bProtected && !bPage )
+ rSet.DisableItem( nSlotId );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshb.cxx b/sc/source/ui/view/tabvwshb.cxx
new file mode 100644
index 0000000000..ad0e757ce0
--- /dev/null
+++ b/sc/source/ui/view/tabvwshb.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 <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/awt/XRequestCallback.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <vcl/errinf.hxx>
+#include <sfx2/app.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <svx/fontworkbar.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/soerr.hxx>
+#include <svl/rectitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/strings.hrc>
+#include <unotools/moduleoptions.hxx>
+#include <sot/exchange.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <client.hxx>
+#include <fuinsert.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <ChartRangeSelectionListener.hxx>
+#include <gridwin.hxx>
+#include <undomanager.hxx>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <svx/svdpagv.hxx>
+#include <o3tl/temporary.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+
+void ScTabViewShell::ConnectObject( const SdrOle2Obj* pObj )
+{
+ // is called from paint
+
+ uno::Reference < embed::XEmbeddedObject > xObj = pObj->GetObjRef();
+ vcl::Window* pWin = GetActiveWin();
+
+ // when already connected do not execute SetObjArea/SetSizeScale again
+
+ SfxInPlaceClient* pClient = FindIPClient( xObj, pWin );
+ if ( pClient )
+ return;
+
+ pClient = new ScClient( this, pWin, &GetScDrawView()->GetModel(), pObj );
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bNegativeX = comphelper::LibreOfficeKit::isActive() && rDoc.IsNegativePage(rViewData.GetTabNo());
+ if (bNegativeX)
+ pClient->SetNegativeX(true);
+
+ tools::Rectangle aRect = pObj->GetLogicRect();
+ Size aDrawSize = aRect.GetSize();
+
+ Size aOleSize = pObj->GetOrigObjSize();
+
+ Fraction aScaleWidth (aDrawSize.Width(), aOleSize.Width() );
+ Fraction aScaleHeight(aDrawSize.Height(), aOleSize.Height() );
+ aScaleWidth.ReduceInaccurate(10); // compatible with SdrOle2Obj
+ aScaleHeight.ReduceInaccurate(10);
+ pClient->SetSizeScale(aScaleWidth,aScaleHeight);
+
+ // visible section is only changed inplace!
+ // the object area must be set after the scaling since it triggers the resizing
+ aRect.SetSize( aOleSize );
+ pClient->SetObjArea( aRect );
+}
+
+namespace {
+
+class PopupCallback : public cppu::WeakImplHelper<css::awt::XCallback>
+{
+ ScTabViewShell* m_pViewShell;
+ SdrOle2Obj* m_pObject;
+
+public:
+ explicit PopupCallback(ScTabViewShell* pViewShell, SdrOle2Obj* pObject)
+ : m_pViewShell(pViewShell)
+ , m_pObject(pObject)
+ {}
+
+ // XCallback
+ virtual void SAL_CALL notify(const css::uno::Any& aData) override
+ {
+ uno::Sequence<beans::PropertyValue> aProperties;
+ if (!(aData >>= aProperties))
+ return;
+
+ awt::Rectangle xRectangle;
+ sal_Int32 dimensionIndex = 0;
+ OUString sPivotTableName("DataPilot1");
+
+ for (beans::PropertyValue const & rProperty : std::as_const(aProperties))
+ {
+ if (rProperty.Name == "Rectangle")
+ rProperty.Value >>= xRectangle;
+ if (rProperty.Name == "DimensionIndex")
+ rProperty.Value >>= dimensionIndex;
+ if (rProperty.Name == "PivotTableName")
+ rProperty.Value >>= sPivotTableName;
+ }
+
+ tools::Rectangle aChartRect = m_pObject->GetLogicRect();
+
+ Point aPoint(xRectangle.X + aChartRect.Left(), xRectangle.Y + aChartRect.Top());
+ Size aSize(xRectangle.Width, xRectangle.Height);
+
+ m_pViewShell->DoDPFieldPopup(sPivotTableName, dimensionIndex, aPoint, aSize);
+ }
+};
+
+}
+
+void ScTabViewShell::ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb)
+{
+ // Do not leave the hint message box on top of the object
+ RemoveHintWindow();
+
+ uno::Reference < embed::XEmbeddedObject > xObj = pObj->GetObjRef();
+ vcl::Window* pWin = GetActiveWin();
+ ErrCodeMsg nErr = ERRCODE_NONE;
+ bool bErrorShown = false;
+
+ {
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bNegativeX = comphelper::LibreOfficeKit::isActive() && rDoc.IsNegativePage(rViewData.GetTabNo());
+ SfxInPlaceClient* pClient = FindIPClient( xObj, pWin );
+ if ( !pClient )
+ pClient = new ScClient( this, pWin, &GetScDrawView()->GetModel(), pObj );
+
+ if (bNegativeX)
+ pClient->SetNegativeX(true);
+
+ if ( (sal_uInt32(nErr.GetCode()) & ERRCODE_ERROR_MASK) == 0 && xObj.is() )
+ {
+ tools::Rectangle aRect = pObj->GetLogicRect();
+
+ {
+ // #i118485# center on BoundRect for activation,
+ // OLE may be sheared/rotated now
+ const tools::Rectangle& rBoundRect = pObj->GetCurrentBoundRect();
+ const Point aDelta(rBoundRect.Center() - aRect.Center());
+ aRect.Move(aDelta.X(), aDelta.Y());
+ }
+
+ Size aDrawSize = aRect.GetSize();
+
+ MapMode aMapMode( MapUnit::Map100thMM );
+ Size aOleSize = pObj->GetOrigObjSize( &aMapMode );
+
+ if ( pClient->GetAspect() != embed::Aspects::MSOLE_ICON
+ && ( xObj->getStatus( pClient->GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) )
+ {
+ // scale must always be 1 - change VisArea if different from client size
+
+ if ( aDrawSize != aOleSize )
+ {
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( pClient->GetAspect() ) );
+ aOleSize = OutputDevice::LogicToLogic( aDrawSize,
+ MapMode(MapUnit::Map100thMM), MapMode(aUnit));
+ awt::Size aSz( aOleSize.Width(), aOleSize.Height() );
+ xObj->setVisualAreaSize( pClient->GetAspect(), aSz );
+ }
+ Fraction aOne( 1, 1 );
+ pClient->SetSizeScale( aOne, aOne );
+ }
+ else
+ {
+ // calculate scale from client and VisArea size
+
+ Fraction aScaleWidth (aDrawSize.Width(), aOleSize.Width() );
+ Fraction aScaleHeight(aDrawSize.Height(), aOleSize.Height() );
+ aScaleWidth.ReduceInaccurate(10); // compatible with SdrOle2Obj
+ aScaleHeight.ReduceInaccurate(10);
+ pClient->SetSizeScale(aScaleWidth,aScaleHeight);
+ }
+
+ // visible section is only changed inplace!
+ // the object area must be set after the scaling since it triggers the resizing
+ aRect.SetSize( aOleSize );
+ pClient->SetObjArea( aRect );
+
+ nErr = pClient->DoVerb( nVerb );
+ bErrorShown = true;
+ // SfxViewShell::DoVerb shows its error messages
+
+ // attach listener to selection changes in chart that affect cell
+ // ranges, so those can be highlighted
+ // note: do that after DoVerb, so that the chart controller exists
+ if ( SvtModuleOptions().IsChart() )
+ {
+ SvGlobalName aObjClsId ( xObj->getClassID() );
+ if (SotExchange::IsChart( aObjClsId ))
+ {
+ try
+ {
+ uno::Reference < embed::XComponentSupplier > xSup( xObj, uno::UNO_QUERY_THROW );
+ uno::Reference< chart2::data::XDataReceiver > xDataReceiver(
+ xSup->getComponent(), uno::UNO_QUERY_THROW );
+ uno::Reference< chart2::data::XRangeHighlighter > xRangeHighlighter(
+ xDataReceiver->getRangeHighlighter());
+ if (xRangeHighlighter.is())
+ {
+ uno::Reference< view::XSelectionChangeListener > xListener(
+ new ScChartRangeSelectionListener( this ));
+ xRangeHighlighter->addSelectionChangeListener( xListener );
+ }
+ uno::Reference<awt::XRequestCallback> xPopupRequest(xDataReceiver->getPopupRequest());
+ if (xPopupRequest.is())
+ {
+ uno::Reference<awt::XCallback> xCallback(new PopupCallback(this, pObj));
+ uno::Any aAny;
+ xPopupRequest->addCallback(xCallback, aAny);
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "Exception caught while querying chart" );
+ }
+ }
+ }
+ }
+ }
+ if (nErr != ERRCODE_NONE && !bErrorShown)
+ ErrorHandler::HandleError(nErr);
+
+ // #i118524# refresh handles to suppress for activated OLE
+ if(GetScDrawView())
+ {
+ GetScDrawView()->AdjustMarkHdl();
+ }
+ //! SetDocumentName should already happen in Sfx ???
+ //TODO/LATER: how "SetDocumentName"?
+ //xIPObj->SetDocumentName( GetViewData().GetDocShell()->GetTitle() );
+}
+
+ErrCode ScTabViewShell::DoVerb(sal_Int32 nVerb)
+{
+ SdrView* pView = GetScDrawView();
+ if (!pView)
+ return ERRCODE_SO_NOTIMPL; // should not be
+
+ SdrOle2Obj* pOle2Obj = nullptr;
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
+ pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
+ }
+
+ if (pOle2Obj)
+ {
+ ActivateObject( pOle2Obj, nVerb );
+ }
+ else
+ {
+ OSL_FAIL("no object for Verb found");
+ }
+
+ return ERRCODE_NONE;
+}
+
+void ScTabViewShell::DeactivateOle()
+{
+ // deactivate inplace editing if currently active
+
+ ScModule* pScMod = SC_MOD();
+ bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
+
+ ScClient* pClient = static_cast<ScClient*>(GetIPClient());
+ if ( pClient && pClient->IsObjectInPlaceActive() && !bUnoRefDialog )
+ pClient->DeactivateObject();
+}
+
+IMPL_LINK( ScTabViewShell, DialogClosedHdl, css::ui::dialogs::DialogClosedEvent*, pEvent, void )
+{
+ if( pEvent->DialogResult == ui::dialogs::ExecutableDialogResults::CANCEL )
+ {
+ ScTabView* pTabView = GetViewData().GetView();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ ScViewData& rData = GetViewData();
+ ScDocShell* pScDocSh = rData.GetDocShell();
+ ScDocument& rScDoc = pScDocSh->GetDocument();
+ // leave OLE inplace mode and unmark
+ OSL_ASSERT( pView );
+ DeactivateOle();
+ pView->UnMarkAll();
+
+ rScDoc.GetUndoManager()->Undo();
+ rScDoc.GetUndoManager()->ClearRedo();
+
+ // leave the draw shell
+ SetDrawShell( false );
+
+ // reset marked cell area
+ ScMarkData aMark = GetViewData().GetMarkData();
+ GetViewData().GetViewShell()->SetMarkData(aMark);
+ }
+ else
+ {
+ OSL_ASSERT( pEvent->DialogResult == ui::dialogs::ExecutableDialogResults::OK );
+ //@todo maybe move chart to different table
+ }
+}
+
+void ScTabViewShell::ExecDrawIns(SfxRequest& rReq)
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+ if (nSlot != SID_OBJECTRESIZE )
+ {
+ SC_MOD()->InputEnterHandler();
+ UpdateInputHandler();
+ }
+
+ // insertion of border for Chart is cancelled:
+ FuPoor* pPoor = GetDrawFuncPtr();
+ if ( pPoor && pPoor->GetSlotID() == SID_DRAW_CHART )
+ GetViewData().GetDispatcher().Execute(SID_DRAW_CHART, SfxCallMode::SLOT | SfxCallMode::RECORD);
+
+ MakeDrawLayer();
+
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ ScTabView* pTabView = GetViewData().GetView();
+ vcl::Window* pWin = pTabView->GetActiveWin();
+ ScDrawView* pView = pTabView->GetScDrawView();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SdrModel& rModel = pView->GetModel();
+
+ switch ( nSlot )
+ {
+ case SID_INSERT_GRAPHIC:
+ FuInsertGraphic(*this, pWin, pView, &rModel, rReq);
+ // shell is set in MarkListHasChanged
+ break;
+
+ case SID_INSERT_AVMEDIA:
+ FuInsertMedia(*this, pWin, pView, &rModel, rReq);
+ // shell is set in MarkListHasChanged
+ break;
+
+ case SID_INSERT_DIAGRAM:
+ FuInsertChart(*this, pWin, pView, &rModel, rReq, LINK( this, ScTabViewShell, DialogClosedHdl ));
+ if (comphelper::LibreOfficeKit::isActive())
+ pDocSh->SetModified();
+ break;
+
+ case SID_INSERT_OBJECT:
+ case SID_INSERT_SMATH:
+ case SID_INSERT_FLOATINGFRAME:
+ FuInsertOLE(*this, pWin, pView, &rModel, rReq);
+ break;
+
+ case SID_INSERT_SIGNATURELINE:
+ case SID_EDIT_SIGNATURELINE:
+ {
+ const uno::Reference<frame::XModel> xModel( GetViewData().GetDocShell()->GetBaseModel() );
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSignatureLineDialog> pDialog(pFact->CreateSignatureLineDialog(
+ pWin->GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_SIGNATURELINE));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_SIGN_SIGNATURELINE:
+ {
+ const uno::Reference<frame::XModel> xModel(
+ GetViewData().GetDocShell()->GetBaseModel());
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSignSignatureLineDialog> pDialog(
+ pFact->CreateSignSignatureLineDialog(GetFrameWeld(), xModel));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_INSERT_QRCODE:
+ case SID_EDIT_QRCODE:
+ {
+ const uno::Reference<frame::XModel> xModel( GetViewData().GetDocShell()->GetBaseModel() );
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractQrCodeGenDialog> pDialog(pFact->CreateQrCodeGenDialog(
+ pWin->GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_QRCODE));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_ADDITIONS_DIALOG:
+ {
+ OUString sAdditionsTag = "";
+
+ const SfxStringItem* pStringArg = rReq.GetArg<SfxStringItem>(FN_PARAM_ADDITIONS_TAG);
+ if (pStringArg)
+ sAdditionsTag = pStringArg->GetValue();
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractAdditionsDialog> pDialog(
+ pFact->CreateAdditionsDialog(pWin->GetFrameWeld(), sAdditionsTag));
+ pDialog->Execute();
+ break;
+ }
+
+ case SID_OBJECTRESIZE:
+ {
+ // the server would like to change the client size
+
+ SfxInPlaceClient* pClient = GetIPClient();
+
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ {
+ const SfxRectangleItem& rRect = rReq.GetArgs()->Get(SID_OBJECTRESIZE);
+ tools::Rectangle aRect( pWin->PixelToLogic( rRect.GetValue() ) );
+
+ if ( pView->AreObjectsMarked() )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ SdrObjKind nSdrObjKind = pObj->GetObjIdentifier();
+
+ if (nSdrObjKind == SdrObjKind::OLE2)
+ {
+ if ( static_cast<SdrOle2Obj*>(pObj)->GetObjRef().is() )
+ {
+ pObj->SetLogicRect(aRect);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_LINKS:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
+ {
+ std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(
+ nullptr, VclMessageType::Warning, VclButtonsType::Ok,
+ SvtResId(STR_WARNING_EXTERNAL_LINK_EDIT_DISABLED)));
+ xError->run();
+ break;
+ }
+
+ ScopedVclPtr<SfxAbstractLinksDialog> pDlg(pFact->CreateLinksDialog(pWin->GetFrameWeld(), rDoc.GetLinkManager()));
+ pDlg->Execute();
+ rBindings.Invalidate( nSlot );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_CREATE_FIELDCONTROL:
+ {
+ const SfxUnoAnyItem* pDescriptorItem = rReq.GetArg<SfxUnoAnyItem>(SID_FM_DATACCESS_DESCRIPTOR);
+ OSL_ENSURE( pDescriptorItem, "SID_FM_CREATE_FIELDCONTROL: invalid request args!" );
+
+ if(pDescriptorItem)
+ {
+ //! merge with ScViewFunc::PasteDataFormat (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE)?
+
+ ScDrawView* pDrView = GetScDrawView();
+ SdrPageView* pPageView = pDrView ? pDrView->GetSdrPageView() : nullptr;
+ if(pPageView)
+ {
+ svx::ODataAccessDescriptor aDescriptor(pDescriptorItem->GetValue());
+ rtl::Reference<SdrObject> pNewDBField = pDrView->CreateFieldControl(aDescriptor);
+
+ if(pNewDBField)
+ {
+ tools::Rectangle aVisArea = pWin->PixelToLogic(tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel()));
+ Point aObjPos(aVisArea.Center());
+ Size aObjSize(pNewDBField->GetLogicRect().GetSize());
+ aObjPos.AdjustX( -(aObjSize.Width() / 2) );
+ aObjPos.AdjustY( -(aObjSize.Height() / 2) );
+ tools::Rectangle aNewObjectRectangle(aObjPos, aObjSize);
+
+ pNewDBField->SetLogicRect(aNewObjectRectangle);
+
+ // controls must be on control layer, groups on front layer
+ if ( dynamic_cast<const SdrUnoObj*>( pNewDBField.get() ) != nullptr )
+ pNewDBField->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pNewDBField->NbcSetLayer(SC_LAYER_FRONT);
+ if (dynamic_cast<const SdrObjGroup*>( pNewDBField.get() ) != nullptr)
+ {
+ SdrObjListIter aIter( *pNewDBField, SdrIterMode::DeepWithGroups );
+ SdrObject* pSubObj = aIter.Next();
+ while (pSubObj)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pSubObj) != nullptr )
+ pSubObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pSubObj->NbcSetLayer(SC_LAYER_FRONT);
+ pSubObj = aIter.Next();
+ }
+ }
+
+ pView->InsertObjectAtView(pNewDBField.get(), *pPageView);
+ }
+ }
+ }
+ rReq.Done();
+ }
+ break;
+
+ case SID_FONTWORK_GALLERY_FLOATER:
+ svx::FontworkBar::execute(*pView, rReq, GetViewFrame().GetBindings());
+ rReq.Ignore();
+ break;
+ }
+}
+
+void ScTabViewShell::GetDrawInsState(SfxItemSet &rSet)
+{
+ bool bOle = GetViewFrame().GetFrame().IsInPlace();
+ bool bTabProt = GetViewData().GetDocument().IsTabProtected(GetViewData().GetTabNo());
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ bool bShared = pDocShell && pDocShell->IsDocShared();
+ SdrView* pSdrView = GetScDrawView();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+ case SID_INSERT_DIAGRAM:
+ if ( bOle || bTabProt || !SvtModuleOptions().IsChart() || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_SMATH:
+ if ( bOle || bTabProt || !SvtModuleOptions().IsMath() || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_OBJECT:
+ case SID_INSERT_FLOATINGFRAME:
+ if ( bOle || bTabProt || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_AVMEDIA:
+ case SID_FONTWORK_GALLERY_FLOATER:
+ if ( bTabProt || bShared )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_INSERT_SIGNATURELINE:
+ if ( bTabProt || bShared || (pSdrView && pSdrView->GetMarkedObjectCount() != 0))
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_EDIT_SIGNATURELINE:
+ case SID_SIGN_SIGNATURELINE:
+ if (!IsSignatureLineSelected() || IsSignatureLineSigned())
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_INSERT_QRCODE:
+ if ( bTabProt || bShared || (pSdrView && pSdrView->GetMarkedObjectCount() != 0))
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_EDIT_QRCODE:
+ if (!IsQRCodeSelected())
+ rSet.DisableItem(nWhich);
+ break;
+
+ case SID_INSERT_GRAPHIC:
+ if (bTabProt || bShared)
+ {
+ // do not disable 'insert graphic' item if the currently marked area is editable (not protected)
+ // if there is no marked area, check the current cell
+ bool bDisableInsertImage = true;
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (!rMark.GetMarkedRanges().empty() && GetViewData().GetDocument().IsSelectionEditable(rMark))
+ bDisableInsertImage = false;
+ else
+ {
+ if (GetViewData().GetDocument().IsBlockEditable
+ (GetViewData().GetTabNo(), GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetCurX(), GetViewData().GetCurY()))
+ {
+ bDisableInsertImage = false;
+ }
+ }
+
+ if (bDisableInsertImage)
+ rSet.DisableItem(nWhich);
+ }
+ break;
+
+ case SID_LINKS:
+ {
+ if (GetViewData().GetDocument().GetLinkManager()->GetLinks().empty())
+ rSet.DisableItem( SID_LINKS );
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+bool ScTabViewShell::IsSignatureLineSelected()
+{
+ SdrView* pSdrView = GetScDrawView();
+ if (!pSdrView)
+ return false;
+
+ if (pSdrView->GetMarkedObjectCount() != 1)
+ return false;
+
+ SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0);
+ if (!pPickObj)
+ return false;
+
+ SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj);
+ if (!pGraphic)
+ return false;
+
+ return pGraphic->isSignatureLine();
+}
+
+bool ScTabViewShell::IsQRCodeSelected()
+{
+ SdrView* pSdrView = GetScDrawView();
+ if (!pSdrView)
+ return false;
+
+ if (pSdrView->GetMarkedObjectCount() != 1)
+ return false;
+
+ SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0);
+ if (!pPickObj)
+ return false;
+
+ SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj);
+ if (!pGraphic)
+ return false;
+
+ if(pGraphic->getQrCode())
+ {
+ return true;
+ }
+ else{
+ return false;
+ }
+}
+
+bool ScTabViewShell::IsSignatureLineSigned()
+{
+ SdrView* pSdrView = GetScDrawView();
+ if (!pSdrView)
+ return false;
+
+ if (pSdrView->GetMarkedObjectCount() != 1)
+ return false;
+
+ SdrObject* pPickObj = pSdrView->GetMarkedObjectByIndex(0);
+ if (!pPickObj)
+ return false;
+
+ SdrGrafObj* pGraphic = dynamic_cast<SdrGrafObj*>(pPickObj);
+ if (!pGraphic)
+ return false;
+
+ return pGraphic->isSignatureLineSigned();
+}
+
+void ScTabViewShell::ExecuteUndo(SfxRequest& rReq)
+{
+ SfxShell* pSh = GetViewData().GetDispatcher().GetShell(0);
+ if (!pSh)
+ return;
+
+ ScUndoManager* pUndoManager = static_cast<ScUndoManager*>(pSh->GetUndoManager());
+
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+
+ sal_uInt16 nSlot = rReq.GetSlot();
+ switch ( nSlot )
+ {
+ case SID_UNDO:
+ case SID_REDO:
+ if ( pUndoManager )
+ {
+ bool bIsUndo = ( nSlot == SID_UNDO );
+
+ sal_uInt16 nCount = 1;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET )
+ nCount = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ // Repair mode: allow undo/redo of all undo actions, even if access would
+ // be limited based on the view shell ID.
+ bool bRepair = false;
+ if (pReqArgs && pReqArgs->GetItemState(SID_REPAIRPACKAGE, false, &pItem) == SfxItemState::SET)
+ bRepair = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ sal_uInt16 nUndoOffset = 0;
+ if (comphelper::LibreOfficeKit::isActive() && !bRepair)
+ {
+ SfxUndoAction* pAction = nullptr;
+ if (bIsUndo)
+ {
+ if (pUndoManager->GetUndoActionCount() != 0)
+ pAction = pUndoManager->GetUndoAction();
+ }
+ else
+ {
+ if (pUndoManager->GetRedoActionCount() != 0)
+ pAction = pUndoManager->GetRedoAction();
+ }
+ if (pAction)
+ {
+ // If another view created the undo action, prevent undoing it from this view.
+ // Unless we know that the other view's undo action is independent from us.
+ ViewShellId nViewShellId = GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ {
+ sal_uInt16 nOffset = 0;
+ if (pUndoManager->IsViewUndoActionIndependent(this, nOffset))
+ {
+ // Execute the undo with an offset: don't undo the top action, but an
+ // earlier one, since it's independent and that belongs to our view.
+ nUndoOffset += nOffset;
+ }
+ else
+ {
+ rReq.SetReturnValue(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ return;
+ }
+ }
+ }
+ }
+
+ // lock paint for more than one cell undo action (not for editing within a cell)
+ bool bLockPaint = ( nCount > 1 && pUndoManager == GetUndoManager() );
+ if ( bLockPaint )
+ pDocSh->LockPaint();
+
+ try
+ {
+ ScUndoRedoContext aUndoRedoContext;
+ aUndoRedoContext.SetUndoOffset(nUndoOffset);
+
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ if ( bIsUndo )
+ pUndoManager->UndoWithContext(aUndoRedoContext);
+ else
+ pUndoManager->RedoWithContext(aUndoRedoContext);
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ // no need to handle. By definition, the UndoManager handled this by clearing the
+ // Undo/Redo stacks
+ }
+
+ if ( bLockPaint )
+ pDocSh->UnlockPaint();
+
+ GetViewFrame().GetBindings().InvalidateAll(false);
+ }
+ break;
+// default:
+// GetViewFrame().ExecuteSlot( rReq );
+ }
+}
+
+void ScTabViewShell::GetUndoState(SfxItemSet &rSet)
+{
+ SfxShell* pSh = GetViewData().GetDispatcher().GetShell(0);
+ if (!pSh)
+ return;
+
+ SfxUndoManager* pUndoManager = pSh->GetUndoManager();
+ ScUndoManager* pScUndoManager = dynamic_cast<ScUndoManager*>(pUndoManager);
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_GETUNDOSTRINGS:
+ case SID_GETREDOSTRINGS:
+ {
+ SfxStringListItem aStrLst( nWhich );
+ if ( pUndoManager )
+ {
+ std::vector<OUString> &aList = aStrLst.GetList();
+ bool bIsUndo = ( nWhich == SID_GETUNDOSTRINGS );
+ size_t nCount = bIsUndo ? pUndoManager->GetUndoActionCount() : pUndoManager->GetRedoActionCount();
+ for (size_t i=0; i<nCount; ++i)
+ {
+ aList.push_back( bIsUndo ? pUndoManager->GetUndoActionComment(i) :
+ pUndoManager->GetRedoActionComment(i) );
+ }
+ }
+ rSet.Put( aStrLst );
+ }
+ break;
+
+ case SID_UNDO:
+ {
+ if (pScUndoManager)
+ {
+ if (pScUndoManager->GetUndoActionCount())
+ {
+ const SfxUndoAction* pAction = pScUndoManager->GetUndoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId()
+ && !pScUndoManager->IsViewUndoActionIndependent(this, o3tl::temporary(sal_uInt16())))
+ {
+ rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put( SfxStringItem( SID_UNDO, SvtResId(STR_UNDO)+pScUndoManager->GetUndoActionComment() ) );
+ }
+ }
+ else
+ rSet.DisableItem( SID_UNDO );
+ }
+ else
+ // get state from sfx view frame
+ GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ break;
+ }
+ case SID_REDO:
+ {
+ if (pScUndoManager)
+ {
+ if (pScUndoManager->GetRedoActionCount())
+ {
+ const SfxUndoAction* pAction = pScUndoManager->GetRedoAction();
+ SfxViewShell *pViewSh = GetViewShell();
+ if (pViewSh && pAction->GetViewShellId() != pViewSh->GetViewShellId())
+ {
+ rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
+ }
+ else
+ {
+ rSet.Put(SfxStringItem(SID_REDO, SvtResId(STR_REDO) + pScUndoManager->GetRedoActionComment()));
+ }
+ }
+ else
+ rSet.DisableItem( SID_REDO );
+ }
+ else
+ // get state from sfx view frame
+ GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ break;
+ }
+ default:
+ // get state from sfx view frame
+ GetViewFrame().GetSlotState( nWhich, nullptr, &rSet );
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshc.cxx b/sc/source/ui/view/tabvwshc.cxx
new file mode 100644
index 0000000000..8e2e74e8e4
--- /dev/null
+++ b/sc/source/ui/view/tabvwshc.cxx
@@ -0,0 +1,775 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/dispatch.hxx>
+#include <editeng/editview.hxx>
+#include <inputhdl.hxx>
+
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <scres.hrc>
+#include <global.hxx>
+#include <scmod.hxx>
+#include <document.hxx>
+#include <uiitems.hxx>
+#include <namedlg.hxx>
+#include <namedefdlg.hxx>
+#include <solvrdlg.hxx>
+#include <optsolver.hxx>
+#include <tabopdlg.hxx>
+#include <consdlg.hxx>
+#include <filtdlg.hxx>
+#include <dbnamdlg.hxx>
+#include <areasdlg.hxx>
+#include <crnrdlg.hxx>
+#include <formula.hxx>
+#include <highred.hxx>
+#include <simpref.hxx>
+#include <funcdesc.hxx>
+#include <dpobject.hxx>
+#include <markdata.hxx>
+#include <reffact.hxx>
+#include <condformatdlg.hxx>
+#include <condformateasydlg.hxx>
+#include <xmlsourcedlg.hxx>
+#include <condformatdlgitem.hxx>
+#include <formdata.hxx>
+#include <inputwin.hxx>
+
+#include <RandomNumberGeneratorDialog.hxx>
+#include <SamplingDialog.hxx>
+#include <DescriptiveStatisticsDialog.hxx>
+#include <AnalysisOfVarianceDialog.hxx>
+#include <CorrelationDialog.hxx>
+#include <CovarianceDialog.hxx>
+#include <ExponentialSmoothingDialog.hxx>
+#include <MovingAverageDialog.hxx>
+#include <RegressionDialog.hxx>
+#include <TTestDialog.hxx>
+#include <FTestDialog.hxx>
+#include <ZTestDialog.hxx>
+#include <ChiSquareTestDialog.hxx>
+#include <FourierAnalysisDialog.hxx>
+
+#include <PivotLayoutDialog.hxx>
+#include <SparklineDialog.hxx>
+#include <SparklineDataRangeDialog.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/unreachable.hxx>
+#include <o3tl/make_shared.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+void ScTabViewShell::SetCurRefDlgId( sal_uInt16 nNew )
+{
+ // CurRefDlgId is stored in ScModule to find if a ref dialog is open,
+ // and in the view to identify the view that has opened the dialog
+ nCurRefDlgId = nNew;
+}
+
+//ugly hack to call Define Name from Manage Names
+void ScTabViewShell::SwitchBetweenRefDialogs(SfxModelessDialogController* pDialog)
+{
+ sal_uInt16 nSlotId = SC_MOD()->GetCurRefDlgId();
+ if( nSlotId == FID_ADD_NAME )
+ {
+ static_cast<ScNameDefDlg*>(pDialog)->GetNewData(maName, maScope);
+ static_cast<ScNameDefDlg*>(pDialog)->Close();
+ sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+ }
+ else if (nSlotId == FID_DEFINE_NAME)
+ {
+ mbInSwitch = true;
+ static_cast<ScNameDlg*>(pDialog)->GetRangeNames(m_RangeMap);
+ static_cast<ScNameDlg*>(pDialog)->Close();
+ sal_uInt16 nId = ScNameDefDlgWrapper::GetChildWindowId();
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
+
+ SC_MOD()->SetRefDialog( nId, pWnd == nullptr );
+ }
+}
+
+std::shared_ptr<SfxModelessDialogController> ScTabViewShell::CreateRefDialogController(
+ SfxBindings* pB, SfxChildWindow* pCW,
+ const SfxChildWinInfo* pInfo,
+ weld::Window* pParent, sal_uInt16 nSlotId)
+{
+ // only open dialog when called through ScModule::SetRefDialog,
+ // so that it does not re appear for instance after a crash (#42341#).
+
+ if ( SC_MOD()->GetCurRefDlgId() != nSlotId )
+ return nullptr;
+
+ if ( nCurRefDlgId != nSlotId )
+ {
+ if (!(comphelper::LibreOfficeKit::isActive() && nSlotId == SID_OPENDLG_FUNCTION))
+ {
+ // the dialog has been opened in a different view
+ // -> lock the dispatcher for this view (modal mode)
+
+ GetViewData().GetDispatcher().Lock( true ); // lock is reset when closing dialog
+ }
+ return nullptr;
+ }
+
+ std::shared_ptr<SfxModelessDialogController> xResult;
+
+ if(pCW)
+ pCW->SetHideNotDelete(true);
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ switch( nSlotId )
+ {
+ case SID_CORRELATION_DIALOG:
+ xResult = std::make_shared<ScCorrelationDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_SAMPLING_DIALOG:
+ xResult = std::make_shared<ScSamplingDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_DESCRIPTIVE_STATISTICS_DIALOG:
+ xResult = std::make_shared<ScDescriptiveStatisticsDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_ANALYSIS_OF_VARIANCE_DIALOG:
+ xResult = std::make_shared<ScAnalysisOfVarianceDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_COVARIANCE_DIALOG:
+ xResult = std::make_shared<ScCovarianceDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_EXPONENTIAL_SMOOTHING_DIALOG:
+ xResult = std::make_shared<ScExponentialSmoothingDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_MOVING_AVERAGE_DIALOG:
+ xResult = std::make_shared<ScMovingAverageDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_REGRESSION_DIALOG:
+ xResult = std::make_shared<ScRegressionDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_FTEST_DIALOG:
+ xResult = std::make_shared<ScFTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_TTEST_DIALOG:
+ xResult = std::make_shared<ScTTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_ZTEST_DIALOG:
+ xResult = std::make_shared<ScZTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_CHI_SQUARE_TEST_DIALOG:
+ xResult = std::make_shared<ScChiSquareTestDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_FOURIER_ANALYSIS_DIALOG:
+ xResult = std::make_shared<ScFourierAnalysisDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case WID_SIMPLE_REF:
+ {
+ // dialog checks, what is in the cell
+
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetRefTabNo( rViewData.GetTabNo() );
+ xResult = std::make_shared<ScSimpleRefDlg>(pB, pCW, pParent);
+ break;
+ }
+ case FID_DEFINE_NAME:
+ {
+ if (!mbInSwitch)
+ {
+ xResult = std::make_shared<ScNameDlg>(pB, pCW, pParent, GetViewData(),
+ ScAddress( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() ) );
+ }
+ else
+ {
+ xResult = std::make_shared<ScNameDlg>( pB, pCW, pParent, GetViewData(),
+ ScAddress( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() ), &m_RangeMap);
+ static_cast<ScNameDlg*>(xResult.get())->SetEntry(maName, maScope);
+ mbInSwitch = false;
+ }
+ break;
+ }
+ case FID_ADD_NAME:
+ {
+ if (!mbInSwitch)
+ {
+ std::map<OUString, ScRangeName*> aRangeMap;
+ rDoc.GetRangeNameMap(aRangeMap);
+ xResult = std::make_shared<ScNameDefDlg>(pB, pCW, pParent, GetViewData(), std::move(aRangeMap),
+ ScAddress(GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo()), true);
+ }
+ else
+ {
+ std::map<OUString, ScRangeName*> aRangeMap;
+ for (auto& itr : m_RangeMap)
+ {
+ aRangeMap.insert(std::pair<OUString, ScRangeName*>(itr.first, &itr.second));
+ }
+ xResult = std::make_shared<ScNameDefDlg>(pB, pCW, pParent, GetViewData(), std::move(aRangeMap),
+ ScAddress(GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo()), false);
+ }
+ break;
+ }
+ case SID_RANDOM_NUMBER_GENERATOR_DIALOG:
+ xResult = std::make_shared<ScRandomNumberGeneratorDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_SPARKLINE_DIALOG:
+ {
+ xResult = std::make_shared<sc::SparklineDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_SPARKLINE_DATA_RANGE_DIALOG:
+ {
+ xResult = std::make_shared<sc::SparklineDataRangeDialog>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_DEFINE_DBNAME:
+ {
+ // when called for an existing range, then mark
+ GetDBData( true, SC_DB_OLD );
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ MarkDataArea( false );
+
+ xResult = std::make_shared<ScDbNameDlg>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_OPENDLG_EDIT_PRINTAREA:
+ xResult = std::make_shared<ScPrintAreasDlg>(pB, pCW, pParent);
+ break;
+ case SID_DEFINE_COLROWNAMERANGES:
+ xResult = std::make_shared<ScColRowNameRangesDlg>(pB, pCW, pParent, GetViewData());
+ break;
+ case SID_OPENDLG_SOLVE:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScAddress aCurPos( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ xResult = std::make_shared<ScSolverDlg>(pB, pCW, pParent, &rViewData.GetDocument(), aCurPos);
+ break;
+ }
+ case SID_OPENDLG_TABOP:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScRefAddress aCurPos ( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo());
+
+ xResult = std::make_shared<ScTabOpDlg>(pB, pCW, pParent, &rViewData.GetDocument(), aCurPos);
+ break;
+ }
+ case SID_OPENDLG_CONSOLIDATE:
+ {
+ SfxItemSetFixed<SCITEM_CONSOLIDATEDATA,
+ SCITEM_CONSOLIDATEDATA> aArgSet( GetPool() );
+
+ const ScConsolidateParam* pDlgData =
+ rDoc.GetConsolidateDlgData();
+
+ if ( !pDlgData )
+ {
+ ScConsolidateParam aConsParam;
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+
+ GetViewData().GetSimpleArea( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab );
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+
+ aConsParam.nCol = nStartCol;
+ aConsParam.nRow = nStartRow;
+ aConsParam.nTab = nStartTab;
+
+ aArgSet.Put( ScConsolidateItem( SCITEM_CONSOLIDATEDATA,
+ &aConsParam ) );
+ }
+ else
+ {
+ aArgSet.Put( ScConsolidateItem( SCITEM_CONSOLIDATEDATA, pDlgData ) );
+ }
+ xResult = std::make_shared<ScConsolidateDlg>(pB, pCW, pParent, aArgSet);
+ break;
+ }
+ case SID_EASY_CONDITIONAL_FORMAT_DIALOG:
+ {
+ xResult = std::make_shared<sc::ConditionalFormatEasyDialog>(pB, pCW, pParent, &GetViewData());
+ break;
+ }
+ case SID_FILTER:
+ {
+
+ ScQueryParam aQueryParam;
+ SfxItemSetFixed<SCITEM_QUERYDATA, SCITEM_QUERYDATA> aArgSet( GetPool() );
+
+ ScDBData* pDBData = GetDBData(false, SC_DB_MAKE, ScGetDBSelection::RowDown);
+ pDBData->ExtendDataArea(rDoc);
+ pDBData->ExtendBackColorArea(rDoc);
+ pDBData->GetQueryParam( aQueryParam );
+
+ ScRange aArea;
+ pDBData->GetArea(aArea);
+ MarkRange(aArea, false);
+
+ aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA,
+ &GetViewData(),
+ &aQueryParam ) );
+
+ // mark current sheet (due to RefInput in dialog)
+ GetViewData().SetRefTabNo( GetViewData().GetTabNo() );
+
+ xResult = std::make_shared<ScFilterDlg>(pB, pCW, pParent, aArgSet);
+ break;
+ }
+ case SID_SPECIAL_FILTER:
+ {
+ ScQueryParam aQueryParam;
+ SfxItemSetFixed<SCITEM_QUERYDATA,
+ SCITEM_QUERYDATA> aArgSet( GetPool() );
+
+ ScDBData* pDBData = GetDBData(false, SC_DB_MAKE, ScGetDBSelection::RowDown);
+ pDBData->ExtendDataArea(rDoc);
+ pDBData->GetQueryParam( aQueryParam );
+
+ ScRange aArea;
+ pDBData->GetArea(aArea);
+ MarkRange(aArea, false);
+
+ ScQueryItem aItem( SCITEM_QUERYDATA, &GetViewData(), &aQueryParam );
+ ScRange aAdvSource;
+ if (pDBData->GetAdvancedQuerySource(aAdvSource))
+ aItem.SetAdvancedQuerySource( &aAdvSource );
+
+ aArgSet.Put( aItem );
+
+ // mark current sheet (due to RefInput in dialog)
+ GetViewData().SetRefTabNo( GetViewData().GetTabNo() );
+
+ xResult = std::make_shared<ScSpecialFilterDlg>(pB, pCW, pParent, aArgSet);
+ break;
+ }
+ case SID_OPENDLG_OPTSOLVER:
+ {
+ ScViewData& rViewData = GetViewData();
+ ScAddress aCurPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ xResult = std::make_shared<ScOptSolverDlg>(pB, pCW, pParent, rViewData.GetDocShell(), aCurPos);
+ break;
+ }
+ case FID_CHG_SHOW:
+ {
+ // dialog checks, what is in the cell
+ xResult = std::make_shared<ScHighlightChgDlg>(pB, pCW, pParent, GetViewData());
+ break;
+ }
+ case SID_MANAGE_XML_SOURCE:
+ {
+ xResult = std::make_shared<ScXMLSourceDlg>(pB, pCW, pParent, &rDoc);
+ break;
+ }
+ case SID_OPENDLG_PIVOTTABLE:
+ {
+ // all settings must be in pDialogDPObject
+
+ if( pDialogDPObject )
+ {
+ // Check for an existing datapilot output.
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetRefTabNo( rViewData.GetTabNo() );
+ ScDPObject* pObj = rDoc.GetDPAtCursor(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo());
+ xResult = std::make_shared<ScPivotLayoutDialog>(pB, pCW, pParent, &rViewData, pDialogDPObject.get(), pObj == nullptr);
+ }
+
+ break;
+ }
+ case SID_OPENDLG_FUNCTION:
+ {
+ if (!isLOKMobilePhone())
+ {
+ // dialog checks, what is in the cell
+ xResult = o3tl::make_shared<ScFormulaDlg>(pB, pCW, pParent, GetViewData(), ScGlobal::GetStarCalcFunctionMgr());
+ }
+ break;
+ }
+ case WID_CONDFRMT_REF:
+ {
+ const ScCondFormatDlgItem* pDlgItem = nullptr;
+ // Get the pool item stored by Conditional Format Manager Dialog.
+ auto itemsRange = GetPool().GetItemSurrogates(SCITEM_CONDFORMATDLGDATA);
+ if (itemsRange.begin() != itemsRange.end())
+ {
+ const SfxPoolItem* pItem = *itemsRange.begin();
+ pDlgItem = static_cast<const ScCondFormatDlgItem*>(pItem);
+ }
+
+ if (pDlgItem)
+ {
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetRefTabNo( rViewData.GetTabNo() );
+
+ xResult = std::make_shared<ScCondFormatDlg>(pB, pCW, pParent, &rViewData, pDlgItem);
+
+ // Remove the pool item stored by Conditional Format Manager Dialog.
+ GetPool().DirectRemoveItemFromPool(*pDlgItem);
+ }
+
+ break;
+ }
+ }
+
+ if (xResult)
+ xResult->Initialize( pInfo );
+ return xResult;
+}
+
+int ScTabViewShell::getPart() const
+{
+ return GetViewData().GetTabNo();
+}
+
+void ScTabViewShell::afterCallbackRegistered()
+{
+ // common tasks
+ SfxViewShell::afterCallbackRegistered();
+
+ UpdateInputHandler(true, false);
+
+ ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : SC_MOD()->GetInputHdl();
+ if (pHdl)
+ {
+ ScInputWindow* pInputWindow = pHdl->GetInputWindow();
+ if (pInputWindow)
+ {
+ pInputWindow->NotifyLOKClient();
+ }
+ }
+}
+
+void ScTabViewShell::NotifyCursor(SfxViewShell* pOtherShell) const
+{
+ ScDrawView* pDrView = const_cast<ScTabViewShell*>(this)->GetScDrawView();
+ if (pDrView)
+ {
+ if (pDrView->GetTextEditObject())
+ {
+ // Blinking cursor.
+ EditView& rEditView = pDrView->GetTextEditOutlinerView()->GetEditView();
+ rEditView.RegisterOtherShell(pOtherShell);
+ rEditView.ShowCursor();
+ rEditView.RegisterOtherShell(nullptr);
+ // Text selection, if any.
+ rEditView.DrawSelectionXOR(pOtherShell);
+ }
+ else
+ {
+ // Graphic selection.
+ pDrView->AdjustMarkHdl(pOtherShell);
+ }
+ }
+
+ const ScGridWindow* pWin = GetViewData().GetActiveWin();
+ if (pWin)
+ pWin->updateKitCellCursor(pOtherShell);
+}
+
+::Color ScTabViewShell::GetColorConfigColor(svtools::ColorConfigEntry nColorType) const
+{
+ const ScViewOptions& rViewOptions = GetViewData().GetOptions();
+
+ switch (nColorType)
+ {
+ case svtools::ColorConfigEntry::DOCCOLOR:
+ {
+ return rViewOptions.GetDocColor();
+ }
+ // Should never be called for an unimplemented color type
+ default:
+ {
+ O3TL_UNREACHABLE;
+ }
+ }
+}
+
+css::uno::Reference<css::datatransfer::XTransferable2> ScTabViewShell::GetClipData(vcl::Window* pWin)
+{
+ SfxViewFrame* pViewFrame = nullptr;
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable;
+ css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard;
+
+ if (pWin)
+ xClipboard = pWin->GetClipboard();
+ else if ((pViewFrame = SfxViewFrame::GetFirst(nullptr, false)))
+ xClipboard = pViewFrame->GetWindow().GetClipboard();
+
+ xTransferable.set(xClipboard.is() ? xClipboard->getContents() : nullptr, css::uno::UNO_QUERY);
+
+ return xTransferable;
+}
+
+void ScTabViewShell::notifyAllViewsHeaderInvalidation(const SfxViewShell* pForViewShell, HeaderType eHeaderType, SCTAB nCurrentTabIndex)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ OString aPayload;
+ switch (eHeaderType)
+ {
+ case COLUMN_HEADER:
+ aPayload = "column"_ostr;
+ break;
+ case ROW_HEADER:
+ aPayload = "row"_ostr;
+ break;
+ case BOTH_HEADERS:
+ default:
+ aPayload = "all"_ostr;
+ break;
+ }
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pViewShell->GetDocId() == pForViewShell->GetDocId()
+ && (nCurrentTabIndex == -1 || pTabViewShell->getPart() == nCurrentTabIndex))
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_HEADER, aPayload);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool ScTabViewShell::isAnyEditViewInRange(const SfxViewShell* pForViewShell, bool bColumns, SCCOLROW nStart, SCCOLROW nEnd)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pForViewShell->GetDocId())
+ {
+ ScInputHandler* pInputHandler = pTabViewShell->GetInputHandler();
+ if (pInputHandler && pInputHandler->GetActiveView())
+ {
+ const ScViewData& rViewData = pTabViewShell->GetViewData();
+ SCCOLROW nPos = bColumns ? rViewData.GetCurX() : rViewData.GetCurY();
+ if (nStart <= nPos && nPos <= nEnd)
+ return true;
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+ }
+ return false;
+}
+
+void ScTabViewShell::notifyAllViewsSheetGeomInvalidation(const SfxViewShell* pForViewShell, bool bColumns,
+ bool bRows, bool bSizes, bool bHidden, bool bFiltered,
+ bool bGroups, SCTAB nCurrentTabIndex)
+{
+ if (!comphelper::LibreOfficeKit::isActive() ||
+ !comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
+ return;
+
+ if (!bColumns && !bRows)
+ return;
+
+ bool bAllTypes = bSizes && bHidden && bFiltered && bGroups;
+ bool bAllDims = bColumns && bRows;
+ OString aPayload = bAllDims ? "all" : bColumns ? "columns" : "rows";
+
+ if (!bAllTypes)
+ {
+ if (bSizes)
+ aPayload += " sizes";
+
+ if (bHidden)
+ aPayload += " hidden";
+
+ if (bFiltered)
+ aPayload += " filtered";
+
+ if (bGroups)
+ aPayload += " groups";
+ }
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pViewShell->GetDocId() == pForViewShell->GetDocId() &&
+ (nCurrentTabIndex == -1 || pTabViewShell->getPart() == nCurrentTabIndex))
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY, aPayload);
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool ScTabViewShell::UseSubTotal(ScRangeList* pRangeList)
+{
+ bool bSubTotal = false;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ size_t nRangeCount (pRangeList->size());
+ size_t nRangeIndex (0);
+ while (!bSubTotal && nRangeIndex < nRangeCount)
+ {
+ const ScRange& rRange = (*pRangeList)[nRangeIndex];
+ SCTAB nTabEnd(rRange.aEnd.Tab());
+ SCTAB nTab(rRange.aStart.Tab());
+ while (!bSubTotal && nTab <= nTabEnd)
+ {
+ SCROW nRowEnd(rRange.aEnd.Row());
+ SCROW nRow(rRange.aStart.Row());
+ while (!bSubTotal && nRow <= nRowEnd)
+ {
+ if (rDoc.RowFiltered(nRow, nTab))
+ bSubTotal = true;
+ else
+ ++nRow;
+ }
+ ++nTab;
+ }
+ ++nRangeIndex;
+ }
+
+ if (!bSubTotal)
+ {
+ const ScDBCollection::NamedDBs& rDBs = rDoc.GetDBCollection()->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ const ScDBData& rDB = *rxDB;
+ if (!rDB.HasAutoFilter())
+ continue;
+
+ nRangeIndex = 0;
+ while (!bSubTotal && nRangeIndex < nRangeCount)
+ {
+ const ScRange & rRange = (*pRangeList)[nRangeIndex];
+ ScRange aDBArea;
+ rDB.GetArea(aDBArea);
+ if (aDBArea.Intersects(rRange))
+ bSubTotal = true;
+ ++nRangeIndex;
+ }
+
+ if (bSubTotal)
+ break;
+ }
+ }
+ return bSubTotal;
+}
+
+OUString ScTabViewShell::DoAutoSum(bool& rRangeFinder, bool& rSubTotal, const OpCode eCode)
+{
+ OUString aFormula;
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ ScRangeList aMarkRangeList;
+ rRangeFinder = rSubTotal = false;
+ rMark.FillRangeListWithMarks( &aMarkRangeList, false );
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ // check if one of the marked ranges is empty
+ bool bEmpty = false;
+ const size_t nCount = aMarkRangeList.size();
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange( aMarkRangeList[i] );
+ if ( rDoc.IsBlockEmpty( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab() ) )
+ {
+ bEmpty = true;
+ break;
+ }
+ }
+
+ if ( bEmpty )
+ {
+ ScRangeList aRangeList;
+ const bool bDataFound = GetAutoSumArea( aRangeList );
+ if ( bDataFound )
+ {
+ ScAddress aAddr = aRangeList.back().aEnd;
+ aAddr.IncRow();
+ const bool bSubTotal( UseSubTotal( &aRangeList ) );
+ EnterAutoSum( aRangeList, bSubTotal, aAddr, eCode );
+ }
+ }
+ else
+ {
+ const bool bSubTotal( UseSubTotal( &aMarkRangeList ) );
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange = aMarkRangeList[i];
+ const bool bSetCursor = ( i == nCount - 1 );
+ const bool bContinue = ( i != 0 );
+ if ( !AutoSum( rRange, bSubTotal, bSetCursor, bContinue, eCode ) )
+ {
+ MarkRange( rRange, false );
+ SetCursor( rRange.aEnd.Col(), rRange.aEnd.Row() );
+ const ScRangeList aRangeList;
+ ScAddress aAddr = rRange.aEnd;
+ aAddr.IncRow();
+ aFormula = GetAutoSumFormula( aRangeList, bSubTotal, aAddr , eCode);
+ break;
+ }
+ }
+ }
+ }
+ else // Only insert into input row
+ {
+ ScRangeList aRangeList;
+ rRangeFinder = GetAutoSumArea( aRangeList );
+ rSubTotal = UseSubTotal( &aRangeList );
+ ScAddress aAddr = GetViewData().GetCurPos();
+ aFormula = GetAutoSumFormula( aRangeList, rSubTotal, aAddr , eCode);
+ }
+ return aFormula;
+}
+
+void ScTabViewShell::InitFormEditData()
+{
+ mpFormEditData.reset(new ScFormEditData);
+}
+
+void ScTabViewShell::ClearFormEditData()
+{
+ mpFormEditData.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshd.cxx b/sc/source/ui/view/tabvwshd.cxx
new file mode 100644
index 0000000000..52d97d1a25
--- /dev/null
+++ b/sc/source/ui/view/tabvwshd.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 <sfx2/childwin.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <docsh.hxx>
+#include <gridwin.hxx>
+
+//! parent window for dialogs
+//! Problem: OLE Server!
+
+weld::Window* ScTabViewShell::GetDialogParent()
+{
+ // if a ref-input dialog is open, use it as parent
+ // (necessary when a slot is executed from the dialog's OK handler)
+ if (nCurRefDlgId && nCurRefDlgId == SC_MOD()->GetCurRefDlgId())
+ {
+ SfxViewFrame& rViewFrm = GetViewFrame();
+ if (rViewFrm.HasChildWindow(nCurRefDlgId))
+ {
+ SfxChildWindow* pChild = rViewFrm.GetChildWindow(nCurRefDlgId);
+ if (pChild)
+ {
+ auto xController = pChild->GetController();
+ weld::Window* pRet = xController ? xController->getDialog() : nullptr;
+ if (pRet && pRet->get_visible())
+ return pRet;
+ }
+ }
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if (pDocSh->IsOle())
+ {
+ // TODO/LATER: how to GetEditWindow in embedded document?!
+ // It should be OK to return the ViewShell Window!
+ vcl::Window* pWin = GetWindow();
+ return pWin ? pWin->GetFrameWeld() : nullptr;
+ // SvInPlaceEnvironment* pEnv = pDocSh->GetIPEnv();
+ // if (pEnv)
+ // return pEnv->GetEditWin();
+ }
+
+ vcl::Window* pWin = GetActiveWin(); // for normal views, too
+ return pWin ? pWin->GetFrameWeld() : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshe.cxx b/sc/source/ui/view/tabvwshe.cxx
new file mode 100644
index 0000000000..0c809b31b3
--- /dev/null
+++ b/sc/source/ui/view/tabvwshe.cxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
+#include <editeng/eeitem.hxx>
+#include <osl/diagnose.h>
+
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <svx/hlnkitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <scmod.hxx>
+#include <impex.hxx>
+#include <editsh.hxx>
+#include <dociter.hxx>
+#include <inputhdl.hxx>
+#include <document.hxx>
+
+OUString ScTabViewShell::GetSelectionText( bool bWholeWord, bool bOnlyASample )
+{
+ OUString aStrSelection;
+
+ if ( pEditShell && pEditShell.get() == GetMySubShell() )
+ {
+ aStrSelection = pEditShell->GetSelectionText( bWholeWord );
+ }
+ else
+ {
+ ScRange aRange;
+
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( (bOnlyASample || bInFormatDialog) && aRange.aStart.Row() != aRange.aEnd.Row() )
+ {
+ // limit range to one data row
+ // (only when the call comes from a format dialog)
+ ScHorizontalCellIterator aIter( rDoc, aRange.aStart.Tab(),
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ SCCOL nCol;
+ SCROW nRow;
+ if ( aIter.GetNext( nCol, nRow ) )
+ {
+ aRange.aStart.SetCol( nCol );
+ aRange.aStart.SetRow( nRow );
+ aRange.aEnd.SetRow( nRow );
+ }
+ else
+ aRange.aEnd = aRange.aStart;
+ }
+ else
+ {
+ // #i111531# with 1M rows it was necessary to limit the range
+ // to the actually used data area.
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ SCTAB nTab1, nTab2;
+ aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ bool bShrunk;
+ rDoc.ShrinkToUsedDataArea( bShrunk, nTab1, nCol1, nRow1, nCol2, nRow2, false);
+ if (bShrunk)
+ {
+ aRange.aStart.SetCol( nCol1 );
+ aRange.aStart.SetRow( nRow1 );
+ aRange.aEnd.SetCol( nCol2 );
+ aRange.aEnd.SetRow( nRow2 );
+ }
+ }
+
+ ScImportExport aObj( rDoc, aRange );
+ // tdf#148437 - if cell contains a formula, overwrite entire content of the cell
+ aObj.SetFormulas(true);
+ OUString aExportOUString;
+ /* TODO: STRING_TSVC under some circumstances? */
+ aObj.ExportString( aExportOUString, SotClipboardFormatId::STRING );
+ aStrSelection = convertLineEnd(aExportOUString, LINEEND_CR);
+
+ // replace Tab/CR with space, if for dialog or through Basic/SelectionTextExt,
+ // or when it is a single row.
+ // Otherwise keep Tabs in multi-row (for instance mail or Basic/SelectionText).
+ // for mail the Tabs are then later changed into (multiple) spaces.
+
+ if ( bInFormatDialog || bWholeWord || aRange.aEnd.Row() == aRange.aStart.Row() )
+ {
+ aStrSelection = aStrSelection.replaceAll("\r", " ");
+ aStrSelection = aStrSelection.replaceAll("\t", " ");
+ aStrSelection = comphelper::string::stripEnd(aStrSelection, ' ');
+ }
+ }
+ }
+
+ return aStrSelection;
+}
+
+void ScTabViewShell::InsertURL( const OUString& rName, const OUString& rURL, const OUString& rTarget,
+ sal_uInt16 nMode )
+{
+ SvxLinkInsertMode eMode = static_cast<SvxLinkInsertMode>(nMode);
+ bool bAsText = ( eMode != HLINK_BUTTON ); // default is now text
+
+ if ( bAsText )
+ {
+ if ( GetViewData().IsActive() )
+ {
+ // if the view is active, always use InsertURLField, which starts EditMode
+ // and selects the URL, so it can be changed from the URL bar / dialog
+
+ InsertURLField( rName, rURL, rTarget );
+ }
+ else
+ {
+ // if the view is not active, InsertURLField doesn't work
+ // -> use InsertBookmark to directly manipulate cell content
+ // bTryReplace=sal_True -> if cell contains only one URL, replace it
+
+ SCCOL nPosX = GetViewData().GetCurX();
+ SCROW nPosY = GetViewData().GetCurY();
+ InsertBookmark( rName, rURL, nPosX, nPosY, &rTarget, true );
+ }
+ }
+ else
+ {
+ SC_MOD()->InputEnterHandler();
+ InsertURLButton( rName, rURL, rTarget, nullptr );
+ }
+}
+
+static void lcl_SelectFieldAfterInsert( EditView& rView )
+{
+ ESelection aSel = rView.GetSelection();
+ if ( aSel.nStartPos == aSel.nEndPos && aSel.nStartPos > 0 )
+ {
+ // Cursor is behind the inserted field -> extend selection to the left
+
+ --aSel.nStartPos;
+ rView.SetSelection( aSel );
+ }
+}
+
+void ScTabViewShell::InsertURLField( const OUString& rName, const OUString& rURL, const OUString& rTarget )
+{
+ SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr );
+ aURLField.SetTargetFrame( rTarget );
+ SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD );
+
+ ScViewData& rViewData = GetViewData();
+ ScModule* pScMod = SC_MOD();
+ ScInputHandler* pHdl = pScMod->GetInputHdl( rViewData.GetViewShell() );
+
+ bool bSelectFirst = false;
+ bool bIsEditMode = pScMod->IsEditMode();
+ int nSelInd = 1;
+ OUString sSeltext(GetSelectionText());
+
+ if ( !bIsEditMode )
+ {
+ if ( !SelectionEditable() )
+ {
+ // no error message (may be called from drag&drop)
+ return;
+ }
+
+ // single url in cell is shown in the dialog and replaced
+ bSelectFirst = HasBookmarkAtCursor( nullptr );
+ pScMod->SetInputMode( SC_INPUT_TABLE );
+ }
+
+ EditView* pTopView = pHdl->GetTopView();
+ EditView* pTableView = pHdl->GetTableView();
+ OSL_ENSURE( pTopView || pTableView, "No EditView" );
+
+ // Check if user selected a whole cell by single click, and cell has content.
+ // tdf#80043 - if true, replace the entire content of the selected cell instead of
+ // inserting a duplicate, or appending the url.
+ if (!bIsEditMode && !bSelectFirst && pTableView && !sSeltext.isEmpty())
+ {
+ nSelInd = sSeltext.getLength();
+ bSelectFirst = true;
+ }
+
+ if ( bSelectFirst )
+ {
+ if ( pTopView )
+ pTopView->SetSelection( ESelection(0,0,0,1) );
+ if ( pTableView )
+ pTableView->SetSelection( ESelection(0,0,0,nSelInd) );
+ }
+
+ pHdl->DataChanging();
+
+ if ( pTopView )
+ {
+ pTopView->InsertField( aURLItem );
+ lcl_SelectFieldAfterInsert( *pTopView );
+ }
+ if ( pTableView )
+ {
+ pTableView->InsertField( aURLItem );
+ lcl_SelectFieldAfterInsert( *pTableView );
+ }
+
+ pHdl->DataChanged();
+}
+
+void ScTabViewShell::ExecSearch( SfxRequest& rReq )
+{
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxPoolItem* pItem;
+
+ switch ( nSlot )
+ {
+ case FID_SEARCH_NOW:
+ {
+ const SvxSearchItem* pSearchItem;
+ if ( pReqArgs &&
+ (pSearchItem = pReqArgs->GetItemIfSet(SID_SEARCH_ITEM, false)) )
+ {
+ ScGlobal::SetSearchItem( *pSearchItem );
+ SearchAndReplace( pSearchItem, true, rReq.IsAPI() );
+ rReq.Done();
+ }
+ }
+ break;
+
+ case SID_SEARCH_ITEM:
+ {
+ const SvxSearchItem* pSearchItem;
+ if (pReqArgs && (pSearchItem =
+ pReqArgs->GetItemIfSet(SID_SEARCH_ITEM, false)))
+ {
+ // remember search item
+ ScGlobal::SetSearchItem( *pSearchItem );
+ }
+ else
+ {
+ OSL_FAIL("SID_SEARCH_ITEM without Parameter");
+ }
+ break;
+ }
+ case FID_SEARCH:
+ case FID_REPLACE:
+ case FID_REPLACE_ALL:
+ case FID_SEARCH_ALL:
+ {
+ if (pReqArgs && SfxItemState::SET == pReqArgs->GetItemState(nSlot, false, &pItem))
+ {
+ // get search item
+
+ SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
+
+ // fill search item
+
+ aSearchItem.SetSearchString(static_cast<const SfxStringItem*>(pItem)->GetValue());
+ if(SfxItemState::SET == pReqArgs->GetItemState(FN_PARAM_1, false, &pItem))
+ aSearchItem.SetReplaceString(static_cast<const SfxStringItem*>(pItem)->GetValue());
+
+ if (nSlot == FID_SEARCH)
+ aSearchItem.SetCommand(SvxSearchCmd::FIND);
+ else if(nSlot == FID_REPLACE)
+ aSearchItem.SetCommand(SvxSearchCmd::REPLACE);
+ else if(nSlot == FID_REPLACE_ALL)
+ aSearchItem.SetCommand(SvxSearchCmd::REPLACE_ALL);
+ else
+ aSearchItem.SetCommand(SvxSearchCmd::FIND_ALL);
+
+ // execute request (which stores the SearchItem)
+
+ aSearchItem.SetWhich(SID_SEARCH_ITEM);
+ GetViewData().GetDispatcher().ExecuteList(FID_SEARCH_NOW,
+ rReq.IsAPI() ? SfxCallMode::API|SfxCallMode::SYNCHRON :
+ SfxCallMode::RECORD,
+ { &aSearchItem });
+ }
+ else
+ {
+ GetViewData().GetDispatcher().Execute(
+ SID_SEARCH_DLG, SfxCallMode::ASYNCHRON|SfxCallMode::RECORD );
+ }
+ }
+ break;
+ case FID_REPEAT_SEARCH:
+ {
+ // once more with ScGlobal::GetSearchItem()
+
+ SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
+ aSearchItem.SetWhich(SID_SEARCH_ITEM);
+ GetViewData().GetDispatcher().ExecuteList( FID_SEARCH_NOW,
+ rReq.IsAPI() ? SfxCallMode::API|SfxCallMode::SYNCHRON :
+ SfxCallMode::RECORD,
+ { &aSearchItem });
+ }
+ break;
+// case FID_SEARCH_COUNT:
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshf.cxx b/sc/source/ui/view/tabvwshf.cxx
new file mode 100644
index 0000000000..2ac3b93760
--- /dev/null
+++ b/sc/source/ui/view/tabvwshf.cxx
@@ -0,0 +1,1094 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <memory>
+
+#include <sfx2/request.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sberrors.hxx>
+#include <svl/ctloptions.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/objface.hxx>
+#include <svx/svxdlg.hxx>
+#include <editeng/colritem.hxx>
+
+#include <tabvwsh.hxx>
+#include <sc.hrc>
+#include <helpids.h>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <globstr.hrc>
+#include <strings.hrc>
+#include <docfunc.hxx>
+#include <eventuno.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+
+#include <scabstdlg.hxx>
+
+#include <tabbgcolor.hxx>
+#include <markdata.hxx>
+
+#include <vector>
+
+using std::unique_ptr;
+using namespace com::sun::star;
+
+void ScTabViewShell::ExecuteTable( SfxRequest& rReq )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ SCTAB nCurrentTab = rViewData.GetTabNo();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ sal_uInt16 nSlot = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ HideListBox(); // Autofilter-DropDown-Listbox
+
+ switch ( nSlot )
+ {
+ case FID_TABLE_VISIBLE:
+ {
+ OUString aName;
+ rDoc.GetName( nCurrentTab, aName );
+
+ bool bVisible=true;
+ if( pReqArgs != nullptr )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FID_TABLE_VISIBLE, &pItem ) )
+ bVisible = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+
+ if( ! bVisible ) // fade out
+ {
+ if ( rDoc.IsDocEditable() )
+ {
+ ScMarkData& rMark = rViewData.GetMarkData();
+ HideTable( rMark );
+ }
+ }
+ else // fade in
+ {
+ std::vector<OUString> rNames { aName };
+ ShowTable( rNames );
+ }
+ }
+ break;
+
+ case FID_TABLE_HIDE:
+ {
+ if ( rDoc.IsDocEditable() )
+ {
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nActiveTab = -1;
+ // For the cases when user right clicks on a non-active tab and hides it. This case is possible for Online.
+ if (pReqArgs)
+ {
+ const SfxPoolItem *pItem;
+ if( pReqArgs->HasItem( FID_TABLE_HIDE, &pItem ) )
+ {
+ SCTAB nTabNumber = static_cast<const SfxInt16Item*>(pItem)->GetValue();
+ // Does selected sheets (tabs) list include the sheet to be hidden?
+ std::set<SCTAB>::iterator it = rMark.GetSelectedTabs().find(nTabNumber);
+ if (it == rMark.GetSelectedTabs().end())
+ {
+ // No it doesn't, so we won't shift the selected tab. Let's remember its position.
+ nActiveTab = GetViewData().GetTabNo();
+ }
+ rMark.SelectOneTable(nTabNumber);
+ }
+ }
+ HideTable( rMark, nActiveTab );
+ }
+ }
+ break;
+
+ case FID_TABLE_SHOW:
+ {
+ std::vector<OUString> rNames;
+ if ( pReqArgs )
+ {
+ const SfxPoolItem* pItem;
+ if( pReqArgs->HasItem( FID_TABLE_SHOW, &pItem ) )
+ {
+ OUString aName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+ rNames.push_back(aName);
+ ShowTable( rNames );
+
+ if( ! rReq.IsAPI() )
+ rReq.Done();
+ }
+ }
+ else
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ VclPtr<AbstractScShowTabDlg> pDlg(pFact->CreateScShowTabDlg(GetFrameWeld()));
+
+ OUString aTabName;
+ bool bFirst = true;
+ for ( SCTAB i=0; i != nTabCount; i++ )
+ {
+ if (!rDoc.IsVisible(i))
+ {
+ rDoc.GetName( i, aTabName );
+ pDlg->Insert( aTabName, bFirst );
+ bFirst = false;
+ }
+ }
+
+ std::shared_ptr<SfxRequest> pReq = std::make_shared<SfxRequest>(rReq);
+ pDlg->StartExecuteAsync([this, pDlg, pReq](sal_Int32 nResult){
+ std::vector<OUString> sTables;
+ if (RET_OK == nResult)
+ {
+ std::vector<sal_Int32> aSelectedRows = pDlg->GetSelectedRows();
+ for (auto a : aSelectedRows)
+ {
+ OUString sTable = pDlg->GetEntry(a);
+ pReq->AppendItem( SfxStringItem( FID_TABLE_SHOW, sTable ) );
+ sTables.push_back(sTable);
+ }
+ ShowTable( sTables );
+ pReq->Done();
+ }
+ pDlg->disposeOnce();
+ });
+ rReq.Ignore();
+ }
+ }
+ break;
+
+ case FID_INS_TABLE:
+ case FID_INS_TABLE_EXT:
+ {
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+ SCTAB nTabNr = nCurrentTab;
+
+ if ( !rDoc.IsDocEditable() )
+ break; // locked
+
+ if ( pReqArgs != nullptr ) // from basic
+ {
+ bool bOk = false;
+ const SfxPoolItem* pTabItem;
+ const SfxPoolItem* pNameItem;
+
+ if ( pReqArgs->HasItem( FN_PARAM_1, &pTabItem ) &&
+ pReqArgs->HasItem( nSlot, &pNameItem ) )
+ {
+ OUString aName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
+ rDoc.CreateValidTabName(aName);
+
+ // sheet number from basic: 1-based
+ // 0 is special, means adding at the end
+ nTabNr = static_cast<const SfxUInt16Item*>(pTabItem)->GetValue();
+ if (nTabNr == 0)
+ nTabNr = nTabCount;
+ else
+ --nTabNr;
+
+ if (nTabNr > nTabCount)
+ nTabNr = nTabCount;
+
+ bOk = InsertTable(aName, nTabNr);
+ }
+
+ if (bOk)
+ rReq.Done( *pReqArgs );
+ //! else set error
+ }
+ else // dialog
+ {
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScInsertTableDlg> pDlg(pFact->CreateScInsertTableDlg(GetFrameWeld(), rViewData,
+ nTabSelCount, nSlot == FID_INS_TABLE_EXT));
+ if ( RET_OK == pDlg->Execute() )
+ {
+ if (pDlg->GetTablesFromFile())
+ {
+ std::vector<SCTAB> nTabs;
+ sal_uInt16 n = 0;
+ const OUString* pStr = pDlg->GetFirstTable( &n );
+ while ( pStr )
+ {
+ nTabs.push_back( static_cast<SCTAB>(n) );
+ pStr = pDlg->GetNextTable( &n );
+ }
+ bool bLink = pDlg->GetTablesAsLink();
+ if (!nTabs.empty())
+ {
+ if(pDlg->IsTableBefore())
+ {
+ ImportTables( pDlg->GetDocShellTables(), nTabs.size(), nTabs.data(),
+ bLink,nTabNr );
+ }
+ else
+ {
+ SCTAB nTabAfter = nTabNr+1;
+
+ for(SCTAB j=nCurrentTab+1;j<nTabCount;j++)
+ {
+ if(!rDoc.IsScenario(j))
+ {
+ nTabAfter=j;
+ break;
+ }
+ }
+
+ ImportTables( pDlg->GetDocShellTables(), nTabs.size(), nTabs.data(),
+ bLink,nTabAfter );
+ }
+ }
+ }
+ else
+ {
+ SCTAB nCount=pDlg->GetTableCount();
+ if(pDlg->IsTableBefore())
+ {
+ if(nCount==1 && !pDlg->GetFirstTable()->isEmpty())
+ {
+ rReq.AppendItem( SfxStringItem( FID_INS_TABLE, *pDlg->GetFirstTable() ) );
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nTabNr) + 1 ) ); // 1-based
+ rReq.Done();
+
+ InsertTable( *pDlg->GetFirstTable(), nTabNr );
+ }
+ else
+ {
+ std::vector<OUString> aNames(0);
+ InsertTables( aNames, nTabNr,nCount );
+ }
+ }
+ else
+ {
+ SCTAB nTabAfter = nTabNr+1;
+ SCTAB nSelHigh = rMark.GetLastSelected();
+
+ for(SCTAB j=nSelHigh+1;j<nTabCount;j++)
+ {
+ if(!rDoc.IsScenario(j))
+ {
+ nTabAfter=j;
+ break;
+ }
+ else // #101672#; increase nTabAfter, because it is possible that the scenario tables are the last
+ nTabAfter = j + 1;
+ }
+
+ if(nCount==1 && !pDlg->GetFirstTable()->isEmpty())
+ {
+ rReq.AppendItem( SfxStringItem( FID_INS_TABLE, *pDlg->GetFirstTable() ) );
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nTabAfter) + 1 ) ); // 1-based
+ rReq.Done();
+
+ InsertTable( *pDlg->GetFirstTable(), nTabAfter);
+ }
+ else
+ {
+ std::vector<OUString> aNames(0);
+ InsertTables( aNames, nTabAfter,nCount);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_APPEND:
+ case FID_TAB_RENAME:
+ case FID_TAB_MENU_RENAME:
+ {
+ // FID_TAB_MENU_RENAME - "rename" in menu
+ // FID_TAB_RENAME - "name"-property for basic
+ // equal execute, but MENU_RENAME may be disabled inside GetState
+
+ if ( nSlot == FID_TAB_MENU_RENAME )
+ nSlot = FID_TAB_RENAME; // equal execute
+
+ SCTAB nTabNr = rViewData.GetTabNo();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ if ( !rDoc.IsDocEditable() )
+ break; // everything locked
+
+ if ( nSlot != FID_TAB_APPEND &&
+ ( rDoc.IsTabProtected( nTabNr ) || nTabSelCount > 1 ) )
+ break; // no rename
+
+ if( pReqArgs != nullptr )
+ {
+ bool bDone = false;
+ const SfxPoolItem* pItem;
+ OUString aName;
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ {
+ nTabNr = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ // inserting is 1-based, let's be consistent
+ if (nTabNr > 0)
+ --nTabNr;
+ }
+
+ if( pReqArgs->HasItem( nSlot, &pItem ) )
+ aName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ switch ( nSlot )
+ {
+ case FID_TAB_APPEND:
+ bDone = AppendTable( aName );
+ break;
+ case FID_TAB_RENAME:
+ bDone = RenameTable( aName, nTabNr );
+ break;
+ }
+
+ if( bDone )
+ {
+ rReq.Done( *pReqArgs );
+ }
+ }
+ else
+ {
+ sal_uInt16 nRet = RET_OK;
+ bool bDone = false;
+ OUString aErrMsg ( ScResId( STR_INVALIDTABNAME ) );
+ OUString aName;
+ OUString aDlgTitle;
+ OUString sHelpId;
+
+ switch ( nSlot )
+ {
+ case FID_TAB_APPEND:
+ aDlgTitle = ScResId(SCSTR_APDTABLE);
+ rDoc.CreateValidTabName( aName );
+ sHelpId = HID_SC_APPEND_NAME;
+ break;
+
+ case FID_TAB_RENAME:
+ aDlgTitle = ScResId(SCSTR_RENAMETAB);
+ rDoc.GetName( rViewData.GetTabNo(), aName );
+ sHelpId = HID_SC_RENAME_NAME;
+ break;
+ }
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScStringInputDlg> pDlg(pFact->CreateScStringInputDlg(
+ GetFrameWeld(), aDlgTitle, ScResId(SCSTR_NAME),
+ aName, GetStaticInterface()->GetSlot(nSlot)->GetCommand(),
+ sHelpId));
+
+
+ while ( !bDone && nRet == RET_OK )
+ {
+ nRet = pDlg->Execute();
+
+ if ( nRet == RET_OK )
+ {
+ aName = pDlg->GetInputString();
+
+ switch ( nSlot )
+ {
+ case FID_TAB_APPEND:
+ bDone = AppendTable( aName );
+ break;
+ case FID_TAB_RENAME:
+ bDone = RenameTable( aName, nTabNr );
+ break;
+ }
+
+ if ( bDone )
+ {
+ rReq.AppendItem( SfxStringItem( nSlot, aName ) );
+ rReq.Done();
+ }
+ else
+ {
+ if( rReq.IsAPI() )
+ {
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::Error( ERRCODE_BASIC_SETPROP_FAILED ); // XXX error handling???
+#endif
+ }
+ else
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, aErrMsg));
+ nRet = xBox->run();
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_MOVE:
+ {
+ if ( rDoc.GetChangeTrack() != nullptr )
+ break; // if ChangeTracking is active, then no TabMove
+
+ bool bDoIt = false;
+ sal_uInt16 nDoc = 0;
+ SCTAB nTab = rViewData.GetTabNo();
+ bool bCpy = false, bUseCurrentDocument = false;
+ OUString aDocName;
+ OUString aTabName;
+
+ if( pReqArgs != nullptr )
+ {
+ SCTAB nTableCount = rDoc.GetTableCount();
+ const SfxPoolItem* pItem;
+
+ // if UseCurrentDocument(FN_PARAM_3) is true ignore the document name provided and use current document
+ if( pReqArgs->HasItem( FN_PARAM_3, &pItem ) )
+ bUseCurrentDocument = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ if (bUseCurrentDocument)
+ aDocName = GetViewData().GetDocShell()->GetTitle();
+ else if(pReqArgs->HasItem( FID_TAB_MOVE, &pItem ))
+ aDocName = static_cast<const SfxStringItem*>(pItem)->GetValue();
+
+ if( pReqArgs->HasItem( FN_PARAM_1, &pItem ) )
+ {
+ // table is 1-based
+ nTab = static_cast<const SfxUInt16Item*>(pItem)->GetValue() - 1;
+ if ( nTab >= nTableCount )
+ nTab = SC_TAB_APPEND;
+ }
+ if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) )
+ bCpy = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+
+ if (!aDocName.isEmpty())
+ {
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ ScDocShell* pScSh = nullptr;
+ sal_uInt16 i=0;
+
+ while ( pSh )
+ {
+ pScSh = dynamic_cast<ScDocShell*>( pSh );
+
+ if( pScSh )
+ {
+ pScSh->GetTitle();
+
+ if (aDocName == pScSh->GetTitle())
+ {
+ nDoc = i;
+ ScDocument& rDestDoc = pScSh->GetDocument();
+ nTableCount = rDestDoc.GetTableCount();
+ bDoIt = rDestDoc.IsDocEditable();
+ break;
+ }
+
+ i++; // only count ScDocShell
+ }
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+ }
+ else // no doc-name -> new doc
+ {
+ nDoc = SC_DOC_NEW;
+ bDoIt = true;
+ }
+
+ if ( bDoIt && nTab >= nTableCount ) // if necessary append
+ nTab = SC_TAB_APPEND;
+ }
+ else
+ {
+ OUString aDefaultName;
+ rDoc.GetName( rViewData.GetTabNo(), aDefaultName );
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+
+ ScopedVclPtr<AbstractScMoveTableDlg> pDlg(pFact->CreateScMoveTableDlg(GetFrameWeld(),
+ aDefaultName));
+
+ SCTAB nTableCount = rDoc.GetTableCount();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ if(nTableCount==nTabSelCount)
+ {
+ pDlg->SetForceCopyTable();
+ }
+
+ // We support direct renaming of sheet only when one sheet
+ // is selected.
+ pDlg->EnableRenameTable(nTabSelCount == 1);
+
+ if ( pDlg->Execute() == RET_OK )
+ {
+ nDoc = pDlg->GetSelectedDocument();
+ nTab = pDlg->GetSelectedTable();
+ bCpy = pDlg->GetCopyTable();
+ bool bRna = pDlg->GetRenameTable();
+ // Leave aTabName string empty, when Rename is FALSE.
+ if( bRna )
+ {
+ pDlg->GetTabNameString( aTabName );
+ }
+ bDoIt = true;
+
+ OUString aFoundDocName;
+ if ( nDoc != SC_DOC_NEW )
+ {
+ ScDocShell* pSh = ScDocShell::GetShellByNum( nDoc );
+ if (pSh)
+ {
+ aFoundDocName = pSh->GetTitle();
+ if ( !pSh->GetDocument().IsDocEditable() )
+ {
+ ErrorMessage(STR_READONLYERR);
+ bDoIt = false;
+ }
+ }
+ }
+ rReq.AppendItem( SfxStringItem( FID_TAB_MOVE, aFoundDocName ) );
+ // 1-based table, if not APPEND
+ SCTAB nBasicTab = ( nTab <= MAXTAB ) ? (nTab+1) : nTab;
+ rReq.AppendItem( SfxUInt16Item( FN_PARAM_1, static_cast<sal_uInt16>(nBasicTab) ) );
+ rReq.AppendItem( SfxBoolItem( FN_PARAM_2, bCpy ) );
+ }
+ }
+
+ if( bDoIt )
+ {
+ rReq.Done(); // record, while doc is active
+
+ MoveTable( nDoc, nTab, bCpy, &aTabName );
+ }
+ }
+ break;
+
+ case FID_TAB_DUPLICATE:
+ {
+ // Get info about current document and selected tab
+ SCTAB nTab = rViewData.GetTabNo();
+ OUString aDocName = GetViewData().GetDocShell()->GetTitle();
+ sal_uInt16 nDoc = 0;
+ bool bCpy = true;
+
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ ScDocShell* pScSh = nullptr;
+ sal_uInt16 i = 0;
+
+ // Determine the index of the current document
+ while ( pSh )
+ {
+ pScSh = dynamic_cast<ScDocShell*>( pSh );
+
+ if( pScSh )
+ {
+ pScSh->GetTitle();
+
+ if (aDocName == pScSh->GetTitle())
+ {
+ nDoc = i;
+ break;
+ }
+ // Only count ScDocShell
+ i++;
+ }
+ pSh = SfxObjectShell::GetNext( *pSh );
+ }
+
+ MoveTable( nDoc, nTab + 1, bCpy );
+ }
+ break;
+
+ case FID_DELETE_TABLE:
+ {
+ bool bHasIndex = (pReqArgs != nullptr);
+
+ // allow removing via the Index/FID_DELETE_TABLE parameter
+ SCTAB nTabNr = nCurrentTab;
+ if (bHasIndex)
+ {
+ const SfxPoolItem* pItem;
+ if (pReqArgs->HasItem(FID_DELETE_TABLE, &pItem))
+ {
+ nTabNr = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ // inserting is 1-based, let's be consistent
+ if (nTabNr > 0)
+ --nTabNr;
+ }
+ }
+
+ bool bDoIt = bHasIndex;
+ if (!bDoIt)
+ {
+ bool bTabWithPivotTable = false;
+ if (rDoc.HasPivotTable())
+ {
+ const ScDPCollection* pDPs = rDoc.GetDPCollection();
+ if (pDPs)
+ {
+ const ScMarkData::MarkedTabsType& rSelectedTabs = rViewData.GetMarkData().GetSelectedTabs();
+ const size_t nCount = pDPs->GetCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const ScDPObject& rDPObj = (*pDPs)[i];
+ const ScSheetSourceDesc* pSheetSourceDesc = rDPObj.GetSheetDesc();
+ if (pSheetSourceDesc)
+ {
+ SCTAB nTabOut = rDPObj.GetOutRange().aStart.Tab();
+ SCTAB nTabSource = pSheetSourceDesc->GetSourceRange().aStart.Tab();
+ bool bTabOutSel = false;
+ for (const SCTAB nSelTab : rSelectedTabs)
+ {
+ if (nSelTab == nTabSource)
+ bTabWithPivotTable = true;
+ if (nSelTab == nTabOut)
+ bTabOutSel = true;
+ if (bTabWithPivotTable && bTabOutSel)
+ break;
+ }
+ // if both pivot table and data are selected
+ // no need to warn for source data losing
+ if (bTabWithPivotTable && bTabOutSel)
+ bTabWithPivotTable = false;
+ if (bTabWithPivotTable)
+ break;
+ }
+ }
+ }
+ }
+
+ SCTAB nTabSelCnt = rViewData.GetMarkData().GetSelectCount();
+ OUString aTabSelCnt = Application::GetSettings().GetUILocaleDataWrapper().getNum( nTabSelCnt, 0 );
+ OUString aQueryDeleteTab = ScResId( STR_QUERY_DELTAB, nTabSelCnt )
+ .replaceAll( "%d", aTabSelCnt );
+ if (bTabWithPivotTable)
+ {
+ OUString aStr = ScResId( STR_QUERY_PIVOTTABLE_DELTAB, nTabSelCnt )
+ .replaceAll( "%d", aTabSelCnt )
+ + " " + aQueryDeleteTab;
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aStr));
+ xQueryBox->set_default_response(RET_NO);
+
+ // Hard warning as there is potential of data loss on deletion
+ bDoIt = (RET_YES == xQueryBox->run());
+ }
+ else
+ {
+ bool bHasData = false;
+ ScMarkData& rMark = rViewData.GetMarkData();
+ for ( SCTAB i = 0; i < nTabCount && !bHasData; i++ )
+ {
+ if ( rMark.GetTableSelect(i) && !rDoc.IsTabProtected(i) )
+ {
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ bHasData = rDoc.GetDataStart( i, nStartCol, nStartRow );
+ }
+ }
+ // Do not ask for confirmation if all selected tabs are empty
+ if (bHasData)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aQueryDeleteTab));
+ xQueryBox->set_default_response(RET_YES);
+
+ // no parameter given, ask for confirmation
+ bDoIt = (RET_YES == xQueryBox->run());
+ }
+ else
+ bDoIt = true;
+ }
+ }
+
+ if (bDoIt)
+ {
+ SCTAB nNewTab = nCurrentTab;
+ std::vector<SCTAB> TheTabs;
+
+ if (bHasIndex)
+ {
+ // sheet no. provided by the parameter
+ TheTabs.push_back(nTabNr);
+ if (nNewTab > nTabNr && nNewTab > 0)
+ --nNewTab;
+ }
+ else
+ {
+ SCTAB nFirstTab = 0;
+ bool bTabFlag = false;
+ ScMarkData& rMark = rViewData.GetMarkData();
+ for (SCTAB i = 0; i < nTabCount; i++)
+ {
+ if (rMark.GetTableSelect(i) && !rDoc.IsTabProtected(i))
+ {
+ TheTabs.push_back(i);
+ bTabFlag = true;
+ if (nNewTab == i && i+1 < nTabCount)
+ nNewTab++;
+ }
+ if (!bTabFlag)
+ nFirstTab = i;
+ }
+ if (nNewTab >= nTabCount - static_cast<SCTAB>(TheTabs.size()))
+ nNewTab = nFirstTab;
+ }
+
+ rViewData.SetTabNo(nNewTab);
+ DeleteTables(TheTabs);
+ TheTabs.clear();
+ rReq.Done();
+ }
+ }
+ break;
+
+ case FID_TAB_RTL:
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+ bool bSet = !rDoc.IsLayoutRTL( nCurrentTab );
+
+ const ScMarkData& rMark = rViewData.GetMarkData();
+ if ( rMark.GetSelectCount() != 0 )
+ {
+ // handle several sheets
+
+ SfxUndoManager* pUndoManager = pDocSh->GetUndoManager();
+ OUString aUndo = ScResId( STR_UNDO_TAB_RTL );
+ pUndoManager->EnterListAction( aUndo, aUndo, 0, rViewData.GetViewShell()->GetViewShellId() );
+
+ for (const auto& rTab : rMark)
+ rFunc.SetLayoutRTL( rTab, bSet );
+
+ pUndoManager->LeaveListAction();
+ }
+ else
+ rFunc.SetLayoutRTL( nCurrentTab, bSet );
+ }
+ break;
+
+ case FID_TAB_TOGGLE_GRID:
+ {
+ bool bShowGrid = rViewData.GetShowGrid();
+ rViewData.SetShowGrid(!bShowGrid);
+ SfxBindings& rBindings = GetViewFrame().GetBindings();
+ rBindings.Invalidate( FID_TAB_TOGGLE_GRID );
+ ScDocShellModificator aModificator(*rViewData.GetDocShell());
+ aModificator.SetDocumentModified();
+ PaintGrid();
+ rReq.Done();
+ }
+ break;
+
+ case FID_TAB_SET_TAB_BG_COLOR:
+ case FID_TAB_MENU_SET_TAB_BG_COLOR:
+ {
+ if ( nSlot == FID_TAB_MENU_SET_TAB_BG_COLOR )
+ nSlot = FID_TAB_SET_TAB_BG_COLOR;
+ SCTAB nTabNr = rViewData.GetTabNo();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+ if ( !rDoc.IsDocEditable() )
+ break;
+
+ if ( rDoc.IsTabProtected( nTabNr ) ) // ||nTabSelCount > 1
+ break;
+
+ if( pReqArgs != nullptr )
+ {
+ bool bDone = false;
+ const SfxPoolItem* pItem;
+ Color aColor;
+
+ if( pReqArgs->HasItem( nSlot, &pItem ) )
+ aColor = static_cast<const SvxColorItem*>(pItem)->GetValue();
+
+ if ( nTabSelCount > 1 )
+ {
+ std::unique_ptr<ScUndoTabColorInfo::List>
+ pTabColorList(new ScUndoTabColorInfo::List);
+ for (const auto& rTab : rMark)
+ {
+ if ( !rDoc.IsTabProtected(rTab) )
+ {
+ ScUndoTabColorInfo aTabColorInfo(rTab);
+ aTabColorInfo.maNewTabBgColor = aColor;
+ pTabColorList->push_back(aTabColorInfo);
+ }
+ }
+ bDone = SetTabBgColor( *pTabColorList );
+ }
+ else
+ {
+ bDone = SetTabBgColor( aColor, nCurrentTab ); //ScViewFunc.SetTabBgColor
+ }
+ if( bDone )
+ {
+ rReq.Done( *pReqArgs );
+ }
+ }
+ else
+ {
+ sal_uInt16 nRet = RET_OK; /// temp
+ bool bDone = false; /// temp
+
+ Color aTabBgColor = rDoc.GetTabBgColor( nCurrentTab );
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractScTabBgColorDlg> pDlg(pFact->CreateScTabBgColorDlg(
+ GetFrameWeld(),
+ ScResId(SCSTR_SET_TAB_BG_COLOR),
+ ScResId(SCSTR_NO_TAB_BG_COLOR),
+ aTabBgColor));
+ while ( !bDone && nRet == RET_OK )
+ {
+ nRet = pDlg->Execute();
+ if( nRet == RET_OK )
+ {
+ Color aSelectedColor;
+ pDlg->GetSelectedColor(aSelectedColor);
+ std::unique_ptr<ScUndoTabColorInfo::List>
+ pTabColorList(new ScUndoTabColorInfo::List);
+ if ( nTabSelCount > 1 )
+ {
+ for (const auto& rTab : rMark)
+ {
+ if ( !rDoc.IsTabProtected(rTab) )
+ {
+ ScUndoTabColorInfo aTabColorInfo(rTab);
+ aTabColorInfo.maNewTabBgColor = aSelectedColor;
+ pTabColorList->push_back(aTabColorInfo);
+ }
+ }
+ bDone = SetTabBgColor( *pTabColorList );
+ }
+ else
+ {
+ bDone = SetTabBgColor( aSelectedColor, nCurrentTab ); //ScViewFunc.SetTabBgColor
+ }
+
+ if ( bDone )
+ {
+ rReq.AppendItem( SvxColorItem( aTabBgColor, nSlot ) );
+ rReq.Done();
+ }
+ else
+ {
+ if( rReq.IsAPI() )
+ {
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::Error( ERRCODE_BASIC_SETPROP_FAILED );
+#endif
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case FID_TAB_EVENTS:
+ {
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ uno::Reference<container::XNameReplace> xEvents( new ScSheetEventsObj( pDocSh, nCurrentTab ) );
+ uno::Reference<frame::XFrame> xFrame = GetViewFrame().GetFrame().GetFrameInterface();
+ SvxAbstractDialogFactory* pDlgFactory = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDialog( pDlgFactory->CreateSvxMacroAssignDlg(
+ GetFrameWeld(), xFrame, false, xEvents, 0 ) );
+ if ( pDialog->Execute() == RET_OK )
+ {
+ // the dialog modifies the settings directly
+ }
+ }
+ break;
+ case FID_TOGGLEHIDDENCOLROW:
+ {
+ svtools::EditableColorConfig aEditableConfig;
+ svtools::ColorConfigValue aValue = aEditableConfig.GetColorValue(svtools::CALCHIDDENROWCOL);
+ aValue.bIsVisible = !aValue.bIsVisible;
+ aEditableConfig.SetColorValue(svtools::CALCHIDDENROWCOL, aValue);
+ }
+ break;
+ default:
+ OSL_FAIL("unknown message for ViewShell");
+ break;
+ }
+}
+
+void ScTabViewShell::GetStateTable( SfxItemSet& rSet )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocShell = rViewData.GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while ( nWhich )
+ {
+ switch ( nWhich )
+ {
+
+ case FID_TABLE_VISIBLE:
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsVisible(nTab) ));
+ break;
+
+ case FID_TABLE_HIDE:
+ {
+ sal_uInt16 nVis = 0;
+ // enable menu : check to make sure we won't hide all sheets. we need at least one visible at all times.
+ for ( SCTAB i=0; i < nTabCount && nVis<nTabSelCount + 1; i++ )
+ if (rDoc.IsVisible(i))
+ ++nVis;
+ if ( nVis<=nTabSelCount || !rDoc.IsDocEditable() )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_TABLE_SHOW:
+ {
+ bool bHasHidden = false;
+ for ( SCTAB i=0; i < nTabCount && !bHasHidden; i++ )
+ if (!rDoc.IsVisible(i))
+ bHasHidden = true;
+ if ( !bHasHidden || rDoc.IsDocProtected() || nTabSelCount > 1 )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_DELETE_TABLE:
+ {
+ if ( rDoc.GetChangeTrack() )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ sal_uInt16 nVis = 0;
+ for ( SCTAB i=0; i < nTabCount && nVis<2; i++ )
+ if (rDoc.IsVisible(i))
+ ++nVis;
+ if ( rDoc.IsTabProtected(nTab)
+ || !rDoc.IsDocEditable()
+ || nVis < 2
+ || nTabSelCount == nTabCount)
+ rSet.DisableItem( nWhich );
+ }
+ }
+ break;
+
+ case FID_INS_TABLE:
+ case FID_INS_TABLE_EXT:
+ case FID_TAB_APPEND:
+ if ( !rDoc.IsDocEditable() ||
+ nTabCount > MAXTAB ||
+ ( nWhich == FID_INS_TABLE_EXT && pDocShell && pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_TAB_MOVE:
+ if ( !rDoc.IsDocEditable()
+ || rDoc.GetChangeTrack() != nullptr
+ || nTabCount > MAXTAB)
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_TAB_DUPLICATE:
+ if ( !rDoc.IsDocEditable()
+ || rDoc.GetChangeTrack() != nullptr
+ || nTabCount > MAXTAB)
+ rSet.DisableItem( nWhich );
+ break;
+
+ // FID_TAB_MENU_RENAME - "rename" from Menu
+ // FID_TAB_RENAME - "name"-property for Basic
+
+ case FID_TAB_MENU_RENAME:
+ if ( !rDoc.IsDocEditable() ||
+ rDoc.IsTabProtected(nTab) ||nTabSelCount > 1 ||
+ ( pDocShell && pDocShell->IsDocShared() ) )
+ rSet.DisableItem( nWhich );
+ break;
+
+ case FID_TAB_RENAME:
+ {
+ OUString aTabName;
+ rDoc.GetName( nTab, aTabName );
+
+ rSet.Put( SfxStringItem( nWhich, aTabName ));
+
+ }
+ break;
+
+ case FID_TAB_RTL:
+ {
+ if ( !SvtCTLOptions::IsCTLFontEnabled() )
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem( nWhich, rDoc.IsLayoutRTL( nTab ) ) );
+ }
+ break;
+
+ case FID_TAB_MENU_SET_TAB_BG_COLOR:
+ {
+ if ( !rDoc.IsDocEditable()
+ || ( pDocShell && pDocShell->IsDocShared() )
+ || rDoc.IsTabProtected(nTab) )
+ rSet.DisableItem( nWhich );
+ }
+ break;
+
+ case FID_TAB_SET_TAB_BG_COLOR:
+ {
+ Color aColor = rDoc.GetTabBgColor( nTab );
+ rSet.Put( SvxColorItem( aColor, nWhich ) );
+ }
+ break;
+
+ case FID_TAB_TOGGLE_GRID:
+ rSet.Put( SfxBoolItem(nWhich, rViewData.GetShowGrid()) );
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshg.cxx b/sc/source/ui/view/tabvwshg.cxx
new file mode 100644
index 0000000000..6b6820b9a3
--- /dev/null
+++ b/sc/source/ui/view/tabvwshg.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <tools/urlobj.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/svdouno.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <drawview.hxx>
+#include <globstr.hrc>
+#include <gridwin.hxx>
+#include <avmedia/mediawindow.hxx>
+
+using namespace com::sun::star;
+
+void ScTabViewShell::InsertURLButton( const OUString& rName, const OUString& rURL,
+ const OUString& rTarget,
+ const Point* pInsPos )
+{
+ // protected sheet ?
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SCTAB nTab = rViewData.GetTabNo();
+ if ( rDoc.IsTabProtected(nTab) )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ MakeDrawLayer();
+
+ ScTabView* pView = rViewData.GetView();
+ ScDrawView* pDrView = pView->GetScDrawView();
+ SdrModel& rModel = pDrView->GetModel();
+
+ rtl::Reference<SdrObject> pObj = SdrObjFactory::MakeNewObject(
+ rModel,
+ SdrInventor::FmForm,
+ SdrObjKind::FormButton);
+
+ SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( pObj.get() );
+ OSL_ENSURE( pUnoCtrl, "no SdrUnoObj");
+ if( !pUnoCtrl )
+ return;
+
+ uno::Reference<awt::XControlModel> xControlModel = pUnoCtrl->GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "UNO control without model" );
+ if( !xControlModel.is() )
+ return;
+
+ uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY );
+
+ xPropSet->setPropertyValue("Label", uno::Any(rName) );
+
+ OUString aTmp = INetURLObject::GetAbsURL( rDoc.GetDocumentShell()->GetMedium()->GetBaseURL(), rURL );
+ xPropSet->setPropertyValue("TargetURL", uno::Any(aTmp) );
+
+ if( !rTarget.isEmpty() )
+ {
+ xPropSet->setPropertyValue("TargetFrame", uno::Any(rTarget) );
+ }
+
+ xPropSet->setPropertyValue("ButtonType", uno::Any(form::FormButtonType_URL) );
+
+#if HAVE_FEATURE_AVMEDIA
+ if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) )
+ {
+ xPropSet->setPropertyValue("DispatchURLInternal", uno::Any(true) );
+ }
+#endif
+
+ Point aPos;
+ if (pInsPos)
+ aPos = *pInsPos;
+ else
+ aPos = GetInsertPos();
+
+ // Size as in 3.1:
+ Size aSize = GetActiveWin()->PixelToLogic(Size(140, 20));
+
+ if ( rDoc.IsNegativePage(nTab) )
+ aPos.AdjustX( -(aSize.Width()) );
+
+ pObj->SetLogicRect(tools::Rectangle(aPos, aSize));
+
+ // for the old VC-Button the position/size had to be set explicitly once more
+ // that seems not to be needed with UnoControls
+
+ // do not mark when Ole
+ pDrView->InsertObjectSafe( pObj.get(), *pDrView->GetSdrPageView() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabvwshh.cxx b/sc/source/ui/view/tabvwshh.cxx
new file mode 100644
index 0000000000..d1263590a8
--- /dev/null
+++ b/sc/source/ui/view/tabvwshh.cxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <basic/sberrors.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <basic/sbxcore.hxx>
+#include <svl/stritem.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <sc.hrc>
+#include <drwlayer.hxx>
+#include <retypepassdlg.hxx>
+#include <tabprotection.hxx>
+
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+
+using namespace com::sun::star;
+
+void ScTabViewShell::ExecuteObject( const SfxRequest& rReq )
+{
+ sal_uInt16 nSlotId = rReq.GetSlot();
+ const SfxItemSet* pReqArgs = rReq.GetArgs();
+
+ // Always activate/deactivate object in the visible View
+
+ ScTabViewShell* pVisibleSh = this;
+ if ( nSlotId == SID_OLE_SELECT || nSlotId == SID_OLE_ACTIVATE || nSlotId == SID_OLE_DEACTIVATE )
+ {
+ OSL_FAIL("old slot SID_OLE...");
+ }
+
+ switch (nSlotId)
+ {
+ case SID_OLE_SELECT:
+ case SID_OLE_ACTIVATE:
+ {
+ // In both cases, first select in the visible View
+
+ OUString aName;
+ SdrView* pDrView = GetScDrawView();
+ if (pDrView)
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ aName = ScDrawLayer::GetVisibleName( rMarkList.GetMark(0)->GetMarkedSdrObj() );
+ }
+ pVisibleSh->SelectObject( aName );
+
+ // activate
+
+ if ( nSlotId == SID_OLE_ACTIVATE )
+ pVisibleSh->DoVerb(css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY);
+ }
+ break;
+ case SID_OLE_DEACTIVATE:
+ pVisibleSh->DeactivateOle();
+ break;
+
+ case SID_OBJECT_LEFT:
+ case SID_OBJECT_TOP:
+ case SID_OBJECT_WIDTH:
+ case SID_OBJECT_HEIGHT:
+ {
+ bool bDone = false;
+ const SfxPoolItem* pItem;
+ if ( pReqArgs && pReqArgs->GetItemState( nSlotId, true, &pItem ) == SfxItemState::SET )
+ {
+ tools::Long nNewVal = static_cast<const SfxInt32Item*>(pItem)->GetValue();
+ if ( nNewVal < 0 )
+ nNewVal = 0;
+
+ //! convert from something into 1/100mm ??????
+
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ tools::Rectangle aRect = pObj->GetLogicRect();
+
+ if ( nSlotId == SID_OBJECT_LEFT )
+ pDrView->MoveMarkedObj( Size( nNewVal - aRect.Left(), 0 ) );
+ else if ( nSlotId == SID_OBJECT_TOP )
+ pDrView->MoveMarkedObj( Size( 0, nNewVal - aRect.Top() ) );
+ else if ( nSlotId == SID_OBJECT_WIDTH )
+ pDrView->ResizeMarkedObj( aRect.TopLeft(),
+ Fraction( nNewVal, aRect.GetWidth() ),
+ Fraction( 1, 1 ) );
+ else // if ( nSlotId == SID_OBJECT_HEIGHT )
+ pDrView->ResizeMarkedObj( aRect.TopLeft(),
+ Fraction( 1, 1 ),
+ Fraction( nNewVal, aRect.GetHeight() ) );
+ bDone = true;
+ }
+ }
+ }
+#if HAVE_FEATURE_SCRIPTING
+ if (!bDone)
+ SbxBase::SetError( ERRCODE_BASIC_BAD_PARAMETER ); // basic error
+#endif
+ }
+ break;
+
+ }
+}
+
+static uno::Reference < embed::XEmbeddedObject > lcl_GetSelectedObj( const SdrView* pDrView ) //! member of ScDrawView?
+{
+ uno::Reference < embed::XEmbeddedObject > xRet;
+ if (pDrView)
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
+ {
+ SdrOle2Obj* pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
+ xRet = pOle2Obj->GetObjRef();
+ }
+ }
+ }
+
+ return xRet;
+}
+
+void ScTabViewShell::GetObjectState( SfxItemSet& rSet )
+{
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch (nWhich)
+ {
+ case SID_ACTIVE_OBJ_NAME:
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xOLE = lcl_GetSelectedObj( GetScDrawView() );
+ if (xOLE.is())
+ {
+ aName = GetViewData().GetSfxDocShell()->GetEmbeddedObjectContainer().GetEmbeddedObjectName( xOLE );
+ }
+ rSet.Put( SfxStringItem( nWhich, aName ) );
+ }
+ break;
+ case SID_OBJECT_LEFT:
+ case SID_OBJECT_TOP:
+ case SID_OBJECT_WIDTH:
+ case SID_OBJECT_HEIGHT:
+ {
+ SdrView* pDrView = GetScDrawView();
+ if ( pDrView )
+ {
+ const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ tools::Rectangle aRect = pObj->GetLogicRect();
+
+ tools::Long nVal;
+ if ( nWhich == SID_OBJECT_LEFT )
+ nVal = aRect.Left();
+ else if ( nWhich == SID_OBJECT_TOP )
+ nVal = aRect.Top();
+ else if ( nWhich == SID_OBJECT_WIDTH )
+ nVal = aRect.GetWidth();
+ else // if ( nWhich == SID_OBJECT_HEIGHT )
+ nVal = aRect.GetHeight();
+
+ //! convert from 1/100mm to something else ??????
+
+ rSet.Put( SfxInt32Item( TypedWhichId<SfxInt32Item>(nWhich), nVal ) );
+ }
+ }
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+void ScTabViewShell::AddAccessibilityObject( SfxListener& rObject )
+{
+ if (!pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster.reset( new SfxBroadcaster );
+
+ rObject.StartListening( *pAccessibilityBroadcaster );
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.AddUnoObject(rObject);
+}
+
+void ScTabViewShell::RemoveAccessibilityObject( SfxListener& rObject )
+{
+ SolarMutexGuard g;
+
+ if (pAccessibilityBroadcaster)
+ {
+ rObject.EndListening( *pAccessibilityBroadcaster );
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.RemoveUnoObject(rObject);
+ }
+ else
+ {
+ OSL_FAIL("no accessibility broadcaster?");
+ }
+}
+
+void ScTabViewShell::BroadcastAccessibility( const SfxHint &rHint )
+{
+ if (pAccessibilityBroadcaster)
+ pAccessibilityBroadcaster->Broadcast( rHint );
+}
+
+bool ScTabViewShell::HasAccessibilityObjects() const
+{
+ return pAccessibilityBroadcaster && pAccessibilityBroadcaster->HasListeners();
+}
+
+bool ScTabViewShell::ExecuteRetypePassDlg(ScPasswordHash eDesiredHash)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ ScRetypePassDlg aDlg(GetFrameWeld());
+ aDlg.SetDataFromDocument(rDoc);
+ aDlg.SetDesiredHash(eDesiredHash);
+ if (aDlg.run() != RET_OK)
+ return false;
+
+ aDlg.WriteNewDataToDocument(rDoc);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewdata.cxx b/sc/source/ui/view/viewdata.cxx
new file mode 100644
index 0000000000..25e602669e
--- /dev/null
+++ b/sc/source/ui/view/viewdata.cxx
@@ -0,0 +1,4365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/string_view.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unolingu.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <vcl/svapp.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+#include <viewdata.hxx>
+#include <docoptio.hxx>
+#include <scmod.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <attrib.hxx>
+#include <tabview.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <patattr.hxx>
+#include <editutil.hxx>
+#include <scextopt.hxx>
+#include <miscuno.hxx>
+#include <unonames.hxx>
+#include <inputopt.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+#include <viewutil.hxx>
+#include <markdata.hxx>
+#include <ViewSettingsSequenceDefines.hxx>
+#include <gridwin.hxx>
+#include <transobj.hxx>
+#include <clipparam.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/document/NamedPropertyValues.hpp>
+
+using namespace com::sun::star;
+
+#define SC_GROWY_SMALL_EXTRA 100
+#define SC_GROWY_BIG_EXTRA 200
+
+constexpr OUString TAG_TABBARWIDTH = u"tw:"_ustr;
+
+namespace {
+
+void lcl_LOKRemoveWindow(ScTabViewShell* pTabViewShell, ScSplitPos eWhich)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ auto lRemoveWindows =
+ [pTabViewShell, eWhich] (ScTabViewShell* pOtherViewShell)
+ { pOtherViewShell->RemoveWindowFromForeignEditView(pTabViewShell, eWhich); };
+
+ SfxLokHelper::forEachOtherView(pTabViewShell, lRemoveWindows);
+ }
+}
+
+} // anonymous namespace
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+}
+
+const ScPositionHelper::index_type ScPositionHelper::null; // definition
+
+bool ScPositionHelper::Comp::operator() (const value_type& rValue1, const value_type& rValue2) const
+{
+ if (rValue1.first == null || rValue2.first == null)
+ {
+ return rValue1.second < rValue2.second;
+ }
+ else
+ {
+ return rValue1.first < rValue2.first;
+ }
+}
+
+ScPositionHelper::ScPositionHelper(const ScDocument *pDoc, bool bColumn)
+ : MAX_INDEX(bColumn ? (pDoc ? pDoc->MaxCol() : -1) : MAXTILEDROW)
+{
+ mData.insert(std::make_pair(-1, 0));
+}
+
+void ScPositionHelper::setDocument(const ScDocument& rDoc, bool bColumn)
+{
+ MAX_INDEX = bColumn ? rDoc.MaxCol() : MAXTILEDROW;
+}
+
+void ScPositionHelper::insert(index_type nIndex, tools::Long nPos)
+{
+ if (nIndex < 0) return;
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::insert: nIndex: "
+ << nIndex << ", nPos: " << nPos << ", size: " << mData.size());
+ value_type aValue = std::make_pair(nIndex, nPos);
+ mData.erase(aValue);
+ mData.insert(aValue);
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::insert: after insert: size: " << mData.size());
+}
+
+void ScPositionHelper::removeByIndex(index_type nIndex)
+{
+ if (nIndex < 0)
+ return;
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::remove: nIndex: " << nIndex
+ << ", size: " << mData.size());
+ auto it = mData.find(std::make_pair(nIndex, 0));
+ if (it == mData.end()) return;
+ mData.erase(it);
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::remove: after erase: size: " << mData.size());
+}
+
+void ScPositionHelper::invalidateByIndex(index_type nIndex)
+{
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::invalidate: nIndex: " << nIndex);
+ if (nIndex < 0)
+ {
+ mData.clear();
+ mData.insert(std::make_pair(-1, 0));
+ }
+ else
+ {
+ auto it = mData.lower_bound(std::make_pair(nIndex, 0));
+ mData.erase(it, mData.end());
+ }
+}
+
+void ScPositionHelper::invalidateByPosition(tools::Long nPos)
+{
+ SAL_INFO("sc.lok.poshelper", "ScPositionHelper::invalidate: nPos: " << nPos);
+ if (nPos <= 0)
+ {
+ mData.clear();
+ mData.insert(std::make_pair(-1, 0));
+ }
+ else
+ {
+ auto it = mData.lower_bound(std::make_pair(null, nPos));
+ mData.erase(it, mData.end());
+ }
+}
+
+const ScPositionHelper::value_type&
+ScPositionHelper::getNearestByIndex(index_type nIndex) const
+{
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::getNearest: nIndex: " << nIndex << ", size: " << mData.size());
+ auto posUB = mData.upper_bound(std::make_pair(nIndex, 0));
+ if (posUB == mData.begin())
+ {
+ return *posUB;
+ }
+
+ auto posLB = std::prev(posUB);
+ // coverity[copy_paste_error : FALSE] - posUB is correct
+ if (posUB == mData.end())
+ {
+ return *posLB;
+ }
+
+ tools::Long nDiffUB = posUB->first - nIndex;
+ tools::Long nDiffLB = posLB->first - nIndex;
+ if (nDiffUB < -nDiffLB)
+ {
+ return *posUB;
+ }
+ else
+ {
+ return *posLB;
+ }
+}
+
+const ScPositionHelper::value_type&
+ScPositionHelper::getNearestByPosition(tools::Long nPos) const
+{
+ SAL_INFO("sc.lok.poshelper",
+ "ScPositionHelper::getNearest: nPos: " << nPos << ", size: " << mData.size());
+ auto posUB = mData.upper_bound(std::make_pair(null, nPos));
+
+ if (posUB == mData.begin())
+ {
+ return *posUB;
+ }
+
+ auto posLB = std::prev(posUB);
+ // coverity[copy_paste_error : FALSE] - posUB is correct
+ if (posUB == mData.end())
+ {
+ return *posLB;
+ }
+
+ tools::Long nDiffUB = posUB->second - nPos;
+ tools::Long nDiffLB = posLB->second - nPos;
+
+ if (nDiffUB < -nDiffLB)
+ {
+ return *posUB;
+ }
+ else
+ {
+ return *posLB;
+ }
+}
+
+tools::Long ScPositionHelper::getPosition(index_type nIndex) const
+{
+ auto it = mData.find(std::make_pair(nIndex, 0));
+ if (it == mData.end()) return -1;
+ return it->second;
+}
+
+tools::Long ScPositionHelper::computePosition(index_type nIndex, const std::function<long (index_type)>& getSizePx)
+{
+ assert(MAX_INDEX > 0);
+ if (nIndex < 0) nIndex = 0;
+ if (nIndex > MAX_INDEX) nIndex = MAX_INDEX;
+
+ const auto& rNearest = getNearestByIndex(nIndex);
+ index_type nStartIndex = rNearest.first;
+ tools::Long nTotalPixels = rNearest.second;
+
+ if (nStartIndex < nIndex)
+ {
+ for (index_type nIdx = nStartIndex + 1; nIdx <= nIndex; ++nIdx)
+ {
+ nTotalPixels += getSizePx(nIdx);
+ }
+ }
+ else
+ {
+ for (index_type nIdx = nStartIndex; nIdx > nIndex; --nIdx)
+ {
+ nTotalPixels -= getSizePx(nIdx);
+ }
+ }
+ return nTotalPixels;
+}
+
+ScBoundsProvider::ScBoundsProvider(const ScViewData &rView, SCTAB nT, bool bColHeader)
+ : rDoc(rView.GetDocument())
+ , nTab(nT)
+ , bColumnHeader(bColHeader)
+ , MAX_INDEX(bColHeader ? rDoc.MaxCol() : MAXTILEDROW)
+ , mfPPTX(rView.GetPPTX())
+ , mfPPTY(rView.GetPPTY())
+ , nFirstIndex(-1)
+ , nSecondIndex(-1)
+ , nFirstPositionPx(-1)
+ , nSecondPositionPx(-1)
+{}
+
+void ScBoundsProvider::GetStartIndexAndPosition(SCCOL& nIndex, tools::Long& nPosition) const
+{
+ assert(bColumnHeader);
+ nIndex = nFirstIndex;
+ nPosition = nFirstPositionPx;
+}
+
+void ScBoundsProvider::GetEndIndexAndPosition(SCCOL& nIndex, tools::Long& nPosition) const
+{
+ assert(bColumnHeader);
+ nIndex = nSecondIndex;
+ nPosition = nSecondPositionPx;
+}
+
+void ScBoundsProvider::GetStartIndexAndPosition(SCROW& nIndex, tools::Long& nPosition) const
+{
+ assert(!bColumnHeader);
+ nIndex = nFirstIndex;
+ nPosition = nFirstPositionPx;
+}
+
+void ScBoundsProvider::GetEndIndexAndPosition(SCROW& nIndex, tools::Long& nPosition) const
+{
+ assert(!bColumnHeader);
+ nIndex = nSecondIndex;
+ nPosition = nSecondPositionPx;
+}
+
+tools::Long ScBoundsProvider::GetSize(index_type nIndex) const
+{
+ const sal_uInt16 nSize = bColumnHeader ? rDoc.GetColWidth(nIndex, nTab) : rDoc.GetRowHeight(nIndex, nTab);
+ return ScViewData::ToPixel(nSize, bColumnHeader ? mfPPTX : mfPPTY);
+}
+
+void ScBoundsProvider::GetIndexAndPos(index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition,
+ bool bTowards, tools::Long nDiff)
+{
+ if (nDiff > 0) // nBound < nNearestPosition
+ GeIndexBackwards(nNearestIndex, nNearestPosition, nBound,
+ nFoundIndex, nPosition, bTowards);
+ else
+ GetIndexTowards(nNearestIndex, nNearestPosition, nBound,
+ nFoundIndex, nPosition, bTowards);
+}
+
+void ScBoundsProvider::Compute(
+ value_type aFirstNearest, value_type aSecondNearest,
+ tools::Long nFirstBound, tools::Long nSecondBound)
+{
+ SAL_INFO("sc.lok.header", "BoundsProvider: nFirstBound: " << nFirstBound
+ << ", nSecondBound: " << nSecondBound);
+
+ tools::Long nFirstDiff = aFirstNearest.second - nFirstBound;
+ tools::Long nSecondDiff = aSecondNearest.second - nSecondBound;
+ SAL_INFO("sc.lok.header", "BoundsProvider: rTopNearest: index: " << aFirstNearest.first
+ << ", pos: " << aFirstNearest.second << ", diff: " << nFirstDiff);
+ SAL_INFO("sc.lok.header", "BoundsProvider: rBottomNearest: index: " << aSecondNearest.first
+ << ", pos: " << aSecondNearest.second << ", diff: " << nSecondDiff);
+
+ bool bReverse = (std::abs(nFirstDiff) >= std::abs(nSecondDiff));
+
+ if(bReverse)
+ {
+ std::swap(aFirstNearest, aSecondNearest);
+ std::swap(nFirstBound, nSecondBound);
+ std::swap(nFirstDiff, nSecondDiff);
+ }
+
+ index_type nNearestIndex = aFirstNearest.first;
+ tools::Long nNearestPosition = aFirstNearest.second;
+ SAL_INFO("sc.lok.header", "BoundsProvider: nearest to first bound: nNearestIndex: "
+ << nNearestIndex << ", nNearestPosition: " << nNearestPosition);
+
+ GetIndexAndPos(nNearestIndex, nNearestPosition, nFirstBound,
+ nFirstIndex, nFirstPositionPx, !bReverse, nFirstDiff);
+ SAL_INFO("sc.lok.header", "BoundsProvider: nFirstIndex: " << nFirstIndex
+ << ", nFirstPositionPx: " << nFirstPositionPx);
+
+ if (std::abs(nSecondDiff) < std::abs(nSecondBound - nFirstPositionPx))
+ {
+ nNearestIndex = aSecondNearest.first;
+ nNearestPosition = aSecondNearest.second;
+ }
+ else
+ {
+ nNearestPosition = nFirstPositionPx;
+ nNearestIndex = nFirstIndex;
+ nSecondDiff = !bReverse ? -1 : 1;
+ }
+ SAL_INFO("sc.lok.header", "BoundsProvider: nearest to second bound: nNearestIndex: "
+ << nNearestIndex << ", nNearestPosition: " << nNearestPosition
+ << ", diff: " << nSecondDiff);
+
+ GetIndexAndPos(nNearestIndex, nNearestPosition, nSecondBound,
+ nSecondIndex, nSecondPositionPx, bReverse, nSecondDiff);
+ SAL_INFO("sc.lok.header", "BoundsProvider: nSecondIndex: " << nSecondIndex
+ << ", nSecondPositionPx: " << nSecondPositionPx);
+
+ if (bReverse)
+ {
+ std::swap(nFirstIndex, nSecondIndex);
+ std::swap(nFirstPositionPx, nSecondPositionPx);
+ }
+}
+
+void ScBoundsProvider::EnlargeStartBy(tools::Long nOffset)
+{
+ const index_type nNewFirstIndex =
+ std::max(static_cast<index_type>(-1),
+ static_cast<index_type>(nFirstIndex - nOffset));
+ for (index_type nIndex = nFirstIndex; nIndex > nNewFirstIndex; --nIndex)
+ {
+ const tools::Long nSizePx = GetSize(nIndex);
+ nFirstPositionPx -= nSizePx;
+ }
+ nFirstIndex = nNewFirstIndex;
+ SAL_INFO("sc.lok.header", "BoundsProvider: added offset: nFirstIndex: " << nFirstIndex
+ << ", nFirstPositionPx: " << nFirstPositionPx);
+}
+
+void ScBoundsProvider::EnlargeEndBy(tools::Long nOffset)
+{
+ const index_type nNewSecondIndex = std::min(MAX_INDEX, static_cast<index_type>(nSecondIndex + nOffset));
+ for (index_type nIndex = nSecondIndex + 1; nIndex <= nNewSecondIndex; ++nIndex)
+ {
+ const tools::Long nSizePx = GetSize(nIndex);
+ nSecondPositionPx += nSizePx;
+ }
+ nSecondIndex = nNewSecondIndex;
+ SAL_INFO("sc.lok.header", "BoundsProvider: added offset: nSecondIndex: " << nSecondIndex
+ << ", nSecondPositionPx: " << nSecondPositionPx);
+}
+
+void ScBoundsProvider::GeIndexBackwards(
+ index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition, bool bTowards)
+{
+ nFoundIndex = -1;
+ for (index_type nIndex = nNearestIndex; nIndex >= 0; --nIndex)
+ {
+ if (nBound >= nNearestPosition)
+ {
+ nFoundIndex = nIndex; // last index whose nPosition is less than nBound
+ nPosition = nNearestPosition;
+ break;
+ }
+
+ const tools::Long nSizePx = GetSize(nIndex);
+ nNearestPosition -= nSizePx;
+ }
+ if (!bTowards && nFoundIndex != -1)
+ {
+ nFoundIndex += 1;
+ nPosition += GetSize(nFoundIndex);
+ }
+}
+
+void ScBoundsProvider::GetIndexTowards(
+ index_type nNearestIndex, tools::Long nNearestPosition,
+ tools::Long nBound, index_type& nFoundIndex, tools::Long& nPosition, bool bTowards)
+{
+ nFoundIndex = -2;
+ for (index_type nIndex = nNearestIndex + 1; nIndex <= MAX_INDEX; ++nIndex)
+ {
+ const tools::Long nSizePx = GetSize(nIndex);
+ nNearestPosition += nSizePx;
+
+ if (nNearestPosition > nBound)
+ {
+ nFoundIndex = nIndex; // first index whose nPosition is greater than nBound
+ nPosition = nNearestPosition;
+ break;
+ }
+ }
+ if (nFoundIndex == -2)
+ {
+ nFoundIndex = MAX_INDEX;
+ nPosition = nNearestPosition;
+ }
+ else if (bTowards)
+ {
+ nPosition -= GetSize(nFoundIndex);
+ nFoundIndex -= 1;
+ }
+}
+
+ScViewDataTable::ScViewDataTable(const ScDocument *pDoc) :
+ eZoomType( SvxZoomType::PERCENT ),
+ aZoomX( 1,1 ),
+ aZoomY( 1,1 ),
+ aPageZoomX( 3,5 ), // Page-Default: 60%
+ aPageZoomY( 3,5 ),
+ nHSplitPos( 0 ),
+ nVSplitPos( 0 ),
+ eHSplitMode( SC_SPLIT_NONE ),
+ eVSplitMode( SC_SPLIT_NONE ),
+ eWhichActive( SC_SPLIT_BOTTOMLEFT ),
+ nFixPosX( 0 ),
+ nFixPosY( 0 ),
+ nCurX( 0 ),
+ nCurY( 0 ),
+ nOldCurX( 0 ),
+ nOldCurY( 0 ),
+ aWidthHelper(pDoc, true),
+ aHeightHelper(pDoc, false),
+ nMaxTiledCol( 20 ),
+ nMaxTiledRow( 50 ),
+ bShowGrid( true ),
+ mbOldCursorValid( false )
+{
+ nPosX[0]=nPosX[1]=0;
+ nPosY[0]=nPosY[1]=0;
+ nTPosX[0]=nTPosX[1]=0;
+ nTPosY[0]=nTPosY[1]=0;
+ nMPosX[0]=nMPosX[1]=0;
+ nMPosY[0]=nMPosY[1]=0;
+ nPixPosX[0]=nPixPosX[1]=0;
+ nPixPosY[0]=nPixPosY[1]=0;
+}
+
+void ScViewDataTable::InitData(const ScDocument& rDoc)
+{
+ aWidthHelper.setDocument(rDoc, true);
+ aHeightHelper.setDocument(rDoc, false);
+}
+
+void ScViewDataTable::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rSettings, const ScViewData& rViewData, SCTAB nTab) const
+{
+ rSettings.realloc(SC_TABLE_VIEWSETTINGS_COUNT);
+ beans::PropertyValue* pSettings = rSettings.getArray();
+
+ ScSplitMode eExHSplitMode = eHSplitMode;
+ ScSplitMode eExVSplitMode = eVSplitMode;
+ SCCOL nExFixPosX = nFixPosX;
+ SCROW nExFixPosY = nFixPosY;
+ tools::Long nExHSplitPos = nHSplitPos;
+ tools::Long nExVSplitPos = nVSplitPos;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ rViewData.OverrideWithLOKFreeze(eExHSplitMode, eExVSplitMode,
+ nExFixPosX, nExFixPosY,
+ nExHSplitPos, nExVSplitPos, nTab);
+ }
+
+ pSettings[SC_CURSOR_X].Name = SC_CURSORPOSITIONX;
+ pSettings[SC_CURSOR_X].Value <<= sal_Int32(nCurX);
+ pSettings[SC_CURSOR_Y].Name = SC_CURSORPOSITIONY;
+ pSettings[SC_CURSOR_Y].Value <<= sal_Int32(nCurY);
+
+ // Write freezepan data only when freeze pans are set
+ if(nExFixPosX != 0 || nExFixPosY != 0 || nExHSplitPos != 0 || nExVSplitPos != 0)
+ {
+ pSettings[SC_HORIZONTAL_SPLIT_MODE].Name = SC_HORIZONTALSPLITMODE;
+ pSettings[SC_HORIZONTAL_SPLIT_MODE].Value <<= sal_Int16(eExHSplitMode);
+ pSettings[SC_VERTICAL_SPLIT_MODE].Name = SC_VERTICALSPLITMODE;
+ pSettings[SC_VERTICAL_SPLIT_MODE].Value <<= sal_Int16(eExVSplitMode);
+ pSettings[SC_HORIZONTAL_SPLIT_POSITION].Name = SC_HORIZONTALSPLITPOSITION;
+ if (eExHSplitMode == SC_SPLIT_FIX)
+ pSettings[SC_HORIZONTAL_SPLIT_POSITION].Value <<= sal_Int32(nExFixPosX);
+ else
+ pSettings[SC_HORIZONTAL_SPLIT_POSITION].Value <<= sal_Int32(nExHSplitPos);
+ pSettings[SC_VERTICAL_SPLIT_POSITION].Name = SC_VERTICALSPLITPOSITION;
+ if (eExVSplitMode == SC_SPLIT_FIX)
+ pSettings[SC_VERTICAL_SPLIT_POSITION].Value <<= sal_Int32(nExFixPosY);
+ else
+ pSettings[SC_VERTICAL_SPLIT_POSITION].Value <<= sal_Int32(nExVSplitPos);
+ }
+
+ // Prevent writing odd settings that would make crash versions that
+ // don't apply SanitizeWhichActive() when reading the settings.
+ // See tdf#117093
+ const ScSplitPos eActiveSplitRange = SanitizeWhichActive();
+ // And point out to give us a chance to inspect weird things (if anyone
+ // remembers what s/he did).
+ assert(eWhichActive == eActiveSplitRange);
+ pSettings[SC_ACTIVE_SPLIT_RANGE].Name = SC_ACTIVESPLITRANGE;
+ pSettings[SC_ACTIVE_SPLIT_RANGE].Value <<= sal_Int16(eActiveSplitRange);
+ pSettings[SC_POSITION_LEFT].Name = SC_POSITIONLEFT;
+ pSettings[SC_POSITION_LEFT].Value <<= sal_Int32(nPosX[SC_SPLIT_LEFT]);
+ pSettings[SC_POSITION_RIGHT].Name = SC_POSITIONRIGHT;
+ pSettings[SC_POSITION_RIGHT].Value <<= sal_Int32(nPosX[SC_SPLIT_RIGHT]);
+ pSettings[SC_POSITION_TOP].Name = SC_POSITIONTOP;
+ pSettings[SC_POSITION_TOP].Value <<= sal_Int32(nPosY[SC_SPLIT_TOP]);
+ pSettings[SC_POSITION_BOTTOM].Name = SC_POSITIONBOTTOM;
+ pSettings[SC_POSITION_BOTTOM].Value <<= sal_Int32(nPosY[SC_SPLIT_BOTTOM]);
+
+ sal_Int32 nZoomValue = tools::Long(aZoomY * 100);
+ sal_Int32 nPageZoomValue = tools::Long(aPageZoomY * 100);
+ pSettings[SC_TABLE_ZOOM_TYPE].Name = SC_ZOOMTYPE;
+ pSettings[SC_TABLE_ZOOM_TYPE].Value <<= sal_Int16(eZoomType);
+ pSettings[SC_TABLE_ZOOM_VALUE].Name = SC_ZOOMVALUE;
+ pSettings[SC_TABLE_ZOOM_VALUE].Value <<= nZoomValue;
+ pSettings[SC_TABLE_PAGE_VIEW_ZOOM_VALUE].Name = SC_PAGEVIEWZOOMVALUE;
+ pSettings[SC_TABLE_PAGE_VIEW_ZOOM_VALUE].Value <<= nPageZoomValue;
+
+ pSettings[SC_TABLE_SHOWGRID].Name = SC_UNO_SHOWGRID;
+ pSettings[SC_TABLE_SHOWGRID].Value <<= bShowGrid;
+
+ // Common SdrModel processing
+ rViewData.GetDocument().GetDrawLayer()->WriteUserDataSequence(rSettings);
+}
+
+void ScViewDataTable::ReadUserDataSequence(const uno::Sequence <beans::PropertyValue>& aSettings, ScViewData& rViewData, SCTAB nTab, bool& rHasZoom )
+{
+ rHasZoom = false;
+
+ sal_Int32 nTemp32(0);
+ sal_Int16 nTemp16(0);
+ sal_Int32 nTempPosV(0);
+ sal_Int32 nTempPosH(0);
+ sal_Int32 nTempPosVTw(0);
+ sal_Int32 nTempPosHTw(0);
+ bool bHasVSplitInTwips = false;
+ bool bHasHSplitInTwips = false;
+ for (const auto& rSetting : aSettings)
+ {
+ OUString sName(rSetting.Name);
+ if (sName == SC_CURSORPOSITIONX)
+ {
+ rSetting.Value >>= nTemp32;
+ nCurX = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>(nTemp32));
+ }
+ else if (sName == SC_CURSORPOSITIONY)
+ {
+ rSetting.Value >>= nTemp32;
+ nCurY = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>(nTemp32));
+ }
+ else if (sName == SC_HORIZONTALSPLITMODE)
+ {
+ if ((rSetting.Value >>= nTemp16) && nTemp16 <= ScSplitMode::SC_SPLIT_MODE_MAX_ENUM)
+ eHSplitMode = static_cast<ScSplitMode>(nTemp16);
+ }
+ else if (sName == SC_VERTICALSPLITMODE)
+ {
+ if ((rSetting.Value >>= nTemp16) && nTemp16 <= ScSplitMode::SC_SPLIT_MODE_MAX_ENUM)
+ eVSplitMode = static_cast<ScSplitMode>(nTemp16);
+ }
+ else if (sName == SC_HORIZONTALSPLITPOSITION)
+ {
+ rSetting.Value >>= nTempPosH;
+ bHasHSplitInTwips = false;
+ }
+ else if (sName == SC_VERTICALSPLITPOSITION)
+ {
+ rSetting.Value >>= nTempPosV;
+ bHasVSplitInTwips = false;
+ }
+ else if (sName == SC_HORIZONTALSPLITPOSITION_TWIPS)
+ {
+ rSetting.Value >>= nTempPosHTw;
+ bHasHSplitInTwips = true;
+ }
+ else if (sName == SC_VERTICALSPLITPOSITION_TWIPS)
+ {
+ rSetting.Value >>= nTempPosVTw;
+ bHasVSplitInTwips = true;
+ }
+ else if (sName == SC_ACTIVESPLITRANGE)
+ {
+ if ((rSetting.Value >>= nTemp16) && nTemp16 <= ScSplitPos::SC_SPLIT_POS_MAX_ENUM)
+ eWhichActive = static_cast<ScSplitPos>(nTemp16);
+ }
+ else if (sName == SC_POSITIONLEFT)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosX[SC_SPLIT_LEFT] = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>(nTemp32));
+ }
+ else if (sName == SC_POSITIONRIGHT)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosX[SC_SPLIT_RIGHT] = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>(nTemp32));
+ }
+ else if (sName == SC_POSITIONTOP)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosY[SC_SPLIT_TOP] = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>(nTemp32));
+ }
+ else if (sName == SC_POSITIONBOTTOM)
+ {
+ rSetting.Value >>= nTemp32;
+ nPosY[SC_SPLIT_BOTTOM] = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>(nTemp32));
+ }
+ else if (sName == SC_ZOOMTYPE)
+ {
+ rSetting.Value >>= nTemp16;
+ eZoomType = SvxZoomType(nTemp16);
+ rHasZoom = true; // set if there is any zoom information
+ }
+ else if (sName == SC_ZOOMVALUE)
+ {
+ rSetting.Value >>= nTemp32;
+ Fraction aZoom(nTemp32, 100);
+ aZoomX = aZoomY = aZoom;
+ rHasZoom = true;
+ }
+ else if (sName == SC_PAGEVIEWZOOMVALUE)
+ {
+ rSetting.Value >>= nTemp32;
+ Fraction aZoom(nTemp32, 100);
+ aPageZoomX = aPageZoomY = aZoom;
+ rHasZoom = true;
+ }
+ else if (sName == SC_UNO_SHOWGRID)
+ {
+ rSetting.Value >>= bShowGrid;
+ }
+ else if (sName == SC_TABLESELECTED)
+ {
+ bool bSelected = false;
+ rSetting.Value >>= bSelected;
+ rViewData.GetMarkData().SelectTable( nTab, bSelected );
+ }
+ else if (sName == SC_UNONAME_TABCOLOR)
+ {
+ // There are documents out there that have their tab color defined as a view setting.
+ Color aColor = COL_AUTO;
+ rSetting.Value >>= aColor;
+ if (aColor != COL_AUTO)
+ {
+ ScDocument& rDoc = rViewData.GetDocument();
+ rDoc.SetTabBgColor(nTab, aColor);
+ }
+ }
+ // Fallback to common SdrModel processing
+ else rViewData.GetDocument().GetDrawLayer()->ReadUserDataSequenceValue(&rSetting);
+ }
+
+ if (eHSplitMode == SC_SPLIT_FIX)
+ nFixPosX = rViewData.GetDocument().SanitizeCol( static_cast<SCCOL>( bHasHSplitInTwips ? nTempPosHTw : nTempPosH ));
+ else
+ nHSplitPos = bHasHSplitInTwips ? static_cast< tools::Long >( nTempPosHTw * rViewData.GetPPTX() ) : nTempPosH;
+
+ if (eVSplitMode == SC_SPLIT_FIX)
+ nFixPosY = rViewData.GetDocument().SanitizeRow( static_cast<SCROW>( bHasVSplitInTwips ? nTempPosVTw : nTempPosV ));
+ else
+ nVSplitPos = bHasVSplitInTwips ? static_cast< tools::Long >( nTempPosVTw * rViewData.GetPPTY() ) : nTempPosV;
+
+ eWhichActive = SanitizeWhichActive();
+}
+
+ScSplitPos ScViewDataTable::SanitizeWhichActive() const
+{
+ if ((WhichH(eWhichActive) == SC_SPLIT_RIGHT && eHSplitMode == SC_SPLIT_NONE) ||
+ (WhichV(eWhichActive) == SC_SPLIT_TOP && eVSplitMode == SC_SPLIT_NONE))
+ {
+ SAL_WARN("sc.ui","ScViewDataTable::SanitizeWhichActive - bad eWhichActive " << eWhichActive);
+ // The default always initialized grid window is SC_SPLIT_BOTTOMLEFT.
+ return SC_SPLIT_BOTTOMLEFT;
+ }
+ return eWhichActive;
+}
+
+ScViewData::ScViewData(ScDocShell& rDocSh, ScTabViewShell* pViewSh)
+ : ScViewData(nullptr, &rDocSh, pViewSh)
+{
+}
+
+ScViewData::ScViewData(ScDocument& rDoc)
+ : ScViewData(&rDoc, nullptr, nullptr)
+{
+}
+
+static ScViewOptions DefaultOptions()
+{
+ ScViewOptions aOptions;
+ aOptions.SetOption(VOPT_GRID, true);
+ aOptions.SetOption(VOPT_SYNTAX, false);
+ aOptions.SetOption(VOPT_HEADER, true);
+ aOptions.SetOption(VOPT_TABCONTROLS, true);
+ aOptions.SetOption(VOPT_VSCROLL, true);
+ aOptions.SetOption(VOPT_HSCROLL, true);
+ aOptions.SetOption(VOPT_OUTLINER, true);
+ return aOptions;
+}
+
+// Either pDoc or pDocSh must be valid
+ScViewData::ScViewData(ScDocument* pDoc, ScDocShell* pDocSh, ScTabViewShell* pViewSh) :
+ nPPTX(0.0),
+ nPPTY(0.0),
+ maMarkData (pDocSh ? pDocSh->GetDocument().GetSheetLimits() : pDoc->GetSheetLimits()),
+ maHighlightData (pDocSh ? pDocSh->GetDocument().GetSheetLimits() : pDoc->GetSheetLimits()),
+ pDocShell ( pDocSh ),
+ mrDoc (pDocSh ? pDocSh->GetDocument() : *pDoc),
+ pView ( pViewSh ),
+ maOptions (pDocSh ? pDocSh->GetDocument().GetViewOptions() : DefaultOptions()),
+ pSpellingView ( nullptr ),
+ aLogicMode ( MapUnit::Map100thMM ),
+ eDefZoomType( SvxZoomType::PERCENT ),
+ aDefZoomX ( 1,1 ),
+ aDefZoomY ( 1,1 ),
+ aDefPageZoomX( 3,5 ),
+ aDefPageZoomY( 3,5 ),
+ eRefType ( SC_REFTYPE_NONE ),
+ nTabNo ( 0 ),
+ nRefTabNo ( 0 ),
+ nRefStartX(0),
+ nRefStartY(0),
+ nRefStartZ(0),
+ nRefEndX(0),
+ nRefEndY(0),
+ nRefEndZ(0),
+ nFillStartX(0),
+ nFillStartY(0),
+ nFillEndX(0),
+ nFillEndY(0),
+ nPasteFlags ( ScPasteFlags::NONE ),
+ eEditActivePart( SC_SPLIT_BOTTOMLEFT ),
+ nFillMode ( ScFillMode::NONE ),
+ eEditAdjust ( SvxAdjust::Left ),
+ bActive ( true ), // how to initialize?
+ bIsRefMode ( false ),
+ bDelMarkValid( false ),
+ bPagebreak ( false ),
+ bSelCtrlMouseClick( false ),
+ bMoveArea ( false ),
+ bGrowing (false),
+ nFormulaBarLines(1),
+ m_nLOKPageUpDownOffset( 0 )
+{
+ assert(bool(pDoc) != bool(pDocSh)); // either one or the other, not both
+ maMarkData.SelectOneTable(0); // Sync with nTabNo
+
+ aScrSize = Size( o3tl::convert(STD_COL_WIDTH * OLE_STD_CELLS_X, o3tl::Length::twip, o3tl::Length::px),
+ o3tl::convert(mrDoc.GetSheetOptimalMinRowHeight(nTabNo) * OLE_STD_CELLS_Y,
+ o3tl::Length::twip, o3tl::Length::px));
+ maTabData.emplace_back( new ScViewDataTable(nullptr) );
+ pThisTab = maTabData[nTabNo].get();
+
+ nEditEndCol = nEditStartCol = nEditCol = 0;
+ nEditEndRow = nEditRow = 0;
+ nTabStartCol = SC_TABSTART_NONE;
+
+ // don't show hidden tables
+ if (!mrDoc.IsVisible(nTabNo))
+ {
+ while (!mrDoc.IsVisible(nTabNo) && mrDoc.HasTable(nTabNo + 1))
+ {
+ ++nTabNo;
+ maTabData.emplace_back(nullptr);
+ }
+ maTabData[nTabNo].reset( new ScViewDataTable(nullptr) );
+ pThisTab = maTabData[nTabNo].get();
+ }
+
+ SCTAB nTableCount = mrDoc.GetTableCount();
+ EnsureTabDataSize(nTableCount);
+
+ for (auto& xTabData : maTabData)
+ {
+ if (xTabData)
+ xTabData->InitData(mrDoc);
+ }
+
+ CalcPPT();
+}
+
+ScViewData::~ScViewData() COVERITY_NOEXCEPT_FALSE
+{
+ KillEditView();
+}
+
+ScDBFunc* ScViewData::GetView() const { return pView; }
+
+void ScViewData::UpdateCurrentTab()
+{
+ assert(0 <= nTabNo && o3tl::make_unsigned(nTabNo) < maTabData.size());
+ pThisTab = maTabData[nTabNo].get();
+ while (!pThisTab)
+ {
+ if (nTabNo > 0)
+ pThisTab = maTabData[--nTabNo].get();
+ else
+ {
+ maTabData[0].reset(new ScViewDataTable(&mrDoc));
+ pThisTab = maTabData[0].get();
+ }
+ }
+}
+
+void ScViewData::InsertTab( SCTAB nTab )
+{
+ if( nTab >= static_cast<SCTAB>(maTabData.size()))
+ maTabData.resize(nTab+1);
+ else
+ maTabData.insert( maTabData.begin() + nTab, nullptr );
+ CreateTabData( nTab );
+
+ UpdateCurrentTab();
+ maMarkData.InsertTab(nTab);
+
+ collectUIInformation({{}}, "InsertTab");
+}
+
+void ScViewData::InsertTabs( SCTAB nTab, SCTAB nNewSheets )
+{
+ if (nTab >= static_cast<SCTAB>(maTabData.size()))
+ maTabData.resize(nTab+nNewSheets);
+ else
+ {
+ // insert nNewSheets new tables at position nTab
+ auto prevSize = maTabData.size();
+ maTabData.resize(prevSize + nNewSheets);
+ std::move_backward(maTabData.begin() + nTab, maTabData.begin() + prevSize, maTabData.end());
+ }
+ for (SCTAB i = nTab; i < nTab + nNewSheets; ++i)
+ {
+ CreateTabData( i );
+ maMarkData.InsertTab(i);
+ }
+ UpdateCurrentTab();
+}
+
+void ScViewData::DeleteTab( SCTAB nTab )
+{
+ assert(nTab < static_cast<SCTAB>(maTabData.size()));
+ maTabData.erase(maTabData.begin() + nTab);
+
+ if (o3tl::make_unsigned(nTabNo) >= maTabData.size())
+ {
+ EnsureTabDataSize(1);
+ nTabNo = maTabData.size() - 1;
+ }
+ UpdateCurrentTab();
+ maMarkData.DeleteTab(nTab);
+}
+
+void ScViewData::DeleteTabs( SCTAB nTab, SCTAB nSheets )
+{
+ for (SCTAB i = 0; i < nSheets; ++i)
+ {
+ maMarkData.DeleteTab(nTab + i);
+ }
+ maTabData.erase(maTabData.begin() + nTab, maTabData.begin()+ nTab+nSheets);
+ if (o3tl::make_unsigned(nTabNo) >= maTabData.size())
+ {
+ EnsureTabDataSize(1);
+ nTabNo = maTabData.size() - 1;
+ }
+ UpdateCurrentTab();
+}
+
+void ScViewData::CopyTab( SCTAB nSrcTab, SCTAB nDestTab )
+{
+ if (nDestTab==SC_TAB_APPEND)
+ nDestTab = mrDoc.GetTableCount() - 1; // something had to have been copied
+
+ if (nDestTab > MAXTAB)
+ {
+ OSL_FAIL("too many sheets");
+ return;
+ }
+
+ if (nSrcTab >= static_cast<SCTAB>(maTabData.size()))
+ OSL_FAIL("pTabData out of bounds, FIX IT");
+
+ EnsureTabDataSize(nDestTab + 1);
+
+ if ( maTabData[nSrcTab] )
+ maTabData.emplace(maTabData.begin() + nDestTab, new ScViewDataTable( *maTabData[nSrcTab] ));
+ else
+ maTabData.insert(maTabData.begin() + nDestTab, nullptr);
+
+ UpdateCurrentTab();
+ maMarkData.InsertTab(nDestTab);
+}
+
+void ScViewData::MoveTab( SCTAB nSrcTab, SCTAB nDestTab )
+{
+ if (nDestTab==SC_TAB_APPEND)
+ nDestTab = mrDoc.GetTableCount() - 1;
+ std::unique_ptr<ScViewDataTable> pTab;
+ if (nSrcTab < static_cast<SCTAB>(maTabData.size()))
+ {
+ pTab = std::move(maTabData[nSrcTab]);
+ maTabData.erase( maTabData.begin() + nSrcTab );
+ }
+
+ if (nDestTab < static_cast<SCTAB>(maTabData.size()))
+ maTabData.insert( maTabData.begin() + nDestTab, std::move(pTab) );
+ else
+ {
+ EnsureTabDataSize(nDestTab + 1);
+ maTabData[nDestTab] = std::move(pTab);
+ }
+
+ UpdateCurrentTab();
+ maMarkData.DeleteTab(nSrcTab);
+ maMarkData.InsertTab(nDestTab); // adapted if needed
+}
+
+void ScViewData::CreateTabData( std::vector< SCTAB >& rvTabs )
+{
+ for ( const auto& rTab : rvTabs )
+ CreateTabData(rTab);
+}
+
+void ScViewData::SetZoomType( SvxZoomType eNew, std::vector< SCTAB >& tabs )
+{
+ bool bAll = tabs.empty();
+
+ if ( !bAll ) // create associated table data
+ CreateTabData( tabs );
+
+ if ( bAll )
+ {
+ for ( auto & i: maTabData )
+ {
+ if ( i )
+ i->eZoomType = eNew;
+ }
+ eDefZoomType = eNew;
+ }
+ else
+ {
+ for ( const SCTAB& i : tabs )
+ {
+ if ( i < static_cast<SCTAB>(maTabData.size()) && maTabData[i] )
+ maTabData[i]->eZoomType = eNew;
+ }
+ }
+}
+
+void ScViewData::SetZoomType( SvxZoomType eNew, bool bAll )
+{
+ std::vector< SCTAB > vTabs; // Empty for all tabs
+ if ( !bAll ) // get selected tabs
+ {
+ ScMarkData::const_iterator itr = maMarkData.begin(), itrEnd = maMarkData.end();
+ vTabs.insert(vTabs.begin(), itr, itrEnd);
+ }
+ SetZoomType( eNew, vTabs );
+}
+
+void ScViewData::SetZoom( const Fraction& rNewX, const Fraction& rNewY, std::vector< SCTAB >& tabs )
+{
+ bool bAll = tabs.empty();
+ if ( !bAll ) // create associated table data
+ CreateTabData( tabs );
+
+ // sanity check - we shouldn't need something this low / big
+ SAL_WARN_IF(rNewX < Fraction(1, 100) || rNewX > Fraction(100, 1), "sc.viewdata",
+ "fraction rNewX not sensible: " << static_cast<double>(rNewX));
+ SAL_WARN_IF(rNewY < Fraction(1, 100) || rNewY > Fraction(100, 1), "sc.viewdata",
+ "fraction rNewY not sensible: " << static_cast<double>(rNewY));
+
+ if ( bAll )
+ {
+ for ( auto & i: maTabData )
+ {
+ if ( i )
+ {
+ if ( bPagebreak )
+ {
+ i->aPageZoomX = rNewX;
+ i->aPageZoomY = rNewY;
+ }
+ else
+ {
+ i->aZoomX = rNewX;
+ i->aZoomY = rNewY;
+ }
+ }
+ }
+ if ( bPagebreak )
+ {
+ aDefPageZoomX = rNewX;
+ aDefPageZoomY = rNewY;
+ }
+ else
+ {
+ aDefZoomX = rNewX;
+ aDefZoomY = rNewY;
+ }
+ }
+ else
+ {
+ for ( const SCTAB& i : tabs )
+ {
+ if ( i < static_cast<SCTAB>(maTabData.size()) && maTabData[i] )
+ {
+ if ( bPagebreak )
+ {
+ maTabData[i]->aPageZoomX = rNewX;
+ maTabData[i]->aPageZoomY = rNewY;
+ }
+ else
+ {
+ maTabData[i]->aZoomX = rNewX;
+ maTabData[i]->aZoomY = rNewY;
+ }
+ }
+ }
+ }
+ RefreshZoom();
+}
+
+void ScViewData::SetZoom( const Fraction& rNewX, const Fraction& rNewY, bool bAll )
+{
+ std::vector< SCTAB > vTabs;
+ if ( !bAll ) // get selected tabs
+ {
+ ScMarkData::const_iterator itr = maMarkData.begin(), itrEnd = maMarkData.end();
+ vTabs.insert(vTabs.begin(), itr, itrEnd);
+ }
+ SetZoom( rNewX, rNewY, vTabs );
+}
+
+void ScViewData::SetShowGrid( bool bShow )
+{
+ CreateSelectedTabData();
+ maTabData[nTabNo]->bShowGrid = bShow;
+}
+
+void ScViewData::RefreshZoom()
+{
+ // recalculate zoom-dependent values (only for current sheet)
+
+ CalcPPT();
+ RecalcPixPos();
+ aScenButSize = Size(0,0);
+ aLogicMode.SetScaleX( GetZoomX() );
+ aLogicMode.SetScaleY( GetZoomY() );
+}
+
+void ScViewData::SetPagebreakMode( bool bSet )
+{
+ bPagebreak = bSet;
+
+ RefreshZoom();
+}
+
+ScMarkType ScViewData::GetSimpleArea( ScRange & rRange, ScMarkData & rNewMark ) const
+{
+ ScMarkType eMarkType = SC_MARK_NONE;
+
+ if ( rNewMark.IsMarked() || rNewMark.IsMultiMarked() )
+ {
+ if ( rNewMark.IsMultiMarked() )
+ rNewMark.MarkToSimple();
+
+ if ( rNewMark.IsMarked() && !rNewMark.IsMultiMarked() )
+ {
+ rRange = rNewMark.GetMarkArea();
+ if (ScViewUtil::HasFiltered(rRange, GetDocument()))
+ eMarkType = SC_MARK_SIMPLE_FILTERED;
+ else
+ eMarkType = SC_MARK_SIMPLE;
+ }
+ else
+ eMarkType = SC_MARK_MULTI;
+ }
+ if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
+ {
+ if (eMarkType == SC_MARK_NONE)
+ eMarkType = SC_MARK_SIMPLE;
+ const ScPatternAttr* pMarkPattern = mrDoc.GetPattern(GetCurX(), GetCurY(), GetTabNo());
+ if (pMarkPattern && pMarkPattern->GetItemSet().GetItemState(ATTR_MERGE, false) == SfxItemState::SET)
+ {
+ SCROW nRow = pMarkPattern->GetItem(ATTR_MERGE).GetRowMerge();
+ SCCOL nCol = pMarkPattern->GetItem(ATTR_MERGE).GetColMerge();
+ if ( nRow < 1 || nCol < 1 )
+ {
+ // This kind of cells do exist. Not sure if that is intended or a bug.
+ rRange = ScRange(GetCurX(), GetCurY(), GetTabNo());
+ }
+ else
+ {
+ rRange = ScRange(GetCurX(), GetCurY(), GetTabNo(),
+ GetCurX() + nCol - 1, GetCurY() + nRow - 1, GetTabNo());
+ if ( ScViewUtil::HasFiltered(rRange, GetDocument()) )
+ eMarkType = SC_MARK_SIMPLE_FILTERED;
+ }
+ }
+ else
+ rRange = ScRange(GetCurX(), GetCurY(), GetTabNo());
+ }
+ return eMarkType;
+}
+
+ScMarkType ScViewData::GetSimpleArea( SCCOL& rStartCol, SCROW& rStartRow, SCTAB& rStartTab,
+ SCCOL& rEndCol, SCROW& rEndRow, SCTAB& rEndTab ) const
+{
+ // parameter bMergeMark is no longer needed: The view's selection is never modified
+ // (a local copy is used), and a multi selection that adds to a single range can always
+ // be treated like a single selection (GetSimpleArea isn't used in selection
+ // handling itself)
+
+ ScRange aRange;
+ ScMarkData aNewMark(maMarkData); // use a local copy for MarkToSimple
+ ScMarkType eMarkType = GetSimpleArea( aRange, aNewMark);
+ aRange.GetVars( rStartCol, rStartRow, rStartTab, rEndCol, rEndRow, rEndTab);
+ return eMarkType;
+}
+
+ScMarkType ScViewData::GetSimpleArea( ScRange& rRange ) const
+{
+ // parameter bMergeMark is no longer needed, see above
+
+ ScMarkData aNewMark(maMarkData); // use a local copy for MarkToSimple
+ return GetSimpleArea( rRange, aNewMark);
+}
+
+void ScViewData::GetMultiArea( ScRangeListRef& rRange ) const
+{
+ // parameter bMergeMark is no longer needed, see GetSimpleArea
+
+ ScMarkData aNewMark(maMarkData); // use a local copy for MarkToSimple
+
+ bool bMulti = aNewMark.IsMultiMarked();
+ if (bMulti)
+ {
+ aNewMark.MarkToSimple();
+ bMulti = aNewMark.IsMultiMarked();
+ }
+ if (bMulti)
+ {
+ rRange = new ScRangeList;
+ aNewMark.FillRangeListWithMarks( rRange.get(), false );
+ }
+ else
+ {
+ ScRange aSimple;
+ GetSimpleArea(aSimple);
+ rRange = new ScRangeList(aSimple);
+ }
+}
+
+bool ScViewData::SimpleColMarked()
+{
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ if (GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ if (nStartRow == 0 && nEndRow == mrDoc.MaxRow())
+ return true;
+
+ return false;
+}
+
+bool ScViewData::SimpleRowMarked()
+{
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ if (GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ if (nStartCol == 0 && nEndCol == mrDoc.MaxCol())
+ return true;
+
+ return false;
+}
+
+bool ScViewData::IsMultiMarked() const
+{
+ // Test for "real" multi selection, calling MarkToSimple on a local copy,
+ // and taking filtered in simple area marks into account.
+
+ ScRange aDummy;
+ ScMarkType eType = GetSimpleArea(aDummy);
+ return (eType & SC_MARK_SIMPLE) != SC_MARK_SIMPLE;
+}
+
+bool ScViewData::SelectionForbidsPaste( ScDocument* pClipDoc )
+{
+ if (!pClipDoc)
+ {
+ // Same as checkDestRanges() in sc/source/ui/view/cellsh.cxx but
+ // different return details.
+
+ vcl::Window* pWin = GetActiveWin();
+ if (!pWin)
+ // No window doesn't mean paste would be forbidden.
+ return false;
+
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if (!pOwnClip)
+ // Foreign content does not get repeatedly replicated.
+ return false;
+
+ pClipDoc = pOwnClip->GetDocument();
+ if (!pClipDoc)
+ // No clipdoc doesn't mean paste would be forbidden.
+ return false;
+ }
+
+ const ScRange aSrcRange = pClipDoc->GetClipParam().getWholeRange();
+ const SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
+ const SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ return SelectionForbidsPaste( nColSize, nRowSize);
+}
+
+bool ScViewData::SelectionForbidsPaste( SCCOL nSrcCols, SCROW nSrcRows )
+{
+ ScRange aSelRange( ScAddress::UNINITIALIZED );
+ ScMarkType eMarkType = GetSimpleArea( aSelRange);
+
+ if (eMarkType == SC_MARK_MULTI)
+ // Not because of DOOM.
+ return false;
+
+ if (aSelRange.aEnd.Row() - aSelRange.aStart.Row() + 1 == nSrcRows)
+ // This also covers entire col(s) copied to be pasted on entire cols.
+ return false;
+
+ if (aSelRange.aEnd.Col() - aSelRange.aStart.Col() + 1 == nSrcCols)
+ // This also covers entire row(s) copied to be pasted on entire rows.
+ return false;
+
+ return SelectionFillDOOM( aSelRange);
+}
+
+bool ScViewData::SelectionForbidsCellFill()
+{
+ ScRange aSelRange( ScAddress::UNINITIALIZED );
+ ScMarkType eMarkType = GetSimpleArea( aSelRange);
+ return eMarkType != SC_MARK_MULTI && SelectionFillDOOM( aSelRange);
+}
+
+// static
+bool ScViewData::SelectionFillDOOM( const ScRange& rRange )
+{
+ // Assume that more than 23 full columns (23M cells) will not be
+ // successful... Even with only 10 bytes per cell that would already be
+ // 230MB, formula cells would be 100 bytes and more per cell.
+ // rows * columns > 23m => rows > 23m / columns
+ // to not overflow in case number of available columns or rows would be
+ // arbitrarily increased.
+ // We could refine this and take some actual cell size into account,
+ // evaluate available memory and what not, but...
+ const sal_Int32 kMax = 23 * 1024 * 1024; // current MAXROWCOUNT1 is 1024*1024=1048576
+ return (rRange.aEnd.Row() - rRange.aStart.Row() + 1) > (kMax / (rRange.aEnd.Col() - rRange.aStart.Col() + 1));
+}
+
+void ScViewData::SetFillMode( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ nFillMode = ScFillMode::FILL;
+ nFillStartX = nStartCol;
+ nFillStartY = nStartRow;
+ nFillEndX = nEndCol;
+ nFillEndY = nEndRow;
+}
+
+void ScViewData::SetDragMode( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScFillMode nMode )
+{
+ nFillMode = nMode;
+ nFillStartX = nStartCol;
+ nFillStartY = nStartRow;
+ nFillEndX = nEndCol;
+ nFillEndY = nEndRow;
+}
+
+void ScViewData::ResetFillMode()
+{
+ nFillMode = ScFillMode::NONE;
+}
+
+void ScViewData::GetFillData( SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow )
+{
+ rStartCol = nFillStartX;
+ rStartRow = nFillStartY;
+ rEndCol = nFillEndX;
+ rEndRow = nFillEndY;
+}
+
+SCCOL ScViewData::GetOldCurX() const
+{
+ if (pThisTab->mbOldCursorValid)
+ return pThisTab->nOldCurX;
+ else
+ return pThisTab->nCurX;
+}
+
+SCROW ScViewData::GetOldCurY() const
+{
+ if (pThisTab->mbOldCursorValid)
+ return pThisTab->nOldCurY;
+ else
+ return pThisTab->nCurY;
+}
+
+void ScViewData::SetOldCursor( SCCOL nNewX, SCROW nNewY )
+{
+ pThisTab->nOldCurX = nNewX;
+ pThisTab->nOldCurY = nNewY;
+ pThisTab->mbOldCursorValid = true;
+}
+
+void ScViewData::ResetOldCursor()
+{
+ pThisTab->mbOldCursorValid = false;
+}
+
+SCCOL ScViewData::GetPosX( ScHSplitPos eWhich, SCTAB nForTab ) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return 0;
+
+ if (nForTab == -1)
+ return pThisTab->nPosX[eWhich];
+
+ if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ return -1;
+
+ return maTabData[nForTab]->nPosX[eWhich];
+}
+
+SCROW ScViewData::GetPosY( ScVSplitPos eWhich, SCTAB nForTab ) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return 0;
+
+ if (nForTab == -1)
+ return pThisTab->nPosY[eWhich];
+
+ if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ return -1;
+
+ return maTabData[nForTab]->nPosY[eWhich];
+}
+
+ScViewDataTable* ScViewData::FetchTableData(SCTAB nTabIndex) const
+{
+ if (!ValidTab(nTabIndex) || (nTabIndex >= static_cast<SCTAB>(maTabData.size())))
+ return nullptr;
+ ScViewDataTable* pRet = maTabData[nTabIndex].get();
+ SAL_WARN_IF(!pRet, "sc.viewdata", "ScViewData::FetchTableData: hidden sheet = " << nTabIndex);
+ return pRet;
+}
+
+SCCOL ScViewData::GetCurXForTab( SCTAB nTabIndex ) const
+{
+ ScViewDataTable* pTabData = FetchTableData(nTabIndex);
+ return pTabData ? pTabData->nCurX : -1;
+}
+
+SCROW ScViewData::GetCurYForTab( SCTAB nTabIndex ) const
+{
+ ScViewDataTable* pTabData = FetchTableData(nTabIndex);
+ return pTabData ? pTabData->nCurY : -1;
+}
+
+void ScViewData::SetCurXForTab( SCCOL nNewCurX, SCTAB nTabIndex )
+{
+ if (ScViewDataTable* pTabData = FetchTableData(nTabIndex))
+ pTabData->nCurX = nNewCurX;
+}
+
+void ScViewData::SetCurYForTab( SCCOL nNewCurY, SCTAB nTabIndex )
+{
+ if (ScViewDataTable* pTabData = FetchTableData(nTabIndex))
+ pTabData->nCurY = nNewCurY;
+}
+
+void ScViewData::SetMaxTiledCol( SCCOL nNewMaxCol )
+{
+ nNewMaxCol = std::clamp(nNewMaxCol, SCCOL(0), mrDoc.MaxCol());
+
+ const SCTAB nTab = GetTabNo();
+ auto GetColWidthPx = [this, nTab](SCCOL nCol) {
+ const sal_uInt16 nSize = this->mrDoc.GetColWidth(nCol, nTab);
+ const tools::Long nSizePx = ScViewData::ToPixel(nSize, nPPTX);
+ return nSizePx;
+ };
+
+ tools::Long nTotalPixels = GetLOKWidthHelper().computePosition(nNewMaxCol, GetColWidthPx);
+
+ SAL_INFO("sc.lok.docsize", "ScViewData::SetMaxTiledCol: nNewMaxCol: "
+ << nNewMaxCol << ", nTotalPixels: " << nTotalPixels);
+
+ GetLOKWidthHelper().removeByIndex(pThisTab->nMaxTiledCol);
+ GetLOKWidthHelper().insert(nNewMaxCol, nTotalPixels);
+
+ pThisTab->nMaxTiledCol = nNewMaxCol;
+}
+
+void ScViewData::SetMaxTiledRow( SCROW nNewMaxRow )
+{
+ if (nNewMaxRow < 0)
+ nNewMaxRow = 0;
+ if (nNewMaxRow > MAXTILEDROW)
+ nNewMaxRow = MAXTILEDROW;
+
+ const SCTAB nTab = GetTabNo();
+ auto GetRowHeightPx = [this, nTab](SCROW nRow) {
+ const sal_uInt16 nSize = this->mrDoc.GetRowHeight(nRow, nTab);
+ const tools::Long nSizePx = ScViewData::ToPixel(nSize, nPPTY);
+ return nSizePx;
+ };
+
+ tools::Long nTotalPixels = GetLOKHeightHelper().computePosition(nNewMaxRow, GetRowHeightPx);
+
+ SAL_INFO("sc.lok.docsize", "ScViewData::SetMaxTiledRow: nNewMaxRow: "
+ << nNewMaxRow << ", nTotalPixels: " << nTotalPixels);
+
+ GetLOKHeightHelper().removeByIndex(pThisTab->nMaxTiledRow);
+ GetLOKHeightHelper().insert(nNewMaxRow, nTotalPixels);
+
+ pThisTab->nMaxTiledRow = nNewMaxRow;
+}
+
+tools::Rectangle ScViewData::GetEditArea( ScSplitPos eWhich, SCCOL nPosX, SCROW nPosY,
+ vcl::Window* pWin, const ScPatternAttr* pPattern,
+ bool bForceToTop, bool bInPrintTwips )
+{
+ Point aCellTopLeft = bInPrintTwips ?
+ GetPrintTwipsPos(nPosX, nPosY) : GetScrPos(nPosX, nPosY, eWhich, true);
+ return ScEditUtil(&mrDoc, nPosX, nPosY, nTabNo, aCellTopLeft,
+ pWin->GetOutDev(), nPPTX, nPPTY, GetZoomX(), GetZoomY(), bInPrintTwips ).
+ GetEditArea( pPattern, bForceToTop );
+}
+
+void ScViewData::SetEditEngine( ScSplitPos eWhich,
+ ScEditEngineDefaulter* pNewEngine,
+ vcl::Window* pWin, SCCOL nNewX, SCROW nNewY )
+{
+ bool bLayoutRTL = mrDoc.IsLayoutRTL(nTabNo);
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ ScVSplitPos eVWhich = WhichV(eWhich);
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLOKPrintTwips = bLOKActive && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+ bool bLOKLayoutRTL = bLOKActive && bLayoutRTL;
+
+ bool bWasThere = false;
+ if (pEditView[eWhich])
+ {
+ // if the view is already there don't call anything that changes the cursor position
+ if (bEditActive[eWhich])
+ {
+ bWasThere = true;
+ }
+ else
+ {
+ lcl_LOKRemoveWindow(GetViewShell(), eWhich);
+ pEditView[eWhich]->SetEditEngine(pNewEngine);
+ }
+
+ if (pEditView[eWhich]->GetWindow() != pWin)
+ {
+ lcl_LOKRemoveWindow(GetViewShell(), eWhich);
+ pEditView[eWhich]->SetWindow(pWin);
+ OSL_FAIL("EditView Window has changed");
+ }
+ }
+ else
+ {
+ pEditView[eWhich].reset(new EditView( pNewEngine, pWin ));
+
+ if (bLOKActive)
+ {
+ // We can broadcast the view-cursor message in print-twips for all views.
+ pEditView[eWhich]->SetBroadcastLOKViewCursor(bLOKPrintTwips);
+ pEditView[eWhich]->RegisterViewShell(pView);
+ }
+ }
+
+ // add windows from other views
+ if (!bWasThere && bLOKActive)
+ {
+ ScTabViewShell* pThisViewShell = GetViewShell();
+ SCTAB nThisTabNo = GetTabNo();
+ auto lAddWindows =
+ [pThisViewShell, nThisTabNo, eWhich] (ScTabViewShell* pOtherViewShell)
+ {
+ ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
+ SCTAB nOtherTabNo = rOtherViewData.GetTabNo();
+ if (nThisTabNo == nOtherTabNo)
+ pOtherViewShell->AddWindowToForeignEditView(pThisViewShell, eWhich);
+ };
+
+ SfxLokHelper::forEachOtherView(pThisViewShell, lAddWindows);
+ }
+
+ // if view is gone then during IdleFormat sometimes a cursor is drawn
+
+ EEControlBits nEC = pNewEngine->GetControlWord();
+ pNewEngine->SetControlWord(nEC & ~EEControlBits::DOIDLEFORMAT);
+
+ EVControlBits nVC = pEditView[eWhich]->GetControlWord();
+ pEditView[eWhich]->SetControlWord(nVC & ~EVControlBits::AUTOSCROLL);
+
+ bEditActive[eWhich] = true;
+
+ const ScPatternAttr* pPattern = mrDoc.GetPattern(nNewX, nNewY, nTabNo);
+ SvxCellHorJustify eJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+
+ bool bBreak = ( eJust == SvxCellHorJustify::Block ) ||
+ pPattern->GetItem(ATTR_LINEBREAK).GetValue();
+
+ bool bAsianVertical = pNewEngine->IsEffectivelyVertical(); // set by InputHandler
+
+ tools::Rectangle aPixRect = ScEditUtil(&mrDoc, nNewX, nNewY, nTabNo, GetScrPos(nNewX, nNewY, eWhich),
+ pWin->GetOutDev(), nPPTX,nPPTY,GetZoomX(),GetZoomY() ).
+ GetEditArea( pPattern, true );
+
+ tools::Rectangle aPTwipsRect;
+ if (bLOKPrintTwips)
+ {
+ aPTwipsRect = ScEditUtil(&mrDoc, nNewX, nNewY, nTabNo, GetPrintTwipsPos(nNewX, nNewY),
+ pWin->GetOutDev(), nPPTX, nPPTY, GetZoomX(), GetZoomY(), true /* bInPrintTwips */).
+ GetEditArea(pPattern, true);
+ }
+
+ // when right-aligned, leave space for the cursor
+ // in vertical mode, editing is always right-aligned
+ if ( GetEditAdjust() == SvxAdjust::Right || bAsianVertical )
+ {
+ aPixRect.AdjustRight(1 );
+ if (bLOKPrintTwips)
+ aPTwipsRect.AdjustRight(o3tl::convert(1, o3tl::Length::px, o3tl::Length::twip));
+ }
+
+ if (bLOKPrintTwips)
+ {
+ if (!pEditView[eWhich]->HasLOKSpecialPositioning())
+ pEditView[eWhich]->InitLOKSpecialPositioning(MapUnit::MapTwip, aPTwipsRect, Point());
+ else
+ pEditView[eWhich]->SetLOKSpecialOutputArea(aPTwipsRect);
+ }
+
+ if (bLOKActive && pEditView[eWhich]->HasLOKSpecialPositioning())
+ pEditView[eWhich]->SetLOKSpecialFlags(bLOKLayoutRTL ? LOKSpecialFlags::LayoutRTL : LOKSpecialFlags::NONE);
+
+ tools::Rectangle aOutputArea = pWin->PixelToLogic( aPixRect, GetLogicMode() );
+ pEditView[eWhich]->SetOutputArea( aOutputArea );
+
+ if ( bActive && eWhich == GetActivePart() )
+ {
+ // keep the part that has the active edit view available after
+ // switching sheets or reference input on a different part
+ eEditActivePart = eWhich;
+
+ // modify members nEditCol etc. only if also extending for needed area
+ nEditCol = nNewX;
+ nEditRow = nNewY;
+ const ScMergeAttr* pMergeAttr = &pPattern->GetItem(ATTR_MERGE);
+ nEditEndCol = nEditCol;
+ if (pMergeAttr->GetColMerge() > 1)
+ nEditEndCol += pMergeAttr->GetColMerge() - 1;
+ nEditEndRow = nEditRow;
+ if (pMergeAttr->GetRowMerge() > 1)
+ nEditEndRow += pMergeAttr->GetRowMerge() - 1;
+ nEditStartCol = nEditCol;
+
+ // For growing use only the alignment value from the attribute, numbers
+ // (existing or started) with default alignment extend to the right.
+ bool bGrowCentered = ( eJust == SvxCellHorJustify::Center );
+ bool bGrowToLeft = ( eJust == SvxCellHorJustify::Right ); // visual left
+ bool bLOKRTLInvert = (bLOKActive && bLayoutRTL);
+ if ( bAsianVertical )
+ bGrowCentered = bGrowToLeft = false; // keep old behavior for asian mode
+
+ tools::Long nSizeXPix, nSizeXPTwips = 0;
+
+ const tools::Long nGridWidthPx = pView->GetGridWidth(eHWhich);
+ const tools::Long nGridHeightPx = pView->GetGridHeight(eVWhich);
+ tools::Long nGridWidthTwips = 0, nGridHeightTwips = 0;
+ if (bLOKPrintTwips)
+ {
+ Size aGridSize(nGridWidthPx, nGridHeightPx);
+ const MapMode& rWinMapMode = GetLogicMode();
+ aGridSize = OutputDevice::LogicToLogic(
+ pWin->PixelToLogic(aGridSize, rWinMapMode),
+ rWinMapMode, MapMode(MapUnit::MapTwip));
+ nGridWidthTwips = aGridSize.Width();
+ nGridHeightTwips = aGridSize.Height();
+ }
+
+ if (bBreak && !bAsianVertical)
+ {
+ nSizeXPix = aPixRect.GetWidth(); // papersize -> no horizontal scrolling
+ if (bLOKPrintTwips)
+ nSizeXPTwips = aPTwipsRect.GetWidth();
+ }
+ else
+ {
+ OSL_ENSURE(pView,"no View for EditView");
+
+ if ( bGrowCentered )
+ {
+ // growing into both directions until one edge is reached
+ //! should be limited to whole cells in both directions
+ tools::Long nLeft = aPixRect.Left();
+ tools::Long nRight = nGridWidthPx - aPixRect.Right();
+ nSizeXPix = aPixRect.GetWidth() + 2 * std::min( nLeft, nRight );
+ if (bLOKPrintTwips)
+ {
+ tools::Long nLeftPTwips = aPTwipsRect.Left();
+ tools::Long nRightPTwips = nGridWidthTwips - aPTwipsRect.Right();
+ nSizeXPTwips = aPTwipsRect.GetWidth() + 2 * std::min(nLeftPTwips, nRightPTwips);
+ }
+ }
+ else if ( (bGrowToLeft && !bLOKRTLInvert) || (!bGrowToLeft && bLOKRTLInvert) )
+ {
+ nSizeXPix = aPixRect.Right(); // space that's available in the window when growing to the left
+ if (bLOKPrintTwips)
+ nSizeXPTwips = aPTwipsRect.Right();
+ }
+ else
+ {
+ nSizeXPix = nGridWidthPx - aPixRect.Left();
+ if (bLOKPrintTwips)
+ nSizeXPTwips = nGridWidthTwips - aPTwipsRect.Left();
+ }
+
+ if ( nSizeXPix <= 0 )
+ {
+ nSizeXPix = aPixRect.GetWidth(); // editing outside to the right of the window -> keep cell width
+ if (bLOKPrintTwips)
+ nSizeXPTwips = aPTwipsRect.GetWidth();
+ }
+ }
+ OSL_ENSURE(pView,"no View for EditView");
+ tools::Long nSizeYPix = nGridHeightPx - aPixRect.Top();
+ tools::Long nSizeYPTwips = bLOKPrintTwips ? (nGridHeightTwips - aPTwipsRect.Top()) : 0;
+
+ if ( nSizeYPix <= 0 )
+ {
+ nSizeYPix = aPixRect.GetHeight(); // editing outside below the window -> keep cell height
+ if (bLOKPrintTwips)
+ nSizeYPTwips = aPTwipsRect.GetHeight();
+ }
+
+ Size aPaperSize = pView->GetActiveWin()->PixelToLogic( Size( nSizeXPix, nSizeYPix ), GetLogicMode() );
+ Size aPaperSizePTwips(nSizeXPTwips, nSizeYPTwips);
+ if ( bBreak && !bAsianVertical && SC_MOD()->GetInputOptions().GetTextWysiwyg() )
+ {
+ // if text is formatted for printer, use the exact same paper width
+ // (and same line breaks) as for output.
+
+ Fraction aFract(1,1);
+ constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100);
+ tools::Rectangle aUtilRect = ScEditUtil(&mrDoc, nNewX, nNewY, nTabNo, Point(0, 0), pWin->GetOutDev(),
+ HMM_PER_TWIPS, HMM_PER_TWIPS, aFract, aFract ).GetEditArea( pPattern, false );
+ aPaperSize.setWidth( aUtilRect.GetWidth() );
+ if (bLOKPrintTwips)
+ {
+ aPaperSizePTwips.setWidth(o3tl::convert(aUtilRect.GetWidth(), o3tl::Length::mm100, o3tl::Length::twip));
+ }
+ }
+
+ pNewEngine->SetPaperSize( aPaperSize );
+ if (bLOKPrintTwips)
+ pNewEngine->SetLOKSpecialPaperSize(aPaperSizePTwips);
+
+ // sichtbarer Ausschnitt
+ Size aPaper = pNewEngine->GetPaperSize();
+ tools::Rectangle aVis = pEditView[eWhich]->GetVisArea();
+ tools::Rectangle aVisPTwips;
+ if (bLOKPrintTwips)
+ aVisPTwips = pEditView[eWhich]->GetLOKSpecialVisArea();
+
+ tools::Long nDiff = aVis.Right() - aVis.Left();
+ tools::Long nDiffPTwips = bLOKPrintTwips ? (aVisPTwips.Right() - aVisPTwips.Left()) : 0;
+ if ( GetEditAdjust() == SvxAdjust::Right )
+ {
+ aVis.SetRight( aPaper.Width() - 1 );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetRight( aPaperSizePTwips.Width() - 1 );
+ bMoveArea = !bLayoutRTL;
+ }
+ else if ( GetEditAdjust() == SvxAdjust::Center )
+ {
+ aVis.SetRight( ( aPaper.Width() - 1 + nDiff ) / 2 );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetRight( ( aPaperSizePTwips.Width() - 1 + nDiffPTwips ) / 2 );
+ bMoveArea = true; // always
+ }
+ else
+ {
+ aVis.SetRight( nDiff );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetRight(nDiffPTwips);
+ bMoveArea = bLayoutRTL;
+ }
+ aVis.SetLeft( aVis.Right() - nDiff );
+ if (bLOKPrintTwips)
+ aVisPTwips.SetLeft(aVisPTwips.Right() - nDiffPTwips);
+ // #i49561# Important note:
+ // The set offset of the visible area of the EditView for centered and
+ // right alignment in horizontal layout is consider by instances of
+ // class <ScEditObjectViewForwarder> in its methods <LogicToPixel(..)>
+ // and <PixelToLogic(..)>. This is needed for the correct visibility
+ // of paragraphs in edit mode at the accessibility API.
+ pEditView[eWhich]->SetVisArea(aVis);
+ if (bLOKPrintTwips)
+ pEditView[eWhich]->SetLOKSpecialVisArea(aVisPTwips);
+ // UpdateMode has been disabled in ScInputHandler::StartTable
+ // must be enabled before EditGrowY (GetTextHeight)
+ pNewEngine->SetUpdateLayout( true );
+
+ pNewEngine->SetStatusEventHdl( LINK( this, ScViewData, EditEngineHdl ) );
+
+ EditGrowY( true ); // adjust to existing text content
+ EditGrowX();
+
+ Point aDocPos = pEditView[eWhich]->GetWindowPosTopLeft(0);
+ if (aDocPos.Y() < aOutputArea.Top())
+ pEditView[eWhich]->Scroll( 0, aOutputArea.Top() - aDocPos.Y() );
+ }
+
+ // here bEditActive needs to be set already
+ // (due to Map-Mode during Paint)
+ if (!bWasThere)
+ pNewEngine->InsertView(pEditView[eWhich].get());
+
+ // background color of the cell
+ Color aBackCol = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
+
+ ScModule* pScMod = SC_MOD();
+ if ( aBackCol.IsTransparent() )
+ {
+ aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ }
+ pEditView[eWhich]->SetBackgroundColor( aBackCol );
+
+ pEditView[eWhich]->Invalidate(); // needed?
+ // needed, if position changed
+}
+
+IMPL_LINK( ScViewData, EditEngineHdl, EditStatus&, rStatus, void )
+{
+ EditStatusFlags nStatus = rStatus.GetStatusWord();
+ if (nStatus & (EditStatusFlags::HSCROLL | EditStatusFlags::TextHeightChanged | EditStatusFlags::TEXTWIDTHCHANGED | EditStatusFlags::CURSOROUT))
+ {
+ EditGrowY();
+ EditGrowX();
+
+ if (nStatus & EditStatusFlags::CURSOROUT)
+ {
+ ScSplitPos eWhich = GetActivePart();
+ if (pEditView[eWhich])
+ pEditView[eWhich]->ShowCursor(false);
+ }
+ }
+}
+
+void ScViewData::EditGrowX()
+{
+ // It is insane to call EditGrowX while the output area is already growing.
+ // That could occur because of the call to SetDefaultItem later.
+ // We end up with wrong start/end edit columns and the changes
+ // to the output area performed by the inner call to this method are
+ // useless since they are discarded by the outer call.
+ if (bGrowing)
+ return;
+
+ comphelper::FlagRestorationGuard aFlagGuard(bGrowing, true);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLOKPrintTwips = bLOKActive && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ ScDocument& rLocalDoc = GetDocument();
+
+ ScSplitPos eWhich = GetActivePart();
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ EditView* pCurView = pEditView[eWhich].get();
+
+ if ( !pCurView || !bEditActive[eWhich])
+ return;
+
+ bool bLayoutRTL = rLocalDoc.IsLayoutRTL( nTabNo );
+
+ ScEditEngineDefaulter* pEngine =
+ static_cast<ScEditEngineDefaulter*>( pCurView->GetEditEngine() );
+ vcl::Window* pWin = pCurView->GetWindow();
+
+ // Get the left- and right-most column positions.
+ SCCOL nLeft = GetPosX(eHWhich);
+ SCCOL nRight = nLeft + VisibleCellsX(eHWhich);
+
+ Size aSize = pEngine->GetPaperSize();
+ Size aSizePTwips;
+ if (bLOKPrintTwips)
+ aSizePTwips = pEngine->GetLOKSpecialPaperSize();
+
+ tools::Rectangle aArea = pCurView->GetOutputArea();
+ tools::Rectangle aAreaPTwips;
+ if (bLOKPrintTwips)
+ aAreaPTwips = pCurView->GetLOKSpecialOutputArea();
+
+ tools::Long nOldRight = aArea.Right();
+
+ // Margin is already included in the original width.
+ tools::Long nTextWidth = pEngine->CalcTextWidth();
+
+ bool bChanged = false;
+ bool bAsianVertical = pEngine->IsEffectivelyVertical();
+
+ // get bGrow... variables the same way as in SetEditEngine
+ const ScPatternAttr* pPattern = rLocalDoc.GetPattern( nEditCol, nEditRow, nTabNo );
+ SvxCellHorJustify eJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+ bool bGrowCentered = ( eJust == SvxCellHorJustify::Center );
+ bool bGrowToLeft = ( eJust == SvxCellHorJustify::Right ); // visual left
+ bool bGrowBackwards = bGrowToLeft; // logical left
+ if ( bLayoutRTL )
+ bGrowBackwards = !bGrowBackwards; // invert on RTL sheet
+ if ( bAsianVertical )
+ bGrowCentered = bGrowToLeft = bGrowBackwards = false; // keep old behavior for asian mode
+
+ bool bUnevenGrow = false;
+ if ( bGrowCentered )
+ {
+ while (aArea.GetWidth() + 0 < nTextWidth && ( nEditStartCol > nLeft || nEditEndCol < nRight ) )
+ {
+ tools::Long nLogicLeft = 0;
+ tools::Long nLogicLeftPTwips = 0;
+ if ( nEditStartCol > nLeft )
+ {
+ --nEditStartCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditStartCol, nTabNo );
+ tools::Long nLeftPix = ToPixel( nColWidth, nPPTX );
+ nLogicLeft = pWin->PixelToLogic(Size(nLeftPix,0)).Width();
+ if (bLOKPrintTwips)
+ nLogicLeftPTwips = nColWidth;
+ }
+ tools::Long nLogicRight = 0;
+ tools::Long nLogicRightPTwips = 0;
+ if ( nEditEndCol < nRight )
+ {
+ ++nEditEndCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditEndCol, nTabNo );
+ tools::Long nRightPix = ToPixel( nColWidth, nPPTX );
+ nLogicRight = pWin->PixelToLogic(Size(nRightPix,0)).Width();
+ if (bLOKPrintTwips)
+ nLogicRightPTwips = nColWidth;
+ }
+
+ aArea.AdjustLeft( -((bLayoutRTL && !bLOKActive) ? nLogicRight : nLogicLeft) );
+ aArea.AdjustRight((bLayoutRTL && !bLOKActive) ? nLogicLeft : nLogicRight );
+ if (bLOKPrintTwips)
+ {
+ aAreaPTwips.AdjustLeft(-nLogicLeftPTwips);
+ aAreaPTwips.AdjustRight(nLogicRightPTwips);
+ }
+
+ if ( aArea.Right() > aArea.Left() + aSize.Width() - 1 )
+ {
+ tools::Long nCenter = ( aArea.Left() + aArea.Right() ) / 2;
+ tools::Long nHalf = aSize.Width() / 2;
+ aArea.SetLeft( nCenter - nHalf + 1 );
+ aArea.SetRight( nCenter + aSize.Width() - nHalf - 1 );
+
+ if (bLOKPrintTwips)
+ {
+ tools::Long nCenterPTwips = ( aAreaPTwips.Left() + aAreaPTwips.Right() ) / 2;
+ tools::Long nHalfPTwips = aSizePTwips.Width() / 2;
+ aAreaPTwips.SetLeft( nCenterPTwips - nHalfPTwips + 1 );
+ aAreaPTwips.SetRight( nCenterPTwips + aSizePTwips.Width() - nHalfPTwips - 1 );
+ }
+ }
+
+ bChanged = true;
+ if ( nLogicLeft != nLogicRight )
+ bUnevenGrow = true;
+ }
+ }
+ else if ( bGrowBackwards )
+ {
+ while (aArea.GetWidth() + 0 < nTextWidth && nEditStartCol > nLeft)
+ {
+ --nEditStartCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditStartCol, nTabNo );
+ tools::Long nPix = ToPixel( nColWidth, nPPTX );
+ tools::Long nLogicWidth = pWin->PixelToLogic(Size(nPix,0)).Width();
+ tools::Long& nLogicWidthPTwips = nColWidth;
+
+ if ( !bLayoutRTL || bLOKActive )
+ {
+ aArea.AdjustLeft( -nLogicWidth );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustLeft( -nLogicWidthPTwips );
+ }
+ else
+ {
+ aArea.AdjustRight(nLogicWidth );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustRight(nLogicWidthPTwips);
+ }
+
+ if ( aArea.Right() > aArea.Left() + aSize.Width() - 1 )
+ {
+ if ( !bLayoutRTL || bLOKActive )
+ {
+ aArea.SetLeft( aArea.Right() - aSize.Width() + 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetLeft( aAreaPTwips.Right() - aSizePTwips.Width() + 1 );
+ }
+ else
+ {
+ aArea.SetRight( aArea.Left() + aSize.Width() - 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetRight( aAreaPTwips.Left() + aSizePTwips.Width() - 1 );
+ }
+ }
+
+ bChanged = true;
+ }
+ }
+ else
+ {
+ while (aArea.GetWidth() + 0 < nTextWidth && nEditEndCol < nRight)
+ {
+ ++nEditEndCol;
+ tools::Long nColWidth = rLocalDoc.GetColWidth( nEditEndCol, nTabNo );
+ tools::Long nPix = ToPixel( nColWidth, nPPTX );
+ tools::Long nLogicWidth = pWin->PixelToLogic(Size(nPix,0)).Width();
+ tools::Long& nLogicWidthPTwips = nColWidth;
+ if ( bLayoutRTL && !bLOKActive )
+ {
+ aArea.AdjustLeft( -nLogicWidth );
+ }
+ else
+ {
+ aArea.AdjustRight(nLogicWidth );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustRight(nLogicWidthPTwips);
+ }
+
+ if ( aArea.Right() > aArea.Left() + aSize.Width() - 1 )
+ {
+ if ( bLayoutRTL && !bLOKActive )
+ {
+ aArea.SetLeft( aArea.Right() - aSize.Width() + 1 );
+ }
+ else
+ {
+ aArea.SetRight( aArea.Left() + aSize.Width() - 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetRight( aAreaPTwips.Left() + aSizePTwips.Width() - 1 );
+ }
+ }
+
+ bChanged = true;
+ }
+ }
+
+ if (!bChanged)
+ return;
+
+ if ( bMoveArea || bGrowCentered || bGrowBackwards || bLayoutRTL )
+ {
+ tools::Rectangle aVis = pCurView->GetVisArea();
+ tools::Rectangle aVisPTwips;
+ if (bLOKPrintTwips)
+ aVisPTwips = pCurView->GetLOKSpecialVisArea();
+
+ if ( bGrowCentered )
+ {
+ // switch to center-aligned (undo?) and reset VisArea to center
+
+ pEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) );
+
+ tools::Long nCenter = aSize.Width() / 2;
+ tools::Long nVisSize = aArea.GetWidth();
+ aVis.SetLeft( nCenter - nVisSize / 2 );
+ aVis.SetRight( aVis.Left() + nVisSize - 1 );
+
+ if (bLOKPrintTwips)
+ {
+ tools::Long nCenterPTwips = aSizePTwips.Width() / 2;
+ tools::Long nVisSizePTwips = aAreaPTwips.GetWidth();
+ aVisPTwips.SetLeft( nCenterPTwips - nVisSizePTwips / 2 );
+ aVisPTwips.SetRight( aVisPTwips.Left() + nVisSizePTwips - 1 );
+ }
+ }
+ else if ( bGrowToLeft )
+ {
+ // switch to right-aligned (undo?) and reset VisArea to the right
+
+ pEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+
+ aVis.SetRight( aSize.Width() - 1 );
+ aVis.SetLeft( aSize.Width() - aArea.GetWidth() ); // with the new, increased area
+
+ if (bLOKPrintTwips)
+ {
+ aVisPTwips.SetRight( aSizePTwips.Width() - 1 );
+ aVisPTwips.SetLeft( aSizePTwips.Width() - aAreaPTwips.GetWidth() ); // with the new, increased area
+ }
+ }
+ else
+ {
+ // switch to left-aligned (undo?) and reset VisArea to the left
+
+ pEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) );
+
+ tools::Long nMove = aVis.Left();
+ aVis.SetLeft( 0 );
+ aVis.AdjustRight( -nMove );
+
+ if (bLOKPrintTwips)
+ {
+ tools::Long nMovePTwips = aVisPTwips.Left();
+ aVisPTwips.SetLeft( 0 );
+ aVisPTwips.AdjustRight( -nMovePTwips );
+ }
+ }
+
+ pCurView->SetVisArea( aVis );
+ if (bLOKPrintTwips)
+ pCurView->SetLOKSpecialVisArea( aVisPTwips );
+
+ bMoveArea = false;
+ }
+
+ if (bLOKPrintTwips)
+ pCurView->SetLOKSpecialOutputArea(aAreaPTwips);
+
+ pCurView->SetOutputArea(aArea);
+
+ // In vertical mode, the whole text is moved to the next cell (right-aligned),
+ // so everything must be repainted. Otherwise, paint only the new area.
+ // If growing in centered alignment, if the cells left and right have different sizes,
+ // the whole text will move, and may not even obscure all of the original display.
+ if ( bUnevenGrow )
+ {
+ aArea.SetLeft( pWin->PixelToLogic( Point(0,0) ).X() );
+ aArea.SetRight( pWin->PixelToLogic( aScrSize ).Width() );
+ }
+ else if ( !bAsianVertical && !bGrowToLeft && !bGrowCentered )
+ aArea.SetLeft( nOldRight );
+ pWin->Invalidate(aArea);
+
+ // invalidate other views
+ pCurView->InvalidateOtherViewWindows(aArea);
+}
+
+void ScViewData::EditGrowY( bool bInitial )
+{
+ if (bGrowing)
+ return;
+
+ comphelper::FlagRestorationGuard aFlagGuard(bGrowing, true);
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+ bool bLOKPrintTwips = bLOKActive && comphelper::LibreOfficeKit::isCompatFlagSet(
+ comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+ ScSplitPos eWhich = GetActivePart();
+ ScVSplitPos eVWhich = WhichV(eWhich);
+ EditView* pCurView = pEditView[eWhich].get();
+
+ if ( !pCurView || !bEditActive[eWhich])
+ return;
+
+ EVControlBits nControl = pEditView[eWhich]->GetControlWord();
+ if ( nControl & EVControlBits::AUTOSCROLL )
+ {
+ // if end of screen had already been reached and scrolling enabled,
+ // don't further try to grow the edit area
+
+ pCurView->SetOutputArea( pCurView->GetOutputArea() ); // re-align to pixels
+ return;
+ }
+
+ EditEngine* pEngine = pCurView->GetEditEngine();
+ vcl::Window* pWin = pCurView->GetWindow();
+
+ SCROW nBottom = GetPosY(eVWhich) + VisibleCellsY(eVWhich);
+
+ Size aSize = pEngine->GetPaperSize();
+ Size aSizePTwips;
+ tools::Rectangle aArea = pCurView->GetOutputArea();
+ tools::Rectangle aAreaPTwips;
+
+ if (bLOKPrintTwips)
+ {
+ aSizePTwips = pEngine->GetLOKSpecialPaperSize();
+ aAreaPTwips = pCurView->GetLOKSpecialOutputArea();
+ }
+
+ tools::Long nOldBottom = aArea.Bottom();
+ tools::Long nTextHeight = pEngine->GetTextHeight();
+
+ // When editing a formula in a cell with optimal height, allow a larger portion
+ // to be clipped before extending to following rows, to avoid obscuring cells for
+ // reference input (next row is likely to be useful in formulas).
+ tools::Long nAllowedExtra = SC_GROWY_SMALL_EXTRA;
+ if (nEditEndRow == nEditRow && !(mrDoc.GetRowFlags(nEditRow, nTabNo) & CRFlags::ManualSize) &&
+ pEngine->GetParagraphCount() <= 1 )
+ {
+ // If the (only) paragraph starts with a '=', it's a formula.
+ // If this is the initial call and the text is empty, allow the larger value, too,
+ // because this occurs in the normal progress of editing a formula.
+ // Subsequent calls with empty text might involve changed attributes (including
+ // font height), so they are treated like normal text.
+ OUString aText = pEngine->GetText( 0 );
+ if ( ( aText.isEmpty() && bInitial ) || aText.startsWith("=") )
+ nAllowedExtra = SC_GROWY_BIG_EXTRA;
+ }
+
+ bool bChanged = false;
+ bool bMaxReached = false;
+ while (aArea.GetHeight() + nAllowedExtra < nTextHeight && nEditEndRow < nBottom && !bMaxReached)
+ {
+ ++nEditEndRow;
+ ScDocument& rLocalDoc = GetDocument();
+ tools::Long nRowHeight = rLocalDoc.GetRowHeight( nEditEndRow, nTabNo );
+ tools::Long nPix = ToPixel( nRowHeight, nPPTY );
+ aArea.AdjustBottom(pWin->PixelToLogic(Size(0,nPix)).Height() );
+ if (bLOKPrintTwips)
+ aAreaPTwips.AdjustBottom(nRowHeight);
+
+ if ( aArea.Bottom() > aArea.Top() + aSize.Height() - 1 )
+ {
+ aArea.SetBottom( aArea.Top() + aSize.Height() - 1 );
+ if (bLOKPrintTwips)
+ aAreaPTwips.SetBottom( aAreaPTwips.Top() + aSizePTwips.Height() - 1 );
+ bMaxReached = true; // don't occupy more cells beyond paper size
+ }
+
+ bChanged = true;
+ nAllowedExtra = SC_GROWY_SMALL_EXTRA; // larger value is only for first row
+ }
+
+ if (!bChanged)
+ return;
+
+ if (bLOKPrintTwips)
+ pCurView->SetLOKSpecialOutputArea(aAreaPTwips);
+
+ pCurView->SetOutputArea(aArea);
+
+ if (nEditEndRow >= nBottom || bMaxReached)
+ {
+ if (!(nControl & EVControlBits::AUTOSCROLL))
+ pCurView->SetControlWord( nControl | EVControlBits::AUTOSCROLL );
+ }
+
+ aArea.SetTop( nOldBottom );
+ pWin->Invalidate(aArea);
+
+ // invalidate other views
+ pCurView->InvalidateOtherViewWindows(aArea);
+}
+
+void ScViewData::ResetEditView()
+{
+ EditEngine* pEngine = nullptr;
+ for (sal_uInt16 i=0; i<4; i++)
+ if (pEditView[i])
+ {
+ if (bEditActive[i])
+ {
+ lcl_LOKRemoveWindow(GetViewShell(), static_cast<ScSplitPos>(i));
+ pEngine = pEditView[i]->GetEditEngine();
+ pEngine->RemoveView(pEditView[i].get());
+ pEditView[i]->SetOutputArea( tools::Rectangle() );
+ }
+ bEditActive[i] = false;
+ }
+
+ if (pEngine)
+ pEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+}
+
+void ScViewData::KillEditView()
+{
+ EditEngine* pEngine = nullptr;
+ for (sal_uInt16 i=0; i<4; i++)
+ if (pEditView[i])
+ {
+ if (bEditActive[i])
+ {
+ pEngine = pEditView[i]->GetEditEngine();
+ if (pEngine)
+ pEngine->RemoveView(pEditView[i].get());
+ }
+ pEditView[i].reset();
+ }
+}
+
+void ScViewData::GetEditView( ScSplitPos eWhich, EditView*& rViewPtr, SCCOL& rCol, SCROW& rRow )
+{
+ rViewPtr = pEditView[eWhich].get();
+ rCol = nEditCol;
+ rRow = nEditRow;
+}
+
+void ScViewData::CreateTabData( SCTAB nNewTab )
+{
+ EnsureTabDataSize(nNewTab + 1);
+
+ if (!maTabData[nNewTab])
+ {
+ maTabData[nNewTab].reset(new ScViewDataTable(&mrDoc));
+
+ maTabData[nNewTab]->eZoomType = eDefZoomType;
+ maTabData[nNewTab]->aZoomX = aDefZoomX;
+ maTabData[nNewTab]->aZoomY = aDefZoomY;
+ maTabData[nNewTab]->aPageZoomX = aDefPageZoomX;
+ maTabData[nNewTab]->aPageZoomY = aDefPageZoomY;
+ }
+}
+
+void ScViewData::CreateSelectedTabData()
+{
+ for (const auto& rTab : maMarkData)
+ CreateTabData(rTab);
+}
+
+void ScViewData::EnsureTabDataSize(size_t nSize)
+{
+ if (nSize > maTabData.size())
+ maTabData.resize(nSize);
+}
+
+void ScViewData::SetTabNo( SCTAB nNewTab )
+{
+ if (!ValidTab(nNewTab))
+ {
+ OSL_FAIL("wrong sheet number");
+ return;
+ }
+
+ nTabNo = nNewTab;
+ CreateTabData(nTabNo);
+ pThisTab = maTabData[nTabNo].get();
+
+ CalcPPT(); // for common column width correction
+ RecalcPixPos(); //! not always needed!
+}
+
+ScPositionHelper* ScViewData::GetLOKWidthHelper(SCTAB nTabIndex)
+{
+ if (!ValidTab(nTabIndex) || (nTabIndex >= static_cast<SCTAB>(maTabData.size())) ||
+ !maTabData[nTabIndex])
+ {
+ return nullptr;
+ }
+ return &(maTabData[nTabIndex]->aWidthHelper);
+}
+
+ScPositionHelper* ScViewData::GetLOKHeightHelper(SCTAB nTabIndex)
+{
+ if (!ValidTab(nTabIndex) || (nTabIndex >= static_cast<SCTAB>(maTabData.size())) ||
+ !maTabData[nTabIndex])
+ {
+ return nullptr;
+ }
+ return &(maTabData[nTabIndex]->aHeightHelper);
+}
+
+void ScViewData::SetActivePart( ScSplitPos eNewActive )
+{
+ pThisTab->eWhichActive = eNewActive;
+
+ // Let's hope we find the culprit for tdf#117093
+ // Don't sanitize the real value (yet?) because this function might be
+ // called before setting the then corresponding split modes. For which in
+ // fact then the order should be changed.
+ assert(eNewActive == pThisTab->SanitizeWhichActive());
+}
+
+Point ScViewData::GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScHSplitPos eWhich ) const
+{
+ OSL_ENSURE( eWhich==SC_SPLIT_LEFT || eWhich==SC_SPLIT_RIGHT, "wrong position" );
+ ScSplitPos ePos = ( eWhich == SC_SPLIT_LEFT ) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
+ return GetScrPos( nWhereX, nWhereY, ePos );
+}
+
+Point ScViewData::GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScVSplitPos eWhich ) const
+{
+ OSL_ENSURE( eWhich==SC_SPLIT_TOP || eWhich==SC_SPLIT_BOTTOM, "wrong position" );
+ ScSplitPos ePos = ( eWhich == SC_SPLIT_TOP ) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
+ return GetScrPos( nWhereX, nWhereY, ePos );
+}
+
+Point ScViewData::GetScrPos( SCCOL nWhereX, SCROW nWhereY, ScSplitPos eWhich,
+ bool bAllowNeg, SCTAB nForTab ) const
+{
+ ScHSplitPos eWhichX = SC_SPLIT_LEFT;
+ ScVSplitPos eWhichY = SC_SPLIT_BOTTOM;
+ switch( eWhich )
+ {
+ case SC_SPLIT_TOPLEFT:
+ eWhichX = SC_SPLIT_LEFT;
+ eWhichY = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_TOPRIGHT:
+ eWhichX = SC_SPLIT_RIGHT;
+ eWhichY = SC_SPLIT_TOP;
+ break;
+ case SC_SPLIT_BOTTOMLEFT:
+ eWhichX = SC_SPLIT_LEFT;
+ eWhichY = SC_SPLIT_BOTTOM;
+ break;
+ case SC_SPLIT_BOTTOMRIGHT:
+ eWhichX = SC_SPLIT_RIGHT;
+ eWhichY = SC_SPLIT_BOTTOM;
+ break;
+ }
+
+ if (nForTab == -1)
+ nForTab = nTabNo;
+ bool bForCurTab = (nForTab == nTabNo);
+ if (!bForCurTab && (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size()))))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::GetScrPos : invalid nForTab = " << nForTab);
+ nForTab = nTabNo;
+ bForCurTab = true;
+ }
+
+ ScViewDataTable* pViewTable = bForCurTab ? pThisTab : maTabData[nForTab].get();
+
+ if (pView)
+ {
+ const_cast<ScViewData*>(this)->aScrSize.setWidth( pView->GetGridWidth(eWhichX) );
+ const_cast<ScViewData*>(this)->aScrSize.setHeight( pView->GetGridHeight(eWhichY) );
+ }
+
+ sal_uInt16 nTSize;
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ SCCOL nPosX = GetPosX(eWhichX, nForTab);
+ tools::Long nScrPosX = 0;
+
+ if (bAllowNeg || nWhereX >= nPosX)
+ {
+ SCROW nStartPosX = nPosX;
+ if (bIsTiledRendering)
+ {
+ OSL_ENSURE(nPosX == 0, "Unsupported case.");
+ const auto& rNearest = pViewTable->aWidthHelper.getNearestByIndex(nWhereX - 1);
+ nStartPosX = rNearest.first + 1;
+ nScrPosX = rNearest.second;
+ }
+
+ if (nWhereX >= nStartPosX)
+ {
+ for (SCCOL nX = nStartPosX; nX < nWhereX && (bAllowNeg || bIsTiledRendering || nScrPosX <= aScrSize.Width()); nX++)
+ {
+ if (nX > mrDoc.MaxCol())
+ nScrPosX = 0x7FFFFFFF;
+ else
+ {
+ nTSize = mrDoc.GetColWidth(nX, nForTab);
+ if (nTSize)
+ {
+ tools::Long nSizeXPix = ToPixel( nTSize, nPPTX );
+ nScrPosX += nSizeXPix;
+ }
+ else
+ { // If the width is zero, the column is possibly hidden, skips groups of such columns.
+ SCCOL lastHidden = -1;
+ if(mrDoc.ColHidden(nX, nForTab, nullptr, &lastHidden) && lastHidden > nX)
+ nX = lastHidden - 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SCCOL nX = nStartPosX; nX > nWhereX;)
+ {
+ --nX;
+ nTSize = mrDoc.GetColWidth(nX, nForTab);
+ if (nTSize)
+ {
+ tools::Long nSizeXPix = ToPixel( nTSize, nPPTX );
+ nScrPosX -= nSizeXPix;
+ }
+ else
+ { // If the width is zero, the column is possibly hidden, skips groups of such columns.
+ SCCOL firstHidden = -1;
+ if(mrDoc.ColHidden(nX, nForTab, &firstHidden, nullptr) && firstHidden >= 0)
+ nX = firstHidden;
+ }
+ }
+ }
+
+ }
+
+
+ SCROW nPosY = GetPosY(eWhichY, nForTab);
+ tools::Long nScrPosY = 0;
+
+ if (bAllowNeg || nWhereY >= nPosY)
+ {
+ SCROW nStartPosY = nPosY;
+ if (bIsTiledRendering)
+ {
+ OSL_ENSURE(nPosY == 0, "Unsupported case.");
+ const auto& rNearest = pViewTable->aHeightHelper.getNearestByIndex(nWhereY - 1);
+ nStartPosY = rNearest.first + 1;
+ nScrPosY = rNearest.second;
+ }
+
+ if (nWhereY >= nStartPosY)
+ {
+ for (SCROW nY = nStartPosY; nY < nWhereY && (bAllowNeg || bIsTiledRendering || nScrPosY <= aScrSize.Height()); nY++)
+ {
+ if ( nY > mrDoc.MaxRow() )
+ nScrPosY = 0x7FFFFFFF;
+ else
+ {
+ nTSize = mrDoc.GetRowHeight( nY, nTabNo );
+ if (nTSize)
+ {
+ tools::Long nSizeYPix = ToPixel( nTSize, nPPTY );
+ nScrPosY += nSizeYPix;
+ }
+ else if ( nY < mrDoc.MaxRow() )
+ {
+ // skip multiple hidden rows (forward only for now)
+ SCROW nNext = mrDoc.FirstVisibleRow(nY + 1, mrDoc.MaxRow(), nTabNo);
+ if ( nNext > mrDoc.MaxRow() )
+ nY = mrDoc.MaxRow();
+ else
+ nY = nNext - 1; // +=nDir advances to next visible row
+ }
+ }
+ }
+ }
+ else
+ {
+ for (SCROW nY = nStartPosY; nY > nWhereY;)
+ {
+ --nY;
+ nTSize = mrDoc.GetRowHeight(nY, nForTab);
+ if (nTSize)
+ {
+ tools::Long nSizeYPix = ToPixel( nTSize, nPPTY );
+ nScrPosY -= nSizeYPix;
+ }
+ else
+ { // If the height is zero, the row is possibly hidden, skips groups of such rows.
+ SCROW firstHidden = -1;
+ if(mrDoc.RowHidden(nY, nForTab, &firstHidden, nullptr) && firstHidden >= 0)
+ nY = firstHidden;
+ }
+ }
+ }
+ }
+
+ if (mrDoc.IsLayoutRTL(nForTab) && !bIsTiledRendering)
+ {
+ // mirror horizontal position
+ nScrPosX = aScrSize.Width() - 1 - nScrPosX;
+ }
+
+ return Point( nScrPosX, nScrPosY );
+}
+
+Point ScViewData::GetPrintTwipsPos(SCCOL nCol, SCROW nRow) const
+{
+ // hidden ones are given 0 sizes by these by default.
+ // TODO: rewrite this to loop over spans (matters for jumbosheets).
+ tools::Long nPosX = nCol ? mrDoc.GetColWidth(0, nCol - 1, nTabNo) : 0;
+ // This is now fast as it loops over spans.
+ tools::Long nPosY = nRow ? mrDoc.GetRowHeight(0, nRow - 1, nTabNo) : 0;
+ // TODO: adjust for RTL layout case.
+
+ return Point(nPosX, nPosY);
+}
+
+Point ScViewData::GetPrintTwipsPosFromTileTwips(const Point& rTileTwipsPos) const
+{
+ const tools::Long nPixelX = static_cast<tools::Long>(rTileTwipsPos.X() * nPPTX);
+ const tools::Long nPixelY = static_cast<tools::Long>(rTileTwipsPos.Y() * nPPTY);
+ SCCOL nCol = 0;
+ SCROW nRow = 0;
+
+ // The following call (with bTestMerge = false) will not modify any members.
+ const_cast<ScViewData*>(this)->GetPosFromPixel(nPixelX, nPixelY, SC_SPLIT_TOPLEFT, nCol, nRow, false /* bTestMerge */);
+ const Point aPixCellPos = GetScrPos(nCol, nRow, SC_SPLIT_TOPLEFT, true /* bAllowNeg */);
+ const Point aTileTwipsCellPos(aPixCellPos.X() / nPPTX, aPixCellPos.Y() / nPPTY);
+ const Point aPrintTwipsCellPos = GetPrintTwipsPos(nCol, nRow);
+ return aPrintTwipsCellPos + (rTileTwipsPos - aTileTwipsCellPos);
+}
+
+OString ScViewData::describeCellCursorAt(SCCOL nX, SCROW nY, bool bPixelAligned) const
+{
+ const bool bPosSizeInPixels = bPixelAligned;
+ Point aCellPos = bPosSizeInPixels ? GetScrPos( nX, nY, SC_SPLIT_BOTTOMRIGHT, true ) :
+ GetPrintTwipsPos(nX, nY);
+
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ if (bPosSizeInPixels)
+ GetMergeSizePixel( nX, nY, nSizeX, nSizeY );
+ else
+ GetMergeSizePrintTwips(nX, nY, nSizeX, nSizeY);
+
+ std::stringstream ss;
+ if (bPosSizeInPixels)
+ {
+ double fPPTX = GetPPTX();
+ double fPPTY = GetPPTY();
+
+ // make it a slim cell cursor, but not empty
+ if (nSizeX == 0)
+ nSizeX = 1;
+
+ if (nSizeY == 0)
+ nSizeY = 1;
+
+ tools::Long nPosXTw = rtl::math::round(aCellPos.getX() / fPPTX);
+ tools::Long nPosYTw = rtl::math::round(aCellPos.getY() / fPPTY);
+ // look at Rectangle( const Point& rLT, const Size& rSize ) for the '- 1'
+ tools::Long nSizeXTw = rtl::math::round(nSizeX / fPPTX) - 1;
+ tools::Long nSizeYTw = rtl::math::round(nSizeY / fPPTY) - 1;
+
+ ss << nPosXTw << ", " << nPosYTw << ", " << nSizeXTw << ", " << nSizeYTw << ", "
+ << nX << ", " << nY;
+ }
+ else
+ {
+ // look at Rectangle( const Point& rLT, const Size& rSize ) for the decrement.
+ if (nSizeX)
+ --nSizeX;
+ if (nSizeY)
+ --nSizeY;
+ ss << aCellPos.getX() << ", " << aCellPos.getY()
+ << ", " << nSizeX << ", " << nSizeY << ", "
+ << nX << ", " << nY;
+ }
+
+ return OString(ss.str());
+}
+
+// Number of cells on a screen
+SCCOL ScViewData::CellsAtX( SCCOL nPosX, SCCOL nDir, ScHSplitPos eWhichX, sal_uInt16 nScrSizeX ) const
+{
+ OSL_ENSURE( nDir==1 || nDir==-1, "wrong CellsAt call" );
+
+ if (pView)
+ const_cast<ScViewData*>(this)->aScrSize.setWidth( pView->GetGridWidth(eWhichX) );
+
+ SCCOL nX;
+ sal_uInt16 nScrPosX = 0;
+ if (nScrSizeX == SC_SIZE_NONE) nScrSizeX = static_cast<sal_uInt16>(aScrSize.Width());
+
+ if (nDir==1)
+ nX = nPosX; // forwards
+ else
+ nX = nPosX-1; // backwards
+
+ bool bOut = false;
+ for ( ; nScrPosX<=nScrSizeX && !bOut; nX = sal::static_int_cast<SCCOL>(nX + nDir) )
+ {
+ SCCOL nColNo = nX;
+ if (nColNo < 0 || nColNo > mrDoc.MaxCol())
+ bOut = true;
+ else
+ {
+ sal_uInt16 nTSize = mrDoc.GetColWidth(nColNo, nTabNo);
+ if (nTSize)
+ {
+ tools::Long nSizeXPix = ToPixel( nTSize, nPPTX );
+ nScrPosX = sal::static_int_cast<sal_uInt16>( nScrPosX + static_cast<sal_uInt16>(nSizeXPix) );
+ }
+ }
+ }
+
+ if (nDir==1)
+ nX = sal::static_int_cast<SCCOL>( nX - nPosX );
+ else
+ nX = (nPosX-1)-nX;
+
+ if (nX>0) --nX;
+ return nX;
+}
+
+SCROW ScViewData::CellsAtY( SCROW nPosY, SCROW nDir, ScVSplitPos eWhichY, sal_uInt16 nScrSizeY ) const
+{
+ OSL_ENSURE( nDir==1 || nDir==-1, "wrong CellsAt call" );
+
+ if (pView)
+ const_cast<ScViewData*>(this)->aScrSize.setHeight( pView->GetGridHeight(eWhichY) );
+
+ if (nScrSizeY == SC_SIZE_NONE) nScrSizeY = static_cast<sal_uInt16>(aScrSize.Height());
+
+ SCROW nY;
+
+ if (nDir==1)
+ {
+ // forward
+ nY = nPosY;
+ tools::Long nScrPosY = 0;
+ AddPixelsWhile(nScrPosY, nScrSizeY, nY, mrDoc.MaxRow(), nPPTY, &mrDoc, nTabNo);
+ // Original loop ended on last evaluated +1 or if that was MaxRow even on MaxRow+2.
+ nY += (nY == mrDoc.MaxRow() ? 2 : 1);
+ nY -= nPosY;
+ }
+ else
+ {
+ // backward
+ nY = nPosY-1;
+ tools::Long nScrPosY = 0;
+ AddPixelsWhileBackward(nScrPosY, nScrSizeY, nY, 0, nPPTY, &mrDoc, nTabNo);
+ // Original loop ended on last evaluated -1 or if that was 0 even on -2.
+ nY -= (nY == 0 ? 2 : 1);
+ nY = (nPosY-1)-nY;
+ }
+
+ if (nY>0) --nY;
+ return nY;
+}
+
+SCCOL ScViewData::VisibleCellsX( ScHSplitPos eWhichX ) const
+{
+ return CellsAtX( GetPosX( eWhichX ), 1, eWhichX );
+}
+
+SCROW ScViewData::VisibleCellsY( ScVSplitPos eWhichY ) const
+{
+ return CellsAtY( GetPosY( eWhichY ), 1, eWhichY );
+}
+
+SCCOL ScViewData::PrevCellsX( ScHSplitPos eWhichX ) const
+{
+ return CellsAtX( GetPosX( eWhichX ), -1, eWhichX );
+}
+
+SCROW ScViewData::PrevCellsY( ScVSplitPos eWhichY ) const
+{
+ return CellsAtY( GetPosY( eWhichY ), -1, eWhichY );
+}
+
+bool ScViewData::GetMergeSizePixel( SCCOL nX, SCROW nY, tools::Long& rSizeXPix, tools::Long& rSizeYPix ) const
+{
+ const ScMergeAttr* pMerge = mrDoc.GetAttr(nX, nY, nTabNo, ATTR_MERGE);
+ if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
+ {
+ tools::Long nOutWidth = 0;
+ tools::Long nOutHeight = 0;
+ SCCOL nCountX = pMerge->GetColMerge();
+ for (SCCOL i=0; i<nCountX; i++)
+ nOutWidth += ToPixel(mrDoc.GetColWidth(nX + i, nTabNo), nPPTX);
+ SCROW nCountY = pMerge->GetRowMerge();
+
+ for (SCROW nRow = nY; nRow <= nY+nCountY-1; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (mrDoc.RowHidden(nRow, nTabNo, nullptr, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ sal_uInt16 nHeight = mrDoc.GetRowHeight(nRow, nTabNo);
+ nOutHeight += ToPixel(nHeight, nPPTY);
+ }
+
+ rSizeXPix = nOutWidth;
+ rSizeYPix = nOutHeight;
+ return true;
+ }
+ else
+ {
+ rSizeXPix = ToPixel(mrDoc.GetColWidth(nX, nTabNo), nPPTX);
+ rSizeYPix = ToPixel(mrDoc.GetRowHeight(nY, nTabNo), nPPTY);
+ return false;
+ }
+}
+
+bool ScViewData::GetMergeSizePrintTwips(SCCOL nX, SCROW nY, tools::Long& rSizeXTwips, tools::Long& rSizeYTwips) const
+{
+ const ScMergeAttr* pMerge = mrDoc.GetAttr(nX, nY, nTabNo, ATTR_MERGE);
+ SCCOL nCountX = pMerge->GetColMerge();
+ if (!nCountX)
+ nCountX = 1;
+ rSizeXTwips = mrDoc.GetColWidth(nX, nX + nCountX - 1, nTabNo);
+
+ SCROW nCountY = pMerge->GetRowMerge();
+ if (!nCountY)
+ nCountY = 1;
+ rSizeYTwips = mrDoc.GetRowHeight(nY, nY + nCountY - 1, nTabNo);
+
+ return (nCountX > 1 || nCountY > 1);
+}
+
+void ScViewData::GetPosFromPixel( tools::Long nClickX, tools::Long nClickY, ScSplitPos eWhich,
+ SCCOL& rPosX, SCROW& rPosY,
+ bool bTestMerge, bool bRepair, SCTAB nForTab )
+{
+ // special handling of 0 is now in ScViewFunctionSet::SetCursorAtPoint
+
+ if (nForTab == -1)
+ nForTab = nTabNo;
+ bool bForCurTab = (nForTab == nTabNo);
+ if (!bForCurTab && (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size()))))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::GetPosFromPixel : invalid nForTab = " << nForTab);
+ nForTab = nTabNo;
+ bForCurTab = true;
+ }
+
+ ScHSplitPos eHWhich = WhichH(eWhich);
+ ScVSplitPos eVWhich = WhichV(eWhich);
+
+ if (mrDoc.IsLayoutRTL(nForTab))
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // mirror horizontal position
+ if (pView)
+ aScrSize.setWidth( pView->GetGridWidth(eHWhich) );
+ nClickX = aScrSize.Width() - 1 - nClickX;
+ }
+ }
+
+ SCCOL nStartPosX = GetPosX(eHWhich, nForTab);
+ SCROW nStartPosY = GetPosY(eVWhich, nForTab);
+ rPosX = nStartPosX;
+ rPosY = nStartPosY;
+ tools::Long nScrX = 0;
+ tools::Long nScrY = 0;
+
+ if (nClickX > 0)
+ {
+ while (rPosX <= mrDoc.MaxCol() && nClickX >= nScrX)
+ {
+ nScrX += ToPixel(mrDoc.GetColWidth(rPosX, nForTab), nPPTX);
+ ++rPosX;
+ }
+ --rPosX;
+ }
+ else
+ {
+ while ( rPosX>0 && nClickX < nScrX )
+ {
+ --rPosX;
+ nScrX -= ToPixel(mrDoc.GetColWidth(rPosX, nForTab), nPPTX);
+ }
+ }
+
+ if (nClickY > 0)
+ AddPixelsWhile(nScrY, nClickY, rPosY, mrDoc.MaxRow(), nPPTY, &mrDoc, nForTab);
+ else
+ {
+ /* TODO: could need some "SubPixelsWhileBackward" method */
+ while ( rPosY>0 && nClickY < nScrY )
+ {
+ --rPosY;
+ nScrY -= ToPixel(mrDoc.GetRowHeight(rPosY, nForTab), nPPTY);
+ }
+ }
+
+ // cells too big?
+ if ( rPosX == nStartPosX && nClickX > 0 )
+ {
+ if (pView)
+ aScrSize.setWidth( pView->GetGridWidth(eHWhich) );
+ if ( nClickX > aScrSize.Width() )
+ ++rPosX;
+ }
+ if ( rPosY == nStartPosY && nClickY > 0 )
+ {
+ if (pView)
+ aScrSize.setHeight( pView->GetGridHeight(eVWhich) );
+ if ( nClickY > aScrSize.Height() )
+ ++rPosY;
+ }
+
+ rPosX = std::clamp(rPosX, SCCOL(0), mrDoc.MaxCol());
+ rPosY = std::clamp(rPosY, SCROW(0), mrDoc.MaxRow());
+
+ if (!(bTestMerge && bForCurTab))
+ return;
+
+ // public method to adapt position
+ SCCOL nOrigX = rPosX;
+ SCROW nOrigY = rPosY;
+ mrDoc.SkipOverlapped(rPosX, rPosY, nTabNo);
+ bool bHOver = (nOrigX != rPosX);
+ bool bVOver = (nOrigY != rPosY);
+
+ if ( !(bRepair && ( bHOver || bVOver )) )
+ return;
+
+ const ScMergeAttr* pMerge = mrDoc.GetAttr(rPosX, rPosY, nTabNo, ATTR_MERGE);
+ if ( ( bHOver && pMerge->GetColMerge() <= 1 ) ||
+ ( bVOver && pMerge->GetRowMerge() <= 1 ) )
+ {
+ OSL_FAIL("merge error found");
+
+ mrDoc.RemoveFlagsTab(0, 0, mrDoc.MaxCol(), mrDoc.MaxRow(), nTabNo, ScMF::Hor | ScMF::Ver);
+ SCCOL nEndCol = mrDoc.MaxCol();
+ SCROW nEndRow = mrDoc.MaxRow();
+ mrDoc.ExtendMerge(0, 0, nEndCol, nEndRow, nTabNo, true);
+ if (pDocShell)
+ pDocShell->PostPaint(ScRange(0, 0, nTabNo, mrDoc.MaxCol(), mrDoc.MaxRow(), nTabNo),
+ PaintPartFlags::Grid);
+ }
+}
+
+void ScViewData::GetMouseQuadrant( const Point& rClickPos, ScSplitPos eWhich,
+ SCCOL nPosX, SCROW nPosY, bool& rLeft, bool& rTop )
+{
+ bool bLayoutRTL = mrDoc.IsLayoutRTL(nTabNo);
+ tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
+
+ Point aCellStart = GetScrPos( nPosX, nPosY, eWhich, true );
+ tools::Long nSizeX;
+ tools::Long nSizeY;
+ GetMergeSizePixel( nPosX, nPosY, nSizeX, nSizeY );
+ rLeft = ( rClickPos.X() - aCellStart.X() ) * nLayoutSign <= nSizeX / 2;
+ rTop = rClickPos.Y() - aCellStart.Y() <= nSizeY / 2;
+}
+
+void ScViewData::SetPosX( ScHSplitPos eWhich, SCCOL nNewPosX )
+{
+ // in the tiled rendering case, nPosX [the leftmost visible column] must be 0
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ if (nNewPosX != 0 && !bIsTiledRendering)
+ {
+ SCCOL nOldPosX = pThisTab->nPosX[eWhich];
+ tools::Long nTPosX = pThisTab->nTPosX[eWhich];
+ tools::Long nPixPosX = pThisTab->nPixPosX[eWhich];
+ SCCOL i;
+ if ( nNewPosX > nOldPosX )
+ for ( i=nOldPosX; i<nNewPosX; i++ )
+ {
+ tools::Long nThis = mrDoc.GetColWidth(i, nTabNo);
+ nTPosX -= nThis;
+ nPixPosX -= ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTX);
+ }
+ else
+ for ( i=nNewPosX; i<nOldPosX; i++ )
+ {
+ tools::Long nThis = mrDoc.GetColWidth(i, nTabNo);
+ nTPosX += nThis;
+ nPixPosX += ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTX);
+ }
+
+ pThisTab->nPosX[eWhich] = nNewPosX;
+ pThisTab->nTPosX[eWhich] = nTPosX;
+ pThisTab->nMPosX[eWhich] = o3tl::convert(nTPosX, o3tl::Length::twip, o3tl::Length::mm100);
+ pThisTab->nPixPosX[eWhich] = nPixPosX;
+ }
+ else
+ {
+ pThisTab->nPixPosX[eWhich] =
+ pThisTab->nTPosX[eWhich] =
+ pThisTab->nMPosX[eWhich] =
+ pThisTab->nPosX[eWhich] = 0;
+ }
+}
+
+void ScViewData::SetPosY( ScVSplitPos eWhich, SCROW nNewPosY )
+{
+ // in the tiled rendering case, nPosY [the topmost visible row] must be 0
+ bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
+ if (nNewPosY != 0 && !bIsTiledRendering)
+ {
+ SCROW nOldPosY = pThisTab->nPosY[eWhich];
+ tools::Long nTPosY = pThisTab->nTPosY[eWhich];
+ tools::Long nPixPosY = pThisTab->nPixPosY[eWhich];
+ SCROW i, nHeightEndRow;
+ if ( nNewPosY > nOldPosY )
+ for ( i=nOldPosY; i<nNewPosY; i++ )
+ {
+ tools::Long nThis = mrDoc.GetRowHeight(i, nTabNo, nullptr, &nHeightEndRow);
+ SCROW nRows = std::min( nNewPosY, nHeightEndRow + 1) - i;
+ i = nHeightEndRow;
+ nTPosY -= nThis * nRows;
+ nPixPosY -= ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTY) * nRows;
+ }
+ else
+ for ( i=nNewPosY; i<nOldPosY; i++ )
+ {
+ tools::Long nThis = mrDoc.GetRowHeight(i, nTabNo, nullptr, &nHeightEndRow);
+ SCROW nRows = std::min( nOldPosY, nHeightEndRow + 1) - i;
+ i = nHeightEndRow;
+ nTPosY += nThis * nRows;
+ nPixPosY += ToPixel(sal::static_int_cast<sal_uInt16>(nThis), nPPTY) * nRows;
+ }
+
+ pThisTab->nPosY[eWhich] = nNewPosY;
+ pThisTab->nTPosY[eWhich] = nTPosY;
+ pThisTab->nMPosY[eWhich] = o3tl::convert(nTPosY, o3tl::Length::twip, o3tl::Length::mm100);
+ pThisTab->nPixPosY[eWhich] = nPixPosY;
+ }
+ else
+ {
+ pThisTab->nPixPosY[eWhich] =
+ pThisTab->nTPosY[eWhich] =
+ pThisTab->nMPosY[eWhich] =
+ pThisTab->nPosY[eWhich] = 0;
+ }
+}
+
+void ScViewData::RecalcPixPos() // after zoom changes
+{
+ for (sal_uInt16 eWhich=0; eWhich<2; eWhich++)
+ {
+ tools::Long nPixPosX = 0;
+ SCCOL nPosX = pThisTab->nPosX[eWhich];
+ for (SCCOL i=0; i<nPosX; i++)
+ nPixPosX -= ToPixel(mrDoc.GetColWidth(i, nTabNo), nPPTX);
+ pThisTab->nPixPosX[eWhich] = nPixPosX;
+
+ tools::Long nPixPosY = 0;
+ SCROW nPosY = pThisTab->nPosY[eWhich];
+ tools::Long nRowHeight = -1;
+ SCROW nLastSameHeightRow = -1;
+ for (SCROW j=0; j<nPosY; j++)
+ {
+ if(nLastSameHeightRow < j)
+ nRowHeight = ToPixel(mrDoc.GetRowHeight(j, nTabNo, nullptr, &nLastSameHeightRow), nPPTY);
+ nPixPosY -= nRowHeight;
+ }
+ pThisTab->nPixPosY[eWhich] = nPixPosY;
+ }
+}
+
+const MapMode& ScViewData::GetLogicMode( ScSplitPos eWhich )
+{
+ aLogicMode.SetOrigin( Point( pThisTab->nMPosX[WhichH(eWhich)],
+ pThisTab->nMPosY[WhichV(eWhich)] ) );
+ return aLogicMode;
+}
+
+const MapMode& ScViewData::GetLogicMode()
+{
+ aLogicMode.SetOrigin( Point() );
+ return aLogicMode;
+}
+
+void ScViewData::SetScreen( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ sal_uInt16 nTSize;
+ tools::Long nSizePix;
+ tools::Long nScrPosX = 0;
+ tools::Long nScrPosY = 0;
+
+ SetActivePart( SC_SPLIT_BOTTOMLEFT );
+ SetPosX( SC_SPLIT_LEFT, nCol1 );
+ SetPosY( SC_SPLIT_BOTTOM, nRow1 );
+
+ for (nCol=nCol1; nCol<=nCol2; nCol++)
+ {
+ nTSize = mrDoc.GetColWidth(nCol, nTabNo);
+ if (nTSize)
+ {
+ nSizePix = ToPixel( nTSize, nPPTX );
+ nScrPosX += static_cast<sal_uInt16>(nSizePix);
+ }
+ }
+
+ for (nRow=nRow1; nRow<=nRow2; nRow++)
+ {
+ nTSize = mrDoc.GetRowHeight(nRow, nTabNo);
+ if (nTSize)
+ {
+ nSizePix = ToPixel( nTSize, nPPTY );
+ nScrPosY += static_cast<sal_uInt16>(nSizePix);
+ }
+ }
+
+ aScrSize = Size( nScrPosX, nScrPosY );
+}
+
+void ScViewData::SetScreenPos( const Point& rVisAreaStart )
+{
+ tools::Long nSize;
+ tools::Long nTwips;
+ tools::Long nAdd;
+ bool bEnd;
+
+ nSize = 0;
+ nTwips = o3tl::convert(rVisAreaStart.X(), o3tl::Length::mm100, o3tl::Length::twip);
+ if (mrDoc.IsLayoutRTL(nTabNo))
+ nTwips = -nTwips;
+ SCCOL nX1 = 0;
+ bEnd = false;
+ while (!bEnd)
+ {
+ nAdd = static_cast<tools::Long>(mrDoc.GetColWidth(nX1, nTabNo));
+ if (nSize + nAdd <= nTwips + 1 && nX1 < mrDoc.MaxCol())
+ {
+ nSize += nAdd;
+ ++nX1;
+ }
+ else
+ bEnd = true;
+ }
+
+ nSize = 0;
+ nTwips = o3tl::convert(rVisAreaStart.Y(), o3tl::Length::mm100, o3tl::Length::twip);
+ SCROW nY1 = 0;
+ bEnd = false;
+ while (!bEnd)
+ {
+ nAdd = static_cast<tools::Long>(mrDoc.GetRowHeight(nY1, nTabNo));
+ if (nSize + nAdd <= nTwips + 1 && nY1 < mrDoc.MaxRow())
+ {
+ nSize += nAdd;
+ ++nY1;
+ }
+ else
+ bEnd = true;
+ }
+
+ SetActivePart( SC_SPLIT_BOTTOMLEFT );
+ SetPosX( SC_SPLIT_LEFT, nX1 );
+ SetPosY( SC_SPLIT_BOTTOM, nY1 );
+
+ SetCurX( nX1 );
+ SetCurY( nY1 );
+}
+
+void ScViewData::SetScreen( const tools::Rectangle& rVisArea )
+{
+ SetScreenPos( rVisArea.TopLeft() );
+
+ // here without GetOutputFactor(), since it's for the output into a Metafile
+
+ aScrSize = rVisArea.GetSize();
+ aScrSize.setWidth(std::round(o3tl::convert( aScrSize.Width(), o3tl::Length::mm100, o3tl::Length::twip) * ScGlobal::nScreenPPTX));
+ aScrSize.setHeight(std::round(o3tl::convert( aScrSize.Height(), o3tl::Length::mm100, o3tl::Length::twip) * ScGlobal::nScreenPPTY));
+}
+
+ScDocFunc& ScViewData::GetDocFunc() const
+{
+ return pDocShell->GetDocFunc();
+}
+
+SfxBindings& ScViewData::GetBindings()
+{
+ assert(pView && "GetBindings() without ViewShell");
+ return pView->GetViewFrame().GetBindings();
+}
+
+SfxDispatcher& ScViewData::GetDispatcher()
+{
+ assert(pView && "GetDispatcher() without ViewShell");
+ return *pView->GetViewFrame().GetDispatcher();
+}
+
+ScMarkData& ScViewData::GetMarkData()
+{
+ return maMarkData;
+}
+
+ScMarkData& ScViewData::GetHighlightData()
+{
+ return maHighlightData;
+}
+
+const ScMarkData& ScViewData::GetMarkData() const
+{
+ return maMarkData;
+}
+
+weld::Window* ScViewData::GetDialogParent()
+{
+ assert(pView && "GetDialogParent() without ViewShell");
+ return pView->GetDialogParent();
+}
+
+ScGridWindow* ScViewData::GetActiveWin()
+{
+ assert(pView && "GetActiveWin() without View");
+ return pView->GetActiveWin();
+}
+
+const ScGridWindow* ScViewData::GetActiveWin() const
+{
+ assert(pView && "GetActiveWin() without View");
+ return pView->GetActiveWin();
+}
+
+ScDrawView* ScViewData::GetScDrawView()
+{
+ assert(pView && "GetScDrawView() without View");
+ return pView->GetScDrawView();
+}
+
+bool ScViewData::IsMinimized() const
+{
+ assert(pView && "IsMinimized() without View");
+ return pView->IsMinimized();
+}
+
+void ScViewData::UpdateScreenZoom( const Fraction& rNewX, const Fraction& rNewY )
+{
+ Fraction aOldX = GetZoomX();
+ Fraction aOldY = GetZoomY();
+
+ SetZoom( rNewX, rNewY, false );
+
+ Fraction aWidth = GetZoomX();
+ aWidth *= Fraction( aScrSize.Width(),1 );
+ aWidth /= aOldX;
+
+ Fraction aHeight = GetZoomY();
+ aHeight *= Fraction( aScrSize.Height(),1 );
+ aHeight /= aOldY;
+
+ aScrSize.setWidth( static_cast<tools::Long>(aWidth) );
+ aScrSize.setHeight( static_cast<tools::Long>(aHeight) );
+}
+
+void ScViewData::CalcPPT()
+{
+ double nOldPPTX = nPPTX;
+ double nOldPPTY = nPPTY;
+ nPPTX = ScGlobal::nScreenPPTX * static_cast<double>(GetZoomX());
+ if (pDocShell)
+ nPPTX = nPPTX / pDocShell->GetOutputFactor(); // Factor is printer to screen
+ nPPTY = ScGlobal::nScreenPPTY * static_cast<double>(GetZoomY());
+
+ // if detective objects are present,
+ // try to adjust horizontal scale so the most common column width has minimal rounding errors,
+ // to avoid differences between cell and drawing layer output
+
+ if (mrDoc.HasDetectiveObjects(nTabNo))
+ {
+ SCCOL nEndCol = 0;
+ SCROW nDummy = 0;
+ mrDoc.GetTableArea(nTabNo, nEndCol, nDummy);
+ if (nEndCol<20)
+ nEndCol = 20; // same end position as when determining draw scale
+
+ sal_uInt16 nTwips = mrDoc.GetCommonWidth(nEndCol, nTabNo);
+ if ( nTwips )
+ {
+ double fOriginal = nTwips * nPPTX;
+ if ( fOriginal < static_cast<double>(nEndCol) )
+ {
+ // if one column is smaller than the column count,
+ // rounding errors are likely to add up to a whole column.
+
+ double fRounded = ::rtl::math::approxFloor( fOriginal + 0.5 );
+ if ( fRounded > 0.0 )
+ {
+ double fScale = fRounded / fOriginal + 1E-6;
+ if ( fScale >= 0.9 && fScale <= 1.1 )
+ nPPTX *= fScale;
+ }
+ }
+ }
+ }
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nTabCount = maTabData.size();
+ bool bResetWidths = (nPPTX != nOldPPTX);
+ bool bResetHeights = (nPPTY != nOldPPTY);
+ for (SCTAB nTabIdx = 0; nTabIdx < nTabCount; ++nTabIdx)
+ {
+ if (!maTabData[nTabIdx])
+ continue;
+
+ if (bResetWidths)
+ if (auto* pWHelper = GetLOKWidthHelper(nTabIdx))
+ pWHelper->invalidateByPosition(0L);
+
+ if (bResetHeights)
+ if (auto* pHHelper = GetLOKHeightHelper(nTabIdx))
+ pHHelper->invalidateByPosition(0L);
+ }
+}
+
+#define SC_OLD_TABSEP '/'
+#define SC_NEW_TABSEP '+'
+
+void ScViewData::WriteUserData(OUString& rData)
+{
+ // nZoom (until 364v) or nZoom/nPageZoom/bPageMode (from 364w)
+ // nTab
+ // Tab control width
+ // per sheet:
+ // CursorX/CursorY/HSplitMode/VSplitMode/HSplitPos/VSplitPos/SplitActive/
+ // PosX[left]/PosX[right]/PosY[top]/PosY[bottom]
+ // when rows bigger than 8192, "+" instead of "/"
+
+ sal_uInt16 nZoom = static_cast<sal_uInt16>(tools::Long(pThisTab->aZoomY * 100));
+ rData = OUString::number( nZoom ) + "/";
+ nZoom = static_cast<sal_uInt16>(tools::Long(pThisTab->aPageZoomY * 100));
+ rData += OUString::number( nZoom ) + "/";
+ if (bPagebreak)
+ rData += "1";
+ else
+ rData += "0";
+
+ rData += ";" + OUString::number( nTabNo ) + ";" + TAG_TABBARWIDTH +
+ OUString::number( pView->GetTabBarWidth() );
+
+ SCTAB nTabCount = mrDoc.GetTableCount();
+ for (SCTAB i=0; i<nTabCount; i++)
+ {
+ rData += ";"; // Numbering must not get mixed up under any circumstances
+ if (i < static_cast<SCTAB>(maTabData.size()) && maTabData[i])
+ {
+ OUString cTabSep(SC_OLD_TABSEP); // like 3.1
+ if ( maTabData[i]->nCurY > MAXROW_30 ||
+ maTabData[i]->nPosY[0] > MAXROW_30 || maTabData[i]->nPosY[1] > MAXROW_30 ||
+ ( maTabData[i]->eVSplitMode == SC_SPLIT_FIX &&
+ maTabData[i]->nFixPosY > MAXROW_30 ) )
+ {
+ cTabSep = OUStringChar(SC_NEW_TABSEP); // in order to not kill a 3.1-version
+ }
+
+ rData += OUString::number( maTabData[i]->nCurX ) + cTabSep +
+ OUString::number( maTabData[i]->nCurY ) + cTabSep +
+ OUString::number( maTabData[i]->eHSplitMode ) + cTabSep +
+ OUString::number( maTabData[i]->eVSplitMode ) + cTabSep;
+ if ( maTabData[i]->eHSplitMode == SC_SPLIT_FIX )
+ rData += OUString::number( maTabData[i]->nFixPosX );
+ else
+ rData += OUString::number( maTabData[i]->nHSplitPos );
+ rData += cTabSep;
+ if ( maTabData[i]->eVSplitMode == SC_SPLIT_FIX )
+ rData += OUString::number( maTabData[i]->nFixPosY );
+ else
+ rData += OUString::number( maTabData[i]->nVSplitPos );
+ rData += cTabSep +
+ OUString::number( maTabData[i]->eWhichActive ) + cTabSep +
+ OUString::number( maTabData[i]->nPosX[0] ) + cTabSep +
+ OUString::number( maTabData[i]->nPosX[1] ) + cTabSep +
+ OUString::number( maTabData[i]->nPosY[0] ) + cTabSep +
+ OUString::number( maTabData[i]->nPosY[1] );
+ }
+ }
+}
+
+void ScViewData::ReadUserData(std::u16string_view rData)
+{
+ if (rData.empty()) // empty string on "reload"
+ return; // then exit without assertion
+
+ if ( comphelper::string::getTokenCount(rData, ';') <= 2 )
+ {
+ // when reload, in page preview, the preview UserData may have been left intact.
+ // we don't want the zoom from the page preview here.
+ OSL_FAIL("ReadUserData: This is not my data");
+ return;
+ }
+
+ sal_Int32 nMainIdx {0};
+ sal_Int32 nIdx {0};
+
+ std::u16string_view aZoomStr = o3tl::getToken(rData, 0, ';', nMainIdx); // Zoom/PageZoom/Mode
+ sal_Unicode cMode = o3tl::getToken(aZoomStr, 2, '/', nIdx)[0]; // 0 or "0"/"1"
+ SetPagebreakMode( cMode == '1' );
+ // SetPagebreakMode must always be called due to CalcPPT / RecalcPixPos()
+
+ // sheet may have become invalid (for instance last version):
+ SCTAB nNewTab = static_cast<SCTAB>(o3tl::toUInt32(o3tl::getToken(rData, 0, ';', nMainIdx)));
+ if (mrDoc.HasTable(nNewTab))
+ SetTabNo(nNewTab);
+
+ // if available, get tab bar width:
+ const sal_Int32 nMainIdxRef {nMainIdx};
+ std::u16string_view aTabOpt = o3tl::getToken(rData, 0, ';', nMainIdx);
+
+ std::u16string_view aRest;
+ if (o3tl::starts_with(aTabOpt, TAG_TABBARWIDTH, &aRest))
+ {
+ pView->SetTabBarWidth(o3tl::toInt32(aRest));
+ }
+ else
+ {
+ // Tab bar width not specified, token to be processed again
+ nMainIdx = nMainIdxRef;
+ }
+
+ // per sheet
+ SCTAB nPos = 0;
+ while ( nMainIdx>0 )
+ {
+ aTabOpt = o3tl::getToken(rData, 0, ';', nMainIdx);
+ EnsureTabDataSize(nPos + 1);
+ if (!maTabData[nPos])
+ maTabData[nPos].reset(new ScViewDataTable(&mrDoc));
+
+ sal_Unicode cTabSep = 0;
+ if (comphelper::string::getTokenCount(aTabOpt, SC_OLD_TABSEP) >= 11)
+ cTabSep = SC_OLD_TABSEP;
+ else if (comphelper::string::getTokenCount(aTabOpt, SC_NEW_TABSEP) >= 11)
+ cTabSep = SC_NEW_TABSEP;
+ // '+' is only allowed, if we can deal with rows > 8192
+
+ if (cTabSep)
+ {
+ nIdx = 0;
+ maTabData[nPos]->nCurX = mrDoc.SanitizeCol(static_cast<SCCOL>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx))));
+ maTabData[nPos]->nCurY = mrDoc.SanitizeRow(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->eHSplitMode = static_cast<ScSplitMode>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->eVSplitMode = static_cast<ScSplitMode>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+
+ sal_Int32 nTmp = o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx));
+ if ( maTabData[nPos]->eHSplitMode == SC_SPLIT_FIX )
+ {
+ maTabData[nPos]->nFixPosX = mrDoc.SanitizeCol(static_cast<SCCOL>(nTmp));
+ UpdateFixX(nPos);
+ }
+ else
+ maTabData[nPos]->nHSplitPos = nTmp;
+
+ nTmp = o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx));
+ if ( maTabData[nPos]->eVSplitMode == SC_SPLIT_FIX )
+ {
+ maTabData[nPos]->nFixPosY = mrDoc.SanitizeRow(nTmp);
+ UpdateFixY(nPos);
+ }
+ else
+ maTabData[nPos]->nVSplitPos = nTmp;
+
+ maTabData[nPos]->eWhichActive = static_cast<ScSplitPos>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->nPosX[0] = mrDoc.SanitizeCol(static_cast<SCCOL>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx))));
+ maTabData[nPos]->nPosX[1] = mrDoc.SanitizeCol(static_cast<SCCOL>(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx))));
+ maTabData[nPos]->nPosY[0] = mrDoc.SanitizeRow(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+ maTabData[nPos]->nPosY[1] = mrDoc.SanitizeRow(o3tl::toInt32(o3tl::getToken(aTabOpt, 0, cTabSep, nIdx)));
+
+ maTabData[nPos]->eWhichActive = maTabData[nPos]->SanitizeWhichActive();
+ }
+ ++nPos;
+ }
+
+ RecalcPixPos();
+}
+
+void ScViewData::WriteExtOptions( ScExtDocOptions& rDocOpt ) const
+{
+ // *** Fill extended document data for export filters ***
+
+ // document settings
+ ScExtDocSettings& rDocSett = rDocOpt.GetDocSettings();
+
+ // displayed sheet
+ rDocSett.mnDisplTab = GetTabNo();
+
+ // width of the tabbar, relative to frame window width
+ rDocSett.mfTabBarWidth = pView->GetPendingRelTabBarWidth();
+ if( rDocSett.mfTabBarWidth < 0.0 )
+ rDocSett.mfTabBarWidth = ScTabView::GetRelTabBarWidth();
+
+ bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ // sheet settings
+ for( SCTAB nTab = 0; nTab < static_cast<SCTAB>(maTabData.size()); ++nTab )
+ {
+ if( const ScViewDataTable* pViewTab = maTabData[ nTab ].get() )
+ {
+ ScExtTabSettings& rTabSett = rDocOpt.GetOrCreateTabSettings( nTab );
+
+ // split mode
+ ScSplitMode eExHSplit = pViewTab->eHSplitMode;
+ ScSplitMode eExVSplit = pViewTab->eVSplitMode;
+ SCCOL nExFixPosX = pViewTab->nFixPosX;
+ SCROW nExFixPosY = pViewTab->nFixPosY;
+ tools::Long nExHSplitPos = pViewTab->nHSplitPos;
+ tools::Long nExVSplitPos = pViewTab->nVSplitPos;
+
+ if (bLOKActive)
+ {
+ OverrideWithLOKFreeze(eExHSplit, eExVSplit,
+ nExFixPosX, nExFixPosY,
+ nExHSplitPos, nExVSplitPos, nTab);
+ }
+
+ bool bHSplit = eExHSplit != SC_SPLIT_NONE;
+ bool bVSplit = eExVSplit != SC_SPLIT_NONE;
+ bool bRealSplit = (eExHSplit == SC_SPLIT_NORMAL) || (eExVSplit == SC_SPLIT_NORMAL);
+ bool bFrozen = (eExHSplit == SC_SPLIT_FIX) || (eExVSplit == SC_SPLIT_FIX);
+ OSL_ENSURE( !bRealSplit || !bFrozen, "ScViewData::WriteExtOptions - split and freeze in same sheet" );
+ rTabSett.mbFrozenPanes = !bRealSplit && bFrozen;
+
+ // split and freeze position
+ rTabSett.maSplitPos = Point( 0, 0 );
+ rTabSett.maFreezePos.Set( 0, 0, nTab );
+ if( bRealSplit )
+ {
+ Point& rSplitPos = rTabSett.maSplitPos;
+ rSplitPos = Point( bHSplit ? nExHSplitPos : 0, bVSplit ? nExVSplitPos : 0 );
+ rSplitPos = Application::GetDefaultDevice()->PixelToLogic( rSplitPos, MapMode( MapUnit::MapTwip ) );
+ if( pDocShell )
+ rSplitPos.setX( static_cast<tools::Long>(static_cast<double>(rSplitPos.X()) / pDocShell->GetOutputFactor()) );
+ }
+ else if( bFrozen )
+ {
+ if( bHSplit ) rTabSett.maFreezePos.SetCol( nExFixPosX );
+ if( bVSplit ) rTabSett.maFreezePos.SetRow( nExFixPosY );
+ }
+
+ // first visible cell in top-left and additional panes
+ rTabSett.maFirstVis.Set( pViewTab->nPosX[ SC_SPLIT_LEFT ], pViewTab->nPosY[ bVSplit ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM ], nTab );
+ rTabSett.maSecondVis.Set( pViewTab->nPosX[ SC_SPLIT_RIGHT ], pViewTab->nPosY[ SC_SPLIT_BOTTOM ], nTab );
+
+ // active pane
+ switch( pViewTab->eWhichActive )
+ {
+ // no horizontal split -> always use left panes
+ // no vertical split -> always use top panes
+ case SC_SPLIT_TOPLEFT:
+ rTabSett.meActivePane = SCEXT_PANE_TOPLEFT;
+ break;
+ case SC_SPLIT_TOPRIGHT:
+ rTabSett.meActivePane = bHSplit ? SCEXT_PANE_TOPRIGHT : SCEXT_PANE_TOPLEFT;
+ break;
+ case SC_SPLIT_BOTTOMLEFT:
+ rTabSett.meActivePane = bVSplit ? SCEXT_PANE_BOTTOMLEFT : SCEXT_PANE_TOPLEFT;
+ break;
+ case SC_SPLIT_BOTTOMRIGHT:
+ rTabSett.meActivePane = bHSplit ?
+ (bVSplit ? SCEXT_PANE_BOTTOMRIGHT : SCEXT_PANE_TOPRIGHT) :
+ (bVSplit ? SCEXT_PANE_BOTTOMLEFT : SCEXT_PANE_TOPLEFT);
+ break;
+ }
+
+ // cursor position
+ rTabSett.maCursor.Set( pViewTab->nCurX, pViewTab->nCurY, nTab );
+
+ // sheet selection and selected ranges
+ const ScMarkData& rMarkData = GetMarkData();
+ rTabSett.mbSelected = rMarkData.GetTableSelect( nTab );
+ rMarkData.FillRangeListWithMarks( &rTabSett.maSelection, true );
+ rTabSett.mbShowGrid = pViewTab->bShowGrid;
+
+ // view mode and zoom
+ rTabSett.mbPageMode = bPagebreak;
+ rTabSett.mnNormalZoom = static_cast< tools::Long >( pViewTab->aZoomY * Fraction( 100.0 ) );
+ rTabSett.mnPageZoom = static_cast< tools::Long >( pViewTab->aPageZoomY * Fraction( 100.0 ) );
+ }
+ }
+}
+
+void ScViewData::ReadExtOptions( const ScExtDocOptions& rDocOpt )
+{
+ // *** Get extended document data from import filters ***
+
+ if( !rDocOpt.IsChanged() ) return;
+
+ // document settings
+ const ScExtDocSettings& rDocSett = rDocOpt.GetDocSettings();
+
+ // displayed sheet
+ SetTabNo( rDocSett.mnDisplTab );
+
+ /* Width of the tabbar, relative to frame window width. We do not have the
+ correct width of the frame window here -> store in ScTabView, which sets
+ the size in the next resize. */
+ pView->SetPendingRelTabBarWidth( rDocSett.mfTabBarWidth );
+
+ // sheet settings
+ SCTAB nLastTab = rDocOpt.GetLastTab();
+ if (static_cast<SCTAB>(maTabData.size()) <= nLastTab)
+ maTabData.resize(nLastTab+1);
+
+ for( SCTAB nTab = 0; nTab < static_cast<SCTAB>(maTabData.size()); ++nTab )
+ {
+ if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nTab ) )
+ {
+ if( !maTabData[ nTab ] )
+ maTabData[nTab].reset(new ScViewDataTable(&mrDoc));
+
+ const ScExtTabSettings& rTabSett = *pTabSett;
+ ScViewDataTable& rViewTab = *maTabData[ nTab ];
+
+ // split mode initialization
+ bool bFrozen = rTabSett.mbFrozenPanes;
+ bool bHSplit = bFrozen ? (rTabSett.maFreezePos.Col() > 0) : (rTabSett.maSplitPos.X() > 0);
+ bool bVSplit = bFrozen ? (rTabSett.maFreezePos.Row() > 0) : (rTabSett.maSplitPos.Y() > 0);
+
+ // first visible cell of top-left pane and additional panes
+ if (rTabSett.maFirstVis.IsValid())
+ {
+ rViewTab.nPosX[ SC_SPLIT_LEFT ] = rTabSett.maFirstVis.Col();
+ rViewTab.nPosY[ bVSplit ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM ] = rTabSett.maFirstVis.Row();
+ }
+
+ if (rTabSett.maSecondVis.IsValid())
+ {
+ if (bHSplit)
+ rViewTab.nPosX[ SC_SPLIT_RIGHT ] = rTabSett.maSecondVis.Col();
+ if (bVSplit)
+ rViewTab.nPosY[ SC_SPLIT_BOTTOM ] = rTabSett.maSecondVis.Row();
+ }
+
+ // split mode, split and freeze position
+ rViewTab.eHSplitMode = rViewTab.eVSplitMode = SC_SPLIT_NONE;
+ rViewTab.nHSplitPos = rViewTab.nVSplitPos = 0;
+ rViewTab.nFixPosX = 0;
+ rViewTab.nFixPosY = 0;
+ if( bFrozen )
+ {
+ if( bHSplit )
+ {
+ rViewTab.eHSplitMode = SC_SPLIT_FIX;
+ rViewTab.nFixPosX = rTabSett.maFreezePos.Col();
+ UpdateFixX( nTab );
+ }
+ if( bVSplit )
+ {
+ rViewTab.eVSplitMode = SC_SPLIT_FIX;
+ rViewTab.nFixPosY = rTabSett.maFreezePos.Row();
+ UpdateFixY( nTab );
+ }
+ }
+ else
+ {
+ Point aPixel = Application::GetDefaultDevice()->LogicToPixel(
+ rTabSett.maSplitPos, MapMode( MapUnit::MapTwip ) ); //! Zoom?
+ // the test for use of printer metrics for text formatting here
+ // effectively results in the nFactor = 1.0 regardless of the Option setting.
+ if( pDocShell && SC_MOD()->GetInputOptions().GetTextWysiwyg())
+ {
+ double nFactor = pDocShell->GetOutputFactor();
+ aPixel.setX( static_cast<tools::Long>( aPixel.X() * nFactor + 0.5 ) );
+ }
+
+ bHSplit = bHSplit && aPixel.X() > 0;
+ bVSplit = bVSplit && aPixel.Y() > 0;
+ if( bHSplit )
+ {
+ rViewTab.eHSplitMode = SC_SPLIT_NORMAL;
+ rViewTab.nHSplitPos = aPixel.X();
+ }
+ if( bVSplit )
+ {
+ rViewTab.eVSplitMode = SC_SPLIT_NORMAL;
+ rViewTab.nVSplitPos = aPixel.Y();
+ }
+ }
+
+ // active pane
+ ScSplitPos ePos = SC_SPLIT_BOTTOMLEFT;
+ switch( rTabSett.meActivePane )
+ {
+ // no horizontal split -> always use left panes
+ // no vertical split -> always use *bottom* panes
+ case SCEXT_PANE_TOPLEFT:
+ ePos = bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
+ break;
+ case SCEXT_PANE_TOPRIGHT:
+ ePos = bHSplit ?
+ (bVSplit ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT) :
+ (bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT);
+ break;
+ case SCEXT_PANE_BOTTOMLEFT:
+ ePos = SC_SPLIT_BOTTOMLEFT;
+ break;
+ case SCEXT_PANE_BOTTOMRIGHT:
+ ePos = bHSplit ? SC_SPLIT_BOTTOMRIGHT : SC_SPLIT_BOTTOMLEFT;
+ break;
+ }
+ rViewTab.eWhichActive = ePos;
+
+ // cursor position
+ const ScAddress& rCursor = rTabSett.maCursor;
+ if( rCursor.IsValid() )
+ {
+ rViewTab.nCurX = rCursor.Col();
+ rViewTab.nCurY = rCursor.Row();
+ }
+
+ // sheet selection and selected ranges
+ ScMarkData& rMarkData = GetMarkData();
+ rMarkData.SelectTable( nTab, rTabSett.mbSelected );
+
+ // zoom for each sheet
+ if( rTabSett.mnNormalZoom )
+ rViewTab.aZoomX = rViewTab.aZoomY = Fraction( rTabSett.mnNormalZoom, 100 );
+ if( rTabSett.mnPageZoom )
+ rViewTab.aPageZoomX = rViewTab.aPageZoomY = Fraction( rTabSett.mnPageZoom, 100 );
+
+ rViewTab.bShowGrid = rTabSett.mbShowGrid;
+
+ // get some settings from displayed Excel sheet, set at Calc document
+ if( nTab == GetTabNo() )
+ {
+ // view mode and default zoom (for new sheets) from current sheet
+ if( rTabSett.mnNormalZoom )
+ aDefZoomX = aDefZoomY = Fraction( rTabSett.mnNormalZoom, 100L );
+ if( rTabSett.mnPageZoom )
+ aDefPageZoomX = aDefPageZoomY = Fraction( rTabSett.mnPageZoom, 100L );
+ /* #i46820# set pagebreak mode via SetPagebreakMode(), this will
+ update map modes that are needed to draw text correctly. */
+ SetPagebreakMode( rTabSett.mbPageMode );
+ }
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ DeriveLOKFreezeAllSheets();
+
+ // RecalcPixPos or so - also nMPos - also for ReadUserData ??!?!
+}
+
+void ScViewData::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rSettings) const
+{
+ rSettings.realloc(SC_VIEWSETTINGS_COUNT);
+ // + 1, because we have to put the view id in the sequence
+ beans::PropertyValue* pSettings = rSettings.getArray();
+
+ sal_uInt16 nViewID(pView->GetViewFrame().GetCurViewId());
+ pSettings[SC_VIEW_ID].Name = SC_VIEWID;
+ pSettings[SC_VIEW_ID].Value <<= SC_VIEW + OUString::number(nViewID);
+
+ uno::Reference<container::XNameContainer> xNameContainer =
+ document::NamedPropertyValues::create( comphelper::getProcessComponentContext() );
+ for (SCTAB nTab=0; nTab<static_cast<SCTAB>(maTabData.size()); nTab++)
+ {
+ if (maTabData[nTab])
+ {
+ uno::Sequence <beans::PropertyValue> aTableViewSettings;
+ maTabData[nTab]->WriteUserDataSequence(aTableViewSettings, *this, nTab);
+ OUString sTabName;
+ GetDocument().GetName( nTab, sTabName );
+ try
+ {
+ xNameContainer->insertByName(sTabName, uno::Any(aTableViewSettings));
+ }
+ //#101739#; two tables with the same name are possible
+ catch ( container::ElementExistException& )
+ {
+ OSL_FAIL("seems there are two tables with the same name");
+ }
+ catch ( uno::RuntimeException& )
+ {
+ OSL_FAIL("something went wrong");
+ }
+ }
+ }
+ pSettings[SC_TABLE_VIEWSETTINGS].Name = SC_TABLES;
+ pSettings[SC_TABLE_VIEWSETTINGS].Value <<= xNameContainer;
+
+ OUString sName;
+ GetDocument().GetName( nTabNo, sName );
+ pSettings[SC_ACTIVE_TABLE].Name = SC_ACTIVETABLE;
+ pSettings[SC_ACTIVE_TABLE].Value <<= sName;
+ pSettings[SC_HORIZONTAL_SCROLL_BAR_WIDTH].Name = SC_HORIZONTALSCROLLBARWIDTH;
+ pSettings[SC_HORIZONTAL_SCROLL_BAR_WIDTH].Value <<= sal_Int32(pView->GetTabBarWidth());
+ sal_Int32 nZoomValue = tools::Long(pThisTab->aZoomY * 100);
+ sal_Int32 nPageZoomValue = tools::Long(pThisTab->aPageZoomY * 100);
+ pSettings[SC_ZOOM_TYPE].Name = SC_ZOOMTYPE;
+ pSettings[SC_ZOOM_TYPE].Value <<= sal_Int16(pThisTab->eZoomType);
+ pSettings[SC_ZOOM_VALUE].Name = SC_ZOOMVALUE;
+ pSettings[SC_ZOOM_VALUE].Value <<= nZoomValue;
+ pSettings[SC_PAGE_VIEW_ZOOM_VALUE].Name = SC_PAGEVIEWZOOMVALUE;
+ pSettings[SC_PAGE_VIEW_ZOOM_VALUE].Value <<= nPageZoomValue;
+ pSettings[SC_PAGE_BREAK_PREVIEW].Name = SC_SHOWPAGEBREAKPREVIEW;
+ pSettings[SC_PAGE_BREAK_PREVIEW].Value <<= bPagebreak;
+
+ pSettings[SC_SHOWZERO].Name = SC_UNO_SHOWZERO;
+ pSettings[SC_SHOWZERO].Value <<= maOptions.GetOption(VOPT_NULLVALS);
+ pSettings[SC_SHOWNOTES].Name = SC_UNO_SHOWNOTES;
+ pSettings[SC_SHOWNOTES].Value <<= maOptions.GetOption(VOPT_NOTES);
+ pSettings[SC_SHOWFORMULASMARKS].Name = SC_UNO_SHOWFORMULASMARKS;
+ pSettings[SC_SHOWFORMULASMARKS].Value <<= maOptions.GetOption(VOPT_FORMULAS_MARKS);
+ pSettings[SC_SHOWGRID].Name = SC_UNO_SHOWGRID;
+ pSettings[SC_SHOWGRID].Value <<= maOptions.GetOption(VOPT_GRID);
+ pSettings[SC_GRIDCOLOR].Name = SC_UNO_GRIDCOLOR;
+ OUString aColorName;
+ Color aColor = maOptions.GetGridColor(&aColorName);
+ pSettings[SC_GRIDCOLOR].Value <<= aColor;
+ pSettings[SC_SHOWPAGEBR].Name = SC_UNO_SHOWPAGEBR;
+ pSettings[SC_SHOWPAGEBR].Value <<= maOptions.GetOption(VOPT_PAGEBREAKS);
+ pSettings[SC_COLROWHDR].Name = SC_UNO_COLROWHDR;
+ pSettings[SC_COLROWHDR].Value <<= maOptions.GetOption(VOPT_HEADER);
+ pSettings[SC_SHEETTABS].Name = SC_UNO_SHEETTABS;
+ pSettings[SC_SHEETTABS].Value <<= maOptions.GetOption(VOPT_TABCONTROLS);
+ pSettings[SC_OUTLSYMB].Name = SC_UNO_OUTLSYMB;
+ pSettings[SC_OUTLSYMB].Value <<= maOptions.GetOption(VOPT_OUTLINER);
+ pSettings[SC_VALUE_HIGHLIGHTING].Name = SC_UNO_VALUEHIGH;
+ pSettings[SC_VALUE_HIGHLIGHTING].Value <<= maOptions.GetOption(VOPT_SYNTAX);
+ pSettings[SC_FORMULA_BAR_HEIGHT_VALUE].Name = SC_FORMULABARHEIGHT;
+ pSettings[SC_FORMULA_BAR_HEIGHT_VALUE].Value <<= GetFormulaBarLines();;
+
+ const ScGridOptions& aGridOpt = maOptions.GetGridOptions();
+ pSettings[SC_SNAPTORASTER].Name = SC_UNO_SNAPTORASTER;
+ pSettings[SC_SNAPTORASTER].Value <<= aGridOpt.GetUseGridSnap();
+ pSettings[SC_RASTERVIS].Name = SC_UNO_RASTERVIS;
+ pSettings[SC_RASTERVIS].Value <<= aGridOpt.GetGridVisible();
+ pSettings[SC_RASTERRESX].Name = SC_UNO_RASTERRESX;
+ pSettings[SC_RASTERRESX].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDrawX());
+ pSettings[SC_RASTERRESY].Name = SC_UNO_RASTERRESY;
+ pSettings[SC_RASTERRESY].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDrawY());
+ pSettings[SC_RASTERSUBX].Name = SC_UNO_RASTERSUBX;
+ pSettings[SC_RASTERSUBX].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDivisionX());
+ pSettings[SC_RASTERSUBY].Name = SC_UNO_RASTERSUBY;
+ pSettings[SC_RASTERSUBY].Value <<= static_cast<sal_Int32>(aGridOpt.GetFieldDivisionY());
+ pSettings[SC_RASTERSYNC].Name = SC_UNO_RASTERSYNC;
+ pSettings[SC_RASTERSYNC].Value <<= aGridOpt.GetSynchronize();
+
+ // Common SdrModel processing
+ GetDocument().GetDrawLayer()->WriteUserDataSequence(rSettings);
+}
+
+void ScViewData::ReadUserDataSequence(const uno::Sequence <beans::PropertyValue>& rSettings)
+{
+ std::vector<bool> aHasZoomVect( GetDocument().GetTableCount(), false );
+
+ sal_Int32 nTemp32(0);
+ sal_Int16 nTemp16(0);
+ sal_Int16 nFormulaBarLineCount(0);
+ bool bPageMode(false);
+
+ EnsureTabDataSize(GetDocument().GetTableCount());
+
+ for (const auto& rSetting : rSettings)
+ {
+ // SC_VIEWID has to parse and use by mba
+ OUString sName(rSetting.Name);
+ if (sName == SC_TABLES)
+ {
+ uno::Reference<container::XNameContainer> xNameContainer;
+ if ((rSetting.Value >>= xNameContainer) && xNameContainer->hasElements())
+ {
+ const uno::Sequence< OUString > aNames(xNameContainer->getElementNames());
+ for (const OUString& sTabName : aNames)
+ {
+ SCTAB nTab(0);
+ if (GetDocument().GetTable(sTabName, nTab))
+ {
+ uno::Any aAny = xNameContainer->getByName(sTabName);
+ uno::Sequence<beans::PropertyValue> aTabSettings;
+ if (aAny >>= aTabSettings)
+ {
+ EnsureTabDataSize(nTab + 1);
+ if (!maTabData[nTab])
+ maTabData[nTab].reset(new ScViewDataTable(&mrDoc));
+
+ bool bHasZoom = false;
+ maTabData[nTab]->ReadUserDataSequence(aTabSettings, *this, nTab, bHasZoom);
+ aHasZoomVect[nTab] = bHasZoom;
+ }
+ }
+ }
+ }
+ }
+ else if (sName == SC_ACTIVETABLE)
+ {
+ OUString sTabName;
+ if(rSetting.Value >>= sTabName)
+ {
+ SCTAB nTab(0);
+ if (GetDocument().GetTable(sTabName, nTab))
+ nTabNo = nTab;
+ }
+ }
+ else if (sName == SC_HORIZONTALSCROLLBARWIDTH)
+ {
+ if (rSetting.Value >>= nTemp32)
+ pView->SetTabBarWidth(nTemp32);
+ }
+ else if (sName == SC_RELHORIZONTALTABBARWIDTH)
+ {
+ double fWidth = 0.0;
+ if (rSetting.Value >>= fWidth)
+ pView->SetPendingRelTabBarWidth( fWidth );
+ }
+ else if (sName == SC_ZOOMTYPE)
+ {
+ if (rSetting.Value >>= nTemp16)
+ eDefZoomType = SvxZoomType(nTemp16);
+ }
+ else if (sName == SC_ZOOMVALUE)
+ {
+ if (rSetting.Value >>= nTemp32)
+ {
+ Fraction aZoom(nTemp32, 100);
+ aDefZoomX = aDefZoomY = aZoom;
+ }
+ }
+ else if (sName == SC_PAGEVIEWZOOMVALUE)
+ {
+ if (rSetting.Value >>= nTemp32)
+ {
+ Fraction aZoom(nTemp32, 100);
+ aDefPageZoomX = aDefPageZoomY = aZoom;
+ }
+ }
+ else if (sName == SC_FORMULABARHEIGHT)
+ {
+ if (rSetting.Value >>= nFormulaBarLineCount)
+ {
+ SetFormulaBarLines(nFormulaBarLineCount);
+ // Notify formula bar about changed lines
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if (pInputHdl)
+ {
+ ScInputWindow* pInputWin = pInputHdl->GetInputWindow();
+ if (pInputWin)
+ pInputWin->NumLinesChanged();
+ }
+ }
+ }
+ else if (sName == SC_SHOWPAGEBREAKPREVIEW)
+ bPageMode = ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value );
+ else if ( sName == SC_UNO_SHOWZERO )
+ maOptions.SetOption(VOPT_NULLVALS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWNOTES )
+ maOptions.SetOption(VOPT_NOTES, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWFORMULASMARKS )
+ maOptions.SetOption(VOPT_FORMULAS_MARKS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWGRID )
+ maOptions.SetOption(VOPT_GRID, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_GRIDCOLOR )
+ {
+ Color aColor;
+ if (rSetting.Value >>= aColor)
+ maOptions.SetGridColor(aColor, OUString());
+ }
+ else if ( sName == SC_UNO_SHOWPAGEBR )
+ maOptions.SetOption(VOPT_PAGEBREAKS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_COLROWHDR )
+ maOptions.SetOption(VOPT_HEADER, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHEETTABS )
+ maOptions.SetOption(VOPT_TABCONTROLS, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_OUTLSYMB )
+ maOptions.SetOption(VOPT_OUTLINER, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else if ( sName == SC_UNO_SHOWOBJ )
+ {
+ // #i80528# placeholders not supported anymore
+ if ( rSetting.Value >>= nTemp16 )
+ maOptions.SetObjMode(VOBJ_TYPE_OLE, (nTemp16 == 1) ? VOBJ_MODE_HIDE : VOBJ_MODE_SHOW);
+ }
+ else if ( sName == SC_UNO_SHOWCHARTS )
+ {
+ // #i80528# placeholders not supported anymore
+ if ( rSetting.Value >>= nTemp16 )
+ maOptions.SetObjMode(VOBJ_TYPE_CHART, (nTemp16 == 1) ? VOBJ_MODE_HIDE : VOBJ_MODE_SHOW);
+ }
+ else if ( sName == SC_UNO_SHOWDRAW )
+ {
+ // #i80528# placeholders not supported anymore
+ if ( rSetting.Value >>= nTemp16 )
+ maOptions.SetObjMode(VOBJ_TYPE_DRAW, (nTemp16 == 1) ? VOBJ_MODE_HIDE : VOBJ_MODE_SHOW);
+ }
+ else if ( sName == SC_UNO_VALUEHIGH && !comphelper::LibreOfficeKit::isActive() )
+ maOptions.SetOption(VOPT_SYNTAX, ScUnoHelpFunctions::GetBoolFromAny(rSetting.Value));
+ else
+ {
+ ScGridOptions aGridOpt(maOptions.GetGridOptions());
+ if ( sName == SC_UNO_SNAPTORASTER )
+ aGridOpt.SetUseGridSnap( ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value ) );
+ else if ( sName == SC_UNO_RASTERVIS )
+ aGridOpt.SetGridVisible( ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value ) );
+ else if ( sName == SC_UNO_RASTERRESX )
+ aGridOpt.SetFieldDrawX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERRESY )
+ aGridOpt.SetFieldDrawY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERSUBX )
+ aGridOpt.SetFieldDivisionX( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERSUBY )
+ aGridOpt.SetFieldDivisionY( static_cast <sal_uInt32> ( ScUnoHelpFunctions::GetInt32FromAny( rSetting.Value ) ) );
+ else if ( sName == SC_UNO_RASTERSYNC )
+ aGridOpt.SetSynchronize( ScUnoHelpFunctions::GetBoolFromAny( rSetting.Value ) );
+ // Fallback to common SdrModel processing
+ else GetDocument().GetDrawLayer()->ReadUserDataSequenceValue(&rSetting);
+
+ maOptions.SetGridOptions(aGridOpt);
+ }
+ }
+
+ // copy default zoom to sheets where a different one wasn't specified
+ for (SCTAB nZoomTab=0; nZoomTab< static_cast<SCTAB>(maTabData.size()); ++nZoomTab)
+ if (maTabData[nZoomTab] && ( nZoomTab >= static_cast<SCTAB>(aHasZoomVect.size()) || !aHasZoomVect[nZoomTab] ))
+ {
+ maTabData[nZoomTab]->eZoomType = eDefZoomType;
+ maTabData[nZoomTab]->aZoomX = aDefZoomX;
+ maTabData[nZoomTab]->aZoomY = aDefZoomY;
+ maTabData[nZoomTab]->aPageZoomX = aDefPageZoomX;
+ maTabData[nZoomTab]->aPageZoomY = aDefPageZoomY;
+ }
+
+ if (rSettings.hasElements())
+ SetPagebreakMode( bPageMode );
+
+ // #i47426# write view options to document, needed e.g. for Excel export
+ mrDoc.SetViewOptions(maOptions);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ DeriveLOKFreezeAllSheets();
+}
+
+void ScViewData::SetOptions( const ScViewOptions& rOpt )
+{
+ // if visibility of horizontal ScrollBar is changed, TabBar may have to be resized...
+ bool bHScrollChanged = (rOpt.GetOption(VOPT_HSCROLL) != maOptions.GetOption(VOPT_HSCROLL));
+
+ // if graphics are turned on or off, animation has to be started or stopped
+ // graphics are controlled by VOBJ_TYPE_OLE
+ bool bGraphicsChanged = (maOptions.GetObjMode(VOBJ_TYPE_OLE) !=
+ rOpt.GetObjMode(VOBJ_TYPE_OLE) );
+
+ maOptions = rOpt;
+ OSL_ENSURE( pView, "No View" );
+
+ if( pView )
+ {
+ pView->ViewOptionsHasChanged( bHScrollChanged, bGraphicsChanged );
+ }
+}
+
+Point ScViewData::GetMousePosPixel()
+{
+ OSL_ENSURE( pView, "GetMousePosPixel() without View" );
+ return pView->GetMousePosPixel();
+}
+
+void ScViewData::UpdateInputHandler( bool bForce )
+{
+ if (pView)
+ pView->UpdateInputHandler(bForce);
+}
+
+bool ScViewData::IsOle() const
+{
+ return pDocShell && pDocShell->IsOle();
+}
+
+bool ScViewData::UpdateFixX( SCTAB nTab ) // true = value changed
+{
+ if (!ValidTab(nTab)) // Default
+ nTab=nTabNo; // current table
+
+ if (!pView || maTabData[nTab]->eHSplitMode != SC_SPLIT_FIX)
+ return false;
+
+ ScDocument& rLocalDoc = GetDocument();
+ if (!rLocalDoc.HasTable(nTab)) // if called from reload, the sheet may not exist
+ return false;
+
+ SCCOL nFix = maTabData[nTab]->nFixPosX;
+ tools::Long nNewPos = 0;
+ for (SCCOL nX=maTabData[nTab]->nPosX[SC_SPLIT_LEFT]; nX<nFix; nX++)
+ {
+ sal_uInt16 nTSize = rLocalDoc.GetColWidth( nX, nTab );
+ if (nTSize)
+ {
+ tools::Long nPix = ToPixel( nTSize, nPPTX );
+ nNewPos += nPix;
+ }
+ }
+ nNewPos += pView->GetGridOffset().X();
+ if (nNewPos != maTabData[nTab]->nHSplitPos)
+ {
+ maTabData[nTab]->nHSplitPos = nNewPos;
+ if (nTab == nTabNo)
+ RecalcPixPos(); // should not be needed
+ return true;
+ }
+
+ return false;
+}
+
+bool ScViewData::UpdateFixY( SCTAB nTab ) // true = value changed
+{
+ if (!ValidTab(nTab)) // Default
+ nTab=nTabNo; // current table
+
+ if (!pView || maTabData[nTab]->eVSplitMode != SC_SPLIT_FIX)
+ return false;
+
+ ScDocument& rLocalDoc = GetDocument();
+ if (!rLocalDoc.HasTable(nTab)) // if called from reload, the sheet may not exist
+ return false;
+
+ SCROW nFix = maTabData[nTab]->nFixPosY;
+ tools::Long nNewPos = 0;
+ for (SCROW nY=maTabData[nTab]->nPosY[SC_SPLIT_TOP]; nY<nFix; nY++)
+ {
+ sal_uInt16 nTSize = rLocalDoc.GetRowHeight( nY, nTab );
+ if (nTSize)
+ {
+ tools::Long nPix = ToPixel( nTSize, nPPTY );
+ nNewPos += nPix;
+ }
+ }
+ nNewPos += pView->GetGridOffset().Y();
+ if (nNewPos != maTabData[nTab]->nVSplitPos)
+ {
+ maTabData[nTab]->nVSplitPos = nNewPos;
+ if (nTab == nTabNo)
+ RecalcPixPos(); // should not be needed
+ return true;
+ }
+
+ return false;
+}
+
+void ScViewData::UpdateOutlinerFlags( Outliner& rOutl ) const
+{
+ ScDocument& rLocalDoc = GetDocument();
+ bool bOnlineSpell = rLocalDoc.GetDocOptions().IsAutoSpell();
+
+ EEControlBits nCntrl = rOutl.GetControlWord();
+ nCntrl |= EEControlBits::MARKNONURLFIELDS;
+ nCntrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output
+ nCntrl |= EEControlBits::AUTOCORRECT;
+ if( bOnlineSpell )
+ nCntrl |= EEControlBits::ONLINESPELLING;
+ else
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ rOutl.SetControlWord(nCntrl);
+
+ rOutl.SetCalcFieldValueHdl( LINK( SC_MOD(), ScModule, CalcFieldValueHdl ) );
+
+ // don't call GetSpellChecker if online spelling isn't enabled.
+ // The language for AutoCorrect etc. is taken from the pool defaults
+ // (set in ScDocument::UpdateDrawLanguages)
+
+ if ( bOnlineSpell )
+ {
+ css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
+ rOutl.SetSpeller( xXSpellChecker1 );
+ }
+
+ rOutl.SetDefaultHorizontalTextDirection(
+ rLocalDoc.GetEditTextDirection( nTabNo ) );
+}
+
+ScAddress ScViewData::GetCurPos() const
+{
+ return ScAddress( GetCurX(), GetCurY(), GetTabNo() );
+}
+
+void ScViewData::SetRefStart( SCCOL nNewX, SCROW nNewY, SCTAB nNewZ )
+{
+ nRefStartX = nNewX; nRefStartY = nNewY; nRefStartZ = nNewZ;
+}
+
+void ScViewData::SetRefEnd( SCCOL nNewX, SCROW nNewY, SCTAB nNewZ )
+{
+ nRefEndX = nNewX; nRefEndY = nNewY; nRefEndZ = nNewZ;
+}
+
+void ScViewData::AddPixelsWhile( tools::Long & rScrY, tools::Long nEndPixels, SCROW & rPosY,
+ SCROW nEndRow, double nPPTY, const ScDocument * pDoc, SCTAB nTabNo )
+{
+ SCROW nRow = rPosY;
+ while (rScrY <= nEndPixels && nRow <= nEndRow)
+ {
+ SCROW nHeightEndRow;
+ sal_uInt16 nHeight = pDoc->GetRowHeight( nRow, nTabNo, nullptr, &nHeightEndRow);
+ if (nHeightEndRow > nEndRow)
+ nHeightEndRow = nEndRow;
+ if (!nHeight)
+ {
+ if (ValidTab(nTabNo) && nTabNo <= pDoc->GetMaxTableNumber())
+ nRow = nHeightEndRow + 1;
+ else
+ break;
+ }
+ else
+ {
+ SCROW nRows = nHeightEndRow - nRow + 1;
+ sal_Int64 nPixel = ToPixel( nHeight, nPPTY);
+ sal_Int64 nAdd = nPixel * nRows;
+ if (nAdd + rScrY > nEndPixels)
+ {
+ sal_Int64 nDiff = rScrY + nAdd - nEndPixels;
+ nRows -= static_cast<SCROW>(nDiff / nPixel);
+ nAdd = nPixel * nRows;
+ // We're looking for a value that satisfies loop condition.
+ if (nAdd + rScrY <= nEndPixels)
+ {
+ ++nRows;
+ nAdd += nPixel;
+ }
+ }
+ rScrY += static_cast<tools::Long>(nAdd);
+ nRow += nRows;
+ }
+ }
+ if (nRow > rPosY)
+ --nRow;
+ rPosY = nRow;
+}
+
+void ScViewData::AddPixelsWhileBackward( tools::Long & rScrY, tools::Long nEndPixels,
+ SCROW & rPosY, SCROW nStartRow, double nPPTY, const ScDocument * pDoc,
+ SCTAB nTabNo )
+{
+ SCROW nRow = rPosY;
+ while (rScrY <= nEndPixels && nRow >= nStartRow)
+ {
+ SCROW nHeightStartRow;
+ sal_uInt16 nHeight = pDoc->GetRowHeight( nRow, nTabNo, &nHeightStartRow, nullptr);
+ if (nHeightStartRow < nStartRow)
+ nHeightStartRow = nStartRow;
+ if (!nHeight)
+ nRow = nHeightStartRow - 1;
+ else
+ {
+ SCROW nRows = nRow - nHeightStartRow + 1;
+ sal_Int64 nPixel = ToPixel( nHeight, nPPTY);
+ sal_Int64 nAdd = nPixel * nRows;
+ if (nAdd + rScrY > nEndPixels)
+ {
+ sal_Int64 nDiff = nAdd + rScrY - nEndPixels;
+ nRows -= static_cast<SCROW>(nDiff / nPixel);
+ nAdd = nPixel * nRows;
+ // We're looking for a value that satisfies loop condition.
+ if (nAdd + rScrY <= nEndPixels)
+ {
+ ++nRows;
+ nAdd += nPixel;
+ }
+ }
+ rScrY += static_cast<tools::Long>(nAdd);
+ nRow -= nRows;
+ }
+ }
+ if (nRow < rPosY)
+ ++nRow;
+ rPosY = nRow;
+}
+
+SCCOLROW ScViewData::GetLOKSheetFreezeIndex(bool bIsCol) const
+{
+ SCCOLROW nFreezeIndex = bIsCol ? mrDoc.GetLOKFreezeCol(nTabNo) : mrDoc.GetLOKFreezeRow(nTabNo);
+ return nFreezeIndex >= 0 ? nFreezeIndex : 0;
+}
+
+bool ScViewData::SetLOKSheetFreezeIndex(const SCCOLROW nFreezeIndex, bool bIsCol, SCTAB nForTab)
+{
+ if (nForTab == -1)
+ {
+ nForTab = nTabNo;
+ }
+ else if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::SetLOKSheetFreezeIndex : invalid nForTab = " << nForTab);
+ return false;
+ }
+
+ return bIsCol ? mrDoc.SetLOKFreezeCol(static_cast<SCCOL>(nFreezeIndex), nForTab)
+ : mrDoc.SetLOKFreezeRow(static_cast<SCROW>(nFreezeIndex), nForTab);
+}
+
+bool ScViewData::RemoveLOKFreeze()
+{
+ bool colUnfreezed = SetLOKSheetFreezeIndex(0, true);
+ bool rowUnfreezed = SetLOKSheetFreezeIndex(0, false);
+ return colUnfreezed || rowUnfreezed;
+}
+
+void ScViewData::DeriveLOKFreezeAllSheets()
+{
+ SCTAB nMaxTab = static_cast<SCTAB>(maTabData.size()) - 1;
+ for (SCTAB nTab = 0; nTab <= nMaxTab; ++nTab)
+ DeriveLOKFreezeIfNeeded(nTab);
+}
+
+void ScViewData::DeriveLOKFreezeIfNeeded(SCTAB nForTab)
+{
+ if (!ValidTab(nForTab) || (nForTab >= static_cast<SCTAB>(maTabData.size())))
+ {
+ SAL_WARN("sc.viewdata", "ScViewData::DeriveLOKFreezeIfNeeded : invalid nForTab = " << nForTab);
+ return;
+ }
+
+ ScViewDataTable* pViewTable = maTabData[nForTab].get();
+ if (!pViewTable)
+ return;
+
+ bool bConvertToFreezeX = false;
+ bool bConvertToFreezeY = false;
+ SCCOL nFreezeCol = mrDoc.GetLOKFreezeCol(nForTab);
+ SCROW nFreezeRow = mrDoc.GetLOKFreezeRow(nForTab);
+
+ if (nFreezeCol == -1)
+ {
+ ScSplitMode eSplitMode = pViewTable->eHSplitMode;
+ if (eSplitMode == SC_SPLIT_FIX)
+ nFreezeCol = pViewTable->nFixPosX;
+ else if (eSplitMode == SC_SPLIT_NORMAL)
+ bConvertToFreezeX = true;
+ else
+ nFreezeCol = 0;
+ }
+
+ if (nFreezeRow == -1)
+ {
+ ScSplitMode eSplitMode = pViewTable->eVSplitMode;
+ if (eSplitMode == SC_SPLIT_FIX)
+ nFreezeRow = pViewTable->nFixPosY;
+ else if (eSplitMode == SC_SPLIT_NORMAL)
+ bConvertToFreezeY = true;
+ else
+ nFreezeRow = 0;
+ }
+
+ if (bConvertToFreezeX || bConvertToFreezeY)
+ {
+ SCCOL nCol;
+ SCROW nRow;
+ GetPosFromPixel(bConvertToFreezeX ? pViewTable->nHSplitPos : 0,
+ bConvertToFreezeY ? pViewTable->nVSplitPos : 0,
+ SC_SPLIT_BOTTOMLEFT, nCol, nRow,
+ false /* bTestMerge */, false /* bRepair */,
+ nForTab);
+ if (bConvertToFreezeX)
+ nFreezeCol = nCol;
+ if (bConvertToFreezeY)
+ nFreezeRow = nRow;
+ }
+
+ mrDoc.SetLOKFreezeCol(nFreezeCol, nForTab);
+ mrDoc.SetLOKFreezeRow(nFreezeRow, nForTab);
+}
+
+void ScViewData::OverrideWithLOKFreeze(ScSplitMode& eExHSplitMode, ScSplitMode& eExVSplitMode,
+ SCCOL& nExFixPosX, SCROW& nExFixPosY,
+ tools::Long& nExHSplitPos, tools::Long& nExVSplitPos, SCTAB nForTab) const
+{
+ SCCOL nFreezeCol = mrDoc.GetLOKFreezeCol(nForTab);
+ SCROW nFreezeRow = mrDoc.GetLOKFreezeRow(nForTab);
+
+ bool bConvertToScrPosX = false;
+ bool bConvertToScrPosY = false;
+
+ if (nFreezeCol >= 0)
+ {
+ if (eExHSplitMode == SC_SPLIT_NONE)
+ eExHSplitMode = SC_SPLIT_FIX;
+
+ if (eExHSplitMode == SC_SPLIT_FIX)
+ {
+ nExFixPosX = nFreezeCol;
+ pThisTab->nPosX[SC_SPLIT_RIGHT] = nFreezeCol;
+ }
+ else
+ bConvertToScrPosX = true;
+ }
+
+ if (nFreezeRow >= 0)
+ {
+ if (eExVSplitMode == SC_SPLIT_NONE)
+ eExVSplitMode = SC_SPLIT_FIX;
+
+ if (eExVSplitMode == SC_SPLIT_FIX)
+ {
+ nExFixPosY = nFreezeRow;
+ pThisTab->nPosY[SC_SPLIT_BOTTOM] = nFreezeRow;
+ }
+ else
+ bConvertToScrPosY = true;
+ }
+
+ if (bConvertToScrPosX || bConvertToScrPosY)
+ {
+ Point aExSplitPos = GetScrPos(nFreezeCol, nFreezeRow, SC_SPLIT_BOTTOMLEFT, true, nForTab);
+ if (bConvertToScrPosX)
+ nExHSplitPos = aExSplitPos.X();
+ if (bConvertToScrPosY)
+ nExVSplitPos = aExSplitPos.Y();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun2.cxx b/sc/source/ui/view/viewfun2.cxx
new file mode 100644
index 0000000000..224bb722e0
--- /dev/null
+++ b/sc/source/ui/view/viewfun2.cxx
@@ -0,0 +1,3487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source eCode Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <sfx2/app.hxx>
+#include <sfx2/request.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/srchdlg.hxx>
+#include <svx/svdview.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <osl/diagnose.h>
+
+#include <viewfunc.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <attrib.hxx>
+#include <autoform.hxx>
+#include <formulacell.hxx>
+#include <cellmergeoption.hxx>
+#include <compiler.hxx>
+#include <docfunc.hxx>
+#include <docpool.hxx>
+#include <docsh.hxx>
+#include <docoptio.hxx>
+#include <global.hxx>
+#include <patattr.hxx>
+#include <printfun.hxx>
+#include <refundo.hxx>
+#include <table.hxx>
+#include <tablink.hxx>
+#include <tabvwsh.hxx>
+#include <uiitems.hxx>
+#include <undoblk.hxx>
+#include <undotab.hxx>
+#include <sizedev.hxx>
+#include <editable.hxx>
+#include <docuno.hxx>
+#include <charthelper.hxx>
+#include <tabbgcolor.hxx>
+#include <clipparam.hxx>
+#include <prnsave.hxx>
+#include <searchresults.hxx>
+#include <tokenarray.hxx>
+#include <rowheightcontext.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <mergecellsdialog.hxx>
+#include <sheetevents.hxx>
+#include <columnspanset.hxx>
+
+#include <vector>
+#include <memory>
+#include <boost/property_tree/json_parser.hpp>
+#include <tools/json_writer.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+
+using namespace com::sun::star;
+using ::editeng::SvxBorderLine;
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+}
+
+using ::std::vector;
+using ::std::unique_ptr;
+
+bool ScViewFunc::AdjustBlockHeight( bool bPaint, ScMarkData* pMarkData )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if (!pMarkData)
+ pMarkData = &GetViewData().GetMarkData();
+
+ ScDocument& rDoc = pDocSh->GetDocument();
+ std::vector<sc::ColRowSpan> aMarkedRows = pMarkData->GetMarkedRowSpans();
+
+ if (aMarkedRows.empty())
+ {
+ SCROW nCurRow = GetViewData().GetCurY();
+ aMarkedRows.emplace_back(nCurRow, nCurRow);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SCCOLROW nStart = aMarkedRows[0].mnStart;
+ OnLOKSetWidthOrHeight(nStart, /*width: */ false);
+ }
+
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
+ bool bAnyChanged = false;
+ for (const SCTAB& nTab : *pMarkData)
+ {
+ bool bChanged = false;
+ SCROW nPaintY = 0;
+ for (const auto& rRow : aMarkedRows)
+ {
+ SCROW nStartNo = rRow.mnStart;
+ SCROW nEndNo = rRow.mnEnd;
+ ScAddress aTopLeft(0, nStartNo, nTab);
+ rDoc.UpdateScriptTypes(aTopLeft, rDoc.GetSheetLimits().GetMaxColCount(), nEndNo-nStartNo+1);
+ if (rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true))
+ {
+ if (!bChanged)
+ nPaintY = nStartNo;
+ bAnyChanged = bChanged = true;
+ }
+ }
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ rDoc.SetDrawPageSize(nTab);
+ if ( bPaint && bChanged )
+ pDocSh->PostPaint( 0, nPaintY, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+
+ if ( bPaint && bAnyChanged )
+ pDocSh->UpdateOle(GetViewData());
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(),
+ false /* bColumns */, true /* bRows */,
+ true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
+ false /* bGroups */, nTab);
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, nTab);
+ }
+
+ return bAnyChanged;
+}
+
+bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, bool bApi )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OnLOKSetWidthOrHeight(nStartRow, /*width: */ false);
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+ sal_uInt16 nOldPixel = 0;
+ if (nStartRow == nEndRow)
+ nOldPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
+ bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
+
+ // tdf#76183: recalculate objects' positions
+ if (bChanged)
+ rDoc.SetDrawPageSize(nTab);
+
+ if (bChanged && ( nStartRow == nEndRow ))
+ {
+ sal_uInt16 nNewPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
+ if ( nNewPixel == nOldPixel )
+ bChanged = false;
+ }
+
+ if ( bChanged )
+ pDocSh->PostPaint( 0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
+ PaintPartFlags::Grid | PaintPartFlags::Left );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
+ GetViewData().GetViewShell(),
+ false /* bColumns */, true /* bRows */,
+ true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
+ false /* bGroups */, nTab);
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
+ }
+
+ return bChanged;
+}
+
+namespace {
+
+enum ScAutoSum
+{
+ ScAutoSumNone = 0,
+ ScAutoSumData,
+ ScAutoSumSum,
+ ScAutoSumAverage,
+ ScAutoSumMax,
+ ScAutoSumMin,
+ ScAutoSumCount,
+ ScAutoSumCountA,
+ ScAutoSumProduct,
+ ScAutoSumStDev,
+ ScAutoSumStDevP,
+ ScAutoSumVar,
+ ScAutoSumVarP,
+ ScAutoSumEnd
+};
+
+}
+
+static ScAutoSum lcl_IsAutoSumData( ScDocument& rDoc, SCCOL nCol, SCROW nRow,
+ SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
+{
+ ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
+ if (aCell.hasNumeric())
+ {
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScAutoSum val = ScAutoSumNone;
+ ScTokenArray* pCode = aCell.getFormula()->GetCode();
+ if ( pCode )
+ {
+ switch( pCode->GetOuterFuncOpCode() )
+ {
+ case ocSum : val = ScAutoSumSum;
+ break;
+ case ocAverage : val = ScAutoSumAverage;
+ break;
+ case ocMax : val = ScAutoSumMax;
+ break;
+ case ocMin : val = ScAutoSumMin;
+ break;
+ case ocCount : val = ScAutoSumCount;
+ break;
+ case ocCount2 : val = ScAutoSumCountA;
+ break;
+ case ocProduct : val = ScAutoSumProduct;
+ break;
+ case ocStDev : val = ScAutoSumStDev;
+ break;
+ case ocStDevP : val = ScAutoSumStDevP;
+ break;
+ case ocVar : val = ScAutoSumVar;
+ break;
+ case ocVarP : val = ScAutoSumVarP;
+ break;
+ default :
+ break;
+ }
+ if ( pCode->GetAdjacentExtendOfOuterFuncRefs( nExtend,
+ ScAddress( nCol, nRow, nTab ), eDir ) )
+ return val;
+ }
+ }
+ return ScAutoSumData;
+ }
+ return ScAutoSumNone;
+}
+
+#define SC_AUTOSUM_MAXCOUNT 20
+
+static ScAutoSum lcl_SeekAutoSumData( ScDocument& rDoc, SCCOL& nCol, SCROW& nRow,
+ SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
+{
+ sal_uInt16 nCount = 0;
+ while (nCount < SC_AUTOSUM_MAXCOUNT)
+ {
+ if ( eDir == DIR_TOP )
+ {
+ if (nRow > 0)
+ --nRow;
+ else
+ return ScAutoSumNone;
+ }
+ else
+ {
+ if (nCol > 0)
+ --nCol;
+ else
+ return ScAutoSumNone;
+ }
+ ScAutoSum eSum;
+ if ( (eSum = lcl_IsAutoSumData(
+ rDoc, nCol, nRow, nTab, eDir, nExtend )) != ScAutoSumNone )
+ return eSum;
+ ++nCount;
+ }
+ return ScAutoSumNone;
+}
+
+#undef SC_AUTOSUM_MAXCOUNT
+
+static bool lcl_FindNextSumEntryInColumn( ScDocument& rDoc, SCCOL nCol, SCROW& nRow,
+ SCTAB nTab, SCCOLROW& nExtend, SCROW nMinRow )
+{
+ const SCROW nTmp = nRow;
+ ScAutoSum eSkip = ScAutoSumNone;
+ for (;;)
+ {
+ eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend );
+ if (eSkip != ScAutoSumData || nRow <= nMinRow )
+ break;
+ --nRow;
+ }
+ return eSkip >= ScAutoSumSum && nRow < nTmp;
+}
+
+static bool lcl_FindNextSumEntryInRow( ScDocument& rDoc, SCCOL& nCol, SCROW nRow,
+ SCTAB nTab, SCCOLROW& nExtend, SCCOL nMinCol )
+{
+ const SCCOL nTmp = nCol;
+ ScAutoSum eSkip = ScAutoSumNone;
+ for (;;)
+ {
+ eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend );
+ if (eSkip != ScAutoSumData || nCol <= nMinCol )
+ break;
+ --nCol;
+ }
+ return eSkip >= ScAutoSumSum && nCol < nTmp;
+}
+
+static ScAutoSum lcl_GetAutoSumForColumnRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
+{
+ const ScAddress aStart = rRange.aStart;
+ const ScAddress aEnd = rRange.aEnd;
+ if ( aStart.Col() != aEnd.Col() )
+ {
+ return ScAutoSumNone;
+ }
+
+ const SCTAB nTab = aEnd.Tab();
+ const SCCOL nCol = aEnd.Col();
+ SCROW nEndRow = aEnd.Row();
+ SCROW nStartRow = nEndRow;
+ SCCOLROW nExtend = 0;
+ ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nCol, nEndRow, nTab, DIR_TOP, nExtend /*out*/ );
+
+ if ( eSum >= ScAutoSumSum )
+ {
+ bool bContinue = false;
+ do
+ {
+ rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
+ nEndRow = static_cast< SCROW >( nExtend );
+ bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, aStart.Row() );
+ if ( bContinue )
+ {
+ nStartRow = nEndRow;
+ }
+ } while ( bContinue );
+ }
+ else
+ {
+ while ( nStartRow > aStart.Row() )
+ {
+ eSum = lcl_IsAutoSumData( rDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ );
+ if (eSum >= ScAutoSumSum )
+ break;
+ --nStartRow;
+ }
+ rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
+ if (eSum == ScAutoSumNone)
+ eSum = ScAutoSumData;
+ }
+
+ return eSum;
+}
+
+static ScAutoSum lcl_GetAutoSumForRowRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
+{
+ const ScAddress aStart = rRange.aStart;
+ const ScAddress aEnd = rRange.aEnd;
+ if ( aStart.Row() != aEnd.Row() )
+ {
+ return ScAutoSumNone;
+ }
+
+ const SCTAB nTab = aEnd.Tab();
+ const SCROW nRow = aEnd.Row();
+ SCCOL nEndCol = aEnd.Col();
+ SCCOL nStartCol = nEndCol;
+ SCCOLROW nExtend = 0;
+ ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nEndCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
+
+ if ( eSum >= ScAutoSumSum )
+ {
+ bool bContinue = false;
+ do
+ {
+ rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
+ nEndCol = static_cast< SCCOL >( nExtend );
+ bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, aStart.Col() );
+ if ( bContinue )
+ {
+ nStartCol = nEndCol;
+ }
+ } while ( bContinue );
+ }
+ else
+ {
+ while ( nStartCol > aStart.Col() )
+ {
+ eSum = lcl_IsAutoSumData( rDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
+ if (eSum >= ScAutoSumSum )
+ break;
+ --nStartCol;
+ }
+ rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
+ if (eSum == ScAutoSumNone)
+ eSum = ScAutoSumData;
+ }
+
+ return eSum;
+}
+
+static sal_Int8 GetSubTotal( const OpCode eCode )
+{
+ sal_Int8 val;
+ switch ( eCode )
+ {
+ case ocSum : val = 9;
+ break;
+ case ocAverage : val = 1;
+ break;
+ case ocMax : val = 4;
+ break;
+ case ocMin : val = 5;
+ break;
+ case ocCount : val = 2;
+ break;
+ case ocCount2 : val = 3;
+ break;
+ case ocProduct : val = 6;
+ break;
+ case ocStDev : val = 7;
+ break;
+ case ocStDevP : val = 8;
+ break;
+ case ocVar : val = 10;
+ break;
+ case ocVarP : val = 11;
+ break;
+ default : val = 9;
+ }
+
+ return val;
+}
+
+bool ScViewFunc::GetAutoSumArea( ScRangeList& rRangeList )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+
+ SCCOL nStartCol = nCol;
+ SCROW nStartRow = nRow;
+ SCCOL nEndCol = nCol;
+ SCROW nEndRow = nRow;
+ SCCOL nSeekCol = nCol;
+ SCROW nSeekRow = nRow;
+ SCCOLROW nExtend; // will become valid via reference for ScAutoSumSum
+
+ bool bCol = false;
+ bool bRow = false;
+
+ ScAutoSum eSum;
+ if ( nRow != 0
+ && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
+ DIR_TOP, nExtend /*out*/ )) == ScAutoSumData )
+ && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
+ DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
+ )
+ {
+ bRow = true;
+ nSeekRow = nRow - 1;
+ }
+ else if ( nCol != 0 && (eSum = lcl_IsAutoSumData( rDoc, nCol-1, nRow, nTab,
+ DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
+ {
+ bCol = true;
+ nSeekCol = nCol - 1;
+ }
+ else if ( (eSum = lcl_SeekAutoSumData( rDoc, nCol, nSeekRow, nTab, DIR_TOP, nExtend /*out*/ )) != ScAutoSumNone )
+ bRow = true;
+ else if (( eSum = lcl_SeekAutoSumData( rDoc, nSeekCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) != ScAutoSumNone )
+ bCol = true;
+
+ if ( bCol || bRow )
+ {
+ if ( bRow )
+ {
+ nStartRow = nSeekRow; // nSeekRow might be adjusted via reference
+ if ( eSum >= ScAutoSumSum && eSum < ScAutoSumEnd )
+ nEndRow = nStartRow; // only sum sums
+ else
+ nEndRow = nRow - 1; // maybe extend data area at bottom
+ }
+ else
+ {
+ nStartCol = nSeekCol; // nSeekCol might be adjusted via reference
+ if ( eSum >= ScAutoSumSum )
+ nEndCol = nStartCol; // only sum sums
+ else
+ nEndCol = nCol - 1; // maybe extend data area to the right
+ }
+ bool bContinue = false;
+ do
+ {
+ if ( eSum == ScAutoSumData )
+ {
+ if ( bRow )
+ {
+ while ( nStartRow != 0 && lcl_IsAutoSumData( rDoc, nCol,
+ nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) == eSum )
+ --nStartRow;
+ }
+ else
+ {
+ while ( nStartCol != 0 && lcl_IsAutoSumData( rDoc, nStartCol-1,
+ nRow, nTab, DIR_LEFT, nExtend /*out*/ ) == eSum )
+ --nStartCol;
+ }
+ }
+ rRangeList.push_back(
+ ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ) );
+ if ( eSum >= ScAutoSumSum )
+ {
+ if ( bRow )
+ {
+ nEndRow = static_cast< SCROW >( nExtend );
+ bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, 0 );
+ if ( bContinue )
+ {
+ nStartRow = nEndRow;
+ }
+ }
+ else
+ {
+ nEndCol = static_cast< SCCOL >( nExtend );
+ bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, 0 );
+ if ( bContinue )
+ {
+ nStartCol = nEndCol;
+ }
+ }
+ }
+ } while ( bContinue );
+ return true;
+ }
+ return false;
+}
+
+void ScViewFunc::EnterAutoSum(const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode)
+{
+ OUString aFormula = GetAutoSumFormula( rRangeList, bSubTotal, rAddr , eCode);
+ EnterBlock( aFormula, nullptr );
+}
+
+bool ScViewFunc::AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue , const OpCode eCode)
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ const SCTAB nTab = rRange.aStart.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ const SCCOL nEndCol = rRange.aEnd.Col();
+ const SCROW nEndRow = rRange.aEnd.Row();
+ SCCOLROW nExtend = 0; // out parameter for lcl_IsAutoSumData
+
+ // ignore rows at the top of the given range which don't contain autosum data
+ bool bRowData = false;
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) != ScAutoSumNone )
+ {
+ bRowData = true;
+ break;
+ }
+ }
+ if ( bRowData )
+ {
+ nStartRow = nRow;
+ break;
+ }
+ }
+ if ( !bRowData )
+ {
+ return false;
+ }
+
+ // ignore columns at the left of the given range which don't contain autosum data
+ bool bColData = false;
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) != ScAutoSumNone )
+ {
+ bColData = true;
+ break;
+ }
+ }
+ if ( bColData )
+ {
+ nStartCol = nCol;
+ break;
+ }
+ }
+ if ( !bColData )
+ {
+ return false;
+ }
+
+ const bool bEndRowEmpty = rDoc.IsBlockEmpty( nStartCol, nEndRow, nEndCol, nEndRow, nTab );
+ const bool bEndColEmpty = rDoc.IsBlockEmpty( nEndCol, nStartRow, nEndCol, nEndRow, nTab );
+ bool bRow = ( nStartRow != nEndRow ) && ( bEndRowEmpty || !bEndColEmpty );
+ bool bCol = ( nStartCol != nEndCol ) && ( bEndColEmpty || nStartRow == nEndRow );
+
+ // find an empty row for entering the result
+ SCROW nInsRow = nEndRow;
+ if ( bRow && !bEndRowEmpty )
+ {
+ if ( nInsRow < rDoc.MaxRow() )
+ {
+ ++nInsRow;
+ while ( !rDoc.IsBlockEmpty( nStartCol, nInsRow, nEndCol, nInsRow, nTab ) )
+ {
+ if ( nInsRow < rDoc.MaxRow() )
+ {
+ ++nInsRow;
+ }
+ else
+ {
+ bRow = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ bRow = false;
+ }
+ }
+
+ // find an empty column for entering the result
+ SCCOL nInsCol = nEndCol;
+ if ( bCol && !bEndColEmpty )
+ {
+ if ( nInsCol < rDoc.MaxCol() )
+ {
+ ++nInsCol;
+ while ( !rDoc.IsBlockEmpty( nInsCol, nStartRow, nInsCol, nEndRow, nTab ) )
+ {
+ if ( nInsCol < rDoc.MaxCol() )
+ {
+ ++nInsCol;
+ }
+ else
+ {
+ bCol = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ bCol = false;
+ }
+ }
+
+ if ( !bRow && !bCol )
+ {
+ return false;
+ }
+
+ SCCOL nMarkEndCol = nEndCol;
+ SCROW nMarkEndRow = nEndRow;
+ ScAutoSum eSum = ScAutoSumNone;
+ SCROW nColSums = 0;
+ SCCOL nRowSums = 0;
+ SCROW nColSumsStartRow = 0;
+ SCCOL nRowSumsStartCol = 0;
+
+ if ( bRow )
+ {
+ // calculate the row sums for all columns of the given range
+
+ SCROW nSumEndRow = nEndRow;
+
+ if ( bEndRowEmpty )
+ {
+ // the last row of the given range is empty;
+ // don't take into account for calculating the autosum
+ --nSumEndRow;
+ }
+ else
+ {
+ // increase mark range
+ ++nMarkEndRow;
+ }
+
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ if ( !rDoc.IsBlockEmpty( nCol, nStartRow, nCol, nSumEndRow, nTab ) )
+ {
+ ScRangeList aRangeList;
+ // Include the originally selected start row.
+ const ScRange aRange( nCol, rRange.aStart.Row(), nTab, nCol, nSumEndRow, nTab );
+ if ( (eSum = lcl_GetAutoSumForColumnRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
+ {
+ if (++nRowSums == 1)
+ nRowSumsStartCol = aRangeList[0].aStart.Col();
+ const OUString aFormula = GetAutoSumFormula(
+ aRangeList, bSubTotal, ScAddress(nCol, nInsRow, nTab), eCode);
+ EnterData( nCol, nInsRow, nTab, aFormula );
+ }
+ }
+ }
+ }
+
+ if ( bCol )
+ {
+ // calculate the column sums for all rows of the given range
+
+ SCCOL nSumEndCol = nEndCol;
+
+ if ( bEndColEmpty )
+ {
+ // the last column of the given range is empty;
+ // don't take into account for calculating the autosum
+ --nSumEndCol;
+ }
+ else
+ {
+ // increase mark range
+ ++nMarkEndCol;
+ }
+
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ if ( !rDoc.IsBlockEmpty( nStartCol, nRow, nSumEndCol, nRow, nTab ) )
+ {
+ ScRangeList aRangeList;
+ // Include the originally selected start column.
+ const ScRange aRange( rRange.aStart.Col(), nRow, nTab, nSumEndCol, nRow, nTab );
+ if ( (eSum = lcl_GetAutoSumForRowRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
+ {
+ if (++nColSums == 1)
+ nColSumsStartRow = aRangeList[0].aStart.Row();
+ const OUString aFormula = GetAutoSumFormula( aRangeList, bSubTotal, ScAddress(nInsCol, nRow, nTab), eCode );
+ EnterData( nInsCol, nRow, nTab, aFormula );
+ }
+ }
+ }
+ }
+
+ // Set new mark range and cursor position.
+ // For sum of sums (and data until sum) mark the actual resulting range if
+ // there is only one, or the data range if more than one. Otherwise use the
+ // original selection. All extended by end column/row where the sum is put.
+ const ScRange aMarkRange(
+ (eSum >= ScAutoSumSum ?
+ (nRowSums == 1 ? nRowSumsStartCol : nStartCol) :
+ rRange.aStart.Col()),
+ (eSum >= ScAutoSumSum ?
+ (nColSums == 1 ? nColSumsStartRow : nStartRow) :
+ rRange.aStart.Row()),
+ nTab, nMarkEndCol, nMarkEndRow, nTab );
+ MarkRange( aMarkRange, false, bContinue );
+ if ( bSetCursor )
+ {
+ SetCursor( nMarkEndCol, nMarkEndRow );
+ }
+
+ return true;
+}
+
+OUString ScViewFunc::GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr , const OpCode eCode)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScTokenArray aArray(rDoc);
+
+ aArray.AddOpCode(bSubTotal ? ocSubTotal : eCode);
+ aArray.AddOpCode(ocOpen);
+
+ if (bSubTotal)
+ {
+ aArray.AddDouble( GetSubTotal( eCode ) );
+ aArray.AddOpCode(ocSep);
+ }
+
+ if(!rRangeList.empty())
+ {
+ ScRangeList aRangeList = rRangeList;
+ size_t ListSize = aRangeList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ const ScRange & r = aRangeList[i];
+ if (i != 0)
+ aArray.AddOpCode(ocSep);
+ ScComplexRefData aRef;
+ aRef.InitRangeRel(rDoc, r, rAddr);
+ aArray.AddDoubleReference(aRef);
+ }
+ }
+
+ aArray.AddOpCode(ocClose);
+
+ ScCompiler aComp(rDoc, rAddr, aArray, rDoc.GetGrammar());
+ OUStringBuffer aBuf;
+ aComp.CreateStringFromTokenArray(aBuf);
+ aBuf.insert(0, "=");
+ return aBuf.makeStringAndClear();
+}
+
+void ScViewFunc::EnterBlock( const OUString& rString, const EditTextObject* pData )
+{
+ // test for multi selection
+
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMultiMarked() )
+ {
+ rMark.MarkToSimple();
+ if ( rMark.IsMultiMarked() )
+ { // "Insert into multi selection not possible"
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+
+ // insert into single cell
+ if ( pData )
+ EnterData(nCol, nRow, nTab, *pData);
+ else
+ EnterData( nCol, nRow, nTab, rString );
+ return;
+ }
+ }
+
+ if (GetViewData().SelectionForbidsCellFill())
+ {
+ PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
+ return;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ OUString aNewStr = rString;
+ if ( pData )
+ {
+ const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
+ aEngine.SetTextCurrentDefaults(*pData);
+
+ ScEditAttrTester aTester( &aEngine );
+ if (!aTester.NeedsObject())
+ {
+ aNewStr = aEngine.GetText();
+ pData = nullptr;
+ }
+ }
+
+ // Insert via PasteFromClip
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ ScAddress aPos( nCol, nRow, nTab );
+
+ ScDocumentUniquePtr pInsDoc(new ScDocument( SCDOCMODE_CLIP ));
+ pInsDoc->ResetClip( &rDoc, nTab );
+
+ if (aNewStr[0] == '=') // Formula ?
+ {
+ // SetString not possible, because in Clipboard-Documents nothing will be compiled!
+ pInsDoc->SetFormulaCell(aPos, new ScFormulaCell(rDoc, aPos, aNewStr));
+ }
+ else if ( pData )
+ {
+ // A copy of pData will be stored.
+ pInsDoc->SetEditText(aPos, *pData, rDoc.GetEditPool());
+ }
+ else
+ pInsDoc->SetString( nCol, nRow, nTab, aNewStr );
+
+ pInsDoc->SetClipArea( ScRange(aPos) );
+ // insert Block, with Undo etc.
+ if ( !PasteFromClip( InsertDeleteFlags::CONTENTS, pInsDoc.get(), ScPasteFunc::NONE, false, false,
+ false, INS_NONE, InsertDeleteFlags::ATTRIB ) )
+ return;
+
+ const SfxUInt32Item* pItem = pInsDoc->GetAttr(
+ nCol, nRow, nTab, ATTR_VALUE_FORMAT );
+ if ( pItem )
+ { // set number format if incompatible
+ // MarkData was already MarkToSimple'ed in PasteFromClip
+ const ScRange& aRange = rMark.GetMarkArea();
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( *pItem );
+ SvNumFormatType nNewType = rDoc.GetFormatTable()->GetType( pItem->GetValue() );
+ rDoc.ApplyPatternIfNumberformatIncompatible( aRange, rMark,
+ aPattern, nNewType );
+ }
+}
+
+// manual page break
+
+void ScViewFunc::InsertPageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
+ bool bSetModified )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCursor;
+ if (pPos)
+ aCursor = *pPos;
+ else
+ aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
+
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ InsertPageBreak( bColumn, aCursor, bRecord, bSetModified );
+
+ if ( bSuccess && bSetModified )
+ UpdatePageBreakData( true ); // for PageBreak-Mode
+}
+
+void ScViewFunc::DeletePageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
+ bool bSetModified )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCursor;
+ if (pPos)
+ aCursor = *pPos;
+ else
+ aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
+
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ RemovePageBreak( bColumn, aCursor, bRecord, bSetModified );
+
+ if ( bSuccess && bSetModified )
+ UpdatePageBreakData( true ); // for PageBreak-Mode
+}
+
+void ScViewFunc::RemoveManualBreaks()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
+ rDoc.CopyToDocument( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRemoveBreaks>( pDocSh, nTab, std::move(pUndoDoc) ) );
+ }
+
+ rDoc.RemoveManualBreaks(nTab);
+ rDoc.UpdatePageBreaks(nTab);
+
+ UpdatePageBreakData( true );
+ pDocSh->SetDocumentModified();
+ pDocSh->PostPaint( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
+}
+
+void ScViewFunc::SetPrintZoom(sal_uInt16 nScale)
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+ pDocSh->SetPrintZoom( nTab, nScale, 0/*nPages*/ );
+}
+
+void ScViewFunc::AdjustPrintZoom()
+{
+ ScRange aRange;
+ if ( GetViewData().GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
+ aRange = GetViewData().GetMarkData().GetMultiMarkArea();
+ GetViewData().GetDocShell()->AdjustPrintZoom( aRange );
+}
+
+void ScViewFunc::SetPrintRanges( bool bEntireSheet, const OUString* pPrint,
+ const OUString* pRepCol, const OUString* pRepRow,
+ bool bAddPrint )
+{
+ // on all selected tables
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
+
+ ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0);
+
+ for (const SCTAB& nTab : rMark)
+ {
+ ScRange aRange( 0,0,nTab );
+
+ // print ranges
+
+ if( !bAddPrint )
+ {
+ rDoc.ClearPrintRanges( nTab );
+ rDoc.ClearPrintNamedRanges(nTab);
+ }
+
+ if( bEntireSheet )
+ {
+ rDoc.SetPrintEntireSheet( nTab );
+ }
+ else if ( pPrint )
+ {
+ if ( !pPrint->isEmpty() )
+ {
+ const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
+ sal_Int32 nPos = 0;
+ do
+ {
+ const OUString aToken = pPrint->getToken(0, sep, nPos);
+ if ( aRange.ParseAny( aToken, rDoc, aDetails ) & ScRefFlags::VALID )
+ rDoc.AddPrintRange( nTab, aRange );
+ }
+ while (nPos >= 0);
+ }
+ }
+ else // NULL = use selection (print range is always set), use empty string to delete all ranges
+ {
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ rDoc.AddPrintRange( nTab, aRange );
+ }
+ else if ( rMark.IsMultiMarked() )
+ {
+ rMark.MarkToMulti();
+ ScRangeListRef pList( new ScRangeList );
+ rMark.FillRangeListWithMarks( pList.get(), false );
+ for (size_t i = 0, n = pList->size(); i < n; ++i)
+ {
+ const ScRange & rR = (*pList)[i];
+ rDoc.AddPrintRange(nTab, rR);
+ }
+ }
+ }
+
+ // repeat columns
+
+ if ( pRepCol )
+ {
+ if ( pRepCol->isEmpty() )
+ rDoc.SetRepeatColRange( nTab, std::nullopt );
+ else
+ if ( aRange.ParseAny( *pRepCol, rDoc, aDetails ) & ScRefFlags::VALID )
+ rDoc.SetRepeatColRange( nTab, std::move(aRange) );
+ }
+
+ // repeat rows
+
+ if ( pRepRow )
+ {
+ if ( pRepRow->isEmpty() )
+ rDoc.SetRepeatRowRange( nTab, std::nullopt );
+ else
+ if ( aRange.ParseAny( *pRepRow, rDoc, aDetails ) & ScRefFlags::VALID )
+ rDoc.SetRepeatRowRange( nTab, std::move(aRange) );
+ }
+ }
+
+ // undo (for all tables)
+ if (bUndo)
+ {
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ std::unique_ptr<ScPrintRangeSaver> pNewRanges = rDoc.CreatePrintRangeSaver();
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ tools::JsonWriter aJsonWriter;
+ pNewRanges->GetPrintRangesInfo(aJsonWriter);
+
+ SfxViewShell* pViewShell = GetViewData().GetViewShell();
+ const OString message = aJsonWriter.finishAndGetAsOString();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_PRINT_RANGES, message);
+ }
+
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPrintRange>( pDocSh, nCurTab, std::move(pOldRanges), std::move(pNewRanges) ) );
+ }
+ else
+ pOldRanges.reset();
+
+ // update page breaks
+
+ for (const auto& rTab : rMark)
+ ScPrintFunc( pDocSh, pDocSh->GetPrinter(), rTab ).UpdatePages();
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_DELETE_PRINTAREA );
+
+ pDocSh->SetDocumentModified();
+}
+
+// Merge cells
+
+bool ScViewFunc::TestMergeCells() // pre-test (for menu)
+{
+ // simple test: true if there's a selection but no multi selection and not filtered
+
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ ScRange aRange;
+ bool bMergeable = ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE );
+ bMergeable = bMergeable && ( aRange.aStart.Col() != aRange.aEnd.Col() ||
+ aRange.aStart.Row() != aRange.aEnd.Row() );
+ return bMergeable;
+ }
+ else
+ return false;
+}
+
+void ScViewFunc::MergeCells( bool bApi, bool bDoContents, bool bCenter,
+ const sal_uInt16 nSlot )
+{
+ // Editable- and Being-Nested- test must be at the beginning (in DocFunc too),
+ // so that the Contents-QueryBox won't appear
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ rMark.MarkToSimple();
+ if (!rMark.IsMarked())
+ {
+ ErrorMessage(STR_NOMULTISELECT);
+ return;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ const ScRange& aMarkRange = rMark.GetMarkArea();
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+ if ( nStartCol == nEndCol && nStartRow == nEndRow )
+ {
+ // nothing to do
+ return;
+ }
+
+ if ( rDoc.HasAttrib( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
+ { // "Don't nest merging !"
+ ErrorMessage(STR_MSSG_MERGECELLS_0);
+ return;
+ }
+
+ // Check for the contents of all selected tables.
+ bool bAskDialog = false;
+ ScCellMergeOption aMergeOption(nStartCol, nStartRow, nEndCol, nEndRow, bCenter);
+ for (const SCTAB& i : rMark)
+ {
+ aMergeOption.maTabs.insert(i);
+
+ sc::MultiDataCellState aState = rDoc.HasMultipleDataCells(aMergeOption.getSingleRange(i));
+ switch (aState.meState)
+ {
+ case sc::MultiDataCellState::HasMultipleCells:
+ {
+ // this range contains multiple data cells.
+ bAskDialog = true;
+ break;
+ }
+ case sc::MultiDataCellState::HasOneCell:
+ {
+ // this range contains only one data cell.
+ if (nStartCol != aState.mnCol1 || nStartRow != aState.mnRow1)
+ bDoContents = true; // move the value to the top-left.
+ break;
+ }
+ default:
+ ;
+ }
+ }
+
+ bool bEmptyMergedCells = officecfg::Office::Calc::Compatibility::MergeCells::EmptyMergedCells::get();
+
+ auto doMerge = [this, pDocSh, aMergeOption, bApi, nStartCol, nStartRow, aMarkRange]
+ (bool bNowDoContents, bool bNowEmptyMergedCells)
+ {
+ if (pDocSh->GetDocFunc().MergeCells(aMergeOption, bNowDoContents, true/*bRecord*/,
+ bApi, bNowEmptyMergedCells))
+ {
+ SetCursor( nStartCol, nStartRow );
+ // DoneBlockMode( sal_False);
+ Unmark();
+
+ pDocSh->UpdateOle(GetViewData());
+ UpdateInputLine();
+
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "MERGE_CELLS");
+ }
+ };
+
+ if (bAskDialog)
+ {
+ bool bShowDialog = officecfg::Office::Calc::Compatibility::MergeCells::ShowDialog::get();
+ if (!bApi && bShowDialog)
+ {
+ auto pBox = std::make_shared<ScMergeCellsDialog>(GetViewData().GetDialogParent());
+
+ SfxViewShell* pViewShell = GetViewData().GetViewShell();
+
+ weld::DialogController::runAsync(pBox, [=](sal_Int32 nRetVal) {
+ if (nRetVal == RET_OK)
+ {
+ bool bRealDoContents = bDoContents;
+ bool bRealEmptyMergedCells = bEmptyMergedCells;
+ switch (pBox->GetMergeCellsOption())
+ {
+ case MoveContentHiddenCells:
+ bRealDoContents = true;
+ break;
+ case KeepContentHiddenCells:
+ bRealEmptyMergedCells = false;
+ break;
+ case EmptyContentHiddenCells:
+ bRealEmptyMergedCells = true;
+ break;
+ default:
+ assert(!"Unknown option for merge cells.");
+ break;
+ }
+
+ doMerge(bRealDoContents, bRealEmptyMergedCells);
+
+ if (nSlot != 0)
+ {
+ SfxRequest aReq(pViewShell->GetViewFrame(), nSlot);
+ if (!bApi && bRealDoContents)
+ aReq.AppendItem(SfxBoolItem(nSlot, bDoContents));
+ SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
+ rBindings.Invalidate(nSlot);
+ aReq.Done();
+ }
+ }
+ // else cancelled
+ });
+ }
+ } else
+ doMerge(bDoContents, bEmptyMergedCells);
+}
+
+bool ScViewFunc::TestRemoveMerge()
+{
+ bool bMerged = false;
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) )
+ bMerged = true;
+ }
+ return bMerged;
+}
+
+static bool lcl_extendMergeRange(ScCellMergeOption& rOption, const ScRange& rRange)
+{
+ bool bExtended = false;
+ if (rOption.mnStartCol > rRange.aStart.Col())
+ {
+ rOption.mnStartCol = rRange.aStart.Col();
+ bExtended = true;
+ }
+ if (rOption.mnStartRow > rRange.aStart.Row())
+ {
+ rOption.mnStartRow = rRange.aStart.Row();
+ bExtended = true;
+ }
+ if (rOption.mnEndCol < rRange.aEnd.Col())
+ {
+ rOption.mnEndCol = rRange.aEnd.Col();
+ bExtended = true;
+ }
+ if (rOption.mnEndRow < rRange.aEnd.Row())
+ {
+ rOption.mnEndRow = rRange.aEnd.Row();
+ bExtended = true;
+ }
+ return bExtended;
+}
+
+bool ScViewFunc::RemoveMerge()
+{
+ ScRange aRange;
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+ else if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScRange aExtended( aRange );
+ rDoc.ExtendMerge( aExtended );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ ScCellMergeOption aOption(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row());
+ bool bExtended = false;
+ do
+ {
+ bExtended = false;
+ for (const SCTAB& i : rMark)
+ {
+ aOption.maTabs.insert(i);
+ aExtended.aStart.SetTab(i);
+ aExtended.aEnd.SetTab(i);
+ rDoc.ExtendMerge(aExtended);
+ rDoc.ExtendOverlapped(aExtended);
+
+ // Expand the current range to be inclusive of all merged
+ // areas on all sheets.
+ bExtended = lcl_extendMergeRange(aOption, aExtended);
+ }
+ }
+ while (bExtended);
+
+ bool bOk = pDocSh->GetDocFunc().UnmergeCells(aOption, true/*bRecord*/, nullptr);
+ aExtended = aOption.getFirstSingleRange();
+ MarkRange( aExtended );
+
+ if (bOk)
+ pDocSh->UpdateOle(GetViewData());
+ }
+
+ OUString aCellLocation = aRange.aStart.GetColRowString();
+ collectUIInformation({{"CELL", aCellLocation}}, "UNMERGE_CELL");
+
+ return true; //! bOk ??
+}
+
+void ScViewFunc::FillSimple( FillDir eDir )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().FillSimple( aRange, &rMark, eDir, false );
+ if (bSuccess)
+ {
+ pDocSh->UpdateOle(GetViewData());
+ UpdateScrollBars();
+
+ auto& rDoc = pDocSh->GetDocument();
+ bool bDoAutoSpell = rDoc.GetDocOptions().IsAutoSpell();
+ if ( bDoAutoSpell )
+ {
+ // Copy AutoSpellData from above(left/right/below) if no selection.
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ if (aRange.aStart.Row() > 0 && aRange.aStart.Row() == aRange.aEnd.Row())
+ aRange.aStart.IncRow(-1);
+ break;
+ case FILL_TO_TOP:
+ if (aRange.aEnd.Row() < rDoc.MaxRow() && aRange.aStart.Row() == aRange.aEnd.Row())
+ aRange.aEnd.IncRow(1);
+ break;
+ case FILL_TO_RIGHT:
+ if (aRange.aStart.Col() > 0 && aRange.aStart.Col() == aRange.aEnd.Col())
+ aRange.aStart.IncCol(-1);
+ break;
+ case FILL_TO_LEFT:
+ if (aRange.aEnd.Col() < rDoc.MaxCol() && aRange.aStart.Col() == aRange.aEnd.Col())
+ aRange.aEnd.IncCol(1);
+ break;
+ }
+ CopyAutoSpellData(eDir, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(),
+ ::std::numeric_limits<sal_uLong>::max());
+ }
+
+ // Invalidate cell slots and update input line with new content.
+ CellContentChanged();
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+void ScViewFunc::FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
+ double fStart, double fStep, double fMax )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().
+ FillSeries( aRange, &rMark, eDir, eCmd, eDateCmd,
+ fStart, fStep, fMax, false );
+ if (bSuccess)
+ {
+ pDocSh->UpdateOle(GetViewData());
+ UpdateScrollBars();
+
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange);
+ }
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
+{
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScRange aRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab );
+ ScRange aSourceRange( aRange );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().
+ FillAuto( aRange, &rMark, eDir, nCount, false );
+ if (!bSuccess)
+ return;
+
+ MarkRange( aRange, false ); // aRange was modified in FillAuto
+ pDocSh->UpdateOle(GetViewData());
+ UpdateScrollBars();
+
+ bool bDoAutoSpell = pDocSh->GetDocument().GetDocOptions().IsAutoSpell();
+ if ( bDoAutoSpell )
+ CopyAutoSpellData(eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount);
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ ScRangeList aChangeRanges;
+ ScRange aChangeRange( aRange );
+ switch (eDir)
+ {
+ case FILL_TO_BOTTOM:
+ aChangeRange.aStart.SetRow( aSourceRange.aEnd.Row() + 1 );
+ break;
+ case FILL_TO_TOP:
+ aChangeRange.aEnd.SetRow( aSourceRange.aStart.Row() - 1 );
+ break;
+ case FILL_TO_RIGHT:
+ aChangeRange.aStart.SetCol( aSourceRange.aEnd.Col() + 1 );
+ break;
+ case FILL_TO_LEFT:
+ aChangeRange.aEnd.SetCol( aSourceRange.aStart.Col() - 1 );
+ break;
+ default:
+ break;
+ }
+ aChangeRanges.push_back( aChangeRange );
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
+ else if (pModelObj)
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate");
+}
+
+void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
+{
+ const ScDocument* pDoc = &GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ CellType eCellType;
+
+ ScGridWindow* pWin = GetActiveWin();
+ if ( pWin->InsideVisibleRange(nStartCol, nStartRow) && pWin->InsideVisibleRange(nEndCol, nEndRow) )
+ {
+ if ( nCount == ::std::numeric_limits<sal_uLong>::max() )
+ {
+ switch( eDir )
+ {
+ case FILL_TO_BOTTOM:
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ eCellType = pDoc->GetCellType(nColItr, nStartRow, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nStartRow);
+ if ( !pRanges )
+ continue;
+ for ( SCROW nRowItr = nStartRow + 1; nRowItr <= nEndRow; ++nRowItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ case FILL_TO_TOP:
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ eCellType = pDoc->GetCellType(nColItr, nEndRow, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nEndRow);
+ if ( !pRanges )
+ continue;
+ for ( SCROW nRowItr = nEndRow - 1; nRowItr >= nStartRow; --nRowItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ case FILL_TO_RIGHT:
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ eCellType = pDoc->GetCellType(nStartCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nStartCol, nRowItr);
+ if ( !pRanges )
+ continue;
+ for ( SCCOL nColItr = nStartCol + 1; nColItr <= nEndCol; ++nColItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ case FILL_TO_LEFT:
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ eCellType = pDoc->GetCellType(nEndCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nEndCol, nRowItr);
+ if ( !pRanges )
+ continue;
+ for ( SCCOL nColItr = nEndCol - 1; nColItr >= nStartCol; --nColItr )
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ break;
+ }
+ return;
+ }
+
+ typedef const std::vector<editeng::MisspellRanges>* MisspellRangesType;
+ SCROW nRowRepeatSize = nEndRow - nStartRow + 1;
+ SCCOL nColRepeatSize = nEndCol - nStartCol + 1;
+ SCROW nTillRow = 0;
+ SCCOL nTillCol = 0;
+ std::vector<std::vector<MisspellRangesType>> aSourceSpellRanges(nRowRepeatSize, std::vector<MisspellRangesType>(nColRepeatSize, nullptr));
+
+ for ( SCROW nRowIdx = 0; nRowIdx < nRowRepeatSize; ++nRowIdx )
+ {
+ for ( SCCOL nColIdx = 0; nColIdx < nColRepeatSize; ++nColIdx )
+ {
+ eCellType = pDoc->GetCellType(nStartCol + nColIdx, nStartRow + nRowIdx, nTab); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
+ aSourceSpellRanges[nRowIdx][nColIdx] = pWin->GetAutoSpellData( nStartCol + nColIdx, nStartRow + nRowIdx );
+ }
+ }
+
+ switch( eDir )
+ {
+ case FILL_TO_BOTTOM:
+ nTillRow = nEndRow + nCount;
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ for ( SCROW nRowItr = nEndRow + 1; nRowItr <= nTillRow; ++nRowItr )
+ {
+ size_t nSourceRowIdx = ( nRowItr - nEndRow - 1 ) % nRowRepeatSize;
+ MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+
+ case FILL_TO_TOP:
+ nTillRow = nStartRow - nCount;
+ for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
+ {
+ for ( SCROW nRowItr = nStartRow - 1; nRowItr >= nTillRow; --nRowItr )
+ {
+ size_t nSourceRowIdx = nRowRepeatSize - 1 - ( ( nStartRow - 1 - nRowItr ) % nRowRepeatSize );
+ MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+
+ case FILL_TO_RIGHT:
+ nTillCol = nEndCol + nCount;
+ for ( SCCOL nColItr = nEndCol + 1; nColItr <= nTillCol; ++nColItr )
+ {
+ size_t nSourceColIdx = ( nColItr - nEndCol - 1 ) % nColRepeatSize;
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+
+ case FILL_TO_LEFT:
+ nTillCol = nStartCol - nCount;
+ for ( SCCOL nColItr = nStartCol - 1; nColItr >= nTillCol; --nColItr )
+ {
+ size_t nSourceColIdx = nColRepeatSize - 1 - ( ( nStartCol - 1 - nColItr ) % nColRepeatSize );
+ for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
+ {
+ MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
+ if ( !pRanges )
+ continue;
+ pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
+ }
+ }
+ break;
+ }
+ }
+ else
+ pWin->ResetAutoSpellForContentChange();
+
+}
+
+void ScViewFunc::FillTab( InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bAsLink )
+{
+ //! allow source sheet to be protected
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ ScRange aMarkRange;
+ rMark.MarkToSimple();
+ bool bMulti = rMark.IsMultiMarked();
+ if (bMulti)
+ aMarkRange = rMark.GetMultiMarkArea();
+ else if (rMark.IsMarked())
+ aMarkRange = rMark.GetMarkArea();
+ else
+ aMarkRange = ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
+
+ ScDocumentUniquePtr pUndoDoc;
+
+ if (bUndo)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ for (const SCTAB& i : rMark)
+ if (i != nTab )
+ {
+ pUndoDoc->AddUndoTab( i, i );
+ aMarkRange.aStart.SetTab( i );
+ aMarkRange.aEnd.SetTab( i );
+ rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc );
+ }
+ }
+
+ if (bMulti)
+ rDoc.FillTabMarked( nTab, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
+ else
+ {
+ aMarkRange.aStart.SetTab( nTab );
+ aMarkRange.aEnd.SetTab( nTab );
+ rDoc.FillTab( aMarkRange, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
+ }
+
+ if (bUndo)
+ { //! for ChangeTrack not until the end
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoFillTable>( pDocSh, rMark,
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nTab,
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab,
+ std::move(pUndoDoc), bMulti, nTab, nFlags, nFunction, bSkipEmpty, bAsLink ) );
+ }
+
+ pDocSh->PostPaintGridAll();
+ pDocSh->PostDataChanged();
+}
+
+/** Downward fill of selected cell(s) by double-clicking cross-hair cursor
+
+ Either, extends a current selection if non-empty cells exist immediately
+ below the selection, overwriting cells below the selection up to the
+ minimum row of already filled cells.
+
+ Or, extends a current selection down to the last non-empty cell of an
+ adjacent column when the lower-right corner of the selection is
+ double-clicked. It uses a left-adjoining non-empty column as a guide if
+ such is available, otherwise a right-adjoining non-empty column is used.
+
+ @return No return value
+
+ @see #i12313#
+*/
+void ScViewFunc::FillCrossDblClick()
+{
+ ScRange aRange;
+ GetViewData().GetSimpleArea( aRange );
+ aRange.PutInOrder();
+
+ SCTAB nTab = GetViewData().GetCurPos().Tab();
+ SCCOL nStartX = aRange.aStart.Col();
+ SCROW nStartY = aRange.aStart.Row();
+ SCCOL nEndX = aRange.aEnd.Col();
+ SCROW nEndY = aRange.aEnd.Row();
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ if (nEndY >= rDoc.MaxRow())
+ // Nothing to fill.
+ return;
+
+ // Make sure the selection is not empty
+ if ( rDoc.IsBlockEmpty( nStartX, nStartY, nEndX, nEndY, nTab ) )
+ return;
+
+ // If there is data in all columns immediately below the selection then
+ // switch to overwriting fill.
+ SCROW nOverWriteEndRow = rDoc.MaxRow();
+ for (SCCOL nCol = nStartX; nCol <= nEndX; ++nCol)
+ {
+ if (rDoc.HasData( nCol, nEndY + 1, nTab))
+ {
+ // Determine the shortest data column to end the fill.
+ SCROW nY = nEndY + 1;
+ // FindAreaPos() returns the start row of the next data block if
+ // the current row is the last row of a data block and an empty
+ // cell follows. Somewhat unexpected behaviour...
+ // So check beforehand if there is one non-empty cell following.
+ if (rDoc.HasData( nCol, nY + 1, nTab))
+ {
+ rDoc.FindAreaPos( nCol, nY, nTab, SC_MOVE_DOWN);
+ if (nOverWriteEndRow > nY)
+ nOverWriteEndRow = nY;
+ }
+ else
+ {
+ nOverWriteEndRow = nY;
+ }
+ }
+ else
+ {
+ nOverWriteEndRow = 0;
+ break; // for
+ }
+ }
+
+ if (nOverWriteEndRow > nEndY)
+ {
+ FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nOverWriteEndRow - nEndY);
+ return;
+ }
+
+ // Non-overwriting fill follows.
+
+ const bool bDataLeft = (nStartX > 0);
+ if (!bDataLeft && nEndX >= rDoc.MaxCol())
+ // Absolutely no data left or right of selection.
+ return;
+
+ // Check that there is
+ // 1) data immediately left (preferred) or right of start (row) of selection
+ // 2) data there below
+ // 3) no data immediately below selection
+
+ SCCOL nMovX = (bDataLeft ? nStartX - 1 : nEndX + 1);
+ SCROW nMovY = nStartY;
+ bool bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
+ if (!bDataFound && bDataLeft && nEndX < rDoc.MaxCol())
+ {
+ nMovX = nEndX + 1; // check right
+ bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
+ }
+
+ if (!(bDataFound && rDoc.IsEmptyData( nStartX, nEndY + 1, nEndX, nEndY + 1, nTab )))
+ return;
+
+ // Get end of data left or right.
+ rDoc.FindAreaPos( nMovX, nMovY, nTab, SC_MOVE_DOWN);
+ // Find minimum end row of below empty area and data right.
+ for (SCCOL nX = nStartX; nX <= nEndX; ++nX)
+ {
+ SCROW nY = nEndY + 1;
+ // Get next row with data in this column.
+ rDoc.FindAreaPos( nX, nY, nTab, SC_MOVE_DOWN);
+ if (nMovY == rDoc.MaxRow() && nY == rDoc.MaxRow())
+ {
+ // FindAreaPos() returns MAXROW also if there is no data at all
+ // from the start, so check if that contains data if the nearby
+ // (left or right) data ends there and increment if no data
+ // here, pretending the next data would be thereafter so nMovY
+ // will not be decremented.
+ if (!rDoc.HasData( nX, nY, nTab))
+ ++nY;
+ }
+ if (nMovY > nY - 1)
+ nMovY = nY - 1;
+ }
+
+ if (nMovY > nEndY)
+ {
+ FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nMovY - nEndY);
+ }
+}
+
+void ScViewFunc::ConvertFormulaToValue()
+{
+ ScRange aRange;
+ GetViewData().GetSimpleArea(aRange);
+ aRange.PutInOrder();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().ConvertFormulaToValue(aRange, true);
+ // tdf#131326 - invalidate cell slots and update input line with new content
+ CellContentChanged();
+ pDocSh->PostPaint(aRange, PaintPartFlags::Grid);
+}
+
+void ScViewFunc::TransliterateText( TransliterationFlags nType )
+{
+ ScMarkData aFuncMark = GetViewData().GetMarkData();
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ // no selection -> use cursor position
+
+ ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ aFuncMark.SetMarkArea( ScRange( aCursor ) );
+ }
+
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ TransliterateText( aFuncMark, nType, false );
+ if (bSuccess)
+ {
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+}
+
+// AutoFormat
+
+ScAutoFormatData* ScViewFunc::CreateAutoFormatData()
+{
+ ScAutoFormatData* pData = nullptr;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
+ {
+ if ( nEndCol-nStartCol >= 3 && nEndRow-nStartRow >= 3 )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ pData = new ScAutoFormatData;
+ rDoc.GetAutoFormatData( nStartTab, nStartCol,nStartRow,nEndCol,nEndRow, *pData );
+ }
+ }
+ return pData;
+}
+
+void ScViewFunc::AutoFormat( sal_uInt16 nFormatNo )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ bool bSuccess = pDocSh->GetDocFunc().AutoFormat( aRange, &rMark, nFormatNo, false );
+ if (bSuccess)
+ pDocSh->UpdateOle(GetViewData());
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+// Search & Replace
+
+bool ScViewFunc::SearchAndReplace( const SvxSearchItem* pSearchItem,
+ bool bAddUndo, bool bIsApi )
+{
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (bAddUndo && !rDoc.IsUndoEnabled())
+ bAddUndo = false;
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() && (pSearchItem->HasStartPoint()) )
+ {
+ // No selection -> but we have a start point (top left corner of the
+ // current view), start searching from there, not from the current
+ // cursor position.
+ SCCOL nPosX;
+ SCROW nPosY;
+
+ int nPixelX = pSearchItem->GetStartPointX() * GetViewData().GetPPTX();
+ int nPixelY = pSearchItem->GetStartPointY() * GetViewData().GetPPTY();
+
+ GetViewData().GetPosFromPixel(nPixelX, nPixelY, GetViewData().GetActivePart(), nPosX, nPosY);
+
+ AlignToCursor( nPosX, nPosY, SC_FOLLOW_JUMP );
+ SetCursor( nPosX, nPosY, true );
+ }
+
+ SCCOL nCol, nOldCol;
+ SCROW nRow, nOldRow;
+ SCTAB nTab, nOldTab;
+ nCol = nOldCol = GetViewData().GetCurX();
+ nRow = nOldRow = GetViewData().GetCurY();
+ nTab = nOldTab = GetViewData().GetTabNo();
+
+ SvxSearchCmd nCommand = pSearchItem->GetCommand();
+ bool bAllTables = pSearchItem->IsAllTables();
+ std::set<SCTAB> aOldSelectedTables;
+ SCTAB nLastTab = rDoc.GetTableCount() - 1;
+ SCTAB nStartTab, nEndTab;
+ if ( bAllTables )
+ {
+ nStartTab = 0;
+ nEndTab = nLastTab;
+ std::set<SCTAB> aTmp(rMark.begin(), rMark.end());
+ aOldSelectedTables.swap(aTmp);
+ }
+ else
+ { //! at least one is always selected
+ nStartTab = rMark.GetFirstSelected();
+ nEndTab = rMark.GetLastSelected();
+ }
+
+ if ( nCommand == SvxSearchCmd::FIND
+ || nCommand == SvxSearchCmd::FIND_ALL)
+ bAddUndo = false;
+
+ //! account for bAttrib during Undo !!!
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScMarkData> pUndoMark;
+ OUString aUndoStr;
+ if (bAddUndo)
+ {
+ pUndoMark.reset(new ScMarkData(rMark)); // Mark is being modified
+ if ( nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ }
+ }
+
+ if ( bAllTables )
+ { //! select all, after pUndoMark has been created
+ for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
+ {
+ rMark.SelectTable( j, true );
+ }
+ }
+
+ DoneBlockMode(true); // don't delete mark
+ InitOwnBlockMode( ScRange( nCol, nRow, nStartTab, nCol, nRow, nEndTab));
+
+ // If search starts at the beginning don't ask again whether it shall start at the beginning
+ bool bFirst = true;
+ if ( nCol == 0 && nRow == 0 && nTab == nStartTab && !pSearchItem->GetBackward() )
+ bFirst = false;
+
+ bool bFound = false;
+ while (true)
+ {
+ GetFrameWin()->EnterWait();
+ ScRangeList aMatchedRanges;
+ bool bMatchedRangesWereClamped = false;
+ if (rDoc.SearchAndReplace(*pSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, pUndoDoc.get(), bMatchedRangesWereClamped))
+ {
+ bFound = true;
+ if (bAddUndo)
+ {
+ GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoReplace>( GetViewData().GetDocShell(), *pUndoMark,
+ nCol, nRow, nTab,
+ aUndoStr, std::move(pUndoDoc), pSearchItem ) );
+ }
+
+ if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
+ {
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ bool bShow = GetViewData().GetViewShell()->GetViewData().GetOptions().GetOption( VOPT_SUMMARY );
+
+ if (bShow && pViewFrm && !comphelper::LibreOfficeKit::isActive())
+ {
+ pViewFrm->ShowChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
+ SfxChildWindow* pWnd = pViewFrm->GetChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
+ if (pWnd)
+ {
+ sc::SearchResultsDlg* pDlg = static_cast<sc::SearchResultsDlg*>(pWnd->GetController().get());
+ if (pDlg)
+ {
+ const bool bCellNotes = (pSearchItem->GetCellType() == SvxSearchCellType::NOTE);
+ // ScCellIterator iterates over cells with content,
+ // for empty cells iterate over match positions.
+ const bool bEmptyCells = (!bCellNotes
+ && ((nCommand == SvxSearchCmd::FIND_ALL
+ && ScDocument::IsEmptyCellSearch(*pSearchItem))
+ || (nCommand == SvxSearchCmd::REPLACE_ALL
+ && pSearchItem->GetReplaceString().isEmpty())));
+ pDlg->FillResults(rDoc, aMatchedRanges, bCellNotes, bEmptyCells, bMatchedRangesWereClamped);
+ }
+ }
+ }
+
+ rMark.ResetMark();
+ for (size_t i = 0, n = aMatchedRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = aMatchedRanges[i];
+ if (r.aStart.Tab() == nTab)
+ rMark.SetMultiMarkArea(r);
+ }
+ }
+
+ break; // break 'while (TRUE)'
+ }
+ else if ( bFirst && (nCommand == SvxSearchCmd::FIND ||
+ nCommand == SvxSearchCmd::REPLACE) )
+ {
+ bFirst = false;
+ GetFrameWin()->LeaveWait();
+ if (!bIsApi)
+ {
+ if ( nStartTab == nEndTab )
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::EndSheet);
+ else
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
+
+ rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow );
+ if (pSearchItem->GetBackward())
+ nTab = nEndTab;
+ else
+ nTab = nStartTab;
+ }
+ else
+ {
+ break; // break 'while (TRUE)'
+ }
+ }
+ else // nothing found
+ {
+ if ( nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ pDocSh->PostPaintGridAll(); // Mark
+ }
+
+ GetFrameWin()->LeaveWait();
+ if (!bIsApi)
+ {
+ GetViewData().GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, pSearchItem->GetSearchString().toUtf8());
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
+ }
+
+ break; // break 'while (TRUE)'
+ }
+ } // of while true
+
+ if (!aOldSelectedTables.empty())
+ {
+ // restore originally selected table
+ for (SCTAB i = 0; i <= nEndTab; ++i)
+ rMark.SelectTable(i, false);
+
+ for (const auto& rTab : aOldSelectedTables)
+ rMark.SelectTable(rTab, true);
+
+ if ( bFound )
+ { // if a table is selected as a "match" it remains selected.
+ rMark.SelectTable( nTab, true );
+ // It's a swap if only one table was selected before
+ //! otherwise now one table more might be selected
+ if ( aOldSelectedTables.size() == 1 && nTab != nOldTab )
+ rMark.SelectTable( nOldTab, false );
+ }
+ }
+
+ // Avoid LOK selection notifications before we have all the results.
+ GetViewData().GetViewShell()->setTiledSearching(true);
+ MarkDataChanged();
+ GetViewData().GetViewShell()->setTiledSearching(false);
+
+ if ( bFound )
+ {
+ if ( nTab != GetViewData().GetTabNo() )
+ SetTabNo( nTab );
+
+ // if nothing is marked, DoneBlockMode, then marking can start
+ // directly from this place via Shift-Cursor
+ if (!rMark.IsMarked() && !rMark.IsMultiMarked())
+ DoneBlockMode(true);
+
+ AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
+ SetCursor( nCol, nRow, true );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ Point aCurPos = GetViewData().GetScrPos(nCol, nRow, GetViewData().GetActivePart());
+
+ // just update the cell selection
+ ScGridWindow* pGridWindow = GetViewData().GetActiveWin();
+ // Don't move cell selection handles for find-all: selection of all but the first result would be lost.
+ if (pGridWindow && nCommand == SvxSearchCmd::FIND)
+ {
+ // move the cell selection handles
+ pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_RESET, aCurPos.X(), aCurPos.Y());
+ pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_START, aCurPos.X(), aCurPos.Y());
+ pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_END, aCurPos.X(), aCurPos.Y());
+ }
+
+ if (pGridWindow)
+ {
+ std::vector<tools::Rectangle> aLogicRects;
+ pGridWindow->GetCellSelection(aLogicRects);
+
+ boost::property_tree::ptree aTree;
+ aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr());
+ aTree.put("highlightAll", nCommand == SvxSearchCmd::FIND_ALL);
+
+ boost::property_tree::ptree aSelections;
+ for (const tools::Rectangle& rLogicRect : aLogicRects)
+ {
+ boost::property_tree::ptree aSelection;
+ aSelection.put("part", OString::number(nTab).getStr());
+ aSelection.put("rectangles", rLogicRect.toString().getStr());
+ aSelections.push_back(std::make_pair("", aSelection));
+ }
+ aTree.add_child("searchResultSelection", aSelections);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ OString aPayload( aStream.str() );
+ SfxViewShell* pViewShell = GetViewData().GetViewShell();
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload);
+
+ // Trigger LOK_CALLBACK_TEXT_SELECTION now.
+ MarkDataChanged();
+ }
+ }
+
+ if ( nCommand == SvxSearchCmd::REPLACE
+ || nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ if ( nCommand == SvxSearchCmd::REPLACE )
+ {
+ pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid );
+
+ // jump to next cell if we replaced everything in the cell
+ // where the cursor was positioned (but avoid switching tabs)
+ if ( nCol == nOldCol && nRow == nOldRow && nTab == nOldTab )
+ {
+ SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
+ aSearchItem.SetCommand(SvxSearchCmd::FIND);
+ aSearchItem.SetWhich(SID_SEARCH_ITEM);
+
+ ScRangeList aMatchedRanges;
+ bool bMatchedRangesWereClamped;
+ ScTable::UpdateSearchItemAddressForReplace( aSearchItem, nCol, nRow );
+ if ( rDoc.SearchAndReplace( aSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped ) &&
+ ( nTab == nOldTab ) &&
+ ( nCol != nOldCol || nRow != nOldRow ) )
+ {
+ AlignToCursor(nCol, nRow, SC_FOLLOW_JUMP);
+ SetCursor( nCol, nRow, true );
+ }
+ }
+ }
+ else
+ pDocSh->PostPaintGridAll();
+ pDocSh->SetDocumentModified();
+ }
+ else if ( nCommand == SvxSearchCmd::FIND_ALL )
+ pDocSh->PostPaintGridAll(); // mark
+ GetFrameWin()->LeaveWait();
+ }
+ return bFound;
+}
+
+// Goal Seek
+
+void ScViewFunc::Solve( const ScSolveParam& rParam )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ SCCOL nDestCol = rParam.aRefVariableCell.Col();
+ SCROW nDestRow = rParam.aRefVariableCell.Row();
+ SCTAB nDestTab = rParam.aRefVariableCell.Tab();
+
+ ScEditableTester aTester( rDoc, nDestTab, nDestCol,nDestRow, nDestCol,nDestRow );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ OUString aTargetValStr;
+ if ( rParam.pStrTargetVal )
+ aTargetValStr = *rParam.pStrTargetVal;
+
+ OUString aMsgStr;
+ OUString aResStr;
+ double nSolveResult;
+
+ GetFrameWin()->EnterWait();
+
+ bool bExact =
+ rDoc.Solver(
+ rParam.aRefFormulaCell.Col(),
+ rParam.aRefFormulaCell.Row(),
+ rParam.aRefFormulaCell.Tab(),
+ nDestCol, nDestRow, nDestTab,
+ aTargetValStr,
+ nSolveResult );
+
+ GetFrameWin()->LeaveWait();
+
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ sal_uLong nFormat = 0;
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nDestCol, nDestRow, nDestTab );
+ if ( pPattern )
+ nFormat = pPattern->GetNumberFormat( pFormatter );
+ const Color* p;
+ pFormatter->GetOutputString( nSolveResult, nFormat, aResStr, &p );
+
+ if ( bExact )
+ {
+ aMsgStr += ScResId( STR_MSSG_SOLVE_0 ) +
+ aResStr +
+ ScResId( STR_MSSG_SOLVE_1 );
+ }
+ else
+ {
+ aMsgStr = ScResId( STR_MSSG_SOLVE_2 ) +
+ ScResId( STR_MSSG_SOLVE_3 ) +
+ aResStr +
+ ScResId( STR_MSSG_SOLVE_4 );
+ }
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo, aMsgStr));
+ xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0));
+ xBox->set_default_response(RET_NO);
+ if (xBox->run() == RET_YES)
+ EnterValue( nDestCol, nDestRow, nDestTab, nSolveResult );
+
+ GetViewData().GetViewShell()->UpdateInputHandler( true );
+}
+
+// multi operation
+
+void ScViewFunc::TabOp( const ScTabOpParam& rParam, bool bRecord )
+{
+ ScRange aRange;
+ if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ pDocSh->GetDocFunc().TabOp( aRange, &rMark, rParam, bRecord, false );
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+void ScViewFunc::MakeScenario( const OUString& rName, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ SCTAB nNewTab = pDocSh->MakeScenario( nTab, rName, rComment, rColor, nFlags, rMark );
+ if (nFlags & ScScenarioFlags::CopyAll)
+ SetTabNo( nNewTab, true ); // ScScenarioFlags::CopyAll -> visible
+ else
+ {
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_STATUS_DOCPOS ); // Statusbar
+ rBindings.Invalidate( SID_ROWCOL_SELCOUNT ); // Statusbar
+ rBindings.Invalidate( SID_TABLES_COUNT );
+ rBindings.Invalidate( SID_SELECT_SCENARIO );
+ rBindings.Invalidate( FID_TABLE_SHOW );
+ }
+}
+
+void ScViewFunc::ExtendScenario()
+{
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ // Undo: apply attributes
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) );
+ aPattern.GetItemSet().Put( ScProtectionAttr( true ) );
+ ApplySelectionPattern(aPattern);
+}
+
+void ScViewFunc::UseScenario( const OUString& rName )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ DoneBlockMode();
+ InitOwnBlockMode( ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab));
+ pDocSh->UseScenario( nTab, rName );
+}
+
+// Insert table
+
+bool ScViewFunc::InsertTable( const OUString& rName, SCTAB nTab, bool bRecord )
+{
+ // Order Table/Name is inverted for DocFunc
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ InsertTable( nTab, rName, bRecord, false );
+ if (bSuccess)
+ SetTabNo( nTab, true );
+
+ return bSuccess;
+}
+
+// Insert tables
+
+void ScViewFunc::InsertTables(std::vector<OUString>& aNames, SCTAB nTab,
+ SCTAB nCount, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ if (bRecord)
+ {
+ rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
+ }
+
+ bool bFlag=false;
+
+ if(aNames.empty())
+ {
+ rDoc.CreateValidTabNames(aNames, nCount);
+ }
+ if (rDoc.InsertTabs(nTab, aNames))
+ {
+ pDocSh->Broadcast( ScTablesHint( SC_TABS_INSERTED, nTab, nCount ) );
+ bFlag = true;
+ }
+
+ if (!bFlag)
+ return;
+
+ if (bRecord)
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTables>( pDocSh, nTab, std::move(aNames)));
+
+ // Update views
+
+ SetTabNo( nTab, true );
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+}
+
+bool ScViewFunc::AppendTable( const OUString& rName, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ if (bRecord)
+ rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
+
+ if (rDoc.InsertTab( SC_TAB_APPEND, rName ))
+ {
+ SCTAB nTab = rDoc.GetTableCount()-1;
+ if (bRecord)
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoInsertTab>( pDocSh, nTab, true, rName));
+ GetViewData().InsertTab( nTab );
+ SetTabNo( nTab, true );
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void ScViewFunc::DeleteTable( SCTAB nTab, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ bool bSuccess = pDocSh->GetDocFunc().DeleteTable( nTab, bRecord );
+ if (bSuccess)
+ {
+ SCTAB nNewTab = nTab;
+ if ( nNewTab >= rDoc.GetTableCount() )
+ --nNewTab;
+ SetTabNo( nNewTab, true );
+ }
+}
+
+//only use this method for undo for now, all sheets must be connected
+//this method doesn't support undo for now, merge it when it with the other method later
+void ScViewFunc::DeleteTables( const SCTAB nTab, SCTAB nSheets )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bVbaEnabled = rDoc.IsInVBAMode();
+ SCTAB nNewTab = nTab;
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+
+ while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
+ --nNewTab;
+
+ if (!rDoc.DeleteTabs(nTab, nSheets))
+ return;
+
+ if( bVbaEnabled )
+ {
+ for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
+ {
+ OUString sCodeName;
+ bool bHasCodeName = rDoc.GetCodeName( nTab + aTab, sCodeName );
+ if ( bHasCodeName )
+ VBA_DeleteModule( *pDocSh, sCodeName );
+ }
+ }
+
+ pDocSh->Broadcast( ScTablesHint( SC_TABS_DELETED, nTab, nSheets ) );
+ if ( nNewTab >= rDoc.GetTableCount() )
+ nNewTab = rDoc.GetTableCount() - 1;
+ SetTabNo( nNewTab, true );
+
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+bool ScViewFunc::DeleteTables(const vector<SCTAB> &TheTabs, bool bRecord )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bVbaEnabled = rDoc.IsInVBAMode();
+ SCTAB nNewTab = TheTabs.front();
+ weld::WaitObject aWait(GetViewData().GetDialogParent());
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+ if ( bVbaEnabled )
+ bRecord = false;
+
+ while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
+ --nNewTab;
+
+ bool bWasLinked = false;
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nCount = rDoc.GetTableCount();
+
+ OUString aOldName;
+ for(size_t i=0; i<TheTabs.size(); ++i)
+ {
+ SCTAB nTab = TheTabs[i];
+ if (i==0)
+ pUndoDoc->InitUndo( rDoc, nTab,nTab, true,true ); // incl. column/fow flags
+ else
+ pUndoDoc->AddUndoTab( nTab,nTab, true,true ); // incl. column/fow flags
+
+ rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
+ rDoc.GetName( nTab, aOldName );
+ pUndoDoc->RenameTab( nTab, aOldName );
+ if (rDoc.IsLinked(nTab))
+ {
+ bWasLinked = true;
+ pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
+ rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
+ rDoc.GetLinkTab(nTab),
+ rDoc.GetLinkRefreshDelay(nTab) );
+ }
+ if ( rDoc.IsScenario(nTab) )
+ {
+ pUndoDoc->SetScenario( nTab, true );
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nScenFlags;
+ rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
+ pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
+ bool bActive = rDoc.IsActiveScenario( nTab );
+ pUndoDoc->SetActiveScenario( nTab, bActive );
+ }
+ pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
+ pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
+ auto pSheetEvents = rDoc.GetSheetEvents( nTab );
+ pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
+ pUndoDoc->SetLayoutRTL( nTab, rDoc.IsLayoutRTL( nTab ) );
+
+ if ( rDoc.IsTabProtected( nTab ) )
+ pUndoDoc->SetTabProtection(nTab, rDoc.GetTabProtection(nTab));
+
+ // Drawing-Layer is responsible for its Undo !!!
+ // pUndoDoc->TransferDrawPage(rDoc, nTab,nTab);
+ }
+
+ pUndoDoc->AddUndoTab( 0, nCount-1 ); // all Tabs for references
+
+ rDoc.BeginDrawUndo(); // DeleteTab creates a SdrUndoDelPage
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+ }
+
+ bool bDelDone = false;
+
+ for(int i=TheTabs.size()-1; i>=0; --i)
+ {
+ OUString sCodeName;
+ bool bHasCodeName = rDoc.GetCodeName( TheTabs[i], sCodeName );
+ if (rDoc.DeleteTab(TheTabs[i]))
+ {
+ bDelDone = true;
+ if( bVbaEnabled && bHasCodeName )
+ {
+ VBA_DeleteModule( *pDocSh, sCodeName );
+ }
+ pDocSh->Broadcast( ScTablesHint( SC_TAB_DELETED, TheTabs[i] ) );
+ }
+ }
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDeleteTab>( GetViewData().GetDocShell(), TheTabs,
+ std::move(pUndoDoc), std::move(pUndoData) ));
+ }
+
+ if (bDelDone)
+ {
+ if ( nNewTab >= rDoc.GetTableCount() )
+ nNewTab = rDoc.GetTableCount() - 1;
+
+ SetTabNo( nNewTab, true );
+
+ if (bWasLinked)
+ {
+ pDocSh->UpdateLinks(); // update Link-Manager
+ GetViewData().GetBindings().Invalidate(SID_LINKS);
+ }
+
+ pDocSh->PostPaintExtras();
+ pDocSh->SetDocumentModified();
+
+ SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+ }
+ return bDelDone;
+}
+
+bool ScViewFunc::RenameTable( const OUString& rName, SCTAB nTab )
+{
+ // order Table/Name is inverted for DocFunc
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
+ RenameTable( nTab, rName, true, false );
+ if (bSuccess)
+ {
+ // the table name might be part of a formula
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+ return bSuccess;
+}
+
+bool ScViewFunc::SetTabBgColor( const Color& rColor, SCTAB nTab )
+{
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( nTab, rColor, true, false );
+ if (bSuccess)
+ {
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+ return bSuccess;
+}
+
+bool ScViewFunc::SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList )
+{
+ bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( rUndoSetTabBgColorInfoList, false );
+ if (bSuccess)
+ {
+ GetViewData().GetViewShell()->UpdateInputHandler();
+ }
+ return bSuccess;
+}
+
+void ScViewFunc::InsertAreaLink( const OUString& rFile,
+ const OUString& rFilter, const OUString& rOptions,
+ const OUString& rSource )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCCOL nPosX = GetViewData().GetCurX();
+ SCROW nPosY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aPos( nPosX, nPosY, nTab );
+
+ pDocSh->GetDocFunc().InsertAreaLink( rFile, rFilter, rOptions, rSource, aPos, 0/*nRefresh*/, false, false );
+}
+
+void ScViewFunc::InsertTableLink( const OUString& rFile,
+ const OUString& rFilter, const OUString& rOptions,
+ std::u16string_view rTabName )
+{
+ OUString aFilterName = rFilter;
+ OUString aOpt = rOptions;
+ ScDocumentLoader aLoader( rFile, aFilterName, aOpt );
+ if (aLoader.IsError())
+ return;
+
+ ScDocShell* pSrcSh = aLoader.GetDocShell();
+ ScDocument& rSrcDoc = pSrcSh->GetDocument();
+ SCTAB nTab = MAXTAB+1;
+ if (rTabName.empty()) // no name given -> first table
+ nTab = 0;
+ else
+ {
+ OUString aTemp;
+ SCTAB nCount = rSrcDoc.GetTableCount();
+ for (SCTAB i=0; i<nCount; i++)
+ {
+ rSrcDoc.GetName( i, aTemp );
+ if ( aTemp == rTabName )
+ nTab = i;
+ }
+ }
+
+ if ( nTab <= MAXTAB )
+ ImportTables( pSrcSh, 1, &nTab, true,
+ GetViewData().GetTabNo() );
+}
+
+// Copy/link tables from another document
+
+void ScViewFunc::ImportTables( ScDocShell* pSrcShell,
+ SCTAB nCount, const SCTAB* pSrcTabs, bool bLink,SCTAB nTab )
+{
+ ScDocument& rSrcDoc = pSrcShell->GetDocument();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ bool bError = false;
+ bool bRefs = false;
+ bool bName = false;
+
+ if (rSrcDoc.GetDrawLayer())
+ pDocSh->MakeDrawLayer();
+
+ if (bUndo)
+ rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ SCTAB nInsCount = 0;
+ SCTAB i;
+ for( i=0; i<nCount; i++ )
+ { // insert sheets first and update all references
+ OUString aName;
+ rSrcDoc.GetName( pSrcTabs[i], aName );
+ rDoc.CreateValidTabName( aName );
+ if ( !rDoc.InsertTab( nTab+i, aName ) )
+ {
+ bError = true; // total error
+ break; // for
+ }
+ ++nInsCount;
+ }
+ for (i=0; i<nCount && !bError; i++)
+ {
+ SCTAB nSrcTab = pSrcTabs[i];
+ SCTAB nDestTab1=nTab+i;
+ sal_uLong nErrVal = pDocSh->TransferTab( *pSrcShell, nSrcTab, nDestTab1,
+ false, false ); // no insert
+
+ switch (nErrVal)
+ {
+ case 0: // internal error or full of errors
+ bError = true;
+ break;
+ case 2:
+ bRefs = true;
+ break;
+ case 3:
+ bName = true;
+ break;
+ case 4:
+ bRefs = bName = true;
+ break;
+ }
+
+ }
+
+ if (bLink)
+ {
+ sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
+
+ SfxMedium* pMed = pSrcShell->GetMedium();
+ OUString aFileName = pMed->GetName();
+ OUString aFilterName;
+ if (pMed->GetFilter())
+ aFilterName = pMed->GetFilter()->GetFilterName();
+ OUString aOptions = ScDocumentLoader::GetOptions(*pMed);
+
+ bool bWasThere = rDoc.HasLink( aFileName, aFilterName, aOptions );
+
+ sal_uLong nRefresh = 0;
+ OUString aTabStr;
+ for (i=0; i<nInsCount; i++)
+ {
+ rSrcDoc.GetName( pSrcTabs[i], aTabStr );
+ rDoc.SetLink( nTab+i, ScLinkMode::NORMAL,
+ aFileName, aFilterName, aOptions, aTabStr, nRefresh );
+ }
+
+ if (!bWasThere) // Insert link only once per source document
+ {
+ ScTableLink* pLink = new ScTableLink( pDocSh, aFileName, aFilterName, aOptions, nRefresh );
+ pLink->SetInCreate( true );
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilterName );
+ pLink->Update();
+ pLink->SetInCreate( false );
+
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_LINKS );
+ }
+ }
+
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoImportTab>( pDocSh, nTab, nCount ) );
+ }
+
+ for (i=0; i<nInsCount; i++)
+ GetViewData().InsertTab(nTab);
+ SetTabNo(nTab,true);
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras );
+
+ SfxApplication* pSfxApp = SfxGetpApp();
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+
+ pDocSh->PostPaintExtras();
+ pDocSh->PostPaintGridAll();
+ pDocSh->SetDocumentModified();
+
+ if (bRefs)
+ ErrorMessage(STR_ABSREFLOST);
+ if (bName)
+ ErrorMessage(STR_NAMECONFLICT);
+}
+
+// Move/Copy table to another document
+
+void ScViewFunc::MoveTable(
+ sal_uInt16 nDestDocNo, SCTAB nDestTab, bool bCopy, const OUString* pNewTabName )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScDocShell* pDestShell = nullptr;
+ ScTabViewShell* pDestViewSh = nullptr;
+ bool bUndo (rDoc.IsUndoEnabled());
+ bool bRename = pNewTabName && !pNewTabName->isEmpty();
+
+ bool bNewDoc = (nDestDocNo == SC_DOC_NEW);
+ if ( bNewDoc )
+ {
+ nDestTab = 0; // firstly insert
+
+ // execute without SfxCallMode::RECORD, because already contained in move command
+
+ SfxStringItem aItem( SID_FILE_NAME, "private:factory/" + STRING_SCAPP );
+ SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
+
+ const SfxPoolItemHolder aResult(GetViewData().GetDispatcher().ExecuteList(
+ SID_OPENDOC, SfxCallMode::API|SfxCallMode::SYNCHRON,
+ { &aItem, &aTarget }));
+ if (nullptr != aResult.getItem())
+ {
+ if ( auto pObjectItem = dynamic_cast<const SfxObjectItem*>(aResult.getItem()) )
+ pDestShell = dynamic_cast<ScDocShell*>( pObjectItem->GetShell() );
+ else if ( auto pViewFrameItem = dynamic_cast<const SfxViewFrameItem*>(aResult.getItem()))
+ {
+ SfxViewFrame* pFrm = pViewFrameItem->GetFrame();
+ if (pFrm)
+ pDestShell = dynamic_cast<ScDocShell*>( pFrm->GetObjectShell() );
+ }
+ if (pDestShell)
+ pDestViewSh = pDestShell->GetBestViewShell();
+ }
+ }
+ else
+ pDestShell = ScDocShell::GetShellByNum( nDestDocNo );
+
+ if (!pDestShell)
+ {
+ OSL_FAIL("Destination document not found !!!");
+ return;
+ }
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (bRename && rMark.GetSelectCount() != 1)
+ {
+ // Custom sheet name is provided, but more than one sheet is selected.
+ // We don't support this scenario at the moment.
+ return;
+ }
+
+ ScDocument& rDestDoc = pDestShell->GetDocument();
+
+ if (&rDestDoc != &rDoc)
+ {
+ if (bNewDoc)
+ {
+ while (rDestDoc.GetTableCount() > 1)
+ rDestDoc.DeleteTab(0);
+ rDestDoc.RenameTab( 0, "______42_____" );
+ }
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ vector<SCTAB> TheTabs;
+
+ for(SCTAB i=0; i<nTabCount; ++i)
+ {
+ if(rMark.GetTableSelect(i))
+ {
+ OUString aTabName;
+ rDoc.GetName( i, aTabName);
+ TheTabs.push_back(i);
+ for(SCTAB j=i+1;j<nTabCount;j++)
+ {
+ if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
+ {
+ rDoc.GetName( j, aTabName);
+ TheTabs.push_back(j);
+ i=j;
+ }
+ else break;
+ }
+ }
+ }
+
+ GetFrameWin()->EnterWait();
+
+ if (rDoc.GetDrawLayer())
+ pDestShell->MakeDrawLayer();
+
+ if (!bNewDoc && bUndo)
+ rDestDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ sal_uLong nErrVal =1;
+ if(nDestTab==SC_TAB_APPEND)
+ nDestTab=rDestDoc.GetTableCount();
+ SCTAB nDestTab1=nDestTab;
+ ScClipParam aParam;
+ for( size_t j=0; j<TheTabs.size(); ++j, ++nDestTab1 )
+ { // insert sheets first and update all references
+ OUString aName;
+ if (bRename)
+ aName = *pNewTabName;
+ else
+ rDoc.GetName( TheTabs[j], aName );
+
+ rDestDoc.CreateValidTabName( aName );
+ if ( !rDestDoc.InsertTab( nDestTab1, aName ) )
+ {
+ nErrVal = 0; // total error
+ break; // for
+ }
+ ScRange aRange( 0, 0, TheTabs[j], rDoc.MaxCol(), rDoc.MaxRow(), TheTabs[j] );
+ aParam.maRanges.push_back(aRange);
+ }
+ rDoc.SetClipParam(aParam);
+ if ( nErrVal > 0 )
+ {
+ nDestTab1 = nDestTab;
+ for(SCTAB nTab : TheTabs)
+ {
+ nErrVal = pDestShell->TransferTab( *pDocShell, nTab, nDestTab1, false, false );
+ nDestTab1++;
+ }
+ }
+ if (!bNewDoc && bUndo)
+ {
+ OUString sName;
+ rDestDoc.GetName(nDestTab, sName);
+ pDestShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoImportTab>( pDestShell, nDestTab,
+ static_cast<SCTAB>(TheTabs.size())));
+
+ }
+ else
+ {
+ pDestShell->GetUndoManager()->Clear();
+ }
+
+ GetFrameWin()->LeaveWait();
+ switch (nErrVal)
+ {
+ case 0: // internal error or full of errors
+ {
+ ErrorMessage(STR_TABINSERT_ERROR);
+ return;
+ }
+ case 2:
+ ErrorMessage(STR_ABSREFLOST);
+ break;
+ case 3:
+ ErrorMessage(STR_NAMECONFLICT);
+ break;
+ case 4:
+ {
+ ErrorMessage(STR_ABSREFLOST);
+ ErrorMessage(STR_NAMECONFLICT);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!bCopy)
+ {
+ if(nTabCount!=nTabSelCount)
+ DeleteTables(TheTabs); // incl. Paint & Undo
+ else
+ ErrorMessage(STR_TABREMOVE_ERROR);
+ }
+
+ if (bNewDoc)
+ {
+ // ChartListenerCollection must be updated before DeleteTab
+ if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
+ rDestDoc.UpdateChartListenerCollection();
+
+ SCTAB nNumTabsInserted = static_cast<SCTAB>(TheTabs.size());
+ pDestShell->Broadcast( ScTablesHint( SC_TABS_INSERTED, 0, nNumTabsInserted ) );
+
+ rDestDoc.DeleteTab( nNumTabsInserted ); // old first table
+ pDestShell->Broadcast( ScTablesHint( SC_TAB_DELETED, nNumTabsInserted ) );
+
+ if (pDestViewSh)
+ {
+ // Make sure to clear the cached page view after sheet
+ // deletion, which still points to the sdr page belonging to
+ // the deleted sheet.
+ SdrView* pSdrView = pDestViewSh->GetScDrawView();
+ if (pSdrView)
+ pSdrView->ClearPageView();
+
+ pDestViewSh->TabChanged(); // pages on the drawing layer
+ }
+ pDestShell->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
+ PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left |
+ PaintPartFlags::Extras | PaintPartFlags::Size );
+ // PaintPartFlags::Size for outline
+ }
+ else
+ {
+ pDestShell->Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestTab ) );
+ pDestShell->PostPaintExtras();
+ pDestShell->PostPaintGridAll();
+ }
+
+ TheTabs.clear();
+
+ pDestShell->SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ }
+ else
+ {
+ // Move or copy within the same document.
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ unique_ptr< vector<SCTAB> > pSrcTabs(new vector<SCTAB>);
+ unique_ptr< vector<SCTAB> > pDestTabs(new vector<SCTAB>);
+ unique_ptr< vector<OUString> > pTabNames(new vector<OUString>);
+ unique_ptr< vector<OUString> > pDestNames;
+ pSrcTabs->reserve(nTabCount);
+ pDestTabs->reserve(nTabCount);
+ pTabNames->reserve(nTabCount);
+ OUString aDestName;
+
+ for(SCTAB i=0;i<nTabCount;i++)
+ {
+ if(rMark.GetTableSelect(i))
+ {
+ OUString aTabName;
+ rDoc.GetName( i, aTabName);
+ pTabNames->push_back(aTabName);
+
+ for(SCTAB j=i+1;j<nTabCount;j++)
+ {
+ if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
+ {
+ rDoc.GetName( j, aTabName);
+ pTabNames->push_back(aTabName);
+ i=j;
+ }
+ else break;
+ }
+ }
+ }
+
+ if (bCopy && bUndo)
+ rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
+
+ rDoc.GetName( nDestTab, aDestName);
+ SCTAB nDestTab1=nDestTab;
+ SCTAB nMovTab=0;
+ for (size_t j = 0, n = pTabNames->size(); j < n; ++j)
+ {
+ nTabCount = rDoc.GetTableCount();
+ const OUString& rStr = (*pTabNames)[j];
+ if(!rDoc.GetTable(rStr,nMovTab))
+ {
+ nMovTab=nTabCount;
+ }
+ if(!rDoc.GetTable(aDestName,nDestTab1))
+ {
+ nDestTab1=nTabCount;
+ }
+ pDocShell->MoveTable( nMovTab, nDestTab1, bCopy, false ); // Undo is here
+
+ // tdf#43175 - Adjust chart references on every copied sheet
+ if (bCopy)
+ {
+ // New position of source table after moving
+ SCTAB nSrcTab = (nDestTab1 <= nMovTab) ? nMovTab + 1 : nMovTab;
+ //#i29848# adjust references to data on the copied sheet
+ ScChartHelper::AdjustRangesOfChartsOnDestinationPage(rDoc, rDestDoc, nSrcTab,
+ nDestTab1);
+ }
+
+ if(bCopy && rDoc.IsScenario(nMovTab))
+ {
+ OUString aComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+
+ rDoc.GetScenarioData(nMovTab, aComment,aColor, nFlags);
+ rDoc.SetScenario(nDestTab1,true);
+ rDoc.SetScenarioData(nDestTab1,aComment,aColor,nFlags);
+ bool bActive = rDoc.IsActiveScenario(nMovTab );
+ rDoc.SetActiveScenario( nDestTab1, bActive );
+ bool bVisible=rDoc.IsVisible(nMovTab);
+ rDoc.SetVisible(nDestTab1,bVisible );
+ }
+
+ pSrcTabs->push_back(nMovTab);
+
+ if(!bCopy)
+ {
+ if(!rDoc.GetTable(rStr,nDestTab1))
+ {
+ nDestTab1=nTabCount;
+ }
+ }
+
+ pDestTabs->push_back(nDestTab1);
+ }
+
+ // Rename must be done after all sheets have been moved.
+ if (bRename)
+ {
+ pDestNames.reset(new vector<OUString>);
+ size_t n = pDestTabs->size();
+ pDestNames->reserve(n);
+ for (size_t j = 0; j < n; ++j)
+ {
+ SCTAB nRenameTab = (*pDestTabs)[j];
+ OUString aTabName = *pNewTabName;
+ rDoc.CreateValidTabName( aTabName );
+ pDestNames->push_back(aTabName);
+ rDoc.RenameTab(nRenameTab, aTabName);
+ }
+ }
+ else
+ // No need to keep this around when we are not renaming.
+ pTabNames.reset();
+
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ if (bUndo)
+ {
+ if (bCopy)
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoCopyTab>(
+ pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pDestNames)));
+ }
+ else
+ {
+ pDocShell->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoMoveTab>(
+ pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pTabNames), std::move(pDestNames)));
+ }
+ }
+
+ SCTAB nNewTab = nDestTab;
+ if (nNewTab == SC_TAB_APPEND)
+ nNewTab = rDoc.GetTableCount()-1;
+ else if (!bCopy && nTab<nDestTab)
+ nNewTab--;
+
+ SetTabNo( nNewTab, true );
+ }
+}
+
+void ScViewFunc::ShowTable( const std::vector<OUString>& rNames )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ std::vector<SCTAB> undoTabs;
+ SCTAB nPos = 0;
+
+ bool bFound(false);
+
+ for (const OUString& aName : rNames)
+ {
+ if (rDoc.GetTable(aName, nPos))
+ {
+ rDoc.SetVisible( nPos, true );
+ SetTabNo( nPos, true );
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ if (!bFound)
+ bFound = true;
+ if (bUndo)
+ undoTabs.push_back(nPos);
+ }
+ }
+ if (bFound)
+ {
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, std::move(undoTabs), true ) );
+ }
+ pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
+ pDocSh->SetDocumentModified();
+ }
+}
+
+void ScViewFunc::HideTable( const ScMarkData& rMark, SCTAB nTabToSelect )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bUndo(rDoc.IsUndoEnabled());
+ SCTAB nVisible = 0;
+ SCTAB nTabCount = rDoc.GetTableCount();
+
+ SCTAB nTabSelCount = rMark.GetSelectCount();
+
+ // check to make sure we won't hide all sheets. we need at least one visible at all times.
+ for ( SCTAB i=0; i < nTabCount && nVisible <= nTabSelCount ; i++ )
+ if (rDoc.IsVisible(i))
+ ++nVisible;
+
+ if (nVisible <= nTabSelCount)
+ return;
+
+ std::vector<SCTAB> undoTabs;
+
+ // need to take a copy of selectedtabs since it is modified in the loop
+ const ScMarkData::MarkedTabsType selectedTabs = rMark.GetSelectedTabs();
+ for (const SCTAB& nTab : selectedTabs)
+ {
+ if (rDoc.IsVisible( nTab ))
+ {
+ rDoc.SetVisible( nTab, false );
+ // Update views
+ pDocSh->Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
+ SetTabNo( nTab, true );
+ // Store for undo
+ if (bUndo)
+ undoTabs.push_back(nTab);
+ }
+ }
+
+ if (nTabToSelect != -1)
+ SetTabNo(nTabToSelect);
+
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, std::move(undoTabs), false ) );
+ }
+
+ // Update views
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
+ pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
+ pDocSh->SetDocumentModified();
+}
+
+void ScViewFunc::InsertSpecialChar( const OUString& rStr, const vcl::Font& rFont )
+{
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ const sal_Unicode* pChar = rStr.getStr();
+ ScTabViewShell* pViewShell = GetViewData().GetViewShell();
+ SvxFontItem aFontItem( rFont.GetFamilyType(),
+ rFont.GetFamilyName(),
+ rFont.GetStyleName(),
+ rFont.GetPitch(),
+ rFont.GetCharSet(),
+ ATTR_FONT );
+
+ // if string contains WEAK characters, set all fonts
+ SvtScriptType nScript;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ if ( rDoc.HasStringWeakCharacters( rStr ) )
+ nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
+ else
+ nScript = rDoc.GetStringScriptType( rStr );
+
+ SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, pViewShell->GetPool() );
+ aSetItem.PutItemForScriptType( nScript, aFontItem );
+ ApplyUserItemSet( aSetItem.GetItemSet() );
+
+ while ( *pChar )
+ pViewShell->TabKeyInput( KeyEvent( *(pChar++), vcl::KeyCode() ) );
+}
+
+void ScViewFunc::UpdateLineAttrs( SvxBorderLine& rLine,
+ const SvxBorderLine* pDestLine,
+ const SvxBorderLine* pSrcLine,
+ bool bColor )
+{
+ if ( !(pSrcLine && pDestLine) )
+ return;
+
+ if ( bColor )
+ {
+ rLine.SetColor ( pSrcLine->GetColor() );
+ rLine.SetBorderLineStyle(pDestLine->GetBorderLineStyle());
+ rLine.SetWidth ( pDestLine->GetWidth() );
+ }
+ else
+ {
+ rLine.SetColor ( pDestLine->GetColor() );
+ rLine.SetBorderLineStyle(pSrcLine->GetBorderLineStyle());
+ rLine.SetWidth ( pSrcLine->GetWidth() );
+ }
+}
+
+#define SET_LINE_ATTRIBUTES(LINE,BOXLINE) \
+ pBoxLine = aBoxItem.Get##LINE(); \
+ if ( pBoxLine ) \
+ { \
+ if ( pLine ) \
+ { \
+ UpdateLineAttrs( aLine, pBoxLine, pLine, bColorOnly ); \
+ aBoxItem.SetLine( &aLine, BOXLINE ); \
+ } \
+ else \
+ aBoxItem.SetLine( nullptr, BOXLINE ); \
+ }
+
+void ScViewFunc::SetSelectionFrameLines( const SvxBorderLine* pLine,
+ bool bColorOnly )
+{
+ // Not editable only due to a matrix? Attribute is ok anyhow.
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScPatternAttr* pSelAttrs = GetSelectionPattern();
+ const SfxItemSet& rSelItemSet = pSelAttrs->GetItemSet();
+
+ const SfxPoolItem* pBorderAttr = nullptr;
+ SfxItemState eItemState = rSelItemSet.GetItemState( ATTR_BORDER, true, &pBorderAttr );
+
+ const SfxPoolItem* pTLBRItem = nullptr;
+ SfxItemState eTLBRState = rSelItemSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem );
+
+ const SfxPoolItem* pBLTRItem = nullptr;
+ SfxItemState eBLTRState = rSelItemSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem );
+
+ // any of the lines visible?
+ if( !((eItemState != SfxItemState::DEFAULT) || (eTLBRState != SfxItemState::DEFAULT) || (eBLTRState != SfxItemState::DEFAULT)) )
+ return;
+
+ // none of the lines don't care?
+ if( (eItemState != SfxItemState::DONTCARE) && (eTLBRState != SfxItemState::DONTCARE) && (eBLTRState != SfxItemState::DONTCARE) )
+ {
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aOldSet( *rDoc.GetPool() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *rDoc.GetPool() );
+
+ SvxBorderLine aLine;
+
+ if( pBorderAttr )
+ {
+ const SvxBorderLine* pBoxLine = nullptr;
+ SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem*>(pBorderAttr) );
+ SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
+
+ // here pBoxLine is used
+ SET_LINE_ATTRIBUTES(Top,SvxBoxItemLine::TOP)
+ SET_LINE_ATTRIBUTES(Bottom,SvxBoxItemLine::BOTTOM)
+ SET_LINE_ATTRIBUTES(Left,SvxBoxItemLine::LEFT)
+ SET_LINE_ATTRIBUTES(Right,SvxBoxItemLine::RIGHT)
+
+ aBoxInfoItem.SetLine( aBoxItem.GetTop(), SvxBoxInfoItemLine::HORI );
+ aBoxInfoItem.SetLine( aBoxItem.GetLeft(), SvxBoxInfoItemLine::VERT );
+ aBoxInfoItem.ResetFlags(); // set Lines to Valid
+
+ aOldSet.Put( *pBorderAttr );
+ aNewSet.Put( aBoxItem );
+ aNewSet.Put( aBoxInfoItem );
+ }
+
+ if( pTLBRItem && static_cast<const SvxLineItem*>(pTLBRItem)->GetLine() )
+ {
+ SvxLineItem aTLBRItem( *static_cast<const SvxLineItem*>(pTLBRItem) );
+ UpdateLineAttrs( aLine, aTLBRItem.GetLine(), pLine, bColorOnly );
+ aTLBRItem.SetLine( &aLine );
+ aOldSet.Put( *pTLBRItem );
+ aNewSet.Put( aTLBRItem );
+ }
+
+ if( pBLTRItem && static_cast<const SvxLineItem*>(pBLTRItem)->GetLine() )
+ {
+ SvxLineItem aBLTRItem( *static_cast<const SvxLineItem*>(pBLTRItem) );
+ UpdateLineAttrs( aLine, aBLTRItem.GetLine(), pLine, bColorOnly );
+ aBLTRItem.SetLine( &aLine );
+ aOldSet.Put( *pBLTRItem );
+ aNewSet.Put( aBLTRItem );
+ }
+
+ ApplyAttributes( aNewSet, aOldSet );
+ }
+ else // if ( eItemState == SfxItemState::DONTCARE )
+ {
+ aFuncMark.MarkToMulti();
+ rDoc.ApplySelectionLineStyle( aFuncMark, pLine, bColorOnly );
+ }
+
+ const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+ pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab,
+ PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
+
+ pDocSh->UpdateOle(GetViewData());
+ pDocSh->SetDocumentModified();
+}
+
+#undef SET_LINE_ATTRIBUTES
+
+void ScViewFunc::SetValidation( const ScValidationData& rNew )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ sal_uInt32 nIndex = rDoc.AddValidationEntry(rNew); // for it there is no Undo
+ SfxUInt32Item aItem( ATTR_VALIDDATA, nIndex );
+
+ ApplyAttr( aItem ); // with Paint and Undo...
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun3.cxx b/sc/source/ui/view/viewfun3.cxx
new file mode 100644
index 0000000000..7a6403237b
--- /dev/null
+++ b/sc/source/ui/view/viewfun3.cxx
@@ -0,0 +1,2028 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/docfile.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/lok.hxx>
+#include <sot/formats.hxx>
+#include <sot/storage.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <tools/urlobj.hxx>
+#include <sot/exchange.hxx>
+#include <memory>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <osl/diagnose.h>
+
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <dociter.hxx>
+#include <viewfunc.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <docfunc.hxx>
+#include <undoblk.hxx>
+#include <refundo.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <transobj.hxx>
+#include <drwtrans.hxx>
+#include <chgtrack.hxx>
+#include <waitoff.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <warnbox.hxx>
+#include <drwlayer.hxx>
+#include <editable.hxx>
+#include <docuno.hxx>
+#include <clipparam.hxx>
+#include <undodat.hxx>
+#include <drawview.hxx>
+#include <cliputil.hxx>
+#include <clipoptions.hxx>
+#include <gridwin.hxx>
+#include <com/sun/star/util/XCloneable.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& action)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = action;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+// GlobalName of writer-DocShell from comphelper/classids.hxx
+
+// C U T
+
+void ScViewFunc::CutToClip()
+{
+ UpdateInputLine();
+
+ ScEditableTester aTester( this );
+ if (!aTester.IsEditable()) // selection editable?
+ {
+ ErrorMessage( aTester.GetMessageId() );
+ return;
+ }
+
+ ScRange aRange; // delete this range
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ const bool bRecord(rDoc.IsUndoEnabled()); // Undo/Redo
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) // mark the range if not marked yet
+ {
+ DoneBlockMode();
+ InitOwnBlockMode( aRange );
+ rMark.SetMarkArea( aRange );
+ MarkDataChanged();
+ }
+
+ CopyToClip( nullptr, true, false, true/*bIncludeObjects*/ ); // copy to clipboard
+
+ ScAddress aOldEnd( aRange.aEnd ); // combined cells in this range?
+ rDoc.ExtendMerge( aRange, true );
+
+ ScDocumentUniquePtr pUndoDoc;
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndoSelected( rDoc, rMark );
+ // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
+ ScRange aCopyRange = aRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(rDoc.GetTableCount()-1);
+ rDoc.CopyToDocument( aCopyRange, (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
+ rDoc.BeginDrawUndo();
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ pDocSh->UpdatePaintExt( nExtFlags, aRange );
+
+ rMark.MarkToMulti();
+ rDoc.DeleteSelection( InsertDeleteFlags::ALL, rMark );
+ rDoc.DeleteObjectsInSelection( rMark );
+ rMark.MarkToSimple();
+
+ if ( !AdjustRowHeight( aRange.aStart.Row(), aRange.aEnd.Row(), true ) )
+ pDocSh->PostPaint( aRange, PaintPartFlags::Grid, nExtFlags );
+
+ if ( bRecord ) // Draw-Undo now available
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoCut>( pDocSh, aRange, aOldEnd, rMark, std::move(pUndoDoc) ) );
+
+ aModificator.SetDocumentModified();
+ pDocSh->UpdateOle(GetViewData());
+
+ CellContentChanged();
+
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "CUT");
+ }
+ else
+ ErrorMessage( STR_NOMULTISELECT );
+}
+
+// C O P Y
+
+bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit )
+{
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aRange );
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bDone = false;
+
+ if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
+ {
+ ScRangeList aRangeList( aRange );
+ bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit );
+ }
+ else if (eMarkType == SC_MARK_MULTI)
+ {
+ ScRangeList aRangeList;
+ rMark.MarkToSimple();
+ rMark.FillRangeListWithMarks(&aRangeList, false);
+ bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit );
+ }
+ else
+ {
+ if (!bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+ }
+ if( !bCut ){
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "COPY");
+ }
+ return bDone;
+}
+
+// Copy the content of the Range into clipboard.
+bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit )
+{
+ if ( rRanges.empty() )
+ return false;
+ if ( bStopEdit )
+ UpdateInputLine();
+
+ bool bDone;
+ if (rRanges.size() > 1) // isMultiRange
+ bDone = CopyToClipMultiRange(pClipDoc, rRanges, bCut, bApi, bIncludeObjects);
+ else
+ bDone = CopyToClipSingleRange(pClipDoc, rRanges, bCut, bIncludeObjects);
+
+ return bDone;
+}
+
+bool ScViewFunc::CopyToClipSingleRange( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bIncludeObjects )
+{
+ ScRange aRange = rRanges[0];
+ ScClipParam aClipParam( aRange, bCut );
+ aClipParam.maRanges = rRanges;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if (rDoc.HasSelectedBlockMatrixFragment( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rMark ) )
+ return false;
+
+ std::shared_ptr<ScDocument> pSysClipDoc;
+ if ( !pClipDoc ) // no clip doc specified
+ {
+ // Create one (deleted by ScTransferObj), and copy into system.
+ pSysClipDoc = std::make_shared<ScDocument>( SCDOCMODE_CLIP );
+ pClipDoc = pSysClipDoc.get();
+ }
+ if ( !bCut )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut();
+ }
+
+ if ( pSysClipDoc && bIncludeObjects )
+ {
+ bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange );
+ // Update ScGlobal::xDrawClipDocShellRef.
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle, pSysClipDoc ) );
+ }
+
+ // is this necessary?, will setting the doc id upset the
+ // following paste operation with range? would be nicer to just set this always
+ // and lose the 'if' above
+ aClipParam.setSourceDocID( rDoc.GetDocumentID() );
+
+ if (ScDocShell* pObjectShell = rDoc.GetDocumentShell())
+ {
+ // Copy document properties from pObjectShell to pClipDoc (to its clip options, as it has no object shell).
+ uno::Reference<util::XCloneable> xCloneable(pObjectShell->getDocProperties(), uno::UNO_QUERY_THROW);
+ std::unique_ptr<ScClipOptions> pOptions(new ScClipOptions);
+ pOptions->m_xDocumentProperties.set(xCloneable->createClone(), uno::UNO_QUERY);
+ pClipDoc->SetClipOptions(std::move(pOptions));
+ }
+
+ rDoc.CopyToClip( aClipParam, pClipDoc, &rMark, false, bIncludeObjects );
+ if (ScDrawLayer* pDrawLayer = pClipDoc->GetDrawLayer())
+ {
+ ScClipParam& rClipDocClipParam = pClipDoc->GetClipParam();
+ ScRangeListVector& rRangesVector = rClipDocClipParam.maProtectedChartRangesVector;
+ SCTAB nTabCount = pClipDoc->GetTableCount();
+ for ( SCTAB nTab = 0; nTab < nTabCount; ++nTab )
+ {
+ SdrPage* pPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
+ if ( pPage )
+ {
+ ScChartHelper::FillProtectedChartRangesVector( rRangesVector, rDoc, pPage );
+ }
+ }
+ }
+
+ if ( pSysClipDoc )
+ {
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+ ScGlobal::SetClipDocName( rDoc.GetDocumentShell()->GetTitle( SFX_TITLE_FULLNAME ) );
+ }
+ pClipDoc->ExtendMerge( aRange, true );
+
+ if ( pSysClipDoc )
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj(new ScTransferObj( pSysClipDoc, std::move(aObjDesc) ));
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() );
+ pTransferObj->SetDrawPersist( aPersistRef );// keep persist for ole objects alive
+ }
+ pTransferObj->CopyToClipboard( GetActiveWin() );
+ }
+
+ return true;
+}
+
+bool ScViewFunc::CopyToClipMultiRange( const ScDocument* pInputClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects )
+{
+ if (bCut)
+ {
+ // We don't support cutting of multi-selections.
+ if (!bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+ return false;
+ }
+ if (pInputClipDoc)
+ {
+ // TODO: What's this for?
+ if (!bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+ return false;
+ }
+
+ ScClipParam aClipParam( rRanges[0], bCut );
+ aClipParam.maRanges = rRanges;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bDone = false;
+ bool bSuccess = false;
+ aClipParam.mbCutMode = false;
+
+ do
+ {
+ ScDocumentUniquePtr pDocClip(new ScDocument(SCDOCMODE_CLIP));
+
+ // Check for geometrical feasibility of the ranges.
+ bool bValidRanges = true;
+ ScRange const * p = &aClipParam.maRanges.front();
+ SCCOL nPrevColDelta = 0;
+ SCROW nPrevRowDelta = 0;
+ SCCOL nPrevCol = p->aStart.Col();
+ SCROW nPrevRow = p->aStart.Row();
+ SCCOL nPrevColSize = p->aEnd.Col() - p->aStart.Col() + 1;
+ SCROW nPrevRowSize = p->aEnd.Row() - p->aStart.Row() + 1;
+ for ( size_t i = 1; i < aClipParam.maRanges.size(); ++i )
+ {
+ p = &aClipParam.maRanges[i];
+ if ( rDoc.HasSelectedBlockMatrixFragment(
+ p->aStart.Col(), p->aStart.Row(), p->aEnd.Col(), p->aEnd.Row(), rMark) )
+ {
+ if (!bApi)
+ ErrorMessage(STR_MATRIXFRAGMENTERR);
+ return false;
+ }
+
+ SCCOL nColDelta = p->aStart.Col() - nPrevCol;
+ SCROW nRowDelta = p->aStart.Row() - nPrevRow;
+
+ if ((nColDelta && nRowDelta) || (nPrevColDelta && nRowDelta) || (nPrevRowDelta && nColDelta))
+ {
+ bValidRanges = false;
+ break;
+ }
+
+ if (aClipParam.meDirection == ScClipParam::Unspecified)
+ {
+ if (nColDelta)
+ aClipParam.meDirection = ScClipParam::Column;
+ if (nRowDelta)
+ aClipParam.meDirection = ScClipParam::Row;
+ }
+
+ SCCOL nColSize = p->aEnd.Col() - p->aStart.Col() + 1;
+ SCROW nRowSize = p->aEnd.Row() - p->aStart.Row() + 1;
+
+ if (aClipParam.meDirection == ScClipParam::Column && nRowSize != nPrevRowSize)
+ {
+ // column-oriented ranges must have identical row size.
+ bValidRanges = false;
+ break;
+ }
+ if (aClipParam.meDirection == ScClipParam::Row && nColSize != nPrevColSize)
+ {
+ // likewise, row-oriented ranges must have identical
+ // column size.
+ bValidRanges = false;
+ break;
+ }
+
+ nPrevCol = p->aStart.Col();
+ nPrevRow = p->aStart.Row();
+ nPrevColDelta = nColDelta;
+ nPrevRowDelta = nRowDelta;
+ nPrevColSize = nColSize;
+ nPrevRowSize = nRowSize;
+ }
+ if (!bValidRanges)
+ break;
+ rDoc.CopyToClip(aClipParam, pDocClip.get(), &rMark, false, bIncludeObjects );
+
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut(); // no more cut-mode
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ // maSize is set in ScTransferObj ctor
+
+ rtl::Reference<ScTransferObj> pTransferObj(new ScTransferObj( std::move(pDocClip), std::move(aObjDesc) ));
+ if ( ScGlobal::xDrawClipDocShellRef.is() )
+ {
+ SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() );
+ pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive
+ }
+ pTransferObj->CopyToClipboard( GetActiveWin() ); // system clipboard
+
+ bSuccess = true;
+ }
+ while (false);
+
+ if (!bSuccess && !bApi)
+ ErrorMessage(STR_NOMULTISELECT);
+
+ bDone = bSuccess;
+
+ return bDone;
+}
+
+rtl::Reference<ScTransferObj> ScViewFunc::CopyToTransferable()
+{
+ ScRange aRange;
+ auto eMarkType = GetViewData().GetSimpleArea( aRange );
+ if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rDoc.HasSelectedBlockMatrixFragment(
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(),
+ rMark ) )
+ {
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); // create one (deleted by ScTransferObj)
+
+ bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange, &rMark );
+ ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) );
+
+ ScClipParam aClipParam(aRange, false);
+ rDoc.CopyToClip(aClipParam, pClipDoc.get(), &rMark, false, true);
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+ pClipDoc->ExtendMerge( aRange, true );
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ TransferableObjectDescriptor aObjDesc;
+ pDocSh->FillTransferableObjectDescriptor( aObjDesc );
+ aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass();
+ return new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
+ }
+ }
+
+ return nullptr;
+}
+
+// P A S T E
+
+void ScViewFunc::PasteDraw()
+{
+ ScViewData& rViewData = GetViewData();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ vcl::Window* pWin = GetActiveWin();
+ Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY,
+ rViewData.GetActivePart() ) );
+ const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin()));
+ if (pDrawClip)
+ {
+ const OUString& aSrcShellID = pDrawClip->GetShellID();
+ OUString aDestShellID = SfxObjectShell::CreateShellID(rViewData.GetDocShell());
+ PasteDraw(aPos, pDrawClip->GetModel(), false, aSrcShellID, aDestShellID);
+ }
+}
+
+void ScViewFunc::PasteFromSystem()
+{
+ UpdateInputLine();
+
+ vcl::Window* pWin = GetActiveWin();
+ css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(ScTabViewShell::GetClipData(pWin));
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(xTransferable2);
+ // keep a reference in case the clipboard is changed during PasteFromClip
+ const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(xTransferable2);
+ if (pOwnClip)
+ {
+ PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ true ); // allow warning dialog
+ }
+ else if (pDrawClip)
+ PasteDraw();
+ else
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+
+ {
+ SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8");
+ SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5");
+
+ SotClipboardFormatId nFormat; // output param for GetExchangeAction
+ sal_uInt8 nEventAction; // output param for GetExchangeAction
+
+ uno::Reference<css::datatransfer::XTransferable> xTransferable( aDataHelper.GetXTransferable() );
+ sal_uInt8 nAction = SotExchange::GetExchangeAction(
+ aDataHelper.GetDataFlavorExVector(),
+ SotExchangeDest::SCDOC_FREE_AREA,
+ EXCHG_IN_ACTION_COPY,
+ EXCHG_IN_ACTION_DEFAULT,
+ nFormat, nEventAction, SotClipboardFormatId::NONE,
+ &xTransferable );
+
+ if ( nAction != EXCHG_INOUT_ACTION_NONE )
+ {
+ switch( nAction )
+ {
+ case EXCHG_OUT_ACTION_INSERT_SVXB:
+ case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE:
+ case EXCHG_OUT_ACTION_INSERT_BITMAP:
+ case EXCHG_OUT_ACTION_INSERT_GRAPH:
+ // SotClipboardFormatId::BITMAP
+ // SotClipboardFormatId::PNG
+ // SotClipboardFormatId::GDIMETAFILE
+ // SotClipboardFormatId::SVXB
+ PasteFromSystem(nFormat);
+ break;
+ default:
+ nAction = EXCHG_INOUT_ACTION_NONE;
+ }
+ }
+
+ if ( nAction == EXCHG_INOUT_ACTION_NONE )
+ {
+ // first SvDraw-model, then drawing
+ // (only one drawing is allowed)
+
+ if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ))
+ {
+ // special case for tables from drawing
+ if( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) )
+ {
+ PasteFromSystem( SotClipboardFormatId::RTF );
+ }
+ else if( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )
+ {
+ PasteFromSystem( SotClipboardFormatId::RICHTEXT );
+ }
+ else
+ {
+ PasteFromSystem( SotClipboardFormatId::DRAWING );
+ }
+ }
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ))
+ {
+ // If it's a Writer object, insert RTF instead of OLE
+
+ // Else, if the class id is all-zero, and SYLK is available,
+ // it probably is spreadsheet cells that have been put
+ // on the clipboard by OOo, so use the SYLK. (fdo#31077)
+
+ bool bDoRtf = false;
+ TransferableObjectDescriptor aObjDesc;
+ if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) )
+ {
+ bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
+ aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
+ && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) );
+ }
+ if ( bDoRtf )
+ PasteFromSystem( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT );
+ else if ( aObjDesc.maClassName == SvGlobalName( 0,0,0,0,0,0,0,0,0,0,0 )
+ && aDataHelper.HasFormat( SotClipboardFormatId::SYLK ))
+ PasteFromSystem( SotClipboardFormatId::SYLK );
+ else
+ PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE );
+ }
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ))
+ PasteFromSystem( SotClipboardFormatId::LINK_SOURCE );
+ // the following format can not affect scenario from #89579#
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ))
+ PasteFromSystem( SotClipboardFormatId::EMBEDDED_OBJ_OLE );
+ // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL)
+ else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats
+ PasteFromSystem(nBiff8);
+ else if (aDataHelper.HasFormat(nBiff5))
+ PasteFromSystem(nBiff5);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF))
+ PasteFromSystem(SotClipboardFormatId::RTF);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT))
+ PasteFromSystem(SotClipboardFormatId::RICHTEXT);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML))
+ PasteFromSystem(SotClipboardFormatId::HTML);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP))
+ PasteFromSystem(SotClipboardFormatId::BITMAP);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
+ PasteFromSystem(SotClipboardFormatId::HTML_SIMPLE);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK))
+ PasteFromSystem(SotClipboardFormatId::SYLK);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC))
+ PasteFromSystem(SotClipboardFormatId::STRING_TSVC);
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING))
+ PasteFromSystem(SotClipboardFormatId::STRING);
+ // xxx_OLE formats come last, like in SotExchange tables
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ))
+ PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE_OLE );
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ))
+ PasteFromSystem( SotClipboardFormatId::LINK_SOURCE_OLE );
+ }
+ }
+ }
+ // no exception-> SID_PASTE has FastCall-flag from idl
+ // will be called in case of empty clipboard (#42531#)
+}
+
+void ScViewFunc::PasteFromTransferable( const uno::Reference<datatransfer::XTransferable>& rxTransferable )
+{
+ if (auto pOwnClip = dynamic_cast<ScTransferObj*>(rxTransferable.get()))
+ {
+ PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ true ); // allow warning dialog
+ }
+ else if (auto pDrawClip = dynamic_cast<ScDrawTransferObj*>(rxTransferable.get()))
+ {
+ ScViewData& rViewData = GetViewData();
+ SCCOL nPosX = rViewData.GetCurX();
+ SCROW nPosY = rViewData.GetCurY();
+ vcl::Window* pWin = GetActiveWin();
+ Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, rViewData.GetActivePart() ) );
+ PasteDraw(
+ aPos, pDrawClip->GetModel(), false,
+ pDrawClip->GetShellID(), SfxObjectShell::CreateShellID(rViewData.GetDocShell()));
+ }
+ else
+ {
+ TransferableDataHelper aDataHelper( rxTransferable );
+ SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8");
+ SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5");
+ SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE;
+ // first SvDraw-model, then drawing
+ // (only one drawing is allowed)
+
+ if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ))
+ nFormatId = SotClipboardFormatId::DRAWING;
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::SVXB ))
+ nFormatId = SotClipboardFormatId::SVXB;
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE ))
+ {
+ // If it's a Writer object, insert RTF instead of OLE
+ bool bDoRtf = false;
+ TransferableObjectDescriptor aObjDesc;
+ if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) )
+ {
+ bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) ||
+ aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) )
+ && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ));
+ }
+ if ( bDoRtf )
+ nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT;
+ else
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE;
+ }
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE ))
+ nFormatId = SotClipboardFormatId::LINK_SOURCE;
+ // the following format can not affect scenario from #89579#
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ))
+ nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE;
+ // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL)
+ else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats
+ nFormatId = nBiff8;
+ else if (aDataHelper.HasFormat(nBiff5))
+ nFormatId = nBiff5;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF))
+ nFormatId = SotClipboardFormatId::RTF;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT))
+ nFormatId = SotClipboardFormatId::RICHTEXT;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML))
+ nFormatId = SotClipboardFormatId::HTML;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE))
+ nFormatId = SotClipboardFormatId::HTML_SIMPLE;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK))
+ nFormatId = SotClipboardFormatId::SYLK;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC))
+ nFormatId = SotClipboardFormatId::STRING_TSVC;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING))
+ nFormatId = SotClipboardFormatId::STRING;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::GDIMETAFILE))
+ nFormatId = SotClipboardFormatId::GDIMETAFILE;
+ else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP))
+ nFormatId = SotClipboardFormatId::BITMAP;
+ // xxx_OLE formats come last, like in SotExchange tables
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ))
+ nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE;
+ else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ))
+ nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE;
+ else
+ return;
+
+ PasteDataFormat( nFormatId, aDataHelper.GetTransferable(),
+ GetViewData().GetCurX(), GetViewData().GetCurY(), nullptr );
+ }
+}
+
+bool ScViewFunc::PasteFromSystem( SotClipboardFormatId nFormatId, bool bApi )
+{
+ UpdateInputLine();
+
+ bool bRet = true;
+ vcl::Window* pWin = GetActiveWin();
+ // keep a reference in case the clipboard is changed during PasteFromClip
+ const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin));
+ if ( nFormatId == SotClipboardFormatId::NONE && pOwnClip )
+ {
+ PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ !bApi ); // allow warning dialog
+ }
+ else
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) );
+ if ( !aDataHelper.GetTransferable().is() )
+ return false;
+
+ SCCOL nPosX = 0;
+ SCROW nPosY = 0;
+
+ ScViewData& rViewData = GetViewData();
+ ScRange aRange;
+ if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ nPosX = aRange.aStart.Col();
+ nPosY = aRange.aStart.Row();
+ }
+ else
+ {
+ nPosX = rViewData.GetCurX();
+ nPosY = rViewData.GetCurY();
+ }
+
+ bRet = PasteDataFormat( nFormatId, aDataHelper.GetTransferable(),
+ nPosX, nPosY,
+ nullptr, false, !bApi ); // allow warning dialog
+
+ if ( !bRet && !bApi )
+ {
+ ErrorMessage(STR_PASTE_ERROR);
+ }
+ else if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = rViewData.GetViewShell();
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, true /* bColumns */, true /* bRows */,
+ true /* bSizes */, false /* bHidden */, false /* bFiltered */, false /* bGroups */, rViewData.GetTabNo());
+ }
+ }
+ return bRet;
+}
+
+// P A S T E
+
+bool ScViewFunc::PasteOnDrawObjectLinked(
+ const uno::Reference<datatransfer::XTransferable>& rxTransferable,
+ SdrObject& rHitObj)
+{
+ TransferableDataHelper aDataHelper( rxTransferable );
+
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
+ {
+ tools::SvRef<SotTempStream> xStm;
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if( pScDrawView && aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) )
+ {
+ Graphic aGraphic;
+ TypeSerializer aSerializer(*xStm);
+ aSerializer.readGraphic(aGraphic);
+
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ if(pScDrawView->ApplyGraphicToObject( rHitObj, aGraphic, aBeginUndo, "" ))
+ {
+ return true;
+ }
+ }
+ }
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
+ {
+ GDIMetaFile aMtf;
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if( pScDrawView && aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) )
+ {
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aMtf), aBeginUndo, "" ))
+ {
+ return true;
+ }
+ }
+ }
+ else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) || aDataHelper.HasFormat( SotClipboardFormatId::PNG ) )
+ {
+ BitmapEx aBmpEx;
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if( pScDrawView && aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) )
+ {
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+
+ if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aBmpEx), aBeginUndo, "" ))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bool lcl_SelHasAttrib( const ScDocument& rDoc, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rTabSelection, HasAttrFlags nMask )
+{
+ return std::any_of(rTabSelection.begin(), rTabSelection.end(),
+ [&](const SCTAB& rTab) { return rDoc.HasAttrib( nCol1, nRow1, rTab, nCol2, nRow2, rTab, nMask ); });
+}
+
+// paste into sheet:
+
+// internal paste
+
+namespace {
+
+bool checkDestRangeForOverwrite(const ScRangeList& rDestRanges, const ScDocument& rDoc, const ScMarkData& rMark, weld::Window* pParentWnd)
+{
+ bool bIsEmpty = true;
+ size_t nRangeSize = rDestRanges.size();
+ for (const auto& rTab : rMark)
+ {
+ for (size_t i = 0; i < nRangeSize && bIsEmpty; ++i)
+ {
+ const ScRange& rRange = rDestRanges[i];
+ bIsEmpty = rDoc.IsBlockEmpty(
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rTab );
+ }
+ if (!bIsEmpty)
+ break;
+ }
+
+ if (!bIsEmpty)
+ {
+ ScReplaceWarnBox aBox(pParentWnd);
+ if (aBox.run() != RET_YES)
+ {
+ // changing the configuration is within the ScReplaceWarnBox
+ return false;
+ }
+ }
+ return true;
+}
+
+}
+
+bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc,
+ ScPasteFunc nFunction, bool bSkipEmptyCells,
+ bool bTranspose, bool bAsLink,
+ InsCellCmd eMoveMode, InsertDeleteFlags nUndoExtraFlags,
+ bool bAllowDialogs )
+{
+ if (!pClipDoc)
+ {
+ OSL_FAIL("PasteFromClip: pClipDoc=0 not allowed");
+ return false;
+ }
+
+ if (GetViewData().SelectionForbidsPaste(pClipDoc))
+ return false;
+
+ // undo: save all or no content
+ InsertDeleteFlags nContFlags = InsertDeleteFlags::NONE;
+ if (nFlags & InsertDeleteFlags::CONTENTS)
+ nContFlags |= InsertDeleteFlags::CONTENTS;
+ if (nFlags & InsertDeleteFlags::ATTRIB)
+ nContFlags |= InsertDeleteFlags::ATTRIB;
+ // move attributes to undo without copying them from clip to doc
+ InsertDeleteFlags nUndoFlags = nContFlags;
+ if (nUndoExtraFlags & InsertDeleteFlags::ATTRIB)
+ nUndoFlags |= InsertDeleteFlags::ATTRIB;
+ // do not copy note captions into undo document
+ nUndoFlags |= InsertDeleteFlags::NOCAPTIONS;
+
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ if (rClipParam.isMultiRange())
+ {
+ // Source data is multi-range.
+ return PasteMultiRangesFromClip(nFlags, pClipDoc, nFunction, bSkipEmptyCells, bTranspose,
+ bAsLink, bAllowDialogs, eMoveMode, nUndoFlags);
+ }
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (rMark.IsMultiMarked())
+ {
+ // Source data is single-range but destination is multi-range.
+ return PasteFromClipToMultiRanges(
+ nFlags, pClipDoc, nFunction, bSkipEmptyCells, bTranspose, bAsLink, bAllowDialogs,
+ eMoveMode, nUndoFlags);
+ }
+
+ bool bCutMode = pClipDoc->IsCutMode(); // if transposing, take from original clipdoc
+ bool bIncludeFiltered = bCutMode;
+
+ // paste drawing: also if InsertDeleteFlags::NOTE is set (to create drawing layer for note captions)
+ bool bPasteDraw = ( pClipDoc->GetDrawLayer() && ( nFlags & (InsertDeleteFlags::OBJECTS|InsertDeleteFlags::NOTE) ) );
+
+ ScDocShellRef aTransShellRef; // for objects in xTransClip - must remain valid as long as xTransClip
+ ScDocument* pOrigClipDoc = nullptr;
+ ScDocumentUniquePtr xTransClip;
+ if ( bTranspose )
+ {
+ SCCOL nX;
+ SCROW nY;
+ // include filtered rows until TransposeClip can skip them
+ pClipDoc->GetClipArea( nX, nY, true );
+ if ( nY > static_cast<sal_Int32>(pClipDoc->MaxCol()) ) // too many lines for transpose
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+ pOrigClipDoc = pClipDoc; // refs
+
+ if ( bPasteDraw )
+ {
+ aTransShellRef = new ScDocShell; // DocShell needs a Ref immediately
+ aTransShellRef->DoInitNew();
+ }
+ ScDrawLayer::SetGlobalDrawPersist( aTransShellRef.get() );
+
+ xTransClip.reset( new ScDocument( SCDOCMODE_CLIP ));
+ pClipDoc->TransposeClip(xTransClip.get(), nFlags, bAsLink, bIncludeFiltered);
+ pClipDoc = xTransClip.get();
+
+ ScDrawLayer::SetGlobalDrawPersist(nullptr);
+ }
+
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ SCCOL nClipSizeX;
+ SCROW nClipSizeY;
+ pClipDoc->GetClipArea( nClipSizeX, nClipSizeY, true ); // size in clipboard doc
+
+ // size in target doc: include filtered rows only if CutMode is set
+ SCCOL nDestSizeX;
+ SCROW nDestSizeY;
+ pClipDoc->GetClipArea( nDestSizeX, nDestSizeY, bIncludeFiltered );
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ const bool bRecord(rDoc.IsUndoEnabled());
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScRange aMarkRange;
+ ScMarkData aFilteredMark( rMark); // local copy for all modifications
+ ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange, aFilteredMark);
+ bool bMarkIsFiltered = (eMarkType == SC_MARK_SIMPLE_FILTERED);
+ bool bNoPaste = ((eMarkType != SC_MARK_SIMPLE && !bMarkIsFiltered) ||
+ (bMarkIsFiltered && (eMoveMode != INS_NONE || bAsLink)));
+
+ if (!bNoPaste)
+ {
+ if (!rMark.IsMarked())
+ {
+ // Create a selection with clipboard row count and check that for
+ // filtered.
+ nStartCol = GetViewData().GetCurX();
+ nStartRow = GetViewData().GetCurY();
+ nStartTab = GetViewData().GetTabNo();
+ nEndCol = nStartCol + nDestSizeX;
+ nEndRow = nStartRow + nDestSizeY;
+ nEndTab = nStartTab;
+ aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ if (ScViewUtil::HasFiltered(aMarkRange, rDoc))
+ {
+ bMarkIsFiltered = true;
+ // Fit to clipboard's row count unfiltered rows. If there is no
+ // fit assume that pasting is not possible. Note that nDestSizeY is
+ // size-1 (difference).
+ if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1))
+ bNoPaste = true;
+ }
+ aFilteredMark.SetMarkArea( aMarkRange);
+ }
+ else
+ {
+ // Expand the marked area when the destination area is larger than the
+ // current selection, to get the undo do the right thing. (i#106711)
+ ScRange aRange = aFilteredMark.GetMarkArea();
+ if( (aRange.aEnd.Col() - aRange.aStart.Col()) < nDestSizeX )
+ {
+ aRange.aEnd.SetCol(aRange.aStart.Col() + nDestSizeX);
+ aFilteredMark.SetMarkArea(aRange);
+ }
+ }
+ }
+
+ if (bNoPaste)
+ {
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ SCROW nUnfilteredRows = aMarkRange.aEnd.Row() - aMarkRange.aStart.Row() + 1;
+ ScRangeList aRangeList;
+ if (bMarkIsFiltered)
+ {
+ ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc);
+ aFilteredMark.FillRangeListWithMarks( &aRangeList, false);
+ nUnfilteredRows = 0;
+ size_t ListSize = aRangeList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScRange & r = aRangeList[i];
+ nUnfilteredRows += r.aEnd.Row() - r.aStart.Row() + 1;
+ }
+#if 0
+ /* This isn't needed but could be a desired restriction. */
+ // For filtered, destination rows have to be an exact multiple of
+ // source rows. Note that nDestSizeY is size-1 (difference), so
+ // nDestSizeY==0 fits always.
+ if ((nUnfilteredRows % (nDestSizeY+1)) != 0)
+ {
+ /* FIXME: this should be a more descriptive error message then. */
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+#endif
+ }
+
+ // Also for a filtered selection the area is used, for undo et al.
+ if ( aFilteredMark.IsMarked() || bMarkIsFiltered )
+ {
+ aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ SCCOL nBlockAddX = nEndCol-nStartCol;
+ SCROW nBlockAddY = nEndRow-nStartRow;
+
+ // request, if the selection is greater than one row/column, but smaller
+ // as the Clipboard (then inserting is done beyond the selection)
+
+ // ClipSize is not size, but difference
+ if ( ( nBlockAddX != 0 && nBlockAddX < nDestSizeX ) ||
+ ( nBlockAddY != 0 && nBlockAddY < nDestSizeY ) ||
+ ( bMarkIsFiltered && nUnfilteredRows < nDestSizeY+1 ) )
+ {
+ ScWaitCursorOff aWaitOff( GetFrameWin() );
+ OUString aMessage = ScResId( STR_PASTE_BIGGER );
+
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ aMessage));
+ xQueryBox->set_default_response(RET_NO);
+ if (xQueryBox->run() != RET_YES)
+ {
+ return false;
+ }
+ }
+
+ if (nBlockAddX <= nDestSizeX)
+ nEndCol = nStartCol + nDestSizeX;
+
+ if (nBlockAddY <= nDestSizeY)
+ {
+ nEndRow = nStartRow + nDestSizeY;
+ if (bMarkIsFiltered || nEndRow > aMarkRange.aEnd.Row())
+ {
+ // Same as above if nothing was marked: re-fit selection to
+ // unfiltered rows. Extending the selection actually may
+ // introduce filtered rows where there weren't any before, so
+ // we also need to test for that.
+ aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ if (bMarkIsFiltered || ScViewUtil::HasFiltered(aMarkRange, rDoc))
+ {
+ bMarkIsFiltered = true;
+ // Worst case: all rows up to the end of the sheet are filtered.
+ if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+ }
+ aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
+ aFilteredMark.SetMarkArea( aMarkRange);
+ if (bMarkIsFiltered)
+ {
+ ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc);
+ aFilteredMark.FillRangeListWithMarks( &aRangeList, true);
+ }
+ }
+ }
+ }
+ else
+ {
+ nStartCol = GetViewData().GetCurX();
+ nStartRow = GetViewData().GetCurY();
+ nStartTab = GetViewData().GetTabNo();
+ nEndCol = nStartCol + nDestSizeX;
+ nEndRow = nStartRow + nDestSizeY;
+ nEndTab = nStartTab;
+ }
+
+ bool bOffLimits = !rDoc.ValidCol(nEndCol) || !rDoc.ValidRow(nEndRow);
+
+ // target-range, as displayed:
+ ScRange aUserRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab );
+
+ // should lines be inserted?
+ // ( too large nEndCol/nEndRow are detected below)
+ bool bInsertCells = ( eMoveMode != INS_NONE && !bOffLimits );
+ if ( bInsertCells )
+ {
+ // Instead of EnterListAction, the paste undo action is merged into the
+ // insert action, so Repeat can insert the right cells
+
+ MarkRange( aUserRange ); // set through CopyFromClip
+
+ // CutMode is reset on insertion of cols/rows but needed again on cell move
+ bool bCut = pClipDoc->IsCutMode();
+ if (!InsertCells( eMoveMode, bRecord, true )) // is inserting possible?
+ {
+ return false;
+ // #i21036# EnterListAction isn't used, and InsertCells doesn't insert
+ // its undo action on failure, so no undo handling is needed here
+ }
+ if ( bCut )
+ pClipDoc->SetCutMode( bCut );
+ }
+ else if (!bOffLimits)
+ {
+ bool bAskIfNotEmpty = bAllowDialogs &&
+ ( nFlags & InsertDeleteFlags::CONTENTS ) &&
+ nFunction == ScPasteFunc::NONE &&
+ SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
+ if ( bAskIfNotEmpty )
+ {
+ ScRangeList aTestRanges(aUserRange);
+ if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aFilteredMark, GetViewData().GetDialogParent()))
+ return false;
+ }
+ }
+
+ SCCOL nClipStartX; // enlarge clipboard-range
+ SCROW nClipStartY;
+ pClipDoc->GetClipStart( nClipStartX, nClipStartY );
+ SCCOL nUndoEndCol = nClipStartX + nClipSizeX;
+ SCROW nUndoEndRow = nClipStartY + nClipSizeY; // end of source area in clipboard document
+ bool bClipOver = false;
+ // #i68690# ExtendMerge for the clip doc must be called with the clipboard's sheet numbers.
+ // The same end column/row can be used for all calls because the clip doc doesn't contain
+ // content outside the clip area.
+ for (SCTAB nClipTab=0; nClipTab < pClipDoc->GetTableCount(); nClipTab++)
+ if ( pClipDoc->HasTable(nClipTab) )
+ if ( pClipDoc->ExtendMerge( nClipStartX,nClipStartY, nUndoEndCol,nUndoEndRow, nClipTab ) )
+ bClipOver = true;
+ nUndoEndCol -= nClipStartX + nClipSizeX;
+ nUndoEndRow -= nClipStartY + nClipSizeY; // now contains only the difference added by ExtendMerge
+ nUndoEndCol = sal::static_int_cast<SCCOL>( nUndoEndCol + nEndCol );
+ nUndoEndRow = sal::static_int_cast<SCROW>( nUndoEndRow + nEndRow ); // destination area, expanded for merged cells
+
+ if (nUndoEndCol>pClipDoc->MaxCol() || nUndoEndRow>pClipDoc->MaxRow())
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ rDoc.ExtendMergeSel( nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark );
+
+ // check cell-protection
+
+ ScEditableTester aTester( rDoc, nStartTab, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return false;
+ }
+
+ //! check overlapping
+ //! just check truly intersection !!!!!!!
+
+ ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
+ if ( bRecord )
+ {
+ OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pUndoMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+ }
+
+ if (bClipOver)
+ if (lcl_SelHasAttrib( rDoc, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark, HasAttrFlags::Overlapped ))
+ { // "Cell merge not possible if cells already merged"
+ ScDocAttrIterator aIter( rDoc, nStartTab, nStartCol, nStartRow, nUndoEndCol, nUndoEndRow );
+ const ScPatternAttr* pPattern = nullptr;
+ SCCOL nCol = -1;
+ SCROW nRow1 = -1;
+ SCROW nRow2 = -1;
+ while ( ( pPattern = aIter.GetNext( nCol, nRow1, nRow2 ) ) != nullptr )
+ {
+ const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
+ const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
+ if (rMergeFlag.IsMerged() || rMergeFlagAttr.IsOverlapped())
+ {
+ ScRange aRange(nCol, nRow1, nStartTab);
+ rDoc.ExtendOverlapped(aRange);
+ rDoc.ExtendMerge(aRange, true);
+ rDocFunc.UnmergeCells(aRange, bRecord, nullptr /*TODO: should pass combined UndoDoc if bRecord*/);
+ }
+ }
+ }
+
+ if ( !bCutMode )
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut(); // no more cut-mode
+ }
+
+ bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() );
+ bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() );
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScDocument> pRefUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndoSelected( rDoc, aFilteredMark, bColInfo, bRowInfo );
+
+ // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
+ SCTAB nTabCount = rDoc.GetTableCount();
+ rDoc.CopyToDocument( nStartCol, nStartRow, 0, nUndoEndCol, nUndoEndRow, nTabCount-1,
+ nUndoFlags, false, *pUndoDoc );
+
+ if ( bCutMode )
+ {
+ pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+ }
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab ); // content before the change
+
+ if (GetViewData().IsActive())
+ {
+ DoneBlockMode();
+ InitOwnBlockMode( aUserRange );
+ }
+ rMark.SetMarkArea( aUserRange );
+ MarkDataChanged();
+
+ // copy from clipboard
+ // save original data in case of calculation
+
+ ScDocumentUniquePtr pMixDoc;
+ if (nFunction != ScPasteFunc::NONE)
+ {
+ bSkipEmptyCells = false;
+ if ( nFlags & InsertDeleteFlags::CONTENTS )
+ {
+ pMixDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pMixDoc->InitUndo( rDoc, nStartTab, nEndTab );
+ rDoc.CopyToDocument(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
+ InsertDeleteFlags::CONTENTS, false, *pMixDoc);
+ }
+ }
+
+ /* Make draw layer and start drawing undo.
+ - Needed before AdjustBlockHeight to track moved drawing objects.
+ - Needed before rDoc.CopyFromClip to track inserted note caption objects.
+ */
+ if ( bPasteDraw )
+ pDocSh->MakeDrawLayer();
+ if ( bRecord )
+ rDoc.BeginDrawUndo();
+
+ InsertDeleteFlags nNoObjFlags = nFlags & ~InsertDeleteFlags::OBJECTS;
+ if (!bAsLink)
+ {
+ // copy normally (original range)
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags,
+ pRefUndoDoc.get(), pClipDoc, true, false, bIncludeFiltered,
+ bSkipEmptyCells, (bMarkIsFiltered ? &aRangeList : nullptr) );
+
+ // adapt refs manually in case of transpose
+ if ( bTranspose && bCutMode && (nFlags & InsertDeleteFlags::CONTENTS) )
+ rDoc.UpdateTranspose( aUserRange.aStart, pOrigClipDoc, aFilteredMark, pRefUndoDoc.get() );
+ }
+ else if (!bTranspose)
+ {
+ // copy with bAsLink=TRUE
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, pRefUndoDoc.get(), pClipDoc,
+ true, true, bIncludeFiltered, bSkipEmptyCells );
+ }
+ else
+ {
+ // copy all content (TransClipDoc contains only formula)
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, nContFlags, pRefUndoDoc.get(), pClipDoc );
+ }
+
+ // skipped rows and merged cells don't mix
+ if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
+ rDocFunc.UnmergeCells( aUserRange, false, nullptr );
+
+ rDoc.ExtendMergeSel( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, true ); // refresh
+ // new range
+
+ if ( pMixDoc ) // calculate with original data?
+ {
+ rDoc.MixDocument( aUserRange, nFunction, bSkipEmptyCells, *pMixDoc );
+ }
+ pMixDoc.reset();
+
+ AdjustBlockHeight(); // update row heights before pasting objects
+
+ ::std::vector< OUString > aExcludedChartNames;
+ SdrPage* pPage = nullptr;
+
+ if ( nFlags & InsertDeleteFlags::OBJECTS )
+ {
+ ScDrawView* pScDrawView = GetScDrawView();
+ SdrModel* pModel = ( pScDrawView ? &pScDrawView->GetModel() : nullptr );
+ pPage = ( pModel ? pModel->GetPage( static_cast< sal_uInt16 >( nStartTab ) ) : nullptr );
+ if ( pPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
+ }
+
+ // Paste the drawing objects after the row heights have been updated.
+
+ rDoc.CopyFromClip( aUserRange, aFilteredMark, InsertDeleteFlags::OBJECTS, pRefUndoDoc.get(), pClipDoc,
+ true, false, bIncludeFiltered );
+ }
+
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab ); // content after the change
+
+ // if necessary, delete autofilter-heads
+ if (bCutMode)
+ if (rDoc.RefreshAutoFilter( nClipStartX,nClipStartY, nClipStartX+nClipSizeX,
+ nClipStartY+nClipSizeY, nStartTab ))
+ {
+ pDocSh->PostPaint(
+ ScRange(nClipStartX, nClipStartY, nStartTab, nClipStartX+nClipSizeX, nClipStartY, nStartTab),
+ PaintPartFlags::Grid );
+ }
+
+ //! remove block-range on RefUndoDoc !!!
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pRedoDoc;
+ // copy redo data after appearance of the first undo
+ // don't create Redo-Doc without RefUndoDoc
+
+ if (pRefUndoDoc)
+ {
+ pRedoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nStartTab, nEndTab, bColInfo, bRowInfo );
+
+ // move adapted refs to Redo-Doc
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pRedoDoc->AddUndoTab( 0, nTabCount-1 );
+ rDoc.CopyUpdated( pRefUndoDoc.get(), pRedoDoc.get() );
+
+ // move old refs to Undo-Doc
+
+ // not charts?
+ pUndoDoc->AddUndoTab( 0, nTabCount-1 );
+ pRefUndoDoc->DeleteArea( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, InsertDeleteFlags::ALL );
+ pRefUndoDoc->CopyToDocument( 0,0,0, pUndoDoc->MaxCol(), pUndoDoc->MaxRow(), nTabCount-1,
+ InsertDeleteFlags::FORMULA, false, *pUndoDoc );
+ pRefUndoDoc.reset();
+ }
+
+ // DeleteUnchanged for pUndoData is in ScUndoPaste ctor,
+ // UndoData for redo is made during first undo
+
+ ScUndoPasteOptions aOptions; // store options for repeat
+ aOptions.nFunction = nFunction;
+ aOptions.bSkipEmptyCells = bSkipEmptyCells;
+ aOptions.bTranspose = bTranspose;
+ aOptions.bAsLink = bAsLink;
+ aOptions.eMoveMode = eMoveMode;
+
+ std::unique_ptr<SfxUndoAction> pUndo(new ScUndoPaste(
+ pDocSh, ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
+ aFilteredMark, std::move(pUndoDoc), std::move(pRedoDoc), nFlags | nUndoFlags, std::move(pUndoData),
+ false, &aOptions )); // false = Redo data not yet copied
+
+ if ( bInsertCells )
+ {
+ // Merge the paste undo action into the insert action.
+ // Use ScUndoWrapper so the ScUndoPaste pointer can be stored in the insert action.
+
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoWrapper>( std::move(pUndo) ), true );
+ }
+ else
+ pUndoMgr->AddUndoAction( std::move(pUndo) );
+ pUndoMgr->LeaveListAction();
+ }
+
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ if (bColInfo)
+ {
+ nPaint |= PaintPartFlags::Top;
+ nUndoEndCol = rDoc.MaxCol(); // just for drawing !
+ }
+ if (bRowInfo)
+ {
+ nPaint |= PaintPartFlags::Left;
+ nUndoEndRow = rDoc.MaxRow(); // just for drawing !
+ }
+ pDocSh->PostPaint(
+ ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
+ nPaint, nExtFlags);
+ // AdjustBlockHeight has already been called above
+
+ aModificator.SetDocumentModified();
+ PostPasteFromClip(aUserRange, rMark);
+
+ if ( nFlags & InsertDeleteFlags::OBJECTS )
+ {
+ ScModelObj* pModelObj = pDocSh->GetModel();
+ if ( pPage && pModelObj )
+ {
+ bool bSameDoc = ( rClipParam.getSourceDocID() == rDoc.GetDocumentID() );
+ const ScRangeListVector& rProtectedChartRangesVector( rClipParam.maProtectedChartRangesVector );
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDoc, pPage, pModelObj, nStartTab,
+ rProtectedChartRangesVector, aExcludedChartNames, bSameDoc );
+ }
+ }
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "PASTE");
+ return true;
+}
+
+bool ScViewFunc::PasteMultiRangesFromClip(InsertDeleteFlags nFlags, ScDocument* pClipDoc,
+ ScPasteFunc nFunction, bool bSkipEmptyCells,
+ bool bTranspose, bool bAsLink,
+ bool bAllowDialogs, InsCellCmd eMoveMode,
+ InsertDeleteFlags nUndoFlags)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScMarkData aMark(rViewData.GetMarkData());
+ const ScAddress& rCurPos = rViewData.GetCurPos();
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ SCCOL nColSize = rClipParam.getPasteColSize();
+ SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, /*bIncludeFiltered*/false);
+
+ if (bTranspose)
+ {
+ if (static_cast<SCROW>(rCurPos.Col()) + nRowSize-1 > static_cast<SCROW>(pClipDoc->MaxCol()))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+ pClipDoc->TransposeClip(pTransClip.get(), nFlags, bAsLink, /*bIncludeFiltered*/false);
+ pClipDoc = pTransClip.release();
+ SCCOL nTempColSize = nColSize;
+ nColSize = static_cast<SCCOL>(nRowSize);
+ nRowSize = static_cast<SCROW>(nTempColSize);
+ }
+
+ if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ // Determine the first and last selected sheet numbers.
+ SCTAB nTab1 = aMark.GetFirstSelected();
+ SCTAB nTab2 = aMark.GetLastSelected();
+
+ ScDocShellModificator aModificator(*pDocSh);
+
+ // For multi-selection paste, we don't support cell duplication for larger
+ // destination range. In case the destination is marked, we reset it to
+ // the clip size.
+ ScRange aMarkedRange(rCurPos.Col(), rCurPos.Row(), nTab1,
+ rCurPos.Col()+nColSize-1, rCurPos.Row()+nRowSize-1, nTab2);
+
+ // Extend the marked range to account for filtered rows in the destination
+ // area.
+ if (ScViewUtil::HasFiltered(aMarkedRange, rDoc))
+ {
+ if (!ScViewUtil::FitToUnfilteredRows(aMarkedRange, rDoc, nRowSize))
+ return false;
+ }
+
+ bool bAskIfNotEmpty =
+ bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) &&
+ nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
+
+ if (bAskIfNotEmpty)
+ {
+ ScRangeList aTestRanges(aMarkedRange);
+ if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aMark, GetViewData().GetDialogParent()))
+ return false;
+ }
+
+ aMark.SetMarkArea(aMarkedRange);
+ MarkRange(aMarkedRange);
+
+ bool bInsertCells = (eMoveMode != INS_NONE);
+ if (bInsertCells)
+ {
+ if (!InsertCells(eMoveMode, rDoc.IsUndoEnabled(), true))
+ return false;
+ }
+
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
+ bool bRowInfo = ( aMarkedRange.aStart.Col()==0 && aMarkedRange.aEnd.Col()==pClipDoc->MaxCol() );
+ ScDocumentUniquePtr pUndoDoc;
+ if (rDoc.IsUndoEnabled())
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndoSelected(rDoc, aMark, false, bRowInfo);
+ rDoc.CopyToDocument(aMarkedRange, nUndoFlags, false, *pUndoDoc, &aMark);
+ }
+
+ ScDocumentUniquePtr pMixDoc;
+ if ( bSkipEmptyCells || nFunction != ScPasteFunc::NONE)
+ {
+ if ( nFlags & InsertDeleteFlags::CONTENTS )
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndoSelected(rDoc, aMark);
+ rDoc.CopyToDocument(aMarkedRange, InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark);
+ }
+ }
+
+ /* Make draw layer and start drawing undo.
+ - Needed before AdjustBlockHeight to track moved drawing objects.
+ - Needed before rDoc.CopyFromClip to track inserted note caption objects.
+ */
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ pDocSh->MakeDrawLayer();
+ if (rDoc.IsUndoEnabled())
+ rDoc.BeginDrawUndo();
+
+ InsertDeleteFlags nCopyFlags = nFlags & ~InsertDeleteFlags::OBJECTS;
+ // in case of transpose, links were added in TransposeClip()
+ if (bAsLink && bTranspose)
+ nCopyFlags |= InsertDeleteFlags::FORMULA;
+ rDoc.CopyMultiRangeFromClip(rCurPos, aMark, nCopyFlags, pClipDoc, true, bAsLink && !bTranspose,
+ /*bIncludeFiltered*/false, bSkipEmptyCells);
+
+ if (pMixDoc)
+ rDoc.MixDocument(aMarkedRange, nFunction, bSkipEmptyCells, *pMixDoc);
+
+ AdjustBlockHeight(); // update row heights before pasting objects
+
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ {
+ // Paste the drawing objects after the row heights have been updated.
+ rDoc.CopyMultiRangeFromClip(rCurPos, aMark, InsertDeleteFlags::OBJECTS, pClipDoc, true,
+ false, /*bIncludeFiltered*/false, true);
+ }
+
+ if (bRowInfo)
+ pDocSh->PostPaint(aMarkedRange.aStart.Col(), aMarkedRange.aStart.Row(), nTab1, pClipDoc->MaxCol(), pClipDoc->MaxRow(), nTab1, PaintPartFlags::Grid|PaintPartFlags::Left);
+ else
+ {
+ ScRange aTmp = aMarkedRange;
+ aTmp.aStart.SetTab(nTab1);
+ aTmp.aEnd.SetTab(nTab1);
+ pDocSh->PostPaint(aTmp, PaintPartFlags::Grid);
+ }
+
+ if (rDoc.IsUndoEnabled())
+ {
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ OUString aUndo = ScResId(
+ pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY);
+ pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId());
+
+ ScUndoPasteOptions aOptions; // store options for repeat
+ aOptions.nFunction = nFunction;
+ aOptions.bSkipEmptyCells = bSkipEmptyCells;
+ aOptions.bTranspose = bTranspose;
+ aOptions.bAsLink = bAsLink;
+ aOptions.eMoveMode = eMoveMode;
+
+ std::unique_ptr<ScUndoPaste> pUndo(new ScUndoPaste(pDocSh,
+ aMarkedRange, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions));
+
+ if (bInsertCells)
+ pUndoMgr->AddUndoAction(std::make_unique<ScUndoWrapper>(std::move(pUndo)), true);
+ else
+ pUndoMgr->AddUndoAction(std::move(pUndo));
+
+ pUndoMgr->LeaveListAction();
+ }
+
+ aModificator.SetDocumentModified();
+ PostPasteFromClip(aMarkedRange, aMark);
+ return true;
+}
+
+bool ScViewFunc::PasteFromClipToMultiRanges(
+ InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction,
+ bool bSkipEmptyCells, bool bTranspose, bool bAsLink, bool bAllowDialogs,
+ InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags )
+{
+ if (bTranspose)
+ {
+ // We don't allow transpose for this yet.
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ if (eMoveMode != INS_NONE)
+ {
+ // We don't allow insertion mode either. Too complicated.
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ ScViewData& rViewData = GetViewData();
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ if (rClipParam.mbCutMode)
+ {
+ // No cut and paste with this, please.
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ const ScAddress& rCurPos = rViewData.GetCurPos();
+ ScDocument& rDoc = rViewData.GetDocument();
+
+ ScRange aSrcRange = rClipParam.getWholeRange();
+ SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
+ SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
+
+ if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1))
+ {
+ ErrorMessage(STR_PASTE_FULL);
+ return false;
+ }
+
+ ScMarkData aMark(rViewData.GetMarkData());
+
+ ScRangeList aRanges;
+ aMark.MarkToSimple();
+ aMark.FillRangeListWithMarks(&aRanges, false);
+ if (!ScClipUtil::CheckDestRanges(rDoc, nColSize, nRowSize, aMark, aRanges))
+ {
+ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
+ return false;
+ }
+
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ ScDocShellModificator aModificator(*pDocSh);
+
+ bool bAskIfNotEmpty =
+ bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) &&
+ nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn();
+
+ if (bAskIfNotEmpty)
+ {
+ if (!checkDestRangeForOverwrite(aRanges, rDoc, aMark, GetViewData().GetDialogParent()))
+ return false;
+ }
+
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (rDoc.IsUndoEnabled())
+ {
+ pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndoSelected(rDoc, aMark);
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyToDocument(
+ aRanges[i], nUndoFlags, false, *pUndoDoc, &aMark);
+ }
+ }
+
+ ScDocumentUniquePtr pMixDoc;
+ if (bSkipEmptyCells || nFunction != ScPasteFunc::NONE)
+ {
+ if (nFlags & InsertDeleteFlags::CONTENTS)
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndoSelected(rDoc, aMark);
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyToDocument(
+ aRanges[i], InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark);
+ }
+ }
+ }
+
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ pDocSh->MakeDrawLayer();
+ if (rDoc.IsUndoEnabled())
+ rDoc.BeginDrawUndo();
+
+ // First, paste everything but the drawing objects.
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyFromClip(
+ aRanges[i], aMark, (nFlags & ~InsertDeleteFlags::OBJECTS), nullptr, pClipDoc,
+ false, false, true, bSkipEmptyCells);
+ }
+
+ if (pMixDoc)
+ {
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ rDoc.MixDocument(aRanges[i], nFunction, bSkipEmptyCells, *pMixDoc);
+ }
+
+ AdjustBlockHeight(); // update row heights before pasting objects
+
+ // Then paste the objects.
+ if (nFlags & InsertDeleteFlags::OBJECTS)
+ {
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ rDoc.CopyFromClip(
+ aRanges[i], aMark, InsertDeleteFlags::OBJECTS, nullptr, pClipDoc,
+ false, false, true, bSkipEmptyCells);
+ }
+ }
+
+ // Refresh the range that includes all pasted ranges. We only need to
+ // refresh the current sheet.
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ bool bRowInfo = (aSrcRange.aStart.Col()==0 && aSrcRange.aEnd.Col()==pClipDoc->MaxCol());
+ if (bRowInfo)
+ nPaint |= PaintPartFlags::Left;
+ pDocSh->PostPaint(aRanges, nPaint);
+
+ if (rDoc.IsUndoEnabled())
+ {
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ OUString aUndo = ScResId(
+ pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY);
+ pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId());
+
+ ScUndoPasteOptions aOptions; // store options for repeat
+ aOptions.nFunction = nFunction;
+ aOptions.bSkipEmptyCells = bSkipEmptyCells;
+ aOptions.bTranspose = bTranspose;
+ aOptions.bAsLink = bAsLink;
+ aOptions.eMoveMode = eMoveMode;
+
+
+ pUndoMgr->AddUndoAction(
+ std::make_unique<ScUndoPaste>(
+ pDocSh, aRanges, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions));
+ pUndoMgr->LeaveListAction();
+ }
+
+ aModificator.SetDocumentModified();
+ PostPasteFromClip(aRanges, aMark);
+
+ return false;
+}
+
+void ScViewFunc::PostPasteFromClip(const ScRangeList& rPasteRanges, const ScMarkData& rMark)
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ pDocSh->UpdateOle(rViewData);
+
+ SelectionChanged(true);
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ ScRangeList aChangeRanges;
+ for (size_t i = 0, n = rPasteRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = rPasteRanges[i];
+ for (const auto& rTab : rMark)
+ {
+ ScRange aChangeRange(r);
+ aChangeRange.aStart.SetTab(rTab);
+ aChangeRange.aEnd.SetTab(rTab);
+ aChangeRanges.push_back(aChangeRange);
+ }
+ }
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "paste");
+ else if (pModelObj)
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate");
+}
+
+// D R A G A N D D R O P
+
+// inside the doc
+
+bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos,
+ bool bCut )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ HideAllCursors();
+
+ ResetAutoSpellForContentChange();
+
+ bool bSuccess = true;
+ SCTAB nDestTab = rDestPos.Tab();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( rSource.aStart.Tab() == nDestTab && rSource.aEnd.Tab() == nDestTab && rMark.GetSelectCount() > 1 )
+ {
+ // moving within one table and several tables selected -> apply to all selected tables
+
+ OUString aUndo = ScResId( bCut ? STR_UNDO_MOVE : STR_UNDO_COPY );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+
+ // collect ranges of consecutive selected tables
+
+ ScRange aLocalSource = rSource;
+ ScAddress aLocalDest = rDestPos;
+ SCTAB nTabCount = pDocSh->GetDocument().GetTableCount();
+ SCTAB nStartTab = 0;
+ while ( nStartTab < nTabCount && bSuccess )
+ {
+ while ( nStartTab < nTabCount && !rMark.GetTableSelect(nStartTab) )
+ ++nStartTab;
+ if ( nStartTab < nTabCount )
+ {
+ SCTAB nEndTab = nStartTab;
+ while ( nEndTab+1 < nTabCount && rMark.GetTableSelect(nEndTab+1) )
+ ++nEndTab;
+
+ aLocalSource.aStart.SetTab( nStartTab );
+ aLocalSource.aEnd.SetTab( nEndTab );
+ aLocalDest.SetTab( nStartTab );
+
+ bSuccess = pDocSh->GetDocFunc().MoveBlock(
+ aLocalSource, aLocalDest, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ );
+
+ nStartTab = nEndTab + 1;
+ }
+ }
+
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+ else
+ {
+ // move the block as specified
+ bSuccess = pDocSh->GetDocFunc().MoveBlock(
+ rSource, rDestPos, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ );
+ }
+
+ ShowAllCursors();
+ if (bSuccess)
+ {
+ // mark destination range
+ ScAddress aDestEnd(
+ rDestPos.Col() + rSource.aEnd.Col() - rSource.aStart.Col(),
+ rDestPos.Row() + rSource.aEnd.Row() - rSource.aStart.Row(),
+ nDestTab );
+
+ bool bIncludeFiltered = bCut;
+ if ( !bIncludeFiltered )
+ {
+ // find number of non-filtered rows
+ SCROW nPastedCount = pDocSh->GetDocument().CountNonFilteredRows(
+ rSource.aStart.Row(), rSource.aEnd.Row(), rSource.aStart.Tab());
+
+ if ( nPastedCount == 0 )
+ nPastedCount = 1;
+ aDestEnd.SetRow( rDestPos.Row() + nPastedCount - 1 );
+ }
+
+ MarkRange( ScRange( rDestPos, aDestEnd ), false ); //! sal_False ???
+
+ pDocSh->UpdateOle(GetViewData());
+ SelectionChanged();
+ }
+ return bSuccess;
+}
+
+// link inside the doc
+
+bool ScViewFunc::LinkBlock( const ScRange& rSource, const ScAddress& rDestPos )
+{
+ // check overlapping
+
+ if ( rSource.aStart.Tab() == rDestPos.Tab() )
+ {
+ SCCOL nDestEndCol = rDestPos.Col() + ( rSource.aEnd.Col() - rSource.aStart.Col() );
+ SCROW nDestEndRow = rDestPos.Row() + ( rSource.aEnd.Row() - rSource.aStart.Row() );
+
+ if ( rSource.aStart.Col() <= nDestEndCol && rDestPos.Col() <= rSource.aEnd.Col() &&
+ rSource.aStart.Row() <= nDestEndRow && rDestPos.Row() <= rSource.aEnd.Row() )
+ {
+ return false;
+ }
+ }
+
+ // run with paste
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+ rDoc.CopyTabToClip( rSource.aStart.Col(), rSource.aStart.Row(),
+ rSource.aEnd.Col(), rSource.aEnd.Row(),
+ rSource.aStart.Tab(), pClipDoc.get() );
+
+ // mark destination area (set cursor, no marks)
+
+ if ( GetViewData().GetTabNo() != rDestPos.Tab() )
+ SetTabNo( rDestPos.Tab() );
+
+ MoveCursorAbs( rDestPos.Col(), rDestPos.Row(), SC_FOLLOW_NONE, false, false );
+
+ // Paste
+
+ PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(), ScPasteFunc::NONE, false, false, true ); // as a link
+
+ return true;
+}
+
+void ScViewFunc::DataFormPutData( SCROW nCurrentRow ,
+ SCROW nStartRow , SCCOL nStartCol ,
+ SCROW nEndRow , SCCOL nEndCol ,
+ std::vector<std::unique_ptr<ScDataFormFragment>>& rEdits,
+ sal_uInt16 aColLength )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocShellModificator aModificator( *pDocSh );
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+
+ const bool bRecord( rDoc.IsUndoEnabled());
+ ScDocumentUniquePtr pUndoDoc;
+ ScDocumentUniquePtr pRedoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ SCTAB nTab = GetViewData().GetTabNo();
+ SCTAB nStartTab = nTab;
+ SCTAB nEndTab = nTab;
+
+ {
+ ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
+ if ( pChangeTrack )
+ pChangeTrack->ResetLastCut(); // no more cut-mode
+ }
+ ScRange aUserRange( nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab );
+ bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() );
+ bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() );
+ SCCOL nUndoEndCol = nStartCol+aColLength-1;
+ SCROW nUndoEndRow = nCurrentRow;
+
+ if ( bRecord )
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndoSelected( rDoc , rMark , bColInfo , bRowInfo );
+ rDoc.CopyToDocument( aUserRange , InsertDeleteFlags::VALUE , false, *pUndoDoc );
+ }
+ sal_uInt16 nExtFlags = 0;
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab , nEndCol, nEndRow, nEndTab ); // content before the change
+ rDoc.BeginDrawUndo();
+
+ for(sal_uInt16 i = 0; i < aColLength; i++)
+ {
+ if (rEdits[i] != nullptr)
+ {
+ OUString aFieldName = rEdits[i]->m_xEdit->get_text();
+ rDoc.SetString( nStartCol + i, nCurrentRow, nTab, aFieldName );
+ }
+ }
+ pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab ); // content after the change
+ std::unique_ptr<SfxUndoAction> pUndo( new ScUndoDataForm( pDocSh,
+ nStartCol, nCurrentRow, nStartTab,
+ nUndoEndCol, nUndoEndRow, nEndTab, rMark,
+ std::move(pUndoDoc), std::move(pRedoDoc),
+ std::move(pUndoData) ) );
+ pUndoMgr->AddUndoAction( std::make_unique<ScUndoWrapper>( std::move(pUndo) ), true );
+
+ PaintPartFlags nPaint = PaintPartFlags::Grid;
+ if (bColInfo)
+ {
+ nPaint |= PaintPartFlags::Top;
+ nUndoEndCol = rDoc.MaxCol(); // just for drawing !
+ }
+ if (bRowInfo)
+ {
+ nPaint |= PaintPartFlags::Left;
+ nUndoEndRow = rDoc.MaxRow(); // just for drawing !
+ }
+
+ pDocSh->PostPaint(
+ ScRange(nStartCol, nCurrentRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab),
+ nPaint, nExtFlags);
+ pDocSh->UpdateOle(GetViewData());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun4.cxx b/sc/source/ui/view/viewfun4.cxx
new file mode 100644
index 0000000000..d75541b302
--- /dev/null
+++ b/sc/source/ui/view/viewfun4.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 <config_features.h>
+
+#include <memory>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+#include <sot/storage.hxx>
+#include <svx/hlnkitem.hxx>
+#include <editeng/unolingu.hxx>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svtools/langtab.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/transfer.hxx>
+#include <svl/urlbmk.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+
+#include <viewfunc.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <scresid.hxx>
+#include <undoblk.hxx>
+#include <undocell.hxx>
+#include <formulacell.hxx>
+#include <scmod.hxx>
+#include <spelleng.hxx>
+#include <patattr.hxx>
+#include <tabvwsh.hxx>
+#include <impex.hxx>
+#include <editutil.hxx>
+#include <editable.hxx>
+#include <dociter.hxx>
+#include <reffind.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <refupdatecontext.hxx>
+#include <gridwin.hxx>
+#include <refundo.hxx>
+
+using namespace com::sun::star;
+
+void ScViewFunc::PasteRTF( SCCOL nStartCol, SCROW nStartRow,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable )
+{
+ TransferableDataHelper aDataHelper( rxTransferable );
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) )
+ {
+ HideAllCursors();
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ const bool bRecord (rDoc.IsUndoEnabled());
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nStartCol, nStartRow, nTab );
+ std::optional<ScTabEditEngine> pEngine(std::in_place, *pPattern, rDoc.GetEnginePool(), &rDoc );
+ pEngine->EnableUndo( false );
+
+ vcl::Window* pActWin = GetActiveWin();
+ if (pActWin)
+ {
+ pEngine->SetPaperSize(Size(100000,100000));
+ ScopedVclPtrInstance< vcl::Window > aWin( pActWin );
+ EditView aEditView( &*pEngine, aWin.get() );
+ aEditView.SetOutputArea(tools::Rectangle(0,0,100000,100000));
+
+ // same method now for clipboard or drag&drop
+ // mba: clipboard always must contain absolute URLs (could be from alien source)
+ aEditView.InsertText( rxTransferable, OUString(), true );
+ }
+
+ sal_Int32 nParCnt = pEngine->GetParagraphCount();
+ if (nParCnt)
+ {
+ SCROW nEndRow = nStartRow + static_cast<SCROW>(nParCnt) - 1;
+ if (nEndRow > rDoc.MaxRow())
+ nEndRow = rDoc.MaxRow();
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument( nStartCol,nStartRow,nTab, nStartCol,nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
+ }
+
+ SCROW nRow = nStartRow;
+
+ // Temporarily turn off undo generation for this lot
+ bool bUndoEnabled = rDoc.IsUndoEnabled();
+ rDoc.EnableUndo( false );
+ for( sal_Int32 n = 0; n < nParCnt; n++ )
+ {
+ std::unique_ptr<EditTextObject> pObject(pEngine->CreateTextObject(n));
+ EnterData(nStartCol, nRow, nTab, *pObject, true);
+ if( ++nRow > rDoc.MaxRow() )
+ break;
+ }
+ rDoc.EnableUndo(bUndoEnabled);
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+ rDoc.CopyToDocument( nStartCol,nStartRow,nTab, nStartCol,nEndRow,nTab, InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pRedoDoc );
+
+ ScRange aMarkRange(nStartCol, nStartRow, nTab, nStartCol, nEndRow, nTab);
+ ScMarkData aDestMark(rDoc.GetSheetLimits());
+ aDestMark.SetMarkArea( aMarkRange );
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoPaste>( pDocSh, aMarkRange, aDestMark,
+ std::move(pUndoDoc), std::move(pRedoDoc), InsertDeleteFlags::ALL, nullptr));
+ }
+ }
+
+ pEngine.reset();
+
+ ShowAllCursors();
+ }
+ else
+ {
+ HideAllCursors();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScImportExport aImpEx( pDocSh->GetDocument(),
+ ScAddress( nStartCol, nStartRow, GetViewData().GetTabNo() ) );
+
+ OUString aStr;
+ tools::SvRef<SotTempStream> xStream;
+ if ( aDataHelper.GetSotStorageStream( SotClipboardFormatId::RTF, xStream ) && xStream.is() )
+ // mba: clipboard always must contain absolute URLs (could be from alien source)
+ aImpEx.ImportStream( *xStream, OUString(), SotClipboardFormatId::RTF );
+ else if ( aDataHelper.GetString( SotClipboardFormatId::RTF, aStr ) )
+ aImpEx.ImportString( aStr, SotClipboardFormatId::RTF );
+ else if ( aDataHelper.GetSotStorageStream( SotClipboardFormatId::RICHTEXT, xStream ) && xStream.is() )
+ aImpEx.ImportStream( *xStream, OUString(), SotClipboardFormatId::RICHTEXT );
+ else if ( aDataHelper.GetString( SotClipboardFormatId::RICHTEXT, aStr ) )
+ aImpEx.ImportString( aStr, SotClipboardFormatId::RICHTEXT );
+
+ AdjustRowHeight( nStartRow, aImpEx.GetRange().aEnd.Row(), true );
+ pDocSh->UpdateOle(GetViewData());
+ ShowAllCursors();
+ }
+}
+void ScViewFunc::DoRefConversion()
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScRange aMarkRange;
+ rMark.MarkToSimple();
+ bool bMulti = rMark.IsMultiMarked();
+ if (bMulti)
+ aMarkRange = rMark.GetMultiMarkArea();
+ else if (rMark.IsMarked())
+ aMarkRange = rMark.GetMarkArea();
+ else
+ {
+ aMarkRange = ScRange( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ }
+ ScEditableTester aTester( rDoc, aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(),rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ bool bOk = false;
+
+ ScDocumentUniquePtr pUndoDoc;
+ if (bRecord)
+ {
+ pUndoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ SCTAB nTab = aMarkRange.aStart.Tab();
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if ( rMark.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMark)
+ if ( rTab != nTab )
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ }
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc, &rMark );
+ }
+
+ ScRangeListRef xRanges;
+ GetViewData().GetMultiArea( xRanges );
+ size_t nCount = xRanges->size();
+
+ for (const SCTAB& i : rMark)
+ {
+ for (size_t j = 0; j < nCount; ++j)
+ {
+ ScRange aRange = (*xRanges)[j];
+ aRange.aStart.SetTab(i);
+ aRange.aEnd.SetTab(i);
+ ScCellIterator aIter( rDoc, aRange );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ ScFormulaCell* pCell = aIter.getFormulaCell();
+ ScMatrixMode eMatrixMode = pCell->GetMatrixFlag();
+ if (eMatrixMode == ScMatrixMode::Reference)
+ continue;
+
+ OUString aOld = pCell->GetFormula();
+ sal_Int32 nLen = aOld.getLength();
+ if (eMatrixMode == ScMatrixMode::Formula)
+ {
+ assert(nLen >= 2 && aOld[0] == '{' && aOld[nLen-1] == '}');
+ nLen -= 2;
+ aOld = aOld.copy( 1, nLen);
+ }
+ ScRefFinder aFinder( aOld, aIter.GetPos(), rDoc, rDoc.GetAddressConvention() );
+ aFinder.ToggleRel( 0, nLen );
+ if (aFinder.GetFound())
+ {
+ ScAddress aPos = pCell->aPos;
+ const OUString& aNew = aFinder.GetText();
+ ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar());
+ std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aNew));
+ ScFormulaCell* pNewCell =
+ new ScFormulaCell(
+ rDoc, aPos, *pArr, formula::FormulaGrammar::GRAM_DEFAULT, eMatrixMode);
+
+ rDoc.SetFormulaCell(aPos, pNewCell);
+ bOk = true;
+ }
+ }
+ }
+ }
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nTab = aMarkRange.aStart.Tab();
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if ( rMark.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMark)
+ if ( rTab != nTab )
+ pRedoDoc->AddUndoTab( rTab, rTab );
+ }
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ALL, bMulti, *pRedoDoc, &rMark );
+
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoRefConversion>( pDocSh,
+ aMarkRange, rMark, std::move(pUndoDoc), std::move(pRedoDoc), bMulti) );
+ }
+
+ pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid );
+ pDocSh->UpdateOle(GetViewData());
+ pDocSh->SetDocumentModified();
+ CellContentChanged();
+
+ if (!bOk)
+ ErrorMessage(STR_ERR_NOREF);
+}
+
+// Thesaurus - Undo ok
+void ScViewFunc::DoThesaurus()
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScSplitPos eWhich = GetViewData().GetActivePart();
+ EESpellState eState;
+ EditView* pEditView = nullptr;
+ std::unique_ptr<ESelection> pEditSel;
+ std::unique_ptr<ScEditEngineDefaulter> pThesaurusEngine;
+ bool bIsEditMode = GetViewData().HasEditView(eWhich);
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ if (bIsEditMode) // edit mode active
+ {
+ GetViewData().GetEditView(eWhich, pEditView, nCol, nRow);
+ pEditSel.reset(new ESelection(pEditView->GetSelection()));
+ SC_MOD()->InputEnterHandler();
+ GetViewData().GetBindings().Update(); // otherwise the Sfx becomes mixed-up...
+ }
+ else
+ {
+ nCol = GetViewData().GetCurX();
+ nRow = GetViewData().GetCurY();
+ }
+ nTab = GetViewData().GetTabNo();
+
+ ScAddress aPos(nCol, nRow, nTab);
+ ScEditableTester aTester( rDoc, nCol, nRow, nCol, nRow, rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+
+ ScCellValue aOldText;
+ aOldText.assign(rDoc, aPos);
+ if (aOldText.getType() != CELLTYPE_STRING && aOldText.getType() != CELLTYPE_EDIT)
+ {
+ ErrorMessage(STR_THESAURUS_NO_STRING);
+ return;
+ }
+
+ uno::Reference<linguistic2::XSpellChecker1> xSpeller = LinguMgr::GetSpellChecker();
+
+ pThesaurusEngine.reset(new ScEditEngineDefaulter(rDoc.GetEnginePool()));
+ pThesaurusEngine->SetEditTextObjectPool( rDoc.GetEditPool() );
+ pThesaurusEngine->SetRefDevice(GetViewData().GetActiveWin()->GetOutDev());
+ pThesaurusEngine->SetSpeller(xSpeller);
+ MakeEditView(pThesaurusEngine.get(), nCol, nRow );
+ SfxItemSet aEditDefaults(pThesaurusEngine->GetEmptyItemSet());
+ const ScPatternAttr* pPattern = rDoc.GetPattern(nCol, nRow, nTab);
+ if (pPattern)
+ {
+ pPattern->FillEditItemSet( &aEditDefaults );
+ pThesaurusEngine->SetDefaults( aEditDefaults );
+ }
+
+ if (aOldText.getType() == CELLTYPE_EDIT)
+ pThesaurusEngine->SetTextCurrentDefaults(*aOldText.getEditText());
+ else
+ pThesaurusEngine->SetTextCurrentDefaults(aOldText.getString(rDoc));
+
+ pEditView = GetViewData().GetEditView(GetViewData().GetActivePart());
+ if (pEditSel)
+ pEditView->SetSelection(*pEditSel);
+ else
+ pEditView->SetSelection(ESelection(0,0,0,0));
+
+ pThesaurusEngine->ClearModifyFlag();
+
+ // language is now in EditEngine attributes -> no longer passed to StartThesaurus
+
+ eState = pEditView->StartThesaurus(GetViewData().GetDialogParent());
+ OSL_ENSURE(eState != EESpellState::NoSpeller, "No SpellChecker");
+
+ if (eState == EESpellState::ErrorFound) // should happen later through Wrapper!
+ {
+ LanguageType eLnge = ScViewUtil::GetEffLanguage( rDoc, ScAddress( nCol, nRow, nTab ) );
+ OUString aErr = SvtLanguageTable::GetLanguageString(eLnge) + ScResId( STR_SPELLING_NO_LANG );
+
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
+ VclMessageType::Info, VclButtonsType::Ok,
+ aErr));
+ xInfoBox->run();
+ }
+ if (pThesaurusEngine->IsModified())
+ {
+ ScCellValue aNewText;
+
+ if (aOldText.getType() == CELLTYPE_EDIT)
+ {
+ // The cell will own the text object instance.
+ std::unique_ptr<EditTextObject> pText = pThesaurusEngine->CreateTextObject();
+ auto tmp = pText.get();
+ if (rDoc.SetEditText(ScAddress(nCol,nRow,nTab), std::move(pText)))
+ aNewText.set(*tmp);
+ }
+ else
+ {
+ OUString aStr = pThesaurusEngine->GetText();
+ aNewText.set(rDoc.GetSharedStringPool().intern(aStr));
+ rDoc.SetString(nCol, nRow, nTab, aStr);
+ }
+
+ pDocSh->SetDocumentModified();
+ if (bRecord)
+ {
+ GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoThesaurus>(
+ GetViewData().GetDocShell(), nCol, nRow, nTab, aOldText, aNewText));
+ }
+ }
+
+ KillEditView(true);
+ pDocSh->PostPaintGridAll();
+}
+
+void ScViewFunc::DoHangulHanjaConversion()
+{
+ ScConversionParam aConvParam( SC_CONVERSION_HANGULHANJA, LANGUAGE_KOREAN, 0, true );
+ DoSheetConversion( aConvParam );
+}
+
+void ScViewFunc::DoSheetConversion( const ScConversionParam& rConvParam )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+ ScSplitPos eWhich = rViewData.GetActivePart();
+ EditView* pEditView = nullptr;
+ bool bIsEditMode = rViewData.HasEditView(eWhich);
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+ if (bIsEditMode) // edit mode active
+ {
+ rViewData.GetEditView(eWhich, pEditView, nCol, nRow);
+ SC_MOD()->InputEnterHandler();
+ }
+ else
+ {
+ nCol = rViewData.GetCurX();
+ nRow = rViewData.GetCurY();
+
+ AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP);
+ }
+ nTab = rViewData.GetTabNo();
+
+ rMark.MarkToMulti();
+ bool bMarked = rMark.IsMultiMarked();
+ if (bMarked)
+ {
+ ScEditableTester aTester( rDoc, rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ return;
+ }
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ ScDocumentUniquePtr pRedoDoc;
+ if (bRecord)
+ {
+ pUndoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ pRedoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
+ pRedoDoc->InitUndo( rDoc, nTab, nTab );
+
+ if ( rMark.GetSelectCount() > 1 )
+ {
+ for (const auto& rTab : rMark)
+ if ( rTab != nTab )
+ {
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ pRedoDoc->AddUndoTab( rTab, rTab );
+ }
+ }
+ }
+
+ // from here no return
+
+ bool bOldEnabled = rDoc.IsIdleEnabled();
+ rDoc.EnableIdle(false); // stop online spelling
+
+ // *** create and init the edit engine *** --------------------------------
+
+ std::unique_ptr<ScConversionEngineBase> pEngine;
+ switch( rConvParam.GetType() )
+ {
+ case SC_CONVERSION_SPELLCHECK:
+ pEngine.reset(new ScSpellingEngine(
+ rDoc.GetEnginePool(), rViewData, pUndoDoc.get(), pRedoDoc.get(), LinguMgr::GetSpellChecker() ));
+ break;
+ case SC_CONVERSION_HANGULHANJA:
+ case SC_CONVERSION_CHINESE_TRANSL:
+ pEngine.reset(new ScTextConversionEngine(
+ rDoc.GetEnginePool(), rViewData, rConvParam, pUndoDoc.get(), pRedoDoc.get() ));
+ break;
+ default:
+ OSL_FAIL( "ScViewFunc::DoSheetConversion - unknown conversion type" );
+ }
+
+ MakeEditView( pEngine.get(), nCol, nRow );
+ pEngine->SetRefDevice( rViewData.GetActiveWin()->GetOutDev() );
+ // simulate dummy cell:
+ pEditView = rViewData.GetEditView( rViewData.GetActivePart() );
+ rViewData.SetSpellingView( pEditView );
+ tools::Rectangle aRect( Point( 0, 0 ), Point( 0, 0 ) );
+ pEditView->SetOutputArea( aRect );
+ pEngine->SetControlWord( EEControlBits::USECHARATTRIBS );
+ pEngine->EnableUndo( false );
+ pEngine->SetPaperSize( aRect.GetSize() );
+ pEngine->SetTextCurrentDefaults( OUString() );
+
+ // *** do the conversion *** ----------------------------------------------
+
+ pEngine->ClearModifyFlag();
+ pEngine->ConvertAll(GetViewData().GetDialogParent(), *pEditView);
+
+ // *** undo/redo *** ------------------------------------------------------
+
+ if( pEngine->IsAnyModified() )
+ {
+ if (bRecord)
+ {
+ SCCOL nNewCol = rViewData.GetCurX();
+ SCROW nNewRow = rViewData.GetCurY();
+ rViewData.GetDocShell()->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoConversion>(
+ pDocSh, rMark,
+ nCol, nRow, nTab, std::move(pUndoDoc),
+ nNewCol, nNewRow, nTab, std::move(pRedoDoc), rConvParam ) );
+ }
+
+ sc::SetFormulaDirtyContext aCxt;
+ rDoc.SetAllFormulasDirty(aCxt);
+
+ pDocSh->SetDocumentModified();
+ }
+ else
+ {
+ pUndoDoc.reset();
+ pRedoDoc.reset();
+ }
+
+ // *** final cleanup *** --------------------------------------------------
+
+ rViewData.SetSpellingView( nullptr );
+ KillEditView(true);
+ pEngine.reset();
+ pDocSh->PostPaintGridAll();
+ rViewData.GetViewShell()->UpdateInputHandler();
+ rDoc.EnableIdle(bOldEnabled);
+}
+
+// past from SotClipboardFormatId::FILE items
+// is not called directly from Drop, but asynchronously -> dialogs are allowed
+
+bool ScViewFunc::PasteFile( const Point& rPos, const OUString& rFile, bool bLink )
+{
+ INetURLObject aURL;
+ aURL.SetSmartURL( rFile );
+ OUString aStrURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ // is it a media URL?
+#if HAVE_FEATURE_AVMEDIA
+ if( ::avmedia::MediaWindow::isMediaURL( aStrURL, ""/*TODO?*/ ) )
+ {
+ const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, aStrURL );
+ const SfxPoolItemHolder aResult(GetViewData().GetDispatcher().ExecuteList(
+ SID_INSERT_AVMEDIA, SfxCallMode::SYNCHRON,
+ { &aMediaURLItem }));
+ return (nullptr != aResult.getItem());
+ }
+#endif
+
+ if (!bLink) // for bLink only graphics or URL
+ {
+ // 1. can I open the file?
+ std::shared_ptr<const SfxFilter> pFlt;
+
+ // search only for its own filters, without selection box (as in ScDocumentLoader)
+ SfxFilterMatcher aMatcher( ScDocShell::Factory().GetFilterContainer()->GetName() );
+ SfxMedium aSfxMedium( aStrURL, (StreamMode::READ | StreamMode::SHARE_DENYNONE) );
+ // #i73992# GuessFilter no longer calls UseInteractionHandler.
+ // This is UI, so it can be called here.
+ aSfxMedium.UseInteractionHandler(true);
+ ErrCode nErr = aMatcher.GuessFilter( aSfxMedium, pFlt );
+
+ if ( pFlt && !nErr )
+ {
+ // code stolen from the SFX!
+ SfxDispatcher &rDispatcher = GetViewData().GetDispatcher();
+ SfxStringItem aFileNameItem( SID_FILE_NAME, aStrURL );
+ SfxStringItem aFilterItem( SID_FILTER_NAME, pFlt->GetName() );
+ // #i69524# add target, as in SfxApplication when the Open dialog is used
+ SfxStringItem aTargetItem( SID_TARGETNAME, "_default" );
+
+ // Open Asynchronously, because it can also happen from D&D
+ // and that is not so good for the MAC...
+ const SfxPoolItemHolder aResult(rDispatcher.ExecuteList(SID_OPENDOC,
+ SfxCallMode::ASYNCHRON,
+ { &aFileNameItem, &aFilterItem, &aTargetItem}));
+ return (nullptr != aResult.getItem());
+ }
+ }
+
+ // 2. can the file be inserted using the graphics filter?
+ // (as a link, since the Gallery provides it in this way)
+
+ Graphic aGraphic;
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+
+ if (!rGraphicFilter.ImportGraphic(aGraphic, aURL,
+ GRFILTER_FORMAT_DONTKNOW ))
+ {
+ if ( bLink )
+ {
+ return PasteGraphic( rPos, aGraphic, aStrURL );
+ }
+ else
+ {
+ // #i76709# if bLink isn't set, pass empty URL/filter, so a non-linked image is inserted
+ return PasteGraphic( rPos, aGraphic, OUString() );
+ }
+ }
+
+ if (bLink) // for bLink everything, which is not graphics, as URL
+ {
+ tools::Rectangle aRect( rPos, Size(0,0) );
+ ScRange aRange = GetViewData().GetDocument().
+ GetRange( GetViewData().GetTabNo(), aRect );
+ SCCOL nPosX = aRange.aStart.Col();
+ SCROW nPosY = aRange.aStart.Row();
+
+ InsertBookmark( aStrURL, aStrURL, nPosX, nPosY );
+ return true;
+ }
+ else
+ {
+ // 3. can the file be inserted as OLE?
+ // also non-storages, for instance sounds (#38282#)
+ uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage();
+
+ //TODO/LATER: what about "bLink"?
+
+ uno::Sequence < beans::PropertyValue > aMedium{ comphelper::makePropertyValue("URL",
+ aStrURL) };
+
+ comphelper::EmbeddedObjectContainer aCnt( xStorage );
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj = aCnt.InsertEmbeddedObject( aMedium, aName );
+ if( xObj.is() )
+ return PasteObject( rPos, xObj, nullptr );
+
+ // If an OLE object can't be created, insert a URL button
+
+ GetViewData().GetViewShell()->InsertURLButton( aStrURL, aStrURL, OUString(), &rPos );
+ return true;
+ }
+}
+
+bool ScViewFunc::PasteBookmark( SotClipboardFormatId nFormatId,
+ const css::uno::Reference< css::datatransfer::XTransferable >& rxTransferable,
+ SCCOL nPosX, SCROW nPosY )
+{
+ INetBookmark aBookmark;
+ TransferableDataHelper aDataHelper( rxTransferable );
+ if ( !aDataHelper.GetINetBookmark( nFormatId, aBookmark ) )
+ return false;
+
+ InsertBookmark( aBookmark.GetDescription(), aBookmark.GetURL(), nPosX, nPosY );
+ return true;
+}
+
+void ScViewFunc::InsertBookmark( const OUString& rDescription, const OUString& rURL,
+ SCCOL nPosX, SCROW nPosY, const OUString* pTarget,
+ bool bTryReplace )
+{
+ ScViewData& rViewData = GetViewData();
+ if ( rViewData.HasEditView( rViewData.GetActivePart() ) &&
+ nPosX >= rViewData.GetEditStartCol() && nPosX <= rViewData.GetEditEndCol() &&
+ nPosY >= rViewData.GetEditStartRow() && nPosY <= rViewData.GetEditEndRow() )
+ {
+ // insert into the cell which just got edited
+
+ OUString aTargetFrame;
+ if (pTarget)
+ aTargetFrame = *pTarget;
+ rViewData.GetViewShell()->InsertURLField( rDescription, rURL, aTargetFrame );
+ return;
+ }
+
+ // insert into not edited cell
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aCellPos( nPosX, nPosY, nTab );
+ EditEngine aEngine( rDoc.GetEnginePool() );
+
+ const EditTextObject* pOld = rDoc.GetEditText(aCellPos);
+ if (pOld)
+ aEngine.SetText(*pOld);
+ else
+ {
+ OUString aOld = rDoc.GetInputString(nPosX, nPosY, nTab);
+ if (!aOld.isEmpty())
+ aEngine.SetText(aOld);
+ }
+
+ sal_Int32 nPara = aEngine.GetParagraphCount();
+ if (nPara)
+ --nPara;
+ sal_Int32 nTxtLen = aEngine.GetTextLen(nPara);
+ ESelection aInsSel( nPara, nTxtLen, nPara, nTxtLen );
+
+ if ( bTryReplace && HasBookmarkAtCursor( nullptr ) )
+ {
+ // if called from hyperlink slot and cell contains only a URL,
+ // replace old URL with new one
+
+ aInsSel = ESelection( 0, 0, 0, 1 ); // replace first character (field)
+ }
+
+ SvxURLField aField( rURL, rDescription, SvxURLFormat::AppDefault );
+ if (pTarget)
+ aField.SetTargetFrame(*pTarget);
+ aEngine.QuickInsertField( SvxFieldItem( aField, EE_FEATURE_FIELD ), aInsSel );
+
+ std::unique_ptr<EditTextObject> pData(aEngine.CreateTextObject());
+ EnterData(nPosX, nPosY, nTab, *pData);
+}
+
+bool ScViewFunc::HasBookmarkAtCursor( SvxHyperlinkItem* pContent )
+{
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument();
+
+ const EditTextObject* pData = rDoc.GetEditText(aPos);
+ if (!pData)
+ return false;
+
+ if (!pData->IsFieldObject())
+ // not a field object.
+ return false;
+
+ const SvxFieldItem* pFieldItem = pData->GetField();
+ if (!pFieldItem)
+ // doesn't have a field item.
+ return false;
+
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if (!pField)
+ // doesn't have a field item data.
+ return false;
+
+ if (pField->GetClassId() != css::text::textfield::Type::URL)
+ // not a URL field.
+ return false;
+
+ if (pContent)
+ {
+ const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
+ pContent->SetName( pURLField->GetRepresentation() );
+ pContent->SetURL( pURLField->GetURL() );
+ pContent->SetTargetFrame( pURLField->GetTargetFrame() );
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun5.cxx b/sc/source/ui/view/viewfun5.cxx
new file mode 100644
index 0000000000..1098319506
--- /dev/null
+++ b/sc/source/ui/view/viewfun5.cxx
@@ -0,0 +1,818 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/MSOLEObjectSystemCreator.hpp>
+
+#include <svx/unomodel.hxx>
+#include <unotools/streamwrap.hxx>
+
+#include <svx/fmmodel.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <comphelper/classids.hxx>
+#include <sot/formats.hxx>
+#include <sot/filelist.hxx>
+#include <sot/storage.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <osl/thread.h>
+#include <o3tl/unit_conversion.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <comphelper/automationinvokedzone.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+
+#include <viewfunc.hxx>
+#include <docsh.hxx>
+#include <drawview.hxx>
+#include <impex.hxx>
+#include <dbdata.hxx>
+#include <sc.hrc>
+#include <filter.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <scextopt.hxx>
+#include <tabvwsh.hxx>
+#include <compiler.hxx>
+#include <scmod.hxx>
+
+#include <asciiopt.hxx>
+#include <scabstdlg.hxx>
+#include <clipparam.hxx>
+#include <markdata.hxx>
+#include <sfx2/frame.hxx>
+#include <svx/dbaexchange.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+bool ScViewFunc::PasteDataFormat( SotClipboardFormatId nFormatId,
+ const uno::Reference<datatransfer::XTransferable>& rxTransferable,
+ SCCOL nPosX, SCROW nPosY, const Point* pLogicPos, bool bLink, bool bAllowDialogs )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ rDoc.SetPastingDrawFromOtherDoc( true );
+
+ Point aPos; // inserting position (1/100 mm)
+ if (pLogicPos)
+ aPos = *pLogicPos;
+ else
+ {
+ // inserting position isn't needed for text formats
+ bool bIsTextFormat = ( ScImportExport::IsFormatSupported( nFormatId ) ||
+ nFormatId == SotClipboardFormatId::RTF );
+ if ( !bIsTextFormat )
+ {
+ // Window MapMode isn't drawing MapMode if DrawingLayer hasn't been created yet
+
+ SCTAB nTab = GetViewData().GetTabNo();
+ tools::Long nXT = 0;
+ for (SCCOL i=0; i<nPosX; i++)
+ nXT += rDoc.GetColWidth(i,nTab);
+ if (rDoc.IsNegativePage(nTab))
+ nXT = -nXT;
+ tools::Long nYT = rDoc.GetRowHeight( 0, nPosY-1, nTab);
+ aPos = Point(o3tl::convert(nXT, o3tl::Length::twip, o3tl::Length::mm100),
+ o3tl::convert(nYT, o3tl::Length::twip, o3tl::Length::mm100));
+ }
+ }
+
+ TransferableDataHelper aDataHelper( rxTransferable );
+ bool bRet = false;
+
+ // handle individual formats
+
+ if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE ||
+ nFormatId == SotClipboardFormatId::LINK_SOURCE ||
+ nFormatId == SotClipboardFormatId::EMBED_SOURCE_OLE ||
+ nFormatId == SotClipboardFormatId::LINK_SOURCE_OLE ||
+ nFormatId == SotClipboardFormatId::EMBEDDED_OBJ_OLE )
+ {
+ uno::Reference < io::XInputStream > xStm;
+ TransferableObjectDescriptor aObjDesc;
+
+ if (aDataHelper.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc))
+ xStm = aDataHelper.GetInputStream(nFormatId, OUString());
+
+ if (xStm.is())
+ {
+ if ( aObjDesc.maClassName == SvGlobalName( SO3_SC_CLASSID_60 ) )
+ {
+ uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm );
+
+ // mba: BaseURL doesn't make sense for clipboard
+ // #i43716# Medium must be allocated with "new".
+ // DoLoad stores the pointer and deletes it with the SfxObjectShell.
+ SfxMedium* pMedium = new SfxMedium( xStore, OUString() );
+
+ // TODO/LATER: is it a problem that we don't support binary formats here?
+ ScDocShellRef xDocShRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT);
+ if (xDocShRef->DoLoad(pMedium))
+ {
+ ScDocument& rSrcDoc = xDocShRef->GetDocument();
+ SCTAB nSrcTab = rSrcDoc.GetVisibleTab();
+ if (!rSrcDoc.HasTable(nSrcTab))
+ nSrcTab = 0;
+
+ ScMarkData aSrcMark(rSrcDoc.GetSheetLimits());
+ aSrcMark.SelectOneTable( nSrcTab ); // for CopyToClip
+ ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
+
+ SCCOL nFirstCol, nLastCol;
+ SCROW nFirstRow, nLastRow;
+ if ( rSrcDoc.GetDataStart( nSrcTab, nFirstCol, nFirstRow ) )
+ {
+ rSrcDoc.GetCellArea( nSrcTab, nLastCol, nLastRow );
+ if (nLastCol < nFirstCol)
+ nLastCol = nFirstCol;
+ if (nLastRow < nFirstRow)
+ nLastRow = nFirstRow;
+ }
+ else
+ {
+ nFirstCol = nLastCol = 0;
+ nFirstRow = nLastRow = 0;
+ }
+
+ bool bIncludeObjects = false; // include drawing layer objects in CopyToClip ?
+
+ if (nFormatId == SotClipboardFormatId::EMBED_SOURCE)
+ {
+ const ScDrawLayer* pDraw = rSrcDoc.GetDrawLayer();
+ SCCOL nPrintEndCol = nFirstCol;
+ SCROW nPrintEndRow = nFirstRow;
+ bool bHasObjects = pDraw && pDraw->HasObjects();
+ // Extend the range to include the drawing layer objects.
+ if (bHasObjects && rSrcDoc.GetPrintArea(nSrcTab, nPrintEndCol, nPrintEndRow, true))
+ {
+ nLastCol = std::max<SCCOL>(nLastCol, nPrintEndCol);
+ nLastRow = std::max<SCROW>(nLastRow, nPrintEndRow);
+ }
+
+ bIncludeObjects = bHasObjects;
+ }
+
+ ScClipParam aClipParam(ScRange(nFirstCol, nFirstRow, nSrcTab, nLastCol, nLastRow, nSrcTab), false);
+ rSrcDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSrcMark, false, bIncludeObjects);
+ ScGlobal::SetClipDocName( xDocShRef->GetTitle( SFX_TITLE_FULLNAME ) );
+
+ SetCursor( nPosX, nPosY );
+ Unmark();
+ PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(),
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ bAllowDialogs );
+ bRet = true;
+ }
+
+ xDocShRef->DoClose();
+ xDocShRef.clear();
+ }
+ else
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj = GetViewData().GetViewShell()->GetObjectShell()->
+ GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName );
+ if ( xObj.is() )
+ {
+ // try to get the replacement image from the clipboard
+ Graphic aGraphic;
+ SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE;
+
+ // limit the size of the preview metafile to 100000 actions
+ GDIMetaFile aMetafile;
+ if (aDataHelper.GetGDIMetaFile(SotClipboardFormatId::GDIMETAFILE, aMetafile, 100000))
+ {
+ nGrFormat = SotClipboardFormatId::GDIMETAFILE;
+ aGraphic = aMetafile;
+ }
+
+ // insert replacement image ( if there is one ) into the object helper
+ if ( nGrFormat != SotClipboardFormatId::NONE )
+ {
+ datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor );
+ PasteObject( aPos, xObj, &aObjDesc.maSize, &aGraphic, aDataFlavor.MimeType, aObjDesc.mnViewAspect );
+ }
+ else
+ PasteObject( aPos, xObj, &aObjDesc.maSize );
+
+ bRet = true;
+ }
+ else
+ {
+ OSL_FAIL("Error in CreateAndLoad");
+ }
+ }
+ }
+ else
+ {
+ if ( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE, aObjDesc ) )
+ {
+ OUString aName;
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBED_SOURCE_OLE, OUString());
+ if (!xStm.is())
+ aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString());
+
+ if (xStm.is())
+ {
+ xObj = GetViewData().GetDocShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName );
+ }
+ else
+ {
+ try
+ {
+ uno::Reference< embed::XStorage > xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage();
+ uno::Reference < embed::XEmbedObjectClipboardCreator > xClipboardCreator =
+ embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() );
+
+ embed::InsertedObjectInfo aInfo = xClipboardCreator->createInstanceInitFromClipboard(
+ xTmpStor,
+ "DummyName",
+ uno::Sequence< beans::PropertyValue >() );
+
+ // TODO/LATER: in future InsertedObjectInfo will be used to get container related information
+ // for example whether the object should be an iconified one
+ xObj = aInfo.Object;
+ if ( xObj.is() )
+ GetViewData().GetDocShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ if ( xObj.is() )
+ {
+ // try to get the replacement image from the clipboard
+ Graphic aGraphic;
+ SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE;
+
+// (for Selection Manager in Trusted Solaris)
+#ifndef __sun
+ if( aDataHelper.GetGraphic( SotClipboardFormatId::SVXB, aGraphic ) )
+ nGrFormat = SotClipboardFormatId::SVXB;
+ else if( aDataHelper.GetGraphic( SotClipboardFormatId::GDIMETAFILE, aGraphic ) )
+ nGrFormat = SotClipboardFormatId::GDIMETAFILE;
+ else if( aDataHelper.GetGraphic( SotClipboardFormatId::BITMAP, aGraphic ) )
+ nGrFormat = SotClipboardFormatId::BITMAP;
+#endif
+
+ // insert replacement image ( if there is one ) into the object helper
+ if ( nGrFormat != SotClipboardFormatId::NONE )
+ {
+ datatransfer::DataFlavor aDataFlavor;
+ SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor );
+ PasteObject( aPos, xObj, &aObjDesc.maSize, &aGraphic, aDataFlavor.MimeType, aObjDesc.mnViewAspect );
+ }
+ else
+ PasteObject( aPos, xObj, &aObjDesc.maSize );
+
+ // let object stay in loaded state after insertion
+ SdrOle2Obj::Unload( xObj, embed::Aspects::MSOLE_CONTENT );
+ bRet = true;
+ }
+ else
+ {
+ OSL_FAIL("Error creating external OLE object");
+ }
+ }
+ //TODO/LATER: if format is not available, create picture
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::LINK ) // LINK is also in ScImportExport
+ {
+ bRet = PasteLink( rxTransferable );
+ }
+ else if ( ScImportExport::IsFormatSupported( nFormatId ) || nFormatId == SotClipboardFormatId::RTF ||
+ nFormatId == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT )
+ {
+ if ( nFormatId == SotClipboardFormatId::RTF && ( aDataHelper.HasFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) ) )
+ {
+ // use EditView's PasteSpecial / Drop
+ PasteRTF( nPosX, nPosY, rxTransferable );
+ bRet = true;
+ }
+ else
+ {
+ ScAddress aCellPos( nPosX, nPosY, GetViewData().GetTabNo() );
+ auto pObj = std::make_shared<ScImportExport>(GetViewData().GetDocument(), aCellPos);
+ pObj->SetOverwriting( true );
+
+
+ auto pStrBuffer = std::make_shared<OUString>();
+ tools::SvRef<SotTempStream> xStream;
+ if ( aDataHelper.GetSotStorageStream( nFormatId, xStream ) && xStream.is() )
+ {
+ // Static variables for per-session storage. This could be
+ // changed to longer-term storage in future.
+ static bool bHaveSavedPreferences = false;
+ static LanguageType eSavedLanguage;
+ static bool bSavedDateConversion;
+ static bool bSavedScientificConversion;
+
+ if (nFormatId == SotClipboardFormatId::HTML &&
+ !comphelper::LibreOfficeKit::isActive())
+ {
+ if (bHaveSavedPreferences)
+ {
+ ScAsciiOptions aOptions;
+ aOptions.SetLanguage(eSavedLanguage);
+ aOptions.SetDetectSpecialNumber(bSavedDateConversion);
+ aOptions.SetDetectScientificNumber(bSavedScientificConversion);
+ pObj->SetExtOptions(aOptions);
+ }
+ else
+ {
+ // Launch the text import options dialog. For now, we do
+ // this for html pasting only, but in the future it may
+ // make sense to do it for other data types too.
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ vcl::Window* pParent = GetActiveWin();
+ ScopedVclPtr<AbstractScTextImportOptionsDlg> pDlg(
+ pFact->CreateScTextImportOptionsDlg(pParent ? pParent->GetFrameWeld() : nullptr));
+
+ if (pDlg->Execute() == RET_OK)
+ {
+ ScAsciiOptions aOptions;
+ aOptions.SetLanguage(pDlg->GetLanguageType());
+ aOptions.SetDetectSpecialNumber(pDlg->IsDateConversionSet());
+ aOptions.SetDetectScientificNumber(pDlg->IsScientificConversionSet());
+ if (!pDlg->IsKeepAskingSet())
+ {
+ bHaveSavedPreferences = true;
+ eSavedLanguage = pDlg->GetLanguageType();
+ bSavedDateConversion = pDlg->IsDateConversionSet();
+ bSavedScientificConversion = pDlg->IsScientificConversionSet();
+ }
+ pObj->SetExtOptions(aOptions);
+ }
+ else
+ {
+ // prevent error dialog for user cancel action
+ bRet = true;
+ }
+ }
+ }
+ if(!bRet)
+ bRet = pObj->ImportStream( *xStream, OUString(), nFormatId );
+ // mba: clipboard always must contain absolute URLs (could be from alien source)
+ }
+ else if ((nFormatId == SotClipboardFormatId::STRING || nFormatId == SotClipboardFormatId::STRING_TSVC)
+ && aDataHelper.GetString( nFormatId, *pStrBuffer ))
+ {
+ // Do CSV dialog if more than one line. But not if invoked from Automation.
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ sal_Int32 nDelim = pStrBuffer->indexOf('\n');
+ if (!(pViewShell && pViewShell->isLOKMobilePhone()) && !comphelper::Automation::AutomationInvokedZone::isActive()
+ && nDelim >= 0 && nDelim != pStrBuffer->getLength () - 1)
+ {
+ vcl::Window* pParent = GetActiveWin();
+
+ auto pStrm = std::make_shared<ScImportStringStream>(*pStrBuffer);
+
+ ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
+ VclPtr<AbstractScImportAsciiDlg> pDlg(
+ pFact->CreateScImportAsciiDlg(pParent ? pParent->GetFrameWeld() : nullptr, OUString(), pStrm.get(), SC_PASTETEXT));
+
+ bAllowDialogs = bAllowDialogs && !SC_MOD()->IsInExecuteDrop();
+
+ pDlg->StartExecuteAsync([this, pDlg, &rDoc, pStrm, nFormatId, pStrBuffer, pObj, bAllowDialogs](sal_Int32 nResult){
+ bool bShowErrorDialog = bAllowDialogs;
+ if (RET_OK == nResult)
+ {
+ ScAsciiOptions aOptions;
+ pDlg->GetOptions( aOptions );
+ pDlg->SaveParameters();
+ pObj->SetExtOptions( aOptions );
+ pObj->ImportString( *pStrBuffer, nFormatId );
+
+ // TODO: what if (aObj.IsOverflow())
+ // Content was partially pasted, which can be undone by
+ // the user though.
+ bShowErrorDialog = bShowErrorDialog && pObj->IsOverflow();
+ }
+ else
+ {
+ bShowErrorDialog = false;
+ // Yes, no failure, don't raise a "couldn't paste"
+ // dialog if user cancelled.
+ }
+
+ InvalidateAttribs();
+ GetViewData().UpdateInputHandler();
+
+ rDoc.SetPastingDrawFromOtherDoc( false );
+
+ if (bShowErrorDialog)
+ ErrorMessage(STR_PASTE_ERROR);
+ pDlg->disposeOnce();
+ });
+ return true;
+ }
+ else
+ bRet = pObj->ImportString( *pStrBuffer, nFormatId );
+ }
+ else if ((nFormatId != SotClipboardFormatId::STRING && nFormatId != SotClipboardFormatId::STRING_TSVC)
+ && aDataHelper.GetString( nFormatId, *pStrBuffer ))
+ bRet = pObj->ImportString( *pStrBuffer, nFormatId );
+
+ InvalidateAttribs();
+ GetViewData().UpdateInputHandler();
+ }
+ }
+ else if (nFormatId == SotClipboardFormatId::SBA_DATAEXCHANGE)
+ {
+ // import of database data into table
+
+ const DataFlavorExVector& rVector = aDataHelper.GetDataFlavorExVector();
+ if ( svx::ODataAccessObjectTransferable::canExtractObjectDescriptor(rVector) )
+ {
+ // transport the whole ODataAccessDescriptor as slot parameter
+ svx::ODataAccessDescriptor aDesc = svx::ODataAccessObjectTransferable::extractObjectDescriptor(aDataHelper);
+ uno::Any aDescAny;
+ uno::Sequence<beans::PropertyValue> aProperties = aDesc.createPropertyValueSequence();
+ aDescAny <<= aProperties;
+ SfxUnoAnyItem aDataDesc(SID_SBA_IMPORT, aDescAny);
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ ClickCursor(nPosX, nPosY, false); // set cursor position
+
+ // Creation of database area "Import1" isn't here, but in the DocShell
+ // slot execute, so it can be added to the undo action
+
+ ScDBData* pDBData = pDocSh->GetDBData( ScRange(nPosX,nPosY,nTab), SC_DB_OLD, ScGetDBSelection::Keep );
+ OUString sTarget;
+ if (pDBData)
+ sTarget = pDBData->GetName();
+ else
+ {
+ ScAddress aCellPos( nPosX,nPosY,nTab );
+ sTarget = aCellPos.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention());
+ }
+ SfxStringItem aTarget(FN_PARAM_1, sTarget);
+
+ bool bAreaIsNew = !pDBData;
+ SfxBoolItem aAreaNew(FN_PARAM_2, bAreaIsNew);
+
+ // asynchronous, to avoid doing the whole import in drop handler
+ SfxDispatcher& rDisp = GetViewData().GetDispatcher();
+ rDisp.ExecuteList(SID_SBA_IMPORT, SfxCallMode::ASYNCHRON,
+ { &aDataDesc, &aTarget, &aAreaNew });
+
+ bRet = true;
+ }
+ }
+ else if (nFormatId == SotClipboardFormatId::SBA_FIELDDATAEXCHANGE)
+ {
+ // insert database field control
+
+ if ( svx::OColumnTransferable::canExtractColumnDescriptor( aDataHelper.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE ) )
+ {
+ MakeDrawLayer();
+ ScDrawView* pScDrawView = GetScDrawView();
+ rtl::Reference<SdrObject> pObj = pScDrawView->CreateFieldControl( svx::OColumnTransferable::extractColumnDescriptor( aDataHelper ) );
+ if (pObj)
+ {
+ Point aInsPos = aPos;
+ tools::Rectangle aRect(pObj->GetLogicRect());
+ aInsPos.AdjustX( -(aRect.GetSize().Width() / 2) );
+ aInsPos.AdjustY( -(aRect.GetSize().Height() / 2) );
+ if ( aInsPos.X() < 0 ) aInsPos.setX( 0 );
+ if ( aInsPos.Y() < 0 ) aInsPos.setY( 0 );
+ aRect.SetPos(aInsPos);
+ pObj->SetLogicRect(aRect);
+
+ if ( dynamic_cast<const SdrUnoObj*>( pObj.get() ) != nullptr )
+ pObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pObj->NbcSetLayer(SC_LAYER_FRONT);
+ if (dynamic_cast<const SdrObjGroup*>( pObj.get() ) != nullptr)
+ {
+ SdrObjListIter aIter( *pObj, SdrIterMode::DeepWithGroups );
+ SdrObject* pSubObj = aIter.Next();
+ while (pSubObj)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pSubObj) != nullptr )
+ pSubObj->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pSubObj->NbcSetLayer(SC_LAYER_FRONT);
+ pSubObj = aIter.Next();
+ }
+ }
+
+ pScDrawView->InsertObjectSafe(pObj.get(), *pScDrawView->GetSdrPageView());
+
+ GetViewData().GetViewShell()->SetDrawShell( true );
+ bRet = true;
+ }
+ }
+ }
+ else if (nFormatId == SotClipboardFormatId::BITMAP || nFormatId == SotClipboardFormatId::PNG || nFormatId == SotClipboardFormatId::JPEG)
+ {
+ BitmapEx aBmpEx;
+ if( aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) )
+ bRet = PasteBitmapEx( aPos, aBmpEx );
+ }
+ else if (nFormatId == SotClipboardFormatId::GDIMETAFILE)
+ {
+ GDIMetaFile aMtf;
+ if( aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) )
+ bRet = PasteMetaFile( aPos, aMtf );
+ }
+ else if (nFormatId == SotClipboardFormatId::SVXB)
+ {
+ tools::SvRef<SotTempStream> xStm;
+ if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) )
+ {
+ Graphic aGraphic;
+ TypeSerializer aSerializer(*xStm);
+ aSerializer.readGraphic(aGraphic);
+ bRet = PasteGraphic( aPos, aGraphic, OUString() );
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::DRAWING )
+ {
+ tools::SvRef<SotTempStream> xStm;
+ if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStm ) )
+ {
+ MakeDrawLayer(); // before loading model, so 3D factory has been created
+
+ ScDocShellRef aDragShellRef( new ScDocShell );
+ aDragShellRef->MakeDrawLayer();
+ aDragShellRef->DoInitNew();
+
+ ScDrawLayer* pModel = aDragShellRef->GetDocument().GetDrawLayer();
+
+ xStm->Seek(0);
+
+ css::uno::Reference< css::io::XInputStream > xInputStream( new utl::OInputStreamWrapper( *xStm ) );
+ SvxDrawingLayerImport( pModel, xInputStream );
+
+ // set everything to right layer:
+ size_t nObjCount = 0;
+ sal_uInt16 nPages = pModel->GetPageCount();
+ for (sal_uInt16 i=0; i<nPages; i++)
+ {
+ SdrPage* pPage = pModel->GetPage(i);
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pObject) != nullptr )
+ pObject->NbcSetLayer(SC_LAYER_CONTROLS);
+ else
+ pObject->NbcSetLayer(SC_LAYER_FRONT);
+ pObject = aIter.Next();
+ }
+
+ nObjCount += pPage->GetObjCount(); // count group object only once
+ }
+
+ PasteDraw(aPos, pModel, (nObjCount > 1), u"A", u"B"); // grouped if more than 1 object
+ aDragShellRef->DoClose();
+ bRet = true;
+ }
+ }
+ else if ( (nFormatId == SotClipboardFormatId::BIFF_5) || (nFormatId == SotClipboardFormatId::BIFF_8) )
+ {
+ // do excel import into a clipboard document
+ //TODO/MBA: testing
+ uno::Reference <io::XInputStream> xStm = aDataHelper.GetInputStream(nFormatId, OUString());
+ if (xStm.is())
+ {
+ ScDocument aInsDoc( SCDOCMODE_CLIP );
+ SCTAB nSrcTab = 0; // Biff5 in clipboard: always sheet 0
+ aInsDoc.ResetClip( &rDoc, nSrcTab );
+
+ SfxMedium aMed;
+ aMed.GetItemSet().Put( SfxUnoAnyItem( SID_INPUTSTREAM, uno::Any( xStm ) ) );
+ ErrCode eErr = ScFormatFilter::Get().ScImportExcel( aMed, &aInsDoc, EIF_AUTO );
+ if ( eErr == ERRCODE_NONE )
+ {
+ ScRange aSource;
+ const ScExtDocOptions* pExtOpt = aInsDoc.GetExtDocOptions();
+ const ScExtTabSettings* pTabSett = pExtOpt ? pExtOpt->GetTabSettings( nSrcTab ) : nullptr;
+ if( pTabSett && pTabSett->maUsedArea.IsValid() )
+ {
+ aSource = pTabSett->maUsedArea;
+ // ensure correct sheet indexes
+ aSource.aStart.SetTab( nSrcTab );
+ aSource.aEnd.SetTab( nSrcTab );
+// don't use selection area: if cursor is moved in Excel after Copy, selection
+// represents the new cursor position and not the copied area
+ }
+ else
+ {
+ OSL_FAIL("no dimension"); //! possible?
+ SCCOL nFirstCol, nLastCol;
+ SCROW nFirstRow, nLastRow;
+ if ( aInsDoc.GetDataStart( nSrcTab, nFirstCol, nFirstRow ) )
+ aInsDoc.GetCellArea( nSrcTab, nLastCol, nLastRow );
+ else
+ {
+ nFirstCol = nLastCol = 0;
+ nFirstRow = nLastRow = 0;
+ }
+ aSource = ScRange( nFirstCol, nFirstRow, nSrcTab,
+ nLastCol, nLastRow, nSrcTab );
+ }
+
+ if ( pLogicPos )
+ {
+ // position specified (Drag&Drop) - change selection
+ MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false );
+ Unmark();
+ }
+
+ aInsDoc.SetClipArea( aSource );
+ PasteFromClip( InsertDeleteFlags::ALL, &aInsDoc,
+ ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE,
+ bAllowDialogs );
+ bRet = true;
+ }
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::SIMPLE_FILE )
+ {
+ OUString aFile;
+ if ( aDataHelper.GetString( nFormatId, aFile ) )
+ bRet = PasteFile( aPos, aFile, bLink );
+ }
+ else if ( nFormatId == SotClipboardFormatId::FILE_LIST )
+ {
+ FileList aFileList;
+ if ( aDataHelper.GetFileList( nFormatId, aFileList ) )
+ {
+ sal_uLong nCount = aFileList.Count();
+ for( sal_uLong i = 0; i < nCount ; i++ )
+ {
+ OUString aFile = aFileList.GetFile( i );
+
+ PasteFile( aPos, aFile, bLink );
+
+ aPos.AdjustX(400 );
+ aPos.AdjustY(400 );
+ }
+ bRet = true;
+ }
+ }
+ else if ( nFormatId == SotClipboardFormatId::SOLK ||
+ nFormatId == SotClipboardFormatId::UNIFORMRESOURCELOCATOR ||
+ nFormatId == SotClipboardFormatId::NETSCAPE_BOOKMARK ||
+ nFormatId == SotClipboardFormatId::FILEGRPDESCRIPTOR )
+ {
+ bRet = PasteBookmark( nFormatId, rxTransferable, nPosX, nPosY );
+ }
+
+ rDoc.SetPastingDrawFromOtherDoc( false );
+
+ return bRet;
+}
+
+bool ScViewFunc::PasteLink( const uno::Reference<datatransfer::XTransferable>& rxTransferable )
+{
+ TransferableDataHelper aDataHelper( rxTransferable );
+
+ // get link data from transferable before string data,
+ // so the source knows it will be used for a link
+
+ uno::Sequence<sal_Int8> aSequence = aDataHelper.GetSequence(SotClipboardFormatId::LINK, OUString());
+ if (!aSequence.hasElements())
+ {
+ OSL_FAIL("DDE Data not found.");
+ return false;
+ }
+
+ // check size (only if string is available in transferable)
+
+ sal_uInt16 nCols = 1;
+ sal_uInt16 nRows = 1;
+ if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) )
+ {
+ OUString aDataStr;
+ if ( aDataHelper.GetString( SotClipboardFormatId::STRING, aDataStr ) )
+ {
+ // get size from string the same way as in ScDdeLink::DataChanged
+
+ aDataStr = convertLineEnd(aDataStr, LINEEND_LF);
+ sal_Int32 nLen = aDataStr.getLength();
+ if (nLen && aDataStr[nLen-1] == '\n')
+ aDataStr = aDataStr.copy(0, nLen-1);
+
+ if (!aDataStr.isEmpty())
+ {
+ nRows = comphelper::string::getTokenCount(aDataStr, '\n');
+ std::u16string_view aLine = o3tl::getToken(aDataStr, 0, '\n' );
+ if (!aLine.empty())
+ nCols = comphelper::string::getTokenCount(aLine, '\t');
+ }
+ }
+ }
+
+ // create formula
+
+ sal_Int32 nSeqLen = aSequence.getLength();
+ const char* p = reinterpret_cast<const char*>(aSequence.getConstArray());
+
+ rtl_TextEncoding eSysEnc = osl_getThreadTextEncoding();
+
+ // char array delimited by \0.
+ // app \0 topic \0 item \0 (extra \0) where the extra is optional.
+ ::std::vector<OUString> aStrs;
+ const char* pStart = p;
+ sal_Int32 nStart = 0;
+ for (sal_Int32 i = 0; i < nSeqLen; ++i, ++p)
+ {
+ if (*p == '\0')
+ {
+ sal_Int32 nLen = i - nStart;
+ aStrs.emplace_back(pStart, nLen, eSysEnc);
+ nStart = ++i;
+ pStart = ++p;
+ }
+ }
+
+ if (aStrs.size() < 3)
+ return false;
+
+ const OUString& pApp = aStrs[0];
+ const OUString& pTopic = aStrs[1];
+ const OUString& pItem = aStrs[2];
+ const OUString* pExtra = nullptr;
+ if (aStrs.size() > 3)
+ pExtra = &aStrs[3];
+
+ if ( pExtra && *pExtra == "calc:extref" )
+ {
+ // Paste this as an external reference. Note that paste link always
+ // uses Calc A1 syntax even when another formula syntax is specified
+ // in the UI.
+ EnterMatrix("='"
+ + ScGlobal::GetAbsDocName(pTopic, GetViewData().GetDocument().GetDocumentShell())
+ + "'#" + pItem
+ , ::formula::FormulaGrammar::GRAM_NATIVE);
+ return true;
+ }
+ else
+ {
+ // DDE in all other cases.
+
+ // TODO: we could define ocQuote for "
+ EnterMatrix("=" + ScCompiler::GetNativeSymbol(ocDde)
+ + ScCompiler::GetNativeSymbol(ocOpen)
+ + "\"" + pApp + "\""
+ + ScCompiler::GetNativeSymbol(ocSep)
+ + "\"" + pTopic + "\""
+ + ScCompiler::GetNativeSymbol(ocSep)
+ + "\"" + pItem + "\""
+ + ScCompiler::GetNativeSymbol(ocClose)
+ , ::formula::FormulaGrammar::GRAM_NATIVE);
+ }
+
+ // mark range
+
+ SCTAB nTab = GetViewData().GetTabNo();
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ HideAllCursors();
+ DoneBlockMode();
+ InitBlockMode( nCurX, nCurY, nTab );
+ MarkCursor( nCurX+static_cast<SCCOL>(nCols)-1, nCurY+static_cast<SCROW>(nRows)-1, nTab );
+ ShowAllCursors();
+ CursorPosChanged();
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun6.cxx b/sc/source/ui/view/viewfun6.cxx
new file mode 100644
index 0000000000..a840670dfc
--- /dev/null
+++ b/sc/source/ui/view/viewfun6.cxx
@@ -0,0 +1,559 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formula/token.hxx>
+#include <svx/svdocapt.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <svl/stritem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <editeng/editview.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+
+#include <viewfunc.hxx>
+#include <viewdata.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <futext.hxx>
+#include <docfunc.hxx>
+#include <sc.hrc>
+#include <fusel.hxx>
+#include <reftokenhelper.hxx>
+#include <externalrefmgr.hxx>
+#include <markdata.hxx>
+#include <drawview.hxx>
+#include <inputhdl.hxx>
+#include <tabvwsh.hxx>
+#include <scmod.hxx>
+#include <postit.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <vector>
+
+namespace
+{
+
+void collectUIInformation( const OUString& aevent )
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aParameters = {{ aevent , ""}};
+ aDescription.aAction = "COMMENT";
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+using ::std::vector;
+
+void ScViewFunc::DetectiveAddPred()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveAddPred( GetViewData().GetCurPos() );
+ RecalcPPT(); //! use broadcast in DocFunc instead?
+}
+
+void ScViewFunc::DetectiveDelPred()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveDelPred( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveAddSucc()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveAddSucc( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveDelSucc()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveDelSucc( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveAddError()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveAddError( GetViewData().GetCurPos() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveDelAll()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveDelAll( GetViewData().GetTabNo() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveMarkInvalid()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveMarkInvalid( GetViewData().GetTabNo() );
+ RecalcPPT();
+}
+
+void ScViewFunc::DetectiveRefresh()
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ pDocSh->GetDocFunc().DetectiveRefresh();
+ RecalcPPT();
+}
+
+static void lcl_jumpToRange(const ScRange& rRange, ScViewData* pView, const ScDocument& rDoc)
+{
+ OUString aAddrText(rRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D));
+ SfxStringItem aPosItem(SID_CURRENTCELL, aAddrText);
+ SfxBoolItem aUnmarkItem(FN_PARAM_1, true); // remove existing selection
+ pView->GetDispatcher().ExecuteList(
+ SID_CURRENTCELL, SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aPosItem, &aUnmarkItem });
+}
+
+void ScViewFunc::MarkAndJumpToRanges(const ScRangeList& rRanges)
+{
+ ScViewData& rView = GetViewData();
+ ScDocShell* pDocSh = rView.GetDocShell();
+
+ ScRangeList aRanges(rRanges);
+ ScRangeList aRangesToMark;
+ ScAddress aCurPos = rView.GetCurPos();
+ size_t ListSize = aRanges.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ const ScRange & r = aRanges[i];
+ // Collect only those ranges that are on the same sheet as the current
+ // cursor.
+ if (r.aStart.Tab() == aCurPos.Tab())
+ aRangesToMark.push_back(r);
+ }
+
+ if (aRangesToMark.empty())
+ return;
+
+ // Jump to the first range of all precedent ranges.
+ const ScRange & r = aRangesToMark.front();
+ lcl_jumpToRange(r, &rView, pDocSh->GetDocument());
+
+ ListSize = aRangesToMark.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ MarkRange(aRangesToMark[i], false, true);
+ }
+}
+
+void ScViewFunc::DetectiveMarkPred()
+{
+ ScViewData& rView = GetViewData();
+ ScDocShell* pDocSh = rView.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMarkData = rView.GetMarkData();
+ ScAddress aCurPos = rView.GetCurPos();
+ ScRangeList aRanges;
+ if (rMarkData.IsMarked() || rMarkData.IsMultiMarked())
+ rMarkData.FillRangeListWithMarks(&aRanges, false);
+ else
+ aRanges.push_back(aCurPos);
+
+ vector<ScTokenRef> aRefTokens;
+ pDocSh->GetDocFunc().DetectiveCollectAllPreds(aRanges, aRefTokens);
+
+ if (aRefTokens.empty())
+ // No precedents found. Nothing to do.
+ return;
+
+ ScTokenRef p = aRefTokens.front();
+ if (ScRefTokenHelper::isExternalRef(p))
+ {
+ // This is external. Open the external document if available, and
+ // jump to the destination.
+
+ sal_uInt16 nFileId = p->GetIndex();
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ const OUString* pPath = pRefMgr->getExternalFileName(nFileId);
+
+ ScRange aRange;
+ if (pPath && ScRefTokenHelper::getRangeFromToken(&rDoc, aRange, p, aCurPos, true))
+ {
+ OUString aTabName = p->GetString().getString();
+ OUString aRangeStr(aRange.Format(rDoc, ScRefFlags::VALID));
+ OUString sUrl =
+ *pPath +
+ "#" +
+ aTabName +
+ "." +
+ aRangeStr;
+
+ ScGlobal::OpenURL(sUrl, OUString());
+ }
+ return;
+ }
+ else
+ {
+ ScRange aRange;
+ ScRefTokenHelper::getRangeFromToken(&rDoc, aRange, p, aCurPos);
+ if (aRange.aStart.Tab() != aCurPos.Tab())
+ {
+ // The first precedent range is on a different sheet. Jump to it
+ // immediately and forget the rest.
+ lcl_jumpToRange(aRange, &rView, rDoc);
+ return;
+ }
+ }
+
+ ScRangeList aDestRanges;
+ ScRefTokenHelper::getRangeListFromTokens(&rDoc, aDestRanges, aRefTokens, aCurPos);
+ MarkAndJumpToRanges(aDestRanges);
+}
+
+void ScViewFunc::DetectiveMarkSucc()
+{
+ ScViewData& rView = GetViewData();
+ ScDocShell* pDocSh = rView.GetDocShell();
+ ScMarkData& rMarkData = rView.GetMarkData();
+ ScAddress aCurPos = rView.GetCurPos();
+ ScRangeList aRanges;
+ if (rMarkData.IsMarked() || rMarkData.IsMultiMarked())
+ rMarkData.FillRangeListWithMarks(&aRanges, false);
+ else
+ aRanges.push_back(aCurPos);
+
+ vector<ScTokenRef> aRefTokens;
+ pDocSh->GetDocFunc().DetectiveCollectAllSuccs(aRanges, aRefTokens);
+
+ if (aRefTokens.empty())
+ // No dependents found. Nothing to do.
+ return;
+
+ ScRangeList aDestRanges;
+ ScRefTokenHelper::getRangeListFromTokens(&rView.GetDocument(), aDestRanges, aRefTokens, aCurPos);
+ MarkAndJumpToRanges(aDestRanges);
+}
+
+/** Insert date or time into current cell.
+
+ If cell is in input or edit mode, insert date/time at cursor position, else
+ create a date or time or date+time cell as follows:
+
+ - key date on time cell => current date + time of cell => date+time formatted cell
+ - unless time cell was empty or 00:00 time => current date => date formatted cell
+ - key date on date+time cell => current date + 00:00 time => date+time formatted cell
+ - unless date was current date => current date => date formatted cell
+ - key date on other cell => current date => date formatted cell
+ - key time on date cell => date of cell + current time => date+time formatted cell
+ - unless date cell was empty => current time => time formatted cell
+ - key time on date+time cell => current time => time formatted cell
+ - unless cell was empty => current date+time => date+time formatted cell
+ - key time on other cell => current time => time formatted cell
+ */
+void ScViewFunc::InsertCurrentTime(SvNumFormatType nReqFmt, const OUString& rUndoStr)
+{
+ ScViewData& rViewData = GetViewData();
+
+ ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl( rViewData.GetViewShell());
+ bool bInputMode = (pInputHdl && pInputHdl->IsInputMode());
+
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScAddress aCurPos = rViewData.GetCurPos();
+ const sal_uInt32 nCurNumFormat = rDoc.GetNumberFormat(aCurPos);
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ const SvNumberformat* pCurNumFormatEntry = pFormatter->GetEntry(nCurNumFormat);
+ const SvNumFormatType nCurNumFormatType = (pCurNumFormatEntry ?
+ pCurNumFormatEntry->GetMaskedType() : SvNumFormatType::UNDEFINED);
+
+ const int nView(comphelper::LibreOfficeKit::isActive() ? SfxLokHelper::getView() : -1);
+ if (nView >= 0)
+ {
+ const auto [isTimezoneSet, aTimezone] = SfxLokHelper::getViewTimezone(nView);
+ comphelper::LibreOfficeKit::setTimezone(isTimezoneSet, aTimezone);
+ }
+
+ comphelper::ScopeGuard aAutoUserTimezone(
+ [nView]()
+ {
+ if (nView >= 0)
+ {
+ const auto [isTimezoneSet, aTimezone] = SfxLokHelper::getDefaultTimezone();
+ comphelper::LibreOfficeKit::setTimezone(isTimezoneSet, aTimezone);
+ }
+ });
+
+ if (bInputMode)
+ {
+ double fVal = 0.0;
+ sal_uInt32 nFormat = 0;
+ switch (nReqFmt)
+ {
+ case SvNumFormatType::DATE:
+ {
+ Date aActDate( Date::SYSTEM );
+ fVal = aActDate - pFormatter->GetNullDate();
+ if (nCurNumFormatType == SvNumFormatType::DATE)
+ nFormat = nCurNumFormat;
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ tools::Time aActTime( tools::Time::SYSTEM );
+ fVal = aActTime.GetTimeInDays();
+ if (nCurNumFormatType == SvNumFormatType::TIME)
+ nFormat = nCurNumFormat;
+ }
+ break;
+ default:
+ SAL_WARN("sc.ui","unhandled current date/time request");
+ nReqFmt = SvNumFormatType::DATETIME;
+ [[fallthrough]];
+ case SvNumFormatType::DATETIME:
+ {
+ DateTime aActDateTime( DateTime::SYSTEM );
+ fVal = DateTime::Sub( aActDateTime, DateTime( pFormatter->GetNullDate()));
+ if (nCurNumFormatType == SvNumFormatType::DATETIME)
+ nFormat = nCurNumFormat;
+ }
+ break;
+ }
+
+ if (!nFormat)
+ {
+ const LanguageType nLang = (pCurNumFormatEntry ? pCurNumFormatEntry->GetLanguage() : ScGlobal::eLnge);
+ nFormat = pFormatter->GetStandardFormat( nReqFmt, nLang);
+ // This would return a more precise format with seconds and 100th
+ // seconds for a time request.
+ //nFormat = pFormatter->GetStandardFormat( fVal, nFormat, nReqFmt, nLang);
+ }
+ OUString aString;
+ const Color* pColor;
+ pFormatter->GetOutputString( fVal, nFormat, aString, &pColor);
+
+ pInputHdl->DataChanging();
+ EditView* pTopView = pInputHdl->GetTopView();
+ if (pTopView)
+ pTopView->InsertText( aString);
+ EditView* pTableView = pInputHdl->GetTableView();
+ if (pTableView)
+ pTableView->InsertText( aString);
+ pInputHdl->DataChanged();
+ }
+ else
+ {
+ // Clear "Enter pastes" mode.
+ rViewData.SetPasteMode( ScPasteFlags::NONE );
+ // Clear CopySourceOverlay in each window of a split/frozen tabview.
+ rViewData.GetViewShell()->UpdateCopySourceOverlay();
+
+ bool bForceReqFmt = false;
+ const double fCell = rDoc.GetValue( aCurPos);
+ // Combine requested date/time stamp with existing cell time/date, if any.
+ switch (nReqFmt)
+ {
+ case SvNumFormatType::DATE:
+ switch (nCurNumFormatType)
+ {
+ case SvNumFormatType::TIME:
+ // An empty cell formatted as time (or 00:00 time) shall
+ // not result in the current date with 00:00 time, but only
+ // in current date.
+ if (fCell != 0.0)
+ nReqFmt = SvNumFormatType::DATETIME;
+ break;
+ case SvNumFormatType::DATETIME:
+ {
+ // Force to only date if the existing date+time is the
+ // current date. This way inserting current date twice
+ // on an existing date+time cell can be used to force
+ // date, which otherwise would only be possible by
+ // applying a date format.
+ double fDate = rtl::math::approxFloor( fCell);
+ if (fDate == (Date( Date::SYSTEM) - pFormatter->GetNullDate()))
+ bForceReqFmt = true;
+ }
+ break;
+ default: break;
+ }
+ break;
+ case SvNumFormatType::TIME:
+ switch (nCurNumFormatType)
+ {
+ case SvNumFormatType::DATE:
+ // An empty cell formatted as date shall not result in the
+ // null date and current time, but only in current time.
+ if (fCell != 0.0)
+ nReqFmt = SvNumFormatType::DATETIME;
+ break;
+ case SvNumFormatType::DATETIME:
+ // Requesting current time on an empty date+time cell
+ // inserts both current date+time.
+ if (fCell == 0.0)
+ nReqFmt = SvNumFormatType::DATETIME;
+ else
+ {
+ // Add current time to an existing date+time where time is
+ // zero and date is current date, else force time only.
+ double fDate = rtl::math::approxFloor( fCell);
+ double fTime = fCell - fDate;
+ if (fTime == 0.0 && fDate == (Date( Date::SYSTEM) - pFormatter->GetNullDate()))
+ nReqFmt = SvNumFormatType::DATETIME;
+ else
+ bForceReqFmt = true;
+ }
+ break;
+ default: break;
+ }
+ break;
+ default:
+ SAL_WARN("sc.ui","unhandled current date/time request");
+ nReqFmt = SvNumFormatType::DATETIME;
+ [[fallthrough]];
+ case SvNumFormatType::DATETIME:
+ break;
+ }
+ double fVal = 0.0;
+ switch (nReqFmt)
+ {
+ case SvNumFormatType::DATE:
+ {
+ Date aActDate( Date::SYSTEM );
+ fVal = aActDate - pFormatter->GetNullDate();
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ tools::Time aActTime( tools::Time::SYSTEM );
+ fVal = aActTime.GetTimeInDays();
+ }
+ break;
+ case SvNumFormatType::DATETIME:
+ switch (nCurNumFormatType)
+ {
+ case SvNumFormatType::DATE:
+ {
+ double fDate = rtl::math::approxFloor( fCell);
+ tools::Time aActTime( tools::Time::SYSTEM );
+ fVal = fDate + aActTime.GetTimeInDays();
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ double fTime = fCell - rtl::math::approxFloor( fCell);
+ Date aActDate( Date::SYSTEM );
+ fVal = (aActDate - pFormatter->GetNullDate()) + fTime;
+ }
+ break;
+ default:
+ {
+ DateTime aActDateTime( DateTime::SYSTEM );
+ fVal = DateTime::Sub( aActDateTime, DateTime( pFormatter->GetNullDate()));
+ }
+ }
+ break;
+ default: break;
+
+ }
+
+ SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager();
+ pUndoMgr->EnterListAction(rUndoStr, rUndoStr, 0, rViewData.GetViewShell()->GetViewShellId());
+
+ pDocSh->GetDocFunc().SetValueCell(aCurPos, fVal, true);
+
+ // Set the new cell format only when it differs from the current cell
+ // format type. Preserve a date+time format unless we force a format
+ // through.
+ if (bForceReqFmt || (nReqFmt != nCurNumFormatType && nCurNumFormatType != SvNumFormatType::DATETIME))
+ SetNumberFormat(nReqFmt);
+ else
+ rViewData.UpdateInputHandler(); // update input bar with new value
+
+ pUndoMgr->LeaveListAction();
+ }
+}
+
+void ScViewFunc::ShowNote( bool bShow )
+{
+ if( bShow )
+ HideNoteMarker();
+ const ScViewData& rViewData = GetViewData();
+ ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
+ // show note moved to ScDocFunc, to be able to use it in notesuno.cxx
+ rViewData.GetDocShell()->GetDocFunc().ShowNote( aPos, bShow );
+}
+
+void ScViewFunc::EditNote()
+{
+ // for editing display and activate
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScAddress aPos( nCol, nRow, nTab );
+
+ // start drawing undo to catch undo action for insertion of the caption object
+ pDocSh->MakeDrawLayer();
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ pDrawLayer->BeginCalcUndo(true);
+ // generated undo action is processed in FuText::StopEditMode
+
+ // get existing note or create a new note (including caption drawing object)
+ ScPostIt* pNote = rDoc.GetOrCreateNote( aPos );
+ if(!pNote)
+ return;
+
+ // hide temporary note caption
+ HideNoteMarker();
+ // show caption object without changing internal visibility state
+ pNote->ShowCaptionTemp( aPos );
+
+ /* Drawing object has been created in ScDocument::GetOrCreateNote() or
+ in ScPostIt::ShowCaptionTemp(), so ScPostIt::GetCaption() should
+ return a caption object. */
+ SdrCaptionObj* pCaption = pNote->GetCaption();
+ if( !pCaption )
+ return;
+
+ if ( ScDrawView* pScDrawView = GetScDrawView() )
+ pScDrawView->SyncForGrid( pCaption );
+
+ // activate object (as in FuSelection::TestComment)
+ GetViewData().GetDispatcher().Execute( SID_DRAW_NOTEEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
+ // now get the created FuText and set into EditMode
+ FuText* pFuText = dynamic_cast<FuText*>(GetDrawFuncPtr());
+ if (pFuText)
+ {
+ ScrollToObject( pCaption ); // make object fully visible
+ pFuText->SetInEditMode( pCaption );
+
+ ScTabView::OnLOKNoteStateChanged( pNote );
+ }
+ collectUIInformation("OPEN");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfun7.cxx b/sc/source/ui/view/viewfun7.cxx
new file mode 100644
index 0000000000..f704256756
--- /dev/null
+++ b/sc/source/ui/view/viewfun7.cxx
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svtools/embedhlp.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/ipclient.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <osl/diagnose.h>
+
+#include <document.hxx>
+#include <viewfunc.hxx>
+#include <tabvwsh.hxx>
+#include <drawview.hxx>
+#include <scmod.hxx>
+#include <drwlayer.hxx>
+#include <drwtrans.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <dragdata.hxx>
+#include <gridwin.hxx>
+#include <stlpool.hxx>
+
+bool bPasteIsMove = false;
+
+using namespace com::sun::star;
+
+static void lcl_AdjustInsertPos( ScViewData& rData, Point& rPos, const Size& rSize )
+{
+ SdrPage* pPage = rData.GetScDrawView()->GetModel().GetPage( static_cast<sal_uInt16>(rData.GetTabNo()) );
+ OSL_ENSURE(pPage,"pPage ???");
+ Size aPgSize( pPage->GetSize() );
+ if (aPgSize.Width() < 0)
+ aPgSize.setWidth( -aPgSize.Width() );
+ tools::Long x = aPgSize.Width() - rPos.X() - rSize.Width();
+ tools::Long y = aPgSize.Height() - rPos.Y() - rSize.Height();
+ // if necessary: adjustments (80/200) for pixel approx. errors
+ if( x < 0 )
+ rPos.AdjustX(x + 80 );
+ if( y < 0 )
+ rPos.AdjustY(y + 200 );
+ rPos.AdjustX(rSize.Width() / 2 ); // position at paste is center
+ rPos.AdjustY(rSize.Height() / 2 );
+}
+
+void ScViewFunc::PasteDraw( const Point& rLogicPos, SdrModel* pModel,
+ bool bGroup, std::u16string_view rSrcShellID, std::u16string_view rDestShellID )
+{
+ bool bSameDocClipboard = rSrcShellID == rDestShellID;
+
+ MakeDrawLayer();
+ Point aPos( rLogicPos );
+
+ // MapMode at Outliner-RefDevice has to be right (as in FuText::MakeOutliner)
+ //! merge with FuText::MakeOutliner?
+ MapMode aOldMapMode;
+ OutputDevice* pRef = GetViewData().GetDocument().GetDrawLayer()->GetRefDevice();
+ if (pRef)
+ {
+ aOldMapMode = pRef->GetMapMode();
+ pRef->SetMapMode( MapMode(MapUnit::Map100thMM) );
+ }
+
+ bool bNegativePage = GetViewData().GetDocument().IsNegativePage( GetViewData().GetTabNo() );
+
+ SdrView* pDragEditView = nullptr;
+ ScModule* pScMod = SC_MOD();
+ const ScDragData& rData = pScMod->GetDragData();
+ ScDrawTransferObj* pDrawTrans = rData.pDrawTransfer;
+ if (pDrawTrans)
+ {
+ pDragEditView = pDrawTrans->GetDragSourceView();
+
+ aPos -= aDragStartDiff;
+ if ( bNegativePage )
+ {
+ if (aPos.X() > 0) aPos.setX( 0 );
+ }
+ else
+ {
+ if (aPos.X() < 0) aPos.setX( 0 );
+ }
+ if (aPos.Y() < 0) aPos.setY( 0 );
+ }
+
+ ScDrawView* pScDrawView = GetScDrawView();
+ if (bGroup)
+ pScDrawView->BegUndo( ScResId( STR_UNDO_PASTE ) );
+
+ bool bSameDoc = ( pDragEditView && &pDragEditView->GetModel() == &pScDrawView->GetModel() );
+ if (bSameDoc)
+ {
+ // copy locally - incl. charts
+
+ Point aSourceStart = pDragEditView->GetAllMarkedRect().TopLeft();
+ tools::Long nDiffX = aPos.X() - aSourceStart.X();
+ tools::Long nDiffY = aPos.Y() - aSourceStart.Y();
+
+ // move within a page?
+
+ if ( bPasteIsMove &&
+ pScDrawView->GetSdrPageView()->GetPage() ==
+ pDragEditView->GetSdrPageView()->GetPage() )
+ {
+ if ( nDiffX != 0 || nDiffY != 0 )
+ pDragEditView->MoveAllMarked(Size(nDiffX,nDiffY));
+ }
+ else
+ {
+ SdrModel& rDrawModel = pDragEditView->GetModel();
+ SCTAB nTab = GetViewData().GetTabNo();
+ SdrPage* pDestPage = rDrawModel.GetPage( static_cast< sal_uInt16 >( nTab ) );
+ OSL_ENSURE(pDestPage,"who is this, Page?");
+
+ ::std::vector< OUString > aExcludedChartNames;
+ if ( pDestPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pDestPage );
+ }
+
+ SdrMarkList aMark = pDragEditView->GetMarkedObjectList();
+ aMark.ForceSort();
+ const size_t nMarkCnt=aMark.GetMarkCount();
+ for (size_t nm=0; nm<nMarkCnt; ++nm) {
+ const SdrMark* pM=aMark.GetMark(nm);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+
+ // Directly Clone to target SdrModel
+ rtl::Reference<SdrObject> pNewObj(pObj->CloneSdrObject(rDrawModel));
+
+ if (pNewObj!=nullptr)
+ {
+ // copy graphics within the same model - always needs new name
+ if ( dynamic_cast<const SdrGrafObj*>( pNewObj.get()) != nullptr && !bPasteIsMove )
+ pNewObj->SetName(static_cast<ScDrawLayer*>(&rDrawModel)->GetNewGraphicName());
+
+ if (nDiffX!=0 || nDiffY!=0)
+ pNewObj->NbcMove(Size(nDiffX,nDiffY));
+ if (pDestPage)
+ pDestPage->InsertObject( pNewObj.get() );
+ pScDrawView->AddUndo(std::make_unique<SdrUndoInsertObj>( *pNewObj ));
+
+ if (ScDrawLayer::IsCellAnchored(*pNewObj))
+ ScDrawLayer::SetCellAnchoredFromPosition(*pNewObj, GetViewData().GetDocument(), nTab,
+ ScDrawLayer::IsResizeWithCell(*pNewObj));
+ }
+ }
+
+ if (bPasteIsMove)
+ pDragEditView->DeleteMarked();
+
+ ScDocument& rDocument = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScModelObj* pModelObj = ( pDocShell ? pDocShell->GetModel() : nullptr );
+ if ( pDestPage && pModelObj && pDrawTrans )
+ {
+ const ScRangeListVector& rProtectedChartRangesVector( pDrawTrans->GetProtectedChartRangesVector() );
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDocument, pDestPage, pModelObj, nTab,
+ rProtectedChartRangesVector, aExcludedChartNames, bSameDoc );
+ }
+ }
+ }
+ else
+ {
+ bPasteIsMove = false; // no internal move happened
+ SdrView aView(*pModel); // #i71529# never create a base class of SdrView directly!
+ SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel().GetPage(0));
+ aView.MarkAllObj(pPv);
+ Size aSize = aView.GetAllMarkedRect().GetSize();
+ lcl_AdjustInsertPos( GetViewData(), aPos, aSize );
+
+ // don't change marking if OLE object is active
+ // (at Drop from OLE object it would be deactivated in the middle of ExecuteDrag!)
+
+ SdrInsertFlags nOptions = SdrInsertFlags::NONE;
+ SfxInPlaceClient* pClient = GetViewData().GetViewShell()->GetIPClient();
+ if ( pClient && pClient->IsObjectInPlaceActive() )
+ nOptions |= SdrInsertFlags::DONTMARK;
+
+ ::std::vector< OUString > aExcludedChartNames;
+ SCTAB nTab = GetViewData().GetTabNo();
+ SdrPage* pPage = pScDrawView->GetModel().GetPage( static_cast< sal_uInt16 >( nTab ) );
+ OSL_ENSURE( pPage, "Page?" );
+ if ( pPage )
+ {
+ ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
+ }
+
+ if ( !bSameDocClipboard )
+ {
+ auto pPool = static_cast<ScStyleSheetPool*>(pScDrawView->GetModel().GetStyleSheetPool());
+ pPool->CopyUsedGraphicStylesFrom(pModel->GetStyleSheetPool());
+
+ // #89247# Set flag for ScDocument::UpdateChartListeners() which is
+ // called during paste.
+ GetViewData().GetDocument().SetPastingDrawFromOtherDoc( true );
+ }
+
+ pScDrawView->Paste(*pModel, aPos, nullptr, nOptions);
+
+ if ( !bSameDocClipboard )
+ GetViewData().GetDocument().SetPastingDrawFromOtherDoc( false );
+
+ // Paste puts all objects on the active (front) layer
+ // controls must be on SC_LAYER_CONTROLS
+ if (pPage)
+ {
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ if ( dynamic_cast<const SdrUnoObj*>( pObject) != nullptr && pObject->GetLayer() != SC_LAYER_CONTROLS )
+ pObject->NbcSetLayer(SC_LAYER_CONTROLS);
+
+ if (ScDrawLayer::IsCellAnchored(*pObject))
+ ScDrawLayer::SetCellAnchoredFromPosition(*pObject, GetViewData().GetDocument(), nTab,
+ ScDrawLayer::IsResizeWithCell(*pObject));
+
+ pObject = aIter.Next();
+ }
+ }
+
+ // all graphics objects must have names
+ GetViewData().GetDocument().EnsureGraphicNames();
+
+ ScDocument& rDocument = GetViewData().GetDocument();
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScModelObj* pModelObj = ( pDocShell ? pDocShell->GetModel() : nullptr );
+ const ScDrawTransferObj* pTransferObj = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(GetViewData().GetActiveWin()));
+ if ( pPage && pModelObj && ( pTransferObj || pDrawTrans ) )
+ {
+ const ScRangeListVector& rProtectedChartRangesVector(
+ pTransferObj ? pTransferObj->GetProtectedChartRangesVector() : pDrawTrans->GetProtectedChartRangesVector() );
+ ScChartHelper::CreateProtectedChartListenersAndNotify( rDocument, pPage, pModelObj, nTab,
+ rProtectedChartRangesVector, aExcludedChartNames, bSameDocClipboard );
+ }
+ }
+
+ if (bGroup)
+ {
+ pScDrawView->GroupMarked();
+ pScDrawView->EndUndo();
+ }
+
+ if (pRef)
+ pRef->SetMapMode( aOldMapMode );
+
+ // GetViewData().GetViewShell()->SetDrawShell( true );
+ // It is not sufficient to just set the DrawShell if we pasted, for
+ // example, a chart. SetDrawShellOrSub() would only work for D&D in the
+ // same document but not if inserting from the clipboard, therefore
+ // MarkListHasChanged() is what we need.
+ pScDrawView->MarkListHasChanged();
+
+}
+
+bool ScViewFunc::PasteObject( const Point& rPos, const uno::Reference < embed::XEmbeddedObject >& xObj,
+ const Size* pDescSize, const Graphic* pReplGraph, const OUString& aMediaType, sal_Int64 nAspect )
+{
+ MakeDrawLayer();
+ if ( xObj.is() )
+ {
+ OUString aName;
+ //TODO/MBA: is that OK?
+ comphelper::EmbeddedObjectContainer& aCnt = GetViewData().GetViewShell()->GetObjectShell()->GetEmbeddedObjectContainer();
+ if ( !aCnt.HasEmbeddedObject( xObj ) )
+ aCnt.InsertEmbeddedObject( xObj, aName );
+ else
+ aName = aCnt.GetEmbeddedObjectName( xObj );
+
+ svt::EmbeddedObjectRef aObjRef( xObj, nAspect );
+ if ( pReplGraph )
+ aObjRef.SetGraphic( *pReplGraph, aMediaType );
+
+ Size aSize;
+ if ( nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ MapMode aMapMode( MapUnit::Map100thMM );
+ aSize = aObjRef.GetSize( &aMapMode );
+ }
+ else
+ {
+ // working with visual area can switch object to running state
+ MapUnit aMapObj = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
+ MapUnit aMap100 = MapUnit::Map100thMM;
+
+ if ( pDescSize && pDescSize->Width() && pDescSize->Height() )
+ {
+ // use size from object descriptor if given
+ aSize = OutputDevice::LogicToLogic(*pDescSize, MapMode(aMap100), MapMode(aMapObj));
+ awt::Size aSz;
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xObj->setVisualAreaSize( nAspect, aSz );
+ }
+
+ awt::Size aSz;
+ try
+ {
+ aSz = xObj->getVisualAreaSize( nAspect );
+ }
+ catch ( embed::NoVisualAreaSizeException& )
+ {
+ // the default size will be set later
+ }
+
+ aSize = Size( aSz.Width, aSz.Height );
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapObj), MapMode(aMap100)); // for SdrOle2Obj
+
+ if( aSize.IsEmpty() )
+ {
+ OSL_FAIL("SvObjectDescriptor::GetSize == 0");
+ aSize.setWidth( 5000 );
+ aSize.setHeight( 5000 );
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMap100), MapMode(aMapObj));
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xObj->setVisualAreaSize( nAspect, aSz );
+ }
+ }
+
+ // don't call AdjustInsertPos
+ Point aInsPos = rPos;
+ if ( GetViewData().GetDocument().IsNegativePage( GetViewData().GetTabNo() ) )
+ aInsPos.AdjustX( -(aSize.Width()) );
+ tools::Rectangle aRect( aInsPos, aSize );
+
+ ScDrawView* pDrView = GetScDrawView();
+ rtl::Reference<SdrOle2Obj> pSdrObj = new SdrOle2Obj(
+ pDrView->getSdrModelFromSdrView(),
+ aObjRef,
+ aName,
+ aRect);
+
+ SdrPageView* pPV = pDrView->GetSdrPageView();
+ pDrView->InsertObjectSafe( pSdrObj.get(), *pPV ); // don't mark if OLE
+ GetViewData().GetViewShell()->SetDrawShell( true );
+ return true;
+ }
+ else
+ return false;
+}
+
+bool ScViewFunc::PasteBitmapEx( const Point& rPos, const BitmapEx& rBmpEx )
+{
+ Graphic aGraphic(rBmpEx);
+ return PasteGraphic( rPos, aGraphic, "" );
+}
+
+bool ScViewFunc::PasteMetaFile( const Point& rPos, const GDIMetaFile& rMtf )
+{
+ Graphic aGraphic(rMtf);
+ return PasteGraphic( rPos, aGraphic, "" );
+}
+
+bool ScViewFunc::PasteGraphic( const Point& rPos, const Graphic& rGraphic,
+ const OUString& rFile )
+{
+ MakeDrawLayer();
+ ScDrawView* pScDrawView = GetScDrawView();
+
+ if (!pScDrawView)
+ return false;
+
+ // #i123922# check if the drop was over an existing object; if yes, evtl. replace
+ // the graphic for a SdrGraphObj (including link state updates) or adapt the fill
+ // style for other objects
+ SdrPageView* pPageView = pScDrawView->GetSdrPageView();
+ if (pPageView)
+ {
+ SdrObject* pPickObj = pScDrawView->PickObj(rPos, pScDrawView->getHitTolLog(), pPageView);
+ if (pPickObj)
+ {
+ const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP));
+ SdrObject* pResult = pScDrawView->ApplyGraphicToObject(
+ *pPickObj,
+ rGraphic,
+ aBeginUndo,
+ rFile);
+
+ if (pResult)
+ {
+ // we are done; mark the modified/new object
+ pScDrawView->MarkObj(pResult, pScDrawView->GetSdrPageView());
+ return true;
+ }
+ }
+ }
+
+ Point aPos( rPos );
+ vcl::Window* pWin = GetActiveWin();
+ MapMode aSourceMap = rGraphic.GetPrefMapMode();
+ MapMode aDestMap( MapUnit::Map100thMM );
+
+ if (aSourceMap.GetMapUnit() == MapUnit::MapPixel)
+ {
+ // consider pixel correction, so bitmap fits to screen
+ Fraction aScaleX, aScaleY;
+ pScDrawView->CalcNormScale( aScaleX, aScaleY );
+ aDestMap.SetScaleX(aScaleX);
+ aDestMap.SetScaleY(aScaleY);
+ }
+
+ Size aSize = pWin->LogicToLogic( rGraphic.GetPrefSize(), &aSourceMap, &aDestMap );
+
+ if ( GetViewData().GetDocument().IsNegativePage( GetViewData().GetTabNo() ) )
+ aPos.AdjustX( -(aSize.Width()) );
+
+ GetViewData().GetViewShell()->SetDrawShell( true );
+ tools::Rectangle aRect(aPos, aSize);
+ rtl::Reference<SdrGrafObj> pGrafObj = new SdrGrafObj(
+ pScDrawView->getSdrModelFromSdrView(),
+ rGraphic,
+ aRect);
+
+ // path was the name of the graphic in history
+
+ ScDrawLayer* pLayer = static_cast<ScDrawLayer*>(&pScDrawView->GetModel());
+ OUString aName = pLayer->GetNewGraphicName(); // "Graphics"
+ pGrafObj->SetName(aName);
+
+ // don't mark if OLE
+ bool bSuccess = pScDrawView->InsertObjectSafe(pGrafObj.get(), *pScDrawView->GetSdrPageView());
+
+ // SetGraphicLink has to be used after inserting the object,
+ // otherwise an empty graphic is swapped in and the contact stuff crashes.
+ // See #i37444#.
+ if (bSuccess && !rFile.isEmpty())
+ pGrafObj->SetGraphicLink( rFile );
+
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
new file mode 100644
index 0000000000..25633cae38
--- /dev/null
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -0,0 +1,3183 @@
+/* -*- 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 <scitems.hxx>
+
+#include <sfx2/app.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/bindings.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/virdev.hxx>
+#include <stdlib.h>
+#include <unotools/charclass.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <osl/diagnose.h>
+#include <formula/paramclass.hxx>
+
+#include <viewfunc.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <sc.hrc>
+#include <undocell.hxx>
+#include <undoblk.hxx>
+#include <refundo.hxx>
+#include <olinetab.hxx>
+#include <rangenam.hxx>
+#include <globstr.hrc>
+#include <global.hxx>
+#include <stlsheet.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <scresid.hxx>
+#include <inputhdl.hxx>
+#include <scmod.hxx>
+#include <inputopt.hxx>
+#include <compiler.hxx>
+#include <docfunc.hxx>
+#include <appoptio.hxx>
+#include <sizedev.hxx>
+#include <editable.hxx>
+#include <scui_def.hxx>
+#include <funcdesc.hxx>
+#include <docuno.hxx>
+#include <cellsuno.hxx>
+#include <tokenarray.hxx>
+#include <rowheightcontext.hxx>
+#include <comphelper/lok.hxx>
+#include <conditio.hxx>
+#include <columnspanset.hxx>
+#include <stringutil.hxx>
+#include <SparklineList.hxx>
+
+#include <memory>
+
+static void ShowFilteredRows(ScDocument& rDoc, SCTAB nTab, SCCOLROW nStartNo, SCCOLROW nEndNo,
+ bool bShow)
+{
+ SCROW nFirstRow = nStartNo;
+ SCROW nLastRow = nStartNo;
+ do
+ {
+ if (!rDoc.RowFiltered(nFirstRow, nTab, nullptr, &nLastRow))
+ rDoc.ShowRows(nFirstRow, nLastRow < nEndNo ? nLastRow : nEndNo, nTab, bShow);
+ nFirstRow = nLastRow + 1;
+ } while (nFirstRow <= nEndNo);
+}
+
+static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell *pDocSh )
+{
+ if( pCondFmt )
+ {
+ const ScRangeList& rRanges = pCondFmt->GetRange();
+
+ pDocSh->PostPaint( rRanges, PaintPartFlags::All );
+ }
+}
+
+static void lcl_PostRepaintSparkLine(sc::SparklineList* pSparklineList, const ScRange& rRange,
+ ScDocShell* pDocSh)
+{
+ if (pSparklineList)
+ {
+ for (auto& rSparkLineGroup : pSparklineList->getSparklineGroups())
+ {
+ for (auto& rSparkline : pSparklineList->getSparklinesFor(rSparkLineGroup))
+ {
+ if (rSparkline->getInputRange().Contains(rRange))
+ {
+ pDocSh->PostPaint(
+ ScRange(rSparkline->getColumn(), rSparkline->getRow(), rRange.aStart.Tab()),
+ PaintPartFlags::All, SC_PF_TESTMERGE);
+ }
+ }
+ }
+ }
+}
+
+ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
+ ScTabView( pParent, rDocSh, pViewShell ),
+ bFormatValid( false )
+{
+}
+
+ScViewFunc::~ScViewFunc()
+{
+}
+
+namespace {
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+void ScViewFunc::StartFormatArea()
+{
+ // anything to do?
+ if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
+ return;
+
+ // start only with single cell (marked or cursor position)
+ ScRange aMarkRange;
+ bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
+ if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
+ bOk = false;
+
+ if (bOk)
+ {
+ bFormatValid = true;
+ aFormatSource = aMarkRange.aStart;
+ aFormatArea = ScRange( aFormatSource );
+ }
+ else
+ bFormatValid = false; // discard old range
+}
+
+bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
+{
+ // anything to do?
+ if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
+ return false;
+
+ // Test: treat input with numberformat (bAttrChanged) always as new Attribute
+ // (discard old Area ). If not wanted, discard if-statement
+ if ( bAttrChanged )
+ {
+ StartFormatArea();
+ return false;
+ }
+
+ //! Test if cell empty ???
+
+ bool bFound = false;
+ ScRange aNewRange = aFormatArea;
+ if ( bFormatValid && nTab == aFormatSource.Tab() )
+ {
+ if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
+ {
+ // within range?
+ if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
+ {
+ bFound = true; // do not change range
+ }
+ // left ?
+ if ( nCol+1 == aFormatArea.aStart.Col() )
+ {
+ bFound = true;
+ aNewRange.aStart.SetCol( nCol );
+ }
+ // right ?
+ if ( nCol == aFormatArea.aEnd.Col()+1 )
+ {
+ bFound = true;
+ aNewRange.aEnd.SetCol( nCol );
+ }
+ }
+ if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
+ {
+ // top ?
+ if ( nRow+1 == aFormatArea.aStart.Row() )
+ {
+ bFound = true;
+ aNewRange.aStart.SetRow( nRow );
+ }
+ // bottom ?
+ if ( nRow == aFormatArea.aEnd.Row()+1 )
+ {
+ bFound = true;
+ aNewRange.aEnd.SetRow( nRow );
+ }
+ }
+ }
+
+ if (bFound)
+ aFormatArea = aNewRange; // extend
+ else
+ bFormatValid = false; // outside of range -> break
+
+ return bFound;
+}
+
+void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ bool bAttrChanged )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ const ScPatternAttr* pSource = rDoc.GetPattern(
+ aFormatSource.Col(), aFormatSource.Row(), nTab );
+ if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
+ {
+ ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SetMarkArea( aRange );
+
+ ScDocFunc &rFunc = GetViewData().GetDocFunc();
+
+ // pOldPattern is only valid until call to ApplyAttributes!
+ const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
+ if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
+ rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );
+
+ rFunc.ApplyAttributes( aMark, *pSource, false );
+ }
+
+ if ( bAttrChanged ) // value entered with number format?
+ aFormatSource.Set( nCol, nRow, nTab ); // then set a new source
+}
+
+// additional routines
+
+sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
+ nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
+ return nTwips;
+}
+
+bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
+{
+ bool bRet;
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ if (rMark.IsMarked() || rMark.IsMultiMarked())
+ bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
+ else
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
+ pOnlyNotBecauseOfMatrix );
+ }
+ return bRet;
+}
+
+static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
+{
+ const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
+ if ( pFuncList )
+ {
+ sal_uLong nCount = pFuncList->GetCount();
+ for (sal_uLong i=0; i<nCount; i++)
+ if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
+ return true;
+ }
+ return false;
+}
+
+static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
+{
+ sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
+ sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
+ sal_uInt16 nPos;
+ for (nPos=0; nPos<nOldCount; nPos++)
+ if (pOldList[nPos] == nOpCode) // is the function already in the list?
+ {
+ if ( nPos == 0 )
+ return false; // already at the top -> no change
+
+ // count doesn't change, so the original array is modified
+
+ for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
+ pOldList[nCopy] = pOldList[nCopy-1];
+ pOldList[0] = nOpCode;
+
+ return true; // list has changed
+ }
+
+ if ( !lcl_FunctionKnown( nOpCode ) )
+ return false; // not in function list -> no change
+
+ sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
+ sal_uInt16 nNewList[LRU_MAX];
+ nNewList[0] = nOpCode;
+ for (nPos=1; nPos<nNewCount; nPos++)
+ nNewList[nPos] = pOldList[nPos-1];
+ rAppOpt.SetLRUFuncList( nNewList, nNewCount );
+
+ return true; // list has changed
+}
+
+namespace HelperNotifyChanges
+{
+ static void NotifyIfChangesListeners(const ScDocShell &rDocShell, ScMarkData& rMark,
+ SCCOL nCol, SCROW nRow, const OUString& rType = "cell-change")
+ {
+ ScModelObj* pModelObj = rDocShell.GetModel();
+
+ ScRangeList aChangeRanges;
+ for (const auto& rTab : rMark)
+ aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );
+
+ if (getMustPropagateChangesModel(pModelObj))
+ Notify(*pModelObj, aChangeRanges, rType);
+ else
+ {
+ Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
+ ? OUString("data-area-invalidate") : OUString("data-area-extend"));
+ }
+ }
+}
+
+namespace
+{
+ class AutoCorrectQuery : public weld::MessageDialogController
+ {
+ private:
+ std::unique_ptr<weld::TextView> m_xError;
+ public:
+ AutoCorrectQuery(weld::Window* pParent, const OUString& rFormula)
+ : weld::MessageDialogController(pParent, "modules/scalc/ui/warnautocorrect.ui", "WarnAutoCorrect", "grid")
+ , m_xError(m_xBuilder->weld_text_view("error"))
+ {
+ m_xDialog->set_default_response(RET_YES);
+
+ const int nMaxWidth = m_xError->get_approximate_digit_width() * 65;
+ const int nMaxHeight = m_xError->get_height_rows(6);
+ m_xError->set_size_request(nMaxWidth, nMaxHeight);
+
+ m_xError->set_text(rFormula);
+ }
+ };
+}
+
+// actual functions
+
+// input - undo OK
+void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const OUString& rString,
+ const EditTextObject* pData,
+ bool bMatrixExpand )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData rMark(GetViewData().GetMarkData());
+ bool bRecord = rDoc.IsUndoEnabled();
+ SCTAB i;
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocFunc &rFunc = GetViewData().GetDocFunc();
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, rMark );
+ if (!aTester.IsEditable())
+ {
+ ErrorMessage(aTester.GetMessageId());
+ PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
+ return;
+ }
+
+ if ( bRecord )
+ rFunc.EnterListAction( STR_UNDO_ENTERDATA );
+
+ bool bFormula = false;
+
+ // do not check formula if it is a text cell
+ sal_uInt32 format = rDoc.GetNumberFormat( nCol, nRow, nTab );
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ // a single '=' character is handled as string (needed for special filters)
+ if ( pFormatter->GetType(format) != SvNumFormatType::TEXT && rString.getLength() > 1 )
+ {
+ if ( rString[0] == '=' )
+ {
+ // handle as formula
+ bFormula = true;
+ }
+ else if ( rString[0] == '+' || rString[0] == '-' )
+ {
+ // if there is more than one leading '+' or '-' character, remove the additional ones
+ sal_Int32 nIndex = 1;
+ sal_Int32 nLen = rString.getLength();
+ while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
+ {
+ ++nIndex;
+ }
+ OUString aString = rString.replaceAt( 1, nIndex - 1, u"" );
+
+ // if the remaining part without the leading '+' or '-' character
+ // is non-empty and not a number, handle as formula
+ if ( aString.getLength() > 1 )
+ {
+ double fNumber = 0;
+ if ( !pFormatter->IsNumberFormat( aString, format, fNumber ) )
+ {
+ bFormula = true;
+ }
+ }
+ }
+ }
+
+ bool bNumFmtChanged = false;
+ if ( bFormula )
+ { // formula, compile with autoCorrection
+ i = rMark.GetFirstSelected();
+ ScAddress aPos( nCol, nRow, i );
+ ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar(), true, false );
+//2do: enable/disable autoCorrection via calcoptions
+ aComp.SetAutoCorrection( true );
+ if ( rString[0] == '+' || rString[0] == '-' )
+ {
+ aComp.SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK );
+ }
+ OUString aFormula( rString );
+ std::unique_ptr< ScTokenArray > pArr;
+ bool bAgain;
+ do
+ {
+ bAgain = false;
+ bool bAddEqual = false;
+ pArr = aComp.CompileString( aFormula );
+ bool bCorrected = aComp.IsCorrected();
+ std::unique_ptr< ScTokenArray > pArrFirst;
+ if ( bCorrected )
+ { // try to parse with first parser-correction
+ pArrFirst = std::move( pArr );
+ pArr = aComp.CompileString( aComp.GetCorrectedFormula() );
+ }
+ if ( pArr->GetCodeError() == FormulaError::NONE )
+ {
+ bAddEqual = true;
+ aComp.CompileTokenArray();
+ bCorrected |= aComp.IsCorrected();
+ }
+ if ( bCorrected )
+ {
+ OUString aCorrectedFormula;
+ if ( bAddEqual )
+ {
+ aCorrectedFormula = "=" + aComp.GetCorrectedFormula();
+ }
+ else
+ aCorrectedFormula = aComp.GetCorrectedFormula();
+ short nResult;
+ if ( aCorrectedFormula.getLength() == 1 )
+ nResult = RET_NO; // empty formula, just '='
+ else
+ {
+ AutoCorrectQuery aQueryBox(GetViewData().GetDialogParent(), aCorrectedFormula);
+ nResult = aQueryBox.run();
+ }
+ if ( nResult == RET_YES )
+ {
+ aFormula = aCorrectedFormula;
+ bAgain = true;
+ }
+ else
+ {
+ if ( pArrFirst )
+ pArr = std::move( pArrFirst );
+ }
+ }
+ } while ( bAgain );
+ // to be used in multiple tabs, the formula must be compiled anew
+ // via ScFormulaCell copy-ctor because of RangeNames,
+ // the same code-array for all cells is not possible.
+ // If the array has an error, (it) must be RPN-erased in the newly generated
+ // cells and the error be set explicitly, so that
+ // via FormulaCell copy-ctor and Interpreter it will be, when possible,
+ // ironed out again, too intelligent... e.g.: =1))
+ FormulaError nError = pArr->GetCodeError();
+ if ( nError == FormulaError::NONE )
+ {
+ // update list of recent functions with all functions that
+ // are not within parentheses
+
+ ScModule* pScMod = SC_MOD();
+ ScAppOptions aAppOpt = pScMod->GetAppOptions();
+ bool bOptChanged = false;
+
+ formula::FormulaToken** ppToken = pArr->GetArray();
+ sal_uInt16 nTokens = pArr->GetLen();
+ sal_uInt16 nLevel = 0;
+ for (sal_uInt16 nTP=0; nTP<nTokens; nTP++)
+ {
+ formula::FormulaToken* pTok = ppToken[nTP];
+ OpCode eOp = pTok->GetOpCode();
+ if ( eOp == ocOpen )
+ ++nLevel;
+ else if ( eOp == ocClose && nLevel )
+ --nLevel;
+ if ( nLevel == 0 && pTok->IsFunction() &&
+ lcl_AddFunction( aAppOpt, sal::static_int_cast<sal_uInt16>( eOp ) ) )
+ bOptChanged = true;
+ }
+
+ if ( bOptChanged )
+ {
+ pScMod->SetAppOptions(aAppOpt);
+ }
+
+ if (bMatrixExpand)
+ {
+ // If the outer function/operator returns an array/matrix then
+ // enter a matrix formula. ScViewFunc::EnterMatrix() takes care
+ // of selection/mark of the result dimensions or preselected
+ // mark. If the user wanted less or a single cell then should
+ // mark such prior to entering the formula.
+ const formula::FormulaToken* pToken = pArr->LastRPNToken();
+ if (pToken && (formula::FormulaCompiler::IsMatrixFunction( pToken->GetOpCode())
+ || pToken->IsInForceArray()))
+ {
+ // Discard this (still empty here) Undo action,
+ // EnterMatrix() will create its own.
+ if (bRecord)
+ rFunc.EndListAction();
+
+ // Use corrected formula string.
+ EnterMatrix( aFormula, rDoc.GetGrammar());
+
+ return;
+ }
+ }
+ }
+
+ ScFormulaCell aCell(rDoc, aPos, std::move( pArr ), formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::NONE);
+
+ for (const auto& rTab : rMark)
+ {
+ i = rTab;
+ aPos.SetTab( i );
+ const sal_uInt32 nIndex = rDoc.GetAttr(
+ nCol, nRow, i, ATTR_VALUE_FORMAT )->GetValue();
+ const SvNumFormatType nType = pFormatter->GetType( nIndex);
+ if (nType == SvNumFormatType::TEXT ||
+ ((rString[0] == '+' || rString[0] == '-') && nError != FormulaError::NONE && rString == aFormula))
+ {
+ if ( pData )
+ {
+ // A clone of pData will be stored in the cell.
+ rFunc.SetEditCell(aPos, *pData, true);
+ }
+ else
+ rFunc.SetStringCell(aPos, aFormula, true);
+ }
+ else
+ {
+ ScFormulaCell* pCell = new ScFormulaCell( aCell, rDoc, aPos );
+ if ( nError != FormulaError::NONE )
+ {
+ pCell->GetCode()->DelRPN();
+ pCell->SetErrCode( nError );
+ if(pCell->GetCode()->IsHyperLink())
+ pCell->GetCode()->SetHyperLink(false);
+ }
+ if (nType == SvNumFormatType::LOGICAL)
+ {
+ // Reset to General so the actual format can be determined
+ // after the cell has been interpreted. A sticky boolean
+ // number format is highly likely unwanted... see tdf#75650.
+ // General of same locale as current number format.
+ const SvNumberformat* pEntry = pFormatter->GetEntry( nIndex);
+ const LanguageType nLang = (pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge);
+ const sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, nLang);
+ ScPatternAttr aPattern( rDoc.GetPool());
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat));
+ ScMarkData aMark(rDoc.GetSheetLimits());
+ aMark.SelectTable( i, true);
+ aMark.SetMarkArea( ScRange( aPos));
+ rFunc.ApplyAttributes( aMark, aPattern, false);
+ bNumFmtChanged = true;
+ }
+ rFunc.SetFormulaCell(aPos, pCell, true);
+ }
+ }
+ }
+ else
+ {
+ ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
+ for (const auto& rTab : rMark)
+ {
+ bool bNumFmtSet = false;
+ const ScAddress aScAddress(nCol, nRow, rTab);
+
+ // tdf#104902 - handle embedded newline
+ if (ScStringUtil::isMultiline(rString))
+ {
+ rEngine.SetTextCurrentDefaults(rString);
+ rDoc.SetEditText(aScAddress, rEngine.CreateTextObject());
+ pDocSh->AdjustRowHeight(nRow, nRow, rTab);
+ }
+ else
+ {
+ rFunc.SetNormalString(bNumFmtSet, aScAddress, rString, false);
+ }
+
+ if (bNumFmtSet)
+ {
+ /* FIXME: if set on any sheet results in changed only on
+ * sheet nTab for TestFormatArea() and DoAutoAttributes() */
+ bNumFmtChanged = true;
+ }
+ }
+ }
+
+ bool bAutoFormat = TestFormatArea(nCol, nRow, nTab, bNumFmtChanged);
+
+ if (bAutoFormat)
+ DoAutoAttributes(nCol, nRow, nTab, bNumFmtChanged);
+
+ pDocSh->UpdateOle(GetViewData());
+
+ const OUString aType(rString.isEmpty() ? u"delete-content" : u"cell-change");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
+
+ if ( bRecord )
+ rFunc.EndListAction();
+
+ aModificator.SetDocumentModified();
+ lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
+ lcl_PostRepaintSparkLine(rDoc.GetSparklineList(nTab), ScRange(nCol, nRow, nTab), pDocSh);
+}
+
+// enter value in single cell (on nTab only)
+
+void ScViewFunc::EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+
+ if (!pDocSh)
+ return;
+
+ bool bUndo(rDoc.IsUndoEnabled());
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
+ if (aTester.IsEditable())
+ {
+ ScAddress aPos( nCol, nRow, nTab );
+ ScCellValue aUndoCell;
+ if (bUndo)
+ aUndoCell.assign(rDoc, aPos);
+
+ rDoc.SetValue( nCol, nRow, nTab, rValue );
+
+ // because of ChangeTrack after change in document
+ if (bUndo)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterValue>(pDocSh, aPos, aUndoCell, rValue));
+ }
+
+ pDocSh->PostPaintCell( aPos );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ }
+ else
+ ErrorMessage(aTester.GetMessageId());
+}
+
+void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const EditTextObject& rData, bool bTestSimple )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ bool bRecord = rDoc.IsUndoEnabled();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
+ if (aTester.IsEditable())
+ {
+
+ // test for attribute
+
+ bool bSimple = false;
+ bool bCommon = false;
+ std::unique_ptr<ScPatternAttr> pCellAttrs;
+ OUString aString;
+
+ const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
+ aEngine.SetTextCurrentDefaults(rData);
+
+ if (bTestSimple) // test, if simple string without attribute
+ {
+ ScEditAttrTester aAttrTester( &aEngine );
+ bSimple = !aAttrTester.NeedsObject();
+ bCommon = aAttrTester.NeedsCellAttr();
+
+ // formulas have to be recognized even if they're formatted
+ // (but common attributes are still collected)
+
+ if (!bSimple)
+ {
+ OUString aParStr(aEngine.GetText( 0 ));
+ if ( aParStr[0] == '=' )
+ bSimple = true;
+ }
+
+ if (bCommon) // attribute for tab
+ {
+ pCellAttrs.reset(new ScPatternAttr( *pOldPattern ));
+ pCellAttrs->GetFromEditItemSet( &aAttrTester.GetAttribs() );
+ //! remove common attributes from EditEngine?
+ }
+ }
+
+ // #i97726# always get text for "repeat" of undo action
+ aString = ScEditUtil::GetMultilineString(aEngine);
+
+ // undo
+
+ std::unique_ptr<EditTextObject> pUndoData;
+ ScUndoEnterData::ValuesType aOldValues;
+
+ if (bRecord && !bSimple)
+ {
+ for (const auto& rTab : rMark)
+ {
+ ScUndoEnterData::Value aOldValue;
+ aOldValue.mnTab = rTab;
+ aOldValue.maCell.assign(rDoc, ScAddress(nCol, nRow, rTab));
+ aOldValues.push_back(aOldValue);
+ }
+
+ pUndoData = rData.Clone();
+ }
+
+ // enter data
+
+ if (bCommon)
+ rDoc.ApplyPattern(nCol,nRow,nTab,*pCellAttrs); //! undo
+
+ if (bSimple)
+ {
+ if (bCommon)
+ AdjustRowHeight(nRow,nRow,true);
+
+ EnterData( nCol, nRow, nTab, aString, nullptr, true /*bMatrixExpand*/);
+ }
+ else
+ {
+ for (const auto& rTab : rMark)
+ {
+ ScAddress aPos(nCol, nRow, rTab);
+ rDoc.SetEditText(aPos, rData, rDoc.GetEditPool());
+ }
+
+ if ( bRecord )
+ { // because of ChangeTrack current first
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoEnterData>(pDocSh, ScAddress(nCol,nRow,nTab), aOldValues, aString, std::move(pUndoData)));
+ }
+
+ HideAllCursors();
+
+ AdjustRowHeight(nRow,nRow,true);
+
+ for (const auto& rTab : rMark)
+ pDocSh->PostPaintCell( nCol, nRow, rTab );
+
+ ShowAllCursors();
+
+ pDocSh->UpdateOle(GetViewData());
+
+ bool bIsEmpty = rData.GetParagraphCount() == 0
+ || (rData.GetParagraphCount() == 1 && rData.GetText(0).isEmpty());
+ const OUString aType(bIsEmpty ? u"delete-content" : u"cell-change");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
+
+ aModificator.SetDocumentModified();
+ }
+ lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
+ }
+ else
+ {
+ ErrorMessage(aTester.GetMessageId());
+ PaintArea( nCol, nRow, nCol, nRow ); // possibly the edit-engine is still painted there
+ }
+}
+
+void ScViewFunc::EnterDataAtCursor( const OUString& rString )
+{
+ SCCOL nPosX = GetViewData().GetCurX();
+ SCROW nPosY = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ EnterData( nPosX, nPosY, nTab, rString );
+ // tdf#154174: update repeated data in the cell
+ GetViewData().GetViewShell()->UpdateInputHandler();
+}
+
+void ScViewFunc::EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram )
+{
+ ScViewData& rData = GetViewData();
+ const SCCOL nCol = rData.GetCurX();
+ const SCROW nRow = rData.GetCurY();
+ const ScMarkData& rMark = rData.GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ // nothing marked -> temporarily calculate block
+ // with size of result formula to get the size
+
+ ScDocument& rDoc = rData.GetDocument();
+ SCTAB nTab = rData.GetTabNo();
+ ScFormulaCell aFormCell( rDoc, ScAddress(nCol,nRow,nTab), rString, eGram, ScMatrixMode::Formula );
+
+ SCSIZE nSizeX;
+ SCSIZE nSizeY;
+ aFormCell.GetResultDimensions( nSizeX, nSizeY );
+ if ( nSizeX != 0 && nSizeY != 0 &&
+ nCol+nSizeX-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxCol()) &&
+ nRow+nSizeY-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxRow()) )
+ {
+ ScRange aResult( nCol, nRow, nTab,
+ sal::static_int_cast<SCCOL>(nCol+nSizeX-1),
+ sal::static_int_cast<SCROW>(nRow+nSizeY-1), nTab );
+ MarkRange( aResult, false );
+ }
+ }
+
+ ScRange aRange;
+ if (rData.GetSimpleArea(aRange) == SC_MARK_SIMPLE)
+ {
+ ScDocShell* pDocSh = rData.GetDocShell();
+ bool bSuccess = pDocSh->GetDocFunc().EnterMatrix(
+ aRange, &rMark, nullptr, rString, false, false, OUString(), eGram );
+ if (bSuccess)
+ pDocSh->UpdateOle(GetViewData());
+ else
+ PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
+ }
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+}
+
+SvtScriptType ScViewFunc::GetSelectionScriptType()
+{
+ SvtScriptType nScript = SvtScriptType::NONE;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ // no selection -> cursor
+
+ nScript = rDoc.GetScriptType( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo());
+ }
+ else
+ {
+ ScRangeList aRanges;
+ rMark.FillRangeListWithMarks( &aRanges, false );
+ nScript = rDoc.GetRangeScriptType(aRanges);
+ }
+
+ if (nScript == SvtScriptType::NONE)
+ nScript = ScGlobal::GetDefaultScriptType();
+
+ return nScript;
+}
+
+static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc);
+
+const ScPatternAttr* ScViewFunc::GetSelectionPattern()
+{
+ // Don't use UnmarkFiltered in slot state functions, for performance reasons.
+ // The displayed state is always that of the whole selection including filtered rows.
+
+ ScMarkData aMark = GetViewData().GetMarkData();
+ ScDocument& rDoc = GetViewData().GetDocument();
+
+ // tdf#155368 if the selection is the whole sheet, we need to shrink the mark area, otherwise
+ // we will not return a consistent result
+ // (consistent compared to what happens in ScViewFunc::ApplySelectionPattern)
+ ShrinkToDataArea( aMark, rDoc );
+
+ if ( aMark.IsMarked() || aMark.IsMultiMarked() )
+ {
+ // MarkToMulti is no longer necessary for rDoc.GetSelectionPattern
+ const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
+ return pAttr;
+ }
+ else
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ // copy sheet selection
+ aMark.SetMarkArea( ScRange( nCol, nRow, nTab ) );
+ const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
+ return pAttr;
+ }
+}
+
+void ScViewFunc::GetSelectionFrame(
+ std::shared_ptr<SvxBoxItem>& rLineOuter,
+ std::shared_ptr<SvxBoxInfoItem>& rLineInner )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ {
+ rDoc.GetSelectionFrame( rMark, *rLineOuter, *rLineInner );
+ }
+ else
+ {
+ const ScPatternAttr* pAttrs =
+ rDoc.GetPattern( GetViewData().GetCurX(),
+ GetViewData().GetCurY(),
+ GetViewData().GetTabNo() );
+
+ rLineOuter.reset(pAttrs->GetItem(ATTR_BORDER).Clone());
+ rLineInner.reset(pAttrs->GetItem(ATTR_BORDER_INNER).Clone());
+
+ rLineInner->SetTable(false);
+ rLineInner->SetDist(true);
+ rLineInner->SetMinDist(false);
+ }
+}
+
+// apply attribute - undo OK
+//
+// complete set ( ATTR_STARTINDEX, ATTR_ENDINDEX )
+
+void ScViewFunc::ApplyAttributes( const SfxItemSet& rDialogSet,
+ const SfxItemSet& rOldSet,
+ bool bAdjustBlockHeight)
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScPatternAttr aOldAttrs(( SfxItemSet(rOldSet) ));
+ ScPatternAttr aNewAttrs(( SfxItemSet(rDialogSet) ));
+ aNewAttrs.DeleteUnchanged( &aOldAttrs );
+
+ if ( rDialogSet.GetItemState( ATTR_VALUE_FORMAT ) == SfxItemState::SET )
+ { // don't reset to default SYSTEM GENERAL if not intended
+ sal_uInt32 nOldFormat =
+ rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ sal_uInt32 nNewFormat =
+ rDialogSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ if ( nNewFormat != nOldFormat )
+ {
+ SvNumberFormatter* pFormatter =
+ GetViewData().GetDocument().GetFormatTable();
+ const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
+ LanguageType eOldLang =
+ pOldEntry ? pOldEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
+ LanguageType eNewLang =
+ pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
+ if ( eNewLang != eOldLang )
+ {
+ aNewAttrs.GetItemSet().Put(
+ SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
+
+ // only the language has changed -> do not touch numberformat-attribute
+ sal_uInt32 nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
+ if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
+ nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
+ aNewAttrs.GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
+ }
+ }
+ }
+
+ if (rDialogSet.HasItem(ATTR_FONT_LANGUAGE))
+ // font language has changed. Redo the online spelling.
+ ResetAutoSpell();
+
+ const SvxBoxItem& rOldOuter = rOldSet.Get(ATTR_BORDER);
+ const SvxBoxItem& rNewOuter = rDialogSet.Get(ATTR_BORDER);
+ const SvxBoxInfoItem& rOldInner = rOldSet.Get(ATTR_BORDER_INNER);
+ const SvxBoxInfoItem& rNewInner = rDialogSet.Get(ATTR_BORDER_INNER);
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ SfxItemPool* pNewPool = rNewSet.GetPool();
+
+ pNewPool->DirectPutItemInPool(rNewOuter); // don't delete yet
+ pNewPool->DirectPutItemInPool(rNewInner);
+ rNewSet.ClearItem( ATTR_BORDER );
+ rNewSet.ClearItem( ATTR_BORDER_INNER );
+
+ /*
+ * establish whether border attribute is to be set:
+ * 1. new != old
+ * 2. is one of the borders not-DontCare (since 238.f: IsxxValid())
+ *
+ */
+
+ bool bFrame = (rDialogSet.GetItemState( ATTR_BORDER ) != SfxItemState::DEFAULT)
+ || (rDialogSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::DEFAULT);
+
+ if (SfxPoolItem::areSame(rNewOuter, rOldOuter) && SfxPoolItem::areSame(rNewInner, rOldInner))
+ bFrame = false;
+
+ // this should be intercepted by the pool: ?!??!??
+
+ if (bFrame && rNewOuter == rOldOuter && rNewInner == rOldInner)
+ bFrame = false;
+
+ bFrame = bFrame
+ && ( rNewInner.IsValid(SvxBoxInfoItemValidFlags::LEFT)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::TOP)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::HORI)
+ || rNewInner.IsValid(SvxBoxInfoItemValidFlags::VERT) );
+
+ if (!bFrame)
+ ApplySelectionPattern( aNewAttrs ); // standard only
+ else
+ {
+ // if new items are default-items, overwrite the old items:
+
+ bool bDefNewOuter = IsStaticDefaultItem(&rNewOuter);
+ bool bDefNewInner = IsStaticDefaultItem(&rNewInner);
+
+ ApplyPatternLines( aNewAttrs,
+ bDefNewOuter ? rOldOuter : rNewOuter,
+ bDefNewInner ? &rOldInner : &rNewInner );
+ }
+
+ pNewPool->DirectRemoveItemFromPool(rNewOuter); // release
+ pNewPool->DirectRemoveItemFromPool(rNewInner);
+
+ // adjust height only if needed
+ if (bAdjustBlockHeight)
+ AdjustBlockHeight();
+
+ // CellContentChanged is called in ApplySelectionPattern / ApplyPatternLines
+}
+
+void ScViewFunc::ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScPatternAttr aNewAttrs(
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( *GetViewData().GetDocument().GetPool() ) );
+
+ aNewAttrs.GetItemSet().Put( rAttrItem );
+ // if justify is set (with Buttons), always indentation 0
+ if ( rAttrItem.Which() == ATTR_HOR_JUSTIFY )
+ aNewAttrs.GetItemSet().Put( ScIndentItem( 0 ) );
+ ApplySelectionPattern( aNewAttrs );
+
+ // Prevent useless compute
+ if (bAdjustBlockHeight)
+ AdjustBlockHeight();
+
+ // CellContentChanged is called in ApplySelectionPattern
+}
+
+// patterns and borders
+
+void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem& rNewOuter,
+ const SvxBoxInfoItem* pNewInner )
+{
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ bool bRemoveAdjCellBorder = rNewOuter.IsRemoveAdjacentCellBorder();
+ ScRange aMarkRange, aMarkRangeWithEnvelope;
+ aFuncMark.MarkToSimple();
+ bool bMulti = aFuncMark.IsMultiMarked();
+ if (bMulti)
+ aMarkRange = aFuncMark.GetMultiMarkArea();
+ else if (aFuncMark.IsMarked())
+ aMarkRange = aFuncMark.GetMarkArea();
+ else
+ {
+ aMarkRange = ScRange( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ DoneBlockMode();
+ InitOwnBlockMode( aMarkRange );
+ aFuncMark.SetMarkArea(aMarkRange);
+ MarkDataChanged();
+ }
+ if( bRemoveAdjCellBorder )
+ aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
+ else
+ aMarkRangeWithEnvelope = aMarkRange;
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ if (bRecord)
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bCopyOnlyMarked = false;
+ if( !bRemoveAdjCellBorder )
+ bCopyOnlyMarked = bMulti;
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+
+ ScRange aCopyRange = aMarkRangeWithEnvelope;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, *pUndoDoc, &aFuncMark );
+
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionAttr>(
+ pDocSh, aFuncMark,
+ aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
+ aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
+ std::move(pUndoDoc), bCopyOnlyMarked, &rAttr, &rNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
+ }
+
+ sal_uInt16 nExt = SC_PF_TESTMERGE;
+ pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change
+
+ rDoc.ApplySelectionFrame(aFuncMark, rNewOuter, pNewInner);
+
+ pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change
+
+ aFuncMark.MarkToMulti();
+ rDoc.ApplySelectionPattern( rAttr, aFuncMark );
+
+ pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid, nExt );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ CellContentChanged();
+
+ StartFormatArea();
+}
+
+// tdf#147842 if the marked area is the entire sheet, then shrink it to the data area.
+// Otherwise ctrl-A, perform-action, will take a very long time as it tries to modify
+// cells that we are not using.
+static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc)
+{
+ // do not make it marked if it is not already marked
+ if (!rFuncMark.IsMarked())
+ return;
+ if (rFuncMark.IsMultiMarked())
+ return;
+ ScRange aMarkArea = rFuncMark.GetMarkArea();
+ const ScSheetLimits& rLimits = rDoc.GetSheetLimits();
+ if (aMarkArea.aStart.Row() != 0 || aMarkArea.aStart.Col() != 0)
+ return;
+ if (aMarkArea.aEnd.Row() != rLimits.MaxRow() || aMarkArea.aEnd.Col() != rLimits.MaxCol())
+ return;
+ if (aMarkArea.aStart.Tab() != aMarkArea.aEnd.Tab())
+ return;
+ SCCOL nStartCol = aMarkArea.aStart.Col();
+ SCROW nStartRow = aMarkArea.aStart.Row();
+ SCCOL nEndCol = aMarkArea.aEnd.Col();
+ SCROW nEndRow = aMarkArea.aEnd.Row();
+ rDoc.ShrinkToDataArea(aMarkArea.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow);
+ aMarkArea.aStart.SetCol(nStartCol);
+ aMarkArea.aStart.SetRow(nStartRow);
+ aMarkArea.aEnd.SetCol(nEndCol);
+ aMarkArea.aEnd.SetRow(nEndRow);
+ rFuncMark.ResetMark();
+ rFuncMark.SetMarkArea(aMarkArea);
+}
+
+// pattern only
+
+void ScViewFunc::ApplySelectionPattern( const ScPatternAttr& rAttr, bool bCursorOnly )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ // State from old ItemSet doesn't matter for paint flags, as any change will be
+ // from SfxItemState::SET in the new ItemSet (default is ignored in ApplyPattern).
+ // New alignment is checked (check in PostPaint isn't enough) in case a right
+ // alignment is changed to left.
+ const SfxItemSet& rNewSet = rAttr.GetItemSet();
+ bool bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
+ rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
+ bool bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
+
+ sal_uInt16 nExtFlags = 0;
+ if ( bSetLines )
+ nExtFlags |= SC_PF_LINES;
+ if ( bSetAlign )
+ nExtFlags |= SC_PF_WHOLEROWS;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ bool bMulti = aFuncMark.IsMultiMarked();
+ aFuncMark.MarkToMulti();
+ bool bOnlyTab = (!aFuncMark.IsMultiMarked() && !bCursorOnly && aFuncMark.GetSelectCount() > 1);
+ if (bOnlyTab)
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ aFuncMark.SetMarkArea(ScRange(nCol,nRow,nTab));
+ aFuncMark.MarkToMulti();
+ }
+
+ ScRangeList aChangeRanges;
+
+ if (aFuncMark.IsMultiMarked() && !bCursorOnly)
+ {
+ const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (const auto& rTab : aFuncMark)
+ {
+ ScRange aChangeRange( aMarkRange );
+ aChangeRange.aStart.SetTab( rTab );
+ aChangeRange.aEnd.SetTab( rTab );
+ aChangeRanges.push_back( aChangeRange );
+ }
+
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+
+ ScEditDataArray* pEditDataArray = nullptr;
+ if (bRecord)
+ {
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nStartTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &aFuncMark );
+
+ aFuncMark.MarkToMulti();
+
+ ScUndoSelectionAttr* pUndoAttr = new ScUndoSelectionAttr(
+ pDocSh, aFuncMark, nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), bMulti, &rAttr );
+ pDocSh->GetUndoManager()->AddUndoAction(std::unique_ptr<ScUndoSelectionAttr>(pUndoAttr));
+ pEditDataArray = pUndoAttr->GetDataArray();
+ }
+
+ rDoc.ApplySelectionPattern( rAttr, aFuncMark, pEditDataArray );
+
+ pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab,
+ PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ CellContentChanged();
+ }
+ else // single cell - simpler undo
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ std::unique_ptr<EditTextObject> pOldEditData;
+ std::unique_ptr<EditTextObject> pNewEditData;
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(rDoc, aPos);
+ if (aCell.getType() == CELLTYPE_EDIT)
+ {
+ const EditTextObject* pEditObj = aCell.getEditText();
+ pOldEditData = pEditObj->Clone();
+ rDoc.RemoveEditTextCharAttribs(aPos, rAttr);
+ pEditObj = rDoc.GetEditText(aPos);
+ pNewEditData = pEditObj->Clone();
+ }
+
+ aChangeRanges.push_back(aPos);
+ std::optional<ScPatternAttr> pOldPat(*rDoc.GetPattern( nCol, nRow, nTab ));
+
+ rDoc.ApplyPattern( nCol, nRow, nTab, rAttr );
+
+ const ScPatternAttr* pNewPat = rDoc.GetPattern( nCol, nRow, nTab );
+
+ if (bRecord)
+ {
+ std::unique_ptr<ScUndoCursorAttr> pUndo(new ScUndoCursorAttr(
+ pDocSh, nCol, nRow, nTab, &*pOldPat, pNewPat, &rAttr ));
+ pUndo->SetEditData(std::move(pOldEditData), std::move(pNewEditData));
+ pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndo));
+ }
+ pOldPat.reset(); // is copied in undo (Pool)
+
+ pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
+ pDocSh->UpdateOle(GetViewData());
+ aModificator.SetDocumentModified();
+ CellContentChanged();
+ }
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aProperties;
+ sal_Int32 nCount = 0;
+ const SfxItemPropertyMap& rMap = ScCellObj::GetCellPropertyMap();
+ for ( sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; ++nWhich )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if ( rNewSet.GetItemState( nWhich, true, &pItem ) == SfxItemState::SET && pItem )
+ {
+ for ( const auto pEntry : rMap.getPropertyEntries())
+ {
+ if ( pEntry->nWID == nWhich )
+ {
+ css::uno::Any aVal;
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+ aProperties.realloc( nCount + 1 );
+ auto pProperties = aProperties.getArray();
+ pProperties[ nCount ].Name = pEntry->aName;
+ pProperties[ nCount ].Value = aVal;
+ ++nCount;
+ }
+ }
+ }
+ }
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "attribute", aProperties);
+ }
+
+ StartFormatArea();
+}
+
+void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
+{
+ // ItemSet from UI, may have different pool
+
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
+ SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
+ rNewSet.Put( rItemSet, false );
+ ApplySelectionPattern( aNewAttrs );
+
+ AdjustBlockHeight();
+}
+
+const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
+{
+ // Don't use UnmarkFiltered in slot state functions, for performance reasons.
+ // The displayed state is always that of the whole selection including filtered rows.
+
+ const ScStyleSheet* pSheet = nullptr;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScMarkData& rMark = rViewData.GetMarkData();
+
+ if ( rMark.IsMarked() || rMark.IsMultiMarked() )
+ pSheet = rDoc.GetSelectionStyle( rMark ); // MarkToMulti isn't necessary
+ else
+ pSheet = rDoc.GetStyle( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo() );
+
+ return pSheet;
+}
+
+void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ if ( !pStyleSheet) return;
+
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+ SCTAB nTabCount = rDoc.GetTableCount();
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
+ {
+ aFuncMark.MarkToMulti();
+ const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
+
+ if ( bRecord )
+ {
+ SCTAB nTab = rViewData.GetTabNo();
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+
+ ScRange aCopyRange = aMarkRange;
+ aCopyRange.aStart.SetTab(0);
+ aCopyRange.aEnd.SetTab(nTabCount-1);
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aFuncMark );
+ aFuncMark.MarkToMulti();
+
+ OUString aName = pStyleSheet->GetName();
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionStyle>( pDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
+ }
+
+ rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );
+
+ if (!AdjustBlockHeight())
+ rViewData.GetDocShell()->PostPaint( aMarkRange, PaintPartFlags::Grid );
+
+ aFuncMark.MarkToSimple();
+ }
+ else
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+
+ if ( bRecord )
+ {
+ ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab );
+ for (const auto& rTab : aFuncMark)
+ if (rTab != nTab)
+ pUndoDoc->AddUndoTab( rTab, rTab );
+
+ ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
+ rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );
+
+ ScRange aMarkRange ( nCol, nRow, nTab );
+ ScMarkData aUndoMark = aFuncMark;
+ aUndoMark.SetMultiMarkArea( aMarkRange );
+
+ OUString aName = pStyleSheet->GetName();
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoSelectionStyle>( pDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
+ }
+
+ for (const auto& rTab : aFuncMark)
+ rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );
+
+ if (!AdjustBlockHeight())
+ rViewData.GetDocShell()->PostPaintCell( nCol, nRow, nTab );
+
+ }
+
+ aModificator.SetDocumentModified();
+
+ StartFormatArea();
+}
+
+void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
+{
+ if ( !pStyleSheet) return;
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
+ rViewData.GetPPTX(),
+ rViewData.GetPPTY(),
+ rViewData.GetZoomX(),
+ rViewData.GetZoomY() );
+
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ aModificator.SetDocumentModified();
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+}
+
+void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
+{
+ if ( !pStyleSheet) return;
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
+ rViewData.GetPPTX(),
+ rViewData.GetPPTY(),
+ rViewData.GetZoomX(),
+ rViewData.GetZoomY() );
+
+ pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
+ aModificator.SetDocumentModified();
+
+ ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
+ if (pHdl)
+ pHdl->ForgetLastPattern();
+}
+
+
+void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
+{
+ if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
+ return;
+
+ SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
+ SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStartCol);
+
+ // if we remove a column the cursor position and the current selection
+ // in other views could need to be moved on the left by one column.
+ if (pTabViewShell != this)
+ {
+ if (pTabViewShell->getPart() == nCurrentTabIndex)
+ {
+ SCCOL nX = pTabViewShell->GetViewData().GetCurX();
+ if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
+ {
+ ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
+ SCROW nY = pTabViewShell->GetViewData().GetCurY();
+ pTabViewShell->SetCursor(nX + nOffset, nY);
+ if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ pInputHdl->SetModified();
+ }
+ }
+
+ ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
+ aMultiMark.SetMarking( false );
+ aMultiMark.MarkToMulti();
+ if (aMultiMark.IsMultiMarked())
+ {
+ aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
+ pTabViewShell->SetMarkData(aMultiMark);
+ }
+ }
+ else
+ {
+ SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
+ if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
+ {
+ pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
+ }
+ }
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
+{
+ if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
+ return;
+
+ SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
+ SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
+ pPosHelper->invalidateByIndex(nStartRow);
+
+ // if we remove a row the cursor position and the current selection
+ // in other views could need to be moved up by one row.
+ if (pTabViewShell != this)
+ {
+ if (pTabViewShell->getPart() == nCurrentTabIndex)
+ {
+ SCROW nY = pTabViewShell->GetViewData().GetCurY();
+ if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
+ {
+ ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
+ SCCOL nX = pTabViewShell->GetViewData().GetCurX();
+ pTabViewShell->SetCursor(nX, nY + nOffset);
+ if (pInputHdl && pInputHdl->IsInputMode())
+ {
+ pInputHdl->SetModified();
+ }
+ }
+
+ ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
+ aMultiMark.SetMarking( false );
+ aMultiMark.MarkToMulti();
+ if (aMultiMark.IsMultiMarked())
+ {
+ aMultiMark.ShiftRows(pTabViewShell->GetViewData().GetDocument(), nStartRow, nOffset);
+ pTabViewShell->SetMarkData(aMultiMark);
+ }
+ }
+ else
+ {
+ SCROW nY = pTabViewShell->GetViewData().GetCurYForTab(nCurrentTabIndex);
+ if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
+ {
+ pTabViewShell->GetViewData().SetCurYForTab(nY + nOffset, nCurrentTabIndex);
+ }
+ }
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+void ScViewFunc::OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
+ if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
+ {
+ if (bWidth)
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurTab))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+ else
+ {
+ if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurTab))
+ pPosHelper->invalidateByIndex(nStart);
+ }
+ }
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+// insert cells - undo OK
+
+bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste )
+{
+ ScRange aRange;
+ ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
+ if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+ bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste );
+ if (bSuccess)
+ {
+ ResetAutoSpellForContentChange();
+ bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
+ bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
+
+ pDocSh->UpdateOle(GetViewData());
+ CellContentChanged();
+
+ if ( bInsertCols || bInsertRows )
+ {
+ OUString aOperation = bInsertRows ?
+ OUString("insert-rows"):
+ OUString("insert-columns");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (bInsertCols)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
+
+ if (bInsertRows)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bInsertCols, bInsertRows, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ }
+ else
+ {
+ ErrorMessage(STR_ERR_INSERT_CELLS);
+ }
+
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "INSERT_CELLS");
+ return bSuccess;
+ }
+ else
+ {
+ ErrorMessage(STR_NOMULTISELECT);
+ return false;
+ }
+}
+
+// delete cells - undo OK
+
+void ScViewFunc::DeleteCells( DelCellCmd eCmd )
+{
+ ScRange aRange;
+ if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
+ {
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ const ScMarkData& rMark = GetViewData().GetMarkData();
+
+#if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
+ // #i94841# [Collaboration] if deleting rows is rejected, the content is sometimes wrong
+ if ( pDocSh->IsDocShared() && ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols ) )
+ {
+ ScRange aDelRange( aRange.aStart );
+ SCCOLROW nCount = 0;
+ if ( eCmd == DelCellCmd::Rows )
+ {
+ nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
+ }
+ else
+ {
+ nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Col() - aRange.aStart.Col() + 1 );
+ }
+ while ( nCount > 0 )
+ {
+ pDocSh->GetDocFunc().DeleteCells( aDelRange, &rMark, eCmd, false );
+ --nCount;
+ }
+ }
+ else
+#endif
+ {
+ pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
+ }
+
+ ResetAutoSpellForContentChange();
+ pDocSh->UpdateOle(GetViewData());
+ CellContentChanged();
+
+ if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
+ {
+ OUString aOperation = ( eCmd == DelCellCmd::Rows) ?
+ OUString("delete-rows"):
+ OUString("delete-columns");
+ HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
+ }
+
+ // put cursor directly behind deleted range
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ if ( eCmd==DelCellCmd::CellsLeft || eCmd==DelCellCmd::Cols )
+ nCurX = aRange.aStart.Col();
+ else
+ nCurY = aRange.aStart.Row();
+ SetCursor( nCurX, nCurY );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bColsDeleted = (eCmd == DelCellCmd::Cols);
+ bool bRowsDeleted = (eCmd == DelCellCmd::Rows);
+ if (bColsDeleted)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
+
+ if (bRowsDeleted)
+ ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
+
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bColsDeleted, bRowsDeleted, true /* bSizes*/,
+ true /* bHidden */, true /* bFiltered */,
+ true /* bGroups */, GetViewData().GetTabNo());
+ }
+ }
+ else
+ {
+ if (eCmd == DelCellCmd::Cols)
+ DeleteMulti( false );
+ else if (eCmd == DelCellCmd::Rows)
+ DeleteMulti( true );
+ else
+ ErrorMessage(STR_NOMULTISELECT);
+ }
+
+ OUString aStartAddress = aRange.aStart.GetColRowString();
+ OUString aEndAddress = aRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE_CELLS");
+
+ Unmark();
+}
+
+void ScViewFunc::DeleteMulti( bool bRows )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocShellModificator aModificator( *pDocSh );
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+
+ bool bRecord = true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ std::vector<sc::ColRowSpan> aSpans;
+ if (bRows)
+ aSpans = aFuncMark.GetMarkedRowSpans();
+ else
+ aSpans = aFuncMark.GetMarkedColSpans();
+
+ if (aSpans.empty())
+ {
+ SCCOLROW nCurPos = bRows ? GetViewData().GetCurY() : GetViewData().GetCurX();
+ aSpans.emplace_back(nCurPos, nCurPos);
+ }
+
+ // test if allowed
+
+ TranslateId pErrorId;
+ bool bNeedRefresh = false;
+ for (size_t i = 0, n = aSpans.size(); i < n && !pErrorId; ++i)
+ {
+ SCCOLROW nStart = aSpans[i].mnStart;
+ SCCOLROW nEnd = aSpans[i].mnEnd;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ if ( bRows )
+ {
+ nStartCol = 0;
+ nEndCol = rDoc.MaxCol();
+ nStartRow = static_cast<SCROW>(nStart);
+ nEndRow = static_cast<SCROW>(nEnd);
+ }
+ else
+ {
+ nStartCol = static_cast<SCCOL>(nStart);
+ nEndCol = static_cast<SCCOL>(nEnd);
+ nStartRow = 0;
+ nEndRow = rDoc.MaxRow();
+ }
+
+ // cell protection (only needed for first range, as all following cells are moved)
+ if (i == 0)
+ {
+ // test to the end of the sheet
+ ScEditableTester aTester( rDoc, nTab, nStartCol, nStartRow, rDoc.MaxCol(), rDoc.MaxRow() );
+ if (!aTester.IsEditable())
+ pErrorId = aTester.GetMessageId();
+ }
+
+ // merged cells
+ SCCOL nMergeStartX = nStartCol;
+ SCROW nMergeStartY = nStartRow;
+ SCCOL nMergeEndX = nEndCol;
+ SCROW nMergeEndY = nEndRow;
+ rDoc.ExtendMerge( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
+ rDoc.ExtendOverlapped( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
+
+ if ( nMergeStartX != nStartCol || nMergeStartY != nStartRow )
+ {
+ // Disallow deleting parts of a merged cell.
+ // Deleting the start is allowed (merge is removed), so the end doesn't have to be checked.
+
+ pErrorId = STR_MSSG_DELETECELLS_0;
+ }
+ if ( nMergeEndX != nEndCol || nMergeEndY != nEndRow )
+ {
+ // detect if the start of a merged cell is deleted, so the merge flags can be refreshed
+
+ bNeedRefresh = true;
+ }
+ }
+
+ if (pErrorId)
+ {
+ ErrorMessage(pErrorId);
+ return;
+ }
+
+ // proceed
+
+ weld::WaitObject aWait(GetViewData().GetDialogParent()); // important for TrackFormulas in UpdateReference
+
+ ResetAutoSpellForContentChange();
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScRefUndoData> pUndoData;
+ if (bRecord)
+ {
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, !bRows, bRows ); // row height
+
+ for (const sc::ColRowSpan & rSpan : aSpans)
+ {
+ SCCOLROW nStart = rSpan.mnStart;
+ SCCOLROW nEnd = rSpan.mnEnd;
+ if (bRows)
+ rDoc.CopyToDocument( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, InsertDeleteFlags::ALL,false,*pUndoDoc );
+ else
+ rDoc.CopyToDocument( static_cast<SCCOL>(nStart),0,nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
+ InsertDeleteFlags::ALL,false,*pUndoDoc );
+ }
+
+ // all Formulas because of references
+ SCTAB nTabCount = rDoc.GetTableCount();
+ pUndoDoc->AddUndoTab( 0, nTabCount-1 );
+ rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA,false,*pUndoDoc );
+
+ pUndoData.reset(new ScRefUndoData( &rDoc ));
+
+ rDoc.BeginDrawUndo();
+ }
+
+ std::vector<sc::ColRowSpan>::const_reverse_iterator ri = aSpans.rbegin(), riEnd = aSpans.rend();
+ aFuncMark.SelectOneTable(nTab);
+ for (; ri != riEnd; ++ri)
+ {
+ SCCOLROW nEnd = ri->mnEnd;
+ SCCOLROW nStart = ri->mnStart;
+
+ if (bRows)
+ {
+ rDoc.DeleteObjectsInArea(0, nStart, rDoc.MaxCol(), nEnd, aFuncMark, true);
+ rDoc.DeleteRow(0, nTab, rDoc.MaxCol(), nTab, nStart, static_cast<SCSIZE>(nEnd - nStart + 1));
+ }
+ else
+ {
+ rDoc.DeleteObjectsInArea(nStart, 0, nEnd, rDoc.MaxRow(), aFuncMark, true);
+ rDoc.DeleteCol(0, nTab, rDoc.MaxRow(), nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd - nStart + 1));
+ }
+ }
+
+ if (bNeedRefresh)
+ {
+ SCCOLROW nFirstStart = aSpans[0].mnStart;
+ SCCOL nStartCol = bRows ? 0 : static_cast<SCCOL>(nFirstStart);
+ SCROW nStartRow = bRows ? static_cast<SCROW>(nFirstStart) : 0;
+ SCCOL nEndCol = rDoc.MaxCol();
+ SCROW nEndRow = rDoc.MaxRow();
+
+ rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
+ rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
+ }
+
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoDeleteMulti>(
+ pDocSh, bRows, bNeedRefresh, nTab, std::vector(aSpans), std::move(pUndoDoc), std::move(pUndoData)));
+ }
+
+ if (!AdjustRowHeight(0, rDoc.MaxRow(), true))
+ {
+ if (bRows)
+ {
+ pDocSh->PostPaint(
+ 0, aSpans[0].mnStart, nTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Left));
+ }
+ else
+ {
+ pDocSh->PostPaint(
+ static_cast<SCCOL>(aSpans[0].mnStart), 0, nTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Top));
+ }
+ }
+
+ aModificator.SetDocumentModified();
+
+ CellContentChanged();
+
+ // put cursor directly behind the first deleted range
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ if ( bRows )
+ nCurY = aSpans[0].mnStart;
+ else
+ nCurX = static_cast<SCCOL>(aSpans[0].mnStart);
+ SetCursor( nCurX, nCurY );
+
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
+}
+
+// delete contents
+
+void ScViewFunc::DeleteContents( InsertDeleteFlags nFlags )
+{
+ ScViewData& rViewData = GetViewData();
+ rViewData.SetPasteMode( ScPasteFlags::NONE );
+ rViewData.GetViewShell()->UpdateCopySourceOverlay();
+
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ bool bEditable = SelectionEditable( &bOnlyNotBecauseOfMatrix );
+ if ( !bEditable )
+ {
+ if ( !(bOnlyNotBecauseOfMatrix &&
+ ((nFlags & (InsertDeleteFlags::ATTRIB | InsertDeleteFlags::EDITATTR)) == nFlags)) )
+ {
+ ErrorMessage(bOnlyNotBecauseOfMatrix ? STR_MATRIXFRAGMENTERR : STR_PROTECTIONERR);
+ return;
+ }
+ }
+
+ ScRange aMarkRange;
+ bool bSimple = false;
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
+ ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
+
+ bool bRecord =true;
+ if (!rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
+ {
+ aMarkRange.aStart.SetCol(GetViewData().GetCurX());
+ aMarkRange.aStart.SetRow(GetViewData().GetCurY());
+ aMarkRange.aStart.SetTab(GetViewData().GetTabNo());
+ aMarkRange.aEnd = aMarkRange.aStart;
+ if ( rDoc.HasAttrib( aMarkRange, HasAttrFlags::Merged ) )
+ {
+ aFuncMark.SetMarkArea( aMarkRange );
+ }
+ else
+ bSimple = true;
+ }
+
+ HideAllCursors(); // for if summary is cancelled
+
+ ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
+
+ // Can we really be sure that we can pass the bApi parameter as false to DeleteCell() and
+ // DeleteContents() here? (Meaning that this is interactive use.) Is this never invoked from
+ // scripting and whatnot?
+ if (bSimple)
+ rDocFunc.DeleteCell(aMarkRange.aStart, aFuncMark, nFlags, bRecord, /*bApi=*/ false);
+ else
+ rDocFunc.DeleteContents(aFuncMark, nFlags, bRecord, /*bApi=*/ false);
+
+ pDocSh->UpdateOle(GetViewData());
+
+ if (ScModelObj* pModelObj = pDocSh->GetModel())
+ {
+ ScRangeList aChangeRanges;
+ if ( bSimple )
+ {
+ aChangeRanges.push_back( aMarkRange );
+ }
+ else
+ {
+ aFuncMark.FillRangeListWithMarks( &aChangeRanges, false );
+ }
+
+ if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "delete-content");
+ else if (pModelObj)
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate");
+ }
+
+ CellContentChanged();
+ ShowAllCursors();
+
+ if ( nFlags & InsertDeleteFlags::ATTRIB )
+ {
+ if ( nFlags & InsertDeleteFlags::CONTENTS )
+ bFormatValid = false;
+ else
+ StartFormatArea(); // delete attribute is also attribute-change
+ }
+ OUString aStartAddress = aMarkRange.aStart.GetColRowString();
+ OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
+ collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE");
+}
+
+// column width/row height (via header) - undo OK
+
+void ScViewFunc::SetWidthOrHeight(
+ bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
+ sal_uInt16 nSizeTwips, bool bRecord, const ScMarkData* pMarkData )
+{
+ if (rRanges.empty())
+ return;
+
+ // Use view's mark if none specified, but do not modify the original data,
+ // i.e. no MarkToMulti() on that.
+ ScMarkData aMarkData( pMarkData ? *pMarkData : GetViewData().GetMarkData());
+
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCCOL nCurX = GetViewData().GetCurX();
+ SCROW nCurY = GetViewData().GetCurY();
+ SCTAB nFirstTab = aMarkData.GetFirstSelected();
+ SCTAB nCurTab = GetViewData().GetTabNo();
+ if (bRecord && !rDoc.IsUndoEnabled())
+ bRecord = false;
+
+ ScDocShellModificator aModificator( *pDocSh );
+
+ bool bAllowed = true;
+ for (const SCTAB& nTab : aMarkData)
+ {
+ bAllowed = std::all_of(rRanges.begin(), rRanges.end(),
+ [&bWidth, &rDoc, &nTab](const sc::ColRowSpan& rRange) {
+ bool bOnlyMatrix;
+ bool bIsBlockEditable;
+ if (bWidth)
+ bIsBlockEditable = rDoc.IsBlockEditable(nTab, rRange.mnStart, 0, rRange.mnEnd, rDoc.MaxRow(), &bOnlyMatrix);
+ else
+ bIsBlockEditable = rDoc.IsBlockEditable(nTab, 0, rRange.mnStart, rDoc.MaxCol(), rRange.mnEnd, &bOnlyMatrix);
+ return bIsBlockEditable || bOnlyMatrix;
+ });
+ if (!bAllowed)
+ break;
+ }
+
+ // Allow users to resize cols/rows in readonly docs despite the r/o state.
+ // It is frustrating to be unable to see content in mis-sized cells.
+ if( !bAllowed && !pDocSh->IsReadOnly() )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ SCCOLROW nStart = rRanges.front().mnStart;
+ SCCOLROW nEnd = rRanges.back().mnEnd;
+
+ OnLOKSetWidthOrHeight(nStart, bWidth);
+
+ bool bFormula = false;
+ if ( eMode == SC_SIZE_OPTIMAL )
+ {
+ const ScViewOptions& rOpts = GetViewData().GetOptions();
+ bFormula = rOpts.GetOption( VOPT_FORMULAS );
+ }
+
+ ScDocumentUniquePtr pUndoDoc;
+ std::unique_ptr<ScOutlineTable> pUndoTab;
+ std::vector<sc::ColRowSpan> aUndoRanges;
+
+ if ( bRecord )
+ {
+ rDoc.BeginDrawUndo(); // Drawing Updates
+
+ pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
+ for (const SCTAB& nTab : aMarkData)
+ {
+ if (bWidth)
+ {
+ if ( nTab == nFirstTab )
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
+ else
+ pUndoDoc->AddUndoTab( nTab, nTab, true );
+ rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
+ false, *pUndoDoc );
+ }
+ else
+ {
+ if ( nTab == nFirstTab )
+ pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
+ else
+ pUndoDoc->AddUndoTab( nTab, nTab, false, true );
+ rDoc.CopyToDocument( 0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
+ }
+ }
+
+ aUndoRanges = rRanges;
+
+ //! outlines from all tab?
+ ScOutlineTable* pTable = rDoc.GetOutlineTable( nCurTab );
+ if (pTable)
+ pUndoTab.reset(new ScOutlineTable( *pTable ));
+ }
+
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ aMarkData.MarkToMulti();
+
+ bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
+ bool bOutline = false;
+
+ for (const SCTAB& nTab : aMarkData)
+ {
+ for (const sc::ColRowSpan & rRange : rRanges)
+ {
+ SCCOLROW nStartNo = rRange.mnStart;
+ SCCOLROW nEndNo = rRange.mnEnd;
+
+ if ( !bWidth ) // height always blockwise
+ {
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ {
+ bool bAll = ( eMode==SC_SIZE_OPTIMAL );
+ bool bFiltered = false;
+ if (!bAll)
+ {
+ // delete CRFlags::ManualSize for all in range,
+ // then SetOptimalHeight with bShrink = FALSE
+ for (SCROW nRow = nStartNo; nRow <= nEndNo; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ CRFlags nOld = rDoc.GetRowFlags(nRow, nTab);
+ if (nOld & CRFlags::ManualSize)
+ rDoc.SetRowFlags(nRow, nTab, nOld & ~CRFlags::ManualSize);
+ }
+ }
+ else
+ {
+ SCROW nLastRow = nStartNo;
+ if (rDoc.RowFiltered(nStartNo, nTab, nullptr, &nLastRow)
+ || nLastRow < nEndNo)
+ bFiltered = true;
+ }
+
+
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
+ aCxt.setForceAutoSize(bAll);
+ aCxt.setExtraHeight(nSizeTwips);
+ rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true);
+
+ if (bFiltered)
+ ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, bShow);
+
+ // Manual-Flag already (re)set in SetOptimalHeight in case of bAll=sal_True
+ // (set for Extra-Height, else reset).
+ }
+ else if ( eMode==SC_SIZE_DIRECT )
+ {
+ if (nSizeTwips)
+ {
+ rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
+ rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
+ }
+
+ // tdf#36383 - Skip consecutive rows hidden by AutoFilter
+ ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, nSizeTwips != 0);
+
+ if (!bShow && nStartNo <= nCurY && nCurY <= nEndNo && nTab == nCurTab)
+ {
+ nCurY = -1;
+ }
+ }
+ else if ( eMode==SC_SIZE_SHOW )
+ {
+ rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
+ }
+ }
+ else // column width
+ {
+ for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
+ {
+ const bool bIsColHidden = rDoc.ColHidden(nCol, nTab);
+ if ( eMode != SC_SIZE_VISOPT || !bIsColHidden )
+ {
+ sal_uInt16 nThisSize = nSizeTwips;
+
+ if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
+ nThisSize = nSizeTwips + GetOptimalColWidth( nCol, nTab, bFormula );
+ if ( nThisSize )
+ rDoc.SetColWidth( nCol, nTab, nThisSize );
+
+ // tdf#131073 - Don't show hidden cols after setting optimal col width
+ if (eMode == SC_SIZE_OPTIMAL)
+ rDoc.ShowCol(nCol, nTab, !bIsColHidden);
+ else
+ rDoc.ShowCol( nCol, nTab, bShow );
+
+ if (!bShow && nCol == nCurX && nTab == nCurTab)
+ {
+ nCurX = -1;
+ }
+ }
+ }
+ }
+
+ // adjust outline
+ if (bWidth)
+ {
+ if ( rDoc.UpdateOutlineCol( static_cast<SCCOL>(nStartNo),
+ static_cast<SCCOL>(nEndNo), nTab, bShow ) )
+ bOutline = true;
+ }
+ else
+ {
+ if ( rDoc.UpdateOutlineRow( nStartNo, nEndNo, nTab, bShow ) )
+ bOutline = true;
+ }
+ }
+ rDoc.SetDrawPageSize(nTab);
+ }
+
+ if (!bOutline)
+ pUndoTab.reset();
+
+ if (bRecord)
+ {
+ pDocSh->GetUndoManager()->AddUndoAction(
+ std::make_unique<ScUndoWidthOrHeight>(
+ pDocSh, aMarkData, nStart, nCurTab, nEnd, nCurTab,
+ std::move(pUndoDoc), std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
+ }
+
+ if (nCurX < 0)
+ {
+ MoveCursorRel( 1, 0, SC_FOLLOW_LINE, false );
+ }
+
+ if (nCurY < 0)
+ {
+ MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
+ }
+
+ // fdo#36247 Ensure that the drawing layer's map mode scaling factors match
+ // the new heights and widths.
+ GetViewData().GetView()->RefreshZoom();
+
+ for (const SCTAB& nTab : aMarkData)
+ rDoc.UpdatePageBreaks( nTab );
+
+ bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
+ ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
+ bWidth /* bColumns */, !bWidth /* bRows */,
+ true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
+ false /* bGroups */, nCurTab);
+ GetViewData().GetView()->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
+
+ {
+ for (const SCTAB& nTab : aMarkData)
+ {
+ if (bWidth)
+ {
+ if (rDoc.HasAttrib( static_cast<SCCOL>(nStart),0,nTab,
+ static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ nStart = 0;
+ if (nStart > 0) // go upwards because of Lines and cursor
+ --nStart;
+ pDocSh->PostPaint( static_cast<SCCOL>(nStart), 0, nTab,
+ rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Top );
+ }
+ else
+ {
+ if (rDoc.HasAttrib( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ nStart = 0;
+ if (nStart != 0)
+ --nStart;
+ pDocSh->PostPaint( 0, nStart, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left );
+ }
+ }
+
+ pDocSh->UpdateOle(GetViewData());
+ if( !pDocSh->IsReadOnly() )
+ aModificator.SetDocumentModified();
+ }
+
+ if ( !bWidth )
+ return;
+
+ ScModelObj* pModelObj = pDocSh->GetModel();
+
+ if (!HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
+ return;
+
+ ScRangeList aChangeRanges;
+ for (const SCTAB& nTab : aMarkData)
+ {
+ for (const sc::ColRowSpan & rRange : rRanges)
+ {
+ SCCOL nStartCol = rRange.mnStart;
+ SCCOL nEndCol = rRange.mnEnd;
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ aChangeRanges.push_back( ScRange( nCol, 0, nTab ) );
+ }
+ }
+ }
+ HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "column-resize");
+}
+
+// column width/row height (via marked range)
+
+void ScViewFunc::SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips )
+{
+ ScMarkData& rMark = GetViewData().GetMarkData();
+
+ rMark.MarkToMulti();
+ if (!rMark.IsMultiMarked())
+ {
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ const ScRange aMarkRange( nCol, nRow, nTab);
+ DoneBlockMode();
+ InitOwnBlockMode( aMarkRange );
+ rMark.SetMultiMarkArea( aMarkRange );
+ MarkDataChanged();
+ }
+
+ std::vector<sc::ColRowSpan> aRanges =
+ bWidth ? rMark.GetMarkedColSpans() : rMark.GetMarkedRowSpans();
+
+ SetWidthOrHeight(bWidth, aRanges, eMode, nSizeTwips);
+
+ rMark.MarkToSimple();
+}
+
+void ScViewFunc::ModifyCellSize( ScDirection eDir, bool bOptimal )
+{
+ ScModule* pScMod = SC_MOD();
+ bool bAnyEdit = pScMod->IsInputMode();
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+
+ bool bAllowed, bOnlyMatrix;
+ if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
+ bAllowed = rDoc.IsBlockEditable( nTab, nCol,0, nCol,rDoc.MaxRow(), &bOnlyMatrix );
+ else
+ bAllowed = rDoc.IsBlockEditable( nTab, 0,nRow, rDoc.MaxCol(), nRow, &bOnlyMatrix );
+ if ( !bAllowed && !bOnlyMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ HideAllCursors();
+
+ //! step size adjustable
+ // step size is also minimum
+ constexpr sal_uInt16 nStepX = STD_COL_WIDTH / 5;
+ const sal_uInt16 nStepY = rDoc.GetSheetOptimalMinRowHeight(nTab);
+
+ sal_uInt16 nWidth = rDoc.GetColWidth( nCol, nTab );
+ sal_uInt16 nHeight = rDoc.GetRowHeight( nRow, nTab );
+ std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(0,0));
+ if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
+ {
+ if (bOptimal) // width of this single cell
+ {
+ if ( bAnyEdit )
+ {
+ // when editing the actual entered width
+ ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
+ if (pHdl)
+ {
+ tools::Long nEdit = pHdl->GetTextSize().Width(); // in 0.01 mm
+
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ const SvxMarginItem& rMItem = pPattern->GetItem(ATTR_MARGIN);
+ sal_uInt16 nMargin = rMItem.GetLeftMargin() + rMItem.GetRightMargin();
+ if ( pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Left )
+ nMargin = sal::static_int_cast<sal_uInt16>(
+ nMargin + pPattern->GetItem(ATTR_INDENT).GetValue() );
+
+ nWidth = std::round(o3tl::convert(nEdit * pDocSh->GetOutputFactor(),
+ o3tl::Length::mm100, o3tl::Length::twip))
+ + nMargin + STD_EXTRA_WIDTH;
+ }
+ }
+ else
+ {
+ double nPPTX = GetViewData().GetPPTX();
+ double nPPTY = GetViewData().GetPPTY();
+ Fraction aZoomX = GetViewData().GetZoomX();
+ Fraction aZoomY = GetViewData().GetZoomY();
+
+ ScSizeDeviceProvider aProv(pDocSh);
+ if (aProv.IsPrinter())
+ {
+ nPPTX = aProv.GetPPTX();
+ nPPTY = aProv.GetPPTY();
+ aZoomX = aZoomY = Fraction( 1, 1 );
+ }
+
+ tools::Long nPixel = rDoc.GetNeededSize( nCol, nRow, nTab, aProv.GetDevice(),
+ nPPTX, nPPTY, aZoomX, aZoomY, true );
+ sal_uInt16 nTwips = static_cast<sal_uInt16>( nPixel / nPPTX );
+ if (nTwips != 0)
+ nWidth = nTwips + STD_EXTRA_WIDTH;
+ else
+ nWidth = STD_COL_WIDTH;
+ }
+ }
+ else // increment / decrement
+ {
+ if ( eDir == DIR_RIGHT )
+ nWidth = sal::static_int_cast<sal_uInt16>( nWidth + nStepX );
+ else if ( nWidth > nStepX )
+ nWidth = sal::static_int_cast<sal_uInt16>( nWidth - nStepX );
+ if ( nWidth < nStepX ) nWidth = nStepX;
+ if ( nWidth > MAX_COL_WIDTH ) nWidth = MAX_COL_WIDTH;
+ }
+ aRange[0].mnStart = nCol;
+ aRange[0].mnEnd = nCol;
+ SetWidthOrHeight(true, aRange, SC_SIZE_DIRECT, nWidth);
+
+ // adjust height of this row if width demands/allows this
+
+ if (!bAnyEdit)
+ {
+ const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
+ bool bNeedHeight =
+ pPattern->GetItem( ATTR_LINEBREAK ).GetValue() ||
+ pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block;
+ if (bNeedHeight)
+ AdjustRowHeight( nRow, nRow, true );
+ }
+ }
+ else
+ {
+ ScSizeMode eMode;
+ if (bOptimal)
+ {
+ eMode = SC_SIZE_OPTIMAL;
+ nHeight = 0;
+ }
+ else
+ {
+ eMode = SC_SIZE_DIRECT;
+ if ( eDir == DIR_BOTTOM )
+ nHeight = sal::static_int_cast<sal_uInt16>( nHeight + nStepY );
+ else if ( nHeight > nStepY )
+ nHeight = sal::static_int_cast<sal_uInt16>( nHeight - nStepY );
+ if ( nHeight < nStepY ) nHeight = nStepY;
+ if ( nHeight > MAX_ROW_HEIGHT ) nHeight = MAX_ROW_HEIGHT;
+ }
+ aRange[0].mnStart = nRow;
+ aRange[0].mnEnd = nRow;
+ SetWidthOrHeight(false, aRange, eMode, nHeight);
+ }
+
+ if ( bAnyEdit )
+ {
+ UpdateEditView();
+ if ( rDoc.HasAttrib( nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::NeedHeight ) )
+ {
+ ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
+ if (pHdl)
+ pHdl->SetModified(); // so that the height is adjusted with Enter
+ }
+ }
+
+ ShowAllCursors();
+}
+
+void ScViewFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
+{
+ if (nTab == TABLEID_DOC)
+ return;
+
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+ bool bUndo(rDoc.IsUndoEnabled());
+
+ // modifying several tabs is handled here
+
+ if (bUndo)
+ {
+ OUString aUndo = ScResId( STR_UNDO_PROTECT_TAB );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+ }
+
+ for (const auto& rTab : rMark)
+ {
+ rFunc.ProtectSheet(rTab, rProtect);
+ }
+
+ if (bUndo)
+ pDocSh->GetUndoManager()->LeaveListAction();
+
+ UpdateLayerLocks(); //! broadcast to all views
+}
+
+void ScViewFunc::ProtectDoc( const OUString& rPassword )
+{
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+
+ rFunc.Protect( TABLEID_DOC, rPassword );
+
+ UpdateLayerLocks(); //! broadcast to all views
+}
+
+bool ScViewFunc::Unprotect( SCTAB nTab, const OUString& rPassword )
+{
+ ScMarkData& rMark = GetViewData().GetMarkData();
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ ScDocFunc &rFunc = pDocSh->GetDocFunc();
+ bool bChanged = false;
+ bool bUndo (rDoc.IsUndoEnabled());
+
+ if ( nTab == TABLEID_DOC || rMark.GetSelectCount() <= 1 )
+ {
+ bChanged = rFunc.Unprotect( nTab, rPassword, false );
+ if (bChanged && nTab != TABLEID_DOC)
+ SetTabProtectionSymbol(nTab, false);
+ }
+ else
+ {
+ // modifying several tabs is handled here
+
+ if (bUndo)
+ {
+ OUString aUndo = ScResId( STR_UNDO_UNPROTECT_TAB );
+ pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
+ }
+
+ for (const auto& rTab : rMark)
+ {
+ if ( rFunc.Unprotect( rTab, rPassword, false ) )
+ {
+ bChanged = true;
+ SetTabProtectionSymbol( rTab, false);
+ }
+ }
+
+ if (bUndo)
+ pDocSh->GetUndoManager()->LeaveListAction();
+ }
+
+ if (bChanged)
+ UpdateLayerLocks(); //! broadcast to all views
+
+ return bChanged;
+}
+
+void ScViewFunc::SetNoteText( const ScAddress& rPos, const OUString& rNoteText )
+{
+ GetViewData().GetDocShell()->GetDocFunc().SetNoteText( rPos, rNoteText, false );
+}
+
+void ScViewFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate )
+{
+ GetViewData().GetDocShell()->GetDocFunc().ReplaceNote( rPos, rNoteText, pAuthor, pDate, false );
+}
+
+void ScViewFunc::SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ sal_uInt32 nNumberFormat = 0;
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pNumberFormatter = rDoc.GetFormatTable();
+ LanguageType eLanguage = ScGlobal::eLnge;
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+
+ // always take language from cursor position, even if there is a selection
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(),
+ rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pNumberFormatter->GetEntry( nCurrentNumberFormat );
+ if (pEntry)
+ eLanguage = pEntry->GetLanguage(); // else keep ScGlobal::eLnge
+
+ nNumberFormat = pNumberFormatter->GetStandardFormat( nFormatType, eLanguage ) + nAdd;
+
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
+ // ATTR_LANGUAGE_FORMAT not
+ ApplySelectionPattern( aNewAttrs );
+}
+
+void ScViewFunc::SetNumFmtByStr( const OUString& rCode )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScViewData& rViewData = GetViewData();
+ ScDocument& rDoc = rViewData.GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ // language always from cursor position
+
+ sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
+ rViewData.GetTabNo());
+ const SvNumberformat* pEntry = pFormatter->GetEntry( nCurrentNumberFormat );
+ LanguageType eLanguage = pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge;
+
+ // determine index for String
+
+ bool bOk = true;
+ sal_uInt32 nNumberFormat = pFormatter->GetEntryKey( rCode, eLanguage );
+ if ( nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ // enter new
+
+ OUString aFormat = rCode; // will be changed
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nType = SvNumFormatType::ALL; //! ???
+ bOk = pFormatter->PutEntry( aFormat, nErrPos, nType, nNumberFormat, eLanguage );
+ }
+
+ if ( bOk ) // valid format?
+ {
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
+ rSet.Put( SvxLanguageItem( eLanguage, ATTR_LANGUAGE_FORMAT ) );
+ ApplySelectionPattern( aNewAttrs );
+ }
+
+ //! else return error / issue warning ???
+}
+
+void ScViewFunc::ChangeNumFmtDecimals( bool bIncrement )
+{
+ // not editable because of matrix only? attribute OK nonetheless
+ bool bOnlyNotBecauseOfMatrix;
+ if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
+ {
+ ErrorMessage(STR_PROTECTIONERR);
+ return;
+ }
+
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+
+ SCCOL nCol = GetViewData().GetCurX();
+ SCROW nRow = GetViewData().GetCurY();
+ SCTAB nTab = GetViewData().GetTabNo();
+
+ sal_uInt32 nOldFormat = rDoc.GetNumberFormat( nCol, nRow, nTab );
+ const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
+ if (!pOldEntry)
+ {
+ OSL_FAIL("numberformat not found !!!");
+ return;
+ }
+
+ // what have we got here?
+
+ sal_uInt32 nNewFormat = nOldFormat;
+ bool bError = false;
+
+ LanguageType eLanguage = pOldEntry->GetLanguage();
+ bool bThousand, bNegRed;
+ sal_uInt16 nPrecision, nLeading;
+ pOldEntry->GetFormatSpecialInfo( bThousand, bNegRed, nPrecision, nLeading );
+
+ SvNumFormatType nOldType = pOldEntry->GetType();
+ if ( SvNumFormatType::ALL == ( nOldType & (
+ SvNumFormatType::NUMBER | SvNumFormatType::CURRENCY | SvNumFormatType::PERCENT | SvNumFormatType::SCIENTIFIC | SvNumFormatType::TIME ) ) )
+ {
+ // date, fraction, logical, text can not be changed
+ bError = true;
+ }
+
+ //! SvNumberformat has a Member bStandard, but doesn't disclose it
+ bool bWasStandard = ( nOldFormat == pFormatter->GetStandardIndex( eLanguage ) );
+ OUString sExponentialStandardFormat = "";
+ if (bWasStandard)
+ {
+ // with "Standard" the decimal places depend on cell content
+ // 0 if empty or text -> no decimal places
+ double nVal = rDoc.GetValue( ScAddress( nCol, nRow, nTab ) );
+
+ // the ways of the Numberformatters are unfathomable, so try:
+ OUString aOut;
+ const Color* pCol;
+ const_cast<SvNumberformat*>(pOldEntry)->GetOutputString( nVal, aOut, &pCol );
+
+ nPrecision = 0;
+ // 'E' for exponential is fixed in Numberformatter
+ sal_Int32 nIndexE = aOut.indexOf('E');
+ if ( nIndexE >= 0 )
+ {
+ sExponentialStandardFormat = aOut.copy( nIndexE ).replace( '-', '+' );
+ for ( sal_Int32 i=1 ; i<sExponentialStandardFormat.getLength() ; i++ )
+ {
+ if ( sExponentialStandardFormat[i] >= '1' && sExponentialStandardFormat[i] <= '9' )
+ sExponentialStandardFormat = sExponentialStandardFormat.replaceAt( i, 1, u"0" );
+ }
+ aOut = aOut.copy( 0, nIndexE ); // remove exponential part
+ }
+ OUString aDecSep( pFormatter->GetFormatDecimalSep( nOldFormat ) );
+ sal_Int32 nPos = aOut.indexOf( aDecSep );
+ if ( nPos >= 0 )
+ nPrecision = aOut.getLength() - nPos - aDecSep.getLength();
+ // else keep 0
+ }
+ else
+ {
+ if ( (nOldType & SvNumFormatType::SCIENTIFIC) && !bThousand &&
+ (pOldEntry->GetFormatIntegerDigits()%3 == 0) && pOldEntry->GetFormatIntegerDigits() > 0 )
+ bThousand = true;
+ }
+
+ if (!bError)
+ {
+ if (bIncrement)
+ {
+ if (nPrecision<20)
+ ++nPrecision; // increment
+ else
+ bError = true; // 20 is maximum
+ }
+ else
+ {
+ if (nPrecision)
+ --nPrecision; // decrement
+ else
+ bError = true; // 0 is minimum
+ }
+ }
+
+ if (!bError)
+ {
+ OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLanguage,
+ bThousand, bNegRed,
+ nPrecision, nLeading)
+ + sExponentialStandardFormat;
+
+ nNewFormat = pFormatter->GetEntryKey( aNewPicture, eLanguage );
+ if ( nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nNewType = SvNumFormatType::ALL;
+ bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos,
+ nNewType, nNewFormat, eLanguage );
+ OSL_ENSURE( bOk, "incorrect numberformat generated" );
+ if (!bOk)
+ bError = true;
+ }
+ }
+
+ if (!bError)
+ {
+ ScPatternAttr aNewAttrs( rDoc.GetPool() );
+ SfxItemSet& rSet = aNewAttrs.GetItemSet();
+ rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
+ // ATTR_LANGUAGE_FORMAT not
+ ApplySelectionPattern( aNewAttrs );
+ }
+}
+
+void ScViewFunc::ChangeIndent( bool bIncrement )
+{
+ ScViewData& rViewData = GetViewData();
+ ScDocShell* pDocSh = rViewData.GetDocShell();
+ ScMarkData& rMark = rViewData.GetMarkData();
+
+ ScMarkData aWorkMark = rMark;
+ ScViewUtil::UnmarkFiltered( aWorkMark, pDocSh->GetDocument() );
+ aWorkMark.MarkToMulti();
+ if (!aWorkMark.IsMultiMarked())
+ {
+ SCCOL nCol = rViewData.GetCurX();
+ SCROW nRow = rViewData.GetCurY();
+ SCTAB nTab = rViewData.GetTabNo();
+ aWorkMark.SetMultiMarkArea( ScRange(nCol,nRow,nTab) );
+ }
+
+ bool bSuccess = pDocSh->GetDocFunc().ChangeIndent( aWorkMark, bIncrement, false );
+ if (bSuccess)
+ {
+ pDocSh->UpdateOle(rViewData);
+ StartFormatArea();
+
+ // stuff for sidebar panels
+ SfxBindings& rBindings = GetViewData().GetBindings();
+ rBindings.Invalidate( SID_H_ALIGNCELL );
+ rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
+ }
+}
+
+bool ScViewFunc::InsertName( const OUString& rName, const OUString& rSymbol,
+ const OUString& rType )
+{
+ // Type = P,R,C,F (and combinations)
+ //! undo...
+
+ bool bOk = false;
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ ScDocument& rDoc = pDocSh->GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ ScRangeName* pList = rDoc.GetRangeName();
+
+ ScRangeData::Type nType = ScRangeData::Type::Name;
+ auto pNewEntry = std::make_unique<ScRangeData>(
+ rDoc, rName, rSymbol, ScAddress( GetViewData().GetCurX(),
+ GetViewData().GetCurY(), nTab), nType );
+ OUString aUpType = rType.toAsciiUpperCase();
+ if ( aUpType.indexOf( 'P' ) != -1 )
+ nType |= ScRangeData::Type::PrintArea;
+ if ( aUpType.indexOf( 'R' ) != -1 )
+ nType |= ScRangeData::Type::RowHeader;
+ if ( aUpType.indexOf( 'C' ) != -1 )
+ nType |= ScRangeData::Type::ColHeader;
+ if ( aUpType.indexOf( 'F' ) != -1 )
+ nType |= ScRangeData::Type::Criteria;
+ pNewEntry->AddType(nType);
+
+ if ( pNewEntry->GetErrCode() == FormulaError::NONE ) // text valid?
+ {
+ ScDocShellModificator aModificator( *pDocSh );
+
+ rDoc.PreprocessRangeNameUpdate();
+
+ // input available yet? Then remove beforehand (=change)
+ ScRangeData* pData = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
+ if (pData)
+ { // take old Index
+ pNewEntry->SetIndex(pData->GetIndex());
+ pList->erase(*pData);
+ }
+
+ // don't delete, insert took ownership, even on failure!
+ if ( pList->insert( pNewEntry.release() ) )
+ bOk = true;
+
+ rDoc.CompileHybridFormula();
+
+ aModificator.SetDocumentModified();
+ SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
+ }
+
+ return bOk;
+}
+
+void ScViewFunc::CreateNames( CreateNameFlags nFlags )
+{
+ bool bDone = false;
+ ScRange aRange;
+ if ( GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE )
+ bDone = GetViewData().GetDocShell()->GetDocFunc().CreateNames( aRange, nFlags, false );
+
+ if (!bDone)
+ ErrorMessage(STR_CREATENAME_MARKERR);
+}
+
+CreateNameFlags ScViewFunc::GetCreateNameFlags()
+{
+ CreateNameFlags nFlags = CreateNameFlags::NONE;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nDummy;
+ if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nDummy,nEndCol,nEndRow,nDummy) == SC_MARK_SIMPLE)
+ {
+ ScDocument& rDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ bool bOk;
+ SCCOL i;
+ SCROW j;
+
+ bOk = true;
+ SCCOL nFirstCol = nStartCol;
+ SCCOL nLastCol = nEndCol;
+ if (nStartCol+1 < nEndCol) { ++nFirstCol; --nLastCol; }
+ for (i=nFirstCol; i<=nLastCol && bOk; i++)
+ if (!rDoc.HasStringData( i,nStartRow,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Top;
+ else // Bottom only if not Top
+ {
+ bOk = true;
+ for (i=nFirstCol; i<=nLastCol && bOk; i++)
+ if (!rDoc.HasStringData( i,nEndRow,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Bottom;
+ }
+
+ bOk = true;
+ SCROW nFirstRow = nStartRow;
+ SCROW nLastRow = nEndRow;
+ if (nStartRow+1 < nEndRow) { ++nFirstRow; --nLastRow; }
+ for (j=nFirstRow; j<=nLastRow && bOk; j++)
+ if (!rDoc.HasStringData( nStartCol,j,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Left;
+ else // Right only if not Left
+ {
+ bOk = true;
+ for (j=nFirstRow; j<=nLastRow && bOk; j++)
+ if (!rDoc.HasStringData( nEndCol,j,nTab ))
+ bOk = false;
+ if (bOk)
+ nFlags |= CreateNameFlags::Right;
+ }
+ }
+
+ if (nStartCol == nEndCol)
+ nFlags &= ~CreateNameFlags( CreateNameFlags::Left | CreateNameFlags::Right );
+ if (nStartRow == nEndRow)
+ nFlags &= ~CreateNameFlags( CreateNameFlags::Top | CreateNameFlags::Bottom );
+
+ return nFlags;
+}
+
+void ScViewFunc::InsertNameList()
+{
+ ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
+ ScDocShell* pDocSh = GetViewData().GetDocShell();
+ if ( pDocSh->GetDocFunc().InsertNameList( aPos, false ) )
+ pDocSh->UpdateOle(GetViewData());
+}
+
+void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr )
+{
+ ScDocShell* pDocShell = GetViewData().GetDocShell();
+ ScRange aMarkRange;
+ if (rSel.IsMultiMarked() )
+ aMarkRange = rSel.GetMultiMarkArea();
+ else
+ aMarkRange = rSel.GetMarkArea();
+
+ bool bSetLines = false;
+ bool bSetAlign = false;
+ if ( pAttr )
+ {
+ const SfxItemSet& rNewSet = pAttr->GetItemSet();
+ bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
+ rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
+ bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
+ }
+
+ sal_uInt16 nExtFlags = 0;
+ if ( bSetLines )
+ nExtFlags |= SC_PF_LINES;
+ if ( bSetAlign )
+ nExtFlags |= SC_PF_WHOLEROWS;
+
+ SCCOL nStartCol = aMarkRange.aStart.Col();
+ SCROW nStartRow = aMarkRange.aStart.Row();
+ SCTAB nStartTab = aMarkRange.aStart.Tab();
+ SCCOL nEndCol = aMarkRange.aEnd.Col();
+ SCROW nEndRow = aMarkRange.aEnd.Row();
+ SCTAB nEndTab = aMarkRange.aEnd.Tab();
+ pDocShell->PostPaint( nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab,
+ PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
+ ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
+ pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewutil.cxx b/sc/source/ui/view/viewutil.cxx
new file mode 100644
index 0000000000..68575ac79f
--- /dev/null
+++ b/sc/source/ui/view/viewutil.cxx
@@ -0,0 +1,426 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <osl/diagnose.h>
+
+#include <viewutil.hxx>
+#include <chgtrack.hxx>
+#include <chgviset.hxx>
+#include <markdata.hxx>
+#include <document.hxx>
+#include <tabvwsh.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <memory>
+
+void ScViewUtil::PutItemScript( SfxItemSet& rShellSet, const SfxItemSet& rCoreSet,
+ sal_uInt16 nWhichId, SvtScriptType nScript )
+{
+ // take the effective item from rCoreSet according to nScript
+ // and put in rShellSet under the (base) nWhichId
+
+ SfxItemPool& rPool = *rShellSet.GetPool();
+ SvxScriptSetItem aSetItem( rPool.GetSlotId(nWhichId), rPool );
+ // use PutExtended with eDefaultAs = SfxItemState::SET, so defaults from rCoreSet
+ // (document pool) are read and put into rShellSet (MessagePool)
+ aSetItem.GetItemSet().PutExtended( rCoreSet, SfxItemState::DONTCARE, SfxItemState::SET );
+ const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScript );
+ if (pI)
+ {
+ rShellSet.Put( pI->CloneSetWhich(nWhichId) );
+ }
+ else
+ rShellSet.InvalidateItem( nWhichId );
+}
+
+LanguageType ScViewUtil::GetEffLanguage( ScDocument& rDoc, const ScAddress& rPos )
+{
+ // used for thesaurus
+
+ SvtScriptType nScript = rDoc.GetScriptType(rPos.Col(), rPos.Row(), rPos.Tab());
+ sal_uInt16 nWhich = ( nScript == SvtScriptType::ASIAN ) ? ATTR_CJK_FONT_LANGUAGE :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE : ATTR_FONT_LANGUAGE );
+ const SfxPoolItem* pItem = rDoc.GetAttr( rPos.Col(), rPos.Row(), rPos.Tab(), nWhich);
+ const SvxLanguageItem* pLangIt = dynamic_cast<const SvxLanguageItem*>( pItem );
+ LanguageType eLnge;
+ if (pLangIt)
+ {
+ eLnge = pLangIt->GetValue();
+ if (eLnge == LANGUAGE_DONTKNOW) //! can this happen?
+ {
+ LanguageType eLatin, eCjk, eCtl;
+ rDoc.GetLanguage( eLatin, eCjk, eCtl );
+ eLnge = ( nScript == SvtScriptType::ASIAN ) ? eCjk :
+ ( ( nScript == SvtScriptType::COMPLEX ) ? eCtl : eLatin );
+ }
+ }
+ else
+ eLnge = LANGUAGE_ENGLISH_US;
+ if ( eLnge == LANGUAGE_SYSTEM )
+ eLnge = Application::GetSettings().GetLanguageTag().getLanguageType(); // never use SYSTEM for spelling
+
+ return eLnge;
+}
+
+TransliterationFlags ScViewUtil::GetTransliterationType( sal_uInt16 nSlotID )
+{
+ TransliterationFlags nType = TransliterationFlags::NONE;
+ switch ( nSlotID )
+ {
+ case SID_TRANSLITERATE_SENTENCE_CASE:
+ nType = TransliterationFlags::SENTENCE_CASE;
+ break;
+ case SID_TRANSLITERATE_TITLE_CASE:
+ nType = TransliterationFlags::TITLE_CASE;
+ break;
+ case SID_TRANSLITERATE_TOGGLE_CASE:
+ nType = TransliterationFlags::TOGGLE_CASE;
+ break;
+ case SID_TRANSLITERATE_UPPER:
+ nType = TransliterationFlags::LOWERCASE_UPPERCASE;
+ break;
+ case SID_TRANSLITERATE_LOWER:
+ nType = TransliterationFlags::UPPERCASE_LOWERCASE;
+ break;
+ case SID_TRANSLITERATE_HALFWIDTH:
+ nType = TransliterationFlags::FULLWIDTH_HALFWIDTH;
+ break;
+ case SID_TRANSLITERATE_FULLWIDTH:
+ nType = TransliterationFlags::HALFWIDTH_FULLWIDTH;
+ break;
+ case SID_TRANSLITERATE_HIRAGANA:
+ nType = TransliterationFlags::KATAKANA_HIRAGANA;
+ break;
+ case SID_TRANSLITERATE_KATAKANA:
+ nType = TransliterationFlags::HIRAGANA_KATAKANA;
+ break;
+ }
+ return nType;
+}
+
+bool ScViewUtil::IsActionShown( const ScChangeAction& rAction,
+ const ScChangeViewSettings& rSettings,
+ ScDocument& rDocument )
+{
+ // discarded are displayed as inverted accepted action, because of this
+ // order of ShowRejected/ShowAccepted is important
+
+ if ( !rSettings.IsShowRejected() && rAction.IsRejecting() )
+ return false;
+
+ if ( !rSettings.IsShowAccepted() && rAction.IsAccepted() && !rAction.IsRejecting() )
+ return false;
+
+ if ( rSettings.HasAuthor() && rAction.GetUser() != rSettings.GetTheAuthorToShow() )
+ return false;
+
+ if ( rSettings.HasComment() )
+ {
+ OUString aTmp = rAction.GetDescription(rDocument);
+ OUString aComStr = rAction.GetComment() + " (" + aTmp + ")";
+
+ if(!rSettings.IsValidComment(&aComStr))
+ return false;
+ }
+
+ if ( rSettings.HasRange() )
+ if ( !rSettings.GetTheRangeList().Intersects( rAction.GetBigRange().MakeRange( rDocument ) ) )
+ return false;
+
+ if (rSettings.HasDate() && rSettings.GetTheDateMode() != SvxRedlinDateMode::NONE)
+ {
+ DateTime aDateTime = rAction.GetDateTime();
+ const DateTime& rFirst = rSettings.GetTheFirstDateTime();
+ const DateTime& rLast = rSettings.GetTheLastDateTime();
+ switch ( rSettings.GetTheDateMode() )
+ { // corresponds with ScHighlightChgDlg::OKBtnHdl
+ case SvxRedlinDateMode::BEFORE:
+ if ( aDateTime > rFirst )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::SINCE:
+ if ( aDateTime < rFirst )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::EQUAL:
+ case SvxRedlinDateMode::BETWEEN:
+ if ( aDateTime < rFirst || aDateTime > rLast )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::NOTEQUAL:
+ if ( aDateTime >= rFirst && aDateTime <= rLast )
+ return false;
+ break;
+
+ case SvxRedlinDateMode::SAVE:
+ {
+ ScChangeTrack* pTrack = rDocument.GetChangeTrack();
+ if ( !pTrack || pTrack->GetLastSavedActionNumber() >=
+ rAction.GetActionNumber() )
+ return false;
+ }
+ break;
+
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ if ( rSettings.HasActionRange() )
+ {
+ sal_uLong nAction = rAction.GetActionNumber();
+ sal_uLong nFirstAction;
+ sal_uLong nLastAction;
+ rSettings.GetTheActionRange( nFirstAction, nLastAction );
+ if ( nAction < nFirstAction || nAction > nLastAction )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ScViewUtil::UnmarkFiltered( ScMarkData& rMark, const ScDocument& rDoc )
+{
+ rMark.MarkToMulti();
+
+ const ScRange& aMultiArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aMultiArea.aStart.Col();
+ SCROW nStartRow = aMultiArea.aStart.Row();
+ SCCOL nEndCol = aMultiArea.aEnd.Col();
+ SCROW nEndRow = aMultiArea.aEnd.Row();
+
+ bool bChanged = false;
+ for (const SCTAB& nTab : rMark)
+ {
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ SCROW nLastRow = nRow;
+ if (rDoc.RowFiltered(nRow, nTab, nullptr, &nLastRow))
+ {
+ // use nStartCol/nEndCol, so the multi mark area isn't extended to all columns
+ // (visible in repaint for indentation)
+ rMark.SetMultiMarkArea(
+ ScRange(nStartCol, nRow, nTab, nEndCol, nLastRow, nTab), false);
+ bChanged = true;
+ nRow = nLastRow;
+ }
+ }
+ }
+
+ if ( bChanged && !rMark.HasAnyMultiMarks() )
+ rMark.ResetMark();
+
+ rMark.MarkToSimple();
+}
+
+bool ScViewUtil::FitToUnfilteredRows( ScRange & rRange, const ScDocument& rDoc, size_t nRows )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ bool bOneTabOnly = (nTab == rRange.aEnd.Tab());
+ // Always fit the range on its first sheet.
+ OSL_ENSURE( bOneTabOnly, "ScViewUtil::ExtendToUnfilteredRows: works only on one sheet");
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nLastRow = rDoc.LastNonFilteredRow(nStartRow, rDoc.MaxRow(), nTab);
+ if (rDoc.ValidRow(nLastRow))
+ rRange.aEnd.SetRow(nLastRow);
+ SCROW nCount = rDoc.CountNonFilteredRows(nStartRow, rDoc.MaxRow(), nTab);
+ return static_cast<size_t>(nCount) == nRows && bOneTabOnly;
+}
+
+bool ScViewUtil::HasFiltered( const ScRange& rRange, const ScDocument& rDoc )
+{
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for (SCTAB nTab=rRange.aStart.Tab(); nTab<=rRange.aEnd.Tab(); nTab++)
+ {
+ if (rDoc.HasFilteredRows(nStartRow, nEndRow, nTab))
+ return true;
+ }
+
+ return false;
+}
+
+void ScViewUtil::HideDisabledSlot( SfxItemSet& rSet, SfxBindings& rBindings, sal_uInt16 nSlotId )
+{
+ SvtCTLOptions aCTLOptions;
+ bool bEnabled = true;
+
+ switch( nSlotId )
+ {
+ case SID_CHINESE_CONVERSION:
+ case SID_HANGUL_HANJA_CONVERSION:
+ bEnabled = SvtCJKOptions::IsAnyEnabled();
+ break;
+
+ case SID_TRANSLITERATE_HALFWIDTH:
+ case SID_TRANSLITERATE_FULLWIDTH:
+ case SID_TRANSLITERATE_HIRAGANA:
+ case SID_TRANSLITERATE_KATAKANA:
+ bEnabled = SvtCJKOptions::IsChangeCaseMapEnabled();
+ break;
+
+ case SID_INSERT_RLM:
+ case SID_INSERT_LRM:
+ bEnabled = SvtCTLOptions::IsCTLFontEnabled();
+ break;
+
+ default:
+ OSL_FAIL( "ScViewUtil::HideDisabledSlot - unknown slot ID" );
+ return;
+ }
+
+ rBindings.SetVisibleState( nSlotId, bEnabled );
+ if( !bEnabled )
+ rSet.DisableItem( nSlotId );
+}
+
+void ScViewUtil::ExecuteCharMap(const SvxFontItem& rOldFont,
+ const ScTabViewShell& rShell)
+{
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ SfxViewFrame& rFrame = rShell.GetViewFrame();
+ SfxAllItemSet aSet( rFrame.GetObjectShell()->GetPool() );
+ aSet.Put( SfxBoolItem( FN_PARAM_1, false ) );
+ aSet.Put( SvxFontItem( rOldFont.GetFamily(), rOldFont.GetFamilyName(), rOldFont.GetStyleName(), rOldFont.GetPitch(), rOldFont.GetCharSet(), aSet.GetPool()->GetWhich( SID_ATTR_CHAR_FONT ) ) );
+ auto xFrame = rFrame.GetFrame().GetFrameInterface();
+ ScopedVclPtr<SfxAbstractDialog> pDlg(pFact->CreateCharMapDialog(rShell.GetFrameWeld(), aSet, xFrame));
+ pDlg->Execute();
+}
+
+bool ScViewUtil::IsFullScreen( const SfxViewShell& rViewShell )
+{
+ SfxBindings& rBindings = rViewShell.GetViewFrame().GetBindings();
+ std::unique_ptr<SfxBoolItem> pItem;
+ bool bIsFullScreen = false;
+
+ if (rBindings.QueryState( SID_WIN_FULLSCREEN, pItem ) >= SfxItemState::DEFAULT)
+ bIsFullScreen = pItem->GetValue();
+
+ return bIsFullScreen;
+}
+
+void ScViewUtil::SetFullScreen( const SfxViewShell& rViewShell, bool bSet )
+{
+ if( IsFullScreen( rViewShell ) != bSet )
+ {
+ SfxBoolItem aItem( SID_WIN_FULLSCREEN, bSet );
+ rViewShell.GetDispatcher()->ExecuteList(SID_WIN_FULLSCREEN,
+ SfxCallMode::RECORD, { &aItem });
+ }
+}
+
+ScUpdateRect::ScUpdateRect( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
+ : nNewStartX(0)
+ , nNewStartY(0)
+ , nNewEndX(0)
+ , nNewEndY(0)
+{
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ nOldStartX = nX1;
+ nOldStartY = nY1;
+ nOldEndX = nX2;
+ nOldEndY = nY2;
+}
+
+void ScUpdateRect::SetNew( SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2 )
+{
+ PutInOrder( nX1, nX2 );
+ PutInOrder( nY1, nY2 );
+
+ nNewStartX = nX1;
+ nNewStartY = nY1;
+ nNewEndX = nX2;
+ nNewEndY = nY2;
+}
+
+bool ScUpdateRect::GetDiff( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 )
+{
+ if ( nNewStartX == nOldStartX && nNewEndX == nOldEndX &&
+ nNewStartY == nOldStartY && nNewEndY == nOldEndY )
+ {
+ rX1 = nNewStartX;
+ rY1 = nNewStartY;
+ rX2 = nNewStartX;
+ rY2 = nNewStartY;
+ return false;
+ }
+
+ rX1 = std::min(nNewStartX,nOldStartX);
+ rY1 = std::min(nNewStartY,nOldStartY);
+ rX2 = std::max(nNewEndX,nOldEndX);
+ rY2 = std::max(nNewEndY,nOldEndY);
+
+ if ( nNewStartX == nOldStartX && nNewEndX == nOldEndX )
+ {
+ if ( nNewStartY == nOldStartY )
+ {
+ rY1 = std::min( nNewEndY, nOldEndY );
+ rY2 = std::max( nNewEndY, nOldEndY );
+ }
+ else if ( nNewEndY == nOldEndY )
+ {
+ rY1 = std::min( nNewStartY, nOldStartY );
+ rY2 = std::max( nNewStartY, nOldStartY );
+ }
+ }
+ else if ( nNewStartY == nOldStartY && nNewEndY == nOldEndY )
+ {
+ if ( nNewStartX == nOldStartX )
+ {
+ rX1 = std::min( nNewEndX, nOldEndX );
+ rX2 = std::max( nNewEndX, nOldEndX );
+ }
+ else if ( nNewEndX == nOldEndX )
+ {
+ rX1 = std::min( nNewStartX, nOldStartX );
+ rX2 = std::max( nNewStartX, nOldStartX );
+ }
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/waitoff.cxx b/sc/source/ui/view/waitoff.cxx
new file mode 100644
index 0000000000..94b2728bc0
--- /dev/null
+++ b/sc/source/ui/view/waitoff.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/window.hxx>
+
+#include <waitoff.hxx>
+
+ScWaitCursorOff::ScWaitCursorOff( vcl::Window* pWinP )
+ :
+ pWin( pWinP ),
+ nWaiters(0)
+{
+ if ( pWin )
+ {
+ while ( pWin->IsWait() )
+ {
+ nWaiters++;
+ pWin->LeaveWait();
+ }
+ }
+}
+
+ScWaitCursorOff::~ScWaitCursorOff()
+{
+ if ( pWin )
+ {
+ while ( nWaiters )
+ {
+ nWaiters--;
+ pWin->EnterWait();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/xmlsource/xmlsourcedlg.cxx b/sc/source/ui/xmlsource/xmlsourcedlg.cxx
new file mode 100644
index 0000000000..40d52d9a4d
--- /dev/null
+++ b/sc/source/ui/xmlsource/xmlsourcedlg.cxx
@@ -0,0 +1,625 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <xmlsourcedlg.hxx>
+#include <bitmaps.hlst>
+#include <document.hxx>
+#include <orcusfilters.hxx>
+#include <filter.hxx>
+#include <reffact.hxx>
+#include <tabvwsh.hxx>
+
+#include <tools/urlobj.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+
+using namespace com::sun::star;
+
+namespace {
+
+bool isAttribute(const weld::TreeView& rControl, const weld::TreeIter& rEntry)
+{
+ const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rControl, rEntry);
+ if (!pUserData)
+ return false;
+
+ return pUserData->meType == ScOrcusXMLTreeParam::Attribute;
+}
+
+OUString getXPath(
+ const weld::TreeView& rTree, const weld::TreeIter& rEntry, std::vector<size_t>& rNamespaces)
+{
+ OUStringBuffer aBuf;
+ std::unique_ptr<weld::TreeIter> xEntry(rTree.make_iterator(&rEntry));
+ do
+ {
+ // Collect used namespace.
+ const ScOrcusXMLTreeParam::EntryData* pData = ScOrcusXMLTreeParam::getUserData(rTree, *xEntry);
+ if (pData)
+ rNamespaces.push_back(pData->mnNamespaceID);
+
+ // element separator is '/' whereas attribute separator is '/@' in xpath.
+ std::u16string_view sSeparator;
+ if (isAttribute(rTree, *xEntry))
+ sSeparator = u"/@";
+ else
+ sSeparator = u"/";
+ aBuf.insert(0, sSeparator + rTree.get_text(*xEntry, 0));
+ }
+ while (rTree.iter_parent(*xEntry));
+
+ return aBuf.makeStringAndClear();
+}
+
+}
+
+ScXMLSourceDlg::ScXMLSourceDlg(
+ SfxBindings* pB, SfxChildWindow* pCW, weld::Window* pParent, ScDocument* pDoc)
+ : ScAnyRefDlgController(pB, pCW, pParent, "modules/scalc/ui/xmlsourcedialog.ui", "XMLSourceDialog")
+ , mpDoc(pDoc)
+ , mbDlgLostFocus(false)
+ , mxBtnSelectSource(m_xBuilder->weld_button("selectsource"))
+ , mxFtSourceFile(m_xBuilder->weld_label("sourcefile"))
+ , mxMapGrid(m_xBuilder->weld_container("mapgrid"))
+ , mxLbTree(m_xBuilder->weld_tree_view("tree"))
+ , mxRefEdit(new formula::RefEdit(m_xBuilder->weld_entry("edit")))
+ , mxRefBtn(new formula::RefButton(m_xBuilder->weld_button("ref")))
+ , mxBtnOk(m_xBuilder->weld_button("ok"))
+ , mxBtnCancel(m_xBuilder->weld_button("cancel"))
+ , maCustomCompare(*mxLbTree)
+ , maCellLinks(maCustomCompare)
+ , maRangeLinks(maCustomCompare)
+{
+ mxLbTree->set_size_request(mxLbTree->get_approximate_digit_width() * 40,
+ mxLbTree->get_height_rows(15));
+ mxLbTree->set_selection_mode(SelectionMode::Multiple);
+ mxRefEdit->SetReferences(this, nullptr);
+ mxRefBtn->SetReferences(this, mxRefEdit.get());
+
+ mpActiveEdit = mxRefEdit.get();
+
+ maXMLParam.maImgElementDefault = RID_BMP_ELEMENT_DEFAULT;
+ maXMLParam.maImgElementRepeat = RID_BMP_ELEMENT_REPEAT;
+ maXMLParam.maImgAttribute = RID_BMP_ELEMENT_ATTRIBUTE;
+
+ Link<weld::Button&,void> aBtnHdl = LINK(this, ScXMLSourceDlg, BtnPressedHdl);
+ mxBtnSelectSource->connect_clicked(aBtnHdl);
+ mxBtnOk->connect_clicked(aBtnHdl);
+ mxBtnCancel->connect_clicked(aBtnHdl);
+
+ mxLbTree->connect_changed(LINK(this, ScXMLSourceDlg, TreeItemSelectHdl));
+
+ Link<formula::RefEdit&,void> aLink = LINK(this, ScXMLSourceDlg, RefModifiedHdl);
+ mxRefEdit->SetModifyHdl(aLink);
+
+ mxBtnOk->set_sensitive(false);
+
+ SetNonLinkable();
+ mxBtnSelectSource->grab_focus(); // Initial focus is on the select source button.
+}
+
+ScXMLSourceDlg::~ScXMLSourceDlg()
+{
+}
+
+bool ScXMLSourceDlg::IsRefInputMode() const
+{
+ return mpActiveEdit != nullptr && mpActiveEdit->GetWidget()->get_sensitive();
+}
+
+void ScXMLSourceDlg::SetReference(const ScRange& rRange, ScDocument& rDoc)
+{
+ if (!mpActiveEdit)
+ return;
+
+ if (rRange.aStart != rRange.aEnd)
+ RefInputStart(mpActiveEdit);
+
+ OUString aStr(rRange.aStart.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()));
+ mpActiveEdit->SetRefString(aStr);
+
+ RefEditModified();
+}
+
+void ScXMLSourceDlg::Deactivate()
+{
+ mbDlgLostFocus = true;
+}
+
+void ScXMLSourceDlg::SetActive()
+{
+ if (mbDlgLostFocus)
+ {
+ mbDlgLostFocus = false;
+ if (mpActiveEdit)
+ {
+ mpActiveEdit->GrabFocus();
+ }
+ }
+ else
+ {
+ m_xDialog->grab_focus();
+ }
+
+ RefInputDone();
+}
+
+void ScXMLSourceDlg::Close()
+{
+ DoClose(ScXMLSourceDlgWrapper::GetChildWindowId());
+}
+
+void ScXMLSourceDlg::SelectSourceFile()
+{
+ sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, m_xDialog.get());
+ aDlgHelper.SetContext(sfx2::FileDialogHelper::CalcXMLSource);
+
+ uno::Reference<ui::dialogs::XFilePicker3> xFilePicker = aDlgHelper.GetFilePicker();
+
+ // Use the directory of current source file.
+ INetURLObject aURL(maSrcPath);
+ aURL.removeSegment();
+ aURL.removeFinalSlash();
+ OUString aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ xFilePicker->setDisplayDirectory(aPath);
+
+ if (xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK)
+ // File picker dialog cancelled.
+ return;
+
+ uno::Sequence<OUString> aFiles = xFilePicker->getSelectedFiles();
+ if (!aFiles.hasElements())
+ return;
+
+ // There should only be one file returned from the file picker.
+ maSrcPath = aFiles[0];
+ mxFtSourceFile->set_label(maSrcPath);
+ LoadSourceFileStructure(maSrcPath);
+}
+
+void ScXMLSourceDlg::LoadSourceFileStructure(const OUString& rPath)
+{
+ ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters();
+ if (!pOrcus)
+ return;
+
+ mpXMLContext = pOrcus->createXMLContext(*mpDoc, rPath);
+ if (!mpXMLContext)
+ return;
+
+ mpXMLContext->loadXMLStructure(*mxLbTree, maXMLParam);
+}
+
+namespace {
+
+/**
+ * The current entry is the reference entry for a cell link. For a range
+ * link, the reference entry is the shallowest repeat element entry up from
+ * the current entry position. The mapped cell position for a range link is
+ * stored with the reference entry.
+ */
+std::unique_ptr<weld::TreeIter> getReferenceEntry(const weld::TreeView& rTree, const weld::TreeIter& rCurEntry)
+{
+ std::unique_ptr<weld::TreeIter> xParent(rTree.make_iterator(&rCurEntry));
+ bool bParent = rTree.iter_parent(*xParent);
+ std::unique_ptr<weld::TreeIter> xRefEntry;
+ while (bParent)
+ {
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rTree, *xParent);
+ OSL_ASSERT(pUserData);
+ if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
+ {
+ // This is a repeat element - a potential reference entry.
+ xRefEntry = rTree.make_iterator(xParent.get());
+ }
+ bParent = rTree.iter_parent(*xParent);
+ }
+
+ if (xRefEntry)
+ return xRefEntry;
+
+ std::unique_ptr<weld::TreeIter> xCurEntry(rTree.make_iterator(&rCurEntry));
+ return xCurEntry;
+}
+
+}
+
+void ScXMLSourceDlg::TreeItemSelected()
+{
+ std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
+ if (!mxLbTree->get_cursor(xEntry.get()))
+ return;
+
+ mxLbTree->unselect_all();
+ mxLbTree->select(*xEntry);
+
+ mxCurRefEntry = getReferenceEntry(*mxLbTree, *xEntry);
+
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *mxCurRefEntry);
+ OSL_ASSERT(pUserData);
+
+ const ScAddress& rPos = pUserData->maLinkedPos;
+ if (rPos.IsValid())
+ {
+ OUString aStr(rPos.Format(ScRefFlags::ADDR_ABS_3D, mpDoc, mpDoc->GetAddressConvention()));
+ mxRefEdit->SetRefString(aStr);
+ }
+ else
+ mxRefEdit->SetRefString(OUString());
+
+ switch (pUserData->meType)
+ {
+ case ScOrcusXMLTreeParam::Attribute:
+ AttributeSelected(*mxCurRefEntry);
+ break;
+ case ScOrcusXMLTreeParam::ElementDefault:
+ DefaultElementSelected(*mxCurRefEntry);
+ break;
+ case ScOrcusXMLTreeParam::ElementRepeat:
+ RepeatElementSelected(*mxCurRefEntry);
+ break;
+ default:
+ ;
+ }
+}
+
+void ScXMLSourceDlg::DefaultElementSelected(const weld::TreeIter& rEntry)
+{
+ if (mxLbTree->iter_has_child(rEntry))
+ {
+ // Only an element with no child elements (leaf element) can be linked.
+ bool bHasChild = false;
+ std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(&rEntry));
+ (void)mxLbTree->iter_children(*xChild);
+ do
+ {
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *xChild);
+ OSL_ASSERT(pUserData);
+ if (pUserData->meType != ScOrcusXMLTreeParam::Attribute)
+ {
+ // This child is not an attribute. Bail out.
+ bHasChild = true;
+ break;
+ }
+ }
+ while (mxLbTree->iter_next_sibling(*xChild));
+
+ if (bHasChild)
+ {
+ SetNonLinkable();
+ return;
+ }
+ }
+
+ // Check all its parents and make sure non of them are range-linked nor
+ // repeat elements.
+ if (IsParentDirty(&rEntry))
+ {
+ SetNonLinkable();
+ return;
+ }
+
+ SetSingleLinkable();
+}
+
+void ScXMLSourceDlg::RepeatElementSelected(const weld::TreeIter& rEntry)
+{
+ // Check all its parents first.
+
+ if (IsParentDirty(&rEntry))
+ {
+ SetNonLinkable();
+ return;
+ }
+
+ // Check all its child elements / attributes and make sure non of them are
+ // linked.
+
+ if (IsChildrenDirty(&rEntry))
+ {
+ SetNonLinkable();
+ return;
+ }
+
+ if (!mxLbTree->is_selected(rEntry))
+ {
+ // Highlight the entry if not highlighted already. This can happen
+ // when the current entry is a child entry of a repeat element entry.
+ mxLbTree->select(rEntry);
+ }
+
+ SelectAllChildEntries(rEntry);
+ SetRangeLinkable();
+}
+
+void ScXMLSourceDlg::AttributeSelected(const weld::TreeIter& rEntry)
+{
+ // Check all its parent elements and make sure non of them are linked nor
+ // repeat elements. In attribute's case, it's okay to have the immediate
+ // parent element linked (but not range-linked).
+ std::unique_ptr<weld::TreeIter> xParent(mxLbTree->make_iterator(&rEntry));
+ mxLbTree->iter_parent(*xParent);
+
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *xParent);
+ OSL_ASSERT(pUserData);
+ if (pUserData->maLinkedPos.IsValid() && pUserData->mbRangeParent)
+ {
+ // Parent element is range-linked. Bail out.
+ SetNonLinkable();
+ return;
+ }
+
+ if (IsParentDirty(&rEntry))
+ {
+ SetNonLinkable();
+ return;
+ }
+
+ SetSingleLinkable();
+}
+
+void ScXMLSourceDlg::SetNonLinkable()
+{
+ mxMapGrid->set_sensitive(false);
+}
+
+void ScXMLSourceDlg::SetSingleLinkable()
+{
+ mxMapGrid->set_sensitive(true);
+}
+
+void ScXMLSourceDlg::SetRangeLinkable()
+{
+ mxMapGrid->set_sensitive(true);
+}
+
+void ScXMLSourceDlg::SelectAllChildEntries(const weld::TreeIter& rEntry)
+{
+ std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(&rEntry));
+ if (!mxLbTree->iter_children(*xChild))
+ return;
+ do
+ {
+ SelectAllChildEntries(*xChild); // select recursively.
+ mxLbTree->select(*xChild);
+ } while (mxLbTree->iter_next_sibling(*xChild));
+}
+
+bool ScXMLSourceDlg::IsParentDirty(const weld::TreeIter* pEntry) const
+{
+ std::unique_ptr<weld::TreeIter> xParent(mxLbTree->make_iterator(pEntry));
+ if (!mxLbTree->iter_parent(*xParent))
+ return false;
+ do
+ {
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *xParent);
+ assert(pUserData);
+ if (pUserData->maLinkedPos.IsValid())
+ {
+ // This parent is already linked.
+ return true;
+ }
+ }
+ while (mxLbTree->iter_parent(*xParent));
+ return false;
+}
+
+bool ScXMLSourceDlg::IsChildrenDirty(const weld::TreeIter* pEntry) const
+{
+ std::unique_ptr<weld::TreeIter> xChild(mxLbTree->make_iterator(pEntry));
+ if (!mxLbTree->iter_children(*xChild))
+ return false;
+
+ do
+ {
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *xChild);
+ OSL_ASSERT(pUserData);
+ if (pUserData->maLinkedPos.IsValid())
+ // Already linked.
+ return true;
+
+ if (pUserData->meType == ScOrcusXMLTreeParam::ElementDefault)
+ {
+ // Check recursively.
+ if (IsChildrenDirty(xChild.get()))
+ return true;
+ }
+ } while (mxLbTree->iter_next_sibling(*xChild));
+
+ return false;
+}
+
+namespace {
+
+/**
+ * Pick only the leaf elements.
+ */
+void getFieldLinks(
+ ScOrcusImportXMLParam::RangeLink& rRangeLink, std::vector<size_t>& rNamespaces,
+ const weld::TreeView& rTree, const weld::TreeIter& rEntry)
+{
+ OUString aPath = getXPath(rTree, rEntry, rNamespaces);
+ const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(rTree, rEntry);
+
+ if (pUserData)
+ {
+ if (pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat)
+ // nested repeat element automatically becomes a row-group node.
+ rRangeLink.maRowGroups.push_back(
+ OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
+
+ if (pUserData->mbLeafNode && !aPath.isEmpty())
+ // XPath should never be empty anyway, but it won't hurt to check...
+ rRangeLink.maFieldPaths.push_back(OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
+ }
+
+ std::unique_ptr<weld::TreeIter> xChild(rTree.make_iterator(&rEntry));
+
+ if (!rTree.iter_children(*xChild))
+ // No more children. We're done.
+ return;
+
+ do
+ {
+ // Walk recursively.
+ getFieldLinks(rRangeLink, rNamespaces, rTree, *xChild);
+ }
+ while (rTree.iter_next_sibling(*xChild));
+}
+
+void removeDuplicates(std::vector<size_t>& rArray)
+{
+ std::sort(rArray.begin(), rArray.end());
+ std::vector<size_t>::iterator it = std::unique(rArray.begin(), rArray.end());
+ rArray.erase(it, rArray.end());
+}
+
+}
+
+void ScXMLSourceDlg::OkPressed()
+{
+ if (!mpXMLContext)
+ return;
+
+ // Begin import.
+
+ ScOrcusImportXMLParam aParam;
+
+ // Convert single cell links.
+ for (const auto& rEntry : maCellLinks)
+ {
+ OUString aPath = getXPath(*mxLbTree, *rEntry, aParam.maNamespaces);
+ const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *rEntry);
+
+ aParam.maCellLinks.emplace_back(
+ pUserData->maLinkedPos, OUStringToOString(aPath, RTL_TEXTENCODING_UTF8));
+ }
+
+ // Convert range links. For now, an element with range link takes all its
+ // child elements as its fields.
+ for (const auto& rEntry: maRangeLinks)
+ {
+ const ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *rEntry);
+
+ ScOrcusImportXMLParam::RangeLink aRangeLink;
+ aRangeLink.maPos = pUserData->maLinkedPos;
+
+ // Go through all its child elements.
+ getFieldLinks(aRangeLink, aParam.maNamespaces, *mxLbTree, *rEntry);
+
+ // Add the reference entry as a row-group node, which will be used
+ // as a row position increment point.
+ OUString aThisEntry = getXPath(*mxLbTree, *rEntry, aParam.maNamespaces);
+ aRangeLink.maRowGroups.push_back(
+ OUStringToOString(aThisEntry, RTL_TEXTENCODING_UTF8));
+
+ aParam.maRangeLinks.push_back(aRangeLink);
+ }
+
+ // Remove duplicate namespace IDs.
+ removeDuplicates(aParam.maNamespaces);
+
+ // Now do the import.
+ mpXMLContext->importXML(aParam);
+
+ // Don't forget to broadcast the change.
+ ScDocShell* pShell = mpDoc->GetDocumentShell();
+ pShell->Broadcast(SfxHint(SfxHintId::ScDataChanged));
+
+ // Repaint the grid to force repaint the cell values.
+ ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
+ if (pViewShell)
+ pViewShell->PaintGrid();
+
+ m_xDialog->response(RET_OK);
+}
+
+void ScXMLSourceDlg::CancelPressed()
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+void ScXMLSourceDlg::RefEditModified()
+{
+ OUString aRefStr = mxRefEdit->GetText();
+
+ // Check if the address is valid.
+ // Preset current sheet in case only address was entered.
+ ScAddress aLinkedPos;
+ aLinkedPos.SetTab( ScDocShell::GetCurTab());
+ ScRefFlags nRes = aLinkedPos.Parse(aRefStr, *mpDoc, mpDoc->GetAddressConvention());
+ bool bValid = ( (nRes & ScRefFlags::VALID) == ScRefFlags::VALID );
+
+ // TODO: For some unknown reason, setting the ref invalid will hide the text altogether.
+ // Find out how to make this work.
+// mxRefEdit->SetRefValid(bValid);
+
+ if (!bValid)
+ aLinkedPos.SetInvalid();
+
+ // Set this address to the current reference entry.
+ if (!mxCurRefEntry)
+ // This should never happen.
+ return;
+
+ ScOrcusXMLTreeParam::EntryData* pUserData = ScOrcusXMLTreeParam::getUserData(*mxLbTree, *mxCurRefEntry);
+ if (!pUserData)
+ // This should never happen either.
+ return;
+
+ bool bRepeatElem = pUserData->meType == ScOrcusXMLTreeParam::ElementRepeat;
+ pUserData->maLinkedPos = aLinkedPos;
+ pUserData->mbRangeParent = aLinkedPos.IsValid() && bRepeatElem;
+
+ if (bRepeatElem)
+ {
+ if (bValid)
+ maRangeLinks.insert(mxLbTree->make_iterator(mxCurRefEntry.get()));
+ else
+ maRangeLinks.erase(mxCurRefEntry);
+ }
+ else
+ {
+ if (bValid)
+ maCellLinks.insert(mxLbTree->make_iterator(mxCurRefEntry.get()));
+ else
+ maCellLinks.erase(mxCurRefEntry);
+ }
+
+ // Enable the import button only when at least one link exists.
+ bool bHasLink = !maCellLinks.empty() || !maRangeLinks.empty();
+ mxBtnOk->set_sensitive(bHasLink);
+}
+
+IMPL_LINK(ScXMLSourceDlg, BtnPressedHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnSelectSource.get())
+ SelectSourceFile();
+ else if (&rBtn == mxBtnOk.get())
+ OkPressed();
+ else if (&rBtn == mxBtnCancel.get())
+ CancelPressed();
+}
+
+IMPL_LINK_NOARG(ScXMLSourceDlg, TreeItemSelectHdl, weld::TreeView&, void)
+{
+ TreeItemSelected();
+}
+
+IMPL_LINK_NOARG(ScXMLSourceDlg, RefModifiedHdl, formula::RefEdit&, void)
+{
+ RefEditModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */